From 6752f06335fddcec0933a8d01cfec019fe6057b8 Mon Sep 17 00:00:00 2001 From: ysy <2901457817@qq.com> Date: Thu, 30 May 2024 15:41:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 8 + .idea/book_management_sys.iml | 21 + .idea/dataSources.xml | 14 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + __pycache__/forms.cpython-36.pyc | Bin 0 -> 2796 bytes app.py | 497 + data.sqlite | Bin 0 -> 49152 bytes forms.py | 56 + static/icon.jpg | Bin 0 -> 63940 bytes static/layui/css/layui.css | 2 + static/layui/css/layui.mobile.css | 2 + static/layui/css/modules/code.css | 2 + .../css/modules/laydate/default/laydate.css | 2 + .../css/modules/layer/default/icon-ext.png | Bin 0 -> 5911 bytes .../layui/css/modules/layer/default/icon.png | Bin 0 -> 11493 bytes .../layui/css/modules/layer/default/layer.css | 2 + .../css/modules/layer/default/loading-0.gif | Bin 0 -> 5793 bytes .../css/modules/layer/default/loading-1.gif | Bin 0 -> 701 bytes .../css/modules/layer/default/loading-2.gif | Bin 0 -> 1787 bytes static/layui/font/iconfont.eot | Bin 0 -> 40844 bytes static/layui/font/iconfont.svg | 473 + static/layui/font/iconfont.ttf | Bin 0 -> 40668 bytes static/layui/font/iconfont.woff | Bin 0 -> 26744 bytes static/layui/images/face/0.gif | Bin 0 -> 2689 bytes static/layui/images/face/1.gif | Bin 0 -> 5514 bytes static/layui/images/face/10.gif | Bin 0 -> 2797 bytes static/layui/images/face/11.gif | Bin 0 -> 4121 bytes static/layui/images/face/12.gif | Bin 0 -> 3361 bytes static/layui/images/face/13.gif | Bin 0 -> 7425 bytes static/layui/images/face/14.gif | Bin 0 -> 2375 bytes static/layui/images/face/15.gif | Bin 0 -> 1793 bytes static/layui/images/face/16.gif | Bin 0 -> 6721 bytes static/layui/images/face/17.gif | Bin 0 -> 4439 bytes static/layui/images/face/18.gif | Bin 0 -> 3017 bytes static/layui/images/face/19.gif | Bin 0 -> 3040 bytes static/layui/images/face/2.gif | Bin 0 -> 3222 bytes static/layui/images/face/20.gif | Bin 0 -> 5144 bytes static/layui/images/face/21.gif | Bin 0 -> 5191 bytes static/layui/images/face/22.gif | Bin 0 -> 9823 bytes static/layui/images/face/23.gif | Bin 0 -> 3792 bytes static/layui/images/face/24.gif | Bin 0 -> 8096 bytes static/layui/images/face/25.gif | Bin 0 -> 3127 bytes static/layui/images/face/26.gif | Bin 0 -> 3291 bytes static/layui/images/face/27.gif | Bin 0 -> 4377 bytes static/layui/images/face/28.gif | Bin 0 -> 2793 bytes static/layui/images/face/29.gif | Bin 0 -> 4854 bytes static/layui/images/face/3.gif | Bin 0 -> 4017 bytes static/layui/images/face/30.gif | Bin 0 -> 2555 bytes static/layui/images/face/31.gif | Bin 0 -> 2002 bytes static/layui/images/face/32.gif | Bin 0 -> 3481 bytes static/layui/images/face/33.gif | Bin 0 -> 2454 bytes static/layui/images/face/34.gif | Bin 0 -> 3700 bytes static/layui/images/face/35.gif | Bin 0 -> 1800 bytes static/layui/images/face/36.gif | Bin 0 -> 2331 bytes static/layui/images/face/37.gif | Bin 0 -> 1513 bytes static/layui/images/face/38.gif | Bin 0 -> 3615 bytes static/layui/images/face/39.gif | Bin 0 -> 6495 bytes static/layui/images/face/4.gif | Bin 0 -> 5689 bytes static/layui/images/face/40.gif | Bin 0 -> 3154 bytes static/layui/images/face/41.gif | Bin 0 -> 3644 bytes static/layui/images/face/42.gif | Bin 0 -> 5305 bytes static/layui/images/face/43.gif | Bin 0 -> 2674 bytes static/layui/images/face/44.gif | Bin 0 -> 4126 bytes static/layui/images/face/45.gif | Bin 0 -> 3417 bytes static/layui/images/face/46.gif | Bin 0 -> 3007 bytes static/layui/images/face/47.gif | Bin 0 -> 2333 bytes static/layui/images/face/48.gif | Bin 0 -> 2689 bytes static/layui/images/face/49.gif | Bin 0 -> 2315 bytes static/layui/images/face/5.gif | Bin 0 -> 4567 bytes static/layui/images/face/50.gif | Bin 0 -> 5866 bytes static/layui/images/face/51.gif | Bin 0 -> 2785 bytes static/layui/images/face/52.gif | Bin 0 -> 777 bytes static/layui/images/face/53.gif | Bin 0 -> 2127 bytes static/layui/images/face/54.gif | Bin 0 -> 2196 bytes static/layui/images/face/55.gif | Bin 0 -> 1971 bytes static/layui/images/face/56.gif | Bin 0 -> 2034 bytes static/layui/images/face/57.gif | Bin 0 -> 2705 bytes static/layui/images/face/58.gif | Bin 0 -> 2258 bytes static/layui/images/face/59.gif | Bin 0 -> 10311 bytes static/layui/images/face/6.gif | Bin 0 -> 2213 bytes static/layui/images/face/60.gif | Bin 0 -> 3245 bytes static/layui/images/face/61.gif | Bin 0 -> 2495 bytes static/layui/images/face/62.gif | Bin 0 -> 2017 bytes static/layui/images/face/63.gif | Bin 0 -> 5871 bytes static/layui/images/face/64.gif | Bin 0 -> 6448 bytes static/layui/images/face/65.gif | Bin 0 -> 3576 bytes static/layui/images/face/66.gif | Bin 0 -> 3029 bytes static/layui/images/face/67.gif | Bin 0 -> 2701 bytes static/layui/images/face/68.gif | Bin 0 -> 1424 bytes static/layui/images/face/69.gif | Bin 0 -> 2431 bytes static/layui/images/face/7.gif | Bin 0 -> 3398 bytes static/layui/images/face/70.gif | Bin 0 -> 4590 bytes static/layui/images/face/71.gif | Bin 0 -> 5304 bytes static/layui/images/face/8.gif | Bin 0 -> 4050 bytes static/layui/images/face/9.gif | Bin 0 -> 4221 bytes static/layui/lay/modules/carousel.js | 2 + static/layui/lay/modules/code.js | 2 + static/layui/lay/modules/colorpicker.js | 2 + static/layui/lay/modules/element.js | 2 + static/layui/lay/modules/flow.js | 2 + static/layui/lay/modules/form.js | 2 + static/layui/lay/modules/jquery.js | 5 + static/layui/lay/modules/laydate.js | 2 + static/layui/lay/modules/layedit.js | 2 + static/layui/lay/modules/layer.js | 2 + static/layui/lay/modules/laypage.js | 2 + static/layui/lay/modules/laytpl.js | 2 + static/layui/lay/modules/mobile.js | 2 + static/layui/lay/modules/rate.js | 2 + static/layui/lay/modules/slider.js | 2 + static/layui/lay/modules/table.js | 2 + static/layui/lay/modules/tree.js | 2 + static/layui/lay/modules/upload.js | 2 + static/layui/lay/modules/util.js | 2 + static/layui/layui.all.js | 5 + static/layui/layui.js | 2 + static/login.css | 581 ++ templates/base-user.html | 54 + templates/base.html | 113 + templates/base2.html | 66 + templates/borrow.html | 174 + templates/change-info.html | 40 + templates/change-password.html | 39 + templates/index.html | 109 + templates/login.html | 23 + templates/new-store.html | 67 + templates/return.html | 195 + templates/search-book.html | 129 + templates/search-student.html | 167 + templates/storage.html | 54 + templates/user-book.html | 131 + templates/user-info.html | 19 + templates/user-student.html | 168 + .../Click-7.0.dist-info/INSTALLER | 1 + .../Click-7.0.dist-info/LICENSE.txt | 39 + .../Click-7.0.dist-info/METADATA | 121 + .../site-packages/Click-7.0.dist-info/RECORD | 40 + .../site-packages/Click-7.0.dist-info/WHEEL | 6 + .../Click-7.0.dist-info/top_level.txt | 1 + .../Flask-1.0.2.dist-info/INSTALLER | 1 + .../Flask-1.0.2.dist-info/LICENSE.txt | 31 + .../Flask-1.0.2.dist-info/METADATA | 130 + .../Flask-1.0.2.dist-info/RECORD | 48 + .../site-packages/Flask-1.0.2.dist-info/WHEEL | 6 + .../Flask-1.0.2.dist-info/entry_points.txt | 3 + .../Flask-1.0.2.dist-info/top_level.txt | 1 + .../Flask_Login-0.4.1-py3.6.egg-info/PKG-INFO | 44 + .../SOURCES.txt | 20 + .../dependency_links.txt | 1 + .../installed-files.txt | 23 + .../not-zip-safe | 1 + .../requires.txt | 1 + .../top_level.txt | 1 + .../version_info.json | 6 + .../DESCRIPTION.rst | 15 + .../INSTALLER | 1 + .../LICENSE.txt | 31 + .../Flask_SQLAlchemy-2.3.2.dist-info/METADATA | 43 + .../Flask_SQLAlchemy-2.3.2.dist-info/RECORD | 14 + .../Flask_SQLAlchemy-2.3.2.dist-info/WHEEL | 6 + .../metadata.json | 1 + .../top_level.txt | 1 + .../PKG-INFO | 36 + .../SOURCES.txt | 29 + .../dependency_links.txt | 1 + .../installed-files.txt | 14 + .../not-zip-safe | 1 + .../requires.txt | 1 + .../top_level.txt | 1 + .../DESCRIPTION.rst | 21 + .../Flask_WTF-0.14.2.dist-info/INSTALLER | 1 + .../Flask_WTF-0.14.2.dist-info/LICENSE.txt | 32 + .../Flask_WTF-0.14.2.dist-info/METADATA | 52 + .../Flask_WTF-0.14.2.dist-info/RECORD | 30 + .../Flask_WTF-0.14.2.dist-info/WHEEL | 6 + .../Flask_WTF-0.14.2.dist-info/metadata.json | 1 + .../Flask_WTF-0.14.2.dist-info/top_level.txt | 1 + .../Jinja2-2.10.dist-info/DESCRIPTION.rst | 37 + .../Jinja2-2.10.dist-info/INSTALLER | 1 + .../Jinja2-2.10.dist-info/LICENSE.txt | 31 + .../Jinja2-2.10.dist-info/METADATA | 68 + .../Jinja2-2.10.dist-info/RECORD | 63 + .../site-packages/Jinja2-2.10.dist-info/WHEEL | 6 + .../Jinja2-2.10.dist-info/entry_points.txt | 4 + .../Jinja2-2.10.dist-info/metadata.json | 1 + .../Jinja2-2.10.dist-info/top_level.txt | 1 + .../MarkupSafe-1.1.0.dist-info/INSTALLER | 1 + .../MarkupSafe-1.1.0.dist-info/LICENSE.rst | 33 + .../MarkupSafe-1.1.0.dist-info/METADATA | 103 + .../MarkupSafe-1.1.0.dist-info/RECORD | 15 + .../MarkupSafe-1.1.0.dist-info/WHEEL | 5 + .../MarkupSafe-1.1.0.dist-info/top_level.txt | 1 + .../SQLAlchemy-1.2.15-py3.6.egg-info/PKG-INFO | 162 + .../SOURCES.txt | 838 ++ .../dependency_links.txt | 1 + .../installed-files.txt | 390 + .../requires.txt | 27 + .../top_level.txt | 1 + .../WTForms-2.2.1.dist-info/INSTALLER | 1 + .../WTForms-2.2.1.dist-info/METADATA | 101 + .../WTForms-2.2.1.dist-info/RECORD | 147 + .../WTForms-2.2.1.dist-info/WHEEL | 6 + .../WTForms-2.2.1.dist-info/top_level.txt | 1 + .../Werkzeug-0.14.1.dist-info/DESCRIPTION.rst | 80 + .../Werkzeug-0.14.1.dist-info/INSTALLER | 1 + .../Werkzeug-0.14.1.dist-info/LICENSE.txt | 31 + .../Werkzeug-0.14.1.dist-info/METADATA | 116 + .../Werkzeug-0.14.1.dist-info/RECORD | 97 + .../Werkzeug-0.14.1.dist-info/WHEEL | 6 + .../Werkzeug-0.14.1.dist-info/metadata.json | 1 + .../Werkzeug-0.14.1.dist-info/top_level.txt | 1 + venv/Lib/site-packages/click/__init__.py | 97 + .../click/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 2664 bytes .../click/__pycache__/_compat.cpython-36.pyc | Bin 0 -> 16795 bytes .../__pycache__/_unicodefun.cpython-36.pyc | Bin 0 -> 3385 bytes .../__pycache__/_winconsole.cpython-36.pyc | Bin 0 -> 8787 bytes .../click/__pycache__/core.cpython-36.pyc | Bin 0 -> 59858 bytes .../__pycache__/decorators.cpython-36.pyc | Bin 0 -> 11616 bytes .../__pycache__/exceptions.cpython-36.pyc | Bin 0 -> 8618 bytes .../__pycache__/formatting.cpython-36.pyc | Bin 0 -> 8561 bytes .../click/__pycache__/globals.cpython-36.pyc | Bin 0 -> 1890 bytes .../click/__pycache__/parser.cpython-36.pyc | Bin 0 -> 11479 bytes .../click/__pycache__/termui.cpython-36.pyc | Bin 0 -> 20828 bytes .../click/__pycache__/types.cpython-36.pyc | Bin 0 -> 21875 bytes .../click/__pycache__/utils.cpython-36.pyc | Bin 0 -> 15256 bytes venv/Lib/site-packages/click/_bashcomplete.py | 293 + venv/Lib/site-packages/click/_compat.py | 703 ++ venv/Lib/site-packages/click/_termui_impl.py | 621 ++ venv/Lib/site-packages/click/_textwrap.py | 38 + venv/Lib/site-packages/click/_unicodefun.py | 125 + venv/Lib/site-packages/click/_winconsole.py | 307 + venv/Lib/site-packages/click/core.py | 1856 ++++ venv/Lib/site-packages/click/decorators.py | 311 + venv/Lib/site-packages/click/exceptions.py | 235 + venv/Lib/site-packages/click/formatting.py | 256 + venv/Lib/site-packages/click/globals.py | 48 + venv/Lib/site-packages/click/parser.py | 427 + venv/Lib/site-packages/click/termui.py | 606 ++ venv/Lib/site-packages/click/testing.py | 374 + venv/Lib/site-packages/click/types.py | 668 ++ venv/Lib/site-packages/click/utils.py | 440 + .../dominate-2.3.5.dist-info/INSTALLER | 1 + .../dominate-2.3.5.dist-info/LICENSE.txt | 165 + .../dominate-2.3.5.dist-info/METADATA | 601 ++ .../dominate-2.3.5.dist-info/RECORD | 20 + .../dominate-2.3.5.dist-info/WHEEL | 6 + .../dominate-2.3.5.dist-info/top_level.txt | 1 + venv/Lib/site-packages/dominate/__init__.py | 4 + venv/Lib/site-packages/dominate/_version.py | 2 + venv/Lib/site-packages/dominate/document.py | 75 + venv/Lib/site-packages/dominate/dom1core.py | 68 + venv/Lib/site-packages/dominate/dom_tag.py | 449 + venv/Lib/site-packages/dominate/tags.py | 1038 +++ venv/Lib/site-packages/dominate/util.py | 170 + venv/Lib/site-packages/easy-install.pth | 1 + venv/Lib/site-packages/flask/__init__.py | 49 + venv/Lib/site-packages/flask/__main__.py | 14 + .../flask/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 1838 bytes .../flask/__pycache__/_compat.cpython-36.pyc | Bin 0 -> 3237 bytes .../flask/__pycache__/app.cpython-36.pyc | Bin 0 -> 70912 bytes .../__pycache__/blueprints.cpython-36.pyc | Bin 0 -> 20501 bytes .../flask/__pycache__/cli.cpython-36.pyc | Bin 0 -> 24878 bytes .../flask/__pycache__/config.cpython-36.pyc | Bin 0 -> 10004 bytes .../flask/__pycache__/ctx.cpython-36.pyc | Bin 0 -> 13974 bytes .../flask/__pycache__/globals.cpython-36.pyc | Bin 0 -> 1765 bytes .../flask/__pycache__/helpers.cpython-36.pyc | Bin 0 -> 33073 bytes .../flask/__pycache__/logging.cpython-36.pyc | Bin 0 -> 2394 bytes .../flask/__pycache__/sessions.cpython-36.pyc | Bin 0 -> 12241 bytes .../flask/__pycache__/signals.cpython-36.pyc | Bin 0 -> 2426 bytes .../__pycache__/templating.cpython-36.pyc | Bin 0 -> 4975 bytes .../flask/__pycache__/wrappers.cpython-36.pyc | Bin 0 -> 6784 bytes venv/Lib/site-packages/flask/_compat.py | 99 + venv/Lib/site-packages/flask/app.py | 2315 +++++ venv/Lib/site-packages/flask/blueprints.py | 448 + venv/Lib/site-packages/flask/cli.py | 898 ++ venv/Lib/site-packages/flask/config.py | 265 + venv/Lib/site-packages/flask/ctx.py | 457 + venv/Lib/site-packages/flask/debughelpers.py | 168 + venv/Lib/site-packages/flask/globals.py | 61 + venv/Lib/site-packages/flask/helpers.py | 1044 +++ venv/Lib/site-packages/flask/json/__init__.py | 327 + .../json/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 10284 bytes .../flask/json/__pycache__/tag.cpython-36.pyc | Bin 0 -> 11086 bytes venv/Lib/site-packages/flask/json/tag.py | 300 + venv/Lib/site-packages/flask/logging.py | 78 + venv/Lib/site-packages/flask/sessions.py | 385 + venv/Lib/site-packages/flask/signals.py | 57 + venv/Lib/site-packages/flask/templating.py | 150 + venv/Lib/site-packages/flask/testing.py | 250 + venv/Lib/site-packages/flask/views.py | 158 + venv/Lib/site-packages/flask/wrappers.py | 216 + .../site-packages/flask_login/__about__.py | 10 + .../Lib/site-packages/flask_login/__init__.py | 65 + .../__pycache__/__about__.cpython-36.pyc | Bin 0 -> 607 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 1873 bytes .../__pycache__/_compat.cpython-36.pyc | Bin 0 -> 994 bytes .../__pycache__/config.cpython-36.pyc | Bin 0 -> 867 bytes .../__pycache__/login_manager.cpython-36.pyc | Bin 0 -> 14051 bytes .../__pycache__/mixins.cpython-36.pyc | Bin 0 -> 2545 bytes .../__pycache__/signals.cpython-36.pyc | Bin 0 -> 899 bytes .../__pycache__/utils.cpython-36.pyc | Bin 0 -> 11963 bytes venv/Lib/site-packages/flask_login/_compat.py | 52 + venv/Lib/site-packages/flask_login/config.py | 54 + .../flask_login/login_manager.py | 478 + venv/Lib/site-packages/flask_login/mixins.py | 76 + venv/Lib/site-packages/flask_login/signals.py | 56 + venv/Lib/site-packages/flask_login/utils.py | 378 + .../site-packages/flask_script/__init__.py | 421 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 11675 bytes .../__pycache__/_compat.cpython-36.pyc | Bin 0 -> 3598 bytes .../__pycache__/cli.cpython-36.pyc | Bin 0 -> 2518 bytes .../__pycache__/commands.cpython-36.pyc | Bin 0 -> 17394 bytes .../Lib/site-packages/flask_script/_compat.py | 115 + venv/Lib/site-packages/flask_script/cli.py | 94 + .../site-packages/flask_script/commands.py | 561 ++ .../flask_sqlalchemy/__init__.py | 1001 ++ .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 33367 bytes .../__pycache__/_compat.cpython-36.pyc | Bin 0 -> 1388 bytes .../__pycache__/model.cpython-36.pyc | Bin 0 -> 4629 bytes .../site-packages/flask_sqlalchemy/_compat.py | 54 + .../site-packages/flask_sqlalchemy/model.py | 154 + venv/Lib/site-packages/flask_wtf/__init__.py | 19 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 614 bytes .../__pycache__/_compat.cpython-36.pyc | Bin 0 -> 1095 bytes .../flask_wtf/__pycache__/csrf.cpython-36.pyc | Bin 0 -> 11075 bytes .../flask_wtf/__pycache__/form.cpython-36.pyc | Bin 0 -> 5739 bytes .../flask_wtf/__pycache__/i18n.cpython-36.pyc | Bin 0 -> 1777 bytes venv/Lib/site-packages/flask_wtf/_compat.py | 35 + venv/Lib/site-packages/flask_wtf/csrf.py | 364 + venv/Lib/site-packages/flask_wtf/file.py | 94 + venv/Lib/site-packages/flask_wtf/form.py | 158 + venv/Lib/site-packages/flask_wtf/html5.py | 13 + venv/Lib/site-packages/flask_wtf/i18n.py | 69 + .../flask_wtf/recaptcha/__init__.py | 4 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 254 bytes .../__pycache__/fields.cpython-36.pyc | Bin 0 -> 795 bytes .../__pycache__/validators.cpython-36.pyc | Bin 0 -> 2367 bytes .../__pycache__/widgets.cpython-36.pyc | Bin 0 -> 1642 bytes .../flask_wtf/recaptcha/fields.py | 17 + .../flask_wtf/recaptcha/validators.py | 77 + .../flask_wtf/recaptcha/widgets.py | 42 + .../itsdangerous-1.1.0.dist-info/INSTALLER | 1 + .../itsdangerous-1.1.0.dist-info/LICENSE.rst | 47 + .../itsdangerous-1.1.0.dist-info/METADATA | 98 + .../itsdangerous-1.1.0.dist-info/RECORD | 26 + .../itsdangerous-1.1.0.dist-info/WHEEL | 6 + .../top_level.txt | 1 + .../site-packages/itsdangerous/__init__.py | 22 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 1011 bytes .../__pycache__/_compat.cpython-36.pyc | Bin 0 -> 1173 bytes .../__pycache__/_json.cpython-36.pyc | Bin 0 -> 866 bytes .../__pycache__/encoding.cpython-36.pyc | Bin 0 -> 1638 bytes .../__pycache__/exc.cpython-36.pyc | Bin 0 -> 3227 bytes .../__pycache__/jws.cpython-36.pyc | Bin 0 -> 6686 bytes .../__pycache__/serializer.cpython-36.pyc | Bin 0 -> 8041 bytes .../__pycache__/signer.cpython-36.pyc | Bin 0 -> 5802 bytes .../__pycache__/timed.cpython-36.pyc | Bin 0 -> 4568 bytes .../__pycache__/url_safe.cpython-36.pyc | Bin 0 -> 2557 bytes .../Lib/site-packages/itsdangerous/_compat.py | 46 + venv/Lib/site-packages/itsdangerous/_json.py | 18 + .../site-packages/itsdangerous/encoding.py | 49 + venv/Lib/site-packages/itsdangerous/exc.py | 98 + venv/Lib/site-packages/itsdangerous/jws.py | 218 + .../site-packages/itsdangerous/serializer.py | 233 + venv/Lib/site-packages/itsdangerous/signer.py | 179 + venv/Lib/site-packages/itsdangerous/timed.py | 147 + .../site-packages/itsdangerous/url_safe.py | 65 + venv/Lib/site-packages/jinja2/__init__.py | 83 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 2541 bytes .../jinja2/__pycache__/_compat.cpython-36.pyc | Bin 0 -> 3351 bytes .../__pycache__/_identifier.cpython-36.pyc | Bin 0 -> 1854 bytes .../__pycache__/asyncfilters.cpython-36.pyc | Bin 0 -> 4822 bytes .../__pycache__/asyncsupport.cpython-36.pyc | Bin 0 -> 8171 bytes .../jinja2/__pycache__/bccache.cpython-36.pyc | Bin 0 -> 12727 bytes .../__pycache__/compiler.cpython-36.pyc | Bin 0 -> 46908 bytes .../__pycache__/defaults.cpython-36.pyc | Bin 0 -> 1467 bytes .../__pycache__/environment.cpython-36.pyc | Bin 0 -> 43278 bytes .../__pycache__/exceptions.cpython-36.pyc | Bin 0 -> 5027 bytes .../jinja2/__pycache__/ext.cpython-36.pyc | Bin 0 -> 20118 bytes .../jinja2/__pycache__/filters.cpython-36.pyc | Bin 0 -> 34425 bytes .../__pycache__/idtracking.cpython-36.pyc | Bin 0 -> 9956 bytes .../jinja2/__pycache__/lexer.cpython-36.pyc | Bin 0 -> 18580 bytes .../jinja2/__pycache__/loaders.cpython-36.pyc | Bin 0 -> 16587 bytes .../jinja2/__pycache__/nodes.cpython-36.pyc | Bin 0 -> 36675 bytes .../__pycache__/optimizer.cpython-36.pyc | Bin 0 -> 2047 bytes .../jinja2/__pycache__/parser.cpython-36.pyc | Bin 0 -> 25352 bytes .../jinja2/__pycache__/runtime.cpython-36.pyc | Bin 0 -> 24617 bytes .../jinja2/__pycache__/tests.cpython-36.pyc | Bin 0 -> 4423 bytes .../jinja2/__pycache__/utils.cpython-36.pyc | Bin 0 -> 20880 bytes .../jinja2/__pycache__/visitor.cpython-36.pyc | Bin 0 -> 3363 bytes venv/Lib/site-packages/jinja2/_compat.py | 99 + venv/Lib/site-packages/jinja2/_identifier.py | 2 + venv/Lib/site-packages/jinja2/asyncfilters.py | 146 + venv/Lib/site-packages/jinja2/asyncsupport.py | 256 + venv/Lib/site-packages/jinja2/bccache.py | 362 + venv/Lib/site-packages/jinja2/compiler.py | 1721 ++++ venv/Lib/site-packages/jinja2/constants.py | 32 + venv/Lib/site-packages/jinja2/debug.py | 372 + venv/Lib/site-packages/jinja2/defaults.py | 56 + venv/Lib/site-packages/jinja2/environment.py | 1276 +++ venv/Lib/site-packages/jinja2/exceptions.py | 146 + venv/Lib/site-packages/jinja2/ext.py | 627 ++ venv/Lib/site-packages/jinja2/filters.py | 1190 +++ venv/Lib/site-packages/jinja2/idtracking.py | 286 + venv/Lib/site-packages/jinja2/lexer.py | 739 ++ venv/Lib/site-packages/jinja2/loaders.py | 481 + venv/Lib/site-packages/jinja2/meta.py | 106 + venv/Lib/site-packages/jinja2/nativetypes.py | 220 + venv/Lib/site-packages/jinja2/nodes.py | 999 ++ venv/Lib/site-packages/jinja2/optimizer.py | 49 + venv/Lib/site-packages/jinja2/parser.py | 903 ++ venv/Lib/site-packages/jinja2/runtime.py | 813 ++ venv/Lib/site-packages/jinja2/sandbox.py | 475 + venv/Lib/site-packages/jinja2/tests.py | 175 + venv/Lib/site-packages/jinja2/utils.py | 647 ++ venv/Lib/site-packages/jinja2/visitor.py | 87 + venv/Lib/site-packages/markupsafe/__init__.py | 327 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 11015 bytes .../__pycache__/_compat.cpython-36.pyc | Bin 0 -> 807 bytes venv/Lib/site-packages/markupsafe/_compat.py | 33 + .../site-packages/markupsafe/_constants.py | 264 + venv/Lib/site-packages/markupsafe/_native.py | 69 + .../markupsafe/_speedups.cp36-win_amd64.pyd | Bin 0 -> 15360 bytes .../pip-18.1.dist-info/LICENSE.txt | 20 + .../site-packages/pip-18.1.dist-info/METADATA | 70 + .../site-packages/pip-18.1.dist-info/RECORD | 311 + .../site-packages/pip-18.1.dist-info/WHEEL | 6 + .../pip-18.1.dist-info/entry_points.txt | 5 + .../pip-18.1.dist-info/top_level.txt | 1 + .../pip-9.0.1-py3.6.egg/EGG-INFO/PKG-INFO | 61 + .../pip-9.0.1-py3.6.egg/EGG-INFO/SOURCES.txt | 290 + .../EGG-INFO/dependency_links.txt | 1 + .../EGG-INFO/entry_points.txt | 5 + .../pip-9.0.1-py3.6.egg/EGG-INFO/not-zip-safe | 1 + .../pip-9.0.1-py3.6.egg/EGG-INFO/requires.txt | 7 + .../EGG-INFO/top_level.txt | 1 + .../pip-9.0.1-py3.6.egg/pip/__init__.py | 331 + .../pip-9.0.1-py3.6.egg/pip/__main__.py | 19 + .../pip/_vendor/__init__.py | 107 + .../pip/_vendor/appdirs.py | 552 ++ .../pip/_vendor/cachecontrol/__init__.py | 11 + .../pip/_vendor/cachecontrol/_cmd.py | 60 + .../pip/_vendor/cachecontrol/adapter.py | 125 + .../pip/_vendor/cachecontrol/cache.py | 39 + .../_vendor/cachecontrol/caches/__init__.py | 18 + .../_vendor/cachecontrol/caches/file_cache.py | 116 + .../cachecontrol/caches/redis_cache.py | 41 + .../pip/_vendor/cachecontrol/compat.py | 20 + .../pip/_vendor/cachecontrol/controller.py | 353 + .../pip/_vendor/cachecontrol/filewrapper.py | 78 + .../pip/_vendor/cachecontrol/heuristics.py | 138 + .../pip/_vendor/cachecontrol/serialize.py | 196 + .../pip/_vendor/cachecontrol/wrapper.py | 21 + .../pip/_vendor/colorama/__init__.py | 7 + .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 236 + .../pip/_vendor/colorama/initialise.py | 82 + .../pip/_vendor/colorama/win32.py | 154 + .../pip/_vendor/colorama/winterm.py | 162 + .../pip/_vendor/distlib/__init__.py | 23 + .../pip/_vendor/distlib/_backport/__init__.py | 6 + .../pip/_vendor/distlib/_backport/misc.py | 41 + .../pip/_vendor/distlib/_backport/shutil.py | 761 ++ .../_vendor/distlib/_backport/sysconfig.cfg | 84 + .../_vendor/distlib/_backport/sysconfig.py | 788 ++ .../pip/_vendor/distlib/_backport/tarfile.py | 2607 ++++++ .../pip/_vendor/distlib/compat.py | 1111 +++ .../pip/_vendor/distlib/database.py | 1312 +++ .../pip/_vendor/distlib/index.py | 515 ++ .../pip/_vendor/distlib/locators.py | 1283 +++ .../pip/_vendor/distlib/manifest.py | 393 + .../pip/_vendor/distlib/markers.py | 190 + .../pip/_vendor/distlib/metadata.py | 1068 +++ .../pip/_vendor/distlib/resources.py | 355 + .../pip/_vendor/distlib/scripts.py | 384 + .../pip/_vendor/distlib/t32.exe | Bin 0 -> 89088 bytes .../pip/_vendor/distlib/t64.exe | Bin 0 -> 97792 bytes .../pip/_vendor/distlib/util.py | 1611 ++++ .../pip/_vendor/distlib/version.py | 742 ++ .../pip/_vendor/distlib/w32.exe | Bin 0 -> 85504 bytes .../pip/_vendor/distlib/w64.exe | Bin 0 -> 94208 bytes .../pip/_vendor/distlib/wheel.py | 978 ++ .../pip-9.0.1-py3.6.egg/pip/_vendor/distro.py | 1081 +++ .../pip/_vendor/html5lib/__init__.py | 25 + .../pip/_vendor/html5lib/_ihatexml.py | 288 + .../pip/_vendor/html5lib/_inputstream.py | 923 ++ .../pip/_vendor/html5lib/_tokenizer.py | 1721 ++++ .../pip/_vendor/html5lib/_trie/__init__.py | 14 + .../pip/_vendor/html5lib/_trie/_base.py | 38 + .../pip/_vendor/html5lib/_trie/datrie.py | 44 + .../pip/_vendor/html5lib/_trie/py.py | 67 + .../pip/_vendor/html5lib/_utils.py | 127 + .../pip/_vendor/html5lib/constants.py | 2945 ++++++ .../pip/_vendor/html5lib/filters/__init__.py | 0 .../filters/alphabeticalattributes.py | 20 + .../pip/_vendor/html5lib/filters/base.py | 12 + .../html5lib/filters/inject_meta_charset.py | 65 + .../pip/_vendor/html5lib/filters/lint.py | 81 + .../_vendor/html5lib/filters/optionaltags.py | 206 + .../pip/_vendor/html5lib/filters/sanitizer.py | 865 ++ .../_vendor/html5lib/filters/whitespace.py | 38 + .../pip/_vendor/html5lib/html5parser.py | 2733 ++++++ .../pip/_vendor/html5lib/serializer.py | 334 + .../_vendor/html5lib/treeadapters/__init__.py | 12 + .../_vendor/html5lib/treeadapters/genshi.py | 47 + .../pip/_vendor/html5lib/treeadapters/sax.py | 44 + .../_vendor/html5lib/treebuilders/__init__.py | 76 + .../pip/_vendor/html5lib/treebuilders/base.py | 383 + .../pip/_vendor/html5lib/treebuilders/dom.py | 236 + .../_vendor/html5lib/treebuilders/etree.py | 340 + .../html5lib/treebuilders/etree_lxml.py | 367 + .../_vendor/html5lib/treewalkers/__init__.py | 143 + .../pip/_vendor/html5lib/treewalkers/base.py | 150 + .../pip/_vendor/html5lib/treewalkers/dom.py | 43 + .../pip/_vendor/html5lib/treewalkers/etree.py | 137 + .../html5lib/treewalkers/etree_lxml.py | 213 + .../_vendor/html5lib/treewalkers/genshi.py | 69 + .../pip/_vendor/ipaddress.py | 2425 +++++ .../pip/_vendor/lockfile/__init__.py | 347 + .../pip/_vendor/lockfile/linklockfile.py | 73 + .../pip/_vendor/lockfile/mkdirlockfile.py | 84 + .../pip/_vendor/lockfile/pidlockfile.py | 190 + .../pip/_vendor/lockfile/sqlitelockfile.py | 156 + .../pip/_vendor/lockfile/symlinklockfile.py | 70 + .../pip/_vendor/ordereddict.py | 127 + .../pip/_vendor/packaging/__about__.py | 21 + .../pip/_vendor/packaging/__init__.py | 14 + .../pip/_vendor/packaging/_compat.py | 30 + .../pip/_vendor/packaging/_structures.py | 68 + .../pip/_vendor/packaging/markers.py | 303 + .../pip/_vendor/packaging/requirements.py | 129 + .../pip/_vendor/packaging/specifiers.py | 774 ++ .../pip/_vendor/packaging/utils.py | 14 + .../pip/_vendor/packaging/version.py | 393 + .../pip/_vendor/pkg_resources/__init__.py | 3052 ++++++ .../pip/_vendor/progress/__init__.py | 123 + .../pip/_vendor/progress/bar.py | 83 + .../pip/_vendor/progress/counter.py | 47 + .../pip/_vendor/progress/helpers.py | 91 + .../pip/_vendor/progress/spinner.py | 40 + .../pip/_vendor/pyparsing.py | 5696 ++++++++++++ .../pip/_vendor/re-vendor.py | 34 + .../pip/_vendor/requests/__init__.py | 88 + .../pip/_vendor/requests/adapters.py | 503 + .../pip/_vendor/requests/api.py | 148 + .../pip/_vendor/requests/auth.py | 252 + .../pip/_vendor/requests/cacert.pem | 5616 +++++++++++ .../pip/_vendor/requests/certs.py | 25 + .../pip/_vendor/requests/compat.py | 68 + .../pip/_vendor/requests/cookies.py | 540 ++ .../pip/_vendor/requests/exceptions.py | 114 + .../pip/_vendor/requests/hooks.py | 34 + .../pip/_vendor/requests/models.py | 873 ++ .../pip/_vendor/requests/packages/__init__.py | 36 + .../requests/packages/chardet/__init__.py | 32 + .../requests/packages/chardet/big5freq.py | 925 ++ .../requests/packages/chardet/big5prober.py | 42 + .../requests/packages/chardet/chardetect.py | 80 + .../packages/chardet/chardistribution.py | 231 + .../packages/chardet/charsetgroupprober.py | 106 + .../packages/chardet/charsetprober.py | 62 + .../packages/chardet/codingstatemachine.py | 61 + .../requests/packages/chardet/compat.py | 34 + .../requests/packages/chardet/constants.py | 39 + .../requests/packages/chardet/cp949prober.py | 44 + .../requests/packages/chardet/escprober.py | 86 + .../requests/packages/chardet/escsm.py | 242 + .../requests/packages/chardet/eucjpprober.py | 90 + .../requests/packages/chardet/euckrfreq.py | 596 ++ .../requests/packages/chardet/euckrprober.py | 42 + .../requests/packages/chardet/euctwfreq.py | 428 + .../requests/packages/chardet/euctwprober.py | 41 + .../requests/packages/chardet/gb2312freq.py | 472 + .../requests/packages/chardet/gb2312prober.py | 41 + .../requests/packages/chardet/hebrewprober.py | 283 + .../requests/packages/chardet/jisfreq.py | 569 ++ .../requests/packages/chardet/jpcntx.py | 227 + .../packages/chardet/langbulgarianmodel.py | 229 + .../packages/chardet/langcyrillicmodel.py | 329 + .../packages/chardet/langgreekmodel.py | 225 + .../packages/chardet/langhebrewmodel.py | 201 + .../packages/chardet/langhungarianmodel.py | 225 + .../packages/chardet/langthaimodel.py | 200 + .../requests/packages/chardet/latin1prober.py | 139 + .../packages/chardet/mbcharsetprober.py | 86 + .../packages/chardet/mbcsgroupprober.py | 54 + .../requests/packages/chardet/mbcssm.py | 572 ++ .../packages/chardet/sbcharsetprober.py | 120 + .../packages/chardet/sbcsgroupprober.py | 69 + .../requests/packages/chardet/sjisprober.py | 91 + .../packages/chardet/universaldetector.py | 170 + .../requests/packages/chardet/utf8prober.py | 76 + .../requests/packages/urllib3/__init__.py | 96 + .../requests/packages/urllib3/_collections.py | 324 + .../requests/packages/urllib3/connection.py | 330 + .../packages/urllib3/connectionpool.py | 866 ++ .../packages/urllib3/contrib/__init__.py | 0 .../packages/urllib3/contrib/appengine.py | 231 + .../packages/urllib3/contrib/ntlmpool.py | 115 + .../packages/urllib3/contrib/pyopenssl.py | 358 + .../packages/urllib3/contrib/socks.py | 172 + .../requests/packages/urllib3/exceptions.py | 209 + .../requests/packages/urllib3/fields.py | 178 + .../requests/packages/urllib3/filepost.py | 94 + .../packages/urllib3/packages/__init__.py | 5 + .../packages/urllib3/packages/ordered_dict.py | 259 + .../requests/packages/urllib3/packages/six.py | 868 ++ .../packages/ssl_match_hostname/__init__.py | 13 + .../ssl_match_hostname/_implementation.py | 105 + .../requests/packages/urllib3/poolmanager.py | 367 + .../requests/packages/urllib3/request.py | 151 + .../requests/packages/urllib3/response.py | 530 ++ .../packages/urllib3/util/__init__.py | 46 + .../packages/urllib3/util/connection.py | 144 + .../requests/packages/urllib3/util/request.py | 72 + .../packages/urllib3/util/response.py | 74 + .../requests/packages/urllib3/util/retry.py | 300 + .../requests/packages/urllib3/util/ssl_.py | 320 + .../requests/packages/urllib3/util/timeout.py | 242 + .../requests/packages/urllib3/util/url.py | 217 + .../pip/_vendor/requests/sessions.py | 712 ++ .../pip/_vendor/requests/status_codes.py | 91 + .../pip/_vendor/requests/structures.py | 105 + .../pip/_vendor/requests/utils.py | 817 ++ .../pip/_vendor/retrying.py | 267 + .../pip-9.0.1-py3.6.egg/pip/_vendor/six.py | 868 ++ .../pip/_vendor/webencodings/__init__.py | 342 + .../pip/_vendor/webencodings/labels.py | 231 + .../pip/_vendor/webencodings/mklabels.py | 59 + .../pip/_vendor/webencodings/tests.py | 153 + .../_vendor/webencodings/x_user_defined.py | 325 + .../pip-9.0.1-py3.6.egg/pip/basecommand.py | 337 + .../pip-9.0.1-py3.6.egg/pip/baseparser.py | 293 + .../pip-9.0.1-py3.6.egg/pip/cmdoptions.py | 633 ++ .../pip/commands/__init__.py | 86 + .../pip-9.0.1-py3.6.egg/pip/commands/check.py | 39 + .../pip/commands/completion.py | 81 + .../pip/commands/download.py | 212 + .../pip/commands/freeze.py | 87 + .../pip-9.0.1-py3.6.egg/pip/commands/hash.py | 57 + .../pip-9.0.1-py3.6.egg/pip/commands/help.py | 35 + .../pip/commands/install.py | 437 + .../pip-9.0.1-py3.6.egg/pip/commands/list.py | 337 + .../pip/commands/search.py | 133 + .../pip-9.0.1-py3.6.egg/pip/commands/show.py | 154 + .../pip/commands/uninstall.py | 76 + .../pip-9.0.1-py3.6.egg/pip/commands/wheel.py | 208 + .../pip/compat/__init__.py | 164 + .../pip/compat/dictconfig.py | 565 ++ .../pip-9.0.1-py3.6.egg/pip/download.py | 906 ++ .../pip-9.0.1-py3.6.egg/pip/exceptions.py | 244 + .../pip-9.0.1-py3.6.egg/pip/index.py | 1102 +++ .../pip-9.0.1-py3.6.egg/pip/locations.py | 182 + .../pip/models/__init__.py | 4 + .../pip-9.0.1-py3.6.egg/pip/models/index.py | 16 + .../pip/operations/__init__.py | 0 .../pip/operations/check.py | 49 + .../pip/operations/freeze.py | 132 + .../pip-9.0.1-py3.6.egg/pip/pep425tags.py | 324 + .../pip-9.0.1-py3.6.egg/pip/req/__init__.py | 10 + .../pip-9.0.1-py3.6.egg/pip/req/req_file.py | 342 + .../pip/req/req_install.py | 1204 +++ .../pip-9.0.1-py3.6.egg/pip/req/req_set.py | 798 ++ .../pip/req/req_uninstall.py | 195 + .../pip-9.0.1-py3.6.egg/pip/status_codes.py | 8 + .../pip-9.0.1-py3.6.egg/pip/utils/__init__.py | 852 ++ .../pip-9.0.1-py3.6.egg/pip/utils/appdirs.py | 248 + .../pip-9.0.1-py3.6.egg/pip/utils/build.py | 42 + .../pip/utils/deprecation.py | 76 + .../pip-9.0.1-py3.6.egg/pip/utils/encoding.py | 31 + .../pip/utils/filesystem.py | 28 + .../pip-9.0.1-py3.6.egg/pip/utils/glibc.py | 81 + .../pip-9.0.1-py3.6.egg/pip/utils/hashes.py | 92 + .../pip-9.0.1-py3.6.egg/pip/utils/logging.py | 130 + .../pip-9.0.1-py3.6.egg/pip/utils/outdated.py | 162 + .../pip/utils/packaging.py | 63 + .../pip/utils/setuptools_build.py | 8 + .../pip-9.0.1-py3.6.egg/pip/utils/ui.py | 344 + .../pip-9.0.1-py3.6.egg/pip/vcs/__init__.py | 366 + .../pip-9.0.1-py3.6.egg/pip/vcs/bazaar.py | 116 + .../pip-9.0.1-py3.6.egg/pip/vcs/git.py | 300 + .../pip-9.0.1-py3.6.egg/pip/vcs/mercurial.py | 103 + .../pip-9.0.1-py3.6.egg/pip/vcs/subversion.py | 269 + .../pip-9.0.1-py3.6.egg/pip/wheel.py | 853 ++ venv/Lib/site-packages/pip/__init__.py | 1 + venv/Lib/site-packages/pip/__main__.py | 19 + .../site-packages/pip/_internal/__init__.py | 78 + .../site-packages/pip/_internal/build_env.py | 142 + venv/Lib/site-packages/pip/_internal/cache.py | 202 + .../pip/_internal/cli/__init__.py | 4 + .../pip/_internal/cli/autocompletion.py | 152 + .../pip/_internal/cli/base_command.py | 278 + .../pip/_internal/cli/cmdoptions.py | 714 ++ .../pip/_internal/cli/main_parser.py | 96 + .../site-packages/pip/_internal/cli/parser.py | 261 + .../pip/_internal/cli/status_codes.py | 8 + .../pip/_internal/commands/__init__.py | 79 + .../pip/_internal/commands/check.py | 41 + .../pip/_internal/commands/completion.py | 94 + .../pip/_internal/commands/configuration.py | 227 + .../pip/_internal/commands/download.py | 174 + .../pip/_internal/commands/freeze.py | 96 + .../pip/_internal/commands/hash.py | 57 + .../pip/_internal/commands/help.py | 37 + .../pip/_internal/commands/install.py | 535 ++ .../pip/_internal/commands/list.py | 306 + .../pip/_internal/commands/search.py | 135 + .../pip/_internal/commands/show.py | 168 + .../pip/_internal/commands/uninstall.py | 78 + .../pip/_internal/commands/wheel.py | 183 + .../pip/_internal/configuration.py | 387 + .../site-packages/pip/_internal/download.py | 921 ++ .../site-packages/pip/_internal/exceptions.py | 268 + venv/Lib/site-packages/pip/_internal/index.py | 899 ++ .../site-packages/pip/_internal/locations.py | 194 + .../pip/_internal/models/__init__.py | 2 + .../pip/_internal/models/candidate.py | 23 + .../pip/_internal/models/format_control.py | 62 + .../pip/_internal/models/index.py | 29 + .../pip/_internal/models/link.py | 141 + .../pip/_internal/operations/__init__.py | 0 .../pip/_internal/operations/check.py | 148 + .../pip/_internal/operations/freeze.py | 264 + .../pip/_internal/operations/prepare.py | 355 + .../site-packages/pip/_internal/pep425tags.py | 317 + .../site-packages/pip/_internal/pyproject.py | 144 + .../pip/_internal/req/__init__.py | 69 + .../pip/_internal/req/constructors.py | 298 + .../pip/_internal/req/req_file.py | 340 + .../pip/_internal/req/req_install.py | 860 ++ .../pip/_internal/req/req_set.py | 181 + .../pip/_internal/req/req_tracker.py | 76 + .../pip/_internal/req/req_uninstall.py | 460 + .../site-packages/pip/_internal/resolve.py | 353 + .../pip/_internal/utils/__init__.py | 0 .../pip/_internal/utils/appdirs.py | 258 + .../pip/_internal/utils/compat.py | 248 + .../pip/_internal/utils/deprecation.py | 89 + .../pip/_internal/utils/encoding.py | 33 + .../pip/_internal/utils/filesystem.py | 28 + .../pip/_internal/utils/glibc.py | 84 + .../pip/_internal/utils/hashes.py | 94 + .../pip/_internal/utils/logging.py | 225 + .../site-packages/pip/_internal/utils/misc.py | 940 ++ .../pip/_internal/utils/models.py | 40 + .../pip/_internal/utils/outdated.py | 154 + .../pip/_internal/utils/packaging.py | 75 + .../pip/_internal/utils/setuptools_build.py | 8 + .../pip/_internal/utils/temp_dir.py | 82 + .../pip/_internal/utils/typing.py | 29 + .../site-packages/pip/_internal/utils/ui.py | 421 + .../pip/_internal/vcs/__init__.py | 509 + .../site-packages/pip/_internal/vcs/bazaar.py | 112 + .../site-packages/pip/_internal/vcs/git.py | 346 + .../pip/_internal/vcs/mercurial.py | 101 + .../pip/_internal/vcs/subversion.py | 213 + venv/Lib/site-packages/pip/_internal/wheel.py | 831 ++ .../Lib/site-packages/pip/_vendor/__init__.py | 110 + venv/Lib/site-packages/pip/_vendor/appdirs.py | 604 ++ .../pip/_vendor/cachecontrol/__init__.py | 11 + .../pip/_vendor/cachecontrol/_cmd.py | 57 + .../pip/_vendor/cachecontrol/adapter.py | 133 + .../pip/_vendor/cachecontrol/cache.py | 39 + .../_vendor/cachecontrol/caches/__init__.py | 2 + .../_vendor/cachecontrol/caches/file_cache.py | 146 + .../cachecontrol/caches/redis_cache.py | 33 + .../pip/_vendor/cachecontrol/compat.py | 29 + .../pip/_vendor/cachecontrol/controller.py | 367 + .../pip/_vendor/cachecontrol/filewrapper.py | 80 + .../pip/_vendor/cachecontrol/heuristics.py | 135 + .../pip/_vendor/cachecontrol/serialize.py | 186 + .../pip/_vendor/cachecontrol/wrapper.py | 29 + .../pip/_vendor/certifi/__init__.py | 3 + .../pip/_vendor/certifi/__main__.py | 2 + .../pip/_vendor/certifi/cacert.pem | 4300 +++++++++ .../site-packages/pip/_vendor/certifi/core.py | 37 + .../pip/_vendor/chardet/__init__.py | 39 + .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 233 + .../pip/_vendor/chardet/charsetgroupprober.py | 106 + .../pip/_vendor/chardet/charsetprober.py | 145 + .../pip/_vendor/chardet/cli/__init__.py | 1 + .../pip/_vendor/chardet/cli/chardetect.py | 85 + .../pip/_vendor/chardet/codingstatemachine.py | 88 + .../pip/_vendor/chardet/compat.py | 34 + .../pip/_vendor/chardet/cp949prober.py | 49 + .../pip/_vendor/chardet/enums.py | 76 + .../pip/_vendor/chardet/escprober.py | 101 + .../pip/_vendor/chardet/escsm.py | 246 + .../pip/_vendor/chardet/eucjpprober.py | 92 + .../pip/_vendor/chardet/euckrfreq.py | 195 + .../pip/_vendor/chardet/euckrprober.py | 47 + .../pip/_vendor/chardet/euctwfreq.py | 387 + .../pip/_vendor/chardet/euctwprober.py | 46 + .../pip/_vendor/chardet/gb2312freq.py | 283 + .../pip/_vendor/chardet/gb2312prober.py | 46 + .../pip/_vendor/chardet/hebrewprober.py | 292 + .../pip/_vendor/chardet/jisfreq.py | 325 + .../pip/_vendor/chardet/jpcntx.py | 233 + .../pip/_vendor/chardet/langbulgarianmodel.py | 228 + .../pip/_vendor/chardet/langcyrillicmodel.py | 333 + .../pip/_vendor/chardet/langgreekmodel.py | 225 + .../pip/_vendor/chardet/langhebrewmodel.py | 200 + .../pip/_vendor/chardet/langhungarianmodel.py | 225 + .../pip/_vendor/chardet/langthaimodel.py | 199 + .../pip/_vendor/chardet/langturkishmodel.py | 193 + .../pip/_vendor/chardet/latin1prober.py | 145 + .../pip/_vendor/chardet/mbcharsetprober.py | 91 + .../pip/_vendor/chardet/mbcsgroupprober.py | 54 + .../pip/_vendor/chardet/mbcssm.py | 572 ++ .../pip/_vendor/chardet/sbcharsetprober.py | 132 + .../pip/_vendor/chardet/sbcsgroupprober.py | 73 + .../pip/_vendor/chardet/sjisprober.py | 92 + .../pip/_vendor/chardet/universaldetector.py | 286 + .../pip/_vendor/chardet/utf8prober.py | 82 + .../pip/_vendor/chardet/version.py | 9 + .../pip/_vendor/colorama/__init__.py | 7 + .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 236 + .../pip/_vendor/colorama/initialise.py | 82 + .../pip/_vendor/colorama/win32.py | 156 + .../pip/_vendor/colorama/winterm.py | 162 + .../pip/_vendor/distlib/__init__.py | 23 + .../pip/_vendor/distlib/_backport/__init__.py | 6 + .../pip/_vendor/distlib/_backport/misc.py | 41 + .../pip/_vendor/distlib/_backport/shutil.py | 761 ++ .../_vendor/distlib/_backport/sysconfig.cfg | 84 + .../_vendor/distlib/_backport/sysconfig.py | 788 ++ .../pip/_vendor/distlib/_backport/tarfile.py | 2607 ++++++ .../pip/_vendor/distlib/compat.py | 1120 +++ .../pip/_vendor/distlib/database.py | 1336 +++ .../pip/_vendor/distlib/index.py | 516 ++ .../pip/_vendor/distlib/locators.py | 1292 +++ .../pip/_vendor/distlib/manifest.py | 393 + .../pip/_vendor/distlib/markers.py | 131 + .../pip/_vendor/distlib/metadata.py | 1091 +++ .../pip/_vendor/distlib/resources.py | 355 + .../pip/_vendor/distlib/scripts.py | 415 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 92672 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 102400 bytes .../site-packages/pip/_vendor/distlib/util.py | 1755 ++++ .../pip/_vendor/distlib/version.py | 736 ++ .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 89088 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 99328 bytes .../pip/_vendor/distlib/wheel.py | 984 ++ venv/Lib/site-packages/pip/_vendor/distro.py | 1197 +++ .../pip/_vendor/html5lib/__init__.py | 35 + .../pip/_vendor/html5lib/_ihatexml.py | 288 + .../pip/_vendor/html5lib/_inputstream.py | 923 ++ .../pip/_vendor/html5lib/_tokenizer.py | 1721 ++++ .../pip/_vendor/html5lib/_trie/__init__.py | 14 + .../pip/_vendor/html5lib/_trie/_base.py | 37 + .../pip/_vendor/html5lib/_trie/datrie.py | 44 + .../pip/_vendor/html5lib/_trie/py.py | 67 + .../pip/_vendor/html5lib/_utils.py | 124 + .../pip/_vendor/html5lib/constants.py | 2947 ++++++ .../pip/_vendor/html5lib/filters/__init__.py | 0 .../filters/alphabeticalattributes.py | 29 + .../pip/_vendor/html5lib/filters/base.py | 12 + .../html5lib/filters/inject_meta_charset.py | 73 + .../pip/_vendor/html5lib/filters/lint.py | 93 + .../_vendor/html5lib/filters/optionaltags.py | 207 + .../pip/_vendor/html5lib/filters/sanitizer.py | 896 ++ .../_vendor/html5lib/filters/whitespace.py | 38 + .../pip/_vendor/html5lib/html5parser.py | 2791 ++++++ .../pip/_vendor/html5lib/serializer.py | 409 + .../_vendor/html5lib/treeadapters/__init__.py | 30 + .../_vendor/html5lib/treeadapters/genshi.py | 54 + .../pip/_vendor/html5lib/treeadapters/sax.py | 50 + .../_vendor/html5lib/treebuilders/__init__.py | 88 + .../pip/_vendor/html5lib/treebuilders/base.py | 417 + .../pip/_vendor/html5lib/treebuilders/dom.py | 236 + .../_vendor/html5lib/treebuilders/etree.py | 340 + .../html5lib/treebuilders/etree_lxml.py | 366 + .../_vendor/html5lib/treewalkers/__init__.py | 154 + .../pip/_vendor/html5lib/treewalkers/base.py | 252 + .../pip/_vendor/html5lib/treewalkers/dom.py | 43 + .../pip/_vendor/html5lib/treewalkers/etree.py | 130 + .../html5lib/treewalkers/etree_lxml.py | 213 + .../_vendor/html5lib/treewalkers/genshi.py | 69 + .../pip/_vendor/idna/__init__.py | 2 + .../site-packages/pip/_vendor/idna/codec.py | 118 + .../site-packages/pip/_vendor/idna/compat.py | 12 + .../site-packages/pip/_vendor/idna/core.py | 399 + .../pip/_vendor/idna/idnadata.py | 1893 ++++ .../pip/_vendor/idna/intranges.py | 53 + .../pip/_vendor/idna/package_data.py | 2 + .../pip/_vendor/idna/uts46data.py | 8179 +++++++++++++++++ .../site-packages/pip/_vendor/ipaddress.py | 2419 +++++ .../pip/_vendor/lockfile/__init__.py | 347 + .../pip/_vendor/lockfile/linklockfile.py | 73 + .../pip/_vendor/lockfile/mkdirlockfile.py | 84 + .../pip/_vendor/lockfile/pidlockfile.py | 190 + .../pip/_vendor/lockfile/sqlitelockfile.py | 156 + .../pip/_vendor/lockfile/symlinklockfile.py | 70 + .../pip/_vendor/msgpack/__init__.py | 66 + .../pip/_vendor/msgpack/_version.py | 1 + .../pip/_vendor/msgpack/exceptions.py | 41 + .../pip/_vendor/msgpack/fallback.py | 977 ++ .../pip/_vendor/packaging/__about__.py | 21 + .../pip/_vendor/packaging/__init__.py | 14 + .../pip/_vendor/packaging/_compat.py | 30 + .../pip/_vendor/packaging/_structures.py | 70 + .../pip/_vendor/packaging/markers.py | 301 + .../pip/_vendor/packaging/requirements.py | 130 + .../pip/_vendor/packaging/specifiers.py | 774 ++ .../pip/_vendor/packaging/utils.py | 63 + .../pip/_vendor/packaging/version.py | 441 + .../pip/_vendor/pep517/__init__.py | 4 + .../pip/_vendor/pep517/_in_process.py | 182 + .../site-packages/pip/_vendor/pep517/check.py | 194 + .../pip/_vendor/pep517/colorlog.py | 110 + .../pip/_vendor/pep517/compat.py | 23 + .../pip/_vendor/pep517/envbuild.py | 150 + .../pip/_vendor/pep517/wrappers.py | 134 + .../pip/_vendor/pkg_resources/__init__.py | 3149 +++++++ .../pip/_vendor/pkg_resources/py31compat.py | 23 + .../pip/_vendor/progress/__init__.py | 127 + .../site-packages/pip/_vendor/progress/bar.py | 94 + .../pip/_vendor/progress/counter.py | 48 + .../pip/_vendor/progress/helpers.py | 91 + .../pip/_vendor/progress/spinner.py | 44 + .../site-packages/pip/_vendor/pyparsing.py | 5742 ++++++++++++ .../pip/_vendor/pytoml/__init__.py | 3 + .../site-packages/pip/_vendor/pytoml/core.py | 13 + .../pip/_vendor/pytoml/parser.py | 374 + .../pip/_vendor/pytoml/writer.py | 127 + .../pip/_vendor/requests/__init__.py | 138 + .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 42 + .../pip/_vendor/requests/adapters.py | 530 ++ .../site-packages/pip/_vendor/requests/api.py | 152 + .../pip/_vendor/requests/auth.py | 305 + .../pip/_vendor/requests/certs.py | 18 + .../pip/_vendor/requests/compat.py | 75 + .../pip/_vendor/requests/cookies.py | 546 ++ .../pip/_vendor/requests/exceptions.py | 126 + .../pip/_vendor/requests/help.py | 120 + .../pip/_vendor/requests/hooks.py | 34 + .../pip/_vendor/requests/models.py | 952 ++ .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 741 ++ .../pip/_vendor/requests/status_codes.py | 120 + .../pip/_vendor/requests/structures.py | 103 + .../pip/_vendor/requests/utils.py | 976 ++ .../Lib/site-packages/pip/_vendor/retrying.py | 267 + venv/Lib/site-packages/pip/_vendor/six.py | 891 ++ .../pip/_vendor/urllib3/__init__.py | 97 + .../pip/_vendor/urllib3/_collections.py | 332 + .../pip/_vendor/urllib3/connection.py | 403 + .../pip/_vendor/urllib3/connectionpool.py | 906 ++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../contrib/_securetransport/__init__.py | 0 .../contrib/_securetransport/bindings.py | 593 ++ .../contrib/_securetransport/low_level.py | 346 + .../pip/_vendor/urllib3/contrib/appengine.py | 305 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 112 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 457 + .../urllib3/contrib/securetransport.py | 804 ++ .../pip/_vendor/urllib3/contrib/socks.py | 192 + .../pip/_vendor/urllib3/exceptions.py | 246 + .../pip/_vendor/urllib3/fields.py | 178 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 5 + .../urllib3/packages/backports/__init__.py | 0 .../urllib3/packages/backports/makefile.py | 53 + .../_vendor/urllib3/packages/ordered_dict.py | 259 + .../pip/_vendor/urllib3/packages/six.py | 868 ++ .../packages/ssl_match_hostname/__init__.py | 19 + .../ssl_match_hostname/_implementation.py | 157 + .../pip/_vendor/urllib3/poolmanager.py | 449 + .../pip/_vendor/urllib3/request.py | 150 + .../pip/_vendor/urllib3/response.py | 676 ++ .../pip/_vendor/urllib3/util/__init__.py | 54 + .../pip/_vendor/urllib3/util/connection.py | 126 + .../pip/_vendor/urllib3/util/queue.py | 21 + .../pip/_vendor/urllib3/util/request.py | 118 + .../pip/_vendor/urllib3/util/response.py | 81 + .../pip/_vendor/urllib3/util/retry.py | 411 + .../pip/_vendor/urllib3/util/ssl_.py | 396 + .../pip/_vendor/urllib3/util/timeout.py | 242 + .../pip/_vendor/urllib3/util/url.py | 230 + .../pip/_vendor/urllib3/util/wait.py | 153 + .../pip/_vendor/webencodings/__init__.py | 342 + .../pip/_vendor/webencodings/labels.py | 231 + .../pip/_vendor/webencodings/mklabels.py | 59 + .../pip/_vendor/webencodings/tests.py | 153 + .../_vendor/webencodings/x_user_defined.py | 325 + .../site-packages/setuptools-28.8.0-py3.6.egg | Bin 0 -> 465038 bytes venv/Lib/site-packages/setuptools.pth | 1 + venv/Lib/site-packages/sqlalchemy/__init__.py | 148 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 2915 bytes .../__pycache__/events.cpython-36.pyc | Bin 0 -> 54321 bytes .../sqlalchemy/__pycache__/exc.cpython-36.pyc | Bin 0 -> 17214 bytes .../__pycache__/inspection.cpython-36.pyc | Bin 0 -> 2953 bytes .../__pycache__/interfaces.cpython-36.pyc | Bin 0 -> 11174 bytes .../sqlalchemy/__pycache__/log.cpython-36.pyc | Bin 0 -> 6591 bytes .../__pycache__/pool.cpython-36.pyc | Bin 0 -> 47794 bytes .../__pycache__/processors.cpython-36.pyc | Bin 0 -> 4124 bytes .../__pycache__/schema.cpython-36.pyc | Bin 0 -> 1463 bytes .../__pycache__/types.cpython-36.pyc | Bin 0 -> 1683 bytes .../sqlalchemy/connectors/__init__.py | 10 + .../sqlalchemy/connectors/mxodbc.py | 150 + .../sqlalchemy/connectors/pyodbc.py | 164 + .../sqlalchemy/connectors/zxJDBC.py | 60 + .../sqlalchemy/cprocessors.cp36-win_amd64.pyd | Bin 0 -> 17408 bytes .../cresultproxy.cp36-win_amd64.pyd | Bin 0 -> 19456 bytes .../sqlalchemy/cutils.cp36-win_amd64.pyd | Bin 0 -> 12800 bytes .../sqlalchemy/databases/__init__.py | 30 + .../sqlalchemy/dialects/__init__.py | 56 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 1198 bytes .../sqlalchemy/dialects/firebird/__init__.py | 20 + .../sqlalchemy/dialects/firebird/base.py | 741 ++ .../sqlalchemy/dialects/firebird/fdb.py | 118 + .../dialects/firebird/kinterbasdb.py | 184 + .../sqlalchemy/dialects/mssql/__init__.py | 26 + .../sqlalchemy/dialects/mssql/adodbapi.py | 87 + .../sqlalchemy/dialects/mssql/base.py | 2295 +++++ .../dialects/mssql/information_schema.py | 139 + .../sqlalchemy/dialects/mssql/mxodbc.py | 139 + .../sqlalchemy/dialects/mssql/pymssql.py | 116 + .../sqlalchemy/dialects/mssql/pyodbc.py | 310 + .../sqlalchemy/dialects/mssql/zxjdbc.py | 69 + .../sqlalchemy/dialects/mysql/__init__.py | 34 + .../sqlalchemy/dialects/mysql/base.py | 2438 +++++ .../sqlalchemy/dialects/mysql/cymysql.py | 76 + .../sqlalchemy/dialects/mysql/dml.py | 80 + .../sqlalchemy/dialects/mysql/enumerated.py | 311 + .../sqlalchemy/dialects/mysql/gaerdbms.py | 102 + .../sqlalchemy/dialects/mysql/json.py | 79 + .../dialects/mysql/mysqlconnector.py | 251 + .../sqlalchemy/dialects/mysql/mysqldb.py | 210 + .../sqlalchemy/dialects/mysql/oursql.py | 243 + .../sqlalchemy/dialects/mysql/pymysql.py | 70 + .../sqlalchemy/dialects/mysql/pyodbc.py | 79 + .../sqlalchemy/dialects/mysql/reflection.py | 479 + .../sqlalchemy/dialects/mysql/types.py | 766 ++ .../sqlalchemy/dialects/mysql/zxjdbc.py | 117 + .../sqlalchemy/dialects/oracle/__init__.py | 24 + .../sqlalchemy/dialects/oracle/base.py | 1779 ++++ .../sqlalchemy/dialects/oracle/cx_oracle.py | 1040 +++ .../sqlalchemy/dialects/oracle/zxjdbc.py | 235 + .../dialects/postgresql/__init__.py | 37 + .../sqlalchemy/dialects/postgresql/array.py | 303 + .../sqlalchemy/dialects/postgresql/base.py | 3171 +++++++ .../sqlalchemy/dialects/postgresql/dml.py | 214 + .../sqlalchemy/dialects/postgresql/ext.py | 230 + .../sqlalchemy/dialects/postgresql/hstore.py | 420 + .../sqlalchemy/dialects/postgresql/json.py | 300 + .../sqlalchemy/dialects/postgresql/pg8000.py | 295 + .../dialects/postgresql/psycopg2.py | 740 ++ .../dialects/postgresql/psycopg2cffi.py | 61 + .../dialects/postgresql/pygresql.py | 243 + .../dialects/postgresql/pypostgresql.py | 97 + .../sqlalchemy/dialects/postgresql/ranges.py | 172 + .../sqlalchemy/dialects/postgresql/zxjdbc.py | 46 + .../sqlalchemy/dialects/sqlite/__init__.py | 21 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 775 bytes .../sqlite/__pycache__/base.cpython-36.pyc | Bin 0 -> 50831 bytes .../__pycache__/pysqlcipher.cpython-36.pyc | Bin 0 -> 4965 bytes .../__pycache__/pysqlite.cpython-36.pyc | Bin 0 -> 15362 bytes .../sqlalchemy/dialects/sqlite/base.py | 1585 ++++ .../sqlalchemy/dialects/sqlite/pysqlcipher.py | 130 + .../sqlalchemy/dialects/sqlite/pysqlite.py | 377 + .../sqlalchemy/dialects/sybase/__init__.py | 27 + .../sqlalchemy/dialects/sybase/base.py | 839 ++ .../sqlalchemy/dialects/sybase/mxodbc.py | 33 + .../sqlalchemy/dialects/sybase/pyodbc.py | 86 + .../sqlalchemy/dialects/sybase/pysybase.py | 102 + .../sqlalchemy/engine/__init__.py | 472 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 20526 bytes .../engine/__pycache__/base.cpython-36.pyc | Bin 0 -> 67856 bytes .../engine/__pycache__/default.cpython-36.pyc | Bin 0 -> 36124 bytes .../__pycache__/interfaces.cpython-36.pyc | Bin 0 -> 41112 bytes .../__pycache__/reflection.cpython-36.pyc | Bin 0 -> 25622 bytes .../engine/__pycache__/result.cpython-36.pyc | Bin 0 -> 44084 bytes .../__pycache__/strategies.cpython-36.pyc | Bin 0 -> 8468 bytes .../__pycache__/threadlocal.cpython-36.pyc | Bin 0 -> 4593 bytes .../engine/__pycache__/url.cpython-36.pyc | Bin 0 -> 8467 bytes .../engine/__pycache__/util.cpython-36.pyc | Bin 0 -> 1761 bytes .../site-packages/sqlalchemy/engine/base.py | 2234 +++++ .../sqlalchemy/engine/default.py | 1364 +++ .../sqlalchemy/engine/interfaces.py | 1305 +++ .../sqlalchemy/engine/reflection.py | 877 ++ .../site-packages/sqlalchemy/engine/result.py | 1448 +++ .../sqlalchemy/engine/strategies.py | 285 + .../sqlalchemy/engine/threadlocal.py | 138 + .../site-packages/sqlalchemy/engine/url.py | 288 + .../site-packages/sqlalchemy/engine/util.py | 74 + .../sqlalchemy/event/__init__.py | 11 + .../event/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 462 bytes .../event/__pycache__/api.cpython-36.pyc | Bin 0 -> 6168 bytes .../event/__pycache__/attr.cpython-36.pyc | Bin 0 -> 14730 bytes .../event/__pycache__/base.cpython-36.pyc | Bin 0 -> 9153 bytes .../event/__pycache__/legacy.cpython-36.pyc | Bin 0 -> 4842 bytes .../event/__pycache__/registry.cpython-36.pyc | Bin 0 -> 5568 bytes .../Lib/site-packages/sqlalchemy/event/api.py | 188 + .../site-packages/sqlalchemy/event/attr.py | 401 + .../site-packages/sqlalchemy/event/base.py | 289 + .../site-packages/sqlalchemy/event/legacy.py | 169 + .../sqlalchemy/event/registry.py | 262 + venv/Lib/site-packages/sqlalchemy/events.py | 1274 +++ venv/Lib/site-packages/sqlalchemy/exc.py | 480 + .../site-packages/sqlalchemy/ext/__init__.py | 11 + .../ext/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 281 bytes .../ext/__pycache__/baked.cpython-36.pyc | Bin 0 -> 18706 bytes .../sqlalchemy/ext/associationproxy.py | 1145 +++ .../site-packages/sqlalchemy/ext/automap.py | 1055 +++ .../Lib/site-packages/sqlalchemy/ext/baked.py | 585 ++ .../site-packages/sqlalchemy/ext/compiler.py | 474 + .../sqlalchemy/ext/declarative/__init__.py | 18 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 611 bytes .../__pycache__/api.cpython-36.pyc | Bin 0 -> 25388 bytes .../__pycache__/base.cpython-36.pyc | Bin 0 -> 17923 bytes .../__pycache__/clsregistry.cpython-36.pyc | Bin 0 -> 10149 bytes .../sqlalchemy/ext/declarative/api.py | 731 ++ .../sqlalchemy/ext/declarative/base.py | 770 ++ .../sqlalchemy/ext/declarative/clsregistry.py | 328 + .../sqlalchemy/ext/horizontal_shard.py | 196 + .../site-packages/sqlalchemy/ext/hybrid.py | 1149 +++ .../site-packages/sqlalchemy/ext/indexable.py | 349 + .../sqlalchemy/ext/instrumentation.py | 414 + .../site-packages/sqlalchemy/ext/mutable.py | 950 ++ .../sqlalchemy/ext/orderinglist.py | 380 + .../sqlalchemy/ext/serializer.py | 159 + .../site-packages/sqlalchemy/inspection.py | 93 + .../site-packages/sqlalchemy/interfaces.py | 312 + venv/Lib/site-packages/sqlalchemy/log.py | 217 + .../site-packages/sqlalchemy/orm/__init__.py | 297 + .../orm/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 8465 bytes .../orm/__pycache__/attributes.cpython-36.pyc | Bin 0 -> 49310 bytes .../orm/__pycache__/base.cpython-36.pyc | Bin 0 -> 12836 bytes .../__pycache__/collections.cpython-36.pyc | Bin 0 -> 53322 bytes .../orm/__pycache__/dependency.cpython-36.pyc | Bin 0 -> 23288 bytes .../deprecated_interfaces.cpython-36.pyc | Bin 0 -> 18898 bytes .../descriptor_props.cpython-36.pyc | Bin 0 -> 28191 bytes .../orm/__pycache__/dynamic.cpython-36.pyc | Bin 0 -> 11435 bytes .../orm/__pycache__/evaluator.cpython-36.pyc | Bin 0 -> 5791 bytes .../orm/__pycache__/events.cpython-36.pyc | Bin 0 -> 90870 bytes .../orm/__pycache__/exc.cpython-36.pyc | Bin 0 -> 6477 bytes .../orm/__pycache__/identity.cpython-36.pyc | Bin 0 -> 11408 bytes .../instrumentation.cpython-36.pyc | Bin 0 -> 17943 bytes .../orm/__pycache__/interfaces.cpython-36.pyc | Bin 0 -> 26213 bytes .../orm/__pycache__/loading.cpython-36.pyc | Bin 0 -> 17575 bytes .../orm/__pycache__/mapper.cpython-36.pyc | Bin 0 -> 89127 bytes .../__pycache__/path_registry.cpython-36.pyc | Bin 0 -> 9996 bytes .../__pycache__/persistence.cpython-36.pyc | Bin 0 -> 37162 bytes .../orm/__pycache__/properties.cpython-36.pyc | Bin 0 -> 10214 bytes .../orm/__pycache__/query.cpython-36.pyc | Bin 0 -> 134821 bytes .../__pycache__/relationships.cpython-36.pyc | Bin 0 -> 93046 bytes .../orm/__pycache__/scoping.cpython-36.pyc | Bin 0 -> 6347 bytes .../orm/__pycache__/session.cpython-36.pyc | Bin 0 -> 107134 bytes .../orm/__pycache__/state.cpython-36.pyc | Bin 0 -> 25536 bytes .../orm/__pycache__/strategies.cpython-36.pyc | Bin 0 -> 47031 bytes .../strategy_options.cpython-36.pyc | Bin 0 -> 39294 bytes .../orm/__pycache__/sync.cpython-36.pyc | Bin 0 -> 3604 bytes .../orm/__pycache__/unitofwork.cpython-36.pyc | Bin 0 -> 19821 bytes .../orm/__pycache__/util.cpython-36.pyc | Bin 0 -> 36108 bytes .../sqlalchemy/orm/attributes.py | 1698 ++++ venv/Lib/site-packages/sqlalchemy/orm/base.py | 547 ++ .../sqlalchemy/orm/collections.py | 1551 ++++ .../sqlalchemy/orm/dependency.py | 1165 +++ .../sqlalchemy/orm/deprecated_interfaces.py | 487 + .../sqlalchemy/orm/descriptor_props.py | 772 ++ .../site-packages/sqlalchemy/orm/dynamic.py | 379 + .../site-packages/sqlalchemy/orm/evaluator.py | 150 + .../site-packages/sqlalchemy/orm/events.py | 2275 +++++ venv/Lib/site-packages/sqlalchemy/orm/exc.py | 167 + .../site-packages/sqlalchemy/orm/identity.py | 355 + .../sqlalchemy/orm/instrumentation.py | 573 ++ .../sqlalchemy/orm/interfaces.py | 710 ++ .../site-packages/sqlalchemy/orm/loading.py | 881 ++ .../site-packages/sqlalchemy/orm/mapper.py | 3180 +++++++ .../sqlalchemy/orm/path_registry.py | 271 + .../sqlalchemy/orm/persistence.py | 1627 ++++ .../sqlalchemy/orm/properties.py | 277 + .../Lib/site-packages/sqlalchemy/orm/query.py | 4351 +++++++++ .../sqlalchemy/orm/relationships.py | 2913 ++++++ .../site-packages/sqlalchemy/orm/scoping.py | 181 + .../site-packages/sqlalchemy/orm/session.py | 3153 +++++++ .../Lib/site-packages/sqlalchemy/orm/state.py | 887 ++ .../sqlalchemy/orm/strategies.py | 2039 ++++ .../sqlalchemy/orm/strategy_options.py | 1493 +++ venv/Lib/site-packages/sqlalchemy/orm/sync.py | 140 + .../sqlalchemy/orm/unitofwork.py | 704 ++ venv/Lib/site-packages/sqlalchemy/orm/util.py | 1130 +++ venv/Lib/site-packages/sqlalchemy/pool.py | 1500 +++ .../site-packages/sqlalchemy/processors.py | 148 + venv/Lib/site-packages/sqlalchemy/schema.py | 70 + .../site-packages/sqlalchemy/sql/__init__.py | 101 + .../sql/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 2275 bytes .../sql/__pycache__/annotation.cpython-36.pyc | Bin 0 -> 5732 bytes .../sql/__pycache__/base.cpython-36.pyc | Bin 0 -> 23345 bytes .../sql/__pycache__/compiler.cpython-36.pyc | Bin 0 -> 91153 bytes .../sql/__pycache__/crud.cpython-36.pyc | Bin 0 -> 15377 bytes .../sql/__pycache__/ddl.cpython-36.pyc | Bin 0 -> 37395 bytes .../default_comparator.cpython-36.pyc | Bin 0 -> 6921 bytes .../sql/__pycache__/dml.cpython-36.pyc | Bin 0 -> 32522 bytes .../sql/__pycache__/elements.cpython-36.pyc | Bin 0 -> 147645 bytes .../sql/__pycache__/expression.cpython-36.pyc | Bin 0 -> 5458 bytes .../sql/__pycache__/functions.cpython-36.pyc | Bin 0 -> 27588 bytes .../sql/__pycache__/naming.cpython-36.pyc | Bin 0 -> 4291 bytes .../sql/__pycache__/operators.cpython-36.pyc | Bin 0 -> 45700 bytes .../sql/__pycache__/schema.cpython-36.pyc | Bin 0 -> 143059 bytes .../sql/__pycache__/selectable.cpython-36.pyc | Bin 0 -> 122398 bytes .../sql/__pycache__/sqltypes.cpython-36.pyc | Bin 0 -> 87757 bytes .../sql/__pycache__/type_api.cpython-36.pyc | Bin 0 -> 47762 bytes .../sql/__pycache__/util.cpython-36.pyc | Bin 0 -> 21713 bytes .../sql/__pycache__/visitors.cpython-36.pyc | Bin 0 -> 10260 bytes .../sqlalchemy/sql/annotation.py | 206 + venv/Lib/site-packages/sqlalchemy/sql/base.py | 636 ++ .../site-packages/sqlalchemy/sql/compiler.py | 3232 +++++++ venv/Lib/site-packages/sqlalchemy/sql/crud.py | 700 ++ venv/Lib/site-packages/sqlalchemy/sql/ddl.py | 1138 +++ .../sqlalchemy/sql/default_comparator.py | 325 + venv/Lib/site-packages/sqlalchemy/sql/dml.py | 879 ++ .../site-packages/sqlalchemy/sql/elements.py | 4470 +++++++++ .../sqlalchemy/sql/expression.py | 145 + .../site-packages/sqlalchemy/sql/functions.py | 883 ++ .../site-packages/sqlalchemy/sql/naming.py | 146 + .../site-packages/sqlalchemy/sql/operators.py | 1452 +++ .../site-packages/sqlalchemy/sql/schema.py | 4147 +++++++++ .../sqlalchemy/sql/selectable.py | 3729 ++++++++ .../site-packages/sqlalchemy/sql/sqltypes.py | 2734 ++++++ .../site-packages/sqlalchemy/sql/type_api.py | 1413 +++ venv/Lib/site-packages/sqlalchemy/sql/util.py | 766 ++ .../site-packages/sqlalchemy/sql/visitors.py | 328 + .../sqlalchemy/testing/__init__.py | 36 + .../sqlalchemy/testing/assertions.py | 543 ++ .../sqlalchemy/testing/assertsql.py | 399 + .../sqlalchemy/testing/config.py | 101 + .../sqlalchemy/testing/engines.py | 371 + .../sqlalchemy/testing/entities.py | 101 + .../sqlalchemy/testing/exclusions.py | 446 + .../sqlalchemy/testing/fixtures.py | 386 + .../site-packages/sqlalchemy/testing/mock.py | 21 + .../sqlalchemy/testing/pickleable.py | 143 + .../sqlalchemy/testing/plugin/__init__.py | 0 .../sqlalchemy/testing/plugin/bootstrap.py | 44 + .../sqlalchemy/testing/plugin/noseplugin.py | 107 + .../sqlalchemy/testing/plugin/plugin_base.py | 586 ++ .../sqlalchemy/testing/plugin/pytestplugin.py | 210 + .../sqlalchemy/testing/profiling.py | 265 + .../sqlalchemy/testing/provision.py | 423 + .../sqlalchemy/testing/replay_fixture.py | 172 + .../sqlalchemy/testing/requirements.py | 924 ++ .../sqlalchemy/testing/runner.py | 50 + .../sqlalchemy/testing/schema.py | 100 + .../sqlalchemy/testing/suite/__init__.py | 11 + .../sqlalchemy/testing/suite/test_cte.py | 193 + .../sqlalchemy/testing/suite/test_ddl.py | 65 + .../sqlalchemy/testing/suite/test_dialect.py | 120 + .../sqlalchemy/testing/suite/test_insert.py | 319 + .../testing/suite/test_reflection.py | 998 ++ .../sqlalchemy/testing/suite/test_results.py | 367 + .../sqlalchemy/testing/suite/test_select.py | 515 ++ .../sqlalchemy/testing/suite/test_sequence.py | 148 + .../sqlalchemy/testing/suite/test_types.py | 1005 ++ .../testing/suite/test_update_delete.py | 63 + .../site-packages/sqlalchemy/testing/util.py | 280 + .../sqlalchemy/testing/warnings.py | 41 + venv/Lib/site-packages/sqlalchemy/types.py | 81 + .../site-packages/sqlalchemy/util/__init__.py | 49 + .../util/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 3421 bytes .../__pycache__/_collections.cpython-36.pyc | Bin 0 -> 37036 bytes .../util/__pycache__/compat.cpython-36.pyc | Bin 0 -> 8936 bytes .../__pycache__/deprecations.cpython-36.pyc | Bin 0 -> 4593 bytes .../__pycache__/langhelpers.cpython-36.pyc | Bin 0 -> 42056 bytes .../util/__pycache__/queue.cpython-36.pyc | Bin 0 -> 5826 bytes .../__pycache__/topological.cpython-36.pyc | Bin 0 -> 1992 bytes .../sqlalchemy/util/_collections.py | 1056 +++ .../site-packages/sqlalchemy/util/compat.py | 338 + .../sqlalchemy/util/deprecations.py | 146 + .../sqlalchemy/util/langhelpers.py | 1422 +++ .../site-packages/sqlalchemy/util/queue.py | 199 + .../sqlalchemy/util/topological.py | 100 + .../visitor-0.1.3-py3.6.egg-info/PKG-INFO | 103 + .../visitor-0.1.3-py3.6.egg-info/SOURCES.txt | 10 + .../dependency_links.txt | 1 + .../installed-files.txt | 6 + .../top_level.txt | 1 + venv/Lib/site-packages/visitor/__init__.py | 51 + venv/Lib/site-packages/werkzeug/__init__.py | 151 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 4739 bytes .../__pycache__/_compat.cpython-36.pyc | Bin 0 -> 7142 bytes .../__pycache__/_internal.cpython-36.pyc | Bin 0 -> 12628 bytes .../__pycache__/datastructures.cpython-36.pyc | Bin 0 -> 100079 bytes .../__pycache__/exceptions.cpython-36.pyc | Bin 0 -> 22973 bytes .../__pycache__/filesystem.cpython-36.pyc | Bin 0 -> 2250 bytes .../__pycache__/formparser.cpython-36.pyc | Bin 0 -> 16196 bytes .../werkzeug/__pycache__/http.cpython-36.pyc | Bin 0 -> 33502 bytes .../werkzeug/__pycache__/local.cpython-36.pyc | Bin 0 -> 18719 bytes .../__pycache__/routing.cpython-36.pyc | Bin 0 -> 60193 bytes .../__pycache__/security.cpython-36.pyc | Bin 0 -> 8561 bytes .../__pycache__/serving.cpython-36.pyc | Bin 0 -> 26545 bytes .../werkzeug/__pycache__/test.cpython-36.pyc | Bin 0 -> 31079 bytes .../werkzeug/__pycache__/urls.cpython-36.pyc | Bin 0 -> 33353 bytes .../werkzeug/__pycache__/utils.cpython-36.pyc | Bin 0 -> 21206 bytes .../__pycache__/wrappers.cpython-36.pyc | Bin 0 -> 76453 bytes .../werkzeug/__pycache__/wsgi.cpython-36.pyc | Bin 0 -> 44708 bytes venv/Lib/site-packages/werkzeug/_compat.py | 206 + venv/Lib/site-packages/werkzeug/_internal.py | 419 + venv/Lib/site-packages/werkzeug/_reloader.py | 277 + .../werkzeug/contrib/__init__.py | 16 + .../site-packages/werkzeug/contrib/atom.py | 355 + .../site-packages/werkzeug/contrib/cache.py | 913 ++ .../site-packages/werkzeug/contrib/fixers.py | 254 + .../site-packages/werkzeug/contrib/iterio.py | 352 + .../werkzeug/contrib/jsrouting.py | 264 + .../site-packages/werkzeug/contrib/limiter.py | 41 + .../site-packages/werkzeug/contrib/lint.py | 343 + .../werkzeug/contrib/profiler.py | 147 + .../werkzeug/contrib/securecookie.py | 323 + .../werkzeug/contrib/sessions.py | 352 + .../werkzeug/contrib/testtools.py | 73 + .../werkzeug/contrib/wrappers.py | 284 + .../site-packages/werkzeug/datastructures.py | 2762 ++++++ .../site-packages/werkzeug/debug/__init__.py | 470 + .../site-packages/werkzeug/debug/console.py | 215 + venv/Lib/site-packages/werkzeug/debug/repr.py | 280 + .../werkzeug/debug/shared/FONT_LICENSE | 96 + .../werkzeug/debug/shared/console.png | Bin 0 -> 507 bytes .../werkzeug/debug/shared/debugger.js | 205 + .../werkzeug/debug/shared/jquery.js | 5 + .../werkzeug/debug/shared/less.png | Bin 0 -> 191 bytes .../werkzeug/debug/shared/more.png | Bin 0 -> 200 bytes .../werkzeug/debug/shared/source.png | Bin 0 -> 818 bytes .../werkzeug/debug/shared/style.css | 143 + .../werkzeug/debug/shared/ubuntu.ttf | Bin 0 -> 70220 bytes .../site-packages/werkzeug/debug/tbtools.py | 556 ++ venv/Lib/site-packages/werkzeug/exceptions.py | 719 ++ venv/Lib/site-packages/werkzeug/filesystem.py | 66 + venv/Lib/site-packages/werkzeug/formparser.py | 534 ++ venv/Lib/site-packages/werkzeug/http.py | 1158 +++ venv/Lib/site-packages/werkzeug/local.py | 420 + .../site-packages/werkzeug/posixemulation.py | 106 + venv/Lib/site-packages/werkzeug/routing.py | 1792 ++++ venv/Lib/site-packages/werkzeug/script.py | 318 + venv/Lib/site-packages/werkzeug/security.py | 270 + venv/Lib/site-packages/werkzeug/serving.py | 862 ++ venv/Lib/site-packages/werkzeug/test.py | 948 ++ venv/Lib/site-packages/werkzeug/testapp.py | 230 + venv/Lib/site-packages/werkzeug/urls.py | 1007 ++ venv/Lib/site-packages/werkzeug/useragents.py | 212 + venv/Lib/site-packages/werkzeug/utils.py | 628 ++ venv/Lib/site-packages/werkzeug/websocket.py | 337 + venv/Lib/site-packages/werkzeug/wrappers.py | 2028 ++++ venv/Lib/site-packages/werkzeug/wsgi.py | 1364 +++ venv/Lib/site-packages/wtforms/__init__.py | 16 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 615 bytes .../wtforms/__pycache__/compat.cpython-36.pyc | Bin 0 -> 969 bytes .../wtforms/__pycache__/form.cpython-36.pyc | Bin 0 -> 11541 bytes .../wtforms/__pycache__/i18n.cpython-36.pyc | Bin 0 -> 2808 bytes .../wtforms/__pycache__/meta.cpython-36.pyc | Bin 0 -> 3848 bytes .../wtforms/__pycache__/utils.cpython-36.pyc | Bin 0 -> 2593 bytes .../__pycache__/validators.cpython-36.pyc | Bin 0 -> 20477 bytes venv/Lib/site-packages/wtforms/compat.py | 32 + .../site-packages/wtforms/csrf/__init__.py | 0 .../csrf/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 183 bytes .../csrf/__pycache__/core.cpython-36.pyc | Bin 0 -> 3970 bytes venv/Lib/site-packages/wtforms/csrf/core.py | 98 + .../Lib/site-packages/wtforms/csrf/session.py | 88 + .../Lib/site-packages/wtforms/ext/__init__.py | 0 .../wtforms/ext/appengine/__init__.py | 8 + .../site-packages/wtforms/ext/appengine/db.py | 464 + .../wtforms/ext/appengine/fields.py | 215 + .../wtforms/ext/appengine/ndb.py | 418 + .../wtforms/ext/csrf/__init__.py | 1 + .../site-packages/wtforms/ext/csrf/fields.py | 18 + .../site-packages/wtforms/ext/csrf/form.py | 53 + .../site-packages/wtforms/ext/csrf/session.py | 71 + .../wtforms/ext/dateutil/__init__.py | 0 .../wtforms/ext/dateutil/fields.py | 97 + .../wtforms/ext/django/__init__.py | 8 + .../wtforms/ext/django/fields.py | 133 + .../site-packages/wtforms/ext/django/i18n.py | 24 + .../site-packages/wtforms/ext/django/orm.py | 168 + .../ext/django/templatetags/__init__.py | 0 .../ext/django/templatetags/wtforms.py | 80 + .../wtforms/ext/i18n/__init__.py | 0 .../site-packages/wtforms/ext/i18n/form.py | 45 + .../site-packages/wtforms/ext/i18n/utils.py | 15 + .../wtforms/ext/sqlalchemy/__init__.py | 10 + .../wtforms/ext/sqlalchemy/fields.py | 190 + .../wtforms/ext/sqlalchemy/orm.py | 304 + .../site-packages/wtforms/fields/__init__.py | 7 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 403 bytes .../fields/__pycache__/core.cpython-36.pyc | Bin 0 -> 36091 bytes .../fields/__pycache__/simple.cpython-36.pyc | Bin 0 -> 3271 bytes venv/Lib/site-packages/wtforms/fields/core.py | 1007 ++ .../Lib/site-packages/wtforms/fields/html5.py | 95 + .../site-packages/wtforms/fields/simple.py | 84 + venv/Lib/site-packages/wtforms/form.py | 310 + venv/Lib/site-packages/wtforms/i18n.py | 75 + .../site-packages/wtforms/locale/README.md | 22 + .../wtforms/locale/ar/LC_MESSAGES/wtforms.mo | Bin 0 -> 4530 bytes .../wtforms/locale/ar/LC_MESSAGES/wtforms.po | 188 + .../wtforms/locale/bg/LC_MESSAGES/wtforms.mo | Bin 0 -> 4297 bytes .../wtforms/locale/bg/LC_MESSAGES/wtforms.po | 181 + .../wtforms/locale/ca/LC_MESSAGES/wtforms.mo | Bin 0 -> 3425 bytes .../wtforms/locale/ca/LC_MESSAGES/wtforms.po | 179 + .../locale/cs_CZ/LC_MESSAGES/wtforms.mo | Bin 0 -> 3618 bytes .../locale/cs_CZ/LC_MESSAGES/wtforms.po | 181 + .../wtforms/locale/cy/LC_MESSAGES/wtforms.mo | Bin 0 -> 3371 bytes .../wtforms/locale/cy/LC_MESSAGES/wtforms.po | 181 + .../wtforms/locale/de/LC_MESSAGES/wtforms.mo | Bin 0 -> 3412 bytes .../wtforms/locale/de/LC_MESSAGES/wtforms.po | 181 + .../locale/de_CH/LC_MESSAGES/wtforms.mo | Bin 0 -> 3418 bytes .../locale/de_CH/LC_MESSAGES/wtforms.po | 181 + .../wtforms/locale/el/LC_MESSAGES/wtforms.mo | Bin 0 -> 4307 bytes .../wtforms/locale/el/LC_MESSAGES/wtforms.po | 179 + .../wtforms/locale/en/LC_MESSAGES/wtforms.mo | Bin 0 -> 3285 bytes .../wtforms/locale/en/LC_MESSAGES/wtforms.po | 179 + .../wtforms/locale/es/LC_MESSAGES/wtforms.mo | Bin 0 -> 3394 bytes .../wtforms/locale/es/LC_MESSAGES/wtforms.po | 181 + .../wtforms/locale/et/LC_MESSAGES/wtforms.mo | Bin 0 -> 3456 bytes .../wtforms/locale/et/LC_MESSAGES/wtforms.po | 179 + .../wtforms/locale/fa/LC_MESSAGES/wtforms.mo | Bin 0 -> 4137 bytes .../wtforms/locale/fa/LC_MESSAGES/wtforms.po | 178 + .../wtforms/locale/fi/LC_MESSAGES/wtforms.mo | Bin 0 -> 3416 bytes .../wtforms/locale/fi/LC_MESSAGES/wtforms.po | 179 + .../wtforms/locale/fr/LC_MESSAGES/wtforms.mo | Bin 0 -> 3484 bytes .../wtforms/locale/fr/LC_MESSAGES/wtforms.po | 181 + .../wtforms/locale/he/LC_MESSAGES/wtforms.mo | Bin 0 -> 3649 bytes .../wtforms/locale/he/LC_MESSAGES/wtforms.po | 179 + .../wtforms/locale/hu/LC_MESSAGES/wtforms.mo | Bin 0 -> 3544 bytes .../wtforms/locale/hu/LC_MESSAGES/wtforms.po | 179 + .../wtforms/locale/it/LC_MESSAGES/wtforms.mo | Bin 0 -> 3510 bytes .../wtforms/locale/it/LC_MESSAGES/wtforms.po | 181 + .../wtforms/locale/ja/LC_MESSAGES/wtforms.mo | Bin 0 -> 3736 bytes .../wtforms/locale/ja/LC_MESSAGES/wtforms.po | 177 + .../wtforms/locale/ko/LC_MESSAGES/wtforms.mo | Bin 0 -> 3851 bytes .../wtforms/locale/ko/LC_MESSAGES/wtforms.po | 177 + .../wtforms/locale/nb/LC_MESSAGES/wtforms.mo | Bin 0 -> 3337 bytes .../wtforms/locale/nb/LC_MESSAGES/wtforms.po | 179 + .../wtforms/locale/nl/LC_MESSAGES/wtforms.mo | Bin 0 -> 3350 bytes .../wtforms/locale/nl/LC_MESSAGES/wtforms.po | 179 + .../wtforms/locale/pl/LC_MESSAGES/wtforms.mo | Bin 0 -> 3610 bytes .../wtforms/locale/pl/LC_MESSAGES/wtforms.po | 182 + .../wtforms/locale/pt/LC_MESSAGES/wtforms.mo | Bin 0 -> 3438 bytes .../wtforms/locale/pt/LC_MESSAGES/wtforms.po | 179 + .../wtforms/locale/ru/LC_MESSAGES/wtforms.mo | Bin 0 -> 4406 bytes .../wtforms/locale/ru/LC_MESSAGES/wtforms.po | 184 + .../wtforms/locale/sk/LC_MESSAGES/wtforms.mo | Bin 0 -> 3548 bytes .../wtforms/locale/sk/LC_MESSAGES/wtforms.po | 181 + .../wtforms/locale/sv/LC_MESSAGES/wtforms.mo | Bin 0 -> 3376 bytes .../wtforms/locale/sv/LC_MESSAGES/wtforms.po | 179 + .../wtforms/locale/tr/LC_MESSAGES/wtforms.mo | Bin 0 -> 3391 bytes .../wtforms/locale/tr/LC_MESSAGES/wtforms.po | 179 + .../wtforms/locale/uk/LC_MESSAGES/wtforms.mo | Bin 0 -> 4451 bytes .../wtforms/locale/uk/LC_MESSAGES/wtforms.po | 182 + .../site-packages/wtforms/locale/wtforms.pot | 178 + .../wtforms/locale/zh/LC_MESSAGES/wtforms.mo | Bin 0 -> 3362 bytes .../wtforms/locale/zh/LC_MESSAGES/wtforms.po | 179 + .../locale/zh_TW/LC_MESSAGES/wtforms.mo | Bin 0 -> 3204 bytes .../locale/zh_TW/LC_MESSAGES/wtforms.po | 177 + venv/Lib/site-packages/wtforms/meta.py | 121 + venv/Lib/site-packages/wtforms/utils.py | 54 + venv/Lib/site-packages/wtforms/validators.py | 582 ++ .../site-packages/wtforms/widgets/__init__.py | 4 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 294 bytes .../widgets/__pycache__/core.cpython-36.pyc | Bin 0 -> 12524 bytes .../Lib/site-packages/wtforms/widgets/core.py | 348 + .../site-packages/wtforms/widgets/html5.py | 124 + venv/Lib/tcl8.6/init.tcl | 818 ++ venv/Scripts/Activate.ps1 | 51 + venv/Scripts/_asyncio.pyd | Bin 0 -> 60568 bytes venv/Scripts/_asyncio_d.pyd | Bin 0 -> 109568 bytes venv/Scripts/_bz2.pyd | Bin 0 -> 94872 bytes venv/Scripts/_bz2_d.pyd | Bin 0 -> 180736 bytes venv/Scripts/_ctypes.pyd | Bin 0 -> 130712 bytes venv/Scripts/_ctypes_d.pyd | Bin 0 -> 253440 bytes venv/Scripts/_ctypes_test.pyd | Bin 0 -> 31896 bytes venv/Scripts/_ctypes_test_d.pyd | Bin 0 -> 56320 bytes venv/Scripts/_decimal.pyd | Bin 0 -> 268440 bytes venv/Scripts/_decimal_d.pyd | Bin 0 -> 478720 bytes venv/Scripts/_elementtree.pyd | Bin 0 -> 170136 bytes venv/Scripts/_elementtree_d.pyd | Bin 0 -> 421888 bytes venv/Scripts/_hashlib.pyd | Bin 0 -> 1664152 bytes venv/Scripts/_hashlib_d.pyd | Bin 0 -> 2323456 bytes venv/Scripts/_lzma.pyd | Bin 0 -> 254616 bytes venv/Scripts/_lzma_d.pyd | Bin 0 -> 318976 bytes venv/Scripts/_msi.pyd | Bin 0 -> 39576 bytes venv/Scripts/_msi_d.pyd | Bin 0 -> 64000 bytes venv/Scripts/_multiprocessing.pyd | Bin 0 -> 29848 bytes venv/Scripts/_multiprocessing_d.pyd | Bin 0 -> 52224 bytes venv/Scripts/_overlapped.pyd | Bin 0 -> 43160 bytes venv/Scripts/_overlapped_d.pyd | Bin 0 -> 65024 bytes venv/Scripts/_socket.pyd | Bin 0 -> 73368 bytes venv/Scripts/_socket_d.pyd | Bin 0 -> 112128 bytes venv/Scripts/_sqlite3.pyd | Bin 0 -> 85656 bytes venv/Scripts/_sqlite3_d.pyd | Bin 0 -> 140288 bytes venv/Scripts/_ssl.pyd | Bin 0 -> 2061464 bytes venv/Scripts/_ssl_d.pyd | Bin 0 -> 3041280 bytes venv/Scripts/_testbuffer.pyd | Bin 0 -> 52376 bytes venv/Scripts/_testbuffer_d.pyd | Bin 0 -> 96768 bytes venv/Scripts/_testcapi.pyd | Bin 0 -> 94360 bytes venv/Scripts/_testcapi_d.pyd | Bin 0 -> 164864 bytes venv/Scripts/_testconsole.pyd | Bin 0 -> 24216 bytes venv/Scripts/_testconsole_d.pyd | Bin 0 -> 43008 bytes venv/Scripts/_testimportmultiple.pyd | Bin 0 -> 22680 bytes venv/Scripts/_testimportmultiple_d.pyd | Bin 0 -> 40960 bytes venv/Scripts/_testmultiphase.pyd | Bin 0 -> 30872 bytes venv/Scripts/_testmultiphase_d.pyd | Bin 0 -> 54784 bytes venv/Scripts/_tkinter.pyd | Bin 0 -> 69784 bytes venv/Scripts/_tkinter_d.pyd | Bin 0 -> 112640 bytes venv/Scripts/activate | 76 + venv/Scripts/activate.bat | 32 + venv/Scripts/deactivate.bat | 21 + venv/Scripts/easy_install-3.6-script.py | 12 + venv/Scripts/easy_install-3.6.exe | Bin 0 -> 74752 bytes venv/Scripts/easy_install-script.py | 12 + venv/Scripts/easy_install.exe | Bin 0 -> 74752 bytes venv/Scripts/flask.exe | Bin 0 -> 98155 bytes venv/Scripts/pip-script.py | 12 + venv/Scripts/pip.exe | Bin 0 -> 74752 bytes venv/Scripts/pip3-script.py | 12 + venv/Scripts/pip3.6-script.py | 12 + venv/Scripts/pip3.6.exe | Bin 0 -> 74752 bytes venv/Scripts/pip3.exe | Bin 0 -> 74752 bytes venv/Scripts/pyexpat.pyd | Bin 0 -> 197784 bytes venv/Scripts/pyexpat_d.pyd | Bin 0 -> 406528 bytes venv/Scripts/python.exe | Bin 0 -> 100504 bytes venv/Scripts/python3.dll | Bin 0 -> 58520 bytes venv/Scripts/python36.dll | Bin 0 -> 3610776 bytes venv/Scripts/python36_d.dll | Bin 0 -> 7423488 bytes venv/Scripts/python3_d.dll | Bin 0 -> 69120 bytes venv/Scripts/python_d.exe | Bin 0 -> 135168 bytes venv/Scripts/pythonw.exe | Bin 0 -> 98968 bytes venv/Scripts/pythonw_d.exe | Bin 0 -> 133120 bytes venv/Scripts/select.pyd | Bin 0 -> 27288 bytes venv/Scripts/select_d.pyd | Bin 0 -> 48128 bytes venv/Scripts/sqlite3.dll | Bin 0 -> 1153176 bytes venv/Scripts/sqlite3_d.dll | Bin 0 -> 1495040 bytes venv/Scripts/tcl86t.dll | Bin 0 -> 1666048 bytes venv/Scripts/tcl86tg.dll | Bin 0 -> 2705920 bytes venv/Scripts/tk86t.dll | Bin 0 -> 1967104 bytes venv/Scripts/tk86tg.dll | Bin 0 -> 1949696 bytes venv/Scripts/unicodedata.pyd | Bin 0 -> 906392 bytes venv/Scripts/unicodedata_d.pyd | Bin 0 -> 1109504 bytes venv/Scripts/vcruntime140.dll | Bin 0 -> 87888 bytes venv/Scripts/winsound.pyd | Bin 0 -> 28312 bytes venv/Scripts/winsound_d.pyd | Bin 0 -> 47104 bytes venv/pip-selfcheck.json | 1 + venv/pyvenv.cfg | 3 + 1548 files changed, 387101 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/book_management_sys.iml create mode 100644 .idea/dataSources.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 __pycache__/forms.cpython-36.pyc create mode 100644 app.py create mode 100644 data.sqlite create mode 100644 forms.py create mode 100644 static/icon.jpg create mode 100644 static/layui/css/layui.css create mode 100644 static/layui/css/layui.mobile.css create mode 100644 static/layui/css/modules/code.css create mode 100644 static/layui/css/modules/laydate/default/laydate.css create mode 100644 static/layui/css/modules/layer/default/icon-ext.png create mode 100644 static/layui/css/modules/layer/default/icon.png create mode 100644 static/layui/css/modules/layer/default/layer.css create mode 100644 static/layui/css/modules/layer/default/loading-0.gif create mode 100644 static/layui/css/modules/layer/default/loading-1.gif create mode 100644 static/layui/css/modules/layer/default/loading-2.gif create mode 100644 static/layui/font/iconfont.eot create mode 100644 static/layui/font/iconfont.svg create mode 100644 static/layui/font/iconfont.ttf create mode 100644 static/layui/font/iconfont.woff create mode 100644 static/layui/images/face/0.gif create mode 100644 static/layui/images/face/1.gif create mode 100644 static/layui/images/face/10.gif create mode 100644 static/layui/images/face/11.gif create mode 100644 static/layui/images/face/12.gif create mode 100644 static/layui/images/face/13.gif create mode 100644 static/layui/images/face/14.gif create mode 100644 static/layui/images/face/15.gif create mode 100644 static/layui/images/face/16.gif create mode 100644 static/layui/images/face/17.gif create mode 100644 static/layui/images/face/18.gif create mode 100644 static/layui/images/face/19.gif create mode 100644 static/layui/images/face/2.gif create mode 100644 static/layui/images/face/20.gif create mode 100644 static/layui/images/face/21.gif create mode 100644 static/layui/images/face/22.gif create mode 100644 static/layui/images/face/23.gif create mode 100644 static/layui/images/face/24.gif create mode 100644 static/layui/images/face/25.gif create mode 100644 static/layui/images/face/26.gif create mode 100644 static/layui/images/face/27.gif create mode 100644 static/layui/images/face/28.gif create mode 100644 static/layui/images/face/29.gif create mode 100644 static/layui/images/face/3.gif create mode 100644 static/layui/images/face/30.gif create mode 100644 static/layui/images/face/31.gif create mode 100644 static/layui/images/face/32.gif create mode 100644 static/layui/images/face/33.gif create mode 100644 static/layui/images/face/34.gif create mode 100644 static/layui/images/face/35.gif create mode 100644 static/layui/images/face/36.gif create mode 100644 static/layui/images/face/37.gif create mode 100644 static/layui/images/face/38.gif create mode 100644 static/layui/images/face/39.gif create mode 100644 static/layui/images/face/4.gif create mode 100644 static/layui/images/face/40.gif create mode 100644 static/layui/images/face/41.gif create mode 100644 static/layui/images/face/42.gif create mode 100644 static/layui/images/face/43.gif create mode 100644 static/layui/images/face/44.gif create mode 100644 static/layui/images/face/45.gif create mode 100644 static/layui/images/face/46.gif create mode 100644 static/layui/images/face/47.gif create mode 100644 static/layui/images/face/48.gif create mode 100644 static/layui/images/face/49.gif create mode 100644 static/layui/images/face/5.gif create mode 100644 static/layui/images/face/50.gif create mode 100644 static/layui/images/face/51.gif create mode 100644 static/layui/images/face/52.gif create mode 100644 static/layui/images/face/53.gif create mode 100644 static/layui/images/face/54.gif create mode 100644 static/layui/images/face/55.gif create mode 100644 static/layui/images/face/56.gif create mode 100644 static/layui/images/face/57.gif create mode 100644 static/layui/images/face/58.gif create mode 100644 static/layui/images/face/59.gif create mode 100644 static/layui/images/face/6.gif create mode 100644 static/layui/images/face/60.gif create mode 100644 static/layui/images/face/61.gif create mode 100644 static/layui/images/face/62.gif create mode 100644 static/layui/images/face/63.gif create mode 100644 static/layui/images/face/64.gif create mode 100644 static/layui/images/face/65.gif create mode 100644 static/layui/images/face/66.gif create mode 100644 static/layui/images/face/67.gif create mode 100644 static/layui/images/face/68.gif create mode 100644 static/layui/images/face/69.gif create mode 100644 static/layui/images/face/7.gif create mode 100644 static/layui/images/face/70.gif create mode 100644 static/layui/images/face/71.gif create mode 100644 static/layui/images/face/8.gif create mode 100644 static/layui/images/face/9.gif create mode 100644 static/layui/lay/modules/carousel.js create mode 100644 static/layui/lay/modules/code.js create mode 100644 static/layui/lay/modules/colorpicker.js create mode 100644 static/layui/lay/modules/element.js create mode 100644 static/layui/lay/modules/flow.js create mode 100644 static/layui/lay/modules/form.js create mode 100644 static/layui/lay/modules/jquery.js create mode 100644 static/layui/lay/modules/laydate.js create mode 100644 static/layui/lay/modules/layedit.js create mode 100644 static/layui/lay/modules/layer.js create mode 100644 static/layui/lay/modules/laypage.js create mode 100644 static/layui/lay/modules/laytpl.js create mode 100644 static/layui/lay/modules/mobile.js create mode 100644 static/layui/lay/modules/rate.js create mode 100644 static/layui/lay/modules/slider.js create mode 100644 static/layui/lay/modules/table.js create mode 100644 static/layui/lay/modules/tree.js create mode 100644 static/layui/lay/modules/upload.js create mode 100644 static/layui/lay/modules/util.js create mode 100644 static/layui/layui.all.js create mode 100644 static/layui/layui.js create mode 100644 static/login.css create mode 100644 templates/base-user.html create mode 100644 templates/base.html create mode 100644 templates/base2.html create mode 100644 templates/borrow.html create mode 100644 templates/change-info.html create mode 100644 templates/change-password.html create mode 100644 templates/index.html create mode 100644 templates/login.html create mode 100644 templates/new-store.html create mode 100644 templates/return.html create mode 100644 templates/search-book.html create mode 100644 templates/search-student.html create mode 100644 templates/storage.html create mode 100644 templates/user-book.html create mode 100644 templates/user-info.html create mode 100644 templates/user-student.html create mode 100644 venv/Lib/site-packages/Click-7.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/Click-7.0.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/Click-7.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/Click-7.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/Click-7.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/Click-7.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/Flask-1.0.2.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/Flask-1.0.2.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/Flask-1.0.2.dist-info/METADATA create mode 100644 venv/Lib/site-packages/Flask-1.0.2.dist-info/RECORD create mode 100644 venv/Lib/site-packages/Flask-1.0.2.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/Flask-1.0.2.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/Flask-1.0.2.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/PKG-INFO create mode 100644 venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/SOURCES.txt create mode 100644 venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/dependency_links.txt create mode 100644 venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/installed-files.txt create mode 100644 venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/not-zip-safe create mode 100644 venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/requires.txt create mode 100644 venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/top_level.txt create mode 100644 venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/version_info.json create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/DESCRIPTION.rst create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/METADATA create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/RECORD create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/metadata.json create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/PKG-INFO create mode 100644 venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/SOURCES.txt create mode 100644 venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/dependency_links.txt create mode 100644 venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/installed-files.txt create mode 100644 venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/not-zip-safe create mode 100644 venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/requires.txt create mode 100644 venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/top_level.txt create mode 100644 venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/DESCRIPTION.rst create mode 100644 venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/METADATA create mode 100644 venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/RECORD create mode 100644 venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/metadata.json create mode 100644 venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/Jinja2-2.10.dist-info/DESCRIPTION.rst create mode 100644 venv/Lib/site-packages/Jinja2-2.10.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/Jinja2-2.10.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/Jinja2-2.10.dist-info/METADATA create mode 100644 venv/Lib/site-packages/Jinja2-2.10.dist-info/RECORD create mode 100644 venv/Lib/site-packages/Jinja2-2.10.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/Jinja2-2.10.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/Jinja2-2.10.dist-info/metadata.json create mode 100644 venv/Lib/site-packages/Jinja2-2.10.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/LICENSE.rst create mode 100644 venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/PKG-INFO create mode 100644 venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/SOURCES.txt create mode 100644 venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/dependency_links.txt create mode 100644 venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/installed-files.txt create mode 100644 venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/requires.txt create mode 100644 venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/top_level.txt create mode 100644 venv/Lib/site-packages/WTForms-2.2.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/WTForms-2.2.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/WTForms-2.2.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/WTForms-2.2.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/WTForms-2.2.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/DESCRIPTION.rst create mode 100644 venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/metadata.json create mode 100644 venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/click/__init__.py create mode 100644 venv/Lib/site-packages/click/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_unicodefun.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_winconsole.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/core.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/decorators.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/exceptions.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/formatting.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/globals.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/parser.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/termui.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/types.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/utils.cpython-36.pyc create mode 100644 venv/Lib/site-packages/click/_bashcomplete.py create mode 100644 venv/Lib/site-packages/click/_compat.py create mode 100644 venv/Lib/site-packages/click/_termui_impl.py create mode 100644 venv/Lib/site-packages/click/_textwrap.py create mode 100644 venv/Lib/site-packages/click/_unicodefun.py create mode 100644 venv/Lib/site-packages/click/_winconsole.py create mode 100644 venv/Lib/site-packages/click/core.py create mode 100644 venv/Lib/site-packages/click/decorators.py create mode 100644 venv/Lib/site-packages/click/exceptions.py create mode 100644 venv/Lib/site-packages/click/formatting.py create mode 100644 venv/Lib/site-packages/click/globals.py create mode 100644 venv/Lib/site-packages/click/parser.py create mode 100644 venv/Lib/site-packages/click/termui.py create mode 100644 venv/Lib/site-packages/click/testing.py create mode 100644 venv/Lib/site-packages/click/types.py create mode 100644 venv/Lib/site-packages/click/utils.py create mode 100644 venv/Lib/site-packages/dominate-2.3.5.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/dominate-2.3.5.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/dominate-2.3.5.dist-info/METADATA create mode 100644 venv/Lib/site-packages/dominate-2.3.5.dist-info/RECORD create mode 100644 venv/Lib/site-packages/dominate-2.3.5.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/dominate-2.3.5.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/dominate/__init__.py create mode 100644 venv/Lib/site-packages/dominate/_version.py create mode 100644 venv/Lib/site-packages/dominate/document.py create mode 100644 venv/Lib/site-packages/dominate/dom1core.py create mode 100644 venv/Lib/site-packages/dominate/dom_tag.py create mode 100644 venv/Lib/site-packages/dominate/tags.py create mode 100644 venv/Lib/site-packages/dominate/util.py create mode 100644 venv/Lib/site-packages/easy-install.pth create mode 100644 venv/Lib/site-packages/flask/__init__.py create mode 100644 venv/Lib/site-packages/flask/__main__.py create mode 100644 venv/Lib/site-packages/flask/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/_compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/app.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/blueprints.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/cli.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/config.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/ctx.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/globals.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/helpers.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/logging.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/sessions.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/signals.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/templating.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/wrappers.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/_compat.py create mode 100644 venv/Lib/site-packages/flask/app.py create mode 100644 venv/Lib/site-packages/flask/blueprints.py create mode 100644 venv/Lib/site-packages/flask/cli.py create mode 100644 venv/Lib/site-packages/flask/config.py create mode 100644 venv/Lib/site-packages/flask/ctx.py create mode 100644 venv/Lib/site-packages/flask/debughelpers.py create mode 100644 venv/Lib/site-packages/flask/globals.py create mode 100644 venv/Lib/site-packages/flask/helpers.py create mode 100644 venv/Lib/site-packages/flask/json/__init__.py create mode 100644 venv/Lib/site-packages/flask/json/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/json/__pycache__/tag.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask/json/tag.py create mode 100644 venv/Lib/site-packages/flask/logging.py create mode 100644 venv/Lib/site-packages/flask/sessions.py create mode 100644 venv/Lib/site-packages/flask/signals.py create mode 100644 venv/Lib/site-packages/flask/templating.py create mode 100644 venv/Lib/site-packages/flask/testing.py create mode 100644 venv/Lib/site-packages/flask/views.py create mode 100644 venv/Lib/site-packages/flask/wrappers.py create mode 100644 venv/Lib/site-packages/flask_login/__about__.py create mode 100644 venv/Lib/site-packages/flask_login/__init__.py create mode 100644 venv/Lib/site-packages/flask_login/__pycache__/__about__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_login/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_login/__pycache__/_compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_login/__pycache__/config.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_login/__pycache__/login_manager.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_login/__pycache__/mixins.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_login/__pycache__/signals.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_login/__pycache__/utils.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_login/_compat.py create mode 100644 venv/Lib/site-packages/flask_login/config.py create mode 100644 venv/Lib/site-packages/flask_login/login_manager.py create mode 100644 venv/Lib/site-packages/flask_login/mixins.py create mode 100644 venv/Lib/site-packages/flask_login/signals.py create mode 100644 venv/Lib/site-packages/flask_login/utils.py create mode 100644 venv/Lib/site-packages/flask_script/__init__.py create mode 100644 venv/Lib/site-packages/flask_script/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_script/__pycache__/_compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_script/__pycache__/cli.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_script/__pycache__/commands.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_script/_compat.py create mode 100644 venv/Lib/site-packages/flask_script/cli.py create mode 100644 venv/Lib/site-packages/flask_script/commands.py create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/__init__.py create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/__pycache__/_compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/__pycache__/model.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/_compat.py create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/model.py create mode 100644 venv/Lib/site-packages/flask_wtf/__init__.py create mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/_compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/csrf.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/form.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/i18n.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_wtf/_compat.py create mode 100644 venv/Lib/site-packages/flask_wtf/csrf.py create mode 100644 venv/Lib/site-packages/flask_wtf/file.py create mode 100644 venv/Lib/site-packages/flask_wtf/form.py create mode 100644 venv/Lib/site-packages/flask_wtf/html5.py create mode 100644 venv/Lib/site-packages/flask_wtf/i18n.py create mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__init__.py create mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/fields.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/validators.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/widgets.cpython-36.pyc create mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/fields.py create mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/validators.py create mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/widgets.py create mode 100644 venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/LICENSE.rst create mode 100644 venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/itsdangerous/__init__.py create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/_compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/_json.cpython-36.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/encoding.cpython-36.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/exc.cpython-36.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/jws.cpython-36.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/serializer.cpython-36.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/signer.cpython-36.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/timed.cpython-36.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/url_safe.cpython-36.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/_compat.py create mode 100644 venv/Lib/site-packages/itsdangerous/_json.py create mode 100644 venv/Lib/site-packages/itsdangerous/encoding.py create mode 100644 venv/Lib/site-packages/itsdangerous/exc.py create mode 100644 venv/Lib/site-packages/itsdangerous/jws.py create mode 100644 venv/Lib/site-packages/itsdangerous/serializer.py create mode 100644 venv/Lib/site-packages/itsdangerous/signer.py create mode 100644 venv/Lib/site-packages/itsdangerous/timed.py create mode 100644 venv/Lib/site-packages/itsdangerous/url_safe.py create mode 100644 venv/Lib/site-packages/jinja2/__init__.py create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/_compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/_identifier.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/asyncfilters.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/asyncsupport.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/bccache.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/compiler.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/defaults.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/environment.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/exceptions.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/ext.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/filters.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/idtracking.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/lexer.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/loaders.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/nodes.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/optimizer.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/parser.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/runtime.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/tests.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/utils.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/visitor.cpython-36.pyc create mode 100644 venv/Lib/site-packages/jinja2/_compat.py create mode 100644 venv/Lib/site-packages/jinja2/_identifier.py create mode 100644 venv/Lib/site-packages/jinja2/asyncfilters.py create mode 100644 venv/Lib/site-packages/jinja2/asyncsupport.py create mode 100644 venv/Lib/site-packages/jinja2/bccache.py create mode 100644 venv/Lib/site-packages/jinja2/compiler.py create mode 100644 venv/Lib/site-packages/jinja2/constants.py create mode 100644 venv/Lib/site-packages/jinja2/debug.py create mode 100644 venv/Lib/site-packages/jinja2/defaults.py create mode 100644 venv/Lib/site-packages/jinja2/environment.py create mode 100644 venv/Lib/site-packages/jinja2/exceptions.py create mode 100644 venv/Lib/site-packages/jinja2/ext.py create mode 100644 venv/Lib/site-packages/jinja2/filters.py create mode 100644 venv/Lib/site-packages/jinja2/idtracking.py create mode 100644 venv/Lib/site-packages/jinja2/lexer.py create mode 100644 venv/Lib/site-packages/jinja2/loaders.py create mode 100644 venv/Lib/site-packages/jinja2/meta.py create mode 100644 venv/Lib/site-packages/jinja2/nativetypes.py create mode 100644 venv/Lib/site-packages/jinja2/nodes.py create mode 100644 venv/Lib/site-packages/jinja2/optimizer.py create mode 100644 venv/Lib/site-packages/jinja2/parser.py create mode 100644 venv/Lib/site-packages/jinja2/runtime.py create mode 100644 venv/Lib/site-packages/jinja2/sandbox.py create mode 100644 venv/Lib/site-packages/jinja2/tests.py create mode 100644 venv/Lib/site-packages/jinja2/utils.py create mode 100644 venv/Lib/site-packages/jinja2/visitor.py create mode 100644 venv/Lib/site-packages/markupsafe/__init__.py create mode 100644 venv/Lib/site-packages/markupsafe/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/markupsafe/__pycache__/_compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/markupsafe/_compat.py create mode 100644 venv/Lib/site-packages/markupsafe/_constants.py create mode 100644 venv/Lib/site-packages/markupsafe/_native.py create mode 100644 venv/Lib/site-packages/markupsafe/_speedups.cp36-win_amd64.pyd create mode 100644 venv/Lib/site-packages/pip-18.1.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/pip-18.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pip-18.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pip-18.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pip-18.1.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/pip-18.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/PKG-INFO create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/SOURCES.txt create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/dependency_links.txt create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/entry_points.txt create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/not-zip-safe create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/requires.txt create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/top_level.txt create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/__main__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/appdirs.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/_cmd.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/adapter.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/cache.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/compat.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/controller.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/heuristics.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/serialize.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/wrapper.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/ansi.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/ansitowin32.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/initialise.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/win32.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/winterm.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/misc.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/shutil.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/sysconfig.cfg create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/sysconfig.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/tarfile.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/compat.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/database.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/index.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/locators.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/manifest.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/markers.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/metadata.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/resources.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/scripts.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/t32.exe create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/t64.exe create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/util.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/version.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/w32.exe create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/w64.exe create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/wheel.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distro.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_ihatexml.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_inputstream.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_tokenizer.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/_base.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/datrie.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/py.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_utils.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/constants.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/alphabeticalattributes.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/base.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/inject_meta_charset.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/lint.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/optionaltags.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/sanitizer.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/whitespace.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/html5parser.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/serializer.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treeadapters/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treeadapters/genshi.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treeadapters/sax.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/base.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/dom.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/etree.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/etree_lxml.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/base.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/dom.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/etree.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/etree_lxml.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/genshi.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/ipaddress.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/linklockfile.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/mkdirlockfile.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/pidlockfile.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/sqlitelockfile.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/symlinklockfile.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/ordereddict.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/__about__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/_compat.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/_structures.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/markers.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/requirements.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/specifiers.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/utils.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/version.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/pkg_resources/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/bar.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/counter.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/helpers.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/spinner.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/pyparsing.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/re-vendor.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/adapters.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/api.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/auth.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/cacert.pem create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/certs.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/compat.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/cookies.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/exceptions.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/hooks.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/models.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/big5freq.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/big5prober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/chardetect.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/chardistribution.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/charsetgroupprober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/charsetprober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/codingstatemachine.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/compat.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/constants.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/cp949prober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/escprober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/escsm.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/eucjpprober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euckrfreq.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euckrprober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euctwfreq.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euctwprober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/gb2312freq.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/gb2312prober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/hebrewprober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/jisfreq.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/jpcntx.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langbulgarianmodel.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langcyrillicmodel.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langgreekmodel.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langhebrewmodel.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langhungarianmodel.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langthaimodel.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/latin1prober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/mbcharsetprober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/mbcsgroupprober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/mbcssm.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/sbcharsetprober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/sbcsgroupprober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/sjisprober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/universaldetector.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/utf8prober.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/_collections.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/connection.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/connectionpool.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/appengine.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/socks.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/exceptions.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/fields.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/filepost.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/six.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/poolmanager.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/request.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/response.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/connection.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/request.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/response.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/retry.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/ssl_.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/timeout.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/url.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/sessions.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/status_codes.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/structures.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/utils.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/retrying.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/six.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/labels.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/mklabels.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/tests.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/x_user_defined.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/basecommand.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/baseparser.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/cmdoptions.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/check.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/completion.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/download.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/freeze.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/hash.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/help.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/install.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/list.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/search.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/show.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/uninstall.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/wheel.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/compat/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/compat/dictconfig.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/download.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/exceptions.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/index.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/locations.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/models/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/models/index.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/operations/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/operations/check.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/operations/freeze.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/pep425tags.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_file.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_install.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_set.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_uninstall.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/status_codes.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/appdirs.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/build.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/deprecation.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/encoding.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/filesystem.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/glibc.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/hashes.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/logging.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/outdated.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/packaging.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/setuptools_build.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/ui.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/__init__.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/bazaar.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/git.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/mercurial.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/subversion.py create mode 100644 venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/wheel.py create mode 100644 venv/Lib/site-packages/pip/__init__.py create mode 100644 venv/Lib/site-packages/pip/__main__.py create mode 100644 venv/Lib/site-packages/pip/_internal/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/build_env.py create mode 100644 venv/Lib/site-packages/pip/_internal/cache.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/base_command.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/main_parser.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/parser.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/status_codes.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/check.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/completion.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/configuration.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/download.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/freeze.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/hash.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/help.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/install.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/list.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/search.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/show.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/uninstall.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/wheel.py create mode 100644 venv/Lib/site-packages/pip/_internal/configuration.py create mode 100644 venv/Lib/site-packages/pip/_internal/download.py create mode 100644 venv/Lib/site-packages/pip/_internal/exceptions.py create mode 100644 venv/Lib/site-packages/pip/_internal/index.py create mode 100644 venv/Lib/site-packages/pip/_internal/locations.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/candidate.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/format_control.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/index.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/link.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/check.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/freeze.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/prepare.py create mode 100644 venv/Lib/site-packages/pip/_internal/pep425tags.py create mode 100644 venv/Lib/site-packages/pip/_internal/pyproject.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/constructors.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/req_file.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/req_install.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/req_set.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/req_tracker.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolve.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/appdirs.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/compat.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/deprecation.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/encoding.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/filesystem.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/glibc.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/hashes.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/logging.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/misc.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/models.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/outdated.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/packaging.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/typing.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/ui.py create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/git.py create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/subversion.py create mode 100644 venv/Lib/site-packages/pip/_internal/wheel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/appdirs.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/core.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/enums.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langcyrillicmodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/version.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/win32.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/misc.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/shutil.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/sysconfig.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/tarfile.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/database.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/index.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/locators.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/markers.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/resources.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/t64.exe create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/util.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/version.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/w64.exe create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distro.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_ihatexml.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_inputstream.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_tokenizer.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_trie/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_trie/_base.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_trie/datrie.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_trie/py.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_utils.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/constants.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/base.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/lint.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/optionaltags.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/sanitizer.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/whitespace.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/html5parser.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/serializer.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/sax.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/base.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/dom.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/base.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/dom.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/codec.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/core.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/intranges.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/package_data.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 venv/Lib/site-packages/pip/_vendor/ipaddress.py create mode 100644 venv/Lib/site-packages/pip/_vendor/lockfile/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/lockfile/linklockfile.py create mode 100644 venv/Lib/site-packages/pip/_vendor/lockfile/mkdirlockfile.py create mode 100644 venv/Lib/site-packages/pip/_vendor/lockfile/pidlockfile.py create mode 100644 venv/Lib/site-packages/pip/_vendor/lockfile/sqlitelockfile.py create mode 100644 venv/Lib/site-packages/pip/_vendor/lockfile/symlinklockfile.py create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/_version.py create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/_compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/markers.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/utils.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/version.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/_in_process.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/check.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/colorlog.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/wrappers.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pkg_resources/py31compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/bar.py create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/counter.py create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/helpers.py create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/spinner.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pytoml/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pytoml/core.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pytoml/parser.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pytoml/writer.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__version__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/adapters.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/api.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/auth.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/certs.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/cookies.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/help.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/hooks.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/models.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/packages.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/sessions.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/structures.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/utils.py create mode 100644 venv/Lib/site-packages/pip/_vendor/retrying.py create mode 100644 venv/Lib/site-packages/pip/_vendor/six.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/ordered_dict.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/request.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/response.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 venv/Lib/site-packages/setuptools-28.8.0-py3.6.egg create mode 100644 venv/Lib/site-packages/setuptools.pth create mode 100644 venv/Lib/site-packages/sqlalchemy/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/events.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/exc.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/inspection.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/interfaces.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/log.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/pool.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/processors.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/schema.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/types.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/connectors/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/connectors/mxodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/connectors/pyodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/connectors/zxJDBC.py create mode 100644 venv/Lib/site-packages/sqlalchemy/cprocessors.cp36-win_amd64.pyd create mode 100644 venv/Lib/site-packages/sqlalchemy/cresultproxy.cp36-win_amd64.pyd create mode 100644 venv/Lib/site-packages/sqlalchemy/cutils.cp36-win_amd64.pyd create mode 100644 venv/Lib/site-packages/sqlalchemy/databases/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/firebird/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/firebird/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/firebird/fdb.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/firebird/kinterbasdb.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/adodbapi.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/information_schema.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/mxodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/pymssql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/pyodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/zxjdbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/cymysql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/dml.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/enumerated.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/gaerdbms.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/json.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/mysqlconnector.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/mysqldb.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/oursql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/pymysql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/pyodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/reflection.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/types.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/zxjdbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/oracle/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/oracle/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/oracle/zxjdbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/array.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/dml.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/ext.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/hstore.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/json.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pg8000.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg2cffi.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pygresql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pypostgresql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/ranges.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/zxjdbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/base.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/pysqlcipher.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/mxodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/pyodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/pysybase.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/base.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/default.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/interfaces.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/reflection.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/result.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/strategies.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/threadlocal.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/url.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/util.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/default.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/interfaces.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/reflection.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/result.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/strategies.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/threadlocal.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/url.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/util.py create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__pycache__/api.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__pycache__/attr.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__pycache__/base.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__pycache__/legacy.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__pycache__/registry.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/event/api.py create mode 100644 venv/Lib/site-packages/sqlalchemy/event/attr.py create mode 100644 venv/Lib/site-packages/sqlalchemy/event/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/event/legacy.py create mode 100644 venv/Lib/site-packages/sqlalchemy/event/registry.py create mode 100644 venv/Lib/site-packages/sqlalchemy/events.py create mode 100644 venv/Lib/site-packages/sqlalchemy/exc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/baked.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/associationproxy.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/automap.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/baked.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/compiler.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/declarative/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/api.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/base.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/clsregistry.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/declarative/api.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/declarative/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/declarative/clsregistry.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/horizontal_shard.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/hybrid.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/indexable.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/instrumentation.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mutable.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/orderinglist.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/serializer.py create mode 100644 venv/Lib/site-packages/sqlalchemy/inspection.py create mode 100644 venv/Lib/site-packages/sqlalchemy/interfaces.py create mode 100644 venv/Lib/site-packages/sqlalchemy/log.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/attributes.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/base.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/collections.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/dependency.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/deprecated_interfaces.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/descriptor_props.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/dynamic.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/evaluator.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/events.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/exc.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/identity.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/instrumentation.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/interfaces.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/loading.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/mapper.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/path_registry.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/persistence.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/properties.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/query.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/relationships.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/scoping.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/session.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/state.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/strategies.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/strategy_options.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/sync.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/unitofwork.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/util.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/attributes.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/collections.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/dependency.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/deprecated_interfaces.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/descriptor_props.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/dynamic.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/evaluator.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/events.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/exc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/identity.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/instrumentation.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/interfaces.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/loading.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/mapper.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/path_registry.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/persistence.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/properties.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/query.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/relationships.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/scoping.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/session.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/state.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/strategies.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/strategy_options.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/sync.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/unitofwork.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/util.py create mode 100644 venv/Lib/site-packages/sqlalchemy/pool.py create mode 100644 venv/Lib/site-packages/sqlalchemy/processors.py create mode 100644 venv/Lib/site-packages/sqlalchemy/schema.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/annotation.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/base.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/compiler.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/crud.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/ddl.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/default_comparator.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/dml.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/elements.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/expression.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/functions.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/naming.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/operators.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/schema.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/selectable.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/sqltypes.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/type_api.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/util.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/visitors.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/annotation.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/compiler.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/crud.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/ddl.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/default_comparator.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/dml.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/elements.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/expression.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/functions.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/naming.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/operators.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/schema.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/selectable.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/sqltypes.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/type_api.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/util.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/visitors.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/assertions.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/assertsql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/config.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/engines.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/entities.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/exclusions.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/fixtures.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/mock.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/pickleable.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/bootstrap.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/noseplugin.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/plugin_base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/pytestplugin.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/profiling.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/provision.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/replay_fixture.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/requirements.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/runner.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/schema.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_cte.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_ddl.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_dialect.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_insert.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_reflection.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_results.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_select.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_sequence.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_types.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_update_delete.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/util.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/warnings.py create mode 100644 venv/Lib/site-packages/sqlalchemy/types.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/_collections.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/deprecations.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/langhelpers.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/queue.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/topological.cpython-36.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/_collections.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/compat.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/deprecations.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/langhelpers.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/queue.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/topological.py create mode 100644 venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/PKG-INFO create mode 100644 venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/SOURCES.txt create mode 100644 venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/dependency_links.txt create mode 100644 venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/installed-files.txt create mode 100644 venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/top_level.txt create mode 100644 venv/Lib/site-packages/visitor/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/_compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/_internal.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/datastructures.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/exceptions.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/filesystem.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/formparser.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/http.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/local.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/routing.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/security.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/serving.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/test.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/urls.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/utils.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/wrappers.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/wsgi.cpython-36.pyc create mode 100644 venv/Lib/site-packages/werkzeug/_compat.py create mode 100644 venv/Lib/site-packages/werkzeug/_internal.py create mode 100644 venv/Lib/site-packages/werkzeug/_reloader.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/atom.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/cache.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/fixers.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/iterio.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/jsrouting.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/limiter.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/lint.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/profiler.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/securecookie.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/sessions.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/testtools.py create mode 100644 venv/Lib/site-packages/werkzeug/contrib/wrappers.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures.py create mode 100644 venv/Lib/site-packages/werkzeug/debug/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/debug/console.py create mode 100644 venv/Lib/site-packages/werkzeug/debug/repr.py create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/FONT_LICENSE create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/console.png create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/debugger.js create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/jquery.js create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/less.png create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/more.png create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/source.png create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/style.css create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/ubuntu.ttf create mode 100644 venv/Lib/site-packages/werkzeug/debug/tbtools.py create mode 100644 venv/Lib/site-packages/werkzeug/exceptions.py create mode 100644 venv/Lib/site-packages/werkzeug/filesystem.py create mode 100644 venv/Lib/site-packages/werkzeug/formparser.py create mode 100644 venv/Lib/site-packages/werkzeug/http.py create mode 100644 venv/Lib/site-packages/werkzeug/local.py create mode 100644 venv/Lib/site-packages/werkzeug/posixemulation.py create mode 100644 venv/Lib/site-packages/werkzeug/routing.py create mode 100644 venv/Lib/site-packages/werkzeug/script.py create mode 100644 venv/Lib/site-packages/werkzeug/security.py create mode 100644 venv/Lib/site-packages/werkzeug/serving.py create mode 100644 venv/Lib/site-packages/werkzeug/test.py create mode 100644 venv/Lib/site-packages/werkzeug/testapp.py create mode 100644 venv/Lib/site-packages/werkzeug/urls.py create mode 100644 venv/Lib/site-packages/werkzeug/useragents.py create mode 100644 venv/Lib/site-packages/werkzeug/utils.py create mode 100644 venv/Lib/site-packages/werkzeug/websocket.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers.py create mode 100644 venv/Lib/site-packages/werkzeug/wsgi.py create mode 100644 venv/Lib/site-packages/wtforms/__init__.py create mode 100644 venv/Lib/site-packages/wtforms/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/__pycache__/compat.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/__pycache__/form.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/__pycache__/i18n.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/__pycache__/meta.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/__pycache__/utils.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/__pycache__/validators.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/compat.py create mode 100644 venv/Lib/site-packages/wtforms/csrf/__init__.py create mode 100644 venv/Lib/site-packages/wtforms/csrf/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/csrf/__pycache__/core.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/csrf/core.py create mode 100644 venv/Lib/site-packages/wtforms/csrf/session.py create mode 100644 venv/Lib/site-packages/wtforms/ext/__init__.py create mode 100644 venv/Lib/site-packages/wtforms/ext/appengine/__init__.py create mode 100644 venv/Lib/site-packages/wtforms/ext/appengine/db.py create mode 100644 venv/Lib/site-packages/wtforms/ext/appengine/fields.py create mode 100644 venv/Lib/site-packages/wtforms/ext/appengine/ndb.py create mode 100644 venv/Lib/site-packages/wtforms/ext/csrf/__init__.py create mode 100644 venv/Lib/site-packages/wtforms/ext/csrf/fields.py create mode 100644 venv/Lib/site-packages/wtforms/ext/csrf/form.py create mode 100644 venv/Lib/site-packages/wtforms/ext/csrf/session.py create mode 100644 venv/Lib/site-packages/wtforms/ext/dateutil/__init__.py create mode 100644 venv/Lib/site-packages/wtforms/ext/dateutil/fields.py create mode 100644 venv/Lib/site-packages/wtforms/ext/django/__init__.py create mode 100644 venv/Lib/site-packages/wtforms/ext/django/fields.py create mode 100644 venv/Lib/site-packages/wtforms/ext/django/i18n.py create mode 100644 venv/Lib/site-packages/wtforms/ext/django/orm.py create mode 100644 venv/Lib/site-packages/wtforms/ext/django/templatetags/__init__.py create mode 100644 venv/Lib/site-packages/wtforms/ext/django/templatetags/wtforms.py create mode 100644 venv/Lib/site-packages/wtforms/ext/i18n/__init__.py create mode 100644 venv/Lib/site-packages/wtforms/ext/i18n/form.py create mode 100644 venv/Lib/site-packages/wtforms/ext/i18n/utils.py create mode 100644 venv/Lib/site-packages/wtforms/ext/sqlalchemy/__init__.py create mode 100644 venv/Lib/site-packages/wtforms/ext/sqlalchemy/fields.py create mode 100644 venv/Lib/site-packages/wtforms/ext/sqlalchemy/orm.py create mode 100644 venv/Lib/site-packages/wtforms/fields/__init__.py create mode 100644 venv/Lib/site-packages/wtforms/fields/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/fields/__pycache__/core.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/fields/__pycache__/simple.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/fields/core.py create mode 100644 venv/Lib/site-packages/wtforms/fields/html5.py create mode 100644 venv/Lib/site-packages/wtforms/fields/simple.py create mode 100644 venv/Lib/site-packages/wtforms/form.py create mode 100644 venv/Lib/site-packages/wtforms/i18n.py create mode 100644 venv/Lib/site-packages/wtforms/locale/README.md create mode 100644 venv/Lib/site-packages/wtforms/locale/ar/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/ar/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/bg/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/bg/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/ca/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/ca/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/cy/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/cy/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/de/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/de/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/de_CH/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/de_CH/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/el/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/el/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/en/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/en/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/es/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/es/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/et/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/et/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/fa/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/fa/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/fi/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/fi/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/fr/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/fr/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/he/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/he/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/hu/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/hu/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/it/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/it/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/ja/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/ja/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/ko/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/ko/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/nb/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/nb/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/nl/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/nl/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/pl/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/pl/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/pt/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/pt/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/ru/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/ru/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/sk/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/sk/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/sv/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/sv/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/tr/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/tr/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/uk/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/uk/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/wtforms.pot create mode 100644 venv/Lib/site-packages/wtforms/locale/zh/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/zh/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.mo create mode 100644 venv/Lib/site-packages/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.po create mode 100644 venv/Lib/site-packages/wtforms/meta.py create mode 100644 venv/Lib/site-packages/wtforms/utils.py create mode 100644 venv/Lib/site-packages/wtforms/validators.py create mode 100644 venv/Lib/site-packages/wtforms/widgets/__init__.py create mode 100644 venv/Lib/site-packages/wtforms/widgets/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/widgets/__pycache__/core.cpython-36.pyc create mode 100644 venv/Lib/site-packages/wtforms/widgets/core.py create mode 100644 venv/Lib/site-packages/wtforms/widgets/html5.py create mode 100644 venv/Lib/tcl8.6/init.tcl create mode 100644 venv/Scripts/Activate.ps1 create mode 100644 venv/Scripts/_asyncio.pyd create mode 100644 venv/Scripts/_asyncio_d.pyd create mode 100644 venv/Scripts/_bz2.pyd create mode 100644 venv/Scripts/_bz2_d.pyd create mode 100644 venv/Scripts/_ctypes.pyd create mode 100644 venv/Scripts/_ctypes_d.pyd create mode 100644 venv/Scripts/_ctypes_test.pyd create mode 100644 venv/Scripts/_ctypes_test_d.pyd create mode 100644 venv/Scripts/_decimal.pyd create mode 100644 venv/Scripts/_decimal_d.pyd create mode 100644 venv/Scripts/_elementtree.pyd create mode 100644 venv/Scripts/_elementtree_d.pyd create mode 100644 venv/Scripts/_hashlib.pyd create mode 100644 venv/Scripts/_hashlib_d.pyd create mode 100644 venv/Scripts/_lzma.pyd create mode 100644 venv/Scripts/_lzma_d.pyd create mode 100644 venv/Scripts/_msi.pyd create mode 100644 venv/Scripts/_msi_d.pyd create mode 100644 venv/Scripts/_multiprocessing.pyd create mode 100644 venv/Scripts/_multiprocessing_d.pyd create mode 100644 venv/Scripts/_overlapped.pyd create mode 100644 venv/Scripts/_overlapped_d.pyd create mode 100644 venv/Scripts/_socket.pyd create mode 100644 venv/Scripts/_socket_d.pyd create mode 100644 venv/Scripts/_sqlite3.pyd create mode 100644 venv/Scripts/_sqlite3_d.pyd create mode 100644 venv/Scripts/_ssl.pyd create mode 100644 venv/Scripts/_ssl_d.pyd create mode 100644 venv/Scripts/_testbuffer.pyd create mode 100644 venv/Scripts/_testbuffer_d.pyd create mode 100644 venv/Scripts/_testcapi.pyd create mode 100644 venv/Scripts/_testcapi_d.pyd create mode 100644 venv/Scripts/_testconsole.pyd create mode 100644 venv/Scripts/_testconsole_d.pyd create mode 100644 venv/Scripts/_testimportmultiple.pyd create mode 100644 venv/Scripts/_testimportmultiple_d.pyd create mode 100644 venv/Scripts/_testmultiphase.pyd create mode 100644 venv/Scripts/_testmultiphase_d.pyd create mode 100644 venv/Scripts/_tkinter.pyd create mode 100644 venv/Scripts/_tkinter_d.pyd create mode 100644 venv/Scripts/activate create mode 100644 venv/Scripts/activate.bat create mode 100644 venv/Scripts/deactivate.bat create mode 100644 venv/Scripts/easy_install-3.6-script.py create mode 100644 venv/Scripts/easy_install-3.6.exe create mode 100644 venv/Scripts/easy_install-script.py create mode 100644 venv/Scripts/easy_install.exe create mode 100644 venv/Scripts/flask.exe create mode 100644 venv/Scripts/pip-script.py create mode 100644 venv/Scripts/pip.exe create mode 100644 venv/Scripts/pip3-script.py create mode 100644 venv/Scripts/pip3.6-script.py create mode 100644 venv/Scripts/pip3.6.exe create mode 100644 venv/Scripts/pip3.exe create mode 100644 venv/Scripts/pyexpat.pyd create mode 100644 venv/Scripts/pyexpat_d.pyd create mode 100644 venv/Scripts/python.exe create mode 100644 venv/Scripts/python3.dll create mode 100644 venv/Scripts/python36.dll create mode 100644 venv/Scripts/python36_d.dll create mode 100644 venv/Scripts/python3_d.dll create mode 100644 venv/Scripts/python_d.exe create mode 100644 venv/Scripts/pythonw.exe create mode 100644 venv/Scripts/pythonw_d.exe create mode 100644 venv/Scripts/select.pyd create mode 100644 venv/Scripts/select_d.pyd create mode 100644 venv/Scripts/sqlite3.dll create mode 100644 venv/Scripts/sqlite3_d.dll create mode 100644 venv/Scripts/tcl86t.dll create mode 100644 venv/Scripts/tcl86tg.dll create mode 100644 venv/Scripts/tk86t.dll create mode 100644 venv/Scripts/tk86tg.dll create mode 100644 venv/Scripts/unicodedata.pyd create mode 100644 venv/Scripts/unicodedata_d.pyd create mode 100644 venv/Scripts/vcruntime140.dll create mode 100644 venv/Scripts/winsound.pyd create mode 100644 venv/Scripts/winsound_d.pyd create mode 100644 venv/pip-selfcheck.json create mode 100644 venv/pyvenv.cfg diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/book_management_sys.iml b/.idea/book_management_sys.iml new file mode 100644 index 0000000..46cae00 --- /dev/null +++ b/.idea/book_management_sys.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..e59ae44 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,14 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$PROJECT_DIR$/data.sqlite + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..796775d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..8ac078c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/__pycache__/forms.cpython-36.pyc b/__pycache__/forms.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98eb12e2f218a71565cc588867884594a359961d GIT binary patch literal 2796 zcma)8&2Jk;6yNo(_se#^Tb1%vD5zMFf(l#^Agf=fQn64CmsKQdCz20U zKKtWDwe4uyU)q*G6Y~;Ad=G+YOmAwT?&~_uGtEqB_(o{@W|;M}q2*gjZ#3=D@tx51 z-O%$rUHd>|Cd+=Vu`Jh@4L=8LF&o%cI1lVF7uZ#}0PL|Ga8BV7;5;h;7Ze@^9$}-v zqY94!kFjy!afQc$C)gzLq{0(ys-hKlb>V?l)TMj88N{cLM(w2z%>2hEQoX`SS)dNN<5tNMQf z3g3c;Yrf7jKf?{Cb8}g%>nyX9@w41w#ujXIhndT|@3JgRw`BHYRByD}7Eai>dA)P- z7X0u&4Vn!WB$0^KLg&{n*1tcert9C{?cD#mT=b+O$3n>5c?t7LGx zA`F}=YzS!wwOZ6}C4w%J&O*;=K`uxuR_y$wi3$1wiGSZa^3KdBF&FX7(t-HE(#%mF zpH8BMnZr?Zdiukl71VjiTgh}~DNcAe9R~O(X6C?sd~jh&csPTuib**77(Plj#2;B! zqD^Ip$MR>S{DDWL8nae1^^9WH2GjNyW*zP_2h4iR1+$)U*~{20c(j=X9&MRF z#u~|q)?7q|rN~;p{Nvh%Tb)ZQT^&bt_d)czB!|{6URwS0TDj;v?5#>i3V}2!L3eO# z4>pn$F?sLkQ@S9{_AZsM(szj!sE4qzy6L0vxWThjj7T4M#qd#nDQ4aZijbol!POkg z!_{#)QsIH9%~Ot(v(lZ#OqJO`u_3HK|7g`M5?-fW#jC5>DADA;?14wzu)a# z_*t5bc(x_Y6P3edbQZfdA2n(`R#_*lTEs<-4-t3i%KFV8%EgR$7PpD#ATWQP=KIq5 z3p9T*o$sglOEk|vL^P!(EM&2un5X$=#EFnrs#kph=0vRoCA>ozp1~1MVf3;quTSVB zx*+XPC@8ii+D6v(Ljm&BPN;)y>Vz4UR|d#7<#jPGM!(a-L=Svab4#?pvuh&=~_AdJuUC9eNWgh&q$* zQzx=iC$izGBi#yGIVTUhGwfBDMJkltLUKL28>c^f%o<@v|{m@IN z8P$TM5w&(8V;Y-Z#^{A|3N^J`7o^>ukh56%e}v#k>pQR;|Z# zioQHc>C2b-;($T%I*jTuD2Tjn3F1)(3lG)A=3A&3RK%$=li3A|Zkiv1wq&io%7fNY=qPKW(^QTLi zuIeSDoK?@idX-ckR~pS C)o6|Y literal 0 HcmV?d00001 diff --git a/app.py b/app.py new file mode 100644 index 0000000..cd7a7fa --- /dev/null +++ b/app.py @@ -0,0 +1,497 @@ +from flask import Flask, render_template, session, redirect, url_for, flash, request, jsonify +import os +from flask_sqlalchemy import SQLAlchemy +from flask_script import Manager, Shell +from forms import Login, SearchBookForm, ChangePasswordForm, EditInfoForm, SearchStudentForm, NewStoreForm, StoreForm, BorrowForm +from flask_login import UserMixin, LoginManager, login_required, login_user, logout_user, current_user +import time, datetime + + +basedir = os.path.abspath(os.path.dirname(__file__)) + +app = Flask(__name__) +manager = Manager(app) + +app.config['SECRET_KEY'] = 'hard to guess string' +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite') +app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True +db = SQLAlchemy(app) + + +def make_shell_context(): + return dict(app=app, db=db, Admin=Admin, Book=Book) + + +manager.add_command("shell", Shell(make_context=make_shell_context)) + +login_manager = LoginManager() +login_manager.init_app(app) +login_manager.session_protection = 'basic' +login_manager.login_view = 'login' +login_manager.login_message = u"请先登录。" + + +class Admin(UserMixin, db.Model): + __tablename__ = 'admin' + admin_id = db.Column(db.String(6), primary_key=True) + admin_name = db.Column(db.String(32)) + password = db.Column(db.String(24)) + right = db.Column(db.String(32)) + + def __init__(self, admin_id, admin_name, password, right): + self.admin_id = admin_id + self.admin_name = admin_name + self.password = password + self.right = right + + def get_id(self): + return self.admin_id + + def verify_password(self, password): + if password == self.password: + return True + else: + return False + + def __repr__(self): + return '' % self.admin_name + + +class Book(db.Model): + __tablename__ = 'book' + isbn = db.Column(db.String(13), primary_key=True) + book_name = db.Column(db.String(64)) + author = db.Column(db.String(64)) + press = db.Column(db.String(32)) + class_name = db.Column(db.String(64)) + + def __repr__(self): + return '' % self.book_name + + +class Student(db.Model): + __tablename__ = 'student' + card_id = db.Column(db.String(8), primary_key=True) + student_id = db.Column(db.String(9)) + student_name = db.Column(db.String(32)) + sex = db.Column(db.String(2)) + telephone = db.Column(db.String(11), nullable=True) + enroll_date = db.Column(db.String(13)) + valid_date = db.Column(db.String(13)) + loss = db.Column(db.Boolean, default=False) # 是否挂失 + debt = db.Column(db.Boolean, default=False) # 是否欠费 + + def __repr__(self): + return '' % self.student_name + + +class Inventory(db.Model): + __tablename__ = 'inventory' + barcode = db.Column(db.String(6), primary_key=True) + isbn = db.Column(db.ForeignKey('book.isbn')) + storage_date = db.Column(db.String(13)) + location = db.Column(db.String(32)) + withdraw = db.Column(db.Boolean, default=False) # 是否注销 + status = db.Column(db.Boolean, default=True) # 是否在馆 + admin = db.Column(db.ForeignKey('admin.admin_id')) # 入库操作员 + + def __repr__(self): + return '' % self.barcode + + +class ReadBook(db.Model): + __tablename__ = 'readbook' + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + barcode = db.Column(db.ForeignKey('inventory.barcode'), index=True) + card_id = db.Column(db.ForeignKey('student.card_id'), index=True) + start_date = db.Column(db.String(13)) + borrow_admin = db.Column(db.ForeignKey('admin.admin_id')) # 借书操作员 + end_date = db.Column(db.String(13), nullable=True) + return_admin = db.Column(db.ForeignKey('admin.admin_id')) # 还书操作员 + due_date = db.Column(db.String(13)) # 应还日期 + + def __repr__(self): + return '' % self.id + + +@login_manager.user_loader +def load_user(admin_id): + return Admin.query.get(int(admin_id)) + + +@app.route('/', methods=['GET', 'POST']) +def login(): + form = Login() + if form.validate_on_submit(): + user = Admin.query.filter_by(admin_id=form.account.data, password=form.password.data).first() + if user is None: + flash('账号或密码错误!') + return redirect(url_for('login')) + else: + login_user(user) + session['admin_id'] = user.admin_id + session['name'] = user.admin_name + return redirect(url_for('index')) + return render_template('login.html', form=form) + + +@app.route('/logout') +@login_required +def logout(): + logout_user() + flash('您已经登出!') + return redirect(url_for('login')) + + +@app.route('/index') +@login_required +def index(): + return render_template('index.html', name=session.get('name')) + + +@app.route('/echarts') +@login_required +def echarts(): + days = [] + num = [] + today_date = datetime.date.today() + today_str = today_date.strftime("%Y-%m-%d") + today_stamp = time.mktime(time.strptime(today_str + ' 00:00:00', '%Y-%m-%d %H:%M:%S')) + ten_ago = int(today_stamp) - 9 * 86400 + for i in range(0, 10): + borr = ReadBook.query.filter_by(start_date=str((ten_ago+i*86400)*1000)).count() + retu = ReadBook.query.filter_by(end_date=str((ten_ago+i*86400)*1000)).count() + num.append(borr + retu) + days.append(timeStamp((ten_ago+i*86400)*1000)) + data = [] + for i in range(0, 10): + item = {'name': days[i], 'num': num[i]} + data.append(item) + return jsonify(data) + + +@app.route('/user/') +@login_required +def user_info(id): + user = Admin.query.filter_by(admin_id=id).first() + return render_template('user-info.html', user=user, name=session.get('name')) + + +@app.route('/change_password', methods=['GET', 'POST']) +@login_required +def change_password(): + form = ChangePasswordForm() + if form.password2.data != form.password.data: + flash(u'两次密码不一致!') + if form.validate_on_submit(): + if current_user.verify_password(form.old_password.data): + current_user.password = form.password.data + db.session.add(current_user) + db.session.commit() + flash(u'已成功修改密码!') + return redirect(url_for('index')) + else: + flash(u'原密码输入错误,修改失败!') + return render_template("change-password.html", form=form) + + +@app.route('/change_info', methods=['GET', 'POST']) +@login_required +def change_info(): + form = EditInfoForm() + if form.validate_on_submit(): + current_user.admin_name = form.name.data + db.session.add(current_user) + flash(u'已成功修改个人信息!') + return redirect(url_for('user_info', id=current_user.admin_id)) + form.name.data = current_user.admin_name + id = current_user.admin_id + right = current_user.right + return render_template('change-info.html', form=form, id=id, right=right) + + +@app.route('/search_book', methods=['GET', 'POST']) +@login_required +def search_book(): # 这个函数里不再处理提交按钮,使用Ajax局部刷新 + form = SearchBookForm() + return render_template('search-book.html', name=session.get('name'), form=form) + + +@app.route('/books', methods=['POST']) +def find_book(): + + def find_name(): + return Book.query.filter(Book.book_name.like('%'+request.form.get('content')+'%')).all() + + def find_author(): + return Book.query.filter(Book.author.contains(request.form.get('content'))).all() + + def find_class(): + return Book.query.filter(Book.class_name.contains(request.form.get('content'))).all() + + def find_isbn(): + return Book.query.filter(Book.isbn.contains(request.form.get('content'))).all() + + methods = { + 'book_name': find_name, + 'author': find_author, + 'class_name': find_class, + 'isbn': find_isbn + } + books = methods[request.form.get('method')]() + data = [] + for book in books: + count = Inventory.query.filter_by(isbn=book.isbn).count() + available = Inventory.query.filter_by(isbn=book.isbn, status=True).count() + item = {'isbn': book.isbn, 'book_name': book.book_name, 'press': book.press, 'author': book.author, + 'class_name': book.class_name, 'count': count, 'available': available} + data.append(item) + return jsonify(data) + + +@app.route('/user/book', methods=['GET', 'POST']) +def user_book(): + form = SearchBookForm() + return render_template('user-book.html', form=form) + + +@app.route('/search_student', methods=['GET', 'POST']) +@login_required +def search_student(): + form = SearchStudentForm() + return render_template('search-student.html', name=session.get('name'), form=form) + + +def timeStamp(timeNum): + if timeNum is None: + return timeNum + else: + timeStamp = float(float(timeNum)/1000) + timeArray = time.localtime(timeStamp) + print(time.strftime("%Y-%m-%d", timeArray)) + return time.strftime("%Y-%m-%d", timeArray) + + +@app.route('/student', methods=['POST']) +def find_student(): + stu = Student.query.filter_by(card_id=request.form.get('card')).first() + if stu is None: + return jsonify([]) + else: + valid_date = timeStamp(stu.valid_date) + return jsonify([{'name': stu.student_name, 'gender': stu.sex, 'valid_date': valid_date, 'debt': stu.debt}]) + + +@app.route('/record', methods=['POST']) +def find_record(): + records = db.session.query(ReadBook).join(Inventory).join(Book).filter(ReadBook.card_id == request.form.get('card'))\ + .with_entities(ReadBook.barcode, Inventory.isbn, Book.book_name, Book.author, ReadBook.start_date, + ReadBook.end_date, ReadBook.due_date).all() # with_entities啊啊啊啊卡了好久啊 + data = [] + for record in records: + start_date = timeStamp(record.start_date) + due_date = timeStamp(record.due_date) + end_date = timeStamp(record.end_date) + if end_date is None: + end_date = '未归还' + item = {'barcode': record.barcode, 'book_name': record.book_name, 'author': record.author, + 'start_date': start_date, 'due_date': due_date, 'end_date': end_date} + data.append(item) + return jsonify(data) + + +@app.route('/user/student', methods=['GET', 'POST']) +def user_student(): + form = SearchStudentForm() + return render_template('user-student.html', form=form) + + +@app.route('/storage', methods=['GET', 'POST']) +@login_required +def storage(): + form = StoreForm() + if form.validate_on_submit(): + book = Book.query.filter_by(isbn=request.form.get('isbn')).first() + exist = Inventory.query.filter_by(barcode=request.form.get('barcode')).first() + if book is None: + flash(u'添加失败,请注意本书信息是否已录入,若未登记,请在‘新书入库’窗口录入信息。') + else: + if len(request.form.get('barcode')) != 6: + flash(u'图书编码长度错误') + else: + if exist is not None: + flash(u'该编号已经存在!') + else: + item = Inventory() + item.barcode = request.form.get('barcode') + item.isbn = request.form.get('isbn') + item.admin = current_user.admin_id + item.location = request.form.get('location') + item.status = True + item.withdraw = False + today_date = datetime.date.today() + today_str = today_date.strftime("%Y-%m-%d") + today_stamp = time.mktime(time.strptime(today_str + ' 00:00:00', '%Y-%m-%d %H:%M:%S')) + item.storage_date = int(today_stamp)*1000 + db.session.add(item) + db.session.commit() + flash(u'入库成功!') + return redirect(url_for('storage')) + return render_template('storage.html', name=session.get('name'), form=form) + + +@app.route('/new_store', methods=['GET', 'POST']) +@login_required +def new_store(): + form = NewStoreForm() + if form.validate_on_submit(): + if len(request.form.get('isbn')) != 13: + flash(u'ISBN长度错误') + else: + exist = Book.query.filter_by(isbn=request.form.get('isbn')).first() + if exist is not None: + flash(u'该图书信息已经存在,请核对后再录入;或者填写入库表。') + else: + book = Book() + book.isbn = request.form.get('isbn') + book.book_name = request.form.get('book_name') + book.press = request.form.get('press') + book.author = request.form.get('author') + book.class_name = request.form.get('class_name') + db.session.add(book) + db.session.commit() + flash(u'图书信息添加成功!') + return redirect(url_for('new_store')) + return render_template('new-store.html', name=session.get('name'), form=form) + + +@app.route('/borrow', methods=['GET', 'POST']) +@login_required +def borrow(): + form = BorrowForm() + return render_template('borrow.html', name=session.get('name'), form=form) + + +@app.route('/find_stu_book', methods=['GET', 'POST']) +def find_stu_book(): + stu = Student.query.filter_by(card_id=request.form.get('card')).first() + today_date = datetime.date.today() + today_str = today_date.strftime("%Y-%m-%d") + today_stamp = time.mktime(time.strptime(today_str + ' 00:00:00', '%Y-%m-%d %H:%M:%S')) + if stu is None: + return jsonify([{'stu': 0}]) # 没找到 + if stu.debt is True: + return jsonify([{'stu': 1}]) # 欠费 + if int(stu.valid_date) < int(today_stamp)*1000: + return jsonify([{'stu': 2}]) # 到期 + if stu.loss is True: + return jsonify([{'stu': 3}]) # 已经挂失 + books = db.session.query(Book).join(Inventory).filter(Book.book_name.contains(request.form.get('book_name')), + Inventory.status == 1).with_entities(Inventory.barcode, Book.isbn, Book.book_name, Book.author, Book.press).\ + all() + data = [] + for book in books: + item = {'barcode': book.barcode, 'isbn': book.isbn, 'book_name': book.book_name, + 'author': book.author, 'press': book.press} + data.append(item) + return jsonify(data) + + +@app.route('/out', methods=['GET', 'POST']) +@login_required +def out(): + today_date = datetime.date.today() + today_str = today_date.strftime("%Y-%m-%d") + today_stamp = time.mktime(time.strptime(today_str + ' 00:00:00', '%Y-%m-%d %H:%M:%S')) + barcode = request.args.get('barcode') + card = request.args.get('card') + book_name = request.args.get('book_name') + readbook = ReadBook() + readbook.barcode = barcode + readbook.card_id = card + readbook.start_date = int(today_stamp)*1000 + readbook.due_date = (int(today_stamp)+40*86400)*1000 + readbook.borrow_admin = current_user.admin_id + db.session.add(readbook) + db.session.commit() + book = Inventory.query.filter_by(barcode=barcode).first() + book.status = False + db.session.add(book) + db.session.commit() + bks = db.session.query(Book).join(Inventory).filter(Book.book_name.contains(book_name), Inventory.status == 1).\ + with_entities(Inventory.barcode, Book.isbn, Book.book_name, Book.author, Book.press).all() + data = [] + for bk in bks: + item = {'barcode': bk.barcode, 'isbn': bk.isbn, 'book_name': bk.book_name, + 'author': bk.author, 'press': bk.press} + data.append(item) + return jsonify(data) + + +@app.route('/return', methods=['GET', 'POST']) +@login_required +def return_book(): + form = SearchStudentForm() + return render_template('return.html', name=session.get('name'), form=form) + + +@app.route('/find_not_return_book', methods=['GET', 'POST']) +def find_not_return_book(): + stu = Student.query.filter_by(card_id=request.form.get('card')).first() + today_date = datetime.date.today() + today_str = today_date.strftime("%Y-%m-%d") + today_stamp = time.mktime(time.strptime(today_str + ' 00:00:00', '%Y-%m-%d %H:%M:%S')) + if stu is None: + return jsonify([{'stu': 0}]) # 没找到 + if stu.debt is True: + return jsonify([{'stu': 1}]) # 欠费 + if int(stu.valid_date) < int(today_stamp)*1000: + return jsonify([{'stu': 2}]) # 到期 + if stu.loss is True: + return jsonify([{'stu': 3}]) # 已经挂失 + books = db.session.query(ReadBook).join(Inventory).join(Book).filter(ReadBook.card_id == request.form.get('card'), + ReadBook.end_date.is_(None)).with_entities(ReadBook.barcode, Book.isbn, Book.book_name, ReadBook.start_date, + ReadBook.due_date).all() + data = [] + for book in books: + start_date = timeStamp(book.start_date) + due_date = timeStamp(book.due_date) + item = {'barcode': book.barcode, 'isbn': book.isbn, 'book_name': book.book_name, + 'start_date': start_date, 'due_date': due_date} + data.append(item) + return jsonify(data) + + +@app.route('/in', methods=['GET', 'POST']) +@login_required +def bookin(): + barcode = request.args.get('barcode') + card = request.args.get('card') + record = ReadBook.query.filter(ReadBook.barcode == barcode, ReadBook.card_id == card, ReadBook.end_date.is_(None)).\ + first() + today_date = datetime.date.today() + today_str = today_date.strftime("%Y-%m-%d") + today_stamp = time.mktime(time.strptime(today_str + ' 00:00:00', '%Y-%m-%d %H:%M:%S')) + record.end_date = int(today_stamp)*1000 + record.return_admin = current_user.admin_id + db.session.add(record) + db.session.commit() + book = Inventory.query.filter_by(barcode=barcode).first() + book.status = True + db.session.add(book) + db.session.commit() + bks = db.session.query(ReadBook).join(Inventory).join(Book).filter(ReadBook.card_id == card, + ReadBook.end_date.is_(None)).with_entities(ReadBook.barcode, Book.isbn, Book.book_name, ReadBook.start_date, + ReadBook.due_date).all() + data = [] + for bk in bks: + start_date = timeStamp(bk.start_date) + due_date = timeStamp(bk.due_date) + item = {'barcode': bk.barcode, 'isbn': bk.isbn, 'book_name': bk.book_name, + 'start_date': start_date, 'due_date': due_date} + data.append(item) + return jsonify(data) + + +if __name__ == '__main__': + manager.run() diff --git a/data.sqlite b/data.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..0f7565acaccf6e2dbca08a1d257df7bb85bdb79d GIT binary patch literal 49152 zcmeI5dvF`Y9mgfh*3;7Q3aUtqk4HxKkYsM}WSvCXnWnCwdEihJS||mN9Gk>oEGjDm zrqikI5aJNxBqU8jfEZ|A(8K{^h$Dwr|LAn+Fzr7~TV^`bnd&5Y+G+pjW7^Ji+TZT# zR(D1YwWsY2<#%IQdwaXz`~5z@y_0Tl_jJcs*Y)Lk<@+<)htfG&b6nWrxG@ z9=saiWiB^CqTXDAcc&#iSMoi_@_%H5(qA0`?;3}+MEW44hyK`jF68z9sd32X^}Y<* zxF7)}fCP{L5jTPw%6Bk9K8y(>>jp%!6Is>FmZ#Pp`S|Ywt|0 z+K`f0cYHZ@mt3QuyuL#=3(GB)+O7Bc=aAo+&h~Wm^_Vpp=ajD$w98jATetQ3JmID$ z=Z>Yhba%fn)>I8tR5z^p(z=wzI=`>42Rzx3x;51)cdXwacig#d-7?wltC%f+ZB=Lc zS5|el#9L{uX@Z%XV7Y_2bT-%3lZNkM<_4m%E!~-HHnX)W-Sbf2fY`>RdIx%HR>}6} zwqyrtR_WQ&TeHgTovUwK)p@tPHg&h$($~ZGe9QXI)aqM1Xr2gx+?l#1)tTyOPu(H+ z4Ls62kjrErt44@AXsu?VwN@LaDU85tKXNb=x^QT z^LWC|&Ccy<7Hi>(NO*107TI!3t+?94-Z$7?4OAtFgMcZ$sW$rl%*J%CFH^0fsm!yj zeYwp&+4NTVOY7IKORegln4p>DwhUTxYPAbnFwKK!Hf3t24H7+2A@($zmTmt^s(r28 zBCJ^5A-5>YWYTKgTCvBz&DsE%dA8lm4x+!6c-`Stv(v%O8iU{Jhl`P}^p;$Pt#=9Z zT|^}PBb^PN@Z!bJ$G^?OS7}m}H@7WxqPf*N^lX8vl!TTgj5GK<*kj?b>*F5|lN%!~FK9>74gSL40^mga0@wdg=dPG&? z$LibTQ`y3ls$2Z6%iZqqs>O4~!Gu663D0oSBa za3p{PkN^@u0!RP}AOR$R1dsp{Kmz}10;;ni5{a~{M2V|f@%b@h_ZU&NSXw-O;UU^_!A6ZL~(t%TayD@RJIC{9H^!8Y*TzuhT z@#rCAYFlx1w7l=oN@HT@%<%s5*?on4zWD0CnURUofj5jD`O?nO(y_P86EBt~4i%rv z7oQv|K6kde(H$kr+X*!$L6n55#Z@KMdAl(-UfTOaVQLKaR6hSkdDpmc3UV$kGp45C zCZh4wp+f$2A%ARU=!kLbq;cki*g*+)u=ferQ{m!~(#d_WXuc0L1Q~$o>D#(C{oG_xs$@ zueUc}R|$`*x9Le`P5P1a9UHTK59j2W9~>^_PnJ&YGV*)M6O`xj(&^J0(7gx3aj)Fb zw{dd@qA9npkiU_B5E2j+xb<3m{%zyjtHoz87*9ScLv-4`%#R4&kE+I%SWHRAX2#DI zF6=Fz-EC~2f*l#hUN-XQO9u{@N4CvE!b=W#0IkMhKwF9>5_(+Yjiz}1H6wqbg5)AZ z{7`A~rPBTrE6O7W%VV$AZXBNk=We$ zS|Y?%O#+joOk4b(UC#qT`>vAFrp#0p)77#J5{D39%g zzna1Sc-y?MzS^3IN>nA5h$l-Yr=gAT;2I;_jaT=SCy&yD=*(W@JXn83G|W$IP2Dbc zREvlJXKf8EDgW0u%O&o2aTXYPKhb1uBkDNyIOeX z#p3YU65|CSDZX^Jkl#mJ@R^+et~~O(@Z{rY?s2-KU#QBGHlif&vYi_E2?m_l8E*gmw zMIoxHSLWj%KSvUYq@u!@5ltg8U5&RTRaTU=CAC;A9%C!3>pmu>nolFY zU=MRkt#g7@v&|}EN~z_Esq&7=(hqhqwWLbGG#J$~w;G@S|B*xblT?yE`pktG9Yz93 z00|%gB!C2v01`j~NB{{S0VMD#C(!6>a+|G%{1EeDMF&q_Ci z3ZdsiUkgdW9|wnnH#h#V@pNNvr-Sr@a3lcyANB{}^w+W<{dLz;3O0O?M6nFw{!Zu~=nIv(!6p-5sLb^?r`o8D7*I!@ zBAOC|IdITPeo3dUGi6jo(_wBe$cV9fF!l{5=fSi|x~NP>tFX~28{K51revFpx=@*B zn~Z&$?>wxz)|8160yBiEHDdT7$i!h9C=YmyIz@;z;BnI_l4`QaFl!bfv&59qlms0n z3D$^#j?^09Ub$_;5GSnG2*f0{*e1iAT8PXwHW}vBLS!PQ3=EM}6U-VhRui0R)AdTj zVTctIu}a%bqKf!)HW_BkLS(|GjHVHmYgKEmw#k@?=Pk3y zCSxL=x6D;ShA5iKoic~If>U}M%*1Agrp8*aVs%P2ol;X*+GMy>3y`_OCc~XtfXwBl z3{hh6G;nq>m=l@7saV2(E{<89vYd-Av&qC}$;9GXTf%xupG!uv$(Zeij|-;OB$M!5 zak}u6=v*=(n~d3R<}DMn$(ZeC-ZG7*j0zuj8nZ?WZKel50#5%bU_DYs3AIkga(2Ad3XY9TW9rVRU<#EvP;HwueW zm;OfKvdNfi_=7KOY%VQRXOl76%v;9EWh!6VOg8hC@k{SI{L-jX`it~3{06|Nv|svA zIx0=VuK~OdUtituYJgWgyy$NR)`fl(`bFpi=^9Cr>fz}CinKKJx6toHKMb9Rt+*fo zB!C2v01`j~NB{{S0VIF~kN^^x7XkjLdfIJ&ok*&(NRnon^GN!}H`L?qS4 zB1x`cWm&gwL?p@QcmdkI8Wu_QYLO(1Sdw*BUL}(1l_E*5ph@2CcezN$E)z*j5=k{A zk|aozyeq4b=fX`KJs@(4pCxHW4DpEq#7mRBJJ>_1`u+7Thm#f1sF7*|SD^UnSy`G( z*z^Bc2MB-uKT8ri{(ouc0S8?F(_aAmFtjT)6xtGc07e6BlD;W@Rk{WG1&%_mz^L?{ z(CN^Np@Y)<(mT>5_5;kTL1SSgfCP{L5xT3wQWispL<8(cj(}mZ+uFJ?iUsLwV*k-=MyXX zsukJo;(xc|8V|1+jYRn!Ws9lRsV1hWmI;L-E14khi-~m6y}}57Z`q<%or!2xw79*d zp=t!XfnBMng4=O=x1PzH6=QMuZAD8KEtP~!p0%c4Xz>mNixIW7;5Q|?^-O-2mP=@{ z8|(0xJ!T1gQ2!@u(NY$>>Vy`915f|sUO_2%VqoPlfphuDv@~;W-x{Y~%W79l)8WSx z=~C6APy4rL=?Qo$Aj|SgZ^HQhtP7t1_mT90bV7Ot?);aeccrJK@6O%GaSIYa0!RP} zAOR$R1dsp{Kmter2_OL^aA^XTNAZz>WkvH_SA3Qg;WJ>Lq#Fr1UO+uLD8zHb^i8quR^oB={fu-hqW<)`KhuTUuBTwi;|}V?ES??&M%^@4)o*a;66dOrA6` zASjR>nHbCdX%;6ih@UE$HBY=)vUo~N`l_^rza=hOoP-WSvaz*wuyYveoWr&zLzYes)6A!bQo8CDN3PUsq(V{B2d%hK;#- zn>KHe7ndkX%XXCSRH~~rT3yZF+I@$Q=#L&dUf*!y{Dq5##!Hv4Ts5|~-Mn@C&fWG$ zU622M(*5*VPw(qDZ~Nc9|M2g}Pw2Wx7-itH;OD@F{XeeD2Cj>2VnQ)7Mb||luY`XT z8xyKKlV%$xFkO&7)MH$>89sIUo|v2$wq=nPD&(W<5jTR0e9EvJ(tYPr}VfvzH#$?2F@EyoxV zbv6~(^GO_BEVD?67B-WjIKE`tGC4yg;4#wK=1~#w(}K~~5{sKLqoN|{ST!zeCX4fh zE$HJ~l?nO^s#YgYY$kV=;FKu2i5K1*&1X_^u^gj1_pqyRY_LL43Zuy^1suAHw!O)h zp;1xsdJV3Y4OXaHr5tRMKu+o^lMThiJ8(W(T-5qH#zTfXCAKoo%UrDdr0O!+2!=GL zN{J;Tu%!|QVQWS#Et~4=EtQZYx(!VX7N!>~hp;};Ccw2>3pfrMe}gxpjZcx(2;eF> zI40nbWflyHY5_NjgW&=W#e%Mup?|jWL;>51C04Ys%{hK_l}R<6zmQa~u98Q?=MBMf zEY(4)mJR3l;+R6%5^bWE3nEQ4T6K{*H&&x#(zP0uiMp&9jzl+LUS7e)Mdlou9OJ>a zB}rI%1*XV@S1n@O^l)v(=#9l?`Fyfa*d@oJ1w1LklE+KnVGMXb9Ggy}qi>TlBsgg& z2cKcQc>;WQI>ZXs=TtIe;F%(7qBnj;xf63HyuT2DP3zIq7x|=imSNMz3(ITwWR-eWe35^1! z*j`T8xoL3)pRyMfXbW3iPK79tqPS6PDqWq;&Q(JU>FP45PGKvAQK2{E(+%V@NDD!f zoXJLo4NF;8Tqc9nhiK|85=_wBAkEQV5F>ke0&^hq;oBcYu`Lb0AyDN`432{qDuEeg zguHfVy@oZd@ntcZ}(No@@} z_+XkkJ62_)67c9Gjvqr^sfTmP7qF?m4r&>kaYhsrYDjAayqq2-XK*M(aeckU;0ECH zYT%rNkbYDL2&E;gt%?kA0$&&{+N(0@8z@#h4;LC?o#hD(nF0!hJrK;oHUS%|j;KZ= z0)RqH6gkKTb0|>DRDd%`8^~hfvsfpQLaf$WJp`_oheboYAeah(D6-HgF`fG(R$gAH zx>0Dw#Z~ZRG<98(dSU?6%8t? z0lmOWgaxvoSD(f)SV$tc30N3fsd7Lo0R$KSBD7&!X@ax{FN`HmtA#wLNX4DibMx$hU5k$$MD`|;juKRjV{1qb9o{kUWCx;0WzfkYYxH|0;1(u z0)U-0sssbYQO;*Xs`w;1GmZ=yClv3%Rgi^Iu?7c~+2!tv`?}GFfDz3UsnsN-4TKr5 z??&|`;K~K;!A*{uCLB|}jic?1YL~fIHpk)3(l{O#5V1;U?3TF4n5O=d3iJLRO2#UG>S3m$X)>R2!!R~?2C{ufK3RzourI5 z1y)WpSAue+IuKym4o}N?ycO=Mq;wQ|n$g&WI;t3;JJl<=sF?zN7qGq>*P-4(F3Y|p zWA;et`>gfA11ttO5Mwi1Y&dy9ULFG~)7Z=aY5*vD5r<4>Lo69G6quZB4jm;uK$b6X z7Qjvpc_{vf%L`JG6&4H$Nv9A(M%l}$!3h1NVMH!aP(2%PGpetbFXoF2rID=qjK%Ese<=xBw;5E%1|zY6h(g)Ebjbg$Nt43B^^=zpSe97lQU8E4b{E z(}*$^^{d3<0agTQ4j^0tZ(v&iH0H^yjnL8ARzwmMS4Ger+L=*d02%oiQPZ-pMz!2$ zGa^1@q8rk+&{^mqOA4$oU(FY9_nmZ6Vxi{h(tl7Mbs&Q!65@HfDB~} zya*u3;6Ngap*|q`dI5Wr05xhq;AxnFu7-Rw8ijH;j+!L_4}c?>whwyvMXf3$3Nq2x z4{+5CvH+s$$;y?s*{~bM`J#HwD|RkL&dla9!f?w4c<1yxn$c|Q?Ob_Lox@|L$uzFe zPD2I|L!oxCNP8XwApMW@qt1d(`l!_!*x=gW$OA5;s_SwaQ^QUpYCD3&d- z>{Woa-gM|Nuoz5&t7zs0~Ii+Qdbc20x-SH6AEM_)?$-0N*+icL1w504@_m=zj|K4yt+} z{RAI^!$=wEnF;x#L%<{<4~pS>8PL~%oJ0hj0!4x%OTq9O@GHJG4Cpnag+Auj6t|M-=!7XTYl2@q>46sE8h&`$y20Wt}) zzZs+Y!lFUNK!zabMwt&80<{FM5n!l$s$7{6%_k`XqPC?>Xr@?sh2kgIN=Q3d$%=fD zgw)&iX``sEnb}x!8bOg6ut;xlH#I^s8D&3r$oi_5DKFr zN1DY!Wnys98W3@*5uy(POcQ`hbkP4HwZfK2T1%_}w_M6##z9~LkrWdH{4i0wp+VLb z;8;6vg%{L4(mSYkQPKg+Rlt!Tgh0xGc}zgb#A84*FyIH~U0ep`EpFl1{Y3rIL@y$I z#Rk?|Fu-%+CA}GtU6}a605?pTyL8-Aoyb65=)&e>y$Q=Wa z-IXe6s#H4azy@-0RW!sl1nF2u(3t;Ink~2FYzQ@a9=9ak3#n)r=x`Y15Y3?)&xA~4 zMnE$;s1s{hoqUgRjIbIT2vgc55x2j3gz#6~GN6xVH7bE;5*5iA9~Z>>j@3~dq~oN2 z3!N|ts{yetTO<@F#xkA@j3^r#6MDs5wZj-`YaC$1q3J|U)Vod z@IZ9+SUYNwEeJG?K(XOR4-+UOv{Jw*P|46JLCt1wf!bwLMpyzG(WGaMLO!KOs%nmL!wEUz4Yy9On4FaLQNmGuT6X4Q@-NX4($+C424lthVHAc4CoJ zLW(j|;*b#rt4x$7-MW*%4#=b&%TJSB0!@UeOvsgt^d4@Z#Nl);oy8|7$$9A-7N&S1 zlAf2@aYFExI9kyK{1L#FgF-Ks)7%M}XU3yC)WAhRjrber8iXO-U0_(~p=yZ|;6O+7 z`cb_osHiU@T{aL_2pFyqDB181;;E^qkS;>m3K4TPlW0*bLYbnUgkTRm?Tfl9)Z~v> zyb*8i0x$pptFVj~I*Aas^7R7j6|gq~cXbK}P=G(Z02ydXMkE4cmOiiCoC_)*R{*UR z4%YKQQbS`+Py^aZH}ewcngId~{7n46y%u5y;L1x=nP?4gH0Y6lrQi=4QF}yI6LddJ zi(~|}Svoud=O~*)hLk`RIJJdfm`E*CVL{xkOK?T9%#{xbk7cHI$|Tj7A4!EdQUY=f&H_bu~J;EmW{k@z$np8^hwQ> z*V9s#_NJxK=&S|}tx$ozFm_caq4DWA)xkPAG!0+Nm_`t5f(2Q6;#D7O&2*(YO1d)C zBGCr%Sro7ASaOj_reSh|E27xVKqDy@i2{xrnSmJwda4@iD?NeD5NhbpzL;AzaM+@Y z9tsPpFA5Pzs0lmZSj+)j#{rE&-U1G^ApZ#vd?_P+p!<4JtXnv^SvXg|!T~5Qq?`h0xBQzXN>_pR+K{nfPTSX zK}APO+d7c~UYQjR)Jg#)%^I))9H9J+ScCI3itP^k^t1hLi2RWUdN32b2?tO-sN9MA z#z6jqC6w!?1s=(9CIXCl=>XEY;TBN6!WJIN@&t^@#`Stkq36@B#i*+S{Ql_W0;qSy zUi?A2`a+uO71%>*M=h8m=V~Z|WQCL1>v@=truB^kh1G;LlUtPVwMM~xIcAywOcXv{ zey#?(6?PR&uOJnzdk0G)mbo}pI#mO~9;_8b2J`8TZj+3yjK^9E4q<~UfodS3b<1;i z)OGC+L&Q2bUu12O234n*C8-Ocn!S_0EPpCDq0W=Ap#B${S!JX!KzWKL`NXW5ygR&kRi_j8UnI6hC(Is zDKP*I3apj$A91FWplJb9f>(QafH*|aE5i{UgwqW{$hyk|mI2^i4dx9*87Mts+aAT0 zoKac8-&fY~gA3`P*u+(GpKBWtG4Mqk>;!_CE)=E+V80ikgqsKG1sT>A@Rty*;F?CU zH^AA0enFg?XxR)2@NE?owh*yj2p*zqRN(zUyetua0ul|DAOIp5(Qx53Xa?x=#2}j? zTMd5gKWMTQ;#rBt?qaz~5Hfz-u+z^pO}^MDUSbPe!ypd1NRIuiUn_0U(qzuZS)KEZ za#0g_Y+x{hinCx$W9cn1ttpOT50>!;`3j12gp`WCjWiVsA|wui2r!G8k*M8~<@D=x zl2qCN9BT!+g56a#1hr{@bP5npwo2RrmL%x?WZ>8YN(FF$f{Ep9cc4Wqkpq-0a-{V2 zh;ZdVW3DDNmckM#N{fV49Ba^^WBp)c;Ddk_G2kv6;L>Htk3gA0gOw;pI+^$zsvXC! z@uhHvGywtc0l0WKTs+aMVZ9(a|AJ~x28zaN2ehz4?xYkbcu_0K3P=D3%1Ft>nB4n0 z(g3z2aGH9pVRkvnJID|q?x4A`PGJ`z!3=b~9^nSO#gA@X4y{344r)}`rLcfYLob2$ zjV2@R0rp}d-yPg3NOz#%VI0)gz{Vn40jg$5c9<@dWrGf)4{%VF9Jb&O1DAkSkI`BB z_5>`%2w7nbos?At@rY`KOobPn>DIjgF-?vt(+v^Q03{?=lNXLb$W&C?E*~b?CL<}V zR9#-pe(-(ArX(bQqDP)DFBqagbA%$cU_-`=5s8Az2BPV&VZj$KWw5ol4Vy++35^ab zwNUtgF$i@ad1+p>9e6$BwXhgc9HTw0^XpA+r;~ry>(3RoH2@W_>Is8bf*ZS)tx*>Z z)*wFx$cPh^I0Id6HroJP9I3pq2+4^Hhczs^Xn>ZC2r@(*rY8WKUvd(Ae+* zG6Fu=5`k!)7?Q#aL`N6uNFK;DO;&)Q#k})d`JSV>e>fQ@D{zIv{l1_TqdT#G)o)u-JT509?8<2_;R{-hjJAdHKtI2=rL#zt&tgQTUxc7ys#=#Tr4qV4VHK!u0sQRt&p~31xvt2!sC|Ojy_4~WGVF5 zYE=t=f*Fb;J7#nb+iC>gyoLl}5HyX=Vq1lzV&1?Ym~y_oT!#zQYj+?$Ob|_ zG`3?{4)~|Y&>{4{BXR;@Q3Jr(fc9lcD7_yI#2ZM40*o?PZA^+A8U3x$?Mv{-Tz1|M z*9;9az$x{HLT#wAmO^GWOJNm(ETNyBf&qD7f`1BUTb`@NHAcQW6B-fFh0^M7WzzoW zQ^wSN^#^7!TP;_lX<6D~cJkz4Y2oj+k7F_U+`hlada$O`pcVJyxJ6xiHdW4Soz6q)#Sj151OqPqaX8g76B`$OF zF7d!N`!R_I3JOpJ+7uE4G6pvT#?SG$02h~94Lk`$(`ZP-1T^r9fDAG*;Cd$$FhHPu zz_q$*PB(@nOApq$XxoHBLtAd!s zYJ|X^D`Hg(B00XkZjPwa=EFgo+%+oGmD;nq?h+RE57hzeJ-UMvT{BV+v+m?xejE0s z1?<#Fad$zRLV@+Q?X1S4s;Np zZjmvd?visPBy*inNw-!g9>RGc`wdyCIY3mzVkkTX;8E*pJ|;z)cPJPFgkcX|iYs3W zDO6uQqO|~0qj^zxNow?8P2TO;rc`&{?o{^#vSGO*H)XlLLMk^b->G@i+t?nXPaHFo z*&4m#mqp%J{YKka{%S0R6l!F2&h%NLVa&|`^@1}=e5RF)K&VtT)2*W~!Y5UqE6pC< zKA}x3ozUztNhsDp&oX%OW zq6#BoWbh&e3YpRjTN}V0!;#bmK8xtN8IW&1TsR~Vavu@pA-Wo&dk}>KUT87^S-}4t zAiJPMQErtHt{a3NW#>f$xeNdvhKh(3xDO0{5x4_Ya!`rozv+w{B9nSJ0j!4-67*o{ z1uYms0ya_`1cidY!4v}gALQGkv@Sv?Lf2LUk-XETWK2t-dHM$0dAZ}*26>_p!at0q zmjSoY@6^H~z%~I3>}Lj$9s}6*H9%Cr2jsF18dh=%%Zw2a6+^TvCO~vXgcN>Zi1H0+ zgaPJJX-ozN2Np6&!CF`X!lCs74Fd0q7DF!EAT< zl2fgjZ5J~lIUbwSBLJ6ERWm(C$BY*BA%i=$IyG8YROj#B5jj)a!Ihi*G_%CcuYIbX zWS$UnbOPjynG6tSc1NVJ>bybZ%(^u5y@)4BkKe~{<=s>(av7atW^_QdC;)rxVu{scgfhYnRAbdz5k_%e1VFWZ981g`f{RV&o z_zwO7 zm>`>dkxbwsB)DuT*bz8vzX4vY*CY1uLMYB}iCn+|c?s1(v4x?8I@`)HTzXdxhjD;` zEzD*>P$*EDC|}cnr_m5aTVghg}1} zfJ#m?fP55ge5cM?rQW^(#)7_XLt$GDuzp&Xk^^?Czx!y&3g}&Cn;~|0G#zZ~zUtH{ z)Hdwf=N|QIpR1M~qP52yO#(0>z{aQX>IBgsyEP}`{OW+jsAl?n{53E5=;0#mut9(| zt*%GsFk%?7U5EIWVi>b5l9fs7u0tY#Dt(f!yhm;j>nl>u@s?gCUmPwmL>(?kWyT{i z5u;C4KN3$Ocn{Ok;Eunck7maMAKDH28n~Gu^uMY}6u~Yb2tsx|RGOSV2=O2o;}T_s z6d!;F+93fc;fMB*07ZhHCS zh06w1fW&(RCNT!RLks%!mYnLol`Bt(264`m2Si9nY%Ll1FeD0oO_|2Z3zkQ>pzTr; zj~%Q$Utbh?p>RXAxFte5Zn6~XWt`kpH+-){FC+Ka1NLC8ZfSLvL3~dsF zQQ!YM76ZBlohV%m#Fb|S8A9wf5rAUlg)0+yG**tZp7oZyoxKtGE|^qKVH&F(MkH&) zULEb@DAt}@&D)3+o~tskH6Th!3t=T!C>dbqbyslwos|rIFLEzn(-5Smv6+$%loaie zkTb5*NfQKzwE|^k3)}9hh&Q7yr$~#VSkNP(j$84~=I4dSHMAts7Z11KBi) zCTRj4<%?H=9wIg#Wo!GQwM|TUjJE_RbpRXY|MsBEm~0CxM-cLxSRkm2F0hjGR~z}1 z4}7PcAb?I3+;nzSCB2Q1DjGE8?tIWavY{-2e6Id%s1u1*ukDBwC+|qFPW7W=-Z6A< zg+-zT&0D$>F3KX@5@rFA8S-cl_Lu=uOwRU`GJaEM7l8Z7cb6DIxWK>*nHY)0TYSA> zf}^fb20%ImvWBurNcQZ69>!7z#N2z%9^6#{)GSgJX3RG(bo$u}Gil|*UJoo5?HM== zl$V8A5=eEJA~b+un4HsQY%|N0HgqdvykANvzwMGV`AyUsxMJ`afresfELAI&t}>_V zl6SDM-h`#n4E7~~5;pG)Qo=DXVKVZpR*Fh{CbS$YhuykH&CpNsP54PEDY&WRa?MMV zw3J#QUC6;^dn={#kI!S9Fn68PD$p+uQVk+1{tg`X1f&l0>~y8I=|HYF*SSwkN!6c} zX3x3GTH;Yht)36yqkE7d9dYx5gRVRm=(a|f`hLH+O_+QuBH5N}X6zn|=hA_aGx{La zX1+J#HED;U>sWHVsJ(h&O?vzWU`fYBJpO3te$y+YTIuE=X$B$?NkYHT$WQ_ZKbs0u zcBH=1!~i@>gZD&&BY`-B)`VQT9gv(bKO~If2u8mcw#+3GgnEEh=Odl~RoBKR#=-!P zupJ3%UJZ!u|GTAVcfXKzwFoB3bif3@a(G?p7VJs|94sIXK z%a6gXNslxEtw2{L9f|-v1dCN&WYFU6iOrP5^e#xpDe`0#pyYgn24DbWfZflMWlE7_ zBPU==v=UIL8+NTg%VvNh2Nn(%EMM9iz`i6Df$%3?G%ge-D<#%l97kQUoc#DmB+MYr z0=HYLqS?fZclRjDX4~Yq%2@UA$+4`R9!1JQe9Cd5y>4h^3>I|MyEj=ni0^Kw9V8kg zS}7tufd}TgGL`ut5m#rgE9=g-U5aJi2oZn2DbYk8Z(T99dbdJm_ zr$nNvhqE-DFBI?m^ZvGg`{hGmvw#)o&uDNakVTZLVE{qko6KQYKr1$o-I(&E2%js7 ztq+7kSF&-9v$Mw+>4M{zi2rDfFvQ^)xZ}SC8Up4xot|T&srNo476%Ft(jzzldqUUEB+njP? z(+bCJ7@i8G)RGt{BRYvpdTmbte}GnL389mCNZvxQv+ zdhD8rr>wy1aZ;joxKAV1n?A_Kgr&8Lij3!*#A~l;N5LtOZ6#MUSE^ZO&4F0YH}>Hq z8@!a6u(aKT88_a|yT~iZ7o$(n_pJLmG4c3Ii*V??`m7o=*6WYGbbq|NPj1Y>o;99^3k+=jI;%Dy&j1!~) z6SZSBYKsS41P6z(W9ZhcMzCY&qdY|R0JIZ{YAPC9p?_!}t2qZxhJ1xR?O;fXNRW1F z*cb=%{9!B++6kM8^t%#Ufp@kjX~EDn6xw@^5hvrzd-pz4yJXW#@ z0Ai|8DXal6ar5u+;6R_QRQ+%!XoJ3)JYy;}%za|K)o#w+NFj9CWS<4{KR`~v(`vD< zy!Ulj3{&psC)`q4v}eCASSqhFTonO#oMkz^)h=YKV(-j?qw@`sNvxNn?OsRth!qv| zh6(Nllj*sP39a=9{u83`fLDTSk_+HptOMXPEYK^yfIg<}Lfcs7CKKdB1c0Ed%XRL+ zx{HZKq=vF>MvOSV(qz0Z^r#DnFpu zJK+O~nNngt8=u?-a|GBN1u21Yj^MrY|10kZ zHw@f7*xNrJjKLpUWd_vzfA_S&U!;JDM^Sn{&UX*P)o9z1AWE5v%hpMn;9M!S(w$m@ zK|p7QO)WrMu&J=cUtOLDT&kB&s*qZ=0gPY~Gb;}3kZYAG#A^>l&}|a6?z}_t82w9X z5^%b&`&tsFf;BdsX|X{5JFYwtYPcgS zF;N>j#^}(L_@-&pj^~^UikgGQCF3*xa#6shmd-@&WPQcjeH{_h0Eze3t%|y11xJ@m zHB+5kORryEIzbt8RC0bTBX5$Zet8j0mPT8Oh(2!T9i(ZL46;;f%#{=P_MR{pcu}27 zIe3);ehfPvoE0>3HkTuN8E)c$n_FP7G!HZ_^ip3OD7@tWm;v>pYbZwmMd%I)3={+; zpiOm9ua2x_I0!I)&@?dfQU(h9Gh+~IVxhjd)ZGFTCn*{W(< z&6@(U?P@-CjZ^fDL!^GyoI`z)W2T?ew5PnfTR=a$j7K(D=?e1n)aoqZ*6>b2+M%k0 zE&5yKM_m-}^z&Y}q~E!6XDwsRk)AmoO{?X<9o}l(x@olkscN44l+sha?yeVN?OzYx z%t&0LGcCTac)jG(@t!$q`Xvrt&krJR-NeX!KePWUgR^HcHP!iyVkhOG`q9;ebz7EX z%YSRFI4vo|js1yZ|vi_wnxJ*y+RL8AhG~{ zWHN);7Y4~cunR{4E)`@rvX_vpiT~6C? z5;m$vi%8|V?O?8f$HrTg5TievO!uhL8^zg{>Z)VlYZ3Rw{GfTGV0#ltRJ}`EtCKR5 zuA*%;&>g_tVAH7Mt7%nHY`b=npdzIq#-r-W6Hc6`i#~4%?>Lx77Zf_T`5D3`kGG4q zbB}G!-E1LT@_s*+kqhctR_C5j3A5anqpe}yzyn*&&)&nMSmPr-IYi5}RPR3cNtg;7 z8n5u@Bp|u*XD7?80KN;4?IdAZ5GsgVfZN1>k@8gxbHnv)A>5F|f=IXhRJwn~B9^s&2mlCEJ-2Qf&HE3L5`>BO=vj|H$zAKy zGTqf6ID)O~Q9oK>GU$%zR8h$E&2~FHcIOM7H(MqdOH4l>o$N&tThoI1nQdB*dl=gW zpV`BndfF;VwVFX zF|nzc19LcVP(V=(REkhU*uHKC2dKMfbSNZ8h{-$;{zlwSBZ{nPk%N8Aj5BzV5p7hI z>r8%w3xh-1fM3a#PiQuoL%%CZ)pT&qUxY`(zG^&irpMiBI}nRb920(cgL4eHJZXpK zEMjRBz#NZ#1jfWY-`q_H;2$=~`h|0k&VA;jBhR@{ zKDVSDioftox=Ub}@z)2{rC0E62j``v)voz7Yto-tejzpj_FW4LhVKy$ zTM@#{@LVr(rqMkb?u`AtP7~Jsa`r@X&xNj9iT4qkF3H01&Ofd(X6F>MV!UR|6<#^= z`AgF0s~qOL)pk9j13&MT9DKP|k++k2G0@XL@{bq6-)6X*nrxSaC}BW!;Q8s*(fToe zEFYQv#m4H=+n(8DALFKfA58kzU;j-JKUk^cB(?vxJGP-~<}b7Qix#bCe*GnOP`Tf% z32&$Qf5#SNtIVO?!q#LN10KJ!o>GIGP1Wae!o0p}7BAV6#^@hsQ9I?dZfe@?1vkD1 z2p?Q6cXGgu)TB`P7}7`Kqf5Uxht|2hp0X>e?(BC=M)@kCEG8H1d^Vc){NgS3y>FLC zN)K(kUru>YH$STO!8LC2n{}&AeL~&Yh5zV+^M!Wj98G3b?Z})cb>3DhPPrId@WRpN z=UCPI`*CkonUh^7z0>Y3t^Iu3zzwg>n!ajOz3Op;&P0FIddaWa`%;px*!PZ zNw6z(@A0n65quL_bDTEdw|CmXAxhe$5ysYWHGA*|PmnnBrT;Jj3)@Iuz7ejIrFT`P z#0`dQUtc7cOa(DbAz(=p8_x#%Ah^pv8Y8hES`HQ<8s{Q)#;4TMVH=P7znk&?moO5P zkQns75Xub*oF1qEuB9!48bMGO_nslJVhyXUuz$BJjWCT-HQX>?RmF*t&?E>>PquAs>rXd%XK zVzbPZrXbgpXn|4Yd`{z0bXoq$i8FKQ)M66r%ltQV@{|x!43FKf>$}0j3QJfWTQ~du zdAn)Ex@(=^W|Xe{Y0##o2BgQIy5FJZMEHZ%%XHnxWDV< zQ%lx{dF(IUS~a{|`OA=Rw!>~rsNMGA#=kS}jNdx(-^KpNtNXw7%+77~YvI+e=-lZ4 zPNe)JV|8juW)t1&Yx6jXBLla%zIOSY#Y>h4)`SV1(}un|i+xDRTzGFd)_U&1!dshO zPYr$OEBcNtnM1uk@`jEvd)rPYS*t-j<&->h<ZL12_x!fHVRQfBX>ax&j$?+hj6+}k-TGv3#>x8A`lF^d8n&2ybH2Om za>nc%U27*N?wx&SNS6iklk)NGEfZf#$4QxQNPc-AGv+d=7+O_K2G6Jsi~W_@X*wl#yvZ3c(Z(WU;J=j z1EcSg@#6N#_|90dTl(k2cU3=YTIV*7*c=#KnE%*upD|`+WkC8fpUmU>nThvpYii@) zJSbaVH|E2HUu@*Y=D+hDYf_v3C7t~$apDAnpKJ;Lu4W#4lrxPTfikU_>y)^(c#f~U7+`0FU;d{n^Tz9(mZZlC0oCia@4XM}MB+x8W{ z$G`yvI4~dDOG8hfO@k+Q2M9&Z;K2k(OrKhi&nm#$CZ6 z2TR}rXy5`bRZ(zp!MC=h?_ok&y&kMM3&Q>*Hh%(afFL2fy)>1nz2^Q}MxYZ(XCmv~ zG2W$joP?a%BFLMfKUv^5vYOS@Sb+9oL_gpSM;{4<4Nb^q1eLxmA?7cTe^N%^*bljW zq*rX3bdXr_$7!Z|)V~E-;5FUO*nrqKlbc*(8Jpt*+Z9~p+)eFD!J&4U%@f1aH&cRJ zt4375795_~uk-#X@#)Cs!>(B?i|<|5yt(`;I-&SLL9g`DmEIJ~@eT`~NBYITogrQG zdPe3U)}>CNL>zbP{QVPHPlM!`lY;^Zl>R)*?@cll=qi=epWxd;PUf8&G@ZXai?aN=h5rp^4w)rW%Ywydw4d!uyb8&ONpjmOi~cm1TpXJu*Z4?Fy` zFujf8;3}GZ+v&wMCV9-yPrJu&cxs*1mc!>H$j4V=eYrB{2njANBobk8qHi^ z?#1uj@275M1T=CZM(+w@U2wnQz2{!UBev~F#hFD@V;raGNef0M2l3;Kn$EN2^nYy( z!jYehe0#r-)whZ+4_Pq%!*-qvFteNtV|j#h*L;dH?kMiICuGx9M&g)A&&5HUF>7lr zrk0PpULShFq4UD0bw?ApPj-;*m6K%V&lo#dzfTc%#7XeYefP=>owz}t9+JNF*0LTn z!gS8({7X-*cNb#__?CIxUMT~OmB5>bV$)#f4>HJ++opjnwK62UC#tAlq*fzZHn1l!Y!d!(?F4N@cEy>S$oZaKDHqJehKB40_L4;nrYpr#~eIw<>v;im0+I)uo?<~1KO$U1{FOwh#Z~wBNf+LZA8@+R5Bka;$vZ z<63R!nWOg8j`~SS-jAMh>$BQ^<(pI(h8w0zC+bWLo9eu$)rEsaxtq1r4yK=2-1aEt zA14%nr{PfqXR6bV6+j;fqYf4fP`;j*`!YI!D|bgoCR_w}lMzHRw4n7Ul7%ET)_O;3DZWX!qn@3}9G+$*KW-q&m~)rOtQ zYUv)5Fmq5@cpgrfv2nvO>9;iXg@*Y-*BLA2bx()zCOzu;)H1(e;UVo?=B<4dx&2?I zQ<~3gcnkBWhiQ_g=|i)I+}d$t3h~5sX~2wAz+OKZhwom%$$54#CUAUw=Vy!X<&#aH za&mv24R+!Fud3DSer-*E5gMF1NZr^Qn7rTdo1!&cK4zHjOU)Q;WF@KdrsG&%A^x;; zhU3eHC9jUHJt-1+HF-v)PGw!*ylhI1c5&RVZ+2I0dEYl_pB38P4ngXe>$xbn2S zo6%uJR%;By;6N*CY$0|c3Y0WTzqb0dI-8chJ1bUyia&3JU#JcHY;9v&I`3fgcWlLQ zm&f?)C#8Qy3r^>8B0ly!UFIrc53w(tHj>e=`?~Yc?(wFN1(lz+T~|u1xRbP|J50Vx zwZo2BEb?_P){wT{uRU|c`san+`|>=sA=f-llWG}xRFflzJ_+4qHWys$eli|e!RnNg z4V!FSPx$+II+z}`bCIshV8^e*KCPRcO$}TD`#Zj4zU#kEe?VVcN1ht&BnT-i*mhpl z7B^Uozd7B``4$o-Fs3M5j4P-KEm?N0{(kq#>a15A19u6cuJN9Z~K?q7ZR5K zX?=m~KZ=C)Hz0G%f0wFPbd-L+H}QF_ebcGYm$&qCuZQ|j!gX;&@07gdq*Z@yUSBtR z+eN3O@7P~g!oPJc3Dqt)#{W_Lv-zp6Ki8zT<7?v*erkOpxv+zCgB`oRbjr-|O5w>6 z(*B;O-{vj&()&}}i^NCE+(v!S(ayk=W4QSRohAMFfvYEu zUhkZj;QBFZ!?$l1%kR%&eq6dBluKP+c5J3?P1)r)hpR3c>JPbWa%S8<#_Sw7CbRXT zNVb1T*rl#j`~FT|ck|~<@!t9UWdZ%^ahB$17maGRN%XpUi|am9&5p$Cw`Hyb`EXIlz&>Z@z=Sk<4-Ha`?a|j zPwu*xUt6F1@nGa9;gP`$SDp?15cee3;yZS4{8QcackzF04|Lh-QCT)?_RK{cCHVoe z2W1&k!&5z*Gyl{&c-FdBr48*k?Dlr9%?YgUvTN>7$DSoJPRuYZxH!UM&4E(knEtqn z>wf)H7#jTc(!mQO=FL95t?TWOe+BQ&c7*&sQ&cIY-Ya=GQjk0=c=AxslS8h{!h$T# z-+P>Tq<@|_(y+`u){tivvE=t~yU#4G?7AKI+w=n=)val3hdhb;*XeB-^~E&ua7F0L zgSw9^e;WFv)26wqE^{?E1^TD=#IMsr<5%KTNj%HoalT=Yo&3%6FVj3;U(a z`CO+|)|vVCJNCMln$eaq_Cw?shHmQMLG&rPxvR2s9<{E@%M^U!aP3?b8QT~~zV(qZ zT9q&Df_~W?_F?<_#$AU;uf5CvI$~D&DqHHk_bV+zUIghJz9wDzLMu;vXTKd^q+w6) zzwEzy{`fz6UjpJp!3Qmjd6ThG z-m=Nz>CR!**Zvs)`R8i0d+)dE=Lug>qg;;fcR&C0wM#8$mUYu9Eyr8V)JH9|?Y-PF zcjn_em-4=2mr`zj@iIQ$>bL$u=p#!0LL5;~tcJ{&sji=_eakLc{m}Iv zUBS|?XI}0M{QBm>qu6s9wQr|=s9kgJd7|p}*gLWlt8=mzSq(YR8_E8~oa7nWdxJN| zFKPc<(>j-wPq*Dxbo|p1lYjE`q$APutnUX#jS7uy-#Ykd($leZE?KwT#((a<&iWYS z__-i1W{#+I(4`uCep-4Z^vj!H_pD`1F@}g6IBiE4Kl!z5?ZJ%b)M>K-H(bV?x#$F6 z@_V4$iunv(-}=*uTTb5;Y+fCD>G;!_z!Q5$uHEvdXYBdz;P2ScZS$@xZ_8FJ_;}l8 z>AQ%Bv+F=ZD|tyjWR& zfb}BxTGOmEi$+cCs#F#inz`MYnDg%jMaJ_tt?O>Asr$C4aAFUqXl}*xy`F_mJNi~+ zyS~r+$LZdUo9VM`M<34o7*+Y^%zX9b*yxtP6^>^-kB<*C_RU*X^!MUQ^7@2OV|vLl z?`el>7RCkm9#fzF+B)rhjqA8lmxjyxk|)Lv*-oEw(ki0)>fHsnYFCZ4W})9ICsX0$ zppk2{nw9E5?|C1w918a{ZFH$#&=ldeqkGwr>yxLucW-s;PHum&$I;|*(DTR>Imgx| zUCO!|uWMY>K>CiQ8OxvC$ESIlex_}8pLqGtDY`?X;^kRw;&0JQ%fhTDQ%JMV-}?LD zVk~5S&Rv)vfSt2{jOb)e^A$hZa{OV-!Vygsql6WXGq}TU&cU}Hpo@xtPdWY|caI0T(D&5l3co@g0y z_Wf1Yr_sfZeA=C+T~}*Zvj@LuajLuY;Fp5!{fPw^KPy9zCMQ#_O}iw-^KY#VAG4+S zu&voSpIDy>N7t0aSH&&0vbSpz?B~{#r&n6Oj&;(M|4k`5*N%O8?nn3Do^v1rn)DHQ zs^O?HGU}Y)wSQ*Eu{QmY!9BXkp>cAfbkod`Eq~X3vrgae?)mJ)9Xzx96AH&!Jk0-^ z!ccjv_p^Ru`8xMi7jMWJnYAuQZ%pYAlH6N=-$b->ER6mbMyt8=_FbG@ z=<`$Ce9!LBQ>JWGKKYPwP8IO$h|eU>g`yr!+kvwVvOiCdL+e~qk4`Fgp5-_yllkek z|H8gq(!lrEVUFrNdTzYSn;{pcM?}#=pKBf8H_hKPqEZ?2_|R8L_uz2pnX}xEm2RI5 z(Qg(kYxp(vp6V9~|4b5D9eDb3W9T%y$j<0t zULB@w!nL}o%{xl5+iC zTD5mYeHKg&{9cDCGVScmHSJQ+tBJ~(cdlKb!J$rmA-2mj_dwmjB9sCZ{0-iYpXcT@Rt~d4H^XRE?2E?NjrFQZ&ToiX3prs3rDyo zO7QsuPk}f^Z^}1<*_w6`#IAz|&ItD2q>g~U(RPJ(`i1D@4X}gMrGFuM6FkCq;)!E^ z+3b7V@^0<>&Xl(F^EH3#InHOBg1eU=Pg_Fwytl=gR6Ls3^*p94A1XuYd7ZF4@y|``L?&;(B9pQbR5sTVEGE*(ddE?y~dxJvaNheNIM-*ZPO@4leWkklXmr{f)1q zw(sb>d6e0mb2-}M)al|0KdU0rLW8q9?w(ti%w3=;$t~n!LGR9n?y18!#ntb4a*Xs> z(nyz-r~-TQHTCCx_PukDh1t}MhN^Ek^Az{_jw^D;()}&gYiOfnY@gmtpfL5uYCC(4 z7TO~Ww$n^O1jx}0llkx~TF(B;>@09V=?r}0NQ$O$)2=@XX&K5w+I^PoFq=HUGRQDU z{{ZVzdut;gMc5GFaKAA8YW<_xE<}3)0B1aLOqWowNeWHo1{{wq8NX05MyO4ujQdS1 zJKF6dkbegRkyaN-vo09G2px|~vnHvh!GIQbC!Qm2KDiXw^;=`ikThsULvPMbdSmHN zP)aC_>DJ_t_GosW_G&paduJak2)y-QmZbjMv)%`jc2tbyLfQUQTWdD=Go(pxa;1qI zo-M2S=9Z(fA{s;)8C-!*O)3<9%D6tM3`f-S{{YvbD=6+=$%^haA1>)59Q_D2xUu7@ z$)ze!9MibDOe3VIQmXQ*pInUow85l=IZ0hx7zc63<6GWcFrdlc(zxt%l4%z{R^RkO z#A83a=ijeda_GwAFR(5+^2?9sT^EtxjQUb}IOVWET6bimk<1Pp1(1q{>lUb(#lwLJC6g>w|uZ^q6560?8E!G7!>X+ z8A+$WfZ_r({{T%J{Ax7PAOw;hJPYMn0NOE* z_!X(;()nk#7Ys8Q`s+m205wvZJqv@(|T&ol*y ze5L9YLG{HRO7ZfqkjWLqeq=H%i=1Und7u@8pU$H&MqhuHyT?YyBCKrmr3vdr!o&e~kU#*BN^ocA zpVFn-M#?Tb5pI`uck$_u>qnU<_o_{lY@i59C9-f&y(yAT%s(oBlBL9qi@kCKV~{?y z?UV||Xa;#HZiB*a72?wzm#Ujqg2vDOveqOb6?Bmjt?8DNFip5{GN!X9dfpX_0 zvGy3NK0@@QHsYPa%&KRVf&J?*JyZdY=RuX6f?3DVkyaWNMay6hQ%oWtR2NLY-RG?W zBlBCX;yLI${*>W6bIIr1rA_1yN=Aq(l1EQLngYW-|EN|!Py0FBjw#cR%Ppv%#X$bm*tc;_Or1Amh^``#-X-Via=}!9}erOg)8?bTfMuPW* z8k3LV82V7!i-aU#pQkk8X~#oIZ1SXHjRW2yI-`TcV~=x8{?wN|#H>%@QhkW@#%ZR+ z?z5=R2QKJfWN56p8_R%{j%S_t^Y=ZAJU z_FjKFkL`1UNg(}2BkDJ7xH8~+4k#HZ)Gd-XC?dhYDk8@EgU+;K_?if^2S_{r0N*uD z)11^Un4-W=_i%DHxi%^sO$PwC%r){<|KYLlB%FE{HV6mFRsDzEu>`~N%=?XNj{f<0|?&IbH;vD z+&=;{NC&;$ng;Tffnm-gOdoEPTPT(%FxxqB0L*bC0r=D_rD`&dlE&oyVc%S&#nxADzBEjG}{woCY9EX8*DD0v>>)kr))sZNUi zP>opNnMqy0P)G93K-xX>&d`{EAdE3@k+a-4Lq)>I#2P=9iD0{oILHpMq0i@xQ*JDL z&)#0$joH95TMlv96Ije|H2Ffgk~|JM%8-2s;MAi|)uqJ0W4dsDU`iAG=nFCO>In?- z{gq--7yvTDpp))<)9y8fxQEJgNRaT&3`!W(YoKb68_2eeganbeXO6&n)I&+sazD_e zIp+m%PvO>)M3(w?n>;Gbst8!*m3Wwu&rS|1JG&h^2iTf}&FWGPpP8xS@b0SkEojKO z0Ogk;dgC>9ydig-{h~G){t?gRnpy?fjpXTJ05e?{?sfnN;hH4-1ZxDA>hd|ynH<9) zdUVEWG_`pyzRZmunYgIj;YNQN(Hx%{l!s$2e}~D?T(SBMO@rZ>pX zrWoV-R&elj!3Q^yjDOQJ%`x1{x&F_JWn_gVQ_k4sU_Vj)Y8Hk&tCd?Tb{u7seshke zC#7vo;R~-VOcPr(DEYGNHy@!KP`(hkYh-`5LYExwLUN|js-O3BSa9f932_t0d2GwhWJV+> z>CSztS}hSg!1oukNPp>&3O_TNX&K4uqbAjC?PuNR+m^^AZP=`b)Ex7ldgjKNV!X?$ zuLlK0eExZ^xM|idRLHYPbF}W0fsvNuFLaq{#PR_|1r${#Q2|4%qa+2h?0qXo`#VMo1e4sO0rL}#=iF`Lw&2%w3DbO9 zUiJ34T$AlV)wS0P{ObGY+A2s<<(UWo zH0z7_?rrx;Zmy(ovnM0pntY=6hdDN%q$c$(I3Q;x zP>!N7zzj63%-Q@n{VS%Q!`g7$yhH}> z3Kj(Z8O2sU9McRtBc5|NPrF=FN~bQS1-6fW zc8u`bIaTYqm46Osx6!XB4BknNRN$EyFF!+Fn%v1KbA?j9jybBj&85BE32}DJtb>MA z+tU;nv<5pI`*D zOv(U0z5f6z)Bga3b~j*FTX^RKjjti%iy`x~8&c5P#wgx5ns#%Is|GgxPI~)QTU{>h z?b%_sn1moX+xIi=T`j+dC$?uX-$fa5gSA}e^`{L2`47peDO_U)CO_2DvKpH*?lc~W z4W+;!b0bWk6Z9geK8!3vF{F zsOlMUx9Pj7}0uDT5x{GyJQpmQ77x{akwo@dg>FVtcae8!NbVW06acr&+FtKAHxoOsBp z-ae<+r5ii+H0?CF*qX-FLmOY;6^l6@v=2vdIBN3FULD;`Ve70l4e;&MkMVQ@QO zcB{~8kj~2QcCdCafI$4|+>3b+w$t5MG&+nEafADu0s7UlvRf|pg>JvxbWDDA1lmoM z@-@-Eo-rrQgC#+_q9vbK7|h*-HjLrB9A2BWG|+ zrrvr}@x@89Nel=oz1_{Vk_j#wXk&#P*wwsNZnLE(qKMfdNWt6b%^)&vwaKM$A)bZ)@$jK}hcwF+Y7$@@>tjpVbh}_F8Xc>SCK<5IBD6i0E4$yhQtx`uU zs!21U`#J?A6wY!^I@LoIbI4_i?r7OTAG<#(A464KY93i__yBrhsm8Yv+Q?o_s&SF{ z)g+8%QdiJMhD5NE>ETfjeXSb|5P!Yz)}L>!JWz;{)<2s$0~EIpC-ze#KbR-`sz+~M z(vsgma`A=(2L$_6JsG?;TALASwzAuaC3GNg-!S5>S<7d1rLFcp)6NG05}>ZolSB-Z4w=A#n~QOWC;Du0z%eMa8eL`%3Ow_-SM8GkWY zliTZFb2PGp>+^B>ReeTFrj95iktB5l?T$QSC#4AqzQyEUs zvbye5k?maNw}^D$Os-lvUmM3HXV)DnNW4vH6A>iAl%9yBQspn5ofsa}aagb7jWIA| zb0@A8jDB?`w}@?S=U6SHk)8hly@CB{SbXNADf3nLxBz{@j32ElOp(Zl?l;JAiQSE6 z-c6{4lgbeW=Vt8jT)cKVuA!-zqdM&1 zb#!hHM$wKCdID+Mjm^chWW#D_IOFC6oYZwRzLzFf$Yv|FFCBBA%A%A^%ZQ{wp1aqP z`BQ$^kT=`9@zaBk)}j~c((G$F%8q$ZdHm|^X4o`0QN-I_CYL-Q`364{d8%8rcD_e8 z{!}aSNWOBhJmemnaavD)s0kLcwmlSojbQ4YA-av=`#f(OZ~p*P>JOMbI@2dZp+P&M zu?Vf6Q}1#0I4*{ny_EZ zgyk{+x0~q2z{=F6jHgzIs z*l^#aXKQvb>Xv>|%+bu8#=x9s-`cXTF7<1Dt8W3AFy;32{3^3vYn#bfB-#f!j0_*^ zO31EeWzTbYVK>?COP!y3DI0RPr{P-mJ|YoB!doG^@skK-_;C$d1-V<74tW5GvGKdDuSFFh%Z>_5A9TTj*sfkdCdLe}|^sq$3FnfXo9k z7C6sLnz3_&j8X)1#(BX$qX0UT@AIwHQrm2dF-j z+-yuLshINW`gw|NI`B_!%b1zbv!8!#)b_JNHe2bMwY*RW_NsTuJd>VsJ9^VRL7)vx zn~PAXcs};yfgr{(ee03Ev%1r+Jd2c7lW1?1gA5;WP|k>+k!LOVw^4YIJh=Pu6Scn& z%9$RfT4IG2fH=rMt$E$O?euaXhG};v-2ju1pst%rn_In)$(qs^hi*xf82UrzCj{fL>qri1tjeTrIL}H$ zxf~ZFW@YkbO}*VP4Okr02U>XG&YG1TOWHIj>g%)AVh1e-JidJGk*iiG%X-S}C3W{k0oXt!vKnBrB zRCmF}1?i=!h+I*MZt+I$XaQS+z{&K@Ap(u#){~b%D9tej1t?MJK|(r}{OK}BQ9!Og z6w)a}(iWg%5GkcaF5#NamiJMPMpd$n!jHTr!9MuK2$s?>?ax7)Ob(Sw4MOp_lTF$^ zcLP?E?TFlMoAMvyPQxowf)924HNXP0PM2g(yC2f6pEin*NKjWH@lF(byH{uN*CmM+f0@S2DvHbc@VfXXWJnRhx@W$5d)YQRW_E80k$h4@!^ipqQ98=p-5QFfcx~9oC}- zqbHi}vQN6$qRf2JC-R4-G{ik9YnWrnX$I5ISn8bk0YlCik34RlE{o3C~<;1`_s6JLc#|>HQZo(a%opiuqQujE$fC+R~SID zDl!5aIO)`LK)17QMY?m}6kMAWUq*p}^6volU=Q*&O5;hMHpFiftJsGN{OfpGS|9ml zIrbmnOqWlykZtn&aCkq`pD=QqozxOr$YYr{z5(hF>MEn0*4Cq>-U%6zOvs}HV&LQH z{uPTPZjM#am4{{+HA$0VOEk8YQZA%qL4r2po@=1CgG#ozNg|0vzw*qqoA7;cn&#Tt zRgcVMAv*4+#T?SUAxDvypOHz&EMbcbV2sd^q}$lrvGEL-xFXBTiBB>ayZ6AyHFfn>5r^Oo+3#Ha z;74c-f9S(*RBQ{oKAyD&(%nf6c*_LfuG%b#=aIWMxW8zHN)U~m#6B^_D{8TpWf99H zN3S^_PLqOej;TeK?U=Fy?>sgC(Zf&Hvxq^7+Mg*W%8}Xi%THfW|U(AtWiP*3uPDgBX_3ceq zhtJWME;UQJHmRJEf^pKbBDe=uGfEkecBAvj8R?(Kv@Oi@TPiZev^GmNGJg(xRI8&~ zi6ovW_Q@jszO&`B_h_w5=I2@Eln&!-i9 zI~#c8mPGSHjFrh5_NtdQ!bz4`*$jch9>a=*SEG=N&l9S47e*$`V)=;{3prAa=U71hKutgj5pz^WGn1MV@`@vEtA zF5Qw$uaEAo37}RhTTkcO!zMQuK-_%=Oddr!+QfAEds8f7hSJe3u_Fb@Uo+)wdUUH2 zTf;O)NadSh0Bsom019Gh^c5Xlg8A$Z=}ftW*ySf?Bag;TuJc&2WoBLpRI1)+NA0Y z&_LsrZomGzLH(u^5l0XMpmwLDBUrNoPqZ3Q3h>LlNh8e8(VDY$sU4V6U-zoMZ9VogVE4|2f32l7XTozTzvYrX|6-T$NaR1_=mXuwKp_Llr(GR zxU>b4$ieok)m_7Tj3x{6;bQ)_vBS zty<3~k#N@xK*3iVi2nd|{xlB7h_>C>UdE^IMlMqwjyHekTZLs^UR-&yD4IfrmSr1> z1xV(#bZsL3Db??0co7rjNQ$$Zlff7u4oz*?Sn0MBAch%MWL>Ie1du(uQDEjs#M3of z_-B#>0;yPufuDF1V+WI-c&(j3#8#KTyLT|$05>BI!za-9tu_%(0CZUZIRS=nYNgJL zVR3So4+u#kK!axrM{J4$=3((v&|OPyHMkC}LY&|c+o`Dk0Jp{Tg5Kp(75P#~Bj{@6 z-Wt>8xHGfd?>>$(`C_DxL(^?Gv4TmYUPC(*{HdZH*_nBFr}=_8E#oY_22uc~K`xNb zydXzXOCfIHE5YiMkVxQPHzdB-BKV3$yeY^*l~{qj7v>FZiE!>V7| zk|Eh2aU*Tdz7J}5B|)Pq3F5K3E|Ce4jif*G6@EQdA3R7EQXsh5&O!dQJ->%-7%-as z*y9W3t^hvy{{ZV$YvDWQP=3*CkN2DWDorezWwm2*42x?dQT&_+Bpm)U+kIBpf5*XM z+2drd{xpBWAi)RvdT-A^yEy)Je$T@eR>7KZZy4|caHH_`qTys!mr&Cri)+a$f;0ZP zhwGY1?c$m>HqUgqJxtC=h5Yd*JeEXQkGm=I# zBNkuQpZ%1oRpz^u@!4>I{#^dGq4N>}J*l#7jY%hVJ5#wlsDL?wFqj;FbakM@B#qwG z`7;&c(>~&!w?8R3EJvu|Q)9V#GSQ-BcE$|=go5T*gBKBmnFr2;pTv7pSwUQY!#yeO ziN+cx133)22R@>h#G)A_4Ixp;MdX3)>pU1tXtIyJ4j@#KBJ2^6nX(#D2j*`KmkSor+as$6kg*=Af?3sQqm|)WOGI-*rOBx&ny$1Q??IE zao}W}0Z2nI`A_4F&;(N3$0-HZIO$Fis2mJ-r!yjb_f`6uW86TUWaJ`)9j!??;9|T zcW>~iV!N}M!lNedLfAEev&$>GV?e0JT55l{NV-mvgu*hM9O}4d;-9pBoeFYrB?Df#ki^KMj zMYR-e$5Jy;rh^(}%Bd%&c&*?n39KzAlgyglIUoDXxiq<$-85!5hivC?WKf6usl`bL zhV3n`W11YRA;{-7ryZ;bF_|J&kc<`rrteFcl%R&RO6!6%?@Wr?ION_J=Jly)%`25; zEj86$aKj>u(V76HZJc09<%G_2xaOk0ib#T(iOKb-bqyYSnM=i}Ore`QSnvn6Shmwl z1zKC%tA+y~JN&2cs=1qu^eM7jNT3EF_r*bFr^;^G=3-7rL+C4aVvd5EN+S%A4L)IV zTXHdIyaULoKFtnu<*sSrfgBzwb5p5lE`tOSDab)blBKhbJt|6SFBfq=%sM76KKZ(RxgMyucIGku!kjw%_cezYTKqVDQuC?(wJD_BGB*U zlLbogoYst98#@rl&6Cu6S6&f)DRvJ^cO}N>DxMmTIdaFIm0CXzTs(@Qwg5=awRW2U z?M_t#r5wWcXB}~%-P->EtC!3lOjd2hrP?WXM+@tYE71xA)KgmCCykawQiO5{t5qLU z8DW*~k2Hm3NtRv2V>riBYimiime8A-?j9Zd1YZ9DTAJR*C7*T|JHAj)LE5gMo(q`g z*o*>{8}q>@sQ2Qjvna*iQr)$sqN;-p%N9Lk-NiQN!}@j1rc(n6G06^pmr8f`v&0xp zZ7VqGu!0ZMJk+sjwih2Pl7xJpl24SMaZq$C4z|tp=ld!1XN$HA@`&!-Z z78G?Xc+PrOb;glw=_}2uTjoxNNXN@tLG@Ntn`*Y@gVqfn@WjE?n2 zeN)X0tmO0Zu<8i!SIUfCXKN#}jz{tewDIpJAgCh)=}bd)E$5b(3el+E=WaG;wGGKkdu(p8jxZV9dk&1);;YGZ6BWh4U8m+& z!3+&^Z+O=U`^mBgYN~D(P6HFoQl`3?%M~X5)OK=Q+sSUg&lvVC!1~uIE~3jkq|P^P z<(2Xe(wNI7zzmk9_~nSeH9`n2VJFT>oq^;j08p1M!8!X?d1!SyHPT5w`KJVtgd4cc zZjw(yTwR1rW@$926ikCSmA7%zf$Lk+-(RJ;D{8GSISk}~O3rHR>Y*z_#+g5I_ z8}=anHOxM}tqkJ9Mmy~$t4ZP=Z_L4jC;K8go>N|PukxQfx`FklqAH%5_cg&>Yx>D- zJdGc)Tva#K^(Sn#)TI9azG$#ro|A-Z{G%h%m+p_309Tqer>M8yxtIR>G=osoFmZD? z{q|_EU7nM^Bx21ff`7a~#Z%O;p|`bZlI9^IkCZV4IsWfj^R}Mhj#@@1+}4hbpS!9f-0n2%xU~yNclL12eFPo-Q~RTpSDQ`8>y9lTN7!mz;- zM;Z(&D8Lk^;vp)=(k}Ms1tFv_l^MuJ%MRJ;PY|gX*tzxVQM@eU1@?~r04S{cwA3Py zNf{GFry!?a#85Lp!VYkDurz|YdB`#|$2?;OmB3-kbM7%fh;Vv}a&@Of ztjbaTWgm7xaB=TUHnXWM9p@c{vF9I^Fu5{}_RTe{<8|86j2^U^yiv-;{^KB3dx^C{ z{-z6a^>dm)*>)IK-Y}7-)$4&zthT(>m!+gf=;!eB7U<|yS3bZd?& zz}0J2x+)CPAsxat@7AQ6MTRD3ILECk517c+wRkOWgjWUQj~Ega$`jY9tm~_RG~2eE z0gUz{msP!q)yI`1D-4aG8s;qJk}3XKUL?sRG5K-ytrM|~4 zm=#s(PZ_A;)UCl_N|DA6aw;dv-K6wq?Wmi zGY#?%7jftCt`gGtPdX$K;fcmqB#P{#-C=H@4@jq>#59M!ll+HJv7ZU%Wn%~(+(jY~5Oj+m_VM*2vkwy6UD z0Hj;1Gj}IB_NyrBk@-mwMf6TFQ0g*I62{P4y}-^2pyV25wZxLCXSALYryFDd6Z9G8 zpFv*E#kmg1_ilB^Va|U#$8Qr`#sB*Y2Ozk{Eu= z5I$j<^URK_8NQ?eNYM$SHD75V|-zV&f&bnpEx(zL(fAs@LS8DvRugApu2J+WHLY^8`6k;WIJ6<^YuMYL0ejPi)dl0A6@6F?fa@Y>nJL~%Rk zsmGYcr7+t{ij8W82Ou&5imPgq!x&aoBxeWAEPq03AUHurqoUfd*LeZ_SOexn@zHJ+Xu z(t$~S-8%p06c&JN54$|v7Nf~kI+=^2gU;sAbpdBgPjz@jW>2H3_RNh5!7o0zpxPBxWSoLi^+XCh+ zvEVYY)`yuSU4CV%w^u0{Pu@iup3ffa{!|XkkX@!XM2)j-QQ6B$xnRNCIQ;6l~B1@bIa? zQTb+`BwU@Kap_o>FL~yqmY3FnGlofHKmNK^k5w*w+nYW8S8n9~bOEr;t^nPPpYLP! zsur50cXyW-!U-a18*3Q|FvT-UQa?3@8N!Xm zfYi3Q?MBhpBveZLQtl^_kUo@#Jg(q(F?Bz}af)k6vxbep9l*%_DW!_?#3(%pJ^qyM zGidqQ7wTvNk<c)t7rqEG>_l%|>75FiS&F}EqRK7`|+N`6QIg06ezh{aM(DgdT3lZ6}x^P8W3YIR8Aj7Kfgk`D$} z9R4^I4P3SumF#|pH6F;f7AQA*m8|*n*OWh#s@x!N;Q1VXEYt?aP=!GM0EB=kJ*0qs zoYDhXp@shdhX>e^QGJPWcFf><92`|8({*P6jeBzXi*f!m{{ZZbMWhV++{6yc2weWO zTpIEpaTGSpts|%hpP1sgtNWQZksRQW(`9Z?;h64Rz0K{!t?p=l4BD6kLTLjJyCZT( zx6+--<$aEB8|&yBa$K^G;{g7Ku1BnDdomU`1NfbJ{VPelD37-{&&Q-=ng@l#t``1G z9u81{jRhGKYKWH}bS|(veek^;`;YQ#OHbC8;lj+`TXh9b1X3Rc+m|Qpvtu9J8LL*B z1eTGA;<$m2_u3J-{b)fYp=^$5+w~Oeb4mq4pqTWf>FrBJ03gRv-kn7)0{|&;&sqg7 z03|8T4m#tCD5M85In@g?`DIv>!O1zO-Ys~d#CLJ%zt+0SGgZuI$Z)eksz%4mGASKS zPZeF-<+g%Y;8&nJtcX?AFg-!22A^yH06c|0U~p$4YPx|fCa98b(^B>_}E2h*m zw7!*DpK7S+2U>l$m1%8)ktt!^imqaL8I5PA-b)!oxL}~}B#sHIEv9M+3z@;d$r=9u z>sH_aMgRbNQ?j8+V}VPVkJ+xLF7`4^M*jdZX61U;_K{#T`#&hLEXQ%l&OL=#Ku7QZ zy@;s+q}|JeY&cV?nWfE1W5pS$yyeaZC)%8^xM!!apjRZ#CTY2&Gz@lV%{Meq1F0y@ zHJB*NFay+_Pyr-?7je(vGf<1TiNqig>gpL=a6Jt=q_NaeQs#gblu}VZ#|j&iVb>tiW{l7xm6eQfVZ5-u z*ung2@}hx&qw=FPW{Zg=fuf2)L&lAsIZ;u)qzn*wN&<8W02MB3wfEXhvHKxkG#-pI zkwP1>ld5S7)2xc|#~E&gMn0U?XwA5sM+Lkkfx(s3bMzIBtKVvZM!v3VDvB#8n<$Z`r2KQUA4L+y4FBy0&dWzQUn z!@0556~18Y^f{}RnsxM{MDiG;@9X$b(%#~X=H0nWHq${Yz+jWg`cvZ2r<4+iNbc3u zjXniGC^*JB=B&o68U#^*DLM8oG&`KJ&}Ab&UIsc>Lj)%0OIv%ePFHJZsmDWBvqoxM zn>B?P!(*GD!^K&)WT-q-?EE^9wn>`{*J!SXrv{3C(U7pvy~w`JACe*qg&5+p#)A>K zk(hj>j4UA^3{b-_-cPS*5kj7L$!apiZ1?VwSQgJ|7 zh2n}&^@ygRlTU$|6^#esS8ypfr*Sx@QvS?@oUl{%S{qM{aT=f<{#v_59L!viGNsY2 z8<&Mi^#ZD0-KLKiM4QV_%1I~b^sa)k^-U5DLHyEo$&3?>=AFdgmd0HEFNslnlrGrh zu;V_}Qco7j`T5;FfZ~$x!w!e{E%*~zvuM-A*kmW^Pf~R1bT*zUfbGnRtVRfa25U~& zS&s;A-YCoSkic=;xfpZ@lN=IzjMM)Bu*N@lR@{NfEyWfU`!lusexD@2>*FqX+z(oi z#~4l|LZ_iE(zs;MS=~%?t10i2Do4>SW9A!-W3Y2Yhs<;qa9s)MiTESoxjpXt2)bh#VA2a5Bp1&x@D6rIx^QJ&bGTK{2 zddSG+AHXm5q>|@PwcQvGAZ{2*DRKIm=S;f6Tr(j({#@tZHIqBtZXk`UI^bf0TW@2u zd&_%>>EfAl z({DefDd;7B=TdH?NcMf2+h_lUV=3e6PugjHEIs@9E^R7S` zJ^Pwk4JX{`E_ExbGLobDkuV`K#yz;HV(}A9fwz#k>0HRVx!U_e>|uuu%Adp5tH(5| z!Jvqi`xcOa<%TT|0t+vG}iHQdaaD7E)+xUWbZWqjzivIu_o}TCFSRZM+2ZDI% z?NG@qtod`zCdU}Qr=k74N~C)T)|O-}0GA1xDd=ca0B)TEPk z`(jD-ew5Qh$}+x(Qo7qg8%riOj=AQqL8#cuj>zoo7|S3#vEPqc;-%D@OLaqPPyj~Y zN2OM=)ov_U`AV$3e5^B{#*?vdPTHNy_=8WKHY`rcM<~&bo|vl^zuA&N-)4m)MLdLE zocjS>8|xDNr_YW72|TibNGH~tCb@Yg+!ahNK1IfT=@qHp!{tjMHn*U-{u9)FYS@-# z;zR_Fq;xgrR@$}AtFyuYW;tf=O=?+kx?p8vfvOej-&*4>*dlho6 z3ZU8)uH1eVVs@1k%xENa1gOXapGsJrW{4I81ahahwIF6AYFdTUD%(t(nT|r+82|&< zn$nuu=6Lp-*&<@2nA9#0T;is;w6}qelnRzP=bU>AvdFHuEkGEtM|O2KNiK}G>+>D! zf=)OC7$1-6R?K$>Qs#KX0AXWTfj;|~{{Tw1v6T5js)gVY=}^eG5kyQeZzG&;89$u^ z8EdCn+JVO-Oa1_N@FpYJHmF(;WxR%Ltu19bfHRUy}H58jw$ zf3n>El=H0Vua%Y2xyQ{JKhl6M{isHu%Oa@8cN6mdbp7F!umOjv;EK#$V@HZsXxbEg zfEMzPE9ugtORHT$OG9fc@sIgw9Bcsk9uMU}%VO$0a!O}_4M!%S1TeI+Oay8-GZV%s zV8X7{hVyCZ<_15hrx+o-jHB5z#zqRrpbyBJ0FFIF%~fcinbos~+l4=!278$S%nKLI zf#%36b+L>9pJatmMid-yJuyrDmukTTmhljruTzQw#&Sn=w~E(S5;5lq56FY+I+}gV z(a9TT-Ue97`Q|`WA7R|p-|Wjc_Y{dynB=xOCaXzeWr|c^AU-}tIVw-T{{UKnS(T2N zrOyhP?Csx1*XdQ~@aB~Joi{<81@ffeRQea$?yTN9l#7&dvadpf9)S8(w$~Ba zY7M7O-fEQ+K=L5mSD^%f$p^gwbDPvq={H*pHw9mmk{R5q56^>EY!0t0Ah(NHjxsW2 zk<<^sFz55G#5M=YG4!UFJcIh0MO?`K)*5R?ZGSD$k+lKF~c zMaJwNfShh0Ls9se(eHGIL>ZDc5=ZI?s`^#h>$*nCkMDpnzrIEf=}cE6jTGt=EzFH9 zkj9e7u;5BPao-2|R)bvalW-%-UGOdM>d-eT<`w7hsq|fPYu9^wnaA2P9yj};$Mz`j!dMnHb*{`i&~@6MU9--9(&s;m4^}nLHc#z z)Q@RwrE;4?9Bw@G^)-*7>apEo31lKq;6A@v+>o-4Q+l4FnLeZ@JDYgGSsp@LoB#kG z!mc9#gpx)&@lY8spf=)%zB*BEB}Z+?%!vKdjz`vj*qlufP{KivT1>>pB*sr-c&e9= zC9J-4Ics*nC=d{*>(-;1Exj!p-a^JmPw!b z_!y>6`l$gD*J1wp&b)nS9mSVTfVpUs%aC$14nM-9F`S%dJ1yi{40UzYmY4r_CADatskj@cROU$ zZZXr4q<=bwX)PUpd#9D#%8<$k`g2T0cr|PDCes`A=vVsF?saS1b=`AoENP7HF=P+K zWL8U~30OmMapkf5tiy`7BAt(o~*_7ckL zkfFU4168EcCPRd{lb`meu++TH#L|jsJkUP1nXvrPO{E5a9Yr)6P-qzJ(&m|(T+jok z%{GkC1G7eHxup~U>QPM;xugTA%{Mf;&rHw)=9u%v8)4i7jN-Fzb^9APmL_(2qu`)Z z*V8r5&*BLu3>8dn=O#|2euA14zeBH=P>vQ6re0QE!50}L*V?g$rFS*cG^!&D_f#+^ z9{!b$YpHo2a8V?(NsK#n`cVboR+3qF85wySe@bNPbr5QWh*ZG}aM`QE>||v+_XmpP zZ1sO8UB(6=;9~=dyLGI`g?ONH#PgAgVr`~$ZEyk-RfpE0GlJlUBz|>+{kqaxHv3j) z=ssb`zG^6Sd9Xjw(6%xDvm8(jvtIiB?Uh>$Bk!7g8r`}=o^Q%nXRlhpYwOF4hKZgy zA(!Rb_kxdH*7dG~V+3j>Pd$M9&QItlVHElrHn(wI#pXiUT7?E98oNBDy(bJ zFG_xD4F_h7({oFj1}~=o}~M? zu(8`g7{WxVJ%%abGzWzg1527k*$>SqG|bVOLm!%4(=$t&0DfrAGc;y^B;7`7nWHoS z{Lz|bXwSU>KQv~UnlnHd+U}T|ow}d-YZgl5zI#_AZ4yO#sdTIqHW&`LuKxgaR+iQY zWLJ2>WjU)(##4pe$tVtu|}OBVvOjj=a;cJnGkE`Kou9EV2lrI6YX3L#ReZ z7XbS8s_g1pD0x1$N1LgFYkv&aYsCZd~Kx0S+{Wl(s^ z9%(p^H8j-NNq4gVK2gy6)5b{11d73JddAoliaB%s(wtN=YvJY_r5j1$bfc6#sz*p0 zlkG(@Uvh(tXSFVBO|E9%>l0bX18J8HANTk<{Eb<%)$FceN0E{jZ{Tx+KDB3Ys#EE9 zHxo3oTdO;DIXu(Qhkk~blG|=Xk>l5OR+mz?M=HBm4)vWbgJSFZpzt45WBjWY=GOC1 zm7`K6PO8p;mJK_R+Bffsx3m zOH+4K6SFGIY}h3m2B=(V0%-({%z5XU+08VXcO#V*Ahp_qjCK`aU{IKEn>}g!u}Qj% zjUpj#7)ya`Kr~K+ADKGO|+T$&mA#MHt;wH zem$y?1$n0lah&w&OpEphK`eqW;WS-!T$AtD9<6{NO4mSgl*B-K1EjlKX&BN-DlMYK zhylXr7!87SDbgT0LK+05LrF>B$M5g+{(*aZxW^c9pZlEaT<5xUm_>~8u%-ozbjBcG zQC*7k&giWZ@i`7(mwh(cyu4DeJqJrfJXNz*Yjo_i_RNMan9c6$Wz}hPqYfnU(d`PA z6uP_;Y0Y=9jfHA`(rX6z(OJrLvcAd0RTfoDlf;QNWTO*H9}RdQ=|U&IZT#|v=>u7} zmpJX#3ujukB}Q(}D!!*LE%fYr8jyx38C^mFR3@*OCx4C)l^&NBjrU4@*E4K~jLlxO zrOahFZzKk0rhGwaX9Y!)Z7mWI1=MCl)^)Z|uFYV%gYU6gb|u_1KDMAXUE>z%!uy&pLA*lE2W!)Y z?(rr+?>ehwlL*jxmUjE{DfBd;X&LI{cpxKeef7RYUgGl>Z2O?Zy%|F`Zl@ZAAeHpsF|u2nJuC z78Cj?ap99GalfFw9u;CXnZHS0JA%H2c==kaftTQj7(I?A(+~bEMFD4`Pq;REy@V1q zUa8v0g)ihkoO~B@c}i@$JfAh-$GDc+JHB^~bQ&{u+VnvbV%y{=o(eXy2r)I1!OSMw zbjxOX0$dwMLf_&^Z#GqYd^mcRmS-IQknqA(Tf3y7>-&S*@}DHBJ0pKAf`l|u$Hz?v z8!2KNXvv@5Gc)O6OnQOyICHRfX;D7&r`#GW#iU`XD(1q=ob{wT4OWtu2dOIRQbJ-F z#HHz1t77~^x|wZ$6Z9>e!PYl_SM^dS_8r<%1er@*mo}#Q`jt^1%t1O&mYRFm6htf6 zCRLjoc?42~GuLu7!FwUxxpdAWqqyiHAvkgkR~g zfGTn6)r@YEY~7unrl;TQ{_LCS6P&13As5_aL=39x(oao(Ji*&L-#t?CwYNBvsALM! zx)R84OqH555BY*h`nE8wm|Dv=+s~emPS?aFC|gIi&zpL(Pv|OZ`~=mjv=Bje^|?Ku z@|$^+<+CM{SasUIAwSz|MaONw5Z8C@gPfH6q~6CyoF&UMbKft)cSCn7M+XBxV&AeK zyrkIi6FVP91eU}ud}X=%ME=p%Om{J$OfBg5Bz;GV!E{|)_g64OLZINjL0zSuN+$b! zjN7a*bIBqe`A1LqNZXLzySe~;AAOxLR?C%-G8yd(q@6BRa%?3%N_y%6=_xaB!5*2F z!saZ^pVbi>F7K+^@AQU97K`o`^V5-%e-X9WP}h`8l4?@%pARo4VopCPaew)`X9M9L zg~zPG`1bPQQb|wInVpBTaYjQ$G125yM2J@fexq70vquN|=Vt206J8!whL7x2EYVeN zD(j81CmD*p4-Q@u8K*IR(X7#XTgh5TBs{YTbiy+8{)Q0cki2|>ax`nI1GPEnwaKOub zTzRODDNCivL%I#$@w-k7wHJ;0^a^Pp&rIB;Q)guZ79Wy6X=W;${4SKg8j>OzwU~Hv z1|{oHbafeFvR;t1+I&>>Ry>SQTsP=!8a7vKWLW#ihvi2MvEy0Be8i@DgI@3!TQr|k zp|c}eM1u=$G40S*tB$s(%O%?Q0OU@YI(WzG=y>RgvbR=uGdtkrgn}&As5iz&TtxGA zWf3CeiU8?tBc81UdPpvF$HcE(`^+E$YLeUnF4;+Jzc@Kc=_fcKG{1A>ss60Fh0->= zP}Ul%&QZ3xO2_HAzxCOp+y^2jRbM&Y{ER8VYZhe$?1%_npIA@e<&5b+ZSd5at1o=2 zQW*P7QGwK@i})@V#*e_dwVvnxg_~5>-LmNxnYIViY_j2DJJu#93lie7*#Y8}*{ao2Ir>Js?vdEtex}ht3`yF^r}uRNDZTR5%qJ&S*DFCUGJ^EK z(RMwMArQGT`&E3frt|sKqR6?{(MSk+2d1W+Xxn=ndvado+qQG){^+9q#gkw}Ns?y`I=~()FolCyHRL_-Q55cxek#Or8I5?bF{`~V|788V zjQ;9V0F9s(U%GMMc~rG?j280&BA=K}t>Bz)wi?CD$Jy6C!h2~~U6c0UisGzo9twTz{mtq=`fd;LmKnbB* zHsfOPFP+=z{ zNa-ebriT)}l6pTO1_wSr-jaDnJLm}ZjwzQ`*~sXfu^2g}>cHM3@0R&pmW%d2BfQ(l z;P)FCE#vOVxrj2wz-m4iKs z_@kFLKx;?zi?(7wv^|%{yczb4r+M0uAJG3?2vS1Kz8~Slc1RFscyA%h#GZ=3X0&qK zMvti08)**M!A~4PIU|`#&RD@?zK1{8sgubN_f&m_1eA*A!VoU@#yTJ&R(shCs&S`^ zq?wBTm2{B~m>&^Fx4C7S&4^8tAGvDTbwbsr>=$GocvN@9E0vmvGj~_Uzs$AnsZIVt zAfmrywnNY4m*jV#RehIWIW-@*W3!xT8Czr-_#3qJo^^EjEA6$A{vcwX;yWRGV7!8E z9fhDY?SRA}qcP7CBuj~uG-OV<$C#NX$Acr#5Asij?CZ;sto_Gq>^)FF=CIzbi)fF= zruocX@WKP4U5h#Y27vG&92>YVY6GJ#@>=seSJ2jVa^Fj;M~;`BC4lX z{)OD)))(|>rogEYrViODQ@6TigMd_>61+D59uq*2kL4zHdAAmZ&CYsvepsO3b`o>X za@w#t#f0&8FV=fcQE=-)ABBnl8&{3b*W3DF-fskt#L?mdn@+UD8eEa@adpURN?V~n zV-|Yru#QNj7t{7S;)44ZUs!ug&}V~-=Xbt)taYp7a-`L|7K=J{sP}?0dj(YNo|`Jq;H?C3L`KGtk&=Psv(8 zcJQ`omfF@&{-Uh=g3*(p+lHo|tM@GlQRHBYB!_{MKxx!N(XUhN zUXJKrkmR@aU;Fj*=|-m13O8p0uxE%Ob>l6sJ{>>eb?;^9^kk9pg=I3c=eFhDU)5|d zgatCfx<|kD*Ucnv${P)#Y1t61dSemKBM4`^hjk)*P#>a?iqdRe*cDA{8QS3(`_Rxq z!5yOd$jIm=m4u)#O2@A`;}VOOjhbZK8sZiibN`@WHr^#X0} zkA(Tf|Doa>RGGLPo5&*D7ob3mU=<}Cuwg`dya!KmewW=ZBqdwv`dx~hWrpYZb1?<# zgneL{a@~*rzzFJoMa=@dx4OBa%pY>9I%m8$0ULlwFCqR#F8KANdpe{yI)y#klxjYqE8VYbD`L%I`5g`ki{IpPVdh?s ztGyB-$(Qr|*q}#F|uz-Is5h(M|JR<}_ zoqK$kW~SBSR}!_E%th(vRYJZm2%Txc%w8-Zy$vywwsJ``*NB;HyEEvcnmERqBw!}U zjYqr2oO;0q{|cpQ&y|5-j7V5}R6J}q`<=4F2&Gkrs^F1L-s0n5Ns#h7 z4JnTd=9pRZV-y<<5$m2uBRkr!r_s*SX0I8nj|)+Ztq_hcos|M8Cki=v6~?SSl)7ip z{4m)Ryn<*jq&IIsB%d-ZlGCCo|J|$hflBcr0xRFQ&PHYERZLhaBJovb zK>B=%0VtIY>t(qW_4P=UeE7;t$mO)t)33PCSYDJed{i65@^MU(46uJ>#~YKZ`Iw&7H7A)W5hv^>`Zw2JyXu&etQs$BBUB)2?CWgoqM?GNl3B5VgS^|jGb_5 zeEj=IIz2*UPqR4oeu>SxWPz7OCqR@Sal9~P2?>|dePl8uZd}_o<;Ay{_>@OqWz$Eu z71uxbw(7YKcH7yl8|w)Ik{THAxzkRIg)UMPTV}ueQkkBbY8!6RX0<#rNBxpDoUJ70 zK6Q;0tn9d|T+^l7zPNyrUCD#&?9RHo=cTCTN^x@?t165Ip%E?1ccu^*SJAtyn+{%u zm&VrUNt3`9#w&2*8sLt$jZsD*&E z_Hh12-YGO3@K!MioK(`i`JpMiR;By{UZvTB62-|_LJg0~N86BO9N)dyho_vz7TAhMNJD@MK&izZUZ@6FbrP0;U+lIpJ(V(p7*^~RdK{sgI~a{?ryNM=QR#>r9>%opGy3d@>odO8mA4lknCsIw8#>x-imN}1 z)zY}+B!}&*b!lwTXc_92Ra>2cDAZ*$9p2yXf8UskcR4BcWLP>bLHd)pHMSC3L5PR9 zty?B)QR!r4No9O!L9ttBFpe<#-OrSc>2qX>PLt>xo=%2AE#s8fpF1?0PYT7@H-HXs z1iR15IVs)0klMW}DI&ZsqocP+@u)tVDU_k`b{bt|+m_Qk)TIe(WA@ZCRi%t(s`KSc z`^|RryLa;jLv%3_6EU6Fl;ygLu*ixTU~8OCyE8rA!8BcM^%pcDWVl&flXd5*N>1%# za2D&-KH2N$vFiQodVKW9^m9J6wr@vqDSvl?XlVm0r|w>0#GlD~_AXrq+?z54CQVb! zDszunDYOeSzND2Y8TIH1Azl(<_f@#uWK+OrK4lF`!eI<0@w6a5L?A_Q>*_o5-CkSp{JGHy5}`f%*j=no_B(}SFtw?8Gjm!)H-#jrT&_w4$4x^4GnVQX4nBFh z_hmFd^kz;EU>6bmLikj=9DjhYKJ{4BJEtxmb#uZSk4@T`<%qrG#leI}>ULJqY%_Od z|E>U;8%uAu_X7W|)$5E_2bri0ZTc6VqWCgG*9#n)zt=7W8|&vB8VH+?x*zi?d^A|O zO(s#mn5(A!aKK;7b&h@&q$XiY(QT={WVOgk$Xou+kHaDB?(W;o({kP24>ingG32T? zL>D&M`E{Bn#|0YwCOFdtp6sO?ie7ug2_&{uYV+MF-gq(Y!e_UpJj;myEv?~}{T*or zZ@5e-tRFjBF0g)o!1rroU1Eagd4%I~wC`1BNF~>rW?dIXCj;1!?{$h+d#4} zSv2zNLA38nMicJfqpLq0Q$#R5ojxn(#~+Mcq*YD4VCKO>6v0PhVe@#v(Z-3>RdZtoRnLzqXT)K&aw$Fi*B z@R>}HG6$w@OZZNo23fMxJ@vf!Ro=*!`lqS$Trl>gpFy=2vyHL8o5(BuiWYq-Iq0+v zt-n@e?30NRRHRWKQt#cG7RbAdn9PxREYL?*o@M7nI!jB@mJ3WX-uLej%)8XJ(oCty zTND@~3#v66HxCdYWLc9bt5AJA!t;Z@gif5f4#(sEaAe2mmBCR#t<^2nQz6}>8rqG_ z2`K^E_>c13bT2@Ro3bktW}@!tUb6YQ$LqS`>OmfeeBI1sogm+hP|n%LKPr@wa<`;c zj+CQzo^q*VEd2Cf ztt`~PB>(rl3^Cljb8fTpEW(9`LouzuO_aWJfmyL1EKb-IcD7CwnFEyNR$;mWzhXp~n z#w88YPRgHJ&C}J)W<+SG<=72%m8kXc7R*o-m>F$`pcRzam(k0=#^j(dnv<)HI|+5dAp;hM()2#K+iGY)2^deMeQx2k3!% z+=#S!n+4Pg?5P(SJRicd?br~#5-(_n6ow?2$lYC_@no)7a}bCdQnAWbh}2t_({In* zJx`8aTzLu7%VaKig!Ba)zkg4i0S$d{=W;6Tq){R1+o72$W>~;iFAv{XOgFo5ZC9;9 z2lp=ijix4AL~U;(N#HVB+r%ABhva6PAZuebX#KH7mhf%^6Y%)Z}cMDH8+#};be$H+*_~}_2vX< z$OF2y&GMQM=50*>T%UL)MID1WFx zRuejc5S^$a%rG8xm?B*+K@Vj*ZbB2_WEBn44;S=MZpXtb#uf;ZTTG~f0|~tJ5G1c| z>v7yaZff!amaWli?smyMd|Ym4bLOSEd5zU?7f_K7GX2A9&jALX%qZo6Z?j{-=`dIN zsi^rd-k;Dyk-+=Qk<=uq8kr}QZ?!88g>#CrXotSvV z{7A+|G?Ldn30uw^$ zeW|`Nd%qlA@h=#$lNg1im9^gZ7kDsnQw_v6pE9okNaytq)3VtUz?$Rf{B2Gg3yO({ z?+WV%HnY;)AU)D#wl4ABQlyie#DRo#jJJNjn3TA@rF;{w0Dm8YC6t{aQ=~i3z1x_$ zdogBmX&_Mu&`Px1E_K^3$p`ZmPYQx?ITiw~biV+Wy3{*Q^l>ehGRIrzk4gYni%|BZ zAg4v$gF}>(6Rze`XWSHpi(e1jYADCzt+#61d6bWtwZ zp(r8evm@&XG{ChNphLmbo(nT(=1Cl?n5Fhs`r~waG9KNr)hnv^r*vSjxUPbuq2q4k;KC#t>!% zeO3SJ!X|R^(Z``TN7^M7UgbAaYjb6birkN0Iq(X{dM@bnFpVq@Cg$*)+z?S&+BP;J z=r;(Ai890f7_*VTJJ?@UH?Oy?|Lk}B=$=`Qnd+mo4mm!rs=g9{FFTv@NfFaWnWRe5 zNd_NrexaV>L19BE?E#Vr>20bhNqrxFNV*@cx>{k-yh?N zAM2&)uZBdo%*?0_ZUi)<->NUhDz=YyOeYqSXcvUI8y3p#v|KNxnF!ph-I>Mn7mpvWt zV|D3;+il4e34E|`t5xKLwU|c>sK=d2WE6XZ{!(r#v$1z?%So*Ie6AespJvVy*K#_T zW4moJHImNJmO(EZHFgdtuX{@)qFM-((a=>2$Mf|#uM%w#gxkxn{(@dj$8(>3EoFlB7xEZHOa@|U$BPOyioY$lWeIKDjBV>mdPGhi72#?rDd%P_toSya&HO=n8+m~UW?LKV6$951y z(*`OQ8@8<)k)4cFrWI5&48=ZpWraq`U7TQs6{v5%wDoFRdF$!m#Y_8Qz!+#IUaTY2 zHd}(^DXFp^5hY;5{siin;|%iQ7YRgGa6fo#iv}fqh<^8F>=v5sw4Ku#$^jhtu@Q80 zeRG+WiAi_Pa_@>LvPb}oO~7|OSr5mlfYKA*iA?^I!kP<0Yhd-&_3vMg=U|kc&tT$d@(2(@PV^Hp0Zpy{g(6zBKVKp_)rIU=%)7-l- zdSM=!+nK`SdpKSe<1Km1>YqcK1$Lg;l4eq&!R^Vy|4!_5AvN_oh zdJQk-T?38mG*~3mLQt9on8JB}tnAp=066!ByrVC2V`wQUs_Xi!y%q*|v+<{E+uRcz z3{o@{M(z}B4=Z6mmV<48_K(*Opc8Nzr-UUd1do)~9(VBmnIL@wZUg!}GZ`N=n!tYX zl}|?hf+Wgh-DTiK={WmnKHd*zp8Fai)N2tZW!cuJk5D0Q+LQX#Ri*+W=W(p4Wl@vH z2HEgfH17I_hYUozBj|3)6XZ!oRPi?a{ZRIDC0F`k-*nzcpq@k8LwDw!N`~M2e!90NmBw(~jw7GmzxTv`~ z7mY6kxF;p+;BLgC-4ISVs{ibT|D9v$g~K2zs6KED!JQA-V{43~@gM8%D{@eh{xcso z11TWe?{ODmatTIdAR>S*B1i!7@ABUZPB2 z7i3ZS$*L^(oeHm3I&${CjhFu}yhB$NO(4jmgn%frP|{mqb>SpH7ZT!xeT%K|{xR!0u(ED28hB(*YbnPb zZvKUe@f`?oAdLbt*YQ;7d{JGC5r_%~!S^)AwA8jtQmenSv>RPgVlY9bQ^m{C=C7=I zSDB}CzE7ufya;o6mtZ^-alPfMsW+$o>P4Gcl*i7O;=rG|&FIB*MK;J`#B`y07u8r( z96aIWuoP<0XHNZ@>D;@6`B9vH0xdPef$PePB(Si&AUhBFbF=le-vpf_SgI8yjWlth zX#HI9CG}Mwpmq=`m2Y9%^4%JfL=#8Rk5@&;({1eg{f?f`Uy$+0YKYlS_p1HJ9I7(| zpM?ok@jOn}u8(yrmdjGiA8pg*=yDGT!8q{Zc^yAEVj#auUd(+cV)4ds7@epUfg^ry9#Qr6jq|S{;xdtwc|!*xYPv@?5*S_OboiHvJ9=5q%JruL+2u z_&Gr5?yg2Vb;&TkP!Us;sQ}s2CM=&@etuJJKJ3)}u!}4w%i)>xhBecquk}8X@F!QG z;NC04a@DE>j6rNXYsU{Htq!lrvZ|xn(=J7ZwyW#Y=>t{Mv=MeS;|G5m=>e^8{z3wy@%$pS=Wbg;U_+eAidW)gOex z&yc+GQfYIC1EjzEm0mZ1dCA=-Et9r;GJmPGCIlyMQdHQL4s1uyw_sg2gT^0{Fy|?Q zQ+N>7bG@b>SlcMKfHTUA_h-Hm7YG$W%9&)`1lmiWU?>(i0Is)i6&ho~ z{p07x+^T2Nla?*xqxB#ad+RCCHMeP#Gpx?FJuq>|%A*8w;g43{hiV)O zBLFocigyvlrlPW+c$!PX3w8er($5qSreT{a4W?{nE1)=m;@I*K`mV-?i@W4#OfuWr z;;+9t)*O+-gHwBkW&T2l49g=Gyl{#?9KHpsfhPG!odMac#ocI)tI1S#^b-STlFY}@ z*Bxt&0Get1G~@TS(($&gkiAWDI7DPe6UWPSBkhyh)$-#SQEAG->g~ z?-C>;xDGf+1#{H!ZmF1qw!Qn*K&XJz;;24v!}DlEBM0I#RT)BVlGIc+zx0#hzT%n) z-_v7QGtgHq+YEFYvU9mwnyipQ9gcqdp;nyz7nB857Z}lEmJ_ZWCz21feeA@D1hO~i zd^cL4R*8aU+h!kLmsDGPW{Q^}%w&3%h#s~;eWOpNJQ&W=0$yfu(&tfu0J(>3QU0+| z=f`!(6N+o`%ir)S6*KfO@??1F&H@xQ( zzQ~l2pe{a7X8RZ~k3!uq$vZ=vZT$iJq2jx6rVzC-EoCm~j~q2n#!#`_@Kc}&1gVg5 z4=bZZ-~)1$z}u22iSSf!2?b{(UKv=&Ft zLInQ6aS6qK4v^ANOJV=6fh#n~cSl5%eH8`xkvkRH0SARFFrAn_T z&x=q>hvaezL5aqdSesPeuz8lt|cHdCn!7OnLYU4ZGc>vprAnzAl$EkBaNJswzBA zRE*YlO`axj5!8=3R6X+=oq2pz{xkW$fvfn6O=bEMofDE2_E&tl9~ zWebSh&L;>r_JuHuOJbyYmC(9ipUaA_ovhW40`gl~UCRk+KQVkM$M-#yk~73lfL=2dN~p^riskV)1@o|Ftq<(~our9u>AG zQRjKx)&@5QeDw19e)Z539Of9&-tN=HvrnzLV2L2lzKug&E{&q+f{zd%gq?aWSco&emnOE3x#4 zJGDqAZSalrx8IDB?)|1@rg9z7hcC8Xe9m8eEIFGhm#q|R{l|c$e13uaq%c->ta0($ z3s&jvxu6nS;=qD@GZkaP?6=ESOd?Kqdu z`wiLT+!sI0!u$i@bT)8r&ZKXdl$Lm|g6E+U!~?+%>fwR{c^Ijd6A}7ZT`&}pRf3sUXaNbH7vZThe*S@|MYna zRs61QUYW-7H3a`1L*@esHc_QvffNy50v=hzUhh*tGi{XrMCicxjewUt!08H=l&)`6 zXX{OL%yn_xg8bf*us|x;d(u47lkOfs1{`=^OqH9TM+W9-_!hmHFlX{Ri$;Y)&@kW=# zB}s8AX-0a9RV105+wMz3h@;&bCPR2_7F}6BFofUMc)MqHjq@9 zQpgCT`MfBJ2m=C$f#Fls06Reg4ax|)GROeDam->o_NdX%nff+0o?`y&Y9LGkZ*j}M z`9vK}-Fj~|W0nkTeYHzzbMMAB@0g`w6UX0yQ=VF&=Flcc?@JxlTPvVH*%}7B3AB|s z9ER3|)HiG+@qR%kSYbELG0?U#yl?ax6T%_=`Dx?}L5L@>Hque5*^@Y&LEAuTFpQrF zk3waiWKPHd(_2h`bQq)q^}Usx?(YGGg+tKRupk(%F@f-glL4UEMl{IRX;cOQks8Ab zD}XL{riPKAA^(sw;Li~#B|v5(s%UPU4j`~o-tk4i=+%JG-NX4W_~Rx;+fc`2fI0(+ z?@sp}Qj?+;{*@na5=A;7q65VUG50VfNLP_xkXRvH&HK_dmzU}MI#wxn-w0cr^@8qF|pw~ zh;?NOqk4pxr;NK8H6!wdHZLDljV~`HsE5NH=*rKj`6>Cv*MFnJc?Ca}(&0xd@~ajo zFSZ!zWw-Y{qzQZfl;F=#m*;`-uobIm8O>x69z6n9 zN`n5KtVu7W*IVLlx2OKm^3C{xV>E1I#i^?6CS!*y$N>&=*%|1K>FJap=?=s3%jZQ16%hLwBX9kVwdQq z@so$@QflycKXJJ*UFoXpem{zZQ;cWfsHOu^jqGfhafAHEgM8!o1&8z~xVf#^q7TAdckfN1!Y+st ztBUY`vQ*#x=tV5geWbv(vzPc$3#4Rrmb6K1-979^N=yo*^lm#_0vzE-^XL5%TOCJ5 z)pW!)`Xgk*5K;}~itZ6m+tcRaoC1~*+EA6dAJ>>32T9p#3 z+@q$^!B+SvNI#wJwVTe&8DrlX^PCH}6OI-VT zrPVh!mA$cP-Eo@|s06Y8(@ z)#}T@r{H*vDA%*v-<}%s-+l+GgG=z(7(wMG6M`gc zy6gb9eBBF?9`bRyh2l8%!`d(PN#X&aBST^D<>R8mfuCPQ;M7CWk`u4jWrPeH!?rJU z>9l#z@%P00OH(#On!|qykn)H?R4C@$vgkJ zU>G{7njRU-@fD>(guY|GSg)H2$$F@9VujruQWw#1d~y3lKi< z8t?6N_^~*O`Z9b&)chREh*#tyYzBa;nMLSG{+Xw%3zpIDB~iQe!n0wC#iY+2=qiqc zuAgqUv=Z|(^iH&ei+!{u@y85OpJw+*)vO)Zy+?H;u%R;BIax zNdDTECfcRDeOvSS_c?}G3hl?yBT^EjSDW^_whT3=oto*&SB1mM_WC&4{a2Maao_{l zI$o$FcaL_-YL1Wqdh6wyW|(JP6K>)jReC`Gu=j3=AKl=>;N8EV7qF3HONZ0oljyaZ z!hbk~%N9YKdWW3WOf!*F|6%lIZ?ms>x8qLT@2Z8!F2%dDP0)+Tz1cQJPTri`xwhHH zm-b)UYjb`Tg}iwFQeh)7jDi)8Xm zxN}aW`)#BqKFQSye#>lAZ! z-wo@nj}M>QG#A~JYk5W-U8OvVBPy8@jd^E6^PasgSgy3U+!-rEi;4F$1W^DkOtmwI zIL|Z!fo4M6yu-GD%&bcO>dzxlj>*bYM)@JFK&tf{=laZWqvX)QO>}977i{|Du91rw zzR}I~>+JL%shHHBAst0=ZnmE+Hxh+&LEJb^1+G#=)rEaB_i*`)97?5QxZgh~HcsGG zm!3B4Nj_Q-S!dde$PQ0b!DJfl^23I^YuCAcmE7{wxZ9G#X&9C#PpI5|Kr<)A(Nu6l zCJ<&=xdl)~QqrO%Edc}?_BWfghdF5KmT3o2Ud%$tX!O3Vliz=(uR`ub)S2o z-X_i*DL#*QYCGH=Mkx81v(DD;y>IT!=V(F5s`bp;u8JS^+{nms+7FxMC2N$?>kjfo zSe(HNp#OLu+9mdibc}|-F87>mTf^}$$Z1ONwh&xQ+3PMBU+xfQ@ySjG8A2Rai1+cR zY@e&#aSQQQ;bt$GuISNMUl~b#lC_c>=jMbnSQZyL(B~QwCV|@igQcNcpCq{1#zX-K zh)`k>f;5HfFl0JB==UM~!9`SqlfWyK&N}j(Vzoi6G4snwUSclN+{pEGNO#nT~!Qfb505}OZ33)#d{((3RtOepQuw-xS9w&ku zXf{I!&2T$6VlU|dIK#vjX!QK=)8m@Mlt3hh+D&*X;#kk-X8-~H-|Mt*0l^!{zJ&kozwZX728b~f*#YkZtQ1fLj2eOf=t?ZvCX_n% zpX46y#y`{_E&z-d`T#8-ps^F^zNQG0O{TZ!&Lu)Z5aCHmfaPFjkst)>SuboVvw9tD_JQ53=$m50%_<0*w9xZFh#jm!KY;#OsNdYgC0@3tCEkZE zPWwJ9P*ku}Qsx_OVn`Abm31zNqin5XwRWk;oHnfP$Isrt)Wpm^+6Ws-Uk2(do5WT| zEalg)Y}qGnblZAW)2u2Txp&^JmwW{fyhaeC!6Eb1oWak)kVv?e7ra-7yUB!{2&8Ar zv<+w5LEL#1%7BJwaza9Nv=k|S>Mcs-DmY>0N1cr{3-P|sh2DQX9PX(!fqwph?knc< zBp07#_bbL=9hf?Ec9&_<3AB9_%JKMU#Y5&!;cV~bLhd=9DBVVUN$y?ZUdo&$pWVjj zcxk;u*Ni7j%_9rHbIjyn^dQN#V#=7)X1 zb^V;}@J@dqdlR1IbfSSxA{VgJY1V+0% zzmD{7rbC9b3281k;Rq=*>V*R&fL$HjK%4l^2;^jwjX3@-B`7p;BHAOdPuV(0L9>7o zhii`O5SqS!%na$=2)Wqjx;b%X)E&j(*u9*O_kuiGEnq+IC|pGT1*MpdsJEd-blv;l z2$H}tVav`BeoZBQj(NiZYs(ip`14-|3b-Z>%=_FBB>&J)PokPjwDdCIbxClWytp|R z%6HK>dHXWk3h2{*Z1d}3^+XE6sr|UhgI`W3hTJHNnzIA3g)%eb6%}10_2-1W;pPX8#i%aRY8eJM0KM;Y>PYLKNkiyjwME*!K>u4LM+pR!OGxg&kl;fJM267n=6n>i4%qUAjA-ur zz(Wr=5W%=9`%=ZfOiPxYjEb6`QtHX~`fsCDKP8)Up;yKGmx8V2Xs^psh6jA+qQVyZ z%8rNZ6Z*UG&q`w4B)HJStyvV|3P-cMK$GICt8_1jNBYtS!-kt^^Dy(QaG(&wg%mHs zeQTwPzd4=ovaomql?Wm2wHUQ4_4pg(*-`tXv>P)V6V&#?&!&su9f% z(@;nI#So&<#t@PMYBN?gRyt+rl$uSrpg+se%)(^Mv_;g>mhXG9=KK5m1BDCsz4yX< z&Uwyro^#)|5{1mM$?dr#Gnz4YEg<$v=FNrSd-*{==#Wt$? ztAb;Q=dddP9=18z)2A|H*_4?QnM5`JOlDmR2P!@|@y_RRzco}b*2k0;L# z^0d%PlNe_EhUcM(TH?GEUe>|$3L!U8^uwluCSQe6Wjz=m_$%i9s4Jx|{9gH>?;!`8 zE-zzwvJwbTg8zbSp765H&LA*0dG4On^oshj;zP?PGJ>N=DW{*!DkA#Mg(8nl-{ibe4$jK0R|fqvoVlnEnj6zI=ySuP+0U zj=LuV)&_g5i-h1VR65(O`WaAB10Wp&d5{O#u8V`8?HR;D5W276dRjnVlFsabclex4 zW!fmlffZR1l71G&f)WK)gl9JgOSj)z^%1(p_<1AYrNP&!X$I-bQV8_cg|2c+( z`!-Q_@96{X26_SVx8)bW4}8k%KDzAD;ZHq98k75Hr^&Ws4J&% zbYA9j**;@=NW88Z<*Td504Em>rh+cFq?qZ>PzOqY%gK@259l4+KYIF0%cjlgIp^I@ zyeS2ZSr@S~u@sz$bRp<=<(iFKtTEDJ>?eXAGuezo^EhA9X@VfTyJF-@~6dp z{>85YgSL0e#?Cc(_$B@~@9g^_B$JUOX^_zMiV}E!+rhGBmIUwa`#eacf%#4%{9Frw?jghMEE@Phu>u%F;#9|_xZ3k zss8k&a&_vC-JLrhYp3ZWZ1Gz3k?5usB?-N@X~%yDiz7zclp5&$7C!ReW;&|HmVG;$ z_W>W?PBr{T(-JDt_tjj)n`H=si`0K=T*mfah21aO?r@RS@6{RmjZ6(;wR2Ui9uwVE zIMS4(((_swp=6h6%+3v)XD$>@3BY zkG4+TO!}hieo=Zp4ziZ!`-BoaXF!n`+MFzX#sQG4W_U5$oV(w-1yv7i_(I?x6{?{a z57)^SYw*lC5a-|q&_${A(&OnQpMR))Hs@!a$rF*gE>TC z?*aE00}vQ!9~Web*(oZeb3Z`J%K>Xh3J=3puwW5N*x4{#*uHzt4N}v!-&ia?W0WB% zXBTCs*{J@y`gi12_uIIoFTNt)NjDFBAK20Sh%lY>cTqrpPgZ>OT>Xc@JAWFL-a1!v zbmoxjuJ_57g!(xHR})9~1iCT$_dwgjzjM>-`1jyu*;#c+uh137wt9QnoUJD+1HN}G zyHYrK=IYw7roPYF-PT-AHOE_l~d!Bqf zv^DYMe^FWRLh;4lGBVb^?3eYmD$fiCe{sd;O6tdkU%M`ZS;d7VxF}ea8DBL^E$l?$ zm9YR2Z?Ou{?jK#|J#+!{Q%DC8AL+|E=&?+M0DUhI&;Wz}QiDB# z+Yk^K@XtBIztYiVqyYc|i1?ry3dV$hms#f00TTH990YG2m7@-J1&3KqICMt?cwkTO zauqb`^O=FZG2qq!mH=2C;DKD=27q$|5C))n-QNlh2>JlWG@AGTp$-lBg6WU~AxMmZ z|G4uuACOT=r(Xk%63~d~p(QJwj&`5y;J*#tvB`DMGy=2utwAKKj`my@K#kqft>=S@ z3j40`R+h;Zp43v3?k(##xW_ z7kVHDC*4}V*8Ujcvdki2V{x07a7U8cO`gQTBW42s^HE=reaUU4KV`eA>>5C*#&;o? z#IbQR*oBuvfBUL@_S+X#{A9vk%nQ=HzJO#d_Gffz6@98gBT|i?Ylavd6X7Cx&x&Ti z!D2oB0iztBsqnpNW_a|sa)|tBm0n8od%TYTaCaf(h&7-pZRH}t0sDDHG0Kv#!IF@n za#v9B@$e)8sfA~-16`QJ#-LkmQAb4jejF02Ns71@>>x>Qo$6Ha9+Q@OVcjf-nb$)M zQ}WM3wcpp$B?d$yzM^vcwAKC$_`HmqL#qB8AJiEI$T~OG>GF>ldwp*-x%P!`FXa20 zBzx5=@02^Xl3`N4sR0g!+24X;z(!BMRytKtTs65Cs^T>!Z~?5~MvZbHzdoSq zLMMbesx;RfPP#=#iJ+Tdvd5DV)nA6hYescm0AUqK^N9eF9hiwK`V+_nmrBru9Ew{* zn@pOaVy_R*&$KN4y2gLDY2rq*%j|BKT}GP=Z(z&kv=>cRm{|Bs@9eJ2X;_RRq=gQ} zGjQf3VY?1(x@2cKQA^;IO+?n=7`c$1!#SoZGEI$U;^!7mP*_VJl8xuIHe6J_u#+-4~#-*JGv7D_5yNi4k$nY z$b1k3y##lw)L9e&_`o{h%aFbuAmyY&<83;v0w^?bd%eKT&j|*ZuAtiVN%GbRt`iiQ zbY!T{@=jnlomMUur*oOf7$6Hb7U5*lcooPEAYTx)Gbq0Tc3^qHWls?73i(!m0WvW~ zAYT9yyHDbWIKZA+ zGk*Lem~^$hPJ&Z_={pDImMGHX7&@tZj`MlT1mB8!?xKLT7Z)xfWm*53R)PJQn?PVT|#(P}Az05CT0ZyZ$3!AYUH2@Vcp zf^!jcDK{KoP7MlCkG|P*5Nm;g7n=UfQn8usj>JIPPCp){9dcvs^~_JfO>+wfpw{OK zih7nJFbt7|`TIU9=8IGGr*uOF1J0&1DND9Q;7Tu?B09u`rJ+#)|A1fEhlcjAR8^G8 z0S12amw*G(vSH$RY79TOdbZtduecZfHjksfkslIyF}c$&*;$MzrUh$MUgnxh7 zJfV4!LL@2#0HEW$Ua+FBixbUX;2hI}00<@$+gz3`DTb09^* zO{;$90s{?30i(f91#i%2n`HUh8n&r1apQ>g)l{&GD}<72M39>txsvEOA-D=*l8VK% z+X`P%3iVS*HobGVUG*z}6yHQM@K^`*wif-c8DY5;Tc#3_b|bP|Vq?!k?A{ zh;Cwk-V3N~05U9*%aorao}YuqzjhKG4U#nQE5boRHXaCTaFATl2U;J%EWfK85QuGb zMOt0F(NpT?`%fUyg%S(^G6v8y!quj#urwtD)L!*4peUQfU5ZWuMd4sqyIPq%};z`(^F$3=0z8R4^KgJ(oF$yDSO5Z3uv-Xk|NA25E`TrxPv(wwt%ve;qJ!V&tG5sFz3XJ?f4>8re}%h=La- zw~ecKkFaHXwUD?kln7Q$N#X)`^?wf;CIKk-jMroeJ%NQ(_(t@)P4GPVQ!> zQIu}hBq^jpICEf&Op*6vJi6Z+xRm=r4ksMt$dC3ybM9d&5`}s)czPpexnZDZg1T%*VffwCnscal7Fb-+>Jz196Y^7b@*2VaC zUtY(F=S|o$xs|bLl^iqAW4YFhj_ZNWC!*${(qj$T~#VoVt}!TLiADoP>|>isB}7T@F^CgbdIc5JapJjX{s zAs)zHkgmf^=cgkMObF54{FffY`8kj>^Yrld zh)PEn2f4|MdI_LwNmcycIX{R{sUE8wCY2avdk#d>mrY6WyVt__7gu>utfS9BK@@q4LwiWtzGtuqb&uZ^tGEF+jJ6uoi>VR=W2vb z8Qg?RnC=>nwzE=-8i}J#H}+kc&Vi%(1B)1oD8LS<}5Mb@!a^+m9aY!4?zP5 zo%m6RM_kLtb+#DLd@sK+3tEsuVs5kvDA{coH5p-5vPh0*O;Sk3q}{uhc0VM=6v9Nm zuf7%G3L&|#J#nwvt=lm`vb!+~EoiwZ9iHFM)hay`oJpNmZ9umGHc?&E;#ehQgP@uU zZ4u9c_yl4U69hS>-~O%}=^y<1DQnn9L^Yjfxi_JX1}QqE>|N4ISijvz!TVZ*u-pbVD0f&<;7N`w7yM z_mFWa_fy?{;tc9A(2%NYBDc^^ly*(_zKM_mZ7O{e)}h9_nq+^%2u^C4ELFv<&_PHL4>3@qFp8qLUiakREjkI)r?+%lL^|jttJ0xFU5fW9?t_jD z9Ed^yD{W$u1AT+`eVEMY#ESbd0h$xAd=;*1ILdQ%3%sQz%G%%K#q(&U{XzK^HqG)V! ztA92DdaSukxb=NSKs+@4m$2y1_x>58t*!T*od*jo3=NSyKQuEk(05F{6t_Lj#GW{( z80YARm+VScOQ#}t!ac3l4QY7p4T6eYJZ;IaxIt6D%J{VB?uz>`In(yidS1T!m9ubc zIx1$c+mPGr#S+$4m6najwJcsBi^|Jo#o_`U=}1 z+lJwmLZ|$HfX_lJ@VPGBboZ3=yAzEBF4YdXp_pE)>W$t+!k(SZ!-CE|s>9Qxvc$#X zc7C=g43yi5QTEDGrgD?pRs6f#zzRmx*VPoWBrIg+Gk7K>H?ZDM@7=pzX6ppoGI_Fp zYLstb7EGLpV-E5?B5BCAVRsX->RAP}d!QIm+=GeRbR>#dUP5@eFXcg+r!=o0rO&oM zyN}JBN%6cuaquzwP$#Qk4qsOEB0B5*HJG1|2j)4@D1PPTk%8GRN9XF;-C_aF5GrgX zr}$<``4Mn<^7w0<8#0af?DRkuh~5(RJKLxVYUk51U)A`*PB{c&pJ#Li(pFC#D4aXr zzz!nKc+rB8D*>6lGGOyp*JfMi&O7gqw|Wy$R0O}c?a0t14f|uKzgR%AK=R=7V`n<0 zWcQ6{|4hW@nduSEJx*s%O@^Ktc^ZLO`O_)KhN*rs-r8HdB4`2AaPIa-AKpON^KPmV zBBbYZ`#uw@YgpdQ!gf=hk%l-?Xaws-ke5{H8HMdmG2HTY+>S@oec~~*WEj!Lx~Ii@ z;e07XBof37__*1SyvIe28TASwDj~c-Q>=&ochT4^#Zh zG1T~@{7|Qt}T>>EPmeow5vWPJnfS^5WC&!#)fdal|A^Q$Lpg!BS$M(ki^&w=}2&dgftLvMWsMPWTq zSt@*n92&mFAWn&($VvL>@J7vA@tDf;q@7n7ol1Zb^Cox!RlNMkoMCQ;q!H40DvZvV z6h+tHEapmBoGqXxzr&4=gFW#W8K2R1>htIBsV9R-q;<4BIRa=O?$ejfBm&TdUoiU(3iDgKQ%P z%(=1yWa=>w%lccKQ2wfMCePrN$hso;l@eNmmSYmf%>Lj&zR^WlMj3g4{bQlh{E5Dz%$|0oF^E%EPTxpe_V3BW=`QBW+Ml z6c?3v!;!vVrxWWUJSaLxSSM z$zV+DH}ap0`&D90!#F>$FPIt@-QtZN!?#hf*!<+GLe5Pr+;>(K)3%y|Xu(87M^?3x ziTa}L>5&OjgB%=ei`e`|ao00`L(<~0y`(A*(u)mx!>9eDg1ro6rAW)l*k~p=h))kD zu^gQBW{;qrZ|m`s+K_R>)(QQ)vb_hOWEHXHOnahRg)*HK^jmtpQz0 z9mOWJQ_sL@_dKVqe)txAL7Q`{13(N$HP;!q2ALF;a_}bq;YM}is?Hq8l!T8b&R6H9 zTe3rmQ(ys0ui{J6nm7-GIO!>Z!h6+t7Bm%#Wa9ksbzjogpvm*hg0{a~&baz$XWtfcpsbKh}QnKpPFap zBcUQC{2+IXZ-%}KKH+O0s-clk3hSg zP8|TImZ4UfXbV!O)4ZVt@cD1OxCP(7o_XBgvPpOOp3G@rM3DY&6E#`6;kq3Q4CCm6 x<&b;8r5jNZMHH<}V-JSW4@nPnH(iFP3|Nr0E(aravI5V6Eg_-c(qaEI^uMKeW{>~? literal 0 HcmV?d00001 diff --git a/static/layui/css/layui.css b/static/layui/css/layui.css new file mode 100644 index 0000000..f297bf6 --- /dev/null +++ b/static/layui/css/layui.css @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + .layui-inline,img{display:inline-block;vertical-align:middle}h1,h2,h3,h4,h5,h6{font-weight:400}.layui-edge,.layui-header,.layui-inline,.layui-main{position:relative}.layui-elip,.layui-form-checkbox span,.layui-form-pane .layui-form-label{text-overflow:ellipsis;white-space:nowrap}.layui-btn,.layui-edge,.layui-inline,img{vertical-align:middle}.layui-btn,.layui-disabled,.layui-icon,.layui-unselect{-webkit-user-select:none;-ms-user-select:none;-moz-user-select:none}blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,li,ol,p,pre,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}a:active,a:hover{outline:0}img{border:none}li{list-style:none}table{border-collapse:collapse;border-spacing:0}h4,h5,h6{font-size:100%}button,input,optgroup,option,select,textarea{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;outline:0}pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}body{line-height:24px;font:14px Helvetica Neue,Helvetica,PingFang SC,Tahoma,Arial,sans-serif}hr{height:1px;margin:10px 0;border:0;clear:both}a{color:#333;text-decoration:none}a:hover{color:#777}a cite{font-style:normal;*cursor:pointer}.layui-border-box,.layui-border-box *{box-sizing:border-box}.layui-box,.layui-box *{box-sizing:content-box}.layui-clear{clear:both;*zoom:1}.layui-clear:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-inline{*display:inline;*zoom:1}.layui-edge{display:inline-block;width:0;height:0;border-width:6px;border-style:dashed;border-color:transparent;overflow:hidden}.layui-edge-top{top:-4px;border-bottom-color:#999;border-bottom-style:solid}.layui-edge-right{border-left-color:#999;border-left-style:solid}.layui-edge-bottom{top:2px;border-top-color:#999;border-top-style:solid}.layui-edge-left{border-right-color:#999;border-right-style:solid}.layui-elip{overflow:hidden}.layui-disabled,.layui-disabled:hover{color:#d2d2d2!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=240);src:url(../font/iconfont.eot?v=240#iefix) format('embedded-opentype'),url(../font/iconfont.svg?v=240#iconfont) format('svg'),url(../font/iconfont.woff?v=240) format('woff'),url(../font/iconfont.ttf?v=240) format('truetype')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-icon-reply-fill:before{content:"\e611"}.layui-icon-set-fill:before{content:"\e614"}.layui-icon-menu-fill:before{content:"\e60f"}.layui-icon-search:before{content:"\e615"}.layui-icon-share:before{content:"\e641"}.layui-icon-set-sm:before{content:"\e620"}.layui-icon-engine:before{content:"\e628"}.layui-icon-close:before{content:"\1006"}.layui-icon-close-fill:before{content:"\1007"}.layui-icon-chart-screen:before{content:"\e629"}.layui-icon-star:before{content:"\e600"}.layui-icon-circle-dot:before{content:"\e617"}.layui-icon-chat:before{content:"\e606"}.layui-icon-release:before{content:"\e609"}.layui-icon-list:before{content:"\e60a"}.layui-icon-chart:before{content:"\e62c"}.layui-icon-ok-circle:before{content:"\1005"}.layui-icon-layim-theme:before{content:"\e61b"}.layui-icon-table:before{content:"\e62d"}.layui-icon-right:before{content:"\e602"}.layui-icon-left:before{content:"\e603"}.layui-icon-cart-simple:before{content:"\e698"}.layui-icon-face-cry:before{content:"\e69c"}.layui-icon-face-smile:before{content:"\e6af"}.layui-icon-survey:before{content:"\e6b2"}.layui-icon-tree:before{content:"\e62e"}.layui-icon-upload-circle:before{content:"\e62f"}.layui-icon-add-circle:before{content:"\e61f"}.layui-icon-download-circle:before{content:"\e601"}.layui-icon-templeate-1:before{content:"\e630"}.layui-icon-util:before{content:"\e631"}.layui-icon-face-surprised:before{content:"\e664"}.layui-icon-edit:before{content:"\e642"}.layui-icon-speaker:before{content:"\e645"}.layui-icon-down:before{content:"\e61a"}.layui-icon-file:before{content:"\e621"}.layui-icon-layouts:before{content:"\e632"}.layui-icon-rate-half:before{content:"\e6c9"}.layui-icon-add-circle-fine:before{content:"\e608"}.layui-icon-prev-circle:before{content:"\e633"}.layui-icon-read:before{content:"\e705"}.layui-icon-404:before{content:"\e61c"}.layui-icon-carousel:before{content:"\e634"}.layui-icon-help:before{content:"\e607"}.layui-icon-code-circle:before{content:"\e635"}.layui-icon-water:before{content:"\e636"}.layui-icon-username:before{content:"\e66f"}.layui-icon-find-fill:before{content:"\e670"}.layui-icon-about:before{content:"\e60b"}.layui-icon-location:before{content:"\e715"}.layui-icon-up:before{content:"\e619"}.layui-icon-pause:before{content:"\e651"}.layui-icon-date:before{content:"\e637"}.layui-icon-layim-uploadfile:before{content:"\e61d"}.layui-icon-delete:before{content:"\e640"}.layui-icon-play:before{content:"\e652"}.layui-icon-top:before{content:"\e604"}.layui-icon-friends:before{content:"\e612"}.layui-icon-refresh-3:before{content:"\e9aa"}.layui-icon-ok:before{content:"\e605"}.layui-icon-layer:before{content:"\e638"}.layui-icon-face-smile-fine:before{content:"\e60c"}.layui-icon-dollar:before{content:"\e659"}.layui-icon-group:before{content:"\e613"}.layui-icon-layim-download:before{content:"\e61e"}.layui-icon-picture-fine:before{content:"\e60d"}.layui-icon-link:before{content:"\e64c"}.layui-icon-diamond:before{content:"\e735"}.layui-icon-log:before{content:"\e60e"}.layui-icon-rate-solid:before{content:"\e67a"}.layui-icon-fonts-del:before{content:"\e64f"}.layui-icon-unlink:before{content:"\e64d"}.layui-icon-fonts-clear:before{content:"\e639"}.layui-icon-triangle-r:before{content:"\e623"}.layui-icon-circle:before{content:"\e63f"}.layui-icon-radio:before{content:"\e643"}.layui-icon-align-center:before{content:"\e647"}.layui-icon-align-right:before{content:"\e648"}.layui-icon-align-left:before{content:"\e649"}.layui-icon-loading-1:before{content:"\e63e"}.layui-icon-return:before{content:"\e65c"}.layui-icon-fonts-strong:before{content:"\e62b"}.layui-icon-upload:before{content:"\e67c"}.layui-icon-dialogue:before{content:"\e63a"}.layui-icon-video:before{content:"\e6ed"}.layui-icon-headset:before{content:"\e6fc"}.layui-icon-cellphone-fine:before{content:"\e63b"}.layui-icon-add-1:before{content:"\e654"}.layui-icon-face-smile-b:before{content:"\e650"}.layui-icon-fonts-html:before{content:"\e64b"}.layui-icon-form:before{content:"\e63c"}.layui-icon-cart:before{content:"\e657"}.layui-icon-camera-fill:before{content:"\e65d"}.layui-icon-tabs:before{content:"\e62a"}.layui-icon-fonts-code:before{content:"\e64e"}.layui-icon-fire:before{content:"\e756"}.layui-icon-set:before{content:"\e716"}.layui-icon-fonts-u:before{content:"\e646"}.layui-icon-triangle-d:before{content:"\e625"}.layui-icon-tips:before{content:"\e702"}.layui-icon-picture:before{content:"\e64a"}.layui-icon-more-vertical:before{content:"\e671"}.layui-icon-flag:before{content:"\e66c"}.layui-icon-loading:before{content:"\e63d"}.layui-icon-fonts-i:before{content:"\e644"}.layui-icon-refresh-1:before{content:"\e666"}.layui-icon-rmb:before{content:"\e65e"}.layui-icon-home:before{content:"\e68e"}.layui-icon-user:before{content:"\e770"}.layui-icon-notice:before{content:"\e667"}.layui-icon-login-weibo:before{content:"\e675"}.layui-icon-voice:before{content:"\e688"}.layui-icon-upload-drag:before{content:"\e681"}.layui-icon-login-qq:before{content:"\e676"}.layui-icon-snowflake:before{content:"\e6b1"}.layui-icon-file-b:before{content:"\e655"}.layui-icon-template:before{content:"\e663"}.layui-icon-auz:before{content:"\e672"}.layui-icon-console:before{content:"\e665"}.layui-icon-app:before{content:"\e653"}.layui-icon-prev:before{content:"\e65a"}.layui-icon-website:before{content:"\e7ae"}.layui-icon-next:before{content:"\e65b"}.layui-icon-component:before{content:"\e857"}.layui-icon-more:before{content:"\e65f"}.layui-icon-login-wechat:before{content:"\e677"}.layui-icon-shrink-right:before{content:"\e668"}.layui-icon-spread-left:before{content:"\e66b"}.layui-icon-camera:before{content:"\e660"}.layui-icon-note:before{content:"\e66e"}.layui-icon-refresh:before{content:"\e669"}.layui-icon-female:before{content:"\e661"}.layui-icon-male:before{content:"\e662"}.layui-icon-password:before{content:"\e673"}.layui-icon-senior:before{content:"\e674"}.layui-icon-theme:before{content:"\e66a"}.layui-icon-tread:before{content:"\e6c5"}.layui-icon-praise:before{content:"\e6c6"}.layui-icon-star-fill:before{content:"\e658"}.layui-icon-rate:before{content:"\e67b"}.layui-icon-template-1:before{content:"\e656"}.layui-icon-vercode:before{content:"\e679"}.layui-icon-cellphone:before{content:"\e678"}.layui-icon-screen-full:before{content:"\e622"}.layui-icon-screen-restore:before{content:"\e758"}.layui-icon-cols:before{content:"\e610"}.layui-icon-export:before{content:"\e67d"}.layui-icon-print:before{content:"\e66d"}.layui-icon-slider:before{content:"\e714"}.layui-main{width:1140px;margin:0 auto}.layui-header{z-index:1000;height:60px}.layui-header a:hover{transition:all .5s;-webkit-transition:all .5s}.layui-side{position:fixed;left:0;top:0;bottom:0;z-index:999;width:200px;overflow-x:hidden}.layui-side-scroll{position:relative;width:220px;height:100%;overflow-x:hidden}.layui-body{position:absolute;left:200px;right:0;top:0;bottom:0;z-index:998;width:auto;overflow:hidden;overflow-y:auto;box-sizing:border-box}.layui-layout-body{overflow:hidden}.layui-layout-admin .layui-header{background-color:#23262E}.layui-layout-admin .layui-side{top:60px;width:200px;overflow-x:hidden}.layui-layout-admin .layui-body{top:60px;bottom:44px}.layui-layout-admin .layui-main{width:auto;margin:0 15px}.layui-layout-admin .layui-footer{position:fixed;left:200px;right:0;bottom:0;height:44px;line-height:44px;padding:0 15px;background-color:#eee}.layui-layout-admin .layui-logo{position:absolute;left:0;top:0;width:200px;height:100%;line-height:60px;text-align:center;color:#009688;font-size:16px}.layui-layout-admin .layui-header .layui-nav{background:0 0}.layui-layout-left{position:absolute!important;left:200px;top:0}.layui-layout-right{position:absolute!important;right:0;top:0}.layui-container{position:relative;margin:0 auto;padding:0 15px;box-sizing:border-box}.layui-fluid{position:relative;margin:0 auto;padding:0 15px}.layui-row:after,.layui-row:before{content:'';display:block;clear:both}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9,.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9,.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9,.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{position:relative;display:block;box-sizing:border-box}.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{float:left}.layui-col-xs1{width:8.33333333%}.layui-col-xs2{width:16.66666667%}.layui-col-xs3{width:25%}.layui-col-xs4{width:33.33333333%}.layui-col-xs5{width:41.66666667%}.layui-col-xs6{width:50%}.layui-col-xs7{width:58.33333333%}.layui-col-xs8{width:66.66666667%}.layui-col-xs9{width:75%}.layui-col-xs10{width:83.33333333%}.layui-col-xs11{width:91.66666667%}.layui-col-xs12{width:100%}.layui-col-xs-offset1{margin-left:8.33333333%}.layui-col-xs-offset2{margin-left:16.66666667%}.layui-col-xs-offset3{margin-left:25%}.layui-col-xs-offset4{margin-left:33.33333333%}.layui-col-xs-offset5{margin-left:41.66666667%}.layui-col-xs-offset6{margin-left:50%}.layui-col-xs-offset7{margin-left:58.33333333%}.layui-col-xs-offset8{margin-left:66.66666667%}.layui-col-xs-offset9{margin-left:75%}.layui-col-xs-offset10{margin-left:83.33333333%}.layui-col-xs-offset11{margin-left:91.66666667%}.layui-col-xs-offset12{margin-left:100%}@media screen and (max-width:768px){.layui-hide-xs{display:none!important}.layui-show-xs-block{display:block!important}.layui-show-xs-inline{display:inline!important}.layui-show-xs-inline-block{display:inline-block!important}}@media screen and (min-width:768px){.layui-container{width:750px}.layui-hide-sm{display:none!important}.layui-show-sm-block{display:block!important}.layui-show-sm-inline{display:inline!important}.layui-show-sm-inline-block{display:inline-block!important}.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9{float:left}.layui-col-sm1{width:8.33333333%}.layui-col-sm2{width:16.66666667%}.layui-col-sm3{width:25%}.layui-col-sm4{width:33.33333333%}.layui-col-sm5{width:41.66666667%}.layui-col-sm6{width:50%}.layui-col-sm7{width:58.33333333%}.layui-col-sm8{width:66.66666667%}.layui-col-sm9{width:75%}.layui-col-sm10{width:83.33333333%}.layui-col-sm11{width:91.66666667%}.layui-col-sm12{width:100%}.layui-col-sm-offset1{margin-left:8.33333333%}.layui-col-sm-offset2{margin-left:16.66666667%}.layui-col-sm-offset3{margin-left:25%}.layui-col-sm-offset4{margin-left:33.33333333%}.layui-col-sm-offset5{margin-left:41.66666667%}.layui-col-sm-offset6{margin-left:50%}.layui-col-sm-offset7{margin-left:58.33333333%}.layui-col-sm-offset8{margin-left:66.66666667%}.layui-col-sm-offset9{margin-left:75%}.layui-col-sm-offset10{margin-left:83.33333333%}.layui-col-sm-offset11{margin-left:91.66666667%}.layui-col-sm-offset12{margin-left:100%}}@media screen and (min-width:992px){.layui-container{width:970px}.layui-hide-md{display:none!important}.layui-show-md-block{display:block!important}.layui-show-md-inline{display:inline!important}.layui-show-md-inline-block{display:inline-block!important}.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9{float:left}.layui-col-md1{width:8.33333333%}.layui-col-md2{width:16.66666667%}.layui-col-md3{width:25%}.layui-col-md4{width:33.33333333%}.layui-col-md5{width:41.66666667%}.layui-col-md6{width:50%}.layui-col-md7{width:58.33333333%}.layui-col-md8{width:66.66666667%}.layui-col-md9{width:75%}.layui-col-md10{width:83.33333333%}.layui-col-md11{width:91.66666667%}.layui-col-md12{width:100%}.layui-col-md-offset1{margin-left:8.33333333%}.layui-col-md-offset2{margin-left:16.66666667%}.layui-col-md-offset3{margin-left:25%}.layui-col-md-offset4{margin-left:33.33333333%}.layui-col-md-offset5{margin-left:41.66666667%}.layui-col-md-offset6{margin-left:50%}.layui-col-md-offset7{margin-left:58.33333333%}.layui-col-md-offset8{margin-left:66.66666667%}.layui-col-md-offset9{margin-left:75%}.layui-col-md-offset10{margin-left:83.33333333%}.layui-col-md-offset11{margin-left:91.66666667%}.layui-col-md-offset12{margin-left:100%}}@media screen and (min-width:1200px){.layui-container{width:1170px}.layui-hide-lg{display:none!important}.layui-show-lg-block{display:block!important}.layui-show-lg-inline{display:inline!important}.layui-show-lg-inline-block{display:inline-block!important}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9{float:left}.layui-col-lg1{width:8.33333333%}.layui-col-lg2{width:16.66666667%}.layui-col-lg3{width:25%}.layui-col-lg4{width:33.33333333%}.layui-col-lg5{width:41.66666667%}.layui-col-lg6{width:50%}.layui-col-lg7{width:58.33333333%}.layui-col-lg8{width:66.66666667%}.layui-col-lg9{width:75%}.layui-col-lg10{width:83.33333333%}.layui-col-lg11{width:91.66666667%}.layui-col-lg12{width:100%}.layui-col-lg-offset1{margin-left:8.33333333%}.layui-col-lg-offset2{margin-left:16.66666667%}.layui-col-lg-offset3{margin-left:25%}.layui-col-lg-offset4{margin-left:33.33333333%}.layui-col-lg-offset5{margin-left:41.66666667%}.layui-col-lg-offset6{margin-left:50%}.layui-col-lg-offset7{margin-left:58.33333333%}.layui-col-lg-offset8{margin-left:66.66666667%}.layui-col-lg-offset9{margin-left:75%}.layui-col-lg-offset10{margin-left:83.33333333%}.layui-col-lg-offset11{margin-left:91.66666667%}.layui-col-lg-offset12{margin-left:100%}}.layui-col-space1{margin:-.5px}.layui-col-space1>*{padding:.5px}.layui-col-space3{margin:-1.5px}.layui-col-space3>*{padding:1.5px}.layui-col-space5{margin:-2.5px}.layui-col-space5>*{padding:2.5px}.layui-col-space8{margin:-3.5px}.layui-col-space8>*{padding:3.5px}.layui-col-space10{margin:-5px}.layui-col-space10>*{padding:5px}.layui-col-space12{margin:-6px}.layui-col-space12>*{padding:6px}.layui-col-space15{margin:-7.5px}.layui-col-space15>*{padding:7.5px}.layui-col-space18{margin:-9px}.layui-col-space18>*{padding:9px}.layui-col-space20{margin:-10px}.layui-col-space20>*{padding:10px}.layui-col-space22{margin:-11px}.layui-col-space22>*{padding:11px}.layui-col-space25{margin:-12.5px}.layui-col-space25>*{padding:12.5px}.layui-col-space30{margin:-15px}.layui-col-space30>*{padding:15px}.layui-btn,.layui-input,.layui-select,.layui-textarea,.layui-upload-button{outline:0;-webkit-appearance:none;transition:all .3s;-webkit-transition:all .3s;box-sizing:border-box}.layui-elem-quote{margin-bottom:10px;padding:15px;line-height:22px;border-left:5px solid #009688;border-radius:0 2px 2px 0;background-color:#f2f2f2}.layui-quote-nm{border-style:solid;border-width:1px 1px 1px 5px;background:0 0}.layui-elem-field{margin-bottom:10px;padding:0;border-width:1px;border-style:solid}.layui-elem-field legend{margin-left:20px;padding:0 10px;font-size:20px;font-weight:300}.layui-field-title{margin:10px 0 20px;border-width:1px 0 0}.layui-field-box{padding:10px 15px}.layui-field-title .layui-field-box{padding:10px 0}.layui-progress{position:relative;height:6px;border-radius:20px;background-color:#e2e2e2}.layui-progress-bar{position:absolute;left:0;top:0;width:0;max-width:100%;height:6px;border-radius:20px;text-align:right;background-color:#5FB878;transition:all .3s;-webkit-transition:all .3s}.layui-progress-big,.layui-progress-big .layui-progress-bar{height:18px;line-height:18px}.layui-progress-text{position:relative;top:-20px;line-height:18px;font-size:12px;color:#666}.layui-progress-big .layui-progress-text{position:static;padding:0 10px;color:#fff}.layui-collapse{border-width:1px;border-style:solid;border-radius:2px}.layui-colla-content,.layui-colla-item{border-top-width:1px;border-top-style:solid}.layui-colla-item:first-child{border-top:none}.layui-colla-title{position:relative;height:42px;line-height:42px;padding:0 15px 0 35px;color:#333;background-color:#f2f2f2;cursor:pointer;font-size:14px;overflow:hidden}.layui-colla-content{display:none;padding:10px 15px;line-height:22px;color:#666}.layui-colla-icon{position:absolute;left:15px;top:0;font-size:14px}.layui-card{margin-bottom:15px;border-radius:2px;background-color:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.layui-card:last-child{margin-bottom:0}.layui-card-header{position:relative;height:42px;line-height:42px;padding:0 15px;border-bottom:1px solid #f6f6f6;color:#333;border-radius:2px 2px 0 0;font-size:14px}.layui-bg-black,.layui-bg-blue,.layui-bg-cyan,.layui-bg-green,.layui-bg-orange,.layui-bg-red{color:#fff!important}.layui-card-body{position:relative;padding:10px 15px;line-height:24px}.layui-card-body[pad15]{padding:15px}.layui-card-body[pad20]{padding:20px}.layui-card-body .layui-table{margin:5px 0}.layui-card .layui-tab{margin:0}.layui-panel-window{position:relative;padding:15px;border-radius:0;border-top:5px solid #E6E6E6;background-color:#fff}.layui-auxiliar-moving{position:fixed;left:0;right:0;top:0;bottom:0;width:100%;height:100%;background:0 0;z-index:9999999999}.layui-form-label,.layui-form-mid,.layui-form-select,.layui-input-block,.layui-input-inline,.layui-textarea{position:relative}.layui-bg-red{background-color:#FF5722!important}.layui-bg-orange{background-color:#FFB800!important}.layui-bg-green{background-color:#009688!important}.layui-bg-cyan{background-color:#2F4056!important}.layui-bg-blue{background-color:#1E9FFF!important}.layui-bg-black{background-color:#393D49!important}.layui-bg-gray{background-color:#eee!important;color:#666!important}.layui-badge-rim,.layui-colla-content,.layui-colla-item,.layui-collapse,.layui-elem-field,.layui-form-pane .layui-form-item[pane],.layui-form-pane .layui-form-label,.layui-input,.layui-layedit,.layui-layedit-tool,.layui-quote-nm,.layui-select,.layui-tab-bar,.layui-tab-card,.layui-tab-title,.layui-tab-title .layui-this:after,.layui-textarea{border-color:#e6e6e6}.layui-timeline-item:before,hr{background-color:#e6e6e6}.layui-text{line-height:22px;font-size:14px;color:#666}.layui-text h1,.layui-text h2,.layui-text h3{font-weight:500;color:#333}.layui-text h1{font-size:30px}.layui-text h2{font-size:24px}.layui-text h3{font-size:18px}.layui-text a:not(.layui-btn){color:#01AAED}.layui-text a:not(.layui-btn):hover{text-decoration:underline}.layui-text ul{padding:5px 0 5px 15px}.layui-text ul li{margin-top:5px;list-style-type:disc}.layui-text em,.layui-word-aux{color:#999!important;padding:0 5px!important}.layui-btn{display:inline-block;height:38px;line-height:38px;padding:0 18px;background-color:#009688;color:#fff;white-space:nowrap;text-align:center;font-size:14px;border:none;border-radius:2px;cursor:pointer}.layui-btn:hover{opacity:.8;filter:alpha(opacity=80);color:#fff}.layui-btn:active{opacity:1;filter:alpha(opacity=100)}.layui-btn+.layui-btn{margin-left:10px}.layui-btn-container{font-size:0}.layui-btn-container .layui-btn{margin-right:10px;margin-bottom:10px}.layui-btn-container .layui-btn+.layui-btn{margin-left:0}.layui-table .layui-btn-container .layui-btn{margin-bottom:9px}.layui-btn-radius{border-radius:100px}.layui-btn .layui-icon{margin-right:3px;font-size:18px;vertical-align:bottom;vertical-align:middle\9}.layui-btn-primary{border:1px solid #C9C9C9;background-color:#fff;color:#555}.layui-btn-primary:hover{border-color:#009688;color:#333}.layui-btn-normal{background-color:#1E9FFF}.layui-btn-warm{background-color:#FFB800}.layui-btn-danger{background-color:#FF5722}.layui-btn-disabled,.layui-btn-disabled:active,.layui-btn-disabled:hover{border:1px solid #e6e6e6;background-color:#FBFBFB;color:#C9C9C9;cursor:not-allowed;opacity:1}.layui-btn-lg{height:44px;line-height:44px;padding:0 25px;font-size:16px}.layui-btn-sm{height:30px;line-height:30px;padding:0 10px;font-size:12px}.layui-btn-sm i{font-size:16px!important}.layui-btn-xs{height:22px;line-height:22px;padding:0 5px;font-size:12px}.layui-btn-xs i{font-size:14px!important}.layui-btn-group{display:inline-block;vertical-align:middle;font-size:0}.layui-btn-group .layui-btn{margin-left:0!important;margin-right:0!important;border-left:1px solid rgba(255,255,255,.5);border-radius:0}.layui-btn-group .layui-btn-primary{border-left:none}.layui-btn-group .layui-btn-primary:hover{border-color:#C9C9C9;color:#009688}.layui-btn-group .layui-btn:first-child{border-left:none;border-radius:2px 0 0 2px}.layui-btn-group .layui-btn-primary:first-child{border-left:1px solid #c9c9c9}.layui-btn-group .layui-btn:last-child{border-radius:0 2px 2px 0}.layui-btn-group .layui-btn+.layui-btn{margin-left:0}.layui-btn-group+.layui-btn-group{margin-left:10px}.layui-btn-fluid{width:100%}.layui-input,.layui-select,.layui-textarea{height:38px;line-height:1.3;line-height:38px\9;border-width:1px;border-style:solid;background-color:#fff;border-radius:2px}.layui-input::-webkit-input-placeholder,.layui-select::-webkit-input-placeholder,.layui-textarea::-webkit-input-placeholder{line-height:1.3}.layui-input,.layui-textarea{display:block;width:100%;padding-left:10px}.layui-input:hover,.layui-textarea:hover{border-color:#D2D2D2!important}.layui-input:focus,.layui-textarea:focus{border-color:#C9C9C9!important}.layui-textarea{min-height:100px;height:auto;line-height:20px;padding:6px 10px;resize:vertical}.layui-select{padding:0 10px}.layui-form input[type=checkbox],.layui-form input[type=radio],.layui-form select{display:none}.layui-form [lay-ignore]{display:initial}.layui-form-item{margin-bottom:15px;clear:both;*zoom:1}.layui-form-item:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-form-label{float:left;display:block;padding:9px 15px;width:80px;font-weight:400;line-height:20px;text-align:right}.layui-form-label-col{display:block;float:none;padding:9px 0;line-height:20px;text-align:left}.layui-form-item .layui-inline{margin-bottom:5px;margin-right:10px}.layui-input-block{margin-left:110px;min-height:36px}.layui-input-inline{display:inline-block;vertical-align:middle}.layui-form-item .layui-input-inline{float:left;width:190px;margin-right:10px}.layui-form-text .layui-input-inline{width:auto}.layui-form-mid{float:left;display:block;padding:9px 0!important;line-height:20px;margin-right:10px}.layui-form-danger+.layui-form-select .layui-input,.layui-form-danger:focus{border-color:#FF5722!important}.layui-form-select .layui-input{padding-right:30px;cursor:pointer}.layui-form-select .layui-edge{position:absolute;right:10px;top:50%;margin-top:-3px;cursor:pointer;border-width:6px;border-top-color:#c2c2c2;border-top-style:solid;transition:all .3s;-webkit-transition:all .3s}.layui-form-select dl{display:none;position:absolute;left:0;top:42px;padding:5px 0;z-index:899;min-width:100%;border:1px solid #d2d2d2;max-height:300px;overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box}.layui-form-select dl dd,.layui-form-select dl dt{padding:0 10px;line-height:36px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.layui-form-select dl dt{font-size:12px;color:#999}.layui-form-select dl dd{cursor:pointer}.layui-form-select dl dd:hover{background-color:#f2f2f2;-webkit-transition:.5s all;transition:.5s all}.layui-form-select .layui-select-group dd{padding-left:20px}.layui-form-select dl dd.layui-select-tips{padding-left:10px!important;color:#999}.layui-form-select dl dd.layui-this{background-color:#5FB878;color:#fff}.layui-form-checkbox,.layui-form-select dl dd.layui-disabled{background-color:#fff}.layui-form-selected dl{display:block}.layui-form-checkbox,.layui-form-checkbox *,.layui-form-switch{display:inline-block;vertical-align:middle}.layui-form-selected .layui-edge{margin-top:-9px;-webkit-transform:rotate(180deg);transform:rotate(180deg);margin-top:-3px\9}:root .layui-form-selected .layui-edge{margin-top:-9px\0/IE9}.layui-form-selectup dl{top:auto;bottom:42px}.layui-select-none{margin:5px 0;text-align:center;color:#999}.layui-select-disabled .layui-disabled{border-color:#eee!important}.layui-select-disabled .layui-edge{border-top-color:#d2d2d2}.layui-form-checkbox{position:relative;height:30px;line-height:30px;margin-right:10px;padding-right:30px;cursor:pointer;font-size:0;-webkit-transition:.1s linear;transition:.1s linear;box-sizing:border-box}.layui-form-checkbox span{padding:0 10px;height:100%;font-size:14px;border-radius:2px 0 0 2px;background-color:#d2d2d2;color:#fff;overflow:hidden}.layui-form-checkbox:hover span{background-color:#c2c2c2}.layui-form-checkbox i{position:absolute;right:0;top:0;width:30px;height:28px;border:1px solid #d2d2d2;border-left:none;border-radius:0 2px 2px 0;color:#fff;font-size:20px;text-align:center}.layui-form-checkbox:hover i{border-color:#c2c2c2;color:#c2c2c2}.layui-form-checked,.layui-form-checked:hover{border-color:#5FB878}.layui-form-checked span,.layui-form-checked:hover span{background-color:#5FB878}.layui-form-checked i,.layui-form-checked:hover i{color:#5FB878}.layui-form-item .layui-form-checkbox{margin-top:4px}.layui-form-checkbox[lay-skin=primary]{height:auto!important;line-height:normal!important;min-width:18px;min-height:18px;border:none!important;margin-right:0;padding-left:28px;padding-right:0;background:0 0}.layui-form-checkbox[lay-skin=primary] span{padding-left:0;padding-right:15px;line-height:18px;background:0 0;color:#666}.layui-form-checkbox[lay-skin=primary] i{right:auto;left:0;width:16px;height:16px;line-height:16px;border:1px solid #d2d2d2;font-size:12px;border-radius:2px;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-checkbox[lay-skin=primary]:hover i{border-color:#5FB878;color:#fff}.layui-form-checked[lay-skin=primary] i{border-color:#5FB878;background-color:#5FB878;color:#fff}.layui-checkbox-disbaled[lay-skin=primary] span{background:0 0!important;color:#c2c2c2}.layui-checkbox-disbaled[lay-skin=primary]:hover i{border-color:#d2d2d2}.layui-form-item .layui-form-checkbox[lay-skin=primary]{margin-top:10px}.layui-form-switch{position:relative;height:22px;line-height:22px;min-width:35px;padding:0 5px;margin-top:8px;border:1px solid #d2d2d2;border-radius:20px;cursor:pointer;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch i{position:absolute;left:5px;top:3px;width:16px;height:16px;border-radius:20px;background-color:#d2d2d2;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch em{position:relative;top:0;width:25px;margin-left:21px;padding:0!important;text-align:center!important;color:#999!important;font-style:normal!important;font-size:12px}.layui-form-onswitch{border-color:#5FB878;background-color:#5FB878}.layui-checkbox-disbaled,.layui-checkbox-disbaled i{border-color:#e2e2e2!important}.layui-form-onswitch i{left:100%;margin-left:-21px;background-color:#fff}.layui-form-onswitch em{margin-left:5px;margin-right:21px;color:#fff!important}.layui-checkbox-disbaled span{background-color:#e2e2e2!important}.layui-checkbox-disbaled:hover i{color:#fff!important}[lay-radio]{display:none}.layui-form-radio,.layui-form-radio *{display:inline-block;vertical-align:middle}.layui-form-radio{line-height:28px;margin:6px 10px 0 0;padding-right:10px;cursor:pointer;font-size:0}.layui-form-radio *{font-size:14px}.layui-form-radio>i{margin-right:8px;font-size:22px;color:#c2c2c2}.layui-form-radio>i:hover,.layui-form-radioed>i{color:#5FB878}.layui-radio-disbaled>i{color:#e2e2e2!important}.layui-form-pane .layui-form-label{width:110px;padding:8px 15px;height:38px;line-height:20px;border-width:1px;border-style:solid;border-radius:2px 0 0 2px;text-align:center;background-color:#FBFBFB;overflow:hidden;box-sizing:border-box}.layui-form-pane .layui-input-inline{margin-left:-1px}.layui-form-pane .layui-input-block{margin-left:110px;left:-1px}.layui-form-pane .layui-input{border-radius:0 2px 2px 0}.layui-form-pane .layui-form-text .layui-form-label{float:none;width:100%;border-radius:2px;box-sizing:border-box;text-align:left}.layui-form-pane .layui-form-text .layui-input-inline{display:block;margin:0;top:-1px;clear:both}.layui-form-pane .layui-form-text .layui-input-block{margin:0;left:0;top:-1px}.layui-form-pane .layui-form-text .layui-textarea{min-height:100px;border-radius:0 0 2px 2px}.layui-form-pane .layui-form-checkbox{margin:4px 0 4px 10px}.layui-form-pane .layui-form-radio,.layui-form-pane .layui-form-switch{margin-top:6px;margin-left:10px}.layui-form-pane .layui-form-item[pane]{position:relative;border-width:1px;border-style:solid}.layui-form-pane .layui-form-item[pane] .layui-form-label{position:absolute;left:0;top:0;height:100%;border-width:0 1px 0 0}.layui-form-pane .layui-form-item[pane] .layui-input-inline{margin-left:110px}@media screen and (max-width:450px){.layui-form-item .layui-form-label{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-form-item .layui-inline{display:block;margin-right:0;margin-bottom:20px;clear:both}.layui-form-item .layui-inline:after{content:'\20';clear:both;display:block;height:0}.layui-form-item .layui-input-inline{display:block;float:none;left:-3px;width:auto;margin:0 0 10px 112px}.layui-form-item .layui-input-inline+.layui-form-mid{margin-left:110px;top:-5px;padding:0}.layui-form-item .layui-form-checkbox{margin-right:5px;margin-bottom:5px}}.layui-layedit{border-width:1px;border-style:solid;border-radius:2px}.layui-layedit-tool{padding:3px 5px;border-bottom-width:1px;border-bottom-style:solid;font-size:0}.layedit-tool-fixed{position:fixed;top:0;border-top:1px solid #e2e2e2}.layui-layedit-tool .layedit-tool-mid,.layui-layedit-tool .layui-icon{display:inline-block;vertical-align:middle;text-align:center;font-size:14px}.layui-layedit-tool .layui-icon{position:relative;width:32px;height:30px;line-height:30px;margin:3px 5px;color:#777;cursor:pointer;border-radius:2px}.layui-layedit-tool .layui-icon:hover{color:#393D49}.layui-layedit-tool .layui-icon:active{color:#000}.layui-layedit-tool .layedit-tool-active{background-color:#e2e2e2;color:#000}.layui-layedit-tool .layui-disabled,.layui-layedit-tool .layui-disabled:hover{color:#d2d2d2;cursor:not-allowed}.layui-layedit-tool .layedit-tool-mid{width:1px;height:18px;margin:0 10px;background-color:#d2d2d2}.layedit-tool-html{width:50px!important;font-size:30px!important}.layedit-tool-b,.layedit-tool-code,.layedit-tool-help{font-size:16px!important}.layedit-tool-d,.layedit-tool-face,.layedit-tool-image,.layedit-tool-unlink{font-size:18px!important}.layedit-tool-image input{position:absolute;font-size:0;left:0;top:0;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-layedit-iframe iframe{display:block;width:100%}#LAY_layedit_code{overflow:hidden}.layui-laypage{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;margin:10px 0;font-size:0}.layui-laypage>a:first-child,.layui-laypage>a:first-child em{border-radius:2px 0 0 2px}.layui-laypage>a:last-child,.layui-laypage>a:last-child em{border-radius:0 2px 2px 0}.layui-laypage>:first-child{margin-left:0!important}.layui-laypage>:last-child{margin-right:0!important}.layui-laypage a,.layui-laypage button,.layui-laypage input,.layui-laypage select,.layui-laypage span{border:1px solid #e2e2e2}.layui-laypage a,.layui-laypage span{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding:0 15px;height:28px;line-height:28px;margin:0 -1px 5px 0;background-color:#fff;color:#333;font-size:12px}.layui-flow-more a *,.layui-laypage input,.layui-table-view select[lay-ignore]{display:inline-block}.layui-laypage a:hover{color:#009688}.layui-laypage em{font-style:normal}.layui-laypage .layui-laypage-spr{color:#999;font-weight:700}.layui-laypage a{text-decoration:none}.layui-laypage .layui-laypage-curr{position:relative}.layui-laypage .layui-laypage-curr em{position:relative;color:#fff}.layui-laypage .layui-laypage-curr .layui-laypage-em{position:absolute;left:-1px;top:-1px;padding:1px;width:100%;height:100%;background-color:#009688}.layui-laypage-em{border-radius:2px}.layui-laypage-next em,.layui-laypage-prev em{font-family:Sim sun;font-size:16px}.layui-laypage .layui-laypage-count,.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-refresh,.layui-laypage .layui-laypage-skip{margin-left:10px;margin-right:10px;padding:0;border:none}.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-refresh{vertical-align:top}.layui-laypage .layui-laypage-refresh i{font-size:18px;cursor:pointer}.layui-laypage select{height:22px;padding:3px;border-radius:2px;cursor:pointer}.layui-laypage .layui-laypage-skip{height:30px;line-height:30px;color:#999}.layui-laypage button,.layui-laypage input{height:30px;line-height:30px;border-radius:2px;vertical-align:top;background-color:#fff;box-sizing:border-box}.layui-laypage input{width:40px;margin:0 10px;padding:0 3px;text-align:center}.layui-laypage input:focus,.layui-laypage select:focus{border-color:#009688!important}.layui-laypage button{margin-left:10px;padding:0 10px;cursor:pointer}.layui-table,.layui-table-view{margin:10px 0}.layui-flow-more{margin:10px 0;text-align:center;color:#999;font-size:14px}.layui-flow-more a{height:32px;line-height:32px}.layui-flow-more a *{vertical-align:top}.layui-flow-more a cite{padding:0 20px;border-radius:3px;background-color:#eee;color:#333;font-style:normal}.layui-flow-more a cite:hover{opacity:.8}.layui-flow-more a i{font-size:30px;color:#737383}.layui-table{width:100%;background-color:#fff;color:#666}.layui-table tr{transition:all .3s;-webkit-transition:all .3s}.layui-table th{text-align:left;font-weight:400}.layui-table tbody tr:hover,.layui-table thead tr,.layui-table-click,.layui-table-header,.layui-table-hover,.layui-table-mend,.layui-table-patch,.layui-table-tool,.layui-table-total,.layui-table-total tr,.layui-table[lay-even] tr:nth-child(even){background-color:#f2f2f2}.layui-table td,.layui-table th,.layui-table-col-set,.layui-table-fixed-r,.layui-table-grid-down,.layui-table-header,.layui-table-page,.layui-table-tips-main,.layui-table-tool,.layui-table-total,.layui-table-view,.layui-table[lay-skin=line],.layui-table[lay-skin=row]{border-width:1px;border-style:solid;border-color:#e6e6e6}.layui-table td,.layui-table th{position:relative;padding:9px 15px;min-height:20px;line-height:20px;font-size:14px}.layui-table[lay-skin=line] td,.layui-table[lay-skin=line] th{border-width:0 0 1px}.layui-table[lay-skin=row] td,.layui-table[lay-skin=row] th{border-width:0 1px 0 0}.layui-table[lay-skin=nob] td,.layui-table[lay-skin=nob] th{border:none}.layui-table img{max-width:100px}.layui-table[lay-size=lg] td,.layui-table[lay-size=lg] th{padding:15px 30px}.layui-table-view .layui-table[lay-size=lg] .layui-table-cell{height:40px;line-height:40px}.layui-table[lay-size=sm] td,.layui-table[lay-size=sm] th{font-size:12px;padding:5px 10px}.layui-table-view .layui-table[lay-size=sm] .layui-table-cell{height:20px;line-height:20px}.layui-table[lay-data]{display:none}.layui-table-box{position:relative;overflow:hidden}.layui-table-view .layui-table{position:relative;width:auto;margin:0}.layui-table-view .layui-table[lay-skin=line]{border-width:0 1px 0 0}.layui-table-view .layui-table[lay-skin=row]{border-width:0 0 1px}.layui-table-view .layui-table td,.layui-table-view .layui-table th{padding:5px 0;border-top:none;border-left:none}.layui-table-view .layui-table th.layui-unselect .layui-table-cell span{cursor:pointer}.layui-table-view .layui-table td{cursor:default}.layui-table-view .layui-form-checkbox[lay-skin=primary] i{width:18px;height:18px}.layui-table-view .layui-form-radio{line-height:0;padding:0}.layui-table-view .layui-form-radio>i{margin:0;font-size:20px}.layui-table-init{position:absolute;left:0;top:0;width:100%;height:100%;text-align:center;z-index:110}.layui-table-init .layui-icon{position:absolute;left:50%;top:50%;margin:-15px 0 0 -15px;font-size:30px;color:#c2c2c2}.layui-table-header{border-width:0 0 1px;overflow:hidden}.layui-table-header .layui-table{margin-bottom:-1px}.layui-table-tool .layui-inline[lay-event]{position:relative;width:26px;height:26px;padding:5px;line-height:16px;margin-right:10px;text-align:center;color:#333;border:1px solid #ccc;cursor:pointer;-webkit-transition:.5s all;transition:.5s all}.layui-table-tool .layui-inline[lay-event]:hover{border:1px solid #999}.layui-table-tool-temp{padding-right:120px}.layui-table-tool-self{position:absolute;right:17px;top:10px}.layui-table-tool .layui-table-tool-self .layui-inline[lay-event]{margin:0 0 0 10px}.layui-table-tool-panel{position:absolute;top:29px;left:-1px;padding:5px 0;min-width:150px;min-height:40px;border:1px solid #d2d2d2;text-align:left;overflow-y:auto;background-color:#fff;box-shadow:0 2px 4px rgba(0,0,0,.12)}.layui-table-cell,.layui-table-tool-panel li{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.layui-table-tool-panel li{padding:0 10px;line-height:30px;-webkit-transition:.5s all;transition:.5s all}.layui-table-tool-panel li .layui-form-checkbox[lay-skin=primary]{width:100%;padding-left:28px}.layui-table-tool-panel li:hover{background-color:#f2f2f2}.layui-table-tool-panel li .layui-form-checkbox[lay-skin=primary] i{position:absolute;left:0;top:0}.layui-table-tool-panel li .layui-form-checkbox[lay-skin=primary] span{padding:0}.layui-table-tool .layui-table-tool-self .layui-table-tool-panel{left:auto;right:-1px}.layui-table-col-set{position:absolute;right:0;top:0;width:20px;height:100%;border-width:0 0 0 1px;background-color:#fff}.layui-table-sort{width:10px;height:20px;margin-left:5px;cursor:pointer!important}.layui-table-sort .layui-edge{position:absolute;left:5px;border-width:5px}.layui-table-sort .layui-table-sort-asc{top:3px;border-top:none;border-bottom-style:solid;border-bottom-color:#b2b2b2}.layui-table-sort .layui-table-sort-asc:hover{border-bottom-color:#666}.layui-table-sort .layui-table-sort-desc{bottom:5px;border-bottom:none;border-top-style:solid;border-top-color:#b2b2b2}.layui-table-sort .layui-table-sort-desc:hover{border-top-color:#666}.layui-table-sort[lay-sort=asc] .layui-table-sort-asc{border-bottom-color:#000}.layui-table-sort[lay-sort=desc] .layui-table-sort-desc{border-top-color:#000}.layui-table-cell{height:28px;line-height:28px;padding:0 15px;position:relative;box-sizing:border-box}.layui-table-cell .layui-form-checkbox[lay-skin=primary]{top:-1px;padding:0}.layui-table-cell .layui-table-link{color:#01AAED}.laytable-cell-checkbox,.laytable-cell-numbers,.laytable-cell-radio,.laytable-cell-space{padding:0;text-align:center}.layui-table-body{position:relative;overflow:auto;margin-right:-1px;margin-bottom:-1px}.layui-table-body .layui-none{line-height:26px;padding:15px;text-align:center;color:#999}.layui-table-fixed{position:absolute;left:0;top:0;z-index:101}.layui-table-fixed .layui-table-body{overflow:hidden}.layui-table-fixed-l{box-shadow:0 -1px 8px rgba(0,0,0,.08)}.layui-table-fixed-r{left:auto;right:-1px;border-width:0 0 0 1px;box-shadow:-1px 0 8px rgba(0,0,0,.08)}.layui-table-fixed-r .layui-table-header{position:relative;overflow:visible}.layui-table-mend{position:absolute;right:-49px;top:0;height:100%;width:50px}.layui-table-tool{position:relative;z-index:890;width:100%;min-height:50px;line-height:30px;padding:10px 15px;border-width:0 0 1px}.layui-table-tool .layui-btn-container{margin-bottom:-10px}.layui-table-page,.layui-table-total{border-width:1px 0 0;margin-bottom:-1px;overflow:hidden}.layui-table-page{position:relative;width:100%;padding:7px 7px 0;height:41px;font-size:12px;white-space:nowrap}.layui-table-page>div{height:26px}.layui-table-page .layui-laypage{margin:0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span{height:26px;line-height:26px;margin-bottom:10px;border:none;background:0 0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span.layui-laypage-curr{padding:0 12px}.layui-table-page .layui-laypage span{margin-left:0;padding:0}.layui-table-page .layui-laypage .layui-laypage-prev{margin-left:-7px!important}.layui-table-page .layui-laypage .layui-laypage-curr .layui-laypage-em{left:0;top:0;padding:0}.layui-table-page .layui-laypage button,.layui-table-page .layui-laypage input{height:26px;line-height:26px}.layui-table-page .layui-laypage input{width:40px}.layui-table-page .layui-laypage button{padding:0 10px}.layui-table-page select{height:18px}.layui-table-patch .layui-table-cell{padding:0;width:30px}.layui-table-edit{position:absolute;left:0;top:0;width:100%;height:100%;padding:0 14px 1px;border-radius:0;box-shadow:1px 1px 20px rgba(0,0,0,.15)}.layui-table-edit:focus{border-color:#5FB878!important}select.layui-table-edit{padding:0 0 0 10px;border-color:#C9C9C9}.layui-table-view .layui-form-checkbox,.layui-table-view .layui-form-radio,.layui-table-view .layui-form-switch{top:0;margin:0;box-sizing:content-box}.layui-table-view .layui-form-checkbox{top:-1px;height:26px;line-height:26px}.layui-table-view .layui-form-checkbox i{height:26px}.layui-table-grid .layui-table-cell{overflow:visible}.layui-table-grid-down{position:absolute;top:0;right:0;width:26px;height:100%;padding:5px 0;border-width:0 0 0 1px;text-align:center;background-color:#fff;color:#999;cursor:pointer}.layui-table-grid-down .layui-icon{position:absolute;top:50%;left:50%;margin:-8px 0 0 -8px}.layui-table-grid-down:hover{background-color:#fbfbfb}body .layui-table-tips .layui-layer-content{background:0 0;padding:0;box-shadow:0 1px 6px rgba(0,0,0,.12)}.layui-table-tips-main{margin:-44px 0 0 -1px;max-height:150px;padding:8px 15px;font-size:14px;overflow-y:scroll;background-color:#fff;color:#666}.layui-table-tips-c{position:absolute;right:-3px;top:-13px;width:20px;height:20px;padding:3px;cursor:pointer;background-color:#666;border-radius:50%;color:#fff}.layui-table-tips-c:hover{background-color:#777}.layui-table-tips-c:before{position:relative;right:-2px}.layui-upload-file{display:none!important;opacity:.01;filter:Alpha(opacity=1)}.layui-upload-drag,.layui-upload-form,.layui-upload-wrap{display:inline-block}.layui-upload-list{margin:10px 0}.layui-upload-choose{padding:0 10px;color:#999}.layui-upload-drag{position:relative;padding:30px;border:1px dashed #e2e2e2;background-color:#fff;text-align:center;cursor:pointer;color:#999}.layui-upload-drag .layui-icon{font-size:50px;color:#009688}.layui-upload-drag[lay-over]{border-color:#009688}.layui-upload-iframe{position:absolute;width:0;height:0;border:0;visibility:hidden}.layui-upload-wrap{position:relative;vertical-align:middle}.layui-upload-wrap .layui-upload-file{display:block!important;position:absolute;left:0;top:0;z-index:10;font-size:100px;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-tree{line-height:26px}.layui-tree li{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-tree li .layui-tree-spread,.layui-tree li a{display:inline-block;vertical-align:top;height:26px;*display:inline;*zoom:1;cursor:pointer}.layui-tree li a{font-size:0}.layui-tree li a i{font-size:16px}.layui-tree li a cite{padding:0 6px;font-size:14px;font-style:normal}.layui-tree li i{padding-left:6px;color:#333;-moz-user-select:none}.layui-tree li .layui-tree-check{font-size:13px}.layui-tree li .layui-tree-check:hover{color:#009E94}.layui-tree li ul{display:none;margin-left:20px}.layui-tree li .layui-tree-enter{line-height:24px;border:1px dotted #000}.layui-tree-drag{display:none;position:absolute;left:-666px;top:-666px;background-color:#f2f2f2;padding:5px 10px;border:1px dotted #000;white-space:nowrap}.layui-tree-drag i{padding-right:5px}.layui-nav{position:relative;padding:0 20px;background-color:#393D49;color:#fff;border-radius:2px;font-size:0;box-sizing:border-box}.layui-nav *{font-size:14px}.layui-nav .layui-nav-item{position:relative;display:inline-block;*display:inline;*zoom:1;vertical-align:middle;line-height:60px}.layui-nav .layui-nav-item a{display:block;padding:0 20px;color:#fff;color:rgba(255,255,255,.7);transition:all .3s;-webkit-transition:all .3s}.layui-nav .layui-this:after,.layui-nav-bar,.layui-nav-tree .layui-nav-itemed:after{position:absolute;left:0;top:0;width:0;height:5px;background-color:#5FB878;transition:all .2s;-webkit-transition:all .2s}.layui-nav-bar{z-index:1000}.layui-nav .layui-nav-item a:hover,.layui-nav .layui-this a{color:#fff}.layui-nav .layui-this:after{content:'';top:auto;bottom:0;width:100%}.layui-nav-img{width:30px;height:30px;margin-right:10px;border-radius:50%}.layui-nav .layui-nav-more{content:'';width:0;height:0;border-style:solid dashed dashed;border-color:#fff transparent transparent;overflow:hidden;cursor:pointer;transition:all .2s;-webkit-transition:all .2s;position:absolute;top:50%;right:3px;margin-top:-3px;border-width:6px;border-top-color:rgba(255,255,255,.7)}.layui-nav .layui-nav-mored,.layui-nav-itemed>a .layui-nav-more{margin-top:-9px;border-style:dashed dashed solid;border-color:transparent transparent #fff}.layui-nav-child{display:none;position:absolute;left:0;top:65px;min-width:100%;line-height:36px;padding:5px 0;box-shadow:0 2px 4px rgba(0,0,0,.12);border:1px solid #d2d2d2;background-color:#fff;z-index:100;border-radius:2px;white-space:nowrap}.layui-nav .layui-nav-child a{color:#333}.layui-nav .layui-nav-child a:hover{background-color:#f2f2f2;color:#000}.layui-nav-child dd{position:relative}.layui-nav .layui-nav-child dd.layui-this a,.layui-nav-child dd.layui-this{background-color:#5FB878;color:#fff}.layui-nav-child dd.layui-this:after{display:none}.layui-nav-tree{width:200px;padding:0}.layui-nav-tree .layui-nav-item{display:block;width:100%;line-height:45px}.layui-nav-tree .layui-nav-item a{position:relative;height:45px;line-height:45px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-nav-tree .layui-nav-item a:hover{background-color:#4E5465}.layui-nav-tree .layui-nav-bar{width:5px;height:0;background-color:#009688}.layui-nav-tree .layui-nav-child dd.layui-this,.layui-nav-tree .layui-nav-child dd.layui-this a,.layui-nav-tree .layui-this,.layui-nav-tree .layui-this>a,.layui-nav-tree .layui-this>a:hover{background-color:#009688;color:#fff}.layui-nav-tree .layui-this:after{display:none}.layui-nav-itemed>a,.layui-nav-tree .layui-nav-title a,.layui-nav-tree .layui-nav-title a:hover{color:#fff!important}.layui-nav-tree .layui-nav-child{position:relative;z-index:0;top:0;border:none;box-shadow:none}.layui-nav-tree .layui-nav-child a{height:40px;line-height:40px;color:#fff;color:rgba(255,255,255,.7)}.layui-nav-tree .layui-nav-child,.layui-nav-tree .layui-nav-child a:hover{background:0 0;color:#fff}.layui-nav-tree .layui-nav-more{right:10px}.layui-nav-itemed>.layui-nav-child{display:block;padding:0;background-color:rgba(0,0,0,.3)!important}.layui-nav-itemed>.layui-nav-child>.layui-this>.layui-nav-child{display:block}.layui-nav-side{position:fixed;top:0;bottom:0;left:0;overflow-x:hidden;z-index:999}.layui-bg-blue .layui-nav-bar,.layui-bg-blue .layui-nav-itemed:after,.layui-bg-blue .layui-this:after{background-color:#93D1FF}.layui-bg-blue .layui-nav-child dd.layui-this{background-color:#1E9FFF}.layui-bg-blue .layui-nav-itemed>a,.layui-nav-tree.layui-bg-blue .layui-nav-title a,.layui-nav-tree.layui-bg-blue .layui-nav-title a:hover{background-color:#007DDB!important}.layui-breadcrumb{visibility:hidden;font-size:0}.layui-breadcrumb>*{font-size:14px}.layui-breadcrumb a{color:#999!important}.layui-breadcrumb a:hover{color:#5FB878!important}.layui-breadcrumb a cite{color:#666;font-style:normal}.layui-breadcrumb span[lay-separator]{margin:0 10px;color:#999}.layui-tab{margin:10px 0;text-align:left!important}.layui-tab[overflow]>.layui-tab-title{overflow:hidden}.layui-tab-title{position:relative;left:0;height:40px;white-space:nowrap;font-size:0;border-bottom-width:1px;border-bottom-style:solid;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;font-size:14px;transition:all .2s;-webkit-transition:all .2s;position:relative;line-height:40px;min-width:65px;padding:0 15px;text-align:center;cursor:pointer}.layui-tab-title li a{display:block}.layui-tab-title .layui-this{color:#000}.layui-tab-title .layui-this:after{position:absolute;left:0;top:0;content:'';width:100%;height:41px;border-width:1px;border-style:solid;border-bottom-color:#fff;border-radius:2px 2px 0 0;box-sizing:border-box;pointer-events:none}.layui-tab-bar{position:absolute;right:0;top:0;z-index:10;width:30px;height:39px;line-height:39px;border-width:1px;border-style:solid;border-radius:2px;text-align:center;background-color:#fff;cursor:pointer}.layui-tab-bar .layui-icon{position:relative;display:inline-block;top:3px;transition:all .3s;-webkit-transition:all .3s}.layui-tab-item{display:none}.layui-tab-more{padding-right:30px;height:auto!important;white-space:normal!important}.layui-tab-more li.layui-this:after{border-bottom-color:#e2e2e2;border-radius:2px}.layui-tab-more .layui-tab-bar .layui-icon{top:-2px;top:3px\9;-webkit-transform:rotate(180deg);transform:rotate(180deg)}:root .layui-tab-more .layui-tab-bar .layui-icon{top:-2px\0/IE9}.layui-tab-content{padding:10px}.layui-tab-title li .layui-tab-close{position:relative;display:inline-block;width:18px;height:18px;line-height:20px;margin-left:8px;top:1px;text-align:center;font-size:14px;color:#c2c2c2;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li .layui-tab-close:hover{border-radius:2px;background-color:#FF5722;color:#fff}.layui-tab-brief>.layui-tab-title .layui-this{color:#009688}.layui-tab-brief>.layui-tab-more li.layui-this:after,.layui-tab-brief>.layui-tab-title .layui-this:after{border:none;border-radius:0;border-bottom:2px solid #5FB878}.layui-tab-brief[overflow]>.layui-tab-title .layui-this:after{top:-1px}.layui-tab-card{border-width:1px;border-style:solid;border-radius:2px;box-shadow:0 2px 5px 0 rgba(0,0,0,.1)}.layui-tab-card>.layui-tab-title{background-color:#f2f2f2}.layui-tab-card>.layui-tab-title li{margin-right:-1px;margin-left:-1px}.layui-tab-card>.layui-tab-title .layui-this{background-color:#fff}.layui-tab-card>.layui-tab-title .layui-this:after{border-top:none;border-width:1px;border-bottom-color:#fff}.layui-tab-card>.layui-tab-title .layui-tab-bar{height:40px;line-height:40px;border-radius:0;border-top:none;border-right:none}.layui-tab-card>.layui-tab-more .layui-this{background:0 0;color:#5FB878}.layui-tab-card>.layui-tab-more .layui-this:after{border:none}.layui-timeline{padding-left:5px}.layui-timeline-item{position:relative;padding-bottom:20px}.layui-timeline-axis{position:absolute;left:-5px;top:0;z-index:10;width:20px;height:20px;line-height:20px;background-color:#fff;color:#5FB878;border-radius:50%;text-align:center;cursor:pointer}.layui-timeline-axis:hover{color:#FF5722}.layui-timeline-item:before{content:'';position:absolute;left:5px;top:0;z-index:0;width:1px;height:100%}.layui-timeline-item:last-child:before{display:none}.layui-timeline-item:first-child:before{display:block}.layui-timeline-content{padding-left:25px}.layui-timeline-title{position:relative;margin-bottom:10px}.layui-badge,.layui-badge-dot,.layui-badge-rim{position:relative;display:inline-block;padding:0 6px;font-size:12px;text-align:center;background-color:#FF5722;color:#fff;border-radius:2px}.layui-badge{height:18px;line-height:18px}.layui-badge-dot{width:8px;height:8px;padding:0;border-radius:50%}.layui-badge-rim{height:18px;line-height:18px;border-width:1px;border-style:solid;background-color:#fff;color:#666}.layui-btn .layui-badge,.layui-btn .layui-badge-dot{margin-left:5px}.layui-nav .layui-badge,.layui-nav .layui-badge-dot{position:absolute;top:50%;margin:-8px 6px 0}.layui-tab-title .layui-badge,.layui-tab-title .layui-badge-dot{left:5px;top:-2px}.layui-carousel{position:relative;left:0;top:0;background-color:#f8f8f8}.layui-carousel>[carousel-item]{position:relative;width:100%;height:100%;overflow:hidden}.layui-carousel>[carousel-item]:before{position:absolute;content:'\e63d';left:50%;top:50%;width:100px;line-height:20px;margin:-10px 0 0 -50px;text-align:center;color:#c2c2c2;font-family:layui-icon!important;font-size:30px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-carousel>[carousel-item]>*{display:none;position:absolute;left:0;top:0;width:100%;height:100%;background-color:#f8f8f8;transition-duration:.3s;-webkit-transition-duration:.3s}.layui-carousel-updown>*{-webkit-transition:.3s ease-in-out up;transition:.3s ease-in-out up}.layui-carousel-arrow{display:none\9;opacity:0;position:absolute;left:10px;top:50%;margin-top:-18px;width:36px;height:36px;line-height:36px;text-align:center;font-size:20px;border:0;border-radius:50%;background-color:rgba(0,0,0,.2);color:#fff;-webkit-transition-duration:.3s;transition-duration:.3s;cursor:pointer}.layui-carousel-arrow[lay-type=add]{left:auto!important;right:10px}.layui-carousel:hover .layui-carousel-arrow[lay-type=add],.layui-carousel[lay-arrow=always] .layui-carousel-arrow[lay-type=add]{right:20px}.layui-carousel[lay-arrow=always] .layui-carousel-arrow{opacity:1;left:20px}.layui-carousel[lay-arrow=none] .layui-carousel-arrow{display:none}.layui-carousel-arrow:hover,.layui-carousel-ind ul:hover{background-color:rgba(0,0,0,.35)}.layui-carousel:hover .layui-carousel-arrow{display:block\9;opacity:1;left:20px}.layui-carousel-ind{position:relative;top:-35px;width:100%;line-height:0!important;text-align:center;font-size:0}.layui-carousel[lay-indicator=outside]{margin-bottom:30px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind{top:10px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind ul{background-color:rgba(0,0,0,.5)}.layui-carousel[lay-indicator=none] .layui-carousel-ind{display:none}.layui-carousel-ind ul{display:inline-block;padding:5px;background-color:rgba(0,0,0,.2);border-radius:10px;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li{display:inline-block;width:10px;height:10px;margin:0 3px;font-size:14px;background-color:#e2e2e2;background-color:rgba(255,255,255,.5);border-radius:50%;cursor:pointer;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li:hover{background-color:rgba(255,255,255,.7)}.layui-carousel-ind li.layui-this{background-color:#fff}.layui-carousel>[carousel-item]>.layui-carousel-next,.layui-carousel>[carousel-item]>.layui-carousel-prev,.layui-carousel>[carousel-item]>.layui-this{display:block}.layui-carousel>[carousel-item]>.layui-this{left:0}.layui-carousel>[carousel-item]>.layui-carousel-prev{left:-100%}.layui-carousel>[carousel-item]>.layui-carousel-next{left:100%}.layui-carousel>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel>[carousel-item]>.layui-carousel-prev.layui-carousel-right{left:0}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-left{left:-100%}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-right{left:100%}.layui-carousel[lay-anim=updown] .layui-carousel-arrow{left:50%!important;top:20px;margin:0 0 0 -18px}.layui-carousel[lay-anim=updown]>[carousel-item]>*,.layui-carousel[lay-anim=fade]>[carousel-item]>*{left:0!important}.layui-carousel[lay-anim=updown] .layui-carousel-arrow[lay-type=add]{top:auto!important;bottom:20px}.layui-carousel[lay-anim=updown] .layui-carousel-ind{position:absolute;top:50%;right:20px;width:auto;height:auto}.layui-carousel[lay-anim=updown] .layui-carousel-ind ul{padding:3px 5px}.layui-carousel[lay-anim=updown] .layui-carousel-ind li{display:block;margin:6px 0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next{top:100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-left{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-right{top:100%}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev{opacity:0}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{opacity:1}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-right{opacity:0}.layui-fixbar{position:fixed;right:15px;bottom:15px;z-index:999999}.layui-fixbar li{width:50px;height:50px;line-height:50px;margin-bottom:1px;text-align:center;cursor:pointer;font-size:30px;background-color:#9F9F9F;color:#fff;border-radius:2px;opacity:.95}.layui-fixbar li:hover{opacity:.85}.layui-fixbar li:active{opacity:1}.layui-fixbar .layui-fixbar-top{display:none;font-size:40px}body .layui-util-face{border:none;background:0 0}body .layui-util-face .layui-layer-content{padding:0;background-color:#fff;color:#666;box-shadow:none}.layui-util-face .layui-layer-TipsG{display:none}.layui-util-face ul{position:relative;width:372px;padding:10px;border:1px solid #D9D9D9;background-color:#fff;box-shadow:0 0 20px rgba(0,0,0,.2)}.layui-util-face ul li{cursor:pointer;float:left;border:1px solid #e8e8e8;height:22px;width:26px;overflow:hidden;margin:-1px 0 0 -1px;padding:4px 2px;text-align:center}.layui-util-face ul li:hover{position:relative;z-index:2;border:1px solid #eb7350;background:#fff9ec}.layui-code{position:relative;margin:10px 0;padding:15px;line-height:20px;border:1px solid #ddd;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New;font-size:12px}.layui-rate,.layui-rate *{display:inline-block;vertical-align:middle}.layui-rate{padding:10px 5px 10px 0;font-size:0}.layui-rate li i.layui-icon{font-size:20px;color:#FFB800;margin-right:5px;transition:all .3s;-webkit-transition:all .3s}.layui-rate li i:hover{cursor:pointer;transform:scale(1.12);-webkit-transform:scale(1.12)}.layui-rate[readonly] li i:hover{cursor:default;transform:scale(1)}.layui-colorpicker{width:26px;height:26px;border:1px solid #e6e6e6;padding:5px;border-radius:2px;line-height:24px;display:inline-block;cursor:pointer;transition:all .3s;-webkit-transition:all .3s}.layui-colorpicker:hover{border-color:#d2d2d2}.layui-colorpicker.layui-colorpicker-lg{width:34px;height:34px;line-height:32px}.layui-colorpicker.layui-colorpicker-sm{width:24px;height:24px;line-height:22px}.layui-colorpicker.layui-colorpicker-xs{width:22px;height:22px;line-height:20px}.layui-colorpicker-trigger-bgcolor{display:block;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);border-radius:2px}.layui-colorpicker-trigger-span{display:block;height:100%;box-sizing:border-box;border:1px solid rgba(0,0,0,.15);border-radius:2px;text-align:center}.layui-colorpicker-trigger-i{display:inline-block;color:#FFF;font-size:12px}.layui-colorpicker-trigger-i.layui-icon-close{color:#999}.layui-colorpicker-main{position:absolute;z-index:66666666;width:280px;padding:7px;background:#FFF;border:1px solid #d2d2d2;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12)}.layui-colorpicker-main-wrapper{height:180px;position:relative}.layui-colorpicker-basis{width:260px;height:100%;position:relative}.layui-colorpicker-basis-white{width:100%;height:100%;position:absolute;top:0;left:0;background:linear-gradient(90deg,#FFF,hsla(0,0%,100%,0))}.layui-colorpicker-basis-black{width:100%;height:100%;position:absolute;top:0;left:0;background:linear-gradient(0deg,#000,transparent)}.layui-colorpicker-basis-cursor{width:10px;height:10px;border:1px solid #FFF;border-radius:50%;position:absolute;top:-3px;right:-3px;cursor:pointer}.layui-colorpicker-side{position:absolute;top:0;right:0;width:12px;height:100%;background:linear-gradient(red,#FF0,#0F0,#0FF,#00F,#F0F,red)}.layui-colorpicker-side-slider{width:100%;height:5px;box-shadow:0 0 1px #888;box-sizing:border-box;background:#FFF;border-radius:1px;border:1px solid #f0f0f0;cursor:pointer;position:absolute;left:0}.layui-colorpicker-main-alpha{display:none;height:12px;margin-top:7px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.layui-colorpicker-alpha-bgcolor{height:100%;position:relative}.layui-colorpicker-alpha-slider{width:5px;height:100%;box-shadow:0 0 1px #888;box-sizing:border-box;background:#FFF;border-radius:1px;border:1px solid #f0f0f0;cursor:pointer;position:absolute;top:0}.layui-colorpicker-main-pre{padding-top:7px;font-size:0}.layui-colorpicker-pre{width:20px;height:20px;border-radius:2px;display:inline-block;margin-left:6px;margin-bottom:7px;cursor:pointer}.layui-colorpicker-pre:nth-child(11n+1){margin-left:0}.layui-colorpicker-pre-isalpha{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.layui-colorpicker-pre.layui-this{box-shadow:0 0 3px 2px rgba(0,0,0,.15)}.layui-colorpicker-pre>div{height:100%;border-radius:2px}.layui-colorpicker-main-input{text-align:right;padding-top:7px}.layui-colorpicker-main-input .layui-btn-container .layui-btn{margin:0 0 0 10px}.layui-colorpicker-main-input div.layui-inline{float:left;margin-right:10px;font-size:14px}.layui-colorpicker-main-input input.layui-input{width:150px;height:30px;color:#666}.layui-slider{height:4px;background:#e2e2e2;border-radius:3px;position:relative;cursor:pointer}.layui-slider-bar{border-radius:3px;position:absolute;height:100%}.layui-slider-step{position:absolute;top:0;width:4px;height:4px;border-radius:50%;background:#FFF;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.layui-slider-wrap{width:36px;height:36px;position:absolute;top:-16px;-webkit-transform:translateX(-50%);transform:translateX(-50%);z-index:10;text-align:center}.layui-slider-wrap-btn{width:12px;height:12px;border-radius:50%;background:#FFF;display:inline-block;vertical-align:middle;cursor:pointer;transition:.3s}.layui-slider-wrap:after{content:"";height:100%;display:inline-block;vertical-align:middle}.layui-slider-wrap-btn.layui-slider-hover,.layui-slider-wrap-btn:hover{transform:scale(1.2)}.layui-slider-wrap-btn.layui-disabled:hover{transform:scale(1)!important}.layui-slider-tips{position:absolute;top:-42px;z-index:66666666;white-space:nowrap;display:none;-webkit-transform:translateX(-50%);transform:translateX(-50%);color:#FFF;background:#000;border-radius:3px;height:25px;line-height:25px;padding:0 10px}.layui-slider-tips:after{content:'';position:absolute;bottom:-12px;left:50%;margin-left:-6px;width:0;height:0;border-width:6px;border-style:solid;border-color:#000 transparent transparent}.layui-slider-input{width:70px;height:32px;border:1px solid #e6e6e6;border-radius:3px;font-size:16px;line-height:32px;position:absolute;right:0;top:-15px}.layui-slider-input-btn{display:none;position:absolute;top:0;right:0;width:20px;height:100%;border-left:1px solid #d2d2d2}.layui-slider-input-btn i{cursor:pointer;position:absolute;right:0;bottom:0;width:20px;height:50%;font-size:12px;line-height:16px;text-align:center;color:#999}.layui-slider-input-btn i:first-child{top:0;border-bottom:1px solid #d2d2d2}.layui-slider-input-txt{height:100%;font-size:14px}.layui-slider-input-txt input{height:100%;border:none}.layui-slider-input-btn i:hover{color:#009688}.layui-slider-vertical{width:4px;margin-left:34px}.layui-slider-vertical .layui-slider-bar{width:4px}.layui-slider-vertical .layui-slider-step{top:auto;left:0;-webkit-transform:translateY(50%);transform:translateY(50%)}.layui-slider-vertical .layui-slider-wrap{top:auto;left:-16px;-webkit-transform:translateY(50%);transform:translateY(50%)}.layui-slider-vertical .layui-slider-tips{top:auto;left:2px}@media \0screen{.layui-slider-wrap-btn{margin-left:-20px}.layui-slider-vertical .layui-slider-wrap-btn{margin-left:0;margin-bottom:-20px}.layui-slider-vertical .layui-slider-tips{margin-left:-8px}.layui-slider>span{margin-left:8px}}.layui-anim{-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-anim.layui-icon{display:inline-block}.layui-anim-loop{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.layui-trans,.layui-trans a{transition:all .3s;-webkit-transition:all .3s}@-webkit-keyframes layui-rotate{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@keyframes layui-rotate{from{transform:rotate(0)}to{transform:rotate(360deg)}}.layui-anim-rotate{-webkit-animation-name:layui-rotate;animation-name:layui-rotate;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-timing-function:linear;animation-timing-function:linear}@-webkit-keyframes layui-up{from{-webkit-transform:translate3d(0,100%,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-up{from{transform:translate3d(0,100%,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-up{-webkit-animation-name:layui-up;animation-name:layui-up}@-webkit-keyframes layui-upbit{from{-webkit-transform:translate3d(0,30px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-upbit{from{transform:translate3d(0,30px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-upbit{-webkit-animation-name:layui-upbit;animation-name:layui-upbit}@-webkit-keyframes layui-scale{0%{opacity:.3;-webkit-transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale{0%{opacity:.3;-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-ms-transform:scale(1);transform:scale(1)}}.layui-anim-scale{-webkit-animation-name:layui-scale;animation-name:layui-scale}@-webkit-keyframes layui-scale-spring{0%{opacity:.5;-webkit-transform:scale(.5)}80%{opacity:.8;-webkit-transform:scale(1.1)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale-spring{0%{opacity:.5;transform:scale(.5)}80%{opacity:.8;transform:scale(1.1)}100%{opacity:1;transform:scale(1)}}.layui-anim-scaleSpring{-webkit-animation-name:layui-scale-spring;animation-name:layui-scale-spring}@-webkit-keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}@keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}.layui-anim-fadein{-webkit-animation-name:layui-fadein;animation-name:layui-fadein}@-webkit-keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}.layui-anim-fadeout{-webkit-animation-name:layui-fadeout;animation-name:layui-fadeout} \ No newline at end of file diff --git a/static/layui/css/layui.mobile.css b/static/layui/css/layui.mobile.css new file mode 100644 index 0000000..6f7f0a1 --- /dev/null +++ b/static/layui/css/layui.mobile.css @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,legend,li,ol,p,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}html{font:12px 'Helvetica Neue','PingFang SC',STHeitiSC-Light,Helvetica,Arial,sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a,button,input{-webkit-tap-highlight-color:rgba(255,0,0,0)}a{text-decoration:none;background:0 0}a:active,a:hover{outline:0}table{border-collapse:collapse;border-spacing:0}li{list-style:none}b,strong{font-weight:700}h1,h2,h3,h4,h5,h6{font-weight:500}address,cite,dfn,em,var{font-style:normal}dfn{font-style:italic}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}img{border:0;vertical-align:bottom}.layui-inline,input,label{vertical-align:middle}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0;outline:0}button,select{text-transform:none}select{-webkit-appearance:none;border:none}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=1.0.7);src:url(../font/iconfont.eot?v=1.0.7#iefix) format('embedded-opentype'),url(../font/iconfont.woff?v=1.0.7) format('woff'),url(../font/iconfont.ttf?v=1.0.7) format('truetype'),url(../font/iconfont.svg?v=1.0.7#iconfont) format('svg')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-box,.layui-box *{-webkit-box-sizing:content-box!important;-moz-box-sizing:content-box!important;box-sizing:content-box!important}.layui-border-box,.layui-border-box *{-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important}.layui-inline{position:relative;display:inline-block;*display:inline;*zoom:1}.layui-edge,.layui-upload-iframe{position:absolute;width:0;height:0}.layui-edge{border-style:dashed;border-color:transparent;overflow:hidden}.layui-elip{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-unselect{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-disabled,.layui-disabled:active{background-color:#d2d2d2!important;color:#fff!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}.layui-upload-iframe{border:0;visibility:hidden}.layui-upload-enter{border:1px solid #009E94;background-color:#009E94;color:#fff;-webkit-transform:scale(1.1);transform:scale(1.1)}@-webkit-keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}.layui-m-anim-scale{animation-name:layui-m-anim-scale;-webkit-animation-name:layui-m-anim-scale}@-webkit-keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}.layui-m-anim-up{-webkit-animation-name:layui-m-anim-up;animation-name:layui-m-anim-up}@-webkit-keyframes layui-m-anim-left{0%{-webkit-transform:translateX(100%);transform:translateX(100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes layui-m-anim-left{0%{-webkit-transform:translateX(100%);transform:translateX(100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}.layui-m-anim-left{-webkit-animation-name:layui-m-anim-left;animation-name:layui-m-anim-left}@-webkit-keyframes layui-m-anim-right{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes layui-m-anim-right{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}.layui-m-anim-right{-webkit-animation-name:layui-m-anim-right;animation-name:layui-m-anim-right}@-webkit-keyframes layui-m-anim-lout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}}@keyframes layui-m-anim-lout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}}.layui-m-anim-lout{-webkit-animation-name:layui-m-anim-lout;animation-name:layui-m-anim-lout}@-webkit-keyframes layui-m-anim-rout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(100%);transform:translateX(100%)}}@keyframes layui-m-anim-rout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(100%);transform:translateX(100%)}}.layui-m-anim-rout{-webkit-animation-name:layui-m-anim-rout;animation-name:layui-m-anim-rout}.layui-m-layer{position:relative;z-index:19891014}.layui-m-layer *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.layui-m-layermain,.layui-m-layershade{position:fixed;left:0;top:0;width:100%;height:100%}.layui-m-layershade{background-color:rgba(0,0,0,.7);pointer-events:auto}.layui-m-layermain{display:table;font-family:Helvetica,arial,sans-serif;pointer-events:none}.layui-m-layermain .layui-m-layersection{display:table-cell;vertical-align:middle;text-align:center}.layui-m-layerchild{position:relative;display:inline-block;text-align:left;background-color:#fff;font-size:14px;border-radius:5px;box-shadow:0 0 8px rgba(0,0,0,.1);pointer-events:auto;-webkit-overflow-scrolling:touch;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}.layui-m-layer0 .layui-m-layerchild{width:90%;max-width:640px}.layui-m-layer1 .layui-m-layerchild{border:none;border-radius:0}.layui-m-layer2 .layui-m-layerchild{width:auto;max-width:260px;min-width:40px;border:none;background:0 0;box-shadow:none;color:#fff}.layui-m-layerchild h3{padding:0 10px;height:60px;line-height:60px;font-size:16px;font-weight:400;border-radius:5px 5px 0 0;text-align:center}.layui-m-layerbtn span,.layui-m-layerchild h3{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-m-layercont{padding:50px 30px;line-height:22px;text-align:center}.layui-m-layer1 .layui-m-layercont{padding:0;text-align:left}.layui-m-layer2 .layui-m-layercont{text-align:center;padding:0;line-height:0}.layui-m-layer2 .layui-m-layercont i{width:25px;height:25px;margin-left:8px;display:inline-block;background-color:#fff;border-radius:100%;-webkit-animation:layui-m-anim-loading 1.4s infinite ease-in-out;animation:layui-m-anim-loading 1.4s infinite ease-in-out;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-m-layerbtn,.layui-m-layerbtn span{position:relative;text-align:center;border-radius:0 0 5px 5px}.layui-m-layer2 .layui-m-layercont p{margin-top:20px}@-webkit-keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}@keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}.layui-m-layer2 .layui-m-layercont i:first-child{margin-left:0;-webkit-animation-delay:-.32s;animation-delay:-.32s}.layui-m-layer2 .layui-m-layercont i.layui-m-layerload{-webkit-animation-delay:-.16s;animation-delay:-.16s}.layui-m-layer2 .layui-m-layercont>div{line-height:22px;padding-top:7px;margin-bottom:20px;font-size:14px}.layui-m-layerbtn{display:box;display:-moz-box;display:-webkit-box;width:100%;height:50px;line-height:50px;font-size:0;border-top:1px solid #D0D0D0;background-color:#F2F2F2}.layui-m-layerbtn span{display:block;-moz-box-flex:1;box-flex:1;-webkit-box-flex:1;font-size:14px;cursor:pointer}.layui-m-layerbtn span[yes]{color:#40AFFE}.layui-m-layerbtn span[no]{border-right:1px solid #D0D0D0;border-radius:0 0 0 5px}.layui-m-layerbtn span:active{background-color:#F6F6F6}.layui-m-layerend{position:absolute;right:7px;top:10px;width:30px;height:30px;border:0;font-weight:400;background:0 0;cursor:pointer;-webkit-appearance:none;font-size:30px}.layui-m-layerend::after,.layui-m-layerend::before{position:absolute;left:5px;top:15px;content:'';width:18px;height:1px;background-color:#999;transform:rotate(45deg);-webkit-transform:rotate(45deg);border-radius:3px}.layui-m-layerend::after{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}body .layui-m-layer .layui-m-layer-footer{position:fixed;width:95%;max-width:100%;margin:0 auto;left:0;right:0;bottom:10px;background:0 0}.layui-m-layer-footer .layui-m-layercont{padding:20px;border-radius:5px 5px 0 0;background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn{display:block;height:auto;background:0 0;border-top:none}.layui-m-layer-footer .layui-m-layerbtn span{background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn span[no]{color:#FD482C;border-top:1px solid #c2c2c2;border-radius:0 0 5px 5px}.layui-m-layer-footer .layui-m-layerbtn span[yes]{margin-top:10px;border-radius:5px}body .layui-m-layer .layui-m-layer-msg{width:auto;max-width:90%;margin:0 auto;bottom:-150px;background-color:rgba(0,0,0,.7);color:#fff}.layui-m-layer-msg .layui-m-layercont{padding:10px 20px} \ No newline at end of file diff --git a/static/layui/css/modules/code.css b/static/layui/css/modules/code.css new file mode 100644 index 0000000..d0d3822 --- /dev/null +++ b/static/layui/css/modules/code.css @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #e2e2e2;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:32px;line-height:32px;border-bottom:1px solid #e2e2e2}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 5px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none} \ No newline at end of file diff --git a/static/layui/css/modules/laydate/default/laydate.css b/static/layui/css/modules/laydate/default/laydate.css new file mode 100644 index 0000000..f7e690e --- /dev/null +++ b/static/layui/css/modules/laydate/default/laydate.css @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + .laydate-set-ym,.layui-laydate,.layui-laydate *,.layui-laydate-list{box-sizing:border-box}html #layuicss-laydate{display:none;position:absolute;width:1989px}.layui-laydate *{margin:0;padding:0}.layui-laydate{position:absolute;z-index:66666666;margin:5px 0;border-radius:2px;font-size:14px;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:laydate-upbit;animation-name:laydate-upbit}.layui-laydate-main{width:272px}.layui-laydate-content td,.layui-laydate-header *,.layui-laydate-list li{transition-duration:.3s;-webkit-transition-duration:.3s}@-webkit-keyframes laydate-upbit{from{-webkit-transform:translate3d(0,20px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes laydate-upbit{from{transform:translate3d(0,20px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-laydate-static{position:relative;z-index:0;display:inline-block;margin:0;-webkit-animation:none;animation:none}.laydate-ym-show .laydate-next-m,.laydate-ym-show .laydate-prev-m{display:none!important}.laydate-ym-show .laydate-next-y,.laydate-ym-show .laydate-prev-y{display:inline-block!important}.laydate-time-show .laydate-set-ym span[lay-type=month],.laydate-time-show .laydate-set-ym span[lay-type=year],.laydate-time-show .layui-laydate-header .layui-icon,.laydate-ym-show .laydate-set-ym span[lay-type=month]{display:none!important}.layui-laydate-header{position:relative;line-height:30px;padding:10px 70px 5px}.laydate-set-ym span,.layui-laydate-header i{padding:0 5px;cursor:pointer}.layui-laydate-header *{display:inline-block;vertical-align:bottom}.layui-laydate-header i{position:absolute;top:10px;color:#999;font-size:18px}.layui-laydate-header i.laydate-prev-y{left:15px}.layui-laydate-header i.laydate-prev-m{left:45px}.layui-laydate-header i.laydate-next-y{right:15px}.layui-laydate-header i.laydate-next-m{right:45px}.laydate-set-ym{width:100%;text-align:center;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.laydate-time-text{cursor:default!important}.layui-laydate-content{position:relative;padding:10px;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-laydate-content table{border-collapse:collapse;border-spacing:0}.layui-laydate-content td,.layui-laydate-content th{width:36px;height:30px;padding:5px;text-align:center}.layui-laydate-content td{position:relative;cursor:pointer}.laydate-day-mark{position:absolute;left:0;top:0;width:100%;height:100%;line-height:30px;font-size:12px;overflow:hidden}.laydate-day-mark::after{position:absolute;content:'';right:2px;top:2px;width:5px;height:5px;border-radius:50%}.layui-laydate-footer{position:relative;height:46px;line-height:26px;padding:10px 20px}.layui-laydate-footer span{margin-right:15px;display:inline-block;cursor:pointer;font-size:12px}.layui-laydate-footer span:hover{color:#5FB878}.laydate-footer-btns{position:absolute;right:10px;top:10px}.laydate-footer-btns span{height:26px;line-height:26px;margin:0 0 0 -1px;padding:0 10px;border:1px solid #C9C9C9;background-color:#fff;white-space:nowrap;vertical-align:top;border-radius:2px}.layui-laydate-list>li,.layui-laydate-range .layui-laydate-main{display:inline-block;vertical-align:middle}.layui-laydate-list{position:absolute;left:0;top:0;width:100%;height:100%;padding:10px;background-color:#fff}.layui-laydate-list>li{position:relative;width:33.3%;height:36px;line-height:36px;margin:3px 0;text-align:center;cursor:pointer}.laydate-month-list>li{width:25%;margin:17px 0}.laydate-time-list>li{height:100%;margin:0;line-height:normal;cursor:default}.laydate-time-list p{position:relative;top:-4px;line-height:29px}.laydate-time-list ol{height:181px;overflow:hidden}.laydate-time-list>li:hover ol{overflow-y:auto}.laydate-time-list ol li{width:130%;padding-left:33px;line-height:30px;text-align:left;cursor:pointer}.layui-laydate-hint{position:absolute;top:115px;left:50%;width:250px;margin-left:-125px;line-height:20px;padding:15px;text-align:center;font-size:12px}.layui-laydate-range{width:546px}.layui-laydate-range .laydate-main-list-0 .laydate-next-m,.layui-laydate-range .laydate-main-list-0 .laydate-next-y,.layui-laydate-range .laydate-main-list-1 .laydate-prev-m,.layui-laydate-range .laydate-main-list-1 .laydate-prev-y{display:none}.layui-laydate-range .laydate-main-list-1 .layui-laydate-content{border-left:1px solid #e2e2e2}.layui-laydate,.layui-laydate-hint{border:1px solid #d2d2d2;box-shadow:0 2px 4px rgba(0,0,0,.12);background-color:#fff;color:#666}.layui-laydate-header{border-bottom:1px solid #e2e2e2}.layui-laydate-header i:hover,.layui-laydate-header span:hover{color:#5FB878}.layui-laydate-content{border-top:none 0;border-bottom:none 0}.layui-laydate-content th{font-weight:400;color:#333}.layui-laydate-content td{color:#666}.layui-laydate-content td.laydate-selected{background-color:#00F7DE}.laydate-selected:hover{background-color:#00F7DE!important}.layui-laydate-content td:hover,.layui-laydate-list li:hover{background-color:#eaeaea;color:#333}.laydate-time-list li ol{margin:0;padding:0;border:1px solid #e2e2e2;border-left-width:0}.laydate-time-list li:first-child ol{border-left-width:1px}.laydate-time-list>li:hover{background:0 0}.layui-laydate-content .laydate-day-next,.layui-laydate-content .laydate-day-prev{color:#d2d2d2}.laydate-selected.laydate-day-next,.laydate-selected.laydate-day-prev{background-color:#f8f8f8!important}.layui-laydate-footer{border-top:1px solid #e2e2e2}.layui-laydate-hint{color:#FF5722}.laydate-day-mark::after{background-color:#5FB878}.layui-laydate-content td.layui-this .laydate-day-mark::after{display:none}.layui-laydate-footer span[lay-type=date]{color:#5FB878}.layui-laydate .layui-this{background-color:#009688!important;color:#fff!important}.layui-laydate .laydate-disabled,.layui-laydate .laydate-disabled:hover{background:0 0!important;color:#d2d2d2!important;cursor:not-allowed!important;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.laydate-theme-molv{border:none}.laydate-theme-molv.layui-laydate-range{width:548px}.laydate-theme-molv .layui-laydate-main{width:274px}.laydate-theme-molv .layui-laydate-header{border:none;background-color:#009688}.laydate-theme-molv .layui-laydate-header i,.laydate-theme-molv .layui-laydate-header span{color:#f6f6f6}.laydate-theme-molv .layui-laydate-header i:hover,.laydate-theme-molv .layui-laydate-header span:hover{color:#fff}.laydate-theme-molv .layui-laydate-content{border:1px solid #e2e2e2;border-top:none;border-bottom:none}.laydate-theme-molv .laydate-main-list-1 .layui-laydate-content{border-left:none}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li,.laydate-theme-grid .layui-laydate-content td,.laydate-theme-grid .layui-laydate-content thead,.laydate-theme-molv .layui-laydate-footer{border:1px solid #e2e2e2}.laydate-theme-grid .laydate-selected,.laydate-theme-grid .laydate-selected:hover{background-color:#f2f2f2!important;color:#009688!important}.laydate-theme-grid .laydate-selected.laydate-day-next,.laydate-theme-grid .laydate-selected.laydate-day-prev{color:#d2d2d2!important}.laydate-theme-grid .laydate-month-list,.laydate-theme-grid .laydate-year-list{margin:1px 0 0 1px}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li{margin:0 -1px -1px 0}.laydate-theme-grid .laydate-year-list>li{height:43px;line-height:43px}.laydate-theme-grid .laydate-month-list>li{height:71px;line-height:71px} \ No newline at end of file diff --git a/static/layui/css/modules/layer/default/icon-ext.png b/static/layui/css/modules/layer/default/icon-ext.png new file mode 100644 index 0000000000000000000000000000000000000000..bbbb669bb311514baa5db3a6a00b4644d0e280f1 GIT binary patch literal 5911 zcmY+I2Q(bf_s2JgAUe^aMOKL(VwGqSy<0@0i{8cRqDzD%ST(B#i!4FHDp8XlI?-*k z=$*)lUVhK-{LcTJ|C}>3XXea%^WJ^;-tXtWSbbeJ3NjWl2n0f*p{@)EcPu#VNQl8z z1kb_-ZbS$r4I>h8JSVYx1)fR0)Sn&qHr}8y{y+4^AUz zcYBDagvi~yB6shN>mfA37p#|G7`9y&Ggi_)mcoDUevwZ%`QQ+u`Spkp9gx zTYuuo_8p5IL4SGDE=2#lxUGErKvu^NZ*;4Tj}QBeHs#sycwNE47h{3wpZ|9emH((u z9sRflNhSr++WU1KOOW>%Hbg-aK-&p%Q&ht?^+2LRNG+S62f~|#IHbK7^Ddkcx)J1Q z0S7-})`HegD(zyqd3ie^Xb3L+7UdQyoXc9w+U)bw_5iL6R1v||XHI%*wrz$^Hxo(q z4GqONss`jwc1leu&Ie}C_iF{Y#ELuWnzl6x0$Yn+EWq{3{85roZ0UUaYXG0b)L=y?`*9JA#80I z3P(##E(C&bEKxAud)k68*!7p?g7>p#8~i=*Q(G^3Q}7`S4GptXIHeC{8;MWMNzpPwJM({dpXnId*kn{Y5EiD@N@df+QF z=ydO?XqznoUo&{Dudh#pk{Zx!=;*Y&!4i%`+VW%iA)5@ZRhS}sZ!`B~ge$$|!57kC z871jaeGcN{4!xWL0L6rzKKTQ{CGhEnft!6{hpBOL@H)dt#qvkFpkh)jIe7!-rRUdp>qgmJfFq zu+`PvIwEDAvWR8v{he98pdc9`A)$|^)nqNRdM+;OA7%#BqsQ#odE$E4*4F56+(4$K zsq)ctF_F`f6JI+gX1PU8^4qTgCGJRhvcGj(PEM?EXEz`bdS^_aKk8|n(uNonokkJ~ zag?3Cy}{$huW)WWtdtA*BPsuF*6i$TQs!XF8--%I1#}uhDYUHLC5;re$(42JWcdZfurd&*Jj(-wE3U z8p;?N6=YEnPf2Mh(w;fF3mu3Gk>_Afh;hsbd^z3VUpfT4cTeBcw1gC8&%6JByc1M_PomP9JdP7ad#I|Ex0?^gtOKU zS}xQ|ue9x;{3qE}?K*yG^rj{Yaj}ONmn%l7{4PRP*70t&`|8*tWxo=;xaG7+xv%q#ha*J2qI9~PFF+Y+mbgD ziF_c%s!C1d;_7;|oarfw($1iLFOrgTw4!h!ZC2}HY+qhlT7bpU=MJQQ!hAVj-Qaa4 ztn-@to@J1PBefH;Y?PA2+51Vcg88_?ZdMB3?h#8Dw#WxwQZV?AUM#rDa>_%p<#@Cr zV5@q3qN+M?E-Q5(z`GHQiIYXd@6&1Q{x96RE4Gcd^@@Dp0H{!lq1#bD?~a_Dm*Q zij@+o@!eV!xX}0P`~K7_22})mJWS+b4!ulcRWin!Wt8cVpc;Hqr*d2DTvsfl4fCH8L@O* z?nN!Gtd!cil@-W#fZt&-m@Ayz+%L8!Ypb3gd4tultdRWXkCO}`6}r;*rhLQ~`gtUh z^TTT>n8{S#Gs38Eic+i&zp&2q3=9N&QrY<`$_8z7Ucd220cZclG3DjNTmvSSmb%ZL z-Sw!=EH5u7nq6yM^W@bgu~@%V;3it{vqlSY`a^mZyC)7qXbs>g$_68iBg9c4k?3+# z|2}BBkXz}`Hr#-D&h+936cRcX2GJvg?ps5J?8M#X_*4Oty5~n?k(``8VmKU5(7cYi zbToq=exH@{G*rQ?#%-=Gmd<6mNGCI3x1CYq&OhsY{&hGNVRBb=m)-nEMa%N{7uQP~ zQ7BYzu0rm}h!H^qq>{Dt5A?Gdb0|sV*Lb%3LFyK8`1cay(mw&R0kS!v%{{AP6MePy zBdv;0=9_&t7)D1&qm^!bpA*$BPJVHnao$H}ltSB71!x2*{M8g?;F&95F1&b`Cm7%Owcs1q(qa=-&BynT$mBqLgRMzppzZQ zGpuq!MrCHzE;oR~WvpUi5Ho7&K}>wXxs#KV(!T5TKo&?M!v~$vK&S2)7Jc9~!^Vl7 zQmY`@?)!NycG6UPEOn>4O?eCu9p8-9HGN1`1B_(zKJM591)}l1I*9%D>vpSF`}YH6*luWP;=xh;*vXvvYM3cw6r2N6?VyfqweC zfh_5V4<8az<7zNVGhgm&>XoUV4XSZqd|M9NMLIh>)jO-&=6f53|B33O8Hgg**Ijh8 zW!k%vdDm7~)#K!b|0u4fq|ncV99U4Y%Xa$DhIDjrglU_ZnJMWmwegd*d7;^zi7xUq zv+sZ3pO37BAa-Wtp37Uoi89vWIY~f15M;O>L&^4Zy55&n$_rA3%NkK?~ zLzzoi1qd~pLeGvJ^V2ivO?my=3hu9(tjEVw+AqtcWk#K();BkwpRA_GT6GV_3hV}* z=%f4p8|`IfWfA}qbC&T(k%fhYR%!}#uUQ4AF@%4Dnhd=`@Bw_d##&9OY5} zR9^HdO;zWY_f6W76RDI=7RVIyX#8^5m?u|dpj78Jds8)n1 z2Yq>*5YkWp&Gx5WYfnYv3z`{DKb)3?8s*r2+LP$9A^t%)24vIF(lRIZ)dWtKT6T<{ zT0?B-6;F08jfRqyGBmCwzCV1Adygr+KrKO6I_&&(9=|dmat>q&BlyaWCKxjuL3(s_ zw10B2bFtP+rEuyR9DEYtah>aE6}~|p*&MA4GWho-ZY>8AgV4XpxxI&{_<>@z4O<~! z;;+piCu#A_;tpitt#j`JE_v7&&LVq>^sr#*uU^?>CKPT1Su>Q9`dg0>cwn_8G04XC= z&i-1sT32C@kxV;iDb-}V`QrSfx~b3-=;a=h)->roY)#Eeb72#EK)@CU-Isqkm8Tg@?m5|+yDr&~&N`L+;d>8ic!Cez8F!MA3&2Do0)UCg>? zsdO6Tl910D8zAxP*g076k+}?dkZM3wglA=Cp^-tK^1c$M)R&a-^9D(~z+3i)wCEx( zly1YX0R;|K$kQh&9_~6l!fWX1je|jKgJcBNaM?`k?Y$)AfsaqBRyQ}be;xj8V%A^3 zdY$1k09z>U^;@y<5gG~;%Dy6lV#=zvhOv&M?DRSlb$4w{O4YL163^TSdF?3{td2j`{98*`gzmLzKc1Ek8 zgM)d*Nq6}8tbr$hR2Xi0zRqwY^amgL%V6=Mv4Y+bRCkc=tLp{0nUX*w;*Ge3hFUWepyi@hQ*CCmG zKg>Lv+8YD$K%6p?gP?g|vBJJrNRv!szktd`I^-CeL3-V~KTBHnXfYY6RNsKH09;a1 z693D!;@Qc*J4AwfVpvb%?c~;v6+HK$E{EulzBQp{2pFhA>hbSyQNdWQYMh&DnmsUb z84oR4OzYy}Vq$uFF%Ruf{fJ*fHXvn~$5f}}>~lip843U~kFie3qM-H1(F7YN>%cz^ zQh&Kr7rCmq1SBE~i;7+z9|uXuwPT%!-${D1=kvKV7lTyn)F(u z|Bhvv;FEk*j?AOHuRfTQ2VGo~a!7rE8}n_kV2!A%a37DZGO4TsSMTobK3p%Y2=Bb# zT5i#BxTY5t*Rh~cH}aYMD$EF@#^U7g0Y1QH6MS1K_KnZKb>sE*b!wsrFDdOuj~GBI zF`*;njv6`GnO*U3Ibj182QgP`=_LcX;VPrG*fuULGA%^^?l!Uee&TV%PIJT0CO9%^ zcfNg1IX*$!_UG~^gQW3UK!Dd7g*i27D+QC0$Zz>7uP;$B-4s>4AJmkRnrdLe_=E+> zs{3ROx2&|ItWw0k#QKA4%YB)}ZN0CI`9zJ^kMJuy&K@4;{s)=>V=Ny%s^JSlF&DsM z-X^Jk$jiG_u|`XgNY>WVzQ~&Yfo0Xhk%7l*O zL`+veGywua{JNb>@JS`K!M|{P!`L#$wwf}F);$@pldcY+-Df*g_h2x7n&f-P;c;tG z&Nwa|9UUwd3p5>+&c(yA!)qfxRAuiM@A@=MpYGSTEd6+UQ&D-{cVi60+^m}U_! zdvLnEuPNsIh~-`zK>X@S(SuHl`&*OuBqX?Xh~P^qez;0|?RTONgf9N}hyZ$kINu40YZOS$tn2wQJX^7$k4DA;4ji%`qluAKwb<#ej4=0in_3s zRmcF_LB4M0j~{oUHIj``o>O%XEG)7!!c;c+)+R&GHms^ZTvs>N*Jl96qa`64aeGpr zBN*LJCWF01G{;y322+FzG_WL~^x6j>KjAX0HC9n~~2pkZca2HkLym^VL1 zUBc0tT_}LtJ9q9F^yp9%)wX|B7yzhcq1yJgo*E`Uk z_r{ozHjg13O8PfI*2mZPv&$$ypw!~DT&ZV~0Q{Vk9GIH_+q`qrN9NfVb97-LW?>aX z%kad+2jN&(HkIW|paoF+VW}g5!x2zABqNdeB`;PO58=aEcf_-4fy$mi%Z{RJ=K!eM zLoF?>q0UXe2C$6tsV0^-qb0^JM}TZ6s$J9TSJ-Najxu514T!?RG!kbk4>Vqt(|H)mToz#peQ#y6|Tp}<1aBrlW#nk?aP zxRaC9Zy4f*msc+bDkP*c zt&&cDoo5<=IM`F#-RzqQgC<_9Kl9Lu%*PBeZwFJExsI+T!yQ(co4 z*NNxQl&YkNJ{{IxohMt4Xj2wBt&54T| zEcW>k&M}v52(;l3DO6>670t4m?eP8DsiK?xBPK#weB$4C-5+@?#$mgfmK;1u@!!8i z4dX)J+d|(`DBko+QYSX!UOQz|4K>nQxuBui%JcO}N?pvg9U5GFDU9vE{o?;$+ApsB YZmOLxGt_1UThtH@6k?11;06>$MlhS}5=b&FE!8cRn$r(cw*CLxiM=BH4${Ax7y;K}kc|Yw?S8cxq>*aTjQ8v6{l9 zH@7H!N#68nTt6@Ke%^biXL^`i@jn0X<)XWz$A8Lq$~~VEnG#-}VqFJzNf^EZy%>C= zMyiaYN(V?`C+9Cg<@d(R?s~NOh)Eo9=rHo+pjFHxhYXrg^73Z%^+_lwD9|%9Qd3i#YxykZI|Z~vLdZp9dfJo4{E6+H zF#v8l=-CkIBL%vW9G!&UW-M+~AocB*r{|SjyFIaBPFZ1V{{8cEP2_y%-%EWo{d{Bb zIG`27vEtj&PbVCyOJ8WQQ|z3@Z2eHm9*q|AOTLhn=4vLi-pVvvwozD5%Rv^X)R&#D zHDz&f1ap3R-j!NtejVLjdeOLGqBl?Hf9~@6u{4i*wh`TChcR|sp61YuGtR~Ylmhpa z*|28&7zZ;!n`0mKzF~Q?i9k9Kc9B?vYgx?nazH;7eI3-XHR5u7=;W`I6|woD+IX zlV2>vWkhg~SJMcY_iWH^>5a36RP2nrsz~zA&Kl=t$Q{@ZEccpPZ9d=QPs=6aV!}?h zdP4%PbYGO|X7PR$GS-XnS|Wg>Ep4t*lIA(pjL>28Na-tbt_mFf1UKWA)qVgNt$vCd zclrZ*kxA09#G@w-9@uImTl7R)<$~ik|B`+CVWj+HX)_0nBf7+~I4W0BhdnZ=N{v)d zeFLrcG*<+}s8_%F!+k|iUU*?uRg9|WYg%h7&-KmC7e>aC($X*}oSJ_9V$V_nZ8)8I z3F=h;fMPB?JNxJiwKYjvTH4TS)shL=0QjFIQsPM~R<@Qu{JB?PeC!?g z`0?LRSgT!q-rM_T*z-B>jB>sV7+3cz(1$j=YhakTiS*$?5<%ntP)PFUR5FVlu!@Z8iJWo#ozHZdhwx z5MuDrOHzfP7u&K{pX2JyqsE;f%N$)R%Bs`J>U7RsD2W#$c_s#);iUI|_^yKdq>QAVh{H@LGf_q?EJd3oxYoh(YbOawAerPz0_A zMR`+*CXc^7Z}D)uaR^RmQDYbme{v5pn&G1OCe_sZl;$(fEl@YYVCt)aB~sM1H9NVv ziJl81nqhU2TsJ$|tm%Ia;^_`M>}JmV?Sgacy%GAg7kA`fWthRyL9^JfU1QeM@2*z&1n&>irCh-+N(t--^jFyZ2gW1TAo%{WL@L4?4XQW+ zS4li@%6{Q&krye&OglNvx7H)O2yapNt5nTMpQ3ZVM3vu}bmhhh;wd^bWKEt3P6WE& zRhHBimj^e0tAx?G8ab(Zm@~oGEgPGe4!=_d?r)R^`=YrWJjT~rxC=!1q9irzztAOa ziw1qdBw%1on0>{3n0^TpTShrz_4^b!iX+!?Lu@YxcHmm&r5F`hcw^8SHco=it~rhB zn38C4T;sXB+?sB(90xXe@u8mNWfeMl!K3#(zERwQ1FWSI+$2ka3id7 z?mQeBR_;P6hsoE8(z44*qe+(SdPAk~3Q>X+6?r85Z`jCxOcH+30daI z?fo?T-%uINCKCKO&2^=vK)Q95^}LW?!l$S(AyTh`TH{)SwkC&Fj=J`P?1f=&2#_|q zsp)TFPh(>;)ChBMaL}``B+wrQT{0U68z=79`LowQog5h+uDU0|KiHxFb>{n?n-}VI zG7)7q?R~io`E_|c@^I=4y6VW>&BmIga+Q9vfNvQ0&7FSA8C|wyo7RFw{V3nU`*-b~ z4?M?e2D(*Gc?H#3yF*9=u(x{YW>tQZeqnyYkk>^_>y|JEK| zcY~ZJS@)xVW*A>FbAM87LOH(mU%5OwsHra)Tn*pDX!TGywS6)P{MI~iT)oWGDoAmb*dC6oqE)-fc z9aDYYcxCQz3d=W@f#ehc=W@21NqZ|Bbjm~6Y69v;&scB2Y?xw`J$hY@Wn~c!+MF!! z&Q@!HjZ{TZ>rT7|tq$9-_gfW3MKHfsm7JUc?t^S?zr4P_=JLBEMD~l@+S-E3H1)Gx zUVVjaQR)Y-|2?xBN=X(%DH?b=_FW3jE|HlJjeVuWhM?j4VbvSNUY#-=@bnB43gp;rJ{|!m%o|YH&-~aL4;Q73l$6kY#B|#<)G}~Pvd7F3$exko zz`)B${AeE-%vyjhKuq%&5r?szhBFaLB60+#+J7P5UBK%NJ%r~_1sS$1CqRA9QSdG< z?74NywQn`X%saRM;t*UQ=6(40SRvENINIr&3(6lt4MWu&a>V8enSGL^micFX5l(Nr z1t@PxH@+diuZsQ^ZbyJtzy_}E0_BfBzW`RrA1v+6K9jR!Cr8LNQrloNK@)t zg4ffQPx!aIHOv@MyPlJy{?`ku^-CuiPyR?8^WU%IN99ukTRuV~+)-_3h{?%%oKB;a zF-YH;=i5-~EbD%T5#19)i5k2Zo)e?OP3O_)jhI|vPI>M_#8nQgjZy;`wd^fvP;KtN zj@9+miK_4N6Dp!TjiYJ{9cSx0uP|*o&gLd2SLH06`ao?qZbK5|~@(H&%pJXSB=tJ^U1}L2ZCf z^<5#@v3GPSf6~TXmomp{xK{UEbV98E9I1>IB)$|%;*pMYNr-TwTj+OU4pxZZXl}0# zDI^HLWI~S&?dT9Vn8-@?*tG7CKr{4Q)DAc`*xHF8cKUnKd3hi!`h?Ze(z38Td;mW( zI{I}gmmjdOxVY`Yr{>>5xbp1kvT40jNkg0qI3iio?I&nZVaX zhx6*#m3pKf&ILi?u88mXxuIKM9~-x3YC%+EN(+Z>26q7f=i zz8zO#o*NyM8$+2te2xFgs{LSnRSgqg&uB{#-&u2G(}5(>lfUhK$Kze2JO1khL^Jd2 zRPoYk|CBM~?+zk0SOPp_!oWC7O*X?;0)WZCpxkK@Tur6l9d^0X!r@SJP&#kkb>MgF z&Kgah>b2iu6RV)6!n<8vP5E~Pxi68&+p^Cc((=YBjvp8I`xZa*fcI;5@JAyEbqIVF zdGKk^K+E#MM!ZAzH?WD~pT^Yk^3Sl}0Jylg$i9i>qAC=arjOVASZm6kaiHAk>sqTL z^7-Lpj%-kn$ocs>7dJN)6sR!a&4aqbBGcJ$P-E^3+sg7ncjDT8OSkam&Ra7Fjys(` zMa~rtg+A-e^r^ajPRR+o@#}|Xd}S$HrvY96OyN2isH@IsI+Ssb0i1St&5>Wh{zdii zsk-Tp;y{Bt?{Zj+RB+Kbg2q~x|DQl`W$7q>Opzzzy<-#1i)$DP- z)uXXc-um}Fb}e08x1qU#8>uS%#eal=>@&-w&qCiz3qnd+WlXM7EX=Qpa9l284Z=$q zrKW&HHB;Ksii#+fmX`8|(H)(g?8C0l`1ts{UY$5#1E%zboB!z1JEY{udOB8c3Dv`! z^5uJBZtI|*xWpH7w z-KOyDbb74>0gU1tA3IQ?*I>SzrD_|Hy1l(*(g}i*AeF5Gc7{B776bXWLVu4AGCNsq z1G($SF6y4?NfwjpW+6(CW^Ya}X;E&J`9v9LWo5`4X%9t;ZeHl={$v#A*R+2MGxLKH z%4%`9W{Gl%^JQ!uW#*3AH++Z>1mDr+$=6^ochvQ>)i$_o=J08R$ct_%0yY5Z-*FT$ zBpT_OF(?O0I_w+tYtz!wN-eyLkRZTXotMdYb&QOnnd$skr@4hI@BV$onZ3MIKRdeg zlvbC~_E&t92(L^;t}x6*XmnnZ|7(IbV~DBZhsE1(Q~nR3jqcJ<4rDAZ5>i>mwjW*s zx0@P#(ygb=q^-^{YcQvwcxkRGL00ziUB@9)4)f8H#i3(HXNZW8hi{m7+OGqj$ITP_ zl(-SjD>c%E+8YY52kudyKDc~DN>AF^`J*TbEb0-V_j4To9Z8M1XP$KlGVGa`?^gG zQ$CCX^T#)ZcX!33sva+KBC}ak&I~hu?b!}jKz--4+fQHK+1Msx7ANmKGg)CYg?x|8{Y;{u53cE zLa^9&L;p|}_`Hkb*=}sImu~oLMZ7lm8o$WOzww0=JyCmP#+%)((th8)+l6$P5&m5^ z!w$^pi*rmQ`03tU74W`dQru{U1L|RNGj-0auJG^`cOdJFQO>>Pt)(iZEzY}fzpqmH zz%Y1obE-9&wt%0uUDpG&^O`4Llvd_<8@lf{IrkLIbr3B80+ z3VT67cQV-^aigg6(v>MhDTsIXqf+)?iU#o4-3w|#zI|7Xt*!ABFzHt;OB>G`MpY&% ziiy;EUMg$Lq7D+|@yNf`)#brA)nBn-DusCr>tC}%xP1wNqGYGWB&-Kt+%&LYzLLpjBo3O%pU@}KMEl+xgug?5#eeMZZ*M5pTIz@L2p=Xq6sTNQ zHJ{b+VnaDVZM~mV-(sqZU2q9KORaAy{J}YfIfYl+Jgb+Rj?_mO_g_V(*;L`^u0<|O zhyQIn@;nmKk6@dXSXnfek*~Y0*%&U2AL$UJEoP5=tPXZS8|_6l*YK>jpWG3$`>3Gu z&Pe*eH_&hDNLLZTqn#yUHkMA9#ns_Ib>}{!8*o9Q>Ha<8I$0LHyYn?!6%}+km0Y=3AWWz5 zL*c~aq%`O6D6wI^y|@L~e99GWO(PYxPcz3!oE)idDu8bZe@-EU zZlq(U5&l3W!DhD$CfK`@5#Pp~Q=r^?#CcZ~+}+BA;rhB)h;>TS(gqq4ZXI735S-`(JQw*2UNO>Ib&~cA z&9_@wsS$+!-g7oM<8Mk9Q0Bj4aQudxgUmiMqc5bVQRW0xUVtkJKw+3;?bF{D3NESy zL40aF+8RJ$)S_K{%s0ib)4I+CG-4jMz^B_ZM~b7`(877~NW`*7EiF{Tg+_sAf|Xx5 zCjVg4H0jj*{V^pdMmerQU4K(z!xd+ydr;+x{b%aA3Sh-_1+v_B;i0P2HUX&UKgM6Q zOK*RZF4Nw-Tg3Wz+naO^Xp`UPnU$>4E}-h7U%Ji*qnFA{-g0BA@WU7iY^Yw$G%`^# zHVcIixcKt~xBB^iGp z3@t5fb8~ZClsCB97AlOotvR;EkFX4AYG{0S()V2v$3dwYkMYja~K%b>bjg5E{lkmt*IRvYMM zkDPx`B|zB_hPD1KU?|4CXa9Z)<00tuvx7pgR2Js+;DJUnl)uo&=U~+>rO{a$P3NB? zWa|XQGuB`}#3CsBPT+HGN!>%7i25SUqvez#$UimFG}+EiH;B`Z8sT@{-8U5LHx z30FMSuqs?xDRPaaj()s1WCuHD`eQI$Rddg;EG-Sxy0W3D!sD7jJ8nP5pM?tw5aBLtT>Ezo~F{N9z31aC$`tOwx&-_siZR-;He}OGw1aExggDS?qn>kae!^ocJ;%-cARcbAHYopeQTFY z^t;gqb_t*}c{tr*pgzCZCN+y7v7ib&D|LooBp>Y@9!uGKtu8dspoZ1`hl_8n_w1-; zz*~OW^GQ>Razt;nG}sc&&5106|7LcQ4?n7^nTPXgRQ71BJgL>i)~A!_UggEQK+Ka| zo&ZF2AeR%9rUUye$U@WOY{jMWf||ZHe&qOO=3tX>(^yY$RF*tYN>)~O;?LqX#FJVY zDxWMaE`GwrRoC;q@K!KzLwl`%{jh)gGwP4_YYqS2%dPH+9>0wu9bK&n=WnF(z^z`; z*;ABX+I$7UN{etNmFXz|?0SlD!IoZ|`lW5+N5k#~!!di!3+u${lm5a)X$>ms8 zdK=m-CZo^4;&4Db=AXwo$FfrJNCP!5Z4Z^7#Tk?b*EtBfVhFWwNlXCy#~Az~{T@$sAr3&$MY> zZS6+i8!N~Nbz&5>TKfDl_+EMTzM$vHu+Due_)VdC3nX{j^K7+naoFJ%9cjSxk$cor zC!FC-p~r^k2+z99i@oqEH9Z()Su!GW=`ua zfic<-4J8bxat(%{#u1VF`w7bYxVKhb6q?tch9)4|d-w!er=Z!MnN!4!@Ihdzw5szln>D%zv?t-xGXgvX`#n(Ul#65nmJxQ%| zv%d$g4nJn_PhBu(RVTHReCHNpZj5spF#O?OGgA5~k~Qi%;R5^I*!!i4r9w>@a-SFn z#I55s#v}FKVtQJ7Re?VfhQvOdOK9ho^fPL03*KB7zt4NH&-fIm1)jSqc5FCp|MQcL z)Ycx1BoCqwdf6q9<=ym=u*x?;+Pz*c< z?V!kOU-3GGgs}eW@~YEGD`$RCbwuE1+ksEUG%R|!%|ZyUquB5Asz5aCTACdgO3;K4 z6S#&?27CiRkr7&4n!C&b^?BZx!>9q*Sd}NfG*R$j-+Oj#)dGJWw%p{)G%Cu^QyQ<>l(az?qz3h|Ff=628kWoA70-80dtfW@Nohfe5T094KY_5vh(gnzNrm!6B)AKSUR z^O~EV*BooEnzfN(wrrg9z~&d^g?4Mx4DIJ3sv(YB5Q0;An7nb(m+Ej`m?Iz6A(2G5H1EdN}>6QyuvI$ccCsaH63n1 zWS=y2G6o%;@j)?(iQ%Ff;V{OGOMq5D@q8`8gp!yVOylyT*)L%($%t#YPk0l{Q zAwP#PU13Z)U=HtmQg_qvYS=y;#ucG1o2z4!g}K8mpF&lv+1b%iSzBAy)`p)k9!?{l zIceqQ=2lr#LqkVr?IV2t$4)O@ROO$~j;r^r?_;xcFAs%#x#|Z~FkhBX>fdW@(sHhi zE59ALL}Xjkl7wvu4efvSDKy<|9tO(FXygG`;tzwC5Vo&<@O7OR0#*}Ixe8bk>rCU< zYoiIYc=r{Pa6+A1s@jSk*?X8u0@3#6Cyzt40%Jqc2&}_9WH@sKGNh8>rBx^RH1d9; zw;RVE*Vp5$^XuzE#R3&7d1FT28@S1)(nyV5>f>CwofRhAvyNn5c`>Yo+cUO*PcsPG z-`(K!C)>;3`~Uaqa&lEw=`bxRG+8iUFxUq&i0{g$p(w&h(%#+63aCtsIOa=Sf9HXJKW@F?1IC=PouT1bSJ9FJl;*>pZyYEU&P6|D8 z_q!B)??CFb@Vz0QK{Aaz&NoDC7XyI9E6nef!N5%_*p>OS!^lftq)MvK_nFZ7Y!^dv zZKRPGMGAZ$H+gdtr~24y^{df#zPu?=Nl>o+AkTf`TLkw3&m* z{Kms|qi=DPNGrAz+YCmOQroX>1)bj8UyY?3oC+d?^{f*9F2N=O5_NT$((UcM--VW> zf7SqBRR8p#1;u0MGQ4=!*E7WG@A$1%TMv)A74kIIaNtwD-VnuC3cBuyr3LEg?eo>@ zm)PyEzo7B zlFNrhOBDi5HP-0Z)4K+jS5=`{=+zO88Ew?fI3zlGqPKkUxUXiSpM@+iA7hzir&WJ_ z9Ybx3$M#o!rk48D_dFaRRyX4ZTS$rtiz?NC~=Yz_9>6yGfZ~2U59G!il{NtqaE^36RP%Z%n9Y&j0us#DtDaM_`QdB zd~??$$u5UlXcF4WGmUoXc@`14^X9mI9;0=IieGM=dK}cV(F%x>4 zC=QfLgncKfZ&8R?GZ7Zl9Qo$0^}|_?qn=H|`MFd+V=7I33rlVb08wbVhE7JEN^|gj z`M_m_qk~Y=Ob~bh%R=FGE7oPI8Ca{1#FG;beO}0pm*Kx5vYh zjsQvanv3K9w|SdbQ3L@?l!iM`y3@;shC->~jy2}A0~=_=D`jh~3}gWpuwUg~OmDkk-Iup>==y_L^Mt8Vg< zm7B}Pp%YnS_dKCxA1~W6joJqyQQ{)@LvWslNl?B>q?BHL=gMHb(-el!nH+lhcdFOv z#avca2KW+9FS8Ne-|qMtE$k^d_z7E@feN;vlll%{#^)SiaACTgWnFY>;X>twBcBiW z=r>*y!qCl(APrBU)yTKF*gIcgBBY3R#S=;eJ41hM#x<*&#g5qjz6D)WeK!o_C7g^n ze2GA~Nni9H)`uv>+X&kFEj^-rdd#XWkIE>(CeUb(KpXu$B_hH`HneI?F){|Ju;?el zSP*J3RGtXiGR@1+=R1@!HYT%Qt{XIKoLOIwn?EjXZcgL2Un*2gp|AvQEN!pvPDDFj z&N-k#$Cho1DUuZPCZ@Tja|7FrW;DrFlmdPV+FE6#bkPE%CDLU56P z8dF^2j6BoOBqqt4ibnKEhv}xPTph#9%OpI=-YEO@)Ea2daCsSjj!(sc%I zbVj~fZx5_6gMDI2XsPO{`pnDK#&4fl%czHKGEw!TAEmJzXPnxb%v^2q>Bkz`9{6d> zgNZz3q{Isyd>yqRL(4k2&RW`@))NxQ6!weEbgDXzLcUFQB{2kzuLN(cP>+8%bC{Bn zQpqh1fNq8YGT{*s6PJ*nP_pxrperaUnjdU3GB6N5r5uasrB_F|PiGaD(R1pDaiTQG zagJ-XJpA9bo*h~TxW{s_vxA*qBZ6#}>s8bD8JK&W)_Y*yNyZ0-k3t9tm`XVM+Dm)5 zOF1&qkj&lXM=9ks#IzT?Q)rmeEVC@f)6o(WOX}(pwq6c+U6-jnJv|{%ABSBsOYqB{ zmkZK^xf7nbjxE$YG9dcOJ?9R3Elo5qIos7DxqtX#5W?zoM9rxja{@l*a=HBOUhj?U zYX6N620I*SapB_4*K*K|Tg!_NwyF2Zn>ZU407s-ZY#QcZv!({J2o*0u_g1t+la`J} z12kP74HP2MH8W!eNwUcfK2oj!oySQAMSV89iKv60wJ=LL&nyM8QI&K7H54^5`fYmNm)BkdfCJcsL>ioSU8*&gp#q zHZx_n_-6{{#`LE8W7xnc0M5#Bp&HU`2cWUF_X$m!LP-0glFII5MwJ(-+O<4Xv!j9)hvSyF*$AI>L_ z*Z(zzUeMeqjQ(e&Bx9-Y@FLpZWtq+l+J8E#G;u`h7mud0s@RcN(>@iroQq(f>aFl1 zR;JL%WAugxLcOL0-{YQK@{b>b-S?Y{K|ySJ&6s@%6UmnCjy|j67pk=K8~~DYwTC-{ zx*NRtE-L1p?p29a^_bCDQ3*Nohqbi><80$&Q2!n-e%C;@RKScA2=DPrmMVk^A}g9{7`yI zcJP1PuyM}yXO$mZgLDejR`uM{($H*)tgAqthnE-CILGc#JT)h0s@^XvG*4GJim;;} zG`3|l^5ms{LJ-jO-IoA20kObCesMxi>|ZEchC0QxYoBNfLI0mbf0xtN9!EaV;Hz&7 zLZQr0qB$m%Pmy^6_fF^dE6JG?c$Owz`cwAOg1zNZ($17!0wtoB5uJD7@ckSL@c-XS z5{$+UBj`4>dWLIz0)&0r|DVJRw*1lb}G2peR}lqQ5=SV;(BqpU!Hu=ge)A|fDV zMnRUdimSG+R*hC$tpn_M)!Noy@U8Xr_u=>c@ykEC&%Mt%=lMS8oadZ--8@{aZCG}I z7BGebe);m?iyNnY9jWG}WkHcomKiq!H0N%y|Z(D%p z&!6kQMelS83UsFXxtN(!p&$SN%3Gm6eq;tq#8Up-Ib;Y}>;#U89L?Di$J?Q|JUypJ zj*Ho$7h>T<^$|F8xN+`TLQY`T&LDPH0^24=&%vH-mtmWcBuL_jSmcbPL|%$*#tuDJ z3_qGbmPg=R{n2;a?F<$EvXb!`@oxrOAC@qIqx7OHD=~%h?tzbwt*>(NaXeNcgU?P);_yTs zj2(JP5!0Bs7+botGlM~Q-9~dUqgq7#Gp{&N*f5hJVTgrf5z5FMWl!JeAk;7BEPN>W@@a$hKj`T51l^7Cg;pZxUr(T_hoeDMAKdy2bvZh!aft(!NlU;F0kt6yEY zeCgtarSs>$JbUK!sgoxb7v|??kIzgWJ1U==oERS){bJ+si%9kF8k6S;vF#VRPfpm*grT3@NznT%W8z&^0I1iq1 zj*rNPMweK>GA`*DO`eBg%K8@tCk?c27+bQEO&Y!{TcDfPVYTzqE~xuK?(#{@;q5>Z z(6*O&vWbUBr=^*b5ZMVnctozJ6vr0Fa!Ih#a2TP|i=bK;tE$nQ(AK`4AWPlf8`gJ_ zc4&a3(KOVGw;7>8JIB;|Btw|$ifL~`?LY(ngd6+}b_tvdCWJJz@PaeJ=fq39OK)AD zet^GQzaD}NN9L2P4?>GcKo>_f!+`{P`T9s1vR|?ip}2 z$a)n$tFoXul~e= zC^aO}=v3E2A(9YXvSti?^8ub|RLLgm__XSM=_GX2;V;VWJg%Mzb)q_wS?QlB1Vz10 zEaZwoiHuk>0!uvOy0Gv}h@Ckh%ITMlLXX@6$>8agNrP&iq3fppMEfDn^00{XD5fqt zN07P4tuS6yk6#iflv-EBSJ&3z8?2h5^uX4Dow$xIU6ECK`{@LDYC%(xZuEXmN-Fly z0bS6c7IqvL>Nc7_t_Nv_kbq-n#J+QaMNQN(=owYvReQSgiHoP)ptpVP(5MHSQTHGD zn$a>>e*4$USENtYa`T;!NPCG$x7Ll0M^WxNo9L4UKEqGkpX`D_*zP)d8cohTKdx=+ z>|K4o!F49`cch34`m-tZ@WT*K^3-p~Qc!XC6Aj6R^8D=OvlU*kB9}NkGbfuVHV7&R zMsBo`@Kmq}Q=p;}53p*Z>KhH4TNqWi_N>lGnQ3`1rY`|IaJa!Czoo2rywm}41e5D| zsB@nKC>JFY+QU1U-42izH!_|JG~xx+405hzOs% z%hYX0mWLKAKtaab9jC7){q~Tt#G1=0)2F^K?#}XKqo3P}>_~mj%(0n;$=C=(FwX^A zm|29N0A&^#70hh^YQx$(OhaQ-vqP&vX-By%s>-PYQ*cPFNMe}U(N)poU{f*#mkHRt z6h@hQQs%t>-dIR(=omQLRD_(4rG?UM?UE5eu^WN=z}@vA3h@|Wta$c(dF6#-O|PE& z2r;CZY_!EVMyi6;zm!tj;=JF=882^C$#?ypM0AIG)!wj4w^SIo){}H@7;CJk+s$F~ z$0HiB<6Hz8k*3x-%$lg#IW>1hL$)NpHj$Wa3w1?Eg#Yu$AbR9K=GVqv6CN#j6$+2 z?7TJHQrPyUkkIUeC>TLlz=k@|pd|@>d~_vSpij%Hj|d6GHMjWo7<6>WFg?8oum{^Q z%EKuncKAS>UUAq!S@{uvhYU<-y}KtKT*NYKB=u2)M4toDY5h~!Gm%&K5z$9u#6ge{ z*!XUD3^I)bFSrpz1Zn2x4;@kYSm@SYjpt=_h2vx%lCUN6?8rSaa;>aYe6#`KWU_aw zo`>UXh*Q7F(|{9=JcjlK3!VIpFtHX53cR&>=jGE2FU_onZ#*F%>haVl?9Uj<07toQ zh36fE)bGo)$K4O!#0-!xeqJ`178Vh?568-3>#OF6sU)VLOLC$C;}FZn6n80ddh z`pS6lW@7iZK7JY(zqp~G^)5pvpKrH0`_OY8I$dm%MfC)8g}n8EI2|jY212B4s7_jS zSZ$qY1-yyf+OG6D9<@JOr>ZZcv#X+U&|jx;M6KTxz?V%THgMY$W{AkiS^3BeW|6?! ze|bZ&Sk<-Pg9J$yB8+0&Lg7Z%U4bO@KDKDbPd5``=d~Pwm&@A5yUkwzg@dRgiOuB8 zbIOXeWpHi!Up~+)+YuLBY-vT}0R#(? zwh0aX2%gkHf0v2;X~(vLgmW);_=IDqm;SX{uxM)+tQ#L(uTUbZi;d(+W#EfPdLyZ~ zQZ&Z-%lJ*L98jQDrRFj+s(76xKFw+k?IWHYJh6pt*IhAU-7eD}ztzrIDXO@O1>^It ztuwkv(Yg4L_#}(~COKsDl`qhF?sSwGQ_P3zmPuVJs%rT4jc(22R<`b*j);VoZfqU7 zw}Svw`FEFmChABhWcnW3t22}rkE;}q7LRXf-~!&q>`(FK=DA_23k4VK`H1sQcm`Yn zKTbeLiILZCo1;-TP>+AQ4MF{i625r#`u8_FYo6^(A7GWO*Ml)6qGw<+AU>~qsSM;o z7M|L5%{1mM(v9(?e6OA}Wb<`9Z{v)@FcqOiQph8lF2yFgjr3)V(In+W$AjptiLAU$L)s!3F*;_q#rUVzQ0r%Z5$?`=3M&BB*c)sUz@#oimLOwh(AIeXOAN*j&Lv}5r#(cnGsoYp1ek4OY?XBBYe1%6G$ zg^zp~%7o-k0mh!f{Ci8|Y%XN+sh#eOmSfT*KL9Uzp!Q<{wA)i>?#;N@c>qU6UtI+ z4@9S;52Zx<59MLu#I77)e>~V8glKh&4Uaob2n@2MjCwmG0nE&*w?!2aRlKXTR1X(OR#DL`yw3Ai)jgd>n zg^GeLeSt29hc4*J0;peX0qf5{y&mF2^itzL1Kkn6BbnZ?oV^S}ez>^ELQ>*gCf$}> z=~+lksY%1dRPLe1Ns}S<7zZl4X4`IjduFGjlhl;}mcWunipGfOA#dbiKO88MYuL;| z78Y@6BWIJPudaUm&9&=VYl2++0HPq3$8ZbKiowDCTIV;j=?OTkU7U7fZn+FoMa~ZHJE)d>7*qHu zC>Y&Jgvnr=j)aUh;NzQzHp1KLJV=NN1RX$(v`@v&M)}h<(5k|V+7Q%36z?_(4G*I} zUOo-~8UAQR`Revs^Bt4RI&qgylU+xZi{6bqmwDql9u-#z}NQsG@MJ^i638u*#| bWW^%>x7XlHV^#Q1uOX^B?ki3(VEg|7!QHuF literal 0 HcmV?d00001 diff --git a/static/layui/css/modules/layer/default/loading-1.gif b/static/layui/css/modules/layer/default/loading-1.gif new file mode 100644 index 0000000000000000000000000000000000000000..db3a483e4b74971fbfb1cc0fb6499852cedfe650 GIT binary patch literal 701 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 dS2dWbc-(>9=%hBYV + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/layui/font/iconfont.ttf b/static/layui/font/iconfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0c8b0a50f686021dd59dbd6c61d212406a8ad3ef GIT binary patch literal 40668 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 ALI3~& literal 0 HcmV?d00001 diff --git a/static/layui/font/iconfont.woff b/static/layui/font/iconfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..786bb2a8c4d1aaa7d3d80b3692b27d013815eae1 GIT binary patch literal 26744 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/0.gif b/static/layui/images/face/0.gif new file mode 100644 index 0000000000000000000000000000000000000000..a63f0d523561c523476e5330a0d67eb7318cb187 GIT binary patch literal 2689 zcmbVNX;c%*77h|fSVf7btRc9;2uTMBA%GGHLDVpaID<1f5|U^lkc1>iKxY6!1rbG@ zd2aAjaK>G62Sr5@Tv%lj5ZMBV%8n>ts{1vL_@3u@=Ev*PeX8o#z2E)rQq{hGY`QDg z1Y?5vfMDRklNoDFn+y1$-V+&nI?*1FjNVaKeTNKPP*>Wj%Sp)S#fb{Le6b$#p-{ol zsW=k|kKKQjucL0^f#x9i&7RS^Kv2J=HOHjvYO1<34zzKv#bH4$O z#ZFq03?oqI0iLr!jWcLn2O9kz?9&3Z4A8^@wQiuy9MsUj%i!U1H=q*1{h6R?C3x4%9Z~bY?lcTmR7j#Mw>w(q{eMOd|)qc5L4N&c* zuC#-D;)kkvpmjZHTn0Kef@(+gGxF;@#-Mq1Z|Ta0Q`*Srt;^f+@S9XnW&vKOd~B6~ zHyNOJFDSJDP5y6-g21a^k$3r^XIIl%Q@Cp@Xb*$?kBqma0hL(Ye-`v4To=x`Eyook z8;-r))qmH#x4;x0I$fJ%qOMq2{D&DbUW-iBk2QvUX!>cQSp;gFz)RlCiz^#1xfLC- z87Zj=eCi$V&2U#t-?L}$cK{g8qbIOabq>? zp4*0{8D2G6Ot>!#v2jzcD7SojpVX4O3Vsy}_iV4&YYK*PK%Fl#a&`FG zM$objsAAv_jp9{L8DlbVvx3c0C zr|Pl<9y$m2ro$@nL4nz&9SmgnEc|9WGSQyB1p-}ha92Fse?ZQ_Bctc8B-z2L2xQ`g z`dyy7!B5?H1nxhi9>_)z#2V4__X%3<%?$9NI#4tL15Xk`R&u3e$bmwRh?tD)leSzDDEKdpZ)(efVx$6cpg=B(lJNwny3Ld^ zn!A5?G^vQPVSFzWp+(__vn0Hz2!UAP$MPVdI}UsipFwe?qgixuqPS2gG&%&)Szcb= zUTn5A#oNb)?!ubF_?9i(g+_B>QeAz}{6mlrgqnM?ATOpDlj_W-In$Ej}3 z2Pdarnjc+!IX(h0dS+zk;m|-}p#C^9KK9}L=(~~Op}~RvzTUTQUiZA}?&?%^w70dk zylj5a)Y$O6zOJ^W`dL+FMR{52(=Vb2{W~l3=#j&R4*u(p-w$N$Pv5sUZBOd%l;mAIcO)flPl%8E z?bmHvV`G%jTcQ+lS)^1VjtCFiED~ zT(R75nJ?Rih87J7rZv4a|qPj4f%`NKP{w9zJutaJ^PRj7vDCX2@s-1fMsBq7R3UP)M+%4tA?D+}XvHFzz*y%L=kmCmK7^vJPkuUQkAmPS3^L3MDk2U`FtXG(OBw%kf#qS1^68svpOwv@mo8;P zDGCaXYN>-Qp0~Xx+rV_GI`^sF6U7K8(~zl=;$%)R)~gj@bP&VoMw_I4V?6f(!^$+;TlKZKNWRvr5bBw-5Huyw**;mBJ z$e)Q{?zFlcJ6(9*C`9kUZrt2~XFpTKnRkA%Zm|z`^-ujm+}lE&XVKZs-c=Znar+N+ CBC!|% literal 0 HcmV?d00001 diff --git a/static/layui/images/face/1.gif b/static/layui/images/face/1.gif new file mode 100644 index 0000000000000000000000000000000000000000..b2b78b218996f70773540d0d3a8649d3c2d199a2 GIT binary patch literal 5514 zcmeI0X;f2Z8pm%|0tv}wF|q`>VUY-kh?astAQA;dB?6_fabYVUO9#+)7)Q(s2}KDA zighC_TBPE*AR-hSAO%6du#{TxXn-QNiVcVgN|Fij%v8I5oO9-!+)ww*d!7%!|9hVI z{|nx@!P_T>2oQmXY@j$fRCS-Fst#8Fl-Ji<_wt8B#oMn7hHBnZ4SIK-bRDmyJo~XxHIe<@S;}yGsph#%R&3pM=?m3xsA@drb+`R>L5S-6 zz>yk^YAiuB+oYNAZ_LF?#Sxmh?wKCnks9lU+z5q2ar+DGPfeziHw@J?S6jsf&!0cP zdJv^~b+7qw%%v2U$@--gvCCCsdk<_PHte%$Jmk_-lRDCrG;npxi~bPRgV?D7zN#&_ zCe2g*w7m0(@9fi`>xz@w^U1XtK2y!sP1)pEFP~|YwX*|ZH)_kL?|yde{AtyLq;?6r zrS5!RPuG*%o2S}r6<3x__s6NL<5VMzZyPSC`gf@xv3stwub#tO^-JmF&hTmZ_Q|h(Unn2|LQqcJljcz`CqNT-d#=P4TsI+`$f9Ch+HH6_=gXNx+LO~J;=#8M2bsj0pAsmE~jpC z+e9r#gRZAtL!FaYC?7{MaSw`>N^<&&ALWFFS+Qsk`6z^CS-}uaQG8o7OPH^h*N!^J zB?bn*FiR_2UPN3N6Xq3j4@!m%=yWd_((FOaO!^t=l&v`jJMzn0XXJy!`5gR078K1v9z+wE}3v}$MR=6 zIFTWuo=H)j?LKN}v%e&C+c=j~Y*=?dE}h{YLpr1Us*A?L@@)yGj!AMTkYgK|JtLTp zUWYwtNqJJ#_vQJ4FS9uod6LmbIk@-lL+QNel<(bl-msVKb!gj$+4V_uU<(q(x}GL@ zP{By=zzPD^M&24#*nggXldXc+BNE$8YZmMYeZm}wO7Pr>N#jQ-B(?~MM===Y3%&*=Az{%xjzo9W+X z`aYxYGyeyD21WR52Y^Qx#cLG+wFnJr4M0ddVvCPf0r+fTZ3%Hb=^BdjXJOs64!{j- z=8t&W0hH)!X479OfRW#}%z;G(K(o*q2{@ATH=C@;d9$e&vFpb-3t_8bq(m%AT5@Bi zGKi3|JlPKn^yD5RScM;yAK_GS4BiW0*wZ&o;~|Z$kQjO8WbdgJcAJw{tw!Ydd%mLV z?5ZPx4kl~B=EBi-Yvfii`n%!vDN)FMpTt8(Ew_zvl;e_+rl=Jxb0hxKtiGzC91`BW zqTyE4hbwpcrhAe4$e0eaKLebbF8(+pE&#b81IH#>A%oS!(rt_mamlx3=#H zfJ`;IZGvzuAmC!j{5rHu0AsnC&xJqi665{5&Q?zcb2^b9QInYSv`~04FS!aFd?!vT z?KXphV}y6&glQ}sz4LCID8Gpl_h}Ya87wsNA?=`Nl$ZW;|K?|kRPTx)u)uh~1X4b8 z&WAd9zsAXep^cMydzlIptr}~=83}fgrZaeGbNI#uc}5V>o9Fz&=fwToM4rGRP+`G| nqjLpT%jon>&iI|hI5DGFNqSH|Meaf1bVU=!q}mt-2YmkxGX3TC literal 0 HcmV?d00001 diff --git a/static/layui/images/face/10.gif b/static/layui/images/face/10.gif new file mode 100644 index 0000000000000000000000000000000000000000..556c7e326801a0ea090bd693ca43807925d3cbc2 GIT binary patch literal 2797 zcmd^A`#)6c8Xsolx+O`H%Q93$nTyL{n9MM)A-684%gM}GWn?bROqpmGJGZ^Fi(PxC zQc0BVBpao@iAv-WQX~e2E`%;_^UgXmoi68xeb(o*-uGGG@ArA0^?g3?^6~R_ zc3~Q#3{evksAPYm+ldxe`B>25hYY5H$5ilyap#a4=-C9i!a--i^?{+Kq>xB__^Jl4Vn&qDieZbVwCxhx2@ zILKOvg+lCu6V~AQ`is#P@}Y#M=jR}wYR+vn|5TiK@RaXU!OgY6vPDVqMt==D71BV~Djr?Wup*KT5)s0Nt z1Ea;`y*Z$Vg^U%+NAu)^dy4m>3*sq2x(&3`O809f`!AG_6@bUi^1&ZV_v?Zdd(dFk zaB1^UmD5ngJo#|eL{F^zWjuI$5Ol4%mt`$$vYF`IF#R$WJTyT@^O32h{2hyB4HojL z=IPO!U?>rcovq1KN5(EAA1kK%5`D7*2eJ&VEMP|C>Q*F(8D`a)X#VL03k>m1L zNkAHLnrro;Eo8bcxh+q#qdYo0+G?;|YpN42;h4%E5#%GMLHA}^^K#ikNY-R6d*oOs zTKf3>vYC2QS))~Z!7^DZ^;7j+S>2+dJsMN@jAc!@>Domz^^oi_W2V7c*5V2pOc4Y@ z&2m{F+Cg67VV?A0FFQODt)hS_LhA#AAqG>-R6O0>B?2aUJ1mA+@HP&QihXzMZ!E-# zq+-`O1`-1Wo^TY$H%P1BC+l(AvXy{!WD2~F%yz-V|XHpgowuN zu~vo&Sm-lEyq$`rD_=m$oSu9k3_0TMZP`Q;2_lp6BnL+_nY043Ba-Y0M8!?EB{@(W z$rL*$=*xvwWD`birGzrPzvNQvsMsj6SU@2VVq;_RvG#bra2tW-;^Lyzu(PvOAZ$gu zd19u-mM60Os=$CnY#~P==J0utQjy8x?-Wz9ic0_80$1=&mM6l0ZU@iia|sfrfIz|% zl`VY*1_u6bD3|*UEfR;q-|PLa!lLlq0+> z3;A5=bJG;g?6o+|s`rH(IeRURvvLWFVhBoW|HJC9A;tVCm){0gvH3RoFi$b>Ld9UK zAaWp^nVy=Q`1J9^`|+{ScO%1Z-@JY`G&u0`MSowfw5R)d*R#%!r%&1+x3#uBYHn(5 zcvxTep!WW~yEWB!sw)5f>vqMho8@Jtf0o=RzFt&#?dp}wmo8o?IDalb?`-awoYSXH zW}i5IEbHhWzaKgLTV}?ggTJO9_~qxc)Rg3;{XZop?A!a}p7^-klGt54#UkMj0iVZ> z*&fY_+O`#rWV4tNTmHFu)5Z<|Sif%Vn((mDkl@upfdQ-h{d|4Ay%?Szbej80s+%jt z#o3AM=wNS0A`&{X=-C5hG6x_Mhp(KcU9@@YgLMeldR?axtN zwEd5V&Z~-T*Iv@rI8N2DD@?W3Rvb>{t;?G1!&v7sZ$i}`xn`>FQLpFZa5oZbQ!WU# z6N0id*ekoxJ8V?Re!96gG%&ojbC{6bC=zRL(qC9 z9AVRaGo3jGR+2<4BiUquM~)5rObdsjH=o&{j`7Fgq#-%QIg)q|V7l8WN}3~Mr^HPsnv zYBVTkuWYI`!P&3tY=gmCJ!#Glz7;G6+M758sp#>>(`c$v8b;e4Wr3n58|AJ_HBZ4M zKkT%FdUFzk{<}(CnOdM}KuhNHXeOR1V!?SZ4Gh!>qiTZn!)^dLP>z z5V6E{^;KymZ80qbv*Od_xEFz6_>=chubAD346L#aA6UNh2+PBUmt8$z7(H-o8%=4b z+W)tqA(h_^MWGdj)+!Cv!1vT~=&*>=JeMuyj@YH oWz4#Y8lWniX5g=81^=RXq3ldhwpWFqvq6_o40028>wtN;K2 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/11.gif b/static/layui/images/face/11.gif new file mode 100644 index 0000000000000000000000000000000000000000..2bfc58be8c70d512f4371b50b2b98c7b8adbe719 GIT binary patch literal 4121 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/15.gif b/static/layui/images/face/15.gif new file mode 100644 index 0000000000000000000000000000000000000000..c9f25fa1d25412e2e929347c0db0878b0b641d58 GIT binary patch literal 1793 zcmZ?wbhEHb6k`x$cvjC)UFLjcxo+2{sU44AEqro)?t{~@-b%N3XFR-Hwxr+ulK%oH+No|8@joUb;8o>khO;% z-pt54c=5$f_l$zz1xIF9ZC!k8rPSX?m75>!3YoF0@6_@yw?fNicKtc8QFZZ3q7CQQ zJ28K6dKNF8dwdf2>Dz5FoA&JK_I!S*#c%QM(+{R^st_tYd#Py0=A#ot|K1Au_q1vD z6!%ScSEOWk{yZ-7e1G7dQxZR(%&R%HD{|8GPuFK(p3ArTeE-+$eg#|BB~~S$dA0ra zCi@?c7ri|%pb4sD-Qd^>BZ@TkN^$;DL)19`Dfod@V3w#!8pDhc-E$->ITP|u(PWCJ5fjDdmu ze?xs!vs8YWOpZ)hzDjdnpQuc^mXOXgX{{oe-u@~|ZFy%{R@dafm z@IQa9kQKz2C85GpugPS}VD;TfljXPO4=V=Kgd@?P4xMV}iXHU$+K4vH=vCN_(f6?!dfWS6n2`SY^jFjF5#yp%}dht@+k^lQ5mUcOp* zcs?^%!Geg4gAHe92$#NpR-1INsfoGiRz$%hw=FUkg|bMf)@r}A%N=>H&t=-OGD)%dJ&%aS1;>?S28-4y56)#MguSwF{Wn{Bi0wrzT;!uy3&oA(_^ zP4_wBW87?f(kHzUYzsfa7D1pbvq83K2{lV=HFwoZ*)}QIN_Dj=yXlz=G?~}CDNBnw z0PXZuXlB-166sJM;cnU891&3uiN0ojaP%=)9kJ4E);wy(ATN9lXld$sVfl^MZ`{0f z`<5JUy@-h7JMl*sta~4a6oo18!%LOf$q&}sITYaYiet1X;M?G&Sqt4V`0s%?q;x>VWruo zITPfDMZ!^SQHzD;+gGnyyKeo4jrC#{^>Vzt^*iM1EyS8U1jSr=;w_HEA1-Lp7hf0JU8dSF%T&D%E<+7ylUKYC)M*n|{ZEMUu7fY$3aNE)ViSQzrOwtGm1);sbf z_Dz^%r|aYuqCS15lTnC&w5K1hmuJ8d&l*pEtR`qRKwZ!p-%_Ak4|D*~1b>MAGiJ?5 Y2vsx^3s|^#p`syH?V}DDY9U|^06Jl%MgRZ+ literal 0 HcmV?d00001 diff --git a/static/layui/images/face/16.gif b/static/layui/images/face/16.gif new file mode 100644 index 0000000000000000000000000000000000000000..34f28e4cde0cd13acdbbd7482282eb306f5b21e5 GIT binary patch literal 6721 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/17.gif b/static/layui/images/face/17.gif new file mode 100644 index 0000000000000000000000000000000000000000..39cd03538a5bc595fbc46c97cf2b06c8bd8dadd1 GIT binary patch literal 4439 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 IdD(dV7kW}yvj6}9 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/18.gif b/static/layui/images/face/18.gif new file mode 100644 index 0000000000000000000000000000000000000000..7bce2997f86e477a6008fb1cae8dd03cda10abf6 GIT binary patch literal 3017 zcmeH}`&W`_9>8Dl1yK_aL;=Ob3nGb&N=AwoykI7&SsFG?;bls#Q?`m}L+3&8mQre& zIhILwGPi9z*=?-c$}De97tM;9W?i*RZLH05$~M7$)uy&if585*zr8;^=RME!`JU(V zdBVcQg2f3|kQMY4K>D$L6S^e*VCwsu-ujWeGlg;bq3@?|+D^5LdymJCUa~Y8^!gsY z{z2f>E#`2~Uw$8|*Z2Aw-rjh4oPRNYo?+rb`(D}^h4Ei=yw2tG^*=}Kj3iFKyRZM} zw>>}ZHeB#+uVQcHV~3jDI;&&G?pO8JbDnoe441zu5^60UJokvzr zv@PhaVNYFm*7q#wK9SmdwCvfV*1neFSKZ$}>n?nC)p6=?;rbSj_t$KHAL}3PEa<7l z>mRPT*&x4NGr#As@2yM6pSLjn(Y*dj>7NEpTfVv_{OyY4+Zz^dS_zZCtkypa)!&IX zoLZ*8lhA*0#ic^$@t)+qlbrW=g8yD6d*A8Wcf0xDy12e8^3j1`$4=T$Pfs(a%_2ir zt_T)IhkCm6j8G5+G2g)$PY6J6$Tu=1D+n^4RsxkeFTbELWWAf4sj1ugkixtW)6Ecv zWNS;}U`@q)+|pne3uOy!gtTyu*sY)rX4J3DCQt+`soayN)bb+?bUm^900+)sKxuL{ z8%=Mmr~F_`>6A0*Shk9_(VXAA|H04URGZCwbQ%k!hM3(&Bw}bBN2+@$iK77J6p;J1m6pocLCoTxg|5!y ze0Aaf=r6G|2gf1@CqP8x;N4093&QL$)a(tQM5EzQI10xor(GH%kwT&%jUNV^Cc!$Qr>))HJJ+FdjJP<~NCyx1LPaqE_63aOSL zgJ#3zZC@Ma=aC`_vIgR0DmB(FDmg1eWaa-$x(nHA>C)vM+xn(*6BFAkVVCoQt)yUc zi`;qCl*tU^fl7hP3x0~0)j?;Ir{@K}9Dr56MqFM(BC4y7VU+0E<@FR@0E;H2X%)v} z@f=?+L)W;zvo>&cXtSq;Arxc*kv?21kSdccVylbQVo%pZV7DqhphS%gSiH`C74Qou zvSgKjLh&Xz#rpvTd8hh^c&7$$;F+OHld7a#6W1_8So~tSn(GC`T+gsL@33|2Jmpdm zS>YrI@jDy0{X2rUlOH+5eK0+6lrp`Ee8TDsDT5j z{-{H!`6`2m$W+<;CXJK>VGkKG6H^utqr~ZXveDXw4q44brq@gD?PV95b>qfeAzVB@vs@)69ynJ_({9pf zhXVH+nX;txC(7&P0o&fLE>_-)ne&9@%ylt>um~*^gjS9cFwg+8GeA@d_$)LQ;uHZi zgD)qu=rnE_w5OnwK~M8frDxL6Wr(NuN%zy8qu5)>%nUU;MT(Z%M9@8KPp(5}(^nwq}4<2ejm;;1)1H#-RipPvSpCYvuGQVrc%(rF&-pS>snM~eV|J%Sv zVq-^GX)VgPl2}_>75Xf-Tx#V}ke21+QxWOo&{t9T)y@1L%_lUNxjB7y$3Ez%ct$^V zbNYej{G*ulPYDA+6bhIzPzGY4;_7%OZ-TeLGlR7lNL7qa5<4|1H190@IRAMg5(j8}L?(phY-y`50aH_-& z8U{DVAdq$){Y{AivqB20s8-wpxRcT?Z`FaicaB1cs)H@ zqu@!rE~yt8sHR{M}v6=UaO z1;V8&kz9rTSSvM(4H%a_|H#z?Bv%Z^7s2BH^hL48W|DP0J3FWiUv6)uh@L!>i#eM%g!m2b=EomsZoMrFNGwm%(_p IDGd4k2Wv_t&Hw-a literal 0 HcmV?d00001 diff --git a/static/layui/images/face/19.gif b/static/layui/images/face/19.gif new file mode 100644 index 0000000000000000000000000000000000000000..adac542fd33e152fc08baf165d1c5a467f0c2747 GIT binary patch literal 3040 zcmeH}`&Uxg9>5Q86bs~`fKmvGN=OJ=XsCdSFDfllZ)I9iBDJa+pA|D35dp&{&8Do( zY;4M!>h`{Mb4u-@Ju!RP^cd|iS-0+W(rKJY^ny7y&FUYxYu(?@4||=p&*yvg=d)wt z6ylJDZjc-F6M$^5zP`10c7JD6)#SchlXsnjv(6| z>EJc5=N)_dp6s#Rns#?z&_7FEhJL*;)OV`2igr|s`EHfycxjOBUP9S){P1tLZ9k?D zotoBK?YlGx{p=*K^F;QQZAq_hZ@zN8_Vs1ut=;}ZXX5Vf!w&stkF7!6_vF~~Q_e42 zQwFd5Z7rPp{N$7umsj06{^iwb7u)?=E$cCNcT@WxZGL;VwCh{n!S+dC&tvtpE@`a} z>AzoKJ09@5IkaXOckr_N+m5)tp0@s5Nzbou89I^r`sNqsEVLj0A>Wii8n{S$u_w6y z;-m-7>#a51!Sg=0j@TRfa~i*`czOR^-;MN_H%xDu*n0ImrrWFwyHC$LZSv{uP`$H8K03&4+LrO|lK-RYjlXxMJUUzOqWi(CgUsRKVXtA_ zjOgUqk+Q^SAzy%iK@jBi8xkdi0K~ID+lS-^L6~73(3coWjV2JaB*57@AYe(9xkTZN z;gUT)$y{i;^9ECZf)YoQcu0yM3<{+QFxGSV)MPOQUzEI`@%O_e`5K6ki{etYdJ-TM z;*2K0#2-UvmP|bqDW)S>ja&4r@f$q3$u32Ab{dUSUB|Z%WYr@|umzzS z8QeEhTIEOcJk%^~L;JWA>!g)T<+^(M3`J&BCNvYLYXAONR(5JEH6pX5PP3yhWsM7pwJ6#Sc%<&*+cVb)n|V zh-4;ne&?aZnDT9c#FhGX)TQZ0^OACDKVgbXM=viAc2#xr(0#$lKv*R*bgH0gibqRJ zbzDV+@}yMTVz^!khmDZOJIG@pIE00ecJg$cSRz3KBcQUm)Ka6COO6(qHUb{N6n|&g#oQ7BP0T2A!<>-(?d%YZ0Y)3g*%^dbs`F=VP_01?5WkkRBu3`YSH!tr@T1}2Wn1C6=y;p(WM zWE@W9o>0S$Q{{;k;1YeipWs%ri5L|d|K#7jL8K?~x~|Wy-Mz)(-0;z7` zrq|^&C|BA1fvl{xrB3eEY3+&ZHWU{hzbx~cNWxl5Xx@XQzMs|^32qyeiQk+<6h}x? z_II}EitEth^oI8CsGdy`U+SRBH;ZD-!C)=1mzno+V$iIU%#{_E@PxslwJZbq4VGTz5S8EOsyqc)urqel zLWyG*BFPf<0W+yl>L!t_yn1dTA@k6anYFyaiw{<@{N!+aVi=*V?=k+epF6+(RAd(( z2A|h5E5En3wRQqb>vyVhPme{ilEq*Wj(NvoV4v$%pGgY{aO8Bi*F9@9+vY3G!!EQ9 z12dT;M{Nw-pPfBwCg2DZum@_nB|u?M6tE}Cemk;o2C_C$fvM3wl%!KSf9 zA%G)N){#hEY(p^qk|_q`<4|=5kCv+8YJ(nzps^Vmtng>=7LRFx7yXhJHOCRe!9jzE zcNW7Crh=jKD<4PUHXvs|!gh=#OaH3QTz?@FKFxJVTePUyiG&MusvD@%=~@}t`h^1` x&iYmvvfx#bmpT3`PtF^0k?a@iE%))*1NVySmZizF-M!R86}DbhY`;HF`!D7eFhu|W literal 0 HcmV?d00001 diff --git a/static/layui/images/face/2.gif b/static/layui/images/face/2.gif new file mode 100644 index 0000000000000000000000000000000000000000..7edbb58a843cec27a1641a5c9b6d30b8fc1f8c26 GIT binary patch literal 3222 zcmeH}Sx{4F97iv?30p`p30s7afI%Y0usQ~*kRTvo39F(M9fDyo)qyIGxZdPOAXfuw zMXOZ@MT%f;N3m|GEf6fID6)v4qTr4|g<*?Gl4b#gPG5ZI?R!4|GvE0w;|8(3`Bs1x z@CE`_yJD2}b|0SX8E<8)R4Qff@v))So3@t97s9rhpC4WH_pkwL!xySPHq{)oZ7p>h zzvrkPIQOBeShx`LV*^5%D6C%`ebEiEl~ zk7CE4B~|Y;QY-4zgO`-;+$Y!56kQ@|0&T3P_~I^%dg!{c*>mvT;;r*BVqy5BGo%N1 z>y$4yj<eSIAsOnz$S)c;g7`UNK{5a%gXzk9w@* zb+!N1Z*Z#q(|tE>A72qE-$~S?mF?$QKjr5tH)lqBI}f!)-Psh@T^p)=9MN)#S6{a5 z)5GBNdp(u?=bL}eIw4v-(&%^nXp+1*wON*U)QfoKAiI@LkEEj?KB(WsGWwPb_Q~x# z%Pj_))}E%~{$aCw?}kb3!SMzc<>;fP!fzD)jmqa~!+lLXRgS8nQwfW3qmScXUR|)( z58qz3wdeK<)!QANf2Y5%2`s^ZidsvxTJ505@&aRHmU1EkJ(zBw0RR9FAJG_700J1A zx8_Jz0AQpRL9k>=GRa;_N?sz#%Zsq5<3pC>LV|oY+spD{Qbe2&7lU`gW43MM#*2ar*Rwx+$e-nGQen7iJ*HH&6s0DE)T16NM@URLM4sVjkz-7 zEVM@ox@)%#;|K;6VBO^yGDT45hL=an%;wJ9`Sg_goSH(sqv#czn<8!~#3sD~%K0_n zM8E1+ZQZBKnoC0ED>;cRViOmBxv=BT@LrSCmTmn8?91ocFUGXRDmGPv$5s+|%)PO4 zu!z}lmB=t-ZS|%1LinMQX;MOwcmS^G?9*u1hBP-+<}$vIIbo+9TL7Q-4AIssuw z=N}TG(+?p~3br$0y?OA2c(7hNM6a9{%@o_D&;*eH}6v}Ak*0_@l)tisouy=(k7zah6uVrZhe;O``p=gyk5s|!3?bZf88ne*I%R2?0u zS@(sfB0s%2$%Th?;siz>TCk?_9ePuuvyJQQP8vMj7X;0X5*K4F3?nP3QSNhuu{urs z84)e)87pS2n6YB!ulU@SnZM$z3t}))Q*A*^DVoWmscAMP6tU3N6pmY^9V;nttf6+O z=$l~2j(DK-Bo4lc`8k zQg{>bzl{g&%nN?YhqNV#O ztd%y!jg0~xk-W}^+K#6lpv-gGpDC^eLab>noeHkN6PT!JRZZ+!1SKWgcK&*DT0tgB zXm^Ef+`*7`+buyz`jvr4Q3^sy@c+#WJ(tOup_y~jhsI<}!D%hkERC;dDT30s^pD^v pEloJ|-EsmUGDRV$ zP#i?L)mNjAN`;gz%Sjj1%zm5doX+{3^ZVoX`2F|$Job3(b9ujBujl*qdOlyTx4WmC zjV(n3&;VvdfOyDm{y%u}eNXW{lK8H}eBUzh)18w|iu1i_@t~Esf7$$iu~@KL{J>{n z>Qu)OsCYa^{2N(3Xgz<2Ao+AV+C*;d21@)S&tsi8Vy&o{hUmo;BpMf9CeKIjN; z$y8pLE|YwI^kK+PGS@2}XFh77ibpA>JMklzTr*vjr=AXrpB-FyRXEunCm!3eFkLB{ z*e`zQ|FQ*H#)(*%I(shG@ZcJ-pl&N9c;27bd}!6B1BMHe9KmUoOCd_)VfT9% zJUX*X9yINe^x@uht^VALk=#U2-lRRfVj_N(FX@oKxX1SP&nv_uTPCh~h@b8jKZ%?h zbQ22?FHHS3KXLHWIIo6n^|~GR;;PE)CXLxH)nUHYn;!f77uQ}d(ED^#T{3&4Gf!dW z`qC@O@U|TI^?bFiqdF&pl>5rOUUqs4M;d1zY?*%)Qpav{q1Ip9buo-7c=&V?Td4FvmxMAEmw^%$MBr znf_HzGBb3SPnw^qmUODj-Co;7N6t@XWe4aV@kd^vs0>tvB)Vu_O!pQ)+w-uk6nwp>cMoj9}a8QNCy_vg@#RZKAXI;g;VnguWd)IZ|)` zc92oMZ_UGo;D*C~Puiop4qA&RSf9sO_v?cO%WX!Q{972fcdwq#-@-{G65zW_SE9VZ zH88-%$=B7wi~s>iKS^H&`}ktXlz58t-+o;JlS17^kH?17Bcfs*aKB&u6^D(YIp8)~ z`4D`VF7(JKk9{n9z`hNE)P1|Cwlv&2N34B3`z=N5|~IL?+$bpU!6NW>M+V<$AJEOgk4A zof6Mr1u_`X-?Hc%$%tpLBNN??a(R#mZh7En4{a*m=gX<0-LJx+mEIC-q?# z6-BcnTewMz+Lml(Ye{q?xLAI(B{QhIW9YH*-)w1r+PeL# zt&9vYOlf2?ofWl*PIF^1Vz7&wwu}0AEH3}*@2f5C-?5PY)z(}J##~m~?@Imcm6SiS z+dqgaefWd=^jIn1SyEzyBn#qs(dSQdvojw*yni?S_V=kb!q=}}zLmBWFzqYnqyV~5;c;)gh4VUWcYA;@> zsXl+MieFh#UUv3OY02qRKNp|;=|s`-V@C@M^7C?!96pqjot2r9o|c-DoW$cE{E>5D z|Gvb8y?b`YvsrOWMr=&o>cjE>h@AY1u z9`0_gWEW>A(mF>6`?YqqHfyY{ELU3)3Fc;|CdR9b@P;c5^!0GM*cHo{VRW>$(3%?R zYAB>CVyVg!xU!NWOhFzh2LVglgYG9es1hIoOk_<&HbeoK1ejICpgV5xR;-)D)T)M; zxTL$Do5-`s?9S145!#;^ zMXpcbID~y5Qtu`y)7WHBFlG$M0ofWkPN4Bm$;ry{#WrXsO1dg)Zw0z>07!!K?14MH zxKag0A6)PdTGs%nsFoWBAHGiAo{ooT`-Tf>u9^xin0Vc5;hxE$-FCw3JOwUbp;ib4WtgcIq6IsFD14Pe_-pkSpp zS5-;dxllL(Xfxm5_Ab=~@Mt5!LCo5=C8DA`?%JQ{(wA#H@C6WU6rtNG)efthtBHa8 zlTty!Nnj8@Sa8}Aibrs<5a_rINCS&_YEXGCDalMpjmw29&W$FyXuaT4jf3%}omv&N zBmn$l!T<=joQ`nQYOtx#*4v5KE!RcP>!!Ga<4QYCnu?vW^pBQ+0G-S>-gA=v5GzeP zztOK}r86ec@YHesShh|qZ1oFA5J*;LMUbq_U;qp(1(c*^&IjIi`A<7y-RZTfek76)$Rzex$i^OR_xr zvM?JN@$6cI2y2;9YmQE}25aYDbD^9Dn!mvOI~#jsZ1kutX-|fsl!HQ-=*~zZYa+2#eR?vs4@sv`SBa&5GRNoAT)%faw?M9&P6Hc;e~|o@!M(; zO%=i4nWmx=Ny_vrP~V1u#|?D7&N5H{!LVm_v*k?<{R;;KL6EZzs`mnG{XtnEn0$$_ zhC2}k2FDSi+nFhDNNt1~6^I*1mIH6ZH0_ww?568~7fY&P%^(aA5{V z6fEV3!4Qwb==Ofu*#m=S6nUApzoVSSKwvIeABu2g1$nSa?uY_q`%>gjDrq@nFjPOK zu*h=roi0#^ncXInsM;erX(rkY#U%bSzPuZFrlu;a99($Rmmfqzf=C$nRsyD!hX4n| zu`uib=TfM8o+;j57fbmb8e}nR%2MhU0<-Eb>6IzT9N1Pas32UcBh=Gtgveu_&eSl> zmvc!dJ>!04*{p%6Y&^9x>r^Ozt`I(EXu&4LotsP3X)!pB==BT-;1{wQ+t403g|Rec zKWs5Sqn}i$tb?$QY|uZ^l;u}q8Z{Jlx|*Wikx#;_L=RI$)yqU~w}Y+FqOzKpt)@;n zD3`V+Mt*hV$GY7!P`O2o(PSF?0#r>3v<#TF5=hI-=fF10a;Vm^hE@?jp2B>h<6V9% z%n4t)2y`1^w6i@UYvq(TkqH`0U9NAL$;U6YK!m@n^BvFHmT)0BDR(i4fkUi7h+fLB zRl!w2IZmxgJy)oQP`IuEcSi`gqw0_h4iaWxI*`)=Rh@o^ z(_dBRG+Odk)#Wmo&R_t7%TaCH=bZuqklr@vFc3r@fWe1-0Ir-o5sVqCyf~BcP%(BR z?6%XBb|+|t!mye?>@on#(DY^2a-q_JXyT1D*qP@Dg(8AIn7-+=ez1^C)u&24&bsAx zn0|y?`>a}3S$+~H&Esu40v0#sik>tY#R5QW9t@-O<~np$I;~_(*SXPmEL}QizgHIs zPyvuqW<&rw((c^~-#13{_zG}z?PZnY|_ z1yq#fIhXexCs2EfJug+#cSfTs*>lyy zqvr{M@?T=BePJa?+WBR%2c`0<%6IQAjh`IVI9ZNLNkZk-^Pw>j5f@=Fo->VDnX%-z>%eZeE^Pu8q=Q)p_hQ=#1o7e8> zKS@l0l}~@%dh0=Jm=zweGoWrscAyDsDm$-)Pd}x+k`R^~_MV>7pOLBRlB1R02X#BI z@jh`y>zz(+&#ssbk5%29vcB$f@gVa>eX7!ZHvhBwXcM{>{_AUnyS|)F5K}sfXA_vY zkZs|w-(e&Q#0j083;H;D9wpqATx7vYsQP%Zd9S01cG5TZRbADys!jL}-{&4G&AcK2lcI5xvV3II z^YtfqB5<(?{JO7+7oqXd_paVCFb`Jz5u4g^pCtFoh8E{M@38X$UWLU(g8_9@_p6vH TKb&t*bx4GV=Q(`>7FhdV^sO<; literal 0 HcmV?d00001 diff --git a/static/layui/images/face/21.gif b/static/layui/images/face/21.gif new file mode 100644 index 0000000000000000000000000000000000000000..b98421282467fb377dced2eabd72bcc5525f3e8e GIT binary patch literal 5191 zcmeHJXIN9|w%#EDLMRCx5oHq)5g~;dB9Mdx2uKqUK~NJ&fFL1(1O*ETN)=1&;5bqh zMjZheJy=FUQGrpZ_5uo{aK-|HBPxco1LNqq&-Fg{-~DmVPM&A){q1k9^{#KdtN8l) zklfftfD!P12C%cU!{Kno#>RL&-ptI5OeR}eS`r8ZQ&ZEWOP89Pn_F93TUl8xT)2=% zqfsc7&!0b|(dfmC7u(v}IypIEFc<>^16Nm9ZEbBLk?7#y;OOY+=H_N&V?!d5bai#- z&!4|!$r5L0=Xvwyd3bnOSXe|vL?{#r6B82#gYog>#}6MqczSv!CnwYC^kvJI85$b; z`uavjMsnifHm0Y0dwZ{ni16|8ad&su)6=V{sK8>e@87>)yLN3yM@L^@-_+DpcX#)V z8#kh(qj`ymK|w*Gp`mecahozTy}Z1lA|n?pSm5vP&-C?8OiYaDax*q;c=6%|o5k|= z@u{w^j*E?TadDAmWu*!P{(gSpVPOgJ@uJjJu}Jjk)2E)Ep5&yY)lpF_7R!&xR@ z-@bjzrcHUdxoK%>H*enDyLWFuKmdosS+iyho6Vk{p6>7OU%q^KQBl#YTenuMSW#0` zBb7*EqN5)^eE9hB<0ns^tX{qP-o1OdIXRh`nJZVW?Ck8kdiCnNckdoOdbFdku&k_X zWMt&|^XKQ!pTB+kc5rZTY;5eEJ9n;KyS91r=2fd!J$UdSGb3Z)zJ2@m?@vlf+O=!f z!Gj0e+S()%iAW?03=9+sg?v80tE;Q9uuxrH{q*V6$BrEv9v)6lPahZNfCMqi{8yXt!-@nh}@d^qGdV71fZrwUQK3-K-HSpxgxpU`Ig~GA1vE1C;vuDrN z)z!Uv^(rwT;lhOrO-)TvQBjkVlc}kx8X6k<`ubEVb#!#}?%lgOIy#*l9j&dcfBf-B zd3pJ%Q>R|Pek~9P5)u+*GTG435DJB2GMQRhT3a@64hsuIB9Sj&zD(lr4jed;mzS58 zn)=gEKh@XQ7Z(>dHa6zx=c}oyy?OJ7%jL$z#BANXxum2dKQFJgwibav7#SIrmX>bV zuz{D5kdcuQ%VxKU6JY15M)!yE|Xwf1N z1OWh;nY}D#G=sdu!|1dSZwDL!4ud{GOOb&gAcHMtLw9#dhL9b%o+}1pxe2@!5A2&u zO<0h}@xZQf4kQE$>D)vfb5j~OY*SEp+@|$$ZX7Jd6LilYXYhr5u9yvG@RL(SdmnDcl%E!Nn z3%&eJeQpZG_cVyuuo=bY=}#X&Ouhf}-Q>jE@v%3rU%mX}pQ9r$p8q~P^z7;2z>~+1 z9zM8#@9v%3{e8VX-CehC-uSJvqy74|t8J|<&GM!zzh1s{@j~N2&Y%0G;cR_f?U|oX zpE~(dP4$W6Rh1RxWu?b{Jo>|tlH$XM4j$OQZ?A06?p-^Jb`%!m=jG;X-qSi_49c?X5lm=2=O%(xGfy#r$2ZSmIfEp)MDWDc=1OUhxU`E3%k_DFna223?kX=U4 zM+5FQX;t1u^L#=Rfw5;#selfL!>*|6)L&wKq_M_gTdA+i0TobRpR1{D=lDyN4Q9v- zo^R*=2&BaZm=Ks}s1~t9YC1Z^(k=2R^Ej^e)bEyI50eSRpS5A}1;_eXK z(D`duB;Z2EpNsR)noGoBP5J3o{!vsJ9?9EW5oCeSKp`?Kw|5qv-?DIcDj#euHB4Vg zx&W$Oh&J>$npFAd;0V@=1c&|!03u)VzIf+)) z)?&#IX#KLvOWgz*tfIqSwgw|~S=|6-c{1;LiHa&h89EJ=GIZL27N8DjK%pZ+p_}gY z8KfAWyU4Q{N6W1xn1vdT7Bg5j*~rVtM}O9~s@u+lGYzIX=!L;5i%gsnSuLFs7;KgY zv&z2mAXdo(W?fnHcBH;-OnY5KE=w)r= zi+5yi(?Tz=3)$-#z{<2(TF;_>HLLVN z74iZ=+5$75w-XZDo3!9oCP)kRfR3u#)VO6`6)am~q*q4WYsn~-7y>Wl7|g6VOnC$_ z00HO&C`i0~7Ig^AYI)KTw(c5v2L7wt`~59`OwG^tp^0#Z)YM>)XAqT4hGZ zfDqpEo2^g6==<88HzoBbgh{W6MzKuq$J|am(Ak8T z7qc>8Sfl@hE4k)q-RPU!O&B1_XF_+s3zOugSLsX_C1U<@Ws4Kz96X8d|VQ5oFLC4Km`6SWnWJCRc@;5vZr+(y!<9Z(RFaG`_yqim#$?8-5SI zuPJ}=u(w{#`hHzAGaKa2`1VpS$z{cl^Q>jXSY35R;M=LL0kkeu;2ja8ZLMwwp=)DD zYwtE?9TN789v*$#WCeGBX)w~&)3xTf^<>y2Mx_uSjf{uuqI3~SCs!A4N=%||lc332 z?(YSGmPp%pLjdD7Xc~$($J&BIxVkP(PaI-Wk$c>KkC6gSpt0u?Y&NP&)N@?h2_0Kp z=n|#@bNsDFOuMY(Xs%*<0eRw0N2#f%pETB{d%$eN>l+7CbTXlvB)SJ_gEiaBI#;?z@CEe9($%a!yn{{%xZ6V4h|b__5Emu-`QJq{2IO3=co9 zZ}$2AMWN*n)mCuC>u6Vjh0I^m*=(>~>J>^slN_{=|9-4<0A&!sOSElm#RQfN8Pnda zT5WC{vY?33dV{j2ayOw(yp4T^$+@L9@nTPLg~YDKe%c~d+m)CHrcb)QFkhPY>&MRx z2MdFm0u+i5%X@d0S7v?ouP+%3UJ-({ij=Re!5UOVlfuvGE$TlYUqv&EH?db%M;P=2kO*LAt_IH~jgpo6v#dPy4J#E5`_+&b zbGpGr-Kw!tl-JD1G0aF8)Z1oAQ<&lx&AH~=Ur%>WVEqp47AlTQ!EV2%Y{j2PseQHU zN+b)`yO(_kz77&{gT)S_jlD5`CfCK%!iG( zNPEFJJtdF>S5zFQSDO5%R{lS$l@4=l@&B$?&Q23w+xikHEtWwe0|ia9gNO`mP3x-O&QFJ!nzJS0>tLe>Afaq?fjZH2@6bB6Em!&%OdTG4OpzSW zvKf5ZweCkfqvuof+bMoINu{1_n|RpjI<#Ez$`KHh&K2!BRbR)NsM!JG`(w@c@td9n zu$caTo+w8!fFGu58Na}vd0Hr@b3aaDvJV$OG^I{cua@rc)deG_hRfTk3-rh=Kw8iH zW}5kG?LWix|8kgSmRSp?EISbMtiTY}Y5ABrMeSunmx__UEFk*M(YMXic}%KQ6^ZGV zs-VWUKhwF=xKfJH+1d?58p)+CNaS#VmsYy>P>(u%r;S`jZxMD1`24{hih&bB3JY5=_{)1Y}iDzqL7DO4o@Y9LV|BQ3V8gQq z`;RItYB%`(qcvGM-TK)X_tRxUL(i(8+Y_HYsCjnI`{9Ym`%Ub<**-trIdW-VjG#ZhSshwl2_A%Vd*s;+52po3#knqfY z;n{tu_ZzvjFuppEV>}#uV5#4u{?80e-qm93!FtP~y9Wk)sxGNhv|GV@E%4wW%flbp z-u#-;RciY{>!LrOHQbqFxK}>hyREsz*>E}bkuLJ_ZOPEp*~THw;|sCBoC@su(tYUS zbb}^JS3Tpwh5X^J+=ulxU(OK3x|WCRdhb`pL3EeQ-^ zmTV1{tUXZc;!Lb+BU`RPIN_0Ygh;FnNL)rNE#B$2d5HDxZ1H%l*w zgTe_V1C!|jqSP~-3w2|UBtyRV1(Q|M3LoWotNf1qiVEWSP_ev9*X{1>0A_p_Ri$n! zCUb?Du5Bx$V`R?Mzwtcd_1{Pd%%#(SVfRd|?3l&c&w&a)PQ{!IgqFI+B5K=oORfqS zJ~d85cW@dIbhoL47S?{nlWN;``US*qjS}^@BK#RAp$s~|SwRV1)MZ1LDu57Mhji}+ zY$*gpZ|R`_xCj8~q$CC@%3A{lWeP25+KNfUaGX#+n8%VF0N6s_Oh6M;G88y>vTbJL z4#U%8Eav^=rJC?sUNeyjA3|l+l180QHek;cq>1%|8Y0T;=qtPw)PS%9CSGBi$9VlD zm(C|lE_%)@VePYp9KO~SYvQFJO1Rdz(VZKv>r=eo6%lf%)-Cc0uPg{Tw2iJ|BPxpR zqKsP{+_x&oAHVIW+kk*RjwA%^LV8a=bjK+H+IEeQejCQl)mz8nr5GperZ4qb5*24& zAl{C_lS2?}1LV>x8!5IFx4QW^tB;NFrRJQObM}A!Uo_{;JkQMY?Cn2)m~&>%nK@_X zoSB~+|ETvf@V&ZE|8eFSXl3#s7ytz5fEDUN)|fnq9HuH%*7A6j+%-v_@^LYooc5tS zG{sdeq9jEGCCal|5%}crrK~i3u+>!gYO5%95P5|IGYW2yHCTyrR=~bU*v2q<*zD6k z$Wmit{Zf&YehCSw(^aXdo`~m-lHNWfq+j857s1WDKNH6F4fIM)cl_Vil4YvJS_adU z1u#8HneuTVlAM;O#H6??MdOkp@R7<@d(b*ovC{s|gOri3Ks*-BXg;#Su#>(F#wzH+s2rZT$Isq>}Z#RDY30wAMj5F~+N0-}JS zWLQl0k}C+gcFG+{6v=@~B`S%#Vc!zi4_ROF*}Np(o#x;288m{AD(p0Od%GFlE z=o(hy^<+XJLYzR;Wo;!BljE+3-0X)(k&?jIC_=eHxk*(}xVfl!ixM?_sPr>wKxd*iYW;q% z`9O~`&xfFzZ^y5iZ!e@KEk|%zCb`Nr35#VgQnF6ik(r5zJD1sjZXwe0PG}16Dv8NO z=0YM$Py{QNkod%FG;v%=(&>q3VuM|pH5%ISPl-|YzSrrZbz`asLoY_QI=QClx?@2H zKCt3sU4>TZ>UhiABR3~nM&9X*Pbk%>y>KpmJh2W~09x&{@Ko79+TSWA(DS3#-3}GV zaOqIF{i8|?FB@-B>CwE(n3;!(r}JlV2FUgp|75nRE;qBqqMpq@Oyl)qD=0BZ9yCnG z$?Xw>8y0{YTUx;0NCfM;#}}zEf^cy^uz1W6d5;<*3Q(ugUR6+9*s)Vm7|^>W_54i| z)&?KW%i!^muneOGO~l7c+z^jSSYEUx|0M}~azn;#b+(lchvQkr%W8HFgt%$E18Ne} zD>2WZlm$Y&$qxA3b=5pv2^h4hAy-wAQ%sA1{3Y#KsMK2_5V2@IO(`H*M_b$NewV_H zMkOpus35pnmGAN>ozrwyM)bX01L)%fdq@`8(9%r2oIrIeYNNGfAb36=S~XGUhID)V z2_GlURkv>DCpVUyZ^44(&>4qnIq4>;SNuZiwP^y_SPCXPkXtWm7Gmju(y2*{X$1hb zb*VQyUnHT-)2j5Sgxy4*@=yi!^a+4Cj8IjI8I!R7616e^;L~9uh7~$SLB7`aEWr?*m|`WiZm>s5cFiv5d&UDpQo;v<>?#zP7Eh4aFT0Bs$OyFb zZ}5va{31L*sNP9TW9fHzNlg}3p(*3fKP}FZVije6GiH}~Zcw-V6_ov3VAbxuXI=$n z=O&k}vCJjB046yMNH%-x)9WOGw@ttt-%()Zgqag&PS{&|k!YS_<{4(5VQ=$Qy*Xj# TguQ+mZP{ha!SUN*Q#T$ZGKvWRLv@}vHJ9z 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 Lls229f${$VzcBA< literal 0 HcmV?d00001 diff --git a/static/layui/images/face/25.gif b/static/layui/images/face/25.gif new file mode 100644 index 0000000000000000000000000000000000000000..0b4a88322946c1b65e9236657d6f42c71e412a94 GIT binary patch literal 3127 zcmeH}Sy)r~8OBdG60(?tEF?im2r+;L1lbWy5OoM5NLU65Q$b)@h6?I*ijH!^Ie`Ra zC+-{I0$SlgMa2~iD4?}c!BMND2!czsN^wC=U``U8YM-`Oz34^na(Djk_j{LoC12#{ zpTq{(z%U3*yz6*VPh5@91jVmp9hk*h_ue zPJQ3W9I7V2)w{mF$m+;*>u>N~s}C$^0`3@1m;r!i)ctaNxQyEFz(9+eC`^XN3!8BQIW zt(<1eq0t%6P)gK1?i~Ktd>1RgD&R3clgnP50)~j$^r-=)g}=2U`P7FrwO!vqU_*lU zFw_kbR;~X&u~yc;$&E*};A ze5@dpf)h2mKgvz4`5VTT6Y|IUpn?~B`O!Xow+E^_0HQ)fRwZ|q!mi;yyT(Q|dNO5( z(vaIEBo^5_+8bJG6foKk?C;Q)qcmHc(BdBLY_wYC6rS^a^2noz&3GFqSXDV4J%vAC z=ygah&ds5MJuT_7^Jnmlu+);;JL9n6_Cf`txBd`g4RhfCW$xgc}fq5F^bFhU|C@tyaSPrXmiWx_-;j z5{OH5aS8B-y#rjF!nZ?Npn#VKr||^yXmubK7N3GoHlI6xp_vXT&t-${;6-psCed4Z zju{@l@;WGkgg5K1S4V?6;eLXa^J0PgH(&%T2jlBr%DGT{sEszJRRQMz+ZQ1&fFZ;L zTCD{?98kf}EP*W8;ThJXyzh2EtH5btM3@+smE~CvCcpxi0iQZ?p|$PAT1a%k0lb8~ zN*V1XM=G=P7E0aLQrUW{8%L`2wu_dUPcC+qVwegWOwJEdU(ghkP-diGnZMYcPcW7@VbmOIpx>pYmRCzed%2X+OamT;YIX&wUMpf-{_n-e{s zN%$P^C$0a3oe}+cXRJY+JA)~xIrynF;Ke9!5mD7YqETbKiJ6Anb{rbZpvC2^4Zc{! z@Rbq`?@=ki6uJ~mORT8L#Gz4nS%;41Un!I3?CJ-SYA;dAvBLTW)x%B-Lr`6rn?L$k zUQFOV%IqF#gC5}Bdi>;-4rr1qs0)dcpyspYF!zUWfX;B1Pw|1=G5GRA`1$l9>H$VDpJQo!fe^lQ++|HN)tdbJB?X#PIO)x4gJ8!2~2 z#cM>{Dvru&7`$UFnDf{c`}j!Jz`M*RZyV@ z^7Pj#MM@!7tHBCOaFE%PsLDbV6GZ%IZL;I;y%5Uc#Be!-<|=i7Qv73~>`5Pbu))rF z4u;KN_alhm#Gf?TP=UVBjrl==;SVbw#biM?3VSySG~J$pUOaa$xC~86cY`RyE~c(y z;VHb7q8o$qEoz*V&^-!AW>ZrGnNNZrm(c~e^_=&;`>AxV)ar!vH+7{Dj`*x8YjbMB zHIxKdFw=fb%KE~K|CGA|=P)WgdLGg&yjf|cZgHZsr#XXx0-Pulm#|#GXl8gM;4(!x zK_DnH%g;`r(Zn=Kf+dqU&=N`%R>FP2!cLyzQz()2zGiX$kx5}ne_g7o0A|~ha|cB7 zLV~2(5Gkyw#S&tyLY|H|Y{a_6+>X-?Jf!k$DT*zx1Tt9>YjOOZusexEd`ajc0VncO z5C&z(?hjceaQeanUKQm*8~Y?)J-YnWH9d5H%9D_3Gk;f#^_)%f$Z?0hy@IC_R`#iE1Ud98@p~5HAYTDuK4U(zc<#GppqcpSq`GsG$aR6;x2k0_?6-$i z7m=~qrPV9qZVTWbdZ literal 0 HcmV?d00001 diff --git a/static/layui/images/face/26.gif b/static/layui/images/face/26.gif new file mode 100644 index 0000000000000000000000000000000000000000..45c4fb5563081d684f00cc3560df41b803cf7bbf GIT binary patch literal 3291 zcmeHI`#;p_7awFU=3N(!aa z)@F63%VJeZ7o=9ZQnt0-6xqs9JUi^x?ho7dFZh0*&+GMhp7S~9ywBzRI**T^H`7C4 z2s4C@Ps2a}4)C#HI~NoigHSB6*8!HOC}Tq)*G;fC2OKPj(FGJVaMc0jhEI`juo?%# zaMPRdAVYurYr<3!zjB>V21i&gak{lAP5J_jKOvfu-^l)FrbJJ zYMHdu?mXbW<6M<|Y| zA7M8>`ZC!V2bA`phykRIr;IV+AX^n{3v%H74Duba8K{B=jqE_49XJ^VR%_n&!S)dB z9_rY26KnuO1C-jd*p-SfdSR<_g2Or}K^W7S`c_1>>H(0|FnoE2Dtn+MKt7mk-&j=y~ypsn%lmxoVC zdZ5H|I>KavivdiXQX}mqD?~$B*i#4WNk77aw9K>q-nW-Wl{i>Tyn5Y}zx7_pjuXpm z-amiVI}dkG5I%)vQPqK4LQ?mp&HY5Z8&QJyH1vm&(Xlk* z^NW`zUCN#(D_Jix)u)Q(37JxkRKCK&g@>X)-I7O%@1c zF(Q_FKO=S~L$fN%L^j!T<_9!`=_ON$Aa{zZQ!JH6gXnY$&CQ)or#V6{RGN!3RsGVP zXl^WbI?IIt{r!;DXbNE*E12i~Hba%90aga>y%$4%zw(V#U%Kf!(Zyzdy@QnyeH^V?};EHd*b1A{Gl- z3|Bs#>dBzWidmzAMzk)=-blj~^x{RO9d8z5QeC&FIMMSHmw~Jb(7|_o3gO{5tsf(ZIw0Uw(ej z_tX7*y?1-MyYAe+b+hxv^^R*-ueATz*4pyJGV% z-LO7InXFhRmq{f_iEG6P@o^$yY>Xg!&FZMgi14sgD_1NJ4G9hkTo&N})zT$?zCPZ3 zo)_1Xvv?7E;R2QilR>co;>YZj#w?Q2>&na`qI z;P6A9*UbBTGj5qHeb0NMbcesk>yo7nt>&=64j3%Gu)bZ(E-AD_%hEo&CC|scp>@Z? zB&#mGyRfCTjNaG9(VE+vk+&0#)yRV{sBXxHXUVS)?Mav<;AeZMGt;}7F;7r07;<#B zcYzt6SxCK7!7kZSa~aA~zn0s5Gh?nELQ$(3%P%ybUKFn@4$Lx4U+a6kuT_ag6*8M`RjFL!A)XSUC?DQd$IFpPS^kjr2)m5Fadb!#?~ zU?17hwYm<~GGKB$Q+BxvfZm2h8moZb!vTPlnm{-Fbvs zTiy8VW>&3@PL@xR?~6<-^j{c3F!98{F|xnG&BMSuQmipmE8=;oyT7A{X96tV-m~NL8l~6+F7aUlOIu@M5({Ic8FO>gh^2MGk_!BcJ BI{yFw literal 0 HcmV?d00001 diff --git a/static/layui/images/face/27.gif b/static/layui/images/face/27.gif new file mode 100644 index 0000000000000000000000000000000000000000..7a4c0131dc926ee932804276038bf053a5cc1634 GIT binary patch literal 4377 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 G^ZyND2?^l< literal 0 HcmV?d00001 diff --git a/static/layui/images/face/3.gif b/static/layui/images/face/3.gif new file mode 100644 index 0000000000000000000000000000000000000000..86df67b7aad1ac80079562c74945d9c4ced3c397 GIT binary patch literal 4017 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 Z`!_Z%27Br#6>oByc-ZnwM>1&V{{f6`u%rM0 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/30.gif b/static/layui/images/face/30.gif new file mode 100644 index 0000000000000000000000000000000000000000..b751f98abcf8173ae3c08402c99ae885a0dfe162 GIT binary patch literal 2555 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/35.gif b/static/layui/images/face/35.gif new file mode 100644 index 0000000000000000000000000000000000000000..a9322643dae616390d8263e8cf1bc4d9fdfa03ec GIT binary patch literal 1800 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/38.gif b/static/layui/images/face/38.gif new file mode 100644 index 0000000000000000000000000000000000000000..8b1c88a3e8658cde7b25b6e063d23d010f7ed86a GIT binary patch literal 3615 zcmeHIc~ld39-o=q5R!1nAxDM?f(RjrT#0}Q2{9;W1Xl3ang9_}4T&Mv=qe;!f?|nR zi+8-Swp;6Jz1lSiqLiwQXzQ(@Xwia#r2;hxJ3+DAee1q`uYc@6J2UUi%x}Ku@ALV7 zW@?%+fi3k0ynz87P&M7p-d3J?AIn+ z>TyrbxZA#)FnHd}b|u>W(>nV@wWIg2{Ri%ajee)AiT3NMw#(yxJVmhGTlDd~G4_Y= z5BHyP{A_;NmS+1l@b)J-OU)QZ&;A_?Ny9^J*AK^BXb8W5)XV;Tyko#*d$RTF{)il= z`-@AHtviAo&vu@ujdk=Mt;=$CJlopZ;M()G|Mi2uJxxK6kNey{Q`G%!`q0x)zN(Kp zQ61iSlKy!mudgltrIM8&!g3GsE`hxY)Zk z8H>Z7T$npN*g9})?)n^(Y1{0cmZWbE$sI%Y`mITQch|jaBDd`#{n{E(HU;W?uz#T0 zy~85Ca#xzP4>$E%C9mmO;k<$3j8ne+T|&dFVJ+vEC^yOsStfi=#7RV&meNkuZzFWJNQOD=vx| z!-SMH-EY6CJ&5*)saFNpz*UFq+9`vz;+mtSH1H zs$_YwMyib}E*Uo(K_Dy1SIbozxw05`MwI3$muiw|NTsh^P^jL9EiR#tv_sEVDx$Sg zRWy^%aJDpRn33_HO%;l_)+L%u*>Cm!+rTB+%T==IOj(I?sXAYVn2&R&QgQfdnN*`x zXDgM9M|V+Nq|_)&ij*ptpA`p(XGrtq#m#wR zpTdqwWyPej83HCVm6^hh6$<0nf>?G8Q^?@QjQR?c`AZeDV$G;;!Ee5?@AP$&L7_r2 z3uJ2f2eJa8TB(3Xn&!ygT?^}-@!t3ryt|f3@AQpEiivhY`%h4hE+PDJKE5R`^75AY zvSNhqYJ}L}u)}V9_3O~!z{_9ye}2)|`}|o?_fJosbp81FQRl-49qsq;-Mw@B*3BE& zfB62|)wV0v?=D}u_-*SqEf>x=pKG$1O=ti9^;c(3pE`Ns%j3uXcJzxQpa1om48{W zT)XUprJ54;5|y%8v3Sw@@}k0pvV#0Psbs->f1W>Y?ws75*?-E;%A6&hIU^%|`m{9B zRAH)spTgr#Nlr?f%wZ?Q$Hm4>Vlf%f^r*;*5fj40#)pQCqXom%puhnCv3|Zj-ebHx zJ=`f|k{i+0g@DIlF=!No$V2cDnot1ffJmoiIe(%6ei#4=0eISKEDj&r2{4aHn?gwj zajw4k+?sfg0|}BYin!K8uuzmssj!S8#aIGz4XG9ULf+%q`g2pSb7?79okFtGb1QR9*h8c^l8IGmiuNVyF8j;l5v zXX-)M@fmlp33o$GxG*uH*Lg0BJCBmgh2WLt;yv+sHmM7P;`TB<(bNTQn0slDODoe zmZWkkKJyVi3oIL7LuqMht@aMJ`dXp=)5se=#-k}*V~R(RE)(6@TUgsb(j2>Hr0}7u zr^+I9B2ZKbV$1M&yw$AJV{k-G!}NA?1(2_38RX49@8#Dx3)gPLp(+S*KrF!ZHw8k+ z6|TnHTlG|wTS1wx!`wuD&^&qIrKgmyaxHTa^fnnGEV1n9g3sG0r|74cXC>Bm{cO}x zpMF14HPADHnHad$VVq8V(e~X>0}i;!?mrpd$){2(+ne>cHUqD0po(Eac@s6|q+qW2 zT$K-9wSXE719~dwm{knL69Ev7-`AM8Mj|kzs7Rrvs#=$8MSG!njk+dGAVFgxClKL% zhKNR>62s`fVI_pmH<&j1%!l`E$ev9Qah(`p{t!kYCq`yztw-_-d;+DZ!fQfKf)(${ zwXFRJwYw`4ECg%G)N-phvuXwobbSx+7jDsQrm~7k^(
  1. ap0JjxBHsOrGeE$JcnI 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/4.gif b/static/layui/images/face/4.gif new file mode 100644 index 0000000000000000000000000000000000000000..d52200c51ee924f0b91132e5cd3737fadcf40ef8 GIT binary patch literal 5689 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 E2fD;K%>V!Z literal 0 HcmV?d00001 diff --git a/static/layui/images/face/42.gif b/static/layui/images/face/42.gif new file mode 100644 index 0000000000000000000000000000000000000000..0eb1434b4e81d4437098efe06cb4e1ab44a86211 GIT binary patch literal 5305 zcmeH~Ygkj~0mo0^kOXo&KnQ_Au3TbK#w}bT)dT{>Ajri^K(Gmy8kCK&isJR;LP$U^ zkqSk0YrG-a+6lUjwbVgCMJo$M$H`}j>#dkk(v3$GV{6*}BFogLpYc0=u zKR>~-Jn6LD+if0BwA`ZyF>EB(yK615x?nd;V zkF(F}WncgC7d>^znVFf&rS|QWQ8Qz^F6|@k@jx{eP(~{%ERT7eL*BjtR{s)r*-F7F|Ic4F_%#>Q{@-nzWU<6+;@FF#!;2?VbE)@$TkUDu(F{Rab{ zj>xqV_J=Cp+vnDtP{#JY{`!;Ax~JbheE7xI8*MRH58y{m2Yl5|ynp}x-^TY0o{Y7O z@9L}%KkQ4|>W6s#dHUGze0%DNmS_z<3TiIsq`S*!pdSL;?WRoB~#w?QxscUMI-QzHu z9RfFt7+c({YgNI?1e!k?L%@)Wi~Y9-|K_~_X=(u>Fwl!okSfiLI;q|k#b6fXMPxfP zWJlzIZvIi*)S+(cq(Ugdb9;klgiuHX@S!SSF~^MzqM#h=W@^q~z*dhkUw9+OgYF|l zIPaA}t+)ArkBpaJ*8a`aY9@;pDSq6|cL+%OV6|KxQ;CLH8}y+j3Ao&wqLWFx(l7^b zTw*OGHLcQnvRA5jgK3vVNL2ozGS^-|G%>1RdgzeyZW_SSbJUk z$B+c*`WQ{YtbMo?Ej#6G+>fiF zQ;MG5NExrIbjNwF3^L}Pt0P@?SS)QTh?#Qt1NZSDuzM0)jI5Y8*!MmCc6h1zE&wRY zpP`}YlvRH^dzkmX!17J|9@ctZ@Na^XK`FG;Mr$WTT>pYrZsN`eltY7)r(I(1OIjfU zVPwSih6qnc5VAn)mvSg4+)67v$MnnM1zbT0s)XDM#-gzVJ3X*x^kVb!(5h-+)Ra`S z81<{5NWYfz6cjyvh`qk68x3=fLE?+`2rqAJf-dN>zYs_CFE}%D(WeJZELII2s6Ve& z24K&ylRkb@g`gF-pg*}OI$WuxsqC7e8)W&N5YuCC z#<-Ez`fb`JjnO-Te5gsr*Ptkjj>1h_y+0{sITre1O-z%v1Oa|)C{r5VG2lbiRhwK# z`Q_ns#nA9rXDSLn`{)uBT?xEHc^HXSJn4nYkQoasGLXv((^cs~US0(6jGXYmhVYyWZ>ks7XK-x`2II^ZVXSyC zBEIvzqw%ne#>6ujDOaiyZ`*&?^a~*7iR?Lq^G4Nv`}{6;Byclf2ubG?#IYF7t*qrF300 zAW&(@H;!-P^HG9<09?GWo4=+uIVztMe&uTU^++%IKY=p8|fRqn49 zmG`|BdlYkxv)FQ^aVnvz{PLEL>eRmzdGk6 zNVYb~_q{JtiwFpzjP9{&V`vpjOZfla(xZ(XpOgf7bio~uqS|J)(Qa;O5=Ojt#Ftu^}7U&!WricKDW0ccoQSQQT;IoEgc;-VL~uu zB2OCf!=|uBQ-s<8_D!HOeLZqi8XYV~lqbw^iYkT+q}J~HE%3VjdZO(C7wKd zj&>nYT(nOxv(Dc=P#~x9l~o=uIj5)#OK(Y1 zb{|}=4ZwzM$d?~gqwrMW%IBvG22~Eus-hvXk8#C{f5?AkBHxgXQ9zuy9TSgHRkc*% zBl>`W&$>>h=%4=62(nAKUhOGAbt8mv- %FKWjtVdL1P%I)BVn;-eLqw9kid`E}a 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 Sh@V0@dM8BG^xew?0{#K$QUnA5 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/44.gif b/static/layui/images/face/44.gif new file mode 100644 index 0000000000000000000000000000000000000000..ad44497691d150ea5d02a900231e48ff519d9616 GIT binary patch literal 4126 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/45.gif b/static/layui/images/face/45.gif new file mode 100644 index 0000000000000000000000000000000000000000..6837fcaf214a394d8efb68f5d48c9ec6f9ea1fa3 GIT binary patch literal 3417 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 Be_#Lr literal 0 HcmV?d00001 diff --git a/static/layui/images/face/47.gif b/static/layui/images/face/47.gif new file mode 100644 index 0000000000000000000000000000000000000000..58a083611d7c1afd95d136e3a7fa798c86d0c2ef GIT binary patch literal 2333 zcmeH|YfMuI7>2)d+R_WymLAH@(krc}g@UaIXysZc6v|DCFw{lML_tx9cp184*`ZL1 z8relh&=?dGG3o?08na|UL^p_06A@IL<1U+y4ZKZ27)xB1?B{;%hyT9c&-c9VYf4Gd z>GQq88%FK$%pZF7=(9wa`2rr;b`38_u1Ni#YoJ?-PQ<{Q1Mt)dw;~qzlI6dI!E6&; zR4S&=!}MYJULot>=bAnPrxl7WG3{Y~-ZMS?R7Fmx;JV!ZrqlUU4ZS8>Z=`Gh;F>w* zWhsn$!lPWuh!*)(43jHi(gHKv;NC*y$D;Kw)BTS}1Ph!lJ4eqYW z{g%g>PJ`Dq@YVr0ZM$bS!gWJbUl8S%5Sg+-e-LyD{YIE@A&eMd!^;HdTx;!1^10x_ zc$`oDtrVHP22a+a6LRQY#Ggupb_~Y&@Tv?ZN#um07ao2`LhsAe+>VK!o12r&xo0FU z&rUEbOVoxerXTCRc;d(gnBusp#@^!YJb!2 zEjv?U4%vf|HLNuvF}0Gq+TAmiuS;9EgNm>?9F_(VG~2`EI{r7h$iiJ^X3FI&h0z%4 zL6hltEXxP6ib9p1!Xj|?EmBaZ#MNqCo0Ah8sZwch=Bm7K5>qgR3O1d7XW6dOArs4w zeTi$Z#&Hh_i;mYzcq1Kd6%cVmEO9zGLXsFOmM(H|d63aejf+oa1m%s#@d~JB5|f|p zkhEvl9ORZ>`lH{%a6?t;-40NlJkhCOSOwY!SO8(U=fBerAgKw)j~WY_$x-8%lobMT zW7F7yst~s7Xu+w;z)Ft$W}2Y_4NP#ueD06(iH`OLp_|x4(cOjy1niZi9*xp*|H8+7 zhYy*DhrPq+6`BzeOuFpBOEN^>#J7Aurs3C{ZP@)Gp7QeZ;u1*0eVkjWV_tTGL5lLsZ%QmVr#~iiiTQ&bn%& zAUN6hdnd%DpD^<88eN#Qi{-iWM!ilP=s2j$lAgWD8yu=<+sn3hwix!OON`05TM$!!`a0qYQ+D0Gm zjNQ!D;g+hwM9lVZCZ`~Ozsn=@Xkban)%L+;Zpe$0I!N|e`gP{GrlKDW&>WlF*Kz-^fm&{KNpQTa;MH3VbMTSO)Iy`7$cc!}$F;)1yn)K77`oAz1q&dbPwMcqZS4-Gw#0WxD0%0Pa38b|9mayJf#kALK}E#J$BBuS_BN>|>~!Pl rL2H#EiEFFJCp(ZsP5Wj&2CVH=rzDGc@3T0Qspp*aTJ3^`42b*_XAT7W literal 0 HcmV?d00001 diff --git a/static/layui/images/face/48.gif b/static/layui/images/face/48.gif new file mode 100644 index 0000000000000000000000000000000000000000..7ffd1613b5fa36c3ffa7daca94e7e8cd6f8d82ef GIT binary patch literal 2689 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/5.gif b/static/layui/images/face/5.gif new file mode 100644 index 0000000000000000000000000000000000000000..4e8b09f15101f3e3fad0777ad068fb559ed483a9 GIT binary patch literal 4567 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/51.gif b/static/layui/images/face/51.gif new file mode 100644 index 0000000000000000000000000000000000000000..ad3f4d3a8158106dbfa48e521084e6c56fc6406f GIT binary patch literal 2785 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 Y?y9hKVE|Dl+bifDKf^#>4e-@}0Qq7Ywg3PC literal 0 HcmV?d00001 diff --git a/static/layui/images/face/52.gif b/static/layui/images/face/52.gif new file mode 100644 index 0000000000000000000000000000000000000000..39f8a22846945358446ed010e2b9bd50dfcc7a25 GIT binary patch literal 777 zcmV+k1NQt!Nk%w1VHN-u0Oo%H_RysE&7t_xrK?&j)|+hq+_3!Ft?IFT)0AV!c0$aC zRqD8h_|TsJ>&E)ltKOw@>9vIN%AU!BNyvIc?7@=%;kM9?T>taZ_{^H%oM+RGRsZhF z`r5JEo^R2GQ{9+i_}Q%gdgjaz^&bp0Z&w@W+~| zS1ZnjO~rUYx^+wGxrybje(A1!?Z1)y)1|L4Q~-nP||Tg`+@%Y8|vQYP4yVcL>g zvt~NObU**!w6tnI+^U8B*sH*IM&_z|#eGZp)T8>;r>$c?$Bk&qfJV7zHKbH2=(mZ+ zgI36gSAsHn*{k%yj=+0P^2nRuqi?otLDrynz;{Fc`{2HH zLjBdIzja5VP9(HtHTc)8{no1Ese0nAeZ78I+ml?_ms$MWwEzGAA^8LW004ggEC2ui z02Tli000O7fPaF6gnvseg^7xP76J~4i-a$bjEyHm@r9@V1n%lQKHaE#3h9gAw5QHw^!C@58A-z#?=7-ZCbD#S>_nhy%@3*|?JLj$r zU5vC!qK3OKOyIjL&=f_HLf;KT*4p zQZHIL*>mDv-L`YvVqf%rGdps+K56x(eBtbHo$;}uzd}0JS}|NHxm~Bf|9!#3hMa)| zDaI$A1BX-3rLP^W*F9<~ynSN(aJBl!)R389s$bvw?#gaq-51dF?#kK0W4(oOk6Vjd zl)UkdlJ?B7ryW&o`i)(Mab3EYQ(Gg(4dpMdA02BcGLHN()!#I5aO>55Th64cdC-(| zwOC--@u~4nqj9|JV(vQQqt;8=(I;hVCN5SESIegd&yKZyJy4;Xxn48%Yi)PVrrDum zwVPMYj&|Ov(T&s>bmqnO6vK~ON+vEGtV;-P&x!A?*#7eJ{->>_51Vs-Z`$|ZOwr`k zQzuiRjZ=f?cYHSW^ZuKa>Y;{;!RoB3YgL!?HW>SkyuMb`y;nRv)bQ$7qwz+=!{*|X zim<)|Dg70x&o7rxc9a*g78|FA2CFj9?@E{%ta~+Ff2TU_Zf%CqXmm4LNAO~zIqXOt znLsk12LJ$KS|1(>as_Gu^7RkYC- zP{Pp}5i!DyWFb?83tR>IYgihoLJF$|phhZ@saTo-oIoUxhgrzjWG3RkHxPAl0FG^1 z08KGDawQB>34Xpp5(I%X8UdnEX*9?OB$FUAk%YW7Ux>n@(pY3V`0l|W(UhVstSB!3 zT`Xi4fJ;=X6)Yk#EiH|Z=0}h#6NnI#$u#*OlYJ3{uPR-p7HE8BD*T)WF02wN#R|1p zE(1*-1@ZC}bpQ^@^lb@JMR@po$1)Y+O+f^qTuRgk6hw$XB1)yEy568w>L~a>X?zf^ zib+?%#3)!LPf-eC1dg90Bij9WAQKSDhP75HMm9wt;mU<6Qdp)A-l=L?+7Ba#f;S0dk^gpjWs+ zD3+NF-lh$DTQ3(@ic?_`Un!S@Zxm*UKac^%j|uTeTr!FOzbb#r75zV|5s_qwruO)+ zc9}CFJ!l%fZ+>L*{`kN$I5A-hEE2{GHgEc3W88+%KU=@WKC$uYCez431$Zct_tv9$zn3-G%Cf943UTgU!N7ry}dk_dAQ?oZlLSO zE`RyRd8yM9$Hfkd?Coq9E|_m)ZH2{HqEQy+X2=!eHf@1*0JMM)a@!!k0Y?C^G-hda zIt)gi?Vp>kx0Ypw>SM!KI2GiF%B@^;w6Q!MswCgqRYTBm(HyWU*Wbpa`euwQ`( z?ld}ph9XzSBFsm1-tb@LJnV#;e?cw?yRuZ+b(fAQtFN7aB`_E zNo`e`D+i->_KEXu((}=12xIHxlF_CMVxWVt_~h+fx(#$2+s{26mvxzO&0IWFah{&+ zHZ~Ycu#012!K9HY_Ixv&V3Zp+BG}70G0sXSEHbzCdVyK*#_hCN*q4 S8f7`(nZXDyt@tenSosGvnoLjt literal 0 HcmV?d00001 diff --git a/static/layui/images/face/54.gif b/static/layui/images/face/54.gif new file mode 100644 index 0000000000000000000000000000000000000000..e289d929b9552beb7be70329b7cbefb1d0618008 GIT binary patch literal 2196 zcmbW2X;@QN8i2zdHUU9)$4j6T6|#|pkg|m=0&O4yDyY*Ek}CwrViIH#8X#;!fP&G% z;({n0L5fgC!J;fx7EuSJ6skCc5iO!DMeEQ(dwT=o^oP?QbD#S>_nhy1>wCU)?zUhi z*_&gHutq$QBVOLB89$TJt%#ne&wlbv@m#0sX>;C<%y8&wJ2chSpS2xY=pHNs@2L{U zzf2e|2=B-Ug`Qu7rmsG0DeB4I(UlVN=EvrdNtcv+MjPfjj@?zpydF6ZJ!*j_uRN|hID75rRAc^~Lwn{g zW4=?uQ?iy3^hE$T7wu+`$(t{dFyU>5G{}eRaK6)a#zgz&_Klf^|wk3%H z&0HOBIJ`7;daF>sZ5x-zo0fVGFF$U1Gu=AVrh0z$2sD4~a)OV#K1W@jyZBwz z-Lm+*M}&*lmC*QwpW4d1vUbi~DTmaT=Gx1kpKnamruP=@8z_ib8me0!se>ji&30Fg zoe*`)7_)6(K(ig==j2ljiifoahK@-F%E0cd2nd3lAmdO*WCV@MX5i6S9W4X`0gIwo zY=F*@ap13yf2x?nO$22?EEvZZ`l9B)?LYy1o-b+_k%eW6X<$4*C|v?Zq=!aw(-XPg zJe0p5;FC&86^I3(j02T32z@IoX&h33!eF+;$<>1 z1%pXRNkON0qD7K849?rzTjK+d_kcA#q-jDKC)GnJbzbv82c=vIUo7K`gn-5)Csvd! z^F_g#u9hGWvsmvO3#I6lg3w%%0F%lQV{m9JMj+7CwW2MRg@gZ1lKn6Y#&mc2AaX1Dp(3{9)lDz3eZvu{qr4iO_ zgN0HVN5}=&;_~6RZ*6gZYD=L>K#oi#i4=*F);fS4FOrF*@ggxmiy#4REDo11)Ce|f zHfXh8Iw;{E0C`M_NC2!5rtsgB0fFF+V_@lcEc1U=zG}<+f2d*LWH6fc_(Qv_iQpd8 z4Bs_BJb8C~Kp}i)B=F(Tf_{7Rdig)UF8%w}FE3v#{=6_hH~akA%+u+go;;p<^shdJ$t6+boHr|CyrNrd93npM~|ouSCoHIR$8JgE;>|Lke`>U$jQ!< zf1a6fFg-0bE+QwwiX@S%Le?MQJk0{<`FA|a9iN|3vXb<S)7PjMJ~WhE@nU!X3VC;NO5X0$~7U$#WEPGlS39 z&90mn}`MmRFg6x+I1XVw*+WNk9Lpx7AVlVoRR zbl_uKZEe*S8+U9{^bUPHJ6~TLa|I(L+0-GKhP1$7kZ3BEKoAbewLt`SI}(WroEI3Q zo$Y_w5SintW9>snreds-dlwkSI?lK0XZs3zx=T0GT$-wEwTkTQcV=W&It3Z6w~dP` zoT66TEY*Esb*=%Rv5mBy8mBAyUEL)P^dDMdnw)wqwD`LHh-|LX&=L{rR$^hGpJiz2 z_j-XH$}HWa|I!vacypk@EJbX-fUODC!zvP{rqxr(LWCAUKi7hm6Og6P3vF+v1_?^7 zw}z|6I;+EU%uPes|4rGyebcC2z;H-TQS)rxf zYik)q%Pq8U)-^#`x@Ig*P99e}`hRa8Q1ex>%_cvw8c=iSc}6k%BZGrohK^QRp4uiz z=X~94glXULAg*F?x2eZYmpXBPmLA}i71`NzR&?5atL_KU%{2`5t*G7P>yO>c5g+{y DWiE05 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/55.gif b/static/layui/images/face/55.gif new file mode 100644 index 0000000000000000000000000000000000000000..4351083ac8e0e65a64ff362cbd480f2c86eab4fb GIT binary patch literal 1971 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 O9M literal 0 HcmV?d00001 diff --git a/static/layui/images/face/57.gif b/static/layui/images/face/57.gif new file mode 100644 index 0000000000000000000000000000000000000000..0bf130f0d930a1fb72ca83265bb504304cb08090 GIT binary patch literal 2705 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/6.gif b/static/layui/images/face/6.gif new file mode 100644 index 0000000000000000000000000000000000000000..f7715bf52817bc577c0d33cd0cbd17da898848a1 GIT binary patch literal 2213 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/61.gif b/static/layui/images/face/61.gif new file mode 100644 index 0000000000000000000000000000000000000000..f092d7e351cb195eaf707dbec74d32ced42f432f GIT binary patch literal 2495 zcmZvddpwls0>@{J%gYR7j4+up;~ps-g^~<|amh}yl}_7Y+vT7Wvfa*3b5mn-Xw(QEPFk@m^%H;^Pm=R@3$tBn3-FJp|Pv^7m^UwSD_xb+5kGGGPlXIvZOb_;w z4hu>WRu9bO^{zf1oIGFNTQT^7Th-I``h94IsJwfkq%pUy5n8e4q73P$oDwNA;h+TRVnUA`_FJACO6c)M++_wCByGB~^fmi{J_E`ZV%ux@x^UqeupTcd6~3D0Ton_H_0dMVc-Ceds;7_7Op3fM?JF4-(bWew!@<4uipnOUf!5Fgk22^bcK<9t@@i zGUzOJ!o^FRL@r%h`{;L9cxhLT+S;;hHGHjda`bQ9$hGqE`;ukr>qwwg1iB)Ba1E>= zIQo{*H$+bQXPwkpP7f2#7?1(FWFz!_ND+*?I0CmU!Y%?{_}D zgB9YeWcn`vZu)Kp4uqR3Ift;0s3_^+y^owVpxk|gF^N>x?KB?ea#FcF4ONmD~V zOSFv{O=Ol(xcPUxJUjIaHyf_N$Ui51Du&lY#~(285j^#0 z+p)y$5wMc&90YA8OG+(0$;KU54((qFY0GR#-E>RB_@<5BWT5+m-*`@lH`jD>3>hhz zO~73TLx7mVd9In)PVrDkF?$jV7g=gyLt50aTFVsNwqKcC>~5umE*o)@KCH9I?^-I1 zneX=3MJU0PVLC93;v#%wA)ts0iChFHIM&yQOGI(gh+HEG+aj=;={M8#GNt0Ik;)Iwz)ZYnw*K#u=Ajy&HMWM(0U$r z8WcwtvX<6=aIbLZ?X}1*j+C92WHcJL^@OhOiBq^avgOX`vt{m75!@Q6lt`x;`D%um z?Jn0NMjJ)kEy>0Fh*CkaD%CyCN!$K7PU~y;^Q(&xo3w3TlqNDEHdnE60n`H)%qPm< zipFzoO~l+H3u8)cb!gM39T?sXK*x=f9A)oD8L(0jeU7z{R|c?*%fVLe>6YAlQ!cOi zD<#NKxt}PxpPyiX%)1Dg$8`<72${F8-8KH2c1C;@iq9t!`EpKj;qLyZ5UzYpw)%!O z*%YSPZ0>k$J(D9)C5odx@fG2tVqgz{w$-kxU{cj& zQ`HSX+q^LqYmT2w6K16$uolXvd78?9fsqdv4jq^pfTBc1g@vvj=GIqKdz z>}2-8ad;CGJZ?`4i9pK^M8;a*G;+zN5lBHrIj+bG*TAx%sI?v6t5pDvGO9(PYHNwa z+K)gxyA(j%)_@KTe*$#s%{tI@xp^zAa-P0oCN(TEbFaCMl|SI##?#UGbFB8zSkvTV zjm{^`NJxel)l|G4qskPctFUi2#n9TR60Z&Clq&4QMUFn{ETAVB8$^~0ZmLoXA^Ubj zLiQEzqAf%A-9GlPRI$uTx_Mb%Q!(?Is=w-kvOI#N_TQnIhg!$-^3&(&W4wIWDJ*=n zD>5SH_I#u^`C373Y>nE|@)|V_fY&IcAkhG}#wm=P!cdCyN*22z zDC{HyrbKL8n1Bb&E>X^f6c)Fp6N*t=3SeBV&1Ae2C8p%%Rb_kbbSG1!s$q=Y{1YZr zBE}%dY#mbQiz^%}>TvCk?!9erOiccHgSq00G{hJ8OKsB&D?W|QM@i-wbUNU@UEhku z(%0Ya4RFKvAzk(Msa{jvr{{{?7f$y@X3%k4b#LjO!qIY(zCJ~CV>M)K&2~J2p6kP9 zd8t(**0*4*=}hVpw~ zk>4iKC;9E}`7^(Cw7Eno`6R#7|DRt=YQ#S*Joc_vr63V;(D;a;NlNliR7Kb&hNt4? zZ&a6}DrHbrO4^{Rl(}$cpsH*=IpmK)Kvi+D0P^xn16W1s6NxyeDm*OpGcqqGyy?>J tNMrII%;*&@oa8uz=YCKFxNClV+=*Obvw`TNkx8belN0|*V!~j%{{ZDj7zF?T literal 0 HcmV?d00001 diff --git a/static/layui/images/face/62.gif b/static/layui/images/face/62.gif new file mode 100644 index 0000000000000000000000000000000000000000..7fe49840bf69219fa40585741f901fa0b187c044 GIT binary patch literal 2017 zcmb7E2~<-@6b%XriWPA~E&7S5U?CeMKrqS@kf@<3s92X0ldnl3i^)eK*p}kPCV~ix zfJKFXfD#rh3MxeF%Bm~@$`%k=RFE|W0{x-ja#}p6bLO0xdGqdl@7;N4Hc}idt!Rr7 zix6#oh~B}G&K^Ze*N58H!N&G6xk4GARi+#rdr?2s-mN$Zt8|D2&!)bX9 z@fj60tpjNVjWOA^RZV?0ErU@hrAfEy&WTE&RlgllC~rT09U&^daj!XAEKSO4Z0OE?(wdM}k@xJCRMvO;(u1r*Sz~*@ zq`Y0$DZiRi^Q5v@Qr1~s|L**?@+fgtLCM?PXKk_BwY9CiaoN&K;!4HHXkmFrbBA21 z9KBQYI=!&*?6v1`Er`#C!PsnP2RB!HTNei$3Nu{|zJg8eR2P6ugJ|%&ZiA3VqkDo7 zupd0g;*gNtdH0b3i$Ox}#8WX;o;}E9Ir;KIS6^p0y00hQih}inbI*>EI=Tju;zFI0c!%_ zFjyQK15bh})|`kZ5OEg3#6ZH)_>2R@ZDhxZSn!F2WI_;+h(`PP_@I2uP+a~&G}g+> zO2vW0nZh2Xf+HM=CN$*;R!%aIK>?l5;z2Af2T(E6_H(@<5)#hzpAy)-kF*>CYP=j2 zoy$fGX*@I*g;A9>=}4u1-IdM$=q-S@fuHjIPhx@F5gv%%1`4>|d^!lbuT+KN5$*XP z4dU|MxLmJE6^PHtOFLFmUsf*4rhUJu(PwYFgL?aV#!>(Hyh+YlUT+lEN)7ysu|ckI5HXJ zvkrp{M?RMgj2BI0O_>YMaZ0`kEMw|iFjHdDa58As(|-2UlP0)-RNIe@3m-nVKFERl zoewv*+SsUaWLWV*J~TMc-`D%Tr~BR8H(i|_?XTNjwYJEbn;IMH|EjC4kyck#R=j*s zURGLCT=e|e($a^f-#b&cY~Dn1+UV#&wzspj*|46p z&YEatX+gl7o8hn+G|JTEyS2tfYgVr^T!}OQRxH>5cG=P;dW#n=TrgjEp3dBF=4j8J zrKLG@hKBm|Y4G=8@IhT`0m2VqqWVNspEzRXh`(RJvEwIBo(c>K4hiuvHMQOtdR#}q zFwx(=H_}f>UlZqsTW+Eky2fa^dRm$zKn^rB_L`5MHH*J~PM-F`%<2aO_;S@38RMvJ~yY(nXQh9H$#C+X_N6J7*$<3$?X(0mkzJForwl@3YZR zD(7ypEO`2UwUg#jKaV}(l2@CjA#_$wOK5tyC3Nl~OJ>c>w!&a-TDZC%g`ap*7qR1L z3eZjo)z+#wr(=^XLRhaYF-#~^4@uA#A)`uxOotZCF_r@COs07jYnjoR41IHx^3z(23>`O(`)4x0N+N9Ug7`A4=;!*$FD3vz)I&(EtPkek$P3WH+wR>*i|S;@u67A}zhS~gjRlsT&q z>2ep_vPiupx+%s62_mZ6VYfJn&1J1cwzHwYdCiDZL26Es7v)%gW`st7wp5Hko~cZ8 fzaAzQ7Zd=~XY>c^Z;pPWn-TRQb#LAp9fbAYHD_Zj literal 0 HcmV?d00001 diff --git a/static/layui/images/face/63.gif b/static/layui/images/face/63.gif new file mode 100644 index 0000000000000000000000000000000000000000..cf8e23e5b2e83bf4f383cd994510a7a04a74f57c GIT binary patch literal 5871 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/64.gif b/static/layui/images/face/64.gif new file mode 100644 index 0000000000000000000000000000000000000000..a7797198af0f3bb98361b1b8737784d0a8c2cba0 GIT binary patch literal 6448 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 E1xN?}xc~qF literal 0 HcmV?d00001 diff --git a/static/layui/images/face/66.gif b/static/layui/images/face/66.gif new file mode 100644 index 0000000000000000000000000000000000000000..bb6d077504e3b39871f72150c1f5fa43d4e18bfe GIT binary patch literal 3029 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 n_4QGfNp-c(#;#v)!?fJ=R?7`D2^Hm literal 0 HcmV?d00001 diff --git a/static/layui/images/face/67.gif b/static/layui/images/face/67.gif new file mode 100644 index 0000000000000000000000000000000000000000..6e33f7c4f762b5c4eda2a630793d681345423734 GIT binary patch literal 2701 zcmeHHX;@Qd7QP`wAR!P4LKMNv4ag!{fe<2l0wNMfff>RmQ$ung5we&B2sE}NQQ0hl z0##@QM5-uS7zc}jV`Pm>RVw1%Vylj=ZLzi0Qai)kFhb|YIKSsdzvp|N`+eu0_dVx) zXIZ;0ksTp&2i$>2dSD!leuTm7o^ZHfYuo>&Z`mPt!x{U-cRY5C*l#mC41LSmbHT;( zUGkI2nA2#Rrb`|(j*iO1cHQ?pn>;WFZ!LY-f^QrJJ1>S+WVvi^!dIWSuQ{>0b;9GY z9lH1^R@7iOPITRW)o1$v;bH{m?OsCtkax=nP1QzdzT*05X7UsP*FHsTpY$DAMmjku zHJ&8a40_cKyS{&MMd$Uv!e*DAJDlB@v7OgebU^t17Vx9nkfdcv>K1JA{$-LT+^>20 z*PhFU9}1p=KA*p=ujus4GGL#0dDncr?3a|ZhSRZ=1VYCp|7s4ldSK}bF7J8PhFLnj z>w4I+JCvP6u7>XATl;W>Unkd$;tqWpd@ms2%JYp!ZuyKpf*;VCr@mm{{^?CcEAB@( zx33b`9=e<|HY094!#O-AZXH=K-Q{|9M)S;X)z$<44FiFZSoG=!L84kW$5MoU&$>XH+5?|RuZc&g_t5tDm zw33n%YDoxHsm`Y{A|fKJ984w!(V%Eb6l7NT zNFj#T@uDDvgDR6rIAIZS^zd+cNJ1Es%U~oh;#mwPJ3cOs7akWE9~Kw7faNL0MRHi7 zUBF6y!-l>VYb^%33bD+C)v{t(lBib7!TF#$ve)~s zFNl!(u^tx-7r88!KCD3MU5ykrYVM!^`1R#4vp>Ih{?m`oe)#_B%y-{@^W^KV9{>H( z!v|k}aew;r&+gs5bNklK8&lUOuYEdk^~&W>E?vBE-ZDNmIx;+T&V2T7X9fpOpX&ek zWZz#;{H3?&qvOZAj~?kdeCWf@gB=Ij+gkU3@P144zP;}?H8$+oU0=7W_T8N|)m4=h zJGO5#84cy{yuDTbR#~a8q_{|{QEyQx74pJ@%`$0z9xM^(iZ;FZ=bVij{**1u`r{um zGt%D>q^0sx)+et^TAP@_xD9-fyB-6$pG36_y?M{(1_q6hr8^^LS-#_kcH}58IGV?6=MdMD+5tUHk&0(>?g&0>`2iA*g?VMdC zmdaRm(jLeZy-5U`hE1NFpy2)?1KNnSCyiL>(s4(Os~bURs4yDQHnyRo9cFXYNIGc+ z2pWb^k*-f4kC~d!HQq2r>t@r}blF69IS`U4(--l}tGJdA<(@ATpV^Rn%cCu8r)H*y zXJ*^F24{JO`Q$h_FC=Fvu=G`Oaw*$$jg{LdK^2uoN{&7$7w~EV$Z4s1O07Phh&(;P zi7c;3?jY#3wHQ5$fI{nY1UVqkhjNOl%+DnD^*I6KUdCi}PyuZ8SChsXNkzrQMJbbP z&$HsoB}DcV&l6UA7AQy$)S2Q?E>}s-%cII9FJ&8%K{F=ojVJH2ccCY(D4jSsf~(-W zdI!dCx2=jSjZe8Bl8{_!$~YioOMS3AJ)9Y-*=>w5H{8ngl|A$y4q_@wuWq_~{zQIS zs9x9Ieyv05ygZZ{kkf2Pb@4V<6V}B)v}v(%;|n6T7^|ynU?vzJvZJP|MYuUd2HD`6 zOi+Hm&_WL2x-moYw(a$Kc+P|g7G~-Mh7)u*?!>io2`ZH#lbLZlN1QCU3@6uz?6Qi{de e&Ns&5+yCAe|96dnSxgQJK<1box?FDn*8B_Be&pBy literal 0 HcmV?d00001 diff --git a/static/layui/images/face/68.gif b/static/layui/images/face/68.gif new file mode 100644 index 0000000000000000000000000000000000000000..1a6c400d2aee3c2bf384ea98105ea143a7be97a9 GIT binary patch literal 1424 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 ig6vPE;(vp8vN=19VTuwMWT7@8^o literal 0 HcmV?d00001 diff --git a/static/layui/images/face/7.gif b/static/layui/images/face/7.gif new file mode 100644 index 0000000000000000000000000000000000000000..e6d4db80572a8321fa233d7efd82636fd896bf49 GIT binary patch literal 3398 zcmd^>X;@Qd7RPTELI_!bkd;Wn76gSL8VCjmtHxL~xKN`N+n}hegVj>h8D8>cxj_Xi zSjVMQtw_~*7^&k@lqw)n5L%Qf;?}a%$_S(4Sc^ztZUR`_vClK#=R=++pYA>9{D0^C zZu0D;h{zS5h$r$BKyDljeeg~Gzpv>>pPpV9$sVltd~rxR`uyh5zR4p4e|vFC+O#|H zv~`i~-j?S%p|-YZRTls5(~6<90^7Bykw*t_{yoLk9^2PA*Vd`G-JE0VS^KObdni9- zrJysV)|MRV zaBJYG?OEYU%}|A8=;|Avr3>mxX52f;8N5DkxF+IZrSRUFsV^@0bbS{2%grTUf2o$GyCOZ|TCDz;sYyO{G90Ih5#fO`;Ud>i6G9Sc>o%d*M-cshOA3A78FdM{l3e3 z=iv1o5zA&zFEoa5a1lOUE(8}Z0nH~Xq%>s4f(3602*DW{)Gyc(>Ix$sVT<^*41ecb ze}Y$%isPfk8w2pXi+Yb!A2;jyVLW~SpYku+tU=qPli8pW=;!x4wjSv$pR&(F+B z_YEXRt-{9|pf4I)HIeQ!AzOyucb@(bM=` z(PEI~p50sx_;3Nc9 z5xKbS5K0$YDtkL?YQ-f1a9u?2idnO)BD(NSXl0^lvMR{K`%0sstSMecA>VpCmWdB^ z@(>jtRYRiLFa{*W4kQjCV$D&p_HwZHqNP}SnKaZqq4a2a~w^|4;5dy4mPab+0 z@VU$0XQ*Ye@MVyO=Y)qZjsZ(tUFW#U_<&P=UIrJ=CC(>K`nsukk{Tw!D5MIx2HKYc zBoPM^@!4k+E#||T@_s1pJ1iuZQyxDx{C3t%$E@qIVmLUg7y%7+kPk8Nv2wxU0{nWd0|dEHQp6|+bed(ZJD^M+%YPXeg< z6m|}mqJlK4fJ}`HEkT=%@he;Igy!|N?h2UV^j_`sm712_GyXK<@;ive8awt1M6WUg zUttK?8A2_YhYx{qhAP?cw+z`4I>+BPh7d+j(>cJu#l0HCdkIiacX&I7&!dHl0sG-& zcVOoSC*Xn9XHL@Yp4v%)%FVsZx5?Y+bfE|TY}bM0V=;8$(mRb;uk6Bmr5gIVty( z*3rs1?|2L_`#PWd<0PV`j&%l^G`RF|{_l-Q8s7*$=lf0> zkGpU#m=7kwKH|ekY95k6Bt{`{aA0Vi1JVHSj2pOC0oIHR4z%rp`lwn0V+d)tfSZtNXQOIQR zHj`(XLTM6{ghJBRI5GJ!nW9xHNg7j1%Arc&2DBuK6Ds+~ISQ&PHt8;SYBft&kjUgWbx@~;SRvvy;0@rWbt?pvmb>Pe*9vuRaX&t0 zpsy_?GY`Z8li6Yl&o-@Aps9d^x|`BSg`mhHHY??HI$5!LwOF}-zmP;(A^coUP7^CC z=zxBl0!sHE*NI6dPYcOaCZ$lR)#3Xyby_@m+qPkM0*+oFwZGrfJm%=S6h3H2pyl(Iu2|m8IIiYIUBrX7AZE zQl{fthK+q4Onm%wVLeU=8N4)dA=~m>BSSoI--MB&!^qnlLWT|@e>5)S^S=`^_|?dr z_VW*ZRdP3`WZ-bJC78J}iSdy%zMZRZM<^b?EMkV;0463JGl1C78#6$y%Gq5-Z~1Y> zlg?IxSn*`{`mBpJyApMu4sSXUhl2uoM90$bmD{~p^3kWU-ihR^iLgxn=0E@wiTV!_ Ch@v0> literal 0 HcmV?d00001 diff --git a/static/layui/images/face/70.gif b/static/layui/images/face/70.gif new file mode 100644 index 0000000000000000000000000000000000000000..416c5c14a19fbdaf8fb8d6baf5cf7e31e54f751a GIT binary patch literal 4590 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/71.gif b/static/layui/images/face/71.gif new file mode 100644 index 0000000000000000000000000000000000000000..c17d60cbd15e80d83629b2220b562a224eec5554 GIT binary patch literal 5304 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 literal 0 HcmV?d00001 diff --git a/static/layui/images/face/8.gif b/static/layui/images/face/8.gif new file mode 100644 index 0000000000000000000000000000000000000000..66f967b48da173b68cbfc92986e4426ea35ade74 GIT binary patch literal 4050 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 JEmCgizW{zshWG#g literal 0 HcmV?d00001 diff --git a/static/layui/images/face/9.gif b/static/layui/images/face/9.gif new file mode 100644 index 0000000000000000000000000000000000000000..60447400d31b035f56554b29af1aefc76bda1698 GIT binary patch literal 4221 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 literal 0 HcmV?d00001 diff --git a/static/layui/lay/modules/carousel.js b/static/layui/lay/modules/carousel.js new file mode 100644 index 0000000..2be2c8c --- /dev/null +++ b/static/layui/lay/modules/carousel.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i(['",'"].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['
      ',function(){var i=[];return layui.each(e.elemItem,function(e){i.push("")}),i.join("")}(),"
    "].join(""));n.elem.attr("lay-indicator",n.indicator),n.elem.find("."+c)[0]&&n.elem.find("."+c).remove(),n.elem.append(t),"updown"===n.anim&&t.css("margin-top",-(t.height()/2)),t.find("li").on("hover"===n.trigger?"mouseover":n.trigger,function(){var t=i(this),a=t.index();a>n.index?e.slide("add",a-n.index):a/g,">").replace(/'/g,"'").replace(/"/g,""")),c.html('
    1. '+o.replace(/[\r\t\n]+/g,"
    2. ")+"
    "),c.find(">.layui-code-h3")[0]||c.prepend('

    '+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code':"")+"

    ");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss"); \ No newline at end of file diff --git a/static/layui/lay/modules/colorpicker.js b/static/layui/lay/modules/colorpicker.js new file mode 100644 index 0000000..fd99bf8 --- /dev/null +++ b/static/layui/lay/modules/colorpicker.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var i=layui.jquery,o={config:{},index:layui.colorpicker?layui.colorpicker.index+1e4:0,set:function(e){var o=this;return o.config=i.extend({},o.config,e),o},on:function(e,i){return layui.onevent.call(this,"colorpicker",e,i)}},r=function(){var e=this,i=e.config;return{config:i}},t="colorpicker",n="layui-show",l="layui-colorpicker",c=".layui-colorpicker-main",a="layui-icon-down",s="layui-icon-close",f="layui-colorpicker-trigger-span",d="layui-colorpicker-trigger-i",u="layui-colorpicker-side",p="layui-colorpicker-side-slider",g="layui-colorpicker-basis",v="layui-colorpicker-alpha-bgcolor",h="layui-colorpicker-alpha-slider",m="layui-colorpicker-basis-cursor",b="layui-colorpicker-main-input",k=function(e){var i={h:0,s:0,b:0},o=Math.min(e.r,e.g,e.b),r=Math.max(e.r,e.g,e.b),t=r-o;return i.b=r,i.s=0!=r?255*t/r:0,0!=i.s?e.r==r?i.h=(e.g-e.b)/t:e.g==r?i.h=2+(e.b-e.r)/t:i.h=4+(e.r-e.g)/t:i.h=-1,r==o&&(i.h=0),i.h*=60,i.h<0&&(i.h+=360),i.s*=100/255,i.b*=100/255,i},y=function(e){var e=e.indexOf("#")>-1?e.substring(1):e;if(3==e.length){var i=e.split("");e=i[0]+i[0]+i[1]+i[1]+i[2]+i[2]}e=parseInt(e,16);var o={r:e>>16,g:(65280&e)>>8,b:255&e};return k(o)},x=function(e){var i={},o=e.h,r=255*e.s/100,t=255*e.b/100;if(0==r)i.r=i.g=i.b=t;else{var n=t,l=(255-r)*t/255,c=(n-l)*(o%60)/60;360==o&&(o=0),o<60?(i.r=n,i.b=l,i.g=l+c):o<120?(i.g=n,i.b=l,i.r=n-c):o<180?(i.g=n,i.r=l,i.b=l+c):o<240?(i.b=n,i.r=l,i.g=n-c):o<300?(i.b=n,i.g=l,i.r=l+c):o<360?(i.r=n,i.g=l,i.b=n-c):(i.r=0,i.g=0,i.b=0)}return{r:Math.round(i.r),g:Math.round(i.g),b:Math.round(i.b)}},C=function(e){var o=x(e),r=[o.r.toString(16),o.g.toString(16),o.b.toString(16)];return i.each(r,function(e,i){1==i.length&&(r[e]="0"+i)}),r.join("")},P=function(e){var i=/[0-9]{1,3}/g,o=e.match(i)||[];return{r:o[0],g:o[1],b:o[2]}},B=i(window),w=i(document),D=function(e){var r=this;r.index=++o.index,r.config=i.extend({},r.config,o.config,e),r.render()};D.prototype.config={color:"",size:null,alpha:!1,format:"hex",predefine:!1,colors:["#009688","#5FB878","#1E9FFF","#FF5722","#FFB800","#01AAED","#999","#c00","#ff8c00","#ffd700","#90ee90","#00ced1","#1e90ff","#c71585","rgb(0, 186, 189)","rgb(255, 120, 0)","rgb(250, 212, 0)","#393D49","rgba(0,0,0,.5)","rgba(255, 69, 0, 0.68)","rgba(144, 240, 144, 0.5)","rgba(31, 147, 255, 0.73)"]},D.prototype.render=function(){var e=this,o=e.config,r=i(['
    ',"",'3&&(o.alpha&&"rgb"==o.format||(e="#"+C(k(P(o.color))))),"background: "+e):e}()+'">','',"","","
    "].join("")),t=i(o.elem);o.size&&r.addClass("layui-colorpicker-"+o.size),t.addClass("layui-inline").html(e.elemColorBox=r),e.color=e.elemColorBox.find("."+f)[0].style.background,e.events()},D.prototype.renderPicker=function(){var e=this,o=e.config,r=e.elemColorBox[0],t=e.elemPicker=i(['
    ','
    ','
    ','
    ','
    ','
    ',"
    ",'
    ','
    ',"
    ","
    ",'
    ','
    ','
    ',"
    ","
    ",function(){if(o.predefine){var e=['
    '];return layui.each(o.colors,function(i,o){e.push(['
    ','
    ',"
    "].join(""))}),e.push("
    "),e.join("")}return""}(),'
    ','
    ','',"
    ",'
    ','','',"","
    "].join(""));e.elemColorBox.find("."+f)[0];i(c)[0]&&i(c).data("index")==e.index?e.removePicker(D.thisElemInd):(e.removePicker(D.thisElemInd),i("body").append(t)),D.thisElemInd=e.index,D.thisColor=r.style.background,e.position(),e.pickerEvents()},D.prototype.removePicker=function(e){var o=this;o.config;return i("#layui-colorpicker"+(e||o.index)).remove(),o},D.prototype.position=function(){var e=this,i=e.config,o=e.bindElem||e.elemColorBox[0],r=e.elemPicker[0],t=o.getBoundingClientRect(),n=r.offsetWidth,l=r.offsetHeight,c=function(e){return e=e?"scrollLeft":"scrollTop",document.body[e]|document.documentElement[e]},a=function(e){return document.documentElement[e?"clientWidth":"clientHeight"]},s=5,f=t.left,d=t.bottom;f-=(n-o.offsetWidth)/2,d+=s,f+n+s>a("width")?f=a("width")-n-s:fa()&&(d=t.top>l?t.top-l:a()-l,d-=2*s),i.position&&(r.style.position=i.position),r.style.left=f+("fixed"===i.position?0:c(1))+"px",r.style.top=d+("fixed"===i.position?0:c())+"px"},D.prototype.val=function(){var e=this,i=(e.config,e.elemColorBox.find("."+f)),o=e.elemPicker.find("."+b),r=i[0],t=r.style.backgroundColor;if(t){var n=k(P(t)),l=i.attr("lay-type");if(e.select(n.h,n.s,n.b),"torgb"===l&&o.find("input").val(t),"rgba"===l){var c=P(t);if(3==(t.match(/[0-9]{1,3}/g)||[]).length)o.find("input").val("rgba("+c.r+", "+c.g+", "+c.b+", 1)"),e.elemPicker.find("."+h).css("left",280);else{o.find("input").val(t);var a=280*t.slice(t.lastIndexOf(",")+1,t.length-1);e.elemPicker.find("."+h).css("left",a)}e.elemPicker.find("."+v)[0].style.background="linear-gradient(to right, rgba("+c.r+", "+c.g+", "+c.b+", 0), rgb("+c.r+", "+c.g+", "+c.b+"))"}}else e.select(0,100,100),o.find("input").val(""),e.elemPicker.find("."+v)[0].style.background="",e.elemPicker.find("."+h).css("left",280)},D.prototype.side=function(){var e=this,o=e.config,r=e.elemColorBox.find("."+f),t=r.attr("lay-type"),n=e.elemPicker.find("."+u),l=e.elemPicker.find("."+p),c=e.elemPicker.find("."+g),y=e.elemPicker.find("."+m),C=e.elemPicker.find("."+v),w=e.elemPicker.find("."+h),D=l[0].offsetTop/180*360,E=100-(y[0].offsetTop+3)/180*100,H=(y[0].offsetLeft+3)/260*100,W=Math.round(w[0].offsetLeft/280*100)/100,j=e.elemColorBox.find("."+d),F=e.elemPicker.find(".layui-colorpicker-pre").children("div"),L=function(i,n,l,c){e.select(i,n,l);var f=x({h:i,s:n,b:l});if(j.addClass(a).removeClass(s),r[0].style.background="rgb("+f.r+", "+f.g+", "+f.b+")","torgb"===t&&e.elemPicker.find("."+b).find("input").val("rgb("+f.r+", "+f.g+", "+f.b+")"),"rgba"===t){var d=0;d=280*c,w.css("left",d),e.elemPicker.find("."+b).find("input").val("rgba("+f.r+", "+f.g+", "+f.b+", "+c+")"),r[0].style.background="rgba("+f.r+", "+f.g+", "+f.b+", "+c+")",C[0].style.background="linear-gradient(to right, rgba("+f.r+", "+f.g+", "+f.b+", 0), rgb("+f.r+", "+f.g+", "+f.b+"))"}o.change&&o.change(e.elemPicker.find("."+b).find("input").val())},M=i(['
    t&&(r=t);var l=r/180*360;D=l,L(l,H,E,W),e.preventDefault()};Y(r),e.preventDefault()}),n.on("click",function(e){var o=e.clientY-i(this).offset().top;o<0&&(o=0),o>this.offsetHeight&&(o=this.offsetHeight);var r=o/180*360;D=r,L(r,H,E,W),e.preventDefault()}),y.on("mousedown",function(e){var i=this.offsetTop,o=this.offsetLeft,r=e.clientY,t=e.clientX,n=function(e){var n=i+(e.clientY-r),l=o+(e.clientX-t),a=c[0].offsetHeight-3,s=c[0].offsetWidth-3;n<-3&&(n=-3),n>a&&(n=a),l<-3&&(l=-3),l>s&&(l=s);var f=(l+3)/260*100,d=100-(n+3)/180*100;E=d,H=f,L(D,f,d,W),e.preventDefault()};layui.stope(e),Y(n),e.preventDefault()}),c.on("mousedown",function(e){var o=e.clientY-i(this).offset().top-3+B.scrollTop(),r=e.clientX-i(this).offset().left-3+B.scrollLeft();o<-3&&(o=-3),o>this.offsetHeight-3&&(o=this.offsetHeight-3),r<-3&&(r=-3),r>this.offsetWidth-3&&(r=this.offsetWidth-3);var t=(r+3)/260*100,n=100-(o+3)/180*100;E=n,H=t,L(D,t,n,W),e.preventDefault(),y.trigger(e,"mousedown")}),w.on("mousedown",function(e){var i=this.offsetLeft,o=e.clientX,r=function(e){var r=i+(e.clientX-o),t=C[0].offsetWidth;r<0&&(r=0),r>t&&(r=t);var n=Math.round(r/280*100)/100;W=n,L(D,H,E,n),e.preventDefault()};Y(r),e.preventDefault()}),C.on("click",function(e){var o=e.clientX-i(this).offset().left;o<0&&(o=0),o>this.offsetWidth&&(o=this.offsetWidth);var r=Math.round(o/280*100)/100;W=r,L(D,H,E,r),e.preventDefault()}),F.each(function(){i(this).on("click",function(){i(this).parent(".layui-colorpicker-pre").addClass("selected").siblings().removeClass("selected");var e,o=this.style.backgroundColor,r=k(P(o)),t=o.slice(o.lastIndexOf(",")+1,o.length-1);D=r.h,H=r.s,E=r.b,3==(o.match(/[0-9]{1,3}/g)||[]).length&&(t=1),W=t,e=280*t,L(r.h,r.s,r.b,t)})})},D.prototype.select=function(e,i,o,r){var t=this,n=(t.config,C({h:e,s:100,b:100})),l=C({h:e,s:i,b:o}),c=e/360*180,a=180-o/100*180-3,s=i/100*260-3;t.elemPicker.find("."+p).css("top",c),t.elemPicker.find("."+g)[0].style.background="#"+n,t.elemPicker.find("."+m).css({top:a,left:s}),"change"!==r&&t.elemPicker.find("."+b).find("input").val("#"+l)},D.prototype.pickerEvents=function(){var e=this,o=e.config,r=e.elemColorBox.find("."+f),t=e.elemPicker.find("."+b+" input"),n={clear:function(i){r[0].style.background="",e.elemColorBox.find("."+d).removeClass(a).addClass(s),e.color="",o.done&&o.done(""),e.removePicker()},confirm:function(i,n){var l=t.val(),c=l,f={};if(l.indexOf(",")>-1){if(f=k(P(l)),e.select(f.h,f.s,f.b),r[0].style.background=c="#"+C(f),(l.match(/[0-9]{1,3}/g)||[]).length>3&&"rgba"===r.attr("lay-type")){var u=280*l.slice(l.lastIndexOf(",")+1,l.length-1);e.elemPicker.find("."+h).css("left",u),r[0].style.background=l,c=l}}else f=y(l),r[0].style.background=c="#"+C(f),e.elemColorBox.find("."+d).removeClass(s).addClass(a);return"change"===n?(e.select(f.h,f.s,f.b,n),void(o.change&&o.change(c))):(e.color=l,o.done&&o.done(l),void e.removePicker())}};e.elemPicker.on("click","*[colorpicker-events]",function(){var e=i(this),o=e.attr("colorpicker-events");n[o]&&n[o].call(this,e)}),t.on("keyup",function(e){var o=i(this);n.confirm.call(this,o,13===e.keyCode?null:"change")})},D.prototype.events=function(){var e=this,o=e.config,r=e.elemColorBox.find("."+f);e.elemColorBox.on("click",function(){e.renderPicker(),i(c)[0]&&(e.val(),e.side())}),o.elem[0]&&!e.elemColorBox[0].eventHandler&&(w.on("click",function(o){if(!i(o.target).hasClass(l)&&!i(o.target).parents("."+l)[0]&&!i(o.target).hasClass(c.replace(/\./g,""))&&!i(o.target).parents(c)[0]&&e.elemPicker){if(e.color){var t=k(P(e.color));e.select(t.h,t.s,t.b)}else e.elemColorBox.find("."+d).removeClass(a).addClass(s);r[0].style.background=e.color||"",e.removePicker()}}),B.on("resize",function(){return!(!e.elemPicker||!i(c)[0])&&void e.position()}),e.elemColorBox[0].eventHandler=!0)},o.render=function(e){var i=new D(e);return r.call(i)},e(t,o)}); \ No newline at end of file diff --git a/static/layui/lay/modules/element.js b/static/layui/lay/modules/element.js new file mode 100644 index 0000000..ac628df --- /dev/null +++ b/static/layui/lay/modules/element.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(t){"use strict";var a=layui.$,i=(layui.hint(),layui.device()),e="element",l="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(t){var i=this;return a.extend(!0,i.config,t),i},s.prototype.on=function(t,a){return layui.onevent.call(this,e,t,a)},s.prototype.tabAdd=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.children(".layui-tab-bar"),o=l.children(".layui-tab-content"),r='
  2. "+(i.title||"unnaming")+"
  3. ";return s[0]?s.before(r):n.append(r),o.append('
    '+(i.content||"")+"
    "),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabClick.call(s[0],null,null,s),this},s.prototype.tab=function(t){t=t||{},b.on("click",t.headerElem,function(i){var e=a(this).index();f.tabClick.call(this,i,e,null,t)})},s.prototype.progress=function(t,i){var e="layui-progress",l=a("."+e+"[lay-filter="+t+"]"),n=l.find("."+e+"-bar"),s=n.find("."+e+"-text");return n.css("width",i),s.text(i),this};var o=".layui-nav",r="layui-nav-item",c="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",y="layui-nav-more",h="layui-anim layui-anim-upbit",f={tabClick:function(t,i,s,o){o=o||{};var r=s||a(this),i=i||r.parent().children("li").index(r),c=o.headerElem?r.parent():r.parents(".layui-tab").eq(0),u=o.bodyElem?a(o.bodyElem):c.children(".layui-tab-content").children(".layui-tab-item"),d=r.find("a"),y=c.attr("lay-filter");"javascript:;"!==d.attr("href")&&"_blank"===d.attr("target")||(r.addClass(l).siblings().removeClass(l),u.eq(i).addClass(n).siblings().removeClass(n)),layui.event.call(this,e,"tab("+y+")",{elem:c,index:i})},tabDelete:function(t,i){var n=i||a(this).parent(),s=n.index(),o=n.parents(".layui-tab").eq(0),r=o.children(".layui-tab-content").children(".layui-tab-item"),c=o.attr("lay-filter");n.hasClass(l)&&(n.next()[0]?f.tabClick.call(n.next()[0],null,s+1):n.prev()[0]&&f.tabClick.call(n.prev()[0],null,s-1)),n.remove(),r.eq(s).remove(),setTimeout(function(){f.tabAuto()},50),layui.event.call(this,e,"tabDelete("+c+")",{elem:o,index:s})},tabAuto:function(){var t="layui-tab-more",e="layui-tab-bar",l="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),r=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),c=a('');if(n===window&&8!=i.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var t=a(this);if(!t.find("."+l)[0]){var i=a('');i.on("click",f.tabDelete),t.append(i)}}),"string"!=typeof s.attr("lay-unauto"))if(o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+e)[0])return;o.append(c),s.attr("overflow",""),c.on("click",function(a){o[this.title?"removeClass":"addClass"](t),this.title=this.title?"":"收缩"})}else o.find("."+e).remove(),s.removeAttr("overflow")})},hideTabMore:function(t){var i=a(".layui-tab-title");t!==!0&&"tabmore"===a(t.target).attr("lay-stope")||(i.removeClass("layui-tab-more"),i.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var t=a(this),i=t.parents(o),n=i.attr("lay-filter"),s=t.parent(),c=t.siblings("."+d),y="string"==typeof s.attr("lay-unselect");"javascript:;"!==t.attr("href")&&"_blank"===t.attr("target")||y||c[0]||(i.find("."+l).removeClass(l),s.addClass(l)),i.hasClass(u)&&(c.removeClass(h),c[0]&&(s["none"===c.css("display")?"addClass":"removeClass"](r+"ed"),"all"===i.attr("lay-shrink")&&s.siblings().removeClass(r+"ed"))),layui.event.call(this,e,"nav("+n+")",t)},collapse:function(){var t=a(this),i=t.find(".layui-colla-icon"),l=t.siblings(".layui-colla-content"),s=t.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),r="none"===l.css("display");if("string"==typeof s.attr("lay-accordion")){var c=s.children(".layui-colla-item").children("."+n);c.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),c.removeClass(n)}l[r?"addClass":"removeClass"](n),i.html(r?"":""),layui.event.call(this,e,"collapse("+o+")",{title:t,content:l,show:r})}};s.prototype.init=function(t,e){var l=function(){return e?'[lay-filter="'+e+'"]':""}(),s={tab:function(){f.tabAuto.call({})},nav:function(){var t=200,e={},s={},p={},b=function(l,o,r){var c=a(this),f=c.find("."+d);o.hasClass(u)?l.css({top:c.position().top,height:c.children("a").outerHeight(),opacity:1}):(f.addClass(h),l.css({left:c.position().left+parseFloat(c.css("marginLeft")),top:c.position().top+c.height()-l.height()}),e[r]=setTimeout(function(){l.css({width:c.width(),opacity:1})},i.ie&&i.ie<10?0:t),clearTimeout(p[r]),"block"===f.css("display")&&clearTimeout(s[r]),s[r]=setTimeout(function(){f.addClass(n),c.find("."+y).addClass(y+"d")},300))};a(o+l).each(function(i){var l=a(this),o=a(''),h=l.find("."+r);l.find("."+c)[0]||(l.append(o),h.on("mouseenter",function(){b.call(this,o,l,i)}).on("mouseleave",function(){l.hasClass(u)||(clearTimeout(s[i]),s[i]=setTimeout(function(){l.find("."+d).removeClass(n),l.find("."+y).removeClass(y+"d")},300))}),l.on("mouseleave",function(){clearTimeout(e[i]),p[i]=setTimeout(function(){l.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},t)})),h.find("a").each(function(){var t=a(this),i=(t.parent(),t.siblings("."+d));i[0]&&!t.children("."+y)[0]&&t.append(''),t.off("click",f.clickThis).on("click",f.clickThis)})})},breadcrumb:function(){var t=".layui-breadcrumb";a(t+l).each(function(){var t=a(this),i="lay-separator",e=t.attr(i)||"/",l=t.find("a");l.next("span["+i+"]")[0]||(l.each(function(t){t!==l.length-1&&a(this).after(""+e+"")}),t.css("visibility","visible"))})},progress:function(){var t="layui-progress";a("."+t+l).each(function(){var i=a(this),e=i.find(".layui-progress-bar"),l=e.attr("lay-percent");e.css("width",function(){return/^.+\/.+$/.test(l)?100*new Function("return "+l)()+"%":l}()),i.attr("lay-showPercent")&&setTimeout(function(){e.html(''+l+"")},350)})},collapse:function(){var t="layui-collapse";a("."+t+l).each(function(){var t=a(this).find(".layui-colla-item");t.each(function(){var t=a(this),i=t.find(".layui-colla-title"),e=t.find(".layui-colla-content"),l="none"===e.css("display");i.find(".layui-colla-icon").remove(),i.append(''+(l?"":"")+""),i.off("click",f.collapse).on("click",f.collapse)})})}};return s[t]?s[t]():layui.each(s,function(t,a){a()})},s.prototype.render=s.prototype.init;var p=new s,b=a(document);p.render();var v=".layui-tab-title li";b.on("click",v,f.tabClick),b.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),t(e,p)}); \ No newline at end of file diff --git a/static/layui/lay/modules/flow.js b/static/layui/lay/modules/flow.js new file mode 100644 index 0000000..8a80c05 --- /dev/null +++ b/static/layui/lay/modules/flow.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var l=layui.$,o=function(e){},t='';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="加载更多",h=l('");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;su)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)}); \ No newline at end of file diff --git a/static/layui/lay/modules/form.js b/static/layui/lay/modules/form.js new file mode 100644 index 0000000..daa8ce5 --- /dev/null +++ b/static/layui/lay/modules/form.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define("layer",function(e){"use strict";var t=layui.$,i=layui.layer,a=layui.hint(),n=layui.device(),l="form",r=".layui-form",s="layui-this",o="layui-hide",c="layui-disabled",u=function(){this.config={verify:{required:[/[\S]+/,"必填项不能为空"],phone:[/^1\d{10}$/,"请输入正确的手机号"],email:[/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,"邮箱格式不正确"],url:[/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,"链接格式不正确"],number:function(e){if(!e||isNaN(e))return"只能填写数字"},date:[/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,"日期格式不正确"],identity:[/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,"请输入正确的身份证号"]}}};u.prototype.set=function(e){var i=this;return t.extend(!0,i.config,e),i},u.prototype.verify=function(e){var i=this;return t.extend(!0,i.config.verify,e),i},u.prototype.on=function(e,t){return layui.onevent.call(this,l,e,t)},u.prototype.val=function(e,i){var a=t(r+'[lay-filter="'+e+'"]');a.each(function(e,a){var n=t(this);layui.each(i,function(e,t){var i,a=n.find('[name="'+e+'"]');a[0]&&(i=a[0].type,"checkbox"===i?a[0].checked=t:"radio"===i?a.each(function(){this.value===t&&(this.checked=!0)}):a.val(t))})}),f.render(null,e)},u.prototype.render=function(e,i){var n=this,u=t(r+function(){return i?'[lay-filter="'+i+'"]':""}()),d={select:function(){var e,i="请选择",a="layui-form-select",n="layui-select-title",r="layui-select-none",d="",f=u.find("select"),v=function(i,l){t(i.target).parent().hasClass(n)&&!l||(t("."+a).removeClass(a+"ed "+a+"up"),e&&d&&e.val(d)),e=null},y=function(i,u,f){var y,p=t(this),m=i.find("."+n),k=m.find("input"),x=i.find("dl"),g=x.children("dd"),b=this.selectedIndex;if(!u){var C=function(){var e=i.offset().top+i.outerHeight()+5-h.scrollTop(),t=x.outerHeight();b=p[0].selectedIndex,i.addClass(a+"ed"),g.removeClass(o),y=null,g.eq(b).addClass(s).siblings().removeClass(s),e+t>h.height()&&e>=t&&i.addClass(a+"up"),$()},w=function(e){i.removeClass(a+"ed "+a+"up"),k.blur(),y=null,e||T(k.val(),function(e){var i=p[0].selectedIndex;e&&(d=t(p[0].options[i]).html(),0===i&&d===k.attr("placeholder")&&(d=""),k.val(d||""))})},$=function(){var e=x.children("dd."+s);if(e[0]){var t=e.position().top,i=x.height(),a=e.height();t>i&&x.scrollTop(t+x.scrollTop()-i+a-5),t<0&&x.scrollTop(t+x.scrollTop()-5)}};m.on("click",function(e){i.hasClass(a+"ed")?w():(v(e,!0),C()),x.find("."+r).remove()}),m.find(".layui-edge").on("click",function(){k.focus()}),k.on("keyup",function(e){var t=e.keyCode;9===t&&C()}).on("keydown",function(e){var t=e.keyCode;9===t&&w();var i=function(t,a){var n,l;e.preventDefault();var r=function(){var e=x.children("dd."+s);if(x.children("dd."+o)[0]&&"next"===t){var i=x.children("dd:not(."+o+",."+c+")"),n=i.eq(0).index();if(n>=0&&n无匹配项

    '):x.find("."+r).remove()},"keyup"),""===t&&x.find("."+r).remove(),void $())};f&&k.on("keyup",j).on("blur",function(i){var a=p[0].selectedIndex;e=k,d=t(p[0].options[a]).html(),0===a&&d===k.attr("placeholder")&&(d=""),setTimeout(function(){T(k.val(),function(e){d||k.val("")},"blur")},200)}),g.on("click",function(){var e=t(this),a=e.attr("lay-value"),n=p.attr("lay-filter");return!e.hasClass(c)&&(e.hasClass("layui-select-tips")?k.val(""):(k.val(e.text()),e.addClass(s)),e.siblings().removeClass(s),p.val(a).removeClass("layui-form-danger"),layui.event.call(this,l,"select("+n+")",{elem:p[0],value:a,othis:i}),w(!0),!1)}),i.find("dl>dt").on("click",function(e){return!1}),t(document).off("click",v).on("click",v)}};f.each(function(e,l){var r=t(this),o=r.next("."+a),u=this.disabled,d=l.value,f=t(l.options[l.selectedIndex]),v=l.options[0];if("string"==typeof r.attr("lay-ignore"))return r.show();var h="string"==typeof r.attr("lay-search"),p=v?v.value?i:v.innerHTML||i:i,m=t(['
    ','
    ','','
    ','
    ',function(e){var t=[];return layui.each(e,function(e,a){0!==e||a.value?"optgroup"===a.tagName.toLowerCase()?t.push("
    "+a.label+"
    "):t.push('
    '+a.innerHTML+"
    "):t.push('
    '+(a.innerHTML||i)+"
    ")}),0===t.length&&t.push('
    没有选项
    '),t.join("")}(r.find("*"))+"
    ","
    "].join(""));o[0]&&o.remove(),r.after(m),y.call(this,m,u,h)})},checkbox:function(){var e={checkbox:["layui-form-checkbox","layui-form-checked","checkbox"],_switch:["layui-form-switch","layui-form-onswitch","switch"]},i=u.find("input[type=checkbox]"),a=function(e,i){var a=t(this);e.on("click",function(){var t=a.attr("lay-filter"),n=(a.attr("lay-text")||"").split("|");a[0].disabled||(a[0].checked?(a[0].checked=!1,e.removeClass(i[1]).find("em").text(n[1])):(a[0].checked=!0,e.addClass(i[1]).find("em").text(n[0])),layui.event.call(a[0],l,i[2]+"("+t+")",{elem:a[0],value:a[0].value,othis:e}))})};i.each(function(i,n){var l=t(this),r=l.attr("lay-skin"),s=(l.attr("lay-text")||"").split("|"),o=this.disabled;"switch"===r&&(r="_"+r);var u=e[r]||e.checkbox;if("string"==typeof l.attr("lay-ignore"))return l.show();var d=l.next("."+u[0]),f=t(['
    ",function(){var e=n.title.replace(/\s/g,""),t={checkbox:[e?""+n.title+"":"",''].join(""),_switch:""+((n.checked?s[0]:s[1])||"")+""};return t[r]||t.checkbox}(),"
    "].join(""));d[0]&&d.remove(),l.after(f),a.call(this,f,u)})},radio:function(){var e="layui-form-radio",i=["",""],a=u.find("input[type=radio]"),n=function(a){var n=t(this),s="layui-anim-scaleSpring";a.on("click",function(){var o=n[0].name,c=n.parents(r),u=n.attr("lay-filter"),d=c.find("input[name="+o.replace(/(\.|#|\[|\])/g,"\\$1")+"]");n[0].disabled||(layui.each(d,function(){var a=t(this).next("."+e);this.checked=!1,a.removeClass(e+"ed"),a.find(".layui-icon").removeClass(s).html(i[1])}),n[0].checked=!0,a.addClass(e+"ed"),a.find(".layui-icon").addClass(s).html(i[0]),layui.event.call(n[0],l,"radio("+u+")",{elem:n[0],value:n[0].value,othis:a}))})};a.each(function(a,l){var r=t(this),s=r.next("."+e),o=this.disabled;if("string"==typeof r.attr("lay-ignore"))return r.show();s[0]&&s.remove();var u=t(['
    ',''+i[l.checked?0:1]+"","
    "+function(){var e=l.title||"";return"string"==typeof r.next().attr("lay-radio")&&(e=r.next().html(),r.next().remove()),e}()+"
    ","
    "].join(""));r.after(u),n.call(this,u)})}};return e?d[e]?d[e]():a.error("不支持的"+e+"表单渲染"):layui.each(d,function(e,t){t()}),n};var d=function(){var e=t(this),a=f.config.verify,s=null,o="layui-form-danger",c={},u=e.parents(r),d=u.find("*[lay-verify]"),v=e.parents("form")[0],h=u.find("input,select,textarea"),y=e.attr("lay-filter");if(layui.each(d,function(e,l){var r=t(this),c=r.attr("lay-verify").split("|"),u=r.attr("lay-verType"),d=r.val();if(r.removeClass(o),layui.each(c,function(e,t){var c,f="",v="function"==typeof a[t];if(a[t]){var c=v?f=a[t](d,l):!a[t][0].test(d);if(f=f||a[t][1],c)return"tips"===u?i.tips(f,function(){return"string"==typeof r.attr("lay-ignore")||"select"!==l.tagName.toLowerCase()&&!/^checkbox|radio$/.test(l.type)?r:r.next()}(),{tips:1}):"alert"===u?i.alert(f,{title:"提示",shadeClose:!0}):i.msg(f,{icon:5,shift:6}),n.android||n.ios||l.focus(),r.addClass(o),s=!0}}),s)return s}),s)return!1;var p={};return layui.each(h,function(e,t){if(t.name=(t.name||"").replace(/^\s*|\s*&/,""),t.name){if(/^.*\[\]$/.test(t.name)){var i=t.name.match(/^(.*)\[\]$/g)[0];p[i]=0|p[i],t.name=t.name.replace(/^(.*)\[\]$/,"$1["+p[i]++ +"]")}/^checkbox|radio$/.test(t.type)&&!t.checked||(c[t.name]=t.value)}}),layui.event.call(this,l,"submit("+y+")",{elem:this,form:v,field:c})},f=new u,v=t(document),h=t(window);f.render(),v.on("reset",r,function(){var e=t(this).attr("lay-filter");setTimeout(function(){f.render(null,e)},50)}),v.on("submit",r,d).on("click","*[lay-submit]",d),e(l,f)}); \ No newline at end of file diff --git a/static/layui/lay/modules/jquery.js b/static/layui/lay/modules/jquery.js new file mode 100644 index 0000000..242696a --- /dev/null +++ b/static/layui/lay/modules/jquery.js @@ -0,0 +1,5 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=!!e&&"length"in e&&e.length,n=pe.type(e);return"function"!==n&&!pe.isWindow(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function r(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return pe.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(Ce.test(t))return pe.filter(t,e,n);t=pe.filter(t,e)}return pe.grep(e,function(e){return pe.inArray(e,t)>-1!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t={};return pe.each(e.match(De)||[],function(e,n){t[n]=!0}),t}function a(){re.addEventListener?(re.removeEventListener("DOMContentLoaded",s),e.removeEventListener("load",s)):(re.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(re.addEventListener||"load"===e.event.type||"complete"===re.readyState)&&(a(),pe.ready())}function u(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(_e,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:qe.test(n)?pe.parseJSON(n):n)}catch(i){}pe.data(e,t,n)}else n=void 0}return n}function l(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(He(e)){var i,o,a=pe.expando,s=e.nodeType,u=s?pe.cache:e,l=s?e[a]:e[a]&&a;if(l&&u[l]&&(r||u[l].data)||void 0!==n||"string"!=typeof t)return l||(l=s?e[a]=ne.pop()||pe.guid++:a),u[l]||(u[l]=s?{}:{toJSON:pe.noop}),"object"!=typeof t&&"function"!=typeof t||(r?u[l]=pe.extend(u[l],t):u[l].data=pe.extend(u[l].data,t)),o=u[l],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[pe.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[pe.camelCase(t)])):i=o,i}}function f(e,t,n){if(He(e)){var r,i,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe.expando]:pe.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){pe.isArray(t)?t=t.concat(pe.map(t,pe.camelCase)):t in r?t=[t]:(t=pe.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!l(r):!pe.isEmptyObject(r))return}(n||(delete a[s].data,l(a[s])))&&(o?pe.cleanData([e],!0):fe.deleteExpando||a!=a.window?delete a[s]:a[s]=void 0)}}}function d(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:function(){return pe.css(e,t,"")},u=s(),l=n&&n[3]||(pe.cssNumber[t]?"":"px"),c=(pe.cssNumber[t]||"px"!==l&&+u)&&Me.exec(pe.css(e,t));if(c&&c[3]!==l){l=l||c[3],n=n||[],c=+u||1;do o=o||".5",c/=o,pe.style(e,t,c+l);while(o!==(o=s()/u)&&1!==o&&--a)}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function h(e,t){var n,r,i=0,o="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||pe.nodeName(r,t)?o.push(r):pe.merge(o,h(r,t));return void 0===t||t&&pe.nodeName(e,t)?pe.merge([e],o):o}function g(e,t){for(var n,r=0;null!=(n=e[r]);r++)pe._data(n,"globalEval",!t||pe._data(t[r],"globalEval"))}function m(e){Be.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t,n,r,i){for(var o,a,s,u,l,c,f,d=e.length,y=p(t),v=[],x=0;x"!==f[1]||Ve.test(a)?0:u:u.firstChild,o=a&&a.childNodes.length;o--;)pe.nodeName(c=a.childNodes[o],"tbody")&&!c.childNodes.length&&a.removeChild(c);for(pe.merge(v,u.childNodes),u.textContent="";u.firstChild;)u.removeChild(u.firstChild);u=y.lastChild}else v.push(t.createTextNode(a));for(u&&y.removeChild(u),fe.appendChecked||pe.grep(h(v,"input"),m),x=0;a=v[x++];)if(r&&pe.inArray(a,r)>-1)i&&i.push(a);else if(s=pe.contains(a.ownerDocument,a),u=h(y.appendChild(a),"script"),s&&g(u),n)for(o=0;a=u[o++];)Ie.test(a.type||"")&&n.push(a);return u=null,y}function v(){return!0}function x(){return!1}function b(){try{return re.activeElement}catch(e){}}function w(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)w(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),i===!1)i=x;else if(!i)return e;return 1===o&&(a=i,i=function(e){return pe().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=pe.guid++)),e.each(function(){pe.event.add(this,t,i,r,n)})}function T(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function C(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}function E(e){var t=it.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function N(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,r,i,o=pe._data(e),a=pe._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;r1&&"string"==typeof p&&!fe.checkClone&&rt.test(p))return e.each(function(i){var o=e.eq(i);g&&(t[0]=p.call(this,i,o.html())),S(o,t,n,r)});if(f&&(l=y(t,e[0].ownerDocument,!1,e,r),i=l.firstChild,1===l.childNodes.length&&(l=i),i||r)){for(s=pe.map(h(l,"script"),C),a=s.length;c")).appendTo(t.documentElement),t=(ut[0].contentWindow||ut[0].contentDocument).document,t.write(),t.close(),n=D(e,t),ut.detach()),lt[e]=n),n}function L(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function H(e){if(e in Et)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=Ct.length;n--;)if(e=Ct[n]+t,e in Et)return e}function q(e,t){for(var n,r,i,o=[],a=0,s=e.length;a=0&&n=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==pe.type(e)||e.nodeType||pe.isWindow(e))return!1;try{if(e.constructor&&!ce.call(e,"constructor")&&!ce.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(!fe.ownFirst)for(t in e)return ce.call(e,t);for(t in e);return void 0===t||ce.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?ue[le.call(e)]||"object":typeof e},globalEval:function(t){t&&pe.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(ge,"ms-").replace(me,ye)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t){var r,i=0;if(n(e))for(r=e.length;iT.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[P]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||V)-(~e.sourceIndex||V);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function u(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function l(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function f(){}function d(e){for(var t=0,n=e.length,r="";t1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function g(e,n,r){for(var i=0,o=n.length;i-1&&(r[l]=!(a[l]=f))}}else x=m(x===a?x.splice(h,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function v(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return ee(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==A)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s1&&h(c),s>1&&d(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,s0,o=e.length>0,a=function(r,a,s,u,l){var c,f,d,p=0,h="0",g=r&&[],y=[],v=A,x=r||o&&T.find.TAG("*",l),b=W+=null==v?1:Math.random()||.1,w=x.length;for(l&&(A=a===H||a||l);h!==w&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===H||(L(c),s=!_);d=e[f++];)if(d(c,a||H,s)){u.push(c);break}l&&(W=b)}i&&((c=!d&&c)&&p--,r&&g.push(c))}if(p+=h,i&&h!==p){for(f=0;d=n[f++];)d(g,y,a,s);if(r){if(p>0)for(;h--;)g[h]||y[h]||(y[h]=G.call(u));y=m(y)}Q.apply(u,y),l&&!r&&y.length>0&&p+n.length>1&&t.uniqueSort(u)}return l&&(W=b,A=v),g};return i?r(a):a}var b,w,T,C,E,N,k,S,A,D,j,L,H,q,_,F,M,O,R,P="sizzle"+1*new Date,B=e.document,W=0,I=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(j=!0),0},V=1<<31,Y={}.hasOwnProperty,J=[],G=J.pop,K=J.push,Q=J.push,Z=J.slice,ee=function(e,t){for(var n=0,r=e.length;n+~]|"+ne+")"+ne+"*"),ce=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),fe=new RegExp(oe),de=new RegExp("^"+re+"$"),pe={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re+"|[*])"),ATTR:new RegExp("^"+ie),PSEUDO:new RegExp("^"+oe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},he=/^(?:input|select|textarea|button)$/i,ge=/^h\d$/i,me=/^[^{]+\{\s*\[native \w/,ye=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,xe=/'|\\/g,be=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),we=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},Te=function(){L()};try{Q.apply(J=Z.call(B.childNodes),B.childNodes),J[B.childNodes.length].nodeType}catch(Ce){Q={apply:J.length?function(e,t){K.apply(e,Z.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},E=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},L=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:B;return r!==H&&9===r.nodeType&&r.documentElement?(H=r,q=H.documentElement,_=!E(H),(n=H.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Te,!1):n.attachEvent&&n.attachEvent("onunload",Te)),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(H.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=me.test(H.getElementsByClassName),w.getById=i(function(e){return q.appendChild(e).id=P,!H.getElementsByName||!H.getElementsByName(P).length}),w.getById?(T.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&_){var n=t.getElementById(e);return n?[n]:[]}},T.filter.ID=function(e){var t=e.replace(be,we);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(be,we);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):w.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&_)return t.getElementsByClassName(e)},M=[],F=[],(w.qsa=me.test(H.querySelectorAll))&&(i(function(e){q.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&F.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+P+"-]").length||F.push("~="),e.querySelectorAll(":checked").length||F.push(":checked"),e.querySelectorAll("a#"+P+"+*").length||F.push(".#.+[+~]")}),i(function(e){var t=H.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+ne+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=me.test(O=q.matches||q.webkitMatchesSelector||q.mozMatchesSelector||q.oMatchesSelector||q.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",oe)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=me.test(q.compareDocumentPosition),R=t||me.test(q.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return j=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!w.sortDetached&&t.compareDocumentPosition(e)===n?e===H||e.ownerDocument===B&&R(B,e)?-1:t===H||t.ownerDocument===B&&R(B,t)?1:D?ee(D,e)-ee(D,t):0:4&n?-1:1)}:function(e,t){if(e===t)return j=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===H?-1:t===H?1:i?-1:o?1:D?ee(D,e)-ee(D,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===B?-1:u[r]===B?1:0},H):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(ce,"='$1']"),w.matchesSelector&&_&&!X[n+" "]&&(!M||!M.test(n))&&(!F||!F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!_):void 0;return void 0!==r?r:w.attributes||!_?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(j=!w.detectDuplicates,D=!w.sortStable&&e.slice(0),e.sort(U),j){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return D=null,e},C=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=C(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=C(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:pe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(be,we),e[3]=(e[3]||e[4]||e[5]||"").replace(be,we),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&fe.test(n)&&(t=N(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(be,we).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ae," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,d,p,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s,x=!1;if(m){if(o){for(;g;){for(d=t;d=d[g];)if(s?d.nodeName.toLowerCase()===y:1===d.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){for(d=m,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}), +l=c[e]||[],p=l[0]===W&&l[1],x=p&&l[2],d=p&&m.childNodes[p];d=++p&&d&&d[g]||(x=p=0)||h.pop();)if(1===d.nodeType&&++x&&d===t){c[e]=[W,p,x];break}}else if(v&&(d=t,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),l=c[e]||[],p=l[0]===W&&l[1],x=p),x===!1)for(;(d=++p&&d&&d[g]||(x=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==y:1!==d.nodeType)||!++x||(v&&(f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),c[e]=[W,x]),d!==t)););return x-=i,x===r||x%r===0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[P]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=ee(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(se,"$1"));return i[P]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(be,we),function(t){return(t.textContent||t.innerText||C(t)).indexOf(e)>-1}}),lang:r(function(e){return de.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(be,we).toLowerCase(),function(t){var n;do if(n=_?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===q},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return ge.test(e.nodeName)},input:function(e){return he.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:l(function(){return[0]}),last:l(function(e,t){return[t-1]}),eq:l(function(e,t,n){return[n<0?n+t:n]}),even:l(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:l(function(e,t,n){for(var r=n<0?n+t:n;++r2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&_&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(be,we),t)||[])[0],!t)return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=pe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((u=T.find[s])&&(r=u(a.matches[0].replace(be,we),ve.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&d(o),!e)return Q.apply(n,r),n;break}}return(l||k(e,f))(r,t,!_,n,!t||ve.test(e)&&c(t.parentNode)||t),n},w.sortStable=P.split("").sort(U).join("")===P,w.detectDuplicates=!!j,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(te,function(e,t,n){var r;if(!n)return e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);pe.find=ve,pe.expr=ve.selectors,pe.expr[":"]=pe.expr.pseudos,pe.uniqueSort=pe.unique=ve.uniqueSort,pe.text=ve.getText,pe.isXMLDoc=ve.isXML,pe.contains=ve.contains;var xe=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&pe(e).is(n))break;r.push(e)}return r},be=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},we=pe.expr.match.needsContext,Te=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Ce=/^.[^:#\[\.,]*$/;pe.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?pe.find.matchesSelector(r,e)?[r]:[]:pe.find.matches(e,pe.grep(t,function(e){return 1===e.nodeType}))},pe.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(pe(e).filter(function(){for(t=0;t1?pe.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&we.test(e)?pe(e):e||[],!1).length}});var Ee,Ne=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,ke=pe.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||Ee,"string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:Ne.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof pe?t[0]:t,pe.merge(this,pe.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:re,!0)),Te.test(r[1])&&pe.isPlainObject(t))for(r in t)pe.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}if(i=re.getElementById(r[2]),i&&i.parentNode){if(i.id!==r[2])return Ee.find(e);this.length=1,this[0]=i}return this.context=re,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):pe.isFunction(e)?"undefined"!=typeof n.ready?n.ready(e):e(pe):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),pe.makeArray(e,this))};ke.prototype=pe.fn,Ee=pe(re);var Se=/^(?:parents|prev(?:Until|All))/,Ae={children:!0,contents:!0,next:!0,prev:!0};pe.fn.extend({has:function(e){var t,n=pe(e,this),r=n.length;return this.filter(function(){for(t=0;t-1:1===n.nodeType&&pe.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?pe.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?pe.inArray(this[0],pe(e)):pe.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(pe.uniqueSort(pe.merge(this.get(),pe(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),pe.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return xe(e,"parentNode")},parentsUntil:function(e,t,n){return xe(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return xe(e,"nextSibling")},prevAll:function(e){return xe(e,"previousSibling")},nextUntil:function(e,t,n){return xe(e,"nextSibling",n)},prevUntil:function(e,t,n){return xe(e,"previousSibling",n)},siblings:function(e){return be((e.parentNode||{}).firstChild,e)},children:function(e){return be(e.firstChild)},contents:function(e){return pe.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:pe.merge([],e.childNodes)}},function(e,t){pe.fn[e]=function(n,r){var i=pe.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=pe.filter(r,i)),this.length>1&&(Ae[e]||(i=pe.uniqueSort(i)),Se.test(e)&&(i=i.reverse())),this.pushStack(i)}});var De=/\S+/g;pe.Callbacks=function(e){e="string"==typeof e?o(e):pe.extend({},e);var t,n,r,i,a=[],s=[],u=-1,l=function(){for(i=e.once,r=t=!0;s.length;u=-1)for(n=s.shift();++u-1;)a.splice(n,1),n<=u&&u--}),this},has:function(e){return e?pe.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return i=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return i=!0,n||c.disable(),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||l()),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},pe.extend({Deferred:function(e){var t=[["resolve","done",pe.Callbacks("once memory"),"resolved"],["reject","fail",pe.Callbacks("once memory"),"rejected"],["notify","progress",pe.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return pe.Deferred(function(n){pe.each(t,function(t,o){var a=pe.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&pe.isFunction(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?pe.extend(e,r):r}},i={};return r.pipe=r.then,pe.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=ie.call(arguments),a=o.length,s=1!==a||e&&pe.isFunction(e.promise)?a:0,u=1===s?e:pe.Deferred(),l=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?ie.call(arguments):i,r===t?u.notifyWith(n,r):--s||u.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);i0||(je.resolveWith(re,[pe]),pe.fn.triggerHandler&&(pe(re).triggerHandler("ready"),pe(re).off("ready"))))}}),pe.ready.promise=function(t){if(!je)if(je=pe.Deferred(),"complete"===re.readyState||"loading"!==re.readyState&&!re.documentElement.doScroll)e.setTimeout(pe.ready);else if(re.addEventListener)re.addEventListener("DOMContentLoaded",s),e.addEventListener("load",s);else{re.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&re.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!pe.isReady){try{n.doScroll("left")}catch(t){return e.setTimeout(i,50)}a(),pe.ready()}}()}return je.promise(t)},pe.ready.promise();var Le;for(Le in pe(fe))break;fe.ownFirst="0"===Le,fe.inlineBlockNeedsLayout=!1,pe(function(){var e,t,n,r;n=re.getElementsByTagName("body")[0],n&&n.style&&(t=re.createElement("div"),r=re.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),"undefined"!=typeof t.style.zoom&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",fe.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=re.createElement("div");fe.deleteExpando=!0;try{delete e.test}catch(t){fe.deleteExpando=!1}e=null}();var He=function(e){var t=pe.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return(1===n||9===n)&&(!t||t!==!0&&e.getAttribute("classid")===t)},qe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,_e=/([A-Z])/g;pe.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?pe.cache[e[pe.expando]]:e[pe.expando],!!e&&!l(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return f(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return f(e,t,!0)}}),pe.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=pe.data(o),1===o.nodeType&&!pe._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=pe.camelCase(r.slice(5)),u(o,r,i[r])));pe._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){pe.data(this,e)}):arguments.length>1?this.each(function(){pe.data(this,e,t)}):o?u(o,e,pe.data(o,e)):void 0},removeData:function(e){return this.each(function(){pe.removeData(this,e)})}}),pe.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=pe._data(e,t),n&&(!r||pe.isArray(n)?r=pe._data(e,t,pe.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=pe.queue(e,t),r=n.length,i=n.shift(),o=pe._queueHooks(e,t),a=function(){pe.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return pe._data(e,n)||pe._data(e,n,{empty:pe.Callbacks("once memory").add(function(){pe._removeData(e,t+"queue"),pe._removeData(e,n)})})}}),pe.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length
    a",fe.leadingWhitespace=3===e.firstChild.nodeType,fe.tbody=!e.getElementsByTagName("tbody").length,fe.htmlSerialize=!!e.getElementsByTagName("link").length,fe.html5Clone="<:nav>"!==re.createElement("nav").cloneNode(!0).outerHTML,n.type="checkbox",n.checked=!0,t.appendChild(n),fe.appendChecked=n.checked,e.innerHTML="",fe.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue,t.appendChild(e),n=re.createElement("input"),n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),e.appendChild(n),fe.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,fe.noCloneEvent=!!e.addEventListener,e[pe.expando]=1,fe.attributes=!e.getAttribute(pe.expando)}();var Xe={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:fe.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]};Xe.optgroup=Xe.option,Xe.tbody=Xe.tfoot=Xe.colgroup=Xe.caption=Xe.thead,Xe.th=Xe.td;var Ue=/<|&#?\w+;/,Ve=/-1&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[pe.expando]?t:new pe.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:pe.makeArray(n,[t]),l=pe.event.special[p]||{},i||!l.trigger||l.trigger.apply(r,n)!==!1)){if(!i&&!l.noBubble&&!pe.isWindow(r)){for(u=l.delegateType||p,Ke.test(u+p)||(s=s.parentNode);s;s=s.parentNode)d.push(s),c=s;c===(r.ownerDocument||re)&&d.push(c.defaultView||c.parentWindow||e)}for(f=0;(s=d[f++])&&!t.isPropagationStopped();)t.type=f>1?u:l.bindType||p,o=(pe._data(s,"events")||{})[t.type]&&pe._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&He(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!l._default||l._default.apply(d.pop(),n)===!1)&&He(r)&&a&&r[p]&&!pe.isWindow(r)){c=r[a],c&&(r[a]=null),pe.event.triggered=p;try{r[p]()}catch(g){}pe.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=pe.event.fix(e);var t,n,r,i,o,a=[],s=ie.call(arguments),u=(pe._data(this,"events")||{})[e.type]||[],l=pe.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){for(a=pe.event.handlers.call(this,e,u),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!e.isImmediatePropagationStopped();)e.rnamespace&&!e.rnamespace.test(o.namespace)||(e.handleObj=o,e.data=o.data,r=((pe.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s),void 0!==r&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()));return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,u=e.target;if(s&&u.nodeType&&("click"!==e.type||isNaN(e.button)||e.button<1))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(r=[],n=0;n-1:pe.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&a.push({elem:u,handlers:r})}return s]","i"),tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,nt=/\s*$/g,at=p(re),st=at.appendChild(re.createElement("div"));pe.extend({htmlPrefilter:function(e){return e.replace(tt,"<$1>")},clone:function(e,t,n){var r,i,o,a,s,u=pe.contains(e.ownerDocument,e);if(fe.html5Clone||pe.isXMLDoc(e)||!et.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(st.innerHTML=e.outerHTML,st.removeChild(o=st.firstChild)),!(fe.noCloneEvent&&fe.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||pe.isXMLDoc(e)))for(r=h(o),s=h(e),a=0;null!=(i=s[a]);++a)r[a]&&k(i,r[a]);if(t)if(n)for(s=s||h(e),r=r||h(o),a=0;null!=(i=s[a]);a++)N(i,r[a]);else N(e,o);return r=h(o,"script"),r.length>0&&g(r,!u&&h(e,"script")),r=s=i=null,o},cleanData:function(e,t){for(var n,r,i,o,a=0,s=pe.expando,u=pe.cache,l=fe.attributes,c=pe.event.special;null!=(n=e[a]);a++)if((t||He(n))&&(i=n[s],o=i&&u[i])){if(o.events)for(r in o.events)c[r]?pe.event.remove(n,r):pe.removeEvent(n,r,o.handle);u[i]&&(delete u[i],l||"undefined"==typeof n.removeAttribute?n[s]=void 0:n.removeAttribute(s),ne.push(i))}}}),pe.fn.extend({domManip:S,detach:function(e){return A(this,e,!0)},remove:function(e){return A(this,e)},text:function(e){return Pe(this,function(e){return void 0===e?pe.text(this):this.empty().append((this[0]&&this[0].ownerDocument||re).createTextNode(e))},null,e,arguments.length)},append:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.appendChild(e)}})},prepend:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&pe.cleanData(h(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&pe.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return pe.clone(this,e,t)})},html:function(e){return Pe(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ze,""):void 0;if("string"==typeof e&&!nt.test(e)&&(fe.htmlSerialize||!et.test(e))&&(fe.leadingWhitespace||!$e.test(e))&&!Xe[(We.exec(e)||["",""])[1].toLowerCase()]){e=pe.htmlPrefilter(e);try{for(;nt",t=l.getElementsByTagName("td"),t[0].style.cssText="margin:0;border:0;padding:0;display:none",o=0===t[0].offsetHeight,o&&(t[0].style.display="",t[1].style.display="none",o=0===t[0].offsetHeight)),f.removeChild(u)}var n,r,i,o,a,s,u=re.createElement("div"),l=re.createElement("div");l.style&&(l.style.cssText="float:left;opacity:.5",fe.opacity="0.5"===l.style.opacity,fe.cssFloat=!!l.style.cssFloat,l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",fe.clearCloneStyle="content-box"===l.style.backgroundClip,u=re.createElement("div"),u.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",l.innerHTML="",u.appendChild(l),fe.boxSizing=""===l.style.boxSizing||""===l.style.MozBoxSizing||""===l.style.WebkitBoxSizing,pe.extend(fe,{reliableHiddenOffsets:function(){return null==n&&t(),o},boxSizingReliable:function(){return null==n&&t(),i},pixelMarginRight:function(){return null==n&&t(),r},pixelPosition:function(){return null==n&&t(),n},reliableMarginRight:function(){return null==n&&t(),a},reliableMarginLeft:function(){return null==n&&t(),s}}))}();var ht,gt,mt=/^(top|right|bottom|left)$/;e.getComputedStyle?(ht=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n.getPropertyValue(t)||n[t]:void 0,""!==a&&void 0!==a||pe.contains(e.ownerDocument,e)||(a=pe.style(e,t)),n&&!fe.pixelMarginRight()&&ft.test(a)&&ct.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o),void 0===a?a:a+""}):pt.currentStyle&&(ht=function(e){return e.currentStyle},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),ft.test(a)&&!mt.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"});var yt=/alpha\([^)]*\)/i,vt=/opacity\s*=\s*([^)]*)/i,xt=/^(none|table(?!-c[ea]).+)/,bt=new RegExp("^("+Fe+")(.*)$","i"),wt={position:"absolute",visibility:"hidden",display:"block"},Tt={letterSpacing:"0",fontWeight:"400"},Ct=["Webkit","O","Moz","ms"],Et=re.createElement("div").style;pe.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=gt(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":fe.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=pe.camelCase(t),u=e.style;if(t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:u[t];if(o=typeof n,"string"===o&&(i=Me.exec(n))&&i[1]&&(n=d(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(pe.cssNumber[s]?"":"px")),fe.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{u[t]=n}catch(l){}}},css:function(e,t,n,r){var i,o,a,s=pe.camelCase(t);return t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=gt(e,t,r)),"normal"===o&&t in Tt&&(o=Tt[t]),""===n||n?(i=parseFloat(o),n===!0||isFinite(i)?i||0:o):o}}),pe.each(["height","width"],function(e,t){pe.cssHooks[t]={get:function(e,n,r){if(n)return xt.test(pe.css(e,"display"))&&0===e.offsetWidth?dt(e,wt,function(){return M(e,t,r)}):M(e,t,r)},set:function(e,n,r){var i=r&&ht(e);return _(e,n,r?F(e,t,r,fe.boxSizing&&"border-box"===pe.css(e,"boxSizing",!1,i),i):0)}}}),fe.opacity||(pe.cssHooks.opacity={get:function(e,t){return vt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=pe.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===pe.trim(o.replace(yt,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=yt.test(o)?o.replace(yt,i):o+" "+i)}}),pe.cssHooks.marginRight=L(fe.reliableMarginRight,function(e,t){if(t)return dt(e,{display:"inline-block"},gt,[e,"marginRight"])}),pe.cssHooks.marginLeft=L(fe.reliableMarginLeft,function(e,t){if(t)return(parseFloat(gt(e,"marginLeft"))||(pe.contains(e.ownerDocument,e)?e.getBoundingClientRect().left-dt(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}):0))+"px"}),pe.each({margin:"",padding:"",border:"Width"},function(e,t){pe.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+Oe[r]+t]=o[r]||o[r-2]||o[0];return i}},ct.test(e)||(pe.cssHooks[e+t].set=_)}),pe.fn.extend({css:function(e,t){return Pe(this,function(e,t,n){var r,i,o={},a=0;if(pe.isArray(t)){for(r=ht(e),i=t.length;a1)},show:function(){return q(this,!0)},hide:function(){return q(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Re(this)?pe(this).show():pe(this).hide()})}}),pe.Tween=O,O.prototype={constructor:O,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||pe.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(pe.cssNumber[n]?"":"px")},cur:function(){var e=O.propHooks[this.prop];return e&&e.get?e.get(this):O.propHooks._default.get(this)},run:function(e){var t,n=O.propHooks[this.prop];return this.options.duration?this.pos=t=pe.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):O.propHooks._default.set(this),this}},O.prototype.init.prototype=O.prototype,O.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=pe.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){pe.fx.step[e.prop]?pe.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[pe.cssProps[e.prop]]&&!pe.cssHooks[e.prop]?e.elem[e.prop]=e.now:pe.style(e.elem,e.prop,e.now+e.unit)}}},O.propHooks.scrollTop=O.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},pe.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},pe.fx=O.prototype.init,pe.fx.step={};var Nt,kt,St=/^(?:toggle|show|hide)$/,At=/queueHooks$/;pe.Animation=pe.extend($,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return d(n.elem,e,Me.exec(t),n),n}]},tweener:function(e,t){pe.isFunction(e)?(t=e,e=["*"]):e=e.match(De);for(var n,r=0,i=e.length;r
    a",e=n.getElementsByTagName("a")[0],t.setAttribute("type","checkbox"),n.appendChild(t),e=n.getElementsByTagName("a")[0],e.style.cssText="top:1px",fe.getSetAttribute="t"!==n.className,fe.style=/top/.test(e.getAttribute("style")),fe.hrefNormalized="/a"===e.getAttribute("href"),fe.checkOn=!!t.value,fe.optSelected=i.selected,fe.enctype=!!re.createElement("form").enctype,r.disabled=!0,fe.optDisabled=!i.disabled,t=re.createElement("input"),t.setAttribute("value",""),fe.input=""===t.getAttribute("value"),t.value="t",t.setAttribute("type","radio"),fe.radioValue="t"===t.value}();var Dt=/\r/g,jt=/[\x20\t\r\n\f]+/g;pe.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=pe.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,pe(this).val()):e,null==i?i="":"number"==typeof i?i+="":pe.isArray(i)&&(i=pe.map(i,function(e){return null==e?"":e+""})),t=pe.valHooks[this.type]||pe.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=pe.valHooks[i.type]||pe.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(Dt,""):null==n?"":n)}}}),pe.extend({valHooks:{option:{get:function(e){var t=pe.find.attr(e,"value");return null!=t?t:pe.trim(pe.text(e)).replace(jt," ")}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||i<0,a=o?null:[],s=o?i+1:r.length,u=i<0?s:o?i:0;u-1)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),pe.each(["radio","checkbox"],function(){pe.valHooks[this]={set:function(e,t){if(pe.isArray(t))return e.checked=pe.inArray(pe(e).val(),t)>-1}},fe.checkOn||(pe.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Lt,Ht,qt=pe.expr.attrHandle,_t=/^(?:checked|selected)$/i,Ft=fe.getSetAttribute,Mt=fe.input;pe.fn.extend({attr:function(e,t){return Pe(this,pe.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){pe.removeAttr(this,e)})}}),pe.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?pe.prop(e,t,n):(1===o&&pe.isXMLDoc(e)||(t=t.toLowerCase(),i=pe.attrHooks[t]||(pe.expr.match.bool.test(t)?Ht:Lt)),void 0!==n?null===n?void pe.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=pe.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!fe.radioValue&&"radio"===t&&pe.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(De);if(o&&1===e.nodeType)for(;n=o[i++];)r=pe.propFix[n]||n,pe.expr.match.bool.test(n)?Mt&&Ft||!_t.test(n)?e[r]=!1:e[pe.camelCase("default-"+n)]=e[r]=!1:pe.attr(e,n,""),e.removeAttribute(Ft?n:r)}}),Ht={set:function(e,t,n){return t===!1?pe.removeAttr(e,n):Mt&&Ft||!_t.test(n)?e.setAttribute(!Ft&&pe.propFix[n]||n,n):e[pe.camelCase("default-"+n)]=e[n]=!0,n}},pe.each(pe.expr.match.bool.source.match(/\w+/g),function(e,t){var n=qt[t]||pe.find.attr;Mt&&Ft||!_t.test(t)?qt[t]=function(e,t,r){var i,o;return r||(o=qt[t],qt[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,qt[t]=o),i}:qt[t]=function(e,t,n){if(!n)return e[pe.camelCase("default-"+t)]?t.toLowerCase():null}}),Mt&&Ft||(pe.attrHooks.value={set:function(e,t,n){return pe.nodeName(e,"input")?void(e.defaultValue=t):Lt&&Lt.set(e,t,n)}}),Ft||(Lt={set:function(e,t,n){var r=e.getAttributeNode(n);if(r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n))return t}},qt.id=qt.name=qt.coords=function(e,t,n){var r;if(!n)return(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},pe.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);if(n&&n.specified)return n.value},set:Lt.set},pe.attrHooks.contenteditable={set:function(e,t,n){Lt.set(e,""!==t&&t,n)}},pe.each(["width","height"],function(e,t){pe.attrHooks[t]={set:function(e,n){if(""===n)return e.setAttribute(t,"auto"),n}}})),fe.style||(pe.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var Ot=/^(?:input|select|textarea|button|object)$/i,Rt=/^(?:a|area)$/i;pe.fn.extend({prop:function(e,t){return Pe(this,pe.prop,e,t,arguments.length>1)},removeProp:function(e){return e=pe.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),pe.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&pe.isXMLDoc(e)||(t=pe.propFix[t]||t,i=pe.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=pe.find.attr(e,"tabindex");return t?parseInt(t,10):Ot.test(e.nodeName)||Rt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),fe.hrefNormalized||pe.each(["href","src"],function(e,t){pe.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),fe.optSelected||(pe.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),pe.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){pe.propFix[this.toLowerCase()]=this}),fe.enctype||(pe.propFix.enctype="encoding");var Pt=/[\t\r\n\f]/g;pe.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).addClass(e.call(this,t,z(this)))});if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).removeClass(e.call(this,t,z(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):pe.isFunction(e)?this.each(function(n){pe(this).toggleClass(e.call(this,n,z(this),t),t)}):this.each(function(){var t,r,i,o;if("string"===n)for(r=0,i=pe(this),o=e.match(De)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||(t=z(this),t&&pe._data(this,"__className__",t),pe.attr(this,"class",t||e===!1?"":pe._data(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+z(n)+" ").replace(Pt," ").indexOf(t)>-1)return!0;return!1}}),pe.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){pe.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),pe.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}});var Bt=e.location,Wt=pe.now(),It=/\?/,$t=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;pe.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=pe.trim(t+"");return i&&!pe.trim(i.replace($t,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():pe.error("Invalid JSON: "+t)},pe.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new e.DOMParser,n=r.parseFromString(t,"text/xml")):(n=new e.ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||pe.error("Invalid XML: "+t),n};var zt=/#.*$/,Xt=/([?&])_=[^&]*/,Ut=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Vt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Yt=/^(?:GET|HEAD)$/,Jt=/^\/\//,Gt=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Kt={},Qt={},Zt="*/".concat("*"),en=Bt.href,tn=Gt.exec(en.toLowerCase())||[];pe.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:en,type:"GET",isLocal:Vt.test(tn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":pe.parseJSON,"text xml":pe.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?V(V(e,pe.ajaxSettings),t):V(pe.ajaxSettings,e)},ajaxPrefilter:X(Kt),ajaxTransport:X(Qt),ajax:function(t,n){function r(t,n,r,i){var o,f,v,x,w,C=n;2!==b&&(b=2,u&&e.clearTimeout(u),c=void 0,s=i||"",T.readyState=t>0?4:0,o=t>=200&&t<300||304===t,r&&(x=Y(d,T,r)),x=J(d,x,T,o),o?(d.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(pe.lastModified[a]=w),w=T.getResponseHeader("etag"),w&&(pe.etag[a]=w)),204===t||"HEAD"===d.type?C="nocontent":304===t?C="notmodified":(C=x.state,f=x.data,v=x.error,o=!v)):(v=C,!t&&C||(C="error",t<0&&(t=0))),T.status=t,T.statusText=(n||C)+"",o?g.resolveWith(p,[f,C,T]):g.rejectWith(p,[T,C,v]),T.statusCode(y),y=void 0,l&&h.trigger(o?"ajaxSuccess":"ajaxError",[T,d,o?f:v]),m.fireWith(p,[T,C]),l&&(h.trigger("ajaxComplete",[T,d]),--pe.active||pe.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,d=pe.ajaxSetup({},n),p=d.context||d,h=d.context&&(p.nodeType||p.jquery)?pe(p):pe.event,g=pe.Deferred(),m=pe.Callbacks("once memory"),y=d.statusCode||{},v={},x={},b=0,w="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!f)for(f={};t=Ut.exec(s);)f[t[1].toLowerCase()]=t[2];t=f[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?s:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=x[n]=x[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(b<2)for(t in e)y[t]=[y[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||w;return c&&c.abort(t),r(0,t),this}};if(g.promise(T).complete=m.add,T.success=T.done,T.error=T.fail,d.url=((t||d.url||en)+"").replace(zt,"").replace(Jt,tn[1]+"//"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=pe.trim(d.dataType||"*").toLowerCase().match(De)||[""],null==d.crossDomain&&(i=Gt.exec(d.url.toLowerCase()),d.crossDomain=!(!i||i[1]===tn[1]&&i[2]===tn[2]&&(i[3]||("http:"===i[1]?"80":"443"))===(tn[3]||("http:"===tn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=pe.param(d.data,d.traditional)),U(Kt,d,n,T),2===b)return T;l=pe.event&&d.global,l&&0===pe.active++&&pe.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Yt.test(d.type),a=d.url,d.hasContent||(d.data&&(a=d.url+=(It.test(a)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=Xt.test(a)?a.replace(Xt,"$1_="+Wt++):a+(It.test(a)?"&":"?")+"_="+Wt++)),d.ifModified&&(pe.lastModified[a]&&T.setRequestHeader("If-Modified-Since",pe.lastModified[a]),pe.etag[a]&&T.setRequestHeader("If-None-Match",pe.etag[a])),(d.data&&d.hasContent&&d.contentType!==!1||n.contentType)&&T.setRequestHeader("Content-Type",d.contentType),T.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Zt+"; q=0.01":""):d.accepts["*"]);for(o in d.headers)T.setRequestHeader(o,d.headers[o]);if(d.beforeSend&&(d.beforeSend.call(p,T,d)===!1||2===b))return T.abort();w="abort";for(o in{success:1,error:1,complete:1})T[o](d[o]);if(c=U(Qt,d,n,T)){if(T.readyState=1,l&&h.trigger("ajaxSend",[T,d]),2===b)return T;d.async&&d.timeout>0&&(u=e.setTimeout(function(){T.abort("timeout")},d.timeout));try{b=1,c.send(v,r)}catch(C){if(!(b<2))throw C;r(-1,C)}}else r(-1,"No Transport");return T},getJSON:function(e,t,n){return pe.get(e,t,n,"json")},getScript:function(e,t){return pe.get(e,void 0,t,"script")}}),pe.each(["get","post"],function(e,t){pe[t]=function(e,n,r,i){return pe.isFunction(n)&&(i=i||r,r=n,n=void 0),pe.ajax(pe.extend({url:e,type:t,dataType:i,data:n,success:r},pe.isPlainObject(e)&&e))}}),pe._evalUrl=function(e){return pe.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},pe.fn.extend({wrapAll:function(e){if(pe.isFunction(e))return this.each(function(t){pe(this).wrapAll(e.call(this,t))});if(this[0]){var t=pe(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return pe.isFunction(e)?this.each(function(t){pe(this).wrapInner(e.call(this,t))}):this.each(function(){var t=pe(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=pe.isFunction(e);return this.each(function(n){pe(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){pe.nodeName(this,"body")||pe(this).replaceWith(this.childNodes)}).end()}}),pe.expr.filters.hidden=function(e){return fe.reliableHiddenOffsets()?e.offsetWidth<=0&&e.offsetHeight<=0&&!e.getClientRects().length:K(e)},pe.expr.filters.visible=function(e){return!pe.expr.filters.hidden(e)};var nn=/%20/g,rn=/\[\]$/,on=/\r?\n/g,an=/^(?:submit|button|image|reset|file)$/i,sn=/^(?:input|select|textarea|keygen)/i;pe.param=function(e,t){var n,r=[],i=function(e,t){t=pe.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=pe.ajaxSettings&&pe.ajaxSettings.traditional),pe.isArray(e)||e.jquery&&!pe.isPlainObject(e))pe.each(e,function(){i(this.name,this.value)});else for(n in e)Q(n,e[n],t,i);return r.join("&").replace(nn,"+")},pe.fn.extend({serialize:function(){return pe.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=pe.prop(this,"elements");return e?pe.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!pe(this).is(":disabled")&&sn.test(this.nodeName)&&!an.test(e)&&(this.checked||!Be.test(e))}).map(function(e,t){var n=pe(this).val();return null==n?null:pe.isArray(n)?pe.map(n,function(e){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),pe.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return this.isLocal?ee():re.documentMode>8?Z():/^(get|post|head|put|delete|options)$/i.test(this.type)&&Z()||ee()}:Z;var un=0,ln={},cn=pe.ajaxSettings.xhr();e.attachEvent&&e.attachEvent("onunload",function(){for(var e in ln)ln[e](void 0,!0)}),fe.cors=!!cn&&"withCredentials"in cn,cn=fe.ajax=!!cn,cn&&pe.ajaxTransport(function(t){if(!t.crossDomain||fe.cors){var n;return{send:function(r,i){var o,a=t.xhr(),s=++un;if(a.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(o in t.xhrFields)a[o]=t.xhrFields[o];t.mimeType&&a.overrideMimeType&&a.overrideMimeType(t.mimeType),t.crossDomain||r["X-Requested-With"]||(r["X-Requested-With"]="XMLHttpRequest");for(o in r)void 0!==r[o]&&a.setRequestHeader(o,r[o]+"");a.send(t.hasContent&&t.data||null),n=function(e,r){var o,u,l;if(n&&(r||4===a.readyState))if(delete ln[s],n=void 0,a.onreadystatechange=pe.noop,r)4!==a.readyState&&a.abort();else{l={},o=a.status,"string"==typeof a.responseText&&(l.text=a.responseText);try{u=a.statusText}catch(c){u=""}o||!t.isLocal||t.crossDomain?1223===o&&(o=204):o=l.text?200:404}l&&i(o,u,l,a.getAllResponseHeaders())},t.async?4===a.readyState?e.setTimeout(n):a.onreadystatechange=ln[s]=n:n()},abort:function(){n&&n(void 0,!0)}}}}),pe.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return pe.globalEval(e),e}}}),pe.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),pe.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=re.head||pe("head")[0]||re.documentElement;return{send:function(r,i){t=re.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var fn=[],dn=/(=)\?(?=&|$)|\?\?/;pe.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=fn.pop()||pe.expando+"_"+Wt++;return this[e]=!0,e}}),pe.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(dn.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&dn.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=pe.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(dn,"$1"+i):t.jsonp!==!1&&(t.url+=(It.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||pe.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?pe(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,fn.push(i)),a&&pe.isFunction(o)&&o(a[0]),a=o=void 0}),"script"}),pe.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||re;var r=Te.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=y([e],t,i),i&&i.length&&pe(i).remove(),pe.merge([],r.childNodes))};var pn=pe.fn.load;return pe.fn.load=function(e,t,n){if("string"!=typeof e&&pn)return pn.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=pe.trim(e.slice(s,e.length)),e=e.slice(0,s)),pe.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&pe.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?pe("
    ").append(pe.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},pe.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){pe.fn[t]=function(e){return this.on(t,e)}}),pe.expr.filters.animated=function(e){return pe.grep(pe.timers,function(t){return e===t.elem}).length},pe.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=pe.css(e,"position"),f=pe(e),d={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=pe.css(e,"top"),u=pe.css(e,"left"),l=("absolute"===c||"fixed"===c)&&pe.inArray("auto",[o,u])>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),pe.isFunction(t)&&(t=t.call(e,n,pe.extend({},s))),null!=t.top&&(d.top=t.top-s.top+a),null!=t.left&&(d.left=t.left-s.left+i),"using"in t?t.using.call(e,d):f.css(d)}},pe.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){pe.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,pe.contains(t,i)?("undefined"!=typeof i.getBoundingClientRect&&(r=i.getBoundingClientRect()),n=te(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===pe.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),pe.nodeName(e[0],"html")||(n=e.offset()),n.top+=pe.css(e[0],"borderTopWidth",!0),n.left+=pe.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-pe.css(r,"marginTop",!0),left:t.left-n.left-pe.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){ +for(var e=this.offsetParent;e&&!pe.nodeName(e,"html")&&"static"===pe.css(e,"position");)e=e.offsetParent;return e||pt})}}),pe.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);pe.fn[e]=function(r){return Pe(this,function(e,r,i){var o=te(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?pe(o).scrollLeft():i,n?i:pe(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),pe.each(["top","left"],function(e,t){pe.cssHooks[t]=L(fe.pixelPosition,function(e,n){if(n)return n=gt(e,t),ft.test(n)?pe(e).position()[t]+"px":n})}),pe.each({Height:"height",Width:"width"},function(e,t){pe.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){pe.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return Pe(this,function(t,n,r){var i;return pe.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?pe.css(t,n,a):pe.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),pe.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),pe.fn.size=function(){return this.length},pe.fn.andSelf=pe.fn.addBack,layui.define(function(e){layui.$=pe,e("jquery",pe)}),pe}); \ No newline at end of file diff --git a/static/layui/lay/modules/laydate.js b/static/layui/lay/modules/laydate.js new file mode 100644 index 0000000..ae0a591 --- /dev/null +++ b/static/layui/lay/modules/laydate.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;!function(){"use strict";var e=window.layui&&layui.define,t={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,n=t.length-1,a=n;a>0;a--)if("interactive"===t[a].readyState){e=t[a].src;break}return e||t[n].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),getStyle:function(e,t){var n=e.currentStyle?e.currentStyle:window.getComputedStyle(e,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](t)},link:function(e,a,i){if(n.path){var r=document.getElementsByTagName("head")[0],o=document.createElement("link");"string"==typeof a&&(i=a);var s=(i||e).replace(/\.|\//g,""),l="layuicss-"+s,d=0;o.rel="stylesheet",o.href=n.path+e,o.id=l,document.getElementById(l)||r.appendChild(o),"function"==typeof a&&!function c(){return++d>80?window.console&&console.error("laydate.css: Invalid"):void(1989===parseInt(t.getStyle(document.getElementById(l),"width"))?a():setTimeout(c,100))}()}}},n={v:"5.0.9",config:{},index:window.laydate&&window.laydate.v?1e5:0,path:t.getPath,set:function(e){var t=this;return t.config=w.extend({},t.config,e),t},ready:function(a){var i="laydate",r="",o=(e?"modules/laydate/":"theme/")+"default/laydate.css?v="+n.v+r;return e?layui.addcss(o,a,i):t.link(o,a,i),this}},a=function(){var e=this;return{hint:function(t){e.hint.call(e,t)},config:e.config}},i="laydate",r=".layui-laydate",o="layui-this",s="laydate-disabled",l="开始日期超出了结束日期
    建议重新选择",d=[100,2e5],c="layui-laydate-static",m="layui-laydate-list",u="laydate-selected",h="layui-laydate-hint",y="laydate-day-prev",f="laydate-day-next",p="layui-laydate-footer",g=".laydate-btns-confirm",v="laydate-time-text",D=".laydate-btns-time",T=function(e){var t=this;t.index=++n.index,t.config=w.extend({},t.config,n.config,e),n.ready(function(){t.init()})},w=function(e){return new C(e)},C=function(e){for(var t=0,n="object"==typeof e?[e]:(this.selector=e,document.querySelectorAll(e||null));t0)return n[0].getAttribute(e)}():n.each(function(n,a){a.setAttribute(e,t)})},C.prototype.removeAttr=function(e){return this.each(function(t,n){n.removeAttribute(e)})},C.prototype.html=function(e){return this.each(function(t,n){n.innerHTML=e})},C.prototype.val=function(e){return this.each(function(t,n){n.value=e})},C.prototype.append=function(e){return this.each(function(t,n){"object"==typeof e?n.appendChild(e):n.innerHTML=n.innerHTML+e})},C.prototype.remove=function(e){return this.each(function(t,n){e?n.removeChild(e):n.parentNode.removeChild(n)})},C.prototype.on=function(e,t){return this.each(function(n,a){a.attachEvent?a.attachEvent("on"+e,function(e){e.target=e.srcElement,t.call(a,e)}):a.addEventListener(e,t,!1)})},C.prototype.off=function(e,t){return this.each(function(n,a){a.detachEvent?a.detachEvent("on"+e,t):a.removeEventListener(e,t,!1)})},T.isLeapYear=function(e){return e%4===0&&e%100!==0||e%400===0},T.prototype.config={type:"date",range:!1,format:"yyyy-MM-dd",value:null,isInitValue:!0,min:"1900-1-1",max:"2099-12-31",trigger:"focus",show:!1,showBottom:!0,btns:["clear","now","confirm"],lang:"cn",theme:"default",position:null,calendar:!1,mark:{},zIndex:null,done:null,change:null},T.prototype.lang=function(){var e=this,t=e.config,n={cn:{weeks:["日","一","二","三","四","五","六"],time:["时","分","秒"],timeTips:"选择时间",startTime:"开始时间",endTime:"结束时间",dateTips:"返回日期",month:["一","二","三","四","五","六","七","八","九","十","十一","十二"],tools:{confirm:"确定",clear:"清空",now:"现在"}},en:{weeks:["Su","Mo","Tu","We","Th","Fr","Sa"],time:["Hours","Minutes","Seconds"],timeTips:"Select Time",startTime:"Start Time",endTime:"End Time",dateTips:"Select Date",month:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],tools:{confirm:"Confirm",clear:"Clear",now:"Now"}}};return n[t.lang]||n.cn},T.prototype.init=function(){var e=this,t=e.config,n="yyyy|y|MM|M|dd|d|HH|H|mm|m|ss|s",a="static"===t.position,i={year:"yyyy",month:"yyyy-MM",date:"yyyy-MM-dd",time:"HH:mm:ss",datetime:"yyyy-MM-dd HH:mm:ss"};t.elem=w(t.elem),t.eventElem=w(t.eventElem),t.elem[0]&&(t.range===!0&&(t.range="-"),t.format===i.date&&(t.format=i[t.type]),e.format=t.format.match(new RegExp(n+"|.","g"))||[],e.EXP_IF="",e.EXP_SPLIT="",w.each(e.format,function(t,a){var i=new RegExp(n).test(a)?"\\d{"+function(){return new RegExp(n).test(e.format[0===t?t+1:t-1]||"")?/^yyyy|y$/.test(a)?4:a.length:/^yyyy$/.test(a)?"1,4":/^y$/.test(a)?"1,308":"1,2"}()+"}":"\\"+a;e.EXP_IF=e.EXP_IF+i,e.EXP_SPLIT=e.EXP_SPLIT+"("+i+")"}),e.EXP_IF=new RegExp("^"+(t.range?e.EXP_IF+"\\s\\"+t.range+"\\s"+e.EXP_IF:e.EXP_IF)+"$"),e.EXP_SPLIT=new RegExp("^"+e.EXP_SPLIT+"$",""),e.isInput(t.elem[0])||"focus"===t.trigger&&(t.trigger="click"),t.elem.attr("lay-key")||(t.elem.attr("lay-key",e.index),t.eventElem.attr("lay-key",e.index)),t.mark=w.extend({},t.calendar&&"cn"===t.lang?{"0-1-1":"元旦","0-2-14":"情人","0-3-8":"妇女","0-3-12":"植树","0-4-1":"愚人","0-5-1":"劳动","0-5-4":"青年","0-6-1":"儿童","0-9-10":"教师","0-9-18":"国耻","0-10-1":"国庆","0-12-25":"圣诞"}:{},t.mark),w.each(["min","max"],function(e,n){var a=[],i=[];if("number"==typeof t[n]){var r=t[n],o=(new Date).getTime(),s=864e5,l=new Date(r?r0)return!0;var a=w.elem("div",{"class":"layui-laydate-header"}),i=[function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-y"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-m"});return e.innerHTML="",e}(),function(){var e=w.elem("div",{"class":"laydate-set-ym"}),t=w.elem("span"),n=w.elem("span");return e.appendChild(t),e.appendChild(n),e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-m"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-y"});return e.innerHTML="",e}()],d=w.elem("div",{"class":"layui-laydate-content"}),c=w.elem("table"),m=w.elem("thead"),u=w.elem("tr");w.each(i,function(e,t){a.appendChild(t)}),m.appendChild(u),w.each(new Array(6),function(e){var t=c.insertRow(0);w.each(new Array(7),function(a){if(0===e){var i=w.elem("th");i.innerHTML=n.weeks[a],u.appendChild(i)}t.insertCell(a)})}),c.insertBefore(m,c.children[0]),d.appendChild(c),r[e]=w.elem("div",{"class":"layui-laydate-main laydate-main-list-"+e}),r[e].appendChild(a),r[e].appendChild(d),o.push(i),s.push(d),l.push(c)}),w(d).html(function(){var e=[],i=[];return"datetime"===t.type&&e.push(''+n.timeTips+""),w.each(t.btns,function(e,r){var o=n.tools[r]||"btn";t.range&&"now"===r||(a&&"clear"===r&&(o="cn"===t.lang?"重置":"Reset"),i.push(''+o+""))}),e.push('"),e.join("")}()),w.each(r,function(e,t){i.appendChild(t)}),t.showBottom&&i.appendChild(d),/^#/.test(t.theme)){var m=w.elem("style"),u=["#{{id}} .layui-laydate-header{background-color:{{theme}};}","#{{id}} .layui-this{background-color:{{theme}} !important;}"].join("").replace(/{{id}}/g,e.elemID).replace(/{{theme}}/g,t.theme);"styleSheet"in m?(m.setAttribute("type","text/css"),m.styleSheet.cssText=u):m.innerHTML=u,w(i).addClass("laydate-theme-molv"),i.appendChild(m)}e.remove(T.thisElemDate),a?t.elem.append(i):(document.body.appendChild(i),e.position()),e.checkDate().calendar(),e.changeEvent(),T.thisElemDate=e.elemID,"function"==typeof t.ready&&t.ready(w.extend({},t.dateTime,{month:t.dateTime.month+1}))},T.prototype.remove=function(e){var t=this,n=(t.config,w("#"+(e||t.elemID)));return n.hasClass(c)||t.checkDate(function(){n.remove()}),t},T.prototype.position=function(){var e=this,t=e.config,n=e.bindElem||t.elem[0],a=n.getBoundingClientRect(),i=e.elem.offsetWidth,r=e.elem.offsetHeight,o=function(e){return e=e?"scrollLeft":"scrollTop",document.body[e]|document.documentElement[e]},s=function(e){return document.documentElement[e?"clientWidth":"clientHeight"]},l=5,d=a.left,c=a.bottom;d+i+l>s("width")&&(d=s("width")-i-l),c+r+l>s()&&(c=a.top>r?a.top-r:s()-r,c-=2*l),t.position&&(e.elem.style.position=t.position),e.elem.style.left=d+("fixed"===t.position?0:o(1))+"px",e.elem.style.top=c+("fixed"===t.position?0:o())+"px"},T.prototype.hint=function(e){var t=this,n=(t.config,w.elem("div",{"class":h}));t.elem&&(n.innerHTML=e||"",w(t.elem).find("."+h).remove(),t.elem.appendChild(n),clearTimeout(t.hinTimer),t.hinTimer=setTimeout(function(){w(t.elem).find("."+h).remove()},3e3))},T.prototype.getAsYM=function(e,t,n){return n?t--:t++,t<0&&(t=11,e--),t>11&&(t=0,e++),[e,t]},T.prototype.systemDate=function(e){var t=e||new Date;return{year:t.getFullYear(),month:t.getMonth(),date:t.getDate(),hours:e?e.getHours():0,minutes:e?e.getMinutes():0,seconds:e?e.getSeconds():0}},T.prototype.checkDate=function(e){var t,a,i=this,r=(new Date,i.config),o=r.dateTime=r.dateTime||i.systemDate(),s=i.bindElem||r.elem[0],l=(i.isInput(s)?"val":"html",i.isInput(s)?s.value:"static"===r.position?"":s.innerHTML),c=function(e){e.year>d[1]&&(e.year=d[1],a=!0),e.month>11&&(e.month=11,a=!0),e.hours>23&&(e.hours=0,a=!0),e.minutes>59&&(e.minutes=0,e.hours++,a=!0),e.seconds>59&&(e.seconds=0,e.minutes++,a=!0),t=n.getEndDate(e.month+1,e.year),e.date>t&&(e.date=t,a=!0)},m=function(e,t,n){var o=["startTime","endTime"];t=(t.match(i.EXP_SPLIT)||[]).slice(1),n=n||0,r.range&&(i[o[n]]=i[o[n]]||{}),w.each(i.format,function(s,l){var c=parseFloat(t[s]);t[s].length必须遵循下述格式:
    "+(r.range?r.format+" "+r.range+" "+r.format:r.format)+"
    已为你重置"),a=!0):l&&l.constructor===Date?r.dateTime=i.systemDate(l):(r.dateTime=i.systemDate(),delete i.startState,delete i.endState,delete i.startDate,delete i.endDate,delete i.startTime,delete i.endTime),c(o),a&&l&&i.setValue(r.range?i.endDate?i.parse():"":i.parse()),e&&e(),i)},T.prototype.mark=function(e,t){var n,a=this,i=a.config;return w.each(i.mark,function(e,a){var i=e.split("-");i[0]!=t[0]&&0!=i[0]||i[1]!=t[1]&&0!=i[1]||i[2]!=t[2]||(n=a||t[2])}),n&&e.html(''+n+""),a},T.prototype.limit=function(e,t,n,a){var i,r=this,o=r.config,l={},d=o[n>41?"endDate":"dateTime"],c=w.extend({},d,t||{});return w.each({now:c,min:o.min,max:o.max},function(e,t){l[e]=r.newDate(w.extend({year:t.year,month:t.month,date:t.date},function(){var e={};return w.each(a,function(n,a){e[a]=t[a]}),e}())).getTime()}),i=l.nowl.max,e&&e[i?"addClass":"removeClass"](s),i},T.prototype.calendar=function(e){var t,a,i,r=this,s=r.config,l=e||s.dateTime,c=new Date,m=r.lang(),u="date"!==s.type&&"datetime"!==s.type,h=e?1:0,y=w(r.table[h]).find("td"),f=w(r.elemHeader[h][2]).find("span");if(l.yeard[1]&&(l.year=d[1],r.hint("最高只能支持到公元"+d[1]+"年")),r.firstDate||(r.firstDate=w.extend({},l)),c.setFullYear(l.year,l.month,1),t=c.getDay(),a=n.getEndDate(l.month||12,l.year),i=n.getEndDate(l.month+1,l.year),w.each(y,function(e,n){var d=[l.year,l.month],c=0;n=w(n),n.removeAttr("class"),e=t&&e=n.firstDate.year&&(r.month=a.max.month,r.date=a.max.date),n.limit(w(i),r,t),M++}),w(u[f?0:1]).attr("lay-ym",M-8+"-"+T[1]).html(b+p+" - "+(M-1+p))}else if("month"===e)w.each(new Array(12),function(e){var i=w.elem("li",{"lay-ym":e}),s={year:T[0],month:e};e+1==T[1]&&w(i).addClass(o),i.innerHTML=r.month[e]+(f?"月":""),d.appendChild(i),T[0]=n.firstDate.year&&(s.date=a.max.date),n.limit(w(i),s,t)}),w(u[f?0:1]).attr("lay-ym",T[0]+"-"+T[1]).html(T[0]+p);else if("time"===e){var E=function(){w(d).find("ol").each(function(e,a){w(a).find("li").each(function(a,i){n.limit(w(i),[{hours:a},{hours:n[x].hours,minutes:a},{hours:n[x].hours,minutes:n[x].minutes,seconds:a}][e],t,[["hours"],["hours","minutes"],["hours","minutes","seconds"]][e])})}),a.range||n.limit(w(n.footer).find(g),n[x],0,["hours","minutes","seconds"])};a.range?n[x]||(n[x]={hours:0,minutes:0,seconds:0}):n[x]=i,w.each([24,60,60],function(e,t){var a=w.elem("li"),i=["

    "+r.time[e]+"

      "];w.each(new Array(t),function(t){i.push(""+w.digit(t,2)+"")}),a.innerHTML=i.join("")+"
    ",d.appendChild(a)}),E()}if(y&&h.removeChild(y),h.appendChild(d),"year"===e||"month"===e)w(n.elemMain[t]).addClass("laydate-ym-show"),w(d).find("li").on("click",function(){var r=0|w(this).attr("lay-ym");if(!w(this).hasClass(s)){if(0===t)i[e]=r,l&&(n.startDate[e]=r),n.limit(w(n.footer).find(g),null,0);else if(l)n.endDate[e]=r;else{var c="year"===e?n.getAsYM(r,T[1]-1,"sub"):n.getAsYM(T[0],r,"sub");w.extend(i,{year:c[0],month:c[1]})}"year"===a.type||"month"===a.type?(w(d).find("."+o).removeClass(o),w(this).addClass(o),"month"===a.type&&"year"===e&&(n.listYM[t][0]=r,l&&(n[["startDate","endDate"][t]].year=r),n.list("month",t))):(n.checkDate("limit").calendar(),n.closeList()),n.setBtnStatus(),a.range||n.done(null,"change"),w(n.footer).find(D).removeClass(s)}});else{var S=w.elem("span",{"class":v}),k=function(){w(d).find("ol").each(function(e){var t=this,a=w(t).find("li");t.scrollTop=30*(n[x][C[e]]-2),t.scrollTop<=0&&a.each(function(e,n){if(!w(this).hasClass(s))return t.scrollTop=30*(e-2),!0})})},H=w(c[2]).find("."+v);k(),S.innerHTML=a.range?[r.startTime,r.endTime][t]:r.timeTips,w(n.elemMain[t]).addClass("laydate-time-show"),H[0]&&H.remove(),c[2].appendChild(S),w(d).find("ol").each(function(e){var t=this;w(t).find("li").on("click",function(){var r=0|this.innerHTML;w(this).hasClass(s)||(a.range?n[x][C[e]]=r:i[C[e]]=r,w(t).find("."+o).removeClass(o),w(this).addClass(o),E(),k(),(n.endDate||"time"===a.type)&&n.done(null,"change"),n.setBtnStatus())})})}return n},T.prototype.listYM=[],T.prototype.closeList=function(){var e=this;e.config;w.each(e.elemCont,function(t,n){w(this).find("."+m).remove(),w(e.elemMain[t]).removeClass("laydate-ym-show laydate-time-show")}),w(e.elem).find("."+v).remove()},T.prototype.setBtnStatus=function(e,t,n){var a,i=this,r=i.config,o=w(i.footer).find(g),d=r.range&&"date"!==r.type&&"time"!==r.type;d&&(t=t||i.startDate,n=n||i.endDate,a=i.newDate(t).getTime()>i.newDate(n).getTime(),i.limit(null,t)||i.limit(null,n)?o.addClass(s):o[a?"addClass":"removeClass"](s),e&&a&&i.hint("string"==typeof e?l.replace(/日期/g,e):l))},T.prototype.parse=function(e,t){var n=this,a=n.config,i=t||(e?w.extend({},n.endDate,n.endTime):a.range?w.extend({},n.startDate,n.startTime):a.dateTime),r=n.format.concat();return w.each(r,function(e,t){/yyyy|y/.test(t)?r[e]=w.digit(i.year,t.length):/MM|M/.test(t)?r[e]=w.digit(i.month+1,t.length):/dd|d/.test(t)?r[e]=w.digit(i.date,t.length):/HH|H/.test(t)?r[e]=w.digit(i.hours,t.length):/mm|m/.test(t)?r[e]=w.digit(i.minutes,t.length):/ss|s/.test(t)&&(r[e]=w.digit(i.seconds,t.length))}),a.range&&!e?r.join("")+" "+a.range+" "+n.parse(1):r.join("")},T.prototype.newDate=function(e){return e=e||{},new Date(e.year||1,e.month||0,e.date||1,e.hours||0,e.minutes||0,e.seconds||0)},T.prototype.setValue=function(e){var t=this,n=t.config,a=t.bindElem||n.elem[0],i=t.isInput(a)?"val":"html";return"static"===n.position||w(a)[i](e||""),this},T.prototype.stampRange=function(){var e,t,n=this,a=n.config,i=w(n.elem).find("td");if(a.range&&!n.endDate&&w(n.footer).find(g).addClass(s),n.endDate)return e=n.newDate({year:n.startDate.year,month:n.startDate.month,date:n.startDate.date}).getTime(),t=n.newDate({year:n.endDate.year,month:n.endDate.month,date:n.endDate.date}).getTime(),e>t?n.hint(l):void w.each(i,function(a,i){var r=w(i).attr("lay-ymd").split("-"),s=n.newDate({year:r[0],month:r[1]-1,date:r[2]}).getTime();w(i).removeClass(u+" "+o),s!==e&&s!==t||w(i).addClass(w(i).hasClass(y)||w(i).hasClass(f)?u:o),s>e&&s','
    '+f+"
    ",'
    ','',"
    ","
    "].join(""));return l.ie&&l.ie<8?c.removeClass("layui-hide").addClass(o):(d[0]&&d.remove(),s.call(a,m,c[0],y),c.addClass("layui-hide").after(m),a.index)},c.prototype.getContent=function(t){var e=u(t);if(e[0])return d(e[0].document.body.innerHTML)},c.prototype.getText=function(t){var i=u(t);if(i[0])return e(i[0].document.body).text()},c.prototype.setContent=function(t,i,a){var l=u(t);l[0]&&(a?e(l[0].document.body).append(i):e(l[0].document.body).html(i),layedit.sync(t))},c.prototype.sync=function(t){var i=u(t);if(i[0]){var a=e("#"+i[1].attr("textarea"));a.val(d(i[0].document.body.innerHTML))}},c.prototype.getSelection=function(t){var e=u(t);if(e[0]){var i=m(e[0].document);return document.selection?i.text:i.toString()}};var s=function(t,i,a){var l=this,n=t.find("iframe");n.css({height:a.height}).on("load",function(){var o=n.contents(),r=n.prop("contentWindow"),c=o.find("head"),s=e([""].join("")),u=o.find("body");c.append(s),u.attr("contenteditable","true").css({"min-height":a.height}).html(i.value||""),y.apply(l,[r,n,i,a]),g.call(l,r,t,a)})},u=function(t){var i=e("#LAY_layedit_"+t),a=i.prop("contentWindow");return[a,i]},d=function(t){return 8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),t},y=function(t,a,n,o){var r=t.document,c=e(r.body);c.on("keydown",function(t){var e=t.keyCode;if(13===e){var a=m(r),l=p(a),n=l.parentNode;if("pre"===n.tagName.toLowerCase()){if(t.shiftKey)return;return i.msg("请暂时用shift+enter"),!1}r.execCommand("formatBlock",!1,"

    ")}}),e(n).parents("form").on("submit",function(){var t=c.html();8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),n.value=t}),c.on("paste",function(e){r.execCommand("formatBlock",!1,"

    "),setTimeout(function(){f.call(t,c),n.value=c.html()},100)})},f=function(t){var i=this;i.document;t.find("*[style]").each(function(){var t=this.style.textAlign;this.removeAttribute("style"),e(this).css({"text-align":t||""})}),t.find("table").addClass("layui-table"),t.find("script,link").remove()},m=function(t){return t.selection?t.selection.createRange():t.getSelection().getRangeAt(0)},p=function(t){return t.endContainer||t.parentElement().childNodes[0]},v=function(t,i,a){var l=this.document,n=document.createElement(t);for(var o in i)n.setAttribute(o,i[o]);if(n.removeAttribute("text"),l.selection){var r=a.text||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.pasteHTML(e(n).prop("outerHTML")),a.select()}else{var r=a.toString()||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.deleteContents(),a.insertNode(n)}},h=function(t,i){var a=this.document,l="layedit-tool-active",n=p(m(a)),o=function(e){return t.find(".layedit-tool-"+e)};i&&i[i.hasClass(l)?"removeClass":"addClass"](l),t.find(">i").removeClass(l),o("unlink").addClass(r),e(n).parents().each(function(){var t=this.tagName.toLowerCase(),e=this.style.textAlign;"b"!==t&&"strong"!==t||o("b").addClass(l),"i"!==t&&"em"!==t||o("i").addClass(l),"u"===t&&o("u").addClass(l),"strike"===t&&o("d").addClass(l),"p"===t&&("center"===e?o("center").addClass(l):"right"===e?o("right").addClass(l):o("left").addClass(l)),"a"===t&&(o("link").addClass(l),o("unlink").removeClass(r))})},g=function(t,a,l){var n=t.document,o=e(n.body),c={link:function(i){var a=p(i),l=e(a).parent();b.call(o,{href:l.attr("href"),target:l.attr("target")},function(e){var a=l[0];"A"===a.tagName?a.href=e.url:v.call(t,"a",{target:e.target,href:e.url,text:e.url},i)})},unlink:function(t){n.execCommand("unlink")},face:function(e){x.call(this,function(i){v.call(t,"img",{src:i.src,alt:i.alt},e)})},image:function(a){var n=this;layui.use("upload",function(o){var r=l.uploadImage||{};o.render({url:r.url,method:r.type,elem:e(n).find("input")[0],done:function(e){0==e.code?(e.data=e.data||{},v.call(t,"img",{src:e.data.src,alt:e.data.title},a)):i.msg(e.msg||"上传失败")}})})},code:function(e){k.call(o,function(i){v.call(t,"pre",{text:i.code,"lay-lang":i.lang},e)})},help:function(){i.open({type:2,title:"帮助",area:["600px","380px"],shadeClose:!0,shade:.1,skin:"layui-layer-msg",content:["http://www.layui.com/about/layedit/help.html","no"]})}},s=a.find(".layui-layedit-tool"),u=function(){var i=e(this),a=i.attr("layedit-event"),l=i.attr("lay-command");if(!i.hasClass(r)){o.focus();var u=m(n);u.commonAncestorContainer;l?(n.execCommand(l),/justifyLeft|justifyCenter|justifyRight/.test(l)&&n.execCommand("formatBlock",!1,"

    "),setTimeout(function(){o.focus()},10)):c[a]&&c[a].call(this,u),h.call(t,s,i)}},d=/image/;s.find(">i").on("mousedown",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)||u.call(this)}).on("click",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)&&u.call(this)}),o.on("click",function(){h.call(t,s),i.close(x.index)})},b=function(t,e){var l=this,n=i.open({type:1,id:"LAY_layedit_link",area:"350px",shade:.05,shadeClose:!0,moveType:1,title:"超链接",skin:"layui-layer-msg",content:['

      ','
    • ','','
      ','',"
      ","
    • ",'
    • ','','
      ','",'","
      ","
    • ",'
    • ','','',"
    • ","
    "].join(""),success:function(t,n){var o="submit(layedit-link-yes)";a.render("radio"),t.find(".layui-btn-primary").on("click",function(){i.close(n),l.focus()}),a.on(o,function(t){i.close(b.index),e&&e(t.field)})}});b.index=n},x=function(t){var a=function(){var t=["[微笑]","[嘻嘻]","[哈哈]","[可爱]","[可怜]","[挖鼻]","[吃惊]","[害羞]","[挤眼]","[闭嘴]","[鄙视]","[爱你]","[泪]","[偷笑]","[亲亲]","[生病]","[太开心]","[白眼]","[右哼哼]","[左哼哼]","[嘘]","[衰]","[委屈]","[吐]","[哈欠]","[抱抱]","[怒]","[疑问]","[馋嘴]","[拜拜]","[思考]","[汗]","[困]","[睡]","[钱]","[失望]","[酷]","[色]","[哼]","[鼓掌]","[晕]","[悲伤]","[抓狂]","[黑线]","[阴险]","[怒骂]","[互粉]","[心]","[伤心]","[猪头]","[熊猫]","[兔子]","[ok]","[耶]","[good]","[NO]","[赞]","[来]","[弱]","[草泥马]","[神马]","[囧]","[浮云]","[给力]","[围观]","[威武]","[奥特曼]","[礼物]","[钟]","[话筒]","[蜡烛]","[蛋糕]"],e={};return layui.each(t,function(t,i){e[i]=layui.cache.dir+"images/face/"+t+".gif"}),e}();return x.hide=x.hide||function(t){"face"!==e(t.target).attr("layedit-event")&&i.close(x.index)},x.index=i.tips(function(){var t=[];return layui.each(a,function(e,i){t.push('
  4. '+e+'
  5. ')}),'
      '+t.join("")+"
    "}(),this,{tips:1,time:0,skin:"layui-box layui-util-face",maxWidth:500,success:function(l,n){l.css({marginTop:-4,marginLeft:-10}).find(".layui-clear>li").on("click",function(){t&&t({src:a[this.title],alt:this.title}),i.close(n)}),e(document).off("click",x.hide).on("click",x.hide)}})},k=function(t){var e=this,l=i.open({type:1,id:"LAY_layedit_code",area:"550px",shade:.05,shadeClose:!0,moveType:1,title:"插入代码",skin:"layui-layer-msg",content:['
      ','
    • ','','
      ','","
      ","
    • ",'
    • ','','
      ','',"
      ","
    • ",'
    • ','','',"
    • ","
    "].join(""),success:function(l,n){var o="submit(layedit-code-yes)";a.render("select"),l.find(".layui-btn-primary").on("click",function(){i.close(n),e.focus()}),a.on(o,function(e){i.close(k.index),t&&t(e.field)})}});k.index=l},C={html:'',strong:'',italic:'',underline:'',del:'',"|":'',left:'',center:'',right:'',link:'',unlink:'',face:'',image:'',code:'',help:''},w=new c;t(n,w)}); \ No newline at end of file diff --git a/static/layui/lay/modules/layer.js b/static/layui/lay/modules/layer.js new file mode 100644 index 0000000..c6d5378 --- /dev/null +++ b/static/layui/lay/modules/layer.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,i=t.length-1,n=i;n>0;n--)if("interactive"===t[n].readyState){e=t[n].src;break}return e||t[i].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"],getStyle:function(t,i){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](i)},link:function(t,i,n){if(r.path){var a=document.getElementsByTagName("head")[0],s=document.createElement("link");"string"==typeof i&&(n=i);var l=(n||t).replace(/\.|\//g,""),f="layuicss-"+l,c=0;s.rel="stylesheet",s.href=r.path+t,s.id=f,document.getElementById(f)||a.appendChild(s),"function"==typeof i&&!function u(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(o.getStyle(document.getElementById(f),"width"))?i():setTimeout(u,100))}()}}},r={v:"3.1.1",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):o.link("theme/"+e.extend),this):this},ready:function(e){var t="layer",i="",n=(a?"modules/layer/":"theme/")+"default/layer.css?v="+r.v+i;return a?layui.addcss(n,e,t):o.link(n,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s="function"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+" layui-layer-hui",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];l.anim=["layer-anim-00","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'
    '+(f?r.title[0]:r.title)+"
    ":"";return r.zIndex=s,t([r.shade?'
    ':"",'
    '+(e&&2!=r.type?"":u)+'
    '+(0==r.type&&r.icon!==-1?'':"")+(1==r.type&&e?"":r.content||"")+'
    '+function(){var e=c?'':"";return r.closeBtn&&(e+=''),e}()+""+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t'+r.btn[t]+"";return'
    '+e+"
    "}():"")+(r.resize?'':"")+"
    "],u,i('
    ')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f="object"==typeof s,c=i("body");if(!t.id||!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var s=t.content=f?t.content:[t.content||"","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}if(e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){s.parents("."+l[0])[0]||(s.data("display",s.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+l[0]+a).find("."+l[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=u),e.layero=i("#"+l[0]+a),t.scrollbar||l.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),i("#layui-layer-shade"+e.index).css({"background-color":t.shade[1]||"#000",opacity:t.shade[0]||t.shade}),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]){var u="layer-anim "+l.anim[t.anim];e.layero.addClass(u).one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",function(){i(this).removeClass(u)})}t.isOutAnim&&e.layero.data("isOutAnim",!0)}},s.pt.auto=function(e){var t=this,a=t.config,o=i("#"+l[0]+e);""===a.area[0]&&a.maxWidth>0&&(r.ie&&r.ie<8&&a.btn&&o.width(o.innerWidth()),o.outerWidth()>a.maxWidth&&o.width(a.maxWidth));var s=[o.innerWidth(),o.innerHeight()],f=o.find(l[1]).outerHeight()||0,c=o.find("."+l[6]).outerHeight()||0,u=function(e){e=o.find(e),e.height(s[1]-f-c-2*(0|parseFloat(e.css("padding-top"))))};switch(a.type){case 2:u("iframe");break;default:""===a.area[1]?a.maxHeight>0&&o.outerHeight()>a.maxHeight?(s[1]=a.maxHeight,u("."+l[5])):a.fixed&&s[1]>=n.height()&&(s[1]=n.height(),u("."+l[5])):u("."+l[5])}return t},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:"auto"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find("."+l[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(".layui-layer-resize"),c={};return t.move&&l.css("cursor","move"),l.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css("left")),e.clientY-parseFloat(s.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l="fixed"===s.css("position");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;af&&(a=f),ou&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+l[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+l[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+l[0])[0]||1==n.attr("layer")&&i("."+l[0]).length<1&&n.removeAttr("layer").show(),n=null})},s.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+l[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr("layer-full")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty("overflow"):l.html[0].style.removeAttribute("overflow"),l.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+l[4]).attr("times"),i("#"+l[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+l[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find("."+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+l[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+l[0]+e),r=a.find(".layui-layer-content"),s=a.attr("type"),f=a.find(l[1]).outerHeight()||0,c=a.find("."+l[6]).outerHeight()||0;a.attr("minLeft");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+l[6]).outerHeight(),s===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+l[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+l[0]+e);o.record(a),l.html.attr("layer-full")||l.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i("#"+l[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var s="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+l[5]+")").remove();for(var a=t.find("."+s),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(s)}else{if(n===o.type[2])try{var f=i("#"+l[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("isOutAnim")&&t.addClass("layer-anim "+a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),r.ie&&r.ie<10||!t.data("isOutAnim")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i("."+l[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var s,l=2==e.formType?'":function(){return''}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:["确定","取消"],content:l,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(t){s=t.find(".layui-layer-input"),s.val(e.value||"").focus(),"function"==typeof f&&f(t)},resize:!1,yes:function(i){var n=s.val();""===n?s.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n="layui-this",a=e.success;return delete e.success,r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,a="";if(e>0)for(a=''+t[0].title+"";i"+t[i].title+"";return a}(),content:'
      '+function(){var e=t.length,i=1,a="";if(e>0)for(a='
    • '+(t[0].content||"no content")+"
    • ";i'+(t[i].content||"no content")+"";return a}()+"
    ",success:function(t){var o=t.find(".layui-layer-title").children(),r=t.find(".layui-layer-tabmain").children();o.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var a=i(this),o=a.index();a.addClass(n).siblings().removeClass(n),r.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)}),"function"==typeof a&&a(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||"img";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg("没有图片")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),u.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(h(),0===u.length)return;if(n||p.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),s.imgnext()}),i(document).on("keyup",s.keyup)},s.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:"layui-layer-photos",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(u[d].alt||
    '+(u.length>1?'':"")+'
    '+(u[d].alt||"")+""+s.imgIndex+"/"+u.length+"
    ",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
    是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.$),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window); \ No newline at end of file diff --git a/static/layui/lay/modules/laypage.js b/static/layui/lay/modules/laypage.js new file mode 100644 index 0000000..fd07fda --- /dev/null +++ b/static/layui/lay/modules/laypage.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define(function(e){"use strict";var a=document,t="getElementById",n="getElementsByTagName",i="laypage",r="layui-disabled",u=function(e){var a=this;a.config=e||{},a.config.index=++s.index,a.render(!0)};u.prototype.type=function(){var e=this.config;if("object"==typeof e.elem)return void 0===e.elem.length?2:3},u.prototype.view=function(){var e=this,a=e.config,t=a.groups="groups"in a?0|a.groups:5;a.layout="object"==typeof a.layout?a.layout:["prev","page","next"],a.count=0|a.count,a.curr=0|a.curr||1,a.limits="object"==typeof a.limits?a.limits:[10,20,30,40,50],a.limit=0|a.limit||10,a.pages=Math.ceil(a.count/a.limit)||1,a.curr>a.pages&&(a.curr=a.pages),t<0?t=1:t>a.pages&&(t=a.pages),a.prev="prev"in a?a.prev:"上一页",a.next="next"in a?a.next:"下一页";var n=a.pages>t?Math.ceil((a.curr+(t>1?1:0))/(t>0?t:1)):1,i={prev:function(){return a.prev?''+a.prev+"":""}(),page:function(){var e=[];if(a.count<1)return"";n>1&&a.first!==!1&&0!==t&&e.push(''+(a.first||1)+"");var i=Math.floor((t-1)/2),r=n>1?a.curr-i:1,u=n>1?function(){var e=a.curr+(t-i-1);return e>a.pages?a.pages:e}():t;for(u-r2&&e.push('');r<=u;r++)r===a.curr?e.push('"+r+""):e.push(''+r+"");return a.pages>t&&a.pages>u&&a.last!==!1&&(u+1…'),0!==t&&e.push(''+(a.last||a.pages)+"")),e.join("")}(),next:function(){return a.next?''+a.next+"":""}(),count:'共 '+a.count+" 条",limit:function(){var e=['"}(),refresh:['','',""].join(""),skip:function(){return['到第','','页',""].join("")}()};return['
    ',function(){var e=[];return layui.each(a.layout,function(a,t){i[t]&&e.push(i[t])}),e.join("")}(),"
    "].join("")},u.prototype.jump=function(e,a){if(e){var t=this,i=t.config,r=e.children,u=e[n]("button")[0],l=e[n]("input")[0],p=e[n]("select")[0],c=function(){var e=0|l.value.replace(/\s|\D/g,"");e&&(i.curr=e,t.render())};if(a)return c();for(var o=0,y=r.length;oi.pages||(i.curr=e,t.render())});p&&s.on(p,"change",function(){var e=this.value;i.curr*e>i.count&&(i.curr=Math.ceil(i.count/e)),i.limit=e,t.render()}),u&&s.on(u,"click",function(){c()})}},u.prototype.skip=function(e){if(e){var a=this,t=e[n]("input")[0];t&&s.on(t,"keyup",function(t){var n=this.value,i=t.keyCode;/^(37|38|39|40)$/.test(i)||(/\D/.test(n)&&(this.value=n.replace(/\D/,"")),13===i&&a.jump(e,!0))})}},u.prototype.render=function(e){var n=this,i=n.config,r=n.type(),u=n.view();2===r?i.elem&&(i.elem.innerHTML=u):3===r?i.elem.html(u):a[t](i.elem)&&(a[t](i.elem).innerHTML=u),i.jump&&i.jump(i,e);var s=a[t]("layui-laypage-"+i.index);n.jump(s),i.hash&&!e&&(location.hash="!"+i.hash+"="+i.curr),n.skip(s)};var s={render:function(e){var a=new u(e);return a.index},index:layui.laypage?layui.laypage.index+1e4:0,on:function(e,a,t){return e.attachEvent?e.attachEvent("on"+a,function(a){a.target=a.srcElement,t.call(e,a)}):e.addEventListener(a,t,!1),this}};e(i,s)}); \ No newline at end of file diff --git a/static/layui/lay/modules/laytpl.js b/static/layui/lay/modules/laytpl.js new file mode 100644 index 0000000..d482d4e --- /dev/null +++ b/static/layui/lay/modules/laytpl.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},c={exp:function(e){return new RegExp(e,"g")},query:function(e,c,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return n((c||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)}); \ No newline at end of file diff --git a/static/layui/lay/modules/mobile.js b/static/layui/lay/modules/mobile.js new file mode 100644 index 0000000..35cfdf0 --- /dev/null +++ b/static/layui/lay/modules/mobile.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define(function(i){i("layui.mobile",layui.v)});layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},c={exp:function(e){return new RegExp(e,"g")},query:function(e,c,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return n((c||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)});layui.define(function(e){"use strict";var t=(window,document),i="querySelectorAll",n="getElementsByClassName",a=function(e){return t[i](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var i in e)t[i]=e[i];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var o=0,r=["layui-m-layer"],d=function(e){var t=this;t.config=l.extend(e),t.view()};d.prototype.view=function(){var e=this,i=e.config,s=t.createElement("div");e.id=s.id=r[0]+o,s.setAttribute("class",r[0]+" "+r[0]+(i.type||0)),s.setAttribute("index",o);var l=function(){var e="object"==typeof i.title;return i.title?'

    '+(e?i.title[0]:i.title)+"

    ":""}(),d=function(){"string"==typeof i.btn&&(i.btn=[i.btn]);var e,t=(i.btn||[]).length;return 0!==t&&i.btn?(e=''+i.btn[0]+"",2===t&&(e=''+i.btn[1]+""+e),'
    '+e+"
    "):""}();if(i.fixed||(i.top=i.hasOwnProperty("top")?i.top:100,i.style=i.style||"",i.style+=" top:"+(t.body.scrollTop+i.top)+"px"),2===i.type&&(i.content='

    '+(i.content||"")+"

    "),i.skin&&(i.anim="up"),"msg"===i.skin&&(i.shade=!1),s.innerHTML=(i.shade?"
    ':"")+'
    "+l+'
    '+i.content+"
    "+d+"
    ",!i.type||2===i.type){var y=t[n](r[0]+i.type),u=y.length;u>=1&&c.close(y[0].getAttribute("index"))}document.body.appendChild(s);var m=e.elem=a("#"+e.id)[0];i.success&&i.success(m),e.index=o++,e.action(i,m)},d.prototype.action=function(e,t){var i=this;e.time&&(l.timer[i.index]=setTimeout(function(){c.close(i.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),c.close(i.index)):e.yes?e.yes(i.index):c.close(i.index)};if(e.btn)for(var s=t[n]("layui-m-layerbtn")[0].children,o=s.length,r=0;r0&&e-1 in t)}function s(t){return A.call(t,function(t){return null!=t})}function u(t){return t.length>0?T.fn.concat.apply([],t):t}function c(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function l(t){return t in F?F[t]:F[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function f(t,e){return"number"!=typeof e||k[c(t)]?e:e+"px"}function h(t){var e,n;return $[t]||(e=L.createElement(t),L.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),$[t]=n),$[t]}function p(t){return"children"in t?D.call(t.children):T.map(t.childNodes,function(t){if(1==t.nodeType)return t})}function d(t,e){var n,r=t?t.length:0;for(n=0;n]*>/,R=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,Z=/^(?:body|html)$/i,q=/([A-Z])/g,H=["val","css","html","text","data","width","height","offset"],I=["after","prepend","before","append"],V=L.createElement("table"),_=L.createElement("tr"),B={tr:L.createElement("tbody"),tbody:V,thead:V,tfoot:V,td:_,th:_,"*":L.createElement("div")},U=/complete|loaded|interactive/,X=/^[\w-]*$/,J={},W=J.toString,Y={},G=L.createElement("div"),K={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},Q=Array.isArray||function(t){return t instanceof Array};return Y.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.matches||t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var r,i=t.parentNode,o=!i;return o&&(i=G).appendChild(t),r=~Y.qsa(i,e).indexOf(t),o&&G.removeChild(t),r},C=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},N=function(t){return A.call(t,function(e,n){return t.indexOf(e)==n})},Y.fragment=function(t,e,n){var r,i,a;return R.test(t)&&(r=T(L.createElement(RegExp.$1))),r||(t.replace&&(t=t.replace(z,"<$1>")),e===E&&(e=M.test(t)&&RegExp.$1),e in B||(e="*"),a=B[e],a.innerHTML=""+t,r=T.each(D.call(a.childNodes),function(){a.removeChild(this)})),o(n)&&(i=T(r),T.each(n,function(t,e){H.indexOf(t)>-1?i[t](e):i.attr(t,e)})),r},Y.Z=function(t,e){return new d(t,e)},Y.isZ=function(t){return t instanceof Y.Z},Y.init=function(t,n){var r;if(!t)return Y.Z();if("string"==typeof t)if(t=t.trim(),"<"==t[0]&&M.test(t))r=Y.fragment(t,RegExp.$1,n),t=null;else{if(n!==E)return T(n).find(t);r=Y.qsa(L,t)}else{if(e(t))return T(L).ready(t);if(Y.isZ(t))return t;if(Q(t))r=s(t);else if(i(t))r=[t],t=null;else if(M.test(t))r=Y.fragment(t.trim(),RegExp.$1,n),t=null;else{if(n!==E)return T(n).find(t);r=Y.qsa(L,t)}}return Y.Z(r,t)},T=function(t,e){return Y.init(t,e)},T.extend=function(t){var e,n=D.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){m(t,n,e)}),t},Y.qsa=function(t,e){var n,r="#"==e[0],i=!r&&"."==e[0],o=r||i?e.slice(1):e,a=X.test(o);return t.getElementById&&a&&r?(n=t.getElementById(o))?[n]:[]:1!==t.nodeType&&9!==t.nodeType&&11!==t.nodeType?[]:D.call(a&&!r&&t.getElementsByClassName?i?t.getElementsByClassName(o):t.getElementsByTagName(e):t.querySelectorAll(e))},T.contains=L.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},T.type=t,T.isFunction=e,T.isWindow=n,T.isArray=Q,T.isPlainObject=o,T.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},T.isNumeric=function(t){var e=Number(t),n=typeof t;return null!=t&&"boolean"!=n&&("string"!=n||t.length)&&!isNaN(e)&&isFinite(e)||!1},T.inArray=function(t,e,n){return O.indexOf.call(e,t,n)},T.camelCase=C,T.trim=function(t){return null==t?"":String.prototype.trim.call(t)},T.uuid=0,T.support={},T.expr={},T.noop=function(){},T.map=function(t,e){var n,r,i,o=[];if(a(t))for(r=0;r=0?t:t+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return O.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return e(t)?this.not(this.not(t)):T(A.call(this,function(e){return Y.matches(e,t)}))},add:function(t,e){return T(N(this.concat(T(t,e))))},is:function(t){return this.length>0&&Y.matches(this[0],t)},not:function(t){var n=[];if(e(t)&&t.call!==E)this.each(function(e){t.call(this,e)||n.push(this)});else{var r="string"==typeof t?this.filter(t):a(t)&&e(t.item)?D.call(t):T(t);this.forEach(function(t){r.indexOf(t)<0&&n.push(t)})}return T(n)},has:function(t){return this.filter(function(){return i(t)?T.contains(this,t):T(this).find(t).size()})},eq:function(t){return t===-1?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!i(t)?t:T(t)},last:function(){var t=this[this.length-1];return t&&!i(t)?t:T(t)},find:function(t){var e,n=this;return e=t?"object"==typeof t?T(t).filter(function(){var t=this;return O.some.call(n,function(e){return T.contains(e,t)})}):1==this.length?T(Y.qsa(this[0],t)):this.map(function(){return Y.qsa(this,t)}):T()},closest:function(t,e){var n=[],i="object"==typeof t&&T(t);return this.each(function(o,a){for(;a&&!(i?i.indexOf(a)>=0:Y.matches(a,t));)a=a!==e&&!r(a)&&a.parentNode;a&&n.indexOf(a)<0&&n.push(a)}),T(n)},parents:function(t){for(var e=[],n=this;n.length>0;)n=T.map(n,function(t){if((t=t.parentNode)&&!r(t)&&e.indexOf(t)<0)return e.push(t),t});return v(e,t)},parent:function(t){return v(N(this.pluck("parentNode")),t)},children:function(t){return v(this.map(function(){return p(this)}),t)},contents:function(){return this.map(function(){return this.contentDocument||D.call(this.childNodes)})},siblings:function(t){return v(this.map(function(t,e){return A.call(p(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return T.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=h(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var n=e(t);if(this[0]&&!n)var r=T(t).get(0),i=r.parentNode||this.length>1;return this.each(function(e){T(this).wrapAll(n?t.call(this,e):i?r.cloneNode(!0):r)})},wrapAll:function(t){if(this[0]){T(this[0]).before(t=T(t));for(var e;(e=t.children()).length;)t=e.first();T(t).append(this)}return this},wrapInner:function(t){var n=e(t);return this.each(function(e){var r=T(this),i=r.contents(),o=n?t.call(this,e):t;i.length?i.wrapAll(o):r.append(o)})},unwrap:function(){return this.parent().each(function(){T(this).replaceWith(T(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(t){return this.each(function(){var e=T(this);(t===E?"none"==e.css("display"):t)?e.show():e.hide()})},prev:function(t){return T(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return T(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var n=this.innerHTML;T(this).empty().append(g(this,t,e,n))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=g(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this.pluck("textContent").join(""):null},attr:function(t,e){var n;return"string"!=typeof t||1 in arguments?this.each(function(n){if(1===this.nodeType)if(i(t))for(j in t)y(this,j,t[j]);else y(this,t,g(this,e,n,this.getAttribute(t)))}):0 in this&&1==this[0].nodeType&&null!=(n=this[0].getAttribute(t))?n:E},removeAttr:function(t){return this.each(function(){1===this.nodeType&&t.split(" ").forEach(function(t){y(this,t)},this)})},prop:function(t,e){return t=K[t]||t,1 in arguments?this.each(function(n){this[t]=g(this,e,n,this[t])}):this[0]&&this[0][t]},removeProp:function(t){return t=K[t]||t,this.each(function(){delete this[t]})},data:function(t,e){var n="data-"+t.replace(q,"-$1").toLowerCase(),r=1 in arguments?this.attr(n,e):this.attr(n);return null!==r?b(r):E},val:function(t){return 0 in arguments?(null==t&&(t=""),this.each(function(e){this.value=g(this,t,e,this.value)})):this[0]&&(this[0].multiple?T(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(t){if(t)return this.each(function(e){var n=T(this),r=g(this,t,e,n.offset()),i=n.offsetParent().offset(),o={top:r.top-i.top,left:r.left-i.left};"static"==n.css("position")&&(o.position="relative"),n.css(o)});if(!this.length)return null;if(L.documentElement!==this[0]&&!T.contains(L.documentElement,this[0]))return{top:0,left:0};var e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(e,n){if(arguments.length<2){var r=this[0];if("string"==typeof e){if(!r)return;return r.style[C(e)]||getComputedStyle(r,"").getPropertyValue(e)}if(Q(e)){if(!r)return;var i={},o=getComputedStyle(r,"");return T.each(e,function(t,e){i[e]=r.style[C(e)]||o.getPropertyValue(e)}),i}}var a="";if("string"==t(e))n||0===n?a=c(e)+":"+f(e,n):this.each(function(){this.style.removeProperty(c(e))});else for(j in e)e[j]||0===e[j]?a+=c(j)+":"+f(j,e[j])+";":this.each(function(){this.style.removeProperty(c(j))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf(T(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return!!t&&O.some.call(this,function(t){return this.test(x(t))},l(t))},addClass:function(t){return t?this.each(function(e){if("className"in this){S=[];var n=x(this),r=g(this,t,e,n);r.split(/\s+/g).forEach(function(t){T(this).hasClass(t)||S.push(t)},this),S.length&&x(this,n+(n?" ":"")+S.join(" "))}}):this},removeClass:function(t){return this.each(function(e){if("className"in this){if(t===E)return x(this,"");S=x(this),g(this,t,e,S).split(/\s+/g).forEach(function(t){S=S.replace(l(t)," ")}),x(this,S.trim())}})},toggleClass:function(t,e){return t?this.each(function(n){var r=T(this),i=g(this,t,n,x(this));i.split(/\s+/g).forEach(function(t){(e===E?!r.hasClass(t):e)?r.addClass(t):r.removeClass(t)})}):this},scrollTop:function(t){if(this.length){var e="scrollTop"in this[0];return t===E?e?this[0].scrollTop:this[0].pageYOffset:this.each(e?function(){this.scrollTop=t}:function(){this.scrollTo(this.scrollX,t)})}},scrollLeft:function(t){if(this.length){var e="scrollLeft"in this[0];return t===E?e?this[0].scrollLeft:this[0].pageXOffset:this.each(e?function(){this.scrollLeft=t}:function(){this.scrollTo(t,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),n=this.offset(),r=Z.test(e[0].nodeName)?{top:0,left:0}:e.offset();return n.top-=parseFloat(T(t).css("margin-top"))||0,n.left-=parseFloat(T(t).css("margin-left"))||0,r.top+=parseFloat(T(e[0]).css("border-top-width"))||0,r.left+=parseFloat(T(e[0]).css("border-left-width"))||0,{top:n.top-r.top,left:n.left-r.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||L.body;t&&!Z.test(t.nodeName)&&"static"==T(t).css("position");)t=t.offsetParent;return t})}},T.fn.detach=T.fn.remove,["width","height"].forEach(function(t){var e=t.replace(/./,function(t){return t[0].toUpperCase()});T.fn[t]=function(i){var o,a=this[0];return i===E?n(a)?a["inner"+e]:r(a)?a.documentElement["scroll"+e]:(o=this.offset())&&o[t]:this.each(function(e){a=T(this),a.css(t,g(this,i,e,a[t]()))})}}),I.forEach(function(e,n){var r=n%2;T.fn[e]=function(){var e,i,o=T.map(arguments,function(n){var r=[];return e=t(n),"array"==e?(n.forEach(function(t){return t.nodeType!==E?r.push(t):T.zepto.isZ(t)?r=r.concat(t.get()):void(r=r.concat(Y.fragment(t)))}),r):"object"==e||null==n?n:Y.fragment(n)}),a=this.length>1;return o.length<1?this:this.each(function(t,e){i=r?e:e.parentNode,e=0==n?e.nextSibling:1==n?e.firstChild:2==n?e:null;var s=T.contains(L.documentElement,i);o.forEach(function(t){if(a)t=t.cloneNode(!0);else if(!i)return T(t).remove();i.insertBefore(t,e),s&&w(t,function(t){if(!(null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src)){var e=t.ownerDocument?t.ownerDocument.defaultView:window;e.eval.call(e,t.innerHTML)}})})})},T.fn[r?e+"To":"insert"+(n?"Before":"After")]=function(t){return T(t)[e](this),this}}),Y.Z.prototype=d.prototype=T.fn,Y.uniq=N,Y.deserializeValue=b,T.zepto=Y,T}();!function(t){function e(t){return t._zid||(t._zid=h++)}function n(t,n,o,a){if(n=r(n),n.ns)var s=i(n.ns);return(v[e(t)]||[]).filter(function(t){return t&&(!n.e||t.e==n.e)&&(!n.ns||s.test(t.ns))&&(!o||e(t.fn)===e(o))&&(!a||t.sel==a)})}function r(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function i(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function o(t,e){return t.del&&!y&&t.e in x||!!e}function a(t){return b[t]||y&&x[t]||t}function s(n,i,s,u,l,h,p){var d=e(n),m=v[d]||(v[d]=[]);i.split(/\s/).forEach(function(e){if("ready"==e)return t(document).ready(s);var i=r(e);i.fn=s,i.sel=l,i.e in b&&(s=function(e){var n=e.relatedTarget;if(!n||n!==this&&!t.contains(this,n))return i.fn.apply(this,arguments)}),i.del=h;var d=h||s;i.proxy=function(t){if(t=c(t),!t.isImmediatePropagationStopped()){t.data=u;var e=d.apply(n,t._args==f?[t]:[t].concat(t._args));return e===!1&&(t.preventDefault(),t.stopPropagation()),e}},i.i=m.length,m.push(i),"addEventListener"in n&&n.addEventListener(a(i.e),i.proxy,o(i,p))})}function u(t,r,i,s,u){var c=e(t);(r||"").split(/\s/).forEach(function(e){n(t,e,i,s).forEach(function(e){delete v[c][e.i],"removeEventListener"in t&&t.removeEventListener(a(e.e),e.proxy,o(e,u))})})}function c(e,n){return!n&&e.isDefaultPrevented||(n||(n=e),t.each(T,function(t,r){var i=n[t];e[t]=function(){return this[r]=w,i&&i.apply(n,arguments)},e[r]=E}),e.timeStamp||(e.timeStamp=Date.now()),(n.defaultPrevented!==f?n.defaultPrevented:"returnValue"in n?n.returnValue===!1:n.getPreventDefault&&n.getPreventDefault())&&(e.isDefaultPrevented=w)),e}function l(t){var e,n={originalEvent:t};for(e in t)j.test(e)||t[e]===f||(n[e]=t[e]);return c(n,t)}var f,h=1,p=Array.prototype.slice,d=t.isFunction,m=function(t){return"string"==typeof t},v={},g={},y="onfocusin"in window,x={focus:"focusin",blur:"focusout"},b={mouseenter:"mouseover",mouseleave:"mouseout"};g.click=g.mousedown=g.mouseup=g.mousemove="MouseEvents",t.event={add:s,remove:u},t.proxy=function(n,r){var i=2 in arguments&&p.call(arguments,2);if(d(n)){var o=function(){return n.apply(r,i?i.concat(p.call(arguments)):arguments)};return o._zid=e(n),o}if(m(r))return i?(i.unshift(n[r],n),t.proxy.apply(null,i)):t.proxy(n[r],n);throw new TypeError("expected function")},t.fn.bind=function(t,e,n){return this.on(t,e,n)},t.fn.unbind=function(t,e){return this.off(t,e)},t.fn.one=function(t,e,n,r){return this.on(t,e,n,r,1)};var w=function(){return!0},E=function(){return!1},j=/^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,T={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return this.off(e,t,n)},t.fn.live=function(e,n){return t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,n,r,i,o){var a,c,h=this;return e&&!m(e)?(t.each(e,function(t,e){h.on(t,n,r,e,o)}),h):(m(n)||d(i)||i===!1||(i=r,r=n,n=f),i!==f&&r!==!1||(i=r,r=f),i===!1&&(i=E),h.each(function(f,h){o&&(a=function(t){return u(h,t.type,i),i.apply(this,arguments)}),n&&(c=function(e){var r,o=t(e.target).closest(n,h).get(0);if(o&&o!==h)return r=t.extend(l(e),{currentTarget:o,liveFired:h}),(a||i).apply(o,[r].concat(p.call(arguments,1)))}),s(h,e,i,r,n,c||a)}))},t.fn.off=function(e,n,r){var i=this;return e&&!m(e)?(t.each(e,function(t,e){i.off(t,n,e)}),i):(m(n)||d(r)||r===!1||(r=n,n=f),r===!1&&(r=E),i.each(function(){u(this,e,r,n)}))},t.fn.trigger=function(e,n){return e=m(e)||t.isPlainObject(e)?t.Event(e):c(e),e._args=n,this.each(function(){e.type in x&&"function"==typeof this[e.type]?this[e.type]():"dispatchEvent"in this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,r){var i,o;return this.each(function(a,s){i=l(m(e)?t.Event(e):e),i._args=r,i.target=s,t.each(n(s,e.type||e),function(t,e){if(o=e.proxy(i),i.isImmediatePropagationStopped())return!1})}),o},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(e){t.fn[e]=function(t){return 0 in arguments?this.bind(e,t):this.trigger(e)}}),t.Event=function(t,e){m(t)||(e=t,t=e.type);var n=document.createEvent(g[t]||"Events"),r=!0;if(e)for(var i in e)"bubbles"==i?r=!!e[i]:n[i]=e[i];return n.initEvent(t,r,!0),c(n)}}(e),function(t){function e(e,n,r){var i=t.Event(n);return t(e).trigger(i,r),!i.isDefaultPrevented()}function n(t,n,r,i){if(t.global)return e(n||x,r,i)}function r(e){e.global&&0===t.active++&&n(e,null,"ajaxStart")}function i(e){e.global&&!--t.active&&n(e,null,"ajaxStop")}function o(t,e){var r=e.context;return e.beforeSend.call(r,t,e)!==!1&&n(e,r,"ajaxBeforeSend",[t,e])!==!1&&void n(e,r,"ajaxSend",[t,e])}function a(t,e,r,i){var o=r.context,a="success";r.success.call(o,t,a,e),i&&i.resolveWith(o,[t,a,e]),n(r,o,"ajaxSuccess",[e,r,t]),u(a,e,r)}function s(t,e,r,i,o){var a=i.context;i.error.call(a,r,e,t),o&&o.rejectWith(a,[r,e,t]),n(i,a,"ajaxError",[r,i,t||e]),u(e,r,i)}function u(t,e,r){var o=r.context;r.complete.call(o,e,t),n(r,o,"ajaxComplete",[e,r]),i(r)}function c(t,e,n){if(n.dataFilter==l)return t;var r=n.context;return n.dataFilter.call(r,t,e)}function l(){}function f(t){return t&&(t=t.split(";",2)[0]),t&&(t==T?"html":t==j?"json":w.test(t)?"script":E.test(t)&&"xml")||"text"}function h(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function p(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()&&"jsonp"!=e.dataType||(e.url=h(e.url,e.data),e.data=void 0)}function d(e,n,r,i){return t.isFunction(n)&&(i=r,r=n,n=void 0),t.isFunction(r)||(i=r,r=void 0),{url:e,data:n,success:r,dataType:i}}function m(e,n,r,i){var o,a=t.isArray(n),s=t.isPlainObject(n);t.each(n,function(n,u){o=t.type(u),i&&(n=r?i:i+"["+(s||"object"==o||"array"==o?n:"")+"]"),!i&&a?e.add(u.name,u.value):"array"==o||!r&&"object"==o?m(e,u,r,n):e.add(n,u)})}var v,g,y=+new Date,x=window.document,b=/)<[^<]*)*<\/script>/gi,w=/^(?:text|application)\/javascript/i,E=/^(?:text|application)\/xml/i,j="application/json",T="text/html",S=/^\s*$/,C=x.createElement("a");C.href=window.location.href,t.active=0,t.ajaxJSONP=function(e,n){if(!("type"in e))return t.ajax(e);var r,i,u=e.jsonpCallback,c=(t.isFunction(u)?u():u)||"Zepto"+y++,l=x.createElement("script"),f=window[c],h=function(e){t(l).triggerHandler("error",e||"abort")},p={abort:h};return n&&n.promise(p),t(l).on("load error",function(o,u){clearTimeout(i),t(l).off().remove(),"error"!=o.type&&r?a(r[0],p,e,n):s(null,u||"error",p,e,n),window[c]=f,r&&t.isFunction(f)&&f(r[0]),f=r=void 0}),o(p,e)===!1?(h("abort"),p):(window[c]=function(){r=arguments},l.src=e.url.replace(/\?(.+)=\?/,"?$1="+c),x.head.appendChild(l),e.timeout>0&&(i=setTimeout(function(){h("timeout")},e.timeout)),p)},t.ajaxSettings={type:"GET",beforeSend:l,success:l,error:l,complete:l,context:null,global:!0,xhr:function(){return new window.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript, application/x-javascript",json:j,xml:"application/xml, text/xml",html:T,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0,dataFilter:l},t.ajax=function(e){var n,i,u=t.extend({},e||{}),d=t.Deferred&&t.Deferred();for(v in t.ajaxSettings)void 0===u[v]&&(u[v]=t.ajaxSettings[v]);r(u),u.crossDomain||(n=x.createElement("a"),n.href=u.url,n.href=n.href,u.crossDomain=C.protocol+"//"+C.host!=n.protocol+"//"+n.host),u.url||(u.url=window.location.toString()),(i=u.url.indexOf("#"))>-1&&(u.url=u.url.slice(0,i)),p(u);var m=u.dataType,y=/\?.+=\?/.test(u.url);if(y&&(m="jsonp"),u.cache!==!1&&(e&&e.cache===!0||"script"!=m&&"jsonp"!=m)||(u.url=h(u.url,"_="+Date.now())),"jsonp"==m)return y||(u.url=h(u.url,u.jsonp?u.jsonp+"=?":u.jsonp===!1?"":"callback=?")),t.ajaxJSONP(u,d);var b,w=u.accepts[m],E={},j=function(t,e){E[t.toLowerCase()]=[t,e]},T=/^([\w-]+:)\/\//.test(u.url)?RegExp.$1:window.location.protocol,N=u.xhr(),O=N.setRequestHeader;if(d&&d.promise(N),u.crossDomain||j("X-Requested-With","XMLHttpRequest"),j("Accept",w||"*/*"),(w=u.mimeType||w)&&(w.indexOf(",")>-1&&(w=w.split(",",2)[0]),N.overrideMimeType&&N.overrideMimeType(w)),(u.contentType||u.contentType!==!1&&u.data&&"GET"!=u.type.toUpperCase())&&j("Content-Type",u.contentType||"application/x-www-form-urlencoded"),u.headers)for(g in u.headers)j(g,u.headers[g]);if(N.setRequestHeader=j,N.onreadystatechange=function(){if(4==N.readyState){N.onreadystatechange=l,clearTimeout(b);var e,n=!1;if(N.status>=200&&N.status<300||304==N.status||0==N.status&&"file:"==T){if(m=m||f(u.mimeType||N.getResponseHeader("content-type")),"arraybuffer"==N.responseType||"blob"==N.responseType)e=N.response;else{e=N.responseText;try{e=c(e,m,u),"script"==m?(0,eval)(e):"xml"==m?e=N.responseXML:"json"==m&&(e=S.test(e)?null:t.parseJSON(e))}catch(r){n=r}if(n)return s(n,"parsererror",N,u,d)}a(e,N,u,d)}else s(N.statusText||null,N.status?"error":"abort",N,u,d)}},o(N,u)===!1)return N.abort(),s(null,"abort",N,u,d),N;var P=!("async"in u)||u.async;if(N.open(u.type,u.url,P,u.username,u.password),u.xhrFields)for(g in u.xhrFields)N[g]=u.xhrFields[g];for(g in E)O.apply(N,E[g]);return u.timeout>0&&(b=setTimeout(function(){N.onreadystatechange=l,N.abort(),s(null,"timeout",N,u,d)},u.timeout)),N.send(u.data?u.data:null),N},t.get=function(){return t.ajax(d.apply(null,arguments))},t.post=function(){var e=d.apply(null,arguments);return e.type="POST",t.ajax(e)},t.getJSON=function(){var e=d.apply(null,arguments);return e.dataType="json",t.ajax(e)},t.fn.load=function(e,n,r){if(!this.length)return this;var i,o=this,a=e.split(/\s/),s=d(e,n,r),u=s.success;return a.length>1&&(s.url=a[0],i=a[1]),s.success=function(e){o.html(i?t("
    ").html(e.replace(b,"")).find(i):e),u&&u.apply(o,arguments)},t.ajax(s),this};var N=encodeURIComponent;t.param=function(e,n){var r=[];return r.add=function(e,n){t.isFunction(n)&&(n=n()),null==n&&(n=""),this.push(N(e)+"="+N(n))},m(r,e,n),r.join("&").replace(/%20/g,"+")}}(e),function(t){t.fn.serializeArray=function(){var e,n,r=[],i=function(t){return t.forEach?t.forEach(i):void r.push({name:e,value:t})};return this[0]&&t.each(this[0].elements,function(r,o){n=o.type,e=o.name,e&&"fieldset"!=o.nodeName.toLowerCase()&&!o.disabled&&"submit"!=n&&"reset"!=n&&"button"!=n&&"file"!=n&&("radio"!=n&&"checkbox"!=n||o.checked)&&i(t(o).val())}),r},t.fn.serialize=function(){var t=[];return this.serializeArray().forEach(function(e){t.push(encodeURIComponent(e.name)+"="+encodeURIComponent(e.value))}),t.join("&")},t.fn.submit=function(e){if(0 in arguments)this.bind("submit",e);else if(this.length){var n=t.Event("submit");this.eq(0).trigger(n),n.isDefaultPrevented()||this.get(0).submit()}return this}}(e),function(){try{getComputedStyle(void 0)}catch(t){var e=getComputedStyle;window.getComputedStyle=function(t,n){try{return e(t,n)}catch(r){return null}}}}(),t("zepto",e)});layui.define(["layer-mobile","zepto"],function(e){"use strict";var t=layui.zepto,a=layui["layer-mobile"],i=(layui.device(),"layui-upload-enter"),n="layui-upload-iframe",r={icon:2,shift:6},o={file:"文件",video:"视频",audio:"音频"};a.msg=function(e){return a.open({content:e||"",skin:"msg",time:2})};var s=function(e){this.options=e};s.prototype.init=function(){var e=this,a=e.options,r=t("body"),s=t(a.elem||".layui-upload-file"),u=t('');return t("#"+n)[0]||r.append(u),s.each(function(r,s){s=t(s);var u='
    ',l=s.attr("lay-type")||a.type;a.unwrap||(u='
    '+u+''+(s.attr("lay-title")||a.title||"上传"+(o[l]||"图片"))+"
    "),u=t(u),a.unwrap||u.on("dragover",function(e){e.preventDefault(),t(this).addClass(i)}).on("dragleave",function(){t(this).removeClass(i)}).on("drop",function(){t(this).removeClass(i)}),s.parent("form").attr("target")===n&&(a.unwrap?s.unwrap():(s.parent().next().remove(),s.unwrap().unwrap())),s.wrap(u),s.off("change").on("change",function(){e.action(this,l)})})},s.prototype.action=function(e,i){var o=this,s=o.options,u=e.value,l=t(e),p=l.attr("lay-ext")||s.ext||"";if(u){switch(i){case"file":if(p&&!RegExp("\\w\\.("+p+")$","i").test(escape(u)))return a.msg("不支持该文件格式",r),e.value="";break;case"video":if(!RegExp("\\w\\.("+(p||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(u)))return a.msg("不支持该视频格式",r),e.value="";break;case"audio":if(!RegExp("\\w\\.("+(p||"mp3|wav|mid")+")$","i").test(escape(u)))return a.msg("不支持该音频格式",r),e.value="";break;default:if(!RegExp("\\w\\.("+(p||"jpg|png|gif|bmp|jpeg")+")$","i").test(escape(u)))return a.msg("不支持该图片格式",r),e.value=""}s.before&&s.before(e),l.parent().submit();var c=t("#"+n),f=setInterval(function(){var t;try{t=c.contents().find("body").text()}catch(i){a.msg("上传接口存在跨域",r),clearInterval(f)}if(t){clearInterval(f),c.contents().find("body").html("");try{t=JSON.parse(t)}catch(i){return t={},a.msg("请对上传接口返回JSON字符",r)}"function"==typeof s.success&&s.success(t,e)}},30);e.value=""}},e("upload-mobile",function(e){var t=new s(e=e||{});t.init()})});layui.define(function(i){i("layim-mobile",layui.v)});layui["layui.mobile"]||layui.config({base:layui.cache.dir+"lay/modules/mobile/"}).extend({"layer-mobile":"layer-mobile",zepto:"zepto","upload-mobile":"upload-mobile","layim-mobile":"layim-mobile"}),layui.define(["layer-mobile","zepto","layim-mobile"],function(l){l("mobile",{layer:layui["layer-mobile"],layim:layui["layim-mobile"]})}); \ No newline at end of file diff --git a/static/layui/lay/modules/rate.js b/static/layui/lay/modules/rate.js new file mode 100644 index 0000000..762fb3a --- /dev/null +++ b/static/layui/lay/modules/rate.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var a=layui.jquery,i={config:{},index:layui.rate?layui.rate.index+1e4:0,set:function(e){var i=this;return i.config=a.extend({},i.config,e),i},on:function(e,a){return layui.onevent.call(this,n,e,a)}},l=function(){var e=this,a=e.config;return{setvalue:function(a){e.setvalue.call(e,a)},config:a}},n="rate",t="layui-rate",o="layui-icon-rate",s="layui-icon-rate-solid",u="layui-icon-rate-half",r="layui-icon-rate-solid layui-icon-rate-half",c="layui-icon-rate-solid layui-icon-rate",f="layui-icon-rate layui-icon-rate-half",v=function(e){var l=this;l.index=++i.index,l.config=a.extend({},l.config,i.config,e),l.render()};v.prototype.config={length:5,text:!1,readonly:!1,half:!1,value:0,theme:""},v.prototype.render=function(){var e=this,i=e.config,l=i.theme?'style="color: '+i.theme+';"':"";i.elem=a(i.elem),parseInt(i.value)!==i.value&&(i.half||(i.value=Math.ceil(i.value)-i.value<.5?Math.ceil(i.value):Math.floor(i.value)));for(var n='
      ",u=1;u<=i.length;u++){var r='
    • ";i.half&&parseInt(i.value)!==i.value&&u==Math.ceil(i.value)?n=n+'
    • ":n+=r}n+="
    "+(i.text?''+i.value+"星":"")+"";var c=i.elem,f=c.next("."+t);f[0]&&f.remove(),e.elemTemp=a(n),i.span=e.elemTemp.next("span"),i.setText&&i.setText(i.value),c.html(e.elemTemp),c.addClass("layui-inline"),i.readonly||e.action()},v.prototype.setvalue=function(e){var a=this,i=a.config;i.value=e,a.render()},v.prototype.action=function(){var e=this,i=e.config,l=e.elemTemp,n=l.find("i").width();l.children("li").each(function(e){var t=e+1,v=a(this);v.on("click",function(e){if(i.value=t,i.half){var o=e.pageX-a(this).offset().left;o<=n/2&&(i.value=i.value-.5)}i.text&&l.next("span").text(i.value+"星"),i.choose&&i.choose(i.value),i.setText&&i.setText(i.value)}),v.on("mousemove",function(e){if(l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+t+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half){var c=e.pageX-a(this).offset().left;c<=n/2&&v.children("i").addClass(u).removeClass(s)}}),v.on("mouseleave",function(){l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+Math.floor(i.value)+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half&&parseInt(i.value)!==i.value&&l.children("li:eq("+Math.floor(i.value)+")").children("i").addClass(u).removeClass(c)})})},v.prototype.events=function(){var e=this;e.config},i.render=function(e){var a=new v(e);return l.call(a)},e(n,i)}); \ No newline at end of file diff --git a/static/layui/lay/modules/slider.js b/static/layui/lay/modules/slider.js new file mode 100644 index 0000000..446378d --- /dev/null +++ b/static/layui/lay/modules/slider.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var i=layui.jquery,t={config:{},index:layui.slider?layui.slider.index+1e4:0,set:function(e){var t=this;return t.config=i.extend({},t.config,e),t},on:function(e,i){return layui.onevent.call(this,n,e,i)}},a=function(){var e=this,i=e.config;return{setValue:function(i,t){return e.slide("set",i,t||0)},config:i}},n="slider",l="layui-disabled",s="layui-slider",r="layui-slider-bar",o="layui-slider-wrap",u="layui-slider-wrap-btn",d="layui-slider-tips",v="layui-slider-input",c="layui-slider-input-txt",m="layui-slider-input-btn",p="layui-slider-hover",f=function(e){var a=this;a.index=++t.index,a.config=i.extend({},a.config,t.config,e),a.render()};f.prototype.config={type:"default",min:0,max:100,value:0,step:1,showstep:!1,tips:!0,input:!1,range:!1,height:200,disabled:!1,theme:"#009688"},f.prototype.render=function(){var e=this,t=e.config;if(t.step<1&&(t.step=1),t.maxt.min?a:t.min,t.value[1]=n>t.min?n:t.min,t.value[0]=t.value[0]>t.max?t.max:t.value[0],t.value[1]=t.value[1]>t.max?t.max:t.value[1];var r=Math.floor((t.value[0]-t.min)/(t.max-t.min)*100),v=Math.floor((t.value[1]-t.min)/(t.max-t.min)*100),m=v-r+"%";r+="%",v+="%"}else{"object"==typeof t.value&&(t.value=Math.min.apply(null,t.value)),t.valuet.max&&(t.value=t.max);var m=Math.floor((t.value-t.min)/(t.max-t.min)*100)+"%"}var p=t.disabled?"#c2c2c2":t.theme,f='
    '+(t.tips?'
    ':"")+'
    '+(t.range?'
    ':"")+"
    ",h=i(t.elem),y=h.next("."+s);if(y[0]&&y.remove(),e.elemTemp=i(f),t.range?(e.elemTemp.find("."+o).eq(0).data("value",t.value[0]),e.elemTemp.find("."+o).eq(1).data("value",t.value[1])):e.elemTemp.find("."+o).data("value",t.value),h.html(e.elemTemp),"vertical"===t.type&&e.elemTemp.height(t.height+"px"),t.showstep){for(var g=(t.max-t.min)/t.step,b="",x=1;x
    ')}e.elemTemp.append(b)}if(t.input&&!t.range){var w=i('
    ');h.css("position","relative"),h.append(w),h.find("."+c).children("input").val(t.value),"vertical"===t.type?w.css({left:0,top:-48}):e.elemTemp.css("margin-right",w.outerWidth()+15)}t.disabled?(e.elemTemp.addClass(l),e.elemTemp.find("."+u).addClass(l)):e.slide(),e.elemTemp.find("."+u).on("mouseover",function(){var a="vertical"===t.type?t.height:e.elemTemp[0].offsetWidth,n=e.elemTemp.find("."+o),l="vertical"===t.type?a-i(this).parent()[0].offsetTop-n.height():i(this).parent()[0].offsetLeft,s=l/a*100,r=i(this).parent().data("value"),u=t.setTips?t.setTips(r):r;e.elemTemp.find("."+d).html(u),"vertical"===t.type?e.elemTemp.find("."+d).css({bottom:s+"%","margin-bottom":"20px",display:"inline-block"}):e.elemTemp.find("."+d).css({left:s+"%",display:"inline-block"})}).on("mouseout",function(){e.elemTemp.find("."+d).css("display","none")})},f.prototype.slide=function(e,t,a){var n=this,l=n.config,s=n.elemTemp,f=function(){return"vertical"===l.type?l.height:s[0].offsetWidth},h=s.find("."+o),y=s.next("."+v),g=y.children("."+c).children("input").val(),b=100/((l.max-l.min)/Math.ceil(l.step)),x=function(e,i){e=Math.ceil(e)*b>100?Math.ceil(e)*b:Math.round(e)*b,e=e>100?100:e,h.eq(i).css("vertical"===l.type?"bottom":"left",e+"%");var t=T(h[0].offsetLeft),a=l.range?T(h[1].offsetLeft):0;"vertical"===l.type?(s.find("."+d).css({bottom:e+"%","margin-bottom":"20px"}),t=T(f()-h[0].offsetTop-h.height()),a=l.range?T(f()-h[1].offsetTop-h.height()):0):s.find("."+d).css("left",e+"%"),t=t>100?100:t,a=a>100?100:a;var n=Math.min(t,a),o=Math.abs(t-a);"vertical"===l.type?s.find("."+r).css({height:o+"%",bottom:n+"%"}):s.find("."+r).css({width:o+"%",left:n+"%"});var u=l.min+Math.round((l.max-l.min)*e/100);if(g=u,y.children("."+c).children("input").val(g),h.eq(i).data("value",u),u=l.setTips?l.setTips(u):u,s.find("."+d).html(u),l.range){var v=[h.eq(0).data("value"),h.eq(1).data("value")];v[0]>v[1]&&v.reverse()}l.change&&l.change(l.range?v:u)},T=function(e){var i=e/f()*100/b,t=Math.round(i)*b;return e==f()&&(t=Math.ceil(i)*b),t},w=i(['
    f()&&(r=f());var o=r/f()*100/b;x(o,e),t.addClass(p),s.find("."+d).show(),i.preventDefault()},o=function(){t.removeClass(p),s.find("."+d).hide()};M(r,o)})}),s.on("click",function(e){var t=i("."+u);if(!t.is(event.target)&&0===t.has(event.target).length&&t.length){var a,n="vertical"===l.type?f()-e.clientY+i(this).offset().top:e.clientX-i(this).offset().left;n<0&&(n=0),n>f()&&(n=f());var s=n/f()*100/b;a=l.range?"vertical"===l.type?Math.abs(n-parseInt(i(h[0]).css("bottom")))>Math.abs(n-parseInt(i(h[1]).css("bottom")))?1:0:Math.abs(n-h[0].offsetLeft)>Math.abs(n-h[1].offsetLeft)?1:0:0,x(s,a),e.preventDefault()}}),y.hover(function(){var e=i(this);e.children("."+m).fadeIn("fast")},function(){var e=i(this);e.children("."+m).fadeOut("fast")}),y.children("."+m).children("i").each(function(e){i(this).on("click",function(){g=1==e?g-l.stepl.max?l.max:Number(g)+l.step;var i=(g-l.min)/(l.max-l.min)*100/b;x(i,0)})});var q=function(){var e=this.value;e=isNaN(e)?0:e,e=el.max?l.max:e,this.value=e;var i=(e-l.min)/(l.max-l.min)*100/b;x(i,0)};y.children("."+c).children("input").on("keydown",function(e){13===e.keyCode&&(e.preventDefault(),q.call(this))}).on("change",q)},f.prototype.events=function(){var e=this;e.config},t.render=function(e){var i=new f(e);return a.call(i)},e(n,t)}); \ No newline at end of file diff --git a/static/layui/lay/modules/table.js b/static/layui/lay/modules/table.js new file mode 100644 index 0000000..e9217db --- /dev/null +++ b/static/layui/lay/modules/table.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define(["laytpl","laypage","layer","form","util"],function(e){"use strict";var t=layui.$,i=layui.laytpl,a=layui.laypage,l=layui.layer,n=layui.form,o=(layui.util,layui.hint()),r=layui.device(),d={config:{checkName:"LAY_CHECKED",indexName:"LAY_TABLE_INDEX"},cache:{},index:layui.table?layui.table.index+1e4:0,set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,u,e,t)}},c=function(){var e=this,t=e.config,i=t.id||t.index;return i&&(c.that[i]=e,c.config[i]=t),{reload:function(t){e.reload.call(e,t)},setColsWidth:function(){e.setColsWidth.call(e)},resize:function(){e.resize.call(e)},config:t}},s=function(e){var t=c.config[e];return t||o.error("The ID option was not found in the table instance"),t||null},u="table",h=".layui-table",y="layui-hide",f="layui-none",p="layui-table-view",v=".layui-table-tool",m=".layui-table-box",g=".layui-table-init",b=".layui-table-header",x=".layui-table-body",k=".layui-table-main",C=".layui-table-fixed",w=".layui-table-fixed-l",T=".layui-table-fixed-r",A=".layui-table-total",L=".layui-table-page",S=".layui-table-sort",N="layui-table-edit",W="layui-table-hover",_=function(e){var t='{{#if(item2.colspan){}} colspan="{{item2.colspan}}"{{#} if(item2.rowspan){}} rowspan="{{item2.rowspan}}"{{#}}}';return e=e||{},['',"","{{# layui.each(d.data.cols, function(i1, item1){ }}","","{{# layui.each(item1, function(i2, item2){ }}",'{{# if(item2.fixed && item2.fixed !== "right"){ left = true; } }}','{{# if(item2.fixed === "right"){ right = true; } }}',function(){return e.fixed&&"right"!==e.fixed?'{{# if(item2.fixed && item2.fixed !== "right"){ }}':"right"===e.fixed?'{{# if(item2.fixed === "right"){ }}':""}(),"{{# var isSort = !(item2.colGroup) && item2.sort; }}",'",e.fixed?"{{# }; }}":"","{{# }); }}","","{{# }); }}","","
    ','
    ','{{# if(item2.type === "checkbox"){ }}','',"{{# } else { }}",'{{item2.title||""}}',"{{# if(isSort){ }}",'',"{{# } }}","{{# } }}","
    ","
    "].join("")},E=['',"","
    "].join(""),z=['
    ',"{{# if(d.data.toolbar){ }}",'
    ','
    ','
    ',"
    ","{{# } }}",'
    ',"{{# if(d.data.loading){ }}",'
    ','',"
    ","{{# } }}","{{# var left, right; }}",'
    ',_(),"
    ",'
    ',E,"
    ","{{# if(left){ }}",'
    ','
    ',_({fixed:!0}),"
    ",'
    ',E,"
    ","
    ","{{# }; }}","{{# if(right){ }}",'
    ','
    ',_({fixed:"right"}),'
    ',"
    ",'
    ',E,"
    ","
    ","{{# }; }}","
    ","{{# if(d.data.totalRow){ }}",'
    ','','',"
    ","
    ","{{# } }}","{{# if(d.data.page){ }}",'
    ','
    ',"
    ","{{# } }}","","
    "].join(""),H=t(window),R=t(document),F=function(e){var i=this;i.index=++d.index,i.config=t.extend({},i.config,d.config,e),i.render()};F.prototype.config={limit:10,loading:!0,cellMinWidth:60,defaultToolbar:["filter","exports","print"],autoSort:!0,text:{none:"无数据"}},F.prototype.render=function(){var e=this,a=e.config;if(a.elem=t(a.elem),a.where=a.where||{},a.id=a.id||a.elem.attr("id")||e.index,a.request=t.extend({pageName:"page",limitName:"limit"},a.request),a.response=t.extend({statusName:"code",statusCode:0,msgName:"msg",dataName:"data",countName:"count"},a.response),"object"==typeof a.page&&(a.limit=a.page.limit||a.limit,a.limits=a.page.limits||a.limits,e.page=a.page.curr=a.page.curr||1,delete a.page.elem,delete a.page.jump),!a.elem[0])return e;a.height&&/^full-\d+$/.test(a.height)&&(e.fullHeightGap=a.height.split("-")[1],a.height=H.height()-e.fullHeightGap),e.setInit();var l=a.elem,n=l.next("."+p),o=e.elem=t(i(z).render({VIEW_CLASS:p,data:a,index:e.index}));if(a.index=e.index,n[0]&&n.remove(),l.after(o),e.layTool=o.find(v),e.layBox=o.find(m),e.layHeader=o.find(b),e.layMain=o.find(k),e.layBody=o.find(x),e.layFixed=o.find(C),e.layFixLeft=o.find(w),e.layFixRight=o.find(T),e.layTotal=o.find(A),e.layPage=o.find(L),e.renderToolbar(),e.fullSize(),a.cols.length>1){var r=e.layFixed.find(b).find("th");r.height(e.layHeader.height()-1-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom")))}e.pullData(e.page),e.events()},F.prototype.initOpts=function(e){var t=this,i=(t.config,{checkbox:48,radio:48,space:15,numbers:40});e.checkbox&&(e.type="checkbox"),e.space&&(e.type="space"),e.type||(e.type="normal"),"normal"!==e.type&&(e.unresize=!0,e.width=e.width||i[e.type])},F.prototype.setInit=function(e){var t=this,i=t.config;return i.clientWidth=i.width||function(){var e=function(t){var a,l;t=t||i.elem.parent(),a=t.width();try{l="none"===t.css("display")}catch(n){}return!t[0]||a&&!l?a:e(t.parent())};return e()}(),"width"===e?i.clientWidth:void layui.each(i.cols,function(e,a){layui.each(a,function(l,n){if(!n)return void a.splice(l,1);if(n.key=e+"-"+l,n.hide=n.hide||!1,n.colGroup||n.colspan>1){var o=0;layui.each(i.cols[e+1],function(t,i){i.HAS_PARENT||o>1&&o==n.colspan||(i.HAS_PARENT=!0,i.parentKey=e+"-"+l,o+=parseInt(i.colspan>1?i.colspan:1))}),n.colGroup=!0}t.initOpts(n)})})},F.prototype.renderToolbar=function(){var e=this,a=e.config,l=['
    ','
    ','
    '].join(""),n=e.layTool.find(".layui-table-tool-temp");if("default"===a.toolbar)n.html(l);else if("string"==typeof a.toolbar){var o=t(a.toolbar).html()||"";o&&n.html(i(o).render(a))}var r={filter:{title:"筛选列",layEvent:"LAYTABLE_COLS",icon:"layui-icon-cols"},exports:{title:"导出",layEvent:"LAYTABLE_EXPORT",icon:"layui-icon-export"},print:{title:"打印",layEvent:"LAYTABLE_PRINT",icon:"layui-icon-print"}},d=[];"object"==typeof a.defaultToolbar&&layui.each(a.defaultToolbar,function(e,t){var i=r[t];i&&d.push('
    ')}),e.layTool.find(".layui-table-tool-self").html(d.join(""))},F.prototype.setParentCol=function(e,t){var i=this,a=i.config,l=i.layHeader.find('th[data-key="'+a.index+"-"+t+'"]'),n=parseInt(l.attr("colspan"))||0;if(l[0]){var o=t.split("-"),r=a.cols[o[0]][o[1]];e?n--:n++,l.attr("colspan",n),l[n<1?"addClass":"removeClass"](y),r.colspan=n,r.hide=n<1;var d=l.data("parentkey");d&&i.setParentCol(e,d)}},F.prototype.setColsPatch=function(){var e=this,t=e.config;layui.each(t.cols,function(t,i){layui.each(i,function(t,i){i.hide&&e.setParentCol(i.hide,i.parentKey)})})},F.prototype.setColsWidth=function(){var e=this,t=e.config,i=0,a=0,l=0,n=0,o=e.setInit("width");e.eachCols(function(e,t){t.hide||i++}),o=o-function(){return"line"===t.skin||"nob"===t.skin?2:i+1}()-e.getScrollWidth(e.layMain[0])-1;var r=function(e){layui.each(t.cols,function(i,r){layui.each(r,function(i,d){var c=0,s=d.minWidth||t.cellMinWidth;return d?void(d.colGroup||d.hide||(e?l&&ln&&a&&(l=(o-n)/a)};r(),r(!0),e.autoColNums=a,e.eachCols(function(i,a){var n=a.minWidth||t.cellMinWidth;a.colGroup||a.hide||(0===a.width?e.getCssRule(t.index+"-"+a.key,function(e){e.style.width=Math.floor(l>=n?l:n)+"px"}):/\d+%$/.test(a.width)&&e.getCssRule(t.index+"-"+a.key,function(e){e.style.width=Math.floor(parseFloat(a.width)/100*o)+"px"}))});var d=e.layMain.width()-e.getScrollWidth(e.layMain[0])-e.layMain.children("table").outerWidth();if(e.autoColNums&&d>=-i&&d<=i){var c=function(t){var i;return t=t||e.layHeader.eq(0).find("thead th:last-child"),i=t.data("field"),!i&&t.prev()[0]?c(t.prev()):t},s=c(),u=s.data("key");e.getCssRule(u,function(t){var i=t.style.width||s.outerWidth();t.style.width=parseFloat(i)+d+"px",e.layMain.height()-e.layMain.prop("clientHeight")>0&&(t.style.width=parseFloat(t.style.width)-1+"px")})}e.loading(!0)},F.prototype.resize=function(){var e=this;e.fullSize(),e.setColsWidth(),e.scrollPatch()},F.prototype.reload=function(e){var i=this;i.config.data&&i.config.data.constructor===Array&&delete i.config.data,i.config=t.extend({},i.config,e),i.render()},F.prototype.page=1,F.prototype.pullData=function(e){var i=this,a=i.config,l=a.request,n=a.response,o=function(){"object"==typeof a.initSort&&i.sort(a.initSort.field,a.initSort.type)};if(i.startTime=(new Date).getTime(),a.url){var r={};r[l.pageName]=e,r[l.limitName]=a.limit;var d=t.extend(r,a.where);a.contentType&&0==a.contentType.indexOf("application/json")&&(d=JSON.stringify(d)),t.ajax({type:a.method||"get",url:a.url,contentType:a.contentType,data:d,dataType:"json",headers:a.headers||{},success:function(t){"function"==typeof a.parseData&&(t=a.parseData(t)||t),t[n.statusName]!=n.statusCode?(i.renderForm(),i.layMain.html('
    '+(t[n.msgName]||"返回的数据不符合规范,正确的成功状态码 ("+n.statusName+") 应为:"+n.statusCode)+"
    ")):(i.renderData(t,e,t[n.countName]),o(),a.time=(new Date).getTime()-i.startTime+" ms"),i.setColsWidth(),"function"==typeof a.done&&a.done(t,e,t[n.countName])},error:function(e,t){i.layMain.html('
    数据接口请求异常:'+t+"
    "),i.renderForm(),i.setColsWidth()}})}else if(a.data&&a.data.constructor===Array){var c={},s=e*a.limit-a.limit;c[n.dataName]=a.data.concat().splice(s,a.limit),c[n.countName]=a.data.length,i.renderData(c,e,a.data.length),o(),i.setColsWidth(),"function"==typeof a.done&&a.done(c,e,c[n.countName])}},F.prototype.eachCols=function(e){var t=this;return d.eachCols(null,e,t.config.cols),t},F.prototype.renderData=function(e,n,o,r){var c=this,s=c.config,u=e[s.response.dataName]||[],h=[],p=[],v=[],m=function(){var e;return!r&&c.sortKey?c.sort(c.sortKey.field,c.sortKey.sort,!0):(layui.each(u,function(a,l){var o=[],u=[],f=[],m=a+s.limit*(n-1)+1;0!==l.length&&(r||(l[d.config.indexName]=a),c.eachCols(function(n,r){var c=r.field||n,h=s.index+"-"+r.key,p=l[c];if(void 0!==p&&null!==p||(p=""),!r.colGroup){var v=['','
    '+function(){var n=t.extend(!0,{LAY_INDEX:m},l),o=d.config.checkName;switch(r.type){case"checkbox":return'";case"radio":return n[o]&&(e=a),'';case"numbers":return m}return r.toolbar?i(t(r.toolbar).html()||"").render(n):r.templet?function(){return"function"==typeof r.templet?r.templet(n):i(t(r.templet).html()||String(p)).render(n)}():p}(),"
    "].join("");o.push(v),r.fixed&&"right"!==r.fixed&&u.push(v),"right"===r.fixed&&f.push(v)}}),h.push(''+o.join("")+""),p.push(''+u.join("")+""),v.push(''+f.join("")+""))}),c.layBody.scrollTop(0),c.layMain.find("."+f).remove(),c.layMain.find("tbody").html(h.join("")),c.layFixLeft.find("tbody").html(p.join("")),c.layFixRight.find("tbody").html(v.join("")),c.renderForm(),"number"==typeof e&&c.setThisRowChecked(e),c.syncCheckAll(),c.haveInit?c.scrollPatch():setTimeout(function(){c.scrollPatch()},50),c.haveInit=!0,l.close(c.tipsIndex),s.HAS_SET_COLS_PATCH||c.setColsPatch(),void(s.HAS_SET_COLS_PATCH=!0))};return c.key=s.id||s.index,d.cache[c.key]=u,c.layPage[0==o||0===u.length&&1==n?"addClass":"removeClass"](y),r?m():0===u.length?(c.renderForm(),c.layFixed.remove(),c.layMain.find("tbody").html(""),c.layMain.find("."+f).remove(),c.layMain.append('
    '+s.text.none+"
    ")):(m(),c.renderTotal(u),void(s.page&&(s.page=t.extend({elem:"layui-table-page"+s.index,count:o,limit:s.limit,limits:s.limits||[10,20,30,40,50,60,70,80,90],groups:3,layout:["prev","page","next","skip","count","limit"],prev:'',next:'',jump:function(e,t){t||(c.page=e.curr,s.limit=e.limit,c.loading(),c.pullData(e.curr))}},s.page),s.page.count=o,a.render(s.page))))},F.prototype.renderTotal=function(e){var t=this,i=t.config,a={};if(i.totalRow){layui.each(e,function(e,i){0!==i.length&&t.eachCols(function(e,t){var l=t.field||e,n=i[l];t.totalRow&&(a[l]=(a[l]||0)+(parseFloat(n)||0))})});var l=[];t.eachCols(function(e,t){var n=t.field||e,o=['','
    '+function(){var e=t.totalRowText||"";return t.totalRow?parseFloat(a[n]).toFixed(2)||e:e}(),"
    "].join("");l.push(o)}),t.layTotal.find("tbody").html(""+l.join("")+"")}},F.prototype.getColElem=function(e,t){var i=this,a=i.config;return e.eq(0).find(".laytable-cell-"+(a.index+"-"+t)+":eq(0)")},F.prototype.renderForm=function(e){n.render(e,"LAY-table-"+this.index)},F.prototype.setThisRowChecked=function(e){var t=this,i=(t.config,"layui-table-click"),a=t.layBody.find('tr[data-index="'+e+'"]');a.addClass(i).siblings("tr").removeClass(i)},F.prototype.sort=function(e,i,a,l){var n,r,c=this,s={},h=c.config,y=h.elem.attr("lay-filter"),f=d.cache[c.key];"string"==typeof e&&c.layHeader.find("th").each(function(i,a){var l=t(this),o=l.data("field");if(o===e)return e=l,n=o,!1});try{var n=n||e.data("field"),p=e.data("key");if(c.sortKey&&!a&&n===c.sortKey.field&&i===c.sortKey.sort)return;var v=c.layHeader.find("th .laytable-cell-"+p).find(S);c.layHeader.find("th").find(S).removeAttr("lay-sort"),v.attr("lay-sort",i||null),c.layFixed.find("th")}catch(m){return o.error("Table modules: Did not match to field")}c.sortKey={field:n,sort:i},h.autoSort&&("asc"===i?r=layui.sort(f,n):"desc"===i?r=layui.sort(f,n,!0):(r=layui.sort(f,d.config.indexName),delete c.sortKey)),s[h.response.dataName]=r||f,c.renderData(s,c.page,c.count,!0),l&&layui.event.call(e,u,"sort("+y+")",{field:n,type:i})},F.prototype.loading=function(e){var i=this,a=i.config;a.loading&&(e?(i.layInit&&i.layInit.remove(),delete i.layInit,i.layBox.find(g).remove()):(i.layInit=t(['
    ','',"
    "].join("")),i.layBox.append(i.layInit)))},F.prototype.setCheckData=function(e,t){var i=this,a=i.config,l=d.cache[i.key];l[e]&&l[e].constructor!==Array&&(l[e][a.checkName]=t)},F.prototype.syncCheckAll=function(){var e=this,t=e.config,i=e.layHeader.find('input[name="layTableCheckbox"]'),a=function(i){return e.eachCols(function(e,a){"checkbox"===a.type&&(a[t.checkName]=i)}),i};i[0]&&(d.checkStatus(e.key).isAll?(i[0].checked||(i.prop("checked",!0),e.renderForm("checkbox")),a(!0)):(i[0].checked&&(i.prop("checked",!1),e.renderForm("checkbox")),a(!1)))},F.prototype.getCssRule=function(e,t){var i=this,a=i.elem.find("style")[0],l=a.sheet||a.styleSheet||{},n=l.cssRules||l.rules;layui.each(n,function(i,a){if(a.selectorText===".laytable-cell-"+e)return t(a),!0})},F.prototype.fullSize=function(){var e,t=this,i=t.config,a=i.height;t.fullHeightGap&&(a=H.height()-t.fullHeightGap,a<135&&(a=135),t.elem.css("height",a)),a&&(e=parseFloat(a)-(t.layHeader.outerHeight()||38),i.toolbar&&(e-=t.layTool.outerHeight()||50),i.totalRow&&(e-=t.layTotal.outerHeight()||40),i.page&&(e=e-(t.layPage.outerHeight()||41)-2),t.layMain.css("height",e))},F.prototype.getScrollWidth=function(e){var t=0;return e?t=e.offsetWidth-e.clientWidth:(e=document.createElement("div"),e.style.width="100px",e.style.height="100px",e.style.overflowY="scroll",document.body.appendChild(e),t=e.offsetWidth-e.clientWidth,document.body.removeChild(e)),t},F.prototype.scrollPatch=function(){var e=this,i=e.layMain.children("table"),a=e.layMain.width()-e.layMain.prop("clientWidth"),l=e.layMain.height()-e.layMain.prop("clientHeight"),n=(e.getScrollWidth(e.layMain[0]),i.outerWidth()-e.layMain.width()),o=function(e){if(a&&l){if(e=e.eq(0),!e.find(".layui-table-patch")[0]){var i=t('
    ');i.find("div").css({width:a}),e.find("tr").append(i)}}else e.find(".layui-table-patch").remove()};o(e.layHeader),o(e.layTotal);var r=e.layMain.height(),d=r-l;e.layFixed.find(x).css("height",i.height()>=d?d:"auto"),e.layFixRight[n>0?"removeClass":"addClass"](y),e.layFixRight.css("right",a-1)},F.prototype.events=function(){var e,a=this,o=a.config,c=t("body"),s={},h=a.layHeader.find("th"),f=".layui-table-cell",p=o.elem.attr("lay-filter");a.layTool.on("click","*[lay-event]",function(e){var i=t(this),c=i.attr("lay-event"),s=function(e){var l=t(e.list),n=t('
      ');n.html(l),o.height&&n.css("max-height",o.height-(a.layTool.outerHeight()||50)),i.find(".layui-table-tool-panel")[0]||i.append(n),a.renderForm(),n.on("click",function(e){layui.stope(e)}),e.done&&e.done(n,l)};switch(layui.stope(e),R.trigger("table.tool.panel.remove"),l.close(a.tipsIndex),c){case"LAYTABLE_COLS":s({list:function(){var e=[];return a.eachCols(function(t,i){i.field&&"normal"==i.type&&e.push('
    • ')}),e.join("")}(),done:function(){n.on("checkbox(LAY_TABLE_TOOL_COLS)",function(e){var i=t(e.elem),l=this.checked,n=i.data("key"),r=i.data("parentkey");layui.each(o.cols,function(e,t){layui.each(t,function(t,i){if(e+"-"+t===n){var d=i.hide;i.hide=!l,a.elem.find('*[data-key="'+o.index+"-"+n+'"]')[l?"removeClass":"addClass"](y),d!=i.hide&&a.setParentCol(!l,r),a.resize()}})})})}});break;case"LAYTABLE_EXPORT":r.ie?l.tips("导出功能不支持 IE,请用 Chrome 等高级浏览器导出",this,{tips:3}):s({list:function(){return['
    • 导出到 Csv 文件
    • ','
    • 导出到 Excel 文件
    • '].join("")}(),done:function(e,i){i.on("click",function(){var e=t(this).data("type");d.exportFile(o.id,null,e)})}});break;case"LAYTABLE_PRINT":var h=window.open("打印窗口","_blank"),f=[""].join(""),v=t(a.layHeader.html());v.append(a.layMain.find("table").html()),v.find("th.layui-table-patch").remove(),v.find(".layui-table-col-special").remove(),h.document.write(f+v.prop("outerHTML")),h.document.close(),h.print(),h.close()}layui.event.call(this,u,"toolbar("+p+")",t.extend({event:c,config:o},{}))}),h.on("mousemove",function(e){var i=t(this),a=i.offset().left,l=e.clientX-a;i.data("unresize")||s.resizeStart||(s.allowResize=i.width()-l<=10,c.css("cursor",s.allowResize?"col-resize":""))}).on("mouseleave",function(){t(this);s.resizeStart||c.css("cursor","")}).on("mousedown",function(e){var i=t(this);if(s.allowResize){var l=i.data("key");e.preventDefault(),s.resizeStart=!0,s.offset=[e.clientX,e.clientY],a.getCssRule(l,function(e){var t=e.style.width||i.outerWidth();s.rule=e,s.ruleWidth=parseFloat(t),s.minWidth=i.data("minwidth")||o.cellMinWidth})}}),R.on("mousemove",function(t){if(s.resizeStart){if(t.preventDefault(),s.rule){var i=s.ruleWidth+t.clientX-s.offset[0];i');return n[0].value=i.data("content")||l.text(),i.find("."+N)[0]||i.append(n),n.focus(),void layui.stope(e)}}).on("mouseenter","td",function(){b.call(this)}).on("mouseleave","td",function(){b.call(this,"hide")});var g="layui-table-grid-down",b=function(e){var i=t(this),a=i.children(f);if(e)i.find(".layui-table-grid-down").remove();else if(a.prop("scrollWidth")>a.outerWidth()){if(a.find("."+g)[0])return;i.append('
      ')}};a.layBody.on("click","."+g,function(e){var i=t(this),n=i.parent(),d=n.children(f);a.tipsIndex=l.tips(['
      ',d.html(),"
      ",''].join(""),d[0],{tips:[3,""],time:-1,anim:-1,maxWidth:r.ios||r.android?300:a.elem.width()/2,isOutAnim:!1,skin:"layui-table-tips",success:function(e,t){e.find(".layui-table-tips-c").on("click",function(){l.close(t)})}}),layui.stope(e)}),a.layBody.on("click","*[lay-event]",function(){var e=t(this),i=e.parents("tr").eq(0).data("index");layui.event.call(this,u,"tool("+p+")",v.call(this,{event:e.attr("lay-event")})),a.setThisRowChecked(i)}),a.layMain.on("scroll",function(){var e=t(this),i=e.scrollLeft(),n=e.scrollTop();a.layHeader.scrollLeft(i),a.layTotal.scrollLeft(i),a.layFixed.find(x).scrollTop(n),l.close(a.tipsIndex)}),R.on("click",function(){R.trigger("table.remove.tool.panel")}),R.on("table.remove.tool.panel",function(){t(".layui-table-tool-panel").remove()}),H.on("resize",function(){a.resize()})},d.init=function(e,i){i=i||{};var a=this,l=t(e?'table[lay-filter="'+e+'"]':h+"[lay-data]"),n="Table element property lay-data configuration item has a syntax error: ";return l.each(function(){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){o.error(n+l)}var c=[],s=t.extend({elem:this,cols:[],data:[],skin:a.attr("lay-skin"),size:a.attr("lay-size"),even:"string"==typeof a.attr("lay-even")},d.config,i,l);e&&a.hide(),a.find("thead>tr").each(function(e){s.cols[e]=[],t(this).children().each(function(i){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){return o.error(n+l)}var d=t.extend({title:a.text(),colspan:a.attr("colspan")||0,rowspan:a.attr("rowspan")||0},l);d.colspan<2&&c.push(d),s.cols[e].push(d)})}),a.find("tbody>tr").each(function(e){var i=t(this),a={};i.children("td").each(function(e,i){var l=t(this),n=l.data("field");if(n)return a[n]=l.html()}),layui.each(c,function(e,t){var l=i.children("td").eq(e);a[t.field]=l.html()}),s.data[e]=a}),d.render(s)}),a},c.that={},c.config={},d.eachCols=function(e,i,a){var l=c.config[e]||{},n=[],o=0;a=t.extend(!0,[],a||l.cols),layui.each(a,function(e,t){layui.each(t,function(t,i){if(i.colGroup){var l=0;o++,i.CHILD_COLS=[],layui.each(a[e+1],function(e,t){t.PARENT_COL_INDEX||l>1&&l==i.colspan||(t.PARENT_COL_INDEX=o,i.CHILD_COLS.push(t),l+=parseInt(t.colspan>1?t.colspan:1))})}i.PARENT_COL_INDEX||n.push(i)})});var r=function(e){layui.each(e||n,function(e,t){return t.CHILD_COLS?r(t.CHILD_COLS):void("function"==typeof i&&i(e,t))})};r()},d.checkStatus=function(e){var t=0,i=0,a=[],l=d.cache[e]||[];return layui.each(l,function(e,l){return l.constructor===Array?void i++:void(l[d.config.checkName]&&(t++,a.push(d.clearCacheKey(l))))}),{data:a,isAll:!!l.length&&t===l.length-i}},d.exportFile=function(e,t,i){t=t||d.clearCacheKey(d.cache[e]),i=i||"csv";var a=c.config[e]||{},l={csv:"text/csv",xls:"application/vnd.ms-excel"}[i],n=document.createElement("a");return r.ie?o.error("IE_NOT_SUPPORT_EXPORTS"):(n.href="data:"+l+";charset=utf-8,\ufeff"+encodeURIComponent(function(){var i=[],a=[];return layui.each(t,function(t,l){var n=[];"object"==typeof e?(layui.each(e,function(e,a){0==t&&i.push(a||"")}),layui.each(d.clearCacheKey(l),function(e,t){n.push(t)})):d.eachCols(e,function(e,a){a.field&&"normal"==a.type&&!a.hide&&(0==t&&i.push(a.title||""),n.push(l[a.field]))}),a.push(n.join(","))}),i.join(",")+"\r\n"+a.join("\r\n")}()),n.download=(a.title||"table_"+(a.index||""))+"."+i,document.body.appendChild(n),n.click(),void document.body.removeChild(n))},d.resize=function(e){if(e){var t=s(e);if(!t)return;c.that[e].resize()}else layui.each(c.that,function(){this.resize()})},d.reload=function(e,i){i=i||{};var a=s(e);if(a)return i.data&&i.data.constructor===Array&&delete a.data,d.render(t.extend(!0,{},a,i))},d.render=function(e){var t=new F(e);return c.call(t)},d.clearCacheKey=function(e){return e=t.extend({},e),delete e[d.config.checkName],delete e[d.config.indexName],e},d.init(),e(u,d)}); \ No newline at end of file diff --git a/static/layui/lay/modules/tree.js b/static/layui/lay/modules/tree.js new file mode 100644 index 0000000..e8c053d --- /dev/null +++ b/static/layui/lay/modules/tree.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var o=layui.$,a=layui.hint(),i="layui-tree-enter",r=function(e){this.options=e},t={arrow:["",""],checkbox:["",""],radio:["",""],branch:["",""],leaf:""};r.prototype.init=function(e){var o=this;e.addClass("layui-box layui-tree"),o.options.skin&&e.addClass("layui-tree-skin-"+o.options.skin),o.tree(e),o.on(e)},r.prototype.tree=function(e,a){var i=this,r=i.options,n=a||r.nodes;layui.each(n,function(a,n){var l=n.children&&n.children.length>0,c=o('
        '),s=o(["
      • ",function(){return l?''+(n.spread?t.arrow[1]:t.arrow[0])+"":""}(),function(){return r.check?''+("checkbox"===r.check?t.checkbox[0]:"radio"===r.check?t.radio[0]:"")+"":""}(),function(){return'"+(''+(l?n.spread?t.branch[1]:t.branch[0]:t.leaf)+"")+(""+(n.name||"未命名")+"")}(),"
      • "].join(""));l&&(s.append(c),i.tree(c,n.children)),e.append(s),"function"==typeof r.click&&i.click(s,n),i.spread(s,n),r.drag&&i.drag(s,n)})},r.prototype.click=function(e,o){var a=this,i=a.options;e.children("a").on("click",function(e){layui.stope(e),i.click(o)})},r.prototype.spread=function(e,o){var a=this,i=(a.options,e.children(".layui-tree-spread")),r=e.children("ul"),n=e.children("a"),l=function(){e.data("spread")?(e.data("spread",null),r.removeClass("layui-show"),i.html(t.arrow[0]),n.find(".layui-icon").html(t.branch[0])):(e.data("spread",!0),r.addClass("layui-show"),i.html(t.arrow[1]),n.find(".layui-icon").html(t.branch[1]))};r[0]&&(i.on("click",l),n.on("dblclick",l))},r.prototype.on=function(e){var a=this,r=a.options,t="layui-tree-drag";e.find("i").on("selectstart",function(e){return!1}),r.drag&&o(document).on("mousemove",function(e){var i=a.move;if(i.from){var r=(i.to,o('
        '));e.preventDefault(),o("."+t)[0]||o("body").append(r);var n=o("."+t)[0]?o("."+t):r;n.addClass("layui-show").html(i.from.elem.children("a").html()),n.css({left:e.pageX+10,top:e.pageY+10})}}).on("mouseup",function(){var e=a.move;e.from&&(e.from.elem.children("a").removeClass(i),e.to&&e.to.elem.children("a").removeClass(i),a.move={},o("."+t).remove())})},r.prototype.move={},r.prototype.drag=function(e,a){var r=this,t=(r.options,e.children("a")),n=function(){var t=o(this),n=r.move;n.from&&(n.to={item:a,elem:e},t.addClass(i))};t.on("mousedown",function(){var o=r.move;o.from={item:a,elem:e}}),t.on("mouseenter",n).on("mousemove",n).on("mouseleave",function(){var e=o(this),a=r.move;a.from&&(delete a.to,e.removeClass(i))})},e("tree",function(e){var i=new r(e=e||{}),t=o(e.elem);return t[0]?void i.init(t):a.error("layui.tree 没有找到"+e.elem+"元素")})}); \ No newline at end of file diff --git a/static/layui/lay/modules/upload.js b/static/layui/lay/modules/upload.js new file mode 100644 index 0000000..404d534 --- /dev/null +++ b/static/layui/lay/modules/upload.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define("layer",function(e){"use strict";var i=layui.$,t=layui.layer,n=layui.hint(),a=layui.device(),o={config:{},set:function(e){var t=this;return t.config=i.extend({},t.config,e),t},on:function(e,i){return layui.onevent.call(this,r,e,i)}},l=function(){var e=this;return{upload:function(i){e.upload.call(e,i)},config:e.config}},r="upload",u="layui-upload-file",c="layui-upload-form",f="layui-upload-iframe",s="layui-upload-choose",p=function(e){var t=this;t.config=i.extend({},t.config,o.config,e),t.render()};p.prototype.config={accept:"images",exts:"",auto:!0,bindAction:"",url:"",field:"file",method:"post",data:{},drag:!0,size:0,number:0,multiple:!1},p.prototype.render=function(e){var t=this,e=t.config;e.elem=i(e.elem),e.bindAction=i(e.bindAction),t.file(),t.events()},p.prototype.file=function(){var e=this,t=e.config,n=e.elemFile=i(['"].join("")),o=t.elem.next();(o.hasClass(u)||o.hasClass(c))&&o.remove(),a.ie&&a.ie<10&&t.elem.wrap('
        '),e.isFile()?(e.elemFile=t.elem,t.field=t.elem[0].name):t.elem.after(n),a.ie&&a.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,t=e.config,n=i(''),a=i(['
        ',"
        "].join(""));i("#"+f)[0]||i("body").append(n),t.elem.next().hasClass(c)||(e.elemFile.wrap(a),t.elem.next("."+c).append(function(){var e=[];return layui.each(t.data,function(i,t){t="function"==typeof t?t():t,e.push('')}),e.join("")}()))},p.prototype.msg=function(e){return t.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var i=this;window.FileReader&&layui.each(i.chooseFiles,function(i,t){var n=new FileReader;n.readAsDataURL(t),n.onload=function(){e&&e(i,t,this.result)}})},p.prototype.upload=function(e,t){var n,o=this,l=o.config,r=o.elemFile[0],u=function(){var t=0,n=0,a=e||o.files||o.chooseFiles||r.files,u=function(){l.multiple&&t+n===o.fileLength&&"function"==typeof l.allDone&&l.allDone({total:o.fileLength,successful:t,aborted:n})};layui.each(a,function(e,a){var r=new FormData;r.append(l.field,a),layui.each(l.data,function(e,i){i="function"==typeof i?i():i,r.append(e,i)}),i.ajax({url:l.url,type:"post",data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(i){t++,d(e,i),u()},error:function(){n++,o.msg("请求上传接口出现异常"),m(e),u()}})})},c=function(){var e=i("#"+f);o.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var i,t=e.contents().find("body");try{i=t.text()}catch(n){o.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}i&&(clearInterval(p.timer),t.html(""),d(0,i))},30)},d=function(e,i){if(o.elemFile.next("."+s).remove(),r.value="","object"!=typeof i)try{i=JSON.parse(i)}catch(t){return i={},o.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(i,e||0,function(e){o.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){o.upload(e)})},h=l.exts,v=function(){var i=[];return layui.each(e||o.chooseFiles,function(e,t){i.push(t.name)}),i}(),g={preview:function(e){o.preview(e)},upload:function(e,i){var t={};t[e]=i,o.upload(t)},pushFile:function(){return o.files=o.files||{},layui.each(o.chooseFiles,function(e,i){o.files[e]=i}),o.files},resetFile:function(e,i,t){var n=new File([i],t);o.files=o.files||{},o.files[e]=n}},y=function(){if("choose"!==t&&!l.auto||(l.choose&&l.choose(g),"choose"!==t))return l.before&&l.before(g),a.ie?a.ie>9?u():c():void u()};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return o.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return o.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return o.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,i){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(i))||(n=!0)}),n)return o.msg("选择的图片中包含不支持的格式"),r.value=""}if(o.fileLength=function(){var i=0,t=e||o.files||o.chooseFiles||r.files;return layui.each(t,function(){i++}),i}(),l.number&&o.fileLength>l.number)return o.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(a.ie&&a.ie<10)){var F;if(layui.each(o.chooseFiles,function(e,i){if(i.size>1024*l.size){var t=l.size/1024;t=t>=1?t.toFixed(2)+"MB":l.size+"KB",r.value="",F=t}}),F)return o.msg("文件不能超过"+F)}y()}},p.prototype.events=function(){var e=this,t=e.config,o=function(i){e.chooseFiles={},layui.each(i,function(i,t){var n=(new Date).getTime();e.chooseFiles[n+"-"+i]=t})},l=function(i,n){var a=e.elemFile,o=i.length>1?i.length+"个文件":(i[0]||{}).name||a[0].value.match(/[^\/\\]+\..+/g)||[]||"";a.next().hasClass(s)&&a.next().remove(),e.upload(null,"choose"),e.isFile()||t.choose||a.after(''+o+"")};t.elem.off("upload.start").on("upload.start",function(){var a=i(this),o=a.attr("lay-data");if(o)try{o=new Function("return "+o)(),e.config=i.extend({},t,o)}catch(l){n.error("Upload element property lay-data configuration item has a syntax error: "+o)}e.config.item=a,e.elemFile[0].click()}),a.ie&&a.ie<10||t.elem.off("upload.over").on("upload.over",function(){var e=i(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=i(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,a){var r=i(this),u=a.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),o(u),t.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var i=this.files||[];o(i),t.auto?e.upload():l(i)}),t.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),t.elem.data("haveEvents")||(e.elemFile.on("change",function(){i(this).trigger("upload.change")}),t.elem.on("click",function(){e.isFile()||i(this).trigger("upload.start")}),t.drag&&t.elem.on("dragover",function(e){e.preventDefault(),i(this).trigger("upload.over")}).on("dragleave",function(e){i(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),i(this).trigger("upload.drop",e)}),t.bindAction.on("click",function(){i(this).trigger("upload.action")}),t.elem.data("haveEvents",!0))},o.render=function(e){var i=new p(e);return l.call(i)},e(r,o)}); \ No newline at end of file diff --git a/static/layui/lay/modules/util.js b/static/layui/lay/modules/util.js new file mode 100644 index 0000000..8d38508 --- /dev/null +++ b/static/layui/lay/modules/util.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(t){"use strict";var e=layui.$,i={fixbar:function(t){var i,a,n="layui-fixbar",r="layui-fixbar-top",o=e(document),l=e("body");t=e.extend({showHeight:200},t),t.bar1=t.bar1===!0?"":t.bar1,t.bar2=t.bar2===!0?"":t.bar2,t.bgcolor=t.bgcolor?"background-color:"+t.bgcolor:"";var c=[t.bar1,t.bar2,""],g=e(['
          ',t.bar1?'
        • '+c[0]+"
        • ":"",t.bar2?'
        • '+c[1]+"
        • ":"",'
        • '+c[2]+"
        • ","
        "].join("")),s=g.find("."+r),u=function(){var e=o.scrollTop();e>=t.showHeight?i||(s.show(),i=1):i&&(s.hide(),i=0)};e("."+n)[0]||("object"==typeof t.css&&g.css(t.css),l.append(g),u(),g.find("li").on("click",function(){var i=e(this),a=i.attr("lay-type");"top"===a&&e("html,body").animate({scrollTop:0},200),t.click&&t.click.call(this,a)}),o.on("scroll",function(){clearTimeout(a),a=setTimeout(function(){u()},100)}))},countdown:function(t,e,i){var a=this,n="function"==typeof e,r=new Date(t).getTime(),o=new Date(!e||n?(new Date).getTime():e).getTime(),l=r-o,c=[Math.floor(l/864e5),Math.floor(l/36e5)%24,Math.floor(l/6e4)%60,Math.floor(l/1e3)%60];n&&(i=e);var g=setTimeout(function(){a.countdown(t,o+1e3,i)},1e3);return i&&i(l>0?c:[0,0,0,0],e,g),l<=0&&clearTimeout(g),g},timeAgo:function(t,e){var i=this,a=[[],[]],n=(new Date).getTime()-new Date(t).getTime();return n>6912e5?(n=new Date(t),a[0][0]=i.digit(n.getFullYear(),4),a[0][1]=i.digit(n.getMonth()+1),a[0][2]=i.digit(n.getDate()),e||(a[1][0]=i.digit(n.getHours()),a[1][1]=i.digit(n.getMinutes()),a[1][2]=i.digit(n.getSeconds())),a[0].join("-")+" "+a[1].join(":")):n>=864e5?(n/1e3/60/60/24|0)+"天前":n>=36e5?(n/1e3/60/60|0)+"小时前":n>=12e4?(n/1e3/60|0)+"分钟前":n<0?"未来":"刚刚"},digit:function(t,e){var i="";t=String(t),e=e||2;for(var a=t.length;a/g,">").replace(/'/g,"'").replace(/"/g,""")}};!function(t,e,i){"$:nomunge";function a(){n=e[l](function(){r.each(function(){var e=t(this),i=e.width(),a=e.height(),n=t.data(this,g);(i!==n.w||a!==n.h)&&e.trigger(c,[n.w=i,n.h=a])}),a()},o[s])}var n,r=t([]),o=t.resize=t.extend(t.resize,{}),l="setTimeout",c="resize",g=c+"-special-event",s="delay",u="throttleWindow";o[s]=250,o[u]=!0,t.event.special[c]={setup:function(){if(!o[u]&&this[l])return!1;var e=t(this);r=r.add(e),t.data(this,g,{w:e.width(),h:e.height()}),1===r.length&&a()},teardown:function(){if(!o[u]&&this[l])return!1;var e=t(this);r=r.not(e),e.removeData(g),r.length||clearTimeout(n)},add:function(e){function a(e,a,r){var o=t(this),l=t.data(this,g)||{};l.w=a!==i?a:o.width(),l.h=r!==i?r:o.height(),n.apply(this,arguments)}if(!o[u]&&this[l])return!1;var n;return t.isFunction(e)?(n=e,a):(n=e.handler,void(e.handler=a))}}}(e,window),t("util",i)}); \ No newline at end of file diff --git a/static/layui/layui.all.js b/static/layui/layui.all.js new file mode 100644 index 0000000..5cd5973 --- /dev/null +++ b/static/layui/layui.all.js @@ -0,0 +1,5 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;!function(e){"use strict";var t=document,o={modules:{},status:{},timeout:10,event:{}},n=function(){this.v="2.4.5"},r=function(){var e=t.currentScript?t.currentScript.src:function(){for(var e,o=t.scripts,n=o.length-1,r=n;r>0;r--)if("interactive"===o[r].readyState){e=o[r].src;break}return e||o[n].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),i=function(t){e.console&&console.error&&console.error("Layui hint: "+t)},a="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),u={layer:"modules/layer",laydate:"modules/laydate",laypage:"modules/laypage",laytpl:"modules/laytpl",layim:"modules/layim",layedit:"modules/layedit",form:"modules/form",upload:"modules/upload",tree:"modules/tree",table:"modules/table",element:"modules/element",rate:"modules/rate",colorpicker:"modules/colorpicker",slider:"modules/slider",carousel:"modules/carousel",flow:"modules/flow",util:"modules/util",code:"modules/code",jquery:"modules/jquery",mobile:"modules/mobile","layui.all":"../layui.all"};n.prototype.cache=o,n.prototype.define=function(e,t){var n=this,r="function"==typeof e,i=function(){var e=function(e,t){layui[e]=t,o.status[e]=!0};return"function"==typeof t&&t(function(n,r){e(n,r),o.callback[n]=function(){t(e)}}),this};return r&&(t=e,e=[]),layui["layui.all"]||!layui["layui.all"]&&layui["layui.mobile"]?i.call(n):(n.use(e,i),n)},n.prototype.use=function(e,n,l){function s(e,t){var n="PLaySTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/;("load"===e.type||n.test((e.currentTarget||e.srcElement).readyState))&&(o.modules[f]=t,d.removeChild(v),function r(){return++m>1e3*o.timeout/4?i(f+" is not a valid module"):void(o.status[f]?c():setTimeout(r,4))}())}function c(){l.push(layui[f]),e.length>1?y.use(e.slice(1),n,l):"function"==typeof n&&n.apply(layui,l)}var y=this,p=o.dir=o.dir?o.dir:r,d=t.getElementsByTagName("head")[0];e="string"==typeof e?[e]:e,window.jQuery&&jQuery.fn.on&&(y.each(e,function(t,o){"jquery"===o&&e.splice(t,1)}),layui.jquery=layui.$=jQuery);var f=e[0],m=0;if(l=l||[],o.host=o.host||(p.match(/\/\/([\s\S]+?)\//)||["//"+location.host+"/"])[0],0===e.length||layui["layui.all"]&&u[f]||!layui["layui.all"]&&layui["layui.mobile"]&&u[f])return c(),y;if(o.modules[f])!function g(){return++m>1e3*o.timeout/4?i(f+" is not a valid module"):void("string"==typeof o.modules[f]&&o.status[f]?c():setTimeout(g,4))}();else{var v=t.createElement("script"),h=(u[f]?p+"lay/":/^\{\/\}/.test(y.modules[f])?"":o.base||"")+(y.modules[f]||f)+".js";h=h.replace(/^\{\/\}/,""),v.async=!0,v.charset="utf-8",v.src=h+function(){var e=o.version===!0?o.v||(new Date).getTime():o.version||"";return e?"?v="+e:""}(),d.appendChild(v),!v.attachEvent||v.attachEvent.toString&&v.attachEvent.toString().indexOf("[native code")<0||a?v.addEventListener("load",function(e){s(e,h)},!1):v.attachEvent("onreadystatechange",function(e){s(e,h)}),o.modules[f]=h}return y},n.prototype.getStyle=function(t,o){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](o)},n.prototype.link=function(e,n,r){var a=this,u=t.createElement("link"),l=t.getElementsByTagName("head")[0];"string"==typeof n&&(r=n);var s=(r||e).replace(/\.|\//g,""),c=u.id="layuicss-"+s,y=0;return u.rel="stylesheet",u.href=e+(o.debug?"?v="+(new Date).getTime():""),u.media="all",t.getElementById(c)||l.appendChild(u),"function"!=typeof n?a:(function p(){return++y>1e3*o.timeout/100?i(e+" timeout"):void(1989===parseInt(a.getStyle(t.getElementById(c),"width"))?function(){n()}():setTimeout(p,100))}(),a)},o.callback={},n.prototype.factory=function(e){if(layui[e])return"function"==typeof o.callback[e]?o.callback[e]:null},n.prototype.addcss=function(e,t,n){return layui.link(o.dir+"css/"+e,t,n)},n.prototype.img=function(e,t,o){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,"function"==typeof t&&t(n)},void(n.onerror=function(e){n.onerror=null,"function"==typeof o&&o(e)}))},n.prototype.config=function(e){e=e||{};for(var t in e)o[t]=e[t];return this},n.prototype.modules=function(){var e={};for(var t in u)e[t]=u[t];return e}(),n.prototype.extend=function(e){var t=this;e=e||{};for(var o in e)t[o]||t.modules[o]?i("模块名 "+o+" 已被占用"):t.modules[o]=e[o];return t},n.prototype.router=function(e){var t=this,e=e||location.hash,o={path:[],search:{},hash:(e.match(/[^#](#.*$)/)||[])[1]||""};return/^#\//.test(e)?(e=e.replace(/^#\//,""),o.href="/"+e,e=e.replace(/([^#])(#.*$)/,"$1").split("/")||[],t.each(e,function(e,t){/^\w+=/.test(t)?function(){t=t.split("="),o.search[t[0]]=t[1]}():o.path.push(t)}),o):o},n.prototype.data=function(t,o,n){if(t=t||"layui",n=n||localStorage,e.JSON&&e.JSON.parse){if(null===o)return delete n[t];o="object"==typeof o?o:{key:o};try{var r=JSON.parse(n[t])}catch(i){var r={}}return"value"in o&&(r[o.key]=o.value),o.remove&&delete r[o.key],n[t]=JSON.stringify(r),o.key?r[o.key]:r}},n.prototype.sessionData=function(e,t){return this.data(e,t,sessionStorage)},n.prototype.device=function(t){var o=navigator.userAgent.toLowerCase(),n=function(e){var t=new RegExp(e+"/([^\\s\\_\\-]+)");return e=(o.match(t)||[])[1],e||!1},r={os:function(){return/windows/.test(o)?"windows":/linux/.test(o)?"linux":/iphone|ipod|ipad|ios/.test(o)?"ios":/mac/.test(o)?"mac":void 0}(),ie:function(){return!!(e.ActiveXObject||"ActiveXObject"in e)&&((o.match(/msie\s(\d+)/)||[])[1]||"11")}(),weixin:n("micromessenger")};return t&&!r[t]&&(r[t]=n(t)),r.android=/android/.test(o),r.ios="ios"===r.os,r},n.prototype.hint=function(){return{error:i}},n.prototype.each=function(e,t){var o,n=this;if("function"!=typeof t)return n;if(e=e||[],e.constructor===Object){for(o in e)if(t.call(e[o],o,e[o]))break}else for(o=0;oi?1:r/g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)});layui.define(function(e){"use strict";var a=document,t="getElementById",n="getElementsByTagName",i="laypage",r="layui-disabled",u=function(e){var a=this;a.config=e||{},a.config.index=++s.index,a.render(!0)};u.prototype.type=function(){var e=this.config;if("object"==typeof e.elem)return void 0===e.elem.length?2:3},u.prototype.view=function(){var e=this,a=e.config,t=a.groups="groups"in a?0|a.groups:5;a.layout="object"==typeof a.layout?a.layout:["prev","page","next"],a.count=0|a.count,a.curr=0|a.curr||1,a.limits="object"==typeof a.limits?a.limits:[10,20,30,40,50],a.limit=0|a.limit||10,a.pages=Math.ceil(a.count/a.limit)||1,a.curr>a.pages&&(a.curr=a.pages),t<0?t=1:t>a.pages&&(t=a.pages),a.prev="prev"in a?a.prev:"上一页",a.next="next"in a?a.next:"下一页";var n=a.pages>t?Math.ceil((a.curr+(t>1?1:0))/(t>0?t:1)):1,i={prev:function(){return a.prev?''+a.prev+"":""}(),page:function(){var e=[];if(a.count<1)return"";n>1&&a.first!==!1&&0!==t&&e.push(''+(a.first||1)+"");var i=Math.floor((t-1)/2),r=n>1?a.curr-i:1,u=n>1?function(){var e=a.curr+(t-i-1);return e>a.pages?a.pages:e}():t;for(u-r2&&e.push('');r<=u;r++)r===a.curr?e.push('"+r+""):e.push(''+r+"");return a.pages>t&&a.pages>u&&a.last!==!1&&(u+1…'),0!==t&&e.push(''+(a.last||a.pages)+"")),e.join("")}(),next:function(){return a.next?''+a.next+"":""}(),count:'共 '+a.count+" 条",limit:function(){var e=['"}(),refresh:['','',""].join(""),skip:function(){return['到第','','页',""].join("")}()};return['
        ',function(){var e=[];return layui.each(a.layout,function(a,t){i[t]&&e.push(i[t])}),e.join("")}(),"
        "].join("")},u.prototype.jump=function(e,a){if(e){var t=this,i=t.config,r=e.children,u=e[n]("button")[0],l=e[n]("input")[0],p=e[n]("select")[0],c=function(){var e=0|l.value.replace(/\s|\D/g,"");e&&(i.curr=e,t.render())};if(a)return c();for(var o=0,y=r.length;oi.pages||(i.curr=e,t.render())});p&&s.on(p,"change",function(){var e=this.value;i.curr*e>i.count&&(i.curr=Math.ceil(i.count/e)),i.limit=e,t.render()}),u&&s.on(u,"click",function(){c()})}},u.prototype.skip=function(e){if(e){var a=this,t=e[n]("input")[0];t&&s.on(t,"keyup",function(t){var n=this.value,i=t.keyCode;/^(37|38|39|40)$/.test(i)||(/\D/.test(n)&&(this.value=n.replace(/\D/,"")),13===i&&a.jump(e,!0))})}},u.prototype.render=function(e){var n=this,i=n.config,r=n.type(),u=n.view();2===r?i.elem&&(i.elem.innerHTML=u):3===r?i.elem.html(u):a[t](i.elem)&&(a[t](i.elem).innerHTML=u),i.jump&&i.jump(i,e);var s=a[t]("layui-laypage-"+i.index);n.jump(s),i.hash&&!e&&(location.hash="!"+i.hash+"="+i.curr),n.skip(s)};var s={render:function(e){var a=new u(e);return a.index},index:layui.laypage?layui.laypage.index+1e4:0,on:function(e,a,t){return e.attachEvent?e.attachEvent("on"+a,function(a){a.target=a.srcElement,t.call(e,a)}):e.addEventListener(a,t,!1),this}};e(i,s)});!function(){"use strict";var e=window.layui&&layui.define,t={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,n=t.length-1,a=n;a>0;a--)if("interactive"===t[a].readyState){e=t[a].src;break}return e||t[n].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),getStyle:function(e,t){var n=e.currentStyle?e.currentStyle:window.getComputedStyle(e,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](t)},link:function(e,a,i){if(n.path){var r=document.getElementsByTagName("head")[0],o=document.createElement("link");"string"==typeof a&&(i=a);var s=(i||e).replace(/\.|\//g,""),l="layuicss-"+s,d=0;o.rel="stylesheet",o.href=n.path+e,o.id=l,document.getElementById(l)||r.appendChild(o),"function"==typeof a&&!function c(){return++d>80?window.console&&console.error("laydate.css: Invalid"):void(1989===parseInt(t.getStyle(document.getElementById(l),"width"))?a():setTimeout(c,100))}()}}},n={v:"5.0.9",config:{},index:window.laydate&&window.laydate.v?1e5:0,path:t.getPath,set:function(e){var t=this;return t.config=w.extend({},t.config,e),t},ready:function(a){var i="laydate",r="",o=(e?"modules/laydate/":"theme/")+"default/laydate.css?v="+n.v+r;return e?layui.addcss(o,a,i):t.link(o,a,i),this}},a=function(){var e=this;return{hint:function(t){e.hint.call(e,t)},config:e.config}},i="laydate",r=".layui-laydate",o="layui-this",s="laydate-disabled",l="开始日期超出了结束日期
        建议重新选择",d=[100,2e5],c="layui-laydate-static",m="layui-laydate-list",u="laydate-selected",h="layui-laydate-hint",y="laydate-day-prev",f="laydate-day-next",p="layui-laydate-footer",g=".laydate-btns-confirm",v="laydate-time-text",D=".laydate-btns-time",T=function(e){var t=this;t.index=++n.index,t.config=w.extend({},t.config,n.config,e),n.ready(function(){t.init()})},w=function(e){return new C(e)},C=function(e){for(var t=0,n="object"==typeof e?[e]:(this.selector=e,document.querySelectorAll(e||null));t0)return n[0].getAttribute(e)}():n.each(function(n,a){a.setAttribute(e,t)})},C.prototype.removeAttr=function(e){return this.each(function(t,n){n.removeAttribute(e)})},C.prototype.html=function(e){return this.each(function(t,n){n.innerHTML=e})},C.prototype.val=function(e){return this.each(function(t,n){n.value=e})},C.prototype.append=function(e){return this.each(function(t,n){"object"==typeof e?n.appendChild(e):n.innerHTML=n.innerHTML+e})},C.prototype.remove=function(e){return this.each(function(t,n){e?n.removeChild(e):n.parentNode.removeChild(n)})},C.prototype.on=function(e,t){return this.each(function(n,a){a.attachEvent?a.attachEvent("on"+e,function(e){e.target=e.srcElement,t.call(a,e)}):a.addEventListener(e,t,!1)})},C.prototype.off=function(e,t){return this.each(function(n,a){a.detachEvent?a.detachEvent("on"+e,t):a.removeEventListener(e,t,!1)})},T.isLeapYear=function(e){return e%4===0&&e%100!==0||e%400===0},T.prototype.config={type:"date",range:!1,format:"yyyy-MM-dd",value:null,isInitValue:!0,min:"1900-1-1",max:"2099-12-31",trigger:"focus",show:!1,showBottom:!0,btns:["clear","now","confirm"],lang:"cn",theme:"default",position:null,calendar:!1,mark:{},zIndex:null,done:null,change:null},T.prototype.lang=function(){var e=this,t=e.config,n={cn:{weeks:["日","一","二","三","四","五","六"],time:["时","分","秒"],timeTips:"选择时间",startTime:"开始时间",endTime:"结束时间",dateTips:"返回日期",month:["一","二","三","四","五","六","七","八","九","十","十一","十二"],tools:{confirm:"确定",clear:"清空",now:"现在"}},en:{weeks:["Su","Mo","Tu","We","Th","Fr","Sa"],time:["Hours","Minutes","Seconds"],timeTips:"Select Time",startTime:"Start Time",endTime:"End Time",dateTips:"Select Date",month:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],tools:{confirm:"Confirm",clear:"Clear",now:"Now"}}};return n[t.lang]||n.cn},T.prototype.init=function(){var e=this,t=e.config,n="yyyy|y|MM|M|dd|d|HH|H|mm|m|ss|s",a="static"===t.position,i={year:"yyyy",month:"yyyy-MM",date:"yyyy-MM-dd",time:"HH:mm:ss",datetime:"yyyy-MM-dd HH:mm:ss"};t.elem=w(t.elem),t.eventElem=w(t.eventElem),t.elem[0]&&(t.range===!0&&(t.range="-"),t.format===i.date&&(t.format=i[t.type]),e.format=t.format.match(new RegExp(n+"|.","g"))||[],e.EXP_IF="",e.EXP_SPLIT="",w.each(e.format,function(t,a){var i=new RegExp(n).test(a)?"\\d{"+function(){return new RegExp(n).test(e.format[0===t?t+1:t-1]||"")?/^yyyy|y$/.test(a)?4:a.length:/^yyyy$/.test(a)?"1,4":/^y$/.test(a)?"1,308":"1,2"}()+"}":"\\"+a;e.EXP_IF=e.EXP_IF+i,e.EXP_SPLIT=e.EXP_SPLIT+"("+i+")"}),e.EXP_IF=new RegExp("^"+(t.range?e.EXP_IF+"\\s\\"+t.range+"\\s"+e.EXP_IF:e.EXP_IF)+"$"),e.EXP_SPLIT=new RegExp("^"+e.EXP_SPLIT+"$",""),e.isInput(t.elem[0])||"focus"===t.trigger&&(t.trigger="click"),t.elem.attr("lay-key")||(t.elem.attr("lay-key",e.index),t.eventElem.attr("lay-key",e.index)),t.mark=w.extend({},t.calendar&&"cn"===t.lang?{"0-1-1":"元旦","0-2-14":"情人","0-3-8":"妇女","0-3-12":"植树","0-4-1":"愚人","0-5-1":"劳动","0-5-4":"青年","0-6-1":"儿童","0-9-10":"教师","0-9-18":"国耻","0-10-1":"国庆","0-12-25":"圣诞"}:{},t.mark),w.each(["min","max"],function(e,n){var a=[],i=[];if("number"==typeof t[n]){var r=t[n],o=(new Date).getTime(),s=864e5,l=new Date(r?r0)return!0;var a=w.elem("div",{"class":"layui-laydate-header"}),i=[function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-y"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-m"});return e.innerHTML="",e}(),function(){var e=w.elem("div",{"class":"laydate-set-ym"}),t=w.elem("span"),n=w.elem("span");return e.appendChild(t),e.appendChild(n),e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-m"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-y"});return e.innerHTML="",e}()],d=w.elem("div",{"class":"layui-laydate-content"}),c=w.elem("table"),m=w.elem("thead"),u=w.elem("tr");w.each(i,function(e,t){a.appendChild(t)}),m.appendChild(u),w.each(new Array(6),function(e){var t=c.insertRow(0);w.each(new Array(7),function(a){if(0===e){var i=w.elem("th");i.innerHTML=n.weeks[a],u.appendChild(i)}t.insertCell(a)})}),c.insertBefore(m,c.children[0]),d.appendChild(c),r[e]=w.elem("div",{"class":"layui-laydate-main laydate-main-list-"+e}),r[e].appendChild(a),r[e].appendChild(d),o.push(i),s.push(d),l.push(c)}),w(d).html(function(){var e=[],i=[];return"datetime"===t.type&&e.push(''+n.timeTips+""),w.each(t.btns,function(e,r){var o=n.tools[r]||"btn";t.range&&"now"===r||(a&&"clear"===r&&(o="cn"===t.lang?"重置":"Reset"),i.push(''+o+""))}),e.push('"),e.join("")}()),w.each(r,function(e,t){i.appendChild(t)}),t.showBottom&&i.appendChild(d),/^#/.test(t.theme)){var m=w.elem("style"),u=["#{{id}} .layui-laydate-header{background-color:{{theme}};}","#{{id}} .layui-this{background-color:{{theme}} !important;}"].join("").replace(/{{id}}/g,e.elemID).replace(/{{theme}}/g,t.theme);"styleSheet"in m?(m.setAttribute("type","text/css"),m.styleSheet.cssText=u):m.innerHTML=u,w(i).addClass("laydate-theme-molv"),i.appendChild(m)}e.remove(T.thisElemDate),a?t.elem.append(i):(document.body.appendChild(i),e.position()),e.checkDate().calendar(),e.changeEvent(),T.thisElemDate=e.elemID,"function"==typeof t.ready&&t.ready(w.extend({},t.dateTime,{month:t.dateTime.month+1}))},T.prototype.remove=function(e){var t=this,n=(t.config,w("#"+(e||t.elemID)));return n.hasClass(c)||t.checkDate(function(){n.remove()}),t},T.prototype.position=function(){var e=this,t=e.config,n=e.bindElem||t.elem[0],a=n.getBoundingClientRect(),i=e.elem.offsetWidth,r=e.elem.offsetHeight,o=function(e){return e=e?"scrollLeft":"scrollTop",document.body[e]|document.documentElement[e]},s=function(e){return document.documentElement[e?"clientWidth":"clientHeight"]},l=5,d=a.left,c=a.bottom;d+i+l>s("width")&&(d=s("width")-i-l),c+r+l>s()&&(c=a.top>r?a.top-r:s()-r,c-=2*l),t.position&&(e.elem.style.position=t.position),e.elem.style.left=d+("fixed"===t.position?0:o(1))+"px",e.elem.style.top=c+("fixed"===t.position?0:o())+"px"},T.prototype.hint=function(e){var t=this,n=(t.config,w.elem("div",{"class":h}));t.elem&&(n.innerHTML=e||"",w(t.elem).find("."+h).remove(),t.elem.appendChild(n),clearTimeout(t.hinTimer),t.hinTimer=setTimeout(function(){w(t.elem).find("."+h).remove()},3e3))},T.prototype.getAsYM=function(e,t,n){return n?t--:t++,t<0&&(t=11,e--),t>11&&(t=0,e++),[e,t]},T.prototype.systemDate=function(e){var t=e||new Date;return{year:t.getFullYear(),month:t.getMonth(),date:t.getDate(),hours:e?e.getHours():0,minutes:e?e.getMinutes():0,seconds:e?e.getSeconds():0}},T.prototype.checkDate=function(e){var t,a,i=this,r=(new Date,i.config),o=r.dateTime=r.dateTime||i.systemDate(),s=i.bindElem||r.elem[0],l=(i.isInput(s)?"val":"html",i.isInput(s)?s.value:"static"===r.position?"":s.innerHTML),c=function(e){e.year>d[1]&&(e.year=d[1],a=!0),e.month>11&&(e.month=11,a=!0),e.hours>23&&(e.hours=0,a=!0),e.minutes>59&&(e.minutes=0,e.hours++,a=!0),e.seconds>59&&(e.seconds=0,e.minutes++,a=!0),t=n.getEndDate(e.month+1,e.year),e.date>t&&(e.date=t,a=!0)},m=function(e,t,n){var o=["startTime","endTime"];t=(t.match(i.EXP_SPLIT)||[]).slice(1),n=n||0,r.range&&(i[o[n]]=i[o[n]]||{}),w.each(i.format,function(s,l){var c=parseFloat(t[s]);t[s].length必须遵循下述格式:
        "+(r.range?r.format+" "+r.range+" "+r.format:r.format)+"
        已为你重置"),a=!0):l&&l.constructor===Date?r.dateTime=i.systemDate(l):(r.dateTime=i.systemDate(),delete i.startState,delete i.endState,delete i.startDate,delete i.endDate,delete i.startTime,delete i.endTime),c(o),a&&l&&i.setValue(r.range?i.endDate?i.parse():"":i.parse()),e&&e(),i)},T.prototype.mark=function(e,t){var n,a=this,i=a.config;return w.each(i.mark,function(e,a){var i=e.split("-");i[0]!=t[0]&&0!=i[0]||i[1]!=t[1]&&0!=i[1]||i[2]!=t[2]||(n=a||t[2])}),n&&e.html(''+n+""),a},T.prototype.limit=function(e,t,n,a){var i,r=this,o=r.config,l={},d=o[n>41?"endDate":"dateTime"],c=w.extend({},d,t||{});return w.each({now:c,min:o.min,max:o.max},function(e,t){l[e]=r.newDate(w.extend({year:t.year,month:t.month,date:t.date},function(){var e={};return w.each(a,function(n,a){e[a]=t[a]}),e}())).getTime()}),i=l.nowl.max,e&&e[i?"addClass":"removeClass"](s),i},T.prototype.calendar=function(e){var t,a,i,r=this,s=r.config,l=e||s.dateTime,c=new Date,m=r.lang(),u="date"!==s.type&&"datetime"!==s.type,h=e?1:0,y=w(r.table[h]).find("td"),f=w(r.elemHeader[h][2]).find("span");if(l.yeard[1]&&(l.year=d[1],r.hint("最高只能支持到公元"+d[1]+"年")),r.firstDate||(r.firstDate=w.extend({},l)),c.setFullYear(l.year,l.month,1),t=c.getDay(),a=n.getEndDate(l.month||12,l.year),i=n.getEndDate(l.month+1,l.year),w.each(y,function(e,n){var d=[l.year,l.month],c=0;n=w(n),n.removeAttr("class"),e=t&&e=n.firstDate.year&&(r.month=a.max.month,r.date=a.max.date),n.limit(w(i),r,t),M++}),w(u[f?0:1]).attr("lay-ym",M-8+"-"+T[1]).html(b+p+" - "+(M-1+p))}else if("month"===e)w.each(new Array(12),function(e){var i=w.elem("li",{"lay-ym":e}),s={year:T[0],month:e};e+1==T[1]&&w(i).addClass(o),i.innerHTML=r.month[e]+(f?"月":""),d.appendChild(i),T[0]=n.firstDate.year&&(s.date=a.max.date),n.limit(w(i),s,t)}),w(u[f?0:1]).attr("lay-ym",T[0]+"-"+T[1]).html(T[0]+p);else if("time"===e){var E=function(){w(d).find("ol").each(function(e,a){w(a).find("li").each(function(a,i){n.limit(w(i),[{hours:a},{hours:n[x].hours,minutes:a},{hours:n[x].hours,minutes:n[x].minutes,seconds:a}][e],t,[["hours"],["hours","minutes"],["hours","minutes","seconds"]][e])})}),a.range||n.limit(w(n.footer).find(g),n[x],0,["hours","minutes","seconds"])};a.range?n[x]||(n[x]={hours:0,minutes:0,seconds:0}):n[x]=i,w.each([24,60,60],function(e,t){var a=w.elem("li"),i=["

        "+r.time[e]+"

          "];w.each(new Array(t),function(t){i.push(""+w.digit(t,2)+"")}),a.innerHTML=i.join("")+"
        ",d.appendChild(a)}),E()}if(y&&h.removeChild(y),h.appendChild(d),"year"===e||"month"===e)w(n.elemMain[t]).addClass("laydate-ym-show"),w(d).find("li").on("click",function(){var r=0|w(this).attr("lay-ym");if(!w(this).hasClass(s)){if(0===t)i[e]=r,l&&(n.startDate[e]=r),n.limit(w(n.footer).find(g),null,0);else if(l)n.endDate[e]=r;else{var c="year"===e?n.getAsYM(r,T[1]-1,"sub"):n.getAsYM(T[0],r,"sub");w.extend(i,{year:c[0],month:c[1]})}"year"===a.type||"month"===a.type?(w(d).find("."+o).removeClass(o),w(this).addClass(o),"month"===a.type&&"year"===e&&(n.listYM[t][0]=r,l&&(n[["startDate","endDate"][t]].year=r),n.list("month",t))):(n.checkDate("limit").calendar(),n.closeList()),n.setBtnStatus(),a.range||n.done(null,"change"),w(n.footer).find(D).removeClass(s)}});else{var S=w.elem("span",{"class":v}),k=function(){w(d).find("ol").each(function(e){var t=this,a=w(t).find("li");t.scrollTop=30*(n[x][C[e]]-2),t.scrollTop<=0&&a.each(function(e,n){if(!w(this).hasClass(s))return t.scrollTop=30*(e-2),!0})})},H=w(c[2]).find("."+v);k(),S.innerHTML=a.range?[r.startTime,r.endTime][t]:r.timeTips,w(n.elemMain[t]).addClass("laydate-time-show"),H[0]&&H.remove(),c[2].appendChild(S),w(d).find("ol").each(function(e){var t=this;w(t).find("li").on("click",function(){var r=0|this.innerHTML;w(this).hasClass(s)||(a.range?n[x][C[e]]=r:i[C[e]]=r,w(t).find("."+o).removeClass(o),w(this).addClass(o),E(),k(),(n.endDate||"time"===a.type)&&n.done(null,"change"),n.setBtnStatus())})})}return n},T.prototype.listYM=[],T.prototype.closeList=function(){var e=this;e.config;w.each(e.elemCont,function(t,n){w(this).find("."+m).remove(),w(e.elemMain[t]).removeClass("laydate-ym-show laydate-time-show")}),w(e.elem).find("."+v).remove()},T.prototype.setBtnStatus=function(e,t,n){var a,i=this,r=i.config,o=w(i.footer).find(g),d=r.range&&"date"!==r.type&&"time"!==r.type;d&&(t=t||i.startDate,n=n||i.endDate,a=i.newDate(t).getTime()>i.newDate(n).getTime(),i.limit(null,t)||i.limit(null,n)?o.addClass(s):o[a?"addClass":"removeClass"](s),e&&a&&i.hint("string"==typeof e?l.replace(/日期/g,e):l))},T.prototype.parse=function(e,t){var n=this,a=n.config,i=t||(e?w.extend({},n.endDate,n.endTime):a.range?w.extend({},n.startDate,n.startTime):a.dateTime),r=n.format.concat();return w.each(r,function(e,t){/yyyy|y/.test(t)?r[e]=w.digit(i.year,t.length):/MM|M/.test(t)?r[e]=w.digit(i.month+1,t.length):/dd|d/.test(t)?r[e]=w.digit(i.date,t.length):/HH|H/.test(t)?r[e]=w.digit(i.hours,t.length):/mm|m/.test(t)?r[e]=w.digit(i.minutes,t.length):/ss|s/.test(t)&&(r[e]=w.digit(i.seconds,t.length))}),a.range&&!e?r.join("")+" "+a.range+" "+n.parse(1):r.join("")},T.prototype.newDate=function(e){return e=e||{},new Date(e.year||1,e.month||0,e.date||1,e.hours||0,e.minutes||0,e.seconds||0)},T.prototype.setValue=function(e){var t=this,n=t.config,a=t.bindElem||n.elem[0],i=t.isInput(a)?"val":"html";return"static"===n.position||w(a)[i](e||""),this},T.prototype.stampRange=function(){var e,t,n=this,a=n.config,i=w(n.elem).find("td");if(a.range&&!n.endDate&&w(n.footer).find(g).addClass(s),n.endDate)return e=n.newDate({year:n.startDate.year,month:n.startDate.month,date:n.startDate.date}).getTime(),t=n.newDate({year:n.endDate.year,month:n.endDate.month,date:n.endDate.date}).getTime(),e>t?n.hint(l):void w.each(i,function(a,i){var r=w(i).attr("lay-ymd").split("-"),s=n.newDate({year:r[0],month:r[1]-1,date:r[2]}).getTime();w(i).removeClass(u+" "+o),s!==e&&s!==t||w(i).addClass(w(i).hasClass(y)||w(i).hasClass(f)?u:o),s>e&&s0&&t-1 in e)}function r(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return pe.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(Ce.test(t))return pe.filter(t,e,n);t=pe.filter(t,e)}return pe.grep(e,function(e){return pe.inArray(e,t)>-1!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t={};return pe.each(e.match(De)||[],function(e,n){t[n]=!0}),t}function a(){re.addEventListener?(re.removeEventListener("DOMContentLoaded",s),e.removeEventListener("load",s)):(re.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(re.addEventListener||"load"===e.event.type||"complete"===re.readyState)&&(a(),pe.ready())}function u(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(_e,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:qe.test(n)?pe.parseJSON(n):n)}catch(i){}pe.data(e,t,n)}else n=void 0}return n}function l(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(He(e)){var i,o,a=pe.expando,s=e.nodeType,u=s?pe.cache:e,l=s?e[a]:e[a]&&a;if(l&&u[l]&&(r||u[l].data)||void 0!==n||"string"!=typeof t)return l||(l=s?e[a]=ne.pop()||pe.guid++:a),u[l]||(u[l]=s?{}:{toJSON:pe.noop}),"object"!=typeof t&&"function"!=typeof t||(r?u[l]=pe.extend(u[l],t):u[l].data=pe.extend(u[l].data,t)),o=u[l],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[pe.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[pe.camelCase(t)])):i=o,i}}function f(e,t,n){if(He(e)){var r,i,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe.expando]:pe.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){pe.isArray(t)?t=t.concat(pe.map(t,pe.camelCase)):t in r?t=[t]:(t=pe.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!l(r):!pe.isEmptyObject(r))return}(n||(delete a[s].data,l(a[s])))&&(o?pe.cleanData([e],!0):fe.deleteExpando||a!=a.window?delete a[s]:a[s]=void 0)}}}function d(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:function(){return pe.css(e,t,"")},u=s(),l=n&&n[3]||(pe.cssNumber[t]?"":"px"),c=(pe.cssNumber[t]||"px"!==l&&+u)&&Me.exec(pe.css(e,t));if(c&&c[3]!==l){l=l||c[3],n=n||[],c=+u||1;do o=o||".5",c/=o,pe.style(e,t,c+l);while(o!==(o=s()/u)&&1!==o&&--a)}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function h(e,t){var n,r,i=0,o="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||pe.nodeName(r,t)?o.push(r):pe.merge(o,h(r,t));return void 0===t||t&&pe.nodeName(e,t)?pe.merge([e],o):o}function g(e,t){for(var n,r=0;null!=(n=e[r]);r++)pe._data(n,"globalEval",!t||pe._data(t[r],"globalEval"))}function m(e){Be.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t,n,r,i){for(var o,a,s,u,l,c,f,d=e.length,y=p(t),v=[],x=0;x"!==f[1]||Ve.test(a)?0:u:u.firstChild,o=a&&a.childNodes.length;o--;)pe.nodeName(c=a.childNodes[o],"tbody")&&!c.childNodes.length&&a.removeChild(c);for(pe.merge(v,u.childNodes),u.textContent="";u.firstChild;)u.removeChild(u.firstChild);u=y.lastChild}else v.push(t.createTextNode(a));for(u&&y.removeChild(u),fe.appendChecked||pe.grep(h(v,"input"),m),x=0;a=v[x++];)if(r&&pe.inArray(a,r)>-1)i&&i.push(a);else if(s=pe.contains(a.ownerDocument,a),u=h(y.appendChild(a),"script"),s&&g(u),n)for(o=0;a=u[o++];)Ie.test(a.type||"")&&n.push(a);return u=null,y}function v(){return!0}function x(){return!1}function b(){try{return re.activeElement}catch(e){}}function w(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)w(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),i===!1)i=x;else if(!i)return e;return 1===o&&(a=i,i=function(e){return pe().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=pe.guid++)),e.each(function(){pe.event.add(this,t,i,r,n)})}function T(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function C(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}function E(e){var t=it.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function N(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,r,i,o=pe._data(e),a=pe._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;r1&&"string"==typeof p&&!fe.checkClone&&rt.test(p))return e.each(function(i){var o=e.eq(i);g&&(t[0]=p.call(this,i,o.html())),S(o,t,n,r)});if(f&&(l=y(t,e[0].ownerDocument,!1,e,r),i=l.firstChild,1===l.childNodes.length&&(l=i),i||r)){for(s=pe.map(h(l,"script"),C),a=s.length;c")).appendTo(t.documentElement),t=(ut[0].contentWindow||ut[0].contentDocument).document,t.write(),t.close(),n=D(e,t),ut.detach()),lt[e]=n),n}function L(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function H(e){if(e in Et)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=Ct.length;n--;)if(e=Ct[n]+t,e in Et)return e}function q(e,t){for(var n,r,i,o=[],a=0,s=e.length;a=0&&n=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==pe.type(e)||e.nodeType||pe.isWindow(e))return!1;try{if(e.constructor&&!ce.call(e,"constructor")&&!ce.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(!fe.ownFirst)for(t in e)return ce.call(e,t);for(t in e);return void 0===t||ce.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?ue[le.call(e)]||"object":typeof e},globalEval:function(t){t&&pe.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(ge,"ms-").replace(me,ye)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t){var r,i=0;if(n(e))for(r=e.length;iT.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[P]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||V)-(~e.sourceIndex||V);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function u(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function l(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function f(){}function d(e){for(var t=0,n=e.length,r="";t1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function g(e,n,r){for(var i=0,o=n.length;i-1&&(r[l]=!(a[l]=f))}}else x=m(x===a?x.splice(h,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function v(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return ee(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==A)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s1&&h(c),s>1&&d(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,s0,o=e.length>0,a=function(r,a,s,u,l){var c,f,d,p=0,h="0",g=r&&[],y=[],v=A,x=r||o&&T.find.TAG("*",l),b=W+=null==v?1:Math.random()||.1,w=x.length;for(l&&(A=a===H||a||l);h!==w&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===H||(L(c),s=!_);d=e[f++];)if(d(c,a||H,s)){u.push(c);break}l&&(W=b)}i&&((c=!d&&c)&&p--,r&&g.push(c))}if(p+=h,i&&h!==p){for(f=0;d=n[f++];)d(g,y,a,s);if(r){if(p>0)for(;h--;)g[h]||y[h]||(y[h]=G.call(u));y=m(y)}Q.apply(u,y),l&&!r&&y.length>0&&p+n.length>1&&t.uniqueSort(u)}return l&&(W=b,A=v),g};return i?r(a):a}var b,w,T,C,E,N,k,S,A,D,j,L,H,q,_,F,M,O,R,P="sizzle"+1*new Date,B=e.document,W=0,I=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(j=!0),0},V=1<<31,Y={}.hasOwnProperty,J=[],G=J.pop,K=J.push,Q=J.push,Z=J.slice,ee=function(e,t){for(var n=0,r=e.length;n+~]|"+ne+")"+ne+"*"),ce=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),fe=new RegExp(oe),de=new RegExp("^"+re+"$"),pe={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re+"|[*])"),ATTR:new RegExp("^"+ie),PSEUDO:new RegExp("^"+oe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},he=/^(?:input|select|textarea|button)$/i,ge=/^h\d$/i,me=/^[^{]+\{\s*\[native \w/,ye=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,xe=/'|\\/g,be=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),we=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},Te=function(){L()};try{Q.apply(J=Z.call(B.childNodes),B.childNodes),J[B.childNodes.length].nodeType}catch(Ce){Q={apply:J.length?function(e,t){K.apply(e,Z.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},E=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},L=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:B;return r!==H&&9===r.nodeType&&r.documentElement?(H=r,q=H.documentElement,_=!E(H),(n=H.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Te,!1):n.attachEvent&&n.attachEvent("onunload",Te)),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(H.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=me.test(H.getElementsByClassName),w.getById=i(function(e){return q.appendChild(e).id=P,!H.getElementsByName||!H.getElementsByName(P).length}),w.getById?(T.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&_){var n=t.getElementById(e);return n?[n]:[]}},T.filter.ID=function(e){var t=e.replace(be,we);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(be,we);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):w.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&_)return t.getElementsByClassName(e)},M=[],F=[],(w.qsa=me.test(H.querySelectorAll))&&(i(function(e){q.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&F.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+P+"-]").length||F.push("~="),e.querySelectorAll(":checked").length||F.push(":checked"),e.querySelectorAll("a#"+P+"+*").length||F.push(".#.+[+~]")}),i(function(e){var t=H.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+ne+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=me.test(O=q.matches||q.webkitMatchesSelector||q.mozMatchesSelector||q.oMatchesSelector||q.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",oe)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=me.test(q.compareDocumentPosition),R=t||me.test(q.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return j=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!w.sortDetached&&t.compareDocumentPosition(e)===n?e===H||e.ownerDocument===B&&R(B,e)?-1:t===H||t.ownerDocument===B&&R(B,t)?1:D?ee(D,e)-ee(D,t):0:4&n?-1:1)}:function(e,t){if(e===t)return j=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===H?-1:t===H?1:i?-1:o?1:D?ee(D,e)-ee(D,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===B?-1:u[r]===B?1:0},H):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(ce,"='$1']"),w.matchesSelector&&_&&!X[n+" "]&&(!M||!M.test(n))&&(!F||!F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!_):void 0;return void 0!==r?r:w.attributes||!_?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(j=!w.detectDuplicates,D=!w.sortStable&&e.slice(0),e.sort(U),j){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return D=null,e},C=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=C(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=C(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:pe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(be,we),e[3]=(e[3]||e[4]||e[5]||"").replace(be,we),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&fe.test(n)&&(t=N(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(be,we).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ae," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,d,p,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s,x=!1;if(m){if(o){for(;g;){for(d=t;d=d[g];)if(s?d.nodeName.toLowerCase()===y:1===d.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){for(d=m,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}), +l=c[e]||[],p=l[0]===W&&l[1],x=p&&l[2],d=p&&m.childNodes[p];d=++p&&d&&d[g]||(x=p=0)||h.pop();)if(1===d.nodeType&&++x&&d===t){c[e]=[W,p,x];break}}else if(v&&(d=t,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),l=c[e]||[],p=l[0]===W&&l[1],x=p),x===!1)for(;(d=++p&&d&&d[g]||(x=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==y:1!==d.nodeType)||!++x||(v&&(f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),c[e]=[W,x]),d!==t)););return x-=i,x===r||x%r===0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[P]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=ee(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(se,"$1"));return i[P]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(be,we),function(t){return(t.textContent||t.innerText||C(t)).indexOf(e)>-1}}),lang:r(function(e){return de.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(be,we).toLowerCase(),function(t){var n;do if(n=_?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===q},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return ge.test(e.nodeName)},input:function(e){return he.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:l(function(){return[0]}),last:l(function(e,t){return[t-1]}),eq:l(function(e,t,n){return[n<0?n+t:n]}),even:l(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:l(function(e,t,n){for(var r=n<0?n+t:n;++r2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&_&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(be,we),t)||[])[0],!t)return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=pe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((u=T.find[s])&&(r=u(a.matches[0].replace(be,we),ve.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&d(o),!e)return Q.apply(n,r),n;break}}return(l||k(e,f))(r,t,!_,n,!t||ve.test(e)&&c(t.parentNode)||t),n},w.sortStable=P.split("").sort(U).join("")===P,w.detectDuplicates=!!j,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(te,function(e,t,n){var r;if(!n)return e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);pe.find=ve,pe.expr=ve.selectors,pe.expr[":"]=pe.expr.pseudos,pe.uniqueSort=pe.unique=ve.uniqueSort,pe.text=ve.getText,pe.isXMLDoc=ve.isXML,pe.contains=ve.contains;var xe=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&pe(e).is(n))break;r.push(e)}return r},be=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},we=pe.expr.match.needsContext,Te=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Ce=/^.[^:#\[\.,]*$/;pe.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?pe.find.matchesSelector(r,e)?[r]:[]:pe.find.matches(e,pe.grep(t,function(e){return 1===e.nodeType}))},pe.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(pe(e).filter(function(){for(t=0;t1?pe.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&we.test(e)?pe(e):e||[],!1).length}});var Ee,Ne=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,ke=pe.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||Ee,"string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:Ne.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof pe?t[0]:t,pe.merge(this,pe.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:re,!0)),Te.test(r[1])&&pe.isPlainObject(t))for(r in t)pe.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}if(i=re.getElementById(r[2]),i&&i.parentNode){if(i.id!==r[2])return Ee.find(e);this.length=1,this[0]=i}return this.context=re,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):pe.isFunction(e)?"undefined"!=typeof n.ready?n.ready(e):e(pe):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),pe.makeArray(e,this))};ke.prototype=pe.fn,Ee=pe(re);var Se=/^(?:parents|prev(?:Until|All))/,Ae={children:!0,contents:!0,next:!0,prev:!0};pe.fn.extend({has:function(e){var t,n=pe(e,this),r=n.length;return this.filter(function(){for(t=0;t-1:1===n.nodeType&&pe.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?pe.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?pe.inArray(this[0],pe(e)):pe.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(pe.uniqueSort(pe.merge(this.get(),pe(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),pe.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return xe(e,"parentNode")},parentsUntil:function(e,t,n){return xe(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return xe(e,"nextSibling")},prevAll:function(e){return xe(e,"previousSibling")},nextUntil:function(e,t,n){return xe(e,"nextSibling",n)},prevUntil:function(e,t,n){return xe(e,"previousSibling",n)},siblings:function(e){return be((e.parentNode||{}).firstChild,e)},children:function(e){return be(e.firstChild)},contents:function(e){return pe.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:pe.merge([],e.childNodes)}},function(e,t){pe.fn[e]=function(n,r){var i=pe.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=pe.filter(r,i)),this.length>1&&(Ae[e]||(i=pe.uniqueSort(i)),Se.test(e)&&(i=i.reverse())),this.pushStack(i)}});var De=/\S+/g;pe.Callbacks=function(e){e="string"==typeof e?o(e):pe.extend({},e);var t,n,r,i,a=[],s=[],u=-1,l=function(){for(i=e.once,r=t=!0;s.length;u=-1)for(n=s.shift();++u-1;)a.splice(n,1),n<=u&&u--}),this},has:function(e){return e?pe.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return i=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return i=!0,n||c.disable(),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||l()),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},pe.extend({Deferred:function(e){var t=[["resolve","done",pe.Callbacks("once memory"),"resolved"],["reject","fail",pe.Callbacks("once memory"),"rejected"],["notify","progress",pe.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return pe.Deferred(function(n){pe.each(t,function(t,o){var a=pe.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&pe.isFunction(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?pe.extend(e,r):r}},i={};return r.pipe=r.then,pe.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=ie.call(arguments),a=o.length,s=1!==a||e&&pe.isFunction(e.promise)?a:0,u=1===s?e:pe.Deferred(),l=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?ie.call(arguments):i,r===t?u.notifyWith(n,r):--s||u.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);i0||(je.resolveWith(re,[pe]),pe.fn.triggerHandler&&(pe(re).triggerHandler("ready"),pe(re).off("ready"))))}}),pe.ready.promise=function(t){if(!je)if(je=pe.Deferred(),"complete"===re.readyState||"loading"!==re.readyState&&!re.documentElement.doScroll)e.setTimeout(pe.ready);else if(re.addEventListener)re.addEventListener("DOMContentLoaded",s),e.addEventListener("load",s);else{re.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&re.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!pe.isReady){try{n.doScroll("left")}catch(t){return e.setTimeout(i,50)}a(),pe.ready()}}()}return je.promise(t)},pe.ready.promise();var Le;for(Le in pe(fe))break;fe.ownFirst="0"===Le,fe.inlineBlockNeedsLayout=!1,pe(function(){var e,t,n,r;n=re.getElementsByTagName("body")[0],n&&n.style&&(t=re.createElement("div"),r=re.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),"undefined"!=typeof t.style.zoom&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",fe.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=re.createElement("div");fe.deleteExpando=!0;try{delete e.test}catch(t){fe.deleteExpando=!1}e=null}();var He=function(e){var t=pe.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return(1===n||9===n)&&(!t||t!==!0&&e.getAttribute("classid")===t)},qe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,_e=/([A-Z])/g;pe.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?pe.cache[e[pe.expando]]:e[pe.expando],!!e&&!l(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return f(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return f(e,t,!0)}}),pe.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=pe.data(o),1===o.nodeType&&!pe._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=pe.camelCase(r.slice(5)),u(o,r,i[r])));pe._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){pe.data(this,e)}):arguments.length>1?this.each(function(){pe.data(this,e,t)}):o?u(o,e,pe.data(o,e)):void 0},removeData:function(e){return this.each(function(){pe.removeData(this,e)})}}),pe.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=pe._data(e,t),n&&(!r||pe.isArray(n)?r=pe._data(e,t,pe.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=pe.queue(e,t),r=n.length,i=n.shift(),o=pe._queueHooks(e,t),a=function(){pe.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return pe._data(e,n)||pe._data(e,n,{empty:pe.Callbacks("once memory").add(function(){pe._removeData(e,t+"queue"),pe._removeData(e,n)})})}}),pe.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length
        a",fe.leadingWhitespace=3===e.firstChild.nodeType,fe.tbody=!e.getElementsByTagName("tbody").length,fe.htmlSerialize=!!e.getElementsByTagName("link").length,fe.html5Clone="<:nav>"!==re.createElement("nav").cloneNode(!0).outerHTML,n.type="checkbox",n.checked=!0,t.appendChild(n),fe.appendChecked=n.checked,e.innerHTML="",fe.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue,t.appendChild(e),n=re.createElement("input"),n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),e.appendChild(n),fe.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,fe.noCloneEvent=!!e.addEventListener,e[pe.expando]=1,fe.attributes=!e.getAttribute(pe.expando)}();var Xe={option:[1,""],legend:[1,"
        ","
        "],area:[1,"",""],param:[1,"",""],thead:[1,"","
        "],tr:[2,"","
        "],col:[2,"","
        "],td:[3,"","
        "],_default:fe.htmlSerialize?[0,"",""]:[1,"X
        ","
        "]};Xe.optgroup=Xe.option,Xe.tbody=Xe.tfoot=Xe.colgroup=Xe.caption=Xe.thead,Xe.th=Xe.td;var Ue=/<|&#?\w+;/,Ve=/-1&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[pe.expando]?t:new pe.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:pe.makeArray(n,[t]),l=pe.event.special[p]||{},i||!l.trigger||l.trigger.apply(r,n)!==!1)){if(!i&&!l.noBubble&&!pe.isWindow(r)){for(u=l.delegateType||p,Ke.test(u+p)||(s=s.parentNode);s;s=s.parentNode)d.push(s),c=s;c===(r.ownerDocument||re)&&d.push(c.defaultView||c.parentWindow||e)}for(f=0;(s=d[f++])&&!t.isPropagationStopped();)t.type=f>1?u:l.bindType||p,o=(pe._data(s,"events")||{})[t.type]&&pe._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&He(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!l._default||l._default.apply(d.pop(),n)===!1)&&He(r)&&a&&r[p]&&!pe.isWindow(r)){c=r[a],c&&(r[a]=null),pe.event.triggered=p;try{r[p]()}catch(g){}pe.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=pe.event.fix(e);var t,n,r,i,o,a=[],s=ie.call(arguments),u=(pe._data(this,"events")||{})[e.type]||[],l=pe.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){for(a=pe.event.handlers.call(this,e,u),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!e.isImmediatePropagationStopped();)e.rnamespace&&!e.rnamespace.test(o.namespace)||(e.handleObj=o,e.data=o.data,r=((pe.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s),void 0!==r&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()));return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,u=e.target;if(s&&u.nodeType&&("click"!==e.type||isNaN(e.button)||e.button<1))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(r=[],n=0;n-1:pe.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&a.push({elem:u,handlers:r})}return s]","i"),tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,nt=/\s*$/g,at=p(re),st=at.appendChild(re.createElement("div"));pe.extend({htmlPrefilter:function(e){return e.replace(tt,"<$1>")},clone:function(e,t,n){var r,i,o,a,s,u=pe.contains(e.ownerDocument,e);if(fe.html5Clone||pe.isXMLDoc(e)||!et.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(st.innerHTML=e.outerHTML,st.removeChild(o=st.firstChild)),!(fe.noCloneEvent&&fe.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||pe.isXMLDoc(e)))for(r=h(o),s=h(e),a=0;null!=(i=s[a]);++a)r[a]&&k(i,r[a]);if(t)if(n)for(s=s||h(e),r=r||h(o),a=0;null!=(i=s[a]);a++)N(i,r[a]);else N(e,o);return r=h(o,"script"),r.length>0&&g(r,!u&&h(e,"script")),r=s=i=null,o},cleanData:function(e,t){for(var n,r,i,o,a=0,s=pe.expando,u=pe.cache,l=fe.attributes,c=pe.event.special;null!=(n=e[a]);a++)if((t||He(n))&&(i=n[s],o=i&&u[i])){if(o.events)for(r in o.events)c[r]?pe.event.remove(n,r):pe.removeEvent(n,r,o.handle);u[i]&&(delete u[i],l||"undefined"==typeof n.removeAttribute?n[s]=void 0:n.removeAttribute(s),ne.push(i))}}}),pe.fn.extend({domManip:S,detach:function(e){return A(this,e,!0)},remove:function(e){return A(this,e)},text:function(e){return Pe(this,function(e){return void 0===e?pe.text(this):this.empty().append((this[0]&&this[0].ownerDocument||re).createTextNode(e))},null,e,arguments.length)},append:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.appendChild(e)}})},prepend:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&pe.cleanData(h(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&pe.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return pe.clone(this,e,t)})},html:function(e){return Pe(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ze,""):void 0;if("string"==typeof e&&!nt.test(e)&&(fe.htmlSerialize||!et.test(e))&&(fe.leadingWhitespace||!$e.test(e))&&!Xe[(We.exec(e)||["",""])[1].toLowerCase()]){e=pe.htmlPrefilter(e);try{for(;nt",t=l.getElementsByTagName("td"),t[0].style.cssText="margin:0;border:0;padding:0;display:none",o=0===t[0].offsetHeight,o&&(t[0].style.display="",t[1].style.display="none",o=0===t[0].offsetHeight)),f.removeChild(u)}var n,r,i,o,a,s,u=re.createElement("div"),l=re.createElement("div");l.style&&(l.style.cssText="float:left;opacity:.5",fe.opacity="0.5"===l.style.opacity,fe.cssFloat=!!l.style.cssFloat,l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",fe.clearCloneStyle="content-box"===l.style.backgroundClip,u=re.createElement("div"),u.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",l.innerHTML="",u.appendChild(l),fe.boxSizing=""===l.style.boxSizing||""===l.style.MozBoxSizing||""===l.style.WebkitBoxSizing,pe.extend(fe,{reliableHiddenOffsets:function(){return null==n&&t(),o},boxSizingReliable:function(){return null==n&&t(),i},pixelMarginRight:function(){return null==n&&t(),r},pixelPosition:function(){return null==n&&t(),n},reliableMarginRight:function(){return null==n&&t(),a},reliableMarginLeft:function(){return null==n&&t(),s}}))}();var ht,gt,mt=/^(top|right|bottom|left)$/;e.getComputedStyle?(ht=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n.getPropertyValue(t)||n[t]:void 0,""!==a&&void 0!==a||pe.contains(e.ownerDocument,e)||(a=pe.style(e,t)),n&&!fe.pixelMarginRight()&&ft.test(a)&&ct.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o),void 0===a?a:a+""}):pt.currentStyle&&(ht=function(e){return e.currentStyle},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),ft.test(a)&&!mt.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"});var yt=/alpha\([^)]*\)/i,vt=/opacity\s*=\s*([^)]*)/i,xt=/^(none|table(?!-c[ea]).+)/,bt=new RegExp("^("+Fe+")(.*)$","i"),wt={position:"absolute",visibility:"hidden",display:"block"},Tt={letterSpacing:"0",fontWeight:"400"},Ct=["Webkit","O","Moz","ms"],Et=re.createElement("div").style;pe.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=gt(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":fe.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=pe.camelCase(t),u=e.style;if(t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:u[t];if(o=typeof n,"string"===o&&(i=Me.exec(n))&&i[1]&&(n=d(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(pe.cssNumber[s]?"":"px")),fe.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{u[t]=n}catch(l){}}},css:function(e,t,n,r){var i,o,a,s=pe.camelCase(t);return t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=gt(e,t,r)),"normal"===o&&t in Tt&&(o=Tt[t]),""===n||n?(i=parseFloat(o),n===!0||isFinite(i)?i||0:o):o}}),pe.each(["height","width"],function(e,t){pe.cssHooks[t]={get:function(e,n,r){if(n)return xt.test(pe.css(e,"display"))&&0===e.offsetWidth?dt(e,wt,function(){return M(e,t,r)}):M(e,t,r)},set:function(e,n,r){var i=r&&ht(e);return _(e,n,r?F(e,t,r,fe.boxSizing&&"border-box"===pe.css(e,"boxSizing",!1,i),i):0)}}}),fe.opacity||(pe.cssHooks.opacity={get:function(e,t){return vt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=pe.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===pe.trim(o.replace(yt,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=yt.test(o)?o.replace(yt,i):o+" "+i)}}),pe.cssHooks.marginRight=L(fe.reliableMarginRight,function(e,t){if(t)return dt(e,{display:"inline-block"},gt,[e,"marginRight"])}),pe.cssHooks.marginLeft=L(fe.reliableMarginLeft,function(e,t){if(t)return(parseFloat(gt(e,"marginLeft"))||(pe.contains(e.ownerDocument,e)?e.getBoundingClientRect().left-dt(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}):0))+"px"}),pe.each({margin:"",padding:"",border:"Width"},function(e,t){pe.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+Oe[r]+t]=o[r]||o[r-2]||o[0];return i}},ct.test(e)||(pe.cssHooks[e+t].set=_)}),pe.fn.extend({css:function(e,t){return Pe(this,function(e,t,n){var r,i,o={},a=0;if(pe.isArray(t)){for(r=ht(e),i=t.length;a1)},show:function(){return q(this,!0)},hide:function(){return q(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Re(this)?pe(this).show():pe(this).hide()})}}),pe.Tween=O,O.prototype={constructor:O,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||pe.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(pe.cssNumber[n]?"":"px")},cur:function(){var e=O.propHooks[this.prop];return e&&e.get?e.get(this):O.propHooks._default.get(this)},run:function(e){var t,n=O.propHooks[this.prop];return this.options.duration?this.pos=t=pe.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):O.propHooks._default.set(this),this}},O.prototype.init.prototype=O.prototype,O.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=pe.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){pe.fx.step[e.prop]?pe.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[pe.cssProps[e.prop]]&&!pe.cssHooks[e.prop]?e.elem[e.prop]=e.now:pe.style(e.elem,e.prop,e.now+e.unit)}}},O.propHooks.scrollTop=O.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},pe.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},pe.fx=O.prototype.init,pe.fx.step={};var Nt,kt,St=/^(?:toggle|show|hide)$/,At=/queueHooks$/;pe.Animation=pe.extend($,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return d(n.elem,e,Me.exec(t),n),n}]},tweener:function(e,t){pe.isFunction(e)?(t=e,e=["*"]):e=e.match(De);for(var n,r=0,i=e.length;r
        a",e=n.getElementsByTagName("a")[0],t.setAttribute("type","checkbox"),n.appendChild(t),e=n.getElementsByTagName("a")[0],e.style.cssText="top:1px",fe.getSetAttribute="t"!==n.className,fe.style=/top/.test(e.getAttribute("style")),fe.hrefNormalized="/a"===e.getAttribute("href"),fe.checkOn=!!t.value,fe.optSelected=i.selected,fe.enctype=!!re.createElement("form").enctype,r.disabled=!0,fe.optDisabled=!i.disabled,t=re.createElement("input"),t.setAttribute("value",""),fe.input=""===t.getAttribute("value"),t.value="t",t.setAttribute("type","radio"),fe.radioValue="t"===t.value}();var Dt=/\r/g,jt=/[\x20\t\r\n\f]+/g;pe.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=pe.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,pe(this).val()):e,null==i?i="":"number"==typeof i?i+="":pe.isArray(i)&&(i=pe.map(i,function(e){return null==e?"":e+""})),t=pe.valHooks[this.type]||pe.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=pe.valHooks[i.type]||pe.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(Dt,""):null==n?"":n)}}}),pe.extend({valHooks:{option:{get:function(e){var t=pe.find.attr(e,"value");return null!=t?t:pe.trim(pe.text(e)).replace(jt," ")}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||i<0,a=o?null:[],s=o?i+1:r.length,u=i<0?s:o?i:0;u-1)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),pe.each(["radio","checkbox"],function(){pe.valHooks[this]={set:function(e,t){if(pe.isArray(t))return e.checked=pe.inArray(pe(e).val(),t)>-1}},fe.checkOn||(pe.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Lt,Ht,qt=pe.expr.attrHandle,_t=/^(?:checked|selected)$/i,Ft=fe.getSetAttribute,Mt=fe.input;pe.fn.extend({attr:function(e,t){return Pe(this,pe.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){pe.removeAttr(this,e)})}}),pe.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?pe.prop(e,t,n):(1===o&&pe.isXMLDoc(e)||(t=t.toLowerCase(),i=pe.attrHooks[t]||(pe.expr.match.bool.test(t)?Ht:Lt)),void 0!==n?null===n?void pe.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=pe.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!fe.radioValue&&"radio"===t&&pe.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(De);if(o&&1===e.nodeType)for(;n=o[i++];)r=pe.propFix[n]||n,pe.expr.match.bool.test(n)?Mt&&Ft||!_t.test(n)?e[r]=!1:e[pe.camelCase("default-"+n)]=e[r]=!1:pe.attr(e,n,""),e.removeAttribute(Ft?n:r)}}),Ht={set:function(e,t,n){return t===!1?pe.removeAttr(e,n):Mt&&Ft||!_t.test(n)?e.setAttribute(!Ft&&pe.propFix[n]||n,n):e[pe.camelCase("default-"+n)]=e[n]=!0,n}},pe.each(pe.expr.match.bool.source.match(/\w+/g),function(e,t){var n=qt[t]||pe.find.attr;Mt&&Ft||!_t.test(t)?qt[t]=function(e,t,r){var i,o;return r||(o=qt[t],qt[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,qt[t]=o),i}:qt[t]=function(e,t,n){if(!n)return e[pe.camelCase("default-"+t)]?t.toLowerCase():null}}),Mt&&Ft||(pe.attrHooks.value={set:function(e,t,n){return pe.nodeName(e,"input")?void(e.defaultValue=t):Lt&&Lt.set(e,t,n)}}),Ft||(Lt={set:function(e,t,n){var r=e.getAttributeNode(n);if(r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n))return t}},qt.id=qt.name=qt.coords=function(e,t,n){var r;if(!n)return(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},pe.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);if(n&&n.specified)return n.value},set:Lt.set},pe.attrHooks.contenteditable={set:function(e,t,n){Lt.set(e,""!==t&&t,n)}},pe.each(["width","height"],function(e,t){pe.attrHooks[t]={set:function(e,n){if(""===n)return e.setAttribute(t,"auto"),n}}})),fe.style||(pe.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var Ot=/^(?:input|select|textarea|button|object)$/i,Rt=/^(?:a|area)$/i;pe.fn.extend({prop:function(e,t){return Pe(this,pe.prop,e,t,arguments.length>1)},removeProp:function(e){return e=pe.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),pe.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&pe.isXMLDoc(e)||(t=pe.propFix[t]||t,i=pe.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=pe.find.attr(e,"tabindex");return t?parseInt(t,10):Ot.test(e.nodeName)||Rt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),fe.hrefNormalized||pe.each(["href","src"],function(e,t){pe.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),fe.optSelected||(pe.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),pe.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){pe.propFix[this.toLowerCase()]=this}),fe.enctype||(pe.propFix.enctype="encoding");var Pt=/[\t\r\n\f]/g;pe.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).addClass(e.call(this,t,z(this)))});if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).removeClass(e.call(this,t,z(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):pe.isFunction(e)?this.each(function(n){pe(this).toggleClass(e.call(this,n,z(this),t),t)}):this.each(function(){var t,r,i,o;if("string"===n)for(r=0,i=pe(this),o=e.match(De)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||(t=z(this),t&&pe._data(this,"__className__",t),pe.attr(this,"class",t||e===!1?"":pe._data(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+z(n)+" ").replace(Pt," ").indexOf(t)>-1)return!0;return!1}}),pe.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){pe.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),pe.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}});var Bt=e.location,Wt=pe.now(),It=/\?/,$t=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;pe.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=pe.trim(t+"");return i&&!pe.trim(i.replace($t,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():pe.error("Invalid JSON: "+t)},pe.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new e.DOMParser,n=r.parseFromString(t,"text/xml")):(n=new e.ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||pe.error("Invalid XML: "+t),n};var zt=/#.*$/,Xt=/([?&])_=[^&]*/,Ut=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Vt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Yt=/^(?:GET|HEAD)$/,Jt=/^\/\//,Gt=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Kt={},Qt={},Zt="*/".concat("*"),en=Bt.href,tn=Gt.exec(en.toLowerCase())||[];pe.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:en,type:"GET",isLocal:Vt.test(tn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":pe.parseJSON,"text xml":pe.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?V(V(e,pe.ajaxSettings),t):V(pe.ajaxSettings,e)},ajaxPrefilter:X(Kt),ajaxTransport:X(Qt),ajax:function(t,n){function r(t,n,r,i){var o,f,v,x,w,C=n;2!==b&&(b=2,u&&e.clearTimeout(u),c=void 0,s=i||"",T.readyState=t>0?4:0,o=t>=200&&t<300||304===t,r&&(x=Y(d,T,r)),x=J(d,x,T,o),o?(d.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(pe.lastModified[a]=w),w=T.getResponseHeader("etag"),w&&(pe.etag[a]=w)),204===t||"HEAD"===d.type?C="nocontent":304===t?C="notmodified":(C=x.state,f=x.data,v=x.error,o=!v)):(v=C,!t&&C||(C="error",t<0&&(t=0))),T.status=t,T.statusText=(n||C)+"",o?g.resolveWith(p,[f,C,T]):g.rejectWith(p,[T,C,v]),T.statusCode(y),y=void 0,l&&h.trigger(o?"ajaxSuccess":"ajaxError",[T,d,o?f:v]),m.fireWith(p,[T,C]),l&&(h.trigger("ajaxComplete",[T,d]),--pe.active||pe.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,d=pe.ajaxSetup({},n),p=d.context||d,h=d.context&&(p.nodeType||p.jquery)?pe(p):pe.event,g=pe.Deferred(),m=pe.Callbacks("once memory"),y=d.statusCode||{},v={},x={},b=0,w="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!f)for(f={};t=Ut.exec(s);)f[t[1].toLowerCase()]=t[2];t=f[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?s:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=x[n]=x[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(b<2)for(t in e)y[t]=[y[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||w;return c&&c.abort(t),r(0,t),this}};if(g.promise(T).complete=m.add,T.success=T.done,T.error=T.fail,d.url=((t||d.url||en)+"").replace(zt,"").replace(Jt,tn[1]+"//"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=pe.trim(d.dataType||"*").toLowerCase().match(De)||[""],null==d.crossDomain&&(i=Gt.exec(d.url.toLowerCase()),d.crossDomain=!(!i||i[1]===tn[1]&&i[2]===tn[2]&&(i[3]||("http:"===i[1]?"80":"443"))===(tn[3]||("http:"===tn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=pe.param(d.data,d.traditional)),U(Kt,d,n,T),2===b)return T;l=pe.event&&d.global,l&&0===pe.active++&&pe.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Yt.test(d.type),a=d.url,d.hasContent||(d.data&&(a=d.url+=(It.test(a)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=Xt.test(a)?a.replace(Xt,"$1_="+Wt++):a+(It.test(a)?"&":"?")+"_="+Wt++)),d.ifModified&&(pe.lastModified[a]&&T.setRequestHeader("If-Modified-Since",pe.lastModified[a]),pe.etag[a]&&T.setRequestHeader("If-None-Match",pe.etag[a])),(d.data&&d.hasContent&&d.contentType!==!1||n.contentType)&&T.setRequestHeader("Content-Type",d.contentType),T.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Zt+"; q=0.01":""):d.accepts["*"]);for(o in d.headers)T.setRequestHeader(o,d.headers[o]);if(d.beforeSend&&(d.beforeSend.call(p,T,d)===!1||2===b))return T.abort();w="abort";for(o in{success:1,error:1,complete:1})T[o](d[o]);if(c=U(Qt,d,n,T)){if(T.readyState=1,l&&h.trigger("ajaxSend",[T,d]),2===b)return T;d.async&&d.timeout>0&&(u=e.setTimeout(function(){T.abort("timeout")},d.timeout));try{b=1,c.send(v,r)}catch(C){if(!(b<2))throw C;r(-1,C)}}else r(-1,"No Transport");return T},getJSON:function(e,t,n){return pe.get(e,t,n,"json")},getScript:function(e,t){return pe.get(e,void 0,t,"script")}}),pe.each(["get","post"],function(e,t){pe[t]=function(e,n,r,i){return pe.isFunction(n)&&(i=i||r,r=n,n=void 0),pe.ajax(pe.extend({url:e,type:t,dataType:i,data:n,success:r},pe.isPlainObject(e)&&e))}}),pe._evalUrl=function(e){return pe.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},pe.fn.extend({wrapAll:function(e){if(pe.isFunction(e))return this.each(function(t){pe(this).wrapAll(e.call(this,t))});if(this[0]){var t=pe(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return pe.isFunction(e)?this.each(function(t){pe(this).wrapInner(e.call(this,t))}):this.each(function(){var t=pe(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=pe.isFunction(e);return this.each(function(n){pe(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){pe.nodeName(this,"body")||pe(this).replaceWith(this.childNodes)}).end()}}),pe.expr.filters.hidden=function(e){return fe.reliableHiddenOffsets()?e.offsetWidth<=0&&e.offsetHeight<=0&&!e.getClientRects().length:K(e)},pe.expr.filters.visible=function(e){return!pe.expr.filters.hidden(e)};var nn=/%20/g,rn=/\[\]$/,on=/\r?\n/g,an=/^(?:submit|button|image|reset|file)$/i,sn=/^(?:input|select|textarea|keygen)/i;pe.param=function(e,t){var n,r=[],i=function(e,t){t=pe.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=pe.ajaxSettings&&pe.ajaxSettings.traditional),pe.isArray(e)||e.jquery&&!pe.isPlainObject(e))pe.each(e,function(){i(this.name,this.value)});else for(n in e)Q(n,e[n],t,i);return r.join("&").replace(nn,"+")},pe.fn.extend({serialize:function(){return pe.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=pe.prop(this,"elements");return e?pe.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!pe(this).is(":disabled")&&sn.test(this.nodeName)&&!an.test(e)&&(this.checked||!Be.test(e))}).map(function(e,t){var n=pe(this).val();return null==n?null:pe.isArray(n)?pe.map(n,function(e){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),pe.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return this.isLocal?ee():re.documentMode>8?Z():/^(get|post|head|put|delete|options)$/i.test(this.type)&&Z()||ee()}:Z;var un=0,ln={},cn=pe.ajaxSettings.xhr();e.attachEvent&&e.attachEvent("onunload",function(){for(var e in ln)ln[e](void 0,!0)}),fe.cors=!!cn&&"withCredentials"in cn,cn=fe.ajax=!!cn,cn&&pe.ajaxTransport(function(t){if(!t.crossDomain||fe.cors){var n;return{send:function(r,i){var o,a=t.xhr(),s=++un;if(a.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(o in t.xhrFields)a[o]=t.xhrFields[o];t.mimeType&&a.overrideMimeType&&a.overrideMimeType(t.mimeType),t.crossDomain||r["X-Requested-With"]||(r["X-Requested-With"]="XMLHttpRequest");for(o in r)void 0!==r[o]&&a.setRequestHeader(o,r[o]+"");a.send(t.hasContent&&t.data||null),n=function(e,r){var o,u,l;if(n&&(r||4===a.readyState))if(delete ln[s],n=void 0,a.onreadystatechange=pe.noop,r)4!==a.readyState&&a.abort();else{l={},o=a.status,"string"==typeof a.responseText&&(l.text=a.responseText);try{u=a.statusText}catch(c){u=""}o||!t.isLocal||t.crossDomain?1223===o&&(o=204):o=l.text?200:404}l&&i(o,u,l,a.getAllResponseHeaders())},t.async?4===a.readyState?e.setTimeout(n):a.onreadystatechange=ln[s]=n:n()},abort:function(){n&&n(void 0,!0)}}}}),pe.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return pe.globalEval(e),e}}}),pe.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),pe.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=re.head||pe("head")[0]||re.documentElement;return{send:function(r,i){t=re.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var fn=[],dn=/(=)\?(?=&|$)|\?\?/;pe.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=fn.pop()||pe.expando+"_"+Wt++;return this[e]=!0,e}}),pe.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(dn.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&dn.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=pe.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(dn,"$1"+i):t.jsonp!==!1&&(t.url+=(It.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||pe.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?pe(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,fn.push(i)),a&&pe.isFunction(o)&&o(a[0]),a=o=void 0}),"script"}),pe.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||re;var r=Te.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=y([e],t,i),i&&i.length&&pe(i).remove(),pe.merge([],r.childNodes))};var pn=pe.fn.load;return pe.fn.load=function(e,t,n){if("string"!=typeof e&&pn)return pn.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=pe.trim(e.slice(s,e.length)),e=e.slice(0,s)),pe.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&pe.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?pe("
        ").append(pe.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},pe.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){pe.fn[t]=function(e){return this.on(t,e)}}),pe.expr.filters.animated=function(e){return pe.grep(pe.timers,function(t){return e===t.elem}).length},pe.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=pe.css(e,"position"),f=pe(e),d={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=pe.css(e,"top"),u=pe.css(e,"left"),l=("absolute"===c||"fixed"===c)&&pe.inArray("auto",[o,u])>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),pe.isFunction(t)&&(t=t.call(e,n,pe.extend({},s))),null!=t.top&&(d.top=t.top-s.top+a),null!=t.left&&(d.left=t.left-s.left+i),"using"in t?t.using.call(e,d):f.css(d)}},pe.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){pe.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,pe.contains(t,i)?("undefined"!=typeof i.getBoundingClientRect&&(r=i.getBoundingClientRect()),n=te(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===pe.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),pe.nodeName(e[0],"html")||(n=e.offset()),n.top+=pe.css(e[0],"borderTopWidth",!0),n.left+=pe.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-pe.css(r,"marginTop",!0),left:t.left-n.left-pe.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){ +for(var e=this.offsetParent;e&&!pe.nodeName(e,"html")&&"static"===pe.css(e,"position");)e=e.offsetParent;return e||pt})}}),pe.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);pe.fn[e]=function(r){return Pe(this,function(e,r,i){var o=te(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?pe(o).scrollLeft():i,n?i:pe(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),pe.each(["top","left"],function(e,t){pe.cssHooks[t]=L(fe.pixelPosition,function(e,n){if(n)return n=gt(e,t),ft.test(n)?pe(e).position()[t]+"px":n})}),pe.each({Height:"height",Width:"width"},function(e,t){pe.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){pe.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return Pe(this,function(t,n,r){var i;return pe.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?pe.css(t,n,a):pe.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),pe.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),pe.fn.size=function(){return this.length},pe.fn.andSelf=pe.fn.addBack,layui.define(function(e){layui.$=pe,e("jquery",pe)}),pe});!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,i=t.length-1,n=i;n>0;n--)if("interactive"===t[n].readyState){e=t[n].src;break}return e||t[i].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"],getStyle:function(t,i){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](i)},link:function(t,i,n){if(r.path){var a=document.getElementsByTagName("head")[0],s=document.createElement("link");"string"==typeof i&&(n=i);var l=(n||t).replace(/\.|\//g,""),f="layuicss-"+l,c=0;s.rel="stylesheet",s.href=r.path+t,s.id=f,document.getElementById(f)||a.appendChild(s),"function"==typeof i&&!function u(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(o.getStyle(document.getElementById(f),"width"))?i():setTimeout(u,100))}()}}},r={v:"3.1.1",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):o.link("theme/"+e.extend),this):this},ready:function(e){var t="layer",i="",n=(a?"modules/layer/":"theme/")+"default/layer.css?v="+r.v+i;return a?layui.addcss(n,e,t):o.link(n,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s="function"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+" layui-layer-hui",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];l.anim=["layer-anim-00","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'
        '+(f?r.title[0]:r.title)+"
        ":"";return r.zIndex=s,t([r.shade?'
        ':"",'
        '+(e&&2!=r.type?"":u)+'
        '+(0==r.type&&r.icon!==-1?'':"")+(1==r.type&&e?"":r.content||"")+'
        '+function(){var e=c?'':"";return r.closeBtn&&(e+=''),e}()+""+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t'+r.btn[t]+"";return'
        '+e+"
        "}():"")+(r.resize?'':"")+"
        "],u,i('
        ')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f="object"==typeof s,c=i("body");if(!t.id||!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var s=t.content=f?t.content:[t.content||"","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}if(e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){s.parents("."+l[0])[0]||(s.data("display",s.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+l[0]+a).find("."+l[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=u),e.layero=i("#"+l[0]+a),t.scrollbar||l.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),i("#layui-layer-shade"+e.index).css({"background-color":t.shade[1]||"#000",opacity:t.shade[0]||t.shade}),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]){var u="layer-anim "+l.anim[t.anim];e.layero.addClass(u).one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",function(){i(this).removeClass(u)})}t.isOutAnim&&e.layero.data("isOutAnim",!0)}},s.pt.auto=function(e){var t=this,a=t.config,o=i("#"+l[0]+e);""===a.area[0]&&a.maxWidth>0&&(r.ie&&r.ie<8&&a.btn&&o.width(o.innerWidth()),o.outerWidth()>a.maxWidth&&o.width(a.maxWidth));var s=[o.innerWidth(),o.innerHeight()],f=o.find(l[1]).outerHeight()||0,c=o.find("."+l[6]).outerHeight()||0,u=function(e){e=o.find(e),e.height(s[1]-f-c-2*(0|parseFloat(e.css("padding-top"))))};switch(a.type){case 2:u("iframe");break;default:""===a.area[1]?a.maxHeight>0&&o.outerHeight()>a.maxHeight?(s[1]=a.maxHeight,u("."+l[5])):a.fixed&&s[1]>=n.height()&&(s[1]=n.height(),u("."+l[5])):u("."+l[5])}return t},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:"auto"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find("."+l[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(".layui-layer-resize"),c={};return t.move&&l.css("cursor","move"),l.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css("left")),e.clientY-parseFloat(s.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l="fixed"===s.css("position");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;af&&(a=f),ou&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+l[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+l[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+l[0])[0]||1==n.attr("layer")&&i("."+l[0]).length<1&&n.removeAttr("layer").show(),n=null})},s.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+l[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr("layer-full")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty("overflow"):l.html[0].style.removeAttribute("overflow"),l.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+l[4]).attr("times"),i("#"+l[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+l[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find("."+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+l[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+l[0]+e),r=a.find(".layui-layer-content"),s=a.attr("type"),f=a.find(l[1]).outerHeight()||0,c=a.find("."+l[6]).outerHeight()||0;a.attr("minLeft");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+l[6]).outerHeight(),s===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+l[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+l[0]+e);o.record(a),l.html.attr("layer-full")||l.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i("#"+l[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var s="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+l[5]+")").remove();for(var a=t.find("."+s),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(s)}else{if(n===o.type[2])try{var f=i("#"+l[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("isOutAnim")&&t.addClass("layer-anim "+a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),r.ie&&r.ie<10||!t.data("isOutAnim")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i("."+l[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var s,l=2==e.formType?'":function(){return''}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:["确定","取消"],content:l,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(t){s=t.find(".layui-layer-input"),s.val(e.value||"").focus(),"function"==typeof f&&f(t)},resize:!1,yes:function(i){var n=s.val();""===n?s.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n="layui-this",a=e.success;return delete e.success,r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,a="";if(e>0)for(a=''+t[0].title+"";i"+t[i].title+"";return a}(),content:'
          '+function(){var e=t.length,i=1,a="";if(e>0)for(a='
        • '+(t[0].content||"no content")+"
        • ";i'+(t[i].content||"no content")+"";return a}()+"
        ",success:function(t){var o=t.find(".layui-layer-title").children(),r=t.find(".layui-layer-tabmain").children();o.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var a=i(this),o=a.index();a.addClass(n).siblings().removeClass(n),r.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)}),"function"==typeof a&&a(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||"img";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg("没有图片")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),u.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(h(),0===u.length)return;if(n||p.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),s.imgnext()}),i(document).on("keyup",s.keyup)},s.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:"layui-layer-photos",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(u[d].alt||
        '+(u.length>1?'':"")+'
        '+(u[d].alt||"")+""+s.imgIndex+"/"+u.length+"
        ",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
        是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.$),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window);layui.define("jquery",function(t){"use strict";var a=layui.$,i=(layui.hint(),layui.device()),e="element",l="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(t){var i=this;return a.extend(!0,i.config,t),i},s.prototype.on=function(t,a){return layui.onevent.call(this,e,t,a)},s.prototype.tabAdd=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.children(".layui-tab-bar"),o=l.children(".layui-tab-content"),r='
      • "+(i.title||"unnaming")+"
      • ";return s[0]?s.before(r):n.append(r),o.append('
        '+(i.content||"")+"
        "),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabClick.call(s[0],null,null,s),this},s.prototype.tab=function(t){t=t||{},b.on("click",t.headerElem,function(i){var e=a(this).index();f.tabClick.call(this,i,e,null,t)})},s.prototype.progress=function(t,i){var e="layui-progress",l=a("."+e+"[lay-filter="+t+"]"),n=l.find("."+e+"-bar"),s=n.find("."+e+"-text");return n.css("width",i),s.text(i),this};var o=".layui-nav",r="layui-nav-item",c="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",y="layui-nav-more",h="layui-anim layui-anim-upbit",f={tabClick:function(t,i,s,o){o=o||{};var r=s||a(this),i=i||r.parent().children("li").index(r),c=o.headerElem?r.parent():r.parents(".layui-tab").eq(0),u=o.bodyElem?a(o.bodyElem):c.children(".layui-tab-content").children(".layui-tab-item"),d=r.find("a"),y=c.attr("lay-filter");"javascript:;"!==d.attr("href")&&"_blank"===d.attr("target")||(r.addClass(l).siblings().removeClass(l),u.eq(i).addClass(n).siblings().removeClass(n)),layui.event.call(this,e,"tab("+y+")",{elem:c,index:i})},tabDelete:function(t,i){var n=i||a(this).parent(),s=n.index(),o=n.parents(".layui-tab").eq(0),r=o.children(".layui-tab-content").children(".layui-tab-item"),c=o.attr("lay-filter");n.hasClass(l)&&(n.next()[0]?f.tabClick.call(n.next()[0],null,s+1):n.prev()[0]&&f.tabClick.call(n.prev()[0],null,s-1)),n.remove(),r.eq(s).remove(),setTimeout(function(){f.tabAuto()},50),layui.event.call(this,e,"tabDelete("+c+")",{elem:o,index:s})},tabAuto:function(){var t="layui-tab-more",e="layui-tab-bar",l="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),r=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),c=a('');if(n===window&&8!=i.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var t=a(this);if(!t.find("."+l)[0]){var i=a('');i.on("click",f.tabDelete),t.append(i)}}),"string"!=typeof s.attr("lay-unauto"))if(o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+e)[0])return;o.append(c),s.attr("overflow",""),c.on("click",function(a){o[this.title?"removeClass":"addClass"](t),this.title=this.title?"":"收缩"})}else o.find("."+e).remove(),s.removeAttr("overflow")})},hideTabMore:function(t){var i=a(".layui-tab-title");t!==!0&&"tabmore"===a(t.target).attr("lay-stope")||(i.removeClass("layui-tab-more"),i.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var t=a(this),i=t.parents(o),n=i.attr("lay-filter"),s=t.parent(),c=t.siblings("."+d),y="string"==typeof s.attr("lay-unselect");"javascript:;"!==t.attr("href")&&"_blank"===t.attr("target")||y||c[0]||(i.find("."+l).removeClass(l),s.addClass(l)),i.hasClass(u)&&(c.removeClass(h),c[0]&&(s["none"===c.css("display")?"addClass":"removeClass"](r+"ed"),"all"===i.attr("lay-shrink")&&s.siblings().removeClass(r+"ed"))),layui.event.call(this,e,"nav("+n+")",t)},collapse:function(){var t=a(this),i=t.find(".layui-colla-icon"),l=t.siblings(".layui-colla-content"),s=t.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),r="none"===l.css("display");if("string"==typeof s.attr("lay-accordion")){var c=s.children(".layui-colla-item").children("."+n);c.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),c.removeClass(n)}l[r?"addClass":"removeClass"](n),i.html(r?"":""),layui.event.call(this,e,"collapse("+o+")",{title:t,content:l,show:r})}};s.prototype.init=function(t,e){var l=function(){return e?'[lay-filter="'+e+'"]':""}(),s={tab:function(){f.tabAuto.call({})},nav:function(){var t=200,e={},s={},p={},b=function(l,o,r){var c=a(this),f=c.find("."+d);o.hasClass(u)?l.css({top:c.position().top,height:c.children("a").outerHeight(),opacity:1}):(f.addClass(h),l.css({left:c.position().left+parseFloat(c.css("marginLeft")),top:c.position().top+c.height()-l.height()}),e[r]=setTimeout(function(){l.css({width:c.width(),opacity:1})},i.ie&&i.ie<10?0:t),clearTimeout(p[r]),"block"===f.css("display")&&clearTimeout(s[r]),s[r]=setTimeout(function(){f.addClass(n),c.find("."+y).addClass(y+"d")},300))};a(o+l).each(function(i){var l=a(this),o=a(''),h=l.find("."+r);l.find("."+c)[0]||(l.append(o),h.on("mouseenter",function(){b.call(this,o,l,i)}).on("mouseleave",function(){l.hasClass(u)||(clearTimeout(s[i]),s[i]=setTimeout(function(){l.find("."+d).removeClass(n),l.find("."+y).removeClass(y+"d")},300))}),l.on("mouseleave",function(){clearTimeout(e[i]),p[i]=setTimeout(function(){l.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},t)})),h.find("a").each(function(){var t=a(this),i=(t.parent(),t.siblings("."+d));i[0]&&!t.children("."+y)[0]&&t.append(''),t.off("click",f.clickThis).on("click",f.clickThis)})})},breadcrumb:function(){var t=".layui-breadcrumb";a(t+l).each(function(){var t=a(this),i="lay-separator",e=t.attr(i)||"/",l=t.find("a");l.next("span["+i+"]")[0]||(l.each(function(t){t!==l.length-1&&a(this).after(""+e+"")}),t.css("visibility","visible"))})},progress:function(){var t="layui-progress";a("."+t+l).each(function(){var i=a(this),e=i.find(".layui-progress-bar"),l=e.attr("lay-percent");e.css("width",function(){return/^.+\/.+$/.test(l)?100*new Function("return "+l)()+"%":l}()),i.attr("lay-showPercent")&&setTimeout(function(){e.html(''+l+"")},350)})},collapse:function(){var t="layui-collapse";a("."+t+l).each(function(){var t=a(this).find(".layui-colla-item");t.each(function(){var t=a(this),i=t.find(".layui-colla-title"),e=t.find(".layui-colla-content"),l="none"===e.css("display");i.find(".layui-colla-icon").remove(),i.append(''+(l?"":"")+""),i.off("click",f.collapse).on("click",f.collapse)})})}};return s[t]?s[t]():layui.each(s,function(t,a){a()})},s.prototype.render=s.prototype.init;var p=new s,b=a(document);p.render();var v=".layui-tab-title li";b.on("click",v,f.tabClick),b.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),t(e,p)});layui.define("layer",function(e){"use strict";var i=layui.$,t=layui.layer,n=layui.hint(),a=layui.device(),o={config:{},set:function(e){var t=this;return t.config=i.extend({},t.config,e),t},on:function(e,i){return layui.onevent.call(this,r,e,i)}},l=function(){var e=this;return{upload:function(i){e.upload.call(e,i)},config:e.config}},r="upload",u="layui-upload-file",c="layui-upload-form",f="layui-upload-iframe",s="layui-upload-choose",p=function(e){var t=this;t.config=i.extend({},t.config,o.config,e),t.render()};p.prototype.config={accept:"images",exts:"",auto:!0,bindAction:"",url:"",field:"file",method:"post",data:{},drag:!0,size:0,number:0,multiple:!1},p.prototype.render=function(e){var t=this,e=t.config;e.elem=i(e.elem),e.bindAction=i(e.bindAction),t.file(),t.events()},p.prototype.file=function(){var e=this,t=e.config,n=e.elemFile=i(['"].join("")),o=t.elem.next();(o.hasClass(u)||o.hasClass(c))&&o.remove(),a.ie&&a.ie<10&&t.elem.wrap('
        '),e.isFile()?(e.elemFile=t.elem,t.field=t.elem[0].name):t.elem.after(n),a.ie&&a.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,t=e.config,n=i(''),a=i(['
        ',"
        "].join(""));i("#"+f)[0]||i("body").append(n),t.elem.next().hasClass(c)||(e.elemFile.wrap(a),t.elem.next("."+c).append(function(){var e=[];return layui.each(t.data,function(i,t){t="function"==typeof t?t():t,e.push('')}),e.join("")}()))},p.prototype.msg=function(e){return t.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var i=this;window.FileReader&&layui.each(i.chooseFiles,function(i,t){var n=new FileReader;n.readAsDataURL(t),n.onload=function(){e&&e(i,t,this.result)}})},p.prototype.upload=function(e,t){var n,o=this,l=o.config,r=o.elemFile[0],u=function(){var t=0,n=0,a=e||o.files||o.chooseFiles||r.files,u=function(){l.multiple&&t+n===o.fileLength&&"function"==typeof l.allDone&&l.allDone({total:o.fileLength,successful:t,aborted:n})};layui.each(a,function(e,a){var r=new FormData;r.append(l.field,a),layui.each(l.data,function(e,i){i="function"==typeof i?i():i,r.append(e,i)}),i.ajax({url:l.url,type:"post",data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(i){t++,d(e,i),u()},error:function(){n++,o.msg("请求上传接口出现异常"),m(e),u()}})})},c=function(){var e=i("#"+f);o.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var i,t=e.contents().find("body");try{i=t.text()}catch(n){o.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}i&&(clearInterval(p.timer),t.html(""),d(0,i))},30)},d=function(e,i){if(o.elemFile.next("."+s).remove(),r.value="","object"!=typeof i)try{i=JSON.parse(i)}catch(t){return i={},o.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(i,e||0,function(e){o.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){o.upload(e)})},h=l.exts,v=function(){var i=[];return layui.each(e||o.chooseFiles,function(e,t){i.push(t.name)}),i}(),g={preview:function(e){o.preview(e)},upload:function(e,i){var t={};t[e]=i,o.upload(t)},pushFile:function(){return o.files=o.files||{},layui.each(o.chooseFiles,function(e,i){o.files[e]=i}),o.files},resetFile:function(e,i,t){var n=new File([i],t);o.files=o.files||{},o.files[e]=n}},y=function(){if("choose"!==t&&!l.auto||(l.choose&&l.choose(g),"choose"!==t))return l.before&&l.before(g),a.ie?a.ie>9?u():c():void u()};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return o.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return o.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return o.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,i){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(i))||(n=!0)}),n)return o.msg("选择的图片中包含不支持的格式"),r.value=""}if(o.fileLength=function(){var i=0,t=e||o.files||o.chooseFiles||r.files;return layui.each(t,function(){i++}),i}(),l.number&&o.fileLength>l.number)return o.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(a.ie&&a.ie<10)){var F;if(layui.each(o.chooseFiles,function(e,i){if(i.size>1024*l.size){var t=l.size/1024;t=t>=1?t.toFixed(2)+"MB":l.size+"KB",r.value="",F=t}}),F)return o.msg("文件不能超过"+F)}y()}},p.prototype.events=function(){var e=this,t=e.config,o=function(i){e.chooseFiles={},layui.each(i,function(i,t){var n=(new Date).getTime();e.chooseFiles[n+"-"+i]=t})},l=function(i,n){var a=e.elemFile,o=i.length>1?i.length+"个文件":(i[0]||{}).name||a[0].value.match(/[^\/\\]+\..+/g)||[]||"";a.next().hasClass(s)&&a.next().remove(),e.upload(null,"choose"),e.isFile()||t.choose||a.after(''+o+"")};t.elem.off("upload.start").on("upload.start",function(){var a=i(this),o=a.attr("lay-data");if(o)try{o=new Function("return "+o)(),e.config=i.extend({},t,o)}catch(l){n.error("Upload element property lay-data configuration item has a syntax error: "+o)}e.config.item=a,e.elemFile[0].click()}),a.ie&&a.ie<10||t.elem.off("upload.over").on("upload.over",function(){var e=i(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=i(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,a){var r=i(this),u=a.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),o(u),t.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var i=this.files||[];o(i),t.auto?e.upload():l(i)}),t.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),t.elem.data("haveEvents")||(e.elemFile.on("change",function(){i(this).trigger("upload.change")}),t.elem.on("click",function(){e.isFile()||i(this).trigger("upload.start")}),t.drag&&t.elem.on("dragover",function(e){e.preventDefault(),i(this).trigger("upload.over")}).on("dragleave",function(e){i(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),i(this).trigger("upload.drop",e)}),t.bindAction.on("click",function(){i(this).trigger("upload.action")}),t.elem.data("haveEvents",!0))},o.render=function(e){var i=new p(e);return l.call(i)},e(r,o)});layui.define("jquery",function(e){"use strict";var i=layui.jquery,t={config:{},index:layui.slider?layui.slider.index+1e4:0,set:function(e){var t=this;return t.config=i.extend({},t.config,e),t},on:function(e,i){return layui.onevent.call(this,n,e,i)}},a=function(){var e=this,i=e.config;return{setValue:function(i,t){return e.slide("set",i,t||0)},config:i}},n="slider",l="layui-disabled",s="layui-slider",r="layui-slider-bar",o="layui-slider-wrap",u="layui-slider-wrap-btn",d="layui-slider-tips",v="layui-slider-input",c="layui-slider-input-txt",m="layui-slider-input-btn",p="layui-slider-hover",f=function(e){var a=this;a.index=++t.index,a.config=i.extend({},a.config,t.config,e),a.render()};f.prototype.config={type:"default",min:0,max:100,value:0,step:1,showstep:!1,tips:!0,input:!1,range:!1,height:200,disabled:!1,theme:"#009688"},f.prototype.render=function(){var e=this,t=e.config;if(t.step<1&&(t.step=1),t.maxt.min?a:t.min,t.value[1]=n>t.min?n:t.min,t.value[0]=t.value[0]>t.max?t.max:t.value[0],t.value[1]=t.value[1]>t.max?t.max:t.value[1];var r=Math.floor((t.value[0]-t.min)/(t.max-t.min)*100),v=Math.floor((t.value[1]-t.min)/(t.max-t.min)*100),m=v-r+"%";r+="%",v+="%"}else{"object"==typeof t.value&&(t.value=Math.min.apply(null,t.value)),t.valuet.max&&(t.value=t.max);var m=Math.floor((t.value-t.min)/(t.max-t.min)*100)+"%"}var p=t.disabled?"#c2c2c2":t.theme,f='
        '+(t.tips?'
        ':"")+'
        '+(t.range?'
        ':"")+"
        ",h=i(t.elem),y=h.next("."+s);if(y[0]&&y.remove(),e.elemTemp=i(f),t.range?(e.elemTemp.find("."+o).eq(0).data("value",t.value[0]),e.elemTemp.find("."+o).eq(1).data("value",t.value[1])):e.elemTemp.find("."+o).data("value",t.value),h.html(e.elemTemp),"vertical"===t.type&&e.elemTemp.height(t.height+"px"),t.showstep){for(var g=(t.max-t.min)/t.step,b="",x=1;x
        ')}e.elemTemp.append(b)}if(t.input&&!t.range){var w=i('
        ');h.css("position","relative"),h.append(w),h.find("."+c).children("input").val(t.value),"vertical"===t.type?w.css({left:0,top:-48}):e.elemTemp.css("margin-right",w.outerWidth()+15)}t.disabled?(e.elemTemp.addClass(l),e.elemTemp.find("."+u).addClass(l)):e.slide(),e.elemTemp.find("."+u).on("mouseover",function(){var a="vertical"===t.type?t.height:e.elemTemp[0].offsetWidth,n=e.elemTemp.find("."+o),l="vertical"===t.type?a-i(this).parent()[0].offsetTop-n.height():i(this).parent()[0].offsetLeft,s=l/a*100,r=i(this).parent().data("value"),u=t.setTips?t.setTips(r):r;e.elemTemp.find("."+d).html(u),"vertical"===t.type?e.elemTemp.find("."+d).css({bottom:s+"%","margin-bottom":"20px",display:"inline-block"}):e.elemTemp.find("."+d).css({left:s+"%",display:"inline-block"})}).on("mouseout",function(){e.elemTemp.find("."+d).css("display","none")})},f.prototype.slide=function(e,t,a){var n=this,l=n.config,s=n.elemTemp,f=function(){return"vertical"===l.type?l.height:s[0].offsetWidth},h=s.find("."+o),y=s.next("."+v),g=y.children("."+c).children("input").val(),b=100/((l.max-l.min)/Math.ceil(l.step)),x=function(e,i){e=Math.ceil(e)*b>100?Math.ceil(e)*b:Math.round(e)*b,e=e>100?100:e,h.eq(i).css("vertical"===l.type?"bottom":"left",e+"%");var t=T(h[0].offsetLeft),a=l.range?T(h[1].offsetLeft):0;"vertical"===l.type?(s.find("."+d).css({bottom:e+"%","margin-bottom":"20px"}),t=T(f()-h[0].offsetTop-h.height()),a=l.range?T(f()-h[1].offsetTop-h.height()):0):s.find("."+d).css("left",e+"%"),t=t>100?100:t,a=a>100?100:a;var n=Math.min(t,a),o=Math.abs(t-a);"vertical"===l.type?s.find("."+r).css({height:o+"%",bottom:n+"%"}):s.find("."+r).css({width:o+"%",left:n+"%"});var u=l.min+Math.round((l.max-l.min)*e/100);if(g=u,y.children("."+c).children("input").val(g),h.eq(i).data("value",u),u=l.setTips?l.setTips(u):u,s.find("."+d).html(u),l.range){var v=[h.eq(0).data("value"),h.eq(1).data("value")];v[0]>v[1]&&v.reverse()}l.change&&l.change(l.range?v:u)},T=function(e){var i=e/f()*100/b,t=Math.round(i)*b;return e==f()&&(t=Math.ceil(i)*b),t},w=i(['
        f()&&(r=f());var o=r/f()*100/b;x(o,e),t.addClass(p),s.find("."+d).show(),i.preventDefault()},o=function(){t.removeClass(p),s.find("."+d).hide()};M(r,o)})}),s.on("click",function(e){var t=i("."+u);if(!t.is(event.target)&&0===t.has(event.target).length&&t.length){var a,n="vertical"===l.type?f()-e.clientY+i(this).offset().top:e.clientX-i(this).offset().left;n<0&&(n=0),n>f()&&(n=f());var s=n/f()*100/b;a=l.range?"vertical"===l.type?Math.abs(n-parseInt(i(h[0]).css("bottom")))>Math.abs(n-parseInt(i(h[1]).css("bottom")))?1:0:Math.abs(n-h[0].offsetLeft)>Math.abs(n-h[1].offsetLeft)?1:0:0,x(s,a),e.preventDefault()}}),y.hover(function(){var e=i(this);e.children("."+m).fadeIn("fast")},function(){var e=i(this);e.children("."+m).fadeOut("fast")}),y.children("."+m).children("i").each(function(e){i(this).on("click",function(){g=1==e?g-l.stepl.max?l.max:Number(g)+l.step;var i=(g-l.min)/(l.max-l.min)*100/b;x(i,0)})});var q=function(){var e=this.value;e=isNaN(e)?0:e,e=el.max?l.max:e,this.value=e;var i=(e-l.min)/(l.max-l.min)*100/b;x(i,0)};y.children("."+c).children("input").on("keydown",function(e){13===e.keyCode&&(e.preventDefault(),q.call(this))}).on("change",q)},f.prototype.events=function(){var e=this;e.config},t.render=function(e){var i=new f(e);return a.call(i)},e(n,t)});layui.define("jquery",function(e){"use strict";var i=layui.jquery,o={config:{},index:layui.colorpicker?layui.colorpicker.index+1e4:0,set:function(e){var o=this;return o.config=i.extend({},o.config,e),o},on:function(e,i){return layui.onevent.call(this,"colorpicker",e,i)}},r=function(){var e=this,i=e.config;return{config:i}},t="colorpicker",n="layui-show",l="layui-colorpicker",c=".layui-colorpicker-main",a="layui-icon-down",s="layui-icon-close",f="layui-colorpicker-trigger-span",d="layui-colorpicker-trigger-i",u="layui-colorpicker-side",p="layui-colorpicker-side-slider",g="layui-colorpicker-basis",v="layui-colorpicker-alpha-bgcolor",h="layui-colorpicker-alpha-slider",m="layui-colorpicker-basis-cursor",b="layui-colorpicker-main-input",k=function(e){var i={h:0,s:0,b:0},o=Math.min(e.r,e.g,e.b),r=Math.max(e.r,e.g,e.b),t=r-o;return i.b=r,i.s=0!=r?255*t/r:0,0!=i.s?e.r==r?i.h=(e.g-e.b)/t:e.g==r?i.h=2+(e.b-e.r)/t:i.h=4+(e.r-e.g)/t:i.h=-1,r==o&&(i.h=0),i.h*=60,i.h<0&&(i.h+=360),i.s*=100/255,i.b*=100/255,i},y=function(e){var e=e.indexOf("#")>-1?e.substring(1):e;if(3==e.length){var i=e.split("");e=i[0]+i[0]+i[1]+i[1]+i[2]+i[2]}e=parseInt(e,16);var o={r:e>>16,g:(65280&e)>>8,b:255&e};return k(o)},x=function(e){var i={},o=e.h,r=255*e.s/100,t=255*e.b/100;if(0==r)i.r=i.g=i.b=t;else{var n=t,l=(255-r)*t/255,c=(n-l)*(o%60)/60;360==o&&(o=0),o<60?(i.r=n,i.b=l,i.g=l+c):o<120?(i.g=n,i.b=l,i.r=n-c):o<180?(i.g=n,i.r=l,i.b=l+c):o<240?(i.b=n,i.r=l,i.g=n-c):o<300?(i.b=n,i.g=l,i.r=l+c):o<360?(i.r=n,i.g=l,i.b=n-c):(i.r=0,i.g=0,i.b=0)}return{r:Math.round(i.r),g:Math.round(i.g),b:Math.round(i.b)}},C=function(e){var o=x(e),r=[o.r.toString(16),o.g.toString(16),o.b.toString(16)];return i.each(r,function(e,i){1==i.length&&(r[e]="0"+i)}),r.join("")},P=function(e){var i=/[0-9]{1,3}/g,o=e.match(i)||[];return{r:o[0],g:o[1],b:o[2]}},B=i(window),w=i(document),D=function(e){var r=this;r.index=++o.index,r.config=i.extend({},r.config,o.config,e),r.render()};D.prototype.config={color:"",size:null,alpha:!1,format:"hex",predefine:!1,colors:["#009688","#5FB878","#1E9FFF","#FF5722","#FFB800","#01AAED","#999","#c00","#ff8c00","#ffd700","#90ee90","#00ced1","#1e90ff","#c71585","rgb(0, 186, 189)","rgb(255, 120, 0)","rgb(250, 212, 0)","#393D49","rgba(0,0,0,.5)","rgba(255, 69, 0, 0.68)","rgba(144, 240, 144, 0.5)","rgba(31, 147, 255, 0.73)"]},D.prototype.render=function(){var e=this,o=e.config,r=i(['
        ',"",'3&&(o.alpha&&"rgb"==o.format||(e="#"+C(k(P(o.color))))),"background: "+e):e}()+'">','',"","","
        "].join("")),t=i(o.elem);o.size&&r.addClass("layui-colorpicker-"+o.size),t.addClass("layui-inline").html(e.elemColorBox=r),e.color=e.elemColorBox.find("."+f)[0].style.background,e.events()},D.prototype.renderPicker=function(){var e=this,o=e.config,r=e.elemColorBox[0],t=e.elemPicker=i(['
        ','
        ','
        ','
        ','
        ','
        ',"
        ",'
        ','
        ',"
        ","
        ",'
        ','
        ','
        ',"
        ","
        ",function(){if(o.predefine){var e=['
        '];return layui.each(o.colors,function(i,o){e.push(['
        ','
        ',"
        "].join(""))}),e.push("
        "),e.join("")}return""}(),'
        ','
        ','',"
        ",'
        ','','',"","
        "].join(""));e.elemColorBox.find("."+f)[0];i(c)[0]&&i(c).data("index")==e.index?e.removePicker(D.thisElemInd):(e.removePicker(D.thisElemInd),i("body").append(t)),D.thisElemInd=e.index,D.thisColor=r.style.background,e.position(),e.pickerEvents()},D.prototype.removePicker=function(e){var o=this;o.config;return i("#layui-colorpicker"+(e||o.index)).remove(),o},D.prototype.position=function(){var e=this,i=e.config,o=e.bindElem||e.elemColorBox[0],r=e.elemPicker[0],t=o.getBoundingClientRect(),n=r.offsetWidth,l=r.offsetHeight,c=function(e){return e=e?"scrollLeft":"scrollTop",document.body[e]|document.documentElement[e]},a=function(e){return document.documentElement[e?"clientWidth":"clientHeight"]},s=5,f=t.left,d=t.bottom;f-=(n-o.offsetWidth)/2,d+=s,f+n+s>a("width")?f=a("width")-n-s:fa()&&(d=t.top>l?t.top-l:a()-l,d-=2*s),i.position&&(r.style.position=i.position),r.style.left=f+("fixed"===i.position?0:c(1))+"px",r.style.top=d+("fixed"===i.position?0:c())+"px"},D.prototype.val=function(){var e=this,i=(e.config,e.elemColorBox.find("."+f)),o=e.elemPicker.find("."+b),r=i[0],t=r.style.backgroundColor;if(t){var n=k(P(t)),l=i.attr("lay-type");if(e.select(n.h,n.s,n.b),"torgb"===l&&o.find("input").val(t),"rgba"===l){var c=P(t);if(3==(t.match(/[0-9]{1,3}/g)||[]).length)o.find("input").val("rgba("+c.r+", "+c.g+", "+c.b+", 1)"),e.elemPicker.find("."+h).css("left",280);else{o.find("input").val(t);var a=280*t.slice(t.lastIndexOf(",")+1,t.length-1);e.elemPicker.find("."+h).css("left",a)}e.elemPicker.find("."+v)[0].style.background="linear-gradient(to right, rgba("+c.r+", "+c.g+", "+c.b+", 0), rgb("+c.r+", "+c.g+", "+c.b+"))"}}else e.select(0,100,100),o.find("input").val(""),e.elemPicker.find("."+v)[0].style.background="",e.elemPicker.find("."+h).css("left",280)},D.prototype.side=function(){var e=this,o=e.config,r=e.elemColorBox.find("."+f),t=r.attr("lay-type"),n=e.elemPicker.find("."+u),l=e.elemPicker.find("."+p),c=e.elemPicker.find("."+g),y=e.elemPicker.find("."+m),C=e.elemPicker.find("."+v),w=e.elemPicker.find("."+h),D=l[0].offsetTop/180*360,E=100-(y[0].offsetTop+3)/180*100,H=(y[0].offsetLeft+3)/260*100,W=Math.round(w[0].offsetLeft/280*100)/100,j=e.elemColorBox.find("."+d),F=e.elemPicker.find(".layui-colorpicker-pre").children("div"),L=function(i,n,l,c){e.select(i,n,l);var f=x({h:i,s:n,b:l});if(j.addClass(a).removeClass(s),r[0].style.background="rgb("+f.r+", "+f.g+", "+f.b+")","torgb"===t&&e.elemPicker.find("."+b).find("input").val("rgb("+f.r+", "+f.g+", "+f.b+")"),"rgba"===t){var d=0;d=280*c,w.css("left",d),e.elemPicker.find("."+b).find("input").val("rgba("+f.r+", "+f.g+", "+f.b+", "+c+")"),r[0].style.background="rgba("+f.r+", "+f.g+", "+f.b+", "+c+")",C[0].style.background="linear-gradient(to right, rgba("+f.r+", "+f.g+", "+f.b+", 0), rgb("+f.r+", "+f.g+", "+f.b+"))"}o.change&&o.change(e.elemPicker.find("."+b).find("input").val())},M=i(['
        t&&(r=t);var l=r/180*360;D=l,L(l,H,E,W),e.preventDefault()};Y(r),e.preventDefault()}),n.on("click",function(e){var o=e.clientY-i(this).offset().top;o<0&&(o=0),o>this.offsetHeight&&(o=this.offsetHeight);var r=o/180*360;D=r,L(r,H,E,W),e.preventDefault()}),y.on("mousedown",function(e){var i=this.offsetTop,o=this.offsetLeft,r=e.clientY,t=e.clientX,n=function(e){var n=i+(e.clientY-r),l=o+(e.clientX-t),a=c[0].offsetHeight-3,s=c[0].offsetWidth-3;n<-3&&(n=-3),n>a&&(n=a),l<-3&&(l=-3),l>s&&(l=s);var f=(l+3)/260*100,d=100-(n+3)/180*100;E=d,H=f,L(D,f,d,W),e.preventDefault()};layui.stope(e),Y(n),e.preventDefault()}),c.on("mousedown",function(e){var o=e.clientY-i(this).offset().top-3+B.scrollTop(),r=e.clientX-i(this).offset().left-3+B.scrollLeft();o<-3&&(o=-3),o>this.offsetHeight-3&&(o=this.offsetHeight-3),r<-3&&(r=-3),r>this.offsetWidth-3&&(r=this.offsetWidth-3);var t=(r+3)/260*100,n=100-(o+3)/180*100;E=n,H=t,L(D,t,n,W),e.preventDefault(),y.trigger(e,"mousedown")}),w.on("mousedown",function(e){var i=this.offsetLeft,o=e.clientX,r=function(e){var r=i+(e.clientX-o),t=C[0].offsetWidth;r<0&&(r=0),r>t&&(r=t);var n=Math.round(r/280*100)/100;W=n,L(D,H,E,n),e.preventDefault()};Y(r),e.preventDefault()}),C.on("click",function(e){var o=e.clientX-i(this).offset().left;o<0&&(o=0),o>this.offsetWidth&&(o=this.offsetWidth);var r=Math.round(o/280*100)/100;W=r,L(D,H,E,r),e.preventDefault()}),F.each(function(){i(this).on("click",function(){i(this).parent(".layui-colorpicker-pre").addClass("selected").siblings().removeClass("selected");var e,o=this.style.backgroundColor,r=k(P(o)),t=o.slice(o.lastIndexOf(",")+1,o.length-1);D=r.h,H=r.s,E=r.b,3==(o.match(/[0-9]{1,3}/g)||[]).length&&(t=1),W=t,e=280*t,L(r.h,r.s,r.b,t)})})},D.prototype.select=function(e,i,o,r){var t=this,n=(t.config,C({h:e,s:100,b:100})),l=C({h:e,s:i,b:o}),c=e/360*180,a=180-o/100*180-3,s=i/100*260-3;t.elemPicker.find("."+p).css("top",c),t.elemPicker.find("."+g)[0].style.background="#"+n,t.elemPicker.find("."+m).css({top:a,left:s}),"change"!==r&&t.elemPicker.find("."+b).find("input").val("#"+l)},D.prototype.pickerEvents=function(){var e=this,o=e.config,r=e.elemColorBox.find("."+f),t=e.elemPicker.find("."+b+" input"),n={clear:function(i){r[0].style.background="",e.elemColorBox.find("."+d).removeClass(a).addClass(s),e.color="",o.done&&o.done(""),e.removePicker()},confirm:function(i,n){var l=t.val(),c=l,f={};if(l.indexOf(",")>-1){if(f=k(P(l)),e.select(f.h,f.s,f.b),r[0].style.background=c="#"+C(f),(l.match(/[0-9]{1,3}/g)||[]).length>3&&"rgba"===r.attr("lay-type")){var u=280*l.slice(l.lastIndexOf(",")+1,l.length-1);e.elemPicker.find("."+h).css("left",u),r[0].style.background=l,c=l}}else f=y(l),r[0].style.background=c="#"+C(f),e.elemColorBox.find("."+d).removeClass(s).addClass(a);return"change"===n?(e.select(f.h,f.s,f.b,n),void(o.change&&o.change(c))):(e.color=l,o.done&&o.done(l),void e.removePicker())}};e.elemPicker.on("click","*[colorpicker-events]",function(){var e=i(this),o=e.attr("colorpicker-events");n[o]&&n[o].call(this,e)}),t.on("keyup",function(e){var o=i(this);n.confirm.call(this,o,13===e.keyCode?null:"change")})},D.prototype.events=function(){var e=this,o=e.config,r=e.elemColorBox.find("."+f);e.elemColorBox.on("click",function(){e.renderPicker(),i(c)[0]&&(e.val(),e.side())}),o.elem[0]&&!e.elemColorBox[0].eventHandler&&(w.on("click",function(o){if(!i(o.target).hasClass(l)&&!i(o.target).parents("."+l)[0]&&!i(o.target).hasClass(c.replace(/\./g,""))&&!i(o.target).parents(c)[0]&&e.elemPicker){if(e.color){var t=k(P(e.color));e.select(t.h,t.s,t.b)}else e.elemColorBox.find("."+d).removeClass(a).addClass(s);r[0].style.background=e.color||"",e.removePicker()}}),B.on("resize",function(){return!(!e.elemPicker||!i(c)[0])&&void e.position()}),e.elemColorBox[0].eventHandler=!0)},o.render=function(e){var i=new D(e);return r.call(i)},e(t,o)});layui.define("layer",function(e){"use strict";var t=layui.$,i=layui.layer,a=layui.hint(),n=layui.device(),l="form",r=".layui-form",s="layui-this",o="layui-hide",c="layui-disabled",u=function(){this.config={verify:{required:[/[\S]+/,"必填项不能为空"],phone:[/^1\d{10}$/,"请输入正确的手机号"],email:[/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,"邮箱格式不正确"],url:[/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,"链接格式不正确"],number:function(e){if(!e||isNaN(e))return"只能填写数字"},date:[/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,"日期格式不正确"],identity:[/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,"请输入正确的身份证号"]}}};u.prototype.set=function(e){var i=this;return t.extend(!0,i.config,e),i},u.prototype.verify=function(e){var i=this;return t.extend(!0,i.config.verify,e),i},u.prototype.on=function(e,t){return layui.onevent.call(this,l,e,t)},u.prototype.val=function(e,i){var a=t(r+'[lay-filter="'+e+'"]');a.each(function(e,a){var n=t(this);layui.each(i,function(e,t){var i,a=n.find('[name="'+e+'"]');a[0]&&(i=a[0].type,"checkbox"===i?a[0].checked=t:"radio"===i?a.each(function(){this.value===t&&(this.checked=!0)}):a.val(t))})}),f.render(null,e)},u.prototype.render=function(e,i){var n=this,u=t(r+function(){return i?'[lay-filter="'+i+'"]':""}()),d={select:function(){var e,i="请选择",a="layui-form-select",n="layui-select-title",r="layui-select-none",d="",f=u.find("select"),v=function(i,l){t(i.target).parent().hasClass(n)&&!l||(t("."+a).removeClass(a+"ed "+a+"up"),e&&d&&e.val(d)),e=null},y=function(i,u,f){var y,p=t(this),m=i.find("."+n),k=m.find("input"),x=i.find("dl"),g=x.children("dd"),b=this.selectedIndex;if(!u){var C=function(){var e=i.offset().top+i.outerHeight()+5-h.scrollTop(),t=x.outerHeight();b=p[0].selectedIndex,i.addClass(a+"ed"),g.removeClass(o),y=null,g.eq(b).addClass(s).siblings().removeClass(s),e+t>h.height()&&e>=t&&i.addClass(a+"up"),$()},w=function(e){i.removeClass(a+"ed "+a+"up"),k.blur(),y=null,e||T(k.val(),function(e){var i=p[0].selectedIndex;e&&(d=t(p[0].options[i]).html(),0===i&&d===k.attr("placeholder")&&(d=""),k.val(d||""))})},$=function(){var e=x.children("dd."+s);if(e[0]){var t=e.position().top,i=x.height(),a=e.height();t>i&&x.scrollTop(t+x.scrollTop()-i+a-5),t<0&&x.scrollTop(t+x.scrollTop()-5)}};m.on("click",function(e){i.hasClass(a+"ed")?w():(v(e,!0),C()),x.find("."+r).remove()}),m.find(".layui-edge").on("click",function(){k.focus()}),k.on("keyup",function(e){var t=e.keyCode;9===t&&C()}).on("keydown",function(e){var t=e.keyCode;9===t&&w();var i=function(t,a){var n,l;e.preventDefault();var r=function(){var e=x.children("dd."+s);if(x.children("dd."+o)[0]&&"next"===t){var i=x.children("dd:not(."+o+",."+c+")"),n=i.eq(0).index();if(n>=0&&n无匹配项

        '):x.find("."+r).remove()},"keyup"),""===t&&x.find("."+r).remove(),void $())};f&&k.on("keyup",j).on("blur",function(i){var a=p[0].selectedIndex;e=k,d=t(p[0].options[a]).html(),0===a&&d===k.attr("placeholder")&&(d=""),setTimeout(function(){T(k.val(),function(e){d||k.val("")},"blur")},200)}),g.on("click",function(){var e=t(this),a=e.attr("lay-value"),n=p.attr("lay-filter");return!e.hasClass(c)&&(e.hasClass("layui-select-tips")?k.val(""):(k.val(e.text()),e.addClass(s)),e.siblings().removeClass(s),p.val(a).removeClass("layui-form-danger"),layui.event.call(this,l,"select("+n+")",{elem:p[0],value:a,othis:i}),w(!0),!1)}),i.find("dl>dt").on("click",function(e){return!1}),t(document).off("click",v).on("click",v)}};f.each(function(e,l){var r=t(this),o=r.next("."+a),u=this.disabled,d=l.value,f=t(l.options[l.selectedIndex]),v=l.options[0];if("string"==typeof r.attr("lay-ignore"))return r.show();var h="string"==typeof r.attr("lay-search"),p=v?v.value?i:v.innerHTML||i:i,m=t(['
        ','
        ','','
        ','
        ',function(e){var t=[];return layui.each(e,function(e,a){0!==e||a.value?"optgroup"===a.tagName.toLowerCase()?t.push("
        "+a.label+"
        "):t.push('
        '+a.innerHTML+"
        "):t.push('
        '+(a.innerHTML||i)+"
        ")}),0===t.length&&t.push('
        没有选项
        '),t.join("")}(r.find("*"))+"
        ","
        "].join(""));o[0]&&o.remove(),r.after(m),y.call(this,m,u,h)})},checkbox:function(){var e={checkbox:["layui-form-checkbox","layui-form-checked","checkbox"],_switch:["layui-form-switch","layui-form-onswitch","switch"]},i=u.find("input[type=checkbox]"),a=function(e,i){var a=t(this);e.on("click",function(){var t=a.attr("lay-filter"),n=(a.attr("lay-text")||"").split("|");a[0].disabled||(a[0].checked?(a[0].checked=!1,e.removeClass(i[1]).find("em").text(n[1])):(a[0].checked=!0,e.addClass(i[1]).find("em").text(n[0])),layui.event.call(a[0],l,i[2]+"("+t+")",{elem:a[0],value:a[0].value,othis:e}))})};i.each(function(i,n){var l=t(this),r=l.attr("lay-skin"),s=(l.attr("lay-text")||"").split("|"),o=this.disabled;"switch"===r&&(r="_"+r);var u=e[r]||e.checkbox;if("string"==typeof l.attr("lay-ignore"))return l.show();var d=l.next("."+u[0]),f=t(['
        ",function(){var e=n.title.replace(/\s/g,""),t={checkbox:[e?""+n.title+"":"",''].join(""),_switch:""+((n.checked?s[0]:s[1])||"")+""};return t[r]||t.checkbox}(),"
        "].join(""));d[0]&&d.remove(),l.after(f),a.call(this,f,u)})},radio:function(){var e="layui-form-radio",i=["",""],a=u.find("input[type=radio]"),n=function(a){var n=t(this),s="layui-anim-scaleSpring";a.on("click",function(){var o=n[0].name,c=n.parents(r),u=n.attr("lay-filter"),d=c.find("input[name="+o.replace(/(\.|#|\[|\])/g,"\\$1")+"]");n[0].disabled||(layui.each(d,function(){var a=t(this).next("."+e);this.checked=!1,a.removeClass(e+"ed"),a.find(".layui-icon").removeClass(s).html(i[1])}),n[0].checked=!0,a.addClass(e+"ed"),a.find(".layui-icon").addClass(s).html(i[0]),layui.event.call(n[0],l,"radio("+u+")",{elem:n[0],value:n[0].value,othis:a}))})};a.each(function(a,l){var r=t(this),s=r.next("."+e),o=this.disabled;if("string"==typeof r.attr("lay-ignore"))return r.show();s[0]&&s.remove();var u=t(['
        ',''+i[l.checked?0:1]+"","
        "+function(){var e=l.title||"";return"string"==typeof r.next().attr("lay-radio")&&(e=r.next().html(),r.next().remove()),e}()+"
        ","
        "].join(""));r.after(u),n.call(this,u)})}};return e?d[e]?d[e]():a.error("不支持的"+e+"表单渲染"):layui.each(d,function(e,t){t()}),n};var d=function(){var e=t(this),a=f.config.verify,s=null,o="layui-form-danger",c={},u=e.parents(r),d=u.find("*[lay-verify]"),v=e.parents("form")[0],h=u.find("input,select,textarea"),y=e.attr("lay-filter");if(layui.each(d,function(e,l){var r=t(this),c=r.attr("lay-verify").split("|"),u=r.attr("lay-verType"),d=r.val();if(r.removeClass(o),layui.each(c,function(e,t){var c,f="",v="function"==typeof a[t];if(a[t]){var c=v?f=a[t](d,l):!a[t][0].test(d);if(f=f||a[t][1],c)return"tips"===u?i.tips(f,function(){return"string"==typeof r.attr("lay-ignore")||"select"!==l.tagName.toLowerCase()&&!/^checkbox|radio$/.test(l.type)?r:r.next()}(),{tips:1}):"alert"===u?i.alert(f,{title:"提示",shadeClose:!0}):i.msg(f,{icon:5,shift:6}),n.android||n.ios||l.focus(),r.addClass(o),s=!0}}),s)return s}),s)return!1;var p={};return layui.each(h,function(e,t){if(t.name=(t.name||"").replace(/^\s*|\s*&/,""),t.name){if(/^.*\[\]$/.test(t.name)){var i=t.name.match(/^(.*)\[\]$/g)[0];p[i]=0|p[i],t.name=t.name.replace(/^(.*)\[\]$/,"$1["+p[i]++ +"]")}/^checkbox|radio$/.test(t.type)&&!t.checked||(c[t.name]=t.value)}}),layui.event.call(this,l,"submit("+y+")",{elem:this,form:v,field:c})},f=new u,v=t(document),h=t(window);f.render(),v.on("reset",r,function(){var e=t(this).attr("lay-filter");setTimeout(function(){f.render(null,e)},50)}),v.on("submit",r,d).on("click","*[lay-submit]",d),e(l,f)});layui.define("jquery",function(e){"use strict";var o=layui.$,a=layui.hint(),i="layui-tree-enter",r=function(e){this.options=e},t={arrow:["",""],checkbox:["",""],radio:["",""],branch:["",""],leaf:""};r.prototype.init=function(e){var o=this;e.addClass("layui-box layui-tree"),o.options.skin&&e.addClass("layui-tree-skin-"+o.options.skin),o.tree(e),o.on(e)},r.prototype.tree=function(e,a){var i=this,r=i.options,n=a||r.nodes;layui.each(n,function(a,n){var l=n.children&&n.children.length>0,c=o('
          '),s=o(["
        • ",function(){return l?''+(n.spread?t.arrow[1]:t.arrow[0])+"":""}(),function(){return r.check?''+("checkbox"===r.check?t.checkbox[0]:"radio"===r.check?t.radio[0]:"")+"":""}(),function(){return'"+(''+(l?n.spread?t.branch[1]:t.branch[0]:t.leaf)+"")+(""+(n.name||"未命名")+"")}(),"
        • "].join(""));l&&(s.append(c),i.tree(c,n.children)),e.append(s),"function"==typeof r.click&&i.click(s,n),i.spread(s,n),r.drag&&i.drag(s,n)})},r.prototype.click=function(e,o){var a=this,i=a.options;e.children("a").on("click",function(e){layui.stope(e),i.click(o)})},r.prototype.spread=function(e,o){var a=this,i=(a.options,e.children(".layui-tree-spread")),r=e.children("ul"),n=e.children("a"),l=function(){e.data("spread")?(e.data("spread",null),r.removeClass("layui-show"),i.html(t.arrow[0]),n.find(".layui-icon").html(t.branch[0])):(e.data("spread",!0),r.addClass("layui-show"),i.html(t.arrow[1]),n.find(".layui-icon").html(t.branch[1]))};r[0]&&(i.on("click",l),n.on("dblclick",l))},r.prototype.on=function(e){var a=this,r=a.options,t="layui-tree-drag";e.find("i").on("selectstart",function(e){return!1}),r.drag&&o(document).on("mousemove",function(e){var i=a.move;if(i.from){var r=(i.to,o('
          '));e.preventDefault(),o("."+t)[0]||o("body").append(r);var n=o("."+t)[0]?o("."+t):r;n.addClass("layui-show").html(i.from.elem.children("a").html()),n.css({left:e.pageX+10,top:e.pageY+10})}}).on("mouseup",function(){var e=a.move;e.from&&(e.from.elem.children("a").removeClass(i),e.to&&e.to.elem.children("a").removeClass(i),a.move={},o("."+t).remove())})},r.prototype.move={},r.prototype.drag=function(e,a){var r=this,t=(r.options,e.children("a")),n=function(){var t=o(this),n=r.move;n.from&&(n.to={item:a,elem:e},t.addClass(i))};t.on("mousedown",function(){var o=r.move;o.from={item:a,elem:e}}),t.on("mouseenter",n).on("mousemove",n).on("mouseleave",function(){var e=o(this),a=r.move;a.from&&(delete a.to,e.removeClass(i))})},e("tree",function(e){var i=new r(e=e||{}),t=o(e.elem);return t[0]?void i.init(t):a.error("layui.tree 没有找到"+e.elem+"元素")})});layui.define(["laytpl","laypage","layer","form","util"],function(e){"use strict";var t=layui.$,i=layui.laytpl,a=layui.laypage,l=layui.layer,n=layui.form,o=(layui.util,layui.hint()),r=layui.device(),d={config:{checkName:"LAY_CHECKED",indexName:"LAY_TABLE_INDEX"},cache:{},index:layui.table?layui.table.index+1e4:0,set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,u,e,t)}},c=function(){var e=this,t=e.config,i=t.id||t.index;return i&&(c.that[i]=e,c.config[i]=t),{reload:function(t){e.reload.call(e,t)},setColsWidth:function(){e.setColsWidth.call(e)},resize:function(){e.resize.call(e)},config:t}},s=function(e){var t=c.config[e];return t||o.error("The ID option was not found in the table instance"),t||null},u="table",h=".layui-table",y="layui-hide",f="layui-none",p="layui-table-view",v=".layui-table-tool",m=".layui-table-box",g=".layui-table-init",b=".layui-table-header",x=".layui-table-body",k=".layui-table-main",C=".layui-table-fixed",w=".layui-table-fixed-l",T=".layui-table-fixed-r",A=".layui-table-total",L=".layui-table-page",S=".layui-table-sort",N="layui-table-edit",W="layui-table-hover",_=function(e){var t='{{#if(item2.colspan){}} colspan="{{item2.colspan}}"{{#} if(item2.rowspan){}} rowspan="{{item2.rowspan}}"{{#}}}';return e=e||{},['',"","{{# layui.each(d.data.cols, function(i1, item1){ }}","","{{# layui.each(item1, function(i2, item2){ }}",'{{# if(item2.fixed && item2.fixed !== "right"){ left = true; } }}','{{# if(item2.fixed === "right"){ right = true; } }}',function(){return e.fixed&&"right"!==e.fixed?'{{# if(item2.fixed && item2.fixed !== "right"){ }}':"right"===e.fixed?'{{# if(item2.fixed === "right"){ }}':""}(),"{{# var isSort = !(item2.colGroup) && item2.sort; }}",'",e.fixed?"{{# }; }}":"","{{# }); }}","","{{# }); }}","","
          ','
          ','{{# if(item2.type === "checkbox"){ }}','',"{{# } else { }}",'{{item2.title||""}}',"{{# if(isSort){ }}",'',"{{# } }}","{{# } }}","
          ","
          "].join("")},E=['',"","
          "].join(""),z=['
          ',"{{# if(d.data.toolbar){ }}",'
          ','
          ','
          ',"
          ","{{# } }}",'
          ',"{{# if(d.data.loading){ }}",'
          ','',"
          ","{{# } }}","{{# var left, right; }}",'
          ',_(),"
          ",'
          ',E,"
          ","{{# if(left){ }}",'
          ','
          ',_({fixed:!0}),"
          ",'
          ',E,"
          ","
          ","{{# }; }}","{{# if(right){ }}",'
          ','
          ',_({fixed:"right"}),'
          ',"
          ",'
          ',E,"
          ","
          ","{{# }; }}","
          ","{{# if(d.data.totalRow){ }}",'
          ','','',"
          ","
          ","{{# } }}","{{# if(d.data.page){ }}",'
          ','
          ',"
          ","{{# } }}","","
          "].join(""),H=t(window),R=t(document),F=function(e){var i=this;i.index=++d.index,i.config=t.extend({},i.config,d.config,e),i.render()};F.prototype.config={limit:10,loading:!0,cellMinWidth:60,defaultToolbar:["filter","exports","print"],autoSort:!0,text:{none:"无数据"}},F.prototype.render=function(){var e=this,a=e.config;if(a.elem=t(a.elem),a.where=a.where||{},a.id=a.id||a.elem.attr("id")||e.index,a.request=t.extend({pageName:"page",limitName:"limit"},a.request),a.response=t.extend({statusName:"code",statusCode:0,msgName:"msg",dataName:"data",countName:"count"},a.response),"object"==typeof a.page&&(a.limit=a.page.limit||a.limit,a.limits=a.page.limits||a.limits,e.page=a.page.curr=a.page.curr||1,delete a.page.elem,delete a.page.jump),!a.elem[0])return e;a.height&&/^full-\d+$/.test(a.height)&&(e.fullHeightGap=a.height.split("-")[1],a.height=H.height()-e.fullHeightGap),e.setInit();var l=a.elem,n=l.next("."+p),o=e.elem=t(i(z).render({VIEW_CLASS:p,data:a,index:e.index}));if(a.index=e.index,n[0]&&n.remove(),l.after(o),e.layTool=o.find(v),e.layBox=o.find(m),e.layHeader=o.find(b),e.layMain=o.find(k),e.layBody=o.find(x),e.layFixed=o.find(C),e.layFixLeft=o.find(w),e.layFixRight=o.find(T),e.layTotal=o.find(A),e.layPage=o.find(L),e.renderToolbar(),e.fullSize(),a.cols.length>1){var r=e.layFixed.find(b).find("th");r.height(e.layHeader.height()-1-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom")))}e.pullData(e.page),e.events()},F.prototype.initOpts=function(e){var t=this,i=(t.config,{checkbox:48,radio:48,space:15,numbers:40});e.checkbox&&(e.type="checkbox"),e.space&&(e.type="space"),e.type||(e.type="normal"),"normal"!==e.type&&(e.unresize=!0,e.width=e.width||i[e.type])},F.prototype.setInit=function(e){var t=this,i=t.config;return i.clientWidth=i.width||function(){var e=function(t){var a,l;t=t||i.elem.parent(),a=t.width();try{l="none"===t.css("display")}catch(n){}return!t[0]||a&&!l?a:e(t.parent())};return e()}(),"width"===e?i.clientWidth:void layui.each(i.cols,function(e,a){layui.each(a,function(l,n){if(!n)return void a.splice(l,1);if(n.key=e+"-"+l,n.hide=n.hide||!1,n.colGroup||n.colspan>1){var o=0;layui.each(i.cols[e+1],function(t,i){i.HAS_PARENT||o>1&&o==n.colspan||(i.HAS_PARENT=!0,i.parentKey=e+"-"+l,o+=parseInt(i.colspan>1?i.colspan:1))}),n.colGroup=!0}t.initOpts(n)})})},F.prototype.renderToolbar=function(){var e=this,a=e.config,l=['
          ','
          ','
          '].join(""),n=e.layTool.find(".layui-table-tool-temp");if("default"===a.toolbar)n.html(l);else if("string"==typeof a.toolbar){var o=t(a.toolbar).html()||"";o&&n.html(i(o).render(a))}var r={filter:{title:"筛选列",layEvent:"LAYTABLE_COLS",icon:"layui-icon-cols"},exports:{title:"导出",layEvent:"LAYTABLE_EXPORT",icon:"layui-icon-export"},print:{title:"打印",layEvent:"LAYTABLE_PRINT",icon:"layui-icon-print"}},d=[];"object"==typeof a.defaultToolbar&&layui.each(a.defaultToolbar,function(e,t){var i=r[t];i&&d.push('
          ')}),e.layTool.find(".layui-table-tool-self").html(d.join(""))},F.prototype.setParentCol=function(e,t){var i=this,a=i.config,l=i.layHeader.find('th[data-key="'+a.index+"-"+t+'"]'),n=parseInt(l.attr("colspan"))||0;if(l[0]){var o=t.split("-"),r=a.cols[o[0]][o[1]];e?n--:n++,l.attr("colspan",n),l[n<1?"addClass":"removeClass"](y),r.colspan=n,r.hide=n<1;var d=l.data("parentkey");d&&i.setParentCol(e,d)}},F.prototype.setColsPatch=function(){var e=this,t=e.config;layui.each(t.cols,function(t,i){layui.each(i,function(t,i){i.hide&&e.setParentCol(i.hide,i.parentKey)})})},F.prototype.setColsWidth=function(){var e=this,t=e.config,i=0,a=0,l=0,n=0,o=e.setInit("width");e.eachCols(function(e,t){t.hide||i++}),o=o-function(){return"line"===t.skin||"nob"===t.skin?2:i+1}()-e.getScrollWidth(e.layMain[0])-1;var r=function(e){layui.each(t.cols,function(i,r){layui.each(r,function(i,d){var c=0,s=d.minWidth||t.cellMinWidth;return d?void(d.colGroup||d.hide||(e?l&&ln&&a&&(l=(o-n)/a)};r(),r(!0),e.autoColNums=a,e.eachCols(function(i,a){var n=a.minWidth||t.cellMinWidth;a.colGroup||a.hide||(0===a.width?e.getCssRule(t.index+"-"+a.key,function(e){e.style.width=Math.floor(l>=n?l:n)+"px"}):/\d+%$/.test(a.width)&&e.getCssRule(t.index+"-"+a.key,function(e){e.style.width=Math.floor(parseFloat(a.width)/100*o)+"px"}))});var d=e.layMain.width()-e.getScrollWidth(e.layMain[0])-e.layMain.children("table").outerWidth();if(e.autoColNums&&d>=-i&&d<=i){var c=function(t){var i;return t=t||e.layHeader.eq(0).find("thead th:last-child"),i=t.data("field"),!i&&t.prev()[0]?c(t.prev()):t},s=c(),u=s.data("key");e.getCssRule(u,function(t){var i=t.style.width||s.outerWidth();t.style.width=parseFloat(i)+d+"px",e.layMain.height()-e.layMain.prop("clientHeight")>0&&(t.style.width=parseFloat(t.style.width)-1+"px")})}e.loading(!0)},F.prototype.resize=function(){var e=this;e.fullSize(),e.setColsWidth(),e.scrollPatch()},F.prototype.reload=function(e){var i=this;i.config.data&&i.config.data.constructor===Array&&delete i.config.data,i.config=t.extend({},i.config,e),i.render()},F.prototype.page=1,F.prototype.pullData=function(e){var i=this,a=i.config,l=a.request,n=a.response,o=function(){"object"==typeof a.initSort&&i.sort(a.initSort.field,a.initSort.type)};if(i.startTime=(new Date).getTime(),a.url){var r={};r[l.pageName]=e,r[l.limitName]=a.limit;var d=t.extend(r,a.where);a.contentType&&0==a.contentType.indexOf("application/json")&&(d=JSON.stringify(d)),t.ajax({type:a.method||"get",url:a.url,contentType:a.contentType,data:d,dataType:"json",headers:a.headers||{},success:function(t){"function"==typeof a.parseData&&(t=a.parseData(t)||t),t[n.statusName]!=n.statusCode?(i.renderForm(),i.layMain.html('
          '+(t[n.msgName]||"返回的数据不符合规范,正确的成功状态码 ("+n.statusName+") 应为:"+n.statusCode)+"
          ")):(i.renderData(t,e,t[n.countName]),o(),a.time=(new Date).getTime()-i.startTime+" ms"),i.setColsWidth(),"function"==typeof a.done&&a.done(t,e,t[n.countName])},error:function(e,t){i.layMain.html('
          数据接口请求异常:'+t+"
          "),i.renderForm(),i.setColsWidth()}})}else if(a.data&&a.data.constructor===Array){var c={},s=e*a.limit-a.limit;c[n.dataName]=a.data.concat().splice(s,a.limit),c[n.countName]=a.data.length,i.renderData(c,e,a.data.length),o(),i.setColsWidth(),"function"==typeof a.done&&a.done(c,e,c[n.countName])}},F.prototype.eachCols=function(e){var t=this;return d.eachCols(null,e,t.config.cols),t},F.prototype.renderData=function(e,n,o,r){var c=this,s=c.config,u=e[s.response.dataName]||[],h=[],p=[],v=[],m=function(){var e;return!r&&c.sortKey?c.sort(c.sortKey.field,c.sortKey.sort,!0):(layui.each(u,function(a,l){var o=[],u=[],f=[],m=a+s.limit*(n-1)+1;0!==l.length&&(r||(l[d.config.indexName]=a),c.eachCols(function(n,r){var c=r.field||n,h=s.index+"-"+r.key,p=l[c];if(void 0!==p&&null!==p||(p=""),!r.colGroup){var v=['','
          '+function(){var n=t.extend(!0,{LAY_INDEX:m},l),o=d.config.checkName;switch(r.type){case"checkbox":return'";case"radio":return n[o]&&(e=a),'';case"numbers":return m}return r.toolbar?i(t(r.toolbar).html()||"").render(n):r.templet?function(){return"function"==typeof r.templet?r.templet(n):i(t(r.templet).html()||String(p)).render(n)}():p}(),"
          "].join("");o.push(v),r.fixed&&"right"!==r.fixed&&u.push(v),"right"===r.fixed&&f.push(v)}}),h.push(''+o.join("")+""),p.push(''+u.join("")+""),v.push(''+f.join("")+""))}),c.layBody.scrollTop(0),c.layMain.find("."+f).remove(),c.layMain.find("tbody").html(h.join("")),c.layFixLeft.find("tbody").html(p.join("")),c.layFixRight.find("tbody").html(v.join("")),c.renderForm(),"number"==typeof e&&c.setThisRowChecked(e),c.syncCheckAll(),c.haveInit?c.scrollPatch():setTimeout(function(){c.scrollPatch()},50),c.haveInit=!0,l.close(c.tipsIndex),s.HAS_SET_COLS_PATCH||c.setColsPatch(),void(s.HAS_SET_COLS_PATCH=!0))};return c.key=s.id||s.index,d.cache[c.key]=u,c.layPage[0==o||0===u.length&&1==n?"addClass":"removeClass"](y),r?m():0===u.length?(c.renderForm(),c.layFixed.remove(),c.layMain.find("tbody").html(""),c.layMain.find("."+f).remove(),c.layMain.append('
          '+s.text.none+"
          ")):(m(),c.renderTotal(u),void(s.page&&(s.page=t.extend({elem:"layui-table-page"+s.index,count:o,limit:s.limit,limits:s.limits||[10,20,30,40,50,60,70,80,90],groups:3,layout:["prev","page","next","skip","count","limit"],prev:'',next:'',jump:function(e,t){t||(c.page=e.curr,s.limit=e.limit,c.loading(),c.pullData(e.curr))}},s.page),s.page.count=o,a.render(s.page))))},F.prototype.renderTotal=function(e){var t=this,i=t.config,a={};if(i.totalRow){layui.each(e,function(e,i){0!==i.length&&t.eachCols(function(e,t){var l=t.field||e,n=i[l];t.totalRow&&(a[l]=(a[l]||0)+(parseFloat(n)||0))})});var l=[];t.eachCols(function(e,t){var n=t.field||e,o=['','
          '+function(){var e=t.totalRowText||"";return t.totalRow?parseFloat(a[n]).toFixed(2)||e:e}(),"
          "].join("");l.push(o)}),t.layTotal.find("tbody").html(""+l.join("")+"")}},F.prototype.getColElem=function(e,t){var i=this,a=i.config;return e.eq(0).find(".laytable-cell-"+(a.index+"-"+t)+":eq(0)")},F.prototype.renderForm=function(e){n.render(e,"LAY-table-"+this.index)},F.prototype.setThisRowChecked=function(e){var t=this,i=(t.config,"layui-table-click"),a=t.layBody.find('tr[data-index="'+e+'"]');a.addClass(i).siblings("tr").removeClass(i)},F.prototype.sort=function(e,i,a,l){var n,r,c=this,s={},h=c.config,y=h.elem.attr("lay-filter"),f=d.cache[c.key];"string"==typeof e&&c.layHeader.find("th").each(function(i,a){var l=t(this),o=l.data("field");if(o===e)return e=l,n=o,!1});try{var n=n||e.data("field"),p=e.data("key");if(c.sortKey&&!a&&n===c.sortKey.field&&i===c.sortKey.sort)return;var v=c.layHeader.find("th .laytable-cell-"+p).find(S);c.layHeader.find("th").find(S).removeAttr("lay-sort"),v.attr("lay-sort",i||null),c.layFixed.find("th")}catch(m){return o.error("Table modules: Did not match to field")}c.sortKey={field:n,sort:i},h.autoSort&&("asc"===i?r=layui.sort(f,n):"desc"===i?r=layui.sort(f,n,!0):(r=layui.sort(f,d.config.indexName),delete c.sortKey)),s[h.response.dataName]=r||f,c.renderData(s,c.page,c.count,!0),l&&layui.event.call(e,u,"sort("+y+")",{field:n,type:i})},F.prototype.loading=function(e){var i=this,a=i.config;a.loading&&(e?(i.layInit&&i.layInit.remove(),delete i.layInit,i.layBox.find(g).remove()):(i.layInit=t(['
          ','',"
          "].join("")),i.layBox.append(i.layInit)))},F.prototype.setCheckData=function(e,t){var i=this,a=i.config,l=d.cache[i.key];l[e]&&l[e].constructor!==Array&&(l[e][a.checkName]=t)},F.prototype.syncCheckAll=function(){var e=this,t=e.config,i=e.layHeader.find('input[name="layTableCheckbox"]'),a=function(i){return e.eachCols(function(e,a){"checkbox"===a.type&&(a[t.checkName]=i)}),i};i[0]&&(d.checkStatus(e.key).isAll?(i[0].checked||(i.prop("checked",!0),e.renderForm("checkbox")),a(!0)):(i[0].checked&&(i.prop("checked",!1),e.renderForm("checkbox")),a(!1)))},F.prototype.getCssRule=function(e,t){var i=this,a=i.elem.find("style")[0],l=a.sheet||a.styleSheet||{},n=l.cssRules||l.rules;layui.each(n,function(i,a){if(a.selectorText===".laytable-cell-"+e)return t(a),!0})},F.prototype.fullSize=function(){var e,t=this,i=t.config,a=i.height;t.fullHeightGap&&(a=H.height()-t.fullHeightGap,a<135&&(a=135),t.elem.css("height",a)),a&&(e=parseFloat(a)-(t.layHeader.outerHeight()||38),i.toolbar&&(e-=t.layTool.outerHeight()||50),i.totalRow&&(e-=t.layTotal.outerHeight()||40),i.page&&(e=e-(t.layPage.outerHeight()||41)-2),t.layMain.css("height",e))},F.prototype.getScrollWidth=function(e){var t=0;return e?t=e.offsetWidth-e.clientWidth:(e=document.createElement("div"),e.style.width="100px",e.style.height="100px",e.style.overflowY="scroll",document.body.appendChild(e),t=e.offsetWidth-e.clientWidth,document.body.removeChild(e)),t},F.prototype.scrollPatch=function(){var e=this,i=e.layMain.children("table"),a=e.layMain.width()-e.layMain.prop("clientWidth"),l=e.layMain.height()-e.layMain.prop("clientHeight"),n=(e.getScrollWidth(e.layMain[0]),i.outerWidth()-e.layMain.width()),o=function(e){if(a&&l){if(e=e.eq(0),!e.find(".layui-table-patch")[0]){var i=t('
          ');i.find("div").css({width:a}),e.find("tr").append(i)}}else e.find(".layui-table-patch").remove()};o(e.layHeader),o(e.layTotal);var r=e.layMain.height(),d=r-l;e.layFixed.find(x).css("height",i.height()>=d?d:"auto"),e.layFixRight[n>0?"removeClass":"addClass"](y),e.layFixRight.css("right",a-1)},F.prototype.events=function(){var e,a=this,o=a.config,c=t("body"),s={},h=a.layHeader.find("th"),f=".layui-table-cell",p=o.elem.attr("lay-filter");a.layTool.on("click","*[lay-event]",function(e){var i=t(this),c=i.attr("lay-event"),s=function(e){var l=t(e.list),n=t('
            ');n.html(l),o.height&&n.css("max-height",o.height-(a.layTool.outerHeight()||50)),i.find(".layui-table-tool-panel")[0]||i.append(n),a.renderForm(),n.on("click",function(e){layui.stope(e)}),e.done&&e.done(n,l)};switch(layui.stope(e),R.trigger("table.tool.panel.remove"),l.close(a.tipsIndex),c){case"LAYTABLE_COLS":s({list:function(){var e=[];return a.eachCols(function(t,i){i.field&&"normal"==i.type&&e.push('
          • ')}),e.join("")}(),done:function(){n.on("checkbox(LAY_TABLE_TOOL_COLS)",function(e){var i=t(e.elem),l=this.checked,n=i.data("key"),r=i.data("parentkey");layui.each(o.cols,function(e,t){layui.each(t,function(t,i){if(e+"-"+t===n){var d=i.hide;i.hide=!l,a.elem.find('*[data-key="'+o.index+"-"+n+'"]')[l?"removeClass":"addClass"](y),d!=i.hide&&a.setParentCol(!l,r),a.resize()}})})})}});break;case"LAYTABLE_EXPORT":r.ie?l.tips("导出功能不支持 IE,请用 Chrome 等高级浏览器导出",this,{tips:3}):s({list:function(){return['
          • 导出到 Csv 文件
          • ','
          • 导出到 Excel 文件
          • '].join("")}(),done:function(e,i){i.on("click",function(){var e=t(this).data("type");d.exportFile(o.id,null,e)})}});break;case"LAYTABLE_PRINT":var h=window.open("打印窗口","_blank"),f=[""].join(""),v=t(a.layHeader.html());v.append(a.layMain.find("table").html()),v.find("th.layui-table-patch").remove(),v.find(".layui-table-col-special").remove(),h.document.write(f+v.prop("outerHTML")),h.document.close(),h.print(),h.close()}layui.event.call(this,u,"toolbar("+p+")",t.extend({event:c,config:o},{}))}),h.on("mousemove",function(e){var i=t(this),a=i.offset().left,l=e.clientX-a;i.data("unresize")||s.resizeStart||(s.allowResize=i.width()-l<=10,c.css("cursor",s.allowResize?"col-resize":""))}).on("mouseleave",function(){t(this);s.resizeStart||c.css("cursor","")}).on("mousedown",function(e){var i=t(this);if(s.allowResize){var l=i.data("key");e.preventDefault(),s.resizeStart=!0,s.offset=[e.clientX,e.clientY],a.getCssRule(l,function(e){var t=e.style.width||i.outerWidth();s.rule=e,s.ruleWidth=parseFloat(t),s.minWidth=i.data("minwidth")||o.cellMinWidth})}}),R.on("mousemove",function(t){if(s.resizeStart){if(t.preventDefault(),s.rule){var i=s.ruleWidth+t.clientX-s.offset[0];i');return n[0].value=i.data("content")||l.text(),i.find("."+N)[0]||i.append(n),n.focus(),void layui.stope(e)}}).on("mouseenter","td",function(){b.call(this)}).on("mouseleave","td",function(){b.call(this,"hide")});var g="layui-table-grid-down",b=function(e){var i=t(this),a=i.children(f);if(e)i.find(".layui-table-grid-down").remove();else if(a.prop("scrollWidth")>a.outerWidth()){if(a.find("."+g)[0])return;i.append('
            ')}};a.layBody.on("click","."+g,function(e){var i=t(this),n=i.parent(),d=n.children(f);a.tipsIndex=l.tips(['
            ',d.html(),"
            ",''].join(""),d[0],{tips:[3,""],time:-1,anim:-1,maxWidth:r.ios||r.android?300:a.elem.width()/2,isOutAnim:!1,skin:"layui-table-tips",success:function(e,t){e.find(".layui-table-tips-c").on("click",function(){l.close(t)})}}),layui.stope(e)}),a.layBody.on("click","*[lay-event]",function(){var e=t(this),i=e.parents("tr").eq(0).data("index");layui.event.call(this,u,"tool("+p+")",v.call(this,{event:e.attr("lay-event")})),a.setThisRowChecked(i)}),a.layMain.on("scroll",function(){var e=t(this),i=e.scrollLeft(),n=e.scrollTop();a.layHeader.scrollLeft(i),a.layTotal.scrollLeft(i),a.layFixed.find(x).scrollTop(n),l.close(a.tipsIndex)}),R.on("click",function(){R.trigger("table.remove.tool.panel")}),R.on("table.remove.tool.panel",function(){t(".layui-table-tool-panel").remove()}),H.on("resize",function(){a.resize()})},d.init=function(e,i){i=i||{};var a=this,l=t(e?'table[lay-filter="'+e+'"]':h+"[lay-data]"),n="Table element property lay-data configuration item has a syntax error: ";return l.each(function(){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){o.error(n+l)}var c=[],s=t.extend({elem:this,cols:[],data:[],skin:a.attr("lay-skin"),size:a.attr("lay-size"),even:"string"==typeof a.attr("lay-even")},d.config,i,l);e&&a.hide(),a.find("thead>tr").each(function(e){s.cols[e]=[],t(this).children().each(function(i){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){return o.error(n+l)}var d=t.extend({title:a.text(),colspan:a.attr("colspan")||0,rowspan:a.attr("rowspan")||0},l);d.colspan<2&&c.push(d),s.cols[e].push(d)})}),a.find("tbody>tr").each(function(e){var i=t(this),a={};i.children("td").each(function(e,i){var l=t(this),n=l.data("field");if(n)return a[n]=l.html()}),layui.each(c,function(e,t){var l=i.children("td").eq(e);a[t.field]=l.html()}),s.data[e]=a}),d.render(s)}),a},c.that={},c.config={},d.eachCols=function(e,i,a){var l=c.config[e]||{},n=[],o=0;a=t.extend(!0,[],a||l.cols),layui.each(a,function(e,t){layui.each(t,function(t,i){if(i.colGroup){var l=0;o++,i.CHILD_COLS=[],layui.each(a[e+1],function(e,t){t.PARENT_COL_INDEX||l>1&&l==i.colspan||(t.PARENT_COL_INDEX=o,i.CHILD_COLS.push(t),l+=parseInt(t.colspan>1?t.colspan:1))})}i.PARENT_COL_INDEX||n.push(i)})});var r=function(e){layui.each(e||n,function(e,t){return t.CHILD_COLS?r(t.CHILD_COLS):void("function"==typeof i&&i(e,t))})};r()},d.checkStatus=function(e){var t=0,i=0,a=[],l=d.cache[e]||[];return layui.each(l,function(e,l){return l.constructor===Array?void i++:void(l[d.config.checkName]&&(t++,a.push(d.clearCacheKey(l))))}),{data:a,isAll:!!l.length&&t===l.length-i}},d.exportFile=function(e,t,i){t=t||d.clearCacheKey(d.cache[e]),i=i||"csv";var a=c.config[e]||{},l={csv:"text/csv",xls:"application/vnd.ms-excel"}[i],n=document.createElement("a");return r.ie?o.error("IE_NOT_SUPPORT_EXPORTS"):(n.href="data:"+l+";charset=utf-8,\ufeff"+encodeURIComponent(function(){var i=[],a=[];return layui.each(t,function(t,l){var n=[];"object"==typeof e?(layui.each(e,function(e,a){0==t&&i.push(a||"")}),layui.each(d.clearCacheKey(l),function(e,t){n.push(t)})):d.eachCols(e,function(e,a){a.field&&"normal"==a.type&&!a.hide&&(0==t&&i.push(a.title||""),n.push(l[a.field]))}),a.push(n.join(","))}),i.join(",")+"\r\n"+a.join("\r\n")}()),n.download=(a.title||"table_"+(a.index||""))+"."+i,document.body.appendChild(n),n.click(),void document.body.removeChild(n))},d.resize=function(e){if(e){var t=s(e);if(!t)return;c.that[e].resize()}else layui.each(c.that,function(){this.resize()})},d.reload=function(e,i){i=i||{};var a=s(e);if(a)return i.data&&i.data.constructor===Array&&delete a.data,d.render(t.extend(!0,{},a,i))},d.render=function(e){var t=new F(e);return c.call(t)},d.clearCacheKey=function(e){return e=t.extend({},e),delete e[d.config.checkName],delete e[d.config.indexName],e},d.init(),e(u,d)});layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i(['",'"].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['
              ',function(){var i=[];return layui.each(e.elemItem,function(e){i.push("")}),i.join("")}(),"
            "].join(""));n.elem.attr("lay-indicator",n.indicator),n.elem.find("."+c)[0]&&n.elem.find("."+c).remove(),n.elem.append(t),"updown"===n.anim&&t.css("margin-top",-(t.height()/2)),t.find("li").on("hover"===n.trigger?"mouseover":n.trigger,function(){var t=i(this),a=t.index();a>n.index?e.slide("add",a-n.index):a",u=1;u<=i.length;u++){var r='
          • ";i.half&&parseInt(i.value)!==i.value&&u==Math.ceil(i.value)?n=n+'
          • ":n+=r}n+=""+(i.text?''+i.value+"星":"")+"";var c=i.elem,f=c.next("."+t);f[0]&&f.remove(),e.elemTemp=a(n),i.span=e.elemTemp.next("span"),i.setText&&i.setText(i.value),c.html(e.elemTemp),c.addClass("layui-inline"),i.readonly||e.action()},v.prototype.setvalue=function(e){var a=this,i=a.config;i.value=e,a.render()},v.prototype.action=function(){var e=this,i=e.config,l=e.elemTemp,n=l.find("i").width();l.children("li").each(function(e){var t=e+1,v=a(this);v.on("click",function(e){if(i.value=t,i.half){var o=e.pageX-a(this).offset().left;o<=n/2&&(i.value=i.value-.5)}i.text&&l.next("span").text(i.value+"星"),i.choose&&i.choose(i.value),i.setText&&i.setText(i.value)}),v.on("mousemove",function(e){if(l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+t+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half){var c=e.pageX-a(this).offset().left;c<=n/2&&v.children("i").addClass(u).removeClass(s)}}),v.on("mouseleave",function(){l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+Math.floor(i.value)+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half&&parseInt(i.value)!==i.value&&l.children("li:eq("+Math.floor(i.value)+")").children("i").addClass(u).removeClass(c)})})},v.prototype.events=function(){var e=this;e.config},i.render=function(e){var a=new v(e);return l.call(a)},e(n,i)});layui.define("jquery",function(t){"use strict";var e=layui.$,i={fixbar:function(t){var i,a,n="layui-fixbar",r="layui-fixbar-top",o=e(document),l=e("body");t=e.extend({showHeight:200},t),t.bar1=t.bar1===!0?"":t.bar1,t.bar2=t.bar2===!0?"":t.bar2,t.bgcolor=t.bgcolor?"background-color:"+t.bgcolor:"";var c=[t.bar1,t.bar2,""],g=e(['
              ',t.bar1?'
            • '+c[0]+"
            • ":"",t.bar2?'
            • '+c[1]+"
            • ":"",'
            • '+c[2]+"
            • ","
            "].join("")),s=g.find("."+r),u=function(){var e=o.scrollTop();e>=t.showHeight?i||(s.show(),i=1):i&&(s.hide(),i=0)};e("."+n)[0]||("object"==typeof t.css&&g.css(t.css),l.append(g),u(),g.find("li").on("click",function(){var i=e(this),a=i.attr("lay-type");"top"===a&&e("html,body").animate({scrollTop:0},200),t.click&&t.click.call(this,a)}),o.on("scroll",function(){clearTimeout(a),a=setTimeout(function(){u()},100)}))},countdown:function(t,e,i){var a=this,n="function"==typeof e,r=new Date(t).getTime(),o=new Date(!e||n?(new Date).getTime():e).getTime(),l=r-o,c=[Math.floor(l/864e5),Math.floor(l/36e5)%24,Math.floor(l/6e4)%60,Math.floor(l/1e3)%60];n&&(i=e);var g=setTimeout(function(){a.countdown(t,o+1e3,i)},1e3);return i&&i(l>0?c:[0,0,0,0],e,g),l<=0&&clearTimeout(g),g},timeAgo:function(t,e){var i=this,a=[[],[]],n=(new Date).getTime()-new Date(t).getTime();return n>6912e5?(n=new Date(t),a[0][0]=i.digit(n.getFullYear(),4),a[0][1]=i.digit(n.getMonth()+1),a[0][2]=i.digit(n.getDate()),e||(a[1][0]=i.digit(n.getHours()),a[1][1]=i.digit(n.getMinutes()),a[1][2]=i.digit(n.getSeconds())),a[0].join("-")+" "+a[1].join(":")):n>=864e5?(n/1e3/60/60/24|0)+"天前":n>=36e5?(n/1e3/60/60|0)+"小时前":n>=12e4?(n/1e3/60|0)+"分钟前":n<0?"未来":"刚刚"},digit:function(t,e){var i="";t=String(t),e=e||2;for(var a=t.length;a/g,">").replace(/'/g,"'").replace(/"/g,""")}};!function(t,e,i){"$:nomunge";function a(){n=e[l](function(){r.each(function(){var e=t(this),i=e.width(),a=e.height(),n=t.data(this,g);(i!==n.w||a!==n.h)&&e.trigger(c,[n.w=i,n.h=a])}),a()},o[s])}var n,r=t([]),o=t.resize=t.extend(t.resize,{}),l="setTimeout",c="resize",g=c+"-special-event",s="delay",u="throttleWindow";o[s]=250,o[u]=!0,t.event.special[c]={setup:function(){if(!o[u]&&this[l])return!1;var e=t(this);r=r.add(e),t.data(this,g,{w:e.width(),h:e.height()}),1===r.length&&a()},teardown:function(){if(!o[u]&&this[l])return!1;var e=t(this);r=r.not(e),e.removeData(g),r.length||clearTimeout(n)},add:function(e){function a(e,a,r){var o=t(this),l=t.data(this,g)||{};l.w=a!==i?a:o.width(),l.h=r!==i?r:o.height(),n.apply(this,arguments)}if(!o[u]&&this[l])return!1;var n;return t.isFunction(e)?(n=e,a):(n=e.handler,void(e.handler=a))}}}(e,window),t("util",i)});layui.define("jquery",function(e){"use strict";var l=layui.$,o=function(e){},t='';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="加载更多",h=l('");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;su)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)});layui.define(["layer","form"],function(t){"use strict";var e=layui.$,i=layui.layer,a=layui.form,l=(layui.hint(),layui.device()),n="layedit",o="layui-show",r="layui-disabled",c=function(){var t=this;t.index=0,t.config={tool:["strong","italic","underline","del","|","left","center","right","|","link","unlink","face","image"],hideTool:[],height:280}};c.prototype.set=function(t){var i=this;return e.extend(!0,i.config,t),i},c.prototype.on=function(t,e){return layui.onevent(n,t,e)},c.prototype.build=function(t,i){i=i||{};var a=this,n=a.config,r="layui-layedit",c=e("string"==typeof t?"#"+t:t),u="LAY_layedit_"+ ++a.index,d=c.next("."+r),y=e.extend({},n,i),f=function(){var t=[],e={};return layui.each(y.hideTool,function(t,i){e[i]=!0}),layui.each(y.tool,function(i,a){C[a]&&!e[a]&&t.push(C[a])}),t.join("")}(),m=e(['
            ','
            '+f+"
            ",'
            ','',"
            ","
            "].join(""));return l.ie&&l.ie<8?c.removeClass("layui-hide").addClass(o):(d[0]&&d.remove(),s.call(a,m,c[0],y),c.addClass("layui-hide").after(m),a.index)},c.prototype.getContent=function(t){var e=u(t);if(e[0])return d(e[0].document.body.innerHTML)},c.prototype.getText=function(t){var i=u(t);if(i[0])return e(i[0].document.body).text()},c.prototype.setContent=function(t,i,a){var l=u(t);l[0]&&(a?e(l[0].document.body).append(i):e(l[0].document.body).html(i),layedit.sync(t))},c.prototype.sync=function(t){var i=u(t);if(i[0]){var a=e("#"+i[1].attr("textarea"));a.val(d(i[0].document.body.innerHTML))}},c.prototype.getSelection=function(t){var e=u(t);if(e[0]){var i=m(e[0].document);return document.selection?i.text:i.toString()}};var s=function(t,i,a){var l=this,n=t.find("iframe");n.css({height:a.height}).on("load",function(){var o=n.contents(),r=n.prop("contentWindow"),c=o.find("head"),s=e([""].join("")),u=o.find("body");c.append(s),u.attr("contenteditable","true").css({"min-height":a.height}).html(i.value||""),y.apply(l,[r,n,i,a]),g.call(l,r,t,a)})},u=function(t){var i=e("#LAY_layedit_"+t),a=i.prop("contentWindow");return[a,i]},d=function(t){return 8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),t},y=function(t,a,n,o){var r=t.document,c=e(r.body);c.on("keydown",function(t){var e=t.keyCode;if(13===e){var a=m(r),l=p(a),n=l.parentNode;if("pre"===n.tagName.toLowerCase()){if(t.shiftKey)return;return i.msg("请暂时用shift+enter"),!1}r.execCommand("formatBlock",!1,"

            ")}}),e(n).parents("form").on("submit",function(){var t=c.html();8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),n.value=t}),c.on("paste",function(e){r.execCommand("formatBlock",!1,"

            "),setTimeout(function(){f.call(t,c),n.value=c.html()},100)})},f=function(t){var i=this;i.document;t.find("*[style]").each(function(){var t=this.style.textAlign;this.removeAttribute("style"),e(this).css({"text-align":t||""})}),t.find("table").addClass("layui-table"),t.find("script,link").remove()},m=function(t){return t.selection?t.selection.createRange():t.getSelection().getRangeAt(0)},p=function(t){return t.endContainer||t.parentElement().childNodes[0]},v=function(t,i,a){var l=this.document,n=document.createElement(t);for(var o in i)n.setAttribute(o,i[o]);if(n.removeAttribute("text"),l.selection){var r=a.text||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.pasteHTML(e(n).prop("outerHTML")),a.select()}else{var r=a.toString()||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.deleteContents(),a.insertNode(n)}},h=function(t,i){var a=this.document,l="layedit-tool-active",n=p(m(a)),o=function(e){return t.find(".layedit-tool-"+e)};i&&i[i.hasClass(l)?"removeClass":"addClass"](l),t.find(">i").removeClass(l),o("unlink").addClass(r),e(n).parents().each(function(){var t=this.tagName.toLowerCase(),e=this.style.textAlign;"b"!==t&&"strong"!==t||o("b").addClass(l),"i"!==t&&"em"!==t||o("i").addClass(l),"u"===t&&o("u").addClass(l),"strike"===t&&o("d").addClass(l),"p"===t&&("center"===e?o("center").addClass(l):"right"===e?o("right").addClass(l):o("left").addClass(l)),"a"===t&&(o("link").addClass(l),o("unlink").removeClass(r))})},g=function(t,a,l){var n=t.document,o=e(n.body),c={link:function(i){var a=p(i),l=e(a).parent();b.call(o,{href:l.attr("href"),target:l.attr("target")},function(e){var a=l[0];"A"===a.tagName?a.href=e.url:v.call(t,"a",{target:e.target,href:e.url,text:e.url},i)})},unlink:function(t){n.execCommand("unlink")},face:function(e){x.call(this,function(i){v.call(t,"img",{src:i.src,alt:i.alt},e)})},image:function(a){var n=this;layui.use("upload",function(o){var r=l.uploadImage||{};o.render({url:r.url,method:r.type,elem:e(n).find("input")[0],done:function(e){0==e.code?(e.data=e.data||{},v.call(t,"img",{src:e.data.src,alt:e.data.title},a)):i.msg(e.msg||"上传失败")}})})},code:function(e){k.call(o,function(i){v.call(t,"pre",{text:i.code,"lay-lang":i.lang},e)})},help:function(){i.open({type:2,title:"帮助",area:["600px","380px"],shadeClose:!0,shade:.1,skin:"layui-layer-msg",content:["http://www.layui.com/about/layedit/help.html","no"]})}},s=a.find(".layui-layedit-tool"),u=function(){var i=e(this),a=i.attr("layedit-event"),l=i.attr("lay-command");if(!i.hasClass(r)){o.focus();var u=m(n);u.commonAncestorContainer;l?(n.execCommand(l),/justifyLeft|justifyCenter|justifyRight/.test(l)&&n.execCommand("formatBlock",!1,"

            "),setTimeout(function(){o.focus()},10)):c[a]&&c[a].call(this,u),h.call(t,s,i)}},d=/image/;s.find(">i").on("mousedown",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)||u.call(this)}).on("click",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)&&u.call(this)}),o.on("click",function(){h.call(t,s),i.close(x.index)})},b=function(t,e){var l=this,n=i.open({type:1,id:"LAY_layedit_link",area:"350px",shade:.05,shadeClose:!0,moveType:1,title:"超链接",skin:"layui-layer-msg",content:['

              ','
            • ','','
              ','',"
              ","
            • ",'
            • ','','
              ','",'","
              ","
            • ",'
            • ','','',"
            • ","
            "].join(""),success:function(t,n){var o="submit(layedit-link-yes)";a.render("radio"),t.find(".layui-btn-primary").on("click",function(){i.close(n),l.focus()}),a.on(o,function(t){i.close(b.index),e&&e(t.field)})}});b.index=n},x=function(t){var a=function(){var t=["[微笑]","[嘻嘻]","[哈哈]","[可爱]","[可怜]","[挖鼻]","[吃惊]","[害羞]","[挤眼]","[闭嘴]","[鄙视]","[爱你]","[泪]","[偷笑]","[亲亲]","[生病]","[太开心]","[白眼]","[右哼哼]","[左哼哼]","[嘘]","[衰]","[委屈]","[吐]","[哈欠]","[抱抱]","[怒]","[疑问]","[馋嘴]","[拜拜]","[思考]","[汗]","[困]","[睡]","[钱]","[失望]","[酷]","[色]","[哼]","[鼓掌]","[晕]","[悲伤]","[抓狂]","[黑线]","[阴险]","[怒骂]","[互粉]","[心]","[伤心]","[猪头]","[熊猫]","[兔子]","[ok]","[耶]","[good]","[NO]","[赞]","[来]","[弱]","[草泥马]","[神马]","[囧]","[浮云]","[给力]","[围观]","[威武]","[奥特曼]","[礼物]","[钟]","[话筒]","[蜡烛]","[蛋糕]"],e={};return layui.each(t,function(t,i){e[i]=layui.cache.dir+"images/face/"+t+".gif"}),e}();return x.hide=x.hide||function(t){"face"!==e(t.target).attr("layedit-event")&&i.close(x.index)},x.index=i.tips(function(){var t=[];return layui.each(a,function(e,i){t.push('
          • '+e+'
          • ')}),'
              '+t.join("")+"
            "}(),this,{tips:1,time:0,skin:"layui-box layui-util-face",maxWidth:500,success:function(l,n){l.css({marginTop:-4,marginLeft:-10}).find(".layui-clear>li").on("click",function(){t&&t({src:a[this.title],alt:this.title}),i.close(n)}),e(document).off("click",x.hide).on("click",x.hide)}})},k=function(t){var e=this,l=i.open({type:1,id:"LAY_layedit_code",area:"550px",shade:.05,shadeClose:!0,moveType:1,title:"插入代码",skin:"layui-layer-msg",content:['
              ','
            • ','','
              ','","
              ","
            • ",'
            • ','','
              ','',"
              ","
            • ",'
            • ','','',"
            • ","
            "].join(""),success:function(l,n){var o="submit(layedit-code-yes)";a.render("select"),l.find(".layui-btn-primary").on("click",function(){i.close(n),e.focus()}),a.on(o,function(e){i.close(k.index),t&&t(e.field)})}});k.index=l},C={html:'',strong:'',italic:'',underline:'',del:'',"|":'',left:'',center:'',right:'',link:'',unlink:'',face:'',image:'',code:'',help:''},w=new c;t(n,w)});layui.define("jquery",function(e){"use strict";var a=layui.$,l="http://www.layui.com/doc/modules/code.html";e("code",function(e){var t=[];e=e||{},e.elem=a(e.elem||".layui-code"),e.about=!("about"in e)||e.about,e.elem.each(function(){t.push(this)}),layui.each(t.reverse(),function(t,i){var c=a(i),o=c.html();(c.attr("lay-encode")||e.encode)&&(o=o.replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")),c.html('
            1. '+o.replace(/[\r\t\n]+/g,"
            2. ")+"
            "),c.find(">.layui-code-h3")[0]||c.prepend('

            '+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code':"")+"

            ");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss"); \ No newline at end of file diff --git a/static/layui/layui.js b/static/layui/layui.js new file mode 100644 index 0000000..3cd51c2 --- /dev/null +++ b/static/layui/layui.js @@ -0,0 +1,2 @@ +/** layui-v2.4.5 MIT License By https://www.layui.com */ + ;!function(e){"use strict";var t=document,o={modules:{},status:{},timeout:10,event:{}},n=function(){this.v="2.4.5"},r=function(){var e=t.currentScript?t.currentScript.src:function(){for(var e,o=t.scripts,n=o.length-1,r=n;r>0;r--)if("interactive"===o[r].readyState){e=o[r].src;break}return e||o[n].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),i=function(t){e.console&&console.error&&console.error("Layui hint: "+t)},a="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),u={layer:"modules/layer",laydate:"modules/laydate",laypage:"modules/laypage",laytpl:"modules/laytpl",layim:"modules/layim",layedit:"modules/layedit",form:"modules/form",upload:"modules/upload",tree:"modules/tree",table:"modules/table",element:"modules/element",rate:"modules/rate",colorpicker:"modules/colorpicker",slider:"modules/slider",carousel:"modules/carousel",flow:"modules/flow",util:"modules/util",code:"modules/code",jquery:"modules/jquery",mobile:"modules/mobile","layui.all":"../layui.all"};n.prototype.cache=o,n.prototype.define=function(e,t){var n=this,r="function"==typeof e,i=function(){var e=function(e,t){layui[e]=t,o.status[e]=!0};return"function"==typeof t&&t(function(n,r){e(n,r),o.callback[n]=function(){t(e)}}),this};return r&&(t=e,e=[]),layui["layui.all"]||!layui["layui.all"]&&layui["layui.mobile"]?i.call(n):(n.use(e,i),n)},n.prototype.use=function(e,n,l){function s(e,t){var n="PLaySTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/;("load"===e.type||n.test((e.currentTarget||e.srcElement).readyState))&&(o.modules[f]=t,d.removeChild(v),function r(){return++m>1e3*o.timeout/4?i(f+" is not a valid module"):void(o.status[f]?c():setTimeout(r,4))}())}function c(){l.push(layui[f]),e.length>1?y.use(e.slice(1),n,l):"function"==typeof n&&n.apply(layui,l)}var y=this,p=o.dir=o.dir?o.dir:r,d=t.getElementsByTagName("head")[0];e="string"==typeof e?[e]:e,window.jQuery&&jQuery.fn.on&&(y.each(e,function(t,o){"jquery"===o&&e.splice(t,1)}),layui.jquery=layui.$=jQuery);var f=e[0],m=0;if(l=l||[],o.host=o.host||(p.match(/\/\/([\s\S]+?)\//)||["//"+location.host+"/"])[0],0===e.length||layui["layui.all"]&&u[f]||!layui["layui.all"]&&layui["layui.mobile"]&&u[f])return c(),y;if(o.modules[f])!function g(){return++m>1e3*o.timeout/4?i(f+" is not a valid module"):void("string"==typeof o.modules[f]&&o.status[f]?c():setTimeout(g,4))}();else{var v=t.createElement("script"),h=(u[f]?p+"lay/":/^\{\/\}/.test(y.modules[f])?"":o.base||"")+(y.modules[f]||f)+".js";h=h.replace(/^\{\/\}/,""),v.async=!0,v.charset="utf-8",v.src=h+function(){var e=o.version===!0?o.v||(new Date).getTime():o.version||"";return e?"?v="+e:""}(),d.appendChild(v),!v.attachEvent||v.attachEvent.toString&&v.attachEvent.toString().indexOf("[native code")<0||a?v.addEventListener("load",function(e){s(e,h)},!1):v.attachEvent("onreadystatechange",function(e){s(e,h)}),o.modules[f]=h}return y},n.prototype.getStyle=function(t,o){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](o)},n.prototype.link=function(e,n,r){var a=this,u=t.createElement("link"),l=t.getElementsByTagName("head")[0];"string"==typeof n&&(r=n);var s=(r||e).replace(/\.|\//g,""),c=u.id="layuicss-"+s,y=0;return u.rel="stylesheet",u.href=e+(o.debug?"?v="+(new Date).getTime():""),u.media="all",t.getElementById(c)||l.appendChild(u),"function"!=typeof n?a:(function p(){return++y>1e3*o.timeout/100?i(e+" timeout"):void(1989===parseInt(a.getStyle(t.getElementById(c),"width"))?function(){n()}():setTimeout(p,100))}(),a)},o.callback={},n.prototype.factory=function(e){if(layui[e])return"function"==typeof o.callback[e]?o.callback[e]:null},n.prototype.addcss=function(e,t,n){return layui.link(o.dir+"css/"+e,t,n)},n.prototype.img=function(e,t,o){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,"function"==typeof t&&t(n)},void(n.onerror=function(e){n.onerror=null,"function"==typeof o&&o(e)}))},n.prototype.config=function(e){e=e||{};for(var t in e)o[t]=e[t];return this},n.prototype.modules=function(){var e={};for(var t in u)e[t]=u[t];return e}(),n.prototype.extend=function(e){var t=this;e=e||{};for(var o in e)t[o]||t.modules[o]?i("模块名 "+o+" 已被占用"):t.modules[o]=e[o];return t},n.prototype.router=function(e){var t=this,e=e||location.hash,o={path:[],search:{},hash:(e.match(/[^#](#.*$)/)||[])[1]||""};return/^#\//.test(e)?(e=e.replace(/^#\//,""),o.href="/"+e,e=e.replace(/([^#])(#.*$)/,"$1").split("/")||[],t.each(e,function(e,t){/^\w+=/.test(t)?function(){t=t.split("="),o.search[t[0]]=t[1]}():o.path.push(t)}),o):o},n.prototype.data=function(t,o,n){if(t=t||"layui",n=n||localStorage,e.JSON&&e.JSON.parse){if(null===o)return delete n[t];o="object"==typeof o?o:{key:o};try{var r=JSON.parse(n[t])}catch(i){var r={}}return"value"in o&&(r[o.key]=o.value),o.remove&&delete r[o.key],n[t]=JSON.stringify(r),o.key?r[o.key]:r}},n.prototype.sessionData=function(e,t){return this.data(e,t,sessionStorage)},n.prototype.device=function(t){var o=navigator.userAgent.toLowerCase(),n=function(e){var t=new RegExp(e+"/([^\\s\\_\\-]+)");return e=(o.match(t)||[])[1],e||!1},r={os:function(){return/windows/.test(o)?"windows":/linux/.test(o)?"linux":/iphone|ipod|ipad|ios/.test(o)?"ios":/mac/.test(o)?"mac":void 0}(),ie:function(){return!!(e.ActiveXObject||"ActiveXObject"in e)&&((o.match(/msie\s(\d+)/)||[])[1]||"11")}(),weixin:n("micromessenger")};return t&&!r[t]&&(r[t]=n(t)),r.android=/android/.test(o),r.ios="ios"===r.os,r},n.prototype.hint=function(){return{error:i}},n.prototype.each=function(e,t){var o,n=this;if("function"!=typeof t)return n;if(e=e||[],e.constructor===Object){for(o in e)if(t.call(e[o],o,e[o]))break}else for(o=0;oi?1:r .layui-tab-title li span i { + margin-right: 5px; +} + +.my-side ul.layui-nav li dl dd a i { + margin-left: 15px; +} + +.my-body { + position: fixed; + top: 60px; + bottom: 0; + left: 200px; + right: 0; + z-index: 1; + overflow: hidden; +} + +.body { + padding: 10px; +} + +.my-body .layui-tab, .my-body .layui-tab .layui-tab-content { + margin: 0; + padding: 0; +} + +.my-body .layui-tab .layui-tab-title li:first-child > i { + display: none; +} + +.my-body .layui-tab, .my-body .layui-tab .layui-tab-content, .my-body .layui-tab .layui-tab-item { + height: 100%; +} + +.my-body iframe { + width: 100%; + height: 100%; + border: none; + outline: none; +} + +.layui-layout-admin .my-footer { + height: 40px; + padding: 2px 0; +} + +.layui-layout-admin .my-footer p { + height: 20px; + line-height: 20px; + font-size: 12px; + text-align: center; +} + +.my-btn-box { + height: 38px; + margin-bottom: 10px; +} + +.my-pay-box > div p { + text-align: center; + margin-bottom: 10px; +} + +/* welcome */ +.my-index-main button.layui-icon { + width: 100%; + font-size: 20px; +} + +.my-index-main .my-nav-btn { + background: #efefef; + cursor: pointer; + border-radius: 2px; + overflow: hidden; +} + +.my-index-main .my-nav-text:first-child { + height: 24px; + line-height: 25px; + font-size: 16px; + font-weight: bold; +} + +.my-index-main .my-nav-text:last-child { + height: 20px; + line-height: 20px; + font-size: 12px; +} + +/* login */ +.login-body { + background: url("../image/bg.png") repeat fixed; +} + +.login-box { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + width: 320px; + height: 241px; + max-height: 300px; +} + +.login-body .login-box h3 { + color: #444; + font-size: 22px; + font-weight: 100; + text-align: center; +} + +.login-box .layui-input[type='number'] { + display: inline-block; + width: 50%; + vertical-align: top; +} + +.login-box img { + display: inline-block; + width: 46%; + height: 38px; + border: none; + vertical-align: top; + cursor: pointer; + margin-left: 4%; +} + +.login-box button.btn-reset { + width: 95px; +} + +.login-box button.btn-submit { + width: 190px; +} + +.login-main { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 350px; + margin: 0 auto; +} + +.login-main header { + margin-top: 150px; + height: 35px; + line-height: 35px; + font-size: 30px; + font-weight: 100; + text-align: center; +} + +.login-main header, .login-main form, .login-main form .layui-input-inline { + margin-bottom: 15px; +} + +.login-main form .layui-input-inline, .login-main form .layui-input-inline input, .login-main form .layui-input-inline button { + width: 100%; +} + +.login-main form .login-btn { + margin-bottom: 5px; +} + +/* demo */ +.site-demo-button div { + margin: 20px 30px 10px; +} + +.site-demo-button .layui-btn { + margin: 0 7px 10px 0; +} + +/* check */ +input[type='checkbox'] { + vertical-align: middle; +} + +.my-checkbox { + -webkit-appearance: none; + position: relative; + width: 20px; + height: 20px; + background-color: #FFFFFF; + border: solid 2px #28B779; + border-radius: 2px; + background-clip: padding-box; + display: inline-block; + cursor: pointer; +} + +.my-checkbox:checked { + background-color: #28B779; + border: solid 0 #28B779; +} + +.my-checkbox:checked:before { + position: absolute; + display: inline-block; + right: 50%; + bottom: 50%; + -webkit-transform: translate(50%, 50%); + -ms-transform: translate(50%, 50%); + transform: translate(50%, 50%); + font-family: "Microsoft Yahei"; + content: "√"; + color: #FFFFFF; + font-size: 16px; + font-weight: 600; +} + +/* dblclick css */ +.dblclick-tab tr td { + height: 30px; + line-height: 30px; + padding: 0 6px; + border-radius: 2px; + cursor: pointer; +} + +.dblclick-tab tr td:hover { + color: black; + background: white; +} + +.dblclick-tab tr td i { + position: relative; + top: 2px; + display: inline-block; + margin-right: 5px; +} + +/* tips 404 */ +.my-page-box { + font-family: "Segoe UI", "Lucida Grande", Helvetica, Arial, "Microsoft YaHei", FreeSans, Arimo, "Droid Sans", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", FontAwesome, sans-serif; + text-align: center; + padding: 20px; + background-color: white; +} + +.my-page-box i { + font-size: 100px; +} + +.my-page-box h2, .my-page-box h3, .my-page-box h4, .my-page-box h5 { + font-size: 80px; +} + +.my-page-box p.msg { + color: #dce2ec; + font-size: 20px; + margin-top: 20px; +} + +.my-page-box p.text { + color: #666; + font-size: 16px; + margin-top: 20px; +} + +.my-page-box .my-btn-box { + margin-top: 20px; + margin-bottom: 20px; +} + +/* tree table */ +.my-tree-table-box .tree-table-tree-box { + width: 20%; + min-height: 200px; + display: inline-block; + vertical-align: top; + overflow-y: auto; + overflow-x: auto; +} + +.my-tree-table-box .tree-table-table-box { + display: inline-block; + vertical-align: top; + width: 79%; + margin-left: 1%; +} + +/* skin0 */ +html .skin-0 .dblclick-tips-box .layui-layer-content { + background-color: #009688; +} + +html .skin-0 .dblclick-tips-box i.layui-layer-TipsB { + border-right-color: #009688; +} + +/* skin1 */ +html .skin-1 .my-header .my-header-logo, +html .skin-1 .layui-nav .layui-nav-item a, +html .skin-1 .layui-nav .layui-nav-item a:hover { + color: #444; +} + +html .skin-1 .my-header, +html .skin-1 .layui-nav, +html .skin-1 .layui-nav-child { + background: white; +} + +html .skin-1 .layui-nav .layui-nav-item .layui-nav-child dd.layui-this a { + color: white; +} + +html .skin-1 .layui-nav .layui-nav-item .layui-nav-child dd.layui-this, +html .skin-1 .layui-nav .layui-nav-item .layui-nav-child dd.layui-this > a, +html .skin-1 .layui-nav-tree .layui-nav-item > a:hover, +html .skin-1 .layui-nav .layui-nav-item .layui-nav-child dd:hover > a, +html .skin-1 .layui-tab-title li .layui-tab-close:hover, +html .skin-1 .dblclick-tips-box .layui-layer-content { + color: white !important; + background-color: #666 !important; +} + +html .skin-1 .dblclick-tips-box i.layui-layer-TipsB { + border-right-color: #666; +} + +html .skin-1 .layui-nav .layui-nav-itemed > a { + background: #444 !important; +} + +html .skin-1 .layui-nav .layui-nav-more { + border-color: #444 transparent transparent; +} + +html .skin-1 .layui-nav .layui-nav-mored { + border-color: transparent transparent #444; +} + +/* skin2 */ +html .skin-2 .my-header .my-header-logo, +html .skin-2 .layui-nav .layui-nav-item a, +html .skin-2 .layui-nav .layui-nav-item a:hover { + color: white; +} + +html .skin-2 .my-header, +html .skin-2 .layui-nav, +html .skin-2 .layui-nav-child { + background-color: #01AAED; +} + +html .skin-2 .layui-nav .layui-nav-item .layui-nav-child dd.layui-this a { + color: white; +} + +html .skin-2 .layui-nav .layui-nav-item .layui-nav-child dd.layui-this, +html .skin-2 .layui-nav .layui-nav-item .layui-nav-child dd.layui-this > a, +html .skin-2 .layui-nav-tree .layui-nav-item > a:hover, +html .skin-2 .layui-nav .layui-nav-item .layui-nav-child dd:hover > a, +html .skin-2 .layui-tab-title li .layui-tab-close:hover, +html .skin-2 .dblclick-tips-box .layui-layer-content { + color: white !important; + background-color: #00C0F7 !important; +} + +html .skin-2 .dblclick-tips-box i.layui-layer-TipsB { + border-right-color: #00C0F7; +} + +html .skin-2 .layui-nav .layui-nav-itemed > a { + background-color: #1684af !important; +} + +/* skin0-2 */ +html .skin-2 .layui-nav .layui-nav-more, +html .skin-1 .layui-nav-tree .layui-nav-more, +html .skin-2 .layui-nav-tree .layui-nav-more { + border-color: white transparent transparent; +} + +html .skin-2 .layui-nav .layui-nav-mored, +html .skin-1 .layui-nav-itemed .layui-nav-more, +html .skin-2 .layui-nav-itemed .layui-nav-more { + border-color: transparent transparent white; +} + +/* tools */ +.fl { + float: left; +} + +.fr { + float: right; +} + +.none { + display: none; +} + +.block { + display: block; +} + +.tc { + text-align: center; +} + +.max-auto { + max-height: 450px; + overflow-y: auto; +} + +/* layui css cover */ +html body .layui-nav .layui-nav-bar { + opacity: 0 !important; + overflow: hidden !important; +} + +.layui-nav .layui-this:after, .layui-nav-bar, .layui-nav-tree .layui-nav-itemed:after { + background-color: transparent; +} + +.my-body .layui-tab-card > .layui-tab-title li { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.layui-layer-tips .layui-layer-content { + padding: 5px; +} + +/* media */ +@media screen and (max-width: 1023px) { + .my-header ul.layui-nav, .my-header-btn, .my-side, .layui-field-title, .my-btn-box .fr { + display: none; + } + + .layui-side-scroll { + width: 170px; + } + + body div.my-mobile { + background: #393D49; + } + + body div.my-mobile .layui-layer-content { + overflow-x: hidden; + } + + .my-header ul.my-header-user-nav { + display: inline-block; + padding-left: 0; + padding-right: 10px; + } + + .my-header ul.my-header-user-nav a { + padding: 0 10px; + } + + .my-header ul.my-header-user-nav:last-child a:first-child { + padding-right: 20px; + } + + .my-side, .layui-nav-tree, .my-btn-box input[type='text'] { + width: 150px; + } + + .my-body { + left: 0; + width: 100%; + overflow-x: auto; + } + + .my-body iframe { + width: 100%; + } + + select { + background: white; + } + + .layui-layout-admin .layui-footer { + left: 0; + } +} + +@media screen and ( max-width: 450px) { + .my-header ul.my-header-user-nav li a.pay { + display: none; + } +} \ No newline at end of file diff --git a/templates/base-user.html b/templates/base-user.html new file mode 100644 index 0000000..124f731 --- /dev/null +++ b/templates/base-user.html @@ -0,0 +1,54 @@ + + + + + {% block title %}{% endblock %} + + + + + + + + +
            +
            + + +
            +
            +{% for message in get_flashed_messages() %} +
            + + {{ message }} +
            +{% endfor %} +
            + +{% block body %} +{% endblock %} + + + +{% block script %}{% endblock %} + + \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..5e63595 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,113 @@ + + + + + + {% block title %}{% endblock %} + + + + + + {% block head %}{% endblock %} + + + +
            + + + + +
            + +
            + {% for message in get_flashed_messages() %} +
            + + {{ message }} +
            + {% endfor %} + {% block card %}{% endblock %} +
            + {% block body %} + {% endblock %} +
            +
            +
            + + +
            + + + +{% block script %} + +{% endblock %} + + + diff --git a/templates/base2.html b/templates/base2.html new file mode 100644 index 0000000..90890d1 --- /dev/null +++ b/templates/base2.html @@ -0,0 +1,66 @@ + + + + + {% block title %}{% endblock %} + + + + + + + +
            + + + + + +
            +
            {% block card %}{% endblock %}
            +
            + {% block body %} + {% endblock %} +
            +
            + + + + diff --git a/templates/borrow.html b/templates/borrow.html new file mode 100644 index 0000000..258a087 --- /dev/null +++ b/templates/borrow.html @@ -0,0 +1,174 @@ +{% extends "base.html" %} +{% block title %}学生借书{% endblock %} + +{% block card %}

            学生借书

            {% endblock %} +{% block body %} +
            + +
            +
            + +
            + {{ form.card(class="layui-input", id="card") }} +
            + +
            + {{ form.book_name(class="layui-input", id="book_name") }} +
            +
            {{ form.submit(class="layui-btn", id="search") }}
            +
            +
            +
            +
            + + + + + + + + + + + + + + +
            图书编号ISBN书名作者出版社操作
            +
            +
            + +{% endblock %} + +{% block script %} + + +{% endblock %} + diff --git a/templates/change-info.html b/templates/change-info.html new file mode 100644 index 0000000..eabb0f0 --- /dev/null +++ b/templates/change-info.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} +{% block title %}管理员信息设置{% endblock %} + +{% block body %} +
            + 修改个人信息 +
            +
            +
            + {{ form.csrf_token }} +
            + +
            + +
            +
            +
            + +
            + {{ form.name(class="layui-input", style="width:40%") }} +
            +
            +
            + +
            + + 账号、权限无法修改,若要修改请联系系统管理员 +
            +
            +
            +
            + +
            +
            +
            +
            +
            +{% endblock %} \ No newline at end of file diff --git a/templates/change-password.html b/templates/change-password.html new file mode 100644 index 0000000..d3a7d79 --- /dev/null +++ b/templates/change-password.html @@ -0,0 +1,39 @@ +{% extends "base.html" %} +{% block title %}修改密码{% endblock %} + +{% block body %} +
            + 修改密码 +
            +
            +
            + {{ form.csrf_token }} +
            + +
            + {{ form.old_password(class="layui-input", style="width:40%", placeholder="请输入原密码") }} +
            +
            +
            + +
            + {{ form.password(class="layui-input", style="width:40%", placeholder="请输入新密码") }} +
            +
            +
            + +
            + {{ form.password2(class="layui-input", style="width:40%", placeholder="再次输入新密码") }} +
            +
            +
            +
            + +
            +
            +
            +
            +
            +{% endblock %} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..c2d0e50 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,109 @@ +{% extends "base.html" %} +{% block title %}首页{% endblock %}\ +{% block head %} + +{% endblock %} + +{% block body %} +
            + +{% endblock %} +{% block script %} + + +{% endblock %} \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..bbb7abf --- /dev/null +++ b/templates/login.html @@ -0,0 +1,23 @@ +{% extends "base-user.html" %} +{% block title %}欢迎使用图书管理系统{% endblock %} + +{% block body %} + +{% endblock %} \ No newline at end of file diff --git a/templates/new-store.html b/templates/new-store.html new file mode 100644 index 0000000..5dea4a8 --- /dev/null +++ b/templates/new-store.html @@ -0,0 +1,67 @@ +{% extends 'base.html' %} +{% block title %}库存管理{% endblock %} + +{% block card %} +
            +

            新书登记

            + +
            +{% endblock %} +{% block body %} +
            +
            + {{ form.csrf_token }} +
            + +
            + {{ form.isbn(class="layui-input", style="width:40%") }} +
            +
            +
            + +
            + {{ form.book_name(class="layui-input", style="width:40%") }} +
            +
            +
            + +
            + {{ form.press(class="layui-input", style="width:40%") }} +
            +
            +
            + +
            + {{ form.author(class="layui-input", style="width:40%") }} +
            +
            +
            + +
            + {{ form.class_name(class="layui-input", style="width:40%") }} +
            +
            +
            +
            + {{ form.submit(class="layui-btn", style="width:100px;font-size:medium") }} +
            +
            +
            +{% endblock %} +{% block script %} + +{% endblock %} \ No newline at end of file diff --git a/templates/return.html b/templates/return.html new file mode 100644 index 0000000..25ea772 --- /dev/null +++ b/templates/return.html @@ -0,0 +1,195 @@ +{% extends "base.html" %} +{% block title %}学生还书{% endblock %} + +{% block card %}

            学生还书

            {% endblock %} +{% block body %} +
            + +
            +
            + +
            + {{ form.card(class="layui-input", id="card") }} +
            +
            {{ form.submit(class="layui-btn", id="search") }}
            +
            +
            +
            +
            + + + + + + + + + + + + + + +
            图书编号ISBN书名起始日期应还日期操作
            +
            +
            + +{% endblock %} + +{% block script %} + + +{% endblock %} + diff --git a/templates/search-book.html b/templates/search-book.html new file mode 100644 index 0000000..b45efd4 --- /dev/null +++ b/templates/search-book.html @@ -0,0 +1,129 @@ +{% extends "base.html" %} +{% block title %}查询图书信息{% endblock %} + +{% block card %}

            查询图书信息

            {% endblock %} +{% block body %} +
            + {{ form.csrf_token }} +
            +
            +
            +
            + {{ form.method(class="layui-input-inline") }} +
            +
            + +
            + {{ form.content(class="layui-input",style="width:250px") }} +
            +
            +
            +
            {{ form.submit(class="layui-btn", id="search") }}
            + +
            +
            +
            + +
            +
            +
            + + + + + + + + + + + + + + + +
            ISBN书名出版社作者类别馆藏数量可借数量
            +
            +
            + +{% endblock %} + +{% block script %} + +{% endblock %} + diff --git a/templates/search-student.html b/templates/search-student.html new file mode 100644 index 0000000..0bceb83 --- /dev/null +++ b/templates/search-student.html @@ -0,0 +1,167 @@ +{% extends "base.html" %} +{% block title %}查询学生信息{% endblock %} + +{% block card %}

            查询学生信息

            {% endblock %} +{% block body %} +
            + +
            +
            +
            +
            + +
            +
            +
            + {{ form.card(class="layui-input", id="card") }} +
            +
            +
            +
            {{ form.submit(class="layui-btn", id="search") }}
            + +
            +
            +
            + +
            +
            +
            +

            + 姓名: + 性别: + 有效期至: + 是否欠费: +

            +
            +
            + + + + + + + + + + + + + + +
            图书编号书名作者起始日期应还日期还书日期
            +
            +
            + +{% endblock %} + +{% block script %} + +{% endblock %} + diff --git a/templates/storage.html b/templates/storage.html new file mode 100644 index 0000000..3526409 --- /dev/null +++ b/templates/storage.html @@ -0,0 +1,54 @@ +{% extends 'base.html' %} +{% block title %}库存管理{% endblock %} + +{% block card %} +
            +

            库存补充

            + +
            +{% endblock %} +{% block body %} +
            + {{ form.csrf_token }} +
            + +
            + {{ form.barcode(class="layui-input", style="width:40%") }} +
            +
            +
            + +
            + {{ form.isbn(class="layui-input", style="width:40%") }} +
            +
            +
            + +
            + {{ form.location(class="layui-input", style="width:40%") }} +
            +
            +
            +
            + {{ form.submit(class="layui-btn", style="width:100px;font-size:medium") }} +
            +
            +
            +{% endblock %} +{% block script %} + +{% endblock %} \ No newline at end of file diff --git a/templates/user-book.html b/templates/user-book.html new file mode 100644 index 0000000..8017c7b --- /dev/null +++ b/templates/user-book.html @@ -0,0 +1,131 @@ +{% extends "base-user.html" %} +{% block title %}查询图书信息{% endblock %} + + +{% block body %} +
            +
            +
            + {{ form.csrf_token }} +
            +
            +
            +
            + {{ form.method(class="layui-input-inline") }} +
            +
            + +
            + {{ form.content(class="layui-input",style="width:250px") }} +
            +
            +
            +
            {{ form.submit(class="layui-btn", id="search") }}
            + +
            +
            +
            + +
            +
            +
            + + + + + + + + + + + + + + + +
            ISBN书名出版社作者类别馆藏数量可借数量
            +
            +
            +
            +{% endblock %} + +{% block script %} + +{% endblock %} + diff --git a/templates/user-info.html b/templates/user-info.html new file mode 100644 index 0000000..e2650ff --- /dev/null +++ b/templates/user-info.html @@ -0,0 +1,19 @@ +{% extends 'base.html' %} +{% block title %}个人信息{% endblock %} + +{% block body %} +
            + 个人信息 +
            +
            +      账   号:  {{ user.admin_id }} +
            +
            +      用户名:  {{ user.admin_name }} +
            +
            +   拥有权限: {{ user.right }} +
            +
            +
            +{% endblock %} \ No newline at end of file diff --git a/templates/user-student.html b/templates/user-student.html new file mode 100644 index 0000000..c7c5f6e --- /dev/null +++ b/templates/user-student.html @@ -0,0 +1,168 @@ +{% extends "base-user.html" %} +{% block title %}查询学生信息{% endblock %} + +{% block body %} +
            +
            +
            + +
            +
            +
            +
            + +
            +
            +
            + {{ form.card(class="layui-input", id="card") }} +
            +
            +
            +
            {{ form.submit(class="layui-btn", id="search") }}
            + +
            +
            +
            + +
            +
            +
            +

            + 姓名: + 性别: + 有效期至: + 是否欠费: +

            +
            +
            + + + + + + + + + + + + + + +
            图书编号书名作者起始日期应还日期还书日期
            +
            +
            +
            +{% endblock %} + +{% block script %} + +{% endblock %} + diff --git a/venv/Lib/site-packages/Click-7.0.dist-info/INSTALLER b/venv/Lib/site-packages/Click-7.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/Click-7.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Click-7.0.dist-info/LICENSE.txt b/venv/Lib/site-packages/Click-7.0.dist-info/LICENSE.txt new file mode 100644 index 0000000..87ce152 --- /dev/null +++ b/venv/Lib/site-packages/Click-7.0.dist-info/LICENSE.txt @@ -0,0 +1,39 @@ +Copyright © 2014 by the Pallets team. + +Some rights reserved. + +Redistribution and use in source and binary forms of the software as +well as documentation, with or without modification, are permitted +provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +---- + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright © 2001-2006 Gregory P. Ward. All rights reserved. +Copyright © 2002-2006 Python Software Foundation. All rights reserved. diff --git a/venv/Lib/site-packages/Click-7.0.dist-info/METADATA b/venv/Lib/site-packages/Click-7.0.dist-info/METADATA new file mode 100644 index 0000000..625bdad --- /dev/null +++ b/venv/Lib/site-packages/Click-7.0.dist-info/METADATA @@ -0,0 +1,121 @@ +Metadata-Version: 2.1 +Name: Click +Version: 7.0 +Summary: Composable command line interface toolkit +Home-page: https://palletsprojects.com/p/click/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets Team +Maintainer-email: contact@palletsprojects.com +License: BSD +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Code, https://github.com/pallets/click +Project-URL: Issue tracker, https://github.com/pallets/click/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* + +\$ click\_ +========== + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install click + +Click supports Python 3.4 and newer, Python 2.7, and PyPy. + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +A Simple Example +---------------- + +What does it look like? Here is an example of a simple Click program: + +.. code-block:: python + + import click + + @click.command() + @click.option("--count", default=1, help="Number of greetings.") + @click.option("--name", prompt="Your name", + help="The person to greet.") + def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo("Hello, %s!" % name) + + if __name__ == '__main__': + hello() + +And what it looks like when run: + +.. code-block:: text + + $ python hello.py --count=3 + Your name: Click + Hello, Click! + Hello, Click! + Hello, Click! + + +Donate +------ + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +* Website: https://palletsprojects.com/p/click/ +* Documentation: https://click.palletsprojects.com/ +* License: `BSD `_ +* Releases: https://pypi.org/project/click/ +* Code: https://github.com/pallets/click +* Issue tracker: https://github.com/pallets/click/issues +* Test status: + + * Linux, Mac: https://travis-ci.org/pallets/click + * Windows: https://ci.appveyor.com/project/pallets/click + +* Test coverage: https://codecov.io/gh/pallets/click + + diff --git a/venv/Lib/site-packages/Click-7.0.dist-info/RECORD b/venv/Lib/site-packages/Click-7.0.dist-info/RECORD new file mode 100644 index 0000000..5e49d67 --- /dev/null +++ b/venv/Lib/site-packages/Click-7.0.dist-info/RECORD @@ -0,0 +1,40 @@ +Click-7.0.dist-info/LICENSE.txt,sha256=4hIxn676T0Wcisk3_chVcECjyrivKTZsoqSNI5AlIlw,1876 +Click-7.0.dist-info/METADATA,sha256=-r8jeke3Zer4diRvT1MjFZuiJ6yTT_qFP39svLqdaLI,3516 +Click-7.0.dist-info/RECORD,, +Click-7.0.dist-info/WHEEL,sha256=gduuPyBvFJQSQ0zdyxF7k0zynDXbIbvg5ZBHoXum5uk,110 +Click-7.0.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 +click/__init__.py,sha256=HjGThQ7tef9kkwCV371TBnrf0SAi6fKfU_jtEnbYTvQ,2789 +click/_bashcomplete.py,sha256=iaNUmtxag0YPfxba3TDYCNietiTMQIrvhRLj-H8okFU,11014 +click/_compat.py,sha256=vYmvoj4opPxo-c-2GMQQjYT_r_QkOKybkfGoeVrt0dA,23399 +click/_termui_impl.py,sha256=xHmLtOJhKUCVD6168yucJ9fknUJPAMs0eUTPgVUO-GQ,19611 +click/_textwrap.py,sha256=gwS4m7bdQiJnzaDG8osFcRb-5vn4t4l2qSCy-5csCEc,1198 +click/_unicodefun.py,sha256=QHy2_5jYlX-36O-JVrTHNnHOqg8tquUR0HmQFev7Ics,4364 +click/_winconsole.py,sha256=PPWVak8Iikm_gAPsxMrzwsVFCvHgaW3jPaDWZ1JBl3U,8965 +click/core.py,sha256=q8FLcDZsagBGSRe5Y9Hi_FGvAeZvusNfoO5EkhkSQ8Y,75305 +click/decorators.py,sha256=idKt6duLUUfAFftrHoREi8MJSd39XW36pUVHthdglwk,11226 +click/exceptions.py,sha256=CNpAjBAE7qjaV4WChxQeak95e5yUOau8AsvT-8m6wss,7663 +click/formatting.py,sha256=eh-cypTUAhpI3HD-K4ZpR3vCiURIO62xXvKkR3tNUTM,8889 +click/globals.py,sha256=oQkou3ZQ5DgrbVM6BwIBirwiqozbjfirzsLGAlLRRdg,1514 +click/parser.py,sha256=m-nGZz4VwprM42_qtFlWFGo7yRJQxkBlRcZodoH593Y,15510 +click/termui.py,sha256=o_ZXB2jyvL2Rce7P_bFGq452iyBq9ykJyRApIPMCZO0,23207 +click/testing.py,sha256=aYGqY_iWLu2p4k7lkuJ6t3fqpf6aPGqTsyLzNY_ngKg,13062 +click/types.py,sha256=2Q929p-aBP_ZYuMFJqJR-Ipucofv3fmDc5JzBDPmzJU,23287 +click/utils.py,sha256=6-D0WkAxvv9FkgHXSHwDIv0l9Gdx9Mm6Z5vuKNLIfZI,15763 +Click-7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click/__pycache__/core.cpython-36.pyc,, +click/__pycache__/decorators.cpython-36.pyc,, +click/__pycache__/exceptions.cpython-36.pyc,, +click/__pycache__/formatting.cpython-36.pyc,, +click/__pycache__/globals.cpython-36.pyc,, +click/__pycache__/parser.cpython-36.pyc,, +click/__pycache__/termui.cpython-36.pyc,, +click/__pycache__/testing.cpython-36.pyc,, +click/__pycache__/types.cpython-36.pyc,, +click/__pycache__/utils.cpython-36.pyc,, +click/__pycache__/_bashcomplete.cpython-36.pyc,, +click/__pycache__/_compat.cpython-36.pyc,, +click/__pycache__/_termui_impl.cpython-36.pyc,, +click/__pycache__/_textwrap.cpython-36.pyc,, +click/__pycache__/_unicodefun.cpython-36.pyc,, +click/__pycache__/_winconsole.cpython-36.pyc,, +click/__pycache__/__init__.cpython-36.pyc,, diff --git a/venv/Lib/site-packages/Click-7.0.dist-info/WHEEL b/venv/Lib/site-packages/Click-7.0.dist-info/WHEEL new file mode 100644 index 0000000..1316c41 --- /dev/null +++ b/venv/Lib/site-packages/Click-7.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.31.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/Click-7.0.dist-info/top_level.txt b/venv/Lib/site-packages/Click-7.0.dist-info/top_level.txt new file mode 100644 index 0000000..dca9a90 --- /dev/null +++ b/venv/Lib/site-packages/Click-7.0.dist-info/top_level.txt @@ -0,0 +1 @@ +click diff --git a/venv/Lib/site-packages/Flask-1.0.2.dist-info/INSTALLER b/venv/Lib/site-packages/Flask-1.0.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/Flask-1.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Flask-1.0.2.dist-info/LICENSE.txt b/venv/Lib/site-packages/Flask-1.0.2.dist-info/LICENSE.txt new file mode 100644 index 0000000..8f9252f --- /dev/null +++ b/venv/Lib/site-packages/Flask-1.0.2.dist-info/LICENSE.txt @@ -0,0 +1,31 @@ +Copyright © 2010 by the Pallets team. + +Some rights reserved. + +Redistribution and use in source and binary forms of the software as +well as documentation, with or without modification, are permitted +provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/venv/Lib/site-packages/Flask-1.0.2.dist-info/METADATA b/venv/Lib/site-packages/Flask-1.0.2.dist-info/METADATA new file mode 100644 index 0000000..c600e73 --- /dev/null +++ b/venv/Lib/site-packages/Flask-1.0.2.dist-info/METADATA @@ -0,0 +1,130 @@ +Metadata-Version: 2.1 +Name: Flask +Version: 1.0.2 +Summary: A simple framework for building complex web applications. +Home-page: https://www.palletsprojects.com/p/flask/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets team +Maintainer-email: contact@palletsprojects.com +License: BSD +Project-URL: Documentation, http://flask.pocoo.org/docs/ +Project-URL: Code, https://github.com/pallets/flask +Project-URL: Issue tracker, https://github.com/pallets/flask/issues +Platform: any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Flask +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Provides-Extra: dev +Provides-Extra: docs +Provides-Extra: dotenv +Requires-Dist: Werkzeug (>=0.14) +Requires-Dist: Jinja2 (>=2.10) +Requires-Dist: itsdangerous (>=0.24) +Requires-Dist: click (>=5.1) +Provides-Extra: dev +Requires-Dist: pytest (>=3); extra == 'dev' +Requires-Dist: coverage; extra == 'dev' +Requires-Dist: tox; extra == 'dev' +Requires-Dist: sphinx; extra == 'dev' +Requires-Dist: pallets-sphinx-themes; extra == 'dev' +Requires-Dist: sphinxcontrib-log-cabinet; extra == 'dev' +Provides-Extra: docs +Requires-Dist: sphinx; extra == 'docs' +Requires-Dist: pallets-sphinx-themes; extra == 'docs' +Requires-Dist: sphinxcontrib-log-cabinet; extra == 'docs' +Provides-Extra: dotenv +Requires-Dist: python-dotenv; extra == 'dotenv' + +Flask +===== + +Flask is a lightweight `WSGI`_ web application framework. It is designed +to make getting started quick and easy, with the ability to scale up to +complex applications. It began as a simple wrapper around `Werkzeug`_ +and `Jinja`_ and has become one of the most popular Python web +application frameworks. + +Flask offers suggestions, but doesn't enforce any dependencies or +project layout. It is up to the developer to choose the tools and +libraries they want to use. There are many extensions provided by the +community that make adding new functionality easy. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U Flask + + +A Simple Example +---------------- + +.. code-block:: python + + from flask import Flask + + app = Flask(__name__) + + @app.route('/') + def hello(): + return 'Hello, World!' + +.. code-block:: text + + $ FLASK_APP=hello.py flask run + * Serving Flask app "hello" + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) + + +Donate +------ + +The Pallets organization develops and supports Flask and the libraries +it uses. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://psfmember.org/civicrm/contribute/transact?reset=1&id=20 + + +Links +----- + +* Website: https://www.palletsprojects.com/p/flask/ +* Documentation: http://flask.pocoo.org/docs/ +* License: `BSD `_ +* Releases: https://pypi.org/project/Flask/ +* Code: https://github.com/pallets/flask +* Issue tracker: https://github.com/pallets/flask/issues +* Test status: + + * Linux, Mac: https://travis-ci.org/pallets/flask + * Windows: https://ci.appveyor.com/project/pallets/flask + +* Test coverage: https://codecov.io/gh/pallets/flask + +.. _WSGI: https://wsgi.readthedocs.io +.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/ +.. _Jinja: https://www.palletsprojects.com/p/jinja/ +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + diff --git a/venv/Lib/site-packages/Flask-1.0.2.dist-info/RECORD b/venv/Lib/site-packages/Flask-1.0.2.dist-info/RECORD new file mode 100644 index 0000000..6690ae8 --- /dev/null +++ b/venv/Lib/site-packages/Flask-1.0.2.dist-info/RECORD @@ -0,0 +1,48 @@ +Flask-1.0.2.dist-info/LICENSE.txt,sha256=ziEXA3AIuaiUn1qe4cd1XxCESWTYrk4TjN7Qb06J3l8,1575 +Flask-1.0.2.dist-info/METADATA,sha256=iA5tiNWzTtgCVe80aTZGNWsckj853fJyfvHs9U-WZRk,4182 +Flask-1.0.2.dist-info/RECORD,, +Flask-1.0.2.dist-info/WHEEL,sha256=J3CsTk7Mf2JNUyhImI-mjX-fmI4oDjyiXgWT4qgZiCE,110 +Flask-1.0.2.dist-info/entry_points.txt,sha256=gBLA1aKg0OYR8AhbAfg8lnburHtKcgJLDU52BBctN0k,42 +Flask-1.0.2.dist-info/top_level.txt,sha256=dvi65F6AeGWVU0TBpYiC04yM60-FX1gJFkK31IKQr5c,6 +flask/__init__.py,sha256=qq8lK6QQbxJALf1igz7qsvUwOTAoKvFGfdLm7jPNsso,1673 +flask/__main__.py,sha256=pgIXrHhxM5MAMvgzAqWpw_t6AXZ1zG38us4JRgJKtxk,291 +flask/_compat.py,sha256=UDFGhosh6mOdNB-4evKPuneHum1OpcAlwTNJCRm0irQ,2892 +flask/app.py,sha256=ahpe3T8w98rQd_Er5d7uDxK57S1nnqGQx3V3hirBovU,94147 +flask/blueprints.py,sha256=Cyhl_x99tgwqEZPtNDJUFneAfVJxWfEU4bQA7zWS6VU,18331 +flask/cli.py,sha256=30QYAO10Do9LbZYCLgfI_xhKjASdLopL8wKKVUGS2oA,29442 +flask/config.py,sha256=kznUhj4DLYxsTF_4kfDG8GEHto1oZG_kqblyrLFtpqQ,9951 +flask/ctx.py,sha256=leFzS9fzmo0uaLCdxpHc5_iiJZ1H0X_Ig4yPCOvT--g,16224 +flask/debughelpers.py,sha256=1ceC-UyqZTd4KsJkf0OObHPsVt5R3T6vnmYhiWBjV-w,6479 +flask/globals.py,sha256=pGg72QW_-4xUfsI33I5L_y76c21AeqfSqXDcbd8wvXU,1649 +flask/helpers.py,sha256=YCl8D1plTO1evEYP4KIgaY3H8Izww5j4EdgRJ89oHTw,40106 +flask/logging.py,sha256=qV9h0vt7NIRkKM9OHDWndzO61E5CeBMlqPJyTt-W2Wc,2231 +flask/sessions.py,sha256=2XHV4ASREhSEZ8bsPQW6pNVNuFtbR-04BzfKg0AfvHo,14452 +flask/signals.py,sha256=BGQbVyCYXnzKK2DVCzppKFyWN1qmrtW1QMAYUs-1Nr8,2211 +flask/templating.py,sha256=FDfWMbpgpC3qObW8GGXRAVrkHFF8K4CHOJymB1wvULI,4914 +flask/testing.py,sha256=XD3gWNvLUV8dqVHwKd9tZzsj81fSHtjOphQ1wTNtlMs,9379 +flask/views.py,sha256=Wy-_WkUVtCfE2zCXYeJehNgHuEtviE4v3HYfJ--MpbY,5733 +flask/wrappers.py,sha256=1Z9hF5-hXQajn_58XITQFRY8efv3Vy3uZ0avBfZu6XI,7511 +flask/json/__init__.py,sha256=Ns1Hj805XIxuBMh2z0dYnMVfb_KUgLzDmP3WoUYaPhw,10729 +flask/json/tag.py,sha256=9ehzrmt5k7hxf7ZEK0NOs3swvQyU9fWNe-pnYe69N60,8223 +../../Scripts/flask.exe,sha256=-vt2qxO-er3zapO3x-bsApk6tSLL5Xhq0py6aRsdUJk,98155 +Flask-1.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +flask/json/__pycache__/tag.cpython-36.pyc,, +flask/json/__pycache__/__init__.cpython-36.pyc,, +flask/__pycache__/app.cpython-36.pyc,, +flask/__pycache__/blueprints.cpython-36.pyc,, +flask/__pycache__/cli.cpython-36.pyc,, +flask/__pycache__/config.cpython-36.pyc,, +flask/__pycache__/ctx.cpython-36.pyc,, +flask/__pycache__/debughelpers.cpython-36.pyc,, +flask/__pycache__/globals.cpython-36.pyc,, +flask/__pycache__/helpers.cpython-36.pyc,, +flask/__pycache__/logging.cpython-36.pyc,, +flask/__pycache__/sessions.cpython-36.pyc,, +flask/__pycache__/signals.cpython-36.pyc,, +flask/__pycache__/templating.cpython-36.pyc,, +flask/__pycache__/testing.cpython-36.pyc,, +flask/__pycache__/views.cpython-36.pyc,, +flask/__pycache__/wrappers.cpython-36.pyc,, +flask/__pycache__/_compat.cpython-36.pyc,, +flask/__pycache__/__init__.cpython-36.pyc,, +flask/__pycache__/__main__.cpython-36.pyc,, diff --git a/venv/Lib/site-packages/Flask-1.0.2.dist-info/WHEEL b/venv/Lib/site-packages/Flask-1.0.2.dist-info/WHEEL new file mode 100644 index 0000000..f21b51c --- /dev/null +++ b/venv/Lib/site-packages/Flask-1.0.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.31.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/Flask-1.0.2.dist-info/entry_points.txt b/venv/Lib/site-packages/Flask-1.0.2.dist-info/entry_points.txt new file mode 100644 index 0000000..1eb0252 --- /dev/null +++ b/venv/Lib/site-packages/Flask-1.0.2.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +flask = flask.cli:main + diff --git a/venv/Lib/site-packages/Flask-1.0.2.dist-info/top_level.txt b/venv/Lib/site-packages/Flask-1.0.2.dist-info/top_level.txt new file mode 100644 index 0000000..7e10602 --- /dev/null +++ b/venv/Lib/site-packages/Flask-1.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +flask diff --git a/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/PKG-INFO b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/PKG-INFO new file mode 100644 index 0000000..8850561 --- /dev/null +++ b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/PKG-INFO @@ -0,0 +1,44 @@ +Metadata-Version: 1.1 +Name: Flask-Login +Version: 0.4.1 +Summary: User session management for Flask +Home-page: https://github.com/maxcountryman/flask-login +Author: Matthew Frazier +Author-email: leafstormrush@gmail.com +License: MIT +Description: + Flask-Login + ----------- + + Flask-Login provides user session management for Flask. It handles the common + tasks of logging in, logging out, and remembering your users' + sessions over extended periods of time. + + Flask-Login is not bound to any particular database system or permissions + model. The only requirement is that your user objects implement a few + methods, and that you provide a callback to the extension capable of + loading users from their ID. + + Links + ````` + * `documentation `_ + * `development version `_ + +Platform: any +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules diff --git a/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/SOURCES.txt b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/SOURCES.txt new file mode 100644 index 0000000..f008768 --- /dev/null +++ b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/SOURCES.txt @@ -0,0 +1,20 @@ +LICENSE +MANIFEST.in +README.md +setup.cfg +setup.py +Flask_Login.egg-info/PKG-INFO +Flask_Login.egg-info/SOURCES.txt +Flask_Login.egg-info/dependency_links.txt +Flask_Login.egg-info/not-zip-safe +Flask_Login.egg-info/requires.txt +Flask_Login.egg-info/top_level.txt +Flask_Login.egg-info/version_info.json +flask_login/__about__.py +flask_login/__init__.py +flask_login/_compat.py +flask_login/config.py +flask_login/login_manager.py +flask_login/mixins.py +flask_login/signals.py +flask_login/utils.py \ No newline at end of file diff --git a/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/dependency_links.txt b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/installed-files.txt b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/installed-files.txt new file mode 100644 index 0000000..e6b4d68 --- /dev/null +++ b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/installed-files.txt @@ -0,0 +1,23 @@ +..\flask_login\config.py +..\flask_login\login_manager.py +..\flask_login\mixins.py +..\flask_login\signals.py +..\flask_login\utils.py +..\flask_login\_compat.py +..\flask_login\__about__.py +..\flask_login\__init__.py +..\flask_login\__pycache__\config.cpython-36.pyc +..\flask_login\__pycache__\login_manager.cpython-36.pyc +..\flask_login\__pycache__\mixins.cpython-36.pyc +..\flask_login\__pycache__\signals.cpython-36.pyc +..\flask_login\__pycache__\utils.cpython-36.pyc +..\flask_login\__pycache__\_compat.cpython-36.pyc +..\flask_login\__pycache__\__about__.cpython-36.pyc +..\flask_login\__pycache__\__init__.cpython-36.pyc +dependency_links.txt +not-zip-safe +PKG-INFO +requires.txt +SOURCES.txt +top_level.txt +version_info.json diff --git a/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/not-zip-safe b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/requires.txt b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/requires.txt new file mode 100644 index 0000000..e3e9a71 --- /dev/null +++ b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/requires.txt @@ -0,0 +1 @@ +Flask diff --git a/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/top_level.txt b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/top_level.txt new file mode 100644 index 0000000..31514bd --- /dev/null +++ b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/top_level.txt @@ -0,0 +1 @@ +flask_login diff --git a/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/version_info.json b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/version_info.json new file mode 100644 index 0000000..d76f5ef --- /dev/null +++ b/venv/Lib/site-packages/Flask_Login-0.4.1-py3.6.egg-info/version_info.json @@ -0,0 +1,6 @@ +{ + "release_date": null, + "version": "0.4.0", + "maintainer": "", + "body": "" +} \ No newline at end of file diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/DESCRIPTION.rst b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..c9db5b5 --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/DESCRIPTION.rst @@ -0,0 +1,15 @@ + +Flask-SQLAlchemy +---------------- + +Adds SQLAlchemy support to your Flask application. + +Links +````` + +* `documentation `_ +* `development version + `_ + + + diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/INSTALLER b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/LICENSE.txt b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/LICENSE.txt new file mode 100644 index 0000000..49fcac3 --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/LICENSE.txt @@ -0,0 +1,31 @@ +Copyright (c) 2014 by Armin Ronacher. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +* The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/METADATA b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/METADATA new file mode 100644 index 0000000..4d94cbd --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/METADATA @@ -0,0 +1,43 @@ +Metadata-Version: 2.0 +Name: Flask-SQLAlchemy +Version: 2.3.2 +Summary: Adds SQLAlchemy support to your Flask application +Home-page: http://github.com/mitsuhiko/flask-sqlalchemy +Author: Phil Howell +Author-email: phil@quae.co.uk +License: BSD +Description-Content-Type: UNKNOWN +Platform: any +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Requires-Dist: Flask (>=0.10) +Requires-Dist: SQLAlchemy (>=0.8.0) + + +Flask-SQLAlchemy +---------------- + +Adds SQLAlchemy support to your Flask application. + +Links +````` + +* `documentation `_ +* `development version + `_ + + + diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/RECORD b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/RECORD new file mode 100644 index 0000000..3dddb88 --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/RECORD @@ -0,0 +1,14 @@ +Flask_SQLAlchemy-2.3.2.dist-info/DESCRIPTION.rst,sha256=Mp4bpckSjf082xflOARFwzWLTnUszq7JxcY0dR9vD2w,273 +Flask_SQLAlchemy-2.3.2.dist-info/LICENSE.txt,sha256=2smrI3hNiP6c5TcX0fa6fqODgsdJVLC166X0kVxei9A,1457 +Flask_SQLAlchemy-2.3.2.dist-info/METADATA,sha256=iDXuOIujwz5MXBrH-I4WsW7kLKsY07feI7hggFHFfEk,1384 +Flask_SQLAlchemy-2.3.2.dist-info/RECORD,, +Flask_SQLAlchemy-2.3.2.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110 +Flask_SQLAlchemy-2.3.2.dist-info/metadata.json,sha256=VOw756wP14azHrBwNxHIfbYkK4DkEPrCaV6Kf0VO36E,1257 +Flask_SQLAlchemy-2.3.2.dist-info/top_level.txt,sha256=w2K4fNNoTh4HItoFfz2FRQShSeLcvHYrzU_sZov21QU,17 +flask_sqlalchemy/__init__.py,sha256=0ZyibSbbC_Q1x8Kemp_2s2-NCowd_-CRvLyE1dPfnvw,35991 +flask_sqlalchemy/_compat.py,sha256=6rFcZZ3kxvyeJUC_FyB62mG1saNU8iQthzWHLDcKPVE,1057 +flask_sqlalchemy/model.py,sha256=7CTvGxxKmLscwcwq9mVT5ny_w301QZvTVjSqMoMx6DI,4974 +Flask_SQLAlchemy-2.3.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +flask_sqlalchemy/__pycache__/model.cpython-36.pyc,, +flask_sqlalchemy/__pycache__/_compat.cpython-36.pyc,, +flask_sqlalchemy/__pycache__/__init__.cpython-36.pyc,, diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/WHEEL b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/WHEEL new file mode 100644 index 0000000..7332a41 --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.30.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/metadata.json b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/metadata.json new file mode 100644 index 0000000..96339bd --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6"], "description_content_type": "UNKNOWN", "extensions": {"python.details": {"contacts": [{"email": "phil@quae.co.uk", "name": "Phil Howell", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "http://github.com/mitsuhiko/flask-sqlalchemy"}}}, "extras": [], "generator": "bdist_wheel (0.30.0)", "license": "BSD", "metadata_version": "2.0", "name": "Flask-SQLAlchemy", "platform": "any", "run_requires": [{"requires": ["Flask (>=0.10)", "SQLAlchemy (>=0.8.0)"]}], "summary": "Adds SQLAlchemy support to your Flask application", "version": "2.3.2"} \ No newline at end of file diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/top_level.txt b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/top_level.txt new file mode 100644 index 0000000..8a5538e --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.3.2.dist-info/top_level.txt @@ -0,0 +1 @@ +flask_sqlalchemy diff --git a/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/PKG-INFO b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/PKG-INFO new file mode 100644 index 0000000..a2572ba --- /dev/null +++ b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/PKG-INFO @@ -0,0 +1,36 @@ +Metadata-Version: 1.1 +Name: Flask-Script +Version: 2.0.6 +Summary: Scripting support for Flask +Home-page: http://github.com/smurfix/flask-script +Author: Matthias Urlichs +Author-email: matthias@urlichs.de +License: BSD +Download-URL: https://github.com/smurfix/flask-script/tarball/v2.0.6 +Description: + Flask-Script + -------------- + + Flask support for writing external scripts. + + Links + ````` + + * `documentation `_ + + + +Platform: any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules diff --git a/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/SOURCES.txt b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/SOURCES.txt new file mode 100644 index 0000000..0202fcb --- /dev/null +++ b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/SOURCES.txt @@ -0,0 +1,29 @@ +LICENSE +MANIFEST.in +README.rst +setup.cfg +setup.py +tests.py +Flask_Script.egg-info/PKG-INFO +Flask_Script.egg-info/SOURCES.txt +Flask_Script.egg-info/dependency_links.txt +Flask_Script.egg-info/not-zip-safe +Flask_Script.egg-info/requires.txt +Flask_Script.egg-info/top_level.txt +docs/Makefile +docs/conf.py +docs/index.rst +docs/make.bat +docs/_static/flask-script.png +docs/_static/index.html +docs/_themes/README +docs/_themes/flask_theme_support.py +docs/_themes/flask/theme.conf +docs/_themes/flask/static/flasky.css_t +docs/_themes/flask_small/layout.html +docs/_themes/flask_small/theme.conf +docs/_themes/flask_small/static/flasky.css_t +flask_script/__init__.py +flask_script/_compat.py +flask_script/cli.py +flask_script/commands.py \ No newline at end of file diff --git a/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/dependency_links.txt b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/installed-files.txt b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/installed-files.txt new file mode 100644 index 0000000..8e6f17f --- /dev/null +++ b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/installed-files.txt @@ -0,0 +1,14 @@ +..\flask_script\cli.py +..\flask_script\commands.py +..\flask_script\_compat.py +..\flask_script\__init__.py +..\flask_script\__pycache__\cli.cpython-36.pyc +..\flask_script\__pycache__\commands.cpython-36.pyc +..\flask_script\__pycache__\_compat.cpython-36.pyc +..\flask_script\__pycache__\__init__.cpython-36.pyc +dependency_links.txt +not-zip-safe +PKG-INFO +requires.txt +SOURCES.txt +top_level.txt diff --git a/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/not-zip-safe b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/requires.txt b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/requires.txt new file mode 100644 index 0000000..e3e9a71 --- /dev/null +++ b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/requires.txt @@ -0,0 +1 @@ +Flask diff --git a/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/top_level.txt b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/top_level.txt new file mode 100644 index 0000000..efd6af0 --- /dev/null +++ b/venv/Lib/site-packages/Flask_Script-2.0.6-py3.6.egg-info/top_level.txt @@ -0,0 +1 @@ +flask_script diff --git a/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/DESCRIPTION.rst b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..7d7eef7 --- /dev/null +++ b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/DESCRIPTION.rst @@ -0,0 +1,21 @@ +Flask-WTF +========= + +.. image:: https://travis-ci.org/lepture/flask-wtf.svg?branch=master + :target: https://travis-ci.org/lepture/flask-wtf + :alt: Travis CI Status +.. image:: https://coveralls.io/repos/lepture/flask-wtf/badge.svg?branch=master + :target: https://coveralls.io/r/lepture/flask-wtf + :alt: Coverage Status + +Simple integration of Flask and WTForms, including CSRF, file upload, +and reCAPTCHA. + +Links +----- + +* `Documentation `_ +* `PyPI `_ +* `GitHub `_ + + diff --git a/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/INSTALLER b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/LICENSE.txt b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/LICENSE.txt new file mode 100644 index 0000000..5cbad1a --- /dev/null +++ b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/LICENSE.txt @@ -0,0 +1,32 @@ +Copyright (c) 2010 by Dan Jacob. +Copyright (c) 2013 by Hsiaoming Yang. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +* The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/METADATA b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/METADATA new file mode 100644 index 0000000..8dd02a4 --- /dev/null +++ b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/METADATA @@ -0,0 +1,52 @@ +Metadata-Version: 2.0 +Name: Flask-WTF +Version: 0.14.2 +Summary: Simple integration of Flask and WTForms. +Home-page: https://github.com/lepture/flask-wtf +Author: Hsiaoming Yang +Author-email: me@lepture.com +License: BSD +Platform: any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Flask +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Dist: Flask +Requires-Dist: WTForms + +Flask-WTF +========= + +.. image:: https://travis-ci.org/lepture/flask-wtf.svg?branch=master + :target: https://travis-ci.org/lepture/flask-wtf + :alt: Travis CI Status +.. image:: https://coveralls.io/repos/lepture/flask-wtf/badge.svg?branch=master + :target: https://coveralls.io/r/lepture/flask-wtf + :alt: Coverage Status + +Simple integration of Flask and WTForms, including CSRF, file upload, +and reCAPTCHA. + +Links +----- + +* `Documentation `_ +* `PyPI `_ +* `GitHub `_ + + diff --git a/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/RECORD b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/RECORD new file mode 100644 index 0000000..68e0f28 --- /dev/null +++ b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/RECORD @@ -0,0 +1,30 @@ +Flask_WTF-0.14.2.dist-info/DESCRIPTION.rst,sha256=vyJWnOD4vgnZ6x2ERr5EH1l2uzLxXCBhr_O1L6Ell2E,584 +Flask_WTF-0.14.2.dist-info/LICENSE.txt,sha256=oHX42YrP2wXdmHFiQrniwbOrmHIpJrPEz2yRasFOg1A,1490 +Flask_WTF-0.14.2.dist-info/METADATA,sha256=M8ZfImxUciRZ5Av5r1x37JnEC3wG5sacQv346wmldHU,1846 +Flask_WTF-0.14.2.dist-info/RECORD,, +Flask_WTF-0.14.2.dist-info/WHEEL,sha256=5wvfB7GvgZAbKBSE9uX9Zbi6LCL-_KgezgHblXhCRnM,113 +Flask_WTF-0.14.2.dist-info/metadata.json,sha256=qGwhg5DSr2WilK8cvCcQsdrtDJ5NFgR1faLrO8YZCAY,1370 +Flask_WTF-0.14.2.dist-info/top_level.txt,sha256=zK3flQPSjYTkAMjB0V6Jhu3jyotC0biL1mMhzitYoog,10 +flask_wtf/__init__.py,sha256=zNLRzvfi7PLTc7jkqQT7pzgtsw9_9eN7BfO4fzwKxJc,406 +flask_wtf/_compat.py,sha256=4h1U_W5vbM9L8sJ4ZPFevuneM1TirnBTTVrsHRH3uUE,849 +flask_wtf/csrf.py,sha256=suKAZarzLIBuiJFqwP--RldEYabPj0DGfYkQA32Cc1E,11554 +flask_wtf/file.py,sha256=2UnODjSq47IjsFQMiu_z218vFA5pnQ9nL1FpX7hpK1M,2971 +flask_wtf/form.py,sha256=lpx-ItUnKjYOW8VxQpBAlbhoROJNd2PHi3v0loPPyYI,4948 +flask_wtf/html5.py,sha256=ReZHJto8DAZkO3BxUDdHnkyz5mM21KtqKYh0achJ5IM,372 +flask_wtf/i18n.py,sha256=xMB_jHCOaWfF1RXm7E6hsRHwPsUyVyKX2Rhy3tBOUgk,1790 +flask_wtf/recaptcha/__init__.py,sha256=q3TC7tZPSAZ3On3GApZKGn0EcydX4zprisbyTlhN3sQ,86 +flask_wtf/recaptcha/fields.py,sha256=kN_10iZYQcYg1EtxFp4B87BlFnnrJCktrh7bTykOVj4,453 +flask_wtf/recaptcha/validators.py,sha256=8UgjA72OxUyHVk_lm8-fGhPEvKgkMtsoFNt7yzjo0xw,2398 +flask_wtf/recaptcha/widgets.py,sha256=me-oaqMNPW2BLujNTuDHCXWcVhh6eI7wlm6_TIrIF_U,1267 +Flask_WTF-0.14.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +flask_wtf/recaptcha/__pycache__/fields.cpython-36.pyc,, +flask_wtf/recaptcha/__pycache__/validators.cpython-36.pyc,, +flask_wtf/recaptcha/__pycache__/widgets.cpython-36.pyc,, +flask_wtf/recaptcha/__pycache__/__init__.cpython-36.pyc,, +flask_wtf/__pycache__/csrf.cpython-36.pyc,, +flask_wtf/__pycache__/file.cpython-36.pyc,, +flask_wtf/__pycache__/form.cpython-36.pyc,, +flask_wtf/__pycache__/html5.cpython-36.pyc,, +flask_wtf/__pycache__/i18n.cpython-36.pyc,, +flask_wtf/__pycache__/_compat.cpython-36.pyc,, +flask_wtf/__pycache__/__init__.cpython-36.pyc,, diff --git a/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/WHEEL b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/WHEEL new file mode 100644 index 0000000..7bf9daa --- /dev/null +++ b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.30.0.a0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/metadata.json b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/metadata.json new file mode 100644 index 0000000..d48bac6 --- /dev/null +++ b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Flask", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules"], "extensions": {"python.details": {"contacts": [{"email": "me@lepture.com", "name": "Hsiaoming Yang", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "https://github.com/lepture/flask-wtf"}}}, "extras": [], "generator": "bdist_wheel (0.30.0.a0)", "license": "BSD", "metadata_version": "2.0", "name": "Flask-WTF", "platform": "any", "run_requires": [{"requires": ["Flask", "WTForms"]}], "summary": "Simple integration of Flask and WTForms.", "version": "0.14.2"} \ No newline at end of file diff --git a/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/top_level.txt b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/top_level.txt new file mode 100644 index 0000000..716f422 --- /dev/null +++ b/venv/Lib/site-packages/Flask_WTF-0.14.2.dist-info/top_level.txt @@ -0,0 +1 @@ +flask_wtf diff --git a/venv/Lib/site-packages/Jinja2-2.10.dist-info/DESCRIPTION.rst b/venv/Lib/site-packages/Jinja2-2.10.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..1594da5 --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-2.10.dist-info/DESCRIPTION.rst @@ -0,0 +1,37 @@ + +Jinja2 +~~~~~~ + +Jinja2 is a template engine written in pure Python. It provides a +`Django`_ inspired non-XML syntax but supports inline expressions and +an optional `sandboxed`_ environment. + +Nutshell +-------- + +Here a small example of a Jinja template:: + + {% extends 'base.html' %} + {% block title %}Memberlist{% endblock %} + {% block content %} + + {% endblock %} + +Philosophy +---------- + +Application logic is for the controller but don't try to make the life +for the template designer too hard by giving him too few functionality. + +For more informations visit the new `Jinja2 webpage`_ and `documentation`_. + +.. _sandboxed: https://en.wikipedia.org/wiki/Sandbox_(computer_security) +.. _Django: https://www.djangoproject.com/ +.. _Jinja2 webpage: http://jinja.pocoo.org/ +.. _documentation: http://jinja.pocoo.org/2/documentation/ + + diff --git a/venv/Lib/site-packages/Jinja2-2.10.dist-info/INSTALLER b/venv/Lib/site-packages/Jinja2-2.10.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-2.10.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Jinja2-2.10.dist-info/LICENSE.txt b/venv/Lib/site-packages/Jinja2-2.10.dist-info/LICENSE.txt new file mode 100644 index 0000000..31bf900 --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-2.10.dist-info/LICENSE.txt @@ -0,0 +1,31 @@ +Copyright (c) 2009 by the Jinja Team, see AUTHORS for more details. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/Jinja2-2.10.dist-info/METADATA b/venv/Lib/site-packages/Jinja2-2.10.dist-info/METADATA new file mode 100644 index 0000000..40f2b46 --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-2.10.dist-info/METADATA @@ -0,0 +1,68 @@ +Metadata-Version: 2.0 +Name: Jinja2 +Version: 2.10 +Summary: A small but fast and easy to use stand-alone template engine written in pure python. +Home-page: http://jinja.pocoo.org/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +License: BSD +Description-Content-Type: UNKNOWN +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Dist: MarkupSafe (>=0.23) +Provides-Extra: i18n +Requires-Dist: Babel (>=0.8); extra == 'i18n' + + +Jinja2 +~~~~~~ + +Jinja2 is a template engine written in pure Python. It provides a +`Django`_ inspired non-XML syntax but supports inline expressions and +an optional `sandboxed`_ environment. + +Nutshell +-------- + +Here a small example of a Jinja template:: + + {% extends 'base.html' %} + {% block title %}Memberlist{% endblock %} + {% block content %} + + {% endblock %} + +Philosophy +---------- + +Application logic is for the controller but don't try to make the life +for the template designer too hard by giving him too few functionality. + +For more informations visit the new `Jinja2 webpage`_ and `documentation`_. + +.. _sandboxed: https://en.wikipedia.org/wiki/Sandbox_(computer_security) +.. _Django: https://www.djangoproject.com/ +.. _Jinja2 webpage: http://jinja.pocoo.org/ +.. _documentation: http://jinja.pocoo.org/2/documentation/ + + diff --git a/venv/Lib/site-packages/Jinja2-2.10.dist-info/RECORD b/venv/Lib/site-packages/Jinja2-2.10.dist-info/RECORD new file mode 100644 index 0000000..4bef2c1 --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-2.10.dist-info/RECORD @@ -0,0 +1,63 @@ +Jinja2-2.10.dist-info/DESCRIPTION.rst,sha256=b5ckFDoM7vVtz_mAsJD4OPteFKCqE7beu353g4COoYI,978 +Jinja2-2.10.dist-info/LICENSE.txt,sha256=JvzUNv3Io51EiWrAPm8d_SXjhJnEjyDYvB3Tvwqqils,1554 +Jinja2-2.10.dist-info/METADATA,sha256=18EgU8zR6-av-0-5y_gXebzK4GnBB_76lALUsl-6QHM,2258 +Jinja2-2.10.dist-info/RECORD,, +Jinja2-2.10.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110 +Jinja2-2.10.dist-info/entry_points.txt,sha256=NdzVcOrqyNyKDxD09aERj__3bFx2paZhizFDsKmVhiA,72 +Jinja2-2.10.dist-info/metadata.json,sha256=NPUJ9TMBxVQAv_kTJzvU8HwmP-4XZvbK9mz6_4YUVl4,1473 +Jinja2-2.10.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 +jinja2/__init__.py,sha256=xJHjaMoy51_KXn1wf0cysH6tUUifUxZCwSOfcJGEYZw,2614 +jinja2/_compat.py,sha256=xP60CE5Qr8FTYcDE1f54tbZLKGvMwYml4-8T7Q4KG9k,2596 +jinja2/_identifier.py,sha256=W1QBSY-iJsyt6oR_nKSuNNCzV95vLIOYgUNPUI1d5gU,1726 +jinja2/asyncfilters.py,sha256=cTDPvrS8Hp_IkwsZ1m9af_lr5nHysw7uTa5gV0NmZVE,4144 +jinja2/asyncsupport.py,sha256=UErQ3YlTLaSjFb94P4MVn08-aVD9jJxty2JVfMRb-1M,7878 +jinja2/bccache.py,sha256=nQldx0ZRYANMyfvOihRoYFKSlUdd5vJkS7BjxNwlOZM,12794 +jinja2/compiler.py,sha256=BqC5U6JxObSRhblyT_a6Tp5GtEU5z3US1a4jLQaxxgo,65386 +jinja2/constants.py,sha256=uwwV8ZUhHhacAuz5PTwckfsbqBaqM7aKfyJL7kGX5YQ,1626 +jinja2/debug.py,sha256=WTVeUFGUa4v6ReCsYv-iVPa3pkNB75OinJt3PfxNdXs,12045 +jinja2/defaults.py,sha256=Em-95hmsJxIenDCZFB1YSvf9CNhe9rBmytN3yUrBcWA,1400 +jinja2/environment.py,sha256=VnkAkqw8JbjZct4tAyHlpBrka2vqB-Z58RAP-32P1ZY,50849 +jinja2/exceptions.py,sha256=_Rj-NVi98Q6AiEjYQOsP8dEIdu5AlmRHzcSNOPdWix4,4428 +jinja2/ext.py,sha256=atMQydEC86tN1zUsdQiHw5L5cF62nDbqGue25Yiu3N4,24500 +jinja2/filters.py,sha256=yOAJk0MsH-_gEC0i0U6NweVQhbtYaC-uE8xswHFLF4w,36528 +jinja2/idtracking.py,sha256=2GbDSzIvGArEBGLkovLkqEfmYxmWsEf8c3QZwM4uNsw,9197 +jinja2/lexer.py,sha256=ySEPoXd1g7wRjsuw23uimS6nkGN5aqrYwcOKxCaVMBQ,28559 +jinja2/loaders.py,sha256=xiTuURKAEObyym0nU8PCIXu_Qp8fn0AJ5oIADUUm-5Q,17382 +jinja2/meta.py,sha256=fmKHxkmZYAOm9QyWWy8EMd6eefAIh234rkBMW2X4ZR8,4340 +jinja2/nativetypes.py,sha256=_sJhS8f-8Q0QMIC0dm1YEdLyxEyoO-kch8qOL5xUDfE,7308 +jinja2/nodes.py,sha256=L10L_nQDfubLhO3XjpF9qz46FSh2clL-3e49ogVlMmA,30853 +jinja2/optimizer.py,sha256=MsdlFACJ0FRdPtjmCAdt7JQ9SGrXFaDNUaslsWQaG3M,1722 +jinja2/parser.py,sha256=lPzTEbcpTRBLw8ii6OYyExHeAhaZLMA05Hpv4ll3ULk,35875 +jinja2/runtime.py,sha256=DHdD38Pq8gj7uWQC5usJyWFoNWL317A9AvXOW_CLB34,27755 +jinja2/sandbox.py,sha256=TVyZHlNqqTzsv9fv2NvJNmSdWRHTguhyMHdxjWms32U,16708 +jinja2/tests.py,sha256=iJQLwbapZr-EKquTG_fVOVdwHUUKf3SX9eNkjQDF8oU,4237 +jinja2/utils.py,sha256=q24VupGZotQ-uOyrJxCaXtDWhZC1RgsQG7kcdmjck2Q,20629 +jinja2/visitor.py,sha256=JD1H1cANA29JcntFfN5fPyqQxB4bI4wC00BzZa-XHks,3316 +Jinja2-2.10.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jinja2/__pycache__/asyncfilters.cpython-36.pyc,, +jinja2/__pycache__/asyncsupport.cpython-36.pyc,, +jinja2/__pycache__/bccache.cpython-36.pyc,, +jinja2/__pycache__/compiler.cpython-36.pyc,, +jinja2/__pycache__/constants.cpython-36.pyc,, +jinja2/__pycache__/debug.cpython-36.pyc,, +jinja2/__pycache__/defaults.cpython-36.pyc,, +jinja2/__pycache__/environment.cpython-36.pyc,, +jinja2/__pycache__/exceptions.cpython-36.pyc,, +jinja2/__pycache__/ext.cpython-36.pyc,, +jinja2/__pycache__/filters.cpython-36.pyc,, +jinja2/__pycache__/idtracking.cpython-36.pyc,, +jinja2/__pycache__/lexer.cpython-36.pyc,, +jinja2/__pycache__/loaders.cpython-36.pyc,, +jinja2/__pycache__/meta.cpython-36.pyc,, +jinja2/__pycache__/nativetypes.cpython-36.pyc,, +jinja2/__pycache__/nodes.cpython-36.pyc,, +jinja2/__pycache__/optimizer.cpython-36.pyc,, +jinja2/__pycache__/parser.cpython-36.pyc,, +jinja2/__pycache__/runtime.cpython-36.pyc,, +jinja2/__pycache__/sandbox.cpython-36.pyc,, +jinja2/__pycache__/tests.cpython-36.pyc,, +jinja2/__pycache__/utils.cpython-36.pyc,, +jinja2/__pycache__/visitor.cpython-36.pyc,, +jinja2/__pycache__/_compat.cpython-36.pyc,, +jinja2/__pycache__/_identifier.cpython-36.pyc,, +jinja2/__pycache__/__init__.cpython-36.pyc,, diff --git a/venv/Lib/site-packages/Jinja2-2.10.dist-info/WHEEL b/venv/Lib/site-packages/Jinja2-2.10.dist-info/WHEEL new file mode 100644 index 0000000..7332a41 --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-2.10.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.30.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/Jinja2-2.10.dist-info/entry_points.txt b/venv/Lib/site-packages/Jinja2-2.10.dist-info/entry_points.txt new file mode 100644 index 0000000..32e6b75 --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-2.10.dist-info/entry_points.txt @@ -0,0 +1,4 @@ + + [babel.extractors] + jinja2 = jinja2.ext:babel_extract[i18n] + \ No newline at end of file diff --git a/venv/Lib/site-packages/Jinja2-2.10.dist-info/metadata.json b/venv/Lib/site-packages/Jinja2-2.10.dist-info/metadata.json new file mode 100644 index 0000000..7f5dc38 --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-2.10.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Markup :: HTML"], "description_content_type": "UNKNOWN", "extensions": {"python.details": {"contacts": [{"email": "armin.ronacher@active-4.com", "name": "Armin Ronacher", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "http://jinja.pocoo.org/"}}, "python.exports": {"babel.extractors": {"jinja2": "jinja2.ext:babel_extract [i18n]"}}}, "extras": ["i18n"], "generator": "bdist_wheel (0.30.0)", "license": "BSD", "metadata_version": "2.0", "name": "Jinja2", "run_requires": [{"extra": "i18n", "requires": ["Babel (>=0.8)"]}, {"requires": ["MarkupSafe (>=0.23)"]}], "summary": "A small but fast and easy to use stand-alone template engine written in pure python.", "version": "2.10"} \ No newline at end of file diff --git a/venv/Lib/site-packages/Jinja2-2.10.dist-info/top_level.txt b/venv/Lib/site-packages/Jinja2-2.10.dist-info/top_level.txt new file mode 100644 index 0000000..7f7afbf --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-2.10.dist-info/top_level.txt @@ -0,0 +1 @@ +jinja2 diff --git a/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/INSTALLER b/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/LICENSE.rst b/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/LICENSE.rst new file mode 100644 index 0000000..28c9258 --- /dev/null +++ b/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/LICENSE.rst @@ -0,0 +1,33 @@ +`BSD 3-Clause `_ + +Copyright © 2010 by the Pallets team. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/METADATA b/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/METADATA new file mode 100644 index 0000000..e532eb0 --- /dev/null +++ b/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/METADATA @@ -0,0 +1,103 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 1.1.0 +Summary: Safely add untrusted strings to HTML/XML markup. +Home-page: https://www.palletsprojects.com/p/markupsafe/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets Team +Maintainer-email: contact@palletsprojects.com +License: BSD +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Code, https://github.com/pallets/markupsafe +Project-URL: Issue tracker, https://github.com/pallets/markupsafe/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* + +MarkupSafe +========== + +MarkupSafe implements a text object that escapes characters so it is +safe to use in HTML and XML. Characters that have special meanings are +replaced so that they display as the actual characters. This mitigates +injection attacks, meaning untrusted user input can safely be displayed +on a page. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U MarkupSafe + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +Examples +-------- + +.. code-block:: pycon + + >>> from markupsafe import Markup, escape + >>> # escape replaces special characters and wraps in Markup + >>> escape('') + Markup(u'<script>alert(document.cookie);</script>') + >>> # wrap in Markup to mark text "safe" and prevent escaping + >>> Markup('Hello') + Markup('hello') + >>> escape(Markup('Hello')) + Markup('hello') + >>> # Markup is a text subclass (str on Python 3, unicode on Python 2) + >>> # methods and operators escape their arguments + >>> template = Markup("Hello %s") + >>> template % '"World"' + Markup('Hello "World"') + + +Donate +------ + +The Pallets organization develops and supports MarkupSafe and other +libraries that use it. In order to grow the community of contributors +and users, and allow the maintainers to devote more time to the +projects, `please donate today`_. + +.. _please donate today: https://psfmember.org/civicrm/contribute/transact?reset=1&id=20 + + +Links +----- + +* Website: https://www.palletsprojects.com/p/markupsafe/ +* Documentation: https://markupsafe.palletsprojects.com/ +* License: `BSD `_ +* Releases: https://pypi.org/project/MarkupSafe/ +* Code: https://github.com/pallets/markupsafe +* Issue tracker: https://github.com/pallets/markupsafe/issues +* Test status: + + * Linux, Mac: https://travis-ci.org/pallets/markupsafe + * Windows: https://ci.appveyor.com/project/pallets/markupsafe + +* Test coverage: https://codecov.io/gh/pallets/markupsafe + + diff --git a/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/RECORD b/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/RECORD new file mode 100644 index 0000000..edc8fc0 --- /dev/null +++ b/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/RECORD @@ -0,0 +1,15 @@ +markupsafe/__init__.py,sha256=T5J4pS7LRx1xRqfV3xz-QN_D9pSmfVDJnTrc2cTO4Ro,10164 +markupsafe/_compat.py,sha256=3oSvQpEFzsJ29NKVy-Fqk6ZlRxmlCB5k0G21aN0zNtQ,596 +markupsafe/_constants.py,sha256=ueEz1Jxdw5TKWBbhPr4Ad_2L2MSEh73AYiYe4l3cZy4,4728 +markupsafe/_native.py,sha256=fUrjjbRXIpHM-8l9QXFJ2xg5rv_48U2aN99plyL0kfs,1911 +markupsafe/_speedups.cp36-win_amd64.pyd,sha256=Irk9sta0ZpoWak-R75dyX99VYSYGPNpOhUj1k5PjCXc,15360 +MarkupSafe-1.1.0.dist-info/LICENSE.rst,sha256=7V249lpOdvRv2m6SF9gCDtq_nsg8tFpdeTdsWWM_g9M,1614 +MarkupSafe-1.1.0.dist-info/METADATA,sha256=usFnBges7tmAH4_Yt5Ypb8Bco4R9uLUdD0V6YHbvhLw,3585 +MarkupSafe-1.1.0.dist-info/WHEEL,sha256=sJhBKhJYGYSNowLlgnoOgHmlIo09mHmDvDK6wECJMKo,106 +MarkupSafe-1.1.0.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +MarkupSafe-1.1.0.dist-info/RECORD,, +MarkupSafe-1.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +markupsafe/__pycache__/_compat.cpython-36.pyc,, +markupsafe/__pycache__/_constants.cpython-36.pyc,, +markupsafe/__pycache__/_native.cpython-36.pyc,, +markupsafe/__pycache__/__init__.cpython-36.pyc,, diff --git a/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/WHEEL b/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/WHEEL new file mode 100644 index 0000000..289ef0f --- /dev/null +++ b/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.32.2) +Root-Is-Purelib: false +Tag: cp36-cp36m-win_amd64 + diff --git a/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/top_level.txt b/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/top_level.txt new file mode 100644 index 0000000..75bf729 --- /dev/null +++ b/venv/Lib/site-packages/MarkupSafe-1.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/PKG-INFO b/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/PKG-INFO new file mode 100644 index 0000000..24f94fb --- /dev/null +++ b/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/PKG-INFO @@ -0,0 +1,162 @@ +Metadata-Version: 1.1 +Name: SQLAlchemy +Version: 1.2.15 +Summary: Database Abstraction Library +Home-page: http://www.sqlalchemy.org +Author: Mike Bayer +Author-email: mike_mp@zzzcomputing.com +License: MIT License +Description: SQLAlchemy + ========== + + The Python SQL Toolkit and Object Relational Mapper + + Introduction + ------------- + + SQLAlchemy is the Python SQL toolkit and Object Relational Mapper + that gives application developers the full power and + flexibility of SQL. SQLAlchemy provides a full suite + of well known enterprise-level persistence patterns, + designed for efficient and high-performing database + access, adapted into a simple and Pythonic domain + language. + + Major SQLAlchemy features include: + + * An industrial strength ORM, built + from the core on the identity map, unit of work, + and data mapper patterns. These patterns + allow transparent persistence of objects + using a declarative configuration system. + Domain models + can be constructed and manipulated naturally, + and changes are synchronized with the + current transaction automatically. + * A relationally-oriented query system, exposing + the full range of SQL's capabilities + explicitly, including joins, subqueries, + correlation, and most everything else, + in terms of the object model. + Writing queries with the ORM uses the same + techniques of relational composition you use + when writing SQL. While you can drop into + literal SQL at any time, it's virtually never + needed. + * A comprehensive and flexible system + of eager loading for related collections and objects. + Collections are cached within a session, + and can be loaded on individual access, all + at once using joins, or by query per collection + across the full result set. + * A Core SQL construction system and DBAPI + interaction layer. The SQLAlchemy Core is + separate from the ORM and is a full database + abstraction layer in its own right, and includes + an extensible Python-based SQL expression + language, schema metadata, connection pooling, + type coercion, and custom types. + * All primary and foreign key constraints are + assumed to be composite and natural. Surrogate + integer primary keys are of course still the + norm, but SQLAlchemy never assumes or hardcodes + to this model. + * Database introspection and generation. Database + schemas can be "reflected" in one step into + Python structures representing database metadata; + those same structures can then generate + CREATE statements right back out - all within + the Core, independent of the ORM. + + SQLAlchemy's philosophy: + + * SQL databases behave less and less like object + collections the more size and performance start to + matter; object collections behave less and less like + tables and rows the more abstraction starts to matter. + SQLAlchemy aims to accommodate both of these + principles. + * An ORM doesn't need to hide the "R". A relational + database provides rich, set-based functionality + that should be fully exposed. SQLAlchemy's + ORM provides an open-ended set of patterns + that allow a developer to construct a custom + mediation layer between a domain model and + a relational schema, turning the so-called + "object relational impedance" issue into + a distant memory. + * The developer, in all cases, makes all decisions + regarding the design, structure, and naming conventions + of both the object model as well as the relational + schema. SQLAlchemy only provides the means + to automate the execution of these decisions. + * With SQLAlchemy, there's no such thing as + "the ORM generated a bad query" - you + retain full control over the structure of + queries, including how joins are organized, + how subqueries and correlation is used, what + columns are requested. Everything SQLAlchemy + does is ultimately the result of a developer- + initiated decision. + * Don't use an ORM if the problem doesn't need one. + SQLAlchemy consists of a Core and separate ORM + component. The Core offers a full SQL expression + language that allows Pythonic construction + of SQL constructs that render directly to SQL + strings for a target database, returning + result sets that are essentially enhanced DBAPI + cursors. + * Transactions should be the norm. With SQLAlchemy's + ORM, nothing goes to permanent storage until + commit() is called. SQLAlchemy encourages applications + to create a consistent means of delineating + the start and end of a series of operations. + * Never render a literal value in a SQL statement. + Bound parameters are used to the greatest degree + possible, allowing query optimizers to cache + query plans effectively and making SQL injection + attacks a non-issue. + + Documentation + ------------- + + Latest documentation is at: + + http://www.sqlalchemy.org/docs/ + + Installation / Requirements + --------------------------- + + Full documentation for installation is at + `Installation `_. + + Getting Help / Development / Bug reporting + ------------------------------------------ + + Please refer to the `SQLAlchemy Community Guide `_. + + Code of Conduct + --------------- + + Above all, SQLAlchemy places great emphasis on polite, thoughtful, and + constructive communication between users and developers. + Please see our current Code of Conduct at + `Code of Conduct `_. + + License + ------- + + SQLAlchemy is distributed under the `MIT license + `_. + + +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Database :: Front-Ends +Classifier: Operating System :: OS Independent diff --git a/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/SOURCES.txt b/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/SOURCES.txt new file mode 100644 index 0000000..941055d --- /dev/null +++ b/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/SOURCES.txt @@ -0,0 +1,838 @@ +AUTHORS +CHANGES +LICENSE +MANIFEST.in +README.dialects.rst +README.rst +README.unittests.rst +setup.cfg +setup.py +sqla_nose.py +tox.ini +doc/contents.html +doc/copyright.html +doc/errors.html +doc/genindex.html +doc/glossary.html +doc/index.html +doc/intro.html +doc/notfound.html +doc/search.html +doc/searchindex.js +doc/_images/sqla_arch_small.png +doc/_images/sqla_engine_arch.png +doc/_modules/index.html +doc/_modules/examples/adjacency_list/adjacency_list.html +doc/_modules/examples/association/basic_association.html +doc/_modules/examples/association/dict_of_sets_with_default.html +doc/_modules/examples/association/proxied_association.html +doc/_modules/examples/custom_attributes/active_column_defaults.html +doc/_modules/examples/custom_attributes/custom_management.html +doc/_modules/examples/custom_attributes/listen_for_events.html +doc/_modules/examples/dogpile_caching/advanced.html +doc/_modules/examples/dogpile_caching/caching_query.html +doc/_modules/examples/dogpile_caching/environment.html +doc/_modules/examples/dogpile_caching/fixture_data.html +doc/_modules/examples/dogpile_caching/helloworld.html +doc/_modules/examples/dogpile_caching/local_session_caching.html +doc/_modules/examples/dogpile_caching/model.html +doc/_modules/examples/dogpile_caching/relationship_caching.html +doc/_modules/examples/dynamic_dict/dynamic_dict.html +doc/_modules/examples/elementtree/adjacency_list.html +doc/_modules/examples/elementtree/optimized_al.html +doc/_modules/examples/elementtree/pickle.html +doc/_modules/examples/generic_associations/discriminator_on_association.html +doc/_modules/examples/generic_associations/generic_fk.html +doc/_modules/examples/generic_associations/table_per_association.html +doc/_modules/examples/generic_associations/table_per_related.html +doc/_modules/examples/graphs/directed_graph.html +doc/_modules/examples/inheritance/concrete.html +doc/_modules/examples/inheritance/joined.html +doc/_modules/examples/inheritance/single.html +doc/_modules/examples/join_conditions/cast.html +doc/_modules/examples/join_conditions/threeway.html +doc/_modules/examples/large_collection/large_collection.html +doc/_modules/examples/materialized_paths/materialized_paths.html +doc/_modules/examples/nested_sets/nested_sets.html +doc/_modules/examples/performance/__main__.html +doc/_modules/examples/performance/bulk_inserts.html +doc/_modules/examples/performance/bulk_updates.html +doc/_modules/examples/performance/large_resultsets.html +doc/_modules/examples/performance/short_selects.html +doc/_modules/examples/performance/single_inserts.html +doc/_modules/examples/postgis/postgis.html +doc/_modules/examples/sharding/attribute_shard.html +doc/_modules/examples/versioned_history/history_meta.html +doc/_modules/examples/versioned_history/test_versioning.html +doc/_modules/examples/versioned_rows/versioned_map.html +doc/_modules/examples/versioned_rows/versioned_rows.html +doc/_modules/examples/versioned_rows/versioned_rows_w_versionid.html +doc/_modules/examples/versioned_rows/versioned_update_old_row.html +doc/_modules/examples/vertical/dictlike-polymorphic.html +doc/_modules/examples/vertical/dictlike.html +doc/_static/basic.css +doc/_static/changelog.css +doc/_static/comment-bright.png +doc/_static/comment-close.png +doc/_static/comment.png +doc/_static/detectmobile.js +doc/_static/docs.css +doc/_static/doctools.js +doc/_static/documentation_options.js +doc/_static/down-pressed.png +doc/_static/down.png +doc/_static/dragons.png +doc/_static/file.png +doc/_static/init.js +doc/_static/jquery-3.2.1.js +doc/_static/jquery.js +doc/_static/minus.png +doc/_static/plus.png +doc/_static/pygments.css +doc/_static/searchtools.js +doc/_static/sphinx_paramlinks.css +doc/_static/underscore-1.3.1.js +doc/_static/underscore.js +doc/_static/up-pressed.png +doc/_static/up.png +doc/_static/websupport.js +doc/build/Makefile +doc/build/conf.py +doc/build/contents.rst +doc/build/copyright.rst +doc/build/corrections.py +doc/build/errors.rst +doc/build/glossary.rst +doc/build/index.rst +doc/build/intro.rst +doc/build/requirements.txt +doc/build/sqla_arch_small.png +doc/build/changelog/changelog_01.rst +doc/build/changelog/changelog_02.rst +doc/build/changelog/changelog_03.rst +doc/build/changelog/changelog_04.rst +doc/build/changelog/changelog_05.rst +doc/build/changelog/changelog_06.rst +doc/build/changelog/changelog_07.rst +doc/build/changelog/changelog_08.rst +doc/build/changelog/changelog_09.rst +doc/build/changelog/changelog_10.rst +doc/build/changelog/changelog_11.rst +doc/build/changelog/changelog_12.rst +doc/build/changelog/index.rst +doc/build/changelog/migration_04.rst +doc/build/changelog/migration_05.rst +doc/build/changelog/migration_06.rst +doc/build/changelog/migration_07.rst +doc/build/changelog/migration_08.rst +doc/build/changelog/migration_09.rst +doc/build/changelog/migration_10.rst +doc/build/changelog/migration_11.rst +doc/build/changelog/migration_12.rst +doc/build/changelog/unreleased_10/4065.rst +doc/build/changelog/unreleased_10/README.txt +doc/build/changelog/unreleased_11/README.txt +doc/build/changelog/unreleased_12/README.txt +doc/build/core/api_basics.rst +doc/build/core/compiler.rst +doc/build/core/connections.rst +doc/build/core/constraints.rst +doc/build/core/custom_types.rst +doc/build/core/ddl.rst +doc/build/core/defaults.rst +doc/build/core/dml.rst +doc/build/core/engines.rst +doc/build/core/engines_connections.rst +doc/build/core/event.rst +doc/build/core/events.rst +doc/build/core/exceptions.rst +doc/build/core/expression_api.rst +doc/build/core/functions.rst +doc/build/core/index.rst +doc/build/core/inspection.rst +doc/build/core/interfaces.rst +doc/build/core/internals.rst +doc/build/core/metadata.rst +doc/build/core/pooling.rst +doc/build/core/reflection.rst +doc/build/core/schema.rst +doc/build/core/selectable.rst +doc/build/core/serializer.rst +doc/build/core/sqla_engine_arch.png +doc/build/core/sqlelement.rst +doc/build/core/tutorial.rst +doc/build/core/type_api.rst +doc/build/core/type_basics.rst +doc/build/core/types.rst +doc/build/dialects/firebird.rst +doc/build/dialects/index.rst +doc/build/dialects/mssql.rst +doc/build/dialects/mysql.rst +doc/build/dialects/oracle.rst +doc/build/dialects/postgresql.rst +doc/build/dialects/sqlite.rst +doc/build/dialects/sybase.rst +doc/build/faq/connections.rst +doc/build/faq/index.rst +doc/build/faq/metadata_schema.rst +doc/build/faq/ormconfiguration.rst +doc/build/faq/performance.rst +doc/build/faq/sessions.rst +doc/build/faq/sqlexpressions.rst +doc/build/orm/backref.rst +doc/build/orm/basic_relationships.rst +doc/build/orm/cascades.rst +doc/build/orm/classical.rst +doc/build/orm/collections.rst +doc/build/orm/composites.rst +doc/build/orm/constructors.rst +doc/build/orm/contextual.rst +doc/build/orm/deprecated.rst +doc/build/orm/events.rst +doc/build/orm/examples.rst +doc/build/orm/exceptions.rst +doc/build/orm/extending.rst +doc/build/orm/index.rst +doc/build/orm/inheritance.rst +doc/build/orm/inheritance_loading.rst +doc/build/orm/internals.rst +doc/build/orm/join_conditions.rst +doc/build/orm/loading.rst +doc/build/orm/loading_columns.rst +doc/build/orm/loading_objects.rst +doc/build/orm/loading_relationships.rst +doc/build/orm/mapped_attributes.rst +doc/build/orm/mapped_sql_expr.rst +doc/build/orm/mapper_config.rst +doc/build/orm/mapping_api.rst +doc/build/orm/mapping_columns.rst +doc/build/orm/mapping_styles.rst +doc/build/orm/nonstandard_mappings.rst +doc/build/orm/persistence_techniques.rst +doc/build/orm/query.rst +doc/build/orm/relationship_api.rst +doc/build/orm/relationship_persistence.rst +doc/build/orm/relationships.rst +doc/build/orm/scalar_mapping.rst +doc/build/orm/self_referential.rst +doc/build/orm/session.rst +doc/build/orm/session_api.rst +doc/build/orm/session_basics.rst +doc/build/orm/session_events.rst +doc/build/orm/session_state_management.rst +doc/build/orm/session_transaction.rst +doc/build/orm/tutorial.rst +doc/build/orm/versioning.rst +doc/build/orm/extensions/associationproxy.rst +doc/build/orm/extensions/automap.rst +doc/build/orm/extensions/baked.rst +doc/build/orm/extensions/horizontal_shard.rst +doc/build/orm/extensions/hybrid.rst +doc/build/orm/extensions/index.rst +doc/build/orm/extensions/indexable.rst +doc/build/orm/extensions/instrumentation.rst +doc/build/orm/extensions/mutable.rst +doc/build/orm/extensions/orderinglist.rst +doc/build/orm/extensions/declarative/api.rst +doc/build/orm/extensions/declarative/basic_use.rst +doc/build/orm/extensions/declarative/index.rst +doc/build/orm/extensions/declarative/inheritance.rst +doc/build/orm/extensions/declarative/mixins.rst +doc/build/orm/extensions/declarative/relationships.rst +doc/build/orm/extensions/declarative/table_config.rst +doc/build/texinputs/Makefile +doc/build/texinputs/sphinx.sty +doc/changelog/changelog_01.html +doc/changelog/changelog_02.html +doc/changelog/changelog_03.html +doc/changelog/changelog_04.html +doc/changelog/changelog_05.html +doc/changelog/changelog_06.html +doc/changelog/changelog_07.html +doc/changelog/changelog_08.html +doc/changelog/changelog_09.html +doc/changelog/changelog_10.html +doc/changelog/changelog_11.html +doc/changelog/changelog_12.html +doc/changelog/index.html +doc/changelog/migration_04.html +doc/changelog/migration_05.html +doc/changelog/migration_06.html +doc/changelog/migration_07.html +doc/changelog/migration_08.html +doc/changelog/migration_09.html +doc/changelog/migration_10.html +doc/changelog/migration_11.html +doc/changelog/migration_12.html +doc/core/api_basics.html +doc/core/compiler.html +doc/core/connections.html +doc/core/constraints.html +doc/core/custom_types.html +doc/core/ddl.html +doc/core/defaults.html +doc/core/dml.html +doc/core/engines.html +doc/core/engines_connections.html +doc/core/event.html +doc/core/events.html +doc/core/exceptions.html +doc/core/expression_api.html +doc/core/functions.html +doc/core/index.html +doc/core/inspection.html +doc/core/interfaces.html +doc/core/internals.html +doc/core/metadata.html +doc/core/pooling.html +doc/core/reflection.html +doc/core/schema.html +doc/core/selectable.html +doc/core/serializer.html +doc/core/sqlelement.html +doc/core/tutorial.html +doc/core/type_api.html +doc/core/type_basics.html +doc/core/types.html +doc/dialects/firebird.html +doc/dialects/index.html +doc/dialects/mssql.html +doc/dialects/mysql.html +doc/dialects/oracle.html +doc/dialects/postgresql.html +doc/dialects/sqlite.html +doc/dialects/sybase.html +doc/faq/connections.html +doc/faq/index.html +doc/faq/metadata_schema.html +doc/faq/ormconfiguration.html +doc/faq/performance.html +doc/faq/sessions.html +doc/faq/sqlexpressions.html +doc/orm/backref.html +doc/orm/basic_relationships.html +doc/orm/cascades.html +doc/orm/classical.html +doc/orm/collections.html +doc/orm/composites.html +doc/orm/constructors.html +doc/orm/contextual.html +doc/orm/deprecated.html +doc/orm/events.html +doc/orm/examples.html +doc/orm/exceptions.html +doc/orm/extending.html +doc/orm/index.html +doc/orm/inheritance.html +doc/orm/inheritance_loading.html +doc/orm/internals.html +doc/orm/join_conditions.html +doc/orm/loading.html +doc/orm/loading_columns.html +doc/orm/loading_objects.html +doc/orm/loading_relationships.html +doc/orm/mapped_attributes.html +doc/orm/mapped_sql_expr.html +doc/orm/mapper_config.html +doc/orm/mapping_api.html +doc/orm/mapping_columns.html +doc/orm/mapping_styles.html +doc/orm/nonstandard_mappings.html +doc/orm/persistence_techniques.html +doc/orm/query.html +doc/orm/relationship_api.html +doc/orm/relationship_persistence.html +doc/orm/relationships.html +doc/orm/scalar_mapping.html +doc/orm/self_referential.html +doc/orm/session.html +doc/orm/session_api.html +doc/orm/session_basics.html +doc/orm/session_events.html +doc/orm/session_state_management.html +doc/orm/session_transaction.html +doc/orm/tutorial.html +doc/orm/versioning.html +doc/orm/extensions/associationproxy.html +doc/orm/extensions/automap.html +doc/orm/extensions/baked.html +doc/orm/extensions/horizontal_shard.html +doc/orm/extensions/hybrid.html +doc/orm/extensions/index.html +doc/orm/extensions/indexable.html +doc/orm/extensions/instrumentation.html +doc/orm/extensions/mutable.html +doc/orm/extensions/orderinglist.html +doc/orm/extensions/declarative/api.html +doc/orm/extensions/declarative/basic_use.html +doc/orm/extensions/declarative/index.html +doc/orm/extensions/declarative/inheritance.html +doc/orm/extensions/declarative/mixins.html +doc/orm/extensions/declarative/relationships.html +doc/orm/extensions/declarative/table_config.html +examples/__init__.py +examples/adjacency_list/__init__.py +examples/adjacency_list/adjacency_list.py +examples/association/__init__.py +examples/association/basic_association.py +examples/association/dict_of_sets_with_default.py +examples/association/proxied_association.py +examples/custom_attributes/__init__.py +examples/custom_attributes/active_column_defaults.py +examples/custom_attributes/custom_management.py +examples/custom_attributes/listen_for_events.py +examples/dogpile_caching/__init__.py +examples/dogpile_caching/advanced.py +examples/dogpile_caching/caching_query.py +examples/dogpile_caching/environment.py +examples/dogpile_caching/fixture_data.py +examples/dogpile_caching/helloworld.py +examples/dogpile_caching/local_session_caching.py +examples/dogpile_caching/model.py +examples/dogpile_caching/relationship_caching.py +examples/dynamic_dict/__init__.py +examples/dynamic_dict/dynamic_dict.py +examples/elementtree/__init__.py +examples/elementtree/adjacency_list.py +examples/elementtree/optimized_al.py +examples/elementtree/pickle.py +examples/elementtree/test.xml +examples/elementtree/test2.xml +examples/elementtree/test3.xml +examples/generic_associations/__init__.py +examples/generic_associations/discriminator_on_association.py +examples/generic_associations/generic_fk.py +examples/generic_associations/table_per_association.py +examples/generic_associations/table_per_related.py +examples/graphs/__init__.py +examples/graphs/directed_graph.py +examples/inheritance/__init__.py +examples/inheritance/concrete.py +examples/inheritance/joined.py +examples/inheritance/single.py +examples/join_conditions/__init__.py +examples/join_conditions/cast.py +examples/join_conditions/threeway.py +examples/large_collection/__init__.py +examples/large_collection/large_collection.py +examples/materialized_paths/__init__.py +examples/materialized_paths/materialized_paths.py +examples/nested_sets/__init__.py +examples/nested_sets/nested_sets.py +examples/performance/__init__.py +examples/performance/__main__.py +examples/performance/bulk_inserts.py +examples/performance/bulk_updates.py +examples/performance/large_resultsets.py +examples/performance/short_selects.py +examples/performance/single_inserts.py +examples/postgis/__init__.py +examples/postgis/postgis.py +examples/sharding/__init__.py +examples/sharding/attribute_shard.py +examples/versioned_history/__init__.py +examples/versioned_history/history_meta.py +examples/versioned_history/test_versioning.py +examples/versioned_rows/__init__.py +examples/versioned_rows/versioned_map.py +examples/versioned_rows/versioned_rows.py +examples/versioned_rows/versioned_rows_w_versionid.py +examples/versioned_rows/versioned_update_old_row.py +examples/vertical/__init__.py +examples/vertical/dictlike-polymorphic.py +examples/vertical/dictlike.py +lib/sqlalchemy/cextension/processors.c +lib/sqlalchemy/cextension/resultproxy.c +lib/sqlalchemy/cextension/utils.c +lib/SQLAlchemy.egg-info/PKG-INFO +lib/SQLAlchemy.egg-info/SOURCES.txt +lib/SQLAlchemy.egg-info/dependency_links.txt +lib/SQLAlchemy.egg-info/requires.txt +lib/SQLAlchemy.egg-info/top_level.txt +lib/sqlalchemy/__init__.py +lib/sqlalchemy/events.py +lib/sqlalchemy/exc.py +lib/sqlalchemy/inspection.py +lib/sqlalchemy/interfaces.py +lib/sqlalchemy/log.py +lib/sqlalchemy/pool.py +lib/sqlalchemy/processors.py +lib/sqlalchemy/schema.py +lib/sqlalchemy/types.py +lib/sqlalchemy/cextension/processors.c +lib/sqlalchemy/cextension/resultproxy.c +lib/sqlalchemy/cextension/utils.c +lib/sqlalchemy/connectors/__init__.py +lib/sqlalchemy/connectors/mxodbc.py +lib/sqlalchemy/connectors/pyodbc.py +lib/sqlalchemy/connectors/zxJDBC.py +lib/sqlalchemy/databases/__init__.py +lib/sqlalchemy/dialects/__init__.py +lib/sqlalchemy/dialects/type_migration_guidelines.txt +lib/sqlalchemy/dialects/firebird/__init__.py +lib/sqlalchemy/dialects/firebird/base.py +lib/sqlalchemy/dialects/firebird/fdb.py +lib/sqlalchemy/dialects/firebird/kinterbasdb.py +lib/sqlalchemy/dialects/mssql/__init__.py +lib/sqlalchemy/dialects/mssql/adodbapi.py +lib/sqlalchemy/dialects/mssql/base.py +lib/sqlalchemy/dialects/mssql/information_schema.py +lib/sqlalchemy/dialects/mssql/mxodbc.py +lib/sqlalchemy/dialects/mssql/pymssql.py +lib/sqlalchemy/dialects/mssql/pyodbc.py +lib/sqlalchemy/dialects/mssql/zxjdbc.py +lib/sqlalchemy/dialects/mysql/__init__.py +lib/sqlalchemy/dialects/mysql/base.py +lib/sqlalchemy/dialects/mysql/cymysql.py +lib/sqlalchemy/dialects/mysql/dml.py +lib/sqlalchemy/dialects/mysql/enumerated.py +lib/sqlalchemy/dialects/mysql/gaerdbms.py +lib/sqlalchemy/dialects/mysql/json.py +lib/sqlalchemy/dialects/mysql/mysqlconnector.py +lib/sqlalchemy/dialects/mysql/mysqldb.py +lib/sqlalchemy/dialects/mysql/oursql.py +lib/sqlalchemy/dialects/mysql/pymysql.py +lib/sqlalchemy/dialects/mysql/pyodbc.py +lib/sqlalchemy/dialects/mysql/reflection.py +lib/sqlalchemy/dialects/mysql/types.py +lib/sqlalchemy/dialects/mysql/zxjdbc.py +lib/sqlalchemy/dialects/oracle/__init__.py +lib/sqlalchemy/dialects/oracle/base.py +lib/sqlalchemy/dialects/oracle/cx_oracle.py +lib/sqlalchemy/dialects/oracle/zxjdbc.py +lib/sqlalchemy/dialects/postgresql/__init__.py +lib/sqlalchemy/dialects/postgresql/array.py +lib/sqlalchemy/dialects/postgresql/base.py +lib/sqlalchemy/dialects/postgresql/dml.py +lib/sqlalchemy/dialects/postgresql/ext.py +lib/sqlalchemy/dialects/postgresql/hstore.py +lib/sqlalchemy/dialects/postgresql/json.py +lib/sqlalchemy/dialects/postgresql/pg8000.py +lib/sqlalchemy/dialects/postgresql/psycopg2.py +lib/sqlalchemy/dialects/postgresql/psycopg2cffi.py +lib/sqlalchemy/dialects/postgresql/pygresql.py +lib/sqlalchemy/dialects/postgresql/pypostgresql.py +lib/sqlalchemy/dialects/postgresql/ranges.py +lib/sqlalchemy/dialects/postgresql/zxjdbc.py +lib/sqlalchemy/dialects/sqlite/__init__.py +lib/sqlalchemy/dialects/sqlite/base.py +lib/sqlalchemy/dialects/sqlite/pysqlcipher.py +lib/sqlalchemy/dialects/sqlite/pysqlite.py +lib/sqlalchemy/dialects/sybase/__init__.py +lib/sqlalchemy/dialects/sybase/base.py +lib/sqlalchemy/dialects/sybase/mxodbc.py +lib/sqlalchemy/dialects/sybase/pyodbc.py +lib/sqlalchemy/dialects/sybase/pysybase.py +lib/sqlalchemy/engine/__init__.py +lib/sqlalchemy/engine/base.py +lib/sqlalchemy/engine/default.py +lib/sqlalchemy/engine/interfaces.py +lib/sqlalchemy/engine/reflection.py +lib/sqlalchemy/engine/result.py +lib/sqlalchemy/engine/strategies.py +lib/sqlalchemy/engine/threadlocal.py +lib/sqlalchemy/engine/url.py +lib/sqlalchemy/engine/util.py +lib/sqlalchemy/event/__init__.py +lib/sqlalchemy/event/api.py +lib/sqlalchemy/event/attr.py +lib/sqlalchemy/event/base.py +lib/sqlalchemy/event/legacy.py +lib/sqlalchemy/event/registry.py +lib/sqlalchemy/ext/__init__.py +lib/sqlalchemy/ext/associationproxy.py +lib/sqlalchemy/ext/automap.py +lib/sqlalchemy/ext/baked.py +lib/sqlalchemy/ext/compiler.py +lib/sqlalchemy/ext/horizontal_shard.py +lib/sqlalchemy/ext/hybrid.py +lib/sqlalchemy/ext/indexable.py +lib/sqlalchemy/ext/instrumentation.py +lib/sqlalchemy/ext/mutable.py +lib/sqlalchemy/ext/orderinglist.py +lib/sqlalchemy/ext/serializer.py +lib/sqlalchemy/ext/declarative/__init__.py +lib/sqlalchemy/ext/declarative/api.py +lib/sqlalchemy/ext/declarative/base.py +lib/sqlalchemy/ext/declarative/clsregistry.py +lib/sqlalchemy/orm/__init__.py +lib/sqlalchemy/orm/attributes.py +lib/sqlalchemy/orm/base.py +lib/sqlalchemy/orm/collections.py +lib/sqlalchemy/orm/dependency.py +lib/sqlalchemy/orm/deprecated_interfaces.py +lib/sqlalchemy/orm/descriptor_props.py +lib/sqlalchemy/orm/dynamic.py +lib/sqlalchemy/orm/evaluator.py +lib/sqlalchemy/orm/events.py +lib/sqlalchemy/orm/exc.py +lib/sqlalchemy/orm/identity.py +lib/sqlalchemy/orm/instrumentation.py +lib/sqlalchemy/orm/interfaces.py +lib/sqlalchemy/orm/loading.py +lib/sqlalchemy/orm/mapper.py +lib/sqlalchemy/orm/path_registry.py +lib/sqlalchemy/orm/persistence.py +lib/sqlalchemy/orm/properties.py +lib/sqlalchemy/orm/query.py +lib/sqlalchemy/orm/relationships.py +lib/sqlalchemy/orm/scoping.py +lib/sqlalchemy/orm/session.py +lib/sqlalchemy/orm/state.py +lib/sqlalchemy/orm/strategies.py +lib/sqlalchemy/orm/strategy_options.py +lib/sqlalchemy/orm/sync.py +lib/sqlalchemy/orm/unitofwork.py +lib/sqlalchemy/orm/util.py +lib/sqlalchemy/sql/__init__.py +lib/sqlalchemy/sql/annotation.py +lib/sqlalchemy/sql/base.py +lib/sqlalchemy/sql/compiler.py +lib/sqlalchemy/sql/crud.py +lib/sqlalchemy/sql/ddl.py +lib/sqlalchemy/sql/default_comparator.py +lib/sqlalchemy/sql/dml.py +lib/sqlalchemy/sql/elements.py +lib/sqlalchemy/sql/expression.py +lib/sqlalchemy/sql/functions.py +lib/sqlalchemy/sql/naming.py +lib/sqlalchemy/sql/operators.py +lib/sqlalchemy/sql/schema.py +lib/sqlalchemy/sql/selectable.py +lib/sqlalchemy/sql/sqltypes.py +lib/sqlalchemy/sql/type_api.py +lib/sqlalchemy/sql/util.py +lib/sqlalchemy/sql/visitors.py +lib/sqlalchemy/testing/__init__.py +lib/sqlalchemy/testing/assertions.py +lib/sqlalchemy/testing/assertsql.py +lib/sqlalchemy/testing/config.py +lib/sqlalchemy/testing/engines.py +lib/sqlalchemy/testing/entities.py +lib/sqlalchemy/testing/exclusions.py +lib/sqlalchemy/testing/fixtures.py +lib/sqlalchemy/testing/mock.py +lib/sqlalchemy/testing/pickleable.py +lib/sqlalchemy/testing/profiling.py +lib/sqlalchemy/testing/provision.py +lib/sqlalchemy/testing/replay_fixture.py +lib/sqlalchemy/testing/requirements.py +lib/sqlalchemy/testing/runner.py +lib/sqlalchemy/testing/schema.py +lib/sqlalchemy/testing/util.py +lib/sqlalchemy/testing/warnings.py +lib/sqlalchemy/testing/plugin/__init__.py +lib/sqlalchemy/testing/plugin/bootstrap.py +lib/sqlalchemy/testing/plugin/noseplugin.py +lib/sqlalchemy/testing/plugin/plugin_base.py +lib/sqlalchemy/testing/plugin/pytestplugin.py +lib/sqlalchemy/testing/suite/__init__.py +lib/sqlalchemy/testing/suite/test_cte.py +lib/sqlalchemy/testing/suite/test_ddl.py +lib/sqlalchemy/testing/suite/test_dialect.py +lib/sqlalchemy/testing/suite/test_insert.py +lib/sqlalchemy/testing/suite/test_reflection.py +lib/sqlalchemy/testing/suite/test_results.py +lib/sqlalchemy/testing/suite/test_select.py +lib/sqlalchemy/testing/suite/test_sequence.py +lib/sqlalchemy/testing/suite/test_types.py +lib/sqlalchemy/testing/suite/test_update_delete.py +lib/sqlalchemy/util/__init__.py +lib/sqlalchemy/util/_collections.py +lib/sqlalchemy/util/compat.py +lib/sqlalchemy/util/deprecations.py +lib/sqlalchemy/util/langhelpers.py +lib/sqlalchemy/util/queue.py +lib/sqlalchemy/util/topological.py +test/__init__.py +test/binary_data_one.dat +test/binary_data_two.dat +test/conftest.py +test/requirements.py +test/aaa_profiling/__init__.py +test/aaa_profiling/test_compiler.py +test/aaa_profiling/test_memusage.py +test/aaa_profiling/test_orm.py +test/aaa_profiling/test_pool.py +test/aaa_profiling/test_resultset.py +test/aaa_profiling/test_zoomark.py +test/aaa_profiling/test_zoomark_orm.py +test/base/__init__.py +test/base/test_dependency.py +test/base/test_events.py +test/base/test_except.py +test/base/test_inspect.py +test/base/test_tutorials.py +test/base/test_utils.py +test/dialect/__init__.py +test/dialect/test_all.py +test/dialect/test_firebird.py +test/dialect/test_mxodbc.py +test/dialect/test_pyodbc.py +test/dialect/test_sqlite.py +test/dialect/test_suite.py +test/dialect/test_sybase.py +test/dialect/mssql/__init__.py +test/dialect/mssql/test_compiler.py +test/dialect/mssql/test_engine.py +test/dialect/mssql/test_query.py +test/dialect/mssql/test_reflection.py +test/dialect/mssql/test_types.py +test/dialect/mysql/__init__.py +test/dialect/mysql/test_compiler.py +test/dialect/mysql/test_dialect.py +test/dialect/mysql/test_on_duplicate.py +test/dialect/mysql/test_query.py +test/dialect/mysql/test_reflection.py +test/dialect/mysql/test_types.py +test/dialect/oracle/__init__.py +test/dialect/oracle/test_compiler.py +test/dialect/oracle/test_dialect.py +test/dialect/oracle/test_reflection.py +test/dialect/oracle/test_types.py +test/dialect/postgresql/__init__.py +test/dialect/postgresql/test_compiler.py +test/dialect/postgresql/test_dialect.py +test/dialect/postgresql/test_on_conflict.py +test/dialect/postgresql/test_query.py +test/dialect/postgresql/test_reflection.py +test/dialect/postgresql/test_types.py +test/engine/__init__.py +test/engine/test_bind.py +test/engine/test_ddlevents.py +test/engine/test_execute.py +test/engine/test_logging.py +test/engine/test_parseconnect.py +test/engine/test_pool.py +test/engine/test_processors.py +test/engine/test_reconnect.py +test/engine/test_reflection.py +test/engine/test_transaction.py +test/ext/__init__.py +test/ext/test_associationproxy.py +test/ext/test_automap.py +test/ext/test_baked.py +test/ext/test_compiler.py +test/ext/test_extendedattr.py +test/ext/test_horizontal_shard.py +test/ext/test_hybrid.py +test/ext/test_indexable.py +test/ext/test_mutable.py +test/ext/test_orderinglist.py +test/ext/test_serializer.py +test/ext/declarative/__init__.py +test/ext/declarative/test_basic.py +test/ext/declarative/test_clsregistry.py +test/ext/declarative/test_inheritance.py +test/ext/declarative/test_mixin.py +test/ext/declarative/test_reflection.py +test/orm/__init__.py +test/orm/_fixtures.py +test/orm/test_association.py +test/orm/test_assorted_eager.py +test/orm/test_attributes.py +test/orm/test_backref_mutations.py +test/orm/test_bind.py +test/orm/test_bulk.py +test/orm/test_bundle.py +test/orm/test_cascade.py +test/orm/test_collection.py +test/orm/test_compile.py +test/orm/test_composites.py +test/orm/test_cycles.py +test/orm/test_default_strategies.py +test/orm/test_defaults.py +test/orm/test_deferred.py +test/orm/test_deprecations.py +test/orm/test_descriptor.py +test/orm/test_dynamic.py +test/orm/test_eager_relations.py +test/orm/test_evaluator.py +test/orm/test_events.py +test/orm/test_expire.py +test/orm/test_froms.py +test/orm/test_generative.py +test/orm/test_hasparent.py +test/orm/test_immediate_load.py +test/orm/test_inspect.py +test/orm/test_instrumentation.py +test/orm/test_joins.py +test/orm/test_lazy_relations.py +test/orm/test_load_on_fks.py +test/orm/test_loading.py +test/orm/test_lockmode.py +test/orm/test_manytomany.py +test/orm/test_mapper.py +test/orm/test_merge.py +test/orm/test_naturalpks.py +test/orm/test_of_type.py +test/orm/test_onetoone.py +test/orm/test_options.py +test/orm/test_pickled.py +test/orm/test_query.py +test/orm/test_rel_fn.py +test/orm/test_relationships.py +test/orm/test_scoping.py +test/orm/test_selectable.py +test/orm/test_selectin_relations.py +test/orm/test_session.py +test/orm/test_subquery_relations.py +test/orm/test_sync.py +test/orm/test_transaction.py +test/orm/test_unitofwork.py +test/orm/test_unitofworkv2.py +test/orm/test_update_delete.py +test/orm/test_utils.py +test/orm/test_validators.py +test/orm/test_versioning.py +test/orm/inheritance/__init__.py +test/orm/inheritance/_poly_fixtures.py +test/orm/inheritance/test_abc_inheritance.py +test/orm/inheritance/test_abc_polymorphic.py +test/orm/inheritance/test_assorted_poly.py +test/orm/inheritance/test_basic.py +test/orm/inheritance/test_concrete.py +test/orm/inheritance/test_magazine.py +test/orm/inheritance/test_manytomany.py +test/orm/inheritance/test_poly_linked_list.py +test/orm/inheritance/test_poly_loading.py +test/orm/inheritance/test_poly_persistence.py +test/orm/inheritance/test_polymorphic_rel.py +test/orm/inheritance/test_productspec.py +test/orm/inheritance/test_relationship.py +test/orm/inheritance/test_selects.py +test/orm/inheritance/test_single.py +test/orm/inheritance/test_with_poly.py +test/perf/invalidate_stresstest.py +test/perf/orm2010.py +test/sql/__init__.py +test/sql/test_case_statement.py +test/sql/test_compiler.py +test/sql/test_constraints.py +test/sql/test_cte.py +test/sql/test_ddlemit.py +test/sql/test_defaults.py +test/sql/test_delete.py +test/sql/test_functions.py +test/sql/test_generative.py +test/sql/test_insert.py +test/sql/test_insert_exec.py +test/sql/test_inspect.py +test/sql/test_join_rewriting.py +test/sql/test_labels.py +test/sql/test_lateral.py +test/sql/test_metadata.py +test/sql/test_operators.py +test/sql/test_query.py +test/sql/test_quote.py +test/sql/test_resultset.py +test/sql/test_returning.py +test/sql/test_rowcount.py +test/sql/test_selectable.py +test/sql/test_tablesample.py +test/sql/test_text.py +test/sql/test_type_expressions.py +test/sql/test_types.py +test/sql/test_unicode.py +test/sql/test_update.py +test/sql/test_utils.py \ No newline at end of file diff --git a/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/dependency_links.txt b/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/installed-files.txt b/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/installed-files.txt new file mode 100644 index 0000000..28a1d65 --- /dev/null +++ b/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/installed-files.txt @@ -0,0 +1,390 @@ +..\sqlalchemy\events.py +..\sqlalchemy\exc.py +..\sqlalchemy\inspection.py +..\sqlalchemy\interfaces.py +..\sqlalchemy\log.py +..\sqlalchemy\pool.py +..\sqlalchemy\processors.py +..\sqlalchemy\schema.py +..\sqlalchemy\types.py +..\sqlalchemy\__init__.py +..\sqlalchemy\connectors\mxodbc.py +..\sqlalchemy\connectors\pyodbc.py +..\sqlalchemy\connectors\zxJDBC.py +..\sqlalchemy\connectors\__init__.py +..\sqlalchemy\databases\__init__.py +..\sqlalchemy\dialects\__init__.py +..\sqlalchemy\engine\base.py +..\sqlalchemy\engine\default.py +..\sqlalchemy\engine\interfaces.py +..\sqlalchemy\engine\reflection.py +..\sqlalchemy\engine\result.py +..\sqlalchemy\engine\strategies.py +..\sqlalchemy\engine\threadlocal.py +..\sqlalchemy\engine\url.py +..\sqlalchemy\engine\util.py +..\sqlalchemy\engine\__init__.py +..\sqlalchemy\event\api.py +..\sqlalchemy\event\attr.py +..\sqlalchemy\event\base.py +..\sqlalchemy\event\legacy.py +..\sqlalchemy\event\registry.py +..\sqlalchemy\event\__init__.py +..\sqlalchemy\ext\associationproxy.py +..\sqlalchemy\ext\automap.py +..\sqlalchemy\ext\baked.py +..\sqlalchemy\ext\compiler.py +..\sqlalchemy\ext\horizontal_shard.py +..\sqlalchemy\ext\hybrid.py +..\sqlalchemy\ext\indexable.py +..\sqlalchemy\ext\instrumentation.py +..\sqlalchemy\ext\mutable.py +..\sqlalchemy\ext\orderinglist.py +..\sqlalchemy\ext\serializer.py +..\sqlalchemy\ext\__init__.py +..\sqlalchemy\orm\attributes.py +..\sqlalchemy\orm\base.py +..\sqlalchemy\orm\collections.py +..\sqlalchemy\orm\dependency.py +..\sqlalchemy\orm\deprecated_interfaces.py +..\sqlalchemy\orm\descriptor_props.py +..\sqlalchemy\orm\dynamic.py +..\sqlalchemy\orm\evaluator.py +..\sqlalchemy\orm\events.py +..\sqlalchemy\orm\exc.py +..\sqlalchemy\orm\identity.py +..\sqlalchemy\orm\instrumentation.py +..\sqlalchemy\orm\interfaces.py +..\sqlalchemy\orm\loading.py +..\sqlalchemy\orm\mapper.py +..\sqlalchemy\orm\path_registry.py +..\sqlalchemy\orm\persistence.py +..\sqlalchemy\orm\properties.py +..\sqlalchemy\orm\query.py +..\sqlalchemy\orm\relationships.py +..\sqlalchemy\orm\scoping.py +..\sqlalchemy\orm\session.py +..\sqlalchemy\orm\state.py +..\sqlalchemy\orm\strategies.py +..\sqlalchemy\orm\strategy_options.py +..\sqlalchemy\orm\sync.py +..\sqlalchemy\orm\unitofwork.py +..\sqlalchemy\orm\util.py +..\sqlalchemy\orm\__init__.py +..\sqlalchemy\sql\annotation.py +..\sqlalchemy\sql\base.py +..\sqlalchemy\sql\compiler.py +..\sqlalchemy\sql\crud.py +..\sqlalchemy\sql\ddl.py +..\sqlalchemy\sql\default_comparator.py +..\sqlalchemy\sql\dml.py +..\sqlalchemy\sql\elements.py +..\sqlalchemy\sql\expression.py +..\sqlalchemy\sql\functions.py +..\sqlalchemy\sql\naming.py +..\sqlalchemy\sql\operators.py +..\sqlalchemy\sql\schema.py +..\sqlalchemy\sql\selectable.py +..\sqlalchemy\sql\sqltypes.py +..\sqlalchemy\sql\type_api.py +..\sqlalchemy\sql\util.py +..\sqlalchemy\sql\visitors.py +..\sqlalchemy\sql\__init__.py +..\sqlalchemy\testing\assertions.py +..\sqlalchemy\testing\assertsql.py +..\sqlalchemy\testing\config.py +..\sqlalchemy\testing\engines.py +..\sqlalchemy\testing\entities.py +..\sqlalchemy\testing\exclusions.py +..\sqlalchemy\testing\fixtures.py +..\sqlalchemy\testing\mock.py +..\sqlalchemy\testing\pickleable.py +..\sqlalchemy\testing\profiling.py +..\sqlalchemy\testing\provision.py +..\sqlalchemy\testing\replay_fixture.py +..\sqlalchemy\testing\requirements.py +..\sqlalchemy\testing\runner.py +..\sqlalchemy\testing\schema.py +..\sqlalchemy\testing\util.py +..\sqlalchemy\testing\warnings.py +..\sqlalchemy\testing\__init__.py +..\sqlalchemy\util\compat.py +..\sqlalchemy\util\deprecations.py +..\sqlalchemy\util\langhelpers.py +..\sqlalchemy\util\queue.py +..\sqlalchemy\util\topological.py +..\sqlalchemy\util\_collections.py +..\sqlalchemy\util\__init__.py +..\sqlalchemy\dialects\firebird\base.py +..\sqlalchemy\dialects\firebird\fdb.py +..\sqlalchemy\dialects\firebird\kinterbasdb.py +..\sqlalchemy\dialects\firebird\__init__.py +..\sqlalchemy\dialects\mssql\adodbapi.py +..\sqlalchemy\dialects\mssql\base.py +..\sqlalchemy\dialects\mssql\information_schema.py +..\sqlalchemy\dialects\mssql\mxodbc.py +..\sqlalchemy\dialects\mssql\pymssql.py +..\sqlalchemy\dialects\mssql\pyodbc.py +..\sqlalchemy\dialects\mssql\zxjdbc.py +..\sqlalchemy\dialects\mssql\__init__.py +..\sqlalchemy\dialects\mysql\base.py +..\sqlalchemy\dialects\mysql\cymysql.py +..\sqlalchemy\dialects\mysql\dml.py +..\sqlalchemy\dialects\mysql\enumerated.py +..\sqlalchemy\dialects\mysql\gaerdbms.py +..\sqlalchemy\dialects\mysql\json.py +..\sqlalchemy\dialects\mysql\mysqlconnector.py +..\sqlalchemy\dialects\mysql\mysqldb.py +..\sqlalchemy\dialects\mysql\oursql.py +..\sqlalchemy\dialects\mysql\pymysql.py +..\sqlalchemy\dialects\mysql\pyodbc.py +..\sqlalchemy\dialects\mysql\reflection.py +..\sqlalchemy\dialects\mysql\types.py +..\sqlalchemy\dialects\mysql\zxjdbc.py +..\sqlalchemy\dialects\mysql\__init__.py +..\sqlalchemy\dialects\oracle\base.py +..\sqlalchemy\dialects\oracle\cx_oracle.py +..\sqlalchemy\dialects\oracle\zxjdbc.py +..\sqlalchemy\dialects\oracle\__init__.py +..\sqlalchemy\dialects\postgresql\array.py +..\sqlalchemy\dialects\postgresql\base.py +..\sqlalchemy\dialects\postgresql\dml.py +..\sqlalchemy\dialects\postgresql\ext.py +..\sqlalchemy\dialects\postgresql\hstore.py +..\sqlalchemy\dialects\postgresql\json.py +..\sqlalchemy\dialects\postgresql\pg8000.py +..\sqlalchemy\dialects\postgresql\psycopg2.py +..\sqlalchemy\dialects\postgresql\psycopg2cffi.py +..\sqlalchemy\dialects\postgresql\pygresql.py +..\sqlalchemy\dialects\postgresql\pypostgresql.py +..\sqlalchemy\dialects\postgresql\ranges.py +..\sqlalchemy\dialects\postgresql\zxjdbc.py +..\sqlalchemy\dialects\postgresql\__init__.py +..\sqlalchemy\dialects\sqlite\base.py +..\sqlalchemy\dialects\sqlite\pysqlcipher.py +..\sqlalchemy\dialects\sqlite\pysqlite.py +..\sqlalchemy\dialects\sqlite\__init__.py +..\sqlalchemy\dialects\sybase\base.py +..\sqlalchemy\dialects\sybase\mxodbc.py +..\sqlalchemy\dialects\sybase\pyodbc.py +..\sqlalchemy\dialects\sybase\pysybase.py +..\sqlalchemy\dialects\sybase\__init__.py +..\sqlalchemy\ext\declarative\api.py +..\sqlalchemy\ext\declarative\base.py +..\sqlalchemy\ext\declarative\clsregistry.py +..\sqlalchemy\ext\declarative\__init__.py +..\sqlalchemy\testing\plugin\bootstrap.py +..\sqlalchemy\testing\plugin\noseplugin.py +..\sqlalchemy\testing\plugin\plugin_base.py +..\sqlalchemy\testing\plugin\pytestplugin.py +..\sqlalchemy\testing\plugin\__init__.py +..\sqlalchemy\testing\suite\test_cte.py +..\sqlalchemy\testing\suite\test_ddl.py +..\sqlalchemy\testing\suite\test_dialect.py +..\sqlalchemy\testing\suite\test_insert.py +..\sqlalchemy\testing\suite\test_reflection.py +..\sqlalchemy\testing\suite\test_results.py +..\sqlalchemy\testing\suite\test_select.py +..\sqlalchemy\testing\suite\test_sequence.py +..\sqlalchemy\testing\suite\test_types.py +..\sqlalchemy\testing\suite\test_update_delete.py +..\sqlalchemy\testing\suite\__init__.py +..\sqlalchemy\__pycache__\events.cpython-36.pyc +..\sqlalchemy\__pycache__\exc.cpython-36.pyc +..\sqlalchemy\__pycache__\inspection.cpython-36.pyc +..\sqlalchemy\__pycache__\interfaces.cpython-36.pyc +..\sqlalchemy\__pycache__\log.cpython-36.pyc +..\sqlalchemy\__pycache__\pool.cpython-36.pyc +..\sqlalchemy\__pycache__\processors.cpython-36.pyc +..\sqlalchemy\__pycache__\schema.cpython-36.pyc +..\sqlalchemy\__pycache__\types.cpython-36.pyc +..\sqlalchemy\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\connectors\__pycache__\mxodbc.cpython-36.pyc +..\sqlalchemy\connectors\__pycache__\pyodbc.cpython-36.pyc +..\sqlalchemy\connectors\__pycache__\zxJDBC.cpython-36.pyc +..\sqlalchemy\connectors\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\databases\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\dialects\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\engine\__pycache__\base.cpython-36.pyc +..\sqlalchemy\engine\__pycache__\default.cpython-36.pyc +..\sqlalchemy\engine\__pycache__\interfaces.cpython-36.pyc +..\sqlalchemy\engine\__pycache__\reflection.cpython-36.pyc +..\sqlalchemy\engine\__pycache__\result.cpython-36.pyc +..\sqlalchemy\engine\__pycache__\strategies.cpython-36.pyc +..\sqlalchemy\engine\__pycache__\threadlocal.cpython-36.pyc +..\sqlalchemy\engine\__pycache__\url.cpython-36.pyc +..\sqlalchemy\engine\__pycache__\util.cpython-36.pyc +..\sqlalchemy\engine\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\event\__pycache__\api.cpython-36.pyc +..\sqlalchemy\event\__pycache__\attr.cpython-36.pyc +..\sqlalchemy\event\__pycache__\base.cpython-36.pyc +..\sqlalchemy\event\__pycache__\legacy.cpython-36.pyc +..\sqlalchemy\event\__pycache__\registry.cpython-36.pyc +..\sqlalchemy\event\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\ext\__pycache__\associationproxy.cpython-36.pyc +..\sqlalchemy\ext\__pycache__\automap.cpython-36.pyc +..\sqlalchemy\ext\__pycache__\baked.cpython-36.pyc +..\sqlalchemy\ext\__pycache__\compiler.cpython-36.pyc +..\sqlalchemy\ext\__pycache__\horizontal_shard.cpython-36.pyc +..\sqlalchemy\ext\__pycache__\hybrid.cpython-36.pyc +..\sqlalchemy\ext\__pycache__\indexable.cpython-36.pyc +..\sqlalchemy\ext\__pycache__\instrumentation.cpython-36.pyc +..\sqlalchemy\ext\__pycache__\mutable.cpython-36.pyc +..\sqlalchemy\ext\__pycache__\orderinglist.cpython-36.pyc +..\sqlalchemy\ext\__pycache__\serializer.cpython-36.pyc +..\sqlalchemy\ext\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\attributes.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\base.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\collections.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\dependency.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\deprecated_interfaces.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\descriptor_props.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\dynamic.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\evaluator.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\events.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\exc.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\identity.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\instrumentation.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\interfaces.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\loading.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\mapper.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\path_registry.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\persistence.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\properties.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\query.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\relationships.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\scoping.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\session.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\state.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\strategies.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\strategy_options.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\sync.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\unitofwork.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\util.cpython-36.pyc +..\sqlalchemy\orm\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\annotation.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\base.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\compiler.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\crud.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\ddl.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\default_comparator.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\dml.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\elements.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\expression.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\functions.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\naming.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\operators.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\schema.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\selectable.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\sqltypes.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\type_api.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\util.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\visitors.cpython-36.pyc +..\sqlalchemy\sql\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\assertions.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\assertsql.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\config.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\engines.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\entities.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\exclusions.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\fixtures.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\mock.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\pickleable.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\profiling.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\provision.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\replay_fixture.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\requirements.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\runner.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\schema.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\util.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\warnings.cpython-36.pyc +..\sqlalchemy\testing\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\util\__pycache__\compat.cpython-36.pyc +..\sqlalchemy\util\__pycache__\deprecations.cpython-36.pyc +..\sqlalchemy\util\__pycache__\langhelpers.cpython-36.pyc +..\sqlalchemy\util\__pycache__\queue.cpython-36.pyc +..\sqlalchemy\util\__pycache__\topological.cpython-36.pyc +..\sqlalchemy\util\__pycache__\_collections.cpython-36.pyc +..\sqlalchemy\util\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\dialects\firebird\__pycache__\base.cpython-36.pyc +..\sqlalchemy\dialects\firebird\__pycache__\fdb.cpython-36.pyc +..\sqlalchemy\dialects\firebird\__pycache__\kinterbasdb.cpython-36.pyc +..\sqlalchemy\dialects\firebird\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\dialects\mssql\__pycache__\adodbapi.cpython-36.pyc +..\sqlalchemy\dialects\mssql\__pycache__\base.cpython-36.pyc +..\sqlalchemy\dialects\mssql\__pycache__\information_schema.cpython-36.pyc +..\sqlalchemy\dialects\mssql\__pycache__\mxodbc.cpython-36.pyc +..\sqlalchemy\dialects\mssql\__pycache__\pymssql.cpython-36.pyc +..\sqlalchemy\dialects\mssql\__pycache__\pyodbc.cpython-36.pyc +..\sqlalchemy\dialects\mssql\__pycache__\zxjdbc.cpython-36.pyc +..\sqlalchemy\dialects\mssql\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\base.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\cymysql.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\dml.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\enumerated.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\gaerdbms.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\json.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\mysqlconnector.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\mysqldb.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\oursql.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\pymysql.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\pyodbc.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\reflection.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\types.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\zxjdbc.cpython-36.pyc +..\sqlalchemy\dialects\mysql\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\dialects\oracle\__pycache__\base.cpython-36.pyc +..\sqlalchemy\dialects\oracle\__pycache__\cx_oracle.cpython-36.pyc +..\sqlalchemy\dialects\oracle\__pycache__\zxjdbc.cpython-36.pyc +..\sqlalchemy\dialects\oracle\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\array.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\base.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\dml.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\ext.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\hstore.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\json.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\pg8000.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\psycopg2.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\psycopg2cffi.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\pygresql.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\pypostgresql.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\ranges.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\zxjdbc.cpython-36.pyc +..\sqlalchemy\dialects\postgresql\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\dialects\sqlite\__pycache__\base.cpython-36.pyc +..\sqlalchemy\dialects\sqlite\__pycache__\pysqlcipher.cpython-36.pyc +..\sqlalchemy\dialects\sqlite\__pycache__\pysqlite.cpython-36.pyc +..\sqlalchemy\dialects\sqlite\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\dialects\sybase\__pycache__\base.cpython-36.pyc +..\sqlalchemy\dialects\sybase\__pycache__\mxodbc.cpython-36.pyc +..\sqlalchemy\dialects\sybase\__pycache__\pyodbc.cpython-36.pyc +..\sqlalchemy\dialects\sybase\__pycache__\pysybase.cpython-36.pyc +..\sqlalchemy\dialects\sybase\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\ext\declarative\__pycache__\api.cpython-36.pyc +..\sqlalchemy\ext\declarative\__pycache__\base.cpython-36.pyc +..\sqlalchemy\ext\declarative\__pycache__\clsregistry.cpython-36.pyc +..\sqlalchemy\ext\declarative\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\testing\plugin\__pycache__\bootstrap.cpython-36.pyc +..\sqlalchemy\testing\plugin\__pycache__\noseplugin.cpython-36.pyc +..\sqlalchemy\testing\plugin\__pycache__\plugin_base.cpython-36.pyc +..\sqlalchemy\testing\plugin\__pycache__\pytestplugin.cpython-36.pyc +..\sqlalchemy\testing\plugin\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\testing\suite\__pycache__\test_cte.cpython-36.pyc +..\sqlalchemy\testing\suite\__pycache__\test_ddl.cpython-36.pyc +..\sqlalchemy\testing\suite\__pycache__\test_dialect.cpython-36.pyc +..\sqlalchemy\testing\suite\__pycache__\test_insert.cpython-36.pyc +..\sqlalchemy\testing\suite\__pycache__\test_reflection.cpython-36.pyc +..\sqlalchemy\testing\suite\__pycache__\test_results.cpython-36.pyc +..\sqlalchemy\testing\suite\__pycache__\test_select.cpython-36.pyc +..\sqlalchemy\testing\suite\__pycache__\test_sequence.cpython-36.pyc +..\sqlalchemy\testing\suite\__pycache__\test_types.cpython-36.pyc +..\sqlalchemy\testing\suite\__pycache__\test_update_delete.cpython-36.pyc +..\sqlalchemy\testing\suite\__pycache__\__init__.cpython-36.pyc +..\sqlalchemy\cprocessors.cp36-win_amd64.pyd +..\sqlalchemy\cresultproxy.cp36-win_amd64.pyd +..\sqlalchemy\cutils.cp36-win_amd64.pyd +dependency_links.txt +PKG-INFO +requires.txt +SOURCES.txt +top_level.txt diff --git a/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/requires.txt b/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/requires.txt new file mode 100644 index 0000000..141a2e8 --- /dev/null +++ b/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/requires.txt @@ -0,0 +1,27 @@ + +[mssql_pymssql] +pymssql + +[mssql_pyodbc] +pyodbc + +[mysql] +mysqlclient + +[oracle] +cx_oracle + +[postgresql] +psycopg2 + +[postgresql_pg8000] +pg8000 + +[postgresql_psycopg2binary] +psycopg2-binary + +[postgresql_psycopg2cffi] +psycopg2cffi + +[pymysql] +pymysql diff --git a/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/top_level.txt b/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/top_level.txt new file mode 100644 index 0000000..39fb2be --- /dev/null +++ b/venv/Lib/site-packages/SQLAlchemy-1.2.15-py3.6.egg-info/top_level.txt @@ -0,0 +1 @@ +sqlalchemy diff --git a/venv/Lib/site-packages/WTForms-2.2.1.dist-info/INSTALLER b/venv/Lib/site-packages/WTForms-2.2.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/WTForms-2.2.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/WTForms-2.2.1.dist-info/METADATA b/venv/Lib/site-packages/WTForms-2.2.1.dist-info/METADATA new file mode 100644 index 0000000..8a77324 --- /dev/null +++ b/venv/Lib/site-packages/WTForms-2.2.1.dist-info/METADATA @@ -0,0 +1,101 @@ +Metadata-Version: 2.1 +Name: WTForms +Version: 2.2.1 +Summary: A flexible forms validation and rendering library for Python web development. +Home-page: https://wtforms.readthedocs.io/ +Author: Thomas Johansson, James Crasta +Author-email: wtforms@simplecodes.com +Maintainer: WTForms team +Maintainer-email: davidism@gmail.com +License: BSD +Project-URL: Documentation, https://wtforms.readthedocs.io/ +Project-URL: Code, https://github.com/wtforms/wtforms +Project-URL: Issue tracker, https://github.com/wtforms/wtforms/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Provides-Extra: locale +Requires-Dist: ordereddict; python_version=="2.6" +Provides-Extra: locale +Requires-Dist: Babel (>=1.3); extra == 'locale' + +WTForms +======= + +WTForms is a flexible forms validation and rendering library for Python +web development. It is `framework agnostic`_ and can work with whatever +web framework and template engine you choose. There are various +community libraries that provide closer integration with popular +frameworks. + +To get started using WTForms, we recommend reading the `crash course`_ +in the docs. + +.. _crash course: https://wtforms.readthedocs.io/en/stable/crash_course.html +.. _framework agnostic: https://wtforms.readthedocs.io/en/stable/faq.html#does-wtforms-work-with-library-here + + +Installation +------------ + +Install and update using pip:: + + pip install -U WTForms + + +Third-Party Library Integrations +-------------------------------- + +WTForms is designed to work with any web framework and template engine. +There are a number of community-provided libraries that make integrating +with frameworks even better. + +- `Flask-WTF`_ integrates with the Flask framework. It can + automatically load data from the request, uses Flask-Babel to + translate based on user-selected locale, provides full-application + CSRF, and more. +- `WTForms-Alchemy`_ provides rich support for generating forms from + SQLAlchemy models, including an expanded set of fields and + validators. +- `WTForms-SQLAlchemy`_ provides ORM-backed fields and form generation + from SQLAlchemy models. +- `WTForms-AppEngine`_ provides ORM-backed fields and form generation + from AppEnding db/ndb schema +- `WTForms-AppEngine`_ provides ORM-backed fields and form generation + from Django models, as well as integration with Django's I18N + support. + +.. _Flask-WTF: https://flask-wtf.readthedocs.io/ +.. _WTForms-Alchemy: https://wtforms-alchemy.readthedocs.io/ +.. _WTForms-SQLAlchemy: https://github.com/wtforms/wtforms-sqlalchemy +.. _WTForms-AppEngine: https://github.com/wtforms/wtforms-appengine +.. _WTForms-Django: https://github.com/wtforms/wtforms-django + + +Links +----- + +- Documentation: https://wtforms.readthedocs.io/ +- License: `BSD `_ +- Releases: https://pypi.org/project/WTForms/ +- Code: https://github.com/wtforms/wtforms +- Issue tracker: https://github.com/wtforms/wtforms/issues +- Test status: + + - Linux: https://travis-ci.org/wtforms/wtforms + +- Test coverage: https://coveralls.io/github/wtforms/wtforms + + diff --git a/venv/Lib/site-packages/WTForms-2.2.1.dist-info/RECORD b/venv/Lib/site-packages/WTForms-2.2.1.dist-info/RECORD new file mode 100644 index 0000000..84c5436 --- /dev/null +++ b/venv/Lib/site-packages/WTForms-2.2.1.dist-info/RECORD @@ -0,0 +1,147 @@ +WTForms-2.2.1.dist-info/METADATA,sha256=Aqv5s_FPo1o3VxjnX-nclKn2dBPIVOpTwggPPH-DJs0,3771 +WTForms-2.2.1.dist-info/RECORD,, +WTForms-2.2.1.dist-info/WHEEL,sha256=gduuPyBvFJQSQ0zdyxF7k0zynDXbIbvg5ZBHoXum5uk,110 +WTForms-2.2.1.dist-info/top_level.txt,sha256=k5K62RAEkLEN23p118t3tRgvL6I_k56NiIU7Hk8Phv8,8 +wtforms/__init__.py,sha256=h4gmUHtk1Y9cGJ-l63rhrp-nC9REGdpcRPBGoJKP9hk,380 +wtforms/compat.py,sha256=buY-q7yLNO-2OlxA5QPAcdBO8urjZTtxvFnxg_1Euuo,589 +wtforms/form.py,sha256=ahME3_8CmTuvVsatV-AKqinBkOSEnLOE_nMeQLgrQEA,11608 +wtforms/i18n.py,sha256=RuMPdvfsxHGMqKySUy4DpMfEAzruPK_7gHe6GQTrekc,2175 +wtforms/meta.py,sha256=9yLQuKP4N_OiPBsPy3tBc7auldxhFryZweySDsKL8zI,3822 +wtforms/utils.py,sha256=Zg70vKv96pnHjrkSZ6KlzSo1noh20GV5IqfPy6FrOyA,1504 +wtforms/validators.py,sha256=niMtYGGRijIiZ2ruslYfRP7CTGDul_DHiR-iYen7zRg,19430 +wtforms/csrf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wtforms/csrf/core.py,sha256=Ot8eOSAZ88qeDBlSUhRqiLfyWA13g3EFJ4zWZ7EGYnc,3157 +wtforms/csrf/session.py,sha256=baww8MJ5YObyYItXX0Vz5AjxZTdOfTqti3zsD3koka0,3056 +wtforms/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wtforms/ext/appengine/__init__.py,sha256=xXkE1qkwzkkBw4o0YhWGZSZXcsV60DaLxX4fkxNcNe8,269 +wtforms/ext/appengine/db.py,sha256=IEJng34ztXLVSlLxneZ7M4kgGOZOPf9zR_6RTqv6Z1Q,18588 +wtforms/ext/appengine/fields.py,sha256=8Z2BJy7ft0fu_vZksneZ7xdVxdqHkWIMNjgnyfdKtho,7574 +wtforms/ext/appengine/ndb.py,sha256=szIwWA5FyD2lqZefayl__C2UsXMEAGQndqPYPhOH4Vk,17124 +wtforms/ext/csrf/__init__.py,sha256=bIQ48rbnoYrYPZkkGz04b_7PZ8leQY_CExEqYw8yitI,45 +wtforms/ext/csrf/fields.py,sha256=Ta3vLg9KQkpUTCnDF-7CP3IW11X0UqqhvL68sAopYTs,430 +wtforms/ext/csrf/form.py,sha256=ZxmvC3Um2qYeUncu6D390-W62mVQclzwPLP9_R7GedU,1785 +wtforms/ext/csrf/session.py,sha256=aKYb9_jgEmxIgvWuk0cdx9YAGTi9s3F4xy_0ibxyhbo,2627 +wtforms/ext/dateutil/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wtforms/ext/dateutil/fields.py,sha256=RlupqB1WX_HiKJEYqi9IAxiCElxgbBDHHuXrGF4nbYs,3429 +wtforms/ext/django/__init__.py,sha256=OQ0wr3s5_cUmUU7htHXhobyxVWJS16Ve4qBK_PLs_rw,259 +wtforms/ext/django/fields.py,sha256=pEWxaAtMq5_p8QaJPOffWsX7U4LB5f8Bq8ZBw4fedxk,4580 +wtforms/ext/django/i18n.py,sha256=VLvzJ8lQOqs5Uxnhe4aOE5StGgPEvGhfBEHNrRQFtp0,626 +wtforms/ext/django/orm.py,sha256=Mme5i_o_bJTXGKkabRz03EJmGggPMejAg95XNhYtNUc,6096 +wtforms/ext/django/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wtforms/ext/django/templatetags/wtforms.py,sha256=iCOicSMEkixm5bcJHz35Zx0h6xVwnz1H9JglB_hU69o,2826 +wtforms/ext/i18n/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wtforms/ext/i18n/form.py,sha256=mfsavr4LGI1GhoFLsWSuSqVPHH6QNiyqoAfY94u-XP0,1608 +wtforms/ext/i18n/utils.py,sha256=rx9-pNYjIp8DLU-VQ9XxRSXHYZuFv4ktRejzVBPTDBg,530 +wtforms/ext/sqlalchemy/__init__.py,sha256=4U9BzeiFD_YF8pXRsTehei0ekP6jikt2bX4MN3GNT9s,431 +wtforms/ext/sqlalchemy/fields.py,sha256=XwOgJUJCcXvw-QGdF6q2w51m1CI4E_COq8GXb9blgI0,6846 +wtforms/ext/sqlalchemy/orm.py,sha256=6wJN-Zm4YB3st9xsXU5xJR5jQUsdSRqcbEZ7JvvGD9s,10671 +wtforms/fields/__init__.py,sha256=M-0pFfY9EEk-GoYzRkg3yvarM_iP_cRhPjpLEl5KgVU,219 +wtforms/fields/core.py,sha256=KevHc47k4mMJgRGe8Y07UrS_9o_nzXbn3U2HznpdMI0,34307 +wtforms/fields/html5.py,sha256=bwLHIBrEWICRcS80am_lBp6GitDCVIRvBdIWEPJeSz0,1995 +wtforms/fields/simple.py,sha256=dY7cYfb6PXMDjUefXcDeTDWpv3UGyr_BMlebJAeoRso,2218 +wtforms/locale/README.md,sha256=xL3Ain6UPZK3UdL8tMrIKwfodEsPT0IYCVDpI6do524,1062 +wtforms/locale/wtforms.pot,sha256=Sqe4LRpObVRUc30htYXgZuueKYfW7wt2lNVKtM_Jrr0,4170 +wtforms/locale/ar/LC_MESSAGES/wtforms.mo,sha256=r1DDYnBCr1hT7KwEG3NpQLR52i4j_-er5ENIVqT9Sbo,4530 +wtforms/locale/ar/LC_MESSAGES/wtforms.po,sha256=Qkhg_pS-ZEf7jEZz76mDC47UPpqWcU_8t7L88ALAPvk,6262 +wtforms/locale/bg/LC_MESSAGES/wtforms.mo,sha256=aPnglyINf0hH4FGUM3U5OJpqcJT_8XRx6GiaD4Jif3g,4297 +wtforms/locale/bg/LC_MESSAGES/wtforms.po,sha256=xflJaMOGUTNN7zbFMWL-FbMVjmj-Svmvkek84mJl5NI,6356 +wtforms/locale/ca/LC_MESSAGES/wtforms.mo,sha256=zBX48Ru44A2O82FXwC9CwzU3_FiFkUyb4KGNya4toSg,3425 +wtforms/locale/ca/LC_MESSAGES/wtforms.po,sha256=oT09ydRQNsmf0a1uwskao0wfbwQqAh2tKXjFqI_iscw,5465 +wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.mo,sha256=MJQPoiMNPfdHYX5eQQ2OW7PmvQ9BFETva2qm3xmPSvo,3618 +wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.po,sha256=MZ1Iv28-oX4dqzSPgGo65YU3iijeBmYBKZSGsl8YYS0,5596 +wtforms/locale/cy/LC_MESSAGES/wtforms.mo,sha256=8pJPG9dguZLej33ksWSwWmCOKIJ7VmpNVlaDMb30_lc,3371 +wtforms/locale/cy/LC_MESSAGES/wtforms.po,sha256=DTGkDUWJ1MsZqFPV8YhwHaBI1uJP6uXwiud7K3LW1yw,5415 +wtforms/locale/de/LC_MESSAGES/wtforms.mo,sha256=D4BRsJeeT_cKYagO7W1LHQ8YpwC2c7_0hbv3tDgk82E,3412 +wtforms/locale/de/LC_MESSAGES/wtforms.po,sha256=BF7F3vwQOAL_yaZTHi7x2KZnaCTzz3MNUNCtuc6e47A,5457 +wtforms/locale/de_CH/LC_MESSAGES/wtforms.mo,sha256=lBUgz2N_AlkXB4W-CxaNGuHdwhgTrYCPtwM9DWL-pP0,3418 +wtforms/locale/de_CH/LC_MESSAGES/wtforms.po,sha256=LiAqravsNbETdXHJiOi3vJD4o3hWrTRZWSHcLNvHjgc,5477 +wtforms/locale/el/LC_MESSAGES/wtforms.mo,sha256=r0_oQGB_KYBZdSmFsielQMCF0P7rgsLDCA28u37XAkw,4307 +wtforms/locale/el/LC_MESSAGES/wtforms.po,sha256=snlBcC-cjlFdpIbSG9pRGYlWFhl1EaQX72Umv2PWfp8,6345 +wtforms/locale/en/LC_MESSAGES/wtforms.mo,sha256=DCJnvT-_j_oec9za8vxn0FZSog4mm5PnaiWIpesctDE,3285 +wtforms/locale/en/LC_MESSAGES/wtforms.po,sha256=-GGpFQm9Sdz3Yg0EqltIGTEcOwnYqmepRSREkHV_UVU,5347 +wtforms/locale/es/LC_MESSAGES/wtforms.mo,sha256=U_oe-S3-i6A2VsBTVKxZ8N5QAEbpqXBlenSIaLnFupE,3394 +wtforms/locale/es/LC_MESSAGES/wtforms.po,sha256=P36kwWq3LZNjYHXTyoyMl86WziWpZYXxGFsFiqev1oU,5368 +wtforms/locale/et/LC_MESSAGES/wtforms.mo,sha256=Ugx0IpG1TJtP-DKpNZiVyo-L5F8ESrr_qCpPXR96pww,3456 +wtforms/locale/et/LC_MESSAGES/wtforms.po,sha256=doeYijsnPkyHy_JK4JRH6AQdHG8uaQTQWYwsCP6_Iuk,5497 +wtforms/locale/fa/LC_MESSAGES/wtforms.mo,sha256=exJzwjxXvOALqJhsQetN9Kcad4Lx62Exvnx2jtzja8Q,4137 +wtforms/locale/fa/LC_MESSAGES/wtforms.po,sha256=MHjVwlp-MHMV-TTUUkUYtuBdtbEjfV0jzVSgWHFv80Q,6149 +wtforms/locale/fi/LC_MESSAGES/wtforms.mo,sha256=NiodjvNOW25UkxEpuCioXdpvjbGwPoYmz0dfiMxE3S8,3416 +wtforms/locale/fi/LC_MESSAGES/wtforms.po,sha256=4uP6A6sfNoATdRR_8PlecqiiTsVzIp9qpcn9qe0jGMA,5456 +wtforms/locale/fr/LC_MESSAGES/wtforms.mo,sha256=BoZI4I1MK0-nipyLWOSG-s_55E9x9eG0WqYdz1qZ1KQ,3484 +wtforms/locale/fr/LC_MESSAGES/wtforms.po,sha256=60tb7Uyco3tdKc1Z4sdvwta46V_RGSmvXM9SdvuBvhg,5529 +wtforms/locale/he/LC_MESSAGES/wtforms.mo,sha256=UhetGKepgOnGXa5IsjZBdOi5IbPLCufpIugkkDuXkjQ,3649 +wtforms/locale/he/LC_MESSAGES/wtforms.po,sha256=GJy7zG0ik8U0YnubNlfjjl9iPT62w3XyaAP4kNCntkQ,5657 +wtforms/locale/hu/LC_MESSAGES/wtforms.mo,sha256=Z-qEeJI422dmm7-2qJIgCuCS1eyS2pJfoavPnGK2334,3544 +wtforms/locale/hu/LC_MESSAGES/wtforms.po,sha256=eiyNXYa4_XLQWRd-j4KmAXml27cYAPjIBhjjIv9WMbE,5492 +wtforms/locale/it/LC_MESSAGES/wtforms.mo,sha256=petuqW4x1p1S69sJax15WpLQryWoDRXW0uQjr58E9Jw,3510 +wtforms/locale/it/LC_MESSAGES/wtforms.po,sha256=EuI0Plf7nLfg5NcRPqQvfg3z7fpfIdRQGBmyq1ivpGE,5556 +wtforms/locale/ja/LC_MESSAGES/wtforms.mo,sha256=thfPsxKfihz2wNvb9LA7MzYb4PnfyXT81gaE_802AlM,3736 +wtforms/locale/ja/LC_MESSAGES/wtforms.po,sha256=ydUzTwxnk8sUQcPTeS7AuU7sgArIMWgbDzxFt85mhG8,5753 +wtforms/locale/ko/LC_MESSAGES/wtforms.mo,sha256=ZRJGcizRhJifuw4rElZ6Bb-hNdH3zqCYzxhwYJisCpU,3851 +wtforms/locale/ko/LC_MESSAGES/wtforms.po,sha256=9os2sRuqxoX0fTWHr47IvBwlkY_sDoLKdn3byS7MfjQ,5842 +wtforms/locale/nb/LC_MESSAGES/wtforms.mo,sha256=0YxYTElaTGBpIurcZqZHPU2lXslt3UNF_HOw575OAKM,3337 +wtforms/locale/nb/LC_MESSAGES/wtforms.po,sha256=NXrr3nrnoOo2x2t0g8UZXT2Jm9KQnkYdnieeoB7U9Yw,5387 +wtforms/locale/nl/LC_MESSAGES/wtforms.mo,sha256=8wLTkRK82jpG5oDkqM-jLNVLYHte4fRHYF6VAN7lB6U,3350 +wtforms/locale/nl/LC_MESSAGES/wtforms.po,sha256=9xSoztymVdIgFBA2vnzaHeSK4qEGTGbiPbfwjdcHN0k,5388 +wtforms/locale/pl/LC_MESSAGES/wtforms.mo,sha256=QUs5iz_IOoo6oCVmcpWWNNkXyqYA0X01wERmQYQiXYo,3610 +wtforms/locale/pl/LC_MESSAGES/wtforms.po,sha256=XrkwltOhyLHrOOgxYVvcmR2Hcw4LUN3_sZEdJofS5Vk,5652 +wtforms/locale/pt/LC_MESSAGES/wtforms.mo,sha256=PC5HRiM-QYt4GX3eMPapzG31jLKmo3zt6nKGVb_o174,3438 +wtforms/locale/pt/LC_MESSAGES/wtforms.po,sha256=cXIZJJZ4UDDR24yrQ-XYck3klonRZd9Ajt8A7dqqJc4,5481 +wtforms/locale/ru/LC_MESSAGES/wtforms.mo,sha256=ski71qWfnwGL9GtZEQZ1fksHBeZsePxi4ZN16AlLeZE,4406 +wtforms/locale/ru/LC_MESSAGES/wtforms.po,sha256=3eeI-CxivICl6FzYpKrqfYnz2rB68hMNCicC_9aM90s,6407 +wtforms/locale/sk/LC_MESSAGES/wtforms.mo,sha256=Lo_5eGNF_LnkJsJLOde_YNWE_F3UZtScFTFlO4v-EyU,3548 +wtforms/locale/sk/LC_MESSAGES/wtforms.po,sha256=ywPpnxYnHgEkD6Ab7LJgyqgC6dIj8cBmn6hB21aS3NI,5586 +wtforms/locale/sv/LC_MESSAGES/wtforms.mo,sha256=U7noK9cso_pRgaQcvF4duRQ69joI7SHN0XcHyd0mAVg,3376 +wtforms/locale/sv/LC_MESSAGES/wtforms.po,sha256=jMtpwUlQPbi4Xiut9KNfLjGhsjqmys1Y_iGZ3lJA4NQ,5416 +wtforms/locale/tr/LC_MESSAGES/wtforms.mo,sha256=kp3J8k2FVBaXVVJJclGnUmZTEUYHS6Hg1v2baGwtReo,3391 +wtforms/locale/tr/LC_MESSAGES/wtforms.po,sha256=PFo_e3vKCMgKtkcQSaXqNOlr-YgzxvgUtg8Ju5M-8f8,5431 +wtforms/locale/uk/LC_MESSAGES/wtforms.mo,sha256=5iZS-8LmCyeteqN3TXQ15byNTGJbjpsDa8AF3zh6L1o,4451 +wtforms/locale/uk/LC_MESSAGES/wtforms.po,sha256=fIijOGm8gXO-yZkdYoX6kWMPXZE6j9yALhekfQCK5KU,6520 +wtforms/locale/zh/LC_MESSAGES/wtforms.mo,sha256=yCzjCCwAf5yu80NhllpGqlk7V6PBFyJYfoZ6IF2dQnM,3362 +wtforms/locale/zh/LC_MESSAGES/wtforms.po,sha256=ZIh59O9rnjZMRpdKFfvrk59wouOAUHyjZS0f-TMsN6U,5378 +wtforms/locale/zh_TW/LC_MESSAGES/wtforms.mo,sha256=iha5oFUQDVs7wPBpcWLLAP_Jgm42Ea9n9xIlaCsUsNE,3204 +wtforms/locale/zh_TW/LC_MESSAGES/wtforms.po,sha256=a7q2T9fdwN_xESBCD4umHMfSptN7Qt-abjO9UFRWDBo,5218 +wtforms/widgets/__init__.py,sha256=nxI0oIsofuJCNgc4Oxwzf3_q3IiCYZTSiCoEuSRZeJM,124 +wtforms/widgets/core.py,sha256=X3I5PRFbPeX1nU3DrPpsJyglsObujdN1hMxHHFTkKOk,11150 +wtforms/widgets/html5.py,sha256=LDnNegNTx-LYpw4YkbymvS2TaA2V03p2rRdYN83skYQ,2440 +WTForms-2.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +wtforms/csrf/__pycache__/core.cpython-36.pyc,, +wtforms/csrf/__pycache__/session.cpython-36.pyc,, +wtforms/csrf/__pycache__/__init__.cpython-36.pyc,, +wtforms/ext/appengine/__pycache__/db.cpython-36.pyc,, +wtforms/ext/appengine/__pycache__/fields.cpython-36.pyc,, +wtforms/ext/appengine/__pycache__/ndb.cpython-36.pyc,, +wtforms/ext/appengine/__pycache__/__init__.cpython-36.pyc,, +wtforms/ext/csrf/__pycache__/fields.cpython-36.pyc,, +wtforms/ext/csrf/__pycache__/form.cpython-36.pyc,, +wtforms/ext/csrf/__pycache__/session.cpython-36.pyc,, +wtforms/ext/csrf/__pycache__/__init__.cpython-36.pyc,, +wtforms/ext/dateutil/__pycache__/fields.cpython-36.pyc,, +wtforms/ext/dateutil/__pycache__/__init__.cpython-36.pyc,, +wtforms/ext/django/templatetags/__pycache__/wtforms.cpython-36.pyc,, +wtforms/ext/django/templatetags/__pycache__/__init__.cpython-36.pyc,, +wtforms/ext/django/__pycache__/fields.cpython-36.pyc,, +wtforms/ext/django/__pycache__/i18n.cpython-36.pyc,, +wtforms/ext/django/__pycache__/orm.cpython-36.pyc,, +wtforms/ext/django/__pycache__/__init__.cpython-36.pyc,, +wtforms/ext/i18n/__pycache__/form.cpython-36.pyc,, +wtforms/ext/i18n/__pycache__/utils.cpython-36.pyc,, +wtforms/ext/i18n/__pycache__/__init__.cpython-36.pyc,, +wtforms/ext/sqlalchemy/__pycache__/fields.cpython-36.pyc,, +wtforms/ext/sqlalchemy/__pycache__/orm.cpython-36.pyc,, +wtforms/ext/sqlalchemy/__pycache__/__init__.cpython-36.pyc,, +wtforms/ext/__pycache__/__init__.cpython-36.pyc,, +wtforms/fields/__pycache__/core.cpython-36.pyc,, +wtforms/fields/__pycache__/html5.cpython-36.pyc,, +wtforms/fields/__pycache__/simple.cpython-36.pyc,, +wtforms/fields/__pycache__/__init__.cpython-36.pyc,, +wtforms/widgets/__pycache__/core.cpython-36.pyc,, +wtforms/widgets/__pycache__/html5.cpython-36.pyc,, +wtforms/widgets/__pycache__/__init__.cpython-36.pyc,, +wtforms/__pycache__/compat.cpython-36.pyc,, +wtforms/__pycache__/form.cpython-36.pyc,, +wtforms/__pycache__/i18n.cpython-36.pyc,, +wtforms/__pycache__/meta.cpython-36.pyc,, +wtforms/__pycache__/utils.cpython-36.pyc,, +wtforms/__pycache__/validators.cpython-36.pyc,, +wtforms/__pycache__/__init__.cpython-36.pyc,, diff --git a/venv/Lib/site-packages/WTForms-2.2.1.dist-info/WHEEL b/venv/Lib/site-packages/WTForms-2.2.1.dist-info/WHEEL new file mode 100644 index 0000000..1316c41 --- /dev/null +++ b/venv/Lib/site-packages/WTForms-2.2.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.31.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/WTForms-2.2.1.dist-info/top_level.txt b/venv/Lib/site-packages/WTForms-2.2.1.dist-info/top_level.txt new file mode 100644 index 0000000..26d80fd --- /dev/null +++ b/venv/Lib/site-packages/WTForms-2.2.1.dist-info/top_level.txt @@ -0,0 +1 @@ +wtforms diff --git a/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/DESCRIPTION.rst b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..675f08d --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/DESCRIPTION.rst @@ -0,0 +1,80 @@ +Werkzeug +======== + +Werkzeug is a comprehensive `WSGI`_ web application library. It began as +a simple collection of various utilities for WSGI applications and has +become one of the most advanced WSGI utility libraries. + +It includes: + +* An interactive debugger that allows inspecting stack traces and source + code in the browser with an interactive interpreter for any frame in + the stack. +* A full-featured request object with objects to interact with headers, + query args, form data, files, and cookies. +* A response object that can wrap other WSGI applications and handle + streaming data. +* A routing system for matching URLs to endpoints and generating URLs + for endpoints, with an extensible system for capturing variables from + URLs. +* HTTP utilities to handle entity tags, cache control, dates, user + agents, cookies, files, and more. +* A threaded WSGI server for use while developing applications locally. +* A test client for simulating HTTP requests during testing without + requiring running a server. + +Werkzeug is Unicode aware and doesn't enforce any dependencies. It is up +to the developer to choose a template engine, database adapter, and even +how to handle requests. It can be used to build all sorts of end user +applications such as blogs, wikis, or bulletin boards. + +`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while +providing more structure and patterns for defining powerful +applications. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U Werkzeug + + +A Simple Example +---------------- + +.. code-block:: python + + from werkzeug.wrappers import Request, Response + + @Request.application + def application(request): + return Response('Hello, World!') + + if __name__ == '__main__': + from werkzeug.serving import run_simple + run_simple('localhost', 4000, application) + + +Links +----- + +* Website: https://www.palletsprojects.com/p/werkzeug/ +* Releases: https://pypi.org/project/Werkzeug/ +* Code: https://github.com/pallets/werkzeug +* Issue tracker: https://github.com/pallets/werkzeug/issues +* Test status: + + * Linux, Mac: https://travis-ci.org/pallets/werkzeug + * Windows: https://ci.appveyor.com/project/davidism/werkzeug + +* Test coverage: https://codecov.io/gh/pallets/werkzeug + +.. _WSGI: https://wsgi.readthedocs.io/en/latest/ +.. _Flask: https://www.palletsprojects.com/p/flask/ +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + diff --git a/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/INSTALLER b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/LICENSE.txt b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/LICENSE.txt new file mode 100644 index 0000000..1cc75bb --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/LICENSE.txt @@ -0,0 +1,31 @@ +Copyright © 2007 by the Pallets team. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/METADATA b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/METADATA new file mode 100644 index 0000000..bfc3c4e --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/METADATA @@ -0,0 +1,116 @@ +Metadata-Version: 2.0 +Name: Werkzeug +Version: 0.14.1 +Summary: The comprehensive WSGI web application library. +Home-page: https://www.palletsprojects.org/p/werkzeug/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +License: BSD +Description-Content-Type: UNKNOWN +Platform: any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Provides-Extra: dev +Requires-Dist: coverage; extra == 'dev' +Requires-Dist: pytest; extra == 'dev' +Requires-Dist: sphinx; extra == 'dev' +Requires-Dist: tox; extra == 'dev' +Provides-Extra: termcolor +Requires-Dist: termcolor; extra == 'termcolor' +Provides-Extra: watchdog +Requires-Dist: watchdog; extra == 'watchdog' + +Werkzeug +======== + +Werkzeug is a comprehensive `WSGI`_ web application library. It began as +a simple collection of various utilities for WSGI applications and has +become one of the most advanced WSGI utility libraries. + +It includes: + +* An interactive debugger that allows inspecting stack traces and source + code in the browser with an interactive interpreter for any frame in + the stack. +* A full-featured request object with objects to interact with headers, + query args, form data, files, and cookies. +* A response object that can wrap other WSGI applications and handle + streaming data. +* A routing system for matching URLs to endpoints and generating URLs + for endpoints, with an extensible system for capturing variables from + URLs. +* HTTP utilities to handle entity tags, cache control, dates, user + agents, cookies, files, and more. +* A threaded WSGI server for use while developing applications locally. +* A test client for simulating HTTP requests during testing without + requiring running a server. + +Werkzeug is Unicode aware and doesn't enforce any dependencies. It is up +to the developer to choose a template engine, database adapter, and even +how to handle requests. It can be used to build all sorts of end user +applications such as blogs, wikis, or bulletin boards. + +`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while +providing more structure and patterns for defining powerful +applications. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U Werkzeug + + +A Simple Example +---------------- + +.. code-block:: python + + from werkzeug.wrappers import Request, Response + + @Request.application + def application(request): + return Response('Hello, World!') + + if __name__ == '__main__': + from werkzeug.serving import run_simple + run_simple('localhost', 4000, application) + + +Links +----- + +* Website: https://www.palletsprojects.com/p/werkzeug/ +* Releases: https://pypi.org/project/Werkzeug/ +* Code: https://github.com/pallets/werkzeug +* Issue tracker: https://github.com/pallets/werkzeug/issues +* Test status: + + * Linux, Mac: https://travis-ci.org/pallets/werkzeug + * Windows: https://ci.appveyor.com/project/davidism/werkzeug + +* Test coverage: https://codecov.io/gh/pallets/werkzeug + +.. _WSGI: https://wsgi.readthedocs.io/en/latest/ +.. _Flask: https://www.palletsprojects.com/p/flask/ +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + diff --git a/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/RECORD b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/RECORD new file mode 100644 index 0000000..357d9b7 --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/RECORD @@ -0,0 +1,97 @@ +Werkzeug-0.14.1.dist-info/DESCRIPTION.rst,sha256=rOCN36jwsWtWsTpqPG96z7FMilB5qI1CIARSKRuUmz8,2452 +Werkzeug-0.14.1.dist-info/LICENSE.txt,sha256=xndz_dD4m269AF9l_Xbl5V3tM1N3C1LoZC2PEPxWO-8,1534 +Werkzeug-0.14.1.dist-info/METADATA,sha256=FbfadrPdJNUWAxMOKxGUtHe5R3IDSBKYYmAz3FvI3uY,3872 +Werkzeug-0.14.1.dist-info/RECORD,, +Werkzeug-0.14.1.dist-info/WHEEL,sha256=GrqQvamwgBV4nLoJe0vhYRSWzWsx7xjlt74FT0SWYfE,110 +Werkzeug-0.14.1.dist-info/metadata.json,sha256=4489UTt6HBp2NQil95-pBkjU4Je93SMHvMxZ_rjOpqA,1452 +Werkzeug-0.14.1.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9 +werkzeug/__init__.py,sha256=NR0d4n_-U9BLVKlOISean3zUt2vBwhvK-AZE6M0sC0k,6842 +werkzeug/_compat.py,sha256=8c4U9o6A_TR9nKCcTbpZNxpqCXcXDVIbFawwKM2s92c,6311 +werkzeug/_internal.py,sha256=GhEyGMlsSz_tYjsDWO9TG35VN7304MM8gjKDrXLEdVc,13873 +werkzeug/_reloader.py,sha256=AyPphcOHPbu6qzW0UbrVvTDJdre5WgpxbhIJN_TqzUc,9264 +werkzeug/datastructures.py,sha256=3IgNKNqrz-ZjmAG7y3YgEYK-enDiMT_b652PsypWcYg,90080 +werkzeug/exceptions.py,sha256=3wp95Hqj9FqV8MdikV99JRcHse_fSMn27V8tgP5Hw2c,20505 +werkzeug/filesystem.py,sha256=hHWeWo_gqLMzTRfYt8-7n2wWcWUNTnDyudQDLOBEICE,2175 +werkzeug/formparser.py,sha256=mUuCwjzjb8_E4RzrAT2AioLuZSYpqR1KXTK6LScRYzA,21722 +werkzeug/http.py,sha256=RQg4MJuhRv2isNRiEh__Phh09ebpfT3Kuu_GfrZ54_c,40079 +werkzeug/local.py,sha256=QdQhWV5L8p1Y1CJ1CDStwxaUs24SuN5aebHwjVD08C8,14553 +werkzeug/posixemulation.py,sha256=xEF2Bxc-vUCPkiu4IbfWVd3LW7DROYAT-ExW6THqyzw,3519 +werkzeug/routing.py,sha256=2JVtdSgxKGeANy4Z_FP-dKESvKtkYGCZ1J2fARCLGCY,67214 +werkzeug/script.py,sha256=DwaVDcXdaOTffdNvlBdLitxWXjKaRVT32VbhDtljFPY,11365 +werkzeug/security.py,sha256=0m107exslz4QJLWQCpfQJ04z3re4eGHVggRvrQVAdWc,9193 +werkzeug/serving.py,sha256=A0flnIJHufdn2QJ9oeuHfrXwP3LzP8fn3rNW6hbxKUg,31926 +werkzeug/test.py,sha256=XmECSmnpASiYQTct4oMiWr0LT5jHWCtKqnpYKZd2ui8,36100 +werkzeug/testapp.py,sha256=3HQRW1sHZKXuAjCvFMet4KXtQG3loYTFnvn6LWt-4zI,9396 +werkzeug/urls.py,sha256=dUeLg2IeTm0WLmSvFeD4hBZWGdOs-uHudR5-t8n9zPo,36771 +werkzeug/useragents.py,sha256=BhYMf4cBTHyN4U0WsQedePIocmNlH_34C-UwqSThGCc,5865 +werkzeug/utils.py,sha256=BrY1j0DHQ8RTb0K1StIobKuMJhN9SQQkWEARbrh2qpk,22972 +werkzeug/websocket.py,sha256=PpSeDxXD_0UsPAa5hQhQNM6mxibeUgn8lA8eRqiS0vM,11344 +werkzeug/wrappers.py,sha256=kbyL_aFjxELwPgMwfNCYjKu-CR6kNkh-oO8wv3GXbk8,84511 +werkzeug/wsgi.py,sha256=1Nob-aeChWQf7MsiicO8RZt6J90iRzEcik44ev9Qu8s,49347 +werkzeug/contrib/__init__.py,sha256=f7PfttZhbrImqpr5Ezre8CXgwvcGUJK7zWNpO34WWrw,623 +werkzeug/contrib/atom.py,sha256=qqfJcfIn2RYY-3hO3Oz0aLq9YuNubcPQ_KZcNsDwVJo,15575 +werkzeug/contrib/cache.py,sha256=xBImHNj09BmX_7kC5NUCx8f_l4L8_O7zi0jCL21UZKE,32163 +werkzeug/contrib/fixers.py,sha256=gR06T-w71ur-tHQ_31kP_4jpOncPJ4Wc1dOqTvYusr8,10179 +werkzeug/contrib/iterio.py,sha256=RlqDvGhz0RneTpzE8dVc-yWCUv4nkPl1jEc_EDp2fH0,10814 +werkzeug/contrib/jsrouting.py,sha256=QTmgeDoKXvNK02KzXgx9lr3cAH6fAzpwF5bBdPNvJPs,8564 +werkzeug/contrib/limiter.py,sha256=iS8-ahPZ-JLRnmfIBzxpm7O_s3lPsiDMVWv7llAIDCI,1334 +werkzeug/contrib/lint.py,sha256=Mj9NeUN7s4zIUWeQOAVjrmtZIcl3Mm2yDe9BSIr9YGE,12558 +werkzeug/contrib/profiler.py,sha256=ISwCWvwVyGpDLRBRpLjo_qUWma6GXYBrTAco4PEQSHY,5151 +werkzeug/contrib/securecookie.py,sha256=uWMyHDHY3lkeBRiCSayGqWkAIy4a7xAbSE_Hln9ecqc,12196 +werkzeug/contrib/sessions.py,sha256=39LVNvLbm5JWpbxM79WC2l87MJFbqeISARjwYbkJatw,12577 +werkzeug/contrib/testtools.py,sha256=G9xN-qeihJlhExrIZMCahvQOIDxdL9NiX874jiiHFMs,2453 +werkzeug/contrib/wrappers.py,sha256=v7OYlz7wQtDlS9fey75UiRZ1IkUWqCpzbhsLy4k14Hw,10398 +werkzeug/debug/__init__.py,sha256=uSn9BqCZ5E3ySgpoZtundpROGsn-uYvZtSFiTfAX24M,17452 +werkzeug/debug/console.py,sha256=n3-dsKk1TsjnN-u4ZgmuWCU_HO0qw5IA7ttjhyyMM6I,5607 +werkzeug/debug/repr.py,sha256=bKqstDYGfECpeLerd48s_hxuqK4b6UWnjMu3d_DHO8I,9340 +werkzeug/debug/tbtools.py,sha256=rBudXCmkVdAKIcdhxANxgf09g6kQjJWW9_5bjSpr4OY,18451 +werkzeug/debug/shared/FONT_LICENSE,sha256=LwAVEI1oYnvXiNMT9SnCH_TaLCxCpeHziDrMg0gPkAI,4673 +werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507 +werkzeug/debug/shared/debugger.js,sha256=PKPVYuyO4SX1hkqLOwCLvmIEO5154WatFYaXE-zIfKI,6264 +werkzeug/debug/shared/jquery.js,sha256=7LkWEzqTdpEfELxcZZlS6wAx5Ff13zZ83lYO2_ujj7g,95957 +werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191 +werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200 +werkzeug/debug/shared/source.png,sha256=RoGcBTE4CyCB85GBuDGTFlAnUqxwTBiIfDqW15EpnUQ,818 +werkzeug/debug/shared/style.css,sha256=IEO0PC2pWmh2aEyGCaN--txuWsRCliuhlbEhPDFwh0A,6270 +werkzeug/debug/shared/ubuntu.ttf,sha256=1eaHFyepmy4FyDvjLVzpITrGEBu_CZYY94jE0nED1c0,70220 +Werkzeug-0.14.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +werkzeug/contrib/__pycache__/atom.cpython-36.pyc,, +werkzeug/contrib/__pycache__/cache.cpython-36.pyc,, +werkzeug/contrib/__pycache__/fixers.cpython-36.pyc,, +werkzeug/contrib/__pycache__/iterio.cpython-36.pyc,, +werkzeug/contrib/__pycache__/jsrouting.cpython-36.pyc,, +werkzeug/contrib/__pycache__/limiter.cpython-36.pyc,, +werkzeug/contrib/__pycache__/lint.cpython-36.pyc,, +werkzeug/contrib/__pycache__/profiler.cpython-36.pyc,, +werkzeug/contrib/__pycache__/securecookie.cpython-36.pyc,, +werkzeug/contrib/__pycache__/sessions.cpython-36.pyc,, +werkzeug/contrib/__pycache__/testtools.cpython-36.pyc,, +werkzeug/contrib/__pycache__/wrappers.cpython-36.pyc,, +werkzeug/contrib/__pycache__/__init__.cpython-36.pyc,, +werkzeug/debug/__pycache__/console.cpython-36.pyc,, +werkzeug/debug/__pycache__/repr.cpython-36.pyc,, +werkzeug/debug/__pycache__/tbtools.cpython-36.pyc,, +werkzeug/debug/__pycache__/__init__.cpython-36.pyc,, +werkzeug/__pycache__/datastructures.cpython-36.pyc,, +werkzeug/__pycache__/exceptions.cpython-36.pyc,, +werkzeug/__pycache__/filesystem.cpython-36.pyc,, +werkzeug/__pycache__/formparser.cpython-36.pyc,, +werkzeug/__pycache__/http.cpython-36.pyc,, +werkzeug/__pycache__/local.cpython-36.pyc,, +werkzeug/__pycache__/posixemulation.cpython-36.pyc,, +werkzeug/__pycache__/routing.cpython-36.pyc,, +werkzeug/__pycache__/script.cpython-36.pyc,, +werkzeug/__pycache__/security.cpython-36.pyc,, +werkzeug/__pycache__/serving.cpython-36.pyc,, +werkzeug/__pycache__/test.cpython-36.pyc,, +werkzeug/__pycache__/testapp.cpython-36.pyc,, +werkzeug/__pycache__/urls.cpython-36.pyc,, +werkzeug/__pycache__/useragents.cpython-36.pyc,, +werkzeug/__pycache__/utils.cpython-36.pyc,, +werkzeug/__pycache__/websocket.cpython-36.pyc,, +werkzeug/__pycache__/wrappers.cpython-36.pyc,, +werkzeug/__pycache__/wsgi.cpython-36.pyc,, +werkzeug/__pycache__/_compat.cpython-36.pyc,, +werkzeug/__pycache__/_internal.cpython-36.pyc,, +werkzeug/__pycache__/_reloader.cpython-36.pyc,, +werkzeug/__pycache__/__init__.cpython-36.pyc,, diff --git a/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/WHEEL b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/WHEEL new file mode 100644 index 0000000..0de529b --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.26.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/metadata.json b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/metadata.json new file mode 100644 index 0000000..bca8d12 --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/metadata.json @@ -0,0 +1 @@ +{"generator": "bdist_wheel (0.26.0)", "summary": "The comprehensive WSGI web application library.", "classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules"], "description_content_type": "UNKNOWN", "extensions": {"python.details": {"project_urls": {"Home": "https://www.palletsprojects.org/p/werkzeug/"}, "contacts": [{"email": "armin.ronacher@active-4.com", "name": "Armin Ronacher", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}}}, "license": "BSD", "metadata_version": "2.0", "name": "Werkzeug", "platform": "any", "extras": ["dev", "termcolor", "watchdog"], "run_requires": [{"requires": ["coverage", "pytest", "sphinx", "tox"], "extra": "dev"}, {"requires": ["termcolor"], "extra": "termcolor"}, {"requires": ["watchdog"], "extra": "watchdog"}], "version": "0.14.1"} \ No newline at end of file diff --git a/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/top_level.txt b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/top_level.txt new file mode 100644 index 0000000..6fe8da8 --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-0.14.1.dist-info/top_level.txt @@ -0,0 +1 @@ +werkzeug diff --git a/venv/Lib/site-packages/click/__init__.py b/venv/Lib/site-packages/click/__init__.py new file mode 100644 index 0000000..d3c3366 --- /dev/null +++ b/venv/Lib/site-packages/click/__init__.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +""" +click +~~~~~ + +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. + +:copyright: © 2014 by the Pallets team. +:license: BSD, see LICENSE.rst for more details. +""" + +# Core classes +from .core import Context, BaseCommand, Command, MultiCommand, Group, \ + CommandCollection, Parameter, Option, Argument + +# Globals +from .globals import get_current_context + +# Decorators +from .decorators import pass_context, pass_obj, make_pass_decorator, \ + command, group, argument, option, confirmation_option, \ + password_option, version_option, help_option + +# Types +from .types import ParamType, File, Path, Choice, IntRange, Tuple, \ + DateTime, STRING, INT, FLOAT, BOOL, UUID, UNPROCESSED, FloatRange + +# Utilities +from .utils import echo, get_binary_stream, get_text_stream, open_file, \ + format_filename, get_app_dir, get_os_args + +# Terminal functions +from .termui import prompt, confirm, get_terminal_size, echo_via_pager, \ + progressbar, clear, style, unstyle, secho, edit, launch, getchar, \ + pause + +# Exceptions +from .exceptions import ClickException, UsageError, BadParameter, \ + FileError, Abort, NoSuchOption, BadOptionUsage, BadArgumentUsage, \ + MissingParameter + +# Formatting +from .formatting import HelpFormatter, wrap_text + +# Parsing +from .parser import OptionParser + + +__all__ = [ + # Core classes + 'Context', 'BaseCommand', 'Command', 'MultiCommand', 'Group', + 'CommandCollection', 'Parameter', 'Option', 'Argument', + + # Globals + 'get_current_context', + + # Decorators + 'pass_context', 'pass_obj', 'make_pass_decorator', 'command', 'group', + 'argument', 'option', 'confirmation_option', 'password_option', + 'version_option', 'help_option', + + # Types + 'ParamType', 'File', 'Path', 'Choice', 'IntRange', 'Tuple', + 'DateTime', 'STRING', 'INT', 'FLOAT', 'BOOL', 'UUID', 'UNPROCESSED', + 'FloatRange', + + # Utilities + 'echo', 'get_binary_stream', 'get_text_stream', 'open_file', + 'format_filename', 'get_app_dir', 'get_os_args', + + # Terminal functions + 'prompt', 'confirm', 'get_terminal_size', 'echo_via_pager', + 'progressbar', 'clear', 'style', 'unstyle', 'secho', 'edit', 'launch', + 'getchar', 'pause', + + # Exceptions + 'ClickException', 'UsageError', 'BadParameter', 'FileError', + 'Abort', 'NoSuchOption', 'BadOptionUsage', 'BadArgumentUsage', + 'MissingParameter', + + # Formatting + 'HelpFormatter', 'wrap_text', + + # Parsing + 'OptionParser', +] + + +# Controls if click should emit the warning about the use of unicode +# literals. +disable_unicode_literals_warning = False + + +__version__ = '7.0' diff --git a/venv/Lib/site-packages/click/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/click/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dc7883cb8b65336f7af86f59b707a4671a492a57 GIT binary patch literal 2664 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/_unicodefun.cpython-36.pyc b/venv/Lib/site-packages/click/__pycache__/_unicodefun.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ea603b5e3014e7e1d41cdd21e04e57380aee1f6 GIT binary patch literal 3385 zcma)9&2!tv6~}@g2tu;s4_W?f2K6*{m9gk)+?jYhNyn~YH_o`OCU)vtqXq@BOA#ap zz`F}c)R333*UaSHLr=N%-1eTc{{&of%DFe6`g;pdR+CF9V?W;RzW4Th{NDP`sZ;*p zzaNLIXD#b{>%`B&_&z@MM>O2xEVXv(p6xI*w|8uA^H$oLI6Drr1RJ%5JL+(U+Y6gp zm+`mb@iuq=YVCA+hkNLK-sL{}70#Yn8!JD6xApV~hIWHht?ic|RIC>hk$$b_+d{7j zG#r9cYi?<#A$nn}bz-CKQH!@0Y|eOVZXZ6HVb{1TM>95ZMs29mUf3g-y9*{i2dy(_ zGpPNcHMd{gz}y>kXV%Cct=zZdU}itFwyjq;UO*NU>^zmETh$HsgdWIfBBV`oFAOtG z!mzTA+&E3*ac^U_^22aY>QWNDGmKQEwX9lds$| zj9D|LI}{CAcfPh6LKc2(*M3BdIR}xcLyz z2FawzrS1j6^I@U_{2PjBKS`$nPgIDaQ)j5pkoEI4qzm7?szr2g@)B2Dz*cH%Q` zCb@+047(th}%#^%HhdVFo#SMF>+mCo1F=olc zZrxmb8b>&nH7lqZ4=YMonC|H?MTH*B0GqOs$JwNIjr0?kE2u^*_+XZUAap0yws)_ zQvL@Utw&WyiXx3-@y6|yda!x-O`H0=Z``=*kPD3BI&U-(H6$fUm4LWu0O$hmMh$F&X7u16+*MZTpER3R5SDF&zW5{y^0qqfoFhO&RV>t~&`0@IC z>N-jfQbqfi(uu}4f**ZXt9+28DclOb1%px|DkQjyxHMH{S=N?06&yqg=B~qy%4lv% z)`MqTj~@qE5*cb6*`3N2Mc4_?_kyh)>745Sqb`Z;_J_U0xtrP`wRX z-ax9#n`kipC5<=g@mu;;Q@`ZTL9bjYys@J6&&Op^t>Il(NHIXt3%*pna_FsBbsrwa zq97wwcCJtz%RVY+ER?F++j$|f%GrLr{jgf0Od<4%w5U3Nc=k}roSuR7;!70O^(c!) z)$UKVfMm$02cNG{<8_*3ptY%z7gZa%B4p(u>*_~hE8@IzMtPC}ncyD5t4?E1xk}er z{k+U{G7(1PMz^`+K!vzU)fR_ZM)CvNXv_VbRf5W}3F{pn@6|943D7Es)Y$Q51=Y^ CGv%-V literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/_winconsole.cpython-36.pyc b/venv/Lib/site-packages/click/__pycache__/_winconsole.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c95613d0f12334753819b53c6504e0753391a5c GIT binary patch literal 8787 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/core.cpython-36.pyc b/venv/Lib/site-packages/click/__pycache__/core.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a277a179822b5a2e9e5287a660836c986569dcf1 GIT binary patch literal 59858 zcmeIbeVAO=ecw0lJ3G7BT`WMn5u`>SL10O6#TSW^Ac_JA0wN@8Nt3W%R%B)eduM@J z%+4Bs>4~dAh*dxpVKi=brOBzw>)Pzw`L6U6pIUc)qsqKsNJx znbco_^HY2}|fr$abWqt#=+%-8;6z;+5O4Z-5ZCO4{zMFe9y+c%lF!~O6y%4)#d8O zyO-Z>zo%M9Htt)#Z{z;u`|bC1>*&S<%MWZETRvvLceRdhEG#c zVb^C`4{t0kFK)bN`90Z8eAn*f`Dar}_P!;1Pa5V!zMu`_bSrem`dQJ;U$EgZJ|Ly^Uumf0p0x3!dQj z6ZZQYn2bes<&h@_AsAeJQi>p>GOBmKL&unbpm9w{fkzQE%5THA1eG!p2sk z-qpSBt)Sj*)UJm0t*u7*%`DF>(P8*1FfX8iVrrcK5}4`%+_2c>4M0pIayn z=3l6MWE7GiO@O)hlPOtv0s0&CT|p@|_);c{U6;!@=~^_27kiSl?)L8{wdM z`XW^g=04Hvbeio;$&JFU}|e~EBdie80260%wvP; zW~Ui6+TG?_Qx7+J0iTV|U>ac1P6MLea(8X@a%1&MZEZ8GZMU1Nn?a+-iyC3Q)fw!m z0dQbi+q&MpyxD%d)@Z-h+kNKwPkiF^(lgc1o__J8FTJw3xY&EpUwSmTR{gBM_E>Vw z%I>Njf9C89FP=Sf`oh^~7J7T1X>5g!)w;m^se0I^Z6~X}`z~B=R981QK#!n$^mrR! zFLbKSPBrkQ8^PkL@Irt3F7i3Ur*oV`KeL_*@9$>2xo$ql@~hCy?t{hz`7h>zLQwo- z4t&gjkENwSL3I!EVIu(Za%-0^@Re_bAqZ7TUKUPMZMcua;6T#)+WDnVKL7EvwWa4T zoIi6mypP*@dUYfC=}h<_zux)GnUgPnC&Sly`TDWWvFk5C)9763Zf?E&^ycQ3hd*Js zzriRy{L=Lfqx0}aov-la*8ua&t>(p-I}F6bTlLi|AYbR@RpI2ztD9kCaqIfvp4vwJ z`bCiY^5)fA($ZRcv)f#49O3O95#da>lnqbtJp?X@vICdObu;VP_1q2M$XVW>71oRW z%#Ey{xVc_S=v=+p1P`{m)l2p8B5h)bTCK*a#_mM5dzta+Y&9A|r@FmWZK(Xk>(%;2 zJ@Duw8=WdRR;ltw$@ZmAwbi`Rs9vmhE^DN>S`AgRXa$y-g89z%&Y;xk20T6}u4z`= zPsV1D+w2UAHN63N7lgA0Zg+TyE)6Ev0Hu{U#?MZPL#9y4R(l4fkFxKY|EWHbFj zKhw|lbN&2{Lil2LA}Gf2o9I0Xy=sK*daHUFoNhHjK?6wK1c7R2dut1lvbfZnt{v^v zm_{!=clN^Bcd`!;vb9jHrPI@bn+IECp*)y?dbaATjX{y80nwur8%%&l%@FdHhq?`R z)zZ&|@27+TW1$!>sPrU+>Qc?71Ekglq)~gV9xg+x{jX*l>fxRgdMEG>pXBEwd^&R+ zGP!bgZ+1Fc$QE+b*_o`}J-2G;$X#_|l8?>T7dX*;oz8Ujb^%^DcOy5Q2@hy~_VeM9 zAiGm&7lPbQfw`O)h#7*cz`R~uFY(JA3u_bmK-V{rEtuDZrQYiKuIL4`p}MsRX};KM zRJ)tidJt4M-~i2tx^$}bPIdKivlWDmww2(K%M8y@DF&);^)=fRu5;V~>3YGr&20!& zeWWlocD649H6veo>qvpIwJlbw7c_-@t34Vtg5D1u?QrjmukOgv&JmUAG`ec8YmLaH z-MGrT>0^UN4K?B-bCCH+!<*esqqU|QMq0%?jz^W(t4BLF*?FMu-a(aI6MDE;Ei5if z42t&pK?QCDYSh~x;PfX1Uftqm ziuf$oS49Q&r|*~wh{$&{j(Laz_Omx~Z}SaWWtU*Dg%%ANU%g)4ibS94YQ5FE2y-&bGwf|t+l{N$ zPV-W`-rWuxM&df#s}Oi!6rAkV!!G3Cu&Zh;t@_yFBf`hytKDlST>U4iuhm=I4R95jF7FBeCha6>ts!iw0P_z!UvjRq?kuT(1P#Pr_F} zm1%!hfx)~|2|t;IlVmf!M<8HX<25H3np_P$jdqcq1ToKSadhGi%YXB+8y;+7;8@5Z{6F2Dn`bV3{ zRMk2}^VRJJRI0kUmLM`DmyW`TH$wyoBb*31t)O~Q0!~|kG~%J_H#{3Qx!N#czbdj@ z7fD527K0hC)u=o|N~i0Eqp-oOXm_lpI^s{q2(sRcP=^rQOs3TM-wgjwq+WYC zmgPi5rugg;5I(@+`pjD}Q_-!RELe9KT*-nf4`#ta@=49Wv!?POer?vfkho5*!GMX}iR^9gNN|lQI;UCVWcZo^F_8=g z<&IRXD~;=)QyVKBGT9tLf&TSQ2y||;bxZ|GfV=o)iz-{4rxv3Mcu}5u_$uiV5<6l$ z)h+K18gP`*FxJ0+jqBEkg{+kcO)5Ne>({-;15RKrPEUkQehDArzZuR>@#*|Bhek#= z4D1(KY!{27!RYCQpcG7?i5G)%Fv)kRF@Xj>6-=9+UT#bVyMmcF8JOitFdNKKVk+1j z%=0}R>pCxc4MBp5WR*#{anb<#M7zb5(sfuhj>S3^X6?Mw)FD_Preg}eX zvf;^-V!3t)ZS1s(iyF2Sdedw*MVR4#8mg5O)fHJuyjj8|vd+qh=+Tufl0h4lQv-;& zQ(N0^udbvh>n-2^_}KS@1t_z|h0GGOVib18T{cu?7%+zf?rzkKSR{$ydUJo@JNA93 zubZLzh{S?gjRE9J8>mG_816vfqqnSV)UP=+3-Gzx47!)c^z@{)B|_Yj##THQBkTz2 zoZ5%D3ZXd?e+N7)Bet1SO8CM~akv#iRBc?+^gQu#P?oi7X;%8^U<7_kn#`-_-i0l+$eHG|1W7h6akx+RP zrWiJ1e{Cu}->y2t&s8MY&PmBDNcKj6?od@c9Ly@jP~>BV1d<|I>4fjM@oV0BhpL?7 z#gkHO1@5S>;ojX5*d8@qx8V8BNSSkOa<4H!Z8R@kzEi8HpxW7l9(U?%jqY{k{??WR z9SItsW2h>IChkpg#7PH90JPSsUlRFgw2k_xf_4fwlNsA-5nKI3PynFs`Tgez2!O*FteRbzrfL`2mwFKTL%G-WoG;0LhoP>&z+ zNR))jd4qUt4z>#XG<5<|A}1x_r2*bBxN;%fju?j1>p{(JwdS_us#-->*QheF9UwGQ z!g>?9gVUy}ODb~r?gXDJ_|GCViR2xbIn%_`Ug*v9utqp@Y|X2W=93uVuFZ#*z=^_9}#)8liK7ID(I z(Bx8Y4MFB|?n%8EJakO2iihhC%@^1rsOn9L3Zf}#Mr+5k?@&)v2M?{ReD>JG5390c zudJ*Lk+*SLj|q_u=BUlmgB;2AegjkNZqK0|Kx18sp+vXBD(VOF1ng}S&t50izHT)F z)oP==x)?J_qeR|1Kcr`VE6s^X7*9&`YZdJxq{pmY)1^hA=dHjl*n#`IRY{hwwxh598TKr3*zK55U`*?I*ZW_G94$DL5)I>)L0ggxW$x|jSM1)e;!mB~sq`yco9R#4wE@>=cc%JNT$}Ar z+qFO9+T6}`f0}D^{aw2Df76&_4WNsTpwra`*vpgv)tcjW3oRG^v#{QP$b*{OHI93a)hN<#J_vA|KVMm9K4%<4&@*DPbxT?c7 z9j@!p(?M42@Vj&v_IS&I)@&1zQ~PhZ*;(h1DVK8m%kwFg@L%wqoyp7LSix;nnl9z+ znvRsg4OvVbCrdenCzS2^z1dQMyG32W;e-!lPUUl4SMAx}+!?1aE=G-rJp%IG$t7}z zPv;PaK4gSQhV1)sbJp+%xTuc4+~m0>7fz51zlVb1-5fBNzpnF<&f>F0aKuz!`xGy5 z;S|{K%UPm<;+FUlu{SgF&RA1aVbqilSW}o>#N=Gbg;mZ7v5D54eXw>55z!axq~X zcmBL{obJl~Al zB||&ii7ZWu20}S8lRdU-dlrkY~}y&>?)=)h{p`D@Ne@Gz@KZ!E1u_Lq58c)zZcZ!++hGLonle zd%ZV%>vdMVLKe%Y(X*RCe;`bC#cq_ zBtB6QaD)hl;DcQF+9)+F8oFH+DU@F`b58+cn1jpg%@wk}T@gjMQbUY|Cmdr@Je}w_ z#4KC?i}0HW^@gmpu@v3GFh|D>-RVtwq9ux{X?zmSF>?Y24WC?yc*(J8h`gZ?IwcpM zA|a|xWO;$M)5UdT_NjQVoof=iD;kR_AlIo>6MWOv%AlZA$|Gq_LGaj;pZ}t&#fX&H z4&6&p8-!zSa`Oa_r)<$z8?9Dq!i$9BrFPWH)E)WscwB_N-6Z~?8f+5qi%HEQafPSn zWGwP3THFaD%(fvMTlMZ`S6RJ!X;VzX)Id|pad%d$pYkfE!NCxaiJ+b*plxDqb#7XF zjQc3xn9WQ@Md>!MQq{8_C|7-zXn#1`nF#61QZP|53p)fLa(dc(>w&&(>gYw6obw6F z5B%_jpsc0g%g9;mYYKw5C7I8iUixV5<7Yop{ctr39;-f7J+{~)7W`O3f;6R=GU7Ki z-P(rw9yjq|43UM1Qr%b@8;?dbM@6kAV}CYk$SXW*72o`F)fm5g%zIrt$oLoAOBSi& zc=VzZ)noWtzw@PI3(1?t6XK|?kc5zlI$ov=uW*YPA|Hu%s5c$Wdv$MU+Q)jRlv0JH ztt+S+ZPLPPa=rJSLE7kg=L8(t1+>SgW@@f%XT}4oUg0H%q-g$&!KCyKJB4`d=M{-u zKECOL84Ko*-}K|^g&LWWY`ib-hhqQZLa|h*m>GRcELD$0$mkJ~F$Bz6oL{|5jYiUl zEP=p7VeoWpG&U~Ew}5j$GVj3rBYU$WP%$Hm5yu4#i2BLWtr@ZEMGQIVnK*LOOY(xM z@ZbEe-i9oqK`>5S6|-t{J0yC^q#DUMlu(VM@fIT_1<$EuAtyj4Q~?surd>#7s^g@& zr%$e&qbO1tYb>0JH;TMep(f^a$>4FPlgc#bQ*+ph0Bg>F6vK=5dU%*sIF3??F*X-| zHzP7AY2`;dSjahMmR%cVVY>wO+Gg7+fIl50z~pqccUOb}LuF!&9vc5DZ@VTvKl4oH zmCBB=02Sob@~1MUy+?6#htHeT%h)UIVi*LAV!dj0)Y{zYsQwYY$?acMBP|e}@WD!o z&su%soA(6I5%{O+CS7SVw5Hl`BHj4C$Q^?BgZzF_cYj75SkK?E80g-wUod3bz`VpT zd!vH!lN7$049g@j99UVgDY~)}qns=dQ7oW$_tnVCsw=T zGBJHa8cQqyi>|4;YxiTLQbUhJzjH?Iq3@g?nFMm$CCrmjHe@I0}ObS!ha#!lt)xQdbGW5@kCE4DdXgpHo z#`p0E&uM~)f=d&(=-GQUlM7t0*){9e_dKT+Cq7szRqaYu;qizHZn&Cl!ewl@e6OKe z^%x5k;T*|oLg1BDM^_|nqK(JF5+=ST7`()I|FB>oJCv8y!~Z}R#^CB;7p8Us%xdlh zzZ2sC>sZQQK|<$-$_>FN=5r@Jj2sRKT8`ssRf|@%gy093P0m@<;;>-nITk6|QXa8~ z$mT6lQBS#7?X4t4kbc zzF>>~9JV9@fu5RSow>y7K(Fn%z`-@e04bC<2FwN#i=v^4EEk1fxEqdfI(MrtD{j9A zpseOJq_QsN+B3@uUwm}J74+;M3LffUrj!VLx)D=43cvwg8nH!n7=kuwaJE_BH znXF{DkkyJT01LZ+9KSc0D42G~OngJIkC9e1TClfuJr42@pT&&l0B`Xtt4@b;nx&^q zGUGvM1Cgg}sL<$$BRtOeO<+;+f01flEC=hx5Lkv9=I0Ory;6dE^=F4Hc?=b@yF_3U zh9lY6seG|y>@9IIQh4@Y{#Vc0l5wUJvStq^rB)c99uM*G$0+qq+@V5ZqKzN?mV5PQ z?$E1P7WmV3sjQZ%u3PlztMo`RyTjZ(DFAsb$m+d2neL=W?3WH}iQt!}IpTurXWs^K zVq@B++s}w8&l&NF)vu1M4Abn6Vqv2BSg6?a{_W-m{^BpQt4jZ zR52z_%780v!|cu}tK_3Rt~yz%4A+k$M$FEF{Q^TL0mb=CeeDDV?hs*XzLgy#fjx|Rtov7X>mNk z)S^^dflSg8bUB%FOcsEK%PjAzPr?O<>WLilZnj!O;;I)|@CW<=*_i&L37=8%q1r6w z2sKS1_YoIOo2{2H_`6PBY|!S0kC}RW@v$+lxGMXPywk29;E8JXYghyW-ci3)ml$KC zZ5Hsz7-aS>kx%Pb5}gPFq3t^99vGiXGcz?Pttvp-k*HivD66a#7QS4)Y>29Q0-{;1 z6@dE^A+#|pWT?`pna_fV+LJ5WTd>oh@o;zZVS4K$G1Q5rZ;Bv87Gr&&_YqLvbAbpc zVp)u6g&_>~7-B98?;TxSsF*mb#Sx}ggrDRM;kpj@aTrWqK73lq*^Te;m^I=Yop59+oZY_&?swW=h-CzT^J zHhQ(4ST|$~a;qQ7<1Qi4i&M9ASMBet(D^ z(%^a?Vt2@*2Sn-~)RlhWAcU(g689h^E(aORuR-=i%2;OgU%%AcK#oI~+>E4b*A^#W z3kT(GiO@Sj+<5Hc>640u@(!(tGdgpwr#jIPd&(ErV`fn^s9+z~G;pnmiI>c`&Xse* zMUFTk=Bs1Y3A^;S6#h8y>K!^0Q9X!`>XXCA@MDq1)2ApB{)7%cse|w${2B*tE$jTJ zb+D1wGV8G;J>jX)!y0Nc$rtA&bkJN1|15_=*-?$|bu$r$Z|lat zr-Q5*ra_1QzRpaO`44sWmvs0M9sXk-epH7a)8VT+2pHjC(IG+b7|U&PeJ?j{`C56Y zkVE>6|5f_sB3||E^wH_VoK2TY)4R$O<#KtlT;W)#6v`$39kW_ujHfok_*1I(B8Q;` zTRE0$wq=0XRY{Sh?=O%DjBdi%v3#(b-V_-C4a{d)hlTJhc z(t`3Bm{9}AKuF!z7+62-Wl=ZLH0Zx;Nf&FfNx5(%9L9kf&uTPtSkdu#xxST&WRBR& zGV0~=PkZ_#*O0tKvCls?J39lbUoK-z)ZS{m4Ay*cyV)s#i}%MVxM`Gl56oi!hww6XgAJJxoO`sN0I->~RoieVzzwwkwo>VMf%fs+&ZTgYbCEwcadA_-K)o=VKBd_W+UnmRS zLQJF=OBxw%=8tjh67Teh3VL^?8<=R4YL))^8wO48Avtfdt`ALr9q<8bW(4qXF^B>YqQnvrQ)F}d(&d}2QEKd+R~lY1D-3V{mR1^SwjxNWIc8=36R$Q@MV|6F zg~aN-dBq`XjBmf-P!*{;X_EFQUUYct$`ZlS(?2N%j+iEB0KIZ-4Tfwf0wA$bq(D5P z{&@;2$j935&Vh%ca|p^~tg8MwTMxn#CrkHmvxe%W%$ta3{CagU`Qw!|kVOblEVyo^ z)v|L4r$4|HTtXy%7vG_g#dV#Tz*(^lyWDGKCn}ptXX0~}~F5{4NxbhE}_?3Nk?_BR4XL422B2X>Sf*@AF9a zH>F7T9v!C5UY4_^AjHXtg<3lk(}`JDjbJ->j~fpguSCW-Zuj$y)|)ZaF821N;h&8A z?F_)4(>dsWZUp*XhID<6VC8hPp>W0+cVhw2S&o647B@+rg{HzYw0HeY^4Z77_}On^-RtlRx>F`EPL+E9$s3t| zd3~R1<(XN|_bWCd&Vy6_t}=OWKFzZS?AcHIXHQb{VE5qqp({CRJlDOOrw`xAb|-6x z?R<}&?@^sH>H@iRb{$tMc~Qul5>od)R`(Al?V`>%)9q=O)UuaG+w}y`?zLxs&Go!r z_;R-M^*1uz`Jl-2f&slvTH&&>nCZ7ve-sOLG@ZqgWZY%`Icxw$eW=G!C>=}TF|0sN zVk9T>uG1yYgtWTYSOGi1=$3Muz%=bue!ZhJ5W7Xtspw)88?;u~`&#O2sDr8%B}W2> zWE5r*|22ISHYw$3W#uJ{>yp1F6;GfrJZ7my!=a_w!qK?Z))=Q%syd6ijhUFuEpN85 zcPH@#PMbs*iEFCI;VfsI?hUnzZzft%lno=5jRMw{&WNF^gL(^VrR*sL#@PB*+Ao5v zydZ*3QpnQ6rCxPAJ}<2~4i|FzAB@*1EnRbv1)_1Y)w1{x6U}R^4mLoe@-4zVUi<8$ zCt0Z`$Vr(@$W|mQMCtv!yx`rGCi9r;JKPu=@N`Vg#y7%RBLL7Eb9q`_ydU5=i2xd^ z@S^&aXt1yW-cD_^3T&O{%1Yd=N8mD} z(-SJcIGl|#(&}xZBtGy|WTk;%^susWr1qp>Vv_SyaSF#HjE+s*_#X56_6@RZj#_$m z({Lt8XPD_=;F%AO2e&rQ7b_lTK3 zjjqusjRt|Y=6KlPJ>SAA#33)Zt3+K%^eeed+A@sYv0pyq(&9ZXAaHTRBfG53SB z#W4F~xNx3S;)7I2adq@J+J+Yv9m`3HiQk&?Aff0_p+HotFKM-k4@NxWv>C`QlyPIk z%alxsC1?94WufSc#l!c?7OBt(-o0>dcn^Y8ctnSH>#$3Q^E${v;BuuFEP|{I0U$HM zMk9QfJC-N#HLdhgwV2G6^N=@cWA zu|KS`@AAwq@3Ez93+DN%pG8gak{0wC3Lv_}Tq5jA zCTdHIc9GR+S45<|$;OGJMt9f9TrAI^5jr!sLxWHI3ZmkUn@G5D-V~E&r9qnR2Envc zCO9xUOy$+fR+o{CsEkQ%_NTWS*}k@6y2A{qYw_Hu7WO54jL(Th2z7!W6N#GVJh z>sW?KOYk24M+p%esg49uWsvoDFUAIH3%N9eOXkpPZMo=5|*QZ&e2G6YW8iQmh> zHQ#=_Hr%T9@p6N4@0LU$7R5TeXuU!-;X1Z<)BAiFI*gC;0Hq;kQFAV|1(HQ`+LFaH8K1rBe8yD4X;WQyZrkuZBQ%*^)h-tnw`qm7tdk zI54QYoHI_ZUs}<8I65J!FYdCv1{nk zJCmP%{)G$YpI>_El?&%q#U%BoZ+SM(@abIUpq%{*BEtuPwZ`Rx=ohvQ*{TIX*}{`- zd{DenA_oLPZAy!K1MdaFXfAg_(K0(=zrdsAayXPNN5Z7#XQ)_4268kgs)LyOX_Kg& zj5Tds$^{QtB#Xtt6m543l5;bO2Bn5gh{3EIs*dF(AgvvloMTiS%#Ps5pyUQ8JVx&p zrk${JALq_k9KS036fp47^n-{94Oe)#S4{)vD12mlns@j|7iWxXg7Y0svc7}Yz>Lov=!Rv8hx`vPUxZ@ zoZ-`XfP;iB0=%QZZ?-VNwK*9ck;(6p){a>4x5T2H3vQZ?oN9g=`$hMObUB#Pc>B8} z%^g(qv`xd#`_*d6GTMwn!RxGknNPRe6^kwKYI=;&!x6B? z4bBkIc+{cy6PzeeT;5&4_5fhZ0kXpeGVo;Gti6Cw=S2hEdBa$}7#nUJmi<{C?l&_8 zLvxR`)+DxuNRZb0izSe(+ zIMuIkMjQV-T@Z5`l%6+84WYtx>g}EIAF1qcFT!8v+6A?LjuTOm8A3(Mg_0@az5Tc7 z$4y3Uy;bK1=Y!h)Yi)HFp@o=C6jWuucu*t#w$$IS@m6O8t; zNo1)-I)0Fsg+?NFiO-WT7ZOhpSi1j~(}-(h=fRY@D&0fk3x4&RNCddg+Av2?)1}1h z^hfD$pI!IfK4*N_u@A#-V;ptwdpC=mCzgt0@)ER*#y}X1g}#`}*b`<5l!lZL;5Asz z=2d~w7uBf`rAyLsg#Q)209qzLOnJ7UW`+NN8{t3H-IUl6iXV+)&sVv&F4!6NLta@k z7E3u1)%d0iT`|T8hJxQ2J}wH1IlD!UoAqpXK#?(qHMy-r=B>*0p8u#8&6!x|G!9y7 zY-ZIBkkl`Yks`mO1X#&Nyc86_%B8fnv>5(*UQEYKw9_OCr*y%uY$~>hFTQE>mw9!? zDt0;tGF7Nv8$RR+-c8N-;bO7Cp)SYWelI`hC{x8tcGl_moM{rD!-?Iw2gg58i699M ziLWGR;Z_JjK*(#6@)TV#Z3Ik_9VC`mB2tE8@iaqy&`%Qe z9!k+WSx(RJ>4Y3Kl;P8o8^vKHLfy!GiOn2!9m>ZH2~V&bsPKOMVpAzem1%EwoBdfR z(;lf|&gv?5O|nRQ-yRVjA4LR4RQ>6k(n6uGCW@bq^z5=xn!)Ss^tWZ@z@?lT97;|HHVfctlCI+#3U?T<27O!yV`A|4gADQFm>=dOXrP<)Lo6M#Q~T*$F;p_?V$PLKu@vAh5`Y z*_T<8$MU`6wFlusWMA}3kfi*5(Skf;II)Hh**U>#_C?9};pfo_ipZS$F8S|?AlEHZ zl1;L%R5huzQ;GJ(%pP zMHPcQsl3A9v@<1itmt7iWx)r_88(_%)%Gd9Pw@4RSC|TWfy5v~|WStC-sLygWUgMmFvo$>leu zQer&6?C^M+WG=+q5O=lE+cJ?WWX2iOV3Ad!vP54emw!!XQr>PCXskm2sels6O=$2e zu5vi+Oxz0{67n5td{t-vqYh$x zp{Q~w3KD8wImEFJJgA?^!Hir#!L@&j$YUFzmJdeDXexKHY-WPbBwnUjEuCS>%xrmv zr8E=eIdk5u@?`v{E}!DlX>l0xI7Q254*DfCsD-vb!&usB>|yw`rWoE@e94? zp--_V0L>#hziq>epa4|g48VPUE&AaG)rH3eNp#(~F-yD`W-3m@QT#f*!q$A+5-Epe zEO?06Ga0>!`jM4Ws@1sW*o;-H{meEBf{V#|W`R2Bn|Kty1QI-_Ny!CK=K!{7U@ab0XBgj<7L3JFtR%g&*H+2TTA-}T^h`eP$ZH} zTOO`a>O0u;(sw@X_Zxqy_KC9>PJihy4 zHngNxC6#6PG&dG}NX~tnhyOx{f3Cy-t%K?e-{CMQD&T zxuaMSMz&|I^LF{3&2V{_f%S}+LvHV-I!t{LLfjzl1qy))$3SwCWCkaQNZql9 z4~h7n*VPd@LpJ+AR+~*YGGlb_-9s%KYqsYJ`_p%9lGLL1sFf1kL0SyMu}wwjgf}Yh z+9^S3Fzc1iXl@QQcbpAOPyI_ZS1(o0=ic2zjrH|KnpaMx_){~ZPiLMV-=Fz@v;z52 zwY{i;Fkj!G$mxK1<~n{93GPF4U9`*LeKd)OIXO%@p8BJYLlf&QR*nSMV;8wdk3Up0NF*P5TzA4@w zPYCfuVNXY&Lmc{{>uv|%4mFv;(^T2W;M2m1SNh5;N|HJL7 z`gVkqOhKk!c8(`RnlS0yJlldCM!?e^6f(JC3_g6jzQ;JNfzZcr`Whz^?=1_gjs!sH z7ba560?U!mFLSVC2ED;BX#rpqHfGL`GC9ofV$~)I9Lv@!?IYOVL#YmdCM!+Fy(|_Gg?m5E*B>KT{`bDa}i;zr&wUhxu4;7j`eUwlQj;Y;9tl){ati^`pprR2IkB?7A0o9Qif zUa})hggSqVdOk^+?E1u8kc=BSvuhU_iUK9eL-iNeCm|I1x5zI*LOPPpS>}Np>n$r} zAAp!7ZCW{eGbL57la<>0|voYZ;#&(~9JsX5RavawJTwPw8#c zT3V+sIcbxnTItJ~SKm>Vy|Giw?!rDotf(i4o)eT@g4i8pJJAtJ4=$y4#7rr*;X@rOrUs{U+7Dv(uC%ic>c7g6>*dNE~1t) z19eTyKB|;qK}eB>BwVf7B9V%1CLu545j?hIKpB%XF`FVEBWZ7Q0%w7nh_lFU5wmD! zi?L|sV|fN51YrUt>`W}#$QeSKW>s9SFG2`O>^rutTJhvES1E@_NbxRw;}Pl?yvIjr zAc=xQ{D7nF+!Fb9BK4E*;XD(L4>OCCD0b zAlg-p)bj34To<$Mjc0HuVE>eWA>koY=p7j9xPn6bR2=eF#hvC!sFz4b@4|~w@FX$( zN_I`gjqQv6X&(~kEvxv33CBllvRIbwR$>&EUA*qCf|8;lvG#S|<0#Nb`%5(CXH)Gz zG}QhfVP>JUM5ecMaNKhXb7NghSM?Uj11>UKRvPCz0>d+*WRaBrNN8l>3)O~{XqEu? zG*A3Zki-%XmiMs?jL0)ECd+cAB6-)2mJj1ZdZv7+Tr$7SD&^xpA=fEBoo6@W8=Z~d z)EitQ_d=vV&gFVD2Dc=ml$xPGeT(v*;p5^$b6aI-p@o5)8g!_uRRiRhQrwq3;TpRy z7DO?jpv1~N={&i`-i;TNWgM(nPcOqc0Z$7O5X1Cb3q+04a$; zZNas|qZv!Uu296@ZU@~|saS1_#(b6sOlp<5`Te;yLSD^?2y0`c^L0i)bJ_*Qt!hDX zyy1w{I)0H)Q>dt@Q-{v>D-ttsCB(oJtRmoEkf~59>zu8u1CUcb5CG)&gY$i}1f6k3 z&5eyl&}3OMiDQ!$I;!5Me~sUstka5e`Vr+9*CT_Dn?_7dAFP%FRcdK*3^mbmp{B`x z&WbJj%iEwFEI#{I&P5Li3Eez#NwLFOO%ZRw&}cHU#$A7!b7yZzXqzDZ8yZaEzuO0} zS9JsF!I6l0@o+eD?zSCFRjs<*t(#tG@|5JXzaLM?(p;gpYv@gJ$G=5%>HV8O0j?NQ z{An@8D*68~o(ziCgKryC{_zN=-0Q~AD*JbbCq%kserE_zrc&=265BLIjA~2GmNAPd z0iBMfgYgD!nh+5;(|&FxK>eQHsTwmeF}s!I6u(zAP;ow5Z4tfoGS?hQb+Nj)lz9Fi z^};=_i^HN$ZV54GiA2oLsLG$w;jilOvpT$?gXX&1*i}%wgCH|Qkzdq1WxgyI3%N?M zQX%?fx||PnZ<1`G6A^5y}M|-!*4G*#|B`x9yG`(RN}))srV^`lfS2BcVWA$xf4!aOw(K zuQy4p0|!x-^(3WL5)WW6s;??Bhul|@*c+{AVJFLUyC|YP__X&Fr#{b+XVP34u`!xK z(w2F;m7*I?xc@fAAzR5fP8=dE)3szsBq9p()*{jWE(G8MmLn2H5#h-gCE}5gTz~r3 z$0phwCw`)V@+?yk+UI(Yo_4-J?FcgjF6ZBmynk^lQb^XcTxbn7&E3tENeX}M;!kRU|}?dyEG2`@4;D7hvlW(Fsv8b8#t@2D># zUKw;&w((MCQe0W{Wh+|f+_+_OGiJ7rX=E|{9iFqk3`%a{ zmQ#d8#3H0Js`+`9+bhf(9vLg11G?T~Btr-**WPvWz}!sRFkYzEafEG;-el0drCzq1 zB7Z3Oi3mG#RxXswp{}oTDgK+{>=d8Q4|5nIWeVhbz^9KWHcBpm#0}-Zb}1yvK83`j zPa#o3S5pd!slcU>nD!|ocKH+%Gr@u2Aophp0KA*;x!`bc58u0kdxLlJJ#*6m zqlrllWJs~nWV3)nlDiDhCaL?lINfFYOZb8IE>@LnKvi!wd-88uqhF$b+Js6A$z3ys zUV0J5D)QAhZNSHvMXN{>k1byGo0C1CaTlvOg+ zq3m_$YBd=fKzH72$JNIcA90!*zRjc_=e}{yToMPsB-jY#%85uj@Nrlc&Y%p8=qD)R zp<5fo?m1)|uU_BWRtN>15h{@1gRK}XNG+-I+8RwX;dLrw zh0#h+bS38n15cu=>Fn@d6hbz{Y9q#}MSzB7QK?&2V{1PhNAzvgPHHY<%ywNg*33dB zj7u5=Q*+rz+NHNh65H9V(RLKcLY%n0;9tG^!gV54nJwC<&4qcifF9Y`EkB!5a1m0H zT5A*{cii0Sk(8pcjBROC*vKs(2LiajsL{=yd07lx!tiJqs6W(uA1mPi;D7k&5<)XK zS@5NIMeFS>tiT~Bl!3XVw3{(b#Q@L=l)`O5tn_Iva5fHRiMHa5-bWjinJ~3OO1df^ z>&vVYrMYCGmEOh^nS^MGONruIrE}+nO$IXF2sClM#+2@6xa4L9bak>@ij80n9bFhqQZI zty>s^2h^6FmLW+NS6u83&2Z}> zVmNt1zS)*OFs|hu-J!DTgV!?>S}yC8-(OlJ#fq3S=@vMr>dX*$RfOGw);m|4Sjc@a zkM9V9<_(hE01W$bgXVaj_s#(dzu!@t7rRoZJ1&m@R(TlxoD(uZKoyrHj-22eKZpP} z&I3gt{C|k2cu@E+XYdEFe1pKQq2VO!q>W}hS}08A5mQhUvv|2vw&1wqQdEu|jS(II z1z5cj!ESjnCL2rVE{snR^L+??lJ@)At;!k>Svi`ud4UB169SAoOftU0fk_3UpsDa2 z<=NqkrIPxd*eSJ%9l)il=gJlqJZWLUBn{@ul%45e#u;9=N9JKh=!K4TavM5XOFeza|2o%${05&Xu1ui!3$83ot4PLv0Stn75-}# z`lJdKZN?7DrZ+LaY~B4}!q0MBia%fzM4M)$sIUPV%!rYxQj&%uh4~CK1HVklwFuvJ z#=VEF+Wd;4Lpr-hhrg@_T-4!S{d|`WBWl4L+`)DFxuk$qmkHH>m9}$VJedgHw z4#6i!;Y_i2PXd84C1cyDO6z05ILC>AVM^w1e`r%=MQ3DL z?e{cybFB2G6u|VT*~>33F5;NAQ5Fuk*dgC@ho>Lpt)XxM4c>Zcg@=dVC!qJweOmpL z8W&i|H4&uPJjC5ZLXtI>b;@CDvw_I4X%&1CjR%_xDq2%wzofd znam+^&WZSSdP9sxPw;~nK5xd9Ma0lXW&~VaF7&{zJf;u=LEBvuoE1AS3#*~VV7Ec2 zrjX{EBQ~}l){r5=$lPc2$S~Xny9mnYAW%9mx7D47Obhd1O#DTGgVi{_A0v_s)dR>W?dT*Ex%;)+xQSDss= zuD7x`(9~o1l?CaJ!8%klK^5dzs~R zmoA$)VPaOacTvQF&51RvfVkDN!e8U8mU3^!_<@seS0bjs&xEr^VdF7VF|p2z@OqoE zsNS2W(KR-SM|x&noOEh=B$&+oLIH&m0Y+_(b#?B ztKVW#$4i-R7Ca+h%d!h$X54x(!Lum3W_+*Z0HoJ@={0>;boT);Y$)DT5L$8sBNwXE z;V}aMS$ml*(sBpI7OA0IZsx_ke=F7dr@n>WN41TC2QW6SQ8ZnIUpCPf)Qo(1Tum}# z;_I{&KB?=WHAA<)SIv;7 zsI?&`K{vI`HR*mHdreK6SCfimNKgS{Udod5H;41`;4PasM2tj`&hW8}dkW?vP-`zV zltGluIbH%@A0`vhG8iuH$gfE%S>}r$(;-2|%^harn4M7+q^eGhheCoA zgI!*}cZB%Q3I;;$Oc{oqBEUnXh9*MHSx)ej7ec1xDNw(I+?9Oiu((Kgob5C+Op@ce zXC~0X(giOf=iD%A1S2^k9H};VGUM-3T8`1k zYck>Vg;Tu9rlrd~{-}PQ)WLkUKCr+hsi_yAx8m_Z1Y@u_iso}-hC|h#5+ouy)1u7H zr&7uF?oFUE?!g!ynytgwYcrKTIvkh;XTe-CdOK6H5kY{vHr6f#vJk#U18Q{0$l<)@ z1oa}(w3fJ5 zp$8tb3)iJKAmvE|-YGD0)dHd2Y*q6yzhMh;^PQ*PU<7vxZgUpO7nu!{;ak`Db|rfo z#`8SOXL!o_CZA+Ka5m8OGbU?mL~(q-LgT{+ z1k6PagUPee{t0ddNe2!;tAlBzn>xFX!;mBHcXd^`JuW+5_{ZG%>jLxroG1cjnwZ@= zzLmlq*`1@(zw7rra=g5A$q+aZY$#z#j7mZy+I$O%{x+cjf5bz-rWUCNVi8h|Mw)|V zE^?`PZv8G7%^3>=uT#-j#o@QP@f)dkr6{G|Wf?Mk9?SS;nj`ucb{*k_@yl$nVbXe5 znL0^F>cks_;z)#ML*&nW6x&nXTZXim7lNM8HsM_3PDwZAT4|;+S(6VBHQeVKM9xjZ zyW&Oq&Z@m7uNf9PTlX&>X-_7ac`sjmPjyJFQu{ml?6da2GP8p~)z9wBqy?&8L1E1? z5MznTv7ccH@^>R3B0;7E7Xi^4Q$tAajuV*jmZK(ow{ZM7X-t{}kfw^KYO_}EFs0C0W%}p1urGXu7mm1J2eSsvn#sPhg}6YIpHoR|f zY;ruw9s%X@UF9kAwM_A^#5ubLgnDk3N8&%l8J^y}L|eIT-#EUQ+)XP-*94bvD~*n_6c?!rf!kTKP&$$3v6C zS`}a()izWNbuGfzyGu{wP;{}THg=6e=Wa;Sn1D_tSN=(jn1bERa^=A;bB)_59jgt6?41c= zxW$B({R$6c*QF910+A|lg7Hc3svadS^u-~MwL|6II-0TGN9$aY#^{1HX#RU-t5;bNX|I25Lx4fP~ax)ckCge z^=h+=OuQ8$MzvPEO@#!Br#RU#2^3JjNfrwYQyW*jiEvD)g!+uwC1 zs6D($&RKGKS7Z0}c*Km_KN1yMPkQyJE<(D?ZDGw zF6vN+2KG&jD7JstJ|22&9AJdCX~_EfyPhnhVo zx%+aXng)$fBU`3q%8&Eu3=p#VbC_?)8gV1nB{{&(?ylw9)Sjp~LZ_P9{+xCQN5RaK zPf{xrF^ixqk~{rIjunQPo%!}W_e$J*5ruNTjmoz(-=D`>${wm5Bj@Ta&MV#-`*wfA z)Xg^oR{pk?mol?Hjmpg`;P5N`-QiaPlu#V6Dpd}DhP%7S^5pV1MUL3a`b_wZu2kJw z%h~jeK0A6)`WxNdJUbuAvAd^TvYPgo3wLjSl6Ap*T{+Im-MbX#{6)Eq=li>LFOZXW zmpOm;nX7kyA0?=E0C#excCbIg@lb!3Nt!&A?7?+GU5ci`FtzqJB$#((db zR*5zxD!zof@)l*Lx$_ox*q_#(-NSG7Rjt&w!AR$n0XkM@FTa1Ad;9qQRj%*S^|qj( zH~uzPW^~1jGqX$P?J_Ish42YR+%k2Zvm^!!SuzAP+r4|9cd24Q=_Ie1^8h?qeWdr$ z8F$&UE-`vd^H_d$B8NzCXR-I7QisNy14I-+&q&6I=(C<*?7i<;)#*wGNWU1>Fle}qi7Q%a{-HpF7_V3`MFh*E~BolM}Fs`gCNQ%zrSIl4=tJ z`@$aQ0j{cnPwAjug&)w_2X#+mXt^XC>{d+omMAuH~prw4o$u`#N zMBOX>qK7A0Tup$pJj*Hd(KN?5c&LEu!1e>ml;*MrNH3wi&Py(H7{;3tX(serjzlF& zaQVeZouFSiE#lUGEViUO9iUK9Q1&4{DyfCVZePF(_4j5So+J_Td77cWDLx{cXZUnp zpjXPa!iVVcog&|*YhUkYv7&x$X9CL{`AxzPkZ(mcwZ~X!hm1@-F;ZOVSHizV2I_+QtvkHWJwuD)XE@g#5*4V0<;`E~mmnTxN>13a)l4vdh}H!Xx6P-K(IfSzq7DzUQ6nhhl3F&^K$JriR8vSsC7IsQXz&pmqBx$EeaDx_<#LJA zMn%K-=}e@xci$OPnMK4T4u=p|zqV5ZaMP%4?=p+*-Dx5+$mNO_rk z!78Glb-JvRux(;S;2!R0^p*s28LBe4a_pbYVK`Bi?vDxI|)jQL95(khTnE5K52Y!n{=vyr5c0k&_- z7Q(Nwnxx3Qz%^@j_EIS3J;Bl2xyhYX2uO$`Vd5*Sc=iI5yE_rzolM_-^_NGVrp`*b zMEiiO=RpN4dh#AyO>NA5ip+rnV2_+=K8aW9nj2mbV^`bmK_*tWyw{KKaM+7k(y2u}k}6$v$e-h- ziXaFp0@9_-4~%5NA*KU&-wheH^PJ_vrz{1QuHy0$S>&v+bxtS%Q7`hV82%E+l4Mhp zFW=HcXye??H9j7-8k$1FqmAQ*Dqw{iS{#gXEv&tvU{y= z(CAI|W7gG9$wS>e>w9%YWE%vZ;#x_+k*U?f>GgTBEdTzyDD#3{N4f;%i~PP8%=8O< zUk24@_e0LN)`_c`Tb~gtus)OFhx=~^w0Z0Mg5BI9YYB`%?w|Rkn0DtGD)CV%BAur> zh<4w|!t2cA7Ty~$I0Rbo?|XAksvGjoKE<~(K|Q4aH_qXR`!lIPw+k)@eIYzRjZQ)_ zxkQF-tnWs`KF_V?DT3T<9Vz#X4)bA;emX5qa*5q=;*Z&}C5Q~BYHoW3@V@gEA#W7H zBm!mo^*rPeREBw}VMGLKmxY9Nq-wB?`Mu)NPVH!?H--8so8ieSKXNCkF_eVdf|U5u z?fn_Kqn#md#vmJv&N@ZId|eG!t@tt)?$q2k6sA`={<)one+R7!zhewFP`mzms$G-E zy$N=qH|2%Jlhx1dm^-~!I@a8x040li@i>vx3M95;CYpQU8PLel_%wg1PaXvWg(QKge>-)&B5+0RW9UE;;a*a zk8mdE@eIcU@?;|5&Ot0KBT=rmZ~PFC_xC-^E5#MVCpl`iJxO*y=G%JSMFkK?Xq*sf z6-5|=s3TjZy?Vf{o$OA?->dDlnCe6iPPz$bY{L>cZSn#KeA9JIci1NY{1i9R&ats+ zgjMM8#PhC*C_tYDliqx)mmbVRJ*@Dg&7yOhKs5;9l3!mmB|O|UI^yDWx?%=+{W+2n zZCzwvfqJcn2VUjK=l5x+PyO1vz zqLze(If7(}eui3pUxUf>2_^DsAT!QszX|1xgmi*z40lY&G&R)JOKGLc6>GZ%&@d;3 zE>2{*OJ}CMj(KVb?W}51{3x#HbvBvk}*s5+iCv6gB-0zWbw0w+_ z;(EfyGkiMFafq_y5Ty}CqA;^8qKdd2W?ZePjJ~L{u2}3gnzb~4zy}b} zvqf`@4$=Em5PqlnVmKhD{ScymM)ybhphEAc4`ps<%qYT+Fb93hZw@qDm|Z%@A5jPA zE^r|I-^{q6gYa=R;|;w~Dqm=k1r{-2Gt?SsIBM|(7G97v%+3t+WXq#0;BQ7A(e6=IW z=bzf3JQZFLJ4fUN`Zfa##2E(V$R{+M`$Fjaalt&`;L({q=;UW$S1O^j)a@MVwyqJK z&)(g|E9`&OMd%P(<9lEp<9moyX=b@Sx55-)#I9&(22zy7mhQ5%bf0rNtWHbM>LJ=pBy9EojaA_i0xD+0t;VCKJpsj)M(Zqv1y3}ZA z@8ka6>Z~MzXt7ek1Z9cK*ca}KyFRpDNu)!6LZ_HQukeF@9qQ*g^8p5iOBu6t8fueD zj^2UdIV9z0)USg)xT6%GY(VQ>X0qkx+jHGaP;#Zc9TY3*rL3!s&DeTJZ);7A=AD6Q z)DZcBs5?*`GNciBgdj?&3+qY~Y6RBHl4uj*1UqPVt49McSqN=LQ0XY^>11RF^0hw>?bz7h~a;O zYR3kMiW8qyLxt5!Qq$WPH+9IyCc$FY9eDfC=`A8C1|P@jH3}{y(3pNgeG;}Vu9;OE zBsi5zV(XQu=@N{5m&O0-J_Bv)qtalhDP#xb__6SpRQ*eOrHQf+^Aj(ebE&tQ7ekHa zU=pqvcIB501?Rz}>F)Mh-5wMb4B4?|xz3L!>1R;fMvv-*lbpMhw^KS3R)o8Bzv5QU z!#z5obbxSH2RYZn-8!f@p%kP+NiG03(=q+SG?9I}Tya(fB|!=ItFRfYzM->&I*7&( zCN8x$FOsAryjzzJ+a+5uTsNHv)L~tRzox@a=x|zx8C9yZJdQC+ zOfykvP=T^+LRdE06(}CQ6s3r901wQ@X0Y9A$mWviT;w1Jfd1J|xNOpRI9KH;8{1s& zKz1%)$_r5nL~rBzW7EINuF4+r1I^~L)AQxox$n;C-&1^_;&YnM)w%c0y^HS)e7-w# IZ0;lfFBSur=>Px# literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/decorators.cpython-36.pyc b/venv/Lib/site-packages/click/__pycache__/decorators.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..365b0faa63bbdef13d2d2af2b9b39d46636f98ca GIT binary patch literal 11616 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/exceptions.cpython-36.pyc b/venv/Lib/site-packages/click/__pycache__/exceptions.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7760478c7ae274fb3b4538aa2f34642a8ddc1ea GIT binary patch literal 8618 zcmbtZOK>DdnXXqq)T7b#XvUg{!ITFNW5JAU47=-97^AT~5Z(YWfeitTh@MhaNh+zk zs-0CmBX#s}k&nQ^XHKw}joskH?qN@R;l>^|;8ek3Bj92#+&S@me^ymLBoTvM9hqI3 zm6ebG@%@kb(#4CdqyK!T`{z#^#vhES%fbByO8OgAff1NJqi=T1zSXh%cE>i^XZ4(Z zty9Cj9XKBto%+{}pcd3WFoJs6II%lTv>QPa?Pl0Sdjai*poMlTTtK^p_Jv>(?ZvQ# z_64*r21{r!$@4|DF9mJ1+tR*>_T^w1?PX~%p?xK|iuP5R{}S5I1uJN;gl)`s8SQJq zb+oTbdpWrAuCaRaBNInjwR8L7_g~AGcB5Vxd;PGRCEa0^U)l?^ZWbP8-82hArB=<{ z3H|-V=S#f!PvUH%q@P2T8Dk?eGi%p6vDB@=Jg@^Rus=ZW+s27`V!n$zEbDBIZ1)wn zn#aPcHs@ejK{Ymhxb)NvjIp_5d>6~SZv}XF)fzdsZl|}l{tdUewf1Um9;rHpOZAuT z=KA(`(om(_$9K{@$J=j*>0y=(w%K-If z+#Ptz>v#8~I7`>w=q0|_OW$0pURZVVR+L6@nt8Dw=Cy1%=!Ln{i_$E2_ noKb{ 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,=VNc*tu zQ&R~k2axH*{loRr1O_?~*N_nQ4vKgoXs1DnwAglIeH}qjgbNVD2@RpzA{HdDVWc#7 zs1x1P6_i2u)<}icm$Cv&ObkHdzhw{E2 z^1PHpx`;4v(NH^h3tXq*P@@1r!3s>hSC}E(eY3LW4rTWSZO(KdCJhyC@^}Y%6;`?1 z5A~I`x=mBDi@@FhdGEQCY;<65SW9`yV-%OVp1Zd-#M~k2;3KJnSbS~Ua$VQKHSl*9 I{#so6A1SDKH~;_u literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/globals.cpython-36.pyc b/venv/Lib/site-packages/click/__pycache__/globals.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef640c90d60f72054db9ba5d2d2c2bc9a4387ac4 GIT binary patch literal 1890 zcmZuxPj4eN6t`zGX_}@*+SM*_Kr$;(3ED;ogoH*20fm)%AVO`Cpo)|@V<+RrGalKV zw5h^nvsXR{;>?$sE2r`ixWKa~vztY0X*~Y({NC^V-k;A754*R2o{j&G5c&)4j1bme z!)Icc7-CqW3}0c4INqSEfc03&S}*Wb$RgH$fv#F?k9FW5u|w8n2XND7`xmHxbPw-Q zA6Jo736;+h?DjC+zX#tbeC7+7R!0_D%R~( z{2c#+^5AF0T4170p9ct?b6e`%5Swz6lv?xLl0=GRK@yc)erpGWT&BX1gyv+*NohDE z)DTK;=!#g?B`|1`aPvPM^=qQ0*F3Rgrd390zPd@d=8a0wN=c?PCn77rrm(d(X}Qj+ z6-y2VJhFIJ=1Dv$s4?R$t_fj0QJPw%8+}g53Y8OKyrM#jjA|iQ4)v^*WTrGpxh%Lg z#FR1IU zEN=PhdNr4d6s1YuB=MBQ8MkRXIo-Z6i7NPHm+?WZWH2C0zz!}_#yE>(^7-JC`YJA{ zrWr9pLIh)AfEaEaeUnRFf-;9p`&g4Dl_gMa$YiK;xDqpxt0vqYtc&T+aB*5vA?Z}s zvE#t~jF!@ow50%Kz?i1Oa6;dtB<8me!Sgb=BIDm{t@LEz<*LY7nk?>JX5YPk=@P_& z14tC22|4h3Iu7suh0%|yy*ankT3xlrzDBB}Z(t{;rLEOh-LstWTW_lB?HY!%YS%>l zj0CqK}f&UpsBo?NUT5weqv!cUKuJYSBa zn2wCF{G^HGo;{>)sj~rv)}y_#6Heuc4j7=?80k4NyJY*4-aZ z&Ri$Rsob^@$1eodV{OWzE-f|lhl!HV8)lNup+TL^wq^0M`dl+}O~oFYZDqm^I+*J) zoINE9NYFQ~p1g>JRCEG`XUJEMW2uR5)2;o*@UoBf0r=Msd{R}B#{#>@aHt=+$#eIL zz(>&Ilw5Kk5m1k}4jqGJouyF+GPH|3SikM`;%9w4?C(|kHr1Rm=!n{pTZhVp+h>Dz g^!o=p=3d$Ce3>a$O8%8g6mO;%w!`k@PSlD11M&F|g8%>k literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/parser.cpython-36.pyc b/venv/Lib/site-packages/click/__pycache__/parser.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de6625048c2a5e76c38c19793d9c2c9e9a504ae0 GIT binary patch literal 11479 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

            A1kn%(i zSG2fZ0^|p;eP~|ax2|7rd+I7CD9&S3>0>}i2oDl0kl#=;2F1B}3xRBeEQ%lyB4NN; z0^={R{5$H!d>OV|!=J?Vo*-4AjA!1EdGCo1 zRi;AhX%)jXFoK7Rq|0_uOcC=SZfH%d1NS{+mebZ=@<7rv@a8t6h#Fct@4E-}CyYX@ zXAWp%%+SI#gJp9_5hP-i)yg`hEH=A!F*#S#X3-;hFZ-pVVz)AGD z6dWYRkY4pS7#K)qm3pHk1WHF!!8dUt3FS&=U>2w+af0&>h3;1;$uQn2^U5PvtTMbB2QH`;& zp`og7g!?3%HKO)xYzTJUKsLAP!i{Yp+NedRXDXJx4)o1pv z8b-7l#L)S55TZKZDM?thoad0KNzBxpp-2$MN}_aGGoQ>pw2mZ8A)?pNMl=HV0#`mn``{Le=6sdEz_V68q(vRzm?r+1hv2n;{$uub z3@N)9Ld?}=+yHB8y(1E3l#dJoF(%Ve?@Lf6T4L}2W2qvu;{^Can0k%levPjDe~Q*e z!M?3}*t5ou1ZKYx`Y&1}|7^4Wjcz&&aLIuEiytBRtYwAUXykZ0B1B)uO?ilC?Og!L zL2SZz+#z4cJ7o5*;e^CSX76>K*~2@BY|4MBB9?Zi*x2q7NEFsfaeM1VfQ>Z-#Q0@2IBpAn0M zHYXf$(G*iEbK78eMiXe%J)|OO5SEaZ^iCRtd5=mqX$-h3kxfoc*@R5Oweo0|Q3+(u zmSEMA{JXt-87W1{xWK7iK%H|?o7UiPt*OH^hC>S>8T+`gr&=(s zlAuhCeN!J^95wT%*xs3ie@Zy2Xy2b90q8(*6842-3i)(KJk{2_a&n|OAc0X20~U=4 z2OfwJVB#4?wq0$`@8P~v13|%6vZ%1*a98~O_3_ZafR#RHoi)!QwYUH2 z-(aN`qsSyuOc?cD-0bdxNd}T(DD{c^fd$(Q6T-}E$RyiUk{$O*2+F|&n1&;Ud$8>` zauN4vOu#hFFhJ5lO@$bZIR!C95Alw9Uw~PQOyy_~E#M}6Gk1hd83d*R?VuGxG$K14 zxr=*TtQe3ocgoOCQ<%|j;o>(cNF~k8Fo`NO?6@E-ju%hTavKdkL0U4&!-Fv#E8!r} zL5fSV?@V%C{y(USb-vUem?PFYSU7O{22pC2mNplT%^=d3ykmL2NZekP2DHRa6(&t z4p7UioHAVXw zPS{dv`_Lv18nBdRL;~qriI8>@|REp`p6~)pFq3g7`w#SN1BNj5+UEY<@ z&F8&9w^QBY+r34~dK(N)bz$N40njD<)Q7E21Y$TlpEd?{^~f5-@nApq#X!oy$XOMHeNG)p~Lm_I`g zDDMjx`P3BkqHVUibkBHr2|fsG|B>ONU>Uo>aogbZ)aChcl{l@hy(Xe1LI$Cx zz&%ixP_|d*k%q?hYp7LS7Lte*Artu$fjS{%C*+GhL=0fz4AMAhx3rXr-0=j+fVJd7lLI+p?$g6brVClLXhCgw zCpmUHB8!m27TdV6t)Fz|Rj{^*ntFfyE)=1WpMPh$+pg zHmZl#L2Zve8nPjr@MDL-HUPx00T4R?#NR6cG4j`yPpiRHQ%GILEr-bN;l7?ddWZRO z4~OA$e5Ap0>A5~Mzo?o0X{-&Wdo?-dJ;LT3_1bvQiK#sn!f=6SFXay(%HOBp z0B4r3UObVHE$ZEFnD)9|ow)8}HnqiKhXt{TqEl6}W7#K{c4WEp1TG_&pWzbc>$M!K zc@9SGY_sVtdrj{%p6~s(Vw?6EHN}@&MNusDQlvU1gOTYU7&W8aUZj1n#>1jN6d5JC ze*Sz%Tjk5VlPLcNhjMj5pkr@FZvw+*%BQWj@NVj3tXEE>tz;A&*wvPK!SO!lxn9Go Ndn?N?FP~X%{4bC{=4}7~ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/termui.cpython-36.pyc b/venv/Lib/site-packages/click/__pycache__/termui.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..83989930fa148c9099791aaf82ac191cfaa865df GIT binary patch literal 20828 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 b-1(q#2KVk%W~!(0_oeFd#X|K$rSyLR$L@?T literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/types.cpython-36.pyc b/venv/Lib/site-packages/click/__pycache__/types.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dbb974a15bd007c5edaf65911300a8542f9dedf0 GIT binary patch literal 21875 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/_bashcomplete.py b/venv/Lib/site-packages/click/_bashcomplete.py new file mode 100644 index 0000000..a5f1084 --- /dev/null +++ b/venv/Lib/site-packages/click/_bashcomplete.py @@ -0,0 +1,293 @@ +import copy +import os +import re + +from .utils import echo +from .parser import split_arg_string +from .core import MultiCommand, Option, Argument +from .types import Choice + +try: + from collections import abc +except ImportError: + import collections as abc + +WORDBREAK = '=' + +# Note, only BASH version 4.4 and later have the nosort option. +COMPLETION_SCRIPT_BASH = ''' +%(complete_func)s() { + local IFS=$'\n' + COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \\ + COMP_CWORD=$COMP_CWORD \\ + %(autocomplete_var)s=complete $1 ) ) + return 0 +} + +%(complete_func)setup() { + local COMPLETION_OPTIONS="" + local BASH_VERSION_ARR=(${BASH_VERSION//./ }) + # Only BASH version 4.4 and later have the nosort option. + if [ ${BASH_VERSION_ARR[0]} -gt 4 ] || ([ ${BASH_VERSION_ARR[0]} -eq 4 ] && [ ${BASH_VERSION_ARR[1]} -ge 4 ]); then + COMPLETION_OPTIONS="-o nosort" + fi + + complete $COMPLETION_OPTIONS -F %(complete_func)s %(script_names)s +} + +%(complete_func)setup +''' + +COMPLETION_SCRIPT_ZSH = ''' +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + response=("${(@f)$( env COMP_WORDS=\"${words[*]}\" \\ + COMP_CWORD=$((CURRENT-1)) \\ + %(autocomplete_var)s=\"complete_zsh\" \\ + %(script_names)s )}") + + for key descr in ${(kv)response}; do + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U -Q + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -Q -a completions + fi + compstate[insert]="automenu" +} + +compdef %(complete_func)s %(script_names)s +''' + +_invalid_ident_char_re = re.compile(r'[^a-zA-Z0-9_]') + + +def get_completion_script(prog_name, complete_var, shell): + cf_name = _invalid_ident_char_re.sub('', prog_name.replace('-', '_')) + script = COMPLETION_SCRIPT_ZSH if shell == 'zsh' else COMPLETION_SCRIPT_BASH + return (script % { + 'complete_func': '_%s_completion' % cf_name, + 'script_names': prog_name, + 'autocomplete_var': complete_var, + }).strip() + ';' + + +def resolve_ctx(cli, prog_name, args): + """ + Parse into a hierarchy of contexts. Contexts are connected through the parent variable. + :param cli: command definition + :param prog_name: the program that is running + :param args: full list of args + :return: the final context/command parsed + """ + ctx = cli.make_context(prog_name, args, resilient_parsing=True) + args = ctx.protected_args + ctx.args + while args: + if isinstance(ctx.command, MultiCommand): + if not ctx.command.chain: + cmd_name, cmd, args = ctx.command.resolve_command(ctx, args) + if cmd is None: + return ctx + ctx = cmd.make_context(cmd_name, args, parent=ctx, + resilient_parsing=True) + args = ctx.protected_args + ctx.args + else: + # Walk chained subcommand contexts saving the last one. + while args: + cmd_name, cmd, args = ctx.command.resolve_command(ctx, args) + if cmd is None: + return ctx + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True) + args = sub_ctx.args + ctx = sub_ctx + args = sub_ctx.protected_args + sub_ctx.args + else: + break + return ctx + + +def start_of_option(param_str): + """ + :param param_str: param_str to check + :return: whether or not this is the start of an option declaration (i.e. starts "-" or "--") + """ + return param_str and param_str[:1] == '-' + + +def is_incomplete_option(all_args, cmd_param): + """ + :param all_args: the full original list of args supplied + :param cmd_param: the current command paramter + :return: whether or not the last option declaration (i.e. starts "-" or "--") is incomplete and + corresponds to this cmd_param. In other words whether this cmd_param option can still accept + values + """ + if not isinstance(cmd_param, Option): + return False + if cmd_param.is_flag: + return False + last_option = None + for index, arg_str in enumerate(reversed([arg for arg in all_args if arg != WORDBREAK])): + if index + 1 > cmd_param.nargs: + break + if start_of_option(arg_str): + last_option = arg_str + + return True if last_option and last_option in cmd_param.opts else False + + +def is_incomplete_argument(current_params, cmd_param): + """ + :param current_params: the current params and values for this argument as already entered + :param cmd_param: the current command parameter + :return: whether or not the last argument is incomplete and corresponds to this cmd_param. In + other words whether or not the this cmd_param argument can still accept values + """ + if not isinstance(cmd_param, Argument): + return False + current_param_values = current_params[cmd_param.name] + if current_param_values is None: + return True + if cmd_param.nargs == -1: + return True + if isinstance(current_param_values, abc.Iterable) \ + and cmd_param.nargs > 1 and len(current_param_values) < cmd_param.nargs: + return True + return False + + +def get_user_autocompletions(ctx, args, incomplete, cmd_param): + """ + :param ctx: context associated with the parsed command + :param args: full list of args + :param incomplete: the incomplete text to autocomplete + :param cmd_param: command definition + :return: all the possible user-specified completions for the param + """ + results = [] + if isinstance(cmd_param.type, Choice): + # Choices don't support descriptions. + results = [(c, None) + for c in cmd_param.type.choices if str(c).startswith(incomplete)] + elif cmd_param.autocompletion is not None: + dynamic_completions = cmd_param.autocompletion(ctx=ctx, + args=args, + incomplete=incomplete) + results = [c if isinstance(c, tuple) else (c, None) + for c in dynamic_completions] + return results + + +def get_visible_commands_starting_with(ctx, starts_with): + """ + :param ctx: context associated with the parsed command + :starts_with: string that visible commands must start with. + :return: all visible (not hidden) commands that start with starts_with. + """ + for c in ctx.command.list_commands(ctx): + if c.startswith(starts_with): + command = ctx.command.get_command(ctx, c) + if not command.hidden: + yield command + + +def add_subcommand_completions(ctx, incomplete, completions_out): + # Add subcommand completions. + if isinstance(ctx.command, MultiCommand): + completions_out.extend( + [(c.name, c.get_short_help_str()) for c in get_visible_commands_starting_with(ctx, incomplete)]) + + # Walk up the context list and add any other completion possibilities from chained commands + while ctx.parent is not None: + ctx = ctx.parent + if isinstance(ctx.command, MultiCommand) and ctx.command.chain: + remaining_commands = [c for c in get_visible_commands_starting_with(ctx, incomplete) + if c.name not in ctx.protected_args] + completions_out.extend([(c.name, c.get_short_help_str()) for c in remaining_commands]) + + +def get_choices(cli, prog_name, args, incomplete): + """ + :param cli: command definition + :param prog_name: the program that is running + :param args: full list of args + :param incomplete: the incomplete text to autocomplete + :return: all the possible completions for the incomplete + """ + all_args = copy.deepcopy(args) + + ctx = resolve_ctx(cli, prog_name, args) + if ctx is None: + return [] + + # In newer versions of bash long opts with '='s are partitioned, but it's easier to parse + # without the '=' + if start_of_option(incomplete) and WORDBREAK in incomplete: + partition_incomplete = incomplete.partition(WORDBREAK) + all_args.append(partition_incomplete[0]) + incomplete = partition_incomplete[2] + elif incomplete == WORDBREAK: + incomplete = '' + + completions = [] + if start_of_option(incomplete): + # completions for partial options + for param in ctx.command.params: + if isinstance(param, Option) and not param.hidden: + param_opts = [param_opt for param_opt in param.opts + + param.secondary_opts if param_opt not in all_args or param.multiple] + completions.extend([(o, param.help) for o in param_opts if o.startswith(incomplete)]) + return completions + # completion for option values from user supplied values + for param in ctx.command.params: + if is_incomplete_option(all_args, param): + return get_user_autocompletions(ctx, all_args, incomplete, param) + # completion for argument values from user supplied values + for param in ctx.command.params: + if is_incomplete_argument(ctx.params, param): + return get_user_autocompletions(ctx, all_args, incomplete, param) + + add_subcommand_completions(ctx, incomplete, completions) + # Sort before returning so that proper ordering can be enforced in custom types. + return sorted(completions) + + +def do_complete(cli, prog_name, include_descriptions): + cwords = split_arg_string(os.environ['COMP_WORDS']) + cword = int(os.environ['COMP_CWORD']) + args = cwords[1:cword] + try: + incomplete = cwords[cword] + except IndexError: + incomplete = '' + + for item in get_choices(cli, prog_name, args, incomplete): + echo(item[0]) + if include_descriptions: + # ZSH has trouble dealing with empty array parameters when returned from commands, so use a well defined character '_' to indicate no description is present. + echo(item[1] if item[1] else '_') + + return True + + +def bashcomplete(cli, prog_name, complete_var, complete_instr): + if complete_instr.startswith('source'): + shell = 'zsh' if complete_instr == 'source_zsh' else 'bash' + echo(get_completion_script(prog_name, complete_var, shell)) + return True + elif complete_instr == 'complete' or complete_instr == 'complete_zsh': + return do_complete(cli, prog_name, complete_instr == 'complete_zsh') + return False diff --git a/venv/Lib/site-packages/click/_compat.py b/venv/Lib/site-packages/click/_compat.py new file mode 100644 index 0000000..937e230 --- /dev/null +++ b/venv/Lib/site-packages/click/_compat.py @@ -0,0 +1,703 @@ +import re +import io +import os +import sys +import codecs +from weakref import WeakKeyDictionary + + +PY2 = sys.version_info[0] == 2 +CYGWIN = sys.platform.startswith('cygwin') +# Determine local App Engine environment, per Google's own suggestion +APP_ENGINE = ('APPENGINE_RUNTIME' in os.environ and + 'Development/' in os.environ['SERVER_SOFTWARE']) +WIN = sys.platform.startswith('win') and not APP_ENGINE +DEFAULT_COLUMNS = 80 + + +_ansi_re = re.compile(r'\033\[((?:\d|;)*)([a-zA-Z])') + + +def get_filesystem_encoding(): + return sys.getfilesystemencoding() or sys.getdefaultencoding() + + +def _make_text_stream(stream, encoding, errors, + force_readable=False, force_writable=False): + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = 'replace' + return _NonClosingTextIOWrapper(stream, encoding, errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable) + + +def is_ascii_encoding(encoding): + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == 'ascii' + except LookupError: + return False + + +def get_best_encoding(stream): + """Returns the default stream encoding if not found.""" + rv = getattr(stream, 'encoding', None) or sys.getdefaultencoding() + if is_ascii_encoding(rv): + return 'utf-8' + return rv + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + + def __init__(self, stream, encoding, errors, + force_readable=False, force_writable=False, **extra): + self._stream = stream = _FixupStream(stream, force_readable, + force_writable) + io.TextIOWrapper.__init__(self, stream, encoding, errors, **extra) + + # The io module is a place where the Python 3 text behavior + # was forced upon Python 2, so we need to unbreak + # it to look like Python 2. + if PY2: + def write(self, x): + if isinstance(x, str) or is_bytes(x): + try: + self.flush() + except Exception: + pass + return self.buffer.write(str(x)) + return io.TextIOWrapper.write(self, x) + + def writelines(self, lines): + for line in lines: + self.write(line) + + def __del__(self): + try: + self.detach() + except Exception: + pass + + def isatty(self): + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream(object): + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__(self, stream, force_readable=False, force_writable=False): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name): + return getattr(self._stream, name) + + def read1(self, size): + f = getattr(self._stream, 'read1', None) + if f is not None: + return f(size) + # We only dispatch to readline instead of read in Python 2 as we + # do not want cause problems with the different implementation + # of line buffering. + if PY2: + return self._stream.readline(size) + return self._stream.read(size) + + def readable(self): + if self._force_readable: + return True + x = getattr(self._stream, 'readable', None) + if x is not None: + return x() + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self): + if self._force_writable: + return True + x = getattr(self._stream, 'writable', None) + if x is not None: + return x() + try: + self._stream.write('') + except Exception: + try: + self._stream.write(b'') + except Exception: + return False + return True + + def seekable(self): + x = getattr(self._stream, 'seekable', None) + if x is not None: + return x() + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +if PY2: + text_type = unicode + bytes = str + raw_input = raw_input + string_types = (str, unicode) + int_types = (int, long) + iteritems = lambda x: x.iteritems() + range_type = xrange + + def is_bytes(x): + return isinstance(x, (buffer, bytearray)) + + _identifier_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$') + + # For Windows, we need to force stdout/stdin/stderr to binary if it's + # fetched for that. This obviously is not the most correct way to do + # it as it changes global state. Unfortunately, there does not seem to + # be a clear better way to do it as just reopening the file in binary + # mode does not change anything. + # + # An option would be to do what Python 3 does and to open the file as + # binary only, patch it back to the system, and then use a wrapper + # stream that converts newlines. It's not quite clear what's the + # correct option here. + # + # This code also lives in _winconsole for the fallback to the console + # emulation stream. + # + # There are also Windows environments where the `msvcrt` module is not + # available (which is why we use try-catch instead of the WIN variable + # here), such as the Google App Engine development server on Windows. In + # those cases there is just nothing we can do. + def set_binary_mode(f): + return f + + try: + import msvcrt + except ImportError: + pass + else: + def set_binary_mode(f): + try: + fileno = f.fileno() + except Exception: + pass + else: + msvcrt.setmode(fileno, os.O_BINARY) + return f + + try: + import fcntl + except ImportError: + pass + else: + def set_binary_mode(f): + try: + fileno = f.fileno() + except Exception: + pass + else: + flags = fcntl.fcntl(fileno, fcntl.F_GETFL) + fcntl.fcntl(fileno, fcntl.F_SETFL, flags & ~os.O_NONBLOCK) + return f + + def isidentifier(x): + return _identifier_re.search(x) is not None + + def get_binary_stdin(): + return set_binary_mode(sys.stdin) + + def get_binary_stdout(): + _wrap_std_stream('stdout') + return set_binary_mode(sys.stdout) + + def get_binary_stderr(): + _wrap_std_stream('stderr') + return set_binary_mode(sys.stderr) + + def get_text_stdin(encoding=None, errors=None): + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _make_text_stream(sys.stdin, encoding, errors, + force_readable=True) + + def get_text_stdout(encoding=None, errors=None): + _wrap_std_stream('stdout') + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _make_text_stream(sys.stdout, encoding, errors, + force_writable=True) + + def get_text_stderr(encoding=None, errors=None): + _wrap_std_stream('stderr') + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _make_text_stream(sys.stderr, encoding, errors, + force_writable=True) + + def filename_to_ui(value): + if isinstance(value, bytes): + value = value.decode(get_filesystem_encoding(), 'replace') + return value +else: + import io + text_type = str + raw_input = input + string_types = (str,) + int_types = (int,) + range_type = range + isidentifier = lambda x: x.isidentifier() + iteritems = lambda x: iter(x.items()) + + def is_bytes(x): + return isinstance(x, (bytes, memoryview, bytearray)) + + def _is_binary_reader(stream, default=False): + try: + return isinstance(stream.read(0), bytes) + except Exception: + return default + # This happens in some cases where the stream was already + # closed. In this case, we assume the default. + + def _is_binary_writer(stream, default=False): + try: + stream.write(b'') + except Exception: + try: + stream.write('') + return False + except Exception: + pass + return default + return True + + def _find_binary_reader(stream): + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return stream + + buf = getattr(stream, 'buffer', None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return buf + + def _find_binary_writer(stream): + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detatching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return stream + + buf = getattr(stream, 'buffer', None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return buf + + def _stream_is_misconfigured(stream): + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, 'encoding', None) or 'ascii') + + def _is_compatible_text_stream(stream, encoding, errors): + stream_encoding = getattr(stream, 'encoding', None) + stream_errors = getattr(stream, 'errors', None) + + # Perfect match. + if stream_encoding == encoding and stream_errors == errors: + return True + + # Otherwise, it's only a compatible stream if we did not ask for + # an encoding. + if encoding is None: + return stream_encoding is not None + + return False + + def _force_correct_text_reader(text_reader, encoding, errors, + force_readable=False): + if _is_binary_reader(text_reader, False): + binary_reader = text_reader + else: + # If there is no target encoding set, we need to verify that the + # reader is not actually misconfigured. + if encoding is None and not _stream_is_misconfigured(text_reader): + return text_reader + + if _is_compatible_text_stream(text_reader, encoding, errors): + return text_reader + + # If the reader has no encoding, we try to find the underlying + # binary reader for it. If that fails because the environment is + # misconfigured, we silently go with the same reader because this + # is too common to happen. In that case, mojibake is better than + # exceptions. + binary_reader = _find_binary_reader(text_reader) + if binary_reader is None: + return text_reader + + # At this point, we default the errors to replace instead of strict + # because nobody handles those errors anyways and at this point + # we're so fundamentally fucked that nothing can repair it. + if errors is None: + errors = 'replace' + return _make_text_stream(binary_reader, encoding, errors, + force_readable=force_readable) + + def _force_correct_text_writer(text_writer, encoding, errors, + force_writable=False): + if _is_binary_writer(text_writer, False): + binary_writer = text_writer + else: + # If there is no target encoding set, we need to verify that the + # writer is not actually misconfigured. + if encoding is None and not _stream_is_misconfigured(text_writer): + return text_writer + + if _is_compatible_text_stream(text_writer, encoding, errors): + return text_writer + + # If the writer has no encoding, we try to find the underlying + # binary writer for it. If that fails because the environment is + # misconfigured, we silently go with the same writer because this + # is too common to happen. In that case, mojibake is better than + # exceptions. + binary_writer = _find_binary_writer(text_writer) + if binary_writer is None: + return text_writer + + # At this point, we default the errors to replace instead of strict + # because nobody handles those errors anyways and at this point + # we're so fundamentally fucked that nothing can repair it. + if errors is None: + errors = 'replace' + return _make_text_stream(binary_writer, encoding, errors, + force_writable=force_writable) + + def get_binary_stdin(): + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError('Was not able to determine binary ' + 'stream for sys.stdin.') + return reader + + def get_binary_stdout(): + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError('Was not able to determine binary ' + 'stream for sys.stdout.') + return writer + + def get_binary_stderr(): + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError('Was not able to determine binary ' + 'stream for sys.stderr.') + return writer + + def get_text_stdin(encoding=None, errors=None): + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, + force_readable=True) + + def get_text_stdout(encoding=None, errors=None): + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, + force_writable=True) + + def get_text_stderr(encoding=None, errors=None): + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, + force_writable=True) + + def filename_to_ui(value): + if isinstance(value, bytes): + value = value.decode(get_filesystem_encoding(), 'replace') + else: + value = value.encode('utf-8', 'surrogateescape') \ + .decode('utf-8', 'replace') + return value + + +def get_streerror(e, default=None): + if hasattr(e, 'strerror'): + msg = e.strerror + else: + if default is not None: + msg = default + else: + msg = str(e) + if isinstance(msg, bytes): + msg = msg.decode('utf-8', 'replace') + return msg + + +def open_stream(filename, mode='r', encoding=None, errors='strict', + atomic=False): + # Standard streams first. These are simple because they don't need + # special handling for the atomic flag. It's entirely ignored. + if filename == '-': + if any(m in mode for m in ['w', 'a', 'x']): + if 'b' in mode: + return get_binary_stdout(), False + return get_text_stdout(encoding=encoding, errors=errors), False + if 'b' in mode: + return get_binary_stdin(), False + return get_text_stdin(encoding=encoding, errors=errors), False + + # Non-atomic writes directly go out through the regular open functions. + if not atomic: + if encoding is None: + return open(filename, mode), True + return io.open(filename, mode, encoding=encoding, errors=errors), True + + # Some usability stuff for atomic writes + if 'a' in mode: + raise ValueError( + 'Appending to an existing file is not supported, because that ' + 'would involve an expensive `copy`-operation to a temporary ' + 'file. Open the file in normal `w`-mode and copy explicitly ' + 'if that\'s what you\'re after.' + ) + if 'x' in mode: + raise ValueError('Use the `overwrite`-parameter instead.') + if 'w' not in mode: + raise ValueError('Atomic writes only make sense with `w`-mode.') + + # Atomic writes are more complicated. They work by opening a file + # as a proxy in the same folder and then using the fdopen + # functionality to wrap it in a Python file. Then we wrap it in an + # atomic file that moves the file over on close. + import tempfile + fd, tmp_filename = tempfile.mkstemp(dir=os.path.dirname(filename), + prefix='.__atomic-write') + + if encoding is not None: + f = io.open(fd, mode, encoding=encoding, errors=errors) + else: + f = os.fdopen(fd, mode) + + return _AtomicFile(f, tmp_filename, os.path.realpath(filename)), True + + +# Used in a destructor call, needs extra protection from interpreter cleanup. +if hasattr(os, 'replace'): + _replace = os.replace + _can_replace = True +else: + _replace = os.rename + _can_replace = not WIN + + +class _AtomicFile(object): + + def __init__(self, f, tmp_filename, real_filename): + self._f = f + self._tmp_filename = tmp_filename + self._real_filename = real_filename + self.closed = False + + @property + def name(self): + return self._real_filename + + def close(self, delete=False): + if self.closed: + return + self._f.close() + if not _can_replace: + try: + os.remove(self._real_filename) + except OSError: + pass + _replace(self._tmp_filename, self._real_filename) + self.closed = True + + def __getattr__(self, name): + return getattr(self._f, name) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + self.close(delete=exc_type is not None) + + def __repr__(self): + return repr(self._f) + + +auto_wrap_for_ansi = None +colorama = None +get_winterm_size = None + + +def strip_ansi(value): + return _ansi_re.sub('', value) + + +def should_strip_ansi(stream=None, color=None): + if color is None: + if stream is None: + stream = sys.stdin + return not isatty(stream) + return not color + + +# If we're on Windows, we provide transparent integration through +# colorama. This will make ANSI colors through the echo function +# work automatically. +if WIN: + # Windows has a smaller terminal + DEFAULT_COLUMNS = 79 + + from ._winconsole import _get_windows_console_stream, _wrap_std_stream + + def _get_argv_encoding(): + import locale + return locale.getpreferredencoding() + + if PY2: + def raw_input(prompt=''): + sys.stderr.flush() + if prompt: + stdout = _default_text_stdout() + stdout.write(prompt) + stdin = _default_text_stdin() + return stdin.readline().rstrip('\r\n') + + try: + import colorama + except ImportError: + pass + else: + _ansi_stream_wrappers = WeakKeyDictionary() + + def auto_wrap_for_ansi(stream, color=None): + """This function wraps a stream so that calls through colorama + are issued to the win32 console API to recolor on demand. It + also ensures to reset the colors if a write call is interrupted + to not destroy the console afterwards. + """ + try: + cached = _ansi_stream_wrappers.get(stream) + except Exception: + cached = None + if cached is not None: + return cached + strip = should_strip_ansi(stream, color) + ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) + rv = ansi_wrapper.stream + _write = rv.write + + def _safe_write(s): + try: + return _write(s) + except: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + return rv + + def get_winterm_size(): + win = colorama.win32.GetConsoleScreenBufferInfo( + colorama.win32.STDOUT).srWindow + return win.Right - win.Left, win.Bottom - win.Top +else: + def _get_argv_encoding(): + return getattr(sys.stdin, 'encoding', None) or get_filesystem_encoding() + + _get_windows_console_stream = lambda *x: None + _wrap_std_stream = lambda *x: None + + +def term_len(x): + return len(strip_ansi(x)) + + +def isatty(stream): + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func(src_func, wrapper_func): + cache = WeakKeyDictionary() + def func(): + stream = src_func() + try: + rv = cache.get(stream) + except Exception: + rv = None + if rv is not None: + return rv + rv = wrapper_func() + try: + stream = src_func() # In case wrapper_func() modified the stream + cache[stream] = rv + except Exception: + pass + return rv + return func + + +_default_text_stdin = _make_cached_stream_func( + lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func( + lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func( + lambda: sys.stderr, get_text_stderr) + + +binary_streams = { + 'stdin': get_binary_stdin, + 'stdout': get_binary_stdout, + 'stderr': get_binary_stderr, +} + +text_streams = { + 'stdin': get_text_stdin, + 'stdout': get_text_stdout, + 'stderr': get_text_stderr, +} diff --git a/venv/Lib/site-packages/click/_termui_impl.py b/venv/Lib/site-packages/click/_termui_impl.py new file mode 100644 index 0000000..00a8e5e --- /dev/null +++ b/venv/Lib/site-packages/click/_termui_impl.py @@ -0,0 +1,621 @@ +# -*- coding: utf-8 -*- +""" +click._termui_impl +~~~~~~~~~~~~~~~~~~ + +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. + +:copyright: © 2014 by the Pallets team. +:license: BSD, see LICENSE.rst for more details. +""" + +import os +import sys +import time +import math +import contextlib +from ._compat import _default_text_stdout, range_type, PY2, isatty, \ + open_stream, strip_ansi, term_len, get_best_encoding, WIN, int_types, \ + CYGWIN +from .utils import echo +from .exceptions import ClickException + + +if os.name == 'nt': + BEFORE_BAR = '\r' + AFTER_BAR = '\n' +else: + BEFORE_BAR = '\r\033[?25l' + AFTER_BAR = '\033[?25h\n' + + +def _length_hint(obj): + """Returns the length hint of an object.""" + try: + return len(obj) + except (AttributeError, TypeError): + try: + get_hint = type(obj).__length_hint__ + except AttributeError: + return None + try: + hint = get_hint(obj) + except TypeError: + return None + if hint is NotImplemented or \ + not isinstance(hint, int_types) or \ + hint < 0: + return None + return hint + + +class ProgressBar(object): + + def __init__(self, iterable, length=None, fill_char='#', empty_char=' ', + bar_template='%(bar)s', info_sep=' ', show_eta=True, + show_percent=None, show_pos=False, item_show_func=None, + label=None, file=None, color=None, width=30): + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label = label or '' + if file is None: + file = _default_text_stdout() + self.file = file + self.color = color + self.width = width + self.autowidth = width == 0 + + if length is None: + length = _length_hint(iterable) + if iterable is None: + if length is None: + raise TypeError('iterable or length is required') + iterable = range_type(length) + self.iter = iter(iterable) + self.length = length + self.length_known = length is not None + self.pos = 0 + self.avg = [] + self.start = self.last_eta = time.time() + self.eta_known = False + self.finished = False + self.max_width = None + self.entered = False + self.current_item = None + self.is_hidden = not isatty(self.file) + self._last_line = None + self.short_limit = 0.5 + + def __enter__(self): + self.entered = True + self.render_progress() + return self + + def __exit__(self, exc_type, exc_value, tb): + self.render_finish() + + def __iter__(self): + if not self.entered: + raise RuntimeError('You need to use progress bars in a with block.') + self.render_progress() + return self.generator() + + def is_fast(self): + return time.time() - self.start <= self.short_limit + + def render_finish(self): + if self.is_hidden or self.is_fast(): + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self): + if self.finished: + return 1.0 + return min(self.pos / (float(self.length) or 1), 1.0) + + @property + def time_per_iteration(self): + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self): + if self.length_known and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self): + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + days = t + return '%dd %02d:%02d:%02d' % (days, hours, minutes, seconds) + else: + return '%02d:%02d:%02d' % (hours, minutes, seconds) + return '' + + def format_pos(self): + pos = str(self.pos) + if self.length_known: + pos += '/%s' % self.length + return pos + + def format_pct(self): + return ('% 4d%%' % int(self.pct * 100))[1:] + + def format_bar(self): + if self.length_known: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + bar = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + bar[int((math.cos(self.pos * self.time_per_iteration) + / 2.0 + 0.5) * self.width)] = self.fill_char + bar = ''.join(bar) + return bar + + def format_progress_line(self): + show_percent = self.show_percent + + info_bits = [] + if self.length_known and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return (self.bar_template % { + 'label': self.label, + 'bar': self.format_bar(), + 'info': self.info_sep.join(info_bits) + }).rstrip() + + def render_progress(self): + from .termui import get_terminal_size + + if self.is_hidden: + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, get_terminal_size()[0] - clutter_length) + if new_width < old_width: + buf.append(BEFORE_BAR) + buf.append(' ' * self.max_width) + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(' ' * (clear_width - line_len)) + line = ''.join(buf) + # Render the line only if it changed. + + if line != self._last_line and not self.is_fast(): + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps): + self.pos += n_steps + if self.length_known and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length_known + + def update(self, n_steps): + self.make_step(n_steps) + self.render_progress() + + def finish(self): + self.eta_known = 0 + self.current_item = None + self.finished = True + + def generator(self): + """ + Returns a generator which yields the items added to the bar during + construction, and updates the progress bar *after* the yielded block + returns. + """ + if not self.entered: + raise RuntimeError('You need to use progress bars in a with block.') + + if self.is_hidden: + for rv in self.iter: + yield rv + else: + for rv in self.iter: + self.current_item = rv + yield rv + self.update(1) + self.finish() + self.render_progress() + + +def pager(generator, color=None): + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + pager_cmd = (os.environ.get('PAGER', None) or '').strip() + if pager_cmd: + if WIN: + return _tempfilepager(generator, pager_cmd, color) + return _pipepager(generator, pager_cmd, color) + if os.environ.get('TERM') in ('dumb', 'emacs'): + return _nullpager(stdout, generator, color) + if WIN or sys.platform.startswith('os2'): + return _tempfilepager(generator, 'more <', color) + if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: + return _pipepager(generator, 'less', color) + + import tempfile + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0: + return _pipepager(generator, 'more', color) + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager(generator, cmd, color): + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + """ + import subprocess + env = dict(os.environ) + + # If we're piping to less we might support colors under the + # condition that + cmd_detail = cmd.rsplit('/', 1)[-1].split() + if color is None and cmd_detail[0] == 'less': + less_flags = os.environ.get('LESS', '') + ' '.join(cmd_detail[1:]) + if not less_flags: + env['LESS'] = '-R' + color = True + elif 'r' in less_flags or 'R' in less_flags: + color = True + + c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, + env=env) + encoding = get_best_encoding(c.stdin) + try: + for text in generator: + if not color: + text = strip_ansi(text) + + c.stdin.write(text.encode(encoding, 'replace')) + except (IOError, KeyboardInterrupt): + pass + else: + c.stdin.close() + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + +def _tempfilepager(generator, cmd, color): + """Page through text by invoking a program on a temporary file.""" + import tempfile + filename = tempfile.mktemp() + # TODO: This never terminates if the passed generator never terminates. + text = "".join(generator) + if not color: + text = strip_ansi(text) + encoding = get_best_encoding(sys.stdout) + with open_stream(filename, 'wb')[0] as f: + f.write(text.encode(encoding)) + try: + os.system(cmd + ' "' + filename + '"') + finally: + os.unlink(filename) + + +def _nullpager(stream, generator, color): + """Simply print unformatted text. This is the ultimate fallback.""" + for text in generator: + if not color: + text = strip_ansi(text) + stream.write(text) + + +class Editor(object): + + def __init__(self, editor=None, env=None, require_save=True, + extension='.txt'): + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self): + if self.editor is not None: + return self.editor + for key in 'VISUAL', 'EDITOR': + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return 'notepad' + for editor in 'vim', 'nano': + if os.system('which %s >/dev/null 2>&1' % editor) == 0: + return editor + return 'vi' + + def edit_file(self, filename): + import subprocess + editor = self.get_editor() + if self.env: + environ = os.environ.copy() + environ.update(self.env) + else: + environ = None + try: + c = subprocess.Popen('%s "%s"' % (editor, filename), + env=environ, shell=True) + exit_code = c.wait() + if exit_code != 0: + raise ClickException('%s: Editing failed!' % editor) + except OSError as e: + raise ClickException('%s: Editing failed: %s' % (editor, e)) + + def edit(self, text): + import tempfile + + text = text or '' + if text and not text.endswith('\n'): + text += '\n' + + fd, name = tempfile.mkstemp(prefix='editor-', suffix=self.extension) + try: + if WIN: + encoding = 'utf-8-sig' + text = text.replace('\n', '\r\n') + else: + encoding = 'utf-8' + text = text.encode(encoding) + + f = os.fdopen(fd, 'wb') + f.write(text) + f.close() + timestamp = os.path.getmtime(name) + + self.edit_file(name) + + if self.require_save \ + and os.path.getmtime(name) == timestamp: + return None + + f = open(name, 'rb') + try: + rv = f.read() + finally: + f.close() + return rv.decode('utf-8-sig').replace('\r\n', '\n') + finally: + os.unlink(name) + + +def open_url(url, wait=False, locate=False): + import subprocess + + def _unquote_file(url): + try: + import urllib + except ImportError: + import urllib + if url.startswith('file://'): + url = urllib.unquote(url[7:]) + return url + + if sys.platform == 'darwin': + args = ['open'] + if wait: + args.append('-W') + if locate: + args.append('-R') + args.append(_unquote_file(url)) + null = open('/dev/null', 'w') + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url) + args = 'explorer /select,"%s"' % _unquote_file( + url.replace('"', '')) + else: + args = 'start %s "" "%s"' % ( + wait and '/WAIT' or '', url.replace('"', '')) + return os.system(args) + elif CYGWIN: + if locate: + url = _unquote_file(url) + args = 'cygstart "%s"' % (os.path.dirname(url).replace('"', '')) + else: + args = 'cygstart %s "%s"' % ( + wait and '-w' or '', url.replace('"', '')) + return os.system(args) + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or '.' + else: + url = _unquote_file(url) + c = subprocess.Popen(['xdg-open', url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(('http://', 'https://')) and not locate and not wait: + import webbrowser + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch): + if ch == u'\x03': + raise KeyboardInterrupt() + if ch == u'\x04' and not WIN: # Unix-like, Ctrl+D + raise EOFError() + if ch == u'\x1a' and WIN: # Windows, Ctrl+Z + raise EOFError() + + +if WIN: + import msvcrt + + @contextlib.contextmanager + def raw_terminal(): + yield + + def getchar(echo): + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + if echo: + func = msvcrt.getwche + else: + func = msvcrt.getwch + + rv = func() + if rv in (u'\x00', u'\xe0'): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + _translate_ch_to_exc(rv) + return rv +else: + import tty + import termios + + @contextlib.contextmanager + def raw_terminal(): + if not isatty(sys.stdin): + f = open('/dev/tty') + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + try: + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo): + with raw_terminal() as fd: + ch = os.read(fd, 32) + ch = ch.decode(get_best_encoding(sys.stdin), 'replace') + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + _translate_ch_to_exc(ch) + return ch diff --git a/venv/Lib/site-packages/click/_textwrap.py b/venv/Lib/site-packages/click/_textwrap.py new file mode 100644 index 0000000..7e77603 --- /dev/null +++ b/venv/Lib/site-packages/click/_textwrap.py @@ -0,0 +1,38 @@ +import textwrap +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + + def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = last[:space_left] + res = last[space_left:] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + @contextmanager + def extra_indent(self, indent): + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text): + rv = [] + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + if idx > 0: + indent = self.subsequent_indent + rv.append(indent + line) + return '\n'.join(rv) diff --git a/venv/Lib/site-packages/click/_unicodefun.py b/venv/Lib/site-packages/click/_unicodefun.py new file mode 100644 index 0000000..620edff --- /dev/null +++ b/venv/Lib/site-packages/click/_unicodefun.py @@ -0,0 +1,125 @@ +import os +import sys +import codecs + +from ._compat import PY2 + + +# If someone wants to vendor click, we want to ensure the +# correct package is discovered. Ideally we could use a +# relative import here but unfortunately Python does not +# support that. +click = sys.modules[__name__.rsplit('.', 1)[0]] + + +def _find_unicode_literals_frame(): + import __future__ + if not hasattr(sys, '_getframe'): # not all Python implementations have it + return 0 + frm = sys._getframe(1) + idx = 1 + while frm is not None: + if frm.f_globals.get('__name__', '').startswith('click.'): + frm = frm.f_back + idx += 1 + elif frm.f_code.co_flags & __future__.unicode_literals.compiler_flag: + return idx + else: + break + return 0 + + +def _check_for_unicode_literals(): + if not __debug__: + return + if not PY2 or click.disable_unicode_literals_warning: + return + bad_frame = _find_unicode_literals_frame() + if bad_frame <= 0: + return + from warnings import warn + warn(Warning('Click detected the use of the unicode_literals ' + '__future__ import. This is heavily discouraged ' + 'because it can introduce subtle bugs in your ' + 'code. You should instead use explicit u"" literals ' + 'for your unicode strings. For more information see ' + 'https://click.palletsprojects.com/python3/'), + stacklevel=bad_frame) + + +def _verify_python3_env(): + """Ensures that the environment is good for unicode on Python 3.""" + if PY2: + return + try: + import locale + fs_enc = codecs.lookup(locale.getpreferredencoding()).name + except Exception: + fs_enc = 'ascii' + if fs_enc != 'ascii': + return + + extra = '' + if os.name == 'posix': + import subprocess + try: + rv = subprocess.Popen(['locale', '-a'], stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate()[0] + except OSError: + rv = b'' + good_locales = set() + has_c_utf8 = False + + # Make sure we're operating on text here. + if isinstance(rv, bytes): + rv = rv.decode('ascii', 'replace') + + for line in rv.splitlines(): + locale = line.strip() + if locale.lower().endswith(('.utf-8', '.utf8')): + good_locales.add(locale) + if locale.lower() in ('c.utf8', 'c.utf-8'): + has_c_utf8 = True + + extra += '\n\n' + if not good_locales: + extra += ( + 'Additional information: on this system no suitable UTF-8\n' + 'locales were discovered. This most likely requires resolving\n' + 'by reconfiguring the locale system.' + ) + elif has_c_utf8: + extra += ( + 'This system supports the C.UTF-8 locale which is recommended.\n' + 'You might be able to resolve your issue by exporting the\n' + 'following environment variables:\n\n' + ' export LC_ALL=C.UTF-8\n' + ' export LANG=C.UTF-8' + ) + else: + extra += ( + 'This system lists a couple of UTF-8 supporting locales that\n' + 'you can pick from. The following suitable locales were\n' + 'discovered: %s' + ) % ', '.join(sorted(good_locales)) + + bad_locale = None + for locale in os.environ.get('LC_ALL'), os.environ.get('LANG'): + if locale and locale.lower().endswith(('.utf-8', '.utf8')): + bad_locale = locale + if locale is not None: + break + if bad_locale is not None: + extra += ( + '\n\nClick discovered that you exported a UTF-8 locale\n' + 'but the locale system could not pick up from it because\n' + 'it does not exist. The exported locale is "%s" but it\n' + 'is not supported' + ) % bad_locale + + raise RuntimeError( + 'Click will abort further execution because Python 3 was' + ' configured to use ASCII as encoding for the environment.' + ' Consult https://click.palletsprojects.com/en/7.x/python3/ for' + ' mitigation steps.' + extra + ) diff --git a/venv/Lib/site-packages/click/_winconsole.py b/venv/Lib/site-packages/click/_winconsole.py new file mode 100644 index 0000000..bbb080d --- /dev/null +++ b/venv/Lib/site-packages/click/_winconsole.py @@ -0,0 +1,307 @@ +# -*- coding: utf-8 -*- +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prmopt. + +import io +import os +import sys +import zlib +import time +import ctypes +import msvcrt +from ._compat import _NonClosingTextIOWrapper, text_type, PY2 +from ctypes import byref, POINTER, c_int, c_char, c_char_p, \ + c_void_p, py_object, c_ssize_t, c_ulong, windll, WINFUNCTYPE +try: + from ctypes import pythonapi + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release +except ImportError: + pythonapi = None +from ctypes.wintypes import LPWSTR, LPCWSTR + + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)( + ('GetCommandLineW', windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE( + POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ('CommandLineToArgvW', windll.shell32)) + + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b'\x1a' +MAX_BYTES_WRITTEN = 32767 + + +class Py_buffer(ctypes.Structure): + _fields_ = [ + ('buf', c_void_p), + ('obj', py_object), + ('len', c_ssize_t), + ('itemsize', c_ssize_t), + ('readonly', c_int), + ('ndim', c_int), + ('format', c_char_p), + ('shape', c_ssize_p), + ('strides', c_ssize_p), + ('suboffsets', c_ssize_p), + ('internal', c_void_p) + ] + + if PY2: + _fields_.insert(-1, ('smalltable', c_ssize_t * 2)) + + +# On PyPy we cannot get buffers so our ability to operate here is +# serverly limited. +if pythonapi is None: + get_buffer = None +else: + def get_buffer(obj, writable=False): + buf = Py_buffer() + flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + try: + buffer_type = c_char * buf.len + return buffer_type.from_address(buf.buf) + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + + def __init__(self, handle): + self.handle = handle + + def isatty(self): + io.RawIOBase.isatty(self) + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + + def readable(self): + return True + + def readinto(self, b): + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError('cannot read odd number of bytes from ' + 'UTF-16-LE encoded console') + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW(self.handle, buffer, code_units_to_be_read, + byref(code_units_read), None) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError('Windows error: %s' % GetLastError()) + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + + def writable(self): + return True + + @staticmethod + def _get_error_message(errno): + if errno == ERROR_SUCCESS: + return 'ERROR_SUCCESS' + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return 'ERROR_NOT_ENOUGH_MEMORY' + return 'Windows error %s' % errno + + def write(self, b): + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, + MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW(self.handle, buf, code_units_to_be_written, + byref(code_units_written), None) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream(object): + + def __init__(self, text_stream, byte_stream): + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self): + return self.buffer.name + + def write(self, x): + if isinstance(x, text_type): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines): + for line in lines: + self.write(line) + + def __getattr__(self, name): + return getattr(self._text_stream, name) + + def isatty(self): + return self.buffer.isatty() + + def __repr__(self): + return '' % ( + self.name, + self.encoding, + ) + + +class WindowsChunkedWriter(object): + """ + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()' which we wrap to write in + limited chunks due to a Windows limitation on binary console streams. + """ + def __init__(self, wrapped): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def write(self, text): + total_to_write = len(text) + written = 0 + + while written < total_to_write: + to_write = min(total_to_write - written, MAX_BYTES_WRITTEN) + self.__wrapped.write(text[written:written+to_write]) + written += to_write + + +_wrapped_std_streams = set() + + +def _wrap_std_stream(name): + # Python 2 & Windows 7 and below + if PY2 and sys.getwindowsversion()[:2] <= (6, 1) and name not in _wrapped_std_streams: + setattr(sys, name, WindowsChunkedWriter(getattr(sys, name))) + _wrapped_std_streams.add(name) + + +def _get_text_stdin(buffer_stream): + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + 'utf-16-le', 'strict', line_buffering=True) + return ConsoleStream(text_stream, buffer_stream) + + +def _get_text_stdout(buffer_stream): + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + 'utf-16-le', 'strict', line_buffering=True) + return ConsoleStream(text_stream, buffer_stream) + + +def _get_text_stderr(buffer_stream): + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + 'utf-16-le', 'strict', line_buffering=True) + return ConsoleStream(text_stream, buffer_stream) + + +if PY2: + def _hash_py_argv(): + return zlib.crc32('\x00'.join(sys.argv[1:])) + + _initial_argv_hash = _hash_py_argv() + + def _get_windows_argv(): + argc = c_int(0) + argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc)) + argv = [argv_unicode[i] for i in range(0, argc.value)] + + if not hasattr(sys, 'frozen'): + argv = argv[1:] + while len(argv) > 0: + arg = argv[0] + if not arg.startswith('-') or arg == '-': + break + argv = argv[1:] + if arg.startswith(('-c', '-m')): + break + + return argv[1:] + + +_stream_factories = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _get_windows_console_stream(f, encoding, errors): + if get_buffer is not None and \ + encoding in ('utf-16-le', None) \ + and errors in ('strict', None) and \ + hasattr(f, 'isatty') and f.isatty(): + func = _stream_factories.get(f.fileno()) + if func is not None: + if not PY2: + f = getattr(f, 'buffer', None) + if f is None: + return None + else: + # If we are on Python 2 we need to set the stream that we + # deal with to binary mode as otherwise the exercise if a + # bit moot. The same problems apply as for + # get_binary_stdin and friends from _compat. + msvcrt.setmode(f.fileno(), os.O_BINARY) + return func(f) diff --git a/venv/Lib/site-packages/click/core.py b/venv/Lib/site-packages/click/core.py new file mode 100644 index 0000000..7a1e342 --- /dev/null +++ b/venv/Lib/site-packages/click/core.py @@ -0,0 +1,1856 @@ +import errno +import inspect +import os +import sys +from contextlib import contextmanager +from itertools import repeat +from functools import update_wrapper + +from .types import convert_type, IntRange, BOOL +from .utils import PacifyFlushWrapper, make_str, make_default_short_help, \ + echo, get_os_args +from .exceptions import ClickException, UsageError, BadParameter, Abort, \ + MissingParameter, Exit +from .termui import prompt, confirm, style +from .formatting import HelpFormatter, join_options +from .parser import OptionParser, split_opt +from .globals import push_context, pop_context + +from ._compat import PY2, isidentifier, iteritems, string_types +from ._unicodefun import _check_for_unicode_literals, _verify_python3_env + + +_missing = object() + + +SUBCOMMAND_METAVAR = 'COMMAND [ARGS]...' +SUBCOMMANDS_METAVAR = 'COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...' + +DEPRECATED_HELP_NOTICE = ' (DEPRECATED)' +DEPRECATED_INVOKE_NOTICE = 'DeprecationWarning: ' + \ + 'The command %(name)s is deprecated.' + + +def _maybe_show_deprecated_notice(cmd): + if cmd.deprecated: + echo(style(DEPRECATED_INVOKE_NOTICE % {'name': cmd.name}, fg='red'), err=True) + + +def fast_exit(code): + """Exit without garbage collection, this speeds up exit by about 10ms for + things like bash completion. + """ + sys.stdout.flush() + sys.stderr.flush() + os._exit(code) + + +def _bashcomplete(cmd, prog_name, complete_var=None): + """Internal handler for the bash completion support.""" + if complete_var is None: + complete_var = '_%s_COMPLETE' % (prog_name.replace('-', '_')).upper() + complete_instr = os.environ.get(complete_var) + if not complete_instr: + return + + from ._bashcomplete import bashcomplete + if bashcomplete(cmd, prog_name, complete_var, complete_instr): + fast_exit(1) + + +def _check_multicommand(base_command, cmd_name, cmd, register=False): + if not base_command.chain or not isinstance(cmd, MultiCommand): + return + if register: + hint = 'It is not possible to add multi commands as children to ' \ + 'another multi command that is in chain mode' + else: + hint = 'Found a multi command as subcommand to a multi command ' \ + 'that is in chain mode. This is not supported' + raise RuntimeError('%s. Command "%s" is set to chain and "%s" was ' + 'added as subcommand but it in itself is a ' + 'multi command. ("%s" is a %s within a chained ' + '%s named "%s").' % ( + hint, base_command.name, cmd_name, + cmd_name, cmd.__class__.__name__, + base_command.__class__.__name__, + base_command.name)) + + +def batch(iterable, batch_size): + return list(zip(*repeat(iter(iterable), batch_size))) + + +def invoke_param_callback(callback, ctx, param, value): + code = getattr(callback, '__code__', None) + args = getattr(code, 'co_argcount', 3) + + if args < 3: + # This will become a warning in Click 3.0: + from warnings import warn + warn(Warning('Invoked legacy parameter callback "%s". The new ' + 'signature for such callbacks starting with ' + 'click 2.0 is (ctx, param, value).' + % callback), stacklevel=3) + return callback(ctx, value) + return callback(ctx, param, value) + + +@contextmanager +def augment_usage_errors(ctx, param=None): + """Context manager that attaches extra information to exceptions that + fly. + """ + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing(invocation_order, declaration_order): + """Given a sequence of parameters in the order as should be considered + for processing and an iterable of parameters that exist, this returns + a list in the correct order as they should be processed. + """ + def sort_key(item): + try: + idx = invocation_order.index(item) + except ValueError: + idx = float('inf') + return (not item.is_eager, idx) + + return sorted(declaration_order, key=sort_key) + + +class Context(object): + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + .. versionadded:: 2.0 + Added the `resilient_parsing`, `help_option_names`, + `token_normalize_func` parameters. + + .. versionadded:: 3.0 + Added the `allow_extra_args` and `allow_interspersed_args` + parameters. + + .. versionadded:: 4.0 + Added the `color`, `ignore_unknown_options`, and + `max_content_width` parameters. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + """ + + def __init__(self, command, parent=None, info_name=None, obj=None, + auto_envvar_prefix=None, default_map=None, + terminal_width=None, max_content_width=None, + resilient_parsing=False, allow_extra_args=None, + allow_interspersed_args=None, + ignore_unknown_options=None, help_option_names=None, + token_normalize_func=None, color=None): + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: the parsed parameters except if the value is hidden in which + #: case it's not remembered. + self.params = {} + #: the leftover arguments. + self.args = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self.protected_args = [] + if obj is None and parent is not None: + obj = parent.obj + #: the user object stored. + self.obj = obj + self._meta = getattr(parent, 'meta', {}) + + #: A dictionary (-like object) with defaults for parameters. + if default_map is None \ + and parent is not None \ + and parent.default_map is not None: + default_map = parent.default_map.get(info_name) + self.default_map = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`resultcallback`. + self.invoked_subcommand = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + #: The width of the terminal (None is autodetection). + self.terminal_width = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ['--help'] + + #: The names for the help options. + self.help_option_names = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func = token_normalize_func + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if parent is not None \ + and parent.auto_envvar_prefix is not None and \ + self.info_name is not None: + auto_envvar_prefix = '%s_%s' % (parent.auto_envvar_prefix, + self.info_name.upper()) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + self.auto_envvar_prefix = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color = color + + self._close_callbacks = [] + self._depth = 0 + + def __enter__(self): + self._depth += 1 + push_context(self) + return self + + def __exit__(self, exc_type, exc_value, tb): + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup=True): + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self): + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = __name__ + '.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self): + """Creates the formatter for the help and usage output.""" + return HelpFormatter(width=self.terminal_width, + max_width=self.max_content_width) + + def call_on_close(self, f): + """This decorator remembers a function as callback that should be + executed when the context tears down. This is most useful to bind + resource handling to the script execution. For instance, file objects + opened by the :class:`File` type will register their close callbacks + here. + + :param f: the function to execute on teardown. + """ + self._close_callbacks.append(f) + return f + + def close(self): + """Invokes all close callbacks.""" + for cb in self._close_callbacks: + cb() + self._close_callbacks = [] + + @property + def command_path(self): + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = '' + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + rv = self.parent.command_path + ' ' + rv + return rv.lstrip() + + def find_root(self): + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type): + """Finds the closest object of a given type.""" + node = self + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + node = node.parent + + def ensure_object(self, object_type): + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + def lookup_default(self, name): + """Looks up the default for a parameter name. This by default + looks into the :attr:`default_map` if available. + """ + if self.default_map is not None: + rv = self.default_map.get(name) + if callable(rv): + rv = rv() + return rv + + def fail(self, message): + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self): + """Aborts the script.""" + raise Abort() + + def exit(self, code=0): + """Exits the application with a given exit code.""" + raise Exit(code) + + def get_usage(self): + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self): + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def invoke(*args, **kwargs): + """Invokes a command callback in exactly the way it expects. There + are two ways to invoke this method: + + 1. the first argument can be a callback and all other arguments and + keyword arguments are forwarded directly to the function. + 2. the first argument is a click command object. In that case all + arguments are forwarded as well but proper click parameters + (options and click arguments) must be keyword arguments and Click + will fill in defaults. + + Note that before Click 3.2 keyword arguments were not properly filled + in against the intention of this code and no context was created. For + more information about this change and why it was done in a bugfix + release see :ref:`upgrade-to-3.2`. + """ + self, callback = args[:2] + ctx = self + + # It's also possible to invoke another command which might or + # might not have a callback. In that case we also fill + # in defaults and make a new context for this command. + if isinstance(callback, Command): + other_cmd = callback + callback = other_cmd.callback + ctx = Context(other_cmd, info_name=other_cmd.name, parent=self) + if callback is None: + raise TypeError('The given command does not have a ' + 'callback that can be invoked.') + + for param in other_cmd.params: + if param.name not in kwargs and param.expose_value: + kwargs[param.name] = param.get_default(ctx) + + args = args[2:] + with augment_usage_errors(self): + with ctx: + return callback(*args, **kwargs) + + def forward(*args, **kwargs): + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + """ + self, cmd = args[:2] + + # It's also possible to invoke another command which might or + # might not have a callback. + if not isinstance(cmd, Command): + raise TypeError('Callback is not a command.') + + for param in self.params: + if param not in kwargs: + kwargs[param] = self.params[param] + + return self.invoke(cmd, **kwargs) + + +class BaseCommand(object): + """The base command implements the minimal API contract of commands. + Most code will never use this as it does not implement a lot of useful + functionality but it can act as the direct subclass of alternative + parsing methods that do not depend on the Click parser. + + For instance, this can be used to bridge Click and other systems like + argparse or docopt. + + Because base commands do not implement a lot of the API that other + parts of Click take for granted, they are not supported for all + operations. For instance, they cannot be used with the decorators + usually and they have no built-in callback system. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + """ + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + def __init__(self, name, context_settings=None): + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + if context_settings is None: + context_settings = {} + #: an optional dictionary with defaults passed to the context. + self.context_settings = context_settings + + def get_usage(self, ctx): + raise NotImplementedError('Base commands cannot get usage') + + def get_help(self, ctx): + raise NotImplementedError('Base commands cannot get help') + + def make_context(self, info_name, args, parent=None, **extra): + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + :param info_name: the info name for this invokation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it it's + the name of the script. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + """ + for key, value in iteritems(self.context_settings): + if key not in extra: + extra[key] = value + ctx = Context(self, info_name=info_name, parent=parent, **extra) + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx, args): + """Given a context and a list of arguments this creates the parser + and parses the arguments, then modifies the context as necessary. + This is automatically invoked by :meth:`make_context`. + """ + raise NotImplementedError('Base commands do not know how to parse ' + 'arguments.') + + def invoke(self, ctx): + """Given a context, this invokes the command. The default + implementation is raising a not implemented error. + """ + raise NotImplementedError('Base commands are not invokable by default') + + def main(self, args=None, prog_name=None, complete_var=None, + standalone_mode=True, **extra): + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + .. versionadded:: 3.0 + Added the `standalone_mode` flag to control the standalone mode. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + """ + # If we are in Python 3, we will verify that the environment is + # sane at this point or reject further execution to avoid a + # broken script. + if not PY2: + _verify_python3_env() + else: + _check_for_unicode_literals() + + if args is None: + args = get_os_args() + else: + args = list(args) + + if prog_name is None: + prog_name = make_str(os.path.basename( + sys.argv and sys.argv[0] or __file__)) + + # Hook for the Bash completion. This only activates if the Bash + # completion is actually enabled, otherwise this is quite a fast + # noop. + _bashcomplete(self, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt): + echo(file=sys.stderr) + raise Abort() + except ClickException as e: + if not standalone_mode: + raise + e.show() + sys.exit(e.exit_code) + except IOError as e: + if e.errno == errno.EPIPE: + sys.stdout = PacifyFlushWrapper(sys.stdout) + sys.stderr = PacifyFlushWrapper(sys.stderr) + sys.exit(1) + else: + raise + except Exit as e: + if standalone_mode: + sys.exit(e.exit_code) + else: + # in non-standalone mode, return the exit code + # note that this is only reached if `self.invoke` above raises + # an Exit explicitly -- thus bypassing the check there which + # would return its result + # the results of non-standalone execution may therefore be + # somewhat ambiguous: if there are codepaths which lead to + # `ctx.exit(1)` and to `return 1`, the caller won't be able to + # tell the difference between the two + return e.exit_code + except Abort: + if not standalone_mode: + raise + echo('Aborted!', file=sys.stderr) + sys.exit(1) + + def __call__(self, *args, **kwargs): + """Alias for :meth:`main`.""" + return self.main(*args, **kwargs) + + +class Command(BaseCommand): + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param hidden: hide this command from help outputs. + + :param deprecated: issues a message indicating that + the command is deprecated. + """ + + def __init__(self, name, context_settings=None, callback=None, + params=None, help=None, epilog=None, short_help=None, + options_metavar='[OPTIONS]', add_help_option=True, + hidden=False, deprecated=False): + BaseCommand.__init__(self, name, context_settings) + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params = params or [] + # if a form feed (page break) is found in the help text, truncate help + # text to the content preceding the first form feed + if help and '\f' in help: + help = help.split('\f', 1)[0] + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self.hidden = hidden + self.deprecated = deprecated + + def get_usage(self, ctx): + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip('\n') + + def get_params(self, ctx): + rv = self.params + help_option = self.get_help_option(ctx) + if help_option is not None: + rv = rv + [help_option] + return rv + + def format_usage(self, ctx, formatter): + """Writes the usage line into the formatter.""" + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, ' '.join(pieces)) + + def collect_usage_pieces(self, ctx): + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + return rv + + def get_help_option_names(self, ctx): + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return all_names + + def get_help_option(self, ctx): + """Returns the help option object.""" + help_options = self.get_help_option_names(ctx) + if not help_options or not self.add_help_option: + return + + def show_help(ctx, param, value): + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + return Option(help_options, is_flag=True, + is_eager=True, expose_value=False, + callback=show_help, + help='Show this message and exit.') + + def make_parser(self, ctx): + """Creates the underlying option parser for this command.""" + parser = OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx): + """Formats the help into a string and returns it. This creates a + formatter and will call into the following formatting methods: + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip('\n') + + def get_short_help_str(self, limit=45): + """Gets short help for the command or makes it by shortening the long help string.""" + return self.short_help or self.help and make_default_short_help(self.help, limit) or '' + + def format_help(self, ctx, formatter): + """Writes the help into the formatter if it exists. + + This calls into the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx, formatter): + """Writes the help text to the formatter if it exists.""" + if self.help: + formatter.write_paragraph() + with formatter.indentation(): + help_text = self.help + if self.deprecated: + help_text += DEPRECATED_HELP_NOTICE + formatter.write_text(help_text) + elif self.deprecated: + formatter.write_paragraph() + with formatter.indentation(): + formatter.write_text(DEPRECATED_HELP_NOTICE) + + def format_options(self, ctx, formatter): + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section('Options'): + formatter.write_dl(opts) + + def format_epilog(self, ctx, formatter): + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + formatter.write_paragraph() + with formatter.indentation(): + formatter.write_text(self.epilog) + + def parse_args(self, ctx, args): + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing( + param_order, self.get_params(ctx)): + value, args = param.handle_parse_result(ctx, opts, args) + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail('Got unexpected extra argument%s (%s)' + % (len(args) != 1 and 's' or '', + ' '.join(map(make_str, args)))) + + ctx.args = args + return args + + def invoke(self, ctx): + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + _maybe_show_deprecated_notice(self) + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + +class MultiCommand(Command): + """A multi command is the basic implementation of a command that + dispatches to subcommands. The most common version is the + :class:`Group`. + + :param invoke_without_command: this controls how the multi command itself + is invoked. By default it's only invoked + if a subcommand is provided. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is enabled by default if + `invoke_without_command` is disabled or disabled + if it's enabled. If enabled this will add + ``--help`` as argument if no arguments are + passed. + :param subcommand_metavar: the string that is used in the documentation + to indicate the subcommand place. + :param chain: if this is set to `True` chaining of multiple subcommands + is enabled. This restricts the form of commands in that + they cannot have optional arguments but it allows + multiple commands to be chained together. + :param result_callback: the result callback to attach to this multi + command. + """ + allow_extra_args = True + allow_interspersed_args = False + + def __init__(self, name=None, invoke_without_command=False, + no_args_is_help=None, subcommand_metavar=None, + chain=False, result_callback=None, **attrs): + Command.__init__(self, name, **attrs) + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + if subcommand_metavar is None: + if chain: + subcommand_metavar = SUBCOMMANDS_METAVAR + else: + subcommand_metavar = SUBCOMMAND_METAVAR + self.subcommand_metavar = subcommand_metavar + self.chain = chain + #: The result callback that is stored. This can be set or + #: overridden with the :func:`resultcallback` decorator. + self.result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError('Multi commands in chain mode cannot ' + 'have optional arguments.') + + def collect_usage_pieces(self, ctx): + rv = Command.collect_usage_pieces(self, ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx, formatter): + Command.format_options(self, ctx, formatter) + self.format_commands(ctx, formatter) + + def resultcallback(self, replace=False): + """Adds a result callback to the chain command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.resultcallback() + def process_result(result, input): + return result + input + + .. versionadded:: 3.0 + + :param replace: if set to `True` an already existing result + callback will be removed. + """ + def decorator(f): + old_callback = self.result_callback + if old_callback is None or replace: + self.result_callback = f + return f + def function(__value, *args, **kwargs): + return f(old_callback(__value, *args, **kwargs), + *args, **kwargs) + self.result_callback = rv = update_wrapper(function, f) + return rv + return decorator + + def format_commands(self, ctx, formatter): + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section('Commands'): + formatter.write_dl(rows) + + def parse_args(self, ctx, args): + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + rest = Command.parse_args(self, ctx, args) + if self.chain: + ctx.protected_args = rest + ctx.args = [] + elif rest: + ctx.protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx): + def _process_result(value): + if self.result_callback is not None: + value = ctx.invoke(self.result_callback, value, + **ctx.params) + return value + + if not ctx.protected_args: + # If we are invoked without command the chain flag controls + # how this happens. If we are not in chain mode, the return + # value here is the return value of the command. + # If however we are in chain mode, the return value is the + # return value of the result processor invoked with an empty + # list (which means that no subcommand actually was executed). + if self.invoke_without_command: + if not self.chain: + return Command.invoke(self, ctx) + with ctx: + Command.invoke(self, ctx) + return _process_result([]) + ctx.fail('Missing command.') + + # Fetch args back out + args = ctx.protected_args + ctx.args + ctx.args = [] + ctx.protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + ctx.invoked_subcommand = cmd_name + Command.invoke(self, ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = args and '*' or None + Command.invoke(self, ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command(self, ctx, args): + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if split_opt(cmd_name)[0]: + self.parse_args(ctx, ctx.args) + ctx.fail('No such command "%s".' % original_cmd_name) + + return cmd_name, cmd, args[1:] + + def get_command(self, ctx, cmd_name): + """Given a context and a command name, this returns a + :class:`Command` object if it exists or returns `None`. + """ + raise NotImplementedError() + + def list_commands(self, ctx): + """Returns a list of subcommand names in the order they should + appear. + """ + return [] + + +class Group(MultiCommand): + """A group allows a command to have subcommands attached. This is the + most common way to implement nesting in Click. + + :param commands: a dictionary of commands. + """ + + def __init__(self, name=None, commands=None, **attrs): + MultiCommand.__init__(self, name, **attrs) + #: the registered subcommands by their exported names. + self.commands = commands or {} + + def add_command(self, cmd, name=None): + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError('Command has no name.') + _check_multicommand(self, name, cmd, register=True) + self.commands[name] = cmd + + def command(self, *args, **kwargs): + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` but + immediately registers the created command with this instance by + calling into :meth:`add_command`. + """ + def decorator(f): + cmd = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + return decorator + + def group(self, *args, **kwargs): + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` but + immediately registers the created command with this instance by + calling into :meth:`add_command`. + """ + def decorator(f): + cmd = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + return decorator + + def get_command(self, ctx, cmd_name): + return self.commands.get(cmd_name) + + def list_commands(self, ctx): + return sorted(self.commands) + + +class CommandCollection(MultiCommand): + """A command collection is a multi command that merges multiple multi + commands together into one. This is a straightforward implementation + that accepts a list of different multi commands as sources and + provides all the commands for each of them. + """ + + def __init__(self, name=None, sources=None, **attrs): + MultiCommand.__init__(self, name, **attrs) + #: The list of registered multi commands. + self.sources = sources or [] + + def add_source(self, multi_cmd): + """Adds a new multi command to the chain dispatcher.""" + self.sources.append(multi_cmd) + + def get_command(self, ctx, cmd_name): + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + if rv is not None: + if self.chain: + _check_multicommand(self, cmd_name, rv) + return rv + + def list_commands(self, ctx): + rv = set() + for source in self.sources: + rv.update(source.list_commands(ctx)) + return sorted(rv) + + +class Parameter(object): + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. In Click 2.0, the old callback format will still work, + but it will raise a warning to give you change to migrate the + code easier. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The later is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: a callback that should be executed after the parameter + was matched. This is called as ``fn(ctx, param, + value)`` and needs to return the value. Before Click + 2.0, the signature was ``(ctx, value)``. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: a string or list of strings that are environment variables + that should be checked. + """ + param_type_name = 'parameter' + + def __init__(self, param_decls=None, type=None, required=False, + default=None, callback=None, nargs=None, metavar=None, + expose_value=True, is_eager=False, envvar=None, + autocompletion=None): + self.name, self.opts, self.secondary_opts = \ + self._parse_decls(param_decls or (), expose_value) + + self.type = convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = False + self.expose_value = expose_value + self.default = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + self.autocompletion = autocompletion + + @property + def human_readable_name(self): + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name + + def make_metavar(self): + if self.metavar is not None: + return self.metavar + metavar = self.type.get_metavar(self) + if metavar is None: + metavar = self.type.name.upper() + if self.nargs != 1: + metavar += '...' + return metavar + + def get_default(self, ctx): + """Given a context variable this calculates the default value.""" + # Otherwise go with the regular default. + if callable(self.default): + rv = self.default() + else: + rv = self.default + return self.type_cast_value(ctx, rv) + + def add_to_parser(self, parser, ctx): + pass + + def consume_value(self, ctx, opts): + value = opts.get(self.name) + if value is None: + value = self.value_from_envvar(ctx) + if value is None: + value = ctx.lookup_default(self.name) + return value + + def type_cast_value(self, ctx, value): + """Given a value this runs it properly through the type system. + This automatically handles things like `nargs` and `multiple` as + well as composite types. + """ + if self.type.is_composite: + if self.nargs <= 1: + raise TypeError('Attempted to invoke composite type ' + 'but nargs has been set to %s. This is ' + 'not supported; nargs needs to be set to ' + 'a fixed value > 1.' % self.nargs) + if self.multiple: + return tuple(self.type(x or (), self, ctx) for x in value or ()) + return self.type(value or (), self, ctx) + + def _convert(value, level): + if level == 0: + return self.type(value, self, ctx) + return tuple(_convert(x, level - 1) for x in value or ()) + return _convert(value, (self.nargs != 1) + bool(self.multiple)) + + def process_value(self, ctx, value): + """Given a value and context this runs the logic to convert the + value as necessary. + """ + # If the value we were given is None we do nothing. This way + # code that calls this can easily figure out if something was + # not provided. Otherwise it would be converted into an empty + # tuple for multiple invocations which is inconvenient. + if value is not None: + return self.type_cast_value(ctx, value) + + def value_is_missing(self, value): + if value is None: + return True + if (self.nargs != 1 or self.multiple) and value == (): + return True + return False + + def full_process_value(self, ctx, value): + value = self.process_value(ctx, value) + + if value is None and not ctx.resilient_parsing: + value = self.get_default(ctx) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + return value + + def resolve_envvar_value(self, ctx): + if self.envvar is None: + return + if isinstance(self.envvar, (tuple, list)): + for envvar in self.envvar: + rv = os.environ.get(envvar) + if rv is not None: + return rv + else: + return os.environ.get(self.envvar) + + def value_from_envvar(self, ctx): + rv = self.resolve_envvar_value(ctx) + if rv is not None and self.nargs != 1: + rv = self.type.split_envvar_value(rv) + return rv + + def handle_parse_result(self, ctx, opts, args): + with augment_usage_errors(ctx, param=self): + value = self.consume_value(ctx, opts) + try: + value = self.full_process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + value = None + if self.callback is not None: + try: + value = invoke_param_callback( + self.callback, ctx, self, value) + except Exception: + if not ctx.resilient_parsing: + raise + + if self.expose_value: + ctx.params[self.name] = value + return value, args + + def get_help_record(self, ctx): + pass + + def get_usage_pieces(self, ctx): + return [] + + def get_error_hint(self, ctx): + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + """ + hint_list = self.opts or [self.human_readable_name] + return ' / '.join('"%s"' % x for x in hint_list) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: controls if the default value should be shown on the + help page. Normally, defaults are not shown. If this + value is a string, it shows the string instead of the + value. This is particularly useful for dynamic options. + :param show_envvar: controls if an environment variable should be shown on + the help page. Normally, environment variables + are not shown. + :param prompt: if set to `True` or a non empty string then the user will be + prompted for input. If set to `True` the prompt will be the + option name capitalized. + :param confirmation_prompt: if set then the value will need to be confirmed + if it was prompted for. + :param hide_input: if this is `True` then the input on the prompt will be + hidden from the user. This is useful for password + input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + """ + param_type_name = 'option' + + def __init__(self, param_decls=None, show_default=False, + prompt=False, confirmation_prompt=False, + hide_input=False, is_flag=None, flag_value=None, + multiple=False, count=False, allow_from_autoenv=True, + type=None, help=None, hidden=False, show_choices=True, + show_envvar=False, **attrs): + default_is_missing = attrs.get('default', _missing) is _missing + Parameter.__init__(self, param_decls, type=type, **attrs) + + if prompt is True: + prompt_text = self.name.replace('_', ' ').capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.hide_input = hide_input + self.hidden = hidden + + # Flags + if is_flag is None: + if flag_value is not None: + is_flag = True + else: + is_flag = bool(self.secondary_opts) + if is_flag and default_is_missing: + self.default = False + if flag_value is None: + flag_value = not self.default + self.is_flag = is_flag + self.flag_value = flag_value + if self.is_flag and isinstance(self.flag_value, bool) \ + and type is None: + self.type = BOOL + self.is_bool_flag = True + else: + self.is_bool_flag = False + + # Counting + self.count = count + if count: + if type is None: + self.type = IntRange(min=0) + if default_is_missing: + self.default = 0 + + self.multiple = multiple + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + # Sanity check for stuff we don't support + if __debug__: + if self.nargs < 0: + raise TypeError('Options cannot have nargs < 0') + if self.prompt and self.is_flag and not self.is_bool_flag: + raise TypeError('Cannot prompt for flags that are not bools.') + if not self.is_bool_flag and self.secondary_opts: + raise TypeError('Got secondary option for non boolean flag.') + if self.is_bool_flag and self.hide_input \ + and self.prompt is not None: + raise TypeError('Hidden input does not work with boolean ' + 'flag prompts.') + if self.count: + if self.multiple: + raise TypeError('Options cannot be multiple and count ' + 'at the same time.') + elif self.is_flag: + raise TypeError('Options cannot be count and flags at ' + 'the same time.') + + def _parse_decls(self, decls, expose_value): + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if isidentifier(decl): + if name is not None: + raise TypeError('Name defined twice') + name = decl + else: + split_char = decl[:1] == '/' and ';' or '/' + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + else: + possible_names.append(split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace('-', '_').lower() + if not isidentifier(name): + name = None + + if name is None: + if not expose_value: + return None, opts, secondary_opts + raise TypeError('Could not determine name for option') + + if not opts and not secondary_opts: + raise TypeError('No options defined but a name was passed (%s). ' + 'Did you mean to declare an argument instead ' + 'of an option?' % name) + + return name, opts, secondary_opts + + def add_to_parser(self, parser, ctx): + kwargs = { + 'dest': self.name, + 'nargs': self.nargs, + 'obj': self, + } + + if self.multiple: + action = 'append' + elif self.count: + action = 'count' + else: + action = 'store' + + if self.is_flag: + kwargs.pop('nargs', None) + if self.is_bool_flag and self.secondary_opts: + parser.add_option(self.opts, action=action + '_const', + const=True, **kwargs) + parser.add_option(self.secondary_opts, action=action + + '_const', const=False, **kwargs) + else: + parser.add_option(self.opts, action=action + '_const', + const=self.flag_value, + **kwargs) + else: + kwargs['action'] = action + parser.add_option(self.opts, **kwargs) + + def get_help_record(self, ctx): + if self.hidden: + return + any_prefix_is_slash = [] + + def _write_opts(opts): + rv, any_slashes = join_options(opts) + if any_slashes: + any_prefix_is_slash[:] = [True] + if not self.is_flag and not self.count: + rv += ' ' + self.make_metavar() + return rv + + rv = [_write_opts(self.opts)] + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or '' + extra = [] + if self.show_envvar: + envvar = self.envvar + if envvar is None: + if self.allow_from_autoenv and \ + ctx.auto_envvar_prefix is not None: + envvar = '%s_%s' % (ctx.auto_envvar_prefix, self.name.upper()) + if envvar is not None: + extra.append('env var: %s' % ( + ', '.join('%s' % d for d in envvar) + if isinstance(envvar, (list, tuple)) + else envvar, )) + if self.default is not None and self.show_default: + if isinstance(self.show_default, string_types): + default_string = '({})'.format(self.show_default) + elif isinstance(self.default, (list, tuple)): + default_string = ', '.join('%s' % d for d in self.default) + elif inspect.isfunction(self.default): + default_string = "(dynamic)" + else: + default_string = self.default + extra.append('default: {}'.format(default_string)) + + if self.required: + extra.append('required') + if extra: + help = '%s[%s]' % (help and help + ' ' or '', '; '.join(extra)) + + return ((any_prefix_is_slash and '; ' or ' / ').join(rv), help) + + def get_default(self, ctx): + # If we're a non boolean flag out default is more complex because + # we need to look at all flags in the same group to figure out + # if we're the the default one in which case we return the flag + # value as default. + if self.is_flag and not self.is_bool_flag: + for param in ctx.command.params: + if param.name == self.name and param.default: + return param.flag_value + return None + return Parameter.get_default(self, ctx) + + def prompt_for_value(self, ctx): + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + # Calculate the default before prompting anything to be stable. + default = self.get_default(ctx) + + # If this is a prompt for a flag we need to handle this + # differently. + if self.is_bool_flag: + return confirm(self.prompt, default) + + return prompt(self.prompt, default=default, type=self.type, + hide_input=self.hide_input, show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x)) + + def resolve_envvar_value(self, ctx): + rv = Parameter.resolve_envvar_value(self, ctx) + if rv is not None: + return rv + if self.allow_from_autoenv and \ + ctx.auto_envvar_prefix is not None: + envvar = '%s_%s' % (ctx.auto_envvar_prefix, self.name.upper()) + return os.environ.get(envvar) + + def value_from_envvar(self, ctx): + rv = self.resolve_envvar_value(ctx) + if rv is None: + return None + value_depth = (self.nargs != 1) + bool(self.multiple) + if value_depth > 0 and rv is not None: + rv = self.type.split_envvar_value(rv) + if self.multiple and self.nargs != 1: + rv = batch(rv, self.nargs) + return rv + + def full_process_value(self, ctx, value): + if value is None and self.prompt is not None \ + and not ctx.resilient_parsing: + return self.prompt_for_value(ctx) + return Parameter.full_process_value(self, ctx, value) + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the parameter constructor. + """ + param_type_name = 'argument' + + def __init__(self, param_decls, required=None, **attrs): + if required is None: + if attrs.get('default') is not None: + required = False + else: + required = attrs.get('nargs', 1) > 0 + Parameter.__init__(self, param_decls, required=required, **attrs) + if self.default is not None and self.nargs < 0: + raise TypeError('nargs=-1 in combination with a default value ' + 'is not supported.') + + @property + def human_readable_name(self): + if self.metavar is not None: + return self.metavar + return self.name.upper() + + def make_metavar(self): + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(self) + if not var: + var = self.name.upper() + if not self.required: + var = '[%s]' % var + if self.nargs != 1: + var += '...' + return var + + def _parse_decls(self, decls, expose_value): + if not decls: + if not expose_value: + return None, [], [] + raise TypeError('Could not determine name for argument') + if len(decls) == 1: + name = arg = decls[0] + name = name.replace('-', '_').lower() + else: + raise TypeError('Arguments take exactly one ' + 'parameter declaration, got %d' % len(decls)) + return name, [arg], [] + + def get_usage_pieces(self, ctx): + return [self.make_metavar()] + + def get_error_hint(self, ctx): + return '"%s"' % self.make_metavar() + + def add_to_parser(self, parser, ctx): + parser.add_argument(dest=self.name, nargs=self.nargs, + obj=self) + + +# Circular dependency between decorators and core +from .decorators import command, group diff --git a/venv/Lib/site-packages/click/decorators.py b/venv/Lib/site-packages/click/decorators.py new file mode 100644 index 0000000..c57c530 --- /dev/null +++ b/venv/Lib/site-packages/click/decorators.py @@ -0,0 +1,311 @@ +import sys +import inspect + +from functools import update_wrapper + +from ._compat import iteritems +from ._unicodefun import _check_for_unicode_literals +from .utils import echo +from .globals import get_current_context + + +def pass_context(f): + """Marks a callback as wanting to receive the current context + object as first argument. + """ + def new_func(*args, **kwargs): + return f(get_current_context(), *args, **kwargs) + return update_wrapper(new_func, f) + + +def pass_obj(f): + """Similar to :func:`pass_context`, but only pass the object on the + context onwards (:attr:`Context.obj`). This is useful if that object + represents the state of a nested system. + """ + def new_func(*args, **kwargs): + return f(get_current_context().obj, *args, **kwargs) + return update_wrapper(new_func, f) + + +def make_pass_decorator(object_type, ensure=False): + """Given an object type this creates a decorator that will work + similar to :func:`pass_obj` but instead of passing the object of the + current context, it will find the innermost context of type + :func:`object_type`. + + This generates a decorator that works roughly like this:: + + from functools import update_wrapper + + def decorator(f): + @pass_context + def new_func(ctx, *args, **kwargs): + obj = ctx.find_object(object_type) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + :param object_type: the type of the object to pass. + :param ensure: if set to `True`, a new object will be created and + remembered on the context if it's not there yet. + """ + def decorator(f): + def new_func(*args, **kwargs): + ctx = get_current_context() + if ensure: + obj = ctx.ensure_object(object_type) + else: + obj = ctx.find_object(object_type) + if obj is None: + raise RuntimeError('Managed to invoke callback without a ' + 'context object of type %r existing' + % object_type.__name__) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + +def _make_command(f, name, attrs, cls): + if isinstance(f, Command): + raise TypeError('Attempted to convert a callback into a ' + 'command twice.') + try: + params = f.__click_params__ + params.reverse() + del f.__click_params__ + except AttributeError: + params = [] + help = attrs.get('help') + if help is None: + help = inspect.getdoc(f) + if isinstance(help, bytes): + help = help.decode('utf-8') + else: + help = inspect.cleandoc(help) + attrs['help'] = help + _check_for_unicode_literals() + return cls(name=name or f.__name__.lower().replace('_', '-'), + callback=f, params=params, **attrs) + + +def command(name=None, cls=None, **attrs): + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function. If you + want to change that, you can pass the intended name as the first + argument. + + All keyword arguments are forwarded to the underlying command class. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: the name of the command. This defaults to the function + name with underscores replaced by dashes. + :param cls: the command class to instantiate. This defaults to + :class:`Command`. + """ + if cls is None: + cls = Command + def decorator(f): + cmd = _make_command(f, name, attrs, cls) + cmd.__doc__ = f.__doc__ + return cmd + return decorator + + +def group(name=None, **attrs): + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + """ + attrs.setdefault('cls', Group) + return command(name, **attrs) + + +def _param_memo(f, param): + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, '__click_params__'): + f.__click_params__ = [] + f.__click_params__.append(param) + + +def argument(*param_decls, **attrs): + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + """ + def decorator(f): + ArgumentClass = attrs.pop('cls', Argument) + _param_memo(f, ArgumentClass(param_decls, **attrs)) + return f + return decorator + + +def option(*param_decls, **attrs): + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + """ + def decorator(f): + # Issue 926, copy attrs, so pre-defined options can re-use the same cls= + option_attrs = attrs.copy() + + if 'help' in option_attrs: + option_attrs['help'] = inspect.cleandoc(option_attrs['help']) + OptionClass = option_attrs.pop('cls', Option) + _param_memo(f, OptionClass(param_decls, **option_attrs)) + return f + return decorator + + +def confirmation_option(*param_decls, **attrs): + """Shortcut for confirmation prompts that can be ignored by passing + ``--yes`` as parameter. + + This is equivalent to decorating a function with :func:`option` with + the following parameters:: + + def callback(ctx, param, value): + if not value: + ctx.abort() + + @click.command() + @click.option('--yes', is_flag=True, callback=callback, + expose_value=False, prompt='Do you want to continue?') + def dropdb(): + pass + """ + def decorator(f): + def callback(ctx, param, value): + if not value: + ctx.abort() + attrs.setdefault('is_flag', True) + attrs.setdefault('callback', callback) + attrs.setdefault('expose_value', False) + attrs.setdefault('prompt', 'Do you want to continue?') + attrs.setdefault('help', 'Confirm the action without prompting.') + return option(*(param_decls or ('--yes',)), **attrs)(f) + return decorator + + +def password_option(*param_decls, **attrs): + """Shortcut for password prompts. + + This is equivalent to decorating a function with :func:`option` with + the following parameters:: + + @click.command() + @click.option('--password', prompt=True, confirmation_prompt=True, + hide_input=True) + def changeadmin(password): + pass + """ + def decorator(f): + attrs.setdefault('prompt', True) + attrs.setdefault('confirmation_prompt', True) + attrs.setdefault('hide_input', True) + return option(*(param_decls or ('--password',)), **attrs)(f) + return decorator + + +def version_option(version=None, *param_decls, **attrs): + """Adds a ``--version`` option which immediately ends the program + printing out the version number. This is implemented as an eager + option that prints the version and exits the program in the callback. + + :param version: the version number to show. If not provided Click + attempts an auto discovery via setuptools. + :param prog_name: the name of the program (defaults to autodetection) + :param message: custom message to show instead of the default + (``'%(prog)s, version %(version)s'``) + :param others: everything else is forwarded to :func:`option`. + """ + if version is None: + if hasattr(sys, '_getframe'): + module = sys._getframe(1).f_globals.get('__name__') + else: + module = '' + + def decorator(f): + prog_name = attrs.pop('prog_name', None) + message = attrs.pop('message', '%(prog)s, version %(version)s') + + def callback(ctx, param, value): + if not value or ctx.resilient_parsing: + return + prog = prog_name + if prog is None: + prog = ctx.find_root().info_name + ver = version + if ver is None: + try: + import pkg_resources + except ImportError: + pass + else: + for dist in pkg_resources.working_set: + scripts = dist.get_entry_map().get('console_scripts') or {} + for script_name, entry_point in iteritems(scripts): + if entry_point.module_name == module: + ver = dist.version + break + if ver is None: + raise RuntimeError('Could not determine version') + echo(message % { + 'prog': prog, + 'version': ver, + }, color=ctx.color) + ctx.exit() + + attrs.setdefault('is_flag', True) + attrs.setdefault('expose_value', False) + attrs.setdefault('is_eager', True) + attrs.setdefault('help', 'Show the version and exit.') + attrs['callback'] = callback + return option(*(param_decls or ('--version',)), **attrs)(f) + return decorator + + +def help_option(*param_decls, **attrs): + """Adds a ``--help`` option which immediately ends the program + printing out the help page. This is usually unnecessary to add as + this is added by default to all commands unless suppressed. + + Like :func:`version_option`, this is implemented as eager option that + prints in the callback and exits. + + All arguments are forwarded to :func:`option`. + """ + def decorator(f): + def callback(ctx, param, value): + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + attrs.setdefault('is_flag', True) + attrs.setdefault('expose_value', False) + attrs.setdefault('help', 'Show this message and exit.') + attrs.setdefault('is_eager', True) + attrs['callback'] = callback + return option(*(param_decls or ('--help',)), **attrs)(f) + return decorator + + +# Circular dependencies between core and decorators +from .core import Command, Group, Argument, Option diff --git a/venv/Lib/site-packages/click/exceptions.py b/venv/Lib/site-packages/click/exceptions.py new file mode 100644 index 0000000..6fa1765 --- /dev/null +++ b/venv/Lib/site-packages/click/exceptions.py @@ -0,0 +1,235 @@ +from ._compat import PY2, filename_to_ui, get_text_stderr +from .utils import echo + + +def _join_param_hints(param_hint): + if isinstance(param_hint, (tuple, list)): + return ' / '.join('"%s"' % x for x in param_hint) + return param_hint + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception + exit_code = 1 + + def __init__(self, message): + ctor_msg = message + if PY2: + if ctor_msg is not None: + ctor_msg = ctor_msg.encode('utf-8') + Exception.__init__(self, ctor_msg) + self.message = message + + def format_message(self): + return self.message + + def __str__(self): + return self.message + + if PY2: + __unicode__ = __str__ + + def __str__(self): + return self.message.encode('utf-8') + + def show(self, file=None): + if file is None: + file = get_text_stderr() + echo('Error: %s' % self.format_message(), file=file) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + exit_code = 2 + + def __init__(self, message, ctx=None): + ClickException.__init__(self, message) + self.ctx = ctx + self.cmd = self.ctx and self.ctx.command or None + + def show(self, file=None): + if file is None: + file = get_text_stderr() + color = None + hint = '' + if (self.cmd is not None and + self.cmd.get_help_option(self.ctx) is not None): + hint = ('Try "%s %s" for help.\n' + % (self.ctx.command_path, self.ctx.help_option_names[0])) + if self.ctx is not None: + color = self.ctx.color + echo(self.ctx.get_usage() + '\n%s' % hint, file=file, color=color) + echo('Error: %s' % self.format_message(), file=file, color=color) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + def __init__(self, message, ctx=None, param=None, + param_hint=None): + UsageError.__init__(self, message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self): + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) + else: + return 'Invalid value: %s' % self.message + param_hint = _join_param_hints(param_hint) + + return 'Invalid value for %s: %s' % (param_hint, self.message) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + def __init__(self, message=None, ctx=None, param=None, + param_hint=None, param_type=None): + BadParameter.__init__(self, message, ctx, param, param_hint) + self.param_type = param_type + + def format_message(self): + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) + else: + param_hint = None + param_hint = _join_param_hints(param_hint) + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message(self.param) + if msg_extra: + if msg: + msg += '. ' + msg_extra + else: + msg = msg_extra + + return 'Missing %s%s%s%s' % ( + param_type, + param_hint and ' %s' % param_hint or '', + msg and '. ' or '.', + msg or '', + ) + + +class NoSuchOption(UsageError): + """Raised if click attempted to handle an option that does not + exist. + + .. versionadded:: 4.0 + """ + + def __init__(self, option_name, message=None, possibilities=None, + ctx=None): + if message is None: + message = 'no such option: %s' % option_name + UsageError.__init__(self, message, ctx) + self.option_name = option_name + self.possibilities = possibilities + + def format_message(self): + bits = [self.message] + if self.possibilities: + if len(self.possibilities) == 1: + bits.append('Did you mean %s?' % self.possibilities[0]) + else: + possibilities = sorted(self.possibilities) + bits.append('(Possible options: %s)' % ', '.join(possibilities)) + return ' '.join(bits) + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + def __init__(self, option_name, message, ctx=None): + UsageError.__init__(self, message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + def __init__(self, message, ctx=None): + UsageError.__init__(self, message, ctx) + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename, hint=None): + ui_filename = filename_to_ui(filename) + if hint is None: + hint = 'unknown error' + ClickException.__init__(self, hint) + self.ui_filename = ui_filename + self.filename = filename + + def format_message(self): + return 'Could not open file %s: %s' % (self.ui_filename, self.message) + + +class Abort(RuntimeError): + """An internal signalling exception that signals Click to abort.""" + + +class Exit(RuntimeError): + """An exception that indicates that the application should exit with some + status code. + + :param code: the status code to exit with. + """ + def __init__(self, code=0): + self.exit_code = code diff --git a/venv/Lib/site-packages/click/formatting.py b/venv/Lib/site-packages/click/formatting.py new file mode 100644 index 0000000..a3d6a4d --- /dev/null +++ b/venv/Lib/site-packages/click/formatting.py @@ -0,0 +1,256 @@ +from contextlib import contextmanager +from .termui import get_terminal_size +from .parser import split_opt +from ._compat import term_len + + +# Can force a width. This is used by the test system +FORCED_WIDTH = None + + +def measure_table(rows): + widths = {} + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows(rows, col_count): + for row in rows: + row = tuple(row) + yield row + ('',) * (col_count - len(row)) + + +def wrap_text(text, width=78, initial_indent='', subsequent_indent='', + preserve_paragraphs=False): + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + """ + from ._textwrap import TextWrapper + text = text.expandtabs() + wrapper = TextWrapper(width, initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False) + if not preserve_paragraphs: + return wrapper.fill(text) + + p = [] + buf = [] + indent = None + + def _flush_par(): + if not buf: + return + if buf[0].strip() == '\b': + p.append((indent or 0, True, '\n'.join(buf[1:]))) + else: + p.append((indent or 0, False, ' '.join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(' ' * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return '\n\n'.join(rv) + + +class HelpFormatter(object): + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + def __init__(self, indent_increment=2, width=None, max_width=None): + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + width = FORCED_WIDTH + if width is None: + width = max(min(get_terminal_size()[0], max_width) - 2, 50) + self.width = width + self.current_indent = 0 + self.buffer = [] + + def write(self, string): + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self): + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self): + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage(self, prog, args='', prefix='Usage: '): + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: the prefix for the first line. + """ + usage_prefix = '%*s%s ' % (self.current_indent, prefix, prog) + text_width = self.width - self.current_indent + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = ' ' * term_len(usage_prefix) + self.write(wrap_text(args, text_width, + initial_indent=usage_prefix, + subsequent_indent=indent)) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write('\n') + indent = ' ' * (max(self.current_indent, term_len(prefix)) + 4) + self.write(wrap_text(args, text_width, + initial_indent=indent, + subsequent_indent=indent)) + + self.write('\n') + + def write_heading(self, heading): + """Writes a heading into the buffer.""" + self.write('%*s%s:\n' % (self.current_indent, '', heading)) + + def write_paragraph(self): + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write('\n') + + def write_text(self, text): + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + text_width = max(self.width - self.current_indent, 11) + indent = ' ' * self.current_indent + self.write(wrap_text(text, text_width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True)) + self.write('\n') + + def write_dl(self, rows, col_max=30, col_spacing=2): + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError('Expected two columns for definition list') + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write('%*s%s' % (self.current_indent, '', first)) + if not second: + self.write('\n') + continue + if term_len(first) <= first_col - col_spacing: + self.write(' ' * (first_col - term_len(first))) + else: + self.write('\n') + self.write(' ' * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + lines = iter(wrap_text(second, text_width).splitlines()) + if lines: + self.write(next(lines) + '\n') + for line in lines: + self.write('%*s%s\n' % ( + first_col + self.current_indent, '', line)) + else: + self.write('\n') + + @contextmanager + def section(self, name): + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self): + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self): + """Returns the buffer contents.""" + return ''.join(self.buffer) + + +def join_options(options): + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + for opt in options: + prefix = split_opt(opt)[0] + if prefix == '/': + any_prefix_is_slash = True + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + + rv = ', '.join(x[1] for x in rv) + return rv, any_prefix_is_slash diff --git a/venv/Lib/site-packages/click/globals.py b/venv/Lib/site-packages/click/globals.py new file mode 100644 index 0000000..843b594 --- /dev/null +++ b/venv/Lib/site-packages/click/globals.py @@ -0,0 +1,48 @@ +from threading import local + + +_local = local() + + +def get_current_context(silent=False): + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: is set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return getattr(_local, 'stack')[-1] + except (AttributeError, IndexError): + if not silent: + raise RuntimeError('There is no active click context.') + + +def push_context(ctx): + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault('stack', []).append(ctx) + + +def pop_context(): + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color=None): + """"Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + ctx = get_current_context(silent=True) + if ctx is not None: + return ctx.color diff --git a/venv/Lib/site-packages/click/parser.py b/venv/Lib/site-packages/click/parser.py new file mode 100644 index 0000000..1c3ae9c --- /dev/null +++ b/venv/Lib/site-packages/click/parser.py @@ -0,0 +1,427 @@ +# -*- coding: utf-8 -*- +""" +click.parser +~~~~~~~~~~~~ + +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. +""" + +import re +from collections import deque +from .exceptions import UsageError, NoSuchOption, BadOptionUsage, \ + BadArgumentUsage + + +def _unpack_args(args, nargs_spec): + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with `None`. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv = [] + spos = None + + def _fetch(c): + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return None + + while nargs_spec: + nargs = _fetch(nargs_spec) + if nargs == 1: + rv.append(_fetch(args)) + elif nargs > 1: + x = [_fetch(args) for _ in range(nargs)] + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError('Cannot have two nargs < 0') + spos = len(rv) + rv.append(None) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1:] = reversed(rv[spos + 1:]) + + return tuple(rv), list(args) + + +def _error_opt_args(nargs, opt): + if nargs == 1: + raise BadOptionUsage(opt, '%s option requires an argument' % opt) + raise BadOptionUsage(opt, '%s option requires %d arguments' % (opt, nargs)) + + +def split_opt(opt): + first = opt[:1] + if first.isalnum(): + return '', opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def normalize_opt(opt, ctx): + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = split_opt(opt) + return prefix + ctx.token_normalize_func(opt) + + +def split_arg_string(string): + """Given an argument string this attempts to split it into small parts.""" + rv = [] + for match in re.finditer(r"('([^'\\]*(?:\\.[^'\\]*)*)'" + r'|"([^"\\]*(?:\\.[^"\\]*)*)"' + r'|\S+)\s*', string, re.S): + arg = match.group().strip() + if arg[:1] == arg[-1:] and arg[:1] in '"\'': + arg = arg[1:-1].encode('ascii', 'backslashreplace') \ + .decode('unicode-escape') + try: + arg = type(string)(arg) + except UnicodeError: + pass + rv.append(arg) + return rv + + +class Option(object): + + def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None): + self._short_opts = [] + self._long_opts = [] + self.prefixes = set() + + for opt in opts: + prefix, value = split_opt(opt) + if not prefix: + raise ValueError('Invalid start character for option (%s)' + % opt) + self.prefixes.add(prefix[0]) + if len(prefix) == 1 and len(value) == 1: + self._short_opts.append(opt) + else: + self._long_opts.append(opt) + self.prefixes.add(prefix) + + if action is None: + action = 'store' + + self.dest = dest + self.action = action + self.nargs = nargs + self.const = const + self.obj = obj + + @property + def takes_value(self): + return self.action in ('store', 'append') + + def process(self, value, state): + if self.action == 'store': + state.opts[self.dest] = value + elif self.action == 'store_const': + state.opts[self.dest] = self.const + elif self.action == 'append': + state.opts.setdefault(self.dest, []).append(value) + elif self.action == 'append_const': + state.opts.setdefault(self.dest, []).append(self.const) + elif self.action == 'count': + state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 + else: + raise ValueError('unknown action %r' % self.action) + state.order.append(self.obj) + + +class Argument(object): + + def __init__(self, dest, nargs=1, obj=None): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process(self, value, state): + if self.nargs > 1: + holes = sum(1 for x in value if x is None) + if holes == len(value): + value = None + elif holes != 0: + raise BadArgumentUsage('argument %s takes %d values' + % (self.dest, self.nargs)) + state.opts[self.dest] = value + state.order.append(self.obj) + + +class ParsingState(object): + + def __init__(self, rargs): + self.opts = {} + self.largs = [] + self.rargs = rargs + self.order = [] + + +class OptionParser(object): + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + """ + + def __init__(self, ctx=None): + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args = True + #: This tells the parser how to deal with unknown options. By + #: default it will error out (which is sensible), but there is a + #: second mode where it will ignore it and continue processing + #: after shifting all the unknown options into the resulting args. + self.ignore_unknown_options = False + if ctx is not None: + self.allow_interspersed_args = ctx.allow_interspersed_args + self.ignore_unknown_options = ctx.ignore_unknown_options + self._short_opt = {} + self._long_opt = {} + self._opt_prefixes = set(['-', '--']) + self._args = [] + + def add_option(self, opts, dest, action=None, nargs=1, const=None, + obj=None): + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``appnd_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + if obj is None: + obj = dest + opts = [normalize_opt(opt, self.ctx) for opt in opts] + option = Option(opts, dest, action=action, nargs=nargs, + const=const, obj=obj) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument(self, dest, nargs=1, obj=None): + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + if obj is None: + obj = dest + self._args.append(Argument(dest=dest, nargs=nargs, obj=obj)) + + def parse_args(self, args): + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state): + pargs, args = _unpack_args(state.largs + state.rargs, + [x.nargs for x in self._args]) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state): + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == '--': + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt(self, opt, explicit_value, state): + if opt not in self._long_opt: + possibilities = [word for word in self._long_opt + if word.startswith(opt)] + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + nargs = option.nargs + if len(state.rargs) < nargs: + _error_opt_args(nargs, opt) + elif nargs == 1: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + elif explicit_value is not None: + raise BadOptionUsage(opt, '%s option does not take a value' % opt) + + else: + value = None + + option.process(value, state) + + def _match_short_opt(self, arg, state): + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = normalize_opt(prefix + ch, self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + nargs = option.nargs + if len(state.rargs) < nargs: + _error_opt_args(nargs, opt) + elif nargs == 1: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + else: + value = None + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we re-combinate the string of the + # remaining options and re-attach the prefix, then report that + # to the state as new larg. This way there is basic combinatorics + # that can be achieved while still ignoring unknown arguments. + if self.ignore_unknown_options and unknown_options: + state.largs.append(prefix + ''.join(unknown_options)) + + def _process_opts(self, arg, state): + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if '=' in arg: + long_opt, explicit_value = arg.split('=', 1) + else: + long_opt = arg + norm_long_opt = normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + return self._match_short_opt(arg, state) + if not self.ignore_unknown_options: + raise + state.largs.append(arg) diff --git a/venv/Lib/site-packages/click/termui.py b/venv/Lib/site-packages/click/termui.py new file mode 100644 index 0000000..bf9a3aa --- /dev/null +++ b/venv/Lib/site-packages/click/termui.py @@ -0,0 +1,606 @@ +import os +import sys +import struct +import inspect +import itertools + +from ._compat import raw_input, text_type, string_types, \ + isatty, strip_ansi, get_winterm_size, DEFAULT_COLUMNS, WIN +from .utils import echo +from .exceptions import Abort, UsageError +from .types import convert_type, Choice, Path +from .globals import resolve_color_default + + +# The prompt functions to use. The doc tools currently override these +# functions to customize how they work. +visible_prompt_func = raw_input + +_ansi_colors = { + 'black': 30, + 'red': 31, + 'green': 32, + 'yellow': 33, + 'blue': 34, + 'magenta': 35, + 'cyan': 36, + 'white': 37, + 'reset': 39, + 'bright_black': 90, + 'bright_red': 91, + 'bright_green': 92, + 'bright_yellow': 93, + 'bright_blue': 94, + 'bright_magenta': 95, + 'bright_cyan': 96, + 'bright_white': 97, +} +_ansi_reset_all = '\033[0m' + + +def hidden_prompt_func(prompt): + import getpass + return getpass.getpass(prompt) + + +def _build_prompt(text, suffix, show_default=False, default=None, show_choices=True, type=None): + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += ' (' + ", ".join(map(str, type.choices)) + ')' + if default is not None and show_default: + prompt = '%s [%s]' % (prompt, default) + return prompt + suffix + + +def prompt(text, default=None, hide_input=False, confirmation_prompt=False, + type=None, value_proc=None, prompt_suffix=': ', show_default=True, + err=False, show_choices=True): + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending a interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + .. versionadded:: 7.0 + Added the show_choices parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: asks for confirmation for the value. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + """ + result = None + + def prompt_func(text): + f = hide_input and hidden_prompt_func or visible_prompt_func + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(text, nl=False, err=err) + return f('') + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt(text, prompt_suffix, show_default, default, show_choices, type) + + while 1: + while 1: + value = prompt_func(prompt) + if value: + break + elif default is not None: + if isinstance(value_proc, Path): + # validate Path default value(exists, dir_okay etc.) + value = default + break + return default + try: + result = value_proc(value) + except UsageError as e: + echo('Error: %s' % e.message, err=err) + continue + if not confirmation_prompt: + return result + while 1: + value2 = prompt_func('Repeat for confirmation: ') + if value2: + break + if value == value2: + return result + echo('Error: the two entered values do not match', err=err) + + +def confirm(text, default=False, abort=False, prompt_suffix=': ', + show_default=True, err=False): + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param text: the question to ask. + :param default: the default for the prompt. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + prompt = _build_prompt(text, prompt_suffix, show_default, + default and 'Y/n' or 'y/N') + while 1: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt, nl=False, err=err) + value = visible_prompt_func('').lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() + if value in ('y', 'yes'): + rv = True + elif value in ('n', 'no'): + rv = False + elif value == '': + rv = default + else: + echo('Error: invalid input', err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def get_terminal_size(): + """Returns the current size of the terminal as tuple in the form + ``(width, height)`` in columns and rows. + """ + # If shutil has get_terminal_size() (Python 3.3 and later) use that + if sys.version_info >= (3, 3): + import shutil + shutil_get_terminal_size = getattr(shutil, 'get_terminal_size', None) + if shutil_get_terminal_size: + sz = shutil_get_terminal_size() + return sz.columns, sz.lines + + # We provide a sensible default for get_winterm_size() when being invoked + # inside a subprocess. Without this, it would not provide a useful input. + if get_winterm_size is not None: + size = get_winterm_size() + if size == (0, 0): + return (79, 24) + else: + return size + + def ioctl_gwinsz(fd): + try: + import fcntl + import termios + cr = struct.unpack( + 'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) + except Exception: + return + return cr + + cr = ioctl_gwinsz(0) or ioctl_gwinsz(1) or ioctl_gwinsz(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + try: + cr = ioctl_gwinsz(fd) + finally: + os.close(fd) + except Exception: + pass + if not cr or not cr[0] or not cr[1]: + cr = (os.environ.get('LINES', 25), + os.environ.get('COLUMNS', DEFAULT_COLUMNS)) + return int(cr[1]), int(cr[0]) + + +def echo_via_pager(text_or_generator, color=None): + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = text_or_generator() + elif isinstance(text_or_generator, string_types): + i = [text_or_generator] + else: + i = iter(text_or_generator) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, string_types) else text_type(el) + for el in i) + + from ._termui_impl import pager + return pager(itertools.chain(text_generator, "\n"), color) + + +def progressbar(iterable=None, length=None, label=None, show_eta=True, + show_percent=None, show_pos=False, + item_show_func=None, fill_char='#', empty_char='-', + bar_template='%(label)s [%(bar)s] %(info)s', + info_sep=' ', width=36, file=None, color=None): + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already displayed. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `color` parameter. Added a `update` method to the + progressbar object. + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: a function called with the current item which + can return a string to show the current item + next to the progress bar. Note that the current + item can be `None`! + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: the file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + """ + from ._termui_impl import ProgressBar + color = resolve_color_default(color) + return ProgressBar(iterable=iterable, length=length, show_eta=show_eta, + show_percent=show_percent, show_pos=show_pos, + item_show_func=item_show_func, fill_char=fill_char, + empty_char=empty_char, bar_template=bar_template, + info_sep=info_sep, file=file, label=label, + width=width, color=color) + + +def clear(): + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + # If we're on Windows and we don't have colorama available, then we + # clear the screen by shelling out. Otherwise we can use an escape + # sequence. + if WIN: + os.system('cls') + else: + sys.stdout.write('\033[2J\033[1;1H') + + +def style(text, fg=None, bg=None, bold=None, dim=None, underline=None, + blink=None, reverse=None, reset=True): + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + .. versionadded:: 2.0 + + .. versionadded:: 7.0 + Added support for bright colors. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + """ + bits = [] + if fg: + try: + bits.append('\033[%dm' % (_ansi_colors[fg])) + except KeyError: + raise TypeError('Unknown color %r' % fg) + if bg: + try: + bits.append('\033[%dm' % (_ansi_colors[bg] + 10)) + except KeyError: + raise TypeError('Unknown color %r' % bg) + if bold is not None: + bits.append('\033[%dm' % (1 if bold else 22)) + if dim is not None: + bits.append('\033[%dm' % (2 if dim else 22)) + if underline is not None: + bits.append('\033[%dm' % (4 if underline else 24)) + if blink is not None: + bits.append('\033[%dm' % (5 if blink else 25)) + if reverse is not None: + bits.append('\033[%dm' % (7 if reverse else 27)) + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return ''.join(bits) + + +def unstyle(text): + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho(message=None, file=None, nl=True, err=False, color=None, **styles): + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + .. versionadded:: 2.0 + """ + if message is not None: + message = style(message, **styles) + return echo(message, file=file, nl=nl, err=err, color=color) + + +def edit(text=None, editor=None, env=None, require_save=True, + extension='.txt', filename=None): + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. + """ + from ._termui_impl import Editor + editor = Editor(editor=editor, env=env, require_save=require_save, + extension=extension) + if filename is None: + return editor.edit(text) + editor.edit_file(filename) + + +def launch(url, wait=False, locate=False): + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: waits for the program to stop. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar = None + + +def getchar(echo=False): + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + f = _getchar + if f is None: + from ._termui_impl import getchar as f + return f(echo) + + +def raw_terminal(): + from ._termui_impl import raw_terminal as f + return f() + + +def pause(info='Press any key to continue ...', err=False): + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: the info string to print before pausing. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/venv/Lib/site-packages/click/testing.py b/venv/Lib/site-packages/click/testing.py new file mode 100644 index 0000000..1b2924e --- /dev/null +++ b/venv/Lib/site-packages/click/testing.py @@ -0,0 +1,374 @@ +import os +import sys +import shutil +import tempfile +import contextlib +import shlex + +from ._compat import iteritems, PY2, string_types + + +# If someone wants to vendor click, we want to ensure the +# correct package is discovered. Ideally we could use a +# relative import here but unfortunately Python does not +# support that. +clickpkg = sys.modules[__name__.rsplit('.', 1)[0]] + + +if PY2: + from cStringIO import StringIO +else: + import io + from ._compat import _find_binary_reader + + +class EchoingStdin(object): + + def __init__(self, input, output): + self._input = input + self._output = output + + def __getattr__(self, x): + return getattr(self._input, x) + + def _echo(self, rv): + self._output.write(rv) + return rv + + def read(self, n=-1): + return self._echo(self._input.read(n)) + + def readline(self, n=-1): + return self._echo(self._input.readline(n)) + + def readlines(self): + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self): + return iter(self._echo(x) for x in self._input) + + def __repr__(self): + return repr(self._input) + + +def make_input_stream(input, charset): + # Is already an input stream. + if hasattr(input, 'read'): + if PY2: + return input + rv = _find_binary_reader(input) + if rv is not None: + return rv + raise TypeError('Could not find binary reader for input stream.') + + if input is None: + input = b'' + elif not isinstance(input, bytes): + input = input.encode(charset) + if PY2: + return StringIO(input) + return io.BytesIO(input) + + +class Result(object): + """Holds the captured result of an invoked CLI script.""" + + def __init__(self, runner, stdout_bytes, stderr_bytes, exit_code, + exception, exc_info=None): + #: The runner that created the result + self.runner = runner + #: The standard output as bytes. + self.stdout_bytes = stdout_bytes + #: The standard error as bytes, or False(y) if not available + self.stderr_bytes = stderr_bytes + #: The exit code as integer. + self.exit_code = exit_code + #: The exception that happened if one did. + self.exception = exception + #: The traceback + self.exc_info = exc_info + + @property + def output(self): + """The (standard) output as unicode string.""" + return self.stdout + + @property + def stdout(self): + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, 'replace') \ + .replace('\r\n', '\n') + + @property + def stderr(self): + """The standard error as unicode string.""" + if not self.stderr_bytes: + raise ValueError("stderr not separately captured") + return self.stderr_bytes.decode(self.runner.charset, 'replace') \ + .replace('\r\n', '\n') + + + def __repr__(self): + return '<%s %s>' % ( + type(self).__name__, + self.exception and repr(self.exception) or 'okay', + ) + + +class CliRunner(object): + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. This is + UTF-8 by default and should not be changed currently as + the reporting to Click only works in Python 2 properly. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from stdin writes + to stdout. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param mix_stderr: if this is set to `False`, then stdout and stderr are + preserved as independent streams. This is useful for + Unix-philosophy apps that have predictable stdout and + noisy stderr, such that each may be measured + independently + """ + + def __init__(self, charset=None, env=None, echo_stdin=False, + mix_stderr=True): + if charset is None: + charset = 'utf-8' + self.charset = charset + self.env = env or {} + self.echo_stdin = echo_stdin + self.mix_stderr = mix_stderr + + def get_default_prog_name(self, cli): + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or 'root' + + def make_env(self, overrides=None): + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation(self, input=None, env=None, color=False): + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up stdin with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + .. versionadded:: 4.0 + The ``color`` parameter was added. + + :param input: the input stream to put into sys.stdin. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + """ + input = make_input_stream(input, self.charset) + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = clickpkg.formatting.FORCED_WIDTH + clickpkg.formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + if PY2: + bytes_output = StringIO() + if self.echo_stdin: + input = EchoingStdin(input, bytes_output) + sys.stdout = bytes_output + if not self.mix_stderr: + bytes_error = StringIO() + sys.stderr = bytes_error + else: + bytes_output = io.BytesIO() + if self.echo_stdin: + input = EchoingStdin(input, bytes_output) + input = io.TextIOWrapper(input, encoding=self.charset) + sys.stdout = io.TextIOWrapper( + bytes_output, encoding=self.charset) + if not self.mix_stderr: + bytes_error = io.BytesIO() + sys.stderr = io.TextIOWrapper( + bytes_error, encoding=self.charset) + + if self.mix_stderr: + sys.stderr = sys.stdout + + sys.stdin = input + + def visible_input(prompt=None): + sys.stdout.write(prompt or '') + val = input.readline().rstrip('\r\n') + sys.stdout.write(val + '\n') + sys.stdout.flush() + return val + + def hidden_input(prompt=None): + sys.stdout.write((prompt or '') + '\n') + sys.stdout.flush() + return input.readline().rstrip('\r\n') + + def _getchar(echo): + char = sys.stdin.read(1) + if echo: + sys.stdout.write(char) + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi(stream=None, color=None): + if color is None: + return not default_color + return not color + + old_visible_prompt_func = clickpkg.termui.visible_prompt_func + old_hidden_prompt_func = clickpkg.termui.hidden_prompt_func + old__getchar_func = clickpkg.termui._getchar + old_should_strip_ansi = clickpkg.utils.should_strip_ansi + clickpkg.termui.visible_prompt_func = visible_input + clickpkg.termui.hidden_prompt_func = hidden_input + clickpkg.termui._getchar = _getchar + clickpkg.utils.should_strip_ansi = should_strip_ansi + + old_env = {} + try: + for key, value in iteritems(env): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (bytes_output, not self.mix_stderr and bytes_error) + finally: + for key, value in iteritems(old_env): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + clickpkg.termui.visible_prompt_func = old_visible_prompt_func + clickpkg.termui.hidden_prompt_func = old_hidden_prompt_func + clickpkg.termui._getchar = old__getchar_func + clickpkg.utils.should_strip_ansi = old_should_strip_ansi + clickpkg.formatting.FORCED_WIDTH = old_forced_width + + def invoke(self, cli, args=None, input=None, env=None, + catch_exceptions=True, color=False, mix_stderr=False, **extra): + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + .. versionadded:: 3.0 + The ``catch_exceptions`` parameter was added. + + .. versionchanged:: 3.0 + The result object now has an `exc_info` attribute with the + traceback if available. + + .. versionadded:: 4.0 + The ``color`` parameter was added. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + """ + exc_info = None + with self.isolation(input=input, env=env, color=color) as outstreams: + exception = None + exit_code = 0 + + if isinstance(args, string_types): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + exit_code = e.code + if exit_code is None: + exit_code = 0 + + if exit_code != 0: + exception = e + + if not isinstance(exit_code, int): + sys.stdout.write(str(exit_code)) + sys.stdout.write('\n') + exit_code = 1 + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + stdout = outstreams[0].getvalue() + stderr = outstreams[1] and outstreams[1].getvalue() + + return Result(runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + exit_code=exit_code, + exception=exception, + exc_info=exc_info) + + @contextlib.contextmanager + def isolated_filesystem(self): + """A context manager that creates a temporary folder and changes + the current working directory to it for isolated filesystem tests. + """ + cwd = os.getcwd() + t = tempfile.mkdtemp() + os.chdir(t) + try: + yield t + finally: + os.chdir(cwd) + try: + shutil.rmtree(t) + except (OSError, IOError): + pass diff --git a/venv/Lib/site-packages/click/types.py b/venv/Lib/site-packages/click/types.py new file mode 100644 index 0000000..1f88032 --- /dev/null +++ b/venv/Lib/site-packages/click/types.py @@ -0,0 +1,668 @@ +import os +import stat +from datetime import datetime + +from ._compat import open_stream, text_type, filename_to_ui, \ + get_filesystem_encoding, get_streerror, _get_argv_encoding, PY2 +from .exceptions import BadParameter +from .utils import safecall, LazyFile + + +class ParamType(object): + """Helper for converting values through types. The following is + necessary for a valid type: + + * it needs a name + * it needs to pass through None unchanged + * it needs to convert from a string + * it needs to convert its result type through unchanged + (eg: needs to be idempotent) + * it needs to be able to deal with param and context being `None`. + This can be the case when the object is used with prompt + inputs. + """ + is_composite = False + + #: the descriptive name of this type + name = None + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter = None + + def __call__(self, value, param=None, ctx=None): + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param): + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param): + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert(self, value, param, ctx): + """Converts the value. This is not invoked for values that are + `None` (the missing value). + """ + return value + + def split_envvar_value(self, rv): + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or '').split(self.envvar_list_splitter) + + def fail(self, message, param=None, ctx=None): + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self): + raise NotImplementedError() + + +class FuncParamType(ParamType): + + def __init__(self, func): + self.name = func.__name__ + self.func = func + + def convert(self, value, param, ctx): + try: + return self.func(value) + except ValueError: + try: + value = text_type(value) + except UnicodeError: + value = str(value).decode('utf-8', 'replace') + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = 'text' + + def convert(self, value, param, ctx): + return value + + def __repr__(self): + return 'UNPROCESSED' + + +class StringParamType(ParamType): + name = 'text' + + def convert(self, value, param, ctx): + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = get_filesystem_encoding() + if fs_enc != enc: + try: + value = value.decode(fs_enc) + except UnicodeError: + value = value.decode('utf-8', 'replace') + return value + return value + + def __repr__(self): + return 'STRING' + + +class Choice(ParamType): + """The choice type allows a value to be checked against a fixed set + of supported values. All of these values have to be strings. + + You should only pass a list or tuple of choices. Other iterables + (like generators) may lead to surprising results. + + See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + """ + + name = 'choice' + + def __init__(self, choices, case_sensitive=True): + self.choices = choices + self.case_sensitive = case_sensitive + + def get_metavar(self, param): + return '[%s]' % '|'.join(self.choices) + + def get_missing_message(self, param): + return 'Choose from:\n\t%s.' % ',\n\t'.join(self.choices) + + def convert(self, value, param, ctx): + # Exact match + if value in self.choices: + return value + + # Match through normalization and case sensitivity + # first do token_normalize_func, then lowercase + # preserve original `value` to produce an accurate message in + # `self.fail` + normed_value = value + normed_choices = self.choices + + if ctx is not None and \ + ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(value) + normed_choices = [ctx.token_normalize_func(choice) for choice in + self.choices] + + if not self.case_sensitive: + normed_value = normed_value.lower() + normed_choices = [choice.lower() for choice in normed_choices] + + if normed_value in normed_choices: + return normed_value + + self.fail('invalid choice: %s. (choose from %s)' % + (value, ', '.join(self.choices)), param, ctx) + + def __repr__(self): + return 'Choice(%r)' % list(self.choices) + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + name = 'datetime' + + def __init__(self, formats=None): + self.formats = formats or [ + '%Y-%m-%d', + '%Y-%m-%dT%H:%M:%S', + '%Y-%m-%d %H:%M:%S' + ] + + def get_metavar(self, param): + return '[{}]'.format('|'.join(self.formats)) + + def _try_to_convert_date(self, value, format): + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert(self, value, param, ctx): + # Exact match + for format in self.formats: + dtime = self._try_to_convert_date(value, format) + if dtime: + return dtime + + self.fail( + 'invalid datetime format: {}. (choose from {})'.format( + value, ', '.join(self.formats))) + + def __repr__(self): + return 'DateTime' + + +class IntParamType(ParamType): + name = 'integer' + + def convert(self, value, param, ctx): + try: + return int(value) + except (ValueError, UnicodeError): + self.fail('%s is not a valid integer' % value, param, ctx) + + def __repr__(self): + return 'INT' + + +class IntRange(IntParamType): + """A parameter that works similar to :data:`click.INT` but restricts + the value to fit into a range. The default behavior is to fail if the + value falls outside the range, but it can also be silently clamped + between the two edges. + + See :ref:`ranges` for an example. + """ + name = 'integer range' + + def __init__(self, min=None, max=None, clamp=False): + self.min = min + self.max = max + self.clamp = clamp + + def convert(self, value, param, ctx): + rv = IntParamType.convert(self, value, param, ctx) + if self.clamp: + if self.min is not None and rv < self.min: + return self.min + if self.max is not None and rv > self.max: + return self.max + if self.min is not None and rv < self.min or \ + self.max is not None and rv > self.max: + if self.min is None: + self.fail('%s is bigger than the maximum valid value ' + '%s.' % (rv, self.max), param, ctx) + elif self.max is None: + self.fail('%s is smaller than the minimum valid value ' + '%s.' % (rv, self.min), param, ctx) + else: + self.fail('%s is not in the valid range of %s to %s.' + % (rv, self.min, self.max), param, ctx) + return rv + + def __repr__(self): + return 'IntRange(%r, %r)' % (self.min, self.max) + + +class FloatParamType(ParamType): + name = 'float' + + def convert(self, value, param, ctx): + try: + return float(value) + except (UnicodeError, ValueError): + self.fail('%s is not a valid floating point value' % + value, param, ctx) + + def __repr__(self): + return 'FLOAT' + + +class FloatRange(FloatParamType): + """A parameter that works similar to :data:`click.FLOAT` but restricts + the value to fit into a range. The default behavior is to fail if the + value falls outside the range, but it can also be silently clamped + between the two edges. + + See :ref:`ranges` for an example. + """ + name = 'float range' + + def __init__(self, min=None, max=None, clamp=False): + self.min = min + self.max = max + self.clamp = clamp + + def convert(self, value, param, ctx): + rv = FloatParamType.convert(self, value, param, ctx) + if self.clamp: + if self.min is not None and rv < self.min: + return self.min + if self.max is not None and rv > self.max: + return self.max + if self.min is not None and rv < self.min or \ + self.max is not None and rv > self.max: + if self.min is None: + self.fail('%s is bigger than the maximum valid value ' + '%s.' % (rv, self.max), param, ctx) + elif self.max is None: + self.fail('%s is smaller than the minimum valid value ' + '%s.' % (rv, self.min), param, ctx) + else: + self.fail('%s is not in the valid range of %s to %s.' + % (rv, self.min, self.max), param, ctx) + return rv + + def __repr__(self): + return 'FloatRange(%r, %r)' % (self.min, self.max) + + +class BoolParamType(ParamType): + name = 'boolean' + + def convert(self, value, param, ctx): + if isinstance(value, bool): + return bool(value) + value = value.lower() + if value in ('true', 't', '1', 'yes', 'y'): + return True + elif value in ('false', 'f', '0', 'no', 'n'): + return False + self.fail('%s is not a valid boolean' % value, param, ctx) + + def __repr__(self): + return 'BOOL' + + +class UUIDParameterType(ParamType): + name = 'uuid' + + def convert(self, value, param, ctx): + import uuid + try: + if PY2 and isinstance(value, text_type): + value = value.encode('ascii') + return uuid.UUID(value) + except (UnicodeError, ValueError): + self.fail('%s is not a valid UUID value' % value, param, ctx) + + def __repr__(self): + return 'UUID' + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Starting with Click 2.0, files can also be opened atomically in which + case all writes go into a separate file in the same folder and upon + completion the file will be moved over to the original location. This + is useful if a file regularly read by other users is modified. + + See :ref:`file-args` for more information. + """ + name = 'filename' + envvar_list_splitter = os.path.pathsep + + def __init__(self, mode='r', encoding=None, errors='strict', lazy=None, + atomic=False): + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def resolve_lazy_flag(self, value): + if self.lazy is not None: + return self.lazy + if value == '-': + return False + elif 'w' in self.mode: + return True + return False + + def convert(self, value, param, ctx): + try: + if hasattr(value, 'read') or hasattr(value, 'write'): + return value + + lazy = self.resolve_lazy_flag(value) + + if lazy: + f = LazyFile(value, self.mode, self.encoding, self.errors, + atomic=self.atomic) + if ctx is not None: + ctx.call_on_close(f.close_intelligently) + return f + + f, should_close = open_stream(value, self.mode, + self.encoding, self.errors, + atomic=self.atomic) + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(safecall(f.close)) + else: + ctx.call_on_close(safecall(f.flush)) + return f + except (IOError, OSError) as e: + self.fail('Could not open file: %s: %s' % ( + filename_to_ui(value), + get_streerror(e), + ), param, ctx) + + +class Path(ParamType): + """The path type is similar to the :class:`File` type but it performs + different checks. First of all, instead of returning an open file + handle it returns just the filename. Secondly, it can perform various + basic checks about what the file or directory should be. + + .. versionchanged:: 6.0 + `allow_dash` was added. + + :param exists: if set to true, the file or directory needs to exist for + this value to be valid. If this is not required and a + file does indeed not exist, then all further checks are + silently skipped. + :param file_okay: controls if a file is a possible value. + :param dir_okay: controls if a directory is a possible value. + :param writable: if true, a writable check is performed. + :param readable: if true, a readable check is performed. + :param resolve_path: if this is true, then the path is fully resolved + before the value is passed onwards. This means + that it's absolute and symlinks are resolved. It + will not expand a tilde-prefix, as this is + supposed to be done by the shell only. + :param allow_dash: If this is set to `True`, a single dash to indicate + standard streams is permitted. + :param path_type: optionally a string type that should be used to + represent the path. The default is `None` which + means the return value will be either bytes or + unicode depending on what makes most sense given the + input data Click deals with. + """ + envvar_list_splitter = os.path.pathsep + + def __init__(self, exists=False, file_okay=True, dir_okay=True, + writable=False, readable=True, resolve_path=False, + allow_dash=False, path_type=None): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.writable = writable + self.readable = readable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name = 'file' + self.path_type = 'File' + elif self.dir_okay and not self.file_okay: + self.name = 'directory' + self.path_type = 'Directory' + else: + self.name = 'path' + self.path_type = 'Path' + + def coerce_path_result(self, rv): + if self.type is not None and not isinstance(rv, self.type): + if self.type is text_type: + rv = rv.decode(get_filesystem_encoding()) + else: + rv = rv.encode(get_filesystem_encoding()) + return rv + + def convert(self, value, param, ctx): + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b'-', '-') + + if not is_dash: + if self.resolve_path: + rv = os.path.realpath(rv) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail('%s "%s" does not exist.' % ( + self.path_type, + filename_to_ui(value) + ), param, ctx) + + if not self.file_okay and stat.S_ISREG(st.st_mode): + self.fail('%s "%s" is a file.' % ( + self.path_type, + filename_to_ui(value) + ), param, ctx) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail('%s "%s" is a directory.' % ( + self.path_type, + filename_to_ui(value) + ), param, ctx) + if self.writable and not os.access(value, os.W_OK): + self.fail('%s "%s" is not writable.' % ( + self.path_type, + filename_to_ui(value) + ), param, ctx) + if self.readable and not os.access(value, os.R_OK): + self.fail('%s "%s" is not readable.' % ( + self.path_type, + filename_to_ui(value) + ), param, ctx) + + return self.coerce_path_result(rv) + + +class Tuple(CompositeParamType): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types): + self.types = [convert_type(ty) for ty in types] + + @property + def name(self): + return "<" + " ".join(ty.name for ty in self.types) + ">" + + @property + def arity(self): + return len(self.types) + + def convert(self, value, param, ctx): + if len(value) != len(self.types): + raise TypeError('It would appear that nargs is set to conflict ' + 'with the composite type arity.') + return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) + + +def convert_type(ty, default=None): + """Converts a callable or python ty into the most appropriate param + ty. + """ + guessed_type = False + if ty is None and default is not None: + if isinstance(default, tuple): + ty = tuple(map(type, default)) + else: + ty = type(default) + guessed_type = True + + if isinstance(ty, tuple): + return Tuple(ty) + if isinstance(ty, ParamType): + return ty + if ty is text_type or ty is str or ty is None: + return STRING + if ty is int: + return INT + # Booleans are only okay if not guessed. This is done because for + # flags the default value is actually a bit of a lie in that it + # indicates which of the flags is the one we want. See get_default() + # for more information. + if ty is bool and not guessed_type: + return BOOL + if ty is float: + return FLOAT + if guessed_type: + return STRING + + # Catch a common mistake + if __debug__: + try: + if issubclass(ty, ParamType): + raise AssertionError('Attempted to use an uninstantiated ' + 'parameter type (%s).' % ty) + except TypeError: + pass + return FuncParamType(ty) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but internally +#: no string conversion takes place. This is necessary to achieve the +#: same bytes/unicode behavior on Python 2/3 in situations where you want +#: to not convert argument types. This is usually useful when working +#: with file paths as they can appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL = BoolParamType() + +#: A UUID parameter. +UUID = UUIDParameterType() diff --git a/venv/Lib/site-packages/click/utils.py b/venv/Lib/site-packages/click/utils.py new file mode 100644 index 0000000..fc84369 --- /dev/null +++ b/venv/Lib/site-packages/click/utils.py @@ -0,0 +1,440 @@ +import os +import sys + +from .globals import resolve_color_default + +from ._compat import text_type, open_stream, get_filesystem_encoding, \ + get_streerror, string_types, PY2, binary_streams, text_streams, \ + filename_to_ui, auto_wrap_for_ansi, strip_ansi, should_strip_ansi, \ + _default_text_stdout, _default_text_stderr, is_bytes, WIN + +if not PY2: + from ._compat import _find_binary_writer +elif WIN: + from ._winconsole import _get_windows_argv, \ + _hash_py_argv, _initial_argv_hash + + +echo_native_types = string_types + (bytes, bytearray) + + +def _posixify(name): + return '-'.join(name.split()).lower() + + +def safecall(func): + """Wraps a function so that it swallows exceptions.""" + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except Exception: + pass + return wrapper + + +def make_str(value): + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(get_filesystem_encoding()) + except UnicodeError: + return value.decode('utf-8', 'replace') + return text_type(value) + + +def make_default_short_help(help, max_length=45): + """Return a condensed version of help string.""" + words = help.split() + total_length = 0 + result = [] + done = False + + for word in words: + if word[-1:] == '.': + done = True + new_length = result and 1 + len(word) or len(word) + if total_length + new_length > max_length: + result.append('...') + done = True + else: + if result: + result.append(' ') + result.append(word) + if done: + break + total_length += new_length + + return ''.join(result) + + +class LazyFile(object): + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + """ + + def __init__(self, filename, mode='r', encoding=None, errors='strict', + atomic=False): + self.name = filename + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + + if filename == '-': + self._f, self.should_close = open_stream(filename, mode, + encoding, errors) + else: + if 'r' in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name): + return getattr(self.open(), name) + + def __repr__(self): + if self._f is not None: + return repr(self._f) + return '' % (self.name, self.mode) + + def open(self): + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream(self.name, self.mode, + self.encoding, + self.errors, + atomic=self.atomic) + except (IOError, OSError) as e: + from .exceptions import FileError + raise FileError(self.name, hint=get_streerror(e)) + self._f = rv + return rv + + def close(self): + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self): + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + self.close_intelligently() + + def __iter__(self): + self.open() + return iter(self._f) + + +class KeepOpenFile(object): + + def __init__(self, file): + self._file = file + + def __getattr__(self, name): + return getattr(self._file, name) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + pass + + def __repr__(self): + return repr(self._file) + + def __iter__(self): + return iter(self._file) + + +def echo(message=None, file=None, nl=True, err=False, color=None): + """Prints a message plus a newline to the given file or stdout. On + first sight, this looks like the print function, but it has improved + support for handling Unicode and binary data that does not fail no + matter how badly configured the system is. + + Primarily it means that you can print binary data as well as Unicode + data on both 2.x and 3.x to the given file in the most appropriate way + possible. This is a very carefree function in that it will try its + best to not fail. As of Click 6.0 this includes support for unicode + output on the Windows console. + + In addition to that, if `colorama`_ is installed, the echo function will + also support clever handling of ANSI codes. Essentially it will then + do the following: + + - add transparent handling of ANSI color codes on Windows. + - hide ANSI codes automatically if the destination file is not a + terminal. + + .. _colorama: https://pypi.org/project/colorama/ + + .. versionchanged:: 6.0 + As of Click 6.0 the echo function will properly support unicode + output on the windows console. Not that click does not modify + the interpreter in any way which means that `sys.stdout` or the + print statement or function will still not provide unicode support. + + .. versionchanged:: 2.0 + Starting with version 2.0 of Click, the echo function will work + with colorama if it's installed. + + .. versionadded:: 3.0 + The `err` parameter was added. + + .. versionchanged:: 4.0 + Added the `color` flag. + + :param message: the message to print + :param file: the file to write to (defaults to ``stdout``) + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``. This is faster and easier than calling + :func:`get_text_stderr` yourself. + :param nl: if set to `True` (the default) a newline is printed afterwards. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, echo_native_types): + message = text_type(message) + + if nl: + message = message or u'' + if isinstance(message, text_type): + message += u'\n' + else: + message += b'\n' + + # If there is a message, and we're in Python 3, and the value looks + # like bytes, we manually need to find the binary stream and write the + # message in there. This is done separately so that most stream + # types will work as you would expect. Eg: you can write to StringIO + # for other cases. + if message and not PY2 and is_bytes(message): + binary_file = _find_binary_writer(file) + if binary_file is not None: + file.flush() + binary_file.write(message) + binary_file.flush() + return + + # ANSI-style support. If there is no message or we are dealing with + # bytes nothing is happening. If we are connected to a file we want + # to strip colors. If we are on windows we either wrap the stream + # to strip the color or we use the colorama support to translate the + # ansi codes to API calls. + if message and not is_bytes(message): + color = resolve_color_default(color) + if should_strip_ansi(file, color): + message = strip_ansi(message) + elif WIN: + if auto_wrap_for_ansi is not None: + file = auto_wrap_for_ansi(file) + elif not color: + message = strip_ansi(message) + + if message: + file.write(message) + file.flush() + + +def get_binary_stream(name): + """Returns a system stream for byte processing. This essentially + returns the stream from the sys module with the given name but it + solves some compatibility issues between different Python versions. + Primarily this function is necessary for getting binary streams on + Python 3. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError('Unknown standard stream %r' % name) + return opener() + + +def get_text_stream(name, encoding=None, errors='strict'): + """Returns a system stream for text processing. This usually returns + a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts on Python 3 + for already correctly configured streams. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError('Unknown standard stream %r' % name) + return opener(encoding, errors) + + +def open_file(filename, mode='r', encoding=None, errors='strict', + lazy=False, atomic=False): + """This is similar to how the :class:`File` works but for manual + usage. Files are opened non lazy by default. This can open regular + files as well as stdin/stdout if ``'-'`` is passed. + + If stdin/stdout is returned the stream is wrapped so that the context + manager will not close the stream accidentally. This makes it possible + to always use the function like this without having to worry to + accidentally close a standard stream:: + + with open_file(filename) as f: + ... + + .. versionadded:: 3.0 + + :param filename: the name of the file to open (or ``'-'`` for stdin/stdout). + :param mode: the mode in which to open the file. + :param encoding: the encoding to use. + :param errors: the error handling for this file. + :param lazy: can be flipped to true to open the file lazily. + :param atomic: in atomic mode writes go into a temporary file and it's + moved on close. + """ + if lazy: + return LazyFile(filename, mode, encoding, errors, atomic=atomic) + f, should_close = open_stream(filename, mode, encoding, errors, + atomic=atomic) + if not should_close: + f = KeepOpenFile(f) + return f + + +def get_os_args(): + """This returns the argument part of sys.argv in the most appropriate + form for processing. What this means is that this return value is in + a format that works for Click to process but does not necessarily + correspond well to what's actually standard for the interpreter. + + On most environments the return value is ``sys.argv[:1]`` unchanged. + However if you are on Windows and running Python 2 the return value + will actually be a list of unicode strings instead because the + default behavior on that platform otherwise will not be able to + carry all possible values that sys.argv can have. + + .. versionadded:: 6.0 + """ + # We can only extract the unicode argv if sys.argv has not been + # changed since the startup of the application. + if PY2 and WIN and _initial_argv_hash == _hash_py_argv(): + return _get_windows_argv() + return sys.argv[1:] + + +def format_filename(filename, shorten=False): + """Formats a filename for user display. The main purpose of this + function is to ensure that the filename can be displayed at all. This + will decode the filename to unicode if necessary in a way that it will + not fail. Optionally, it can shorten the filename to not include the + full path to the filename. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + return filename_to_ui(filename) + + +def get_app_dir(app_name, roaming=True, force_posix=False): + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Win XP (roaming): + ``C:\Documents and Settings\\Local Settings\Application Data\Foo Bar`` + Win XP (not roaming): + ``C:\Documents and Settings\\Application Data\Foo Bar`` + Win 7 (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Win 7 (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no affect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = roaming and 'APPDATA' or 'LOCALAPPDATA' + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser('~') + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser('~/.' + _posixify(app_name))) + if sys.platform == 'darwin': + return os.path.join(os.path.expanduser( + '~/Library/Application Support'), app_name) + return os.path.join( + os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), + _posixify(app_name)) + + +class PacifyFlushWrapper(object): + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped): + self.wrapped = wrapped + + def flush(self): + try: + self.wrapped.flush() + except IOError as e: + import errno + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr): + return getattr(self.wrapped, attr) diff --git a/venv/Lib/site-packages/dominate-2.3.5.dist-info/INSTALLER b/venv/Lib/site-packages/dominate-2.3.5.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/dominate-2.3.5.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/dominate-2.3.5.dist-info/LICENSE.txt b/venv/Lib/site-packages/dominate-2.3.5.dist-info/LICENSE.txt new file mode 100644 index 0000000..cca7fc2 --- /dev/null +++ b/venv/Lib/site-packages/dominate-2.3.5.dist-info/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/venv/Lib/site-packages/dominate-2.3.5.dist-info/METADATA b/venv/Lib/site-packages/dominate-2.3.5.dist-info/METADATA new file mode 100644 index 0000000..fc5aca6 --- /dev/null +++ b/venv/Lib/site-packages/dominate-2.3.5.dist-info/METADATA @@ -0,0 +1,601 @@ +Metadata-Version: 2.1 +Name: dominate +Version: 2.3.5 +Summary: Dominate is a Python library for creating and manipulating HTML documents using an elegant DOM API. +Home-page: https://github.com/Knio/dominate/ +Author: Tom Flanagan and Jake Wharton +Author-email: tom@zkpq.ca +License: LGPLv3 +Keywords: framework templating template html xhtml python html5 +Platform: UNKNOWN +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3) +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* +Description-Content-Type: text/markdown + +Dominate +======== + +`Dominate` is a Python library for creating and manipulating HTML documents using an elegant DOM API. +It allows you to write HTML pages in pure Python very concisely, which eliminates the need to learn another template language, and lets you take advantage of the more powerful features of Python. + +Python: + +```python +import dominate +from dominate.tags import * + +doc = dominate.document(title='Dominate your HTML') + +with doc.head: + link(rel='stylesheet', href='style.css') + script(type='text/javascript', src='script.js') + +with doc: + with div(id='header').add(ol()): + for i in ['home', 'about', 'contact']: + li(a(i.title(), href='/%s.html' % i)) + + with div(): + attr(cls='body') + p('Lorem ipsum..') + +print(doc) +``` + +Output: + +```html + + + + Dominate your HTML + + + + +

            +
            +

            Lorem ipsum..

            +
            + + +``` + + +Compatibility +------------- + +`Dominate` is compatible with both Python 2.7 and Python 3.4+. + +[![Build Status](https://travis-ci.org/Knio/dominate.svg?branch=master)](https://travis-ci.org/Knio/dominate) +[![Coverage Status](https://coveralls.io/repos/github/Knio/dominate/badge.svg?branch=master)](https://coveralls.io/github/Knio/dominate?branch=master) + + + +Installation +------------ + +The recommended way to install `dominate` is with +[`pip`](http://pypi.python.org/pypi/pip/): + + sudo pip install dominate + +[![PyPI version](https://badge.fury.io/py/dominate.svg)](http://badge.fury.io/py/dominate) + + +Developed By +------------ + +* Tom Flanagan - +* Jake Wharton - +* [Brad Janke](//github.com/bradj) + +Git repository located at +[github.com/Knio/dominate](//github.com/Knio/dominate) + + +Examples +======== + +All examples assume you have imported the appropriate tags or entire tag set: + +```python +from dominate.tags import * +``` + + +Hello, World! +------------- + +The most basic feature of `dominate` exposes a class for each HTML element, where the constructor +accepts child elements, text, or keyword attributes. `dominate` nodes return their HTML representation +from the `__str__`, `__unicode__`, and `render()` methods. + +```python +print(html(body(h1('Hello, World!')))) +``` +```html + + +

            Hello, World!

            + + +``` + +Attributes +---------- + +`Dominate` can also use keyword arguments to append attributes onto your tags. Most of the attributes are a direct copy from the HTML spec with a few variations. + +For attributes `class` and `for` which conflict with Python's [reserved keywords](http://docs.python.org/2/reference/lexical_analysis.html#keywords "Reserved Keywords"), you can use the following aliases: + +| class | for | +|-------|-----| +|_class | _for | +|cls | fr | +|className|htmlFor| +|class_name|html_for| + + +```python +test = label(cls='classname anothername', fr='someinput') +print(test) +``` +```html + +``` + +Use `data_*` for [custom HTML5 data attributes](http://www.w3.org/html/wg/drafts/html/master/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes "HTML5 Data Attributes"). + +```python +test = div(data_employee='101011') +print(test) +``` +```html +
            +``` + +You can also modify the attributes of tags through a dictionary-like interface: + +```python +header = div() +header['id'] = 'header' +print(header) +``` +```html + +``` + +Complex Structures +------------------ + +Through the use of the `+=` operator and the `.add()` method you can easily create more advanced structures. + +Create a simple list: + +```python +list = ul() +for item in range(4): + list += li('Item #', item) +print(list) +``` +```html +
              +
            • Item #0
            • +
            • Item #1
            • +
            • Item #2
            • +
            • Item #3
            • +
            +``` + +`dominate` supports iterables to help streamline your code: + +```python +print(ul(li(a(name, href=link), __pretty=False) for name, link in menu_items)) +``` +```html + +``` + +A simple document tree: + +```python +_html = html() +_body = _html.add(body()) +header = _body.add(div(id='header')) +content = _body.add(div(id='content')) +footer = _body.add(div(id='footer')) +print(_html) +``` +```html + + + +
            + + + +``` + +For clean code, the `.add()` method returns children in tuples. The above example can be cleaned up and expanded like this: + +```python +_html = html() +_head, _body = _html.add(head(title('Simple Document Tree')), body()) +names = ['header', 'content', 'footer'] +header, content, footer = _body.add(div(id=name) for name in names) +print(_html) +``` +```html + + + Simple Document Tree + + + +
            + + + +``` + +You can modify the attributes of tags through a dictionary-like interface: + +```python +header = div() +header['id'] = 'header' +print(header) +``` +```html + +``` + +Or the children of a tag though an array-line interface: + +```python +header = div('Test') +header[0] = 'Hello World' +print(header) +``` +```html +
            Hello World
            +``` + +Comments can be created using objects too! + +```python +print(comment('BEGIN HEADER')) +``` +```html + +``` + +```python +print(comment(p('Upgrade to newer IE!'), condition='lt IE9')) +``` +```html + +``` + +Rendering +--------- + +By default, `render()` tries to make all output human readable, with one HTML +element per line and two spaces of indentation. + +This behavior can be controlled by the `__pretty` (default: `True` except for +certain element types like `pre`) attribute when creating an element, and by +the `pretty` (default: `True`), `indent` (default: ` `) and `xhtml` (default: `False`) + arguments to `render()`. Rendering options propagate to all descendant nodes. + +```python +a = div(span('Hello World')) +print(a.render()) +``` +```html +
            + Hello World +
            +``` +```python +print(a.render(pretty=False)) +``` +```html +
            Hello World
            +``` +```python +print(a.render(indent='\t')) +``` +```html +
            + Hello World +
            +``` +```python +a = div(span('Hello World'), __pretty=False) +print(a.render()) +``` +```html +
            Hello World
            +``` +```python +d = div() +with d: + hr() + p("Test") + br() +print(d.render()) +print(d.render(xhtml=True)) +``` +```html +
            +
            +

            Test


            +
            +
            +
            +

            Test


            +
            +``` + + +Context Managers +---------------- + +You can also add child elements using Python's `with` statement: + +```python +h = ul() +with h: + li('One') + li('Two') + li('Three') + +print(h) +``` +```html +
              +
            • One
            • +
            • Two
            • +
            • Three
            • +
            +``` + + +You can use this along with the other mechanisms of adding children elements, including nesting `with` statements, and it works as expected: + +```python +h = html() +with h.add(body()).add(div(id='content')): + h1('Hello World!') + p('Lorem ipsum ...') + with table().add(tbody()): + l = tr() + l += td('One') + l.add(td('Two')) + with l: + td('Three') + +print(h) +``` +```html + + +
            +

            Hello World!

            +

            Lorem ipsum ...

            + + + + + + + + +
            OneTwoThree
            +
            + + +``` + +When the context is closed, any nodes that were not already added to something get added to the current context. + +Attributes can be added to the current context with the `attr` function: + +```python +d = div() +with d: + attr(id='header') + + print(d) + ``` + ```html + +``` + +And text nodes can be added with the `dominate.util.text` function: + +```python +from dominate.util import text +para = p(__pretty=False) +with para: + text('Have a look at our ') + a('other products', href='/products') + +print(para) +``` +```html +

            Have a look at our other products

            +``` + + +Decorators +---------- + +`Dominate` is great for creating reusable widgets for parts of your page. Consider this example: + +```python +def greeting(name): + with div() as d: + p('Hello, %s' % name) + return d + +print(greeting('Bob')) +``` +```html +
            +

            Hello, Bob

            +
            +``` + +You can see the following pattern being repeated here: + +```python +def widget(parameters): + with tag() as t: + ... + return t +``` + +This boilerplate can be avoided by using tags (objects and instances) as decorators + +```python +@div +def greeting(name): + p('Hello %s' % name) +print(greeting('Bob')) +``` +```html +
            +

            Hello Bob

            +
            +``` + +The decorated function will return a new instance of the tag used to decorate it, and execute in a `with` context which will collect all the nodes created inside it. + +You can also use instances of tags as decorators, if you need to add attributes or other data to the root node of the widget. +Each call to the decorated function will return a copy of the node used to decorate it. + +```python +@div(h2('Welcome'), cls='greeting') +def greeting(name): + p('Hello %s' % name) + +print(greeting('Bob')) +``` +```html + +
            +

            Welcome

            +

            Hello Bob

            +
            +``` + +Creating Documents +------------------ + +Since creating the common structure of an HTML document everytime would be excessively tedious dominate provides a class to create and manage them for you: `document`. + +When you create a new document, the basic HTML tag structure is created for you. + +```python +d = document() +print(d) +``` +```html + + + + Dominate + + + +``` + +The `document` class accepts `title`, `doctype`, and `request` keyword arguments. +The default values for these arguments are `Dominate`, ``, and `None` respectively. + +The `document` class also provides helpers to allow you to access the `title`, `head`, and `body` nodes directly. + +```python +d = document() +``` + +```python +>>> d.head + +>>> d.body + +>>> d.title +u'Dominate' +``` + + +The `document` class also provides helpers to allow you to directly add nodes to the `body` tag. + +```python +d = document() +d += h1('Hello, World!') +d += p('This is a paragraph.') +print(d) +``` +```html + + + + Dominate + + +

            Hello, World!

            +

            This is a paragraph.

            + + +``` + +Embedding HTML +-------------- + +If you need to embed a node of pre-formed HTML coming from a library such as markdown or the like, you can avoid escaped HTML by using the raw method from the dominate.util package: + +``` +from dominate.util import raw +... +td(raw('Example')) +``` + +Without the raw call, this code would render escaped HTML with lt, etc. + + diff --git a/venv/Lib/site-packages/dominate-2.3.5.dist-info/RECORD b/venv/Lib/site-packages/dominate-2.3.5.dist-info/RECORD new file mode 100644 index 0000000..525660d --- /dev/null +++ b/venv/Lib/site-packages/dominate-2.3.5.dist-info/RECORD @@ -0,0 +1,20 @@ +dominate/__init__.py,sha256=qZ6CLTkIM6XUGeCvrWxODTC0KwHqdGHhuPK_IOq9FN8,88 +dominate/_version.py,sha256=HXwotufXh39XAYrijeX2QHTUeybK0neLP3aCrZuHQmA,23 +dominate/document.py,sha256=wKkHjVd4YINjgtDUvtkdpbav2wq29P4ANBMqbAUgWK4,2217 +dominate/dom1core.py,sha256=abRAFVImeyRmdK_eLUE3Fr0LfnIGiRkJzfKZCfKJXqM,1760 +dominate/dom_tag.py,sha256=l2KqEFeT22HtJS0Y-M7IhiI3tHjbKJLV3vqsrgN0ENo,12409 +dominate/tags.py,sha256=Y7gy_CbllxY6FR032f-FXzcrvFdw5W43ISqQ8J_apWE,28147 +dominate/util.py,sha256=1G72cjVMy2JuciAo50JOcHJPYV6d99PZXFGmxNwrVkU,3903 +dominate-2.3.5.dist-info/LICENSE.txt,sha256=nM8mz-hF4OuLtYBT5HNm56traXrgEPdlCXjUtxt9H8E,7637 +dominate-2.3.5.dist-info/METADATA,sha256=DMJzyDJprZ45tCx9ulx2Zs1f-uJ5xlLljMDtEyg2U6o,13125 +dominate-2.3.5.dist-info/RECORD,, +dominate-2.3.5.dist-info/WHEEL,sha256=gduuPyBvFJQSQ0zdyxF7k0zynDXbIbvg5ZBHoXum5uk,110 +dominate-2.3.5.dist-info/top_level.txt,sha256=lkq2NuHoGLvvjfSl6OLHyLNBidBuRCf1CbUpKhH-lYY,9 +dominate-2.3.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +dominate/__pycache__/document.cpython-36.pyc,, +dominate/__pycache__/dom1core.cpython-36.pyc,, +dominate/__pycache__/dom_tag.cpython-36.pyc,, +dominate/__pycache__/tags.cpython-36.pyc,, +dominate/__pycache__/util.cpython-36.pyc,, +dominate/__pycache__/_version.cpython-36.pyc,, +dominate/__pycache__/__init__.cpython-36.pyc,, diff --git a/venv/Lib/site-packages/dominate-2.3.5.dist-info/WHEEL b/venv/Lib/site-packages/dominate-2.3.5.dist-info/WHEEL new file mode 100644 index 0000000..1316c41 --- /dev/null +++ b/venv/Lib/site-packages/dominate-2.3.5.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.31.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/dominate-2.3.5.dist-info/top_level.txt b/venv/Lib/site-packages/dominate-2.3.5.dist-info/top_level.txt new file mode 100644 index 0000000..dec87aa --- /dev/null +++ b/venv/Lib/site-packages/dominate-2.3.5.dist-info/top_level.txt @@ -0,0 +1 @@ +dominate diff --git a/venv/Lib/site-packages/dominate/__init__.py b/venv/Lib/site-packages/dominate/__init__.py new file mode 100644 index 0000000..50668cb --- /dev/null +++ b/venv/Lib/site-packages/dominate/__init__.py @@ -0,0 +1,4 @@ +from ._version import __version__ +version = __version__ + +from .document import document diff --git a/venv/Lib/site-packages/dominate/_version.py b/venv/Lib/site-packages/dominate/_version.py new file mode 100644 index 0000000..fa6ddbb --- /dev/null +++ b/venv/Lib/site-packages/dominate/_version.py @@ -0,0 +1,2 @@ +__version__ = '2.3.5' + diff --git a/venv/Lib/site-packages/dominate/document.py b/venv/Lib/site-packages/dominate/document.py new file mode 100644 index 0000000..22dc81c --- /dev/null +++ b/venv/Lib/site-packages/dominate/document.py @@ -0,0 +1,75 @@ +__license__ = ''' +This file is part of Dominate. + +Dominate is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +Dominate is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General +Public License along with Dominate. If not, see +. +''' + +from . import tags + +try: + basestring = basestring +except NameError: # py3 + basestring = str + unicode = str + +class document(tags.html): + tagname = 'html' + def __init__(self, title='Dominate', doctype='', request=None): + ''' + Creates a new document instance. Accepts `title`, `doctype`, and `request` keyword arguments. + ''' + super(document, self).__init__() + self.doctype = doctype + self.head = super(document, self).add(tags.head()) + self.body = super(document, self).add(tags.body()) + self.title_node = self.head.add(tags.title(title)) + self._entry = self.body + + def get_title(self): + return self.title_node.text + + def set_title(self, title): + if isinstance(title, basestring): + self.title_node.text = title + else: + self.head.remove(self.title_node) + self.head.add(title) + self.title_node = title + + title = property(get_title, set_title) + + def add(self, *args): + ''' + Adding tags to a document appends them to the . + ''' + return self._entry.add(*args) + + def render(self, *args, **kwargs): + ''' + Creates a tag if not present and renders the DOCTYPE and tag tree. + ''' + r = [] + + #Validates the tag tree and adds the doctype if one was set + if self.doctype: + r.append(self.doctype) + r.append('\n') + r.append(super(document, self).render(*args, **kwargs)) + + return u''.join(r) + __str__ = __unicode__ = render + + def __repr__(self): + return '<dominate.document "%s">' % self.title diff --git a/venv/Lib/site-packages/dominate/dom1core.py b/venv/Lib/site-packages/dominate/dom1core.py new file mode 100644 index 0000000..5045784 --- /dev/null +++ b/venv/Lib/site-packages/dominate/dom1core.py @@ -0,0 +1,68 @@ +__license__ = ''' +This file is part of Dominate. + +Dominate is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +Dominate is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General +Public License along with Dominate. If not, see +<http://www.gnu.org/licenses/>. +''' + +try: + basestring = basestring +except NameError: # py3 + basestring = str + unicode = str + + +class dom1core(object): + ''' + Implements the Document Object Model (Core) Level 1 + + http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ + http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html + ''' + @property + def parentNode(self): + ''' + DOM API: Returns the parent tag of the current element. + ''' + return self.parent + + def getElementById(self, id): + ''' + DOM API: Returns single element with matching id value. + ''' + results = self.get(id=id) + if len(results) > 1: + raise ValueError('Multiple tags with id "%s".' % id) + elif results: + return results[0] + else: + return None + + def getElementsByTagName(self, name): + ''' + DOM API: Returns all tags that match name. + ''' + if isinstance(name, basestring): + return self.get(name.lower()) + else: + return None + + def appendChild(self, obj): + ''' + DOM API: Add an item to the end of the children list. + ''' + self.add(obj) + return self + + diff --git a/venv/Lib/site-packages/dominate/dom_tag.py b/venv/Lib/site-packages/dominate/dom_tag.py new file mode 100644 index 0000000..ca9becc --- /dev/null +++ b/venv/Lib/site-packages/dominate/dom_tag.py @@ -0,0 +1,449 @@ +__license__ = ''' +This file is part of Dominate. + +Dominate is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +Dominate is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General +Public License along with Dominate. If not, see +<http://www.gnu.org/licenses/>. +''' + +# pylint: disable=bad-indentation, bad-whitespace, missing-docstring + +import copy +import numbers +from collections import defaultdict, namedtuple +from functools import wraps +import threading + +try: + # Python 3 + from collections.abc import Callable +except ImportError: + # Python 2.7 + from collections import Callable + +try: + basestring = basestring +except NameError: # py3 + basestring = str + unicode = str + + +try: + import greenlet +except ImportError: + greenlet = None + +def _get_thread_context(): + context = [threading.current_thread()] + if greenlet: + context.append(greenlet.getcurrent()) + return hash(tuple(context)) + + +class dom_tag(object): + is_single = False # Tag does not require matching end tag (ex. <hr/>) + is_pretty = True # Text inside the tag should be left as-is (ex. <pre>) + # otherwise, text will be escaped() and whitespace may be + # modified + is_inline = False + + frame = namedtuple('frame', ['tag', 'items', 'used']) + + def __new__(_cls, *args, **kwargs): + ''' + Check if bare tag is being used a a decorator. + decorate the function and return + ''' + if len(args) == 1 and isinstance(args[0], Callable) \ + and not isinstance(args[0], dom_tag) and not kwargs: + wrapped = args[0] + + @wraps(wrapped) + def f(*args, **kwargs): + with _cls() as _tag: + return wrapped(*args, **kwargs) or _tag + return f + return object.__new__(_cls) + + def __init__(self, *args, **kwargs): + ''' + Creates a new tag. Child tags should be passed as aruguments and attributes + should be passed as keyword arguments. + + There is a non-rendering attribute which controls how the tag renders: + + * `__inline` - Boolean value. If True renders all children tags on the same + line. + ''' + + self.attributes = {} + self.children = [] + self.parent = None + self.document = None + + # Does not insert newlines on all children if True (recursive attribute) + self.is_inline = kwargs.pop('__inline', self.is_inline) + self.is_pretty = kwargs.pop('__pretty', self.is_pretty) + + #Add child elements + if args: + self.add(*args) + + for attr, value in kwargs.items(): + self.set_attribute(*dom_tag.clean_pair(attr, value)) + + self._ctx = None + self._add_to_ctx() + + def _add_to_ctx(self): + ctx = dom_tag._with_contexts[_get_thread_context()] + if ctx and ctx[-1]: + self._ctx = ctx[-1] + ctx[-1].items.append(self) + + # stack of (root_tag, [new_tags], set(used_tags)) + _with_contexts = defaultdict(list) + + def __enter__(self): + ctx = dom_tag._with_contexts[_get_thread_context()] + ctx.append(dom_tag.frame(self, [], set())) + return self + + def __exit__(self, type, value, traceback): + ctx = dom_tag._with_contexts[_get_thread_context()] + slf, items, used = ctx[-1] + ctx[-1] = None + for item in items: + if item in used: continue + self.add(item) + ctx.pop() + + def __call__(self, func): + ''' + tag instance is being used as a decorator. + wrap func to make a copy of this tag + ''' + # remove decorator from its context so it doesn't + # get added in where it was defined + if self._ctx: + self._ctx.used.add(self) + + @wraps(func) + def f(*args, **kwargs): + tag = copy.deepcopy(self) + tag._add_to_ctx() + with tag: + return func(*args, **kwargs) or tag + return f + + def set_attribute(self, key, value): + ''' + Add or update the value of an attribute. + ''' + if isinstance(key, int): + self.children[key] = value + elif isinstance(key, basestring): + self.attributes[key] = value + else: + raise TypeError('Only integer and string types are valid for assigning ' + 'child tags and attributes, respectively.') + __setitem__ = set_attribute + + def delete_attribute(self, key): + if isinstance(key, int): + del self.children[key:key+1] + else: + del self.attributes[key] + __delitem__ = delete_attribute + + def setdocument(self, doc): + ''' + Creates a reference to the parent document to allow for partial-tree + validation. + ''' + # assume that a document is correct in the subtree + if self.document != doc: + self.document = doc + for i in self.children: + if not isinstance(i, dom_tag): return + i.setdocument(doc) + + def add(self, *args): + ''' + Add new child tags. + ''' + for obj in args: + if isinstance(obj, numbers.Number): + # Convert to string so we fall into next if block + obj = str(obj) + + if isinstance(obj, basestring): + obj = escape(obj) + self.children.append(obj) + + elif isinstance(obj, dom_tag): + ctx = dom_tag._with_contexts[_get_thread_context()] + if ctx and ctx[-1]: + ctx[-1].used.add(obj) + self.children.append(obj) + obj.parent = self + obj.setdocument(self.document) + + elif isinstance(obj, dict): + for attr, value in obj.items(): + self.set_attribute(*dom_tag.clean_pair(attr, value)) + + elif hasattr(obj, '__iter__'): + for subobj in obj: + self.add(subobj) + + else: # wtf is it? + raise ValueError('%r not a tag or string.' % obj) + + if len(args) == 1: + return args[0] + + return args + + def add_raw_string(self, s): + self.children.append(s) + + def remove(self, obj): + self.children.remove(obj) + + def clear(self): + for i in self.children: + if isinstance(i, dom_tag) and i.parent is self: + i.parent = None + self.children = [] + + def get(self, tag=None, **kwargs): + ''' + Recursively searches children for tags of a certain + type with matching attributes. + ''' + # Stupid workaround since we can not use dom_tag in the method declaration + if tag is None: tag = dom_tag + + attrs = [(dom_tag.clean_attribute(attr), value) + for attr, value in kwargs.items()] + + results = [] + for child in self.children: + if (isinstance(tag, basestring) and type(child).__name__ == tag) or \ + (not isinstance(tag, basestring) and isinstance(child, tag)): + + if all(child.attributes.get(attribute) == value + for attribute, value in attrs): + # If the child is of correct type and has all attributes and values + # in kwargs add as a result + results.append(child) + if isinstance(child, dom_tag): + # If the child is a dom_tag extend the search down through its children + results.extend(child.get(tag, **kwargs)) + return results + + def __getitem__(self, key): + ''' + Returns the stored value of the specified attribute or child + (if it exists). + ''' + if isinstance(key, int): + # Children are accessed using integers + try: + return object.__getattribute__(self, 'children')[key] + except KeyError: + raise IndexError('Child with index "%s" does not exist.' % key) + elif isinstance(key, basestring): + # Attributes are accessed using strings + try: + return object.__getattribute__(self, 'attributes')[key] + except KeyError: + raise AttributeError('Attribute "%s" does not exist.' % key) + else: + raise TypeError('Only integer and string types are valid for accessing ' + 'child tags and attributes, respectively.') + __getattr__ = __getitem__ + + def __len__(self): + ''' + Number of child elements. + ''' + return len(self.children) + + def __bool__(self): + ''' + Hack for "if x" and __len__ + ''' + return True + __nonzero__ = __bool__ + + def __iter__(self): + ''' + Iterates over child elements. + ''' + return self.children.__iter__() + + def __contains__(self, item): + ''' + Checks recursively if item is in children tree. + Accepts both a string and a class. + ''' + return bool(self.get(item)) + + def __iadd__(self, obj): + ''' + Reflexive binary addition simply adds tag as a child. + ''' + self.add(obj) + return self + + # String and unicode representations are the same as render() + def __unicode__(self): + return self.render() + __str__ = __unicode__ + + def render(self, indent=' ', pretty=True, xhtml=False): + data = self._render([], 0, indent, pretty, xhtml) + return u''.join(data) + + def _render(self, sb, indent_level, indent_str, pretty, xhtml): + pretty = pretty and self.is_pretty + + t = type(self) + name = getattr(t, 'tagname', t.__name__) + + # Workaround for python keywords and standard classes/methods + # (del, object, input) + if name[-1] == '_': + name = name[:-1] + + # open tag + sb.append('<') + sb.append(name) + + for attribute, value in sorted(self.attributes.items()): + sb.append(' %s="%s"' % (attribute, escape(unicode(value), True))) + + sb.append(' />' if self.is_single and xhtml else '>') + + if not self.is_single: + inline = self._render_children(sb, indent_level + 1, indent_str, pretty, xhtml) + + if pretty and not inline: + sb.append('\n') + sb.append(indent_str * indent_level) + + # close tag + sb.append('</') + sb.append(name) + sb.append('>') + + return sb + + def _render_children(self, sb, indent_level, indent_str, pretty, xhtml): + inline = True + for child in self.children: + if isinstance(child, dom_tag): + if pretty and not child.is_inline: + inline = False + sb.append('\n') + sb.append(indent_str * indent_level) + child._render(sb, indent_level, indent_str, pretty, xhtml) + else: + sb.append(unicode(child)) + + return inline + + def __repr__(self): + name = '%s.%s' % (self.__module__, type(self).__name__) + + attributes_len = len(self.attributes) + attributes = '%s attribute' % attributes_len + if attributes_len != 1: attributes += 's' + + children_len = len(self.children) + children = '%s child' % children_len + if children_len != 1: children += 'ren' + + return '<%s at %x: %s, %s>' % (name, id(self), attributes, children) + + @staticmethod + def clean_attribute(attribute): + ''' + Normalize attribute names for shorthand and work arounds for limitations + in Python's syntax + ''' + + # Shorthand + attribute = { + 'cls': 'class', + 'className': 'class', + 'class_name': 'class', + 'fr': 'for', + 'html_for': 'for', + 'htmlFor': 'for', + }.get(attribute, attribute) + + # Workaround for Python's reserved words + if attribute[0] == '_': + attribute = attribute[1:] + + # Workaround for dash + if attribute in set(['http_equiv']) or attribute.startswith('data_'): + attribute = attribute.replace('_', '-').lower() + + # Workaround for colon + if attribute.split('_')[0] in ('xlink', 'xml', 'xmlns'): + attribute = attribute.replace('_', ':', 1).lower() + + return attribute + + + @classmethod + def clean_pair(cls, attribute, value): + ''' + This will call `clean_attribute` on the attribute and also allows for the + creation of boolean attributes. + + Ex. input(selected=True) is equivalent to input(selected="selected") + ''' + attribute = cls.clean_attribute(attribute) + + # Check for boolean attributes + # (i.e. selected=True becomes selected="selected") + if value is True: + value = attribute + + if value is False: + value = "false" + + return (attribute, value) + + +def attr(*args, **kwargs): + ''' + Set attributes on the current active tag context + ''' + ctx = dom_tag._with_contexts[_get_thread_context()] + if ctx and ctx[-1]: + dicts = args + (kwargs,) + for d in dicts: + for attr, value in d.items(): + ctx[-1].tag.set_attribute(*dom_tag.clean_pair(attr, value)) + else: + raise ValueError('not in a tag context') + + +# escape() is used in render +from .util import escape diff --git a/venv/Lib/site-packages/dominate/tags.py b/venv/Lib/site-packages/dominate/tags.py new file mode 100644 index 0000000..3d09192 --- /dev/null +++ b/venv/Lib/site-packages/dominate/tags.py @@ -0,0 +1,1038 @@ +''' +HTML tag classes. +''' +__license__ = ''' +This file is part of Dominate. + +Dominate is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +Dominate is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General +Public License along with Dominate. If not, see +<http://www.gnu.org/licenses/>. +''' +from .dom_tag import dom_tag, attr +from .dom1core import dom1core + +try: + basestring = basestring +except NameError: # py3 + basestring = str + unicode = str + +underscored_classes = set(['del', 'input', 'map', 'object']) + +# Tag attributes +_ATTR_GLOBAL = set([ + 'accesskey', 'class', 'class', 'contenteditable', 'contextmenu', 'dir', + 'draggable', 'id', 'item', 'hidden', 'lang', 'itemprop', 'spellcheck', + 'style', 'subject', 'tabindex', 'title' +]) +_ATTR_EVENTS = set([ + 'onabort', 'onblur', 'oncanplay', 'oncanplaythrough', 'onchange', 'onclick', + 'oncontextmenu', 'ondblclick', 'ondrag', 'ondragend', 'ondragenter', + 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'ondurationchange', + 'onemptied', 'onended', 'onerror', 'onfocus', 'onformchange', 'onforminput', + 'oninput', 'oninvalid', 'onkeydown', 'onkeypress', 'onkeyup', 'onload', + 'onloadeddata', 'onloadedmetadata', 'onloadstart', 'onmousedown', + 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', + 'onpause', 'onplay', 'onplaying', 'onprogress', 'onratechange', + 'onreadystatechange', 'onscroll', 'onseeked', 'onseeking', 'onselect', + 'onshow', 'onstalled', 'onsubmit', 'onsuspend', 'ontimeupdate', + 'onvolumechange', 'onwaiting' +]) + + +ERR_ATTRIBUTE = 'attributes' +ERR_CONTEXT = 'context' +ERR_CONTENT = 'content' + + +class html_tag(dom_tag, dom1core): + def __init__(self, *args, **kwargs): + ''' + Creates a new html tag instance. + ''' + super(html_tag, self).__init__(*args, **kwargs) + + + # def validate(self): + # ''' + # Validate the tag. This will check the attributes, context, and contents and + # emit tuples in the form of: element, message. + # ''' + # errors = [] + + # errors.extend(self.validate_attributes()) + # errors.extend(self.validate_context()) + # errors.extend(self.validate_content()) + + # return errors + + # def validate_attributes(self): + # ''' + # Validate the tag attributes. + # ''' + # return [] + + # def validate_context(self): + # ''' + # Validate the tag context. + # ''' + # return [] + + # def validate_content(self): + # ''' + # Validate the content of the tag. + # ''' + # return [] + + # def _check_attributes(self, *attrs): + # valid = set([]) + # for attr in attrs: + # if hasattr(attr, '__iter__'): + # valid |= set(attr) + # else: + # valid.add(attr) + # return set(list(self.attributes.iterkeys())) - valid + + + +################################################################################ +############################### Html Tag Classes ############################### +################################################################################ + +# Root element + +class html(html_tag): + ''' + The html element represents the root of an HTML document. + ''' + pass + # def validate_attributes(self): + # errors = [] + # for invalid in self._check_attributes(_ATTR_GLOBAL, 'manifest'): + # errors.append( (self, ERR_ATTRIBUTE, 'Invalid attribute: "%s"' % invalid) ) + # return errors + + # def validate_context(self): + # if self.parent is not None and not isinstance(self.parent, iframe): + # return [(self, ERR_CONTEXT, 'Must be root element or child of an <iframe>')] + # return [] + + # def validate_content(self): + # if len(self) != 2 or not isinstance(self[0], head) or not isinstance(self[1], body): + # return [(self, ERR_CONTENT, 'Children must be <head> and then <body>.')] + # return [] + + + +# Document metadata + +class head(html_tag): + ''' + The head element represents a collection of metadata for the document. + ''' + pass + +class title(html_tag): + ''' + The title element represents the document's title or name. Authors should use + titles that identify their documents even when they are used out of context, + for example in a user's history or bookmarks, or in search results. The + document's title is often different from its first heading, since the first + heading does not have to stand alone when taken out of context. + ''' + def _get_text(self): + return u''.join(self.get(basestring)) + def _set_text(self, text): + self.clear() + self.add(text) + text = property(_get_text, _set_text) + +class base(html_tag): + ''' + The base element allows authors to specify the document base URL for the + purposes of resolving relative URLs, and the name of the default browsing + context for the purposes of following hyperlinks. The element does not + represent any content beyond this information. + ''' + is_single = True + +class link(html_tag): + ''' + The link element allows authors to link their document to other resources. + ''' + is_single = True + +class meta(html_tag): + ''' + The meta element represents various kinds of metadata that cannot be + expressed using the title, base, link, style, and script elements. + ''' + is_single = True + +class style(html_tag): + ''' + The style element allows authors to embed style information in their + documents. The style element is one of several inputs to the styling + processing model. The element does not represent content for the user. + ''' + is_pretty = False + + +# Scripting + +class script(html_tag): + ''' + The script element allows authors to include dynamic script and data blocks + in their documents. The element does not represent content for the user. + ''' + is_pretty = False + +class noscript(html_tag): + ''' + The noscript element represents nothing if scripting is enabled, and + represents its children if scripting is disabled. It is used to present + different markup to user agents that support scripting and those that don't + support scripting, by affecting how the document is parsed. + ''' + pass + + +# Sections + +class body(html_tag): + ''' + The body element represents the main content of the document. + ''' + pass + +class section(html_tag): + ''' + The section element represents a generic section of a document or + application. A section, in this context, is a thematic grouping of content, + typically with a heading. + ''' + pass + +class nav(html_tag): + ''' + The nav element represents a section of a page that links to other pages or + to parts within the page: a section with navigation links. + ''' + pass + +class article(html_tag): + ''' + The article element represents a self-contained composition in a document, + page, application, or site and that is, in principle, independently + distributable or reusable, e.g. in syndication. This could be a forum post, a + magazine or newspaper article, a blog entry, a user-submitted comment, an + interactive widget or gadget, or any other independent item of content. + ''' + pass + +class aside(html_tag): + ''' + The aside element represents a section of a page that consists of content + that is tangentially related to the content around the aside element, and + which could be considered separate from that content. Such sections are + often represented as sidebars in printed typography. + ''' + pass + +class h1(html_tag): + ''' + Represents the highest ranking heading. + ''' + pass + +class h2(html_tag): + ''' + Represents the second-highest ranking heading. + ''' + pass + +class h3(html_tag): + ''' + Represents the third-highest ranking heading. + ''' + pass + +class h4(html_tag): + ''' + Represents the fourth-highest ranking heading. + ''' + pass + +class h5(html_tag): + ''' + Represents the fifth-highest ranking heading. + ''' + pass + +class h6(html_tag): + ''' + Represents the sixth-highest ranking heading. + ''' + pass + +class hgroup(html_tag): + ''' + The hgroup element represents the heading of a section. The element is used + to group a set of h1-h6 elements when the heading has multiple levels, such + as subheadings, alternative titles, or taglines. + ''' + pass + +class header(html_tag): + ''' + The header element represents a group of introductory or navigational aids. + ''' + pass + +class footer(html_tag): + ''' + The footer element represents a footer for its nearest ancestor sectioning + content or sectioning root element. A footer typically contains information + about its section such as who wrote it, links to related documents, + copyright data, and the like. + ''' + pass + +class address(html_tag): + ''' + The address element represents the contact information for its nearest + article or body element ancestor. If that is the body element, then the + contact information applies to the document as a whole. + ''' + pass + + +# Grouping content + +class p(html_tag): + ''' + The p element represents a paragraph. + ''' + pass + +class hr(html_tag): + ''' + The hr element represents a paragraph-level thematic break, e.g. a scene + change in a story, or a transition to another topic within a section of a + reference book. + ''' + is_single = True + +class pre(html_tag): + ''' + The pre element represents a block of preformatted text, in which structure + is represented by typographic conventions rather than by elements. + ''' + is_pretty = False + +class blockquote(html_tag): + ''' + The blockquote element represents a section that is quoted from another + source. + ''' + pass + +class ol(html_tag): + ''' + The ol element represents a list of items, where the items have been + intentionally ordered, such that changing the order would change the + meaning of the document. + ''' + pass + +class ul(html_tag): + ''' + The ul element represents a list of items, where the order of the items is + not important - that is, where changing the order would not materially change + the meaning of the document. + ''' + pass + +class li(html_tag): + ''' + The li element represents a list item. If its parent element is an ol, ul, or + menu element, then the element is an item of the parent element's list, as + defined for those elements. Otherwise, the list item has no defined + list-related relationship to any other li element. + ''' + pass + +class dl(html_tag): + ''' + The dl element represents an association list consisting of zero or more + name-value groups (a description list). Each group must consist of one or + more names (dt elements) followed by one or more values (dd elements). + Within a single dl element, there should not be more than one dt element for + each name. + ''' + pass + +class dt(html_tag): + ''' + The dt element represents the term, or name, part of a term-description group + in a description list (dl element). + ''' + pass + +class dd(html_tag): + ''' + The dd element represents the description, definition, or value, part of a + term-description group in a description list (dl element). + ''' + pass + +class figure(html_tag): + ''' + The figure element represents some flow content, optionally with a caption, + that is self-contained and is typically referenced as a single unit from the + main flow of the document. + ''' + pass + +class figcaption(html_tag): + ''' + The figcaption element represents a caption or legend for the rest of the + contents of the figcaption element's parent figure element, if any. + ''' + pass + +class div(html_tag): + ''' + The div element has no special meaning at all. It represents its children. It + can be used with the class, lang, and title attributes to mark up semantics + common to a group of consecutive elements. + ''' + pass + + + +# Text semantics + +class a(html_tag): + ''' + If the a element has an href attribute, then it represents a hyperlink (a + hypertext anchor). + + If the a element has no href attribute, then the element represents a + placeholder for where a link might otherwise have been placed, if it had been + relevant. + ''' + pass + +class em(html_tag): + ''' + The em element represents stress emphasis of its contents. + ''' + pass + +class strong(html_tag): + ''' + The strong element represents strong importance for its contents. + ''' + pass + +class small(html_tag): + ''' + The small element represents side comments such as small print. + ''' + pass + +class s(html_tag): + ''' + The s element represents contents that are no longer accurate or no longer + relevant. + ''' + pass + +class cite(html_tag): + ''' + The cite element represents the title of a work (e.g. a book, a paper, an + essay, a poem, a score, a song, a script, a film, a TV show, a game, a + sculpture, a painting, a theatre production, a play, an opera, a musical, an + exhibition, a legal case report, etc). This can be a work that is being + quoted or referenced in detail (i.e. a citation), or it can just be a work + that is mentioned in passing. + ''' + pass + +class q(html_tag): + ''' + The q element represents some phrasing content quoted from another source. + ''' + pass + +class dfn(html_tag): + ''' + The dfn element represents the defining instance of a term. The paragraph, + description list group, or section that is the nearest ancestor of the dfn + element must also contain the definition(s) for the term given by the dfn + element. + ''' + pass + +class abbr(html_tag): + ''' + The abbr element represents an abbreviation or acronym, optionally with its + expansion. The title attribute may be used to provide an expansion of the + abbreviation. The attribute, if specified, must contain an expansion of the + abbreviation, and nothing else. + ''' + pass + +class time_(html_tag): + ''' + The time element represents either a time on a 24 hour clock, or a precise + date in the proleptic Gregorian calendar, optionally with a time and a + time-zone offset. + ''' + pass +_time = time_ + +class code(html_tag): + ''' + The code element represents a fragment of computer code. This could be an XML + element name, a filename, a computer program, or any other string that a + computer would recognize. + ''' + pass + +class var(html_tag): + ''' + The var element represents a variable. This could be an actual variable in a + mathematical expression or programming context, an identifier representing a + constant, a function parameter, or just be a term used as a placeholder in + prose. + ''' + pass + +class samp(html_tag): + ''' + The samp element represents (sample) output from a program or computing + system. + ''' + pass + +class kbd(html_tag): + ''' + The kbd element represents user input (typically keyboard input, although it + may also be used to represent other input, such as voice commands). + ''' + pass + +class sub(html_tag): + ''' + The sub element represents a subscript. + ''' + pass + +class sup(html_tag): + ''' + The sup element represents a superscript. + ''' + pass + +class i(html_tag): + ''' + The i element represents a span of text in an alternate voice or mood, or + otherwise offset from the normal prose in a manner indicating a different + quality of text, such as a taxonomic designation, a technical term, an + idiomatic phrase from another language, a thought, or a ship name in Western + texts. + ''' + pass + +class b(html_tag): + ''' + The b element represents a span of text to which attention is being drawn for + utilitarian purposes without conveying any extra importance and with no + implication of an alternate voice or mood, such as key words in a document + abstract, product names in a review, actionable words in interactive + text-driven software, or an article lede. + ''' + pass + +class u(html_tag): + ''' + The u element represents a span of text with an unarticulated, though + explicitly rendered, non-textual annotation, such as labeling the text as + being a proper name in Chinese text (a Chinese proper name mark), or + labeling the text as being misspelt. + ''' + pass + +class mark(html_tag): + ''' + The mark element represents a run of text in one document marked or + highlighted for reference purposes, due to its relevance in another context. + When used in a quotation or other block of text referred to from the prose, + it indicates a highlight that was not originally present but which has been + added to bring the reader's attention to a part of the text that might not + have been considered important by the original author when the block was + originally written, but which is now under previously unexpected scrutiny. + When used in the main prose of a document, it indicates a part of the + document that has been highlighted due to its likely relevance to the user's + current activity. + ''' + pass + +class ruby(html_tag): + ''' + The ruby element allows one or more spans of phrasing content to be marked + with ruby annotations. Ruby annotations are short runs of text presented + alongside base text, primarily used in East Asian typography as a guide for + pronunciation or to include other annotations. In Japanese, this form of + typography is also known as furigana. + ''' + pass + +class rt(html_tag): + ''' + The rt element marks the ruby text component of a ruby annotation. + ''' + pass + +class rp(html_tag): + ''' + The rp element can be used to provide parentheses around a ruby text + component of a ruby annotation, to be shown by user agents that don't support + ruby annotations. + ''' + pass + +class bdi(html_tag): + ''' + The bdi element represents a span of text that is to be isolated from its + surroundings for the purposes of bidirectional text formatting. + ''' + pass + +class bdo(html_tag): + ''' + The bdo element represents explicit text directionality formatting control + for its children. It allows authors to override the Unicode bidirectional + algorithm by explicitly specifying a direction override. + ''' + pass + +class span(html_tag): + ''' + The span element doesn't mean anything on its own, but can be useful when + used together with the global attributes, e.g. class, lang, or dir. It + represents its children. + ''' + pass + +class br(html_tag): + ''' + The br element represents a line break. + ''' + is_single = True + is_inline = True + +class wbr(html_tag): + ''' + The wbr element represents a line break opportunity. + ''' + is_single = True + is_inline = True + +# Edits + +class ins(html_tag): + ''' + The ins element represents an addition to the document. + ''' + pass + +class del_(html_tag): + ''' + The del element represents a removal from the document. + ''' + pass + + +# Embedded content + +class img(html_tag): + ''' + An img element represents an image. + ''' + is_single = True + +class iframe(html_tag): + ''' + The iframe element represents a nested browsing context. + ''' + pass + +class embed(html_tag): + ''' + The embed element represents an integration point for an external (typically + non-HTML) application or interactive content. + ''' + is_single = True + +class object_(html_tag): + ''' + The object element can represent an external resource, which, depending on + the type of the resource, will either be treated as an image, as a nested + browsing context, or as an external resource to be processed by a plugin. + ''' + pass +_object = object_ + +class param(html_tag): + ''' + The param element defines parameters for plugins invoked by object elements. + It does not represent anything on its own. + ''' + is_single = True + +class video(html_tag): + ''' + A video element is used for playing videos or movies, and audio files with + captions. + ''' + pass + +class audio(html_tag): + ''' + An audio element represents a sound or audio stream. + ''' + pass + +class source(html_tag): + ''' + The source element allows authors to specify multiple alternative media + resources for media elements. It does not represent anything on its own. + ''' + is_single = True + +class track(html_tag): + ''' + The track element allows authors to specify explicit external timed text + tracks for media elements. It does not represent anything on its own. + ''' + is_single = True + +class canvas(html_tag): + ''' + The canvas element provides scripts with a resolution-dependent bitmap + canvas, which can be used for rendering graphs, game graphics, or other + visual images on the fly. + ''' + pass + +class map_(html_tag): + ''' + The map element, in conjunction with any area element descendants, defines an + image map. The element represents its children. + ''' + pass + +class area(html_tag): + ''' + The area element represents either a hyperlink with some text and a + corresponding area on an image map, or a dead area on an image map. + ''' + is_single = True + + + +# Tabular data + +class table(html_tag): + ''' + The table element represents data with more than one dimension, in the form + of a table. + ''' + pass + +class caption(html_tag): + ''' + The caption element represents the title of the table that is its parent, if + it has a parent and that is a table element. + ''' + pass + +class colgroup(html_tag): + ''' + The colgroup element represents a group of one or more columns in the table + that is its parent, if it has a parent and that is a table element. + ''' + pass + +class col(html_tag): + ''' + If a col element has a parent and that is a colgroup element that itself has + a parent that is a table element, then the col element represents one or more + columns in the column group represented by that colgroup. + ''' + is_single = True + +class tbody(html_tag): + ''' + The tbody element represents a block of rows that consist of a body of data + for the parent table element, if the tbody element has a parent and it is a + table. + ''' + pass + +class thead(html_tag): + ''' + The thead element represents the block of rows that consist of the column + labels (headers) for the parent table element, if the thead element has a + parent and it is a table. + ''' + pass + +class tfoot(html_tag): + ''' + The tfoot element represents the block of rows that consist of the column + summaries (footers) for the parent table element, if the tfoot element has a + parent and it is a table. + ''' + pass + +class tr(html_tag): + ''' + The tr element represents a row of cells in a table. + ''' + pass + +class td(html_tag): + ''' + The td element represents a data cell in a table. + ''' + pass + +class th(html_tag): + ''' + The th element represents a header cell in a table. + ''' + pass + + + +# Forms + +class form(html_tag): + ''' + The form element represents a collection of form-associated elements, some of + which can represent editable values that can be submitted to a server for + processing. + ''' + pass + +class fieldset(html_tag): + ''' + The fieldset element represents a set of form controls optionally grouped + under a common name. + ''' + pass + +class legend(html_tag): + ''' + The legend element represents a caption for the rest of the contents of the + legend element's parent fieldset element, if any. + ''' + pass + +class label(html_tag): + ''' + The label represents a caption in a user interface. The caption can be + associated with a specific form control, known as the label element's labeled + control, either using for attribute, or by putting the form control inside + the label element itself. + ''' + pass + +class input_(html_tag): + ''' + The input element represents a typed data field, usually with a form control + to allow the user to edit the data. + ''' + is_single = True +input = _input = input_ + +class button(html_tag): + ''' + The button element represents a button. If the element is not disabled, then + the user agent should allow the user to activate the button. + ''' + pass + +class select(html_tag): + ''' + The select element represents a control for selecting amongst a set of + options. + ''' + pass + +class datalist(html_tag): + ''' + The datalist element represents a set of option elements that represent + predefined options for other controls. The contents of the element represents + fallback content for legacy user agents, intermixed with option elements that + represent the predefined options. In the rendering, the datalist element + represents nothing and it, along with its children, should be hidden. + ''' + pass + +class optgroup(html_tag): + ''' + The optgroup element represents a group of option elements with a common + label. + ''' + pass + +class option(html_tag): + ''' + The option element represents an option in a select element or as part of a + list of suggestions in a datalist element. + ''' + pass + +class textarea(html_tag): + ''' + The textarea element represents a multiline plain text edit control for the + element's raw value. The contents of the control represent the control's + default value. + ''' + pass + +class keygen(html_tag): + ''' + The keygen element represents a key pair generator control. When the + control's form is submitted, the private key is stored in the local keystore, + and the public key is packaged and sent to the server. + ''' + is_single = True + +class output(html_tag): + ''' + The output element represents the result of a calculation. + ''' + pass + +class progress(html_tag): + ''' + The progress element represents the completion progress of a task. The + progress is either indeterminate, indicating that progress is being made but + that it is not clear how much more work remains to be done before the task is + complete (e.g. because the task is waiting for a remote host to respond), or + the progress is a number in the range zero to a maximum, giving the fraction + of work that has so far been completed. + ''' + pass + +class meter(html_tag): + ''' + The meter element represents a scalar measurement within a known range, or a + fractional value; for example disk usage, the relevance of a query result, or + the fraction of a voting population to have selected a particular candidate. + ''' + pass + + +# Interactive elements + +class details(html_tag): + ''' + The details element represents a disclosure widget from which the user can + obtain additional information or controls. + ''' + pass + +class summary(html_tag): + ''' + The summary element represents a summary, caption, or legend for the rest of + the contents of the summary element's parent details element, if any. + ''' + pass + +class command(html_tag): + ''' + The command element represents a command that the user can invoke. + ''' + is_single = True + +class menu(html_tag): + ''' + The menu element represents a list of commands. + ''' + pass + +class font(html_tag): + ''' + The font element represents the font in a html . + ''' + pass + + +# Additional markup + +class comment(html_tag): + ''' + Normal, one-line comment: + >>> print comment("Hello, comments!") + <!--Hello, comments!--> + + For IE's "if" statement comments: + >>> print comment(p("Upgrade your browser."), condition='lt IE6') + <!--[if lt IE6]><p>Upgrade your browser.</p><![endif]--> + + Downlevel conditional comments: + >>> print comment(p("You are using a ", em("downlevel"), " browser."), + condition='false', downlevel='revealed') + <![if false]><p>You are using a <em>downlevel</em> browser.</p><![endif]> + + For more on conditional comments see: + http://msdn.microsoft.com/en-us/library/ms537512(VS.85).aspx + ''' + + ATTRIBUTE_CONDITION = 'condition' + + # Valid values are 'hidden', 'downlevel' or 'revealed' + ATTRIBUTE_DOWNLEVEL = 'downlevel' + + def _render(self, sb, indent_level=1, indent_str=' ', pretty=True, xhtml=False): + has_condition = comment.ATTRIBUTE_CONDITION in self.attributes + is_revealed = comment.ATTRIBUTE_DOWNLEVEL in self.attributes and \ + self.attributes[comment.ATTRIBUTE_DOWNLEVEL] == 'revealed' + + sb.append('<!') + if not is_revealed: + sb.append('--') + if has_condition: + sb.append('[if %s]>' % self.attributes[comment.ATTRIBUTE_CONDITION]) + + pretty = self._render_children(sb, indent_level - 1, indent_str, pretty, xhtml) + + # if len(self.children) > 1: + if any(isinstance(child, dom_tag) for child in self): + sb.append('\n') + sb.append(indent_str * (indent_level - 1)) + + if has_condition: + sb.append('<![endif]') + if not is_revealed: + sb.append('--') + sb.append('>') + + return sb diff --git a/venv/Lib/site-packages/dominate/util.py b/venv/Lib/site-packages/dominate/util.py new file mode 100644 index 0000000..06a3c11 --- /dev/null +++ b/venv/Lib/site-packages/dominate/util.py @@ -0,0 +1,170 @@ +''' +Utility classes for creating dynamic html documents +''' + +__license__ = ''' +This file is part of Dominate. + +Dominate is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +Dominate is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General +Public License along with Dominate. If not, see +<http://www.gnu.org/licenses/>. +''' + +import re +from .dom_tag import dom_tag + + +try: + basestring = basestring +except NameError: + basestring = str + unichr = chr + +def include(f): + ''' + includes the contents of a file on disk. + takes a filename + ''' + fl = open(f, 'r') + data = fl.read() + fl.close() + return raw(data) + + +def system(cmd, data=None): + ''' + pipes the output of a program + ''' + import subprocess + s = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) + out, err = s.communicate(data) + return out.decode('utf8') + + +def escape(data, quote=True): # stoled from std lib cgi + ''' + Escapes special characters into their html entities + Replace special characters "&", "<" and ">" to HTML-safe sequences. + If the optional flag quote is true, the quotation mark character (") + is also translated. + + This is used to escape content that appears in the body of an HTML cocument + ''' + data = data.replace("&", "&") # Must be done first! + data = data.replace("<", "<") + data = data.replace(">", ">") + if quote: + data = data.replace('"', """) + return data + + +_unescape = { + 'quot': 34, + 'amp': 38, + 'lt': 60, + 'gt': 62, + 'nbsp': 32, + # more here + # http://www.w3.org/TR/html4/sgml/entities.html + 'yuml': 255, +} +str_escape = escape + +def unescape(data): + ''' + unescapes html entities. the opposite of escape. + ''' + cc = re.compile(r'&(?:(?:#(\d+))|([^;]+));') + + result = [] + m = cc.search(data) + while m: + result.append(data[0:m.start()]) + d = m.group(1) + if d: + d = int(d) + result.append(unichr(d)) + else: + d = _unescape.get(m.group(2), ord('?')) + result.append(unichr(d)) + + data = data[m.end():] + m = cc.search(data) + + result.append(data) + return ''.join(result) + + +_reserved = ";/?:@&=+$, " +_replace_map = dict((c, '%%%2X' % ord(c)) for c in _reserved) + + +def url_escape(data): + return ''.join(_replace_map.get(c, c) for c in data) + + +def url_unescape(data): + return re.sub('%([0-9a-fA-F]{2})', + lambda m: unichr(int(m.group(1), 16)), data) + + +class lazy(dom_tag): + ''' + delays function execution until rendered + ''' + def __new__(_cls, *args, **kwargs): + ''' + Need to reset this special method or else + dom_tag will think it's being used as a dectorator. + + This means lazy() can't be used as a dectorator, but + thinking about when you might want that just confuses me. + ''' + return object.__new__(_cls) + + def __init__(self, func, *args, **kwargs): + super(lazy, self).__init__() + self.func = func + self.args = args + self.kwargs = kwargs + + + def _render(self, sb, *a, **kw): + r = self.func(*self.args, **self.kwargs) + sb.append(str(r)) + + +# TODO rename this to raw? +class text(dom_tag): + ''' + Just a string. useful for inside context managers + ''' + is_pretty = False + is_inline = True + + def __init__(self, _text, escape=True): + super(text, self).__init__() + if escape: + self.text = str_escape(_text) + else: + self.text = _text + + def _render(self, sb, *a, **kw): + sb.append(self.text) + return sb + +def raw(s): + ''' + Inserts a raw string into the DOM. Unsafe. + ''' + return text(s, escape=False) diff --git a/venv/Lib/site-packages/easy-install.pth b/venv/Lib/site-packages/easy-install.pth new file mode 100644 index 0000000..5b5808a --- /dev/null +++ b/venv/Lib/site-packages/easy-install.pth @@ -0,0 +1 @@ +./setuptools-28.8.0-py3.6.egg diff --git a/venv/Lib/site-packages/flask/__init__.py b/venv/Lib/site-packages/flask/__init__.py new file mode 100644 index 0000000..ded1982 --- /dev/null +++ b/venv/Lib/site-packages/flask/__init__.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +""" + flask + ~~~~~ + + A microframework based on Werkzeug. It's extensively documented + and follows best practice patterns. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +__version__ = '1.0.2' + +# utilities we import from Werkzeug and Jinja2 that are unused +# in the module but are exported as public interface. +from werkzeug.exceptions import abort +from werkzeug.utils import redirect +from jinja2 import Markup, escape + +from .app import Flask, Request, Response +from .config import Config +from .helpers import url_for, flash, send_file, send_from_directory, \ + get_flashed_messages, get_template_attribute, make_response, safe_join, \ + stream_with_context +from .globals import current_app, g, request, session, _request_ctx_stack, \ + _app_ctx_stack +from .ctx import has_request_context, has_app_context, \ + after_this_request, copy_current_request_context +from .blueprints import Blueprint +from .templating import render_template, render_template_string + +# the signals +from .signals import signals_available, template_rendered, request_started, \ + request_finished, got_request_exception, request_tearing_down, \ + appcontext_tearing_down, appcontext_pushed, \ + appcontext_popped, message_flashed, before_render_template + +# We're not exposing the actual json module but a convenient wrapper around +# it. +from . import json + +# This was the only thing that Flask used to export at one point and it had +# a more generic name. +jsonify = json.jsonify + +# backwards compat, goes away in 1.0 +from .sessions import SecureCookieSession as Session +json_available = True diff --git a/venv/Lib/site-packages/flask/__main__.py b/venv/Lib/site-packages/flask/__main__.py new file mode 100644 index 0000000..4aee654 --- /dev/null +++ b/venv/Lib/site-packages/flask/__main__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" + flask.__main__ + ~~~~~~~~~~~~~~ + + Alias for flask.run for the command line. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +if __name__ == '__main__': + from .cli import main + main(as_module=True) diff --git a/venv/Lib/site-packages/flask/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6935d2c623838f059d3faba482123338c722d53 GIT binary patch literal 1838 zcmZ8h-EI>{6kh+XV>?b9=MM-l5Fi+0lY~%cN~@MYrAP%;q*bLOSF5#mY;QcXyPX*) zF&Dk>qwoNIfj-7uwpYADuX@hL4p2+}_MC6dp8wg$^Yi8NKaadWijMP-^T{uZ^0)Zc z4>FDuIN$^s$OPFSC$cgJxr~EyUgTu~3aMNWMd^YoOHe|+7`S3imZ2;wP?1%rrd&zP z%No>Dc}^_IMOc(|sLLf-k_~9cWmuLgup(DsRj$FBT!;14wk)p5t8i6bgKKgFHsp1< zE^ojMc@u7?=M}-^Ex09b!)>_<oAM6aN%^X{EAPR*RGt@G@;=;`O=zZiO+1hf;i24y zZP|jBY(rZ<f=6-(cH}PX%01YVkKwW0hkf}3o}}+Ah%e+*c$#te)01cL469reU&;gc z3Qz0sJXkt*nvL<_*tIglUtjooluEzTubhhCF&Xw$+*iKjXR#WwuCIB(;)wmi)#x1` z588|!nJvxux#5uxPq~<|AnuJNj|>k|Cw>&Lek??MrdgM3!xH89OxWWr@r~gs((Q|A z2fa9%sBkbe2kfu+Y;R|Ghjk~+3_1JJ7lIqj4EJR_^?D%CL!|iudvW}7n`zG34@ZZu z-W<QeV2a6DaTajnhXUQdE9|y++It_cSf`n>1-~0BgF;F1AXL0(nps<X?W@r^u|=+X ze!@TCCOOTVEqqUnvF=;`+ZdC#rMFxsF$Tuhi-&R44+qUT>yDN1Fs?07ABT2M^C<B8 zq2RW0A*xt<7qMbBvC9K)ywsEjp5$8l1Fr2Vv4+b;_=bDf92IuQhTD1RkGQ9%fzi3& z=iW&iM(ErqY>9UknxWT=vFGQeS+tejSSg%<=O>BH46LiB*}EEJg>ht;&|O-P*E8py zHhynpYh?aWZ{}@d=<AP$7w+u>k*VdBSn~TgM$ZhxOJlo1v-D<ZT*jJan6Yy&#F!^4 zjHvYsm}@{Drls4}{}GrVnhu&3Ti4+r@&zVvilg<r*x>r?HO!RU@PHg%6o-W=+(WxK zBl=+!>LD^KgV<b_#?N~^G1QmknIUc(h4g~>EV64@?zFp@<R=I9PY#lC>i9D%P7+FI zI^o&G?P{0fvd~CBs~cA;f1=|EpN(UV&Eto0JPP^o#n66w)BM`H9;VduJg9iyDOWU1 zR2pZq`*>N3R`gYb+U3dE;9XETB8w+sbmH%+JSwVyU~^cAa!D-_%n?__l5xFN85MAc zTqGDzRp`7*Fi%h;SRhy=s1qy^G!U#i5OEjBrIv|WAy_3?BUmT6f&e*OLUonYYXlnv z*Xf|#ovn#hH&S9cY#fccNoI^#ce)ko7E!keHVN(!+$Fe2utjj6ph@t6;30x_)HXd_ zYJS+CKxukSv}z#_?sNc9BZH5t_uf(a<b&YfUk?vDKWpqzXR@WYCY_gDk4&6&Uf>8@ zul)#bIDHgb#}kb;w4{#@)j8$SX-9<Jjt&iPC3p?#OVCNb#vRYYZ8e_PPA0bWT*kpz k@NcO9njz$~`Fu4~%`9e@5w~+JyPicYx9?W-#auP>A3)eVJ^%m! literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/_compat.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/_compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65fc26cc89cc14bbcc808653b6b4796ccb2e0d41 GIT binary patch literal 3237 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/app.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/app.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b32c3418f7bf56a43772b58ff518700db59c3491 GIT binary patch literal 70912 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 IeOl}C-`%7Bu>b%7 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/blueprints.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/blueprints.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d18129d9601c13e0ebd6c3cf6eadbf9e4a8bceb4 GIT binary patch literal 20501 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/cli.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/cli.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c97f818c8793287fabcf93786e1b11cb5dc1649 GIT binary patch literal 24878 zcmch9d2k$8eqMJ^&xwN|NKq7*(zdXY$l(GIMQJ51mePviA#s<2xFjS>v(yd-^BTYa zbMbW#Nel<7iXcaF5=t98iH~e;Hi_)m>x%6hc5Nl)D#uCwiJi;kq>^^xRN|_nwvtrk z_$#U;&iDOZch3N%?Bt(-?4CYezxVpR-|_v9*H2GOly3jZ#i|on*6&**e-GgJC2YZk zjAbdSVkuiWO}ph(9Gl0PW~P#nU$^4QZ?=+^-&`dpzxhgDehZa?{1z)k{JPCjYpgOR zb!404t%=Hn9Os&ot$me!t*OdXT>t*cew5EQr&|Xq2XL*Rip_(q2PzN9ajAKz^<d?} z*5S(G)<cztT1P5J<oa0ibFHJ5qjEgne7N;U<&oCsE1z$970<S|#-%5ZRvx`(wGX?N zKeJiV|H-!aPh~2PzHPOgx2#tzHK8Wox74J+f8z@~naX2opPG8#syx1Gt=ek8nttD| zJfRM#gSd7~J)jQZ_qcjc9ma221?q_U97>!}N7ci)@<r4#s~%ai)#sJxe^EVze~;eH zRp!vnjQRrF`GP->R-VN9W9o67KQ8Ad@!V7XNtAx#u2VUsj;Z6Q?P-)Ms}m@70{5T6 zb6>)hFREEwnf1Sf^JmqZnn%g!RH&X*C()DV)l=#eu6!9KpH|PH<TH470cF3UzNDVT zozpmfK|QCQS6{yC;P^$gpuVC`s~6OZcU?LEs(MMCQ7@<GXZ^42Sn8ZQe>WrdFJS*w z^wq=m3a*{T(W~m>-K^aGn#!oJ<5^So63$*r&lYj^dU|#l&woSCPN^&E>igD?eeb`@ zOQ`h)wWQvV{yweVRM$+&${D%ymU=sVlZyH#uDz^!JGQ#6mQ^k8(+XOuD}_Eit$ejQ zY^ieAe^#w+ob$6A=hgbA^X-hzsYcsT8zWZ^t4&;Ks@7dcweQ-M3+kros4hnP75`Or zL!Nrse+6ymyEduos#U(#`&DpM$;01jvleX5*P9JFdpG$j$>|v{XtcUb->Y|8ty)`o z%|_ezYTa(LQLlxKPTLDRp6<0xExZxTn<p3Qo$i)ytgVL&-f#T8ck1L*C%u&|FI@M% z%e7|H4+AgsYpr={W&zdt?Z98~UcP#6)(d>!yL9pF`NgZ}z15EPS{?0s$`5OeCaV7- zdTy2NXtJvt?XbGqYuDM6MPBN4RW0<Zw={a<Yg{SS!cedIVTdy)axZo2n^A6QUHdhK zyAyc1jt;9qh^p3pXq$@1s@0pm4p3*cT6Uun9<S-OpzGJ8VgnUnYXwnW`?}T${HPfE zx5H|<)%8*1SiPsUk8Ys@<xDh*XRFFz>8({U&TCPg=YIQUw2uePPEA!+C&Yz1Un==y zKVHHXOkw9+6<b*qN7;~a4(1NEE_TjzyXSA${Vw0U|MV4hM0t%>uh!l*JM~(--3h%F zALH1=^kFdBsITT1@n(f;wOwoZ)oN6#R$Cp_Yw~!!TD{S$HRC(^DtcJ2R)5~o5281+ z`}D1|3)kNWFh<w6js?fIuAlRR&9KwG{&J_YIrmzvU0d^87=*d2TLH#uu2sX2zJ3$$ zdA-?KxgKBy=eo7}CdviZg#fRE0Ovt4(`e%uU){m3V9R-($NbA-8^;DcU<Cz?5@e{; zv|9F$LTcWALQS2s-g#u(37vsGa8!0Dvz-YugUpB4j-#gsp!*xs*Q^gHIddxiej#)R z_C|KcJ_pX;cEjAj1z*~mj$R(Ps_>9?)k?3q`rR<EiW>zyQB<Y7kOc49x3$`j7B_aa zm<>xCV*}Kx-`_eKjt?vxJ0t!00sC>yy79hig%d-WA5QX_;CJ4$(9Q(f!8@R($-53) zdK=~6!gh^!_wDY5%7P}Q`?p?cV0^tAn1%D`HQGT~Yu9~nTm!788$R|OZ?yp`AQP_m zLFjcmLC{!f`oe^u*p#aqU~CkM>jKpXsRtx4KOsBAy1IbL0O4mZP>w_y)Dn%2RJB+q zHS>?Nmc@1eTkr^WYu3Pe=g@U$+r|i514rh`9XNm5x#3<lltvD21j)OA^qu*Wk-Zt& zH_Ntu2z3~uJdC4HOQ)NSAcUamzPP|9NHsaD|0rL%%GV`N&Yx~}>a}L@A`X($nmz1? z=lsyGhhU>tuNgKd^yHOB`6<A#CWA6_$BfSQ%)AF)@MbPtI&<~w)iakbzmVKnAP>!O z*eN_3t493QJCG3d(Cf7QdAwiFR1=lz5K53;9Kb@Wwkb#b@1(t2t<}Sh-tuPd#7EmR z_wCKBx6%tCuX|19k@xYoO|KDpw;JJk2X`RKdSuSvo^BsY`<*s%oPw`>Y}Y#sD}Y4J z$DPrSatzxSAfhpX38CFHfD}N<i~PS2<>*Mt=sEfbn$@4fuI%U;9)E$y`yxxIL*y+r zfIiB5W7tvXsYX47pvyG<wg{Lg2QAa8g_@)p<w0(ku8@?MD3@rQ!&r^G9u=1$?ayo7 z(b0od-tkqH;|s;pX|Gjf&~njOP}hxaSZ%aXJ;$d~WM4$)O2?x-8m+JUfpAPxyO4B3 z-lXhkViyU25toAJv9sLqOvxU1N_N3LoEf(Z_PFiXt~2hMtBzgbJ$n+H!#nO_m-qE4 zJX<F_@{dB*#da23@Gb1XiV$zx5S$wji`YBcfYSqaGpA38V6_KU!~V#=3YOK+^2$dL zl>=wo(r-cBQiwVbqHhhdDzjO@FUs$*w7r9JWw-ykOQ3F%C7|_$9A>?mx4b+uV#{7< zWrKV|*x_|V(_y5yP%i9&J4I%7pz((Ee0JThZw3MxD82mK;BcQZkj1=_y3>cGa!z0% z)$u9xDVYgij;`MZ;93K-LLk;mXQWips@K(>n+<AIsX;%95fqsS1*=OqiCmIHR8anE z4T2@m$8oWoGX<Ywfnxd!AP|(T8mNIoA(fyq&cpQiR1Iqqhn~ewVG9moXJrd^&dxcU zbQjZp#4b2eB9+3FibZTP-7n!Fv^Fp?%6`b&w)-b;KQ`d>74<3TA7>*our^#w;t8Tg zYRhf=#?&=SK|a50zhwy?$@c%N47_)1-N(>qkvJT)nwPMTQ6CCdmX}l3Szd+`s@Hmf znM#hT$f{K%Lm(;Q+g{LVp=MSY>!n15h*lDPCWG&jBNP1Dt?61Tp{@l{XUSt}w`59h z)dFeQS5nD>_QRfT;|bk^8eNqqM>-~je#l{5ys%WxMVVj=vkR>#d>$3TmFg;5_90ZN z^^Wo>aQF*>lgGU_z#?STV|YGt!WE;xWJ+Z==W+cOHi3RtVcY=|+mnv#lrmsk-c1>o z#6@b8nN-kW2^!kY*j9+SQ<;GS?BNdFU^aA>yOAX-GT3F{Zsc|>=-%vhZfi2kgGq@~ z@~}ov0+|$mOm5oNHb7SYAaGIvSOgS8EOO0y+X8;dZ5Q4xyd_quAfEh={cT%6Kgj85 z>rgnpF`@EkFQ*D@Z#xIX^LwawGTgW7pzePj*L@iMDxw_l4p5a~o!#}{_&v1Yu3G5j zR5%4Rl^^7Q>x$^-1=P2HvxHWkiEBF^P7kCX!6a~$tHw;tpxXm#{BGv{y>GjN{B|)s zI4BM>I}U2QG$;c3xj<oAK8c>iC`{!bzV`M1V=QK3(pVGRWh@8}P^;$Mtm#G#sLG@I zr^XvH!n}8JmCBgqgpd+aoaI%hf<%59z|ep$O@)KVs)WZ-Iy6#Ls@W|$m(z!a^35<n zY%2*EkSmaOx&uQB$U`bhTZ_dRp;4@h`j3ChJ8}G*-|~)^-}z!0hhJT|e*F0SiLc^# z_N(5B>*X@`kM&=exx*JHJP8DWa73C7b?>+&FAquG)jptTr%goU^6|xf@%SC|CfJ@W zqXSQ)AN~2WDXIZ?19R<`#1IZMcPPG1^YefwHrOw@(y!m~w@XV9Gl?7+Eqz}sH3Fkx zLK#Ir9J~9>(R?|hUqV;)8Sbd_`xnl{uYWHXqU0e&>#V@EZDX!ib*F`Qh=Jw8?w673 zq~h`eQFfJZ+khNL`%zYqgrFH+Mzc|FO?P_TKtI8g$uqzm4d6lpT4Xf!33icS0-!wz zqwywf+gh{gW45Bwo3&;yhB#wax7uOtw%{F6CVGyKQWyzh5^z;68I8twHg7NR{2Wie z${lAAx{zS(C7ug%+pWu@X#qN%u$8UYDE}B`1J+{Y#({NQ;uyP>p<tY}r)<v|$GKx4 zvJc7~4`R~6^=XJr2S={+m<vRNUqL>XP*17~GdQ6^A_DXWIG`%raEMm6%`wy;O~Qwu z=zwTW%6)S!19fT2xKhT&{Y!E`Yo1?&I=%g@T*>hYM6I<=qb?7j3^$D2DagJ!uwbm> zYH;PUb>j+*ywV2Tnh(J|+RW%rc(ySHVGc7E?wj035h^;5e}x}G86vB4vH#<V`L_xH zE^<fgzFQ4|{}5IK5IM-V-fM!<g>AZ9FbCRRw-&AgdMP*$LJ<HcNUjR}ny#<QZIK~H zDH?{Whj0%hW>DM~pb@wod@Lr^h-ui*(5$Bzp(1Rw8$jNDcv#F4?nU<e;t&4-f93JW z=>(C>E=2_xLrrrOz$~SUB`U1c0(rM6uNs;SM_IWU<v=n3YEe!gAL`iXG}@XNIm!Xe z`#J<)euJlDhPMm`7rNX-g77N&5H5bzxQ2cc4+S(H0Jfcj_LMV4BFvD)O3oo5tx0HS zeI56g>Mo1nFL6@{UHup?DB$R=af7RE82{}hs4g375L%o6sH_3O@(%%Q=?fun^G0r9 zhxv^HuDV7owvP-l`a14qc~9kx8gIJ_nA$jtQ41&obV)UD0kpjrmW=xW>THZhpgh1h zK2a_#E(uEQFI-y(#dzjz39%CC4XS8+7sWp&obORu8NjtrD%}ZJwgF>g1Yg7}_Kyr@ zv#Bes2qN8-dmZI4$?!Fq5<o;_BMr!W=`W*@et|o>f{ZLHhPqbwSKu&;O2$&F0&l^3 z5?4nc_KYTLC$RdPe3T+MnwB0{8>>|a+NwCgqUmZit+2Wd>|U)J*}8`mN^}(%i~K*J zuE4|25=k;{Pi7`X-b{-`n##b^0_ZL|TA@q|(MS=gggK29T1f$%LK~Kz;yi#dG9hIS z991CznsUD590K2O<W%M^xE)9N9fkzNa$4+v(I}C}kH-$ma!|T#82RzzhNBbLWjUaA z9v7=j?zstnO}*3X=yIX5ADXw(Y|v*(r3w<jJvf3kBiFC5cQlO!Gc`VsR<XmK+l>$o z#7sTB9c2Y+M_H(io{vd^B8OjZ53r5K=<tlY1AH0-Tv)6w{^c;1CH&KE)ZQ=7&BX=# zhpw)7Zi&<pedop3FO;*9yY4r;(Ktj-C-AGL@d8z}&`G`#h{v^3z=6;H)KPW%N74Th zwt$|ZT^`m+!~k6Q&RvyNIm9Ef6twuwDfs>Hn^$9M9KQuMK{Oamo@)e<Q+1jrXS&_m zA-vmFdV3%uA(c{(nwT0YE^B;owR;^*u%R0DbrHl6r-n^{3*a~xISYhS^Z0&L?&8R! zBYWN>s-$a<_K&wN!q6DB25Q3H)X^|eMUn>?QB)H^wrfBSW8Yl6`pQK-*jDiGubEB| z5Y-`Yr87w}UvKvP-Wm*JxU?~-Udykw;aH?;jJ8;J;PtwRowlNJ?!zQwpy5Kw794#V zP-i&<&tOLvK@~)fy&XC`{LRQ2yn*^Sm>^3gXcx#BDLdGU<$RPAceajP`pq<XEy}{L z4(dHvU0D+kJKl)m!kA~1#_8T`vy88z1LnNZt`3J4Zbmooo2xoxb^BqU8VX8k+e6y= zH5B_iNqQ0o7VU3X-Z{b)r~lw?@|aH)-!2eBF~Knj9>Y%j)3CwcFKpXX8LNoc+;spQ z9NOurD5#^T{GnlsqEg(d7jOWXVtoh+^YJN$8F3_!LF+sMtwZDM*;wmjHrySQtY(e= zfLU*S!@6R@tf#bb7Gd4$o2XFV#x5$JPaMA^f|{l(#5+5KpFRtV5Tz{}70uXUVoq>$ z=(&gZ6lW9!sNMh4-m#H*$gZgvx@*(AAl7n>$M~T@N+==fTOa{kq?(sbZ1a*GN@tRe zu}^vnyE1#jhb((MKW_JD{^)+}ny!>|Ib9fS5bp-D9kp&zdqTL-@AQks)5WoLGzUv_ z6p~(Y*o?y4qj^3D+0_fy_fW8j_Q2S+0^0r@OG9b>6MNs_vmf8}CM3GE*aYDuZ?Xe- zAQ^ER?}J~wS4<5VVF=yB1M)7TuMs)==3c%a*?fh)Wr%>iokV~85AW@(%zDC*sf-{i z^DU)&Z{dKdk^?P+e1u1IJ?@}$I2IDeuk-N}qBKpZ(=4^^irfIeq9-JIW)Gpe=B@r1 z+8L63IV;2z<q@}Pz_A-m3XN5vFuNV1N}0d+_{^U9qss@Hs9HV4*C%)K_2HET@dG8m zDOa%XBO1&z2xML;7mZ~_pPps{gg(R_C5@1<mY#Be^^b5jY+sIq)C1f&%=Srgg)Cf1 zrNUStU&v|RVc*lg431vH7R+F`OC9F#QHP|4LR7jczU_-}ZP#|t$g5DsiNh8qgaUdu zhVngDFX9f~5@S;M-wR+#z@-apCMu%s*NGmCy@KGbuVJeDrXpypMtF?RK#|G4wmciV z1l3RtOI0*@h+?Q55uWbBr{^c|A)wkb9TO`lb~KBz)aoEE)@*G0=vKE`+mgzlbQ_}X zbf@XF|65(ezu9x?l8M|vz4pQm?C|)OiFV8L3w2oQ!NRh%`3enl_&yPf+3NJ5W1)?k zLB~UU-He8*j{20Y?=GIivDDLP->PW^-w`~Pe7ShUjecEBd56=P3Msmq2X=Q-Oif4G zJJhxC3}Wv#$lzIF^IR<SE5$U9RnF_ncv$aX7iA!DqkWL&33#V*CLx!|Ma;bxjfsRt zAOvL+$?zc`CrynIsL&<}`5I|cM1wP($z>oK4h)IvUPYP5Biznn3w{H;L|%!z`XE^y zc$$h5;y|vQL?GA*2V8Bh<9bFnx!-U})jN)I*6auIjLQCq^AMe(8H9nea0A-gS%iLR zF}ZLyI_=rpC)C(E>zx^(^<DSzERuolx|IGJqjf`H1I7<NL728gcVh?(ZaW7zC|&A5 z7~}>{wJ1Z6QsATU{?A=PIGzA%B--d5hij33p)DVzDB=3Dkb*J7Ne!UEv!Fjf8=(ur zE1~2xbQv~)!@)Q(|2KM|Xvi!C9}-+vYZ5nZ#g!AM!a<PM1Xq`7DG6$@`lZEB?FBIz z^>Y~fPxH5eHAd_|rR|O13sAEwxY7Uq&=55ClXoASJMX;>(pPW_@;&1aij2ZAj5_1R za+lG{)Okp=6rASG#O=?}!8&95Fq1eX633cJdWa=NoSA`E8Gc156%IoEr_g167d!Z` z{sfPOT4#AA#QP?XXx@uqXKd1aIE}Ibfx#kzH|i{Lggbf}#N3U>Vk?)(U4O*OLn%!r zhzg?l(DfB^!bx1RD5c?4;C|A{*&ejTq{Q%`H(+Vv4lJcyHs=Ch;{Gm<NooUfTLf|= z9@O{KAK&JUVONF<<B#LUBD+FtRLG+fW180(q(*iQ1ZWB#99&LOE8QcME<@xB1_F*B z*o^3d%rpJ}aSa(c^o>($5@{o*lUNOq>1#a%Yas3+*&(+<fBtR14sWGOovlKgCW43Z z7_-!{s(0|<ZE^&Rprm>ffw<TH8Y6%j^_WR-8gU-`xR#emHV~xCX{-@^D47q4GU9oY zMwE5}3M=tgOLrjP<Di@e_yWacv;i~`z5j7QFdecCMu~vt^VnpvDX~I`;|&-*^#6Q( zP@s$ZAre#u_IDvs7-Jm)DlmAm(S(_as?k|8KbkXYLdfx8RN(cx+1d2VnMxK=B)o0B zuenXA55)8B$X+!RAaqz%v=3`wiJ#)^b6zd@y81H`(#9AiL!9PLqz^#Quc35hoLUND zE}06=R|RkVS^Vff#~o=5@<A?0ll%5m{o`rFxoKDb1d5ZR(m#^hOW4E(vkRX%Dg$F2 z!7Y`=FHCljTRtkB>2^iU)_}k*K^wO^8lhoivo)GEO)9IAJm3#Syi!zYqNCq6uwg_? z$I7=3`xyC3BBfUR_1et_Bu{J@FEBv4upDQ}ErTxN5gnm(p_$zffdpnMOb81!)b<v5 zwy?ZsjF#ib<{~7f;S!AZs=y(Z^pY1C0T~7uq$J3h<wgM}Q13$<cpA!b$NACB2i6Dn z2hInXRZ)(xK9K-`!OS9DPqb#n&%cc<6u=$=BpC?4cL#Z8#*Gj_)WuORdMe@pP8Cbi z!~2Y|@S2IkQat=&!;n^YKc=IrgtSOE#`4|~eBlCsmcNXFq(<YPklIo@Kp++|#Ko3= z3@0CEH2t<FA^=X^$b9ImLacq)`4AZpypACPUmBVfUWl?nI#E6*hM4YA<Y`o>{|tB3 z8PParA1+BP3_29U<d|P1KTr%CgSr2OgrMi+T8D;lN@`&1Ot;fTL!y#~tqoalZy)rZ zM++v6;HPo)b7`yDaeLbBPwj0r9Vl`*H@b9%DdC*}B|)6QX-RkX2ErnvVyx`+Kl%Sc zM@gE5Q0E$<Hu#$mdIaDkbeO!}2tmN5*$g0NOMxH8Rq3C^OUl&hpTbd;m0A;sI~-=2 z!QnLi1(f}p98i&`7|{M?(%!t(@L5WlGoktPeU&sj^SxxA3OUy)j7taWxSsw=lQ;`r z#%`CqoD@k(_a;*yDKUXx@tad6HHP23DDiPM@jjB}KY+BTAQ%oeXK5%^Y7?kFad8H{ z6*IhvAdD@Y<W-kFGY8@!40>Ir+Zxv-;R?A_l6zyShJs}TlxUc;t+58op@v>k==en@ zYDpH5@UZf4`pphfeLdBIXeOA1V1{Vw7!z@5J_<ZGU8UuPCZOP<<fR%Wn9w+c+E+K3 zHK}N30!;*{6dam(L_-*9OkF}4iqU77n3A&xNGjDNd5T6HHj-563#dSHz-SvH<_7Bx zqjhVY>41qH?63BklJ|`ClT|1ZVD?UH&H$buk^BqdX=k)9$p(VTmAvd=!DOnK>Xw+f zl0cahqcBYy*g2Jb0UYQ}jI*hLwgQnCDo>!xNOQ5@y=v017Ff#Yt>xv^S-%VvC0SZV zyv9!tvl{VKJI&)tksA7GVuzI47<bOSd2Jc9T|G|hAC@t85s~1o<^%+rl<EcV5=cxc zY$KY<eAfj=M;DgoVOStX0(L}lVU^KzUQI`bB`Gv8DdBw$5%@8#(YzAd-CzT0i;8m8 zE=$b~R>qzWFAy&w@J}w_jn#%mAK9?U){gc4{CwiKFs-PC1@Ec(lW9+xQNm<jW+Bdc zCeSlyhJceQvnnV7?Q?kU|2AoL!hy-SeD**|KSANtqDzZ)y6gBy3!T#E>)3)WcF>+# z*yBiH-*LKAtJI`9L^5XvxeX_Tr44n##3UwV+k;#KX-Gc-+zTX32L)2Hu}5-}UIkh% zghiH@BUsn*(HM^uOOJ~SBeNLgginGC<%!5;maqN}Is(fa!h3QzB^%i;ZnYWS9vh(_ zAZjpL%pcJh*e5-i5Z*~oC!uCSpZHgJ2|1yx2Ck59v$qC+cEV$^wOBcLKMoelvog(F zd;0hZdzOWbqigyv@=f+=8g~D8s1gq00LUFiqQs`fTx3`cafky$`F*czg_@jB!n4?d z73^r5=n{uh)+t#{*mDRV9C1wm;iL&5$e_d1dKDo9<_$9AjM?<>91<rCVmFN2V4ou@ zxbY$a44fu;qU<h8QovGwQg|=HH37!x=h1^`qPw|9Piv>A;S<zMOs(u^h6<KXAfch! zMT8&8lt06=E{9u;?nEp90*gC-ch68o5125*o_YEA96XxJ4BpVfW_W^WlvBtn=^Op? z3ZcaOeIHN#Ze)TnY%-4PIN&($zyy&Huklx7O~N1}KI|FdYmQoolvyMQ%pfNZ<Fx_C zFkb4k<R!CapnVYIpGN=({sR6pVH+WOK+uBzt7uUO+5}r;s?$Hl%e3Q-od|%_5I;uj z-qstuC4v0+a1^<ss`ilBFXGOBPf5%!6q!>@FNK3l8~U^n&N(zfVhr}i?We0ZC>GMu zQ8*cnPJ$SbB8)T`P!BRq!yJw>TSq`Yc_bmjk+x9;s)WHzh6gns#b}f;8Wl4d;n>DF z$2v}im|&WX0q2u4GDy@!$cWaefpHU*3`r-0;V4BpvxrDWOaBtf54fvh7ZuH#BW54u z0zesGC2(6tkprlInY%sX_t$Z)=zz+ma9|-{Tk?~To;#Vx$Q>RTxm~?V@ixbfTq>3u zc>!2k+?=Pukr+qtojw9_iucEO!Gh!137r-6^Ke-*p}@T>*AS-4oMx#!Biw@5Ay}DQ zv{546(dkgmFGeo&U!!T`4ZiZmV)eq6*I%oiy>v0k!1n;FW(M7Wyn@dgEO`@2k5#13 ztTt{}vhqxMZ0J4K`%zX?j_D7$6Kdr1`n%X6cGm-h)8FH<+k!?+26YIoe}H>1aEkC| z5X~cFLq?d+1pT+LJIcYG#(|Y((tA$QF~$*Q0q)*6GQ6htrNyNsIQ$Oq>HE3Jgp)|~ zniD4tL=$n-b)crCdG-M#@wZt>y7E07{Tlg%q2R&;u4CDy{e_9bp~3-qy_6Zy-C<_P z6F4#MdH9@d<j_M)Dw{s(4Hwt3FlYxXu}N2&)Bi5B=V9A+02wqe7h#O{TOFca;v&Dj z24?^O=JGOK^e7=3oj&6wdbgMwmBbT(2$E7GnW?O%gQa^71X!56*z;%8Vv@N^90{Qe zhY{Kl1dw(I>&kLw-o+96Hr`AU-Vvy0ifor$*oALMlCiS#4tsbd+K)UGETur+M$*bj zPA*5xa`mrcpRxs6f<jjWF6S!*V<7YC*#Tnd6uMAJ%&SX|$tI^H&qmTam|>$p?#REh z;h%pMV=cLQl9yxOeo+;n%%0`qBxda~5d?vFlnyp-TtN;NTY6VoL1F?Jjw~t9L)jsV z52)p5akmH+Xd{o0$6zqQEqs=CRjKxCc&$qWWitk`%@|w-M29I*uxl`pU@KT8vXJ2q z#iM57Wt<AqzDbw4OjqpeCy+JyAT*OeFZvdiGGc)Q7F>2bAY>!~VReq+UgI*Csii;? zZz#DNTndcNXFX;T#>HcxdUnJ?mN?sEG&}-&V-zg00a`<`;Wz}p(yDmwXMtZq%cMu} ze6J<e1x`(}32hy-Obts9LHDo(8A*^eBPqs~%E%%K5=H_w3_n&GCr**rn7G3AQpRHs zlng&I+(>4DqVP}risHgR!!?+ib7&QTn&n-Hd%4OX=K!aed<f&KyNWJ&>tWap7M^@^ z4R6_7Aq0979Sb<(tyvSJ^KxUeog9rM1QkWXDHr9I+cPGGbGvqsw04QI4`n>4dQn z8%)NUS^#U)UWA;$%OF#o<_F^^2Eq<IAA?fhQ-9O~S^U(cIrV9AUT_=^``_?RcuS4$ z0_BY{vON}wNeg5I^BL0UT*sT~Hkd0U#>;NYMOK{A2FHN3<}0}^NZrmYXaHw*ZK-Tm za=2H&r65WE4%lA*26pAKC})^xL;$_TyHxs6GLp_lB~%%cJma|V8@!tXVnKaKeOm|Z z07g<kRq}2B7FLUC1CK=G4Al;6dXAEb;@dh<u#XZuLgm0G1ITS+IOfW@GYKq->_9$K zcUc~Plw3R(=|#UF<OgIHL$s1_d{7qMKwU#NoyPJFF6WRHS-~kRP|ZGvga)yl03VGN zh1_7#9U^0pPl}x6E=ph#29wt)?c@vzY-&QX3z21vy1y+FaT3qk`a`u3i^-=9$daYv z`}^-m5}w!}o@7A4rxs`V#m;LnTp4l{#zJNU1qAp2Hq)$dU}LQfjS}0r8ZH}{M{!zM zW&gGFSH503|Hdm-qKwzhTwK(@iosay9|0X*MO}PORJa5xXYQQbp6T!NjyG$46T!s& za{mAdpEr3Lj2A9=C>E6<4^&T<hxfncdET`%R~9cWzOq2tBag-e1{aCAktEQV^d3r% z;v{~2)M%csWFQ5h_x~=s**}hY-vFWRDlPeRX-&d{^Ca7hYP9;(=;}Ep&@o(zHSg$Y z#X)*Wrn9;_8gn;TiP`)Hw$h@|e;+&eCo^Cu6I5iF5>X+hIEr<G(a<fH+)b|0G?VkH zrcX$AYqx#9PU?LGWi59qp<2WlOU@Lj7}%cCM=ZkrEtIIUSn`MGte3D!!rCtXq65z& z6Da9fGzpaOGeXl9qKUIe!@kT#SzH0u|CzHUfgzUZxRkEjqfe=h!VpQZL&-Zs?niz( z1SKddcDG5gH;IEXYu1pSEaZnsJ62Qco%*rm<yl#P1H>bl%+f|pNHTE(zZ(S2`Kwni zoi)L$Bot_bq=<nOZUEQ~;RcOuGA!dK*fMv)dR0fPF?Vt40z?R957zxPbhWct+lt0% z%{FiP0&j3zq>_6XQXFOl_VFhKjQ4)`<VoVspX6+b?7-ag5A7Pxp-rC%mmC@=9zKgL z_&#=9leD*p@R8sE%hMIB6!z06vHb)~G`Y*fK%})1rZ~C{9{}E~nca3_b<N<p9G+TF zpMroIP&8$@PMu#Z830moMHyYq08H7CtH%nPmi}mv)!!Xpy?T2Jsp(m<*c@}^y-{nn zxEN(+{>;kkxi=)7Q&V%8Qje6;Smoy@;Gfg}jUK}JNM_x79V-AZjbt;}W}pbT37*4y z|1ggo^aPLf@9=m+|5F~z_>aF_Q$mzj{H>$h)uk(t4^bA>hSPjpr+6PU>oIEz#$#P5 zDW$Ud1+X^2qM0EX0R>AGrpbpkxf28NU-0M$-0hxVMvL00HT;;9ED8ne#|19aU_j%L zAVYE`AQ${H6@ZIc`VZ_TgSZxC#@^Ss5i6hWBK@y+_d$AdnAiRxc024u0S5)Foq;xU z<!U{HEBqsArFx5~n}kJ1h>cFHm4Rj=_AX)4pBZF2C$WSOnKNN-6`{s)tWao=%N4G0 z0D%BoXQ78a8o0U=7B@<Yxi>Zm3Ix-XEBW+FCU{q_0CUG>Kzyq-f`Q^s!z}>s;`aw` z_a_HgtVsU8l!bX4m!%Cg*8d2|1VUnNZqwgNr3}{n1L?vAK3vxYu-KMzu{aXsM`>fE z!)qM{N~D`us*4L5bOxA9Z#ten&g-)tnku8MZmj{;i2NQTJ0y<w$Yl}z+Zf6rnJ)I{ zud(?VBMfCV-HdR>%q%aWzI25h^q89GQjR_4;cy?4WSlIPJe=TE^X#x083l1_j}FCr z|KTf!*8rv2Oiy?+=^RsOh72}l+>{^ieV*s;UvkGZ5j}xjlwH{Z^XiBfewRD4qW)*t zfo+6Ogkywv{xz;j62H891#9x*voUD~dst|KivSw({TGbQFOY^$;$bY@p-!_8fpMtS zM6Wq}f2XjX!`#vT2G2wpK$rfJD?KE)*X{!ku%@L_f<nc&W4H{VpPPeh3c|iKx85&4 zb?UkKllb>k{~^-ZFprTmA>ZHVaBX5fi7$50AZuW33^jBcAHp%wKgef!iE)GEF%~1O z^p8sb4que0jS57A-cg>w=GCR8%U9Wu8$!EkX(cOk6YD1ZkI=CG$87x+8^fsWszB!1 zFvAwr?5?Kf;iLI?eEtS^^z@q+4$jRPeD~zs9Bd?W+<)_YTUNxvV-Tmu2+yQQ(0h59 zOG9Q#ZixK~{08R+ct%pLcnPb(B*UhEcy2CPieTDH&OWQf<o^8L7Lyx)RErPK%?Z|; zBZkGOpjiS@IrVkFcJGTyaWSV*d{?`>?u@(_-*xXOq*+CMZ*Goe@f^}GVc7-f08bHt zNUroBy|)vR7-*hgFY1);{G&sbD}2sk3p92i9M;<fxDM&vm>w_<1Rqty@gq2P@l6LF z&kiz_;-Oir!DSg4EFX7iU*I^4QVtY<kw~zaYvs2~a7&7Fk&CPm)5#6se7u`C-j{N& z#x!N{8Ys+|-uL41QTsc=)h0AMTDIHMTn&WyYP-1wk5)Jg3&%Nz0{u9)Fl1J+>?z#h z=ceGTkRy;MI7OD2#h;BBu>eLeeG=Iv*QBIMFGEU2OdpG>lqo4mo>;@U%ZyX?{NkH0 zjP@R_n;YlOzx>84FFa+G4AQ~%9ZZa1Az+&lBvaLVl?yAd#=+?blNf`_oF#q0rwD{X zu*zi&=p+%F2o%tZETCS~9c>aF10H=FyJ!l~M^^dFbD%*j`uA}e?%ZGIU6*nWi!>pa zqMT_MYoX%hK>9aPq%ux=tj6T2DZq`Ta)d{$YY+I*KZ|R>O?gcOVm->wSI~y$mUE^7 zda&{r&NSSE70;-3--{))iDAcXAjE>1HXzx6b=Jf`qP8@)^$AXh*PB?pOU%x%2yVl8 zW)x-vNxTZ_zZ*GhaQE>WOju9}ulQ>AkhPs@(A-9h0be$NLl&SVtKY}@<i<XJxe3Qp zAba@JKR&vh3-{wo1i6g^aQ$KZ*bYC60^^(UFO$xPNY6pk{J;*}h7!g+M7E%Nk)cdL zlw$3)VF{=o*e-Y{tbi0T7<;XDy?Ff6bn?_Z`5u9gd1^B!xsuFW2gPEXk}Jk7hOD9C z!r+)xr3R+1Vs%?xzMvwGCDU%L&IM&44eBP70w1SqNQT2GvqD;fM48>dJPMQ7$p-_b zad<(yft9=X796^c{C60!VuSO08Pb7RvN5S6Zf%$)pCpKgs2q8x*m9$>2HuG#nUKyD zD=a97N3UOwNAPh<3GeZsbfybe70$l-c<@f?BArp-eO{?dTz-4$)z=rVE}glu^v31L z#xgU$09POx1Db{cT!rNaf#A^P%7pm9=GU5?6(A`6pV5UV1Mj3s4HhWm^I8Eb3qpz+ z{V`7UKj5_q@q?jhu@6ULCjz6yU!KDUi2yz$7a1>Y^aXfF(#*PYk=j824OTTRpO2EM zO1K^nDkdVB2LFwXvU8?i|Aj|{U6p+7-Z7b)yYZF&D=3l6fJ;3bSS5y5Xqpg7@gF>1 zKw4NHZF}%`N$E<?1jhSE#e*a&O(Nx~sgJ3dC#GR64bX=lQfVs*?Q$V0Qwy<}TjP6X z`W)Kquf1l{4MBB$!ZX!Fz|MO!@&N=v#B9en&&5@2*g*WhQYLvy;LXUg5%3PpNyIhy z6$imK{}zoJSTiv<2YQ{0`_z9#Ug*{HXU<9d<@L);7eOcRib<D=hN(;C(KO^kHA~Y8 zLudt9n+H{ELKKt&TtEOjf=hrUL_jHkB?z`Yu?m}7J-CB!+#yVH7$0H!8f<GPl#qkU zL(Mwv!?;s`rM|*DW}T|JBgtG`uL@ZvU%AVuhakm@YD(>YA2C2)+p%#zE$5l^{J`k_ z)eJrWb&#uGVYLGS?3lW;>H&V~?jtO4V80>n#s=<37Nf9<Lz36jA*n0Nx<8UTx1T{h z^g^JXcb>qqi~Kge#TVC*-<ZJHVDcN2JDH7rrlxP(H{f{?xp<`itc*Nm$4GW}Ob3(} z(7J|zC^_*hw|GYSM;7s+N<)0mZ=<>4^N4kFCSmTIY1IFdbN9cogZRh*73=?n<6T}L zC@Z8rU>-}@F+mh7(Bf#bqkje0<Pm6Y*0M+4MTOJNT5CnsUOZ23LIJBEcMaSs86?B( z&ZKDCI$I_c9l$1>C|}V#yp04KrjtXH;X8VVWnRs9^vZVMne{-?5{n!5&Cw5|isUu- ziH8;`EAfDtK8r`dL$O!cQ`2;R#th^A`if8y-k|$pR5+iq@oNbiUjZBMdJSO&z6P7Y zrvRtSf>#dZn+RmzaFWyptN}&|nHF$X+U-5#`f2g>nvc&bb@hv{CEa!L{lCxbZc_UG zouYpaotiYA+S?^tx|H;W^s%Qm8tB$q9QNb>jr?D}xzOTgzqz#a`hD8s2s6qcq)JEc z?oTd~T(BGrKAF)^E;>=BRl9B8FC(KJ+3S&gYwrtssP7v|qxjI`%$++Y7f!>Oy}j*u zckVoe<EP@|Q#d|_<NFW7aHO#k%|2+wlTUtfKDFl-rQvv}qwKAQ0<_$tn>g5SBz<w| z?)wAuPJ@bgJmlw$hLK|FEO$BHoYH@Z1sSf7@;%+GwrX8`_>Xx;R6D_l=l!p-pzsf! zUz*<^jWYd4*L<S_>yHuM9ySGVELX}!EGCDps*FKqCEOKdrSi%+f6|Njb!c#WRJD?m zcMRZ!=-e_ABqcD2lngsBocVL;$8o+j*~2<O4+dO=;=N~9&QxYPgEQ#lgILZ{!gd6k z=2NxzP{|pO9Dq;$c)d_ep(OE=NbMf*NSo)f`PLos^pmVn$t{qwvjBGpCi5dsfmx(Q zJv@rbBp*|pWu=kJw2kEBM=^>wCeWG7mm$ez*{vYL<eOUJ&X-1OUc3UVBn~-5io#GZ zB3e!DtNYeL(0Xjd@H%Eturb$4V#`Qm1}|Iy3cX{}TDI@hTWUM58r^$=hDZl1-#(+r z9_wzTlz>FW#!C2hMkheV48B`KgG!Uc`p#os-+9pk%FLieBr{4@4JMu*oRot|gWS$y z3*N*ojzw5Z8{v|4{Sp^L;wlgU+_1;#WJSo(fpgV)5cZ4$R#-z?$%A;ZYV$)f{7Nk| ztYG{bO9XPGTv{}X{$55CLl<A)<+`bU$poprxfZ%S*8;0OjbasKNp;M(3vp%W7R(y& zIlqT^)3^kGiIp;a2bOHH7R28mHkFLg-bk*9<9ZLz^SKtOe;NlCUE`pASzGN+!E0@@ zcCZqgDL?p-GZLi8@HXNDT(2QALD))EG~p%JU0;-i*8E0|Su%`FAF4h53U?>CyUZO& z-S{t`=TQ~As3>cAIvoIi^a>!KdFfnLr8!UdOte_zAW`{Dg?zQZ#2_Y9gPVL9m_Wnh zte226%2U%KKqOz2ly8{IM+A&3HJ{iTMuO292?=5>{3%wkpS!=u-M6r-l-}USS>%I0 zW-;R_-kRlZp1VujUFS}5*T$}rdtHKb`Y+=o%Eb%Mh+8C&t8zfB4Osw|u0^R#$+=17 zHer&V-~(JhVRCN2$D?28?i22Q3%g1&4G&7dK|Hbgr*Q|ZnlAhkmZc*gwlB`EcktzK z5oscZO`tv{D-#46(v2KZbOM8!j|aU-02llsb_}dAlX3WuUI#`($KiiCjN^m&h8QzL zOPOi6@a6FXg~Nr@g`=gTg)!Pc{K6{#72K(3r(T}S6egx0o(c;2(o==cO&>2D8;5Mp wrCSd5#Gh{vF9$sx*z89hK?{$Xuj0VEa&oSz8Gew*@L^YETOM^w2TS(<0hMtN5C8xG literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/config.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/config.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8c2cccc9559b054dc823a4cf4f931e250beac328 GIT binary patch literal 10004 zcmeHNOLN=Ul?FiYX-an6Za;cFU8t_MWu`2u-AN@WR(DU^vfNRoTXHPAY^&9wAOe&i zfdG9kD2WQyH5GYwlS);7!PM-sN>!@z2WB;^?1N1fv&vtX)qLk%00d>ax;HbcPy&e8 zJvjF~zjH2sI5$^2{?lsbuXi-<zqQ=Q#PR3Y<Q{g8wy8O~WBB@}&fmtS!QX|=0=^5r z*)MJub<HuIqF?HlH_QFXW`)m7ezjlQtm)bpnp1WvuQjLQUN|dk)^T2SYB;aCb2y)O zj18@EVfZJ!u4duW^KE(1?1n+l+vN-2<~}vPvf3Z`Zr=?eX+?XkrJfxMJMzN760UDY zu49Ee`))UqO*N#|4F@CP?e0Y_>py>Qef-h=kF1>$4}W6&zDuNByWeE`E#K?9fplBe z!;MGxEa|$|m#fPwYa1(8FBDck6t3mCk?r{)|2HtMtu^#`&g&0C5p`rFykPflIxaVi zxUNn+(P-exxavi&z}A--VD8H>=+f}?Q^3LJ*yKIzTn$3kNZ=55)hRgUYkkvnYEIE9 z;i#y@x>-^Jj4v*;$R0#^{mw9QPwswYAGp%81ItdY5&K8BaI7fINp7u6@1+lY8`zey z$xGOc^|98`#s>D_1&Y$wFsDkV6WD#X(~0MH-KYckAG!h)YZPKrx_&P<>9(kX!0$Ct z$JhUSz1-S*CS4)7MhkLbwDri92T?fKdKiWWi(lD+y~_$++!#qnXR&YNOKcsw!Qqzg z?QBWNY;j<B5AdAaViVa?YHtok#E%zvQKxf*_$CY1^pbvZ<D5jA>0{Y^>CtSyAK_$- zSsqkGeT=z|^?hxu?dvzS(*nK?+^LFtbTlrU>9|%<@+ib5#fqqa0?)c(iW-jN0*o*= zN%gTI4p|avH%E8Noeqhwa|<Vuq@?K;-PG%P4Sy%s-{hX3&X^}<(Kb<*k+S>=JU3C6 zxQIuXvqpiL5~SC-cxd}WcLqn2IQkHeND_=TlMjA|OacF8rXANhoqp&HeKLxAr}JuP z`-zEQmQL91bi_Q!i7T{wmv%Ja+>Rt7%Gl9ONs@#tjG9@i@YOCZWFMOK=h);Xc2jQh zZssP}oRU*!H@WUqoGOk=aGeXUwav0~!>K!SxKeTEor}0qb#6NEIG1sy=3H^E;>rbf zs`uiOQiA=LlBPWm#euZkW4jyqBg^*=T+6l`uNzr#H)|lmL(hS~9oZwqlF;gTzHfPv z)e~W#PF%^c1@S`;+^%Q)JQfkl7M?4cmet;K5m#(%(NTz?*6$;l4dA=32$<p^91JP8 zk?S%oWp+t~Tk&vNSfg-gb>Z{;`bl(DY}iSxWPVyL6&fk%+Jiw7a+}0&XD}j;?=FCv zNZ!)z?JhLbh;?DvKCy*}d+0(K=}R5A=LO8)xu6^lC^k#W3o_=Fx!}}}z_9O2tJQa- zz1B7}prZG-1#u;kr9h@4ED2=~B4!!b2*+f1ijdq3hrVO&xRxK<j_W|ItC8g)3i>j% z2BDPR4n2$T=PLCiWQJ0)EfKg<igw^wN06!_ow6~JX_|4^m6Ue(hZ5%CBFrXIz{K)$ zezfxN*%#KQR$B~}3Q71IE6Yz;+MR!1d5Jp<jth~714?h!fje5@S<cdt64Bi>7ALw{ z4Xm9IJa=sgW4udZKsHL$AB?Ogqi7FyMd~HtPTg0Ml#P4JQUhpqI2gEsXb~?EBUz{e z!A@rowq8iN&lv|spO-X5%k=#4C?TZ;mKa0cBsskz{v(gtVq_)Hv5C-|gD_HWXL?gP zjOKz1Zy2CrTbZ53%H^3eNmx??BNRPA3HSkWKLd%xt)7T*$KHV?Nbmg+>h%JczAGaV zE1aEPOH!P+c$m6Oh0fHG$vhDTtky%gJZQQQ6NnKV(6GPLDM11w?=HMtfA;jjlP7>A z58A8iYn_dic6)X0i;dJhVFJ+}Ue9VvjM2A;L3fW|H9bxC-97t|odSWKhrXTTMCdF; z^7<niW{QhYGK^q7u)g$y;W2#GvDP=N7fdMiE%%u4ktHL<Fp@*wF_L?4o7$%fOXT!R zQMjbS(Gqd6kc6WEZW=h@kt8;(ZOmdDS^|QwofQ5JY+?5;5r$ER2({7}Vjb;y-8~k) zvTlJv3PU2au?8`(=X>rXaFH)ge-ujz;v~g)L4`1Af-rMdbx*0rQ{iHo44sjc<sM%^ zR3!A)+WuDI;>V1Rvw3VMfK^Qo)^88}NVX_es*sycGb1bPk%!PTiEpufrtu#o*JVns z?_n1K|BQkE26az6GjxRqfrL`zV^aR(%pO-Wrc$8s4<Ha%Ql`%5dV&LOdXK_um-Lea z!DcY+aRznh5!#rqk8wbJ0f*@dhsh7ehPdJA8(LIwaP^vYrvDmz@0;Vo*l-HJp%TNi z%Rn?=!c(E>X=hCx)%q?cWpR<3-XH*LOoJfwzhg^85g$>56M}6aFNvrNn85iYKzSiD zTCNZXUVT?e0=FszlKc9UN};iC=(u@T;rGwZ*P(1CQ~3JrQR15<QxKh&GMqFZF|2P{ zgxu)OWQ?;!lCho)mzX`=8HEzZg6(ZeZ`<&}9!D3B5$YMMhoWhSP|kIfhjbjMB9E9k z_aP5QeU)Bq2{#&wpoR1n`b{hU5{{+QVfY>%c#Nb^Y#<%F;>bgU%7{Ha`Ca=hle2CM za$(9fG#`p&wzT9BQ9}87n)bU4HR!oK+}KD$C>$&a@<f3WBY{B<_$t~@6QjwZBrGX0 zHHxtj%D9{eKP~|I$MvT}B#XYgLiQ40$l<iK+?Y$>$$Aw32uBf@X*a>zTzIIlAdcr> z;JzeZ)arE<AN*%l^pnfWTuv%Ds2<JS5nxy&y6ldB#6jYY+I|5pX`JejxnFdYbL!(l zr!@LeR7PpVuK3&cPtB+jRmbLj?TiX8^9-fb{sjsz&o!s`3+<`)0!P7`sYUgJn)nZ0 zJ4KJ-<YPGb>v3^^E}Gk)$956hrC#BnBwjnE4RjfB@11MfxY&8;*BJj2w4CB;@!R4H zEihhCDR-F)H*_Z|2nSXBU4(;Y0|$XxwGn2c5<>5G9yO(I-a|qqP@CD*j!S72!3DWr z`3`5vBRc2K01^U@6S$fH%Lxjcg?`g_Bw37HA#ph(O>Wv^Cr!^%r&YF<a!TcZtEvN7 zsy*R2b*=2%-jSh?-T~)B6$JqGCuiJSp}eSIKsNP^KzcyAfK%+m<^DfnMaEFGX`v+{ zsk$Px+G<%JHGh)b1((~((YC=PfPo7{H{p~^W)$o@)T4pA+}L!F-R|1slRM9X8L=h} z2(0eAw`HT<xFX(%Tf`<|4fJ^c^8{zZqG|)xzT%ptO^pFGFK8g@W-}bPfmon{Wt4vd zT#Ze_BlHX<T0B!;U042JMA`_#xU{mizOvTF_v*%@)u(Y8=WRNupzp+)C5aTOT4<D1 zW1^B;3<rr=cQ!95n_*sJ{d{LYd;qROY?7K4+WaMSC`!7a&+B*eOXyl$HmGlLmHHM_ zfkV}iU3eOQ3fO0P{4<<H2mld6hPWK5Y_1>Qi71HZkF;;zJ2l3}zImpJTTv06DB}!$ zsIl>h_UaY_1+sm^u>TBlGn|-<Zc1OfuvTCp?ghdh9;C-yBf}ez<-%a%gh|rtg+8hk zM$0J0S!cOoc@g%5l+JCwma1Y1^HGsRuT(F~RgUaPN7N3$3)4`L86#6ekFFb`XOa#U zZVxp^&}IKdft=99Yk+VabPfc1oBn80z^|)rCl_c#2}-~oiO(lh9aTtOphuL2%~_>( z#t;=V5>iQOo=F9mwGnYw1vo`|iODvt2gtc(rrX;YtxOh}JY~uw^iY&!W`(;Qw`-@x z0+*Y3jh)d-iSrz}6inNi89&)0ild2mR*r2d_K0uA^CBP`1UZ$2zYAok$*87C4x={{ zgiC!;^sG^ZrBRT+*%GOZk{glsF{TGj4a3t@4VmQK(<5>~?j>rmr1?RjQBs7tq#1-F zHdm=+p^2?cO`e)q83$;kQ!i=yffUGdS~*7~eRqb0GE$u9^2$~rVVi*!8fI*%EF>EY zs?uAT3Nlu%T|}}>8o+R)iJhd^`qbNJ2SecpqEN2nUNX$h0rJZj+CVoSShCmJR?}NV z!(|c89cP<CDdozEv9KM@vXzY7kRa?Vf6BcwNqx{ES7u2UE6uM;wHd*+Mk%g&po(cA z00@egf&yq<fZNAqPqIhGWne<`$h6wxwr&A#a}Mo^pJ427ut{o3Yqx3$*Ynt>1GQpe z3gHpKWg7Qy*qujsqdSpFVfwdMPYY3TzXSwVK111cYEo#<Fx=09;VQsz|B_(1CNSI% za9TC0?$@wg=o!FoJJfA;3b<Zp{8mr!8`hkr@Y~$jJfpC0+6DwNBkU)A9;Mi%s<VE+ zvA)I-rfIdQu1Z}ZllYs!jfW_apu<7+BE>cC)sEP32NXxsCRj!&Z{~e?Ff0Q`mEgoT z?IF#<voTzbZN>NBVI0G_`}Z?>e}6>Qm}fvH?!uHsgLVYR#3Jpenu&{?^~4fgxKBIk zGKe42?nkuyF?MmCYpz7I36v5)rQ1|$ZB}VLj{y{~v4|GkIahjBnFKxf2i%ocvD4-* z83vHkEg-3Dy#8}lKe@D=E5CT1kk%9FL+lo@tz(n_j@>TSrwB)#!Cejv@i5Y<)Vc|W z0<QWPjtOK%=lcb6P+fcV%gHr54{l|nP4UafbWC-gbWv!$k&ZoXjtf9$cTV+VYfKpJ z#+Gqvo)%He6@k;n<025634}I&uDude&cR(vC%?-3YT})|fRcF3kjpSM40eN%7Ee(s zV~I3rmW06~N4!k!1PyZDGz;tJj6@LV6ePX4$Yf8NXf^^q!GD3QxTSla8rB{=xDR-- zJEZpHJ!==4^tRJRKrW!#ipzVpq=*}r;M;EC2#Stzl~6oma`7q7Q-obk%p@)yV8wM; zZdQ^DQv4G!M4CTm9O414{TDU`vP%T1=k=?`C42*F8}l#{iri?GUCEnALb%<fR6!qF zJ<DJ=75JWZY96matuO#6?IDy_JS`#K!c@+5`N6CCaY6iaTvQ<3(f<|Za#{||aq+Z_ zi2bv1nG68OrLj3KL%s9Qp=Ji)9$xg6J2U6PB-D%*@_@8yqkXbN%@n{drTw&IO4fxk z)pB3HZ%1gh5K!TjvYrP`QL>cC_3qLDx!XH#S}CL(Qrqn9(CzDm)vq3WvC`RSuRmSs zv|m11S@?#QS$cA^vO?Hm0=T9}JbBRmnHsT#ZpjiJTk`tUsQZ4yA3oSv={$S-r6REx zMT6E72<$eG+#Oof2FUAAQ)24)5doHv5=04hC)_n@5jc~^-Kp873jj2qHpGWj3bcAj z1G)6I{pJg{fy-&x%*SFpFKnI{=dUsa5o!7?P*JqdVmYeiB6VHu7pU%~yXR##<52?Q zouTmQ@$Vy_(+QO0bdHQ3_97H{z`VVokA^;uXIdG$aD%LL0wB&Ng@A&>1mPg@d|sza z^ESvQg%eRl)i>-yH~9dRz`IN-0D0#a$Zln?OvYquVl)OFenh?iy21h!1wmD0rIipl zM5fV<P+sinIH&9Ua%i&nmJLieKo#4c*wg<h8&sHhLDwHTs!*OtC1dJ4V)+_6os|3H z8o;rLBo!Cp5nVw3#snJ`)!wLPq&KUXh&Sh_=gO_oSU-sMLlu(GrE30dOGfCgc#FJ- zop#IAFC!8gNY&K#FcFdK`pMO0m9VGWvzsPu`pzOUQE_N_0*e?CQY%H}o^g#9RgATh z@+Y^Ybz6ScFym^c!vzR_JV7B{(25N8f`mH|olZknzvq}{Tl@kK;U`$x?>8o^D(Nu% zZsT$CZ?xNux*|?oiR-kZU(u*vYkWvYAJC492=S11l%xcK4}McFg)txeGme|MOGryn zleqE&{u#&h+WVCcYBwv_YYjp78yD4ks81$-Ks!QQ@jSG6aN-Vko5Kh{uTq6i8G*2m xkfLfKHh=`Sl@v(lpNOz{nD#8D;>stg^8bv=ApUWaX;h%Qicv>Ktrsp``Cls875D%E literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/ctx.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/ctx.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ca550aab1fbd12f71dfa813ab645b409d19c072 GIT binary patch literal 13974 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/globals.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/globals.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..842e608e53701309e1f73026d9ba5dd880f3d102 GIT binary patch literal 1765 zcmb_d!EWO=5S3&(wiVlPx7!|y9@t*C1~x+4-i)FsHVM!Lwn0#*C}0>6XmM;SrbI1C zInl!HVcnnE59kl{GlaeM)L-bSLt2|S=-yh&9CE19@XgF4{dKPw-2VPNdU@bDe>!{3 z$N6{I)oT>YnK;a49(Pl3;<;wt;EmLu_-S*}MBSepu;!%24p{3qXVPYE*1@sE0(OXF z0RGmQ99r3hCInk|(!<e%HtTLZ)cUp7v)Yl>I;?NC0XrHy!$I*DfdmBGoKv+57hKFJ zS9ays{TA4zGngkCC_*_WdI@A_PQ>gQVy$qAYC<KDT#DNS?X<9FaUmsS+Pa{zPS!wT zk!iTq;m+?U7I`U?#Zr&RA8*K4CtseBS!ut$#J#}y2AYP}!-yv_WC}*)+4$@!Q2^w{ z^V9Q-@j00bNm79T7-*Vs6$XD{e9q9VS~-<Ep?v67!3z;nKGrl|)$>b)TfTbxvyiJK zTM$ubl`tS;P9(f6K<PcWSIY#!lzD<acu7*ASsi#O3PQDpG}pjP=!F6@FER{RWRxe` z1cV9702r`<W~9!=_g7b!?{*3aITe{IxVF(V5f>?@jv5~Xe;Uykj2LN<WGK_!D>_5e zWGQaU#&j(bL|~9&C1xDbkbdSm&U*r+d5-Da`M9si{}v>INwCmzPGcZ9iC)^MYCe$t z&37C^YobJowzLe%l|Y5BAuyqsN^4Tb(DsCVA+MTkABL-}He`5YrbkgUPOT;5S- zFN<M}NwUpD*}#5^T@e)8xpseXH!gElZTYeGbYttjcD288H_q0>qvT!;o7JPJCQKCT z+elf8HMGd9?vF*LlN8RS6ta5!zzn2}QnjdB3nZ=95-I9uGzE#bhx5jfhdBH_JsnMd zQXti|e4?I|(=$*jbUb|~#OnA5n$ZOqo*a)$g$z7SDGqYFhHO3M$!w|;4aYe$9_`fB zp7m*c*2BC++#LNBxxp3fD)Bk)s@f<V_rPuABYQaiuL<f$wmR>UV?&fJbX*PY-qiV! z9b6uIGQeSHXA)tvA-m>_JJ81E4)hS$AA%m>N{rn$Q(aS8Th&i-Vyo(G@G9P~>c+YD z>x#-gTHT|RN2pc}+;Hf=7gHy~3zC^oPSElplt<><R!xwqr9de>)Rnuq7=BQ-q6qmJ zMODY%7(#F*4b(~h2IT4$6pN6XYfBndO7lM=O>rzBTN!C0m1YmwG{OL{4(pR<0Lc%{ u@?#@DGU7g)c4kxB{w5Wy;P9<kQlFsky{<dx_-((@ZsF5Dux#kNefJ+;zVtx= literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/helpers.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/helpers.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7bc0108a4372378b0eb84b35321ec5face67ac07 GIT binary patch literal 33073 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 NJ~sP})O2z3{{wY;0F3|u literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/logging.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/logging.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb934308f55828ff156068025434a8c2f73a170f GIT binary patch literal 2394 zcmZuz-EP}96ei_=#ZG^)Zr#;5U`1jeal01ig%AWo^V1f{x&moez;hwdA|=b#k4sWX z9HhNTfo%`4o4r9FVA%a0L$|x?EA(oIl<g*K1(>3!!^3mF^PNw=*Xh_Ne?4~pZX)zA zx^Pus{sa7^fPtYtVwhPmPOQF#^}G^S`W17p_N#EO#<is0ufw|<tH+I`*>7UR>#V_= zZ_zpKw^)nWZ&BaomGKI1!AhIA#~pZDWh>|S9K+5w>wv`-Ryjo7)m*{Bb`VoJ>BU(X zMrmljxp>*O<7d;Eh{BO_$UpDM-8<jkA;TF_BTk;uIOa+c#c9&BojCG&D!D@*9PVxq z$vHWAytDV@aE}C;AW0@TVO-HDmOcADoQt}+xI%|Ai*v=@D4AwLJ%Kg*AoFSbRAeW! z_qa4`2>vSPQu%t@;<8|}13&o%3<`-=g~piSuhAh=)>!9f(Lcb!ev~p3Z<0wx=yWQw zsfeiJgbM+YNhKfw6Qs@x3?LJ#qAcx^#{r>aez;BGiX@@FijMdO0l<jLi02*4FzOj! z((^V*242K*Bst09=Xr7_dr~nl@VuUFynx$TmJ>fqgDA`a7<dvSJ5Gt?LouY|&8yKn zG{HThDT}!P=XzU0XXKcs3QjlP^f77#k!hTD3S~wndTmePfLuatNf5=HfDwy)wLIOV zO5g<J%%|y^B4ss7uPhSWSMjVo9Nq&U0f&<^5&@plZ1X&RqMX~eFL9}-k#S38+%hN) zfyVK4<pOHms;EH?iv~}RB9Wy<1u!e>C1~%EXu(S}d>HIFgXfY9Ihd`<wb@{o%ZbXS zg9lkQ+5C~FG~@|S)#l+$DxPd66mDX01bzo`G#toC@y#jqCtxQBCf30M>FKPv{%O3L zTT(L(*<uAoUo%o)daVjS-NSMhhBGuq=jeC1zs4tb&#>55_*WDFBboKKd4|W<F7)U* z7Cqg2@LZAipXz({HGZ}F0<kJEws!jG&WQUH5(T=FP&iFEGAip&qSVxP9y0Mq)T~fN zj-xo9n`sk+aR@X`HBCb<)+BKfu11c>g8<42uSm>~cucn5ED$&_mc5Xxy@kzzHrbxn z>{9MJ?|VA;n<n>{S6^eu1^tF5_-BOCD~mxNcCl{gnmMRz6;|&~(U|Ab#i9*6%i7v7 z`_S47dCE_wV!NyTmH?lAH=@!t4&3?9-d3yyLT>k#8=mcVYsIxs0xv2wofXabK3TMM z>(dZgzo<{4D^dkP)k}bihMx<)WFlHbrvodleG<aAI>75NplTCWtvbGGM$57-@fEC| z-v8y+I+Xx(!n^V%xwURGNU46*RlK#HWGs*2$<cEzB`~rpFCo0tw{7rPYTG#ml+oA{ z>zY5XR-Kgd8qDij-&NxbI3^u<-&F7gz`!u0gxd8(4w?b_;CX9hpMut{dET5NS^}1$ z0K@|o2uR8~<7_TlFGj#8hbko;?@j4p-+Vh5ALgN_xyn;Ij5*W#U!GN>JLlS5upCAe z{it!fdk>yJgx!HTQ<5}OdizJMTy{d5En8usb)|ox_y2gkK`3K{nnqZN>XPY~RaNU| zX&2<Fzf4Vg2GRVSh(Wul5%lGd%E;H-6AIxPU283o?b=0S?xSb{HxEpE6*U8vA%9VS z^3$`!y=O%e(lxDC)C?GgM1>6*JSscCxWafq^H?pK0Ek=BpRyakjgR^$3|~7we9+*N zT0*l07E-qwvTf@+Zd*+ZEY%F|UM(7~%QD|}3)^*rT*0Tvb%iFL&}}UmFchoDTue@R z9>QM)=sTf1zi5PUHiRZL!flkXjJb0OTJepxZ9{hZ1|tyTqP3iwAT9)NWZq=Z+$!5+ b8~!mN`6UchO9$SBv0Z6*nm23=Pu2eb{Jy(4 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/sessions.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/sessions.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df2bc4884db92b7e0a28b39b54bc652904ebafb7 GIT binary patch literal 12241 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 F{{gFyHsb&Q literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/signals.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/signals.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..390c1c88aad9bfd4a9bc274bea0cf8f80f869160 GIT binary patch literal 2426 zcmZuy&2Aev5Z+y_WLcJf<Tz<A1$Zb>7>I=Q=VSyy5GN@Nv`$dBXuB0aP)o|*Xtk@5 zlobg<4|U$8r#wLKMIU3Yy|k~;Q@`PA6)7kMQbTe$9Dei7>=(Pct=GSw@!y)B_m{WU zb|61OH-BJ|UgSwX^5;h~khL@z*P@#5$uRPN@UkyMPlcnFe&Kuc`^uAb*?8;8hN|B{ z5A!YA#JuU|ji0<M_|a3f8!y^YTQ8e(`^Jy9oz#NVa?%c@9VfLRwVkvJY1c`6koKIk z4{6^?2apb&)PdA-(jlZndF#SEKAQfGXS5jFFcoIhGs$HpQsY*BS#K@3bT*!(YOFGA z*iw@Xgi(^^85^WYHd2};Lnf|8l8Qm9?y=lnDLqS!DlZXfN=H+eNar{kkC@GwU|F6| zdgUD_aXy*r<nqd%us`3i&+mVBpAF^=cGwFXSGe0MG445oCutI^%%~Ig_~OYuW|U%I zpPfEEzj(@qxn|>BD<+kNJEqrq_75KG9s9-hxfm-m5wZF%rlqRYAUr}hcQ7a~@)6)b zh7{blepHh_Agk*Do98j<Qlo$Br|71I!Fq4}H^EH+UH=@H?eJ$}q%It=$F(9fDjgPK z1`mpw%;R^S-omMW`==-U?~T%?KmXKxI`2PGW@Ph8|8bs=y5ERQTsq*p7jt9PxH}g3 z)BS6eUH8*u&^L)yUA$-nJ=1r=#F`6cZ!#|$rF@H=B>z2saj*hm8Fg|uESj8?Z=4q` z&d0f&rX;sHe>D|pxudsXz<IqkAW=LA%^?JLN7Hxn81q#lq#mK0Cm2=;6Smv3CPSbc zmT3~z%k+qlE+J9^y@|e!-a_Ar+CSGNVcgPWQylu9cQ$-aLc^xn8VzHugjGziA!5N| zaNV$3Vy_&n4BqS5M0t>8z;y^}U`tpA#^p0mR%J5DE?GW&PyDHci8O`DBrT1JY-L7i z4gMi5L^iiqbX28A^%y(L5IBL188VsE`bq)Hbj>}M*p(S~!ON+!46!6KQyK_;IW3b= zWD-`8J>?ZiX}e4fddwzGDe$KR{s9J%)Z^Zp03&E#k~PqE>=av^gP5Ej*K~-vThtAb zh^gg3;U=ymSz<Z=7&Ak<p5O2nM{A7lRVpz8<Qj#GJb8|8+88Wq=#Bqt=|yn9cwTZ9 z<>Zo?>12{?ynZ8|Y@lK>H6<EV-9Z*;q35IuJP=1?sa(h&a<KjFG_%QAJ*B8DpH+mw z(G-o*jQ&4-TAjPFl8?)L>1@1;c4O?iMjG@y_auU038*uA92CANg3;{5E1C~dF&;?q z@DrRc=-wuB)i&kjuBOtW>Fc8jGrdd09u1UlR8=xrY{hw&sn|v>nV3rMTnWRqidAxr z>cUYLqutHvT@)0cEOe62`SO^O+~!exeYWPm<AKX~iY8S{7s+Nl3|h5j*bMIaE-e=w zGMyszx*85Dt>og)K*5J<p<5{|_O*I7RmOG^H*KMKP%R8WGINEMqiO{iq(P6a%xBr+ z_GNBizC5g6$7*7eJX?GuCKI@C)oY7O*7Q4T8<VLsys?x|Cb(-KUkHO@+ND~G%g>K* zInayFvaIq|d5N}`9CY=r*uhs4oON6)8u{QQBI3}9ntXZIRnTGu4#HD{Q!K$*F7OJR zq5_96!6_<mcm+;TfrCTtvB=?BRuDL_=D^`AaEdi>Hucw#;3YW4#*zc4sKDV%aQ=@P jMe{-V`FKdIHAM0-2>rls`%Qn(KM3#KZq!@;hrj;-d6c6; literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/templating.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/templating.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64ec329a123648e55c68591849004415e8751c14 GIT binary patch literal 4975 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%0Di0ROwClBJncdU!WPPR#m2@>L>WW2iUlHIt z%DD@-cJuiX4_Q^wBeIh03Z)RxInrj_`HJz#q~D(Han3HBfS8%Mug!TI<!jlp(m=7q z2|g+;*2)MLb)7%Jm;VtKpZ*6dO2`$+!X1iIHAf`48mWIooCoLZ8ocH_pTXkHTr&%M zlO$%M=Z%?v9>8jKW#xRQUhEeatC=3(4BY=K?C_t!OcE!2o$R_m4vv3qkm~5xt|XK= z;6J6I7r5xk1svp4|A3(i4%HkEkMO0{yd;S|D@#b;<CN^ns}a9KEn=&zqOO8M{zpL~ z9sg#KJSrqdO74``a>?lG>;yJWTg;uWhzgl~U)|&$(9J|%^pVbiGaIJ8+*oQnwySpI E->|dk+yDRo literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/wrappers.cpython-36.pyc b/venv/Lib/site-packages/flask/__pycache__/wrappers.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4bb8065f073a56ba83b6e6a0d01d4635a660d0d GIT binary patch literal 6784 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/_compat.py b/venv/Lib/site-packages/flask/_compat.py new file mode 100644 index 0000000..a3b5b9c --- /dev/null +++ b/venv/Lib/site-packages/flask/_compat.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +""" + flask._compat + ~~~~~~~~~~~~~ + + Some py2/py3 compatibility support based on a stripped down + version of six so we don't have to depend on a specific version + of it. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +import sys + +PY2 = sys.version_info[0] == 2 +_identity = lambda x: x + + +if not PY2: + text_type = str + string_types = (str,) + integer_types = (int,) + + iterkeys = lambda d: iter(d.keys()) + itervalues = lambda d: iter(d.values()) + iteritems = lambda d: iter(d.items()) + + from inspect import getfullargspec as getargspec + from io import StringIO + + def reraise(tp, value, tb=None): + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + + implements_to_string = _identity + +else: + text_type = unicode + string_types = (str, unicode) + integer_types = (int, long) + + iterkeys = lambda d: d.iterkeys() + itervalues = lambda d: d.itervalues() + iteritems = lambda d: d.iteritems() + + from inspect import getargspec + from cStringIO import StringIO + + exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') + + def implements_to_string(cls): + cls.__unicode__ = cls.__str__ + cls.__str__ = lambda x: x.__unicode__().encode('utf-8') + return cls + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a + # dummy metaclass for one level of class instantiation that replaces + # itself with the actual metaclass. + class metaclass(type): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +# Certain versions of pypy have a bug where clearing the exception stack +# breaks the __exit__ function in a very peculiar way. The second level of +# exception blocks is necessary because pypy seems to forget to check if an +# exception happened until the next bytecode instruction? +# +# Relevant PyPy bugfix commit: +# https://bitbucket.org/pypy/pypy/commits/77ecf91c635a287e88e60d8ddb0f4e9df4003301 +# According to ronan on #pypy IRC, it is released in PyPy2 2.3 and later +# versions. +# +# Ubuntu 14.04 has PyPy 2.2.1, which does exhibit this bug. +BROKEN_PYPY_CTXMGR_EXIT = False +if hasattr(sys, 'pypy_version_info'): + class _Mgr(object): + def __enter__(self): + return self + def __exit__(self, *args): + if hasattr(sys, 'exc_clear'): + # Python 3 (PyPy3) doesn't have exc_clear + sys.exc_clear() + try: + try: + with _Mgr(): + raise AssertionError() + except: + raise + except TypeError: + BROKEN_PYPY_CTXMGR_EXIT = True + except AssertionError: + pass diff --git a/venv/Lib/site-packages/flask/app.py b/venv/Lib/site-packages/flask/app.py new file mode 100644 index 0000000..87c5900 --- /dev/null +++ b/venv/Lib/site-packages/flask/app.py @@ -0,0 +1,2315 @@ +# -*- coding: utf-8 -*- +""" + flask.app + ~~~~~~~~~ + + This module implements the central WSGI application object. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +import os +import sys +import warnings +from datetime import timedelta +from functools import update_wrapper +from itertools import chain +from threading import Lock + +from werkzeug.datastructures import Headers, ImmutableDict +from werkzeug.exceptions import BadRequest, BadRequestKeyError, HTTPException, \ + InternalServerError, MethodNotAllowed, default_exceptions +from werkzeug.routing import BuildError, Map, RequestRedirect, Rule + +from . import cli, json +from ._compat import integer_types, reraise, string_types, text_type +from .config import Config, ConfigAttribute +from .ctx import AppContext, RequestContext, _AppCtxGlobals +from .globals import _request_ctx_stack, g, request, session +from .helpers import ( + _PackageBoundObject, + _endpoint_from_view_func, find_package, get_env, get_debug_flag, + get_flashed_messages, locked_cached_property, url_for, get_load_dotenv +) +from .logging import create_logger +from .sessions import SecureCookieSessionInterface +from .signals import appcontext_tearing_down, got_request_exception, \ + request_finished, request_started, request_tearing_down +from .templating import DispatchingJinjaLoader, Environment, \ + _default_template_ctx_processor +from .wrappers import Request, Response + +# a singleton sentinel value for parameter defaults +_sentinel = object() + + +def _make_timedelta(value): + if not isinstance(value, timedelta): + return timedelta(seconds=value) + return value + + +def setupmethod(f): + """Wraps a method so that it performs a check in debug mode if the + first request was already handled. + """ + def wrapper_func(self, *args, **kwargs): + if self.debug and self._got_first_request: + raise AssertionError('A setup function was called after the ' + 'first request was handled. This usually indicates a bug ' + 'in the application where a module was not imported ' + 'and decorators or other functionality was called too late.\n' + 'To fix this make sure to import all your view modules, ' + 'database models and everything related at a central place ' + 'before the application starts serving requests.') + return f(self, *args, **kwargs) + return update_wrapper(wrapper_func, f) + + +class Flask(_PackageBoundObject): + """The flask object implements a WSGI application and acts as the central + object. It is passed the name of the module or package of the + application. Once it is created it will act as a central registry for + the view functions, the URL rules, template configuration and much more. + + The name of the package is used to resolve resources from inside the + package or the folder the module is contained in depending on if the + package parameter resolves to an actual python package (a folder with + an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file). + + For more information about resource loading, see :func:`open_resource`. + + Usually you create a :class:`Flask` instance in your main module or + in the :file:`__init__.py` file of your package like this:: + + from flask import Flask + app = Flask(__name__) + + .. admonition:: About the First Parameter + + The idea of the first parameter is to give Flask an idea of what + belongs to your application. This name is used to find resources + on the filesystem, can be used by extensions to improve debugging + information and a lot more. + + So it's important what you provide there. If you are using a single + module, `__name__` is always the correct value. If you however are + using a package, it's usually recommended to hardcode the name of + your package there. + + For example if your application is defined in :file:`yourapplication/app.py` + you should create it with one of the two versions below:: + + app = Flask('yourapplication') + app = Flask(__name__.split('.')[0]) + + Why is that? The application will work even with `__name__`, thanks + to how resources are looked up. However it will make debugging more + painful. Certain extensions can make assumptions based on the + import name of your application. For example the Flask-SQLAlchemy + extension will look for the code in your application that triggered + an SQL query in debug mode. If the import name is not properly set + up, that debugging information is lost. (For example it would only + pick up SQL queries in `yourapplication.app` and not + `yourapplication.views.frontend`) + + .. versionadded:: 0.7 + The `static_url_path`, `static_folder`, and `template_folder` + parameters were added. + + .. versionadded:: 0.8 + The `instance_path` and `instance_relative_config` parameters were + added. + + .. versionadded:: 0.11 + The `root_path` parameter was added. + + .. versionadded:: 1.0 + The ``host_matching`` and ``static_host`` parameters were added. + + .. versionadded:: 1.0 + The ``subdomain_matching`` parameter was added. Subdomain + matching needs to be enabled manually now. Setting + :data:`SERVER_NAME` does not implicitly enable it. + + :param import_name: the name of the application package + :param static_url_path: can be used to specify a different path for the + static files on the web. Defaults to the name + of the `static_folder` folder. + :param static_folder: the folder with static files that should be served + at `static_url_path`. Defaults to the ``'static'`` + folder in the root path of the application. + :param static_host: the host to use when adding the static route. + Defaults to None. Required when using ``host_matching=True`` + with a ``static_folder`` configured. + :param host_matching: set ``url_map.host_matching`` attribute. + Defaults to False. + :param subdomain_matching: consider the subdomain relative to + :data:`SERVER_NAME` when matching routes. Defaults to False. + :param template_folder: the folder that contains the templates that should + be used by the application. Defaults to + ``'templates'`` folder in the root path of the + application. + :param instance_path: An alternative instance path for the application. + By default the folder ``'instance'`` next to the + package or module is assumed to be the instance + path. + :param instance_relative_config: if set to ``True`` relative filenames + for loading the config are assumed to + be relative to the instance path instead + of the application root. + :param root_path: Flask by default will automatically calculate the path + to the root of the application. In certain situations + this cannot be achieved (for instance if the package + is a Python 3 namespace package) and needs to be + manually defined. + """ + + #: The class that is used for request objects. See :class:`~flask.Request` + #: for more information. + request_class = Request + + #: The class that is used for response objects. See + #: :class:`~flask.Response` for more information. + response_class = Response + + #: The class that is used for the Jinja environment. + #: + #: .. versionadded:: 0.11 + jinja_environment = Environment + + #: The class that is used for the :data:`~flask.g` instance. + #: + #: Example use cases for a custom class: + #: + #: 1. Store arbitrary attributes on flask.g. + #: 2. Add a property for lazy per-request database connectors. + #: 3. Return None instead of AttributeError on unexpected attributes. + #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g. + #: + #: In Flask 0.9 this property was called `request_globals_class` but it + #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the + #: flask.g object is now application context scoped. + #: + #: .. versionadded:: 0.10 + app_ctx_globals_class = _AppCtxGlobals + + #: The class that is used for the ``config`` attribute of this app. + #: Defaults to :class:`~flask.Config`. + #: + #: Example use cases for a custom class: + #: + #: 1. Default values for certain config options. + #: 2. Access to config values through attributes in addition to keys. + #: + #: .. versionadded:: 0.11 + config_class = Config + + #: The testing flag. Set this to ``True`` to enable the test mode of + #: Flask extensions (and in the future probably also Flask itself). + #: For example this might activate test helpers that have an + #: additional runtime cost which should not be enabled by default. + #: + #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the + #: default it's implicitly enabled. + #: + #: This attribute can also be configured from the config with the + #: ``TESTING`` configuration key. Defaults to ``False``. + testing = ConfigAttribute('TESTING') + + #: If a secret key is set, cryptographic components can use this to + #: sign cookies and other things. Set this to a complex random value + #: when you want to use the secure cookie for instance. + #: + #: This attribute can also be configured from the config with the + #: :data:`SECRET_KEY` configuration key. Defaults to ``None``. + secret_key = ConfigAttribute('SECRET_KEY') + + #: The secure cookie uses this for the name of the session cookie. + #: + #: This attribute can also be configured from the config with the + #: ``SESSION_COOKIE_NAME`` configuration key. Defaults to ``'session'`` + session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME') + + #: A :class:`~datetime.timedelta` which is used to set the expiration + #: date of a permanent session. The default is 31 days which makes a + #: permanent session survive for roughly one month. + #: + #: This attribute can also be configured from the config with the + #: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to + #: ``timedelta(days=31)`` + permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME', + get_converter=_make_timedelta) + + #: A :class:`~datetime.timedelta` which is used as default cache_timeout + #: for the :func:`send_file` functions. The default is 12 hours. + #: + #: This attribute can also be configured from the config with the + #: ``SEND_FILE_MAX_AGE_DEFAULT`` configuration key. This configuration + #: variable can also be set with an integer value used as seconds. + #: Defaults to ``timedelta(hours=12)`` + send_file_max_age_default = ConfigAttribute('SEND_FILE_MAX_AGE_DEFAULT', + get_converter=_make_timedelta) + + #: Enable this if you want to use the X-Sendfile feature. Keep in + #: mind that the server has to support this. This only affects files + #: sent with the :func:`send_file` method. + #: + #: .. versionadded:: 0.2 + #: + #: This attribute can also be configured from the config with the + #: ``USE_X_SENDFILE`` configuration key. Defaults to ``False``. + use_x_sendfile = ConfigAttribute('USE_X_SENDFILE') + + #: The JSON encoder class to use. Defaults to :class:`~flask.json.JSONEncoder`. + #: + #: .. versionadded:: 0.10 + json_encoder = json.JSONEncoder + + #: The JSON decoder class to use. Defaults to :class:`~flask.json.JSONDecoder`. + #: + #: .. versionadded:: 0.10 + json_decoder = json.JSONDecoder + + #: Options that are passed directly to the Jinja2 environment. + jinja_options = ImmutableDict( + extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_'] + ) + + #: Default configuration parameters. + default_config = ImmutableDict({ + 'ENV': None, + 'DEBUG': None, + 'TESTING': False, + 'PROPAGATE_EXCEPTIONS': None, + 'PRESERVE_CONTEXT_ON_EXCEPTION': None, + 'SECRET_KEY': None, + 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), + 'USE_X_SENDFILE': False, + 'SERVER_NAME': None, + 'APPLICATION_ROOT': '/', + 'SESSION_COOKIE_NAME': 'session', + 'SESSION_COOKIE_DOMAIN': None, + 'SESSION_COOKIE_PATH': None, + 'SESSION_COOKIE_HTTPONLY': True, + 'SESSION_COOKIE_SECURE': False, + 'SESSION_COOKIE_SAMESITE': None, + 'SESSION_REFRESH_EACH_REQUEST': True, + 'MAX_CONTENT_LENGTH': None, + 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), + 'TRAP_BAD_REQUEST_ERRORS': None, + 'TRAP_HTTP_EXCEPTIONS': False, + 'EXPLAIN_TEMPLATE_LOADING': False, + 'PREFERRED_URL_SCHEME': 'http', + 'JSON_AS_ASCII': True, + 'JSON_SORT_KEYS': True, + 'JSONIFY_PRETTYPRINT_REGULAR': False, + 'JSONIFY_MIMETYPE': 'application/json', + 'TEMPLATES_AUTO_RELOAD': None, + 'MAX_COOKIE_SIZE': 4093, + }) + + #: The rule object to use for URL rules created. This is used by + #: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`. + #: + #: .. versionadded:: 0.7 + url_rule_class = Rule + + #: the test client that is used with when `test_client` is used. + #: + #: .. versionadded:: 0.7 + test_client_class = None + + #: The :class:`~click.testing.CliRunner` subclass, by default + #: :class:`~flask.testing.FlaskCliRunner` that is used by + #: :meth:`test_cli_runner`. Its ``__init__`` method should take a + #: Flask app object as the first argument. + #: + #: .. versionadded:: 1.0 + test_cli_runner_class = None + + #: the session interface to use. By default an instance of + #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here. + #: + #: .. versionadded:: 0.8 + session_interface = SecureCookieSessionInterface() + + # TODO remove the next three attrs when Sphinx :inherited-members: works + # https://github.com/sphinx-doc/sphinx/issues/741 + + #: The name of the package or module that this app belongs to. Do not + #: change this once it is set by the constructor. + import_name = None + + #: Location of the template files to be added to the template lookup. + #: ``None`` if templates should not be added. + template_folder = None + + #: Absolute path to the package on the filesystem. Used to look up + #: resources contained in the package. + root_path = None + + def __init__( + self, + import_name, + static_url_path=None, + static_folder='static', + static_host=None, + host_matching=False, + subdomain_matching=False, + template_folder='templates', + instance_path=None, + instance_relative_config=False, + root_path=None + ): + _PackageBoundObject.__init__( + self, + import_name, + template_folder=template_folder, + root_path=root_path + ) + + if static_url_path is not None: + self.static_url_path = static_url_path + + if static_folder is not None: + self.static_folder = static_folder + + if instance_path is None: + instance_path = self.auto_find_instance_path() + elif not os.path.isabs(instance_path): + raise ValueError( + 'If an instance path is provided it must be absolute.' + ' A relative path was given instead.' + ) + + #: Holds the path to the instance folder. + #: + #: .. versionadded:: 0.8 + self.instance_path = instance_path + + #: The configuration dictionary as :class:`Config`. This behaves + #: exactly like a regular dictionary but supports additional methods + #: to load a config from files. + self.config = self.make_config(instance_relative_config) + + #: A dictionary of all view functions registered. The keys will + #: be function names which are also used to generate URLs and + #: the values are the function objects themselves. + #: To register a view function, use the :meth:`route` decorator. + self.view_functions = {} + + #: A dictionary of all registered error handlers. The key is ``None`` + #: for error handlers active on the application, otherwise the key is + #: the name of the blueprint. Each key points to another dictionary + #: where the key is the status code of the http exception. The + #: special key ``None`` points to a list of tuples where the first item + #: is the class for the instance check and the second the error handler + #: function. + #: + #: To register an error handler, use the :meth:`errorhandler` + #: decorator. + self.error_handler_spec = {} + + #: A list of functions that are called when :meth:`url_for` raises a + #: :exc:`~werkzeug.routing.BuildError`. Each function registered here + #: is called with `error`, `endpoint` and `values`. If a function + #: returns ``None`` or raises a :exc:`BuildError` the next function is + #: tried. + #: + #: .. versionadded:: 0.9 + self.url_build_error_handlers = [] + + #: A dictionary with lists of functions that will be called at the + #: beginning of each request. The key of the dictionary is the name of + #: the blueprint this function is active for, or ``None`` for all + #: requests. To register a function, use the :meth:`before_request` + #: decorator. + self.before_request_funcs = {} + + #: A list of functions that will be called at the beginning of the + #: first request to this instance. To register a function, use the + #: :meth:`before_first_request` decorator. + #: + #: .. versionadded:: 0.8 + self.before_first_request_funcs = [] + + #: A dictionary with lists of functions that should be called after + #: each request. The key of the dictionary is the name of the blueprint + #: this function is active for, ``None`` for all requests. This can for + #: example be used to close database connections. To register a function + #: here, use the :meth:`after_request` decorator. + self.after_request_funcs = {} + + #: A dictionary with lists of functions that are called after + #: each request, even if an exception has occurred. The key of the + #: dictionary is the name of the blueprint this function is active for, + #: ``None`` for all requests. These functions are not allowed to modify + #: the request, and their return values are ignored. If an exception + #: occurred while processing the request, it gets passed to each + #: teardown_request function. To register a function here, use the + #: :meth:`teardown_request` decorator. + #: + #: .. versionadded:: 0.7 + self.teardown_request_funcs = {} + + #: A list of functions that are called when the application context + #: is destroyed. Since the application context is also torn down + #: if the request ends this is the place to store code that disconnects + #: from databases. + #: + #: .. versionadded:: 0.9 + self.teardown_appcontext_funcs = [] + + #: A dictionary with lists of functions that are called before the + #: :attr:`before_request_funcs` functions. The key of the dictionary is + #: the name of the blueprint this function is active for, or ``None`` + #: for all requests. To register a function, use + #: :meth:`url_value_preprocessor`. + #: + #: .. versionadded:: 0.7 + self.url_value_preprocessors = {} + + #: A dictionary with lists of functions that can be used as URL value + #: preprocessors. The key ``None`` here is used for application wide + #: callbacks, otherwise the key is the name of the blueprint. + #: Each of these functions has the chance to modify the dictionary + #: of URL values before they are used as the keyword arguments of the + #: view function. For each function registered this one should also + #: provide a :meth:`url_defaults` function that adds the parameters + #: automatically again that were removed that way. + #: + #: .. versionadded:: 0.7 + self.url_default_functions = {} + + #: A dictionary with list of functions that are called without argument + #: to populate the template context. The key of the dictionary is the + #: name of the blueprint this function is active for, ``None`` for all + #: requests. Each returns a dictionary that the template context is + #: updated with. To register a function here, use the + #: :meth:`context_processor` decorator. + self.template_context_processors = { + None: [_default_template_ctx_processor] + } + + #: A list of shell context processor functions that should be run + #: when a shell context is created. + #: + #: .. versionadded:: 0.11 + self.shell_context_processors = [] + + #: all the attached blueprints in a dictionary by name. Blueprints + #: can be attached multiple times so this dictionary does not tell + #: you how often they got attached. + #: + #: .. versionadded:: 0.7 + self.blueprints = {} + self._blueprint_order = [] + + #: a place where extensions can store application specific state. For + #: example this is where an extension could store database engines and + #: similar things. For backwards compatibility extensions should register + #: themselves like this:: + #: + #: if not hasattr(app, 'extensions'): + #: app.extensions = {} + #: app.extensions['extensionname'] = SomeObject() + #: + #: The key must match the name of the extension module. For example in + #: case of a "Flask-Foo" extension in `flask_foo`, the key would be + #: ``'foo'``. + #: + #: .. versionadded:: 0.7 + self.extensions = {} + + #: The :class:`~werkzeug.routing.Map` for this instance. You can use + #: this to change the routing converters after the class was created + #: but before any routes are connected. Example:: + #: + #: from werkzeug.routing import BaseConverter + #: + #: class ListConverter(BaseConverter): + #: def to_python(self, value): + #: return value.split(',') + #: def to_url(self, values): + #: return ','.join(super(ListConverter, self).to_url(value) + #: for value in values) + #: + #: app = Flask(__name__) + #: app.url_map.converters['list'] = ListConverter + self.url_map = Map() + + self.url_map.host_matching = host_matching + self.subdomain_matching = subdomain_matching + + # tracks internally if the application already handled at least one + # request. + self._got_first_request = False + self._before_request_lock = Lock() + + # Add a static route using the provided static_url_path, static_host, + # and static_folder if there is a configured static_folder. + # Note we do this without checking if static_folder exists. + # For one, it might be created while the server is running (e.g. during + # development). Also, Google App Engine stores static files somewhere + if self.has_static_folder: + assert bool(static_host) == host_matching, 'Invalid static_host/host_matching combination' + self.add_url_rule( + self.static_url_path + '/<path:filename>', + endpoint='static', + host=static_host, + view_func=self.send_static_file + ) + + #: The click command line context for this application. Commands + #: registered here show up in the :command:`flask` command once the + #: application has been discovered. The default commands are + #: provided by Flask itself and can be overridden. + #: + #: This is an instance of a :class:`click.Group` object. + self.cli = cli.AppGroup(self.name) + + @locked_cached_property + def name(self): + """The name of the application. This is usually the import name + with the difference that it's guessed from the run file if the + import name is main. This name is used as a display name when + Flask needs the name of the application. It can be set and overridden + to change the value. + + .. versionadded:: 0.8 + """ + if self.import_name == '__main__': + fn = getattr(sys.modules['__main__'], '__file__', None) + if fn is None: + return '__main__' + return os.path.splitext(os.path.basename(fn))[0] + return self.import_name + + @property + def propagate_exceptions(self): + """Returns the value of the ``PROPAGATE_EXCEPTIONS`` configuration + value in case it's set, otherwise a sensible default is returned. + + .. versionadded:: 0.7 + """ + rv = self.config['PROPAGATE_EXCEPTIONS'] + if rv is not None: + return rv + return self.testing or self.debug + + @property + def preserve_context_on_exception(self): + """Returns the value of the ``PRESERVE_CONTEXT_ON_EXCEPTION`` + configuration value in case it's set, otherwise a sensible default + is returned. + + .. versionadded:: 0.7 + """ + rv = self.config['PRESERVE_CONTEXT_ON_EXCEPTION'] + if rv is not None: + return rv + return self.debug + + @locked_cached_property + def logger(self): + """The ``'flask.app'`` logger, a standard Python + :class:`~logging.Logger`. + + In debug mode, the logger's :attr:`~logging.Logger.level` will be set + to :data:`~logging.DEBUG`. + + If there are no handlers configured, a default handler will be added. + See :ref:`logging` for more information. + + .. versionchanged:: 1.0 + Behavior was simplified. The logger is always named + ``flask.app``. The level is only set during configuration, it + doesn't check ``app.debug`` each time. Only one format is used, + not different ones depending on ``app.debug``. No handlers are + removed, and a handler is only added if no handlers are already + configured. + + .. versionadded:: 0.3 + """ + return create_logger(self) + + @locked_cached_property + def jinja_env(self): + """The Jinja2 environment used to load templates.""" + return self.create_jinja_environment() + + @property + def got_first_request(self): + """This attribute is set to ``True`` if the application started + handling the first request. + + .. versionadded:: 0.8 + """ + return self._got_first_request + + def make_config(self, instance_relative=False): + """Used to create the config attribute by the Flask constructor. + The `instance_relative` parameter is passed in from the constructor + of Flask (there named `instance_relative_config`) and indicates if + the config should be relative to the instance path or the root path + of the application. + + .. versionadded:: 0.8 + """ + root_path = self.root_path + if instance_relative: + root_path = self.instance_path + defaults = dict(self.default_config) + defaults['ENV'] = get_env() + defaults['DEBUG'] = get_debug_flag() + return self.config_class(root_path, defaults) + + def auto_find_instance_path(self): + """Tries to locate the instance path if it was not provided to the + constructor of the application class. It will basically calculate + the path to a folder named ``instance`` next to your main file or + the package. + + .. versionadded:: 0.8 + """ + prefix, package_path = find_package(self.import_name) + if prefix is None: + return os.path.join(package_path, 'instance') + return os.path.join(prefix, 'var', self.name + '-instance') + + def open_instance_resource(self, resource, mode='rb'): + """Opens a resource from the application's instance folder + (:attr:`instance_path`). Otherwise works like + :meth:`open_resource`. Instance resources can also be opened for + writing. + + :param resource: the name of the resource. To access resources within + subfolders use forward slashes as separator. + :param mode: resource file opening mode, default is 'rb'. + """ + return open(os.path.join(self.instance_path, resource), mode) + + def _get_templates_auto_reload(self): + """Reload templates when they are changed. Used by + :meth:`create_jinja_environment`. + + This attribute can be configured with :data:`TEMPLATES_AUTO_RELOAD`. If + not set, it will be enabled in debug mode. + + .. versionadded:: 1.0 + This property was added but the underlying config and behavior + already existed. + """ + rv = self.config['TEMPLATES_AUTO_RELOAD'] + return rv if rv is not None else self.debug + + def _set_templates_auto_reload(self, value): + self.config['TEMPLATES_AUTO_RELOAD'] = value + + templates_auto_reload = property( + _get_templates_auto_reload, _set_templates_auto_reload + ) + del _get_templates_auto_reload, _set_templates_auto_reload + + def create_jinja_environment(self): + """Creates the Jinja2 environment based on :attr:`jinja_options` + and :meth:`select_jinja_autoescape`. Since 0.7 this also adds + the Jinja2 globals and filters after initialization. Override + this function to customize the behavior. + + .. versionadded:: 0.5 + .. versionchanged:: 0.11 + ``Environment.auto_reload`` set in accordance with + ``TEMPLATES_AUTO_RELOAD`` configuration option. + """ + options = dict(self.jinja_options) + + if 'autoescape' not in options: + options['autoescape'] = self.select_jinja_autoescape + + if 'auto_reload' not in options: + options['auto_reload'] = self.templates_auto_reload + + rv = self.jinja_environment(self, **options) + rv.globals.update( + url_for=url_for, + get_flashed_messages=get_flashed_messages, + config=self.config, + # request, session and g are normally added with the + # context processor for efficiency reasons but for imported + # templates we also want the proxies in there. + request=request, + session=session, + g=g + ) + rv.filters['tojson'] = json.tojson_filter + return rv + + def create_global_jinja_loader(self): + """Creates the loader for the Jinja2 environment. Can be used to + override just the loader and keeping the rest unchanged. It's + discouraged to override this function. Instead one should override + the :meth:`jinja_loader` function instead. + + The global loader dispatches between the loaders of the application + and the individual blueprints. + + .. versionadded:: 0.7 + """ + return DispatchingJinjaLoader(self) + + def select_jinja_autoescape(self, filename): + """Returns ``True`` if autoescaping should be active for the given + template name. If no template name is given, returns `True`. + + .. versionadded:: 0.5 + """ + if filename is None: + return True + return filename.endswith(('.html', '.htm', '.xml', '.xhtml')) + + def update_template_context(self, context): + """Update the template context with some commonly used variables. + This injects request, session, config and g into the template + context as well as everything template context processors want + to inject. Note that the as of Flask 0.6, the original values + in the context will not be overridden if a context processor + decides to return a value with the same key. + + :param context: the context as a dictionary that is updated in place + to add extra variables. + """ + funcs = self.template_context_processors[None] + reqctx = _request_ctx_stack.top + if reqctx is not None: + bp = reqctx.request.blueprint + if bp is not None and bp in self.template_context_processors: + funcs = chain(funcs, self.template_context_processors[bp]) + orig_ctx = context.copy() + for func in funcs: + context.update(func()) + # make sure the original values win. This makes it possible to + # easier add new variables in context processors without breaking + # existing views. + context.update(orig_ctx) + + def make_shell_context(self): + """Returns the shell context for an interactive shell for this + application. This runs all the registered shell context + processors. + + .. versionadded:: 0.11 + """ + rv = {'app': self, 'g': g} + for processor in self.shell_context_processors: + rv.update(processor()) + return rv + + #: What environment the app is running in. Flask and extensions may + #: enable behaviors based on the environment, such as enabling debug + #: mode. This maps to the :data:`ENV` config key. This is set by the + #: :envvar:`FLASK_ENV` environment variable and may not behave as + #: expected if set in code. + #: + #: **Do not enable development when deploying in production.** + #: + #: Default: ``'production'`` + env = ConfigAttribute('ENV') + + def _get_debug(self): + return self.config['DEBUG'] + + def _set_debug(self, value): + self.config['DEBUG'] = value + self.jinja_env.auto_reload = self.templates_auto_reload + + #: Whether debug mode is enabled. When using ``flask run`` to start + #: the development server, an interactive debugger will be shown for + #: unhandled exceptions, and the server will be reloaded when code + #: changes. This maps to the :data:`DEBUG` config key. This is + #: enabled when :attr:`env` is ``'development'`` and is overridden + #: by the ``FLASK_DEBUG`` environment variable. It may not behave as + #: expected if set in code. + #: + #: **Do not enable debug mode when deploying in production.** + #: + #: Default: ``True`` if :attr:`env` is ``'development'``, or + #: ``False`` otherwise. + debug = property(_get_debug, _set_debug) + del _get_debug, _set_debug + + def run(self, host=None, port=None, debug=None, + load_dotenv=True, **options): + """Runs the application on a local development server. + + Do not use ``run()`` in a production setting. It is not intended to + meet security and performance requirements for a production server. + Instead, see :ref:`deployment` for WSGI server recommendations. + + If the :attr:`debug` flag is set the server will automatically reload + for code changes and show a debugger in case an exception happened. + + If you want to run the application in debug mode, but disable the + code execution on the interactive debugger, you can pass + ``use_evalex=False`` as parameter. This will keep the debugger's + traceback screen active, but disable code execution. + + It is not recommended to use this function for development with + automatic reloading as this is badly supported. Instead you should + be using the :command:`flask` command line script's ``run`` support. + + .. admonition:: Keep in Mind + + Flask will suppress any server error with a generic error page + unless it is in debug mode. As such to enable just the + interactive debugger without the code reloading, you have to + invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``. + Setting ``use_debugger`` to ``True`` without being in debug mode + won't catch any exceptions because there won't be any to + catch. + + :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to + have the server available externally as well. Defaults to + ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable + if present. + :param port: the port of the webserver. Defaults to ``5000`` or the + port defined in the ``SERVER_NAME`` config variable if present. + :param debug: if given, enable or disable debug mode. See + :attr:`debug`. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param options: the options to be forwarded to the underlying Werkzeug + server. See :func:`werkzeug.serving.run_simple` for more + information. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment + variables from :file:`.env` and :file:`.flaskenv` files. + + If set, the :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG` + environment variables will override :attr:`env` and + :attr:`debug`. + + Threaded mode is enabled by default. + + .. versionchanged:: 0.10 + The default port is now picked from the ``SERVER_NAME`` + variable. + """ + # Change this into a no-op if the server is invoked from the + # command line. Have a look at cli.py for more information. + if os.environ.get('FLASK_RUN_FROM_CLI') == 'true': + from .debughelpers import explain_ignored_app_run + explain_ignored_app_run() + return + + if get_load_dotenv(load_dotenv): + cli.load_dotenv() + + # if set, let env vars override previous values + if 'FLASK_ENV' in os.environ: + self.env = get_env() + self.debug = get_debug_flag() + elif 'FLASK_DEBUG' in os.environ: + self.debug = get_debug_flag() + + # debug passed to method overrides all other sources + if debug is not None: + self.debug = bool(debug) + + _host = '127.0.0.1' + _port = 5000 + server_name = self.config.get('SERVER_NAME') + sn_host, sn_port = None, None + + if server_name: + sn_host, _, sn_port = server_name.partition(':') + + host = host or sn_host or _host + port = int(port or sn_port or _port) + + options.setdefault('use_reloader', self.debug) + options.setdefault('use_debugger', self.debug) + options.setdefault('threaded', True) + + cli.show_server_banner(self.env, self.debug, self.name, False) + + from werkzeug.serving import run_simple + + try: + run_simple(host, port, self, **options) + finally: + # reset the first request information if the development server + # reset normally. This makes it possible to restart the server + # without reloader and that stuff from an interactive shell. + self._got_first_request = False + + def test_client(self, use_cookies=True, **kwargs): + """Creates a test client for this application. For information + about unit testing head over to :ref:`testing`. + + Note that if you are testing for assertions or exceptions in your + application code, you must set ``app.testing = True`` in order for the + exceptions to propagate to the test client. Otherwise, the exception + will be handled by the application (not visible to the test client) and + the only indication of an AssertionError or other exception will be a + 500 status code response to the test client. See the :attr:`testing` + attribute. For example:: + + app.testing = True + client = app.test_client() + + The test client can be used in a ``with`` block to defer the closing down + of the context until the end of the ``with`` block. This is useful if + you want to access the context locals for testing:: + + with app.test_client() as c: + rv = c.get('/?vodka=42') + assert request.args['vodka'] == '42' + + Additionally, you may pass optional keyword arguments that will then + be passed to the application's :attr:`test_client_class` constructor. + For example:: + + from flask.testing import FlaskClient + + class CustomClient(FlaskClient): + def __init__(self, *args, **kwargs): + self._authentication = kwargs.pop("authentication") + super(CustomClient,self).__init__( *args, **kwargs) + + app.test_client_class = CustomClient + client = app.test_client(authentication='Basic ....') + + See :class:`~flask.testing.FlaskClient` for more information. + + .. versionchanged:: 0.4 + added support for ``with`` block usage for the client. + + .. versionadded:: 0.7 + The `use_cookies` parameter was added as well as the ability + to override the client to be used by setting the + :attr:`test_client_class` attribute. + + .. versionchanged:: 0.11 + Added `**kwargs` to support passing additional keyword arguments to + the constructor of :attr:`test_client_class`. + """ + cls = self.test_client_class + if cls is None: + from flask.testing import FlaskClient as cls + return cls(self, self.response_class, use_cookies=use_cookies, **kwargs) + + def test_cli_runner(self, **kwargs): + """Create a CLI runner for testing CLI commands. + See :ref:`testing-cli`. + + Returns an instance of :attr:`test_cli_runner_class`, by default + :class:`~flask.testing.FlaskCliRunner`. The Flask app object is + passed as the first argument. + + .. versionadded:: 1.0 + """ + cls = self.test_cli_runner_class + + if cls is None: + from flask.testing import FlaskCliRunner as cls + + return cls(self, **kwargs) + + def open_session(self, request): + """Creates or opens a new session. Default implementation stores all + session data in a signed cookie. This requires that the + :attr:`secret_key` is set. Instead of overriding this method + we recommend replacing the :class:`session_interface`. + + .. deprecated: 1.0 + Will be removed in 1.1. Use ``session_interface.open_session`` + instead. + + :param request: an instance of :attr:`request_class`. + """ + + warnings.warn(DeprecationWarning( + '"open_session" is deprecated and will be removed in 1.1. Use' + ' "session_interface.open_session" instead.' + )) + return self.session_interface.open_session(self, request) + + def save_session(self, session, response): + """Saves the session if it needs updates. For the default + implementation, check :meth:`open_session`. Instead of overriding this + method we recommend replacing the :class:`session_interface`. + + .. deprecated: 1.0 + Will be removed in 1.1. Use ``session_interface.save_session`` + instead. + + :param session: the session to be saved (a + :class:`~werkzeug.contrib.securecookie.SecureCookie` + object) + :param response: an instance of :attr:`response_class` + """ + + warnings.warn(DeprecationWarning( + '"save_session" is deprecated and will be removed in 1.1. Use' + ' "session_interface.save_session" instead.' + )) + return self.session_interface.save_session(self, session, response) + + def make_null_session(self): + """Creates a new instance of a missing session. Instead of overriding + this method we recommend replacing the :class:`session_interface`. + + .. deprecated: 1.0 + Will be removed in 1.1. Use ``session_interface.make_null_session`` + instead. + + .. versionadded:: 0.7 + """ + + warnings.warn(DeprecationWarning( + '"make_null_session" is deprecated and will be removed in 1.1. Use' + ' "session_interface.make_null_session" instead.' + )) + return self.session_interface.make_null_session(self) + + @setupmethod + def register_blueprint(self, blueprint, **options): + """Register a :class:`~flask.Blueprint` on the application. Keyword + arguments passed to this method will override the defaults set on the + blueprint. + + Calls the blueprint's :meth:`~flask.Blueprint.register` method after + recording the blueprint in the application's :attr:`blueprints`. + + :param blueprint: The blueprint to register. + :param url_prefix: Blueprint routes will be prefixed with this. + :param subdomain: Blueprint routes will match on this subdomain. + :param url_defaults: Blueprint routes will use these default values for + view arguments. + :param options: Additional keyword arguments are passed to + :class:`~flask.blueprints.BlueprintSetupState`. They can be + accessed in :meth:`~flask.Blueprint.record` callbacks. + + .. versionadded:: 0.7 + """ + first_registration = False + + if blueprint.name in self.blueprints: + assert self.blueprints[blueprint.name] is blueprint, ( + 'A name collision occurred between blueprints %r and %r. Both' + ' share the same name "%s". Blueprints that are created on the' + ' fly need unique names.' % ( + blueprint, self.blueprints[blueprint.name], blueprint.name + ) + ) + else: + self.blueprints[blueprint.name] = blueprint + self._blueprint_order.append(blueprint) + first_registration = True + + blueprint.register(self, options, first_registration) + + def iter_blueprints(self): + """Iterates over all blueprints by the order they were registered. + + .. versionadded:: 0.11 + """ + return iter(self._blueprint_order) + + @setupmethod + def add_url_rule(self, rule, endpoint=None, view_func=None, + provide_automatic_options=None, **options): + """Connects a URL rule. Works exactly like the :meth:`route` + decorator. If a view_func is provided it will be registered with the + endpoint. + + Basically this example:: + + @app.route('/') + def index(): + pass + + Is equivalent to the following:: + + def index(): + pass + app.add_url_rule('/', 'index', index) + + If the view_func is not provided you will need to connect the endpoint + to a view function like so:: + + app.view_functions['index'] = index + + Internally :meth:`route` invokes :meth:`add_url_rule` so if you want + to customize the behavior via subclassing you only need to change + this method. + + For more information refer to :ref:`url-route-registrations`. + + .. versionchanged:: 0.2 + `view_func` parameter added. + + .. versionchanged:: 0.6 + ``OPTIONS`` is added automatically as method. + + :param rule: the URL rule as string + :param endpoint: the endpoint for the registered URL rule. Flask + itself assumes the name of the view function as + endpoint + :param view_func: the function to call when serving a request to the + provided endpoint + :param provide_automatic_options: controls whether the ``OPTIONS`` + method should be added automatically. This can also be controlled + by setting the ``view_func.provide_automatic_options = False`` + before adding the rule. + :param options: the options to be forwarded to the underlying + :class:`~werkzeug.routing.Rule` object. A change + to Werkzeug is handling of method options. methods + is a list of methods this rule should be limited + to (``GET``, ``POST`` etc.). By default a rule + just listens for ``GET`` (and implicitly ``HEAD``). + Starting with Flask 0.6, ``OPTIONS`` is implicitly + added and handled by the standard request handling. + """ + if endpoint is None: + endpoint = _endpoint_from_view_func(view_func) + options['endpoint'] = endpoint + methods = options.pop('methods', None) + + # if the methods are not given and the view_func object knows its + # methods we can use that instead. If neither exists, we go with + # a tuple of only ``GET`` as default. + if methods is None: + methods = getattr(view_func, 'methods', None) or ('GET',) + if isinstance(methods, string_types): + raise TypeError('Allowed methods have to be iterables of strings, ' + 'for example: @app.route(..., methods=["POST"])') + methods = set(item.upper() for item in methods) + + # Methods that should always be added + required_methods = set(getattr(view_func, 'required_methods', ())) + + # starting with Flask 0.8 the view_func object can disable and + # force-enable the automatic options handling. + if provide_automatic_options is None: + provide_automatic_options = getattr(view_func, + 'provide_automatic_options', None) + + if provide_automatic_options is None: + if 'OPTIONS' not in methods: + provide_automatic_options = True + required_methods.add('OPTIONS') + else: + provide_automatic_options = False + + # Add the required methods now. + methods |= required_methods + + rule = self.url_rule_class(rule, methods=methods, **options) + rule.provide_automatic_options = provide_automatic_options + + self.url_map.add(rule) + if view_func is not None: + old_func = self.view_functions.get(endpoint) + if old_func is not None and old_func != view_func: + raise AssertionError('View function mapping is overwriting an ' + 'existing endpoint function: %s' % endpoint) + self.view_functions[endpoint] = view_func + + def route(self, rule, **options): + """A decorator that is used to register a view function for a + given URL rule. This does the same thing as :meth:`add_url_rule` + but is intended for decorator usage:: + + @app.route('/') + def index(): + return 'Hello World' + + For more information refer to :ref:`url-route-registrations`. + + :param rule: the URL rule as string + :param endpoint: the endpoint for the registered URL rule. Flask + itself assumes the name of the view function as + endpoint + :param options: the options to be forwarded to the underlying + :class:`~werkzeug.routing.Rule` object. A change + to Werkzeug is handling of method options. methods + is a list of methods this rule should be limited + to (``GET``, ``POST`` etc.). By default a rule + just listens for ``GET`` (and implicitly ``HEAD``). + Starting with Flask 0.6, ``OPTIONS`` is implicitly + added and handled by the standard request handling. + """ + def decorator(f): + endpoint = options.pop('endpoint', None) + self.add_url_rule(rule, endpoint, f, **options) + return f + return decorator + + @setupmethod + def endpoint(self, endpoint): + """A decorator to register a function as an endpoint. + Example:: + + @app.endpoint('example.endpoint') + def example(): + return "example" + + :param endpoint: the name of the endpoint + """ + def decorator(f): + self.view_functions[endpoint] = f + return f + return decorator + + @staticmethod + def _get_exc_class_and_code(exc_class_or_code): + """Ensure that we register only exceptions as handler keys""" + if isinstance(exc_class_or_code, integer_types): + exc_class = default_exceptions[exc_class_or_code] + else: + exc_class = exc_class_or_code + + assert issubclass(exc_class, Exception) + + if issubclass(exc_class, HTTPException): + return exc_class, exc_class.code + else: + return exc_class, None + + @setupmethod + def errorhandler(self, code_or_exception): + """Register a function to handle errors by code or exception class. + + A decorator that is used to register a function given an + error code. Example:: + + @app.errorhandler(404) + def page_not_found(error): + return 'This page does not exist', 404 + + You can also register handlers for arbitrary exceptions:: + + @app.errorhandler(DatabaseError) + def special_exception_handler(error): + return 'Database connection failed', 500 + + .. versionadded:: 0.7 + Use :meth:`register_error_handler` instead of modifying + :attr:`error_handler_spec` directly, for application wide error + handlers. + + .. versionadded:: 0.7 + One can now additionally also register custom exception types + that do not necessarily have to be a subclass of the + :class:`~werkzeug.exceptions.HTTPException` class. + + :param code_or_exception: the code as integer for the handler, or + an arbitrary exception + """ + def decorator(f): + self._register_error_handler(None, code_or_exception, f) + return f + return decorator + + @setupmethod + def register_error_handler(self, code_or_exception, f): + """Alternative error attach function to the :meth:`errorhandler` + decorator that is more straightforward to use for non decorator + usage. + + .. versionadded:: 0.7 + """ + self._register_error_handler(None, code_or_exception, f) + + @setupmethod + def _register_error_handler(self, key, code_or_exception, f): + """ + :type key: None|str + :type code_or_exception: int|T<=Exception + :type f: callable + """ + if isinstance(code_or_exception, HTTPException): # old broken behavior + raise ValueError( + 'Tried to register a handler for an exception instance {0!r}.' + ' Handlers can only be registered for exception classes or' + ' HTTP error codes.'.format(code_or_exception) + ) + + try: + exc_class, code = self._get_exc_class_and_code(code_or_exception) + except KeyError: + raise KeyError( + "'{0}' is not a recognized HTTP error code. Use a subclass of" + " HTTPException with that code instead.".format(code_or_exception) + ) + + handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {}) + handlers[exc_class] = f + + @setupmethod + def template_filter(self, name=None): + """A decorator that is used to register custom template filter. + You can specify a name for the filter, otherwise the function + name will be used. Example:: + + @app.template_filter() + def reverse(s): + return s[::-1] + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + def decorator(f): + self.add_template_filter(f, name=name) + return f + return decorator + + @setupmethod + def add_template_filter(self, f, name=None): + """Register a custom template filter. Works exactly like the + :meth:`template_filter` decorator. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + self.jinja_env.filters[name or f.__name__] = f + + @setupmethod + def template_test(self, name=None): + """A decorator that is used to register custom template test. + You can specify a name for the test, otherwise the function + name will be used. Example:: + + @app.template_test() + def is_prime(n): + if n == 2: + return True + for i in range(2, int(math.ceil(math.sqrt(n))) + 1): + if n % i == 0: + return False + return True + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + def decorator(f): + self.add_template_test(f, name=name) + return f + return decorator + + @setupmethod + def add_template_test(self, f, name=None): + """Register a custom template test. Works exactly like the + :meth:`template_test` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + self.jinja_env.tests[name or f.__name__] = f + + @setupmethod + def template_global(self, name=None): + """A decorator that is used to register a custom template global function. + You can specify a name for the global function, otherwise the function + name will be used. Example:: + + @app.template_global() + def double(n): + return 2 * n + + .. versionadded:: 0.10 + + :param name: the optional name of the global function, otherwise the + function name will be used. + """ + def decorator(f): + self.add_template_global(f, name=name) + return f + return decorator + + @setupmethod + def add_template_global(self, f, name=None): + """Register a custom template global function. Works exactly like the + :meth:`template_global` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the global function, otherwise the + function name will be used. + """ + self.jinja_env.globals[name or f.__name__] = f + + @setupmethod + def before_request(self, f): + """Registers a function to run before each request. + + For example, this can be used to open a database connection, or to load + the logged in user from the session. + + The function will be called without any arguments. If it returns a + non-None value, the value is handled as if it was the return value from + the view, and further request handling is stopped. + """ + self.before_request_funcs.setdefault(None, []).append(f) + return f + + @setupmethod + def before_first_request(self, f): + """Registers a function to be run before the first request to this + instance of the application. + + The function will be called without any arguments and its return + value is ignored. + + .. versionadded:: 0.8 + """ + self.before_first_request_funcs.append(f) + return f + + @setupmethod + def after_request(self, f): + """Register a function to be run after each request. + + Your function must take one parameter, an instance of + :attr:`response_class` and return a new response object or the + same (see :meth:`process_response`). + + As of Flask 0.7 this function might not be executed at the end of the + request in case an unhandled exception occurred. + """ + self.after_request_funcs.setdefault(None, []).append(f) + return f + + @setupmethod + def teardown_request(self, f): + """Register a function to be run at the end of each request, + regardless of whether there was an exception or not. These functions + are executed when the request context is popped, even if not an + actual request was performed. + + Example:: + + ctx = app.test_request_context() + ctx.push() + ... + ctx.pop() + + When ``ctx.pop()`` is executed in the above example, the teardown + functions are called just before the request context moves from the + stack of active contexts. This becomes relevant if you are using + such constructs in tests. + + Generally teardown functions must take every necessary step to avoid + that they will fail. If they do execute code that might fail they + will have to surround the execution of these code by try/except + statements and log occurring errors. + + When a teardown function was called because of an exception it will + be passed an error object. + + The return values of teardown functions are ignored. + + .. admonition:: Debug Note + + In debug mode Flask will not tear down a request on an exception + immediately. Instead it will keep it alive so that the interactive + debugger can still access it. This behavior can be controlled + by the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. + """ + self.teardown_request_funcs.setdefault(None, []).append(f) + return f + + @setupmethod + def teardown_appcontext(self, f): + """Registers a function to be called when the application context + ends. These functions are typically also called when the request + context is popped. + + Example:: + + ctx = app.app_context() + ctx.push() + ... + ctx.pop() + + When ``ctx.pop()`` is executed in the above example, the teardown + functions are called just before the app context moves from the + stack of active contexts. This becomes relevant if you are using + such constructs in tests. + + Since a request context typically also manages an application + context it would also be called when you pop a request context. + + When a teardown function was called because of an unhandled exception + it will be passed an error object. If an :meth:`errorhandler` is + registered, it will handle the exception and the teardown will not + receive it. + + The return values of teardown functions are ignored. + + .. versionadded:: 0.9 + """ + self.teardown_appcontext_funcs.append(f) + return f + + @setupmethod + def context_processor(self, f): + """Registers a template context processor function.""" + self.template_context_processors[None].append(f) + return f + + @setupmethod + def shell_context_processor(self, f): + """Registers a shell context processor function. + + .. versionadded:: 0.11 + """ + self.shell_context_processors.append(f) + return f + + @setupmethod + def url_value_preprocessor(self, f): + """Register a URL value preprocessor function for all view + functions in the application. These functions will be called before the + :meth:`before_request` functions. + + The function can modify the values captured from the matched url before + they are passed to the view. For example, this can be used to pop a + common language code value and place it in ``g`` rather than pass it to + every view. + + The function is passed the endpoint name and values dict. The return + value is ignored. + """ + self.url_value_preprocessors.setdefault(None, []).append(f) + return f + + @setupmethod + def url_defaults(self, f): + """Callback function for URL defaults for all view functions of the + application. It's called with the endpoint and values and should + update the values passed in place. + """ + self.url_default_functions.setdefault(None, []).append(f) + return f + + def _find_error_handler(self, e): + """Return a registered error handler for an exception in this order: + blueprint handler for a specific code, app handler for a specific code, + blueprint handler for an exception class, app handler for an exception + class, or ``None`` if a suitable handler is not found. + """ + exc_class, code = self._get_exc_class_and_code(type(e)) + + for name, c in ( + (request.blueprint, code), (None, code), + (request.blueprint, None), (None, None) + ): + handler_map = self.error_handler_spec.setdefault(name, {}).get(c) + + if not handler_map: + continue + + for cls in exc_class.__mro__: + handler = handler_map.get(cls) + + if handler is not None: + return handler + + def handle_http_exception(self, e): + """Handles an HTTP exception. By default this will invoke the + registered error handlers and fall back to returning the + exception as response. + + .. versionadded:: 0.3 + """ + # Proxy exceptions don't have error codes. We want to always return + # those unchanged as errors + if e.code is None: + return e + + handler = self._find_error_handler(e) + if handler is None: + return e + return handler(e) + + def trap_http_exception(self, e): + """Checks if an HTTP exception should be trapped or not. By default + this will return ``False`` for all exceptions except for a bad request + key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to ``True``. It + also returns ``True`` if ``TRAP_HTTP_EXCEPTIONS`` is set to ``True``. + + This is called for all HTTP exceptions raised by a view function. + If it returns ``True`` for any exception the error handler for this + exception is not called and it shows up as regular exception in the + traceback. This is helpful for debugging implicitly raised HTTP + exceptions. + + .. versionchanged:: 1.0 + Bad request errors are not trapped by default in debug mode. + + .. versionadded:: 0.8 + """ + if self.config['TRAP_HTTP_EXCEPTIONS']: + return True + + trap_bad_request = self.config['TRAP_BAD_REQUEST_ERRORS'] + + # if unset, trap key errors in debug mode + if ( + trap_bad_request is None and self.debug + and isinstance(e, BadRequestKeyError) + ): + return True + + if trap_bad_request: + return isinstance(e, BadRequest) + + return False + + def handle_user_exception(self, e): + """This method is called whenever an exception occurs that should be + handled. A special case are + :class:`~werkzeug.exception.HTTPException`\s which are forwarded by + this function to the :meth:`handle_http_exception` method. This + function will either return a response value or reraise the + exception with the same traceback. + + .. versionchanged:: 1.0 + Key errors raised from request data like ``form`` show the the bad + key in debug mode rather than a generic bad request message. + + .. versionadded:: 0.7 + """ + exc_type, exc_value, tb = sys.exc_info() + assert exc_value is e + # ensure not to trash sys.exc_info() at that point in case someone + # wants the traceback preserved in handle_http_exception. Of course + # we cannot prevent users from trashing it themselves in a custom + # trap_http_exception method so that's their fault then. + + # MultiDict passes the key to the exception, but that's ignored + # when generating the response message. Set an informative + # description for key errors in debug mode or when trapping errors. + if ( + (self.debug or self.config['TRAP_BAD_REQUEST_ERRORS']) + and isinstance(e, BadRequestKeyError) + # only set it if it's still the default description + and e.description is BadRequestKeyError.description + ): + e.description = "KeyError: '{0}'".format(*e.args) + + if isinstance(e, HTTPException) and not self.trap_http_exception(e): + return self.handle_http_exception(e) + + handler = self._find_error_handler(e) + + if handler is None: + reraise(exc_type, exc_value, tb) + return handler(e) + + def handle_exception(self, e): + """Default exception handling that kicks in when an exception + occurs that is not caught. In debug mode the exception will + be re-raised immediately, otherwise it is logged and the handler + for a 500 internal server error is used. If no such handler + exists, a default 500 internal server error message is displayed. + + .. versionadded:: 0.3 + """ + exc_type, exc_value, tb = sys.exc_info() + + got_request_exception.send(self, exception=e) + handler = self._find_error_handler(InternalServerError()) + + if self.propagate_exceptions: + # if we want to repropagate the exception, we can attempt to + # raise it with the whole traceback in case we can do that + # (the function was actually called from the except part) + # otherwise, we just raise the error again + if exc_value is e: + reraise(exc_type, exc_value, tb) + else: + raise e + + self.log_exception((exc_type, exc_value, tb)) + if handler is None: + return InternalServerError() + return self.finalize_request(handler(e), from_error_handler=True) + + def log_exception(self, exc_info): + """Logs an exception. This is called by :meth:`handle_exception` + if debugging is disabled and right before the handler is called. + The default implementation logs the exception as error on the + :attr:`logger`. + + .. versionadded:: 0.8 + """ + self.logger.error('Exception on %s [%s]' % ( + request.path, + request.method + ), exc_info=exc_info) + + def raise_routing_exception(self, request): + """Exceptions that are recording during routing are reraised with + this method. During debug we are not reraising redirect requests + for non ``GET``, ``HEAD``, or ``OPTIONS`` requests and we're raising + a different error instead to help debug situations. + + :internal: + """ + if not self.debug \ + or not isinstance(request.routing_exception, RequestRedirect) \ + or request.method in ('GET', 'HEAD', 'OPTIONS'): + raise request.routing_exception + + from .debughelpers import FormDataRoutingRedirect + raise FormDataRoutingRedirect(request) + + def dispatch_request(self): + """Does the request dispatching. Matches the URL and returns the + return value of the view or error handler. This does not have to + be a response object. In order to convert the return value to a + proper response object, call :func:`make_response`. + + .. versionchanged:: 0.7 + This no longer does the exception handling, this code was + moved to the new :meth:`full_dispatch_request`. + """ + req = _request_ctx_stack.top.request + if req.routing_exception is not None: + self.raise_routing_exception(req) + rule = req.url_rule + # if we provide automatic options for this URL and the + # request came with the OPTIONS method, reply automatically + if getattr(rule, 'provide_automatic_options', False) \ + and req.method == 'OPTIONS': + return self.make_default_options_response() + # otherwise dispatch to the handler for that endpoint + return self.view_functions[rule.endpoint](**req.view_args) + + def full_dispatch_request(self): + """Dispatches the request and on top of that performs request + pre and postprocessing as well as HTTP exception catching and + error handling. + + .. versionadded:: 0.7 + """ + self.try_trigger_before_first_request_functions() + try: + request_started.send(self) + rv = self.preprocess_request() + if rv is None: + rv = self.dispatch_request() + except Exception as e: + rv = self.handle_user_exception(e) + return self.finalize_request(rv) + + def finalize_request(self, rv, from_error_handler=False): + """Given the return value from a view function this finalizes + the request by converting it into a response and invoking the + postprocessing functions. This is invoked for both normal + request dispatching as well as error handlers. + + Because this means that it might be called as a result of a + failure a special safe mode is available which can be enabled + with the `from_error_handler` flag. If enabled, failures in + response processing will be logged and otherwise ignored. + + :internal: + """ + response = self.make_response(rv) + try: + response = self.process_response(response) + request_finished.send(self, response=response) + except Exception: + if not from_error_handler: + raise + self.logger.exception('Request finalizing failed with an ' + 'error while handling an error') + return response + + def try_trigger_before_first_request_functions(self): + """Called before each request and will ensure that it triggers + the :attr:`before_first_request_funcs` and only exactly once per + application instance (which means process usually). + + :internal: + """ + if self._got_first_request: + return + with self._before_request_lock: + if self._got_first_request: + return + for func in self.before_first_request_funcs: + func() + self._got_first_request = True + + def make_default_options_response(self): + """This method is called to create the default ``OPTIONS`` response. + This can be changed through subclassing to change the default + behavior of ``OPTIONS`` responses. + + .. versionadded:: 0.7 + """ + adapter = _request_ctx_stack.top.url_adapter + if hasattr(adapter, 'allowed_methods'): + methods = adapter.allowed_methods() + else: + # fallback for Werkzeug < 0.7 + methods = [] + try: + adapter.match(method='--') + except MethodNotAllowed as e: + methods = e.valid_methods + except HTTPException as e: + pass + rv = self.response_class() + rv.allow.update(methods) + return rv + + def should_ignore_error(self, error): + """This is called to figure out if an error should be ignored + or not as far as the teardown system is concerned. If this + function returns ``True`` then the teardown handlers will not be + passed the error. + + .. versionadded:: 0.10 + """ + return False + + def make_response(self, rv): + """Convert the return value from a view function to an instance of + :attr:`response_class`. + + :param rv: the return value from the view function. The view function + must return a response. Returning ``None``, or the view ending + without returning, is not allowed. The following types are allowed + for ``view_rv``: + + ``str`` (``unicode`` in Python 2) + A response object is created with the string encoded to UTF-8 + as the body. + + ``bytes`` (``str`` in Python 2) + A response object is created with the bytes as the body. + + ``tuple`` + Either ``(body, status, headers)``, ``(body, status)``, or + ``(body, headers)``, where ``body`` is any of the other types + allowed here, ``status`` is a string or an integer, and + ``headers`` is a dictionary or a list of ``(key, value)`` + tuples. If ``body`` is a :attr:`response_class` instance, + ``status`` overwrites the exiting value and ``headers`` are + extended. + + :attr:`response_class` + The object is returned unchanged. + + other :class:`~werkzeug.wrappers.Response` class + The object is coerced to :attr:`response_class`. + + :func:`callable` + The function is called as a WSGI application. The result is + used to create a response object. + + .. versionchanged:: 0.9 + Previously a tuple was interpreted as the arguments for the + response object. + """ + + status = headers = None + + # unpack tuple returns + if isinstance(rv, tuple): + len_rv = len(rv) + + # a 3-tuple is unpacked directly + if len_rv == 3: + rv, status, headers = rv + # decide if a 2-tuple has status or headers + elif len_rv == 2: + if isinstance(rv[1], (Headers, dict, tuple, list)): + rv, headers = rv + else: + rv, status = rv + # other sized tuples are not allowed + else: + raise TypeError( + 'The view function did not return a valid response tuple.' + ' The tuple must have the form (body, status, headers),' + ' (body, status), or (body, headers).' + ) + + # the body must not be None + if rv is None: + raise TypeError( + 'The view function did not return a valid response. The' + ' function either returned None or ended without a return' + ' statement.' + ) + + # make sure the body is an instance of the response class + if not isinstance(rv, self.response_class): + if isinstance(rv, (text_type, bytes, bytearray)): + # let the response class set the status and headers instead of + # waiting to do it manually, so that the class can handle any + # special logic + rv = self.response_class(rv, status=status, headers=headers) + status = headers = None + else: + # evaluate a WSGI callable, or coerce a different response + # class to the correct type + try: + rv = self.response_class.force_type(rv, request.environ) + except TypeError as e: + new_error = TypeError( + '{e}\nThe view function did not return a valid' + ' response. The return type must be a string, tuple,' + ' Response instance, or WSGI callable, but it was a' + ' {rv.__class__.__name__}.'.format(e=e, rv=rv) + ) + reraise(TypeError, new_error, sys.exc_info()[2]) + + # prefer the status if it was provided + if status is not None: + if isinstance(status, (text_type, bytes, bytearray)): + rv.status = status + else: + rv.status_code = status + + # extend existing headers with provided headers + if headers: + rv.headers.extend(headers) + + return rv + + def create_url_adapter(self, request): + """Creates a URL adapter for the given request. The URL adapter + is created at a point where the request context is not yet set + up so the request is passed explicitly. + + .. versionadded:: 0.6 + + .. versionchanged:: 0.9 + This can now also be called without a request object when the + URL adapter is created for the application context. + + .. versionchanged:: 1.0 + :data:`SERVER_NAME` no longer implicitly enables subdomain + matching. Use :attr:`subdomain_matching` instead. + """ + if request is not None: + # If subdomain matching is disabled (the default), use the + # default subdomain in all cases. This should be the default + # in Werkzeug but it currently does not have that feature. + subdomain = ((self.url_map.default_subdomain or None) + if not self.subdomain_matching else None) + return self.url_map.bind_to_environ( + request.environ, + server_name=self.config['SERVER_NAME'], + subdomain=subdomain) + # We need at the very least the server name to be set for this + # to work. + if self.config['SERVER_NAME'] is not None: + return self.url_map.bind( + self.config['SERVER_NAME'], + script_name=self.config['APPLICATION_ROOT'], + url_scheme=self.config['PREFERRED_URL_SCHEME']) + + def inject_url_defaults(self, endpoint, values): + """Injects the URL defaults for the given endpoint directly into + the values dictionary passed. This is used internally and + automatically called on URL building. + + .. versionadded:: 0.7 + """ + funcs = self.url_default_functions.get(None, ()) + if '.' in endpoint: + bp = endpoint.rsplit('.', 1)[0] + funcs = chain(funcs, self.url_default_functions.get(bp, ())) + for func in funcs: + func(endpoint, values) + + def handle_url_build_error(self, error, endpoint, values): + """Handle :class:`~werkzeug.routing.BuildError` on :meth:`url_for`. + """ + exc_type, exc_value, tb = sys.exc_info() + for handler in self.url_build_error_handlers: + try: + rv = handler(error, endpoint, values) + if rv is not None: + return rv + except BuildError as e: + # make error available outside except block (py3) + error = e + + # At this point we want to reraise the exception. If the error is + # still the same one we can reraise it with the original traceback, + # otherwise we raise it from here. + if error is exc_value: + reraise(exc_type, exc_value, tb) + raise error + + def preprocess_request(self): + """Called before the request is dispatched. Calls + :attr:`url_value_preprocessors` registered with the app and the + current blueprint (if any). Then calls :attr:`before_request_funcs` + registered with the app and the blueprint. + + If any :meth:`before_request` handler returns a non-None value, the + value is handled as if it was the return value from the view, and + further request handling is stopped. + """ + + bp = _request_ctx_stack.top.request.blueprint + + funcs = self.url_value_preprocessors.get(None, ()) + if bp is not None and bp in self.url_value_preprocessors: + funcs = chain(funcs, self.url_value_preprocessors[bp]) + for func in funcs: + func(request.endpoint, request.view_args) + + funcs = self.before_request_funcs.get(None, ()) + if bp is not None and bp in self.before_request_funcs: + funcs = chain(funcs, self.before_request_funcs[bp]) + for func in funcs: + rv = func() + if rv is not None: + return rv + + def process_response(self, response): + """Can be overridden in order to modify the response object + before it's sent to the WSGI server. By default this will + call all the :meth:`after_request` decorated functions. + + .. versionchanged:: 0.5 + As of Flask 0.5 the functions registered for after request + execution are called in reverse order of registration. + + :param response: a :attr:`response_class` object. + :return: a new response object or the same, has to be an + instance of :attr:`response_class`. + """ + ctx = _request_ctx_stack.top + bp = ctx.request.blueprint + funcs = ctx._after_request_functions + if bp is not None and bp in self.after_request_funcs: + funcs = chain(funcs, reversed(self.after_request_funcs[bp])) + if None in self.after_request_funcs: + funcs = chain(funcs, reversed(self.after_request_funcs[None])) + for handler in funcs: + response = handler(response) + if not self.session_interface.is_null_session(ctx.session): + self.session_interface.save_session(self, ctx.session, response) + return response + + def do_teardown_request(self, exc=_sentinel): + """Called after the request is dispatched and the response is + returned, right before the request context is popped. + + This calls all functions decorated with + :meth:`teardown_request`, and :meth:`Blueprint.teardown_request` + if a blueprint handled the request. Finally, the + :data:`request_tearing_down` signal is sent. + + This is called by + :meth:`RequestContext.pop() <flask.ctx.RequestContext.pop>`, + which may be delayed during testing to maintain access to + resources. + + :param exc: An unhandled exception raised while dispatching the + request. Detected from the current exception information if + not passed. Passed to each teardown function. + + .. versionchanged:: 0.9 + Added the ``exc`` argument. + """ + if exc is _sentinel: + exc = sys.exc_info()[1] + funcs = reversed(self.teardown_request_funcs.get(None, ())) + bp = _request_ctx_stack.top.request.blueprint + if bp is not None and bp in self.teardown_request_funcs: + funcs = chain(funcs, reversed(self.teardown_request_funcs[bp])) + for func in funcs: + func(exc) + request_tearing_down.send(self, exc=exc) + + def do_teardown_appcontext(self, exc=_sentinel): + """Called right before the application context is popped. + + When handling a request, the application context is popped + after the request context. See :meth:`do_teardown_request`. + + This calls all functions decorated with + :meth:`teardown_appcontext`. Then the + :data:`appcontext_tearing_down` signal is sent. + + This is called by + :meth:`AppContext.pop() <flask.ctx.AppContext.pop>`. + + .. versionadded:: 0.9 + """ + if exc is _sentinel: + exc = sys.exc_info()[1] + for func in reversed(self.teardown_appcontext_funcs): + func(exc) + appcontext_tearing_down.send(self, exc=exc) + + def app_context(self): + """Create an :class:`~flask.ctx.AppContext`. Use as a ``with`` + block to push the context, which will make :data:`current_app` + point at this application. + + An application context is automatically pushed by + :meth:`RequestContext.push() <flask.ctx.RequestContext.push>` + when handling a request, and when running a CLI command. Use + this to manually create a context outside of these situations. + + :: + + with app.app_context(): + init_db() + + See :doc:`/appcontext`. + + .. versionadded:: 0.9 + """ + return AppContext(self) + + def request_context(self, environ): + """Create a :class:`~flask.ctx.RequestContext` representing a + WSGI environment. Use a ``with`` block to push the context, + which will make :data:`request` point at this request. + + See :doc:`/reqcontext`. + + Typically you should not call this from your own code. A request + context is automatically pushed by the :meth:`wsgi_app` when + handling a request. Use :meth:`test_request_context` to create + an environment and context instead of this method. + + :param environ: a WSGI environment + """ + return RequestContext(self, environ) + + def test_request_context(self, *args, **kwargs): + """Create a :class:`~flask.ctx.RequestContext` for a WSGI + environment created from the given values. This is mostly useful + during testing, where you may want to run a function that uses + request data without dispatching a full request. + + See :doc:`/reqcontext`. + + Use a ``with`` block to push the context, which will make + :data:`request` point at the request for the created + environment. :: + + with test_request_context(...): + generate_report() + + When using the shell, it may be easier to push and pop the + context manually to avoid indentation. :: + + ctx = app.test_request_context(...) + ctx.push() + ... + ctx.pop() + + Takes the same arguments as Werkzeug's + :class:`~werkzeug.test.EnvironBuilder`, with some defaults from + the application. See the linked Werkzeug docs for most of the + available arguments. Flask-specific behavior is listed here. + + :param path: URL path being requested. + :param base_url: Base URL where the app is being served, which + ``path`` is relative to. If not given, built from + :data:`PREFERRED_URL_SCHEME`, ``subdomain``, + :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. + :param subdomain: Subdomain name to append to + :data:`SERVER_NAME`. + :param url_scheme: Scheme to use instead of + :data:`PREFERRED_URL_SCHEME`. + :param data: The request body, either as a string or a dict of + form keys and values. + :param json: If given, this is serialized as JSON and passed as + ``data``. Also defaults ``content_type`` to + ``application/json``. + :param args: other positional arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + :param kwargs: other keyword arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + """ + from flask.testing import make_test_environ_builder + + builder = make_test_environ_builder(self, *args, **kwargs) + + try: + return self.request_context(builder.get_environ()) + finally: + builder.close() + + def wsgi_app(self, environ, start_response): + """The actual WSGI application. This is not implemented in + :meth:`__call__` so that middlewares can be applied without + losing a reference to the app object. Instead of doing this:: + + app = MyMiddleware(app) + + It's a better idea to do this instead:: + + app.wsgi_app = MyMiddleware(app.wsgi_app) + + Then you still have the original application object around and + can continue to call methods on it. + + .. versionchanged:: 0.7 + Teardown events for the request and app contexts are called + even if an unhandled error occurs. Other events may not be + called depending on when an error occurs during dispatch. + See :ref:`callbacks-and-errors`. + + :param environ: A WSGI environment. + :param start_response: A callable accepting a status code, + a list of headers, and an optional exception context to + start the response. + """ + ctx = self.request_context(environ) + error = None + try: + try: + ctx.push() + response = self.full_dispatch_request() + except Exception as e: + error = e + response = self.handle_exception(e) + except: + error = sys.exc_info()[1] + raise + return response(environ, start_response) + finally: + if self.should_ignore_error(error): + error = None + ctx.auto_pop(error) + + def __call__(self, environ, start_response): + """The WSGI server calls the Flask application object as the + WSGI application. This calls :meth:`wsgi_app` which can be + wrapped to applying middleware.""" + return self.wsgi_app(environ, start_response) + + def __repr__(self): + return '<%s %r>' % ( + self.__class__.__name__, + self.name, + ) diff --git a/venv/Lib/site-packages/flask/blueprints.py b/venv/Lib/site-packages/flask/blueprints.py new file mode 100644 index 0000000..5ce5561 --- /dev/null +++ b/venv/Lib/site-packages/flask/blueprints.py @@ -0,0 +1,448 @@ +# -*- coding: utf-8 -*- +""" + flask.blueprints + ~~~~~~~~~~~~~~~~ + + Blueprints are the recommended way to implement larger or more + pluggable applications in Flask 0.7 and later. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" +from functools import update_wrapper +from werkzeug.urls import url_join + +from .helpers import _PackageBoundObject, _endpoint_from_view_func + + +class BlueprintSetupState(object): + """Temporary holder object for registering a blueprint with the + application. An instance of this class is created by the + :meth:`~flask.Blueprint.make_setup_state` method and later passed + to all register callback functions. + """ + + def __init__(self, blueprint, app, options, first_registration): + #: a reference to the current application + self.app = app + + #: a reference to the blueprint that created this setup state. + self.blueprint = blueprint + + #: a dictionary with all options that were passed to the + #: :meth:`~flask.Flask.register_blueprint` method. + self.options = options + + #: as blueprints can be registered multiple times with the + #: application and not everything wants to be registered + #: multiple times on it, this attribute can be used to figure + #: out if the blueprint was registered in the past already. + self.first_registration = first_registration + + subdomain = self.options.get('subdomain') + if subdomain is None: + subdomain = self.blueprint.subdomain + + #: The subdomain that the blueprint should be active for, ``None`` + #: otherwise. + self.subdomain = subdomain + + url_prefix = self.options.get('url_prefix') + if url_prefix is None: + url_prefix = self.blueprint.url_prefix + #: The prefix that should be used for all URLs defined on the + #: blueprint. + self.url_prefix = url_prefix + + #: A dictionary with URL defaults that is added to each and every + #: URL that was defined with the blueprint. + self.url_defaults = dict(self.blueprint.url_values_defaults) + self.url_defaults.update(self.options.get('url_defaults', ())) + + def add_url_rule(self, rule, endpoint=None, view_func=None, **options): + """A helper method to register a rule (and optionally a view function) + to the application. The endpoint is automatically prefixed with the + blueprint's name. + """ + if self.url_prefix is not None: + if rule: + rule = '/'.join(( + self.url_prefix.rstrip('/'), rule.lstrip('/'))) + else: + rule = self.url_prefix + options.setdefault('subdomain', self.subdomain) + if endpoint is None: + endpoint = _endpoint_from_view_func(view_func) + defaults = self.url_defaults + if 'defaults' in options: + defaults = dict(defaults, **options.pop('defaults')) + self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint), + view_func, defaults=defaults, **options) + + +class Blueprint(_PackageBoundObject): + """Represents a blueprint. A blueprint is an object that records + functions that will be called with the + :class:`~flask.blueprints.BlueprintSetupState` later to register functions + or other things on the main application. See :ref:`blueprints` for more + information. + + .. versionadded:: 0.7 + """ + + warn_on_modifications = False + _got_registered_once = False + + #: Blueprint local JSON decoder class to use. + #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_encoder`. + json_encoder = None + #: Blueprint local JSON decoder class to use. + #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_decoder`. + json_decoder = None + + # TODO remove the next three attrs when Sphinx :inherited-members: works + # https://github.com/sphinx-doc/sphinx/issues/741 + + #: The name of the package or module that this app belongs to. Do not + #: change this once it is set by the constructor. + import_name = None + + #: Location of the template files to be added to the template lookup. + #: ``None`` if templates should not be added. + template_folder = None + + #: Absolute path to the package on the filesystem. Used to look up + #: resources contained in the package. + root_path = None + + def __init__(self, name, import_name, static_folder=None, + static_url_path=None, template_folder=None, + url_prefix=None, subdomain=None, url_defaults=None, + root_path=None): + _PackageBoundObject.__init__(self, import_name, template_folder, + root_path=root_path) + self.name = name + self.url_prefix = url_prefix + self.subdomain = subdomain + self.static_folder = static_folder + self.static_url_path = static_url_path + self.deferred_functions = [] + if url_defaults is None: + url_defaults = {} + self.url_values_defaults = url_defaults + + def record(self, func): + """Registers a function that is called when the blueprint is + registered on the application. This function is called with the + state as argument as returned by the :meth:`make_setup_state` + method. + """ + if self._got_registered_once and self.warn_on_modifications: + from warnings import warn + warn(Warning('The blueprint was already registered once ' + 'but is getting modified now. These changes ' + 'will not show up.')) + self.deferred_functions.append(func) + + def record_once(self, func): + """Works like :meth:`record` but wraps the function in another + function that will ensure the function is only called once. If the + blueprint is registered a second time on the application, the + function passed is not called. + """ + def wrapper(state): + if state.first_registration: + func(state) + return self.record(update_wrapper(wrapper, func)) + + def make_setup_state(self, app, options, first_registration=False): + """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState` + object that is later passed to the register callback functions. + Subclasses can override this to return a subclass of the setup state. + """ + return BlueprintSetupState(self, app, options, first_registration) + + def register(self, app, options, first_registration=False): + """Called by :meth:`Flask.register_blueprint` to register all views + and callbacks registered on the blueprint with the application. Creates + a :class:`.BlueprintSetupState` and calls each :meth:`record` callback + with it. + + :param app: The application this blueprint is being registered with. + :param options: Keyword arguments forwarded from + :meth:`~Flask.register_blueprint`. + :param first_registration: Whether this is the first time this + blueprint has been registered on the application. + """ + self._got_registered_once = True + state = self.make_setup_state(app, options, first_registration) + + if self.has_static_folder: + state.add_url_rule( + self.static_url_path + '/<path:filename>', + view_func=self.send_static_file, endpoint='static' + ) + + for deferred in self.deferred_functions: + deferred(state) + + def route(self, rule, **options): + """Like :meth:`Flask.route` but for a blueprint. The endpoint for the + :func:`url_for` function is prefixed with the name of the blueprint. + """ + def decorator(f): + endpoint = options.pop("endpoint", f.__name__) + self.add_url_rule(rule, endpoint, f, **options) + return f + return decorator + + def add_url_rule(self, rule, endpoint=None, view_func=None, **options): + """Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for + the :func:`url_for` function is prefixed with the name of the blueprint. + """ + if endpoint: + assert '.' not in endpoint, "Blueprint endpoints should not contain dots" + if view_func and hasattr(view_func, '__name__'): + assert '.' not in view_func.__name__, "Blueprint view function name should not contain dots" + self.record(lambda s: + s.add_url_rule(rule, endpoint, view_func, **options)) + + def endpoint(self, endpoint): + """Like :meth:`Flask.endpoint` but for a blueprint. This does not + prefix the endpoint with the blueprint name, this has to be done + explicitly by the user of this method. If the endpoint is prefixed + with a `.` it will be registered to the current blueprint, otherwise + it's an application independent endpoint. + """ + def decorator(f): + def register_endpoint(state): + state.app.view_functions[endpoint] = f + self.record_once(register_endpoint) + return f + return decorator + + def app_template_filter(self, name=None): + """Register a custom template filter, available application wide. Like + :meth:`Flask.template_filter` but for a blueprint. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + def decorator(f): + self.add_app_template_filter(f, name=name) + return f + return decorator + + def add_app_template_filter(self, f, name=None): + """Register a custom template filter, available application wide. Like + :meth:`Flask.add_template_filter` but for a blueprint. Works exactly + like the :meth:`app_template_filter` decorator. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + def register_template(state): + state.app.jinja_env.filters[name or f.__name__] = f + self.record_once(register_template) + + def app_template_test(self, name=None): + """Register a custom template test, available application wide. Like + :meth:`Flask.template_test` but for a blueprint. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + def decorator(f): + self.add_app_template_test(f, name=name) + return f + return decorator + + def add_app_template_test(self, f, name=None): + """Register a custom template test, available application wide. Like + :meth:`Flask.add_template_test` but for a blueprint. Works exactly + like the :meth:`app_template_test` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + def register_template(state): + state.app.jinja_env.tests[name or f.__name__] = f + self.record_once(register_template) + + def app_template_global(self, name=None): + """Register a custom template global, available application wide. Like + :meth:`Flask.template_global` but for a blueprint. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + def decorator(f): + self.add_app_template_global(f, name=name) + return f + return decorator + + def add_app_template_global(self, f, name=None): + """Register a custom template global, available application wide. Like + :meth:`Flask.add_template_global` but for a blueprint. Works exactly + like the :meth:`app_template_global` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + def register_template(state): + state.app.jinja_env.globals[name or f.__name__] = f + self.record_once(register_template) + + def before_request(self, f): + """Like :meth:`Flask.before_request` but for a blueprint. This function + is only executed before each request that is handled by a function of + that blueprint. + """ + self.record_once(lambda s: s.app.before_request_funcs + .setdefault(self.name, []).append(f)) + return f + + def before_app_request(self, f): + """Like :meth:`Flask.before_request`. Such a function is executed + before each request, even if outside of a blueprint. + """ + self.record_once(lambda s: s.app.before_request_funcs + .setdefault(None, []).append(f)) + return f + + def before_app_first_request(self, f): + """Like :meth:`Flask.before_first_request`. Such a function is + executed before the first request to the application. + """ + self.record_once(lambda s: s.app.before_first_request_funcs.append(f)) + return f + + def after_request(self, f): + """Like :meth:`Flask.after_request` but for a blueprint. This function + is only executed after each request that is handled by a function of + that blueprint. + """ + self.record_once(lambda s: s.app.after_request_funcs + .setdefault(self.name, []).append(f)) + return f + + def after_app_request(self, f): + """Like :meth:`Flask.after_request` but for a blueprint. Such a function + is executed after each request, even if outside of the blueprint. + """ + self.record_once(lambda s: s.app.after_request_funcs + .setdefault(None, []).append(f)) + return f + + def teardown_request(self, f): + """Like :meth:`Flask.teardown_request` but for a blueprint. This + function is only executed when tearing down requests handled by a + function of that blueprint. Teardown request functions are executed + when the request context is popped, even when no actual request was + performed. + """ + self.record_once(lambda s: s.app.teardown_request_funcs + .setdefault(self.name, []).append(f)) + return f + + def teardown_app_request(self, f): + """Like :meth:`Flask.teardown_request` but for a blueprint. Such a + function is executed when tearing down each request, even if outside of + the blueprint. + """ + self.record_once(lambda s: s.app.teardown_request_funcs + .setdefault(None, []).append(f)) + return f + + def context_processor(self, f): + """Like :meth:`Flask.context_processor` but for a blueprint. This + function is only executed for requests handled by a blueprint. + """ + self.record_once(lambda s: s.app.template_context_processors + .setdefault(self.name, []).append(f)) + return f + + def app_context_processor(self, f): + """Like :meth:`Flask.context_processor` but for a blueprint. Such a + function is executed each request, even if outside of the blueprint. + """ + self.record_once(lambda s: s.app.template_context_processors + .setdefault(None, []).append(f)) + return f + + def app_errorhandler(self, code): + """Like :meth:`Flask.errorhandler` but for a blueprint. This + handler is used for all requests, even if outside of the blueprint. + """ + def decorator(f): + self.record_once(lambda s: s.app.errorhandler(code)(f)) + return f + return decorator + + def url_value_preprocessor(self, f): + """Registers a function as URL value preprocessor for this + blueprint. It's called before the view functions are called and + can modify the url values provided. + """ + self.record_once(lambda s: s.app.url_value_preprocessors + .setdefault(self.name, []).append(f)) + return f + + def url_defaults(self, f): + """Callback function for URL defaults for this blueprint. It's called + with the endpoint and values and should update the values passed + in place. + """ + self.record_once(lambda s: s.app.url_default_functions + .setdefault(self.name, []).append(f)) + return f + + def app_url_value_preprocessor(self, f): + """Same as :meth:`url_value_preprocessor` but application wide. + """ + self.record_once(lambda s: s.app.url_value_preprocessors + .setdefault(None, []).append(f)) + return f + + def app_url_defaults(self, f): + """Same as :meth:`url_defaults` but application wide. + """ + self.record_once(lambda s: s.app.url_default_functions + .setdefault(None, []).append(f)) + return f + + def errorhandler(self, code_or_exception): + """Registers an error handler that becomes active for this blueprint + only. Please be aware that routing does not happen local to a + blueprint so an error handler for 404 usually is not handled by + a blueprint unless it is caused inside a view function. Another + special case is the 500 internal server error which is always looked + up from the application. + + Otherwise works as the :meth:`~flask.Flask.errorhandler` decorator + of the :class:`~flask.Flask` object. + """ + def decorator(f): + self.record_once(lambda s: s.app._register_error_handler( + self.name, code_or_exception, f)) + return f + return decorator + + def register_error_handler(self, code_or_exception, f): + """Non-decorator version of the :meth:`errorhandler` error attach + function, akin to the :meth:`~flask.Flask.register_error_handler` + application-wide function of the :class:`~flask.Flask` object but + for error handlers limited to this blueprint. + + .. versionadded:: 0.11 + """ + self.record_once(lambda s: s.app._register_error_handler( + self.name, code_or_exception, f)) diff --git a/venv/Lib/site-packages/flask/cli.py b/venv/Lib/site-packages/flask/cli.py new file mode 100644 index 0000000..efc1733 --- /dev/null +++ b/venv/Lib/site-packages/flask/cli.py @@ -0,0 +1,898 @@ +# -*- coding: utf-8 -*- +""" + flask.cli + ~~~~~~~~~ + + A simple command line application to run flask apps. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +from __future__ import print_function + +import ast +import inspect +import os +import re +import ssl +import sys +import traceback +from functools import update_wrapper +from operator import attrgetter +from threading import Lock, Thread + +import click +from werkzeug.utils import import_string + +from . import __version__ +from ._compat import getargspec, iteritems, reraise, text_type +from .globals import current_app +from .helpers import get_debug_flag, get_env, get_load_dotenv + +try: + import dotenv +except ImportError: + dotenv = None + + +class NoAppException(click.UsageError): + """Raised if an application cannot be found or loaded.""" + + +def find_best_app(script_info, module): + """Given a module instance this tries to find the best possible + application in the module or raises an exception. + """ + from . import Flask + + # Search for the most common names first. + for attr_name in ('app', 'application'): + app = getattr(module, attr_name, None) + + if isinstance(app, Flask): + return app + + # Otherwise find the only object that is a Flask instance. + matches = [ + v for k, v in iteritems(module.__dict__) if isinstance(v, Flask) + ] + + if len(matches) == 1: + return matches[0] + elif len(matches) > 1: + raise NoAppException( + 'Detected multiple Flask applications in module "{module}". Use ' + '"FLASK_APP={module}:name" to specify the correct ' + 'one.'.format(module=module.__name__) + ) + + # Search for app factory functions. + for attr_name in ('create_app', 'make_app'): + app_factory = getattr(module, attr_name, None) + + if inspect.isfunction(app_factory): + try: + app = call_factory(script_info, app_factory) + + if isinstance(app, Flask): + return app + except TypeError: + if not _called_with_wrong_args(app_factory): + raise + raise NoAppException( + 'Detected factory "{factory}" in module "{module}", but ' + 'could not call it without arguments. Use ' + '"FLASK_APP=\'{module}:{factory}(args)\'" to specify ' + 'arguments.'.format( + factory=attr_name, module=module.__name__ + ) + ) + + raise NoAppException( + 'Failed to find Flask application or factory in module "{module}". ' + 'Use "FLASK_APP={module}:name to specify one.'.format( + module=module.__name__ + ) + ) + + +def call_factory(script_info, app_factory, arguments=()): + """Takes an app factory, a ``script_info` object and optionally a tuple + of arguments. Checks for the existence of a script_info argument and calls + the app_factory depending on that and the arguments provided. + """ + args_spec = getargspec(app_factory) + arg_names = args_spec.args + arg_defaults = args_spec.defaults + + if 'script_info' in arg_names: + return app_factory(*arguments, script_info=script_info) + elif arguments: + return app_factory(*arguments) + elif not arguments and len(arg_names) == 1 and arg_defaults is None: + return app_factory(script_info) + + return app_factory() + + +def _called_with_wrong_args(factory): + """Check whether calling a function raised a ``TypeError`` because + the call failed or because something in the factory raised the + error. + + :param factory: the factory function that was called + :return: true if the call failed + """ + tb = sys.exc_info()[2] + + try: + while tb is not None: + if tb.tb_frame.f_code is factory.__code__: + # in the factory, it was called successfully + return False + + tb = tb.tb_next + + # didn't reach the factory + return True + finally: + del tb + + +def find_app_by_string(script_info, module, app_name): + """Checks if the given string is a variable name or a function. If it is a + function, it checks for specified arguments and whether it takes a + ``script_info`` argument and calls the function with the appropriate + arguments. + """ + from flask import Flask + match = re.match(r'^ *([^ ()]+) *(?:\((.*?) *,? *\))? *$', app_name) + + if not match: + raise NoAppException( + '"{name}" is not a valid variable name or function ' + 'expression.'.format(name=app_name) + ) + + name, args = match.groups() + + try: + attr = getattr(module, name) + except AttributeError as e: + raise NoAppException(e.args[0]) + + if inspect.isfunction(attr): + if args: + try: + args = ast.literal_eval('({args},)'.format(args=args)) + except (ValueError, SyntaxError)as e: + raise NoAppException( + 'Could not parse the arguments in ' + '"{app_name}".'.format(e=e, app_name=app_name) + ) + else: + args = () + + try: + app = call_factory(script_info, attr, args) + except TypeError as e: + if not _called_with_wrong_args(attr): + raise + + raise NoAppException( + '{e}\nThe factory "{app_name}" in module "{module}" could not ' + 'be called with the specified arguments.'.format( + e=e, app_name=app_name, module=module.__name__ + ) + ) + else: + app = attr + + if isinstance(app, Flask): + return app + + raise NoAppException( + 'A valid Flask application was not obtained from ' + '"{module}:{app_name}".'.format( + module=module.__name__, app_name=app_name + ) + ) + + +def prepare_import(path): + """Given a filename this will try to calculate the python path, add it + to the search path and return the actual module name that is expected. + """ + path = os.path.realpath(path) + + if os.path.splitext(path)[1] == '.py': + path = os.path.splitext(path)[0] + + if os.path.basename(path) == '__init__': + path = os.path.dirname(path) + + module_name = [] + + # move up until outside package structure (no __init__.py) + while True: + path, name = os.path.split(path) + module_name.append(name) + + if not os.path.exists(os.path.join(path, '__init__.py')): + break + + if sys.path[0] != path: + sys.path.insert(0, path) + + return '.'.join(module_name[::-1]) + + +def locate_app(script_info, module_name, app_name, raise_if_not_found=True): + __traceback_hide__ = True + + try: + __import__(module_name) + except ImportError: + # Reraise the ImportError if it occurred within the imported module. + # Determine this by checking whether the trace has a depth > 1. + if sys.exc_info()[-1].tb_next: + raise NoAppException( + 'While importing "{name}", an ImportError was raised:' + '\n\n{tb}'.format(name=module_name, tb=traceback.format_exc()) + ) + elif raise_if_not_found: + raise NoAppException( + 'Could not import "{name}".'.format(name=module_name) + ) + else: + return + + module = sys.modules[module_name] + + if app_name is None: + return find_best_app(script_info, module) + else: + return find_app_by_string(script_info, module, app_name) + + +def get_version(ctx, param, value): + if not value or ctx.resilient_parsing: + return + message = 'Flask %(version)s\nPython %(python_version)s' + click.echo(message % { + 'version': __version__, + 'python_version': sys.version, + }, color=ctx.color) + ctx.exit() + + +version_option = click.Option( + ['--version'], + help='Show the flask version', + expose_value=False, + callback=get_version, + is_flag=True, + is_eager=True +) + + +class DispatchingApp(object): + """Special application that dispatches to a Flask application which + is imported by name in a background thread. If an error happens + it is recorded and shown as part of the WSGI handling which in case + of the Werkzeug debugger means that it shows up in the browser. + """ + + def __init__(self, loader, use_eager_loading=False): + self.loader = loader + self._app = None + self._lock = Lock() + self._bg_loading_exc_info = None + if use_eager_loading: + self._load_unlocked() + else: + self._load_in_background() + + def _load_in_background(self): + def _load_app(): + __traceback_hide__ = True + with self._lock: + try: + self._load_unlocked() + except Exception: + self._bg_loading_exc_info = sys.exc_info() + t = Thread(target=_load_app, args=()) + t.start() + + def _flush_bg_loading_exception(self): + __traceback_hide__ = True + exc_info = self._bg_loading_exc_info + if exc_info is not None: + self._bg_loading_exc_info = None + reraise(*exc_info) + + def _load_unlocked(self): + __traceback_hide__ = True + self._app = rv = self.loader() + self._bg_loading_exc_info = None + return rv + + def __call__(self, environ, start_response): + __traceback_hide__ = True + if self._app is not None: + return self._app(environ, start_response) + self._flush_bg_loading_exception() + with self._lock: + if self._app is not None: + rv = self._app + else: + rv = self._load_unlocked() + return rv(environ, start_response) + + +class ScriptInfo(object): + """Help object to deal with Flask applications. This is usually not + necessary to interface with as it's used internally in the dispatching + to click. In future versions of Flask this object will most likely play + a bigger role. Typically it's created automatically by the + :class:`FlaskGroup` but you can also manually create it and pass it + onwards as click object. + """ + + def __init__(self, app_import_path=None, create_app=None): + #: Optionally the import path for the Flask application. + self.app_import_path = app_import_path or os.environ.get('FLASK_APP') + #: Optionally a function that is passed the script info to create + #: the instance of the application. + self.create_app = create_app + #: A dictionary with arbitrary data that can be associated with + #: this script info. + self.data = {} + self._loaded_app = None + + def load_app(self): + """Loads the Flask app (if not yet loaded) and returns it. Calling + this multiple times will just result in the already loaded app to + be returned. + """ + __traceback_hide__ = True + + if self._loaded_app is not None: + return self._loaded_app + + app = None + + if self.create_app is not None: + app = call_factory(self, self.create_app) + else: + if self.app_import_path: + path, name = (self.app_import_path.split(':', 1) + [None])[:2] + import_name = prepare_import(path) + app = locate_app(self, import_name, name) + else: + for path in ('wsgi.py', 'app.py'): + import_name = prepare_import(path) + app = locate_app(self, import_name, None, + raise_if_not_found=False) + + if app: + break + + if not app: + raise NoAppException( + 'Could not locate a Flask application. You did not provide ' + 'the "FLASK_APP" environment variable, and a "wsgi.py" or ' + '"app.py" module was not found in the current directory.' + ) + + debug = get_debug_flag() + + # Update the app's debug flag through the descriptor so that other + # values repopulate as well. + if debug is not None: + app.debug = debug + + self._loaded_app = app + return app + + +pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True) + + +def with_appcontext(f): + """Wraps a callback so that it's guaranteed to be executed with the + script's application context. If callbacks are registered directly + to the ``app.cli`` object then they are wrapped with this function + by default unless it's disabled. + """ + @click.pass_context + def decorator(__ctx, *args, **kwargs): + with __ctx.ensure_object(ScriptInfo).load_app().app_context(): + return __ctx.invoke(f, *args, **kwargs) + return update_wrapper(decorator, f) + + +class AppGroup(click.Group): + """This works similar to a regular click :class:`~click.Group` but it + changes the behavior of the :meth:`command` decorator so that it + automatically wraps the functions in :func:`with_appcontext`. + + Not to be confused with :class:`FlaskGroup`. + """ + + def command(self, *args, **kwargs): + """This works exactly like the method of the same name on a regular + :class:`click.Group` but it wraps callbacks in :func:`with_appcontext` + unless it's disabled by passing ``with_appcontext=False``. + """ + wrap_for_ctx = kwargs.pop('with_appcontext', True) + def decorator(f): + if wrap_for_ctx: + f = with_appcontext(f) + return click.Group.command(self, *args, **kwargs)(f) + return decorator + + def group(self, *args, **kwargs): + """This works exactly like the method of the same name on a regular + :class:`click.Group` but it defaults the group class to + :class:`AppGroup`. + """ + kwargs.setdefault('cls', AppGroup) + return click.Group.group(self, *args, **kwargs) + + +class FlaskGroup(AppGroup): + """Special subclass of the :class:`AppGroup` group that supports + loading more commands from the configured Flask app. Normally a + developer does not have to interface with this class but there are + some very advanced use cases for which it makes sense to create an + instance of this. + + For information as of why this is useful see :ref:`custom-scripts`. + + :param add_default_commands: if this is True then the default run and + shell commands wil be added. + :param add_version_option: adds the ``--version`` option. + :param create_app: an optional callback that is passed the script info and + returns the loaded app. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment variables + from :file:`.env` and :file:`.flaskenv` files. + """ + + def __init__(self, add_default_commands=True, create_app=None, + add_version_option=True, load_dotenv=True, **extra): + params = list(extra.pop('params', None) or ()) + + if add_version_option: + params.append(version_option) + + AppGroup.__init__(self, params=params, **extra) + self.create_app = create_app + self.load_dotenv = load_dotenv + + if add_default_commands: + self.add_command(run_command) + self.add_command(shell_command) + self.add_command(routes_command) + + self._loaded_plugin_commands = False + + def _load_plugin_commands(self): + if self._loaded_plugin_commands: + return + try: + import pkg_resources + except ImportError: + self._loaded_plugin_commands = True + return + + for ep in pkg_resources.iter_entry_points('flask.commands'): + self.add_command(ep.load(), ep.name) + self._loaded_plugin_commands = True + + def get_command(self, ctx, name): + self._load_plugin_commands() + + # We load built-in commands first as these should always be the + # same no matter what the app does. If the app does want to + # override this it needs to make a custom instance of this group + # and not attach the default commands. + # + # This also means that the script stays functional in case the + # application completely fails. + rv = AppGroup.get_command(self, ctx, name) + if rv is not None: + return rv + + info = ctx.ensure_object(ScriptInfo) + try: + rv = info.load_app().cli.get_command(ctx, name) + if rv is not None: + return rv + except NoAppException: + pass + + def list_commands(self, ctx): + self._load_plugin_commands() + + # The commands available is the list of both the application (if + # available) plus the builtin commands. + rv = set(click.Group.list_commands(self, ctx)) + info = ctx.ensure_object(ScriptInfo) + try: + rv.update(info.load_app().cli.list_commands(ctx)) + except Exception: + # Here we intentionally swallow all exceptions as we don't + # want the help page to break if the app does not exist. + # If someone attempts to use the command we try to create + # the app again and this will give us the error. + # However, we will not do so silently because that would confuse + # users. + traceback.print_exc() + return sorted(rv) + + def main(self, *args, **kwargs): + # Set a global flag that indicates that we were invoked from the + # command line interface. This is detected by Flask.run to make the + # call into a no-op. This is necessary to avoid ugly errors when the + # script that is loaded here also attempts to start a server. + os.environ['FLASK_RUN_FROM_CLI'] = 'true' + + if get_load_dotenv(self.load_dotenv): + load_dotenv() + + obj = kwargs.get('obj') + + if obj is None: + obj = ScriptInfo(create_app=self.create_app) + + kwargs['obj'] = obj + kwargs.setdefault('auto_envvar_prefix', 'FLASK') + return super(FlaskGroup, self).main(*args, **kwargs) + + +def _path_is_ancestor(path, other): + """Take ``other`` and remove the length of ``path`` from it. Then join it + to ``path``. If it is the original value, ``path`` is an ancestor of + ``other``.""" + return os.path.join(path, other[len(path):].lstrip(os.sep)) == other + + +def load_dotenv(path=None): + """Load "dotenv" files in order of precedence to set environment variables. + + If an env var is already set it is not overwritten, so earlier files in the + list are preferred over later files. + + Changes the current working directory to the location of the first file + found, with the assumption that it is in the top level project directory + and will be where the Python path should import local packages from. + + This is a no-op if `python-dotenv`_ is not installed. + + .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme + + :param path: Load the file at this location instead of searching. + :return: ``True`` if a file was loaded. + + .. versionadded:: 1.0 + """ + if dotenv is None: + if path or os.path.exists('.env') or os.path.exists('.flaskenv'): + click.secho( + ' * Tip: There are .env files present.' + ' Do "pip install python-dotenv" to use them.', + fg='yellow') + return + + if path is not None: + return dotenv.load_dotenv(path) + + new_dir = None + + for name in ('.env', '.flaskenv'): + path = dotenv.find_dotenv(name, usecwd=True) + + if not path: + continue + + if new_dir is None: + new_dir = os.path.dirname(path) + + dotenv.load_dotenv(path) + + if new_dir and os.getcwd() != new_dir: + os.chdir(new_dir) + + return new_dir is not None # at least one file was located and loaded + + +def show_server_banner(env, debug, app_import_path, eager_loading): + """Show extra startup messages the first time the server is run, + ignoring the reloader. + """ + if os.environ.get('WERKZEUG_RUN_MAIN') == 'true': + return + + if app_import_path is not None: + message = ' * Serving Flask app "{0}"'.format(app_import_path) + + if not eager_loading: + message += ' (lazy loading)' + + click.echo(message) + + click.echo(' * Environment: {0}'.format(env)) + + if env == 'production': + click.secho( + ' WARNING: Do not use the development server in a production' + ' environment.', fg='red') + click.secho(' Use a production WSGI server instead.', dim=True) + + if debug is not None: + click.echo(' * Debug mode: {0}'.format('on' if debug else 'off')) + + +class CertParamType(click.ParamType): + """Click option type for the ``--cert`` option. Allows either an + existing file, the string ``'adhoc'``, or an import for a + :class:`~ssl.SSLContext` object. + """ + + name = 'path' + + def __init__(self): + self.path_type = click.Path( + exists=True, dir_okay=False, resolve_path=True) + + def convert(self, value, param, ctx): + try: + return self.path_type(value, param, ctx) + except click.BadParameter: + value = click.STRING(value, param, ctx).lower() + + if value == 'adhoc': + try: + import OpenSSL + except ImportError: + raise click.BadParameter( + 'Using ad-hoc certificates requires pyOpenSSL.', + ctx, param) + + return value + + obj = import_string(value, silent=True) + + if sys.version_info < (2, 7): + if obj: + return obj + else: + if isinstance(obj, ssl.SSLContext): + return obj + + raise + + +def _validate_key(ctx, param, value): + """The ``--key`` option must be specified when ``--cert`` is a file. + Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed. + """ + cert = ctx.params.get('cert') + is_adhoc = cert == 'adhoc' + + if sys.version_info < (2, 7): + is_context = cert and not isinstance(cert, (text_type, bytes)) + else: + is_context = isinstance(cert, ssl.SSLContext) + + if value is not None: + if is_adhoc: + raise click.BadParameter( + 'When "--cert" is "adhoc", "--key" is not used.', + ctx, param) + + if is_context: + raise click.BadParameter( + 'When "--cert" is an SSLContext object, "--key is not used.', + ctx, param) + + if not cert: + raise click.BadParameter( + '"--cert" must also be specified.', + ctx, param) + + ctx.params['cert'] = cert, value + + else: + if cert and not (is_adhoc or is_context): + raise click.BadParameter( + 'Required when using "--cert".', + ctx, param) + + return value + + +@click.command('run', short_help='Runs a development server.') +@click.option('--host', '-h', default='127.0.0.1', + help='The interface to bind to.') +@click.option('--port', '-p', default=5000, + help='The port to bind to.') +@click.option('--cert', type=CertParamType(), + help='Specify a certificate file to use HTTPS.') +@click.option('--key', + type=click.Path(exists=True, dir_okay=False, resolve_path=True), + callback=_validate_key, expose_value=False, + help='The key file to use when specifying a certificate.') +@click.option('--reload/--no-reload', default=None, + help='Enable or disable the reloader. By default the reloader ' + 'is active if debug is enabled.') +@click.option('--debugger/--no-debugger', default=None, + help='Enable or disable the debugger. By default the debugger ' + 'is active if debug is enabled.') +@click.option('--eager-loading/--lazy-loader', default=None, + help='Enable or disable eager loading. By default eager ' + 'loading is enabled if the reloader is disabled.') +@click.option('--with-threads/--without-threads', default=True, + help='Enable or disable multithreading.') +@pass_script_info +def run_command(info, host, port, reload, debugger, eager_loading, + with_threads, cert): + """Run a local development server. + + This server is for development purposes only. It does not provide + the stability, security, or performance of production WSGI servers. + + The reloader and debugger are enabled by default if + FLASK_ENV=development or FLASK_DEBUG=1. + """ + debug = get_debug_flag() + + if reload is None: + reload = debug + + if debugger is None: + debugger = debug + + if eager_loading is None: + eager_loading = not reload + + show_server_banner(get_env(), debug, info.app_import_path, eager_loading) + app = DispatchingApp(info.load_app, use_eager_loading=eager_loading) + + from werkzeug.serving import run_simple + run_simple(host, port, app, use_reloader=reload, use_debugger=debugger, + threaded=with_threads, ssl_context=cert) + + +@click.command('shell', short_help='Runs a shell in the app context.') +@with_appcontext +def shell_command(): + """Runs an interactive Python shell in the context of a given + Flask application. The application will populate the default + namespace of this shell according to it's configuration. + + This is useful for executing small snippets of management code + without having to manually configure the application. + """ + import code + from flask.globals import _app_ctx_stack + app = _app_ctx_stack.top.app + banner = 'Python %s on %s\nApp: %s [%s]\nInstance: %s' % ( + sys.version, + sys.platform, + app.import_name, + app.env, + app.instance_path, + ) + ctx = {} + + # Support the regular Python interpreter startup script if someone + # is using it. + startup = os.environ.get('PYTHONSTARTUP') + if startup and os.path.isfile(startup): + with open(startup, 'r') as f: + eval(compile(f.read(), startup, 'exec'), ctx) + + ctx.update(app.make_shell_context()) + + code.interact(banner=banner, local=ctx) + + +@click.command('routes', short_help='Show the routes for the app.') +@click.option( + '--sort', '-s', + type=click.Choice(('endpoint', 'methods', 'rule', 'match')), + default='endpoint', + help=( + 'Method to sort routes by. "match" is the order that Flask will match ' + 'routes when dispatching a request.' + ) +) +@click.option( + '--all-methods', + is_flag=True, + help="Show HEAD and OPTIONS methods." +) +@with_appcontext +def routes_command(sort, all_methods): + """Show all registered routes with endpoints and methods.""" + + rules = list(current_app.url_map.iter_rules()) + if not rules: + click.echo('No routes were registered.') + return + + ignored_methods = set(() if all_methods else ('HEAD', 'OPTIONS')) + + if sort in ('endpoint', 'rule'): + rules = sorted(rules, key=attrgetter(sort)) + elif sort == 'methods': + rules = sorted(rules, key=lambda rule: sorted(rule.methods)) + + rule_methods = [ + ', '.join(sorted(rule.methods - ignored_methods)) for rule in rules + ] + + headers = ('Endpoint', 'Methods', 'Rule') + widths = ( + max(len(rule.endpoint) for rule in rules), + max(len(methods) for methods in rule_methods), + max(len(rule.rule) for rule in rules), + ) + widths = [max(len(h), w) for h, w in zip(headers, widths)] + row = '{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}'.format(*widths) + + click.echo(row.format(*headers).strip()) + click.echo(row.format(*('-' * width for width in widths))) + + for rule, methods in zip(rules, rule_methods): + click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip()) + + +cli = FlaskGroup(help="""\ +A general utility script for Flask applications. + +Provides commands from Flask, extensions, and the application. Loads the +application defined in the FLASK_APP environment variable, or from a wsgi.py +file. Setting the FLASK_ENV environment variable to 'development' will enable +debug mode. + +\b + {prefix}{cmd} FLASK_APP=hello.py + {prefix}{cmd} FLASK_ENV=development + {prefix}flask run +""".format( + cmd='export' if os.name == 'posix' else 'set', + prefix='$ ' if os.name == 'posix' else '> ' +)) + + +def main(as_module=False): + args = sys.argv[1:] + + if as_module: + this_module = 'flask' + + if sys.version_info < (2, 7): + this_module += '.cli' + + name = 'python -m ' + this_module + + # Python rewrites "python -m flask" to the path to the file in argv. + # Restore the original command so that the reloader works. + sys.argv = ['-m', this_module] + args + else: + name = None + + cli.main(args=args, prog_name=name) + + +if __name__ == '__main__': + main(as_module=True) diff --git a/venv/Lib/site-packages/flask/config.py b/venv/Lib/site-packages/flask/config.py new file mode 100644 index 0000000..d6074ba --- /dev/null +++ b/venv/Lib/site-packages/flask/config.py @@ -0,0 +1,265 @@ +# -*- coding: utf-8 -*- +""" + flask.config + ~~~~~~~~~~~~ + + Implements the configuration related objects. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +import os +import types +import errno + +from werkzeug.utils import import_string +from ._compat import string_types, iteritems +from . import json + + +class ConfigAttribute(object): + """Makes an attribute forward to the config""" + + def __init__(self, name, get_converter=None): + self.__name__ = name + self.get_converter = get_converter + + def __get__(self, obj, type=None): + if obj is None: + return self + rv = obj.config[self.__name__] + if self.get_converter is not None: + rv = self.get_converter(rv) + return rv + + def __set__(self, obj, value): + obj.config[self.__name__] = value + + +class Config(dict): + """Works exactly like a dict but provides ways to fill it from files + or special dictionaries. There are two common patterns to populate the + config. + + Either you can fill the config from a config file:: + + app.config.from_pyfile('yourconfig.cfg') + + Or alternatively you can define the configuration options in the + module that calls :meth:`from_object` or provide an import path to + a module that should be loaded. It is also possible to tell it to + use the same module and with that provide the configuration values + just before the call:: + + DEBUG = True + SECRET_KEY = 'development key' + app.config.from_object(__name__) + + In both cases (loading from any Python file or loading from modules), + only uppercase keys are added to the config. This makes it possible to use + lowercase values in the config file for temporary values that are not added + to the config or to define the config keys in the same file that implements + the application. + + Probably the most interesting way to load configurations is from an + environment variable pointing to a file:: + + app.config.from_envvar('YOURAPPLICATION_SETTINGS') + + In this case before launching the application you have to set this + environment variable to the file you want to use. On Linux and OS X + use the export statement:: + + export YOURAPPLICATION_SETTINGS='/path/to/config/file' + + On windows use `set` instead. + + :param root_path: path to which files are read relative from. When the + config object is created by the application, this is + the application's :attr:`~flask.Flask.root_path`. + :param defaults: an optional dictionary of default values + """ + + def __init__(self, root_path, defaults=None): + dict.__init__(self, defaults or {}) + self.root_path = root_path + + def from_envvar(self, variable_name, silent=False): + """Loads a configuration from an environment variable pointing to + a configuration file. This is basically just a shortcut with nicer + error messages for this line of code:: + + app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) + + :param variable_name: name of the environment variable + :param silent: set to ``True`` if you want silent failure for missing + files. + :return: bool. ``True`` if able to load config, ``False`` otherwise. + """ + rv = os.environ.get(variable_name) + if not rv: + if silent: + return False + raise RuntimeError('The environment variable %r is not set ' + 'and as such configuration could not be ' + 'loaded. Set this variable and make it ' + 'point to a configuration file' % + variable_name) + return self.from_pyfile(rv, silent=silent) + + def from_pyfile(self, filename, silent=False): + """Updates the values in the config from a Python file. This function + behaves as if the file was imported as module with the + :meth:`from_object` function. + + :param filename: the filename of the config. This can either be an + absolute filename or a filename relative to the + root path. + :param silent: set to ``True`` if you want silent failure for missing + files. + + .. versionadded:: 0.7 + `silent` parameter. + """ + filename = os.path.join(self.root_path, filename) + d = types.ModuleType('config') + d.__file__ = filename + try: + with open(filename, mode='rb') as config_file: + exec(compile(config_file.read(), filename, 'exec'), d.__dict__) + except IOError as e: + if silent and e.errno in ( + errno.ENOENT, errno.EISDIR, errno.ENOTDIR + ): + return False + e.strerror = 'Unable to load configuration file (%s)' % e.strerror + raise + self.from_object(d) + return True + + def from_object(self, obj): + """Updates the values from the given object. An object can be of one + of the following two types: + + - a string: in this case the object with that name will be imported + - an actual object reference: that object is used directly + + Objects are usually either modules or classes. :meth:`from_object` + loads only the uppercase attributes of the module/class. A ``dict`` + object will not work with :meth:`from_object` because the keys of a + ``dict`` are not attributes of the ``dict`` class. + + Example of module-based configuration:: + + app.config.from_object('yourapplication.default_config') + from yourapplication import default_config + app.config.from_object(default_config) + + You should not use this function to load the actual configuration but + rather configuration defaults. The actual config should be loaded + with :meth:`from_pyfile` and ideally from a location not within the + package because the package might be installed system wide. + + See :ref:`config-dev-prod` for an example of class-based configuration + using :meth:`from_object`. + + :param obj: an import name or object + """ + if isinstance(obj, string_types): + obj = import_string(obj) + for key in dir(obj): + if key.isupper(): + self[key] = getattr(obj, key) + + def from_json(self, filename, silent=False): + """Updates the values in the config from a JSON file. This function + behaves as if the JSON object was a dictionary and passed to the + :meth:`from_mapping` function. + + :param filename: the filename of the JSON file. This can either be an + absolute filename or a filename relative to the + root path. + :param silent: set to ``True`` if you want silent failure for missing + files. + + .. versionadded:: 0.11 + """ + filename = os.path.join(self.root_path, filename) + + try: + with open(filename) as json_file: + obj = json.loads(json_file.read()) + except IOError as e: + if silent and e.errno in (errno.ENOENT, errno.EISDIR): + return False + e.strerror = 'Unable to load configuration file (%s)' % e.strerror + raise + return self.from_mapping(obj) + + def from_mapping(self, *mapping, **kwargs): + """Updates the config like :meth:`update` ignoring items with non-upper + keys. + + .. versionadded:: 0.11 + """ + mappings = [] + if len(mapping) == 1: + if hasattr(mapping[0], 'items'): + mappings.append(mapping[0].items()) + else: + mappings.append(mapping[0]) + elif len(mapping) > 1: + raise TypeError( + 'expected at most 1 positional argument, got %d' % len(mapping) + ) + mappings.append(kwargs.items()) + for mapping in mappings: + for (key, value) in mapping: + if key.isupper(): + self[key] = value + return True + + def get_namespace(self, namespace, lowercase=True, trim_namespace=True): + """Returns a dictionary containing a subset of configuration options + that match the specified namespace/prefix. Example usage:: + + app.config['IMAGE_STORE_TYPE'] = 'fs' + app.config['IMAGE_STORE_PATH'] = '/var/app/images' + app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com' + image_store_config = app.config.get_namespace('IMAGE_STORE_') + + The resulting dictionary `image_store_config` would look like:: + + { + 'type': 'fs', + 'path': '/var/app/images', + 'base_url': 'http://img.website.com' + } + + This is often useful when configuration options map directly to + keyword arguments in functions or class constructors. + + :param namespace: a configuration namespace + :param lowercase: a flag indicating if the keys of the resulting + dictionary should be lowercase + :param trim_namespace: a flag indicating if the keys of the resulting + dictionary should not include the namespace + + .. versionadded:: 0.11 + """ + rv = {} + for k, v in iteritems(self): + if not k.startswith(namespace): + continue + if trim_namespace: + key = k[len(namespace):] + else: + key = k + if lowercase: + key = key.lower() + rv[key] = v + return rv + + def __repr__(self): + return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self)) diff --git a/venv/Lib/site-packages/flask/ctx.py b/venv/Lib/site-packages/flask/ctx.py new file mode 100644 index 0000000..8472c92 --- /dev/null +++ b/venv/Lib/site-packages/flask/ctx.py @@ -0,0 +1,457 @@ +# -*- coding: utf-8 -*- +""" + flask.ctx + ~~~~~~~~~ + + Implements the objects required to keep the context. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +import sys +from functools import update_wrapper + +from werkzeug.exceptions import HTTPException + +from .globals import _request_ctx_stack, _app_ctx_stack +from .signals import appcontext_pushed, appcontext_popped +from ._compat import BROKEN_PYPY_CTXMGR_EXIT, reraise + + +# a singleton sentinel value for parameter defaults +_sentinel = object() + + +class _AppCtxGlobals(object): + """A plain object. Used as a namespace for storing data during an + application context. + + Creating an app context automatically creates this object, which is + made available as the :data:`g` proxy. + + .. describe:: 'key' in g + + Check whether an attribute is present. + + .. versionadded:: 0.10 + + .. describe:: iter(g) + + Return an iterator over the attribute names. + + .. versionadded:: 0.10 + """ + + def get(self, name, default=None): + """Get an attribute by name, or a default value. Like + :meth:`dict.get`. + + :param name: Name of attribute to get. + :param default: Value to return if the attribute is not present. + + .. versionadded:: 0.10 + """ + return self.__dict__.get(name, default) + + def pop(self, name, default=_sentinel): + """Get and remove an attribute by name. Like :meth:`dict.pop`. + + :param name: Name of attribute to pop. + :param default: Value to return if the attribute is not present, + instead of raise a ``KeyError``. + + .. versionadded:: 0.11 + """ + if default is _sentinel: + return self.__dict__.pop(name) + else: + return self.__dict__.pop(name, default) + + def setdefault(self, name, default=None): + """Get the value of an attribute if it is present, otherwise + set and return a default value. Like :meth:`dict.setdefault`. + + :param name: Name of attribute to get. + :param: default: Value to set and return if the attribute is not + present. + + .. versionadded:: 0.11 + """ + return self.__dict__.setdefault(name, default) + + def __contains__(self, item): + return item in self.__dict__ + + def __iter__(self): + return iter(self.__dict__) + + def __repr__(self): + top = _app_ctx_stack.top + if top is not None: + return '<flask.g of %r>' % top.app.name + return object.__repr__(self) + + +def after_this_request(f): + """Executes a function after this request. This is useful to modify + response objects. The function is passed the response object and has + to return the same or a new one. + + Example:: + + @app.route('/') + def index(): + @after_this_request + def add_header(response): + response.headers['X-Foo'] = 'Parachute' + return response + return 'Hello World!' + + This is more useful if a function other than the view function wants to + modify a response. For instance think of a decorator that wants to add + some headers without converting the return value into a response object. + + .. versionadded:: 0.9 + """ + _request_ctx_stack.top._after_request_functions.append(f) + return f + + +def copy_current_request_context(f): + """A helper function that decorates a function to retain the current + request context. This is useful when working with greenlets. The moment + the function is decorated a copy of the request context is created and + then pushed when the function is called. + + Example:: + + import gevent + from flask import copy_current_request_context + + @app.route('/') + def index(): + @copy_current_request_context + def do_some_work(): + # do some work here, it can access flask.request like you + # would otherwise in the view function. + ... + gevent.spawn(do_some_work) + return 'Regular response' + + .. versionadded:: 0.10 + """ + top = _request_ctx_stack.top + if top is None: + raise RuntimeError('This decorator can only be used at local scopes ' + 'when a request context is on the stack. For instance within ' + 'view functions.') + reqctx = top.copy() + def wrapper(*args, **kwargs): + with reqctx: + return f(*args, **kwargs) + return update_wrapper(wrapper, f) + + +def has_request_context(): + """If you have code that wants to test if a request context is there or + not this function can be used. For instance, you may want to take advantage + of request information if the request object is available, but fail + silently if it is unavailable. + + :: + + class User(db.Model): + + def __init__(self, username, remote_addr=None): + self.username = username + if remote_addr is None and has_request_context(): + remote_addr = request.remote_addr + self.remote_addr = remote_addr + + Alternatively you can also just test any of the context bound objects + (such as :class:`request` or :class:`g` for truthness):: + + class User(db.Model): + + def __init__(self, username, remote_addr=None): + self.username = username + if remote_addr is None and request: + remote_addr = request.remote_addr + self.remote_addr = remote_addr + + .. versionadded:: 0.7 + """ + return _request_ctx_stack.top is not None + + +def has_app_context(): + """Works like :func:`has_request_context` but for the application + context. You can also just do a boolean check on the + :data:`current_app` object instead. + + .. versionadded:: 0.9 + """ + return _app_ctx_stack.top is not None + + +class AppContext(object): + """The application context binds an application object implicitly + to the current thread or greenlet, similar to how the + :class:`RequestContext` binds request information. The application + context is also implicitly created if a request context is created + but the application is not on top of the individual application + context. + """ + + def __init__(self, app): + self.app = app + self.url_adapter = app.create_url_adapter(None) + self.g = app.app_ctx_globals_class() + + # Like request context, app contexts can be pushed multiple times + # but there a basic "refcount" is enough to track them. + self._refcnt = 0 + + def push(self): + """Binds the app context to the current context.""" + self._refcnt += 1 + if hasattr(sys, 'exc_clear'): + sys.exc_clear() + _app_ctx_stack.push(self) + appcontext_pushed.send(self.app) + + def pop(self, exc=_sentinel): + """Pops the app context.""" + try: + self._refcnt -= 1 + if self._refcnt <= 0: + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_appcontext(exc) + finally: + rv = _app_ctx_stack.pop() + assert rv is self, 'Popped wrong app context. (%r instead of %r)' \ + % (rv, self) + appcontext_popped.send(self.app) + + def __enter__(self): + self.push() + return self + + def __exit__(self, exc_type, exc_value, tb): + self.pop(exc_value) + + if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None: + reraise(exc_type, exc_value, tb) + + +class RequestContext(object): + """The request context contains all request relevant information. It is + created at the beginning of the request and pushed to the + `_request_ctx_stack` and removed at the end of it. It will create the + URL adapter and request object for the WSGI environment provided. + + Do not attempt to use this class directly, instead use + :meth:`~flask.Flask.test_request_context` and + :meth:`~flask.Flask.request_context` to create this object. + + When the request context is popped, it will evaluate all the + functions registered on the application for teardown execution + (:meth:`~flask.Flask.teardown_request`). + + The request context is automatically popped at the end of the request + for you. In debug mode the request context is kept around if + exceptions happen so that interactive debuggers have a chance to + introspect the data. With 0.4 this can also be forced for requests + that did not fail and outside of ``DEBUG`` mode. By setting + ``'flask._preserve_context'`` to ``True`` on the WSGI environment the + context will not pop itself at the end of the request. This is used by + the :meth:`~flask.Flask.test_client` for example to implement the + deferred cleanup functionality. + + You might find this helpful for unittests where you need the + information from the context local around for a little longer. Make + sure to properly :meth:`~werkzeug.LocalStack.pop` the stack yourself in + that situation, otherwise your unittests will leak memory. + """ + + def __init__(self, app, environ, request=None): + self.app = app + if request is None: + request = app.request_class(environ) + self.request = request + self.url_adapter = app.create_url_adapter(self.request) + self.flashes = None + self.session = None + + # Request contexts can be pushed multiple times and interleaved with + # other request contexts. Now only if the last level is popped we + # get rid of them. Additionally if an application context is missing + # one is created implicitly so for each level we add this information + self._implicit_app_ctx_stack = [] + + # indicator if the context was preserved. Next time another context + # is pushed the preserved context is popped. + self.preserved = False + + # remembers the exception for pop if there is one in case the context + # preservation kicks in. + self._preserved_exc = None + + # Functions that should be executed after the request on the response + # object. These will be called before the regular "after_request" + # functions. + self._after_request_functions = [] + + self.match_request() + + def _get_g(self): + return _app_ctx_stack.top.g + def _set_g(self, value): + _app_ctx_stack.top.g = value + g = property(_get_g, _set_g) + del _get_g, _set_g + + def copy(self): + """Creates a copy of this request context with the same request object. + This can be used to move a request context to a different greenlet. + Because the actual request object is the same this cannot be used to + move a request context to a different thread unless access to the + request object is locked. + + .. versionadded:: 0.10 + """ + return self.__class__(self.app, + environ=self.request.environ, + request=self.request + ) + + def match_request(self): + """Can be overridden by a subclass to hook into the matching + of the request. + """ + try: + url_rule, self.request.view_args = \ + self.url_adapter.match(return_rule=True) + self.request.url_rule = url_rule + except HTTPException as e: + self.request.routing_exception = e + + def push(self): + """Binds the request context to the current context.""" + # If an exception occurs in debug mode or if context preservation is + # activated under exception situations exactly one context stays + # on the stack. The rationale is that you want to access that + # information under debug situations. However if someone forgets to + # pop that context again we want to make sure that on the next push + # it's invalidated, otherwise we run at risk that something leaks + # memory. This is usually only a problem in test suite since this + # functionality is not active in production environments. + top = _request_ctx_stack.top + if top is not None and top.preserved: + top.pop(top._preserved_exc) + + # Before we push the request context we have to ensure that there + # is an application context. + app_ctx = _app_ctx_stack.top + if app_ctx is None or app_ctx.app != self.app: + app_ctx = self.app.app_context() + app_ctx.push() + self._implicit_app_ctx_stack.append(app_ctx) + else: + self._implicit_app_ctx_stack.append(None) + + if hasattr(sys, 'exc_clear'): + sys.exc_clear() + + _request_ctx_stack.push(self) + + # Open the session at the moment that the request context is available. + # This allows a custom open_session method to use the request context. + # Only open a new session if this is the first time the request was + # pushed, otherwise stream_with_context loses the session. + if self.session is None: + session_interface = self.app.session_interface + self.session = session_interface.open_session( + self.app, self.request + ) + + if self.session is None: + self.session = session_interface.make_null_session(self.app) + + def pop(self, exc=_sentinel): + """Pops the request context and unbinds it by doing that. This will + also trigger the execution of functions registered by the + :meth:`~flask.Flask.teardown_request` decorator. + + .. versionchanged:: 0.9 + Added the `exc` argument. + """ + app_ctx = self._implicit_app_ctx_stack.pop() + + try: + clear_request = False + if not self._implicit_app_ctx_stack: + self.preserved = False + self._preserved_exc = None + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_request(exc) + + # If this interpreter supports clearing the exception information + # we do that now. This will only go into effect on Python 2.x, + # on 3.x it disappears automatically at the end of the exception + # stack. + if hasattr(sys, 'exc_clear'): + sys.exc_clear() + + request_close = getattr(self.request, 'close', None) + if request_close is not None: + request_close() + clear_request = True + finally: + rv = _request_ctx_stack.pop() + + # get rid of circular dependencies at the end of the request + # so that we don't require the GC to be active. + if clear_request: + rv.request.environ['werkzeug.request'] = None + + # Get rid of the app as well if necessary. + if app_ctx is not None: + app_ctx.pop(exc) + + assert rv is self, 'Popped wrong request context. ' \ + '(%r instead of %r)' % (rv, self) + + def auto_pop(self, exc): + if self.request.environ.get('flask._preserve_context') or \ + (exc is not None and self.app.preserve_context_on_exception): + self.preserved = True + self._preserved_exc = exc + else: + self.pop(exc) + + def __enter__(self): + self.push() + return self + + def __exit__(self, exc_type, exc_value, tb): + # do not pop the request stack if we are in debug mode and an + # exception happened. This will allow the debugger to still + # access the request object in the interactive shell. Furthermore + # the context can be force kept alive for the test client. + # See flask.testing for how this works. + self.auto_pop(exc_value) + + if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None: + reraise(exc_type, exc_value, tb) + + def __repr__(self): + return '<%s \'%s\' [%s] of %s>' % ( + self.__class__.__name__, + self.request.url, + self.request.method, + self.app.name, + ) diff --git a/venv/Lib/site-packages/flask/debughelpers.py b/venv/Lib/site-packages/flask/debughelpers.py new file mode 100644 index 0000000..e9765f2 --- /dev/null +++ b/venv/Lib/site-packages/flask/debughelpers.py @@ -0,0 +1,168 @@ +# -*- coding: utf-8 -*- +""" + flask.debughelpers + ~~~~~~~~~~~~~~~~~~ + + Various helpers to make the development experience better. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +import os +from warnings import warn + +from ._compat import implements_to_string, text_type +from .app import Flask +from .blueprints import Blueprint +from .globals import _request_ctx_stack + + +class UnexpectedUnicodeError(AssertionError, UnicodeError): + """Raised in places where we want some better error reporting for + unexpected unicode or binary data. + """ + + +@implements_to_string +class DebugFilesKeyError(KeyError, AssertionError): + """Raised from request.files during debugging. The idea is that it can + provide a better error message than just a generic KeyError/BadRequest. + """ + + def __init__(self, request, key): + form_matches = request.form.getlist(key) + buf = ['You tried to access the file "%s" in the request.files ' + 'dictionary but it does not exist. The mimetype for the request ' + 'is "%s" instead of "multipart/form-data" which means that no ' + 'file contents were transmitted. To fix this error you should ' + 'provide enctype="multipart/form-data" in your form.' % + (key, request.mimetype)] + if form_matches: + buf.append('\n\nThe browser instead transmitted some file names. ' + 'This was submitted: %s' % ', '.join('"%s"' % x + for x in form_matches)) + self.msg = ''.join(buf) + + def __str__(self): + return self.msg + + +class FormDataRoutingRedirect(AssertionError): + """This exception is raised by Flask in debug mode if it detects a + redirect caused by the routing system when the request method is not + GET, HEAD or OPTIONS. Reasoning: form data will be dropped. + """ + + def __init__(self, request): + exc = request.routing_exception + buf = ['A request was sent to this URL (%s) but a redirect was ' + 'issued automatically by the routing system to "%s".' + % (request.url, exc.new_url)] + + # In case just a slash was appended we can be extra helpful + if request.base_url + '/' == exc.new_url.split('?')[0]: + buf.append(' The URL was defined with a trailing slash so ' + 'Flask will automatically redirect to the URL ' + 'with the trailing slash if it was accessed ' + 'without one.') + + buf.append(' Make sure to directly send your %s-request to this URL ' + 'since we can\'t make browsers or HTTP clients redirect ' + 'with form data reliably or without user interaction.' % + request.method) + buf.append('\n\nNote: this exception is only raised in debug mode') + AssertionError.__init__(self, ''.join(buf).encode('utf-8')) + + +def attach_enctype_error_multidict(request): + """Since Flask 0.8 we're monkeypatching the files object in case a + request is detected that does not use multipart form data but the files + object is accessed. + """ + oldcls = request.files.__class__ + class newcls(oldcls): + def __getitem__(self, key): + try: + return oldcls.__getitem__(self, key) + except KeyError: + if key not in request.form: + raise + raise DebugFilesKeyError(request, key) + newcls.__name__ = oldcls.__name__ + newcls.__module__ = oldcls.__module__ + request.files.__class__ = newcls + + +def _dump_loader_info(loader): + yield 'class: %s.%s' % (type(loader).__module__, type(loader).__name__) + for key, value in sorted(loader.__dict__.items()): + if key.startswith('_'): + continue + if isinstance(value, (tuple, list)): + if not all(isinstance(x, (str, text_type)) for x in value): + continue + yield '%s:' % key + for item in value: + yield ' - %s' % item + continue + elif not isinstance(value, (str, text_type, int, float, bool)): + continue + yield '%s: %r' % (key, value) + + +def explain_template_loading_attempts(app, template, attempts): + """This should help developers understand what failed""" + info = ['Locating template "%s":' % template] + total_found = 0 + blueprint = None + reqctx = _request_ctx_stack.top + if reqctx is not None and reqctx.request.blueprint is not None: + blueprint = reqctx.request.blueprint + + for idx, (loader, srcobj, triple) in enumerate(attempts): + if isinstance(srcobj, Flask): + src_info = 'application "%s"' % srcobj.import_name + elif isinstance(srcobj, Blueprint): + src_info = 'blueprint "%s" (%s)' % (srcobj.name, + srcobj.import_name) + else: + src_info = repr(srcobj) + + info.append('% 5d: trying loader of %s' % ( + idx + 1, src_info)) + + for line in _dump_loader_info(loader): + info.append(' %s' % line) + + if triple is None: + detail = 'no match' + else: + detail = 'found (%r)' % (triple[1] or '<string>') + total_found += 1 + info.append(' -> %s' % detail) + + seems_fishy = False + if total_found == 0: + info.append('Error: the template could not be found.') + seems_fishy = True + elif total_found > 1: + info.append('Warning: multiple loaders returned a match for the template.') + seems_fishy = True + + if blueprint is not None and seems_fishy: + info.append(' The template was looked up from an endpoint that ' + 'belongs to the blueprint "%s".' % blueprint) + info.append(' Maybe you did not place a template in the right folder?') + info.append(' See http://flask.pocoo.org/docs/blueprints/#templates') + + app.logger.info('\n'.join(info)) + + +def explain_ignored_app_run(): + if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': + warn(Warning('Silently ignoring app.run() because the ' + 'application is run from the flask command line ' + 'executable. Consider putting app.run() behind an ' + 'if __name__ == "__main__" guard to silence this ' + 'warning.'), stacklevel=3) diff --git a/venv/Lib/site-packages/flask/globals.py b/venv/Lib/site-packages/flask/globals.py new file mode 100644 index 0000000..7d50a6f --- /dev/null +++ b/venv/Lib/site-packages/flask/globals.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +""" + flask.globals + ~~~~~~~~~~~~~ + + Defines all the global objects that are proxies to the current + active context. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +from functools import partial +from werkzeug.local import LocalStack, LocalProxy + + +_request_ctx_err_msg = '''\ +Working outside of request context. + +This typically means that you attempted to use functionality that needed +an active HTTP request. Consult the documentation on testing for +information about how to avoid this problem.\ +''' +_app_ctx_err_msg = '''\ +Working outside of application context. + +This typically means that you attempted to use functionality that needed +to interface with the current application object in some way. To solve +this, set up an application context with app.app_context(). See the +documentation for more information.\ +''' + + +def _lookup_req_object(name): + top = _request_ctx_stack.top + if top is None: + raise RuntimeError(_request_ctx_err_msg) + return getattr(top, name) + + +def _lookup_app_object(name): + top = _app_ctx_stack.top + if top is None: + raise RuntimeError(_app_ctx_err_msg) + return getattr(top, name) + + +def _find_app(): + top = _app_ctx_stack.top + if top is None: + raise RuntimeError(_app_ctx_err_msg) + return top.app + + +# context locals +_request_ctx_stack = LocalStack() +_app_ctx_stack = LocalStack() +current_app = LocalProxy(_find_app) +request = LocalProxy(partial(_lookup_req_object, 'request')) +session = LocalProxy(partial(_lookup_req_object, 'session')) +g = LocalProxy(partial(_lookup_app_object, 'g')) diff --git a/venv/Lib/site-packages/flask/helpers.py b/venv/Lib/site-packages/flask/helpers.py new file mode 100644 index 0000000..df0b91f --- /dev/null +++ b/venv/Lib/site-packages/flask/helpers.py @@ -0,0 +1,1044 @@ +# -*- coding: utf-8 -*- +""" + flask.helpers + ~~~~~~~~~~~~~ + + Implements various helpers. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +import os +import socket +import sys +import pkgutil +import posixpath +import mimetypes +from time import time +from zlib import adler32 +from threading import RLock +import unicodedata +from werkzeug.routing import BuildError +from functools import update_wrapper + +from werkzeug.urls import url_quote +from werkzeug.datastructures import Headers, Range +from werkzeug.exceptions import BadRequest, NotFound, \ + RequestedRangeNotSatisfiable + +from werkzeug.wsgi import wrap_file +from jinja2 import FileSystemLoader + +from .signals import message_flashed +from .globals import session, _request_ctx_stack, _app_ctx_stack, \ + current_app, request +from ._compat import string_types, text_type, PY2 + +# sentinel +_missing = object() + + +# what separators does this operating system provide that are not a slash? +# this is used by the send_from_directory function to ensure that nobody is +# able to access files from outside the filesystem. +_os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep] + if sep not in (None, '/')) + + +def get_env(): + """Get the environment the app is running in, indicated by the + :envvar:`FLASK_ENV` environment variable. The default is + ``'production'``. + """ + return os.environ.get('FLASK_ENV') or 'production' + + +def get_debug_flag(): + """Get whether debug mode should be enabled for the app, indicated + by the :envvar:`FLASK_DEBUG` environment variable. The default is + ``True`` if :func:`.get_env` returns ``'development'``, or ``False`` + otherwise. + """ + val = os.environ.get('FLASK_DEBUG') + + if not val: + return get_env() == 'development' + + return val.lower() not in ('0', 'false', 'no') + + +def get_load_dotenv(default=True): + """Get whether the user has disabled loading dotenv files by setting + :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the + files. + + :param default: What to return if the env var isn't set. + """ + val = os.environ.get('FLASK_SKIP_DOTENV') + + if not val: + return default + + return val.lower() in ('0', 'false', 'no') + + +def _endpoint_from_view_func(view_func): + """Internal helper that returns the default endpoint for a given + function. This always is the function name. + """ + assert view_func is not None, 'expected view func if endpoint ' \ + 'is not provided.' + return view_func.__name__ + + +def stream_with_context(generator_or_function): + """Request contexts disappear when the response is started on the server. + This is done for efficiency reasons and to make it less likely to encounter + memory leaks with badly written WSGI middlewares. The downside is that if + you are using streamed responses, the generator cannot access request bound + information any more. + + This function however can help you keep the context around for longer:: + + from flask import stream_with_context, request, Response + + @app.route('/stream') + def streamed_response(): + @stream_with_context + def generate(): + yield 'Hello ' + yield request.args['name'] + yield '!' + return Response(generate()) + + Alternatively it can also be used around a specific generator:: + + from flask import stream_with_context, request, Response + + @app.route('/stream') + def streamed_response(): + def generate(): + yield 'Hello ' + yield request.args['name'] + yield '!' + return Response(stream_with_context(generate())) + + .. versionadded:: 0.9 + """ + try: + gen = iter(generator_or_function) + except TypeError: + def decorator(*args, **kwargs): + gen = generator_or_function(*args, **kwargs) + return stream_with_context(gen) + return update_wrapper(decorator, generator_or_function) + + def generator(): + ctx = _request_ctx_stack.top + if ctx is None: + raise RuntimeError('Attempted to stream with context but ' + 'there was no context in the first place to keep around.') + with ctx: + # Dummy sentinel. Has to be inside the context block or we're + # not actually keeping the context around. + yield None + + # The try/finally is here so that if someone passes a WSGI level + # iterator in we're still running the cleanup logic. Generators + # don't need that because they are closed on their destruction + # automatically. + try: + for item in gen: + yield item + finally: + if hasattr(gen, 'close'): + gen.close() + + # The trick is to start the generator. Then the code execution runs until + # the first dummy None is yielded at which point the context was already + # pushed. This item is discarded. Then when the iteration continues the + # real generator is executed. + wrapped_g = generator() + next(wrapped_g) + return wrapped_g + + +def make_response(*args): + """Sometimes it is necessary to set additional headers in a view. Because + views do not have to return response objects but can return a value that + is converted into a response object by Flask itself, it becomes tricky to + add headers to it. This function can be called instead of using a return + and you will get a response object which you can use to attach headers. + + If view looked like this and you want to add a new header:: + + def index(): + return render_template('index.html', foo=42) + + You can now do something like this:: + + def index(): + response = make_response(render_template('index.html', foo=42)) + response.headers['X-Parachutes'] = 'parachutes are cool' + return response + + This function accepts the very same arguments you can return from a + view function. This for example creates a response with a 404 error + code:: + + response = make_response(render_template('not_found.html'), 404) + + The other use case of this function is to force the return value of a + view function into a response which is helpful with view + decorators:: + + response = make_response(view_function()) + response.headers['X-Parachutes'] = 'parachutes are cool' + + Internally this function does the following things: + + - if no arguments are passed, it creates a new response argument + - if one argument is passed, :meth:`flask.Flask.make_response` + is invoked with it. + - if more than one argument is passed, the arguments are passed + to the :meth:`flask.Flask.make_response` function as tuple. + + .. versionadded:: 0.6 + """ + if not args: + return current_app.response_class() + if len(args) == 1: + args = args[0] + return current_app.make_response(args) + + +def url_for(endpoint, **values): + """Generates a URL to the given endpoint with the method provided. + + Variable arguments that are unknown to the target endpoint are appended + to the generated URL as query arguments. If the value of a query argument + is ``None``, the whole pair is skipped. In case blueprints are active + you can shortcut references to the same blueprint by prefixing the + local endpoint with a dot (``.``). + + This will reference the index function local to the current blueprint:: + + url_for('.index') + + For more information, head over to the :ref:`Quickstart <url-building>`. + + To integrate applications, :class:`Flask` has a hook to intercept URL build + errors through :attr:`Flask.url_build_error_handlers`. The `url_for` + function results in a :exc:`~werkzeug.routing.BuildError` when the current + app does not have a URL for the given endpoint and values. When it does, the + :data:`~flask.current_app` calls its :attr:`~Flask.url_build_error_handlers` if + it is not ``None``, which can return a string to use as the result of + `url_for` (instead of `url_for`'s default to raise the + :exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception. + An example:: + + def external_url_handler(error, endpoint, values): + "Looks up an external URL when `url_for` cannot build a URL." + # This is an example of hooking the build_error_handler. + # Here, lookup_url is some utility function you've built + # which looks up the endpoint in some external URL registry. + url = lookup_url(endpoint, **values) + if url is None: + # External lookup did not have a URL. + # Re-raise the BuildError, in context of original traceback. + exc_type, exc_value, tb = sys.exc_info() + if exc_value is error: + raise exc_type, exc_value, tb + else: + raise error + # url_for will use this result, instead of raising BuildError. + return url + + app.url_build_error_handlers.append(external_url_handler) + + Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and + `endpoint` and `values` are the arguments passed into `url_for`. Note + that this is for building URLs outside the current application, and not for + handling 404 NotFound errors. + + .. versionadded:: 0.10 + The `_scheme` parameter was added. + + .. versionadded:: 0.9 + The `_anchor` and `_method` parameters were added. + + .. versionadded:: 0.9 + Calls :meth:`Flask.handle_build_error` on + :exc:`~werkzeug.routing.BuildError`. + + :param endpoint: the endpoint of the URL (name of the function) + :param values: the variable arguments of the URL rule + :param _external: if set to ``True``, an absolute URL is generated. Server + address can be changed via ``SERVER_NAME`` configuration variable which + defaults to `localhost`. + :param _scheme: a string specifying the desired URL scheme. The `_external` + parameter must be set to ``True`` or a :exc:`ValueError` is raised. The default + behavior uses the same scheme as the current request, or + ``PREFERRED_URL_SCHEME`` from the :ref:`app configuration <config>` if no + request context is available. As of Werkzeug 0.10, this also can be set + to an empty string to build protocol-relative URLs. + :param _anchor: if provided this is added as anchor to the URL. + :param _method: if provided this explicitly specifies an HTTP method. + """ + appctx = _app_ctx_stack.top + reqctx = _request_ctx_stack.top + + if appctx is None: + raise RuntimeError( + 'Attempted to generate a URL without the application context being' + ' pushed. This has to be executed when application context is' + ' available.' + ) + + # If request specific information is available we have some extra + # features that support "relative" URLs. + if reqctx is not None: + url_adapter = reqctx.url_adapter + blueprint_name = request.blueprint + + if endpoint[:1] == '.': + if blueprint_name is not None: + endpoint = blueprint_name + endpoint + else: + endpoint = endpoint[1:] + + external = values.pop('_external', False) + + # Otherwise go with the url adapter from the appctx and make + # the URLs external by default. + else: + url_adapter = appctx.url_adapter + + if url_adapter is None: + raise RuntimeError( + 'Application was not able to create a URL adapter for request' + ' independent URL generation. You might be able to fix this by' + ' setting the SERVER_NAME config variable.' + ) + + external = values.pop('_external', True) + + anchor = values.pop('_anchor', None) + method = values.pop('_method', None) + scheme = values.pop('_scheme', None) + appctx.app.inject_url_defaults(endpoint, values) + + # This is not the best way to deal with this but currently the + # underlying Werkzeug router does not support overriding the scheme on + # a per build call basis. + old_scheme = None + if scheme is not None: + if not external: + raise ValueError('When specifying _scheme, _external must be True') + old_scheme = url_adapter.url_scheme + url_adapter.url_scheme = scheme + + try: + try: + rv = url_adapter.build(endpoint, values, method=method, + force_external=external) + finally: + if old_scheme is not None: + url_adapter.url_scheme = old_scheme + except BuildError as error: + # We need to inject the values again so that the app callback can + # deal with that sort of stuff. + values['_external'] = external + values['_anchor'] = anchor + values['_method'] = method + values['_scheme'] = scheme + return appctx.app.handle_url_build_error(error, endpoint, values) + + if anchor is not None: + rv += '#' + url_quote(anchor) + return rv + + +def get_template_attribute(template_name, attribute): + """Loads a macro (or variable) a template exports. This can be used to + invoke a macro from within Python code. If you for example have a + template named :file:`_cider.html` with the following contents: + + .. sourcecode:: html+jinja + + {% macro hello(name) %}Hello {{ name }}!{% endmacro %} + + You can access this from Python code like this:: + + hello = get_template_attribute('_cider.html', 'hello') + return hello('World') + + .. versionadded:: 0.2 + + :param template_name: the name of the template + :param attribute: the name of the variable of macro to access + """ + return getattr(current_app.jinja_env.get_template(template_name).module, + attribute) + + +def flash(message, category='message'): + """Flashes a message to the next request. In order to remove the + flashed message from the session and to display it to the user, + the template has to call :func:`get_flashed_messages`. + + .. versionchanged:: 0.3 + `category` parameter added. + + :param message: the message to be flashed. + :param category: the category for the message. The following values + are recommended: ``'message'`` for any kind of message, + ``'error'`` for errors, ``'info'`` for information + messages and ``'warning'`` for warnings. However any + kind of string can be used as category. + """ + # Original implementation: + # + # session.setdefault('_flashes', []).append((category, message)) + # + # This assumed that changes made to mutable structures in the session are + # always in sync with the session object, which is not true for session + # implementations that use external storage for keeping their keys/values. + flashes = session.get('_flashes', []) + flashes.append((category, message)) + session['_flashes'] = flashes + message_flashed.send(current_app._get_current_object(), + message=message, category=category) + + +def get_flashed_messages(with_categories=False, category_filter=[]): + """Pulls all flashed messages from the session and returns them. + Further calls in the same request to the function will return + the same messages. By default just the messages are returned, + but when `with_categories` is set to ``True``, the return value will + be a list of tuples in the form ``(category, message)`` instead. + + Filter the flashed messages to one or more categories by providing those + categories in `category_filter`. This allows rendering categories in + separate html blocks. The `with_categories` and `category_filter` + arguments are distinct: + + * `with_categories` controls whether categories are returned with message + text (``True`` gives a tuple, where ``False`` gives just the message text). + * `category_filter` filters the messages down to only those matching the + provided categories. + + See :ref:`message-flashing-pattern` for examples. + + .. versionchanged:: 0.3 + `with_categories` parameter added. + + .. versionchanged:: 0.9 + `category_filter` parameter added. + + :param with_categories: set to ``True`` to also receive categories. + :param category_filter: whitelist of categories to limit return values + """ + flashes = _request_ctx_stack.top.flashes + if flashes is None: + _request_ctx_stack.top.flashes = flashes = session.pop('_flashes') \ + if '_flashes' in session else [] + if category_filter: + flashes = list(filter(lambda f: f[0] in category_filter, flashes)) + if not with_categories: + return [x[1] for x in flashes] + return flashes + + +def send_file(filename_or_fp, mimetype=None, as_attachment=False, + attachment_filename=None, add_etags=True, + cache_timeout=None, conditional=False, last_modified=None): + """Sends the contents of a file to the client. This will use the + most efficient method available and configured. By default it will + try to use the WSGI server's file_wrapper support. Alternatively + you can set the application's :attr:`~Flask.use_x_sendfile` attribute + to ``True`` to directly emit an ``X-Sendfile`` header. This however + requires support of the underlying webserver for ``X-Sendfile``. + + By default it will try to guess the mimetype for you, but you can + also explicitly provide one. For extra security you probably want + to send certain files as attachment (HTML for instance). The mimetype + guessing requires a `filename` or an `attachment_filename` to be + provided. + + ETags will also be attached automatically if a `filename` is provided. You + can turn this off by setting `add_etags=False`. + + If `conditional=True` and `filename` is provided, this method will try to + upgrade the response stream to support range requests. This will allow + the request to be answered with partial content response. + + Please never pass filenames to this function from user sources; + you should use :func:`send_from_directory` instead. + + .. versionadded:: 0.2 + + .. versionadded:: 0.5 + The `add_etags`, `cache_timeout` and `conditional` parameters were + added. The default behavior is now to attach etags. + + .. versionchanged:: 0.7 + mimetype guessing and etag support for file objects was + deprecated because it was unreliable. Pass a filename if you are + able to, otherwise attach an etag yourself. This functionality + will be removed in Flask 1.0 + + .. versionchanged:: 0.9 + cache_timeout pulls its default from application config, when None. + + .. versionchanged:: 0.12 + The filename is no longer automatically inferred from file objects. If + you want to use automatic mimetype and etag support, pass a filepath via + `filename_or_fp` or `attachment_filename`. + + .. versionchanged:: 0.12 + The `attachment_filename` is preferred over `filename` for MIME-type + detection. + + .. versionchanged:: 1.0 + UTF-8 filenames, as specified in `RFC 2231`_, are supported. + + .. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4 + + :param filename_or_fp: the filename of the file to send. + This is relative to the :attr:`~Flask.root_path` + if a relative path is specified. + Alternatively a file object might be provided in + which case ``X-Sendfile`` might not work and fall + back to the traditional method. Make sure that the + file pointer is positioned at the start of data to + send before calling :func:`send_file`. + :param mimetype: the mimetype of the file if provided. If a file path is + given, auto detection happens as fallback, otherwise an + error will be raised. + :param as_attachment: set to ``True`` if you want to send this file with + a ``Content-Disposition: attachment`` header. + :param attachment_filename: the filename for the attachment if it + differs from the file's filename. + :param add_etags: set to ``False`` to disable attaching of etags. + :param conditional: set to ``True`` to enable conditional responses. + + :param cache_timeout: the timeout in seconds for the headers. When ``None`` + (default), this value is set by + :meth:`~Flask.get_send_file_max_age` of + :data:`~flask.current_app`. + :param last_modified: set the ``Last-Modified`` header to this value, + a :class:`~datetime.datetime` or timestamp. + If a file was passed, this overrides its mtime. + """ + mtime = None + fsize = None + if isinstance(filename_or_fp, string_types): + filename = filename_or_fp + if not os.path.isabs(filename): + filename = os.path.join(current_app.root_path, filename) + file = None + if attachment_filename is None: + attachment_filename = os.path.basename(filename) + else: + file = filename_or_fp + filename = None + + if mimetype is None: + if attachment_filename is not None: + mimetype = mimetypes.guess_type(attachment_filename)[0] \ + or 'application/octet-stream' + + if mimetype is None: + raise ValueError( + 'Unable to infer MIME-type because no filename is available. ' + 'Please set either `attachment_filename`, pass a filepath to ' + '`filename_or_fp` or set your own MIME-type via `mimetype`.' + ) + + headers = Headers() + if as_attachment: + if attachment_filename is None: + raise TypeError('filename unavailable, required for ' + 'sending as attachment') + + try: + attachment_filename = attachment_filename.encode('latin-1') + except UnicodeEncodeError: + filenames = { + 'filename': unicodedata.normalize( + 'NFKD', attachment_filename).encode('latin-1', 'ignore'), + 'filename*': "UTF-8''%s" % url_quote(attachment_filename), + } + else: + filenames = {'filename': attachment_filename} + + headers.add('Content-Disposition', 'attachment', **filenames) + + if current_app.use_x_sendfile and filename: + if file is not None: + file.close() + headers['X-Sendfile'] = filename + fsize = os.path.getsize(filename) + headers['Content-Length'] = fsize + data = None + else: + if file is None: + file = open(filename, 'rb') + mtime = os.path.getmtime(filename) + fsize = os.path.getsize(filename) + headers['Content-Length'] = fsize + data = wrap_file(request.environ, file) + + rv = current_app.response_class(data, mimetype=mimetype, headers=headers, + direct_passthrough=True) + + if last_modified is not None: + rv.last_modified = last_modified + elif mtime is not None: + rv.last_modified = mtime + + rv.cache_control.public = True + if cache_timeout is None: + cache_timeout = current_app.get_send_file_max_age(filename) + if cache_timeout is not None: + rv.cache_control.max_age = cache_timeout + rv.expires = int(time() + cache_timeout) + + if add_etags and filename is not None: + from warnings import warn + + try: + rv.set_etag('%s-%s-%s' % ( + os.path.getmtime(filename), + os.path.getsize(filename), + adler32( + filename.encode('utf-8') if isinstance(filename, text_type) + else filename + ) & 0xffffffff + )) + except OSError: + warn('Access %s failed, maybe it does not exist, so ignore etags in ' + 'headers' % filename, stacklevel=2) + + if conditional: + try: + rv = rv.make_conditional(request, accept_ranges=True, + complete_length=fsize) + except RequestedRangeNotSatisfiable: + if file is not None: + file.close() + raise + # make sure we don't send x-sendfile for servers that + # ignore the 304 status code for x-sendfile. + if rv.status_code == 304: + rv.headers.pop('x-sendfile', None) + return rv + + +def safe_join(directory, *pathnames): + """Safely join `directory` and zero or more untrusted `pathnames` + components. + + Example usage:: + + @app.route('/wiki/<path:filename>') + def wiki_page(filename): + filename = safe_join(app.config['WIKI_FOLDER'], filename) + with open(filename, 'rb') as fd: + content = fd.read() # Read and process the file content... + + :param directory: the trusted base directory. + :param pathnames: the untrusted pathnames relative to that directory. + :raises: :class:`~werkzeug.exceptions.NotFound` if one or more passed + paths fall out of its boundaries. + """ + + parts = [directory] + + for filename in pathnames: + if filename != '': + filename = posixpath.normpath(filename) + + if ( + any(sep in filename for sep in _os_alt_seps) + or os.path.isabs(filename) + or filename == '..' + or filename.startswith('../') + ): + raise NotFound() + + parts.append(filename) + + return posixpath.join(*parts) + + +def send_from_directory(directory, filename, **options): + """Send a file from a given directory with :func:`send_file`. This + is a secure way to quickly expose static files from an upload folder + or something similar. + + Example usage:: + + @app.route('/uploads/<path:filename>') + def download_file(filename): + return send_from_directory(app.config['UPLOAD_FOLDER'], + filename, as_attachment=True) + + .. admonition:: Sending files and Performance + + It is strongly recommended to activate either ``X-Sendfile`` support in + your webserver or (if no authentication happens) to tell the webserver + to serve files for the given path on its own without calling into the + web application for improved performance. + + .. versionadded:: 0.5 + + :param directory: the directory where all the files are stored. + :param filename: the filename relative to that directory to + download. + :param options: optional keyword arguments that are directly + forwarded to :func:`send_file`. + """ + filename = safe_join(directory, filename) + if not os.path.isabs(filename): + filename = os.path.join(current_app.root_path, filename) + try: + if not os.path.isfile(filename): + raise NotFound() + except (TypeError, ValueError): + raise BadRequest() + options.setdefault('conditional', True) + return send_file(filename, **options) + + +def get_root_path(import_name): + """Returns the path to a package or cwd if that cannot be found. This + returns the path of a package or the folder that contains a module. + + Not to be confused with the package path returned by :func:`find_package`. + """ + # Module already imported and has a file attribute. Use that first. + mod = sys.modules.get(import_name) + if mod is not None and hasattr(mod, '__file__'): + return os.path.dirname(os.path.abspath(mod.__file__)) + + # Next attempt: check the loader. + loader = pkgutil.get_loader(import_name) + + # Loader does not exist or we're referring to an unloaded main module + # or a main module without path (interactive sessions), go with the + # current working directory. + if loader is None or import_name == '__main__': + return os.getcwd() + + # For .egg, zipimporter does not have get_filename until Python 2.7. + # Some other loaders might exhibit the same behavior. + if hasattr(loader, 'get_filename'): + filepath = loader.get_filename(import_name) + else: + # Fall back to imports. + __import__(import_name) + mod = sys.modules[import_name] + filepath = getattr(mod, '__file__', None) + + # If we don't have a filepath it might be because we are a + # namespace package. In this case we pick the root path from the + # first module that is contained in our package. + if filepath is None: + raise RuntimeError('No root path can be found for the provided ' + 'module "%s". This can happen because the ' + 'module came from an import hook that does ' + 'not provide file name information or because ' + 'it\'s a namespace package. In this case ' + 'the root path needs to be explicitly ' + 'provided.' % import_name) + + # filepath is import_name.py for a module, or __init__.py for a package. + return os.path.dirname(os.path.abspath(filepath)) + + +def _matching_loader_thinks_module_is_package(loader, mod_name): + """Given the loader that loaded a module and the module this function + attempts to figure out if the given module is actually a package. + """ + # If the loader can tell us if something is a package, we can + # directly ask the loader. + if hasattr(loader, 'is_package'): + return loader.is_package(mod_name) + # importlib's namespace loaders do not have this functionality but + # all the modules it loads are packages, so we can take advantage of + # this information. + elif (loader.__class__.__module__ == '_frozen_importlib' and + loader.__class__.__name__ == 'NamespaceLoader'): + return True + # Otherwise we need to fail with an error that explains what went + # wrong. + raise AttributeError( + ('%s.is_package() method is missing but is required by Flask of ' + 'PEP 302 import hooks. If you do not use import hooks and ' + 'you encounter this error please file a bug against Flask.') % + loader.__class__.__name__) + + +def find_package(import_name): + """Finds a package and returns the prefix (or None if the package is + not installed) as well as the folder that contains the package or + module as a tuple. The package path returned is the module that would + have to be added to the pythonpath in order to make it possible to + import the module. The prefix is the path below which a UNIX like + folder structure exists (lib, share etc.). + """ + root_mod_name = import_name.split('.')[0] + loader = pkgutil.get_loader(root_mod_name) + if loader is None or import_name == '__main__': + # import name is not found, or interactive/main module + package_path = os.getcwd() + else: + # For .egg, zipimporter does not have get_filename until Python 2.7. + if hasattr(loader, 'get_filename'): + filename = loader.get_filename(root_mod_name) + elif hasattr(loader, 'archive'): + # zipimporter's loader.archive points to the .egg or .zip + # archive filename is dropped in call to dirname below. + filename = loader.archive + else: + # At least one loader is missing both get_filename and archive: + # Google App Engine's HardenedModulesHook + # + # Fall back to imports. + __import__(import_name) + filename = sys.modules[import_name].__file__ + package_path = os.path.abspath(os.path.dirname(filename)) + + # In case the root module is a package we need to chop of the + # rightmost part. This needs to go through a helper function + # because of python 3.3 namespace packages. + if _matching_loader_thinks_module_is_package( + loader, root_mod_name): + package_path = os.path.dirname(package_path) + + site_parent, site_folder = os.path.split(package_path) + py_prefix = os.path.abspath(sys.prefix) + if package_path.startswith(py_prefix): + return py_prefix, package_path + elif site_folder.lower() == 'site-packages': + parent, folder = os.path.split(site_parent) + # Windows like installations + if folder.lower() == 'lib': + base_dir = parent + # UNIX like installations + elif os.path.basename(parent).lower() == 'lib': + base_dir = os.path.dirname(parent) + else: + base_dir = site_parent + return base_dir, package_path + return None, package_path + + +class locked_cached_property(object): + """A decorator that converts a function into a lazy property. The + function wrapped is called the first time to retrieve the result + and then that calculated result is used the next time you access + the value. Works like the one in Werkzeug but has a lock for + thread safety. + """ + + def __init__(self, func, name=None, doc=None): + self.__name__ = name or func.__name__ + self.__module__ = func.__module__ + self.__doc__ = doc or func.__doc__ + self.func = func + self.lock = RLock() + + def __get__(self, obj, type=None): + if obj is None: + return self + with self.lock: + value = obj.__dict__.get(self.__name__, _missing) + if value is _missing: + value = self.func(obj) + obj.__dict__[self.__name__] = value + return value + + +class _PackageBoundObject(object): + #: The name of the package or module that this app belongs to. Do not + #: change this once it is set by the constructor. + import_name = None + + #: Location of the template files to be added to the template lookup. + #: ``None`` if templates should not be added. + template_folder = None + + #: Absolute path to the package on the filesystem. Used to look up + #: resources contained in the package. + root_path = None + + def __init__(self, import_name, template_folder=None, root_path=None): + self.import_name = import_name + self.template_folder = template_folder + + if root_path is None: + root_path = get_root_path(self.import_name) + + self.root_path = root_path + self._static_folder = None + self._static_url_path = None + + def _get_static_folder(self): + if self._static_folder is not None: + return os.path.join(self.root_path, self._static_folder) + + def _set_static_folder(self, value): + self._static_folder = value + + static_folder = property( + _get_static_folder, _set_static_folder, + doc='The absolute path to the configured static folder.' + ) + del _get_static_folder, _set_static_folder + + def _get_static_url_path(self): + if self._static_url_path is not None: + return self._static_url_path + + if self.static_folder is not None: + return '/' + os.path.basename(self.static_folder) + + def _set_static_url_path(self, value): + self._static_url_path = value + + static_url_path = property( + _get_static_url_path, _set_static_url_path, + doc='The URL prefix that the static route will be registered for.' + ) + del _get_static_url_path, _set_static_url_path + + @property + def has_static_folder(self): + """This is ``True`` if the package bound object's container has a + folder for static files. + + .. versionadded:: 0.5 + """ + return self.static_folder is not None + + @locked_cached_property + def jinja_loader(self): + """The Jinja loader for this package bound object. + + .. versionadded:: 0.5 + """ + if self.template_folder is not None: + return FileSystemLoader(os.path.join(self.root_path, + self.template_folder)) + + def get_send_file_max_age(self, filename): + """Provides default cache_timeout for the :func:`send_file` functions. + + By default, this function returns ``SEND_FILE_MAX_AGE_DEFAULT`` from + the configuration of :data:`~flask.current_app`. + + Static file functions such as :func:`send_from_directory` use this + function, and :func:`send_file` calls this function on + :data:`~flask.current_app` when the given cache_timeout is ``None``. If a + cache_timeout is given in :func:`send_file`, that timeout is used; + otherwise, this method is called. + + This allows subclasses to change the behavior when sending files based + on the filename. For example, to set the cache timeout for .js files + to 60 seconds:: + + class MyFlask(flask.Flask): + def get_send_file_max_age(self, name): + if name.lower().endswith('.js'): + return 60 + return flask.Flask.get_send_file_max_age(self, name) + + .. versionadded:: 0.9 + """ + return total_seconds(current_app.send_file_max_age_default) + + def send_static_file(self, filename): + """Function used internally to send static files from the static + folder to the browser. + + .. versionadded:: 0.5 + """ + if not self.has_static_folder: + raise RuntimeError('No static folder for this object') + # Ensure get_send_file_max_age is called in all cases. + # Here, we ensure get_send_file_max_age is called for Blueprints. + cache_timeout = self.get_send_file_max_age(filename) + return send_from_directory(self.static_folder, filename, + cache_timeout=cache_timeout) + + def open_resource(self, resource, mode='rb'): + """Opens a resource from the application's resource folder. To see + how this works, consider the following folder structure:: + + /myapplication.py + /schema.sql + /static + /style.css + /templates + /layout.html + /index.html + + If you want to open the :file:`schema.sql` file you would do the + following:: + + with app.open_resource('schema.sql') as f: + contents = f.read() + do_something_with(contents) + + :param resource: the name of the resource. To access resources within + subfolders use forward slashes as separator. + :param mode: resource file opening mode, default is 'rb'. + """ + if mode not in ('r', 'rb'): + raise ValueError('Resources can only be opened for reading') + return open(os.path.join(self.root_path, resource), mode) + + +def total_seconds(td): + """Returns the total seconds from a timedelta object. + + :param timedelta td: the timedelta to be converted in seconds + + :returns: number of seconds + :rtype: int + """ + return td.days * 60 * 60 * 24 + td.seconds + + +def is_ip(value): + """Determine if the given string is an IP address. + + Python 2 on Windows doesn't provide ``inet_pton``, so this only + checks IPv4 addresses in that environment. + + :param value: value to check + :type value: str + + :return: True if string is an IP address + :rtype: bool + """ + if PY2 and os.name == 'nt': + try: + socket.inet_aton(value) + return True + except socket.error: + return False + + for family in (socket.AF_INET, socket.AF_INET6): + try: + socket.inet_pton(family, value) + except socket.error: + pass + else: + return True + + return False diff --git a/venv/Lib/site-packages/flask/json/__init__.py b/venv/Lib/site-packages/flask/json/__init__.py new file mode 100644 index 0000000..fbe6b92 --- /dev/null +++ b/venv/Lib/site-packages/flask/json/__init__.py @@ -0,0 +1,327 @@ +# -*- coding: utf-8 -*- +""" +flask.json +~~~~~~~~~~ + +:copyright: © 2010 by the Pallets team. +:license: BSD, see LICENSE for more details. +""" +import codecs +import io +import uuid +from datetime import date, datetime +from flask.globals import current_app, request +from flask._compat import text_type, PY2 + +from werkzeug.http import http_date +from jinja2 import Markup + +# Use the same json implementation as itsdangerous on which we +# depend anyways. +from itsdangerous import json as _json + + +# Figure out if simplejson escapes slashes. This behavior was changed +# from one version to another without reason. +_slash_escape = '\\/' not in _json.dumps('/') + + +__all__ = ['dump', 'dumps', 'load', 'loads', 'htmlsafe_dump', + 'htmlsafe_dumps', 'JSONDecoder', 'JSONEncoder', + 'jsonify'] + + +def _wrap_reader_for_text(fp, encoding): + if isinstance(fp.read(0), bytes): + fp = io.TextIOWrapper(io.BufferedReader(fp), encoding) + return fp + + +def _wrap_writer_for_text(fp, encoding): + try: + fp.write('') + except TypeError: + fp = io.TextIOWrapper(fp, encoding) + return fp + + +class JSONEncoder(_json.JSONEncoder): + """The default Flask JSON encoder. This one extends the default simplejson + encoder by also supporting ``datetime`` objects, ``UUID`` as well as + ``Markup`` objects which are serialized as RFC 822 datetime strings (same + as the HTTP date format). In order to support more data types override the + :meth:`default` method. + """ + + def default(self, o): + """Implement this method in a subclass such that it returns a + serializable object for ``o``, or calls the base implementation (to + raise a :exc:`TypeError`). + + For example, to support arbitrary iterators, you could implement + default like this:: + + def default(self, o): + try: + iterable = iter(o) + except TypeError: + pass + else: + return list(iterable) + return JSONEncoder.default(self, o) + """ + if isinstance(o, datetime): + return http_date(o.utctimetuple()) + if isinstance(o, date): + return http_date(o.timetuple()) + if isinstance(o, uuid.UUID): + return str(o) + if hasattr(o, '__html__'): + return text_type(o.__html__()) + return _json.JSONEncoder.default(self, o) + + +class JSONDecoder(_json.JSONDecoder): + """The default JSON decoder. This one does not change the behavior from + the default simplejson decoder. Consult the :mod:`json` documentation + for more information. This decoder is not only used for the load + functions of this module but also :attr:`~flask.Request`. + """ + + +def _dump_arg_defaults(kwargs): + """Inject default arguments for dump functions.""" + if current_app: + bp = current_app.blueprints.get(request.blueprint) if request else None + kwargs.setdefault( + 'cls', + bp.json_encoder if bp and bp.json_encoder + else current_app.json_encoder + ) + + if not current_app.config['JSON_AS_ASCII']: + kwargs.setdefault('ensure_ascii', False) + + kwargs.setdefault('sort_keys', current_app.config['JSON_SORT_KEYS']) + else: + kwargs.setdefault('sort_keys', True) + kwargs.setdefault('cls', JSONEncoder) + + +def _load_arg_defaults(kwargs): + """Inject default arguments for load functions.""" + if current_app: + bp = current_app.blueprints.get(request.blueprint) if request else None + kwargs.setdefault( + 'cls', + bp.json_decoder if bp and bp.json_decoder + else current_app.json_decoder + ) + else: + kwargs.setdefault('cls', JSONDecoder) + + +def detect_encoding(data): + """Detect which UTF codec was used to encode the given bytes. + + The latest JSON standard (:rfc:`8259`) suggests that only UTF-8 is + accepted. Older documents allowed 8, 16, or 32. 16 and 32 can be big + or little endian. Some editors or libraries may prepend a BOM. + + :param data: Bytes in unknown UTF encoding. + :return: UTF encoding name + """ + head = data[:4] + + if head[:3] == codecs.BOM_UTF8: + return 'utf-8-sig' + + if b'\x00' not in head: + return 'utf-8' + + if head in (codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE): + return 'utf-32' + + if head[:2] in (codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE): + return 'utf-16' + + if len(head) == 4: + if head[:3] == b'\x00\x00\x00': + return 'utf-32-be' + + if head[::2] == b'\x00\x00': + return 'utf-16-be' + + if head[1:] == b'\x00\x00\x00': + return 'utf-32-le' + + if head[1::2] == b'\x00\x00': + return 'utf-16-le' + + if len(head) == 2: + return 'utf-16-be' if head.startswith(b'\x00') else 'utf-16-le' + + return 'utf-8' + + +def dumps(obj, **kwargs): + """Serialize ``obj`` to a JSON formatted ``str`` by using the application's + configured encoder (:attr:`~flask.Flask.json_encoder`) if there is an + application on the stack. + + This function can return ``unicode`` strings or ascii-only bytestrings by + default which coerce into unicode strings automatically. That behavior by + default is controlled by the ``JSON_AS_ASCII`` configuration variable + and can be overridden by the simplejson ``ensure_ascii`` parameter. + """ + _dump_arg_defaults(kwargs) + encoding = kwargs.pop('encoding', None) + rv = _json.dumps(obj, **kwargs) + if encoding is not None and isinstance(rv, text_type): + rv = rv.encode(encoding) + return rv + + +def dump(obj, fp, **kwargs): + """Like :func:`dumps` but writes into a file object.""" + _dump_arg_defaults(kwargs) + encoding = kwargs.pop('encoding', None) + if encoding is not None: + fp = _wrap_writer_for_text(fp, encoding) + _json.dump(obj, fp, **kwargs) + + +def loads(s, **kwargs): + """Unserialize a JSON object from a string ``s`` by using the application's + configured decoder (:attr:`~flask.Flask.json_decoder`) if there is an + application on the stack. + """ + _load_arg_defaults(kwargs) + if isinstance(s, bytes): + encoding = kwargs.pop('encoding', None) + if encoding is None: + encoding = detect_encoding(s) + s = s.decode(encoding) + return _json.loads(s, **kwargs) + + +def load(fp, **kwargs): + """Like :func:`loads` but reads from a file object. + """ + _load_arg_defaults(kwargs) + if not PY2: + fp = _wrap_reader_for_text(fp, kwargs.pop('encoding', None) or 'utf-8') + return _json.load(fp, **kwargs) + + +def htmlsafe_dumps(obj, **kwargs): + """Works exactly like :func:`dumps` but is safe for use in ``<script>`` + tags. It accepts the same arguments and returns a JSON string. Note that + this is available in templates through the ``|tojson`` filter which will + also mark the result as safe. Due to how this function escapes certain + characters this is safe even if used outside of ``<script>`` tags. + + The following characters are escaped in strings: + + - ``<`` + - ``>`` + - ``&`` + - ``'`` + + This makes it safe to embed such strings in any place in HTML with the + notable exception of double quoted attributes. In that case single + quote your attributes or HTML escape it in addition. + + .. versionchanged:: 0.10 + This function's return value is now always safe for HTML usage, even + if outside of script tags or if used in XHTML. This rule does not + hold true when using this function in HTML attributes that are double + quoted. Always single quote attributes if you use the ``|tojson`` + filter. Alternatively use ``|tojson|forceescape``. + """ + rv = dumps(obj, **kwargs) \ + .replace(u'<', u'\\u003c') \ + .replace(u'>', u'\\u003e') \ + .replace(u'&', u'\\u0026') \ + .replace(u"'", u'\\u0027') + if not _slash_escape: + rv = rv.replace('\\/', '/') + return rv + + +def htmlsafe_dump(obj, fp, **kwargs): + """Like :func:`htmlsafe_dumps` but writes into a file object.""" + fp.write(text_type(htmlsafe_dumps(obj, **kwargs))) + + +def jsonify(*args, **kwargs): + """This function wraps :func:`dumps` to add a few enhancements that make + life easier. It turns the JSON output into a :class:`~flask.Response` + object with the :mimetype:`application/json` mimetype. For convenience, it + also converts multiple arguments into an array or multiple keyword arguments + into a dict. This means that both ``jsonify(1,2,3)`` and + ``jsonify([1,2,3])`` serialize to ``[1,2,3]``. + + For clarity, the JSON serialization behavior has the following differences + from :func:`dumps`: + + 1. Single argument: Passed straight through to :func:`dumps`. + 2. Multiple arguments: Converted to an array before being passed to + :func:`dumps`. + 3. Multiple keyword arguments: Converted to a dict before being passed to + :func:`dumps`. + 4. Both args and kwargs: Behavior undefined and will throw an exception. + + Example usage:: + + from flask import jsonify + + @app.route('/_get_current_user') + def get_current_user(): + return jsonify(username=g.user.username, + email=g.user.email, + id=g.user.id) + + This will send a JSON response like this to the browser:: + + { + "username": "admin", + "email": "admin@localhost", + "id": 42 + } + + + .. versionchanged:: 0.11 + Added support for serializing top-level arrays. This introduces a + security risk in ancient browsers. See :ref:`json-security` for details. + + This function's response will be pretty printed if the + ``JSONIFY_PRETTYPRINT_REGULAR`` config parameter is set to True or the + Flask app is running in debug mode. Compressed (not pretty) formatting + currently means no indents and no spaces after separators. + + .. versionadded:: 0.2 + """ + + indent = None + separators = (',', ':') + + if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] or current_app.debug: + indent = 2 + separators = (', ', ': ') + + if args and kwargs: + raise TypeError('jsonify() behavior undefined when passed both args and kwargs') + elif len(args) == 1: # single args are passed directly to dumps() + data = args[0] + else: + data = args or kwargs + + return current_app.response_class( + dumps(data, indent=indent, separators=separators) + '\n', + mimetype=current_app.config['JSONIFY_MIMETYPE'] + ) + + +def tojson_filter(obj, **kwargs): + return Markup(htmlsafe_dumps(obj, **kwargs)) diff --git a/venv/Lib/site-packages/flask/json/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/flask/json/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f550b306068c3909e07191b9bb2815c9f8e833d5 GIT binary patch literal 10284 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/json/__pycache__/tag.cpython-36.pyc b/venv/Lib/site-packages/flask/json/__pycache__/tag.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2075824976f26524fab30d7f44dd1e8105df4389 GIT binary patch literal 11086 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 TC3oqYOBa?F@oyf-8D9M#*15;# literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/json/tag.py b/venv/Lib/site-packages/flask/json/tag.py new file mode 100644 index 0000000..11c966c --- /dev/null +++ b/venv/Lib/site-packages/flask/json/tag.py @@ -0,0 +1,300 @@ +# -*- coding: utf-8 -*- +""" +Tagged JSON +~~~~~~~~~~~ + +A compact representation for lossless serialization of non-standard JSON types. +:class:`~flask.sessions.SecureCookieSessionInterface` uses this to serialize +the session data, but it may be useful in other places. It can be extended to +support other types. + +.. autoclass:: TaggedJSONSerializer + :members: + +.. autoclass:: JSONTag + :members: + +Let's seen an example that adds support for :class:`~collections.OrderedDict`. +Dicts don't have an order in Python or JSON, so to handle this we will dump +the items as a list of ``[key, value]`` pairs. Subclass :class:`JSONTag` and +give it the new key ``' od'`` to identify the type. The session serializer +processes dicts first, so insert the new tag at the front of the order since +``OrderedDict`` must be processed before ``dict``. :: + + from flask.json.tag import JSONTag + + class TagOrderedDict(JSONTag): + __slots__ = ('serializer',) + key = ' od' + + def check(self, value): + return isinstance(value, OrderedDict) + + def to_json(self, value): + return [[k, self.serializer.tag(v)] for k, v in iteritems(value)] + + def to_python(self, value): + return OrderedDict(value) + + app.session_interface.serializer.register(TagOrderedDict, index=0) + +:copyright: © 2010 by the Pallets team. +:license: BSD, see LICENSE for more details. +""" + +from base64 import b64decode, b64encode +from datetime import datetime +from uuid import UUID + +from jinja2 import Markup +from werkzeug.http import http_date, parse_date + +from flask._compat import iteritems, text_type +from flask.json import dumps, loads + + +class JSONTag(object): + """Base class for defining type tags for :class:`TaggedJSONSerializer`.""" + + __slots__ = ('serializer',) + + #: The tag to mark the serialized object with. If ``None``, this tag is + #: only used as an intermediate step during tagging. + key = None + + def __init__(self, serializer): + """Create a tagger for the given serializer.""" + self.serializer = serializer + + def check(self, value): + """Check if the given value should be tagged by this tag.""" + raise NotImplementedError + + def to_json(self, value): + """Convert the Python object to an object that is a valid JSON type. + The tag will be added later.""" + raise NotImplementedError + + def to_python(self, value): + """Convert the JSON representation back to the correct type. The tag + will already be removed.""" + raise NotImplementedError + + def tag(self, value): + """Convert the value to a valid JSON type and add the tag structure + around it.""" + return {self.key: self.to_json(value)} + + +class TagDict(JSONTag): + """Tag for 1-item dicts whose only key matches a registered tag. + + Internally, the dict key is suffixed with `__`, and the suffix is removed + when deserializing. + """ + + __slots__ = () + key = ' di' + + def check(self, value): + return ( + isinstance(value, dict) + and len(value) == 1 + and next(iter(value)) in self.serializer.tags + ) + + def to_json(self, value): + key = next(iter(value)) + return {key + '__': self.serializer.tag(value[key])} + + def to_python(self, value): + key = next(iter(value)) + return {key[:-2]: value[key]} + + +class PassDict(JSONTag): + __slots__ = () + + def check(self, value): + return isinstance(value, dict) + + def to_json(self, value): + # JSON objects may only have string keys, so don't bother tagging the + # key here. + return dict((k, self.serializer.tag(v)) for k, v in iteritems(value)) + + tag = to_json + + +class TagTuple(JSONTag): + __slots__ = () + key = ' t' + + def check(self, value): + return isinstance(value, tuple) + + def to_json(self, value): + return [self.serializer.tag(item) for item in value] + + def to_python(self, value): + return tuple(value) + + +class PassList(JSONTag): + __slots__ = () + + def check(self, value): + return isinstance(value, list) + + def to_json(self, value): + return [self.serializer.tag(item) for item in value] + + tag = to_json + + +class TagBytes(JSONTag): + __slots__ = () + key = ' b' + + def check(self, value): + return isinstance(value, bytes) + + def to_json(self, value): + return b64encode(value).decode('ascii') + + def to_python(self, value): + return b64decode(value) + + +class TagMarkup(JSONTag): + """Serialize anything matching the :class:`~flask.Markup` API by + having a ``__html__`` method to the result of that method. Always + deserializes to an instance of :class:`~flask.Markup`.""" + + __slots__ = () + key = ' m' + + def check(self, value): + return callable(getattr(value, '__html__', None)) + + def to_json(self, value): + return text_type(value.__html__()) + + def to_python(self, value): + return Markup(value) + + +class TagUUID(JSONTag): + __slots__ = () + key = ' u' + + def check(self, value): + return isinstance(value, UUID) + + def to_json(self, value): + return value.hex + + def to_python(self, value): + return UUID(value) + + +class TagDateTime(JSONTag): + __slots__ = () + key = ' d' + + def check(self, value): + return isinstance(value, datetime) + + def to_json(self, value): + return http_date(value) + + def to_python(self, value): + return parse_date(value) + + +class TaggedJSONSerializer(object): + """Serializer that uses a tag system to compactly represent objects that + are not JSON types. Passed as the intermediate serializer to + :class:`itsdangerous.Serializer`. + + The following extra types are supported: + + * :class:`dict` + * :class:`tuple` + * :class:`bytes` + * :class:`~flask.Markup` + * :class:`~uuid.UUID` + * :class:`~datetime.datetime` + """ + + __slots__ = ('tags', 'order') + + #: Tag classes to bind when creating the serializer. Other tags can be + #: added later using :meth:`~register`. + default_tags = [ + TagDict, PassDict, TagTuple, PassList, TagBytes, TagMarkup, TagUUID, + TagDateTime, + ] + + def __init__(self): + self.tags = {} + self.order = [] + + for cls in self.default_tags: + self.register(cls) + + def register(self, tag_class, force=False, index=None): + """Register a new tag with this serializer. + + :param tag_class: tag class to register. Will be instantiated with this + serializer instance. + :param force: overwrite an existing tag. If false (default), a + :exc:`KeyError` is raised. + :param index: index to insert the new tag in the tag order. Useful when + the new tag is a special case of an existing tag. If ``None`` + (default), the tag is appended to the end of the order. + + :raise KeyError: if the tag key is already registered and ``force`` is + not true. + """ + tag = tag_class(self) + key = tag.key + + if key is not None: + if not force and key in self.tags: + raise KeyError("Tag '{0}' is already registered.".format(key)) + + self.tags[key] = tag + + if index is None: + self.order.append(tag) + else: + self.order.insert(index, tag) + + def tag(self, value): + """Convert a value to a tagged representation if necessary.""" + for tag in self.order: + if tag.check(value): + return tag.tag(value) + + return value + + def untag(self, value): + """Convert a tagged representation back to the original type.""" + if len(value) != 1: + return value + + key = next(iter(value)) + + if key not in self.tags: + return value + + return self.tags[key].to_python(value[key]) + + def dumps(self, value): + """Tag the value and dump it to a compact JSON string.""" + return dumps(self.tag(value), separators=(',', ':')) + + def loads(self, value): + """Load data from a JSON string and deserialized any tagged objects.""" + return loads(value, object_hook=self.untag) diff --git a/venv/Lib/site-packages/flask/logging.py b/venv/Lib/site-packages/flask/logging.py new file mode 100644 index 0000000..389c2c2 --- /dev/null +++ b/venv/Lib/site-packages/flask/logging.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +""" +flask.logging +~~~~~~~~~~~~~ + +:copyright: © 2010 by the Pallets team. +:license: BSD, see LICENSE for more details. +""" + +from __future__ import absolute_import + +import logging +import sys + +from werkzeug.local import LocalProxy + +from .globals import request + + +@LocalProxy +def wsgi_errors_stream(): + """Find the most appropriate error stream for the application. If a request + is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``. + + If you configure your own :class:`logging.StreamHandler`, you may want to + use this for the stream. If you are using file or dict configuration and + can't import this directly, you can refer to it as + ``ext://flask.logging.wsgi_errors_stream``. + """ + return request.environ['wsgi.errors'] if request else sys.stderr + + +def has_level_handler(logger): + """Check if there is a handler in the logging chain that will handle the + given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`. + """ + level = logger.getEffectiveLevel() + current = logger + + while current: + if any(handler.level <= level for handler in current.handlers): + return True + + if not current.propagate: + break + + current = current.parent + + return False + + +#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format +#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``. +default_handler = logging.StreamHandler(wsgi_errors_stream) +default_handler.setFormatter(logging.Formatter( + '[%(asctime)s] %(levelname)s in %(module)s: %(message)s' +)) + + +def create_logger(app): + """Get the ``'flask.app'`` logger and configure it if needed. + + When :attr:`~flask.Flask.debug` is enabled, set the logger level to + :data:`logging.DEBUG` if it is not set. + + If there is no handler for the logger's effective level, add a + :class:`~logging.StreamHandler` for + :func:`~flask.logging.wsgi_errors_stream` with a basic format. + """ + logger = logging.getLogger('flask.app') + + if app.debug and logger.level == logging.NOTSET: + logger.setLevel(logging.DEBUG) + + if not has_level_handler(logger): + logger.addHandler(default_handler) + + return logger diff --git a/venv/Lib/site-packages/flask/sessions.py b/venv/Lib/site-packages/flask/sessions.py new file mode 100644 index 0000000..ec4253d --- /dev/null +++ b/venv/Lib/site-packages/flask/sessions.py @@ -0,0 +1,385 @@ +# -*- coding: utf-8 -*- +""" + flask.sessions + ~~~~~~~~~~~~~~ + + Implements cookie based sessions based on itsdangerous. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +import hashlib +import warnings +from collections import MutableMapping +from datetime import datetime + +from itsdangerous import BadSignature, URLSafeTimedSerializer +from werkzeug.datastructures import CallbackDict + +from flask.helpers import is_ip, total_seconds +from flask.json.tag import TaggedJSONSerializer + + +class SessionMixin(MutableMapping): + """Expands a basic dictionary with session attributes.""" + + @property + def permanent(self): + """This reflects the ``'_permanent'`` key in the dict.""" + return self.get('_permanent', False) + + @permanent.setter + def permanent(self, value): + self['_permanent'] = bool(value) + + #: Some implementations can detect whether a session is newly + #: created, but that is not guaranteed. Use with caution. The mixin + # default is hard-coded ``False``. + new = False + + #: Some implementations can detect changes to the session and set + #: this when that happens. The mixin default is hard coded to + #: ``True``. + modified = True + + #: Some implementations can detect when session data is read or + #: written and set this when that happens. The mixin default is hard + #: coded to ``True``. + accessed = True + + +class SecureCookieSession(CallbackDict, SessionMixin): + """Base class for sessions based on signed cookies. + + This session backend will set the :attr:`modified` and + :attr:`accessed` attributes. It cannot reliably track whether a + session is new (vs. empty), so :attr:`new` remains hard coded to + ``False``. + """ + + #: When data is changed, this is set to ``True``. Only the session + #: dictionary itself is tracked; if the session contains mutable + #: data (for example a nested dict) then this must be set to + #: ``True`` manually when modifying that data. The session cookie + #: will only be written to the response if this is ``True``. + modified = False + + #: When data is read or written, this is set to ``True``. Used by + # :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie`` + #: header, which allows caching proxies to cache different pages for + #: different users. + accessed = False + + def __init__(self, initial=None): + def on_update(self): + self.modified = True + self.accessed = True + + super(SecureCookieSession, self).__init__(initial, on_update) + + def __getitem__(self, key): + self.accessed = True + return super(SecureCookieSession, self).__getitem__(key) + + def get(self, key, default=None): + self.accessed = True + return super(SecureCookieSession, self).get(key, default) + + def setdefault(self, key, default=None): + self.accessed = True + return super(SecureCookieSession, self).setdefault(key, default) + + +class NullSession(SecureCookieSession): + """Class used to generate nicer error messages if sessions are not + available. Will still allow read-only access to the empty session + but fail on setting. + """ + + def _fail(self, *args, **kwargs): + raise RuntimeError('The session is unavailable because no secret ' + 'key was set. Set the secret_key on the ' + 'application to something unique and secret.') + __setitem__ = __delitem__ = clear = pop = popitem = \ + update = setdefault = _fail + del _fail + + +class SessionInterface(object): + """The basic interface you have to implement in order to replace the + default session interface which uses werkzeug's securecookie + implementation. The only methods you have to implement are + :meth:`open_session` and :meth:`save_session`, the others have + useful defaults which you don't need to change. + + The session object returned by the :meth:`open_session` method has to + provide a dictionary like interface plus the properties and methods + from the :class:`SessionMixin`. We recommend just subclassing a dict + and adding that mixin:: + + class Session(dict, SessionMixin): + pass + + If :meth:`open_session` returns ``None`` Flask will call into + :meth:`make_null_session` to create a session that acts as replacement + if the session support cannot work because some requirement is not + fulfilled. The default :class:`NullSession` class that is created + will complain that the secret key was not set. + + To replace the session interface on an application all you have to do + is to assign :attr:`flask.Flask.session_interface`:: + + app = Flask(__name__) + app.session_interface = MySessionInterface() + + .. versionadded:: 0.8 + """ + + #: :meth:`make_null_session` will look here for the class that should + #: be created when a null session is requested. Likewise the + #: :meth:`is_null_session` method will perform a typecheck against + #: this type. + null_session_class = NullSession + + #: A flag that indicates if the session interface is pickle based. + #: This can be used by Flask extensions to make a decision in regards + #: to how to deal with the session object. + #: + #: .. versionadded:: 0.10 + pickle_based = False + + def make_null_session(self, app): + """Creates a null session which acts as a replacement object if the + real session support could not be loaded due to a configuration + error. This mainly aids the user experience because the job of the + null session is to still support lookup without complaining but + modifications are answered with a helpful error message of what + failed. + + This creates an instance of :attr:`null_session_class` by default. + """ + return self.null_session_class() + + def is_null_session(self, obj): + """Checks if a given object is a null session. Null sessions are + not asked to be saved. + + This checks if the object is an instance of :attr:`null_session_class` + by default. + """ + return isinstance(obj, self.null_session_class) + + def get_cookie_domain(self, app): + """Returns the domain that should be set for the session cookie. + + Uses ``SESSION_COOKIE_DOMAIN`` if it is configured, otherwise + falls back to detecting the domain based on ``SERVER_NAME``. + + Once detected (or if not set at all), ``SESSION_COOKIE_DOMAIN`` is + updated to avoid re-running the logic. + """ + + rv = app.config['SESSION_COOKIE_DOMAIN'] + + # set explicitly, or cached from SERVER_NAME detection + # if False, return None + if rv is not None: + return rv if rv else None + + rv = app.config['SERVER_NAME'] + + # server name not set, cache False to return none next time + if not rv: + app.config['SESSION_COOKIE_DOMAIN'] = False + return None + + # chop off the port which is usually not supported by browsers + # remove any leading '.' since we'll add that later + rv = rv.rsplit(':', 1)[0].lstrip('.') + + if '.' not in rv: + # Chrome doesn't allow names without a '.' + # this should only come up with localhost + # hack around this by not setting the name, and show a warning + warnings.warn( + '"{rv}" is not a valid cookie domain, it must contain a ".".' + ' Add an entry to your hosts file, for example' + ' "{rv}.localdomain", and use that instead.'.format(rv=rv) + ) + app.config['SESSION_COOKIE_DOMAIN'] = False + return None + + ip = is_ip(rv) + + if ip: + warnings.warn( + 'The session cookie domain is an IP address. This may not work' + ' as intended in some browsers. Add an entry to your hosts' + ' file, for example "localhost.localdomain", and use that' + ' instead.' + ) + + # if this is not an ip and app is mounted at the root, allow subdomain + # matching by adding a '.' prefix + if self.get_cookie_path(app) == '/' and not ip: + rv = '.' + rv + + app.config['SESSION_COOKIE_DOMAIN'] = rv + return rv + + def get_cookie_path(self, app): + """Returns the path for which the cookie should be valid. The + default implementation uses the value from the ``SESSION_COOKIE_PATH`` + config var if it's set, and falls back to ``APPLICATION_ROOT`` or + uses ``/`` if it's ``None``. + """ + return app.config['SESSION_COOKIE_PATH'] \ + or app.config['APPLICATION_ROOT'] + + def get_cookie_httponly(self, app): + """Returns True if the session cookie should be httponly. This + currently just returns the value of the ``SESSION_COOKIE_HTTPONLY`` + config var. + """ + return app.config['SESSION_COOKIE_HTTPONLY'] + + def get_cookie_secure(self, app): + """Returns True if the cookie should be secure. This currently + just returns the value of the ``SESSION_COOKIE_SECURE`` setting. + """ + return app.config['SESSION_COOKIE_SECURE'] + + def get_cookie_samesite(self, app): + """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the + ``SameSite`` attribute. This currently just returns the value of + the :data:`SESSION_COOKIE_SAMESITE` setting. + """ + return app.config['SESSION_COOKIE_SAMESITE'] + + def get_expiration_time(self, app, session): + """A helper method that returns an expiration date for the session + or ``None`` if the session is linked to the browser session. The + default implementation returns now + the permanent session + lifetime configured on the application. + """ + if session.permanent: + return datetime.utcnow() + app.permanent_session_lifetime + + def should_set_cookie(self, app, session): + """Used by session backends to determine if a ``Set-Cookie`` header + should be set for this session cookie for this response. If the session + has been modified, the cookie is set. If the session is permanent and + the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is + always set. + + This check is usually skipped if the session was deleted. + + .. versionadded:: 0.11 + """ + + return session.modified or ( + session.permanent and app.config['SESSION_REFRESH_EACH_REQUEST'] + ) + + def open_session(self, app, request): + """This method has to be implemented and must either return ``None`` + in case the loading failed because of a configuration error or an + instance of a session object which implements a dictionary like + interface + the methods and attributes on :class:`SessionMixin`. + """ + raise NotImplementedError() + + def save_session(self, app, session, response): + """This is called for actual sessions returned by :meth:`open_session` + at the end of the request. This is still called during a request + context so if you absolutely need access to the request you can do + that. + """ + raise NotImplementedError() + + +session_json_serializer = TaggedJSONSerializer() + + +class SecureCookieSessionInterface(SessionInterface): + """The default session interface that stores sessions in signed cookies + through the :mod:`itsdangerous` module. + """ + #: the salt that should be applied on top of the secret key for the + #: signing of cookie based sessions. + salt = 'cookie-session' + #: the hash function to use for the signature. The default is sha1 + digest_method = staticmethod(hashlib.sha1) + #: the name of the itsdangerous supported key derivation. The default + #: is hmac. + key_derivation = 'hmac' + #: A python serializer for the payload. The default is a compact + #: JSON derived serializer with support for some extra Python types + #: such as datetime objects or tuples. + serializer = session_json_serializer + session_class = SecureCookieSession + + def get_signing_serializer(self, app): + if not app.secret_key: + return None + signer_kwargs = dict( + key_derivation=self.key_derivation, + digest_method=self.digest_method + ) + return URLSafeTimedSerializer(app.secret_key, salt=self.salt, + serializer=self.serializer, + signer_kwargs=signer_kwargs) + + def open_session(self, app, request): + s = self.get_signing_serializer(app) + if s is None: + return None + val = request.cookies.get(app.session_cookie_name) + if not val: + return self.session_class() + max_age = total_seconds(app.permanent_session_lifetime) + try: + data = s.loads(val, max_age=max_age) + return self.session_class(data) + except BadSignature: + return self.session_class() + + def save_session(self, app, session, response): + domain = self.get_cookie_domain(app) + path = self.get_cookie_path(app) + + # If the session is modified to be empty, remove the cookie. + # If the session is empty, return without setting the cookie. + if not session: + if session.modified: + response.delete_cookie( + app.session_cookie_name, + domain=domain, + path=path + ) + + return + + # Add a "Vary: Cookie" header if the session was accessed at all. + if session.accessed: + response.vary.add('Cookie') + + if not self.should_set_cookie(app, session): + return + + httponly = self.get_cookie_httponly(app) + secure = self.get_cookie_secure(app) + samesite = self.get_cookie_samesite(app) + expires = self.get_expiration_time(app, session) + val = self.get_signing_serializer(app).dumps(dict(session)) + response.set_cookie( + app.session_cookie_name, + val, + expires=expires, + httponly=httponly, + domain=domain, + path=path, + secure=secure, + samesite=samesite + ) diff --git a/venv/Lib/site-packages/flask/signals.py b/venv/Lib/site-packages/flask/signals.py new file mode 100644 index 0000000..18f2630 --- /dev/null +++ b/venv/Lib/site-packages/flask/signals.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +""" + flask.signals + ~~~~~~~~~~~~~ + + Implements signals based on blinker if available, otherwise + falls silently back to a noop. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +signals_available = False +try: + from blinker import Namespace + signals_available = True +except ImportError: + class Namespace(object): + def signal(self, name, doc=None): + return _FakeSignal(name, doc) + + class _FakeSignal(object): + """If blinker is unavailable, create a fake class with the same + interface that allows sending of signals but will fail with an + error on anything else. Instead of doing anything on send, it + will just ignore the arguments and do nothing instead. + """ + + def __init__(self, name, doc=None): + self.name = name + self.__doc__ = doc + def _fail(self, *args, **kwargs): + raise RuntimeError('signalling support is unavailable ' + 'because the blinker library is ' + 'not installed.') + send = lambda *a, **kw: None + connect = disconnect = has_receivers_for = receivers_for = \ + temporarily_connected_to = connected_to = _fail + del _fail + +# The namespace for code signals. If you are not Flask code, do +# not put signals in here. Create your own namespace instead. +_signals = Namespace() + + +# Core signals. For usage examples grep the source code or consult +# the API documentation in docs/api.rst as well as docs/signals.rst +template_rendered = _signals.signal('template-rendered') +before_render_template = _signals.signal('before-render-template') +request_started = _signals.signal('request-started') +request_finished = _signals.signal('request-finished') +request_tearing_down = _signals.signal('request-tearing-down') +got_request_exception = _signals.signal('got-request-exception') +appcontext_tearing_down = _signals.signal('appcontext-tearing-down') +appcontext_pushed = _signals.signal('appcontext-pushed') +appcontext_popped = _signals.signal('appcontext-popped') +message_flashed = _signals.signal('message-flashed') diff --git a/venv/Lib/site-packages/flask/templating.py b/venv/Lib/site-packages/flask/templating.py new file mode 100644 index 0000000..0240200 --- /dev/null +++ b/venv/Lib/site-packages/flask/templating.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- +""" + flask.templating + ~~~~~~~~~~~~~~~~ + + Implements the bridge to Jinja2. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +from jinja2 import BaseLoader, Environment as BaseEnvironment, \ + TemplateNotFound + +from .globals import _request_ctx_stack, _app_ctx_stack +from .signals import template_rendered, before_render_template + + +def _default_template_ctx_processor(): + """Default template context processor. Injects `request`, + `session` and `g`. + """ + reqctx = _request_ctx_stack.top + appctx = _app_ctx_stack.top + rv = {} + if appctx is not None: + rv['g'] = appctx.g + if reqctx is not None: + rv['request'] = reqctx.request + rv['session'] = reqctx.session + return rv + + +class Environment(BaseEnvironment): + """Works like a regular Jinja2 environment but has some additional + knowledge of how Flask's blueprint works so that it can prepend the + name of the blueprint to referenced templates if necessary. + """ + + def __init__(self, app, **options): + if 'loader' not in options: + options['loader'] = app.create_global_jinja_loader() + BaseEnvironment.__init__(self, **options) + self.app = app + + +class DispatchingJinjaLoader(BaseLoader): + """A loader that looks for templates in the application and all + the blueprint folders. + """ + + def __init__(self, app): + self.app = app + + def get_source(self, environment, template): + if self.app.config['EXPLAIN_TEMPLATE_LOADING']: + return self._get_source_explained(environment, template) + return self._get_source_fast(environment, template) + + def _get_source_explained(self, environment, template): + attempts = [] + trv = None + + for srcobj, loader in self._iter_loaders(template): + try: + rv = loader.get_source(environment, template) + if trv is None: + trv = rv + except TemplateNotFound: + rv = None + attempts.append((loader, srcobj, rv)) + + from .debughelpers import explain_template_loading_attempts + explain_template_loading_attempts(self.app, template, attempts) + + if trv is not None: + return trv + raise TemplateNotFound(template) + + def _get_source_fast(self, environment, template): + for srcobj, loader in self._iter_loaders(template): + try: + return loader.get_source(environment, template) + except TemplateNotFound: + continue + raise TemplateNotFound(template) + + def _iter_loaders(self, template): + loader = self.app.jinja_loader + if loader is not None: + yield self.app, loader + + for blueprint in self.app.iter_blueprints(): + loader = blueprint.jinja_loader + if loader is not None: + yield blueprint, loader + + def list_templates(self): + result = set() + loader = self.app.jinja_loader + if loader is not None: + result.update(loader.list_templates()) + + for blueprint in self.app.iter_blueprints(): + loader = blueprint.jinja_loader + if loader is not None: + for template in loader.list_templates(): + result.add(template) + + return list(result) + + +def _render(template, context, app): + """Renders the template and fires the signal""" + + before_render_template.send(app, template=template, context=context) + rv = template.render(context) + template_rendered.send(app, template=template, context=context) + return rv + + +def render_template(template_name_or_list, **context): + """Renders a template from the template folder with the given + context. + + :param template_name_or_list: the name of the template to be + rendered, or an iterable with template names + the first one existing will be rendered + :param context: the variables that should be available in the + context of the template. + """ + ctx = _app_ctx_stack.top + ctx.app.update_template_context(context) + return _render(ctx.app.jinja_env.get_or_select_template(template_name_or_list), + context, ctx.app) + + +def render_template_string(source, **context): + """Renders a template from the given template source string + with the given context. Template variables will be autoescaped. + + :param source: the source code of the template to be + rendered + :param context: the variables that should be available in the + context of the template. + """ + ctx = _app_ctx_stack.top + ctx.app.update_template_context(context) + return _render(ctx.app.jinja_env.from_string(source), + context, ctx.app) diff --git a/venv/Lib/site-packages/flask/testing.py b/venv/Lib/site-packages/flask/testing.py new file mode 100644 index 0000000..4bf0ebc --- /dev/null +++ b/venv/Lib/site-packages/flask/testing.py @@ -0,0 +1,250 @@ +# -*- coding: utf-8 -*- +""" + flask.testing + ~~~~~~~~~~~~~ + + Implements test support helpers. This module is lazily imported + and usually not used in production environments. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +import werkzeug +from contextlib import contextmanager + +from click.testing import CliRunner +from flask.cli import ScriptInfo +from werkzeug.test import Client, EnvironBuilder +from flask import _request_ctx_stack +from flask.json import dumps as json_dumps +from werkzeug.urls import url_parse + + +def make_test_environ_builder( + app, path='/', base_url=None, subdomain=None, url_scheme=None, + *args, **kwargs +): + """Create a :class:`~werkzeug.test.EnvironBuilder`, taking some + defaults from the application. + + :param app: The Flask application to configure the environment from. + :param path: URL path being requested. + :param base_url: Base URL where the app is being served, which + ``path`` is relative to. If not given, built from + :data:`PREFERRED_URL_SCHEME`, ``subdomain``, + :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. + :param subdomain: Subdomain name to append to :data:`SERVER_NAME`. + :param url_scheme: Scheme to use instead of + :data:`PREFERRED_URL_SCHEME`. + :param json: If given, this is serialized as JSON and passed as + ``data``. Also defaults ``content_type`` to + ``application/json``. + :param args: other positional arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + :param kwargs: other keyword arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + """ + + assert ( + not (base_url or subdomain or url_scheme) + or (base_url is not None) != bool(subdomain or url_scheme) + ), 'Cannot pass "subdomain" or "url_scheme" with "base_url".' + + if base_url is None: + http_host = app.config.get('SERVER_NAME') or 'localhost' + app_root = app.config['APPLICATION_ROOT'] + + if subdomain: + http_host = '{0}.{1}'.format(subdomain, http_host) + + if url_scheme is None: + url_scheme = app.config['PREFERRED_URL_SCHEME'] + + url = url_parse(path) + base_url = '{scheme}://{netloc}/{path}'.format( + scheme=url.scheme or url_scheme, + netloc=url.netloc or http_host, + path=app_root.lstrip('/') + ) + path = url.path + + if url.query: + sep = b'?' if isinstance(url.query, bytes) else '?' + path += sep + url.query + + if 'json' in kwargs: + assert 'data' not in kwargs, ( + "Client cannot provide both 'json' and 'data'." + ) + + # push a context so flask.json can use app's json attributes + with app.app_context(): + kwargs['data'] = json_dumps(kwargs.pop('json')) + + if 'content_type' not in kwargs: + kwargs['content_type'] = 'application/json' + + return EnvironBuilder(path, base_url, *args, **kwargs) + + +class FlaskClient(Client): + """Works like a regular Werkzeug test client but has some knowledge about + how Flask works to defer the cleanup of the request context stack to the + end of a ``with`` body when used in a ``with`` statement. For general + information about how to use this class refer to + :class:`werkzeug.test.Client`. + + .. versionchanged:: 0.12 + `app.test_client()` includes preset default environment, which can be + set after instantiation of the `app.test_client()` object in + `client.environ_base`. + + Basic usage is outlined in the :ref:`testing` chapter. + """ + + preserve_context = False + + def __init__(self, *args, **kwargs): + super(FlaskClient, self).__init__(*args, **kwargs) + self.environ_base = { + "REMOTE_ADDR": "127.0.0.1", + "HTTP_USER_AGENT": "werkzeug/" + werkzeug.__version__ + } + + @contextmanager + def session_transaction(self, *args, **kwargs): + """When used in combination with a ``with`` statement this opens a + session transaction. This can be used to modify the session that + the test client uses. Once the ``with`` block is left the session is + stored back. + + :: + + with client.session_transaction() as session: + session['value'] = 42 + + Internally this is implemented by going through a temporary test + request context and since session handling could depend on + request variables this function accepts the same arguments as + :meth:`~flask.Flask.test_request_context` which are directly + passed through. + """ + if self.cookie_jar is None: + raise RuntimeError('Session transactions only make sense ' + 'with cookies enabled.') + app = self.application + environ_overrides = kwargs.setdefault('environ_overrides', {}) + self.cookie_jar.inject_wsgi(environ_overrides) + outer_reqctx = _request_ctx_stack.top + with app.test_request_context(*args, **kwargs) as c: + session_interface = app.session_interface + sess = session_interface.open_session(app, c.request) + if sess is None: + raise RuntimeError('Session backend did not open a session. ' + 'Check the configuration') + + # Since we have to open a new request context for the session + # handling we want to make sure that we hide out own context + # from the caller. By pushing the original request context + # (or None) on top of this and popping it we get exactly that + # behavior. It's important to not use the push and pop + # methods of the actual request context object since that would + # mean that cleanup handlers are called + _request_ctx_stack.push(outer_reqctx) + try: + yield sess + finally: + _request_ctx_stack.pop() + + resp = app.response_class() + if not session_interface.is_null_session(sess): + session_interface.save_session(app, sess, resp) + headers = resp.get_wsgi_headers(c.request.environ) + self.cookie_jar.extract_wsgi(c.request.environ, headers) + + def open(self, *args, **kwargs): + as_tuple = kwargs.pop('as_tuple', False) + buffered = kwargs.pop('buffered', False) + follow_redirects = kwargs.pop('follow_redirects', False) + + if ( + not kwargs and len(args) == 1 + and isinstance(args[0], (EnvironBuilder, dict)) + ): + environ = self.environ_base.copy() + + if isinstance(args[0], EnvironBuilder): + environ.update(args[0].get_environ()) + else: + environ.update(args[0]) + + environ['flask._preserve_context'] = self.preserve_context + else: + kwargs.setdefault('environ_overrides', {}) \ + ['flask._preserve_context'] = self.preserve_context + kwargs.setdefault('environ_base', self.environ_base) + builder = make_test_environ_builder( + self.application, *args, **kwargs + ) + + try: + environ = builder.get_environ() + finally: + builder.close() + + return Client.open( + self, environ, + as_tuple=as_tuple, + buffered=buffered, + follow_redirects=follow_redirects + ) + + def __enter__(self): + if self.preserve_context: + raise RuntimeError('Cannot nest client invocations') + self.preserve_context = True + return self + + def __exit__(self, exc_type, exc_value, tb): + self.preserve_context = False + + # on exit we want to clean up earlier. Normally the request context + # stays preserved until the next request in the same thread comes + # in. See RequestGlobals.push() for the general behavior. + top = _request_ctx_stack.top + if top is not None and top.preserved: + top.pop() + + +class FlaskCliRunner(CliRunner): + """A :class:`~click.testing.CliRunner` for testing a Flask app's + CLI commands. Typically created using + :meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`. + """ + def __init__(self, app, **kwargs): + self.app = app + super(FlaskCliRunner, self).__init__(**kwargs) + + def invoke(self, cli=None, args=None, **kwargs): + """Invokes a CLI command in an isolated environment. See + :meth:`CliRunner.invoke <click.testing.CliRunner.invoke>` for + full method documentation. See :ref:`testing-cli` for examples. + + If the ``obj`` argument is not given, passes an instance of + :class:`~flask.cli.ScriptInfo` that knows how to load the Flask + app being tested. + + :param cli: Command object to invoke. Default is the app's + :attr:`~flask.app.Flask.cli` group. + :param args: List of strings to invoke the command with. + + :return: a :class:`~click.testing.Result` object. + """ + if cli is None: + cli = self.app.cli + + if 'obj' not in kwargs: + kwargs['obj'] = ScriptInfo(create_app=lambda: self.app) + + return super(FlaskCliRunner, self).invoke(cli, args, **kwargs) diff --git a/venv/Lib/site-packages/flask/views.py b/venv/Lib/site-packages/flask/views.py new file mode 100644 index 0000000..1f2c997 --- /dev/null +++ b/venv/Lib/site-packages/flask/views.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- +""" + flask.views + ~~~~~~~~~~~ + + This module provides class-based views inspired by the ones in Django. + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +from .globals import request +from ._compat import with_metaclass + + +http_method_funcs = frozenset(['get', 'post', 'head', 'options', + 'delete', 'put', 'trace', 'patch']) + + +class View(object): + """Alternative way to use view functions. A subclass has to implement + :meth:`dispatch_request` which is called with the view arguments from + the URL routing system. If :attr:`methods` is provided the methods + do not have to be passed to the :meth:`~flask.Flask.add_url_rule` + method explicitly:: + + class MyView(View): + methods = ['GET'] + + def dispatch_request(self, name): + return 'Hello %s!' % name + + app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview')) + + When you want to decorate a pluggable view you will have to either do that + when the view function is created (by wrapping the return value of + :meth:`as_view`) or you can use the :attr:`decorators` attribute:: + + class SecretView(View): + methods = ['GET'] + decorators = [superuser_required] + + def dispatch_request(self): + ... + + The decorators stored in the decorators list are applied one after another + when the view function is created. Note that you can *not* use the class + based decorators since those would decorate the view class and not the + generated view function! + """ + + #: A list of methods this view can handle. + methods = None + + #: Setting this disables or force-enables the automatic options handling. + provide_automatic_options = None + + #: The canonical way to decorate class-based views is to decorate the + #: return value of as_view(). However since this moves parts of the + #: logic from the class declaration to the place where it's hooked + #: into the routing system. + #: + #: You can place one or more decorators in this list and whenever the + #: view function is created the result is automatically decorated. + #: + #: .. versionadded:: 0.8 + decorators = () + + def dispatch_request(self): + """Subclasses have to override this method to implement the + actual view function code. This method is called with all + the arguments from the URL rule. + """ + raise NotImplementedError() + + @classmethod + def as_view(cls, name, *class_args, **class_kwargs): + """Converts the class into an actual view function that can be used + with the routing system. Internally this generates a function on the + fly which will instantiate the :class:`View` on each request and call + the :meth:`dispatch_request` method on it. + + The arguments passed to :meth:`as_view` are forwarded to the + constructor of the class. + """ + def view(*args, **kwargs): + self = view.view_class(*class_args, **class_kwargs) + return self.dispatch_request(*args, **kwargs) + + if cls.decorators: + view.__name__ = name + view.__module__ = cls.__module__ + for decorator in cls.decorators: + view = decorator(view) + + # We attach the view class to the view function for two reasons: + # first of all it allows us to easily figure out what class-based + # view this thing came from, secondly it's also used for instantiating + # the view class so you can actually replace it with something else + # for testing purposes and debugging. + view.view_class = cls + view.__name__ = name + view.__doc__ = cls.__doc__ + view.__module__ = cls.__module__ + view.methods = cls.methods + view.provide_automatic_options = cls.provide_automatic_options + return view + + +class MethodViewType(type): + """Metaclass for :class:`MethodView` that determines what methods the view + defines. + """ + + def __init__(cls, name, bases, d): + super(MethodViewType, cls).__init__(name, bases, d) + + if 'methods' not in d: + methods = set() + + for key in http_method_funcs: + if hasattr(cls, key): + methods.add(key.upper()) + + # If we have no method at all in there we don't want to add a + # method list. This is for instance the case for the base class + # or another subclass of a base method view that does not introduce + # new methods. + if methods: + cls.methods = methods + + +class MethodView(with_metaclass(MethodViewType, View)): + """A class-based view that dispatches request methods to the corresponding + class methods. For example, if you implement a ``get`` method, it will be + used to handle ``GET`` requests. :: + + class CounterAPI(MethodView): + def get(self): + return session.get('counter', 0) + + def post(self): + session['counter'] = session.get('counter', 0) + 1 + return 'OK' + + app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter')) + """ + + def dispatch_request(self, *args, **kwargs): + meth = getattr(self, request.method.lower(), None) + + # If the request method is HEAD and we don't have a handler for it + # retry with GET. + if meth is None and request.method == 'HEAD': + meth = getattr(self, 'get', None) + + assert meth is not None, 'Unimplemented method %r' % request.method + return meth(*args, **kwargs) diff --git a/venv/Lib/site-packages/flask/wrappers.py b/venv/Lib/site-packages/flask/wrappers.py new file mode 100644 index 0000000..12eff2c --- /dev/null +++ b/venv/Lib/site-packages/flask/wrappers.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +""" + flask.wrappers + ~~~~~~~~~~~~~~ + + Implements the WSGI wrappers (request and response). + + :copyright: © 2010 by the Pallets team. + :license: BSD, see LICENSE for more details. +""" + +from werkzeug.exceptions import BadRequest +from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase + +from flask import json +from flask.globals import current_app + + +class JSONMixin(object): + """Common mixin for both request and response objects to provide JSON + parsing capabilities. + + .. versionadded:: 1.0 + """ + + _cached_json = (Ellipsis, Ellipsis) + + @property + def is_json(self): + """Check if the mimetype indicates JSON data, either + :mimetype:`application/json` or :mimetype:`application/*+json`. + + .. versionadded:: 0.11 + """ + mt = self.mimetype + return ( + mt == 'application/json' + or (mt.startswith('application/')) and mt.endswith('+json') + ) + + @property + def json(self): + """This will contain the parsed JSON data if the mimetype indicates + JSON (:mimetype:`application/json`, see :meth:`is_json`), otherwise it + will be ``None``. + """ + return self.get_json() + + def _get_data_for_json(self, cache): + return self.get_data(cache=cache) + + def get_json(self, force=False, silent=False, cache=True): + """Parse and return the data as JSON. If the mimetype does not + indicate JSON (:mimetype:`application/json`, see + :meth:`is_json`), this returns ``None`` unless ``force`` is + true. If parsing fails, :meth:`on_json_loading_failed` is called + and its return value is used as the return value. + + :param force: Ignore the mimetype and always try to parse JSON. + :param silent: Silence parsing errors and return ``None`` + instead. + :param cache: Store the parsed JSON to return for subsequent + calls. + """ + if cache and self._cached_json[silent] is not Ellipsis: + return self._cached_json[silent] + + if not (force or self.is_json): + return None + + data = self._get_data_for_json(cache=cache) + + try: + rv = json.loads(data) + except ValueError as e: + if silent: + rv = None + if cache: + normal_rv, _ = self._cached_json + self._cached_json = (normal_rv, rv) + else: + rv = self.on_json_loading_failed(e) + if cache: + _, silent_rv = self._cached_json + self._cached_json = (rv, silent_rv) + else: + if cache: + self._cached_json = (rv, rv) + + return rv + + def on_json_loading_failed(self, e): + """Called if :meth:`get_json` parsing fails and isn't silenced. If + this method returns a value, it is used as the return value for + :meth:`get_json`. The default implementation raises a + :class:`BadRequest` exception. + + .. versionchanged:: 0.10 + Raise a :exc:`BadRequest` error instead of returning an error + message as JSON. If you want that behavior you can add it by + subclassing. + + .. versionadded:: 0.8 + """ + if current_app is not None and current_app.debug: + raise BadRequest('Failed to decode JSON object: {0}'.format(e)) + + raise BadRequest() + + +class Request(RequestBase, JSONMixin): + """The request object used by default in Flask. Remembers the + matched endpoint and view arguments. + + It is what ends up as :class:`~flask.request`. If you want to replace + the request object used you can subclass this and set + :attr:`~flask.Flask.request_class` to your subclass. + + The request object is a :class:`~werkzeug.wrappers.Request` subclass and + provides all of the attributes Werkzeug defines plus a few Flask + specific ones. + """ + + #: The internal URL rule that matched the request. This can be + #: useful to inspect which methods are allowed for the URL from + #: a before/after handler (``request.url_rule.methods``) etc. + #: Though if the request's method was invalid for the URL rule, + #: the valid list is available in ``routing_exception.valid_methods`` + #: instead (an attribute of the Werkzeug exception :exc:`~werkzeug.exceptions.MethodNotAllowed`) + #: because the request was never internally bound. + #: + #: .. versionadded:: 0.6 + url_rule = None + + #: A dict of view arguments that matched the request. If an exception + #: happened when matching, this will be ``None``. + view_args = None + + #: If matching the URL failed, this is the exception that will be + #: raised / was raised as part of the request handling. This is + #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or + #: something similar. + routing_exception = None + + @property + def max_content_length(self): + """Read-only view of the ``MAX_CONTENT_LENGTH`` config key.""" + if current_app: + return current_app.config['MAX_CONTENT_LENGTH'] + + @property + def endpoint(self): + """The endpoint that matched the request. This in combination with + :attr:`view_args` can be used to reconstruct the same or a + modified URL. If an exception happened when matching, this will + be ``None``. + """ + if self.url_rule is not None: + return self.url_rule.endpoint + + @property + def blueprint(self): + """The name of the current blueprint""" + if self.url_rule and '.' in self.url_rule.endpoint: + return self.url_rule.endpoint.rsplit('.', 1)[0] + + def _load_form_data(self): + RequestBase._load_form_data(self) + + # In debug mode we're replacing the files multidict with an ad-hoc + # subclass that raises a different error for key errors. + if ( + current_app + and current_app.debug + and self.mimetype != 'multipart/form-data' + and not self.files + ): + from .debughelpers import attach_enctype_error_multidict + attach_enctype_error_multidict(self) + + +class Response(ResponseBase, JSONMixin): + """The response object that is used by default in Flask. Works like the + response object from Werkzeug but is set to have an HTML mimetype by + default. Quite often you don't have to create this object yourself because + :meth:`~flask.Flask.make_response` will take care of that for you. + + If you want to replace the response object used you can subclass this and + set :attr:`~flask.Flask.response_class` to your subclass. + + .. versionchanged:: 1.0 + JSON support is added to the response, like the request. This is useful + when testing to get the test client response data as JSON. + + .. versionchanged:: 1.0 + + Added :attr:`max_cookie_size`. + """ + + default_mimetype = 'text/html' + + def _get_data_for_json(self, cache): + return self.get_data() + + @property + def max_cookie_size(self): + """Read-only view of the :data:`MAX_COOKIE_SIZE` config key. + + See :attr:`~werkzeug.wrappers.BaseResponse.max_cookie_size` in + Werkzeug's docs. + """ + if current_app: + return current_app.config['MAX_COOKIE_SIZE'] + + # return Werkzeug's default when not in an app context + return super(Response, self).max_cookie_size diff --git a/venv/Lib/site-packages/flask_login/__about__.py b/venv/Lib/site-packages/flask_login/__about__.py new file mode 100644 index 0000000..ac63b66 --- /dev/null +++ b/venv/Lib/site-packages/flask_login/__about__.py @@ -0,0 +1,10 @@ +__title__ = 'Flask-Login' +__description__ = 'User session management for Flask' +__url__ = 'https://github.com/maxcountryman/flask-login' +__version_info__ = ('0', '4', '1') +__version__ = '.'.join(__version_info__) +__author__ = 'Matthew Frazier' +__author_email__ = 'leafstormrush@gmail.com' +__maintainer__ = 'Max Countryman' +__license__ = 'MIT' +__copyright__ = '(c) 2011 by Matthew Frazier' diff --git a/venv/Lib/site-packages/flask_login/__init__.py b/venv/Lib/site-packages/flask_login/__init__.py new file mode 100644 index 0000000..57d4114 --- /dev/null +++ b/venv/Lib/site-packages/flask_login/__init__.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +''' + flask_login + ----------- + This module provides user session management for Flask. It lets you log + your users in and out in a database-independent manner. + :copyright: (c) 2011 by Matthew Frazier. + :license: MIT/X11, see LICENSE for more details. +''' + +from .__about__ import __version__ +from .config import (COOKIE_NAME, COOKIE_DURATION, COOKIE_SECURE, + COOKIE_HTTPONLY, LOGIN_MESSAGE, LOGIN_MESSAGE_CATEGORY, + REFRESH_MESSAGE, REFRESH_MESSAGE_CATEGORY, ID_ATTRIBUTE, + AUTH_HEADER_NAME) +from .login_manager import LoginManager +from .mixins import UserMixin, AnonymousUserMixin +from .signals import (user_logged_in, user_logged_out, user_loaded_from_cookie, + user_loaded_from_header, user_loaded_from_request, + user_login_confirmed, user_unauthorized, + user_needs_refresh, user_accessed, session_protected) +from .utils import (current_user, login_url, login_fresh, login_user, + logout_user, confirm_login, login_required, + fresh_login_required, set_login_view, encode_cookie, + decode_cookie, make_next_param) + + +__all__ = [ + LoginManager.__name__, + UserMixin.__name__, + AnonymousUserMixin.__name__, + __version__, + 'COOKIE_NAME', + 'COOKIE_DURATION', + 'COOKIE_SECURE', + 'COOKIE_HTTPONLY', + 'LOGIN_MESSAGE', + 'LOGIN_MESSAGE_CATEGORY', + 'REFRESH_MESSAGE', + 'REFRESH_MESSAGE_CATEGORY', + 'ID_ATTRIBUTE', + 'AUTH_HEADER_NAME', + 'user_logged_in', + 'user_logged_out', + 'user_loaded_from_cookie', + 'user_loaded_from_header', + 'user_loaded_from_request', + 'user_login_confirmed', + 'user_unauthorized', + 'user_needs_refresh', + 'user_accessed', + 'session_protected', + 'current_user', + 'login_url', + 'login_fresh', + 'login_user', + 'logout_user', + 'confirm_login', + 'login_required', + 'fresh_login_required', + 'set_login_view', + 'encode_cookie', + 'decode_cookie', + 'make_next_param', +] diff --git a/venv/Lib/site-packages/flask_login/__pycache__/__about__.cpython-36.pyc b/venv/Lib/site-packages/flask_login/__pycache__/__about__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..339e32446c22e988e429cfc24fca8a090cb01267 GIT binary patch literal 607 zcmZ8e!H&}~5RIF%ZByDsTsUwEr==2Uw+p8gLMs-nv=ZgAaIqqd+<2OJo5YdrlqOfi z$M6CCOs@O|C+zNOLD=$}(P-w)%=7EXB-sDFhVR7letG{K-})E!=`#!5qaG)e@E+~) z3HABWZ6Ez_gg)W}dc=oxz(;h*13Kbk8t}(A-fY^AuX3ih(MOqzvVHMIqY5=@Eo2!M ztYj$`SekGnRd`5jUuDKr`h2lSg~{smJdwqsVEaVYrBRJ7S!@o3xg+ezX-D34WZ98< zd%R-CWcW3_QmhrI+GjbkjW$vhs@B;>S}>72zIM7|`|$FCk@~Ca&+YS<$t*lOT`t3Q z6aGi_Y33^{ji-7JoG7y~9RV0&as;5q033CqL}lz^HZTCFRc@Up06SD}T@d9)0(AXb zX$K$wjYAgzu-ar&-4)#X=;pPNDS-7#V;{N<TbhdmOYJ&O01{a>N~D<q_~EIco6x~; zet8}{y>;B2=#wV?fO>0W6~C8q8?7F+k8T=mu!ss~jf!_z?&4gmV=W9u6-&1Ek92$x k9S)+$cG|V9ZM%8Zbi=oW<aLhkoId)gg+95v!M*L1KYroBFaQ7m literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_login/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/flask_login/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1bb26690e023c950b48fbbf05e31502f7a4b2b5a GIT binary patch literal 1873 zcmbu9+fpMp5I}8XFun{1b2E2w$D1`-l82<IR4QH=z!eNB+f-#$u2KzdVNV#%IMSHK z`$zeJ{ES}nlwZhG)MGD@%gffz)ErB#R=c~^>aQ&;^Z0v}eKQ%0{SkZd?3nQn=2Oe@ zSPWt`260Guagm^jIF84>F_EN6F;2(D1f38mni7+AQlx2GWN1cYX;$QDPFU26^poC{ z$kTi@9`~lj44n}LT8QQo-mI9Tb7G#(iv_wM7U`lW(xO<ROJbQWixs*eR_Ur(qid0V z%3Bv3bVCqI#3tPoTXaio(`~Uscf>B;6?=3~?9+X5Ko3NTmc%RiDjwtcU%uco<|~*l z0h5r13}hh(7ED1NreOvOFbi`q4-2peMOcDmSb<eogLT*d0-LY}+pq(>um}5{9r_v$ z8nIHz$(jUZh<WZjN8K|o=(v6~FF!xgyxDaX5dri)PI@wUaDgk*S6mXsm2v~02*-Ci zTyQ@mcY!2lXz-9!L*nsJkwMTW#(HGSjAgV}5!WY<4<zV^(U5=>I=7DEW!DGZ;|9i@ zvFvks80|f72fcxGJKgY@9JEX1t2akS<aR)6P8fFidvYe75AKt)=eD`8_%W$fn_vEX zboAPI%gJT+#J+0Sk((lroB$6U*Hed?KTSGfrMS*A_P`~E#aJn$vnSWrKUQsaRjJuJ zKboB0)GN*E^_8|pf`)x^Q-5B+Xf}VkzPfy;t;_54>J_WmjYj3%)(iibvXe^FKEJNN z)A_o6R<|1$Pm0BV37%DR)l*h!HtW^5H%(hlS8kdY?82^`+V#jq6F}|~PqG#ri7X{_ z`o<hp&3$xzU8wkhKM+A*J;_XnQ+Sqm{v8g?Xysqd&GG2Pk-!0?a3=%7+Cgyd^8Z=r za-$@5@jp8x|FzFmsOLT!cYR~UzjLMFpl2d!-*@_9H<0cJBPv89pL0-5@;k{@S6h+H zX}3+?;jU5rF;kTxZ-*R8Ih||wr8K3(P*<m;vSxkh>FjVgw3HdiQ3n@Hn8OX$$f@Cd z&{IP_^xl=`FXy7|!$srPNIIo>IFdcM{Jplg-wuF(Op^ut<uosxd(M3R7_y!t9U;d| z<;f&s95I1NAxv|N$uuH^$RcuxX~YbofS5(hA?6Vah($yZv4mJgtRPkqYlwBk27(|q z5nG6D1a?xnYj9O6>l9-Uv>DTB#++Ns7Ly6HrYE9&bv``?Z4Fa9>>W~C#+vEJSf@%( z;eY4ReoDC=-|^I^hpfl@p}E<g6S<yb%y$Ggahn7<p2yg4F?op2A^vKg9Jg*v38~g# zU+oWCr(E5KL9g}JT#xeb24YhzHwG%?qAVQqBU=yLe`tB`ZA-Z!FZZ1Gy=k7R^`Z^8 qn7N0p$?;)tpeMf@_TKNY5LDA3naIW8By;g;!*5f`IKGL`eE$ZCc>^#2 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_login/__pycache__/_compat.cpython-36.pyc b/venv/Lib/site-packages/flask_login/__pycache__/_compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f2f6a121d8e1f86102fb4b795d7f58bde06f1ab3 GIT binary patch literal 994 zcmb7C&5qMB5Vm7CNwaNtZwO8iAeJ7|1Kbcoz;Z!?71D|WQKTr1y;ZhO9NB5LLEMUX z7ao9@@s-oQ0w=~vV2eNk9L@MApTC*;JP)VS;PT7MG$IInMSH`8^;0nY2^>Q)VwgDs z$C%>_hvA!uk+l;OPV{X~;}Iw76(Fc^OE-2)FZM8EBj$cYvCll_!#w7rIH>M+sDnM! zct8aMipBvuU=ucFGv+d%9j?51!kjZSKYA~N5XQO4^g0!CRaA>Kmt~zb-Ffu;=+2*o zrDPky!&=F0!HQ}XHc|>5o=X+xN@^W#xzdHK!k(>I7NTg{MQ{bhqthz~{M<4AMu|F8 znwtPto2pyq*kWMV+1d%1_Q8EX7ifndZ+tq(W?VE}ft4CIn0-ddO8jkk@+f($A$rmt z>*F?g!S%Y4b@E)wb@V!`vK23R)kJ5lhPzRj!K9KcueOOOmWc+&sLt{=;B>MlOfm?w zs9QCHKsRhwwhn~5{lBIi*tEBq*l@^Oxp2SByLYxqH3oMJ><7Eu|H5vgcA`vR(O6mV z4()I+jAN*cU`B&9e+*#@xC1tX_z!j!z%}FkwSarq?1sE~_rQ3Ys>mhdiohR5Ez7v! zmrdHVHJ?w7m!?eSX=;dWwV4e4lNJ^9)YPJhZj_qY%Rd=9;XA$5p@mGKi{KF#eNO>n tCPbR<>1#VgS&@zZxNqSn_678PaFqCDMkuC)I!BHVJD2t|r4GLSz5_D))dK(k literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_login/__pycache__/config.cpython-36.pyc b/venv/Lib/site-packages/flask_login/__pycache__/config.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4c659576fcda78a58c1aa5e1a4c28cb74902cbb GIT binary patch literal 867 zcmZuw&2HL25VpZ!LNFw06!p-9)k~C0aOth8syeU<C;=neNX^R9YQrwX!avBnpun}~ zK1d&+FSFO4`U*XDfTT%(X64zL@i(4tcE@imt9t*fM~+QR`>s8cGT0yC5x1b2hBc<M zLaZkRT+lUE#6@0U1}lBhvBAo?#7taf6>PFSTwztb2YZ$6<9$}cHEuDB)meibu)}HT zvyST!IhyKVH6ZVRm3{5(#d=b;K_)R3GZJUhC~b|ibP`Rs;pRWG4F`9Tu#=1}Vs6j# zY#A{wY{n;a5lefQzR0PJveaJEc)>-h`UCpW&UCdWql7aaON#3`Pk3_6b0V`DPoqTN zA`C`XVsNp3J&ZXO+y+v1l-e@0>3GbAu;r%uoKAU5nF$2ITEBS!=bSF&9ZzL6rjq|Z zsXXP9L`+!@K(jnrZ<%6qA%IH6aOIH~E^gd1ndDsDDI?|gQkm{B=(z*`>`<A6uxw0- zsxYXv=_@ZllWM>1x>r3!22LNT#)H!tdCn$LSq}jpwMX95`)&{n-NE&Zvaa3B-hlLx z?>m=Bo&4dFwiBRB*Sk>-4_$c3@BRk9{44MTt@S#@2?DQoJ_?XJa7ICwbdl3R-i~5X zaEWU^6a;?c3f=g)jy|FOFo2H-UAN=o<B^ZZBjUn^MD!{6s^urp?^gY!?RVh_4p4-v zQ*pWqJ6z0UHV@CUY}V}46fP-&Yiar`A$ihFC~SGS<mobuquWqKk~imcJOi8vpG{-9 k8>iNMr7G`tBlltRlZaQKjAG3&iq)d=xWI#D92wQpFM-4Z(EtDd literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_login/__pycache__/login_manager.cpython-36.pyc b/venv/Lib/site-packages/flask_login/__pycache__/login_manager.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..624a3974ae96436ed5561c3d1ab69c65704c09dc GIT binary patch literal 14051 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 VMK(jMbtzL(zmu(~mois#{|)Pl8k_(C literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_login/__pycache__/mixins.cpython-36.pyc b/venv/Lib/site-packages/flask_login/__pycache__/mixins.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..11a97255b7742190038bf8af83b092194e10f3c0 GIT binary patch literal 2545 zcmb_dUvC>l5Wn3!+vkg8ObdaQsy(TK>LeTlydbC`CD2lpT2)j2)Uvd>+-~BFeRuBe z+Qe3P!TB<L06qiX;a+*-EAYh3UVMqElOn+;J2$&K-r3*I%x^zlU-yoFd&qvTTGk)d zx#obr1zmjtf?HjS6HbRDp<SBTU7J`BEN*k>#Nv){=61IN;|j0BxN62#UfZz(clLwl zL)#yQYQTosL7Z+T@ll*AGu?ivndxW!SoulD$3x+dWHyPpfHD5e&-M;Qq}8VPCxLSX zG`F8T|0u6(aip1^jzk2rWwSxsg08*?A*?O|9F&u;EgZEWDu>l>Rn$1;_K7toT^DqR zSBzc<9S|7Z<1VkCSlu=5@iq81c!RIQcbzu@;cI#QJ0;{}Jl#y59lty`7yIFOsQow@ z4MieS9qKqsmA{`!U-yNd2;I-P0zK6JeY~gkw|GxxD{%ynwDQN`Vu9V)ncoj5V$)be zcn{d{i+vlq!su}V$D^HRzghvw9VLeQvI5ga{1tA0+QW0J-t>;TGwppP)IevW-j`W6 zXg?0q@W7n4y)#u>B<&=GkL*oEI_VALy`GA-Xph2Z0CuW(F1o!Ux|^eEUXK+EBOOn~ zTX-Z2!lJXQ<!x=2>#xRO4jm8)Sy})npwwWPw{V{^TxF``DAa<#1G5Wv*O%R$H+!Ym z`<Hobpw~1@r%5(ex6XOoSoXLuS@O09_9jm6gEIBBb!;7yImFAcG#z;IJp!R5mbN*} z{z9HxDg6<O(8P20ZYT41W4`N$TFZEEtcBn9vx$%r>crnY5Sqbi;7CmWoW?wF-dk9H zEoCP2tDQ_gT%@DmX7Yl|trpweMu08|i%{Z`CYiO)U=}8k2{2gT7hM~GH$ee-M3Xs{ z*8wJ-!wBVo?f^i5L}pJ+CL>R5_eC_oS`fZ?F%E~Zp8DCouYUr_rNiB_62>Y{4@$^I zLEtullY23&DIKPfkXQ|}2_kPUBM+#_sJxPaM_J^QsS%S{RmQ{%#sc`E!o;&2kF=JM z7X~GAM2+YT@*AK4UO+o1KhtBX$-tEx8iIUG8yHG5d%6mDoc<@|R9*qcvIQcri+Xt- z^t@trT>&u_jC}|eMR)<COPqgzM3YX?%w5LPFo8ktF;?(1=ncj&RHrL7#&{MnCh;Qj zYQfj|%YLZ(VC@1ij)c@xiRmQkC_EGxpW^IqpjJ{<p<zHw92=l0gy=dBb?ho_IQZvQ zUDvEfuv9h-$QE>U7sT=dP||)yI3?}F(^b-b?UeRi?m@8D@+<cirCcWB>>Cq6^IXA; zg;~2``vSKic_~IxC}bzd)i8zsQ`Ru7{x4blGA=Tg_yxkmI|q)v{qj)_+5T_rXJTiv z{M6%(3p^V7U!uO1Pn3!AY6ddN@!%Y|TNk)m-0CIXOx1NRbB0OysB}hV*H$<y=&lpg z<(ptBH&EaUC*MYak57@+Ccn>8dsRXsM|=idp_ywtq)29o^PQk(cFb!m%92s2O+^_} em)BsuU^TQ{RV%rBw<yrhk+}?gId;o#eegF?K093i literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_login/__pycache__/signals.cpython-36.pyc b/venv/Lib/site-packages/flask_login/__pycache__/signals.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..57978f963fe0fa2470fca26efb032a3c3c481eaa GIT binary patch literal 899 zcmZvaKabNe7>CpUX|8E?1&JH8ojRlo8$##=2nh*pIasPlmM?zOnkII!okBCP+$Z2; zFfs9EyfX0>n0VtRsOWH1=jqRJoaeP4hhgyf$7}N4_q?CpN$VnC;ZJ|Tj(IDObyzp= z6x~&~<3ab%TlJv#q0f4Eoz=imA8F`lfHZP6MDiVtkj9RDq`=V_>C91pG;wr>6grw9 zO<8#3EvD7iAi|dARBuTx*IX<$UkjRRm&f;;%inFdjta@j9HO0)d(J>d&BVyaXbmP3 z((sG}i#}~YM6c=<;y3jQ(GFB5RdHM*1!X9PG&Niby$pU~S-eHZj<0C}dPh^J#`tAx zU@;d}(4Z_$HP0nw7-vcraVq7O!@t=Ea9mXnPM=fou>@_ZnX|=dBIBw6W+$bfrP)Zu zD_~V90GN&yWD4}g`ZP^JYaBS&c*ie|(c3_3fL$+Uc0>r1DIu%Sm9j0OsyEyEq$!OZ z9>Gpa4XUm;S`jYn?DRjX*+)%K`;pp@9R2<?aFnzc_hvMv_F?;SF7Pd_?zu}(o$cI3 zPMoyjq03r(+uD=A_TM};M@iTHzJ2i|d5>y3Ib7<?L-G>z*2rD*95*L^O9fp+0m8&L z2W_B;3yL?D>_P04oZlvz8;EhoxA>e+P7WoGxI?+z9jyQKc<`Q$a9MqUUB5T!4F^HL Ixmp_h0jc>W!vFvP literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_login/__pycache__/utils.cpython-36.pyc b/venv/Lib/site-packages/flask_login/__pycache__/utils.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0cb4e345bb579b2d70f1338d8140b069491c13df GIT binary patch literal 11963 zcmeHNTW=gkcJA)Ea(ER*+mdX{?aPW9EjjXKz1DIS$GVVf#S$fu^xB>fm}yQmIZgIu zwz@{ta7Re6G&Tza&Tk0zAwYgWfV?J1koP<`@|Gt9dCQxD0Qt_T?w%&CY=TW5Gh}yH zSJ&m#xt~+@!R&0~+21_q{MGx0@vlbVS4R00T=Ax97{X{9!W5QowoN=sz7>?(C6nsP zemSVLD_pMl)u7g{ak=W(gQ@lumur3_m~Kx8Gwm6EuluvXTzf8<Z_ft{?S<e%`$DkT zUJNd_FXH=@X!uLPOYN6}m)kD~ue4v`x@rGXaJhXsxYEAD?=$|Z!E5c;g4f%x2Upuy zO~b8;SuyvuacZ{T5c6W;Yol$83t|z^H^oJ<gy&o0CGj$zZ;Ky`OXBj^X8Rp+MZAid zABc~{Rq+ODt_fSbiJE2cmUtV_>*5{p13a7Jnpnp3hPWlJizZtBP}~qdM9qr$iC7V< zs96;)aT7Hy@ve9e&zs_X@d2LiiZ$^g@gZ8gC#-Fw`O}m84I96G--(Ypesti4t)cS# zm}^(h|G4&NZs<zKx2c_{JU4DNz9n*PHqE3O_ni;ldmp9Bv2+G83XO+R&+(tg=-CKw zGqJPpc48$vy<pI^lEzT_9pUyO;p(y*Qn^`87CX}Y#n6par>CBwhtoSsrh7vv-B5L$ z!62!n-;-MG#<3TLNsR{Ek7RPO@5GbtQK;N!>RWJ+(JUoX^wLqIft%FPe&EPhPdyB| z44|g(J%0SzgZ0klok#1*-1?X6kDlyw9<A?u`uP4fMwlDMt|Vj~xT52Q$$arW8meR| ztMI~3FADcP8MvZHILdwm%RO9i6*py^8lZ(q2%PF1xT-__|Jsn3@R4udb?>e1evZm` zceETYk9P07@sWxKyLY4LX!Vg3ItOk5ysU1IV&w*_frE$Ked>l!cYSYfH};gfIsgXI zF5WE=dzXpb8jO<qZQlv@g!Azg0t=TRsnjD#=|@kJM230+1z{c<V?&u^@cAk6l6CSA z%pW^_FSd_8-?ud_ZO86*hoMJw?RM>_jz4s5FH{je^Z>u-uGmf}?ASX9ZPj;FTFZkS zd#aC0*9OIxV>@<x(pC16J8Cuf`x<EG1oprg`H>^m>>V`KGhlo@KSq!39DtVYM)aTb zN}=OVC$(R``+WS%_nw0hs$izTQOO+0NPu>P2Lvk6sg}+-p^>}{q$IO?&b0mOsERGz zjJmvvdydT#UZ!y|Hg`}MT^}2Vtl3VjUF*5|+yZ7TQ98BcwXt<*iSjAxzA(b2vY|>x z4SCxHNuYl!+s2oMDyz!aJVg(*e=;_=xxQIF`A1$7Q$!0{oFm;n@W3DT-pKBrfw|kV zAMD#tUFq$6Jx4*V>}a1zz(Z*~*!vE2#0u!T8HGetCifo58Vr1|=P7?=gHA&ky5cOM zbpJK`PMA_hq|`3bC2IsV!IOSxj^!MXlvGnqQE}p)=WRkuRAr1ARZ@e*J4#7uQ@h5` ziOJTbjHIlfLcWF@B)6(ZmP8i}#>D&{1@#;$1+SxJOzdvdEpx$am~;56T63mcMqQ6- zivmeT4Hwh%=O`%122l)8(B*jvG&E<7_#JwVONZu>CAWyKsJlf^)LB%gO0Z<qqKvD8 zt18U>>M1BnRBc+$A_S3<vgL|l&y86);$9!}&r;NnVrB2kC;*`~g+JeVXvdN5L9QIo zo7g^%U16(;$06a;4ZE!WyEb(QBI(-hK3KqmUh&U~8#nAgduoB>SMIjwdf@>`FFfh? z6i-qlY@uI9+f;xv#h#BzKtO~s6joUr<4ntJl*DM(B#{Xgh;flrLs$7xPhOzP^1xC3 zq{_M?PAV`fa?~s*Q#m{n%K`ur6aT&Qd@&z5M{Xx1<H6(n6a&XIxEYPQS+|zVCG(0Y z;k-<w{EW^dZOSrUIW^_n7#d&ePRME*l&zlpQ%Y768%<WgVnYKli8YR&dhYRx?LHeo zVG^c^cvNSH4(QJ+l{$I*JXYws;=BOrB~u?wJMO3j+@8!~V^)VL`u+Kf#3+uI(c-&$ zU#UU7cJt<UH7ZfTyt&yd$q#@6`6If0h#Ogdn02XQQu2~eN$$dA$nzAqpx3S4lnyOn z6Mfi(8uf-*w&X3mCuO2huY@A~NGi$5vSbPrQpqfhA!kQ*xil^@B}$Oeva-hILn>7i z<PVZ-jVoj8wxKGA)x(;wPR+r6w5UI?;yyL5s>Zk?N}~LYB`V)o=s!J%o+fHKe+K}V z2=}DxD9Gsqotbzr0m%w#yboZUS@%XE)Tb6kUS-$zv_CeG(Nhl;T>;%Q8+$l}ylY*{ z+(2?eH1iaX)n$eSPtYKCJ^(}3kB(jF`Qm7>vMx+9S#0X~2N(_7o%!8`M?oA$EXM+5 zgWy;K4+Z_gmYY5nxQG-^wpx1+{)Zj_CLRN~><#idS09qs3EM8*eP`$^9&Z>rL)DL@ zcLE*K2ebQ_99|L01=g`Y40~*uj{7iS*$gQ@N6}EGKw#aPTX!NtN)I9e09i#c-(y4| zGloae-FUopZ@u&B<L#Xeik+?X`wzC(@9k`NwNz1`7QBm`62M%j010W@a{vHwt_<NJ zliL+T7}0<|S-bB#0#2O$DJkhl8@Df4HzIlLNYIr^wgbgGkZz3O&zjDOVEiu0ZQ0lF z_&mRI{Sna?cA=++j?aiCXaC$U)wE|x*J^U}HTIK=GiOqs>g|lZG)U=CWKMYE0xJ+t z=6H(2q7s>m@uI=^J=ce0_XRnITqc6VitH1COr2R#A#$;=KTsJPfm5`7JGWYT)y&=P zo>?SAkxrJuvDv=#U-88bbX7Tyq-t972GojN#jQERDu^wSB(1}>Y!u`aUdaJLIB<}Z z;N^3-q>eac;5$86lBQyF1ylABHLhofX*VXoCFMN_0n03863mOvF=VD~1wuoShYakF z$`;1i#TCDdo3T{4>QIw)c4KDDD*hHsZquXB*{_7cJzQ+vlyOLbHXKW+wbIGQ%oglW zYNZ3$y1OHX<OG7zi6W_iPDPNU6+rei!INr--!_0X@_*%Rx=n!Q5<Ak<n(4UppAazY zYL(?(ls9&I<kiuSkW7|?;r)9Q#-$lUE|Q_8r+_jhGpnpaxKyXssqwrruBeKDQXj)< z79}Wm`c^%Kk{4yvn)0`TT2xWCMD1uw{&sW&E?Ny&9oLkoFCjIsWV~XC3w&REmHN)! zHsGGWRW?)u?)hJ8_gqbjxx*Q_<!}OjWqxIZ)|YSs)a>D0XI?EFUclUonEB$VA?Cla z;La}q%NLz8{7FXi!{~r42gQR3nOgQ&(a?_j(a;y{#TBGmw~6)+;df~*>&0vVKoTB` z#(<XXdT|H(01^tlE~9}f)rd?(x4Yr^G04a&W28O$FjC|&f)ogK0e~JT^bv2{T~?@U zRx%Dq^%ZjmM+B)|%D!hDg7Gkz4S(GY2Fi|yy&gOj#1j+-=73~g3>;SuH|*<P3kGt} z?KxNrFJ0pWDmK(hYM#}Y^jRw=Za6`X2B4^ebW`1eSDETJ3BNknqhY@2yFewIz^rlt zgR{@pvF~~k=7h5m<fXw2Ms_6AK=-wQBh55fWf6#M+%<bIinP8?!4N~qp_pEc6GX!h z1_biv!R^~~K?;qKY=ktZ%3(lAmxGZ9Z6vHZRwfGwf=~sD3WgLojftg1%RURX4)}^t zPheZ?!Jx-$T>*!OAZyW0xW00_MVvC@tWlIn6EbNqSw=&)#{>etqMC7)Dj4P|&@NvJ z`UeEKbPr%ai7n5An=*9~-gNr&C}vHwmaV@qc{+rah?I46z&StRWHiqK3mka_i9zCl zMCdIZ`>pe^=~bNpjWlI}`Wr}TfrmSukawWlk`hWu2_#DD8KpPBC7tOlAU^)Kfzr36 z5f5<T_s%EDLe{Q>)JYVI_~fIEUUopPBgEUWzfZ6gpcOeJN7x6ETC<T7A$v!_?0BEt zo!2w`>DR40&81{2S1Cyic8$Yuwsy0?7E)jV=ci;c<B1M^^!A337E=Go>i~<T1q*}2 zgOmeSd%y`sSomZn@Om;zA*VbaFCi%lXA~!Mce1fK&`+k;pY_}UxrOpoj4kUlK?zwh z`5C<~(Q6rQl}Ijfk}V4)wb08~@V!~pZ8xZGJwsGdVU}rEn2eH4O~GWbwMwHF!q2bq zIi|FMacK_5cELn+XwF$>ep+~zkwI9ZECOolD9JxY+a7hGADOv4&o;&jWe1-lGo;L; zs=TC3<bq&KU@DLyLd^}Xfq^lR7dk8-!lbF{DKbc7Q`HXZXt68G$`qB`hNyCnUG9OL z0(wjxHqdMO6h=wZ$N=GO=5Y3uGD~%sqN$T#GfzJ(*db&%;c1a25)rdEg_$I$lviMp z*8V0-cB{!wGYb!o1S1pKJ>NxSFdT5}j6-dxvNXK}PXb!sedfg~)t?&cECfqi>yOqS z-Cf`66w|a78dFN5octr)nkJKSr#Z)z)$CRpfE$Q4B}MPr`B<b9NlsGJv&Zr!s``R% zU($`D%w$@+lwsu5ESwi&DI^VaR*+(e^Bh6`3oym){}zp7ivNvjmAo&$=HP`9Yc#Ay zt6|DLe9f7FnwD@8t1&Nu{2(}d6_6hRDwu*j7myn@@LiZ8${<+f<QI%)w!u&onjo1= zmYF8#CFbRpy{*MBCbJrW00_u^*XFPaI&P#%2IHbv4?3r*CB1U3yLMPP$@>6y6TU0^ zb&O;}oP0tzaz6_gWjAkf)0xvWQ?A4Ne?|u#*;gPvrX<wl+0#r$2t_dsqm17G1%GDj zv#a`b9T7bEpBz@l&s-^(8$^Z(4|)-7DQP`UoTLe658ek@E=^m<L$;@gI7rcF*dytN zS!A4my4odTpM`=|QeViI0!67k#;%92%@`)*2#jZ>D1|NGh(bLc3^syiryiPMYosUF zx)3`r)aiZMD#pZJ<nnx<B5DZOMjA=F&m5#*-L*BH8Yj2o69n`v2|jeMFW(#>>Ri@w z9e=|bpq(i;g`XlA5~@cWZd23i&9%Ia>Pfuy$Qpj2h+<XFRv>&)4tWqT_%Y)-Qwr(u zKqRoqq4^8ZxWb(%UjU^74MAinE(98ZENP~*X^a+`og(7Tf}8rDSr0;lUT|uT&fxQU zQ?IPmY905zUZ1=lNCwdqcphms!2X3^20JEs4#E+F-4tFN@f1`RBM!l6nsEhdBV9n5 zRML|Nezd1YMWzp4+`e5{2IbpX{IDm1*4S+T52bQOI~vX&<dXmZ$7lx%D?`p*XX;so z4pL_k&YkqQefQzxpFP;@+<&lr=kCMx``R1B63Oo)2a7$CG!K*t2*UL|secf{zswT( zt(LtRL4JzVc>=-ce(`~QFoYQg(}M?KGVObhx3(#lvG02aeacdC7DNjWan$WJJ$QV4 z+&ccC70H8}J6ks)pYhGN$?nIi$N+&ySJR%W#1*UgfUEC*-0iI7Jo@R*&J+06kDu&3 zc)Yodcr=@j+0=c;u?DLz<^vmf;@(k4tJKdXqG}(Tl|xQrASqKEXZ$s8p0#2~NKx^R zloTnbH*m$D;6{<OvS5&xegktmHvfWRQ#RT1TA>Ds#`wovU#D%s$rsunoYI!68Xy*l zguIAhwLN1SHJ_$Ng%=aqMKY6dKgmn7a9&`NnW9cXKDO-0gE*-kAsux<d$(m;nzn4L zqvtXt2u@7?2Q3YbC%l#hTeAk7nud9D?W|^L-9{oFcK_qn+3qmWgdkYcBRr%}H`3;) z=L$XL*|Giw-(&KRj5n92WPs=IWflGn6Kfg$Kd}nSxlw=)gj}(4wO4HpSYa9pQQ>hK z6Ts7_99C{nmkVytvgRaqF33cONF-89jiPmya|plh9l<goH46DSupQRU$B`72xp%Pv z?D+_!I8(rb(YB9-*z%@K!RCRnv1f-Mja3{re2~$KDY8sGF=!fOIx{KJia!t8NFx_i z7v)mdh%<ku;YtlC*)Ob~v4K5SKg(6HRXjf`0tSMwfJPu0v-8pz*%Rwc1HTXqvPg6) zfZHQ)q2n7e$9mgYBOS>#ZHrj@WmYUroCD7<vdrn2I$4jTB9C;}?K{Antpgi^3T8y7 z&(~PZbkA~jH`6ezd9mva4jF)6JQzBrsvY4KnTSlaXO#JWtxN_*r>hye`5u8LD`^b+ zozcqVn*M%TSxuc9@dj(@#`m@KG;3)oF#I_cEnTYHryIrSvQM|WxFxe^*b_R@qfdvp zW$KXqWzvbPDc{8AVob5iS>4O}cZFFzH3q2zCjM7ic<DtW;z(7hi!Uur%Lt(4`{AV5 z>AHt2rbVhObitlD@4QXLuOYo^9hKxR5~3W|jLkzNV7Xku-rSVjqNtYwo}(Ie#;HW% z4&H@{XqLZqDq&ss@1PHR{1L!nqHky6kGA*PqC>jKr-#!-nsN`v1P0i}(wk+@<ejv* z-Y3@%2K6Znllo5N<ghcCj{|oYK1lvr*sF}O-=H-^>T786C#e31Im%*(Y=<HpE1ysg zmA}H3ByW~sEP$V;kYN<a1I%_~NP_;mWM4k=nq5eiZc5-B*^{K?yJ5j0c`0vRL<Dwd zDPX2O4r~hZR>xG{ust6RgJSFRf%P0fE_}a9Ovnc`W@gNV(wsG8&Xg8P4NEGF-@}#v z$ZcSTTtR`-D)Op=M@H;)ih~$<Lp)8<G}0h7T`!DNY!5fBO%C{*bvcW!@@=|NdP&kz zkHUHz04&WB$Vc>P65~#!ml92n2<j>d1`<GkmFjZg$--NP)y9|f5D7E<7`qt^v8l?Z zI3hyY2k@O#hH8KH7DzBnnLY$R9UPKM%KgCUC8f|kPOAEpkEGF)slNLxJ<DY2(=a78 zFM!K><~20kAxPQb!XXDfN0Z}}XkNm_aMHFu=~7Bh;faGw&nc;7K#bo7#AVt%r{vTr z4r*RD2+Q;xuva!u=Fjbcri=M^nqWn<RxM4@q&ujbx}ls*0H3CV2u#Wm8YdNf8mI#C z@CI;TYxD_$+_{w{3!DKgbZ^r#iKGU$#OkF>ru-Gk`BG__wB^K0?V^w^6`6)?nOB)G zSSGS{aO)LFgEK(L#Hp&P;iSP`Rfp!oNiLifD4`Z61iH{VY(Vdo1oCl|#KAkVaX?FL z^Q7@Py!O>QNQbHvBl+1hI{e7HhFD|OrE^X(va@Rc7Iq*hmnEOmjW^c_FFGMgD?;{I z$)vJ3LS!lD=@auqbvU3LuslHx&Pg>WUg+RFLa5{)QZKWA9)pV=Fd4cO61U`uYm;Qo zP+|*t8_M1ACgY!DlpLEVPZ)=G@5nk}3(Wk6G`NLxkZd9;AQ4_q=>*;jL@9ZN(`$6b zk`HylOT&pMY|4@e&>M!P{Dx*GPNE+@iIz;q8z?Y~*5xA6Q<#^*lsE%|u#C^$rEHrr zYH2fRY_H$jTHon>w*FO8_2G$y?=>rvgksY0Vsa~;5cv>BE+HSQ`6V9b5GbFbE+#mM zUIr%u5ky&BPYUNJsBM-vHaD=-42<yq`$?_S5mB$xk;nMVr{4NF;F$_F2cp<HiaD-I z=8j!?bm9&VkobWADM^Zx3wb5f#87%_#3$sE*}NWyCStvLO*_&^e$y!pt?oFh$Na@f zJ|Z1!M1Y0tAr4vTRg*gAoot<E<qRu@s>tBuj3**NPR8prD`y7TA)*74e5y|Gm*1k- z7TxaBjgsV?B;`~fM}F*bu$tE#Lh6deRuZ={;Tf9+%;#Dd=UszH41M=w(!%i?Zsk%P we5?N!EW5M>O|Vp%Db=Uz7x6dOc)MO}ykEakZ`4=o*BV#xR;^FfXX@qu1Ut?6)&Kwi literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_login/_compat.py b/venv/Lib/site-packages/flask_login/_compat.py new file mode 100644 index 0000000..2f3293a --- /dev/null +++ b/venv/Lib/site-packages/flask_login/_compat.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +''' + flask_login._compat + ------------------- + A module providing tools for cross-version compatibility. +''' + + +import sys + + +PY2 = sys.version_info[0] == 2 + + +if not PY2: # pragma: no cover + unicode = str # needed for pyflakes in py3 + + +if PY2: # pragma: nocover + + from urlparse import urlparse, urlunparse + + def iteritems(d): + return d.iteritems() + + def itervalues(d): + return d.itervalues() + + text_type = unicode + +else: # pragma: nocover + + from urllib.parse import urlparse, urlunparse + + def iteritems(d): + return iter(d.items()) + + def itervalues(d): + return iter(d.values()) + + text_type = str + + +__all__ = [ + 'PY2', + 'unicode', + 'urlparse', + 'urlunparse', + 'iteritems', + 'itervalues', + 'text_type', +] diff --git a/venv/Lib/site-packages/flask_login/config.py b/venv/Lib/site-packages/flask_login/config.py new file mode 100644 index 0000000..724198b --- /dev/null +++ b/venv/Lib/site-packages/flask_login/config.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +''' + flask_login.config + ------------------ + This module provides default configuration values. +''' + + +from datetime import timedelta + + +#: The default name of the "remember me" cookie (``remember_token``) +COOKIE_NAME = 'remember_token' + +#: The default time before the "remember me" cookie expires (365 days). +COOKIE_DURATION = timedelta(days=365) + +#: Whether the "remember me" cookie requires Secure; defaults to ``None`` +COOKIE_SECURE = None + +#: Whether the "remember me" cookie uses HttpOnly or not; defaults to ``False`` +COOKIE_HTTPONLY = False + +#: The default flash message to display when users need to log in. +LOGIN_MESSAGE = u'Please log in to access this page.' + +#: The default flash message category to display when users need to log in. +LOGIN_MESSAGE_CATEGORY = 'message' + +#: The default flash message to display when users need to reauthenticate. +REFRESH_MESSAGE = u'Please reauthenticate to access this page.' + +#: The default flash message category to display when users need to +#: reauthenticate. +REFRESH_MESSAGE_CATEGORY = 'message' + +#: The default attribute to retreive the unicode id of the user +ID_ATTRIBUTE = 'get_id' + +#: Default name of the auth header (``Authorization``) +AUTH_HEADER_NAME = 'Authorization' + +#: A set of session keys that are populated by Flask-Login. Use this set to +#: purge keys safely and accurately. +SESSION_KEYS = set(['user_id', 'remember', '_id', '_fresh', 'next']) + +#: A set of HTTP methods which are exempt from `login_required` and +#: `fresh_login_required`. By default, this is just ``OPTIONS``. +EXEMPT_METHODS = set(['OPTIONS']) + +#: If true, the page the user is attempting to access is stored in the session +#: rather than a url parameter when redirecting to the login view; defaults to +#: ``False``. +USE_SESSION_FOR_NEXT = False diff --git a/venv/Lib/site-packages/flask_login/login_manager.py b/venv/Lib/site-packages/flask_login/login_manager.py new file mode 100644 index 0000000..406d143 --- /dev/null +++ b/venv/Lib/site-packages/flask_login/login_manager.py @@ -0,0 +1,478 @@ +# -*- coding: utf-8 -*- +''' + flask_login.login_manager + ------------------------- + The LoginManager class. +''' + + +import warnings +from datetime import datetime, timedelta + +from flask import (_request_ctx_stack, abort, current_app, flash, redirect, + request, session) + +from ._compat import text_type +from .config import (COOKIE_NAME, COOKIE_DURATION, COOKIE_SECURE, + COOKIE_HTTPONLY, LOGIN_MESSAGE, LOGIN_MESSAGE_CATEGORY, + REFRESH_MESSAGE, REFRESH_MESSAGE_CATEGORY, ID_ATTRIBUTE, + AUTH_HEADER_NAME, SESSION_KEYS, USE_SESSION_FOR_NEXT) +from .mixins import AnonymousUserMixin +from .signals import (user_loaded_from_cookie, user_loaded_from_header, + user_loaded_from_request, user_unauthorized, + user_needs_refresh, user_accessed, session_protected) +from .utils import (_get_user, login_url as make_login_url, _create_identifier, + _user_context_processor, encode_cookie, decode_cookie, + make_next_param, expand_login_view) + + +class LoginManager(object): + '''This object is used to hold the settings used for logging in. Instances + of :class:`LoginManager` are *not* bound to specific apps, so you can + create one in the main body of your code and then bind it to your + app in a factory function. + ''' + def __init__(self, app=None, add_context_processor=True): + #: A class or factory function that produces an anonymous user, which + #: is used when no one is logged in. + self.anonymous_user = AnonymousUserMixin + + #: The name of the view to redirect to when the user needs to log in. + #: (This can be an absolute URL as well, if your authentication + #: machinery is external to your application.) + self.login_view = None + + #: Names of views to redirect to when the user needs to log in, + #: per blueprint. If the key value is set to None the value of + #: :attr:`login_view` will be used instead. + self.blueprint_login_views = {} + + #: The message to flash when a user is redirected to the login page. + self.login_message = LOGIN_MESSAGE + + #: The message category to flash when a user is redirected to the login + #: page. + self.login_message_category = LOGIN_MESSAGE_CATEGORY + + #: The name of the view to redirect to when the user needs to + #: reauthenticate. + self.refresh_view = None + + #: The message to flash when a user is redirected to the 'needs + #: refresh' page. + self.needs_refresh_message = REFRESH_MESSAGE + + #: The message category to flash when a user is redirected to the + #: 'needs refresh' page. + self.needs_refresh_message_category = REFRESH_MESSAGE_CATEGORY + + #: The mode to use session protection in. This can be either + #: ``'basic'`` (the default) or ``'strong'``, or ``None`` to disable + #: it. + self.session_protection = 'basic' + + #: If present, used to translate flash messages ``self.login_message`` + #: and ``self.needs_refresh_message`` + self.localize_callback = None + + self.user_callback = None + + self.unauthorized_callback = None + + self.needs_refresh_callback = None + + self.id_attribute = ID_ATTRIBUTE + + self.header_callback = None + + self.request_callback = None + + self._session_identifier_generator = _create_identifier + + if app is not None: + self.init_app(app, add_context_processor) + + def setup_app(self, app, add_context_processor=True): # pragma: no cover + ''' + This method has been deprecated. Please use + :meth:`LoginManager.init_app` instead. + ''' + warnings.warn('Warning setup_app is deprecated. Please use init_app.', + DeprecationWarning) + self.init_app(app, add_context_processor) + + def init_app(self, app, add_context_processor=True): + ''' + Configures an application. This registers an `after_request` call, and + attaches this `LoginManager` to it as `app.login_manager`. + + :param app: The :class:`flask.Flask` object to configure. + :type app: :class:`flask.Flask` + :param add_context_processor: Whether to add a context processor to + the app that adds a `current_user` variable to the template. + Defaults to ``True``. + :type add_context_processor: bool + ''' + app.login_manager = self + app.after_request(self._update_remember_cookie) + + self._login_disabled = app.config.get('LOGIN_DISABLED', False) + + if add_context_processor: + app.context_processor(_user_context_processor) + + def unauthorized(self): + ''' + This is called when the user is required to log in. If you register a + callback with :meth:`LoginManager.unauthorized_handler`, then it will + be called. Otherwise, it will take the following actions: + + - Flash :attr:`LoginManager.login_message` to the user. + + - If the app is using blueprints find the login view for + the current blueprint using `blueprint_login_views`. If the app + is not using blueprints or the login view for the current + blueprint is not specified use the value of `login_view`. + + - Redirect the user to the login view. (The page they were + attempting to access will be passed in the ``next`` query + string variable, so you can redirect there if present instead + of the homepage. Alternatively, it will be added to the session + as ``next`` if USE_SESSION_FOR_NEXT is set.) + + If :attr:`LoginManager.login_view` is not defined, then it will simply + raise a HTTP 401 (Unauthorized) error instead. + + This should be returned from a view or before/after_request function, + otherwise the redirect will have no effect. + ''' + user_unauthorized.send(current_app._get_current_object()) + + if self.unauthorized_callback: + return self.unauthorized_callback() + + if request.blueprint in self.blueprint_login_views: + login_view = self.blueprint_login_views[request.blueprint] + else: + login_view = self.login_view + + if not login_view: + abort(401) + + if self.login_message: + if self.localize_callback is not None: + flash(self.localize_callback(self.login_message), + category=self.login_message_category) + else: + flash(self.login_message, category=self.login_message_category) + + config = current_app.config + if config.get('USE_SESSION_FOR_NEXT', USE_SESSION_FOR_NEXT): + login_url = expand_login_view(login_view) + session['next'] = make_next_param(login_url, request.url) + redirect_url = make_login_url(login_view) + else: + redirect_url = make_login_url(login_view, next_url=request.url) + + return redirect(redirect_url) + + def user_loader(self, callback): + ''' + This sets the callback for reloading a user from the session. The + function you set should take a user ID (a ``unicode``) and return a + user object, or ``None`` if the user does not exist. + + :param callback: The callback for retrieving a user object. + :type callback: callable + ''' + self.user_callback = callback + return callback + + def header_loader(self, callback): + ''' + This function has been deprecated. Please use + :meth:`LoginManager.request_loader` instead. + + This sets the callback for loading a user from a header value. + The function you set should take an authentication token and + return a user object, or `None` if the user does not exist. + + :param callback: The callback for retrieving a user object. + :type callback: callable + ''' + self.header_callback = callback + return callback + + def request_loader(self, callback): + ''' + This sets the callback for loading a user from a Flask request. + The function you set should take Flask request object and + return a user object, or `None` if the user does not exist. + + :param callback: The callback for retrieving a user object. + :type callback: callable + ''' + self.request_callback = callback + return callback + + def unauthorized_handler(self, callback): + ''' + This will set the callback for the `unauthorized` method, which among + other things is used by `login_required`. It takes no arguments, and + should return a response to be sent to the user instead of their + normal view. + + :param callback: The callback for unauthorized users. + :type callback: callable + ''' + self.unauthorized_callback = callback + return callback + + def needs_refresh_handler(self, callback): + ''' + This will set the callback for the `needs_refresh` method, which among + other things is used by `fresh_login_required`. It takes no arguments, + and should return a response to be sent to the user instead of their + normal view. + + :param callback: The callback for unauthorized users. + :type callback: callable + ''' + self.needs_refresh_callback = callback + return callback + + def needs_refresh(self): + ''' + This is called when the user is logged in, but they need to be + reauthenticated because their session is stale. If you register a + callback with `needs_refresh_handler`, then it will be called. + Otherwise, it will take the following actions: + + - Flash :attr:`LoginManager.needs_refresh_message` to the user. + + - Redirect the user to :attr:`LoginManager.refresh_view`. (The page + they were attempting to access will be passed in the ``next`` + query string variable, so you can redirect there if present + instead of the homepage.) + + If :attr:`LoginManager.refresh_view` is not defined, then it will + simply raise a HTTP 401 (Unauthorized) error instead. + + This should be returned from a view or before/after_request function, + otherwise the redirect will have no effect. + ''' + user_needs_refresh.send(current_app._get_current_object()) + + if self.needs_refresh_callback: + return self.needs_refresh_callback() + + if not self.refresh_view: + abort(401) + + if self.localize_callback is not None: + flash(self.localize_callback(self.needs_refresh_message), + category=self.needs_refresh_message_category) + else: + flash(self.needs_refresh_message, + category=self.needs_refresh_message_category) + + config = current_app.config + if config.get('USE_SESSION_FOR_NEXT', USE_SESSION_FOR_NEXT): + login_url = expand_login_view(self.refresh_view) + session['next'] = make_next_param(login_url, request.url) + redirect_url = make_login_url(self.refresh_view) + else: + login_url = self.refresh_view + redirect_url = make_login_url(login_url, next_url=request.url) + + return redirect(redirect_url) + + def reload_user(self, user=None): + ''' + This set the ctx.user with the user object loaded by your customized + user_loader callback function, which should retrieved the user object + with the user_id got from session. + + Syntax example: + from flask_login import LoginManager + @login_manager.user_loader + def any_valid_func_name(user_id): + # get your user object using the given user_id, + # if you use SQLAlchemy, for example: + user_obj = User.query.get(int(user_id)) + return user_obj + + Reason to let YOU define this self.user_callback: + Because we won't know how/where you will load you user object. + ''' + ctx = _request_ctx_stack.top + + if user is None: + user_id = session.get('user_id') + if user_id is None: + ctx.user = self.anonymous_user() + else: + if self.user_callback is None: + raise Exception( + "No user_loader has been installed for this " + "LoginManager. Refer to" + "https://flask-login.readthedocs.io/" + "en/latest/#how-it-works for more info.") + user = self.user_callback(user_id) + if user is None: + ctx.user = self.anonymous_user() + else: + ctx.user = user + else: + ctx.user = user + + def _load_user(self): + '''Loads user from session or remember_me cookie as applicable''' + user_accessed.send(current_app._get_current_object()) + + # first check SESSION_PROTECTION + config = current_app.config + if config.get('SESSION_PROTECTION', self.session_protection): + deleted = self._session_protection() + if deleted: + return self.reload_user() + + # If a remember cookie is set, and the session is not, move the + # cookie user ID to the session. + # + # However, the session may have been set if the user has been + # logged out on this request, 'remember' would be set to clear, + # so we should check for that and not restore the session. + is_missing_user_id = 'user_id' not in session + if is_missing_user_id: + cookie_name = config.get('REMEMBER_COOKIE_NAME', COOKIE_NAME) + header_name = config.get('AUTH_HEADER_NAME', AUTH_HEADER_NAME) + has_cookie = (cookie_name in request.cookies and + session.get('remember') != 'clear') + if has_cookie: + return self._load_from_cookie(request.cookies[cookie_name]) + elif self.request_callback: + return self._load_from_request(request) + elif header_name in request.headers: + return self._load_from_header(request.headers[header_name]) + + return self.reload_user() + + def _session_protection(self): + sess = session._get_current_object() + ident = self._session_identifier_generator() + + app = current_app._get_current_object() + mode = app.config.get('SESSION_PROTECTION', self.session_protection) + + # if the sess is empty, it's an anonymous user or just logged out + # so we can skip this + + if sess and ident != sess.get('_id', None): + if mode == 'basic' or sess.permanent: + sess['_fresh'] = False + session_protected.send(app) + return False + elif mode == 'strong': + for k in SESSION_KEYS: + sess.pop(k, None) + + sess['remember'] = 'clear' + session_protected.send(app) + return True + + return False + + def _load_from_cookie(self, cookie): + user_id = decode_cookie(cookie) + if user_id is not None: + session['user_id'] = user_id + session['_fresh'] = False + + self.reload_user() + + if _request_ctx_stack.top.user is not None: + app = current_app._get_current_object() + user_loaded_from_cookie.send(app, user=_get_user()) + + def _load_from_header(self, header): + user = None + if self.header_callback: + user = self.header_callback(header) + if user is not None: + self.reload_user(user=user) + app = current_app._get_current_object() + user_loaded_from_header.send(app, user=_get_user()) + else: + self.reload_user() + + def _load_from_request(self, request): + user = None + if self.request_callback: + user = self.request_callback(request) + if user is not None: + self.reload_user(user=user) + app = current_app._get_current_object() + user_loaded_from_request.send(app, user=_get_user()) + else: + self.reload_user() + + def _update_remember_cookie(self, response): + # Don't modify the session unless there's something to do. + if 'remember' not in session and \ + current_app.config.get('REMEMBER_COOKIE_REFRESH_EACH_REQUEST'): + session['remember'] = 'set' + + if 'remember' in session: + operation = session.pop('remember', None) + + if operation == 'set' and 'user_id' in session: + self._set_cookie(response) + elif operation == 'clear': + self._clear_cookie(response) + + return response + + def _set_cookie(self, response): + # cookie settings + config = current_app.config + cookie_name = config.get('REMEMBER_COOKIE_NAME', COOKIE_NAME) + domain = config.get('REMEMBER_COOKIE_DOMAIN') + path = config.get('REMEMBER_COOKIE_PATH', '/') + + secure = config.get('REMEMBER_COOKIE_SECURE', COOKIE_SECURE) + httponly = config.get('REMEMBER_COOKIE_HTTPONLY', COOKIE_HTTPONLY) + + if 'remember_seconds' in session: + duration = timedelta(seconds=session['remember_seconds']) + else: + duration = config.get('REMEMBER_COOKIE_DURATION', COOKIE_DURATION) + + # prepare data + data = encode_cookie(text_type(session['user_id'])) + + if isinstance(duration, int): + duration = timedelta(seconds=duration) + + try: + expires = datetime.utcnow() + duration + except TypeError: + raise Exception('REMEMBER_COOKIE_DURATION must be a ' + + 'datetime.timedelta, instead got: {0}'.format( + duration)) + + # actually set it + response.set_cookie(cookie_name, + value=data, + expires=expires, + domain=domain, + path=path, + secure=secure, + httponly=httponly) + + def _clear_cookie(self, response): + config = current_app.config + cookie_name = config.get('REMEMBER_COOKIE_NAME', COOKIE_NAME) + domain = config.get('REMEMBER_COOKIE_DOMAIN') + path = config.get('REMEMBER_COOKIE_PATH', '/') + response.delete_cookie(cookie_name, domain=domain, path=path) diff --git a/venv/Lib/site-packages/flask_login/mixins.py b/venv/Lib/site-packages/flask_login/mixins.py new file mode 100644 index 0000000..02fa864 --- /dev/null +++ b/venv/Lib/site-packages/flask_login/mixins.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +''' + flask_login.mixins + ------------------ + This module provides mixin objects. +''' + + +from ._compat import PY2, text_type + + +class UserMixin(object): + ''' + This provides default implementations for the methods that Flask-Login + expects user objects to have. + ''' + + if not PY2: # pragma: no cover + # Python 3 implicitly set __hash__ to None if we override __eq__ + # We set it back to its default implementation + __hash__ = object.__hash__ + + @property + def is_active(self): + return True + + @property + def is_authenticated(self): + return True + + @property + def is_anonymous(self): + return False + + def get_id(self): + try: + return text_type(self.id) + except AttributeError: + raise NotImplementedError('No `id` attribute - override `get_id`') + + def __eq__(self, other): + ''' + Checks the equality of two `UserMixin` objects using `get_id`. + ''' + if isinstance(other, UserMixin): + return self.get_id() == other.get_id() + return NotImplemented + + def __ne__(self, other): + ''' + Checks the inequality of two `UserMixin` objects using `get_id`. + ''' + equal = self.__eq__(other) + if equal is NotImplemented: + return NotImplemented + return not equal + + +class AnonymousUserMixin(object): + ''' + This is the default object for representing an anonymous user. + ''' + @property + def is_authenticated(self): + return False + + @property + def is_active(self): + return False + + @property + def is_anonymous(self): + return True + + def get_id(self): + return diff --git a/venv/Lib/site-packages/flask_login/signals.py b/venv/Lib/site-packages/flask_login/signals.py new file mode 100644 index 0000000..bb15af8 --- /dev/null +++ b/venv/Lib/site-packages/flask_login/signals.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +''' + flask_login.signals + ------------------- + This module provides signals to get notified when Flask-Login performs + certain actions. +''' + + +from flask.signals import Namespace + + +_signals = Namespace() + + +#: Sent when a user is logged in. In addition to the app (which is the +#: sender), it is passed `user`, which is the user being logged in. +user_logged_in = _signals.signal('logged-in') + +#: Sent when a user is logged out. In addition to the app (which is the +#: sender), it is passed `user`, which is the user being logged out. +user_logged_out = _signals.signal('logged-out') + +#: Sent when the user is loaded from the cookie. In addition to the app (which +#: is the sender), it is passed `user`, which is the user being reloaded. +user_loaded_from_cookie = _signals.signal('loaded-from-cookie') + +#: Sent when the user is loaded from the header. In addition to the app (which +#: is the #: sender), it is passed `user`, which is the user being reloaded. +user_loaded_from_header = _signals.signal('loaded-from-header') + +#: Sent when the user is loaded from the request. In addition to the app (which +#: is the #: sender), it is passed `user`, which is the user being reloaded. +user_loaded_from_request = _signals.signal('loaded-from-request') + +#: Sent when a user's login is confirmed, marking it as fresh. (It is not +#: called for a normal login.) +#: It receives no additional arguments besides the app. +user_login_confirmed = _signals.signal('login-confirmed') + +#: Sent when the `unauthorized` method is called on a `LoginManager`. It +#: receives no additional arguments besides the app. +user_unauthorized = _signals.signal('unauthorized') + +#: Sent when the `needs_refresh` method is called on a `LoginManager`. It +#: receives no additional arguments besides the app. +user_needs_refresh = _signals.signal('needs-refresh') + +#: Sent whenever the user is accessed/loaded +#: receives no additional arguments besides the app. +user_accessed = _signals.signal('accessed') + +#: Sent whenever session protection takes effect, and a session is either +#: marked non-fresh or deleted. It receives no additional arguments besides +#: the app. +session_protected = _signals.signal('session-protected') diff --git a/venv/Lib/site-packages/flask_login/utils.py b/venv/Lib/site-packages/flask_login/utils.py new file mode 100644 index 0000000..623c4d2 --- /dev/null +++ b/venv/Lib/site-packages/flask_login/utils.py @@ -0,0 +1,378 @@ +# -*- coding: utf-8 -*- +''' + flask_login.utils + ----------------- + General utilities. +''' + + +import hmac +from hashlib import sha512 +from functools import wraps +from werkzeug.local import LocalProxy +from werkzeug.security import safe_str_cmp +from werkzeug.urls import url_decode, url_encode + +from flask import (_request_ctx_stack, current_app, request, session, url_for, + has_request_context) + +from ._compat import text_type, urlparse, urlunparse +from .config import COOKIE_NAME, EXEMPT_METHODS +from .signals import user_logged_in, user_logged_out, user_login_confirmed + + +#: A proxy for the current user. If no user is logged in, this will be an +#: anonymous user +current_user = LocalProxy(lambda: _get_user()) + + +def encode_cookie(payload): + ''' + This will encode a ``unicode`` value into a cookie, and sign that cookie + with the app's secret key. + + :param payload: The value to encode, as `unicode`. + :type payload: unicode + ''' + return u'{0}|{1}'.format(payload, _cookie_digest(payload)) + + +def decode_cookie(cookie): + ''' + This decodes a cookie given by `encode_cookie`. If verification of the + cookie fails, ``None`` will be implicitly returned. + + :param cookie: An encoded cookie. + :type cookie: str + ''' + try: + payload, digest = cookie.rsplit(u'|', 1) + if hasattr(digest, 'decode'): + digest = digest.decode('ascii') # pragma: no cover + except ValueError: + return + + if safe_str_cmp(_cookie_digest(payload), digest): + return payload + + +def make_next_param(login_url, current_url): + ''' + Reduces the scheme and host from a given URL so it can be passed to + the given `login` URL more efficiently. + + :param login_url: The login URL being redirected to. + :type login_url: str + :param current_url: The URL to reduce. + :type current_url: str + ''' + l = urlparse(login_url) + c = urlparse(current_url) + + if (not l.scheme or l.scheme == c.scheme) and \ + (not l.netloc or l.netloc == c.netloc): + return urlunparse(('', '', c.path, c.params, c.query, '')) + return current_url + + +def expand_login_view(login_view): + ''' + Returns the url for the login view, expanding the view name to a url if + needed. + + :param login_view: The name of the login view or a URL for the login view. + :type login_view: str + ''' + if login_view.startswith(('https://', 'http://', '/')): + return login_view + else: + return url_for(login_view) + + +def login_url(login_view, next_url=None, next_field='next'): + ''' + Creates a URL for redirecting to a login page. If only `login_view` is + provided, this will just return the URL for it. If `next_url` is provided, + however, this will append a ``next=URL`` parameter to the query string + so that the login view can redirect back to that URL. Flask-Login's default + unauthorized handler uses this function when redirecting to your login url. + To force the host name used, set `FORCE_HOST_FOR_REDIRECTS` to a host. This + prevents from redirecting to external sites if request headers Host or + X-Forwarded-For are present. + + :param login_view: The name of the login view. (Alternately, the actual + URL to the login view.) + :type login_view: str + :param next_url: The URL to give the login view for redirection. + :type next_url: str + :param next_field: What field to store the next URL in. (It defaults to + ``next``.) + :type next_field: str + ''' + base = expand_login_view(login_view) + + if next_url is None: + return base + + parsed_result = urlparse(base) + md = url_decode(parsed_result.query) + md[next_field] = make_next_param(base, next_url) + netloc = current_app.config.get('FORCE_HOST_FOR_REDIRECTS') or \ + parsed_result.netloc + parsed_result = parsed_result._replace(netloc=netloc, + query=url_encode(md, sort=True)) + return urlunparse(parsed_result) + + +def login_fresh(): + ''' + This returns ``True`` if the current login is fresh. + ''' + return session.get('_fresh', False) + + +def login_user(user, remember=False, duration=None, force=False, fresh=True): + ''' + Logs a user in. You should pass the actual user object to this. If the + user's `is_active` property is ``False``, they will not be logged in + unless `force` is ``True``. + + This will return ``True`` if the log in attempt succeeds, and ``False`` if + it fails (i.e. because the user is inactive). + + :param user: The user object to log in. + :type user: object + :param remember: Whether to remember the user after their session expires. + Defaults to ``False``. + :type remember: bool + :param duration: The amount of time before the remember cookie expires. If + ``None`` the value set in the settings is used. Defaults to ``None``. + :type duration: :class:`datetime.timedelta` + :param force: If the user is inactive, setting this to ``True`` will log + them in regardless. Defaults to ``False``. + :type force: bool + :param fresh: setting this to ``False`` will log in the user with a session + marked as not "fresh". Defaults to ``True``. + :type fresh: bool + ''' + if not force and not user.is_active: + return False + + user_id = getattr(user, current_app.login_manager.id_attribute)() + session['user_id'] = user_id + session['_fresh'] = fresh + session['_id'] = current_app.login_manager._session_identifier_generator() + + if remember: + session['remember'] = 'set' + if duration is not None: + try: + # equal to timedelta.total_seconds() but works with Python 2.6 + session['remember_seconds'] = (duration.microseconds + + (duration.seconds + + duration.days * 24 * 3600) * + 10**6) / 10.0**6 + except AttributeError: + raise Exception('duration must be a datetime.timedelta, ' + 'instead got: {0}'.format(duration)) + + _request_ctx_stack.top.user = user + user_logged_in.send(current_app._get_current_object(), user=_get_user()) + return True + + +def logout_user(): + ''' + Logs a user out. (You do not need to pass the actual user.) This will + also clean up the remember me cookie if it exists. + ''' + + user = _get_user() + + if 'user_id' in session: + session.pop('user_id') + + if '_fresh' in session: + session.pop('_fresh') + + cookie_name = current_app.config.get('REMEMBER_COOKIE_NAME', COOKIE_NAME) + if cookie_name in request.cookies: + session['remember'] = 'clear' + if 'remember_seconds' in session: + session.pop('remember_seconds') + + user_logged_out.send(current_app._get_current_object(), user=user) + + current_app.login_manager.reload_user() + return True + + +def confirm_login(): + ''' + This sets the current session as fresh. Sessions become stale when they + are reloaded from a cookie. + ''' + session['_fresh'] = True + session['_id'] = current_app.login_manager._session_identifier_generator() + user_login_confirmed.send(current_app._get_current_object()) + + +def login_required(func): + ''' + If you decorate a view with this, it will ensure that the current user is + logged in and authenticated before calling the actual view. (If they are + not, it calls the :attr:`LoginManager.unauthorized` callback.) For + example:: + + @app.route('/post') + @login_required + def post(): + pass + + If there are only certain times you need to require that your user is + logged in, you can do so with:: + + if not current_user.is_authenticated: + return current_app.login_manager.unauthorized() + + ...which is essentially the code that this function adds to your views. + + It can be convenient to globally turn off authentication when unit testing. + To enable this, if the application configuration variable `LOGIN_DISABLED` + is set to `True`, this decorator will be ignored. + + .. Note :: + + Per `W3 guidelines for CORS preflight requests + <http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0>`_, + HTTP ``OPTIONS`` requests are exempt from login checks. + + :param func: The view function to decorate. + :type func: function + ''' + @wraps(func) + def decorated_view(*args, **kwargs): + if request.method in EXEMPT_METHODS: + return func(*args, **kwargs) + elif current_app.login_manager._login_disabled: + return func(*args, **kwargs) + elif not current_user.is_authenticated: + return current_app.login_manager.unauthorized() + return func(*args, **kwargs) + return decorated_view + + +def fresh_login_required(func): + ''' + If you decorate a view with this, it will ensure that the current user's + login is fresh - i.e. their session was not restored from a 'remember me' + cookie. Sensitive operations, like changing a password or e-mail, should + be protected with this, to impede the efforts of cookie thieves. + + If the user is not authenticated, :meth:`LoginManager.unauthorized` is + called as normal. If they are authenticated, but their session is not + fresh, it will call :meth:`LoginManager.needs_refresh` instead. (In that + case, you will need to provide a :attr:`LoginManager.refresh_view`.) + + Behaves identically to the :func:`login_required` decorator with respect + to configutation variables. + + .. Note :: + + Per `W3 guidelines for CORS preflight requests + <http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0>`_, + HTTP ``OPTIONS`` requests are exempt from login checks. + + :param func: The view function to decorate. + :type func: function + ''' + @wraps(func) + def decorated_view(*args, **kwargs): + if request.method in EXEMPT_METHODS: + return func(*args, **kwargs) + elif current_app.login_manager._login_disabled: + return func(*args, **kwargs) + elif not current_user.is_authenticated: + return current_app.login_manager.unauthorized() + elif not login_fresh(): + return current_app.login_manager.needs_refresh() + return func(*args, **kwargs) + return decorated_view + + +def set_login_view(login_view, blueprint=None): + ''' + Sets the login view for the app or blueprint. If a blueprint is passed, + the login view is set for this blueprint on ``blueprint_login_views``. + + :param login_view: The user object to log in. + :type login_view: str + :param blueprint: The blueprint which this login view should be set on. + Defaults to ``None``. + :type blueprint: object + ''' + + num_login_views = len(current_app.login_manager.blueprint_login_views) + if blueprint is not None or num_login_views != 0: + + (current_app.login_manager + .blueprint_login_views[blueprint.name]) = login_view + + if (current_app.login_manager.login_view is not None and + None not in current_app.login_manager.blueprint_login_views): + + (current_app.login_manager + .blueprint_login_views[None]) = (current_app.login_manager + .login_view) + + current_app.login_manager.login_view = None + else: + current_app.login_manager.login_view = login_view + + +def _get_user(): + if has_request_context() and not hasattr(_request_ctx_stack.top, 'user'): + current_app.login_manager._load_user() + + return getattr(_request_ctx_stack.top, 'user', None) + + +def _cookie_digest(payload, key=None): + key = _secret_key(key) + + return hmac.new(key, payload.encode('utf-8'), sha512).hexdigest() + + +def _get_remote_addr(): + address = request.headers.get('X-Forwarded-For', request.remote_addr) + if address is not None: + # An 'X-Forwarded-For' header includes a comma separated list of the + # addresses, the first address being the actual remote address. + address = address.encode('utf-8').split(b',')[0].strip() + return address + + +def _create_identifier(): + user_agent = request.headers.get('User-Agent') + if user_agent is not None: + user_agent = user_agent.encode('utf-8') + base = '{0}|{1}'.format(_get_remote_addr(), user_agent) + if str is bytes: + base = text_type(base, 'utf-8', errors='replace') # pragma: no cover + h = sha512() + h.update(base.encode('utf8')) + return h.hexdigest() + + +def _user_context_processor(): + return dict(current_user=_get_user()) + + +def _secret_key(key=None): + if key is None: + key = current_app.config['SECRET_KEY'] + + if isinstance(key, text_type): # pragma: no cover + key = key.encode('latin1') # ensure bytes + + return key diff --git a/venv/Lib/site-packages/flask_script/__init__.py b/venv/Lib/site-packages/flask_script/__init__.py new file mode 100644 index 0000000..306f522 --- /dev/null +++ b/venv/Lib/site-packages/flask_script/__init__.py @@ -0,0 +1,421 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import os +import re +import sys +import types +import warnings +from gettext import gettext as _ +from collections import OrderedDict + +import argparse + +from flask import Flask +from flask._compat import text_type + +from ._compat import iteritems +from .commands import Group, Option, Command, Server, Shell +from .cli import prompt, prompt_pass, prompt_bool, prompt_choices + +__all__ = ["Command", "Shell", "Server", "Manager", "Group", "Option", + "prompt", "prompt_pass", "prompt_bool", "prompt_choices"] + +safe_actions = (argparse._StoreAction, + argparse._StoreConstAction, + argparse._StoreTrueAction, + argparse._StoreFalseAction, + argparse._AppendAction, + argparse._AppendConstAction, + argparse._CountAction) + + +try: + import argcomplete + ARGCOMPLETE_IMPORTED = True +except ImportError: + ARGCOMPLETE_IMPORTED = False + +def add_help(parser, help_args): + if not help_args: + return + parser.add_argument(*help_args, + action='help', default=argparse.SUPPRESS, help=_('show this help message and exit')) + +class Manager(object): + """ + Controller class for handling a set of commands. + + Typical usage:: + + class Print(Command): + + def run(self): + print "hello" + + app = Flask(__name__) + + manager = Manager(app) + manager.add_command("print", Print()) + + if __name__ == "__main__": + manager.run() + + On command line:: + + python manage.py print + > hello + + :param app: Flask instance, or callable returning a Flask instance. + :param with_default_commands: load commands **runserver** and **shell** + by default. + :param disable_argcomplete: disable automatic loading of argcomplete. + + """ + help_args = ('-?','--help') + + def __init__(self, app=None, with_default_commands=None, usage=None, + help=None, description=None, disable_argcomplete=False): + + self.app = app + + self.subparser_kwargs = dict() + + self._commands = OrderedDict() + self._options = list() + + self.usage = usage + self.help = help if help is not None else usage + self.description = description if description is not None else usage + self.disable_argcomplete = disable_argcomplete + self.with_default_commands = with_default_commands + + self.parent = None + + def add_default_commands(self): + """ + Adds the shell and runserver default commands. To override these, + simply add your own equivalents using add_command or decorators. + """ + + if "shell" not in self._commands: + self.add_command("shell", Shell()) + if "runserver" not in self._commands: + self.add_command("runserver", Server()) + + def add_option(self, *args, **kwargs): + """ + Adds a global option. This is useful if you want to set variables + applying to the application setup, rather than individual commands. + + For this to work, the manager must be initialized with a factory + function rather than a Flask instance. Otherwise any options you set + will be ignored. + + The arguments are then passed to your function, e.g.:: + + def create_my_app(config=None): + app = Flask(__name__) + if config: + app.config.from_pyfile(config) + + return app + + manager = Manager(create_my_app) + manager.add_option("-c", "--config", dest="config", required=False) + @manager.command + def mycommand(app): + app.do_something() + + and are invoked like this:: + + > python manage.py -c dev.cfg mycommand + + Any manager options passed on the command line will not be passed to + the command. + + Arguments for this function are the same as for the Option class. + """ + + self._options.append(Option(*args, **kwargs)) + + def __call__(self, app=None, **kwargs): + """ + This procedure is called with the App instance (if this is a + sub-Manager) and any options. + + If your sub-Manager does not override this, any values for options will get lost. + """ + if app is None: + app = self.app + if app is None: + raise Exception("There is no app here. This is unlikely to work.") + + if isinstance(app, Flask): + if kwargs: + warnings.warn("Options will be ignored.") + return app + + app = app(**kwargs) + self.app = app + return app + + def create_app(self, *args, **kwargs): + warnings.warn("create_app() is deprecated; use __call__().", warnings.DeprecationWarning) + return self(*args,**kwargs) + + def create_parser(self, prog, func_stack=(), parent=None): + """ + Creates an ArgumentParser instance from options returned + by get_options(), and subparser for the given commands. + """ + prog = os.path.basename(prog) + func_stack=func_stack+(self,) + + options_parser = argparse.ArgumentParser(add_help=False) + for option in self.get_options(): + options_parser.add_argument(*option.args, **option.kwargs) + + parser = argparse.ArgumentParser(prog=prog, usage=self.usage, + description=self.description, + parents=[options_parser], + add_help=False) + add_help(parser, self.help_args) + + self._patch_argparser(parser) + + subparsers = parser.add_subparsers(**self.subparser_kwargs) + + for name, command in self._commands.items(): + usage = getattr(command, 'usage', None) + help = getattr(command, 'help', None) + if help is None: help = command.__doc__ + description = getattr(command, 'description', None) + if description is None: description = command.__doc__ + + command_parser = command.create_parser(name, func_stack=func_stack, parent=self) + + subparser = subparsers.add_parser(name, usage=usage, help=help, + description=description, + parents=[command_parser], + add_help=False) + + if isinstance(command, Manager): + self._patch_argparser(subparser) + + ## enable autocomplete only for parent parser when argcomplete is + ## imported and it is NOT disabled in constructor + if parent is None and ARGCOMPLETE_IMPORTED \ + and not self.disable_argcomplete: + argcomplete.autocomplete(parser, always_complete_options=True) + + self.parser = parser + return parser + + # def foo(self, app, *args, **kwargs): + # print(args) + + def _patch_argparser(self, parser): + """ + Patches the parser to print the full help if no arguments are supplied + """ + + def _parse_known_args(self, arg_strings, *args, **kw): + if not arg_strings: + self.print_help() + self.exit(2) + + return self._parse_known_args2(arg_strings, *args, **kw) + + parser._parse_known_args2 = parser._parse_known_args + parser._parse_known_args = types.MethodType(_parse_known_args, parser) + + def get_options(self): + return self._options + + def add_command(self, *args, **kwargs): + """ + Adds command to registry. + + :param command: Command instance + :param name: Name of the command (optional) + :param namespace: Namespace of the command (optional; pass as kwarg) + """ + + if len(args) == 1: + command = args[0] + name = None + + else: + name, command = args + + if name is None: + if hasattr(command, 'name'): + name = command.name + + else: + name = type(command).__name__.lower() + name = re.sub(r'command$', '', name) + + if isinstance(command, Manager): + command.parent = self + + if isinstance(command, type): + command = command() + + namespace = kwargs.get('namespace') + if not namespace: + namespace = getattr(command, 'namespace', None) + + if namespace: + if namespace not in self._commands: + self.add_command(namespace, Manager()) + + self._commands[namespace]._commands[name] = command + + else: + self._commands[name] = command + + def command(self, func): + """ + Decorator to add a command function to the registry. + + :param func: command function.Arguments depend on the + options. + + """ + + command = Command(func) + self.add_command(func.__name__, command) + + return func + + def option(self, *args, **kwargs): + """ + Decorator to add an option to a function. Automatically registers the + function - do not use together with ``@command``. You can add as many + ``@option`` calls as you like, for example:: + + @option('-n', '--name', dest='name') + @option('-u', '--url', dest='url') + def hello(name, url): + print "hello", name, url + + Takes the same arguments as the ``Option`` constructor. + """ + + option = Option(*args, **kwargs) + + def decorate(func): + name = func.__name__ + + if name not in self._commands: + + command = Command() + command.run = func + command.__doc__ = func.__doc__ + command.option_list = [] + + self.add_command(name, command) + + self._commands[name].option_list.append(option) + return func + return decorate + + def shell(self, func): + """ + Decorator that wraps function in shell command. This is equivalent to:: + + def _make_context(app): + return dict(app=app) + + manager.add_command("shell", Shell(make_context=_make_context)) + + The decorated function should take a single "app" argument, and return + a dict. + + For more sophisticated usage use the Shell class. + """ + + self.add_command('shell', Shell(make_context=func)) + + return func + + def set_defaults(self): + if self.with_default_commands is None: + self.with_default_commands = self.parent is None + if self.with_default_commands: + self.add_default_commands() + self.with_default_commands = False + + def handle(self, prog, args=None): + self.set_defaults() + app_parser = self.create_parser(prog) + + args = list(args or []) + app_namespace, remaining_args = app_parser.parse_known_args(args) + + # get the handle function and remove it from parsed options + kwargs = app_namespace.__dict__ + func_stack = kwargs.pop('func_stack', None) + if not func_stack: + app_parser.error('too few arguments') + + last_func = func_stack[-1] + if remaining_args and not getattr(last_func, 'capture_all_args', False): + app_parser.error('too many arguments') + + args = [] + for handle in func_stack: + + # get only safe config options + config_keys = [action.dest for action in handle.parser._actions + if handle is last_func or action.__class__ in safe_actions] + + # pass only safe app config keys + config = dict((k, v) for k, v in iteritems(kwargs) + if k in config_keys) + + # remove application config keys from handle kwargs + kwargs = dict((k, v) for k, v in iteritems(kwargs) + if k not in config_keys) + + if handle is last_func and getattr(last_func, 'capture_all_args', False): + args.append(remaining_args) + try: + res = handle(*args, **config) + except TypeError as err: + err.args = ("{0}: {1}".format(handle,str(err)),) + raise + + args = [res] + + assert not kwargs + return res + + def run(self, commands=None, default_command=None): + """ + Prepares manager to receive command line input. Usually run + inside "if __name__ == "__main__" block in a Python script. + + :param commands: optional dict of commands. Appended to any commands + added using add_command(). + + :param default_command: name of default command to run if no + arguments passed. + """ + + if commands: + self._commands.update(commands) + + # Make sure all of this is Unicode + argv = list(text_type(arg) for arg in sys.argv) + if default_command is not None and len(argv) == 1: + argv.append(default_command) + + try: + result = self.handle(argv[0], argv[1:]) + except SystemExit as e: + result = e.code + + sys.exit(result or 0) diff --git a/venv/Lib/site-packages/flask_script/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/flask_script/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3dbb976fcbd34663aa049444295e4e80087f8edb GIT binary patch literal 11675 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_script/__pycache__/_compat.cpython-36.pyc b/venv/Lib/site-packages/flask_script/__pycache__/_compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b26b40290fa88ee55b8a0c32d6a4b469dd25d5f GIT binary patch literal 3598 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_script/__pycache__/cli.cpython-36.pyc b/venv/Lib/site-packages/flask_script/__pycache__/cli.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f057f12195bf27eb7a03524c59dc24a7cb9a2b5e GIT binary patch literal 2518 zcmc&$QE%He5GE;EcAUgb(=KhgZX<%Vj?tuOu>k{uqAS)Q!yaY~>xKfJ3qvufRJJUs zB%Q{A{8H}+4Eq6l?+@6Y;p?9I7y7h2%5k%yD3FI?vUNO($K#Rr9Y1}sx>`T^^Shp^ z6Y>{1cX^;cf?Mx{5Jb>~bckTWOX!Go*vRX6l!%7#Mder8@x>((h$?6mu_l&89kf8K zixsg7T2;^|q;=&Dcp~jL6l9Cp6>U_U9`(#5m)ch1G#{HjLld}sxG{pqaO)pH6r`Z4 zA!tE2NWLc6H8N!dE69KdZ${PU!q0Du%2)9Agd1A08J&8CrydmE6BsFi;gZ@ZAc_|| zPl*U1n(FDRI*0396&~`jmWsOwc%-rs?`NY?m<pc6sk~cvc6)iK!Vyoyk=)}s80Cf= zd1B6Y3K@mt#O(38!jHpbEO{L9G@HAJk;k!+;;tKaTH(Lk($6e;OTT_g`6=7uZP=Qc zOoe_Ko97Dmq2*g2r?u6&TN{+~Tc(a*6SWF9Z+nmTx<A7zba&F!%}IA(>Y>T<?l)OB z-2FaG!y`G8so8xp(MFDTM<Fz-do0uAZW14Mb!_Br9`=V|r@K)S>S0g!Rh*k{KZ)<= z6SWMpRN>ZZAV}>h-Jp!ts1Lsyb-nLT-j85?f#hu&I(V19haX7aZ;SM4{yY{!rhGdO zwSJi?(fS{gsVcBy6`;WVn&(v2K)2N+X)wl?ua?jNijKIwQ!A)pCQH!(%`TT|?%{|{ zc=3;|ub*u_=HsA`K*0+q$GKhMbU`Ut9*E0OEMDFa>r?OKz?AveDwtT^B>8sXVLM|} zzwq^!SR{o%pfeab4Aj18ES^1hUKf|3h}NNq*07|*J5bH%67iuz;#g7rEIpRWXl^oo zm}Q9!Q#agI%70mE^ZK1g-RnQk;=a`H%CtFY?(imbVRIivmZCIjEM41lF`gX(*?T-V z`!qxAd(J@=VH1Ukb}pjQg{2eg!9<qcw=7Gok5gD`EKMy-GfVGTFN-2uaRV)l6rPqp z9kg44x(u3HM{xzkRTLkg*Z=|j*HFKX0>Pqgp!gWYO%N^SaAqq>_EM@=<sJSy%jfo_ zQ%@JG`yeJUcXz<A#xTfojn){WHykEcK(0{rDZGb+D@)}9oLB=3aQ*=gM`Y^5T{&qN zbe}vsm<9mOz|a{}+eKx-ieN@5Nw-jkvp*1Q6Z(a>Ng}pEu&;cY1_0T)^^3++a`MP{ z1wn2a_(f#~oN*1{JY$2(j7+OVwLmDpLLLC!9|rn&XxWgdPEiGTcg{E>lox=vV-771 z&mtrUcg*uS)-HhT1v-;hgD=I+I8P)x4UfY(2@m1n%qxFfLYa@k2|tt$rATV$FAXFM zLrS*Q=x}=|Cp#rKwMy^0q@y{&FHB?qE{r`M_WLq7F4l4x(IS8dBG^jDNitu>mW%qd zdP_SlRQL(tP=8wGyL|hWZk@6npvAz}V_1teVcM4pKMrgV<~dMhiEMw6#i{xP-nVKF z-L^X4CU?wsmKMnF)XoanR<qp1S*mSN+P)X_jrln|cjt~)tSPb#?8oY~PFL6sV8I5I zz)i=AYgFCFKxiBP-a)(lnySz64Qs0=wl#GL8e8qbr$!zcw;sp#B^}_Q?kdRnIY$hR cA{;x^9*i<EPUJ(}3XM!sqibG$9e&sU2D|rvjQ{`u literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_script/__pycache__/commands.cpython-36.pyc b/venv/Lib/site-packages/flask_script/__pycache__/commands.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0edbdf4fd12d526a49409e348eb9e592aec5e2f3 GIT binary patch literal 17394 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 Q<@3uw#NTfl<>lJ{1q{WHegFUf literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_script/_compat.py b/venv/Lib/site-packages/flask_script/_compat.py new file mode 100644 index 0000000..63f2173 --- /dev/null +++ b/venv/Lib/site-packages/flask_script/_compat.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +""" + flask_script._compat + ~~~~~~~~~~~~~~~~~~~~ + + Some py2/py3 compatibility support based on a stripped down + version of six so we don't have to depend on a specific version + of it. + + :copyright: (c) 2013 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +import sys + +PY2 = sys.version_info[0] == 2 +PYPY = hasattr(sys, 'pypy_translation_info') +_identity = lambda x: x + + +if not PY2: + unichr = chr + range_type = range + text_type = str + string_types = (str, ) + integer_types = (int, ) + + iterkeys = lambda d: iter(d.keys()) + itervalues = lambda d: iter(d.values()) + iteritems = lambda d: iter(d.items()) + + import pickle + from io import BytesIO, StringIO + NativeStringIO = StringIO + + def reraise(tp, value, tb=None): + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + + ifilter = filter + imap = map + izip = zip + intern = sys.intern + + implements_iterator = _identity + implements_to_string = _identity + encode_filename = _identity + get_next = lambda x: x.__next__ + + input = input + +else: + unichr = unichr + text_type = unicode + range_type = xrange + string_types = (str, unicode) + integer_types = (int, long) + + iterkeys = lambda d: d.iterkeys() + itervalues = lambda d: d.itervalues() + iteritems = lambda d: d.iteritems() + + import cPickle as pickle + from cStringIO import StringIO as BytesIO, StringIO + NativeStringIO = BytesIO + + exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') + + from itertools import imap, izip, ifilter + intern = intern + + def implements_iterator(cls): + cls.next = cls.__next__ + del cls.__next__ + return cls + + def implements_to_string(cls): + cls.__unicode__ = cls.__str__ + cls.__str__ = lambda x: x.__unicode__().encode('utf-8') + return cls + + get_next = lambda x: x.next + + def encode_filename(filename): + if isinstance(filename, unicode): + return filename.encode('utf-8') + return filename + + input = raw_input + + +def with_metaclass(meta, *bases): + # This requires a bit of explanation: the basic idea is to make a + # dummy metaclass for one level of class instantiation that replaces + # itself with the actual metaclass. Because of internal type checks + # we also need to make sure that we downgrade the custom metaclass + # for one level to something closer to type (that's why __call__ and + # __init__ comes back from type etc.). + # + # This has the advantage over six.with_metaclass in that it does not + # introduce dummy classes into the final MRO. + class metaclass(meta): + __call__ = type.__call__ + __init__ = type.__init__ + def __new__(cls, name, this_bases, d): + if this_bases is None: + return type.__new__(cls, name, (), d) + return meta(name, bases, d) + return metaclass('temporary_class', None, {}) + + +try: + from urllib.parse import quote_from_bytes as url_quote +except ImportError: + from urllib import quote as url_quote diff --git a/venv/Lib/site-packages/flask_script/cli.py b/venv/Lib/site-packages/flask_script/cli.py new file mode 100644 index 0000000..4457569 --- /dev/null +++ b/venv/Lib/site-packages/flask_script/cli.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +import getpass +from ._compat import string_types, input + + +def prompt(name, default=None): + """ + Grab user input from command line. + + :param name: prompt text + :param default: default value if no input provided. + """ + + prompt = name + (default and ' [%s]' % default or '') + prompt += name.endswith('?') and ' ' or ': ' + while True: + rv = input(prompt) + if rv: + return rv + if default is not None: + return default + + +def prompt_pass(name, default=None): + """ + Grabs hidden (password) input from command line. + + :param name: prompt text + :param default: default value if no input provided. + """ + + prompt = name + (default and ' [%s]' % default or '') + prompt += name.endswith('?') and ' ' or ': ' + while True: + rv = getpass.getpass(prompt) + if rv: + return rv + if default is not None: + return default + + +def prompt_bool(name, default=False, yes_choices=None, no_choices=None): + """ + Grabs user input from command line and converts to boolean + value. + + :param name: prompt text + :param default: default value if no input provided. + :param yes_choices: default 'y', 'yes', '1', 'on', 'true', 't' + :param no_choices: default 'n', 'no', '0', 'off', 'false', 'f' + """ + + yes_choices = yes_choices or ('y', 'yes', '1', 'on', 'true', 't') + no_choices = no_choices or ('n', 'no', '0', 'off', 'false', 'f') + + while True: + rv = prompt(name, default and yes_choices[0] or no_choices[0]) + if not rv: + return default + if rv.lower() in yes_choices: + return True + elif rv.lower() in no_choices: + return False + + +def prompt_choices(name, choices, default=None, no_choice=('none',)): + """ + Grabs user input from command line from set of provided choices. + + :param name: prompt text + :param choices: list or tuple of available choices. Choices may be + single strings or (key, value) tuples. + :param default: default value if no input provided. + :param no_choice: acceptable list of strings for "null choice" + """ + + _choices = [] + options = [] + + for choice in choices: + if isinstance(choice, string_types): + options.append(choice) + else: + options.append("%s [%s]" % (choice[1], choice[0])) + choice = choice[0] + _choices.append(choice) + + while True: + rv = prompt(name + ' - (%s)' % ', '.join(options), default).lower() + if rv in no_choice: + return None + if rv in _choices or rv == default: + return rv diff --git a/venv/Lib/site-packages/flask_script/commands.py b/venv/Lib/site-packages/flask_script/commands.py new file mode 100644 index 0000000..4d401eb --- /dev/null +++ b/venv/Lib/site-packages/flask_script/commands.py @@ -0,0 +1,561 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import,print_function + +import os +import sys +import code +import warnings +import string +import inspect + +import argparse + +from flask import _request_ctx_stack + +from .cli import prompt, prompt_pass, prompt_bool, prompt_choices +from ._compat import izip, text_type + + +class InvalidCommand(Exception): + """\ + This is a generic error for "bad" commands. + It is not used in Flask-Script itself, but you should throw + this error (or one derived from it) in your command handlers, + and your main code should display this error's message without + a stack trace. + + This way, we maintain interoperability if some other plug-in code + supplies Flask-Script hooks. + """ + pass + +class Group(object): + """ + Stores argument groups and mutually exclusive groups for + `ArgumentParser.add_argument_group <http://argparse.googlecode.com/svn/trunk/doc/other-methods.html#argument-groups>` + or `ArgumentParser.add_mutually_exclusive_group <http://argparse.googlecode.com/svn/trunk/doc/other-methods.html#add_mutually_exclusive_group>`. + + Note: The title and description params cannot be used with the exclusive + or required params. + + :param options: A list of Option classes to add to this group + :param title: A string to use as the title of the argument group + :param description: A string to use as the description of the argument + group + :param exclusive: A boolean indicating if this is an argument group or a + mutually exclusive group + :param required: A boolean indicating if this mutually exclusive group + must have an option selected + """ + + def __init__(self, *options, **kwargs): + self.option_list = options + + self.title = kwargs.pop("title", None) + self.description = kwargs.pop("description", None) + self.exclusive = kwargs.pop("exclusive", None) + self.required = kwargs.pop("required", None) + + if ((self.title or self.description) and + (self.required or self.exclusive)): + raise TypeError("title and/or description cannot be used with " + "required and/or exclusive.") + + super(Group, self).__init__(**kwargs) + + def get_options(self): + """ + By default, returns self.option_list. Override if you + need to do instance-specific configuration. + """ + return self.option_list + + +class Option(object): + """ + Stores positional and optional arguments for `ArgumentParser.add_argument + <http://argparse.googlecode.com/svn/trunk/doc/add_argument.html>`_. + + :param name_or_flags: Either a name or a list of option strings, + e.g. foo or -f, --foo + :param action: The basic type of action to be taken when this argument + is encountered at the command-line. + :param nargs: The number of command-line arguments that should be consumed. + :param const: A constant value required by some action and nargs selections. + :param default: The value produced if the argument is absent from + the command-line. + :param type: The type to which the command-line arg should be converted. + :param choices: A container of the allowable values for the argument. + :param required: Whether or not the command-line option may be omitted + (optionals only). + :param help: A brief description of what the argument does. + :param metavar: A name for the argument in usage messages. + :param dest: The name of the attribute to be added to the object + returned by parse_args(). + """ + + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + + +class Command(object): + """ + Base class for creating commands. + + :param func: Initialize this command by introspecting the function. + """ + + option_list = () + help_args = None + + def __init__(self, func=None): + if func is None: + if not self.option_list: + self.option_list = [] + return + + args, varargs, keywords, defaults = inspect.getargspec(func) + if inspect.ismethod(func): + args = args[1:] + + options = [] + + # first arg is always "app" : ignore + + defaults = defaults or [] + kwargs = dict(izip(*[reversed(l) for l in (args, defaults)])) + + for arg in args: + + if arg in kwargs: + + default = kwargs[arg] + + if isinstance(default, bool): + options.append(Option('-%s' % arg[0], + '--%s' % arg, + action="store_true", + dest=arg, + required=False, + default=default)) + else: + options.append(Option('-%s' % arg[0], + '--%s' % arg, + dest=arg, + type=text_type, + required=False, + default=default)) + + else: + options.append(Option(arg, type=text_type)) + + self.run = func + self.__doc__ = func.__doc__ + self.option_list = options + + @property + def description(self): + description = self.__doc__ or '' + return description.strip() + + def add_option(self, option): + """ + Adds Option to option list. + """ + self.option_list.append(option) + + def get_options(self): + """ + By default, returns self.option_list. Override if you + need to do instance-specific configuration. + """ + return self.option_list + + def create_parser(self, *args, **kwargs): + func_stack = kwargs.pop('func_stack',()) + parent = kwargs.pop('parent',None) + parser = argparse.ArgumentParser(*args, add_help=False, **kwargs) + help_args = self.help_args + while help_args is None and parent is not None: + help_args = parent.help_args + parent = getattr(parent,'parent',None) + + if help_args: + from flask_script import add_help + add_help(parser,help_args) + + for option in self.get_options(): + if isinstance(option, Group): + if option.exclusive: + group = parser.add_mutually_exclusive_group( + required=option.required, + ) + else: + group = parser.add_argument_group( + title=option.title, + description=option.description, + ) + for opt in option.get_options(): + group.add_argument(*opt.args, **opt.kwargs) + else: + parser.add_argument(*option.args, **option.kwargs) + + parser.set_defaults(func_stack=func_stack+(self,)) + + self.parser = parser + self.parent = parent + return parser + + def __call__(self, app=None, *args, **kwargs): + """ + Handles the command with the given app. + Default behaviour is to call ``self.run`` within a test request context. + """ + with app.test_request_context(): + return self.run(*args, **kwargs) + + def run(self): + """ + Runs a command. This must be implemented by the subclass. Should take + arguments as configured by the Command options. + """ + raise NotImplementedError + +class Shell(Command): + """ + Runs a Python shell inside Flask application context. + + :param banner: banner appearing at top of shell when started + :param make_context: a callable returning a dict of variables + used in the shell namespace. By default + returns a dict consisting of just the app. + :param use_ptipython: use PtIPython shell if available, ignore if not. + The PtIPython shell can be turned off in command + line by passing the **--no-ptipython** flag. + :param use_ptpython: use PtPython shell if available, ignore if not. + The PtPython shell can be turned off in command + line by passing the **--no-ptpython** flag. + :param use_bpython: use BPython shell if available, ignore if not. + The BPython shell can be turned off in command + line by passing the **--no-bpython** flag. + :param use_ipython: use IPython shell if available, ignore if not. + The IPython shell can be turned off in command + line by passing the **--no-ipython** flag. + """ + + banner = '' + + help = description = 'Runs a Python shell inside Flask application context.' + + def __init__(self, banner=None, make_context=None, use_ipython=True, + use_bpython=True, use_ptipython=True, use_ptpython=True): + + self.banner = banner or self.banner + self.use_ipython = use_ipython + self.use_bpython = use_bpython + self.use_ptipython = use_ptipython + self.use_ptpython = use_ptpython + + if make_context is None: + make_context = lambda: dict(app=_request_ctx_stack.top.app) + + self.make_context = make_context + + def get_options(self): + return ( + Option('--no-ipython', + action="store_true", + dest='no_ipython', + default=not(self.use_ipython), + help="Do not use the IPython shell"), + Option('--no-bpython', + action="store_true", + dest='no_bpython', + default=not(self.use_bpython), + help="Do not use the BPython shell"), + Option('--no-ptipython', + action="store_true", + dest='no_ptipython', + default=not(self.use_ptipython), + help="Do not use the PtIPython shell"), + Option('--no-ptpython', + action="store_true", + dest='no_ptpython', + default=not(self.use_ptpython), + help="Do not use the PtPython shell"), + ) + + def get_context(self): + """ + Returns a dict of context variables added to the shell namespace. + """ + return self.make_context() + + def run(self, no_ipython, no_bpython, no_ptipython, no_ptpython): + """ + Runs the shell. + If no_ptipython is False or use_ptipython is True, then a PtIPython shell is run (if installed). + If no_ptpython is False or use_ptpython is True, then a PtPython shell is run (if installed). + If no_bpython is False or use_bpython is True, then a BPython shell is run (if installed). + If no_ipython is False or use_python is True then a IPython shell is run (if installed). + """ + + context = self.get_context() + + if not no_ptipython: + # Try PtIPython + try: + from ptpython.ipython import embed + history_filename = os.path.expanduser('~/.ptpython_history') + embed(banner1=self.banner, user_ns=context, history_filename=history_filename) + return + except ImportError: + pass + + if not no_ptpython: + # Try PtPython + try: + from ptpython.repl import embed + history_filename = os.path.expanduser('~/.ptpython_history') + embed(globals=context, history_filename=history_filename) + return + except ImportError: + pass + + if not no_bpython: + # Try BPython + try: + from bpython import embed + embed(banner=self.banner, locals_=context) + return + except ImportError: + pass + + if not no_ipython: + # Try IPython + try: + from IPython import embed + embed(banner1=self.banner, user_ns=context) + return + except ImportError: + pass + + # Use basic python shell + code.interact(self.banner, local=context) + + +class Server(Command): + """ + Runs the Flask development server i.e. app.run() + + :param host: server host + :param port: server port + :param use_debugger: Flag whether to default to using the Werkzeug debugger. + This can be overriden in the command line + by passing the **-d** or **-D** flag. + Defaults to False, for security. + + :param use_reloader: Flag whether to use the auto-reloader. + Default to True when debugging. + This can be overriden in the command line by + passing the **-r**/**-R** flag. + :param threaded: should the process handle each request in a separate + thread? + :param processes: number of processes to spawn + :param passthrough_errors: disable the error catching. This means that the server will die on errors but it can be useful to hook debuggers in (pdb etc.) + :param ssl_crt: path to ssl certificate file + :param ssl_key: path to ssl key file + :param options: :func:`werkzeug.run_simple` options. + """ + + help = description = 'Runs the Flask development server i.e. app.run()' + + def __init__(self, host='127.0.0.1', port=5000, use_debugger=None, + use_reloader=None, threaded=False, processes=1, + passthrough_errors=False, ssl_crt=None, ssl_key=None, **options): + + self.port = port + self.host = host + self.use_debugger = use_debugger + self.use_reloader = use_reloader if use_reloader is not None else use_debugger + self.server_options = options + self.threaded = threaded + self.processes = processes + self.passthrough_errors = passthrough_errors + self.ssl_crt = ssl_crt + self.ssl_key = ssl_key + + def get_options(self): + + options = ( + Option('-h', '--host', + dest='host', + default=self.host), + + Option('-p', '--port', + dest='port', + type=int, + default=self.port), + + Option('--threaded', + dest='threaded', + action='store_true', + default=self.threaded), + + Option('--processes', + dest='processes', + type=int, + default=self.processes), + + Option('--passthrough-errors', + action='store_true', + dest='passthrough_errors', + default=self.passthrough_errors), + + Option('-d', '--debug', + action='store_true', + dest='use_debugger', + help='enable the Werkzeug debugger (DO NOT use in production code)', + default=self.use_debugger), + Option('-D', '--no-debug', + action='store_false', + dest='use_debugger', + help='disable the Werkzeug debugger', + default=self.use_debugger), + + Option('-r', '--reload', + action='store_true', + dest='use_reloader', + help='monitor Python files for changes (not 100%% safe for production use)', + default=self.use_reloader), + Option('-R', '--no-reload', + action='store_false', + dest='use_reloader', + help='do not monitor Python files for changes', + default=self.use_reloader), + Option('--ssl-crt', + dest='ssl_crt', + type=str, + help='Path to ssl certificate', + default=self.ssl_crt), + Option('--ssl-key', + dest='ssl_key', + type=str, + help='Path to ssl key', + default=self.ssl_key), + ) + + return options + + def __call__(self, app, host, port, use_debugger, use_reloader, + threaded, processes, passthrough_errors, ssl_crt, ssl_key): + # we don't need to run the server in request context + # so just run it directly + + if use_debugger is None: + use_debugger = app.debug + if use_debugger is None: + use_debugger = True + if sys.stderr.isatty(): + print("Debugging is on. DANGER: Do not allow random users to connect to this server.", file=sys.stderr) + if use_reloader is None: + use_reloader = use_debugger + + if None in [ssl_crt, ssl_key]: + ssl_context = None + else: + ssl_context = (ssl_crt, ssl_key) + + app.run(host=host, + port=port, + debug=use_debugger, + use_debugger=use_debugger, + use_reloader=use_reloader, + threaded=threaded, + processes=processes, + passthrough_errors=passthrough_errors, + ssl_context=ssl_context, + **self.server_options) + + +class Clean(Command): + "Remove *.pyc and *.pyo files recursively starting at current directory" + def run(self): + for dirpath, dirnames, filenames in os.walk('.'): + for filename in filenames: + if filename.endswith('.pyc') or filename.endswith('.pyo'): + full_pathname = os.path.join(dirpath, filename) + print('Removing %s' % full_pathname) + os.remove(full_pathname) + + +class ShowUrls(Command): + """ + Displays all of the url matching routes for the project + """ + def __init__(self, order='rule'): + self.order = order + + def get_options(self): + return ( + Option('url', + nargs='?', + help='Url to test (ex. /static/image.png)'), + Option('--order', + dest='order', + default=self.order, + help='Property on Rule to order by (default: %s)' % self.order) + ) + + def run(self, url, order): + from flask import current_app + from werkzeug.exceptions import NotFound, MethodNotAllowed + + rows = [] + column_length = 0 + column_headers = ('Rule', 'Endpoint', 'Arguments') + + if url: + try: + rule, arguments = current_app.url_map \ + .bind('localhost') \ + .match(url, return_rule=True) + rows.append((rule.rule, rule.endpoint, arguments)) + column_length = 3 + except (NotFound, MethodNotAllowed) as e: + rows.append(("<%s>" % e, None, None)) + column_length = 1 + else: + rules = sorted(current_app.url_map.iter_rules(), key=lambda rule: getattr(rule, order)) + for rule in rules: + rows.append((rule.rule, rule.endpoint, None)) + column_length = 2 + + str_template = '' + table_width = 0 + + if column_length >= 1: + max_rule_length = max(len(r[0]) for r in rows) + max_rule_length = max_rule_length if max_rule_length > 4 else 4 + str_template += '%-' + str(max_rule_length) + 's' + table_width += max_rule_length + + if column_length >= 2: + max_endpoint_length = max(len(str(r[1])) for r in rows) + # max_endpoint_length = max(rows, key=len) + max_endpoint_length = max_endpoint_length if max_endpoint_length > 8 else 8 + str_template += ' %-' + str(max_endpoint_length) + 's' + table_width += 2 + max_endpoint_length + + if column_length >= 3: + max_arguments_length = max(len(str(r[2])) for r in rows) + max_arguments_length = max_arguments_length if max_arguments_length > 9 else 9 + str_template += ' %-' + str(max_arguments_length) + 's' + table_width += 2 + max_arguments_length + + print(str_template % (column_headers[:column_length])) + print('-' * table_width) + + for row in rows: + print(str_template % row[:column_length]) diff --git a/venv/Lib/site-packages/flask_sqlalchemy/__init__.py b/venv/Lib/site-packages/flask_sqlalchemy/__init__.py new file mode 100644 index 0000000..9a90083 --- /dev/null +++ b/venv/Lib/site-packages/flask_sqlalchemy/__init__.py @@ -0,0 +1,1001 @@ +# -*- coding: utf-8 -*- +""" + flask_sqlalchemy + ~~~~~~~~~~~~~~~~ + + Adds basic SQLAlchemy support to your application. + + :copyright: (c) 2014 by Armin Ronacher, Daniel Neuhäuser. + :license: BSD, see LICENSE for more details. +""" +from __future__ import absolute_import + +import functools +import os +import sys +import time +import warnings +from math import ceil +from operator import itemgetter +from threading import Lock + +import sqlalchemy +from flask import _app_ctx_stack, abort, current_app, request +from flask.signals import Namespace +from sqlalchemy import event, inspect, orm +from sqlalchemy.engine.url import make_url +from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base +from sqlalchemy.orm.exc import UnmappedClassError +from sqlalchemy.orm.session import Session as SessionBase + +from flask_sqlalchemy.model import Model +from ._compat import itervalues, string_types, to_str, xrange +from .model import DefaultMeta + +__version__ = '2.3.2' + +# the best timer function for the platform +if sys.platform == 'win32': + _timer = time.clock +else: + _timer = time.time + +_signals = Namespace() +models_committed = _signals.signal('models-committed') +before_models_committed = _signals.signal('before-models-committed') + + +def _make_table(db): + def _make_table(*args, **kwargs): + if len(args) > 1 and isinstance(args[1], db.Column): + args = (args[0], db.metadata) + args[1:] + info = kwargs.pop('info', None) or {} + info.setdefault('bind_key', None) + kwargs['info'] = info + return sqlalchemy.Table(*args, **kwargs) + return _make_table + + +def _set_default_query_class(d, cls): + if 'query_class' not in d: + d['query_class'] = cls + + +def _wrap_with_default_query_class(fn, cls): + @functools.wraps(fn) + def newfn(*args, **kwargs): + _set_default_query_class(kwargs, cls) + if "backref" in kwargs: + backref = kwargs['backref'] + if isinstance(backref, string_types): + backref = (backref, {}) + _set_default_query_class(backref[1], cls) + return fn(*args, **kwargs) + return newfn + + +def _include_sqlalchemy(obj, cls): + for module in sqlalchemy, sqlalchemy.orm: + for key in module.__all__: + if not hasattr(obj, key): + setattr(obj, key, getattr(module, key)) + # Note: obj.Table does not attempt to be a SQLAlchemy Table class. + obj.Table = _make_table(obj) + obj.relationship = _wrap_with_default_query_class(obj.relationship, cls) + obj.relation = _wrap_with_default_query_class(obj.relation, cls) + obj.dynamic_loader = _wrap_with_default_query_class(obj.dynamic_loader, cls) + obj.event = event + + +class _DebugQueryTuple(tuple): + statement = property(itemgetter(0)) + parameters = property(itemgetter(1)) + start_time = property(itemgetter(2)) + end_time = property(itemgetter(3)) + context = property(itemgetter(4)) + + @property + def duration(self): + return self.end_time - self.start_time + + def __repr__(self): + return '<query statement="%s" parameters=%r duration=%.03f>' % ( + self.statement, + self.parameters, + self.duration + ) + + +def _calling_context(app_path): + frm = sys._getframe(1) + while frm.f_back is not None: + name = frm.f_globals.get('__name__') + if name and (name == app_path or name.startswith(app_path + '.')): + funcname = frm.f_code.co_name + return '%s:%s (%s)' % ( + frm.f_code.co_filename, + frm.f_lineno, + funcname + ) + frm = frm.f_back + return '<unknown>' + + +class SignallingSession(SessionBase): + """The signalling session is the default session that Flask-SQLAlchemy + uses. It extends the default session system with bind selection and + modification tracking. + + If you want to use a different session you can override the + :meth:`SQLAlchemy.create_session` function. + + .. versionadded:: 2.0 + + .. versionadded:: 2.1 + The `binds` option was added, which allows a session to be joined + to an external transaction. + """ + + def __init__(self, db, autocommit=False, autoflush=True, **options): + #: The application that this session belongs to. + self.app = app = db.get_app() + track_modifications = app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] + bind = options.pop('bind', None) or db.engine + binds = options.pop('binds', db.get_binds(app)) + + if track_modifications is None or track_modifications: + _SessionSignalEvents.register(self) + + SessionBase.__init__( + self, autocommit=autocommit, autoflush=autoflush, + bind=bind, binds=binds, **options + ) + + def get_bind(self, mapper=None, clause=None): + # mapper is None if someone tries to just get a connection + if mapper is not None: + info = getattr(mapper.mapped_table, 'info', {}) + bind_key = info.get('bind_key') + if bind_key is not None: + state = get_state(self.app) + return state.db.get_engine(self.app, bind=bind_key) + return SessionBase.get_bind(self, mapper, clause) + + +class _SessionSignalEvents(object): + @classmethod + def register(cls, session): + if not hasattr(session, '_model_changes'): + session._model_changes = {} + + event.listen(session, 'before_flush', cls.record_ops) + event.listen(session, 'before_commit', cls.record_ops) + event.listen(session, 'before_commit', cls.before_commit) + event.listen(session, 'after_commit', cls.after_commit) + event.listen(session, 'after_rollback', cls.after_rollback) + + @classmethod + def unregister(cls, session): + if hasattr(session, '_model_changes'): + del session._model_changes + + event.remove(session, 'before_flush', cls.record_ops) + event.remove(session, 'before_commit', cls.record_ops) + event.remove(session, 'before_commit', cls.before_commit) + event.remove(session, 'after_commit', cls.after_commit) + event.remove(session, 'after_rollback', cls.after_rollback) + + @staticmethod + def record_ops(session, flush_context=None, instances=None): + try: + d = session._model_changes + except AttributeError: + return + + for targets, operation in ((session.new, 'insert'), (session.dirty, 'update'), (session.deleted, 'delete')): + for target in targets: + state = inspect(target) + key = state.identity_key if state.has_identity else id(target) + d[key] = (target, operation) + + @staticmethod + def before_commit(session): + try: + d = session._model_changes + except AttributeError: + return + + if d: + before_models_committed.send(session.app, changes=list(d.values())) + + @staticmethod + def after_commit(session): + try: + d = session._model_changes + except AttributeError: + return + + if d: + models_committed.send(session.app, changes=list(d.values())) + d.clear() + + @staticmethod + def after_rollback(session): + try: + d = session._model_changes + except AttributeError: + return + + d.clear() + + +class _EngineDebuggingSignalEvents(object): + """Sets up handlers for two events that let us track the execution time of + queries.""" + + def __init__(self, engine, import_name): + self.engine = engine + self.app_package = import_name + + def register(self): + event.listen( + self.engine, 'before_cursor_execute', self.before_cursor_execute + ) + event.listen( + self.engine, 'after_cursor_execute', self.after_cursor_execute + ) + + def before_cursor_execute( + self, conn, cursor, statement, parameters, context, executemany + ): + if current_app: + context._query_start_time = _timer() + + def after_cursor_execute( + self, conn, cursor, statement, parameters, context, executemany + ): + if current_app: + try: + queries = _app_ctx_stack.top.sqlalchemy_queries + except AttributeError: + queries = _app_ctx_stack.top.sqlalchemy_queries = [] + + queries.append(_DebugQueryTuple(( + statement, parameters, context._query_start_time, _timer(), + _calling_context(self.app_package) + ))) + + +def get_debug_queries(): + """In debug mode Flask-SQLAlchemy will log all the SQL queries sent + to the database. This information is available until the end of request + which makes it possible to easily ensure that the SQL generated is the + one expected on errors or in unittesting. If you don't want to enable + the DEBUG mode for your unittests you can also enable the query + recording by setting the ``'SQLALCHEMY_RECORD_QUERIES'`` config variable + to `True`. This is automatically enabled if Flask is in testing mode. + + The value returned will be a list of named tuples with the following + attributes: + + `statement` + The SQL statement issued + + `parameters` + The parameters for the SQL statement + + `start_time` / `end_time` + Time the query started / the results arrived. Please keep in mind + that the timer function used depends on your platform. These + values are only useful for sorting or comparing. They do not + necessarily represent an absolute timestamp. + + `duration` + Time the query took in seconds + + `context` + A string giving a rough estimation of where in your application + query was issued. The exact format is undefined so don't try + to reconstruct filename or function name. + """ + return getattr(_app_ctx_stack.top, 'sqlalchemy_queries', []) + + +class Pagination(object): + """Internal helper class returned by :meth:`BaseQuery.paginate`. You + can also construct it from any other SQLAlchemy query object if you are + working with other libraries. Additionally it is possible to pass `None` + as query object in which case the :meth:`prev` and :meth:`next` will + no longer work. + """ + + def __init__(self, query, page, per_page, total, items): + #: the unlimited query object that was used to create this + #: pagination object. + self.query = query + #: the current page number (1 indexed) + self.page = page + #: the number of items to be displayed on a page. + self.per_page = per_page + #: the total number of items matching the query + self.total = total + #: the items for the current page + self.items = items + + @property + def pages(self): + """The total number of pages""" + if self.per_page == 0: + pages = 0 + else: + pages = int(ceil(self.total / float(self.per_page))) + return pages + + def prev(self, error_out=False): + """Returns a :class:`Pagination` object for the previous page.""" + assert self.query is not None, 'a query object is required ' \ + 'for this method to work' + return self.query.paginate(self.page - 1, self.per_page, error_out) + + @property + def prev_num(self): + """Number of the previous page.""" + if not self.has_prev: + return None + return self.page - 1 + + @property + def has_prev(self): + """True if a previous page exists""" + return self.page > 1 + + def next(self, error_out=False): + """Returns a :class:`Pagination` object for the next page.""" + assert self.query is not None, 'a query object is required ' \ + 'for this method to work' + return self.query.paginate(self.page + 1, self.per_page, error_out) + + @property + def has_next(self): + """True if a next page exists.""" + return self.page < self.pages + + @property + def next_num(self): + """Number of the next page""" + if not self.has_next: + return None + return self.page + 1 + + def iter_pages(self, left_edge=2, left_current=2, + right_current=5, right_edge=2): + """Iterates over the page numbers in the pagination. The four + parameters control the thresholds how many numbers should be produced + from the sides. Skipped page numbers are represented as `None`. + This is how you could render such a pagination in the templates: + + .. sourcecode:: html+jinja + + {% macro render_pagination(pagination, endpoint) %} + <div class=pagination> + {%- for page in pagination.iter_pages() %} + {% if page %} + {% if page != pagination.page %} + <a href="{{ url_for(endpoint, page=page) }}">{{ page }}</a> + {% else %} + <strong>{{ page }}</strong> + {% endif %} + {% else %} + <span class=ellipsis>…</span> + {% endif %} + {%- endfor %} + </div> + {% endmacro %} + """ + last = 0 + for num in xrange(1, self.pages + 1): + if num <= left_edge or \ + (num > self.page - left_current - 1 and \ + num < self.page + right_current) or \ + num > self.pages - right_edge: + if last + 1 != num: + yield None + yield num + last = num + + +class BaseQuery(orm.Query): + """SQLAlchemy :class:`~sqlalchemy.orm.query.Query` subclass with convenience methods for querying in a web application. + + This is the default :attr:`~Model.query` object used for models, and exposed as :attr:`~SQLAlchemy.Query`. + Override the query class for an individual model by subclassing this and setting :attr:`~Model.query_class`. + """ + + def get_or_404(self, ident): + """Like :meth:`get` but aborts with 404 if not found instead of returning ``None``.""" + + rv = self.get(ident) + if rv is None: + abort(404) + return rv + + def first_or_404(self): + """Like :meth:`first` but aborts with 404 if not found instead of returning ``None``.""" + + rv = self.first() + if rv is None: + abort(404) + return rv + + def paginate(self, page=None, per_page=None, error_out=True, max_per_page=None): + """Returns ``per_page`` items from page ``page``. + + If ``page`` or ``per_page`` are ``None``, they will be retrieved from + the request query. If ``max_per_page`` is specified, ``per_page`` will + be limited to that value. If there is no request or they aren't in the + query, they default to 1 and 20 respectively. + + When ``error_out`` is ``True`` (default), the following rules will + cause a 404 response: + + * No items are found and ``page`` is not 1. + * ``page`` is less than 1, or ``per_page`` is negative. + * ``page`` or ``per_page`` are not ints. + + When ``error_out`` is ``False``, ``page`` and ``per_page`` default to + 1 and 20 respectively. + + Returns a :class:`Pagination` object. + """ + + if request: + if page is None: + try: + page = int(request.args.get('page', 1)) + except (TypeError, ValueError): + if error_out: + abort(404) + + page = 1 + + if per_page is None: + try: + per_page = int(request.args.get('per_page', 20)) + except (TypeError, ValueError): + if error_out: + abort(404) + + per_page = 20 + else: + if page is None: + page = 1 + + if per_page is None: + per_page = 20 + + if max_per_page is not None: + per_page = min(per_page, max_per_page) + + if page < 1: + if error_out: + abort(404) + else: + page = 1 + + if per_page < 0: + if error_out: + abort(404) + else: + per_page = 20 + + items = self.limit(per_page).offset((page - 1) * per_page).all() + + if not items and page != 1 and error_out: + abort(404) + + # No need to count if we're on the first page and there are fewer + # items than we expected. + if page == 1 and len(items) < per_page: + total = len(items) + else: + total = self.order_by(None).count() + + return Pagination(self, page, per_page, total, items) + + +class _QueryProperty(object): + def __init__(self, sa): + self.sa = sa + + def __get__(self, obj, type): + try: + mapper = orm.class_mapper(type) + if mapper: + return type.query_class(mapper, session=self.sa.session()) + except UnmappedClassError: + return None + + +def _record_queries(app): + if app.debug: + return True + rq = app.config['SQLALCHEMY_RECORD_QUERIES'] + if rq is not None: + return rq + return bool(app.config.get('TESTING')) + + +class _EngineConnector(object): + + def __init__(self, sa, app, bind=None): + self._sa = sa + self._app = app + self._engine = None + self._connected_for = None + self._bind = bind + self._lock = Lock() + + def get_uri(self): + if self._bind is None: + return self._app.config['SQLALCHEMY_DATABASE_URI'] + binds = self._app.config.get('SQLALCHEMY_BINDS') or () + assert self._bind in binds, \ + 'Bind %r is not specified. Set it in the SQLALCHEMY_BINDS ' \ + 'configuration variable' % self._bind + return binds[self._bind] + + def get_engine(self): + with self._lock: + uri = self.get_uri() + echo = self._app.config['SQLALCHEMY_ECHO'] + if (uri, echo) == self._connected_for: + return self._engine + info = make_url(uri) + options = {'convert_unicode': True} + self._sa.apply_pool_defaults(self._app, options) + self._sa.apply_driver_hacks(self._app, info, options) + if echo: + options['echo'] = echo + self._engine = rv = sqlalchemy.create_engine(info, **options) + if _record_queries(self._app): + _EngineDebuggingSignalEvents(self._engine, + self._app.import_name).register() + self._connected_for = (uri, echo) + return rv + + +def get_state(app): + """Gets the state for the application""" + assert 'sqlalchemy' in app.extensions, \ + 'The sqlalchemy extension was not registered to the current ' \ + 'application. Please make sure to call init_app() first.' + return app.extensions['sqlalchemy'] + + +class _SQLAlchemyState(object): + """Remembers configuration for the (db, app) tuple.""" + + def __init__(self, db): + self.db = db + self.connectors = {} + + +class SQLAlchemy(object): + """This class is used to control the SQLAlchemy integration to one + or more Flask applications. Depending on how you initialize the + object it is usable right away or will attach as needed to a + Flask application. + + There are two usage modes which work very similarly. One is binding + the instance to a very specific Flask application:: + + app = Flask(__name__) + db = SQLAlchemy(app) + + The second possibility is to create the object once and configure the + application later to support it:: + + db = SQLAlchemy() + + def create_app(): + app = Flask(__name__) + db.init_app(app) + return app + + The difference between the two is that in the first case methods like + :meth:`create_all` and :meth:`drop_all` will work all the time but in + the second case a :meth:`flask.Flask.app_context` has to exist. + + By default Flask-SQLAlchemy will apply some backend-specific settings + to improve your experience with them. As of SQLAlchemy 0.6 SQLAlchemy + will probe the library for native unicode support. If it detects + unicode it will let the library handle that, otherwise do that itself. + Sometimes this detection can fail in which case you might want to set + ``use_native_unicode`` (or the ``SQLALCHEMY_NATIVE_UNICODE`` configuration + key) to ``False``. Note that the configuration key overrides the + value you pass to the constructor. + + This class also provides access to all the SQLAlchemy functions and classes + from the :mod:`sqlalchemy` and :mod:`sqlalchemy.orm` modules. So you can + declare models like this:: + + class User(db.Model): + username = db.Column(db.String(80), unique=True) + pw_hash = db.Column(db.String(80)) + + You can still use :mod:`sqlalchemy` and :mod:`sqlalchemy.orm` directly, but + note that Flask-SQLAlchemy customizations are available only through an + instance of this :class:`SQLAlchemy` class. Query classes default to + :class:`BaseQuery` for `db.Query`, `db.Model.query_class`, and the default + query_class for `db.relationship` and `db.backref`. If you use these + interfaces through :mod:`sqlalchemy` and :mod:`sqlalchemy.orm` directly, + the default query class will be that of :mod:`sqlalchemy`. + + .. admonition:: Check types carefully + + Don't perform type or `isinstance` checks against `db.Table`, which + emulates `Table` behavior but is not a class. `db.Table` exposes the + `Table` interface, but is a function which allows omission of metadata. + + The ``session_options`` parameter, if provided, is a dict of parameters + to be passed to the session constructor. See :class:`~sqlalchemy.orm.session.Session` + for the standard options. + + .. versionadded:: 0.10 + The `session_options` parameter was added. + + .. versionadded:: 0.16 + `scopefunc` is now accepted on `session_options`. It allows specifying + a custom function which will define the SQLAlchemy session's scoping. + + .. versionadded:: 2.1 + The `metadata` parameter was added. This allows for setting custom + naming conventions among other, non-trivial things. + + .. versionadded:: 3.0 + The `query_class` parameter was added, to allow customisation + of the query class, in place of the default of :class:`BaseQuery`. + + The `model_class` parameter was added, which allows a custom model + class to be used in place of :class:`Model`. + + .. versionchanged:: 3.0 + Utilise the same query class across `session`, `Model.query` and `Query`. + """ + + #: Default query class used by :attr:`Model.query` and other queries. + #: Customize this by passing ``query_class`` to :func:`SQLAlchemy`. + #: Defaults to :class:`BaseQuery`. + Query = None + + def __init__(self, app=None, use_native_unicode=True, session_options=None, + metadata=None, query_class=BaseQuery, model_class=Model): + + self.use_native_unicode = use_native_unicode + self.Query = query_class + self.session = self.create_scoped_session(session_options) + self.Model = self.make_declarative_base(model_class, metadata) + self._engine_lock = Lock() + self.app = app + _include_sqlalchemy(self, query_class) + + if app is not None: + self.init_app(app) + + @property + def metadata(self): + """The metadata associated with ``db.Model``.""" + + return self.Model.metadata + + def create_scoped_session(self, options=None): + """Create a :class:`~sqlalchemy.orm.scoping.scoped_session` + on the factory from :meth:`create_session`. + + An extra key ``'scopefunc'`` can be set on the ``options`` dict to + specify a custom scope function. If it's not provided, Flask's app + context stack identity is used. This will ensure that sessions are + created and removed with the request/response cycle, and should be fine + in most cases. + + :param options: dict of keyword arguments passed to session class in + ``create_session`` + """ + + if options is None: + options = {} + + scopefunc = options.pop('scopefunc', _app_ctx_stack.__ident_func__) + options.setdefault('query_cls', self.Query) + return orm.scoped_session( + self.create_session(options), scopefunc=scopefunc + ) + + def create_session(self, options): + """Create the session factory used by :meth:`create_scoped_session`. + + The factory **must** return an object that SQLAlchemy recognizes as a session, + or registering session events may raise an exception. + + Valid factories include a :class:`~sqlalchemy.orm.session.Session` + class or a :class:`~sqlalchemy.orm.session.sessionmaker`. + + The default implementation creates a ``sessionmaker`` for :class:`SignallingSession`. + + :param options: dict of keyword arguments passed to session class + """ + + return orm.sessionmaker(class_=SignallingSession, db=self, **options) + + def make_declarative_base(self, model, metadata=None): + """Creates the declarative base that all models will inherit from. + + :param model: base model class (or a tuple of base classes) to pass + to :func:`~sqlalchemy.ext.declarative.declarative_base`. Or a class + returned from ``declarative_base``, in which case a new base class + is not created. + :param: metadata: :class:`~sqlalchemy.MetaData` instance to use, or + none to use SQLAlchemy's default. + + .. versionchanged 2.3.0:: + ``model`` can be an existing declarative base in order to support + complex customization such as changing the metaclass. + """ + if not isinstance(model, DeclarativeMeta): + model = declarative_base( + cls=model, + name='Model', + metadata=metadata, + metaclass=DefaultMeta + ) + + # if user passed in a declarative base and a metaclass for some reason, + # make sure the base uses the metaclass + if metadata is not None and model.metadata is not metadata: + model.metadata = metadata + + if not getattr(model, 'query_class', None): + model.query_class = self.Query + + model.query = _QueryProperty(self) + return model + + def init_app(self, app): + """This callback can be used to initialize an application for the + use with this database setup. Never use a database in the context + of an application not initialized that way or connections will + leak. + """ + if ( + 'SQLALCHEMY_DATABASE_URI' not in app.config and + 'SQLALCHEMY_BINDS' not in app.config + ): + warnings.warn( + 'Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. ' + 'Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".' + ) + + app.config.setdefault('SQLALCHEMY_DATABASE_URI', 'sqlite:///:memory:') + app.config.setdefault('SQLALCHEMY_BINDS', None) + app.config.setdefault('SQLALCHEMY_NATIVE_UNICODE', None) + app.config.setdefault('SQLALCHEMY_ECHO', False) + app.config.setdefault('SQLALCHEMY_RECORD_QUERIES', None) + app.config.setdefault('SQLALCHEMY_POOL_SIZE', None) + app.config.setdefault('SQLALCHEMY_POOL_TIMEOUT', None) + app.config.setdefault('SQLALCHEMY_POOL_RECYCLE', None) + app.config.setdefault('SQLALCHEMY_MAX_OVERFLOW', None) + app.config.setdefault('SQLALCHEMY_COMMIT_ON_TEARDOWN', False) + track_modifications = app.config.setdefault( + 'SQLALCHEMY_TRACK_MODIFICATIONS', True + ) + + if track_modifications is None: + warnings.warn(FSADeprecationWarning( + 'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and ' + 'will be disabled by default in the future. Set it to True ' + 'or False to suppress this warning.' + )) + + app.extensions['sqlalchemy'] = _SQLAlchemyState(self) + + @app.teardown_appcontext + def shutdown_session(response_or_exc): + if app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']: + if response_or_exc is None: + self.session.commit() + + self.session.remove() + return response_or_exc + + def apply_pool_defaults(self, app, options): + def _setdefault(optionkey, configkey): + value = app.config[configkey] + if value is not None: + options[optionkey] = value + _setdefault('pool_size', 'SQLALCHEMY_POOL_SIZE') + _setdefault('pool_timeout', 'SQLALCHEMY_POOL_TIMEOUT') + _setdefault('pool_recycle', 'SQLALCHEMY_POOL_RECYCLE') + _setdefault('max_overflow', 'SQLALCHEMY_MAX_OVERFLOW') + + def apply_driver_hacks(self, app, info, options): + """This method is called before engine creation and used to inject + driver specific hacks into the options. The `options` parameter is + a dictionary of keyword arguments that will then be used to call + the :func:`sqlalchemy.create_engine` function. + + The default implementation provides some saner defaults for things + like pool sizes for MySQL and sqlite. Also it injects the setting of + `SQLALCHEMY_NATIVE_UNICODE`. + """ + if info.drivername.startswith('mysql'): + info.query.setdefault('charset', 'utf8') + if info.drivername != 'mysql+gaerdbms': + options.setdefault('pool_size', 10) + options.setdefault('pool_recycle', 7200) + elif info.drivername == 'sqlite': + pool_size = options.get('pool_size') + detected_in_memory = False + if info.database in (None, '', ':memory:'): + detected_in_memory = True + from sqlalchemy.pool import StaticPool + options['poolclass'] = StaticPool + if 'connect_args' not in options: + options['connect_args'] = {} + options['connect_args']['check_same_thread'] = False + + # we go to memory and the pool size was explicitly set + # to 0 which is fail. Let the user know that + if pool_size == 0: + raise RuntimeError('SQLite in memory database with an ' + 'empty queue not possible due to data ' + 'loss.') + # if pool size is None or explicitly set to 0 we assume the + # user did not want a queue for this sqlite connection and + # hook in the null pool. + elif not pool_size: + from sqlalchemy.pool import NullPool + options['poolclass'] = NullPool + + # if it's not an in memory database we make the path absolute. + if not detected_in_memory: + info.database = os.path.join(app.root_path, info.database) + + unu = app.config['SQLALCHEMY_NATIVE_UNICODE'] + if unu is None: + unu = self.use_native_unicode + if not unu: + options['use_native_unicode'] = False + + @property + def engine(self): + """Gives access to the engine. If the database configuration is bound + to a specific application (initialized with an application) this will + always return a database connection. If however the current application + is used this might raise a :exc:`RuntimeError` if no application is + active at the moment. + """ + return self.get_engine() + + def make_connector(self, app=None, bind=None): + """Creates the connector for a given state and bind.""" + return _EngineConnector(self, self.get_app(app), bind) + + def get_engine(self, app=None, bind=None): + """Returns a specific engine.""" + + app = self.get_app(app) + state = get_state(app) + + with self._engine_lock: + connector = state.connectors.get(bind) + + if connector is None: + connector = self.make_connector(app, bind) + state.connectors[bind] = connector + + return connector.get_engine() + + def get_app(self, reference_app=None): + """Helper method that implements the logic to look up an + application.""" + + if reference_app is not None: + return reference_app + + if current_app: + return current_app._get_current_object() + + if self.app is not None: + return self.app + + raise RuntimeError( + 'No application found. Either work inside a view function or push' + ' an application context. See' + ' http://flask-sqlalchemy.pocoo.org/contexts/.' + ) + + def get_tables_for_bind(self, bind=None): + """Returns a list of all tables relevant for a bind.""" + result = [] + for table in itervalues(self.Model.metadata.tables): + if table.info.get('bind_key') == bind: + result.append(table) + return result + + def get_binds(self, app=None): + """Returns a dictionary with a table->engine mapping. + + This is suitable for use of sessionmaker(binds=db.get_binds(app)). + """ + app = self.get_app(app) + binds = [None] + list(app.config.get('SQLALCHEMY_BINDS') or ()) + retval = {} + for bind in binds: + engine = self.get_engine(app, bind) + tables = self.get_tables_for_bind(bind) + retval.update(dict((table, engine) for table in tables)) + return retval + + def _execute_for_all_tables(self, app, bind, operation, skip_tables=False): + app = self.get_app(app) + + if bind == '__all__': + binds = [None] + list(app.config.get('SQLALCHEMY_BINDS') or ()) + elif isinstance(bind, string_types) or bind is None: + binds = [bind] + else: + binds = bind + + for bind in binds: + extra = {} + if not skip_tables: + tables = self.get_tables_for_bind(bind) + extra['tables'] = tables + op = getattr(self.Model.metadata, operation) + op(bind=self.get_engine(app, bind), **extra) + + def create_all(self, bind='__all__', app=None): + """Creates all tables. + + .. versionchanged:: 0.12 + Parameters were added + """ + self._execute_for_all_tables(app, bind, 'create_all') + + def drop_all(self, bind='__all__', app=None): + """Drops all tables. + + .. versionchanged:: 0.12 + Parameters were added + """ + self._execute_for_all_tables(app, bind, 'drop_all') + + def reflect(self, bind='__all__', app=None): + """Reflects tables from the database. + + .. versionchanged:: 0.12 + Parameters were added + """ + self._execute_for_all_tables(app, bind, 'reflect', skip_tables=True) + + def __repr__(self): + return '<%s engine=%r>' % ( + self.__class__.__name__, + self.engine.url if self.app or current_app else None + ) + + +class _BoundDeclarativeMeta(DefaultMeta): + def __init__(cls, name, bases, d): + warnings.warn(FSADeprecationWarning( + '"_BoundDeclarativeMeta" has been renamed to "DefaultMeta". The' + ' old name will be removed in 3.0.' + ), stacklevel=3) + super(_BoundDeclarativeMeta, cls).__init__(name, bases, d) + + +class FSADeprecationWarning(DeprecationWarning): + pass + + +warnings.simplefilter('always', FSADeprecationWarning) diff --git a/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d9107904531c4910942245e0f406eea55823639 GIT binary patch literal 33367 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 RDnIpS68v{?YHTX~e*n`Yh#CL@ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/_compat.cpython-36.pyc b/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/_compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e96f59f22ee945d29df79a7c72f1dea78c37c277 GIT binary patch literal 1388 zcmbtT&2G~`5cb-Rle+1z013Dl3DibvQfN7#2qDz8NR^<fRB<4(WI5hV;>KBr-Axor z5tsH+xbOfx0cT!dublb{oS3mg)m9)tj5OcQcxQLN{dT;&wB$}d9|jL>!}w-gF$?0C z;F1GSqis+^&4jcGBc~=McMWO{%(lfW@rv5CbZ)e5>d-QbCFZmpR%Vs1*<PS4)TN8( zr0w#Dv^uX_n%6BtUA0S~YLzY*Rn$B&YHQ=KaH#8nb(2sI0{K1(lc>*zStE$j;V4x3 z<cHswo6jC{#RLx%?>JNalzTgk>Gp18>J=Nroj8eA<|SdqM59>IjM7mi;$B}hz0IiR z?L2<6>vb}3Kjbk>ydyU5fB86;Oyv7FlQ?2rvZl9pvcKg?#=O^ut%IYJ1FxG3Z<q?^ zQKrH;kq!3(E@T{Cn1B(%7z{`B7Pzbc%#2gx%m4w%QH|(@82Ez^B_xD?GDHb#zV}*9 z|1EHkezqYuGJl`Rfl5dIUYZW-Z$cjS*pP8mKglGltq(&Ogg;??;wN#(m#|}f6h;H6 zlm1P4{kgmi&;dJO4XGh%gXH<&a(9vYBuvH($P4JLf_#1h;b2+@xCQ=mcg(dtR^4aX zkxImoijV*rj_56L`4nIV4LPXDbp??kXQsF_1J&}(glr3Rv}R-mF?=O6<BSxV+S-j} z%%uu>#Iyy+=<=AyQA(M17{{?@X)@L2s2>W+6nqsT6|&%s^5^8WqtXCY-$z0&fI-%Y zZMvjt3RLWxN~m7Wyb1wwJSAtBj7)8#Qo1yM=U+;NcYg#vm+BvO7wfQ0JAZtL%`3a( zX=pg2x4{2Tb%7FzMF3suWQs}cP*#X7mT_tYVHF{l_)_Ow%x(C7L*$w%@OKdl0N3Ir zT@C`8MnM3tn@L@rz<&~_JcxNW)z<O5oxF?l=F`qpguKTDzKggGpsUz;ycei!#AJTB qE)_LM{tCHEhPv{6n9^~=Uf><&Jph|j%w-GEs#vyJF{^G7Ipz<7Jum+N literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/model.cpython-36.pyc b/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/model.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..103d8de46467d9ee5e9438574e114bcd2cdef54b GIT binary patch literal 4629 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_sqlalchemy/_compat.py b/venv/Lib/site-packages/flask_sqlalchemy/_compat.py new file mode 100644 index 0000000..f1786a1 --- /dev/null +++ b/venv/Lib/site-packages/flask_sqlalchemy/_compat.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +""" + flask_sqlalchemy._compat + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Internal Python 2.x/3.x compatibility layer. + + :copyright: (c) 2013 by Daniel Neuhäuser + :license: BSD, see LICENSE for more details. +""" +import sys + +PY2 = sys.version_info[0] == 2 + + +if PY2: + def iteritems(d): + return d.iteritems() + + def itervalues(d): + return d.itervalues() + + xrange = xrange + + string_types = (unicode, bytes) + + def to_str(x, charset='utf8', errors='strict'): + if x is None or isinstance(x, str): + return x + + if isinstance(x, unicode): + return x.encode(charset, errors) + + return str(x) + +else: + def iteritems(d): + return iter(d.items()) + + def itervalues(d): + return iter(d.values()) + + xrange = range + + string_types = (str,) + + def to_str(x, charset='utf8', errors='strict'): + if x is None or isinstance(x, str): + return x + + if isinstance(x, bytes): + return x.decode(charset, errors) + + return str(x) diff --git a/venv/Lib/site-packages/flask_sqlalchemy/model.py b/venv/Lib/site-packages/flask_sqlalchemy/model.py new file mode 100644 index 0000000..9c55db2 --- /dev/null +++ b/venv/Lib/site-packages/flask_sqlalchemy/model.py @@ -0,0 +1,154 @@ +import re + +import sqlalchemy as sa +from sqlalchemy import inspect +from sqlalchemy.ext.declarative import DeclarativeMeta, declared_attr +from sqlalchemy.schema import _get_table_key + +from ._compat import to_str + + +def should_set_tablename(cls): + """Determine whether ``__tablename__`` should be automatically generated + for a model. + + * If no class in the MRO sets a name, one should be generated. + * If a declared attr is found, it should be used instead. + * If a name is found, it should be used if the class is a mixin, otherwise + one should be generated. + * Abstract models should not have one generated. + + Later, :meth:`._BoundDeclarativeMeta.__table_cls__` will determine if the + model looks like single or joined-table inheritance. If no primary key is + found, the name will be unset. + """ + if ( + cls.__dict__.get('__abstract__', False) + or not any(isinstance(b, DeclarativeMeta) for b in cls.__mro__[1:]) + ): + return False + + for base in cls.__mro__: + if '__tablename__' not in base.__dict__: + continue + + if isinstance(base.__dict__['__tablename__'], declared_attr): + return False + + return not ( + base is cls + or base.__dict__.get('__abstract__', False) + or not isinstance(base, DeclarativeMeta) + ) + + return True + + +camelcase_re = re.compile(r'([A-Z]+)(?=[a-z0-9])') + + +def camel_to_snake_case(name): + def _join(match): + word = match.group() + + if len(word) > 1: + return ('_%s_%s' % (word[:-1], word[-1])).lower() + + return '_' + word.lower() + + return camelcase_re.sub(_join, name).lstrip('_') + + +class NameMetaMixin(object): + def __init__(cls, name, bases, d): + if should_set_tablename(cls): + cls.__tablename__ = camel_to_snake_case(cls.__name__) + + super(NameMetaMixin, cls).__init__(name, bases, d) + + # __table_cls__ has run at this point + # if no table was created, use the parent table + if ( + '__tablename__' not in cls.__dict__ + and '__table__' in cls.__dict__ + and cls.__dict__['__table__'] is None + ): + del cls.__table__ + + def __table_cls__(cls, *args, **kwargs): + """This is called by SQLAlchemy during mapper setup. It determines the + final table object that the model will use. + + If no primary key is found, that indicates single-table inheritance, + so no table will be created and ``__tablename__`` will be unset. + """ + # check if a table with this name already exists + # allows reflected tables to be applied to model by name + key = _get_table_key(args[0], kwargs.get('schema')) + + if key in cls.metadata.tables: + return sa.Table(*args, **kwargs) + + # if a primary key or constraint is found, create a table for + # joined-table inheritance + for arg in args: + if ( + (isinstance(arg, sa.Column) and arg.primary_key) + or isinstance(arg, sa.PrimaryKeyConstraint) + ): + return sa.Table(*args, **kwargs) + + # if no base classes define a table, return one + # ensures the correct error shows up when missing a primary key + for base in cls.__mro__[1:-1]: + if '__table__' in base.__dict__: + break + else: + return sa.Table(*args, **kwargs) + + # single-table inheritance, use the parent tablename + if '__tablename__' in cls.__dict__: + del cls.__tablename__ + + +class BindMetaMixin(object): + def __init__(cls, name, bases, d): + bind_key = ( + d.pop('__bind_key__', None) + or getattr(cls, '__bind_key__', None) + ) + + super(BindMetaMixin, cls).__init__(name, bases, d) + + if bind_key is not None and hasattr(cls, '__table__'): + cls.__table__.info['bind_key'] = bind_key + + +class DefaultMeta(NameMetaMixin, BindMetaMixin, DeclarativeMeta): + pass + + +class Model(object): + """Base class for SQLAlchemy declarative base model. + + To define models, subclass :attr:`db.Model <SQLAlchemy.Model>`, not this + class. To customize ``db.Model``, subclass this and pass it as + ``model_class`` to :class:`SQLAlchemy`. + """ + + #: Query class used by :attr:`query`. Defaults to + # :class:`SQLAlchemy.Query`, which defaults to :class:`BaseQuery`. + query_class = None + + #: Convenience property to query the database for instances of this model + # using the current session. Equivalent to ``db.session.query(Model)`` + # unless :attr:`query_class` has been changed. + query = None + + def __repr__(self): + identity = inspect(self).identity + if identity is None: + pk = "(transient {0})".format(id(self)) + else: + pk = ', '.join(to_str(value) for value in identity) + return '<{0} {1}>'.format(type(self).__name__, pk) diff --git a/venv/Lib/site-packages/flask_wtf/__init__.py b/venv/Lib/site-packages/flask_wtf/__init__.py new file mode 100644 index 0000000..30ec506 --- /dev/null +++ b/venv/Lib/site-packages/flask_wtf/__init__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +""" + flask_wtf + ~~~~~~~~~ + + Flask-WTF extension + + :copyright: (c) 2010 by Dan Jacob. + :copyright: (c) 2013 - 2015 by Hsiaoming Yang. + :license: BSD, see LICENSE for more details. +""" +# flake8: noqa +from __future__ import absolute_import + +from .csrf import CSRFProtect, CsrfProtect +from .form import FlaskForm, Form +from .recaptcha import * + +__version__ = '0.14.2' diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..288610de0d9c8cac4cf5c7d8a26f6dd473d37ec3 GIT binary patch literal 614 zcmZuu(QeZ)6m_z+NmDBJ34R+vB<ePJsV0Qhu3`u#M1~MiB+E^l&Z-?pwp*4x>@)Tc zd;mYdXXKTq{Q^(eVIUs3l8=3K&b`;ZKAud%>u=}i&H>;D_$OR*er1-jP=XAUPU$j- zyP4~Na<BB*9``bj53)he2W-gw%;!NC^nA!hm!R~sunbZVjc&e&&_u<s+MqAG>e=m| z2z$D;V*KfH3CXo4wW6Z_EzAnhw34o`^b9^JB6u`CoWfNL7r2IRa3NMnpZ_1*F^sMI zg>}DH6bnx4HT;b0^+O7yh3Q0Q@HAaKgNhLN_I!SJk)FXyNXUgGup}B&rjqd9Oaeqs z_ZY8~U|UTP<&BW~-pmd}Za1E%AC~W>(4^3YC|NxaqmBB<ErsNrXVH{#p56?n$>H<l z=pq_-K0>7^5b8pNs;%Bif{?VIka>lvBt3JK0|T9BZpP)%a^LDBNebNP;tF?Tgmy&Q z!6Eb&$cgn>_=)GU{G&2b-X5rfHeV36(W1#uh1kUJaE;f5lUm1Vt2E&;$7YlHj?}xH w(N(UfCUJv{jq$1c?<eL6(VA+6lBVr~6E4avBQM9Mp$ZLn?jv{K*>?i>7a!iQGynhq literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/_compat.cpython-36.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/_compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c96f85e3d47b4b61280554cda9f0d98da46e068 GIT binary patch literal 1095 zcmZuw&2G~`5Z<-(vvHj?AS6nKP)<}iqzAa53ZaTtMI5L=Evm3A88<tw+}LZgyJ?#! zoYE^dUV;bUW%kOcufT~}yOc_FrI}gp-+bRU<LBM3ck%OW^uZ<M7r8Z>INwH7-_dcx zX-0xN(trXvbvSuMxHEAA7hJUkji3pQga$1<>t^ko1&k801|7Q&oy57`Vf7Ag@a826 zJif}?oL$mj<qhE-?p+e@fxmQvF3{5+UxDt@p#<lv+=njj^4>R>BANp->|Y@=vVY~E zA5z1nGAm=LU`*{g4jYjc*+Ell=rmanO_z>*u&@N^l77V%e9D=xAIey$q>%YQX_<<X zfi4EeGYx8U-kj=W^kV25FIA~fIu>Iv9Ss*c(z6mw3&gnKfEXHf@duF%ll}g@vlV_+ zAXPZqP#d#w7t}--W%#-%CZl(;h)*B~p+^TZr6C{XF$Ni)fjA4Z^f*+hhEW-hCwNYU zNfxU~bgq*y8W(vP>&<dzm@c9UW8X4tp(Bn*Jt{q$zlJ@w^4MYHooc8`jm1AT==|v} zSVn4)igK#!999rZZ;&Pf0l<N84qe&6SB+mwR;i&(i&vM2POx(8x?z<{sBEQ9?K<|h zhb*QdwFw{M_iY}l_&r9u8MdU`Xv#+iB%quGSV&t!v%Y6FIy&6jg;K&e)@dP*Vkt1? zJ{FHfk%)7MBI89-UhrvV=YAA@nZ{YYa|@ZRptSI$)%FRRvH~(%eVbOCoyRlenx2S4 z!u%dq275;>Zt8@97-E2vLe5O9&NrlT1s@sqjjQl7JFn3x<DX%hAlxVwNnzagpPu0f zEv5YlrKM_o>~8Fe&8owtt)ppQi&j-zTB*$a|Ebq_eU;{A21%M>kC|Q_|E9l-wqY-8 e%&)BE>M=S-nbUI`v_V_cMn-*?wY_$~;nP3Xi5|`X literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/csrf.cpython-36.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/csrf.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..70dc475ff00e8cc29c276536c7a1a80ba62b29aa GIT binary patch literal 11075 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/form.cpython-36.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/form.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c9ee1875ea61baf685053fd1e94d35d7f1f96c5 GIT binary patch literal 5739 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 HaNYj`c`lL* literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/i18n.cpython-36.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/i18n.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3f3af0ce33d08c2a67bd379e0ead181627a0aa2 GIT binary patch literal 1777 zcmZuxUvC>l5Z~Q9pU)RNbwk=hf$GEq#Sbwpl~5HSRFP7M1bLuAZBIt4%iY>J`~JAQ z>o$%qeQ|l@YasCf_%i#-Q@;Wah?%`4I1+oBz1f-l_nZ06KIrv=lRx+QKOP}}k+rrB z`XdnaJ9H_DNJ>+d(}G2eqVD8Q;YKbc%RY?Uyj6ISN69l%hAt89Ss;I)1V8Kb8}AEJ zvggf;R^BOsD7doX&E<+#zR`G_6=^r>rheLaOQPO)Bn?vUElEAGxpJaw;@XNt{dD6g z8T3wn4MJ$+JXSOQT91d>{fDJBy}8l?YuhiikY%j1s*LmO)GDD`%;%NV;kc6F6Wk|! zcKBr2EZ9w|`BG++W4#-GmJGr#?%jV79xcP~RTfu8R!+hfaXA?V?;vDkK+P7H;^jgp z%@cjXm5!4cSlj1EfErAM=6RLGxqwlx5K6@pq4+%3$EJHI<5K1JTFP|w+9<&TusI+t zkUbDJf=-huJtH+ledf!pls+XXtI3p2ompFcf@AH1*GcKhIioeT^PcP0ikva+P1{qy zX1cQ?6Ub@W`pwR%2@h&mIq**7pM+k>Qib|hgh?f(NOTAwv^de>`VEIR!g~;}a4f5$ zNy*Mpd?fPlb*7I)RTUyEsx%vCNdvK2a$s6^mf42<RkBcgeOuE$w?eqQv?}7P9BfDi zz>TA;xpCt8+yrnLQ7TBQGHtkAthJOFC5hpZz;t5Vyoyul;<y9dz%dTwNVd_}PBWlI zA^srpJsAJ}%iiwjXC<T>E$^zk%h6+@W^mZ(o2r`a{1}&zvO<)4=jl>uQS1~k{N(7B zC|`~8>}aGiEq3ORdra-<lA9ys=5W3=*EzCa9p!B}RlN@#aXso&mvyO2yP$dS^_fTI z4e(u(04HJy9)YNjpc5pbP_hh4k3q>ekZu?LkWfsb4M2hmuwh70_CVA<=rlnt)GQ^) zAk4d_Frq6aKdsr6)pP|m(40maumj^S&N(n}?bS^CI94|7H(^Jc1Y`(A*;OJR8&7Eo z<zlF|5pFtsaU`Pt97J`YBLElR(|`u-bo+3v0EXx1C73~Mm|oN^jK`o@7;f!a7<XzQ z{EW%l2&ZNilAAJs^kwZLq?*;v$^oB?ur5Xe=inWMy-a6OUIi#`fmwcxKL66yk{?)u z0%(gomd2as3mNBzmhwZKk8E1QdIyFNK~x{Q1}ma<SwJ`0>5VIRFAg~vw2aR=(vx!& za1JH3$WiZd{&ErLjmO4O;sI-i+T(4Mu>^)73Pa{ozjH8PmKL@E2G>oS^R!A}*Ouk0 ztuX21cF2`zqhYZvR9i%5W52*>^IIt^8THoO;>2{%Yu3KTwh*J67bakZH4hg$%b{F3 wkMo@KhDBa=G=&G|znlFZ7~_A{#NcZzWXpdnm@Z|sPdDk-#ouuqf4hI{Uw8<?H~;_u literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_wtf/_compat.py b/venv/Lib/site-packages/flask_wtf/_compat.py new file mode 100644 index 0000000..e5956e0 --- /dev/null +++ b/venv/Lib/site-packages/flask_wtf/_compat.py @@ -0,0 +1,35 @@ +import sys +import warnings + +PY2 = sys.version_info[0] == 2 + +if not PY2: + text_type = str + string_types = (str,) + from urllib.parse import urlparse +else: + text_type = unicode + string_types = (str, unicode) + from urlparse import urlparse + + +def to_bytes(text): + """Transform string to bytes.""" + if isinstance(text, text_type): + text = text.encode('utf-8') + return text + + +def to_unicode(input_bytes, encoding='utf-8'): + """Decodes input_bytes to text if needed.""" + if not isinstance(input_bytes, string_types): + input_bytes = input_bytes.decode(encoding) + return input_bytes + + +class FlaskWTFDeprecationWarning(DeprecationWarning): + pass + + +warnings.simplefilter('always', FlaskWTFDeprecationWarning) +warnings.filterwarnings('ignore', category=FlaskWTFDeprecationWarning, module='wtforms|flask_wtf') diff --git a/venv/Lib/site-packages/flask_wtf/csrf.py b/venv/Lib/site-packages/flask_wtf/csrf.py new file mode 100644 index 0000000..7ca569b --- /dev/null +++ b/venv/Lib/site-packages/flask_wtf/csrf.py @@ -0,0 +1,364 @@ +import hashlib +import logging +import os +import warnings +from functools import wraps + +from flask import Blueprint, current_app, g, request, session +from itsdangerous import BadData, SignatureExpired, URLSafeTimedSerializer +from werkzeug.exceptions import BadRequest +from werkzeug.security import safe_str_cmp +from wtforms import ValidationError +from wtforms.csrf.core import CSRF + +from ._compat import FlaskWTFDeprecationWarning, string_types, urlparse + +__all__ = ('generate_csrf', 'validate_csrf', 'CSRFProtect') +logger = logging.getLogger(__name__) + + +def generate_csrf(secret_key=None, token_key=None): + """Generate a CSRF token. The token is cached for a request, so multiple + calls to this function will generate the same token. + + During testing, it might be useful to access the signed token in + ``g.csrf_token`` and the raw token in ``session['csrf_token']``. + + :param secret_key: Used to securely sign the token. Default is + ``WTF_CSRF_SECRET_KEY`` or ``SECRET_KEY``. + :param token_key: Key where token is stored in session for comparision. + Default is ``WTF_CSRF_FIELD_NAME`` or ``'csrf_token'``. + """ + + secret_key = _get_config( + secret_key, 'WTF_CSRF_SECRET_KEY', current_app.secret_key, + message='A secret key is required to use CSRF.' + ) + field_name = _get_config( + token_key, 'WTF_CSRF_FIELD_NAME', 'csrf_token', + message='A field name is required to use CSRF.' + ) + + if field_name not in g: + if field_name not in session: + session[field_name] = hashlib.sha1(os.urandom(64)).hexdigest() + + s = URLSafeTimedSerializer(secret_key, salt='wtf-csrf-token') + setattr(g, field_name, s.dumps(session[field_name])) + + return g.get(field_name) + + +def validate_csrf(data, secret_key=None, time_limit=None, token_key=None): + """Check if the given data is a valid CSRF token. This compares the given + signed token to the one stored in the session. + + :param data: The signed CSRF token to be checked. + :param secret_key: Used to securely sign the token. Default is + ``WTF_CSRF_SECRET_KEY`` or ``SECRET_KEY``. + :param time_limit: Number of seconds that the token is valid. Default is + ``WTF_CSRF_TIME_LIMIT`` or 3600 seconds (60 minutes). + :param token_key: Key where token is stored in session for comparision. + Default is ``WTF_CSRF_FIELD_NAME`` or ``'csrf_token'``. + + :raises ValidationError: Contains the reason that validation failed. + + .. versionchanged:: 0.14 + Raises ``ValidationError`` with a specific error message rather than + returning ``True`` or ``False``. + """ + + secret_key = _get_config( + secret_key, 'WTF_CSRF_SECRET_KEY', current_app.secret_key, + message='A secret key is required to use CSRF.' + ) + field_name = _get_config( + token_key, 'WTF_CSRF_FIELD_NAME', 'csrf_token', + message='A field name is required to use CSRF.' + ) + time_limit = _get_config( + time_limit, 'WTF_CSRF_TIME_LIMIT', 3600, required=False + ) + + if not data: + raise ValidationError('The CSRF token is missing.') + + if field_name not in session: + raise ValidationError('The CSRF session token is missing.') + + s = URLSafeTimedSerializer(secret_key, salt='wtf-csrf-token') + + try: + token = s.loads(data, max_age=time_limit) + except SignatureExpired: + raise ValidationError('The CSRF token has expired.') + except BadData: + raise ValidationError('The CSRF token is invalid.') + + if not safe_str_cmp(session[field_name], token): + raise ValidationError('The CSRF tokens do not match.') + + +def _get_config( + value, config_name, default=None, + required=True, message='CSRF is not configured.' +): + """Find config value based on provided value, Flask config, and default + value. + + :param value: already provided config value + :param config_name: Flask ``config`` key + :param default: default value if not provided or configured + :param required: whether the value must not be ``None`` + :param message: error message if required config is not found + :raises KeyError: if required config is not found + """ + + if value is None: + value = current_app.config.get(config_name, default) + + if required and value is None: + raise KeyError(message) + + return value + + +class _FlaskFormCSRF(CSRF): + def setup_form(self, form): + self.meta = form.meta + return super(_FlaskFormCSRF, self).setup_form(form) + + def generate_csrf_token(self, csrf_token_field): + return generate_csrf( + secret_key=self.meta.csrf_secret, + token_key=self.meta.csrf_field_name + ) + + def validate_csrf_token(self, form, field): + if g.get('csrf_valid', False): + # already validated by CSRFProtect + return + + try: + validate_csrf( + field.data, + self.meta.csrf_secret, + self.meta.csrf_time_limit, + self.meta.csrf_field_name + ) + except ValidationError as e: + logger.info(e.args[0]) + raise + + +class CSRFProtect(object): + """Enable CSRF protection globally for a Flask app. + + :: + + app = Flask(__name__) + csrf = CsrfProtect(app) + + Checks the ``csrf_token`` field sent with forms, or the ``X-CSRFToken`` + header sent with JavaScript requests. Render the token in templates using + ``{{ csrf_token() }}``. + + See the :ref:`csrf` documentation. + """ + + def __init__(self, app=None): + self._exempt_views = set() + self._exempt_blueprints = set() + + if app: + self.init_app(app) + + def init_app(self, app): + app.extensions['csrf'] = self + + app.config.setdefault('WTF_CSRF_ENABLED', True) + app.config.setdefault('WTF_CSRF_CHECK_DEFAULT', True) + app.config['WTF_CSRF_METHODS'] = set(app.config.get( + 'WTF_CSRF_METHODS', ['POST', 'PUT', 'PATCH', 'DELETE'] + )) + app.config.setdefault('WTF_CSRF_FIELD_NAME', 'csrf_token') + app.config.setdefault( + 'WTF_CSRF_HEADERS', ['X-CSRFToken', 'X-CSRF-Token'] + ) + app.config.setdefault('WTF_CSRF_TIME_LIMIT', 3600) + app.config.setdefault('WTF_CSRF_SSL_STRICT', True) + + app.jinja_env.globals['csrf_token'] = generate_csrf + app.context_processor(lambda: {'csrf_token': generate_csrf}) + + @app.before_request + def csrf_protect(): + if not app.config['WTF_CSRF_ENABLED']: + return + + if not app.config['WTF_CSRF_CHECK_DEFAULT']: + return + + if request.method not in app.config['WTF_CSRF_METHODS']: + return + + if not request.endpoint: + return + + view = app.view_functions.get(request.endpoint) + + if not view: + return + + if request.blueprint in self._exempt_blueprints: + return + + dest = '%s.%s' % (view.__module__, view.__name__) + + if dest in self._exempt_views: + return + + self.protect() + + def _get_csrf_token(self): + # find the ``csrf_token`` field in the subitted form + # if the form had a prefix, the name will be + # ``{prefix}-csrf_token`` + field_name = current_app.config['WTF_CSRF_FIELD_NAME'] + + for key in request.form: + if key.endswith(field_name): + csrf_token = request.form[key] + + if csrf_token: + return csrf_token + + for header_name in current_app.config['WTF_CSRF_HEADERS']: + csrf_token = request.headers.get(header_name) + + if csrf_token: + return csrf_token + + return None + + def protect(self): + if request.method not in current_app.config['WTF_CSRF_METHODS']: + return + + try: + validate_csrf(self._get_csrf_token()) + except ValidationError as e: + logger.info(e.args[0]) + self._error_response(e.args[0]) + + if request.is_secure and current_app.config['WTF_CSRF_SSL_STRICT']: + if not request.referrer: + self._error_response('The referrer header is missing.') + + good_referrer = 'https://{0}/'.format(request.host) + + if not same_origin(request.referrer, good_referrer): + self._error_response('The referrer does not match the host.') + + g.csrf_valid = True # mark this request as CSRF valid + + def exempt(self, view): + """Mark a view or blueprint to be excluded from CSRF protection. + + :: + + @app.route('/some-view', methods=['POST']) + @csrf.exempt + def some_view(): + ... + + :: + + bp = Blueprint(...) + csrf.exempt(bp) + + """ + + if isinstance(view, Blueprint): + self._exempt_blueprints.add(view.name) + return view + + if isinstance(view, string_types): + view_location = view + else: + view_location = '%s.%s' % (view.__module__, view.__name__) + + self._exempt_views.add(view_location) + return view + + def _error_response(self, reason): + raise CSRFError(reason) + + def error_handler(self, view): + """Register a function that will generate the response for CSRF errors. + + .. deprecated:: 0.14 + Use the standard Flask error system with + ``@app.errorhandler(CSRFError)`` instead. This will be removed in + version 1.0. + + The function will be passed one argument, ``reason``. By default it will + raise a :class:`~flask_wtf.csrf.CSRFError`. :: + + @csrf.error_handler + def csrf_error(reason): + return render_template('error.html', reason=reason) + + Due to historical reasons, the function may either return a response + or raise an exception with :func:`flask.abort`. + """ + + warnings.warn(FlaskWTFDeprecationWarning( + '"@csrf.error_handler" is deprecated. Use the standard Flask error ' + 'system with "@app.errorhandler(CSRFError)" instead. This will be' + 'removed in 1.0.' + ), stacklevel=2) + + @wraps(view) + def handler(reason): + response = current_app.make_response(view(reason)) + raise CSRFError(response.get_data(as_text=True), response=response) + + self._error_response = handler + return view + + +class CsrfProtect(CSRFProtect): + """ + .. deprecated:: 0.14 + Renamed to :class:`~flask_wtf.csrf.CSRFProtect`. + """ + + def __init__(self, app=None): + warnings.warn(FlaskWTFDeprecationWarning( + '"flask_wtf.CsrfProtect" has been renamed to "CSRFProtect" ' + 'and will be removed in 1.0.' + ), stacklevel=2) + super(CsrfProtect, self).__init__(app=app) + + +class CSRFError(BadRequest): + """Raise if the client sends invalid CSRF data with the request. + + Generates a 400 Bad Request response with the failure reason by default. + Customize the response by registering a handler with + :meth:`flask.Flask.errorhandler`. + """ + + description = 'CSRF validation failed.' + + +def same_origin(current_uri, compare_uri): + current = urlparse(current_uri) + compare = urlparse(compare_uri) + + return ( + current.scheme == compare.scheme + and current.hostname == compare.hostname + and current.port == compare.port + ) diff --git a/venv/Lib/site-packages/flask_wtf/file.py b/venv/Lib/site-packages/flask_wtf/file.py new file mode 100644 index 0000000..e5851f5 --- /dev/null +++ b/venv/Lib/site-packages/flask_wtf/file.py @@ -0,0 +1,94 @@ +import warnings +from collections import Iterable + +from werkzeug.datastructures import FileStorage +from wtforms import FileField as _FileField +from wtforms.validators import DataRequired, StopValidation + +from ._compat import FlaskWTFDeprecationWarning + + +class FileField(_FileField): + """Werkzeug-aware subclass of :class:`wtforms.fields.FileField`.""" + + def process_formdata(self, valuelist): + valuelist = (x for x in valuelist if isinstance(x, FileStorage) and x) + data = next(valuelist, None) + + if data is not None: + self.data = data + else: + self.raw_data = () + + def has_file(self): + """Return ``True`` if ``self.data`` is a + :class:`~werkzeug.datastructures.FileStorage` object. + + .. deprecated:: 0.14.1 + ``data`` is no longer set if the input is not a non-empty + ``FileStorage``. Check ``form.data is not None`` instead. + """ + + warnings.warn(FlaskWTFDeprecationWarning( + '"has_file" is deprecated and will be removed in 1.0. The data is ' + 'checked during processing instead.' + )) + return bool(self.data) + + +class FileRequired(DataRequired): + """Validates that the data is a Werkzeug + :class:`~werkzeug.datastructures.FileStorage` object. + + :param message: error message + + You can also use the synonym ``file_required``. + """ + + def __call__(self, form, field): + if not (isinstance(field.data, FileStorage) and field.data): + if self.message is None: + message = field.gettext('This field is required.') + else: + message = self.message + + raise StopValidation(message) + +file_required = FileRequired + + +class FileAllowed(object): + """Validates that the uploaded file is allowed by a given list of + extensions or a Flask-Uploads :class:`~flaskext.uploads.UploadSet`. + + :param upload_set: A list of extensions or an + :class:`~flaskext.uploads.UploadSet` + :param message: error message + + You can also use the synonym ``file_allowed``. + """ + + def __init__(self, upload_set, message=None): + self.upload_set = upload_set + self.message = message + + def __call__(self, form, field): + if not (isinstance(field.data, FileStorage) and field.data): + return + + filename = field.data.filename.lower() + + if isinstance(self.upload_set, Iterable): + if any(filename.endswith('.' + x) for x in self.upload_set): + return + + raise StopValidation(self.message or field.gettext( + 'File does not have an approved extension: {extensions}' + ).format(extensions=', '.join(self.upload_set))) + + if not self.upload_set.file_allowed(field.data, filename): + raise StopValidation(self.message or field.gettext( + 'File does not have an approved extension.' + )) + +file_allowed = FileAllowed diff --git a/venv/Lib/site-packages/flask_wtf/form.py b/venv/Lib/site-packages/flask_wtf/form.py new file mode 100644 index 0000000..b8a3d96 --- /dev/null +++ b/venv/Lib/site-packages/flask_wtf/form.py @@ -0,0 +1,158 @@ +import warnings + +from flask import current_app, request +from flask import session +from jinja2 import Markup +from werkzeug.datastructures import CombinedMultiDict, ImmutableMultiDict +from werkzeug.utils import cached_property +from wtforms import Form +from wtforms.meta import DefaultMeta +from wtforms.widgets import HiddenInput + +from ._compat import FlaskWTFDeprecationWarning, string_types, text_type +from .csrf import _FlaskFormCSRF + +try: + from .i18n import translations +except ImportError: + translations = None # babel not installed + + +SUBMIT_METHODS = set(('POST', 'PUT', 'PATCH', 'DELETE')) +_Auto = object() + + +class FlaskForm(Form): + """Flask-specific subclass of WTForms :class:`~wtforms.form.Form`. + + If ``formdata`` is not specified, this will use :attr:`flask.request.form` + and :attr:`flask.request.files`. Explicitly pass ``formdata=None`` to + prevent this. + """ + + class Meta(DefaultMeta): + csrf_class = _FlaskFormCSRF + csrf_context = session # not used, provided for custom csrf_class + + @cached_property + def csrf(self): + return current_app.config.get('WTF_CSRF_ENABLED', True) + + @cached_property + def csrf_secret(self): + return current_app.config.get( + 'WTF_CSRF_SECRET_KEY', current_app.secret_key + ) + + @cached_property + def csrf_field_name(self): + return current_app.config.get('WTF_CSRF_FIELD_NAME', 'csrf_token') + + @cached_property + def csrf_time_limit(self): + return current_app.config.get('WTF_CSRF_TIME_LIMIT', 3600) + + def wrap_formdata(self, form, formdata): + if formdata is _Auto: + if _is_submitted(): + if request.files: + return CombinedMultiDict(( + request.files, request.form + )) + elif request.form: + return request.form + elif request.get_json(): + return ImmutableMultiDict(request.get_json()) + + return None + + return formdata + + def get_translations(self, form): + if not current_app.config.get('WTF_I18N_ENABLED', True): + return None + + return translations + + def __init__(self, formdata=_Auto, **kwargs): + csrf_enabled = kwargs.pop('csrf_enabled', None) + + if csrf_enabled is not None: + warnings.warn(FlaskWTFDeprecationWarning( + '"csrf_enabled" is deprecated and will be removed in 1.0. ' + 'Set "meta.csrf" instead.' + ), stacklevel=3) + kwargs['meta'] = kwargs.get('meta') or {} + kwargs['meta'].setdefault('csrf', csrf_enabled) + + super(FlaskForm, self).__init__(formdata=formdata, **kwargs) + + def is_submitted(self): + """Consider the form submitted if there is an active request and + the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. + """ + + return _is_submitted() + + def validate_on_submit(self): + """Call :meth:`validate` only if the form is submitted. + This is a shortcut for ``form.is_submitted() and form.validate()``. + """ + return self.is_submitted() and self.validate() + + def hidden_tag(self, *fields): + """Render the form's hidden fields in one call. + + A field is considered hidden if it uses the + :class:`~wtforms.widgets.HiddenInput` widget. + + If ``fields`` are given, only render the given fields that + are hidden. If a string is passed, render the field with that + name if it exists. + + .. versionchanged:: 0.13 + + No longer wraps inputs in hidden div. + This is valid HTML 5. + + .. versionchanged:: 0.13 + + Skip passed fields that aren't hidden. + Skip passed names that don't exist. + """ + + def hidden_fields(fields): + for f in fields: + if isinstance(f, string_types): + f = getattr(self, f, None) + + if f is None or not isinstance(f.widget, HiddenInput): + continue + + yield f + + return Markup( + u'\n'.join(text_type(f) for f in hidden_fields(fields or self)) + ) + + +def _is_submitted(): + """Consider the form submitted if there is an active request and + the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. + """ + + return bool(request) and request.method in SUBMIT_METHODS + + +class Form(FlaskForm): + """ + .. deprecated:: 0.13 + Renamed to :class:`~flask_wtf.FlaskForm`. + """ + + def __init__(self, *args, **kwargs): + warnings.warn(FlaskWTFDeprecationWarning( + '"flask_wtf.Form" has been renamed to "FlaskForm" ' + 'and will be removed in 1.0.' + ), stacklevel=3) + super(Form, self).__init__(*args, **kwargs) diff --git a/venv/Lib/site-packages/flask_wtf/html5.py b/venv/Lib/site-packages/flask_wtf/html5.py new file mode 100644 index 0000000..23e7927 --- /dev/null +++ b/venv/Lib/site-packages/flask_wtf/html5.py @@ -0,0 +1,13 @@ +# coding: utf-8 +# flake8: noqa +import warnings +from flask_wtf._compat import FlaskWTFDeprecationWarning + +warnings.warn(FlaskWTFDeprecationWarning( + '"flask_wtf.html5" will be removed in 1.0. ' + 'Import directly from "wtforms.fields.html5" ' + 'and "wtforms.widgets.html5".' +), stacklevel=2) + +from wtforms.widgets.html5 import * +from wtforms.fields.html5 import * diff --git a/venv/Lib/site-packages/flask_wtf/i18n.py b/venv/Lib/site-packages/flask_wtf/i18n.py new file mode 100644 index 0000000..4a6dda1 --- /dev/null +++ b/venv/Lib/site-packages/flask_wtf/i18n.py @@ -0,0 +1,69 @@ +# coding: utf-8 +""" + flask_wtf.i18n + ~~~~~~~~~~~~~~ + + Internationalization support for Flask WTF. + + :copyright: (c) 2013 by Hsiaoming Yang. +""" + +from flask import _request_ctx_stack +from babel import support +try: + from flask_babel import get_locale +except ImportError: + from flask_babelex import get_locale +try: + from wtforms.i18n import messages_path +except ImportError: + from wtforms.ext.i18n.utils import messages_path + + +__all__ = ('Translations', 'translations') + + +def _get_translations(): + """Returns the correct gettext translations. + Copy from flask-babel with some modifications. + """ + ctx = _request_ctx_stack.top + if ctx is None: + return None + # babel should be in extensions for get_locale + if 'babel' not in ctx.app.extensions: + return None + translations = getattr(ctx, 'wtforms_translations', None) + if translations is None: + dirname = messages_path() + translations = support.Translations.load( + dirname, [get_locale()], domain='wtforms' + ) + ctx.wtforms_translations = translations + return translations + + +class Translations(object): + def gettext(self, string): + t = _get_translations() + if t is None: + return string + if hasattr(t, 'ugettext'): + return t.ugettext(string) + # Python 3 has no ugettext + return t.gettext(string) + + def ngettext(self, singular, plural, n): + t = _get_translations() + if t is None: + if n == 1: + return singular + return plural + + if hasattr(t, 'ungettext'): + return t.ungettext(singular, plural, n) + # Python 3 has no ungettext + return t.ngettext(singular, plural, n) + + +translations = Translations() diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__init__.py b/venv/Lib/site-packages/flask_wtf/recaptcha/__init__.py new file mode 100644 index 0000000..47627d2 --- /dev/null +++ b/venv/Lib/site-packages/flask_wtf/recaptcha/__init__.py @@ -0,0 +1,4 @@ +# flake8: noqa +from .fields import * +from .validators import * +from .widgets import * diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..423de6450f60abb572e5039527a5e297d563a31f GIT binary patch literal 254 zcmX|+Jx;_h5QXh5TB4Qq2K1{Sg@$gmLI`Lmpjn_A$ujX|vFyZ=$7IQoH~^P%OT`ta z7=FY^?|rZMnmJuA7cXzu>Olzc&iTKB*ExrdxQsB;i@RUSKNielE2`OtWTlc>9^Y28 zJZ~*}LwPZ3Z;ehNQa;>Uv%^HMBF@?UsZV#ghI_(DFs*1c!4;BAVStMeTz#W^y~7Us zRBtCr*wvk8L>RFj!P`e5o3I{q<Jco;y(XvjsfCD*9#Zq9K`GnYq}0hU<#N*n^X&2L M`!71+DrG6Z02RYYq5uE@ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/fields.cpython-36.pyc b/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/fields.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..81b8e29425bfd332d99713276d13ac195a4bf3b8 GIT binary patch literal 795 zcmY*X&5qMB5VoD)mUPRFH((W{_QDOJ6@nFr13j=pNKquq8#^UirwMl2QdCY|X>Ys- z55Obv625ZUSK!1<$`%;OlP}85H{UnT<I!mN>HBN`gA($S{1ZOlXK?HpBuOOIq^23A zL^A1VR(qM(V_*AqkOhE)InBaXM20fBhBrm`UKYvdf~4^+aFCR`;AN$>yoFi#7Hujy zSDghoc&iH0cEtzbPL8lIF0l13P!xuJ1WA#MN|G_<UHN;G1ro-~;D(e`deXlhWYG=r zNfv{E(1g%1bENRF9RisU--BZxLfWx**pWS%(=V)}9ovyTyYhB)$4a*IzM^pgOmvp| zCV+Yu*j1}cPwZmOt7X-3p8C$WN|!Fs;!<fhTnk-E(KW`paIq0)-n-?z(85~IzmZ?n zizoRztBlRJr}lK4pDVlQnl^vlG>gd_u@rMvt7SL2*xF9jlUl&T<ZHED=eoMgZPlqs zD~biw*}MeW1>baKZtfPEm$-8~ZMS9sy2og^-y==v=Li2UdwTT2Oaz6qbb#KLqE?){ zA?I}?R~m7``Nx&ehc5>Cvhk7YeSfbmJlt<KK5F5xu*UQ{-005|3Z*sY26go*rRD_j zeN1?5sM;ckY3%uImd5S`95$x5(}Op|uoeaPN=$;NzodwB(bHr9-x&vl6I}fC;OPv1 ON*hB;SV9vPdcOgKd&lDd literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/validators.cpython-36.pyc b/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/validators.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd279ef803cc9f1040c9fc3dd0a022a9850d012b GIT binary patch literal 2367 zcmZ`)-EP}96sG=V`6q6grpwlC-a4RjF*`taR}4eD)LnyNY2jq;DtIGQk#S|~uOyws zf^t(AeTbpg1NH!WhdqOCchy(e)ea>mO^QYW4-d&h^5}fuIr_HQbdLVs#=qAP`WKyf znxNl-#{U2@ZxBS35V^_|6nAt))v3e2QILFLMGdt`cNGOaMX~y;oY6u9X7tE(&C)^- zt;lxmQa8d{<hTy#Cb7bL)NmV7(`}Y(O4xE+#CBImjX0!E8l?F~ciW^jR@_TJBC<kS zuMla`)rIC>Cdw}Aw%>w(Xs4?b7Naj_l#8y)RG2F2{){mi3+$ySEU!HF!hm=pNa6uw z34<wT#zIVEKOyw3+|pGGTO|0|Tu@#(pv~eyE@qq0g-H4B&CSEZ!~Qr)#v$$dNwmqR z@1??j?rnN$u*m~K4=4-v=UHd(Iqh)jGb%c%$GnIN$~pn>L;>ePJnm<`i!6fpKswq< zSSRwr{e(r7^s_IeIioyHVoonOa`R%F4<6dpv$dr^y&z6!qE~sGtu4*Z&#s@hJ#i*m zKW}|9U(|*t(eWijCyxfo9ccU}2nv)U$W@8rY5=)L^jC_j6N8wqkZTZ&*wC9q1)v=U z6h+zk5=P29US~+Rs`&SJd<md@WG~wmH2yJ&9B!D}tSx}T8|YZcl^kK1gEQ5g?1HZX zDqI;3w(dUK+j?*p4~D~sL%j9y-e9+|BFecpreL9S8t$_?-0>G;GGYFUwr-Dp;gs>w z{06@<AKjyTDw1^cLy}B;KYKCki-3~7-8mOD>O~%OY;-{5gHaef8%YNBQqP})9Utw7 z9-rbvu|GQH*{I}(Clbc{>AbKp4&p#yd`*6=2?8lLyxHn$?E5Dsz6`v43}{j>0~CWI z&_rR5%qF2uG}e)Fq7#*93-wrwTQD}>jbUUS>$%1s2rE}7c8(^sg|bjyLRcqGuI2hd z<rAXkDz3k8Q3l=E*~`|(z0+?3t)g{-vMT{(BV?^Fddtk}@g(dvm~^<XATI@EPnF;L zgeP&Kjj1SfnI=V@(I^oVdxS8imm6SF&?8Y|R9wc(=uv!nd9iY;Vp^63gEe3)Ta){Z z{eXs~uunX6Ys)Kww8Xm-Eoll+_+A)d`~{5oItZkgimsYUTWu+(szcv^ne)(8TTY>| zVVC4l3C*9N%v%zilD8U^-fKEQ1f}<8ReFU+R2lfH><R!SSAp%?8*QN+>%y3r!V)$B zq5zOaZcJ)KpK8n{20-mh>Y@R2%>|P19h<pH%nihjh_#E3zRt}F6yh;jsQ1usA01n{ zC03>;do9{xCAT(U&r9XS>nBK_)vaZpKcY;6z&ip8mFJ6@7j~B6hKk#F&t%i1;r8RZ zdjtH_;AwV!C+WP)vok9lKZ*B)F%(hJ)m*cxOfDcHA%^hF%zpb1i2H@bXTA?fxtG;T z2z#<-^X{dJG-h>BVgE$uW#KOx!&xkXh?XNJ)1lC1fiEohA0#P_i>vQkdOR3z-+zjC z2Sd=9RT|6l*{W2HF!2ay61PHU)FbRNsLYW<GO%k`Y?9uxD<Ir@%7OzASURQiLWlp6 zS2X42K*=f#+?wQdIV_Cwd)}iOy9(#rg2ulBfmBs#$rpP0I$B$GfOo5^EhQ9#wwir9 zJSoNf59B~s+38x6z%l%%FfJU7qlC;tsW&iw0Z=YiER0FwW6W0IV)ls?(wQ?3O7=3D z5M{OA0ENroNM9VozSLbSgABFq7L$om)VHHFVWI-Yg!sc!mEJ~K{+5XkMNpEf%4++N zvS~(V;|e<q!>>5?L<vp_MTrf&F1?UQ6ySE5nuVD>n?P+TefyBph5cRi<=-o?kADV2 XRT>c6RkaO$Luo6vsxKpL>!$WU;+Tcc literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/widgets.cpython-36.pyc b/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/widgets.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b585e55da6e318b01423a98dc8aca5effcb9afb GIT binary patch literal 1642 zcmaJ>TW=dh6rS0eH;&_m5=tsn#RNr>k?bIlK&o6*H?e5Q1<Q6-x-wdAcE--yd+W?N z#+Lm;B2gas8T<hL%)Ii%GjDm~%$i)N5M#~RnK_p^=R2S0i?y}<kG~(de;Ek<f$j|0 zqh~OA3j{#~hbY1hjx<NZYOldwcl2znhel*NCPq{rT8>2wCr3=jCe{EuE8ifJBi1D% z7R_Ji&ML7xsJ!wgL`7vyuKHufXe?YWO{I10vC%j!V`&U|62mt?W}!=CKOuCsb0$Q} zpX}~VCX?zQNd_UU`bo6QsPCo1Kl66IG^h^w>@Z*BJ_}M&;>>@#v(0x(9-qd3iO@b} z_1f-IThG@>a9;95kMpNngUU^ut<p9JD^S++@?QHo{HuTrsPI+d*F%TJGno7_2#Op` zkfRYyv`ggZ1b`ssC3Xy=L&-U2fswC)P(y7L#wrA#fe>hjhv0mH7usB7n?j#!7r1kM z4(o+3jD<1RS#1s>g*n$R@Q3Itv@rhhH3D>@{etHBEh1(O5Fc7Uqdo@w?MwSD+D8{E zx+!##n;UbpuRVYh)r}LU*lzAMTHU=D4fjR&_{iC~-M1R;#&NUTY<I5k*LM~Bjc&tj zbi3`2ljDJ)BRajpB_BgEYN$$8mG8skk3bC29R0Ad0FYFMbNmpg41U$#7?1;=6^JLi z$~N?3n{WN5$e~p~lXwI)Fg+m{XDbloYPk2L_mWe_d($1hGwtnDJ`zdVd!8ht%CQ%F z0~!ILDxE18G^#`%d|2<C#^=2-IPIxKE2-y?z>fF&(EO1*5&hmx_j{A9&%ByWuky7p z-~vdN)<05JEK8bw{thRqH_q;vh{9?uOnfioby!@xmu;q;rR67aKNv_IsKiw7<@zns zoxS!!tIJGvYz&himiKR!-R5!YsL^efEopEX_GLaDpN4_&0_@UI_ev}Em=|$rEeQ@? ziGwtSixhBW%nI;fYbtv2-x+Vf8Gir*;Q}_Wok;_)YeiUZ;@RW>FZje)m8hXoUxj%H zlYa?fT2$O5(CdZq3dN5MB(zbUeTtF#37r71f5NX(tbGsc7#edUW28R&xJ|{F#k?fW zXz3=~QV^$OQBoAG-lbye<@2M1J@>HrdiKdJWVffKCG4d*fk%OgvYB;*-GdCJeMqNG z#u6qA?QtxEh-N$X5R_o3u#sjzpdpba&Frkv#A5@_J^)pQ?NVgO+O7+|4_$W)lspdt z=^8GopElck7sqSkGCnDr5`1GXqOL3Rt{Wv}9IAD}b>ED=aH+|%Vv3A+E>`W$`kFm4 z_hHA?(_(9O@uXaU<Q2=86{|)urIhxBveAr=2TQ)Ke%tx(q?x_6jJ>C<)c;oITsNym kTFL2<`XamGzpj>cZJBXBd(n#Eybd%fU`sPhL$mb10eAYTWdHyG literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/fields.py b/venv/Lib/site-packages/flask_wtf/recaptcha/fields.py new file mode 100644 index 0000000..dff3d3f --- /dev/null +++ b/venv/Lib/site-packages/flask_wtf/recaptcha/fields.py @@ -0,0 +1,17 @@ +from wtforms.fields import Field + +from . import widgets +from .validators import Recaptcha + +__all__ = ["RecaptchaField"] + + +class RecaptchaField(Field): + widget = widgets.RecaptchaWidget() + + # error message if recaptcha validation fails + recaptcha_error = None + + def __init__(self, label='', validators=None, **kwargs): + validators = validators or [Recaptcha()] + super(RecaptchaField, self).__init__(label, validators, **kwargs) diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/validators.py b/venv/Lib/site-packages/flask_wtf/recaptcha/validators.py new file mode 100644 index 0000000..bcff23d --- /dev/null +++ b/venv/Lib/site-packages/flask_wtf/recaptcha/validators.py @@ -0,0 +1,77 @@ +try: + import urllib2 as http +except ImportError: + # Python 3 + from urllib import request as http + +from flask import request, current_app +from wtforms import ValidationError +from werkzeug import url_encode +from .._compat import to_bytes, to_unicode +import json + +RECAPTCHA_VERIFY_SERVER = 'https://www.google.com/recaptcha/api/siteverify' +RECAPTCHA_ERROR_CODES = { + 'missing-input-secret': 'The secret parameter is missing.', + 'invalid-input-secret': 'The secret parameter is invalid or malformed.', + 'missing-input-response': 'The response parameter is missing.', + 'invalid-input-response': 'The response parameter is invalid or malformed.' +} + + +__all__ = ["Recaptcha"] + + +class Recaptcha(object): + """Validates a ReCaptcha.""" + + def __init__(self, message=None): + if message is None: + message = RECAPTCHA_ERROR_CODES['missing-input-response'] + self.message = message + + def __call__(self, form, field): + if current_app.testing: + return True + + if request.json: + response = request.json.get('g-recaptcha-response', '') + else: + response = request.form.get('g-recaptcha-response', '') + remote_ip = request.remote_addr + + if not response: + raise ValidationError(field.gettext(self.message)) + + if not self._validate_recaptcha(response, remote_ip): + field.recaptcha_error = 'incorrect-captcha-sol' + raise ValidationError(field.gettext(self.message)) + + def _validate_recaptcha(self, response, remote_addr): + """Performs the actual validation.""" + try: + private_key = current_app.config['RECAPTCHA_PRIVATE_KEY'] + except KeyError: + raise RuntimeError("No RECAPTCHA_PRIVATE_KEY config set") + + data = url_encode({ + 'secret': private_key, + 'remoteip': remote_addr, + 'response': response + }) + + http_response = http.urlopen(RECAPTCHA_VERIFY_SERVER, to_bytes(data)) + + if http_response.code != 200: + return False + + json_resp = json.loads(to_unicode(http_response.read())) + + if json_resp["success"]: + return True + + for error in json_resp.get("error-codes", []): + if error in RECAPTCHA_ERROR_CODES: + raise ValidationError(RECAPTCHA_ERROR_CODES[error]) + + return False diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/widgets.py b/venv/Lib/site-packages/flask_wtf/recaptcha/widgets.py new file mode 100644 index 0000000..71a1006 --- /dev/null +++ b/venv/Lib/site-packages/flask_wtf/recaptcha/widgets.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +from flask import current_app, Markup +from flask import json +from werkzeug import url_encode +JSONEncoder = json.JSONEncoder + +RECAPTCHA_SCRIPT = u'https://www.google.com/recaptcha/api.js' + +RECAPTCHA_TEMPLATE = u''' +<script src='%s' async defer></script> +<div class="g-recaptcha" %s></div> +''' + +__all__ = ["RecaptchaWidget"] + + +class RecaptchaWidget(object): + + def recaptcha_html(self, public_key): + html = current_app.config.get('RECAPTCHA_HTML') + if html: + return Markup(html) + params = current_app.config.get('RECAPTCHA_PARAMETERS') + script = RECAPTCHA_SCRIPT + if params: + script += u'?' + url_encode(params) + + attrs = current_app.config.get('RECAPTCHA_DATA_ATTRS', {}) + attrs['sitekey'] = public_key + snippet = u' '.join([u'data-%s="%s"' % (k, attrs[k]) for k in attrs]) + return Markup(RECAPTCHA_TEMPLATE % (script, snippet)) + + def __call__(self, field, error=None, **kwargs): + """Returns the recaptcha input HTML.""" + + try: + public_key = current_app.config['RECAPTCHA_PUBLIC_KEY'] + except KeyError: + raise RuntimeError("RECAPTCHA_PUBLIC_KEY config not set") + + return self.recaptcha_html(public_key) diff --git a/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/INSTALLER b/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/LICENSE.rst b/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/LICENSE.rst new file mode 100644 index 0000000..ef9c194 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/LICENSE.rst @@ -0,0 +1,47 @@ +`BSD 3-Clause <https://opensource.org/licenses/BSD-3-Clause>`_ + +Copyright © 2011 by the Pallets team. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +We kindly ask you to use these themes in an unmodified manner only with +Pallets and Pallets-related projects, not for unrelated projects. If you +like the visual style and want to use it for your own projects, please +consider making some larger changes to the themes (such as changing font +faces, sizes, colors or margins). + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +---- + +The initial implementation of itsdangerous was inspired by Django's +signing module. + +Copyright © Django Software Foundation and individual contributors. +All rights reserved. diff --git a/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/METADATA b/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/METADATA new file mode 100644 index 0000000..7389a4d --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/METADATA @@ -0,0 +1,98 @@ +Metadata-Version: 2.1 +Name: itsdangerous +Version: 1.1.0 +Summary: Various helpers to pass data to untrusted environments and back. +Home-page: https://palletsprojects.com/p/itsdangerous/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets Team +Maintainer-email: contact@palletsprojects.com +License: BSD +Project-URL: Documentation, https://itsdangerous.palletsprojects.com/ +Project-URL: Code, https://github.com/pallets/itsdangerous +Project-URL: Issue tracker, https://github.com/pallets/itsdangerous/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* + +itsdangerous +============ + +... so better sign this + +Various helpers to pass data to untrusted environments and to get it +back safe and sound. Data is cryptographically signed to ensure that a +token has not been tampered with. + +It's possible to customize how data is serialized. Data is compressed as +needed. A timestamp can be added and verified automatically while +loading a token. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U itsdangerous + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +A Simple Example +---------------- + +Here's how you could generate a token for transmitting a user's id and +name between web requests. + +.. code-block:: python + + from itsdangerous import URLSafeSerializer + auth_s = URLSafeSerializer("secret key", "auth") + token = auth_s.dumps({"id": 5, "name": "itsdangerous"}) + + print(token) + # eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg + + data = auth_s.loads(token) + print(data["name"]) + # itsdangerous + + +Donate +------ + +The Pallets organization develops and supports itsdangerous and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +* Website: https://palletsprojects.com/p/itsdangerous/ +* Documentation: https://itsdangerous.palletsprojects.com/ +* License: `BSD <https://github.com/pallets/itsdangerous/blob/master/LICENSE.rst>`_ +* Releases: https://pypi.org/project/itsdangerous/ +* Code: https://github.com/pallets/itsdangerous +* Issue tracker: https://github.com/pallets/itsdangerous/issues +* Test status: https://travis-ci.org/pallets/itsdangerous +* Test coverage: https://codecov.io/gh/pallets/itsdangerous + + diff --git a/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/RECORD b/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/RECORD new file mode 100644 index 0000000..a9b3d93 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/RECORD @@ -0,0 +1,26 @@ +itsdangerous/__init__.py,sha256=Dr-SkfFdOyiR_WjiqIXnlFpYRMW0XvPBNV5muzE5N_A,708 +itsdangerous/_compat.py,sha256=oAAMcQAjwQXQpIbuHT3o-aL56ztm_7Fe-4lD7IteF6A,1133 +itsdangerous/_json.py,sha256=W7BLL4RPnSOjNdo2gfKT3BeARMCIikY6O75rwWV0XoE,431 +itsdangerous/encoding.py,sha256=KhY85PsH3bGHe5JANN4LMZ_3b0IwUWRRnnw1wvLlaIg,1224 +itsdangerous/exc.py,sha256=KFxg7K2XMliMQAxL4jkRNgE8e73z2jcRaLrzwqVObnI,2959 +itsdangerous/jws.py,sha256=6Lh9W-Lu8D9s7bRazs0Zb35eyAZm3pzLeZqHmRELeII,7470 +itsdangerous/serializer.py,sha256=bT-dfjKec9zcKa8Qo8n7mHW_8M-XCTPMOFq1TQI_Fv4,8653 +itsdangerous/signer.py,sha256=OOZbK8XomBjQfOFEul8osesn7fc80MXB0L1r7E86_GQ,6345 +itsdangerous/timed.py,sha256=on5Q5lX7LT_LaETOhzF1ZmrRbia8P98263R8FiRyM6Y,5635 +itsdangerous/url_safe.py,sha256=xnFTaukIPmW6Qwn6uNQLgzdau8RuAKnp5N7ukuXykj0,2275 +itsdangerous-1.1.0.dist-info/LICENSE.rst,sha256=_rKL-jSNgWsOfbrt3xhJnufoAHxngT241qs3xl4EbNQ,2120 +itsdangerous-1.1.0.dist-info/METADATA,sha256=yyKjL2WOg_WybH2Yt-7NIvGpV3B93IsMc2HbToWc7Sk,3062 +itsdangerous-1.1.0.dist-info/WHEEL,sha256=CihQvCnsGZQBGAHLEUMf0IdA4fRduS_NBUTMgCTtvPM,110 +itsdangerous-1.1.0.dist-info/top_level.txt,sha256=gKN1OKLk81i7fbWWildJA88EQ9NhnGMSvZqhfz9ICjk,13 +itsdangerous-1.1.0.dist-info/RECORD,, +itsdangerous-1.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +itsdangerous/__pycache__/encoding.cpython-36.pyc,, +itsdangerous/__pycache__/exc.cpython-36.pyc,, +itsdangerous/__pycache__/jws.cpython-36.pyc,, +itsdangerous/__pycache__/serializer.cpython-36.pyc,, +itsdangerous/__pycache__/signer.cpython-36.pyc,, +itsdangerous/__pycache__/timed.cpython-36.pyc,, +itsdangerous/__pycache__/url_safe.cpython-36.pyc,, +itsdangerous/__pycache__/_compat.cpython-36.pyc,, +itsdangerous/__pycache__/_json.cpython-36.pyc,, +itsdangerous/__pycache__/__init__.cpython-36.pyc,, diff --git a/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/WHEEL b/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/WHEEL new file mode 100644 index 0000000..dea0e20 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.32.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/top_level.txt b/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/top_level.txt new file mode 100644 index 0000000..e163955 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous-1.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +itsdangerous diff --git a/venv/Lib/site-packages/itsdangerous/__init__.py b/venv/Lib/site-packages/itsdangerous/__init__.py new file mode 100644 index 0000000..0fcd8c1 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/__init__.py @@ -0,0 +1,22 @@ +from ._json import json +from .encoding import base64_decode +from .encoding import base64_encode +from .encoding import want_bytes +from .exc import BadData +from .exc import BadHeader +from .exc import BadPayload +from .exc import BadSignature +from .exc import BadTimeSignature +from .exc import SignatureExpired +from .jws import JSONWebSignatureSerializer +from .jws import TimedJSONWebSignatureSerializer +from .serializer import Serializer +from .signer import HMACAlgorithm +from .signer import NoneAlgorithm +from .signer import Signer +from .timed import TimedSerializer +from .timed import TimestampSigner +from .url_safe import URLSafeSerializer +from .url_safe import URLSafeTimedSerializer + +__version__ = "1.1.0" diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98a73c38390cff2c5752ade02f519e4de0319683 GIT binary patch literal 1011 zcma))-)_?|6vmUbY16dnpS9ga2jX(uMJFZ@7YHHt2WcQS5M@HLB3bTYiZ?r{Y<FAt zMmzv-z*F>=E8YNc#W~hgty~}y<;(B;e0(fhejLZq^|#mbd%^R5cy}J~-b?t*FObwr zed#Hm_&#z$7F3ZG9T$O1q~y2+9FV|qAj_&kDvry*AqgE<fUBhHI0TMJ<hTl4BQ?hn zaGlg0*JP|3q~W*@+$2rMv23X}X*+Jn71bdf$4%fa={jx!_ejriTdt}#vgUXNc%7^} z?#O$pPx_9#z#C)(xJT}%t3z*l?WYgh-}dLFbCYNAk4Mb#r%!0g&+?St33zrJL{}`c zbTqTvz;I=srH9Niz$)MoXDQcUA~5gSOy(>FR|hu~<BZv<#-=UsABEye*}}ZKo(Rox zUjNPT-N`3DS_}=j7EFp8x5h(kk^ZwJTAC0a9q%3N$#JfQ{i2|Me3EDUZcxURT=S-D zzBJuM#xgZoRJBh(ydAR7e5r2rHvFr@O|ZMOyYuK|`@uY*c-$KA;5>9EE3&aJg4HFI z00)Zv`b?M6R!~BeDoTV>10lupE2HaZW3-W39FJ~bP&RHmx``z%ls3kJ#e>r;7<4cQ zr&>}2`_Ns?dLZ)}rI%bAk!O^C^>iNxP=2Qe&yrJE$t1H)vpGu+xw)|UB-zjNi@`C= z*qAGx*}-sTELQ`?;HQ&Io?RwVj1nU(A57TU1=N{DSd+4B%ym9B38f+vmeQTcY#u&W Tc{-K+1zu3I1yc9_>n;2N=F1Oq literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/_compat.cpython-36.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/_compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0e12e085143cd516359422627abb4fd81b8666cc GIT binary patch literal 1173 zcmZuwU61256t$hSX=mHnVfhdUg#5PZYNrx24=at96)+VN55Q`75h4m|J@IYpX<}z= zXJ&%hr{SI7!4Kfi_?4&m3p{a?N@zhi^0BXTeSFS!aylL#-2U;+yo?F?lf2b1CSPE8 z6@ny^79`z|G^Jh4Qr3-88bNd!OY#wsY{SwX^z01ZL`A<$(u5Kiq(eP`gEWB$=@8iE z2*~Al$<jj^%igafeF#TDWgi~iMd>?|z9y689#19T-7~}q4YB$~Re2k<Ey3Q(JIC%) z1W&f)jv~@Kx?wxEW%g4^chQ#YVm*)yXLJ|eKJnDEZH$?$&AekX^7DHc+x2$+ZM5xe z`(Kjp$u7D&`;lxTiA*RY62DPGUV*Qz=8MG}Tf<_()sp))aDQXC^H%AV<H7=mt6CIK zG3{$nILtfX*bHXc8|7en(8X`sjjBWt8_--iuC#;-5VYrJ&#de60&E-QYhgw1!8$xr z_>Q6!K3`Ay%y4ZyuN`u&k!M-!+$*E`(pWBZgNu5_0j0ECHP20139B5YbTl{0ulYus z8_f&QD-67_EAZ|grL8!}-P+WJ<QKqISrt&CPLQaYpSNJFM5W$v+{u;iPAR}c(R7Dc zwOafC5TX`QDB)UvXRYe9#kv&vbT1I*Qmw%G1xG6{(S^}3K0ld0dwM!Oo$fjDO2~Z+ zlPE-}-9|VM!;8j)uvRpaB#o~{@mYr_X&+POa25I%TwVAO7qIk!-UizIPV58R`&WK` zmi^$c%31T+J#MlYxQ#be_LVW4<8Or)tIqfF>&AH~k4u4r&8|UTXN9`Robqs7iF|{3 zE>qq~p;ur{?Xr32%1^5%e1LQ9ywHB$egXS87T!ex?hyhRAJG_}K5Ow4dPMCQSJT6P z7iRJ(BocB}iXy~q;nHOPy|@s$#)Z)}`oK$`E4?%!dim2y7@|JwjVau|R7tH>ZX|@E zhg($BR1hNk$B@9=5WfPXPG<Wv($Sv1%W|Efkl<wt?```2|2pbw`WB4PMHoCUjjRiJ Y(O#VU5Me;OhaIBd5$jyDQ8f7YZw=B`%>V!Z literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/_json.cpython-36.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/_json.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e91b2ed5121fadd786ab1768ceb0f09337ea2362 GIT binary patch literal 866 zcmZuvJ#X7E5G5)31CH#Wa|cgN4AdUlEhvJZ?g!GM?qC#Y1px#jGLCFXRCrWU2i}~y z|D#i<{u!;E`WHO)D7A%Al)^ndh{wD4j(Xbf_db6=<KHMDKgiw+ke_0}3Bo#%MADRm z^qk}meWKW^XO1XwPrXA!`ibQ1BY90Eliod%p7OR{=u0{$BmXDXAhX!vpXDJr#egFO zg_R`<nWUko{HDT|hArg2Y~2&&ZP~_3ohsl{ofRUsZ|3i3cTdiZC<<kU!ss%W!zJi^ zm}yz2YG|*79fCDU0mIv?#45mJY7(Q<dbu~lm|{Q=!ICB2U@z=LXCqemu3FVfwU98P zRi_YZ$G;NO#_XM#9!DQQ8Hm=0aJY_MC|FrtM9;Nejo*n}EL5g)JD#t>s%)GIe3|G* z<u_59TttA1#;9n8c@QNQq{tTvZ9v3b<4LhLKK60S!09CH?!mtKNiFQJ9hc(r-(tWc zgbiKdrAfABwBep?J8tQcZOA3v&@awvXI2F&hteo6AWo83RS#4ljIi3kjy~Se;}J7X zg*iYNd2ZiIbt%f!8h4FVOO{yyI5AIOEUWhFR+t6ULu#Po4)+lT7~qCSSU>~%kDt2A z4DYzhc`h==dDY{*xlQB&=hvl3fA4fSmpXQz!3vwinX*?}))T8+?X`CeO|8Mr$dOt@ zm-;O5jr%rgg*`~JB2{kkjj4mPtkA~3G)9}MjmA`L>*W7cuDZ_}ohR<Ef-V9>g9AG7 FegSMn)K35a literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/encoding.cpython-36.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/encoding.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0af51ee62b01b94188901a500d78a88eb401fa69 GIT binary patch literal 1638 zcma)6&2HO95Z)yzilQa?U+zB`6hR=MG78jiiy#RKCp`pcRkU`3ln_F!xNC(nMGCu1 z*D~x&l~aK}NFSi@&}Z0dPd@jSQ)iZOVYety5<4^dJ2T&WyPtJB;n{Cblixi;{vbC# z7wj*gsh>e`!f8(8TGN=?z7sp#;co8Eyx5}zY%g!j{MZM3i#K`jn#N7u;^Au&2YiLM zLAL~*hR{}a2=DBWX`6SKH0}sz+U2WYt%}v@TD%T&Bi`g|d>!^|@eRHSdYjWj(%-&< zc*yXIg6vb%(&9`fdNCK!X&x~CglYDsQR(Bs7sgjwW~ojw7yM9Tdkjsz2SSqzqUq9+ zy$dpRF6f1`bS_~G{Lo?FHDRW*LTOf{LgGf_iy|$#fGMg>U^`hcfnh02seUE04XfVp z$M;9il#pt)xTo$dMo)w~(dB$}P?jfyZ&<-5VkQbbI9w<#W`h|6B}b>CI34BLc%(8d z26L94z&bU`wBoFo2w7HYbnS6>zA)jBtkB7Lp@nK;LOuvWL)xaY3s#B<;fLCRX0v_= z49t2-FClZt8LX+x=@PMZ&R-rdCHD8CI?aehh+r{^%Hzm7ib@$F;=9q&D-lVds$AoI zJ;|g9Blr|M7LhKaN{J{dqG#WKt#<9ipBT%-zGnk8{@6M*>y^xLOETWys~sC(qT@N# z)Yh<=SYT_>fip~}-ka3xS7As2g9-5hDz`x2${veivp)g@v*#3<`-_XU=ANdA!V2My z9@!$FlS{G)wKH{b^pJQ&d%AJ#oP=^n0ZdA?&;2F+iT*$e=LO+SKrT4{_APQ;AVB~* z$a-Lb_#Y5PnX>p;7;C6ZgQ+yjjGs-4Qi{GKLkM23pulvmFx~U_pBASq&v;bBF{t6- z5r<na>^Ezi-CPuM4VK?Rx(?K|j^OTmh4eMybCy@NE%Ed|m7Adc3;Ozi9ay0dVB&RY z&vAf&8&rM({d0GaJe1pLIgh@(Rgk~wh(xXFIHlJl99otD8%-z4fZ@8VNpAf7niOtP z&Hfoy`{vqD;9P}X2$gq0-tvyQSvnw5&^!9(aP^mzyYJ%V_l8#i%*hW?AZKkbwmKLN zcrpz+t(5M=6K99D{8v0TmwyUpT*q_Qo+lf4Hs0BO&lbmGZRuX6+BB22oXwf8pB-16 zrM4utpqAhkV$-oCNphj#1yRQ5*#zDTiNVKf$=f@^^7$(^7TWlSTEgM6#dUu#0XWf@ zhi{Gi2=*AiDs1Ot3=V;l810SJu+q}rz~I5G<W(*nAwKps4JiCho3`E14}zfEb3*D< I-wnLK0QjtpF#rGn literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/exc.cpython-36.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/exc.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..179d3fe460474561750af3375a246f12a2c76b83 GIT binary patch literal 3227 zcmbVOOOG2x5T2JG>-Bm~NC*K0b3u_2Yaj#(SrJ-g6A=zr5eWw@quJi+-rY&&VY=I! zwdLC)<;a0w!G#-t##c`K1x|d`GoG=dB_Xuj-O~?OSJn4*Re!#-(>nj-o8Xsi%lgw= zD;~c0QPlzsx40c!i9NDy`gZW`j2zqg+Tt$vezv$L+zWT)VO-;NjO)U~xQ20qH!*IC z8pd^uw|EQVmZ)Rgz<8UtF>agnCdNCwgK@|Fe~a%vvU=UWY^cz)m+r$KZlT-K;#>!M zHW4A_%i<Pa>JC1HHL|%ia=1Nmxij*fTgSFhwK=NgDvs)+A#aQNs0kIJ@~HmY@~qJo z4s0$PcNxFOG@JkW9gCFUest^~G46BB`Ps40(wVO^sr_e6`63OoX{v=3yzf6gjg*g{ zJ{9<f5-lI)IFmk$V_%$yVxprgrFEwLNGs0L6Ctyy^0_#UQla`S^Ot_#KNC`6FXLSB z!NC8t|JfI2X&5?%snnC%RMmTEw59EZb>Td57S_VP0746l1D9Jc%O3W;Wlc>dLP|n+ z*$jdxjdTz!8;MYgors>Z^puE?WdrNv>L3VXrc@C8X8j!;494FpC^eqlR5xeidqO?c z*<^e-%bxCi%ToM55vksLG*en6dkI4)$7dov8^_VnSVdawO<4F8`_y>d^fCO>pUh+( zhjvgEX=mA2?0L7qp}*2JBq~s$p3FZ$RV_5yT3Ao)3p+Q|k>r!Y4B4b_+LSk-y0vTs zfzmPvu3(^yL+$x)b(~qd{4`P#XIU3GkLM>k!+CA0kN3WW1Gj@<nnq#91sq!^a&XbJ zFVjJ`A?fYv{9cP7Nm)YwYz0A*@o7xo?I8GR%Hm=Ln)56S0%M(Vnh_+)2)To1xl_Ov zia5r;(sHP=MVrV0h5|KwD6*$n^Bm7<drjA~J;$ttm@W%>b01Z`gJ$D$)y%cBM;>?J z`uegBA3lmsQl_U;%s;H|5A)S(+UJ>2ewt}NVLCkR4?~hqg?g$7sOl{=8cr^`3|0ru zip#Fi(%8RjOxP^W81K2cpNYQm8a1WuxzR+i4^Rt=wHxzm>-v^Fqsvm0yCk7Y%^TDZ zRdRn<Qq7m~=T)rI9W<hw<VBhZQOiP>?W4Zhi$wg#Q6ef&9<6&)V`-1V3tw|81)lVb z#nD<AV!oOlnJa8U2SV4tj0T76W*_#8z+x^Q6r`T2fq@vhf??rYxCkolCJ<z34#YMh zqzDLT!F%3|U|gqtmG6w(2$4@v3q(4N`HdHCuTXJ@uMG>4=%bZ<(A$wTvjGdml6~4j zP;Zc%DD5u0B?|Y?ClSK@T&{yr(VAdUnk5s;09nk1q&T1YhldF}4-oeb5AzIR+>JdI zJd{-ih=T^04dhqgz@3-zv2tfcMM7*q22n_8x{}5l7Q2|(M}0LBw*bZi!C=b#_ZN)@ z`%cXgnOjf~Ju+h=DCZ?bI?U2|W-LBslZim`qkM=&5`n98<dZxZp0YCmhZGV`emtQt z@hsA(lunI=($_CZlecacY^LZ&h5C}s?_+Qon-|_jqBNH`=X5<+Y1zqSbP}a34v;`I ziTGghN)>A;Hp+LYsX!??{s8le$ZpvkukOvSl#Z?F?G9g`-!~v}A2km<b?}>J{wLZk zDD^N8rR+AY#X90eh+V_FfETZy7)&tO(nE=!KE>u0&nY&8EnKJxUIBPMm2@cq{n!U7 z@_$P~hcG{hl*tc9%8}5|gh-9_iaQWx#Fk_todiVXNL4Q`Bg2p?3&W|zCHDDLM(K%9 zx>1iKmCk4(OMN`+225)?I9!R!LC0k@l3w~equWJ=da4Jg|2>%*25ls=f1}ioB-ey& zyY0>2tgx#X_4;XyQrP2_;AR7SqM0GyCXFD>k_prKLxT)kZrxwKX!B%j5&^|m6LJg_ k&EMB9L>Z-)%{%!6=PQc8<`vSm+iv%Ix6|!*>)osW0FNlk)&Kwi literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/jws.cpython-36.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/jws.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5d0585f6585a04583cf3c6b8516b892ba84645b GIT binary patch literal 6686 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 b{i1r&K(6;}ylFKi|3xbN*gtpXmsah6hMH~T literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/serializer.cpython-36.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/serializer.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..add38f0134065254ff2265b48f45f2923039a4b1 GIT binary patch literal 8041 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 X_7Aiie~8+Vj^=Gk6Meqg?Z*EAtVgy+ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/signer.cpython-36.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/signer.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..580447c3d66d2b2c2cb7cc21ca7fbe0e36e45003 GIT binary patch literal 5802 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 Pfuz}=zq7Mb-?{N$pjW96 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/timed.cpython-36.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/timed.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76b263af7ed1cef8496ee0f745ebc6d271049d8e GIT binary patch literal 4568 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 I)BfZC02D8<y8r+H literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/url_safe.cpython-36.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/url_safe.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a29602a97924c644cff881d0f5fdee734eecf92 GIT binary patch literal 2557 zcmcIm&5zqe6rZujj^j;sw@X#pBEdkBmIyXN0`;(bsI*X21Pki6YOx}DXFZc$ZyY<! zOu9|vaA1qd1^xhpxO0a)7jFCozV_5JH%`2nBu!LWB#>}4Z{B?P`OWYB-kZG9Y=(D# zxfVYN5PE=?9uM?O(9|~|IN~@%5#kPaGu(F~2OFJaZl6TN>K^D`<XPR%{C+K}S-qA8 z{d!cldXR<vm1qT{4-v2P@E+o!Xq>oF6UHmN0po^fiuwtOR$->eS7BxqW>#QkjpJR^ zT6+lIpq+;pWD8e~_)5_quta~f`{@phoBK?Oi*LqUBn1~s6C$5YgqIorjE%B_aaay_ zg-ltNj)jEL+Ksd?_+lzC-<m(}Oi{0cB2a`miX4t3SCGiNkGj}kb1#T|s}t^lUb~M7 ziURQ4ug=`O{_!sBiiP=4(z|Is{vA5OX`0jHR3B1Cr8pR7Oj7m8HJWNz%reTf7X5*y zx}f7M-47`|2>>94QdA!bs<ce=1GC;|Jz?HchlSKa(zHu+k-$DCM{Rm-ZZK1YwKBzx zPGMR$r3x4^ZA>LBrwUGxjp)9hgN!9pzZr$2wWrQFGn8u3zW{9wn)(?8#}iWGBVf%5 zmd}@%yBvM(oI?}L$%Kp=8khJ4PrO?wZxf`QUMO#4H1RPSZ)lf$rO*9uov-jM^toXp ztbFO;M!9nvl|=q9TGiypE4{}oe7|I&_H+x{*R>LzI7dP0b#dwZ=#=hvhHb&l)jtf= z+R~is#`vu(#W3SEFEpJ2HYJ(`Y%paNV3V++613=208HFX#6YJ-4*XgzWO|V1EQ^Jd zg&e=PFlWL^jj^R*E(~@HDeNk@(S<<I)>`!{RGLYx40$90FRS_o^G{0OjES*bg$v`A zr-lRYpl((hCKKYhxmrb($huW+Hc=5K4^-v%j!iF3G%IqXioC)myXtYAWK5|z{t5lb zuWWa20%=uew5c{no%e<6>0;2iTok>nPgu?lL|^23Yj>o8#an#_o$TBZ`JE0p*ioq# zTad~<*rz(FR-EMrLKZ{S8Okg+#A^>m@)Wr63^esJ2!sg^aRa0Savh|D&pT$c`e-!1 z@Z`j6FCRRa^VT3{GTk(F9fU@}Ov4Sw@)I(C;%fJZl)&Q&(OyYTFi042=Pc?Wxm}V8 zR07!L+dW?C${kKh?^!efh61a+ovTwm$q+u!s>yZeX3^lo{$L&rxeD`DFy}<&W+IoS zEVq2QVV2xt|45zzEo!DJp6?U93YYZSl&Mhd<XPA%&lz#vi044GpsCX!5CCStJPnfI zz_zDCx9lZQFo@%v^+g<4VI21hKFo~Xh~qDYESs&=;+Pjn97}_2wZTP~4Kp1tikywC z^{HykXqAX8gEy?lM$C_3y*KP#fu`W%LIEKTaT;eViU~|SdQAFWf~E|}%VnE85DK?C z^*45mf4x=6o}yU_6?EJ3dwZ|FDBF8<f2gT}KtY0YYIvleenZi<mCzPKL)UpVPKsQm zN}JS$nqbQGZ~*x?d-+(%1THO_95M+P8O%>NSu-pf9I}0(DO2<q&@lbp-e$bHxA&S+ zwm`9VKyQOSt-?DkB432#%9o6|V1&t*1&~W5*o1|P(3Hu5fJig`R{+lMwa4zx@o!H; z@v%++1f@Mc1jk-E{~bze#$f*cL-`6o_s$ZOX53nzX0aEWsyQWpU~gHVgRR##qbnEg ziD|hGmr}yqqLru3zY=L^UipymBGH!SHdNAl<Vk_8g14uc_^!zSW$H!XG@we(e(w6K H8@0axJPoH} literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/_compat.py b/venv/Lib/site-packages/itsdangerous/_compat.py new file mode 100644 index 0000000..2291bce --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/_compat.py @@ -0,0 +1,46 @@ +import decimal +import hmac +import numbers +import sys + +PY2 = sys.version_info[0] == 2 + +if PY2: + from itertools import izip + + text_type = unicode # noqa: 821 +else: + izip = zip + text_type = str + +number_types = (numbers.Real, decimal.Decimal) + + +def _constant_time_compare(val1, val2): + """Return ``True`` if the two strings are equal, ``False`` + otherwise. + + The time taken is independent of the number of characters that + match. Do not use this function for anything else than comparision + with known length targets. + + This is should be implemented in C in order to get it completely + right. + + This is an alias of :func:`hmac.compare_digest` on Python>=2.7,3.3. + """ + len_eq = len(val1) == len(val2) + if len_eq: + result = 0 + left = val1 + else: + result = 1 + left = val2 + for x, y in izip(bytearray(left), bytearray(val2)): + result |= x ^ y + return result == 0 + + +# Starting with 2.7/3.3 the standard library has a c-implementation for +# constant time string compares. +constant_time_compare = getattr(hmac, "compare_digest", _constant_time_compare) diff --git a/venv/Lib/site-packages/itsdangerous/_json.py b/venv/Lib/site-packages/itsdangerous/_json.py new file mode 100644 index 0000000..426b36e --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/_json.py @@ -0,0 +1,18 @@ +try: + import simplejson as json +except ImportError: + import json + + +class _CompactJSON(object): + """Wrapper around json module that strips whitespace.""" + + @staticmethod + def loads(payload): + return json.loads(payload) + + @staticmethod + def dumps(obj, **kwargs): + kwargs.setdefault("ensure_ascii", False) + kwargs.setdefault("separators", (",", ":")) + return json.dumps(obj, **kwargs) diff --git a/venv/Lib/site-packages/itsdangerous/encoding.py b/venv/Lib/site-packages/itsdangerous/encoding.py new file mode 100644 index 0000000..1e28969 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/encoding.py @@ -0,0 +1,49 @@ +import base64 +import string +import struct + +from ._compat import text_type +from .exc import BadData + + +def want_bytes(s, encoding="utf-8", errors="strict"): + if isinstance(s, text_type): + s = s.encode(encoding, errors) + return s + + +def base64_encode(string): + """Base64 encode a string of bytes or text. The resulting bytes are + safe to use in URLs. + """ + string = want_bytes(string) + return base64.urlsafe_b64encode(string).rstrip(b"=") + + +def base64_decode(string): + """Base64 decode a URL-safe string of bytes or text. The result is + bytes. + """ + string = want_bytes(string, encoding="ascii", errors="ignore") + string += b"=" * (-len(string) % 4) + + try: + return base64.urlsafe_b64decode(string) + except (TypeError, ValueError): + raise BadData("Invalid base64-encoded data") + + +# The alphabet used by base64.urlsafe_* +_base64_alphabet = (string.ascii_letters + string.digits + "-_=").encode("ascii") + +_int64_struct = struct.Struct(">Q") +_int_to_bytes = _int64_struct.pack +_bytes_to_int = _int64_struct.unpack + + +def int_to_bytes(num): + return _int_to_bytes(num).lstrip(b"\x00") + + +def bytes_to_int(bytestr): + return _bytes_to_int(bytestr.rjust(8, b"\x00"))[0] diff --git a/venv/Lib/site-packages/itsdangerous/exc.py b/venv/Lib/site-packages/itsdangerous/exc.py new file mode 100644 index 0000000..287d691 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/exc.py @@ -0,0 +1,98 @@ +from ._compat import PY2 +from ._compat import text_type + + +class BadData(Exception): + """Raised if bad data of any sort was encountered. This is the base + for all exceptions that itsdangerous defines. + + .. versionadded:: 0.15 + """ + + message = None + + def __init__(self, message): + super(BadData, self).__init__(self, message) + self.message = message + + def __str__(self): + return text_type(self.message) + + if PY2: + __unicode__ = __str__ + + def __str__(self): + return self.__unicode__().encode("utf-8") + + +class BadSignature(BadData): + """Raised if a signature does not match.""" + + def __init__(self, message, payload=None): + BadData.__init__(self, message) + + #: The payload that failed the signature test. In some + #: situations you might still want to inspect this, even if + #: you know it was tampered with. + #: + #: .. versionadded:: 0.14 + self.payload = payload + + +class BadTimeSignature(BadSignature): + """Raised if a time-based signature is invalid. This is a subclass + of :class:`BadSignature`. + """ + + def __init__(self, message, payload=None, date_signed=None): + BadSignature.__init__(self, message, payload) + + #: If the signature expired this exposes the date of when the + #: signature was created. This can be helpful in order to + #: tell the user how long a link has been gone stale. + #: + #: .. versionadded:: 0.14 + self.date_signed = date_signed + + +class SignatureExpired(BadTimeSignature): + """Raised if a signature timestamp is older than ``max_age``. This + is a subclass of :exc:`BadTimeSignature`. + """ + + +class BadHeader(BadSignature): + """Raised if a signed header is invalid in some form. This only + happens for serializers that have a header that goes with the + signature. + + .. versionadded:: 0.24 + """ + + def __init__(self, message, payload=None, header=None, original_error=None): + BadSignature.__init__(self, message, payload) + + #: If the header is actually available but just malformed it + #: might be stored here. + self.header = header + + #: If available, the error that indicates why the payload was + #: not valid. This might be ``None``. + self.original_error = original_error + + +class BadPayload(BadData): + """Raised if a payload is invalid. This could happen if the payload + is loaded despite an invalid signature, or if there is a mismatch + between the serializer and deserializer. The original exception + that occurred during loading is stored on as :attr:`original_error`. + + .. versionadded:: 0.15 + """ + + def __init__(self, message, original_error=None): + BadData.__init__(self, message) + + #: If available, the error that indicates why the payload was + #: not valid. This might be ``None``. + self.original_error = original_error diff --git a/venv/Lib/site-packages/itsdangerous/jws.py b/venv/Lib/site-packages/itsdangerous/jws.py new file mode 100644 index 0000000..92e9ec8 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/jws.py @@ -0,0 +1,218 @@ +import hashlib +import time +from datetime import datetime + +from ._compat import number_types +from ._json import _CompactJSON +from ._json import json +from .encoding import base64_decode +from .encoding import base64_encode +from .encoding import want_bytes +from .exc import BadData +from .exc import BadHeader +from .exc import BadPayload +from .exc import BadSignature +from .exc import SignatureExpired +from .serializer import Serializer +from .signer import HMACAlgorithm +from .signer import NoneAlgorithm + + +class JSONWebSignatureSerializer(Serializer): + """This serializer implements JSON Web Signature (JWS) support. Only + supports the JWS Compact Serialization. + """ + + jws_algorithms = { + "HS256": HMACAlgorithm(hashlib.sha256), + "HS384": HMACAlgorithm(hashlib.sha384), + "HS512": HMACAlgorithm(hashlib.sha512), + "none": NoneAlgorithm(), + } + + #: The default algorithm to use for signature generation + default_algorithm = "HS512" + + default_serializer = _CompactJSON + + def __init__( + self, + secret_key, + salt=None, + serializer=None, + serializer_kwargs=None, + signer=None, + signer_kwargs=None, + algorithm_name=None, + ): + Serializer.__init__( + self, + secret_key=secret_key, + salt=salt, + serializer=serializer, + serializer_kwargs=serializer_kwargs, + signer=signer, + signer_kwargs=signer_kwargs, + ) + if algorithm_name is None: + algorithm_name = self.default_algorithm + self.algorithm_name = algorithm_name + self.algorithm = self.make_algorithm(algorithm_name) + + def load_payload(self, payload, serializer=None, return_header=False): + payload = want_bytes(payload) + if b"." not in payload: + raise BadPayload('No "." found in value') + base64d_header, base64d_payload = payload.split(b".", 1) + try: + json_header = base64_decode(base64d_header) + except Exception as e: + raise BadHeader( + "Could not base64 decode the header because of an exception", + original_error=e, + ) + try: + json_payload = base64_decode(base64d_payload) + except Exception as e: + raise BadPayload( + "Could not base64 decode the payload because of an exception", + original_error=e, + ) + try: + header = Serializer.load_payload(self, json_header, serializer=json) + except BadData as e: + raise BadHeader( + "Could not unserialize header because it was malformed", + original_error=e, + ) + if not isinstance(header, dict): + raise BadHeader("Header payload is not a JSON object", header=header) + payload = Serializer.load_payload(self, json_payload, serializer=serializer) + if return_header: + return payload, header + return payload + + def dump_payload(self, header, obj): + base64d_header = base64_encode( + self.serializer.dumps(header, **self.serializer_kwargs) + ) + base64d_payload = base64_encode( + self.serializer.dumps(obj, **self.serializer_kwargs) + ) + return base64d_header + b"." + base64d_payload + + def make_algorithm(self, algorithm_name): + try: + return self.jws_algorithms[algorithm_name] + except KeyError: + raise NotImplementedError("Algorithm not supported") + + def make_signer(self, salt=None, algorithm=None): + if salt is None: + salt = self.salt + key_derivation = "none" if salt is None else None + if algorithm is None: + algorithm = self.algorithm + return self.signer( + self.secret_key, + salt=salt, + sep=".", + key_derivation=key_derivation, + algorithm=algorithm, + ) + + def make_header(self, header_fields): + header = header_fields.copy() if header_fields else {} + header["alg"] = self.algorithm_name + return header + + def dumps(self, obj, salt=None, header_fields=None): + """Like :meth:`.Serializer.dumps` but creates a JSON Web + Signature. It also allows for specifying additional fields to be + included in the JWS header. + """ + header = self.make_header(header_fields) + signer = self.make_signer(salt, self.algorithm) + return signer.sign(self.dump_payload(header, obj)) + + def loads(self, s, salt=None, return_header=False): + """Reverse of :meth:`dumps`. If requested via ``return_header`` + it will return a tuple of payload and header. + """ + payload, header = self.load_payload( + self.make_signer(salt, self.algorithm).unsign(want_bytes(s)), + return_header=True, + ) + if header.get("alg") != self.algorithm_name: + raise BadHeader("Algorithm mismatch", header=header, payload=payload) + if return_header: + return payload, header + return payload + + def loads_unsafe(self, s, salt=None, return_header=False): + kwargs = {"return_header": return_header} + return self._loads_unsafe_impl(s, salt, kwargs, kwargs) + + +class TimedJSONWebSignatureSerializer(JSONWebSignatureSerializer): + """Works like the regular :class:`JSONWebSignatureSerializer` but + also records the time of the signing and can be used to expire + signatures. + + JWS currently does not specify this behavior but it mentions a + possible extension like this in the spec. Expiry date is encoded + into the header similar to what's specified in `draft-ietf-oauth + -json-web-token <http://self-issued.info/docs/draft-ietf-oauth-json + -web-token.html#expDef>`_. + """ + + DEFAULT_EXPIRES_IN = 3600 + + def __init__(self, secret_key, expires_in=None, **kwargs): + JSONWebSignatureSerializer.__init__(self, secret_key, **kwargs) + if expires_in is None: + expires_in = self.DEFAULT_EXPIRES_IN + self.expires_in = expires_in + + def make_header(self, header_fields): + header = JSONWebSignatureSerializer.make_header(self, header_fields) + iat = self.now() + exp = iat + self.expires_in + header["iat"] = iat + header["exp"] = exp + return header + + def loads(self, s, salt=None, return_header=False): + payload, header = JSONWebSignatureSerializer.loads( + self, s, salt, return_header=True + ) + + if "exp" not in header: + raise BadSignature("Missing expiry date", payload=payload) + + int_date_error = BadHeader("Expiry date is not an IntDate", payload=payload) + try: + header["exp"] = int(header["exp"]) + except ValueError: + raise int_date_error + if header["exp"] < 0: + raise int_date_error + + if header["exp"] < self.now(): + raise SignatureExpired( + "Signature expired", + payload=payload, + date_signed=self.get_issue_date(header), + ) + + if return_header: + return payload, header + return payload + + def get_issue_date(self, header): + rv = header.get("iat") + if isinstance(rv, number_types): + return datetime.utcfromtimestamp(int(rv)) + + def now(self): + return int(time.time()) diff --git a/venv/Lib/site-packages/itsdangerous/serializer.py b/venv/Lib/site-packages/itsdangerous/serializer.py new file mode 100644 index 0000000..12c20f4 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/serializer.py @@ -0,0 +1,233 @@ +import hashlib + +from ._compat import text_type +from ._json import json +from .encoding import want_bytes +from .exc import BadPayload +from .exc import BadSignature +from .signer import Signer + + +def is_text_serializer(serializer): + """Checks whether a serializer generates text or binary.""" + return isinstance(serializer.dumps({}), text_type) + + +class Serializer(object): + """This class provides a serialization interface on top of the + signer. It provides a similar API to json/pickle and other modules + but is structured differently internally. If you want to change the + underlying implementation for parsing and loading you have to + override the :meth:`load_payload` and :meth:`dump_payload` + functions. + + This implementation uses simplejson if available for dumping and + loading and will fall back to the standard library's json module if + it's not available. + + You do not need to subclass this class in order to switch out or + customize the :class:`.Signer`. You can instead pass a different + class to the constructor as well as keyword arguments as a dict that + should be forwarded. + + .. code-block:: python + + s = Serializer(signer_kwargs={'key_derivation': 'hmac'}) + + You may want to upgrade the signing parameters without invalidating + existing signatures that are in use. Fallback signatures can be + given that will be tried if unsigning with the current signer fails. + + Fallback signers can be defined by providing a list of + ``fallback_signers``. Each item can be one of the following: a + signer class (which is instantiated with ``signer_kwargs``, + ``salt``, and ``secret_key``), a tuple + ``(signer_class, signer_kwargs)``, or a dict of ``signer_kwargs``. + + For example, this is a serializer that signs using SHA-512, but will + unsign using either SHA-512 or SHA1: + + .. code-block:: python + + s = Serializer( + signer_kwargs={"digest_method": hashlib.sha512}, + fallback_signers=[{"digest_method": hashlib.sha1}] + ) + + .. versionchanged:: 0.14: + The ``signer`` and ``signer_kwargs`` parameters were added to + the constructor. + + .. versionchanged:: 1.1.0: + Added support for ``fallback_signers`` and configured a default + SHA-512 fallback. This fallback is for users who used the yanked + 1.0.0 release which defaulted to SHA-512. + """ + + #: If a serializer module or class is not passed to the constructor + #: this one is picked up. This currently defaults to :mod:`json`. + default_serializer = json + + #: The default :class:`Signer` class that is being used by this + #: serializer. + #: + #: .. versionadded:: 0.14 + default_signer = Signer + + #: The default fallback signers. + default_fallback_signers = [{"digest_method": hashlib.sha512}] + + def __init__( + self, + secret_key, + salt=b"itsdangerous", + serializer=None, + serializer_kwargs=None, + signer=None, + signer_kwargs=None, + fallback_signers=None, + ): + self.secret_key = want_bytes(secret_key) + self.salt = want_bytes(salt) + if serializer is None: + serializer = self.default_serializer + self.serializer = serializer + self.is_text_serializer = is_text_serializer(serializer) + if signer is None: + signer = self.default_signer + self.signer = signer + self.signer_kwargs = signer_kwargs or {} + if fallback_signers is None: + fallback_signers = list(self.default_fallback_signers or ()) + self.fallback_signers = fallback_signers + self.serializer_kwargs = serializer_kwargs or {} + + def load_payload(self, payload, serializer=None): + """Loads the encoded object. This function raises + :class:`.BadPayload` if the payload is not valid. The + ``serializer`` parameter can be used to override the serializer + stored on the class. The encoded ``payload`` should always be + bytes. + """ + if serializer is None: + serializer = self.serializer + is_text = self.is_text_serializer + else: + is_text = is_text_serializer(serializer) + try: + if is_text: + payload = payload.decode("utf-8") + return serializer.loads(payload) + except Exception as e: + raise BadPayload( + "Could not load the payload because an exception" + " occurred on unserializing the data.", + original_error=e, + ) + + def dump_payload(self, obj): + """Dumps the encoded object. The return value is always bytes. + If the internal serializer returns text, the value will be + encoded as UTF-8. + """ + return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs)) + + def make_signer(self, salt=None): + """Creates a new instance of the signer to be used. The default + implementation uses the :class:`.Signer` base class. + """ + if salt is None: + salt = self.salt + return self.signer(self.secret_key, salt=salt, **self.signer_kwargs) + + def iter_unsigners(self, salt=None): + """Iterates over all signers to be tried for unsigning. Starts + with the configured signer, then constructs each signer + specified in ``fallback_signers``. + """ + if salt is None: + salt = self.salt + yield self.make_signer(salt) + for fallback in self.fallback_signers: + if type(fallback) is dict: + kwargs = fallback + fallback = self.signer + elif type(fallback) is tuple: + fallback, kwargs = fallback + else: + kwargs = self.signer_kwargs + yield fallback(self.secret_key, salt=salt, **kwargs) + + def dumps(self, obj, salt=None): + """Returns a signed string serialized with the internal + serializer. The return value can be either a byte or unicode + string depending on the format of the internal serializer. + """ + payload = want_bytes(self.dump_payload(obj)) + rv = self.make_signer(salt).sign(payload) + if self.is_text_serializer: + rv = rv.decode("utf-8") + return rv + + def dump(self, obj, f, salt=None): + """Like :meth:`dumps` but dumps into a file. The file handle has + to be compatible with what the internal serializer expects. + """ + f.write(self.dumps(obj, salt)) + + def loads(self, s, salt=None): + """Reverse of :meth:`dumps`. Raises :exc:`.BadSignature` if the + signature validation fails. + """ + s = want_bytes(s) + last_exception = None + for signer in self.iter_unsigners(salt): + try: + return self.load_payload(signer.unsign(s)) + except BadSignature as err: + last_exception = err + raise last_exception + + def load(self, f, salt=None): + """Like :meth:`loads` but loads from a file.""" + return self.loads(f.read(), salt) + + def loads_unsafe(self, s, salt=None): + """Like :meth:`loads` but without verifying the signature. This + is potentially very dangerous to use depending on how your + serializer works. The return value is ``(signature_valid, + payload)`` instead of just the payload. The first item will be a + boolean that indicates if the signature is valid. This function + never fails. + + Use it for debugging only and if you know that your serializer + module is not exploitable (for example, do not use it with a + pickle serializer). + + .. versionadded:: 0.15 + """ + return self._loads_unsafe_impl(s, salt) + + def _loads_unsafe_impl(self, s, salt, load_kwargs=None, load_payload_kwargs=None): + """Low level helper function to implement :meth:`loads_unsafe` + in serializer subclasses. + """ + try: + return True, self.loads(s, salt=salt, **(load_kwargs or {})) + except BadSignature as e: + if e.payload is None: + return False, None + try: + return ( + False, + self.load_payload(e.payload, **(load_payload_kwargs or {})), + ) + except BadPayload: + return False, None + + def load_unsafe(self, f, *args, **kwargs): + """Like :meth:`loads_unsafe` but loads from a file. + + .. versionadded:: 0.15 + """ + return self.loads_unsafe(f.read(), *args, **kwargs) diff --git a/venv/Lib/site-packages/itsdangerous/signer.py b/venv/Lib/site-packages/itsdangerous/signer.py new file mode 100644 index 0000000..6bddc03 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/signer.py @@ -0,0 +1,179 @@ +import hashlib +import hmac + +from ._compat import constant_time_compare +from .encoding import _base64_alphabet +from .encoding import base64_decode +from .encoding import base64_encode +from .encoding import want_bytes +from .exc import BadSignature + + +class SigningAlgorithm(object): + """Subclasses must implement :meth:`get_signature` to provide + signature generation functionality. + """ + + def get_signature(self, key, value): + """Returns the signature for the given key and value.""" + raise NotImplementedError() + + def verify_signature(self, key, value, sig): + """Verifies the given signature matches the expected + signature. + """ + return constant_time_compare(sig, self.get_signature(key, value)) + + +class NoneAlgorithm(SigningAlgorithm): + """Provides an algorithm that does not perform any signing and + returns an empty signature. + """ + + def get_signature(self, key, value): + return b"" + + +class HMACAlgorithm(SigningAlgorithm): + """Provides signature generation using HMACs.""" + + #: The digest method to use with the MAC algorithm. This defaults to + #: SHA1, but can be changed to any other function in the hashlib + #: module. + default_digest_method = staticmethod(hashlib.sha1) + + def __init__(self, digest_method=None): + if digest_method is None: + digest_method = self.default_digest_method + self.digest_method = digest_method + + def get_signature(self, key, value): + mac = hmac.new(key, msg=value, digestmod=self.digest_method) + return mac.digest() + + +class Signer(object): + """This class can sign and unsign bytes, validating the signature + provided. + + Salt can be used to namespace the hash, so that a signed string is + only valid for a given namespace. Leaving this at the default value + or re-using a salt value across different parts of your application + where the same signed value in one part can mean something different + in another part is a security risk. + + See :ref:`the-salt` for an example of what the salt is doing and how + you can utilize it. + + .. versionadded:: 0.14 + ``key_derivation`` and ``digest_method`` were added as arguments + to the class constructor. + + .. versionadded:: 0.18 + ``algorithm`` was added as an argument to the class constructor. + """ + + #: The digest method to use for the signer. This defaults to + #: SHA1 but can be changed to any other function in the hashlib + #: module. + #: + #: .. versionadded:: 0.14 + default_digest_method = staticmethod(hashlib.sha1) + + #: Controls how the key is derived. The default is Django-style + #: concatenation. Possible values are ``concat``, ``django-concat`` + #: and ``hmac``. This is used for deriving a key from the secret key + #: with an added salt. + #: + #: .. versionadded:: 0.14 + default_key_derivation = "django-concat" + + def __init__( + self, + secret_key, + salt=None, + sep=".", + key_derivation=None, + digest_method=None, + algorithm=None, + ): + self.secret_key = want_bytes(secret_key) + self.sep = want_bytes(sep) + if self.sep in _base64_alphabet: + raise ValueError( + "The given separator cannot be used because it may be" + " contained in the signature itself. Alphanumeric" + " characters and `-_=` must not be used." + ) + self.salt = "itsdangerous.Signer" if salt is None else salt + if key_derivation is None: + key_derivation = self.default_key_derivation + self.key_derivation = key_derivation + if digest_method is None: + digest_method = self.default_digest_method + self.digest_method = digest_method + if algorithm is None: + algorithm = HMACAlgorithm(self.digest_method) + self.algorithm = algorithm + + def derive_key(self): + """This method is called to derive the key. The default key + derivation choices can be overridden here. Key derivation is not + intended to be used as a security method to make a complex key + out of a short password. Instead you should use large random + secret keys. + """ + salt = want_bytes(self.salt) + if self.key_derivation == "concat": + return self.digest_method(salt + self.secret_key).digest() + elif self.key_derivation == "django-concat": + return self.digest_method(salt + b"signer" + self.secret_key).digest() + elif self.key_derivation == "hmac": + mac = hmac.new(self.secret_key, digestmod=self.digest_method) + mac.update(salt) + return mac.digest() + elif self.key_derivation == "none": + return self.secret_key + else: + raise TypeError("Unknown key derivation method") + + def get_signature(self, value): + """Returns the signature for the given value.""" + value = want_bytes(value) + key = self.derive_key() + sig = self.algorithm.get_signature(key, value) + return base64_encode(sig) + + def sign(self, value): + """Signs the given string.""" + return want_bytes(value) + want_bytes(self.sep) + self.get_signature(value) + + def verify_signature(self, value, sig): + """Verifies the signature for the given value.""" + key = self.derive_key() + try: + sig = base64_decode(sig) + except Exception: + return False + return self.algorithm.verify_signature(key, value, sig) + + def unsign(self, signed_value): + """Unsigns the given string.""" + signed_value = want_bytes(signed_value) + sep = want_bytes(self.sep) + if sep not in signed_value: + raise BadSignature("No %r found in value" % self.sep) + value, sig = signed_value.rsplit(sep, 1) + if self.verify_signature(value, sig): + return value + raise BadSignature("Signature %r does not match" % sig, payload=value) + + def validate(self, signed_value): + """Only validates the given signed value. Returns ``True`` if + the signature exists and is valid. + """ + try: + self.unsign(signed_value) + return True + except BadSignature: + return False diff --git a/venv/Lib/site-packages/itsdangerous/timed.py b/venv/Lib/site-packages/itsdangerous/timed.py new file mode 100644 index 0000000..4c117e4 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/timed.py @@ -0,0 +1,147 @@ +import time +from datetime import datetime + +from ._compat import text_type +from .encoding import base64_decode +from .encoding import base64_encode +from .encoding import bytes_to_int +from .encoding import int_to_bytes +from .encoding import want_bytes +from .exc import BadSignature +from .exc import BadTimeSignature +from .exc import SignatureExpired +from .serializer import Serializer +from .signer import Signer + + +class TimestampSigner(Signer): + """Works like the regular :class:`.Signer` but also records the time + of the signing and can be used to expire signatures. The + :meth:`unsign` method can raise :exc:`.SignatureExpired` if the + unsigning failed because the signature is expired. + """ + + def get_timestamp(self): + """Returns the current timestamp. The function must return an + integer. + """ + return int(time.time()) + + def timestamp_to_datetime(self, ts): + """Used to convert the timestamp from :meth:`get_timestamp` into + a datetime object. + """ + return datetime.utcfromtimestamp(ts) + + def sign(self, value): + """Signs the given string and also attaches time information.""" + value = want_bytes(value) + timestamp = base64_encode(int_to_bytes(self.get_timestamp())) + sep = want_bytes(self.sep) + value = value + sep + timestamp + return value + sep + self.get_signature(value) + + def unsign(self, value, max_age=None, return_timestamp=False): + """Works like the regular :meth:`.Signer.unsign` but can also + validate the time. See the base docstring of the class for + the general behavior. If ``return_timestamp`` is ``True`` the + timestamp of the signature will be returned as a naive + :class:`datetime.datetime` object in UTC. + """ + try: + result = Signer.unsign(self, value) + sig_error = None + except BadSignature as e: + sig_error = e + result = e.payload or b"" + sep = want_bytes(self.sep) + + # If there is no timestamp in the result there is something + # seriously wrong. In case there was a signature error, we raise + # that one directly, otherwise we have a weird situation in + # which we shouldn't have come except someone uses a time-based + # serializer on non-timestamp data, so catch that. + if sep not in result: + if sig_error: + raise sig_error + raise BadTimeSignature("timestamp missing", payload=result) + + value, timestamp = result.rsplit(sep, 1) + try: + timestamp = bytes_to_int(base64_decode(timestamp)) + except Exception: + timestamp = None + + # Signature is *not* okay. Raise a proper error now that we have + # split the value and the timestamp. + if sig_error is not None: + raise BadTimeSignature( + text_type(sig_error), payload=value, date_signed=timestamp + ) + + # Signature was okay but the timestamp is actually not there or + # malformed. Should not happen, but we handle it anyway. + if timestamp is None: + raise BadTimeSignature("Malformed timestamp", payload=value) + + # Check timestamp is not older than max_age + if max_age is not None: + age = self.get_timestamp() - timestamp + if age > max_age: + raise SignatureExpired( + "Signature age %s > %s seconds" % (age, max_age), + payload=value, + date_signed=self.timestamp_to_datetime(timestamp), + ) + + if return_timestamp: + return value, self.timestamp_to_datetime(timestamp) + return value + + def validate(self, signed_value, max_age=None): + """Only validates the given signed value. Returns ``True`` if + the signature exists and is valid.""" + try: + self.unsign(signed_value, max_age=max_age) + return True + except BadSignature: + return False + + +class TimedSerializer(Serializer): + """Uses :class:`TimestampSigner` instead of the default + :class:`.Signer`. + """ + + default_signer = TimestampSigner + + def loads(self, s, max_age=None, return_timestamp=False, salt=None): + """Reverse of :meth:`dumps`, raises :exc:`.BadSignature` if the + signature validation fails. If a ``max_age`` is provided it will + ensure the signature is not older than that time in seconds. In + case the signature is outdated, :exc:`.SignatureExpired` is + raised. All arguments are forwarded to the signer's + :meth:`~TimestampSigner.unsign` method. + """ + s = want_bytes(s) + last_exception = None + for signer in self.iter_unsigners(salt): + try: + base64d, timestamp = signer.unsign(s, max_age, return_timestamp=True) + payload = self.load_payload(base64d) + if return_timestamp: + return payload, timestamp + return payload + # If we get a signature expired it means we could read the + # signature but it's invalid. In that case we do not want to + # try the next signer. + except SignatureExpired: + raise + except BadSignature as err: + last_exception = err + raise last_exception + + def loads_unsafe(self, s, max_age=None, salt=None): + load_kwargs = {"max_age": max_age} + load_payload_kwargs = {} + return self._loads_unsafe_impl(s, salt, load_kwargs, load_payload_kwargs) diff --git a/venv/Lib/site-packages/itsdangerous/url_safe.py b/venv/Lib/site-packages/itsdangerous/url_safe.py new file mode 100644 index 0000000..fcaa011 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/url_safe.py @@ -0,0 +1,65 @@ +import zlib + +from ._json import _CompactJSON +from .encoding import base64_decode +from .encoding import base64_encode +from .exc import BadPayload +from .serializer import Serializer +from .timed import TimedSerializer + + +class URLSafeSerializerMixin(object): + """Mixed in with a regular serializer it will attempt to zlib + compress the string to make it shorter if necessary. It will also + base64 encode the string so that it can safely be placed in a URL. + """ + + default_serializer = _CompactJSON + + def load_payload(self, payload, *args, **kwargs): + decompress = False + if payload.startswith(b"."): + payload = payload[1:] + decompress = True + try: + json = base64_decode(payload) + except Exception as e: + raise BadPayload( + "Could not base64 decode the payload because of an exception", + original_error=e, + ) + if decompress: + try: + json = zlib.decompress(json) + except Exception as e: + raise BadPayload( + "Could not zlib decompress the payload before decoding the payload", + original_error=e, + ) + return super(URLSafeSerializerMixin, self).load_payload(json, *args, **kwargs) + + def dump_payload(self, obj): + json = super(URLSafeSerializerMixin, self).dump_payload(obj) + is_compressed = False + compressed = zlib.compress(json) + if len(compressed) < (len(json) - 1): + json = compressed + is_compressed = True + base64d = base64_encode(json) + if is_compressed: + base64d = b"." + base64d + return base64d + + +class URLSafeSerializer(URLSafeSerializerMixin, Serializer): + """Works like :class:`.Serializer` but dumps and loads into a URL + safe string consisting of the upper and lowercase character of the + alphabet as well as ``'_'``, ``'-'`` and ``'.'``. + """ + + +class URLSafeTimedSerializer(URLSafeSerializerMixin, TimedSerializer): + """Works like :class:`.TimedSerializer` but dumps and loads into a + URL safe string consisting of the upper and lowercase character of + the alphabet as well as ``'_'``, ``'-'`` and ``'.'``. + """ diff --git a/venv/Lib/site-packages/jinja2/__init__.py b/venv/Lib/site-packages/jinja2/__init__.py new file mode 100644 index 0000000..42aa763 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/__init__.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +""" + jinja2 + ~~~~~~ + + Jinja2 is a template engine written in pure Python. It provides a + Django inspired non-XML syntax but supports inline expressions and + an optional sandboxed environment. + + Nutshell + -------- + + Here a small example of a Jinja2 template:: + + {% extends 'base.html' %} + {% block title %}Memberlist{% endblock %} + {% block content %} + <ul> + {% for user in users %} + <li><a href="{{ user.url }}">{{ user.username }}</a></li> + {% endfor %} + </ul> + {% endblock %} + + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +__docformat__ = 'restructuredtext en' +__version__ = '2.10' + +# high level interface +from jinja2.environment import Environment, Template + +# loaders +from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \ + DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \ + ModuleLoader + +# bytecode caches +from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \ + MemcachedBytecodeCache + +# undefined types +from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined, \ + make_logging_undefined + +# exceptions +from jinja2.exceptions import TemplateError, UndefinedError, \ + TemplateNotFound, TemplatesNotFound, TemplateSyntaxError, \ + TemplateAssertionError, TemplateRuntimeError + +# decorators and public utilities +from jinja2.filters import environmentfilter, contextfilter, \ + evalcontextfilter +from jinja2.utils import Markup, escape, clear_caches, \ + environmentfunction, evalcontextfunction, contextfunction, \ + is_undefined, select_autoescape + +__all__ = [ + 'Environment', 'Template', 'BaseLoader', 'FileSystemLoader', + 'PackageLoader', 'DictLoader', 'FunctionLoader', 'PrefixLoader', + 'ChoiceLoader', 'BytecodeCache', 'FileSystemBytecodeCache', + 'MemcachedBytecodeCache', 'Undefined', 'DebugUndefined', + 'StrictUndefined', 'TemplateError', 'UndefinedError', 'TemplateNotFound', + 'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError', + 'TemplateRuntimeError', + 'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape', + 'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined', + 'evalcontextfilter', 'evalcontextfunction', 'make_logging_undefined', + 'select_autoescape', +] + + +def _patch_async(): + from jinja2.utils import have_async_gen + if have_async_gen: + from jinja2.asyncsupport import patch_all + patch_all() + + +_patch_async() +del _patch_async diff --git a/venv/Lib/site-packages/jinja2/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d33331faf4c945d2f439f2a9744248a8a66ef2f GIT binary patch literal 2541 zcma)7TTdf56rPX(AtZrtzhkZ;ED5_?sg)2#EtgfdUC^rRN|hUR<cv)+i#-$B9!hpg zU)tZ%_x^zXnR)F~|3aU7>`5T3`mhttm+@zxbL?|I+n*H*x!b>9x}WD9=WnO)*?{k_ z;8nGh<4^}XG=)=i01wbK&G59y;EWi=gDD5vgM3H~<6)7-Suuh~L=NY~C>|AgoEHUL z5Jg<H<A(T{7{}wHgiB%qPgri4Pl_o#C8qJTn87n*7SCEf%jd*Ao)-&vK`i1$v4oez zGF}!dctxz@RXb*cBe90p#5!IV8+gNVIld{j@Rr!d+oFuiVh8Vt3a*G<yeppICt?rp z!MIVHrv+N1V|1LB=medlQ*@fn&{;Z1=jj4nq)T*}uFzGA=o($88+4Oy(QR6$JG4S~ z=@Ys~tIZ8ur8T<$yMt@2_M4M(eyq`_mrmsw`D4IwatNMxe(;Vwx1Yb-CuhIEwKVb- zB1kjQ=0r2Zf~FrZ^t1G}W&!d8)Q%)WuRFRG1~r6UYSfnDjZYbvSvRNeNYD&nq-y(; zQ51wh^@sCUNOb~DZc!uBNJZ^-D7At?+<0fVZON4K!vLHF)Vd)73fmee!jS^95#E9` z7TowU3<L{wEt&O2q*aS?ZjGwlC+Yfz!AuEKf^ZHo2rP-A@CvBzQhTdBI7kdkaz1Rq zP*^HesN5im)mmEcGTOSc0lFg^JoK)S_BA-%x;tm0!6f&UHpVbj+xveOUKoID-R}*d z!-yZ<JGT?K3MGmZlP2BfUp+Pe6Yd`!64a9H>dW;HAMBu7BssdfTR*yQgJnPj1M+a6 z93Ae1(c?HUR}-plrr>(Ne>xv6C`r?S7q&anZ?^OS+VLvr`O{}#phgGj7E3aV-Y_C+ zHr4_6Jr*c-fQ~Ou_mE-?y?S|ac5!)zOlT28i73;==c<-_KM69>GV(NNjB2O@r1O2| zdF|QLf6OsZ8Hh*E9uyE~-*jbK$;P>3DAucxP$uK?7d~f~9R>C6GKJT~yC%)vfZVC? z>26o?MHF~OXx(=HHDuMl?X^x?A*`U+IuB{YyRAw(E*y6>^Fqo_h}UBA>_gng9Wx*i z&kUiD_(~=oc^gm&7cd$ZPgx^sKD5U!wS;Lt5Ho^YGna==*pf{*>KRss<3cYHXHteT zF5cTD?eSjsMW|ndU>KKrjJl^Ld(@@fsEOrFkNjGJnoO1weyYcR9|hVM%(9S>Qr`x> z^0|g$6s+iPb+=h!H-tZ;pm;;)L|#Yjc!(*Fv{{_@I3v=vDO2%ezte89fb8?rBgcA< zdqmz>4~dFPigD&?mqa>DB1t$C9ho+eF)(Og$iT3Htbq{&a1}W+Z=hhHXkg61xPg*^ zNdr>`rVY#(m^CnGVBWx}fdvDL29^!17(fQr4Xhg2FtB7`&A^0#O#lyG`<Q8)_XJ+G z4M5=@<c>()!HNG1xPo??4~X<#3fd`i4^Rhgfy{-3tJA5Z;$n;3Fqgoo?72-A05j4i z+H1LRSb{F|$t6*XH0(^d1vYrHOIe%l5iU*a%77hUzyEaP4p9I8baGICYmTmZr>x4I z`YBV_I&9aE!|=L#P6F7A<`AzYyR<3@Y*tynflAl8->55JvuZopv#Op1uDh-u_}X=A z?M|F`yR$V5_zof}Q!6KxPi5ehyYPKc*^h@^mxi9}#s&KUM~)!iAs%(z8<-sE*>&+m zcVT@pd(Z_gc6+%!ThyoM#FY8ouHji3e<Ys!ZkcSLkHut4j!V6X-FnR4h3XSTcJDda zaH>ylR#9c}+^0ONH)J>~HLF1@KchF3KhT5g+JxH5_Cdvr$Q}xNCLS5*ss@mO5@gcj h>7Bu;%v@%3aB5&@Fq_JzhTvy#AeBp6ne6aT>OT^D#ialM literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/_compat.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/_compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a52c1709abea8e248d060f99b2bc8d89cc6fd0d GIT binary patch literal 3351 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 G$^QZCVhI2M literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/_identifier.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/_identifier.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90a8de173919f6f69e0b08733fedcb0da3c14bab GIT binary patch literal 1854 zcmYk7&u`<#9mTylv_XOV9YGG$LwhTVqG)^Rp}hpX6aoUbs}{~?y$j0;y6A->X-R9D zwdnOqmTgJ2s9z*SlN3c$mT3v-x$Pl`bv90u@5r~gh(K;R<*}y<_{@9r{l4K0Nf7$! zd++_|Z_#f*TKr&R<6-0fevjaP2@3CGc)an)jmM8Z-1y*~PydeKKjVK%?BCvh%AYIj z^YR;=y}5m}yer<xck12VUHOX|`{ML%mA&n}_3!KK{`|hf?tAyc`_aA4?%l8SuYC63 z{jUe?YyTUEeG`&VXJj<VID?pE^dUnsEQm`NO-2`IWF9jzFUT|*^%UyOAqn*sjQWSv z*C7q+cd2jEpvY*T&>*1!x<i$QWg1p!Sj9x6VS|QU8k#ghY^2aAp%G%1Ojen!3R#F* z8d*A7K3M@-5!p!E3fT$SNIHAulp!z4LDbR6>5^m87@@I3<AlZtxhlD3ax3H_<7(vA z$!(C^CD)`&nJy9WWb#Uo3VHhwjl2f=5+h$BAMHS;0PUbc0os8^!D|ZY6m%&tX@bor z3QZE4AR4L^A{tgGL^RYWd`)4U!Y+j-O%a_cG(|2_DMBu)P=s8hQPiNQOOZ)eGF>SY z?^ArpC_bjRPVos`hvEsvA<Y7sO&QJRbd6iT{s+winxinkruhxc6S}D|y3r^(p`=Mk zL`h7^H6;s5mUJ61TBx)r(V|QX4U+~f@J<&AElXT-7?&2jSZBQ0<i$3`;KeSa$BRRV z%ZmYI%4L~xS>y5{mrvnZTx&6|b@=%a<LAfxd;*#B))8;L;;jj9g}gmwyz`28THL5I zZfLx_$GauowRq29yyx(q54qu^2IHeMZoOvQYI6%cE8*6X+i2Ujd@M0OR{0oLb5-u* zz;4JdQM{D-<psp$-hgp0<bIEFKjvYL@$iU;$B+>ZT^{0W;gp9n9$xbpE8~dAH<0B@ zTCR%Hs(8FoYHX#Puat|G(uea`>d8vIT!kK6g~2LXu403&;^l*6vj@q0ked(ksc3ql zIc1`GD^A<uv?I=ZaTW+e7Dh=JRbiM+7(>yOM7Joq2%3AsG$4WK4VdV;qF-jB|4Ixb zCI*TagiH)2G3<+x#l+}JSc<R?gry1Vh6(Fd*uJnQOxROl$HJZodm-$laEiilm~cGd zM8dfe9=7#t;f;mouE*{5Ok(TV;d*93y!9+x&!+2ZjjgW_*U2)im2cBp)ktgF6~i!3 zYxp!VC}0niz(H0x1hdR!5v+6iV36vP#4yMJUA<TWOBfjy+0V>8)zvJcqM{BnGfnlq zFx5+0wS;OZgegm4KQjlZUKztwVz5Z{Y6p|5o$3eKf&&F4V1bFIf-<OpYF5xd19X82 z23c_ova@K|xfa3bIOb7Sd4;RhJ(zj~vO8$t9F23(O7)X0(nR6ZfjPx_&Ra0&POAIa zBL50xi~MD(PmW+FLttg*Dy=(MUU#NxeVmywDz`e!?INu&4q+BY>Di<O+G%UA&oEfP z&CCScz~J$;N_&~90y{Hq+IlI&yj0WH2v4Lnx&m3)8iOcp+3473DA*lvkr^*-XD_NP zl|UskCjgnYbOB7z19)0($xhp54aTge?Y;`r-~ZOYEBw|dd%yqS(U(7bzPY=b-`Tb{ zcHY6C)BlF=Hh=xI+#d@0okH%#`-S&k<bIPcJlTErJol?-&z`W~ZEkP=HUBify~{p) zQP|BtWluLDJGsyD+n?n=+4@tiu(g|K&o}@41i3=)<E`zFH-D1*XzMYSZ2e^`zw_hg WFTQ;D=TDzK{`8alFS1<=VB>odIClvE literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/asyncfilters.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/asyncfilters.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..defcff9c895f9de7bb443c16b8484604b34dbe74 GIT binary patch literal 4822 zcmc&%-EJGl6`q-0{*$sr*^c~ETX7OQEMmd6(WY=!HI5zE1`-CaTssyR6D!V2CPnU2 zvrAhPmgoglSiWe1-uDIi0DS^KKrec?x4p_M<f`8}%Rf_Yk)kb1VrFO0%+8$io!_}x zD&_b8@}PeGq!51>GruCnpP>cUr4U+Z*%lqSDJ6|nTXn2Wi^o>m?xZ$T7~48^C^pkN ztuuILbXMo^%<8-@;F)uCZmN|ZN?p{YLot+_d8QOVS<ok#Qea9El#g^-FC9v(Dl)AE z+9`dSSCyEu0LmHt9#a;WasrfPeU>RFm~s-7bNW0}PBLW?lneStOj%?~8I+6q5>v`d zSpwyXUeWLK`AbYY1=>}8S%1K^Q*PcZxJ9?*F1RP$vU}29beG&y`oljJHc#ut4N>{{ zJA$$z<J3#zbOQ|XPA~N9PBU~3-ZI<GHby}M;PTHxPmsSs3tH%Q#6X530`Nm+s{3n^ zxB=+zi+=GYKL_fG@XpvGl)HIzOA7iw6;H(8_BSGuEfuLDl(n}^6mNO|OeF4#Zx;q? zX714wtOT>F8t3W>YxR1?iZjohzzIVWmp4McdpqcRjR#=X37ftbr=4!s^)!~HUO4TZ z8(V?97u%4l^Jig5gzvZR+^YR1fONI~)!=HscGnGdu~F@F-`~CVmE$=(ZpZb)Ya9Il zUtQ}sc$nG?*LzWGH@9j5_PW+}8oO8*)LKoi<=m(_#Kj1-)owp7Fq}XXr)LPW1$08% zGA~zUS>@$|G(>9HM8}U75qEbK7H?q?ik6H3K%^pTXxG%h9;71MJlnq;s>mM7Ik?j! zxGlhae+KSnfIAhXhtgb$(vP8py(@tHnHB(6Rx7q42IA$eal4Lj!EW1agmvH4joZS= zkm3z7SINY-V|Ic#z59}%nU1fwdtM_hv;kbNTPK~EJZ)za-4y6kk}2s*G;@Z5I(AGM za{(<Sh)$y2kXdO?<Gleq_(zPS(C8V2=(K!oh2ltQ1sk-?MQv&OP`s8z_de#C6wMTc zS?9O(_)TYCTX26~9m*qE;w$k=zEZEOZCmGb{xCa~19fDXmd-?~uDG`%1;`53d+{PH zp=o@Nj3iO{#7)Ruh~W)llAs_5IV)O(wyGMMhIeI~L7j1^NQ1a`45aK@+v#j+XZ<2= zgC-6x=$_L~4qIJo`wgcZtYeTY14pp+hVO;${)nTv^w#U6cXLRHk2!25u9onq;=+uO z<~^)?H?L+HQy-!+n+**3M@pi02`#ve?g+L#0(F^A)Z%yQtQd$$nJY)&A(DT<`A2Fz z0$Se@T5LKMD}iYdgjjA>?6{biQ343F7neNWbewiQp~XcCY<0#Z+@CSP4AHx;xk8+= z#i=apt%#PF2j|}f^XLm4o7r(gwsO+3IZN!6XFUIfW15Z^f?bl^koL^`)X_O`H6{?2 z$@~h9*emkWW6y&aoI^*RP=ptx)dGTrEwk!e&c<6>8Nz#_P-0~acR<$kN^HXiv^vZV zB*#yyI$2DR(&8mFwof<)&>xa%BN|BF8Wt+I$xMCI+-Z7Fdj`Iob{jrIt#5u#Td;G= zgO84bFtyUj7YPhdIKDJjsbgFanP^Nvm=Dl>iWYEKltsmqNerOlD5|`nB{$IZ%g8}U z!!0=!4><2^2;>}vAoxiV1S8S1;P)`DZ!phpcyPJuuWUC>5Ux;K_+$k!y61WgcjcuM zthk+S*k6UV2q#WF9=xhv=O7s8dY*Q-o1UvH%8;%&;3WW&G>Ja5+J_zDfM0?XWYGzw zWSN5gk1=l0*7PGO(`YQuEeu9MUk%j&TtkOMQK1#uBxNFDWNGX7loZrmxYStciapgb zZBN_&4J{yWcc9~MFCvE^tnm>NIRs%Z`FN=QsP^o{0<0t-6zab=ey{te*KNC;r;tz8 z>h;*#b^CFe6KgOh^!NF-b~6YYey6*BpAIHvrRBjzV6j7+ZS|)%F<lCqyM~38RC4f% zj<f4>CV{IV?ZV}fZ{u{}o6yw>H+F)34qwJO^2p{^FLYzu-S3(?zT*6pfiH-!L>)z$ z#^3yub`aG`8YYcUmSK&M8zve7TvPlxA-@Gz21m5~rZ-|P&Qjc|HxPK3L#fS8Y&FN2 zAw&NPE%747?{9f3A;^qSr9j2jSYu;vB{~44L*_P*i6zp<L(Gj2APZ}t@sBnqN&ku# zQ2wO@q9k$%jgkY0%-9q~4r&{;oQx|%xx#$#$rLk2VRqsMoEx6fdNhI9>W_+wSvyEF z*gZ%=*&a^C36UcGyhORl_fIf8mT3*+|CvnY7svTvsh$xBXyV|~gex+`<B~o40g{#8 z+M`B$lXiqM6hHne(ve+9Z|Uow%0}l+HI@{F$L0E=8cV)+XwQ*M>;DIto=;>VeT{hf zkMuR&V|zxnU(L!!-bK#vH3SbfF&IU6+?WuG5#dq%Jl_8-3B_~eyggIS-<>JvAETVN zsQl+r{xVAW$8)88VO+|0obF8Zo|<tiDoo8O>fTkNr-2Z@-NpPURwf0xxedxBP>$~_ zZ)jNyU1SaHNNKT~Gv`KvQW{Hn*(8w-Qv07LVRTN^*_+G58Byt?XeW1<PsPCAyG~cd zh{DobK{E+U|AvwGiET7-uuKNg>m0Z6(cQ>U`8oq+hEoq}c)D_KAwAy2e4GkG$Aqym zFa9;KIEju;u7e6yom@cuc>}e9q2x5F3@p5mI1De<*aRuyo1_4`7!Iu@5sZ>HRUJXQ z*%)W+B>AM76vR0c#_jscX83$Au$p_={<mmj2c`E)Ax?1xV}63S*rx0j+muk^baFF{ z^U0IKUTnc&kfHhV80V+Hf!lYl18|5d#P*+Iuj=$7RIQu=TevEQzTXZE#fNw)xlpfi zMH%$EU1a^F6iX*FTqkg%<&41<D_4nJT5&?>Olm0G8A=ZvMGb`=L;lHj%xJUZaq0HP z)9Ri2y$4@D`r_fn3^<cS7H8LR*X_04buzVpb}Fi(ELzz@HkZASO_#IfY*rTSa%%FY P_X1i<N&Z`-C%^pzn+np% literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/asyncsupport.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/asyncsupport.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d106923d75c407a907a2fcf551170984bddc734 GIT binary patch literal 8171 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 Jae49F{{n^03bg<L literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/bccache.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/bccache.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..592061e4bdc155cf865b52a8184ad3250d07175d GIT binary patch literal 12727 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/compiler.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/compiler.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..01ac1deb7abab927085fd7a8f492a8d40e642efb GIT binary patch literal 46908 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 MNc2s^<HPCy2lMMhNdN!< literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/defaults.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/defaults.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7263817226103e41f5f0cc80e955bd884ced8f1b GIT binary patch literal 1467 zcmZux-EP}96c!a(wiP>Z?4<3wpK2(I&cGtH-NlL^DCRgy;ns07%jpKfo1lt}D@T7L z={8oA-DHo^7Z|YH?O}MktG+_7Mk-d}0;9llsE?n%b9{K{@9K5q{O_K7UQ(2Qm8DS; z>mS6V{|JK=s33sU5JW0efn2YIE0G4Zs02%q4)v%E%L3P61(jeG>CixBxQZ&UhN`fR z47eudI$DJdF*i^RHid5qoqD(#ZNVG4=300&x&=Q23RdxLtl<*YaT%?@)8Xf+fi{qV znqsz4BLHX>ZN4kRFYuRW3*Gob!Cze|(>qtnzH$Yw05p-|DD9isUxq2hG>E6(BMaeE zZx%9|W50hoj2wI>iROZ5o(3VqgqmKAOy*6fbxqpwlXOmk$t$*Fe&e^zM-Lu;Z@!$H z>=ibz1xDD5tXyy>4163@ykqV;``?-rW3%7u+5^WnPZMHB3Be*9^MX*QzZaEO+JGCx ziznD+^Axu=zB$1$CLY6XDA<u3q;wW>y*u|qOt}6m3B)$94!j7{)bnwszP*>J_b!Eh zo2i%5-_6v!mxBWi453~===DeT&=Jeh$aY3fTj!-bBG*vhGpL7o2&nsc2&hjNA>ty$ z2v*`GNo8K|Mc*jAN)y7|Gd!p5a@O>dC=En%mPh>{a4931F(YA+VT<4}t1s5fBNNQB zbw*~fFMgjDV*JLNXRRqsVhhcpl)9&4iv>xXZ7hLjZ^V~0Yv6Nc1rHy`79&9vVdMi@ zKrz(C^o6Ilg}G3EX<mX0FjcM;qF#U(3Q&Giks^8o3}j6LI!jYb+A3GA2OOOLsgS1l z?!&L$o$(13O)#F{r}yXMeN4|-l8*P1<ZS!Ui@gbsaLl%yIb}H7jy&PW_%)7SkHg^Q zm<9}Qi$ggRJUYH^!*S7uRyzMs-Ib?%o{}ezWv()DO(C1YwZG<N*F}l%x_oWXz^<&U z$B3ly+5((0(L7{DKtz*99thDTqq)~V>OOa!QD-<3cGw#{<&A619_$y$){D-t*V*ga zpAuUi;U(^7_vrA@9!Rw%&1QivF}M1?L9RHm^AC=P_CfDw-pb*EzW`T7YKLx~n8WLR zndx!y+8EeBT}#<Np4fw~&9|Q0_OUw}c6tJT>Mr&_)@X5QiTwZK>-+XW=cGS!7d;P) zqA^SvB~{6(7o7YukX4Co6xg71XgkLp5w=l4$4BA@_iTrk*ene(*Tcm3LdrGS{_@Pc xFm&C#eU?m1%2sxx1kFPH#1L<ER~QY{z%5M!|NCe)P*YV<JQclIsXzyn(tlAhrgH!Q literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/environment.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/environment.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8366404804194c5d5c79cea745457fae28dfbefa GIT binary patch literal 43278 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 Cg}B22 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/exceptions.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/exceptions.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6d3971270c9a3d262ff1773edce98cbc61b997a GIT binary patch literal 5027 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/ext.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/ext.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4df8e9d042c475519c5cd7795c2eb5a040074cd GIT binary patch literal 20118 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 RYnhcB)r-Zi!owo}{vW^vC$0bh literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/filters.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/filters.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c00942a0e80de52e4f9f26049f9406944082451 GIT binary patch literal 34425 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 QAI|R1?#rUKznFjjAFUQA<p2Nx literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/idtracking.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/idtracking.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10810b9ba546cbe8cb8cf7fc5688c49d31ade2ff GIT binary patch literal 9956 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/lexer.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/lexer.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b70c988044c1f48c40840f1f636f6589604473a0 GIT binary patch literal 18580 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 bfsV#A`*FqGgXchhZ~rN1KZAJgW`_PB;YN6! literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/loaders.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/loaders.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d9eb3d0c51de0d2ff9fb48acdc28199c524495ef GIT binary patch literal 16587 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 Yl5=VySh%(D`oe{UFD<;X&{(kl7yb`;r~m)} literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/nodes.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/nodes.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07629c0c157a747bc8dda70c6deae5e86ab5e4c8 GIT binary patch literal 36675 zcmcJ233MFCdFI?R7=R!|L6k^IREy$)k*KjK$&w9AGC+{9C`+JBQl<n^nQ2TnzySs` zV08}(i~!3P<VcFI_)dJpiety|W{(`+_wu|>;&_v6lFcUB_wtfWXXDMj#M#`t*}UB( z%5HYQ@2|cF2d$xO;B`$`b=URRU;kBqRo}B?M{)UIpRBxhIg$9IMC`8*_hY!6OW8!i zN=zrLq?KwUr<3xVnoi+2-AFHHrZY)C%QUi!x#^tTXB+v&!gOJ=Z@O=>I9*)qpYBg4 z?Bx6at{t<f=|MX+zZ2IGu4{1Z!gVdKVO-Z)xzFUMcjNAQTsPp_W92`Sp5BYQ8*$x) z>t<a0aNUB-unJb+3kj<zVgF|e)3?4YVGUS2kha6#zn-4H4e>#1C*nKp1Bf3)e8{>6 z@oVfuh#$6gS=YXhm>xm;uyq~Mue1B?gLsz0(-Km5Th}A?db@=9?TFuC?LmBx#P2|S zuXQ8hH%j~n;x}10BYv}eJKnz&@qN}Uh~FadGUA4HE8@3G{0)fjw{AoHHi_Sb_yOx6 z;s+&u6!AmWVZ;wh{BFcYtP<iSiQj|x?baQL-y!jP5kF$xiTIroe<R{$>kWv%LE`rz zewTF=@uL#IAMv}bdl0`z;%`FyUh9pBzft0EM*KeOe#GyW_$cCUvfhmNn<f4h#7C{S zApREV{{x6WU_FTVgZ2aH|AUA>WW5#fw@UmW#K)|0#K$H6R>Y546Npbpd<^m9)(ON< zNPHaeN$YKhzs(*;dB+fc*gA>$Nr_J&{)qK<#NRIQ<A^_MJ%;#W5<h|XlywU6Qxcy< z{Bi3Yh`+<W1LeI9@zd5B#Lr0lVZ_f`Payt;)PK@?r*#h4@d(nNw4OrxQ<DC6#HX#N z5r10Zk0O5FdIs@lB>ouU-(yt}uSk3f@v3DaZc6+V;xkqa@tVXRN8GY(#BGVc1MyjF z4)HmOpGN$GRY$xo?L1?j$t30z*1WYaf7X7&YFLXeB<y$EyVsKl_pT?8F?K6|k0;iX z{J%bNe<GErBv4Y*YN4c-{U9K44)G=HBH|Y%{v_hcau9bU{uJV_)keH6@oB^_S(g#N zEb*riU$#~dUy=BE#5>lrh(9awXAobt)(~Hl`1c_GF6%kOpObh6@poJALHs@TLugMG z@#n2|#MdQmBL0H)Uc}!k@fpOw*Lo507X>mk#9y*rM*L-oTZn(3^*+SkCvh9`_gf!8 z`~wo7Mf`)-hY<ge#ODzIe(S@Ce^}xd5dVnvQN%wg@jBukvp$aa#|7i(5&wksNyI-X z@dd<x!1_VNe^BBL#6M+y8u3p{d=c@_Sf54wvl4G2{zKO15dWOrM1Nb>E7lJK!<Oue z_7X<#U`0K>WTnm|N}o5^l8Hpoz~6kmId9%wZniAjk%V`}{)&=v_Cnn;7F$-kVH@?u zrG~v|H(kdtEvxR<TTQcJNN&TjRsE7}8MCUjXt)<_!*pC@#&m3XUe<-3bPcoNw2URy zx>QG<hSOTqFBq<}ZR3L7ShAHd+iuoaxMMWx3${_MH!F>L({8q^hS{{FS=Bk)tw_yP zqwS#HnH8yZ$y5$Xz-uVcY&s3oZ7HMPG+GKRG~5=NqZZ9Zy<<x`ODpb$R#V;=VCdfX z%^j_^mR3}K?t(jNjMPfT-FF?mKd9&tg3dT=n~P;BXtYtU*-ghDHIAJ*e#CIln@3Mh zoR~Uu!k9%17F)^&j9jzcaLUC$BTJkrCB3X=Uu@gB$vtMOh4xY@<7Mid`jR(zx$a)5 zEFwd#VLFc2=i19|#a&slz4YTx-CfFi$=hE|zM+$P1FqX~d+^od9j_+uETz44v*oIO zRG<bB)KaVqfBfY&fy*f%SWT=Z7ji&|<kXiFrIeR6z2uA<#1jc#eR^W_{1X7CbAIKp zb9m+aaobsNTTADUwOR{zK4vz}Il+ZH®y?Ey$2HaNXFWJpY=Nt8z^G@Bh?_4r# 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 CBu2sj literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/optimizer.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/optimizer.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..949dc92a8cc6094e965014a726b3dab8a3dd39cc GIT binary patch literal 2047 zcmZuyUvC>l5WhWNZ08!gCA5?WgjV9ASRxjb2T)X@<`1okXj&;jLaxPWbGJ^;xw|#H zdrlk~UeZ@S2p@nKKFYoF#8=>n-|YEJRI%3V&F;+3&irO}{AI5fUj6YR`m^nMe|fh? z0RAb+e2ppbMqa`aKV!K+@)_~1td$3&0DO>ay!A#K&%LCbbl!SNM+P^oQ9J1#dxK5! z#`nCCV@%UxDjpBj%%*vIEw$VH^_>yA-IH@!tMj!sPYsVttz}`e1-E*U7H8Zl9;?Dw zQCL1vS;7UM$V)D-W?CAPLgdVeBH_78(g_a2^4{?YPYXWFL@c>c*b|ngmYZ``!p5oO zQkYa~GB4*MD}|Mi4{P7{Wh_b~*On3`4KI`>Ppp7lk!4P3Vl^Wt(_*4@E-c*P<Oy7G zUd1Y(r5Ua{7ly8yq)rH5RB1osmy*Y#Aj64T+g!onj2WmnLzf7D$oVs+5xK}`ndF&L zGhP;2Wf{3ZJ{FHGzf}4H@znv;VwpH)7^C2wy>l^_KoWROB;n0hk*olq<QSEeE+BV` zAQRk3Adqmg01Q{ikR*%{jSG*?tnkof2E>e2S|%%#yAUmOdUkGi`KR%KKmPoSulQ-5 zt?#I8_=yzx(0R9;rLioG+~xbnhr{r1RHrv!)kdKbX~5h)0&}88VF-4qW7MA;EzHCp zfXp@~>n+(0`^8`SQ?~SO7|NAh|3u+YD&-YiPZ2$`Kmj7>$erRdsmU->i5QTGxT#T> z6zAfpAYG)Js;#kgmd<4{9Qf7dtL6n%8}rnpcF?K<^1iwwi+QS5kpuK<b6xtsdwLV@ z{u3SSj(;#xoAKh2d9)ZGN^@b=Y`m}3#m-Anh%-qica9gv%6umm_~<ciosYBhbc~zi z&P>D?&@<!uHICPp3}=g~+pHcEMAQJhR+s5J_{Owo1|j(rWHL<B8!@y8pV|Z3KnqQP z8Uh;yG6>KXKy6S5)CFyVLeL%17N`g6gYJUvf!+gcgWd<-2R#6NFnahVNLt8GN0V3U zlu|m^DGjk$B9Rfg6{YfMz;p+DfIKiVo4AETvYtr4>PAsoq&A8;R)%bO?0S2hp<yFF ziYaIs^d&?NKxQA)(wo+W#1<b}Hua~iC7T9I|Ayha0cks7zrp#ygS2xI>L<*j-x3A) zqnG&bwRL^}@SRqMXG_J>#`~3pa=sr8x>fH?7E-5i<SIv#!_~$C^}wn>(JGJ1!s-%< zPpYmk5sJ@P-G`R5P=zn@nL=kg&E%k^w`d=IA23j#i(-5AE<ORZ)lZ;e$cX2M%x8TT zu#nwj*Zu!+A~k~iLqfC=xs0MJj3RVhyms(gQS@^uvRa~hFr^=0sy1C5(O!}?>~VS8 zurJH3VGq-J)vtA*p}A>Vug<<!mks+;t<CHeK%hSGN;mS?$~5eeJX^EMR=ryF06ke~ z*({$+i${~+S_*4*6|R)g>8q`kQ)!0B)<{ky<)blKB;Vo>=@zo|nl+W5tLBfvm@cNS z-))7x@0r^@8g!~o6eTJKdhPnP*6HrA{z#p9r8O0#d!z2$|6NmcpVW!?hT5LHD_~n} ItJUrN3)f#u;s5{u literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/parser.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/parser.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e3f52db8fe035b87f2d55da940071070e273e2e9 GIT binary patch literal 25352 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/runtime.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/runtime.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6135c964e1cc324e38699dd4cde2a46f67938d7b GIT binary patch literal 24617 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/tests.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/tests.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52cb13e26ded4fa0e50b2106e9338facb7a9b12f GIT binary patch literal 4423 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 L+CPtRd(!{E1kfe` literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/utils.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/utils.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f1305775b371761b3c90505a5a761b36170ea561 GIT binary patch literal 20880 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/visitor.cpython-36.pyc b/venv/Lib/site-packages/jinja2/__pycache__/visitor.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af00e1ce9f7bf01b10d924f7774cc7f92605ecc9 GIT binary patch literal 3363 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/_compat.py b/venv/Lib/site-packages/jinja2/_compat.py new file mode 100644 index 0000000..61d8530 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/_compat.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +""" + jinja2._compat + ~~~~~~~~~~~~~~ + + Some py2/py3 compatibility support based on a stripped down + version of six so we don't have to depend on a specific version + of it. + + :copyright: Copyright 2013 by the Jinja team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import sys + +PY2 = sys.version_info[0] == 2 +PYPY = hasattr(sys, 'pypy_translation_info') +_identity = lambda x: x + + +if not PY2: + unichr = chr + range_type = range + text_type = str + string_types = (str,) + integer_types = (int,) + + iterkeys = lambda d: iter(d.keys()) + itervalues = lambda d: iter(d.values()) + iteritems = lambda d: iter(d.items()) + + import pickle + from io import BytesIO, StringIO + NativeStringIO = StringIO + + def reraise(tp, value, tb=None): + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + + ifilter = filter + imap = map + izip = zip + intern = sys.intern + + implements_iterator = _identity + implements_to_string = _identity + encode_filename = _identity + +else: + unichr = unichr + text_type = unicode + range_type = xrange + string_types = (str, unicode) + integer_types = (int, long) + + iterkeys = lambda d: d.iterkeys() + itervalues = lambda d: d.itervalues() + iteritems = lambda d: d.iteritems() + + import cPickle as pickle + from cStringIO import StringIO as BytesIO, StringIO + NativeStringIO = BytesIO + + exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') + + from itertools import imap, izip, ifilter + intern = intern + + def implements_iterator(cls): + cls.next = cls.__next__ + del cls.__next__ + return cls + + def implements_to_string(cls): + cls.__unicode__ = cls.__str__ + cls.__str__ = lambda x: x.__unicode__().encode('utf-8') + return cls + + def encode_filename(filename): + if isinstance(filename, unicode): + return filename.encode('utf-8') + return filename + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a + # dummy metaclass for one level of class instantiation that replaces + # itself with the actual metaclass. + class metaclass(type): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +try: + from urllib.parse import quote_from_bytes as url_quote +except ImportError: + from urllib import quote as url_quote diff --git a/venv/Lib/site-packages/jinja2/_identifier.py b/venv/Lib/site-packages/jinja2/_identifier.py new file mode 100644 index 0000000..2eac35d --- /dev/null +++ b/venv/Lib/site-packages/jinja2/_identifier.py @@ -0,0 +1,2 @@ +# generated by scripts/generate_identifier_pattern.py +pattern = '·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣔ-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఃా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑅳𑄴𑆀-𑆂𑆳-𑇊𑇀-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯' diff --git a/venv/Lib/site-packages/jinja2/asyncfilters.py b/venv/Lib/site-packages/jinja2/asyncfilters.py new file mode 100644 index 0000000..5c1f46d --- /dev/null +++ b/venv/Lib/site-packages/jinja2/asyncfilters.py @@ -0,0 +1,146 @@ +from functools import wraps + +from jinja2.asyncsupport import auto_aiter +from jinja2 import filters + + +async def auto_to_seq(value): + seq = [] + if hasattr(value, '__aiter__'): + async for item in value: + seq.append(item) + else: + for item in value: + seq.append(item) + return seq + + +async def async_select_or_reject(args, kwargs, modfunc, lookup_attr): + seq, func = filters.prepare_select_or_reject( + args, kwargs, modfunc, lookup_attr) + if seq: + async for item in auto_aiter(seq): + if func(item): + yield item + + +def dualfilter(normal_filter, async_filter): + wrap_evalctx = False + if getattr(normal_filter, 'environmentfilter', False): + is_async = lambda args: args[0].is_async + wrap_evalctx = False + else: + if not getattr(normal_filter, 'evalcontextfilter', False) and \ + not getattr(normal_filter, 'contextfilter', False): + wrap_evalctx = True + is_async = lambda args: args[0].environment.is_async + + @wraps(normal_filter) + def wrapper(*args, **kwargs): + b = is_async(args) + if wrap_evalctx: + args = args[1:] + if b: + return async_filter(*args, **kwargs) + return normal_filter(*args, **kwargs) + + if wrap_evalctx: + wrapper.evalcontextfilter = True + + wrapper.asyncfiltervariant = True + + return wrapper + + +def asyncfiltervariant(original): + def decorator(f): + return dualfilter(original, f) + return decorator + + +@asyncfiltervariant(filters.do_first) +async def do_first(environment, seq): + try: + return await auto_aiter(seq).__anext__() + except StopAsyncIteration: + return environment.undefined('No first item, sequence was empty.') + + +@asyncfiltervariant(filters.do_groupby) +async def do_groupby(environment, value, attribute): + expr = filters.make_attrgetter(environment, attribute) + return [filters._GroupTuple(key, await auto_to_seq(values)) + for key, values in filters.groupby(sorted( + await auto_to_seq(value), key=expr), expr)] + + +@asyncfiltervariant(filters.do_join) +async def do_join(eval_ctx, value, d=u'', attribute=None): + return filters.do_join(eval_ctx, await auto_to_seq(value), d, attribute) + + +@asyncfiltervariant(filters.do_list) +async def do_list(value): + return await auto_to_seq(value) + + +@asyncfiltervariant(filters.do_reject) +async def do_reject(*args, **kwargs): + return async_select_or_reject(args, kwargs, lambda x: not x, False) + + +@asyncfiltervariant(filters.do_rejectattr) +async def do_rejectattr(*args, **kwargs): + return async_select_or_reject(args, kwargs, lambda x: not x, True) + + +@asyncfiltervariant(filters.do_select) +async def do_select(*args, **kwargs): + return async_select_or_reject(args, kwargs, lambda x: x, False) + + +@asyncfiltervariant(filters.do_selectattr) +async def do_selectattr(*args, **kwargs): + return async_select_or_reject(args, kwargs, lambda x: x, True) + + +@asyncfiltervariant(filters.do_map) +async def do_map(*args, **kwargs): + seq, func = filters.prepare_map(args, kwargs) + if seq: + async for item in auto_aiter(seq): + yield func(item) + + +@asyncfiltervariant(filters.do_sum) +async def do_sum(environment, iterable, attribute=None, start=0): + rv = start + if attribute is not None: + func = filters.make_attrgetter(environment, attribute) + else: + func = lambda x: x + async for item in auto_aiter(iterable): + rv += func(item) + return rv + + +@asyncfiltervariant(filters.do_slice) +async def do_slice(value, slices, fill_with=None): + return filters.do_slice(await auto_to_seq(value), slices, fill_with) + + +ASYNC_FILTERS = { + 'first': do_first, + 'groupby': do_groupby, + 'join': do_join, + 'list': do_list, + # we intentionally do not support do_last because that would be + # ridiculous + 'reject': do_reject, + 'rejectattr': do_rejectattr, + 'map': do_map, + 'select': do_select, + 'selectattr': do_selectattr, + 'sum': do_sum, + 'slice': do_slice, +} diff --git a/venv/Lib/site-packages/jinja2/asyncsupport.py b/venv/Lib/site-packages/jinja2/asyncsupport.py new file mode 100644 index 0000000..b1e7b5c --- /dev/null +++ b/venv/Lib/site-packages/jinja2/asyncsupport.py @@ -0,0 +1,256 @@ +# -*- coding: utf-8 -*- +""" + jinja2.asyncsupport + ~~~~~~~~~~~~~~~~~~~ + + Has all the code for async support which is implemented as a patch + for supported Python versions. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import sys +import asyncio +import inspect +from functools import update_wrapper + +from jinja2.utils import concat, internalcode, Markup +from jinja2.environment import TemplateModule +from jinja2.runtime import LoopContextBase, _last_iteration + + +async def concat_async(async_gen): + rv = [] + async def collect(): + async for event in async_gen: + rv.append(event) + await collect() + return concat(rv) + + +async def generate_async(self, *args, **kwargs): + vars = dict(*args, **kwargs) + try: + async for event in self.root_render_func(self.new_context(vars)): + yield event + except Exception: + exc_info = sys.exc_info() + else: + return + yield self.environment.handle_exception(exc_info, True) + + +def wrap_generate_func(original_generate): + def _convert_generator(self, loop, args, kwargs): + async_gen = self.generate_async(*args, **kwargs) + try: + while 1: + yield loop.run_until_complete(async_gen.__anext__()) + except StopAsyncIteration: + pass + def generate(self, *args, **kwargs): + if not self.environment.is_async: + return original_generate(self, *args, **kwargs) + return _convert_generator(self, asyncio.get_event_loop(), args, kwargs) + return update_wrapper(generate, original_generate) + + +async def render_async(self, *args, **kwargs): + if not self.environment.is_async: + raise RuntimeError('The environment was not created with async mode ' + 'enabled.') + + vars = dict(*args, **kwargs) + ctx = self.new_context(vars) + + try: + return await concat_async(self.root_render_func(ctx)) + except Exception: + exc_info = sys.exc_info() + return self.environment.handle_exception(exc_info, True) + + +def wrap_render_func(original_render): + def render(self, *args, **kwargs): + if not self.environment.is_async: + return original_render(self, *args, **kwargs) + loop = asyncio.get_event_loop() + return loop.run_until_complete(self.render_async(*args, **kwargs)) + return update_wrapper(render, original_render) + + +def wrap_block_reference_call(original_call): + @internalcode + async def async_call(self): + rv = await concat_async(self._stack[self._depth](self._context)) + if self._context.eval_ctx.autoescape: + rv = Markup(rv) + return rv + + @internalcode + def __call__(self): + if not self._context.environment.is_async: + return original_call(self) + return async_call(self) + + return update_wrapper(__call__, original_call) + + +def wrap_macro_invoke(original_invoke): + @internalcode + async def async_invoke(self, arguments, autoescape): + rv = await self._func(*arguments) + if autoescape: + rv = Markup(rv) + return rv + + @internalcode + def _invoke(self, arguments, autoescape): + if not self._environment.is_async: + return original_invoke(self, arguments, autoescape) + return async_invoke(self, arguments, autoescape) + return update_wrapper(_invoke, original_invoke) + + +@internalcode +async def get_default_module_async(self): + if self._module is not None: + return self._module + self._module = rv = await self.make_module_async() + return rv + + +def wrap_default_module(original_default_module): + @internalcode + def _get_default_module(self): + if self.environment.is_async: + raise RuntimeError('Template module attribute is unavailable ' + 'in async mode') + return original_default_module(self) + return _get_default_module + + +async def make_module_async(self, vars=None, shared=False, locals=None): + context = self.new_context(vars, shared, locals) + body_stream = [] + async for item in self.root_render_func(context): + body_stream.append(item) + return TemplateModule(self, context, body_stream) + + +def patch_template(): + from jinja2 import Template + Template.generate = wrap_generate_func(Template.generate) + Template.generate_async = update_wrapper( + generate_async, Template.generate_async) + Template.render_async = update_wrapper( + render_async, Template.render_async) + Template.render = wrap_render_func(Template.render) + Template._get_default_module = wrap_default_module( + Template._get_default_module) + Template._get_default_module_async = get_default_module_async + Template.make_module_async = update_wrapper( + make_module_async, Template.make_module_async) + + +def patch_runtime(): + from jinja2.runtime import BlockReference, Macro + BlockReference.__call__ = wrap_block_reference_call( + BlockReference.__call__) + Macro._invoke = wrap_macro_invoke(Macro._invoke) + + +def patch_filters(): + from jinja2.filters import FILTERS + from jinja2.asyncfilters import ASYNC_FILTERS + FILTERS.update(ASYNC_FILTERS) + + +def patch_all(): + patch_template() + patch_runtime() + patch_filters() + + +async def auto_await(value): + if inspect.isawaitable(value): + return await value + return value + + +async def auto_aiter(iterable): + if hasattr(iterable, '__aiter__'): + async for item in iterable: + yield item + return + for item in iterable: + yield item + + +class AsyncLoopContext(LoopContextBase): + + def __init__(self, async_iterator, undefined, after, length, recurse=None, + depth0=0): + LoopContextBase.__init__(self, undefined, recurse, depth0) + self._async_iterator = async_iterator + self._after = after + self._length = length + + @property + def length(self): + if self._length is None: + raise TypeError('Loop length for some iterators cannot be ' + 'lazily calculated in async mode') + return self._length + + def __aiter__(self): + return AsyncLoopContextIterator(self) + + +class AsyncLoopContextIterator(object): + __slots__ = ('context',) + + def __init__(self, context): + self.context = context + + def __aiter__(self): + return self + + async def __anext__(self): + ctx = self.context + ctx.index0 += 1 + if ctx._after is _last_iteration: + raise StopAsyncIteration() + ctx._before = ctx._current + ctx._current = ctx._after + try: + ctx._after = await ctx._async_iterator.__anext__() + except StopAsyncIteration: + ctx._after = _last_iteration + return ctx._current, ctx + + +async def make_async_loop_context(iterable, undefined, recurse=None, depth0=0): + # Length is more complicated and less efficient in async mode. The + # reason for this is that we cannot know if length will be used + # upfront but because length is a property we cannot lazily execute it + # later. This means that we need to buffer it up and measure :( + # + # We however only do this for actual iterators, not for async + # iterators as blocking here does not seem like the best idea in the + # world. + try: + length = len(iterable) + except (TypeError, AttributeError): + if not hasattr(iterable, '__aiter__'): + iterable = tuple(iterable) + length = len(iterable) + else: + length = None + async_iterator = auto_aiter(iterable) + try: + after = await async_iterator.__anext__() + except StopAsyncIteration: + after = _last_iteration + return AsyncLoopContext(async_iterator, undefined, after, length, recurse, + depth0) diff --git a/venv/Lib/site-packages/jinja2/bccache.py b/venv/Lib/site-packages/jinja2/bccache.py new file mode 100644 index 0000000..080e527 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/bccache.py @@ -0,0 +1,362 @@ +# -*- coding: utf-8 -*- +""" + jinja2.bccache + ~~~~~~~~~~~~~~ + + This module implements the bytecode cache system Jinja is optionally + using. This is useful if you have very complex template situations and + the compiliation of all those templates slow down your application too + much. + + Situations where this is useful are often forking web applications that + are initialized on the first request. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD. +""" +from os import path, listdir +import os +import sys +import stat +import errno +import marshal +import tempfile +import fnmatch +from hashlib import sha1 +from jinja2.utils import open_if_exists +from jinja2._compat import BytesIO, pickle, PY2, text_type + + +# marshal works better on 3.x, one hack less required +if not PY2: + marshal_dump = marshal.dump + marshal_load = marshal.load +else: + + def marshal_dump(code, f): + if isinstance(f, file): + marshal.dump(code, f) + else: + f.write(marshal.dumps(code)) + + def marshal_load(f): + if isinstance(f, file): + return marshal.load(f) + return marshal.loads(f.read()) + + +bc_version = 3 + +# magic version used to only change with new jinja versions. With 2.6 +# we change this to also take Python version changes into account. The +# reason for this is that Python tends to segfault if fed earlier bytecode +# versions because someone thought it would be a good idea to reuse opcodes +# or make Python incompatible with earlier versions. +bc_magic = 'j2'.encode('ascii') + \ + pickle.dumps(bc_version, 2) + \ + pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1]) + + +class Bucket(object): + """Buckets are used to store the bytecode for one template. It's created + and initialized by the bytecode cache and passed to the loading functions. + + The buckets get an internal checksum from the cache assigned and use this + to automatically reject outdated cache material. Individual bytecode + cache subclasses don't have to care about cache invalidation. + """ + + def __init__(self, environment, key, checksum): + self.environment = environment + self.key = key + self.checksum = checksum + self.reset() + + def reset(self): + """Resets the bucket (unloads the bytecode).""" + self.code = None + + def load_bytecode(self, f): + """Loads bytecode from a file or file like object.""" + # make sure the magic header is correct + magic = f.read(len(bc_magic)) + if magic != bc_magic: + self.reset() + return + # the source code of the file changed, we need to reload + checksum = pickle.load(f) + if self.checksum != checksum: + self.reset() + return + # if marshal_load fails then we need to reload + try: + self.code = marshal_load(f) + except (EOFError, ValueError, TypeError): + self.reset() + return + + def write_bytecode(self, f): + """Dump the bytecode into the file or file like object passed.""" + if self.code is None: + raise TypeError('can\'t write empty bucket') + f.write(bc_magic) + pickle.dump(self.checksum, f, 2) + marshal_dump(self.code, f) + + def bytecode_from_string(self, string): + """Load bytecode from a string.""" + self.load_bytecode(BytesIO(string)) + + def bytecode_to_string(self): + """Return the bytecode as string.""" + out = BytesIO() + self.write_bytecode(out) + return out.getvalue() + + +class BytecodeCache(object): + """To implement your own bytecode cache you have to subclass this class + and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of + these methods are passed a :class:`~jinja2.bccache.Bucket`. + + A very basic bytecode cache that saves the bytecode on the file system:: + + from os import path + + class MyCache(BytecodeCache): + + def __init__(self, directory): + self.directory = directory + + def load_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + if path.exists(filename): + with open(filename, 'rb') as f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + with open(filename, 'wb') as f: + bucket.write_bytecode(f) + + A more advanced version of a filesystem based bytecode cache is part of + Jinja2. + """ + + def load_bytecode(self, bucket): + """Subclasses have to override this method to load bytecode into a + bucket. If they are not able to find code in the cache for the + bucket, it must not do anything. + """ + raise NotImplementedError() + + def dump_bytecode(self, bucket): + """Subclasses have to override this method to write the bytecode + from a bucket back to the cache. If it unable to do so it must not + fail silently but raise an exception. + """ + raise NotImplementedError() + + def clear(self): + """Clears the cache. This method is not used by Jinja2 but should be + implemented to allow applications to clear the bytecode cache used + by a particular environment. + """ + + def get_cache_key(self, name, filename=None): + """Returns the unique hash key for this template name.""" + hash = sha1(name.encode('utf-8')) + if filename is not None: + filename = '|' + filename + if isinstance(filename, text_type): + filename = filename.encode('utf-8') + hash.update(filename) + return hash.hexdigest() + + def get_source_checksum(self, source): + """Returns a checksum for the source.""" + return sha1(source.encode('utf-8')).hexdigest() + + def get_bucket(self, environment, name, filename, source): + """Return a cache bucket for the given template. All arguments are + mandatory but filename may be `None`. + """ + key = self.get_cache_key(name, filename) + checksum = self.get_source_checksum(source) + bucket = Bucket(environment, key, checksum) + self.load_bytecode(bucket) + return bucket + + def set_bucket(self, bucket): + """Put the bucket into the cache.""" + self.dump_bytecode(bucket) + + +class FileSystemBytecodeCache(BytecodeCache): + """A bytecode cache that stores bytecode on the filesystem. It accepts + two arguments: The directory where the cache items are stored and a + pattern string that is used to build the filename. + + If no directory is specified a default cache directory is selected. On + Windows the user's temp directory is used, on UNIX systems a directory + is created for the user in the system temp directory. + + The pattern can be used to have multiple separate caches operate on the + same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` + is replaced with the cache key. + + >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') + + This bytecode cache supports clearing of the cache using the clear method. + """ + + def __init__(self, directory=None, pattern='__jinja2_%s.cache'): + if directory is None: + directory = self._get_default_cache_dir() + self.directory = directory + self.pattern = pattern + + def _get_default_cache_dir(self): + def _unsafe_dir(): + raise RuntimeError('Cannot determine safe temp directory. You ' + 'need to explicitly provide one.') + + tmpdir = tempfile.gettempdir() + + # On windows the temporary directory is used specific unless + # explicitly forced otherwise. We can just use that. + if os.name == 'nt': + return tmpdir + if not hasattr(os, 'getuid'): + _unsafe_dir() + + dirname = '_jinja2-cache-%d' % os.getuid() + actual_dir = os.path.join(tmpdir, dirname) + + try: + os.mkdir(actual_dir, stat.S_IRWXU) + except OSError as e: + if e.errno != errno.EEXIST: + raise + try: + os.chmod(actual_dir, stat.S_IRWXU) + actual_dir_stat = os.lstat(actual_dir) + if actual_dir_stat.st_uid != os.getuid() \ + or not stat.S_ISDIR(actual_dir_stat.st_mode) \ + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU: + _unsafe_dir() + except OSError as e: + if e.errno != errno.EEXIST: + raise + + actual_dir_stat = os.lstat(actual_dir) + if actual_dir_stat.st_uid != os.getuid() \ + or not stat.S_ISDIR(actual_dir_stat.st_mode) \ + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU: + _unsafe_dir() + + return actual_dir + + def _get_cache_filename(self, bucket): + return path.join(self.directory, self.pattern % bucket.key) + + def load_bytecode(self, bucket): + f = open_if_exists(self._get_cache_filename(bucket), 'rb') + if f is not None: + try: + bucket.load_bytecode(f) + finally: + f.close() + + def dump_bytecode(self, bucket): + f = open(self._get_cache_filename(bucket), 'wb') + try: + bucket.write_bytecode(f) + finally: + f.close() + + def clear(self): + # imported lazily here because google app-engine doesn't support + # write access on the file system and the function does not exist + # normally. + from os import remove + files = fnmatch.filter(listdir(self.directory), self.pattern % '*') + for filename in files: + try: + remove(path.join(self.directory, filename)) + except OSError: + pass + + +class MemcachedBytecodeCache(BytecodeCache): + """This class implements a bytecode cache that uses a memcache cache for + storing the information. It does not enforce a specific memcache library + (tummy's memcache or cmemcache) but will accept any class that provides + the minimal interface required. + + Libraries compatible with this class: + + - `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache + - `python-memcached <https://www.tummy.com/Community/software/python-memcached/>`_ + - `cmemcache <http://gijsbert.org/cmemcache/>`_ + + (Unfortunately the django cache interface is not compatible because it + does not support storing binary data, only unicode. You can however pass + the underlying cache client to the bytecode cache which is available + as `django.core.cache.cache._client`.) + + The minimal interface for the client passed to the constructor is this: + + .. class:: MinimalClientInterface + + .. method:: set(key, value[, timeout]) + + Stores the bytecode in the cache. `value` is a string and + `timeout` the timeout of the key. If timeout is not provided + a default timeout or no timeout should be assumed, if it's + provided it's an integer with the number of seconds the cache + item should exist. + + .. method:: get(key) + + Returns the value for the cache key. If the item does not + exist in the cache the return value must be `None`. + + The other arguments to the constructor are the prefix for all keys that + is added before the actual cache key and the timeout for the bytecode in + the cache system. We recommend a high (or no) timeout. + + This bytecode cache does not support clearing of used items in the cache. + The clear method is a no-operation function. + + .. versionadded:: 2.7 + Added support for ignoring memcache errors through the + `ignore_memcache_errors` parameter. + """ + + def __init__(self, client, prefix='jinja2/bytecode/', timeout=None, + ignore_memcache_errors=True): + self.client = client + self.prefix = prefix + self.timeout = timeout + self.ignore_memcache_errors = ignore_memcache_errors + + def load_bytecode(self, bucket): + try: + code = self.client.get(self.prefix + bucket.key) + except Exception: + if not self.ignore_memcache_errors: + raise + code = None + if code is not None: + bucket.bytecode_from_string(code) + + def dump_bytecode(self, bucket): + args = (self.prefix + bucket.key, bucket.bytecode_to_string()) + if self.timeout is not None: + args += (self.timeout,) + try: + self.client.set(*args) + except Exception: + if not self.ignore_memcache_errors: + raise diff --git a/venv/Lib/site-packages/jinja2/compiler.py b/venv/Lib/site-packages/jinja2/compiler.py new file mode 100644 index 0000000..d534a82 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/compiler.py @@ -0,0 +1,1721 @@ +# -*- coding: utf-8 -*- +""" + jinja2.compiler + ~~~~~~~~~~~~~~~ + + Compiles nodes into python code. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +from itertools import chain +from copy import deepcopy +from keyword import iskeyword as is_python_keyword +from functools import update_wrapper +from jinja2 import nodes +from jinja2.nodes import EvalContext +from jinja2.visitor import NodeVisitor +from jinja2.optimizer import Optimizer +from jinja2.exceptions import TemplateAssertionError +from jinja2.utils import Markup, concat, escape +from jinja2._compat import range_type, text_type, string_types, \ + iteritems, NativeStringIO, imap, izip +from jinja2.idtracking import Symbols, VAR_LOAD_PARAMETER, \ + VAR_LOAD_RESOLVE, VAR_LOAD_ALIAS, VAR_LOAD_UNDEFINED + + +operators = { + 'eq': '==', + 'ne': '!=', + 'gt': '>', + 'gteq': '>=', + 'lt': '<', + 'lteq': '<=', + 'in': 'in', + 'notin': 'not in' +} + +# what method to iterate over items do we want to use for dict iteration +# in generated code? on 2.x let's go with iteritems, on 3.x with items +if hasattr(dict, 'iteritems'): + dict_item_iter = 'iteritems' +else: + dict_item_iter = 'items' + +code_features = ['division'] + +# does this python version support generator stops? (PEP 0479) +try: + exec('from __future__ import generator_stop') + code_features.append('generator_stop') +except SyntaxError: + pass + +# does this python version support yield from? +try: + exec('def f(): yield from x()') +except SyntaxError: + supports_yield_from = False +else: + supports_yield_from = True + + +def optimizeconst(f): + def new_func(self, node, frame, **kwargs): + # Only optimize if the frame is not volatile + if self.optimized and not frame.eval_ctx.volatile: + new_node = self.optimizer.visit(node, frame.eval_ctx) + if new_node != node: + return self.visit(new_node, frame) + return f(self, node, frame, **kwargs) + return update_wrapper(new_func, f) + + +def generate(node, environment, name, filename, stream=None, + defer_init=False, optimized=True): + """Generate the python source for a node tree.""" + if not isinstance(node, nodes.Template): + raise TypeError('Can\'t compile non template nodes') + generator = environment.code_generator_class(environment, name, filename, + stream, defer_init, + optimized) + generator.visit(node) + if stream is None: + return generator.stream.getvalue() + + +def has_safe_repr(value): + """Does the node have a safe representation?""" + if value is None or value is NotImplemented or value is Ellipsis: + return True + if type(value) in (bool, int, float, complex, range_type, Markup) + string_types: + return True + if type(value) in (tuple, list, set, frozenset): + for item in value: + if not has_safe_repr(item): + return False + return True + elif type(value) is dict: + for key, value in iteritems(value): + if not has_safe_repr(key): + return False + if not has_safe_repr(value): + return False + return True + return False + + +def find_undeclared(nodes, names): + """Check if the names passed are accessed undeclared. The return value + is a set of all the undeclared names from the sequence of names found. + """ + visitor = UndeclaredNameVisitor(names) + try: + for node in nodes: + visitor.visit(node) + except VisitorExit: + pass + return visitor.undeclared + + +class MacroRef(object): + + def __init__(self, node): + self.node = node + self.accesses_caller = False + self.accesses_kwargs = False + self.accesses_varargs = False + + +class Frame(object): + """Holds compile time information for us.""" + + def __init__(self, eval_ctx, parent=None, level=None): + self.eval_ctx = eval_ctx + self.symbols = Symbols(parent and parent.symbols or None, + level=level) + + # a toplevel frame is the root + soft frames such as if conditions. + self.toplevel = False + + # the root frame is basically just the outermost frame, so no if + # conditions. This information is used to optimize inheritance + # situations. + self.rootlevel = False + + # in some dynamic inheritance situations the compiler needs to add + # write tests around output statements. + self.require_output_check = parent and parent.require_output_check + + # inside some tags we are using a buffer rather than yield statements. + # this for example affects {% filter %} or {% macro %}. If a frame + # is buffered this variable points to the name of the list used as + # buffer. + self.buffer = None + + # the name of the block we're in, otherwise None. + self.block = parent and parent.block or None + + # the parent of this frame + self.parent = parent + + if parent is not None: + self.buffer = parent.buffer + + def copy(self): + """Create a copy of the current one.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.symbols = self.symbols.copy() + return rv + + def inner(self, isolated=False): + """Return an inner frame.""" + if isolated: + return Frame(self.eval_ctx, level=self.symbols.level + 1) + return Frame(self.eval_ctx, self) + + def soft(self): + """Return a soft frame. A soft frame may not be modified as + standalone thing as it shares the resources with the frame it + was created of, but it's not a rootlevel frame any longer. + + This is only used to implement if-statements. + """ + rv = self.copy() + rv.rootlevel = False + return rv + + __copy__ = copy + + +class VisitorExit(RuntimeError): + """Exception used by the `UndeclaredNameVisitor` to signal a stop.""" + + +class DependencyFinderVisitor(NodeVisitor): + """A visitor that collects filter and test calls.""" + + def __init__(self): + self.filters = set() + self.tests = set() + + def visit_Filter(self, node): + self.generic_visit(node) + self.filters.add(node.name) + + def visit_Test(self, node): + self.generic_visit(node) + self.tests.add(node.name) + + def visit_Block(self, node): + """Stop visiting at blocks.""" + + +class UndeclaredNameVisitor(NodeVisitor): + """A visitor that checks if a name is accessed without being + declared. This is different from the frame visitor as it will + not stop at closure frames. + """ + + def __init__(self, names): + self.names = set(names) + self.undeclared = set() + + def visit_Name(self, node): + if node.ctx == 'load' and node.name in self.names: + self.undeclared.add(node.name) + if self.undeclared == self.names: + raise VisitorExit() + else: + self.names.discard(node.name) + + def visit_Block(self, node): + """Stop visiting a blocks.""" + + +class CompilerExit(Exception): + """Raised if the compiler encountered a situation where it just + doesn't make sense to further process the code. Any block that + raises such an exception is not further processed. + """ + + +class CodeGenerator(NodeVisitor): + + def __init__(self, environment, name, filename, stream=None, + defer_init=False, optimized=True): + if stream is None: + stream = NativeStringIO() + self.environment = environment + self.name = name + self.filename = filename + self.stream = stream + self.created_block_context = False + self.defer_init = defer_init + self.optimized = optimized + if optimized: + self.optimizer = Optimizer(environment) + + # aliases for imports + self.import_aliases = {} + + # a registry for all blocks. Because blocks are moved out + # into the global python scope they are registered here + self.blocks = {} + + # the number of extends statements so far + self.extends_so_far = 0 + + # some templates have a rootlevel extends. In this case we + # can safely assume that we're a child template and do some + # more optimizations. + self.has_known_extends = False + + # the current line number + self.code_lineno = 1 + + # registry of all filters and tests (global, not block local) + self.tests = {} + self.filters = {} + + # the debug information + self.debug_info = [] + self._write_debug_info = None + + # the number of new lines before the next write() + self._new_lines = 0 + + # the line number of the last written statement + self._last_line = 0 + + # true if nothing was written so far. + self._first_write = True + + # used by the `temporary_identifier` method to get new + # unique, temporary identifier + self._last_identifier = 0 + + # the current indentation + self._indentation = 0 + + # Tracks toplevel assignments + self._assign_stack = [] + + # Tracks parameter definition blocks + self._param_def_block = [] + + # Tracks the current context. + self._context_reference_stack = ['context'] + + # -- Various compilation helpers + + def fail(self, msg, lineno): + """Fail with a :exc:`TemplateAssertionError`.""" + raise TemplateAssertionError(msg, lineno, self.name, self.filename) + + def temporary_identifier(self): + """Get a new unique identifier.""" + self._last_identifier += 1 + return 't_%d' % self._last_identifier + + def buffer(self, frame): + """Enable buffering for the frame from that point onwards.""" + frame.buffer = self.temporary_identifier() + self.writeline('%s = []' % frame.buffer) + + def return_buffer_contents(self, frame, force_unescaped=False): + """Return the buffer contents of the frame.""" + if not force_unescaped: + if frame.eval_ctx.volatile: + self.writeline('if context.eval_ctx.autoescape:') + self.indent() + self.writeline('return Markup(concat(%s))' % frame.buffer) + self.outdent() + self.writeline('else:') + self.indent() + self.writeline('return concat(%s)' % frame.buffer) + self.outdent() + return + elif frame.eval_ctx.autoescape: + self.writeline('return Markup(concat(%s))' % frame.buffer) + return + self.writeline('return concat(%s)' % frame.buffer) + + def indent(self): + """Indent by one.""" + self._indentation += 1 + + def outdent(self, step=1): + """Outdent by step.""" + self._indentation -= step + + def start_write(self, frame, node=None): + """Yield or write into the frame buffer.""" + if frame.buffer is None: + self.writeline('yield ', node) + else: + self.writeline('%s.append(' % frame.buffer, node) + + def end_write(self, frame): + """End the writing process started by `start_write`.""" + if frame.buffer is not None: + self.write(')') + + def simple_write(self, s, frame, node=None): + """Simple shortcut for start_write + write + end_write.""" + self.start_write(frame, node) + self.write(s) + self.end_write(frame) + + def blockvisit(self, nodes, frame): + """Visit a list of nodes as block in a frame. If the current frame + is no buffer a dummy ``if 0: yield None`` is written automatically. + """ + try: + self.writeline('pass') + for node in nodes: + self.visit(node, frame) + except CompilerExit: + pass + + def write(self, x): + """Write a string into the output stream.""" + if self._new_lines: + if not self._first_write: + self.stream.write('\n' * self._new_lines) + self.code_lineno += self._new_lines + if self._write_debug_info is not None: + self.debug_info.append((self._write_debug_info, + self.code_lineno)) + self._write_debug_info = None + self._first_write = False + self.stream.write(' ' * self._indentation) + self._new_lines = 0 + self.stream.write(x) + + def writeline(self, x, node=None, extra=0): + """Combination of newline and write.""" + self.newline(node, extra) + self.write(x) + + def newline(self, node=None, extra=0): + """Add one or more newlines before the next write.""" + self._new_lines = max(self._new_lines, 1 + extra) + if node is not None and node.lineno != self._last_line: + self._write_debug_info = node.lineno + self._last_line = node.lineno + + def signature(self, node, frame, extra_kwargs=None): + """Writes a function call to the stream for the current node. + A leading comma is added automatically. The extra keyword + arguments may not include python keywords otherwise a syntax + error could occour. The extra keyword arguments should be given + as python dict. + """ + # if any of the given keyword arguments is a python keyword + # we have to make sure that no invalid call is created. + kwarg_workaround = False + for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()): + if is_python_keyword(kwarg): + kwarg_workaround = True + break + + for arg in node.args: + self.write(', ') + self.visit(arg, frame) + + if not kwarg_workaround: + for kwarg in node.kwargs: + self.write(', ') + self.visit(kwarg, frame) + if extra_kwargs is not None: + for key, value in iteritems(extra_kwargs): + self.write(', %s=%s' % (key, value)) + if node.dyn_args: + self.write(', *') + self.visit(node.dyn_args, frame) + + if kwarg_workaround: + if node.dyn_kwargs is not None: + self.write(', **dict({') + else: + self.write(', **{') + for kwarg in node.kwargs: + self.write('%r: ' % kwarg.key) + self.visit(kwarg.value, frame) + self.write(', ') + if extra_kwargs is not None: + for key, value in iteritems(extra_kwargs): + self.write('%r: %s, ' % (key, value)) + if node.dyn_kwargs is not None: + self.write('}, **') + self.visit(node.dyn_kwargs, frame) + self.write(')') + else: + self.write('}') + + elif node.dyn_kwargs is not None: + self.write(', **') + self.visit(node.dyn_kwargs, frame) + + def pull_dependencies(self, nodes): + """Pull all the dependencies.""" + visitor = DependencyFinderVisitor() + for node in nodes: + visitor.visit(node) + for dependency in 'filters', 'tests': + mapping = getattr(self, dependency) + for name in getattr(visitor, dependency): + if name not in mapping: + mapping[name] = self.temporary_identifier() + self.writeline('%s = environment.%s[%r]' % + (mapping[name], dependency, name)) + + def enter_frame(self, frame): + undefs = [] + for target, (action, param) in iteritems(frame.symbols.loads): + if action == VAR_LOAD_PARAMETER: + pass + elif action == VAR_LOAD_RESOLVE: + self.writeline('%s = %s(%r)' % + (target, self.get_resolve_func(), param)) + elif action == VAR_LOAD_ALIAS: + self.writeline('%s = %s' % (target, param)) + elif action == VAR_LOAD_UNDEFINED: + undefs.append(target) + else: + raise NotImplementedError('unknown load instruction') + if undefs: + self.writeline('%s = missing' % ' = '.join(undefs)) + + def leave_frame(self, frame, with_python_scope=False): + if not with_python_scope: + undefs = [] + for target, _ in iteritems(frame.symbols.loads): + undefs.append(target) + if undefs: + self.writeline('%s = missing' % ' = '.join(undefs)) + + def func(self, name): + if self.environment.is_async: + return 'async def %s' % name + return 'def %s' % name + + def macro_body(self, node, frame): + """Dump the function def of a macro or call block.""" + frame = frame.inner() + frame.symbols.analyze_node(node) + macro_ref = MacroRef(node) + + explicit_caller = None + skip_special_params = set() + args = [] + for idx, arg in enumerate(node.args): + if arg.name == 'caller': + explicit_caller = idx + if arg.name in ('kwargs', 'varargs'): + skip_special_params.add(arg.name) + args.append(frame.symbols.ref(arg.name)) + + undeclared = find_undeclared(node.body, ('caller', 'kwargs', 'varargs')) + + if 'caller' in undeclared: + # In older Jinja2 versions there was a bug that allowed caller + # to retain the special behavior even if it was mentioned in + # the argument list. However thankfully this was only really + # working if it was the last argument. So we are explicitly + # checking this now and error out if it is anywhere else in + # the argument list. + if explicit_caller is not None: + try: + node.defaults[explicit_caller - len(node.args)] + except IndexError: + self.fail('When defining macros or call blocks the ' + 'special "caller" argument must be omitted ' + 'or be given a default.', node.lineno) + else: + args.append(frame.symbols.declare_parameter('caller')) + macro_ref.accesses_caller = True + if 'kwargs' in undeclared and not 'kwargs' in skip_special_params: + args.append(frame.symbols.declare_parameter('kwargs')) + macro_ref.accesses_kwargs = True + if 'varargs' in undeclared and not 'varargs' in skip_special_params: + args.append(frame.symbols.declare_parameter('varargs')) + macro_ref.accesses_varargs = True + + # macros are delayed, they never require output checks + frame.require_output_check = False + frame.symbols.analyze_node(node) + self.writeline('%s(%s):' % (self.func('macro'), ', '.join(args)), node) + self.indent() + + self.buffer(frame) + self.enter_frame(frame) + + self.push_parameter_definitions(frame) + for idx, arg in enumerate(node.args): + ref = frame.symbols.ref(arg.name) + self.writeline('if %s is missing:' % ref) + self.indent() + try: + default = node.defaults[idx - len(node.args)] + except IndexError: + self.writeline('%s = undefined(%r, name=%r)' % ( + ref, + 'parameter %r was not provided' % arg.name, + arg.name)) + else: + self.writeline('%s = ' % ref) + self.visit(default, frame) + self.mark_parameter_stored(ref) + self.outdent() + self.pop_parameter_definitions() + + self.blockvisit(node.body, frame) + self.return_buffer_contents(frame, force_unescaped=True) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + return frame, macro_ref + + def macro_def(self, macro_ref, frame): + """Dump the macro definition for the def created by macro_body.""" + arg_tuple = ', '.join(repr(x.name) for x in macro_ref.node.args) + name = getattr(macro_ref.node, 'name', None) + if len(macro_ref.node.args) == 1: + arg_tuple += ',' + self.write('Macro(environment, macro, %r, (%s), %r, %r, %r, ' + 'context.eval_ctx.autoescape)' % + (name, arg_tuple, macro_ref.accesses_kwargs, + macro_ref.accesses_varargs, macro_ref.accesses_caller)) + + def position(self, node): + """Return a human readable position for the node.""" + rv = 'line %d' % node.lineno + if self.name is not None: + rv += ' in ' + repr(self.name) + return rv + + def dump_local_context(self, frame): + return '{%s}' % ', '.join( + '%r: %s' % (name, target) for name, target + in iteritems(frame.symbols.dump_stores())) + + def write_commons(self): + """Writes a common preamble that is used by root and block functions. + Primarily this sets up common local helpers and enforces a generator + through a dead branch. + """ + self.writeline('resolve = context.resolve_or_missing') + self.writeline('undefined = environment.undefined') + self.writeline('if 0: yield None') + + def push_parameter_definitions(self, frame): + """Pushes all parameter targets from the given frame into a local + stack that permits tracking of yet to be assigned parameters. In + particular this enables the optimization from `visit_Name` to skip + undefined expressions for parameters in macros as macros can reference + otherwise unbound parameters. + """ + self._param_def_block.append(frame.symbols.dump_param_targets()) + + def pop_parameter_definitions(self): + """Pops the current parameter definitions set.""" + self._param_def_block.pop() + + def mark_parameter_stored(self, target): + """Marks a parameter in the current parameter definitions as stored. + This will skip the enforced undefined checks. + """ + if self._param_def_block: + self._param_def_block[-1].discard(target) + + def push_context_reference(self, target): + self._context_reference_stack.append(target) + + def pop_context_reference(self): + self._context_reference_stack.pop() + + def get_context_ref(self): + return self._context_reference_stack[-1] + + def get_resolve_func(self): + target = self._context_reference_stack[-1] + if target == 'context': + return 'resolve' + return '%s.resolve' % target + + def derive_context(self, frame): + return '%s.derived(%s)' % ( + self.get_context_ref(), + self.dump_local_context(frame), + ) + + def parameter_is_undeclared(self, target): + """Checks if a given target is an undeclared parameter.""" + if not self._param_def_block: + return False + return target in self._param_def_block[-1] + + def push_assign_tracking(self): + """Pushes a new layer for assignment tracking.""" + self._assign_stack.append(set()) + + def pop_assign_tracking(self, frame): + """Pops the topmost level for assignment tracking and updates the + context variables if necessary. + """ + vars = self._assign_stack.pop() + if not frame.toplevel or not vars: + return + public_names = [x for x in vars if x[:1] != '_'] + if len(vars) == 1: + name = next(iter(vars)) + ref = frame.symbols.ref(name) + self.writeline('context.vars[%r] = %s' % (name, ref)) + else: + self.writeline('context.vars.update({') + for idx, name in enumerate(vars): + if idx: + self.write(', ') + ref = frame.symbols.ref(name) + self.write('%r: %s' % (name, ref)) + self.write('})') + if public_names: + if len(public_names) == 1: + self.writeline('context.exported_vars.add(%r)' % + public_names[0]) + else: + self.writeline('context.exported_vars.update((%s))' % + ', '.join(imap(repr, public_names))) + + # -- Statement Visitors + + def visit_Template(self, node, frame=None): + assert frame is None, 'no root frame allowed' + eval_ctx = EvalContext(self.environment, self.name) + + from jinja2.runtime import __all__ as exported + self.writeline('from __future__ import %s' % ', '.join(code_features)) + self.writeline('from jinja2.runtime import ' + ', '.join(exported)) + + if self.environment.is_async: + self.writeline('from jinja2.asyncsupport import auto_await, ' + 'auto_aiter, make_async_loop_context') + + # if we want a deferred initialization we cannot move the + # environment into a local name + envenv = not self.defer_init and ', environment=environment' or '' + + # do we have an extends tag at all? If not, we can save some + # overhead by just not processing any inheritance code. + have_extends = node.find(nodes.Extends) is not None + + # find all blocks + for block in node.find_all(nodes.Block): + if block.name in self.blocks: + self.fail('block %r defined twice' % block.name, block.lineno) + self.blocks[block.name] = block + + # find all imports and import them + for import_ in node.find_all(nodes.ImportedName): + if import_.importname not in self.import_aliases: + imp = import_.importname + self.import_aliases[imp] = alias = self.temporary_identifier() + if '.' in imp: + module, obj = imp.rsplit('.', 1) + self.writeline('from %s import %s as %s' % + (module, obj, alias)) + else: + self.writeline('import %s as %s' % (imp, alias)) + + # add the load name + self.writeline('name = %r' % self.name) + + # generate the root render function. + self.writeline('%s(context, missing=missing%s):' % + (self.func('root'), envenv), extra=1) + self.indent() + self.write_commons() + + # process the root + frame = Frame(eval_ctx) + if 'self' in find_undeclared(node.body, ('self',)): + ref = frame.symbols.declare_parameter('self') + self.writeline('%s = TemplateReference(context)' % ref) + frame.symbols.analyze_node(node) + frame.toplevel = frame.rootlevel = True + frame.require_output_check = have_extends and not self.has_known_extends + if have_extends: + self.writeline('parent_template = None') + self.enter_frame(frame) + self.pull_dependencies(node.body) + self.blockvisit(node.body, frame) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + # make sure that the parent root is called. + if have_extends: + if not self.has_known_extends: + self.indent() + self.writeline('if parent_template is not None:') + self.indent() + if supports_yield_from and not self.environment.is_async: + self.writeline('yield from parent_template.' + 'root_render_func(context)') + else: + self.writeline('%sfor event in parent_template.' + 'root_render_func(context):' % + (self.environment.is_async and 'async ' or '')) + self.indent() + self.writeline('yield event') + self.outdent() + self.outdent(1 + (not self.has_known_extends)) + + # at this point we now have the blocks collected and can visit them too. + for name, block in iteritems(self.blocks): + self.writeline('%s(context, missing=missing%s):' % + (self.func('block_' + name), envenv), + block, 1) + self.indent() + self.write_commons() + # It's important that we do not make this frame a child of the + # toplevel template. This would cause a variety of + # interesting issues with identifier tracking. + block_frame = Frame(eval_ctx) + undeclared = find_undeclared(block.body, ('self', 'super')) + if 'self' in undeclared: + ref = block_frame.symbols.declare_parameter('self') + self.writeline('%s = TemplateReference(context)' % ref) + if 'super' in undeclared: + ref = block_frame.symbols.declare_parameter('super') + self.writeline('%s = context.super(%r, ' + 'block_%s)' % (ref, name, name)) + block_frame.symbols.analyze_node(block) + block_frame.block = name + self.enter_frame(block_frame) + self.pull_dependencies(block.body) + self.blockvisit(block.body, block_frame) + self.leave_frame(block_frame, with_python_scope=True) + self.outdent() + + self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x) + for x in self.blocks), + extra=1) + + # add a function that returns the debug info + self.writeline('debug_info = %r' % '&'.join('%s=%s' % x for x + in self.debug_info)) + + def visit_Block(self, node, frame): + """Call a block and register it for the template.""" + level = 0 + if frame.toplevel: + # if we know that we are a child template, there is no need to + # check if we are one + if self.has_known_extends: + return + if self.extends_so_far > 0: + self.writeline('if parent_template is None:') + self.indent() + level += 1 + + if node.scoped: + context = self.derive_context(frame) + else: + context = self.get_context_ref() + + if supports_yield_from and not self.environment.is_async and \ + frame.buffer is None: + self.writeline('yield from context.blocks[%r][0](%s)' % ( + node.name, context), node) + else: + loop = self.environment.is_async and 'async for' or 'for' + self.writeline('%s event in context.blocks[%r][0](%s):' % ( + loop, node.name, context), node) + self.indent() + self.simple_write('event', frame) + self.outdent() + + self.outdent(level) + + def visit_Extends(self, node, frame): + """Calls the extender.""" + if not frame.toplevel: + self.fail('cannot use extend from a non top-level scope', + node.lineno) + + # if the number of extends statements in general is zero so + # far, we don't have to add a check if something extended + # the template before this one. + if self.extends_so_far > 0: + + # if we have a known extends we just add a template runtime + # error into the generated code. We could catch that at compile + # time too, but i welcome it not to confuse users by throwing the + # same error at different times just "because we can". + if not self.has_known_extends: + self.writeline('if parent_template is not None:') + self.indent() + self.writeline('raise TemplateRuntimeError(%r)' % + 'extended multiple times') + + # if we have a known extends already we don't need that code here + # as we know that the template execution will end here. + if self.has_known_extends: + raise CompilerExit() + else: + self.outdent() + + self.writeline('parent_template = environment.get_template(', node) + self.visit(node.template, frame) + self.write(', %r)' % self.name) + self.writeline('for name, parent_block in parent_template.' + 'blocks.%s():' % dict_item_iter) + self.indent() + self.writeline('context.blocks.setdefault(name, []).' + 'append(parent_block)') + self.outdent() + + # if this extends statement was in the root level we can take + # advantage of that information and simplify the generated code + # in the top level from this point onwards + if frame.rootlevel: + self.has_known_extends = True + + # and now we have one more + self.extends_so_far += 1 + + def visit_Include(self, node, frame): + """Handles includes.""" + if node.ignore_missing: + self.writeline('try:') + self.indent() + + func_name = 'get_or_select_template' + if isinstance(node.template, nodes.Const): + if isinstance(node.template.value, string_types): + func_name = 'get_template' + elif isinstance(node.template.value, (tuple, list)): + func_name = 'select_template' + elif isinstance(node.template, (nodes.Tuple, nodes.List)): + func_name = 'select_template' + + self.writeline('template = environment.%s(' % func_name, node) + self.visit(node.template, frame) + self.write(', %r)' % self.name) + if node.ignore_missing: + self.outdent() + self.writeline('except TemplateNotFound:') + self.indent() + self.writeline('pass') + self.outdent() + self.writeline('else:') + self.indent() + + skip_event_yield = False + if node.with_context: + loop = self.environment.is_async and 'async for' or 'for' + self.writeline('%s event in template.root_render_func(' + 'template.new_context(context.get_all(), True, ' + '%s)):' % (loop, self.dump_local_context(frame))) + elif self.environment.is_async: + self.writeline('for event in (await ' + 'template._get_default_module_async())' + '._body_stream:') + else: + if supports_yield_from: + self.writeline('yield from template._get_default_module()' + '._body_stream') + skip_event_yield = True + else: + self.writeline('for event in template._get_default_module()' + '._body_stream:') + + if not skip_event_yield: + self.indent() + self.simple_write('event', frame) + self.outdent() + + if node.ignore_missing: + self.outdent() + + def visit_Import(self, node, frame): + """Visit regular imports.""" + self.writeline('%s = ' % frame.symbols.ref(node.target), node) + if frame.toplevel: + self.write('context.vars[%r] = ' % node.target) + if self.environment.is_async: + self.write('await ') + self.write('environment.get_template(') + self.visit(node.template, frame) + self.write(', %r).' % self.name) + if node.with_context: + self.write('make_module%s(context.get_all(), True, %s)' + % (self.environment.is_async and '_async' or '', + self.dump_local_context(frame))) + elif self.environment.is_async: + self.write('_get_default_module_async()') + else: + self.write('_get_default_module()') + if frame.toplevel and not node.target.startswith('_'): + self.writeline('context.exported_vars.discard(%r)' % node.target) + + def visit_FromImport(self, node, frame): + """Visit named imports.""" + self.newline(node) + self.write('included_template = %senvironment.get_template(' + % (self.environment.is_async and 'await ' or '')) + self.visit(node.template, frame) + self.write(', %r).' % self.name) + if node.with_context: + self.write('make_module%s(context.get_all(), True, %s)' + % (self.environment.is_async and '_async' or '', + self.dump_local_context(frame))) + elif self.environment.is_async: + self.write('_get_default_module_async()') + else: + self.write('_get_default_module()') + + var_names = [] + discarded_names = [] + for name in node.names: + if isinstance(name, tuple): + name, alias = name + else: + alias = name + self.writeline('%s = getattr(included_template, ' + '%r, missing)' % (frame.symbols.ref(alias), name)) + self.writeline('if %s is missing:' % frame.symbols.ref(alias)) + self.indent() + self.writeline('%s = undefined(%r %% ' + 'included_template.__name__, ' + 'name=%r)' % + (frame.symbols.ref(alias), + 'the template %%r (imported on %s) does ' + 'not export the requested name %s' % ( + self.position(node), + repr(name) + ), name)) + self.outdent() + if frame.toplevel: + var_names.append(alias) + if not alias.startswith('_'): + discarded_names.append(alias) + + if var_names: + if len(var_names) == 1: + name = var_names[0] + self.writeline('context.vars[%r] = %s' % + (name, frame.symbols.ref(name))) + else: + self.writeline('context.vars.update({%s})' % ', '.join( + '%r: %s' % (name, frame.symbols.ref(name)) for name in var_names + )) + if discarded_names: + if len(discarded_names) == 1: + self.writeline('context.exported_vars.discard(%r)' % + discarded_names[0]) + else: + self.writeline('context.exported_vars.difference_' + 'update((%s))' % ', '.join(imap(repr, discarded_names))) + + def visit_For(self, node, frame): + loop_frame = frame.inner() + test_frame = frame.inner() + else_frame = frame.inner() + + # try to figure out if we have an extended loop. An extended loop + # is necessary if the loop is in recursive mode if the special loop + # variable is accessed in the body. + extended_loop = node.recursive or 'loop' in \ + find_undeclared(node.iter_child_nodes( + only=('body',)), ('loop',)) + + loop_ref = None + if extended_loop: + loop_ref = loop_frame.symbols.declare_parameter('loop') + + loop_frame.symbols.analyze_node(node, for_branch='body') + if node.else_: + else_frame.symbols.analyze_node(node, for_branch='else') + + if node.test: + loop_filter_func = self.temporary_identifier() + test_frame.symbols.analyze_node(node, for_branch='test') + self.writeline('%s(fiter):' % self.func(loop_filter_func), node.test) + self.indent() + self.enter_frame(test_frame) + self.writeline(self.environment.is_async and 'async for ' or 'for ') + self.visit(node.target, loop_frame) + self.write(' in ') + self.write(self.environment.is_async and 'auto_aiter(fiter)' or 'fiter') + self.write(':') + self.indent() + self.writeline('if ', node.test) + self.visit(node.test, test_frame) + self.write(':') + self.indent() + self.writeline('yield ') + self.visit(node.target, loop_frame) + self.outdent(3) + self.leave_frame(test_frame, with_python_scope=True) + + # if we don't have an recursive loop we have to find the shadowed + # variables at that point. Because loops can be nested but the loop + # variable is a special one we have to enforce aliasing for it. + if node.recursive: + self.writeline('%s(reciter, loop_render_func, depth=0):' % + self.func('loop'), node) + self.indent() + self.buffer(loop_frame) + + # Use the same buffer for the else frame + else_frame.buffer = loop_frame.buffer + + # make sure the loop variable is a special one and raise a template + # assertion error if a loop tries to write to loop + if extended_loop: + self.writeline('%s = missing' % loop_ref) + + for name in node.find_all(nodes.Name): + if name.ctx == 'store' and name.name == 'loop': + self.fail('Can\'t assign to special loop variable ' + 'in for-loop target', name.lineno) + + if node.else_: + iteration_indicator = self.temporary_identifier() + self.writeline('%s = 1' % iteration_indicator) + + self.writeline(self.environment.is_async and 'async for ' or 'for ', node) + self.visit(node.target, loop_frame) + if extended_loop: + if self.environment.is_async: + self.write(', %s in await make_async_loop_context(' % loop_ref) + else: + self.write(', %s in LoopContext(' % loop_ref) + else: + self.write(' in ') + + if node.test: + self.write('%s(' % loop_filter_func) + if node.recursive: + self.write('reciter') + else: + if self.environment.is_async and not extended_loop: + self.write('auto_aiter(') + self.visit(node.iter, frame) + if self.environment.is_async and not extended_loop: + self.write(')') + if node.test: + self.write(')') + + if node.recursive: + self.write(', undefined, loop_render_func, depth):') + else: + self.write(extended_loop and ', undefined):' or ':') + + self.indent() + self.enter_frame(loop_frame) + + self.blockvisit(node.body, loop_frame) + if node.else_: + self.writeline('%s = 0' % iteration_indicator) + self.outdent() + self.leave_frame(loop_frame, with_python_scope=node.recursive + and not node.else_) + + if node.else_: + self.writeline('if %s:' % iteration_indicator) + self.indent() + self.enter_frame(else_frame) + self.blockvisit(node.else_, else_frame) + self.leave_frame(else_frame) + self.outdent() + + # if the node was recursive we have to return the buffer contents + # and start the iteration code + if node.recursive: + self.return_buffer_contents(loop_frame) + self.outdent() + self.start_write(frame, node) + if self.environment.is_async: + self.write('await ') + self.write('loop(') + if self.environment.is_async: + self.write('auto_aiter(') + self.visit(node.iter, frame) + if self.environment.is_async: + self.write(')') + self.write(', loop)') + self.end_write(frame) + + def visit_If(self, node, frame): + if_frame = frame.soft() + self.writeline('if ', node) + self.visit(node.test, if_frame) + self.write(':') + self.indent() + self.blockvisit(node.body, if_frame) + self.outdent() + for elif_ in node.elif_: + self.writeline('elif ', elif_) + self.visit(elif_.test, if_frame) + self.write(':') + self.indent() + self.blockvisit(elif_.body, if_frame) + self.outdent() + if node.else_: + self.writeline('else:') + self.indent() + self.blockvisit(node.else_, if_frame) + self.outdent() + + def visit_Macro(self, node, frame): + macro_frame, macro_ref = self.macro_body(node, frame) + self.newline() + if frame.toplevel: + if not node.name.startswith('_'): + self.write('context.exported_vars.add(%r)' % node.name) + ref = frame.symbols.ref(node.name) + self.writeline('context.vars[%r] = ' % node.name) + self.write('%s = ' % frame.symbols.ref(node.name)) + self.macro_def(macro_ref, macro_frame) + + def visit_CallBlock(self, node, frame): + call_frame, macro_ref = self.macro_body(node, frame) + self.writeline('caller = ') + self.macro_def(macro_ref, call_frame) + self.start_write(frame, node) + self.visit_Call(node.call, frame, forward_caller=True) + self.end_write(frame) + + def visit_FilterBlock(self, node, frame): + filter_frame = frame.inner() + filter_frame.symbols.analyze_node(node) + self.enter_frame(filter_frame) + self.buffer(filter_frame) + self.blockvisit(node.body, filter_frame) + self.start_write(frame, node) + self.visit_Filter(node.filter, filter_frame) + self.end_write(frame) + self.leave_frame(filter_frame) + + def visit_With(self, node, frame): + with_frame = frame.inner() + with_frame.symbols.analyze_node(node) + self.enter_frame(with_frame) + for idx, (target, expr) in enumerate(izip(node.targets, node.values)): + self.newline() + self.visit(target, with_frame) + self.write(' = ') + self.visit(expr, frame) + self.blockvisit(node.body, with_frame) + self.leave_frame(with_frame) + + def visit_ExprStmt(self, node, frame): + self.newline(node) + self.visit(node.node, frame) + + def visit_Output(self, node, frame): + # if we have a known extends statement, we don't output anything + # if we are in a require_output_check section + if self.has_known_extends and frame.require_output_check: + return + + allow_constant_finalize = True + if self.environment.finalize: + func = self.environment.finalize + if getattr(func, 'contextfunction', False) or \ + getattr(func, 'evalcontextfunction', False): + allow_constant_finalize = False + elif getattr(func, 'environmentfunction', False): + finalize = lambda x: text_type( + self.environment.finalize(self.environment, x)) + else: + finalize = lambda x: text_type(self.environment.finalize(x)) + else: + finalize = text_type + + # if we are inside a frame that requires output checking, we do so + outdent_later = False + if frame.require_output_check: + self.writeline('if parent_template is None:') + self.indent() + outdent_later = True + + # try to evaluate as many chunks as possible into a static + # string at compile time. + body = [] + for child in node.nodes: + try: + if not allow_constant_finalize: + raise nodes.Impossible() + const = child.as_const(frame.eval_ctx) + except nodes.Impossible: + body.append(child) + continue + # the frame can't be volatile here, becaus otherwise the + # as_const() function would raise an Impossible exception + # at that point. + try: + if frame.eval_ctx.autoescape: + if hasattr(const, '__html__'): + const = const.__html__() + else: + const = escape(const) + const = finalize(const) + except Exception: + # if something goes wrong here we evaluate the node + # at runtime for easier debugging + body.append(child) + continue + if body and isinstance(body[-1], list): + body[-1].append(const) + else: + body.append([const]) + + # if we have less than 3 nodes or a buffer we yield or extend/append + if len(body) < 3 or frame.buffer is not None: + if frame.buffer is not None: + # for one item we append, for more we extend + if len(body) == 1: + self.writeline('%s.append(' % frame.buffer) + else: + self.writeline('%s.extend((' % frame.buffer) + self.indent() + for item in body: + if isinstance(item, list): + val = repr(concat(item)) + if frame.buffer is None: + self.writeline('yield ' + val) + else: + self.writeline(val + ',') + else: + if frame.buffer is None: + self.writeline('yield ', item) + else: + self.newline(item) + close = 1 + if frame.eval_ctx.volatile: + self.write('(escape if context.eval_ctx.autoescape' + ' else to_string)(') + elif frame.eval_ctx.autoescape: + self.write('escape(') + else: + self.write('to_string(') + if self.environment.finalize is not None: + self.write('environment.finalize(') + if getattr(self.environment.finalize, + "contextfunction", False): + self.write('context, ') + close += 1 + self.visit(item, frame) + self.write(')' * close) + if frame.buffer is not None: + self.write(',') + if frame.buffer is not None: + # close the open parentheses + self.outdent() + self.writeline(len(body) == 1 and ')' or '))') + + # otherwise we create a format string as this is faster in that case + else: + format = [] + arguments = [] + for item in body: + if isinstance(item, list): + format.append(concat(item).replace('%', '%%')) + else: + format.append('%s') + arguments.append(item) + self.writeline('yield ') + self.write(repr(concat(format)) + ' % (') + self.indent() + for argument in arguments: + self.newline(argument) + close = 0 + if frame.eval_ctx.volatile: + self.write('(escape if context.eval_ctx.autoescape else' + ' to_string)(') + close += 1 + elif frame.eval_ctx.autoescape: + self.write('escape(') + close += 1 + if self.environment.finalize is not None: + self.write('environment.finalize(') + if getattr(self.environment.finalize, + 'contextfunction', False): + self.write('context, ') + elif getattr(self.environment.finalize, + 'evalcontextfunction', False): + self.write('context.eval_ctx, ') + elif getattr(self.environment.finalize, + 'environmentfunction', False): + self.write('environment, ') + close += 1 + self.visit(argument, frame) + self.write(')' * close + ', ') + self.outdent() + self.writeline(')') + + if outdent_later: + self.outdent() + + def visit_Assign(self, node, frame): + self.push_assign_tracking() + self.newline(node) + self.visit(node.target, frame) + self.write(' = ') + self.visit(node.node, frame) + self.pop_assign_tracking(frame) + + def visit_AssignBlock(self, node, frame): + self.push_assign_tracking() + block_frame = frame.inner() + # This is a special case. Since a set block always captures we + # will disable output checks. This way one can use set blocks + # toplevel even in extended templates. + block_frame.require_output_check = False + block_frame.symbols.analyze_node(node) + self.enter_frame(block_frame) + self.buffer(block_frame) + self.blockvisit(node.body, block_frame) + self.newline(node) + self.visit(node.target, frame) + self.write(' = (Markup if context.eval_ctx.autoescape ' + 'else identity)(') + if node.filter is not None: + self.visit_Filter(node.filter, block_frame) + else: + self.write('concat(%s)' % block_frame.buffer) + self.write(')') + self.pop_assign_tracking(frame) + self.leave_frame(block_frame) + + # -- Expression Visitors + + def visit_Name(self, node, frame): + if node.ctx == 'store' and frame.toplevel: + if self._assign_stack: + self._assign_stack[-1].add(node.name) + ref = frame.symbols.ref(node.name) + + # If we are looking up a variable we might have to deal with the + # case where it's undefined. We can skip that case if the load + # instruction indicates a parameter which are always defined. + if node.ctx == 'load': + load = frame.symbols.find_load(ref) + if not (load is not None and load[0] == VAR_LOAD_PARAMETER and \ + not self.parameter_is_undeclared(ref)): + self.write('(undefined(name=%r) if %s is missing else %s)' % + (node.name, ref, ref)) + return + + self.write(ref) + + def visit_NSRef(self, node, frame): + # NSRefs can only be used to store values; since they use the normal + # `foo.bar` notation they will be parsed as a normal attribute access + # when used anywhere but in a `set` context + ref = frame.symbols.ref(node.name) + self.writeline('if not isinstance(%s, Namespace):' % ref) + self.indent() + self.writeline('raise TemplateRuntimeError(%r)' % + 'cannot assign attribute on non-namespace object') + self.outdent() + self.writeline('%s[%r]' % (ref, node.attr)) + + def visit_Const(self, node, frame): + val = node.as_const(frame.eval_ctx) + if isinstance(val, float): + self.write(str(val)) + else: + self.write(repr(val)) + + def visit_TemplateData(self, node, frame): + try: + self.write(repr(node.as_const(frame.eval_ctx))) + except nodes.Impossible: + self.write('(Markup if context.eval_ctx.autoescape else identity)(%r)' + % node.data) + + def visit_Tuple(self, node, frame): + self.write('(') + idx = -1 + for idx, item in enumerate(node.items): + if idx: + self.write(', ') + self.visit(item, frame) + self.write(idx == 0 and ',)' or ')') + + def visit_List(self, node, frame): + self.write('[') + for idx, item in enumerate(node.items): + if idx: + self.write(', ') + self.visit(item, frame) + self.write(']') + + def visit_Dict(self, node, frame): + self.write('{') + for idx, item in enumerate(node.items): + if idx: + self.write(', ') + self.visit(item.key, frame) + self.write(': ') + self.visit(item.value, frame) + self.write('}') + + def binop(operator, interceptable=True): + @optimizeconst + def visitor(self, node, frame): + if self.environment.sandboxed and \ + operator in self.environment.intercepted_binops: + self.write('environment.call_binop(context, %r, ' % operator) + self.visit(node.left, frame) + self.write(', ') + self.visit(node.right, frame) + else: + self.write('(') + self.visit(node.left, frame) + self.write(' %s ' % operator) + self.visit(node.right, frame) + self.write(')') + return visitor + + def uaop(operator, interceptable=True): + @optimizeconst + def visitor(self, node, frame): + if self.environment.sandboxed and \ + operator in self.environment.intercepted_unops: + self.write('environment.call_unop(context, %r, ' % operator) + self.visit(node.node, frame) + else: + self.write('(' + operator) + self.visit(node.node, frame) + self.write(')') + return visitor + + visit_Add = binop('+') + visit_Sub = binop('-') + visit_Mul = binop('*') + visit_Div = binop('/') + visit_FloorDiv = binop('//') + visit_Pow = binop('**') + visit_Mod = binop('%') + visit_And = binop('and', interceptable=False) + visit_Or = binop('or', interceptable=False) + visit_Pos = uaop('+') + visit_Neg = uaop('-') + visit_Not = uaop('not ', interceptable=False) + del binop, uaop + + @optimizeconst + def visit_Concat(self, node, frame): + if frame.eval_ctx.volatile: + func_name = '(context.eval_ctx.volatile and' \ + ' markup_join or unicode_join)' + elif frame.eval_ctx.autoescape: + func_name = 'markup_join' + else: + func_name = 'unicode_join' + self.write('%s((' % func_name) + for arg in node.nodes: + self.visit(arg, frame) + self.write(', ') + self.write('))') + + @optimizeconst + def visit_Compare(self, node, frame): + self.visit(node.expr, frame) + for op in node.ops: + self.visit(op, frame) + + def visit_Operand(self, node, frame): + self.write(' %s ' % operators[node.op]) + self.visit(node.expr, frame) + + @optimizeconst + def visit_Getattr(self, node, frame): + self.write('environment.getattr(') + self.visit(node.node, frame) + self.write(', %r)' % node.attr) + + @optimizeconst + def visit_Getitem(self, node, frame): + # slices bypass the environment getitem method. + if isinstance(node.arg, nodes.Slice): + self.visit(node.node, frame) + self.write('[') + self.visit(node.arg, frame) + self.write(']') + else: + self.write('environment.getitem(') + self.visit(node.node, frame) + self.write(', ') + self.visit(node.arg, frame) + self.write(')') + + def visit_Slice(self, node, frame): + if node.start is not None: + self.visit(node.start, frame) + self.write(':') + if node.stop is not None: + self.visit(node.stop, frame) + if node.step is not None: + self.write(':') + self.visit(node.step, frame) + + @optimizeconst + def visit_Filter(self, node, frame): + if self.environment.is_async: + self.write('await auto_await(') + self.write(self.filters[node.name] + '(') + func = self.environment.filters.get(node.name) + if func is None: + self.fail('no filter named %r' % node.name, node.lineno) + if getattr(func, 'contextfilter', False): + self.write('context, ') + elif getattr(func, 'evalcontextfilter', False): + self.write('context.eval_ctx, ') + elif getattr(func, 'environmentfilter', False): + self.write('environment, ') + + # if the filter node is None we are inside a filter block + # and want to write to the current buffer + if node.node is not None: + self.visit(node.node, frame) + elif frame.eval_ctx.volatile: + self.write('(context.eval_ctx.autoescape and' + ' Markup(concat(%s)) or concat(%s))' % + (frame.buffer, frame.buffer)) + elif frame.eval_ctx.autoescape: + self.write('Markup(concat(%s))' % frame.buffer) + else: + self.write('concat(%s)' % frame.buffer) + self.signature(node, frame) + self.write(')') + if self.environment.is_async: + self.write(')') + + @optimizeconst + def visit_Test(self, node, frame): + self.write(self.tests[node.name] + '(') + if node.name not in self.environment.tests: + self.fail('no test named %r' % node.name, node.lineno) + self.visit(node.node, frame) + self.signature(node, frame) + self.write(')') + + @optimizeconst + def visit_CondExpr(self, node, frame): + def write_expr2(): + if node.expr2 is not None: + return self.visit(node.expr2, frame) + self.write('undefined(%r)' % ('the inline if-' + 'expression on %s evaluated to false and ' + 'no else section was defined.' % self.position(node))) + + self.write('(') + self.visit(node.expr1, frame) + self.write(' if ') + self.visit(node.test, frame) + self.write(' else ') + write_expr2() + self.write(')') + + @optimizeconst + def visit_Call(self, node, frame, forward_caller=False): + if self.environment.is_async: + self.write('await auto_await(') + if self.environment.sandboxed: + self.write('environment.call(context, ') + else: + self.write('context.call(') + self.visit(node.node, frame) + extra_kwargs = forward_caller and {'caller': 'caller'} or None + self.signature(node, frame, extra_kwargs) + self.write(')') + if self.environment.is_async: + self.write(')') + + def visit_Keyword(self, node, frame): + self.write(node.key + '=') + self.visit(node.value, frame) + + # -- Unused nodes for extensions + + def visit_MarkSafe(self, node, frame): + self.write('Markup(') + self.visit(node.expr, frame) + self.write(')') + + def visit_MarkSafeIfAutoescape(self, node, frame): + self.write('(context.eval_ctx.autoescape and Markup or identity)(') + self.visit(node.expr, frame) + self.write(')') + + def visit_EnvironmentAttribute(self, node, frame): + self.write('environment.' + node.name) + + def visit_ExtensionAttribute(self, node, frame): + self.write('environment.extensions[%r].%s' % (node.identifier, node.name)) + + def visit_ImportedName(self, node, frame): + self.write(self.import_aliases[node.importname]) + + def visit_InternalName(self, node, frame): + self.write(node.name) + + def visit_ContextReference(self, node, frame): + self.write('context') + + def visit_Continue(self, node, frame): + self.writeline('continue', node) + + def visit_Break(self, node, frame): + self.writeline('break', node) + + def visit_Scope(self, node, frame): + scope_frame = frame.inner() + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + + def visit_OverlayScope(self, node, frame): + ctx = self.temporary_identifier() + self.writeline('%s = %s' % (ctx, self.derive_context(frame))) + self.writeline('%s.vars = ' % ctx) + self.visit(node.context, frame) + self.push_context_reference(ctx) + + scope_frame = frame.inner(isolated=True) + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + self.pop_context_reference() + + def visit_EvalContextModifier(self, node, frame): + for keyword in node.options: + self.writeline('context.eval_ctx.%s = ' % keyword.key) + self.visit(keyword.value, frame) + try: + val = keyword.value.as_const(frame.eval_ctx) + except nodes.Impossible: + frame.eval_ctx.volatile = True + else: + setattr(frame.eval_ctx, keyword.key, val) + + def visit_ScopedEvalContextModifier(self, node, frame): + old_ctx_name = self.temporary_identifier() + saved_ctx = frame.eval_ctx.save() + self.writeline('%s = context.eval_ctx.save()' % old_ctx_name) + self.visit_EvalContextModifier(node, frame) + for child in node.body: + self.visit(child, frame) + frame.eval_ctx.revert(saved_ctx) + self.writeline('context.eval_ctx.revert(%s)' % old_ctx_name) diff --git a/venv/Lib/site-packages/jinja2/constants.py b/venv/Lib/site-packages/jinja2/constants.py new file mode 100644 index 0000000..11efd1e --- /dev/null +++ b/venv/Lib/site-packages/jinja2/constants.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +""" + jinja.constants + ~~~~~~~~~~~~~~~ + + Various constants. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" + + +#: list of lorem ipsum words used by the lipsum() helper function +LOREM_IPSUM_WORDS = u'''\ +a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at +auctor augue bibendum blandit class commodo condimentum congue consectetuer +consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus +diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend +elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames +faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac +hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum +justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem +luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie +mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non +nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque +penatibus per pharetra phasellus placerat platea porta porttitor posuere +potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus +ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit +sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor +tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices +ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus +viverra volutpat vulputate''' diff --git a/venv/Lib/site-packages/jinja2/debug.py b/venv/Lib/site-packages/jinja2/debug.py new file mode 100644 index 0000000..b61139f --- /dev/null +++ b/venv/Lib/site-packages/jinja2/debug.py @@ -0,0 +1,372 @@ +# -*- coding: utf-8 -*- +""" + jinja2.debug + ~~~~~~~~~~~~ + + Implements the debug interface for Jinja. This module does some pretty + ugly stuff with the Python traceback system in order to achieve tracebacks + with correct line numbers, locals and contents. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import sys +import traceback +from types import TracebackType, CodeType +from jinja2.utils import missing, internal_code +from jinja2.exceptions import TemplateSyntaxError +from jinja2._compat import iteritems, reraise, PY2 + +# on pypy we can take advantage of transparent proxies +try: + from __pypy__ import tproxy +except ImportError: + tproxy = None + + +# how does the raise helper look like? +try: + exec("raise TypeError, 'foo'") +except SyntaxError: + raise_helper = 'raise __jinja_exception__[1]' +except TypeError: + raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]' + + +class TracebackFrameProxy(object): + """Proxies a traceback frame.""" + + def __init__(self, tb): + self.tb = tb + self._tb_next = None + + @property + def tb_next(self): + return self._tb_next + + def set_next(self, next): + if tb_set_next is not None: + try: + tb_set_next(self.tb, next and next.tb or None) + except Exception: + # this function can fail due to all the hackery it does + # on various python implementations. We just catch errors + # down and ignore them if necessary. + pass + self._tb_next = next + + @property + def is_jinja_frame(self): + return '__jinja_template__' in self.tb.tb_frame.f_globals + + def __getattr__(self, name): + return getattr(self.tb, name) + + +def make_frame_proxy(frame): + proxy = TracebackFrameProxy(frame) + if tproxy is None: + return proxy + def operation_handler(operation, *args, **kwargs): + if operation in ('__getattribute__', '__getattr__'): + return getattr(proxy, args[0]) + elif operation == '__setattr__': + proxy.__setattr__(*args, **kwargs) + else: + return getattr(proxy, operation)(*args, **kwargs) + return tproxy(TracebackType, operation_handler) + + +class ProcessedTraceback(object): + """Holds a Jinja preprocessed traceback for printing or reraising.""" + + def __init__(self, exc_type, exc_value, frames): + assert frames, 'no frames for this traceback?' + self.exc_type = exc_type + self.exc_value = exc_value + self.frames = frames + + # newly concatenate the frames (which are proxies) + prev_tb = None + for tb in self.frames: + if prev_tb is not None: + prev_tb.set_next(tb) + prev_tb = tb + prev_tb.set_next(None) + + def render_as_text(self, limit=None): + """Return a string with the traceback.""" + lines = traceback.format_exception(self.exc_type, self.exc_value, + self.frames[0], limit=limit) + return ''.join(lines).rstrip() + + def render_as_html(self, full=False): + """Return a unicode string with the traceback as rendered HTML.""" + from jinja2.debugrenderer import render_traceback + return u'%s\n\n<!--\n%s\n-->' % ( + render_traceback(self, full=full), + self.render_as_text().decode('utf-8', 'replace') + ) + + @property + def is_template_syntax_error(self): + """`True` if this is a template syntax error.""" + return isinstance(self.exc_value, TemplateSyntaxError) + + @property + def exc_info(self): + """Exception info tuple with a proxy around the frame objects.""" + return self.exc_type, self.exc_value, self.frames[0] + + @property + def standard_exc_info(self): + """Standard python exc_info for re-raising""" + tb = self.frames[0] + # the frame will be an actual traceback (or transparent proxy) if + # we are on pypy or a python implementation with support for tproxy + if type(tb) is not TracebackType: + tb = tb.tb + return self.exc_type, self.exc_value, tb + + +def make_traceback(exc_info, source_hint=None): + """Creates a processed traceback object from the exc_info.""" + exc_type, exc_value, tb = exc_info + if isinstance(exc_value, TemplateSyntaxError): + exc_info = translate_syntax_error(exc_value, source_hint) + initial_skip = 0 + else: + initial_skip = 1 + return translate_exception(exc_info, initial_skip) + + +def translate_syntax_error(error, source=None): + """Rewrites a syntax error to please traceback systems.""" + error.source = source + error.translated = True + exc_info = (error.__class__, error, None) + filename = error.filename + if filename is None: + filename = '<unknown>' + return fake_exc_info(exc_info, filename, error.lineno) + + +def translate_exception(exc_info, initial_skip=0): + """If passed an exc_info it will automatically rewrite the exceptions + all the way down to the correct line numbers and frames. + """ + tb = exc_info[2] + frames = [] + + # skip some internal frames if wanted + for x in range(initial_skip): + if tb is not None: + tb = tb.tb_next + initial_tb = tb + + while tb is not None: + # skip frames decorated with @internalcode. These are internal + # calls we can't avoid and that are useless in template debugging + # output. + if tb.tb_frame.f_code in internal_code: + tb = tb.tb_next + continue + + # save a reference to the next frame if we override the current + # one with a faked one. + next = tb.tb_next + + # fake template exceptions + template = tb.tb_frame.f_globals.get('__jinja_template__') + if template is not None: + lineno = template.get_corresponding_lineno(tb.tb_lineno) + tb = fake_exc_info(exc_info[:2] + (tb,), template.filename, + lineno)[2] + + frames.append(make_frame_proxy(tb)) + tb = next + + # if we don't have any exceptions in the frames left, we have to + # reraise it unchanged. + # XXX: can we backup here? when could this happen? + if not frames: + reraise(exc_info[0], exc_info[1], exc_info[2]) + + return ProcessedTraceback(exc_info[0], exc_info[1], frames) + + +def get_jinja_locals(real_locals): + ctx = real_locals.get('context') + if ctx: + locals = ctx.get_all().copy() + else: + locals = {} + + local_overrides = {} + + for name, value in iteritems(real_locals): + if not name.startswith('l_') or value is missing: + continue + try: + _, depth, name = name.split('_', 2) + depth = int(depth) + except ValueError: + continue + cur_depth = local_overrides.get(name, (-1,))[0] + if cur_depth < depth: + local_overrides[name] = (depth, value) + + for name, (_, value) in iteritems(local_overrides): + if value is missing: + locals.pop(name, None) + else: + locals[name] = value + + return locals + + +def fake_exc_info(exc_info, filename, lineno): + """Helper for `translate_exception`.""" + exc_type, exc_value, tb = exc_info + + # figure the real context out + if tb is not None: + locals = get_jinja_locals(tb.tb_frame.f_locals) + + # if there is a local called __jinja_exception__, we get + # rid of it to not break the debug functionality. + locals.pop('__jinja_exception__', None) + else: + locals = {} + + # assamble fake globals we need + globals = { + '__name__': filename, + '__file__': filename, + '__jinja_exception__': exc_info[:2], + + # we don't want to keep the reference to the template around + # to not cause circular dependencies, but we mark it as Jinja + # frame for the ProcessedTraceback + '__jinja_template__': None + } + + # and fake the exception + code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec') + + # if it's possible, change the name of the code. This won't work + # on some python environments such as google appengine + try: + if tb is None: + location = 'template' + else: + function = tb.tb_frame.f_code.co_name + if function == 'root': + location = 'top-level template code' + elif function.startswith('block_'): + location = 'block "%s"' % function[6:] + else: + location = 'template' + + if PY2: + code = CodeType(0, code.co_nlocals, code.co_stacksize, + code.co_flags, code.co_code, code.co_consts, + code.co_names, code.co_varnames, filename, + location, code.co_firstlineno, + code.co_lnotab, (), ()) + else: + code = CodeType(0, code.co_kwonlyargcount, + code.co_nlocals, code.co_stacksize, + code.co_flags, code.co_code, code.co_consts, + code.co_names, code.co_varnames, filename, + location, code.co_firstlineno, + code.co_lnotab, (), ()) + except Exception as e: + pass + + # execute the code and catch the new traceback + try: + exec(code, globals, locals) + except: + exc_info = sys.exc_info() + new_tb = exc_info[2].tb_next + + # return without this frame + return exc_info[:2] + (new_tb,) + + +def _init_ugly_crap(): + """This function implements a few ugly things so that we can patch the + traceback objects. The function returned allows resetting `tb_next` on + any python traceback object. Do not attempt to use this on non cpython + interpreters + """ + import ctypes + from types import TracebackType + + if PY2: + # figure out size of _Py_ssize_t for Python 2: + if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'): + _Py_ssize_t = ctypes.c_int64 + else: + _Py_ssize_t = ctypes.c_int + else: + # platform ssize_t on Python 3 + _Py_ssize_t = ctypes.c_ssize_t + + # regular python + class _PyObject(ctypes.Structure): + pass + _PyObject._fields_ = [ + ('ob_refcnt', _Py_ssize_t), + ('ob_type', ctypes.POINTER(_PyObject)) + ] + + # python with trace + if hasattr(sys, 'getobjects'): + class _PyObject(ctypes.Structure): + pass + _PyObject._fields_ = [ + ('_ob_next', ctypes.POINTER(_PyObject)), + ('_ob_prev', ctypes.POINTER(_PyObject)), + ('ob_refcnt', _Py_ssize_t), + ('ob_type', ctypes.POINTER(_PyObject)) + ] + + class _Traceback(_PyObject): + pass + _Traceback._fields_ = [ + ('tb_next', ctypes.POINTER(_Traceback)), + ('tb_frame', ctypes.POINTER(_PyObject)), + ('tb_lasti', ctypes.c_int), + ('tb_lineno', ctypes.c_int) + ] + + def tb_set_next(tb, next): + """Set the tb_next attribute of a traceback object.""" + if not (isinstance(tb, TracebackType) and + (next is None or isinstance(next, TracebackType))): + raise TypeError('tb_set_next arguments must be traceback objects') + obj = _Traceback.from_address(id(tb)) + if tb.tb_next is not None: + old = _Traceback.from_address(id(tb.tb_next)) + old.ob_refcnt -= 1 + if next is None: + obj.tb_next = ctypes.POINTER(_Traceback)() + else: + next = _Traceback.from_address(id(next)) + next.ob_refcnt += 1 + obj.tb_next = ctypes.pointer(next) + + return tb_set_next + + +# try to get a tb_set_next implementation if we don't have transparent +# proxies. +tb_set_next = None +if tproxy is None: + try: + tb_set_next = _init_ugly_crap() + except: + pass + del _init_ugly_crap diff --git a/venv/Lib/site-packages/jinja2/defaults.py b/venv/Lib/site-packages/jinja2/defaults.py new file mode 100644 index 0000000..7c93dec --- /dev/null +++ b/venv/Lib/site-packages/jinja2/defaults.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +""" + jinja2.defaults + ~~~~~~~~~~~~~~~ + + Jinja default filters and tags. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +from jinja2._compat import range_type +from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner, Namespace + + +# defaults for the parser / lexer +BLOCK_START_STRING = '{%' +BLOCK_END_STRING = '%}' +VARIABLE_START_STRING = '{{' +VARIABLE_END_STRING = '}}' +COMMENT_START_STRING = '{#' +COMMENT_END_STRING = '#}' +LINE_STATEMENT_PREFIX = None +LINE_COMMENT_PREFIX = None +TRIM_BLOCKS = False +LSTRIP_BLOCKS = False +NEWLINE_SEQUENCE = '\n' +KEEP_TRAILING_NEWLINE = False + + +# default filters, tests and namespace +from jinja2.filters import FILTERS as DEFAULT_FILTERS +from jinja2.tests import TESTS as DEFAULT_TESTS +DEFAULT_NAMESPACE = { + 'range': range_type, + 'dict': dict, + 'lipsum': generate_lorem_ipsum, + 'cycler': Cycler, + 'joiner': Joiner, + 'namespace': Namespace +} + + +# default policies +DEFAULT_POLICIES = { + 'compiler.ascii_str': True, + 'urlize.rel': 'noopener', + 'urlize.target': None, + 'truncate.leeway': 5, + 'json.dumps_function': None, + 'json.dumps_kwargs': {'sort_keys': True}, + 'ext.i18n.trimmed': False, +} + + +# export all constants +__all__ = tuple(x for x in locals().keys() if x.isupper()) diff --git a/venv/Lib/site-packages/jinja2/environment.py b/venv/Lib/site-packages/jinja2/environment.py new file mode 100644 index 0000000..549d9af --- /dev/null +++ b/venv/Lib/site-packages/jinja2/environment.py @@ -0,0 +1,1276 @@ +# -*- coding: utf-8 -*- +""" + jinja2.environment + ~~~~~~~~~~~~~~~~~~ + + Provides a class that holds runtime and parsing time options. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import os +import sys +import weakref +from functools import reduce, partial +from jinja2 import nodes +from jinja2.defaults import BLOCK_START_STRING, \ + BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \ + COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \ + LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \ + DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE, \ + DEFAULT_POLICIES, KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS +from jinja2.lexer import get_lexer, TokenStream +from jinja2.parser import Parser +from jinja2.nodes import EvalContext +from jinja2.compiler import generate, CodeGenerator +from jinja2.runtime import Undefined, new_context, Context +from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \ + TemplatesNotFound, TemplateRuntimeError +from jinja2.utils import import_string, LRUCache, Markup, missing, \ + concat, consume, internalcode, have_async_gen +from jinja2._compat import imap, ifilter, string_types, iteritems, \ + text_type, reraise, implements_iterator, implements_to_string, \ + encode_filename, PY2, PYPY + + +# for direct template usage we have up to ten living environments +_spontaneous_environments = LRUCache(10) + +# the function to create jinja traceback objects. This is dynamically +# imported on the first exception in the exception handler. +_make_traceback = None + + +def get_spontaneous_environment(*args): + """Return a new spontaneous environment. A spontaneous environment is an + unnamed and unaccessible (in theory) environment that is used for + templates generated from a string and not from the file system. + """ + try: + env = _spontaneous_environments.get(args) + except TypeError: + return Environment(*args) + if env is not None: + return env + _spontaneous_environments[args] = env = Environment(*args) + env.shared = True + return env + + +def create_cache(size): + """Return the cache class for the given size.""" + if size == 0: + return None + if size < 0: + return {} + return LRUCache(size) + + +def copy_cache(cache): + """Create an empty copy of the given cache.""" + if cache is None: + return None + elif type(cache) is dict: + return {} + return LRUCache(cache.capacity) + + +def load_extensions(environment, extensions): + """Load the extensions from the list and bind it to the environment. + Returns a dict of instantiated environments. + """ + result = {} + for extension in extensions: + if isinstance(extension, string_types): + extension = import_string(extension) + result[extension.identifier] = extension(environment) + return result + + +def fail_for_missing_callable(string, name): + msg = string % name + if isinstance(name, Undefined): + try: + name._fail_with_undefined_error() + except Exception as e: + msg = '%s (%s; did you forget to quote the callable name?)' % (msg, e) + raise TemplateRuntimeError(msg) + + +def _environment_sanity_check(environment): + """Perform a sanity check on the environment.""" + assert issubclass(environment.undefined, Undefined), 'undefined must ' \ + 'be a subclass of undefined because filters depend on it.' + assert environment.block_start_string != \ + environment.variable_start_string != \ + environment.comment_start_string, 'block, variable and comment ' \ + 'start strings must be different' + assert environment.newline_sequence in ('\r', '\r\n', '\n'), \ + 'newline_sequence set to unknown line ending string.' + return environment + + +class Environment(object): + r"""The core component of Jinja is the `Environment`. It contains + important shared variables like configuration, filters, tests, + globals and others. Instances of this class may be modified if + they are not shared and if no template was loaded so far. + Modifications on environments after the first template was loaded + will lead to surprising effects and undefined behavior. + + Here are the possible initialization parameters: + + `block_start_string` + The string marking the beginning of a block. Defaults to ``'{%'``. + + `block_end_string` + The string marking the end of a block. Defaults to ``'%}'``. + + `variable_start_string` + The string marking the beginning of a print statement. + Defaults to ``'{{'``. + + `variable_end_string` + The string marking the end of a print statement. Defaults to + ``'}}'``. + + `comment_start_string` + The string marking the beginning of a comment. Defaults to ``'{#'``. + + `comment_end_string` + The string marking the end of a comment. Defaults to ``'#}'``. + + `line_statement_prefix` + If given and a string, this will be used as prefix for line based + statements. See also :ref:`line-statements`. + + `line_comment_prefix` + If given and a string, this will be used as prefix for line based + comments. See also :ref:`line-statements`. + + .. versionadded:: 2.2 + + `trim_blocks` + If this is set to ``True`` the first newline after a block is + removed (block, not variable tag!). Defaults to `False`. + + `lstrip_blocks` + If this is set to ``True`` leading spaces and tabs are stripped + from the start of a line to a block. Defaults to `False`. + + `newline_sequence` + The sequence that starts a newline. Must be one of ``'\r'``, + ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a + useful default for Linux and OS X systems as well as web + applications. + + `keep_trailing_newline` + Preserve the trailing newline when rendering templates. + The default is ``False``, which causes a single newline, + if present, to be stripped from the end of the template. + + .. versionadded:: 2.7 + + `extensions` + List of Jinja extensions to use. This can either be import paths + as strings or extension classes. For more information have a + look at :ref:`the extensions documentation <jinja-extensions>`. + + `optimized` + should the optimizer be enabled? Default is ``True``. + + `undefined` + :class:`Undefined` or a subclass of it that is used to represent + undefined values in the template. + + `finalize` + A callable that can be used to process the result of a variable + expression before it is output. For example one can convert + ``None`` implicitly into an empty string here. + + `autoescape` + If set to ``True`` the XML/HTML autoescaping feature is enabled by + default. For more details about autoescaping see + :class:`~jinja2.utils.Markup`. As of Jinja 2.4 this can also + be a callable that is passed the template name and has to + return ``True`` or ``False`` depending on autoescape should be + enabled by default. + + .. versionchanged:: 2.4 + `autoescape` can now be a function + + `loader` + The template loader for this environment. + + `cache_size` + The size of the cache. Per default this is ``400`` which means + that if more than 400 templates are loaded the loader will clean + out the least recently used template. If the cache size is set to + ``0`` templates are recompiled all the time, if the cache size is + ``-1`` the cache will not be cleaned. + + .. versionchanged:: 2.8 + The cache size was increased to 400 from a low 50. + + `auto_reload` + Some loaders load templates from locations where the template + sources may change (ie: file system or database). If + ``auto_reload`` is set to ``True`` (default) every time a template is + requested the loader checks if the source changed and if yes, it + will reload the template. For higher performance it's possible to + disable that. + + `bytecode_cache` + If set to a bytecode cache object, this object will provide a + cache for the internal Jinja bytecode so that templates don't + have to be parsed if they were not changed. + + See :ref:`bytecode-cache` for more information. + + `enable_async` + If set to true this enables async template execution which allows + you to take advantage of newer Python features. This requires + Python 3.6 or later. + """ + + #: if this environment is sandboxed. Modifying this variable won't make + #: the environment sandboxed though. For a real sandboxed environment + #: have a look at jinja2.sandbox. This flag alone controls the code + #: generation by the compiler. + sandboxed = False + + #: True if the environment is just an overlay + overlayed = False + + #: the environment this environment is linked to if it is an overlay + linked_to = None + + #: shared environments have this set to `True`. A shared environment + #: must not be modified + shared = False + + #: these are currently EXPERIMENTAL undocumented features. + exception_handler = None + exception_formatter = None + + #: the class that is used for code generation. See + #: :class:`~jinja2.compiler.CodeGenerator` for more information. + code_generator_class = CodeGenerator + + #: the context class thatis used for templates. See + #: :class:`~jinja2.runtime.Context` for more information. + context_class = Context + + def __init__(self, + block_start_string=BLOCK_START_STRING, + block_end_string=BLOCK_END_STRING, + variable_start_string=VARIABLE_START_STRING, + variable_end_string=VARIABLE_END_STRING, + comment_start_string=COMMENT_START_STRING, + comment_end_string=COMMENT_END_STRING, + line_statement_prefix=LINE_STATEMENT_PREFIX, + line_comment_prefix=LINE_COMMENT_PREFIX, + trim_blocks=TRIM_BLOCKS, + lstrip_blocks=LSTRIP_BLOCKS, + newline_sequence=NEWLINE_SEQUENCE, + keep_trailing_newline=KEEP_TRAILING_NEWLINE, + extensions=(), + optimized=True, + undefined=Undefined, + finalize=None, + autoescape=False, + loader=None, + cache_size=400, + auto_reload=True, + bytecode_cache=None, + enable_async=False): + # !!Important notice!! + # The constructor accepts quite a few arguments that should be + # passed by keyword rather than position. However it's important to + # not change the order of arguments because it's used at least + # internally in those cases: + # - spontaneous environments (i18n extension and Template) + # - unittests + # If parameter changes are required only add parameters at the end + # and don't change the arguments (or the defaults!) of the arguments + # existing already. + + # lexer / parser information + self.block_start_string = block_start_string + self.block_end_string = block_end_string + self.variable_start_string = variable_start_string + self.variable_end_string = variable_end_string + self.comment_start_string = comment_start_string + self.comment_end_string = comment_end_string + self.line_statement_prefix = line_statement_prefix + self.line_comment_prefix = line_comment_prefix + self.trim_blocks = trim_blocks + self.lstrip_blocks = lstrip_blocks + self.newline_sequence = newline_sequence + self.keep_trailing_newline = keep_trailing_newline + + # runtime information + self.undefined = undefined + self.optimized = optimized + self.finalize = finalize + self.autoescape = autoescape + + # defaults + self.filters = DEFAULT_FILTERS.copy() + self.tests = DEFAULT_TESTS.copy() + self.globals = DEFAULT_NAMESPACE.copy() + + # set the loader provided + self.loader = loader + self.cache = create_cache(cache_size) + self.bytecode_cache = bytecode_cache + self.auto_reload = auto_reload + + # configurable policies + self.policies = DEFAULT_POLICIES.copy() + + # load extensions + self.extensions = load_extensions(self, extensions) + + self.enable_async = enable_async + self.is_async = self.enable_async and have_async_gen + + _environment_sanity_check(self) + + def add_extension(self, extension): + """Adds an extension after the environment was created. + + .. versionadded:: 2.5 + """ + self.extensions.update(load_extensions(self, [extension])) + + def extend(self, **attributes): + """Add the items to the instance of the environment if they do not exist + yet. This is used by :ref:`extensions <writing-extensions>` to register + callbacks and configuration values without breaking inheritance. + """ + for key, value in iteritems(attributes): + if not hasattr(self, key): + setattr(self, key, value) + + def overlay(self, block_start_string=missing, block_end_string=missing, + variable_start_string=missing, variable_end_string=missing, + comment_start_string=missing, comment_end_string=missing, + line_statement_prefix=missing, line_comment_prefix=missing, + trim_blocks=missing, lstrip_blocks=missing, + extensions=missing, optimized=missing, + undefined=missing, finalize=missing, autoescape=missing, + loader=missing, cache_size=missing, auto_reload=missing, + bytecode_cache=missing): + """Create a new overlay environment that shares all the data with the + current environment except for cache and the overridden attributes. + Extensions cannot be removed for an overlayed environment. An overlayed + environment automatically gets all the extensions of the environment it + is linked to plus optional extra extensions. + + Creating overlays should happen after the initial environment was set + up completely. Not all attributes are truly linked, some are just + copied over so modifications on the original environment may not shine + through. + """ + args = dict(locals()) + del args['self'], args['cache_size'], args['extensions'] + + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.overlayed = True + rv.linked_to = self + + for key, value in iteritems(args): + if value is not missing: + setattr(rv, key, value) + + if cache_size is not missing: + rv.cache = create_cache(cache_size) + else: + rv.cache = copy_cache(self.cache) + + rv.extensions = {} + for key, value in iteritems(self.extensions): + rv.extensions[key] = value.bind(rv) + if extensions is not missing: + rv.extensions.update(load_extensions(rv, extensions)) + + return _environment_sanity_check(rv) + + lexer = property(get_lexer, doc="The lexer for this environment.") + + def iter_extensions(self): + """Iterates over the extensions by priority.""" + return iter(sorted(self.extensions.values(), + key=lambda x: x.priority)) + + def getitem(self, obj, argument): + """Get an item or attribute of an object but prefer the item.""" + try: + return obj[argument] + except (AttributeError, TypeError, LookupError): + if isinstance(argument, string_types): + try: + attr = str(argument) + except Exception: + pass + else: + try: + return getattr(obj, attr) + except AttributeError: + pass + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj, attribute): + """Get an item or attribute of an object but prefer the attribute. + Unlike :meth:`getitem` the attribute *must* be a bytestring. + """ + try: + return getattr(obj, attribute) + except AttributeError: + pass + try: + return obj[attribute] + except (TypeError, LookupError, AttributeError): + return self.undefined(obj=obj, name=attribute) + + def call_filter(self, name, value, args=None, kwargs=None, + context=None, eval_ctx=None): + """Invokes a filter on a value the same way the compiler does it. + + Note that on Python 3 this might return a coroutine in case the + filter is running from an environment in async mode and the filter + supports async execution. It's your responsibility to await this + if needed. + + .. versionadded:: 2.7 + """ + func = self.filters.get(name) + if func is None: + fail_for_missing_callable('no filter named %r', name) + args = [value] + list(args or ()) + if getattr(func, 'contextfilter', False): + if context is None: + raise TemplateRuntimeError('Attempted to invoke context ' + 'filter without context') + args.insert(0, context) + elif getattr(func, 'evalcontextfilter', False): + if eval_ctx is None: + if context is not None: + eval_ctx = context.eval_ctx + else: + eval_ctx = EvalContext(self) + args.insert(0, eval_ctx) + elif getattr(func, 'environmentfilter', False): + args.insert(0, self) + return func(*args, **(kwargs or {})) + + def call_test(self, name, value, args=None, kwargs=None): + """Invokes a test on a value the same way the compiler does it. + + .. versionadded:: 2.7 + """ + func = self.tests.get(name) + if func is None: + fail_for_missing_callable('no test named %r', name) + return func(value, *(args or ()), **(kwargs or {})) + + @internalcode + def parse(self, source, name=None, filename=None): + """Parse the sourcecode and return the abstract syntax tree. This + tree of nodes is used by the compiler to convert the template into + executable source- or bytecode. This is useful for debugging or to + extract information from templates. + + If you are :ref:`developing Jinja2 extensions <writing-extensions>` + this gives you a good overview of the node tree generated. + """ + try: + return self._parse(source, name, filename) + except TemplateSyntaxError: + exc_info = sys.exc_info() + self.handle_exception(exc_info, source_hint=source) + + def _parse(self, source, name, filename): + """Internal parsing function used by `parse` and `compile`.""" + return Parser(self, source, name, encode_filename(filename)).parse() + + def lex(self, source, name=None, filename=None): + """Lex the given sourcecode and return a generator that yields + tokens as tuples in the form ``(lineno, token_type, value)``. + This can be useful for :ref:`extension development <writing-extensions>` + and debugging templates. + + This does not perform preprocessing. If you want the preprocessing + of the extensions to be applied you have to filter source through + the :meth:`preprocess` method. + """ + source = text_type(source) + try: + return self.lexer.tokeniter(source, name, filename) + except TemplateSyntaxError: + exc_info = sys.exc_info() + self.handle_exception(exc_info, source_hint=source) + + def preprocess(self, source, name=None, filename=None): + """Preprocesses the source with all extensions. This is automatically + called for all parsing and compiling methods but *not* for :meth:`lex` + because there you usually only want the actual source tokenized. + """ + return reduce(lambda s, e: e.preprocess(s, name, filename), + self.iter_extensions(), text_type(source)) + + def _tokenize(self, source, name, filename=None, state=None): + """Called by the parser to do the preprocessing and filtering + for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. + """ + source = self.preprocess(source, name, filename) + stream = self.lexer.tokenize(source, name, filename, state) + for ext in self.iter_extensions(): + stream = ext.filter_stream(stream) + if not isinstance(stream, TokenStream): + stream = TokenStream(stream, name, filename) + return stream + + def _generate(self, source, name, filename, defer_init=False): + """Internal hook that can be overridden to hook a different generate + method in. + + .. versionadded:: 2.5 + """ + return generate(source, self, name, filename, defer_init=defer_init, + optimized=self.optimized) + + def _compile(self, source, filename): + """Internal hook that can be overridden to hook a different compile + method in. + + .. versionadded:: 2.5 + """ + return compile(source, filename, 'exec') + + @internalcode + def compile(self, source, name=None, filename=None, raw=False, + defer_init=False): + """Compile a node or template source code. The `name` parameter is + the load name of the template after it was joined using + :meth:`join_path` if necessary, not the filename on the file system. + the `filename` parameter is the estimated filename of the template on + the file system. If the template came from a database or memory this + can be omitted. + + The return value of this method is a python code object. If the `raw` + parameter is `True` the return value will be a string with python + code equivalent to the bytecode returned otherwise. This method is + mainly used internally. + + `defer_init` is use internally to aid the module code generator. This + causes the generated code to be able to import without the global + environment variable to be set. + + .. versionadded:: 2.4 + `defer_init` parameter added. + """ + source_hint = None + try: + if isinstance(source, string_types): + source_hint = source + source = self._parse(source, name, filename) + source = self._generate(source, name, filename, + defer_init=defer_init) + if raw: + return source + if filename is None: + filename = '<template>' + else: + filename = encode_filename(filename) + return self._compile(source, filename) + except TemplateSyntaxError: + exc_info = sys.exc_info() + self.handle_exception(exc_info, source_hint=source_hint) + + def compile_expression(self, source, undefined_to_none=True): + """A handy helper method that returns a callable that accepts keyword + arguments that appear as variables in the expression. If called it + returns the result of the expression. + + This is useful if applications want to use the same rules as Jinja + in template "configuration files" or similar situations. + + Example usage: + + >>> env = Environment() + >>> expr = env.compile_expression('foo == 42') + >>> expr(foo=23) + False + >>> expr(foo=42) + True + + Per default the return value is converted to `None` if the + expression returns an undefined value. This can be changed + by setting `undefined_to_none` to `False`. + + >>> env.compile_expression('var')() is None + True + >>> env.compile_expression('var', undefined_to_none=False)() + Undefined + + .. versionadded:: 2.1 + """ + parser = Parser(self, source, state='variable') + exc_info = None + try: + expr = parser.parse_expression() + if not parser.stream.eos: + raise TemplateSyntaxError('chunk after expression', + parser.stream.current.lineno, + None, None) + expr.set_environment(self) + except TemplateSyntaxError: + exc_info = sys.exc_info() + if exc_info is not None: + self.handle_exception(exc_info, source_hint=source) + body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)] + template = self.from_string(nodes.Template(body, lineno=1)) + return TemplateExpression(template, undefined_to_none) + + def compile_templates(self, target, extensions=None, filter_func=None, + zip='deflated', log_function=None, + ignore_errors=True, py_compile=False): + """Finds all the templates the loader can find, compiles them + and stores them in `target`. If `zip` is `None`, instead of in a + zipfile, the templates will be stored in a directory. + By default a deflate zip algorithm is used. To switch to + the stored algorithm, `zip` can be set to ``'stored'``. + + `extensions` and `filter_func` are passed to :meth:`list_templates`. + Each template returned will be compiled to the target folder or + zipfile. + + By default template compilation errors are ignored. In case a + log function is provided, errors are logged. If you want template + syntax errors to abort the compilation you can set `ignore_errors` + to `False` and you will get an exception on syntax errors. + + If `py_compile` is set to `True` .pyc files will be written to the + target instead of standard .py files. This flag does not do anything + on pypy and Python 3 where pyc files are not picked up by itself and + don't give much benefit. + + .. versionadded:: 2.4 + """ + from jinja2.loaders import ModuleLoader + + if log_function is None: + log_function = lambda x: None + + if py_compile: + if not PY2 or PYPY: + from warnings import warn + warn(Warning('py_compile has no effect on pypy or Python 3')) + py_compile = False + else: + import imp + import marshal + py_header = imp.get_magic() + \ + u'\xff\xff\xff\xff'.encode('iso-8859-15') + + # Python 3.3 added a source filesize to the header + if sys.version_info >= (3, 3): + py_header += u'\x00\x00\x00\x00'.encode('iso-8859-15') + + def write_file(filename, data, mode): + if zip: + info = ZipInfo(filename) + info.external_attr = 0o755 << 16 + zip_file.writestr(info, data) + else: + f = open(os.path.join(target, filename), mode) + try: + f.write(data) + finally: + f.close() + + if zip is not None: + from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED + zip_file = ZipFile(target, 'w', dict(deflated=ZIP_DEFLATED, + stored=ZIP_STORED)[zip]) + log_function('Compiling into Zip archive "%s"' % target) + else: + if not os.path.isdir(target): + os.makedirs(target) + log_function('Compiling into folder "%s"' % target) + + try: + for name in self.list_templates(extensions, filter_func): + source, filename, _ = self.loader.get_source(self, name) + try: + code = self.compile(source, name, filename, True, True) + except TemplateSyntaxError as e: + if not ignore_errors: + raise + log_function('Could not compile "%s": %s' % (name, e)) + continue + + filename = ModuleLoader.get_module_filename(name) + + if py_compile: + c = self._compile(code, encode_filename(filename)) + write_file(filename + 'c', py_header + + marshal.dumps(c), 'wb') + log_function('Byte-compiled "%s" as %s' % + (name, filename + 'c')) + else: + write_file(filename, code, 'w') + log_function('Compiled "%s" as %s' % (name, filename)) + finally: + if zip: + zip_file.close() + + log_function('Finished compiling templates') + + def list_templates(self, extensions=None, filter_func=None): + """Returns a list of templates for this environment. This requires + that the loader supports the loader's + :meth:`~BaseLoader.list_templates` method. + + If there are other files in the template folder besides the + actual templates, the returned list can be filtered. There are two + ways: either `extensions` is set to a list of file extensions for + templates, or a `filter_func` can be provided which is a callable that + is passed a template name and should return `True` if it should end up + in the result list. + + If the loader does not support that, a :exc:`TypeError` is raised. + + .. versionadded:: 2.4 + """ + x = self.loader.list_templates() + if extensions is not None: + if filter_func is not None: + raise TypeError('either extensions or filter_func ' + 'can be passed, but not both') + filter_func = lambda x: '.' in x and \ + x.rsplit('.', 1)[1] in extensions + if filter_func is not None: + x = list(ifilter(filter_func, x)) + return x + + def handle_exception(self, exc_info=None, rendered=False, source_hint=None): + """Exception handling helper. This is used internally to either raise + rewritten exceptions or return a rendered traceback for the template. + """ + global _make_traceback + if exc_info is None: + exc_info = sys.exc_info() + + # the debugging module is imported when it's used for the first time. + # we're doing a lot of stuff there and for applications that do not + # get any exceptions in template rendering there is no need to load + # all of that. + if _make_traceback is None: + from jinja2.debug import make_traceback as _make_traceback + traceback = _make_traceback(exc_info, source_hint) + if rendered and self.exception_formatter is not None: + return self.exception_formatter(traceback) + if self.exception_handler is not None: + self.exception_handler(traceback) + exc_type, exc_value, tb = traceback.standard_exc_info + reraise(exc_type, exc_value, tb) + + def join_path(self, template, parent): + """Join a template with the parent. By default all the lookups are + relative to the loader root so this method returns the `template` + parameter unchanged, but if the paths should be relative to the + parent template, this function can be used to calculate the real + template name. + + Subclasses may override this method and implement template path + joining here. + """ + return template + + @internalcode + def _load_template(self, name, globals): + if self.loader is None: + raise TypeError('no loader for this environment specified') + cache_key = (weakref.ref(self.loader), name) + if self.cache is not None: + template = self.cache.get(cache_key) + if template is not None and (not self.auto_reload or + template.is_up_to_date): + return template + template = self.loader.load(self, name, globals) + if self.cache is not None: + self.cache[cache_key] = template + return template + + @internalcode + def get_template(self, name, parent=None, globals=None): + """Load a template from the loader. If a loader is configured this + method asks the loader for the template and returns a :class:`Template`. + If the `parent` parameter is not `None`, :meth:`join_path` is called + to get the real template name before loading. + + The `globals` parameter can be used to provide template wide globals. + These variables are available in the context at render time. + + If the template does not exist a :exc:`TemplateNotFound` exception is + raised. + + .. versionchanged:: 2.4 + If `name` is a :class:`Template` object it is returned from the + function unchanged. + """ + if isinstance(name, Template): + return name + if parent is not None: + name = self.join_path(name, parent) + return self._load_template(name, self.make_globals(globals)) + + @internalcode + def select_template(self, names, parent=None, globals=None): + """Works like :meth:`get_template` but tries a number of templates + before it fails. If it cannot find any of the templates, it will + raise a :exc:`TemplatesNotFound` exception. + + .. versionadded:: 2.3 + + .. versionchanged:: 2.4 + If `names` contains a :class:`Template` object it is returned + from the function unchanged. + """ + if not names: + raise TemplatesNotFound(message=u'Tried to select from an empty list ' + u'of templates.') + globals = self.make_globals(globals) + for name in names: + if isinstance(name, Template): + return name + if parent is not None: + name = self.join_path(name, parent) + try: + return self._load_template(name, globals) + except TemplateNotFound: + pass + raise TemplatesNotFound(names) + + @internalcode + def get_or_select_template(self, template_name_or_list, + parent=None, globals=None): + """Does a typecheck and dispatches to :meth:`select_template` + if an iterable of template names is given, otherwise to + :meth:`get_template`. + + .. versionadded:: 2.3 + """ + if isinstance(template_name_or_list, string_types): + return self.get_template(template_name_or_list, parent, globals) + elif isinstance(template_name_or_list, Template): + return template_name_or_list + return self.select_template(template_name_or_list, parent, globals) + + def from_string(self, source, globals=None, template_class=None): + """Load a template from a string. This parses the source given and + returns a :class:`Template` object. + """ + globals = self.make_globals(globals) + cls = template_class or self.template_class + return cls.from_code(self, self.compile(source), globals, None) + + def make_globals(self, d): + """Return a dict for the globals.""" + if not d: + return self.globals + return dict(self.globals, **d) + + +class Template(object): + """The central template object. This class represents a compiled template + and is used to evaluate it. + + Normally the template object is generated from an :class:`Environment` but + it also has a constructor that makes it possible to create a template + instance directly using the constructor. It takes the same arguments as + the environment constructor but it's not possible to specify a loader. + + Every template object has a few methods and members that are guaranteed + to exist. However it's important that a template object should be + considered immutable. Modifications on the object are not supported. + + Template objects created from the constructor rather than an environment + do have an `environment` attribute that points to a temporary environment + that is probably shared with other templates created with the constructor + and compatible settings. + + >>> template = Template('Hello {{ name }}!') + >>> template.render(name='John Doe') == u'Hello John Doe!' + True + >>> stream = template.stream(name='John Doe') + >>> next(stream) == u'Hello John Doe!' + True + >>> next(stream) + Traceback (most recent call last): + ... + StopIteration + """ + + def __new__(cls, source, + block_start_string=BLOCK_START_STRING, + block_end_string=BLOCK_END_STRING, + variable_start_string=VARIABLE_START_STRING, + variable_end_string=VARIABLE_END_STRING, + comment_start_string=COMMENT_START_STRING, + comment_end_string=COMMENT_END_STRING, + line_statement_prefix=LINE_STATEMENT_PREFIX, + line_comment_prefix=LINE_COMMENT_PREFIX, + trim_blocks=TRIM_BLOCKS, + lstrip_blocks=LSTRIP_BLOCKS, + newline_sequence=NEWLINE_SEQUENCE, + keep_trailing_newline=KEEP_TRAILING_NEWLINE, + extensions=(), + optimized=True, + undefined=Undefined, + finalize=None, + autoescape=False, + enable_async=False): + env = get_spontaneous_environment( + block_start_string, block_end_string, variable_start_string, + variable_end_string, comment_start_string, comment_end_string, + line_statement_prefix, line_comment_prefix, trim_blocks, + lstrip_blocks, newline_sequence, keep_trailing_newline, + frozenset(extensions), optimized, undefined, finalize, autoescape, + None, 0, False, None, enable_async) + return env.from_string(source, template_class=cls) + + @classmethod + def from_code(cls, environment, code, globals, uptodate=None): + """Creates a template object from compiled code and the globals. This + is used by the loaders and environment to create a template object. + """ + namespace = { + 'environment': environment, + '__file__': code.co_filename + } + exec(code, namespace) + rv = cls._from_namespace(environment, namespace, globals) + rv._uptodate = uptodate + return rv + + @classmethod + def from_module_dict(cls, environment, module_dict, globals): + """Creates a template object from a module. This is used by the + module loader to create a template object. + + .. versionadded:: 2.4 + """ + return cls._from_namespace(environment, module_dict, globals) + + @classmethod + def _from_namespace(cls, environment, namespace, globals): + t = object.__new__(cls) + t.environment = environment + t.globals = globals + t.name = namespace['name'] + t.filename = namespace['__file__'] + t.blocks = namespace['blocks'] + + # render function and module + t.root_render_func = namespace['root'] + t._module = None + + # debug and loader helpers + t._debug_info = namespace['debug_info'] + t._uptodate = None + + # store the reference + namespace['environment'] = environment + namespace['__jinja_template__'] = t + + return t + + def render(self, *args, **kwargs): + """This method accepts the same arguments as the `dict` constructor: + A dict, a dict subclass or some keyword arguments. If no arguments + are given the context will be empty. These two calls do the same:: + + template.render(knights='that say nih') + template.render({'knights': 'that say nih'}) + + This will return the rendered template as unicode string. + """ + vars = dict(*args, **kwargs) + try: + return concat(self.root_render_func(self.new_context(vars))) + except Exception: + exc_info = sys.exc_info() + return self.environment.handle_exception(exc_info, True) + + def render_async(self, *args, **kwargs): + """This works similar to :meth:`render` but returns a coroutine + that when awaited returns the entire rendered template string. This + requires the async feature to be enabled. + + Example usage:: + + await template.render_async(knights='that say nih; asynchronously') + """ + # see asyncsupport for the actual implementation + raise NotImplementedError('This feature is not available for this ' + 'version of Python') + + def stream(self, *args, **kwargs): + """Works exactly like :meth:`generate` but returns a + :class:`TemplateStream`. + """ + return TemplateStream(self.generate(*args, **kwargs)) + + def generate(self, *args, **kwargs): + """For very large templates it can be useful to not render the whole + template at once but evaluate each statement after another and yield + piece for piece. This method basically does exactly that and returns + a generator that yields one item after another as unicode strings. + + It accepts the same arguments as :meth:`render`. + """ + vars = dict(*args, **kwargs) + try: + for event in self.root_render_func(self.new_context(vars)): + yield event + except Exception: + exc_info = sys.exc_info() + else: + return + yield self.environment.handle_exception(exc_info, True) + + def generate_async(self, *args, **kwargs): + """An async version of :meth:`generate`. Works very similarly but + returns an async iterator instead. + """ + # see asyncsupport for the actual implementation + raise NotImplementedError('This feature is not available for this ' + 'version of Python') + + def new_context(self, vars=None, shared=False, locals=None): + """Create a new :class:`Context` for this template. The vars + provided will be passed to the template. Per default the globals + are added to the context. If shared is set to `True` the data + is passed as it to the context without adding the globals. + + `locals` can be a dict of local variables for internal usage. + """ + return new_context(self.environment, self.name, self.blocks, + vars, shared, self.globals, locals) + + def make_module(self, vars=None, shared=False, locals=None): + """This method works like the :attr:`module` attribute when called + without arguments but it will evaluate the template on every call + rather than caching it. It's also possible to provide + a dict which is then used as context. The arguments are the same + as for the :meth:`new_context` method. + """ + return TemplateModule(self, self.new_context(vars, shared, locals)) + + def make_module_async(self, vars=None, shared=False, locals=None): + """As template module creation can invoke template code for + asynchronous exections this method must be used instead of the + normal :meth:`make_module` one. Likewise the module attribute + becomes unavailable in async mode. + """ + # see asyncsupport for the actual implementation + raise NotImplementedError('This feature is not available for this ' + 'version of Python') + + @internalcode + def _get_default_module(self): + if self._module is not None: + return self._module + self._module = rv = self.make_module() + return rv + + @property + def module(self): + """The template as module. This is used for imports in the + template runtime but is also useful if one wants to access + exported template variables from the Python layer: + + >>> t = Template('{% macro foo() %}42{% endmacro %}23') + >>> str(t.module) + '23' + >>> t.module.foo() == u'42' + True + + This attribute is not available if async mode is enabled. + """ + return self._get_default_module() + + def get_corresponding_lineno(self, lineno): + """Return the source line number of a line number in the + generated bytecode as they are not in sync. + """ + for template_line, code_line in reversed(self.debug_info): + if code_line <= lineno: + return template_line + return 1 + + @property + def is_up_to_date(self): + """If this variable is `False` there is a newer version available.""" + if self._uptodate is None: + return True + return self._uptodate() + + @property + def debug_info(self): + """The debug info mapping.""" + return [tuple(imap(int, x.split('='))) for x in + self._debug_info.split('&')] + + def __repr__(self): + if self.name is None: + name = 'memory:%x' % id(self) + else: + name = repr(self.name) + return '<%s %s>' % (self.__class__.__name__, name) + + +@implements_to_string +class TemplateModule(object): + """Represents an imported template. All the exported names of the + template are available as attributes on this object. Additionally + converting it into an unicode- or bytestrings renders the contents. + """ + + def __init__(self, template, context, body_stream=None): + if body_stream is None: + if context.environment.is_async: + raise RuntimeError('Async mode requires a body stream ' + 'to be passed to a template module. Use ' + 'the async methods of the API you are ' + 'using.') + body_stream = list(template.root_render_func(context)) + self._body_stream = body_stream + self.__dict__.update(context.get_exported()) + self.__name__ = template.name + + def __html__(self): + return Markup(concat(self._body_stream)) + + def __str__(self): + return concat(self._body_stream) + + def __repr__(self): + if self.__name__ is None: + name = 'memory:%x' % id(self) + else: + name = repr(self.__name__) + return '<%s %s>' % (self.__class__.__name__, name) + + +class TemplateExpression(object): + """The :meth:`jinja2.Environment.compile_expression` method returns an + instance of this object. It encapsulates the expression-like access + to the template with an expression it wraps. + """ + + def __init__(self, template, undefined_to_none): + self._template = template + self._undefined_to_none = undefined_to_none + + def __call__(self, *args, **kwargs): + context = self._template.new_context(dict(*args, **kwargs)) + consume(self._template.root_render_func(context)) + rv = context.vars['result'] + if self._undefined_to_none and isinstance(rv, Undefined): + rv = None + return rv + + +@implements_iterator +class TemplateStream(object): + """A template stream works pretty much like an ordinary python generator + but it can buffer multiple items to reduce the number of total iterations. + Per default the output is unbuffered which means that for every unbuffered + instruction in the template one unicode string is yielded. + + If buffering is enabled with a buffer size of 5, five items are combined + into a new unicode string. This is mainly useful if you are streaming + big templates to a client via WSGI which flushes after each iteration. + """ + + def __init__(self, gen): + self._gen = gen + self.disable_buffering() + + def dump(self, fp, encoding=None, errors='strict'): + """Dump the complete stream into a file or file-like object. + Per default unicode strings are written, if you want to encode + before writing specify an `encoding`. + + Example usage:: + + Template('Hello {{ name }}!').stream(name='foo').dump('hello.html') + """ + close = False + if isinstance(fp, string_types): + if encoding is None: + encoding = 'utf-8' + fp = open(fp, 'wb') + close = True + try: + if encoding is not None: + iterable = (x.encode(encoding, errors) for x in self) + else: + iterable = self + if hasattr(fp, 'writelines'): + fp.writelines(iterable) + else: + for item in iterable: + fp.write(item) + finally: + if close: + fp.close() + + def disable_buffering(self): + """Disable the output buffering.""" + self._next = partial(next, self._gen) + self.buffered = False + + def _buffered_generator(self, size): + buf = [] + c_size = 0 + push = buf.append + + while 1: + try: + while c_size < size: + c = next(self._gen) + push(c) + if c: + c_size += 1 + except StopIteration: + if not c_size: + return + yield concat(buf) + del buf[:] + c_size = 0 + + def enable_buffering(self, size=5): + """Enable buffering. Buffer `size` items before yielding them.""" + if size <= 1: + raise ValueError('buffer size too small') + + self.buffered = True + self._next = partial(next, self._buffered_generator(size)) + + def __iter__(self): + return self + + def __next__(self): + return self._next() + + +# hook in default template class. if anyone reads this comment: ignore that +# it's possible to use custom templates ;-) +Environment.template_class = Template diff --git a/venv/Lib/site-packages/jinja2/exceptions.py b/venv/Lib/site-packages/jinja2/exceptions.py new file mode 100644 index 0000000..c018a33 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/exceptions.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- +""" + jinja2.exceptions + ~~~~~~~~~~~~~~~~~ + + Jinja exceptions. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +from jinja2._compat import imap, text_type, PY2, implements_to_string + + +class TemplateError(Exception): + """Baseclass for all template errors.""" + + if PY2: + def __init__(self, message=None): + if message is not None: + message = text_type(message).encode('utf-8') + Exception.__init__(self, message) + + @property + def message(self): + if self.args: + message = self.args[0] + if message is not None: + return message.decode('utf-8', 'replace') + + def __unicode__(self): + return self.message or u'' + else: + def __init__(self, message=None): + Exception.__init__(self, message) + + @property + def message(self): + if self.args: + message = self.args[0] + if message is not None: + return message + + +@implements_to_string +class TemplateNotFound(IOError, LookupError, TemplateError): + """Raised if a template does not exist.""" + + # looks weird, but removes the warning descriptor that just + # bogusly warns us about message being deprecated + message = None + + def __init__(self, name, message=None): + IOError.__init__(self) + if message is None: + message = name + self.message = message + self.name = name + self.templates = [name] + + def __str__(self): + return self.message + + +class TemplatesNotFound(TemplateNotFound): + """Like :class:`TemplateNotFound` but raised if multiple templates + are selected. This is a subclass of :class:`TemplateNotFound` + exception, so just catching the base exception will catch both. + + .. versionadded:: 2.2 + """ + + def __init__(self, names=(), message=None): + if message is None: + message = u'none of the templates given were found: ' + \ + u', '.join(imap(text_type, names)) + TemplateNotFound.__init__(self, names and names[-1] or None, message) + self.templates = list(names) + + +@implements_to_string +class TemplateSyntaxError(TemplateError): + """Raised to tell the user that there is a problem with the template.""" + + def __init__(self, message, lineno, name=None, filename=None): + TemplateError.__init__(self, message) + self.lineno = lineno + self.name = name + self.filename = filename + self.source = None + + # this is set to True if the debug.translate_syntax_error + # function translated the syntax error into a new traceback + self.translated = False + + def __str__(self): + # for translated errors we only return the message + if self.translated: + return self.message + + # otherwise attach some stuff + location = 'line %d' % self.lineno + name = self.filename or self.name + if name: + location = 'File "%s", %s' % (name, location) + lines = [self.message, ' ' + location] + + # if the source is set, add the line to the output + if self.source is not None: + try: + line = self.source.splitlines()[self.lineno - 1] + except IndexError: + line = None + if line: + lines.append(' ' + line.strip()) + + return u'\n'.join(lines) + + +class TemplateAssertionError(TemplateSyntaxError): + """Like a template syntax error, but covers cases where something in the + template caused an error at compile time that wasn't necessarily caused + by a syntax error. However it's a direct subclass of + :exc:`TemplateSyntaxError` and has the same attributes. + """ + + +class TemplateRuntimeError(TemplateError): + """A generic runtime error in the template engine. Under some situations + Jinja may raise this exception. + """ + + +class UndefinedError(TemplateRuntimeError): + """Raised if a template tries to operate on :class:`Undefined`.""" + + +class SecurityError(TemplateRuntimeError): + """Raised if a template tries to do something insecure if the + sandbox is enabled. + """ + + +class FilterArgumentError(TemplateRuntimeError): + """This error is raised if a filter was called with inappropriate + arguments + """ diff --git a/venv/Lib/site-packages/jinja2/ext.py b/venv/Lib/site-packages/jinja2/ext.py new file mode 100644 index 0000000..0734a84 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/ext.py @@ -0,0 +1,627 @@ +# -*- coding: utf-8 -*- +""" + jinja2.ext + ~~~~~~~~~~ + + Jinja extensions allow to add custom tags similar to the way django custom + tags work. By default two example extensions exist: an i18n and a cache + extension. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD. +""" +import re + +from jinja2 import nodes +from jinja2.defaults import BLOCK_START_STRING, \ + BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \ + COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \ + LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \ + KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS +from jinja2.environment import Environment +from jinja2.runtime import concat +from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError +from jinja2.utils import contextfunction, import_string, Markup +from jinja2._compat import with_metaclass, string_types, iteritems + + +# the only real useful gettext functions for a Jinja template. Note +# that ugettext must be assigned to gettext as Jinja doesn't support +# non unicode strings. +GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext') + + +class ExtensionRegistry(type): + """Gives the extension an unique identifier.""" + + def __new__(cls, name, bases, d): + rv = type.__new__(cls, name, bases, d) + rv.identifier = rv.__module__ + '.' + rv.__name__ + return rv + + +class Extension(with_metaclass(ExtensionRegistry, object)): + """Extensions can be used to add extra functionality to the Jinja template + system at the parser level. Custom extensions are bound to an environment + but may not store environment specific data on `self`. The reason for + this is that an extension can be bound to another environment (for + overlays) by creating a copy and reassigning the `environment` attribute. + + As extensions are created by the environment they cannot accept any + arguments for configuration. One may want to work around that by using + a factory function, but that is not possible as extensions are identified + by their import name. The correct way to configure the extension is + storing the configuration values on the environment. Because this way the + environment ends up acting as central configuration storage the + attributes may clash which is why extensions have to ensure that the names + they choose for configuration are not too generic. ``prefix`` for example + is a terrible name, ``fragment_cache_prefix`` on the other hand is a good + name as includes the name of the extension (fragment cache). + """ + + #: if this extension parses this is the list of tags it's listening to. + tags = set() + + #: the priority of that extension. This is especially useful for + #: extensions that preprocess values. A lower value means higher + #: priority. + #: + #: .. versionadded:: 2.4 + priority = 100 + + def __init__(self, environment): + self.environment = environment + + def bind(self, environment): + """Create a copy of this extension bound to another environment.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.environment = environment + return rv + + def preprocess(self, source, name, filename=None): + """This method is called before the actual lexing and can be used to + preprocess the source. The `filename` is optional. The return value + must be the preprocessed source. + """ + return source + + def filter_stream(self, stream): + """It's passed a :class:`~jinja2.lexer.TokenStream` that can be used + to filter tokens returned. This method has to return an iterable of + :class:`~jinja2.lexer.Token`\\s, but it doesn't have to return a + :class:`~jinja2.lexer.TokenStream`. + + In the `ext` folder of the Jinja2 source distribution there is a file + called `inlinegettext.py` which implements a filter that utilizes this + method. + """ + return stream + + def parse(self, parser): + """If any of the :attr:`tags` matched this method is called with the + parser as first argument. The token the parser stream is pointing at + is the name token that matched. This method has to return one or a + list of multiple nodes. + """ + raise NotImplementedError() + + def attr(self, name, lineno=None): + """Return an attribute node for the current extension. This is useful + to pass constants on extensions to generated template code. + + :: + + self.attr('_my_attribute', lineno=lineno) + """ + return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno) + + def call_method(self, name, args=None, kwargs=None, dyn_args=None, + dyn_kwargs=None, lineno=None): + """Call a method of the extension. This is a shortcut for + :meth:`attr` + :class:`jinja2.nodes.Call`. + """ + if args is None: + args = [] + if kwargs is None: + kwargs = [] + return nodes.Call(self.attr(name, lineno=lineno), args, kwargs, + dyn_args, dyn_kwargs, lineno=lineno) + + +@contextfunction +def _gettext_alias(__context, *args, **kwargs): + return __context.call(__context.resolve('gettext'), *args, **kwargs) + + +def _make_new_gettext(func): + @contextfunction + def gettext(__context, __string, **variables): + rv = __context.call(func, __string) + if __context.eval_ctx.autoescape: + rv = Markup(rv) + return rv % variables + return gettext + + +def _make_new_ngettext(func): + @contextfunction + def ngettext(__context, __singular, __plural, __num, **variables): + variables.setdefault('num', __num) + rv = __context.call(func, __singular, __plural, __num) + if __context.eval_ctx.autoescape: + rv = Markup(rv) + return rv % variables + return ngettext + + +class InternationalizationExtension(Extension): + """This extension adds gettext support to Jinja2.""" + tags = set(['trans']) + + # TODO: the i18n extension is currently reevaluating values in a few + # situations. Take this example: + # {% trans count=something() %}{{ count }} foo{% pluralize + # %}{{ count }} fooss{% endtrans %} + # something is called twice here. One time for the gettext value and + # the other time for the n-parameter of the ngettext function. + + def __init__(self, environment): + Extension.__init__(self, environment) + environment.globals['_'] = _gettext_alias + environment.extend( + install_gettext_translations=self._install, + install_null_translations=self._install_null, + install_gettext_callables=self._install_callables, + uninstall_gettext_translations=self._uninstall, + extract_translations=self._extract, + newstyle_gettext=False + ) + + def _install(self, translations, newstyle=None): + gettext = getattr(translations, 'ugettext', None) + if gettext is None: + gettext = translations.gettext + ngettext = getattr(translations, 'ungettext', None) + if ngettext is None: + ngettext = translations.ngettext + self._install_callables(gettext, ngettext, newstyle) + + def _install_null(self, newstyle=None): + self._install_callables( + lambda x: x, + lambda s, p, n: (n != 1 and (p,) or (s,))[0], + newstyle + ) + + def _install_callables(self, gettext, ngettext, newstyle=None): + if newstyle is not None: + self.environment.newstyle_gettext = newstyle + if self.environment.newstyle_gettext: + gettext = _make_new_gettext(gettext) + ngettext = _make_new_ngettext(ngettext) + self.environment.globals.update( + gettext=gettext, + ngettext=ngettext + ) + + def _uninstall(self, translations): + for key in 'gettext', 'ngettext': + self.environment.globals.pop(key, None) + + def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS): + if isinstance(source, string_types): + source = self.environment.parse(source) + return extract_from_ast(source, gettext_functions) + + def parse(self, parser): + """Parse a translatable tag.""" + lineno = next(parser.stream).lineno + num_called_num = False + + # find all the variables referenced. Additionally a variable can be + # defined in the body of the trans block too, but this is checked at + # a later state. + plural_expr = None + plural_expr_assignment = None + variables = {} + trimmed = None + while parser.stream.current.type != 'block_end': + if variables: + parser.stream.expect('comma') + + # skip colon for python compatibility + if parser.stream.skip_if('colon'): + break + + name = parser.stream.expect('name') + if name.value in variables: + parser.fail('translatable variable %r defined twice.' % + name.value, name.lineno, + exc=TemplateAssertionError) + + # expressions + if parser.stream.current.type == 'assign': + next(parser.stream) + variables[name.value] = var = parser.parse_expression() + elif trimmed is None and name.value in ('trimmed', 'notrimmed'): + trimmed = name.value == 'trimmed' + continue + else: + variables[name.value] = var = nodes.Name(name.value, 'load') + + if plural_expr is None: + if isinstance(var, nodes.Call): + plural_expr = nodes.Name('_trans', 'load') + variables[name.value] = plural_expr + plural_expr_assignment = nodes.Assign( + nodes.Name('_trans', 'store'), var) + else: + plural_expr = var + num_called_num = name.value == 'num' + + parser.stream.expect('block_end') + + plural = None + have_plural = False + referenced = set() + + # now parse until endtrans or pluralize + singular_names, singular = self._parse_block(parser, True) + if singular_names: + referenced.update(singular_names) + if plural_expr is None: + plural_expr = nodes.Name(singular_names[0], 'load') + num_called_num = singular_names[0] == 'num' + + # if we have a pluralize block, we parse that too + if parser.stream.current.test('name:pluralize'): + have_plural = True + next(parser.stream) + if parser.stream.current.type != 'block_end': + name = parser.stream.expect('name') + if name.value not in variables: + parser.fail('unknown variable %r for pluralization' % + name.value, name.lineno, + exc=TemplateAssertionError) + plural_expr = variables[name.value] + num_called_num = name.value == 'num' + parser.stream.expect('block_end') + plural_names, plural = self._parse_block(parser, False) + next(parser.stream) + referenced.update(plural_names) + else: + next(parser.stream) + + # register free names as simple name expressions + for var in referenced: + if var not in variables: + variables[var] = nodes.Name(var, 'load') + + if not have_plural: + plural_expr = None + elif plural_expr is None: + parser.fail('pluralize without variables', lineno) + + if trimmed is None: + trimmed = self.environment.policies['ext.i18n.trimmed'] + if trimmed: + singular = self._trim_whitespace(singular) + if plural: + plural = self._trim_whitespace(plural) + + node = self._make_node(singular, plural, variables, plural_expr, + bool(referenced), + num_called_num and have_plural) + node.set_lineno(lineno) + if plural_expr_assignment is not None: + return [plural_expr_assignment, node] + else: + return node + + def _trim_whitespace(self, string, _ws_re=re.compile(r'\s*\n\s*')): + return _ws_re.sub(' ', string.strip()) + + def _parse_block(self, parser, allow_pluralize): + """Parse until the next block tag with a given name.""" + referenced = [] + buf = [] + while 1: + if parser.stream.current.type == 'data': + buf.append(parser.stream.current.value.replace('%', '%%')) + next(parser.stream) + elif parser.stream.current.type == 'variable_begin': + next(parser.stream) + name = parser.stream.expect('name').value + referenced.append(name) + buf.append('%%(%s)s' % name) + parser.stream.expect('variable_end') + elif parser.stream.current.type == 'block_begin': + next(parser.stream) + if parser.stream.current.test('name:endtrans'): + break + elif parser.stream.current.test('name:pluralize'): + if allow_pluralize: + break + parser.fail('a translatable section can have only one ' + 'pluralize section') + parser.fail('control structures in translatable sections are ' + 'not allowed') + elif parser.stream.eos: + parser.fail('unclosed translation block') + else: + assert False, 'internal parser error' + + return referenced, concat(buf) + + def _make_node(self, singular, plural, variables, plural_expr, + vars_referenced, num_called_num): + """Generates a useful node from the data provided.""" + # no variables referenced? no need to escape for old style + # gettext invocations only if there are vars. + if not vars_referenced and not self.environment.newstyle_gettext: + singular = singular.replace('%%', '%') + if plural: + plural = plural.replace('%%', '%') + + # singular only: + if plural_expr is None: + gettext = nodes.Name('gettext', 'load') + node = nodes.Call(gettext, [nodes.Const(singular)], + [], None, None) + + # singular and plural + else: + ngettext = nodes.Name('ngettext', 'load') + node = nodes.Call(ngettext, [ + nodes.Const(singular), + nodes.Const(plural), + plural_expr + ], [], None, None) + + # in case newstyle gettext is used, the method is powerful + # enough to handle the variable expansion and autoescape + # handling itself + if self.environment.newstyle_gettext: + for key, value in iteritems(variables): + # the function adds that later anyways in case num was + # called num, so just skip it. + if num_called_num and key == 'num': + continue + node.kwargs.append(nodes.Keyword(key, value)) + + # otherwise do that here + else: + # mark the return value as safe if we are in an + # environment with autoescaping turned on + node = nodes.MarkSafeIfAutoescape(node) + if variables: + node = nodes.Mod(node, nodes.Dict([ + nodes.Pair(nodes.Const(key), value) + for key, value in variables.items() + ])) + return nodes.Output([node]) + + +class ExprStmtExtension(Extension): + """Adds a `do` tag to Jinja2 that works like the print statement just + that it doesn't print the return value. + """ + tags = set(['do']) + + def parse(self, parser): + node = nodes.ExprStmt(lineno=next(parser.stream).lineno) + node.node = parser.parse_tuple() + return node + + +class LoopControlExtension(Extension): + """Adds break and continue to the template engine.""" + tags = set(['break', 'continue']) + + def parse(self, parser): + token = next(parser.stream) + if token.value == 'break': + return nodes.Break(lineno=token.lineno) + return nodes.Continue(lineno=token.lineno) + + +class WithExtension(Extension): + pass + + +class AutoEscapeExtension(Extension): + pass + + +def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS, + babel_style=True): + """Extract localizable strings from the given template node. Per + default this function returns matches in babel style that means non string + parameters as well as keyword arguments are returned as `None`. This + allows Babel to figure out what you really meant if you are using + gettext functions that allow keyword arguments for placeholder expansion. + If you don't want that behavior set the `babel_style` parameter to `False` + which causes only strings to be returned and parameters are always stored + in tuples. As a consequence invalid gettext calls (calls without a single + string parameter or string parameters after non-string parameters) are + skipped. + + This example explains the behavior: + + >>> from jinja2 import Environment + >>> env = Environment() + >>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}') + >>> list(extract_from_ast(node)) + [(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))] + >>> list(extract_from_ast(node, babel_style=False)) + [(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))] + + For every string found this function yields a ``(lineno, function, + message)`` tuple, where: + + * ``lineno`` is the number of the line on which the string was found, + * ``function`` is the name of the ``gettext`` function used (if the + string was extracted from embedded Python code), and + * ``message`` is the string itself (a ``unicode`` object, or a tuple + of ``unicode`` objects for functions with multiple string arguments). + + This extraction function operates on the AST and is because of that unable + to extract any comments. For comment support you have to use the babel + extraction interface or extract comments yourself. + """ + for node in node.find_all(nodes.Call): + if not isinstance(node.node, nodes.Name) or \ + node.node.name not in gettext_functions: + continue + + strings = [] + for arg in node.args: + if isinstance(arg, nodes.Const) and \ + isinstance(arg.value, string_types): + strings.append(arg.value) + else: + strings.append(None) + + for arg in node.kwargs: + strings.append(None) + if node.dyn_args is not None: + strings.append(None) + if node.dyn_kwargs is not None: + strings.append(None) + + if not babel_style: + strings = tuple(x for x in strings if x is not None) + if not strings: + continue + else: + if len(strings) == 1: + strings = strings[0] + else: + strings = tuple(strings) + yield node.lineno, node.node.name, strings + + +class _CommentFinder(object): + """Helper class to find comments in a token stream. Can only + find comments for gettext calls forwards. Once the comment + from line 4 is found, a comment for line 1 will not return a + usable value. + """ + + def __init__(self, tokens, comment_tags): + self.tokens = tokens + self.comment_tags = comment_tags + self.offset = 0 + self.last_lineno = 0 + + def find_backwards(self, offset): + try: + for _, token_type, token_value in \ + reversed(self.tokens[self.offset:offset]): + if token_type in ('comment', 'linecomment'): + try: + prefix, comment = token_value.split(None, 1) + except ValueError: + continue + if prefix in self.comment_tags: + return [comment.rstrip()] + return [] + finally: + self.offset = offset + + def find_comments(self, lineno): + if not self.comment_tags or self.last_lineno > lineno: + return [] + for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset:]): + if token_lineno > lineno: + return self.find_backwards(self.offset + idx) + return self.find_backwards(len(self.tokens)) + + +def babel_extract(fileobj, keywords, comment_tags, options): + """Babel extraction method for Jinja templates. + + .. versionchanged:: 2.3 + Basic support for translation comments was added. If `comment_tags` + is now set to a list of keywords for extraction, the extractor will + try to find the best preceeding comment that begins with one of the + keywords. For best results, make sure to not have more than one + gettext call in one line of code and the matching comment in the + same line or the line before. + + .. versionchanged:: 2.5.1 + The `newstyle_gettext` flag can be set to `True` to enable newstyle + gettext calls. + + .. versionchanged:: 2.7 + A `silent` option can now be provided. If set to `False` template + syntax errors are propagated instead of being ignored. + + :param fileobj: the file-like object the messages should be extracted from + :param keywords: a list of keywords (i.e. function names) that should be + recognized as translation functions + :param comment_tags: a list of translator tags to search for and include + in the results. + :param options: a dictionary of additional options (optional) + :return: an iterator over ``(lineno, funcname, message, comments)`` tuples. + (comments will be empty currently) + """ + extensions = set() + for extension in options.get('extensions', '').split(','): + extension = extension.strip() + if not extension: + continue + extensions.add(import_string(extension)) + if InternationalizationExtension not in extensions: + extensions.add(InternationalizationExtension) + + def getbool(options, key, default=False): + return options.get(key, str(default)).lower() in \ + ('1', 'on', 'yes', 'true') + + silent = getbool(options, 'silent', True) + environment = Environment( + options.get('block_start_string', BLOCK_START_STRING), + options.get('block_end_string', BLOCK_END_STRING), + options.get('variable_start_string', VARIABLE_START_STRING), + options.get('variable_end_string', VARIABLE_END_STRING), + options.get('comment_start_string', COMMENT_START_STRING), + options.get('comment_end_string', COMMENT_END_STRING), + options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX, + options.get('line_comment_prefix') or LINE_COMMENT_PREFIX, + getbool(options, 'trim_blocks', TRIM_BLOCKS), + getbool(options, 'lstrip_blocks', LSTRIP_BLOCKS), + NEWLINE_SEQUENCE, + getbool(options, 'keep_trailing_newline', KEEP_TRAILING_NEWLINE), + frozenset(extensions), + cache_size=0, + auto_reload=False + ) + + if getbool(options, 'trimmed'): + environment.policies['ext.i18n.trimmed'] = True + if getbool(options, 'newstyle_gettext'): + environment.newstyle_gettext = True + + source = fileobj.read().decode(options.get('encoding', 'utf-8')) + try: + node = environment.parse(source) + tokens = list(environment.lex(environment.preprocess(source))) + except TemplateSyntaxError as e: + if not silent: + raise + # skip templates with syntax errors + return + + finder = _CommentFinder(tokens, comment_tags) + for lineno, func, message in extract_from_ast(node, keywords): + yield lineno, func, message, finder.find_comments(lineno) + + +#: nicer import names +i18n = InternationalizationExtension +do = ExprStmtExtension +loopcontrols = LoopControlExtension +with_ = WithExtension +autoescape = AutoEscapeExtension diff --git a/venv/Lib/site-packages/jinja2/filters.py b/venv/Lib/site-packages/jinja2/filters.py new file mode 100644 index 0000000..267dddd --- /dev/null +++ b/venv/Lib/site-packages/jinja2/filters.py @@ -0,0 +1,1190 @@ +# -*- coding: utf-8 -*- +""" + jinja2.filters + ~~~~~~~~~~~~~~ + + Bundled jinja filters. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import re +import math +import random +import warnings + +from itertools import groupby, chain +from collections import namedtuple +from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \ + unicode_urlencode, htmlsafe_json_dumps +from jinja2.runtime import Undefined +from jinja2.exceptions import FilterArgumentError +from jinja2._compat import imap, string_types, text_type, iteritems, PY2 + + +_word_re = re.compile(r'\w+', re.UNICODE) +_word_beginning_split_re = re.compile(r'([-\s\(\{\[\<]+)', re.UNICODE) + + +def contextfilter(f): + """Decorator for marking context dependent filters. The current + :class:`Context` will be passed as first argument. + """ + f.contextfilter = True + return f + + +def evalcontextfilter(f): + """Decorator for marking eval-context dependent filters. An eval + context object is passed as first argument. For more information + about the eval context, see :ref:`eval-context`. + + .. versionadded:: 2.4 + """ + f.evalcontextfilter = True + return f + + +def environmentfilter(f): + """Decorator for marking environment dependent filters. The current + :class:`Environment` is passed to the filter as first argument. + """ + f.environmentfilter = True + return f + + +def ignore_case(value): + """For use as a postprocessor for :func:`make_attrgetter`. Converts strings + to lowercase and returns other types as-is.""" + return value.lower() if isinstance(value, string_types) else value + + +def make_attrgetter(environment, attribute, postprocess=None): + """Returns a callable that looks up the given attribute from a + passed object with the rules of the environment. Dots are allowed + to access attributes of attributes. Integer parts in paths are + looked up as integers. + """ + if attribute is None: + attribute = [] + elif isinstance(attribute, string_types): + attribute = [int(x) if x.isdigit() else x for x in attribute.split('.')] + else: + attribute = [attribute] + + def attrgetter(item): + for part in attribute: + item = environment.getitem(item, part) + + if postprocess is not None: + item = postprocess(item) + + return item + + return attrgetter + + +def do_forceescape(value): + """Enforce HTML escaping. This will probably double escape variables.""" + if hasattr(value, '__html__'): + value = value.__html__() + return escape(text_type(value)) + + +def do_urlencode(value): + """Escape strings for use in URLs (uses UTF-8 encoding). It accepts both + dictionaries and regular strings as well as pairwise iterables. + + .. versionadded:: 2.7 + """ + itemiter = None + if isinstance(value, dict): + itemiter = iteritems(value) + elif not isinstance(value, string_types): + try: + itemiter = iter(value) + except TypeError: + pass + if itemiter is None: + return unicode_urlencode(value) + return u'&'.join(unicode_urlencode(k) + '=' + + unicode_urlencode(v, for_qs=True) + for k, v in itemiter) + + +@evalcontextfilter +def do_replace(eval_ctx, s, old, new, count=None): + """Return a copy of the value with all occurrences of a substring + replaced with a new one. The first argument is the substring + that should be replaced, the second is the replacement string. + If the optional third argument ``count`` is given, only the first + ``count`` occurrences are replaced: + + .. sourcecode:: jinja + + {{ "Hello World"|replace("Hello", "Goodbye") }} + -> Goodbye World + + {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} + -> d'oh, d'oh, aaargh + """ + if count is None: + count = -1 + if not eval_ctx.autoescape: + return text_type(s).replace(text_type(old), text_type(new), count) + if hasattr(old, '__html__') or hasattr(new, '__html__') and \ + not hasattr(s, '__html__'): + s = escape(s) + else: + s = soft_unicode(s) + return s.replace(soft_unicode(old), soft_unicode(new), count) + + +def do_upper(s): + """Convert a value to uppercase.""" + return soft_unicode(s).upper() + + +def do_lower(s): + """Convert a value to lowercase.""" + return soft_unicode(s).lower() + + +@evalcontextfilter +def do_xmlattr(_eval_ctx, d, autospace=True): + """Create an SGML/XML attribute string based on the items in a dict. + All values that are neither `none` nor `undefined` are automatically + escaped: + + .. sourcecode:: html+jinja + + <ul{{ {'class': 'my_list', 'missing': none, + 'id': 'list-%d'|format(variable)}|xmlattr }}> + ... + </ul> + + Results in something like this: + + .. sourcecode:: html + + <ul class="my_list" id="list-42"> + ... + </ul> + + As you can see it automatically prepends a space in front of the item + if the filter returned something unless the second parameter is false. + """ + rv = u' '.join( + u'%s="%s"' % (escape(key), escape(value)) + for key, value in iteritems(d) + if value is not None and not isinstance(value, Undefined) + ) + if autospace and rv: + rv = u' ' + rv + if _eval_ctx.autoescape: + rv = Markup(rv) + return rv + + +def do_capitalize(s): + """Capitalize a value. The first character will be uppercase, all others + lowercase. + """ + return soft_unicode(s).capitalize() + + +def do_title(s): + """Return a titlecased version of the value. I.e. words will start with + uppercase letters, all remaining characters are lowercase. + """ + return ''.join( + [item[0].upper() + item[1:].lower() + for item in _word_beginning_split_re.split(soft_unicode(s)) + if item]) + + +def do_dictsort(value, case_sensitive=False, by='key', reverse=False): + """Sort a dict and yield (key, value) pairs. Because python dicts are + unsorted you may want to use this function to order them by either + key or value: + + .. sourcecode:: jinja + + {% for item in mydict|dictsort %} + sort the dict by key, case insensitive + + {% for item in mydict|dictsort(reverse=true) %} + sort the dict by key, case insensitive, reverse order + + {% for item in mydict|dictsort(true) %} + sort the dict by key, case sensitive + + {% for item in mydict|dictsort(false, 'value') %} + sort the dict by value, case insensitive + """ + if by == 'key': + pos = 0 + elif by == 'value': + pos = 1 + else: + raise FilterArgumentError( + 'You can only sort by either "key" or "value"' + ) + + def sort_func(item): + value = item[pos] + + if not case_sensitive: + value = ignore_case(value) + + return value + + return sorted(value.items(), key=sort_func, reverse=reverse) + + +@environmentfilter +def do_sort( + environment, value, reverse=False, case_sensitive=False, attribute=None +): + """Sort an iterable. Per default it sorts ascending, if you pass it + true as first argument it will reverse the sorting. + + If the iterable is made of strings the third parameter can be used to + control the case sensitiveness of the comparison which is disabled by + default. + + .. sourcecode:: jinja + + {% for item in iterable|sort %} + ... + {% endfor %} + + It is also possible to sort by an attribute (for example to sort + by the date of an object) by specifying the `attribute` parameter: + + .. sourcecode:: jinja + + {% for item in iterable|sort(attribute='date') %} + ... + {% endfor %} + + .. versionchanged:: 2.6 + The `attribute` parameter was added. + """ + key_func = make_attrgetter( + environment, attribute, + postprocess=ignore_case if not case_sensitive else None + ) + return sorted(value, key=key_func, reverse=reverse) + + +@environmentfilter +def do_unique(environment, value, case_sensitive=False, attribute=None): + """Returns a list of unique items from the the given iterable. + + .. sourcecode:: jinja + + {{ ['foo', 'bar', 'foobar', 'FooBar']|unique }} + -> ['foo', 'bar', 'foobar'] + + The unique items are yielded in the same order as their first occurrence in + the iterable passed to the filter. + + :param case_sensitive: Treat upper and lower case strings as distinct. + :param attribute: Filter objects with unique values for this attribute. + """ + getter = make_attrgetter( + environment, attribute, + postprocess=ignore_case if not case_sensitive else None + ) + seen = set() + + for item in value: + key = getter(item) + + if key not in seen: + seen.add(key) + yield item + + +def _min_or_max(environment, value, func, case_sensitive, attribute): + it = iter(value) + + try: + first = next(it) + except StopIteration: + return environment.undefined('No aggregated item, sequence was empty.') + + key_func = make_attrgetter( + environment, attribute, + ignore_case if not case_sensitive else None + ) + return func(chain([first], it), key=key_func) + + +@environmentfilter +def do_min(environment, value, case_sensitive=False, attribute=None): + """Return the smallest item from the sequence. + + .. sourcecode:: jinja + + {{ [1, 2, 3]|min }} + -> 1 + + :param case_sensitive: Treat upper and lower case strings as distinct. + :param attribute: Get the object with the max value of this attribute. + """ + return _min_or_max(environment, value, min, case_sensitive, attribute) + + +@environmentfilter +def do_max(environment, value, case_sensitive=False, attribute=None): + """Return the largest item from the sequence. + + .. sourcecode:: jinja + + {{ [1, 2, 3]|max }} + -> 3 + + :param case_sensitive: Treat upper and lower case strings as distinct. + :param attribute: Get the object with the max value of this attribute. + """ + return _min_or_max(environment, value, max, case_sensitive, attribute) + + +def do_default(value, default_value=u'', boolean=False): + """If the value is undefined it will return the passed default value, + otherwise the value of the variable: + + .. sourcecode:: jinja + + {{ my_variable|default('my_variable is not defined') }} + + This will output the value of ``my_variable`` if the variable was + defined, otherwise ``'my_variable is not defined'``. If you want + to use default with variables that evaluate to false you have to + set the second parameter to `true`: + + .. sourcecode:: jinja + + {{ ''|default('the string was empty', true) }} + """ + if isinstance(value, Undefined) or (boolean and not value): + return default_value + return value + + +@evalcontextfilter +def do_join(eval_ctx, value, d=u'', attribute=None): + """Return a string which is the concatenation of the strings in the + sequence. The separator between elements is an empty string per + default, you can define it with the optional parameter: + + .. sourcecode:: jinja + + {{ [1, 2, 3]|join('|') }} + -> 1|2|3 + + {{ [1, 2, 3]|join }} + -> 123 + + It is also possible to join certain attributes of an object: + + .. sourcecode:: jinja + + {{ users|join(', ', attribute='username') }} + + .. versionadded:: 2.6 + The `attribute` parameter was added. + """ + if attribute is not None: + value = imap(make_attrgetter(eval_ctx.environment, attribute), value) + + # no automatic escaping? joining is a lot eaiser then + if not eval_ctx.autoescape: + return text_type(d).join(imap(text_type, value)) + + # if the delimiter doesn't have an html representation we check + # if any of the items has. If yes we do a coercion to Markup + if not hasattr(d, '__html__'): + value = list(value) + do_escape = False + for idx, item in enumerate(value): + if hasattr(item, '__html__'): + do_escape = True + else: + value[idx] = text_type(item) + if do_escape: + d = escape(d) + else: + d = text_type(d) + return d.join(value) + + # no html involved, to normal joining + return soft_unicode(d).join(imap(soft_unicode, value)) + + +def do_center(value, width=80): + """Centers the value in a field of a given width.""" + return text_type(value).center(width) + + +@environmentfilter +def do_first(environment, seq): + """Return the first item of a sequence.""" + try: + return next(iter(seq)) + except StopIteration: + return environment.undefined('No first item, sequence was empty.') + + +@environmentfilter +def do_last(environment, seq): + """Return the last item of a sequence.""" + try: + return next(iter(reversed(seq))) + except StopIteration: + return environment.undefined('No last item, sequence was empty.') + + +@contextfilter +def do_random(context, seq): + """Return a random item from the sequence.""" + try: + return random.choice(seq) + except IndexError: + return context.environment.undefined('No random item, sequence was empty.') + + +def do_filesizeformat(value, binary=False): + """Format the value like a 'human-readable' file size (i.e. 13 kB, + 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, + Giga, etc.), if the second parameter is set to `True` the binary + prefixes are used (Mebi, Gibi). + """ + bytes = float(value) + base = binary and 1024 or 1000 + prefixes = [ + (binary and 'KiB' or 'kB'), + (binary and 'MiB' or 'MB'), + (binary and 'GiB' or 'GB'), + (binary and 'TiB' or 'TB'), + (binary and 'PiB' or 'PB'), + (binary and 'EiB' or 'EB'), + (binary and 'ZiB' or 'ZB'), + (binary and 'YiB' or 'YB') + ] + if bytes == 1: + return '1 Byte' + elif bytes < base: + return '%d Bytes' % bytes + else: + for i, prefix in enumerate(prefixes): + unit = base ** (i + 2) + if bytes < unit: + return '%.1f %s' % ((base * bytes / unit), prefix) + return '%.1f %s' % ((base * bytes / unit), prefix) + + +def do_pprint(value, verbose=False): + """Pretty print a variable. Useful for debugging. + + With Jinja 1.2 onwards you can pass it a parameter. If this parameter + is truthy the output will be more verbose (this requires `pretty`) + """ + return pformat(value, verbose=verbose) + + +@evalcontextfilter +def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False, + target=None, rel=None): + """Converts URLs in plain text into clickable links. + + If you pass the filter an additional integer it will shorten the urls + to that number. Also a third argument exists that makes the urls + "nofollow": + + .. sourcecode:: jinja + + {{ mytext|urlize(40, true) }} + links are shortened to 40 chars and defined with rel="nofollow" + + If *target* is specified, the ``target`` attribute will be added to the + ``<a>`` tag: + + .. sourcecode:: jinja + + {{ mytext|urlize(40, target='_blank') }} + + .. versionchanged:: 2.8+ + The *target* parameter was added. + """ + policies = eval_ctx.environment.policies + rel = set((rel or '').split() or []) + if nofollow: + rel.add('nofollow') + rel.update((policies['urlize.rel'] or '').split()) + if target is None: + target = policies['urlize.target'] + rel = ' '.join(sorted(rel)) or None + rv = urlize(value, trim_url_limit, rel=rel, target=target) + if eval_ctx.autoescape: + rv = Markup(rv) + return rv + + +def do_indent( + s, width=4, first=False, blank=False, indentfirst=None +): + """Return a copy of the string with each line indented by 4 spaces. The + first line and blank lines are not indented by default. + + :param width: Number of spaces to indent by. + :param first: Don't skip indenting the first line. + :param blank: Don't skip indenting empty lines. + + .. versionchanged:: 2.10 + Blank lines are not indented by default. + + Rename the ``indentfirst`` argument to ``first``. + """ + if indentfirst is not None: + warnings.warn(DeprecationWarning( + 'The "indentfirst" argument is renamed to "first".' + ), stacklevel=2) + first = indentfirst + + s += u'\n' # this quirk is necessary for splitlines method + indention = u' ' * width + + if blank: + rv = (u'\n' + indention).join(s.splitlines()) + else: + lines = s.splitlines() + rv = lines.pop(0) + + if lines: + rv += u'\n' + u'\n'.join( + indention + line if line else line for line in lines + ) + + if first: + rv = indention + rv + + return rv + + +@environmentfilter +def do_truncate(env, s, length=255, killwords=False, end='...', leeway=None): + """Return a truncated copy of the string. The length is specified + with the first parameter which defaults to ``255``. If the second + parameter is ``true`` the filter will cut the text at length. Otherwise + it will discard the last word. If the text was in fact + truncated it will append an ellipsis sign (``"..."``). If you want a + different ellipsis sign than ``"..."`` you can specify it using the + third parameter. Strings that only exceed the length by the tolerance + margin given in the fourth parameter will not be truncated. + + .. sourcecode:: jinja + + {{ "foo bar baz qux"|truncate(9) }} + -> "foo..." + {{ "foo bar baz qux"|truncate(9, True) }} + -> "foo ba..." + {{ "foo bar baz qux"|truncate(11) }} + -> "foo bar baz qux" + {{ "foo bar baz qux"|truncate(11, False, '...', 0) }} + -> "foo bar..." + + The default leeway on newer Jinja2 versions is 5 and was 0 before but + can be reconfigured globally. + """ + if leeway is None: + leeway = env.policies['truncate.leeway'] + assert length >= len(end), 'expected length >= %s, got %s' % (len(end), length) + assert leeway >= 0, 'expected leeway >= 0, got %s' % leeway + if len(s) <= length + leeway: + return s + if killwords: + return s[:length - len(end)] + end + result = s[:length - len(end)].rsplit(' ', 1)[0] + return result + end + + +@environmentfilter +def do_wordwrap(environment, s, width=79, break_long_words=True, + wrapstring=None): + """ + Return a copy of the string passed to the filter wrapped after + ``79`` characters. You can override this default using the first + parameter. If you set the second parameter to `false` Jinja will not + split words apart if they are longer than `width`. By default, the newlines + will be the default newlines for the environment, but this can be changed + using the wrapstring keyword argument. + + .. versionadded:: 2.7 + Added support for the `wrapstring` parameter. + """ + if not wrapstring: + wrapstring = environment.newline_sequence + import textwrap + return wrapstring.join(textwrap.wrap(s, width=width, expand_tabs=False, + replace_whitespace=False, + break_long_words=break_long_words)) + + +def do_wordcount(s): + """Count the words in that string.""" + return len(_word_re.findall(s)) + + +def do_int(value, default=0, base=10): + """Convert the value into an integer. If the + conversion doesn't work it will return ``0``. You can + override this default using the first parameter. You + can also override the default base (10) in the second + parameter, which handles input with prefixes such as + 0b, 0o and 0x for bases 2, 8 and 16 respectively. + The base is ignored for decimal numbers and non-string values. + """ + try: + if isinstance(value, string_types): + return int(value, base) + return int(value) + except (TypeError, ValueError): + # this quirk is necessary so that "42.23"|int gives 42. + try: + return int(float(value)) + except (TypeError, ValueError): + return default + + +def do_float(value, default=0.0): + """Convert the value into a floating point number. If the + conversion doesn't work it will return ``0.0``. You can + override this default using the first parameter. + """ + try: + return float(value) + except (TypeError, ValueError): + return default + + +def do_format(value, *args, **kwargs): + """ + Apply python string formatting on an object: + + .. sourcecode:: jinja + + {{ "%s - %s"|format("Hello?", "Foo!") }} + -> Hello? - Foo! + """ + if args and kwargs: + raise FilterArgumentError('can\'t handle positional and keyword ' + 'arguments at the same time') + return soft_unicode(value) % (kwargs or args) + + +def do_trim(value): + """Strip leading and trailing whitespace.""" + return soft_unicode(value).strip() + + +def do_striptags(value): + """Strip SGML/XML tags and replace adjacent whitespace by one space. + """ + if hasattr(value, '__html__'): + value = value.__html__() + return Markup(text_type(value)).striptags() + + +def do_slice(value, slices, fill_with=None): + """Slice an iterator and return a list of lists containing + those items. Useful if you want to create a div containing + three ul tags that represent columns: + + .. sourcecode:: html+jinja + + <div class="columwrapper"> + {%- for column in items|slice(3) %} + <ul class="column-{{ loop.index }}"> + {%- for item in column %} + <li>{{ item }}</li> + {%- endfor %} + </ul> + {%- endfor %} + </div> + + If you pass it a second argument it's used to fill missing + values on the last iteration. + """ + seq = list(value) + length = len(seq) + items_per_slice = length // slices + slices_with_extra = length % slices + offset = 0 + for slice_number in range(slices): + start = offset + slice_number * items_per_slice + if slice_number < slices_with_extra: + offset += 1 + end = offset + (slice_number + 1) * items_per_slice + tmp = seq[start:end] + if fill_with is not None and slice_number >= slices_with_extra: + tmp.append(fill_with) + yield tmp + + +def do_batch(value, linecount, fill_with=None): + """ + A filter that batches items. It works pretty much like `slice` + just the other way round. It returns a list of lists with the + given number of items. If you provide a second parameter this + is used to fill up missing items. See this example: + + .. sourcecode:: html+jinja + + <table> + {%- for row in items|batch(3, ' ') %} + <tr> + {%- for column in row %} + <td>{{ column }}</td> + {%- endfor %} + </tr> + {%- endfor %} + </table> + """ + tmp = [] + for item in value: + if len(tmp) == linecount: + yield tmp + tmp = [] + tmp.append(item) + if tmp: + if fill_with is not None and len(tmp) < linecount: + tmp += [fill_with] * (linecount - len(tmp)) + yield tmp + + +def do_round(value, precision=0, method='common'): + """Round the number to a given precision. The first + parameter specifies the precision (default is ``0``), the + second the rounding method: + + - ``'common'`` rounds either up or down + - ``'ceil'`` always rounds up + - ``'floor'`` always rounds down + + If you don't specify a method ``'common'`` is used. + + .. sourcecode:: jinja + + {{ 42.55|round }} + -> 43.0 + {{ 42.55|round(1, 'floor') }} + -> 42.5 + + Note that even if rounded to 0 precision, a float is returned. If + you need a real integer, pipe it through `int`: + + .. sourcecode:: jinja + + {{ 42.55|round|int }} + -> 43 + """ + if not method in ('common', 'ceil', 'floor'): + raise FilterArgumentError('method must be common, ceil or floor') + if method == 'common': + return round(value, precision) + func = getattr(math, method) + return func(value * (10 ** precision)) / (10 ** precision) + + +# Use a regular tuple repr here. This is what we did in the past and we +# really want to hide this custom type as much as possible. In particular +# we do not want to accidentally expose an auto generated repr in case +# people start to print this out in comments or something similar for +# debugging. +_GroupTuple = namedtuple('_GroupTuple', ['grouper', 'list']) +_GroupTuple.__repr__ = tuple.__repr__ +_GroupTuple.__str__ = tuple.__str__ + +@environmentfilter +def do_groupby(environment, value, attribute): + """Group a sequence of objects by a common attribute. + + If you for example have a list of dicts or objects that represent persons + with `gender`, `first_name` and `last_name` attributes and you want to + group all users by genders you can do something like the following + snippet: + + .. sourcecode:: html+jinja + + <ul> + {% for group in persons|groupby('gender') %} + <li>{{ group.grouper }}<ul> + {% for person in group.list %} + <li>{{ person.first_name }} {{ person.last_name }}</li> + {% endfor %}</ul></li> + {% endfor %} + </ul> + + Additionally it's possible to use tuple unpacking for the grouper and + list: + + .. sourcecode:: html+jinja + + <ul> + {% for grouper, list in persons|groupby('gender') %} + ... + {% endfor %} + </ul> + + As you can see the item we're grouping by is stored in the `grouper` + attribute and the `list` contains all the objects that have this grouper + in common. + + .. versionchanged:: 2.6 + It's now possible to use dotted notation to group by the child + attribute of another attribute. + """ + expr = make_attrgetter(environment, attribute) + return [_GroupTuple(key, list(values)) for key, values + in groupby(sorted(value, key=expr), expr)] + + +@environmentfilter +def do_sum(environment, iterable, attribute=None, start=0): + """Returns the sum of a sequence of numbers plus the value of parameter + 'start' (which defaults to 0). When the sequence is empty it returns + start. + + It is also possible to sum up only certain attributes: + + .. sourcecode:: jinja + + Total: {{ items|sum(attribute='price') }} + + .. versionchanged:: 2.6 + The `attribute` parameter was added to allow suming up over + attributes. Also the `start` parameter was moved on to the right. + """ + if attribute is not None: + iterable = imap(make_attrgetter(environment, attribute), iterable) + return sum(iterable, start) + + +def do_list(value): + """Convert the value into a list. If it was a string the returned list + will be a list of characters. + """ + return list(value) + + +def do_mark_safe(value): + """Mark the value as safe which means that in an environment with automatic + escaping enabled this variable will not be escaped. + """ + return Markup(value) + + +def do_mark_unsafe(value): + """Mark a value as unsafe. This is the reverse operation for :func:`safe`.""" + return text_type(value) + + +def do_reverse(value): + """Reverse the object or return an iterator that iterates over it the other + way round. + """ + if isinstance(value, string_types): + return value[::-1] + try: + return reversed(value) + except TypeError: + try: + rv = list(value) + rv.reverse() + return rv + except TypeError: + raise FilterArgumentError('argument must be iterable') + + +@environmentfilter +def do_attr(environment, obj, name): + """Get an attribute of an object. ``foo|attr("bar")`` works like + ``foo.bar`` just that always an attribute is returned and items are not + looked up. + + See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details. + """ + try: + name = str(name) + except UnicodeError: + pass + else: + try: + value = getattr(obj, name) + except AttributeError: + pass + else: + if environment.sandboxed and not \ + environment.is_safe_attribute(obj, name, value): + return environment.unsafe_undefined(obj, name) + return value + return environment.undefined(obj=obj, name=name) + + +@contextfilter +def do_map(*args, **kwargs): + """Applies a filter on a sequence of objects or looks up an attribute. + This is useful when dealing with lists of objects but you are really + only interested in a certain value of it. + + The basic usage is mapping on an attribute. Imagine you have a list + of users but you are only interested in a list of usernames: + + .. sourcecode:: jinja + + Users on this page: {{ users|map(attribute='username')|join(', ') }} + + Alternatively you can let it invoke a filter by passing the name of the + filter and the arguments afterwards. A good example would be applying a + text conversion filter on a sequence: + + .. sourcecode:: jinja + + Users on this page: {{ titles|map('lower')|join(', ') }} + + .. versionadded:: 2.7 + """ + seq, func = prepare_map(args, kwargs) + if seq: + for item in seq: + yield func(item) + + +@contextfilter +def do_select(*args, **kwargs): + """Filters a sequence of objects by applying a test to each object, + and only selecting the objects with the test succeeding. + + If no test is specified, each object will be evaluated as a boolean. + + Example usage: + + .. sourcecode:: jinja + + {{ numbers|select("odd") }} + {{ numbers|select("odd") }} + {{ numbers|select("divisibleby", 3) }} + {{ numbers|select("lessthan", 42) }} + {{ strings|select("equalto", "mystring") }} + + .. versionadded:: 2.7 + """ + return select_or_reject(args, kwargs, lambda x: x, False) + + +@contextfilter +def do_reject(*args, **kwargs): + """Filters a sequence of objects by applying a test to each object, + and rejecting the objects with the test succeeding. + + If no test is specified, each object will be evaluated as a boolean. + + Example usage: + + .. sourcecode:: jinja + + {{ numbers|reject("odd") }} + + .. versionadded:: 2.7 + """ + return select_or_reject(args, kwargs, lambda x: not x, False) + + +@contextfilter +def do_selectattr(*args, **kwargs): + """Filters a sequence of objects by applying a test to the specified + attribute of each object, and only selecting the objects with the + test succeeding. + + If no test is specified, the attribute's value will be evaluated as + a boolean. + + Example usage: + + .. sourcecode:: jinja + + {{ users|selectattr("is_active") }} + {{ users|selectattr("email", "none") }} + + .. versionadded:: 2.7 + """ + return select_or_reject(args, kwargs, lambda x: x, True) + + +@contextfilter +def do_rejectattr(*args, **kwargs): + """Filters a sequence of objects by applying a test to the specified + attribute of each object, and rejecting the objects with the test + succeeding. + + If no test is specified, the attribute's value will be evaluated as + a boolean. + + .. sourcecode:: jinja + + {{ users|rejectattr("is_active") }} + {{ users|rejectattr("email", "none") }} + + .. versionadded:: 2.7 + """ + return select_or_reject(args, kwargs, lambda x: not x, True) + + +@evalcontextfilter +def do_tojson(eval_ctx, value, indent=None): + """Dumps a structure to JSON so that it's safe to use in ``<script>`` + tags. It accepts the same arguments and returns a JSON string. Note that + this is available in templates through the ``|tojson`` filter which will + also mark the result as safe. Due to how this function escapes certain + characters this is safe even if used outside of ``<script>`` tags. + + The following characters are escaped in strings: + + - ``<`` + - ``>`` + - ``&`` + - ``'`` + + This makes it safe to embed such strings in any place in HTML with the + notable exception of double quoted attributes. In that case single + quote your attributes or HTML escape it in addition. + + The indent parameter can be used to enable pretty printing. Set it to + the number of spaces that the structures should be indented with. + + Note that this filter is for use in HTML contexts only. + + .. versionadded:: 2.9 + """ + policies = eval_ctx.environment.policies + dumper = policies['json.dumps_function'] + options = policies['json.dumps_kwargs'] + if indent is not None: + options = dict(options) + options['indent'] = indent + return htmlsafe_json_dumps(value, dumper=dumper, **options) + + +def prepare_map(args, kwargs): + context = args[0] + seq = args[1] + + if len(args) == 2 and 'attribute' in kwargs: + attribute = kwargs.pop('attribute') + if kwargs: + raise FilterArgumentError('Unexpected keyword argument %r' % + next(iter(kwargs))) + func = make_attrgetter(context.environment, attribute) + else: + try: + name = args[2] + args = args[3:] + except LookupError: + raise FilterArgumentError('map requires a filter argument') + func = lambda item: context.environment.call_filter( + name, item, args, kwargs, context=context) + + return seq, func + + +def prepare_select_or_reject(args, kwargs, modfunc, lookup_attr): + context = args[0] + seq = args[1] + if lookup_attr: + try: + attr = args[2] + except LookupError: + raise FilterArgumentError('Missing parameter for attribute name') + transfunc = make_attrgetter(context.environment, attr) + off = 1 + else: + off = 0 + transfunc = lambda x: x + + try: + name = args[2 + off] + args = args[3 + off:] + func = lambda item: context.environment.call_test( + name, item, args, kwargs) + except LookupError: + func = bool + + return seq, lambda item: modfunc(func(transfunc(item))) + + +def select_or_reject(args, kwargs, modfunc, lookup_attr): + seq, func = prepare_select_or_reject(args, kwargs, modfunc, lookup_attr) + if seq: + for item in seq: + if func(item): + yield item + + +FILTERS = { + 'abs': abs, + 'attr': do_attr, + 'batch': do_batch, + 'capitalize': do_capitalize, + 'center': do_center, + 'count': len, + 'd': do_default, + 'default': do_default, + 'dictsort': do_dictsort, + 'e': escape, + 'escape': escape, + 'filesizeformat': do_filesizeformat, + 'first': do_first, + 'float': do_float, + 'forceescape': do_forceescape, + 'format': do_format, + 'groupby': do_groupby, + 'indent': do_indent, + 'int': do_int, + 'join': do_join, + 'last': do_last, + 'length': len, + 'list': do_list, + 'lower': do_lower, + 'map': do_map, + 'min': do_min, + 'max': do_max, + 'pprint': do_pprint, + 'random': do_random, + 'reject': do_reject, + 'rejectattr': do_rejectattr, + 'replace': do_replace, + 'reverse': do_reverse, + 'round': do_round, + 'safe': do_mark_safe, + 'select': do_select, + 'selectattr': do_selectattr, + 'slice': do_slice, + 'sort': do_sort, + 'string': soft_unicode, + 'striptags': do_striptags, + 'sum': do_sum, + 'title': do_title, + 'trim': do_trim, + 'truncate': do_truncate, + 'unique': do_unique, + 'upper': do_upper, + 'urlencode': do_urlencode, + 'urlize': do_urlize, + 'wordcount': do_wordcount, + 'wordwrap': do_wordwrap, + 'xmlattr': do_xmlattr, + 'tojson': do_tojson, +} diff --git a/venv/Lib/site-packages/jinja2/idtracking.py b/venv/Lib/site-packages/jinja2/idtracking.py new file mode 100644 index 0000000..491bfe0 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/idtracking.py @@ -0,0 +1,286 @@ +from jinja2.visitor import NodeVisitor +from jinja2._compat import iteritems + + +VAR_LOAD_PARAMETER = 'param' +VAR_LOAD_RESOLVE = 'resolve' +VAR_LOAD_ALIAS = 'alias' +VAR_LOAD_UNDEFINED = 'undefined' + + +def find_symbols(nodes, parent_symbols=None): + sym = Symbols(parent=parent_symbols) + visitor = FrameSymbolVisitor(sym) + for node in nodes: + visitor.visit(node) + return sym + + +def symbols_for_node(node, parent_symbols=None): + sym = Symbols(parent=parent_symbols) + sym.analyze_node(node) + return sym + + +class Symbols(object): + + def __init__(self, parent=None, level=None): + if level is None: + if parent is None: + level = 0 + else: + level = parent.level + 1 + self.level = level + self.parent = parent + self.refs = {} + self.loads = {} + self.stores = set() + + def analyze_node(self, node, **kwargs): + visitor = RootVisitor(self) + visitor.visit(node, **kwargs) + + def _define_ref(self, name, load=None): + ident = 'l_%d_%s' % (self.level, name) + self.refs[name] = ident + if load is not None: + self.loads[ident] = load + return ident + + def find_load(self, target): + if target in self.loads: + return self.loads[target] + if self.parent is not None: + return self.parent.find_load(target) + + def find_ref(self, name): + if name in self.refs: + return self.refs[name] + if self.parent is not None: + return self.parent.find_ref(name) + + def ref(self, name): + rv = self.find_ref(name) + if rv is None: + raise AssertionError('Tried to resolve a name to a reference that ' + 'was unknown to the frame (%r)' % name) + return rv + + def copy(self): + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.refs = self.refs.copy() + rv.loads = self.loads.copy() + rv.stores = self.stores.copy() + return rv + + def store(self, name): + self.stores.add(name) + + # If we have not see the name referenced yet, we need to figure + # out what to set it to. + if name not in self.refs: + # If there is a parent scope we check if the name has a + # reference there. If it does it means we might have to alias + # to a variable there. + if self.parent is not None: + outer_ref = self.parent.find_ref(name) + if outer_ref is not None: + self._define_ref(name, load=(VAR_LOAD_ALIAS, outer_ref)) + return + + # Otherwise we can just set it to undefined. + self._define_ref(name, load=(VAR_LOAD_UNDEFINED, None)) + + def declare_parameter(self, name): + self.stores.add(name) + return self._define_ref(name, load=(VAR_LOAD_PARAMETER, None)) + + def load(self, name): + target = self.find_ref(name) + if target is None: + self._define_ref(name, load=(VAR_LOAD_RESOLVE, name)) + + def branch_update(self, branch_symbols): + stores = {} + for branch in branch_symbols: + for target in branch.stores: + if target in self.stores: + continue + stores[target] = stores.get(target, 0) + 1 + + for sym in branch_symbols: + self.refs.update(sym.refs) + self.loads.update(sym.loads) + self.stores.update(sym.stores) + + for name, branch_count in iteritems(stores): + if branch_count == len(branch_symbols): + continue + target = self.find_ref(name) + assert target is not None, 'should not happen' + + if self.parent is not None: + outer_target = self.parent.find_ref(name) + if outer_target is not None: + self.loads[target] = (VAR_LOAD_ALIAS, outer_target) + continue + self.loads[target] = (VAR_LOAD_RESOLVE, name) + + def dump_stores(self): + rv = {} + node = self + while node is not None: + for name in node.stores: + if name not in rv: + rv[name] = self.find_ref(name) + node = node.parent + return rv + + def dump_param_targets(self): + rv = set() + node = self + while node is not None: + for target, (instr, _) in iteritems(self.loads): + if instr == VAR_LOAD_PARAMETER: + rv.add(target) + node = node.parent + return rv + + +class RootVisitor(NodeVisitor): + + def __init__(self, symbols): + self.sym_visitor = FrameSymbolVisitor(symbols) + + def _simple_visit(self, node, **kwargs): + for child in node.iter_child_nodes(): + self.sym_visitor.visit(child) + + visit_Template = visit_Block = visit_Macro = visit_FilterBlock = \ + visit_Scope = visit_If = visit_ScopedEvalContextModifier = \ + _simple_visit + + def visit_AssignBlock(self, node, **kwargs): + for child in node.body: + self.sym_visitor.visit(child) + + def visit_CallBlock(self, node, **kwargs): + for child in node.iter_child_nodes(exclude=('call',)): + self.sym_visitor.visit(child) + + def visit_OverlayScope(self, node, **kwargs): + for child in node.body: + self.sym_visitor.visit(child) + + def visit_For(self, node, for_branch='body', **kwargs): + if for_branch == 'body': + self.sym_visitor.visit(node.target, store_as_param=True) + branch = node.body + elif for_branch == 'else': + branch = node.else_ + elif for_branch == 'test': + self.sym_visitor.visit(node.target, store_as_param=True) + if node.test is not None: + self.sym_visitor.visit(node.test) + return + else: + raise RuntimeError('Unknown for branch') + for item in branch or (): + self.sym_visitor.visit(item) + + def visit_With(self, node, **kwargs): + for target in node.targets: + self.sym_visitor.visit(target) + for child in node.body: + self.sym_visitor.visit(child) + + def generic_visit(self, node, *args, **kwargs): + raise NotImplementedError('Cannot find symbols for %r' % + node.__class__.__name__) + + +class FrameSymbolVisitor(NodeVisitor): + """A visitor for `Frame.inspect`.""" + + def __init__(self, symbols): + self.symbols = symbols + + def visit_Name(self, node, store_as_param=False, **kwargs): + """All assignments to names go through this function.""" + if store_as_param or node.ctx == 'param': + self.symbols.declare_parameter(node.name) + elif node.ctx == 'store': + self.symbols.store(node.name) + elif node.ctx == 'load': + self.symbols.load(node.name) + + def visit_NSRef(self, node, **kwargs): + self.symbols.load(node.name) + + def visit_If(self, node, **kwargs): + self.visit(node.test, **kwargs) + + original_symbols = self.symbols + + def inner_visit(nodes): + self.symbols = rv = original_symbols.copy() + for subnode in nodes: + self.visit(subnode, **kwargs) + self.symbols = original_symbols + return rv + + body_symbols = inner_visit(node.body) + elif_symbols = inner_visit(node.elif_) + else_symbols = inner_visit(node.else_ or ()) + + self.symbols.branch_update([body_symbols, elif_symbols, else_symbols]) + + def visit_Macro(self, node, **kwargs): + self.symbols.store(node.name) + + def visit_Import(self, node, **kwargs): + self.generic_visit(node, **kwargs) + self.symbols.store(node.target) + + def visit_FromImport(self, node, **kwargs): + self.generic_visit(node, **kwargs) + for name in node.names: + if isinstance(name, tuple): + self.symbols.store(name[1]) + else: + self.symbols.store(name) + + def visit_Assign(self, node, **kwargs): + """Visit assignments in the correct order.""" + self.visit(node.node, **kwargs) + self.visit(node.target, **kwargs) + + def visit_For(self, node, **kwargs): + """Visiting stops at for blocks. However the block sequence + is visited as part of the outer scope. + """ + self.visit(node.iter, **kwargs) + + def visit_CallBlock(self, node, **kwargs): + self.visit(node.call, **kwargs) + + def visit_FilterBlock(self, node, **kwargs): + self.visit(node.filter, **kwargs) + + def visit_With(self, node, **kwargs): + for target in node.values: + self.visit(target) + + def visit_AssignBlock(self, node, **kwargs): + """Stop visiting at block assigns.""" + self.visit(node.target, **kwargs) + + def visit_Scope(self, node, **kwargs): + """Stop visiting at scopes.""" + + def visit_Block(self, node, **kwargs): + """Stop visiting at blocks.""" + + def visit_OverlayScope(self, node, **kwargs): + """Do not visit into overlay scopes.""" diff --git a/venv/Lib/site-packages/jinja2/lexer.py b/venv/Lib/site-packages/jinja2/lexer.py new file mode 100644 index 0000000..6fd135d --- /dev/null +++ b/venv/Lib/site-packages/jinja2/lexer.py @@ -0,0 +1,739 @@ +# -*- coding: utf-8 -*- +""" + jinja2.lexer + ~~~~~~~~~~~~ + + This module implements a Jinja / Python combination lexer. The + `Lexer` class provided by this module is used to do some preprocessing + for Jinja. + + On the one hand it filters out invalid operators like the bitshift + operators we don't allow in templates. On the other hand it separates + template code and python code in expressions. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import re +from collections import deque +from operator import itemgetter + +from jinja2._compat import implements_iterator, intern, iteritems, text_type +from jinja2.exceptions import TemplateSyntaxError +from jinja2.utils import LRUCache + +# cache for the lexers. Exists in order to be able to have multiple +# environments with the same lexer +_lexer_cache = LRUCache(50) + +# static regular expressions +whitespace_re = re.compile(r'\s+', re.U) +string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'" + r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S) +integer_re = re.compile(r'\d+') + +try: + # check if this Python supports Unicode identifiers + compile('föö', '<unknown>', 'eval') +except SyntaxError: + # no Unicode support, use ASCII identifiers + name_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*') + check_ident = False +else: + # Unicode support, build a pattern to match valid characters, and set flag + # to use str.isidentifier to validate during lexing + from jinja2 import _identifier + name_re = re.compile(r'[\w{0}]+'.format(_identifier.pattern)) + check_ident = True + # remove the pattern from memory after building the regex + import sys + del sys.modules['jinja2._identifier'] + import jinja2 + del jinja2._identifier + del _identifier + +float_re = re.compile(r'(?<!\.)\d+\.\d+') +newline_re = re.compile(r'(\r\n|\r|\n)') + +# internal the tokens and keep references to them +TOKEN_ADD = intern('add') +TOKEN_ASSIGN = intern('assign') +TOKEN_COLON = intern('colon') +TOKEN_COMMA = intern('comma') +TOKEN_DIV = intern('div') +TOKEN_DOT = intern('dot') +TOKEN_EQ = intern('eq') +TOKEN_FLOORDIV = intern('floordiv') +TOKEN_GT = intern('gt') +TOKEN_GTEQ = intern('gteq') +TOKEN_LBRACE = intern('lbrace') +TOKEN_LBRACKET = intern('lbracket') +TOKEN_LPAREN = intern('lparen') +TOKEN_LT = intern('lt') +TOKEN_LTEQ = intern('lteq') +TOKEN_MOD = intern('mod') +TOKEN_MUL = intern('mul') +TOKEN_NE = intern('ne') +TOKEN_PIPE = intern('pipe') +TOKEN_POW = intern('pow') +TOKEN_RBRACE = intern('rbrace') +TOKEN_RBRACKET = intern('rbracket') +TOKEN_RPAREN = intern('rparen') +TOKEN_SEMICOLON = intern('semicolon') +TOKEN_SUB = intern('sub') +TOKEN_TILDE = intern('tilde') +TOKEN_WHITESPACE = intern('whitespace') +TOKEN_FLOAT = intern('float') +TOKEN_INTEGER = intern('integer') +TOKEN_NAME = intern('name') +TOKEN_STRING = intern('string') +TOKEN_OPERATOR = intern('operator') +TOKEN_BLOCK_BEGIN = intern('block_begin') +TOKEN_BLOCK_END = intern('block_end') +TOKEN_VARIABLE_BEGIN = intern('variable_begin') +TOKEN_VARIABLE_END = intern('variable_end') +TOKEN_RAW_BEGIN = intern('raw_begin') +TOKEN_RAW_END = intern('raw_end') +TOKEN_COMMENT_BEGIN = intern('comment_begin') +TOKEN_COMMENT_END = intern('comment_end') +TOKEN_COMMENT = intern('comment') +TOKEN_LINESTATEMENT_BEGIN = intern('linestatement_begin') +TOKEN_LINESTATEMENT_END = intern('linestatement_end') +TOKEN_LINECOMMENT_BEGIN = intern('linecomment_begin') +TOKEN_LINECOMMENT_END = intern('linecomment_end') +TOKEN_LINECOMMENT = intern('linecomment') +TOKEN_DATA = intern('data') +TOKEN_INITIAL = intern('initial') +TOKEN_EOF = intern('eof') + +# bind operators to token types +operators = { + '+': TOKEN_ADD, + '-': TOKEN_SUB, + '/': TOKEN_DIV, + '//': TOKEN_FLOORDIV, + '*': TOKEN_MUL, + '%': TOKEN_MOD, + '**': TOKEN_POW, + '~': TOKEN_TILDE, + '[': TOKEN_LBRACKET, + ']': TOKEN_RBRACKET, + '(': TOKEN_LPAREN, + ')': TOKEN_RPAREN, + '{': TOKEN_LBRACE, + '}': TOKEN_RBRACE, + '==': TOKEN_EQ, + '!=': TOKEN_NE, + '>': TOKEN_GT, + '>=': TOKEN_GTEQ, + '<': TOKEN_LT, + '<=': TOKEN_LTEQ, + '=': TOKEN_ASSIGN, + '.': TOKEN_DOT, + ':': TOKEN_COLON, + '|': TOKEN_PIPE, + ',': TOKEN_COMMA, + ';': TOKEN_SEMICOLON +} + +reverse_operators = dict([(v, k) for k, v in iteritems(operators)]) +assert len(operators) == len(reverse_operators), 'operators dropped' +operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in + sorted(operators, key=lambda x: -len(x)))) + +ignored_tokens = frozenset([TOKEN_COMMENT_BEGIN, TOKEN_COMMENT, + TOKEN_COMMENT_END, TOKEN_WHITESPACE, + TOKEN_LINECOMMENT_BEGIN, TOKEN_LINECOMMENT_END, + TOKEN_LINECOMMENT]) +ignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA, + TOKEN_COMMENT, TOKEN_LINECOMMENT]) + + +def _describe_token_type(token_type): + if token_type in reverse_operators: + return reverse_operators[token_type] + return { + TOKEN_COMMENT_BEGIN: 'begin of comment', + TOKEN_COMMENT_END: 'end of comment', + TOKEN_COMMENT: 'comment', + TOKEN_LINECOMMENT: 'comment', + TOKEN_BLOCK_BEGIN: 'begin of statement block', + TOKEN_BLOCK_END: 'end of statement block', + TOKEN_VARIABLE_BEGIN: 'begin of print statement', + TOKEN_VARIABLE_END: 'end of print statement', + TOKEN_LINESTATEMENT_BEGIN: 'begin of line statement', + TOKEN_LINESTATEMENT_END: 'end of line statement', + TOKEN_DATA: 'template data / text', + TOKEN_EOF: 'end of template' + }.get(token_type, token_type) + + +def describe_token(token): + """Returns a description of the token.""" + if token.type == 'name': + return token.value + return _describe_token_type(token.type) + + +def describe_token_expr(expr): + """Like `describe_token` but for token expressions.""" + if ':' in expr: + type, value = expr.split(':', 1) + if type == 'name': + return value + else: + type = expr + return _describe_token_type(type) + + +def count_newlines(value): + """Count the number of newline characters in the string. This is + useful for extensions that filter a stream. + """ + return len(newline_re.findall(value)) + + +def compile_rules(environment): + """Compiles all the rules from the environment into a list of rules.""" + e = re.escape + rules = [ + (len(environment.comment_start_string), 'comment', + e(environment.comment_start_string)), + (len(environment.block_start_string), 'block', + e(environment.block_start_string)), + (len(environment.variable_start_string), 'variable', + e(environment.variable_start_string)) + ] + + if environment.line_statement_prefix is not None: + rules.append((len(environment.line_statement_prefix), 'linestatement', + r'^[ \t\v]*' + e(environment.line_statement_prefix))) + if environment.line_comment_prefix is not None: + rules.append((len(environment.line_comment_prefix), 'linecomment', + r'(?:^|(?<=\S))[^\S\r\n]*' + + e(environment.line_comment_prefix))) + + return [x[1:] for x in sorted(rules, reverse=True)] + + +class Failure(object): + """Class that raises a `TemplateSyntaxError` if called. + Used by the `Lexer` to specify known errors. + """ + + def __init__(self, message, cls=TemplateSyntaxError): + self.message = message + self.error_class = cls + + def __call__(self, lineno, filename): + raise self.error_class(self.message, lineno, filename) + + +class Token(tuple): + """Token class.""" + __slots__ = () + lineno, type, value = (property(itemgetter(x)) for x in range(3)) + + def __new__(cls, lineno, type, value): + return tuple.__new__(cls, (lineno, intern(str(type)), value)) + + def __str__(self): + if self.type in reverse_operators: + return reverse_operators[self.type] + elif self.type == 'name': + return self.value + return self.type + + def test(self, expr): + """Test a token against a token expression. This can either be a + token type or ``'token_type:token_value'``. This can only test + against string values and types. + """ + # here we do a regular string equality check as test_any is usually + # passed an iterable of not interned strings. + if self.type == expr: + return True + elif ':' in expr: + return expr.split(':', 1) == [self.type, self.value] + return False + + def test_any(self, *iterable): + """Test against multiple token expressions.""" + for expr in iterable: + if self.test(expr): + return True + return False + + def __repr__(self): + return 'Token(%r, %r, %r)' % ( + self.lineno, + self.type, + self.value + ) + + +@implements_iterator +class TokenStreamIterator(object): + """The iterator for tokenstreams. Iterate over the stream + until the eof token is reached. + """ + + def __init__(self, stream): + self.stream = stream + + def __iter__(self): + return self + + def __next__(self): + token = self.stream.current + if token.type is TOKEN_EOF: + self.stream.close() + raise StopIteration() + next(self.stream) + return token + + +@implements_iterator +class TokenStream(object): + """A token stream is an iterable that yields :class:`Token`\\s. The + parser however does not iterate over it but calls :meth:`next` to go + one token ahead. The current active token is stored as :attr:`current`. + """ + + def __init__(self, generator, name, filename): + self._iter = iter(generator) + self._pushed = deque() + self.name = name + self.filename = filename + self.closed = False + self.current = Token(1, TOKEN_INITIAL, '') + next(self) + + def __iter__(self): + return TokenStreamIterator(self) + + def __bool__(self): + return bool(self._pushed) or self.current.type is not TOKEN_EOF + __nonzero__ = __bool__ # py2 + + eos = property(lambda x: not x, doc="Are we at the end of the stream?") + + def push(self, token): + """Push a token back to the stream.""" + self._pushed.append(token) + + def look(self): + """Look at the next token.""" + old_token = next(self) + result = self.current + self.push(result) + self.current = old_token + return result + + def skip(self, n=1): + """Got n tokens ahead.""" + for x in range(n): + next(self) + + def next_if(self, expr): + """Perform the token test and return the token if it matched. + Otherwise the return value is `None`. + """ + if self.current.test(expr): + return next(self) + + def skip_if(self, expr): + """Like :meth:`next_if` but only returns `True` or `False`.""" + return self.next_if(expr) is not None + + def __next__(self): + """Go one token ahead and return the old one. + + Use the built-in :func:`next` instead of calling this directly. + """ + rv = self.current + if self._pushed: + self.current = self._pushed.popleft() + elif self.current.type is not TOKEN_EOF: + try: + self.current = next(self._iter) + except StopIteration: + self.close() + return rv + + def close(self): + """Close the stream.""" + self.current = Token(self.current.lineno, TOKEN_EOF, '') + self._iter = None + self.closed = True + + def expect(self, expr): + """Expect a given token type and return it. This accepts the same + argument as :meth:`jinja2.lexer.Token.test`. + """ + if not self.current.test(expr): + expr = describe_token_expr(expr) + if self.current.type is TOKEN_EOF: + raise TemplateSyntaxError('unexpected end of template, ' + 'expected %r.' % expr, + self.current.lineno, + self.name, self.filename) + raise TemplateSyntaxError("expected token %r, got %r" % + (expr, describe_token(self.current)), + self.current.lineno, + self.name, self.filename) + try: + return self.current + finally: + next(self) + + +def get_lexer(environment): + """Return a lexer which is probably cached.""" + key = (environment.block_start_string, + environment.block_end_string, + environment.variable_start_string, + environment.variable_end_string, + environment.comment_start_string, + environment.comment_end_string, + environment.line_statement_prefix, + environment.line_comment_prefix, + environment.trim_blocks, + environment.lstrip_blocks, + environment.newline_sequence, + environment.keep_trailing_newline) + lexer = _lexer_cache.get(key) + if lexer is None: + lexer = Lexer(environment) + _lexer_cache[key] = lexer + return lexer + + +class Lexer(object): + """Class that implements a lexer for a given environment. Automatically + created by the environment class, usually you don't have to do that. + + Note that the lexer is not automatically bound to an environment. + Multiple environments can share the same lexer. + """ + + def __init__(self, environment): + # shortcuts + c = lambda x: re.compile(x, re.M | re.S) + e = re.escape + + # lexing rules for tags + tag_rules = [ + (whitespace_re, TOKEN_WHITESPACE, None), + (float_re, TOKEN_FLOAT, None), + (integer_re, TOKEN_INTEGER, None), + (name_re, TOKEN_NAME, None), + (string_re, TOKEN_STRING, None), + (operator_re, TOKEN_OPERATOR, None) + ] + + # assemble the root lexing rule. because "|" is ungreedy + # we have to sort by length so that the lexer continues working + # as expected when we have parsing rules like <% for block and + # <%= for variables. (if someone wants asp like syntax) + # variables are just part of the rules if variable processing + # is required. + root_tag_rules = compile_rules(environment) + + # block suffix if trimming is enabled + block_suffix_re = environment.trim_blocks and '\\n?' or '' + + # strip leading spaces if lstrip_blocks is enabled + prefix_re = {} + if environment.lstrip_blocks: + # use '{%+' to manually disable lstrip_blocks behavior + no_lstrip_re = e('+') + # detect overlap between block and variable or comment strings + block_diff = c(r'^%s(.*)' % e(environment.block_start_string)) + # make sure we don't mistake a block for a variable or a comment + m = block_diff.match(environment.comment_start_string) + no_lstrip_re += m and r'|%s' % e(m.group(1)) or '' + m = block_diff.match(environment.variable_start_string) + no_lstrip_re += m and r'|%s' % e(m.group(1)) or '' + + # detect overlap between comment and variable strings + comment_diff = c(r'^%s(.*)' % e(environment.comment_start_string)) + m = comment_diff.match(environment.variable_start_string) + no_variable_re = m and r'(?!%s)' % e(m.group(1)) or '' + + lstrip_re = r'^[ \t]*' + block_prefix_re = r'%s%s(?!%s)|%s\+?' % ( + lstrip_re, + e(environment.block_start_string), + no_lstrip_re, + e(environment.block_start_string), + ) + comment_prefix_re = r'%s%s%s|%s\+?' % ( + lstrip_re, + e(environment.comment_start_string), + no_variable_re, + e(environment.comment_start_string), + ) + prefix_re['block'] = block_prefix_re + prefix_re['comment'] = comment_prefix_re + else: + block_prefix_re = '%s' % e(environment.block_start_string) + + self.newline_sequence = environment.newline_sequence + self.keep_trailing_newline = environment.keep_trailing_newline + + # global lexing rules + self.rules = { + 'root': [ + # directives + (c('(.*?)(?:%s)' % '|'.join( + [r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % ( + e(environment.block_start_string), + block_prefix_re, + e(environment.block_end_string), + e(environment.block_end_string) + )] + [ + r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, prefix_re.get(n,r)) + for n, r in root_tag_rules + ])), (TOKEN_DATA, '#bygroup'), '#bygroup'), + # data + (c('.+'), TOKEN_DATA, None) + ], + # comments + TOKEN_COMMENT_BEGIN: [ + (c(r'(.*?)((?:\-%s\s*|%s)%s)' % ( + e(environment.comment_end_string), + e(environment.comment_end_string), + block_suffix_re + )), (TOKEN_COMMENT, TOKEN_COMMENT_END), '#pop'), + (c('(.)'), (Failure('Missing end of comment tag'),), None) + ], + # blocks + TOKEN_BLOCK_BEGIN: [ + (c(r'(?:\-%s\s*|%s)%s' % ( + e(environment.block_end_string), + e(environment.block_end_string), + block_suffix_re + )), TOKEN_BLOCK_END, '#pop'), + ] + tag_rules, + # variables + TOKEN_VARIABLE_BEGIN: [ + (c(r'\-%s\s*|%s' % ( + e(environment.variable_end_string), + e(environment.variable_end_string) + )), TOKEN_VARIABLE_END, '#pop') + ] + tag_rules, + # raw block + TOKEN_RAW_BEGIN: [ + (c(r'(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % ( + e(environment.block_start_string), + block_prefix_re, + e(environment.block_end_string), + e(environment.block_end_string), + block_suffix_re + )), (TOKEN_DATA, TOKEN_RAW_END), '#pop'), + (c('(.)'), (Failure('Missing end of raw directive'),), None) + ], + # line statements + TOKEN_LINESTATEMENT_BEGIN: [ + (c(r'\s*(\n|$)'), TOKEN_LINESTATEMENT_END, '#pop') + ] + tag_rules, + # line comments + TOKEN_LINECOMMENT_BEGIN: [ + (c(r'(.*?)()(?=\n|$)'), (TOKEN_LINECOMMENT, + TOKEN_LINECOMMENT_END), '#pop') + ] + } + + def _normalize_newlines(self, value): + """Called for strings and template data to normalize it to unicode.""" + return newline_re.sub(self.newline_sequence, value) + + def tokenize(self, source, name=None, filename=None, state=None): + """Calls tokeniter + tokenize and wraps it in a token stream. + """ + stream = self.tokeniter(source, name, filename, state) + return TokenStream(self.wrap(stream, name, filename), name, filename) + + def wrap(self, stream, name=None, filename=None): + """This is called with the stream as returned by `tokenize` and wraps + every token in a :class:`Token` and converts the value. + """ + for lineno, token, value in stream: + if token in ignored_tokens: + continue + elif token == 'linestatement_begin': + token = 'block_begin' + elif token == 'linestatement_end': + token = 'block_end' + # we are not interested in those tokens in the parser + elif token in ('raw_begin', 'raw_end'): + continue + elif token == 'data': + value = self._normalize_newlines(value) + elif token == 'keyword': + token = value + elif token == 'name': + value = str(value) + if check_ident and not value.isidentifier(): + raise TemplateSyntaxError( + 'Invalid character in identifier', + lineno, name, filename) + elif token == 'string': + # try to unescape string + try: + value = self._normalize_newlines(value[1:-1]) \ + .encode('ascii', 'backslashreplace') \ + .decode('unicode-escape') + except Exception as e: + msg = str(e).split(':')[-1].strip() + raise TemplateSyntaxError(msg, lineno, name, filename) + elif token == 'integer': + value = int(value) + elif token == 'float': + value = float(value) + elif token == 'operator': + token = operators[value] + yield Token(lineno, token, value) + + def tokeniter(self, source, name, filename=None, state=None): + """This method tokenizes the text and returns the tokens in a + generator. Use this method if you just want to tokenize a template. + """ + source = text_type(source) + lines = source.splitlines() + if self.keep_trailing_newline and source: + for newline in ('\r\n', '\r', '\n'): + if source.endswith(newline): + lines.append('') + break + source = '\n'.join(lines) + pos = 0 + lineno = 1 + stack = ['root'] + if state is not None and state != 'root': + assert state in ('variable', 'block'), 'invalid state' + stack.append(state + '_begin') + else: + state = 'root' + statetokens = self.rules[stack[-1]] + source_length = len(source) + + balancing_stack = [] + + while 1: + # tokenizer loop + for regex, tokens, new_state in statetokens: + m = regex.match(source, pos) + # if no match we try again with the next rule + if m is None: + continue + + # we only match blocks and variables if braces / parentheses + # are balanced. continue parsing with the lower rule which + # is the operator rule. do this only if the end tags look + # like operators + if balancing_stack and \ + tokens in ('variable_end', 'block_end', + 'linestatement_end'): + continue + + # tuples support more options + if isinstance(tokens, tuple): + for idx, token in enumerate(tokens): + # failure group + if token.__class__ is Failure: + raise token(lineno, filename) + # bygroup is a bit more complex, in that case we + # yield for the current token the first named + # group that matched + elif token == '#bygroup': + for key, value in iteritems(m.groupdict()): + if value is not None: + yield lineno, key, value + lineno += value.count('\n') + break + else: + raise RuntimeError('%r wanted to resolve ' + 'the token dynamically' + ' but no group matched' + % regex) + # normal group + else: + data = m.group(idx + 1) + if data or token not in ignore_if_empty: + yield lineno, token, data + lineno += data.count('\n') + + # strings as token just are yielded as it. + else: + data = m.group() + # update brace/parentheses balance + if tokens == 'operator': + if data == '{': + balancing_stack.append('}') + elif data == '(': + balancing_stack.append(')') + elif data == '[': + balancing_stack.append(']') + elif data in ('}', ')', ']'): + if not balancing_stack: + raise TemplateSyntaxError('unexpected \'%s\'' % + data, lineno, name, + filename) + expected_op = balancing_stack.pop() + if expected_op != data: + raise TemplateSyntaxError('unexpected \'%s\', ' + 'expected \'%s\'' % + (data, expected_op), + lineno, name, + filename) + # yield items + if data or tokens not in ignore_if_empty: + yield lineno, tokens, data + lineno += data.count('\n') + + # fetch new position into new variable so that we can check + # if there is a internal parsing error which would result + # in an infinite loop + pos2 = m.end() + + # handle state changes + if new_state is not None: + # remove the uppermost state + if new_state == '#pop': + stack.pop() + # resolve the new state by group checking + elif new_state == '#bygroup': + for key, value in iteritems(m.groupdict()): + if value is not None: + stack.append(key) + break + else: + raise RuntimeError('%r wanted to resolve the ' + 'new state dynamically but' + ' no group matched' % + regex) + # direct state name given + else: + stack.append(new_state) + statetokens = self.rules[stack[-1]] + # we are still at the same position and no stack change. + # this means a loop without break condition, avoid that and + # raise error + elif pos2 == pos: + raise RuntimeError('%r yielded empty string without ' + 'stack change' % regex) + # publish new function and start again + pos = pos2 + break + # if loop terminated without break we haven't found a single match + # either we are at the end of the file or we have a problem + else: + # end of text + if pos >= source_length: + return + # something went wrong + raise TemplateSyntaxError('unexpected char %r at %d' % + (source[pos], pos), lineno, + name, filename) diff --git a/venv/Lib/site-packages/jinja2/loaders.py b/venv/Lib/site-packages/jinja2/loaders.py new file mode 100644 index 0000000..4c79793 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/loaders.py @@ -0,0 +1,481 @@ +# -*- coding: utf-8 -*- +""" + jinja2.loaders + ~~~~~~~~~~~~~~ + + Jinja loader classes. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import os +import sys +import weakref +from types import ModuleType +from os import path +from hashlib import sha1 +from jinja2.exceptions import TemplateNotFound +from jinja2.utils import open_if_exists, internalcode +from jinja2._compat import string_types, iteritems + + +def split_template_path(template): + """Split a path into segments and perform a sanity check. If it detects + '..' in the path it will raise a `TemplateNotFound` error. + """ + pieces = [] + for piece in template.split('/'): + if path.sep in piece \ + or (path.altsep and path.altsep in piece) or \ + piece == path.pardir: + raise TemplateNotFound(template) + elif piece and piece != '.': + pieces.append(piece) + return pieces + + +class BaseLoader(object): + """Baseclass for all loaders. Subclass this and override `get_source` to + implement a custom loading mechanism. The environment provides a + `get_template` method that calls the loader's `load` method to get the + :class:`Template` object. + + A very basic example for a loader that looks up templates on the file + system could look like this:: + + from jinja2 import BaseLoader, TemplateNotFound + from os.path import join, exists, getmtime + + class MyLoader(BaseLoader): + + def __init__(self, path): + self.path = path + + def get_source(self, environment, template): + path = join(self.path, template) + if not exists(path): + raise TemplateNotFound(template) + mtime = getmtime(path) + with file(path) as f: + source = f.read().decode('utf-8') + return source, path, lambda: mtime == getmtime(path) + """ + + #: if set to `False` it indicates that the loader cannot provide access + #: to the source of templates. + #: + #: .. versionadded:: 2.4 + has_source_access = True + + def get_source(self, environment, template): + """Get the template source, filename and reload helper for a template. + It's passed the environment and template name and has to return a + tuple in the form ``(source, filename, uptodate)`` or raise a + `TemplateNotFound` error if it can't locate the template. + + The source part of the returned tuple must be the source of the + template as unicode string or a ASCII bytestring. The filename should + be the name of the file on the filesystem if it was loaded from there, + otherwise `None`. The filename is used by python for the tracebacks + if no loader extension is used. + + The last item in the tuple is the `uptodate` function. If auto + reloading is enabled it's always called to check if the template + changed. No arguments are passed so the function must store the + old state somewhere (for example in a closure). If it returns `False` + the template will be reloaded. + """ + if not self.has_source_access: + raise RuntimeError('%s cannot provide access to the source' % + self.__class__.__name__) + raise TemplateNotFound(template) + + def list_templates(self): + """Iterates over all templates. If the loader does not support that + it should raise a :exc:`TypeError` which is the default behavior. + """ + raise TypeError('this loader cannot iterate over all templates') + + @internalcode + def load(self, environment, name, globals=None): + """Loads a template. This method looks up the template in the cache + or loads one by calling :meth:`get_source`. Subclasses should not + override this method as loaders working on collections of other + loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`) + will not call this method but `get_source` directly. + """ + code = None + if globals is None: + globals = {} + + # first we try to get the source for this template together + # with the filename and the uptodate function. + source, filename, uptodate = self.get_source(environment, name) + + # try to load the code from the bytecode cache if there is a + # bytecode cache configured. + bcc = environment.bytecode_cache + if bcc is not None: + bucket = bcc.get_bucket(environment, name, filename, source) + code = bucket.code + + # if we don't have code so far (not cached, no longer up to + # date) etc. we compile the template + if code is None: + code = environment.compile(source, name, filename) + + # if the bytecode cache is available and the bucket doesn't + # have a code so far, we give the bucket the new code and put + # it back to the bytecode cache. + if bcc is not None and bucket.code is None: + bucket.code = code + bcc.set_bucket(bucket) + + return environment.template_class.from_code(environment, code, + globals, uptodate) + + +class FileSystemLoader(BaseLoader): + """Loads templates from the file system. This loader can find templates + in folders on the file system and is the preferred way to load them. + + The loader takes the path to the templates as string, or if multiple + locations are wanted a list of them which is then looked up in the + given order:: + + >>> loader = FileSystemLoader('/path/to/templates') + >>> loader = FileSystemLoader(['/path/to/templates', '/other/path']) + + Per default the template encoding is ``'utf-8'`` which can be changed + by setting the `encoding` parameter to something else. + + To follow symbolic links, set the *followlinks* parameter to ``True``:: + + >>> loader = FileSystemLoader('/path/to/templates', followlinks=True) + + .. versionchanged:: 2.8+ + The *followlinks* parameter was added. + """ + + def __init__(self, searchpath, encoding='utf-8', followlinks=False): + if isinstance(searchpath, string_types): + searchpath = [searchpath] + self.searchpath = list(searchpath) + self.encoding = encoding + self.followlinks = followlinks + + def get_source(self, environment, template): + pieces = split_template_path(template) + for searchpath in self.searchpath: + filename = path.join(searchpath, *pieces) + f = open_if_exists(filename) + if f is None: + continue + try: + contents = f.read().decode(self.encoding) + finally: + f.close() + + mtime = path.getmtime(filename) + + def uptodate(): + try: + return path.getmtime(filename) == mtime + except OSError: + return False + return contents, filename, uptodate + raise TemplateNotFound(template) + + def list_templates(self): + found = set() + for searchpath in self.searchpath: + walk_dir = os.walk(searchpath, followlinks=self.followlinks) + for dirpath, dirnames, filenames in walk_dir: + for filename in filenames: + template = os.path.join(dirpath, filename) \ + [len(searchpath):].strip(os.path.sep) \ + .replace(os.path.sep, '/') + if template[:2] == './': + template = template[2:] + if template not in found: + found.add(template) + return sorted(found) + + +class PackageLoader(BaseLoader): + """Load templates from python eggs or packages. It is constructed with + the name of the python package and the path to the templates in that + package:: + + loader = PackageLoader('mypackage', 'views') + + If the package path is not given, ``'templates'`` is assumed. + + Per default the template encoding is ``'utf-8'`` which can be changed + by setting the `encoding` parameter to something else. Due to the nature + of eggs it's only possible to reload templates if the package was loaded + from the file system and not a zip file. + """ + + def __init__(self, package_name, package_path='templates', + encoding='utf-8'): + from pkg_resources import DefaultProvider, ResourceManager, \ + get_provider + provider = get_provider(package_name) + self.encoding = encoding + self.manager = ResourceManager() + self.filesystem_bound = isinstance(provider, DefaultProvider) + self.provider = provider + self.package_path = package_path + + def get_source(self, environment, template): + pieces = split_template_path(template) + p = '/'.join((self.package_path,) + tuple(pieces)) + if not self.provider.has_resource(p): + raise TemplateNotFound(template) + + filename = uptodate = None + if self.filesystem_bound: + filename = self.provider.get_resource_filename(self.manager, p) + mtime = path.getmtime(filename) + def uptodate(): + try: + return path.getmtime(filename) == mtime + except OSError: + return False + + source = self.provider.get_resource_string(self.manager, p) + return source.decode(self.encoding), filename, uptodate + + def list_templates(self): + path = self.package_path + if path[:2] == './': + path = path[2:] + elif path == '.': + path = '' + offset = len(path) + results = [] + def _walk(path): + for filename in self.provider.resource_listdir(path): + fullname = path + '/' + filename + if self.provider.resource_isdir(fullname): + _walk(fullname) + else: + results.append(fullname[offset:].lstrip('/')) + _walk(path) + results.sort() + return results + + +class DictLoader(BaseLoader): + """Loads a template from a python dict. It's passed a dict of unicode + strings bound to template names. This loader is useful for unittesting: + + >>> loader = DictLoader({'index.html': 'source here'}) + + Because auto reloading is rarely useful this is disabled per default. + """ + + def __init__(self, mapping): + self.mapping = mapping + + def get_source(self, environment, template): + if template in self.mapping: + source = self.mapping[template] + return source, None, lambda: source == self.mapping.get(template) + raise TemplateNotFound(template) + + def list_templates(self): + return sorted(self.mapping) + + +class FunctionLoader(BaseLoader): + """A loader that is passed a function which does the loading. The + function receives the name of the template and has to return either + an unicode string with the template source, a tuple in the form ``(source, + filename, uptodatefunc)`` or `None` if the template does not exist. + + >>> def load_template(name): + ... if name == 'index.html': + ... return '...' + ... + >>> loader = FunctionLoader(load_template) + + The `uptodatefunc` is a function that is called if autoreload is enabled + and has to return `True` if the template is still up to date. For more + details have a look at :meth:`BaseLoader.get_source` which has the same + return value. + """ + + def __init__(self, load_func): + self.load_func = load_func + + def get_source(self, environment, template): + rv = self.load_func(template) + if rv is None: + raise TemplateNotFound(template) + elif isinstance(rv, string_types): + return rv, None, None + return rv + + +class PrefixLoader(BaseLoader): + """A loader that is passed a dict of loaders where each loader is bound + to a prefix. The prefix is delimited from the template by a slash per + default, which can be changed by setting the `delimiter` argument to + something else:: + + loader = PrefixLoader({ + 'app1': PackageLoader('mypackage.app1'), + 'app2': PackageLoader('mypackage.app2') + }) + + By loading ``'app1/index.html'`` the file from the app1 package is loaded, + by loading ``'app2/index.html'`` the file from the second. + """ + + def __init__(self, mapping, delimiter='/'): + self.mapping = mapping + self.delimiter = delimiter + + def get_loader(self, template): + try: + prefix, name = template.split(self.delimiter, 1) + loader = self.mapping[prefix] + except (ValueError, KeyError): + raise TemplateNotFound(template) + return loader, name + + def get_source(self, environment, template): + loader, name = self.get_loader(template) + try: + return loader.get_source(environment, name) + except TemplateNotFound: + # re-raise the exception with the correct filename here. + # (the one that includes the prefix) + raise TemplateNotFound(template) + + @internalcode + def load(self, environment, name, globals=None): + loader, local_name = self.get_loader(name) + try: + return loader.load(environment, local_name, globals) + except TemplateNotFound: + # re-raise the exception with the correct filename here. + # (the one that includes the prefix) + raise TemplateNotFound(name) + + def list_templates(self): + result = [] + for prefix, loader in iteritems(self.mapping): + for template in loader.list_templates(): + result.append(prefix + self.delimiter + template) + return result + + +class ChoiceLoader(BaseLoader): + """This loader works like the `PrefixLoader` just that no prefix is + specified. If a template could not be found by one loader the next one + is tried. + + >>> loader = ChoiceLoader([ + ... FileSystemLoader('/path/to/user/templates'), + ... FileSystemLoader('/path/to/system/templates') + ... ]) + + This is useful if you want to allow users to override builtin templates + from a different location. + """ + + def __init__(self, loaders): + self.loaders = loaders + + def get_source(self, environment, template): + for loader in self.loaders: + try: + return loader.get_source(environment, template) + except TemplateNotFound: + pass + raise TemplateNotFound(template) + + @internalcode + def load(self, environment, name, globals=None): + for loader in self.loaders: + try: + return loader.load(environment, name, globals) + except TemplateNotFound: + pass + raise TemplateNotFound(name) + + def list_templates(self): + found = set() + for loader in self.loaders: + found.update(loader.list_templates()) + return sorted(found) + + +class _TemplateModule(ModuleType): + """Like a normal module but with support for weak references""" + + +class ModuleLoader(BaseLoader): + """This loader loads templates from precompiled templates. + + Example usage: + + >>> loader = ChoiceLoader([ + ... ModuleLoader('/path/to/compiled/templates'), + ... FileSystemLoader('/path/to/templates') + ... ]) + + Templates can be precompiled with :meth:`Environment.compile_templates`. + """ + + has_source_access = False + + def __init__(self, path): + package_name = '_jinja2_module_templates_%x' % id(self) + + # create a fake module that looks for the templates in the + # path given. + mod = _TemplateModule(package_name) + if isinstance(path, string_types): + path = [path] + else: + path = list(path) + mod.__path__ = path + + sys.modules[package_name] = weakref.proxy(mod, + lambda x: sys.modules.pop(package_name, None)) + + # the only strong reference, the sys.modules entry is weak + # so that the garbage collector can remove it once the + # loader that created it goes out of business. + self.module = mod + self.package_name = package_name + + @staticmethod + def get_template_key(name): + return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest() + + @staticmethod + def get_module_filename(name): + return ModuleLoader.get_template_key(name) + '.py' + + @internalcode + def load(self, environment, name, globals=None): + key = self.get_template_key(name) + module = '%s.%s' % (self.package_name, key) + mod = getattr(self.module, module, None) + if mod is None: + try: + mod = __import__(module, None, None, ['root']) + except ImportError: + raise TemplateNotFound(name) + + # remove the entry from sys.modules, we only want the attribute + # on the module object we have stored on the loader. + sys.modules.pop(module, None) + + return environment.template_class.from_module_dict( + environment, mod.__dict__, globals) diff --git a/venv/Lib/site-packages/jinja2/meta.py b/venv/Lib/site-packages/jinja2/meta.py new file mode 100644 index 0000000..7421914 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/meta.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +""" + jinja2.meta + ~~~~~~~~~~~ + + This module implements various functions that exposes information about + templates that might be interesting for various kinds of applications. + + :copyright: (c) 2017 by the Jinja Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +from jinja2 import nodes +from jinja2.compiler import CodeGenerator +from jinja2._compat import string_types, iteritems + + +class TrackingCodeGenerator(CodeGenerator): + """We abuse the code generator for introspection.""" + + def __init__(self, environment): + CodeGenerator.__init__(self, environment, '<introspection>', + '<introspection>') + self.undeclared_identifiers = set() + + def write(self, x): + """Don't write.""" + + def enter_frame(self, frame): + """Remember all undeclared identifiers.""" + CodeGenerator.enter_frame(self, frame) + for _, (action, param) in iteritems(frame.symbols.loads): + if action == 'resolve': + self.undeclared_identifiers.add(param) + + +def find_undeclared_variables(ast): + """Returns a set of all variables in the AST that will be looked up from + the context at runtime. Because at compile time it's not known which + variables will be used depending on the path the execution takes at + runtime, all variables are returned. + + >>> from jinja2 import Environment, meta + >>> env = Environment() + >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') + >>> meta.find_undeclared_variables(ast) == set(['bar']) + True + + .. admonition:: Implementation + + Internally the code generator is used for finding undeclared variables. + This is good to know because the code generator might raise a + :exc:`TemplateAssertionError` during compilation and as a matter of + fact this function can currently raise that exception as well. + """ + codegen = TrackingCodeGenerator(ast.environment) + codegen.visit(ast) + return codegen.undeclared_identifiers + + +def find_referenced_templates(ast): + """Finds all the referenced templates from the AST. This will return an + iterator over all the hardcoded template extensions, inclusions and + imports. If dynamic inheritance or inclusion is used, `None` will be + yielded. + + >>> from jinja2 import Environment, meta + >>> env = Environment() + >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}') + >>> list(meta.find_referenced_templates(ast)) + ['layout.html', None] + + This function is useful for dependency tracking. For example if you want + to rebuild parts of the website after a layout template has changed. + """ + for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import, + nodes.Include)): + if not isinstance(node.template, nodes.Const): + # a tuple with some non consts in there + if isinstance(node.template, (nodes.Tuple, nodes.List)): + for template_name in node.template.items: + # something const, only yield the strings and ignore + # non-string consts that really just make no sense + if isinstance(template_name, nodes.Const): + if isinstance(template_name.value, string_types): + yield template_name.value + # something dynamic in there + else: + yield None + # something dynamic we don't know about here + else: + yield None + continue + # constant is a basestring, direct template name + if isinstance(node.template.value, string_types): + yield node.template.value + # a tuple or list (latter *should* not happen) made of consts, + # yield the consts that are strings. We could warn here for + # non string values + elif isinstance(node, nodes.Include) and \ + isinstance(node.template.value, (tuple, list)): + for template_name in node.template.value: + if isinstance(template_name, string_types): + yield template_name + # something else we don't care about, we could warn here + else: + yield None diff --git a/venv/Lib/site-packages/jinja2/nativetypes.py b/venv/Lib/site-packages/jinja2/nativetypes.py new file mode 100644 index 0000000..fe17e41 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/nativetypes.py @@ -0,0 +1,220 @@ +import sys +from ast import literal_eval +from itertools import islice, chain +from jinja2 import nodes +from jinja2._compat import text_type +from jinja2.compiler import CodeGenerator, has_safe_repr +from jinja2.environment import Environment, Template +from jinja2.utils import concat, escape + + +def native_concat(nodes): + """Return a native Python type from the list of compiled nodes. If the + result is a single node, its value is returned. Otherwise, the nodes are + concatenated as strings. If the result can be parsed with + :func:`ast.literal_eval`, the parsed value is returned. Otherwise, the + string is returned. + """ + head = list(islice(nodes, 2)) + + if not head: + return None + + if len(head) == 1: + out = head[0] + else: + out = u''.join([text_type(v) for v in chain(head, nodes)]) + + try: + return literal_eval(out) + except (ValueError, SyntaxError, MemoryError): + return out + + +class NativeCodeGenerator(CodeGenerator): + """A code generator which avoids injecting ``to_string()`` calls around the + internal code Jinja uses to render templates. + """ + + def visit_Output(self, node, frame): + """Same as :meth:`CodeGenerator.visit_Output`, but do not call + ``to_string`` on output nodes in generated code. + """ + if self.has_known_extends and frame.require_output_check: + return + + finalize = self.environment.finalize + finalize_context = getattr(finalize, 'contextfunction', False) + finalize_eval = getattr(finalize, 'evalcontextfunction', False) + finalize_env = getattr(finalize, 'environmentfunction', False) + + if finalize is not None: + if finalize_context or finalize_eval: + const_finalize = None + elif finalize_env: + def const_finalize(x): + return finalize(self.environment, x) + else: + const_finalize = finalize + else: + def const_finalize(x): + return x + + # If we are inside a frame that requires output checking, we do so. + outdent_later = False + + if frame.require_output_check: + self.writeline('if parent_template is None:') + self.indent() + outdent_later = True + + # Try to evaluate as many chunks as possible into a static string at + # compile time. + body = [] + + for child in node.nodes: + try: + if const_finalize is None: + raise nodes.Impossible() + + const = child.as_const(frame.eval_ctx) + if not has_safe_repr(const): + raise nodes.Impossible() + except nodes.Impossible: + body.append(child) + continue + + # the frame can't be volatile here, because otherwise the as_const + # function would raise an Impossible exception at that point + try: + if frame.eval_ctx.autoescape: + if hasattr(const, '__html__'): + const = const.__html__() + else: + const = escape(const) + + const = const_finalize(const) + except Exception: + # if something goes wrong here we evaluate the node at runtime + # for easier debugging + body.append(child) + continue + + if body and isinstance(body[-1], list): + body[-1].append(const) + else: + body.append([const]) + + # if we have less than 3 nodes or a buffer we yield or extend/append + if len(body) < 3 or frame.buffer is not None: + if frame.buffer is not None: + # for one item we append, for more we extend + if len(body) == 1: + self.writeline('%s.append(' % frame.buffer) + else: + self.writeline('%s.extend((' % frame.buffer) + + self.indent() + + for item in body: + if isinstance(item, list): + val = repr(native_concat(item)) + + if frame.buffer is None: + self.writeline('yield ' + val) + else: + self.writeline(val + ',') + else: + if frame.buffer is None: + self.writeline('yield ', item) + else: + self.newline(item) + + close = 0 + + if finalize is not None: + self.write('environment.finalize(') + + if finalize_context: + self.write('context, ') + + close += 1 + + self.visit(item, frame) + + if close > 0: + self.write(')' * close) + + if frame.buffer is not None: + self.write(',') + + if frame.buffer is not None: + # close the open parentheses + self.outdent() + self.writeline(len(body) == 1 and ')' or '))') + + # otherwise we create a format string as this is faster in that case + else: + format = [] + arguments = [] + + for item in body: + if isinstance(item, list): + format.append(native_concat(item).replace('%', '%%')) + else: + format.append('%s') + arguments.append(item) + + self.writeline('yield ') + self.write(repr(concat(format)) + ' % (') + self.indent() + + for argument in arguments: + self.newline(argument) + close = 0 + + if finalize is not None: + self.write('environment.finalize(') + + if finalize_context: + self.write('context, ') + elif finalize_eval: + self.write('context.eval_ctx, ') + elif finalize_env: + self.write('environment, ') + + close += 1 + + self.visit(argument, frame) + self.write(')' * close + ', ') + + self.outdent() + self.writeline(')') + + if outdent_later: + self.outdent() + + +class NativeTemplate(Template): + def render(self, *args, **kwargs): + """Render the template to produce a native Python type. If the result + is a single node, its value is returned. Otherwise, the nodes are + concatenated as strings. If the result can be parsed with + :func:`ast.literal_eval`, the parsed value is returned. Otherwise, the + string is returned. + """ + vars = dict(*args, **kwargs) + + try: + return native_concat(self.root_render_func(self.new_context(vars))) + except Exception: + exc_info = sys.exc_info() + + return self.environment.handle_exception(exc_info, True) + + +class NativeEnvironment(Environment): + """An environment that renders templates to native Python types.""" + + code_generator_class = NativeCodeGenerator + template_class = NativeTemplate diff --git a/venv/Lib/site-packages/jinja2/nodes.py b/venv/Lib/site-packages/jinja2/nodes.py new file mode 100644 index 0000000..4d9a01a --- /dev/null +++ b/venv/Lib/site-packages/jinja2/nodes.py @@ -0,0 +1,999 @@ +# -*- coding: utf-8 -*- +""" + jinja2.nodes + ~~~~~~~~~~~~ + + This module implements additional nodes derived from the ast base node. + + It also provides some node tree helper functions like `in_lineno` and + `get_nodes` used by the parser and translator in order to normalize + python and jinja nodes. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import types +import operator + +from collections import deque +from jinja2.utils import Markup +from jinja2._compat import izip, with_metaclass, text_type, PY2 + + +#: the types we support for context functions +_context_function_types = (types.FunctionType, types.MethodType) + + +_binop_to_func = { + '*': operator.mul, + '/': operator.truediv, + '//': operator.floordiv, + '**': operator.pow, + '%': operator.mod, + '+': operator.add, + '-': operator.sub +} + +_uaop_to_func = { + 'not': operator.not_, + '+': operator.pos, + '-': operator.neg +} + +_cmpop_to_func = { + 'eq': operator.eq, + 'ne': operator.ne, + 'gt': operator.gt, + 'gteq': operator.ge, + 'lt': operator.lt, + 'lteq': operator.le, + 'in': lambda a, b: a in b, + 'notin': lambda a, b: a not in b +} + + +class Impossible(Exception): + """Raised if the node could not perform a requested action.""" + + +class NodeType(type): + """A metaclass for nodes that handles the field and attribute + inheritance. fields and attributes from the parent class are + automatically forwarded to the child.""" + + def __new__(cls, name, bases, d): + for attr in 'fields', 'attributes': + storage = [] + storage.extend(getattr(bases[0], attr, ())) + storage.extend(d.get(attr, ())) + assert len(bases) == 1, 'multiple inheritance not allowed' + assert len(storage) == len(set(storage)), 'layout conflict' + d[attr] = tuple(storage) + d.setdefault('abstract', False) + return type.__new__(cls, name, bases, d) + + +class EvalContext(object): + """Holds evaluation time information. Custom attributes can be attached + to it in extensions. + """ + + def __init__(self, environment, template_name=None): + self.environment = environment + if callable(environment.autoescape): + self.autoescape = environment.autoescape(template_name) + else: + self.autoescape = environment.autoescape + self.volatile = False + + def save(self): + return self.__dict__.copy() + + def revert(self, old): + self.__dict__.clear() + self.__dict__.update(old) + + +def get_eval_context(node, ctx): + if ctx is None: + if node.environment is None: + raise RuntimeError('if no eval context is passed, the ' + 'node must have an attached ' + 'environment.') + return EvalContext(node.environment) + return ctx + + +class Node(with_metaclass(NodeType, object)): + """Baseclass for all Jinja2 nodes. There are a number of nodes available + of different types. There are four major types: + + - :class:`Stmt`: statements + - :class:`Expr`: expressions + - :class:`Helper`: helper nodes + - :class:`Template`: the outermost wrapper node + + All nodes have fields and attributes. Fields may be other nodes, lists, + or arbitrary values. Fields are passed to the constructor as regular + positional arguments, attributes as keyword arguments. Each node has + two attributes: `lineno` (the line number of the node) and `environment`. + The `environment` attribute is set at the end of the parsing process for + all nodes automatically. + """ + fields = () + attributes = ('lineno', 'environment') + abstract = True + + def __init__(self, *fields, **attributes): + if self.abstract: + raise TypeError('abstract nodes are not instanciable') + if fields: + if len(fields) != len(self.fields): + if not self.fields: + raise TypeError('%r takes 0 arguments' % + self.__class__.__name__) + raise TypeError('%r takes 0 or %d argument%s' % ( + self.__class__.__name__, + len(self.fields), + len(self.fields) != 1 and 's' or '' + )) + for name, arg in izip(self.fields, fields): + setattr(self, name, arg) + for attr in self.attributes: + setattr(self, attr, attributes.pop(attr, None)) + if attributes: + raise TypeError('unknown attribute %r' % + next(iter(attributes))) + + def iter_fields(self, exclude=None, only=None): + """This method iterates over all fields that are defined and yields + ``(key, value)`` tuples. Per default all fields are returned, but + it's possible to limit that to some fields by providing the `only` + parameter or to exclude some using the `exclude` parameter. Both + should be sets or tuples of field names. + """ + for name in self.fields: + if (exclude is only is None) or \ + (exclude is not None and name not in exclude) or \ + (only is not None and name in only): + try: + yield name, getattr(self, name) + except AttributeError: + pass + + def iter_child_nodes(self, exclude=None, only=None): + """Iterates over all direct child nodes of the node. This iterates + over all fields and yields the values of they are nodes. If the value + of a field is a list all the nodes in that list are returned. + """ + for field, item in self.iter_fields(exclude, only): + if isinstance(item, list): + for n in item: + if isinstance(n, Node): + yield n + elif isinstance(item, Node): + yield item + + def find(self, node_type): + """Find the first node of a given type. If no such node exists the + return value is `None`. + """ + for result in self.find_all(node_type): + return result + + def find_all(self, node_type): + """Find all the nodes of a given type. If the type is a tuple, + the check is performed for any of the tuple items. + """ + for child in self.iter_child_nodes(): + if isinstance(child, node_type): + yield child + for result in child.find_all(node_type): + yield result + + def set_ctx(self, ctx): + """Reset the context of a node and all child nodes. Per default the + parser will all generate nodes that have a 'load' context as it's the + most common one. This method is used in the parser to set assignment + targets and other nodes to a store context. + """ + todo = deque([self]) + while todo: + node = todo.popleft() + if 'ctx' in node.fields: + node.ctx = ctx + todo.extend(node.iter_child_nodes()) + return self + + def set_lineno(self, lineno, override=False): + """Set the line numbers of the node and children.""" + todo = deque([self]) + while todo: + node = todo.popleft() + if 'lineno' in node.attributes: + if node.lineno is None or override: + node.lineno = lineno + todo.extend(node.iter_child_nodes()) + return self + + def set_environment(self, environment): + """Set the environment for all nodes.""" + todo = deque([self]) + while todo: + node = todo.popleft() + node.environment = environment + todo.extend(node.iter_child_nodes()) + return self + + def __eq__(self, other): + return type(self) is type(other) and \ + tuple(self.iter_fields()) == tuple(other.iter_fields()) + + def __ne__(self, other): + return not self.__eq__(other) + + # Restore Python 2 hashing behavior on Python 3 + __hash__ = object.__hash__ + + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + ', '.join('%s=%r' % (arg, getattr(self, arg, None)) for + arg in self.fields) + ) + + def dump(self): + def _dump(node): + if not isinstance(node, Node): + buf.append(repr(node)) + return + + buf.append('nodes.%s(' % node.__class__.__name__) + if not node.fields: + buf.append(')') + return + for idx, field in enumerate(node.fields): + if idx: + buf.append(', ') + value = getattr(node, field) + if isinstance(value, list): + buf.append('[') + for idx, item in enumerate(value): + if idx: + buf.append(', ') + _dump(item) + buf.append(']') + else: + _dump(value) + buf.append(')') + buf = [] + _dump(self) + return ''.join(buf) + + + +class Stmt(Node): + """Base node for all statements.""" + abstract = True + + +class Helper(Node): + """Nodes that exist in a specific context only.""" + abstract = True + + +class Template(Node): + """Node that represents a template. This must be the outermost node that + is passed to the compiler. + """ + fields = ('body',) + + +class Output(Stmt): + """A node that holds multiple expressions which are then printed out. + This is used both for the `print` statement and the regular template data. + """ + fields = ('nodes',) + + +class Extends(Stmt): + """Represents an extends statement.""" + fields = ('template',) + + +class For(Stmt): + """The for loop. `target` is the target for the iteration (usually a + :class:`Name` or :class:`Tuple`), `iter` the iterable. `body` is a list + of nodes that are used as loop-body, and `else_` a list of nodes for the + `else` block. If no else node exists it has to be an empty list. + + For filtered nodes an expression can be stored as `test`, otherwise `None`. + """ + fields = ('target', 'iter', 'body', 'else_', 'test', 'recursive') + + +class If(Stmt): + """If `test` is true, `body` is rendered, else `else_`.""" + fields = ('test', 'body', 'elif_', 'else_') + + +class Macro(Stmt): + """A macro definition. `name` is the name of the macro, `args` a list of + arguments and `defaults` a list of defaults if there are any. `body` is + a list of nodes for the macro body. + """ + fields = ('name', 'args', 'defaults', 'body') + + +class CallBlock(Stmt): + """Like a macro without a name but a call instead. `call` is called with + the unnamed macro as `caller` argument this node holds. + """ + fields = ('call', 'args', 'defaults', 'body') + + +class FilterBlock(Stmt): + """Node for filter sections.""" + fields = ('body', 'filter') + + +class With(Stmt): + """Specific node for with statements. In older versions of Jinja the + with statement was implemented on the base of the `Scope` node instead. + + .. versionadded:: 2.9.3 + """ + fields = ('targets', 'values', 'body') + + +class Block(Stmt): + """A node that represents a block.""" + fields = ('name', 'body', 'scoped') + + +class Include(Stmt): + """A node that represents the include tag.""" + fields = ('template', 'with_context', 'ignore_missing') + + +class Import(Stmt): + """A node that represents the import tag.""" + fields = ('template', 'target', 'with_context') + + +class FromImport(Stmt): + """A node that represents the from import tag. It's important to not + pass unsafe names to the name attribute. The compiler translates the + attribute lookups directly into getattr calls and does *not* use the + subscript callback of the interface. As exported variables may not + start with double underscores (which the parser asserts) this is not a + problem for regular Jinja code, but if this node is used in an extension + extra care must be taken. + + The list of names may contain tuples if aliases are wanted. + """ + fields = ('template', 'names', 'with_context') + + +class ExprStmt(Stmt): + """A statement that evaluates an expression and discards the result.""" + fields = ('node',) + + +class Assign(Stmt): + """Assigns an expression to a target.""" + fields = ('target', 'node') + + +class AssignBlock(Stmt): + """Assigns a block to a target.""" + fields = ('target', 'filter', 'body') + + +class Expr(Node): + """Baseclass for all expressions.""" + abstract = True + + def as_const(self, eval_ctx=None): + """Return the value of the expression as constant or raise + :exc:`Impossible` if this was not possible. + + An :class:`EvalContext` can be provided, if none is given + a default context is created which requires the nodes to have + an attached environment. + + .. versionchanged:: 2.4 + the `eval_ctx` parameter was added. + """ + raise Impossible() + + def can_assign(self): + """Check if it's possible to assign something to this node.""" + return False + + +class BinExpr(Expr): + """Baseclass for all binary expressions.""" + fields = ('left', 'right') + operator = None + abstract = True + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + # intercepted operators cannot be folded at compile time + if self.environment.sandboxed and \ + self.operator in self.environment.intercepted_binops: + raise Impossible() + f = _binop_to_func[self.operator] + try: + return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx)) + except Exception: + raise Impossible() + + +class UnaryExpr(Expr): + """Baseclass for all unary expressions.""" + fields = ('node',) + operator = None + abstract = True + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + # intercepted operators cannot be folded at compile time + if self.environment.sandboxed and \ + self.operator in self.environment.intercepted_unops: + raise Impossible() + f = _uaop_to_func[self.operator] + try: + return f(self.node.as_const(eval_ctx)) + except Exception: + raise Impossible() + + +class Name(Expr): + """Looks up a name or stores a value in a name. + The `ctx` of the node can be one of the following values: + + - `store`: store a value in the name + - `load`: load that name + - `param`: like `store` but if the name was defined as function parameter. + """ + fields = ('name', 'ctx') + + def can_assign(self): + return self.name not in ('true', 'false', 'none', + 'True', 'False', 'None') + + +class NSRef(Expr): + """Reference to a namespace value assignment""" + fields = ('name', 'attr') + + def can_assign(self): + # We don't need any special checks here; NSRef assignments have a + # runtime check to ensure the target is a namespace object which will + # have been checked already as it is created using a normal assignment + # which goes through a `Name` node. + return True + + +class Literal(Expr): + """Baseclass for literals.""" + abstract = True + + +class Const(Literal): + """All constant values. The parser will return this node for simple + constants such as ``42`` or ``"foo"`` but it can be used to store more + complex values such as lists too. Only constants with a safe + representation (objects where ``eval(repr(x)) == x`` is true). + """ + fields = ('value',) + + def as_const(self, eval_ctx=None): + rv = self.value + if PY2 and type(rv) is text_type and \ + self.environment.policies['compiler.ascii_str']: + try: + rv = rv.encode('ascii') + except UnicodeError: + pass + return rv + + @classmethod + def from_untrusted(cls, value, lineno=None, environment=None): + """Return a const object if the value is representable as + constant value in the generated code, otherwise it will raise + an `Impossible` exception. + """ + from .compiler import has_safe_repr + if not has_safe_repr(value): + raise Impossible() + return cls(value, lineno=lineno, environment=environment) + + +class TemplateData(Literal): + """A constant template string.""" + fields = ('data',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile: + raise Impossible() + if eval_ctx.autoescape: + return Markup(self.data) + return self.data + + +class Tuple(Literal): + """For loop unpacking and some other things like multiple arguments + for subscripts. Like for :class:`Name` `ctx` specifies if the tuple + is used for loading the names or storing. + """ + fields = ('items', 'ctx') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return tuple(x.as_const(eval_ctx) for x in self.items) + + def can_assign(self): + for item in self.items: + if not item.can_assign(): + return False + return True + + +class List(Literal): + """Any list literal such as ``[1, 2, 3]``""" + fields = ('items',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return [x.as_const(eval_ctx) for x in self.items] + + +class Dict(Literal): + """Any dict literal such as ``{1: 2, 3: 4}``. The items must be a list of + :class:`Pair` nodes. + """ + fields = ('items',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return dict(x.as_const(eval_ctx) for x in self.items) + + +class Pair(Helper): + """A key, value pair for dicts.""" + fields = ('key', 'value') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx) + + +class Keyword(Helper): + """A key, value pair for keyword arguments where key is a string.""" + fields = ('key', 'value') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.key, self.value.as_const(eval_ctx) + + +class CondExpr(Expr): + """A conditional expression (inline if expression). (``{{ + foo if bar else baz }}``) + """ + fields = ('test', 'expr1', 'expr2') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if self.test.as_const(eval_ctx): + return self.expr1.as_const(eval_ctx) + + # if we evaluate to an undefined object, we better do that at runtime + if self.expr2 is None: + raise Impossible() + + return self.expr2.as_const(eval_ctx) + + +def args_as_const(node, eval_ctx): + args = [x.as_const(eval_ctx) for x in node.args] + kwargs = dict(x.as_const(eval_ctx) for x in node.kwargs) + + if node.dyn_args is not None: + try: + args.extend(node.dyn_args.as_const(eval_ctx)) + except Exception: + raise Impossible() + + if node.dyn_kwargs is not None: + try: + kwargs.update(node.dyn_kwargs.as_const(eval_ctx)) + except Exception: + raise Impossible() + + return args, kwargs + + +class Filter(Expr): + """This node applies a filter on an expression. `name` is the name of + the filter, the rest of the fields are the same as for :class:`Call`. + + If the `node` of a filter is `None` the contents of the last buffer are + filtered. Buffers are created by macros and filter blocks. + """ + + fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + + if eval_ctx.volatile or self.node is None: + raise Impossible() + + # we have to be careful here because we call filter_ below. + # if this variable would be called filter, 2to3 would wrap the + # call in a list beause it is assuming we are talking about the + # builtin filter function here which no longer returns a list in + # python 3. because of that, do not rename filter_ to filter! + filter_ = self.environment.filters.get(self.name) + + if filter_ is None or getattr(filter_, 'contextfilter', False): + raise Impossible() + + # We cannot constant handle async filters, so we need to make sure + # to not go down this path. + if ( + eval_ctx.environment.is_async + and getattr(filter_, 'asyncfiltervariant', False) + ): + raise Impossible() + + args, kwargs = args_as_const(self, eval_ctx) + args.insert(0, self.node.as_const(eval_ctx)) + + if getattr(filter_, 'evalcontextfilter', False): + args.insert(0, eval_ctx) + elif getattr(filter_, 'environmentfilter', False): + args.insert(0, self.environment) + + try: + return filter_(*args, **kwargs) + except Exception: + raise Impossible() + + +class Test(Expr): + """Applies a test on an expression. `name` is the name of the test, the + rest of the fields are the same as for :class:`Call`. + """ + + fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') + + def as_const(self, eval_ctx=None): + test = self.environment.tests.get(self.name) + + if test is None: + raise Impossible() + + eval_ctx = get_eval_context(self, eval_ctx) + args, kwargs = args_as_const(self, eval_ctx) + args.insert(0, self.node.as_const(eval_ctx)) + + try: + return test(*args, **kwargs) + except Exception: + raise Impossible() + + +class Call(Expr): + """Calls an expression. `args` is a list of arguments, `kwargs` a list + of keyword arguments (list of :class:`Keyword` nodes), and `dyn_args` + and `dyn_kwargs` has to be either `None` or a node that is used as + node for dynamic positional (``*args``) or keyword (``**kwargs``) + arguments. + """ + fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') + + +class Getitem(Expr): + """Get an attribute or item from an expression and prefer the item.""" + fields = ('node', 'arg', 'ctx') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if self.ctx != 'load': + raise Impossible() + try: + return self.environment.getitem(self.node.as_const(eval_ctx), + self.arg.as_const(eval_ctx)) + except Exception: + raise Impossible() + + def can_assign(self): + return False + + +class Getattr(Expr): + """Get an attribute or item from an expression that is a ascii-only + bytestring and prefer the attribute. + """ + fields = ('node', 'attr', 'ctx') + + def as_const(self, eval_ctx=None): + if self.ctx != 'load': + raise Impossible() + try: + eval_ctx = get_eval_context(self, eval_ctx) + return self.environment.getattr(self.node.as_const(eval_ctx), + self.attr) + except Exception: + raise Impossible() + + def can_assign(self): + return False + + +class Slice(Expr): + """Represents a slice object. This must only be used as argument for + :class:`Subscript`. + """ + fields = ('start', 'stop', 'step') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + def const(obj): + if obj is None: + return None + return obj.as_const(eval_ctx) + return slice(const(self.start), const(self.stop), const(self.step)) + + +class Concat(Expr): + """Concatenates the list of expressions provided after converting them to + unicode. + """ + fields = ('nodes',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return ''.join(text_type(x.as_const(eval_ctx)) for x in self.nodes) + + +class Compare(Expr): + """Compares an expression with some other expressions. `ops` must be a + list of :class:`Operand`\\s. + """ + fields = ('expr', 'ops') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + result = value = self.expr.as_const(eval_ctx) + try: + for op in self.ops: + new_value = op.expr.as_const(eval_ctx) + result = _cmpop_to_func[op.op](value, new_value) + value = new_value + except Exception: + raise Impossible() + return result + + +class Operand(Helper): + """Holds an operator and an expression.""" + fields = ('op', 'expr') + +if __debug__: + Operand.__doc__ += '\nThe following operators are available: ' + \ + ', '.join(sorted('``%s``' % x for x in set(_binop_to_func) | + set(_uaop_to_func) | set(_cmpop_to_func))) + + +class Mul(BinExpr): + """Multiplies the left with the right node.""" + operator = '*' + + +class Div(BinExpr): + """Divides the left by the right node.""" + operator = '/' + + +class FloorDiv(BinExpr): + """Divides the left by the right node and truncates conver the + result into an integer by truncating. + """ + operator = '//' + + +class Add(BinExpr): + """Add the left to the right node.""" + operator = '+' + + +class Sub(BinExpr): + """Subtract the right from the left node.""" + operator = '-' + + +class Mod(BinExpr): + """Left modulo right.""" + operator = '%' + + +class Pow(BinExpr): + """Left to the power of right.""" + operator = '**' + + +class And(BinExpr): + """Short circuited AND.""" + operator = 'and' + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.left.as_const(eval_ctx) and self.right.as_const(eval_ctx) + + +class Or(BinExpr): + """Short circuited OR.""" + operator = 'or' + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.left.as_const(eval_ctx) or self.right.as_const(eval_ctx) + + +class Not(UnaryExpr): + """Negate the expression.""" + operator = 'not' + + +class Neg(UnaryExpr): + """Make the expression negative.""" + operator = '-' + + +class Pos(UnaryExpr): + """Make the expression positive (noop for most expressions)""" + operator = '+' + + +# Helpers for extensions + + +class EnvironmentAttribute(Expr): + """Loads an attribute from the environment object. This is useful for + extensions that want to call a callback stored on the environment. + """ + fields = ('name',) + + +class ExtensionAttribute(Expr): + """Returns the attribute of an extension bound to the environment. + The identifier is the identifier of the :class:`Extension`. + + This node is usually constructed by calling the + :meth:`~jinja2.ext.Extension.attr` method on an extension. + """ + fields = ('identifier', 'name') + + +class ImportedName(Expr): + """If created with an import name the import name is returned on node + access. For example ``ImportedName('cgi.escape')`` returns the `escape` + function from the cgi module on evaluation. Imports are optimized by the + compiler so there is no need to assign them to local variables. + """ + fields = ('importname',) + + +class InternalName(Expr): + """An internal name in the compiler. You cannot create these nodes + yourself but the parser provides a + :meth:`~jinja2.parser.Parser.free_identifier` method that creates + a new identifier for you. This identifier is not available from the + template and is not threated specially by the compiler. + """ + fields = ('name',) + + def __init__(self): + raise TypeError('Can\'t create internal names. Use the ' + '`free_identifier` method on a parser.') + + +class MarkSafe(Expr): + """Mark the wrapped expression as safe (wrap it as `Markup`).""" + fields = ('expr',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return Markup(self.expr.as_const(eval_ctx)) + + +class MarkSafeIfAutoescape(Expr): + """Mark the wrapped expression as safe (wrap it as `Markup`) but + only if autoescaping is active. + + .. versionadded:: 2.5 + """ + fields = ('expr',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile: + raise Impossible() + expr = self.expr.as_const(eval_ctx) + if eval_ctx.autoescape: + return Markup(expr) + return expr + + +class ContextReference(Expr): + """Returns the current template context. It can be used like a + :class:`Name` node, with a ``'load'`` ctx and will return the + current :class:`~jinja2.runtime.Context` object. + + Here an example that assigns the current template name to a + variable named `foo`:: + + Assign(Name('foo', ctx='store'), + Getattr(ContextReference(), 'name')) + """ + + +class Continue(Stmt): + """Continue a loop.""" + + +class Break(Stmt): + """Break a loop.""" + + +class Scope(Stmt): + """An artificial scope.""" + fields = ('body',) + + +class OverlayScope(Stmt): + """An overlay scope for extensions. This is a largely unoptimized scope + that however can be used to introduce completely arbitrary variables into + a sub scope from a dictionary or dictionary like object. The `context` + field has to evaluate to a dictionary object. + + Example usage:: + + OverlayScope(context=self.call_method('get_context'), + body=[...]) + + .. versionadded:: 2.10 + """ + fields = ('context', 'body') + + +class EvalContextModifier(Stmt): + """Modifies the eval context. For each option that should be modified, + a :class:`Keyword` has to be added to the :attr:`options` list. + + Example to change the `autoescape` setting:: + + EvalContextModifier(options=[Keyword('autoescape', Const(True))]) + """ + fields = ('options',) + + +class ScopedEvalContextModifier(EvalContextModifier): + """Modifies the eval context and reverts it later. Works exactly like + :class:`EvalContextModifier` but will only modify the + :class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`. + """ + fields = ('body',) + + +# make sure nobody creates custom nodes +def _failing_new(*args, **kwargs): + raise TypeError('can\'t create custom node types') +NodeType.__new__ = staticmethod(_failing_new); del _failing_new diff --git a/venv/Lib/site-packages/jinja2/optimizer.py b/venv/Lib/site-packages/jinja2/optimizer.py new file mode 100644 index 0000000..65ab3ce --- /dev/null +++ b/venv/Lib/site-packages/jinja2/optimizer.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +""" + jinja2.optimizer + ~~~~~~~~~~~~~~~~ + + The jinja optimizer is currently trying to constant fold a few expressions + and modify the AST in place so that it should be easier to evaluate it. + + Because the AST does not contain all the scoping information and the + compiler has to find that out, we cannot do all the optimizations we + want. For example loop unrolling doesn't work because unrolled loops would + have a different scoping. + + The solution would be a second syntax tree that has the scoping rules stored. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD. +""" +from jinja2 import nodes +from jinja2.visitor import NodeTransformer + + +def optimize(node, environment): + """The context hint can be used to perform an static optimization + based on the context given.""" + optimizer = Optimizer(environment) + return optimizer.visit(node) + + +class Optimizer(NodeTransformer): + + def __init__(self, environment): + self.environment = environment + + def fold(self, node, eval_ctx=None): + """Do constant folding.""" + node = self.generic_visit(node) + try: + return nodes.Const.from_untrusted(node.as_const(eval_ctx), + lineno=node.lineno, + environment=self.environment) + except nodes.Impossible: + return node + + visit_Add = visit_Sub = visit_Mul = visit_Div = visit_FloorDiv = \ + visit_Pow = visit_Mod = visit_And = visit_Or = visit_Pos = visit_Neg = \ + visit_Not = visit_Compare = visit_Getitem = visit_Getattr = visit_Call = \ + visit_Filter = visit_Test = visit_CondExpr = fold + del fold diff --git a/venv/Lib/site-packages/jinja2/parser.py b/venv/Lib/site-packages/jinja2/parser.py new file mode 100644 index 0000000..ed00d97 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/parser.py @@ -0,0 +1,903 @@ +# -*- coding: utf-8 -*- +""" + jinja2.parser + ~~~~~~~~~~~~~ + + Implements the template parser. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +from jinja2 import nodes +from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError +from jinja2.lexer import describe_token, describe_token_expr +from jinja2._compat import imap + + +_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print', + 'macro', 'include', 'from', 'import', + 'set', 'with', 'autoescape']) +_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq']) + +_math_nodes = { + 'add': nodes.Add, + 'sub': nodes.Sub, + 'mul': nodes.Mul, + 'div': nodes.Div, + 'floordiv': nodes.FloorDiv, + 'mod': nodes.Mod, +} + + +class Parser(object): + """This is the central parsing class Jinja2 uses. It's passed to + extensions and can be used to parse expressions or statements. + """ + + def __init__(self, environment, source, name=None, filename=None, + state=None): + self.environment = environment + self.stream = environment._tokenize(source, name, filename, state) + self.name = name + self.filename = filename + self.closed = False + self.extensions = {} + for extension in environment.iter_extensions(): + for tag in extension.tags: + self.extensions[tag] = extension.parse + self._last_identifier = 0 + self._tag_stack = [] + self._end_token_stack = [] + + def fail(self, msg, lineno=None, exc=TemplateSyntaxError): + """Convenience method that raises `exc` with the message, passed + line number or last line number as well as the current name and + filename. + """ + if lineno is None: + lineno = self.stream.current.lineno + raise exc(msg, lineno, self.name, self.filename) + + def _fail_ut_eof(self, name, end_token_stack, lineno): + expected = [] + for exprs in end_token_stack: + expected.extend(imap(describe_token_expr, exprs)) + if end_token_stack: + currently_looking = ' or '.join( + "'%s'" % describe_token_expr(expr) + for expr in end_token_stack[-1]) + else: + currently_looking = None + + if name is None: + message = ['Unexpected end of template.'] + else: + message = ['Encountered unknown tag \'%s\'.' % name] + + if currently_looking: + if name is not None and name in expected: + message.append('You probably made a nesting mistake. Jinja ' + 'is expecting this tag, but currently looking ' + 'for %s.' % currently_looking) + else: + message.append('Jinja was looking for the following tags: ' + '%s.' % currently_looking) + + if self._tag_stack: + message.append('The innermost block that needs to be ' + 'closed is \'%s\'.' % self._tag_stack[-1]) + + self.fail(' '.join(message), lineno) + + def fail_unknown_tag(self, name, lineno=None): + """Called if the parser encounters an unknown tag. Tries to fail + with a human readable error message that could help to identify + the problem. + """ + return self._fail_ut_eof(name, self._end_token_stack, lineno) + + def fail_eof(self, end_tokens=None, lineno=None): + """Like fail_unknown_tag but for end of template situations.""" + stack = list(self._end_token_stack) + if end_tokens is not None: + stack.append(end_tokens) + return self._fail_ut_eof(None, stack, lineno) + + def is_tuple_end(self, extra_end_rules=None): + """Are we at the end of a tuple?""" + if self.stream.current.type in ('variable_end', 'block_end', 'rparen'): + return True + elif extra_end_rules is not None: + return self.stream.current.test_any(extra_end_rules) + return False + + def free_identifier(self, lineno=None): + """Return a new free identifier as :class:`~jinja2.nodes.InternalName`.""" + self._last_identifier += 1 + rv = object.__new__(nodes.InternalName) + nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno) + return rv + + def parse_statement(self): + """Parse a single statement.""" + token = self.stream.current + if token.type != 'name': + self.fail('tag name expected', token.lineno) + self._tag_stack.append(token.value) + pop_tag = True + try: + if token.value in _statement_keywords: + return getattr(self, 'parse_' + self.stream.current.value)() + if token.value == 'call': + return self.parse_call_block() + if token.value == 'filter': + return self.parse_filter_block() + ext = self.extensions.get(token.value) + if ext is not None: + return ext(self) + + # did not work out, remove the token we pushed by accident + # from the stack so that the unknown tag fail function can + # produce a proper error message. + self._tag_stack.pop() + pop_tag = False + self.fail_unknown_tag(token.value, token.lineno) + finally: + if pop_tag: + self._tag_stack.pop() + + def parse_statements(self, end_tokens, drop_needle=False): + """Parse multiple statements into a list until one of the end tokens + is reached. This is used to parse the body of statements as it also + parses template data if appropriate. The parser checks first if the + current token is a colon and skips it if there is one. Then it checks + for the block end and parses until if one of the `end_tokens` is + reached. Per default the active token in the stream at the end of + the call is the matched end token. If this is not wanted `drop_needle` + can be set to `True` and the end token is removed. + """ + # the first token may be a colon for python compatibility + self.stream.skip_if('colon') + + # in the future it would be possible to add whole code sections + # by adding some sort of end of statement token and parsing those here. + self.stream.expect('block_end') + result = self.subparse(end_tokens) + + # we reached the end of the template too early, the subparser + # does not check for this, so we do that now + if self.stream.current.type == 'eof': + self.fail_eof(end_tokens) + + if drop_needle: + next(self.stream) + return result + + def parse_set(self): + """Parse an assign statement.""" + lineno = next(self.stream).lineno + target = self.parse_assign_target(with_namespace=True) + if self.stream.skip_if('assign'): + expr = self.parse_tuple() + return nodes.Assign(target, expr, lineno=lineno) + filter_node = self.parse_filter(None) + body = self.parse_statements(('name:endset',), + drop_needle=True) + return nodes.AssignBlock(target, filter_node, body, lineno=lineno) + + def parse_for(self): + """Parse a for loop.""" + lineno = self.stream.expect('name:for').lineno + target = self.parse_assign_target(extra_end_rules=('name:in',)) + self.stream.expect('name:in') + iter = self.parse_tuple(with_condexpr=False, + extra_end_rules=('name:recursive',)) + test = None + if self.stream.skip_if('name:if'): + test = self.parse_expression() + recursive = self.stream.skip_if('name:recursive') + body = self.parse_statements(('name:endfor', 'name:else')) + if next(self.stream).value == 'endfor': + else_ = [] + else: + else_ = self.parse_statements(('name:endfor',), drop_needle=True) + return nodes.For(target, iter, body, else_, test, + recursive, lineno=lineno) + + def parse_if(self): + """Parse an if construct.""" + node = result = nodes.If(lineno=self.stream.expect('name:if').lineno) + while 1: + node.test = self.parse_tuple(with_condexpr=False) + node.body = self.parse_statements(('name:elif', 'name:else', + 'name:endif')) + node.elif_ = [] + node.else_ = [] + token = next(self.stream) + if token.test('name:elif'): + node = nodes.If(lineno=self.stream.current.lineno) + result.elif_.append(node) + continue + elif token.test('name:else'): + result.else_ = self.parse_statements(('name:endif',), + drop_needle=True) + break + return result + + def parse_with(self): + node = nodes.With(lineno=next(self.stream).lineno) + targets = [] + values = [] + while self.stream.current.type != 'block_end': + lineno = self.stream.current.lineno + if targets: + self.stream.expect('comma') + target = self.parse_assign_target() + target.set_ctx('param') + targets.append(target) + self.stream.expect('assign') + values.append(self.parse_expression()) + node.targets = targets + node.values = values + node.body = self.parse_statements(('name:endwith',), + drop_needle=True) + return node + + def parse_autoescape(self): + node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno) + node.options = [ + nodes.Keyword('autoescape', self.parse_expression()) + ] + node.body = self.parse_statements(('name:endautoescape',), + drop_needle=True) + return nodes.Scope([node]) + + def parse_block(self): + node = nodes.Block(lineno=next(self.stream).lineno) + node.name = self.stream.expect('name').value + node.scoped = self.stream.skip_if('name:scoped') + + # common problem people encounter when switching from django + # to jinja. we do not support hyphens in block names, so let's + # raise a nicer error message in that case. + if self.stream.current.type == 'sub': + self.fail('Block names in Jinja have to be valid Python ' + 'identifiers and may not contain hyphens, use an ' + 'underscore instead.') + + node.body = self.parse_statements(('name:endblock',), drop_needle=True) + self.stream.skip_if('name:' + node.name) + return node + + def parse_extends(self): + node = nodes.Extends(lineno=next(self.stream).lineno) + node.template = self.parse_expression() + return node + + def parse_import_context(self, node, default): + if self.stream.current.test_any('name:with', 'name:without') and \ + self.stream.look().test('name:context'): + node.with_context = next(self.stream).value == 'with' + self.stream.skip() + else: + node.with_context = default + return node + + def parse_include(self): + node = nodes.Include(lineno=next(self.stream).lineno) + node.template = self.parse_expression() + if self.stream.current.test('name:ignore') and \ + self.stream.look().test('name:missing'): + node.ignore_missing = True + self.stream.skip(2) + else: + node.ignore_missing = False + return self.parse_import_context(node, True) + + def parse_import(self): + node = nodes.Import(lineno=next(self.stream).lineno) + node.template = self.parse_expression() + self.stream.expect('name:as') + node.target = self.parse_assign_target(name_only=True).name + return self.parse_import_context(node, False) + + def parse_from(self): + node = nodes.FromImport(lineno=next(self.stream).lineno) + node.template = self.parse_expression() + self.stream.expect('name:import') + node.names = [] + + def parse_context(): + if self.stream.current.value in ('with', 'without') and \ + self.stream.look().test('name:context'): + node.with_context = next(self.stream).value == 'with' + self.stream.skip() + return True + return False + + while 1: + if node.names: + self.stream.expect('comma') + if self.stream.current.type == 'name': + if parse_context(): + break + target = self.parse_assign_target(name_only=True) + if target.name.startswith('_'): + self.fail('names starting with an underline can not ' + 'be imported', target.lineno, + exc=TemplateAssertionError) + if self.stream.skip_if('name:as'): + alias = self.parse_assign_target(name_only=True) + node.names.append((target.name, alias.name)) + else: + node.names.append(target.name) + if parse_context() or self.stream.current.type != 'comma': + break + else: + self.stream.expect('name') + if not hasattr(node, 'with_context'): + node.with_context = False + return node + + def parse_signature(self, node): + node.args = args = [] + node.defaults = defaults = [] + self.stream.expect('lparen') + while self.stream.current.type != 'rparen': + if args: + self.stream.expect('comma') + arg = self.parse_assign_target(name_only=True) + arg.set_ctx('param') + if self.stream.skip_if('assign'): + defaults.append(self.parse_expression()) + elif defaults: + self.fail('non-default argument follows default argument') + args.append(arg) + self.stream.expect('rparen') + + def parse_call_block(self): + node = nodes.CallBlock(lineno=next(self.stream).lineno) + if self.stream.current.type == 'lparen': + self.parse_signature(node) + else: + node.args = [] + node.defaults = [] + + node.call = self.parse_expression() + if not isinstance(node.call, nodes.Call): + self.fail('expected call', node.lineno) + node.body = self.parse_statements(('name:endcall',), drop_needle=True) + return node + + def parse_filter_block(self): + node = nodes.FilterBlock(lineno=next(self.stream).lineno) + node.filter = self.parse_filter(None, start_inline=True) + node.body = self.parse_statements(('name:endfilter',), + drop_needle=True) + return node + + def parse_macro(self): + node = nodes.Macro(lineno=next(self.stream).lineno) + node.name = self.parse_assign_target(name_only=True).name + self.parse_signature(node) + node.body = self.parse_statements(('name:endmacro',), + drop_needle=True) + return node + + def parse_print(self): + node = nodes.Output(lineno=next(self.stream).lineno) + node.nodes = [] + while self.stream.current.type != 'block_end': + if node.nodes: + self.stream.expect('comma') + node.nodes.append(self.parse_expression()) + return node + + def parse_assign_target(self, with_tuple=True, name_only=False, + extra_end_rules=None, with_namespace=False): + """Parse an assignment target. As Jinja2 allows assignments to + tuples, this function can parse all allowed assignment targets. Per + default assignments to tuples are parsed, that can be disable however + by setting `with_tuple` to `False`. If only assignments to names are + wanted `name_only` can be set to `True`. The `extra_end_rules` + parameter is forwarded to the tuple parsing function. If + `with_namespace` is enabled, a namespace assignment may be parsed. + """ + if with_namespace and self.stream.look().type == 'dot': + token = self.stream.expect('name') + next(self.stream) # dot + attr = self.stream.expect('name') + target = nodes.NSRef(token.value, attr.value, lineno=token.lineno) + elif name_only: + token = self.stream.expect('name') + target = nodes.Name(token.value, 'store', lineno=token.lineno) + else: + if with_tuple: + target = self.parse_tuple(simplified=True, + extra_end_rules=extra_end_rules) + else: + target = self.parse_primary() + target.set_ctx('store') + if not target.can_assign(): + self.fail('can\'t assign to %r' % target.__class__. + __name__.lower(), target.lineno) + return target + + def parse_expression(self, with_condexpr=True): + """Parse an expression. Per default all expressions are parsed, if + the optional `with_condexpr` parameter is set to `False` conditional + expressions are not parsed. + """ + if with_condexpr: + return self.parse_condexpr() + return self.parse_or() + + def parse_condexpr(self): + lineno = self.stream.current.lineno + expr1 = self.parse_or() + while self.stream.skip_if('name:if'): + expr2 = self.parse_or() + if self.stream.skip_if('name:else'): + expr3 = self.parse_condexpr() + else: + expr3 = None + expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno) + lineno = self.stream.current.lineno + return expr1 + + def parse_or(self): + lineno = self.stream.current.lineno + left = self.parse_and() + while self.stream.skip_if('name:or'): + right = self.parse_and() + left = nodes.Or(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_and(self): + lineno = self.stream.current.lineno + left = self.parse_not() + while self.stream.skip_if('name:and'): + right = self.parse_not() + left = nodes.And(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_not(self): + if self.stream.current.test('name:not'): + lineno = next(self.stream).lineno + return nodes.Not(self.parse_not(), lineno=lineno) + return self.parse_compare() + + def parse_compare(self): + lineno = self.stream.current.lineno + expr = self.parse_math1() + ops = [] + while 1: + token_type = self.stream.current.type + if token_type in _compare_operators: + next(self.stream) + ops.append(nodes.Operand(token_type, self.parse_math1())) + elif self.stream.skip_if('name:in'): + ops.append(nodes.Operand('in', self.parse_math1())) + elif (self.stream.current.test('name:not') and + self.stream.look().test('name:in')): + self.stream.skip(2) + ops.append(nodes.Operand('notin', self.parse_math1())) + else: + break + lineno = self.stream.current.lineno + if not ops: + return expr + return nodes.Compare(expr, ops, lineno=lineno) + + def parse_math1(self): + lineno = self.stream.current.lineno + left = self.parse_concat() + while self.stream.current.type in ('add', 'sub'): + cls = _math_nodes[self.stream.current.type] + next(self.stream) + right = self.parse_concat() + left = cls(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_concat(self): + lineno = self.stream.current.lineno + args = [self.parse_math2()] + while self.stream.current.type == 'tilde': + next(self.stream) + args.append(self.parse_math2()) + if len(args) == 1: + return args[0] + return nodes.Concat(args, lineno=lineno) + + def parse_math2(self): + lineno = self.stream.current.lineno + left = self.parse_pow() + while self.stream.current.type in ('mul', 'div', 'floordiv', 'mod'): + cls = _math_nodes[self.stream.current.type] + next(self.stream) + right = self.parse_pow() + left = cls(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_pow(self): + lineno = self.stream.current.lineno + left = self.parse_unary() + while self.stream.current.type == 'pow': + next(self.stream) + right = self.parse_unary() + left = nodes.Pow(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_unary(self, with_filter=True): + token_type = self.stream.current.type + lineno = self.stream.current.lineno + if token_type == 'sub': + next(self.stream) + node = nodes.Neg(self.parse_unary(False), lineno=lineno) + elif token_type == 'add': + next(self.stream) + node = nodes.Pos(self.parse_unary(False), lineno=lineno) + else: + node = self.parse_primary() + node = self.parse_postfix(node) + if with_filter: + node = self.parse_filter_expr(node) + return node + + def parse_primary(self): + token = self.stream.current + if token.type == 'name': + if token.value in ('true', 'false', 'True', 'False'): + node = nodes.Const(token.value in ('true', 'True'), + lineno=token.lineno) + elif token.value in ('none', 'None'): + node = nodes.Const(None, lineno=token.lineno) + else: + node = nodes.Name(token.value, 'load', lineno=token.lineno) + next(self.stream) + elif token.type == 'string': + next(self.stream) + buf = [token.value] + lineno = token.lineno + while self.stream.current.type == 'string': + buf.append(self.stream.current.value) + next(self.stream) + node = nodes.Const(''.join(buf), lineno=lineno) + elif token.type in ('integer', 'float'): + next(self.stream) + node = nodes.Const(token.value, lineno=token.lineno) + elif token.type == 'lparen': + next(self.stream) + node = self.parse_tuple(explicit_parentheses=True) + self.stream.expect('rparen') + elif token.type == 'lbracket': + node = self.parse_list() + elif token.type == 'lbrace': + node = self.parse_dict() + else: + self.fail("unexpected '%s'" % describe_token(token), token.lineno) + return node + + def parse_tuple(self, simplified=False, with_condexpr=True, + extra_end_rules=None, explicit_parentheses=False): + """Works like `parse_expression` but if multiple expressions are + delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created. + This method could also return a regular expression instead of a tuple + if no commas where found. + + The default parsing mode is a full tuple. If `simplified` is `True` + only names and literals are parsed. The `no_condexpr` parameter is + forwarded to :meth:`parse_expression`. + + Because tuples do not require delimiters and may end in a bogus comma + an extra hint is needed that marks the end of a tuple. For example + for loops support tuples between `for` and `in`. In that case the + `extra_end_rules` is set to ``['name:in']``. + + `explicit_parentheses` is true if the parsing was triggered by an + expression in parentheses. This is used to figure out if an empty + tuple is a valid expression or not. + """ + lineno = self.stream.current.lineno + if simplified: + parse = self.parse_primary + elif with_condexpr: + parse = self.parse_expression + else: + parse = lambda: self.parse_expression(with_condexpr=False) + args = [] + is_tuple = False + while 1: + if args: + self.stream.expect('comma') + if self.is_tuple_end(extra_end_rules): + break + args.append(parse()) + if self.stream.current.type == 'comma': + is_tuple = True + else: + break + lineno = self.stream.current.lineno + + if not is_tuple: + if args: + return args[0] + + # if we don't have explicit parentheses, an empty tuple is + # not a valid expression. This would mean nothing (literally + # nothing) in the spot of an expression would be an empty + # tuple. + if not explicit_parentheses: + self.fail('Expected an expression, got \'%s\'' % + describe_token(self.stream.current)) + + return nodes.Tuple(args, 'load', lineno=lineno) + + def parse_list(self): + token = self.stream.expect('lbracket') + items = [] + while self.stream.current.type != 'rbracket': + if items: + self.stream.expect('comma') + if self.stream.current.type == 'rbracket': + break + items.append(self.parse_expression()) + self.stream.expect('rbracket') + return nodes.List(items, lineno=token.lineno) + + def parse_dict(self): + token = self.stream.expect('lbrace') + items = [] + while self.stream.current.type != 'rbrace': + if items: + self.stream.expect('comma') + if self.stream.current.type == 'rbrace': + break + key = self.parse_expression() + self.stream.expect('colon') + value = self.parse_expression() + items.append(nodes.Pair(key, value, lineno=key.lineno)) + self.stream.expect('rbrace') + return nodes.Dict(items, lineno=token.lineno) + + def parse_postfix(self, node): + while 1: + token_type = self.stream.current.type + if token_type == 'dot' or token_type == 'lbracket': + node = self.parse_subscript(node) + # calls are valid both after postfix expressions (getattr + # and getitem) as well as filters and tests + elif token_type == 'lparen': + node = self.parse_call(node) + else: + break + return node + + def parse_filter_expr(self, node): + while 1: + token_type = self.stream.current.type + if token_type == 'pipe': + node = self.parse_filter(node) + elif token_type == 'name' and self.stream.current.value == 'is': + node = self.parse_test(node) + # calls are valid both after postfix expressions (getattr + # and getitem) as well as filters and tests + elif token_type == 'lparen': + node = self.parse_call(node) + else: + break + return node + + def parse_subscript(self, node): + token = next(self.stream) + if token.type == 'dot': + attr_token = self.stream.current + next(self.stream) + if attr_token.type == 'name': + return nodes.Getattr(node, attr_token.value, 'load', + lineno=token.lineno) + elif attr_token.type != 'integer': + self.fail('expected name or number', attr_token.lineno) + arg = nodes.Const(attr_token.value, lineno=attr_token.lineno) + return nodes.Getitem(node, arg, 'load', lineno=token.lineno) + if token.type == 'lbracket': + args = [] + while self.stream.current.type != 'rbracket': + if args: + self.stream.expect('comma') + args.append(self.parse_subscribed()) + self.stream.expect('rbracket') + if len(args) == 1: + arg = args[0] + else: + arg = nodes.Tuple(args, 'load', lineno=token.lineno) + return nodes.Getitem(node, arg, 'load', lineno=token.lineno) + self.fail('expected subscript expression', self.lineno) + + def parse_subscribed(self): + lineno = self.stream.current.lineno + + if self.stream.current.type == 'colon': + next(self.stream) + args = [None] + else: + node = self.parse_expression() + if self.stream.current.type != 'colon': + return node + next(self.stream) + args = [node] + + if self.stream.current.type == 'colon': + args.append(None) + elif self.stream.current.type not in ('rbracket', 'comma'): + args.append(self.parse_expression()) + else: + args.append(None) + + if self.stream.current.type == 'colon': + next(self.stream) + if self.stream.current.type not in ('rbracket', 'comma'): + args.append(self.parse_expression()) + else: + args.append(None) + else: + args.append(None) + + return nodes.Slice(lineno=lineno, *args) + + def parse_call(self, node): + token = self.stream.expect('lparen') + args = [] + kwargs = [] + dyn_args = dyn_kwargs = None + require_comma = False + + def ensure(expr): + if not expr: + self.fail('invalid syntax for function call expression', + token.lineno) + + while self.stream.current.type != 'rparen': + if require_comma: + self.stream.expect('comma') + # support for trailing comma + if self.stream.current.type == 'rparen': + break + if self.stream.current.type == 'mul': + ensure(dyn_args is None and dyn_kwargs is None) + next(self.stream) + dyn_args = self.parse_expression() + elif self.stream.current.type == 'pow': + ensure(dyn_kwargs is None) + next(self.stream) + dyn_kwargs = self.parse_expression() + else: + ensure(dyn_args is None and dyn_kwargs is None) + if self.stream.current.type == 'name' and \ + self.stream.look().type == 'assign': + key = self.stream.current.value + self.stream.skip(2) + value = self.parse_expression() + kwargs.append(nodes.Keyword(key, value, + lineno=value.lineno)) + else: + ensure(not kwargs) + args.append(self.parse_expression()) + + require_comma = True + self.stream.expect('rparen') + + if node is None: + return args, kwargs, dyn_args, dyn_kwargs + return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, + lineno=token.lineno) + + def parse_filter(self, node, start_inline=False): + while self.stream.current.type == 'pipe' or start_inline: + if not start_inline: + next(self.stream) + token = self.stream.expect('name') + name = token.value + while self.stream.current.type == 'dot': + next(self.stream) + name += '.' + self.stream.expect('name').value + if self.stream.current.type == 'lparen': + args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None) + else: + args = [] + kwargs = [] + dyn_args = dyn_kwargs = None + node = nodes.Filter(node, name, args, kwargs, dyn_args, + dyn_kwargs, lineno=token.lineno) + start_inline = False + return node + + def parse_test(self, node): + token = next(self.stream) + if self.stream.current.test('name:not'): + next(self.stream) + negated = True + else: + negated = False + name = self.stream.expect('name').value + while self.stream.current.type == 'dot': + next(self.stream) + name += '.' + self.stream.expect('name').value + dyn_args = dyn_kwargs = None + kwargs = [] + if self.stream.current.type == 'lparen': + args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None) + elif (self.stream.current.type in ('name', 'string', 'integer', + 'float', 'lparen', 'lbracket', + 'lbrace') and not + self.stream.current.test_any('name:else', 'name:or', + 'name:and')): + if self.stream.current.test('name:is'): + self.fail('You cannot chain multiple tests with is') + args = [self.parse_primary()] + else: + args = [] + node = nodes.Test(node, name, args, kwargs, dyn_args, + dyn_kwargs, lineno=token.lineno) + if negated: + node = nodes.Not(node, lineno=token.lineno) + return node + + def subparse(self, end_tokens=None): + body = [] + data_buffer = [] + add_data = data_buffer.append + + if end_tokens is not None: + self._end_token_stack.append(end_tokens) + + def flush_data(): + if data_buffer: + lineno = data_buffer[0].lineno + body.append(nodes.Output(data_buffer[:], lineno=lineno)) + del data_buffer[:] + + try: + while self.stream: + token = self.stream.current + if token.type == 'data': + if token.value: + add_data(nodes.TemplateData(token.value, + lineno=token.lineno)) + next(self.stream) + elif token.type == 'variable_begin': + next(self.stream) + add_data(self.parse_tuple(with_condexpr=True)) + self.stream.expect('variable_end') + elif token.type == 'block_begin': + flush_data() + next(self.stream) + if end_tokens is not None and \ + self.stream.current.test_any(*end_tokens): + return body + rv = self.parse_statement() + if isinstance(rv, list): + body.extend(rv) + else: + body.append(rv) + self.stream.expect('block_end') + else: + raise AssertionError('internal parsing error') + + flush_data() + finally: + if end_tokens is not None: + self._end_token_stack.pop() + + return body + + def parse(self): + """Parse the whole template into a `Template` node.""" + result = nodes.Template(self.subparse(), lineno=1) + result.set_environment(self.environment) + return result diff --git a/venv/Lib/site-packages/jinja2/runtime.py b/venv/Lib/site-packages/jinja2/runtime.py new file mode 100644 index 0000000..f9d7a68 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/runtime.py @@ -0,0 +1,813 @@ +# -*- coding: utf-8 -*- +""" + jinja2.runtime + ~~~~~~~~~~~~~~ + + Runtime helpers. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD. +""" +import sys + +from itertools import chain +from types import MethodType + +from jinja2.nodes import EvalContext, _context_function_types +from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \ + internalcode, object_type_repr, evalcontextfunction, Namespace +from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \ + TemplateNotFound +from jinja2._compat import imap, text_type, iteritems, \ + implements_iterator, implements_to_string, string_types, PY2, \ + with_metaclass + + +# these variables are exported to the template runtime +__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup', + 'TemplateRuntimeError', 'missing', 'concat', 'escape', + 'markup_join', 'unicode_join', 'to_string', 'identity', + 'TemplateNotFound', 'Namespace'] + +#: the name of the function that is used to convert something into +#: a string. We can just use the text type here. +to_string = text_type + +#: the identity function. Useful for certain things in the environment +identity = lambda x: x + +_first_iteration = object() +_last_iteration = object() + + +def markup_join(seq): + """Concatenation that escapes if necessary and converts to unicode.""" + buf = [] + iterator = imap(soft_unicode, seq) + for arg in iterator: + buf.append(arg) + if hasattr(arg, '__html__'): + return Markup(u'').join(chain(buf, iterator)) + return concat(buf) + + +def unicode_join(seq): + """Simple args to unicode conversion and concatenation.""" + return concat(imap(text_type, seq)) + + +def new_context(environment, template_name, blocks, vars=None, + shared=None, globals=None, locals=None): + """Internal helper to for context creation.""" + if vars is None: + vars = {} + if shared: + parent = vars + else: + parent = dict(globals or (), **vars) + if locals: + # if the parent is shared a copy should be created because + # we don't want to modify the dict passed + if shared: + parent = dict(parent) + for key, value in iteritems(locals): + if value is not missing: + parent[key] = value + return environment.context_class(environment, parent, template_name, + blocks) + + +class TemplateReference(object): + """The `self` in templates.""" + + def __init__(self, context): + self.__context = context + + def __getitem__(self, name): + blocks = self.__context.blocks[name] + return BlockReference(name, self.__context, blocks, 0) + + def __repr__(self): + return '<%s %r>' % ( + self.__class__.__name__, + self.__context.name + ) + + +def _get_func(x): + return getattr(x, '__func__', x) + + +class ContextMeta(type): + + def __new__(cls, name, bases, d): + rv = type.__new__(cls, name, bases, d) + if bases == (): + return rv + + resolve = _get_func(rv.resolve) + default_resolve = _get_func(Context.resolve) + resolve_or_missing = _get_func(rv.resolve_or_missing) + default_resolve_or_missing = _get_func(Context.resolve_or_missing) + + # If we have a changed resolve but no changed default or missing + # resolve we invert the call logic. + if resolve is not default_resolve and \ + resolve_or_missing is default_resolve_or_missing: + rv._legacy_resolve_mode = True + elif resolve is default_resolve and \ + resolve_or_missing is default_resolve_or_missing: + rv._fast_resolve_mode = True + + return rv + + +def resolve_or_missing(context, key, missing=missing): + if key in context.vars: + return context.vars[key] + if key in context.parent: + return context.parent[key] + return missing + + +class Context(with_metaclass(ContextMeta)): + """The template context holds the variables of a template. It stores the + values passed to the template and also the names the template exports. + Creating instances is neither supported nor useful as it's created + automatically at various stages of the template evaluation and should not + be created by hand. + + The context is immutable. Modifications on :attr:`parent` **must not** + happen and modifications on :attr:`vars` are allowed from generated + template code only. Template filters and global functions marked as + :func:`contextfunction`\\s get the active context passed as first argument + and are allowed to access the context read-only. + + The template context supports read only dict operations (`get`, + `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`, + `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve` + method that doesn't fail with a `KeyError` but returns an + :class:`Undefined` object for missing variables. + """ + # XXX: we want to eventually make this be a deprecation warning and + # remove it. + _legacy_resolve_mode = False + _fast_resolve_mode = False + + def __init__(self, environment, parent, name, blocks): + self.parent = parent + self.vars = {} + self.environment = environment + self.eval_ctx = EvalContext(self.environment, name) + self.exported_vars = set() + self.name = name + + # create the initial mapping of blocks. Whenever template inheritance + # takes place the runtime will update this mapping with the new blocks + # from the template. + self.blocks = dict((k, [v]) for k, v in iteritems(blocks)) + + # In case we detect the fast resolve mode we can set up an alias + # here that bypasses the legacy code logic. + if self._fast_resolve_mode: + self.resolve_or_missing = MethodType(resolve_or_missing, self) + + def super(self, name, current): + """Render a parent block.""" + try: + blocks = self.blocks[name] + index = blocks.index(current) + 1 + blocks[index] + except LookupError: + return self.environment.undefined('there is no parent block ' + 'called %r.' % name, + name='super') + return BlockReference(name, self, blocks, index) + + def get(self, key, default=None): + """Returns an item from the template context, if it doesn't exist + `default` is returned. + """ + try: + return self[key] + except KeyError: + return default + + def resolve(self, key): + """Looks up a variable like `__getitem__` or `get` but returns an + :class:`Undefined` object with the name of the name looked up. + """ + if self._legacy_resolve_mode: + rv = resolve_or_missing(self, key) + else: + rv = self.resolve_or_missing(key) + if rv is missing: + return self.environment.undefined(name=key) + return rv + + def resolve_or_missing(self, key): + """Resolves a variable like :meth:`resolve` but returns the + special `missing` value if it cannot be found. + """ + if self._legacy_resolve_mode: + rv = self.resolve(key) + if isinstance(rv, Undefined): + rv = missing + return rv + return resolve_or_missing(self, key) + + def get_exported(self): + """Get a new dict with the exported variables.""" + return dict((k, self.vars[k]) for k in self.exported_vars) + + def get_all(self): + """Return the complete context as dict including the exported + variables. For optimizations reasons this might not return an + actual copy so be careful with using it. + """ + if not self.vars: + return self.parent + if not self.parent: + return self.vars + return dict(self.parent, **self.vars) + + @internalcode + def call(__self, __obj, *args, **kwargs): + """Call the callable with the arguments and keyword arguments + provided but inject the active context or environment as first + argument if the callable is a :func:`contextfunction` or + :func:`environmentfunction`. + """ + if __debug__: + __traceback_hide__ = True # noqa + + # Allow callable classes to take a context + if hasattr(__obj, '__call__'): + fn = __obj.__call__ + for fn_type in ('contextfunction', + 'evalcontextfunction', + 'environmentfunction'): + if hasattr(fn, fn_type): + __obj = fn + break + + if isinstance(__obj, _context_function_types): + if getattr(__obj, 'contextfunction', 0): + args = (__self,) + args + elif getattr(__obj, 'evalcontextfunction', 0): + args = (__self.eval_ctx,) + args + elif getattr(__obj, 'environmentfunction', 0): + args = (__self.environment,) + args + try: + return __obj(*args, **kwargs) + except StopIteration: + return __self.environment.undefined('value was undefined because ' + 'a callable raised a ' + 'StopIteration exception') + + def derived(self, locals=None): + """Internal helper function to create a derived context. This is + used in situations where the system needs a new context in the same + template that is independent. + """ + context = new_context(self.environment, self.name, {}, + self.get_all(), True, None, locals) + context.eval_ctx = self.eval_ctx + context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks)) + return context + + def _all(meth): + proxy = lambda self: getattr(self.get_all(), meth)() + proxy.__doc__ = getattr(dict, meth).__doc__ + proxy.__name__ = meth + return proxy + + keys = _all('keys') + values = _all('values') + items = _all('items') + + # not available on python 3 + if PY2: + iterkeys = _all('iterkeys') + itervalues = _all('itervalues') + iteritems = _all('iteritems') + del _all + + def __contains__(self, name): + return name in self.vars or name in self.parent + + def __getitem__(self, key): + """Lookup a variable or raise `KeyError` if the variable is + undefined. + """ + item = self.resolve_or_missing(key) + if item is missing: + raise KeyError(key) + return item + + def __repr__(self): + return '<%s %s of %r>' % ( + self.__class__.__name__, + repr(self.get_all()), + self.name + ) + + +# register the context as mapping if possible +try: + from collections import Mapping + Mapping.register(Context) +except ImportError: + pass + + +class BlockReference(object): + """One block on a template reference.""" + + def __init__(self, name, context, stack, depth): + self.name = name + self._context = context + self._stack = stack + self._depth = depth + + @property + def super(self): + """Super the block.""" + if self._depth + 1 >= len(self._stack): + return self._context.environment. \ + undefined('there is no parent block called %r.' % + self.name, name='super') + return BlockReference(self.name, self._context, self._stack, + self._depth + 1) + + @internalcode + def __call__(self): + rv = concat(self._stack[self._depth](self._context)) + if self._context.eval_ctx.autoescape: + rv = Markup(rv) + return rv + + +class LoopContextBase(object): + """A loop context for dynamic iteration.""" + + _before = _first_iteration + _current = _first_iteration + _after = _last_iteration + _length = None + + def __init__(self, undefined, recurse=None, depth0=0): + self._undefined = undefined + self._recurse = recurse + self.index0 = -1 + self.depth0 = depth0 + self._last_checked_value = missing + + def cycle(self, *args): + """Cycles among the arguments with the current loop index.""" + if not args: + raise TypeError('no items for cycling given') + return args[self.index0 % len(args)] + + def changed(self, *value): + """Checks whether the value has changed since the last call.""" + if self._last_checked_value != value: + self._last_checked_value = value + return True + return False + + first = property(lambda x: x.index0 == 0) + last = property(lambda x: x._after is _last_iteration) + index = property(lambda x: x.index0 + 1) + revindex = property(lambda x: x.length - x.index0) + revindex0 = property(lambda x: x.length - x.index) + depth = property(lambda x: x.depth0 + 1) + + @property + def previtem(self): + if self._before is _first_iteration: + return self._undefined('there is no previous item') + return self._before + + @property + def nextitem(self): + if self._after is _last_iteration: + return self._undefined('there is no next item') + return self._after + + def __len__(self): + return self.length + + @internalcode + def loop(self, iterable): + if self._recurse is None: + raise TypeError('Tried to call non recursive loop. Maybe you ' + "forgot the 'recursive' modifier.") + return self._recurse(iterable, self._recurse, self.depth0 + 1) + + # a nifty trick to enhance the error message if someone tried to call + # the the loop without or with too many arguments. + __call__ = loop + del loop + + def __repr__(self): + return '<%s %r/%r>' % ( + self.__class__.__name__, + self.index, + self.length + ) + + +class LoopContext(LoopContextBase): + + def __init__(self, iterable, undefined, recurse=None, depth0=0): + LoopContextBase.__init__(self, undefined, recurse, depth0) + self._iterator = iter(iterable) + + # try to get the length of the iterable early. This must be done + # here because there are some broken iterators around where there + # __len__ is the number of iterations left (i'm looking at your + # listreverseiterator!). + try: + self._length = len(iterable) + except (TypeError, AttributeError): + self._length = None + self._after = self._safe_next() + + @property + def length(self): + if self._length is None: + # if was not possible to get the length of the iterator when + # the loop context was created (ie: iterating over a generator) + # we have to convert the iterable into a sequence and use the + # length of that + the number of iterations so far. + iterable = tuple(self._iterator) + self._iterator = iter(iterable) + iterations_done = self.index0 + 2 + self._length = len(iterable) + iterations_done + return self._length + + def __iter__(self): + return LoopContextIterator(self) + + def _safe_next(self): + try: + return next(self._iterator) + except StopIteration: + return _last_iteration + + +@implements_iterator +class LoopContextIterator(object): + """The iterator for a loop context.""" + __slots__ = ('context',) + + def __init__(self, context): + self.context = context + + def __iter__(self): + return self + + def __next__(self): + ctx = self.context + ctx.index0 += 1 + if ctx._after is _last_iteration: + raise StopIteration() + ctx._before = ctx._current + ctx._current = ctx._after + ctx._after = ctx._safe_next() + return ctx._current, ctx + + +class Macro(object): + """Wraps a macro function.""" + + def __init__(self, environment, func, name, arguments, + catch_kwargs, catch_varargs, caller, + default_autoescape=None): + self._environment = environment + self._func = func + self._argument_count = len(arguments) + self.name = name + self.arguments = arguments + self.catch_kwargs = catch_kwargs + self.catch_varargs = catch_varargs + self.caller = caller + self.explicit_caller = 'caller' in arguments + if default_autoescape is None: + default_autoescape = environment.autoescape + self._default_autoescape = default_autoescape + + @internalcode + @evalcontextfunction + def __call__(self, *args, **kwargs): + # This requires a bit of explanation, In the past we used to + # decide largely based on compile-time information if a macro is + # safe or unsafe. While there was a volatile mode it was largely + # unused for deciding on escaping. This turns out to be + # problemtic for macros because if a macro is safe or not not so + # much depends on the escape mode when it was defined but when it + # was used. + # + # Because however we export macros from the module system and + # there are historic callers that do not pass an eval context (and + # will continue to not pass one), we need to perform an instance + # check here. + # + # This is considered safe because an eval context is not a valid + # argument to callables otherwise anwyays. Worst case here is + # that if no eval context is passed we fall back to the compile + # time autoescape flag. + if args and isinstance(args[0], EvalContext): + autoescape = args[0].autoescape + args = args[1:] + else: + autoescape = self._default_autoescape + + # try to consume the positional arguments + arguments = list(args[:self._argument_count]) + off = len(arguments) + + # For information why this is necessary refer to the handling + # of caller in the `macro_body` handler in the compiler. + found_caller = False + + # if the number of arguments consumed is not the number of + # arguments expected we start filling in keyword arguments + # and defaults. + if off != self._argument_count: + for idx, name in enumerate(self.arguments[len(arguments):]): + try: + value = kwargs.pop(name) + except KeyError: + value = missing + if name == 'caller': + found_caller = True + arguments.append(value) + else: + found_caller = self.explicit_caller + + # it's important that the order of these arguments does not change + # if not also changed in the compiler's `function_scoping` method. + # the order is caller, keyword arguments, positional arguments! + if self.caller and not found_caller: + caller = kwargs.pop('caller', None) + if caller is None: + caller = self._environment.undefined('No caller defined', + name='caller') + arguments.append(caller) + + if self.catch_kwargs: + arguments.append(kwargs) + elif kwargs: + if 'caller' in kwargs: + raise TypeError('macro %r was invoked with two values for ' + 'the special caller argument. This is ' + 'most likely a bug.' % self.name) + raise TypeError('macro %r takes no keyword argument %r' % + (self.name, next(iter(kwargs)))) + if self.catch_varargs: + arguments.append(args[self._argument_count:]) + elif len(args) > self._argument_count: + raise TypeError('macro %r takes not more than %d argument(s)' % + (self.name, len(self.arguments))) + + return self._invoke(arguments, autoescape) + + def _invoke(self, arguments, autoescape): + """This method is being swapped out by the async implementation.""" + rv = self._func(*arguments) + if autoescape: + rv = Markup(rv) + return rv + + def __repr__(self): + return '<%s %s>' % ( + self.__class__.__name__, + self.name is None and 'anonymous' or repr(self.name) + ) + + +@implements_to_string +class Undefined(object): + """The default undefined type. This undefined type can be printed and + iterated over, but every other access will raise an :exc:`jinja2.exceptions.UndefinedError`: + + >>> foo = Undefined(name='foo') + >>> str(foo) + '' + >>> not foo + True + >>> foo + 42 + Traceback (most recent call last): + ... + jinja2.exceptions.UndefinedError: 'foo' is undefined + """ + __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name', + '_undefined_exception') + + def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError): + self._undefined_hint = hint + self._undefined_obj = obj + self._undefined_name = name + self._undefined_exception = exc + + @internalcode + def _fail_with_undefined_error(self, *args, **kwargs): + """Regular callback function for undefined objects that raises an + `jinja2.exceptions.UndefinedError` on call. + """ + if self._undefined_hint is None: + if self._undefined_obj is missing: + hint = '%r is undefined' % self._undefined_name + elif not isinstance(self._undefined_name, string_types): + hint = '%s has no element %r' % ( + object_type_repr(self._undefined_obj), + self._undefined_name + ) + else: + hint = '%r has no attribute %r' % ( + object_type_repr(self._undefined_obj), + self._undefined_name + ) + else: + hint = self._undefined_hint + raise self._undefined_exception(hint) + + @internalcode + def __getattr__(self, name): + if name[:2] == '__': + raise AttributeError(name) + return self._fail_with_undefined_error() + + __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \ + __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ + __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ + __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ + __float__ = __complex__ = __pow__ = __rpow__ = __sub__ = \ + __rsub__ = _fail_with_undefined_error + + def __eq__(self, other): + return type(self) is type(other) + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return id(type(self)) + + def __str__(self): + return u'' + + def __len__(self): + return 0 + + def __iter__(self): + if 0: + yield None + + def __nonzero__(self): + return False + __bool__ = __nonzero__ + + def __repr__(self): + return 'Undefined' + + +def make_logging_undefined(logger=None, base=None): + """Given a logger object this returns a new undefined class that will + log certain failures. It will log iterations and printing. If no + logger is given a default logger is created. + + Example:: + + logger = logging.getLogger(__name__) + LoggingUndefined = make_logging_undefined( + logger=logger, + base=Undefined + ) + + .. versionadded:: 2.8 + + :param logger: the logger to use. If not provided, a default logger + is created. + :param base: the base class to add logging functionality to. This + defaults to :class:`Undefined`. + """ + if logger is None: + import logging + logger = logging.getLogger(__name__) + logger.addHandler(logging.StreamHandler(sys.stderr)) + if base is None: + base = Undefined + + def _log_message(undef): + if undef._undefined_hint is None: + if undef._undefined_obj is missing: + hint = '%s is undefined' % undef._undefined_name + elif not isinstance(undef._undefined_name, string_types): + hint = '%s has no element %s' % ( + object_type_repr(undef._undefined_obj), + undef._undefined_name) + else: + hint = '%s has no attribute %s' % ( + object_type_repr(undef._undefined_obj), + undef._undefined_name) + else: + hint = undef._undefined_hint + logger.warning('Template variable warning: %s', hint) + + class LoggingUndefined(base): + + def _fail_with_undefined_error(self, *args, **kwargs): + try: + return base._fail_with_undefined_error(self, *args, **kwargs) + except self._undefined_exception as e: + logger.error('Template variable error: %s', str(e)) + raise e + + def __str__(self): + rv = base.__str__(self) + _log_message(self) + return rv + + def __iter__(self): + rv = base.__iter__(self) + _log_message(self) + return rv + + if PY2: + def __nonzero__(self): + rv = base.__nonzero__(self) + _log_message(self) + return rv + + def __unicode__(self): + rv = base.__unicode__(self) + _log_message(self) + return rv + else: + def __bool__(self): + rv = base.__bool__(self) + _log_message(self) + return rv + + return LoggingUndefined + + +@implements_to_string +class DebugUndefined(Undefined): + """An undefined that returns the debug info when printed. + + >>> foo = DebugUndefined(name='foo') + >>> str(foo) + '{{ foo }}' + >>> not foo + True + >>> foo + 42 + Traceback (most recent call last): + ... + jinja2.exceptions.UndefinedError: 'foo' is undefined + """ + __slots__ = () + + def __str__(self): + if self._undefined_hint is None: + if self._undefined_obj is missing: + return u'{{ %s }}' % self._undefined_name + return '{{ no such element: %s[%r] }}' % ( + object_type_repr(self._undefined_obj), + self._undefined_name + ) + return u'{{ undefined value printed: %s }}' % self._undefined_hint + + +@implements_to_string +class StrictUndefined(Undefined): + """An undefined that barks on print and iteration as well as boolean + tests and all kinds of comparisons. In other words: you can do nothing + with it except checking if it's defined using the `defined` test. + + >>> foo = StrictUndefined(name='foo') + >>> str(foo) + Traceback (most recent call last): + ... + jinja2.exceptions.UndefinedError: 'foo' is undefined + >>> not foo + Traceback (most recent call last): + ... + jinja2.exceptions.UndefinedError: 'foo' is undefined + >>> foo + 42 + Traceback (most recent call last): + ... + jinja2.exceptions.UndefinedError: 'foo' is undefined + """ + __slots__ = () + __iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \ + __ne__ = __bool__ = __hash__ = \ + Undefined._fail_with_undefined_error + + +# remove remaining slots attributes, after the metaclass did the magic they +# are unneeded and irritating as they contain wrong data for the subclasses. +del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__ diff --git a/venv/Lib/site-packages/jinja2/sandbox.py b/venv/Lib/site-packages/jinja2/sandbox.py new file mode 100644 index 0000000..93fb9d4 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/sandbox.py @@ -0,0 +1,475 @@ +# -*- coding: utf-8 -*- +""" + jinja2.sandbox + ~~~~~~~~~~~~~~ + + Adds a sandbox layer to Jinja as it was the default behavior in the old + Jinja 1 releases. This sandbox is slightly different from Jinja 1 as the + default behavior is easier to use. + + The behavior can be changed by subclassing the environment. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD. +""" +import types +import operator +from collections import Mapping +from jinja2.environment import Environment +from jinja2.exceptions import SecurityError +from jinja2._compat import string_types, PY2 +from jinja2.utils import Markup + +from markupsafe import EscapeFormatter +from string import Formatter + + +#: maximum number of items a range may produce +MAX_RANGE = 100000 + +#: attributes of function objects that are considered unsafe. +if PY2: + UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict', + 'func_defaults', 'func_globals']) +else: + # On versions > python 2 the special attributes on functions are gone, + # but they remain on methods and generators for whatever reason. + UNSAFE_FUNCTION_ATTRIBUTES = set() + + +#: unsafe method attributes. function attributes are unsafe for methods too +UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self']) + +#: unsafe generator attirbutes. +UNSAFE_GENERATOR_ATTRIBUTES = set(['gi_frame', 'gi_code']) + +#: unsafe attributes on coroutines +UNSAFE_COROUTINE_ATTRIBUTES = set(['cr_frame', 'cr_code']) + +#: unsafe attributes on async generators +UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = set(['ag_code', 'ag_frame']) + +import warnings + +# make sure we don't warn in python 2.6 about stuff we don't care about +warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning, + module='jinja2.sandbox') + +from collections import deque + +_mutable_set_types = (set,) +_mutable_mapping_types = (dict,) +_mutable_sequence_types = (list,) + + +# on python 2.x we can register the user collection types +try: + from UserDict import UserDict, DictMixin + from UserList import UserList + _mutable_mapping_types += (UserDict, DictMixin) + _mutable_set_types += (UserList,) +except ImportError: + pass + +# if sets is still available, register the mutable set from there as well +try: + from sets import Set + _mutable_set_types += (Set,) +except ImportError: + pass + +#: register Python 2.6 abstract base classes +from collections import MutableSet, MutableMapping, MutableSequence +_mutable_set_types += (MutableSet,) +_mutable_mapping_types += (MutableMapping,) +_mutable_sequence_types += (MutableSequence,) + + +_mutable_spec = ( + (_mutable_set_types, frozenset([ + 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove', + 'symmetric_difference_update', 'update' + ])), + (_mutable_mapping_types, frozenset([ + 'clear', 'pop', 'popitem', 'setdefault', 'update' + ])), + (_mutable_sequence_types, frozenset([ + 'append', 'reverse', 'insert', 'sort', 'extend', 'remove' + ])), + (deque, frozenset([ + 'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop', + 'popleft', 'remove', 'rotate' + ])) +) + + +class _MagicFormatMapping(Mapping): + """This class implements a dummy wrapper to fix a bug in the Python + standard library for string formatting. + + See https://bugs.python.org/issue13598 for information about why + this is necessary. + """ + + def __init__(self, args, kwargs): + self._args = args + self._kwargs = kwargs + self._last_index = 0 + + def __getitem__(self, key): + if key == '': + idx = self._last_index + self._last_index += 1 + try: + return self._args[idx] + except LookupError: + pass + key = str(idx) + return self._kwargs[key] + + def __iter__(self): + return iter(self._kwargs) + + def __len__(self): + return len(self._kwargs) + + +def inspect_format_method(callable): + if not isinstance(callable, (types.MethodType, + types.BuiltinMethodType)) or \ + callable.__name__ != 'format': + return None + obj = callable.__self__ + if isinstance(obj, string_types): + return obj + + +def safe_range(*args): + """A range that can't generate ranges with a length of more than + MAX_RANGE items. + """ + rng = range(*args) + if len(rng) > MAX_RANGE: + raise OverflowError('range too big, maximum size for range is %d' % + MAX_RANGE) + return rng + + +def unsafe(f): + """Marks a function or method as unsafe. + + :: + + @unsafe + def delete(self): + pass + """ + f.unsafe_callable = True + return f + + +def is_internal_attribute(obj, attr): + """Test if the attribute given is an internal python attribute. For + example this function returns `True` for the `func_code` attribute of + python objects. This is useful if the environment method + :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden. + + >>> from jinja2.sandbox import is_internal_attribute + >>> is_internal_attribute(str, "mro") + True + >>> is_internal_attribute(str, "upper") + False + """ + if isinstance(obj, types.FunctionType): + if attr in UNSAFE_FUNCTION_ATTRIBUTES: + return True + elif isinstance(obj, types.MethodType): + if attr in UNSAFE_FUNCTION_ATTRIBUTES or \ + attr in UNSAFE_METHOD_ATTRIBUTES: + return True + elif isinstance(obj, type): + if attr == 'mro': + return True + elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)): + return True + elif isinstance(obj, types.GeneratorType): + if attr in UNSAFE_GENERATOR_ATTRIBUTES: + return True + elif hasattr(types, 'CoroutineType') and isinstance(obj, types.CoroutineType): + if attr in UNSAFE_COROUTINE_ATTRIBUTES: + return True + elif hasattr(types, 'AsyncGeneratorType') and isinstance(obj, types.AsyncGeneratorType): + if attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES: + return True + return attr.startswith('__') + + +def modifies_known_mutable(obj, attr): + """This function checks if an attribute on a builtin mutable object + (list, dict, set or deque) would modify it if called. It also supports + the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and + with Python 2.6 onwards the abstract base classes `MutableSet`, + `MutableMapping`, and `MutableSequence`. + + >>> modifies_known_mutable({}, "clear") + True + >>> modifies_known_mutable({}, "keys") + False + >>> modifies_known_mutable([], "append") + True + >>> modifies_known_mutable([], "index") + False + + If called with an unsupported object (such as unicode) `False` is + returned. + + >>> modifies_known_mutable("foo", "upper") + False + """ + for typespec, unsafe in _mutable_spec: + if isinstance(obj, typespec): + return attr in unsafe + return False + + +class SandboxedEnvironment(Environment): + """The sandboxed environment. It works like the regular environment but + tells the compiler to generate sandboxed code. Additionally subclasses of + this environment may override the methods that tell the runtime what + attributes or functions are safe to access. + + If the template tries to access insecure code a :exc:`SecurityError` is + raised. However also other exceptions may occur during the rendering so + the caller has to ensure that all exceptions are caught. + """ + sandboxed = True + + #: default callback table for the binary operators. A copy of this is + #: available on each instance of a sandboxed environment as + #: :attr:`binop_table` + default_binop_table = { + '+': operator.add, + '-': operator.sub, + '*': operator.mul, + '/': operator.truediv, + '//': operator.floordiv, + '**': operator.pow, + '%': operator.mod + } + + #: default callback table for the unary operators. A copy of this is + #: available on each instance of a sandboxed environment as + #: :attr:`unop_table` + default_unop_table = { + '+': operator.pos, + '-': operator.neg + } + + #: a set of binary operators that should be intercepted. Each operator + #: that is added to this set (empty by default) is delegated to the + #: :meth:`call_binop` method that will perform the operator. The default + #: operator callback is specified by :attr:`binop_table`. + #: + #: The following binary operators are interceptable: + #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**`` + #: + #: The default operation form the operator table corresponds to the + #: builtin function. Intercepted calls are always slower than the native + #: operator call, so make sure only to intercept the ones you are + #: interested in. + #: + #: .. versionadded:: 2.6 + intercepted_binops = frozenset() + + #: a set of unary operators that should be intercepted. Each operator + #: that is added to this set (empty by default) is delegated to the + #: :meth:`call_unop` method that will perform the operator. The default + #: operator callback is specified by :attr:`unop_table`. + #: + #: The following unary operators are interceptable: ``+``, ``-`` + #: + #: The default operation form the operator table corresponds to the + #: builtin function. Intercepted calls are always slower than the native + #: operator call, so make sure only to intercept the ones you are + #: interested in. + #: + #: .. versionadded:: 2.6 + intercepted_unops = frozenset() + + def intercept_unop(self, operator): + """Called during template compilation with the name of a unary + operator to check if it should be intercepted at runtime. If this + method returns `True`, :meth:`call_unop` is excuted for this unary + operator. The default implementation of :meth:`call_unop` will use + the :attr:`unop_table` dictionary to perform the operator with the + same logic as the builtin one. + + The following unary operators are interceptable: ``+`` and ``-`` + + Intercepted calls are always slower than the native operator call, + so make sure only to intercept the ones you are interested in. + + .. versionadded:: 2.6 + """ + return False + + + def __init__(self, *args, **kwargs): + Environment.__init__(self, *args, **kwargs) + self.globals['range'] = safe_range + self.binop_table = self.default_binop_table.copy() + self.unop_table = self.default_unop_table.copy() + + def is_safe_attribute(self, obj, attr, value): + """The sandboxed environment will call this method to check if the + attribute of an object is safe to access. Per default all attributes + starting with an underscore are considered private as well as the + special attributes of internal python objects as returned by the + :func:`is_internal_attribute` function. + """ + return not (attr.startswith('_') or is_internal_attribute(obj, attr)) + + def is_safe_callable(self, obj): + """Check if an object is safely callable. Per default a function is + considered safe unless the `unsafe_callable` attribute exists and is + True. Override this method to alter the behavior, but this won't + affect the `unsafe` decorator from this module. + """ + return not (getattr(obj, 'unsafe_callable', False) or + getattr(obj, 'alters_data', False)) + + def call_binop(self, context, operator, left, right): + """For intercepted binary operator calls (:meth:`intercepted_binops`) + this function is executed instead of the builtin operator. This can + be used to fine tune the behavior of certain operators. + + .. versionadded:: 2.6 + """ + return self.binop_table[operator](left, right) + + def call_unop(self, context, operator, arg): + """For intercepted unary operator calls (:meth:`intercepted_unops`) + this function is executed instead of the builtin operator. This can + be used to fine tune the behavior of certain operators. + + .. versionadded:: 2.6 + """ + return self.unop_table[operator](arg) + + def getitem(self, obj, argument): + """Subscribe an object from sandboxed code.""" + try: + return obj[argument] + except (TypeError, LookupError): + if isinstance(argument, string_types): + try: + attr = str(argument) + except Exception: + pass + else: + try: + value = getattr(obj, attr) + except AttributeError: + pass + else: + if self.is_safe_attribute(obj, argument, value): + return value + return self.unsafe_undefined(obj, argument) + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj, attribute): + """Subscribe an object from sandboxed code and prefer the + attribute. The attribute passed *must* be a bytestring. + """ + try: + value = getattr(obj, attribute) + except AttributeError: + try: + return obj[attribute] + except (TypeError, LookupError): + pass + else: + if self.is_safe_attribute(obj, attribute, value): + return value + return self.unsafe_undefined(obj, attribute) + return self.undefined(obj=obj, name=attribute) + + def unsafe_undefined(self, obj, attribute): + """Return an undefined object for unsafe attributes.""" + return self.undefined('access to attribute %r of %r ' + 'object is unsafe.' % ( + attribute, + obj.__class__.__name__ + ), name=attribute, obj=obj, exc=SecurityError) + + def format_string(self, s, args, kwargs): + """If a format call is detected, then this is routed through this + method so that our safety sandbox can be used for it. + """ + if isinstance(s, Markup): + formatter = SandboxedEscapeFormatter(self, s.escape) + else: + formatter = SandboxedFormatter(self) + kwargs = _MagicFormatMapping(args, kwargs) + rv = formatter.vformat(s, args, kwargs) + return type(s)(rv) + + def call(__self, __context, __obj, *args, **kwargs): + """Call an object from sandboxed code.""" + fmt = inspect_format_method(__obj) + if fmt is not None: + return __self.format_string(fmt, args, kwargs) + + # the double prefixes are to avoid double keyword argument + # errors when proxying the call. + if not __self.is_safe_callable(__obj): + raise SecurityError('%r is not safely callable' % (__obj,)) + return __context.call(__obj, *args, **kwargs) + + +class ImmutableSandboxedEnvironment(SandboxedEnvironment): + """Works exactly like the regular `SandboxedEnvironment` but does not + permit modifications on the builtin mutable objects `list`, `set`, and + `dict` by using the :func:`modifies_known_mutable` function. + """ + + def is_safe_attribute(self, obj, attr, value): + if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value): + return False + return not modifies_known_mutable(obj, attr) + + +# This really is not a public API apparenlty. +try: + from _string import formatter_field_name_split +except ImportError: + def formatter_field_name_split(field_name): + return field_name._formatter_field_name_split() + + +class SandboxedFormatterMixin(object): + + def __init__(self, env): + self._env = env + + def get_field(self, field_name, args, kwargs): + first, rest = formatter_field_name_split(field_name) + obj = self.get_value(first, args, kwargs) + for is_attr, i in rest: + if is_attr: + obj = self._env.getattr(obj, i) + else: + obj = self._env.getitem(obj, i) + return obj, first + +class SandboxedFormatter(SandboxedFormatterMixin, Formatter): + + def __init__(self, env): + SandboxedFormatterMixin.__init__(self, env) + Formatter.__init__(self) + +class SandboxedEscapeFormatter(SandboxedFormatterMixin, EscapeFormatter): + + def __init__(self, env, escape): + SandboxedFormatterMixin.__init__(self, env) + EscapeFormatter.__init__(self, escape) diff --git a/venv/Lib/site-packages/jinja2/tests.py b/venv/Lib/site-packages/jinja2/tests.py new file mode 100644 index 0000000..0adc3d4 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/tests.py @@ -0,0 +1,175 @@ +# -*- coding: utf-8 -*- +""" + jinja2.tests + ~~~~~~~~~~~~ + + Jinja test functions. Used with the "is" operator. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import operator +import re +from collections import Mapping +from jinja2.runtime import Undefined +from jinja2._compat import text_type, string_types, integer_types +import decimal + +number_re = re.compile(r'^-?\d+(\.\d+)?$') +regex_type = type(number_re) + + +test_callable = callable + + +def test_odd(value): + """Return true if the variable is odd.""" + return value % 2 == 1 + + +def test_even(value): + """Return true if the variable is even.""" + return value % 2 == 0 + + +def test_divisibleby(value, num): + """Check if a variable is divisible by a number.""" + return value % num == 0 + + +def test_defined(value): + """Return true if the variable is defined: + + .. sourcecode:: jinja + + {% if variable is defined %} + value of variable: {{ variable }} + {% else %} + variable is not defined + {% endif %} + + See the :func:`default` filter for a simple way to set undefined + variables. + """ + return not isinstance(value, Undefined) + + +def test_undefined(value): + """Like :func:`defined` but the other way round.""" + return isinstance(value, Undefined) + + +def test_none(value): + """Return true if the variable is none.""" + return value is None + + +def test_lower(value): + """Return true if the variable is lowercased.""" + return text_type(value).islower() + + +def test_upper(value): + """Return true if the variable is uppercased.""" + return text_type(value).isupper() + + +def test_string(value): + """Return true if the object is a string.""" + return isinstance(value, string_types) + + +def test_mapping(value): + """Return true if the object is a mapping (dict etc.). + + .. versionadded:: 2.6 + """ + return isinstance(value, Mapping) + + +def test_number(value): + """Return true if the variable is a number.""" + return isinstance(value, integer_types + (float, complex, decimal.Decimal)) + + +def test_sequence(value): + """Return true if the variable is a sequence. Sequences are variables + that are iterable. + """ + try: + len(value) + value.__getitem__ + except: + return False + return True + + +def test_sameas(value, other): + """Check if an object points to the same memory address than another + object: + + .. sourcecode:: jinja + + {% if foo.attribute is sameas false %} + the foo attribute really is the `False` singleton + {% endif %} + """ + return value is other + + +def test_iterable(value): + """Check if it's possible to iterate over an object.""" + try: + iter(value) + except TypeError: + return False + return True + + +def test_escaped(value): + """Check if the value is escaped.""" + return hasattr(value, '__html__') + + +def test_in(value, seq): + """Check if value is in seq. + + .. versionadded:: 2.10 + """ + return value in seq + + +TESTS = { + 'odd': test_odd, + 'even': test_even, + 'divisibleby': test_divisibleby, + 'defined': test_defined, + 'undefined': test_undefined, + 'none': test_none, + 'lower': test_lower, + 'upper': test_upper, + 'string': test_string, + 'mapping': test_mapping, + 'number': test_number, + 'sequence': test_sequence, + 'iterable': test_iterable, + 'callable': test_callable, + 'sameas': test_sameas, + 'escaped': test_escaped, + 'in': test_in, + '==': operator.eq, + 'eq': operator.eq, + 'equalto': operator.eq, + '!=': operator.ne, + 'ne': operator.ne, + '>': operator.gt, + 'gt': operator.gt, + 'greaterthan': operator.gt, + 'ge': operator.ge, + '>=': operator.ge, + '<': operator.lt, + 'lt': operator.lt, + 'lessthan': operator.lt, + '<=': operator.le, + 'le': operator.le, +} diff --git a/venv/Lib/site-packages/jinja2/utils.py b/venv/Lib/site-packages/jinja2/utils.py new file mode 100644 index 0000000..502a311 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/utils.py @@ -0,0 +1,647 @@ +# -*- coding: utf-8 -*- +""" + jinja2.utils + ~~~~~~~~~~~~ + + Utility functions. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import re +import json +import errno +from collections import deque +from threading import Lock +from jinja2._compat import text_type, string_types, implements_iterator, \ + url_quote + + +_word_split_re = re.compile(r'(\s+)') +_punctuation_re = re.compile( + '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % ( + '|'.join(map(re.escape, ('(', '<', '<'))), + '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '>'))) + ) +) +_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$') +_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)') +_entity_re = re.compile(r'&([^;]+);') +_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' +_digits = '0123456789' + +# special singleton representing missing values for the runtime +missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})() + +# internal code +internal_code = set() + +concat = u''.join + +_slash_escape = '\\/' not in json.dumps('/') + + +def contextfunction(f): + """This decorator can be used to mark a function or method context callable. + A context callable is passed the active :class:`Context` as first argument when + called from the template. This is useful if a function wants to get access + to the context or functions provided on the context object. For example + a function that returns a sorted list of template variables the current + template exports could look like this:: + + @contextfunction + def get_exported_names(context): + return sorted(context.exported_vars) + """ + f.contextfunction = True + return f + + +def evalcontextfunction(f): + """This decorator can be used to mark a function or method as an eval + context callable. This is similar to the :func:`contextfunction` + but instead of passing the context, an evaluation context object is + passed. For more information about the eval context, see + :ref:`eval-context`. + + .. versionadded:: 2.4 + """ + f.evalcontextfunction = True + return f + + +def environmentfunction(f): + """This decorator can be used to mark a function or method as environment + callable. This decorator works exactly like the :func:`contextfunction` + decorator just that the first argument is the active :class:`Environment` + and not context. + """ + f.environmentfunction = True + return f + + +def internalcode(f): + """Marks the function as internally used""" + internal_code.add(f.__code__) + return f + + +def is_undefined(obj): + """Check if the object passed is undefined. This does nothing more than + performing an instance check against :class:`Undefined` but looks nicer. + This can be used for custom filters or tests that want to react to + undefined variables. For example a custom default filter can look like + this:: + + def default(var, default=''): + if is_undefined(var): + return default + return var + """ + from jinja2.runtime import Undefined + return isinstance(obj, Undefined) + + +def consume(iterable): + """Consumes an iterable without doing anything with it.""" + for event in iterable: + pass + + +def clear_caches(): + """Jinja2 keeps internal caches for environments and lexers. These are + used so that Jinja2 doesn't have to recreate environments and lexers all + the time. Normally you don't have to care about that but if you are + measuring memory consumption you may want to clean the caches. + """ + from jinja2.environment import _spontaneous_environments + from jinja2.lexer import _lexer_cache + _spontaneous_environments.clear() + _lexer_cache.clear() + + +def import_string(import_name, silent=False): + """Imports an object based on a string. This is useful if you want to + use import paths as endpoints or something similar. An import path can + be specified either in dotted notation (``xml.sax.saxutils.escape``) + or with a colon as object delimiter (``xml.sax.saxutils:escape``). + + If the `silent` is True the return value will be `None` if the import + fails. + + :return: imported object + """ + try: + if ':' in import_name: + module, obj = import_name.split(':', 1) + elif '.' in import_name: + items = import_name.split('.') + module = '.'.join(items[:-1]) + obj = items[-1] + else: + return __import__(import_name) + return getattr(__import__(module, None, None, [obj]), obj) + except (ImportError, AttributeError): + if not silent: + raise + + +def open_if_exists(filename, mode='rb'): + """Returns a file descriptor for the filename if that file exists, + otherwise `None`. + """ + try: + return open(filename, mode) + except IOError as e: + if e.errno not in (errno.ENOENT, errno.EISDIR, errno.EINVAL): + raise + + +def object_type_repr(obj): + """Returns the name of the object's type. For some recognized + singletons the name of the object is returned instead. (For + example for `None` and `Ellipsis`). + """ + if obj is None: + return 'None' + elif obj is Ellipsis: + return 'Ellipsis' + # __builtin__ in 2.x, builtins in 3.x + if obj.__class__.__module__ in ('__builtin__', 'builtins'): + name = obj.__class__.__name__ + else: + name = obj.__class__.__module__ + '.' + obj.__class__.__name__ + return '%s object' % name + + +def pformat(obj, verbose=False): + """Prettyprint an object. Either use the `pretty` library or the + builtin `pprint`. + """ + try: + from pretty import pretty + return pretty(obj, verbose=verbose) + except ImportError: + from pprint import pformat + return pformat(obj) + + +def urlize(text, trim_url_limit=None, rel=None, target=None): + """Converts any URLs in text into clickable links. Works on http://, + https:// and www. links. Links can have trailing punctuation (periods, + commas, close-parens) and leading punctuation (opening parens) and + it'll still do the right thing. + + If trim_url_limit is not None, the URLs in link text will be limited + to trim_url_limit characters. + + If nofollow is True, the URLs in link text will get a rel="nofollow" + attribute. + + If target is not None, a target attribute will be added to the link. + """ + trim_url = lambda x, limit=trim_url_limit: limit is not None \ + and (x[:limit] + (len(x) >=limit and '...' + or '')) or x + words = _word_split_re.split(text_type(escape(text))) + rel_attr = rel and ' rel="%s"' % text_type(escape(rel)) or '' + target_attr = target and ' target="%s"' % escape(target) or '' + + for i, word in enumerate(words): + match = _punctuation_re.match(word) + if match: + lead, middle, trail = match.groups() + if middle.startswith('www.') or ( + '@' not in middle and + not middle.startswith('http://') and + not middle.startswith('https://') and + len(middle) > 0 and + middle[0] in _letters + _digits and ( + middle.endswith('.org') or + middle.endswith('.net') or + middle.endswith('.com') + )): + middle = '<a href="http://%s"%s%s>%s</a>' % (middle, + rel_attr, target_attr, trim_url(middle)) + if middle.startswith('http://') or \ + middle.startswith('https://'): + middle = '<a href="%s"%s%s>%s</a>' % (middle, + rel_attr, target_attr, trim_url(middle)) + if '@' in middle and not middle.startswith('www.') and \ + not ':' in middle and _simple_email_re.match(middle): + middle = '<a href="mailto:%s">%s</a>' % (middle, middle) + if lead + middle + trail != word: + words[i] = lead + middle + trail + return u''.join(words) + + +def generate_lorem_ipsum(n=5, html=True, min=20, max=100): + """Generate some lorem ipsum for the template.""" + from jinja2.constants import LOREM_IPSUM_WORDS + from random import choice, randrange + words = LOREM_IPSUM_WORDS.split() + result = [] + + for _ in range(n): + next_capitalized = True + last_comma = last_fullstop = 0 + word = None + last = None + p = [] + + # each paragraph contains out of 20 to 100 words. + for idx, _ in enumerate(range(randrange(min, max))): + while True: + word = choice(words) + if word != last: + last = word + break + if next_capitalized: + word = word.capitalize() + next_capitalized = False + # add commas + if idx - randrange(3, 8) > last_comma: + last_comma = idx + last_fullstop += 2 + word += ',' + # add end of sentences + if idx - randrange(10, 20) > last_fullstop: + last_comma = last_fullstop = idx + word += '.' + next_capitalized = True + p.append(word) + + # ensure that the paragraph ends with a dot. + p = u' '.join(p) + if p.endswith(','): + p = p[:-1] + '.' + elif not p.endswith('.'): + p += '.' + result.append(p) + + if not html: + return u'\n\n'.join(result) + return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result)) + + +def unicode_urlencode(obj, charset='utf-8', for_qs=False): + """URL escapes a single bytestring or unicode string with the + given charset if applicable to URL safe quoting under all rules + that need to be considered under all supported Python versions. + + If non strings are provided they are converted to their unicode + representation first. + """ + if not isinstance(obj, string_types): + obj = text_type(obj) + if isinstance(obj, text_type): + obj = obj.encode(charset) + safe = not for_qs and b'/' or b'' + rv = text_type(url_quote(obj, safe)) + if for_qs: + rv = rv.replace('%20', '+') + return rv + + +class LRUCache(object): + """A simple LRU Cache implementation.""" + + # this is fast for small capacities (something below 1000) but doesn't + # scale. But as long as it's only used as storage for templates this + # won't do any harm. + + def __init__(self, capacity): + self.capacity = capacity + self._mapping = {} + self._queue = deque() + self._postinit() + + def _postinit(self): + # alias all queue methods for faster lookup + self._popleft = self._queue.popleft + self._pop = self._queue.pop + self._remove = self._queue.remove + self._wlock = Lock() + self._append = self._queue.append + + def __getstate__(self): + return { + 'capacity': self.capacity, + '_mapping': self._mapping, + '_queue': self._queue + } + + def __setstate__(self, d): + self.__dict__.update(d) + self._postinit() + + def __getnewargs__(self): + return (self.capacity,) + + def copy(self): + """Return a shallow copy of the instance.""" + rv = self.__class__(self.capacity) + rv._mapping.update(self._mapping) + rv._queue = deque(self._queue) + return rv + + def get(self, key, default=None): + """Return an item from the cache dict or `default`""" + try: + return self[key] + except KeyError: + return default + + def setdefault(self, key, default=None): + """Set `default` if the key is not in the cache otherwise + leave unchanged. Return the value of this key. + """ + self._wlock.acquire() + try: + try: + return self[key] + except KeyError: + self[key] = default + return default + finally: + self._wlock.release() + + def clear(self): + """Clear the cache.""" + self._wlock.acquire() + try: + self._mapping.clear() + self._queue.clear() + finally: + self._wlock.release() + + def __contains__(self, key): + """Check if a key exists in this cache.""" + return key in self._mapping + + def __len__(self): + """Return the current size of the cache.""" + return len(self._mapping) + + def __repr__(self): + return '<%s %r>' % ( + self.__class__.__name__, + self._mapping + ) + + def __getitem__(self, key): + """Get an item from the cache. Moves the item up so that it has the + highest priority then. + + Raise a `KeyError` if it does not exist. + """ + self._wlock.acquire() + try: + rv = self._mapping[key] + if self._queue[-1] != key: + try: + self._remove(key) + except ValueError: + # if something removed the key from the container + # when we read, ignore the ValueError that we would + # get otherwise. + pass + self._append(key) + return rv + finally: + self._wlock.release() + + def __setitem__(self, key, value): + """Sets the value for an item. Moves the item up so that it + has the highest priority then. + """ + self._wlock.acquire() + try: + if key in self._mapping: + self._remove(key) + elif len(self._mapping) == self.capacity: + del self._mapping[self._popleft()] + self._append(key) + self._mapping[key] = value + finally: + self._wlock.release() + + def __delitem__(self, key): + """Remove an item from the cache dict. + Raise a `KeyError` if it does not exist. + """ + self._wlock.acquire() + try: + del self._mapping[key] + try: + self._remove(key) + except ValueError: + # __getitem__ is not locked, it might happen + pass + finally: + self._wlock.release() + + def items(self): + """Return a list of items.""" + result = [(key, self._mapping[key]) for key in list(self._queue)] + result.reverse() + return result + + def iteritems(self): + """Iterate over all items.""" + return iter(self.items()) + + def values(self): + """Return a list of all values.""" + return [x[1] for x in self.items()] + + def itervalue(self): + """Iterate over all values.""" + return iter(self.values()) + + def keys(self): + """Return a list of all keys ordered by most recent usage.""" + return list(self) + + def iterkeys(self): + """Iterate over all keys in the cache dict, ordered by + the most recent usage. + """ + return reversed(tuple(self._queue)) + + __iter__ = iterkeys + + def __reversed__(self): + """Iterate over the values in the cache dict, oldest items + coming first. + """ + return iter(tuple(self._queue)) + + __copy__ = copy + + +# register the LRU cache as mutable mapping if possible +try: + from collections import MutableMapping + MutableMapping.register(LRUCache) +except ImportError: + pass + + +def select_autoescape(enabled_extensions=('html', 'htm', 'xml'), + disabled_extensions=(), + default_for_string=True, + default=False): + """Intelligently sets the initial value of autoescaping based on the + filename of the template. This is the recommended way to configure + autoescaping if you do not want to write a custom function yourself. + + If you want to enable it for all templates created from strings or + for all templates with `.html` and `.xml` extensions:: + + from jinja2 import Environment, select_autoescape + env = Environment(autoescape=select_autoescape( + enabled_extensions=('html', 'xml'), + default_for_string=True, + )) + + Example configuration to turn it on at all times except if the template + ends with `.txt`:: + + from jinja2 import Environment, select_autoescape + env = Environment(autoescape=select_autoescape( + disabled_extensions=('txt',), + default_for_string=True, + default=True, + )) + + The `enabled_extensions` is an iterable of all the extensions that + autoescaping should be enabled for. Likewise `disabled_extensions` is + a list of all templates it should be disabled for. If a template is + loaded from a string then the default from `default_for_string` is used. + If nothing matches then the initial value of autoescaping is set to the + value of `default`. + + For security reasons this function operates case insensitive. + + .. versionadded:: 2.9 + """ + enabled_patterns = tuple('.' + x.lstrip('.').lower() + for x in enabled_extensions) + disabled_patterns = tuple('.' + x.lstrip('.').lower() + for x in disabled_extensions) + def autoescape(template_name): + if template_name is None: + return default_for_string + template_name = template_name.lower() + if template_name.endswith(enabled_patterns): + return True + if template_name.endswith(disabled_patterns): + return False + return default + return autoescape + + +def htmlsafe_json_dumps(obj, dumper=None, **kwargs): + """Works exactly like :func:`dumps` but is safe for use in ``<script>`` + tags. It accepts the same arguments and returns a JSON string. Note that + this is available in templates through the ``|tojson`` filter which will + also mark the result as safe. Due to how this function escapes certain + characters this is safe even if used outside of ``<script>`` tags. + + The following characters are escaped in strings: + + - ``<`` + - ``>`` + - ``&`` + - ``'`` + + This makes it safe to embed such strings in any place in HTML with the + notable exception of double quoted attributes. In that case single + quote your attributes or HTML escape it in addition. + """ + if dumper is None: + dumper = json.dumps + rv = dumper(obj, **kwargs) \ + .replace(u'<', u'\\u003c') \ + .replace(u'>', u'\\u003e') \ + .replace(u'&', u'\\u0026') \ + .replace(u"'", u'\\u0027') + return Markup(rv) + + +@implements_iterator +class Cycler(object): + """A cycle helper for templates.""" + + def __init__(self, *items): + if not items: + raise RuntimeError('at least one item has to be provided') + self.items = items + self.reset() + + def reset(self): + """Resets the cycle.""" + self.pos = 0 + + @property + def current(self): + """Returns the current item.""" + return self.items[self.pos] + + def next(self): + """Goes one item ahead and returns it.""" + rv = self.current + self.pos = (self.pos + 1) % len(self.items) + return rv + + __next__ = next + + +class Joiner(object): + """A joining helper for templates.""" + + def __init__(self, sep=u', '): + self.sep = sep + self.used = False + + def __call__(self): + if not self.used: + self.used = True + return u'' + return self.sep + + +class Namespace(object): + """A namespace object that can hold arbitrary attributes. It may be + initialized from a dictionary or with keyword argments.""" + + def __init__(*args, **kwargs): + self, args = args[0], args[1:] + self.__attrs = dict(*args, **kwargs) + + def __getattribute__(self, name): + if name == '_Namespace__attrs': + return object.__getattribute__(self, name) + try: + return self.__attrs[name] + except KeyError: + raise AttributeError(name) + + def __setitem__(self, name, value): + self.__attrs[name] = value + + def __repr__(self): + return '<Namespace %r>' % self.__attrs + + +# does this python version support async for in and async generators? +try: + exec('async def _():\n async for _ in ():\n yield _') + have_async_gen = True +except SyntaxError: + have_async_gen = False + + +# Imported here because that's where it was in the past +from markupsafe import Markup, escape, soft_unicode diff --git a/venv/Lib/site-packages/jinja2/visitor.py b/venv/Lib/site-packages/jinja2/visitor.py new file mode 100644 index 0000000..ba526df --- /dev/null +++ b/venv/Lib/site-packages/jinja2/visitor.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +""" + jinja2.visitor + ~~~~~~~~~~~~~~ + + This module implements a visitor for the nodes. + + :copyright: (c) 2017 by the Jinja Team. + :license: BSD. +""" +from jinja2.nodes import Node + + +class NodeVisitor(object): + """Walks the abstract syntax tree and call visitor functions for every + node found. The visitor functions may return values which will be + forwarded by the `visit` method. + + Per default the visitor functions for the nodes are ``'visit_'`` + + class name of the node. So a `TryFinally` node visit function would + be `visit_TryFinally`. This behavior can be changed by overriding + the `get_visitor` function. If no visitor function exists for a node + (return value `None`) the `generic_visit` visitor is used instead. + """ + + def get_visitor(self, node): + """Return the visitor function for this node or `None` if no visitor + exists for this node. In that case the generic visit function is + used instead. + """ + method = 'visit_' + node.__class__.__name__ + return getattr(self, method, None) + + def visit(self, node, *args, **kwargs): + """Visit a node.""" + f = self.get_visitor(node) + if f is not None: + return f(node, *args, **kwargs) + return self.generic_visit(node, *args, **kwargs) + + def generic_visit(self, node, *args, **kwargs): + """Called if no explicit visitor function exists for a node.""" + for node in node.iter_child_nodes(): + self.visit(node, *args, **kwargs) + + +class NodeTransformer(NodeVisitor): + """Walks the abstract syntax tree and allows modifications of nodes. + + The `NodeTransformer` will walk the AST and use the return value of the + visitor functions to replace or remove the old node. If the return + value of the visitor function is `None` the node will be removed + from the previous location otherwise it's replaced with the return + value. The return value may be the original node in which case no + replacement takes place. + """ + + def generic_visit(self, node, *args, **kwargs): + for field, old_value in node.iter_fields(): + if isinstance(old_value, list): + new_values = [] + for value in old_value: + if isinstance(value, Node): + value = self.visit(value, *args, **kwargs) + if value is None: + continue + elif not isinstance(value, Node): + new_values.extend(value) + continue + new_values.append(value) + old_value[:] = new_values + elif isinstance(old_value, Node): + new_node = self.visit(old_value, *args, **kwargs) + if new_node is None: + delattr(node, field) + else: + setattr(node, field, new_node) + return node + + def visit_list(self, node, *args, **kwargs): + """As transformers may return lists in some places this method + can be used to enforce a list as return value. + """ + rv = self.visit(node, *args, **kwargs) + if not isinstance(rv, list): + rv = [rv] + return rv diff --git a/venv/Lib/site-packages/markupsafe/__init__.py b/venv/Lib/site-packages/markupsafe/__init__.py new file mode 100644 index 0000000..5e09525 --- /dev/null +++ b/venv/Lib/site-packages/markupsafe/__init__.py @@ -0,0 +1,327 @@ +# -*- coding: utf-8 -*- +""" +markupsafe +~~~~~~~~~~ + +Implements an escape function and a Markup string to replace HTML +special characters with safe representations. + +:copyright: © 2010 by the Pallets team. +:license: BSD, see LICENSE for more details. +""" +import re +import string + +from ._compat import int_types +from ._compat import iteritems +from ._compat import Mapping +from ._compat import PY2 +from ._compat import string_types +from ._compat import text_type +from ._compat import unichr + +__version__ = "1.1.0" + +__all__ = ["Markup", "soft_unicode", "escape", "escape_silent"] + +_striptags_re = re.compile(r"(<!--.*?-->|<[^>]*>)") +_entity_re = re.compile(r"&([^& ;]+);") + + +class Markup(text_type): + """A string that is ready to be safely inserted into an HTML or XML + document, either because it was escaped or because it was marked + safe. + + Passing an object to the constructor converts it to text and wraps + it to mark it safe without escaping. To escape the text, use the + :meth:`escape` class method instead. + + >>> Markup('Hello, <em>World</em>!') + Markup('Hello, <em>World</em>!') + >>> Markup(42) + Markup('42') + >>> Markup.escape('Hello, <em>World</em>!') + Markup('Hello <em>World</em>!') + + This implements the ``__html__()`` interface that some frameworks + use. Passing an object that implements ``__html__()`` will wrap the + output of that method, marking it safe. + + >>> class Foo: + ... def __html__(self): + ... return '<a href="/foo">foo</a>' + ... + >>> Markup(Foo()) + Markup('<a href="/foo">foo</a>') + + This is a subclass of the text type (``str`` in Python 3, + ``unicode`` in Python 2). It has the same methods as that type, but + all methods escape their arguments and return a ``Markup`` instance. + + >>> Markup('<em>%s</em>') % 'foo & bar' + Markup('<em>foo & bar</em>') + >>> Markup('<em>Hello</em> ') + '<foo>' + Markup('<em>Hello</em> <foo>') + """ + + __slots__ = () + + def __new__(cls, base=u"", encoding=None, errors="strict"): + if hasattr(base, "__html__"): + base = base.__html__() + if encoding is None: + return text_type.__new__(cls, base) + return text_type.__new__(cls, base, encoding, errors) + + def __html__(self): + return self + + def __add__(self, other): + if isinstance(other, string_types) or hasattr(other, "__html__"): + return self.__class__(super(Markup, self).__add__(self.escape(other))) + return NotImplemented + + def __radd__(self, other): + if hasattr(other, "__html__") or isinstance(other, string_types): + return self.escape(other).__add__(self) + return NotImplemented + + def __mul__(self, num): + if isinstance(num, int_types): + return self.__class__(text_type.__mul__(self, num)) + return NotImplemented + + __rmul__ = __mul__ + + def __mod__(self, arg): + if isinstance(arg, tuple): + arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg) + else: + arg = _MarkupEscapeHelper(arg, self.escape) + return self.__class__(text_type.__mod__(self, arg)) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, text_type.__repr__(self)) + + def join(self, seq): + return self.__class__(text_type.join(self, map(self.escape, seq))) + + join.__doc__ = text_type.join.__doc__ + + def split(self, *args, **kwargs): + return list(map(self.__class__, text_type.split(self, *args, **kwargs))) + + split.__doc__ = text_type.split.__doc__ + + def rsplit(self, *args, **kwargs): + return list(map(self.__class__, text_type.rsplit(self, *args, **kwargs))) + + rsplit.__doc__ = text_type.rsplit.__doc__ + + def splitlines(self, *args, **kwargs): + return list(map(self.__class__, text_type.splitlines(self, *args, **kwargs))) + + splitlines.__doc__ = text_type.splitlines.__doc__ + + def unescape(self): + """Convert escaped markup back into a text string. This replaces + HTML entities with the characters they represent. + + >>> Markup('Main » <em>About</em>').unescape() + 'Main » <em>About</em>' + """ + from ._constants import HTML_ENTITIES + + def handle_match(m): + name = m.group(1) + if name in HTML_ENTITIES: + return unichr(HTML_ENTITIES[name]) + try: + if name[:2] in ("#x", "#X"): + return unichr(int(name[2:], 16)) + elif name.startswith("#"): + return unichr(int(name[1:])) + except ValueError: + pass + # Don't modify unexpected input. + return m.group() + + return _entity_re.sub(handle_match, text_type(self)) + + def striptags(self): + """:meth:`unescape` the markup, remove tags, and normalize + whitespace to single spaces. + + >>> Markup('Main »\t<em>About</em>').striptags() + 'Main » About' + """ + stripped = u" ".join(_striptags_re.sub("", self).split()) + return Markup(stripped).unescape() + + @classmethod + def escape(cls, s): + """Escape a string. Calls :func:`escape` and ensures that for + subclasses the correct type is returned. + """ + rv = escape(s) + if rv.__class__ is not cls: + return cls(rv) + return rv + + def make_simple_escaping_wrapper(name): # noqa: B902 + orig = getattr(text_type, name) + + def func(self, *args, **kwargs): + args = _escape_argspec(list(args), enumerate(args), self.escape) + _escape_argspec(kwargs, iteritems(kwargs), self.escape) + return self.__class__(orig(self, *args, **kwargs)) + + func.__name__ = orig.__name__ + func.__doc__ = orig.__doc__ + return func + + for method in ( + "__getitem__", + "capitalize", + "title", + "lower", + "upper", + "replace", + "ljust", + "rjust", + "lstrip", + "rstrip", + "center", + "strip", + "translate", + "expandtabs", + "swapcase", + "zfill", + ): + locals()[method] = make_simple_escaping_wrapper(method) + + def partition(self, sep): + return tuple(map(self.__class__, text_type.partition(self, self.escape(sep)))) + + def rpartition(self, sep): + return tuple(map(self.__class__, text_type.rpartition(self, self.escape(sep)))) + + def format(self, *args, **kwargs): + formatter = EscapeFormatter(self.escape) + kwargs = _MagicFormatMapping(args, kwargs) + return self.__class__(formatter.vformat(self, args, kwargs)) + + def __html_format__(self, format_spec): + if format_spec: + raise ValueError("Unsupported format specification " "for Markup.") + return self + + # not in python 3 + if hasattr(text_type, "__getslice__"): + __getslice__ = make_simple_escaping_wrapper("__getslice__") + + del method, make_simple_escaping_wrapper + + +class _MagicFormatMapping(Mapping): + """This class implements a dummy wrapper to fix a bug in the Python + standard library for string formatting. + + See http://bugs.python.org/issue13598 for information about why + this is necessary. + """ + + def __init__(self, args, kwargs): + self._args = args + self._kwargs = kwargs + self._last_index = 0 + + def __getitem__(self, key): + if key == "": + idx = self._last_index + self._last_index += 1 + try: + return self._args[idx] + except LookupError: + pass + key = str(idx) + return self._kwargs[key] + + def __iter__(self): + return iter(self._kwargs) + + def __len__(self): + return len(self._kwargs) + + +if hasattr(text_type, "format"): + + class EscapeFormatter(string.Formatter): + def __init__(self, escape): + self.escape = escape + + def format_field(self, value, format_spec): + if hasattr(value, "__html_format__"): + rv = value.__html_format__(format_spec) + elif hasattr(value, "__html__"): + if format_spec: + raise ValueError( + "Format specifier {0} given, but {1} does not" + " define __html_format__. A class that defines" + " __html__ must define __html_format__ to work" + " with format specifiers.".format(format_spec, type(value)) + ) + rv = value.__html__() + else: + # We need to make sure the format spec is unicode here as + # otherwise the wrong callback methods are invoked. For + # instance a byte string there would invoke __str__ and + # not __unicode__. + rv = string.Formatter.format_field(self, value, text_type(format_spec)) + return text_type(self.escape(rv)) + + +def _escape_argspec(obj, iterable, escape): + """Helper for various string-wrapped functions.""" + for key, value in iterable: + if hasattr(value, "__html__") or isinstance(value, string_types): + obj[key] = escape(value) + return obj + + +class _MarkupEscapeHelper(object): + """Helper for Markup.__mod__""" + + def __init__(self, obj, escape): + self.obj = obj + self.escape = escape + + def __getitem__(self, item): + return _MarkupEscapeHelper(self.obj[item], self.escape) + + def __str__(self): + return text_type(self.escape(self.obj)) + + __unicode__ = __str__ + + def __repr__(self): + return str(self.escape(repr(self.obj))) + + def __int__(self): + return int(self.obj) + + def __float__(self): + return float(self.obj) + + +# we have to import it down here as the speedups and native +# modules imports the markup type which is define above. +try: + from ._speedups import escape, escape_silent, soft_unicode +except ImportError: + from ._native import escape, escape_silent, soft_unicode + +if not PY2: + soft_str = soft_unicode + __all__.append("soft_str") diff --git a/venv/Lib/site-packages/markupsafe/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/markupsafe/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..957c4fab0ee3a6b61482759cc0ae435614b34ca8 GIT binary patch literal 11015 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/markupsafe/__pycache__/_compat.cpython-36.pyc b/venv/Lib/site-packages/markupsafe/__pycache__/_compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e25044b1a626c9e463d6790fd89d6ced862a6cdb GIT binary patch literal 807 zcmZWm&1%~~5Z;w!OIB>BrKetsZov?nG`*QpN}NCgO$fL>R0zaIJ9ZRHTCuw}7L;7l zm&pV41^O6!?J2L&Q)lgzx^!XwR^RM=-{?)R7p}jbr=J``ev+MoHF=Mw*628i38&mC zXiS0LI-I^F++8@a3$FSAUfcq2M&ma4aR436LhkX_Hxln*SG(wzy|_n7bqIZng#OGy zY+$jlk9o-b9rUv;nmbc62$pv^J}jABENjJPFiJB~)=Y<Y|D7-#XQFOoKEKi9=*L5J zeDwM#x^5zU1JM;L3eYOjz{*iLF7ga21>@*sdU_Zsfav4-<m_^K7R`i=N+BWQpjlq1 zQTT`q$mOG5NJedr$3}ZGK~r6HntUa<^q$;PWKAyzj`4C0(zMW*YCsKLKM>hQ<S#ZE zC!ZBal{5$Hph-?aEwreU6CoDE3s$i?lu+s6v{BeQEExuwte{#YMSh(qd~sN_Y=JnH z{M}6Qw3$)en2w#jnf$^Mv`h(=0eWQOU$DB)tNA}-te}k;3qmR6o@|BuX_GDduo19z zzAY{I724B;!E@uMDHmCq8do*SbXO=d7gd^9Ghy7TFUNSVrRiu`>r^*2m@d{xW>YBR zW;fEbmsM_~n^zj8R9mFlOf&nLz(1L3d{vFubtXGF-}tsga4=pWs=3+Q0qt<M>z9_e i3Eq~1FAI2Q6(}pgqk+?RJnGT5H79WUF5Y1tVE!A_;L^+h literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/markupsafe/_compat.py b/venv/Lib/site-packages/markupsafe/_compat.py new file mode 100644 index 0000000..e51d57d --- /dev/null +++ b/venv/Lib/site-packages/markupsafe/_compat.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +""" +markupsafe._compat +~~~~~~~~~~~~~~~~~~ + +:copyright: © 2010 by the Pallets team. +:license: BSD, see LICENSE for more details. +""" +import sys + +PY2 = sys.version_info[0] == 2 + +if not PY2: + text_type = str + string_types = (str,) + unichr = chr + int_types = (int,) + + def iteritems(x): + return iter(x.items()) + + from collections.abc import Mapping + +else: + text_type = unicode + string_types = (str, unicode) + unichr = unichr + int_types = (int, long) + + def iteritems(x): + return x.iteritems() + + from collections import Mapping diff --git a/venv/Lib/site-packages/markupsafe/_constants.py b/venv/Lib/site-packages/markupsafe/_constants.py new file mode 100644 index 0000000..83670cb --- /dev/null +++ b/venv/Lib/site-packages/markupsafe/_constants.py @@ -0,0 +1,264 @@ +# -*- coding: utf-8 -*- +""" +markupsafe._constants +~~~~~~~~~~~~~~~~~~~~~ + +:copyright: © 2010 by the Pallets team. +:license: BSD, see LICENSE for more details. +""" + +HTML_ENTITIES = { + "AElig": 198, + "Aacute": 193, + "Acirc": 194, + "Agrave": 192, + "Alpha": 913, + "Aring": 197, + "Atilde": 195, + "Auml": 196, + "Beta": 914, + "Ccedil": 199, + "Chi": 935, + "Dagger": 8225, + "Delta": 916, + "ETH": 208, + "Eacute": 201, + "Ecirc": 202, + "Egrave": 200, + "Epsilon": 917, + "Eta": 919, + "Euml": 203, + "Gamma": 915, + "Iacute": 205, + "Icirc": 206, + "Igrave": 204, + "Iota": 921, + "Iuml": 207, + "Kappa": 922, + "Lambda": 923, + "Mu": 924, + "Ntilde": 209, + "Nu": 925, + "OElig": 338, + "Oacute": 211, + "Ocirc": 212, + "Ograve": 210, + "Omega": 937, + "Omicron": 927, + "Oslash": 216, + "Otilde": 213, + "Ouml": 214, + "Phi": 934, + "Pi": 928, + "Prime": 8243, + "Psi": 936, + "Rho": 929, + "Scaron": 352, + "Sigma": 931, + "THORN": 222, + "Tau": 932, + "Theta": 920, + "Uacute": 218, + "Ucirc": 219, + "Ugrave": 217, + "Upsilon": 933, + "Uuml": 220, + "Xi": 926, + "Yacute": 221, + "Yuml": 376, + "Zeta": 918, + "aacute": 225, + "acirc": 226, + "acute": 180, + "aelig": 230, + "agrave": 224, + "alefsym": 8501, + "alpha": 945, + "amp": 38, + "and": 8743, + "ang": 8736, + "apos": 39, + "aring": 229, + "asymp": 8776, + "atilde": 227, + "auml": 228, + "bdquo": 8222, + "beta": 946, + "brvbar": 166, + "bull": 8226, + "cap": 8745, + "ccedil": 231, + "cedil": 184, + "cent": 162, + "chi": 967, + "circ": 710, + "clubs": 9827, + "cong": 8773, + "copy": 169, + "crarr": 8629, + "cup": 8746, + "curren": 164, + "dArr": 8659, + "dagger": 8224, + "darr": 8595, + "deg": 176, + "delta": 948, + "diams": 9830, + "divide": 247, + "eacute": 233, + "ecirc": 234, + "egrave": 232, + "empty": 8709, + "emsp": 8195, + "ensp": 8194, + "epsilon": 949, + "equiv": 8801, + "eta": 951, + "eth": 240, + "euml": 235, + "euro": 8364, + "exist": 8707, + "fnof": 402, + "forall": 8704, + "frac12": 189, + "frac14": 188, + "frac34": 190, + "frasl": 8260, + "gamma": 947, + "ge": 8805, + "gt": 62, + "hArr": 8660, + "harr": 8596, + "hearts": 9829, + "hellip": 8230, + "iacute": 237, + "icirc": 238, + "iexcl": 161, + "igrave": 236, + "image": 8465, + "infin": 8734, + "int": 8747, + "iota": 953, + "iquest": 191, + "isin": 8712, + "iuml": 239, + "kappa": 954, + "lArr": 8656, + "lambda": 955, + "lang": 9001, + "laquo": 171, + "larr": 8592, + "lceil": 8968, + "ldquo": 8220, + "le": 8804, + "lfloor": 8970, + "lowast": 8727, + "loz": 9674, + "lrm": 8206, + "lsaquo": 8249, + "lsquo": 8216, + "lt": 60, + "macr": 175, + "mdash": 8212, + "micro": 181, + "middot": 183, + "minus": 8722, + "mu": 956, + "nabla": 8711, + "nbsp": 160, + "ndash": 8211, + "ne": 8800, + "ni": 8715, + "not": 172, + "notin": 8713, + "nsub": 8836, + "ntilde": 241, + "nu": 957, + "oacute": 243, + "ocirc": 244, + "oelig": 339, + "ograve": 242, + "oline": 8254, + "omega": 969, + "omicron": 959, + "oplus": 8853, + "or": 8744, + "ordf": 170, + "ordm": 186, + "oslash": 248, + "otilde": 245, + "otimes": 8855, + "ouml": 246, + "para": 182, + "part": 8706, + "permil": 8240, + "perp": 8869, + "phi": 966, + "pi": 960, + "piv": 982, + "plusmn": 177, + "pound": 163, + "prime": 8242, + "prod": 8719, + "prop": 8733, + "psi": 968, + "quot": 34, + "rArr": 8658, + "radic": 8730, + "rang": 9002, + "raquo": 187, + "rarr": 8594, + "rceil": 8969, + "rdquo": 8221, + "real": 8476, + "reg": 174, + "rfloor": 8971, + "rho": 961, + "rlm": 8207, + "rsaquo": 8250, + "rsquo": 8217, + "sbquo": 8218, + "scaron": 353, + "sdot": 8901, + "sect": 167, + "shy": 173, + "sigma": 963, + "sigmaf": 962, + "sim": 8764, + "spades": 9824, + "sub": 8834, + "sube": 8838, + "sum": 8721, + "sup": 8835, + "sup1": 185, + "sup2": 178, + "sup3": 179, + "supe": 8839, + "szlig": 223, + "tau": 964, + "there4": 8756, + "theta": 952, + "thetasym": 977, + "thinsp": 8201, + "thorn": 254, + "tilde": 732, + "times": 215, + "trade": 8482, + "uArr": 8657, + "uacute": 250, + "uarr": 8593, + "ucirc": 251, + "ugrave": 249, + "uml": 168, + "upsih": 978, + "upsilon": 965, + "uuml": 252, + "weierp": 8472, + "xi": 958, + "yacute": 253, + "yen": 165, + "yuml": 255, + "zeta": 950, + "zwj": 8205, + "zwnj": 8204, +} diff --git a/venv/Lib/site-packages/markupsafe/_native.py b/venv/Lib/site-packages/markupsafe/_native.py new file mode 100644 index 0000000..245f03a --- /dev/null +++ b/venv/Lib/site-packages/markupsafe/_native.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +""" +markupsafe._native +~~~~~~~~~~~~~~~~~~ + +Native Python implementation used when the C module is not compiled. + +:copyright: © 2010 by the Pallets team. +:license: BSD, see LICENSE for more details. +""" +from . import Markup +from ._compat import text_type + + +def escape(s): + """Replace the characters ``&``, ``<``, ``>``, ``'``, and ``"`` in + the string with HTML-safe sequences. Use this if you need to display + text that might contain such characters in HTML. + + If the object has an ``__html__`` method, it is called and the + return value is assumed to already be safe for HTML. + + :param s: An object to be converted to a string and escaped. + :return: A :class:`Markup` string with the escaped text. + """ + if hasattr(s, "__html__"): + return Markup(s.__html__()) + return Markup( + text_type(s) + .replace("&", "&") + .replace(">", ">") + .replace("<", "<") + .replace("'", "'") + .replace('"', """) + ) + + +def escape_silent(s): + """Like :func:`escape` but treats ``None`` as the empty string. + Useful with optional values, as otherwise you get the string + ``'None'`` when the value is ``None``. + + >>> escape(None) + Markup('None') + >>> escape_silent(None) + Markup('') + """ + if s is None: + return Markup() + return escape(s) + + +def soft_unicode(s): + """Convert an object to a string if it isn't already. This preserves + a :class:`Markup` string rather than converting it back to a basic + string, so it will still be marked as safe and won't be escaped + again. + + >>> value = escape('<User 1>') + >>> value + Markup('<User 1>') + >>> escape(str(value)) + Markup('&lt;User 1&gt;') + >>> escape(soft_unicode(value)) + Markup('<User 1>') + """ + if not isinstance(s, text_type): + s = text_type(s) + return s diff --git a/venv/Lib/site-packages/markupsafe/_speedups.cp36-win_amd64.pyd b/venv/Lib/site-packages/markupsafe/_speedups.cp36-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..a8c04555257017027e89641ea14958d1d3dafb98 GIT binary patch literal 15360 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 L@!9pM@W6iq^(r^! literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pip-18.1.dist-info/LICENSE.txt b/venv/Lib/site-packages/pip-18.1.dist-info/LICENSE.txt new file mode 100644 index 0000000..d3379fa --- /dev/null +++ b/venv/Lib/site-packages/pip-18.1.dist-info/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008-2018 The pip developers (see AUTHORS.txt file) + +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/site-packages/pip-18.1.dist-info/METADATA b/venv/Lib/site-packages/pip-18.1.dist-info/METADATA new file mode 100644 index 0000000..2e314aa --- /dev/null +++ b/venv/Lib/site-packages/pip-18.1.dist-info/METADATA @@ -0,0 +1,70 @@ +Metadata-Version: 2.1 +Name: pip +Version: 18.1 +Summary: The PyPA recommended tool for installing Python packages. +Home-page: https://pip.pypa.io/ +Author: The pip developers +Author-email: pypa-dev@groups.google.com +License: MIT +Keywords: distutils easy_install egg setuptools wheel virtualenv +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Build Tools +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* + +pip +=== + +The `PyPA recommended`_ tool for installing Python packages. + +.. image:: https://img.shields.io/pypi/v/pip.svg + :target: https://pypi.org/project/pip/ + +.. image:: https://img.shields.io/travis/pypa/pip/master.svg?label=travis-ci + :target: https://travis-ci.org/pypa/pip + +.. image:: https://img.shields.io/appveyor/ci/pypa/pip.svg?label=appveyor-ci + :target: https://ci.appveyor.com/project/pypa/pip/history + +.. image:: https://readthedocs.org/projects/pip/badge/?version=latest + :target: https://pip.pypa.io/en/latest + +* `Installation`_ +* `Documentation`_ +* `Changelog`_ +* `GitHub Page`_ +* `Issue Tracking`_ +* `User mailing list`_ +* `Dev mailing list`_ +* User IRC: #pypa on Freenode. +* Dev IRC: #pypa-dev on Freenode. + +Code of Conduct +--------------- + +Everyone interacting in the pip project's codebases, issue trackers, chat +rooms and mailing lists is expected to follow the `PyPA Code of Conduct`_. + +.. _PyPA recommended: https://packaging.python.org/en/latest/current/ +.. _Installation: https://pip.pypa.io/en/stable/installing.html +.. _Documentation: https://pip.pypa.io/en/stable/ +.. _Changelog: https://pip.pypa.io/en/stable/news.html +.. _GitHub Page: https://github.com/pypa/pip +.. _Issue Tracking: https://github.com/pypa/pip/issues +.. _User mailing list: https://groups.google.com/forum/#!forum/python-virtualenv +.. _Dev mailing list: https://groups.google.com/forum/#!forum/pypa-dev +.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ + + diff --git a/venv/Lib/site-packages/pip-18.1.dist-info/RECORD b/venv/Lib/site-packages/pip-18.1.dist-info/RECORD new file mode 100644 index 0000000..49426db --- /dev/null +++ b/venv/Lib/site-packages/pip-18.1.dist-info/RECORD @@ -0,0 +1,311 @@ +pip/__init__.py,sha256=nO-iphoXiDoci_ZAMl-PG2zdd4Y7m88jBDILTYzwGy4,21 +pip/__main__.py,sha256=L3IHqBeasELUHvwy5CT_izVEMhM12tve289qut49DvU,623 +pip/_internal/__init__.py,sha256=b0jSFCCViGhB1RWni35_NMkH3Y-mbZrV648DGMagDjs,2869 +pip/_internal/build_env.py,sha256=zKhqmDMnrX5OTSNQ4xBw-mN5mTGVu6wjiNFW-ajWYEI,4797 +pip/_internal/cache.py,sha256=96_aKtDbwgLEVNgNabOT8GrFCYZEACedoiucqU5ccg8,6829 +pip/_internal/configuration.py,sha256=KMgG3ufFrUKX_QESi2cMVvFi47tl845Bg1ZkNthlWik,13243 +pip/_internal/download.py,sha256=c5Hkimq39eJdZ6DN0_0etjK43-0a5CK_W_3sVLqH87g,33300 +pip/_internal/exceptions.py,sha256=EIGotnq6qM2nbGtnlgZ8Xp5VfP2W4-9UOCzQGMwy5MY,8899 +pip/_internal/index.py,sha256=6CAtZ8QTLcpw0fJqQ9OPu-Os1ettLZtVY1pPSKia8r8,34789 +pip/_internal/locations.py,sha256=ujNrLnA04Y_EmSriO0nS6qkkw_BkPfobB_hdwIDPvpM,6307 +pip/_internal/pep425tags.py,sha256=TQhxOPss4RjxgyVgxpSRe31HaTcWmn-LVjWBbkvkjzk,10845 +pip/_internal/pyproject.py,sha256=fpO52MCa3w5xSlXIBXw39BDTGzP8G4570EW34hVvIKQ,5481 +pip/_internal/resolve.py,sha256=tdepxCewsXXNFKSIYGSxiLvzi1xCv7UVFT9jRCDO90A,13578 +pip/_internal/wheel.py,sha256=fg9E936DaI1LyrBPHqtzHG_WEVyuUwipHISkD6N3jNw,32007 +pip/_internal/cli/__init__.py,sha256=FkHBgpxxb-_gd6r1FjnNhfMOzAUYyXoXKJ6abijfcFU,132 +pip/_internal/cli/autocompletion.py,sha256=ptvsMdGjq42pzoY4skABVF43u2xAtLJlXAulPi-A10Y,6083 +pip/_internal/cli/base_command.py,sha256=ke6af4iWzrZoc3HtiPKnCZJvD6GlX8dRwBwpFCg1axc,9963 +pip/_internal/cli/cmdoptions.py,sha256=WoPPY1uHsDjA_NvZek8Mko38rxraD3pX8eZUkNKvk10,19468 +pip/_internal/cli/main_parser.py,sha256=Ga_kT7if-Gg0rmmRqlGEHW6JWVm9zwzO7igJm6RE9EI,2763 +pip/_internal/cli/parser.py,sha256=VZKUKJPbU6I2cHPLDOikin-aCx7OvLcZ3fzYp3xytd8,9378 +pip/_internal/cli/status_codes.py,sha256=F6uDG6Gj7RNKQJUDnd87QKqI16Us-t-B0wPF_4QMpWc,156 +pip/_internal/commands/__init__.py,sha256=CQAzhVx9ViPtqLNUvAeqnKj5iWfFEcqMx5RlZWjJ30c,2251 +pip/_internal/commands/check.py,sha256=CyeYH2kfDKSGSURoBfWtx-sTcZZQP-bK170NmKYlmsg,1398 +pip/_internal/commands/completion.py,sha256=hqvCvoxsIHjysiD7olHKTqK2lzE1_lS6LWn69kN5qyI,2929 +pip/_internal/commands/configuration.py,sha256=265HWuUxPggCNcIeWHA3p-LDDiRVnexwFgwmHGgWOHY,7125 +pip/_internal/commands/download.py,sha256=D_iGMp3xX2iD7KZYZAjXlYT3rf3xjwxyYe05KE-DVzE,6514 +pip/_internal/commands/freeze.py,sha256=VvS3G0wrm_9BH3B7Ex5msLL_1UQTtCq5G8dDI63Iemo,3259 +pip/_internal/commands/hash.py,sha256=K1JycsD-rpjqrRcL_ijacY9UKmI82pQcLYq4kCM4Pv0,1681 +pip/_internal/commands/help.py,sha256=MwBhPJpW1Dt3GfJV3V8V6kgAy_pXT0jGrZJB1wCTW-E,1090 +pip/_internal/commands/install.py,sha256=tKyzfo5bhDGLVTTQCQJ9PFnDjimQvEWnwIAI2XHpaac,21039 +pip/_internal/commands/list.py,sha256=n740MsR0cG34EuvGWMzdVl0uIA3UIYx1_95FUsTktN0,10272 +pip/_internal/commands/search.py,sha256=sLZ9icKMEEGekHvzRRZMiTd1zCFIZeDptyyU1mQCYzk,4728 +pip/_internal/commands/show.py,sha256=9EVh86vY0NZdlhT-wsuV-zq_MAV6qqV4S1Akn3wkUuw,6289 +pip/_internal/commands/uninstall.py,sha256=h0gfPF5jylDESx_IHgF6bZME7QAEOHzQHdn65GP-jrE,2963 +pip/_internal/commands/wheel.py,sha256=ZuVf_DMpKCUzBVstolvQPAeajQRC51Oky5_hDHzhhFs,7020 +pip/_internal/models/__init__.py,sha256=3DHUd_qxpPozfzouoqa9g9ts1Czr5qaHfFxbnxriepM,63 +pip/_internal/models/candidate.py,sha256=zq2Vb5l5JflrVX7smHTJHQciZWHyoJZuYTLeQa1G16c,741 +pip/_internal/models/format_control.py,sha256=aDbH4D2XuyaGjtRjTLQhNzClAcLZdJCKSHO8xbZSmFA,2202 +pip/_internal/models/index.py,sha256=YI1WlhWfS9mVPY0bIboA5la2pjJ2J0qgPJIbvdEjZBk,996 +pip/_internal/models/link.py,sha256=E61PvS2Wrmb9-zT-eAc_8_xI3C-89wJlpL8SL-mlQmg,3998 +pip/_internal/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/operations/check.py,sha256=ahcOg5p68nNow6_wy5prYYK0KZq22lm0CsJn8AyDMCI,4937 +pip/_internal/operations/freeze.py,sha256=lskaBcqf3bPZupG032fuLf76QYv5wpAQ6jsiXac56Bg,10450 +pip/_internal/operations/prepare.py,sha256=atoLFj3OD5KfXsa5dYBMC_mI06l068F5yZhF4jle1JA,14280 +pip/_internal/req/__init__.py,sha256=JnNZWvKUQuqAwHh64LCD3zprzWIVQEXChTo2UGHzVqo,2093 +pip/_internal/req/constructors.py,sha256=97WQp9Svh-Jw3oLZL9_57gJ3zihm5LnWlSRjOwOorDU,9573 +pip/_internal/req/req_file.py,sha256=ORA0GKUjGd6vy7pmBwXR55FFj4h_OxYykFQ6gHuWvt0,11940 +pip/_internal/req/req_install.py,sha256=ry1RtNNCefDHAnf3EeGMpea-9pC6Yk1uHzP0Q5p2Un0,34046 +pip/_internal/req/req_set.py,sha256=nE6oagXJSiQREuuebX3oJO5OHSOVUIlvLLilodetBzc,7264 +pip/_internal/req/req_tracker.py,sha256=zH28YHV5TXAVh1ZOEZi6Z1Edkiu26dN2tXfR6VbQ3B4,2370 +pip/_internal/req/req_uninstall.py,sha256=ORSPah64KOVrKo-InMM3zgS5HQqbl5TLHFnE_Lxstq8,16737 +pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/utils/appdirs.py,sha256=SPfibHtvOKzD_sHrpEZ60HfLae3GharU4Tg7SB3c-XM,9120 +pip/_internal/utils/compat.py,sha256=LSAvzXcsGY2O2drKIPszR5Ja2G0kup__51l3bx1jR_Q,8015 +pip/_internal/utils/deprecation.py,sha256=yQTe6dyWlBfxSBrOv_MdRXF1RPLER_EWOp-pa2zLoZc,3021 +pip/_internal/utils/encoding.py,sha256=D8tmfStCah6xh9OLhH9mWLr77q4akhg580YHJMKpq3Y,1025 +pip/_internal/utils/filesystem.py,sha256=ZOIHbacJ-SJtuZru4GoA5DuSIYyeaE4G5kfZPf5cn1A,915 +pip/_internal/utils/glibc.py,sha256=prOrsBjmgkDE-hY4Pl120yF5MIlkkmGrFLs8XfIyT-w,3004 +pip/_internal/utils/hashes.py,sha256=rJk-gj6F-sHggXAG97dhynqUHFFgApyZLWgaG2xCHME,2900 +pip/_internal/utils/logging.py,sha256=BQeUDEER3zlK0O4yv6DBfz6TK3f9XoLXyDlnB0mZVf0,6295 +pip/_internal/utils/misc.py,sha256=YscDfBiFx1spYOtSgdI_5hnc5BZUysWAyz1aVL5y-48,29904 +pip/_internal/utils/models.py,sha256=DQYZSRhjvSdDTAaJLLCpDtxAn1S_-v_8nlNjv4T2jwY,1042 +pip/_internal/utils/outdated.py,sha256=BXtCMKR6gjTrvMfP3MWzZ1Y4ZU4qqoCfbRNqQCusVt8,5642 +pip/_internal/utils/packaging.py,sha256=Ru8ls_S8PPKR8RKEn7jMetENY_A9jPet1HlhTZwpFxU,2443 +pip/_internal/utils/setuptools_build.py,sha256=0blfscmNJW_iZ5DcswJeDB_PbtTEjfK9RL1R1WEDW2E,278 +pip/_internal/utils/temp_dir.py,sha256=n2FkVlwRX_hS61fYt3nSAh2e2V6CcZn_dfbPId1pAQE,2615 +pip/_internal/utils/typing.py,sha256=ztYtZAcqjCYDwP-WlF6EiAAskAsZBMMXtuqvfgZIlgQ,1139 +pip/_internal/utils/ui.py,sha256=FW8wdtc7DvNwJClGr_TvGZlqcoO482GYe0UY9nKmpso,13657 +pip/_internal/vcs/__init__.py,sha256=2Ct9ogOwzS6ZKKaEXKN2XDiBOiFHMcejnN1KM21mLrQ,16319 +pip/_internal/vcs/bazaar.py,sha256=rjskVmSSn68O7lC5JrGmDTWXneXFMMJJvj_bbdSM8QA,3669 +pip/_internal/vcs/git.py,sha256=n1cFBqTnLIcxAOClZMgOBqELjEjygDBPZ9z-Q7g0qVQ,12580 +pip/_internal/vcs/mercurial.py,sha256=jVTa0XQpFR6EiBcaqW4E4JjTce_t1tFnKRaIhaIPlS8,3471 +pip/_internal/vcs/subversion.py,sha256=vDLTfcjj0kgqcEsbPBfveC4CRxyhWiOjke-qN0Zr8CE,7676 +pip/_vendor/__init__.py,sha256=XnhkujjE1qUGRlYGYbIRrEGYYYBcNLBraE27HH48wYw,4756 +pip/_vendor/appdirs.py,sha256=BENKsvcA08IpccD9345-rMrg3aXWFA1q6BFEglnHg6I,24547 +pip/_vendor/distro.py,sha256=dOMrjIXv-3GmEbtP-NJc057Sv19P7ZAdke-v0TBeNio,42455 +pip/_vendor/ipaddress.py,sha256=2OgbkeAD2rLkcXqbcvof3J5R7lRwjNLoBySyTkBtKnc,79852 +pip/_vendor/pyparsing.py,sha256=My2ZwDJCEaZkZgZyG9gL--48RLGmf9vnVCTW93rhdYI,226342 +pip/_vendor/retrying.py,sha256=k3fflf5_Mm0XcIJYhB7Tj34bqCCPhUDkYbx1NvW2FPE,9972 +pip/_vendor/six.py,sha256=A08MPb-Gi9FfInI3IW7HimXFmEH2T2IPzHgDvdhZPRA,30888 +pip/_vendor/cachecontrol/__init__.py,sha256=6cRPchVqkAkeUtYTSW8qCetjSqJo-GxP-n4VMVDbvmc,302 +pip/_vendor/cachecontrol/_cmd.py,sha256=URGE0KrA87QekCG3SGPatlSPT571dZTDjNa-ZXX3pDc,1295 +pip/_vendor/cachecontrol/adapter.py,sha256=eBGAtVNRZgtl_Kj5JV54miqL9YND-D0JZPahwY8kFtY,4863 +pip/_vendor/cachecontrol/cache.py,sha256=1fc4wJP8HYt1ycnJXeEw5pCpeBL2Cqxx6g9Fb0AYDWQ,805 +pip/_vendor/cachecontrol/compat.py,sha256=kHNvMRdt6s_Xwqq_9qJmr9ou3wYMOMUMxPPcwNxT8Mc,695 +pip/_vendor/cachecontrol/controller.py,sha256=U7g-YwizQ2O5NRgK_MZreF1ntM4E49C3PuF3od-Vwz4,13698 +pip/_vendor/cachecontrol/filewrapper.py,sha256=vACKO8Llzu_ZWyjV1Fxn1MA4TGU60N5N3GSrAFdAY2Q,2533 +pip/_vendor/cachecontrol/heuristics.py,sha256=BFGHJ3yQcxvZizfo90LLZ04T_Z5XSCXvFotrp7Us0sc,4070 +pip/_vendor/cachecontrol/serialize.py,sha256=GebE34fgToyWwAsRPguh8hEPN6CqoG-5hRMXRsjVABQ,6954 +pip/_vendor/cachecontrol/wrapper.py,sha256=sfr9YHWx-5TwNz1H5rT6QOo8ggII6v3vbEDjQFwR6wc,671 +pip/_vendor/cachecontrol/caches/__init__.py,sha256=-gHNKYvaeD0kOk5M74eOrsSgIKUtC6i6GfbmugGweEo,86 +pip/_vendor/cachecontrol/caches/file_cache.py,sha256=8vrSzzGcdfEfICago1uSFbkumNJMGLbCdEkXsmUIExw,4177 +pip/_vendor/cachecontrol/caches/redis_cache.py,sha256=HxelMpNCo-dYr2fiJDwM3hhhRmxUYtB5tXm1GpAAT4Y,856 +pip/_vendor/certifi/__init__.py,sha256=5lCYV1iWxoirX1OAaSHkBYUuZGdcwEjEBS6DS_trL0s,63 +pip/_vendor/certifi/__main__.py,sha256=NaCn6WtWME-zzVWQ2j4zFyl8cY4knDa9CwtHNIeFPhM,53 +pip/_vendor/certifi/cacert.pem,sha256=XA-4HVBsOrBD5lfg-b3PiUzAvwUd2qlIzwXypIMIRGM,263074 +pip/_vendor/certifi/core.py,sha256=xPQDdG_siy5A7BfqGWa7RJhcA61xXEqPiSrw9GNyhHE,836 +pip/_vendor/chardet/__init__.py,sha256=YsP5wQlsHJ2auF1RZJfypiSrCA7_bQiRm3ES_NI76-Y,1559 +pip/_vendor/chardet/big5freq.py,sha256=D_zK5GyzoVsRes0HkLJziltFQX0bKCLOrFe9_xDvO_8,31254 +pip/_vendor/chardet/big5prober.py,sha256=kBxHbdetBpPe7xrlb-e990iot64g_eGSLd32lB7_h3M,1757 +pip/_vendor/chardet/chardistribution.py,sha256=3woWS62KrGooKyqz4zQSnjFbJpa6V7g02daAibTwcl8,9411 +pip/_vendor/chardet/charsetgroupprober.py,sha256=6bDu8YIiRuScX4ca9Igb0U69TA2PGXXDej6Cc4_9kO4,3787 +pip/_vendor/chardet/charsetprober.py,sha256=KSmwJErjypyj0bRZmC5F5eM7c8YQgLYIjZXintZNstg,5110 +pip/_vendor/chardet/codingstatemachine.py,sha256=VYp_6cyyki5sHgXDSZnXW4q1oelHc3cu9AyQTX7uug8,3590 +pip/_vendor/chardet/compat.py,sha256=PKTzHkSbtbHDqS9PyujMbX74q1a8mMpeQTDVsQhZMRw,1134 +pip/_vendor/chardet/cp949prober.py,sha256=TZ434QX8zzBsnUvL_8wm4AQVTZ2ZkqEEQL_lNw9f9ow,1855 +pip/_vendor/chardet/enums.py,sha256=Aimwdb9as1dJKZaFNUH2OhWIVBVd6ZkJJ_WK5sNY8cU,1661 +pip/_vendor/chardet/escprober.py,sha256=kkyqVg1Yw3DIOAMJ2bdlyQgUFQhuHAW8dUGskToNWSc,3950 +pip/_vendor/chardet/escsm.py,sha256=RuXlgNvTIDarndvllNCk5WZBIpdCxQ0kcd9EAuxUh84,10510 +pip/_vendor/chardet/eucjpprober.py,sha256=iD8Jdp0ISRjgjiVN7f0e8xGeQJ5GM2oeZ1dA8nbSeUw,3749 +pip/_vendor/chardet/euckrfreq.py,sha256=-7GdmvgWez4-eO4SuXpa7tBiDi5vRXQ8WvdFAzVaSfo,13546 +pip/_vendor/chardet/euckrprober.py,sha256=MqFMTQXxW4HbzIpZ9lKDHB3GN8SP4yiHenTmf8g_PxY,1748 +pip/_vendor/chardet/euctwfreq.py,sha256=No1WyduFOgB5VITUA7PLyC5oJRNzRyMbBxaKI1l16MA,31621 +pip/_vendor/chardet/euctwprober.py,sha256=13p6EP4yRaxqnP4iHtxHOJ6R2zxHq1_m8hTRjzVZ95c,1747 +pip/_vendor/chardet/gb2312freq.py,sha256=JX8lsweKLmnCwmk8UHEQsLgkr_rP_kEbvivC4qPOrlc,20715 +pip/_vendor/chardet/gb2312prober.py,sha256=gGvIWi9WhDjE-xQXHvNIyrnLvEbMAYgyUSZ65HUfylw,1754 +pip/_vendor/chardet/hebrewprober.py,sha256=c3SZ-K7hvyzGY6JRAZxJgwJ_sUS9k0WYkvMY00YBYFo,13838 +pip/_vendor/chardet/jisfreq.py,sha256=vpmJv2Bu0J8gnMVRPHMFefTRvo_ha1mryLig8CBwgOg,25777 +pip/_vendor/chardet/jpcntx.py,sha256=PYlNqRUQT8LM3cT5FmHGP0iiscFlTWED92MALvBungo,19643 +pip/_vendor/chardet/langbulgarianmodel.py,sha256=1HqQS9Pbtnj1xQgxitJMvw8X6kKr5OockNCZWfEQrPE,12839 +pip/_vendor/chardet/langcyrillicmodel.py,sha256=LODajvsetH87yYDDQKA2CULXUH87tI223dhfjh9Zx9c,17948 +pip/_vendor/chardet/langgreekmodel.py,sha256=8YAW7bU8YwSJap0kIJSbPMw1BEqzGjWzqcqf0WgUKAA,12688 +pip/_vendor/chardet/langhebrewmodel.py,sha256=JSnqmE5E62tDLTPTvLpQsg5gOMO4PbdWRvV7Avkc0HA,11345 +pip/_vendor/chardet/langhungarianmodel.py,sha256=RhapYSG5l0ZaO-VV4Fan5sW0WRGQqhwBM61yx3yxyOA,12592 +pip/_vendor/chardet/langthaimodel.py,sha256=8l0173Gu_W6G8mxmQOTEF4ls2YdE7FxWf3QkSxEGXJQ,11290 +pip/_vendor/chardet/langturkishmodel.py,sha256=W22eRNJsqI6uWAfwXSKVWWnCerYqrI8dZQTm_M0lRFk,11102 +pip/_vendor/chardet/latin1prober.py,sha256=S2IoORhFk39FEFOlSFWtgVybRiP6h7BlLldHVclNkU8,5370 +pip/_vendor/chardet/mbcharsetprober.py,sha256=AR95eFH9vuqSfvLQZN-L5ijea25NOBCoXqw8s5O9xLQ,3413 +pip/_vendor/chardet/mbcsgroupprober.py,sha256=h6TRnnYq2OxG1WdD5JOyxcdVpn7dG0q-vB8nWr5mbh4,2012 +pip/_vendor/chardet/mbcssm.py,sha256=SY32wVIF3HzcjY3BaEspy9metbNSKxIIB0RKPn7tjpI,25481 +pip/_vendor/chardet/sbcharsetprober.py,sha256=LDSpCldDCFlYwUkGkwD2oFxLlPWIWXT09akH_2PiY74,5657 +pip/_vendor/chardet/sbcsgroupprober.py,sha256=1IprcCB_k1qfmnxGC6MBbxELlKqD3scW6S8YIwdeyXA,3546 +pip/_vendor/chardet/sjisprober.py,sha256=IIt-lZj0WJqK4rmUZzKZP4GJlE8KUEtFYVuY96ek5MQ,3774 +pip/_vendor/chardet/universaldetector.py,sha256=qL0174lSZE442eB21nnktT9_VcAye07laFWUeUrjttY,12485 +pip/_vendor/chardet/utf8prober.py,sha256=IdD8v3zWOsB8OLiyPi-y_fqwipRFxV9Nc1eKBLSuIEw,2766 +pip/_vendor/chardet/version.py,sha256=sp3B08mrDXB-pf3K9fqJ_zeDHOCLC8RrngQyDFap_7g,242 +pip/_vendor/chardet/cli/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +pip/_vendor/chardet/cli/chardetect.py,sha256=DI8dlV3FBD0c0XA_y3sQ78z754DUv1J8n34RtDjOXNw,2774 +pip/_vendor/colorama/__init__.py,sha256=V3-Hv_vOa-2lE5Q_0mGkdhZo-9e4XrGTW_44cU81qQY,240 +pip/_vendor/colorama/ansi.py,sha256=Fi0un-QLqRm-v7o_nKiOqyC8PapBJK7DLV_q9LKtTO0,2524 +pip/_vendor/colorama/ansitowin32.py,sha256=QrieYX2tsaWIO19P6biMa1zUCt-_abudoEp2_IdqZZU,9668 +pip/_vendor/colorama/initialise.py,sha256=cHqVJtb82OG7HUCxvQ2joG7N_CoxbIKbI_fgryZkj20,1917 +pip/_vendor/colorama/win32.py,sha256=5Hc7L1LabubrYDhdWAfRyzlt14ErP3YKDvf_zYaarLk,5426 +pip/_vendor/colorama/winterm.py,sha256=V7U7ojwG1q4n6PKripjEvW_htYQi5ueXSM3LUUoqqDY,6290 +pip/_vendor/distlib/__init__.py,sha256=GxRrh1augb66Eo9NB9jrdwQS02KcBypj9o_-C3oyKZI,581 +pip/_vendor/distlib/compat.py,sha256=xdNZmqFN5HwF30HjRn5M415pcC2kgXRBXn767xS8v-M,41404 +pip/_vendor/distlib/database.py,sha256=LqTcNkDyV4bWcc_qDxiYJHnXaNxFs1O1bFSAg_reaI0,50868 +pip/_vendor/distlib/index.py,sha256=Dd1kIV06XIdynNpKxHMMRRIKsXuoUsG7QIzntfVtZCI,21073 +pip/_vendor/distlib/locators.py,sha256=e4UaQSzNg5iG3PfQRH6lnVMfLOwhm2sVmGGRdjdB3ik,51657 +pip/_vendor/distlib/manifest.py,sha256=nQEhYmgoreaBZzyFzwYsXxJARu3fo4EkunU163U16iE,14811 +pip/_vendor/distlib/markers.py,sha256=6Ac3cCfFBERexiESWIOXmg-apIP8l2esafNSX3KMy-8,4387 +pip/_vendor/distlib/metadata.py,sha256=Ns92dqeMxopDPQsiEWnhMtd4RagJaA58lz8O_vjCxyk,39986 +pip/_vendor/distlib/resources.py,sha256=2FGv0ZHF14KXjLIlL0R991lyQQGcewOS4mJ-5n-JVnc,10766 +pip/_vendor/distlib/scripts.py,sha256=WEqXkpRvqR6oe-QlMRYg8gEJxXRWJeWn1GPc0ihZ4N0,16585 +pip/_vendor/distlib/t32.exe,sha256=ftub1bsSPUCOnBn-eCtcarKTk0N0CBEP53BumkIxWJE,92672 +pip/_vendor/distlib/t64.exe,sha256=iChOG627LWTHY8-jzSwlo9SYU5a-0JHwQu4AqDz8I68,102400 +pip/_vendor/distlib/util.py,sha256=FnzjaibVcIg1xOtET6QPNeqTnn3LcWLCjNOficMyGKA,59494 +pip/_vendor/distlib/version.py,sha256=_n7F6juvQGAcn769E_SHa7fOcf5ERlEVymJ_EjPRwGw,23391 +pip/_vendor/distlib/w32.exe,sha256=NPYPpt7PIjVqABEu1CzabbDyHHkJpuw-_qZq_48H0j0,89088 +pip/_vendor/distlib/w64.exe,sha256=Yb-qr1OQEzL8KRGTk-XHUZDwMSljfQeZnVoTk-K4e7E,99328 +pip/_vendor/distlib/wheel.py,sha256=W9aKwi4CQL_bQFYb8IcwH-c6WK-yku5P8SY3RGPv-Mk,39506 +pip/_vendor/distlib/_backport/__init__.py,sha256=bqS_dTOH6uW9iGgd0uzfpPjo6vZ4xpPZ7kyfZJ2vNaw,274 +pip/_vendor/distlib/_backport/misc.py,sha256=KWecINdbFNOxSOP1fGF680CJnaC6S4fBRgEtaYTw0ig,971 +pip/_vendor/distlib/_backport/shutil.py,sha256=VW1t3uYqUjWZH7jV-6QiimLhnldoV5uIpH4EuiT1jfw,25647 +pip/_vendor/distlib/_backport/sysconfig.cfg,sha256=swZKxq9RY5e9r3PXCrlvQPMsvOdiWZBTHLEbqS8LJLU,2617 +pip/_vendor/distlib/_backport/sysconfig.py,sha256=JdJ9ztRy4Hc-b5-VS74x3nUtdEIVr_OBvMsIb8O2sjc,26964 +pip/_vendor/distlib/_backport/tarfile.py,sha256=Ihp7rXRcjbIKw8COm9wSePV9ARGXbSF9gGXAMn2Q-KU,92628 +pip/_vendor/html5lib/__init__.py,sha256=Ztrn7UvF-wIFAgRBBa0ML-Gu5AffH3BPX_INJx4SaBI,1162 +pip/_vendor/html5lib/_ihatexml.py,sha256=3LBtJMlzgwM8vpQiU1TvGmEEmNH72sV0yD8yS53y07A,16705 +pip/_vendor/html5lib/_inputstream.py,sha256=bPUWcAfJScK4xkjQQaG_HsI2BvEVbFvI0AsodDYPQj0,32552 +pip/_vendor/html5lib/_tokenizer.py,sha256=YAaOEBD6qc5ISq9Xt9Nif1OFgcybTTfMdwqBkZhpAq4,76580 +pip/_vendor/html5lib/_utils.py,sha256=ismpASeqa2jqEPQjHUj8vReAf7yIoKnvLN5fuOw6nv0,4015 +pip/_vendor/html5lib/constants.py,sha256=4lmZWLtEPRLnl8NzftOoYTJdo6jpeMtP6dqQC0g_bWQ,83518 +pip/_vendor/html5lib/html5parser.py,sha256=g5g2ezkusHxhi7b23vK_-d6K6BfIJRbqIQmvQ9z4EgI,118963 +pip/_vendor/html5lib/serializer.py,sha256=yfcfBHse2wDs6ojxn-kieJjLT5s1ipilQJ0gL3-rJis,15758 +pip/_vendor/html5lib/_trie/__init__.py,sha256=8VR1bcgD2OpeS2XExpu5yBhP_Q1K-lwKbBKICBPf1kU,289 +pip/_vendor/html5lib/_trie/_base.py,sha256=uJHVhzif9S0MJXgy9F98iEev5evi_rgUk5BmEbUSp8c,930 +pip/_vendor/html5lib/_trie/datrie.py,sha256=EQpqSfkZRuTbE-DuhW7xMdVDxdZNZ0CfmnYfHA_3zxM,1178 +pip/_vendor/html5lib/_trie/py.py,sha256=wXmQLrZRf4MyWNyg0m3h81m9InhLR7GJ002mIIZh-8o,1775 +pip/_vendor/html5lib/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/html5lib/filters/alphabeticalattributes.py,sha256=lViZc2JMCclXi_5gduvmdzrRxtO5Xo9ONnbHBVCsykU,919 +pip/_vendor/html5lib/filters/base.py,sha256=z-IU9ZAYjpsVsqmVt7kuWC63jR11hDMr6CVrvuao8W0,286 +pip/_vendor/html5lib/filters/inject_meta_charset.py,sha256=egDXUEHXmAG9504xz0K6ALDgYkvUrC2q15YUVeNlVQg,2945 +pip/_vendor/html5lib/filters/lint.py,sha256=jk6q56xY0ojiYfvpdP-OZSm9eTqcAdRqhCoPItemPYA,3643 +pip/_vendor/html5lib/filters/optionaltags.py,sha256=8lWT75J0aBOHmPgfmqTHSfPpPMp01T84NKu0CRedxcE,10588 +pip/_vendor/html5lib/filters/sanitizer.py,sha256=4ON02KNjuqda1lCw5_JCUZxb0BzWR5M7ON84dtJ7dm0,26248 +pip/_vendor/html5lib/filters/whitespace.py,sha256=8eWqZxd4UC4zlFGW6iyY6f-2uuT8pOCSALc3IZt7_t4,1214 +pip/_vendor/html5lib/treeadapters/__init__.py,sha256=A0rY5gXIe4bJOiSGRO_j_tFhngRBO8QZPzPtPw5dFzo,679 +pip/_vendor/html5lib/treeadapters/genshi.py,sha256=CH27pAsDKmu4ZGkAUrwty7u0KauGLCZRLPMzaO3M5vo,1715 +pip/_vendor/html5lib/treeadapters/sax.py,sha256=BKS8woQTnKiqeffHsxChUqL4q2ZR_wb5fc9MJ3zQC8s,1776 +pip/_vendor/html5lib/treebuilders/__init__.py,sha256=AysSJyvPfikCMMsTVvaxwkgDieELD5dfR8FJIAuq7hY,3592 +pip/_vendor/html5lib/treebuilders/base.py,sha256=wQGp5yy22TNG8tJ6aREe4UUeTR7A99dEz0BXVaedWb4,14579 +pip/_vendor/html5lib/treebuilders/dom.py,sha256=SY3MsijXyzdNPc8aK5IQsupBoM8J67y56DgNtGvsb9g,8835 +pip/_vendor/html5lib/treebuilders/etree.py,sha256=aqIBOGj_dFYqBURIcTegGNBhAIJOw5iFDHb4jrkYH-8,12764 +pip/_vendor/html5lib/treebuilders/etree_lxml.py,sha256=9V0dXxbJYYq-Skgb5-_OL2NkVYpjioEb4CHajo0e9yI,14122 +pip/_vendor/html5lib/treewalkers/__init__.py,sha256=yhXxHpjlSqfQyUag3v8-vWjMPriFBU8YRAPNpDgBTn8,5714 +pip/_vendor/html5lib/treewalkers/base.py,sha256=ouiOsuSzvI0KgzdWP8PlxIaSNs9falhbiinAEc_UIJY,7476 +pip/_vendor/html5lib/treewalkers/dom.py,sha256=EHyFR8D8lYNnyDU9lx_IKigVJRyecUGua0mOi7HBukc,1413 +pip/_vendor/html5lib/treewalkers/etree.py,sha256=sz1o6mmE93NQ53qJFDO7HKyDtuwgK-Ay3qSFZPC6u00,4550 +pip/_vendor/html5lib/treewalkers/etree_lxml.py,sha256=sY6wfRshWTllu6n48TPWpKsQRPp-0CQrT0hj_AdzHSU,6309 +pip/_vendor/html5lib/treewalkers/genshi.py,sha256=4D2PECZ5n3ZN3qu3jMl9yY7B81jnQApBQSVlfaIuYbA,2309 +pip/_vendor/idna/__init__.py,sha256=9Nt7xpyet3DmOrPUGooDdAwmHZZu1qUAy2EaJ93kGiQ,58 +pip/_vendor/idna/codec.py,sha256=lvYb7yu7PhAqFaAIAdWcwgaWI2UmgseUua-1c0AsG0A,3299 +pip/_vendor/idna/compat.py,sha256=R-h29D-6mrnJzbXxymrWUW7iZUvy-26TQwZ0ij57i4U,232 +pip/_vendor/idna/core.py,sha256=OwI5R_uuXU4PlOSoG8cjaMPA1hhdGGjjZ8I2MZhSPxo,11858 +pip/_vendor/idna/idnadata.py,sha256=zwxvoSsYqPHNa6xzXWHizXpDC28JJMGXRinhJ4Gkcz0,39285 +pip/_vendor/idna/intranges.py,sha256=TY1lpxZIQWEP6tNqjZkFA5hgoMWOj1OBmnUG8ihT87E,1749 +pip/_vendor/idna/package_data.py,sha256=Vt9rtr32BzO7O25rypo8nzAs3syTJhG1ojU3J-s2RFo,21 +pip/_vendor/idna/uts46data.py,sha256=czULzYN5Lr9K5MmOH-1g3CJY7QPjGeHjYmC3saJ_BHk,197803 +pip/_vendor/lockfile/__init__.py,sha256=Tqpz90DwKYfhPsfzVOJl84TL87pdFE5ePNHdXAxs4Tk,9371 +pip/_vendor/lockfile/linklockfile.py,sha256=C7OH3H4GdK68u4FQgp8fkP2kO4fyUTSyj3X6blgfobc,2652 +pip/_vendor/lockfile/mkdirlockfile.py,sha256=e3qgIL-etZMLsS-3ft19iW_8IQ360HNkGOqE3yBKsUw,3096 +pip/_vendor/lockfile/pidlockfile.py,sha256=ukH9uk6NFuxyVmG5QiWw4iKq3fT7MjqUguX95avYPIY,6090 +pip/_vendor/lockfile/sqlitelockfile.py,sha256=o2TMkMRY0iwn-iL1XMRRIFStMUkS4i3ajceeYNntKFg,5506 +pip/_vendor/lockfile/symlinklockfile.py,sha256=ABwXXmvTHvCl5viPblShL3PG-gGsLiT1roAMfDRwhi8,2616 +pip/_vendor/msgpack/__init__.py,sha256=y0bk2YbzK6J2e0J_dyreN6nD7yM2IezT6m_tU2h-Mdg,1677 +pip/_vendor/msgpack/_version.py,sha256=dN7wVIjbyuQIJ35B2o6gymQNDLPlj_7-uTfgCv7KErM,20 +pip/_vendor/msgpack/exceptions.py,sha256=lPkAi_u12NlFajDz4FELSHEdfU8hrR3zeTvKX8aQuz4,1056 +pip/_vendor/msgpack/fallback.py,sha256=h0ll8xnq12mI9PuQ9Qd_Ihtt08Sp8L0JqhG9KY8Vyjk,36411 +pip/_vendor/packaging/__about__.py,sha256=mH-sMIEu48PzdYakZ6Y6OBzL3TlSetzz1fQSkCXiy30,720 +pip/_vendor/packaging/__init__.py,sha256=_vNac5TrzwsrzbOFIbF-5cHqc_Y2aPT2D7zrIR06BOo,513 +pip/_vendor/packaging/_compat.py,sha256=Vi_A0rAQeHbU-a9X0tt1yQm9RqkgQbDSxzRw8WlU9kA,860 +pip/_vendor/packaging/_structures.py,sha256=DCpKtb7u94_oqgVsIJQTrTyZcb3Gz7sSGbk9vYDMME0,1418 +pip/_vendor/packaging/markers.py,sha256=ftZegBU5oEmulEKApDGEPgti2lYIchFQHAfH9tZy3_U,8221 +pip/_vendor/packaging/requirements.py,sha256=xIWdoZXVKhUHxqFP5xmnKylM7NHXQS48hUfIIX1PvY0,4439 +pip/_vendor/packaging/specifiers.py,sha256=pFp716eLYBRt0eLNsy6cnWD9dyMKq-Zag7bsLbLv4Fs,28026 +pip/_vendor/packaging/utils.py,sha256=c9obOpok2CpKDApkc2M5ma0YFnT-jtt4I6XI4F0jYiI,1580 +pip/_vendor/packaging/version.py,sha256=MKL8nbKLPLGPouIwFvwSVnYRzNpkMo5AIcsa6LGqDF8,12219 +pip/_vendor/pep517/__init__.py,sha256=GH4HshnLERtjAjkY0zHoz3f7-35UcIvr27iFWSOUazU,82 +pip/_vendor/pep517/_in_process.py,sha256=iWpagFk2GhNBbvl-Ca2RagfD0ALuits4WWSM6nQMTdg,5831 +pip/_vendor/pep517/check.py,sha256=Yp2NHW71DIOCgkFb7HKJOzKmsum_s_OokRP6HnR3bTg,5761 +pip/_vendor/pep517/colorlog.py,sha256=2AJuPI_DHM5T9IDgcTwf0E8suyHAFnfsesogr0AB7RQ,4048 +pip/_vendor/pep517/compat.py,sha256=4SFG4QN-cNj8ebSa0wV0HUtEEQWwmbok2a0uk1gYEOM,631 +pip/_vendor/pep517/envbuild.py,sha256=osRsJVd7hir1w_uFXiVeeWxfJ3iYhwxsKRgNBWpqtCI,5672 +pip/_vendor/pep517/wrappers.py,sha256=RhgWm-MLxpYPgc9cZ3-A3ToN99ZzgM8-ia4FDB58koM,5018 +pip/_vendor/pkg_resources/__init__.py,sha256=ykZI7-YBIAQ7ztWf0RskP8Oy1VQU88o-16PJbIMCtLg,103915 +pip/_vendor/pkg_resources/py31compat.py,sha256=CRk8fkiPRDLsbi5pZcKsHI__Pbmh_94L8mr9Qy9Ab2U,562 +pip/_vendor/progress/__init__.py,sha256=Hv3Y8Hr6RyM34NdZkrZQWMURjS2h5sONRHJSvZXWZgQ,3188 +pip/_vendor/progress/bar.py,sha256=hlkDAEv9pRRiWqR5XL6vIAgMG4u_dBGEW_8klQhBRq0,2942 +pip/_vendor/progress/counter.py,sha256=XtBuZY4yYmr50E2A_fAzjWhm0IkwaVwxNsNVYDE7nsw,1528 +pip/_vendor/progress/helpers.py,sha256=6FsBLh_xUlKiVua-zZIutCjxth-IO8FtyUj6I2tx9fg,2952 +pip/_vendor/progress/spinner.py,sha256=m7bASI2GUbLFG-PbAefdHtrrWWlJLFhhSBbw70gp2TY,1439 +pip/_vendor/pytoml/__init__.py,sha256=q12Xv23Tta44gtK4HGK68Gr4tKfciILidFPmPuoIqIo,92 +pip/_vendor/pytoml/core.py,sha256=9CrLLTs1PdWjEwRnYzt_i4dhHcZvGxs_GsMlYAX3iY4,509 +pip/_vendor/pytoml/parser.py,sha256=mcTzHB2GQGyK8KVwuQ0EraSz_78O36U60NqHBtgVmV0,11247 +pip/_vendor/pytoml/writer.py,sha256=-mSOVGaiGLrpj5BRR7czmquZXJGflcElHrwAd33J48A,3815 +pip/_vendor/requests/__init__.py,sha256=OrwNk1JwZGqIQ4JVGgMbfpstqey-oHS_Re_Dw6D4ciI,4209 +pip/_vendor/requests/__version__.py,sha256=rJ2xgNOLhjspGkNPfgXTBctqqvsf2uJMFTaE0rlVtbI,436 +pip/_vendor/requests/_internal_utils.py,sha256=Zx3PnEUccyfsB-ie11nZVAW8qClJy0gx1qNME7rgT18,1096 +pip/_vendor/requests/adapters.py,sha256=y5DISepvSsGlu3II_VUsdgKBej1dGY4b5beRrTE2tsI,21428 +pip/_vendor/requests/api.py,sha256=zub9ENcEUT2m9gwgBgqH5RIRDfrx2kwRpZ7L6hX3mcw,6261 +pip/_vendor/requests/auth.py,sha256=oRSQkBYcLkTEssudkzoR1UW1Glb1ts3p1RWusgHf1YU,10208 +pip/_vendor/requests/certs.py,sha256=nXRVq9DtGmv_1AYbwjTu9UrgAcdJv05ZvkNeaoLOZxY,465 +pip/_vendor/requests/compat.py,sha256=7EC6fZY4dJDxuBQnqUGwe13OTZ3VLGO3QfOApE5lE3I,1998 +pip/_vendor/requests/cookies.py,sha256=olUaLeNci_z1K-Bn5PeEKllSspmQqN9-s8Ug7CasaPE,18346 +pip/_vendor/requests/exceptions.py,sha256=-mLam3TAx80V09EaH3H-ZxR61eAVuLRZ8zgBBSLjK44,3197 +pip/_vendor/requests/help.py,sha256=T4K-Oo_FS9fxF8NHVR8hxMwFo71gIkRM7UddCc9vH7Y,3669 +pip/_vendor/requests/hooks.py,sha256=HXAHoC1FNTFRZX6-lNdvPM7Tst4kvGwYTN-AOKRxoRU,767 +pip/_vendor/requests/models.py,sha256=3fmmYdDW7U18SrVeZaseHuk8KPI-7vLp_435DY3ejes,34160 +pip/_vendor/requests/packages.py,sha256=njJmVifY4aSctuW3PP5EFRCxjEwMRDO6J_feG2dKWsI,695 +pip/_vendor/requests/sessions.py,sha256=71MK2HCadovka1vAx9dyDFWAuw69KgRPRBpd0HWSEAo,27829 +pip/_vendor/requests/status_codes.py,sha256=pgw-xlnxO5zHQWn3fKps2cxwQehKzPxEbdhIrMQe6Ec,4128 +pip/_vendor/requests/structures.py,sha256=zoP8qly2Jak5e89HwpqjN1z2diztI-_gaqts1raJJBc,2981 +pip/_vendor/requests/utils.py,sha256=3OxbbLUQFVdm84fdBD9nduXvhw6hIzj59mhvBomKuJI,30156 +pip/_vendor/urllib3/__init__.py,sha256=DZucS8tlzGYKmK5FIsyUViMghpCq_0_0Ouvm_Jp9GNc,2853 +pip/_vendor/urllib3/_collections.py,sha256=iNeAU_we9L3lMGRUKKdq24Mf7o050TXP5U4Jm9AzAY4,10841 +pip/_vendor/urllib3/connection.py,sha256=My76qeWMDkV-KP1l3iChXHOE7J-ZCaUdP3sPIiLA2uE,14485 +pip/_vendor/urllib3/connectionpool.py,sha256=w20OwKdIqk6f8FIl6QGgn6jf9gZ0-tmgH7VPxnpEgkQ,35464 +pip/_vendor/urllib3/exceptions.py,sha256=rFeIfBNKC8KJ61ux-MtJyJlEC9G9ggkmCeF751JwVR4,6604 +pip/_vendor/urllib3/fields.py,sha256=D_TE_SK15YatdbhWDMN0OE3X6UCJn1RTkANINCYOobE,5943 +pip/_vendor/urllib3/filepost.py,sha256=40CROlpRKVBpFUkD0R6wJf_PpvbcRQRFUu0OOQlFkKM,2436 +pip/_vendor/urllib3/poolmanager.py,sha256=FHBjb7odbP2LyQRzeitgpuh1AQAPyegzmrm2b3gSZlY,16821 +pip/_vendor/urllib3/request.py,sha256=fwjlq5nQfcUa7aoncR25z6-fJAX_oNTcPksKKGjBm38,5996 +pip/_vendor/urllib3/response.py,sha256=uAuOTZSuTodzvQWIDCZghDoKmZ2bKbgIRCfaVIIZfn8,24667 +pip/_vendor/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/urllib3/contrib/appengine.py,sha256=Q3BDy5C_TrI-3cSyo0ELNGlNiK2eSVptQAQMdz4PH9Q,11197 +pip/_vendor/urllib3/contrib/ntlmpool.py,sha256=Q9-rO5Rh2-IqyEd4ZicpTDfMnOlf0IPPCkjhChBCjV4,4478 +pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=cM7fVZJRrdLZsprcdWe3meM_hvq8LR73UNDveIMa-20,15480 +pip/_vendor/urllib3/contrib/securetransport.py,sha256=BqXSlChN9_hjCWgyN6JdcgvBUdc37QCCX4u3_8zE_9o,30309 +pip/_vendor/urllib3/contrib/socks.py,sha256=Iom0snbHkCuZbZ7Sle2Kueha1W0jYAJ0SyCOtePLaio,6391 +pip/_vendor/urllib3/contrib/_securetransport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/urllib3/contrib/_securetransport/bindings.py,sha256=x2kLSh-ASZKsun0FxtraBuLVe3oHuth4YW6yZ5Vof-w,17560 +pip/_vendor/urllib3/contrib/_securetransport/low_level.py,sha256=Umy5u-3Z957GirdapnicXVOpHaM4xdOZABJuJxfaeJA,12162 +pip/_vendor/urllib3/packages/__init__.py,sha256=nlChrGzkjCkmhCX9HrF_qHPUgosfsPQkVIJxiiLhk9g,109 +pip/_vendor/urllib3/packages/ordered_dict.py,sha256=VQaPONfhVMsb8B63Xg7ZOydJqIE_jzeMhVN3Pec6ogw,8935 +pip/_vendor/urllib3/packages/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 +pip/_vendor/urllib3/packages/backports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/urllib3/packages/backports/makefile.py,sha256=r1IADol_pBBq2Y1ub4CPyuS2hXuShK47nfFngZRcRhI,1461 +pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py,sha256=WBVbxQBojNAxfZwNavkox3BgJiMA9BJmm-_fwd0jD_o,688 +pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py,sha256=XCW0ydHg171GfOqNbvUAnRzQ0lc0twp5-dIlolgf4RM,5719 +pip/_vendor/urllib3/util/__init__.py,sha256=6Ran4oAVIy40Cu_oEPWnNV9bwF5rXx6G1DUZ7oehjPY,1044 +pip/_vendor/urllib3/util/connection.py,sha256=8K1VXm8BHsM3QATJJGBNRa_MStkzDy1Da2IaPAaCU8c,4279 +pip/_vendor/urllib3/util/queue.py,sha256=myTX3JDHntglKQNBf3b6dasHH-uF-W59vzGSQiFdAfI,497 +pip/_vendor/urllib3/util/request.py,sha256=H5_lrHvtwl2U2BbT1UYN9HpruNc1gsNFlz2njQmhPrQ,3705 +pip/_vendor/urllib3/util/response.py,sha256=SSNL888W-MQ8t3HAi44kNGgF682p6H__ytEXzBYxV_M,2343 +pip/_vendor/urllib3/util/retry.py,sha256=tlxiEq8OU2BSenPpPjYYO1URne8A-qTEgaykam6rZPg,15104 +pip/_vendor/urllib3/util/ssl_.py,sha256=iHJopgSv8_vXfmGg3lOsTS3ldMD9zhe130huHZxQEGU,14022 +pip/_vendor/urllib3/util/timeout.py,sha256=7lHNrgL5YH2cI1j-yZnzV_J8jBlRVdmFhQaNyM1_2b8,9757 +pip/_vendor/urllib3/util/url.py,sha256=qCY_HHUXvo05wAsEERALgExtlgxLnAHSQ7ce1b-g3SM,6487 +pip/_vendor/urllib3/util/wait.py,sha256=_4vvsT1BTTpqxQYK-2kXVfGsUsVRiuc4R4F-0Bf5BPc,5468 +pip/_vendor/webencodings/__init__.py,sha256=qOBJIuPy_4ByYH6W_bNgJF-qYQ2DoU-dKsDu5yRWCXg,10579 +pip/_vendor/webencodings/labels.py,sha256=4AO_KxTddqGtrL9ns7kAPjb0CcN6xsCIxbK37HY9r3E,8979 +pip/_vendor/webencodings/mklabels.py,sha256=GYIeywnpaLnP0GSic8LFWgd0UVvO_l1Nc6YoF-87R_4,1305 +pip/_vendor/webencodings/tests.py,sha256=OtGLyjhNY1fvkW1GvLJ_FV9ZoqC9Anyjr7q3kxTbzNs,6563 +pip/_vendor/webencodings/x_user_defined.py,sha256=yOqWSdmpytGfUgh_Z6JYgDNhoc-BAHyyeeT15Fr42tM,4307 +pip-18.1.dist-info/LICENSE.txt,sha256=ORqHhOMZ2uVDFHfUzJvFBPxdcf2eieHIDxzThV9dfPo,1090 +pip-18.1.dist-info/METADATA,sha256=D7pqBJTuqM9w_HTW91a0XGjLT9vynlBAE4pPCt_W_UE,2588 +pip-18.1.dist-info/WHEEL,sha256=8T8fxefr_r-A79qbOJ9d_AaEgkpCGmEPHc-gpCq5BRg,110 +pip-18.1.dist-info/entry_points.txt,sha256=S_zfxY25QtQDVY1BiLAmOKSkkI5llzCKPLiYOSEupsY,98 +pip-18.1.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip-18.1.dist-info/RECORD,, diff --git a/venv/Lib/site-packages/pip-18.1.dist-info/WHEEL b/venv/Lib/site-packages/pip-18.1.dist-info/WHEEL new file mode 100644 index 0000000..1001235 --- /dev/null +++ b/venv/Lib/site-packages/pip-18.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.32.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/pip-18.1.dist-info/entry_points.txt b/venv/Lib/site-packages/pip-18.1.dist-info/entry_points.txt new file mode 100644 index 0000000..f5809cb --- /dev/null +++ b/venv/Lib/site-packages/pip-18.1.dist-info/entry_points.txt @@ -0,0 +1,5 @@ +[console_scripts] +pip = pip._internal:main +pip3 = pip._internal:main +pip3.7 = pip._internal:main + diff --git a/venv/Lib/site-packages/pip-18.1.dist-info/top_level.txt b/venv/Lib/site-packages/pip-18.1.dist-info/top_level.txt new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/pip-18.1.dist-info/top_level.txt @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/PKG-INFO b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000..6f1032a --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,61 @@ +Metadata-Version: 1.2 +Name: pip +Version: 9.0.1 +Summary: The PyPA recommended tool for installing Python packages. +Home-page: https://pip.pypa.io/ +Author: The pip developers +Author-email: python-virtualenv@groups.google.com +License: MIT +Description: pip + === + + The `PyPA recommended + <https://packaging.python.org/en/latest/current/>`_ + tool for installing Python packages. + + * `Installation <https://pip.pypa.io/en/stable/installing.html>`_ + * `Documentation <https://pip.pypa.io/>`_ + * `Changelog <https://pip.pypa.io/en/stable/news.html>`_ + * `Github Page <https://github.com/pypa/pip>`_ + * `Issue Tracking <https://github.com/pypa/pip/issues>`_ + * `User mailing list <http://groups.google.com/group/python-virtualenv>`_ + * `Dev mailing list <http://groups.google.com/group/pypa-dev>`_ + * User IRC: #pypa on Freenode. + * Dev IRC: #pypa-dev on Freenode. + + + .. image:: https://img.shields.io/pypi/v/pip.svg + :target: https://pypi.python.org/pypi/pip + + .. image:: https://img.shields.io/travis/pypa/pip/master.svg + :target: http://travis-ci.org/pypa/pip + + .. image:: https://img.shields.io/appveyor/ci/pypa/pip.svg + :target: https://ci.appveyor.com/project/pypa/pip/history + + .. image:: https://readthedocs.org/projects/pip/badge/?version=stable + :target: https://pip.pypa.io/en/stable + + Code of Conduct + --------------- + + Everyone interacting in the pip project's codebases, issue trackers, chat + rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_. + + .. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ + +Keywords: easy_install distutils setuptools egg virtualenv +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Build Tools +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=2.6,!=3.0.*,!=3.1.*,!=3.2.* diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/SOURCES.txt b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/SOURCES.txt new file mode 100644 index 0000000..e66d7ac --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/SOURCES.txt @@ -0,0 +1,290 @@ +AUTHORS.txt +CHANGES.txt +LICENSE.txt +MANIFEST.in +README.rst +setup.cfg +setup.py +docs/Makefile +docs/__init__.py +docs/conf.py +docs/configuration.rst +docs/cookbook.rst +docs/development.rst +docs/index.rst +docs/installing.rst +docs/logic.rst +docs/make.bat +docs/news.rst +docs/pipext.py +docs/quickstart.rst +docs/usage.rst +docs/user_guide.rst +docs/reference/index.rst +docs/reference/pip.rst +docs/reference/pip_download.rst +docs/reference/pip_freeze.rst +docs/reference/pip_hash.rst +docs/reference/pip_install.rst +docs/reference/pip_list.rst +docs/reference/pip_search.rst +docs/reference/pip_show.rst +docs/reference/pip_uninstall.rst +docs/reference/pip_wheel.rst +pip/__init__.py +pip/__main__.py +pip/basecommand.py +pip/baseparser.py +pip/cmdoptions.py +pip/download.py +pip/exceptions.py +pip/index.py +pip/locations.py +pip/pep425tags.py +pip/status_codes.py +pip/wheel.py +pip.egg-info/PKG-INFO +pip.egg-info/SOURCES.txt +pip.egg-info/dependency_links.txt +pip.egg-info/entry_points.txt +pip.egg-info/not-zip-safe +pip.egg-info/requires.txt +pip.egg-info/top_level.txt +pip/_vendor/README.rst +pip/_vendor/__init__.py +pip/_vendor/appdirs.py +pip/_vendor/distro.py +pip/_vendor/ipaddress.py +pip/_vendor/ordereddict.py +pip/_vendor/pyparsing.py +pip/_vendor/re-vendor.py +pip/_vendor/retrying.py +pip/_vendor/six.py +pip/_vendor/vendor.txt +pip/_vendor/cachecontrol/__init__.py +pip/_vendor/cachecontrol/_cmd.py +pip/_vendor/cachecontrol/adapter.py +pip/_vendor/cachecontrol/cache.py +pip/_vendor/cachecontrol/compat.py +pip/_vendor/cachecontrol/controller.py +pip/_vendor/cachecontrol/filewrapper.py +pip/_vendor/cachecontrol/heuristics.py +pip/_vendor/cachecontrol/serialize.py +pip/_vendor/cachecontrol/wrapper.py +pip/_vendor/cachecontrol/caches/__init__.py +pip/_vendor/cachecontrol/caches/file_cache.py +pip/_vendor/cachecontrol/caches/redis_cache.py +pip/_vendor/colorama/__init__.py +pip/_vendor/colorama/ansi.py +pip/_vendor/colorama/ansitowin32.py +pip/_vendor/colorama/initialise.py +pip/_vendor/colorama/win32.py +pip/_vendor/colorama/winterm.py +pip/_vendor/distlib/__init__.py +pip/_vendor/distlib/compat.py +pip/_vendor/distlib/database.py +pip/_vendor/distlib/index.py +pip/_vendor/distlib/locators.py +pip/_vendor/distlib/manifest.py +pip/_vendor/distlib/markers.py +pip/_vendor/distlib/metadata.py +pip/_vendor/distlib/resources.py +pip/_vendor/distlib/scripts.py +pip/_vendor/distlib/t32.exe +pip/_vendor/distlib/t64.exe +pip/_vendor/distlib/util.py +pip/_vendor/distlib/version.py +pip/_vendor/distlib/w32.exe +pip/_vendor/distlib/w64.exe +pip/_vendor/distlib/wheel.py +pip/_vendor/distlib/_backport/__init__.py +pip/_vendor/distlib/_backport/misc.py +pip/_vendor/distlib/_backport/shutil.py +pip/_vendor/distlib/_backport/sysconfig.cfg +pip/_vendor/distlib/_backport/sysconfig.py +pip/_vendor/distlib/_backport/tarfile.py +pip/_vendor/html5lib/__init__.py +pip/_vendor/html5lib/_ihatexml.py +pip/_vendor/html5lib/_inputstream.py +pip/_vendor/html5lib/_tokenizer.py +pip/_vendor/html5lib/_utils.py +pip/_vendor/html5lib/constants.py +pip/_vendor/html5lib/html5parser.py +pip/_vendor/html5lib/serializer.py +pip/_vendor/html5lib/_trie/__init__.py +pip/_vendor/html5lib/_trie/_base.py +pip/_vendor/html5lib/_trie/datrie.py +pip/_vendor/html5lib/_trie/py.py +pip/_vendor/html5lib/filters/__init__.py +pip/_vendor/html5lib/filters/alphabeticalattributes.py +pip/_vendor/html5lib/filters/base.py +pip/_vendor/html5lib/filters/inject_meta_charset.py +pip/_vendor/html5lib/filters/lint.py +pip/_vendor/html5lib/filters/optionaltags.py +pip/_vendor/html5lib/filters/sanitizer.py +pip/_vendor/html5lib/filters/whitespace.py +pip/_vendor/html5lib/treeadapters/__init__.py +pip/_vendor/html5lib/treeadapters/genshi.py +pip/_vendor/html5lib/treeadapters/sax.py +pip/_vendor/html5lib/treebuilders/__init__.py +pip/_vendor/html5lib/treebuilders/base.py +pip/_vendor/html5lib/treebuilders/dom.py +pip/_vendor/html5lib/treebuilders/etree.py +pip/_vendor/html5lib/treebuilders/etree_lxml.py +pip/_vendor/html5lib/treewalkers/__init__.py +pip/_vendor/html5lib/treewalkers/base.py +pip/_vendor/html5lib/treewalkers/dom.py +pip/_vendor/html5lib/treewalkers/etree.py +pip/_vendor/html5lib/treewalkers/etree_lxml.py +pip/_vendor/html5lib/treewalkers/genshi.py +pip/_vendor/lockfile/__init__.py +pip/_vendor/lockfile/linklockfile.py +pip/_vendor/lockfile/mkdirlockfile.py +pip/_vendor/lockfile/pidlockfile.py +pip/_vendor/lockfile/sqlitelockfile.py +pip/_vendor/lockfile/symlinklockfile.py +pip/_vendor/packaging/__about__.py +pip/_vendor/packaging/__init__.py +pip/_vendor/packaging/_compat.py +pip/_vendor/packaging/_structures.py +pip/_vendor/packaging/markers.py +pip/_vendor/packaging/requirements.py +pip/_vendor/packaging/specifiers.py +pip/_vendor/packaging/utils.py +pip/_vendor/packaging/version.py +pip/_vendor/pkg_resources/__init__.py +pip/_vendor/progress/__init__.py +pip/_vendor/progress/bar.py +pip/_vendor/progress/counter.py +pip/_vendor/progress/helpers.py +pip/_vendor/progress/spinner.py +pip/_vendor/requests/__init__.py +pip/_vendor/requests/adapters.py +pip/_vendor/requests/api.py +pip/_vendor/requests/auth.py +pip/_vendor/requests/cacert.pem +pip/_vendor/requests/certs.py +pip/_vendor/requests/compat.py +pip/_vendor/requests/cookies.py +pip/_vendor/requests/exceptions.py +pip/_vendor/requests/hooks.py +pip/_vendor/requests/models.py +pip/_vendor/requests/sessions.py +pip/_vendor/requests/status_codes.py +pip/_vendor/requests/structures.py +pip/_vendor/requests/utils.py +pip/_vendor/requests/packages/__init__.py +pip/_vendor/requests/packages/chardet/__init__.py +pip/_vendor/requests/packages/chardet/big5freq.py +pip/_vendor/requests/packages/chardet/big5prober.py +pip/_vendor/requests/packages/chardet/chardetect.py +pip/_vendor/requests/packages/chardet/chardistribution.py +pip/_vendor/requests/packages/chardet/charsetgroupprober.py +pip/_vendor/requests/packages/chardet/charsetprober.py +pip/_vendor/requests/packages/chardet/codingstatemachine.py +pip/_vendor/requests/packages/chardet/compat.py +pip/_vendor/requests/packages/chardet/constants.py +pip/_vendor/requests/packages/chardet/cp949prober.py +pip/_vendor/requests/packages/chardet/escprober.py +pip/_vendor/requests/packages/chardet/escsm.py +pip/_vendor/requests/packages/chardet/eucjpprober.py +pip/_vendor/requests/packages/chardet/euckrfreq.py +pip/_vendor/requests/packages/chardet/euckrprober.py +pip/_vendor/requests/packages/chardet/euctwfreq.py +pip/_vendor/requests/packages/chardet/euctwprober.py +pip/_vendor/requests/packages/chardet/gb2312freq.py +pip/_vendor/requests/packages/chardet/gb2312prober.py +pip/_vendor/requests/packages/chardet/hebrewprober.py +pip/_vendor/requests/packages/chardet/jisfreq.py +pip/_vendor/requests/packages/chardet/jpcntx.py +pip/_vendor/requests/packages/chardet/langbulgarianmodel.py +pip/_vendor/requests/packages/chardet/langcyrillicmodel.py +pip/_vendor/requests/packages/chardet/langgreekmodel.py +pip/_vendor/requests/packages/chardet/langhebrewmodel.py +pip/_vendor/requests/packages/chardet/langhungarianmodel.py +pip/_vendor/requests/packages/chardet/langthaimodel.py +pip/_vendor/requests/packages/chardet/latin1prober.py +pip/_vendor/requests/packages/chardet/mbcharsetprober.py +pip/_vendor/requests/packages/chardet/mbcsgroupprober.py +pip/_vendor/requests/packages/chardet/mbcssm.py +pip/_vendor/requests/packages/chardet/sbcharsetprober.py +pip/_vendor/requests/packages/chardet/sbcsgroupprober.py +pip/_vendor/requests/packages/chardet/sjisprober.py +pip/_vendor/requests/packages/chardet/universaldetector.py +pip/_vendor/requests/packages/chardet/utf8prober.py +pip/_vendor/requests/packages/urllib3/__init__.py +pip/_vendor/requests/packages/urllib3/_collections.py +pip/_vendor/requests/packages/urllib3/connection.py +pip/_vendor/requests/packages/urllib3/connectionpool.py +pip/_vendor/requests/packages/urllib3/exceptions.py +pip/_vendor/requests/packages/urllib3/fields.py +pip/_vendor/requests/packages/urllib3/filepost.py +pip/_vendor/requests/packages/urllib3/poolmanager.py +pip/_vendor/requests/packages/urllib3/request.py +pip/_vendor/requests/packages/urllib3/response.py +pip/_vendor/requests/packages/urllib3/contrib/__init__.py +pip/_vendor/requests/packages/urllib3/contrib/appengine.py +pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.py +pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py +pip/_vendor/requests/packages/urllib3/contrib/socks.py +pip/_vendor/requests/packages/urllib3/packages/__init__.py +pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py +pip/_vendor/requests/packages/urllib3/packages/six.py +pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py +pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py +pip/_vendor/requests/packages/urllib3/util/__init__.py +pip/_vendor/requests/packages/urllib3/util/connection.py +pip/_vendor/requests/packages/urllib3/util/request.py +pip/_vendor/requests/packages/urllib3/util/response.py +pip/_vendor/requests/packages/urllib3/util/retry.py +pip/_vendor/requests/packages/urllib3/util/ssl_.py +pip/_vendor/requests/packages/urllib3/util/timeout.py +pip/_vendor/requests/packages/urllib3/util/url.py +pip/_vendor/webencodings/__init__.py +pip/_vendor/webencodings/labels.py +pip/_vendor/webencodings/mklabels.py +pip/_vendor/webencodings/tests.py +pip/_vendor/webencodings/x_user_defined.py +pip/commands/__init__.py +pip/commands/check.py +pip/commands/completion.py +pip/commands/download.py +pip/commands/freeze.py +pip/commands/hash.py +pip/commands/help.py +pip/commands/install.py +pip/commands/list.py +pip/commands/search.py +pip/commands/show.py +pip/commands/uninstall.py +pip/commands/wheel.py +pip/compat/__init__.py +pip/compat/dictconfig.py +pip/models/__init__.py +pip/models/index.py +pip/operations/__init__.py +pip/operations/check.py +pip/operations/freeze.py +pip/req/__init__.py +pip/req/req_file.py +pip/req/req_install.py +pip/req/req_set.py +pip/req/req_uninstall.py +pip/utils/__init__.py +pip/utils/appdirs.py +pip/utils/build.py +pip/utils/deprecation.py +pip/utils/encoding.py +pip/utils/filesystem.py +pip/utils/glibc.py +pip/utils/hashes.py +pip/utils/logging.py +pip/utils/outdated.py +pip/utils/packaging.py +pip/utils/setuptools_build.py +pip/utils/ui.py +pip/vcs/__init__.py +pip/vcs/bazaar.py +pip/vcs/git.py +pip/vcs/mercurial.py +pip/vcs/subversion.py \ No newline at end of file diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/dependency_links.txt b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/entry_points.txt b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/entry_points.txt new file mode 100644 index 0000000..879fd89 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/entry_points.txt @@ -0,0 +1,5 @@ +[console_scripts] +pip = pip:main +pip3 = pip:main +pip3.6 = pip:main + diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/not-zip-safe b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/not-zip-safe @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/requires.txt b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/requires.txt new file mode 100644 index 0000000..26b980b --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/requires.txt @@ -0,0 +1,7 @@ + +[testing] +pytest +virtualenv>=1.10 +scripttest>=1.3 +mock +pretend diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/top_level.txt b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/top_level.txt new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/__init__.py new file mode 100644 index 0000000..9c1d8f9 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/__init__.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python +from __future__ import absolute_import + +import locale +import logging +import os +import optparse +import warnings + +import sys +import re + +# 2016-06-17 barry@debian.org: urllib3 1.14 added optional support for socks, +# but if invoked (i.e. imported), it will issue a warning to stderr if socks +# isn't available. requests unconditionally imports urllib3's socks contrib +# module, triggering this warning. The warning breaks DEP-8 tests (because of +# the stderr output) and is just plain annoying in normal usage. I don't want +# to add socks as yet another dependency for pip, nor do I want to allow-stder +# in the DEP-8 tests, so just suppress the warning. pdb tells me this has to +# be done before the import of pip.vcs. +from pip._vendor.requests.packages.urllib3.exceptions import DependencyWarning +warnings.filterwarnings("ignore", category=DependencyWarning) # noqa + + +from pip.exceptions import InstallationError, CommandError, PipError +from pip.utils import get_installed_distributions, get_prog +from pip.utils import deprecation, dist_is_editable +from pip.vcs import git, mercurial, subversion, bazaar # noqa +from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter +from pip.commands import get_summaries, get_similar_commands +from pip.commands import commands_dict +from pip._vendor.requests.packages.urllib3.exceptions import ( + InsecureRequestWarning, +) + + +# assignment for flake8 to be happy + +# This fixes a peculiarity when importing via __import__ - as we are +# initialising the pip module, "from pip import cmdoptions" is recursive +# and appears not to work properly in that situation. +import pip.cmdoptions +cmdoptions = pip.cmdoptions + +# The version as used in the setup.py and the docs conf.py +__version__ = "9.0.1" + + +logger = logging.getLogger(__name__) + +# Hide the InsecureRequestWarning from urllib3 +warnings.filterwarnings("ignore", category=InsecureRequestWarning) + + +def autocomplete(): + """Command and option completion for the main option parser (and options) + and its subcommands (and options). + + Enable by sourcing one of the completion shell scripts (bash, zsh or fish). + """ + # Don't complete if user hasn't sourced bash_completion file. + if 'PIP_AUTO_COMPLETE' not in os.environ: + return + cwords = os.environ['COMP_WORDS'].split()[1:] + cword = int(os.environ['COMP_CWORD']) + try: + current = cwords[cword - 1] + except IndexError: + current = '' + + subcommands = [cmd for cmd, summary in get_summaries()] + options = [] + # subcommand + try: + subcommand_name = [w for w in cwords if w in subcommands][0] + except IndexError: + subcommand_name = None + + parser = create_main_parser() + # subcommand options + if subcommand_name: + # special case: 'help' subcommand has no options + if subcommand_name == 'help': + sys.exit(1) + # special case: list locally installed dists for uninstall command + if subcommand_name == 'uninstall' and not current.startswith('-'): + installed = [] + lc = current.lower() + for dist in get_installed_distributions(local_only=True): + if dist.key.startswith(lc) and dist.key not in cwords[1:]: + installed.append(dist.key) + # if there are no dists installed, fall back to option completion + if installed: + for dist in installed: + print(dist) + sys.exit(1) + + subcommand = commands_dict[subcommand_name]() + options += [(opt.get_opt_string(), opt.nargs) + for opt in subcommand.parser.option_list_all + if opt.help != optparse.SUPPRESS_HELP] + + # filter out previously specified options from available options + prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]] + options = [(x, v) for (x, v) in options if x not in prev_opts] + # filter options by current input + options = [(k, v) for k, v in options if k.startswith(current)] + for option in options: + opt_label = option[0] + # append '=' to options which require args + if option[1]: + opt_label += '=' + print(opt_label) + else: + # show main parser options only when necessary + if current.startswith('-') or current.startswith('--'): + opts = [i.option_list for i in parser.option_groups] + opts.append(parser.option_list) + opts = (o for it in opts for o in it) + + subcommands += [i.get_opt_string() for i in opts + if i.help != optparse.SUPPRESS_HELP] + + print(' '.join([x for x in subcommands if x.startswith(current)])) + sys.exit(1) + + +def create_main_parser(): + parser_kw = { + 'usage': '\n%prog <command> [options]', + 'add_help_option': False, + 'formatter': UpdatingDefaultsHelpFormatter(), + 'name': 'global', + 'prog': get_prog(), + } + + parser = ConfigOptionParser(**parser_kw) + parser.disable_interspersed_args() + + pip_pkg_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + parser.version = 'pip %s from %s (python %s)' % ( + __version__, pip_pkg_dir, sys.version[:3]) + + # add the general options + gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser) + parser.add_option_group(gen_opts) + + parser.main = True # so the help formatter knows + + # create command listing for description + command_summaries = get_summaries() + description = [''] + ['%-27s %s' % (i, j) for i, j in command_summaries] + parser.description = '\n'.join(description) + + return parser + + +def parseopts(args): + parser = create_main_parser() + + # Note: parser calls disable_interspersed_args(), so the result of this + # call is to split the initial args into the general options before the + # subcommand and everything else. + # For example: + # args: ['--timeout=5', 'install', '--user', 'INITools'] + # general_options: ['--timeout==5'] + # args_else: ['install', '--user', 'INITools'] + general_options, args_else = parser.parse_args(args) + + # --version + if general_options.version: + sys.stdout.write(parser.version) + sys.stdout.write(os.linesep) + sys.exit() + + # pip || pip help -> print_help() + if not args_else or (args_else[0] == 'help' and len(args_else) == 1): + parser.print_help() + sys.exit() + + # the subcommand name + cmd_name = args_else[0] + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "%s"' % cmd_name] + if guess: + msg.append('maybe you meant "%s"' % guess) + + raise CommandError(' - '.join(msg)) + + # all the args without the subcommand + cmd_args = args[:] + cmd_args.remove(cmd_name) + + return cmd_name, cmd_args + + +def check_isolated(args): + isolated = False + + if "--isolated" in args: + isolated = True + + return isolated + + +def main(args=None): + if args is None: + args = sys.argv[1:] + + # Configure our deprecation warnings to be sent through loggers + deprecation.install_warning_logger() + + autocomplete() + + try: + cmd_name, cmd_args = parseopts(args) + except PipError as exc: + sys.stderr.write("ERROR: %s" % exc) + sys.stderr.write(os.linesep) + sys.exit(1) + + # Needed for locale.getpreferredencoding(False) to work + # in pip.utils.encoding.auto_decode + try: + locale.setlocale(locale.LC_ALL, '') + except locale.Error as e: + # setlocale can apparently crash if locale are uninitialized + logger.debug("Ignoring error %s when setting locale", e) + command = commands_dict[cmd_name](isolated=check_isolated(cmd_args)) + return command.main(cmd_args) + + +# ########################################################### +# # Writing freeze files + +class FrozenRequirement(object): + + def __init__(self, name, req, editable, comments=()): + self.name = name + self.req = req + self.editable = editable + self.comments = comments + + _rev_re = re.compile(r'-r(\d+)$') + _date_re = re.compile(r'-(20\d\d\d\d\d\d)$') + + @classmethod + def from_dist(cls, dist, dependency_links): + location = os.path.normcase(os.path.abspath(dist.location)) + comments = [] + from pip.vcs import vcs, get_src_requirement + if dist_is_editable(dist) and vcs.get_backend_name(location): + editable = True + try: + req = get_src_requirement(dist, location) + except InstallationError as exc: + logger.warning( + "Error when trying to get requirement for VCS system %s, " + "falling back to uneditable format", exc + ) + req = None + if req is None: + logger.warning( + 'Could not determine repository location of %s', location + ) + comments.append( + '## !! Could not determine repository location' + ) + req = dist.as_requirement() + editable = False + else: + editable = False + req = dist.as_requirement() + specs = req.specs + assert len(specs) == 1 and specs[0][0] in ["==", "==="], \ + 'Expected 1 spec with == or ===; specs = %r; dist = %r' % \ + (specs, dist) + version = specs[0][1] + ver_match = cls._rev_re.search(version) + date_match = cls._date_re.search(version) + if ver_match or date_match: + svn_backend = vcs.get_backend('svn') + if svn_backend: + svn_location = svn_backend().get_location( + dist, + dependency_links, + ) + if not svn_location: + logger.warning( + 'Warning: cannot find svn location for %s', req) + comments.append( + '## FIXME: could not find svn URL in dependency_links ' + 'for this package:' + ) + else: + comments.append( + '# Installing as editable to satisfy requirement %s:' % + req + ) + if ver_match: + rev = ver_match.group(1) + else: + rev = '{%s}' % date_match.group(1) + editable = True + req = '%s@%s#egg=%s' % ( + svn_location, + rev, + cls.egg_name(dist) + ) + return cls(dist.project_name, req, editable, comments) + + @staticmethod + def egg_name(dist): + name = dist.egg_name() + match = re.search(r'-py\d\.\d$', name) + if match: + name = name[:match.start()] + return name + + def __str__(self): + req = self.req + if self.editable: + req = '-e %s' % req + return '\n'.join(list(self.comments) + [str(req)]) + '\n' + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/__main__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/__main__.py new file mode 100644 index 0000000..5556539 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/__main__.py @@ -0,0 +1,19 @@ +from __future__ import absolute_import + +import os +import sys + +# If we are running from a wheel, add the wheel to sys.path +# This allows the usage python pip-*.whl/pip install pip-*.whl +if __package__ == '': + # __file__ is pip-*.whl/pip/__main__.py + # first dirname call strips of '/__main__.py', second strips off '/pip' + # Resulting path is the name of the wheel itself + # Add that to sys.path so we can import pip + path = os.path.dirname(os.path.dirname(__file__)) + sys.path.insert(0, path) + +import pip # noqa + +if __name__ == '__main__': + sys.exit(pip.main()) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/__init__.py new file mode 100644 index 0000000..bee5f5e --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/__init__.py @@ -0,0 +1,107 @@ +""" +pip._vendor is for vendoring dependencies of pip to prevent needing pip to +depend on something external. + +Files inside of pip._vendor should be considered immutable and should only be +updated to versions from upstream. +""" +from __future__ import absolute_import + +import glob +import os.path +import sys + +# Downstream redistributors which have debundled our dependencies should also +# patch this value to be true. This will trigger the additional patching +# to cause things like "six" to be available as pip. +DEBUNDLED = False + +# By default, look in this directory for a bunch of .whl files which we will +# add to the beginning of sys.path before attempting to import anything. This +# is done to support downstream re-distributors like Debian and Fedora who +# wish to create their own Wheels for our dependencies to aid in debundling. +WHEEL_DIR = os.path.abspath(os.path.dirname(__file__)) + + +# Define a small helper function to alias our vendored modules to the real ones +# if the vendored ones do not exist. This idea of this was taken from +# https://github.com/kennethreitz/requests/pull/2567. +def vendored(modulename): + vendored_name = "{0}.{1}".format(__name__, modulename) + + try: + __import__(vendored_name, globals(), locals(), level=0) + except ImportError: + try: + __import__(modulename, globals(), locals(), level=0) + except ImportError: + # We can just silently allow import failures to pass here. If we + # got to this point it means that ``import pip._vendor.whatever`` + # failed and so did ``import whatever``. Since we're importing this + # upfront in an attempt to alias imports, not erroring here will + # just mean we get a regular import error whenever pip *actually* + # tries to import one of these modules to use it, which actually + # gives us a better error message than we would have otherwise + # gotten. + pass + else: + sys.modules[vendored_name] = sys.modules[modulename] + base, head = vendored_name.rsplit(".", 1) + setattr(sys.modules[base], head, sys.modules[modulename]) + + +# If we're operating in a debundled setup, then we want to go ahead and trigger +# the aliasing of our vendored libraries as well as looking for wheels to add +# to our sys.path. This will cause all of this code to be a no-op typically +# however downstream redistributors can enable it in a consistent way across +# all platforms. +if DEBUNDLED: + # Actually look inside of WHEEL_DIR to find .whl files and add them to the + # front of our sys.path. + sys.path[:] = glob.glob(os.path.join(WHEEL_DIR, "*.whl")) + sys.path + + # Actually alias all of our vendored dependencies. + vendored("cachecontrol") + vendored("colorama") + vendored("distlib") + vendored("distro") + vendored("html5lib") + vendored("lockfile") + vendored("six") + vendored("six.moves") + vendored("six.moves.urllib") + vendored("packaging") + vendored("packaging.version") + vendored("packaging.specifiers") + vendored("pkg_resources") + vendored("progress") + vendored("retrying") + vendored("requests") + vendored("requests.packages") + vendored("requests.packages.urllib3") + vendored("requests.packages.urllib3._collections") + vendored("requests.packages.urllib3.connection") + vendored("requests.packages.urllib3.connectionpool") + vendored("requests.packages.urllib3.contrib") + vendored("requests.packages.urllib3.contrib.ntlmpool") + vendored("requests.packages.urllib3.contrib.pyopenssl") + vendored("requests.packages.urllib3.exceptions") + vendored("requests.packages.urllib3.fields") + vendored("requests.packages.urllib3.filepost") + vendored("requests.packages.urllib3.packages") + vendored("requests.packages.urllib3.packages.ordered_dict") + vendored("requests.packages.urllib3.packages.six") + vendored("requests.packages.urllib3.packages.ssl_match_hostname") + vendored("requests.packages.urllib3.packages.ssl_match_hostname." + "_implementation") + vendored("requests.packages.urllib3.poolmanager") + vendored("requests.packages.urllib3.request") + vendored("requests.packages.urllib3.response") + vendored("requests.packages.urllib3.util") + vendored("requests.packages.urllib3.util.connection") + vendored("requests.packages.urllib3.util.request") + vendored("requests.packages.urllib3.util.response") + vendored("requests.packages.urllib3.util.retry") + vendored("requests.packages.urllib3.util.ssl_") + vendored("requests.packages.urllib3.util.timeout") + vendored("requests.packages.urllib3.util.url") diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/appdirs.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/appdirs.py new file mode 100644 index 0000000..4b5c38b --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/appdirs.py @@ -0,0 +1,552 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2010 ActiveState Software Inc. +# Copyright (c) 2013 Eddy Petrișor + +"""Utilities for determining application-specific dirs. + +See <http://github.com/ActiveState/appdirs> for details and usage. +""" +# Dev Notes: +# - MSDN on where to store app data files: +# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 +# - macOS: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html +# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + +__version_info__ = (1, 4, 0) +__version__ = '.'.join(map(str, __version_info__)) + + +import sys +import os + +PY3 = sys.version_info[0] == 3 + +if PY3: + unicode = str + +if sys.platform.startswith('java'): + import platform + os_name = platform.java_ver()[3][0] + if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. + system = 'win32' + elif os_name.startswith('Mac'): # "macOS", etc. + system = 'darwin' + else: # "Linux", "SunOS", "FreeBSD", etc. + # Setting this to "linux2" is not ideal, but only Windows or Mac + # are actually checked for and the rest of the module expects + # *sys.platform* style strings. + system = 'linux2' +else: + system = sys.platform + + + +def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user data directories are: + macOS: ~/Library/Application Support/<AppName> + Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName> + Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName> + Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName> + Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName> + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/<AppName>". + """ + if system == "win32": + if appauthor is None: + appauthor = appname + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(_get_win_folder(const)) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('~/Library/Application Support/') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): + """Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of data dirs should be + returned. By default, the first item from XDG_DATA_DIRS is + returned, or '/usr/local/share/<AppName>', + if XDG_DATA_DIRS is not set + + Typical user data directories are: + macOS: /Library/Application Support/<AppName> + Unix: /usr/local/share/<AppName> or /usr/share/<AppName> + Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName> + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7. + + For Unix, this is using the $XDG_DATA_DIRS[0] default. + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('/Library/Application Support') + if appname: + path = os.path.join(path, appname) + else: + # XDG default for $XDG_DATA_DIRS + # only first, if multipath is False + path = os.getenv('XDG_DATA_DIRS', + os.pathsep.join(['/usr/local/share', '/usr/share'])) + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + if appname and version: + path = os.path.join(path, version) + return path + + +def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user data directories are: + macOS: same as user_data_dir + Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by deafult "~/.config/<AppName>". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): + """Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of config dirs should be + returned. By default, the first item from XDG_CONFIG_DIRS is + returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set + + Typical user data directories are: + macOS: same as site_data_dir + Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in + $XDG_CONFIG_DIRS + Win *: same as site_data_dir + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + + For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system in ["win32", "darwin"]: + path = site_data_dir(appname, appauthor) + if appname and version: + path = os.path.join(path, version) + else: + # XDG default for $XDG_CONFIG_DIRS + # only first, if multipath is False + path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + +def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Cache" to the base app data dir for Windows. See + discussion below. + + Typical user cache directories are: + macOS: ~/Library/Caches/<AppName> + Unix: ~/.cache/<AppName> (XDG default) + Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache + Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go in + the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming + app data dir (the default returned by `user_data_dir` above). Apps typically + put cache data somewhere *under* the given dir here. Some examples: + ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache + ...\Acme\SuperApp\Cache\1.0 + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + This can be disabled with the `opinion=False` option. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + if opinion: + path = os.path.join(path, "Cache") + elif system == 'darwin': + path = os.path.expanduser('~/Library/Caches') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific log dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Logs" to the base app data dir for Windows, and "log" to the + base cache dir for Unix. See discussion below. + + Typical user cache directories are: + macOS: ~/Library/Logs/<AppName> + Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs + Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs + + On Windows the only suggestion in the MSDN docs is that local settings + go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in + examples of what some windows apps use for a logs dir.) + + OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` + value for Windows and appends "log" to the user cache dir for Unix. + This can be disabled with the `opinion=False` option. + """ + if system == "darwin": + path = os.path.join( + os.path.expanduser('~/Library/Logs'), + appname) + elif system == "win32": + path = user_data_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "Logs") + else: + path = user_cache_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "log") + if appname and version: + path = os.path.join(path, version) + return path + + +class AppDirs(object): + """Convenience wrapper for getting application dirs.""" + def __init__(self, appname, appauthor=None, version=None, roaming=False, + multipath=False): + self.appname = appname + self.appauthor = appauthor + self.version = version + self.roaming = roaming + self.multipath = multipath + + @property + def user_data_dir(self): + return user_data_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_data_dir(self): + return site_data_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_config_dir(self): + return user_config_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_config_dir(self): + return site_config_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_cache_dir(self): + return user_cache_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_log_dir(self): + return user_log_dir(self.appname, self.appauthor, + version=self.version) + + +#---- internal support stuff + +def _get_win_folder_from_registry(csidl_name): + """This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" + ) + dir, type = _winreg.QueryValueEx(key, shell_folder_name) + return dir + + +def _get_win_folder_with_pywin32(csidl_name): + from win32com.shell import shellcon, shell + dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) + # Try to make this a unicode path because SHGetFolderPath does + # not return unicode strings when there is unicode data in the + # path. + try: + dir = unicode(dir) + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + try: + import win32api + dir = win32api.GetShortPathName(dir) + except ImportError: + pass + except UnicodeError: + pass + return dir + + +def _get_win_folder_with_ctypes(csidl_name): + import ctypes + + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + +def _get_win_folder_with_jna(csidl_name): + import array + from com.sun import jna + from com.sun.jna.platform import win32 + + buf_size = win32.WinDef.MAX_PATH * 2 + buf = array.zeros('c', buf_size) + shell = win32.Shell32.INSTANCE + shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf = array.zeros('c', buf_size) + kernel = win32.Kernel32.INSTANCE + if kernal.GetShortPathName(dir, buf, buf_size): + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + return dir + +if system == "win32": + try: + import win32com.shell + _get_win_folder = _get_win_folder_with_pywin32 + except ImportError: + try: + from ctypes import windll + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + try: + import com.sun.jna + _get_win_folder = _get_win_folder_with_jna + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +#---- self test code + +if __name__ == "__main__": + appname = "MyApp" + appauthor = "MyCompany" + + props = ("user_data_dir", "site_data_dir", + "user_config_dir", "site_config_dir", + "user_cache_dir", "user_log_dir") + + print("-- app dirs (with optional 'version')") + dirs = AppDirs(appname, appauthor, version="1.0") + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'version')") + dirs = AppDirs(appname, appauthor) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'appauthor')") + dirs = AppDirs(appname) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (with disabled 'appauthor')") + dirs = AppDirs(appname, appauthor=False) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/__init__.py new file mode 100644 index 0000000..ec9da2e --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/__init__.py @@ -0,0 +1,11 @@ +"""CacheControl import Interface. + +Make it easy to import from cachecontrol without long namespaces. +""" +__author__ = 'Eric Larson' +__email__ = 'eric@ionrock.org' +__version__ = '0.11.7' + +from .wrapper import CacheControl +from .adapter import CacheControlAdapter +from .controller import CacheController diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/_cmd.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/_cmd.py new file mode 100644 index 0000000..afdcc88 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/_cmd.py @@ -0,0 +1,60 @@ +import logging + +from pip._vendor import requests + +from pip._vendor.cachecontrol.adapter import CacheControlAdapter +from pip._vendor.cachecontrol.cache import DictCache +from pip._vendor.cachecontrol.controller import logger + +from argparse import ArgumentParser + + +def setup_logging(): + logger.setLevel(logging.DEBUG) + handler = logging.StreamHandler() + logger.addHandler(handler) + + +def get_session(): + adapter = CacheControlAdapter( + DictCache(), + cache_etags=True, + serializer=None, + heuristic=None, + ) + sess = requests.Session() + sess.mount('http://', adapter) + sess.mount('https://', adapter) + + sess.cache_controller = adapter.controller + return sess + + +def get_args(): + parser = ArgumentParser() + parser.add_argument('url', help='The URL to try and cache') + return parser.parse_args() + + +def main(args=None): + args = get_args() + sess = get_session() + + # Make a request to get a response + resp = sess.get(args.url) + + # Turn on logging + setup_logging() + + # try setting the cache + sess.cache_controller.cache_response(resp.request, resp.raw) + + # Now try to get it + if sess.cache_controller.cached_request(resp.request): + print('Cached!') + else: + print('Not cached :(') + + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/adapter.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/adapter.py new file mode 100644 index 0000000..2348856 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/adapter.py @@ -0,0 +1,125 @@ +import types +import functools + +from pip._vendor.requests.adapters import HTTPAdapter + +from .controller import CacheController +from .cache import DictCache +from .filewrapper import CallbackFileWrapper + + +class CacheControlAdapter(HTTPAdapter): + invalidating_methods = set(['PUT', 'DELETE']) + + def __init__(self, cache=None, + cache_etags=True, + controller_class=None, + serializer=None, + heuristic=None, + *args, **kw): + super(CacheControlAdapter, self).__init__(*args, **kw) + self.cache = cache or DictCache() + self.heuristic = heuristic + + controller_factory = controller_class or CacheController + self.controller = controller_factory( + self.cache, + cache_etags=cache_etags, + serializer=serializer, + ) + + def send(self, request, **kw): + """ + Send a request. Use the request information to see if it + exists in the cache and cache the response if we need to and can. + """ + if request.method == 'GET': + cached_response = self.controller.cached_request(request) + if cached_response: + return self.build_response(request, cached_response, + from_cache=True) + + # check for etags and add headers if appropriate + request.headers.update( + self.controller.conditional_headers(request) + ) + + resp = super(CacheControlAdapter, self).send(request, **kw) + + return resp + + def build_response(self, request, response, from_cache=False): + """ + Build a response by making a request or using the cache. + + This will end up calling send and returning a potentially + cached response + """ + if not from_cache and request.method == 'GET': + # Check for any heuristics that might update headers + # before trying to cache. + if self.heuristic: + response = self.heuristic.apply(response) + + # apply any expiration heuristics + if response.status == 304: + # We must have sent an ETag request. This could mean + # that we've been expired already or that we simply + # have an etag. In either case, we want to try and + # update the cache if that is the case. + cached_response = self.controller.update_cached_response( + request, response + ) + + if cached_response is not response: + from_cache = True + + # We are done with the server response, read a + # possible response body (compliant servers will + # not return one, but we cannot be 100% sure) and + # release the connection back to the pool. + response.read(decode_content=False) + response.release_conn() + + response = cached_response + + # We always cache the 301 responses + elif response.status == 301: + self.controller.cache_response(request, response) + else: + # Wrap the response file with a wrapper that will cache the + # response when the stream has been consumed. + response._fp = CallbackFileWrapper( + response._fp, + functools.partial( + self.controller.cache_response, + request, + response, + ) + ) + if response.chunked: + super_update_chunk_length = response._update_chunk_length + + def _update_chunk_length(self): + super_update_chunk_length() + if self.chunk_left == 0: + self._fp._close() + response._update_chunk_length = types.MethodType(_update_chunk_length, response) + + resp = super(CacheControlAdapter, self).build_response( + request, response + ) + + # See if we should invalidate the cache. + if request.method in self.invalidating_methods and resp.ok: + cache_url = self.controller.cache_url(request.url) + self.cache.delete(cache_url) + + # Give the request a from_cache attr to let people use it + resp.from_cache = from_cache + + return resp + + def close(self): + self.cache.close() + super(CacheControlAdapter, self).close() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/cache.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/cache.py new file mode 100644 index 0000000..7389a73 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/cache.py @@ -0,0 +1,39 @@ +""" +The cache object API for implementing caches. The default is a thread +safe in-memory dictionary. +""" +from threading import Lock + + +class BaseCache(object): + + def get(self, key): + raise NotImplemented() + + def set(self, key, value): + raise NotImplemented() + + def delete(self, key): + raise NotImplemented() + + def close(self): + pass + + +class DictCache(BaseCache): + + def __init__(self, init_dict=None): + self.lock = Lock() + self.data = init_dict or {} + + def get(self, key): + return self.data.get(key, None) + + def set(self, key, value): + with self.lock: + self.data.update({key: value}) + + def delete(self, key): + with self.lock: + if key in self.data: + self.data.pop(key) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/caches/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/caches/__init__.py new file mode 100644 index 0000000..f9e66a1 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/caches/__init__.py @@ -0,0 +1,18 @@ +from textwrap import dedent + +try: + from .file_cache import FileCache +except ImportError: + notice = dedent(''' + NOTE: In order to use the FileCache you must have + lockfile installed. You can install it via pip: + pip install lockfile + ''') + print(notice) + + +try: + import redis + from .redis_cache import RedisCache +except ImportError: + pass diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/caches/file_cache.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/caches/file_cache.py new file mode 100644 index 0000000..b77728f --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/caches/file_cache.py @@ -0,0 +1,116 @@ +import hashlib +import os + +from pip._vendor.lockfile import LockFile +from pip._vendor.lockfile.mkdirlockfile import MkdirLockFile + +from ..cache import BaseCache +from ..controller import CacheController + + +def _secure_open_write(filename, fmode): + # We only want to write to this file, so open it in write only mode + flags = os.O_WRONLY + + # os.O_CREAT | os.O_EXCL will fail if the file already exists, so we only + # will open *new* files. + # We specify this because we want to ensure that the mode we pass is the + # mode of the file. + flags |= os.O_CREAT | os.O_EXCL + + # Do not follow symlinks to prevent someone from making a symlink that + # we follow and insecurely open a cache file. + if hasattr(os, "O_NOFOLLOW"): + flags |= os.O_NOFOLLOW + + # On Windows we'll mark this file as binary + if hasattr(os, "O_BINARY"): + flags |= os.O_BINARY + + # Before we open our file, we want to delete any existing file that is + # there + try: + os.remove(filename) + except (IOError, OSError): + # The file must not exist already, so we can just skip ahead to opening + pass + + # Open our file, the use of os.O_CREAT | os.O_EXCL will ensure that if a + # race condition happens between the os.remove and this line, that an + # error will be raised. Because we utilize a lockfile this should only + # happen if someone is attempting to attack us. + fd = os.open(filename, flags, fmode) + try: + return os.fdopen(fd, "wb") + except: + # An error occurred wrapping our FD in a file object + os.close(fd) + raise + + +class FileCache(BaseCache): + def __init__(self, directory, forever=False, filemode=0o0600, + dirmode=0o0700, use_dir_lock=None, lock_class=None): + + if use_dir_lock is not None and lock_class is not None: + raise ValueError("Cannot use use_dir_lock and lock_class together") + + if use_dir_lock: + lock_class = MkdirLockFile + + if lock_class is None: + lock_class = LockFile + + self.directory = directory + self.forever = forever + self.filemode = filemode + self.dirmode = dirmode + self.lock_class = lock_class + + + @staticmethod + def encode(x): + return hashlib.sha224(x.encode()).hexdigest() + + def _fn(self, name): + # NOTE: This method should not change as some may depend on it. + # See: https://github.com/ionrock/cachecontrol/issues/63 + hashed = self.encode(name) + parts = list(hashed[:5]) + [hashed] + return os.path.join(self.directory, *parts) + + def get(self, key): + name = self._fn(key) + if not os.path.exists(name): + return None + + with open(name, 'rb') as fh: + return fh.read() + + def set(self, key, value): + name = self._fn(key) + + # Make sure the directory exists + try: + os.makedirs(os.path.dirname(name), self.dirmode) + except (IOError, OSError): + pass + + with self.lock_class(name) as lock: + # Write our actual file + with _secure_open_write(lock.path, self.filemode) as fh: + fh.write(value) + + def delete(self, key): + name = self._fn(key) + if not self.forever: + os.remove(name) + + +def url_to_file_path(url, filecache): + """Return the file cache path based on the URL. + + This does not ensure the file exists! + """ + key = CacheController.cache_url(url) + return filecache._fn(key) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/caches/redis_cache.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/caches/redis_cache.py new file mode 100644 index 0000000..9f5d55f --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/caches/redis_cache.py @@ -0,0 +1,41 @@ +from __future__ import division + +from datetime import datetime + + +def total_seconds(td): + """Python 2.6 compatability""" + if hasattr(td, 'total_seconds'): + return td.total_seconds() + + ms = td.microseconds + secs = (td.seconds + td.days * 24 * 3600) + return (ms + secs * 10**6) / 10**6 + + +class RedisCache(object): + + def __init__(self, conn): + self.conn = conn + + def get(self, key): + return self.conn.get(key) + + def set(self, key, value, expires=None): + if not expires: + self.conn.set(key, value) + else: + expires = expires - datetime.now() + self.conn.setex(key, total_seconds(expires), value) + + def delete(self, key): + self.conn.delete(key) + + def clear(self): + """Helper for clearing all the keys in a database. Use with + caution!""" + for key in self.conn.keys(): + self.conn.delete(key) + + def close(self): + self.conn.disconnect() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/compat.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/compat.py new file mode 100644 index 0000000..018e6ac --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/compat.py @@ -0,0 +1,20 @@ +try: + from urllib.parse import urljoin +except ImportError: + from urlparse import urljoin + + +try: + import cPickle as pickle +except ImportError: + import pickle + + +from pip._vendor.requests.packages.urllib3.response import HTTPResponse +from pip._vendor.requests.packages.urllib3.util import is_fp_closed + +# Replicate some six behaviour +try: + text_type = (unicode,) +except NameError: + text_type = (str,) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/controller.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/controller.py new file mode 100644 index 0000000..5eb961f --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/controller.py @@ -0,0 +1,353 @@ +""" +The httplib2 algorithms ported for use with requests. +""" +import logging +import re +import calendar +import time +from email.utils import parsedate_tz + +from pip._vendor.requests.structures import CaseInsensitiveDict + +from .cache import DictCache +from .serialize import Serializer + + +logger = logging.getLogger(__name__) + +URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?") + + +def parse_uri(uri): + """Parses a URI using the regex given in Appendix B of RFC 3986. + + (scheme, authority, path, query, fragment) = parse_uri(uri) + """ + groups = URI.match(uri).groups() + return (groups[1], groups[3], groups[4], groups[6], groups[8]) + + +class CacheController(object): + """An interface to see if request should cached or not. + """ + def __init__(self, cache=None, cache_etags=True, serializer=None): + self.cache = cache or DictCache() + self.cache_etags = cache_etags + self.serializer = serializer or Serializer() + + @classmethod + def _urlnorm(cls, uri): + """Normalize the URL to create a safe key for the cache""" + (scheme, authority, path, query, fragment) = parse_uri(uri) + if not scheme or not authority: + raise Exception("Only absolute URIs are allowed. uri = %s" % uri) + + scheme = scheme.lower() + authority = authority.lower() + + if not path: + path = "/" + + # Could do syntax based normalization of the URI before + # computing the digest. See Section 6.2.2 of Std 66. + request_uri = query and "?".join([path, query]) or path + defrag_uri = scheme + "://" + authority + request_uri + + return defrag_uri + + @classmethod + def cache_url(cls, uri): + return cls._urlnorm(uri) + + def parse_cache_control(self, headers): + """ + Parse the cache control headers returning a dictionary with values + for the different directives. + """ + retval = {} + + cc_header = 'cache-control' + if 'Cache-Control' in headers: + cc_header = 'Cache-Control' + + if cc_header in headers: + parts = headers[cc_header].split(',') + parts_with_args = [ + tuple([x.strip().lower() for x in part.split("=", 1)]) + for part in parts if -1 != part.find("=") + ] + parts_wo_args = [ + (name.strip().lower(), 1) + for name in parts if -1 == name.find("=") + ] + retval = dict(parts_with_args + parts_wo_args) + return retval + + def cached_request(self, request): + """ + Return a cached response if it exists in the cache, otherwise + return False. + """ + cache_url = self.cache_url(request.url) + logger.debug('Looking up "%s" in the cache', cache_url) + cc = self.parse_cache_control(request.headers) + + # Bail out if the request insists on fresh data + if 'no-cache' in cc: + logger.debug('Request header has "no-cache", cache bypassed') + return False + + if 'max-age' in cc and cc['max-age'] == 0: + logger.debug('Request header has "max_age" as 0, cache bypassed') + return False + + # Request allows serving from the cache, let's see if we find something + cache_data = self.cache.get(cache_url) + if cache_data is None: + logger.debug('No cache entry available') + return False + + # Check whether it can be deserialized + resp = self.serializer.loads(request, cache_data) + if not resp: + logger.warning('Cache entry deserialization failed, entry ignored') + return False + + # If we have a cached 301, return it immediately. We don't + # need to test our response for other headers b/c it is + # intrinsically "cacheable" as it is Permanent. + # See: + # https://tools.ietf.org/html/rfc7231#section-6.4.2 + # + # Client can try to refresh the value by repeating the request + # with cache busting headers as usual (ie no-cache). + if resp.status == 301: + msg = ('Returning cached "301 Moved Permanently" response ' + '(ignoring date and etag information)') + logger.debug(msg) + return resp + + headers = CaseInsensitiveDict(resp.headers) + if not headers or 'date' not in headers: + if 'etag' not in headers: + # Without date or etag, the cached response can never be used + # and should be deleted. + logger.debug('Purging cached response: no date or etag') + self.cache.delete(cache_url) + logger.debug('Ignoring cached response: no date') + return False + + now = time.time() + date = calendar.timegm( + parsedate_tz(headers['date']) + ) + current_age = max(0, now - date) + logger.debug('Current age based on date: %i', current_age) + + # TODO: There is an assumption that the result will be a + # urllib3 response object. This may not be best since we + # could probably avoid instantiating or constructing the + # response until we know we need it. + resp_cc = self.parse_cache_control(headers) + + # determine freshness + freshness_lifetime = 0 + + # Check the max-age pragma in the cache control header + if 'max-age' in resp_cc and resp_cc['max-age'].isdigit(): + freshness_lifetime = int(resp_cc['max-age']) + logger.debug('Freshness lifetime from max-age: %i', + freshness_lifetime) + + # If there isn't a max-age, check for an expires header + elif 'expires' in headers: + expires = parsedate_tz(headers['expires']) + if expires is not None: + expire_time = calendar.timegm(expires) - date + freshness_lifetime = max(0, expire_time) + logger.debug("Freshness lifetime from expires: %i", + freshness_lifetime) + + # Determine if we are setting freshness limit in the + # request. Note, this overrides what was in the response. + if 'max-age' in cc: + try: + freshness_lifetime = int(cc['max-age']) + logger.debug('Freshness lifetime from request max-age: %i', + freshness_lifetime) + except ValueError: + freshness_lifetime = 0 + + if 'min-fresh' in cc: + try: + min_fresh = int(cc['min-fresh']) + except ValueError: + min_fresh = 0 + # adjust our current age by our min fresh + current_age += min_fresh + logger.debug('Adjusted current age from min-fresh: %i', + current_age) + + # Return entry if it is fresh enough + if freshness_lifetime > current_age: + logger.debug('The response is "fresh", returning cached response') + logger.debug('%i > %i', freshness_lifetime, current_age) + return resp + + # we're not fresh. If we don't have an Etag, clear it out + if 'etag' not in headers: + logger.debug( + 'The cached response is "stale" with no etag, purging' + ) + self.cache.delete(cache_url) + + # return the original handler + return False + + def conditional_headers(self, request): + cache_url = self.cache_url(request.url) + resp = self.serializer.loads(request, self.cache.get(cache_url)) + new_headers = {} + + if resp: + headers = CaseInsensitiveDict(resp.headers) + + if 'etag' in headers: + new_headers['If-None-Match'] = headers['ETag'] + + if 'last-modified' in headers: + new_headers['If-Modified-Since'] = headers['Last-Modified'] + + return new_headers + + def cache_response(self, request, response, body=None): + """ + Algorithm for caching requests. + + This assumes a requests Response object. + """ + # From httplib2: Don't cache 206's since we aren't going to + # handle byte range requests + cacheable_status_codes = [200, 203, 300, 301] + if response.status not in cacheable_status_codes: + logger.debug( + 'Status code %s not in %s', + response.status, + cacheable_status_codes + ) + return + + response_headers = CaseInsensitiveDict(response.headers) + + # If we've been given a body, our response has a Content-Length, that + # Content-Length is valid then we can check to see if the body we've + # been given matches the expected size, and if it doesn't we'll just + # skip trying to cache it. + if (body is not None and + "content-length" in response_headers and + response_headers["content-length"].isdigit() and + int(response_headers["content-length"]) != len(body)): + return + + cc_req = self.parse_cache_control(request.headers) + cc = self.parse_cache_control(response_headers) + + cache_url = self.cache_url(request.url) + logger.debug('Updating cache with response from "%s"', cache_url) + + # Delete it from the cache if we happen to have it stored there + no_store = False + if cc.get('no-store'): + no_store = True + logger.debug('Response header has "no-store"') + if cc_req.get('no-store'): + no_store = True + logger.debug('Request header has "no-store"') + if no_store and self.cache.get(cache_url): + logger.debug('Purging existing cache entry to honor "no-store"') + self.cache.delete(cache_url) + + # If we've been given an etag, then keep the response + if self.cache_etags and 'etag' in response_headers: + logger.debug('Caching due to etag') + self.cache.set( + cache_url, + self.serializer.dumps(request, response, body=body), + ) + + # Add to the cache any 301s. We do this before looking that + # the Date headers. + elif response.status == 301: + logger.debug('Caching permanant redirect') + self.cache.set( + cache_url, + self.serializer.dumps(request, response) + ) + + # Add to the cache if the response headers demand it. If there + # is no date header then we can't do anything about expiring + # the cache. + elif 'date' in response_headers: + # cache when there is a max-age > 0 + if cc and cc.get('max-age'): + if cc['max-age'].isdigit() and int(cc['max-age']) > 0: + logger.debug('Caching b/c date exists and max-age > 0') + self.cache.set( + cache_url, + self.serializer.dumps(request, response, body=body), + ) + + # If the request can expire, it means we should cache it + # in the meantime. + elif 'expires' in response_headers: + if response_headers['expires']: + logger.debug('Caching b/c of expires header') + self.cache.set( + cache_url, + self.serializer.dumps(request, response, body=body), + ) + + def update_cached_response(self, request, response): + """On a 304 we will get a new set of headers that we want to + update our cached value with, assuming we have one. + + This should only ever be called when we've sent an ETag and + gotten a 304 as the response. + """ + cache_url = self.cache_url(request.url) + + cached_response = self.serializer.loads( + request, + self.cache.get(cache_url) + ) + + if not cached_response: + # we didn't have a cached response + return response + + # Lets update our headers with the headers from the new request: + # http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-4.1 + # + # The server isn't supposed to send headers that would make + # the cached body invalid. But... just in case, we'll be sure + # to strip out ones we know that might be problmatic due to + # typical assumptions. + excluded_headers = [ + "content-length", + ] + + cached_response.headers.update( + dict((k, v) for k, v in response.headers.items() + if k.lower() not in excluded_headers) + ) + + # we want a 200 b/c we have content via the cache + cached_response.status = 200 + + # update our cache + self.cache.set( + cache_url, + self.serializer.dumps(request, cached_response), + ) + + return cached_response diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/filewrapper.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/filewrapper.py new file mode 100644 index 0000000..f1e1ce0 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/filewrapper.py @@ -0,0 +1,78 @@ +from io import BytesIO + + +class CallbackFileWrapper(object): + """ + Small wrapper around a fp object which will tee everything read into a + buffer, and when that file is closed it will execute a callback with the + contents of that buffer. + + All attributes are proxied to the underlying file object. + + This class uses members with a double underscore (__) leading prefix so as + not to accidentally shadow an attribute. + """ + + def __init__(self, fp, callback): + self.__buf = BytesIO() + self.__fp = fp + self.__callback = callback + + def __getattr__(self, name): + # The vaguaries of garbage collection means that self.__fp is + # not always set. By using __getattribute__ and the private + # name[0] allows looking up the attribute value and raising an + # AttributeError when it doesn't exist. This stop thigns from + # infinitely recursing calls to getattr in the case where + # self.__fp hasn't been set. + # + # [0] https://docs.python.org/2/reference/expressions.html#atom-identifiers + fp = self.__getattribute__('_CallbackFileWrapper__fp') + return getattr(fp, name) + + def __is_fp_closed(self): + try: + return self.__fp.fp is None + except AttributeError: + pass + + try: + return self.__fp.closed + except AttributeError: + pass + + # We just don't cache it then. + # TODO: Add some logging here... + return False + + def _close(self): + if self.__callback: + self.__callback(self.__buf.getvalue()) + + # We assign this to None here, because otherwise we can get into + # really tricky problems where the CPython interpreter dead locks + # because the callback is holding a reference to something which + # has a __del__ method. Setting this to None breaks the cycle + # and allows the garbage collector to do it's thing normally. + self.__callback = None + + def read(self, amt=None): + data = self.__fp.read(amt) + self.__buf.write(data) + if self.__is_fp_closed(): + self._close() + + return data + + def _safe_read(self, amt): + data = self.__fp._safe_read(amt) + if amt == 2 and data == b'\r\n': + # urllib executes this read to toss the CRLF at the end + # of the chunk. + return data + + self.__buf.write(data) + if self.__is_fp_closed(): + self._close() + + return data diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/heuristics.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/heuristics.py new file mode 100644 index 0000000..94715a4 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/heuristics.py @@ -0,0 +1,138 @@ +import calendar +import time + +from email.utils import formatdate, parsedate, parsedate_tz + +from datetime import datetime, timedelta + +TIME_FMT = "%a, %d %b %Y %H:%M:%S GMT" + + +def expire_after(delta, date=None): + date = date or datetime.now() + return date + delta + + +def datetime_to_header(dt): + return formatdate(calendar.timegm(dt.timetuple())) + + +class BaseHeuristic(object): + + def warning(self, response): + """ + Return a valid 1xx warning header value describing the cache + adjustments. + + The response is provided too allow warnings like 113 + http://tools.ietf.org/html/rfc7234#section-5.5.4 where we need + to explicitly say response is over 24 hours old. + """ + return '110 - "Response is Stale"' + + def update_headers(self, response): + """Update the response headers with any new headers. + + NOTE: This SHOULD always include some Warning header to + signify that the response was cached by the client, not + by way of the provided headers. + """ + return {} + + def apply(self, response): + updated_headers = self.update_headers(response) + + if updated_headers: + response.headers.update(updated_headers) + warning_header_value = self.warning(response) + if warning_header_value is not None: + response.headers.update({'Warning': warning_header_value}) + + return response + + +class OneDayCache(BaseHeuristic): + """ + Cache the response by providing an expires 1 day in the + future. + """ + def update_headers(self, response): + headers = {} + + if 'expires' not in response.headers: + date = parsedate(response.headers['date']) + expires = expire_after(timedelta(days=1), + date=datetime(*date[:6])) + headers['expires'] = datetime_to_header(expires) + headers['cache-control'] = 'public' + return headers + + +class ExpiresAfter(BaseHeuristic): + """ + Cache **all** requests for a defined time period. + """ + + def __init__(self, **kw): + self.delta = timedelta(**kw) + + def update_headers(self, response): + expires = expire_after(self.delta) + return { + 'expires': datetime_to_header(expires), + 'cache-control': 'public', + } + + def warning(self, response): + tmpl = '110 - Automatically cached for %s. Response might be stale' + return tmpl % self.delta + + +class LastModified(BaseHeuristic): + """ + If there is no Expires header already, fall back on Last-Modified + using the heuristic from + http://tools.ietf.org/html/rfc7234#section-4.2.2 + to calculate a reasonable value. + + Firefox also does something like this per + https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ + http://lxr.mozilla.org/mozilla-release/source/netwerk/protocol/http/nsHttpResponseHead.cpp#397 + Unlike mozilla we limit this to 24-hr. + """ + cacheable_by_default_statuses = set([ + 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501 + ]) + + def update_headers(self, resp): + headers = resp.headers + + if 'expires' in headers: + return {} + + if 'cache-control' in headers and headers['cache-control'] != 'public': + return {} + + if resp.status not in self.cacheable_by_default_statuses: + return {} + + if 'date' not in headers or 'last-modified' not in headers: + return {} + + date = calendar.timegm(parsedate_tz(headers['date'])) + last_modified = parsedate(headers['last-modified']) + if date is None or last_modified is None: + return {} + + now = time.time() + current_age = max(0, now - date) + delta = date - calendar.timegm(last_modified) + freshness_lifetime = max(0, min(delta / 10, 24 * 3600)) + if freshness_lifetime <= current_age: + return {} + + expires = date + freshness_lifetime + return {'expires': time.strftime(TIME_FMT, time.gmtime(expires))} + + def warning(self, resp): + return None diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/serialize.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/serialize.py new file mode 100644 index 0000000..8f9c589 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/serialize.py @@ -0,0 +1,196 @@ +import base64 +import io +import json +import zlib + +from pip._vendor.requests.structures import CaseInsensitiveDict + +from .compat import HTTPResponse, pickle, text_type + + +def _b64_encode_bytes(b): + return base64.b64encode(b).decode("ascii") + + +def _b64_encode_str(s): + return _b64_encode_bytes(s.encode("utf8")) + + +def _b64_encode(s): + if isinstance(s, text_type): + return _b64_encode_str(s) + return _b64_encode_bytes(s) + + +def _b64_decode_bytes(b): + return base64.b64decode(b.encode("ascii")) + + +def _b64_decode_str(s): + return _b64_decode_bytes(s).decode("utf8") + + +class Serializer(object): + + def dumps(self, request, response, body=None): + response_headers = CaseInsensitiveDict(response.headers) + + if body is None: + body = response.read(decode_content=False) + + # NOTE: 99% sure this is dead code. I'm only leaving it + # here b/c I don't have a test yet to prove + # it. Basically, before using + # `cachecontrol.filewrapper.CallbackFileWrapper`, + # this made an effort to reset the file handle. The + # `CallbackFileWrapper` short circuits this code by + # setting the body as the content is consumed, the + # result being a `body` argument is *always* passed + # into cache_response, and in turn, + # `Serializer.dump`. + response._fp = io.BytesIO(body) + + data = { + "response": { + "body": _b64_encode_bytes(body), + "headers": dict( + (_b64_encode(k), _b64_encode(v)) + for k, v in response.headers.items() + ), + "status": response.status, + "version": response.version, + "reason": _b64_encode_str(response.reason), + "strict": response.strict, + "decode_content": response.decode_content, + }, + } + + # Construct our vary headers + data["vary"] = {} + if "vary" in response_headers: + varied_headers = response_headers['vary'].split(',') + for header in varied_headers: + header = header.strip() + data["vary"][header] = request.headers.get(header, None) + + # Encode our Vary headers to ensure they can be serialized as JSON + data["vary"] = dict( + (_b64_encode(k), _b64_encode(v) if v is not None else v) + for k, v in data["vary"].items() + ) + + return b",".join([ + b"cc=2", + zlib.compress( + json.dumps( + data, separators=(",", ":"), sort_keys=True, + ).encode("utf8"), + ), + ]) + + def loads(self, request, data): + # Short circuit if we've been given an empty set of data + if not data: + return + + # Determine what version of the serializer the data was serialized + # with + try: + ver, data = data.split(b",", 1) + except ValueError: + ver = b"cc=0" + + # Make sure that our "ver" is actually a version and isn't a false + # positive from a , being in the data stream. + if ver[:3] != b"cc=": + data = ver + data + ver = b"cc=0" + + # Get the version number out of the cc=N + ver = ver.split(b"=", 1)[-1].decode("ascii") + + # Dispatch to the actual load method for the given version + try: + return getattr(self, "_loads_v{0}".format(ver))(request, data) + except AttributeError: + # This is a version we don't have a loads function for, so we'll + # just treat it as a miss and return None + return + + def prepare_response(self, request, cached): + """Verify our vary headers match and construct a real urllib3 + HTTPResponse object. + """ + # Special case the '*' Vary value as it means we cannot actually + # determine if the cached response is suitable for this request. + if "*" in cached.get("vary", {}): + return + + # Ensure that the Vary headers for the cached response match our + # request + for header, value in cached.get("vary", {}).items(): + if request.headers.get(header, None) != value: + return + + body_raw = cached["response"].pop("body") + + headers = CaseInsensitiveDict(data=cached['response']['headers']) + if headers.get('transfer-encoding', '') == 'chunked': + headers.pop('transfer-encoding') + + cached['response']['headers'] = headers + + try: + body = io.BytesIO(body_raw) + except TypeError: + # This can happen if cachecontrol serialized to v1 format (pickle) + # using Python 2. A Python 2 str(byte string) will be unpickled as + # a Python 3 str (unicode string), which will cause the above to + # fail with: + # + # TypeError: 'str' does not support the buffer interface + body = io.BytesIO(body_raw.encode('utf8')) + + return HTTPResponse( + body=body, + preload_content=False, + **cached["response"] + ) + + def _loads_v0(self, request, data): + # The original legacy cache data. This doesn't contain enough + # information to construct everything we need, so we'll treat this as + # a miss. + return + + def _loads_v1(self, request, data): + try: + cached = pickle.loads(data) + except ValueError: + return + + return self.prepare_response(request, cached) + + def _loads_v2(self, request, data): + try: + cached = json.loads(zlib.decompress(data).decode("utf8")) + except ValueError: + return + + # We need to decode the items that we've base64 encoded + cached["response"]["body"] = _b64_decode_bytes( + cached["response"]["body"] + ) + cached["response"]["headers"] = dict( + (_b64_decode_str(k), _b64_decode_str(v)) + for k, v in cached["response"]["headers"].items() + ) + cached["response"]["reason"] = _b64_decode_str( + cached["response"]["reason"], + ) + cached["vary"] = dict( + (_b64_decode_str(k), _b64_decode_str(v) if v is not None else v) + for k, v in cached["vary"].items() + ) + + return self.prepare_response(request, cached) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/wrapper.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/wrapper.py new file mode 100644 index 0000000..ea421aa --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/cachecontrol/wrapper.py @@ -0,0 +1,21 @@ +from .adapter import CacheControlAdapter +from .cache import DictCache + + +def CacheControl(sess, + cache=None, + cache_etags=True, + serializer=None, + heuristic=None): + + cache = cache or DictCache() + adapter = CacheControlAdapter( + cache, + cache_etags=cache_etags, + serializer=serializer, + heuristic=heuristic, + ) + sess.mount('http://', adapter) + sess.mount('https://', adapter) + + return sess diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/__init__.py new file mode 100644 index 0000000..670e6b3 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/__init__.py @@ -0,0 +1,7 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit, colorama_text +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.3.7' + diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/ansi.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/ansi.py new file mode 100644 index 0000000..7877658 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/ansi.py @@ -0,0 +1,102 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' +OSC = '\033]' +BEL = '\007' + + +def code_to_chars(code): + return CSI + str(code) + 'm' + +def set_title(title): + return OSC + '2;' + title + BEL + +def clear_screen(mode=2): + return CSI + str(mode) + 'J' + +def clear_line(mode=2): + return CSI + str(mode) + 'K' + + +class AnsiCodes(object): + def __init__(self): + # the subclasses declare class attributes which are numbers. + # Upon instantiation we define instance attributes, which are the same + # as the class attributes but wrapped with the ANSI escape sequence + for name in dir(self): + if not name.startswith('_'): + value = getattr(self, name) + setattr(self, name, code_to_chars(value)) + + +class AnsiCursor(object): + def UP(self, n=1): + return CSI + str(n) + 'A' + def DOWN(self, n=1): + return CSI + str(n) + 'B' + def FORWARD(self, n=1): + return CSI + str(n) + 'C' + def BACK(self, n=1): + return CSI + str(n) + 'D' + def POS(self, x=1, y=1): + return CSI + str(y) + ';' + str(x) + 'H' + + +class AnsiFore(AnsiCodes): + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 90 + LIGHTRED_EX = 91 + LIGHTGREEN_EX = 92 + LIGHTYELLOW_EX = 93 + LIGHTBLUE_EX = 94 + LIGHTMAGENTA_EX = 95 + LIGHTCYAN_EX = 96 + LIGHTWHITE_EX = 97 + + +class AnsiBack(AnsiCodes): + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 100 + LIGHTRED_EX = 101 + LIGHTGREEN_EX = 102 + LIGHTYELLOW_EX = 103 + LIGHTBLUE_EX = 104 + LIGHTMAGENTA_EX = 105 + LIGHTCYAN_EX = 106 + LIGHTWHITE_EX = 107 + + +class AnsiStyle(AnsiCodes): + BRIGHT = 1 + DIM = 2 + NORMAL = 22 + RESET_ALL = 0 + +Fore = AnsiFore() +Back = AnsiBack() +Style = AnsiStyle() +Cursor = AnsiCursor() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/ansitowin32.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/ansitowin32.py new file mode 100644 index 0000000..b7ff6f2 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/ansitowin32.py @@ -0,0 +1,236 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import re +import sys +import os + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style +from .winterm import WinTerm, WinColor, WinStyle +from .win32 import windll, winapi_test + + +winterm = None +if windll is not None: + winterm = WinTerm() + + +def is_stream_closed(stream): + return not hasattr(stream, 'closed') or stream.closed + + +def is_a_tty(stream): + return hasattr(stream, 'isatty') and stream.isatty() + + +class StreamWrapper(object): + ''' + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()', which is delegated to our + Converter instance. + ''' + def __init__(self, wrapped, converter): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + self.__convertor = converter + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def write(self, text): + self.__convertor.write(text) + + +class AnsiToWin32(object): + ''' + Implements a 'write()' method which, on Windows, will strip ANSI character + sequences from the text, and if outputting to a tty, will convert them into + win32 function calls. + ''' + ANSI_CSI_RE = re.compile('\001?\033\[((?:\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer + ANSI_OSC_RE = re.compile('\001?\033\]((?:.|;)*?)(\x07)\002?') # Operating System Command + + def __init__(self, wrapped, convert=None, strip=None, autoreset=False): + # The wrapped stream (normally sys.stdout or sys.stderr) + self.wrapped = wrapped + + # should we reset colors to defaults after every .write() + self.autoreset = autoreset + + # create the proxy wrapping our output stream + self.stream = StreamWrapper(wrapped, self) + + on_windows = os.name == 'nt' + # We test if the WinAPI works, because even if we are on Windows + # we may be using a terminal that doesn't support the WinAPI + # (e.g. Cygwin Terminal). In this case it's up to the terminal + # to support the ANSI codes. + conversion_supported = on_windows and winapi_test() + + # should we strip ANSI sequences from our output? + if strip is None: + strip = conversion_supported or (not is_stream_closed(wrapped) and not is_a_tty(wrapped)) + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = conversion_supported and not is_stream_closed(wrapped) and is_a_tty(wrapped) + self.convert = convert + + # dict of ansi codes to win32 functions and parameters + self.win32_calls = self.get_win32_calls() + + # are we wrapping stderr? + self.on_stderr = self.wrapped is sys.stderr + + def should_wrap(self): + ''' + True if this class is actually needed. If false, then the output + stream will not be affected, nor will win32 calls be issued, so + wrapping stdout is not actually required. This will generally be + False on non-Windows platforms, unless optional functionality like + autoreset has been requested using kwargs to init() + ''' + return self.convert or self.strip or self.autoreset + + def get_win32_calls(self): + if self.convert and winterm: + return { + AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), + AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), + AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), + AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), + AnsiFore.RED: (winterm.fore, WinColor.RED), + AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), + AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), + AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), + AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), + AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), + AnsiFore.WHITE: (winterm.fore, WinColor.GREY), + AnsiFore.RESET: (winterm.fore, ), + AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), + AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), + AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), + AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), + AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), + AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), + AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), + AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), + AnsiBack.BLACK: (winterm.back, WinColor.BLACK), + AnsiBack.RED: (winterm.back, WinColor.RED), + AnsiBack.GREEN: (winterm.back, WinColor.GREEN), + AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), + AnsiBack.BLUE: (winterm.back, WinColor.BLUE), + AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), + AnsiBack.CYAN: (winterm.back, WinColor.CYAN), + AnsiBack.WHITE: (winterm.back, WinColor.GREY), + AnsiBack.RESET: (winterm.back, ), + AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), + AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), + AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), + AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), + AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), + AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), + AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), + AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), + } + return dict() + + def write(self, text): + if self.strip or self.convert: + self.write_and_convert(text) + else: + self.wrapped.write(text) + self.wrapped.flush() + if self.autoreset: + self.reset_all() + + + def reset_all(self): + if self.convert: + self.call_win32('m', (0,)) + elif not self.strip and not is_stream_closed(self.wrapped): + self.wrapped.write(Style.RESET_ALL) + + + def write_and_convert(self, text): + ''' + Write the given text to our wrapped stream, stripping any ANSI + sequences from the text, and optionally converting them into win32 + calls. + ''' + cursor = 0 + text = self.convert_osc(text) + for match in self.ANSI_CSI_RE.finditer(text): + start, end = match.span() + self.write_plain_text(text, cursor, start) + self.convert_ansi(*match.groups()) + cursor = end + self.write_plain_text(text, cursor, len(text)) + + + def write_plain_text(self, text, start, end): + if start < end: + self.wrapped.write(text[start:end]) + self.wrapped.flush() + + + def convert_ansi(self, paramstring, command): + if self.convert: + params = self.extract_params(command, paramstring) + self.call_win32(command, params) + + + def extract_params(self, command, paramstring): + if command in 'Hf': + params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) + while len(params) < 2: + # defaults: + params = params + (1,) + else: + params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) + if len(params) == 0: + # defaults: + if command in 'JKm': + params = (0,) + elif command in 'ABCD': + params = (1,) + + return params + + + def call_win32(self, command, params): + if command == 'm': + for param in params: + if param in self.win32_calls: + func_args = self.win32_calls[param] + func = func_args[0] + args = func_args[1:] + kwargs = dict(on_stderr=self.on_stderr) + func(*args, **kwargs) + elif command in 'J': + winterm.erase_screen(params[0], on_stderr=self.on_stderr) + elif command in 'K': + winterm.erase_line(params[0], on_stderr=self.on_stderr) + elif command in 'Hf': # cursor position - absolute + winterm.set_cursor_position(params, on_stderr=self.on_stderr) + elif command in 'ABCD': # cursor position - relative + n = params[0] + # A - up, B - down, C - forward, D - back + x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] + winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) + + + def convert_osc(self, text): + for match in self.ANSI_OSC_RE.finditer(text): + start, end = match.span() + text = text[:start] + text[end:] + paramstring, command = match.groups() + if command in '\x07': # \x07 = BEL + params = paramstring.split(";") + # 0 - change title and icon (we will only change title) + # 1 - change icon (we don't support this) + # 2 - change title + if params[0] in '02': + winterm.set_title(params[1]) + return text diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/initialise.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/initialise.py new file mode 100644 index 0000000..834962a --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/initialise.py @@ -0,0 +1,82 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import atexit +import contextlib +import sys + +from .ansitowin32 import AnsiToWin32 + + +orig_stdout = None +orig_stderr = None + +wrapped_stdout = None +wrapped_stderr = None + +atexit_done = False + + +def reset_all(): + if AnsiToWin32 is not None: # Issue #74: objects might become None at exit + AnsiToWin32(orig_stdout).reset_all() + + +def init(autoreset=False, convert=None, strip=None, wrap=True): + + if not wrap and any([autoreset, convert, strip]): + raise ValueError('wrap=False conflicts with any other arg=True') + + global wrapped_stdout, wrapped_stderr + global orig_stdout, orig_stderr + + orig_stdout = sys.stdout + orig_stderr = sys.stderr + + if sys.stdout is None: + wrapped_stdout = None + else: + sys.stdout = wrapped_stdout = \ + wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + if sys.stderr is None: + wrapped_stderr = None + else: + sys.stderr = wrapped_stderr = \ + wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + + global atexit_done + if not atexit_done: + atexit.register(reset_all) + atexit_done = True + + +def deinit(): + if orig_stdout is not None: + sys.stdout = orig_stdout + if orig_stderr is not None: + sys.stderr = orig_stderr + + +@contextlib.contextmanager +def colorama_text(*args, **kwargs): + init(*args, **kwargs) + try: + yield + finally: + deinit() + + +def reinit(): + if wrapped_stdout is not None: + sys.stdout = wrapped_stdout + if wrapped_stderr is not None: + sys.stderr = wrapped_stderr + + +def wrap_stream(stream, convert, strip, autoreset, wrap): + if wrap: + wrapper = AnsiToWin32(stream, + convert=convert, strip=strip, autoreset=autoreset) + if wrapper.should_wrap(): + stream = wrapper.stream + return stream + + diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/win32.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/win32.py new file mode 100644 index 0000000..3d1d2f2 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/win32.py @@ -0,0 +1,154 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +try: + import ctypes + from ctypes import LibraryLoader + windll = LibraryLoader(ctypes.WinDLL) + from ctypes import wintypes +except (AttributeError, ImportError): + windll = None + SetConsoleTextAttribute = lambda *_: None + winapi_test = lambda *_: None +else: + from ctypes import byref, Structure, c_char, POINTER + + COORD = wintypes._COORD + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", wintypes.WORD), + ("srWindow", wintypes.SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + def __str__(self): + return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( + self.dwSize.Y, self.dwSize.X + , self.dwCursorPosition.Y, self.dwCursorPosition.X + , self.wAttributes + , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right + , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X + ) + + _GetStdHandle = windll.kernel32.GetStdHandle + _GetStdHandle.argtypes = [ + wintypes.DWORD, + ] + _GetStdHandle.restype = wintypes.HANDLE + + _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo + _GetConsoleScreenBufferInfo.argtypes = [ + wintypes.HANDLE, + POINTER(CONSOLE_SCREEN_BUFFER_INFO), + ] + _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + + _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute + _SetConsoleTextAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + ] + _SetConsoleTextAttribute.restype = wintypes.BOOL + + _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition + _SetConsoleCursorPosition.argtypes = [ + wintypes.HANDLE, + COORD, + ] + _SetConsoleCursorPosition.restype = wintypes.BOOL + + _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA + _FillConsoleOutputCharacterA.argtypes = [ + wintypes.HANDLE, + c_char, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputCharacterA.restype = wintypes.BOOL + + _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute + _FillConsoleOutputAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputAttribute.restype = wintypes.BOOL + + _SetConsoleTitleW = windll.kernel32.SetConsoleTitleA + _SetConsoleTitleW.argtypes = [ + wintypes.LPCSTR + ] + _SetConsoleTitleW.restype = wintypes.BOOL + + handles = { + STDOUT: _GetStdHandle(STDOUT), + STDERR: _GetStdHandle(STDERR), + } + + def winapi_test(): + handle = handles[STDOUT] + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return bool(success) + + def GetConsoleScreenBufferInfo(stream_id=STDOUT): + handle = handles[stream_id] + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = handles[stream_id] + return _SetConsoleTextAttribute(handle, attrs) + + def SetConsoleCursorPosition(stream_id, position, adjust=True): + position = COORD(*position) + # If the position is out of range, do nothing. + if position.Y <= 0 or position.X <= 0: + return + # Adjust for Windows' SetConsoleCursorPosition: + # 1. being 0-based, while ANSI is 1-based. + # 2. expecting (x,y), while ANSI uses (y,x). + adjusted_position = COORD(position.Y - 1, position.X - 1) + if adjust: + # Adjust for viewport's scroll position + sr = GetConsoleScreenBufferInfo(STDOUT).srWindow + adjusted_position.Y += sr.Top + adjusted_position.X += sr.Left + # Resume normal processing + handle = handles[stream_id] + return _SetConsoleCursorPosition(handle, adjusted_position) + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = handles[stream_id] + char = c_char(char.encode()) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + success = _FillConsoleOutputCharacterA( + handle, char, length, start, byref(num_written)) + return num_written.value + + def FillConsoleOutputAttribute(stream_id, attr, length, start): + ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' + handle = handles[stream_id] + attribute = wintypes.WORD(attr) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + return _FillConsoleOutputAttribute( + handle, attribute, length, start, byref(num_written)) + + def SetConsoleTitle(title): + return _SetConsoleTitleW(title) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/winterm.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/winterm.py new file mode 100644 index 0000000..60309d3 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/colorama/winterm.py @@ -0,0 +1,162 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from . import win32 + + +# from wincon.h +class WinColor(object): + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 + MAGENTA = 5 + YELLOW = 6 + GREY = 7 + +# from wincon.h +class WinStyle(object): + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + BRIGHT_BACKGROUND = 0x80 # dim text, bright background + +class WinTerm(object): + + def __init__(self): + self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes + self.set_attrs(self._default) + self._default_fore = self._fore + self._default_back = self._back + self._default_style = self._style + # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style. + # So that LIGHT_EX colors and BRIGHT style do not clobber each other, + # we track them separately, since LIGHT_EX is overwritten by Fore/Back + # and BRIGHT is overwritten by Style codes. + self._light = 0 + + def get_attrs(self): + return self._fore + self._back * 16 + (self._style | self._light) + + def set_attrs(self, value): + self._fore = value & 7 + self._back = (value >> 4) & 7 + self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + + def fore(self, fore=None, light=False, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + # Emulate LIGHT_EX with BRIGHT Style + if light: + self._light |= WinStyle.BRIGHT + else: + self._light &= ~WinStyle.BRIGHT + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, light=False, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style + if light: + self._light |= WinStyle.BRIGHT_BACKGROUND + else: + self._light &= ~WinStyle.BRIGHT_BACKGROUND + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + + def get_position(self, handle): + position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition + # Because Windows coordinates are 0-based, + # and win32.SetConsoleCursorPosition expects 1-based. + position.X += 1 + position.Y += 1 + return position + + def set_cursor_position(self, position=None, on_stderr=False): + if position is None: + # I'm not currently tracking the position, so there is no default. + # position = self.get_position() + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleCursorPosition(handle, position) + + def cursor_adjust(self, x, y, on_stderr=False): + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + position = self.get_position(handle) + adjusted_position = (position.Y + y, position.X + x) + win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) + + def erase_screen(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the screen. + # 1 should clear from the cursor to the beginning of the screen. + # 2 should clear the entire screen, and move cursor to (1,1) + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + # get the number of character cells in the current buffer + cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y + # get number of character cells before current cursor position + cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = cells_in_screen - cells_before_cursor + if mode == 1: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_before_cursor + elif mode == 2: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_in_screen + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + if mode == 2: + # put the cursor where needed + win32.SetConsoleCursorPosition(handle, (1, 1)) + + def erase_line(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the line. + # 1 should clear from the cursor to the beginning of the line. + # 2 should clear the entire line. + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X + if mode == 1: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwCursorPosition.X + elif mode == 2: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwSize.X + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + + def set_title(self, title): + win32.SetConsoleTitle(title) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/__init__.py new file mode 100644 index 0000000..d186b0a --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2016 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import logging + +__version__ = '0.2.4' + +class DistlibException(Exception): + pass + +try: + from logging import NullHandler +except ImportError: # pragma: no cover + class NullHandler(logging.Handler): + def handle(self, record): pass + def emit(self, record): pass + def createLock(self): self.lock = None + +logger = logging.getLogger(__name__) +logger.addHandler(NullHandler()) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/__init__.py new file mode 100644 index 0000000..f7dbf4c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/__init__.py @@ -0,0 +1,6 @@ +"""Modules copied from Python 3 standard libraries, for internal use only. + +Individual classes and functions are found in d2._backport.misc. Intended +usage is to always import things missing from 3.1 from that module: the +built-in/stdlib objects will be used if found. +""" diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/misc.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/misc.py new file mode 100644 index 0000000..cfb318d --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/misc.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Backports for individual classes and functions.""" + +import os +import sys + +__all__ = ['cache_from_source', 'callable', 'fsencode'] + + +try: + from imp import cache_from_source +except ImportError: + def cache_from_source(py_file, debug=__debug__): + ext = debug and 'c' or 'o' + return py_file + ext + + +try: + callable = callable +except NameError: + from collections import Callable + + def callable(obj): + return isinstance(obj, Callable) + + +try: + fsencode = os.fsencode +except AttributeError: + def fsencode(filename): + if isinstance(filename, bytes): + return filename + elif isinstance(filename, str): + return filename.encode(sys.getfilesystemencoding()) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/shutil.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/shutil.py new file mode 100644 index 0000000..159e49e --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/shutil.py @@ -0,0 +1,761 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Utility functions for copying and archiving files and directory trees. + +XXX The functions here don't copy the resource fork or other metadata on Mac. + +""" + +import os +import sys +import stat +from os.path import abspath +import fnmatch +import collections +import errno +from . import tarfile + +try: + import bz2 + _BZ2_SUPPORTED = True +except ImportError: + _BZ2_SUPPORTED = False + +try: + from pwd import getpwnam +except ImportError: + getpwnam = None + +try: + from grp import getgrnam +except ImportError: + getgrnam = None + +__all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2", + "copytree", "move", "rmtree", "Error", "SpecialFileError", + "ExecError", "make_archive", "get_archive_formats", + "register_archive_format", "unregister_archive_format", + "get_unpack_formats", "register_unpack_format", + "unregister_unpack_format", "unpack_archive", "ignore_patterns"] + +class Error(EnvironmentError): + pass + +class SpecialFileError(EnvironmentError): + """Raised when trying to do a kind of operation (e.g. copying) which is + not supported on a special file (e.g. a named pipe)""" + +class ExecError(EnvironmentError): + """Raised when a command could not be executed""" + +class ReadError(EnvironmentError): + """Raised when an archive cannot be read""" + +class RegistryError(Exception): + """Raised when a registry operation with the archiving + and unpacking registries fails""" + + +try: + WindowsError +except NameError: + WindowsError = None + +def copyfileobj(fsrc, fdst, length=16*1024): + """copy data from file-like object fsrc to file-like object fdst""" + while 1: + buf = fsrc.read(length) + if not buf: + break + fdst.write(buf) + +def _samefile(src, dst): + # Macintosh, Unix. + if hasattr(os.path, 'samefile'): + try: + return os.path.samefile(src, dst) + except OSError: + return False + + # All other platforms: check for same pathname. + return (os.path.normcase(os.path.abspath(src)) == + os.path.normcase(os.path.abspath(dst))) + +def copyfile(src, dst): + """Copy data from src to dst""" + if _samefile(src, dst): + raise Error("`%s` and `%s` are the same file" % (src, dst)) + + for fn in [src, dst]: + try: + st = os.stat(fn) + except OSError: + # File most likely does not exist + pass + else: + # XXX What about other special files? (sockets, devices...) + if stat.S_ISFIFO(st.st_mode): + raise SpecialFileError("`%s` is a named pipe" % fn) + + with open(src, 'rb') as fsrc: + with open(dst, 'wb') as fdst: + copyfileobj(fsrc, fdst) + +def copymode(src, dst): + """Copy mode bits from src to dst""" + if hasattr(os, 'chmod'): + st = os.stat(src) + mode = stat.S_IMODE(st.st_mode) + os.chmod(dst, mode) + +def copystat(src, dst): + """Copy all stat info (mode bits, atime, mtime, flags) from src to dst""" + st = os.stat(src) + mode = stat.S_IMODE(st.st_mode) + if hasattr(os, 'utime'): + os.utime(dst, (st.st_atime, st.st_mtime)) + if hasattr(os, 'chmod'): + os.chmod(dst, mode) + if hasattr(os, 'chflags') and hasattr(st, 'st_flags'): + try: + os.chflags(dst, st.st_flags) + except OSError as why: + if (not hasattr(errno, 'EOPNOTSUPP') or + why.errno != errno.EOPNOTSUPP): + raise + +def copy(src, dst): + """Copy data and mode bits ("cp src dst"). + + The destination may be a directory. + + """ + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + copyfile(src, dst) + copymode(src, dst) + +def copy2(src, dst): + """Copy data and all stat info ("cp -p src dst"). + + The destination may be a directory. + + """ + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + copyfile(src, dst) + copystat(src, dst) + +def ignore_patterns(*patterns): + """Function that can be used as copytree() ignore parameter. + + Patterns is a sequence of glob-style patterns + that are used to exclude files""" + def _ignore_patterns(path, names): + ignored_names = [] + for pattern in patterns: + ignored_names.extend(fnmatch.filter(names, pattern)) + return set(ignored_names) + return _ignore_patterns + +def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, + ignore_dangling_symlinks=False): + """Recursively copy a directory tree. + + The destination directory must not already exist. + If exception(s) occur, an Error is raised with a list of reasons. + + If the optional symlinks flag is true, symbolic links in the + source tree result in symbolic links in the destination tree; if + it is false, the contents of the files pointed to by symbolic + links are copied. If the file pointed by the symlink doesn't + exist, an exception will be added in the list of errors raised in + an Error exception at the end of the copy process. + + You can set the optional ignore_dangling_symlinks flag to true if you + want to silence this exception. Notice that this has no effect on + platforms that don't support os.symlink. + + The optional ignore argument is a callable. If given, it + is called with the `src` parameter, which is the directory + being visited by copytree(), and `names` which is the list of + `src` contents, as returned by os.listdir(): + + callable(src, names) -> ignored_names + + Since copytree() is called recursively, the callable will be + called once for each directory that is copied. It returns a + list of names relative to the `src` directory that should + not be copied. + + The optional copy_function argument is a callable that will be used + to copy each file. It will be called with the source path and the + destination path as arguments. By default, copy2() is used, but any + function that supports the same signature (like copy()) can be used. + + """ + names = os.listdir(src) + if ignore is not None: + ignored_names = ignore(src, names) + else: + ignored_names = set() + + os.makedirs(dst) + errors = [] + for name in names: + if name in ignored_names: + continue + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + try: + if os.path.islink(srcname): + linkto = os.readlink(srcname) + if symlinks: + os.symlink(linkto, dstname) + else: + # ignore dangling symlink if the flag is on + if not os.path.exists(linkto) and ignore_dangling_symlinks: + continue + # otherwise let the copy occurs. copy2 will raise an error + copy_function(srcname, dstname) + elif os.path.isdir(srcname): + copytree(srcname, dstname, symlinks, ignore, copy_function) + else: + # Will raise a SpecialFileError for unsupported file types + copy_function(srcname, dstname) + # catch the Error from the recursive copytree so that we can + # continue with other files + except Error as err: + errors.extend(err.args[0]) + except EnvironmentError as why: + errors.append((srcname, dstname, str(why))) + try: + copystat(src, dst) + except OSError as why: + if WindowsError is not None and isinstance(why, WindowsError): + # Copying file access times may fail on Windows + pass + else: + errors.extend((src, dst, str(why))) + if errors: + raise Error(errors) + +def rmtree(path, ignore_errors=False, onerror=None): + """Recursively delete a directory tree. + + If ignore_errors is set, errors are ignored; otherwise, if onerror + is set, it is called to handle the error with arguments (func, + path, exc_info) where func is os.listdir, os.remove, or os.rmdir; + path is the argument to that function that caused it to fail; and + exc_info is a tuple returned by sys.exc_info(). If ignore_errors + is false and onerror is None, an exception is raised. + + """ + if ignore_errors: + def onerror(*args): + pass + elif onerror is None: + def onerror(*args): + raise + try: + if os.path.islink(path): + # symlinks to directories are forbidden, see bug #1669 + raise OSError("Cannot call rmtree on a symbolic link") + except OSError: + onerror(os.path.islink, path, sys.exc_info()) + # can't continue even if onerror hook returns + return + names = [] + try: + names = os.listdir(path) + except os.error: + onerror(os.listdir, path, sys.exc_info()) + for name in names: + fullname = os.path.join(path, name) + try: + mode = os.lstat(fullname).st_mode + except os.error: + mode = 0 + if stat.S_ISDIR(mode): + rmtree(fullname, ignore_errors, onerror) + else: + try: + os.remove(fullname) + except os.error: + onerror(os.remove, fullname, sys.exc_info()) + try: + os.rmdir(path) + except os.error: + onerror(os.rmdir, path, sys.exc_info()) + + +def _basename(path): + # A basename() variant which first strips the trailing slash, if present. + # Thus we always get the last component of the path, even for directories. + return os.path.basename(path.rstrip(os.path.sep)) + +def move(src, dst): + """Recursively move a file or directory to another location. This is + similar to the Unix "mv" command. + + If the destination is a directory or a symlink to a directory, the source + is moved inside the directory. The destination path must not already + exist. + + If the destination already exists but is not a directory, it may be + overwritten depending on os.rename() semantics. + + If the destination is on our current filesystem, then rename() is used. + Otherwise, src is copied to the destination and then removed. + A lot more could be done here... A look at a mv.c shows a lot of + the issues this implementation glosses over. + + """ + real_dst = dst + if os.path.isdir(dst): + if _samefile(src, dst): + # We might be on a case insensitive filesystem, + # perform the rename anyway. + os.rename(src, dst) + return + + real_dst = os.path.join(dst, _basename(src)) + if os.path.exists(real_dst): + raise Error("Destination path '%s' already exists" % real_dst) + try: + os.rename(src, real_dst) + except OSError: + if os.path.isdir(src): + if _destinsrc(src, dst): + raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst)) + copytree(src, real_dst, symlinks=True) + rmtree(src) + else: + copy2(src, real_dst) + os.unlink(src) + +def _destinsrc(src, dst): + src = abspath(src) + dst = abspath(dst) + if not src.endswith(os.path.sep): + src += os.path.sep + if not dst.endswith(os.path.sep): + dst += os.path.sep + return dst.startswith(src) + +def _get_gid(name): + """Returns a gid, given a group name.""" + if getgrnam is None or name is None: + return None + try: + result = getgrnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _get_uid(name): + """Returns an uid, given a user name.""" + if getpwnam is None or name is None: + return None + try: + result = getpwnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, + owner=None, group=None, logger=None): + """Create a (possibly compressed) tar file from all the files under + 'base_dir'. + + 'compress' must be "gzip" (the default), "bzip2", or None. + + 'owner' and 'group' can be used to define an owner and a group for the + archive that is being built. If not provided, the current owner and group + will be used. + + The output tar file will be named 'base_name' + ".tar", possibly plus + the appropriate compression extension (".gz", or ".bz2"). + + Returns the output filename. + """ + tar_compression = {'gzip': 'gz', None: ''} + compress_ext = {'gzip': '.gz'} + + if _BZ2_SUPPORTED: + tar_compression['bzip2'] = 'bz2' + compress_ext['bzip2'] = '.bz2' + + # flags for compression program, each element of list will be an argument + if compress is not None and compress not in compress_ext: + raise ValueError("bad value for 'compress', or compression format not " + "supported : {0}".format(compress)) + + archive_name = base_name + '.tar' + compress_ext.get(compress, '') + archive_dir = os.path.dirname(archive_name) + + if not os.path.exists(archive_dir): + if logger is not None: + logger.info("creating %s", archive_dir) + if not dry_run: + os.makedirs(archive_dir) + + # creating the tarball + if logger is not None: + logger.info('Creating tar archive') + + uid = _get_uid(owner) + gid = _get_gid(group) + + def _set_uid_gid(tarinfo): + if gid is not None: + tarinfo.gid = gid + tarinfo.gname = group + if uid is not None: + tarinfo.uid = uid + tarinfo.uname = owner + return tarinfo + + if not dry_run: + tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) + try: + tar.add(base_dir, filter=_set_uid_gid) + finally: + tar.close() + + return archive_name + +def _call_external_zip(base_dir, zip_filename, verbose=False, dry_run=False): + # XXX see if we want to keep an external call here + if verbose: + zipoptions = "-r" + else: + zipoptions = "-rq" + from distutils.errors import DistutilsExecError + from distutils.spawn import spawn + try: + spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run) + except DistutilsExecError: + # XXX really should distinguish between "couldn't find + # external 'zip' command" and "zip failed". + raise ExecError("unable to create zip file '%s': " + "could neither import the 'zipfile' module nor " + "find a standalone zip utility") % zip_filename + +def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None): + """Create a zip file from all the files under 'base_dir'. + + The output zip file will be named 'base_name' + ".zip". Uses either the + "zipfile" Python module (if available) or the InfoZIP "zip" utility + (if installed and found on the default search path). If neither tool is + available, raises ExecError. Returns the name of the output zip + file. + """ + zip_filename = base_name + ".zip" + archive_dir = os.path.dirname(base_name) + + if not os.path.exists(archive_dir): + if logger is not None: + logger.info("creating %s", archive_dir) + if not dry_run: + os.makedirs(archive_dir) + + # If zipfile module is not available, try spawning an external 'zip' + # command. + try: + import zipfile + except ImportError: + zipfile = None + + if zipfile is None: + _call_external_zip(base_dir, zip_filename, verbose, dry_run) + else: + if logger is not None: + logger.info("creating '%s' and adding '%s' to it", + zip_filename, base_dir) + + if not dry_run: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) + + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zip.write(path, path) + if logger is not None: + logger.info("adding '%s'", path) + zip.close() + + return zip_filename + +_ARCHIVE_FORMATS = { + 'gztar': (_make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), + 'bztar': (_make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), + 'tar': (_make_tarball, [('compress', None)], "uncompressed tar file"), + 'zip': (_make_zipfile, [], "ZIP file"), + } + +if _BZ2_SUPPORTED: + _ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')], + "bzip2'ed tar-file") + +def get_archive_formats(): + """Returns a list of supported formats for archiving and unarchiving. + + Each element of the returned sequence is a tuple (name, description) + """ + formats = [(name, registry[2]) for name, registry in + _ARCHIVE_FORMATS.items()] + formats.sort() + return formats + +def register_archive_format(name, function, extra_args=None, description=''): + """Registers an archive format. + + name is the name of the format. function is the callable that will be + used to create archives. If provided, extra_args is a sequence of + (name, value) tuples that will be passed as arguments to the callable. + description can be provided to describe the format, and will be returned + by the get_archive_formats() function. + """ + if extra_args is None: + extra_args = [] + if not isinstance(function, collections.Callable): + raise TypeError('The %s object is not callable' % function) + if not isinstance(extra_args, (tuple, list)): + raise TypeError('extra_args needs to be a sequence') + for element in extra_args: + if not isinstance(element, (tuple, list)) or len(element) !=2: + raise TypeError('extra_args elements are : (arg_name, value)') + + _ARCHIVE_FORMATS[name] = (function, extra_args, description) + +def unregister_archive_format(name): + del _ARCHIVE_FORMATS[name] + +def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, + dry_run=0, owner=None, group=None, logger=None): + """Create an archive file (eg. zip or tar). + + 'base_name' is the name of the file to create, minus any format-specific + extension; 'format' is the archive format: one of "zip", "tar", "bztar" + or "gztar". + + 'root_dir' is a directory that will be the root directory of the + archive; ie. we typically chdir into 'root_dir' before creating the + archive. 'base_dir' is the directory where we start archiving from; + ie. 'base_dir' will be the common prefix of all files and + directories in the archive. 'root_dir' and 'base_dir' both default + to the current directory. Returns the name of the archive file. + + 'owner' and 'group' are used when creating a tar archive. By default, + uses the current owner and group. + """ + save_cwd = os.getcwd() + if root_dir is not None: + if logger is not None: + logger.debug("changing into '%s'", root_dir) + base_name = os.path.abspath(base_name) + if not dry_run: + os.chdir(root_dir) + + if base_dir is None: + base_dir = os.curdir + + kwargs = {'dry_run': dry_run, 'logger': logger} + + try: + format_info = _ARCHIVE_FORMATS[format] + except KeyError: + raise ValueError("unknown archive format '%s'" % format) + + func = format_info[0] + for arg, val in format_info[1]: + kwargs[arg] = val + + if format != 'zip': + kwargs['owner'] = owner + kwargs['group'] = group + + try: + filename = func(base_name, base_dir, **kwargs) + finally: + if root_dir is not None: + if logger is not None: + logger.debug("changing back to '%s'", save_cwd) + os.chdir(save_cwd) + + return filename + + +def get_unpack_formats(): + """Returns a list of supported formats for unpacking. + + Each element of the returned sequence is a tuple + (name, extensions, description) + """ + formats = [(name, info[0], info[3]) for name, info in + _UNPACK_FORMATS.items()] + formats.sort() + return formats + +def _check_unpack_options(extensions, function, extra_args): + """Checks what gets registered as an unpacker.""" + # first make sure no other unpacker is registered for this extension + existing_extensions = {} + for name, info in _UNPACK_FORMATS.items(): + for ext in info[0]: + existing_extensions[ext] = name + + for extension in extensions: + if extension in existing_extensions: + msg = '%s is already registered for "%s"' + raise RegistryError(msg % (extension, + existing_extensions[extension])) + + if not isinstance(function, collections.Callable): + raise TypeError('The registered function must be a callable') + + +def register_unpack_format(name, extensions, function, extra_args=None, + description=''): + """Registers an unpack format. + + `name` is the name of the format. `extensions` is a list of extensions + corresponding to the format. + + `function` is the callable that will be + used to unpack archives. The callable will receive archives to unpack. + If it's unable to handle an archive, it needs to raise a ReadError + exception. + + If provided, `extra_args` is a sequence of + (name, value) tuples that will be passed as arguments to the callable. + description can be provided to describe the format, and will be returned + by the get_unpack_formats() function. + """ + if extra_args is None: + extra_args = [] + _check_unpack_options(extensions, function, extra_args) + _UNPACK_FORMATS[name] = extensions, function, extra_args, description + +def unregister_unpack_format(name): + """Removes the pack format from the registry.""" + del _UNPACK_FORMATS[name] + +def _ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + if not os.path.isdir(dirname): + os.makedirs(dirname) + +def _unpack_zipfile(filename, extract_dir): + """Unpack zip `filename` to `extract_dir` + """ + try: + import zipfile + except ImportError: + raise ReadError('zlib not supported, cannot unpack this archive.') + + if not zipfile.is_zipfile(filename): + raise ReadError("%s is not a zip file" % filename) + + zip = zipfile.ZipFile(filename) + try: + for info in zip.infolist(): + name = info.filename + + # don't extract absolute paths or ones with .. in them + if name.startswith('/') or '..' in name: + continue + + target = os.path.join(extract_dir, *name.split('/')) + if not target: + continue + + _ensure_directory(target) + if not name.endswith('/'): + # file + data = zip.read(info.filename) + f = open(target, 'wb') + try: + f.write(data) + finally: + f.close() + del data + finally: + zip.close() + +def _unpack_tarfile(filename, extract_dir): + """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir` + """ + try: + tarobj = tarfile.open(filename) + except tarfile.TarError: + raise ReadError( + "%s is not a compressed or uncompressed tar file" % filename) + try: + tarobj.extractall(extract_dir) + finally: + tarobj.close() + +_UNPACK_FORMATS = { + 'gztar': (['.tar.gz', '.tgz'], _unpack_tarfile, [], "gzip'ed tar-file"), + 'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"), + 'zip': (['.zip'], _unpack_zipfile, [], "ZIP file") + } + +if _BZ2_SUPPORTED: + _UNPACK_FORMATS['bztar'] = (['.bz2'], _unpack_tarfile, [], + "bzip2'ed tar-file") + +def _find_unpack_format(filename): + for name, info in _UNPACK_FORMATS.items(): + for extension in info[0]: + if filename.endswith(extension): + return name + return None + +def unpack_archive(filename, extract_dir=None, format=None): + """Unpack an archive. + + `filename` is the name of the archive. + + `extract_dir` is the name of the target directory, where the archive + is unpacked. If not provided, the current working directory is used. + + `format` is the archive format: one of "zip", "tar", or "gztar". Or any + other registered format. If not provided, unpack_archive will use the + filename extension and see if an unpacker was registered for that + extension. + + In case none is found, a ValueError is raised. + """ + if extract_dir is None: + extract_dir = os.getcwd() + + if format is not None: + try: + format_info = _UNPACK_FORMATS[format] + except KeyError: + raise ValueError("Unknown unpack format '{0}'".format(format)) + + func = format_info[1] + func(filename, extract_dir, **dict(format_info[2])) + else: + # we need to look at the registered unpackers supported extensions + format = _find_unpack_format(filename) + if format is None: + raise ReadError("Unknown archive format '{0}'".format(filename)) + + func = _UNPACK_FORMATS[format][1] + kwargs = dict(_UNPACK_FORMATS[format][2]) + func(filename, extract_dir, **kwargs) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/sysconfig.cfg b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/sysconfig.cfg new file mode 100644 index 0000000..1746bd0 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/sysconfig.cfg @@ -0,0 +1,84 @@ +[posix_prefix] +# Configuration directories. Some of these come straight out of the +# configure script. They are for implementing the other variables, not to +# be used directly in [resource_locations]. +confdir = /etc +datadir = /usr/share +libdir = /usr/lib +statedir = /var +# User resource directory +local = ~/.local/{distribution.name} + +stdlib = {base}/lib/python{py_version_short} +platstdlib = {platbase}/lib/python{py_version_short} +purelib = {base}/lib/python{py_version_short}/site-packages +platlib = {platbase}/lib/python{py_version_short}/site-packages +include = {base}/include/python{py_version_short}{abiflags} +platinclude = {platbase}/include/python{py_version_short}{abiflags} +data = {base} + +[posix_home] +stdlib = {base}/lib/python +platstdlib = {base}/lib/python +purelib = {base}/lib/python +platlib = {base}/lib/python +include = {base}/include/python +platinclude = {base}/include/python +scripts = {base}/bin +data = {base} + +[nt] +stdlib = {base}/Lib +platstdlib = {base}/Lib +purelib = {base}/Lib/site-packages +platlib = {base}/Lib/site-packages +include = {base}/Include +platinclude = {base}/Include +scripts = {base}/Scripts +data = {base} + +[os2] +stdlib = {base}/Lib +platstdlib = {base}/Lib +purelib = {base}/Lib/site-packages +platlib = {base}/Lib/site-packages +include = {base}/Include +platinclude = {base}/Include +scripts = {base}/Scripts +data = {base} + +[os2_home] +stdlib = {userbase}/lib/python{py_version_short} +platstdlib = {userbase}/lib/python{py_version_short} +purelib = {userbase}/lib/python{py_version_short}/site-packages +platlib = {userbase}/lib/python{py_version_short}/site-packages +include = {userbase}/include/python{py_version_short} +scripts = {userbase}/bin +data = {userbase} + +[nt_user] +stdlib = {userbase}/Python{py_version_nodot} +platstdlib = {userbase}/Python{py_version_nodot} +purelib = {userbase}/Python{py_version_nodot}/site-packages +platlib = {userbase}/Python{py_version_nodot}/site-packages +include = {userbase}/Python{py_version_nodot}/Include +scripts = {userbase}/Scripts +data = {userbase} + +[posix_user] +stdlib = {userbase}/lib/python{py_version_short} +platstdlib = {userbase}/lib/python{py_version_short} +purelib = {userbase}/lib/python{py_version_short}/site-packages +platlib = {userbase}/lib/python{py_version_short}/site-packages +include = {userbase}/include/python{py_version_short} +scripts = {userbase}/bin +data = {userbase} + +[osx_framework_user] +stdlib = {userbase}/lib/python +platstdlib = {userbase}/lib/python +purelib = {userbase}/lib/python/site-packages +platlib = {userbase}/lib/python/site-packages +include = {userbase}/include +scripts = {userbase}/bin +data = {userbase} diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/sysconfig.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/sysconfig.py new file mode 100644 index 0000000..ec28480 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/sysconfig.py @@ -0,0 +1,788 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Access to Python's configuration information.""" + +import codecs +import os +import re +import sys +from os.path import pardir, realpath +try: + import configparser +except ImportError: + import ConfigParser as configparser + + +__all__ = [ + 'get_config_h_filename', + 'get_config_var', + 'get_config_vars', + 'get_makefile_filename', + 'get_path', + 'get_path_names', + 'get_paths', + 'get_platform', + 'get_python_version', + 'get_scheme_names', + 'parse_config_h', +] + + +def _safe_realpath(path): + try: + return realpath(path) + except OSError: + return path + + +if sys.executable: + _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable)) +else: + # sys.executable can be empty if argv[0] has been changed and Python is + # unable to retrieve the real program name + _PROJECT_BASE = _safe_realpath(os.getcwd()) + +if os.name == "nt" and "pcbuild" in _PROJECT_BASE[-8:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir)) +# PC/VS7.1 +if os.name == "nt" and "\\pc\\v" in _PROJECT_BASE[-10:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) +# PC/AMD64 +if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) + + +def is_python_build(): + for fn in ("Setup.dist", "Setup.local"): + if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)): + return True + return False + +_PYTHON_BUILD = is_python_build() + +_cfg_read = False + +def _ensure_cfg_read(): + global _cfg_read + if not _cfg_read: + from ..resources import finder + backport_package = __name__.rsplit('.', 1)[0] + _finder = finder(backport_package) + _cfgfile = _finder.find('sysconfig.cfg') + assert _cfgfile, 'sysconfig.cfg exists' + with _cfgfile.as_stream() as s: + _SCHEMES.readfp(s) + if _PYTHON_BUILD: + for scheme in ('posix_prefix', 'posix_home'): + _SCHEMES.set(scheme, 'include', '{srcdir}/Include') + _SCHEMES.set(scheme, 'platinclude', '{projectbase}/.') + + _cfg_read = True + + +_SCHEMES = configparser.RawConfigParser() +_VAR_REPL = re.compile(r'\{([^{]*?)\}') + +def _expand_globals(config): + _ensure_cfg_read() + if config.has_section('globals'): + globals = config.items('globals') + else: + globals = tuple() + + sections = config.sections() + for section in sections: + if section == 'globals': + continue + for option, value in globals: + if config.has_option(section, option): + continue + config.set(section, option, value) + config.remove_section('globals') + + # now expanding local variables defined in the cfg file + # + for section in config.sections(): + variables = dict(config.items(section)) + + def _replacer(matchobj): + name = matchobj.group(1) + if name in variables: + return variables[name] + return matchobj.group(0) + + for option, value in config.items(section): + config.set(section, option, _VAR_REPL.sub(_replacer, value)) + +#_expand_globals(_SCHEMES) + + # FIXME don't rely on sys.version here, its format is an implementation detail + # of CPython, use sys.version_info or sys.hexversion +_PY_VERSION = sys.version.split()[0] +_PY_VERSION_SHORT = sys.version[:3] +_PY_VERSION_SHORT_NO_DOT = _PY_VERSION[0] + _PY_VERSION[2] +_PREFIX = os.path.normpath(sys.prefix) +_EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +_CONFIG_VARS = None +_USER_BASE = None + + +def _subst_vars(path, local_vars): + """In the string `path`, replace tokens like {some.thing} with the + corresponding value from the map `local_vars`. + + If there is no corresponding value, leave the token unchanged. + """ + def _replacer(matchobj): + name = matchobj.group(1) + if name in local_vars: + return local_vars[name] + elif name in os.environ: + return os.environ[name] + return matchobj.group(0) + return _VAR_REPL.sub(_replacer, path) + + +def _extend_dict(target_dict, other_dict): + target_keys = target_dict.keys() + for key, value in other_dict.items(): + if key in target_keys: + continue + target_dict[key] = value + + +def _expand_vars(scheme, vars): + res = {} + if vars is None: + vars = {} + _extend_dict(vars, get_config_vars()) + + for key, value in _SCHEMES.items(scheme): + if os.name in ('posix', 'nt'): + value = os.path.expanduser(value) + res[key] = os.path.normpath(_subst_vars(value, vars)) + return res + + +def format_value(value, vars): + def _replacer(matchobj): + name = matchobj.group(1) + if name in vars: + return vars[name] + return matchobj.group(0) + return _VAR_REPL.sub(_replacer, value) + + +def _get_default_scheme(): + if os.name == 'posix': + # the default scheme for posix is posix_prefix + return 'posix_prefix' + return os.name + + +def _getuserbase(): + env_base = os.environ.get("PYTHONUSERBASE", None) + + def joinuser(*args): + return os.path.expanduser(os.path.join(*args)) + + # what about 'os2emx', 'riscos' ? + if os.name == "nt": + base = os.environ.get("APPDATA") or "~" + if env_base: + return env_base + else: + return joinuser(base, "Python") + + if sys.platform == "darwin": + framework = get_config_var("PYTHONFRAMEWORK") + if framework: + if env_base: + return env_base + else: + return joinuser("~", "Library", framework, "%d.%d" % + sys.version_info[:2]) + + if env_base: + return env_base + else: + return joinuser("~", ".local") + + +def _parse_makefile(filename, vars=None): + """Parse a Makefile-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + # Regexes needed for parsing Makefile (and similar syntaxes, + # like old-style Setup files). + _variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") + _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") + _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") + + if vars is None: + vars = {} + done = {} + notdone = {} + + with codecs.open(filename, encoding='utf-8', errors="surrogateescape") as f: + lines = f.readlines() + + for line in lines: + if line.startswith('#') or line.strip() == '': + continue + m = _variable_rx.match(line) + if m: + n, v = m.group(1, 2) + v = v.strip() + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: + notdone[n] = v + else: + try: + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v + + # do variable interpolation here + variables = list(notdone.keys()) + + # Variables with a 'PY_' prefix in the makefile. These need to + # be made available without that prefix through sysconfig. + # Special care is needed to ensure that variable expansion works, even + # if the expansion uses the name without a prefix. + renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') + + while len(variables) > 0: + for name in tuple(variables): + value = notdone[name] + m = _findvar1_rx.search(value) or _findvar2_rx.search(value) + if m is not None: + n = m.group(1) + found = True + if n in done: + item = str(done[n]) + elif n in notdone: + # get it on a subsequent round + found = False + elif n in os.environ: + # do it like make: fall back to environment + item = os.environ[n] + + elif n in renamed_variables: + if (name.startswith('PY_') and + name[3:] in renamed_variables): + item = "" + + elif 'PY_' + n in notdone: + found = False + + else: + item = str(done['PY_' + n]) + + else: + done[n] = item = "" + + if found: + after = value[m.end():] + value = value[:m.start()] + item + after + if "$" in after: + notdone[name] = value + else: + try: + value = int(value) + except ValueError: + done[name] = value.strip() + else: + done[name] = value + variables.remove(name) + + if (name.startswith('PY_') and + name[3:] in renamed_variables): + + name = name[3:] + if name not in done: + done[name] = value + + else: + # bogus variable reference (e.g. "prefix=$/opt/python"); + # just drop it since we can't deal + done[name] = value + variables.remove(name) + + # strip spurious spaces + for k, v in done.items(): + if isinstance(v, str): + done[k] = v.strip() + + # save the results in the global dictionary + vars.update(done) + return vars + + +def get_makefile_filename(): + """Return the path of the Makefile.""" + if _PYTHON_BUILD: + return os.path.join(_PROJECT_BASE, "Makefile") + if hasattr(sys, 'abiflags'): + config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, sys.abiflags) + else: + config_dir_name = 'config' + return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') + + +def _init_posix(vars): + """Initialize the module as appropriate for POSIX systems.""" + # load the installed Makefile: + makefile = get_makefile_filename() + try: + _parse_makefile(makefile, vars) + except IOError as e: + msg = "invalid Python installation: unable to open %s" % makefile + if hasattr(e, "strerror"): + msg = msg + " (%s)" % e.strerror + raise IOError(msg) + # load the installed pyconfig.h: + config_h = get_config_h_filename() + try: + with open(config_h) as f: + parse_config_h(f, vars) + except IOError as e: + msg = "invalid Python installation: unable to open %s" % config_h + if hasattr(e, "strerror"): + msg = msg + " (%s)" % e.strerror + raise IOError(msg) + # On AIX, there are wrong paths to the linker scripts in the Makefile + # -- these paths are relative to the Python source, but when installed + # the scripts are in another directory. + if _PYTHON_BUILD: + vars['LDSHARED'] = vars['BLDSHARED'] + + +def _init_non_posix(vars): + """Initialize the module as appropriate for NT""" + # set basic install directories + vars['LIBDEST'] = get_path('stdlib') + vars['BINLIBDEST'] = get_path('platstdlib') + vars['INCLUDEPY'] = get_path('include') + vars['SO'] = '.pyd' + vars['EXE'] = '.exe' + vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT + vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) + +# +# public APIs +# + + +def parse_config_h(fp, vars=None): + """Parse a config.h-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + if vars is None: + vars = {} + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") + + while True: + line = fp.readline() + if not line: + break + m = define_rx.match(line) + if m: + n, v = m.group(1, 2) + try: + v = int(v) + except ValueError: + pass + vars[n] = v + else: + m = undef_rx.match(line) + if m: + vars[m.group(1)] = 0 + return vars + + +def get_config_h_filename(): + """Return the path of pyconfig.h.""" + if _PYTHON_BUILD: + if os.name == "nt": + inc_dir = os.path.join(_PROJECT_BASE, "PC") + else: + inc_dir = _PROJECT_BASE + else: + inc_dir = get_path('platinclude') + return os.path.join(inc_dir, 'pyconfig.h') + + +def get_scheme_names(): + """Return a tuple containing the schemes names.""" + return tuple(sorted(_SCHEMES.sections())) + + +def get_path_names(): + """Return a tuple containing the paths names.""" + # xxx see if we want a static list + return _SCHEMES.options('posix_prefix') + + +def get_paths(scheme=_get_default_scheme(), vars=None, expand=True): + """Return a mapping containing an install scheme. + + ``scheme`` is the install scheme name. If not provided, it will + return the default scheme for the current platform. + """ + _ensure_cfg_read() + if expand: + return _expand_vars(scheme, vars) + else: + return dict(_SCHEMES.items(scheme)) + + +def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True): + """Return a path corresponding to the scheme. + + ``scheme`` is the install scheme name. + """ + return get_paths(scheme, vars, expand)[name] + + +def get_config_vars(*args): + """With no arguments, return a dictionary of all configuration + variables relevant for the current platform. + + On Unix, this means every variable defined in Python's installed Makefile; + On Windows and Mac OS it's a much smaller set. + + With arguments, return a list of values that result from looking up + each argument in the configuration variable dictionary. + """ + global _CONFIG_VARS + if _CONFIG_VARS is None: + _CONFIG_VARS = {} + # Normalized versions of prefix and exec_prefix are handy to have; + # in fact, these are the standard versions used most places in the + # distutils2 module. + _CONFIG_VARS['prefix'] = _PREFIX + _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX + _CONFIG_VARS['py_version'] = _PY_VERSION + _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT + _CONFIG_VARS['py_version_nodot'] = _PY_VERSION[0] + _PY_VERSION[2] + _CONFIG_VARS['base'] = _PREFIX + _CONFIG_VARS['platbase'] = _EXEC_PREFIX + _CONFIG_VARS['projectbase'] = _PROJECT_BASE + try: + _CONFIG_VARS['abiflags'] = sys.abiflags + except AttributeError: + # sys.abiflags may not be defined on all platforms. + _CONFIG_VARS['abiflags'] = '' + + if os.name in ('nt', 'os2'): + _init_non_posix(_CONFIG_VARS) + if os.name == 'posix': + _init_posix(_CONFIG_VARS) + # Setting 'userbase' is done below the call to the + # init function to enable using 'get_config_var' in + # the init-function. + if sys.version >= '2.6': + _CONFIG_VARS['userbase'] = _getuserbase() + + if 'srcdir' not in _CONFIG_VARS: + _CONFIG_VARS['srcdir'] = _PROJECT_BASE + else: + _CONFIG_VARS['srcdir'] = _safe_realpath(_CONFIG_VARS['srcdir']) + + # Convert srcdir into an absolute path if it appears necessary. + # Normally it is relative to the build directory. However, during + # testing, for example, we might be running a non-installed python + # from a different directory. + if _PYTHON_BUILD and os.name == "posix": + base = _PROJECT_BASE + try: + cwd = os.getcwd() + except OSError: + cwd = None + if (not os.path.isabs(_CONFIG_VARS['srcdir']) and + base != cwd): + # srcdir is relative and we are not in the same directory + # as the executable. Assume executable is in the build + # directory and make srcdir absolute. + srcdir = os.path.join(base, _CONFIG_VARS['srcdir']) + _CONFIG_VARS['srcdir'] = os.path.normpath(srcdir) + + if sys.platform == 'darwin': + kernel_version = os.uname()[2] # Kernel version (8.4.3) + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # On macOS before 10.4, check if -arch and -isysroot + # are in CFLAGS or LDFLAGS and remove them if they are. + # This is needed when building extensions on a 10.3 system + # using a universal build of python. + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + flags = _CONFIG_VARS[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = re.sub('-isysroot [^ \t]*', ' ', flags) + _CONFIG_VARS[key] = flags + else: + # Allow the user to override the architecture flags using + # an environment variable. + # NOTE: This name was introduced by Apple in OSX 10.5 and + # is used by several scripting languages distributed with + # that OS release. + if 'ARCHFLAGS' in os.environ: + arch = os.environ['ARCHFLAGS'] + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _CONFIG_VARS[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = flags + ' ' + arch + _CONFIG_VARS[key] = flags + + # If we're on OSX 10.5 or later and the user tries to + # compiles an extension using an SDK that is not present + # on the current machine it is better to not use an SDK + # than to fail. + # + # The major usecase for this is users using a Python.org + # binary installer on OSX 10.6: that installer uses + # the 10.4u SDK, but that SDK is not installed by default + # when you install Xcode. + # + CFLAGS = _CONFIG_VARS.get('CFLAGS', '') + m = re.search('-isysroot\s+(\S+)', CFLAGS) + if m is not None: + sdk = m.group(1) + if not os.path.exists(sdk): + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _CONFIG_VARS[key] + flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags) + _CONFIG_VARS[key] = flags + + if args: + vals = [] + for name in args: + vals.append(_CONFIG_VARS.get(name)) + return vals + else: + return _CONFIG_VARS + + +def get_config_var(name): + """Return the value of a single variable using the dictionary returned by + 'get_config_vars()'. + + Equivalent to get_config_vars().get(name) + """ + return get_config_vars().get(name) + + +def get_platform(): + """Return a string that identifies the current platform. + + This is used mainly to distinguish platform-specific build directories and + platform-specific built distributions. Typically includes the OS name + and version and the architecture (as supplied by 'os.uname()'), + although the exact information included depends on the OS; eg. for IRIX + the architecture isn't particularly important (IRIX only runs on SGI + hardware), but for Linux the kernel version isn't particularly + important. + + Examples of returned values: + linux-i586 + linux-alpha (?) + solaris-2.6-sun4u + irix-5.3 + irix64-6.2 + + Windows will return one of: + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win-ia64 (64bit Windows on Itanium) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. + """ + if os.name == 'nt': + # sniff sys.version for architecture. + prefix = " bit (" + i = sys.version.find(prefix) + if i == -1: + return sys.platform + j = sys.version.find(")", i) + look = sys.version[i+len(prefix):j].lower() + if look == 'amd64': + return 'win-amd64' + if look == 'itanium': + return 'win-ia64' + return sys.platform + + if os.name != "posix" or not hasattr(os, 'uname'): + # XXX what about the architecture? NT is Intel or Alpha, + # Mac OS is M68k or PPC, etc. + return sys.platform + + # Try to distinguish various flavours of Unix + osname, host, release, version, machine = os.uname() + + # Convert the OS name to lowercase, remove '/' characters + # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") + osname = osname.lower().replace('/', '') + machine = machine.replace(' ', '_') + machine = machine.replace('/', '-') + + if osname[:5] == "linux": + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return "%s-%s" % (osname, machine) + elif osname[:5] == "sunos": + if release[0] >= "5": # SunOS 5 == Solaris 2 + osname = "solaris" + release = "%d.%s" % (int(release[0]) - 3, release[2:]) + # fall through to standard osname-release-machine representation + elif osname[:4] == "irix": # could be "irix64"! + return "%s-%s" % (osname, release) + elif osname[:3] == "aix": + return "%s-%s.%s" % (osname, version, release) + elif osname[:6] == "cygwin": + osname = "cygwin" + rel_re = re.compile(r'[\d.]+') + m = rel_re.match(release) + if m: + release = m.group() + elif osname[:6] == "darwin": + # + # For our purposes, we'll assume that the system version from + # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set + # to. This makes the compatibility story a bit more sane because the + # machine is going to compile and link as if it were + # MACOSX_DEPLOYMENT_TARGET. + cfgvars = get_config_vars() + macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') + + if True: + # Always calculate the release of the running machine, + # needed to determine if we can build fat binaries or not. + + macrelease = macver + # Get the system version. Reading this plist is a documented + # way to get the system version (see the documentation for + # the Gestalt Manager) + try: + f = open('/System/Library/CoreServices/SystemVersion.plist') + except IOError: + # We're on a plain darwin box, fall back to the default + # behaviour. + pass + else: + try: + m = re.search(r'<key>ProductUserVisibleVersion</key>\s*' + r'<string>(.*?)</string>', f.read()) + finally: + f.close() + if m is not None: + macrelease = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + + if not macver: + macver = macrelease + + if macver: + release = macver + osname = "macosx" + + if ((macrelease + '.') >= '10.4.' and + '-arch' in get_config_vars().get('CFLAGS', '').strip()): + # The universal build will build fat binaries, but not on + # systems before 10.4 + # + # Try to detect 4-way universal builds, those have machine-type + # 'universal' instead of 'fat'. + + machine = 'fat' + cflags = get_config_vars().get('CFLAGS') + + archs = re.findall('-arch\s+(\S+)', cflags) + archs = tuple(sorted(set(archs))) + + if len(archs) == 1: + machine = archs[0] + elif archs == ('i386', 'ppc'): + machine = 'fat' + elif archs == ('i386', 'x86_64'): + machine = 'intel' + elif archs == ('i386', 'ppc', 'x86_64'): + machine = 'fat3' + elif archs == ('ppc64', 'x86_64'): + machine = 'fat64' + elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): + machine = 'universal' + else: + raise ValueError( + "Don't know machine value for archs=%r" % (archs,)) + + elif machine == 'i386': + # On OSX the machine type returned by uname is always the + # 32-bit variant, even if the executable architecture is + # the 64-bit variant + if sys.maxsize >= 2**32: + machine = 'x86_64' + + elif machine in ('PowerPC', 'Power_Macintosh'): + # Pick a sane name for the PPC architecture. + # See 'i386' case + if sys.maxsize >= 2**32: + machine = 'ppc64' + else: + machine = 'ppc' + + return "%s-%s-%s" % (osname, release, machine) + + +def get_python_version(): + return _PY_VERSION_SHORT + + +def _print_dict(title, data): + for index, (key, value) in enumerate(sorted(data.items())): + if index == 0: + print('%s: ' % (title)) + print('\t%s = "%s"' % (key, value)) + + +def _main(): + """Display all information sysconfig detains.""" + print('Platform: "%s"' % get_platform()) + print('Python version: "%s"' % get_python_version()) + print('Current installation scheme: "%s"' % _get_default_scheme()) + print() + _print_dict('Paths', get_paths()) + print() + _print_dict('Variables', get_config_vars()) + + +if __name__ == '__main__': + _main() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/tarfile.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/tarfile.py new file mode 100644 index 0000000..d66d856 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/_backport/tarfile.py @@ -0,0 +1,2607 @@ +#------------------------------------------------------------------- +# tarfile.py +#------------------------------------------------------------------- +# Copyright (C) 2002 Lars Gustaebel <lars@gustaebel.de> +# All rights reserved. +# +# 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. +# +from __future__ import print_function + +"""Read from and write to tar format archives. +""" + +__version__ = "$Revision$" + +version = "0.9.0" +__author__ = "Lars Gust\u00e4bel (lars@gustaebel.de)" +__date__ = "$Date: 2011-02-25 17:42:01 +0200 (Fri, 25 Feb 2011) $" +__cvsid__ = "$Id: tarfile.py 88586 2011-02-25 15:42:01Z marc-andre.lemburg $" +__credits__ = "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend." + +#--------- +# Imports +#--------- +import sys +import os +import stat +import errno +import time +import struct +import copy +import re + +try: + import grp, pwd +except ImportError: + grp = pwd = None + +# os.symlink on Windows prior to 6.0 raises NotImplementedError +symlink_exception = (AttributeError, NotImplementedError) +try: + # WindowsError (1314) will be raised if the caller does not hold the + # SeCreateSymbolicLinkPrivilege privilege + symlink_exception += (WindowsError,) +except NameError: + pass + +# from tarfile import * +__all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError"] + +if sys.version_info[0] < 3: + import __builtin__ as builtins +else: + import builtins + +_open = builtins.open # Since 'open' is TarFile.open + +#--------------------------------------------------------- +# tar constants +#--------------------------------------------------------- +NUL = b"\0" # the null character +BLOCKSIZE = 512 # length of processing blocks +RECORDSIZE = BLOCKSIZE * 20 # length of records +GNU_MAGIC = b"ustar \0" # magic gnu tar string +POSIX_MAGIC = b"ustar\x0000" # magic posix tar string + +LENGTH_NAME = 100 # maximum length of a filename +LENGTH_LINK = 100 # maximum length of a linkname +LENGTH_PREFIX = 155 # maximum length of the prefix field + +REGTYPE = b"0" # regular file +AREGTYPE = b"\0" # regular file +LNKTYPE = b"1" # link (inside tarfile) +SYMTYPE = b"2" # symbolic link +CHRTYPE = b"3" # character special device +BLKTYPE = b"4" # block special device +DIRTYPE = b"5" # directory +FIFOTYPE = b"6" # fifo special device +CONTTYPE = b"7" # contiguous file + +GNUTYPE_LONGNAME = b"L" # GNU tar longname +GNUTYPE_LONGLINK = b"K" # GNU tar longlink +GNUTYPE_SPARSE = b"S" # GNU tar sparse file + +XHDTYPE = b"x" # POSIX.1-2001 extended header +XGLTYPE = b"g" # POSIX.1-2001 global header +SOLARIS_XHDTYPE = b"X" # Solaris extended header + +USTAR_FORMAT = 0 # POSIX.1-1988 (ustar) format +GNU_FORMAT = 1 # GNU tar format +PAX_FORMAT = 2 # POSIX.1-2001 (pax) format +DEFAULT_FORMAT = GNU_FORMAT + +#--------------------------------------------------------- +# tarfile constants +#--------------------------------------------------------- +# File types that tarfile supports: +SUPPORTED_TYPES = (REGTYPE, AREGTYPE, LNKTYPE, + SYMTYPE, DIRTYPE, FIFOTYPE, + CONTTYPE, CHRTYPE, BLKTYPE, + GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE) + +# File types that will be treated as a regular file. +REGULAR_TYPES = (REGTYPE, AREGTYPE, + CONTTYPE, GNUTYPE_SPARSE) + +# File types that are part of the GNU tar format. +GNU_TYPES = (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE) + +# Fields from a pax header that override a TarInfo attribute. +PAX_FIELDS = ("path", "linkpath", "size", "mtime", + "uid", "gid", "uname", "gname") + +# Fields from a pax header that are affected by hdrcharset. +PAX_NAME_FIELDS = set(("path", "linkpath", "uname", "gname")) + +# Fields in a pax header that are numbers, all other fields +# are treated as strings. +PAX_NUMBER_FIELDS = { + "atime": float, + "ctime": float, + "mtime": float, + "uid": int, + "gid": int, + "size": int +} + +#--------------------------------------------------------- +# Bits used in the mode field, values in octal. +#--------------------------------------------------------- +S_IFLNK = 0o120000 # symbolic link +S_IFREG = 0o100000 # regular file +S_IFBLK = 0o060000 # block device +S_IFDIR = 0o040000 # directory +S_IFCHR = 0o020000 # character device +S_IFIFO = 0o010000 # fifo + +TSUID = 0o4000 # set UID on execution +TSGID = 0o2000 # set GID on execution +TSVTX = 0o1000 # reserved + +TUREAD = 0o400 # read by owner +TUWRITE = 0o200 # write by owner +TUEXEC = 0o100 # execute/search by owner +TGREAD = 0o040 # read by group +TGWRITE = 0o020 # write by group +TGEXEC = 0o010 # execute/search by group +TOREAD = 0o004 # read by other +TOWRITE = 0o002 # write by other +TOEXEC = 0o001 # execute/search by other + +#--------------------------------------------------------- +# initialization +#--------------------------------------------------------- +if os.name in ("nt", "ce"): + ENCODING = "utf-8" +else: + ENCODING = sys.getfilesystemencoding() + +#--------------------------------------------------------- +# Some useful functions +#--------------------------------------------------------- + +def stn(s, length, encoding, errors): + """Convert a string to a null-terminated bytes object. + """ + s = s.encode(encoding, errors) + return s[:length] + (length - len(s)) * NUL + +def nts(s, encoding, errors): + """Convert a null-terminated bytes object to a string. + """ + p = s.find(b"\0") + if p != -1: + s = s[:p] + return s.decode(encoding, errors) + +def nti(s): + """Convert a number field to a python number. + """ + # There are two possible encodings for a number field, see + # itn() below. + if s[0] != chr(0o200): + try: + n = int(nts(s, "ascii", "strict") or "0", 8) + except ValueError: + raise InvalidHeaderError("invalid header") + else: + n = 0 + for i in range(len(s) - 1): + n <<= 8 + n += ord(s[i + 1]) + return n + +def itn(n, digits=8, format=DEFAULT_FORMAT): + """Convert a python number to a number field. + """ + # POSIX 1003.1-1988 requires numbers to be encoded as a string of + # octal digits followed by a null-byte, this allows values up to + # (8**(digits-1))-1. GNU tar allows storing numbers greater than + # that if necessary. A leading 0o200 byte indicates this particular + # encoding, the following digits-1 bytes are a big-endian + # representation. This allows values up to (256**(digits-1))-1. + if 0 <= n < 8 ** (digits - 1): + s = ("%0*o" % (digits - 1, n)).encode("ascii") + NUL + else: + if format != GNU_FORMAT or n >= 256 ** (digits - 1): + raise ValueError("overflow in number field") + + if n < 0: + # XXX We mimic GNU tar's behaviour with negative numbers, + # this could raise OverflowError. + n = struct.unpack("L", struct.pack("l", n))[0] + + s = bytearray() + for i in range(digits - 1): + s.insert(0, n & 0o377) + n >>= 8 + s.insert(0, 0o200) + return s + +def calc_chksums(buf): + """Calculate the checksum for a member's header by summing up all + characters except for the chksum field which is treated as if + it was filled with spaces. According to the GNU tar sources, + some tars (Sun and NeXT) calculate chksum with signed char, + which will be different if there are chars in the buffer with + the high bit set. So we calculate two checksums, unsigned and + signed. + """ + unsigned_chksum = 256 + sum(struct.unpack("148B", buf[:148]) + struct.unpack("356B", buf[156:512])) + signed_chksum = 256 + sum(struct.unpack("148b", buf[:148]) + struct.unpack("356b", buf[156:512])) + return unsigned_chksum, signed_chksum + +def copyfileobj(src, dst, length=None): + """Copy length bytes from fileobj src to fileobj dst. + If length is None, copy the entire content. + """ + if length == 0: + return + if length is None: + while True: + buf = src.read(16*1024) + if not buf: + break + dst.write(buf) + return + + BUFSIZE = 16 * 1024 + blocks, remainder = divmod(length, BUFSIZE) + for b in range(blocks): + buf = src.read(BUFSIZE) + if len(buf) < BUFSIZE: + raise IOError("end of file reached") + dst.write(buf) + + if remainder != 0: + buf = src.read(remainder) + if len(buf) < remainder: + raise IOError("end of file reached") + dst.write(buf) + return + +filemode_table = ( + ((S_IFLNK, "l"), + (S_IFREG, "-"), + (S_IFBLK, "b"), + (S_IFDIR, "d"), + (S_IFCHR, "c"), + (S_IFIFO, "p")), + + ((TUREAD, "r"),), + ((TUWRITE, "w"),), + ((TUEXEC|TSUID, "s"), + (TSUID, "S"), + (TUEXEC, "x")), + + ((TGREAD, "r"),), + ((TGWRITE, "w"),), + ((TGEXEC|TSGID, "s"), + (TSGID, "S"), + (TGEXEC, "x")), + + ((TOREAD, "r"),), + ((TOWRITE, "w"),), + ((TOEXEC|TSVTX, "t"), + (TSVTX, "T"), + (TOEXEC, "x")) +) + +def filemode(mode): + """Convert a file's mode to a string of the form + -rwxrwxrwx. + Used by TarFile.list() + """ + perm = [] + for table in filemode_table: + for bit, char in table: + if mode & bit == bit: + perm.append(char) + break + else: + perm.append("-") + return "".join(perm) + +class TarError(Exception): + """Base exception.""" + pass +class ExtractError(TarError): + """General exception for extract errors.""" + pass +class ReadError(TarError): + """Exception for unreadable tar archives.""" + pass +class CompressionError(TarError): + """Exception for unavailable compression methods.""" + pass +class StreamError(TarError): + """Exception for unsupported operations on stream-like TarFiles.""" + pass +class HeaderError(TarError): + """Base exception for header errors.""" + pass +class EmptyHeaderError(HeaderError): + """Exception for empty headers.""" + pass +class TruncatedHeaderError(HeaderError): + """Exception for truncated headers.""" + pass +class EOFHeaderError(HeaderError): + """Exception for end of file headers.""" + pass +class InvalidHeaderError(HeaderError): + """Exception for invalid headers.""" + pass +class SubsequentHeaderError(HeaderError): + """Exception for missing and invalid extended headers.""" + pass + +#--------------------------- +# internal stream interface +#--------------------------- +class _LowLevelFile(object): + """Low-level file object. Supports reading and writing. + It is used instead of a regular file object for streaming + access. + """ + + def __init__(self, name, mode): + mode = { + "r": os.O_RDONLY, + "w": os.O_WRONLY | os.O_CREAT | os.O_TRUNC, + }[mode] + if hasattr(os, "O_BINARY"): + mode |= os.O_BINARY + self.fd = os.open(name, mode, 0o666) + + def close(self): + os.close(self.fd) + + def read(self, size): + return os.read(self.fd, size) + + def write(self, s): + os.write(self.fd, s) + +class _Stream(object): + """Class that serves as an adapter between TarFile and + a stream-like object. The stream-like object only + needs to have a read() or write() method and is accessed + blockwise. Use of gzip or bzip2 compression is possible. + A stream-like object could be for example: sys.stdin, + sys.stdout, a socket, a tape device etc. + + _Stream is intended to be used only internally. + """ + + def __init__(self, name, mode, comptype, fileobj, bufsize): + """Construct a _Stream object. + """ + self._extfileobj = True + if fileobj is None: + fileobj = _LowLevelFile(name, mode) + self._extfileobj = False + + if comptype == '*': + # Enable transparent compression detection for the + # stream interface + fileobj = _StreamProxy(fileobj) + comptype = fileobj.getcomptype() + + self.name = name or "" + self.mode = mode + self.comptype = comptype + self.fileobj = fileobj + self.bufsize = bufsize + self.buf = b"" + self.pos = 0 + self.closed = False + + try: + if comptype == "gz": + try: + import zlib + except ImportError: + raise CompressionError("zlib module is not available") + self.zlib = zlib + self.crc = zlib.crc32(b"") + if mode == "r": + self._init_read_gz() + else: + self._init_write_gz() + + if comptype == "bz2": + try: + import bz2 + except ImportError: + raise CompressionError("bz2 module is not available") + if mode == "r": + self.dbuf = b"" + self.cmp = bz2.BZ2Decompressor() + else: + self.cmp = bz2.BZ2Compressor() + except: + if not self._extfileobj: + self.fileobj.close() + self.closed = True + raise + + def __del__(self): + if hasattr(self, "closed") and not self.closed: + self.close() + + def _init_write_gz(self): + """Initialize for writing with gzip compression. + """ + self.cmp = self.zlib.compressobj(9, self.zlib.DEFLATED, + -self.zlib.MAX_WBITS, + self.zlib.DEF_MEM_LEVEL, + 0) + timestamp = struct.pack("<L", int(time.time())) + self.__write(b"\037\213\010\010" + timestamp + b"\002\377") + if self.name.endswith(".gz"): + self.name = self.name[:-3] + # RFC1952 says we must use ISO-8859-1 for the FNAME field. + self.__write(self.name.encode("iso-8859-1", "replace") + NUL) + + def write(self, s): + """Write string s to the stream. + """ + if self.comptype == "gz": + self.crc = self.zlib.crc32(s, self.crc) + self.pos += len(s) + if self.comptype != "tar": + s = self.cmp.compress(s) + self.__write(s) + + def __write(self, s): + """Write string s to the stream if a whole new block + is ready to be written. + """ + self.buf += s + while len(self.buf) > self.bufsize: + self.fileobj.write(self.buf[:self.bufsize]) + self.buf = self.buf[self.bufsize:] + + def close(self): + """Close the _Stream object. No operation should be + done on it afterwards. + """ + if self.closed: + return + + if self.mode == "w" and self.comptype != "tar": + self.buf += self.cmp.flush() + + if self.mode == "w" and self.buf: + self.fileobj.write(self.buf) + self.buf = b"" + if self.comptype == "gz": + # The native zlib crc is an unsigned 32-bit integer, but + # the Python wrapper implicitly casts that to a signed C + # long. So, on a 32-bit box self.crc may "look negative", + # while the same crc on a 64-bit box may "look positive". + # To avoid irksome warnings from the `struct` module, force + # it to look positive on all boxes. + self.fileobj.write(struct.pack("<L", self.crc & 0xffffffff)) + self.fileobj.write(struct.pack("<L", self.pos & 0xffffFFFF)) + + if not self._extfileobj: + self.fileobj.close() + + self.closed = True + + def _init_read_gz(self): + """Initialize for reading a gzip compressed fileobj. + """ + self.cmp = self.zlib.decompressobj(-self.zlib.MAX_WBITS) + self.dbuf = b"" + + # taken from gzip.GzipFile with some alterations + if self.__read(2) != b"\037\213": + raise ReadError("not a gzip file") + if self.__read(1) != b"\010": + raise CompressionError("unsupported compression method") + + flag = ord(self.__read(1)) + self.__read(6) + + if flag & 4: + xlen = ord(self.__read(1)) + 256 * ord(self.__read(1)) + self.read(xlen) + if flag & 8: + while True: + s = self.__read(1) + if not s or s == NUL: + break + if flag & 16: + while True: + s = self.__read(1) + if not s or s == NUL: + break + if flag & 2: + self.__read(2) + + def tell(self): + """Return the stream's file pointer position. + """ + return self.pos + + def seek(self, pos=0): + """Set the stream's file pointer to pos. Negative seeking + is forbidden. + """ + if pos - self.pos >= 0: + blocks, remainder = divmod(pos - self.pos, self.bufsize) + for i in range(blocks): + self.read(self.bufsize) + self.read(remainder) + else: + raise StreamError("seeking backwards is not allowed") + return self.pos + + def read(self, size=None): + """Return the next size number of bytes from the stream. + If size is not defined, return all bytes of the stream + up to EOF. + """ + if size is None: + t = [] + while True: + buf = self._read(self.bufsize) + if not buf: + break + t.append(buf) + buf = "".join(t) + else: + buf = self._read(size) + self.pos += len(buf) + return buf + + def _read(self, size): + """Return size bytes from the stream. + """ + if self.comptype == "tar": + return self.__read(size) + + c = len(self.dbuf) + while c < size: + buf = self.__read(self.bufsize) + if not buf: + break + try: + buf = self.cmp.decompress(buf) + except IOError: + raise ReadError("invalid compressed data") + self.dbuf += buf + c += len(buf) + buf = self.dbuf[:size] + self.dbuf = self.dbuf[size:] + return buf + + def __read(self, size): + """Return size bytes from stream. If internal buffer is empty, + read another block from the stream. + """ + c = len(self.buf) + while c < size: + buf = self.fileobj.read(self.bufsize) + if not buf: + break + self.buf += buf + c += len(buf) + buf = self.buf[:size] + self.buf = self.buf[size:] + return buf +# class _Stream + +class _StreamProxy(object): + """Small proxy class that enables transparent compression + detection for the Stream interface (mode 'r|*'). + """ + + def __init__(self, fileobj): + self.fileobj = fileobj + self.buf = self.fileobj.read(BLOCKSIZE) + + def read(self, size): + self.read = self.fileobj.read + return self.buf + + def getcomptype(self): + if self.buf.startswith(b"\037\213\010"): + return "gz" + if self.buf.startswith(b"BZh91"): + return "bz2" + return "tar" + + def close(self): + self.fileobj.close() +# class StreamProxy + +class _BZ2Proxy(object): + """Small proxy class that enables external file object + support for "r:bz2" and "w:bz2" modes. This is actually + a workaround for a limitation in bz2 module's BZ2File + class which (unlike gzip.GzipFile) has no support for + a file object argument. + """ + + blocksize = 16 * 1024 + + def __init__(self, fileobj, mode): + self.fileobj = fileobj + self.mode = mode + self.name = getattr(self.fileobj, "name", None) + self.init() + + def init(self): + import bz2 + self.pos = 0 + if self.mode == "r": + self.bz2obj = bz2.BZ2Decompressor() + self.fileobj.seek(0) + self.buf = b"" + else: + self.bz2obj = bz2.BZ2Compressor() + + def read(self, size): + x = len(self.buf) + while x < size: + raw = self.fileobj.read(self.blocksize) + if not raw: + break + data = self.bz2obj.decompress(raw) + self.buf += data + x += len(data) + + buf = self.buf[:size] + self.buf = self.buf[size:] + self.pos += len(buf) + return buf + + def seek(self, pos): + if pos < self.pos: + self.init() + self.read(pos - self.pos) + + def tell(self): + return self.pos + + def write(self, data): + self.pos += len(data) + raw = self.bz2obj.compress(data) + self.fileobj.write(raw) + + def close(self): + if self.mode == "w": + raw = self.bz2obj.flush() + self.fileobj.write(raw) +# class _BZ2Proxy + +#------------------------ +# Extraction file object +#------------------------ +class _FileInFile(object): + """A thin wrapper around an existing file object that + provides a part of its data as an individual file + object. + """ + + def __init__(self, fileobj, offset, size, blockinfo=None): + self.fileobj = fileobj + self.offset = offset + self.size = size + self.position = 0 + + if blockinfo is None: + blockinfo = [(0, size)] + + # Construct a map with data and zero blocks. + self.map_index = 0 + self.map = [] + lastpos = 0 + realpos = self.offset + for offset, size in blockinfo: + if offset > lastpos: + self.map.append((False, lastpos, offset, None)) + self.map.append((True, offset, offset + size, realpos)) + realpos += size + lastpos = offset + size + if lastpos < self.size: + self.map.append((False, lastpos, self.size, None)) + + def seekable(self): + if not hasattr(self.fileobj, "seekable"): + # XXX gzip.GzipFile and bz2.BZ2File + return True + return self.fileobj.seekable() + + def tell(self): + """Return the current file position. + """ + return self.position + + def seek(self, position): + """Seek to a position in the file. + """ + self.position = position + + def read(self, size=None): + """Read data from the file. + """ + if size is None: + size = self.size - self.position + else: + size = min(size, self.size - self.position) + + buf = b"" + while size > 0: + while True: + data, start, stop, offset = self.map[self.map_index] + if start <= self.position < stop: + break + else: + self.map_index += 1 + if self.map_index == len(self.map): + self.map_index = 0 + length = min(size, stop - self.position) + if data: + self.fileobj.seek(offset + (self.position - start)) + buf += self.fileobj.read(length) + else: + buf += NUL * length + size -= length + self.position += length + return buf +#class _FileInFile + + +class ExFileObject(object): + """File-like object for reading an archive member. + Is returned by TarFile.extractfile(). + """ + blocksize = 1024 + + def __init__(self, tarfile, tarinfo): + self.fileobj = _FileInFile(tarfile.fileobj, + tarinfo.offset_data, + tarinfo.size, + tarinfo.sparse) + self.name = tarinfo.name + self.mode = "r" + self.closed = False + self.size = tarinfo.size + + self.position = 0 + self.buffer = b"" + + def readable(self): + return True + + def writable(self): + return False + + def seekable(self): + return self.fileobj.seekable() + + def read(self, size=None): + """Read at most size bytes from the file. If size is not + present or None, read all data until EOF is reached. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + buf = b"" + if self.buffer: + if size is None: + buf = self.buffer + self.buffer = b"" + else: + buf = self.buffer[:size] + self.buffer = self.buffer[size:] + + if size is None: + buf += self.fileobj.read() + else: + buf += self.fileobj.read(size - len(buf)) + + self.position += len(buf) + return buf + + # XXX TextIOWrapper uses the read1() method. + read1 = read + + def readline(self, size=-1): + """Read one entire line from the file. If size is present + and non-negative, return a string with at most that + size, which may be an incomplete line. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + pos = self.buffer.find(b"\n") + 1 + if pos == 0: + # no newline found. + while True: + buf = self.fileobj.read(self.blocksize) + self.buffer += buf + if not buf or b"\n" in buf: + pos = self.buffer.find(b"\n") + 1 + if pos == 0: + # no newline found. + pos = len(self.buffer) + break + + if size != -1: + pos = min(size, pos) + + buf = self.buffer[:pos] + self.buffer = self.buffer[pos:] + self.position += len(buf) + return buf + + def readlines(self): + """Return a list with all remaining lines. + """ + result = [] + while True: + line = self.readline() + if not line: break + result.append(line) + return result + + def tell(self): + """Return the current file position. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + return self.position + + def seek(self, pos, whence=os.SEEK_SET): + """Seek to a position in the file. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + if whence == os.SEEK_SET: + self.position = min(max(pos, 0), self.size) + elif whence == os.SEEK_CUR: + if pos < 0: + self.position = max(self.position + pos, 0) + else: + self.position = min(self.position + pos, self.size) + elif whence == os.SEEK_END: + self.position = max(min(self.size + pos, self.size), 0) + else: + raise ValueError("Invalid argument") + + self.buffer = b"" + self.fileobj.seek(self.position) + + def close(self): + """Close the file object. + """ + self.closed = True + + def __iter__(self): + """Get an iterator over the file's lines. + """ + while True: + line = self.readline() + if not line: + break + yield line +#class ExFileObject + +#------------------ +# Exported Classes +#------------------ +class TarInfo(object): + """Informational class which holds the details about an + archive member given by a tar header block. + TarInfo objects are returned by TarFile.getmember(), + TarFile.getmembers() and TarFile.gettarinfo() and are + usually created internally. + """ + + __slots__ = ("name", "mode", "uid", "gid", "size", "mtime", + "chksum", "type", "linkname", "uname", "gname", + "devmajor", "devminor", + "offset", "offset_data", "pax_headers", "sparse", + "tarfile", "_sparse_structs", "_link_target") + + def __init__(self, name=""): + """Construct a TarInfo object. name is the optional name + of the member. + """ + self.name = name # member name + self.mode = 0o644 # file permissions + self.uid = 0 # user id + self.gid = 0 # group id + self.size = 0 # file size + self.mtime = 0 # modification time + self.chksum = 0 # header checksum + self.type = REGTYPE # member type + self.linkname = "" # link name + self.uname = "" # user name + self.gname = "" # group name + self.devmajor = 0 # device major number + self.devminor = 0 # device minor number + + self.offset = 0 # the tar header starts here + self.offset_data = 0 # the file's data starts here + + self.sparse = None # sparse member information + self.pax_headers = {} # pax header information + + # In pax headers the "name" and "linkname" field are called + # "path" and "linkpath". + def _getpath(self): + return self.name + def _setpath(self, name): + self.name = name + path = property(_getpath, _setpath) + + def _getlinkpath(self): + return self.linkname + def _setlinkpath(self, linkname): + self.linkname = linkname + linkpath = property(_getlinkpath, _setlinkpath) + + def __repr__(self): + return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) + + def get_info(self): + """Return the TarInfo's attributes as a dictionary. + """ + info = { + "name": self.name, + "mode": self.mode & 0o7777, + "uid": self.uid, + "gid": self.gid, + "size": self.size, + "mtime": self.mtime, + "chksum": self.chksum, + "type": self.type, + "linkname": self.linkname, + "uname": self.uname, + "gname": self.gname, + "devmajor": self.devmajor, + "devminor": self.devminor + } + + if info["type"] == DIRTYPE and not info["name"].endswith("/"): + info["name"] += "/" + + return info + + def tobuf(self, format=DEFAULT_FORMAT, encoding=ENCODING, errors="surrogateescape"): + """Return a tar header as a string of 512 byte blocks. + """ + info = self.get_info() + + if format == USTAR_FORMAT: + return self.create_ustar_header(info, encoding, errors) + elif format == GNU_FORMAT: + return self.create_gnu_header(info, encoding, errors) + elif format == PAX_FORMAT: + return self.create_pax_header(info, encoding) + else: + raise ValueError("invalid format") + + def create_ustar_header(self, info, encoding, errors): + """Return the object as a ustar header block. + """ + info["magic"] = POSIX_MAGIC + + if len(info["linkname"]) > LENGTH_LINK: + raise ValueError("linkname is too long") + + if len(info["name"]) > LENGTH_NAME: + info["prefix"], info["name"] = self._posix_split_name(info["name"]) + + return self._create_header(info, USTAR_FORMAT, encoding, errors) + + def create_gnu_header(self, info, encoding, errors): + """Return the object as a GNU header block sequence. + """ + info["magic"] = GNU_MAGIC + + buf = b"" + if len(info["linkname"]) > LENGTH_LINK: + buf += self._create_gnu_long_header(info["linkname"], GNUTYPE_LONGLINK, encoding, errors) + + if len(info["name"]) > LENGTH_NAME: + buf += self._create_gnu_long_header(info["name"], GNUTYPE_LONGNAME, encoding, errors) + + return buf + self._create_header(info, GNU_FORMAT, encoding, errors) + + def create_pax_header(self, info, encoding): + """Return the object as a ustar header block. If it cannot be + represented this way, prepend a pax extended header sequence + with supplement information. + """ + info["magic"] = POSIX_MAGIC + pax_headers = self.pax_headers.copy() + + # Test string fields for values that exceed the field length or cannot + # be represented in ASCII encoding. + for name, hname, length in ( + ("name", "path", LENGTH_NAME), ("linkname", "linkpath", LENGTH_LINK), + ("uname", "uname", 32), ("gname", "gname", 32)): + + if hname in pax_headers: + # The pax header has priority. + continue + + # Try to encode the string as ASCII. + try: + info[name].encode("ascii", "strict") + except UnicodeEncodeError: + pax_headers[hname] = info[name] + continue + + if len(info[name]) > length: + pax_headers[hname] = info[name] + + # Test number fields for values that exceed the field limit or values + # that like to be stored as float. + for name, digits in (("uid", 8), ("gid", 8), ("size", 12), ("mtime", 12)): + if name in pax_headers: + # The pax header has priority. Avoid overflow. + info[name] = 0 + continue + + val = info[name] + if not 0 <= val < 8 ** (digits - 1) or isinstance(val, float): + pax_headers[name] = str(val) + info[name] = 0 + + # Create a pax extended header if necessary. + if pax_headers: + buf = self._create_pax_generic_header(pax_headers, XHDTYPE, encoding) + else: + buf = b"" + + return buf + self._create_header(info, USTAR_FORMAT, "ascii", "replace") + + @classmethod + def create_pax_global_header(cls, pax_headers): + """Return the object as a pax global header block sequence. + """ + return cls._create_pax_generic_header(pax_headers, XGLTYPE, "utf8") + + def _posix_split_name(self, name): + """Split a name longer than 100 chars into a prefix + and a name part. + """ + prefix = name[:LENGTH_PREFIX + 1] + while prefix and prefix[-1] != "/": + prefix = prefix[:-1] + + name = name[len(prefix):] + prefix = prefix[:-1] + + if not prefix or len(name) > LENGTH_NAME: + raise ValueError("name is too long") + return prefix, name + + @staticmethod + def _create_header(info, format, encoding, errors): + """Return a header block. info is a dictionary with file + information, format must be one of the *_FORMAT constants. + """ + parts = [ + stn(info.get("name", ""), 100, encoding, errors), + itn(info.get("mode", 0) & 0o7777, 8, format), + itn(info.get("uid", 0), 8, format), + itn(info.get("gid", 0), 8, format), + itn(info.get("size", 0), 12, format), + itn(info.get("mtime", 0), 12, format), + b" ", # checksum field + info.get("type", REGTYPE), + stn(info.get("linkname", ""), 100, encoding, errors), + info.get("magic", POSIX_MAGIC), + stn(info.get("uname", ""), 32, encoding, errors), + stn(info.get("gname", ""), 32, encoding, errors), + itn(info.get("devmajor", 0), 8, format), + itn(info.get("devminor", 0), 8, format), + stn(info.get("prefix", ""), 155, encoding, errors) + ] + + buf = struct.pack("%ds" % BLOCKSIZE, b"".join(parts)) + chksum = calc_chksums(buf[-BLOCKSIZE:])[0] + buf = buf[:-364] + ("%06o\0" % chksum).encode("ascii") + buf[-357:] + return buf + + @staticmethod + def _create_payload(payload): + """Return the string payload filled with zero bytes + up to the next 512 byte border. + """ + blocks, remainder = divmod(len(payload), BLOCKSIZE) + if remainder > 0: + payload += (BLOCKSIZE - remainder) * NUL + return payload + + @classmethod + def _create_gnu_long_header(cls, name, type, encoding, errors): + """Return a GNUTYPE_LONGNAME or GNUTYPE_LONGLINK sequence + for name. + """ + name = name.encode(encoding, errors) + NUL + + info = {} + info["name"] = "././@LongLink" + info["type"] = type + info["size"] = len(name) + info["magic"] = GNU_MAGIC + + # create extended header + name blocks. + return cls._create_header(info, USTAR_FORMAT, encoding, errors) + \ + cls._create_payload(name) + + @classmethod + def _create_pax_generic_header(cls, pax_headers, type, encoding): + """Return a POSIX.1-2008 extended or global header sequence + that contains a list of keyword, value pairs. The values + must be strings. + """ + # Check if one of the fields contains surrogate characters and thereby + # forces hdrcharset=BINARY, see _proc_pax() for more information. + binary = False + for keyword, value in pax_headers.items(): + try: + value.encode("utf8", "strict") + except UnicodeEncodeError: + binary = True + break + + records = b"" + if binary: + # Put the hdrcharset field at the beginning of the header. + records += b"21 hdrcharset=BINARY\n" + + for keyword, value in pax_headers.items(): + keyword = keyword.encode("utf8") + if binary: + # Try to restore the original byte representation of `value'. + # Needless to say, that the encoding must match the string. + value = value.encode(encoding, "surrogateescape") + else: + value = value.encode("utf8") + + l = len(keyword) + len(value) + 3 # ' ' + '=' + '\n' + n = p = 0 + while True: + n = l + len(str(p)) + if n == p: + break + p = n + records += bytes(str(p), "ascii") + b" " + keyword + b"=" + value + b"\n" + + # We use a hardcoded "././@PaxHeader" name like star does + # instead of the one that POSIX recommends. + info = {} + info["name"] = "././@PaxHeader" + info["type"] = type + info["size"] = len(records) + info["magic"] = POSIX_MAGIC + + # Create pax header + record blocks. + return cls._create_header(info, USTAR_FORMAT, "ascii", "replace") + \ + cls._create_payload(records) + + @classmethod + def frombuf(cls, buf, encoding, errors): + """Construct a TarInfo object from a 512 byte bytes object. + """ + if len(buf) == 0: + raise EmptyHeaderError("empty header") + if len(buf) != BLOCKSIZE: + raise TruncatedHeaderError("truncated header") + if buf.count(NUL) == BLOCKSIZE: + raise EOFHeaderError("end of file header") + + chksum = nti(buf[148:156]) + if chksum not in calc_chksums(buf): + raise InvalidHeaderError("bad checksum") + + obj = cls() + obj.name = nts(buf[0:100], encoding, errors) + obj.mode = nti(buf[100:108]) + obj.uid = nti(buf[108:116]) + obj.gid = nti(buf[116:124]) + obj.size = nti(buf[124:136]) + obj.mtime = nti(buf[136:148]) + obj.chksum = chksum + obj.type = buf[156:157] + obj.linkname = nts(buf[157:257], encoding, errors) + obj.uname = nts(buf[265:297], encoding, errors) + obj.gname = nts(buf[297:329], encoding, errors) + obj.devmajor = nti(buf[329:337]) + obj.devminor = nti(buf[337:345]) + prefix = nts(buf[345:500], encoding, errors) + + # Old V7 tar format represents a directory as a regular + # file with a trailing slash. + if obj.type == AREGTYPE and obj.name.endswith("/"): + obj.type = DIRTYPE + + # The old GNU sparse format occupies some of the unused + # space in the buffer for up to 4 sparse structures. + # Save the them for later processing in _proc_sparse(). + if obj.type == GNUTYPE_SPARSE: + pos = 386 + structs = [] + for i in range(4): + try: + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) + except ValueError: + break + structs.append((offset, numbytes)) + pos += 24 + isextended = bool(buf[482]) + origsize = nti(buf[483:495]) + obj._sparse_structs = (structs, isextended, origsize) + + # Remove redundant slashes from directories. + if obj.isdir(): + obj.name = obj.name.rstrip("/") + + # Reconstruct a ustar longname. + if prefix and obj.type not in GNU_TYPES: + obj.name = prefix + "/" + obj.name + return obj + + @classmethod + def fromtarfile(cls, tarfile): + """Return the next TarInfo object from TarFile object + tarfile. + """ + buf = tarfile.fileobj.read(BLOCKSIZE) + obj = cls.frombuf(buf, tarfile.encoding, tarfile.errors) + obj.offset = tarfile.fileobj.tell() - BLOCKSIZE + return obj._proc_member(tarfile) + + #-------------------------------------------------------------------------- + # The following are methods that are called depending on the type of a + # member. The entry point is _proc_member() which can be overridden in a + # subclass to add custom _proc_*() methods. A _proc_*() method MUST + # implement the following + # operations: + # 1. Set self.offset_data to the position where the data blocks begin, + # if there is data that follows. + # 2. Set tarfile.offset to the position where the next member's header will + # begin. + # 3. Return self or another valid TarInfo object. + def _proc_member(self, tarfile): + """Choose the right processing method depending on + the type and call it. + """ + if self.type in (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK): + return self._proc_gnulong(tarfile) + elif self.type == GNUTYPE_SPARSE: + return self._proc_sparse(tarfile) + elif self.type in (XHDTYPE, XGLTYPE, SOLARIS_XHDTYPE): + return self._proc_pax(tarfile) + else: + return self._proc_builtin(tarfile) + + def _proc_builtin(self, tarfile): + """Process a builtin type or an unknown type which + will be treated as a regular file. + """ + self.offset_data = tarfile.fileobj.tell() + offset = self.offset_data + if self.isreg() or self.type not in SUPPORTED_TYPES: + # Skip the following data blocks. + offset += self._block(self.size) + tarfile.offset = offset + + # Patch the TarInfo object with saved global + # header information. + self._apply_pax_info(tarfile.pax_headers, tarfile.encoding, tarfile.errors) + + return self + + def _proc_gnulong(self, tarfile): + """Process the blocks that hold a GNU longname + or longlink member. + """ + buf = tarfile.fileobj.read(self._block(self.size)) + + # Fetch the next header and process it. + try: + next = self.fromtarfile(tarfile) + except HeaderError: + raise SubsequentHeaderError("missing or bad subsequent header") + + # Patch the TarInfo object from the next header with + # the longname information. + next.offset = self.offset + if self.type == GNUTYPE_LONGNAME: + next.name = nts(buf, tarfile.encoding, tarfile.errors) + elif self.type == GNUTYPE_LONGLINK: + next.linkname = nts(buf, tarfile.encoding, tarfile.errors) + + return next + + def _proc_sparse(self, tarfile): + """Process a GNU sparse header plus extra headers. + """ + # We already collected some sparse structures in frombuf(). + structs, isextended, origsize = self._sparse_structs + del self._sparse_structs + + # Collect sparse structures from extended header blocks. + while isextended: + buf = tarfile.fileobj.read(BLOCKSIZE) + pos = 0 + for i in range(21): + try: + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) + except ValueError: + break + if offset and numbytes: + structs.append((offset, numbytes)) + pos += 24 + isextended = bool(buf[504]) + self.sparse = structs + + self.offset_data = tarfile.fileobj.tell() + tarfile.offset = self.offset_data + self._block(self.size) + self.size = origsize + return self + + def _proc_pax(self, tarfile): + """Process an extended or global header as described in + POSIX.1-2008. + """ + # Read the header information. + buf = tarfile.fileobj.read(self._block(self.size)) + + # A pax header stores supplemental information for either + # the following file (extended) or all following files + # (global). + if self.type == XGLTYPE: + pax_headers = tarfile.pax_headers + else: + pax_headers = tarfile.pax_headers.copy() + + # Check if the pax header contains a hdrcharset field. This tells us + # the encoding of the path, linkpath, uname and gname fields. Normally, + # these fields are UTF-8 encoded but since POSIX.1-2008 tar + # implementations are allowed to store them as raw binary strings if + # the translation to UTF-8 fails. + match = re.search(br"\d+ hdrcharset=([^\n]+)\n", buf) + if match is not None: + pax_headers["hdrcharset"] = match.group(1).decode("utf8") + + # For the time being, we don't care about anything other than "BINARY". + # The only other value that is currently allowed by the standard is + # "ISO-IR 10646 2000 UTF-8" in other words UTF-8. + hdrcharset = pax_headers.get("hdrcharset") + if hdrcharset == "BINARY": + encoding = tarfile.encoding + else: + encoding = "utf8" + + # Parse pax header information. A record looks like that: + # "%d %s=%s\n" % (length, keyword, value). length is the size + # of the complete record including the length field itself and + # the newline. keyword and value are both UTF-8 encoded strings. + regex = re.compile(br"(\d+) ([^=]+)=") + pos = 0 + while True: + match = regex.match(buf, pos) + if not match: + break + + length, keyword = match.groups() + length = int(length) + value = buf[match.end(2) + 1:match.start(1) + length - 1] + + # Normally, we could just use "utf8" as the encoding and "strict" + # as the error handler, but we better not take the risk. For + # example, GNU tar <= 1.23 is known to store filenames it cannot + # translate to UTF-8 as raw strings (unfortunately without a + # hdrcharset=BINARY header). + # We first try the strict standard encoding, and if that fails we + # fall back on the user's encoding and error handler. + keyword = self._decode_pax_field(keyword, "utf8", "utf8", + tarfile.errors) + if keyword in PAX_NAME_FIELDS: + value = self._decode_pax_field(value, encoding, tarfile.encoding, + tarfile.errors) + else: + value = self._decode_pax_field(value, "utf8", "utf8", + tarfile.errors) + + pax_headers[keyword] = value + pos += length + + # Fetch the next header. + try: + next = self.fromtarfile(tarfile) + except HeaderError: + raise SubsequentHeaderError("missing or bad subsequent header") + + # Process GNU sparse information. + if "GNU.sparse.map" in pax_headers: + # GNU extended sparse format version 0.1. + self._proc_gnusparse_01(next, pax_headers) + + elif "GNU.sparse.size" in pax_headers: + # GNU extended sparse format version 0.0. + self._proc_gnusparse_00(next, pax_headers, buf) + + elif pax_headers.get("GNU.sparse.major") == "1" and pax_headers.get("GNU.sparse.minor") == "0": + # GNU extended sparse format version 1.0. + self._proc_gnusparse_10(next, pax_headers, tarfile) + + if self.type in (XHDTYPE, SOLARIS_XHDTYPE): + # Patch the TarInfo object with the extended header info. + next._apply_pax_info(pax_headers, tarfile.encoding, tarfile.errors) + next.offset = self.offset + + if "size" in pax_headers: + # If the extended header replaces the size field, + # we need to recalculate the offset where the next + # header starts. + offset = next.offset_data + if next.isreg() or next.type not in SUPPORTED_TYPES: + offset += next._block(next.size) + tarfile.offset = offset + + return next + + def _proc_gnusparse_00(self, next, pax_headers, buf): + """Process a GNU tar extended sparse header, version 0.0. + """ + offsets = [] + for match in re.finditer(br"\d+ GNU.sparse.offset=(\d+)\n", buf): + offsets.append(int(match.group(1))) + numbytes = [] + for match in re.finditer(br"\d+ GNU.sparse.numbytes=(\d+)\n", buf): + numbytes.append(int(match.group(1))) + next.sparse = list(zip(offsets, numbytes)) + + def _proc_gnusparse_01(self, next, pax_headers): + """Process a GNU tar extended sparse header, version 0.1. + """ + sparse = [int(x) for x in pax_headers["GNU.sparse.map"].split(",")] + next.sparse = list(zip(sparse[::2], sparse[1::2])) + + def _proc_gnusparse_10(self, next, pax_headers, tarfile): + """Process a GNU tar extended sparse header, version 1.0. + """ + fields = None + sparse = [] + buf = tarfile.fileobj.read(BLOCKSIZE) + fields, buf = buf.split(b"\n", 1) + fields = int(fields) + while len(sparse) < fields * 2: + if b"\n" not in buf: + buf += tarfile.fileobj.read(BLOCKSIZE) + number, buf = buf.split(b"\n", 1) + sparse.append(int(number)) + next.offset_data = tarfile.fileobj.tell() + next.sparse = list(zip(sparse[::2], sparse[1::2])) + + def _apply_pax_info(self, pax_headers, encoding, errors): + """Replace fields with supplemental information from a previous + pax extended or global header. + """ + for keyword, value in pax_headers.items(): + if keyword == "GNU.sparse.name": + setattr(self, "path", value) + elif keyword == "GNU.sparse.size": + setattr(self, "size", int(value)) + elif keyword == "GNU.sparse.realsize": + setattr(self, "size", int(value)) + elif keyword in PAX_FIELDS: + if keyword in PAX_NUMBER_FIELDS: + try: + value = PAX_NUMBER_FIELDS[keyword](value) + except ValueError: + value = 0 + if keyword == "path": + value = value.rstrip("/") + setattr(self, keyword, value) + + self.pax_headers = pax_headers.copy() + + def _decode_pax_field(self, value, encoding, fallback_encoding, fallback_errors): + """Decode a single field from a pax record. + """ + try: + return value.decode(encoding, "strict") + except UnicodeDecodeError: + return value.decode(fallback_encoding, fallback_errors) + + def _block(self, count): + """Round up a byte count by BLOCKSIZE and return it, + e.g. _block(834) => 1024. + """ + blocks, remainder = divmod(count, BLOCKSIZE) + if remainder: + blocks += 1 + return blocks * BLOCKSIZE + + def isreg(self): + return self.type in REGULAR_TYPES + def isfile(self): + return self.isreg() + def isdir(self): + return self.type == DIRTYPE + def issym(self): + return self.type == SYMTYPE + def islnk(self): + return self.type == LNKTYPE + def ischr(self): + return self.type == CHRTYPE + def isblk(self): + return self.type == BLKTYPE + def isfifo(self): + return self.type == FIFOTYPE + def issparse(self): + return self.sparse is not None + def isdev(self): + return self.type in (CHRTYPE, BLKTYPE, FIFOTYPE) +# class TarInfo + +class TarFile(object): + """The TarFile Class provides an interface to tar archives. + """ + + debug = 0 # May be set from 0 (no msgs) to 3 (all msgs) + + dereference = False # If true, add content of linked file to the + # tar file, else the link. + + ignore_zeros = False # If true, skips empty or invalid blocks and + # continues processing. + + errorlevel = 1 # If 0, fatal errors only appear in debug + # messages (if debug >= 0). If > 0, errors + # are passed to the caller as exceptions. + + format = DEFAULT_FORMAT # The format to use when creating an archive. + + encoding = ENCODING # Encoding for 8-bit character strings. + + errors = None # Error handler for unicode conversion. + + tarinfo = TarInfo # The default TarInfo class to use. + + fileobject = ExFileObject # The default ExFileObject class to use. + + def __init__(self, name=None, mode="r", fileobj=None, format=None, + tarinfo=None, dereference=None, ignore_zeros=None, encoding=None, + errors="surrogateescape", pax_headers=None, debug=None, errorlevel=None): + """Open an (uncompressed) tar archive `name'. `mode' is either 'r' to + read from an existing archive, 'a' to append data to an existing + file or 'w' to create a new file overwriting an existing one. `mode' + defaults to 'r'. + If `fileobj' is given, it is used for reading or writing data. If it + can be determined, `mode' is overridden by `fileobj's mode. + `fileobj' is not closed, when TarFile is closed. + """ + if len(mode) > 1 or mode not in "raw": + raise ValueError("mode must be 'r', 'a' or 'w'") + self.mode = mode + self._mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode] + + if not fileobj: + if self.mode == "a" and not os.path.exists(name): + # Create nonexistent files in append mode. + self.mode = "w" + self._mode = "wb" + fileobj = bltn_open(name, self._mode) + self._extfileobj = False + else: + if name is None and hasattr(fileobj, "name"): + name = fileobj.name + if hasattr(fileobj, "mode"): + self._mode = fileobj.mode + self._extfileobj = True + self.name = os.path.abspath(name) if name else None + self.fileobj = fileobj + + # Init attributes. + if format is not None: + self.format = format + if tarinfo is not None: + self.tarinfo = tarinfo + if dereference is not None: + self.dereference = dereference + if ignore_zeros is not None: + self.ignore_zeros = ignore_zeros + if encoding is not None: + self.encoding = encoding + self.errors = errors + + if pax_headers is not None and self.format == PAX_FORMAT: + self.pax_headers = pax_headers + else: + self.pax_headers = {} + + if debug is not None: + self.debug = debug + if errorlevel is not None: + self.errorlevel = errorlevel + + # Init datastructures. + self.closed = False + self.members = [] # list of members as TarInfo objects + self._loaded = False # flag if all members have been read + self.offset = self.fileobj.tell() + # current position in the archive file + self.inodes = {} # dictionary caching the inodes of + # archive members already added + + try: + if self.mode == "r": + self.firstmember = None + self.firstmember = self.next() + + if self.mode == "a": + # Move to the end of the archive, + # before the first empty block. + while True: + self.fileobj.seek(self.offset) + try: + tarinfo = self.tarinfo.fromtarfile(self) + self.members.append(tarinfo) + except EOFHeaderError: + self.fileobj.seek(self.offset) + break + except HeaderError as e: + raise ReadError(str(e)) + + if self.mode in "aw": + self._loaded = True + + if self.pax_headers: + buf = self.tarinfo.create_pax_global_header(self.pax_headers.copy()) + self.fileobj.write(buf) + self.offset += len(buf) + except: + if not self._extfileobj: + self.fileobj.close() + self.closed = True + raise + + #-------------------------------------------------------------------------- + # Below are the classmethods which act as alternate constructors to the + # TarFile class. The open() method is the only one that is needed for + # public use; it is the "super"-constructor and is able to select an + # adequate "sub"-constructor for a particular compression using the mapping + # from OPEN_METH. + # + # This concept allows one to subclass TarFile without losing the comfort of + # the super-constructor. A sub-constructor is registered and made available + # by adding it to the mapping in OPEN_METH. + + @classmethod + def open(cls, name=None, mode="r", fileobj=None, bufsize=RECORDSIZE, **kwargs): + """Open a tar archive for reading, writing or appending. Return + an appropriate TarFile class. + + mode: + 'r' or 'r:*' open for reading with transparent compression + 'r:' open for reading exclusively uncompressed + 'r:gz' open for reading with gzip compression + 'r:bz2' open for reading with bzip2 compression + 'a' or 'a:' open for appending, creating the file if necessary + 'w' or 'w:' open for writing without compression + 'w:gz' open for writing with gzip compression + 'w:bz2' open for writing with bzip2 compression + + 'r|*' open a stream of tar blocks with transparent compression + 'r|' open an uncompressed stream of tar blocks for reading + 'r|gz' open a gzip compressed stream of tar blocks + 'r|bz2' open a bzip2 compressed stream of tar blocks + 'w|' open an uncompressed stream for writing + 'w|gz' open a gzip compressed stream for writing + 'w|bz2' open a bzip2 compressed stream for writing + """ + + if not name and not fileobj: + raise ValueError("nothing to open") + + if mode in ("r", "r:*"): + # Find out which *open() is appropriate for opening the file. + for comptype in cls.OPEN_METH: + func = getattr(cls, cls.OPEN_METH[comptype]) + if fileobj is not None: + saved_pos = fileobj.tell() + try: + return func(name, "r", fileobj, **kwargs) + except (ReadError, CompressionError) as e: + if fileobj is not None: + fileobj.seek(saved_pos) + continue + raise ReadError("file could not be opened successfully") + + elif ":" in mode: + filemode, comptype = mode.split(":", 1) + filemode = filemode or "r" + comptype = comptype or "tar" + + # Select the *open() function according to + # given compression. + if comptype in cls.OPEN_METH: + func = getattr(cls, cls.OPEN_METH[comptype]) + else: + raise CompressionError("unknown compression type %r" % comptype) + return func(name, filemode, fileobj, **kwargs) + + elif "|" in mode: + filemode, comptype = mode.split("|", 1) + filemode = filemode or "r" + comptype = comptype or "tar" + + if filemode not in "rw": + raise ValueError("mode must be 'r' or 'w'") + + stream = _Stream(name, filemode, comptype, fileobj, bufsize) + try: + t = cls(name, filemode, stream, **kwargs) + except: + stream.close() + raise + t._extfileobj = False + return t + + elif mode in "aw": + return cls.taropen(name, mode, fileobj, **kwargs) + + raise ValueError("undiscernible mode") + + @classmethod + def taropen(cls, name, mode="r", fileobj=None, **kwargs): + """Open uncompressed tar archive name for reading or writing. + """ + if len(mode) > 1 or mode not in "raw": + raise ValueError("mode must be 'r', 'a' or 'w'") + return cls(name, mode, fileobj, **kwargs) + + @classmethod + def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): + """Open gzip compressed tar archive name for reading or writing. + Appending is not allowed. + """ + if len(mode) > 1 or mode not in "rw": + raise ValueError("mode must be 'r' or 'w'") + + try: + import gzip + gzip.GzipFile + except (ImportError, AttributeError): + raise CompressionError("gzip module is not available") + + extfileobj = fileobj is not None + try: + fileobj = gzip.GzipFile(name, mode + "b", compresslevel, fileobj) + t = cls.taropen(name, mode, fileobj, **kwargs) + except IOError: + if not extfileobj and fileobj is not None: + fileobj.close() + if fileobj is None: + raise + raise ReadError("not a gzip file") + except: + if not extfileobj and fileobj is not None: + fileobj.close() + raise + t._extfileobj = extfileobj + return t + + @classmethod + def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): + """Open bzip2 compressed tar archive name for reading or writing. + Appending is not allowed. + """ + if len(mode) > 1 or mode not in "rw": + raise ValueError("mode must be 'r' or 'w'.") + + try: + import bz2 + except ImportError: + raise CompressionError("bz2 module is not available") + + if fileobj is not None: + fileobj = _BZ2Proxy(fileobj, mode) + else: + fileobj = bz2.BZ2File(name, mode, compresslevel=compresslevel) + + try: + t = cls.taropen(name, mode, fileobj, **kwargs) + except (IOError, EOFError): + fileobj.close() + raise ReadError("not a bzip2 file") + t._extfileobj = False + return t + + # All *open() methods are registered here. + OPEN_METH = { + "tar": "taropen", # uncompressed tar + "gz": "gzopen", # gzip compressed tar + "bz2": "bz2open" # bzip2 compressed tar + } + + #-------------------------------------------------------------------------- + # The public methods which TarFile provides: + + def close(self): + """Close the TarFile. In write-mode, two finishing zero blocks are + appended to the archive. + """ + if self.closed: + return + + if self.mode in "aw": + self.fileobj.write(NUL * (BLOCKSIZE * 2)) + self.offset += (BLOCKSIZE * 2) + # fill up the end with zero-blocks + # (like option -b20 for tar does) + blocks, remainder = divmod(self.offset, RECORDSIZE) + if remainder > 0: + self.fileobj.write(NUL * (RECORDSIZE - remainder)) + + if not self._extfileobj: + self.fileobj.close() + self.closed = True + + def getmember(self, name): + """Return a TarInfo object for member `name'. If `name' can not be + found in the archive, KeyError is raised. If a member occurs more + than once in the archive, its last occurrence is assumed to be the + most up-to-date version. + """ + tarinfo = self._getmember(name) + if tarinfo is None: + raise KeyError("filename %r not found" % name) + return tarinfo + + def getmembers(self): + """Return the members of the archive as a list of TarInfo objects. The + list has the same order as the members in the archive. + """ + self._check() + if not self._loaded: # if we want to obtain a list of + self._load() # all members, we first have to + # scan the whole archive. + return self.members + + def getnames(self): + """Return the members of the archive as a list of their names. It has + the same order as the list returned by getmembers(). + """ + return [tarinfo.name for tarinfo in self.getmembers()] + + def gettarinfo(self, name=None, arcname=None, fileobj=None): + """Create a TarInfo object for either the file `name' or the file + object `fileobj' (using os.fstat on its file descriptor). You can + modify some of the TarInfo's attributes before you add it using + addfile(). If given, `arcname' specifies an alternative name for the + file in the archive. + """ + self._check("aw") + + # When fileobj is given, replace name by + # fileobj's real name. + if fileobj is not None: + name = fileobj.name + + # Building the name of the member in the archive. + # Backward slashes are converted to forward slashes, + # Absolute paths are turned to relative paths. + if arcname is None: + arcname = name + drv, arcname = os.path.splitdrive(arcname) + arcname = arcname.replace(os.sep, "/") + arcname = arcname.lstrip("/") + + # Now, fill the TarInfo object with + # information specific for the file. + tarinfo = self.tarinfo() + tarinfo.tarfile = self + + # Use os.stat or os.lstat, depending on platform + # and if symlinks shall be resolved. + if fileobj is None: + if hasattr(os, "lstat") and not self.dereference: + statres = os.lstat(name) + else: + statres = os.stat(name) + else: + statres = os.fstat(fileobj.fileno()) + linkname = "" + + stmd = statres.st_mode + if stat.S_ISREG(stmd): + inode = (statres.st_ino, statres.st_dev) + if not self.dereference and statres.st_nlink > 1 and \ + inode in self.inodes and arcname != self.inodes[inode]: + # Is it a hardlink to an already + # archived file? + type = LNKTYPE + linkname = self.inodes[inode] + else: + # The inode is added only if its valid. + # For win32 it is always 0. + type = REGTYPE + if inode[0]: + self.inodes[inode] = arcname + elif stat.S_ISDIR(stmd): + type = DIRTYPE + elif stat.S_ISFIFO(stmd): + type = FIFOTYPE + elif stat.S_ISLNK(stmd): + type = SYMTYPE + linkname = os.readlink(name) + elif stat.S_ISCHR(stmd): + type = CHRTYPE + elif stat.S_ISBLK(stmd): + type = BLKTYPE + else: + return None + + # Fill the TarInfo object with all + # information we can get. + tarinfo.name = arcname + tarinfo.mode = stmd + tarinfo.uid = statres.st_uid + tarinfo.gid = statres.st_gid + if type == REGTYPE: + tarinfo.size = statres.st_size + else: + tarinfo.size = 0 + tarinfo.mtime = statres.st_mtime + tarinfo.type = type + tarinfo.linkname = linkname + if pwd: + try: + tarinfo.uname = pwd.getpwuid(tarinfo.uid)[0] + except KeyError: + pass + if grp: + try: + tarinfo.gname = grp.getgrgid(tarinfo.gid)[0] + except KeyError: + pass + + if type in (CHRTYPE, BLKTYPE): + if hasattr(os, "major") and hasattr(os, "minor"): + tarinfo.devmajor = os.major(statres.st_rdev) + tarinfo.devminor = os.minor(statres.st_rdev) + return tarinfo + + def list(self, verbose=True): + """Print a table of contents to sys.stdout. If `verbose' is False, only + the names of the members are printed. If it is True, an `ls -l'-like + output is produced. + """ + self._check() + + for tarinfo in self: + if verbose: + print(filemode(tarinfo.mode), end=' ') + print("%s/%s" % (tarinfo.uname or tarinfo.uid, + tarinfo.gname or tarinfo.gid), end=' ') + if tarinfo.ischr() or tarinfo.isblk(): + print("%10s" % ("%d,%d" \ + % (tarinfo.devmajor, tarinfo.devminor)), end=' ') + else: + print("%10d" % tarinfo.size, end=' ') + print("%d-%02d-%02d %02d:%02d:%02d" \ + % time.localtime(tarinfo.mtime)[:6], end=' ') + + print(tarinfo.name + ("/" if tarinfo.isdir() else ""), end=' ') + + if verbose: + if tarinfo.issym(): + print("->", tarinfo.linkname, end=' ') + if tarinfo.islnk(): + print("link to", tarinfo.linkname, end=' ') + print() + + def add(self, name, arcname=None, recursive=True, exclude=None, filter=None): + """Add the file `name' to the archive. `name' may be any type of file + (directory, fifo, symbolic link, etc.). If given, `arcname' + specifies an alternative name for the file in the archive. + Directories are added recursively by default. This can be avoided by + setting `recursive' to False. `exclude' is a function that should + return True for each filename to be excluded. `filter' is a function + that expects a TarInfo object argument and returns the changed + TarInfo object, if it returns None the TarInfo object will be + excluded from the archive. + """ + self._check("aw") + + if arcname is None: + arcname = name + + # Exclude pathnames. + if exclude is not None: + import warnings + warnings.warn("use the filter argument instead", + DeprecationWarning, 2) + if exclude(name): + self._dbg(2, "tarfile: Excluded %r" % name) + return + + # Skip if somebody tries to archive the archive... + if self.name is not None and os.path.abspath(name) == self.name: + self._dbg(2, "tarfile: Skipped %r" % name) + return + + self._dbg(1, name) + + # Create a TarInfo object from the file. + tarinfo = self.gettarinfo(name, arcname) + + if tarinfo is None: + self._dbg(1, "tarfile: Unsupported type %r" % name) + return + + # Change or exclude the TarInfo object. + if filter is not None: + tarinfo = filter(tarinfo) + if tarinfo is None: + self._dbg(2, "tarfile: Excluded %r" % name) + return + + # Append the tar header and data to the archive. + if tarinfo.isreg(): + f = bltn_open(name, "rb") + self.addfile(tarinfo, f) + f.close() + + elif tarinfo.isdir(): + self.addfile(tarinfo) + if recursive: + for f in os.listdir(name): + self.add(os.path.join(name, f), os.path.join(arcname, f), + recursive, exclude, filter=filter) + + else: + self.addfile(tarinfo) + + def addfile(self, tarinfo, fileobj=None): + """Add the TarInfo object `tarinfo' to the archive. If `fileobj' is + given, tarinfo.size bytes are read from it and added to the archive. + You can create TarInfo objects using gettarinfo(). + On Windows platforms, `fileobj' should always be opened with mode + 'rb' to avoid irritation about the file size. + """ + self._check("aw") + + tarinfo = copy.copy(tarinfo) + + buf = tarinfo.tobuf(self.format, self.encoding, self.errors) + self.fileobj.write(buf) + self.offset += len(buf) + + # If there's data to follow, append it. + if fileobj is not None: + copyfileobj(fileobj, self.fileobj, tarinfo.size) + blocks, remainder = divmod(tarinfo.size, BLOCKSIZE) + if remainder > 0: + self.fileobj.write(NUL * (BLOCKSIZE - remainder)) + blocks += 1 + self.offset += blocks * BLOCKSIZE + + self.members.append(tarinfo) + + def extractall(self, path=".", members=None): + """Extract all members from the archive to the current working + directory and set owner, modification time and permissions on + directories afterwards. `path' specifies a different directory + to extract to. `members' is optional and must be a subset of the + list returned by getmembers(). + """ + directories = [] + + if members is None: + members = self + + for tarinfo in members: + if tarinfo.isdir(): + # Extract directories with a safe mode. + directories.append(tarinfo) + tarinfo = copy.copy(tarinfo) + tarinfo.mode = 0o700 + # Do not set_attrs directories, as we will do that further down + self.extract(tarinfo, path, set_attrs=not tarinfo.isdir()) + + # Reverse sort directories. + directories.sort(key=lambda a: a.name) + directories.reverse() + + # Set correct owner, mtime and filemode on directories. + for tarinfo in directories: + dirpath = os.path.join(path, tarinfo.name) + try: + self.chown(tarinfo, dirpath) + self.utime(tarinfo, dirpath) + self.chmod(tarinfo, dirpath) + except ExtractError as e: + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + def extract(self, member, path="", set_attrs=True): + """Extract a member from the archive to the current working directory, + using its full name. Its file information is extracted as accurately + as possible. `member' may be a filename or a TarInfo object. You can + specify a different directory using `path'. File attributes (owner, + mtime, mode) are set unless `set_attrs' is False. + """ + self._check("r") + + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + # Prepare the link target for makelink(). + if tarinfo.islnk(): + tarinfo._link_target = os.path.join(path, tarinfo.linkname) + + try: + self._extract_member(tarinfo, os.path.join(path, tarinfo.name), + set_attrs=set_attrs) + except EnvironmentError as e: + if self.errorlevel > 0: + raise + else: + if e.filename is None: + self._dbg(1, "tarfile: %s" % e.strerror) + else: + self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename)) + except ExtractError as e: + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + def extractfile(self, member): + """Extract a member from the archive as a file object. `member' may be + a filename or a TarInfo object. If `member' is a regular file, a + file-like object is returned. If `member' is a link, a file-like + object is constructed from the link's target. If `member' is none of + the above, None is returned. + The file-like object is read-only and provides the following + methods: read(), readline(), readlines(), seek() and tell() + """ + self._check("r") + + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + if tarinfo.isreg(): + return self.fileobject(self, tarinfo) + + elif tarinfo.type not in SUPPORTED_TYPES: + # If a member's type is unknown, it is treated as a + # regular file. + return self.fileobject(self, tarinfo) + + elif tarinfo.islnk() or tarinfo.issym(): + if isinstance(self.fileobj, _Stream): + # A small but ugly workaround for the case that someone tries + # to extract a (sym)link as a file-object from a non-seekable + # stream of tar blocks. + raise StreamError("cannot extract (sym)link as file object") + else: + # A (sym)link's file object is its target's file object. + return self.extractfile(self._find_link_target(tarinfo)) + else: + # If there's no data associated with the member (directory, chrdev, + # blkdev, etc.), return None instead of a file object. + return None + + def _extract_member(self, tarinfo, targetpath, set_attrs=True): + """Extract the TarInfo object tarinfo to a physical + file called targetpath. + """ + # Fetch the TarInfo object for the given name + # and build the destination pathname, replacing + # forward slashes to platform specific separators. + targetpath = targetpath.rstrip("/") + targetpath = targetpath.replace("/", os.sep) + + # Create all upper directories. + upperdirs = os.path.dirname(targetpath) + if upperdirs and not os.path.exists(upperdirs): + # Create directories that are not part of the archive with + # default permissions. + os.makedirs(upperdirs) + + if tarinfo.islnk() or tarinfo.issym(): + self._dbg(1, "%s -> %s" % (tarinfo.name, tarinfo.linkname)) + else: + self._dbg(1, tarinfo.name) + + if tarinfo.isreg(): + self.makefile(tarinfo, targetpath) + elif tarinfo.isdir(): + self.makedir(tarinfo, targetpath) + elif tarinfo.isfifo(): + self.makefifo(tarinfo, targetpath) + elif tarinfo.ischr() or tarinfo.isblk(): + self.makedev(tarinfo, targetpath) + elif tarinfo.islnk() or tarinfo.issym(): + self.makelink(tarinfo, targetpath) + elif tarinfo.type not in SUPPORTED_TYPES: + self.makeunknown(tarinfo, targetpath) + else: + self.makefile(tarinfo, targetpath) + + if set_attrs: + self.chown(tarinfo, targetpath) + if not tarinfo.issym(): + self.chmod(tarinfo, targetpath) + self.utime(tarinfo, targetpath) + + #-------------------------------------------------------------------------- + # Below are the different file methods. They are called via + # _extract_member() when extract() is called. They can be replaced in a + # subclass to implement other functionality. + + def makedir(self, tarinfo, targetpath): + """Make a directory called targetpath. + """ + try: + # Use a safe mode for the directory, the real mode is set + # later in _extract_member(). + os.mkdir(targetpath, 0o700) + except EnvironmentError as e: + if e.errno != errno.EEXIST: + raise + + def makefile(self, tarinfo, targetpath): + """Make a file called targetpath. + """ + source = self.fileobj + source.seek(tarinfo.offset_data) + target = bltn_open(targetpath, "wb") + if tarinfo.sparse is not None: + for offset, size in tarinfo.sparse: + target.seek(offset) + copyfileobj(source, target, size) + else: + copyfileobj(source, target, tarinfo.size) + target.seek(tarinfo.size) + target.truncate() + target.close() + + def makeunknown(self, tarinfo, targetpath): + """Make a file from a TarInfo object with an unknown type + at targetpath. + """ + self.makefile(tarinfo, targetpath) + self._dbg(1, "tarfile: Unknown file type %r, " \ + "extracted as regular file." % tarinfo.type) + + def makefifo(self, tarinfo, targetpath): + """Make a fifo called targetpath. + """ + if hasattr(os, "mkfifo"): + os.mkfifo(targetpath) + else: + raise ExtractError("fifo not supported by system") + + def makedev(self, tarinfo, targetpath): + """Make a character or block device called targetpath. + """ + if not hasattr(os, "mknod") or not hasattr(os, "makedev"): + raise ExtractError("special devices not supported by system") + + mode = tarinfo.mode + if tarinfo.isblk(): + mode |= stat.S_IFBLK + else: + mode |= stat.S_IFCHR + + os.mknod(targetpath, mode, + os.makedev(tarinfo.devmajor, tarinfo.devminor)) + + def makelink(self, tarinfo, targetpath): + """Make a (symbolic) link called targetpath. If it cannot be created + (platform limitation), we try to make a copy of the referenced file + instead of a link. + """ + try: + # For systems that support symbolic and hard links. + if tarinfo.issym(): + os.symlink(tarinfo.linkname, targetpath) + else: + # See extract(). + if os.path.exists(tarinfo._link_target): + os.link(tarinfo._link_target, targetpath) + else: + self._extract_member(self._find_link_target(tarinfo), + targetpath) + except symlink_exception: + if tarinfo.issym(): + linkpath = os.path.join(os.path.dirname(tarinfo.name), + tarinfo.linkname) + else: + linkpath = tarinfo.linkname + else: + try: + self._extract_member(self._find_link_target(tarinfo), + targetpath) + except KeyError: + raise ExtractError("unable to resolve link inside archive") + + def chown(self, tarinfo, targetpath): + """Set owner of targetpath according to tarinfo. + """ + if pwd and hasattr(os, "geteuid") and os.geteuid() == 0: + # We have to be root to do so. + try: + g = grp.getgrnam(tarinfo.gname)[2] + except KeyError: + g = tarinfo.gid + try: + u = pwd.getpwnam(tarinfo.uname)[2] + except KeyError: + u = tarinfo.uid + try: + if tarinfo.issym() and hasattr(os, "lchown"): + os.lchown(targetpath, u, g) + else: + if sys.platform != "os2emx": + os.chown(targetpath, u, g) + except EnvironmentError as e: + raise ExtractError("could not change owner") + + def chmod(self, tarinfo, targetpath): + """Set file permissions of targetpath according to tarinfo. + """ + if hasattr(os, 'chmod'): + try: + os.chmod(targetpath, tarinfo.mode) + except EnvironmentError as e: + raise ExtractError("could not change mode") + + def utime(self, tarinfo, targetpath): + """Set modification time of targetpath according to tarinfo. + """ + if not hasattr(os, 'utime'): + return + try: + os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime)) + except EnvironmentError as e: + raise ExtractError("could not change modification time") + + #-------------------------------------------------------------------------- + def next(self): + """Return the next member of the archive as a TarInfo object, when + TarFile is opened for reading. Return None if there is no more + available. + """ + self._check("ra") + if self.firstmember is not None: + m = self.firstmember + self.firstmember = None + return m + + # Read the next block. + self.fileobj.seek(self.offset) + tarinfo = None + while True: + try: + tarinfo = self.tarinfo.fromtarfile(self) + except EOFHeaderError as e: + if self.ignore_zeros: + self._dbg(2, "0x%X: %s" % (self.offset, e)) + self.offset += BLOCKSIZE + continue + except InvalidHeaderError as e: + if self.ignore_zeros: + self._dbg(2, "0x%X: %s" % (self.offset, e)) + self.offset += BLOCKSIZE + continue + elif self.offset == 0: + raise ReadError(str(e)) + except EmptyHeaderError: + if self.offset == 0: + raise ReadError("empty file") + except TruncatedHeaderError as e: + if self.offset == 0: + raise ReadError(str(e)) + except SubsequentHeaderError as e: + raise ReadError(str(e)) + break + + if tarinfo is not None: + self.members.append(tarinfo) + else: + self._loaded = True + + return tarinfo + + #-------------------------------------------------------------------------- + # Little helper methods: + + def _getmember(self, name, tarinfo=None, normalize=False): + """Find an archive member by name from bottom to top. + If tarinfo is given, it is used as the starting point. + """ + # Ensure that all members have been loaded. + members = self.getmembers() + + # Limit the member search list up to tarinfo. + if tarinfo is not None: + members = members[:members.index(tarinfo)] + + if normalize: + name = os.path.normpath(name) + + for member in reversed(members): + if normalize: + member_name = os.path.normpath(member.name) + else: + member_name = member.name + + if name == member_name: + return member + + def _load(self): + """Read through the entire archive file and look for readable + members. + """ + while True: + tarinfo = self.next() + if tarinfo is None: + break + self._loaded = True + + def _check(self, mode=None): + """Check if TarFile is still open, and if the operation's mode + corresponds to TarFile's mode. + """ + if self.closed: + raise IOError("%s is closed" % self.__class__.__name__) + if mode is not None and self.mode not in mode: + raise IOError("bad operation for mode %r" % self.mode) + + def _find_link_target(self, tarinfo): + """Find the target member of a symlink or hardlink member in the + archive. + """ + if tarinfo.issym(): + # Always search the entire archive. + linkname = os.path.dirname(tarinfo.name) + "/" + tarinfo.linkname + limit = None + else: + # Search the archive before the link, because a hard link is + # just a reference to an already archived file. + linkname = tarinfo.linkname + limit = tarinfo + + member = self._getmember(linkname, tarinfo=limit, normalize=True) + if member is None: + raise KeyError("linkname %r not found" % linkname) + return member + + def __iter__(self): + """Provide an iterator object. + """ + if self._loaded: + return iter(self.members) + else: + return TarIter(self) + + def _dbg(self, level, msg): + """Write debugging output to sys.stderr. + """ + if level <= self.debug: + print(msg, file=sys.stderr) + + def __enter__(self): + self._check() + return self + + def __exit__(self, type, value, traceback): + if type is None: + self.close() + else: + # An exception occurred. We must not call close() because + # it would try to write end-of-archive blocks and padding. + if not self._extfileobj: + self.fileobj.close() + self.closed = True +# class TarFile + +class TarIter(object): + """Iterator Class. + + for tarinfo in TarFile(...): + suite... + """ + + def __init__(self, tarfile): + """Construct a TarIter object. + """ + self.tarfile = tarfile + self.index = 0 + def __iter__(self): + """Return iterator object. + """ + return self + + def __next__(self): + """Return the next item using TarFile's next() method. + When all members have been read, set TarFile as _loaded. + """ + # Fix for SF #1100429: Under rare circumstances it can + # happen that getmembers() is called during iteration, + # which will cause TarIter to stop prematurely. + if not self.tarfile._loaded: + tarinfo = self.tarfile.next() + if not tarinfo: + self.tarfile._loaded = True + raise StopIteration + else: + try: + tarinfo = self.tarfile.members[self.index] + except IndexError: + raise StopIteration + self.index += 1 + return tarinfo + + next = __next__ # for Python 2.x + +#-------------------- +# exported functions +#-------------------- +def is_tarfile(name): + """Return True if name points to a tar archive that we + are able to handle, else return False. + """ + try: + t = open(name) + t.close() + return True + except TarError: + return False + +bltn_open = open +open = TarFile.open diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/compat.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/compat.py new file mode 100644 index 0000000..2b198dd --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/compat.py @@ -0,0 +1,1111 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2016 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import absolute_import + +import os +import re +import sys + +try: + import ssl +except ImportError: + ssl = None + +if sys.version_info[0] < 3: # pragma: no cover + from StringIO import StringIO + string_types = basestring, + text_type = unicode + from types import FileType as file_type + import __builtin__ as builtins + import ConfigParser as configparser + from ._backport import shutil + from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit + from urllib import (urlretrieve, quote as _quote, unquote, url2pathname, + pathname2url, ContentTooShortError, splittype) + + def quote(s): + if isinstance(s, unicode): + s = s.encode('utf-8') + return _quote(s) + + import urllib2 + from urllib2 import (Request, urlopen, URLError, HTTPError, + HTTPBasicAuthHandler, HTTPPasswordMgr, + HTTPHandler, HTTPRedirectHandler, + build_opener) + if ssl: + from urllib2 import HTTPSHandler + import httplib + import xmlrpclib + import Queue as queue + from HTMLParser import HTMLParser + import htmlentitydefs + raw_input = raw_input + from itertools import ifilter as filter + from itertools import ifilterfalse as filterfalse + + _userprog = None + def splituser(host): + """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" + global _userprog + if _userprog is None: + import re + _userprog = re.compile('^(.*)@(.*)$') + + match = _userprog.match(host) + if match: return match.group(1, 2) + return None, host + +else: # pragma: no cover + from io import StringIO + string_types = str, + text_type = str + from io import TextIOWrapper as file_type + import builtins + import configparser + import shutil + from urllib.parse import (urlparse, urlunparse, urljoin, splituser, quote, + unquote, urlsplit, urlunsplit, splittype) + from urllib.request import (urlopen, urlretrieve, Request, url2pathname, + pathname2url, + HTTPBasicAuthHandler, HTTPPasswordMgr, + HTTPHandler, HTTPRedirectHandler, + build_opener) + if ssl: + from urllib.request import HTTPSHandler + from urllib.error import HTTPError, URLError, ContentTooShortError + import http.client as httplib + import urllib.request as urllib2 + import xmlrpc.client as xmlrpclib + import queue + from html.parser import HTMLParser + import html.entities as htmlentitydefs + raw_input = input + from itertools import filterfalse + filter = filter + +try: + from ssl import match_hostname, CertificateError +except ImportError: # pragma: no cover + class CertificateError(ValueError): + pass + + + def _dnsname_match(dn, hostname, max_wildcards=1): + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + parts = dn.split('.') + leftmost, remainder = parts[0], parts[1:] + + wildcards = leftmost.count('*') + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn)) + + # speed up common case w/o wildcards + if not wildcards: + return dn.lower() == hostname.lower() + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == '*': + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append('[^.]+') + elif leftmost.startswith('xn--') or hostname.startswith('xn--'): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + return pat.match(hostname) + + + def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError("empty or no certificate, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED") + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get('subject', ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == 'commonName': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError("hostname %r " + "doesn't match either of %s" + % (hostname, ', '.join(map(repr, dnsnames)))) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r " + "doesn't match %r" + % (hostname, dnsnames[0])) + else: + raise CertificateError("no appropriate commonName or " + "subjectAltName fields were found") + + +try: + from types import SimpleNamespace as Container +except ImportError: # pragma: no cover + class Container(object): + """ + A generic container for when multiple values need to be returned + """ + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + +try: + from shutil import which +except ImportError: # pragma: no cover + # Implementation from Python 3.3 + def which(cmd, mode=os.F_OK | os.X_OK, path=None): + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + + """ + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) + and not os.path.isdir(fn)) + + # If we're given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get("PATH", os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if not os.curdir in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if not normdir in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None + + +# ZipFile is a context manager in 2.7, but not in 2.6 + +from zipfile import ZipFile as BaseZipFile + +if hasattr(BaseZipFile, '__enter__'): # pragma: no cover + ZipFile = BaseZipFile +else: + from zipfile import ZipExtFile as BaseZipExtFile + + class ZipExtFile(BaseZipExtFile): + def __init__(self, base): + self.__dict__.update(base.__dict__) + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.close() + # return None, so if an exception occurred, it will propagate + + class ZipFile(BaseZipFile): + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.close() + # return None, so if an exception occurred, it will propagate + + def open(self, *args, **kwargs): + base = BaseZipFile.open(self, *args, **kwargs) + return ZipExtFile(base) + +try: + from platform import python_implementation +except ImportError: # pragma: no cover + def python_implementation(): + """Return a string identifying the Python implementation.""" + if 'PyPy' in sys.version: + return 'PyPy' + if os.name == 'java': + return 'Jython' + if sys.version.startswith('IronPython'): + return 'IronPython' + return 'CPython' + +try: + import sysconfig +except ImportError: # pragma: no cover + from ._backport import sysconfig + +try: + callable = callable +except NameError: # pragma: no cover + from collections import Callable + + def callable(obj): + return isinstance(obj, Callable) + + +try: + fsencode = os.fsencode + fsdecode = os.fsdecode +except AttributeError: # pragma: no cover + _fsencoding = sys.getfilesystemencoding() + if _fsencoding == 'mbcs': + _fserrors = 'strict' + else: + _fserrors = 'surrogateescape' + + def fsencode(filename): + if isinstance(filename, bytes): + return filename + elif isinstance(filename, text_type): + return filename.encode(_fsencoding, _fserrors) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) + + def fsdecode(filename): + if isinstance(filename, text_type): + return filename + elif isinstance(filename, bytes): + return filename.decode(_fsencoding, _fserrors) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) + +try: + from tokenize import detect_encoding +except ImportError: # pragma: no cover + from codecs import BOM_UTF8, lookup + import re + + cookie_re = re.compile("coding[:=]\s*([-\w.]+)") + + def _get_normal_name(orig_enc): + """Imitates get_normal_name in tokenizer.c.""" + # Only care about the first 12 characters. + enc = orig_enc[:12].lower().replace("_", "-") + if enc == "utf-8" or enc.startswith("utf-8-"): + return "utf-8" + if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ + enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): + return "iso-8859-1" + return orig_enc + + def detect_encoding(readline): + """ + The detect_encoding() function is used to detect the encoding that should + be used to decode a Python source file. It requires one argument, readline, + in the same way as the tokenize() generator. + + It will call readline a maximum of twice, and return the encoding used + (as a string) and a list of any lines (left as bytes) it has read in. + + It detects the encoding from the presence of a utf-8 bom or an encoding + cookie as specified in pep-0263. If both a bom and a cookie are present, + but disagree, a SyntaxError will be raised. If the encoding cookie is an + invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, + 'utf-8-sig' is returned. + + If no encoding is specified, then the default of 'utf-8' will be returned. + """ + try: + filename = readline.__self__.name + except AttributeError: + filename = None + bom_found = False + encoding = None + default = 'utf-8' + def read_or_stop(): + try: + return readline() + except StopIteration: + return b'' + + def find_cookie(line): + try: + # Decode as UTF-8. Either the line is an encoding declaration, + # in which case it should be pure ASCII, or it must be UTF-8 + # per default encoding. + line_string = line.decode('utf-8') + except UnicodeDecodeError: + msg = "invalid or missing encoding declaration" + if filename is not None: + msg = '{} for {!r}'.format(msg, filename) + raise SyntaxError(msg) + + matches = cookie_re.findall(line_string) + if not matches: + return None + encoding = _get_normal_name(matches[0]) + try: + codec = lookup(encoding) + except LookupError: + # This behaviour mimics the Python interpreter + if filename is None: + msg = "unknown encoding: " + encoding + else: + msg = "unknown encoding for {!r}: {}".format(filename, + encoding) + raise SyntaxError(msg) + + if bom_found: + if codec.name != 'utf-8': + # This behaviour mimics the Python interpreter + if filename is None: + msg = 'encoding problem: utf-8' + else: + msg = 'encoding problem for {!r}: utf-8'.format(filename) + raise SyntaxError(msg) + encoding += '-sig' + return encoding + + first = read_or_stop() + if first.startswith(BOM_UTF8): + bom_found = True + first = first[3:] + default = 'utf-8-sig' + if not first: + return default, [] + + encoding = find_cookie(first) + if encoding: + return encoding, [first] + + second = read_or_stop() + if not second: + return default, [first] + + encoding = find_cookie(second) + if encoding: + return encoding, [first, second] + + return default, [first, second] + +# For converting & <-> & etc. +try: + from html import escape +except ImportError: + from cgi import escape +if sys.version_info[:2] < (3, 4): + unescape = HTMLParser().unescape +else: + from html import unescape + +try: + from collections import ChainMap +except ImportError: # pragma: no cover + from collections import MutableMapping + + try: + from reprlib import recursive_repr as _recursive_repr + except ImportError: + def _recursive_repr(fillvalue='...'): + ''' + Decorator to make a repr function return fillvalue for a recursive + call + ''' + + def decorating_function(user_function): + repr_running = set() + + def wrapper(self): + key = id(self), get_ident() + if key in repr_running: + return fillvalue + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + + # Can't use functools.wraps() here because of bootstrap issues + wrapper.__module__ = getattr(user_function, '__module__') + wrapper.__doc__ = getattr(user_function, '__doc__') + wrapper.__name__ = getattr(user_function, '__name__') + wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) + return wrapper + + return decorating_function + + class ChainMap(MutableMapping): + ''' A ChainMap groups multiple dicts (or other mappings) together + to create a single, updateable view. + + The underlying mappings are stored in a list. That list is public and can + accessed or updated using the *maps* attribute. There is no other state. + + Lookups search the underlying mappings successively until a key is found. + In contrast, writes, updates, and deletions only operate on the first + mapping. + + ''' + + def __init__(self, *maps): + '''Initialize a ChainMap by setting *maps* to the given mappings. + If no mappings are provided, a single empty dictionary is used. + + ''' + self.maps = list(maps) or [{}] # always at least one map + + def __missing__(self, key): + raise KeyError(key) + + def __getitem__(self, key): + for mapping in self.maps: + try: + return mapping[key] # can't use 'key in mapping' with defaultdict + except KeyError: + pass + return self.__missing__(key) # support subclasses that define __missing__ + + def get(self, key, default=None): + return self[key] if key in self else default + + def __len__(self): + return len(set().union(*self.maps)) # reuses stored hash values if possible + + def __iter__(self): + return iter(set().union(*self.maps)) + + def __contains__(self, key): + return any(key in m for m in self.maps) + + def __bool__(self): + return any(self.maps) + + @_recursive_repr() + def __repr__(self): + return '{0.__class__.__name__}({1})'.format( + self, ', '.join(map(repr, self.maps))) + + @classmethod + def fromkeys(cls, iterable, *args): + 'Create a ChainMap with a single dict created from the iterable.' + return cls(dict.fromkeys(iterable, *args)) + + def copy(self): + 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]' + return self.__class__(self.maps[0].copy(), *self.maps[1:]) + + __copy__ = copy + + def new_child(self): # like Django's Context.push() + 'New ChainMap with a new dict followed by all previous maps.' + return self.__class__({}, *self.maps) + + @property + def parents(self): # like Django's Context.pop() + 'New ChainMap from maps[1:].' + return self.__class__(*self.maps[1:]) + + def __setitem__(self, key, value): + self.maps[0][key] = value + + def __delitem__(self, key): + try: + del self.maps[0][key] + except KeyError: + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + + def popitem(self): + 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' + try: + return self.maps[0].popitem() + except KeyError: + raise KeyError('No keys found in the first mapping.') + + def pop(self, key, *args): + 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].' + try: + return self.maps[0].pop(key, *args) + except KeyError: + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + + def clear(self): + 'Clear maps[0], leaving maps[1:] intact.' + self.maps[0].clear() + +try: + from imp import cache_from_source +except ImportError: # pragma: no cover + def cache_from_source(path, debug_override=None): + assert path.endswith('.py') + if debug_override is None: + debug_override = __debug__ + if debug_override: + suffix = 'c' + else: + suffix = 'o' + return path + suffix + +try: + from collections import OrderedDict +except ImportError: # pragma: no cover +## {{{ http://code.activestate.com/recipes/576693/ (r9) +# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. +# Passes Python2.7's test suite and incorporates all the latest updates. + try: + from thread import get_ident as _get_ident + except ImportError: + from dummy_thread import get_ident as _get_ident + + try: + from _abcoll import KeysView, ValuesView, ItemsView + except ImportError: + pass + + + class OrderedDict(dict): + 'Dictionary that remembers insertion order' + # An inherited dict maps keys to values. + # The inherited dict provides __getitem__, __len__, __contains__, and get. + # The remaining methods are order-aware. + # Big-O running times for all methods are the same as for regular dictionaries. + + # The internal self.__map dictionary maps keys to links in a doubly linked list. + # The circular doubly linked list starts and ends with a sentinel element. + # The sentinel element never gets deleted (this simplifies the algorithm). + # Each link is stored as a list of length three: [PREV, NEXT, KEY]. + + def __init__(self, *args, **kwds): + '''Initialize an ordered dictionary. Signature is the same as for + regular dictionaries, but keyword arguments are not recommended + because their insertion order is arbitrary. + + ''' + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__root + except AttributeError: + self.__root = root = [] # sentinel node + root[:] = [root, root, None] + self.__map = {} + self.__update(*args, **kwds) + + def __setitem__(self, key, value, dict_setitem=dict.__setitem__): + 'od.__setitem__(i, y) <==> od[i]=y' + # Setting a new item creates a new link which goes at the end of the linked + # list, and the inherited dictionary is updated with the new key/value pair. + if key not in self: + root = self.__root + last = root[0] + last[1] = root[0] = self.__map[key] = [last, root, key] + dict_setitem(self, key, value) + + def __delitem__(self, key, dict_delitem=dict.__delitem__): + 'od.__delitem__(y) <==> del od[y]' + # Deleting an existing item uses self.__map to find the link which is + # then removed by updating the links in the predecessor and successor nodes. + dict_delitem(self, key) + link_prev, link_next, key = self.__map.pop(key) + link_prev[1] = link_next + link_next[0] = link_prev + + def __iter__(self): + 'od.__iter__() <==> iter(od)' + root = self.__root + curr = root[1] + while curr is not root: + yield curr[2] + curr = curr[1] + + def __reversed__(self): + 'od.__reversed__() <==> reversed(od)' + root = self.__root + curr = root[0] + while curr is not root: + yield curr[2] + curr = curr[0] + + def clear(self): + 'od.clear() -> None. Remove all items from od.' + try: + for node in self.__map.itervalues(): + del node[:] + root = self.__root + root[:] = [root, root, None] + self.__map.clear() + except AttributeError: + pass + dict.clear(self) + + def popitem(self, last=True): + '''od.popitem() -> (k, v), return and remove a (key, value) pair. + Pairs are returned in LIFO order if last is true or FIFO order if false. + + ''' + if not self: + raise KeyError('dictionary is empty') + root = self.__root + if last: + link = root[0] + link_prev = link[0] + link_prev[1] = root + root[0] = link_prev + else: + link = root[1] + link_next = link[1] + root[1] = link_next + link_next[0] = root + key = link[2] + del self.__map[key] + value = dict.pop(self, key) + return key, value + + # -- the following methods do not depend on the internal structure -- + + def keys(self): + 'od.keys() -> list of keys in od' + return list(self) + + def values(self): + 'od.values() -> list of values in od' + return [self[key] for key in self] + + def items(self): + 'od.items() -> list of (key, value) pairs in od' + return [(key, self[key]) for key in self] + + def iterkeys(self): + 'od.iterkeys() -> an iterator over the keys in od' + return iter(self) + + def itervalues(self): + 'od.itervalues -> an iterator over the values in od' + for k in self: + yield self[k] + + def iteritems(self): + 'od.iteritems -> an iterator over the (key, value) items in od' + for k in self: + yield (k, self[k]) + + def update(*args, **kwds): + '''od.update(E, **F) -> None. Update od from dict/iterable E and F. + + If E is a dict instance, does: for k in E: od[k] = E[k] + If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] + Or if E is an iterable of items, does: for k, v in E: od[k] = v + In either case, this is followed by: for k, v in F.items(): od[k] = v + + ''' + if len(args) > 2: + raise TypeError('update() takes at most 2 positional ' + 'arguments (%d given)' % (len(args),)) + elif not args: + raise TypeError('update() takes at least 1 argument (0 given)') + self = args[0] + # Make progressively weaker assumptions about "other" + other = () + if len(args) == 2: + other = args[1] + if isinstance(other, dict): + for key in other: + self[key] = other[key] + elif hasattr(other, 'keys'): + for key in other.keys(): + self[key] = other[key] + else: + for key, value in other: + self[key] = value + for key, value in kwds.items(): + self[key] = value + + __update = update # let subclasses override update without breaking __init__ + + __marker = object() + + def pop(self, key, default=__marker): + '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + + ''' + if key in self: + result = self[key] + del self[key] + return result + if default is self.__marker: + raise KeyError(key) + return default + + def setdefault(self, key, default=None): + 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + if key in self: + return self[key] + self[key] = default + return default + + def __repr__(self, _repr_running=None): + 'od.__repr__() <==> repr(od)' + if not _repr_running: _repr_running = {} + call_key = id(self), _get_ident() + if call_key in _repr_running: + return '...' + _repr_running[call_key] = 1 + try: + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + finally: + del _repr_running[call_key] + + def __reduce__(self): + 'Return state information for pickling' + items = [[k, self[k]] for k in self] + inst_dict = vars(self).copy() + for k in vars(OrderedDict()): + inst_dict.pop(k, None) + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def copy(self): + 'od.copy() -> a shallow copy of od' + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S + and values equal to v (which defaults to None). + + ''' + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + while comparison to a regular mapping is order-insensitive. + + ''' + if isinstance(other, OrderedDict): + return len(self)==len(other) and self.items() == other.items() + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other + + # -- the following methods are only used in Python 2.7 -- + + def viewkeys(self): + "od.viewkeys() -> a set-like object providing a view on od's keys" + return KeysView(self) + + def viewvalues(self): + "od.viewvalues() -> an object providing a view on od's values" + return ValuesView(self) + + def viewitems(self): + "od.viewitems() -> a set-like object providing a view on od's items" + return ItemsView(self) + +try: + from logging.config import BaseConfigurator, valid_ident +except ImportError: # pragma: no cover + IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I) + + + def valid_ident(s): + m = IDENTIFIER.match(s) + if not m: + raise ValueError('Not a valid Python identifier: %r' % s) + return True + + + # The ConvertingXXX classes are wrappers around standard Python containers, + # and they serve to convert any suitable values in the container. The + # conversion converts base dicts, lists and tuples to their wrapped + # equivalents, whereas strings which match a conversion format are converted + # appropriately. + # + # Each wrapper should have a configurator attribute holding the actual + # configurator to use for conversion. + + class ConvertingDict(dict): + """A converting dictionary wrapper.""" + + def __getitem__(self, key): + value = dict.__getitem__(self, key) + result = self.configurator.convert(value) + #If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def get(self, key, default=None): + value = dict.get(self, key, default) + result = self.configurator.convert(value) + #If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, key, default=None): + value = dict.pop(self, key, default) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + class ConvertingList(list): + """A converting list wrapper.""" + def __getitem__(self, key): + value = list.__getitem__(self, key) + result = self.configurator.convert(value) + #If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, idx=-1): + value = list.pop(self, idx) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + return result + + class ConvertingTuple(tuple): + """A converting tuple wrapper.""" + def __getitem__(self, key): + value = tuple.__getitem__(self, key) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + class BaseConfigurator(object): + """ + The configurator base class which defines some useful defaults. + """ + + CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$') + + WORD_PATTERN = re.compile(r'^\s*(\w+)\s*') + DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*') + INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*') + DIGIT_PATTERN = re.compile(r'^\d+$') + + value_converters = { + 'ext' : 'ext_convert', + 'cfg' : 'cfg_convert', + } + + # We might want to use a different one, e.g. importlib + importer = staticmethod(__import__) + + def __init__(self, config): + self.config = ConvertingDict(config) + self.config.configurator = self + + def resolve(self, s): + """ + Resolve strings to objects using standard import and attribute + syntax. + """ + name = s.split('.') + used = name.pop(0) + try: + found = self.importer(used) + for frag in name: + used += '.' + frag + try: + found = getattr(found, frag) + except AttributeError: + self.importer(used) + found = getattr(found, frag) + return found + except ImportError: + e, tb = sys.exc_info()[1:] + v = ValueError('Cannot resolve %r: %s' % (s, e)) + v.__cause__, v.__traceback__ = e, tb + raise v + + def ext_convert(self, value): + """Default converter for the ext:// protocol.""" + return self.resolve(value) + + def cfg_convert(self, value): + """Default converter for the cfg:// protocol.""" + rest = value + m = self.WORD_PATTERN.match(rest) + if m is None: + raise ValueError("Unable to convert %r" % value) + else: + rest = rest[m.end():] + d = self.config[m.groups()[0]] + #print d, rest + while rest: + m = self.DOT_PATTERN.match(rest) + if m: + d = d[m.groups()[0]] + else: + m = self.INDEX_PATTERN.match(rest) + if m: + idx = m.groups()[0] + if not self.DIGIT_PATTERN.match(idx): + d = d[idx] + else: + try: + n = int(idx) # try as number first (most likely) + d = d[n] + except TypeError: + d = d[idx] + if m: + rest = rest[m.end():] + else: + raise ValueError('Unable to convert ' + '%r at %r' % (value, rest)) + #rest should be empty + return d + + def convert(self, value): + """ + Convert values to an appropriate type. dicts, lists and tuples are + replaced by their converting alternatives. Strings are checked to + see if they have a conversion format and are converted if they do. + """ + if not isinstance(value, ConvertingDict) and isinstance(value, dict): + value = ConvertingDict(value) + value.configurator = self + elif not isinstance(value, ConvertingList) and isinstance(value, list): + value = ConvertingList(value) + value.configurator = self + elif not isinstance(value, ConvertingTuple) and\ + isinstance(value, tuple): + value = ConvertingTuple(value) + value.configurator = self + elif isinstance(value, string_types): + m = self.CONVERT_PATTERN.match(value) + if m: + d = m.groupdict() + prefix = d['prefix'] + converter = self.value_converters.get(prefix, None) + if converter: + suffix = d['suffix'] + converter = getattr(self, converter) + value = converter(suffix) + return value + + def configure_custom(self, config): + """Configure an object with a user-supplied factory.""" + c = config.pop('()') + if not callable(c): + c = self.resolve(c) + props = config.pop('.', None) + # Check for valid identifiers + kwargs = dict([(k, config[k]) for k in config if valid_ident(k)]) + result = c(**kwargs) + if props: + for name, value in props.items(): + setattr(result, name, value) + return result + + def as_tuple(self, value): + """Utility function which converts lists to tuples.""" + if isinstance(value, list): + value = tuple(value) + return value diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/database.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/database.py new file mode 100644 index 0000000..c314426 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/database.py @@ -0,0 +1,1312 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2016 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""PEP 376 implementation.""" + +from __future__ import unicode_literals + +import base64 +import codecs +import contextlib +import hashlib +import logging +import os +import posixpath +import sys +import zipimport + +from . import DistlibException, resources +from .compat import StringIO +from .version import get_scheme, UnsupportedVersionError +from .metadata import Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME +from .util import (parse_requirement, cached_property, parse_name_and_version, + read_exports, write_exports, CSVReader, CSVWriter) + + +__all__ = ['Distribution', 'BaseInstalledDistribution', + 'InstalledDistribution', 'EggInfoDistribution', + 'DistributionPath'] + + +logger = logging.getLogger(__name__) + +EXPORTS_FILENAME = 'pydist-exports.json' +COMMANDS_FILENAME = 'pydist-commands.json' + +DIST_FILES = ('INSTALLER', METADATA_FILENAME, 'RECORD', 'REQUESTED', + 'RESOURCES', EXPORTS_FILENAME, 'SHARED') + +DISTINFO_EXT = '.dist-info' + + +class _Cache(object): + """ + A simple cache mapping names and .dist-info paths to distributions + """ + def __init__(self): + """ + Initialise an instance. There is normally one for each DistributionPath. + """ + self.name = {} + self.path = {} + self.generated = False + + def clear(self): + """ + Clear the cache, setting it to its initial state. + """ + self.name.clear() + self.path.clear() + self.generated = False + + def add(self, dist): + """ + Add a distribution to the cache. + :param dist: The distribution to add. + """ + if dist.path not in self.path: + self.path[dist.path] = dist + self.name.setdefault(dist.key, []).append(dist) + + +class DistributionPath(object): + """ + Represents a set of distributions installed on a path (typically sys.path). + """ + def __init__(self, path=None, include_egg=False): + """ + Create an instance from a path, optionally including legacy (distutils/ + setuptools/distribute) distributions. + :param path: The path to use, as a list of directories. If not specified, + sys.path is used. + :param include_egg: If True, this instance will look for and return legacy + distributions as well as those based on PEP 376. + """ + if path is None: + path = sys.path + self.path = path + self._include_dist = True + self._include_egg = include_egg + + self._cache = _Cache() + self._cache_egg = _Cache() + self._cache_enabled = True + self._scheme = get_scheme('default') + + def _get_cache_enabled(self): + return self._cache_enabled + + def _set_cache_enabled(self, value): + self._cache_enabled = value + + cache_enabled = property(_get_cache_enabled, _set_cache_enabled) + + def clear_cache(self): + """ + Clears the internal cache. + """ + self._cache.clear() + self._cache_egg.clear() + + + def _yield_distributions(self): + """ + Yield .dist-info and/or .egg(-info) distributions. + """ + # We need to check if we've seen some resources already, because on + # some Linux systems (e.g. some Debian/Ubuntu variants) there are + # symlinks which alias other files in the environment. + seen = set() + for path in self.path: + finder = resources.finder_for_path(path) + if finder is None: + continue + r = finder.find('') + if not r or not r.is_container: + continue + rset = sorted(r.resources) + for entry in rset: + r = finder.find(entry) + if not r or r.path in seen: + continue + if self._include_dist and entry.endswith(DISTINFO_EXT): + possible_filenames = [METADATA_FILENAME, WHEEL_METADATA_FILENAME] + for metadata_filename in possible_filenames: + metadata_path = posixpath.join(entry, metadata_filename) + pydist = finder.find(metadata_path) + if pydist: + break + else: + continue + + with contextlib.closing(pydist.as_stream()) as stream: + metadata = Metadata(fileobj=stream, scheme='legacy') + logger.debug('Found %s', r.path) + seen.add(r.path) + yield new_dist_class(r.path, metadata=metadata, + env=self) + elif self._include_egg and entry.endswith(('.egg-info', + '.egg')): + logger.debug('Found %s', r.path) + seen.add(r.path) + yield old_dist_class(r.path, self) + + def _generate_cache(self): + """ + Scan the path for distributions and populate the cache with + those that are found. + """ + gen_dist = not self._cache.generated + gen_egg = self._include_egg and not self._cache_egg.generated + if gen_dist or gen_egg: + for dist in self._yield_distributions(): + if isinstance(dist, InstalledDistribution): + self._cache.add(dist) + else: + self._cache_egg.add(dist) + + if gen_dist: + self._cache.generated = True + if gen_egg: + self._cache_egg.generated = True + + @classmethod + def distinfo_dirname(cls, name, version): + """ + The *name* and *version* parameters are converted into their + filename-escaped form, i.e. any ``'-'`` characters are replaced + with ``'_'`` other than the one in ``'dist-info'`` and the one + separating the name from the version number. + + :parameter name: is converted to a standard distribution name by replacing + any runs of non- alphanumeric characters with a single + ``'-'``. + :type name: string + :parameter version: is converted to a standard version string. Spaces + become dots, and all other non-alphanumeric characters + (except dots) become dashes, with runs of multiple + dashes condensed to a single dash. + :type version: string + :returns: directory name + :rtype: string""" + name = name.replace('-', '_') + return '-'.join([name, version]) + DISTINFO_EXT + + def get_distributions(self): + """ + Provides an iterator that looks for distributions and returns + :class:`InstalledDistribution` or + :class:`EggInfoDistribution` instances for each one of them. + + :rtype: iterator of :class:`InstalledDistribution` and + :class:`EggInfoDistribution` instances + """ + if not self._cache_enabled: + for dist in self._yield_distributions(): + yield dist + else: + self._generate_cache() + + for dist in self._cache.path.values(): + yield dist + + if self._include_egg: + for dist in self._cache_egg.path.values(): + yield dist + + def get_distribution(self, name): + """ + Looks for a named distribution on the path. + + This function only returns the first result found, as no more than one + value is expected. If nothing is found, ``None`` is returned. + + :rtype: :class:`InstalledDistribution`, :class:`EggInfoDistribution` + or ``None`` + """ + result = None + name = name.lower() + if not self._cache_enabled: + for dist in self._yield_distributions(): + if dist.key == name: + result = dist + break + else: + self._generate_cache() + + if name in self._cache.name: + result = self._cache.name[name][0] + elif self._include_egg and name in self._cache_egg.name: + result = self._cache_egg.name[name][0] + return result + + def provides_distribution(self, name, version=None): + """ + Iterates over all distributions to find which distributions provide *name*. + If a *version* is provided, it will be used to filter the results. + + This function only returns the first result found, since no more than + one values are expected. If the directory is not found, returns ``None``. + + :parameter version: a version specifier that indicates the version + required, conforming to the format in ``PEP-345`` + + :type name: string + :type version: string + """ + matcher = None + if not version is None: + try: + matcher = self._scheme.matcher('%s (%s)' % (name, version)) + except ValueError: + raise DistlibException('invalid name or version: %r, %r' % + (name, version)) + + for dist in self.get_distributions(): + provided = dist.provides + + for p in provided: + p_name, p_ver = parse_name_and_version(p) + if matcher is None: + if p_name == name: + yield dist + break + else: + if p_name == name and matcher.match(p_ver): + yield dist + break + + def get_file_path(self, name, relative_path): + """ + Return the path to a resource file. + """ + dist = self.get_distribution(name) + if dist is None: + raise LookupError('no distribution named %r found' % name) + return dist.get_resource_path(relative_path) + + def get_exported_entries(self, category, name=None): + """ + Return all of the exported entries in a particular category. + + :param category: The category to search for entries. + :param name: If specified, only entries with that name are returned. + """ + for dist in self.get_distributions(): + r = dist.exports + if category in r: + d = r[category] + if name is not None: + if name in d: + yield d[name] + else: + for v in d.values(): + yield v + + +class Distribution(object): + """ + A base class for distributions, whether installed or from indexes. + Either way, it must have some metadata, so that's all that's needed + for construction. + """ + + build_time_dependency = False + """ + Set to True if it's known to be only a build-time dependency (i.e. + not needed after installation). + """ + + requested = False + """A boolean that indicates whether the ``REQUESTED`` metadata file is + present (in other words, whether the package was installed by user + request or it was installed as a dependency).""" + + def __init__(self, metadata): + """ + Initialise an instance. + :param metadata: The instance of :class:`Metadata` describing this + distribution. + """ + self.metadata = metadata + self.name = metadata.name + self.key = self.name.lower() # for case-insensitive comparisons + self.version = metadata.version + self.locator = None + self.digest = None + self.extras = None # additional features requested + self.context = None # environment marker overrides + self.download_urls = set() + self.digests = {} + + @property + def source_url(self): + """ + The source archive download URL for this distribution. + """ + return self.metadata.source_url + + download_url = source_url # Backward compatibility + + @property + def name_and_version(self): + """ + A utility property which displays the name and version in parentheses. + """ + return '%s (%s)' % (self.name, self.version) + + @property + def provides(self): + """ + A set of distribution names and versions provided by this distribution. + :return: A set of "name (version)" strings. + """ + plist = self.metadata.provides + s = '%s (%s)' % (self.name, self.version) + if s not in plist: + plist.append(s) + return plist + + def _get_requirements(self, req_attr): + md = self.metadata + logger.debug('Getting requirements from metadata %r', md.todict()) + reqts = getattr(md, req_attr) + return set(md.get_requirements(reqts, extras=self.extras, + env=self.context)) + + @property + def run_requires(self): + return self._get_requirements('run_requires') + + @property + def meta_requires(self): + return self._get_requirements('meta_requires') + + @property + def build_requires(self): + return self._get_requirements('build_requires') + + @property + def test_requires(self): + return self._get_requirements('test_requires') + + @property + def dev_requires(self): + return self._get_requirements('dev_requires') + + def matches_requirement(self, req): + """ + Say if this instance matches (fulfills) a requirement. + :param req: The requirement to match. + :rtype req: str + :return: True if it matches, else False. + """ + # Requirement may contain extras - parse to lose those + # from what's passed to the matcher + r = parse_requirement(req) + scheme = get_scheme(self.metadata.scheme) + try: + matcher = scheme.matcher(r.requirement) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + logger.warning('could not read version %r - using name only', + req) + name = req.split()[0] + matcher = scheme.matcher(name) + + name = matcher.key # case-insensitive + + result = False + for p in self.provides: + p_name, p_ver = parse_name_and_version(p) + if p_name != name: + continue + try: + result = matcher.match(p_ver) + break + except UnsupportedVersionError: + pass + return result + + def __repr__(self): + """ + Return a textual representation of this instance, + """ + if self.source_url: + suffix = ' [%s]' % self.source_url + else: + suffix = '' + return '<Distribution %s (%s)%s>' % (self.name, self.version, suffix) + + def __eq__(self, other): + """ + See if this distribution is the same as another. + :param other: The distribution to compare with. To be equal to one + another. distributions must have the same type, name, + version and source_url. + :return: True if it is the same, else False. + """ + if type(other) is not type(self): + result = False + else: + result = (self.name == other.name and + self.version == other.version and + self.source_url == other.source_url) + return result + + def __hash__(self): + """ + Compute hash in a way which matches the equality test. + """ + return hash(self.name) + hash(self.version) + hash(self.source_url) + + +class BaseInstalledDistribution(Distribution): + """ + This is the base class for installed distributions (whether PEP 376 or + legacy). + """ + + hasher = None + + def __init__(self, metadata, path, env=None): + """ + Initialise an instance. + :param metadata: An instance of :class:`Metadata` which describes the + distribution. This will normally have been initialised + from a metadata file in the ``path``. + :param path: The path of the ``.dist-info`` or ``.egg-info`` + directory for the distribution. + :param env: This is normally the :class:`DistributionPath` + instance where this distribution was found. + """ + super(BaseInstalledDistribution, self).__init__(metadata) + self.path = path + self.dist_path = env + + def get_hash(self, data, hasher=None): + """ + Get the hash of some data, using a particular hash algorithm, if + specified. + + :param data: The data to be hashed. + :type data: bytes + :param hasher: The name of a hash implementation, supported by hashlib, + or ``None``. Examples of valid values are ``'sha1'``, + ``'sha224'``, ``'sha384'``, '``sha256'``, ``'md5'`` and + ``'sha512'``. If no hasher is specified, the ``hasher`` + attribute of the :class:`InstalledDistribution` instance + is used. If the hasher is determined to be ``None``, MD5 + is used as the hashing algorithm. + :returns: The hash of the data. If a hasher was explicitly specified, + the returned hash will be prefixed with the specified hasher + followed by '='. + :rtype: str + """ + if hasher is None: + hasher = self.hasher + if hasher is None: + hasher = hashlib.md5 + prefix = '' + else: + hasher = getattr(hashlib, hasher) + prefix = '%s=' % self.hasher + digest = hasher(data).digest() + digest = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('ascii') + return '%s%s' % (prefix, digest) + + +class InstalledDistribution(BaseInstalledDistribution): + """ + Created with the *path* of the ``.dist-info`` directory provided to the + constructor. It reads the metadata contained in ``pydist.json`` when it is + instantiated., or uses a passed in Metadata instance (useful for when + dry-run mode is being used). + """ + + hasher = 'sha256' + + def __init__(self, path, metadata=None, env=None): + self.finder = finder = resources.finder_for_path(path) + if finder is None: + import pdb; pdb.set_trace () + if env and env._cache_enabled and path in env._cache.path: + metadata = env._cache.path[path].metadata + elif metadata is None: + r = finder.find(METADATA_FILENAME) + # Temporary - for Wheel 0.23 support + if r is None: + r = finder.find(WHEEL_METADATA_FILENAME) + # Temporary - for legacy support + if r is None: + r = finder.find('METADATA') + if r is None: + raise ValueError('no %s found in %s' % (METADATA_FILENAME, + path)) + with contextlib.closing(r.as_stream()) as stream: + metadata = Metadata(fileobj=stream, scheme='legacy') + + super(InstalledDistribution, self).__init__(metadata, path, env) + + if env and env._cache_enabled: + env._cache.add(self) + + try: + r = finder.find('REQUESTED') + except AttributeError: + import pdb; pdb.set_trace () + self.requested = r is not None + + def __repr__(self): + return '<InstalledDistribution %r %s at %r>' % ( + self.name, self.version, self.path) + + def __str__(self): + return "%s %s" % (self.name, self.version) + + def _get_records(self): + """ + Get the list of installed files for the distribution + :return: A list of tuples of path, hash and size. Note that hash and + size might be ``None`` for some entries. The path is exactly + as stored in the file (which is as in PEP 376). + """ + results = [] + r = self.get_distinfo_resource('RECORD') + with contextlib.closing(r.as_stream()) as stream: + with CSVReader(stream=stream) as record_reader: + # Base location is parent dir of .dist-info dir + #base_location = os.path.dirname(self.path) + #base_location = os.path.abspath(base_location) + for row in record_reader: + missing = [None for i in range(len(row), 3)] + path, checksum, size = row + missing + #if not os.path.isabs(path): + # path = path.replace('/', os.sep) + # path = os.path.join(base_location, path) + results.append((path, checksum, size)) + return results + + @cached_property + def exports(self): + """ + Return the information exported by this distribution. + :return: A dictionary of exports, mapping an export category to a dict + of :class:`ExportEntry` instances describing the individual + export entries, and keyed by name. + """ + result = {} + r = self.get_distinfo_resource(EXPORTS_FILENAME) + if r: + result = self.read_exports() + return result + + def read_exports(self): + """ + Read exports data from a file in .ini format. + + :return: A dictionary of exports, mapping an export category to a list + of :class:`ExportEntry` instances describing the individual + export entries. + """ + result = {} + r = self.get_distinfo_resource(EXPORTS_FILENAME) + if r: + with contextlib.closing(r.as_stream()) as stream: + result = read_exports(stream) + return result + + def write_exports(self, exports): + """ + Write a dictionary of exports to a file in .ini format. + :param exports: A dictionary of exports, mapping an export category to + a list of :class:`ExportEntry` instances describing the + individual export entries. + """ + rf = self.get_distinfo_file(EXPORTS_FILENAME) + with open(rf, 'w') as f: + write_exports(exports, f) + + def get_resource_path(self, relative_path): + """ + NOTE: This API may change in the future. + + Return the absolute path to a resource file with the given relative + path. + + :param relative_path: The path, relative to .dist-info, of the resource + of interest. + :return: The absolute path where the resource is to be found. + """ + r = self.get_distinfo_resource('RESOURCES') + with contextlib.closing(r.as_stream()) as stream: + with CSVReader(stream=stream) as resources_reader: + for relative, destination in resources_reader: + if relative == relative_path: + return destination + raise KeyError('no resource file with relative path %r ' + 'is installed' % relative_path) + + def list_installed_files(self): + """ + Iterates over the ``RECORD`` entries and returns a tuple + ``(path, hash, size)`` for each line. + + :returns: iterator of (path, hash, size) + """ + for result in self._get_records(): + yield result + + def write_installed_files(self, paths, prefix, dry_run=False): + """ + Writes the ``RECORD`` file, using the ``paths`` iterable passed in. Any + existing ``RECORD`` file is silently overwritten. + + prefix is used to determine when to write absolute paths. + """ + prefix = os.path.join(prefix, '') + base = os.path.dirname(self.path) + base_under_prefix = base.startswith(prefix) + base = os.path.join(base, '') + record_path = self.get_distinfo_file('RECORD') + logger.info('creating %s', record_path) + if dry_run: + return None + with CSVWriter(record_path) as writer: + for path in paths: + if os.path.isdir(path) or path.endswith(('.pyc', '.pyo')): + # do not put size and hash, as in PEP-376 + hash_value = size = '' + else: + size = '%d' % os.path.getsize(path) + with open(path, 'rb') as fp: + hash_value = self.get_hash(fp.read()) + if path.startswith(base) or (base_under_prefix and + path.startswith(prefix)): + path = os.path.relpath(path, base) + writer.writerow((path, hash_value, size)) + + # add the RECORD file itself + if record_path.startswith(base): + record_path = os.path.relpath(record_path, base) + writer.writerow((record_path, '', '')) + return record_path + + def check_installed_files(self): + """ + Checks that the hashes and sizes of the files in ``RECORD`` are + matched by the files themselves. Returns a (possibly empty) list of + mismatches. Each entry in the mismatch list will be a tuple consisting + of the path, 'exists', 'size' or 'hash' according to what didn't match + (existence is checked first, then size, then hash), the expected + value and the actual value. + """ + mismatches = [] + base = os.path.dirname(self.path) + record_path = self.get_distinfo_file('RECORD') + for path, hash_value, size in self.list_installed_files(): + if not os.path.isabs(path): + path = os.path.join(base, path) + if path == record_path: + continue + if not os.path.exists(path): + mismatches.append((path, 'exists', True, False)) + elif os.path.isfile(path): + actual_size = str(os.path.getsize(path)) + if size and actual_size != size: + mismatches.append((path, 'size', size, actual_size)) + elif hash_value: + if '=' in hash_value: + hasher = hash_value.split('=', 1)[0] + else: + hasher = None + + with open(path, 'rb') as f: + actual_hash = self.get_hash(f.read(), hasher) + if actual_hash != hash_value: + mismatches.append((path, 'hash', hash_value, actual_hash)) + return mismatches + + @cached_property + def shared_locations(self): + """ + A dictionary of shared locations whose keys are in the set 'prefix', + 'purelib', 'platlib', 'scripts', 'headers', 'data' and 'namespace'. + The corresponding value is the absolute path of that category for + this distribution, and takes into account any paths selected by the + user at installation time (e.g. via command-line arguments). In the + case of the 'namespace' key, this would be a list of absolute paths + for the roots of namespace packages in this distribution. + + The first time this property is accessed, the relevant information is + read from the SHARED file in the .dist-info directory. + """ + result = {} + shared_path = os.path.join(self.path, 'SHARED') + if os.path.isfile(shared_path): + with codecs.open(shared_path, 'r', encoding='utf-8') as f: + lines = f.read().splitlines() + for line in lines: + key, value = line.split('=', 1) + if key == 'namespace': + result.setdefault(key, []).append(value) + else: + result[key] = value + return result + + def write_shared_locations(self, paths, dry_run=False): + """ + Write shared location information to the SHARED file in .dist-info. + :param paths: A dictionary as described in the documentation for + :meth:`shared_locations`. + :param dry_run: If True, the action is logged but no file is actually + written. + :return: The path of the file written to. + """ + shared_path = os.path.join(self.path, 'SHARED') + logger.info('creating %s', shared_path) + if dry_run: + return None + lines = [] + for key in ('prefix', 'lib', 'headers', 'scripts', 'data'): + path = paths[key] + if os.path.isdir(paths[key]): + lines.append('%s=%s' % (key, path)) + for ns in paths.get('namespace', ()): + lines.append('namespace=%s' % ns) + + with codecs.open(shared_path, 'w', encoding='utf-8') as f: + f.write('\n'.join(lines)) + return shared_path + + def get_distinfo_resource(self, path): + if path not in DIST_FILES: + raise DistlibException('invalid path for a dist-info file: ' + '%r at %r' % (path, self.path)) + finder = resources.finder_for_path(self.path) + if finder is None: + raise DistlibException('Unable to get a finder for %s' % self.path) + return finder.find(path) + + def get_distinfo_file(self, path): + """ + Returns a path located under the ``.dist-info`` directory. Returns a + string representing the path. + + :parameter path: a ``'/'``-separated path relative to the + ``.dist-info`` directory or an absolute path; + If *path* is an absolute path and doesn't start + with the ``.dist-info`` directory path, + a :class:`DistlibException` is raised + :type path: str + :rtype: str + """ + # Check if it is an absolute path # XXX use relpath, add tests + if path.find(os.sep) >= 0: + # it's an absolute path? + distinfo_dirname, path = path.split(os.sep)[-2:] + if distinfo_dirname != self.path.split(os.sep)[-1]: + raise DistlibException( + 'dist-info file %r does not belong to the %r %s ' + 'distribution' % (path, self.name, self.version)) + + # The file must be relative + if path not in DIST_FILES: + raise DistlibException('invalid path for a dist-info file: ' + '%r at %r' % (path, self.path)) + + return os.path.join(self.path, path) + + def list_distinfo_files(self): + """ + Iterates over the ``RECORD`` entries and returns paths for each line if + the path is pointing to a file located in the ``.dist-info`` directory + or one of its subdirectories. + + :returns: iterator of paths + """ + base = os.path.dirname(self.path) + for path, checksum, size in self._get_records(): + # XXX add separator or use real relpath algo + if not os.path.isabs(path): + path = os.path.join(base, path) + if path.startswith(self.path): + yield path + + def __eq__(self, other): + return (isinstance(other, InstalledDistribution) and + self.path == other.path) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + + +class EggInfoDistribution(BaseInstalledDistribution): + """Created with the *path* of the ``.egg-info`` directory or file provided + to the constructor. It reads the metadata contained in the file itself, or + if the given path happens to be a directory, the metadata is read from the + file ``PKG-INFO`` under that directory.""" + + requested = True # as we have no way of knowing, assume it was + shared_locations = {} + + def __init__(self, path, env=None): + def set_name_and_version(s, n, v): + s.name = n + s.key = n.lower() # for case-insensitive comparisons + s.version = v + + self.path = path + self.dist_path = env + if env and env._cache_enabled and path in env._cache_egg.path: + metadata = env._cache_egg.path[path].metadata + set_name_and_version(self, metadata.name, metadata.version) + else: + metadata = self._get_metadata(path) + + # Need to be set before caching + set_name_and_version(self, metadata.name, metadata.version) + + if env and env._cache_enabled: + env._cache_egg.add(self) + super(EggInfoDistribution, self).__init__(metadata, path, env) + + def _get_metadata(self, path): + requires = None + + def parse_requires_data(data): + """Create a list of dependencies from a requires.txt file. + + *data*: the contents of a setuptools-produced requires.txt file. + """ + reqs = [] + lines = data.splitlines() + for line in lines: + line = line.strip() + if line.startswith('['): + logger.warning('Unexpected line: quitting requirement scan: %r', + line) + break + r = parse_requirement(line) + if not r: + logger.warning('Not recognised as a requirement: %r', line) + continue + if r.extras: + logger.warning('extra requirements in requires.txt are ' + 'not supported') + if not r.constraints: + reqs.append(r.name) + else: + cons = ', '.join('%s%s' % c for c in r.constraints) + reqs.append('%s (%s)' % (r.name, cons)) + return reqs + + def parse_requires_path(req_path): + """Create a list of dependencies from a requires.txt file. + + *req_path*: the path to a setuptools-produced requires.txt file. + """ + + reqs = [] + try: + with codecs.open(req_path, 'r', 'utf-8') as fp: + reqs = parse_requires_data(fp.read()) + except IOError: + pass + return reqs + + if path.endswith('.egg'): + if os.path.isdir(path): + meta_path = os.path.join(path, 'EGG-INFO', 'PKG-INFO') + metadata = Metadata(path=meta_path, scheme='legacy') + req_path = os.path.join(path, 'EGG-INFO', 'requires.txt') + requires = parse_requires_path(req_path) + else: + # FIXME handle the case where zipfile is not available + zipf = zipimport.zipimporter(path) + fileobj = StringIO( + zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8')) + metadata = Metadata(fileobj=fileobj, scheme='legacy') + try: + data = zipf.get_data('EGG-INFO/requires.txt') + requires = parse_requires_data(data.decode('utf-8')) + except IOError: + requires = None + elif path.endswith('.egg-info'): + if os.path.isdir(path): + req_path = os.path.join(path, 'requires.txt') + requires = parse_requires_path(req_path) + path = os.path.join(path, 'PKG-INFO') + metadata = Metadata(path=path, scheme='legacy') + else: + raise DistlibException('path must end with .egg-info or .egg, ' + 'got %r' % path) + + if requires: + metadata.add_requirements(requires) + return metadata + + def __repr__(self): + return '<EggInfoDistribution %r %s at %r>' % ( + self.name, self.version, self.path) + + def __str__(self): + return "%s %s" % (self.name, self.version) + + def check_installed_files(self): + """ + Checks that the hashes and sizes of the files in ``RECORD`` are + matched by the files themselves. Returns a (possibly empty) list of + mismatches. Each entry in the mismatch list will be a tuple consisting + of the path, 'exists', 'size' or 'hash' according to what didn't match + (existence is checked first, then size, then hash), the expected + value and the actual value. + """ + mismatches = [] + record_path = os.path.join(self.path, 'installed-files.txt') + if os.path.exists(record_path): + for path, _, _ in self.list_installed_files(): + if path == record_path: + continue + if not os.path.exists(path): + mismatches.append((path, 'exists', True, False)) + return mismatches + + def list_installed_files(self): + """ + Iterates over the ``installed-files.txt`` entries and returns a tuple + ``(path, hash, size)`` for each line. + + :returns: a list of (path, hash, size) + """ + + def _md5(path): + f = open(path, 'rb') + try: + content = f.read() + finally: + f.close() + return hashlib.md5(content).hexdigest() + + def _size(path): + return os.stat(path).st_size + + record_path = os.path.join(self.path, 'installed-files.txt') + result = [] + if os.path.exists(record_path): + with codecs.open(record_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + p = os.path.normpath(os.path.join(self.path, line)) + # "./" is present as a marker between installed files + # and installation metadata files + if not os.path.exists(p): + logger.warning('Non-existent file: %s', p) + if p.endswith(('.pyc', '.pyo')): + continue + #otherwise fall through and fail + if not os.path.isdir(p): + result.append((p, _md5(p), _size(p))) + result.append((record_path, None, None)) + return result + + def list_distinfo_files(self, absolute=False): + """ + Iterates over the ``installed-files.txt`` entries and returns paths for + each line if the path is pointing to a file located in the + ``.egg-info`` directory or one of its subdirectories. + + :parameter absolute: If *absolute* is ``True``, each returned path is + transformed into a local absolute path. Otherwise the + raw value from ``installed-files.txt`` is returned. + :type absolute: boolean + :returns: iterator of paths + """ + record_path = os.path.join(self.path, 'installed-files.txt') + skip = True + with codecs.open(record_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + if line == './': + skip = False + continue + if not skip: + p = os.path.normpath(os.path.join(self.path, line)) + if p.startswith(self.path): + if absolute: + yield p + else: + yield line + + def __eq__(self, other): + return (isinstance(other, EggInfoDistribution) and + self.path == other.path) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + +new_dist_class = InstalledDistribution +old_dist_class = EggInfoDistribution + + +class DependencyGraph(object): + """ + Represents a dependency graph between distributions. + + The dependency relationships are stored in an ``adjacency_list`` that maps + distributions to a list of ``(other, label)`` tuples where ``other`` + is a distribution and the edge is labeled with ``label`` (i.e. the version + specifier, if such was provided). Also, for more efficient traversal, for + every distribution ``x``, a list of predecessors is kept in + ``reverse_list[x]``. An edge from distribution ``a`` to + distribution ``b`` means that ``a`` depends on ``b``. If any missing + dependencies are found, they are stored in ``missing``, which is a + dictionary that maps distributions to a list of requirements that were not + provided by any other distributions. + """ + + def __init__(self): + self.adjacency_list = {} + self.reverse_list = {} + self.missing = {} + + def add_distribution(self, distribution): + """Add the *distribution* to the graph. + + :type distribution: :class:`distutils2.database.InstalledDistribution` + or :class:`distutils2.database.EggInfoDistribution` + """ + self.adjacency_list[distribution] = [] + self.reverse_list[distribution] = [] + #self.missing[distribution] = [] + + def add_edge(self, x, y, label=None): + """Add an edge from distribution *x* to distribution *y* with the given + *label*. + + :type x: :class:`distutils2.database.InstalledDistribution` or + :class:`distutils2.database.EggInfoDistribution` + :type y: :class:`distutils2.database.InstalledDistribution` or + :class:`distutils2.database.EggInfoDistribution` + :type label: ``str`` or ``None`` + """ + self.adjacency_list[x].append((y, label)) + # multiple edges are allowed, so be careful + if x not in self.reverse_list[y]: + self.reverse_list[y].append(x) + + def add_missing(self, distribution, requirement): + """ + Add a missing *requirement* for the given *distribution*. + + :type distribution: :class:`distutils2.database.InstalledDistribution` + or :class:`distutils2.database.EggInfoDistribution` + :type requirement: ``str`` + """ + logger.debug('%s missing %r', distribution, requirement) + self.missing.setdefault(distribution, []).append(requirement) + + def _repr_dist(self, dist): + return '%s %s' % (dist.name, dist.version) + + def repr_node(self, dist, level=1): + """Prints only a subgraph""" + output = [self._repr_dist(dist)] + for other, label in self.adjacency_list[dist]: + dist = self._repr_dist(other) + if label is not None: + dist = '%s [%s]' % (dist, label) + output.append(' ' * level + str(dist)) + suboutput = self.repr_node(other, level + 1) + subs = suboutput.split('\n') + output.extend(subs[1:]) + return '\n'.join(output) + + def to_dot(self, f, skip_disconnected=True): + """Writes a DOT output for the graph to the provided file *f*. + + If *skip_disconnected* is set to ``True``, then all distributions + that are not dependent on any other distribution are skipped. + + :type f: has to support ``file``-like operations + :type skip_disconnected: ``bool`` + """ + disconnected = [] + + f.write("digraph dependencies {\n") + for dist, adjs in self.adjacency_list.items(): + if len(adjs) == 0 and not skip_disconnected: + disconnected.append(dist) + for other, label in adjs: + if not label is None: + f.write('"%s" -> "%s" [label="%s"]\n' % + (dist.name, other.name, label)) + else: + f.write('"%s" -> "%s"\n' % (dist.name, other.name)) + if not skip_disconnected and len(disconnected) > 0: + f.write('subgraph disconnected {\n') + f.write('label = "Disconnected"\n') + f.write('bgcolor = red\n') + + for dist in disconnected: + f.write('"%s"' % dist.name) + f.write('\n') + f.write('}\n') + f.write('}\n') + + def topological_sort(self): + """ + Perform a topological sort of the graph. + :return: A tuple, the first element of which is a topologically sorted + list of distributions, and the second element of which is a + list of distributions that cannot be sorted because they have + circular dependencies and so form a cycle. + """ + result = [] + # Make a shallow copy of the adjacency list + alist = {} + for k, v in self.adjacency_list.items(): + alist[k] = v[:] + while True: + # See what we can remove in this run + to_remove = [] + for k, v in list(alist.items())[:]: + if not v: + to_remove.append(k) + del alist[k] + if not to_remove: + # What's left in alist (if anything) is a cycle. + break + # Remove from the adjacency list of others + for k, v in alist.items(): + alist[k] = [(d, r) for d, r in v if d not in to_remove] + logger.debug('Moving to result: %s', + ['%s (%s)' % (d.name, d.version) for d in to_remove]) + result.extend(to_remove) + return result, list(alist.keys()) + + def __repr__(self): + """Representation of the graph""" + output = [] + for dist, adjs in self.adjacency_list.items(): + output.append(self.repr_node(dist)) + return '\n'.join(output) + + +def make_graph(dists, scheme='default'): + """Makes a dependency graph from the given distributions. + + :parameter dists: a list of distributions + :type dists: list of :class:`distutils2.database.InstalledDistribution` and + :class:`distutils2.database.EggInfoDistribution` instances + :rtype: a :class:`DependencyGraph` instance + """ + scheme = get_scheme(scheme) + graph = DependencyGraph() + provided = {} # maps names to lists of (version, dist) tuples + + # first, build the graph and find out what's provided + for dist in dists: + graph.add_distribution(dist) + + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Add to provided: %s, %s, %s', name, version, dist) + provided.setdefault(name, []).append((version, dist)) + + # now make the edges + for dist in dists: + requires = (dist.run_requires | dist.meta_requires | + dist.build_requires | dist.dev_requires) + for req in requires: + try: + matcher = scheme.matcher(req) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + logger.warning('could not read version %r - using name only', + req) + name = req.split()[0] + matcher = scheme.matcher(name) + + name = matcher.key # case-insensitive + + matched = False + if name in provided: + for version, provider in provided[name]: + try: + match = matcher.match(version) + except UnsupportedVersionError: + match = False + + if match: + graph.add_edge(dist, provider, req) + matched = True + break + if not matched: + graph.add_missing(dist, req) + return graph + + +def get_dependent_dists(dists, dist): + """Recursively generate a list of distributions from *dists* that are + dependent on *dist*. + + :param dists: a list of distributions + :param dist: a distribution, member of *dists* for which we are interested + """ + if dist not in dists: + raise DistlibException('given distribution %r is not a member ' + 'of the list' % dist.name) + graph = make_graph(dists) + + dep = [dist] # dependent distributions + todo = graph.reverse_list[dist] # list of nodes we should inspect + + while todo: + d = todo.pop() + dep.append(d) + for succ in graph.reverse_list[d]: + if succ not in dep: + todo.append(succ) + + dep.pop(0) # remove dist from dep, was there to prevent infinite loops + return dep + + +def get_required_dists(dists, dist): + """Recursively generate a list of distributions from *dists* that are + required by *dist*. + + :param dists: a list of distributions + :param dist: a distribution, member of *dists* for which we are interested + """ + if dist not in dists: + raise DistlibException('given distribution %r is not a member ' + 'of the list' % dist.name) + graph = make_graph(dists) + + req = [] # required distributions + todo = graph.adjacency_list[dist] # list of nodes we should inspect + + while todo: + d = todo.pop()[0] + req.append(d) + for pred in graph.adjacency_list[d]: + if pred not in req: + todo.append(pred) + + return req + + +def make_dist(name, version, **kwargs): + """ + A convenience method for making a dist given just a name and version. + """ + summary = kwargs.pop('summary', 'Placeholder for summary') + md = Metadata(**kwargs) + md.name = name + md.version = version + md.summary = summary or 'Placeholder for summary' + return Distribution(md) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/index.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/index.py new file mode 100644 index 0000000..6803dd2 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/index.py @@ -0,0 +1,515 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import hashlib +import logging +import os +import shutil +import subprocess +import tempfile +try: + from threading import Thread +except ImportError: + from dummy_threading import Thread + +from . import DistlibException +from .compat import (HTTPBasicAuthHandler, Request, HTTPPasswordMgr, + urlparse, build_opener, string_types) +from .util import cached_property, zip_dir, ServerProxy + +logger = logging.getLogger(__name__) + +DEFAULT_INDEX = 'https://pypi.python.org/pypi' +DEFAULT_REALM = 'pypi' + +class PackageIndex(object): + """ + This class represents a package index compatible with PyPI, the Python + Package Index. + """ + + boundary = b'----------ThIs_Is_tHe_distlib_index_bouNdaRY_$' + + def __init__(self, url=None): + """ + Initialise an instance. + + :param url: The URL of the index. If not specified, the URL for PyPI is + used. + """ + self.url = url or DEFAULT_INDEX + self.read_configuration() + scheme, netloc, path, params, query, frag = urlparse(self.url) + if params or query or frag or scheme not in ('http', 'https'): + raise DistlibException('invalid repository: %s' % self.url) + self.password_handler = None + self.ssl_verifier = None + self.gpg = None + self.gpg_home = None + self.rpc_proxy = None + with open(os.devnull, 'w') as sink: + # Use gpg by default rather than gpg2, as gpg2 insists on + # prompting for passwords + for s in ('gpg', 'gpg2'): + try: + rc = subprocess.check_call([s, '--version'], stdout=sink, + stderr=sink) + if rc == 0: + self.gpg = s + break + except OSError: + pass + + def _get_pypirc_command(self): + """ + Get the distutils command for interacting with PyPI configurations. + :return: the command. + """ + from distutils.core import Distribution + from distutils.config import PyPIRCCommand + d = Distribution() + return PyPIRCCommand(d) + + def read_configuration(self): + """ + Read the PyPI access configuration as supported by distutils, getting + PyPI to do the actual work. This populates ``username``, ``password``, + ``realm`` and ``url`` attributes from the configuration. + """ + # get distutils to do the work + c = self._get_pypirc_command() + c.repository = self.url + cfg = c._read_pypirc() + self.username = cfg.get('username') + self.password = cfg.get('password') + self.realm = cfg.get('realm', 'pypi') + self.url = cfg.get('repository', self.url) + + def save_configuration(self): + """ + Save the PyPI access configuration. You must have set ``username`` and + ``password`` attributes before calling this method. + + Again, distutils is used to do the actual work. + """ + self.check_credentials() + # get distutils to do the work + c = self._get_pypirc_command() + c._store_pypirc(self.username, self.password) + + def check_credentials(self): + """ + Check that ``username`` and ``password`` have been set, and raise an + exception if not. + """ + if self.username is None or self.password is None: + raise DistlibException('username and password must be set') + pm = HTTPPasswordMgr() + _, netloc, _, _, _, _ = urlparse(self.url) + pm.add_password(self.realm, netloc, self.username, self.password) + self.password_handler = HTTPBasicAuthHandler(pm) + + def register(self, metadata): + """ + Register a distribution on PyPI, using the provided metadata. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the distribution to be + registered. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + metadata.validate() + d = metadata.todict() + d[':action'] = 'verify' + request = self.encode_request(d.items(), []) + response = self.send_request(request) + d[':action'] = 'submit' + request = self.encode_request(d.items(), []) + return self.send_request(request) + + def _reader(self, name, stream, outbuf): + """ + Thread runner for reading lines of from a subprocess into a buffer. + + :param name: The logical name of the stream (used for logging only). + :param stream: The stream to read from. This will typically a pipe + connected to the output stream of a subprocess. + :param outbuf: The list to append the read lines to. + """ + while True: + s = stream.readline() + if not s: + break + s = s.decode('utf-8').rstrip() + outbuf.append(s) + logger.debug('%s: %s' % (name, s)) + stream.close() + + def get_sign_command(self, filename, signer, sign_password, + keystore=None): + """ + Return a suitable command for signing a file. + + :param filename: The pathname to the file to be signed. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: The signing command as a list suitable to be + passed to :class:`subprocess.Popen`. + """ + cmd = [self.gpg, '--status-fd', '2', '--no-tty'] + if keystore is None: + keystore = self.gpg_home + if keystore: + cmd.extend(['--homedir', keystore]) + if sign_password is not None: + cmd.extend(['--batch', '--passphrase-fd', '0']) + td = tempfile.mkdtemp() + sf = os.path.join(td, os.path.basename(filename) + '.asc') + cmd.extend(['--detach-sign', '--armor', '--local-user', + signer, '--output', sf, filename]) + logger.debug('invoking: %s', ' '.join(cmd)) + return cmd, sf + + def run_command(self, cmd, input_data=None): + """ + Run a command in a child process , passing it any input data specified. + + :param cmd: The command to run. + :param input_data: If specified, this must be a byte string containing + data to be sent to the child process. + :return: A tuple consisting of the subprocess' exit code, a list of + lines read from the subprocess' ``stdout``, and a list of + lines read from the subprocess' ``stderr``. + """ + kwargs = { + 'stdout': subprocess.PIPE, + 'stderr': subprocess.PIPE, + } + if input_data is not None: + kwargs['stdin'] = subprocess.PIPE + stdout = [] + stderr = [] + p = subprocess.Popen(cmd, **kwargs) + # We don't use communicate() here because we may need to + # get clever with interacting with the command + t1 = Thread(target=self._reader, args=('stdout', p.stdout, stdout)) + t1.start() + t2 = Thread(target=self._reader, args=('stderr', p.stderr, stderr)) + t2.start() + if input_data is not None: + p.stdin.write(input_data) + p.stdin.close() + + p.wait() + t1.join() + t2.join() + return p.returncode, stdout, stderr + + def sign_file(self, filename, signer, sign_password, keystore=None): + """ + Sign a file. + + :param filename: The pathname to the file to be signed. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param keystore: The path to a directory which contains the keys + used in signing. If not specified, the instance's + ``gpg_home`` attribute is used instead. + :return: The absolute pathname of the file where the signature is + stored. + """ + cmd, sig_file = self.get_sign_command(filename, signer, sign_password, + keystore) + rc, stdout, stderr = self.run_command(cmd, + sign_password.encode('utf-8')) + if rc != 0: + raise DistlibException('sign command failed with error ' + 'code %s' % rc) + return sig_file + + def upload_file(self, metadata, filename, signer=None, sign_password=None, + filetype='sdist', pyversion='source', keystore=None): + """ + Upload a release file to the index. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the file to be uploaded. + :param filename: The pathname of the file to be uploaded. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param filetype: The type of the file being uploaded. This is the + distutils command which produced that file, e.g. + ``sdist`` or ``bdist_wheel``. + :param pyversion: The version of Python which the release relates + to. For code compatible with any Python, this would + be ``source``, otherwise it would be e.g. ``3.2``. + :param keystore: The path to a directory which contains the keys + used in signing. If not specified, the instance's + ``gpg_home`` attribute is used instead. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + if not os.path.exists(filename): + raise DistlibException('not found: %s' % filename) + metadata.validate() + d = metadata.todict() + sig_file = None + if signer: + if not self.gpg: + logger.warning('no signing program available - not signed') + else: + sig_file = self.sign_file(filename, signer, sign_password, + keystore) + with open(filename, 'rb') as f: + file_data = f.read() + md5_digest = hashlib.md5(file_data).hexdigest() + sha256_digest = hashlib.sha256(file_data).hexdigest() + d.update({ + ':action': 'file_upload', + 'protocol_version': '1', + 'filetype': filetype, + 'pyversion': pyversion, + 'md5_digest': md5_digest, + 'sha256_digest': sha256_digest, + }) + files = [('content', os.path.basename(filename), file_data)] + if sig_file: + with open(sig_file, 'rb') as f: + sig_data = f.read() + files.append(('gpg_signature', os.path.basename(sig_file), + sig_data)) + shutil.rmtree(os.path.dirname(sig_file)) + request = self.encode_request(d.items(), files) + return self.send_request(request) + + def upload_documentation(self, metadata, doc_dir): + """ + Upload documentation to the index. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the documentation to be + uploaded. + :param doc_dir: The pathname of the directory which contains the + documentation. This should be the directory that + contains the ``index.html`` for the documentation. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + if not os.path.isdir(doc_dir): + raise DistlibException('not a directory: %r' % doc_dir) + fn = os.path.join(doc_dir, 'index.html') + if not os.path.exists(fn): + raise DistlibException('not found: %r' % fn) + metadata.validate() + name, version = metadata.name, metadata.version + zip_data = zip_dir(doc_dir).getvalue() + fields = [(':action', 'doc_upload'), + ('name', name), ('version', version)] + files = [('content', name, zip_data)] + request = self.encode_request(fields, files) + return self.send_request(request) + + def get_verify_command(self, signature_filename, data_filename, + keystore=None): + """ + Return a suitable command for verifying a file. + + :param signature_filename: The pathname to the file containing the + signature. + :param data_filename: The pathname to the file containing the + signed data. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: The verifying command as a list suitable to be + passed to :class:`subprocess.Popen`. + """ + cmd = [self.gpg, '--status-fd', '2', '--no-tty'] + if keystore is None: + keystore = self.gpg_home + if keystore: + cmd.extend(['--homedir', keystore]) + cmd.extend(['--verify', signature_filename, data_filename]) + logger.debug('invoking: %s', ' '.join(cmd)) + return cmd + + def verify_signature(self, signature_filename, data_filename, + keystore=None): + """ + Verify a signature for a file. + + :param signature_filename: The pathname to the file containing the + signature. + :param data_filename: The pathname to the file containing the + signed data. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: True if the signature was verified, else False. + """ + if not self.gpg: + raise DistlibException('verification unavailable because gpg ' + 'unavailable') + cmd = self.get_verify_command(signature_filename, data_filename, + keystore) + rc, stdout, stderr = self.run_command(cmd) + if rc not in (0, 1): + raise DistlibException('verify command failed with error ' + 'code %s' % rc) + return rc == 0 + + def download_file(self, url, destfile, digest=None, reporthook=None): + """ + This is a convenience method for downloading a file from an URL. + Normally, this will be a file from the index, though currently + no check is made for this (i.e. a file can be downloaded from + anywhere). + + The method is just like the :func:`urlretrieve` function in the + standard library, except that it allows digest computation to be + done during download and checking that the downloaded data + matched any expected value. + + :param url: The URL of the file to be downloaded (assumed to be + available via an HTTP GET request). + :param destfile: The pathname where the downloaded file is to be + saved. + :param digest: If specified, this must be a (hasher, value) + tuple, where hasher is the algorithm used (e.g. + ``'md5'``) and ``value`` is the expected value. + :param reporthook: The same as for :func:`urlretrieve` in the + standard library. + """ + if digest is None: + digester = None + logger.debug('No digest specified') + else: + if isinstance(digest, (list, tuple)): + hasher, digest = digest + else: + hasher = 'md5' + digester = getattr(hashlib, hasher)() + logger.debug('Digest specified: %s' % digest) + # The following code is equivalent to urlretrieve. + # We need to do it this way so that we can compute the + # digest of the file as we go. + with open(destfile, 'wb') as dfp: + # addinfourl is not a context manager on 2.x + # so we have to use try/finally + sfp = self.send_request(Request(url)) + try: + headers = sfp.info() + blocksize = 8192 + size = -1 + read = 0 + blocknum = 0 + if "content-length" in headers: + size = int(headers["Content-Length"]) + if reporthook: + reporthook(blocknum, blocksize, size) + while True: + block = sfp.read(blocksize) + if not block: + break + read += len(block) + dfp.write(block) + if digester: + digester.update(block) + blocknum += 1 + if reporthook: + reporthook(blocknum, blocksize, size) + finally: + sfp.close() + + # check that we got the whole file, if we can + if size >= 0 and read < size: + raise DistlibException( + 'retrieval incomplete: got only %d out of %d bytes' + % (read, size)) + # if we have a digest, it must match. + if digester: + actual = digester.hexdigest() + if digest != actual: + raise DistlibException('%s digest mismatch for %s: expected ' + '%s, got %s' % (hasher, destfile, + digest, actual)) + logger.debug('Digest verified: %s', digest) + + def send_request(self, req): + """ + Send a standard library :class:`Request` to PyPI and return its + response. + + :param req: The request to send. + :return: The HTTP response from PyPI (a standard library HTTPResponse). + """ + handlers = [] + if self.password_handler: + handlers.append(self.password_handler) + if self.ssl_verifier: + handlers.append(self.ssl_verifier) + opener = build_opener(*handlers) + return opener.open(req) + + def encode_request(self, fields, files): + """ + Encode fields and files for posting to an HTTP server. + + :param fields: The fields to send as a list of (fieldname, value) + tuples. + :param files: The files to send as a list of (fieldname, filename, + file_bytes) tuple. + """ + # Adapted from packaging, which in turn was adapted from + # http://code.activestate.com/recipes/146306 + + parts = [] + boundary = self.boundary + for k, values in fields: + if not isinstance(values, (list, tuple)): + values = [values] + + for v in values: + parts.extend(( + b'--' + boundary, + ('Content-Disposition: form-data; name="%s"' % + k).encode('utf-8'), + b'', + v.encode('utf-8'))) + for key, filename, value in files: + parts.extend(( + b'--' + boundary, + ('Content-Disposition: form-data; name="%s"; filename="%s"' % + (key, filename)).encode('utf-8'), + b'', + value)) + + parts.extend((b'--' + boundary + b'--', b'')) + + body = b'\r\n'.join(parts) + ct = b'multipart/form-data; boundary=' + boundary + headers = { + 'Content-type': ct, + 'Content-length': str(len(body)) + } + return Request(self.url, body, headers) + + def search(self, terms, operator=None): + if isinstance(terms, string_types): + terms = {'name': terms} + if self.rpc_proxy is None: + self.rpc_proxy = ServerProxy(self.url, timeout=3.0) + return self.rpc_proxy.search(terms, operator or 'and') diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/locators.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/locators.py new file mode 100644 index 0000000..14789ef --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/locators.py @@ -0,0 +1,1283 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2015 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# + +import gzip +from io import BytesIO +import json +import logging +import os +import posixpath +import re +try: + import threading +except ImportError: # pragma: no cover + import dummy_threading as threading +import zlib + +from . import DistlibException +from .compat import (urljoin, urlparse, urlunparse, url2pathname, pathname2url, + queue, quote, unescape, string_types, build_opener, + HTTPRedirectHandler as BaseRedirectHandler, text_type, + Request, HTTPError, URLError) +from .database import Distribution, DistributionPath, make_dist +from .metadata import Metadata +from .util import (cached_property, parse_credentials, ensure_slash, + split_filename, get_project_data, parse_requirement, + parse_name_and_version, ServerProxy, normalize_name) +from .version import get_scheme, UnsupportedVersionError +from .wheel import Wheel, is_compatible + +logger = logging.getLogger(__name__) + +HASHER_HASH = re.compile('^(\w+)=([a-f0-9]+)') +CHARSET = re.compile(r';\s*charset\s*=\s*(.*)\s*$', re.I) +HTML_CONTENT_TYPE = re.compile('text/html|application/x(ht)?ml') +DEFAULT_INDEX = 'https://pypi.python.org/pypi' + +def get_all_distribution_names(url=None): + """ + Return all distribution names known by an index. + :param url: The URL of the index. + :return: A list of all known distribution names. + """ + if url is None: + url = DEFAULT_INDEX + client = ServerProxy(url, timeout=3.0) + return client.list_packages() + +class RedirectHandler(BaseRedirectHandler): + """ + A class to work around a bug in some Python 3.2.x releases. + """ + # There's a bug in the base version for some 3.2.x + # (e.g. 3.2.2 on Ubuntu Oneiric). If a Location header + # returns e.g. /abc, it bails because it says the scheme '' + # is bogus, when actually it should use the request's + # URL for the scheme. See Python issue #13696. + def http_error_302(self, req, fp, code, msg, headers): + # Some servers (incorrectly) return multiple Location headers + # (so probably same goes for URI). Use first header. + newurl = None + for key in ('location', 'uri'): + if key in headers: + newurl = headers[key] + break + if newurl is None: + return + urlparts = urlparse(newurl) + if urlparts.scheme == '': + newurl = urljoin(req.get_full_url(), newurl) + if hasattr(headers, 'replace_header'): + headers.replace_header(key, newurl) + else: + headers[key] = newurl + return BaseRedirectHandler.http_error_302(self, req, fp, code, msg, + headers) + + http_error_301 = http_error_303 = http_error_307 = http_error_302 + +class Locator(object): + """ + A base class for locators - things that locate distributions. + """ + source_extensions = ('.tar.gz', '.tar.bz2', '.tar', '.zip', '.tgz', '.tbz') + binary_extensions = ('.egg', '.exe', '.whl') + excluded_extensions = ('.pdf',) + + # A list of tags indicating which wheels you want to match. The default + # value of None matches against the tags compatible with the running + # Python. If you want to match other values, set wheel_tags on a locator + # instance to a list of tuples (pyver, abi, arch) which you want to match. + wheel_tags = None + + downloadable_extensions = source_extensions + ('.whl',) + + def __init__(self, scheme='default'): + """ + Initialise an instance. + :param scheme: Because locators look for most recent versions, they + need to know the version scheme to use. This specifies + the current PEP-recommended scheme - use ``'legacy'`` + if you need to support existing distributions on PyPI. + """ + self._cache = {} + self.scheme = scheme + # Because of bugs in some of the handlers on some of the platforms, + # we use our own opener rather than just using urlopen. + self.opener = build_opener(RedirectHandler()) + # If get_project() is called from locate(), the matcher instance + # is set from the requirement passed to locate(). See issue #18 for + # why this can be useful to know. + self.matcher = None + self.errors = queue.Queue() + + def get_errors(self): + """ + Return any errors which have occurred. + """ + result = [] + while not self.errors.empty(): # pragma: no cover + try: + e = self.errors.get(False) + result.append(e) + except self.errors.Empty: + continue + self.errors.task_done() + return result + + def clear_errors(self): + """ + Clear any errors which may have been logged. + """ + # Just get the errors and throw them away + self.get_errors() + + def clear_cache(self): + self._cache.clear() + + def _get_scheme(self): + return self._scheme + + def _set_scheme(self, value): + self._scheme = value + + scheme = property(_get_scheme, _set_scheme) + + def _get_project(self, name): + """ + For a given project, get a dictionary mapping available versions to Distribution + instances. + + This should be implemented in subclasses. + + If called from a locate() request, self.matcher will be set to a + matcher for the requirement to satisfy, otherwise it will be None. + """ + raise NotImplementedError('Please implement in the subclass') + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Please implement in the subclass') + + def get_project(self, name): + """ + For a given project, get a dictionary mapping available versions to Distribution + instances. + + This calls _get_project to do all the work, and just implements a caching layer on top. + """ + if self._cache is None: + result = self._get_project(name) + elif name in self._cache: + result = self._cache[name] + else: + self.clear_errors() + result = self._get_project(name) + self._cache[name] = result + return result + + def score_url(self, url): + """ + Give an url a score which can be used to choose preferred URLs + for a given project release. + """ + t = urlparse(url) + basename = posixpath.basename(t.path) + compatible = True + is_wheel = basename.endswith('.whl') + if is_wheel: + compatible = is_compatible(Wheel(basename), self.wheel_tags) + return (t.scheme != 'https', 'pypi.python.org' in t.netloc, + is_wheel, compatible, basename) + + def prefer_url(self, url1, url2): + """ + Choose one of two URLs where both are candidates for distribution + archives for the same version of a distribution (for example, + .tar.gz vs. zip). + + The current implementation favours https:// URLs over http://, archives + from PyPI over those from other locations, wheel compatibility (if a + wheel) and then the archive name. + """ + result = url2 + if url1: + s1 = self.score_url(url1) + s2 = self.score_url(url2) + if s1 > s2: + result = url1 + if result != url2: + logger.debug('Not replacing %r with %r', url1, url2) + else: + logger.debug('Replacing %r with %r', url1, url2) + return result + + def split_filename(self, filename, project_name): + """ + Attempt to split a filename in project name, version and Python version. + """ + return split_filename(filename, project_name) + + def convert_url_to_download_info(self, url, project_name): + """ + See if a URL is a candidate for a download URL for a project (the URL + has typically been scraped from an HTML page). + + If it is, a dictionary is returned with keys "name", "version", + "filename" and "url"; otherwise, None is returned. + """ + def same_project(name1, name2): + return normalize_name(name1) == normalize_name(name2) + + result = None + scheme, netloc, path, params, query, frag = urlparse(url) + if frag.lower().startswith('egg='): + logger.debug('%s: version hint in fragment: %r', + project_name, frag) + m = HASHER_HASH.match(frag) + if m: + algo, digest = m.groups() + else: + algo, digest = None, None + origpath = path + if path and path[-1] == '/': + path = path[:-1] + if path.endswith('.whl'): + try: + wheel = Wheel(path) + if is_compatible(wheel, self.wheel_tags): + if project_name is None: + include = True + else: + include = same_project(wheel.name, project_name) + if include: + result = { + 'name': wheel.name, + 'version': wheel.version, + 'filename': wheel.filename, + 'url': urlunparse((scheme, netloc, origpath, + params, query, '')), + 'python-version': ', '.join( + ['.'.join(list(v[2:])) for v in wheel.pyver]), + } + except Exception as e: # pragma: no cover + logger.warning('invalid path for wheel: %s', path) + elif path.endswith(self.downloadable_extensions): + path = filename = posixpath.basename(path) + for ext in self.downloadable_extensions: + if path.endswith(ext): + path = path[:-len(ext)] + t = self.split_filename(path, project_name) + if not t: + logger.debug('No match for project/version: %s', path) + else: + name, version, pyver = t + if not project_name or same_project(project_name, name): + result = { + 'name': name, + 'version': version, + 'filename': filename, + 'url': urlunparse((scheme, netloc, origpath, + params, query, '')), + #'packagetype': 'sdist', + } + if pyver: + result['python-version'] = pyver + break + if result and algo: + result['%s_digest' % algo] = digest + return result + + def _get_digest(self, info): + """ + Get a digest from a dictionary by looking at keys of the form + 'algo_digest'. + + Returns a 2-tuple (algo, digest) if found, else None. Currently + looks only for SHA256, then MD5. + """ + result = None + for algo in ('sha256', 'md5'): + key = '%s_digest' % algo + if key in info: + result = (algo, info[key]) + break + return result + + def _update_version_data(self, result, info): + """ + Update a result dictionary (the final result from _get_project) with a + dictionary for a specific version, which typically holds information + gleaned from a filename or URL for an archive for the distribution. + """ + name = info.pop('name') + version = info.pop('version') + if version in result: + dist = result[version] + md = dist.metadata + else: + dist = make_dist(name, version, scheme=self.scheme) + md = dist.metadata + dist.digest = digest = self._get_digest(info) + url = info['url'] + result['digests'][url] = digest + if md.source_url != info['url']: + md.source_url = self.prefer_url(md.source_url, url) + result['urls'].setdefault(version, set()).add(url) + dist.locator = self + result[version] = dist + + def locate(self, requirement, prereleases=False): + """ + Find the most recent distribution which matches the given + requirement. + + :param requirement: A requirement of the form 'foo (1.0)' or perhaps + 'foo (>= 1.0, < 2.0, != 1.3)' + :param prereleases: If ``True``, allow pre-release versions + to be located. Otherwise, pre-release versions + are not returned. + :return: A :class:`Distribution` instance, or ``None`` if no such + distribution could be located. + """ + result = None + r = parse_requirement(requirement) + if r is None: + raise DistlibException('Not a valid requirement: %r' % requirement) + scheme = get_scheme(self.scheme) + self.matcher = matcher = scheme.matcher(r.requirement) + logger.debug('matcher: %s (%s)', matcher, type(matcher).__name__) + versions = self.get_project(r.name) + if len(versions) > 2: # urls and digests keys are present + # sometimes, versions are invalid + slist = [] + vcls = matcher.version_class + for k in versions: + if k in ('urls', 'digests'): + continue + try: + if not matcher.match(k): + logger.debug('%s did not match %r', matcher, k) + else: + if prereleases or not vcls(k).is_prerelease: + slist.append(k) + else: + logger.debug('skipping pre-release ' + 'version %s of %s', k, matcher.name) + except Exception: # pragma: no cover + logger.warning('error matching %s with %r', matcher, k) + pass # slist.append(k) + if len(slist) > 1: + slist = sorted(slist, key=scheme.key) + if slist: + logger.debug('sorted list: %s', slist) + version = slist[-1] + result = versions[version] + if result: + if r.extras: + result.extras = r.extras + result.download_urls = versions.get('urls', {}).get(version, set()) + d = {} + sd = versions.get('digests', {}) + for url in result.download_urls: + if url in sd: + d[url] = sd[url] + result.digests = d + self.matcher = None + return result + + +class PyPIRPCLocator(Locator): + """ + This locator uses XML-RPC to locate distributions. It therefore + cannot be used with simple mirrors (that only mirror file content). + """ + def __init__(self, url, **kwargs): + """ + Initialise an instance. + + :param url: The URL to use for XML-RPC. + :param kwargs: Passed to the superclass constructor. + """ + super(PyPIRPCLocator, self).__init__(**kwargs) + self.base_url = url + self.client = ServerProxy(url, timeout=3.0) + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + return set(self.client.list_packages()) + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + versions = self.client.package_releases(name, True) + for v in versions: + urls = self.client.release_urls(name, v) + data = self.client.release_data(name, v) + metadata = Metadata(scheme=self.scheme) + metadata.name = data['name'] + metadata.version = data['version'] + metadata.license = data.get('license') + metadata.keywords = data.get('keywords', []) + metadata.summary = data.get('summary') + dist = Distribution(metadata) + if urls: + info = urls[0] + metadata.source_url = info['url'] + dist.digest = self._get_digest(info) + dist.locator = self + result[v] = dist + for info in urls: + url = info['url'] + digest = self._get_digest(info) + result['urls'].setdefault(v, set()).add(url) + result['digests'][url] = digest + return result + +class PyPIJSONLocator(Locator): + """ + This locator uses PyPI's JSON interface. It's very limited in functionality + and probably not worth using. + """ + def __init__(self, url, **kwargs): + super(PyPIJSONLocator, self).__init__(**kwargs) + self.base_url = ensure_slash(url) + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Not available from this locator') + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + url = urljoin(self.base_url, '%s/json' % quote(name)) + try: + resp = self.opener.open(url) + data = resp.read().decode() # for now + d = json.loads(data) + md = Metadata(scheme=self.scheme) + data = d['info'] + md.name = data['name'] + md.version = data['version'] + md.license = data.get('license') + md.keywords = data.get('keywords', []) + md.summary = data.get('summary') + dist = Distribution(md) + dist.locator = self + urls = d['urls'] + result[md.version] = dist + for info in d['urls']: + url = info['url'] + dist.download_urls.add(url) + dist.digests[url] = self._get_digest(info) + result['urls'].setdefault(md.version, set()).add(url) + result['digests'][url] = self._get_digest(info) + # Now get other releases + for version, infos in d['releases'].items(): + if version == md.version: + continue # already done + omd = Metadata(scheme=self.scheme) + omd.name = md.name + omd.version = version + odist = Distribution(omd) + odist.locator = self + result[version] = odist + for info in infos: + url = info['url'] + odist.download_urls.add(url) + odist.digests[url] = self._get_digest(info) + result['urls'].setdefault(version, set()).add(url) + result['digests'][url] = self._get_digest(info) +# for info in urls: +# md.source_url = info['url'] +# dist.digest = self._get_digest(info) +# dist.locator = self +# for info in urls: +# url = info['url'] +# result['urls'].setdefault(md.version, set()).add(url) +# result['digests'][url] = self._get_digest(info) + except Exception as e: + self.errors.put(text_type(e)) + logger.exception('JSON fetch failed: %s', e) + return result + + +class Page(object): + """ + This class represents a scraped HTML page. + """ + # The following slightly hairy-looking regex just looks for the contents of + # an anchor link, which has an attribute "href" either immediately preceded + # or immediately followed by a "rel" attribute. The attribute values can be + # declared with double quotes, single quotes or no quotes - which leads to + # the length of the expression. + _href = re.compile(""" +(rel\s*=\s*(?:"(?P<rel1>[^"]*)"|'(?P<rel2>[^']*)'|(?P<rel3>[^>\s\n]*))\s+)? +href\s*=\s*(?:"(?P<url1>[^"]*)"|'(?P<url2>[^']*)'|(?P<url3>[^>\s\n]*)) +(\s+rel\s*=\s*(?:"(?P<rel4>[^"]*)"|'(?P<rel5>[^']*)'|(?P<rel6>[^>\s\n]*)))? +""", re.I | re.S | re.X) + _base = re.compile(r"""<base\s+href\s*=\s*['"]?([^'">]+)""", re.I | re.S) + + def __init__(self, data, url): + """ + Initialise an instance with the Unicode page contents and the URL they + came from. + """ + self.data = data + self.base_url = self.url = url + m = self._base.search(self.data) + if m: + self.base_url = m.group(1) + + _clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) + + @cached_property + def links(self): + """ + Return the URLs of all the links on a page together with information + about their "rel" attribute, for determining which ones to treat as + downloads and which ones to queue for further scraping. + """ + def clean(url): + "Tidy up an URL." + scheme, netloc, path, params, query, frag = urlparse(url) + return urlunparse((scheme, netloc, quote(path), + params, query, frag)) + + result = set() + for match in self._href.finditer(self.data): + d = match.groupdict('') + rel = (d['rel1'] or d['rel2'] or d['rel3'] or + d['rel4'] or d['rel5'] or d['rel6']) + url = d['url1'] or d['url2'] or d['url3'] + url = urljoin(self.base_url, url) + url = unescape(url) + url = self._clean_re.sub(lambda m: '%%%2x' % ord(m.group(0)), url) + result.add((url, rel)) + # We sort the result, hoping to bring the most recent versions + # to the front + result = sorted(result, key=lambda t: t[0], reverse=True) + return result + + +class SimpleScrapingLocator(Locator): + """ + A locator which scrapes HTML pages to locate downloads for a distribution. + This runs multiple threads to do the I/O; performance is at least as good + as pip's PackageFinder, which works in an analogous fashion. + """ + + # These are used to deal with various Content-Encoding schemes. + decoders = { + 'deflate': zlib.decompress, + 'gzip': lambda b: gzip.GzipFile(fileobj=BytesIO(d)).read(), + 'none': lambda b: b, + } + + def __init__(self, url, timeout=None, num_workers=10, **kwargs): + """ + Initialise an instance. + :param url: The root URL to use for scraping. + :param timeout: The timeout, in seconds, to be applied to requests. + This defaults to ``None`` (no timeout specified). + :param num_workers: The number of worker threads you want to do I/O, + This defaults to 10. + :param kwargs: Passed to the superclass. + """ + super(SimpleScrapingLocator, self).__init__(**kwargs) + self.base_url = ensure_slash(url) + self.timeout = timeout + self._page_cache = {} + self._seen = set() + self._to_fetch = queue.Queue() + self._bad_hosts = set() + self.skip_externals = False + self.num_workers = num_workers + self._lock = threading.RLock() + # See issue #45: we need to be resilient when the locator is used + # in a thread, e.g. with concurrent.futures. We can't use self._lock + # as it is for coordinating our internal threads - the ones created + # in _prepare_threads. + self._gplock = threading.RLock() + + def _prepare_threads(self): + """ + Threads are created only when get_project is called, and terminate + before it returns. They are there primarily to parallelise I/O (i.e. + fetching web pages). + """ + self._threads = [] + for i in range(self.num_workers): + t = threading.Thread(target=self._fetch) + t.setDaemon(True) + t.start() + self._threads.append(t) + + def _wait_threads(self): + """ + Tell all the threads to terminate (by sending a sentinel value) and + wait for them to do so. + """ + # Note that you need two loops, since you can't say which + # thread will get each sentinel + for t in self._threads: + self._to_fetch.put(None) # sentinel + for t in self._threads: + t.join() + self._threads = [] + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + with self._gplock: + self.result = result + self.project_name = name + url = urljoin(self.base_url, '%s/' % quote(name)) + self._seen.clear() + self._page_cache.clear() + self._prepare_threads() + try: + logger.debug('Queueing %s', url) + self._to_fetch.put(url) + self._to_fetch.join() + finally: + self._wait_threads() + del self.result + return result + + platform_dependent = re.compile(r'\b(linux-(i\d86|x86_64|arm\w+)|' + r'win(32|-amd64)|macosx-?\d+)\b', re.I) + + def _is_platform_dependent(self, url): + """ + Does an URL refer to a platform-specific download? + """ + return self.platform_dependent.search(url) + + def _process_download(self, url): + """ + See if an URL is a suitable download for a project. + + If it is, register information in the result dictionary (for + _get_project) about the specific version it's for. + + Note that the return value isn't actually used other than as a boolean + value. + """ + if self._is_platform_dependent(url): + info = None + else: + info = self.convert_url_to_download_info(url, self.project_name) + logger.debug('process_download: %s -> %s', url, info) + if info: + with self._lock: # needed because self.result is shared + self._update_version_data(self.result, info) + return info + + def _should_queue(self, link, referrer, rel): + """ + Determine whether a link URL from a referring page and with a + particular "rel" attribute should be queued for scraping. + """ + scheme, netloc, path, _, _, _ = urlparse(link) + if path.endswith(self.source_extensions + self.binary_extensions + + self.excluded_extensions): + result = False + elif self.skip_externals and not link.startswith(self.base_url): + result = False + elif not referrer.startswith(self.base_url): + result = False + elif rel not in ('homepage', 'download'): + result = False + elif scheme not in ('http', 'https', 'ftp'): + result = False + elif self._is_platform_dependent(link): + result = False + else: + host = netloc.split(':', 1)[0] + if host.lower() == 'localhost': + result = False + else: + result = True + logger.debug('should_queue: %s (%s) from %s -> %s', link, rel, + referrer, result) + return result + + def _fetch(self): + """ + Get a URL to fetch from the work queue, get the HTML page, examine its + links for download candidates and candidates for further scraping. + + This is a handy method to run in a thread. + """ + while True: + url = self._to_fetch.get() + try: + if url: + page = self.get_page(url) + if page is None: # e.g. after an error + continue + for link, rel in page.links: + if link not in self._seen: + self._seen.add(link) + if (not self._process_download(link) and + self._should_queue(link, url, rel)): + logger.debug('Queueing %s from %s', link, url) + self._to_fetch.put(link) + except Exception as e: # pragma: no cover + self.errors.put(text_type(e)) + finally: + # always do this, to avoid hangs :-) + self._to_fetch.task_done() + if not url: + #logger.debug('Sentinel seen, quitting.') + break + + def get_page(self, url): + """ + Get the HTML for an URL, possibly from an in-memory cache. + + XXX TODO Note: this cache is never actually cleared. It's assumed that + the data won't get stale over the lifetime of a locator instance (not + necessarily true for the default_locator). + """ + # http://peak.telecommunity.com/DevCenter/EasyInstall#package-index-api + scheme, netloc, path, _, _, _ = urlparse(url) + if scheme == 'file' and os.path.isdir(url2pathname(path)): + url = urljoin(ensure_slash(url), 'index.html') + + if url in self._page_cache: + result = self._page_cache[url] + logger.debug('Returning %s from cache: %s', url, result) + else: + host = netloc.split(':', 1)[0] + result = None + if host in self._bad_hosts: + logger.debug('Skipping %s due to bad host %s', url, host) + else: + req = Request(url, headers={'Accept-encoding': 'identity'}) + try: + logger.debug('Fetching %s', url) + resp = self.opener.open(req, timeout=self.timeout) + logger.debug('Fetched %s', url) + headers = resp.info() + content_type = headers.get('Content-Type', '') + if HTML_CONTENT_TYPE.match(content_type): + final_url = resp.geturl() + data = resp.read() + encoding = headers.get('Content-Encoding') + if encoding: + decoder = self.decoders[encoding] # fail if not found + data = decoder(data) + encoding = 'utf-8' + m = CHARSET.search(content_type) + if m: + encoding = m.group(1) + try: + data = data.decode(encoding) + except UnicodeError: # pragma: no cover + data = data.decode('latin-1') # fallback + result = Page(data, final_url) + self._page_cache[final_url] = result + except HTTPError as e: + if e.code != 404: + logger.exception('Fetch failed: %s: %s', url, e) + except URLError as e: # pragma: no cover + logger.exception('Fetch failed: %s: %s', url, e) + with self._lock: + self._bad_hosts.add(host) + except Exception as e: # pragma: no cover + logger.exception('Fetch failed: %s: %s', url, e) + finally: + self._page_cache[url] = result # even if None (failure) + return result + + _distname_re = re.compile('<a href=[^>]*>([^<]+)<') + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + page = self.get_page(self.base_url) + if not page: + raise DistlibException('Unable to get %s' % self.base_url) + for match in self._distname_re.finditer(page.data): + result.add(match.group(1)) + return result + +class DirectoryLocator(Locator): + """ + This class locates distributions in a directory tree. + """ + + def __init__(self, path, **kwargs): + """ + Initialise an instance. + :param path: The root of the directory tree to search. + :param kwargs: Passed to the superclass constructor, + except for: + * recursive - if True (the default), subdirectories are + recursed into. If False, only the top-level directory + is searched, + """ + self.recursive = kwargs.pop('recursive', True) + super(DirectoryLocator, self).__init__(**kwargs) + path = os.path.abspath(path) + if not os.path.isdir(path): # pragma: no cover + raise DistlibException('Not a directory: %r' % path) + self.base_dir = path + + def should_include(self, filename, parent): + """ + Should a filename be considered as a candidate for a distribution + archive? As well as the filename, the directory which contains it + is provided, though not used by the current implementation. + """ + return filename.endswith(self.downloadable_extensions) + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + for root, dirs, files in os.walk(self.base_dir): + for fn in files: + if self.should_include(fn, root): + fn = os.path.join(root, fn) + url = urlunparse(('file', '', + pathname2url(os.path.abspath(fn)), + '', '', '')) + info = self.convert_url_to_download_info(url, name) + if info: + self._update_version_data(result, info) + if not self.recursive: + break + return result + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + for root, dirs, files in os.walk(self.base_dir): + for fn in files: + if self.should_include(fn, root): + fn = os.path.join(root, fn) + url = urlunparse(('file', '', + pathname2url(os.path.abspath(fn)), + '', '', '')) + info = self.convert_url_to_download_info(url, None) + if info: + result.add(info['name']) + if not self.recursive: + break + return result + +class JSONLocator(Locator): + """ + This locator uses special extended metadata (not available on PyPI) and is + the basis of performant dependency resolution in distlib. Other locators + require archive downloads before dependencies can be determined! As you + might imagine, that can be slow. + """ + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Not available from this locator') + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + data = get_project_data(name) + if data: + for info in data.get('files', []): + if info['ptype'] != 'sdist' or info['pyversion'] != 'source': + continue + # We don't store summary in project metadata as it makes + # the data bigger for no benefit during dependency + # resolution + dist = make_dist(data['name'], info['version'], + summary=data.get('summary', + 'Placeholder for summary'), + scheme=self.scheme) + md = dist.metadata + md.source_url = info['url'] + # TODO SHA256 digest + if 'digest' in info and info['digest']: + dist.digest = ('md5', info['digest']) + md.dependencies = info.get('requirements', {}) + dist.exports = info.get('exports', {}) + result[dist.version] = dist + result['urls'].setdefault(dist.version, set()).add(info['url']) + return result + +class DistPathLocator(Locator): + """ + This locator finds installed distributions in a path. It can be useful for + adding to an :class:`AggregatingLocator`. + """ + def __init__(self, distpath, **kwargs): + """ + Initialise an instance. + + :param distpath: A :class:`DistributionPath` instance to search. + """ + super(DistPathLocator, self).__init__(**kwargs) + assert isinstance(distpath, DistributionPath) + self.distpath = distpath + + def _get_project(self, name): + dist = self.distpath.get_distribution(name) + if dist is None: + result = {'urls': {}, 'digests': {}} + else: + result = { + dist.version: dist, + 'urls': {dist.version: set([dist.source_url])}, + 'digests': {dist.version: set([None])} + } + return result + + +class AggregatingLocator(Locator): + """ + This class allows you to chain and/or merge a list of locators. + """ + def __init__(self, *locators, **kwargs): + """ + Initialise an instance. + + :param locators: The list of locators to search. + :param kwargs: Passed to the superclass constructor, + except for: + * merge - if False (the default), the first successful + search from any of the locators is returned. If True, + the results from all locators are merged (this can be + slow). + """ + self.merge = kwargs.pop('merge', False) + self.locators = locators + super(AggregatingLocator, self).__init__(**kwargs) + + def clear_cache(self): + super(AggregatingLocator, self).clear_cache() + for locator in self.locators: + locator.clear_cache() + + def _set_scheme(self, value): + self._scheme = value + for locator in self.locators: + locator.scheme = value + + scheme = property(Locator.scheme.fget, _set_scheme) + + def _get_project(self, name): + result = {} + for locator in self.locators: + d = locator.get_project(name) + if d: + if self.merge: + files = result.get('urls', {}) + digests = result.get('digests', {}) + # next line could overwrite result['urls'], result['digests'] + result.update(d) + df = result.get('urls') + if files and df: + for k, v in files.items(): + if k in df: + df[k] |= v + else: + df[k] = v + dd = result.get('digests') + if digests and dd: + dd.update(digests) + else: + # See issue #18. If any dists are found and we're looking + # for specific constraints, we only return something if + # a match is found. For example, if a DirectoryLocator + # returns just foo (1.0) while we're looking for + # foo (>= 2.0), we'll pretend there was nothing there so + # that subsequent locators can be queried. Otherwise we + # would just return foo (1.0) which would then lead to a + # failure to find foo (>= 2.0), because other locators + # weren't searched. Note that this only matters when + # merge=False. + if self.matcher is None: + found = True + else: + found = False + for k in d: + if self.matcher.match(k): + found = True + break + if found: + result = d + break + return result + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + for locator in self.locators: + try: + result |= locator.get_distribution_names() + except NotImplementedError: + pass + return result + + +# We use a legacy scheme simply because most of the dists on PyPI use legacy +# versions which don't conform to PEP 426 / PEP 440. +default_locator = AggregatingLocator( + JSONLocator(), + SimpleScrapingLocator('https://pypi.python.org/simple/', + timeout=3.0), + scheme='legacy') + +locate = default_locator.locate + +NAME_VERSION_RE = re.compile(r'(?P<name>[\w-]+)\s*' + r'\(\s*(==\s*)?(?P<ver>[^)]+)\)$') + +class DependencyFinder(object): + """ + Locate dependencies for distributions. + """ + + def __init__(self, locator=None): + """ + Initialise an instance, using the specified locator + to locate distributions. + """ + self.locator = locator or default_locator + self.scheme = get_scheme(self.locator.scheme) + + def add_distribution(self, dist): + """ + Add a distribution to the finder. This will update internal information + about who provides what. + :param dist: The distribution to add. + """ + logger.debug('adding distribution %s', dist) + name = dist.key + self.dists_by_name[name] = dist + self.dists[(name, dist.version)] = dist + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Add to provided: %s, %s, %s', name, version, dist) + self.provided.setdefault(name, set()).add((version, dist)) + + def remove_distribution(self, dist): + """ + Remove a distribution from the finder. This will update internal + information about who provides what. + :param dist: The distribution to remove. + """ + logger.debug('removing distribution %s', dist) + name = dist.key + del self.dists_by_name[name] + del self.dists[(name, dist.version)] + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Remove from provided: %s, %s, %s', name, version, dist) + s = self.provided[name] + s.remove((version, dist)) + if not s: + del self.provided[name] + + def get_matcher(self, reqt): + """ + Get a version matcher for a requirement. + :param reqt: The requirement + :type reqt: str + :return: A version matcher (an instance of + :class:`distlib.version.Matcher`). + """ + try: + matcher = self.scheme.matcher(reqt) + except UnsupportedVersionError: # pragma: no cover + # XXX compat-mode if cannot read the version + name = reqt.split()[0] + matcher = self.scheme.matcher(name) + return matcher + + def find_providers(self, reqt): + """ + Find the distributions which can fulfill a requirement. + + :param reqt: The requirement. + :type reqt: str + :return: A set of distribution which can fulfill the requirement. + """ + matcher = self.get_matcher(reqt) + name = matcher.key # case-insensitive + result = set() + provided = self.provided + if name in provided: + for version, provider in provided[name]: + try: + match = matcher.match(version) + except UnsupportedVersionError: + match = False + + if match: + result.add(provider) + break + return result + + def try_to_replace(self, provider, other, problems): + """ + Attempt to replace one provider with another. This is typically used + when resolving dependencies from multiple sources, e.g. A requires + (B >= 1.0) while C requires (B >= 1.1). + + For successful replacement, ``provider`` must meet all the requirements + which ``other`` fulfills. + + :param provider: The provider we are trying to replace with. + :param other: The provider we're trying to replace. + :param problems: If False is returned, this will contain what + problems prevented replacement. This is currently + a tuple of the literal string 'cantreplace', + ``provider``, ``other`` and the set of requirements + that ``provider`` couldn't fulfill. + :return: True if we can replace ``other`` with ``provider``, else + False. + """ + rlist = self.reqts[other] + unmatched = set() + for s in rlist: + matcher = self.get_matcher(s) + if not matcher.match(provider.version): + unmatched.add(s) + if unmatched: + # can't replace other with provider + problems.add(('cantreplace', provider, other, + frozenset(unmatched))) + result = False + else: + # can replace other with provider + self.remove_distribution(other) + del self.reqts[other] + for s in rlist: + self.reqts.setdefault(provider, set()).add(s) + self.add_distribution(provider) + result = True + return result + + def find(self, requirement, meta_extras=None, prereleases=False): + """ + Find a distribution and all distributions it depends on. + + :param requirement: The requirement specifying the distribution to + find, or a Distribution instance. + :param meta_extras: A list of meta extras such as :test:, :build: and + so on. + :param prereleases: If ``True``, allow pre-release versions to be + returned - otherwise, don't return prereleases + unless they're all that's available. + + Return a set of :class:`Distribution` instances and a set of + problems. + + The distributions returned should be such that they have the + :attr:`required` attribute set to ``True`` if they were + from the ``requirement`` passed to ``find()``, and they have the + :attr:`build_time_dependency` attribute set to ``True`` unless they + are post-installation dependencies of the ``requirement``. + + The problems should be a tuple consisting of the string + ``'unsatisfied'`` and the requirement which couldn't be satisfied + by any distribution known to the locator. + """ + + self.provided = {} + self.dists = {} + self.dists_by_name = {} + self.reqts = {} + + meta_extras = set(meta_extras or []) + if ':*:' in meta_extras: + meta_extras.remove(':*:') + # :meta: and :run: are implicitly included + meta_extras |= set([':test:', ':build:', ':dev:']) + + if isinstance(requirement, Distribution): + dist = odist = requirement + logger.debug('passed %s as requirement', odist) + else: + dist = odist = self.locator.locate(requirement, + prereleases=prereleases) + if dist is None: + raise DistlibException('Unable to locate %r' % requirement) + logger.debug('located %s', odist) + dist.requested = True + problems = set() + todo = set([dist]) + install_dists = set([odist]) + while todo: + dist = todo.pop() + name = dist.key # case-insensitive + if name not in self.dists_by_name: + self.add_distribution(dist) + else: + #import pdb; pdb.set_trace() + other = self.dists_by_name[name] + if other != dist: + self.try_to_replace(dist, other, problems) + + ireqts = dist.run_requires | dist.meta_requires + sreqts = dist.build_requires + ereqts = set() + if dist in install_dists: + for key in ('test', 'build', 'dev'): + e = ':%s:' % key + if e in meta_extras: + ereqts |= getattr(dist, '%s_requires' % key) + all_reqts = ireqts | sreqts | ereqts + for r in all_reqts: + providers = self.find_providers(r) + if not providers: + logger.debug('No providers found for %r', r) + provider = self.locator.locate(r, prereleases=prereleases) + # If no provider is found and we didn't consider + # prereleases, consider them now. + if provider is None and not prereleases: + provider = self.locator.locate(r, prereleases=True) + if provider is None: + logger.debug('Cannot satisfy %r', r) + problems.add(('unsatisfied', r)) + else: + n, v = provider.key, provider.version + if (n, v) not in self.dists: + todo.add(provider) + providers.add(provider) + if r in ireqts and dist in install_dists: + install_dists.add(provider) + logger.debug('Adding %s to install_dists', + provider.name_and_version) + for p in providers: + name = p.key + if name not in self.dists_by_name: + self.reqts.setdefault(p, set()).add(r) + else: + other = self.dists_by_name[name] + if other != p: + # see if other can be replaced by p + self.try_to_replace(p, other, problems) + + dists = set(self.dists.values()) + for dist in dists: + dist.build_time_dependency = dist not in install_dists + if dist.build_time_dependency: + logger.debug('%s is a build-time dependency only.', + dist.name_and_version) + logger.debug('find done for %s', odist) + return dists, problems diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/manifest.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/manifest.py new file mode 100644 index 0000000..9f03364 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/manifest.py @@ -0,0 +1,393 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +""" +Class representing the list of files in a distribution. + +Equivalent to distutils.filelist, but fixes some problems. +""" +import fnmatch +import logging +import os +import re +import sys + +from . import DistlibException +from .compat import fsdecode +from .util import convert_path + + +__all__ = ['Manifest'] + +logger = logging.getLogger(__name__) + +# a \ followed by some spaces + EOL +_COLLAPSE_PATTERN = re.compile('\\\w*\n', re.M) +_COMMENTED_LINE = re.compile('#.*?(?=\n)|\n(?=$)', re.M | re.S) + +# +# Due to the different results returned by fnmatch.translate, we need +# to do slightly different processing for Python 2.7 and 3.2 ... this needed +# to be brought in for Python 3.6 onwards. +# +_PYTHON_VERSION = sys.version_info[:2] + +class Manifest(object): + """A list of files built by on exploring the filesystem and filtered by + applying various patterns to what we find there. + """ + + def __init__(self, base=None): + """ + Initialise an instance. + + :param base: The base directory to explore under. + """ + self.base = os.path.abspath(os.path.normpath(base or os.getcwd())) + self.prefix = self.base + os.sep + self.allfiles = None + self.files = set() + + # + # Public API + # + + def findall(self): + """Find all files under the base and set ``allfiles`` to the absolute + pathnames of files found. + """ + from stat import S_ISREG, S_ISDIR, S_ISLNK + + self.allfiles = allfiles = [] + root = self.base + stack = [root] + pop = stack.pop + push = stack.append + + while stack: + root = pop() + names = os.listdir(root) + + for name in names: + fullname = os.path.join(root, name) + + # Avoid excess stat calls -- just one will do, thank you! + stat = os.stat(fullname) + mode = stat.st_mode + if S_ISREG(mode): + allfiles.append(fsdecode(fullname)) + elif S_ISDIR(mode) and not S_ISLNK(mode): + push(fullname) + + def add(self, item): + """ + Add a file to the manifest. + + :param item: The pathname to add. This can be relative to the base. + """ + if not item.startswith(self.prefix): + item = os.path.join(self.base, item) + self.files.add(os.path.normpath(item)) + + def add_many(self, items): + """ + Add a list of files to the manifest. + + :param items: The pathnames to add. These can be relative to the base. + """ + for item in items: + self.add(item) + + def sorted(self, wantdirs=False): + """ + Return sorted files in directory order + """ + + def add_dir(dirs, d): + dirs.add(d) + logger.debug('add_dir added %s', d) + if d != self.base: + parent, _ = os.path.split(d) + assert parent not in ('', '/') + add_dir(dirs, parent) + + result = set(self.files) # make a copy! + if wantdirs: + dirs = set() + for f in result: + add_dir(dirs, os.path.dirname(f)) + result |= dirs + return [os.path.join(*path_tuple) for path_tuple in + sorted(os.path.split(path) for path in result)] + + def clear(self): + """Clear all collected files.""" + self.files = set() + self.allfiles = [] + + def process_directive(self, directive): + """ + Process a directive which either adds some files from ``allfiles`` to + ``files``, or removes some files from ``files``. + + :param directive: The directive to process. This should be in a format + compatible with distutils ``MANIFEST.in`` files: + + http://docs.python.org/distutils/sourcedist.html#commands + """ + # Parse the line: split it up, make sure the right number of words + # is there, and return the relevant words. 'action' is always + # defined: it's the first word of the line. Which of the other + # three are defined depends on the action; it'll be either + # patterns, (dir and patterns), or (dirpattern). + action, patterns, thedir, dirpattern = self._parse_directive(directive) + + # OK, now we know that the action is valid and we have the + # right number of words on the line for that action -- so we + # can proceed with minimal error-checking. + if action == 'include': + for pattern in patterns: + if not self._include_pattern(pattern, anchor=True): + logger.warning('no files found matching %r', pattern) + + elif action == 'exclude': + for pattern in patterns: + found = self._exclude_pattern(pattern, anchor=True) + #if not found: + # logger.warning('no previously-included files ' + # 'found matching %r', pattern) + + elif action == 'global-include': + for pattern in patterns: + if not self._include_pattern(pattern, anchor=False): + logger.warning('no files found matching %r ' + 'anywhere in distribution', pattern) + + elif action == 'global-exclude': + for pattern in patterns: + found = self._exclude_pattern(pattern, anchor=False) + #if not found: + # logger.warning('no previously-included files ' + # 'matching %r found anywhere in ' + # 'distribution', pattern) + + elif action == 'recursive-include': + for pattern in patterns: + if not self._include_pattern(pattern, prefix=thedir): + logger.warning('no files found matching %r ' + 'under directory %r', pattern, thedir) + + elif action == 'recursive-exclude': + for pattern in patterns: + found = self._exclude_pattern(pattern, prefix=thedir) + #if not found: + # logger.warning('no previously-included files ' + # 'matching %r found under directory %r', + # pattern, thedir) + + elif action == 'graft': + if not self._include_pattern(None, prefix=dirpattern): + logger.warning('no directories found matching %r', + dirpattern) + + elif action == 'prune': + if not self._exclude_pattern(None, prefix=dirpattern): + logger.warning('no previously-included directories found ' + 'matching %r', dirpattern) + else: # pragma: no cover + # This should never happen, as it should be caught in + # _parse_template_line + raise DistlibException( + 'invalid action %r' % action) + + # + # Private API + # + + def _parse_directive(self, directive): + """ + Validate a directive. + :param directive: The directive to validate. + :return: A tuple of action, patterns, thedir, dir_patterns + """ + words = directive.split() + if len(words) == 1 and words[0] not in ('include', 'exclude', + 'global-include', + 'global-exclude', + 'recursive-include', + 'recursive-exclude', + 'graft', 'prune'): + # no action given, let's use the default 'include' + words.insert(0, 'include') + + action = words[0] + patterns = thedir = dir_pattern = None + + if action in ('include', 'exclude', + 'global-include', 'global-exclude'): + if len(words) < 2: + raise DistlibException( + '%r expects <pattern1> <pattern2> ...' % action) + + patterns = [convert_path(word) for word in words[1:]] + + elif action in ('recursive-include', 'recursive-exclude'): + if len(words) < 3: + raise DistlibException( + '%r expects <dir> <pattern1> <pattern2> ...' % action) + + thedir = convert_path(words[1]) + patterns = [convert_path(word) for word in words[2:]] + + elif action in ('graft', 'prune'): + if len(words) != 2: + raise DistlibException( + '%r expects a single <dir_pattern>' % action) + + dir_pattern = convert_path(words[1]) + + else: + raise DistlibException('unknown action %r' % action) + + return action, patterns, thedir, dir_pattern + + def _include_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Select strings (presumably filenames) from 'self.files' that + match 'pattern', a Unix-style wildcard (glob) pattern. + + Patterns are not quite the same as implemented by the 'fnmatch' + module: '*' and '?' match non-special characters, where "special" + is platform-dependent: slash on Unix; colon, slash, and backslash on + DOS/Windows; and colon on Mac OS. + + If 'anchor' is true (the default), then the pattern match is more + stringent: "*.py" will match "foo.py" but not "foo/bar.py". If + 'anchor' is false, both of these will match. + + If 'prefix' is supplied, then only filenames starting with 'prefix' + (itself a pattern) and ending with 'pattern', with anything in between + them, will match. 'anchor' is ignored in this case. + + If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and + 'pattern' is assumed to be either a string containing a regex or a + regex object -- no translation is done, the regex is just compiled + and used as-is. + + Selected strings will be added to self.files. + + Return True if files are found. + """ + # XXX docstring lying about what the special chars are? + found = False + pattern_re = self._translate_pattern(pattern, anchor, prefix, is_regex) + + # delayed loading of allfiles list + if self.allfiles is None: + self.findall() + + for name in self.allfiles: + if pattern_re.search(name): + self.files.add(name) + found = True + return found + + def _exclude_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Remove strings (presumably filenames) from 'files' that match + 'pattern'. + + Other parameters are the same as for 'include_pattern()', above. + The list 'self.files' is modified in place. Return True if files are + found. + + This API is public to allow e.g. exclusion of SCM subdirs, e.g. when + packaging source distributions + """ + found = False + pattern_re = self._translate_pattern(pattern, anchor, prefix, is_regex) + for f in list(self.files): + if pattern_re.search(f): + self.files.remove(f) + found = True + return found + + def _translate_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Translate a shell-like wildcard pattern to a compiled regular + expression. + + Return the compiled regex. If 'is_regex' true, + then 'pattern' is directly compiled to a regex (if it's a string) + or just returned as-is (assumes it's a regex object). + """ + if is_regex: + if isinstance(pattern, str): + return re.compile(pattern) + else: + return pattern + + if _PYTHON_VERSION > (3, 2): + # ditch start and end characters + start, _, end = self._glob_to_re('_').partition('_') + + if pattern: + pattern_re = self._glob_to_re(pattern) + if _PYTHON_VERSION > (3, 2): + assert pattern_re.startswith(start) and pattern_re.endswith(end) + else: + pattern_re = '' + + base = re.escape(os.path.join(self.base, '')) + if prefix is not None: + # ditch end of pattern character + if _PYTHON_VERSION <= (3, 2): + empty_pattern = self._glob_to_re('') + prefix_re = self._glob_to_re(prefix)[:-len(empty_pattern)] + else: + prefix_re = self._glob_to_re(prefix) + assert prefix_re.startswith(start) and prefix_re.endswith(end) + prefix_re = prefix_re[len(start): len(prefix_re) - len(end)] + sep = os.sep + if os.sep == '\\': + sep = r'\\' + if _PYTHON_VERSION <= (3, 2): + pattern_re = '^' + base + sep.join((prefix_re, + '.*' + pattern_re)) + else: + pattern_re = pattern_re[len(start): len(pattern_re) - len(end)] + pattern_re = r'%s%s%s%s.*%s%s' % (start, base, prefix_re, sep, + pattern_re, end) + else: # no prefix -- respect anchor flag + if anchor: + if _PYTHON_VERSION <= (3, 2): + pattern_re = '^' + base + pattern_re + else: + pattern_re = r'%s%s%s' % (start, base, pattern_re[len(start):]) + + return re.compile(pattern_re) + + def _glob_to_re(self, pattern): + """Translate a shell-like glob pattern to a regular expression. + + Return a string containing the regex. Differs from + 'fnmatch.translate()' in that '*' does not match "special characters" + (which are platform-specific). + """ + pattern_re = fnmatch.translate(pattern) + + # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which + # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, + # and by extension they shouldn't match such "special characters" under + # any OS. So change all non-escaped dots in the RE to match any + # character except the special characters (currently: just os.sep). + sep = os.sep + if os.sep == '\\': + # we're using a regex to manipulate a regex, so we need + # to escape the backslash twice + sep = r'\\\\' + escaped = r'\1[^%s]' % sep + pattern_re = re.sub(r'((?<!\\)(\\\\)*)\.', escaped, pattern_re) + return pattern_re diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/markers.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/markers.py new file mode 100644 index 0000000..afb19c6 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/markers.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Parser for the environment markers micro-language defined in PEP 345.""" + +import ast +import os +import sys +import platform + +from .compat import python_implementation, string_types +from .util import in_venv + +__all__ = ['interpret'] + + +class Evaluator(object): + """ + A limited evaluator for Python expressions. + """ + + operators = { + 'eq': lambda x, y: x == y, + 'gt': lambda x, y: x > y, + 'gte': lambda x, y: x >= y, + 'in': lambda x, y: x in y, + 'lt': lambda x, y: x < y, + 'lte': lambda x, y: x <= y, + 'not': lambda x: not x, + 'noteq': lambda x, y: x != y, + 'notin': lambda x, y: x not in y, + } + + allowed_values = { + 'sys_platform': sys.platform, + 'python_version': '%s.%s' % sys.version_info[:2], + # parsing sys.platform is not reliable, but there is no other + # way to get e.g. 2.7.2+, and the PEP is defined with sys.version + 'python_full_version': sys.version.split(' ', 1)[0], + 'os_name': os.name, + 'platform_in_venv': str(in_venv()), + 'platform_release': platform.release(), + 'platform_version': platform.version(), + 'platform_machine': platform.machine(), + 'platform_python_implementation': python_implementation(), + } + + def __init__(self, context=None): + """ + Initialise an instance. + + :param context: If specified, names are looked up in this mapping. + """ + self.context = context or {} + self.source = None + + def get_fragment(self, offset): + """ + Get the part of the source which is causing a problem. + """ + fragment_len = 10 + s = '%r' % (self.source[offset:offset + fragment_len]) + if offset + fragment_len < len(self.source): + s += '...' + return s + + def get_handler(self, node_type): + """ + Get a handler for the specified AST node type. + """ + return getattr(self, 'do_%s' % node_type, None) + + def evaluate(self, node, filename=None): + """ + Evaluate a source string or node, using ``filename`` when + displaying errors. + """ + if isinstance(node, string_types): + self.source = node + kwargs = {'mode': 'eval'} + if filename: + kwargs['filename'] = filename + try: + node = ast.parse(node, **kwargs) + except SyntaxError as e: + s = self.get_fragment(e.offset) + raise SyntaxError('syntax error %s' % s) + node_type = node.__class__.__name__.lower() + handler = self.get_handler(node_type) + if handler is None: + if self.source is None: + s = '(source not available)' + else: + s = self.get_fragment(node.col_offset) + raise SyntaxError("don't know how to evaluate %r %s" % ( + node_type, s)) + return handler(node) + + def get_attr_key(self, node): + assert isinstance(node, ast.Attribute), 'attribute node expected' + return '%s.%s' % (node.value.id, node.attr) + + def do_attribute(self, node): + if not isinstance(node.value, ast.Name): + valid = False + else: + key = self.get_attr_key(node) + valid = key in self.context or key in self.allowed_values + if not valid: + raise SyntaxError('invalid expression: %s' % key) + if key in self.context: + result = self.context[key] + else: + result = self.allowed_values[key] + return result + + def do_boolop(self, node): + result = self.evaluate(node.values[0]) + is_or = node.op.__class__ is ast.Or + is_and = node.op.__class__ is ast.And + assert is_or or is_and + if (is_and and result) or (is_or and not result): + for n in node.values[1:]: + result = self.evaluate(n) + if (is_or and result) or (is_and and not result): + break + return result + + def do_compare(self, node): + def sanity_check(lhsnode, rhsnode): + valid = True + if isinstance(lhsnode, ast.Str) and isinstance(rhsnode, ast.Str): + valid = False + #elif (isinstance(lhsnode, ast.Attribute) + # and isinstance(rhsnode, ast.Attribute)): + # klhs = self.get_attr_key(lhsnode) + # krhs = self.get_attr_key(rhsnode) + # valid = klhs != krhs + if not valid: + s = self.get_fragment(node.col_offset) + raise SyntaxError('Invalid comparison: %s' % s) + + lhsnode = node.left + lhs = self.evaluate(lhsnode) + result = True + for op, rhsnode in zip(node.ops, node.comparators): + sanity_check(lhsnode, rhsnode) + op = op.__class__.__name__.lower() + if op not in self.operators: + raise SyntaxError('unsupported operation: %r' % op) + rhs = self.evaluate(rhsnode) + result = self.operators[op](lhs, rhs) + if not result: + break + lhs = rhs + lhsnode = rhsnode + return result + + def do_expression(self, node): + return self.evaluate(node.body) + + def do_name(self, node): + valid = False + if node.id in self.context: + valid = True + result = self.context[node.id] + elif node.id in self.allowed_values: + valid = True + result = self.allowed_values[node.id] + if not valid: + raise SyntaxError('invalid expression: %s' % node.id) + return result + + def do_str(self, node): + return node.s + + +def interpret(marker, execution_context=None): + """ + Interpret a marker and return a result depending on environment. + + :param marker: The marker to interpret. + :type marker: str + :param execution_context: The context used for name lookup. + :type execution_context: mapping + """ + return Evaluator(execution_context).evaluate(marker.strip()) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/metadata.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/metadata.py new file mode 100644 index 0000000..75bfd68 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/metadata.py @@ -0,0 +1,1068 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Implementation of the Metadata for Python packages PEPs. + +Supports all metadata formats (1.0, 1.1, 1.2, and 2.0 experimental). +""" +from __future__ import unicode_literals + +import codecs +from email import message_from_file +import json +import logging +import re + + +from . import DistlibException, __version__ +from .compat import StringIO, string_types, text_type +from .markers import interpret +from .util import extract_by_key, get_extras +from .version import get_scheme, PEP440_VERSION_RE + +logger = logging.getLogger(__name__) + + +class MetadataMissingError(DistlibException): + """A required metadata is missing""" + + +class MetadataConflictError(DistlibException): + """Attempt to read or write metadata fields that are conflictual.""" + + +class MetadataUnrecognizedVersionError(DistlibException): + """Unknown metadata version number.""" + + +class MetadataInvalidError(DistlibException): + """A metadata value is invalid""" + +# public API of this module +__all__ = ['Metadata', 'PKG_INFO_ENCODING', 'PKG_INFO_PREFERRED_VERSION'] + +# Encoding used for the PKG-INFO files +PKG_INFO_ENCODING = 'utf-8' + +# preferred version. Hopefully will be changed +# to 1.2 once PEP 345 is supported everywhere +PKG_INFO_PREFERRED_VERSION = '1.1' + +_LINE_PREFIX_1_2 = re.compile('\n \|') +_LINE_PREFIX_PRE_1_2 = re.compile('\n ') +_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'License') + +_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'License', 'Classifier', 'Download-URL', 'Obsoletes', + 'Provides', 'Requires') + +_314_MARKERS = ('Obsoletes', 'Provides', 'Requires', 'Classifier', + 'Download-URL') + +_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'Maintainer', 'Maintainer-email', 'License', + 'Classifier', 'Download-URL', 'Obsoletes-Dist', + 'Project-URL', 'Provides-Dist', 'Requires-Dist', + 'Requires-Python', 'Requires-External') + +_345_MARKERS = ('Provides-Dist', 'Requires-Dist', 'Requires-Python', + 'Obsoletes-Dist', 'Requires-External', 'Maintainer', + 'Maintainer-email', 'Project-URL') + +_426_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'Maintainer', 'Maintainer-email', 'License', + 'Classifier', 'Download-URL', 'Obsoletes-Dist', + 'Project-URL', 'Provides-Dist', 'Requires-Dist', + 'Requires-Python', 'Requires-External', 'Private-Version', + 'Obsoleted-By', 'Setup-Requires-Dist', 'Extension', + 'Provides-Extra') + +_426_MARKERS = ('Private-Version', 'Provides-Extra', 'Obsoleted-By', + 'Setup-Requires-Dist', 'Extension') + +_ALL_FIELDS = set() +_ALL_FIELDS.update(_241_FIELDS) +_ALL_FIELDS.update(_314_FIELDS) +_ALL_FIELDS.update(_345_FIELDS) +_ALL_FIELDS.update(_426_FIELDS) + +EXTRA_RE = re.compile(r'''extra\s*==\s*("([^"]+)"|'([^']+)')''') + + +def _version2fieldlist(version): + if version == '1.0': + return _241_FIELDS + elif version == '1.1': + return _314_FIELDS + elif version == '1.2': + return _345_FIELDS + elif version == '2.0': + return _426_FIELDS + raise MetadataUnrecognizedVersionError(version) + + +def _best_version(fields): + """Detect the best version depending on the fields used.""" + def _has_marker(keys, markers): + for marker in markers: + if marker in keys: + return True + return False + + keys = [] + for key, value in fields.items(): + if value in ([], 'UNKNOWN', None): + continue + keys.append(key) + + possible_versions = ['1.0', '1.1', '1.2', '2.0'] + + # first let's try to see if a field is not part of one of the version + for key in keys: + if key not in _241_FIELDS and '1.0' in possible_versions: + possible_versions.remove('1.0') + if key not in _314_FIELDS and '1.1' in possible_versions: + possible_versions.remove('1.1') + if key not in _345_FIELDS and '1.2' in possible_versions: + possible_versions.remove('1.2') + if key not in _426_FIELDS and '2.0' in possible_versions: + possible_versions.remove('2.0') + + # possible_version contains qualified versions + if len(possible_versions) == 1: + return possible_versions[0] # found ! + elif len(possible_versions) == 0: + raise MetadataConflictError('Unknown metadata set') + + # let's see if one unique marker is found + is_1_1 = '1.1' in possible_versions and _has_marker(keys, _314_MARKERS) + is_1_2 = '1.2' in possible_versions and _has_marker(keys, _345_MARKERS) + is_2_0 = '2.0' in possible_versions and _has_marker(keys, _426_MARKERS) + if int(is_1_1) + int(is_1_2) + int(is_2_0) > 1: + raise MetadataConflictError('You used incompatible 1.1/1.2/2.0 fields') + + # we have the choice, 1.0, or 1.2, or 2.0 + # - 1.0 has a broken Summary field but works with all tools + # - 1.1 is to avoid + # - 1.2 fixes Summary but has little adoption + # - 2.0 adds more features and is very new + if not is_1_1 and not is_1_2 and not is_2_0: + # we couldn't find any specific marker + if PKG_INFO_PREFERRED_VERSION in possible_versions: + return PKG_INFO_PREFERRED_VERSION + if is_1_1: + return '1.1' + if is_1_2: + return '1.2' + + return '2.0' + +_ATTR2FIELD = { + 'metadata_version': 'Metadata-Version', + 'name': 'Name', + 'version': 'Version', + 'platform': 'Platform', + 'supported_platform': 'Supported-Platform', + 'summary': 'Summary', + 'description': 'Description', + 'keywords': 'Keywords', + 'home_page': 'Home-page', + 'author': 'Author', + 'author_email': 'Author-email', + 'maintainer': 'Maintainer', + 'maintainer_email': 'Maintainer-email', + 'license': 'License', + 'classifier': 'Classifier', + 'download_url': 'Download-URL', + 'obsoletes_dist': 'Obsoletes-Dist', + 'provides_dist': 'Provides-Dist', + 'requires_dist': 'Requires-Dist', + 'setup_requires_dist': 'Setup-Requires-Dist', + 'requires_python': 'Requires-Python', + 'requires_external': 'Requires-External', + 'requires': 'Requires', + 'provides': 'Provides', + 'obsoletes': 'Obsoletes', + 'project_url': 'Project-URL', + 'private_version': 'Private-Version', + 'obsoleted_by': 'Obsoleted-By', + 'extension': 'Extension', + 'provides_extra': 'Provides-Extra', +} + +_PREDICATE_FIELDS = ('Requires-Dist', 'Obsoletes-Dist', 'Provides-Dist') +_VERSIONS_FIELDS = ('Requires-Python',) +_VERSION_FIELDS = ('Version',) +_LISTFIELDS = ('Platform', 'Classifier', 'Obsoletes', + 'Requires', 'Provides', 'Obsoletes-Dist', + 'Provides-Dist', 'Requires-Dist', 'Requires-External', + 'Project-URL', 'Supported-Platform', 'Setup-Requires-Dist', + 'Provides-Extra', 'Extension') +_LISTTUPLEFIELDS = ('Project-URL',) + +_ELEMENTSFIELD = ('Keywords',) + +_UNICODEFIELDS = ('Author', 'Maintainer', 'Summary', 'Description') + +_MISSING = object() + +_FILESAFE = re.compile('[^A-Za-z0-9.]+') + + +def _get_name_and_version(name, version, for_filename=False): + """Return the distribution name with version. + + If for_filename is true, return a filename-escaped form.""" + if for_filename: + # For both name and version any runs of non-alphanumeric or '.' + # characters are replaced with a single '-'. Additionally any + # spaces in the version string become '.' + name = _FILESAFE.sub('-', name) + version = _FILESAFE.sub('-', version.replace(' ', '.')) + return '%s-%s' % (name, version) + + +class LegacyMetadata(object): + """The legacy metadata of a release. + + Supports versions 1.0, 1.1 and 1.2 (auto-detected). You can + instantiate the class with one of these arguments (or none): + - *path*, the path to a metadata file + - *fileobj* give a file-like object with metadata as content + - *mapping* is a dict-like object + - *scheme* is a version scheme name + """ + # TODO document the mapping API and UNKNOWN default key + + def __init__(self, path=None, fileobj=None, mapping=None, + scheme='default'): + if [path, fileobj, mapping].count(None) < 2: + raise TypeError('path, fileobj and mapping are exclusive') + self._fields = {} + self.requires_files = [] + self._dependencies = None + self.scheme = scheme + if path is not None: + self.read(path) + elif fileobj is not None: + self.read_file(fileobj) + elif mapping is not None: + self.update(mapping) + self.set_metadata_version() + + def set_metadata_version(self): + self._fields['Metadata-Version'] = _best_version(self._fields) + + def _write_field(self, fileobj, name, value): + fileobj.write('%s: %s\n' % (name, value)) + + def __getitem__(self, name): + return self.get(name) + + def __setitem__(self, name, value): + return self.set(name, value) + + def __delitem__(self, name): + field_name = self._convert_name(name) + try: + del self._fields[field_name] + except KeyError: + raise KeyError(name) + + def __contains__(self, name): + return (name in self._fields or + self._convert_name(name) in self._fields) + + def _convert_name(self, name): + if name in _ALL_FIELDS: + return name + name = name.replace('-', '_').lower() + return _ATTR2FIELD.get(name, name) + + def _default_value(self, name): + if name in _LISTFIELDS or name in _ELEMENTSFIELD: + return [] + return 'UNKNOWN' + + def _remove_line_prefix(self, value): + if self.metadata_version in ('1.0', '1.1'): + return _LINE_PREFIX_PRE_1_2.sub('\n', value) + else: + return _LINE_PREFIX_1_2.sub('\n', value) + + def __getattr__(self, name): + if name in _ATTR2FIELD: + return self[name] + raise AttributeError(name) + + # + # Public API + # + +# dependencies = property(_get_dependencies, _set_dependencies) + + def get_fullname(self, filesafe=False): + """Return the distribution name with version. + + If filesafe is true, return a filename-escaped form.""" + return _get_name_and_version(self['Name'], self['Version'], filesafe) + + def is_field(self, name): + """return True if name is a valid metadata key""" + name = self._convert_name(name) + return name in _ALL_FIELDS + + def is_multi_field(self, name): + name = self._convert_name(name) + return name in _LISTFIELDS + + def read(self, filepath): + """Read the metadata values from a file path.""" + fp = codecs.open(filepath, 'r', encoding='utf-8') + try: + self.read_file(fp) + finally: + fp.close() + + def read_file(self, fileob): + """Read the metadata values from a file object.""" + msg = message_from_file(fileob) + self._fields['Metadata-Version'] = msg['metadata-version'] + + # When reading, get all the fields we can + for field in _ALL_FIELDS: + if field not in msg: + continue + if field in _LISTFIELDS: + # we can have multiple lines + values = msg.get_all(field) + if field in _LISTTUPLEFIELDS and values is not None: + values = [tuple(value.split(',')) for value in values] + self.set(field, values) + else: + # single line + value = msg[field] + if value is not None and value != 'UNKNOWN': + self.set(field, value) + self.set_metadata_version() + + def write(self, filepath, skip_unknown=False): + """Write the metadata fields to filepath.""" + fp = codecs.open(filepath, 'w', encoding='utf-8') + try: + self.write_file(fp, skip_unknown) + finally: + fp.close() + + def write_file(self, fileobject, skip_unknown=False): + """Write the PKG-INFO format data to a file object.""" + self.set_metadata_version() + + for field in _version2fieldlist(self['Metadata-Version']): + values = self.get(field) + if skip_unknown and values in ('UNKNOWN', [], ['UNKNOWN']): + continue + if field in _ELEMENTSFIELD: + self._write_field(fileobject, field, ','.join(values)) + continue + if field not in _LISTFIELDS: + if field == 'Description': + if self.metadata_version in ('1.0', '1.1'): + values = values.replace('\n', '\n ') + else: + values = values.replace('\n', '\n |') + values = [values] + + if field in _LISTTUPLEFIELDS: + values = [','.join(value) for value in values] + + for value in values: + self._write_field(fileobject, field, value) + + def update(self, other=None, **kwargs): + """Set metadata values from the given iterable `other` and kwargs. + + Behavior is like `dict.update`: If `other` has a ``keys`` method, + they are looped over and ``self[key]`` is assigned ``other[key]``. + Else, ``other`` is an iterable of ``(key, value)`` iterables. + + Keys that don't match a metadata field or that have an empty value are + dropped. + """ + def _set(key, value): + if key in _ATTR2FIELD and value: + self.set(self._convert_name(key), value) + + if not other: + # other is None or empty container + pass + elif hasattr(other, 'keys'): + for k in other.keys(): + _set(k, other[k]) + else: + for k, v in other: + _set(k, v) + + if kwargs: + for k, v in kwargs.items(): + _set(k, v) + + def set(self, name, value): + """Control then set a metadata field.""" + name = self._convert_name(name) + + if ((name in _ELEMENTSFIELD or name == 'Platform') and + not isinstance(value, (list, tuple))): + if isinstance(value, string_types): + value = [v.strip() for v in value.split(',')] + else: + value = [] + elif (name in _LISTFIELDS and + not isinstance(value, (list, tuple))): + if isinstance(value, string_types): + value = [value] + else: + value = [] + + if logger.isEnabledFor(logging.WARNING): + project_name = self['Name'] + + scheme = get_scheme(self.scheme) + if name in _PREDICATE_FIELDS and value is not None: + for v in value: + # check that the values are valid + if not scheme.is_valid_matcher(v.split(';')[0]): + logger.warning( + "'%s': '%s' is not valid (field '%s')", + project_name, v, name) + # FIXME this rejects UNKNOWN, is that right? + elif name in _VERSIONS_FIELDS and value is not None: + if not scheme.is_valid_constraint_list(value): + logger.warning("'%s': '%s' is not a valid version (field '%s')", + project_name, value, name) + elif name in _VERSION_FIELDS and value is not None: + if not scheme.is_valid_version(value): + logger.warning("'%s': '%s' is not a valid version (field '%s')", + project_name, value, name) + + if name in _UNICODEFIELDS: + if name == 'Description': + value = self._remove_line_prefix(value) + + self._fields[name] = value + + def get(self, name, default=_MISSING): + """Get a metadata field.""" + name = self._convert_name(name) + if name not in self._fields: + if default is _MISSING: + default = self._default_value(name) + return default + if name in _UNICODEFIELDS: + value = self._fields[name] + return value + elif name in _LISTFIELDS: + value = self._fields[name] + if value is None: + return [] + res = [] + for val in value: + if name not in _LISTTUPLEFIELDS: + res.append(val) + else: + # That's for Project-URL + res.append((val[0], val[1])) + return res + + elif name in _ELEMENTSFIELD: + value = self._fields[name] + if isinstance(value, string_types): + return value.split(',') + return self._fields[name] + + def check(self, strict=False): + """Check if the metadata is compliant. If strict is True then raise if + no Name or Version are provided""" + self.set_metadata_version() + + # XXX should check the versions (if the file was loaded) + missing, warnings = [], [] + + for attr in ('Name', 'Version'): # required by PEP 345 + if attr not in self: + missing.append(attr) + + if strict and missing != []: + msg = 'missing required metadata: %s' % ', '.join(missing) + raise MetadataMissingError(msg) + + for attr in ('Home-page', 'Author'): + if attr not in self: + missing.append(attr) + + # checking metadata 1.2 (XXX needs to check 1.1, 1.0) + if self['Metadata-Version'] != '1.2': + return missing, warnings + + scheme = get_scheme(self.scheme) + + def are_valid_constraints(value): + for v in value: + if not scheme.is_valid_matcher(v.split(';')[0]): + return False + return True + + for fields, controller in ((_PREDICATE_FIELDS, are_valid_constraints), + (_VERSIONS_FIELDS, + scheme.is_valid_constraint_list), + (_VERSION_FIELDS, + scheme.is_valid_version)): + for field in fields: + value = self.get(field, None) + if value is not None and not controller(value): + warnings.append("Wrong value for '%s': %s" % (field, value)) + + return missing, warnings + + def todict(self, skip_missing=False): + """Return fields as a dict. + + Field names will be converted to use the underscore-lowercase style + instead of hyphen-mixed case (i.e. home_page instead of Home-page). + """ + self.set_metadata_version() + + mapping_1_0 = ( + ('metadata_version', 'Metadata-Version'), + ('name', 'Name'), + ('version', 'Version'), + ('summary', 'Summary'), + ('home_page', 'Home-page'), + ('author', 'Author'), + ('author_email', 'Author-email'), + ('license', 'License'), + ('description', 'Description'), + ('keywords', 'Keywords'), + ('platform', 'Platform'), + ('classifiers', 'Classifier'), + ('download_url', 'Download-URL'), + ) + + data = {} + for key, field_name in mapping_1_0: + if not skip_missing or field_name in self._fields: + data[key] = self[field_name] + + if self['Metadata-Version'] == '1.2': + mapping_1_2 = ( + ('requires_dist', 'Requires-Dist'), + ('requires_python', 'Requires-Python'), + ('requires_external', 'Requires-External'), + ('provides_dist', 'Provides-Dist'), + ('obsoletes_dist', 'Obsoletes-Dist'), + ('project_url', 'Project-URL'), + ('maintainer', 'Maintainer'), + ('maintainer_email', 'Maintainer-email'), + ) + for key, field_name in mapping_1_2: + if not skip_missing or field_name in self._fields: + if key != 'project_url': + data[key] = self[field_name] + else: + data[key] = [','.join(u) for u in self[field_name]] + + elif self['Metadata-Version'] == '1.1': + mapping_1_1 = ( + ('provides', 'Provides'), + ('requires', 'Requires'), + ('obsoletes', 'Obsoletes'), + ) + for key, field_name in mapping_1_1: + if not skip_missing or field_name in self._fields: + data[key] = self[field_name] + + return data + + def add_requirements(self, requirements): + if self['Metadata-Version'] == '1.1': + # we can't have 1.1 metadata *and* Setuptools requires + for field in ('Obsoletes', 'Requires', 'Provides'): + if field in self: + del self[field] + self['Requires-Dist'] += requirements + + # Mapping API + # TODO could add iter* variants + + def keys(self): + return list(_version2fieldlist(self['Metadata-Version'])) + + def __iter__(self): + for key in self.keys(): + yield key + + def values(self): + return [self[key] for key in self.keys()] + + def items(self): + return [(key, self[key]) for key in self.keys()] + + def __repr__(self): + return '<%s %s %s>' % (self.__class__.__name__, self.name, + self.version) + + +METADATA_FILENAME = 'pydist.json' +WHEEL_METADATA_FILENAME = 'metadata.json' + + +class Metadata(object): + """ + The metadata of a release. This implementation uses 2.0 (JSON) + metadata where possible. If not possible, it wraps a LegacyMetadata + instance which handles the key-value metadata format. + """ + + METADATA_VERSION_MATCHER = re.compile('^\d+(\.\d+)*$') + + NAME_MATCHER = re.compile('^[0-9A-Z]([0-9A-Z_.-]*[0-9A-Z])?$', re.I) + + VERSION_MATCHER = PEP440_VERSION_RE + + SUMMARY_MATCHER = re.compile('.{1,2047}') + + METADATA_VERSION = '2.0' + + GENERATOR = 'distlib (%s)' % __version__ + + MANDATORY_KEYS = { + 'name': (), + 'version': (), + 'summary': ('legacy',), + } + + INDEX_KEYS = ('name version license summary description author ' + 'author_email keywords platform home_page classifiers ' + 'download_url') + + DEPENDENCY_KEYS = ('extras run_requires test_requires build_requires ' + 'dev_requires provides meta_requires obsoleted_by ' + 'supports_environments') + + SYNTAX_VALIDATORS = { + 'metadata_version': (METADATA_VERSION_MATCHER, ()), + 'name': (NAME_MATCHER, ('legacy',)), + 'version': (VERSION_MATCHER, ('legacy',)), + 'summary': (SUMMARY_MATCHER, ('legacy',)), + } + + __slots__ = ('_legacy', '_data', 'scheme') + + def __init__(self, path=None, fileobj=None, mapping=None, + scheme='default'): + if [path, fileobj, mapping].count(None) < 2: + raise TypeError('path, fileobj and mapping are exclusive') + self._legacy = None + self._data = None + self.scheme = scheme + #import pdb; pdb.set_trace() + if mapping is not None: + try: + self._validate_mapping(mapping, scheme) + self._data = mapping + except MetadataUnrecognizedVersionError: + self._legacy = LegacyMetadata(mapping=mapping, scheme=scheme) + self.validate() + else: + data = None + if path: + with open(path, 'rb') as f: + data = f.read() + elif fileobj: + data = fileobj.read() + if data is None: + # Initialised with no args - to be added + self._data = { + 'metadata_version': self.METADATA_VERSION, + 'generator': self.GENERATOR, + } + else: + if not isinstance(data, text_type): + data = data.decode('utf-8') + try: + self._data = json.loads(data) + self._validate_mapping(self._data, scheme) + except ValueError: + # Note: MetadataUnrecognizedVersionError does not + # inherit from ValueError (it's a DistlibException, + # which should not inherit from ValueError). + # The ValueError comes from the json.load - if that + # succeeds and we get a validation error, we want + # that to propagate + self._legacy = LegacyMetadata(fileobj=StringIO(data), + scheme=scheme) + self.validate() + + common_keys = set(('name', 'version', 'license', 'keywords', 'summary')) + + none_list = (None, list) + none_dict = (None, dict) + + mapped_keys = { + 'run_requires': ('Requires-Dist', list), + 'build_requires': ('Setup-Requires-Dist', list), + 'dev_requires': none_list, + 'test_requires': none_list, + 'meta_requires': none_list, + 'extras': ('Provides-Extra', list), + 'modules': none_list, + 'namespaces': none_list, + 'exports': none_dict, + 'commands': none_dict, + 'classifiers': ('Classifier', list), + 'source_url': ('Download-URL', None), + 'metadata_version': ('Metadata-Version', None), + } + + del none_list, none_dict + + def __getattribute__(self, key): + common = object.__getattribute__(self, 'common_keys') + mapped = object.__getattribute__(self, 'mapped_keys') + if key in mapped: + lk, maker = mapped[key] + if self._legacy: + if lk is None: + result = None if maker is None else maker() + else: + result = self._legacy.get(lk) + else: + value = None if maker is None else maker() + if key not in ('commands', 'exports', 'modules', 'namespaces', + 'classifiers'): + result = self._data.get(key, value) + else: + # special cases for PEP 459 + sentinel = object() + result = sentinel + d = self._data.get('extensions') + if d: + if key == 'commands': + result = d.get('python.commands', value) + elif key == 'classifiers': + d = d.get('python.details') + if d: + result = d.get(key, value) + else: + d = d.get('python.exports') + if not d: + d = self._data.get('python.exports') + if d: + result = d.get(key, value) + if result is sentinel: + result = value + elif key not in common: + result = object.__getattribute__(self, key) + elif self._legacy: + result = self._legacy.get(key) + else: + result = self._data.get(key) + return result + + def _validate_value(self, key, value, scheme=None): + if key in self.SYNTAX_VALIDATORS: + pattern, exclusions = self.SYNTAX_VALIDATORS[key] + if (scheme or self.scheme) not in exclusions: + m = pattern.match(value) + if not m: + raise MetadataInvalidError("'%s' is an invalid value for " + "the '%s' property" % (value, + key)) + + def __setattr__(self, key, value): + self._validate_value(key, value) + common = object.__getattribute__(self, 'common_keys') + mapped = object.__getattribute__(self, 'mapped_keys') + if key in mapped: + lk, _ = mapped[key] + if self._legacy: + if lk is None: + raise NotImplementedError + self._legacy[lk] = value + elif key not in ('commands', 'exports', 'modules', 'namespaces', + 'classifiers'): + self._data[key] = value + else: + # special cases for PEP 459 + d = self._data.setdefault('extensions', {}) + if key == 'commands': + d['python.commands'] = value + elif key == 'classifiers': + d = d.setdefault('python.details', {}) + d[key] = value + else: + d = d.setdefault('python.exports', {}) + d[key] = value + elif key not in common: + object.__setattr__(self, key, value) + else: + if key == 'keywords': + if isinstance(value, string_types): + value = value.strip() + if value: + value = value.split() + else: + value = [] + if self._legacy: + self._legacy[key] = value + else: + self._data[key] = value + + @property + def name_and_version(self): + return _get_name_and_version(self.name, self.version, True) + + @property + def provides(self): + if self._legacy: + result = self._legacy['Provides-Dist'] + else: + result = self._data.setdefault('provides', []) + s = '%s (%s)' % (self.name, self.version) + if s not in result: + result.append(s) + return result + + @provides.setter + def provides(self, value): + if self._legacy: + self._legacy['Provides-Dist'] = value + else: + self._data['provides'] = value + + def get_requirements(self, reqts, extras=None, env=None): + """ + Base method to get dependencies, given a set of extras + to satisfy and an optional environment context. + :param reqts: A list of sometimes-wanted dependencies, + perhaps dependent on extras and environment. + :param extras: A list of optional components being requested. + :param env: An optional environment for marker evaluation. + """ + if self._legacy: + result = reqts + else: + result = [] + extras = get_extras(extras or [], self.extras) + for d in reqts: + if 'extra' not in d and 'environment' not in d: + # unconditional + include = True + else: + if 'extra' not in d: + # Not extra-dependent - only environment-dependent + include = True + else: + include = d.get('extra') in extras + if include: + # Not excluded because of extras, check environment + marker = d.get('environment') + if marker: + include = interpret(marker, env) + if include: + result.extend(d['requires']) + for key in ('build', 'dev', 'test'): + e = ':%s:' % key + if e in extras: + extras.remove(e) + # A recursive call, but it should terminate since 'test' + # has been removed from the extras + reqts = self._data.get('%s_requires' % key, []) + result.extend(self.get_requirements(reqts, extras=extras, + env=env)) + return result + + @property + def dictionary(self): + if self._legacy: + return self._from_legacy() + return self._data + + @property + def dependencies(self): + if self._legacy: + raise NotImplementedError + else: + return extract_by_key(self._data, self.DEPENDENCY_KEYS) + + @dependencies.setter + def dependencies(self, value): + if self._legacy: + raise NotImplementedError + else: + self._data.update(value) + + def _validate_mapping(self, mapping, scheme): + if mapping.get('metadata_version') != self.METADATA_VERSION: + raise MetadataUnrecognizedVersionError() + missing = [] + for key, exclusions in self.MANDATORY_KEYS.items(): + if key not in mapping: + if scheme not in exclusions: + missing.append(key) + if missing: + msg = 'Missing metadata items: %s' % ', '.join(missing) + raise MetadataMissingError(msg) + for k, v in mapping.items(): + self._validate_value(k, v, scheme) + + def validate(self): + if self._legacy: + missing, warnings = self._legacy.check(True) + if missing or warnings: + logger.warning('Metadata: missing: %s, warnings: %s', + missing, warnings) + else: + self._validate_mapping(self._data, self.scheme) + + def todict(self): + if self._legacy: + return self._legacy.todict(True) + else: + result = extract_by_key(self._data, self.INDEX_KEYS) + return result + + def _from_legacy(self): + assert self._legacy and not self._data + result = { + 'metadata_version': self.METADATA_VERSION, + 'generator': self.GENERATOR, + } + lmd = self._legacy.todict(True) # skip missing ones + for k in ('name', 'version', 'license', 'summary', 'description', + 'classifier'): + if k in lmd: + if k == 'classifier': + nk = 'classifiers' + else: + nk = k + result[nk] = lmd[k] + kw = lmd.get('Keywords', []) + if kw == ['']: + kw = [] + result['keywords'] = kw + keys = (('requires_dist', 'run_requires'), + ('setup_requires_dist', 'build_requires')) + for ok, nk in keys: + if ok in lmd and lmd[ok]: + result[nk] = [{'requires': lmd[ok]}] + result['provides'] = self.provides + author = {} + maintainer = {} + return result + + LEGACY_MAPPING = { + 'name': 'Name', + 'version': 'Version', + 'license': 'License', + 'summary': 'Summary', + 'description': 'Description', + 'classifiers': 'Classifier', + } + + def _to_legacy(self): + def process_entries(entries): + reqts = set() + for e in entries: + extra = e.get('extra') + env = e.get('environment') + rlist = e['requires'] + for r in rlist: + if not env and not extra: + reqts.add(r) + else: + marker = '' + if extra: + marker = 'extra == "%s"' % extra + if env: + if marker: + marker = '(%s) and %s' % (env, marker) + else: + marker = env + reqts.add(';'.join((r, marker))) + return reqts + + assert self._data and not self._legacy + result = LegacyMetadata() + nmd = self._data + for nk, ok in self.LEGACY_MAPPING.items(): + if nk in nmd: + result[ok] = nmd[nk] + r1 = process_entries(self.run_requires + self.meta_requires) + r2 = process_entries(self.build_requires + self.dev_requires) + if self.extras: + result['Provides-Extra'] = sorted(self.extras) + result['Requires-Dist'] = sorted(r1) + result['Setup-Requires-Dist'] = sorted(r2) + # TODO: other fields such as contacts + return result + + def write(self, path=None, fileobj=None, legacy=False, skip_unknown=True): + if [path, fileobj].count(None) != 1: + raise ValueError('Exactly one of path and fileobj is needed') + self.validate() + if legacy: + if self._legacy: + legacy_md = self._legacy + else: + legacy_md = self._to_legacy() + if path: + legacy_md.write(path, skip_unknown=skip_unknown) + else: + legacy_md.write_file(fileobj, skip_unknown=skip_unknown) + else: + if self._legacy: + d = self._from_legacy() + else: + d = self._data + if fileobj: + json.dump(d, fileobj, ensure_ascii=True, indent=2, + sort_keys=True) + else: + with codecs.open(path, 'w', 'utf-8') as f: + json.dump(d, f, ensure_ascii=True, indent=2, + sort_keys=True) + + def add_requirements(self, requirements): + if self._legacy: + self._legacy.add_requirements(requirements) + else: + run_requires = self._data.setdefault('run_requires', []) + always = None + for entry in run_requires: + if 'environment' not in entry and 'extra' not in entry: + always = entry + break + if always is None: + always = { 'requires': requirements } + run_requires.insert(0, always) + else: + rset = set(always['requires']) | set(requirements) + always['requires'] = sorted(rset) + + def __repr__(self): + name = self.name or '(no name)' + version = self.version or 'no version' + return '<%s %s %s (%s)>' % (self.__class__.__name__, + self.metadata_version, name, version) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/resources.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/resources.py new file mode 100644 index 0000000..f07cde2 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/resources.py @@ -0,0 +1,355 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2016 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import unicode_literals + +import bisect +import io +import logging +import os +import pkgutil +import shutil +import sys +import types +import zipimport + +from . import DistlibException +from .util import cached_property, get_cache_base, path_to_cache_dir, Cache + +logger = logging.getLogger(__name__) + + +cache = None # created when needed + + +class ResourceCache(Cache): + def __init__(self, base=None): + if base is None: + # Use native string to avoid issues on 2.x: see Python #20140. + base = os.path.join(get_cache_base(), str('resource-cache')) + super(ResourceCache, self).__init__(base) + + def is_stale(self, resource, path): + """ + Is the cache stale for the given resource? + + :param resource: The :class:`Resource` being cached. + :param path: The path of the resource in the cache. + :return: True if the cache is stale. + """ + # Cache invalidation is a hard problem :-) + return True + + def get(self, resource): + """ + Get a resource into the cache, + + :param resource: A :class:`Resource` instance. + :return: The pathname of the resource in the cache. + """ + prefix, path = resource.finder.get_cache_info(resource) + if prefix is None: + result = path + else: + result = os.path.join(self.base, self.prefix_to_dir(prefix), path) + dirname = os.path.dirname(result) + if not os.path.isdir(dirname): + os.makedirs(dirname) + if not os.path.exists(result): + stale = True + else: + stale = self.is_stale(resource, path) + if stale: + # write the bytes of the resource to the cache location + with open(result, 'wb') as f: + f.write(resource.bytes) + return result + + +class ResourceBase(object): + def __init__(self, finder, name): + self.finder = finder + self.name = name + + +class Resource(ResourceBase): + """ + A class representing an in-package resource, such as a data file. This is + not normally instantiated by user code, but rather by a + :class:`ResourceFinder` which manages the resource. + """ + is_container = False # Backwards compatibility + + def as_stream(self): + """ + Get the resource as a stream. + + This is not a property to make it obvious that it returns a new stream + each time. + """ + return self.finder.get_stream(self) + + @cached_property + def file_path(self): + global cache + if cache is None: + cache = ResourceCache() + return cache.get(self) + + @cached_property + def bytes(self): + return self.finder.get_bytes(self) + + @cached_property + def size(self): + return self.finder.get_size(self) + + +class ResourceContainer(ResourceBase): + is_container = True # Backwards compatibility + + @cached_property + def resources(self): + return self.finder.get_resources(self) + + +class ResourceFinder(object): + """ + Resource finder for file system resources. + """ + + if sys.platform.startswith('java'): + skipped_extensions = ('.pyc', '.pyo', '.class') + else: + skipped_extensions = ('.pyc', '.pyo') + + def __init__(self, module): + self.module = module + self.loader = getattr(module, '__loader__', None) + self.base = os.path.dirname(getattr(module, '__file__', '')) + + def _adjust_path(self, path): + return os.path.realpath(path) + + def _make_path(self, resource_name): + # Issue #50: need to preserve type of path on Python 2.x + # like os.path._get_sep + if isinstance(resource_name, bytes): # should only happen on 2.x + sep = b'/' + else: + sep = '/' + parts = resource_name.split(sep) + parts.insert(0, self.base) + result = os.path.join(*parts) + return self._adjust_path(result) + + def _find(self, path): + return os.path.exists(path) + + def get_cache_info(self, resource): + return None, resource.path + + def find(self, resource_name): + path = self._make_path(resource_name) + if not self._find(path): + result = None + else: + if self._is_directory(path): + result = ResourceContainer(self, resource_name) + else: + result = Resource(self, resource_name) + result.path = path + return result + + def get_stream(self, resource): + return open(resource.path, 'rb') + + def get_bytes(self, resource): + with open(resource.path, 'rb') as f: + return f.read() + + def get_size(self, resource): + return os.path.getsize(resource.path) + + def get_resources(self, resource): + def allowed(f): + return (f != '__pycache__' and not + f.endswith(self.skipped_extensions)) + return set([f for f in os.listdir(resource.path) if allowed(f)]) + + def is_container(self, resource): + return self._is_directory(resource.path) + + _is_directory = staticmethod(os.path.isdir) + + def iterator(self, resource_name): + resource = self.find(resource_name) + if resource is not None: + todo = [resource] + while todo: + resource = todo.pop(0) + yield resource + if resource.is_container: + rname = resource.name + for name in resource.resources: + if not rname: + new_name = name + else: + new_name = '/'.join([rname, name]) + child = self.find(new_name) + if child.is_container: + todo.append(child) + else: + yield child + + +class ZipResourceFinder(ResourceFinder): + """ + Resource finder for resources in .zip files. + """ + def __init__(self, module): + super(ZipResourceFinder, self).__init__(module) + archive = self.loader.archive + self.prefix_len = 1 + len(archive) + # PyPy doesn't have a _files attr on zipimporter, and you can't set one + if hasattr(self.loader, '_files'): + self._files = self.loader._files + else: + self._files = zipimport._zip_directory_cache[archive] + self.index = sorted(self._files) + + def _adjust_path(self, path): + return path + + def _find(self, path): + path = path[self.prefix_len:] + if path in self._files: + result = True + else: + if path and path[-1] != os.sep: + path = path + os.sep + i = bisect.bisect(self.index, path) + try: + result = self.index[i].startswith(path) + except IndexError: + result = False + if not result: + logger.debug('_find failed: %r %r', path, self.loader.prefix) + else: + logger.debug('_find worked: %r %r', path, self.loader.prefix) + return result + + def get_cache_info(self, resource): + prefix = self.loader.archive + path = resource.path[1 + len(prefix):] + return prefix, path + + def get_bytes(self, resource): + return self.loader.get_data(resource.path) + + def get_stream(self, resource): + return io.BytesIO(self.get_bytes(resource)) + + def get_size(self, resource): + path = resource.path[self.prefix_len:] + return self._files[path][3] + + def get_resources(self, resource): + path = resource.path[self.prefix_len:] + if path and path[-1] != os.sep: + path += os.sep + plen = len(path) + result = set() + i = bisect.bisect(self.index, path) + while i < len(self.index): + if not self.index[i].startswith(path): + break + s = self.index[i][plen:] + result.add(s.split(os.sep, 1)[0]) # only immediate children + i += 1 + return result + + def _is_directory(self, path): + path = path[self.prefix_len:] + if path and path[-1] != os.sep: + path += os.sep + i = bisect.bisect(self.index, path) + try: + result = self.index[i].startswith(path) + except IndexError: + result = False + return result + +_finder_registry = { + type(None): ResourceFinder, + zipimport.zipimporter: ZipResourceFinder +} + +try: + # In Python 3.6, _frozen_importlib -> _frozen_importlib_external + try: + import _frozen_importlib_external as _fi + except ImportError: + import _frozen_importlib as _fi + _finder_registry[_fi.SourceFileLoader] = ResourceFinder + _finder_registry[_fi.FileFinder] = ResourceFinder + del _fi +except (ImportError, AttributeError): + pass + + +def register_finder(loader, finder_maker): + _finder_registry[type(loader)] = finder_maker + +_finder_cache = {} + + +def finder(package): + """ + Return a resource finder for a package. + :param package: The name of the package. + :return: A :class:`ResourceFinder` instance for the package. + """ + if package in _finder_cache: + result = _finder_cache[package] + else: + if package not in sys.modules: + __import__(package) + module = sys.modules[package] + path = getattr(module, '__path__', None) + if path is None: + raise DistlibException('You cannot get a finder for a module, ' + 'only for a package') + loader = getattr(module, '__loader__', None) + finder_maker = _finder_registry.get(type(loader)) + if finder_maker is None: + raise DistlibException('Unable to locate finder for %r' % package) + result = finder_maker(module) + _finder_cache[package] = result + return result + + +_dummy_module = types.ModuleType(str('__dummy__')) + + +def finder_for_path(path): + """ + Return a resource finder for a path, which should represent a container. + + :param path: The path. + :return: A :class:`ResourceFinder` instance for the path. + """ + result = None + # calls any path hooks, gets importer into cache + pkgutil.get_importer(path) + loader = sys.path_importer_cache.get(path) + finder = _finder_registry.get(type(loader)) + if finder: + module = _dummy_module + module.__file__ = os.path.join(path, '') + module.__loader__ = loader + result = finder(module) + return result diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/scripts.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/scripts.py new file mode 100644 index 0000000..792fc2e --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/scripts.py @@ -0,0 +1,384 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2015 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from io import BytesIO +import logging +import os +import re +import struct +import sys + +from .compat import sysconfig, detect_encoding, ZipFile +from .resources import finder +from .util import (FileOperator, get_export_entry, convert_path, + get_executable, in_venv) + +logger = logging.getLogger(__name__) + +_DEFAULT_MANIFEST = ''' +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <assemblyIdentity version="1.0.0.0" + processorArchitecture="X86" + name="%s" + type="win32"/> + + <!-- Identify the application security requirements. --> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker" uiAccess="false"/> + </requestedPrivileges> + </security> + </trustInfo> +</assembly>'''.strip() + +# check if Python is called on the first line with this expression +FIRST_LINE_RE = re.compile(b'^#!.*pythonw?[0-9.]*([ \t].*)?$') +SCRIPT_TEMPLATE = '''# -*- coding: utf-8 -*- +if __name__ == '__main__': + import sys, re + + def _resolve(module, func): + __import__(module) + mod = sys.modules[module] + parts = func.split('.') + result = getattr(mod, parts.pop(0)) + for p in parts: + result = getattr(result, p) + return result + + try: + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + + func = _resolve('%(module)s', '%(func)s') + rc = func() # None interpreted as 0 + except Exception as e: # only supporting Python >= 2.6 + sys.stderr.write('%%s\\n' %% e) + rc = 1 + sys.exit(rc) +''' + + +def _enquote_executable(executable): + if ' ' in executable: + # make sure we quote only the executable in case of env + # for example /usr/bin/env "/dir with spaces/bin/jython" + # instead of "/usr/bin/env /dir with spaces/bin/jython" + # otherwise whole + if executable.startswith('/usr/bin/env '): + env, _executable = executable.split(' ', 1) + if ' ' in _executable and not _executable.startswith('"'): + executable = '%s "%s"' % (env, _executable) + else: + if not executable.startswith('"'): + executable = '"%s"' % executable + return executable + + +class ScriptMaker(object): + """ + A class to copy or create scripts from source scripts or callable + specifications. + """ + script_template = SCRIPT_TEMPLATE + + executable = None # for shebangs + + def __init__(self, source_dir, target_dir, add_launchers=True, + dry_run=False, fileop=None): + self.source_dir = source_dir + self.target_dir = target_dir + self.add_launchers = add_launchers + self.force = False + self.clobber = False + # It only makes sense to set mode bits on POSIX. + self.set_mode = (os.name == 'posix') or (os.name == 'java' and + os._name == 'posix') + self.variants = set(('', 'X.Y')) + self._fileop = fileop or FileOperator(dry_run) + + self._is_nt = os.name == 'nt' or ( + os.name == 'java' and os._name == 'nt') + + def _get_alternate_executable(self, executable, options): + if options.get('gui', False) and self._is_nt: # pragma: no cover + dn, fn = os.path.split(executable) + fn = fn.replace('python', 'pythonw') + executable = os.path.join(dn, fn) + return executable + + if sys.platform.startswith('java'): # pragma: no cover + def _is_shell(self, executable): + """ + Determine if the specified executable is a script + (contains a #! line) + """ + try: + with open(executable) as fp: + return fp.read(2) == '#!' + except (OSError, IOError): + logger.warning('Failed to open %s', executable) + return False + + def _fix_jython_executable(self, executable): + if self._is_shell(executable): + # Workaround for Jython is not needed on Linux systems. + import java + + if java.lang.System.getProperty('os.name') == 'Linux': + return executable + elif executable.lower().endswith('jython.exe'): + # Use wrapper exe for Jython on Windows + return executable + return '/usr/bin/env %s' % executable + + def _get_shebang(self, encoding, post_interp=b'', options=None): + enquote = True + if self.executable: + executable = self.executable + enquote = False # assume this will be taken care of + elif not sysconfig.is_python_build(): + executable = get_executable() + elif in_venv(): # pragma: no cover + executable = os.path.join(sysconfig.get_path('scripts'), + 'python%s' % sysconfig.get_config_var('EXE')) + else: # pragma: no cover + executable = os.path.join( + sysconfig.get_config_var('BINDIR'), + 'python%s%s' % (sysconfig.get_config_var('VERSION'), + sysconfig.get_config_var('EXE'))) + if options: + executable = self._get_alternate_executable(executable, options) + + if sys.platform.startswith('java'): # pragma: no cover + executable = self._fix_jython_executable(executable) + # Normalise case for Windows + executable = os.path.normcase(executable) + # If the user didn't specify an executable, it may be necessary to + # cater for executable paths with spaces (not uncommon on Windows) + if enquote: + executable = _enquote_executable(executable) + # Issue #51: don't use fsencode, since we later try to + # check that the shebang is decodable using utf-8. + executable = executable.encode('utf-8') + # in case of IronPython, play safe and enable frames support + if (sys.platform == 'cli' and '-X:Frames' not in post_interp + and '-X:FullFrames' not in post_interp): # pragma: no cover + post_interp += b' -X:Frames' + shebang = b'#!' + executable + post_interp + b'\n' + # Python parser starts to read a script using UTF-8 until + # it gets a #coding:xxx cookie. The shebang has to be the + # first line of a file, the #coding:xxx cookie cannot be + # written before. So the shebang has to be decodable from + # UTF-8. + try: + shebang.decode('utf-8') + except UnicodeDecodeError: # pragma: no cover + raise ValueError( + 'The shebang (%r) is not decodable from utf-8' % shebang) + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be decodable from + # the script encoding too. + if encoding != 'utf-8': + try: + shebang.decode(encoding) + except UnicodeDecodeError: # pragma: no cover + raise ValueError( + 'The shebang (%r) is not decodable ' + 'from the script encoding (%r)' % (shebang, encoding)) + return shebang + + def _get_script_text(self, entry): + return self.script_template % dict(module=entry.prefix, + func=entry.suffix) + + manifest = _DEFAULT_MANIFEST + + def get_manifest(self, exename): + base = os.path.basename(exename) + return self.manifest % base + + def _write_script(self, names, shebang, script_bytes, filenames, ext): + use_launcher = self.add_launchers and self._is_nt + linesep = os.linesep.encode('utf-8') + if not use_launcher: + script_bytes = shebang + linesep + script_bytes + else: # pragma: no cover + if ext == 'py': + launcher = self._get_launcher('t') + else: + launcher = self._get_launcher('w') + stream = BytesIO() + with ZipFile(stream, 'w') as zf: + zf.writestr('__main__.py', script_bytes) + zip_data = stream.getvalue() + script_bytes = launcher + shebang + linesep + zip_data + for name in names: + outname = os.path.join(self.target_dir, name) + if use_launcher: # pragma: no cover + n, e = os.path.splitext(outname) + if e.startswith('.py'): + outname = n + outname = '%s.exe' % outname + try: + self._fileop.write_binary_file(outname, script_bytes) + except Exception: + # Failed writing an executable - it might be in use. + logger.warning('Failed to write executable - trying to ' + 'use .deleteme logic') + dfname = '%s.deleteme' % outname + if os.path.exists(dfname): + os.remove(dfname) # Not allowed to fail here + os.rename(outname, dfname) # nor here + self._fileop.write_binary_file(outname, script_bytes) + logger.debug('Able to replace executable using ' + '.deleteme logic') + try: + os.remove(dfname) + except Exception: + pass # still in use - ignore error + else: + if self._is_nt and not outname.endswith('.' + ext): # pragma: no cover + outname = '%s.%s' % (outname, ext) + if os.path.exists(outname) and not self.clobber: + logger.warning('Skipping existing file %s', outname) + continue + self._fileop.write_binary_file(outname, script_bytes) + if self.set_mode: + self._fileop.set_executable_mode([outname]) + filenames.append(outname) + + def _make_script(self, entry, filenames, options=None): + post_interp = b'' + if options: + args = options.get('interpreter_args', []) + if args: + args = ' %s' % ' '.join(args) + post_interp = args.encode('utf-8') + shebang = self._get_shebang('utf-8', post_interp, options=options) + script = self._get_script_text(entry).encode('utf-8') + name = entry.name + scriptnames = set() + if '' in self.variants: + scriptnames.add(name) + if 'X' in self.variants: + scriptnames.add('%s%s' % (name, sys.version[0])) + if 'X.Y' in self.variants: + scriptnames.add('%s-%s' % (name, sys.version[:3])) + if options and options.get('gui', False): + ext = 'pyw' + else: + ext = 'py' + self._write_script(scriptnames, shebang, script, filenames, ext) + + def _copy_script(self, script, filenames): + adjust = False + script = os.path.join(self.source_dir, convert_path(script)) + outname = os.path.join(self.target_dir, os.path.basename(script)) + if not self.force and not self._fileop.newer(script, outname): + logger.debug('not copying %s (up-to-date)', script) + return + + # Always open the file, but ignore failures in dry-run mode -- + # that way, we'll get accurate feedback if we can read the + # script. + try: + f = open(script, 'rb') + except IOError: # pragma: no cover + if not self.dry_run: + raise + f = None + else: + first_line = f.readline() + if not first_line: # pragma: no cover + logger.warning('%s: %s is an empty file (skipping)', + self.get_command_name(), script) + return + + match = FIRST_LINE_RE.match(first_line.replace(b'\r\n', b'\n')) + if match: + adjust = True + post_interp = match.group(1) or b'' + + if not adjust: + if f: + f.close() + self._fileop.copy_file(script, outname) + if self.set_mode: + self._fileop.set_executable_mode([outname]) + filenames.append(outname) + else: + logger.info('copying and adjusting %s -> %s', script, + self.target_dir) + if not self._fileop.dry_run: + encoding, lines = detect_encoding(f.readline) + f.seek(0) + shebang = self._get_shebang(encoding, post_interp) + if b'pythonw' in first_line: # pragma: no cover + ext = 'pyw' + else: + ext = 'py' + n = os.path.basename(outname) + self._write_script([n], shebang, f.read(), filenames, ext) + if f: + f.close() + + @property + def dry_run(self): + return self._fileop.dry_run + + @dry_run.setter + def dry_run(self, value): + self._fileop.dry_run = value + + if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'): # pragma: no cover + # Executable launcher support. + # Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/ + + def _get_launcher(self, kind): + if struct.calcsize('P') == 8: # 64-bit + bits = '64' + else: + bits = '32' + name = '%s%s.exe' % (kind, bits) + # Issue 31: don't hardcode an absolute package name, but + # determine it relative to the current package + distlib_package = __name__.rsplit('.', 1)[0] + result = finder(distlib_package).find(name).bytes + return result + + # Public API follows + + def make(self, specification, options=None): + """ + Make a script. + + :param specification: The specification, which is either a valid export + entry specification (to make a script from a + callable) or a filename (to make a script by + copying from a source location). + :param options: A dictionary of options controlling script generation. + :return: A list of all absolute pathnames written to. + """ + filenames = [] + entry = get_export_entry(specification) + if entry is None: + self._copy_script(specification, filenames) + else: + self._make_script(entry, filenames, options=options) + return filenames + + def make_multiple(self, specifications, options=None): + """ + Take a list of specifications and make scripts from them, + :param specifications: A list of specifications. + :return: A list of all absolute pathnames written to, + """ + filenames = [] + for specification in specifications: + filenames.extend(self.make(specification, options)) + return filenames diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/t32.exe b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/t32.exe new file mode 100644 index 0000000000000000000000000000000000000000..836211d848ec0ba46667d33cfade3348888062ae GIT binary patch literal 89088 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≫*an|s~C9@ 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�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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/t64.exe b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/t64.exe new file mode 100644 index 0000000000000000000000000000000000000000..a401b59d602d4823e0a69138a0268f0bfbec3de2 GIT binary patch literal 97792 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/util.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/util.py new file mode 100644 index 0000000..aadc874 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/util.py @@ -0,0 +1,1611 @@ +# +# Copyright (C) 2012-2016 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import codecs +from collections import deque +import contextlib +import csv +from glob import iglob as std_iglob +import io +import json +import logging +import os +import py_compile +import re +import shutil +import socket +try: + import ssl +except ImportError: # pragma: no cover + ssl = None +import subprocess +import sys +import tarfile +import tempfile +import textwrap + +try: + import threading +except ImportError: # pragma: no cover + import dummy_threading as threading +import time + +from . import DistlibException +from .compat import (string_types, text_type, shutil, raw_input, StringIO, + cache_from_source, urlopen, urljoin, httplib, xmlrpclib, + splittype, HTTPHandler, BaseConfigurator, valid_ident, + Container, configparser, URLError, ZipFile, fsdecode, + unquote) + +logger = logging.getLogger(__name__) + +# +# Requirement parsing code for name + optional constraints + optional extras +# +# e.g. 'foo >= 1.2, < 2.0 [bar, baz]' +# +# The regex can seem a bit hairy, so we build it up out of smaller pieces +# which are manageable. +# + +COMMA = r'\s*,\s*' +COMMA_RE = re.compile(COMMA) + +IDENT = r'(\w|[.-])+' +EXTRA_IDENT = r'(\*|:(\*|\w+):|' + IDENT + ')' +VERSPEC = IDENT + r'\*?' + +RELOP = '([<>=!~]=)|[<>]' + +# +# The first relop is optional - if absent, will be taken as '~=' +# +BARE_CONSTRAINTS = ('(' + RELOP + r')?\s*(' + VERSPEC + ')(' + COMMA + '(' + + RELOP + r')\s*(' + VERSPEC + '))*') + +DIRECT_REF = '(from\s+(?P<diref>.*))' + +# +# Either the bare constraints or the bare constraints in parentheses +# +CONSTRAINTS = (r'\(\s*(?P<c1>' + BARE_CONSTRAINTS + '|' + DIRECT_REF + + r')\s*\)|(?P<c2>' + BARE_CONSTRAINTS + '\s*)') + +EXTRA_LIST = EXTRA_IDENT + '(' + COMMA + EXTRA_IDENT + ')*' +EXTRAS = r'\[\s*(?P<ex>' + EXTRA_LIST + r')?\s*\]' +REQUIREMENT = ('(?P<dn>' + IDENT + r')\s*(' + EXTRAS + r'\s*)?(\s*' + + CONSTRAINTS + ')?$') +REQUIREMENT_RE = re.compile(REQUIREMENT) + +# +# Used to scan through the constraints +# +RELOP_IDENT = '(?P<op>' + RELOP + r')\s*(?P<vn>' + VERSPEC + ')' +RELOP_IDENT_RE = re.compile(RELOP_IDENT) + +def parse_requirement(s): + + def get_constraint(m): + d = m.groupdict() + return d['op'], d['vn'] + + result = None + m = REQUIREMENT_RE.match(s) + if m: + d = m.groupdict() + name = d['dn'] + cons = d['c1'] or d['c2'] + if not d['diref']: + url = None + else: + # direct reference + cons = None + url = d['diref'].strip() + if not cons: + cons = None + constr = '' + rs = d['dn'] + else: + if cons[0] not in '<>!=': + cons = '~=' + cons + iterator = RELOP_IDENT_RE.finditer(cons) + cons = [get_constraint(m) for m in iterator] + rs = '%s (%s)' % (name, ', '.join(['%s %s' % con for con in cons])) + if not d['ex']: + extras = None + else: + extras = COMMA_RE.split(d['ex']) + result = Container(name=name, constraints=cons, extras=extras, + requirement=rs, source=s, url=url) + return result + + +def get_resources_dests(resources_root, rules): + """Find destinations for resources files""" + + def get_rel_path(base, path): + # normalizes and returns a lstripped-/-separated path + base = base.replace(os.path.sep, '/') + path = path.replace(os.path.sep, '/') + assert path.startswith(base) + return path[len(base):].lstrip('/') + + + destinations = {} + for base, suffix, dest in rules: + prefix = os.path.join(resources_root, base) + for abs_base in iglob(prefix): + abs_glob = os.path.join(abs_base, suffix) + for abs_path in iglob(abs_glob): + resource_file = get_rel_path(resources_root, abs_path) + if dest is None: # remove the entry if it was here + destinations.pop(resource_file, None) + else: + rel_path = get_rel_path(abs_base, abs_path) + rel_dest = dest.replace(os.path.sep, '/').rstrip('/') + destinations[resource_file] = rel_dest + '/' + rel_path + return destinations + + +def in_venv(): + if hasattr(sys, 'real_prefix'): + # virtualenv venvs + result = True + else: + # PEP 405 venvs + result = sys.prefix != getattr(sys, 'base_prefix', sys.prefix) + return result + + +def get_executable(): +# The __PYVENV_LAUNCHER__ dance is apparently no longer needed, as +# changes to the stub launcher mean that sys.executable always points +# to the stub on macOS +# if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__' +# in os.environ): +# result = os.environ['__PYVENV_LAUNCHER__'] +# else: +# result = sys.executable +# return result + result = os.path.normcase(sys.executable) + if not isinstance(result, text_type): + result = fsdecode(result) + return result + + +def proceed(prompt, allowed_chars, error_prompt=None, default=None): + p = prompt + while True: + s = raw_input(p) + p = prompt + if not s and default: + s = default + if s: + c = s[0].lower() + if c in allowed_chars: + break + if error_prompt: + p = '%c: %s\n%s' % (c, error_prompt, prompt) + return c + + +def extract_by_key(d, keys): + if isinstance(keys, string_types): + keys = keys.split() + result = {} + for key in keys: + if key in d: + result[key] = d[key] + return result + +def read_exports(stream): + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getreader('utf-8')(stream) + # Try to load as JSON, falling back on legacy format + data = stream.read() + stream = StringIO(data) + try: + jdata = json.load(stream) + result = jdata['extensions']['python.exports']['exports'] + for group, entries in result.items(): + for k, v in entries.items(): + s = '%s = %s' % (k, v) + entry = get_export_entry(s) + assert entry is not None + entries[k] = entry + return result + except Exception: + stream.seek(0, 0) + + def read_stream(cp, stream): + if hasattr(cp, 'read_file'): + cp.read_file(stream) + else: + cp.readfp(stream) + + cp = configparser.ConfigParser() + try: + read_stream(cp, stream) + except configparser.MissingSectionHeaderError: + stream.close() + data = textwrap.dedent(data) + stream = StringIO(data) + read_stream(cp, stream) + + result = {} + for key in cp.sections(): + result[key] = entries = {} + for name, value in cp.items(key): + s = '%s = %s' % (name, value) + entry = get_export_entry(s) + assert entry is not None + #entry.dist = self + entries[name] = entry + return result + + +def write_exports(exports, stream): + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getwriter('utf-8')(stream) + cp = configparser.ConfigParser() + for k, v in exports.items(): + # TODO check k, v for valid values + cp.add_section(k) + for entry in v.values(): + if entry.suffix is None: + s = entry.prefix + else: + s = '%s:%s' % (entry.prefix, entry.suffix) + if entry.flags: + s = '%s [%s]' % (s, ', '.join(entry.flags)) + cp.set(k, entry.name, s) + cp.write(stream) + + +@contextlib.contextmanager +def tempdir(): + td = tempfile.mkdtemp() + try: + yield td + finally: + shutil.rmtree(td) + +@contextlib.contextmanager +def chdir(d): + cwd = os.getcwd() + try: + os.chdir(d) + yield + finally: + os.chdir(cwd) + + +@contextlib.contextmanager +def socket_timeout(seconds=15): + cto = socket.getdefaulttimeout() + try: + socket.setdefaulttimeout(seconds) + yield + finally: + socket.setdefaulttimeout(cto) + + +class cached_property(object): + def __init__(self, func): + self.func = func + #for attr in ('__name__', '__module__', '__doc__'): + # setattr(self, attr, getattr(func, attr, None)) + + def __get__(self, obj, cls=None): + if obj is None: + return self + value = self.func(obj) + object.__setattr__(obj, self.func.__name__, value) + #obj.__dict__[self.func.__name__] = value = self.func(obj) + return value + +def convert_path(pathname): + """Return 'pathname' as a name that will work on the native filesystem. + + The path is split on '/' and put back together again using the current + directory separator. Needed because filenames in the setup script are + always supplied in Unix style, and have to be converted to the local + convention before we can actually use them in the filesystem. Raises + ValueError on non-Unix-ish systems if 'pathname' either starts or + ends with a slash. + """ + if os.sep == '/': + return pathname + if not pathname: + return pathname + if pathname[0] == '/': + raise ValueError("path '%s' cannot be absolute" % pathname) + if pathname[-1] == '/': + raise ValueError("path '%s' cannot end with '/'" % pathname) + + paths = pathname.split('/') + while os.curdir in paths: + paths.remove(os.curdir) + if not paths: + return os.curdir + return os.path.join(*paths) + + +class FileOperator(object): + def __init__(self, dry_run=False): + self.dry_run = dry_run + self.ensured = set() + self._init_record() + + def _init_record(self): + self.record = False + self.files_written = set() + self.dirs_created = set() + + def record_as_written(self, path): + if self.record: + self.files_written.add(path) + + def newer(self, source, target): + """Tell if the target is newer than the source. + + Returns true if 'source' exists and is more recently modified than + 'target', or if 'source' exists and 'target' doesn't. + + Returns false if both exist and 'target' is the same age or younger + than 'source'. Raise PackagingFileError if 'source' does not exist. + + Note that this test is not very accurate: files created in the same + second will have the same "age". + """ + if not os.path.exists(source): + raise DistlibException("file '%r' does not exist" % + os.path.abspath(source)) + if not os.path.exists(target): + return True + + return os.stat(source).st_mtime > os.stat(target).st_mtime + + def copy_file(self, infile, outfile, check=True): + """Copy a file respecting dry-run and force flags. + """ + self.ensure_dir(os.path.dirname(outfile)) + logger.info('Copying %s to %s', infile, outfile) + if not self.dry_run: + msg = None + if check: + if os.path.islink(outfile): + msg = '%s is a symlink' % outfile + elif os.path.exists(outfile) and not os.path.isfile(outfile): + msg = '%s is a non-regular file' % outfile + if msg: + raise ValueError(msg + ' which would be overwritten') + shutil.copyfile(infile, outfile) + self.record_as_written(outfile) + + def copy_stream(self, instream, outfile, encoding=None): + assert not os.path.isdir(outfile) + self.ensure_dir(os.path.dirname(outfile)) + logger.info('Copying stream %s to %s', instream, outfile) + if not self.dry_run: + if encoding is None: + outstream = open(outfile, 'wb') + else: + outstream = codecs.open(outfile, 'w', encoding=encoding) + try: + shutil.copyfileobj(instream, outstream) + finally: + outstream.close() + self.record_as_written(outfile) + + def write_binary_file(self, path, data): + self.ensure_dir(os.path.dirname(path)) + if not self.dry_run: + with open(path, 'wb') as f: + f.write(data) + self.record_as_written(path) + + def write_text_file(self, path, data, encoding): + self.ensure_dir(os.path.dirname(path)) + if not self.dry_run: + with open(path, 'wb') as f: + f.write(data.encode(encoding)) + self.record_as_written(path) + + def set_mode(self, bits, mask, files): + if os.name == 'posix' or (os.name == 'java' and os._name == 'posix'): + # Set the executable bits (owner, group, and world) on + # all the files specified. + for f in files: + if self.dry_run: + logger.info("changing mode of %s", f) + else: + mode = (os.stat(f).st_mode | bits) & mask + logger.info("changing mode of %s to %o", f, mode) + os.chmod(f, mode) + + set_executable_mode = lambda s, f: s.set_mode(0o555, 0o7777, f) + + def ensure_dir(self, path): + path = os.path.abspath(path) + if path not in self.ensured and not os.path.exists(path): + self.ensured.add(path) + d, f = os.path.split(path) + self.ensure_dir(d) + logger.info('Creating %s' % path) + if not self.dry_run: + os.mkdir(path) + if self.record: + self.dirs_created.add(path) + + def byte_compile(self, path, optimize=False, force=False, prefix=None): + dpath = cache_from_source(path, not optimize) + logger.info('Byte-compiling %s to %s', path, dpath) + if not self.dry_run: + if force or self.newer(path, dpath): + if not prefix: + diagpath = None + else: + assert path.startswith(prefix) + diagpath = path[len(prefix):] + py_compile.compile(path, dpath, diagpath, True) # raise error + self.record_as_written(dpath) + return dpath + + def ensure_removed(self, path): + if os.path.exists(path): + if os.path.isdir(path) and not os.path.islink(path): + logger.debug('Removing directory tree at %s', path) + if not self.dry_run: + shutil.rmtree(path) + if self.record: + if path in self.dirs_created: + self.dirs_created.remove(path) + else: + if os.path.islink(path): + s = 'link' + else: + s = 'file' + logger.debug('Removing %s %s', s, path) + if not self.dry_run: + os.remove(path) + if self.record: + if path in self.files_written: + self.files_written.remove(path) + + def is_writable(self, path): + result = False + while not result: + if os.path.exists(path): + result = os.access(path, os.W_OK) + break + parent = os.path.dirname(path) + if parent == path: + break + path = parent + return result + + def commit(self): + """ + Commit recorded changes, turn off recording, return + changes. + """ + assert self.record + result = self.files_written, self.dirs_created + self._init_record() + return result + + def rollback(self): + if not self.dry_run: + for f in list(self.files_written): + if os.path.exists(f): + os.remove(f) + # dirs should all be empty now, except perhaps for + # __pycache__ subdirs + # reverse so that subdirs appear before their parents + dirs = sorted(self.dirs_created, reverse=True) + for d in dirs: + flist = os.listdir(d) + if flist: + assert flist == ['__pycache__'] + sd = os.path.join(d, flist[0]) + os.rmdir(sd) + os.rmdir(d) # should fail if non-empty + self._init_record() + +def resolve(module_name, dotted_path): + if module_name in sys.modules: + mod = sys.modules[module_name] + else: + mod = __import__(module_name) + if dotted_path is None: + result = mod + else: + parts = dotted_path.split('.') + result = getattr(mod, parts.pop(0)) + for p in parts: + result = getattr(result, p) + return result + + +class ExportEntry(object): + def __init__(self, name, prefix, suffix, flags): + self.name = name + self.prefix = prefix + self.suffix = suffix + self.flags = flags + + @cached_property + def value(self): + return resolve(self.prefix, self.suffix) + + def __repr__(self): # pragma: no cover + return '<ExportEntry %s = %s:%s %s>' % (self.name, self.prefix, + self.suffix, self.flags) + + def __eq__(self, other): + if not isinstance(other, ExportEntry): + result = False + else: + result = (self.name == other.name and + self.prefix == other.prefix and + self.suffix == other.suffix and + self.flags == other.flags) + return result + + __hash__ = object.__hash__ + + +ENTRY_RE = re.compile(r'''(?P<name>(\w|[-.+])+) + \s*=\s*(?P<callable>(\w+)([:\.]\w+)*) + \s*(\[\s*(?P<flags>\w+(=\w+)?(,\s*\w+(=\w+)?)*)\s*\])? + ''', re.VERBOSE) + +def get_export_entry(specification): + m = ENTRY_RE.search(specification) + if not m: + result = None + if '[' in specification or ']' in specification: + raise DistlibException("Invalid specification " + "'%s'" % specification) + else: + d = m.groupdict() + name = d['name'] + path = d['callable'] + colons = path.count(':') + if colons == 0: + prefix, suffix = path, None + else: + if colons != 1: + raise DistlibException("Invalid specification " + "'%s'" % specification) + prefix, suffix = path.split(':') + flags = d['flags'] + if flags is None: + if '[' in specification or ']' in specification: + raise DistlibException("Invalid specification " + "'%s'" % specification) + flags = [] + else: + flags = [f.strip() for f in flags.split(',')] + result = ExportEntry(name, prefix, suffix, flags) + return result + + +def get_cache_base(suffix=None): + """ + Return the default base location for distlib caches. If the directory does + not exist, it is created. Use the suffix provided for the base directory, + and default to '.distlib' if it isn't provided. + + On Windows, if LOCALAPPDATA is defined in the environment, then it is + assumed to be a directory, and will be the parent directory of the result. + On POSIX, and on Windows if LOCALAPPDATA is not defined, the user's home + directory - using os.expanduser('~') - will be the parent directory of + the result. + + The result is just the directory '.distlib' in the parent directory as + determined above, or with the name specified with ``suffix``. + """ + if suffix is None: + suffix = '.distlib' + if os.name == 'nt' and 'LOCALAPPDATA' in os.environ: + result = os.path.expandvars('$localappdata') + else: + # Assume posix, or old Windows + result = os.path.expanduser('~') + # we use 'isdir' instead of 'exists', because we want to + # fail if there's a file with that name + if os.path.isdir(result): + usable = os.access(result, os.W_OK) + if not usable: + logger.warning('Directory exists but is not writable: %s', result) + else: + try: + os.makedirs(result) + usable = True + except OSError: + logger.warning('Unable to create %s', result, exc_info=True) + usable = False + if not usable: + result = tempfile.mkdtemp() + logger.warning('Default location unusable, using %s', result) + return os.path.join(result, suffix) + + +def path_to_cache_dir(path): + """ + Convert an absolute path to a directory name for use in a cache. + + The algorithm used is: + + #. On Windows, any ``':'`` in the drive is replaced with ``'---'``. + #. Any occurrence of ``os.sep`` is replaced with ``'--'``. + #. ``'.cache'`` is appended. + """ + d, p = os.path.splitdrive(os.path.abspath(path)) + if d: + d = d.replace(':', '---') + p = p.replace(os.sep, '--') + return d + p + '.cache' + + +def ensure_slash(s): + if not s.endswith('/'): + return s + '/' + return s + + +def parse_credentials(netloc): + username = password = None + if '@' in netloc: + prefix, netloc = netloc.split('@', 1) + if ':' not in prefix: + username = prefix + else: + username, password = prefix.split(':', 1) + return username, password, netloc + + +def get_process_umask(): + result = os.umask(0o22) + os.umask(result) + return result + +def is_string_sequence(seq): + result = True + i = None + for i, s in enumerate(seq): + if not isinstance(s, string_types): + result = False + break + assert i is not None + return result + +PROJECT_NAME_AND_VERSION = re.compile('([a-z0-9_]+([.-][a-z_][a-z0-9_]*)*)-' + '([a-z0-9_.+-]+)', re.I) +PYTHON_VERSION = re.compile(r'-py(\d\.?\d?)') + + +def split_filename(filename, project_name=None): + """ + Extract name, version, python version from a filename (no extension) + + Return name, version, pyver or None + """ + result = None + pyver = None + filename = unquote(filename).replace(' ', '-') + m = PYTHON_VERSION.search(filename) + if m: + pyver = m.group(1) + filename = filename[:m.start()] + if project_name and len(filename) > len(project_name) + 1: + m = re.match(re.escape(project_name) + r'\b', filename) + if m: + n = m.end() + result = filename[:n], filename[n + 1:], pyver + if result is None: + m = PROJECT_NAME_AND_VERSION.match(filename) + if m: + result = m.group(1), m.group(3), pyver + return result + +# Allow spaces in name because of legacy dists like "Twisted Core" +NAME_VERSION_RE = re.compile(r'(?P<name>[\w .-]+)\s*' + r'\(\s*(?P<ver>[^\s)]+)\)$') + +def parse_name_and_version(p): + """ + A utility method used to get name and version from a string. + + From e.g. a Provides-Dist value. + + :param p: A value in a form 'foo (1.0)' + :return: The name and version as a tuple. + """ + m = NAME_VERSION_RE.match(p) + if not m: + raise DistlibException('Ill-formed name/version string: \'%s\'' % p) + d = m.groupdict() + return d['name'].strip().lower(), d['ver'] + +def get_extras(requested, available): + result = set() + requested = set(requested or []) + available = set(available or []) + if '*' in requested: + requested.remove('*') + result |= available + for r in requested: + if r == '-': + result.add(r) + elif r.startswith('-'): + unwanted = r[1:] + if unwanted not in available: + logger.warning('undeclared extra: %s' % unwanted) + if unwanted in result: + result.remove(unwanted) + else: + if r not in available: + logger.warning('undeclared extra: %s' % r) + result.add(r) + return result +# +# Extended metadata functionality +# + +def _get_external_data(url): + result = {} + try: + # urlopen might fail if it runs into redirections, + # because of Python issue #13696. Fixed in locators + # using a custom redirect handler. + resp = urlopen(url) + headers = resp.info() + ct = headers.get('Content-Type') + if not ct.startswith('application/json'): + logger.debug('Unexpected response for JSON request: %s', ct) + else: + reader = codecs.getreader('utf-8')(resp) + #data = reader.read().decode('utf-8') + #result = json.loads(data) + result = json.load(reader) + except Exception as e: + logger.exception('Failed to get external data for %s: %s', url, e) + return result + +_external_data_base_url = 'https://www.red-dove.com/pypi/projects/' + +def get_project_data(name): + url = '%s/%s/project.json' % (name[0].upper(), name) + url = urljoin(_external_data_base_url, url) + result = _get_external_data(url) + return result + +def get_package_data(name, version): + url = '%s/%s/package-%s.json' % (name[0].upper(), name, version) + url = urljoin(_external_data_base_url, url) + return _get_external_data(url) + + +class Cache(object): + """ + A class implementing a cache for resources that need to live in the file system + e.g. shared libraries. This class was moved from resources to here because it + could be used by other modules, e.g. the wheel module. + """ + + def __init__(self, base): + """ + Initialise an instance. + + :param base: The base directory where the cache should be located. + """ + # we use 'isdir' instead of 'exists', because we want to + # fail if there's a file with that name + if not os.path.isdir(base): # pragma: no cover + os.makedirs(base) + if (os.stat(base).st_mode & 0o77) != 0: + logger.warning('Directory \'%s\' is not private', base) + self.base = os.path.abspath(os.path.normpath(base)) + + def prefix_to_dir(self, prefix): + """ + Converts a resource prefix to a directory name in the cache. + """ + return path_to_cache_dir(prefix) + + def clear(self): + """ + Clear the cache. + """ + not_removed = [] + for fn in os.listdir(self.base): + fn = os.path.join(self.base, fn) + try: + if os.path.islink(fn) or os.path.isfile(fn): + os.remove(fn) + elif os.path.isdir(fn): + shutil.rmtree(fn) + except Exception: + not_removed.append(fn) + return not_removed + + +class EventMixin(object): + """ + A very simple publish/subscribe system. + """ + def __init__(self): + self._subscribers = {} + + def add(self, event, subscriber, append=True): + """ + Add a subscriber for an event. + + :param event: The name of an event. + :param subscriber: The subscriber to be added (and called when the + event is published). + :param append: Whether to append or prepend the subscriber to an + existing subscriber list for the event. + """ + subs = self._subscribers + if event not in subs: + subs[event] = deque([subscriber]) + else: + sq = subs[event] + if append: + sq.append(subscriber) + else: + sq.appendleft(subscriber) + + def remove(self, event, subscriber): + """ + Remove a subscriber for an event. + + :param event: The name of an event. + :param subscriber: The subscriber to be removed. + """ + subs = self._subscribers + if event not in subs: + raise ValueError('No subscribers: %r' % event) + subs[event].remove(subscriber) + + def get_subscribers(self, event): + """ + Return an iterator for the subscribers for an event. + :param event: The event to return subscribers for. + """ + return iter(self._subscribers.get(event, ())) + + def publish(self, event, *args, **kwargs): + """ + Publish a event and return a list of values returned by its + subscribers. + + :param event: The event to publish. + :param args: The positional arguments to pass to the event's + subscribers. + :param kwargs: The keyword arguments to pass to the event's + subscribers. + """ + result = [] + for subscriber in self.get_subscribers(event): + try: + value = subscriber(event, *args, **kwargs) + except Exception: + logger.exception('Exception during event publication') + value = None + result.append(value) + logger.debug('publish %s: args = %s, kwargs = %s, result = %s', + event, args, kwargs, result) + return result + +# +# Simple sequencing +# +class Sequencer(object): + def __init__(self): + self._preds = {} + self._succs = {} + self._nodes = set() # nodes with no preds/succs + + def add_node(self, node): + self._nodes.add(node) + + def remove_node(self, node, edges=False): + if node in self._nodes: + self._nodes.remove(node) + if edges: + for p in set(self._preds.get(node, ())): + self.remove(p, node) + for s in set(self._succs.get(node, ())): + self.remove(node, s) + # Remove empties + for k, v in list(self._preds.items()): + if not v: + del self._preds[k] + for k, v in list(self._succs.items()): + if not v: + del self._succs[k] + + def add(self, pred, succ): + assert pred != succ + self._preds.setdefault(succ, set()).add(pred) + self._succs.setdefault(pred, set()).add(succ) + + def remove(self, pred, succ): + assert pred != succ + try: + preds = self._preds[succ] + succs = self._succs[pred] + except KeyError: # pragma: no cover + raise ValueError('%r not a successor of anything' % succ) + try: + preds.remove(pred) + succs.remove(succ) + except KeyError: # pragma: no cover + raise ValueError('%r not a successor of %r' % (succ, pred)) + + def is_step(self, step): + return (step in self._preds or step in self._succs or + step in self._nodes) + + def get_steps(self, final): + if not self.is_step(final): + raise ValueError('Unknown: %r' % final) + result = [] + todo = [] + seen = set() + todo.append(final) + while todo: + step = todo.pop(0) + if step in seen: + # if a step was already seen, + # move it to the end (so it will appear earlier + # when reversed on return) ... but not for the + # final step, as that would be confusing for + # users + if step != final: + result.remove(step) + result.append(step) + else: + seen.add(step) + result.append(step) + preds = self._preds.get(step, ()) + todo.extend(preds) + return reversed(result) + + @property + def strong_connections(self): + #http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm + index_counter = [0] + stack = [] + lowlinks = {} + index = {} + result = [] + + graph = self._succs + + def strongconnect(node): + # set the depth index for this node to the smallest unused index + index[node] = index_counter[0] + lowlinks[node] = index_counter[0] + index_counter[0] += 1 + stack.append(node) + + # Consider successors + try: + successors = graph[node] + except Exception: + successors = [] + for successor in successors: + if successor not in lowlinks: + # Successor has not yet been visited + strongconnect(successor) + lowlinks[node] = min(lowlinks[node],lowlinks[successor]) + elif successor in stack: + # the successor is in the stack and hence in the current + # strongly connected component (SCC) + lowlinks[node] = min(lowlinks[node],index[successor]) + + # If `node` is a root node, pop the stack and generate an SCC + if lowlinks[node] == index[node]: + connected_component = [] + + while True: + successor = stack.pop() + connected_component.append(successor) + if successor == node: break + component = tuple(connected_component) + # storing the result + result.append(component) + + for node in graph: + if node not in lowlinks: + strongconnect(node) + + return result + + @property + def dot(self): + result = ['digraph G {'] + for succ in self._preds: + preds = self._preds[succ] + for pred in preds: + result.append(' %s -> %s;' % (pred, succ)) + for node in self._nodes: + result.append(' %s;' % node) + result.append('}') + return '\n'.join(result) + +# +# Unarchiving functionality for zip, tar, tgz, tbz, whl +# + +ARCHIVE_EXTENSIONS = ('.tar.gz', '.tar.bz2', '.tar', '.zip', + '.tgz', '.tbz', '.whl') + +def unarchive(archive_filename, dest_dir, format=None, check=True): + + def check_path(path): + if not isinstance(path, text_type): + path = path.decode('utf-8') + p = os.path.abspath(os.path.join(dest_dir, path)) + if not p.startswith(dest_dir) or p[plen] != os.sep: + raise ValueError('path outside destination: %r' % p) + + dest_dir = os.path.abspath(dest_dir) + plen = len(dest_dir) + archive = None + if format is None: + if archive_filename.endswith(('.zip', '.whl')): + format = 'zip' + elif archive_filename.endswith(('.tar.gz', '.tgz')): + format = 'tgz' + mode = 'r:gz' + elif archive_filename.endswith(('.tar.bz2', '.tbz')): + format = 'tbz' + mode = 'r:bz2' + elif archive_filename.endswith('.tar'): + format = 'tar' + mode = 'r' + else: # pragma: no cover + raise ValueError('Unknown format for %r' % archive_filename) + try: + if format == 'zip': + archive = ZipFile(archive_filename, 'r') + if check: + names = archive.namelist() + for name in names: + check_path(name) + else: + archive = tarfile.open(archive_filename, mode) + if check: + names = archive.getnames() + for name in names: + check_path(name) + if format != 'zip' and sys.version_info[0] < 3: + # See Python issue 17153. If the dest path contains Unicode, + # tarfile extraction fails on Python 2.x if a member path name + # contains non-ASCII characters - it leads to an implicit + # bytes -> unicode conversion using ASCII to decode. + for tarinfo in archive.getmembers(): + if not isinstance(tarinfo.name, text_type): + tarinfo.name = tarinfo.name.decode('utf-8') + archive.extractall(dest_dir) + + finally: + if archive: + archive.close() + + +def zip_dir(directory): + """zip a directory tree into a BytesIO object""" + result = io.BytesIO() + dlen = len(directory) + with ZipFile(result, "w") as zf: + for root, dirs, files in os.walk(directory): + for name in files: + full = os.path.join(root, name) + rel = root[dlen:] + dest = os.path.join(rel, name) + zf.write(full, dest) + return result + +# +# Simple progress bar +# + +UNITS = ('', 'K', 'M', 'G','T','P') + + +class Progress(object): + unknown = 'UNKNOWN' + + def __init__(self, minval=0, maxval=100): + assert maxval is None or maxval >= minval + self.min = self.cur = minval + self.max = maxval + self.started = None + self.elapsed = 0 + self.done = False + + def update(self, curval): + assert self.min <= curval + assert self.max is None or curval <= self.max + self.cur = curval + now = time.time() + if self.started is None: + self.started = now + else: + self.elapsed = now - self.started + + def increment(self, incr): + assert incr >= 0 + self.update(self.cur + incr) + + def start(self): + self.update(self.min) + return self + + def stop(self): + if self.max is not None: + self.update(self.max) + self.done = True + + @property + def maximum(self): + return self.unknown if self.max is None else self.max + + @property + def percentage(self): + if self.done: + result = '100 %' + elif self.max is None: + result = ' ?? %' + else: + v = 100.0 * (self.cur - self.min) / (self.max - self.min) + result = '%3d %%' % v + return result + + def format_duration(self, duration): + if (duration <= 0) and self.max is None or self.cur == self.min: + result = '??:??:??' + #elif duration < 1: + # result = '--:--:--' + else: + result = time.strftime('%H:%M:%S', time.gmtime(duration)) + return result + + @property + def ETA(self): + if self.done: + prefix = 'Done' + t = self.elapsed + #import pdb; pdb.set_trace() + else: + prefix = 'ETA ' + if self.max is None: + t = -1 + elif self.elapsed == 0 or (self.cur == self.min): + t = 0 + else: + #import pdb; pdb.set_trace() + t = float(self.max - self.min) + t /= self.cur - self.min + t = (t - 1) * self.elapsed + return '%s: %s' % (prefix, self.format_duration(t)) + + @property + def speed(self): + if self.elapsed == 0: + result = 0.0 + else: + result = (self.cur - self.min) / self.elapsed + for unit in UNITS: + if result < 1000: + break + result /= 1000.0 + return '%d %sB/s' % (result, unit) + +# +# Glob functionality +# + +RICH_GLOB = re.compile(r'\{([^}]*)\}') +_CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]') +_CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$') + + +def iglob(path_glob): + """Extended globbing function that supports ** and {opt1,opt2,opt3}.""" + if _CHECK_RECURSIVE_GLOB.search(path_glob): + msg = """invalid glob %r: recursive glob "**" must be used alone""" + raise ValueError(msg % path_glob) + if _CHECK_MISMATCH_SET.search(path_glob): + msg = """invalid glob %r: mismatching set marker '{' or '}'""" + raise ValueError(msg % path_glob) + return _iglob(path_glob) + + +def _iglob(path_glob): + rich_path_glob = RICH_GLOB.split(path_glob, 1) + if len(rich_path_glob) > 1: + assert len(rich_path_glob) == 3, rich_path_glob + prefix, set, suffix = rich_path_glob + for item in set.split(','): + for path in _iglob(''.join((prefix, item, suffix))): + yield path + else: + if '**' not in path_glob: + for item in std_iglob(path_glob): + yield item + else: + prefix, radical = path_glob.split('**', 1) + if prefix == '': + prefix = '.' + if radical == '': + radical = '*' + else: + # we support both + radical = radical.lstrip('/') + radical = radical.lstrip('\\') + for path, dir, files in os.walk(prefix): + path = os.path.normpath(path) + for fn in _iglob(os.path.join(path, radical)): + yield fn + +if ssl: + from .compat import (HTTPSHandler as BaseHTTPSHandler, match_hostname, + CertificateError) + + +# +# HTTPSConnection which verifies certificates/matches domains +# + + class HTTPSConnection(httplib.HTTPSConnection): + ca_certs = None # set this to the path to the certs file (.pem) + check_domain = True # only used if ca_certs is not None + + # noinspection PyPropertyAccess + def connect(self): + sock = socket.create_connection((self.host, self.port), self.timeout) + if getattr(self, '_tunnel_host', False): + self.sock = sock + self._tunnel() + + if not hasattr(ssl, 'SSLContext'): + # For 2.x + if self.ca_certs: + cert_reqs = ssl.CERT_REQUIRED + else: + cert_reqs = ssl.CERT_NONE + self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, + cert_reqs=cert_reqs, + ssl_version=ssl.PROTOCOL_SSLv23, + ca_certs=self.ca_certs) + else: # pragma: no cover + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.options |= ssl.OP_NO_SSLv2 + if self.cert_file: + context.load_cert_chain(self.cert_file, self.key_file) + kwargs = {} + if self.ca_certs: + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(cafile=self.ca_certs) + if getattr(ssl, 'HAS_SNI', False): + kwargs['server_hostname'] = self.host + self.sock = context.wrap_socket(sock, **kwargs) + if self.ca_certs and self.check_domain: + try: + match_hostname(self.sock.getpeercert(), self.host) + logger.debug('Host verified: %s', self.host) + except CertificateError: # pragma: no cover + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + raise + + class HTTPSHandler(BaseHTTPSHandler): + def __init__(self, ca_certs, check_domain=True): + BaseHTTPSHandler.__init__(self) + self.ca_certs = ca_certs + self.check_domain = check_domain + + def _conn_maker(self, *args, **kwargs): + """ + This is called to create a connection instance. Normally you'd + pass a connection class to do_open, but it doesn't actually check for + a class, and just expects a callable. As long as we behave just as a + constructor would have, we should be OK. If it ever changes so that + we *must* pass a class, we'll create an UnsafeHTTPSConnection class + which just sets check_domain to False in the class definition, and + choose which one to pass to do_open. + """ + result = HTTPSConnection(*args, **kwargs) + if self.ca_certs: + result.ca_certs = self.ca_certs + result.check_domain = self.check_domain + return result + + def https_open(self, req): + try: + return self.do_open(self._conn_maker, req) + except URLError as e: + if 'certificate verify failed' in str(e.reason): + raise CertificateError('Unable to verify server certificate ' + 'for %s' % req.host) + else: + raise + + # + # To prevent against mixing HTTP traffic with HTTPS (examples: A Man-In-The- + # Middle proxy using HTTP listens on port 443, or an index mistakenly serves + # HTML containing a http://xyz link when it should be https://xyz), + # you can use the following handler class, which does not allow HTTP traffic. + # + # It works by inheriting from HTTPHandler - so build_opener won't add a + # handler for HTTP itself. + # + class HTTPSOnlyHandler(HTTPSHandler, HTTPHandler): + def http_open(self, req): + raise URLError('Unexpected HTTP request on what should be a secure ' + 'connection: %s' % req) + +# +# XML-RPC with timeouts +# + +_ver_info = sys.version_info[:2] + +if _ver_info == (2, 6): + class HTTP(httplib.HTTP): + def __init__(self, host='', port=None, **kwargs): + if port == 0: # 0 means use port 0, not the default port + port = None + self._setup(self._connection_class(host, port, **kwargs)) + + + if ssl: + class HTTPS(httplib.HTTPS): + def __init__(self, host='', port=None, **kwargs): + if port == 0: # 0 means use port 0, not the default port + port = None + self._setup(self._connection_class(host, port, **kwargs)) + + +class Transport(xmlrpclib.Transport): + def __init__(self, timeout, use_datetime=0): + self.timeout = timeout + xmlrpclib.Transport.__init__(self, use_datetime) + + def make_connection(self, host): + h, eh, x509 = self.get_host_info(host) + if _ver_info == (2, 6): + result = HTTP(h, timeout=self.timeout) + else: + if not self._connection or host != self._connection[0]: + self._extra_headers = eh + self._connection = host, httplib.HTTPConnection(h) + result = self._connection[1] + return result + +if ssl: + class SafeTransport(xmlrpclib.SafeTransport): + def __init__(self, timeout, use_datetime=0): + self.timeout = timeout + xmlrpclib.SafeTransport.__init__(self, use_datetime) + + def make_connection(self, host): + h, eh, kwargs = self.get_host_info(host) + if not kwargs: + kwargs = {} + kwargs['timeout'] = self.timeout + if _ver_info == (2, 6): + result = HTTPS(host, None, **kwargs) + else: + if not self._connection or host != self._connection[0]: + self._extra_headers = eh + self._connection = host, httplib.HTTPSConnection(h, None, + **kwargs) + result = self._connection[1] + return result + + +class ServerProxy(xmlrpclib.ServerProxy): + def __init__(self, uri, **kwargs): + self.timeout = timeout = kwargs.pop('timeout', None) + # The above classes only come into play if a timeout + # is specified + if timeout is not None: + scheme, _ = splittype(uri) + use_datetime = kwargs.get('use_datetime', 0) + if scheme == 'https': + tcls = SafeTransport + else: + tcls = Transport + kwargs['transport'] = t = tcls(timeout, use_datetime=use_datetime) + self.transport = t + xmlrpclib.ServerProxy.__init__(self, uri, **kwargs) + +# +# CSV functionality. This is provided because on 2.x, the csv module can't +# handle Unicode. However, we need to deal with Unicode in e.g. RECORD files. +# + +def _csv_open(fn, mode, **kwargs): + if sys.version_info[0] < 3: + mode += 'b' + else: + kwargs['newline'] = '' + return open(fn, mode, **kwargs) + + +class CSVBase(object): + defaults = { + 'delimiter': str(','), # The strs are used because we need native + 'quotechar': str('"'), # str in the csv API (2.x won't take + 'lineterminator': str('\n') # Unicode) + } + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.stream.close() + + +class CSVReader(CSVBase): + def __init__(self, **kwargs): + if 'stream' in kwargs: + stream = kwargs['stream'] + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getreader('utf-8')(stream) + self.stream = stream + else: + self.stream = _csv_open(kwargs['path'], 'r') + self.reader = csv.reader(self.stream, **self.defaults) + + def __iter__(self): + return self + + def next(self): + result = next(self.reader) + if sys.version_info[0] < 3: + for i, item in enumerate(result): + if not isinstance(item, text_type): + result[i] = item.decode('utf-8') + return result + + __next__ = next + +class CSVWriter(CSVBase): + def __init__(self, fn, **kwargs): + self.stream = _csv_open(fn, 'w') + self.writer = csv.writer(self.stream, **self.defaults) + + def writerow(self, row): + if sys.version_info[0] < 3: + r = [] + for item in row: + if isinstance(item, text_type): + item = item.encode('utf-8') + r.append(item) + row = r + self.writer.writerow(row) + +# +# Configurator functionality +# + +class Configurator(BaseConfigurator): + + value_converters = dict(BaseConfigurator.value_converters) + value_converters['inc'] = 'inc_convert' + + def __init__(self, config, base=None): + super(Configurator, self).__init__(config) + self.base = base or os.getcwd() + + def configure_custom(self, config): + def convert(o): + if isinstance(o, (list, tuple)): + result = type(o)([convert(i) for i in o]) + elif isinstance(o, dict): + if '()' in o: + result = self.configure_custom(o) + else: + result = {} + for k in o: + result[k] = convert(o[k]) + else: + result = self.convert(o) + return result + + c = config.pop('()') + if not callable(c): + c = self.resolve(c) + props = config.pop('.', None) + # Check for valid identifiers + args = config.pop('[]', ()) + if args: + args = tuple([convert(o) for o in args]) + items = [(k, convert(config[k])) for k in config if valid_ident(k)] + kwargs = dict(items) + result = c(*args, **kwargs) + if props: + for n, v in props.items(): + setattr(result, n, convert(v)) + return result + + def __getitem__(self, key): + result = self.config[key] + if isinstance(result, dict) and '()' in result: + self.config[key] = result = self.configure_custom(result) + return result + + def inc_convert(self, value): + """Default converter for the inc:// protocol.""" + if not os.path.isabs(value): + value = os.path.join(self.base, value) + with codecs.open(value, 'r', encoding='utf-8') as f: + result = json.load(f) + return result + +# +# Mixin for running subprocesses and capturing their output +# + +class SubprocessMixin(object): + def __init__(self, verbose=False, progress=None): + self.verbose = verbose + self.progress = progress + + def reader(self, stream, context): + """ + Read lines from a subprocess' output stream and either pass to a progress + callable (if specified) or write progress information to sys.stderr. + """ + progress = self.progress + verbose = self.verbose + while True: + s = stream.readline() + if not s: + break + if progress is not None: + progress(s, context) + else: + if not verbose: + sys.stderr.write('.') + else: + sys.stderr.write(s.decode('utf-8')) + sys.stderr.flush() + stream.close() + + def run_command(self, cmd, **kwargs): + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, **kwargs) + t1 = threading.Thread(target=self.reader, args=(p.stdout, 'stdout')) + t1.start() + t2 = threading.Thread(target=self.reader, args=(p.stderr, 'stderr')) + t2.start() + p.wait() + t1.join() + t2.join() + if self.progress is not None: + self.progress('done.', 'main') + elif self.verbose: + sys.stderr.write('done.\n') + return p + + +def normalize_name(name): + """Normalize a python package name a la PEP 503""" + # https://www.python.org/dev/peps/pep-0503/#normalized-names + return re.sub('[-_.]+', '-', name).lower() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/version.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/version.py new file mode 100644 index 0000000..48c17c0 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/version.py @@ -0,0 +1,742 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2016 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +""" +Implementation of a flexible versioning scheme providing support for PEP-440, +setuptools-compatible and semantic versioning. +""" + +import logging +import re + +from .compat import string_types + +__all__ = ['NormalizedVersion', 'NormalizedMatcher', + 'LegacyVersion', 'LegacyMatcher', + 'SemanticVersion', 'SemanticMatcher', + 'UnsupportedVersionError', 'get_scheme'] + +logger = logging.getLogger(__name__) + + +class UnsupportedVersionError(ValueError): + """This is an unsupported version.""" + pass + + +class Version(object): + def __init__(self, s): + self._string = s = s.strip() + self._parts = parts = self.parse(s) + assert isinstance(parts, tuple) + assert len(parts) > 0 + + def parse(self, s): + raise NotImplementedError('please implement in a subclass') + + def _check_compatible(self, other): + if type(self) != type(other): + raise TypeError('cannot compare %r and %r' % (self, other)) + + def __eq__(self, other): + self._check_compatible(other) + return self._parts == other._parts + + def __ne__(self, other): + return not self.__eq__(other) + + def __lt__(self, other): + self._check_compatible(other) + return self._parts < other._parts + + def __gt__(self, other): + return not (self.__lt__(other) or self.__eq__(other)) + + def __le__(self, other): + return self.__lt__(other) or self.__eq__(other) + + def __ge__(self, other): + return self.__gt__(other) or self.__eq__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + def __hash__(self): + return hash(self._parts) + + def __repr__(self): + return "%s('%s')" % (self.__class__.__name__, self._string) + + def __str__(self): + return self._string + + @property + def is_prerelease(self): + raise NotImplementedError('Please implement in subclasses.') + + +class Matcher(object): + version_class = None + + dist_re = re.compile(r"^(\w[\s\w'.-]*)(\((.*)\))?") + comp_re = re.compile(r'^(<=|>=|<|>|!=|={2,3}|~=)?\s*([^\s,]+)$') + num_re = re.compile(r'^\d+(\.\d+)*$') + + # value is either a callable or the name of a method + _operators = { + '<': lambda v, c, p: v < c, + '>': lambda v, c, p: v > c, + '<=': lambda v, c, p: v == c or v < c, + '>=': lambda v, c, p: v == c or v > c, + '==': lambda v, c, p: v == c, + '===': lambda v, c, p: v == c, + # by default, compatible => >=. + '~=': lambda v, c, p: v == c or v > c, + '!=': lambda v, c, p: v != c, + } + + def __init__(self, s): + if self.version_class is None: + raise ValueError('Please specify a version class') + self._string = s = s.strip() + m = self.dist_re.match(s) + if not m: + raise ValueError('Not valid: %r' % s) + groups = m.groups('') + self.name = groups[0].strip() + self.key = self.name.lower() # for case-insensitive comparisons + clist = [] + if groups[2]: + constraints = [c.strip() for c in groups[2].split(',')] + for c in constraints: + m = self.comp_re.match(c) + if not m: + raise ValueError('Invalid %r in %r' % (c, s)) + groups = m.groups() + op = groups[0] or '~=' + s = groups[1] + if s.endswith('.*'): + if op not in ('==', '!='): + raise ValueError('\'.*\' not allowed for ' + '%r constraints' % op) + # Could be a partial version (e.g. for '2.*') which + # won't parse as a version, so keep it as a string + vn, prefix = s[:-2], True + if not self.num_re.match(vn): + # Just to check that vn is a valid version + self.version_class(vn) + else: + # Should parse as a version, so we can create an + # instance for the comparison + vn, prefix = self.version_class(s), False + clist.append((op, vn, prefix)) + self._parts = tuple(clist) + + def match(self, version): + """ + Check if the provided version matches the constraints. + + :param version: The version to match against this instance. + :type version: String or :class:`Version` instance. + """ + if isinstance(version, string_types): + version = self.version_class(version) + for operator, constraint, prefix in self._parts: + f = self._operators.get(operator) + if isinstance(f, string_types): + f = getattr(self, f) + if not f: + msg = ('%r not implemented ' + 'for %s' % (operator, self.__class__.__name__)) + raise NotImplementedError(msg) + if not f(version, constraint, prefix): + return False + return True + + @property + def exact_version(self): + result = None + if len(self._parts) == 1 and self._parts[0][0] in ('==', '==='): + result = self._parts[0][1] + return result + + def _check_compatible(self, other): + if type(self) != type(other) or self.name != other.name: + raise TypeError('cannot compare %s and %s' % (self, other)) + + def __eq__(self, other): + self._check_compatible(other) + return self.key == other.key and self._parts == other._parts + + def __ne__(self, other): + return not self.__eq__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + def __hash__(self): + return hash(self.key) + hash(self._parts) + + def __repr__(self): + return "%s(%r)" % (self.__class__.__name__, self._string) + + def __str__(self): + return self._string + + +PEP440_VERSION_RE = re.compile(r'^v?(\d+!)?(\d+(\.\d+)*)((a|b|c|rc)(\d+))?' + r'(\.(post)(\d+))?(\.(dev)(\d+))?' + r'(\+([a-zA-Z\d]+(\.[a-zA-Z\d]+)?))?$') + + +def _pep_440_key(s): + s = s.strip() + m = PEP440_VERSION_RE.match(s) + if not m: + raise UnsupportedVersionError('Not a valid version: %s' % s) + groups = m.groups() + nums = tuple(int(v) for v in groups[1].split('.')) + while len(nums) > 1 and nums[-1] == 0: + nums = nums[:-1] + + if not groups[0]: + epoch = 0 + else: + epoch = int(groups[0]) + pre = groups[4:6] + post = groups[7:9] + dev = groups[10:12] + local = groups[13] + if pre == (None, None): + pre = () + else: + pre = pre[0], int(pre[1]) + if post == (None, None): + post = () + else: + post = post[0], int(post[1]) + if dev == (None, None): + dev = () + else: + dev = dev[0], int(dev[1]) + if local is None: + local = () + else: + parts = [] + for part in local.split('.'): + # to ensure that numeric compares as > lexicographic, avoid + # comparing them directly, but encode a tuple which ensures + # correct sorting + if part.isdigit(): + part = (1, int(part)) + else: + part = (0, part) + parts.append(part) + local = tuple(parts) + if not pre: + # either before pre-release, or final release and after + if not post and dev: + # before pre-release + pre = ('a', -1) # to sort before a0 + else: + pre = ('z',) # to sort after all pre-releases + # now look at the state of post and dev. + if not post: + post = ('_',) # sort before 'a' + if not dev: + dev = ('final',) + + #print('%s -> %s' % (s, m.groups())) + return epoch, nums, pre, post, dev, local + + +_normalized_key = _pep_440_key + + +class NormalizedVersion(Version): + """A rational version. + + Good: + 1.2 # equivalent to "1.2.0" + 1.2.0 + 1.2a1 + 1.2.3a2 + 1.2.3b1 + 1.2.3c1 + 1.2.3.4 + TODO: fill this out + + Bad: + 1 # minimum two numbers + 1.2a # release level must have a release serial + 1.2.3b + """ + def parse(self, s): + result = _normalized_key(s) + # _normalized_key loses trailing zeroes in the release + # clause, since that's needed to ensure that X.Y == X.Y.0 == X.Y.0.0 + # However, PEP 440 prefix matching needs it: for example, + # (~= 1.4.5.0) matches differently to (~= 1.4.5.0.0). + m = PEP440_VERSION_RE.match(s) # must succeed + groups = m.groups() + self._release_clause = tuple(int(v) for v in groups[1].split('.')) + return result + + PREREL_TAGS = set(['a', 'b', 'c', 'rc', 'dev']) + + @property + def is_prerelease(self): + return any(t[0] in self.PREREL_TAGS for t in self._parts if t) + + +def _match_prefix(x, y): + x = str(x) + y = str(y) + if x == y: + return True + if not x.startswith(y): + return False + n = len(y) + return x[n] == '.' + + +class NormalizedMatcher(Matcher): + version_class = NormalizedVersion + + # value is either a callable or the name of a method + _operators = { + '~=': '_match_compatible', + '<': '_match_lt', + '>': '_match_gt', + '<=': '_match_le', + '>=': '_match_ge', + '==': '_match_eq', + '===': '_match_arbitrary', + '!=': '_match_ne', + } + + def _adjust_local(self, version, constraint, prefix): + if prefix: + strip_local = '+' not in constraint and version._parts[-1] + else: + # both constraint and version are + # NormalizedVersion instances. + # If constraint does not have a local component, + # ensure the version doesn't, either. + strip_local = not constraint._parts[-1] and version._parts[-1] + if strip_local: + s = version._string.split('+', 1)[0] + version = self.version_class(s) + return version, constraint + + def _match_lt(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if version >= constraint: + return False + release_clause = constraint._release_clause + pfx = '.'.join([str(i) for i in release_clause]) + return not _match_prefix(version, pfx) + + def _match_gt(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if version <= constraint: + return False + release_clause = constraint._release_clause + pfx = '.'.join([str(i) for i in release_clause]) + return not _match_prefix(version, pfx) + + def _match_le(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + return version <= constraint + + def _match_ge(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + return version >= constraint + + def _match_eq(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if not prefix: + result = (version == constraint) + else: + result = _match_prefix(version, constraint) + return result + + def _match_arbitrary(self, version, constraint, prefix): + return str(version) == str(constraint) + + def _match_ne(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if not prefix: + result = (version != constraint) + else: + result = not _match_prefix(version, constraint) + return result + + def _match_compatible(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if version == constraint: + return True + if version < constraint: + return False +# if not prefix: +# return True + release_clause = constraint._release_clause + if len(release_clause) > 1: + release_clause = release_clause[:-1] + pfx = '.'.join([str(i) for i in release_clause]) + return _match_prefix(version, pfx) + +_REPLACEMENTS = ( + (re.compile('[.+-]$'), ''), # remove trailing puncts + (re.compile(r'^[.](\d)'), r'0.\1'), # .N -> 0.N at start + (re.compile('^[.-]'), ''), # remove leading puncts + (re.compile(r'^\((.*)\)$'), r'\1'), # remove parentheses + (re.compile(r'^v(ersion)?\s*(\d+)'), r'\2'), # remove leading v(ersion) + (re.compile(r'^r(ev)?\s*(\d+)'), r'\2'), # remove leading v(ersion) + (re.compile('[.]{2,}'), '.'), # multiple runs of '.' + (re.compile(r'\b(alfa|apha)\b'), 'alpha'), # misspelt alpha + (re.compile(r'\b(pre-alpha|prealpha)\b'), + 'pre.alpha'), # standardise + (re.compile(r'\(beta\)$'), 'beta'), # remove parentheses +) + +_SUFFIX_REPLACEMENTS = ( + (re.compile('^[:~._+-]+'), ''), # remove leading puncts + (re.compile('[,*")([\]]'), ''), # remove unwanted chars + (re.compile('[~:+_ -]'), '.'), # replace illegal chars + (re.compile('[.]{2,}'), '.'), # multiple runs of '.' + (re.compile(r'\.$'), ''), # trailing '.' +) + +_NUMERIC_PREFIX = re.compile(r'(\d+(\.\d+)*)') + + +def _suggest_semantic_version(s): + """ + Try to suggest a semantic form for a version for which + _suggest_normalized_version couldn't come up with anything. + """ + result = s.strip().lower() + for pat, repl in _REPLACEMENTS: + result = pat.sub(repl, result) + if not result: + result = '0.0.0' + + # Now look for numeric prefix, and separate it out from + # the rest. + #import pdb; pdb.set_trace() + m = _NUMERIC_PREFIX.match(result) + if not m: + prefix = '0.0.0' + suffix = result + else: + prefix = m.groups()[0].split('.') + prefix = [int(i) for i in prefix] + while len(prefix) < 3: + prefix.append(0) + if len(prefix) == 3: + suffix = result[m.end():] + else: + suffix = '.'.join([str(i) for i in prefix[3:]]) + result[m.end():] + prefix = prefix[:3] + prefix = '.'.join([str(i) for i in prefix]) + suffix = suffix.strip() + if suffix: + #import pdb; pdb.set_trace() + # massage the suffix. + for pat, repl in _SUFFIX_REPLACEMENTS: + suffix = pat.sub(repl, suffix) + + if not suffix: + result = prefix + else: + sep = '-' if 'dev' in suffix else '+' + result = prefix + sep + suffix + if not is_semver(result): + result = None + return result + + +def _suggest_normalized_version(s): + """Suggest a normalized version close to the given version string. + + If you have a version string that isn't rational (i.e. NormalizedVersion + doesn't like it) then you might be able to get an equivalent (or close) + rational version from this function. + + This does a number of simple normalizations to the given string, based + on observation of versions currently in use on PyPI. Given a dump of + those version during PyCon 2009, 4287 of them: + - 2312 (53.93%) match NormalizedVersion without change + with the automatic suggestion + - 3474 (81.04%) match when using this suggestion method + + @param s {str} An irrational version string. + @returns A rational version string, or None, if couldn't determine one. + """ + try: + _normalized_key(s) + return s # already rational + except UnsupportedVersionError: + pass + + rs = s.lower() + + # part of this could use maketrans + for orig, repl in (('-alpha', 'a'), ('-beta', 'b'), ('alpha', 'a'), + ('beta', 'b'), ('rc', 'c'), ('-final', ''), + ('-pre', 'c'), + ('-release', ''), ('.release', ''), ('-stable', ''), + ('+', '.'), ('_', '.'), (' ', ''), ('.final', ''), + ('final', '')): + rs = rs.replace(orig, repl) + + # if something ends with dev or pre, we add a 0 + rs = re.sub(r"pre$", r"pre0", rs) + rs = re.sub(r"dev$", r"dev0", rs) + + # if we have something like "b-2" or "a.2" at the end of the + # version, that is probably beta, alpha, etc + # let's remove the dash or dot + rs = re.sub(r"([abc]|rc)[\-\.](\d+)$", r"\1\2", rs) + + # 1.0-dev-r371 -> 1.0.dev371 + # 0.1-dev-r79 -> 0.1.dev79 + rs = re.sub(r"[\-\.](dev)[\-\.]?r?(\d+)$", r".\1\2", rs) + + # Clean: 2.0.a.3, 2.0.b1, 0.9.0~c1 + rs = re.sub(r"[.~]?([abc])\.?", r"\1", rs) + + # Clean: v0.3, v1.0 + if rs.startswith('v'): + rs = rs[1:] + + # Clean leading '0's on numbers. + #TODO: unintended side-effect on, e.g., "2003.05.09" + # PyPI stats: 77 (~2%) better + rs = re.sub(r"\b0+(\d+)(?!\d)", r"\1", rs) + + # Clean a/b/c with no version. E.g. "1.0a" -> "1.0a0". Setuptools infers + # zero. + # PyPI stats: 245 (7.56%) better + rs = re.sub(r"(\d+[abc])$", r"\g<1>0", rs) + + # the 'dev-rNNN' tag is a dev tag + rs = re.sub(r"\.?(dev-r|dev\.r)\.?(\d+)$", r".dev\2", rs) + + # clean the - when used as a pre delimiter + rs = re.sub(r"-(a|b|c)(\d+)$", r"\1\2", rs) + + # a terminal "dev" or "devel" can be changed into ".dev0" + rs = re.sub(r"[\.\-](dev|devel)$", r".dev0", rs) + + # a terminal "dev" can be changed into ".dev0" + rs = re.sub(r"(?![\.\-])dev$", r".dev0", rs) + + # a terminal "final" or "stable" can be removed + rs = re.sub(r"(final|stable)$", "", rs) + + # The 'r' and the '-' tags are post release tags + # 0.4a1.r10 -> 0.4a1.post10 + # 0.9.33-17222 -> 0.9.33.post17222 + # 0.9.33-r17222 -> 0.9.33.post17222 + rs = re.sub(r"\.?(r|-|-r)\.?(\d+)$", r".post\2", rs) + + # Clean 'r' instead of 'dev' usage: + # 0.9.33+r17222 -> 0.9.33.dev17222 + # 1.0dev123 -> 1.0.dev123 + # 1.0.git123 -> 1.0.dev123 + # 1.0.bzr123 -> 1.0.dev123 + # 0.1a0dev.123 -> 0.1a0.dev123 + # PyPI stats: ~150 (~4%) better + rs = re.sub(r"\.?(dev|git|bzr)\.?(\d+)$", r".dev\2", rs) + + # Clean '.pre' (normalized from '-pre' above) instead of 'c' usage: + # 0.2.pre1 -> 0.2c1 + # 0.2-c1 -> 0.2c1 + # 1.0preview123 -> 1.0c123 + # PyPI stats: ~21 (0.62%) better + rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs) + + # Tcl/Tk uses "px" for their post release markers + rs = re.sub(r"p(\d+)$", r".post\1", rs) + + try: + _normalized_key(rs) + except UnsupportedVersionError: + rs = None + return rs + +# +# Legacy version processing (distribute-compatible) +# + +_VERSION_PART = re.compile(r'([a-z]+|\d+|[\.-])', re.I) +_VERSION_REPLACE = { + 'pre': 'c', + 'preview': 'c', + '-': 'final-', + 'rc': 'c', + 'dev': '@', + '': None, + '.': None, +} + + +def _legacy_key(s): + def get_parts(s): + result = [] + for p in _VERSION_PART.split(s.lower()): + p = _VERSION_REPLACE.get(p, p) + if p: + if '0' <= p[:1] <= '9': + p = p.zfill(8) + else: + p = '*' + p + result.append(p) + result.append('*final') + return result + + result = [] + for p in get_parts(s): + if p.startswith('*'): + if p < '*final': + while result and result[-1] == '*final-': + result.pop() + while result and result[-1] == '00000000': + result.pop() + result.append(p) + return tuple(result) + + +class LegacyVersion(Version): + def parse(self, s): + return _legacy_key(s) + + @property + def is_prerelease(self): + result = False + for x in self._parts: + if (isinstance(x, string_types) and x.startswith('*') and + x < '*final'): + result = True + break + return result + + +class LegacyMatcher(Matcher): + version_class = LegacyVersion + + _operators = dict(Matcher._operators) + _operators['~='] = '_match_compatible' + + numeric_re = re.compile('^(\d+(\.\d+)*)') + + def _match_compatible(self, version, constraint, prefix): + if version < constraint: + return False + m = self.numeric_re.match(str(constraint)) + if not m: + logger.warning('Cannot compute compatible match for version %s ' + ' and constraint %s', version, constraint) + return True + s = m.groups()[0] + if '.' in s: + s = s.rsplit('.', 1)[0] + return _match_prefix(version, s) + +# +# Semantic versioning +# + +_SEMVER_RE = re.compile(r'^(\d+)\.(\d+)\.(\d+)' + r'(-[a-z0-9]+(\.[a-z0-9-]+)*)?' + r'(\+[a-z0-9]+(\.[a-z0-9-]+)*)?$', re.I) + + +def is_semver(s): + return _SEMVER_RE.match(s) + + +def _semantic_key(s): + def make_tuple(s, absent): + if s is None: + result = (absent,) + else: + parts = s[1:].split('.') + # We can't compare ints and strings on Python 3, so fudge it + # by zero-filling numeric values so simulate a numeric comparison + result = tuple([p.zfill(8) if p.isdigit() else p for p in parts]) + return result + + m = is_semver(s) + if not m: + raise UnsupportedVersionError(s) + groups = m.groups() + major, minor, patch = [int(i) for i in groups[:3]] + # choose the '|' and '*' so that versions sort correctly + pre, build = make_tuple(groups[3], '|'), make_tuple(groups[5], '*') + return (major, minor, patch), pre, build + + +class SemanticVersion(Version): + def parse(self, s): + return _semantic_key(s) + + @property + def is_prerelease(self): + return self._parts[1][0] != '|' + + +class SemanticMatcher(Matcher): + version_class = SemanticVersion + + +class VersionScheme(object): + def __init__(self, key, matcher, suggester=None): + self.key = key + self.matcher = matcher + self.suggester = suggester + + def is_valid_version(self, s): + try: + self.matcher.version_class(s) + result = True + except UnsupportedVersionError: + result = False + return result + + def is_valid_matcher(self, s): + try: + self.matcher(s) + result = True + except UnsupportedVersionError: + result = False + return result + + def is_valid_constraint_list(self, s): + """ + Used for processing some metadata fields + """ + return self.is_valid_matcher('dummy_name (%s)' % s) + + def suggest(self, s): + if self.suggester is None: + result = None + else: + result = self.suggester(s) + return result + +_SCHEMES = { + 'normalized': VersionScheme(_normalized_key, NormalizedMatcher, + _suggest_normalized_version), + 'legacy': VersionScheme(_legacy_key, LegacyMatcher, lambda self, s: s), + 'semantic': VersionScheme(_semantic_key, SemanticMatcher, + _suggest_semantic_version), +} + +_SCHEMES['default'] = _SCHEMES['normalized'] + + +def get_scheme(name): + if name not in _SCHEMES: + raise ValueError('unknown scheme name: %r' % name) + return _SCHEMES[name] diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/w32.exe b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/w32.exe new file mode 100644 index 0000000000000000000000000000000000000000..85a90a5f51a9596679a7365eadd3352443b95782 GIT binary patch literal 85504 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/w64.exe b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/w64.exe new file mode 100644 index 0000000000000000000000000000000000000000..b3aea316f61163e2637a7c0b0a96cd3c664b54dc GIT binary patch literal 94208 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�TMFqLX)9%a}d0V#xElZksBJ&ik>pHd!d9D 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 NhP&x52mVhv@L%Zn@1Fnw literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/wheel.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/wheel.py new file mode 100644 index 0000000..2952b8e --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distlib/wheel.py @@ -0,0 +1,978 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2016 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import unicode_literals + +import base64 +import codecs +import datetime +import distutils.util +from email import message_from_file +import hashlib +import imp +import json +import logging +import os +import posixpath +import re +import shutil +import sys +import tempfile +import zipfile + +from . import __version__, DistlibException +from .compat import sysconfig, ZipFile, fsdecode, text_type, filter +from .database import InstalledDistribution +from .metadata import Metadata, METADATA_FILENAME +from .util import (FileOperator, convert_path, CSVReader, CSVWriter, Cache, + cached_property, get_cache_base, read_exports, tempdir) +from .version import NormalizedVersion, UnsupportedVersionError + +logger = logging.getLogger(__name__) + +cache = None # created when needed + +if hasattr(sys, 'pypy_version_info'): + IMP_PREFIX = 'pp' +elif sys.platform.startswith('java'): + IMP_PREFIX = 'jy' +elif sys.platform == 'cli': + IMP_PREFIX = 'ip' +else: + IMP_PREFIX = 'cp' + +VER_SUFFIX = sysconfig.get_config_var('py_version_nodot') +if not VER_SUFFIX: # pragma: no cover + VER_SUFFIX = '%s%s' % sys.version_info[:2] +PYVER = 'py' + VER_SUFFIX +IMPVER = IMP_PREFIX + VER_SUFFIX + +ARCH = distutils.util.get_platform().replace('-', '_').replace('.', '_') + +ABI = sysconfig.get_config_var('SOABI') +if ABI and ABI.startswith('cpython-'): + ABI = ABI.replace('cpython-', 'cp') +else: + def _derive_abi(): + parts = ['cp', VER_SUFFIX] + if sysconfig.get_config_var('Py_DEBUG'): + parts.append('d') + if sysconfig.get_config_var('WITH_PYMALLOC'): + parts.append('m') + if sysconfig.get_config_var('Py_UNICODE_SIZE') == 4: + parts.append('u') + return ''.join(parts) + ABI = _derive_abi() + del _derive_abi + +FILENAME_RE = re.compile(r''' +(?P<nm>[^-]+) +-(?P<vn>\d+[^-]*) +(-(?P<bn>\d+[^-]*))? +-(?P<py>\w+\d+(\.\w+\d+)*) +-(?P<bi>\w+) +-(?P<ar>\w+(\.\w+)*) +\.whl$ +''', re.IGNORECASE | re.VERBOSE) + +NAME_VERSION_RE = re.compile(r''' +(?P<nm>[^-]+) +-(?P<vn>\d+[^-]*) +(-(?P<bn>\d+[^-]*))?$ +''', re.IGNORECASE | re.VERBOSE) + +SHEBANG_RE = re.compile(br'\s*#![^\r\n]*') +SHEBANG_DETAIL_RE = re.compile(br'^(\s*#!("[^"]+"|\S+))\s+(.*)$') +SHEBANG_PYTHON = b'#!python' +SHEBANG_PYTHONW = b'#!pythonw' + +if os.sep == '/': + to_posix = lambda o: o +else: + to_posix = lambda o: o.replace(os.sep, '/') + + +class Mounter(object): + def __init__(self): + self.impure_wheels = {} + self.libs = {} + + def add(self, pathname, extensions): + self.impure_wheels[pathname] = extensions + self.libs.update(extensions) + + def remove(self, pathname): + extensions = self.impure_wheels.pop(pathname) + for k, v in extensions: + if k in self.libs: + del self.libs[k] + + def find_module(self, fullname, path=None): + if fullname in self.libs: + result = self + else: + result = None + return result + + def load_module(self, fullname): + if fullname in sys.modules: + result = sys.modules[fullname] + else: + if fullname not in self.libs: + raise ImportError('unable to find extension for %s' % fullname) + result = imp.load_dynamic(fullname, self.libs[fullname]) + result.__loader__ = self + parts = fullname.rsplit('.', 1) + if len(parts) > 1: + result.__package__ = parts[0] + return result + +_hook = Mounter() + + +class Wheel(object): + """ + Class to build and install from Wheel files (PEP 427). + """ + + wheel_version = (1, 1) + hash_kind = 'sha256' + + def __init__(self, filename=None, sign=False, verify=False): + """ + Initialise an instance using a (valid) filename. + """ + self.sign = sign + self.should_verify = verify + self.buildver = '' + self.pyver = [PYVER] + self.abi = ['none'] + self.arch = ['any'] + self.dirname = os.getcwd() + if filename is None: + self.name = 'dummy' + self.version = '0.1' + self._filename = self.filename + else: + m = NAME_VERSION_RE.match(filename) + if m: + info = m.groupdict('') + self.name = info['nm'] + # Reinstate the local version separator + self.version = info['vn'].replace('_', '-') + self.buildver = info['bn'] + self._filename = self.filename + else: + dirname, filename = os.path.split(filename) + m = FILENAME_RE.match(filename) + if not m: + raise DistlibException('Invalid name or ' + 'filename: %r' % filename) + if dirname: + self.dirname = os.path.abspath(dirname) + self._filename = filename + info = m.groupdict('') + self.name = info['nm'] + self.version = info['vn'] + self.buildver = info['bn'] + self.pyver = info['py'].split('.') + self.abi = info['bi'].split('.') + self.arch = info['ar'].split('.') + + @property + def filename(self): + """ + Build and return a filename from the various components. + """ + if self.buildver: + buildver = '-' + self.buildver + else: + buildver = '' + pyver = '.'.join(self.pyver) + abi = '.'.join(self.abi) + arch = '.'.join(self.arch) + # replace - with _ as a local version separator + version = self.version.replace('-', '_') + return '%s-%s%s-%s-%s-%s.whl' % (self.name, version, buildver, + pyver, abi, arch) + + @property + def exists(self): + path = os.path.join(self.dirname, self.filename) + return os.path.isfile(path) + + @property + def tags(self): + for pyver in self.pyver: + for abi in self.abi: + for arch in self.arch: + yield pyver, abi, arch + + @cached_property + def metadata(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + wrapper = codecs.getreader('utf-8') + with ZipFile(pathname, 'r') as zf: + wheel_metadata = self.get_wheel_metadata(zf) + wv = wheel_metadata['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + if file_version < (1, 1): + fn = 'METADATA' + else: + fn = METADATA_FILENAME + try: + metadata_filename = posixpath.join(info_dir, fn) + with zf.open(metadata_filename) as bf: + wf = wrapper(bf) + result = Metadata(fileobj=wf) + except KeyError: + raise ValueError('Invalid wheel, because %s is ' + 'missing' % fn) + return result + + def get_wheel_metadata(self, zf): + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + metadata_filename = posixpath.join(info_dir, 'WHEEL') + with zf.open(metadata_filename) as bf: + wf = codecs.getreader('utf-8')(bf) + message = message_from_file(wf) + return dict(message) + + @cached_property + def info(self): + pathname = os.path.join(self.dirname, self.filename) + with ZipFile(pathname, 'r') as zf: + result = self.get_wheel_metadata(zf) + return result + + def process_shebang(self, data): + m = SHEBANG_RE.match(data) + if m: + end = m.end() + shebang, data_after_shebang = data[:end], data[end:] + # Preserve any arguments after the interpreter + if b'pythonw' in shebang.lower(): + shebang_python = SHEBANG_PYTHONW + else: + shebang_python = SHEBANG_PYTHON + m = SHEBANG_DETAIL_RE.match(shebang) + if m: + args = b' ' + m.groups()[-1] + else: + args = b'' + shebang = shebang_python + args + data = shebang + data_after_shebang + else: + cr = data.find(b'\r') + lf = data.find(b'\n') + if cr < 0 or cr > lf: + term = b'\n' + else: + if data[cr:cr + 2] == b'\r\n': + term = b'\r\n' + else: + term = b'\r' + data = SHEBANG_PYTHON + term + data + return data + + def get_hash(self, data, hash_kind=None): + if hash_kind is None: + hash_kind = self.hash_kind + try: + hasher = getattr(hashlib, hash_kind) + except AttributeError: + raise DistlibException('Unsupported hash algorithm: %r' % hash_kind) + result = hasher(data).digest() + result = base64.urlsafe_b64encode(result).rstrip(b'=').decode('ascii') + return hash_kind, result + + def write_record(self, records, record_path, base): + records = list(records) # make a copy for sorting + p = to_posix(os.path.relpath(record_path, base)) + records.append((p, '', '')) + records.sort() + with CSVWriter(record_path) as writer: + for row in records: + writer.writerow(row) + + def write_records(self, info, libdir, archive_paths): + records = [] + distinfo, info_dir = info + hasher = getattr(hashlib, self.hash_kind) + for ap, p in archive_paths: + with open(p, 'rb') as f: + data = f.read() + digest = '%s=%s' % self.get_hash(data) + size = os.path.getsize(p) + records.append((ap, digest, size)) + + p = os.path.join(distinfo, 'RECORD') + self.write_record(records, p, libdir) + ap = to_posix(os.path.join(info_dir, 'RECORD')) + archive_paths.append((ap, p)) + + def build_zip(self, pathname, archive_paths): + with ZipFile(pathname, 'w', zipfile.ZIP_DEFLATED) as zf: + for ap, p in archive_paths: + logger.debug('Wrote %s to %s in wheel', p, ap) + zf.write(p, ap) + + def build(self, paths, tags=None, wheel_version=None): + """ + Build a wheel from files in specified paths, and use any specified tags + when determining the name of the wheel. + """ + if tags is None: + tags = {} + + libkey = list(filter(lambda o: o in paths, ('purelib', 'platlib')))[0] + if libkey == 'platlib': + is_pure = 'false' + default_pyver = [IMPVER] + default_abi = [ABI] + default_arch = [ARCH] + else: + is_pure = 'true' + default_pyver = [PYVER] + default_abi = ['none'] + default_arch = ['any'] + + self.pyver = tags.get('pyver', default_pyver) + self.abi = tags.get('abi', default_abi) + self.arch = tags.get('arch', default_arch) + + libdir = paths[libkey] + + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + archive_paths = [] + + # First, stuff which is not in site-packages + for key in ('data', 'headers', 'scripts'): + if key not in paths: + continue + path = paths[key] + if os.path.isdir(path): + for root, dirs, files in os.walk(path): + for fn in files: + p = fsdecode(os.path.join(root, fn)) + rp = os.path.relpath(p, path) + ap = to_posix(os.path.join(data_dir, key, rp)) + archive_paths.append((ap, p)) + if key == 'scripts' and not p.endswith('.exe'): + with open(p, 'rb') as f: + data = f.read() + data = self.process_shebang(data) + with open(p, 'wb') as f: + f.write(data) + + # Now, stuff which is in site-packages, other than the + # distinfo stuff. + path = libdir + distinfo = None + for root, dirs, files in os.walk(path): + if root == path: + # At the top level only, save distinfo for later + # and skip it for now + for i, dn in enumerate(dirs): + dn = fsdecode(dn) + if dn.endswith('.dist-info'): + distinfo = os.path.join(root, dn) + del dirs[i] + break + assert distinfo, '.dist-info directory expected, not found' + + for fn in files: + # comment out next suite to leave .pyc files in + if fsdecode(fn).endswith(('.pyc', '.pyo')): + continue + p = os.path.join(root, fn) + rp = to_posix(os.path.relpath(p, path)) + archive_paths.append((rp, p)) + + # Now distinfo. Assumed to be flat, i.e. os.listdir is enough. + files = os.listdir(distinfo) + for fn in files: + if fn not in ('RECORD', 'INSTALLER', 'SHARED', 'WHEEL'): + p = fsdecode(os.path.join(distinfo, fn)) + ap = to_posix(os.path.join(info_dir, fn)) + archive_paths.append((ap, p)) + + wheel_metadata = [ + 'Wheel-Version: %d.%d' % (wheel_version or self.wheel_version), + 'Generator: distlib %s' % __version__, + 'Root-Is-Purelib: %s' % is_pure, + ] + for pyver, abi, arch in self.tags: + wheel_metadata.append('Tag: %s-%s-%s' % (pyver, abi, arch)) + p = os.path.join(distinfo, 'WHEEL') + with open(p, 'w') as f: + f.write('\n'.join(wheel_metadata)) + ap = to_posix(os.path.join(info_dir, 'WHEEL')) + archive_paths.append((ap, p)) + + # Now, at last, RECORD. + # Paths in here are archive paths - nothing else makes sense. + self.write_records((distinfo, info_dir), libdir, archive_paths) + # Now, ready to build the zip file + pathname = os.path.join(self.dirname, self.filename) + self.build_zip(pathname, archive_paths) + return pathname + + def install(self, paths, maker, **kwargs): + """ + Install a wheel to the specified paths. If kwarg ``warner`` is + specified, it should be a callable, which will be called with two + tuples indicating the wheel version of this software and the wheel + version in the file, if there is a discrepancy in the versions. + This can be used to issue any warnings to raise any exceptions. + If kwarg ``lib_only`` is True, only the purelib/platlib files are + installed, and the headers, scripts, data and dist-info metadata are + not written. + + The return value is a :class:`InstalledDistribution` instance unless + ``options.lib_only`` is True, in which case the return value is ``None``. + """ + + dry_run = maker.dry_run + warner = kwargs.get('warner') + lib_only = kwargs.get('lib_only', False) + + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + metadata_name = posixpath.join(info_dir, METADATA_FILENAME) + wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') + record_name = posixpath.join(info_dir, 'RECORD') + + wrapper = codecs.getreader('utf-8') + + with ZipFile(pathname, 'r') as zf: + with zf.open(wheel_metadata_name) as bwf: + wf = wrapper(bwf) + message = message_from_file(wf) + wv = message['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + if (file_version != self.wheel_version) and warner: + warner(self.wheel_version, file_version) + + if message['Root-Is-Purelib'] == 'true': + libdir = paths['purelib'] + else: + libdir = paths['platlib'] + + records = {} + with zf.open(record_name) as bf: + with CSVReader(stream=bf) as reader: + for row in reader: + p = row[0] + records[p] = row + + data_pfx = posixpath.join(data_dir, '') + info_pfx = posixpath.join(info_dir, '') + script_pfx = posixpath.join(data_dir, 'scripts', '') + + # make a new instance rather than a copy of maker's, + # as we mutate it + fileop = FileOperator(dry_run=dry_run) + fileop.record = True # so we can rollback if needed + + bc = not sys.dont_write_bytecode # Double negatives. Lovely! + + outfiles = [] # for RECORD writing + + # for script copying/shebang processing + workdir = tempfile.mkdtemp() + # set target dir later + # we default add_launchers to False, as the + # Python Launcher should be used instead + maker.source_dir = workdir + maker.target_dir = None + try: + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + # The signature file won't be in RECORD, + # and we don't currently don't do anything with it + if u_arcname.endswith('/RECORD.jws'): + continue + row = records[u_arcname] + if row[2] and str(zinfo.file_size) != row[2]: + raise DistlibException('size mismatch for ' + '%s' % u_arcname) + if row[1]: + kind, value = row[1].split('=', 1) + with zf.open(arcname) as bf: + data = bf.read() + _, digest = self.get_hash(data, kind) + if digest != value: + raise DistlibException('digest mismatch for ' + '%s' % arcname) + + if lib_only and u_arcname.startswith((info_pfx, data_pfx)): + logger.debug('lib_only: skipping %s', u_arcname) + continue + is_script = (u_arcname.startswith(script_pfx) + and not u_arcname.endswith('.exe')) + + if u_arcname.startswith(data_pfx): + _, where, rp = u_arcname.split('/', 2) + outfile = os.path.join(paths[where], convert_path(rp)) + else: + # meant for site-packages. + if u_arcname in (wheel_metadata_name, record_name): + continue + outfile = os.path.join(libdir, convert_path(u_arcname)) + if not is_script: + with zf.open(arcname) as bf: + fileop.copy_stream(bf, outfile) + outfiles.append(outfile) + # Double check the digest of the written file + if not dry_run and row[1]: + with open(outfile, 'rb') as bf: + data = bf.read() + _, newdigest = self.get_hash(data, kind) + if newdigest != digest: + raise DistlibException('digest mismatch ' + 'on write for ' + '%s' % outfile) + if bc and outfile.endswith('.py'): + try: + pyc = fileop.byte_compile(outfile) + outfiles.append(pyc) + except Exception: + # Don't give up if byte-compilation fails, + # but log it and perhaps warn the user + logger.warning('Byte-compilation failed', + exc_info=True) + else: + fn = os.path.basename(convert_path(arcname)) + workname = os.path.join(workdir, fn) + with zf.open(arcname) as bf: + fileop.copy_stream(bf, workname) + + dn, fn = os.path.split(outfile) + maker.target_dir = dn + filenames = maker.make(fn) + fileop.set_executable_mode(filenames) + outfiles.extend(filenames) + + if lib_only: + logger.debug('lib_only: returning None') + dist = None + else: + # Generate scripts + + # Try to get pydist.json so we can see if there are + # any commands to generate. If this fails (e.g. because + # of a legacy wheel), log a warning but don't give up. + commands = None + file_version = self.info['Wheel-Version'] + if file_version == '1.0': + # Use legacy info + ep = posixpath.join(info_dir, 'entry_points.txt') + try: + with zf.open(ep) as bwf: + epdata = read_exports(bwf) + commands = {} + for key in ('console', 'gui'): + k = '%s_scripts' % key + if k in epdata: + commands['wrap_%s' % key] = d = {} + for v in epdata[k].values(): + s = '%s:%s' % (v.prefix, v.suffix) + if v.flags: + s += ' %s' % v.flags + d[v.name] = s + except Exception: + logger.warning('Unable to read legacy script ' + 'metadata, so cannot generate ' + 'scripts') + else: + try: + with zf.open(metadata_name) as bwf: + wf = wrapper(bwf) + commands = json.load(wf).get('extensions') + if commands: + commands = commands.get('python.commands') + except Exception: + logger.warning('Unable to read JSON metadata, so ' + 'cannot generate scripts') + if commands: + console_scripts = commands.get('wrap_console', {}) + gui_scripts = commands.get('wrap_gui', {}) + if console_scripts or gui_scripts: + script_dir = paths.get('scripts', '') + if not os.path.isdir(script_dir): + raise ValueError('Valid script path not ' + 'specified') + maker.target_dir = script_dir + for k, v in console_scripts.items(): + script = '%s = %s' % (k, v) + filenames = maker.make(script) + fileop.set_executable_mode(filenames) + + if gui_scripts: + options = {'gui': True } + for k, v in gui_scripts.items(): + script = '%s = %s' % (k, v) + filenames = maker.make(script, options) + fileop.set_executable_mode(filenames) + + p = os.path.join(libdir, info_dir) + dist = InstalledDistribution(p) + + # Write SHARED + paths = dict(paths) # don't change passed in dict + del paths['purelib'] + del paths['platlib'] + paths['lib'] = libdir + p = dist.write_shared_locations(paths, dry_run) + if p: + outfiles.append(p) + + # Write RECORD + dist.write_installed_files(outfiles, paths['prefix'], + dry_run) + return dist + except Exception: # pragma: no cover + logger.exception('installation failed.') + fileop.rollback() + raise + finally: + shutil.rmtree(workdir) + + def _get_dylib_cache(self): + global cache + if cache is None: + # Use native string to avoid issues on 2.x: see Python #20140. + base = os.path.join(get_cache_base(), str('dylib-cache'), + sys.version[:3]) + cache = Cache(base) + return cache + + def _get_extensions(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + arcname = posixpath.join(info_dir, 'EXTENSIONS') + wrapper = codecs.getreader('utf-8') + result = [] + with ZipFile(pathname, 'r') as zf: + try: + with zf.open(arcname) as bf: + wf = wrapper(bf) + extensions = json.load(wf) + cache = self._get_dylib_cache() + prefix = cache.prefix_to_dir(pathname) + cache_base = os.path.join(cache.base, prefix) + if not os.path.isdir(cache_base): + os.makedirs(cache_base) + for name, relpath in extensions.items(): + dest = os.path.join(cache_base, convert_path(relpath)) + if not os.path.exists(dest): + extract = True + else: + file_time = os.stat(dest).st_mtime + file_time = datetime.datetime.fromtimestamp(file_time) + info = zf.getinfo(relpath) + wheel_time = datetime.datetime(*info.date_time) + extract = wheel_time > file_time + if extract: + zf.extract(relpath, cache_base) + result.append((name, dest)) + except KeyError: + pass + return result + + def is_compatible(self): + """ + Determine if a wheel is compatible with the running system. + """ + return is_compatible(self) + + def is_mountable(self): + """ + Determine if a wheel is asserted as mountable by its metadata. + """ + return True # for now - metadata details TBD + + def mount(self, append=False): + pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) + if not self.is_compatible(): + msg = 'Wheel %s not compatible with this Python.' % pathname + raise DistlibException(msg) + if not self.is_mountable(): + msg = 'Wheel %s is marked as not mountable.' % pathname + raise DistlibException(msg) + if pathname in sys.path: + logger.debug('%s already in path', pathname) + else: + if append: + sys.path.append(pathname) + else: + sys.path.insert(0, pathname) + extensions = self._get_extensions() + if extensions: + if _hook not in sys.meta_path: + sys.meta_path.append(_hook) + _hook.add(pathname, extensions) + + def unmount(self): + pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) + if pathname not in sys.path: + logger.debug('%s not in path', pathname) + else: + sys.path.remove(pathname) + if pathname in _hook.impure_wheels: + _hook.remove(pathname) + if not _hook.impure_wheels: + if _hook in sys.meta_path: + sys.meta_path.remove(_hook) + + def verify(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + metadata_name = posixpath.join(info_dir, METADATA_FILENAME) + wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') + record_name = posixpath.join(info_dir, 'RECORD') + + wrapper = codecs.getreader('utf-8') + + with ZipFile(pathname, 'r') as zf: + with zf.open(wheel_metadata_name) as bwf: + wf = wrapper(bwf) + message = message_from_file(wf) + wv = message['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + # TODO version verification + + records = {} + with zf.open(record_name) as bf: + with CSVReader(stream=bf) as reader: + for row in reader: + p = row[0] + records[p] = row + + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + if '..' in u_arcname: + raise DistlibException('invalid entry in ' + 'wheel: %r' % u_arcname) + + # The signature file won't be in RECORD, + # and we don't currently don't do anything with it + if u_arcname.endswith('/RECORD.jws'): + continue + row = records[u_arcname] + if row[2] and str(zinfo.file_size) != row[2]: + raise DistlibException('size mismatch for ' + '%s' % u_arcname) + if row[1]: + kind, value = row[1].split('=', 1) + with zf.open(arcname) as bf: + data = bf.read() + _, digest = self.get_hash(data, kind) + if digest != value: + raise DistlibException('digest mismatch for ' + '%s' % arcname) + + def update(self, modifier, dest_dir=None, **kwargs): + """ + Update the contents of a wheel in a generic way. The modifier should + be a callable which expects a dictionary argument: its keys are + archive-entry paths, and its values are absolute filesystem paths + where the contents the corresponding archive entries can be found. The + modifier is free to change the contents of the files pointed to, add + new entries and remove entries, before returning. This method will + extract the entire contents of the wheel to a temporary location, call + the modifier, and then use the passed (and possibly updated) + dictionary to write a new wheel. If ``dest_dir`` is specified, the new + wheel is written there -- otherwise, the original wheel is overwritten. + + The modifier should return True if it updated the wheel, else False. + This method returns the same value the modifier returns. + """ + + def get_version(path_map, info_dir): + version = path = None + key = '%s/%s' % (info_dir, METADATA_FILENAME) + if key not in path_map: + key = '%s/PKG-INFO' % info_dir + if key in path_map: + path = path_map[key] + version = Metadata(path=path).version + return version, path + + def update_version(version, path): + updated = None + try: + v = NormalizedVersion(version) + i = version.find('-') + if i < 0: + updated = '%s+1' % version + else: + parts = [int(s) for s in version[i + 1:].split('.')] + parts[-1] += 1 + updated = '%s+%s' % (version[:i], + '.'.join(str(i) for i in parts)) + except UnsupportedVersionError: + logger.debug('Cannot update non-compliant (PEP-440) ' + 'version %r', version) + if updated: + md = Metadata(path=path) + md.version = updated + legacy = not path.endswith(METADATA_FILENAME) + md.write(path=path, legacy=legacy) + logger.debug('Version updated from %r to %r', version, + updated) + + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + record_name = posixpath.join(info_dir, 'RECORD') + with tempdir() as workdir: + with ZipFile(pathname, 'r') as zf: + path_map = {} + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + if u_arcname == record_name: + continue + if '..' in u_arcname: + raise DistlibException('invalid entry in ' + 'wheel: %r' % u_arcname) + zf.extract(zinfo, workdir) + path = os.path.join(workdir, convert_path(u_arcname)) + path_map[u_arcname] = path + + # Remember the version. + original_version, _ = get_version(path_map, info_dir) + # Files extracted. Call the modifier. + modified = modifier(path_map, **kwargs) + if modified: + # Something changed - need to build a new wheel. + current_version, path = get_version(path_map, info_dir) + if current_version and (current_version == original_version): + # Add or update local version to signify changes. + update_version(current_version, path) + # Decide where the new wheel goes. + if dest_dir is None: + fd, newpath = tempfile.mkstemp(suffix='.whl', + prefix='wheel-update-', + dir=workdir) + os.close(fd) + else: + if not os.path.isdir(dest_dir): + raise DistlibException('Not a directory: %r' % dest_dir) + newpath = os.path.join(dest_dir, self.filename) + archive_paths = list(path_map.items()) + distinfo = os.path.join(workdir, info_dir) + info = distinfo, info_dir + self.write_records(info, workdir, archive_paths) + self.build_zip(newpath, archive_paths) + if dest_dir is None: + shutil.copyfile(newpath, pathname) + return modified + +def compatible_tags(): + """ + Return (pyver, abi, arch) tuples compatible with this Python. + """ + versions = [VER_SUFFIX] + major = VER_SUFFIX[0] + for minor in range(sys.version_info[1] - 1, - 1, -1): + versions.append(''.join([major, str(minor)])) + + abis = [] + for suffix, _, _ in imp.get_suffixes(): + if suffix.startswith('.abi'): + abis.append(suffix.split('.', 2)[1]) + abis.sort() + if ABI != 'none': + abis.insert(0, ABI) + abis.append('none') + result = [] + + arches = [ARCH] + if sys.platform == 'darwin': + m = re.match('(\w+)_(\d+)_(\d+)_(\w+)$', ARCH) + if m: + name, major, minor, arch = m.groups() + minor = int(minor) + matches = [arch] + if arch in ('i386', 'ppc'): + matches.append('fat') + if arch in ('i386', 'ppc', 'x86_64'): + matches.append('fat3') + if arch in ('ppc64', 'x86_64'): + matches.append('fat64') + if arch in ('i386', 'x86_64'): + matches.append('intel') + if arch in ('i386', 'x86_64', 'intel', 'ppc', 'ppc64'): + matches.append('universal') + while minor >= 0: + for match in matches: + s = '%s_%s_%s_%s' % (name, major, minor, match) + if s != ARCH: # already there + arches.append(s) + minor -= 1 + + # Most specific - our Python version, ABI and arch + for abi in abis: + for arch in arches: + result.append((''.join((IMP_PREFIX, versions[0])), abi, arch)) + + # where no ABI / arch dependency, but IMP_PREFIX dependency + for i, version in enumerate(versions): + result.append((''.join((IMP_PREFIX, version)), 'none', 'any')) + if i == 0: + result.append((''.join((IMP_PREFIX, version[0])), 'none', 'any')) + + # no IMP_PREFIX, ABI or arch dependency + for i, version in enumerate(versions): + result.append((''.join(('py', version)), 'none', 'any')) + if i == 0: + result.append((''.join(('py', version[0])), 'none', 'any')) + return set(result) + + +COMPATIBLE_TAGS = compatible_tags() + +del compatible_tags + + +def is_compatible(wheel, tags=None): + if not isinstance(wheel, Wheel): + wheel = Wheel(wheel) # assume it's a filename + result = False + if tags is None: + tags = COMPATIBLE_TAGS + for ver, abi, arch in tags: + if ver in wheel.pyver and abi in wheel.abi and arch in wheel.arch: + result = True + break + return result diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distro.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distro.py new file mode 100644 index 0000000..9e7daad --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/distro.py @@ -0,0 +1,1081 @@ +# Copyright 2015,2016 Nir Cohen +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +The ``distro`` package (``distro`` stands for Linux Distribution) provides +information about the Linux distribution it runs on, such as a reliable +machine-readable distro ID, or version information. + +It is a renewed alternative implementation for Python's original +:py:func:`platform.linux_distribution` function, but it provides much more +functionality. An alternative implementation became necessary because Python +3.5 deprecated this function, and Python 3.7 is expected to remove it +altogether. Its predecessor function :py:func:`platform.dist` was already +deprecated since Python 2.6 and is also expected to be removed in Python 3.7. +Still, there are many cases in which access to Linux distribution information +is needed. See `Python issue 1322 <https://bugs.python.org/issue1322>`_ for +more information. +""" + +import os +import re +import sys +import json +import shlex +import logging +import subprocess + + +if not sys.platform.startswith('linux'): + raise ImportError('Unsupported platform: {0}'.format(sys.platform)) + +_UNIXCONFDIR = '/etc' +_OS_RELEASE_BASENAME = 'os-release' + +#: Translation table for normalizing the "ID" attribute defined in os-release +#: files, for use by the :func:`distro.id` method. +#: +#: * Key: Value as defined in the os-release file, translated to lower case, +#: with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_OS_ID = {} + +#: Translation table for normalizing the "Distributor ID" attribute returned by +#: the lsb_release command, for use by the :func:`distro.id` method. +#: +#: * Key: Value as returned by the lsb_release command, translated to lower +#: case, with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_LSB_ID = { + 'enterpriseenterprise': 'oracle', # Oracle Enterprise Linux + 'redhatenterpriseworkstation': 'rhel', # RHEL 6.7 +} + +#: Translation table for normalizing the distro ID derived from the file name +#: of distro release files, for use by the :func:`distro.id` method. +#: +#: * Key: Value as derived from the file name of a distro release file, +#: translated to lower case, with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_DISTRO_ID = { + 'redhat': 'rhel', # RHEL 6.x, 7.x +} + +# Pattern for content of distro release file (reversed) +_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( + r'(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)') + +# Pattern for base file name of distro release file +_DISTRO_RELEASE_BASENAME_PATTERN = re.compile( + r'(\w+)[-_](release|version)$') + +# Base file names to be ignored when searching for distro release file +_DISTRO_RELEASE_IGNORE_BASENAMES = ( + 'debian_version', + 'lsb-release', + 'oem-release', + _OS_RELEASE_BASENAME, + 'system-release' +) + + +def linux_distribution(full_distribution_name=True): + """ + Return information about the current Linux distribution as a tuple + ``(id_name, version, codename)`` with items as follows: + + * ``id_name``: If *full_distribution_name* is false, the result of + :func:`distro.id`. Otherwise, the result of :func:`distro.name`. + + * ``version``: The result of :func:`distro.version`. + + * ``codename``: The result of :func:`distro.codename`. + + The interface of this function is compatible with the original + :py:func:`platform.linux_distribution` function, supporting a subset of + its parameters. + + The data it returns may not exactly be the same, because it uses more data + sources than the original function, and that may lead to different data if + the Linux distribution is not consistent across multiple data sources it + provides (there are indeed such distributions ...). + + Another reason for differences is the fact that the :func:`distro.id` + method normalizes the distro ID string to a reliable machine-readable value + for a number of popular Linux distributions. + """ + return _distro.linux_distribution(full_distribution_name) + + +def id(): + """ + Return the distro ID of the current Linux distribution, as a + machine-readable string. + + For a number of Linux distributions, the returned distro ID value is + *reliable*, in the sense that it is documented and that it does not change + across releases of the distribution. + + This package maintains the following reliable distro ID values: + + ============== ========================================= + Distro ID Distribution + ============== ========================================= + "ubuntu" Ubuntu + "debian" Debian + "rhel" RedHat Enterprise Linux + "centos" CentOS + "fedora" Fedora + "sles" SUSE Linux Enterprise Server + "opensuse" openSUSE + "amazon" Amazon Linux + "arch" Arch Linux + "cloudlinux" CloudLinux OS + "exherbo" Exherbo Linux + "gentoo" GenToo Linux + "ibm_powerkvm" IBM PowerKVM + "kvmibm" KVM for IBM z Systems + "linuxmint" Linux Mint + "mageia" Mageia + "mandriva" Mandriva Linux + "parallels" Parallels + "pidora" Pidora + "raspbian" Raspbian + "oracle" Oracle Linux (and Oracle Enterprise Linux) + "scientific" Scientific Linux + "slackware" Slackware + "xenserver" XenServer + ============== ========================================= + + If you have a need to get distros for reliable IDs added into this set, + or if you find that the :func:`distro.id` function returns a different + distro ID for one of the listed distros, please create an issue in the + `distro issue tracker`_. + + **Lookup hierarchy and transformations:** + + First, the ID is obtained from the following sources, in the specified + order. The first available and non-empty value is used: + + * the value of the "ID" attribute of the os-release file, + + * the value of the "Distributor ID" attribute returned by the lsb_release + command, + + * the first part of the file name of the distro release file, + + The so determined ID value then passes the following transformations, + before it is returned by this method: + + * it is translated to lower case, + + * blanks (which should not be there anyway) are translated to underscores, + + * a normalization of the ID is performed, based upon + `normalization tables`_. The purpose of this normalization is to ensure + that the ID is as reliable as possible, even across incompatible changes + in the Linux distributions. A common reason for an incompatible change is + the addition of an os-release file, or the addition of the lsb_release + command, with ID values that differ from what was previously determined + from the distro release file name. + """ + return _distro.id() + + +def name(pretty=False): + """ + Return the name of the current Linux distribution, as a human-readable + string. + + If *pretty* is false, the name is returned without version or codename. + (e.g. "CentOS Linux") + + If *pretty* is true, the version and codename are appended. + (e.g. "CentOS Linux 7.1.1503 (Core)") + + **Lookup hierarchy:** + + The name is obtained from the following sources, in the specified order. + The first available and non-empty value is used: + + * If *pretty* is false: + + - the value of the "NAME" attribute of the os-release file, + + - the value of the "Distributor ID" attribute returned by the lsb_release + command, + + - the value of the "<name>" field of the distro release file. + + * If *pretty* is true: + + - the value of the "PRETTY_NAME" attribute of the os-release file, + + - the value of the "Description" attribute returned by the lsb_release + command, + + - the value of the "<name>" field of the distro release file, appended + with the value of the pretty version ("<version_id>" and "<codename>" + fields) of the distro release file, if available. + """ + return _distro.name(pretty) + + +def version(pretty=False, best=False): + """ + Return the version of the current Linux distribution, as a human-readable + string. + + If *pretty* is false, the version is returned without codename (e.g. + "7.0"). + + If *pretty* is true, the codename in parenthesis is appended, if the + codename is non-empty (e.g. "7.0 (Maipo)"). + + Some distributions provide version numbers with different precisions in + the different sources of distribution information. Examining the different + sources in a fixed priority order does not always yield the most precise + version (e.g. for Debian 8.2, or CentOS 7.1). + + The *best* parameter can be used to control the approach for the returned + version: + + If *best* is false, the first non-empty version number in priority order of + the examined sources is returned. + + If *best* is true, the most precise version number out of all examined + sources is returned. + + **Lookup hierarchy:** + + In all cases, the version number is obtained from the following sources. + If *best* is false, this order represents the priority order: + + * the value of the "VERSION_ID" attribute of the os-release file, + * the value of the "Release" attribute returned by the lsb_release + command, + * the version number parsed from the "<version_id>" field of the first line + of the distro release file, + * the version number parsed from the "PRETTY_NAME" attribute of the + os-release file, if it follows the format of the distro release files. + * the version number parsed from the "Description" attribute returned by + the lsb_release command, if it follows the format of the distro release + files. + """ + return _distro.version(pretty, best) + + +def version_parts(best=False): + """ + Return the version of the current Linux distribution as a tuple + ``(major, minor, build_number)`` with items as follows: + + * ``major``: The result of :func:`distro.major_version`. + + * ``minor``: The result of :func:`distro.minor_version`. + + * ``build_number``: The result of :func:`distro.build_number`. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.version_parts(best) + + +def major_version(best=False): + """ + Return the major version of the current Linux distribution, as a string, + if provided. + Otherwise, the empty string is returned. The major version is the first + part of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.major_version(best) + + +def minor_version(best=False): + """ + Return the minor version of the current Linux distribution, as a string, + if provided. + Otherwise, the empty string is returned. The minor version is the second + part of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.minor_version(best) + + +def build_number(best=False): + """ + Return the build number of the current Linux distribution, as a string, + if provided. + Otherwise, the empty string is returned. The build number is the third part + of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.build_number(best) + + +def like(): + """ + Return a space-separated list of distro IDs of distributions that are + closely related to the current Linux distribution in regards to packaging + and programming interfaces, for example distributions the current + distribution is a derivative from. + + **Lookup hierarchy:** + + This information item is only provided by the os-release file. + For details, see the description of the "ID_LIKE" attribute in the + `os-release man page + <http://www.freedesktop.org/software/systemd/man/os-release.html>`_. + """ + return _distro.like() + + +def codename(): + """ + Return the codename for the release of the current Linux distribution, + as a string. + + If the distribution does not have a codename, an empty string is returned. + + Note that the returned codename is not always really a codename. For + example, openSUSE returns "x86_64". This function does not handle such + cases in any special way and just returns the string it finds, if any. + + **Lookup hierarchy:** + + * the codename within the "VERSION" attribute of the os-release file, if + provided, + + * the value of the "Codename" attribute returned by the lsb_release + command, + + * the value of the "<codename>" field of the distro release file. + """ + return _distro.codename() + + +def info(pretty=False, best=False): + """ + Return certain machine-readable information items about the current Linux + distribution in a dictionary, as shown in the following example: + + .. sourcecode:: python + + { + 'id': 'rhel', + 'version': '7.0', + 'version_parts': { + 'major': '7', + 'minor': '0', + 'build_number': '' + }, + 'like': 'fedora', + 'codename': 'Maipo' + } + + The dictionary structure and keys are always the same, regardless of which + information items are available in the underlying data sources. The values + for the various keys are as follows: + + * ``id``: The result of :func:`distro.id`. + + * ``version``: The result of :func:`distro.version`. + + * ``version_parts -> major``: The result of :func:`distro.major_version`. + + * ``version_parts -> minor``: The result of :func:`distro.minor_version`. + + * ``version_parts -> build_number``: The result of + :func:`distro.build_number`. + + * ``like``: The result of :func:`distro.like`. + + * ``codename``: The result of :func:`distro.codename`. + + For a description of the *pretty* and *best* parameters, see the + :func:`distro.version` method. + """ + return _distro.info(pretty, best) + + +def os_release_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the os-release file data source of the current Linux distribution. + + See `os-release file`_ for details about these information items. + """ + return _distro.os_release_info() + + +def lsb_release_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the lsb_release command data source of the current Linux distribution. + + See `lsb_release command output`_ for details about these information + items. + """ + return _distro.lsb_release_info() + + +def distro_release_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the distro release file data source of the current Linux distribution. + + See `distro release file`_ for details about these information items. + """ + return _distro.distro_release_info() + + +def os_release_attr(attribute): + """ + Return a single named information item from the os-release file data source + of the current Linux distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `os-release file`_ for details about these information items. + """ + return _distro.os_release_attr(attribute) + + +def lsb_release_attr(attribute): + """ + Return a single named information item from the lsb_release command output + data source of the current Linux distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `lsb_release command output`_ for details about these information + items. + """ + return _distro.lsb_release_attr(attribute) + + +def distro_release_attr(attribute): + """ + Return a single named information item from the distro release file + data source of the current Linux distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `distro release file`_ for details about these information items. + """ + return _distro.distro_release_attr(attribute) + + +class LinuxDistribution(object): + """ + Provides information about a Linux distribution. + + This package creates a private module-global instance of this class with + default initialization arguments, that is used by the + `consolidated accessor functions`_ and `single source accessor functions`_. + By using default initialization arguments, that module-global instance + returns data about the current Linux distribution (i.e. the distro this + package runs on). + + Normally, it is not necessary to create additional instances of this class. + However, in situations where control is needed over the exact data sources + that are used, instances of this class can be created with a specific + distro release file, or a specific os-release file, or without invoking the + lsb_release command. + """ + + def __init__(self, + include_lsb=True, + os_release_file='', + distro_release_file=''): + """ + The initialization method of this class gathers information from the + available data sources, and stores that in private instance attributes. + Subsequent access to the information items uses these private instance + attributes, so that the data sources are read only once. + + Parameters: + + * ``include_lsb`` (bool): Controls whether the + `lsb_release command output`_ is included as a data source. + + If the lsb_release command is not available in the program execution + path, the data source for the lsb_release command will be empty. + + * ``os_release_file`` (string): The path name of the + `os-release file`_ that is to be used as a data source. + + An empty string (the default) will cause the default path name to + be used (see `os-release file`_ for details). + + If the specified or defaulted os-release file does not exist, the + data source for the os-release file will be empty. + + * ``distro_release_file`` (string): The path name of the + `distro release file`_ that is to be used as a data source. + + An empty string (the default) will cause a default search algorithm + to be used (see `distro release file`_ for details). + + If the specified distro release file does not exist, or if no default + distro release file can be found, the data source for the distro + release file will be empty. + + Public instance attributes: + + * ``os_release_file`` (string): The path name of the + `os-release file`_ that is actually used as a data source. The + empty string if no distro release file is used as a data source. + + * ``distro_release_file`` (string): The path name of the + `distro release file`_ that is actually used as a data source. The + empty string if no distro release file is used as a data source. + + Raises: + + * :py:exc:`IOError`: Some I/O issue with an os-release file or distro + release file. + + * :py:exc:`subprocess.CalledProcessError`: The lsb_release command had + some issue (other than not being available in the program execution + path). + + * :py:exc:`UnicodeError`: A data source has unexpected characters or + uses an unexpected encoding. + """ + self.os_release_file = os_release_file or \ + os.path.join(_UNIXCONFDIR, _OS_RELEASE_BASENAME) + self.distro_release_file = distro_release_file or '' # updated later + self._os_release_info = self._get_os_release_info() + self._lsb_release_info = self._get_lsb_release_info() \ + if include_lsb else {} + self._distro_release_info = self._get_distro_release_info() + + def __repr__(self): + """Return repr of all info + """ + return \ + "LinuxDistribution(" \ + "os_release_file={0!r}, " \ + "distro_release_file={1!r}, " \ + "_os_release_info={2!r}, " \ + "_lsb_release_info={3!r}, " \ + "_distro_release_info={4!r})".format( + self.os_release_file, + self.distro_release_file, + self._os_release_info, + self._lsb_release_info, + self._distro_release_info) + + def linux_distribution(self, full_distribution_name=True): + """ + Return information about the Linux distribution that is compatible + with Python's :func:`platform.linux_distribution`, supporting a subset + of its parameters. + + For details, see :func:`distro.linux_distribution`. + """ + return ( + self.name() if full_distribution_name else self.id(), + self.version(), + self.codename() + ) + + def id(self): + """Return the distro ID of the Linux distribution, as a string. + + For details, see :func:`distro.id`. + """ + def normalize(distro_id, table): + distro_id = distro_id.lower().replace(' ', '_') + return table.get(distro_id, distro_id) + + distro_id = self.os_release_attr('id') + if distro_id: + return normalize(distro_id, NORMALIZED_OS_ID) + + distro_id = self.lsb_release_attr('distributor_id') + if distro_id: + return normalize(distro_id, NORMALIZED_LSB_ID) + + distro_id = self.distro_release_attr('id') + if distro_id: + return normalize(distro_id, NORMALIZED_DISTRO_ID) + + return '' + + def name(self, pretty=False): + """ + Return the name of the Linux distribution, as a string. + + For details, see :func:`distro.name`. + """ + name = self.os_release_attr('name') \ + or self.lsb_release_attr('distributor_id') \ + or self.distro_release_attr('name') + if pretty: + name = self.os_release_attr('pretty_name') \ + or self.lsb_release_attr('description') + if not name: + name = self.distro_release_attr('name') + version = self.version(pretty=True) + if version: + name = name + ' ' + version + return name or '' + + def version(self, pretty=False, best=False): + """ + Return the version of the Linux distribution, as a string. + + For details, see :func:`distro.version`. + """ + versions = [ + self.os_release_attr('version_id'), + self.lsb_release_attr('release'), + self.distro_release_attr('version_id'), + self._parse_distro_release_content( + self.os_release_attr('pretty_name')).get('version_id', ''), + self._parse_distro_release_content( + self.lsb_release_attr('description')).get('version_id', '') + ] + version = '' + if best: + # This algorithm uses the last version in priority order that has + # the best precision. If the versions are not in conflict, that + # does not matter; otherwise, using the last one instead of the + # first one might be considered a surprise. + for v in versions: + if v.count(".") > version.count(".") or version == '': + version = v + else: + for v in versions: + if v != '': + version = v + break + if pretty and version and self.codename(): + version = u'{0} ({1})'.format(version, self.codename()) + return version + + def version_parts(self, best=False): + """ + Return the version of the Linux distribution, as a tuple of version + numbers. + + For details, see :func:`distro.version_parts`. + """ + version_str = self.version(best=best) + if version_str: + version_regex = re.compile(r'(\d+)\.?(\d+)?\.?(\d+)?') + matches = version_regex.match(version_str) + if matches: + major, minor, build_number = matches.groups() + return major, minor or '', build_number or '' + return '', '', '' + + def major_version(self, best=False): + """ + Return the major version number of the current distribution. + + For details, see :func:`distro.major_version`. + """ + return self.version_parts(best)[0] + + def minor_version(self, best=False): + """ + Return the minor version number of the Linux distribution. + + For details, see :func:`distro.minor_version`. + """ + return self.version_parts(best)[1] + + def build_number(self, best=False): + """ + Return the build number of the Linux distribution. + + For details, see :func:`distro.build_number`. + """ + return self.version_parts(best)[2] + + def like(self): + """ + Return the IDs of distributions that are like the Linux distribution. + + For details, see :func:`distro.like`. + """ + return self.os_release_attr('id_like') or '' + + def codename(self): + """ + Return the codename of the Linux distribution. + + For details, see :func:`distro.codename`. + """ + return self.os_release_attr('codename') \ + or self.lsb_release_attr('codename') \ + or self.distro_release_attr('codename') \ + or '' + + def info(self, pretty=False, best=False): + """ + Return certain machine-readable information about the Linux + distribution. + + For details, see :func:`distro.info`. + """ + return dict( + id=self.id(), + version=self.version(pretty, best), + version_parts=dict( + major=self.major_version(best), + minor=self.minor_version(best), + build_number=self.build_number(best) + ), + like=self.like(), + codename=self.codename(), + ) + + def os_release_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the os-release file data source of the Linux distribution. + + For details, see :func:`distro.os_release_info`. + """ + return self._os_release_info + + def lsb_release_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the lsb_release command data source of the Linux + distribution. + + For details, see :func:`distro.lsb_release_info`. + """ + return self._lsb_release_info + + def distro_release_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the distro release file data source of the Linux + distribution. + + For details, see :func:`distro.distro_release_info`. + """ + return self._distro_release_info + + def os_release_attr(self, attribute): + """ + Return a single named information item from the os-release file data + source of the Linux distribution. + + For details, see :func:`distro.os_release_attr`. + """ + return self._os_release_info.get(attribute, '') + + def lsb_release_attr(self, attribute): + """ + Return a single named information item from the lsb_release command + output data source of the Linux distribution. + + For details, see :func:`distro.lsb_release_attr`. + """ + return self._lsb_release_info.get(attribute, '') + + def distro_release_attr(self, attribute): + """ + Return a single named information item from the distro release file + data source of the Linux distribution. + + For details, see :func:`distro.distro_release_attr`. + """ + return self._distro_release_info.get(attribute, '') + + def _get_os_release_info(self): + """ + Get the information items from the specified os-release file. + + Returns: + A dictionary containing all information items. + """ + if os.path.isfile(self.os_release_file): + with open(self.os_release_file) as release_file: + return self._parse_os_release_content(release_file) + return {} + + @staticmethod + def _parse_os_release_content(lines): + """ + Parse the lines of an os-release file. + + Parameters: + + * lines: Iterable through the lines in the os-release file. + Each line must be a unicode string or a UTF-8 encoded byte + string. + + Returns: + A dictionary containing all information items. + """ + props = {} + lexer = shlex.shlex(lines, posix=True) + lexer.whitespace_split = True + + # The shlex module defines its `wordchars` variable using literals, + # making it dependent on the encoding of the Python source file. + # In Python 2.6 and 2.7, the shlex source file is encoded in + # 'iso-8859-1', and the `wordchars` variable is defined as a byte + # string. This causes a UnicodeDecodeError to be raised when the + # parsed content is a unicode object. The following fix resolves that + # (... but it should be fixed in shlex...): + if sys.version_info[0] == 2 and isinstance(lexer.wordchars, bytes): + lexer.wordchars = lexer.wordchars.decode('iso-8859-1') + + tokens = list(lexer) + for token in tokens: + # At this point, all shell-like parsing has been done (i.e. + # comments processed, quotes and backslash escape sequences + # processed, multi-line values assembled, trailing newlines + # stripped, etc.), so the tokens are now either: + # * variable assignments: var=value + # * commands or their arguments (not allowed in os-release) + if '=' in token: + k, v = token.split('=', 1) + if isinstance(v, bytes): + v = v.decode('utf-8') + props[k.lower()] = v + if k == 'VERSION': + # this handles cases in which the codename is in + # the `(CODENAME)` (rhel, centos, fedora) format + # or in the `, CODENAME` format (Ubuntu). + codename = re.search(r'(\(\D+\))|,(\s+)?\D+', v) + if codename: + codename = codename.group() + codename = codename.strip('()') + codename = codename.strip(',') + codename = codename.strip() + # codename appears within paranthese. + props['codename'] = codename + else: + props['codename'] = '' + else: + # Ignore any tokens that are not variable assignments + pass + return props + + def _get_lsb_release_info(self): + """ + Get the information items from the lsb_release command output. + + Returns: + A dictionary containing all information items. + """ + cmd = 'lsb_release -a' + process = subprocess.Popen( + cmd, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + stdout, stderr = stdout.decode('utf-8'), stderr.decode('utf-8') + code = process.returncode + if code == 0: + content = stdout.splitlines() + return self._parse_lsb_release_content(content) + elif code == 127: # Command not found + return {} + else: + if sys.version_info[:2] >= (3, 5): + raise subprocess.CalledProcessError(code, cmd, stdout, stderr) + elif sys.version_info[:2] >= (2, 7): + raise subprocess.CalledProcessError(code, cmd, stdout) + elif sys.version_info[:2] == (2, 6): + raise subprocess.CalledProcessError(code, cmd) + + @staticmethod + def _parse_lsb_release_content(lines): + """ + Parse the output of the lsb_release command. + + Parameters: + + * lines: Iterable through the lines of the lsb_release output. + Each line must be a unicode string or a UTF-8 encoded byte + string. + + Returns: + A dictionary containing all information items. + """ + props = {} + for line in lines: + line = line.decode('utf-8') if isinstance(line, bytes) else line + kv = line.strip('\n').split(':', 1) + if len(kv) != 2: + # Ignore lines without colon. + continue + k, v = kv + props.update({k.replace(' ', '_').lower(): v.strip()}) + return props + + def _get_distro_release_info(self): + """ + Get the information items from the specified distro release file. + + Returns: + A dictionary containing all information items. + """ + if self.distro_release_file: + # If it was specified, we use it and parse what we can, even if + # its file name or content does not match the expected pattern. + distro_info = self._parse_distro_release_file( + self.distro_release_file) + basename = os.path.basename(self.distro_release_file) + # The file name pattern for user-specified distro release files + # is somewhat more tolerant (compared to when searching for the + # file), because we want to use what was specified as best as + # possible. + match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) + if match: + distro_info['id'] = match.group(1) + return distro_info + else: + basenames = os.listdir(_UNIXCONFDIR) + # We sort for repeatability in cases where there are multiple + # distro specific files; e.g. CentOS, Oracle, Enterprise all + # containing `redhat-release` on top of their own. + basenames.sort() + for basename in basenames: + if basename in _DISTRO_RELEASE_IGNORE_BASENAMES: + continue + match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) + if match: + filepath = os.path.join(_UNIXCONFDIR, basename) + distro_info = self._parse_distro_release_file(filepath) + if 'name' in distro_info: + # The name is always present if the pattern matches + self.distro_release_file = filepath + distro_info['id'] = match.group(1) + return distro_info + return {} + + def _parse_distro_release_file(self, filepath): + """ + Parse a distro release file. + + Parameters: + + * filepath: Path name of the distro release file. + + Returns: + A dictionary containing all information items. + """ + if os.path.isfile(filepath): + with open(filepath) as fp: + # Only parse the first line. For instance, on SLES there + # are multiple lines. We don't want them... + return self._parse_distro_release_content(fp.readline()) + return {} + + @staticmethod + def _parse_distro_release_content(line): + """ + Parse a line from a distro release file. + + Parameters: + * line: Line from the distro release file. Must be a unicode string + or a UTF-8 encoded byte string. + + Returns: + A dictionary containing all information items. + """ + if isinstance(line, bytes): + line = line.decode('utf-8') + matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match( + line.strip()[::-1]) + distro_info = {} + if matches: + # regexp ensures non-None + distro_info['name'] = matches.group(3)[::-1] + if matches.group(2): + distro_info['version_id'] = matches.group(2)[::-1] + if matches.group(1): + distro_info['codename'] = matches.group(1)[::-1] + elif line: + distro_info['name'] = line.strip() + return distro_info + + +_distro = LinuxDistribution() + + +def main(): + import argparse + + logger = logging.getLogger(__name__) + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler(sys.stdout)) + + parser = argparse.ArgumentParser(description="Linux distro info tool") + parser.add_argument( + '--json', + '-j', + help="Output in machine readable format", + action="store_true") + args = parser.parse_args() + + if args.json: + logger.info(json.dumps(info(), indent=4, sort_keys=True)) + else: + logger.info('Name: %s', name(pretty=True)) + distribution_version = version(pretty=True) + if distribution_version: + logger.info('Version: %s', distribution_version) + distribution_codename = codename() + if distribution_codename: + logger.info('Codename: %s', distribution_codename) + + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/__init__.py new file mode 100644 index 0000000..7427eb1 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/__init__.py @@ -0,0 +1,25 @@ +""" +HTML parsing library based on the WHATWG "HTML5" +specification. The parser is designed to be compatible with existing +HTML found in the wild and implements well-defined error recovery that +is largely compatible with modern desktop web browsers. + +Example usage: + +import html5lib +f = open("my_document.html") +tree = html5lib.parse(f) +""" + +from __future__ import absolute_import, division, unicode_literals + +from .html5parser import HTMLParser, parse, parseFragment +from .treebuilders import getTreeBuilder +from .treewalkers import getTreeWalker +from .serializer import serialize + +__all__ = ["HTMLParser", "parse", "parseFragment", "getTreeBuilder", + "getTreeWalker", "serialize"] + +# this has to be at the top level, see how setup.py parses this +__version__ = "1.0b10" diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_ihatexml.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_ihatexml.py new file mode 100644 index 0000000..d6d1d6f --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_ihatexml.py @@ -0,0 +1,288 @@ +from __future__ import absolute_import, division, unicode_literals + +import re +import warnings + +from .constants import DataLossWarning + +baseChar = """ +[#x0041-#x005A] | [#x0061-#x007A] | [#x00C0-#x00D6] | [#x00D8-#x00F6] | +[#x00F8-#x00FF] | [#x0100-#x0131] | [#x0134-#x013E] | [#x0141-#x0148] | +[#x014A-#x017E] | [#x0180-#x01C3] | [#x01CD-#x01F0] | [#x01F4-#x01F5] | +[#x01FA-#x0217] | [#x0250-#x02A8] | [#x02BB-#x02C1] | #x0386 | +[#x0388-#x038A] | #x038C | [#x038E-#x03A1] | [#x03A3-#x03CE] | +[#x03D0-#x03D6] | #x03DA | #x03DC | #x03DE | #x03E0 | [#x03E2-#x03F3] | +[#x0401-#x040C] | [#x040E-#x044F] | [#x0451-#x045C] | [#x045E-#x0481] | +[#x0490-#x04C4] | [#x04C7-#x04C8] | [#x04CB-#x04CC] | [#x04D0-#x04EB] | +[#x04EE-#x04F5] | [#x04F8-#x04F9] | [#x0531-#x0556] | #x0559 | +[#x0561-#x0586] | [#x05D0-#x05EA] | [#x05F0-#x05F2] | [#x0621-#x063A] | +[#x0641-#x064A] | [#x0671-#x06B7] | [#x06BA-#x06BE] | [#x06C0-#x06CE] | +[#x06D0-#x06D3] | #x06D5 | [#x06E5-#x06E6] | [#x0905-#x0939] | #x093D | +[#x0958-#x0961] | [#x0985-#x098C] | [#x098F-#x0990] | [#x0993-#x09A8] | +[#x09AA-#x09B0] | #x09B2 | [#x09B6-#x09B9] | [#x09DC-#x09DD] | +[#x09DF-#x09E1] | [#x09F0-#x09F1] | [#x0A05-#x0A0A] | [#x0A0F-#x0A10] | +[#x0A13-#x0A28] | [#x0A2A-#x0A30] | [#x0A32-#x0A33] | [#x0A35-#x0A36] | +[#x0A38-#x0A39] | [#x0A59-#x0A5C] | #x0A5E | [#x0A72-#x0A74] | +[#x0A85-#x0A8B] | #x0A8D | [#x0A8F-#x0A91] | [#x0A93-#x0AA8] | +[#x0AAA-#x0AB0] | [#x0AB2-#x0AB3] | [#x0AB5-#x0AB9] | #x0ABD | #x0AE0 | +[#x0B05-#x0B0C] | [#x0B0F-#x0B10] | [#x0B13-#x0B28] | [#x0B2A-#x0B30] | +[#x0B32-#x0B33] | [#x0B36-#x0B39] | #x0B3D | [#x0B5C-#x0B5D] | +[#x0B5F-#x0B61] | [#x0B85-#x0B8A] | [#x0B8E-#x0B90] | [#x0B92-#x0B95] | +[#x0B99-#x0B9A] | #x0B9C | [#x0B9E-#x0B9F] | [#x0BA3-#x0BA4] | +[#x0BA8-#x0BAA] | [#x0BAE-#x0BB5] | [#x0BB7-#x0BB9] | [#x0C05-#x0C0C] | +[#x0C0E-#x0C10] | [#x0C12-#x0C28] | [#x0C2A-#x0C33] | [#x0C35-#x0C39] | +[#x0C60-#x0C61] | [#x0C85-#x0C8C] | [#x0C8E-#x0C90] | [#x0C92-#x0CA8] | +[#x0CAA-#x0CB3] | [#x0CB5-#x0CB9] | #x0CDE | [#x0CE0-#x0CE1] | +[#x0D05-#x0D0C] | [#x0D0E-#x0D10] | [#x0D12-#x0D28] | [#x0D2A-#x0D39] | +[#x0D60-#x0D61] | [#x0E01-#x0E2E] | #x0E30 | [#x0E32-#x0E33] | +[#x0E40-#x0E45] | [#x0E81-#x0E82] | #x0E84 | [#x0E87-#x0E88] | #x0E8A | +#x0E8D | [#x0E94-#x0E97] | [#x0E99-#x0E9F] | [#x0EA1-#x0EA3] | #x0EA5 | +#x0EA7 | [#x0EAA-#x0EAB] | [#x0EAD-#x0EAE] | #x0EB0 | [#x0EB2-#x0EB3] | +#x0EBD | [#x0EC0-#x0EC4] | [#x0F40-#x0F47] | [#x0F49-#x0F69] | +[#x10A0-#x10C5] | [#x10D0-#x10F6] | #x1100 | [#x1102-#x1103] | +[#x1105-#x1107] | #x1109 | [#x110B-#x110C] | [#x110E-#x1112] | #x113C | +#x113E | #x1140 | #x114C | #x114E | #x1150 | [#x1154-#x1155] | #x1159 | +[#x115F-#x1161] | #x1163 | #x1165 | #x1167 | #x1169 | [#x116D-#x116E] | +[#x1172-#x1173] | #x1175 | #x119E | #x11A8 | #x11AB | [#x11AE-#x11AF] | +[#x11B7-#x11B8] | #x11BA | [#x11BC-#x11C2] | #x11EB | #x11F0 | #x11F9 | +[#x1E00-#x1E9B] | [#x1EA0-#x1EF9] | [#x1F00-#x1F15] | [#x1F18-#x1F1D] | +[#x1F20-#x1F45] | [#x1F48-#x1F4D] | [#x1F50-#x1F57] | #x1F59 | #x1F5B | +#x1F5D | [#x1F5F-#x1F7D] | [#x1F80-#x1FB4] | [#x1FB6-#x1FBC] | #x1FBE | +[#x1FC2-#x1FC4] | [#x1FC6-#x1FCC] | [#x1FD0-#x1FD3] | [#x1FD6-#x1FDB] | +[#x1FE0-#x1FEC] | [#x1FF2-#x1FF4] | [#x1FF6-#x1FFC] | #x2126 | +[#x212A-#x212B] | #x212E | [#x2180-#x2182] | [#x3041-#x3094] | +[#x30A1-#x30FA] | [#x3105-#x312C] | [#xAC00-#xD7A3]""" + +ideographic = """[#x4E00-#x9FA5] | #x3007 | [#x3021-#x3029]""" + +combiningCharacter = """ +[#x0300-#x0345] | [#x0360-#x0361] | [#x0483-#x0486] | [#x0591-#x05A1] | +[#x05A3-#x05B9] | [#x05BB-#x05BD] | #x05BF | [#x05C1-#x05C2] | #x05C4 | +[#x064B-#x0652] | #x0670 | [#x06D6-#x06DC] | [#x06DD-#x06DF] | +[#x06E0-#x06E4] | [#x06E7-#x06E8] | [#x06EA-#x06ED] | [#x0901-#x0903] | +#x093C | [#x093E-#x094C] | #x094D | [#x0951-#x0954] | [#x0962-#x0963] | +[#x0981-#x0983] | #x09BC | #x09BE | #x09BF | [#x09C0-#x09C4] | +[#x09C7-#x09C8] | [#x09CB-#x09CD] | #x09D7 | [#x09E2-#x09E3] | #x0A02 | +#x0A3C | #x0A3E | #x0A3F | [#x0A40-#x0A42] | [#x0A47-#x0A48] | +[#x0A4B-#x0A4D] | [#x0A70-#x0A71] | [#x0A81-#x0A83] | #x0ABC | +[#x0ABE-#x0AC5] | [#x0AC7-#x0AC9] | [#x0ACB-#x0ACD] | [#x0B01-#x0B03] | +#x0B3C | [#x0B3E-#x0B43] | [#x0B47-#x0B48] | [#x0B4B-#x0B4D] | +[#x0B56-#x0B57] | [#x0B82-#x0B83] | [#x0BBE-#x0BC2] | [#x0BC6-#x0BC8] | +[#x0BCA-#x0BCD] | #x0BD7 | [#x0C01-#x0C03] | [#x0C3E-#x0C44] | +[#x0C46-#x0C48] | [#x0C4A-#x0C4D] | [#x0C55-#x0C56] | [#x0C82-#x0C83] | +[#x0CBE-#x0CC4] | [#x0CC6-#x0CC8] | [#x0CCA-#x0CCD] | [#x0CD5-#x0CD6] | +[#x0D02-#x0D03] | [#x0D3E-#x0D43] | [#x0D46-#x0D48] | [#x0D4A-#x0D4D] | +#x0D57 | #x0E31 | [#x0E34-#x0E3A] | [#x0E47-#x0E4E] | #x0EB1 | +[#x0EB4-#x0EB9] | [#x0EBB-#x0EBC] | [#x0EC8-#x0ECD] | [#x0F18-#x0F19] | +#x0F35 | #x0F37 | #x0F39 | #x0F3E | #x0F3F | [#x0F71-#x0F84] | +[#x0F86-#x0F8B] | [#x0F90-#x0F95] | #x0F97 | [#x0F99-#x0FAD] | +[#x0FB1-#x0FB7] | #x0FB9 | [#x20D0-#x20DC] | #x20E1 | [#x302A-#x302F] | +#x3099 | #x309A""" + +digit = """ +[#x0030-#x0039] | [#x0660-#x0669] | [#x06F0-#x06F9] | [#x0966-#x096F] | +[#x09E6-#x09EF] | [#x0A66-#x0A6F] | [#x0AE6-#x0AEF] | [#x0B66-#x0B6F] | +[#x0BE7-#x0BEF] | [#x0C66-#x0C6F] | [#x0CE6-#x0CEF] | [#x0D66-#x0D6F] | +[#x0E50-#x0E59] | [#x0ED0-#x0ED9] | [#x0F20-#x0F29]""" + +extender = """ +#x00B7 | #x02D0 | #x02D1 | #x0387 | #x0640 | #x0E46 | #x0EC6 | #x3005 | +#[#x3031-#x3035] | [#x309D-#x309E] | [#x30FC-#x30FE]""" + +letter = " | ".join([baseChar, ideographic]) + +# Without the +name = " | ".join([letter, digit, ".", "-", "_", combiningCharacter, + extender]) +nameFirst = " | ".join([letter, "_"]) + +reChar = re.compile(r"#x([\d|A-F]{4,4})") +reCharRange = re.compile(r"\[#x([\d|A-F]{4,4})-#x([\d|A-F]{4,4})\]") + + +def charStringToList(chars): + charRanges = [item.strip() for item in chars.split(" | ")] + rv = [] + for item in charRanges: + foundMatch = False + for regexp in (reChar, reCharRange): + match = regexp.match(item) + if match is not None: + rv.append([hexToInt(item) for item in match.groups()]) + if len(rv[-1]) == 1: + rv[-1] = rv[-1] * 2 + foundMatch = True + break + if not foundMatch: + assert len(item) == 1 + + rv.append([ord(item)] * 2) + rv = normaliseCharList(rv) + return rv + + +def normaliseCharList(charList): + charList = sorted(charList) + for item in charList: + assert item[1] >= item[0] + rv = [] + i = 0 + while i < len(charList): + j = 1 + rv.append(charList[i]) + while i + j < len(charList) and charList[i + j][0] <= rv[-1][1] + 1: + rv[-1][1] = charList[i + j][1] + j += 1 + i += j + return rv + +# We don't really support characters above the BMP :( +max_unicode = int("FFFF", 16) + + +def missingRanges(charList): + rv = [] + if charList[0] != 0: + rv.append([0, charList[0][0] - 1]) + for i, item in enumerate(charList[:-1]): + rv.append([item[1] + 1, charList[i + 1][0] - 1]) + if charList[-1][1] != max_unicode: + rv.append([charList[-1][1] + 1, max_unicode]) + return rv + + +def listToRegexpStr(charList): + rv = [] + for item in charList: + if item[0] == item[1]: + rv.append(escapeRegexp(chr(item[0]))) + else: + rv.append(escapeRegexp(chr(item[0])) + "-" + + escapeRegexp(chr(item[1]))) + return "[%s]" % "".join(rv) + + +def hexToInt(hex_str): + return int(hex_str, 16) + + +def escapeRegexp(string): + specialCharacters = (".", "^", "$", "*", "+", "?", "{", "}", + "[", "]", "|", "(", ")", "-") + for char in specialCharacters: + string = string.replace(char, "\\" + char) + + return string + +# output from the above +nonXmlNameBMPRegexp = re.compile('[\x00-,/:-@\\[-\\^`\\{-\xb6\xb8-\xbf\xd7\xf7\u0132-\u0133\u013f-\u0140\u0149\u017f\u01c4-\u01cc\u01f1-\u01f3\u01f6-\u01f9\u0218-\u024f\u02a9-\u02ba\u02c2-\u02cf\u02d2-\u02ff\u0346-\u035f\u0362-\u0385\u038b\u038d\u03a2\u03cf\u03d7-\u03d9\u03db\u03dd\u03df\u03e1\u03f4-\u0400\u040d\u0450\u045d\u0482\u0487-\u048f\u04c5-\u04c6\u04c9-\u04ca\u04cd-\u04cf\u04ec-\u04ed\u04f6-\u04f7\u04fa-\u0530\u0557-\u0558\u055a-\u0560\u0587-\u0590\u05a2\u05ba\u05be\u05c0\u05c3\u05c5-\u05cf\u05eb-\u05ef\u05f3-\u0620\u063b-\u063f\u0653-\u065f\u066a-\u066f\u06b8-\u06b9\u06bf\u06cf\u06d4\u06e9\u06ee-\u06ef\u06fa-\u0900\u0904\u093a-\u093b\u094e-\u0950\u0955-\u0957\u0964-\u0965\u0970-\u0980\u0984\u098d-\u098e\u0991-\u0992\u09a9\u09b1\u09b3-\u09b5\u09ba-\u09bb\u09bd\u09c5-\u09c6\u09c9-\u09ca\u09ce-\u09d6\u09d8-\u09db\u09de\u09e4-\u09e5\u09f2-\u0a01\u0a03-\u0a04\u0a0b-\u0a0e\u0a11-\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a-\u0a3b\u0a3d\u0a43-\u0a46\u0a49-\u0a4a\u0a4e-\u0a58\u0a5d\u0a5f-\u0a65\u0a75-\u0a80\u0a84\u0a8c\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba-\u0abb\u0ac6\u0aca\u0ace-\u0adf\u0ae1-\u0ae5\u0af0-\u0b00\u0b04\u0b0d-\u0b0e\u0b11-\u0b12\u0b29\u0b31\u0b34-\u0b35\u0b3a-\u0b3b\u0b44-\u0b46\u0b49-\u0b4a\u0b4e-\u0b55\u0b58-\u0b5b\u0b5e\u0b62-\u0b65\u0b70-\u0b81\u0b84\u0b8b-\u0b8d\u0b91\u0b96-\u0b98\u0b9b\u0b9d\u0ba0-\u0ba2\u0ba5-\u0ba7\u0bab-\u0bad\u0bb6\u0bba-\u0bbd\u0bc3-\u0bc5\u0bc9\u0bce-\u0bd6\u0bd8-\u0be6\u0bf0-\u0c00\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a-\u0c3d\u0c45\u0c49\u0c4e-\u0c54\u0c57-\u0c5f\u0c62-\u0c65\u0c70-\u0c81\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba-\u0cbd\u0cc5\u0cc9\u0cce-\u0cd4\u0cd7-\u0cdd\u0cdf\u0ce2-\u0ce5\u0cf0-\u0d01\u0d04\u0d0d\u0d11\u0d29\u0d3a-\u0d3d\u0d44-\u0d45\u0d49\u0d4e-\u0d56\u0d58-\u0d5f\u0d62-\u0d65\u0d70-\u0e00\u0e2f\u0e3b-\u0e3f\u0e4f\u0e5a-\u0e80\u0e83\u0e85-\u0e86\u0e89\u0e8b-\u0e8c\u0e8e-\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8-\u0ea9\u0eac\u0eaf\u0eba\u0ebe-\u0ebf\u0ec5\u0ec7\u0ece-\u0ecf\u0eda-\u0f17\u0f1a-\u0f1f\u0f2a-\u0f34\u0f36\u0f38\u0f3a-\u0f3d\u0f48\u0f6a-\u0f70\u0f85\u0f8c-\u0f8f\u0f96\u0f98\u0fae-\u0fb0\u0fb8\u0fba-\u109f\u10c6-\u10cf\u10f7-\u10ff\u1101\u1104\u1108\u110a\u110d\u1113-\u113b\u113d\u113f\u1141-\u114b\u114d\u114f\u1151-\u1153\u1156-\u1158\u115a-\u115e\u1162\u1164\u1166\u1168\u116a-\u116c\u116f-\u1171\u1174\u1176-\u119d\u119f-\u11a7\u11a9-\u11aa\u11ac-\u11ad\u11b0-\u11b6\u11b9\u11bb\u11c3-\u11ea\u11ec-\u11ef\u11f1-\u11f8\u11fa-\u1dff\u1e9c-\u1e9f\u1efa-\u1eff\u1f16-\u1f17\u1f1e-\u1f1f\u1f46-\u1f47\u1f4e-\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e-\u1f7f\u1fb5\u1fbd\u1fbf-\u1fc1\u1fc5\u1fcd-\u1fcf\u1fd4-\u1fd5\u1fdc-\u1fdf\u1fed-\u1ff1\u1ff5\u1ffd-\u20cf\u20dd-\u20e0\u20e2-\u2125\u2127-\u2129\u212c-\u212d\u212f-\u217f\u2183-\u3004\u3006\u3008-\u3020\u3030\u3036-\u3040\u3095-\u3098\u309b-\u309c\u309f-\u30a0\u30fb\u30ff-\u3104\u312d-\u4dff\u9fa6-\uabff\ud7a4-\uffff]') # noqa + +nonXmlNameFirstBMPRegexp = re.compile('[\x00-@\\[-\\^`\\{-\xbf\xd7\xf7\u0132-\u0133\u013f-\u0140\u0149\u017f\u01c4-\u01cc\u01f1-\u01f3\u01f6-\u01f9\u0218-\u024f\u02a9-\u02ba\u02c2-\u0385\u0387\u038b\u038d\u03a2\u03cf\u03d7-\u03d9\u03db\u03dd\u03df\u03e1\u03f4-\u0400\u040d\u0450\u045d\u0482-\u048f\u04c5-\u04c6\u04c9-\u04ca\u04cd-\u04cf\u04ec-\u04ed\u04f6-\u04f7\u04fa-\u0530\u0557-\u0558\u055a-\u0560\u0587-\u05cf\u05eb-\u05ef\u05f3-\u0620\u063b-\u0640\u064b-\u0670\u06b8-\u06b9\u06bf\u06cf\u06d4\u06d6-\u06e4\u06e7-\u0904\u093a-\u093c\u093e-\u0957\u0962-\u0984\u098d-\u098e\u0991-\u0992\u09a9\u09b1\u09b3-\u09b5\u09ba-\u09db\u09de\u09e2-\u09ef\u09f2-\u0a04\u0a0b-\u0a0e\u0a11-\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a-\u0a58\u0a5d\u0a5f-\u0a71\u0a75-\u0a84\u0a8c\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba-\u0abc\u0abe-\u0adf\u0ae1-\u0b04\u0b0d-\u0b0e\u0b11-\u0b12\u0b29\u0b31\u0b34-\u0b35\u0b3a-\u0b3c\u0b3e-\u0b5b\u0b5e\u0b62-\u0b84\u0b8b-\u0b8d\u0b91\u0b96-\u0b98\u0b9b\u0b9d\u0ba0-\u0ba2\u0ba5-\u0ba7\u0bab-\u0bad\u0bb6\u0bba-\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a-\u0c5f\u0c62-\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba-\u0cdd\u0cdf\u0ce2-\u0d04\u0d0d\u0d11\u0d29\u0d3a-\u0d5f\u0d62-\u0e00\u0e2f\u0e31\u0e34-\u0e3f\u0e46-\u0e80\u0e83\u0e85-\u0e86\u0e89\u0e8b-\u0e8c\u0e8e-\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8-\u0ea9\u0eac\u0eaf\u0eb1\u0eb4-\u0ebc\u0ebe-\u0ebf\u0ec5-\u0f3f\u0f48\u0f6a-\u109f\u10c6-\u10cf\u10f7-\u10ff\u1101\u1104\u1108\u110a\u110d\u1113-\u113b\u113d\u113f\u1141-\u114b\u114d\u114f\u1151-\u1153\u1156-\u1158\u115a-\u115e\u1162\u1164\u1166\u1168\u116a-\u116c\u116f-\u1171\u1174\u1176-\u119d\u119f-\u11a7\u11a9-\u11aa\u11ac-\u11ad\u11b0-\u11b6\u11b9\u11bb\u11c3-\u11ea\u11ec-\u11ef\u11f1-\u11f8\u11fa-\u1dff\u1e9c-\u1e9f\u1efa-\u1eff\u1f16-\u1f17\u1f1e-\u1f1f\u1f46-\u1f47\u1f4e-\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e-\u1f7f\u1fb5\u1fbd\u1fbf-\u1fc1\u1fc5\u1fcd-\u1fcf\u1fd4-\u1fd5\u1fdc-\u1fdf\u1fed-\u1ff1\u1ff5\u1ffd-\u2125\u2127-\u2129\u212c-\u212d\u212f-\u217f\u2183-\u3006\u3008-\u3020\u302a-\u3040\u3095-\u30a0\u30fb-\u3104\u312d-\u4dff\u9fa6-\uabff\ud7a4-\uffff]') # noqa + +# Simpler things +nonPubidCharRegexp = re.compile("[^\x20\x0D\x0Aa-zA-Z0-9\-\'()+,./:=?;!*#@$_%]") + + +class InfosetFilter(object): + replacementRegexp = re.compile(r"U[\dA-F]{5,5}") + + def __init__(self, + dropXmlnsLocalName=False, + dropXmlnsAttrNs=False, + preventDoubleDashComments=False, + preventDashAtCommentEnd=False, + replaceFormFeedCharacters=True, + preventSingleQuotePubid=False): + + self.dropXmlnsLocalName = dropXmlnsLocalName + self.dropXmlnsAttrNs = dropXmlnsAttrNs + + self.preventDoubleDashComments = preventDoubleDashComments + self.preventDashAtCommentEnd = preventDashAtCommentEnd + + self.replaceFormFeedCharacters = replaceFormFeedCharacters + + self.preventSingleQuotePubid = preventSingleQuotePubid + + self.replaceCache = {} + + def coerceAttribute(self, name, namespace=None): + if self.dropXmlnsLocalName and name.startswith("xmlns:"): + warnings.warn("Attributes cannot begin with xmlns", DataLossWarning) + return None + elif (self.dropXmlnsAttrNs and + namespace == "http://www.w3.org/2000/xmlns/"): + warnings.warn("Attributes cannot be in the xml namespace", DataLossWarning) + return None + else: + return self.toXmlName(name) + + def coerceElement(self, name): + return self.toXmlName(name) + + def coerceComment(self, data): + if self.preventDoubleDashComments: + while "--" in data: + warnings.warn("Comments cannot contain adjacent dashes", DataLossWarning) + data = data.replace("--", "- -") + if data.endswith("-"): + warnings.warn("Comments cannot end in a dash", DataLossWarning) + data += " " + return data + + def coerceCharacters(self, data): + if self.replaceFormFeedCharacters: + for _ in range(data.count("\x0C")): + warnings.warn("Text cannot contain U+000C", DataLossWarning) + data = data.replace("\x0C", " ") + # Other non-xml characters + return data + + def coercePubid(self, data): + dataOutput = data + for char in nonPubidCharRegexp.findall(data): + warnings.warn("Coercing non-XML pubid", DataLossWarning) + replacement = self.getReplacementCharacter(char) + dataOutput = dataOutput.replace(char, replacement) + if self.preventSingleQuotePubid and dataOutput.find("'") >= 0: + warnings.warn("Pubid cannot contain single quote", DataLossWarning) + dataOutput = dataOutput.replace("'", self.getReplacementCharacter("'")) + return dataOutput + + def toXmlName(self, name): + nameFirst = name[0] + nameRest = name[1:] + m = nonXmlNameFirstBMPRegexp.match(nameFirst) + if m: + warnings.warn("Coercing non-XML name", DataLossWarning) + nameFirstOutput = self.getReplacementCharacter(nameFirst) + else: + nameFirstOutput = nameFirst + + nameRestOutput = nameRest + replaceChars = set(nonXmlNameBMPRegexp.findall(nameRest)) + for char in replaceChars: + warnings.warn("Coercing non-XML name", DataLossWarning) + replacement = self.getReplacementCharacter(char) + nameRestOutput = nameRestOutput.replace(char, replacement) + return nameFirstOutput + nameRestOutput + + def getReplacementCharacter(self, char): + if char in self.replaceCache: + replacement = self.replaceCache[char] + else: + replacement = self.escapeChar(char) + return replacement + + def fromXmlName(self, name): + for item in set(self.replacementRegexp.findall(name)): + name = name.replace(item, self.unescapeChar(item)) + return name + + def escapeChar(self, char): + replacement = "U%05X" % ord(char) + self.replaceCache[char] = replacement + return replacement + + def unescapeChar(self, charcode): + return chr(int(charcode[1:], 16)) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_inputstream.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_inputstream.py new file mode 100644 index 0000000..7c5639f --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_inputstream.py @@ -0,0 +1,923 @@ +from __future__ import absolute_import, division, unicode_literals + +from pip._vendor.six import text_type, binary_type +from pip._vendor.six.moves import http_client, urllib + +import codecs +import re + +from pip._vendor import webencodings + +from .constants import EOF, spaceCharacters, asciiLetters, asciiUppercase +from .constants import ReparseException +from . import _utils + +from io import StringIO + +try: + from io import BytesIO +except ImportError: + BytesIO = StringIO + +# Non-unicode versions of constants for use in the pre-parser +spaceCharactersBytes = frozenset([item.encode("ascii") for item in spaceCharacters]) +asciiLettersBytes = frozenset([item.encode("ascii") for item in asciiLetters]) +asciiUppercaseBytes = frozenset([item.encode("ascii") for item in asciiUppercase]) +spacesAngleBrackets = spaceCharactersBytes | frozenset([b">", b"<"]) + + +invalid_unicode_no_surrogate = "[\u0001-\u0008\u000B\u000E-\u001F\u007F-\u009F\uFDD0-\uFDEF\uFFFE\uFFFF\U0001FFFE\U0001FFFF\U0002FFFE\U0002FFFF\U0003FFFE\U0003FFFF\U0004FFFE\U0004FFFF\U0005FFFE\U0005FFFF\U0006FFFE\U0006FFFF\U0007FFFE\U0007FFFF\U0008FFFE\U0008FFFF\U0009FFFE\U0009FFFF\U000AFFFE\U000AFFFF\U000BFFFE\U000BFFFF\U000CFFFE\U000CFFFF\U000DFFFE\U000DFFFF\U000EFFFE\U000EFFFF\U000FFFFE\U000FFFFF\U0010FFFE\U0010FFFF]" # noqa + +if _utils.supports_lone_surrogates: + # Use one extra step of indirection and create surrogates with + # eval. Not using this indirection would introduce an illegal + # unicode literal on platforms not supporting such lone + # surrogates. + assert invalid_unicode_no_surrogate[-1] == "]" and invalid_unicode_no_surrogate.count("]") == 1 + invalid_unicode_re = re.compile(invalid_unicode_no_surrogate[:-1] + + eval('"\\uD800-\\uDFFF"') + # pylint:disable=eval-used + "]") +else: + invalid_unicode_re = re.compile(invalid_unicode_no_surrogate) + +non_bmp_invalid_codepoints = set([0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, + 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, + 0x6FFFE, 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, + 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, + 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, + 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, + 0x10FFFE, 0x10FFFF]) + +ascii_punctuation_re = re.compile("[\u0009-\u000D\u0020-\u002F\u003A-\u0040\u005B-\u0060\u007B-\u007E]") + +# Cache for charsUntil() +charsUntilRegEx = {} + + +class BufferedStream(object): + """Buffering for streams that do not have buffering of their own + + The buffer is implemented as a list of chunks on the assumption that + joining many strings will be slow since it is O(n**2) + """ + + def __init__(self, stream): + self.stream = stream + self.buffer = [] + self.position = [-1, 0] # chunk number, offset + + def tell(self): + pos = 0 + for chunk in self.buffer[:self.position[0]]: + pos += len(chunk) + pos += self.position[1] + return pos + + def seek(self, pos): + assert pos <= self._bufferedBytes() + offset = pos + i = 0 + while len(self.buffer[i]) < offset: + offset -= len(self.buffer[i]) + i += 1 + self.position = [i, offset] + + def read(self, bytes): + if not self.buffer: + return self._readStream(bytes) + elif (self.position[0] == len(self.buffer) and + self.position[1] == len(self.buffer[-1])): + return self._readStream(bytes) + else: + return self._readFromBuffer(bytes) + + def _bufferedBytes(self): + return sum([len(item) for item in self.buffer]) + + def _readStream(self, bytes): + data = self.stream.read(bytes) + self.buffer.append(data) + self.position[0] += 1 + self.position[1] = len(data) + return data + + def _readFromBuffer(self, bytes): + remainingBytes = bytes + rv = [] + bufferIndex = self.position[0] + bufferOffset = self.position[1] + while bufferIndex < len(self.buffer) and remainingBytes != 0: + assert remainingBytes > 0 + bufferedData = self.buffer[bufferIndex] + + if remainingBytes <= len(bufferedData) - bufferOffset: + bytesToRead = remainingBytes + self.position = [bufferIndex, bufferOffset + bytesToRead] + else: + bytesToRead = len(bufferedData) - bufferOffset + self.position = [bufferIndex, len(bufferedData)] + bufferIndex += 1 + rv.append(bufferedData[bufferOffset:bufferOffset + bytesToRead]) + remainingBytes -= bytesToRead + + bufferOffset = 0 + + if remainingBytes: + rv.append(self._readStream(remainingBytes)) + + return b"".join(rv) + + +def HTMLInputStream(source, **kwargs): + # Work around Python bug #20007: read(0) closes the connection. + # http://bugs.python.org/issue20007 + if (isinstance(source, http_client.HTTPResponse) or + # Also check for addinfourl wrapping HTTPResponse + (isinstance(source, urllib.response.addbase) and + isinstance(source.fp, http_client.HTTPResponse))): + isUnicode = False + elif hasattr(source, "read"): + isUnicode = isinstance(source.read(0), text_type) + else: + isUnicode = isinstance(source, text_type) + + if isUnicode: + encodings = [x for x in kwargs if x.endswith("_encoding")] + if encodings: + raise TypeError("Cannot set an encoding with a unicode input, set %r" % encodings) + + return HTMLUnicodeInputStream(source, **kwargs) + else: + return HTMLBinaryInputStream(source, **kwargs) + + +class HTMLUnicodeInputStream(object): + """Provides a unicode stream of characters to the HTMLTokenizer. + + This class takes care of character encoding and removing or replacing + incorrect byte-sequences and also provides column and line tracking. + + """ + + _defaultChunkSize = 10240 + + def __init__(self, source): + """Initialises the HTMLInputStream. + + HTMLInputStream(source, [encoding]) -> Normalized stream from source + for use by html5lib. + + source can be either a file-object, local filename or a string. + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + """ + + if not _utils.supports_lone_surrogates: + # Such platforms will have already checked for such + # surrogate errors, so no need to do this checking. + self.reportCharacterErrors = None + elif len("\U0010FFFF") == 1: + self.reportCharacterErrors = self.characterErrorsUCS4 + else: + self.reportCharacterErrors = self.characterErrorsUCS2 + + # List of where new lines occur + self.newLines = [0] + + self.charEncoding = (lookupEncoding("utf-8"), "certain") + self.dataStream = self.openStream(source) + + self.reset() + + def reset(self): + self.chunk = "" + self.chunkSize = 0 + self.chunkOffset = 0 + self.errors = [] + + # number of (complete) lines in previous chunks + self.prevNumLines = 0 + # number of columns in the last line of the previous chunk + self.prevNumCols = 0 + + # Deal with CR LF and surrogates split over chunk boundaries + self._bufferedCharacter = None + + def openStream(self, source): + """Produces a file object from source. + + source can be either a file object, local filename or a string. + + """ + # Already a file object + if hasattr(source, 'read'): + stream = source + else: + stream = StringIO(source) + + return stream + + def _position(self, offset): + chunk = self.chunk + nLines = chunk.count('\n', 0, offset) + positionLine = self.prevNumLines + nLines + lastLinePos = chunk.rfind('\n', 0, offset) + if lastLinePos == -1: + positionColumn = self.prevNumCols + offset + else: + positionColumn = offset - (lastLinePos + 1) + return (positionLine, positionColumn) + + def position(self): + """Returns (line, col) of the current position in the stream.""" + line, col = self._position(self.chunkOffset) + return (line + 1, col) + + def char(self): + """ Read one character from the stream or queue if available. Return + EOF when EOF is reached. + """ + # Read a new chunk from the input stream if necessary + if self.chunkOffset >= self.chunkSize: + if not self.readChunk(): + return EOF + + chunkOffset = self.chunkOffset + char = self.chunk[chunkOffset] + self.chunkOffset = chunkOffset + 1 + + return char + + def readChunk(self, chunkSize=None): + if chunkSize is None: + chunkSize = self._defaultChunkSize + + self.prevNumLines, self.prevNumCols = self._position(self.chunkSize) + + self.chunk = "" + self.chunkSize = 0 + self.chunkOffset = 0 + + data = self.dataStream.read(chunkSize) + + # Deal with CR LF and surrogates broken across chunks + if self._bufferedCharacter: + data = self._bufferedCharacter + data + self._bufferedCharacter = None + elif not data: + # We have no more data, bye-bye stream + return False + + if len(data) > 1: + lastv = ord(data[-1]) + if lastv == 0x0D or 0xD800 <= lastv <= 0xDBFF: + self._bufferedCharacter = data[-1] + data = data[:-1] + + if self.reportCharacterErrors: + self.reportCharacterErrors(data) + + # Replace invalid characters + data = data.replace("\r\n", "\n") + data = data.replace("\r", "\n") + + self.chunk = data + self.chunkSize = len(data) + + return True + + def characterErrorsUCS4(self, data): + for _ in range(len(invalid_unicode_re.findall(data))): + self.errors.append("invalid-codepoint") + + def characterErrorsUCS2(self, data): + # Someone picked the wrong compile option + # You lose + skip = False + for match in invalid_unicode_re.finditer(data): + if skip: + continue + codepoint = ord(match.group()) + pos = match.start() + # Pretty sure there should be endianness issues here + if _utils.isSurrogatePair(data[pos:pos + 2]): + # We have a surrogate pair! + char_val = _utils.surrogatePairToCodepoint(data[pos:pos + 2]) + if char_val in non_bmp_invalid_codepoints: + self.errors.append("invalid-codepoint") + skip = True + elif (codepoint >= 0xD800 and codepoint <= 0xDFFF and + pos == len(data) - 1): + self.errors.append("invalid-codepoint") + else: + skip = False + self.errors.append("invalid-codepoint") + + def charsUntil(self, characters, opposite=False): + """ Returns a string of characters from the stream up to but not + including any character in 'characters' or EOF. 'characters' must be + a container that supports the 'in' method and iteration over its + characters. + """ + + # Use a cache of regexps to find the required characters + try: + chars = charsUntilRegEx[(characters, opposite)] + except KeyError: + if __debug__: + for c in characters: + assert(ord(c) < 128) + regex = "".join(["\\x%02x" % ord(c) for c in characters]) + if not opposite: + regex = "^%s" % regex + chars = charsUntilRegEx[(characters, opposite)] = re.compile("[%s]+" % regex) + + rv = [] + + while True: + # Find the longest matching prefix + m = chars.match(self.chunk, self.chunkOffset) + if m is None: + # If nothing matched, and it wasn't because we ran out of chunk, + # then stop + if self.chunkOffset != self.chunkSize: + break + else: + end = m.end() + # If not the whole chunk matched, return everything + # up to the part that didn't match + if end != self.chunkSize: + rv.append(self.chunk[self.chunkOffset:end]) + self.chunkOffset = end + break + # If the whole remainder of the chunk matched, + # use it all and read the next chunk + rv.append(self.chunk[self.chunkOffset:]) + if not self.readChunk(): + # Reached EOF + break + + r = "".join(rv) + return r + + def unget(self, char): + # Only one character is allowed to be ungotten at once - it must + # be consumed again before any further call to unget + if char is not None: + if self.chunkOffset == 0: + # unget is called quite rarely, so it's a good idea to do + # more work here if it saves a bit of work in the frequently + # called char and charsUntil. + # So, just prepend the ungotten character onto the current + # chunk: + self.chunk = char + self.chunk + self.chunkSize += 1 + else: + self.chunkOffset -= 1 + assert self.chunk[self.chunkOffset] == char + + +class HTMLBinaryInputStream(HTMLUnicodeInputStream): + """Provides a unicode stream of characters to the HTMLTokenizer. + + This class takes care of character encoding and removing or replacing + incorrect byte-sequences and also provides column and line tracking. + + """ + + def __init__(self, source, override_encoding=None, transport_encoding=None, + same_origin_parent_encoding=None, likely_encoding=None, + default_encoding="windows-1252", useChardet=True): + """Initialises the HTMLInputStream. + + HTMLInputStream(source, [encoding]) -> Normalized stream from source + for use by html5lib. + + source can be either a file-object, local filename or a string. + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + """ + # Raw Stream - for unicode objects this will encode to utf-8 and set + # self.charEncoding as appropriate + self.rawStream = self.openStream(source) + + HTMLUnicodeInputStream.__init__(self, self.rawStream) + + # Encoding Information + # Number of bytes to use when looking for a meta element with + # encoding information + self.numBytesMeta = 1024 + # Number of bytes to use when using detecting encoding using chardet + self.numBytesChardet = 100 + # Things from args + self.override_encoding = override_encoding + self.transport_encoding = transport_encoding + self.same_origin_parent_encoding = same_origin_parent_encoding + self.likely_encoding = likely_encoding + self.default_encoding = default_encoding + + # Determine encoding + self.charEncoding = self.determineEncoding(useChardet) + assert self.charEncoding[0] is not None + + # Call superclass + self.reset() + + def reset(self): + self.dataStream = self.charEncoding[0].codec_info.streamreader(self.rawStream, 'replace') + HTMLUnicodeInputStream.reset(self) + + def openStream(self, source): + """Produces a file object from source. + + source can be either a file object, local filename or a string. + + """ + # Already a file object + if hasattr(source, 'read'): + stream = source + else: + stream = BytesIO(source) + + try: + stream.seek(stream.tell()) + except: # pylint:disable=bare-except + stream = BufferedStream(stream) + + return stream + + def determineEncoding(self, chardet=True): + # BOMs take precedence over everything + # This will also read past the BOM if present + charEncoding = self.detectBOM(), "certain" + if charEncoding[0] is not None: + return charEncoding + + # If we've been overriden, we've been overriden + charEncoding = lookupEncoding(self.override_encoding), "certain" + if charEncoding[0] is not None: + return charEncoding + + # Now check the transport layer + charEncoding = lookupEncoding(self.transport_encoding), "certain" + if charEncoding[0] is not None: + return charEncoding + + # Look for meta elements with encoding information + charEncoding = self.detectEncodingMeta(), "tentative" + if charEncoding[0] is not None: + return charEncoding + + # Parent document encoding + charEncoding = lookupEncoding(self.same_origin_parent_encoding), "tentative" + if charEncoding[0] is not None and not charEncoding[0].name.startswith("utf-16"): + return charEncoding + + # "likely" encoding + charEncoding = lookupEncoding(self.likely_encoding), "tentative" + if charEncoding[0] is not None: + return charEncoding + + # Guess with chardet, if available + if chardet: + try: + from chardet.universaldetector import UniversalDetector + except ImportError: + pass + else: + buffers = [] + detector = UniversalDetector() + while not detector.done: + buffer = self.rawStream.read(self.numBytesChardet) + assert isinstance(buffer, bytes) + if not buffer: + break + buffers.append(buffer) + detector.feed(buffer) + detector.close() + encoding = lookupEncoding(detector.result['encoding']) + self.rawStream.seek(0) + if encoding is not None: + return encoding, "tentative" + + # Try the default encoding + charEncoding = lookupEncoding(self.default_encoding), "tentative" + if charEncoding[0] is not None: + return charEncoding + + # Fallback to html5lib's default if even that hasn't worked + return lookupEncoding("windows-1252"), "tentative" + + def changeEncoding(self, newEncoding): + assert self.charEncoding[1] != "certain" + newEncoding = lookupEncoding(newEncoding) + if newEncoding is None: + return + if newEncoding.name in ("utf-16be", "utf-16le"): + newEncoding = lookupEncoding("utf-8") + assert newEncoding is not None + elif newEncoding == self.charEncoding[0]: + self.charEncoding = (self.charEncoding[0], "certain") + else: + self.rawStream.seek(0) + self.charEncoding = (newEncoding, "certain") + self.reset() + raise ReparseException("Encoding changed from %s to %s" % (self.charEncoding[0], newEncoding)) + + def detectBOM(self): + """Attempts to detect at BOM at the start of the stream. If + an encoding can be determined from the BOM return the name of the + encoding otherwise return None""" + bomDict = { + codecs.BOM_UTF8: 'utf-8', + codecs.BOM_UTF16_LE: 'utf-16le', codecs.BOM_UTF16_BE: 'utf-16be', + codecs.BOM_UTF32_LE: 'utf-32le', codecs.BOM_UTF32_BE: 'utf-32be' + } + + # Go to beginning of file and read in 4 bytes + string = self.rawStream.read(4) + assert isinstance(string, bytes) + + # Try detecting the BOM using bytes from the string + encoding = bomDict.get(string[:3]) # UTF-8 + seek = 3 + if not encoding: + # Need to detect UTF-32 before UTF-16 + encoding = bomDict.get(string) # UTF-32 + seek = 4 + if not encoding: + encoding = bomDict.get(string[:2]) # UTF-16 + seek = 2 + + # Set the read position past the BOM if one was found, otherwise + # set it to the start of the stream + if encoding: + self.rawStream.seek(seek) + return lookupEncoding(encoding) + else: + self.rawStream.seek(0) + return None + + def detectEncodingMeta(self): + """Report the encoding declared by the meta element + """ + buffer = self.rawStream.read(self.numBytesMeta) + assert isinstance(buffer, bytes) + parser = EncodingParser(buffer) + self.rawStream.seek(0) + encoding = parser.getEncoding() + + if encoding is not None and encoding.name in ("utf-16be", "utf-16le"): + encoding = lookupEncoding("utf-8") + + return encoding + + +class EncodingBytes(bytes): + """String-like object with an associated position and various extra methods + If the position is ever greater than the string length then an exception is + raised""" + def __new__(self, value): + assert isinstance(value, bytes) + return bytes.__new__(self, value.lower()) + + def __init__(self, value): + # pylint:disable=unused-argument + self._position = -1 + + def __iter__(self): + return self + + def __next__(self): + p = self._position = self._position + 1 + if p >= len(self): + raise StopIteration + elif p < 0: + raise TypeError + return self[p:p + 1] + + def next(self): + # Py2 compat + return self.__next__() + + def previous(self): + p = self._position + if p >= len(self): + raise StopIteration + elif p < 0: + raise TypeError + self._position = p = p - 1 + return self[p:p + 1] + + def setPosition(self, position): + if self._position >= len(self): + raise StopIteration + self._position = position + + def getPosition(self): + if self._position >= len(self): + raise StopIteration + if self._position >= 0: + return self._position + else: + return None + + position = property(getPosition, setPosition) + + def getCurrentByte(self): + return self[self.position:self.position + 1] + + currentByte = property(getCurrentByte) + + def skip(self, chars=spaceCharactersBytes): + """Skip past a list of characters""" + p = self.position # use property for the error-checking + while p < len(self): + c = self[p:p + 1] + if c not in chars: + self._position = p + return c + p += 1 + self._position = p + return None + + def skipUntil(self, chars): + p = self.position + while p < len(self): + c = self[p:p + 1] + if c in chars: + self._position = p + return c + p += 1 + self._position = p + return None + + def matchBytes(self, bytes): + """Look for a sequence of bytes at the start of a string. If the bytes + are found return True and advance the position to the byte after the + match. Otherwise return False and leave the position alone""" + p = self.position + data = self[p:p + len(bytes)] + rv = data.startswith(bytes) + if rv: + self.position += len(bytes) + return rv + + def jumpTo(self, bytes): + """Look for the next sequence of bytes matching a given sequence. If + a match is found advance the position to the last byte of the match""" + newPosition = self[self.position:].find(bytes) + if newPosition > -1: + # XXX: This is ugly, but I can't see a nicer way to fix this. + if self._position == -1: + self._position = 0 + self._position += (newPosition + len(bytes) - 1) + return True + else: + raise StopIteration + + +class EncodingParser(object): + """Mini parser for detecting character encoding from meta elements""" + + def __init__(self, data): + """string - the data to work on for encoding detection""" + self.data = EncodingBytes(data) + self.encoding = None + + def getEncoding(self): + methodDispatch = ( + (b"<!--", self.handleComment), + (b"<meta", self.handleMeta), + (b"</", self.handlePossibleEndTag), + (b"<!", self.handleOther), + (b"<?", self.handleOther), + (b"<", self.handlePossibleStartTag)) + for _ in self.data: + keepParsing = True + for key, method in methodDispatch: + if self.data.matchBytes(key): + try: + keepParsing = method() + break + except StopIteration: + keepParsing = False + break + if not keepParsing: + break + + return self.encoding + + def handleComment(self): + """Skip over comments""" + return self.data.jumpTo(b"-->") + + def handleMeta(self): + if self.data.currentByte not in spaceCharactersBytes: + # if we have <meta not followed by a space so just keep going + return True + # We have a valid meta element we want to search for attributes + hasPragma = False + pendingEncoding = None + while True: + # Try to find the next attribute after the current position + attr = self.getAttribute() + if attr is None: + return True + else: + if attr[0] == b"http-equiv": + hasPragma = attr[1] == b"content-type" + if hasPragma and pendingEncoding is not None: + self.encoding = pendingEncoding + return False + elif attr[0] == b"charset": + tentativeEncoding = attr[1] + codec = lookupEncoding(tentativeEncoding) + if codec is not None: + self.encoding = codec + return False + elif attr[0] == b"content": + contentParser = ContentAttrParser(EncodingBytes(attr[1])) + tentativeEncoding = contentParser.parse() + if tentativeEncoding is not None: + codec = lookupEncoding(tentativeEncoding) + if codec is not None: + if hasPragma: + self.encoding = codec + return False + else: + pendingEncoding = codec + + def handlePossibleStartTag(self): + return self.handlePossibleTag(False) + + def handlePossibleEndTag(self): + next(self.data) + return self.handlePossibleTag(True) + + def handlePossibleTag(self, endTag): + data = self.data + if data.currentByte not in asciiLettersBytes: + # If the next byte is not an ascii letter either ignore this + # fragment (possible start tag case) or treat it according to + # handleOther + if endTag: + data.previous() + self.handleOther() + return True + + c = data.skipUntil(spacesAngleBrackets) + if c == b"<": + # return to the first step in the overall "two step" algorithm + # reprocessing the < byte + data.previous() + else: + # Read all attributes + attr = self.getAttribute() + while attr is not None: + attr = self.getAttribute() + return True + + def handleOther(self): + return self.data.jumpTo(b">") + + def getAttribute(self): + """Return a name,value pair for the next attribute in the stream, + if one is found, or None""" + data = self.data + # Step 1 (skip chars) + c = data.skip(spaceCharactersBytes | frozenset([b"/"])) + assert c is None or len(c) == 1 + # Step 2 + if c in (b">", None): + return None + # Step 3 + attrName = [] + attrValue = [] + # Step 4 attribute name + while True: + if c == b"=" and attrName: + break + elif c in spaceCharactersBytes: + # Step 6! + c = data.skip() + break + elif c in (b"/", b">"): + return b"".join(attrName), b"" + elif c in asciiUppercaseBytes: + attrName.append(c.lower()) + elif c is None: + return None + else: + attrName.append(c) + # Step 5 + c = next(data) + # Step 7 + if c != b"=": + data.previous() + return b"".join(attrName), b"" + # Step 8 + next(data) + # Step 9 + c = data.skip() + # Step 10 + if c in (b"'", b'"'): + # 10.1 + quoteChar = c + while True: + # 10.2 + c = next(data) + # 10.3 + if c == quoteChar: + next(data) + return b"".join(attrName), b"".join(attrValue) + # 10.4 + elif c in asciiUppercaseBytes: + attrValue.append(c.lower()) + # 10.5 + else: + attrValue.append(c) + elif c == b">": + return b"".join(attrName), b"" + elif c in asciiUppercaseBytes: + attrValue.append(c.lower()) + elif c is None: + return None + else: + attrValue.append(c) + # Step 11 + while True: + c = next(data) + if c in spacesAngleBrackets: + return b"".join(attrName), b"".join(attrValue) + elif c in asciiUppercaseBytes: + attrValue.append(c.lower()) + elif c is None: + return None + else: + attrValue.append(c) + + +class ContentAttrParser(object): + def __init__(self, data): + assert isinstance(data, bytes) + self.data = data + + def parse(self): + try: + # Check if the attr name is charset + # otherwise return + self.data.jumpTo(b"charset") + self.data.position += 1 + self.data.skip() + if not self.data.currentByte == b"=": + # If there is no = sign keep looking for attrs + return None + self.data.position += 1 + self.data.skip() + # Look for an encoding between matching quote marks + if self.data.currentByte in (b'"', b"'"): + quoteMark = self.data.currentByte + self.data.position += 1 + oldPosition = self.data.position + if self.data.jumpTo(quoteMark): + return self.data[oldPosition:self.data.position] + else: + return None + else: + # Unquoted value + oldPosition = self.data.position + try: + self.data.skipUntil(spaceCharactersBytes) + return self.data[oldPosition:self.data.position] + except StopIteration: + # Return the whole remaining value + return self.data[oldPosition:] + except StopIteration: + return None + + +def lookupEncoding(encoding): + """Return the python codec name corresponding to an encoding or None if the + string doesn't correspond to a valid encoding.""" + if isinstance(encoding, binary_type): + try: + encoding = encoding.decode("ascii") + except UnicodeDecodeError: + return None + + if encoding is not None: + try: + return webencodings.lookup(encoding) + except AttributeError: + return None + else: + return None diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_tokenizer.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_tokenizer.py new file mode 100644 index 0000000..178f6e7 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_tokenizer.py @@ -0,0 +1,1721 @@ +from __future__ import absolute_import, division, unicode_literals + +from pip._vendor.six import unichr as chr + +from collections import deque + +from .constants import spaceCharacters +from .constants import entities +from .constants import asciiLetters, asciiUpper2Lower +from .constants import digits, hexDigits, EOF +from .constants import tokenTypes, tagTokenTypes +from .constants import replacementCharacters + +from ._inputstream import HTMLInputStream + +from ._trie import Trie + +entitiesTrie = Trie(entities) + + +class HTMLTokenizer(object): + """ This class takes care of tokenizing HTML. + + * self.currentToken + Holds the token that is currently being processed. + + * self.state + Holds a reference to the method to be invoked... XXX + + * self.stream + Points to HTMLInputStream object. + """ + + def __init__(self, stream, parser=None, **kwargs): + + self.stream = HTMLInputStream(stream, **kwargs) + self.parser = parser + + # Setup the initial tokenizer state + self.escapeFlag = False + self.lastFourChars = [] + self.state = self.dataState + self.escape = False + + # The current token being created + self.currentToken = None + super(HTMLTokenizer, self).__init__() + + def __iter__(self): + """ This is where the magic happens. + + We do our usually processing through the states and when we have a token + to return we yield the token which pauses processing until the next token + is requested. + """ + self.tokenQueue = deque([]) + # Start processing. When EOF is reached self.state will return False + # instead of True and the loop will terminate. + while self.state(): + while self.stream.errors: + yield {"type": tokenTypes["ParseError"], "data": self.stream.errors.pop(0)} + while self.tokenQueue: + yield self.tokenQueue.popleft() + + def consumeNumberEntity(self, isHex): + """This function returns either U+FFFD or the character based on the + decimal or hexadecimal representation. It also discards ";" if present. + If not present self.tokenQueue.append({"type": tokenTypes["ParseError"]}) is invoked. + """ + + allowed = digits + radix = 10 + if isHex: + allowed = hexDigits + radix = 16 + + charStack = [] + + # Consume all the characters that are in range while making sure we + # don't hit an EOF. + c = self.stream.char() + while c in allowed and c is not EOF: + charStack.append(c) + c = self.stream.char() + + # Convert the set of characters consumed to an int. + charAsInt = int("".join(charStack), radix) + + # Certain characters get replaced with others + if charAsInt in replacementCharacters: + char = replacementCharacters[charAsInt] + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "illegal-codepoint-for-numeric-entity", + "datavars": {"charAsInt": charAsInt}}) + elif ((0xD800 <= charAsInt <= 0xDFFF) or + (charAsInt > 0x10FFFF)): + char = "\uFFFD" + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "illegal-codepoint-for-numeric-entity", + "datavars": {"charAsInt": charAsInt}}) + else: + # Should speed up this check somehow (e.g. move the set to a constant) + if ((0x0001 <= charAsInt <= 0x0008) or + (0x000E <= charAsInt <= 0x001F) or + (0x007F <= charAsInt <= 0x009F) or + (0xFDD0 <= charAsInt <= 0xFDEF) or + charAsInt in frozenset([0x000B, 0xFFFE, 0xFFFF, 0x1FFFE, + 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, + 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, + 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE, + 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, + 0x9FFFF, 0xAFFFE, 0xAFFFF, 0xBFFFE, + 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, + 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, + 0xFFFFF, 0x10FFFE, 0x10FFFF])): + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": + "illegal-codepoint-for-numeric-entity", + "datavars": {"charAsInt": charAsInt}}) + try: + # Try/except needed as UCS-2 Python builds' unichar only works + # within the BMP. + char = chr(charAsInt) + except ValueError: + v = charAsInt - 0x10000 + char = chr(0xD800 | (v >> 10)) + chr(0xDC00 | (v & 0x3FF)) + + # Discard the ; if present. Otherwise, put it back on the queue and + # invoke parseError on parser. + if c != ";": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "numeric-entity-without-semicolon"}) + self.stream.unget(c) + + return char + + def consumeEntity(self, allowedChar=None, fromAttribute=False): + # Initialise to the default output for when no entity is matched + output = "&" + + charStack = [self.stream.char()] + if (charStack[0] in spaceCharacters or charStack[0] in (EOF, "<", "&") or + (allowedChar is not None and allowedChar == charStack[0])): + self.stream.unget(charStack[0]) + + elif charStack[0] == "#": + # Read the next character to see if it's hex or decimal + hex = False + charStack.append(self.stream.char()) + if charStack[-1] in ("x", "X"): + hex = True + charStack.append(self.stream.char()) + + # charStack[-1] should be the first digit + if (hex and charStack[-1] in hexDigits) \ + or (not hex and charStack[-1] in digits): + # At least one digit found, so consume the whole number + self.stream.unget(charStack[-1]) + output = self.consumeNumberEntity(hex) + else: + # No digits found + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "expected-numeric-entity"}) + self.stream.unget(charStack.pop()) + output = "&" + "".join(charStack) + + else: + # At this point in the process might have named entity. Entities + # are stored in the global variable "entities". + # + # Consume characters and compare to these to a substring of the + # entity names in the list until the substring no longer matches. + while (charStack[-1] is not EOF): + if not entitiesTrie.has_keys_with_prefix("".join(charStack)): + break + charStack.append(self.stream.char()) + + # At this point we have a string that starts with some characters + # that may match an entity + # Try to find the longest entity the string will match to take care + # of ¬i for instance. + try: + entityName = entitiesTrie.longest_prefix("".join(charStack[:-1])) + entityLength = len(entityName) + except KeyError: + entityName = None + + if entityName is not None: + if entityName[-1] != ";": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "named-entity-without-semicolon"}) + if (entityName[-1] != ";" and fromAttribute and + (charStack[entityLength] in asciiLetters or + charStack[entityLength] in digits or + charStack[entityLength] == "=")): + self.stream.unget(charStack.pop()) + output = "&" + "".join(charStack) + else: + output = entities[entityName] + self.stream.unget(charStack.pop()) + output += "".join(charStack[entityLength:]) + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-named-entity"}) + self.stream.unget(charStack.pop()) + output = "&" + "".join(charStack) + + if fromAttribute: + self.currentToken["data"][-1][1] += output + else: + if output in spaceCharacters: + tokenType = "SpaceCharacters" + else: + tokenType = "Characters" + self.tokenQueue.append({"type": tokenTypes[tokenType], "data": output}) + + def processEntityInAttribute(self, allowedChar): + """This method replaces the need for "entityInAttributeValueState". + """ + self.consumeEntity(allowedChar=allowedChar, fromAttribute=True) + + def emitCurrentToken(self): + """This method is a generic handler for emitting the tags. It also sets + the state to "data" because that's what's needed after a token has been + emitted. + """ + token = self.currentToken + # Add token to the queue to be yielded + if (token["type"] in tagTokenTypes): + token["name"] = token["name"].translate(asciiUpper2Lower) + if token["type"] == tokenTypes["EndTag"]: + if token["data"]: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "attributes-in-end-tag"}) + if token["selfClosing"]: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "self-closing-flag-on-end-tag"}) + self.tokenQueue.append(token) + self.state = self.dataState + + # Below are the various tokenizer states worked out. + def dataState(self): + data = self.stream.char() + if data == "&": + self.state = self.entityDataState + elif data == "<": + self.state = self.tagOpenState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\u0000"}) + elif data is EOF: + # Tokenization ends. + return False + elif data in spaceCharacters: + # Directly after emitting a token you switch back to the "data + # state". At that point spaceCharacters are important so they are + # emitted separately. + self.tokenQueue.append({"type": tokenTypes["SpaceCharacters"], "data": + data + self.stream.charsUntil(spaceCharacters, True)}) + # No need to update lastFourChars here, since the first space will + # have already been appended to lastFourChars and will have broken + # any <!-- or --> sequences + else: + chars = self.stream.charsUntil(("&", "<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def entityDataState(self): + self.consumeEntity() + self.state = self.dataState + return True + + def rcdataState(self): + data = self.stream.char() + if data == "&": + self.state = self.characterReferenceInRcdata + elif data == "<": + self.state = self.rcdataLessThanSignState + elif data == EOF: + # Tokenization ends. + return False + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data in spaceCharacters: + # Directly after emitting a token you switch back to the "data + # state". At that point spaceCharacters are important so they are + # emitted separately. + self.tokenQueue.append({"type": tokenTypes["SpaceCharacters"], "data": + data + self.stream.charsUntil(spaceCharacters, True)}) + # No need to update lastFourChars here, since the first space will + # have already been appended to lastFourChars and will have broken + # any <!-- or --> sequences + else: + chars = self.stream.charsUntil(("&", "<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def characterReferenceInRcdata(self): + self.consumeEntity() + self.state = self.rcdataState + return True + + def rawtextState(self): + data = self.stream.char() + if data == "<": + self.state = self.rawtextLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + # Tokenization ends. + return False + else: + chars = self.stream.charsUntil(("<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def scriptDataState(self): + data = self.stream.char() + if data == "<": + self.state = self.scriptDataLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + # Tokenization ends. + return False + else: + chars = self.stream.charsUntil(("<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def plaintextState(self): + data = self.stream.char() + if data == EOF: + # Tokenization ends. + return False + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + self.stream.charsUntil("\u0000")}) + return True + + def tagOpenState(self): + data = self.stream.char() + if data == "!": + self.state = self.markupDeclarationOpenState + elif data == "/": + self.state = self.closeTagOpenState + elif data in asciiLetters: + self.currentToken = {"type": tokenTypes["StartTag"], + "name": data, "data": [], + "selfClosing": False, + "selfClosingAcknowledged": False} + self.state = self.tagNameState + elif data == ">": + # XXX In theory it could be something besides a tag name. But + # do we really care? + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-tag-name-but-got-right-bracket"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<>"}) + self.state = self.dataState + elif data == "?": + # XXX In theory it could be something besides a tag name. But + # do we really care? + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-tag-name-but-got-question-mark"}) + self.stream.unget(data) + self.state = self.bogusCommentState + else: + # XXX + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-tag-name"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.dataState + return True + + def closeTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.currentToken = {"type": tokenTypes["EndTag"], "name": data, + "data": [], "selfClosing": False} + self.state = self.tagNameState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-closing-tag-but-got-right-bracket"}) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-closing-tag-but-got-eof"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.state = self.dataState + else: + # XXX data can be _'_... + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-closing-tag-but-got-char", + "datavars": {"data": data}}) + self.stream.unget(data) + self.state = self.bogusCommentState + return True + + def tagNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeAttributeNameState + elif data == ">": + self.emitCurrentToken() + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-tag-name"}) + self.state = self.dataState + elif data == "/": + self.state = self.selfClosingStartTagState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["name"] += "\uFFFD" + else: + self.currentToken["name"] += data + # (Don't use charsUntil here, because tag names are + # very short and it's faster to not do anything fancy) + return True + + def rcdataLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.rcdataEndTagOpenState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.rcdataState + return True + + def rcdataEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer += data + self.state = self.rcdataEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.stream.unget(data) + self.state = self.rcdataState + return True + + def rcdataEndTagNameState(self): + appropriate = self.currentToken and self.currentToken["name"].lower() == self.temporaryBuffer.lower() + data = self.stream.char() + if data in spaceCharacters and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.beforeAttributeNameState + elif data == "/" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.selfClosingStartTagState + elif data == ">" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "</" + self.temporaryBuffer}) + self.stream.unget(data) + self.state = self.rcdataState + return True + + def rawtextLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.rawtextEndTagOpenState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.rawtextState + return True + + def rawtextEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer += data + self.state = self.rawtextEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.stream.unget(data) + self.state = self.rawtextState + return True + + def rawtextEndTagNameState(self): + appropriate = self.currentToken and self.currentToken["name"].lower() == self.temporaryBuffer.lower() + data = self.stream.char() + if data in spaceCharacters and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.beforeAttributeNameState + elif data == "/" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.selfClosingStartTagState + elif data == ">" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "</" + self.temporaryBuffer}) + self.stream.unget(data) + self.state = self.rawtextState + return True + + def scriptDataLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.scriptDataEndTagOpenState + elif data == "!": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<!"}) + self.state = self.scriptDataEscapeStartState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer += data + self.state = self.scriptDataEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEndTagNameState(self): + appropriate = self.currentToken and self.currentToken["name"].lower() == self.temporaryBuffer.lower() + data = self.stream.char() + if data in spaceCharacters and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.beforeAttributeNameState + elif data == "/" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.selfClosingStartTagState + elif data == ">" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "</" + self.temporaryBuffer}) + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEscapeStartState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataEscapeStartDashState + else: + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEscapeStartDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataEscapedDashDashState + else: + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEscapedState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataEscapedDashState + elif data == "<": + self.state = self.scriptDataEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + self.state = self.dataState + else: + chars = self.stream.charsUntil(("<", "-", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def scriptDataEscapedDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataEscapedDashDashState + elif data == "<": + self.state = self.scriptDataEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataEscapedState + elif data == EOF: + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedDashDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + elif data == "<": + self.state = self.scriptDataEscapedLessThanSignState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": ">"}) + self.state = self.scriptDataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataEscapedState + elif data == EOF: + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.scriptDataEscapedEndTagOpenState + elif data in asciiLetters: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<" + data}) + self.temporaryBuffer = data + self.state = self.scriptDataDoubleEscapeStartState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer = data + self.state = self.scriptDataEscapedEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedEndTagNameState(self): + appropriate = self.currentToken and self.currentToken["name"].lower() == self.temporaryBuffer.lower() + data = self.stream.char() + if data in spaceCharacters and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.beforeAttributeNameState + elif data == "/" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.selfClosingStartTagState + elif data == ">" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "</" + self.temporaryBuffer}) + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataDoubleEscapeStartState(self): + data = self.stream.char() + if data in (spaceCharacters | frozenset(("/", ">"))): + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + if self.temporaryBuffer.lower() == "script": + self.state = self.scriptDataDoubleEscapedState + else: + self.state = self.scriptDataEscapedState + elif data in asciiLetters: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.temporaryBuffer += data + else: + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataDoubleEscapedState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataDoubleEscapedDashState + elif data == "<": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.state = self.scriptDataDoubleEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-script-in-script"}) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + return True + + def scriptDataDoubleEscapedDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataDoubleEscapedDashDashState + elif data == "<": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.state = self.scriptDataDoubleEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataDoubleEscapedState + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-script-in-script"}) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataDoubleEscapedState + return True + + def scriptDataDoubleEscapedDashDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + elif data == "<": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.state = self.scriptDataDoubleEscapedLessThanSignState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": ">"}) + self.state = self.scriptDataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataDoubleEscapedState + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-script-in-script"}) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataDoubleEscapedState + return True + + def scriptDataDoubleEscapedLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "/"}) + self.temporaryBuffer = "" + self.state = self.scriptDataDoubleEscapeEndState + else: + self.stream.unget(data) + self.state = self.scriptDataDoubleEscapedState + return True + + def scriptDataDoubleEscapeEndState(self): + data = self.stream.char() + if data in (spaceCharacters | frozenset(("/", ">"))): + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + if self.temporaryBuffer.lower() == "script": + self.state = self.scriptDataEscapedState + else: + self.state = self.scriptDataDoubleEscapedState + elif data in asciiLetters: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.temporaryBuffer += data + else: + self.stream.unget(data) + self.state = self.scriptDataDoubleEscapedState + return True + + def beforeAttributeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.stream.charsUntil(spaceCharacters, True) + elif data in asciiLetters: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data == ">": + self.emitCurrentToken() + elif data == "/": + self.state = self.selfClosingStartTagState + elif data in ("'", '"', "=", "<"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "invalid-character-in-attribute-name"}) + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"].append(["\uFFFD", ""]) + self.state = self.attributeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-attribute-name-but-got-eof"}) + self.state = self.dataState + else: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + return True + + def attributeNameState(self): + data = self.stream.char() + leavingThisState = True + emitToken = False + if data == "=": + self.state = self.beforeAttributeValueState + elif data in asciiLetters: + self.currentToken["data"][-1][0] += data +\ + self.stream.charsUntil(asciiLetters, True) + leavingThisState = False + elif data == ">": + # XXX If we emit here the attributes are converted to a dict + # without being checked and when the code below runs we error + # because data is a dict not a list + emitToken = True + elif data in spaceCharacters: + self.state = self.afterAttributeNameState + elif data == "/": + self.state = self.selfClosingStartTagState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][0] += "\uFFFD" + leavingThisState = False + elif data in ("'", '"', "<"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": + "invalid-character-in-attribute-name"}) + self.currentToken["data"][-1][0] += data + leavingThisState = False + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "eof-in-attribute-name"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][0] += data + leavingThisState = False + + if leavingThisState: + # Attributes are not dropped at this stage. That happens when the + # start tag token is emitted so values can still be safely appended + # to attributes, but we do want to report the parse error in time. + self.currentToken["data"][-1][0] = ( + self.currentToken["data"][-1][0].translate(asciiUpper2Lower)) + for name, _ in self.currentToken["data"][:-1]: + if self.currentToken["data"][-1][0] == name: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "duplicate-attribute"}) + break + # XXX Fix for above XXX + if emitToken: + self.emitCurrentToken() + return True + + def afterAttributeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.stream.charsUntil(spaceCharacters, True) + elif data == "=": + self.state = self.beforeAttributeValueState + elif data == ">": + self.emitCurrentToken() + elif data in asciiLetters: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data == "/": + self.state = self.selfClosingStartTagState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"].append(["\uFFFD", ""]) + self.state = self.attributeNameState + elif data in ("'", '"', "<"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "invalid-character-after-attribute-name"}) + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-end-of-tag-but-got-eof"}) + self.state = self.dataState + else: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + return True + + def beforeAttributeValueState(self): + data = self.stream.char() + if data in spaceCharacters: + self.stream.charsUntil(spaceCharacters, True) + elif data == "\"": + self.state = self.attributeValueDoubleQuotedState + elif data == "&": + self.state = self.attributeValueUnQuotedState + self.stream.unget(data) + elif data == "'": + self.state = self.attributeValueSingleQuotedState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-attribute-value-but-got-right-bracket"}) + self.emitCurrentToken() + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + self.state = self.attributeValueUnQuotedState + elif data in ("=", "<", "`"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "equals-in-unquoted-attribute-value"}) + self.currentToken["data"][-1][1] += data + self.state = self.attributeValueUnQuotedState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-attribute-value-but-got-eof"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data + self.state = self.attributeValueUnQuotedState + return True + + def attributeValueDoubleQuotedState(self): + data = self.stream.char() + if data == "\"": + self.state = self.afterAttributeValueState + elif data == "&": + self.processEntityInAttribute('"') + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-attribute-value-double-quote"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data +\ + self.stream.charsUntil(("\"", "&", "\u0000")) + return True + + def attributeValueSingleQuotedState(self): + data = self.stream.char() + if data == "'": + self.state = self.afterAttributeValueState + elif data == "&": + self.processEntityInAttribute("'") + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-attribute-value-single-quote"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data +\ + self.stream.charsUntil(("'", "&", "\u0000")) + return True + + def attributeValueUnQuotedState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeAttributeNameState + elif data == "&": + self.processEntityInAttribute(">") + elif data == ">": + self.emitCurrentToken() + elif data in ('"', "'", "=", "<", "`"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-character-in-unquoted-attribute-value"}) + self.currentToken["data"][-1][1] += data + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-attribute-value-no-quotes"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data + self.stream.charsUntil( + frozenset(("&", ">", '"', "'", "=", "<", "`", "\u0000")) | spaceCharacters) + return True + + def afterAttributeValueState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeAttributeNameState + elif data == ">": + self.emitCurrentToken() + elif data == "/": + self.state = self.selfClosingStartTagState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-EOF-after-attribute-value"}) + self.stream.unget(data) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-character-after-attribute-value"}) + self.stream.unget(data) + self.state = self.beforeAttributeNameState + return True + + def selfClosingStartTagState(self): + data = self.stream.char() + if data == ">": + self.currentToken["selfClosing"] = True + self.emitCurrentToken() + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": + "unexpected-EOF-after-solidus-in-tag"}) + self.stream.unget(data) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-character-after-solidus-in-tag"}) + self.stream.unget(data) + self.state = self.beforeAttributeNameState + return True + + def bogusCommentState(self): + # Make a new comment token and give it as value all the characters + # until the first > or EOF (charsUntil checks for EOF automatically) + # and emit it. + data = self.stream.charsUntil(">") + data = data.replace("\u0000", "\uFFFD") + self.tokenQueue.append( + {"type": tokenTypes["Comment"], "data": data}) + + # Eat the character directly after the bogus comment which is either a + # ">" or an EOF. + self.stream.char() + self.state = self.dataState + return True + + def markupDeclarationOpenState(self): + charStack = [self.stream.char()] + if charStack[-1] == "-": + charStack.append(self.stream.char()) + if charStack[-1] == "-": + self.currentToken = {"type": tokenTypes["Comment"], "data": ""} + self.state = self.commentStartState + return True + elif charStack[-1] in ('d', 'D'): + matched = True + for expected in (('o', 'O'), ('c', 'C'), ('t', 'T'), + ('y', 'Y'), ('p', 'P'), ('e', 'E')): + charStack.append(self.stream.char()) + if charStack[-1] not in expected: + matched = False + break + if matched: + self.currentToken = {"type": tokenTypes["Doctype"], + "name": "", + "publicId": None, "systemId": None, + "correct": True} + self.state = self.doctypeState + return True + elif (charStack[-1] == "[" and + self.parser is not None and + self.parser.tree.openElements and + self.parser.tree.openElements[-1].namespace != self.parser.tree.defaultNamespace): + matched = True + for expected in ["C", "D", "A", "T", "A", "["]: + charStack.append(self.stream.char()) + if charStack[-1] != expected: + matched = False + break + if matched: + self.state = self.cdataSectionState + return True + + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-dashes-or-doctype"}) + + while charStack: + self.stream.unget(charStack.pop()) + self.state = self.bogusCommentState + return True + + def commentStartState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentStartDashState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "incorrect-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += data + self.state = self.commentState + return True + + def commentStartDashState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentEndState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "-\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "incorrect-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += "-" + data + self.state = self.commentState + return True + + def commentState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentEndDashState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "eof-in-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += data + \ + self.stream.charsUntil(("-", "\u0000")) + return True + + def commentEndDashState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentEndState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "-\uFFFD" + self.state = self.commentState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment-end-dash"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += "-" + data + self.state = self.commentState + return True + + def commentEndState(self): + data = self.stream.char() + if data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "--\uFFFD" + self.state = self.commentState + elif data == "!": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-bang-after-double-dash-in-comment"}) + self.state = self.commentEndBangState + elif data == "-": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-dash-after-double-dash-in-comment"}) + self.currentToken["data"] += data + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment-double-dash"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + # XXX + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-comment"}) + self.currentToken["data"] += "--" + data + self.state = self.commentState + return True + + def commentEndBangState(self): + data = self.stream.char() + if data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "-": + self.currentToken["data"] += "--!" + self.state = self.commentEndDashState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "--!\uFFFD" + self.state = self.commentState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment-end-bang-state"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += "--!" + data + self.state = self.commentState + return True + + def doctypeState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeDoctypeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-doctype-name-but-got-eof"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "need-space-after-doctype"}) + self.stream.unget(data) + self.state = self.beforeDoctypeNameState + return True + + def beforeDoctypeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-doctype-name-but-got-right-bracket"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["name"] = "\uFFFD" + self.state = self.doctypeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-doctype-name-but-got-eof"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["name"] = data + self.state = self.doctypeNameState + return True + + def doctypeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.state = self.afterDoctypeNameState + elif data == ">": + self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["name"] += "\uFFFD" + self.state = self.doctypeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype-name"}) + self.currentToken["correct"] = False + self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["name"] += data + return True + + def afterDoctypeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.currentToken["correct"] = False + self.stream.unget(data) + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + if data in ("p", "P"): + matched = True + for expected in (("u", "U"), ("b", "B"), ("l", "L"), + ("i", "I"), ("c", "C")): + data = self.stream.char() + if data not in expected: + matched = False + break + if matched: + self.state = self.afterDoctypePublicKeywordState + return True + elif data in ("s", "S"): + matched = True + for expected in (("y", "Y"), ("s", "S"), ("t", "T"), + ("e", "E"), ("m", "M")): + data = self.stream.char() + if data not in expected: + matched = False + break + if matched: + self.state = self.afterDoctypeSystemKeywordState + return True + + # All the characters read before the current 'data' will be + # [a-zA-Z], so they're garbage in the bogus doctype and can be + # discarded; only the latest character might be '>' or EOF + # and needs to be ungetted + self.stream.unget(data) + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-space-or-right-bracket-in-doctype", "datavars": + {"data": data}}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + + return True + + def afterDoctypePublicKeywordState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeDoctypePublicIdentifierState + elif data in ("'", '"'): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.stream.unget(data) + self.state = self.beforeDoctypePublicIdentifierState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.stream.unget(data) + self.state = self.beforeDoctypePublicIdentifierState + return True + + def beforeDoctypePublicIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == "\"": + self.currentToken["publicId"] = "" + self.state = self.doctypePublicIdentifierDoubleQuotedState + elif data == "'": + self.currentToken["publicId"] = "" + self.state = self.doctypePublicIdentifierSingleQuotedState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def doctypePublicIdentifierDoubleQuotedState(self): + data = self.stream.char() + if data == "\"": + self.state = self.afterDoctypePublicIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["publicId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["publicId"] += data + return True + + def doctypePublicIdentifierSingleQuotedState(self): + data = self.stream.char() + if data == "'": + self.state = self.afterDoctypePublicIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["publicId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["publicId"] += data + return True + + def afterDoctypePublicIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.betweenDoctypePublicAndSystemIdentifiersState + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == '"': + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierDoubleQuotedState + elif data == "'": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierSingleQuotedState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def betweenDoctypePublicAndSystemIdentifiersState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == '"': + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierDoubleQuotedState + elif data == "'": + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierSingleQuotedState + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def afterDoctypeSystemKeywordState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeDoctypeSystemIdentifierState + elif data in ("'", '"'): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.stream.unget(data) + self.state = self.beforeDoctypeSystemIdentifierState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.stream.unget(data) + self.state = self.beforeDoctypeSystemIdentifierState + return True + + def beforeDoctypeSystemIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == "\"": + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierDoubleQuotedState + elif data == "'": + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierSingleQuotedState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def doctypeSystemIdentifierDoubleQuotedState(self): + data = self.stream.char() + if data == "\"": + self.state = self.afterDoctypeSystemIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["systemId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["systemId"] += data + return True + + def doctypeSystemIdentifierSingleQuotedState(self): + data = self.stream.char() + if data == "'": + self.state = self.afterDoctypeSystemIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["systemId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["systemId"] += data + return True + + def afterDoctypeSystemIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.state = self.bogusDoctypeState + return True + + def bogusDoctypeState(self): + data = self.stream.char() + if data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + # XXX EMIT + self.stream.unget(data) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + pass + return True + + def cdataSectionState(self): + data = [] + while True: + data.append(self.stream.charsUntil("]")) + data.append(self.stream.charsUntil(">")) + char = self.stream.char() + if char == EOF: + break + else: + assert char == ">" + if data[-1][-2:] == "]]": + data[-1] = data[-1][:-2] + break + else: + data.append(char) + + data = "".join(data) # pylint:disable=redefined-variable-type + # Deal with null here rather than in the parser + nullCount = data.count("\u0000") + if nullCount > 0: + for _ in range(nullCount): + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + data = data.replace("\u0000", "\uFFFD") + if data: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": data}) + self.state = self.dataState + return True diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/__init__.py new file mode 100644 index 0000000..a5ba4bf --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/__init__.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import, division, unicode_literals + +from .py import Trie as PyTrie + +Trie = PyTrie + +# pylint:disable=wrong-import-position +try: + from .datrie import Trie as DATrie +except ImportError: + pass +else: + Trie = DATrie +# pylint:enable=wrong-import-position diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/_base.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/_base.py new file mode 100644 index 0000000..25eece4 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/_base.py @@ -0,0 +1,38 @@ +from __future__ import absolute_import, division, unicode_literals + +from collections import Mapping + + +class Trie(Mapping): + """Abstract base class for tries""" + + def keys(self, prefix=None): + # pylint:disable=arguments-differ + keys = super(Trie, self).keys() + + if prefix is None: + return set(keys) + + # Python 2.6: no set comprehensions + return set([x for x in keys if x.startswith(prefix)]) + + def has_keys_with_prefix(self, prefix): + for key in self.keys(): + if key.startswith(prefix): + return True + + return False + + def longest_prefix(self, prefix): + if prefix in self: + return prefix + + for i in range(1, len(prefix) + 1): + if prefix[:-i] in self: + return prefix[:-i] + + raise KeyError(prefix) + + def longest_prefix_item(self, prefix): + lprefix = self.longest_prefix(prefix) + return (lprefix, self[lprefix]) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/datrie.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/datrie.py new file mode 100644 index 0000000..e2e5f86 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/datrie.py @@ -0,0 +1,44 @@ +from __future__ import absolute_import, division, unicode_literals + +from datrie import Trie as DATrie +from pip._vendor.six import text_type + +from ._base import Trie as ABCTrie + + +class Trie(ABCTrie): + def __init__(self, data): + chars = set() + for key in data.keys(): + if not isinstance(key, text_type): + raise TypeError("All keys must be strings") + for char in key: + chars.add(char) + + self._data = DATrie("".join(chars)) + for key, value in data.items(): + self._data[key] = value + + def __contains__(self, key): + return key in self._data + + def __len__(self): + return len(self._data) + + def __iter__(self): + raise NotImplementedError() + + def __getitem__(self, key): + return self._data[key] + + def keys(self, prefix=None): + return self._data.keys(prefix) + + def has_keys_with_prefix(self, prefix): + return self._data.has_keys_with_prefix(prefix) + + def longest_prefix(self, prefix): + return self._data.longest_prefix(prefix) + + def longest_prefix_item(self, prefix): + return self._data.longest_prefix_item(prefix) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/py.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/py.py new file mode 100644 index 0000000..c178b21 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_trie/py.py @@ -0,0 +1,67 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +from bisect import bisect_left + +from ._base import Trie as ABCTrie + + +class Trie(ABCTrie): + def __init__(self, data): + if not all(isinstance(x, text_type) for x in data.keys()): + raise TypeError("All keys must be strings") + + self._data = data + self._keys = sorted(data.keys()) + self._cachestr = "" + self._cachepoints = (0, len(data)) + + def __contains__(self, key): + return key in self._data + + def __len__(self): + return len(self._data) + + def __iter__(self): + return iter(self._data) + + def __getitem__(self, key): + return self._data[key] + + def keys(self, prefix=None): + if prefix is None or prefix == "" or not self._keys: + return set(self._keys) + + if prefix.startswith(self._cachestr): + lo, hi = self._cachepoints + start = i = bisect_left(self._keys, prefix, lo, hi) + else: + start = i = bisect_left(self._keys, prefix) + + keys = set() + if start == len(self._keys): + return keys + + while self._keys[i].startswith(prefix): + keys.add(self._keys[i]) + i += 1 + + self._cachestr = prefix + self._cachepoints = (start, i) + + return keys + + def has_keys_with_prefix(self, prefix): + if prefix in self._data: + return True + + if prefix.startswith(self._cachestr): + lo, hi = self._cachepoints + i = bisect_left(self._keys, prefix, lo, hi) + else: + i = bisect_left(self._keys, prefix) + + if i == len(self._keys): + return False + + return self._keys[i].startswith(prefix) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_utils.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_utils.py new file mode 100644 index 0000000..55d6747 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/_utils.py @@ -0,0 +1,127 @@ +from __future__ import absolute_import, division, unicode_literals + +import sys +from types import ModuleType + +from pip._vendor.six import text_type + +try: + import xml.etree.cElementTree as default_etree +except ImportError: + import xml.etree.ElementTree as default_etree + + +__all__ = ["default_etree", "MethodDispatcher", "isSurrogatePair", + "surrogatePairToCodepoint", "moduleFactoryFactory", + "supports_lone_surrogates", "PY27"] + + +PY27 = sys.version_info[0] == 2 and sys.version_info[1] >= 7 + +# Platforms not supporting lone surrogates (\uD800-\uDFFF) should be +# caught by the below test. In general this would be any platform +# using UTF-16 as its encoding of unicode strings, such as +# Jython. This is because UTF-16 itself is based on the use of such +# surrogates, and there is no mechanism to further escape such +# escapes. +try: + _x = eval('"\\uD800"') # pylint:disable=eval-used + if not isinstance(_x, text_type): + # We need this with u"" because of http://bugs.jython.org/issue2039 + _x = eval('u"\\uD800"') # pylint:disable=eval-used + assert isinstance(_x, text_type) +except: # pylint:disable=bare-except + supports_lone_surrogates = False +else: + supports_lone_surrogates = True + + +class MethodDispatcher(dict): + """Dict with 2 special properties: + + On initiation, keys that are lists, sets or tuples are converted to + multiple keys so accessing any one of the items in the original + list-like object returns the matching value + + md = MethodDispatcher({("foo", "bar"):"baz"}) + md["foo"] == "baz" + + A default value which can be set through the default attribute. + """ + + def __init__(self, items=()): + # Using _dictEntries instead of directly assigning to self is about + # twice as fast. Please do careful performance testing before changing + # anything here. + _dictEntries = [] + for name, value in items: + if isinstance(name, (list, tuple, frozenset, set)): + for item in name: + _dictEntries.append((item, value)) + else: + _dictEntries.append((name, value)) + dict.__init__(self, _dictEntries) + assert len(self) == len(_dictEntries) + self.default = None + + def __getitem__(self, key): + return dict.get(self, key, self.default) + + +# Some utility functions to deal with weirdness around UCS2 vs UCS4 +# python builds + +def isSurrogatePair(data): + return (len(data) == 2 and + ord(data[0]) >= 0xD800 and ord(data[0]) <= 0xDBFF and + ord(data[1]) >= 0xDC00 and ord(data[1]) <= 0xDFFF) + + +def surrogatePairToCodepoint(data): + char_val = (0x10000 + (ord(data[0]) - 0xD800) * 0x400 + + (ord(data[1]) - 0xDC00)) + return char_val + +# Module Factory Factory (no, this isn't Java, I know) +# Here to stop this being duplicated all over the place. + + +def moduleFactoryFactory(factory): + moduleCache = {} + + def moduleFactory(baseModule, *args, **kwargs): + if isinstance(ModuleType.__name__, type("")): + name = "_%s_factory" % baseModule.__name__ + else: + name = b"_%s_factory" % baseModule.__name__ + + kwargs_tuple = tuple(kwargs.items()) + + try: + return moduleCache[name][args][kwargs_tuple] + except KeyError: + mod = ModuleType(name) + objs = factory(baseModule, *args, **kwargs) + mod.__dict__.update(objs) + if "name" not in moduleCache: + moduleCache[name] = {} + if "args" not in moduleCache[name]: + moduleCache[name][args] = {} + if "kwargs" not in moduleCache[name][args]: + moduleCache[name][args][kwargs_tuple] = {} + moduleCache[name][args][kwargs_tuple] = mod + return mod + + return moduleFactory + + +def memoize(func): + cache = {} + + def wrapped(*args, **kwargs): + key = (tuple(args), tuple(kwargs.items())) + if key not in cache: + cache[key] = func(*args, **kwargs) + return cache[key] + + return wrapped diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/constants.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/constants.py new file mode 100644 index 0000000..9e7541d --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/constants.py @@ -0,0 +1,2945 @@ +from __future__ import absolute_import, division, unicode_literals + +import string + +EOF = None + +E = { + "null-character": + "Null character in input stream, replaced with U+FFFD.", + "invalid-codepoint": + "Invalid codepoint in stream.", + "incorrectly-placed-solidus": + "Solidus (/) incorrectly placed in tag.", + "incorrect-cr-newline-entity": + "Incorrect CR newline entity, replaced with LF.", + "illegal-windows-1252-entity": + "Entity used with illegal number (windows-1252 reference).", + "cant-convert-numeric-entity": + "Numeric entity couldn't be converted to character " + "(codepoint U+%(charAsInt)08x).", + "illegal-codepoint-for-numeric-entity": + "Numeric entity represents an illegal codepoint: " + "U+%(charAsInt)08x.", + "numeric-entity-without-semicolon": + "Numeric entity didn't end with ';'.", + "expected-numeric-entity-but-got-eof": + "Numeric entity expected. Got end of file instead.", + "expected-numeric-entity": + "Numeric entity expected but none found.", + "named-entity-without-semicolon": + "Named entity didn't end with ';'.", + "expected-named-entity": + "Named entity expected. Got none.", + "attributes-in-end-tag": + "End tag contains unexpected attributes.", + 'self-closing-flag-on-end-tag': + "End tag contains unexpected self-closing flag.", + "expected-tag-name-but-got-right-bracket": + "Expected tag name. Got '>' instead.", + "expected-tag-name-but-got-question-mark": + "Expected tag name. Got '?' instead. (HTML doesn't " + "support processing instructions.)", + "expected-tag-name": + "Expected tag name. Got something else instead", + "expected-closing-tag-but-got-right-bracket": + "Expected closing tag. Got '>' instead. Ignoring '</>'.", + "expected-closing-tag-but-got-eof": + "Expected closing tag. Unexpected end of file.", + "expected-closing-tag-but-got-char": + "Expected closing tag. Unexpected character '%(data)s' found.", + "eof-in-tag-name": + "Unexpected end of file in the tag name.", + "expected-attribute-name-but-got-eof": + "Unexpected end of file. Expected attribute name instead.", + "eof-in-attribute-name": + "Unexpected end of file in attribute name.", + "invalid-character-in-attribute-name": + "Invalid character in attribute name", + "duplicate-attribute": + "Dropped duplicate attribute on tag.", + "expected-end-of-tag-name-but-got-eof": + "Unexpected end of file. Expected = or end of tag.", + "expected-attribute-value-but-got-eof": + "Unexpected end of file. Expected attribute value.", + "expected-attribute-value-but-got-right-bracket": + "Expected attribute value. Got '>' instead.", + 'equals-in-unquoted-attribute-value': + "Unexpected = in unquoted attribute", + 'unexpected-character-in-unquoted-attribute-value': + "Unexpected character in unquoted attribute", + "invalid-character-after-attribute-name": + "Unexpected character after attribute name.", + "unexpected-character-after-attribute-value": + "Unexpected character after attribute value.", + "eof-in-attribute-value-double-quote": + "Unexpected end of file in attribute value (\").", + "eof-in-attribute-value-single-quote": + "Unexpected end of file in attribute value (').", + "eof-in-attribute-value-no-quotes": + "Unexpected end of file in attribute value.", + "unexpected-EOF-after-solidus-in-tag": + "Unexpected end of file in tag. Expected >", + "unexpected-character-after-solidus-in-tag": + "Unexpected character after / in tag. Expected >", + "expected-dashes-or-doctype": + "Expected '--' or 'DOCTYPE'. Not found.", + "unexpected-bang-after-double-dash-in-comment": + "Unexpected ! after -- in comment", + "unexpected-space-after-double-dash-in-comment": + "Unexpected space after -- in comment", + "incorrect-comment": + "Incorrect comment.", + "eof-in-comment": + "Unexpected end of file in comment.", + "eof-in-comment-end-dash": + "Unexpected end of file in comment (-)", + "unexpected-dash-after-double-dash-in-comment": + "Unexpected '-' after '--' found in comment.", + "eof-in-comment-double-dash": + "Unexpected end of file in comment (--).", + "eof-in-comment-end-space-state": + "Unexpected end of file in comment.", + "eof-in-comment-end-bang-state": + "Unexpected end of file in comment.", + "unexpected-char-in-comment": + "Unexpected character in comment found.", + "need-space-after-doctype": + "No space after literal string 'DOCTYPE'.", + "expected-doctype-name-but-got-right-bracket": + "Unexpected > character. Expected DOCTYPE name.", + "expected-doctype-name-but-got-eof": + "Unexpected end of file. Expected DOCTYPE name.", + "eof-in-doctype-name": + "Unexpected end of file in DOCTYPE name.", + "eof-in-doctype": + "Unexpected end of file in DOCTYPE.", + "expected-space-or-right-bracket-in-doctype": + "Expected space or '>'. Got '%(data)s'", + "unexpected-end-of-doctype": + "Unexpected end of DOCTYPE.", + "unexpected-char-in-doctype": + "Unexpected character in DOCTYPE.", + "eof-in-innerhtml": + "XXX innerHTML EOF", + "unexpected-doctype": + "Unexpected DOCTYPE. Ignored.", + "non-html-root": + "html needs to be the first start tag.", + "expected-doctype-but-got-eof": + "Unexpected End of file. Expected DOCTYPE.", + "unknown-doctype": + "Erroneous DOCTYPE.", + "expected-doctype-but-got-chars": + "Unexpected non-space characters. Expected DOCTYPE.", + "expected-doctype-but-got-start-tag": + "Unexpected start tag (%(name)s). Expected DOCTYPE.", + "expected-doctype-but-got-end-tag": + "Unexpected end tag (%(name)s). Expected DOCTYPE.", + "end-tag-after-implied-root": + "Unexpected end tag (%(name)s) after the (implied) root element.", + "expected-named-closing-tag-but-got-eof": + "Unexpected end of file. Expected end tag (%(name)s).", + "two-heads-are-not-better-than-one": + "Unexpected start tag head in existing head. Ignored.", + "unexpected-end-tag": + "Unexpected end tag (%(name)s). Ignored.", + "unexpected-start-tag-out-of-my-head": + "Unexpected start tag (%(name)s) that can be in head. Moved.", + "unexpected-start-tag": + "Unexpected start tag (%(name)s).", + "missing-end-tag": + "Missing end tag (%(name)s).", + "missing-end-tags": + "Missing end tags (%(name)s).", + "unexpected-start-tag-implies-end-tag": + "Unexpected start tag (%(startName)s) " + "implies end tag (%(endName)s).", + "unexpected-start-tag-treated-as": + "Unexpected start tag (%(originalName)s). Treated as %(newName)s.", + "deprecated-tag": + "Unexpected start tag %(name)s. Don't use it!", + "unexpected-start-tag-ignored": + "Unexpected start tag %(name)s. Ignored.", + "expected-one-end-tag-but-got-another": + "Unexpected end tag (%(gotName)s). " + "Missing end tag (%(expectedName)s).", + "end-tag-too-early": + "End tag (%(name)s) seen too early. Expected other end tag.", + "end-tag-too-early-named": + "Unexpected end tag (%(gotName)s). Expected end tag (%(expectedName)s).", + "end-tag-too-early-ignored": + "End tag (%(name)s) seen too early. Ignored.", + "adoption-agency-1.1": + "End tag (%(name)s) violates step 1, " + "paragraph 1 of the adoption agency algorithm.", + "adoption-agency-1.2": + "End tag (%(name)s) violates step 1, " + "paragraph 2 of the adoption agency algorithm.", + "adoption-agency-1.3": + "End tag (%(name)s) violates step 1, " + "paragraph 3 of the adoption agency algorithm.", + "adoption-agency-4.4": + "End tag (%(name)s) violates step 4, " + "paragraph 4 of the adoption agency algorithm.", + "unexpected-end-tag-treated-as": + "Unexpected end tag (%(originalName)s). Treated as %(newName)s.", + "no-end-tag": + "This element (%(name)s) has no end tag.", + "unexpected-implied-end-tag-in-table": + "Unexpected implied end tag (%(name)s) in the table phase.", + "unexpected-implied-end-tag-in-table-body": + "Unexpected implied end tag (%(name)s) in the table body phase.", + "unexpected-char-implies-table-voodoo": + "Unexpected non-space characters in " + "table context caused voodoo mode.", + "unexpected-hidden-input-in-table": + "Unexpected input with type hidden in table context.", + "unexpected-form-in-table": + "Unexpected form in table context.", + "unexpected-start-tag-implies-table-voodoo": + "Unexpected start tag (%(name)s) in " + "table context caused voodoo mode.", + "unexpected-end-tag-implies-table-voodoo": + "Unexpected end tag (%(name)s) in " + "table context caused voodoo mode.", + "unexpected-cell-in-table-body": + "Unexpected table cell start tag (%(name)s) " + "in the table body phase.", + "unexpected-cell-end-tag": + "Got table cell end tag (%(name)s) " + "while required end tags are missing.", + "unexpected-end-tag-in-table-body": + "Unexpected end tag (%(name)s) in the table body phase. Ignored.", + "unexpected-implied-end-tag-in-table-row": + "Unexpected implied end tag (%(name)s) in the table row phase.", + "unexpected-end-tag-in-table-row": + "Unexpected end tag (%(name)s) in the table row phase. Ignored.", + "unexpected-select-in-select": + "Unexpected select start tag in the select phase " + "treated as select end tag.", + "unexpected-input-in-select": + "Unexpected input start tag in the select phase.", + "unexpected-start-tag-in-select": + "Unexpected start tag token (%(name)s in the select phase. " + "Ignored.", + "unexpected-end-tag-in-select": + "Unexpected end tag (%(name)s) in the select phase. Ignored.", + "unexpected-table-element-start-tag-in-select-in-table": + "Unexpected table element start tag (%(name)s) in the select in table phase.", + "unexpected-table-element-end-tag-in-select-in-table": + "Unexpected table element end tag (%(name)s) in the select in table phase.", + "unexpected-char-after-body": + "Unexpected non-space characters in the after body phase.", + "unexpected-start-tag-after-body": + "Unexpected start tag token (%(name)s)" + " in the after body phase.", + "unexpected-end-tag-after-body": + "Unexpected end tag token (%(name)s)" + " in the after body phase.", + "unexpected-char-in-frameset": + "Unexpected characters in the frameset phase. Characters ignored.", + "unexpected-start-tag-in-frameset": + "Unexpected start tag token (%(name)s)" + " in the frameset phase. Ignored.", + "unexpected-frameset-in-frameset-innerhtml": + "Unexpected end tag token (frameset) " + "in the frameset phase (innerHTML).", + "unexpected-end-tag-in-frameset": + "Unexpected end tag token (%(name)s)" + " in the frameset phase. Ignored.", + "unexpected-char-after-frameset": + "Unexpected non-space characters in the " + "after frameset phase. Ignored.", + "unexpected-start-tag-after-frameset": + "Unexpected start tag (%(name)s)" + " in the after frameset phase. Ignored.", + "unexpected-end-tag-after-frameset": + "Unexpected end tag (%(name)s)" + " in the after frameset phase. Ignored.", + "unexpected-end-tag-after-body-innerhtml": + "Unexpected end tag after body(innerHtml)", + "expected-eof-but-got-char": + "Unexpected non-space characters. Expected end of file.", + "expected-eof-but-got-start-tag": + "Unexpected start tag (%(name)s)" + ". Expected end of file.", + "expected-eof-but-got-end-tag": + "Unexpected end tag (%(name)s)" + ". Expected end of file.", + "eof-in-table": + "Unexpected end of file. Expected table content.", + "eof-in-select": + "Unexpected end of file. Expected select content.", + "eof-in-frameset": + "Unexpected end of file. Expected frameset content.", + "eof-in-script-in-script": + "Unexpected end of file. Expected script content.", + "eof-in-foreign-lands": + "Unexpected end of file. Expected foreign content", + "non-void-element-with-trailing-solidus": + "Trailing solidus not allowed on element %(name)s", + "unexpected-html-element-in-foreign-content": + "Element %(name)s not allowed in a non-html context", + "unexpected-end-tag-before-html": + "Unexpected end tag (%(name)s) before html.", + "unexpected-inhead-noscript-tag": + "Element %(name)s not allowed in a inhead-noscript context", + "eof-in-head-noscript": + "Unexpected end of file. Expected inhead-noscript content", + "char-in-head-noscript": + "Unexpected non-space character. Expected inhead-noscript content", + "XXX-undefined-error": + "Undefined error (this sucks and should be fixed)", +} + +namespaces = { + "html": "http://www.w3.org/1999/xhtml", + "mathml": "http://www.w3.org/1998/Math/MathML", + "svg": "http://www.w3.org/2000/svg", + "xlink": "http://www.w3.org/1999/xlink", + "xml": "http://www.w3.org/XML/1998/namespace", + "xmlns": "http://www.w3.org/2000/xmlns/" +} + +scopingElements = frozenset([ + (namespaces["html"], "applet"), + (namespaces["html"], "caption"), + (namespaces["html"], "html"), + (namespaces["html"], "marquee"), + (namespaces["html"], "object"), + (namespaces["html"], "table"), + (namespaces["html"], "td"), + (namespaces["html"], "th"), + (namespaces["mathml"], "mi"), + (namespaces["mathml"], "mo"), + (namespaces["mathml"], "mn"), + (namespaces["mathml"], "ms"), + (namespaces["mathml"], "mtext"), + (namespaces["mathml"], "annotation-xml"), + (namespaces["svg"], "foreignObject"), + (namespaces["svg"], "desc"), + (namespaces["svg"], "title"), +]) + +formattingElements = frozenset([ + (namespaces["html"], "a"), + (namespaces["html"], "b"), + (namespaces["html"], "big"), + (namespaces["html"], "code"), + (namespaces["html"], "em"), + (namespaces["html"], "font"), + (namespaces["html"], "i"), + (namespaces["html"], "nobr"), + (namespaces["html"], "s"), + (namespaces["html"], "small"), + (namespaces["html"], "strike"), + (namespaces["html"], "strong"), + (namespaces["html"], "tt"), + (namespaces["html"], "u") +]) + +specialElements = frozenset([ + (namespaces["html"], "address"), + (namespaces["html"], "applet"), + (namespaces["html"], "area"), + (namespaces["html"], "article"), + (namespaces["html"], "aside"), + (namespaces["html"], "base"), + (namespaces["html"], "basefont"), + (namespaces["html"], "bgsound"), + (namespaces["html"], "blockquote"), + (namespaces["html"], "body"), + (namespaces["html"], "br"), + (namespaces["html"], "button"), + (namespaces["html"], "caption"), + (namespaces["html"], "center"), + (namespaces["html"], "col"), + (namespaces["html"], "colgroup"), + (namespaces["html"], "command"), + (namespaces["html"], "dd"), + (namespaces["html"], "details"), + (namespaces["html"], "dir"), + (namespaces["html"], "div"), + (namespaces["html"], "dl"), + (namespaces["html"], "dt"), + (namespaces["html"], "embed"), + (namespaces["html"], "fieldset"), + (namespaces["html"], "figure"), + (namespaces["html"], "footer"), + (namespaces["html"], "form"), + (namespaces["html"], "frame"), + (namespaces["html"], "frameset"), + (namespaces["html"], "h1"), + (namespaces["html"], "h2"), + (namespaces["html"], "h3"), + (namespaces["html"], "h4"), + (namespaces["html"], "h5"), + (namespaces["html"], "h6"), + (namespaces["html"], "head"), + (namespaces["html"], "header"), + (namespaces["html"], "hr"), + (namespaces["html"], "html"), + (namespaces["html"], "iframe"), + # Note that image is commented out in the spec as "this isn't an + # element that can end up on the stack, so it doesn't matter," + (namespaces["html"], "image"), + (namespaces["html"], "img"), + (namespaces["html"], "input"), + (namespaces["html"], "isindex"), + (namespaces["html"], "li"), + (namespaces["html"], "link"), + (namespaces["html"], "listing"), + (namespaces["html"], "marquee"), + (namespaces["html"], "menu"), + (namespaces["html"], "meta"), + (namespaces["html"], "nav"), + (namespaces["html"], "noembed"), + (namespaces["html"], "noframes"), + (namespaces["html"], "noscript"), + (namespaces["html"], "object"), + (namespaces["html"], "ol"), + (namespaces["html"], "p"), + (namespaces["html"], "param"), + (namespaces["html"], "plaintext"), + (namespaces["html"], "pre"), + (namespaces["html"], "script"), + (namespaces["html"], "section"), + (namespaces["html"], "select"), + (namespaces["html"], "style"), + (namespaces["html"], "table"), + (namespaces["html"], "tbody"), + (namespaces["html"], "td"), + (namespaces["html"], "textarea"), + (namespaces["html"], "tfoot"), + (namespaces["html"], "th"), + (namespaces["html"], "thead"), + (namespaces["html"], "title"), + (namespaces["html"], "tr"), + (namespaces["html"], "ul"), + (namespaces["html"], "wbr"), + (namespaces["html"], "xmp"), + (namespaces["svg"], "foreignObject") +]) + +htmlIntegrationPointElements = frozenset([ + (namespaces["mathml"], "annotaion-xml"), + (namespaces["svg"], "foreignObject"), + (namespaces["svg"], "desc"), + (namespaces["svg"], "title") +]) + +mathmlTextIntegrationPointElements = frozenset([ + (namespaces["mathml"], "mi"), + (namespaces["mathml"], "mo"), + (namespaces["mathml"], "mn"), + (namespaces["mathml"], "ms"), + (namespaces["mathml"], "mtext") +]) + +adjustSVGAttributes = { + "attributename": "attributeName", + "attributetype": "attributeType", + "basefrequency": "baseFrequency", + "baseprofile": "baseProfile", + "calcmode": "calcMode", + "clippathunits": "clipPathUnits", + "contentscripttype": "contentScriptType", + "contentstyletype": "contentStyleType", + "diffuseconstant": "diffuseConstant", + "edgemode": "edgeMode", + "externalresourcesrequired": "externalResourcesRequired", + "filterres": "filterRes", + "filterunits": "filterUnits", + "glyphref": "glyphRef", + "gradienttransform": "gradientTransform", + "gradientunits": "gradientUnits", + "kernelmatrix": "kernelMatrix", + "kernelunitlength": "kernelUnitLength", + "keypoints": "keyPoints", + "keysplines": "keySplines", + "keytimes": "keyTimes", + "lengthadjust": "lengthAdjust", + "limitingconeangle": "limitingConeAngle", + "markerheight": "markerHeight", + "markerunits": "markerUnits", + "markerwidth": "markerWidth", + "maskcontentunits": "maskContentUnits", + "maskunits": "maskUnits", + "numoctaves": "numOctaves", + "pathlength": "pathLength", + "patterncontentunits": "patternContentUnits", + "patterntransform": "patternTransform", + "patternunits": "patternUnits", + "pointsatx": "pointsAtX", + "pointsaty": "pointsAtY", + "pointsatz": "pointsAtZ", + "preservealpha": "preserveAlpha", + "preserveaspectratio": "preserveAspectRatio", + "primitiveunits": "primitiveUnits", + "refx": "refX", + "refy": "refY", + "repeatcount": "repeatCount", + "repeatdur": "repeatDur", + "requiredextensions": "requiredExtensions", + "requiredfeatures": "requiredFeatures", + "specularconstant": "specularConstant", + "specularexponent": "specularExponent", + "spreadmethod": "spreadMethod", + "startoffset": "startOffset", + "stddeviation": "stdDeviation", + "stitchtiles": "stitchTiles", + "surfacescale": "surfaceScale", + "systemlanguage": "systemLanguage", + "tablevalues": "tableValues", + "targetx": "targetX", + "targety": "targetY", + "textlength": "textLength", + "viewbox": "viewBox", + "viewtarget": "viewTarget", + "xchannelselector": "xChannelSelector", + "ychannelselector": "yChannelSelector", + "zoomandpan": "zoomAndPan" +} + +adjustMathMLAttributes = {"definitionurl": "definitionURL"} + +adjustForeignAttributes = { + "xlink:actuate": ("xlink", "actuate", namespaces["xlink"]), + "xlink:arcrole": ("xlink", "arcrole", namespaces["xlink"]), + "xlink:href": ("xlink", "href", namespaces["xlink"]), + "xlink:role": ("xlink", "role", namespaces["xlink"]), + "xlink:show": ("xlink", "show", namespaces["xlink"]), + "xlink:title": ("xlink", "title", namespaces["xlink"]), + "xlink:type": ("xlink", "type", namespaces["xlink"]), + "xml:base": ("xml", "base", namespaces["xml"]), + "xml:lang": ("xml", "lang", namespaces["xml"]), + "xml:space": ("xml", "space", namespaces["xml"]), + "xmlns": (None, "xmlns", namespaces["xmlns"]), + "xmlns:xlink": ("xmlns", "xlink", namespaces["xmlns"]) +} + +unadjustForeignAttributes = dict([((ns, local), qname) for qname, (prefix, local, ns) in + adjustForeignAttributes.items()]) + +spaceCharacters = frozenset([ + "\t", + "\n", + "\u000C", + " ", + "\r" +]) + +tableInsertModeElements = frozenset([ + "table", + "tbody", + "tfoot", + "thead", + "tr" +]) + +asciiLowercase = frozenset(string.ascii_lowercase) +asciiUppercase = frozenset(string.ascii_uppercase) +asciiLetters = frozenset(string.ascii_letters) +digits = frozenset(string.digits) +hexDigits = frozenset(string.hexdigits) + +asciiUpper2Lower = dict([(ord(c), ord(c.lower())) + for c in string.ascii_uppercase]) + +# Heading elements need to be ordered +headingElements = ( + "h1", + "h2", + "h3", + "h4", + "h5", + "h6" +) + +voidElements = frozenset([ + "base", + "command", + "event-source", + "link", + "meta", + "hr", + "br", + "img", + "embed", + "param", + "area", + "col", + "input", + "source", + "track" +]) + +cdataElements = frozenset(['title', 'textarea']) + +rcdataElements = frozenset([ + 'style', + 'script', + 'xmp', + 'iframe', + 'noembed', + 'noframes', + 'noscript' +]) + +booleanAttributes = { + "": frozenset(["irrelevant"]), + "style": frozenset(["scoped"]), + "img": frozenset(["ismap"]), + "audio": frozenset(["autoplay", "controls"]), + "video": frozenset(["autoplay", "controls"]), + "script": frozenset(["defer", "async"]), + "details": frozenset(["open"]), + "datagrid": frozenset(["multiple", "disabled"]), + "command": frozenset(["hidden", "disabled", "checked", "default"]), + "hr": frozenset(["noshade"]), + "menu": frozenset(["autosubmit"]), + "fieldset": frozenset(["disabled", "readonly"]), + "option": frozenset(["disabled", "readonly", "selected"]), + "optgroup": frozenset(["disabled", "readonly"]), + "button": frozenset(["disabled", "autofocus"]), + "input": frozenset(["disabled", "readonly", "required", "autofocus", "checked", "ismap"]), + "select": frozenset(["disabled", "readonly", "autofocus", "multiple"]), + "output": frozenset(["disabled", "readonly"]), +} + +# entitiesWindows1252 has to be _ordered_ and needs to have an index. It +# therefore can't be a frozenset. +entitiesWindows1252 = ( + 8364, # 0x80 0x20AC EURO SIGN + 65533, # 0x81 UNDEFINED + 8218, # 0x82 0x201A SINGLE LOW-9 QUOTATION MARK + 402, # 0x83 0x0192 LATIN SMALL LETTER F WITH HOOK + 8222, # 0x84 0x201E DOUBLE LOW-9 QUOTATION MARK + 8230, # 0x85 0x2026 HORIZONTAL ELLIPSIS + 8224, # 0x86 0x2020 DAGGER + 8225, # 0x87 0x2021 DOUBLE DAGGER + 710, # 0x88 0x02C6 MODIFIER LETTER CIRCUMFLEX ACCENT + 8240, # 0x89 0x2030 PER MILLE SIGN + 352, # 0x8A 0x0160 LATIN CAPITAL LETTER S WITH CARON + 8249, # 0x8B 0x2039 SINGLE LEFT-POINTING ANGLE QUOTATION MARK + 338, # 0x8C 0x0152 LATIN CAPITAL LIGATURE OE + 65533, # 0x8D UNDEFINED + 381, # 0x8E 0x017D LATIN CAPITAL LETTER Z WITH CARON + 65533, # 0x8F UNDEFINED + 65533, # 0x90 UNDEFINED + 8216, # 0x91 0x2018 LEFT SINGLE QUOTATION MARK + 8217, # 0x92 0x2019 RIGHT SINGLE QUOTATION MARK + 8220, # 0x93 0x201C LEFT DOUBLE QUOTATION MARK + 8221, # 0x94 0x201D RIGHT DOUBLE QUOTATION MARK + 8226, # 0x95 0x2022 BULLET + 8211, # 0x96 0x2013 EN DASH + 8212, # 0x97 0x2014 EM DASH + 732, # 0x98 0x02DC SMALL TILDE + 8482, # 0x99 0x2122 TRADE MARK SIGN + 353, # 0x9A 0x0161 LATIN SMALL LETTER S WITH CARON + 8250, # 0x9B 0x203A SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + 339, # 0x9C 0x0153 LATIN SMALL LIGATURE OE + 65533, # 0x9D UNDEFINED + 382, # 0x9E 0x017E LATIN SMALL LETTER Z WITH CARON + 376 # 0x9F 0x0178 LATIN CAPITAL LETTER Y WITH DIAERESIS +) + +xmlEntities = frozenset(['lt;', 'gt;', 'amp;', 'apos;', 'quot;']) + +entities = { + "AElig": "\xc6", + "AElig;": "\xc6", + "AMP": "&", + "AMP;": "&", + "Aacute": "\xc1", + "Aacute;": "\xc1", + "Abreve;": "\u0102", + "Acirc": "\xc2", + "Acirc;": "\xc2", + "Acy;": "\u0410", + "Afr;": "\U0001d504", + "Agrave": "\xc0", + "Agrave;": "\xc0", + "Alpha;": "\u0391", + "Amacr;": "\u0100", + "And;": "\u2a53", + "Aogon;": "\u0104", + "Aopf;": "\U0001d538", + "ApplyFunction;": "\u2061", + "Aring": "\xc5", + "Aring;": "\xc5", + "Ascr;": "\U0001d49c", + "Assign;": "\u2254", + "Atilde": "\xc3", + "Atilde;": "\xc3", + "Auml": "\xc4", + "Auml;": "\xc4", + "Backslash;": "\u2216", + "Barv;": "\u2ae7", + "Barwed;": "\u2306", + "Bcy;": "\u0411", + "Because;": "\u2235", + "Bernoullis;": "\u212c", + "Beta;": "\u0392", + "Bfr;": "\U0001d505", + "Bopf;": "\U0001d539", + "Breve;": "\u02d8", + "Bscr;": "\u212c", + "Bumpeq;": "\u224e", + "CHcy;": "\u0427", + "COPY": "\xa9", + "COPY;": "\xa9", + "Cacute;": "\u0106", + "Cap;": "\u22d2", + "CapitalDifferentialD;": "\u2145", + "Cayleys;": "\u212d", + "Ccaron;": "\u010c", + "Ccedil": "\xc7", + "Ccedil;": "\xc7", + "Ccirc;": "\u0108", + "Cconint;": "\u2230", + "Cdot;": "\u010a", + "Cedilla;": "\xb8", + "CenterDot;": "\xb7", + "Cfr;": "\u212d", + "Chi;": "\u03a7", + "CircleDot;": "\u2299", + "CircleMinus;": "\u2296", + "CirclePlus;": "\u2295", + "CircleTimes;": "\u2297", + "ClockwiseContourIntegral;": "\u2232", + "CloseCurlyDoubleQuote;": "\u201d", + "CloseCurlyQuote;": "\u2019", + "Colon;": "\u2237", + "Colone;": "\u2a74", + "Congruent;": "\u2261", + "Conint;": "\u222f", + "ContourIntegral;": "\u222e", + "Copf;": "\u2102", + "Coproduct;": "\u2210", + "CounterClockwiseContourIntegral;": "\u2233", + "Cross;": "\u2a2f", + "Cscr;": "\U0001d49e", + "Cup;": "\u22d3", + "CupCap;": "\u224d", + "DD;": "\u2145", + "DDotrahd;": "\u2911", + "DJcy;": "\u0402", + "DScy;": "\u0405", + "DZcy;": "\u040f", + "Dagger;": "\u2021", + "Darr;": "\u21a1", + "Dashv;": "\u2ae4", + "Dcaron;": "\u010e", + "Dcy;": "\u0414", + "Del;": "\u2207", + "Delta;": "\u0394", + "Dfr;": "\U0001d507", + "DiacriticalAcute;": "\xb4", + "DiacriticalDot;": "\u02d9", + "DiacriticalDoubleAcute;": "\u02dd", + "DiacriticalGrave;": "`", + "DiacriticalTilde;": "\u02dc", + "Diamond;": "\u22c4", + "DifferentialD;": "\u2146", + "Dopf;": "\U0001d53b", + "Dot;": "\xa8", + "DotDot;": "\u20dc", + "DotEqual;": "\u2250", + "DoubleContourIntegral;": "\u222f", + "DoubleDot;": "\xa8", + "DoubleDownArrow;": "\u21d3", + "DoubleLeftArrow;": "\u21d0", + "DoubleLeftRightArrow;": "\u21d4", + "DoubleLeftTee;": "\u2ae4", + "DoubleLongLeftArrow;": "\u27f8", + "DoubleLongLeftRightArrow;": "\u27fa", + "DoubleLongRightArrow;": "\u27f9", + "DoubleRightArrow;": "\u21d2", + "DoubleRightTee;": "\u22a8", + "DoubleUpArrow;": "\u21d1", + "DoubleUpDownArrow;": "\u21d5", + "DoubleVerticalBar;": "\u2225", + "DownArrow;": "\u2193", + "DownArrowBar;": "\u2913", + "DownArrowUpArrow;": "\u21f5", + "DownBreve;": "\u0311", + "DownLeftRightVector;": "\u2950", + "DownLeftTeeVector;": "\u295e", + "DownLeftVector;": "\u21bd", + "DownLeftVectorBar;": "\u2956", + "DownRightTeeVector;": "\u295f", + "DownRightVector;": "\u21c1", + "DownRightVectorBar;": "\u2957", + "DownTee;": "\u22a4", + "DownTeeArrow;": "\u21a7", + "Downarrow;": "\u21d3", + "Dscr;": "\U0001d49f", + "Dstrok;": "\u0110", + "ENG;": "\u014a", + "ETH": "\xd0", + "ETH;": "\xd0", + "Eacute": "\xc9", + "Eacute;": "\xc9", + "Ecaron;": "\u011a", + "Ecirc": "\xca", + "Ecirc;": "\xca", + "Ecy;": "\u042d", + "Edot;": "\u0116", + "Efr;": "\U0001d508", + "Egrave": "\xc8", + "Egrave;": "\xc8", + "Element;": "\u2208", + "Emacr;": "\u0112", + "EmptySmallSquare;": "\u25fb", + "EmptyVerySmallSquare;": "\u25ab", + "Eogon;": "\u0118", + "Eopf;": "\U0001d53c", + "Epsilon;": "\u0395", + "Equal;": "\u2a75", + "EqualTilde;": "\u2242", + "Equilibrium;": "\u21cc", + "Escr;": "\u2130", + "Esim;": "\u2a73", + "Eta;": "\u0397", + "Euml": "\xcb", + "Euml;": "\xcb", + "Exists;": "\u2203", + "ExponentialE;": "\u2147", + "Fcy;": "\u0424", + "Ffr;": "\U0001d509", + "FilledSmallSquare;": "\u25fc", + "FilledVerySmallSquare;": "\u25aa", + "Fopf;": "\U0001d53d", + "ForAll;": "\u2200", + "Fouriertrf;": "\u2131", + "Fscr;": "\u2131", + "GJcy;": "\u0403", + "GT": ">", + "GT;": ">", + "Gamma;": "\u0393", + "Gammad;": "\u03dc", + "Gbreve;": "\u011e", + "Gcedil;": "\u0122", + "Gcirc;": "\u011c", + "Gcy;": "\u0413", + "Gdot;": "\u0120", + "Gfr;": "\U0001d50a", + "Gg;": "\u22d9", + "Gopf;": "\U0001d53e", + "GreaterEqual;": "\u2265", + "GreaterEqualLess;": "\u22db", + "GreaterFullEqual;": "\u2267", + "GreaterGreater;": "\u2aa2", + "GreaterLess;": "\u2277", + "GreaterSlantEqual;": "\u2a7e", + "GreaterTilde;": "\u2273", + "Gscr;": "\U0001d4a2", + "Gt;": "\u226b", + "HARDcy;": "\u042a", + "Hacek;": "\u02c7", + "Hat;": "^", + "Hcirc;": "\u0124", + "Hfr;": "\u210c", + "HilbertSpace;": "\u210b", + "Hopf;": "\u210d", + "HorizontalLine;": "\u2500", + "Hscr;": "\u210b", + "Hstrok;": "\u0126", + "HumpDownHump;": "\u224e", + "HumpEqual;": "\u224f", + "IEcy;": "\u0415", + "IJlig;": "\u0132", + "IOcy;": "\u0401", + "Iacute": "\xcd", + "Iacute;": "\xcd", + "Icirc": "\xce", + "Icirc;": "\xce", + "Icy;": "\u0418", + "Idot;": "\u0130", + "Ifr;": "\u2111", + "Igrave": "\xcc", + "Igrave;": "\xcc", + "Im;": "\u2111", + "Imacr;": "\u012a", + "ImaginaryI;": "\u2148", + "Implies;": "\u21d2", + "Int;": "\u222c", + "Integral;": "\u222b", + "Intersection;": "\u22c2", + "InvisibleComma;": "\u2063", + "InvisibleTimes;": "\u2062", + "Iogon;": "\u012e", + "Iopf;": "\U0001d540", + "Iota;": "\u0399", + "Iscr;": "\u2110", + "Itilde;": "\u0128", + "Iukcy;": "\u0406", + "Iuml": "\xcf", + "Iuml;": "\xcf", + "Jcirc;": "\u0134", + "Jcy;": "\u0419", + "Jfr;": "\U0001d50d", + "Jopf;": "\U0001d541", + "Jscr;": "\U0001d4a5", + "Jsercy;": "\u0408", + "Jukcy;": "\u0404", + "KHcy;": "\u0425", + "KJcy;": "\u040c", + "Kappa;": "\u039a", + "Kcedil;": "\u0136", + "Kcy;": "\u041a", + "Kfr;": "\U0001d50e", + "Kopf;": "\U0001d542", + "Kscr;": "\U0001d4a6", + "LJcy;": "\u0409", + "LT": "<", + "LT;": "<", + "Lacute;": "\u0139", + "Lambda;": "\u039b", + "Lang;": "\u27ea", + "Laplacetrf;": "\u2112", + "Larr;": "\u219e", + "Lcaron;": "\u013d", + "Lcedil;": "\u013b", + "Lcy;": "\u041b", + "LeftAngleBracket;": "\u27e8", + "LeftArrow;": "\u2190", + "LeftArrowBar;": "\u21e4", + "LeftArrowRightArrow;": "\u21c6", + "LeftCeiling;": "\u2308", + "LeftDoubleBracket;": "\u27e6", + "LeftDownTeeVector;": "\u2961", + "LeftDownVector;": "\u21c3", + "LeftDownVectorBar;": "\u2959", + "LeftFloor;": "\u230a", + "LeftRightArrow;": "\u2194", + "LeftRightVector;": "\u294e", + "LeftTee;": "\u22a3", + "LeftTeeArrow;": "\u21a4", + "LeftTeeVector;": "\u295a", + "LeftTriangle;": "\u22b2", + "LeftTriangleBar;": "\u29cf", + "LeftTriangleEqual;": "\u22b4", + "LeftUpDownVector;": "\u2951", + "LeftUpTeeVector;": "\u2960", + "LeftUpVector;": "\u21bf", + "LeftUpVectorBar;": "\u2958", + "LeftVector;": "\u21bc", + "LeftVectorBar;": "\u2952", + "Leftarrow;": "\u21d0", + "Leftrightarrow;": "\u21d4", + "LessEqualGreater;": "\u22da", + "LessFullEqual;": "\u2266", + "LessGreater;": "\u2276", + "LessLess;": "\u2aa1", + "LessSlantEqual;": "\u2a7d", + "LessTilde;": "\u2272", + "Lfr;": "\U0001d50f", + "Ll;": "\u22d8", + "Lleftarrow;": "\u21da", + "Lmidot;": "\u013f", + "LongLeftArrow;": "\u27f5", + "LongLeftRightArrow;": "\u27f7", + "LongRightArrow;": "\u27f6", + "Longleftarrow;": "\u27f8", + "Longleftrightarrow;": "\u27fa", + "Longrightarrow;": "\u27f9", + "Lopf;": "\U0001d543", + "LowerLeftArrow;": "\u2199", + "LowerRightArrow;": "\u2198", + "Lscr;": "\u2112", + "Lsh;": "\u21b0", + "Lstrok;": "\u0141", + "Lt;": "\u226a", + "Map;": "\u2905", + "Mcy;": "\u041c", + "MediumSpace;": "\u205f", + "Mellintrf;": "\u2133", + "Mfr;": "\U0001d510", + "MinusPlus;": "\u2213", + "Mopf;": "\U0001d544", + "Mscr;": "\u2133", + "Mu;": "\u039c", + "NJcy;": "\u040a", + "Nacute;": "\u0143", + "Ncaron;": "\u0147", + "Ncedil;": "\u0145", + "Ncy;": "\u041d", + "NegativeMediumSpace;": "\u200b", + "NegativeThickSpace;": "\u200b", + "NegativeThinSpace;": "\u200b", + "NegativeVeryThinSpace;": "\u200b", + "NestedGreaterGreater;": "\u226b", + "NestedLessLess;": "\u226a", + "NewLine;": "\n", + "Nfr;": "\U0001d511", + "NoBreak;": "\u2060", + "NonBreakingSpace;": "\xa0", + "Nopf;": "\u2115", + "Not;": "\u2aec", + "NotCongruent;": "\u2262", + "NotCupCap;": "\u226d", + "NotDoubleVerticalBar;": "\u2226", + "NotElement;": "\u2209", + "NotEqual;": "\u2260", + "NotEqualTilde;": "\u2242\u0338", + "NotExists;": "\u2204", + "NotGreater;": "\u226f", + "NotGreaterEqual;": "\u2271", + "NotGreaterFullEqual;": "\u2267\u0338", + "NotGreaterGreater;": "\u226b\u0338", + "NotGreaterLess;": "\u2279", + "NotGreaterSlantEqual;": "\u2a7e\u0338", + "NotGreaterTilde;": "\u2275", + "NotHumpDownHump;": "\u224e\u0338", + "NotHumpEqual;": "\u224f\u0338", + "NotLeftTriangle;": "\u22ea", + "NotLeftTriangleBar;": "\u29cf\u0338", + "NotLeftTriangleEqual;": "\u22ec", + "NotLess;": "\u226e", + "NotLessEqual;": "\u2270", + "NotLessGreater;": "\u2278", + "NotLessLess;": "\u226a\u0338", + "NotLessSlantEqual;": "\u2a7d\u0338", + "NotLessTilde;": "\u2274", + "NotNestedGreaterGreater;": "\u2aa2\u0338", + "NotNestedLessLess;": "\u2aa1\u0338", + "NotPrecedes;": "\u2280", + "NotPrecedesEqual;": "\u2aaf\u0338", + "NotPrecedesSlantEqual;": "\u22e0", + "NotReverseElement;": "\u220c", + "NotRightTriangle;": "\u22eb", + "NotRightTriangleBar;": "\u29d0\u0338", + "NotRightTriangleEqual;": "\u22ed", + "NotSquareSubset;": "\u228f\u0338", + "NotSquareSubsetEqual;": "\u22e2", + "NotSquareSuperset;": "\u2290\u0338", + "NotSquareSupersetEqual;": "\u22e3", + "NotSubset;": "\u2282\u20d2", + "NotSubsetEqual;": "\u2288", + "NotSucceeds;": "\u2281", + "NotSucceedsEqual;": "\u2ab0\u0338", + "NotSucceedsSlantEqual;": "\u22e1", + "NotSucceedsTilde;": "\u227f\u0338", + "NotSuperset;": "\u2283\u20d2", + "NotSupersetEqual;": "\u2289", + "NotTilde;": "\u2241", + "NotTildeEqual;": "\u2244", + "NotTildeFullEqual;": "\u2247", + "NotTildeTilde;": "\u2249", + "NotVerticalBar;": "\u2224", + "Nscr;": "\U0001d4a9", + "Ntilde": "\xd1", + "Ntilde;": "\xd1", + "Nu;": "\u039d", + "OElig;": "\u0152", + "Oacute": "\xd3", + "Oacute;": "\xd3", + "Ocirc": "\xd4", + "Ocirc;": "\xd4", + "Ocy;": "\u041e", + "Odblac;": "\u0150", + "Ofr;": "\U0001d512", + "Ograve": "\xd2", + "Ograve;": "\xd2", + "Omacr;": "\u014c", + "Omega;": "\u03a9", + "Omicron;": "\u039f", + "Oopf;": "\U0001d546", + "OpenCurlyDoubleQuote;": "\u201c", + "OpenCurlyQuote;": "\u2018", + "Or;": "\u2a54", + "Oscr;": "\U0001d4aa", + "Oslash": "\xd8", + "Oslash;": "\xd8", + "Otilde": "\xd5", + "Otilde;": "\xd5", + "Otimes;": "\u2a37", + "Ouml": "\xd6", + "Ouml;": "\xd6", + "OverBar;": "\u203e", + "OverBrace;": "\u23de", + "OverBracket;": "\u23b4", + "OverParenthesis;": "\u23dc", + "PartialD;": "\u2202", + "Pcy;": "\u041f", + "Pfr;": "\U0001d513", + "Phi;": "\u03a6", + "Pi;": "\u03a0", + "PlusMinus;": "\xb1", + "Poincareplane;": "\u210c", + "Popf;": "\u2119", + "Pr;": "\u2abb", + "Precedes;": "\u227a", + "PrecedesEqual;": "\u2aaf", + "PrecedesSlantEqual;": "\u227c", + "PrecedesTilde;": "\u227e", + "Prime;": "\u2033", + "Product;": "\u220f", + "Proportion;": "\u2237", + "Proportional;": "\u221d", + "Pscr;": "\U0001d4ab", + "Psi;": "\u03a8", + "QUOT": "\"", + "QUOT;": "\"", + "Qfr;": "\U0001d514", + "Qopf;": "\u211a", + "Qscr;": "\U0001d4ac", + "RBarr;": "\u2910", + "REG": "\xae", + "REG;": "\xae", + "Racute;": "\u0154", + "Rang;": "\u27eb", + "Rarr;": "\u21a0", + "Rarrtl;": "\u2916", + "Rcaron;": "\u0158", + "Rcedil;": "\u0156", + "Rcy;": "\u0420", + "Re;": "\u211c", + "ReverseElement;": "\u220b", + "ReverseEquilibrium;": "\u21cb", + "ReverseUpEquilibrium;": "\u296f", + "Rfr;": "\u211c", + "Rho;": "\u03a1", + "RightAngleBracket;": "\u27e9", + "RightArrow;": "\u2192", + "RightArrowBar;": "\u21e5", + "RightArrowLeftArrow;": "\u21c4", + "RightCeiling;": "\u2309", + "RightDoubleBracket;": "\u27e7", + "RightDownTeeVector;": "\u295d", + "RightDownVector;": "\u21c2", + "RightDownVectorBar;": "\u2955", + "RightFloor;": "\u230b", + "RightTee;": "\u22a2", + "RightTeeArrow;": "\u21a6", + "RightTeeVector;": "\u295b", + "RightTriangle;": "\u22b3", + "RightTriangleBar;": "\u29d0", + "RightTriangleEqual;": "\u22b5", + "RightUpDownVector;": "\u294f", + "RightUpTeeVector;": "\u295c", + "RightUpVector;": "\u21be", + "RightUpVectorBar;": "\u2954", + "RightVector;": "\u21c0", + "RightVectorBar;": "\u2953", + "Rightarrow;": "\u21d2", + "Ropf;": "\u211d", + "RoundImplies;": "\u2970", + "Rrightarrow;": "\u21db", + "Rscr;": "\u211b", + "Rsh;": "\u21b1", + "RuleDelayed;": "\u29f4", + "SHCHcy;": "\u0429", + "SHcy;": "\u0428", + "SOFTcy;": "\u042c", + "Sacute;": "\u015a", + "Sc;": "\u2abc", + "Scaron;": "\u0160", + "Scedil;": "\u015e", + "Scirc;": "\u015c", + "Scy;": "\u0421", + "Sfr;": "\U0001d516", + "ShortDownArrow;": "\u2193", + "ShortLeftArrow;": "\u2190", + "ShortRightArrow;": "\u2192", + "ShortUpArrow;": "\u2191", + "Sigma;": "\u03a3", + "SmallCircle;": "\u2218", + "Sopf;": "\U0001d54a", + "Sqrt;": "\u221a", + "Square;": "\u25a1", + "SquareIntersection;": "\u2293", + "SquareSubset;": "\u228f", + "SquareSubsetEqual;": "\u2291", + "SquareSuperset;": "\u2290", + "SquareSupersetEqual;": "\u2292", + "SquareUnion;": "\u2294", + "Sscr;": "\U0001d4ae", + "Star;": "\u22c6", + "Sub;": "\u22d0", + "Subset;": "\u22d0", + "SubsetEqual;": "\u2286", + "Succeeds;": "\u227b", + "SucceedsEqual;": "\u2ab0", + "SucceedsSlantEqual;": "\u227d", + "SucceedsTilde;": "\u227f", + "SuchThat;": "\u220b", + "Sum;": "\u2211", + "Sup;": "\u22d1", + "Superset;": "\u2283", + "SupersetEqual;": "\u2287", + "Supset;": "\u22d1", + "THORN": "\xde", + "THORN;": "\xde", + "TRADE;": "\u2122", + "TSHcy;": "\u040b", + "TScy;": "\u0426", + "Tab;": "\t", + "Tau;": "\u03a4", + "Tcaron;": "\u0164", + "Tcedil;": "\u0162", + "Tcy;": "\u0422", + "Tfr;": "\U0001d517", + "Therefore;": "\u2234", + "Theta;": "\u0398", + "ThickSpace;": "\u205f\u200a", + "ThinSpace;": "\u2009", + "Tilde;": "\u223c", + "TildeEqual;": "\u2243", + "TildeFullEqual;": "\u2245", + "TildeTilde;": "\u2248", + "Topf;": "\U0001d54b", + "TripleDot;": "\u20db", + "Tscr;": "\U0001d4af", + "Tstrok;": "\u0166", + "Uacute": "\xda", + "Uacute;": "\xda", + "Uarr;": "\u219f", + "Uarrocir;": "\u2949", + "Ubrcy;": "\u040e", + "Ubreve;": "\u016c", + "Ucirc": "\xdb", + "Ucirc;": "\xdb", + "Ucy;": "\u0423", + "Udblac;": "\u0170", + "Ufr;": "\U0001d518", + "Ugrave": "\xd9", + "Ugrave;": "\xd9", + "Umacr;": "\u016a", + "UnderBar;": "_", + "UnderBrace;": "\u23df", + "UnderBracket;": "\u23b5", + "UnderParenthesis;": "\u23dd", + "Union;": "\u22c3", + "UnionPlus;": "\u228e", + "Uogon;": "\u0172", + "Uopf;": "\U0001d54c", + "UpArrow;": "\u2191", + "UpArrowBar;": "\u2912", + "UpArrowDownArrow;": "\u21c5", + "UpDownArrow;": "\u2195", + "UpEquilibrium;": "\u296e", + "UpTee;": "\u22a5", + "UpTeeArrow;": "\u21a5", + "Uparrow;": "\u21d1", + "Updownarrow;": "\u21d5", + "UpperLeftArrow;": "\u2196", + "UpperRightArrow;": "\u2197", + "Upsi;": "\u03d2", + "Upsilon;": "\u03a5", + "Uring;": "\u016e", + "Uscr;": "\U0001d4b0", + "Utilde;": "\u0168", + "Uuml": "\xdc", + "Uuml;": "\xdc", + "VDash;": "\u22ab", + "Vbar;": "\u2aeb", + "Vcy;": "\u0412", + "Vdash;": "\u22a9", + "Vdashl;": "\u2ae6", + "Vee;": "\u22c1", + "Verbar;": "\u2016", + "Vert;": "\u2016", + "VerticalBar;": "\u2223", + "VerticalLine;": "|", + "VerticalSeparator;": "\u2758", + "VerticalTilde;": "\u2240", + "VeryThinSpace;": "\u200a", + "Vfr;": "\U0001d519", + "Vopf;": "\U0001d54d", + "Vscr;": "\U0001d4b1", + "Vvdash;": "\u22aa", + "Wcirc;": "\u0174", + "Wedge;": "\u22c0", + "Wfr;": "\U0001d51a", + "Wopf;": "\U0001d54e", + "Wscr;": "\U0001d4b2", + "Xfr;": "\U0001d51b", + "Xi;": "\u039e", + "Xopf;": "\U0001d54f", + "Xscr;": "\U0001d4b3", + "YAcy;": "\u042f", + "YIcy;": "\u0407", + "YUcy;": "\u042e", + "Yacute": "\xdd", + "Yacute;": "\xdd", + "Ycirc;": "\u0176", + "Ycy;": "\u042b", + "Yfr;": "\U0001d51c", + "Yopf;": "\U0001d550", + "Yscr;": "\U0001d4b4", + "Yuml;": "\u0178", + "ZHcy;": "\u0416", + "Zacute;": "\u0179", + "Zcaron;": "\u017d", + "Zcy;": "\u0417", + "Zdot;": "\u017b", + "ZeroWidthSpace;": "\u200b", + "Zeta;": "\u0396", + "Zfr;": "\u2128", + "Zopf;": "\u2124", + "Zscr;": "\U0001d4b5", + "aacute": "\xe1", + "aacute;": "\xe1", + "abreve;": "\u0103", + "ac;": "\u223e", + "acE;": "\u223e\u0333", + "acd;": "\u223f", + "acirc": "\xe2", + "acirc;": "\xe2", + "acute": "\xb4", + "acute;": "\xb4", + "acy;": "\u0430", + "aelig": "\xe6", + "aelig;": "\xe6", + "af;": "\u2061", + "afr;": "\U0001d51e", + "agrave": "\xe0", + "agrave;": "\xe0", + "alefsym;": "\u2135", + "aleph;": "\u2135", + "alpha;": "\u03b1", + "amacr;": "\u0101", + "amalg;": "\u2a3f", + "amp": "&", + "amp;": "&", + "and;": "\u2227", + "andand;": "\u2a55", + "andd;": "\u2a5c", + "andslope;": "\u2a58", + "andv;": "\u2a5a", + "ang;": "\u2220", + "ange;": "\u29a4", + "angle;": "\u2220", + "angmsd;": "\u2221", + "angmsdaa;": "\u29a8", + "angmsdab;": "\u29a9", + "angmsdac;": "\u29aa", + "angmsdad;": "\u29ab", + "angmsdae;": "\u29ac", + "angmsdaf;": "\u29ad", + "angmsdag;": "\u29ae", + "angmsdah;": "\u29af", + "angrt;": "\u221f", + "angrtvb;": "\u22be", + "angrtvbd;": "\u299d", + "angsph;": "\u2222", + "angst;": "\xc5", + "angzarr;": "\u237c", + "aogon;": "\u0105", + "aopf;": "\U0001d552", + "ap;": "\u2248", + "apE;": "\u2a70", + "apacir;": "\u2a6f", + "ape;": "\u224a", + "apid;": "\u224b", + "apos;": "'", + "approx;": "\u2248", + "approxeq;": "\u224a", + "aring": "\xe5", + "aring;": "\xe5", + "ascr;": "\U0001d4b6", + "ast;": "*", + "asymp;": "\u2248", + "asympeq;": "\u224d", + "atilde": "\xe3", + "atilde;": "\xe3", + "auml": "\xe4", + "auml;": "\xe4", + "awconint;": "\u2233", + "awint;": "\u2a11", + "bNot;": "\u2aed", + "backcong;": "\u224c", + "backepsilon;": "\u03f6", + "backprime;": "\u2035", + "backsim;": "\u223d", + "backsimeq;": "\u22cd", + "barvee;": "\u22bd", + "barwed;": "\u2305", + "barwedge;": "\u2305", + "bbrk;": "\u23b5", + "bbrktbrk;": "\u23b6", + "bcong;": "\u224c", + "bcy;": "\u0431", + "bdquo;": "\u201e", + "becaus;": "\u2235", + "because;": "\u2235", + "bemptyv;": "\u29b0", + "bepsi;": "\u03f6", + "bernou;": "\u212c", + "beta;": "\u03b2", + "beth;": "\u2136", + "between;": "\u226c", + "bfr;": "\U0001d51f", + "bigcap;": "\u22c2", + "bigcirc;": "\u25ef", + "bigcup;": "\u22c3", + "bigodot;": "\u2a00", + "bigoplus;": "\u2a01", + "bigotimes;": "\u2a02", + "bigsqcup;": "\u2a06", + "bigstar;": "\u2605", + "bigtriangledown;": "\u25bd", + "bigtriangleup;": "\u25b3", + "biguplus;": "\u2a04", + "bigvee;": "\u22c1", + "bigwedge;": "\u22c0", + "bkarow;": "\u290d", + "blacklozenge;": "\u29eb", + "blacksquare;": "\u25aa", + "blacktriangle;": "\u25b4", + "blacktriangledown;": "\u25be", + "blacktriangleleft;": "\u25c2", + "blacktriangleright;": "\u25b8", + "blank;": "\u2423", + "blk12;": "\u2592", + "blk14;": "\u2591", + "blk34;": "\u2593", + "block;": "\u2588", + "bne;": "=\u20e5", + "bnequiv;": "\u2261\u20e5", + "bnot;": "\u2310", + "bopf;": "\U0001d553", + "bot;": "\u22a5", + "bottom;": "\u22a5", + "bowtie;": "\u22c8", + "boxDL;": "\u2557", + "boxDR;": "\u2554", + "boxDl;": "\u2556", + "boxDr;": "\u2553", + "boxH;": "\u2550", + "boxHD;": "\u2566", + "boxHU;": "\u2569", + "boxHd;": "\u2564", + "boxHu;": "\u2567", + "boxUL;": "\u255d", + "boxUR;": "\u255a", + "boxUl;": "\u255c", + "boxUr;": "\u2559", + "boxV;": "\u2551", + "boxVH;": "\u256c", + "boxVL;": "\u2563", + "boxVR;": "\u2560", + "boxVh;": "\u256b", + "boxVl;": "\u2562", + "boxVr;": "\u255f", + "boxbox;": "\u29c9", + "boxdL;": "\u2555", + "boxdR;": "\u2552", + "boxdl;": "\u2510", + "boxdr;": "\u250c", + "boxh;": "\u2500", + "boxhD;": "\u2565", + "boxhU;": "\u2568", + "boxhd;": "\u252c", + "boxhu;": "\u2534", + "boxminus;": "\u229f", + "boxplus;": "\u229e", + "boxtimes;": "\u22a0", + "boxuL;": "\u255b", + "boxuR;": "\u2558", + "boxul;": "\u2518", + "boxur;": "\u2514", + "boxv;": "\u2502", + "boxvH;": "\u256a", + "boxvL;": "\u2561", + "boxvR;": "\u255e", + "boxvh;": "\u253c", + "boxvl;": "\u2524", + "boxvr;": "\u251c", + "bprime;": "\u2035", + "breve;": "\u02d8", + "brvbar": "\xa6", + "brvbar;": "\xa6", + "bscr;": "\U0001d4b7", + "bsemi;": "\u204f", + "bsim;": "\u223d", + "bsime;": "\u22cd", + "bsol;": "\\", + "bsolb;": "\u29c5", + "bsolhsub;": "\u27c8", + "bull;": "\u2022", + "bullet;": "\u2022", + "bump;": "\u224e", + "bumpE;": "\u2aae", + "bumpe;": "\u224f", + "bumpeq;": "\u224f", + "cacute;": "\u0107", + "cap;": "\u2229", + "capand;": "\u2a44", + "capbrcup;": "\u2a49", + "capcap;": "\u2a4b", + "capcup;": "\u2a47", + "capdot;": "\u2a40", + "caps;": "\u2229\ufe00", + "caret;": "\u2041", + "caron;": "\u02c7", + "ccaps;": "\u2a4d", + "ccaron;": "\u010d", + "ccedil": "\xe7", + "ccedil;": "\xe7", + "ccirc;": "\u0109", + "ccups;": "\u2a4c", + "ccupssm;": "\u2a50", + "cdot;": "\u010b", + "cedil": "\xb8", + "cedil;": "\xb8", + "cemptyv;": "\u29b2", + "cent": "\xa2", + "cent;": "\xa2", + "centerdot;": "\xb7", + "cfr;": "\U0001d520", + "chcy;": "\u0447", + "check;": "\u2713", + "checkmark;": "\u2713", + "chi;": "\u03c7", + "cir;": "\u25cb", + "cirE;": "\u29c3", + "circ;": "\u02c6", + "circeq;": "\u2257", + "circlearrowleft;": "\u21ba", + "circlearrowright;": "\u21bb", + "circledR;": "\xae", + "circledS;": "\u24c8", + "circledast;": "\u229b", + "circledcirc;": "\u229a", + "circleddash;": "\u229d", + "cire;": "\u2257", + "cirfnint;": "\u2a10", + "cirmid;": "\u2aef", + "cirscir;": "\u29c2", + "clubs;": "\u2663", + "clubsuit;": "\u2663", + "colon;": ":", + "colone;": "\u2254", + "coloneq;": "\u2254", + "comma;": ",", + "commat;": "@", + "comp;": "\u2201", + "compfn;": "\u2218", + "complement;": "\u2201", + "complexes;": "\u2102", + "cong;": "\u2245", + "congdot;": "\u2a6d", + "conint;": "\u222e", + "copf;": "\U0001d554", + "coprod;": "\u2210", + "copy": "\xa9", + "copy;": "\xa9", + "copysr;": "\u2117", + "crarr;": "\u21b5", + "cross;": "\u2717", + "cscr;": "\U0001d4b8", + "csub;": "\u2acf", + "csube;": "\u2ad1", + "csup;": "\u2ad0", + "csupe;": "\u2ad2", + "ctdot;": "\u22ef", + "cudarrl;": "\u2938", + "cudarrr;": "\u2935", + "cuepr;": "\u22de", + "cuesc;": "\u22df", + "cularr;": "\u21b6", + "cularrp;": "\u293d", + "cup;": "\u222a", + "cupbrcap;": "\u2a48", + "cupcap;": "\u2a46", + "cupcup;": "\u2a4a", + "cupdot;": "\u228d", + "cupor;": "\u2a45", + "cups;": "\u222a\ufe00", + "curarr;": "\u21b7", + "curarrm;": "\u293c", + "curlyeqprec;": "\u22de", + "curlyeqsucc;": "\u22df", + "curlyvee;": "\u22ce", + "curlywedge;": "\u22cf", + "curren": "\xa4", + "curren;": "\xa4", + "curvearrowleft;": "\u21b6", + "curvearrowright;": "\u21b7", + "cuvee;": "\u22ce", + "cuwed;": "\u22cf", + "cwconint;": "\u2232", + "cwint;": "\u2231", + "cylcty;": "\u232d", + "dArr;": "\u21d3", + "dHar;": "\u2965", + "dagger;": "\u2020", + "daleth;": "\u2138", + "darr;": "\u2193", + "dash;": "\u2010", + "dashv;": "\u22a3", + "dbkarow;": "\u290f", + "dblac;": "\u02dd", + "dcaron;": "\u010f", + "dcy;": "\u0434", + "dd;": "\u2146", + "ddagger;": "\u2021", + "ddarr;": "\u21ca", + "ddotseq;": "\u2a77", + "deg": "\xb0", + "deg;": "\xb0", + "delta;": "\u03b4", + "demptyv;": "\u29b1", + "dfisht;": "\u297f", + "dfr;": "\U0001d521", + "dharl;": "\u21c3", + "dharr;": "\u21c2", + "diam;": "\u22c4", + "diamond;": "\u22c4", + "diamondsuit;": "\u2666", + "diams;": "\u2666", + "die;": "\xa8", + "digamma;": "\u03dd", + "disin;": "\u22f2", + "div;": "\xf7", + "divide": "\xf7", + "divide;": "\xf7", + "divideontimes;": "\u22c7", + "divonx;": "\u22c7", + "djcy;": "\u0452", + "dlcorn;": "\u231e", + "dlcrop;": "\u230d", + "dollar;": "$", + "dopf;": "\U0001d555", + "dot;": "\u02d9", + "doteq;": "\u2250", + "doteqdot;": "\u2251", + "dotminus;": "\u2238", + "dotplus;": "\u2214", + "dotsquare;": "\u22a1", + "doublebarwedge;": "\u2306", + "downarrow;": "\u2193", + "downdownarrows;": "\u21ca", + "downharpoonleft;": "\u21c3", + "downharpoonright;": "\u21c2", + "drbkarow;": "\u2910", + "drcorn;": "\u231f", + "drcrop;": "\u230c", + "dscr;": "\U0001d4b9", + "dscy;": "\u0455", + "dsol;": "\u29f6", + "dstrok;": "\u0111", + "dtdot;": "\u22f1", + "dtri;": "\u25bf", + "dtrif;": "\u25be", + "duarr;": "\u21f5", + "duhar;": "\u296f", + "dwangle;": "\u29a6", + "dzcy;": "\u045f", + "dzigrarr;": "\u27ff", + "eDDot;": "\u2a77", + "eDot;": "\u2251", + "eacute": "\xe9", + "eacute;": "\xe9", + "easter;": "\u2a6e", + "ecaron;": "\u011b", + "ecir;": "\u2256", + "ecirc": "\xea", + "ecirc;": "\xea", + "ecolon;": "\u2255", + "ecy;": "\u044d", + "edot;": "\u0117", + "ee;": "\u2147", + "efDot;": "\u2252", + "efr;": "\U0001d522", + "eg;": "\u2a9a", + "egrave": "\xe8", + "egrave;": "\xe8", + "egs;": "\u2a96", + "egsdot;": "\u2a98", + "el;": "\u2a99", + "elinters;": "\u23e7", + "ell;": "\u2113", + "els;": "\u2a95", + "elsdot;": "\u2a97", + "emacr;": "\u0113", + "empty;": "\u2205", + "emptyset;": "\u2205", + "emptyv;": "\u2205", + "emsp13;": "\u2004", + "emsp14;": "\u2005", + "emsp;": "\u2003", + "eng;": "\u014b", + "ensp;": "\u2002", + "eogon;": "\u0119", + "eopf;": "\U0001d556", + "epar;": "\u22d5", + "eparsl;": "\u29e3", + "eplus;": "\u2a71", + "epsi;": "\u03b5", + "epsilon;": "\u03b5", + "epsiv;": "\u03f5", + "eqcirc;": "\u2256", + "eqcolon;": "\u2255", + "eqsim;": "\u2242", + "eqslantgtr;": "\u2a96", + "eqslantless;": "\u2a95", + "equals;": "=", + "equest;": "\u225f", + "equiv;": "\u2261", + "equivDD;": "\u2a78", + "eqvparsl;": "\u29e5", + "erDot;": "\u2253", + "erarr;": "\u2971", + "escr;": "\u212f", + "esdot;": "\u2250", + "esim;": "\u2242", + "eta;": "\u03b7", + "eth": "\xf0", + "eth;": "\xf0", + "euml": "\xeb", + "euml;": "\xeb", + "euro;": "\u20ac", + "excl;": "!", + "exist;": "\u2203", + "expectation;": "\u2130", + "exponentiale;": "\u2147", + "fallingdotseq;": "\u2252", + "fcy;": "\u0444", + "female;": "\u2640", + "ffilig;": "\ufb03", + "fflig;": "\ufb00", + "ffllig;": "\ufb04", + "ffr;": "\U0001d523", + "filig;": "\ufb01", + "fjlig;": "fj", + "flat;": "\u266d", + "fllig;": "\ufb02", + "fltns;": "\u25b1", + "fnof;": "\u0192", + "fopf;": "\U0001d557", + "forall;": "\u2200", + "fork;": "\u22d4", + "forkv;": "\u2ad9", + "fpartint;": "\u2a0d", + "frac12": "\xbd", + "frac12;": "\xbd", + "frac13;": "\u2153", + "frac14": "\xbc", + "frac14;": "\xbc", + "frac15;": "\u2155", + "frac16;": "\u2159", + "frac18;": "\u215b", + "frac23;": "\u2154", + "frac25;": "\u2156", + "frac34": "\xbe", + "frac34;": "\xbe", + "frac35;": "\u2157", + "frac38;": "\u215c", + "frac45;": "\u2158", + "frac56;": "\u215a", + "frac58;": "\u215d", + "frac78;": "\u215e", + "frasl;": "\u2044", + "frown;": "\u2322", + "fscr;": "\U0001d4bb", + "gE;": "\u2267", + "gEl;": "\u2a8c", + "gacute;": "\u01f5", + "gamma;": "\u03b3", + "gammad;": "\u03dd", + "gap;": "\u2a86", + "gbreve;": "\u011f", + "gcirc;": "\u011d", + "gcy;": "\u0433", + "gdot;": "\u0121", + "ge;": "\u2265", + "gel;": "\u22db", + "geq;": "\u2265", + "geqq;": "\u2267", + "geqslant;": "\u2a7e", + "ges;": "\u2a7e", + "gescc;": "\u2aa9", + "gesdot;": "\u2a80", + "gesdoto;": "\u2a82", + "gesdotol;": "\u2a84", + "gesl;": "\u22db\ufe00", + "gesles;": "\u2a94", + "gfr;": "\U0001d524", + "gg;": "\u226b", + "ggg;": "\u22d9", + "gimel;": "\u2137", + "gjcy;": "\u0453", + "gl;": "\u2277", + "glE;": "\u2a92", + "gla;": "\u2aa5", + "glj;": "\u2aa4", + "gnE;": "\u2269", + "gnap;": "\u2a8a", + "gnapprox;": "\u2a8a", + "gne;": "\u2a88", + "gneq;": "\u2a88", + "gneqq;": "\u2269", + "gnsim;": "\u22e7", + "gopf;": "\U0001d558", + "grave;": "`", + "gscr;": "\u210a", + "gsim;": "\u2273", + "gsime;": "\u2a8e", + "gsiml;": "\u2a90", + "gt": ">", + "gt;": ">", + "gtcc;": "\u2aa7", + "gtcir;": "\u2a7a", + "gtdot;": "\u22d7", + "gtlPar;": "\u2995", + "gtquest;": "\u2a7c", + "gtrapprox;": "\u2a86", + "gtrarr;": "\u2978", + "gtrdot;": "\u22d7", + "gtreqless;": "\u22db", + "gtreqqless;": "\u2a8c", + "gtrless;": "\u2277", + "gtrsim;": "\u2273", + "gvertneqq;": "\u2269\ufe00", + "gvnE;": "\u2269\ufe00", + "hArr;": "\u21d4", + "hairsp;": "\u200a", + "half;": "\xbd", + "hamilt;": "\u210b", + "hardcy;": "\u044a", + "harr;": "\u2194", + "harrcir;": "\u2948", + "harrw;": "\u21ad", + "hbar;": "\u210f", + "hcirc;": "\u0125", + "hearts;": "\u2665", + "heartsuit;": "\u2665", + "hellip;": "\u2026", + "hercon;": "\u22b9", + "hfr;": "\U0001d525", + "hksearow;": "\u2925", + "hkswarow;": "\u2926", + "hoarr;": "\u21ff", + "homtht;": "\u223b", + "hookleftarrow;": "\u21a9", + "hookrightarrow;": "\u21aa", + "hopf;": "\U0001d559", + "horbar;": "\u2015", + "hscr;": "\U0001d4bd", + "hslash;": "\u210f", + "hstrok;": "\u0127", + "hybull;": "\u2043", + "hyphen;": "\u2010", + "iacute": "\xed", + "iacute;": "\xed", + "ic;": "\u2063", + "icirc": "\xee", + "icirc;": "\xee", + "icy;": "\u0438", + "iecy;": "\u0435", + "iexcl": "\xa1", + "iexcl;": "\xa1", + "iff;": "\u21d4", + "ifr;": "\U0001d526", + "igrave": "\xec", + "igrave;": "\xec", + "ii;": "\u2148", + "iiiint;": "\u2a0c", + "iiint;": "\u222d", + "iinfin;": "\u29dc", + "iiota;": "\u2129", + "ijlig;": "\u0133", + "imacr;": "\u012b", + "image;": "\u2111", + "imagline;": "\u2110", + "imagpart;": "\u2111", + "imath;": "\u0131", + "imof;": "\u22b7", + "imped;": "\u01b5", + "in;": "\u2208", + "incare;": "\u2105", + "infin;": "\u221e", + "infintie;": "\u29dd", + "inodot;": "\u0131", + "int;": "\u222b", + "intcal;": "\u22ba", + "integers;": "\u2124", + "intercal;": "\u22ba", + "intlarhk;": "\u2a17", + "intprod;": "\u2a3c", + "iocy;": "\u0451", + "iogon;": "\u012f", + "iopf;": "\U0001d55a", + "iota;": "\u03b9", + "iprod;": "\u2a3c", + "iquest": "\xbf", + "iquest;": "\xbf", + "iscr;": "\U0001d4be", + "isin;": "\u2208", + "isinE;": "\u22f9", + "isindot;": "\u22f5", + "isins;": "\u22f4", + "isinsv;": "\u22f3", + "isinv;": "\u2208", + "it;": "\u2062", + "itilde;": "\u0129", + "iukcy;": "\u0456", + "iuml": "\xef", + "iuml;": "\xef", + "jcirc;": "\u0135", + "jcy;": "\u0439", + "jfr;": "\U0001d527", + "jmath;": "\u0237", + "jopf;": "\U0001d55b", + "jscr;": "\U0001d4bf", + "jsercy;": "\u0458", + "jukcy;": "\u0454", + "kappa;": "\u03ba", + "kappav;": "\u03f0", + "kcedil;": "\u0137", + "kcy;": "\u043a", + "kfr;": "\U0001d528", + "kgreen;": "\u0138", + "khcy;": "\u0445", + "kjcy;": "\u045c", + "kopf;": "\U0001d55c", + "kscr;": "\U0001d4c0", + "lAarr;": "\u21da", + "lArr;": "\u21d0", + "lAtail;": "\u291b", + "lBarr;": "\u290e", + "lE;": "\u2266", + "lEg;": "\u2a8b", + "lHar;": "\u2962", + "lacute;": "\u013a", + "laemptyv;": "\u29b4", + "lagran;": "\u2112", + "lambda;": "\u03bb", + "lang;": "\u27e8", + "langd;": "\u2991", + "langle;": "\u27e8", + "lap;": "\u2a85", + "laquo": "\xab", + "laquo;": "\xab", + "larr;": "\u2190", + "larrb;": "\u21e4", + "larrbfs;": "\u291f", + "larrfs;": "\u291d", + "larrhk;": "\u21a9", + "larrlp;": "\u21ab", + "larrpl;": "\u2939", + "larrsim;": "\u2973", + "larrtl;": "\u21a2", + "lat;": "\u2aab", + "latail;": "\u2919", + "late;": "\u2aad", + "lates;": "\u2aad\ufe00", + "lbarr;": "\u290c", + "lbbrk;": "\u2772", + "lbrace;": "{", + "lbrack;": "[", + "lbrke;": "\u298b", + "lbrksld;": "\u298f", + "lbrkslu;": "\u298d", + "lcaron;": "\u013e", + "lcedil;": "\u013c", + "lceil;": "\u2308", + "lcub;": "{", + "lcy;": "\u043b", + "ldca;": "\u2936", + "ldquo;": "\u201c", + "ldquor;": "\u201e", + "ldrdhar;": "\u2967", + "ldrushar;": "\u294b", + "ldsh;": "\u21b2", + "le;": "\u2264", + "leftarrow;": "\u2190", + "leftarrowtail;": "\u21a2", + "leftharpoondown;": "\u21bd", + "leftharpoonup;": "\u21bc", + "leftleftarrows;": "\u21c7", + "leftrightarrow;": "\u2194", + "leftrightarrows;": "\u21c6", + "leftrightharpoons;": "\u21cb", + "leftrightsquigarrow;": "\u21ad", + "leftthreetimes;": "\u22cb", + "leg;": "\u22da", + "leq;": "\u2264", + "leqq;": "\u2266", + "leqslant;": "\u2a7d", + "les;": "\u2a7d", + "lescc;": "\u2aa8", + "lesdot;": "\u2a7f", + "lesdoto;": "\u2a81", + "lesdotor;": "\u2a83", + "lesg;": "\u22da\ufe00", + "lesges;": "\u2a93", + "lessapprox;": "\u2a85", + "lessdot;": "\u22d6", + "lesseqgtr;": "\u22da", + "lesseqqgtr;": "\u2a8b", + "lessgtr;": "\u2276", + "lesssim;": "\u2272", + "lfisht;": "\u297c", + "lfloor;": "\u230a", + "lfr;": "\U0001d529", + "lg;": "\u2276", + "lgE;": "\u2a91", + "lhard;": "\u21bd", + "lharu;": "\u21bc", + "lharul;": "\u296a", + "lhblk;": "\u2584", + "ljcy;": "\u0459", + "ll;": "\u226a", + "llarr;": "\u21c7", + "llcorner;": "\u231e", + "llhard;": "\u296b", + "lltri;": "\u25fa", + "lmidot;": "\u0140", + "lmoust;": "\u23b0", + "lmoustache;": "\u23b0", + "lnE;": "\u2268", + "lnap;": "\u2a89", + "lnapprox;": "\u2a89", + "lne;": "\u2a87", + "lneq;": "\u2a87", + "lneqq;": "\u2268", + "lnsim;": "\u22e6", + "loang;": "\u27ec", + "loarr;": "\u21fd", + "lobrk;": "\u27e6", + "longleftarrow;": "\u27f5", + "longleftrightarrow;": "\u27f7", + "longmapsto;": "\u27fc", + "longrightarrow;": "\u27f6", + "looparrowleft;": "\u21ab", + "looparrowright;": "\u21ac", + "lopar;": "\u2985", + "lopf;": "\U0001d55d", + "loplus;": "\u2a2d", + "lotimes;": "\u2a34", + "lowast;": "\u2217", + "lowbar;": "_", + "loz;": "\u25ca", + "lozenge;": "\u25ca", + "lozf;": "\u29eb", + "lpar;": "(", + "lparlt;": "\u2993", + "lrarr;": "\u21c6", + "lrcorner;": "\u231f", + "lrhar;": "\u21cb", + "lrhard;": "\u296d", + "lrm;": "\u200e", + "lrtri;": "\u22bf", + "lsaquo;": "\u2039", + "lscr;": "\U0001d4c1", + "lsh;": "\u21b0", + "lsim;": "\u2272", + "lsime;": "\u2a8d", + "lsimg;": "\u2a8f", + "lsqb;": "[", + "lsquo;": "\u2018", + "lsquor;": "\u201a", + "lstrok;": "\u0142", + "lt": "<", + "lt;": "<", + "ltcc;": "\u2aa6", + "ltcir;": "\u2a79", + "ltdot;": "\u22d6", + "lthree;": "\u22cb", + "ltimes;": "\u22c9", + "ltlarr;": "\u2976", + "ltquest;": "\u2a7b", + "ltrPar;": "\u2996", + "ltri;": "\u25c3", + "ltrie;": "\u22b4", + "ltrif;": "\u25c2", + "lurdshar;": "\u294a", + "luruhar;": "\u2966", + "lvertneqq;": "\u2268\ufe00", + "lvnE;": "\u2268\ufe00", + "mDDot;": "\u223a", + "macr": "\xaf", + "macr;": "\xaf", + "male;": "\u2642", + "malt;": "\u2720", + "maltese;": "\u2720", + "map;": "\u21a6", + "mapsto;": "\u21a6", + "mapstodown;": "\u21a7", + "mapstoleft;": "\u21a4", + "mapstoup;": "\u21a5", + "marker;": "\u25ae", + "mcomma;": "\u2a29", + "mcy;": "\u043c", + "mdash;": "\u2014", + "measuredangle;": "\u2221", + "mfr;": "\U0001d52a", + "mho;": "\u2127", + "micro": "\xb5", + "micro;": "\xb5", + "mid;": "\u2223", + "midast;": "*", + "midcir;": "\u2af0", + "middot": "\xb7", + "middot;": "\xb7", + "minus;": "\u2212", + "minusb;": "\u229f", + "minusd;": "\u2238", + "minusdu;": "\u2a2a", + "mlcp;": "\u2adb", + "mldr;": "\u2026", + "mnplus;": "\u2213", + "models;": "\u22a7", + "mopf;": "\U0001d55e", + "mp;": "\u2213", + "mscr;": "\U0001d4c2", + "mstpos;": "\u223e", + "mu;": "\u03bc", + "multimap;": "\u22b8", + "mumap;": "\u22b8", + "nGg;": "\u22d9\u0338", + "nGt;": "\u226b\u20d2", + "nGtv;": "\u226b\u0338", + "nLeftarrow;": "\u21cd", + "nLeftrightarrow;": "\u21ce", + "nLl;": "\u22d8\u0338", + "nLt;": "\u226a\u20d2", + "nLtv;": "\u226a\u0338", + "nRightarrow;": "\u21cf", + "nVDash;": "\u22af", + "nVdash;": "\u22ae", + "nabla;": "\u2207", + "nacute;": "\u0144", + "nang;": "\u2220\u20d2", + "nap;": "\u2249", + "napE;": "\u2a70\u0338", + "napid;": "\u224b\u0338", + "napos;": "\u0149", + "napprox;": "\u2249", + "natur;": "\u266e", + "natural;": "\u266e", + "naturals;": "\u2115", + "nbsp": "\xa0", + "nbsp;": "\xa0", + "nbump;": "\u224e\u0338", + "nbumpe;": "\u224f\u0338", + "ncap;": "\u2a43", + "ncaron;": "\u0148", + "ncedil;": "\u0146", + "ncong;": "\u2247", + "ncongdot;": "\u2a6d\u0338", + "ncup;": "\u2a42", + "ncy;": "\u043d", + "ndash;": "\u2013", + "ne;": "\u2260", + "neArr;": "\u21d7", + "nearhk;": "\u2924", + "nearr;": "\u2197", + "nearrow;": "\u2197", + "nedot;": "\u2250\u0338", + "nequiv;": "\u2262", + "nesear;": "\u2928", + "nesim;": "\u2242\u0338", + "nexist;": "\u2204", + "nexists;": "\u2204", + "nfr;": "\U0001d52b", + "ngE;": "\u2267\u0338", + "nge;": "\u2271", + "ngeq;": "\u2271", + "ngeqq;": "\u2267\u0338", + "ngeqslant;": "\u2a7e\u0338", + "nges;": "\u2a7e\u0338", + "ngsim;": "\u2275", + "ngt;": "\u226f", + "ngtr;": "\u226f", + "nhArr;": "\u21ce", + "nharr;": "\u21ae", + "nhpar;": "\u2af2", + "ni;": "\u220b", + "nis;": "\u22fc", + "nisd;": "\u22fa", + "niv;": "\u220b", + "njcy;": "\u045a", + "nlArr;": "\u21cd", + "nlE;": "\u2266\u0338", + "nlarr;": "\u219a", + "nldr;": "\u2025", + "nle;": "\u2270", + "nleftarrow;": "\u219a", + "nleftrightarrow;": "\u21ae", + "nleq;": "\u2270", + "nleqq;": "\u2266\u0338", + "nleqslant;": "\u2a7d\u0338", + "nles;": "\u2a7d\u0338", + "nless;": "\u226e", + "nlsim;": "\u2274", + "nlt;": "\u226e", + "nltri;": "\u22ea", + "nltrie;": "\u22ec", + "nmid;": "\u2224", + "nopf;": "\U0001d55f", + "not": "\xac", + "not;": "\xac", + "notin;": "\u2209", + "notinE;": "\u22f9\u0338", + "notindot;": "\u22f5\u0338", + "notinva;": "\u2209", + "notinvb;": "\u22f7", + "notinvc;": "\u22f6", + "notni;": "\u220c", + "notniva;": "\u220c", + "notnivb;": "\u22fe", + "notnivc;": "\u22fd", + "npar;": "\u2226", + "nparallel;": "\u2226", + "nparsl;": "\u2afd\u20e5", + "npart;": "\u2202\u0338", + "npolint;": "\u2a14", + "npr;": "\u2280", + "nprcue;": "\u22e0", + "npre;": "\u2aaf\u0338", + "nprec;": "\u2280", + "npreceq;": "\u2aaf\u0338", + "nrArr;": "\u21cf", + "nrarr;": "\u219b", + "nrarrc;": "\u2933\u0338", + "nrarrw;": "\u219d\u0338", + "nrightarrow;": "\u219b", + "nrtri;": "\u22eb", + "nrtrie;": "\u22ed", + "nsc;": "\u2281", + "nsccue;": "\u22e1", + "nsce;": "\u2ab0\u0338", + "nscr;": "\U0001d4c3", + "nshortmid;": "\u2224", + "nshortparallel;": "\u2226", + "nsim;": "\u2241", + "nsime;": "\u2244", + "nsimeq;": "\u2244", + "nsmid;": "\u2224", + "nspar;": "\u2226", + "nsqsube;": "\u22e2", + "nsqsupe;": "\u22e3", + "nsub;": "\u2284", + "nsubE;": "\u2ac5\u0338", + "nsube;": "\u2288", + "nsubset;": "\u2282\u20d2", + "nsubseteq;": "\u2288", + "nsubseteqq;": "\u2ac5\u0338", + "nsucc;": "\u2281", + "nsucceq;": "\u2ab0\u0338", + "nsup;": "\u2285", + "nsupE;": "\u2ac6\u0338", + "nsupe;": "\u2289", + "nsupset;": "\u2283\u20d2", + "nsupseteq;": "\u2289", + "nsupseteqq;": "\u2ac6\u0338", + "ntgl;": "\u2279", + "ntilde": "\xf1", + "ntilde;": "\xf1", + "ntlg;": "\u2278", + "ntriangleleft;": "\u22ea", + "ntrianglelefteq;": "\u22ec", + "ntriangleright;": "\u22eb", + "ntrianglerighteq;": "\u22ed", + "nu;": "\u03bd", + "num;": "#", + "numero;": "\u2116", + "numsp;": "\u2007", + "nvDash;": "\u22ad", + "nvHarr;": "\u2904", + "nvap;": "\u224d\u20d2", + "nvdash;": "\u22ac", + "nvge;": "\u2265\u20d2", + "nvgt;": ">\u20d2", + "nvinfin;": "\u29de", + "nvlArr;": "\u2902", + "nvle;": "\u2264\u20d2", + "nvlt;": "<\u20d2", + "nvltrie;": "\u22b4\u20d2", + "nvrArr;": "\u2903", + "nvrtrie;": "\u22b5\u20d2", + "nvsim;": "\u223c\u20d2", + "nwArr;": "\u21d6", + "nwarhk;": "\u2923", + "nwarr;": "\u2196", + "nwarrow;": "\u2196", + "nwnear;": "\u2927", + "oS;": "\u24c8", + "oacute": "\xf3", + "oacute;": "\xf3", + "oast;": "\u229b", + "ocir;": "\u229a", + "ocirc": "\xf4", + "ocirc;": "\xf4", + "ocy;": "\u043e", + "odash;": "\u229d", + "odblac;": "\u0151", + "odiv;": "\u2a38", + "odot;": "\u2299", + "odsold;": "\u29bc", + "oelig;": "\u0153", + "ofcir;": "\u29bf", + "ofr;": "\U0001d52c", + "ogon;": "\u02db", + "ograve": "\xf2", + "ograve;": "\xf2", + "ogt;": "\u29c1", + "ohbar;": "\u29b5", + "ohm;": "\u03a9", + "oint;": "\u222e", + "olarr;": "\u21ba", + "olcir;": "\u29be", + "olcross;": "\u29bb", + "oline;": "\u203e", + "olt;": "\u29c0", + "omacr;": "\u014d", + "omega;": "\u03c9", + "omicron;": "\u03bf", + "omid;": "\u29b6", + "ominus;": "\u2296", + "oopf;": "\U0001d560", + "opar;": "\u29b7", + "operp;": "\u29b9", + "oplus;": "\u2295", + "or;": "\u2228", + "orarr;": "\u21bb", + "ord;": "\u2a5d", + "order;": "\u2134", + "orderof;": "\u2134", + "ordf": "\xaa", + "ordf;": "\xaa", + "ordm": "\xba", + "ordm;": "\xba", + "origof;": "\u22b6", + "oror;": "\u2a56", + "orslope;": "\u2a57", + "orv;": "\u2a5b", + "oscr;": "\u2134", + "oslash": "\xf8", + "oslash;": "\xf8", + "osol;": "\u2298", + "otilde": "\xf5", + "otilde;": "\xf5", + "otimes;": "\u2297", + "otimesas;": "\u2a36", + "ouml": "\xf6", + "ouml;": "\xf6", + "ovbar;": "\u233d", + "par;": "\u2225", + "para": "\xb6", + "para;": "\xb6", + "parallel;": "\u2225", + "parsim;": "\u2af3", + "parsl;": "\u2afd", + "part;": "\u2202", + "pcy;": "\u043f", + "percnt;": "%", + "period;": ".", + "permil;": "\u2030", + "perp;": "\u22a5", + "pertenk;": "\u2031", + "pfr;": "\U0001d52d", + "phi;": "\u03c6", + "phiv;": "\u03d5", + "phmmat;": "\u2133", + "phone;": "\u260e", + "pi;": "\u03c0", + "pitchfork;": "\u22d4", + "piv;": "\u03d6", + "planck;": "\u210f", + "planckh;": "\u210e", + "plankv;": "\u210f", + "plus;": "+", + "plusacir;": "\u2a23", + "plusb;": "\u229e", + "pluscir;": "\u2a22", + "plusdo;": "\u2214", + "plusdu;": "\u2a25", + "pluse;": "\u2a72", + "plusmn": "\xb1", + "plusmn;": "\xb1", + "plussim;": "\u2a26", + "plustwo;": "\u2a27", + "pm;": "\xb1", + "pointint;": "\u2a15", + "popf;": "\U0001d561", + "pound": "\xa3", + "pound;": "\xa3", + "pr;": "\u227a", + "prE;": "\u2ab3", + "prap;": "\u2ab7", + "prcue;": "\u227c", + "pre;": "\u2aaf", + "prec;": "\u227a", + "precapprox;": "\u2ab7", + "preccurlyeq;": "\u227c", + "preceq;": "\u2aaf", + "precnapprox;": "\u2ab9", + "precneqq;": "\u2ab5", + "precnsim;": "\u22e8", + "precsim;": "\u227e", + "prime;": "\u2032", + "primes;": "\u2119", + "prnE;": "\u2ab5", + "prnap;": "\u2ab9", + "prnsim;": "\u22e8", + "prod;": "\u220f", + "profalar;": "\u232e", + "profline;": "\u2312", + "profsurf;": "\u2313", + "prop;": "\u221d", + "propto;": "\u221d", + "prsim;": "\u227e", + "prurel;": "\u22b0", + "pscr;": "\U0001d4c5", + "psi;": "\u03c8", + "puncsp;": "\u2008", + "qfr;": "\U0001d52e", + "qint;": "\u2a0c", + "qopf;": "\U0001d562", + "qprime;": "\u2057", + "qscr;": "\U0001d4c6", + "quaternions;": "\u210d", + "quatint;": "\u2a16", + "quest;": "?", + "questeq;": "\u225f", + "quot": "\"", + "quot;": "\"", + "rAarr;": "\u21db", + "rArr;": "\u21d2", + "rAtail;": "\u291c", + "rBarr;": "\u290f", + "rHar;": "\u2964", + "race;": "\u223d\u0331", + "racute;": "\u0155", + "radic;": "\u221a", + "raemptyv;": "\u29b3", + "rang;": "\u27e9", + "rangd;": "\u2992", + "range;": "\u29a5", + "rangle;": "\u27e9", + "raquo": "\xbb", + "raquo;": "\xbb", + "rarr;": "\u2192", + "rarrap;": "\u2975", + "rarrb;": "\u21e5", + "rarrbfs;": "\u2920", + "rarrc;": "\u2933", + "rarrfs;": "\u291e", + "rarrhk;": "\u21aa", + "rarrlp;": "\u21ac", + "rarrpl;": "\u2945", + "rarrsim;": "\u2974", + "rarrtl;": "\u21a3", + "rarrw;": "\u219d", + "ratail;": "\u291a", + "ratio;": "\u2236", + "rationals;": "\u211a", + "rbarr;": "\u290d", + "rbbrk;": "\u2773", + "rbrace;": "}", + "rbrack;": "]", + "rbrke;": "\u298c", + "rbrksld;": "\u298e", + "rbrkslu;": "\u2990", + "rcaron;": "\u0159", + "rcedil;": "\u0157", + "rceil;": "\u2309", + "rcub;": "}", + "rcy;": "\u0440", + "rdca;": "\u2937", + "rdldhar;": "\u2969", + "rdquo;": "\u201d", + "rdquor;": "\u201d", + "rdsh;": "\u21b3", + "real;": "\u211c", + "realine;": "\u211b", + "realpart;": "\u211c", + "reals;": "\u211d", + "rect;": "\u25ad", + "reg": "\xae", + "reg;": "\xae", + "rfisht;": "\u297d", + "rfloor;": "\u230b", + "rfr;": "\U0001d52f", + "rhard;": "\u21c1", + "rharu;": "\u21c0", + "rharul;": "\u296c", + "rho;": "\u03c1", + "rhov;": "\u03f1", + "rightarrow;": "\u2192", + "rightarrowtail;": "\u21a3", + "rightharpoondown;": "\u21c1", + "rightharpoonup;": "\u21c0", + "rightleftarrows;": "\u21c4", + "rightleftharpoons;": "\u21cc", + "rightrightarrows;": "\u21c9", + "rightsquigarrow;": "\u219d", + "rightthreetimes;": "\u22cc", + "ring;": "\u02da", + "risingdotseq;": "\u2253", + "rlarr;": "\u21c4", + "rlhar;": "\u21cc", + "rlm;": "\u200f", + "rmoust;": "\u23b1", + "rmoustache;": "\u23b1", + "rnmid;": "\u2aee", + "roang;": "\u27ed", + "roarr;": "\u21fe", + "robrk;": "\u27e7", + "ropar;": "\u2986", + "ropf;": "\U0001d563", + "roplus;": "\u2a2e", + "rotimes;": "\u2a35", + "rpar;": ")", + "rpargt;": "\u2994", + "rppolint;": "\u2a12", + "rrarr;": "\u21c9", + "rsaquo;": "\u203a", + "rscr;": "\U0001d4c7", + "rsh;": "\u21b1", + "rsqb;": "]", + "rsquo;": "\u2019", + "rsquor;": "\u2019", + "rthree;": "\u22cc", + "rtimes;": "\u22ca", + "rtri;": "\u25b9", + "rtrie;": "\u22b5", + "rtrif;": "\u25b8", + "rtriltri;": "\u29ce", + "ruluhar;": "\u2968", + "rx;": "\u211e", + "sacute;": "\u015b", + "sbquo;": "\u201a", + "sc;": "\u227b", + "scE;": "\u2ab4", + "scap;": "\u2ab8", + "scaron;": "\u0161", + "sccue;": "\u227d", + "sce;": "\u2ab0", + "scedil;": "\u015f", + "scirc;": "\u015d", + "scnE;": "\u2ab6", + "scnap;": "\u2aba", + "scnsim;": "\u22e9", + "scpolint;": "\u2a13", + "scsim;": "\u227f", + "scy;": "\u0441", + "sdot;": "\u22c5", + "sdotb;": "\u22a1", + "sdote;": "\u2a66", + "seArr;": "\u21d8", + "searhk;": "\u2925", + "searr;": "\u2198", + "searrow;": "\u2198", + "sect": "\xa7", + "sect;": "\xa7", + "semi;": ";", + "seswar;": "\u2929", + "setminus;": "\u2216", + "setmn;": "\u2216", + "sext;": "\u2736", + "sfr;": "\U0001d530", + "sfrown;": "\u2322", + "sharp;": "\u266f", + "shchcy;": "\u0449", + "shcy;": "\u0448", + "shortmid;": "\u2223", + "shortparallel;": "\u2225", + "shy": "\xad", + "shy;": "\xad", + "sigma;": "\u03c3", + "sigmaf;": "\u03c2", + "sigmav;": "\u03c2", + "sim;": "\u223c", + "simdot;": "\u2a6a", + "sime;": "\u2243", + "simeq;": "\u2243", + "simg;": "\u2a9e", + "simgE;": "\u2aa0", + "siml;": "\u2a9d", + "simlE;": "\u2a9f", + "simne;": "\u2246", + "simplus;": "\u2a24", + "simrarr;": "\u2972", + "slarr;": "\u2190", + "smallsetminus;": "\u2216", + "smashp;": "\u2a33", + "smeparsl;": "\u29e4", + "smid;": "\u2223", + "smile;": "\u2323", + "smt;": "\u2aaa", + "smte;": "\u2aac", + "smtes;": "\u2aac\ufe00", + "softcy;": "\u044c", + "sol;": "/", + "solb;": "\u29c4", + "solbar;": "\u233f", + "sopf;": "\U0001d564", + "spades;": "\u2660", + "spadesuit;": "\u2660", + "spar;": "\u2225", + "sqcap;": "\u2293", + "sqcaps;": "\u2293\ufe00", + "sqcup;": "\u2294", + "sqcups;": "\u2294\ufe00", + "sqsub;": "\u228f", + "sqsube;": "\u2291", + "sqsubset;": "\u228f", + "sqsubseteq;": "\u2291", + "sqsup;": "\u2290", + "sqsupe;": "\u2292", + "sqsupset;": "\u2290", + "sqsupseteq;": "\u2292", + "squ;": "\u25a1", + "square;": "\u25a1", + "squarf;": "\u25aa", + "squf;": "\u25aa", + "srarr;": "\u2192", + "sscr;": "\U0001d4c8", + "ssetmn;": "\u2216", + "ssmile;": "\u2323", + "sstarf;": "\u22c6", + "star;": "\u2606", + "starf;": "\u2605", + "straightepsilon;": "\u03f5", + "straightphi;": "\u03d5", + "strns;": "\xaf", + "sub;": "\u2282", + "subE;": "\u2ac5", + "subdot;": "\u2abd", + "sube;": "\u2286", + "subedot;": "\u2ac3", + "submult;": "\u2ac1", + "subnE;": "\u2acb", + "subne;": "\u228a", + "subplus;": "\u2abf", + "subrarr;": "\u2979", + "subset;": "\u2282", + "subseteq;": "\u2286", + "subseteqq;": "\u2ac5", + "subsetneq;": "\u228a", + "subsetneqq;": "\u2acb", + "subsim;": "\u2ac7", + "subsub;": "\u2ad5", + "subsup;": "\u2ad3", + "succ;": "\u227b", + "succapprox;": "\u2ab8", + "succcurlyeq;": "\u227d", + "succeq;": "\u2ab0", + "succnapprox;": "\u2aba", + "succneqq;": "\u2ab6", + "succnsim;": "\u22e9", + "succsim;": "\u227f", + "sum;": "\u2211", + "sung;": "\u266a", + "sup1": "\xb9", + "sup1;": "\xb9", + "sup2": "\xb2", + "sup2;": "\xb2", + "sup3": "\xb3", + "sup3;": "\xb3", + "sup;": "\u2283", + "supE;": "\u2ac6", + "supdot;": "\u2abe", + "supdsub;": "\u2ad8", + "supe;": "\u2287", + "supedot;": "\u2ac4", + "suphsol;": "\u27c9", + "suphsub;": "\u2ad7", + "suplarr;": "\u297b", + "supmult;": "\u2ac2", + "supnE;": "\u2acc", + "supne;": "\u228b", + "supplus;": "\u2ac0", + "supset;": "\u2283", + "supseteq;": "\u2287", + "supseteqq;": "\u2ac6", + "supsetneq;": "\u228b", + "supsetneqq;": "\u2acc", + "supsim;": "\u2ac8", + "supsub;": "\u2ad4", + "supsup;": "\u2ad6", + "swArr;": "\u21d9", + "swarhk;": "\u2926", + "swarr;": "\u2199", + "swarrow;": "\u2199", + "swnwar;": "\u292a", + "szlig": "\xdf", + "szlig;": "\xdf", + "target;": "\u2316", + "tau;": "\u03c4", + "tbrk;": "\u23b4", + "tcaron;": "\u0165", + "tcedil;": "\u0163", + "tcy;": "\u0442", + "tdot;": "\u20db", + "telrec;": "\u2315", + "tfr;": "\U0001d531", + "there4;": "\u2234", + "therefore;": "\u2234", + "theta;": "\u03b8", + "thetasym;": "\u03d1", + "thetav;": "\u03d1", + "thickapprox;": "\u2248", + "thicksim;": "\u223c", + "thinsp;": "\u2009", + "thkap;": "\u2248", + "thksim;": "\u223c", + "thorn": "\xfe", + "thorn;": "\xfe", + "tilde;": "\u02dc", + "times": "\xd7", + "times;": "\xd7", + "timesb;": "\u22a0", + "timesbar;": "\u2a31", + "timesd;": "\u2a30", + "tint;": "\u222d", + "toea;": "\u2928", + "top;": "\u22a4", + "topbot;": "\u2336", + "topcir;": "\u2af1", + "topf;": "\U0001d565", + "topfork;": "\u2ada", + "tosa;": "\u2929", + "tprime;": "\u2034", + "trade;": "\u2122", + "triangle;": "\u25b5", + "triangledown;": "\u25bf", + "triangleleft;": "\u25c3", + "trianglelefteq;": "\u22b4", + "triangleq;": "\u225c", + "triangleright;": "\u25b9", + "trianglerighteq;": "\u22b5", + "tridot;": "\u25ec", + "trie;": "\u225c", + "triminus;": "\u2a3a", + "triplus;": "\u2a39", + "trisb;": "\u29cd", + "tritime;": "\u2a3b", + "trpezium;": "\u23e2", + "tscr;": "\U0001d4c9", + "tscy;": "\u0446", + "tshcy;": "\u045b", + "tstrok;": "\u0167", + "twixt;": "\u226c", + "twoheadleftarrow;": "\u219e", + "twoheadrightarrow;": "\u21a0", + "uArr;": "\u21d1", + "uHar;": "\u2963", + "uacute": "\xfa", + "uacute;": "\xfa", + "uarr;": "\u2191", + "ubrcy;": "\u045e", + "ubreve;": "\u016d", + "ucirc": "\xfb", + "ucirc;": "\xfb", + "ucy;": "\u0443", + "udarr;": "\u21c5", + "udblac;": "\u0171", + "udhar;": "\u296e", + "ufisht;": "\u297e", + "ufr;": "\U0001d532", + "ugrave": "\xf9", + "ugrave;": "\xf9", + "uharl;": "\u21bf", + "uharr;": "\u21be", + "uhblk;": "\u2580", + "ulcorn;": "\u231c", + "ulcorner;": "\u231c", + "ulcrop;": "\u230f", + "ultri;": "\u25f8", + "umacr;": "\u016b", + "uml": "\xa8", + "uml;": "\xa8", + "uogon;": "\u0173", + "uopf;": "\U0001d566", + "uparrow;": "\u2191", + "updownarrow;": "\u2195", + "upharpoonleft;": "\u21bf", + "upharpoonright;": "\u21be", + "uplus;": "\u228e", + "upsi;": "\u03c5", + "upsih;": "\u03d2", + "upsilon;": "\u03c5", + "upuparrows;": "\u21c8", + "urcorn;": "\u231d", + "urcorner;": "\u231d", + "urcrop;": "\u230e", + "uring;": "\u016f", + "urtri;": "\u25f9", + "uscr;": "\U0001d4ca", + "utdot;": "\u22f0", + "utilde;": "\u0169", + "utri;": "\u25b5", + "utrif;": "\u25b4", + "uuarr;": "\u21c8", + "uuml": "\xfc", + "uuml;": "\xfc", + "uwangle;": "\u29a7", + "vArr;": "\u21d5", + "vBar;": "\u2ae8", + "vBarv;": "\u2ae9", + "vDash;": "\u22a8", + "vangrt;": "\u299c", + "varepsilon;": "\u03f5", + "varkappa;": "\u03f0", + "varnothing;": "\u2205", + "varphi;": "\u03d5", + "varpi;": "\u03d6", + "varpropto;": "\u221d", + "varr;": "\u2195", + "varrho;": "\u03f1", + "varsigma;": "\u03c2", + "varsubsetneq;": "\u228a\ufe00", + "varsubsetneqq;": "\u2acb\ufe00", + "varsupsetneq;": "\u228b\ufe00", + "varsupsetneqq;": "\u2acc\ufe00", + "vartheta;": "\u03d1", + "vartriangleleft;": "\u22b2", + "vartriangleright;": "\u22b3", + "vcy;": "\u0432", + "vdash;": "\u22a2", + "vee;": "\u2228", + "veebar;": "\u22bb", + "veeeq;": "\u225a", + "vellip;": "\u22ee", + "verbar;": "|", + "vert;": "|", + "vfr;": "\U0001d533", + "vltri;": "\u22b2", + "vnsub;": "\u2282\u20d2", + "vnsup;": "\u2283\u20d2", + "vopf;": "\U0001d567", + "vprop;": "\u221d", + "vrtri;": "\u22b3", + "vscr;": "\U0001d4cb", + "vsubnE;": "\u2acb\ufe00", + "vsubne;": "\u228a\ufe00", + "vsupnE;": "\u2acc\ufe00", + "vsupne;": "\u228b\ufe00", + "vzigzag;": "\u299a", + "wcirc;": "\u0175", + "wedbar;": "\u2a5f", + "wedge;": "\u2227", + "wedgeq;": "\u2259", + "weierp;": "\u2118", + "wfr;": "\U0001d534", + "wopf;": "\U0001d568", + "wp;": "\u2118", + "wr;": "\u2240", + "wreath;": "\u2240", + "wscr;": "\U0001d4cc", + "xcap;": "\u22c2", + "xcirc;": "\u25ef", + "xcup;": "\u22c3", + "xdtri;": "\u25bd", + "xfr;": "\U0001d535", + "xhArr;": "\u27fa", + "xharr;": "\u27f7", + "xi;": "\u03be", + "xlArr;": "\u27f8", + "xlarr;": "\u27f5", + "xmap;": "\u27fc", + "xnis;": "\u22fb", + "xodot;": "\u2a00", + "xopf;": "\U0001d569", + "xoplus;": "\u2a01", + "xotime;": "\u2a02", + "xrArr;": "\u27f9", + "xrarr;": "\u27f6", + "xscr;": "\U0001d4cd", + "xsqcup;": "\u2a06", + "xuplus;": "\u2a04", + "xutri;": "\u25b3", + "xvee;": "\u22c1", + "xwedge;": "\u22c0", + "yacute": "\xfd", + "yacute;": "\xfd", + "yacy;": "\u044f", + "ycirc;": "\u0177", + "ycy;": "\u044b", + "yen": "\xa5", + "yen;": "\xa5", + "yfr;": "\U0001d536", + "yicy;": "\u0457", + "yopf;": "\U0001d56a", + "yscr;": "\U0001d4ce", + "yucy;": "\u044e", + "yuml": "\xff", + "yuml;": "\xff", + "zacute;": "\u017a", + "zcaron;": "\u017e", + "zcy;": "\u0437", + "zdot;": "\u017c", + "zeetrf;": "\u2128", + "zeta;": "\u03b6", + "zfr;": "\U0001d537", + "zhcy;": "\u0436", + "zigrarr;": "\u21dd", + "zopf;": "\U0001d56b", + "zscr;": "\U0001d4cf", + "zwj;": "\u200d", + "zwnj;": "\u200c", +} + +replacementCharacters = { + 0x0: "\uFFFD", + 0x0d: "\u000D", + 0x80: "\u20AC", + 0x81: "\u0081", + 0x82: "\u201A", + 0x83: "\u0192", + 0x84: "\u201E", + 0x85: "\u2026", + 0x86: "\u2020", + 0x87: "\u2021", + 0x88: "\u02C6", + 0x89: "\u2030", + 0x8A: "\u0160", + 0x8B: "\u2039", + 0x8C: "\u0152", + 0x8D: "\u008D", + 0x8E: "\u017D", + 0x8F: "\u008F", + 0x90: "\u0090", + 0x91: "\u2018", + 0x92: "\u2019", + 0x93: "\u201C", + 0x94: "\u201D", + 0x95: "\u2022", + 0x96: "\u2013", + 0x97: "\u2014", + 0x98: "\u02DC", + 0x99: "\u2122", + 0x9A: "\u0161", + 0x9B: "\u203A", + 0x9C: "\u0153", + 0x9D: "\u009D", + 0x9E: "\u017E", + 0x9F: "\u0178", +} + +tokenTypes = { + "Doctype": 0, + "Characters": 1, + "SpaceCharacters": 2, + "StartTag": 3, + "EndTag": 4, + "EmptyTag": 5, + "Comment": 6, + "ParseError": 7 +} + +tagTokenTypes = frozenset([tokenTypes["StartTag"], tokenTypes["EndTag"], + tokenTypes["EmptyTag"]]) + + +prefixes = dict([(v, k) for k, v in namespaces.items()]) +prefixes["http://www.w3.org/1998/Math/MathML"] = "math" + + +class DataLossWarning(UserWarning): + pass + + +class ReparseException(Exception): + pass diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/alphabeticalattributes.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/alphabeticalattributes.py new file mode 100644 index 0000000..4795bae --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/alphabeticalattributes.py @@ -0,0 +1,20 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import base + +try: + from collections import OrderedDict +except ImportError: + from ordereddict import OrderedDict + + +class Filter(base.Filter): + def __iter__(self): + for token in base.Filter.__iter__(self): + if token["type"] in ("StartTag", "EmptyTag"): + attrs = OrderedDict() + for name, value in sorted(token["data"].items(), + key=lambda x: x[0]): + attrs[name] = value + token["data"] = attrs + yield token diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/base.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/base.py new file mode 100644 index 0000000..c7dbaed --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/base.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import, division, unicode_literals + + +class Filter(object): + def __init__(self, source): + self.source = source + + def __iter__(self): + return iter(self.source) + + def __getattr__(self, name): + return getattr(self.source, name) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/inject_meta_charset.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/inject_meta_charset.py new file mode 100644 index 0000000..2059ec8 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/inject_meta_charset.py @@ -0,0 +1,65 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import base + + +class Filter(base.Filter): + def __init__(self, source, encoding): + base.Filter.__init__(self, source) + self.encoding = encoding + + def __iter__(self): + state = "pre_head" + meta_found = (self.encoding is None) + pending = [] + + for token in base.Filter.__iter__(self): + type = token["type"] + if type == "StartTag": + if token["name"].lower() == "head": + state = "in_head" + + elif type == "EmptyTag": + if token["name"].lower() == "meta": + # replace charset with actual encoding + has_http_equiv_content_type = False + for (namespace, name), value in token["data"].items(): + if namespace is not None: + continue + elif name.lower() == 'charset': + token["data"][(namespace, name)] = self.encoding + meta_found = True + break + elif name == 'http-equiv' and value.lower() == 'content-type': + has_http_equiv_content_type = True + else: + if has_http_equiv_content_type and (None, "content") in token["data"]: + token["data"][(None, "content")] = 'text/html; charset=%s' % self.encoding + meta_found = True + + elif token["name"].lower() == "head" and not meta_found: + # insert meta into empty head + yield {"type": "StartTag", "name": "head", + "data": token["data"]} + yield {"type": "EmptyTag", "name": "meta", + "data": {(None, "charset"): self.encoding}} + yield {"type": "EndTag", "name": "head"} + meta_found = True + continue + + elif type == "EndTag": + if token["name"].lower() == "head" and pending: + # insert meta into head (if necessary) and flush pending queue + yield pending.pop(0) + if not meta_found: + yield {"type": "EmptyTag", "name": "meta", + "data": {(None, "charset"): self.encoding}} + while pending: + yield pending.pop(0) + meta_found = True + state = "post_head" + + if state == "in_head": + pending.append(token) + else: + yield token diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/lint.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/lint.py new file mode 100644 index 0000000..3b892c8 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/lint.py @@ -0,0 +1,81 @@ +from __future__ import absolute_import, division, unicode_literals + +from pip._vendor.six import text_type + +from . import base +from ..constants import namespaces, voidElements + +from ..constants import spaceCharacters +spaceCharacters = "".join(spaceCharacters) + + +class Filter(base.Filter): + def __init__(self, source, require_matching_tags=True): + super(Filter, self).__init__(source) + self.require_matching_tags = require_matching_tags + + def __iter__(self): + open_elements = [] + for token in base.Filter.__iter__(self): + type = token["type"] + if type in ("StartTag", "EmptyTag"): + namespace = token["namespace"] + name = token["name"] + assert namespace is None or isinstance(namespace, text_type) + assert namespace != "" + assert isinstance(name, text_type) + assert name != "" + assert isinstance(token["data"], dict) + if (not namespace or namespace == namespaces["html"]) and name in voidElements: + assert type == "EmptyTag" + else: + assert type == "StartTag" + if type == "StartTag" and self.require_matching_tags: + open_elements.append((namespace, name)) + for (namespace, name), value in token["data"].items(): + assert namespace is None or isinstance(namespace, text_type) + assert namespace != "" + assert isinstance(name, text_type) + assert name != "" + assert isinstance(value, text_type) + + elif type == "EndTag": + namespace = token["namespace"] + name = token["name"] + assert namespace is None or isinstance(namespace, text_type) + assert namespace != "" + assert isinstance(name, text_type) + assert name != "" + if (not namespace or namespace == namespaces["html"]) and name in voidElements: + assert False, "Void element reported as EndTag token: %(tag)s" % {"tag": name} + elif self.require_matching_tags: + start = open_elements.pop() + assert start == (namespace, name) + + elif type == "Comment": + data = token["data"] + assert isinstance(data, text_type) + + elif type in ("Characters", "SpaceCharacters"): + data = token["data"] + assert isinstance(data, text_type) + assert data != "" + if type == "SpaceCharacters": + assert data.strip(spaceCharacters) == "" + + elif type == "Doctype": + name = token["name"] + assert name is None or isinstance(name, text_type) + assert token["publicId"] is None or isinstance(name, text_type) + assert token["systemId"] is None or isinstance(name, text_type) + + elif type == "Entity": + assert isinstance(token["name"], text_type) + + elif type == "SerializerError": + assert isinstance(token["data"], text_type) + + else: + assert False, "Unknown token type: %(type)s" % {"type": type} + + yield token diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/optionaltags.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/optionaltags.py new file mode 100644 index 0000000..f6edb73 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/optionaltags.py @@ -0,0 +1,206 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import base + + +class Filter(base.Filter): + def slider(self): + previous1 = previous2 = None + for token in self.source: + if previous1 is not None: + yield previous2, previous1, token + previous2 = previous1 + previous1 = token + if previous1 is not None: + yield previous2, previous1, None + + def __iter__(self): + for previous, token, next in self.slider(): + type = token["type"] + if type == "StartTag": + if (token["data"] or + not self.is_optional_start(token["name"], previous, next)): + yield token + elif type == "EndTag": + if not self.is_optional_end(token["name"], next): + yield token + else: + yield token + + def is_optional_start(self, tagname, previous, next): + type = next and next["type"] or None + if tagname in 'html': + # An html element's start tag may be omitted if the first thing + # inside the html element is not a space character or a comment. + return type not in ("Comment", "SpaceCharacters") + elif tagname == 'head': + # A head element's start tag may be omitted if the first thing + # inside the head element is an element. + # XXX: we also omit the start tag if the head element is empty + if type in ("StartTag", "EmptyTag"): + return True + elif type == "EndTag": + return next["name"] == "head" + elif tagname == 'body': + # A body element's start tag may be omitted if the first thing + # inside the body element is not a space character or a comment, + # except if the first thing inside the body element is a script + # or style element and the node immediately preceding the body + # element is a head element whose end tag has been omitted. + if type in ("Comment", "SpaceCharacters"): + return False + elif type == "StartTag": + # XXX: we do not look at the preceding event, so we never omit + # the body element's start tag if it's followed by a script or + # a style element. + return next["name"] not in ('script', 'style') + else: + return True + elif tagname == 'colgroup': + # A colgroup element's start tag may be omitted if the first thing + # inside the colgroup element is a col element, and if the element + # is not immediately preceded by another colgroup element whose + # end tag has been omitted. + if type in ("StartTag", "EmptyTag"): + # XXX: we do not look at the preceding event, so instead we never + # omit the colgroup element's end tag when it is immediately + # followed by another colgroup element. See is_optional_end. + return next["name"] == "col" + else: + return False + elif tagname == 'tbody': + # A tbody element's start tag may be omitted if the first thing + # inside the tbody element is a tr element, and if the element is + # not immediately preceded by a tbody, thead, or tfoot element + # whose end tag has been omitted. + if type == "StartTag": + # omit the thead and tfoot elements' end tag when they are + # immediately followed by a tbody element. See is_optional_end. + if previous and previous['type'] == 'EndTag' and \ + previous['name'] in ('tbody', 'thead', 'tfoot'): + return False + return next["name"] == 'tr' + else: + return False + return False + + def is_optional_end(self, tagname, next): + type = next and next["type"] or None + if tagname in ('html', 'head', 'body'): + # An html element's end tag may be omitted if the html element + # is not immediately followed by a space character or a comment. + return type not in ("Comment", "SpaceCharacters") + elif tagname in ('li', 'optgroup', 'tr'): + # A li element's end tag may be omitted if the li element is + # immediately followed by another li element or if there is + # no more content in the parent element. + # An optgroup element's end tag may be omitted if the optgroup + # element is immediately followed by another optgroup element, + # or if there is no more content in the parent element. + # A tr element's end tag may be omitted if the tr element is + # immediately followed by another tr element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] == tagname + else: + return type == "EndTag" or type is None + elif tagname in ('dt', 'dd'): + # A dt element's end tag may be omitted if the dt element is + # immediately followed by another dt element or a dd element. + # A dd element's end tag may be omitted if the dd element is + # immediately followed by another dd element or a dt element, + # or if there is no more content in the parent element. + if type == "StartTag": + return next["name"] in ('dt', 'dd') + elif tagname == 'dd': + return type == "EndTag" or type is None + else: + return False + elif tagname == 'p': + # A p element's end tag may be omitted if the p element is + # immediately followed by an address, article, aside, + # blockquote, datagrid, dialog, dir, div, dl, fieldset, + # footer, form, h1, h2, h3, h4, h5, h6, header, hr, menu, + # nav, ol, p, pre, section, table, or ul, element, or if + # there is no more content in the parent element. + if type in ("StartTag", "EmptyTag"): + return next["name"] in ('address', 'article', 'aside', + 'blockquote', 'datagrid', 'dialog', + 'dir', 'div', 'dl', 'fieldset', 'footer', + 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'header', 'hr', 'menu', 'nav', 'ol', + 'p', 'pre', 'section', 'table', 'ul') + else: + return type == "EndTag" or type is None + elif tagname == 'option': + # An option element's end tag may be omitted if the option + # element is immediately followed by another option element, + # or if it is immediately followed by an <code>optgroup</code> + # element, or if there is no more content in the parent + # element. + if type == "StartTag": + return next["name"] in ('option', 'optgroup') + else: + return type == "EndTag" or type is None + elif tagname in ('rt', 'rp'): + # An rt element's end tag may be omitted if the rt element is + # immediately followed by an rt or rp element, or if there is + # no more content in the parent element. + # An rp element's end tag may be omitted if the rp element is + # immediately followed by an rt or rp element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] in ('rt', 'rp') + else: + return type == "EndTag" or type is None + elif tagname == 'colgroup': + # A colgroup element's end tag may be omitted if the colgroup + # element is not immediately followed by a space character or + # a comment. + if type in ("Comment", "SpaceCharacters"): + return False + elif type == "StartTag": + # XXX: we also look for an immediately following colgroup + # element. See is_optional_start. + return next["name"] != 'colgroup' + else: + return True + elif tagname in ('thead', 'tbody'): + # A thead element's end tag may be omitted if the thead element + # is immediately followed by a tbody or tfoot element. + # A tbody element's end tag may be omitted if the tbody element + # is immediately followed by a tbody or tfoot element, or if + # there is no more content in the parent element. + # A tfoot element's end tag may be omitted if the tfoot element + # is immediately followed by a tbody element, or if there is no + # more content in the parent element. + # XXX: we never omit the end tag when the following element is + # a tbody. See is_optional_start. + if type == "StartTag": + return next["name"] in ['tbody', 'tfoot'] + elif tagname == 'tbody': + return type == "EndTag" or type is None + else: + return False + elif tagname == 'tfoot': + # A tfoot element's end tag may be omitted if the tfoot element + # is immediately followed by a tbody element, or if there is no + # more content in the parent element. + # XXX: we never omit the end tag when the following element is + # a tbody. See is_optional_start. + if type == "StartTag": + return next["name"] == 'tbody' + else: + return type == "EndTag" or type is None + elif tagname in ('td', 'th'): + # A td element's end tag may be omitted if the td element is + # immediately followed by a td or th element, or if there is + # no more content in the parent element. + # A th element's end tag may be omitted if the th element is + # immediately followed by a td or th element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] in ('td', 'th') + else: + return type == "EndTag" or type is None + return False diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/sanitizer.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/sanitizer.py new file mode 100644 index 0000000..026748d --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/sanitizer.py @@ -0,0 +1,865 @@ +from __future__ import absolute_import, division, unicode_literals + +import re +from xml.sax.saxutils import escape, unescape + +from pip._vendor.six.moves import urllib_parse as urlparse + +from . import base +from ..constants import namespaces, prefixes + +__all__ = ["Filter"] + + +allowed_elements = frozenset(( + (namespaces['html'], 'a'), + (namespaces['html'], 'abbr'), + (namespaces['html'], 'acronym'), + (namespaces['html'], 'address'), + (namespaces['html'], 'area'), + (namespaces['html'], 'article'), + (namespaces['html'], 'aside'), + (namespaces['html'], 'audio'), + (namespaces['html'], 'b'), + (namespaces['html'], 'big'), + (namespaces['html'], 'blockquote'), + (namespaces['html'], 'br'), + (namespaces['html'], 'button'), + (namespaces['html'], 'canvas'), + (namespaces['html'], 'caption'), + (namespaces['html'], 'center'), + (namespaces['html'], 'cite'), + (namespaces['html'], 'code'), + (namespaces['html'], 'col'), + (namespaces['html'], 'colgroup'), + (namespaces['html'], 'command'), + (namespaces['html'], 'datagrid'), + (namespaces['html'], 'datalist'), + (namespaces['html'], 'dd'), + (namespaces['html'], 'del'), + (namespaces['html'], 'details'), + (namespaces['html'], 'dfn'), + (namespaces['html'], 'dialog'), + (namespaces['html'], 'dir'), + (namespaces['html'], 'div'), + (namespaces['html'], 'dl'), + (namespaces['html'], 'dt'), + (namespaces['html'], 'em'), + (namespaces['html'], 'event-source'), + (namespaces['html'], 'fieldset'), + (namespaces['html'], 'figcaption'), + (namespaces['html'], 'figure'), + (namespaces['html'], 'footer'), + (namespaces['html'], 'font'), + (namespaces['html'], 'form'), + (namespaces['html'], 'header'), + (namespaces['html'], 'h1'), + (namespaces['html'], 'h2'), + (namespaces['html'], 'h3'), + (namespaces['html'], 'h4'), + (namespaces['html'], 'h5'), + (namespaces['html'], 'h6'), + (namespaces['html'], 'hr'), + (namespaces['html'], 'i'), + (namespaces['html'], 'img'), + (namespaces['html'], 'input'), + (namespaces['html'], 'ins'), + (namespaces['html'], 'keygen'), + (namespaces['html'], 'kbd'), + (namespaces['html'], 'label'), + (namespaces['html'], 'legend'), + (namespaces['html'], 'li'), + (namespaces['html'], 'm'), + (namespaces['html'], 'map'), + (namespaces['html'], 'menu'), + (namespaces['html'], 'meter'), + (namespaces['html'], 'multicol'), + (namespaces['html'], 'nav'), + (namespaces['html'], 'nextid'), + (namespaces['html'], 'ol'), + (namespaces['html'], 'output'), + (namespaces['html'], 'optgroup'), + (namespaces['html'], 'option'), + (namespaces['html'], 'p'), + (namespaces['html'], 'pre'), + (namespaces['html'], 'progress'), + (namespaces['html'], 'q'), + (namespaces['html'], 's'), + (namespaces['html'], 'samp'), + (namespaces['html'], 'section'), + (namespaces['html'], 'select'), + (namespaces['html'], 'small'), + (namespaces['html'], 'sound'), + (namespaces['html'], 'source'), + (namespaces['html'], 'spacer'), + (namespaces['html'], 'span'), + (namespaces['html'], 'strike'), + (namespaces['html'], 'strong'), + (namespaces['html'], 'sub'), + (namespaces['html'], 'sup'), + (namespaces['html'], 'table'), + (namespaces['html'], 'tbody'), + (namespaces['html'], 'td'), + (namespaces['html'], 'textarea'), + (namespaces['html'], 'time'), + (namespaces['html'], 'tfoot'), + (namespaces['html'], 'th'), + (namespaces['html'], 'thead'), + (namespaces['html'], 'tr'), + (namespaces['html'], 'tt'), + (namespaces['html'], 'u'), + (namespaces['html'], 'ul'), + (namespaces['html'], 'var'), + (namespaces['html'], 'video'), + (namespaces['mathml'], 'maction'), + (namespaces['mathml'], 'math'), + (namespaces['mathml'], 'merror'), + (namespaces['mathml'], 'mfrac'), + (namespaces['mathml'], 'mi'), + (namespaces['mathml'], 'mmultiscripts'), + (namespaces['mathml'], 'mn'), + (namespaces['mathml'], 'mo'), + (namespaces['mathml'], 'mover'), + (namespaces['mathml'], 'mpadded'), + (namespaces['mathml'], 'mphantom'), + (namespaces['mathml'], 'mprescripts'), + (namespaces['mathml'], 'mroot'), + (namespaces['mathml'], 'mrow'), + (namespaces['mathml'], 'mspace'), + (namespaces['mathml'], 'msqrt'), + (namespaces['mathml'], 'mstyle'), + (namespaces['mathml'], 'msub'), + (namespaces['mathml'], 'msubsup'), + (namespaces['mathml'], 'msup'), + (namespaces['mathml'], 'mtable'), + (namespaces['mathml'], 'mtd'), + (namespaces['mathml'], 'mtext'), + (namespaces['mathml'], 'mtr'), + (namespaces['mathml'], 'munder'), + (namespaces['mathml'], 'munderover'), + (namespaces['mathml'], 'none'), + (namespaces['svg'], 'a'), + (namespaces['svg'], 'animate'), + (namespaces['svg'], 'animateColor'), + (namespaces['svg'], 'animateMotion'), + (namespaces['svg'], 'animateTransform'), + (namespaces['svg'], 'clipPath'), + (namespaces['svg'], 'circle'), + (namespaces['svg'], 'defs'), + (namespaces['svg'], 'desc'), + (namespaces['svg'], 'ellipse'), + (namespaces['svg'], 'font-face'), + (namespaces['svg'], 'font-face-name'), + (namespaces['svg'], 'font-face-src'), + (namespaces['svg'], 'g'), + (namespaces['svg'], 'glyph'), + (namespaces['svg'], 'hkern'), + (namespaces['svg'], 'linearGradient'), + (namespaces['svg'], 'line'), + (namespaces['svg'], 'marker'), + (namespaces['svg'], 'metadata'), + (namespaces['svg'], 'missing-glyph'), + (namespaces['svg'], 'mpath'), + (namespaces['svg'], 'path'), + (namespaces['svg'], 'polygon'), + (namespaces['svg'], 'polyline'), + (namespaces['svg'], 'radialGradient'), + (namespaces['svg'], 'rect'), + (namespaces['svg'], 'set'), + (namespaces['svg'], 'stop'), + (namespaces['svg'], 'svg'), + (namespaces['svg'], 'switch'), + (namespaces['svg'], 'text'), + (namespaces['svg'], 'title'), + (namespaces['svg'], 'tspan'), + (namespaces['svg'], 'use'), +)) + +allowed_attributes = frozenset(( + # HTML attributes + (None, 'abbr'), + (None, 'accept'), + (None, 'accept-charset'), + (None, 'accesskey'), + (None, 'action'), + (None, 'align'), + (None, 'alt'), + (None, 'autocomplete'), + (None, 'autofocus'), + (None, 'axis'), + (None, 'background'), + (None, 'balance'), + (None, 'bgcolor'), + (None, 'bgproperties'), + (None, 'border'), + (None, 'bordercolor'), + (None, 'bordercolordark'), + (None, 'bordercolorlight'), + (None, 'bottompadding'), + (None, 'cellpadding'), + (None, 'cellspacing'), + (None, 'ch'), + (None, 'challenge'), + (None, 'char'), + (None, 'charoff'), + (None, 'choff'), + (None, 'charset'), + (None, 'checked'), + (None, 'cite'), + (None, 'class'), + (None, 'clear'), + (None, 'color'), + (None, 'cols'), + (None, 'colspan'), + (None, 'compact'), + (None, 'contenteditable'), + (None, 'controls'), + (None, 'coords'), + (None, 'data'), + (None, 'datafld'), + (None, 'datapagesize'), + (None, 'datasrc'), + (None, 'datetime'), + (None, 'default'), + (None, 'delay'), + (None, 'dir'), + (None, 'disabled'), + (None, 'draggable'), + (None, 'dynsrc'), + (None, 'enctype'), + (None, 'end'), + (None, 'face'), + (None, 'for'), + (None, 'form'), + (None, 'frame'), + (None, 'galleryimg'), + (None, 'gutter'), + (None, 'headers'), + (None, 'height'), + (None, 'hidefocus'), + (None, 'hidden'), + (None, 'high'), + (None, 'href'), + (None, 'hreflang'), + (None, 'hspace'), + (None, 'icon'), + (None, 'id'), + (None, 'inputmode'), + (None, 'ismap'), + (None, 'keytype'), + (None, 'label'), + (None, 'leftspacing'), + (None, 'lang'), + (None, 'list'), + (None, 'longdesc'), + (None, 'loop'), + (None, 'loopcount'), + (None, 'loopend'), + (None, 'loopstart'), + (None, 'low'), + (None, 'lowsrc'), + (None, 'max'), + (None, 'maxlength'), + (None, 'media'), + (None, 'method'), + (None, 'min'), + (None, 'multiple'), + (None, 'name'), + (None, 'nohref'), + (None, 'noshade'), + (None, 'nowrap'), + (None, 'open'), + (None, 'optimum'), + (None, 'pattern'), + (None, 'ping'), + (None, 'point-size'), + (None, 'poster'), + (None, 'pqg'), + (None, 'preload'), + (None, 'prompt'), + (None, 'radiogroup'), + (None, 'readonly'), + (None, 'rel'), + (None, 'repeat-max'), + (None, 'repeat-min'), + (None, 'replace'), + (None, 'required'), + (None, 'rev'), + (None, 'rightspacing'), + (None, 'rows'), + (None, 'rowspan'), + (None, 'rules'), + (None, 'scope'), + (None, 'selected'), + (None, 'shape'), + (None, 'size'), + (None, 'span'), + (None, 'src'), + (None, 'start'), + (None, 'step'), + (None, 'style'), + (None, 'summary'), + (None, 'suppress'), + (None, 'tabindex'), + (None, 'target'), + (None, 'template'), + (None, 'title'), + (None, 'toppadding'), + (None, 'type'), + (None, 'unselectable'), + (None, 'usemap'), + (None, 'urn'), + (None, 'valign'), + (None, 'value'), + (None, 'variable'), + (None, 'volume'), + (None, 'vspace'), + (None, 'vrml'), + (None, 'width'), + (None, 'wrap'), + (namespaces['xml'], 'lang'), + # MathML attributes + (None, 'actiontype'), + (None, 'align'), + (None, 'columnalign'), + (None, 'columnalign'), + (None, 'columnalign'), + (None, 'columnlines'), + (None, 'columnspacing'), + (None, 'columnspan'), + (None, 'depth'), + (None, 'display'), + (None, 'displaystyle'), + (None, 'equalcolumns'), + (None, 'equalrows'), + (None, 'fence'), + (None, 'fontstyle'), + (None, 'fontweight'), + (None, 'frame'), + (None, 'height'), + (None, 'linethickness'), + (None, 'lspace'), + (None, 'mathbackground'), + (None, 'mathcolor'), + (None, 'mathvariant'), + (None, 'mathvariant'), + (None, 'maxsize'), + (None, 'minsize'), + (None, 'other'), + (None, 'rowalign'), + (None, 'rowalign'), + (None, 'rowalign'), + (None, 'rowlines'), + (None, 'rowspacing'), + (None, 'rowspan'), + (None, 'rspace'), + (None, 'scriptlevel'), + (None, 'selection'), + (None, 'separator'), + (None, 'stretchy'), + (None, 'width'), + (None, 'width'), + (namespaces['xlink'], 'href'), + (namespaces['xlink'], 'show'), + (namespaces['xlink'], 'type'), + # SVG attributes + (None, 'accent-height'), + (None, 'accumulate'), + (None, 'additive'), + (None, 'alphabetic'), + (None, 'arabic-form'), + (None, 'ascent'), + (None, 'attributeName'), + (None, 'attributeType'), + (None, 'baseProfile'), + (None, 'bbox'), + (None, 'begin'), + (None, 'by'), + (None, 'calcMode'), + (None, 'cap-height'), + (None, 'class'), + (None, 'clip-path'), + (None, 'color'), + (None, 'color-rendering'), + (None, 'content'), + (None, 'cx'), + (None, 'cy'), + (None, 'd'), + (None, 'dx'), + (None, 'dy'), + (None, 'descent'), + (None, 'display'), + (None, 'dur'), + (None, 'end'), + (None, 'fill'), + (None, 'fill-opacity'), + (None, 'fill-rule'), + (None, 'font-family'), + (None, 'font-size'), + (None, 'font-stretch'), + (None, 'font-style'), + (None, 'font-variant'), + (None, 'font-weight'), + (None, 'from'), + (None, 'fx'), + (None, 'fy'), + (None, 'g1'), + (None, 'g2'), + (None, 'glyph-name'), + (None, 'gradientUnits'), + (None, 'hanging'), + (None, 'height'), + (None, 'horiz-adv-x'), + (None, 'horiz-origin-x'), + (None, 'id'), + (None, 'ideographic'), + (None, 'k'), + (None, 'keyPoints'), + (None, 'keySplines'), + (None, 'keyTimes'), + (None, 'lang'), + (None, 'marker-end'), + (None, 'marker-mid'), + (None, 'marker-start'), + (None, 'markerHeight'), + (None, 'markerUnits'), + (None, 'markerWidth'), + (None, 'mathematical'), + (None, 'max'), + (None, 'min'), + (None, 'name'), + (None, 'offset'), + (None, 'opacity'), + (None, 'orient'), + (None, 'origin'), + (None, 'overline-position'), + (None, 'overline-thickness'), + (None, 'panose-1'), + (None, 'path'), + (None, 'pathLength'), + (None, 'points'), + (None, 'preserveAspectRatio'), + (None, 'r'), + (None, 'refX'), + (None, 'refY'), + (None, 'repeatCount'), + (None, 'repeatDur'), + (None, 'requiredExtensions'), + (None, 'requiredFeatures'), + (None, 'restart'), + (None, 'rotate'), + (None, 'rx'), + (None, 'ry'), + (None, 'slope'), + (None, 'stemh'), + (None, 'stemv'), + (None, 'stop-color'), + (None, 'stop-opacity'), + (None, 'strikethrough-position'), + (None, 'strikethrough-thickness'), + (None, 'stroke'), + (None, 'stroke-dasharray'), + (None, 'stroke-dashoffset'), + (None, 'stroke-linecap'), + (None, 'stroke-linejoin'), + (None, 'stroke-miterlimit'), + (None, 'stroke-opacity'), + (None, 'stroke-width'), + (None, 'systemLanguage'), + (None, 'target'), + (None, 'text-anchor'), + (None, 'to'), + (None, 'transform'), + (None, 'type'), + (None, 'u1'), + (None, 'u2'), + (None, 'underline-position'), + (None, 'underline-thickness'), + (None, 'unicode'), + (None, 'unicode-range'), + (None, 'units-per-em'), + (None, 'values'), + (None, 'version'), + (None, 'viewBox'), + (None, 'visibility'), + (None, 'width'), + (None, 'widths'), + (None, 'x'), + (None, 'x-height'), + (None, 'x1'), + (None, 'x2'), + (namespaces['xlink'], 'actuate'), + (namespaces['xlink'], 'arcrole'), + (namespaces['xlink'], 'href'), + (namespaces['xlink'], 'role'), + (namespaces['xlink'], 'show'), + (namespaces['xlink'], 'title'), + (namespaces['xlink'], 'type'), + (namespaces['xml'], 'base'), + (namespaces['xml'], 'lang'), + (namespaces['xml'], 'space'), + (None, 'y'), + (None, 'y1'), + (None, 'y2'), + (None, 'zoomAndPan'), +)) + +attr_val_is_uri = frozenset(( + (None, 'href'), + (None, 'src'), + (None, 'cite'), + (None, 'action'), + (None, 'longdesc'), + (None, 'poster'), + (None, 'background'), + (None, 'datasrc'), + (None, 'dynsrc'), + (None, 'lowsrc'), + (None, 'ping'), + (namespaces['xlink'], 'href'), + (namespaces['xml'], 'base'), +)) + +svg_attr_val_allows_ref = frozenset(( + (None, 'clip-path'), + (None, 'color-profile'), + (None, 'cursor'), + (None, 'fill'), + (None, 'filter'), + (None, 'marker'), + (None, 'marker-start'), + (None, 'marker-mid'), + (None, 'marker-end'), + (None, 'mask'), + (None, 'stroke'), +)) + +svg_allow_local_href = frozenset(( + (None, 'altGlyph'), + (None, 'animate'), + (None, 'animateColor'), + (None, 'animateMotion'), + (None, 'animateTransform'), + (None, 'cursor'), + (None, 'feImage'), + (None, 'filter'), + (None, 'linearGradient'), + (None, 'pattern'), + (None, 'radialGradient'), + (None, 'textpath'), + (None, 'tref'), + (None, 'set'), + (None, 'use') +)) + +allowed_css_properties = frozenset(( + 'azimuth', + 'background-color', + 'border-bottom-color', + 'border-collapse', + 'border-color', + 'border-left-color', + 'border-right-color', + 'border-top-color', + 'clear', + 'color', + 'cursor', + 'direction', + 'display', + 'elevation', + 'float', + 'font', + 'font-family', + 'font-size', + 'font-style', + 'font-variant', + 'font-weight', + 'height', + 'letter-spacing', + 'line-height', + 'overflow', + 'pause', + 'pause-after', + 'pause-before', + 'pitch', + 'pitch-range', + 'richness', + 'speak', + 'speak-header', + 'speak-numeral', + 'speak-punctuation', + 'speech-rate', + 'stress', + 'text-align', + 'text-decoration', + 'text-indent', + 'unicode-bidi', + 'vertical-align', + 'voice-family', + 'volume', + 'white-space', + 'width', +)) + +allowed_css_keywords = frozenset(( + 'auto', + 'aqua', + 'black', + 'block', + 'blue', + 'bold', + 'both', + 'bottom', + 'brown', + 'center', + 'collapse', + 'dashed', + 'dotted', + 'fuchsia', + 'gray', + 'green', + '!important', + 'italic', + 'left', + 'lime', + 'maroon', + 'medium', + 'none', + 'navy', + 'normal', + 'nowrap', + 'olive', + 'pointer', + 'purple', + 'red', + 'right', + 'solid', + 'silver', + 'teal', + 'top', + 'transparent', + 'underline', + 'white', + 'yellow', +)) + +allowed_svg_properties = frozenset(( + 'fill', + 'fill-opacity', + 'fill-rule', + 'stroke', + 'stroke-width', + 'stroke-linecap', + 'stroke-linejoin', + 'stroke-opacity', +)) + +allowed_protocols = frozenset(( + 'ed2k', + 'ftp', + 'http', + 'https', + 'irc', + 'mailto', + 'news', + 'gopher', + 'nntp', + 'telnet', + 'webcal', + 'xmpp', + 'callto', + 'feed', + 'urn', + 'aim', + 'rsync', + 'tag', + 'ssh', + 'sftp', + 'rtsp', + 'afs', + 'data', +)) + +allowed_content_types = frozenset(( + 'image/png', + 'image/jpeg', + 'image/gif', + 'image/webp', + 'image/bmp', + 'text/plain', +)) + + +data_content_type = re.compile(r''' + ^ + # Match a content type <application>/<type> + (?P<content_type>[-a-zA-Z0-9.]+/[-a-zA-Z0-9.]+) + # Match any character set and encoding + (?:(?:;charset=(?:[-a-zA-Z0-9]+)(?:;(?:base64))?) + |(?:;(?:base64))?(?:;charset=(?:[-a-zA-Z0-9]+))?) + # Assume the rest is data + ,.* + $ + ''', + re.VERBOSE) + + +class Filter(base.Filter): + """ sanitization of XHTML+MathML+SVG and of inline style attributes.""" + def __init__(self, + source, + allowed_elements=allowed_elements, + allowed_attributes=allowed_attributes, + allowed_css_properties=allowed_css_properties, + allowed_css_keywords=allowed_css_keywords, + allowed_svg_properties=allowed_svg_properties, + allowed_protocols=allowed_protocols, + allowed_content_types=allowed_content_types, + attr_val_is_uri=attr_val_is_uri, + svg_attr_val_allows_ref=svg_attr_val_allows_ref, + svg_allow_local_href=svg_allow_local_href): + super(Filter, self).__init__(source) + self.allowed_elements = allowed_elements + self.allowed_attributes = allowed_attributes + self.allowed_css_properties = allowed_css_properties + self.allowed_css_keywords = allowed_css_keywords + self.allowed_svg_properties = allowed_svg_properties + self.allowed_protocols = allowed_protocols + self.allowed_content_types = allowed_content_types + self.attr_val_is_uri = attr_val_is_uri + self.svg_attr_val_allows_ref = svg_attr_val_allows_ref + self.svg_allow_local_href = svg_allow_local_href + + def __iter__(self): + for token in base.Filter.__iter__(self): + token = self.sanitize_token(token) + if token: + yield token + + # Sanitize the +html+, escaping all elements not in ALLOWED_ELEMENTS, and + # stripping out all # attributes not in ALLOWED_ATTRIBUTES. Style + # attributes are parsed, and a restricted set, # specified by + # ALLOWED_CSS_PROPERTIES and ALLOWED_CSS_KEYWORDS, are allowed through. + # attributes in ATTR_VAL_IS_URI are scanned, and only URI schemes specified + # in ALLOWED_PROTOCOLS are allowed. + # + # sanitize_html('<script> do_nasty_stuff() </script>') + # => <script> do_nasty_stuff() </script> + # sanitize_html('<a href="javascript: sucker();">Click here for $100</a>') + # => <a>Click here for $100</a> + def sanitize_token(self, token): + + # accommodate filters which use token_type differently + token_type = token["type"] + if token_type in ("StartTag", "EndTag", "EmptyTag"): + name = token["name"] + namespace = token["namespace"] + if ((namespace, name) in self.allowed_elements or + (namespace is None and + (namespaces["html"], name) in self.allowed_elements)): + return self.allowed_token(token) + else: + return self.disallowed_token(token) + elif token_type == "Comment": + pass + else: + return token + + def allowed_token(self, token): + if "data" in token: + attrs = token["data"] + attr_names = set(attrs.keys()) + + # Remove forbidden attributes + for to_remove in (attr_names - self.allowed_attributes): + del token["data"][to_remove] + attr_names.remove(to_remove) + + # Remove attributes with disallowed URL values + for attr in (attr_names & self.attr_val_is_uri): + assert attr in attrs + # I don't have a clue where this regexp comes from or why it matches those + # characters, nor why we call unescape. I just know it's always been here. + # Should you be worried by this comment in a sanitizer? Yes. On the other hand, all + # this will do is remove *more* than it otherwise would. + val_unescaped = re.sub("[`\x00-\x20\x7f-\xa0\s]+", '', + unescape(attrs[attr])).lower() + # remove replacement characters from unescaped characters + val_unescaped = val_unescaped.replace("\ufffd", "") + try: + uri = urlparse.urlparse(val_unescaped) + except ValueError: + uri = None + del attrs[attr] + if uri and uri.scheme: + if uri.scheme not in self.allowed_protocols: + del attrs[attr] + if uri.scheme == 'data': + m = data_content_type.match(uri.path) + if not m: + del attrs[attr] + elif m.group('content_type') not in self.allowed_content_types: + del attrs[attr] + + for attr in self.svg_attr_val_allows_ref: + if attr in attrs: + attrs[attr] = re.sub(r'url\s*\(\s*[^#\s][^)]+?\)', + ' ', + unescape(attrs[attr])) + if (token["name"] in self.svg_allow_local_href and + (namespaces['xlink'], 'href') in attrs and re.search('^\s*[^#\s].*', + attrs[(namespaces['xlink'], 'href')])): + del attrs[(namespaces['xlink'], 'href')] + if (None, 'style') in attrs: + attrs[(None, 'style')] = self.sanitize_css(attrs[(None, 'style')]) + token["data"] = attrs + return token + + def disallowed_token(self, token): + token_type = token["type"] + if token_type == "EndTag": + token["data"] = "</%s>" % token["name"] + elif token["data"]: + assert token_type in ("StartTag", "EmptyTag") + attrs = [] + for (ns, name), v in token["data"].items(): + attrs.append(' %s="%s"' % (name if ns is None else "%s:%s" % (prefixes[ns], name), escape(v))) + token["data"] = "<%s%s>" % (token["name"], ''.join(attrs)) + else: + token["data"] = "<%s>" % token["name"] + if token.get("selfClosing"): + token["data"] = token["data"][:-1] + "/>" + + token["type"] = "Characters" + + del token["name"] + return token + + def sanitize_css(self, style): + # disallow urls + style = re.compile('url\s*\(\s*[^\s)]+?\s*\)\s*').sub(' ', style) + + # gauntlet + if not re.match("""^([:,;#%.\sa-zA-Z0-9!]|\w-\w|'[\s\w]+'|"[\s\w]+"|\([\d,\s]+\))*$""", style): + return '' + if not re.match("^\s*([-\w]+\s*:[^:;]*(;\s*|$))*$", style): + return '' + + clean = [] + for prop, value in re.findall("([-\w]+)\s*:\s*([^:;]*)", style): + if not value: + continue + if prop.lower() in self.allowed_css_properties: + clean.append(prop + ': ' + value + ';') + elif prop.split('-')[0].lower() in ['background', 'border', 'margin', + 'padding']: + for keyword in value.split(): + if keyword not in self.allowed_css_keywords and \ + not re.match("^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$", keyword): # noqa + break + else: + clean.append(prop + ': ' + value + ';') + elif prop.lower() in self.allowed_svg_properties: + clean.append(prop + ': ' + value + ';') + + return ' '.join(clean) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/whitespace.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/whitespace.py new file mode 100644 index 0000000..8921052 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/filters/whitespace.py @@ -0,0 +1,38 @@ +from __future__ import absolute_import, division, unicode_literals + +import re + +from . import base +from ..constants import rcdataElements, spaceCharacters +spaceCharacters = "".join(spaceCharacters) + +SPACES_REGEX = re.compile("[%s]+" % spaceCharacters) + + +class Filter(base.Filter): + + spacePreserveElements = frozenset(["pre", "textarea"] + list(rcdataElements)) + + def __iter__(self): + preserve = 0 + for token in base.Filter.__iter__(self): + type = token["type"] + if type == "StartTag" \ + and (preserve or token["name"] in self.spacePreserveElements): + preserve += 1 + + elif type == "EndTag" and preserve: + preserve -= 1 + + elif not preserve and type == "SpaceCharacters" and token["data"]: + # Test on token["data"] above to not introduce spaces where there were not + token["data"] = " " + + elif not preserve and type == "Characters": + token["data"] = collapse_spaces(token["data"]) + + yield token + + +def collapse_spaces(text): + return SPACES_REGEX.sub(' ', text) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/html5parser.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/html5parser.py new file mode 100644 index 0000000..f7043cb --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/html5parser.py @@ -0,0 +1,2733 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import with_metaclass, viewkeys, PY3 + +import types + +try: + from collections import OrderedDict +except ImportError: + from pip._vendor.ordereddict import OrderedDict + +from . import _inputstream +from . import _tokenizer + +from . import treebuilders +from .treebuilders.base import Marker + +from . import _utils +from .constants import ( + spaceCharacters, asciiUpper2Lower, + specialElements, headingElements, cdataElements, rcdataElements, + tokenTypes, tagTokenTypes, + namespaces, + htmlIntegrationPointElements, mathmlTextIntegrationPointElements, + adjustForeignAttributes as adjustForeignAttributesMap, + adjustMathMLAttributes, adjustSVGAttributes, + E, + ReparseException +) + + +def parse(doc, treebuilder="etree", namespaceHTMLElements=True, **kwargs): + """Parse a string or file-like object into a tree""" + tb = treebuilders.getTreeBuilder(treebuilder) + p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) + return p.parse(doc, **kwargs) + + +def parseFragment(doc, container="div", treebuilder="etree", namespaceHTMLElements=True, **kwargs): + tb = treebuilders.getTreeBuilder(treebuilder) + p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) + return p.parseFragment(doc, container=container, **kwargs) + + +def method_decorator_metaclass(function): + class Decorated(type): + def __new__(meta, classname, bases, classDict): + for attributeName, attribute in classDict.items(): + if isinstance(attribute, types.FunctionType): + attribute = function(attribute) + + classDict[attributeName] = attribute + return type.__new__(meta, classname, bases, classDict) + return Decorated + + +class HTMLParser(object): + """HTML parser. Generates a tree structure from a stream of (possibly + malformed) HTML""" + + def __init__(self, tree=None, strict=False, namespaceHTMLElements=True, debug=False): + """ + strict - raise an exception when a parse error is encountered + + tree - a treebuilder class controlling the type of tree that will be + returned. Built in treebuilders can be accessed through + html5lib.treebuilders.getTreeBuilder(treeType) + """ + + # Raise an exception on the first error encountered + self.strict = strict + + if tree is None: + tree = treebuilders.getTreeBuilder("etree") + self.tree = tree(namespaceHTMLElements) + self.errors = [] + + self.phases = dict([(name, cls(self, self.tree)) for name, cls in + getPhases(debug).items()]) + + def _parse(self, stream, innerHTML=False, container="div", scripting=False, **kwargs): + + self.innerHTMLMode = innerHTML + self.container = container + self.scripting = scripting + self.tokenizer = _tokenizer.HTMLTokenizer(stream, parser=self, **kwargs) + self.reset() + + try: + self.mainLoop() + except ReparseException: + self.reset() + self.mainLoop() + + def reset(self): + self.tree.reset() + self.firstStartTag = False + self.errors = [] + self.log = [] # only used with debug mode + # "quirks" / "limited quirks" / "no quirks" + self.compatMode = "no quirks" + + if self.innerHTMLMode: + self.innerHTML = self.container.lower() + + if self.innerHTML in cdataElements: + self.tokenizer.state = self.tokenizer.rcdataState + elif self.innerHTML in rcdataElements: + self.tokenizer.state = self.tokenizer.rawtextState + elif self.innerHTML == 'plaintext': + self.tokenizer.state = self.tokenizer.plaintextState + else: + # state already is data state + # self.tokenizer.state = self.tokenizer.dataState + pass + self.phase = self.phases["beforeHtml"] + self.phase.insertHtmlElement() + self.resetInsertionMode() + else: + self.innerHTML = False # pylint:disable=redefined-variable-type + self.phase = self.phases["initial"] + + self.lastPhase = None + + self.beforeRCDataPhase = None + + self.framesetOK = True + + @property + def documentEncoding(self): + """The name of the character encoding + that was used to decode the input stream, + or :obj:`None` if that is not determined yet. + + """ + if not hasattr(self, 'tokenizer'): + return None + return self.tokenizer.stream.charEncoding[0].name + + def isHTMLIntegrationPoint(self, element): + if (element.name == "annotation-xml" and + element.namespace == namespaces["mathml"]): + return ("encoding" in element.attributes and + element.attributes["encoding"].translate( + asciiUpper2Lower) in + ("text/html", "application/xhtml+xml")) + else: + return (element.namespace, element.name) in htmlIntegrationPointElements + + def isMathMLTextIntegrationPoint(self, element): + return (element.namespace, element.name) in mathmlTextIntegrationPointElements + + def mainLoop(self): + CharactersToken = tokenTypes["Characters"] + SpaceCharactersToken = tokenTypes["SpaceCharacters"] + StartTagToken = tokenTypes["StartTag"] + EndTagToken = tokenTypes["EndTag"] + CommentToken = tokenTypes["Comment"] + DoctypeToken = tokenTypes["Doctype"] + ParseErrorToken = tokenTypes["ParseError"] + + for token in self.normalizedTokens(): + prev_token = None + new_token = token + while new_token is not None: + prev_token = new_token + currentNode = self.tree.openElements[-1] if self.tree.openElements else None + currentNodeNamespace = currentNode.namespace if currentNode else None + currentNodeName = currentNode.name if currentNode else None + + type = new_token["type"] + + if type == ParseErrorToken: + self.parseError(new_token["data"], new_token.get("datavars", {})) + new_token = None + else: + if (len(self.tree.openElements) == 0 or + currentNodeNamespace == self.tree.defaultNamespace or + (self.isMathMLTextIntegrationPoint(currentNode) and + ((type == StartTagToken and + token["name"] not in frozenset(["mglyph", "malignmark"])) or + type in (CharactersToken, SpaceCharactersToken))) or + (currentNodeNamespace == namespaces["mathml"] and + currentNodeName == "annotation-xml" and + type == StartTagToken and + token["name"] == "svg") or + (self.isHTMLIntegrationPoint(currentNode) and + type in (StartTagToken, CharactersToken, SpaceCharactersToken))): + phase = self.phase + else: + phase = self.phases["inForeignContent"] + + if type == CharactersToken: + new_token = phase.processCharacters(new_token) + elif type == SpaceCharactersToken: + new_token = phase.processSpaceCharacters(new_token) + elif type == StartTagToken: + new_token = phase.processStartTag(new_token) + elif type == EndTagToken: + new_token = phase.processEndTag(new_token) + elif type == CommentToken: + new_token = phase.processComment(new_token) + elif type == DoctypeToken: + new_token = phase.processDoctype(new_token) + + if (type == StartTagToken and prev_token["selfClosing"] and + not prev_token["selfClosingAcknowledged"]): + self.parseError("non-void-element-with-trailing-solidus", + {"name": prev_token["name"]}) + + # When the loop finishes it's EOF + reprocess = True + phases = [] + while reprocess: + phases.append(self.phase) + reprocess = self.phase.processEOF() + if reprocess: + assert self.phase not in phases + + def normalizedTokens(self): + for token in self.tokenizer: + yield self.normalizeToken(token) + + def parse(self, stream, *args, **kwargs): + """Parse a HTML document into a well-formed tree + + stream - a filelike object or string containing the HTML to be parsed + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + scripting - treat noscript elements as if javascript was turned on + """ + self._parse(stream, False, None, *args, **kwargs) + return self.tree.getDocument() + + def parseFragment(self, stream, *args, **kwargs): + """Parse a HTML fragment into a well-formed tree fragment + + container - name of the element we're setting the innerHTML property + if set to None, default to 'div' + + stream - a filelike object or string containing the HTML to be parsed + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + scripting - treat noscript elements as if javascript was turned on + """ + self._parse(stream, True, *args, **kwargs) + return self.tree.getFragment() + + def parseError(self, errorcode="XXX-undefined-error", datavars=None): + # XXX The idea is to make errorcode mandatory. + if datavars is None: + datavars = {} + self.errors.append((self.tokenizer.stream.position(), errorcode, datavars)) + if self.strict: + raise ParseError(E[errorcode] % datavars) + + def normalizeToken(self, token): + """ HTML5 specific normalizations to the token stream """ + + if token["type"] == tokenTypes["StartTag"]: + raw = token["data"] + token["data"] = OrderedDict(raw) + if len(raw) > len(token["data"]): + # we had some duplicated attribute, fix so first wins + token["data"].update(raw[::-1]) + + return token + + def adjustMathMLAttributes(self, token): + adjust_attributes(token, adjustMathMLAttributes) + + def adjustSVGAttributes(self, token): + adjust_attributes(token, adjustSVGAttributes) + + def adjustForeignAttributes(self, token): + adjust_attributes(token, adjustForeignAttributesMap) + + def reparseTokenNormal(self, token): + # pylint:disable=unused-argument + self.parser.phase() + + def resetInsertionMode(self): + # The name of this method is mostly historical. (It's also used in the + # specification.) + last = False + newModes = { + "select": "inSelect", + "td": "inCell", + "th": "inCell", + "tr": "inRow", + "tbody": "inTableBody", + "thead": "inTableBody", + "tfoot": "inTableBody", + "caption": "inCaption", + "colgroup": "inColumnGroup", + "table": "inTable", + "head": "inBody", + "body": "inBody", + "frameset": "inFrameset", + "html": "beforeHead" + } + for node in self.tree.openElements[::-1]: + nodeName = node.name + new_phase = None + if node == self.tree.openElements[0]: + assert self.innerHTML + last = True + nodeName = self.innerHTML + # Check for conditions that should only happen in the innerHTML + # case + if nodeName in ("select", "colgroup", "head", "html"): + assert self.innerHTML + + if not last and node.namespace != self.tree.defaultNamespace: + continue + + if nodeName in newModes: + new_phase = self.phases[newModes[nodeName]] + break + elif last: + new_phase = self.phases["inBody"] + break + + self.phase = new_phase + + def parseRCDataRawtext(self, token, contentType): + """Generic RCDATA/RAWTEXT Parsing algorithm + contentType - RCDATA or RAWTEXT + """ + assert contentType in ("RAWTEXT", "RCDATA") + + self.tree.insertElement(token) + + if contentType == "RAWTEXT": + self.tokenizer.state = self.tokenizer.rawtextState + else: + self.tokenizer.state = self.tokenizer.rcdataState + + self.originalPhase = self.phase + + self.phase = self.phases["text"] + + +@_utils.memoize +def getPhases(debug): + def log(function): + """Logger that records which phase processes each token""" + type_names = dict((value, key) for key, value in + tokenTypes.items()) + + def wrapped(self, *args, **kwargs): + if function.__name__.startswith("process") and len(args) > 0: + token = args[0] + try: + info = {"type": type_names[token['type']]} + except: + raise + if token['type'] in tagTokenTypes: + info["name"] = token['name'] + + self.parser.log.append((self.parser.tokenizer.state.__name__, + self.parser.phase.__class__.__name__, + self.__class__.__name__, + function.__name__, + info)) + return function(self, *args, **kwargs) + else: + return function(self, *args, **kwargs) + return wrapped + + def getMetaclass(use_metaclass, metaclass_func): + if use_metaclass: + return method_decorator_metaclass(metaclass_func) + else: + return type + + # pylint:disable=unused-argument + class Phase(with_metaclass(getMetaclass(debug, log))): + """Base class for helper object that implements each phase of processing + """ + + def __init__(self, parser, tree): + self.parser = parser + self.tree = tree + + def processEOF(self): + raise NotImplementedError + + def processComment(self, token): + # For most phases the following is correct. Where it's not it will be + # overridden. + self.tree.insertComment(token, self.tree.openElements[-1]) + + def processDoctype(self, token): + self.parser.parseError("unexpected-doctype") + + def processCharacters(self, token): + self.tree.insertText(token["data"]) + + def processSpaceCharacters(self, token): + self.tree.insertText(token["data"]) + + def processStartTag(self, token): + return self.startTagHandler[token["name"]](token) + + def startTagHtml(self, token): + if not self.parser.firstStartTag and token["name"] == "html": + self.parser.parseError("non-html-root") + # XXX Need a check here to see if the first start tag token emitted is + # this token... If it's not, invoke self.parser.parseError(). + for attr, value in token["data"].items(): + if attr not in self.tree.openElements[0].attributes: + self.tree.openElements[0].attributes[attr] = value + self.parser.firstStartTag = False + + def processEndTag(self, token): + return self.endTagHandler[token["name"]](token) + + class InitialPhase(Phase): + def processSpaceCharacters(self, token): + pass + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + correct = token["correct"] + + if (name != "html" or publicId is not None or + systemId is not None and systemId != "about:legacy-compat"): + self.parser.parseError("unknown-doctype") + + if publicId is None: + publicId = "" + + self.tree.insertDoctype(token) + + if publicId != "": + publicId = publicId.translate(asciiUpper2Lower) + + if (not correct or token["name"] != "html" or + publicId.startswith( + ("+//silmaril//dtd html pro v0r11 19970101//", + "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", + "-//as//dtd html 3.0 aswedit + extensions//", + "-//ietf//dtd html 2.0 level 1//", + "-//ietf//dtd html 2.0 level 2//", + "-//ietf//dtd html 2.0 strict level 1//", + "-//ietf//dtd html 2.0 strict level 2//", + "-//ietf//dtd html 2.0 strict//", + "-//ietf//dtd html 2.0//", + "-//ietf//dtd html 2.1e//", + "-//ietf//dtd html 3.0//", + "-//ietf//dtd html 3.2 final//", + "-//ietf//dtd html 3.2//", + "-//ietf//dtd html 3//", + "-//ietf//dtd html level 0//", + "-//ietf//dtd html level 1//", + "-//ietf//dtd html level 2//", + "-//ietf//dtd html level 3//", + "-//ietf//dtd html strict level 0//", + "-//ietf//dtd html strict level 1//", + "-//ietf//dtd html strict level 2//", + "-//ietf//dtd html strict level 3//", + "-//ietf//dtd html strict//", + "-//ietf//dtd html//", + "-//metrius//dtd metrius presentational//", + "-//microsoft//dtd internet explorer 2.0 html strict//", + "-//microsoft//dtd internet explorer 2.0 html//", + "-//microsoft//dtd internet explorer 2.0 tables//", + "-//microsoft//dtd internet explorer 3.0 html strict//", + "-//microsoft//dtd internet explorer 3.0 html//", + "-//microsoft//dtd internet explorer 3.0 tables//", + "-//netscape comm. corp.//dtd html//", + "-//netscape comm. corp.//dtd strict html//", + "-//o'reilly and associates//dtd html 2.0//", + "-//o'reilly and associates//dtd html extended 1.0//", + "-//o'reilly and associates//dtd html extended relaxed 1.0//", + "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", + "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", + "-//spyglass//dtd html 2.0 extended//", + "-//sq//dtd html 2.0 hotmetal + extensions//", + "-//sun microsystems corp.//dtd hotjava html//", + "-//sun microsystems corp.//dtd hotjava strict html//", + "-//w3c//dtd html 3 1995-03-24//", + "-//w3c//dtd html 3.2 draft//", + "-//w3c//dtd html 3.2 final//", + "-//w3c//dtd html 3.2//", + "-//w3c//dtd html 3.2s draft//", + "-//w3c//dtd html 4.0 frameset//", + "-//w3c//dtd html 4.0 transitional//", + "-//w3c//dtd html experimental 19960712//", + "-//w3c//dtd html experimental 970421//", + "-//w3c//dtd w3 html//", + "-//w3o//dtd w3 html 3.0//", + "-//webtechs//dtd mozilla html 2.0//", + "-//webtechs//dtd mozilla html//")) or + publicId in ("-//w3o//dtd w3 html strict 3.0//en//", + "-/w3c/dtd html 4.0 transitional/en", + "html") or + publicId.startswith( + ("-//w3c//dtd html 4.01 frameset//", + "-//w3c//dtd html 4.01 transitional//")) and + systemId is None or + systemId and systemId.lower() == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"): + self.parser.compatMode = "quirks" + elif (publicId.startswith( + ("-//w3c//dtd xhtml 1.0 frameset//", + "-//w3c//dtd xhtml 1.0 transitional//")) or + publicId.startswith( + ("-//w3c//dtd html 4.01 frameset//", + "-//w3c//dtd html 4.01 transitional//")) and + systemId is not None): + self.parser.compatMode = "limited quirks" + + self.parser.phase = self.parser.phases["beforeHtml"] + + def anythingElse(self): + self.parser.compatMode = "quirks" + self.parser.phase = self.parser.phases["beforeHtml"] + + def processCharacters(self, token): + self.parser.parseError("expected-doctype-but-got-chars") + self.anythingElse() + return token + + def processStartTag(self, token): + self.parser.parseError("expected-doctype-but-got-start-tag", + {"name": token["name"]}) + self.anythingElse() + return token + + def processEndTag(self, token): + self.parser.parseError("expected-doctype-but-got-end-tag", + {"name": token["name"]}) + self.anythingElse() + return token + + def processEOF(self): + self.parser.parseError("expected-doctype-but-got-eof") + self.anythingElse() + return True + + class BeforeHtmlPhase(Phase): + # helper methods + def insertHtmlElement(self): + self.tree.insertRoot(impliedTagToken("html", "StartTag")) + self.parser.phase = self.parser.phases["beforeHead"] + + # other + def processEOF(self): + self.insertHtmlElement() + return True + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processSpaceCharacters(self, token): + pass + + def processCharacters(self, token): + self.insertHtmlElement() + return token + + def processStartTag(self, token): + if token["name"] == "html": + self.parser.firstStartTag = True + self.insertHtmlElement() + return token + + def processEndTag(self, token): + if token["name"] not in ("head", "body", "html", "br"): + self.parser.parseError("unexpected-end-tag-before-html", + {"name": token["name"]}) + else: + self.insertHtmlElement() + return token + + class BeforeHeadPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("head", self.startTagHead) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + (("head", "body", "html", "br"), self.endTagImplyHead) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.startTagHead(impliedTagToken("head", "StartTag")) + return True + + def processSpaceCharacters(self, token): + pass + + def processCharacters(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagHead(self, token): + self.tree.insertElement(token) + self.tree.headPointer = self.tree.openElements[-1] + self.parser.phase = self.parser.phases["inHead"] + + def startTagOther(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def endTagImplyHead(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def endTagOther(self, token): + self.parser.parseError("end-tag-after-implied-root", + {"name": token["name"]}) + + class InHeadPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("title", self.startTagTitle), + (("noframes", "style"), self.startTagNoFramesStyle), + ("noscript", self.startTagNoscript), + ("script", self.startTagScript), + (("base", "basefont", "bgsound", "command", "link"), + self.startTagBaseLinkCommand), + ("meta", self.startTagMeta), + ("head", self.startTagHead) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("head", self.endTagHead), + (("br", "html", "body"), self.endTagHtmlBodyBr) + ]) + self.endTagHandler.default = self.endTagOther + + # the real thing + def processEOF(self): + self.anythingElse() + return True + + def processCharacters(self, token): + self.anythingElse() + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagHead(self, token): + self.parser.parseError("two-heads-are-not-better-than-one") + + def startTagBaseLinkCommand(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagMeta(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + attributes = token["data"] + if self.parser.tokenizer.stream.charEncoding[1] == "tentative": + if "charset" in attributes: + self.parser.tokenizer.stream.changeEncoding(attributes["charset"]) + elif ("content" in attributes and + "http-equiv" in attributes and + attributes["http-equiv"].lower() == "content-type"): + # Encoding it as UTF-8 here is a hack, as really we should pass + # the abstract Unicode string, and just use the + # ContentAttrParser on that, but using UTF-8 allows all chars + # to be encoded and as a ASCII-superset works. + data = _inputstream.EncodingBytes(attributes["content"].encode("utf-8")) + parser = _inputstream.ContentAttrParser(data) + codec = parser.parse() + self.parser.tokenizer.stream.changeEncoding(codec) + + def startTagTitle(self, token): + self.parser.parseRCDataRawtext(token, "RCDATA") + + def startTagNoFramesStyle(self, token): + # Need to decide whether to implement the scripting-disabled case + self.parser.parseRCDataRawtext(token, "RAWTEXT") + + def startTagNoscript(self, token): + if self.parser.scripting: + self.parser.parseRCDataRawtext(token, "RAWTEXT") + else: + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inHeadNoscript"] + + def startTagScript(self, token): + self.tree.insertElement(token) + self.parser.tokenizer.state = self.parser.tokenizer.scriptDataState + self.parser.originalPhase = self.parser.phase + self.parser.phase = self.parser.phases["text"] + + def startTagOther(self, token): + self.anythingElse() + return token + + def endTagHead(self, token): + node = self.parser.tree.openElements.pop() + assert node.name == "head", "Expected head got %s" % node.name + self.parser.phase = self.parser.phases["afterHead"] + + def endTagHtmlBodyBr(self, token): + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + self.endTagHead(impliedTagToken("head")) + + class InHeadNoscriptPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("basefont", "bgsound", "link", "meta", "noframes", "style"), self.startTagBaseLinkCommand), + (("head", "noscript"), self.startTagHeadNoscript), + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("noscript", self.endTagNoscript), + ("br", self.endTagBr), + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.parser.parseError("eof-in-head-noscript") + self.anythingElse() + return True + + def processComment(self, token): + return self.parser.phases["inHead"].processComment(token) + + def processCharacters(self, token): + self.parser.parseError("char-in-head-noscript") + self.anythingElse() + return token + + def processSpaceCharacters(self, token): + return self.parser.phases["inHead"].processSpaceCharacters(token) + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagBaseLinkCommand(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagHeadNoscript(self, token): + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + + def startTagOther(self, token): + self.parser.parseError("unexpected-inhead-noscript-tag", {"name": token["name"]}) + self.anythingElse() + return token + + def endTagNoscript(self, token): + node = self.parser.tree.openElements.pop() + assert node.name == "noscript", "Expected noscript got %s" % node.name + self.parser.phase = self.parser.phases["inHead"] + + def endTagBr(self, token): + self.parser.parseError("unexpected-inhead-noscript-tag", {"name": token["name"]}) + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + # Caller must raise parse error first! + self.endTagNoscript(impliedTagToken("noscript")) + + class AfterHeadPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("body", self.startTagBody), + ("frameset", self.startTagFrameset), + (("base", "basefont", "bgsound", "link", "meta", "noframes", "script", + "style", "title"), + self.startTagFromHead), + ("head", self.startTagHead) + ]) + self.startTagHandler.default = self.startTagOther + self.endTagHandler = _utils.MethodDispatcher([(("body", "html", "br"), + self.endTagHtmlBodyBr)]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.anythingElse() + return True + + def processCharacters(self, token): + self.anythingElse() + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagBody(self, token): + self.parser.framesetOK = False + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inBody"] + + def startTagFrameset(self, token): + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inFrameset"] + + def startTagFromHead(self, token): + self.parser.parseError("unexpected-start-tag-out-of-my-head", + {"name": token["name"]}) + self.tree.openElements.append(self.tree.headPointer) + self.parser.phases["inHead"].processStartTag(token) + for node in self.tree.openElements[::-1]: + if node.name == "head": + self.tree.openElements.remove(node) + break + + def startTagHead(self, token): + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + + def startTagOther(self, token): + self.anythingElse() + return token + + def endTagHtmlBodyBr(self, token): + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + self.tree.insertElement(impliedTagToken("body", "StartTag")) + self.parser.phase = self.parser.phases["inBody"] + self.parser.framesetOK = True + + class InBodyPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#parsing-main-inbody + # the really-really-really-very crazy mode + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + # Set this to the default handler + self.processSpaceCharacters = self.processSpaceCharactersNonPre + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("base", "basefont", "bgsound", "command", "link", "meta", + "script", "style", "title"), + self.startTagProcessInHead), + ("body", self.startTagBody), + ("frameset", self.startTagFrameset), + (("address", "article", "aside", "blockquote", "center", "details", + "dir", "div", "dl", "fieldset", "figcaption", "figure", + "footer", "header", "hgroup", "main", "menu", "nav", "ol", "p", + "section", "summary", "ul"), + self.startTagCloseP), + (headingElements, self.startTagHeading), + (("pre", "listing"), self.startTagPreListing), + ("form", self.startTagForm), + (("li", "dd", "dt"), self.startTagListItem), + ("plaintext", self.startTagPlaintext), + ("a", self.startTagA), + (("b", "big", "code", "em", "font", "i", "s", "small", "strike", + "strong", "tt", "u"), self.startTagFormatting), + ("nobr", self.startTagNobr), + ("button", self.startTagButton), + (("applet", "marquee", "object"), self.startTagAppletMarqueeObject), + ("xmp", self.startTagXmp), + ("table", self.startTagTable), + (("area", "br", "embed", "img", "keygen", "wbr"), + self.startTagVoidFormatting), + (("param", "source", "track"), self.startTagParamSource), + ("input", self.startTagInput), + ("hr", self.startTagHr), + ("image", self.startTagImage), + ("isindex", self.startTagIsIndex), + ("textarea", self.startTagTextarea), + ("iframe", self.startTagIFrame), + ("noscript", self.startTagNoscript), + (("noembed", "noframes"), self.startTagRawtext), + ("select", self.startTagSelect), + (("rp", "rt"), self.startTagRpRt), + (("option", "optgroup"), self.startTagOpt), + (("math"), self.startTagMath), + (("svg"), self.startTagSvg), + (("caption", "col", "colgroup", "frame", "head", + "tbody", "td", "tfoot", "th", "thead", + "tr"), self.startTagMisplaced) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("body", self.endTagBody), + ("html", self.endTagHtml), + (("address", "article", "aside", "blockquote", "button", "center", + "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", + "footer", "header", "hgroup", "listing", "main", "menu", "nav", "ol", "pre", + "section", "summary", "ul"), self.endTagBlock), + ("form", self.endTagForm), + ("p", self.endTagP), + (("dd", "dt", "li"), self.endTagListItem), + (headingElements, self.endTagHeading), + (("a", "b", "big", "code", "em", "font", "i", "nobr", "s", "small", + "strike", "strong", "tt", "u"), self.endTagFormatting), + (("applet", "marquee", "object"), self.endTagAppletMarqueeObject), + ("br", self.endTagBr), + ]) + self.endTagHandler.default = self.endTagOther + + def isMatchingFormattingElement(self, node1, node2): + return (node1.name == node2.name and + node1.namespace == node2.namespace and + node1.attributes == node2.attributes) + + # helper + def addFormattingElement(self, token): + self.tree.insertElement(token) + element = self.tree.openElements[-1] + + matchingElements = [] + for node in self.tree.activeFormattingElements[::-1]: + if node is Marker: + break + elif self.isMatchingFormattingElement(node, element): + matchingElements.append(node) + + assert len(matchingElements) <= 3 + if len(matchingElements) == 3: + self.tree.activeFormattingElements.remove(matchingElements[-1]) + self.tree.activeFormattingElements.append(element) + + # the real deal + def processEOF(self): + allowed_elements = frozenset(("dd", "dt", "li", "p", "tbody", "td", + "tfoot", "th", "thead", "tr", "body", + "html")) + for node in self.tree.openElements[::-1]: + if node.name not in allowed_elements: + self.parser.parseError("expected-closing-tag-but-got-eof") + break + # Stop parsing + + def processSpaceCharactersDropNewline(self, token): + # Sometimes (start of <pre>, <listing>, and <textarea> blocks) we + # want to drop leading newlines + data = token["data"] + self.processSpaceCharacters = self.processSpaceCharactersNonPre + if (data.startswith("\n") and + self.tree.openElements[-1].name in ("pre", "listing", "textarea") and + not self.tree.openElements[-1].hasContent()): + data = data[1:] + if data: + self.tree.reconstructActiveFormattingElements() + self.tree.insertText(data) + + def processCharacters(self, token): + if token["data"] == "\u0000": + # The tokenizer should always emit null on its own + return + self.tree.reconstructActiveFormattingElements() + self.tree.insertText(token["data"]) + # This must be bad for performance + if (self.parser.framesetOK and + any([char not in spaceCharacters + for char in token["data"]])): + self.parser.framesetOK = False + + def processSpaceCharactersNonPre(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertText(token["data"]) + + def startTagProcessInHead(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagBody(self, token): + self.parser.parseError("unexpected-start-tag", {"name": "body"}) + if (len(self.tree.openElements) == 1 or + self.tree.openElements[1].name != "body"): + assert self.parser.innerHTML + else: + self.parser.framesetOK = False + for attr, value in token["data"].items(): + if attr not in self.tree.openElements[1].attributes: + self.tree.openElements[1].attributes[attr] = value + + def startTagFrameset(self, token): + self.parser.parseError("unexpected-start-tag", {"name": "frameset"}) + if (len(self.tree.openElements) == 1 or self.tree.openElements[1].name != "body"): + assert self.parser.innerHTML + elif not self.parser.framesetOK: + pass + else: + if self.tree.openElements[1].parent: + self.tree.openElements[1].parent.removeChild(self.tree.openElements[1]) + while self.tree.openElements[-1].name != "html": + self.tree.openElements.pop() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inFrameset"] + + def startTagCloseP(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + + def startTagPreListing(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + self.parser.framesetOK = False + self.processSpaceCharacters = self.processSpaceCharactersDropNewline + + def startTagForm(self, token): + if self.tree.formPointer: + self.parser.parseError("unexpected-start-tag", {"name": "form"}) + else: + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + self.tree.formPointer = self.tree.openElements[-1] + + def startTagListItem(self, token): + self.parser.framesetOK = False + + stopNamesMap = {"li": ["li"], + "dt": ["dt", "dd"], + "dd": ["dt", "dd"]} + stopNames = stopNamesMap[token["name"]] + for node in reversed(self.tree.openElements): + if node.name in stopNames: + self.parser.phase.processEndTag( + impliedTagToken(node.name, "EndTag")) + break + if (node.nameTuple in specialElements and + node.name not in ("address", "div", "p")): + break + + if self.tree.elementInScope("p", variant="button"): + self.parser.phase.processEndTag( + impliedTagToken("p", "EndTag")) + + self.tree.insertElement(token) + + def startTagPlaintext(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + self.parser.tokenizer.state = self.parser.tokenizer.plaintextState + + def startTagHeading(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + if self.tree.openElements[-1].name in headingElements: + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + self.tree.openElements.pop() + self.tree.insertElement(token) + + def startTagA(self, token): + afeAElement = self.tree.elementInActiveFormattingElements("a") + if afeAElement: + self.parser.parseError("unexpected-start-tag-implies-end-tag", + {"startName": "a", "endName": "a"}) + self.endTagFormatting(impliedTagToken("a")) + if afeAElement in self.tree.openElements: + self.tree.openElements.remove(afeAElement) + if afeAElement in self.tree.activeFormattingElements: + self.tree.activeFormattingElements.remove(afeAElement) + self.tree.reconstructActiveFormattingElements() + self.addFormattingElement(token) + + def startTagFormatting(self, token): + self.tree.reconstructActiveFormattingElements() + self.addFormattingElement(token) + + def startTagNobr(self, token): + self.tree.reconstructActiveFormattingElements() + if self.tree.elementInScope("nobr"): + self.parser.parseError("unexpected-start-tag-implies-end-tag", + {"startName": "nobr", "endName": "nobr"}) + self.processEndTag(impliedTagToken("nobr")) + # XXX Need tests that trigger the following + self.tree.reconstructActiveFormattingElements() + self.addFormattingElement(token) + + def startTagButton(self, token): + if self.tree.elementInScope("button"): + self.parser.parseError("unexpected-start-tag-implies-end-tag", + {"startName": "button", "endName": "button"}) + self.processEndTag(impliedTagToken("button")) + return token + else: + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + self.parser.framesetOK = False + + def startTagAppletMarqueeObject(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + self.tree.activeFormattingElements.append(Marker) + self.parser.framesetOK = False + + def startTagXmp(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.reconstructActiveFormattingElements() + self.parser.framesetOK = False + self.parser.parseRCDataRawtext(token, "RAWTEXT") + + def startTagTable(self, token): + if self.parser.compatMode != "quirks": + if self.tree.elementInScope("p", variant="button"): + self.processEndTag(impliedTagToken("p")) + self.tree.insertElement(token) + self.parser.framesetOK = False + self.parser.phase = self.parser.phases["inTable"] + + def startTagVoidFormatting(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + self.parser.framesetOK = False + + def startTagInput(self, token): + framesetOK = self.parser.framesetOK + self.startTagVoidFormatting(token) + if ("type" in token["data"] and + token["data"]["type"].translate(asciiUpper2Lower) == "hidden"): + # input type=hidden doesn't change framesetOK + self.parser.framesetOK = framesetOK + + def startTagParamSource(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagHr(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + self.parser.framesetOK = False + + def startTagImage(self, token): + # No really... + self.parser.parseError("unexpected-start-tag-treated-as", + {"originalName": "image", "newName": "img"}) + self.processStartTag(impliedTagToken("img", "StartTag", + attributes=token["data"], + selfClosing=token["selfClosing"])) + + def startTagIsIndex(self, token): + self.parser.parseError("deprecated-tag", {"name": "isindex"}) + if self.tree.formPointer: + return + form_attrs = {} + if "action" in token["data"]: + form_attrs["action"] = token["data"]["action"] + self.processStartTag(impliedTagToken("form", "StartTag", + attributes=form_attrs)) + self.processStartTag(impliedTagToken("hr", "StartTag")) + self.processStartTag(impliedTagToken("label", "StartTag")) + # XXX Localization ... + if "prompt" in token["data"]: + prompt = token["data"]["prompt"] + else: + prompt = "This is a searchable index. Enter search keywords: " + self.processCharacters( + {"type": tokenTypes["Characters"], "data": prompt}) + attributes = token["data"].copy() + if "action" in attributes: + del attributes["action"] + if "prompt" in attributes: + del attributes["prompt"] + attributes["name"] = "isindex" + self.processStartTag(impliedTagToken("input", "StartTag", + attributes=attributes, + selfClosing=token["selfClosing"])) + self.processEndTag(impliedTagToken("label")) + self.processStartTag(impliedTagToken("hr", "StartTag")) + self.processEndTag(impliedTagToken("form")) + + def startTagTextarea(self, token): + self.tree.insertElement(token) + self.parser.tokenizer.state = self.parser.tokenizer.rcdataState + self.processSpaceCharacters = self.processSpaceCharactersDropNewline + self.parser.framesetOK = False + + def startTagIFrame(self, token): + self.parser.framesetOK = False + self.startTagRawtext(token) + + def startTagNoscript(self, token): + if self.parser.scripting: + self.startTagRawtext(token) + else: + self.startTagOther(token) + + def startTagRawtext(self, token): + """iframe, noembed noframes, noscript(if scripting enabled)""" + self.parser.parseRCDataRawtext(token, "RAWTEXT") + + def startTagOpt(self, token): + if self.tree.openElements[-1].name == "option": + self.parser.phase.processEndTag(impliedTagToken("option")) + self.tree.reconstructActiveFormattingElements() + self.parser.tree.insertElement(token) + + def startTagSelect(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + self.parser.framesetOK = False + if self.parser.phase in (self.parser.phases["inTable"], + self.parser.phases["inCaption"], + self.parser.phases["inColumnGroup"], + self.parser.phases["inTableBody"], + self.parser.phases["inRow"], + self.parser.phases["inCell"]): + self.parser.phase = self.parser.phases["inSelectInTable"] + else: + self.parser.phase = self.parser.phases["inSelect"] + + def startTagRpRt(self, token): + if self.tree.elementInScope("ruby"): + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != "ruby": + self.parser.parseError() + self.tree.insertElement(token) + + def startTagMath(self, token): + self.tree.reconstructActiveFormattingElements() + self.parser.adjustMathMLAttributes(token) + self.parser.adjustForeignAttributes(token) + token["namespace"] = namespaces["mathml"] + self.tree.insertElement(token) + # Need to get the parse error right for the case where the token + # has a namespace not equal to the xmlns attribute + if token["selfClosing"]: + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagSvg(self, token): + self.tree.reconstructActiveFormattingElements() + self.parser.adjustSVGAttributes(token) + self.parser.adjustForeignAttributes(token) + token["namespace"] = namespaces["svg"] + self.tree.insertElement(token) + # Need to get the parse error right for the case where the token + # has a namespace not equal to the xmlns attribute + if token["selfClosing"]: + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagMisplaced(self, token): + """ Elements that should be children of other elements that have a + different insertion mode; here they are ignored + "caption", "col", "colgroup", "frame", "frameset", "head", + "option", "optgroup", "tbody", "td", "tfoot", "th", "thead", + "tr", "noscript" + """ + self.parser.parseError("unexpected-start-tag-ignored", {"name": token["name"]}) + + def startTagOther(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + + def endTagP(self, token): + if not self.tree.elementInScope("p", variant="button"): + self.startTagCloseP(impliedTagToken("p", "StartTag")) + self.parser.parseError("unexpected-end-tag", {"name": "p"}) + self.endTagP(impliedTagToken("p", "EndTag")) + else: + self.tree.generateImpliedEndTags("p") + if self.tree.openElements[-1].name != "p": + self.parser.parseError("unexpected-end-tag", {"name": "p"}) + node = self.tree.openElements.pop() + while node.name != "p": + node = self.tree.openElements.pop() + + def endTagBody(self, token): + if not self.tree.elementInScope("body"): + self.parser.parseError() + return + elif self.tree.openElements[-1].name != "body": + for node in self.tree.openElements[2:]: + if node.name not in frozenset(("dd", "dt", "li", "optgroup", + "option", "p", "rp", "rt", + "tbody", "td", "tfoot", + "th", "thead", "tr", "body", + "html")): + # Not sure this is the correct name for the parse error + self.parser.parseError( + "expected-one-end-tag-but-got-another", + {"gotName": "body", "expectedName": node.name}) + break + self.parser.phase = self.parser.phases["afterBody"] + + def endTagHtml(self, token): + # We repeat the test for the body end tag token being ignored here + if self.tree.elementInScope("body"): + self.endTagBody(impliedTagToken("body")) + return token + + def endTagBlock(self, token): + # Put us back in the right whitespace handling mode + if token["name"] == "pre": + self.processSpaceCharacters = self.processSpaceCharactersNonPre + inScope = self.tree.elementInScope(token["name"]) + if inScope: + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("end-tag-too-early", {"name": token["name"]}) + if inScope: + node = self.tree.openElements.pop() + while node.name != token["name"]: + node = self.tree.openElements.pop() + + def endTagForm(self, token): + node = self.tree.formPointer + self.tree.formPointer = None + if node is None or not self.tree.elementInScope(node): + self.parser.parseError("unexpected-end-tag", + {"name": "form"}) + else: + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1] != node: + self.parser.parseError("end-tag-too-early-ignored", + {"name": "form"}) + self.tree.openElements.remove(node) + + def endTagListItem(self, token): + if token["name"] == "li": + variant = "list" + else: + variant = None + if not self.tree.elementInScope(token["name"], variant=variant): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + else: + self.tree.generateImpliedEndTags(exclude=token["name"]) + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError( + "end-tag-too-early", + {"name": token["name"]}) + node = self.tree.openElements.pop() + while node.name != token["name"]: + node = self.tree.openElements.pop() + + def endTagHeading(self, token): + for item in headingElements: + if self.tree.elementInScope(item): + self.tree.generateImpliedEndTags() + break + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("end-tag-too-early", {"name": token["name"]}) + + for item in headingElements: + if self.tree.elementInScope(item): + item = self.tree.openElements.pop() + while item.name not in headingElements: + item = self.tree.openElements.pop() + break + + def endTagFormatting(self, token): + """The much-feared adoption agency algorithm""" + # http://svn.whatwg.org/webapps/complete.html#adoptionAgency revision 7867 + # XXX Better parseError messages appreciated. + + # Step 1 + outerLoopCounter = 0 + + # Step 2 + while outerLoopCounter < 8: + + # Step 3 + outerLoopCounter += 1 + + # Step 4: + + # Let the formatting element be the last element in + # the list of active formatting elements that: + # - is between the end of the list and the last scope + # marker in the list, if any, or the start of the list + # otherwise, and + # - has the same tag name as the token. + formattingElement = self.tree.elementInActiveFormattingElements( + token["name"]) + if (not formattingElement or + (formattingElement in self.tree.openElements and + not self.tree.elementInScope(formattingElement.name))): + # If there is no such node, then abort these steps + # and instead act as described in the "any other + # end tag" entry below. + self.endTagOther(token) + return + + # Otherwise, if there is such a node, but that node is + # not in the stack of open elements, then this is a + # parse error; remove the element from the list, and + # abort these steps. + elif formattingElement not in self.tree.openElements: + self.parser.parseError("adoption-agency-1.2", {"name": token["name"]}) + self.tree.activeFormattingElements.remove(formattingElement) + return + + # Otherwise, if there is such a node, and that node is + # also in the stack of open elements, but the element + # is not in scope, then this is a parse error; ignore + # the token, and abort these steps. + elif not self.tree.elementInScope(formattingElement.name): + self.parser.parseError("adoption-agency-4.4", {"name": token["name"]}) + return + + # Otherwise, there is a formatting element and that + # element is in the stack and is in scope. If the + # element is not the current node, this is a parse + # error. In any case, proceed with the algorithm as + # written in the following steps. + else: + if formattingElement != self.tree.openElements[-1]: + self.parser.parseError("adoption-agency-1.3", {"name": token["name"]}) + + # Step 5: + + # Let the furthest block be the topmost node in the + # stack of open elements that is lower in the stack + # than the formatting element, and is an element in + # the special category. There might not be one. + afeIndex = self.tree.openElements.index(formattingElement) + furthestBlock = None + for element in self.tree.openElements[afeIndex:]: + if element.nameTuple in specialElements: + furthestBlock = element + break + + # Step 6: + + # If there is no furthest block, then the UA must + # first pop all the nodes from the bottom of the stack + # of open elements, from the current node up to and + # including the formatting element, then remove the + # formatting element from the list of active + # formatting elements, and finally abort these steps. + if furthestBlock is None: + element = self.tree.openElements.pop() + while element != formattingElement: + element = self.tree.openElements.pop() + self.tree.activeFormattingElements.remove(element) + return + + # Step 7 + commonAncestor = self.tree.openElements[afeIndex - 1] + + # Step 8: + # The bookmark is supposed to help us identify where to reinsert + # nodes in step 15. We have to ensure that we reinsert nodes after + # the node before the active formatting element. Note the bookmark + # can move in step 9.7 + bookmark = self.tree.activeFormattingElements.index(formattingElement) + + # Step 9 + lastNode = node = furthestBlock + innerLoopCounter = 0 + + index = self.tree.openElements.index(node) + while innerLoopCounter < 3: + innerLoopCounter += 1 + # Node is element before node in open elements + index -= 1 + node = self.tree.openElements[index] + if node not in self.tree.activeFormattingElements: + self.tree.openElements.remove(node) + continue + # Step 9.6 + if node == formattingElement: + break + # Step 9.7 + if lastNode == furthestBlock: + bookmark = self.tree.activeFormattingElements.index(node) + 1 + # Step 9.8 + clone = node.cloneNode() + # Replace node with clone + self.tree.activeFormattingElements[ + self.tree.activeFormattingElements.index(node)] = clone + self.tree.openElements[ + self.tree.openElements.index(node)] = clone + node = clone + # Step 9.9 + # Remove lastNode from its parents, if any + if lastNode.parent: + lastNode.parent.removeChild(lastNode) + node.appendChild(lastNode) + # Step 9.10 + lastNode = node + + # Step 10 + # Foster parent lastNode if commonAncestor is a + # table, tbody, tfoot, thead, or tr we need to foster + # parent the lastNode + if lastNode.parent: + lastNode.parent.removeChild(lastNode) + + if commonAncestor.name in frozenset(("table", "tbody", "tfoot", "thead", "tr")): + parent, insertBefore = self.tree.getTableMisnestedNodePosition() + parent.insertBefore(lastNode, insertBefore) + else: + commonAncestor.appendChild(lastNode) + + # Step 11 + clone = formattingElement.cloneNode() + + # Step 12 + furthestBlock.reparentChildren(clone) + + # Step 13 + furthestBlock.appendChild(clone) + + # Step 14 + self.tree.activeFormattingElements.remove(formattingElement) + self.tree.activeFormattingElements.insert(bookmark, clone) + + # Step 15 + self.tree.openElements.remove(formattingElement) + self.tree.openElements.insert( + self.tree.openElements.index(furthestBlock) + 1, clone) + + def endTagAppletMarqueeObject(self, token): + if self.tree.elementInScope(token["name"]): + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("end-tag-too-early", {"name": token["name"]}) + + if self.tree.elementInScope(token["name"]): + element = self.tree.openElements.pop() + while element.name != token["name"]: + element = self.tree.openElements.pop() + self.tree.clearActiveFormattingElements() + + def endTagBr(self, token): + self.parser.parseError("unexpected-end-tag-treated-as", + {"originalName": "br", "newName": "br element"}) + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(impliedTagToken("br", "StartTag")) + self.tree.openElements.pop() + + def endTagOther(self, token): + for node in self.tree.openElements[::-1]: + if node.name == token["name"]: + self.tree.generateImpliedEndTags(exclude=token["name"]) + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + while self.tree.openElements.pop() != node: + pass + break + else: + if node.nameTuple in specialElements: + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + break + + class TextPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([]) + self.startTagHandler.default = self.startTagOther + self.endTagHandler = _utils.MethodDispatcher([ + ("script", self.endTagScript)]) + self.endTagHandler.default = self.endTagOther + + def processCharacters(self, token): + self.tree.insertText(token["data"]) + + def processEOF(self): + self.parser.parseError("expected-named-closing-tag-but-got-eof", + {"name": self.tree.openElements[-1].name}) + self.tree.openElements.pop() + self.parser.phase = self.parser.originalPhase + return True + + def startTagOther(self, token): + assert False, "Tried to process start tag %s in RCDATA/RAWTEXT mode" % token['name'] + + def endTagScript(self, token): + node = self.tree.openElements.pop() + assert node.name == "script" + self.parser.phase = self.parser.originalPhase + # The rest of this method is all stuff that only happens if + # document.write works + + def endTagOther(self, token): + self.tree.openElements.pop() + self.parser.phase = self.parser.originalPhase + + class InTablePhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-table + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("caption", self.startTagCaption), + ("colgroup", self.startTagColgroup), + ("col", self.startTagCol), + (("tbody", "tfoot", "thead"), self.startTagRowGroup), + (("td", "th", "tr"), self.startTagImplyTbody), + ("table", self.startTagTable), + (("style", "script"), self.startTagStyleScript), + ("input", self.startTagInput), + ("form", self.startTagForm) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("table", self.endTagTable), + (("body", "caption", "col", "colgroup", "html", "tbody", "td", + "tfoot", "th", "thead", "tr"), self.endTagIgnore) + ]) + self.endTagHandler.default = self.endTagOther + + # helper methods + def clearStackToTableContext(self): + # "clear the stack back to a table context" + while self.tree.openElements[-1].name not in ("table", "html"): + # self.parser.parseError("unexpected-implied-end-tag-in-table", + # {"name": self.tree.openElements[-1].name}) + self.tree.openElements.pop() + # When the current node is <html> it's an innerHTML case + + # processing methods + def processEOF(self): + if self.tree.openElements[-1].name != "html": + self.parser.parseError("eof-in-table") + else: + assert self.parser.innerHTML + # Stop parsing + + def processSpaceCharacters(self, token): + originalPhase = self.parser.phase + self.parser.phase = self.parser.phases["inTableText"] + self.parser.phase.originalPhase = originalPhase + self.parser.phase.processSpaceCharacters(token) + + def processCharacters(self, token): + originalPhase = self.parser.phase + self.parser.phase = self.parser.phases["inTableText"] + self.parser.phase.originalPhase = originalPhase + self.parser.phase.processCharacters(token) + + def insertText(self, token): + # If we get here there must be at least one non-whitespace character + # Do the table magic! + self.tree.insertFromTable = True + self.parser.phases["inBody"].processCharacters(token) + self.tree.insertFromTable = False + + def startTagCaption(self, token): + self.clearStackToTableContext() + self.tree.activeFormattingElements.append(Marker) + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inCaption"] + + def startTagColgroup(self, token): + self.clearStackToTableContext() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inColumnGroup"] + + def startTagCol(self, token): + self.startTagColgroup(impliedTagToken("colgroup", "StartTag")) + return token + + def startTagRowGroup(self, token): + self.clearStackToTableContext() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inTableBody"] + + def startTagImplyTbody(self, token): + self.startTagRowGroup(impliedTagToken("tbody", "StartTag")) + return token + + def startTagTable(self, token): + self.parser.parseError("unexpected-start-tag-implies-end-tag", + {"startName": "table", "endName": "table"}) + self.parser.phase.processEndTag(impliedTagToken("table")) + if not self.parser.innerHTML: + return token + + def startTagStyleScript(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagInput(self, token): + if ("type" in token["data"] and + token["data"]["type"].translate(asciiUpper2Lower) == "hidden"): + self.parser.parseError("unexpected-hidden-input-in-table") + self.tree.insertElement(token) + # XXX associate with form + self.tree.openElements.pop() + else: + self.startTagOther(token) + + def startTagForm(self, token): + self.parser.parseError("unexpected-form-in-table") + if self.tree.formPointer is None: + self.tree.insertElement(token) + self.tree.formPointer = self.tree.openElements[-1] + self.tree.openElements.pop() + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-implies-table-voodoo", {"name": token["name"]}) + # Do the table magic! + self.tree.insertFromTable = True + self.parser.phases["inBody"].processStartTag(token) + self.tree.insertFromTable = False + + def endTagTable(self, token): + if self.tree.elementInScope("table", variant="table"): + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != "table": + self.parser.parseError("end-tag-too-early-named", + {"gotName": "table", + "expectedName": self.tree.openElements[-1].name}) + while self.tree.openElements[-1].name != "table": + self.tree.openElements.pop() + self.tree.openElements.pop() + self.parser.resetInsertionMode() + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-implies-table-voodoo", {"name": token["name"]}) + # Do the table magic! + self.tree.insertFromTable = True + self.parser.phases["inBody"].processEndTag(token) + self.tree.insertFromTable = False + + class InTableTextPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.originalPhase = None + self.characterTokens = [] + + def flushCharacters(self): + data = "".join([item["data"] for item in self.characterTokens]) + if any([item not in spaceCharacters for item in data]): + token = {"type": tokenTypes["Characters"], "data": data} + self.parser.phases["inTable"].insertText(token) + elif data: + self.tree.insertText(data) + self.characterTokens = [] + + def processComment(self, token): + self.flushCharacters() + self.parser.phase = self.originalPhase + return token + + def processEOF(self): + self.flushCharacters() + self.parser.phase = self.originalPhase + return True + + def processCharacters(self, token): + if token["data"] == "\u0000": + return + self.characterTokens.append(token) + + def processSpaceCharacters(self, token): + # pretty sure we should never reach here + self.characterTokens.append(token) + # assert False + + def processStartTag(self, token): + self.flushCharacters() + self.parser.phase = self.originalPhase + return token + + def processEndTag(self, token): + self.flushCharacters() + self.parser.phase = self.originalPhase + return token + + class InCaptionPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-caption + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", + "thead", "tr"), self.startTagTableElement) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("caption", self.endTagCaption), + ("table", self.endTagTable), + (("body", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", + "thead", "tr"), self.endTagIgnore) + ]) + self.endTagHandler.default = self.endTagOther + + def ignoreEndTagCaption(self): + return not self.tree.elementInScope("caption", variant="table") + + def processEOF(self): + self.parser.phases["inBody"].processEOF() + + def processCharacters(self, token): + return self.parser.phases["inBody"].processCharacters(token) + + def startTagTableElement(self, token): + self.parser.parseError() + # XXX Have to duplicate logic here to find out if the tag is ignored + ignoreEndTag = self.ignoreEndTagCaption() + self.parser.phase.processEndTag(impliedTagToken("caption")) + if not ignoreEndTag: + return token + + def startTagOther(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def endTagCaption(self, token): + if not self.ignoreEndTagCaption(): + # AT this code is quite similar to endTagTable in "InTable" + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != "caption": + self.parser.parseError("expected-one-end-tag-but-got-another", + {"gotName": "caption", + "expectedName": self.tree.openElements[-1].name}) + while self.tree.openElements[-1].name != "caption": + self.tree.openElements.pop() + self.tree.openElements.pop() + self.tree.clearActiveFormattingElements() + self.parser.phase = self.parser.phases["inTable"] + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagTable(self, token): + self.parser.parseError() + ignoreEndTag = self.ignoreEndTagCaption() + self.parser.phase.processEndTag(impliedTagToken("caption")) + if not ignoreEndTag: + return token + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def endTagOther(self, token): + return self.parser.phases["inBody"].processEndTag(token) + + class InColumnGroupPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-column + + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("col", self.startTagCol) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("colgroup", self.endTagColgroup), + ("col", self.endTagCol) + ]) + self.endTagHandler.default = self.endTagOther + + def ignoreEndTagColgroup(self): + return self.tree.openElements[-1].name == "html" + + def processEOF(self): + if self.tree.openElements[-1].name == "html": + assert self.parser.innerHTML + return + else: + ignoreEndTag = self.ignoreEndTagColgroup() + self.endTagColgroup(impliedTagToken("colgroup")) + if not ignoreEndTag: + return True + + def processCharacters(self, token): + ignoreEndTag = self.ignoreEndTagColgroup() + self.endTagColgroup(impliedTagToken("colgroup")) + if not ignoreEndTag: + return token + + def startTagCol(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagOther(self, token): + ignoreEndTag = self.ignoreEndTagColgroup() + self.endTagColgroup(impliedTagToken("colgroup")) + if not ignoreEndTag: + return token + + def endTagColgroup(self, token): + if self.ignoreEndTagColgroup(): + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + else: + self.tree.openElements.pop() + self.parser.phase = self.parser.phases["inTable"] + + def endTagCol(self, token): + self.parser.parseError("no-end-tag", {"name": "col"}) + + def endTagOther(self, token): + ignoreEndTag = self.ignoreEndTagColgroup() + self.endTagColgroup(impliedTagToken("colgroup")) + if not ignoreEndTag: + return token + + class InTableBodyPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-table0 + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("tr", self.startTagTr), + (("td", "th"), self.startTagTableCell), + (("caption", "col", "colgroup", "tbody", "tfoot", "thead"), + self.startTagTableOther) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + (("tbody", "tfoot", "thead"), self.endTagTableRowGroup), + ("table", self.endTagTable), + (("body", "caption", "col", "colgroup", "html", "td", "th", + "tr"), self.endTagIgnore) + ]) + self.endTagHandler.default = self.endTagOther + + # helper methods + def clearStackToTableBodyContext(self): + while self.tree.openElements[-1].name not in ("tbody", "tfoot", + "thead", "html"): + # self.parser.parseError("unexpected-implied-end-tag-in-table", + # {"name": self.tree.openElements[-1].name}) + self.tree.openElements.pop() + if self.tree.openElements[-1].name == "html": + assert self.parser.innerHTML + + # the rest + def processEOF(self): + self.parser.phases["inTable"].processEOF() + + def processSpaceCharacters(self, token): + return self.parser.phases["inTable"].processSpaceCharacters(token) + + def processCharacters(self, token): + return self.parser.phases["inTable"].processCharacters(token) + + def startTagTr(self, token): + self.clearStackToTableBodyContext() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inRow"] + + def startTagTableCell(self, token): + self.parser.parseError("unexpected-cell-in-table-body", + {"name": token["name"]}) + self.startTagTr(impliedTagToken("tr", "StartTag")) + return token + + def startTagTableOther(self, token): + # XXX AT Any ideas on how to share this with endTagTable? + if (self.tree.elementInScope("tbody", variant="table") or + self.tree.elementInScope("thead", variant="table") or + self.tree.elementInScope("tfoot", variant="table")): + self.clearStackToTableBodyContext() + self.endTagTableRowGroup( + impliedTagToken(self.tree.openElements[-1].name)) + return token + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def startTagOther(self, token): + return self.parser.phases["inTable"].processStartTag(token) + + def endTagTableRowGroup(self, token): + if self.tree.elementInScope(token["name"], variant="table"): + self.clearStackToTableBodyContext() + self.tree.openElements.pop() + self.parser.phase = self.parser.phases["inTable"] + else: + self.parser.parseError("unexpected-end-tag-in-table-body", + {"name": token["name"]}) + + def endTagTable(self, token): + if (self.tree.elementInScope("tbody", variant="table") or + self.tree.elementInScope("thead", variant="table") or + self.tree.elementInScope("tfoot", variant="table")): + self.clearStackToTableBodyContext() + self.endTagTableRowGroup( + impliedTagToken(self.tree.openElements[-1].name)) + return token + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag-in-table-body", + {"name": token["name"]}) + + def endTagOther(self, token): + return self.parser.phases["inTable"].processEndTag(token) + + class InRowPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-row + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("td", "th"), self.startTagTableCell), + (("caption", "col", "colgroup", "tbody", "tfoot", "thead", + "tr"), self.startTagTableOther) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("tr", self.endTagTr), + ("table", self.endTagTable), + (("tbody", "tfoot", "thead"), self.endTagTableRowGroup), + (("body", "caption", "col", "colgroup", "html", "td", "th"), + self.endTagIgnore) + ]) + self.endTagHandler.default = self.endTagOther + + # helper methods (XXX unify this with other table helper methods) + def clearStackToTableRowContext(self): + while self.tree.openElements[-1].name not in ("tr", "html"): + self.parser.parseError("unexpected-implied-end-tag-in-table-row", + {"name": self.tree.openElements[-1].name}) + self.tree.openElements.pop() + + def ignoreEndTagTr(self): + return not self.tree.elementInScope("tr", variant="table") + + # the rest + def processEOF(self): + self.parser.phases["inTable"].processEOF() + + def processSpaceCharacters(self, token): + return self.parser.phases["inTable"].processSpaceCharacters(token) + + def processCharacters(self, token): + return self.parser.phases["inTable"].processCharacters(token) + + def startTagTableCell(self, token): + self.clearStackToTableRowContext() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inCell"] + self.tree.activeFormattingElements.append(Marker) + + def startTagTableOther(self, token): + ignoreEndTag = self.ignoreEndTagTr() + self.endTagTr(impliedTagToken("tr")) + # XXX how are we sure it's always ignored in the innerHTML case? + if not ignoreEndTag: + return token + + def startTagOther(self, token): + return self.parser.phases["inTable"].processStartTag(token) + + def endTagTr(self, token): + if not self.ignoreEndTagTr(): + self.clearStackToTableRowContext() + self.tree.openElements.pop() + self.parser.phase = self.parser.phases["inTableBody"] + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagTable(self, token): + ignoreEndTag = self.ignoreEndTagTr() + self.endTagTr(impliedTagToken("tr")) + # Reprocess the current tag if the tr end tag was not ignored + # XXX how are we sure it's always ignored in the innerHTML case? + if not ignoreEndTag: + return token + + def endTagTableRowGroup(self, token): + if self.tree.elementInScope(token["name"], variant="table"): + self.endTagTr(impliedTagToken("tr")) + return token + else: + self.parser.parseError() + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag-in-table-row", + {"name": token["name"]}) + + def endTagOther(self, token): + return self.parser.phases["inTable"].processEndTag(token) + + class InCellPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-cell + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", + "thead", "tr"), self.startTagTableOther) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + (("td", "th"), self.endTagTableCell), + (("body", "caption", "col", "colgroup", "html"), self.endTagIgnore), + (("table", "tbody", "tfoot", "thead", "tr"), self.endTagImply) + ]) + self.endTagHandler.default = self.endTagOther + + # helper + def closeCell(self): + if self.tree.elementInScope("td", variant="table"): + self.endTagTableCell(impliedTagToken("td")) + elif self.tree.elementInScope("th", variant="table"): + self.endTagTableCell(impliedTagToken("th")) + + # the rest + def processEOF(self): + self.parser.phases["inBody"].processEOF() + + def processCharacters(self, token): + return self.parser.phases["inBody"].processCharacters(token) + + def startTagTableOther(self, token): + if (self.tree.elementInScope("td", variant="table") or + self.tree.elementInScope("th", variant="table")): + self.closeCell() + return token + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def startTagOther(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def endTagTableCell(self, token): + if self.tree.elementInScope(token["name"], variant="table"): + self.tree.generateImpliedEndTags(token["name"]) + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("unexpected-cell-end-tag", + {"name": token["name"]}) + while True: + node = self.tree.openElements.pop() + if node.name == token["name"]: + break + else: + self.tree.openElements.pop() + self.tree.clearActiveFormattingElements() + self.parser.phase = self.parser.phases["inRow"] + else: + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def endTagImply(self, token): + if self.tree.elementInScope(token["name"], variant="table"): + self.closeCell() + return token + else: + # sometimes innerHTML case + self.parser.parseError() + + def endTagOther(self, token): + return self.parser.phases["inBody"].processEndTag(token) + + class InSelectPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("option", self.startTagOption), + ("optgroup", self.startTagOptgroup), + ("select", self.startTagSelect), + (("input", "keygen", "textarea"), self.startTagInput), + ("script", self.startTagScript) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("option", self.endTagOption), + ("optgroup", self.endTagOptgroup), + ("select", self.endTagSelect) + ]) + self.endTagHandler.default = self.endTagOther + + # http://www.whatwg.org/specs/web-apps/current-work/#in-select + def processEOF(self): + if self.tree.openElements[-1].name != "html": + self.parser.parseError("eof-in-select") + else: + assert self.parser.innerHTML + + def processCharacters(self, token): + if token["data"] == "\u0000": + return + self.tree.insertText(token["data"]) + + def startTagOption(self, token): + # We need to imply </option> if <option> is the current node. + if self.tree.openElements[-1].name == "option": + self.tree.openElements.pop() + self.tree.insertElement(token) + + def startTagOptgroup(self, token): + if self.tree.openElements[-1].name == "option": + self.tree.openElements.pop() + if self.tree.openElements[-1].name == "optgroup": + self.tree.openElements.pop() + self.tree.insertElement(token) + + def startTagSelect(self, token): + self.parser.parseError("unexpected-select-in-select") + self.endTagSelect(impliedTagToken("select")) + + def startTagInput(self, token): + self.parser.parseError("unexpected-input-in-select") + if self.tree.elementInScope("select", variant="select"): + self.endTagSelect(impliedTagToken("select")) + return token + else: + assert self.parser.innerHTML + + def startTagScript(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-in-select", + {"name": token["name"]}) + + def endTagOption(self, token): + if self.tree.openElements[-1].name == "option": + self.tree.openElements.pop() + else: + self.parser.parseError("unexpected-end-tag-in-select", + {"name": "option"}) + + def endTagOptgroup(self, token): + # </optgroup> implicitly closes <option> + if (self.tree.openElements[-1].name == "option" and + self.tree.openElements[-2].name == "optgroup"): + self.tree.openElements.pop() + # It also closes </optgroup> + if self.tree.openElements[-1].name == "optgroup": + self.tree.openElements.pop() + # But nothing else + else: + self.parser.parseError("unexpected-end-tag-in-select", + {"name": "optgroup"}) + + def endTagSelect(self, token): + if self.tree.elementInScope("select", variant="select"): + node = self.tree.openElements.pop() + while node.name != "select": + node = self.tree.openElements.pop() + self.parser.resetInsertionMode() + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-in-select", + {"name": token["name"]}) + + class InSelectInTablePhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), + self.startTagTable) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), + self.endTagTable) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.parser.phases["inSelect"].processEOF() + + def processCharacters(self, token): + return self.parser.phases["inSelect"].processCharacters(token) + + def startTagTable(self, token): + self.parser.parseError("unexpected-table-element-start-tag-in-select-in-table", {"name": token["name"]}) + self.endTagOther(impliedTagToken("select")) + return token + + def startTagOther(self, token): + return self.parser.phases["inSelect"].processStartTag(token) + + def endTagTable(self, token): + self.parser.parseError("unexpected-table-element-end-tag-in-select-in-table", {"name": token["name"]}) + if self.tree.elementInScope(token["name"], variant="table"): + self.endTagOther(impliedTagToken("select")) + return token + + def endTagOther(self, token): + return self.parser.phases["inSelect"].processEndTag(token) + + class InForeignContentPhase(Phase): + breakoutElements = frozenset(["b", "big", "blockquote", "body", "br", + "center", "code", "dd", "div", "dl", "dt", + "em", "embed", "h1", "h2", "h3", + "h4", "h5", "h6", "head", "hr", "i", "img", + "li", "listing", "menu", "meta", "nobr", + "ol", "p", "pre", "ruby", "s", "small", + "span", "strong", "strike", "sub", "sup", + "table", "tt", "u", "ul", "var"]) + + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + def adjustSVGTagNames(self, token): + replacements = {"altglyph": "altGlyph", + "altglyphdef": "altGlyphDef", + "altglyphitem": "altGlyphItem", + "animatecolor": "animateColor", + "animatemotion": "animateMotion", + "animatetransform": "animateTransform", + "clippath": "clipPath", + "feblend": "feBlend", + "fecolormatrix": "feColorMatrix", + "fecomponenttransfer": "feComponentTransfer", + "fecomposite": "feComposite", + "feconvolvematrix": "feConvolveMatrix", + "fediffuselighting": "feDiffuseLighting", + "fedisplacementmap": "feDisplacementMap", + "fedistantlight": "feDistantLight", + "feflood": "feFlood", + "fefunca": "feFuncA", + "fefuncb": "feFuncB", + "fefuncg": "feFuncG", + "fefuncr": "feFuncR", + "fegaussianblur": "feGaussianBlur", + "feimage": "feImage", + "femerge": "feMerge", + "femergenode": "feMergeNode", + "femorphology": "feMorphology", + "feoffset": "feOffset", + "fepointlight": "fePointLight", + "fespecularlighting": "feSpecularLighting", + "fespotlight": "feSpotLight", + "fetile": "feTile", + "feturbulence": "feTurbulence", + "foreignobject": "foreignObject", + "glyphref": "glyphRef", + "lineargradient": "linearGradient", + "radialgradient": "radialGradient", + "textpath": "textPath"} + + if token["name"] in replacements: + token["name"] = replacements[token["name"]] + + def processCharacters(self, token): + if token["data"] == "\u0000": + token["data"] = "\uFFFD" + elif (self.parser.framesetOK and + any(char not in spaceCharacters for char in token["data"])): + self.parser.framesetOK = False + Phase.processCharacters(self, token) + + def processStartTag(self, token): + currentNode = self.tree.openElements[-1] + if (token["name"] in self.breakoutElements or + (token["name"] == "font" and + set(token["data"].keys()) & set(["color", "face", "size"]))): + self.parser.parseError("unexpected-html-element-in-foreign-content", + {"name": token["name"]}) + while (self.tree.openElements[-1].namespace != + self.tree.defaultNamespace and + not self.parser.isHTMLIntegrationPoint(self.tree.openElements[-1]) and + not self.parser.isMathMLTextIntegrationPoint(self.tree.openElements[-1])): + self.tree.openElements.pop() + return token + + else: + if currentNode.namespace == namespaces["mathml"]: + self.parser.adjustMathMLAttributes(token) + elif currentNode.namespace == namespaces["svg"]: + self.adjustSVGTagNames(token) + self.parser.adjustSVGAttributes(token) + self.parser.adjustForeignAttributes(token) + token["namespace"] = currentNode.namespace + self.tree.insertElement(token) + if token["selfClosing"]: + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def processEndTag(self, token): + nodeIndex = len(self.tree.openElements) - 1 + node = self.tree.openElements[-1] + if node.name.translate(asciiUpper2Lower) != token["name"]: + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + while True: + if node.name.translate(asciiUpper2Lower) == token["name"]: + # XXX this isn't in the spec but it seems necessary + if self.parser.phase == self.parser.phases["inTableText"]: + self.parser.phase.flushCharacters() + self.parser.phase = self.parser.phase.originalPhase + while self.tree.openElements.pop() != node: + assert self.tree.openElements + new_token = None + break + nodeIndex -= 1 + + node = self.tree.openElements[nodeIndex] + if node.namespace != self.tree.defaultNamespace: + continue + else: + new_token = self.parser.phase.processEndTag(token) + break + return new_token + + class AfterBodyPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([("html", self.endTagHtml)]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + # Stop parsing + pass + + def processComment(self, token): + # This is needed because data is to be appended to the <html> element + # here and not to whatever is currently open. + self.tree.insertComment(token, self.tree.openElements[0]) + + def processCharacters(self, token): + self.parser.parseError("unexpected-char-after-body") + self.parser.phase = self.parser.phases["inBody"] + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-after-body", + {"name": token["name"]}) + self.parser.phase = self.parser.phases["inBody"] + return token + + def endTagHtml(self, name): + if self.parser.innerHTML: + self.parser.parseError("unexpected-end-tag-after-body-innerhtml") + else: + self.parser.phase = self.parser.phases["afterAfterBody"] + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-after-body", + {"name": token["name"]}) + self.parser.phase = self.parser.phases["inBody"] + return token + + class InFramesetPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-frameset + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("frameset", self.startTagFrameset), + ("frame", self.startTagFrame), + ("noframes", self.startTagNoframes) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("frameset", self.endTagFrameset) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + if self.tree.openElements[-1].name != "html": + self.parser.parseError("eof-in-frameset") + else: + assert self.parser.innerHTML + + def processCharacters(self, token): + self.parser.parseError("unexpected-char-in-frameset") + + def startTagFrameset(self, token): + self.tree.insertElement(token) + + def startTagFrame(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + + def startTagNoframes(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-in-frameset", + {"name": token["name"]}) + + def endTagFrameset(self, token): + if self.tree.openElements[-1].name == "html": + # innerHTML case + self.parser.parseError("unexpected-frameset-in-frameset-innerhtml") + else: + self.tree.openElements.pop() + if (not self.parser.innerHTML and + self.tree.openElements[-1].name != "frameset"): + # If we're not in innerHTML mode and the current node is not a + # "frameset" element (anymore) then switch. + self.parser.phase = self.parser.phases["afterFrameset"] + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-in-frameset", + {"name": token["name"]}) + + class AfterFramesetPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#after3 + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("noframes", self.startTagNoframes) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("html", self.endTagHtml) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + # Stop parsing + pass + + def processCharacters(self, token): + self.parser.parseError("unexpected-char-after-frameset") + + def startTagNoframes(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-after-frameset", + {"name": token["name"]}) + + def endTagHtml(self, token): + self.parser.phase = self.parser.phases["afterAfterFrameset"] + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-after-frameset", + {"name": token["name"]}) + + class AfterAfterBodyPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml) + ]) + self.startTagHandler.default = self.startTagOther + + def processEOF(self): + pass + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processSpaceCharacters(self, token): + return self.parser.phases["inBody"].processSpaceCharacters(token) + + def processCharacters(self, token): + self.parser.parseError("expected-eof-but-got-char") + self.parser.phase = self.parser.phases["inBody"] + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("expected-eof-but-got-start-tag", + {"name": token["name"]}) + self.parser.phase = self.parser.phases["inBody"] + return token + + def processEndTag(self, token): + self.parser.parseError("expected-eof-but-got-end-tag", + {"name": token["name"]}) + self.parser.phase = self.parser.phases["inBody"] + return token + + class AfterAfterFramesetPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("noframes", self.startTagNoFrames) + ]) + self.startTagHandler.default = self.startTagOther + + def processEOF(self): + pass + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processSpaceCharacters(self, token): + return self.parser.phases["inBody"].processSpaceCharacters(token) + + def processCharacters(self, token): + self.parser.parseError("expected-eof-but-got-char") + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagNoFrames(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("expected-eof-but-got-start-tag", + {"name": token["name"]}) + + def processEndTag(self, token): + self.parser.parseError("expected-eof-but-got-end-tag", + {"name": token["name"]}) + # pylint:enable=unused-argument + + return { + "initial": InitialPhase, + "beforeHtml": BeforeHtmlPhase, + "beforeHead": BeforeHeadPhase, + "inHead": InHeadPhase, + "inHeadNoscript": InHeadNoscriptPhase, + "afterHead": AfterHeadPhase, + "inBody": InBodyPhase, + "text": TextPhase, + "inTable": InTablePhase, + "inTableText": InTableTextPhase, + "inCaption": InCaptionPhase, + "inColumnGroup": InColumnGroupPhase, + "inTableBody": InTableBodyPhase, + "inRow": InRowPhase, + "inCell": InCellPhase, + "inSelect": InSelectPhase, + "inSelectInTable": InSelectInTablePhase, + "inForeignContent": InForeignContentPhase, + "afterBody": AfterBodyPhase, + "inFrameset": InFramesetPhase, + "afterFrameset": AfterFramesetPhase, + "afterAfterBody": AfterAfterBodyPhase, + "afterAfterFrameset": AfterAfterFramesetPhase, + # XXX after after frameset + } + + +def adjust_attributes(token, replacements): + if PY3 or _utils.PY27: + needs_adjustment = viewkeys(token['data']) & viewkeys(replacements) + else: + needs_adjustment = frozenset(token['data']) & frozenset(replacements) + if needs_adjustment: + token['data'] = OrderedDict((replacements.get(k, k), v) + for k, v in token['data'].items()) + + +def impliedTagToken(name, type="EndTag", attributes=None, + selfClosing=False): + if attributes is None: + attributes = {} + return {"type": tokenTypes[type], "name": name, "data": attributes, + "selfClosing": selfClosing} + + +class ParseError(Exception): + """Error in parsed document""" + pass diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/serializer.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/serializer.py new file mode 100644 index 0000000..2fb3481 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/serializer.py @@ -0,0 +1,334 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +import re + +from codecs import register_error, xmlcharrefreplace_errors + +from .constants import voidElements, booleanAttributes, spaceCharacters +from .constants import rcdataElements, entities, xmlEntities +from . import treewalkers, _utils +from xml.sax.saxutils import escape + +_quoteAttributeSpecChars = "".join(spaceCharacters) + "\"'=<>`" +_quoteAttributeSpec = re.compile("[" + _quoteAttributeSpecChars + "]") +_quoteAttributeLegacy = re.compile("[" + _quoteAttributeSpecChars + + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n" + "\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15" + "\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x2f\x60\xa0\u1680\u180e\u180f\u2000" + "\u2001\u2002\u2003\u2004\u2005\u2006\u2007" + "\u2008\u2009\u200a\u2028\u2029\u202f\u205f" + "\u3000]") + + +_encode_entity_map = {} +_is_ucs4 = len("\U0010FFFF") == 1 +for k, v in list(entities.items()): + # skip multi-character entities + if ((_is_ucs4 and len(v) > 1) or + (not _is_ucs4 and len(v) > 2)): + continue + if v != "&": + if len(v) == 2: + v = _utils.surrogatePairToCodepoint(v) + else: + v = ord(v) + if v not in _encode_entity_map or k.islower(): + # prefer < over < and similarly for &, >, etc. + _encode_entity_map[v] = k + + +def htmlentityreplace_errors(exc): + if isinstance(exc, (UnicodeEncodeError, UnicodeTranslateError)): + res = [] + codepoints = [] + skip = False + for i, c in enumerate(exc.object[exc.start:exc.end]): + if skip: + skip = False + continue + index = i + exc.start + if _utils.isSurrogatePair(exc.object[index:min([exc.end, index + 2])]): + codepoint = _utils.surrogatePairToCodepoint(exc.object[index:index + 2]) + skip = True + else: + codepoint = ord(c) + codepoints.append(codepoint) + for cp in codepoints: + e = _encode_entity_map.get(cp) + if e: + res.append("&") + res.append(e) + if not e.endswith(";"): + res.append(";") + else: + res.append("&#x%s;" % (hex(cp)[2:])) + return ("".join(res), exc.end) + else: + return xmlcharrefreplace_errors(exc) + +register_error("htmlentityreplace", htmlentityreplace_errors) + + +def serialize(input, tree="etree", encoding=None, **serializer_opts): + # XXX: Should we cache this? + walker = treewalkers.getTreeWalker(tree) + s = HTMLSerializer(**serializer_opts) + return s.render(walker(input), encoding) + + +class HTMLSerializer(object): + + # attribute quoting options + quote_attr_values = "legacy" # be secure by default + quote_char = '"' + use_best_quote_char = True + + # tag syntax options + omit_optional_tags = True + minimize_boolean_attributes = True + use_trailing_solidus = False + space_before_trailing_solidus = True + + # escaping options + escape_lt_in_attrs = False + escape_rcdata = False + resolve_entities = True + + # miscellaneous options + alphabetical_attributes = False + inject_meta_charset = True + strip_whitespace = False + sanitize = False + + options = ("quote_attr_values", "quote_char", "use_best_quote_char", + "omit_optional_tags", "minimize_boolean_attributes", + "use_trailing_solidus", "space_before_trailing_solidus", + "escape_lt_in_attrs", "escape_rcdata", "resolve_entities", + "alphabetical_attributes", "inject_meta_charset", + "strip_whitespace", "sanitize") + + def __init__(self, **kwargs): + """Initialize HTMLSerializer. + + Keyword options (default given first unless specified) include: + + inject_meta_charset=True|False + Whether it insert a meta element to define the character set of the + document. + quote_attr_values="legacy"|"spec"|"always" + Whether to quote attribute values that don't require quoting + per legacy browser behaviour, when required by the standard, or always. + quote_char=u'"'|u"'" + Use given quote character for attribute quoting. Default is to + use double quote unless attribute value contains a double quote, + in which case single quotes are used instead. + escape_lt_in_attrs=False|True + Whether to escape < in attribute values. + escape_rcdata=False|True + Whether to escape characters that need to be escaped within normal + elements within rcdata elements such as style. + resolve_entities=True|False + Whether to resolve named character entities that appear in the + source tree. The XML predefined entities < > & " ' + are unaffected by this setting. + strip_whitespace=False|True + Whether to remove semantically meaningless whitespace. (This + compresses all whitespace to a single space except within pre.) + minimize_boolean_attributes=True|False + Shortens boolean attributes to give just the attribute value, + for example <input disabled="disabled"> becomes <input disabled>. + use_trailing_solidus=False|True + Includes a close-tag slash at the end of the start tag of void + elements (empty elements whose end tag is forbidden). E.g. <hr/>. + space_before_trailing_solidus=True|False + Places a space immediately before the closing slash in a tag + using a trailing solidus. E.g. <hr />. Requires use_trailing_solidus. + sanitize=False|True + Strip all unsafe or unknown constructs from output. + See `html5lib user documentation`_ + omit_optional_tags=True|False + Omit start/end tags that are optional. + alphabetical_attributes=False|True + Reorder attributes to be in alphabetical order. + + .. _html5lib user documentation: http://code.google.com/p/html5lib/wiki/UserDocumentation + """ + unexpected_args = frozenset(kwargs) - frozenset(self.options) + if len(unexpected_args) > 0: + raise TypeError("__init__() got an unexpected keyword argument '%s'" % next(iter(unexpected_args))) + if 'quote_char' in kwargs: + self.use_best_quote_char = False + for attr in self.options: + setattr(self, attr, kwargs.get(attr, getattr(self, attr))) + self.errors = [] + self.strict = False + + def encode(self, string): + assert(isinstance(string, text_type)) + if self.encoding: + return string.encode(self.encoding, "htmlentityreplace") + else: + return string + + def encodeStrict(self, string): + assert(isinstance(string, text_type)) + if self.encoding: + return string.encode(self.encoding, "strict") + else: + return string + + def serialize(self, treewalker, encoding=None): + # pylint:disable=too-many-nested-blocks + self.encoding = encoding + in_cdata = False + self.errors = [] + + if encoding and self.inject_meta_charset: + from .filters.inject_meta_charset import Filter + treewalker = Filter(treewalker, encoding) + # Alphabetical attributes is here under the assumption that none of + # the later filters add or change order of attributes; it needs to be + # before the sanitizer so escaped elements come out correctly + if self.alphabetical_attributes: + from .filters.alphabeticalattributes import Filter + treewalker = Filter(treewalker) + # WhitespaceFilter should be used before OptionalTagFilter + # for maximum efficiently of this latter filter + if self.strip_whitespace: + from .filters.whitespace import Filter + treewalker = Filter(treewalker) + if self.sanitize: + from .filters.sanitizer import Filter + treewalker = Filter(treewalker) + if self.omit_optional_tags: + from .filters.optionaltags import Filter + treewalker = Filter(treewalker) + + for token in treewalker: + type = token["type"] + if type == "Doctype": + doctype = "<!DOCTYPE %s" % token["name"] + + if token["publicId"]: + doctype += ' PUBLIC "%s"' % token["publicId"] + elif token["systemId"]: + doctype += " SYSTEM" + if token["systemId"]: + if token["systemId"].find('"') >= 0: + if token["systemId"].find("'") >= 0: + self.serializeError("System identifer contains both single and double quote characters") + quote_char = "'" + else: + quote_char = '"' + doctype += " %s%s%s" % (quote_char, token["systemId"], quote_char) + + doctype += ">" + yield self.encodeStrict(doctype) + + elif type in ("Characters", "SpaceCharacters"): + if type == "SpaceCharacters" or in_cdata: + if in_cdata and token["data"].find("</") >= 0: + self.serializeError("Unexpected </ in CDATA") + yield self.encode(token["data"]) + else: + yield self.encode(escape(token["data"])) + + elif type in ("StartTag", "EmptyTag"): + name = token["name"] + yield self.encodeStrict("<%s" % name) + if name in rcdataElements and not self.escape_rcdata: + in_cdata = True + elif in_cdata: + self.serializeError("Unexpected child element of a CDATA element") + for (_, attr_name), attr_value in token["data"].items(): + # TODO: Add namespace support here + k = attr_name + v = attr_value + yield self.encodeStrict(' ') + + yield self.encodeStrict(k) + if not self.minimize_boolean_attributes or \ + (k not in booleanAttributes.get(name, tuple()) and + k not in booleanAttributes.get("", tuple())): + yield self.encodeStrict("=") + if self.quote_attr_values == "always" or len(v) == 0: + quote_attr = True + elif self.quote_attr_values == "spec": + quote_attr = _quoteAttributeSpec.search(v) is not None + elif self.quote_attr_values == "legacy": + quote_attr = _quoteAttributeLegacy.search(v) is not None + else: + raise ValueError("quote_attr_values must be one of: " + "'always', 'spec', or 'legacy'") + v = v.replace("&", "&") + if self.escape_lt_in_attrs: + v = v.replace("<", "<") + if quote_attr: + quote_char = self.quote_char + if self.use_best_quote_char: + if "'" in v and '"' not in v: + quote_char = '"' + elif '"' in v and "'" not in v: + quote_char = "'" + if quote_char == "'": + v = v.replace("'", "'") + else: + v = v.replace('"', """) + yield self.encodeStrict(quote_char) + yield self.encode(v) + yield self.encodeStrict(quote_char) + else: + yield self.encode(v) + if name in voidElements and self.use_trailing_solidus: + if self.space_before_trailing_solidus: + yield self.encodeStrict(" /") + else: + yield self.encodeStrict("/") + yield self.encode(">") + + elif type == "EndTag": + name = token["name"] + if name in rcdataElements: + in_cdata = False + elif in_cdata: + self.serializeError("Unexpected child element of a CDATA element") + yield self.encodeStrict("</%s>" % name) + + elif type == "Comment": + data = token["data"] + if data.find("--") >= 0: + self.serializeError("Comment contains --") + yield self.encodeStrict("<!--%s-->" % token["data"]) + + elif type == "Entity": + name = token["name"] + key = name + ";" + if key not in entities: + self.serializeError("Entity %s not recognized" % name) + if self.resolve_entities and key not in xmlEntities: + data = entities[key] + else: + data = "&%s;" % name + yield self.encodeStrict(data) + + else: + self.serializeError(token["data"]) + + def render(self, treewalker, encoding=None): + if encoding: + return b"".join(list(self.serialize(treewalker, encoding))) + else: + return "".join(list(self.serialize(treewalker))) + + def serializeError(self, data="XXX ERROR MESSAGE NEEDED"): + # XXX The idea is to make data mandatory. + self.errors.append(data) + if self.strict: + raise SerializeError + + +class SerializeError(Exception): + """Error in serialized tree""" + pass diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treeadapters/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treeadapters/__init__.py new file mode 100644 index 0000000..4f97846 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treeadapters/__init__.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import sax + +__all__ = ["sax"] + +try: + from . import genshi # noqa +except ImportError: + pass +else: + __all__.append("genshi") diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treeadapters/genshi.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treeadapters/genshi.py new file mode 100644 index 0000000..04e316d --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treeadapters/genshi.py @@ -0,0 +1,47 @@ +from __future__ import absolute_import, division, unicode_literals + +from genshi.core import QName, Attrs +from genshi.core import START, END, TEXT, COMMENT, DOCTYPE + + +def to_genshi(walker): + text = [] + for token in walker: + type = token["type"] + if type in ("Characters", "SpaceCharacters"): + text.append(token["data"]) + elif text: + yield TEXT, "".join(text), (None, -1, -1) + text = [] + + if type in ("StartTag", "EmptyTag"): + if token["namespace"]: + name = "{%s}%s" % (token["namespace"], token["name"]) + else: + name = token["name"] + attrs = Attrs([(QName("{%s}%s" % attr if attr[0] is not None else attr[1]), value) + for attr, value in token["data"].items()]) + yield (START, (QName(name), attrs), (None, -1, -1)) + if type == "EmptyTag": + type = "EndTag" + + if type == "EndTag": + if token["namespace"]: + name = "{%s}%s" % (token["namespace"], token["name"]) + else: + name = token["name"] + + yield END, QName(name), (None, -1, -1) + + elif type == "Comment": + yield COMMENT, token["data"], (None, -1, -1) + + elif type == "Doctype": + yield DOCTYPE, (token["name"], token["publicId"], + token["systemId"]), (None, -1, -1) + + else: + pass # FIXME: What to do? + + if text: + yield TEXT, "".join(text), (None, -1, -1) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treeadapters/sax.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treeadapters/sax.py new file mode 100644 index 0000000..ad47df9 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treeadapters/sax.py @@ -0,0 +1,44 @@ +from __future__ import absolute_import, division, unicode_literals + +from xml.sax.xmlreader import AttributesNSImpl + +from ..constants import adjustForeignAttributes, unadjustForeignAttributes + +prefix_mapping = {} +for prefix, localName, namespace in adjustForeignAttributes.values(): + if prefix is not None: + prefix_mapping[prefix] = namespace + + +def to_sax(walker, handler): + """Call SAX-like content handler based on treewalker walker""" + handler.startDocument() + for prefix, namespace in prefix_mapping.items(): + handler.startPrefixMapping(prefix, namespace) + + for token in walker: + type = token["type"] + if type == "Doctype": + continue + elif type in ("StartTag", "EmptyTag"): + attrs = AttributesNSImpl(token["data"], + unadjustForeignAttributes) + handler.startElementNS((token["namespace"], token["name"]), + token["name"], + attrs) + if type == "EmptyTag": + handler.endElementNS((token["namespace"], token["name"]), + token["name"]) + elif type == "EndTag": + handler.endElementNS((token["namespace"], token["name"]), + token["name"]) + elif type in ("Characters", "SpaceCharacters"): + handler.characters(token["data"]) + elif type == "Comment": + pass + else: + assert False, "Unknown token type" + + for prefix, namespace in prefix_mapping.items(): + handler.endPrefixMapping(prefix) + handler.endDocument() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/__init__.py new file mode 100644 index 0000000..e232884 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/__init__.py @@ -0,0 +1,76 @@ +"""A collection of modules for building different kinds of tree from +HTML documents. + +To create a treebuilder for a new type of tree, you need to do +implement several things: + +1) A set of classes for various types of elements: Document, Doctype, +Comment, Element. These must implement the interface of +_base.treebuilders.Node (although comment nodes have a different +signature for their constructor, see treebuilders.etree.Comment) +Textual content may also be implemented as another node type, or not, as +your tree implementation requires. + +2) A treebuilder object (called TreeBuilder by convention) that +inherits from treebuilders._base.TreeBuilder. This has 4 required attributes: +documentClass - the class to use for the bottommost node of a document +elementClass - the class to use for HTML Elements +commentClass - the class to use for comments +doctypeClass - the class to use for doctypes +It also has one required method: +getDocument - Returns the root node of the complete document tree + +3) If you wish to run the unit tests, you must also create a +testSerializer method on your treebuilder which accepts a node and +returns a string containing Node and its children serialized according +to the format used in the unittests +""" + +from __future__ import absolute_import, division, unicode_literals + +from .._utils import default_etree + +treeBuilderCache = {} + + +def getTreeBuilder(treeType, implementation=None, **kwargs): + """Get a TreeBuilder class for various types of tree with built-in support + + treeType - the name of the tree type required (case-insensitive). Supported + values are: + + "dom" - A generic builder for DOM implementations, defaulting to + a xml.dom.minidom based implementation. + "etree" - A generic builder for tree implementations exposing an + ElementTree-like interface, defaulting to + xml.etree.cElementTree if available and + xml.etree.ElementTree if not. + "lxml" - A etree-based builder for lxml.etree, handling + limitations of lxml's implementation. + + implementation - (Currently applies to the "etree" and "dom" tree types). A + module implementing the tree type e.g. + xml.etree.ElementTree or xml.etree.cElementTree.""" + + treeType = treeType.lower() + if treeType not in treeBuilderCache: + if treeType == "dom": + from . import dom + # Come up with a sane default (pref. from the stdlib) + if implementation is None: + from xml.dom import minidom + implementation = minidom + # NEVER cache here, caching is done in the dom submodule + return dom.getDomModule(implementation, **kwargs).TreeBuilder + elif treeType == "lxml": + from . import etree_lxml + treeBuilderCache[treeType] = etree_lxml.TreeBuilder + elif treeType == "etree": + from . import etree + if implementation is None: + implementation = default_etree + # NEVER cache here, caching is done in the etree submodule + return etree.getETreeModule(implementation, **kwargs).TreeBuilder + else: + raise ValueError("""Unrecognised treebuilder "%s" """ % treeType) + return treeBuilderCache.get(treeType) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/base.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/base.py new file mode 100644 index 0000000..9798f7c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/base.py @@ -0,0 +1,383 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +from ..constants import scopingElements, tableInsertModeElements, namespaces + +# The scope markers are inserted when entering object elements, +# marquees, table cells, and table captions, and are used to prevent formatting +# from "leaking" into tables, object elements, and marquees. +Marker = None + +listElementsMap = { + None: (frozenset(scopingElements), False), + "button": (frozenset(scopingElements | set([(namespaces["html"], "button")])), False), + "list": (frozenset(scopingElements | set([(namespaces["html"], "ol"), + (namespaces["html"], "ul")])), False), + "table": (frozenset([(namespaces["html"], "html"), + (namespaces["html"], "table")]), False), + "select": (frozenset([(namespaces["html"], "optgroup"), + (namespaces["html"], "option")]), True) +} + + +class Node(object): + def __init__(self, name): + """Node representing an item in the tree. + name - The tag name associated with the node + parent - The parent of the current node (or None for the document node) + value - The value of the current node (applies to text nodes and + comments + attributes - a dict holding name, value pairs for attributes of the node + childNodes - a list of child nodes of the current node. This must + include all elements but not necessarily other node types + _flags - A list of miscellaneous flags that can be set on the node + """ + self.name = name + self.parent = None + self.value = None + self.attributes = {} + self.childNodes = [] + self._flags = [] + + def __str__(self): + attributesStr = " ".join(["%s=\"%s\"" % (name, value) + for name, value in + self.attributes.items()]) + if attributesStr: + return "<%s %s>" % (self.name, attributesStr) + else: + return "<%s>" % (self.name) + + def __repr__(self): + return "<%s>" % (self.name) + + def appendChild(self, node): + """Insert node as a child of the current node + """ + raise NotImplementedError + + def insertText(self, data, insertBefore=None): + """Insert data as text in the current node, positioned before the + start of node insertBefore or to the end of the node's text. + """ + raise NotImplementedError + + def insertBefore(self, node, refNode): + """Insert node as a child of the current node, before refNode in the + list of child nodes. Raises ValueError if refNode is not a child of + the current node""" + raise NotImplementedError + + def removeChild(self, node): + """Remove node from the children of the current node + """ + raise NotImplementedError + + def reparentChildren(self, newParent): + """Move all the children of the current node to newParent. + This is needed so that trees that don't store text as nodes move the + text in the correct way + """ + # XXX - should this method be made more general? + for child in self.childNodes: + newParent.appendChild(child) + self.childNodes = [] + + def cloneNode(self): + """Return a shallow copy of the current node i.e. a node with the same + name and attributes but with no parent or child nodes + """ + raise NotImplementedError + + def hasContent(self): + """Return true if the node has children or text, false otherwise + """ + raise NotImplementedError + + +class ActiveFormattingElements(list): + def append(self, node): + equalCount = 0 + if node != Marker: + for element in self[::-1]: + if element == Marker: + break + if self.nodesEqual(element, node): + equalCount += 1 + if equalCount == 3: + self.remove(element) + break + list.append(self, node) + + def nodesEqual(self, node1, node2): + if not node1.nameTuple == node2.nameTuple: + return False + + if not node1.attributes == node2.attributes: + return False + + return True + + +class TreeBuilder(object): + """Base treebuilder implementation + documentClass - the class to use for the bottommost node of a document + elementClass - the class to use for HTML Elements + commentClass - the class to use for comments + doctypeClass - the class to use for doctypes + """ + # pylint:disable=not-callable + + # Document class + documentClass = None + + # The class to use for creating a node + elementClass = None + + # The class to use for creating comments + commentClass = None + + # The class to use for creating doctypes + doctypeClass = None + + # Fragment class + fragmentClass = None + + def __init__(self, namespaceHTMLElements): + if namespaceHTMLElements: + self.defaultNamespace = "http://www.w3.org/1999/xhtml" + else: + self.defaultNamespace = None + self.reset() + + def reset(self): + self.openElements = [] + self.activeFormattingElements = ActiveFormattingElements() + + # XXX - rename these to headElement, formElement + self.headPointer = None + self.formPointer = None + + self.insertFromTable = False + + self.document = self.documentClass() + + def elementInScope(self, target, variant=None): + + # If we pass a node in we match that. if we pass a string + # match any node with that name + exactNode = hasattr(target, "nameTuple") + if not exactNode: + if isinstance(target, text_type): + target = (namespaces["html"], target) + assert isinstance(target, tuple) + + listElements, invert = listElementsMap[variant] + + for node in reversed(self.openElements): + if exactNode and node == target: + return True + elif not exactNode and node.nameTuple == target: + return True + elif (invert ^ (node.nameTuple in listElements)): + return False + + assert False # We should never reach this point + + def reconstructActiveFormattingElements(self): + # Within this algorithm the order of steps described in the + # specification is not quite the same as the order of steps in the + # code. It should still do the same though. + + # Step 1: stop the algorithm when there's nothing to do. + if not self.activeFormattingElements: + return + + # Step 2 and step 3: we start with the last element. So i is -1. + i = len(self.activeFormattingElements) - 1 + entry = self.activeFormattingElements[i] + if entry == Marker or entry in self.openElements: + return + + # Step 6 + while entry != Marker and entry not in self.openElements: + if i == 0: + # This will be reset to 0 below + i = -1 + break + i -= 1 + # Step 5: let entry be one earlier in the list. + entry = self.activeFormattingElements[i] + + while True: + # Step 7 + i += 1 + + # Step 8 + entry = self.activeFormattingElements[i] + clone = entry.cloneNode() # Mainly to get a new copy of the attributes + + # Step 9 + element = self.insertElement({"type": "StartTag", + "name": clone.name, + "namespace": clone.namespace, + "data": clone.attributes}) + + # Step 10 + self.activeFormattingElements[i] = element + + # Step 11 + if element == self.activeFormattingElements[-1]: + break + + def clearActiveFormattingElements(self): + entry = self.activeFormattingElements.pop() + while self.activeFormattingElements and entry != Marker: + entry = self.activeFormattingElements.pop() + + def elementInActiveFormattingElements(self, name): + """Check if an element exists between the end of the active + formatting elements and the last marker. If it does, return it, else + return false""" + + for item in self.activeFormattingElements[::-1]: + # Check for Marker first because if it's a Marker it doesn't have a + # name attribute. + if item == Marker: + break + elif item.name == name: + return item + return False + + def insertRoot(self, token): + element = self.createElement(token) + self.openElements.append(element) + self.document.appendChild(element) + + def insertDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + + doctype = self.doctypeClass(name, publicId, systemId) + self.document.appendChild(doctype) + + def insertComment(self, token, parent=None): + if parent is None: + parent = self.openElements[-1] + parent.appendChild(self.commentClass(token["data"])) + + def createElement(self, token): + """Create an element but don't insert it anywhere""" + name = token["name"] + namespace = token.get("namespace", self.defaultNamespace) + element = self.elementClass(name, namespace) + element.attributes = token["data"] + return element + + def _getInsertFromTable(self): + return self._insertFromTable + + def _setInsertFromTable(self, value): + """Switch the function used to insert an element from the + normal one to the misnested table one and back again""" + self._insertFromTable = value + if value: + self.insertElement = self.insertElementTable + else: + self.insertElement = self.insertElementNormal + + insertFromTable = property(_getInsertFromTable, _setInsertFromTable) + + def insertElementNormal(self, token): + name = token["name"] + assert isinstance(name, text_type), "Element %s not unicode" % name + namespace = token.get("namespace", self.defaultNamespace) + element = self.elementClass(name, namespace) + element.attributes = token["data"] + self.openElements[-1].appendChild(element) + self.openElements.append(element) + return element + + def insertElementTable(self, token): + """Create an element and insert it into the tree""" + element = self.createElement(token) + if self.openElements[-1].name not in tableInsertModeElements: + return self.insertElementNormal(token) + else: + # We should be in the InTable mode. This means we want to do + # special magic element rearranging + parent, insertBefore = self.getTableMisnestedNodePosition() + if insertBefore is None: + parent.appendChild(element) + else: + parent.insertBefore(element, insertBefore) + self.openElements.append(element) + return element + + def insertText(self, data, parent=None): + """Insert text data.""" + if parent is None: + parent = self.openElements[-1] + + if (not self.insertFromTable or (self.insertFromTable and + self.openElements[-1].name + not in tableInsertModeElements)): + parent.insertText(data) + else: + # We should be in the InTable mode. This means we want to do + # special magic element rearranging + parent, insertBefore = self.getTableMisnestedNodePosition() + parent.insertText(data, insertBefore) + + def getTableMisnestedNodePosition(self): + """Get the foster parent element, and sibling to insert before + (or None) when inserting a misnested table node""" + # The foster parent element is the one which comes before the most + # recently opened table element + # XXX - this is really inelegant + lastTable = None + fosterParent = None + insertBefore = None + for elm in self.openElements[::-1]: + if elm.name == "table": + lastTable = elm + break + if lastTable: + # XXX - we should really check that this parent is actually a + # node here + if lastTable.parent: + fosterParent = lastTable.parent + insertBefore = lastTable + else: + fosterParent = self.openElements[ + self.openElements.index(lastTable) - 1] + else: + fosterParent = self.openElements[0] + return fosterParent, insertBefore + + def generateImpliedEndTags(self, exclude=None): + name = self.openElements[-1].name + # XXX td, th and tr are not actually needed + if (name in frozenset(("dd", "dt", "li", "option", "optgroup", "p", "rp", "rt")) and + name != exclude): + self.openElements.pop() + # XXX This is not entirely what the specification says. We should + # investigate it more closely. + self.generateImpliedEndTags(exclude) + + def getDocument(self): + "Return the final tree" + return self.document + + def getFragment(self): + "Return the final fragment" + # assert self.innerHTML + fragment = self.fragmentClass() + self.openElements[0].reparentChildren(fragment) + return fragment + + def testSerializer(self, node): + """Serialize the subtree of node in the format required by unit tests + node - the node from which to start serializing""" + raise NotImplementedError diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/dom.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/dom.py new file mode 100644 index 0000000..dcfac22 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/dom.py @@ -0,0 +1,236 @@ +from __future__ import absolute_import, division, unicode_literals + + +from collections import MutableMapping +from xml.dom import minidom, Node +import weakref + +from . import base +from .. import constants +from ..constants import namespaces +from .._utils import moduleFactoryFactory + + +def getDomBuilder(DomImplementation): + Dom = DomImplementation + + class AttrList(MutableMapping): + def __init__(self, element): + self.element = element + + def __iter__(self): + return iter(self.element.attributes.keys()) + + def __setitem__(self, name, value): + if isinstance(name, tuple): + raise NotImplementedError + else: + attr = self.element.ownerDocument.createAttribute(name) + attr.value = value + self.element.attributes[name] = attr + + def __len__(self): + return len(self.element.attributes) + + def items(self): + return list(self.element.attributes.items()) + + def values(self): + return list(self.element.attributes.values()) + + def __getitem__(self, name): + if isinstance(name, tuple): + raise NotImplementedError + else: + return self.element.attributes[name].value + + def __delitem__(self, name): + if isinstance(name, tuple): + raise NotImplementedError + else: + del self.element.attributes[name] + + class NodeBuilder(base.Node): + def __init__(self, element): + base.Node.__init__(self, element.nodeName) + self.element = element + + namespace = property(lambda self: hasattr(self.element, "namespaceURI") and + self.element.namespaceURI or None) + + def appendChild(self, node): + node.parent = self + self.element.appendChild(node.element) + + def insertText(self, data, insertBefore=None): + text = self.element.ownerDocument.createTextNode(data) + if insertBefore: + self.element.insertBefore(text, insertBefore.element) + else: + self.element.appendChild(text) + + def insertBefore(self, node, refNode): + self.element.insertBefore(node.element, refNode.element) + node.parent = self + + def removeChild(self, node): + if node.element.parentNode == self.element: + self.element.removeChild(node.element) + node.parent = None + + def reparentChildren(self, newParent): + while self.element.hasChildNodes(): + child = self.element.firstChild + self.element.removeChild(child) + newParent.element.appendChild(child) + self.childNodes = [] + + def getAttributes(self): + return AttrList(self.element) + + def setAttributes(self, attributes): + if attributes: + for name, value in list(attributes.items()): + if isinstance(name, tuple): + if name[0] is not None: + qualifiedName = (name[0] + ":" + name[1]) + else: + qualifiedName = name[1] + self.element.setAttributeNS(name[2], qualifiedName, + value) + else: + self.element.setAttribute( + name, value) + attributes = property(getAttributes, setAttributes) + + def cloneNode(self): + return NodeBuilder(self.element.cloneNode(False)) + + def hasContent(self): + return self.element.hasChildNodes() + + def getNameTuple(self): + if self.namespace is None: + return namespaces["html"], self.name + else: + return self.namespace, self.name + + nameTuple = property(getNameTuple) + + class TreeBuilder(base.TreeBuilder): # pylint:disable=unused-variable + def documentClass(self): + self.dom = Dom.getDOMImplementation().createDocument(None, None, None) + return weakref.proxy(self) + + def insertDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + + domimpl = Dom.getDOMImplementation() + doctype = domimpl.createDocumentType(name, publicId, systemId) + self.document.appendChild(NodeBuilder(doctype)) + if Dom == minidom: + doctype.ownerDocument = self.dom + + def elementClass(self, name, namespace=None): + if namespace is None and self.defaultNamespace is None: + node = self.dom.createElement(name) + else: + node = self.dom.createElementNS(namespace, name) + + return NodeBuilder(node) + + def commentClass(self, data): + return NodeBuilder(self.dom.createComment(data)) + + def fragmentClass(self): + return NodeBuilder(self.dom.createDocumentFragment()) + + def appendChild(self, node): + self.dom.appendChild(node.element) + + def testSerializer(self, element): + return testSerializer(element) + + def getDocument(self): + return self.dom + + def getFragment(self): + return base.TreeBuilder.getFragment(self).element + + def insertText(self, data, parent=None): + data = data + if parent != self: + base.TreeBuilder.insertText(self, data, parent) + else: + # HACK: allow text nodes as children of the document node + if hasattr(self.dom, '_child_node_types'): + # pylint:disable=protected-access + if Node.TEXT_NODE not in self.dom._child_node_types: + self.dom._child_node_types = list(self.dom._child_node_types) + self.dom._child_node_types.append(Node.TEXT_NODE) + self.dom.appendChild(self.dom.createTextNode(data)) + + implementation = DomImplementation + name = None + + def testSerializer(element): + element.normalize() + rv = [] + + def serializeElement(element, indent=0): + if element.nodeType == Node.DOCUMENT_TYPE_NODE: + if element.name: + if element.publicId or element.systemId: + publicId = element.publicId or "" + systemId = element.systemId or "" + rv.append("""|%s<!DOCTYPE %s "%s" "%s">""" % + (' ' * indent, element.name, publicId, systemId)) + else: + rv.append("|%s<!DOCTYPE %s>" % (' ' * indent, element.name)) + else: + rv.append("|%s<!DOCTYPE >" % (' ' * indent,)) + elif element.nodeType == Node.DOCUMENT_NODE: + rv.append("#document") + elif element.nodeType == Node.DOCUMENT_FRAGMENT_NODE: + rv.append("#document-fragment") + elif element.nodeType == Node.COMMENT_NODE: + rv.append("|%s<!-- %s -->" % (' ' * indent, element.nodeValue)) + elif element.nodeType == Node.TEXT_NODE: + rv.append("|%s\"%s\"" % (' ' * indent, element.nodeValue)) + else: + if (hasattr(element, "namespaceURI") and + element.namespaceURI is not None): + name = "%s %s" % (constants.prefixes[element.namespaceURI], + element.nodeName) + else: + name = element.nodeName + rv.append("|%s<%s>" % (' ' * indent, name)) + if element.hasAttributes(): + attributes = [] + for i in range(len(element.attributes)): + attr = element.attributes.item(i) + name = attr.nodeName + value = attr.value + ns = attr.namespaceURI + if ns: + name = "%s %s" % (constants.prefixes[ns], attr.localName) + else: + name = attr.nodeName + attributes.append((name, value)) + + for name, value in sorted(attributes): + rv.append('|%s%s="%s"' % (' ' * (indent + 2), name, value)) + indent += 2 + for child in element.childNodes: + serializeElement(child, indent) + serializeElement(element, 0) + + return "\n".join(rv) + + return locals() + + +# The actual means to get a module! +getDomModule = moduleFactoryFactory(getDomBuilder) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/etree.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/etree.py new file mode 100644 index 0000000..0dedf44 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/etree.py @@ -0,0 +1,340 @@ +from __future__ import absolute_import, division, unicode_literals +# pylint:disable=protected-access + +from pip._vendor.six import text_type + +import re + +from . import base +from .. import _ihatexml +from .. import constants +from ..constants import namespaces +from .._utils import moduleFactoryFactory + +tag_regexp = re.compile("{([^}]*)}(.*)") + + +def getETreeBuilder(ElementTreeImplementation, fullTree=False): + ElementTree = ElementTreeImplementation + ElementTreeCommentType = ElementTree.Comment("asd").tag + + class Element(base.Node): + def __init__(self, name, namespace=None): + self._name = name + self._namespace = namespace + self._element = ElementTree.Element(self._getETreeTag(name, + namespace)) + if namespace is None: + self.nameTuple = namespaces["html"], self._name + else: + self.nameTuple = self._namespace, self._name + self.parent = None + self._childNodes = [] + self._flags = [] + + def _getETreeTag(self, name, namespace): + if namespace is None: + etree_tag = name + else: + etree_tag = "{%s}%s" % (namespace, name) + return etree_tag + + def _setName(self, name): + self._name = name + self._element.tag = self._getETreeTag(self._name, self._namespace) + + def _getName(self): + return self._name + + name = property(_getName, _setName) + + def _setNamespace(self, namespace): + self._namespace = namespace + self._element.tag = self._getETreeTag(self._name, self._namespace) + + def _getNamespace(self): + return self._namespace + + namespace = property(_getNamespace, _setNamespace) + + def _getAttributes(self): + return self._element.attrib + + def _setAttributes(self, attributes): + # Delete existing attributes first + # XXX - there may be a better way to do this... + for key in list(self._element.attrib.keys()): + del self._element.attrib[key] + for key, value in attributes.items(): + if isinstance(key, tuple): + name = "{%s}%s" % (key[2], key[1]) + else: + name = key + self._element.set(name, value) + + attributes = property(_getAttributes, _setAttributes) + + def _getChildNodes(self): + return self._childNodes + + def _setChildNodes(self, value): + del self._element[:] + self._childNodes = [] + for element in value: + self.insertChild(element) + + childNodes = property(_getChildNodes, _setChildNodes) + + def hasContent(self): + """Return true if the node has children or text""" + return bool(self._element.text or len(self._element)) + + def appendChild(self, node): + self._childNodes.append(node) + self._element.append(node._element) + node.parent = self + + def insertBefore(self, node, refNode): + index = list(self._element).index(refNode._element) + self._element.insert(index, node._element) + node.parent = self + + def removeChild(self, node): + self._childNodes.remove(node) + self._element.remove(node._element) + node.parent = None + + def insertText(self, data, insertBefore=None): + if not(len(self._element)): + if not self._element.text: + self._element.text = "" + self._element.text += data + elif insertBefore is None: + # Insert the text as the tail of the last child element + if not self._element[-1].tail: + self._element[-1].tail = "" + self._element[-1].tail += data + else: + # Insert the text before the specified node + children = list(self._element) + index = children.index(insertBefore._element) + if index > 0: + if not self._element[index - 1].tail: + self._element[index - 1].tail = "" + self._element[index - 1].tail += data + else: + if not self._element.text: + self._element.text = "" + self._element.text += data + + def cloneNode(self): + element = type(self)(self.name, self.namespace) + for name, value in self.attributes.items(): + element.attributes[name] = value + return element + + def reparentChildren(self, newParent): + if newParent.childNodes: + newParent.childNodes[-1]._element.tail += self._element.text + else: + if not newParent._element.text: + newParent._element.text = "" + if self._element.text is not None: + newParent._element.text += self._element.text + self._element.text = "" + base.Node.reparentChildren(self, newParent) + + class Comment(Element): + def __init__(self, data): + # Use the superclass constructor to set all properties on the + # wrapper element + self._element = ElementTree.Comment(data) + self.parent = None + self._childNodes = [] + self._flags = [] + + def _getData(self): + return self._element.text + + def _setData(self, value): + self._element.text = value + + data = property(_getData, _setData) + + class DocumentType(Element): + def __init__(self, name, publicId, systemId): + Element.__init__(self, "<!DOCTYPE>") + self._element.text = name + self.publicId = publicId + self.systemId = systemId + + def _getPublicId(self): + return self._element.get("publicId", "") + + def _setPublicId(self, value): + if value is not None: + self._element.set("publicId", value) + + publicId = property(_getPublicId, _setPublicId) + + def _getSystemId(self): + return self._element.get("systemId", "") + + def _setSystemId(self, value): + if value is not None: + self._element.set("systemId", value) + + systemId = property(_getSystemId, _setSystemId) + + class Document(Element): + def __init__(self): + Element.__init__(self, "DOCUMENT_ROOT") + + class DocumentFragment(Element): + def __init__(self): + Element.__init__(self, "DOCUMENT_FRAGMENT") + + def testSerializer(element): + rv = [] + + def serializeElement(element, indent=0): + if not(hasattr(element, "tag")): + element = element.getroot() + if element.tag == "<!DOCTYPE>": + if element.get("publicId") or element.get("systemId"): + publicId = element.get("publicId") or "" + systemId = element.get("systemId") or "" + rv.append("""<!DOCTYPE %s "%s" "%s">""" % + (element.text, publicId, systemId)) + else: + rv.append("<!DOCTYPE %s>" % (element.text,)) + elif element.tag == "DOCUMENT_ROOT": + rv.append("#document") + if element.text is not None: + rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text)) + if element.tail is not None: + raise TypeError("Document node cannot have tail") + if hasattr(element, "attrib") and len(element.attrib): + raise TypeError("Document node cannot have attributes") + elif element.tag == ElementTreeCommentType: + rv.append("|%s<!-- %s -->" % (' ' * indent, element.text)) + else: + assert isinstance(element.tag, text_type), \ + "Expected unicode, got %s, %s" % (type(element.tag), element.tag) + nsmatch = tag_regexp.match(element.tag) + + if nsmatch is None: + name = element.tag + else: + ns, name = nsmatch.groups() + prefix = constants.prefixes[ns] + name = "%s %s" % (prefix, name) + rv.append("|%s<%s>" % (' ' * indent, name)) + + if hasattr(element, "attrib"): + attributes = [] + for name, value in element.attrib.items(): + nsmatch = tag_regexp.match(name) + if nsmatch is not None: + ns, name = nsmatch.groups() + prefix = constants.prefixes[ns] + attr_string = "%s %s" % (prefix, name) + else: + attr_string = name + attributes.append((attr_string, value)) + + for name, value in sorted(attributes): + rv.append('|%s%s="%s"' % (' ' * (indent + 2), name, value)) + if element.text: + rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text)) + indent += 2 + for child in element: + serializeElement(child, indent) + if element.tail: + rv.append("|%s\"%s\"" % (' ' * (indent - 2), element.tail)) + serializeElement(element, 0) + + return "\n".join(rv) + + def tostring(element): # pylint:disable=unused-variable + """Serialize an element and its child nodes to a string""" + rv = [] + filter = _ihatexml.InfosetFilter() + + def serializeElement(element): + if isinstance(element, ElementTree.ElementTree): + element = element.getroot() + + if element.tag == "<!DOCTYPE>": + if element.get("publicId") or element.get("systemId"): + publicId = element.get("publicId") or "" + systemId = element.get("systemId") or "" + rv.append("""<!DOCTYPE %s PUBLIC "%s" "%s">""" % + (element.text, publicId, systemId)) + else: + rv.append("<!DOCTYPE %s>" % (element.text,)) + elif element.tag == "DOCUMENT_ROOT": + if element.text is not None: + rv.append(element.text) + if element.tail is not None: + raise TypeError("Document node cannot have tail") + if hasattr(element, "attrib") and len(element.attrib): + raise TypeError("Document node cannot have attributes") + + for child in element: + serializeElement(child) + + elif element.tag == ElementTreeCommentType: + rv.append("<!--%s-->" % (element.text,)) + else: + # This is assumed to be an ordinary element + if not element.attrib: + rv.append("<%s>" % (filter.fromXmlName(element.tag),)) + else: + attr = " ".join(["%s=\"%s\"" % ( + filter.fromXmlName(name), value) + for name, value in element.attrib.items()]) + rv.append("<%s %s>" % (element.tag, attr)) + if element.text: + rv.append(element.text) + + for child in element: + serializeElement(child) + + rv.append("</%s>" % (element.tag,)) + + if element.tail: + rv.append(element.tail) + + serializeElement(element) + + return "".join(rv) + + class TreeBuilder(base.TreeBuilder): # pylint:disable=unused-variable + documentClass = Document + doctypeClass = DocumentType + elementClass = Element + commentClass = Comment + fragmentClass = DocumentFragment + implementation = ElementTreeImplementation + + def testSerializer(self, element): + return testSerializer(element) + + def getDocument(self): + if fullTree: + return self.document._element + else: + if self.defaultNamespace is not None: + return self.document._element.find( + "{%s}html" % self.defaultNamespace) + else: + return self.document._element.find("html") + + def getFragment(self): + return base.TreeBuilder.getFragment(self)._element + + return locals() + + +getETreeModule = moduleFactoryFactory(getETreeBuilder) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/etree_lxml.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/etree_lxml.py new file mode 100644 index 0000000..908820c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treebuilders/etree_lxml.py @@ -0,0 +1,367 @@ +"""Module for supporting the lxml.etree library. The idea here is to use as much +of the native library as possible, without using fragile hacks like custom element +names that break between releases. The downside of this is that we cannot represent +all possible trees; specifically the following are known to cause problems: + +Text or comments as siblings of the root element +Docypes with no name + +When any of these things occur, we emit a DataLossWarning +""" + +from __future__ import absolute_import, division, unicode_literals +# pylint:disable=protected-access + +import warnings +import re +import sys + +from . import base +from ..constants import DataLossWarning +from .. import constants +from . import etree as etree_builders +from .. import _ihatexml + +import lxml.etree as etree + + +fullTree = True +tag_regexp = re.compile("{([^}]*)}(.*)") + +comment_type = etree.Comment("asd").tag + + +class DocumentType(object): + def __init__(self, name, publicId, systemId): + self.name = name + self.publicId = publicId + self.systemId = systemId + + +class Document(object): + def __init__(self): + self._elementTree = None + self._childNodes = [] + + def appendChild(self, element): + self._elementTree.getroot().addnext(element._element) + + def _getChildNodes(self): + return self._childNodes + + childNodes = property(_getChildNodes) + + +def testSerializer(element): + rv = [] + infosetFilter = _ihatexml.InfosetFilter(preventDoubleDashComments=True) + + def serializeElement(element, indent=0): + if not hasattr(element, "tag"): + if hasattr(element, "getroot"): + # Full tree case + rv.append("#document") + if element.docinfo.internalDTD: + if not (element.docinfo.public_id or + element.docinfo.system_url): + dtd_str = "<!DOCTYPE %s>" % element.docinfo.root_name + else: + dtd_str = """<!DOCTYPE %s "%s" "%s">""" % ( + element.docinfo.root_name, + element.docinfo.public_id, + element.docinfo.system_url) + rv.append("|%s%s" % (' ' * (indent + 2), dtd_str)) + next_element = element.getroot() + while next_element.getprevious() is not None: + next_element = next_element.getprevious() + while next_element is not None: + serializeElement(next_element, indent + 2) + next_element = next_element.getnext() + elif isinstance(element, str) or isinstance(element, bytes): + # Text in a fragment + assert isinstance(element, str) or sys.version_info[0] == 2 + rv.append("|%s\"%s\"" % (' ' * indent, element)) + else: + # Fragment case + rv.append("#document-fragment") + for next_element in element: + serializeElement(next_element, indent + 2) + elif element.tag == comment_type: + rv.append("|%s<!-- %s -->" % (' ' * indent, element.text)) + if hasattr(element, "tail") and element.tail: + rv.append("|%s\"%s\"" % (' ' * indent, element.tail)) + else: + assert isinstance(element, etree._Element) + nsmatch = etree_builders.tag_regexp.match(element.tag) + if nsmatch is not None: + ns = nsmatch.group(1) + tag = nsmatch.group(2) + prefix = constants.prefixes[ns] + rv.append("|%s<%s %s>" % (' ' * indent, prefix, + infosetFilter.fromXmlName(tag))) + else: + rv.append("|%s<%s>" % (' ' * indent, + infosetFilter.fromXmlName(element.tag))) + + if hasattr(element, "attrib"): + attributes = [] + for name, value in element.attrib.items(): + nsmatch = tag_regexp.match(name) + if nsmatch is not None: + ns, name = nsmatch.groups() + name = infosetFilter.fromXmlName(name) + prefix = constants.prefixes[ns] + attr_string = "%s %s" % (prefix, name) + else: + attr_string = infosetFilter.fromXmlName(name) + attributes.append((attr_string, value)) + + for name, value in sorted(attributes): + rv.append('|%s%s="%s"' % (' ' * (indent + 2), name, value)) + + if element.text: + rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text)) + indent += 2 + for child in element: + serializeElement(child, indent) + if hasattr(element, "tail") and element.tail: + rv.append("|%s\"%s\"" % (' ' * (indent - 2), element.tail)) + serializeElement(element, 0) + + return "\n".join(rv) + + +def tostring(element): + """Serialize an element and its child nodes to a string""" + rv = [] + + def serializeElement(element): + if not hasattr(element, "tag"): + if element.docinfo.internalDTD: + if element.docinfo.doctype: + dtd_str = element.docinfo.doctype + else: + dtd_str = "<!DOCTYPE %s>" % element.docinfo.root_name + rv.append(dtd_str) + serializeElement(element.getroot()) + + elif element.tag == comment_type: + rv.append("<!--%s-->" % (element.text,)) + + else: + # This is assumed to be an ordinary element + if not element.attrib: + rv.append("<%s>" % (element.tag,)) + else: + attr = " ".join(["%s=\"%s\"" % (name, value) + for name, value in element.attrib.items()]) + rv.append("<%s %s>" % (element.tag, attr)) + if element.text: + rv.append(element.text) + + for child in element: + serializeElement(child) + + rv.append("</%s>" % (element.tag,)) + + if hasattr(element, "tail") and element.tail: + rv.append(element.tail) + + serializeElement(element) + + return "".join(rv) + + +class TreeBuilder(base.TreeBuilder): + documentClass = Document + doctypeClass = DocumentType + elementClass = None + commentClass = None + fragmentClass = Document + implementation = etree + + def __init__(self, namespaceHTMLElements, fullTree=False): + builder = etree_builders.getETreeModule(etree, fullTree=fullTree) + infosetFilter = self.infosetFilter = _ihatexml.InfosetFilter(preventDoubleDashComments=True) + self.namespaceHTMLElements = namespaceHTMLElements + + class Attributes(dict): + def __init__(self, element, value=None): + if value is None: + value = {} + self._element = element + dict.__init__(self, value) # pylint:disable=non-parent-init-called + for key, value in self.items(): + if isinstance(key, tuple): + name = "{%s}%s" % (key[2], infosetFilter.coerceAttribute(key[1])) + else: + name = infosetFilter.coerceAttribute(key) + self._element._element.attrib[name] = value + + def __setitem__(self, key, value): + dict.__setitem__(self, key, value) + if isinstance(key, tuple): + name = "{%s}%s" % (key[2], infosetFilter.coerceAttribute(key[1])) + else: + name = infosetFilter.coerceAttribute(key) + self._element._element.attrib[name] = value + + class Element(builder.Element): + def __init__(self, name, namespace): + name = infosetFilter.coerceElement(name) + builder.Element.__init__(self, name, namespace=namespace) + self._attributes = Attributes(self) + + def _setName(self, name): + self._name = infosetFilter.coerceElement(name) + self._element.tag = self._getETreeTag( + self._name, self._namespace) + + def _getName(self): + return infosetFilter.fromXmlName(self._name) + + name = property(_getName, _setName) + + def _getAttributes(self): + return self._attributes + + def _setAttributes(self, attributes): + self._attributes = Attributes(self, attributes) + + attributes = property(_getAttributes, _setAttributes) + + def insertText(self, data, insertBefore=None): + data = infosetFilter.coerceCharacters(data) + builder.Element.insertText(self, data, insertBefore) + + def appendChild(self, child): + builder.Element.appendChild(self, child) + + class Comment(builder.Comment): + def __init__(self, data): + data = infosetFilter.coerceComment(data) + builder.Comment.__init__(self, data) + + def _setData(self, data): + data = infosetFilter.coerceComment(data) + self._element.text = data + + def _getData(self): + return self._element.text + + data = property(_getData, _setData) + + self.elementClass = Element + self.commentClass = Comment + # self.fragmentClass = builder.DocumentFragment + base.TreeBuilder.__init__(self, namespaceHTMLElements) + + def reset(self): + base.TreeBuilder.reset(self) + self.insertComment = self.insertCommentInitial + self.initial_comments = [] + self.doctype = None + + def testSerializer(self, element): + return testSerializer(element) + + def getDocument(self): + if fullTree: + return self.document._elementTree + else: + return self.document._elementTree.getroot() + + def getFragment(self): + fragment = [] + element = self.openElements[0]._element + if element.text: + fragment.append(element.text) + fragment.extend(list(element)) + if element.tail: + fragment.append(element.tail) + return fragment + + def insertDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + + if not name: + warnings.warn("lxml cannot represent empty doctype", DataLossWarning) + self.doctype = None + else: + coercedName = self.infosetFilter.coerceElement(name) + if coercedName != name: + warnings.warn("lxml cannot represent non-xml doctype", DataLossWarning) + + doctype = self.doctypeClass(coercedName, publicId, systemId) + self.doctype = doctype + + def insertCommentInitial(self, data, parent=None): + assert parent is None or parent is self.document + assert self.document._elementTree is None + self.initial_comments.append(data) + + def insertCommentMain(self, data, parent=None): + if (parent == self.document and + self.document._elementTree.getroot()[-1].tag == comment_type): + warnings.warn("lxml cannot represent adjacent comments beyond the root elements", DataLossWarning) + super(TreeBuilder, self).insertComment(data, parent) + + def insertRoot(self, token): + """Create the document root""" + # Because of the way libxml2 works, it doesn't seem to be possible to + # alter information like the doctype after the tree has been parsed. + # Therefore we need to use the built-in parser to create our initial + # tree, after which we can add elements like normal + docStr = "" + if self.doctype: + assert self.doctype.name + docStr += "<!DOCTYPE %s" % self.doctype.name + if (self.doctype.publicId is not None or + self.doctype.systemId is not None): + docStr += (' PUBLIC "%s" ' % + (self.infosetFilter.coercePubid(self.doctype.publicId or ""))) + if self.doctype.systemId: + sysid = self.doctype.systemId + if sysid.find("'") >= 0 and sysid.find('"') >= 0: + warnings.warn("DOCTYPE system cannot contain single and double quotes", DataLossWarning) + sysid = sysid.replace("'", 'U00027') + if sysid.find("'") >= 0: + docStr += '"%s"' % sysid + else: + docStr += "'%s'" % sysid + else: + docStr += "''" + docStr += ">" + if self.doctype.name != token["name"]: + warnings.warn("lxml cannot represent doctype with a different name to the root element", DataLossWarning) + docStr += "<THIS_SHOULD_NEVER_APPEAR_PUBLICLY/>" + root = etree.fromstring(docStr) + + # Append the initial comments: + for comment_token in self.initial_comments: + comment = self.commentClass(comment_token["data"]) + root.addprevious(comment._element) + + # Create the root document and add the ElementTree to it + self.document = self.documentClass() + self.document._elementTree = root.getroottree() + + # Give the root element the right name + name = token["name"] + namespace = token.get("namespace", self.defaultNamespace) + if namespace is None: + etree_tag = name + else: + etree_tag = "{%s}%s" % (namespace, name) + root.tag = etree_tag + + # Add the root element to the internal child/open data structures + root_element = self.elementClass(name, namespace) + root_element._element = root + self.document._childNodes.append(root_element) + self.openElements.append(root_element) + + # Reset to the default insert comment function + self.insertComment = self.insertCommentMain diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/__init__.py new file mode 100644 index 0000000..9e19a55 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/__init__.py @@ -0,0 +1,143 @@ +"""A collection of modules for iterating through different kinds of +tree, generating tokens identical to those produced by the tokenizer +module. + +To create a tree walker for a new type of tree, you need to do +implement a tree walker object (called TreeWalker by convention) that +implements a 'serialize' method taking a tree as sole argument and +returning an iterator generating tokens. +""" + +from __future__ import absolute_import, division, unicode_literals + +from .. import constants +from .._utils import default_etree + +__all__ = ["getTreeWalker", "pprint", "dom", "etree", "genshi", "etree_lxml"] + +treeWalkerCache = {} + + +def getTreeWalker(treeType, implementation=None, **kwargs): + """Get a TreeWalker class for various types of tree with built-in support + + Args: + treeType (str): the name of the tree type required (case-insensitive). + Supported values are: + + - "dom": The xml.dom.minidom DOM implementation + - "etree": A generic walker for tree implementations exposing an + elementtree-like interface (known to work with + ElementTree, cElementTree and lxml.etree). + - "lxml": Optimized walker for lxml.etree + - "genshi": a Genshi stream + + Implementation: A module implementing the tree type e.g. + xml.etree.ElementTree or cElementTree (Currently applies to the + "etree" tree type only). + """ + + treeType = treeType.lower() + if treeType not in treeWalkerCache: + if treeType == "dom": + from . import dom + treeWalkerCache[treeType] = dom.TreeWalker + elif treeType == "genshi": + from . import genshi + treeWalkerCache[treeType] = genshi.TreeWalker + elif treeType == "lxml": + from . import etree_lxml + treeWalkerCache[treeType] = etree_lxml.TreeWalker + elif treeType == "etree": + from . import etree + if implementation is None: + implementation = default_etree + # XXX: NEVER cache here, caching is done in the etree submodule + return etree.getETreeModule(implementation, **kwargs).TreeWalker + return treeWalkerCache.get(treeType) + + +def concatenateCharacterTokens(tokens): + pendingCharacters = [] + for token in tokens: + type = token["type"] + if type in ("Characters", "SpaceCharacters"): + pendingCharacters.append(token["data"]) + else: + if pendingCharacters: + yield {"type": "Characters", "data": "".join(pendingCharacters)} + pendingCharacters = [] + yield token + if pendingCharacters: + yield {"type": "Characters", "data": "".join(pendingCharacters)} + + +def pprint(walker): + """Pretty printer for tree walkers""" + output = [] + indent = 0 + for token in concatenateCharacterTokens(walker): + type = token["type"] + if type in ("StartTag", "EmptyTag"): + # tag name + if token["namespace"] and token["namespace"] != constants.namespaces["html"]: + if token["namespace"] in constants.prefixes: + ns = constants.prefixes[token["namespace"]] + else: + ns = token["namespace"] + name = "%s %s" % (ns, token["name"]) + else: + name = token["name"] + output.append("%s<%s>" % (" " * indent, name)) + indent += 2 + # attributes (sorted for consistent ordering) + attrs = token["data"] + for (namespace, localname), value in sorted(attrs.items()): + if namespace: + if namespace in constants.prefixes: + ns = constants.prefixes[namespace] + else: + ns = namespace + name = "%s %s" % (ns, localname) + else: + name = localname + output.append("%s%s=\"%s\"" % (" " * indent, name, value)) + # self-closing + if type == "EmptyTag": + indent -= 2 + + elif type == "EndTag": + indent -= 2 + + elif type == "Comment": + output.append("%s<!-- %s -->" % (" " * indent, token["data"])) + + elif type == "Doctype": + if token["name"]: + if token["publicId"]: + output.append("""%s<!DOCTYPE %s "%s" "%s">""" % + (" " * indent, + token["name"], + token["publicId"], + token["systemId"] if token["systemId"] else "")) + elif token["systemId"]: + output.append("""%s<!DOCTYPE %s "" "%s">""" % + (" " * indent, + token["name"], + token["systemId"])) + else: + output.append("%s<!DOCTYPE %s>" % (" " * indent, + token["name"])) + else: + output.append("%s<!DOCTYPE >" % (" " * indent,)) + + elif type == "Characters": + output.append("%s\"%s\"" % (" " * indent, token["data"])) + + elif type == "SpaceCharacters": + assert False, "concatenateCharacterTokens should have got rid of all Space tokens" + + else: + raise ValueError("Unknown token type, %s" % type) + + return "\n".join(output) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/base.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/base.py new file mode 100644 index 0000000..36e1ba2 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/base.py @@ -0,0 +1,150 @@ +from __future__ import absolute_import, division, unicode_literals + +from xml.dom import Node +from ..constants import namespaces, voidElements, spaceCharacters + +__all__ = ["DOCUMENT", "DOCTYPE", "TEXT", "ELEMENT", "COMMENT", "ENTITY", "UNKNOWN", + "TreeWalker", "NonRecursiveTreeWalker"] + +DOCUMENT = Node.DOCUMENT_NODE +DOCTYPE = Node.DOCUMENT_TYPE_NODE +TEXT = Node.TEXT_NODE +ELEMENT = Node.ELEMENT_NODE +COMMENT = Node.COMMENT_NODE +ENTITY = Node.ENTITY_NODE +UNKNOWN = "<#UNKNOWN#>" + +spaceCharacters = "".join(spaceCharacters) + + +class TreeWalker(object): + def __init__(self, tree): + self.tree = tree + + def __iter__(self): + raise NotImplementedError + + def error(self, msg): + return {"type": "SerializeError", "data": msg} + + def emptyTag(self, namespace, name, attrs, hasChildren=False): + yield {"type": "EmptyTag", "name": name, + "namespace": namespace, + "data": attrs} + if hasChildren: + yield self.error("Void element has children") + + def startTag(self, namespace, name, attrs): + return {"type": "StartTag", + "name": name, + "namespace": namespace, + "data": attrs} + + def endTag(self, namespace, name): + return {"type": "EndTag", + "name": name, + "namespace": namespace} + + def text(self, data): + data = data + middle = data.lstrip(spaceCharacters) + left = data[:len(data) - len(middle)] + if left: + yield {"type": "SpaceCharacters", "data": left} + data = middle + middle = data.rstrip(spaceCharacters) + right = data[len(middle):] + if middle: + yield {"type": "Characters", "data": middle} + if right: + yield {"type": "SpaceCharacters", "data": right} + + def comment(self, data): + return {"type": "Comment", "data": data} + + def doctype(self, name, publicId=None, systemId=None): + return {"type": "Doctype", + "name": name, + "publicId": publicId, + "systemId": systemId} + + def entity(self, name): + return {"type": "Entity", "name": name} + + def unknown(self, nodeType): + return self.error("Unknown node type: " + nodeType) + + +class NonRecursiveTreeWalker(TreeWalker): + def getNodeDetails(self, node): + raise NotImplementedError + + def getFirstChild(self, node): + raise NotImplementedError + + def getNextSibling(self, node): + raise NotImplementedError + + def getParentNode(self, node): + raise NotImplementedError + + def __iter__(self): + currentNode = self.tree + while currentNode is not None: + details = self.getNodeDetails(currentNode) + type, details = details[0], details[1:] + hasChildren = False + + if type == DOCTYPE: + yield self.doctype(*details) + + elif type == TEXT: + for token in self.text(*details): + yield token + + elif type == ELEMENT: + namespace, name, attributes, hasChildren = details + if (not namespace or namespace == namespaces["html"]) and name in voidElements: + for token in self.emptyTag(namespace, name, attributes, + hasChildren): + yield token + hasChildren = False + else: + yield self.startTag(namespace, name, attributes) + + elif type == COMMENT: + yield self.comment(details[0]) + + elif type == ENTITY: + yield self.entity(details[0]) + + elif type == DOCUMENT: + hasChildren = True + + else: + yield self.unknown(details[0]) + + if hasChildren: + firstChild = self.getFirstChild(currentNode) + else: + firstChild = None + + if firstChild is not None: + currentNode = firstChild + else: + while currentNode is not None: + details = self.getNodeDetails(currentNode) + type, details = details[0], details[1:] + if type == ELEMENT: + namespace, name, attributes, hasChildren = details + if (namespace and namespace != namespaces["html"]) or name not in voidElements: + yield self.endTag(namespace, name) + if self.tree is currentNode: + currentNode = None + break + nextSibling = self.getNextSibling(currentNode) + if nextSibling is not None: + currentNode = nextSibling + break + else: + currentNode = self.getParentNode(currentNode) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/dom.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/dom.py new file mode 100644 index 0000000..b0c89b0 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/dom.py @@ -0,0 +1,43 @@ +from __future__ import absolute_import, division, unicode_literals + +from xml.dom import Node + +from . import base + + +class TreeWalker(base.NonRecursiveTreeWalker): + def getNodeDetails(self, node): + if node.nodeType == Node.DOCUMENT_TYPE_NODE: + return base.DOCTYPE, node.name, node.publicId, node.systemId + + elif node.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE): + return base.TEXT, node.nodeValue + + elif node.nodeType == Node.ELEMENT_NODE: + attrs = {} + for attr in list(node.attributes.keys()): + attr = node.getAttributeNode(attr) + if attr.namespaceURI: + attrs[(attr.namespaceURI, attr.localName)] = attr.value + else: + attrs[(None, attr.name)] = attr.value + return (base.ELEMENT, node.namespaceURI, node.nodeName, + attrs, node.hasChildNodes()) + + elif node.nodeType == Node.COMMENT_NODE: + return base.COMMENT, node.nodeValue + + elif node.nodeType in (Node.DOCUMENT_NODE, Node.DOCUMENT_FRAGMENT_NODE): + return (base.DOCUMENT,) + + else: + return base.UNKNOWN, node.nodeType + + def getFirstChild(self, node): + return node.firstChild + + def getNextSibling(self, node): + return node.nextSibling + + def getParentNode(self, node): + return node.parentNode diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/etree.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/etree.py new file mode 100644 index 0000000..bcf17d1 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/etree.py @@ -0,0 +1,137 @@ +from __future__ import absolute_import, division, unicode_literals + +try: + from collections import OrderedDict +except ImportError: + try: + from ordereddict import OrderedDict + except ImportError: + OrderedDict = dict + +import re + +from pip._vendor.six import string_types + +from . import base +from .._utils import moduleFactoryFactory + +tag_regexp = re.compile("{([^}]*)}(.*)") + + +def getETreeBuilder(ElementTreeImplementation): + ElementTree = ElementTreeImplementation + ElementTreeCommentType = ElementTree.Comment("asd").tag + + class TreeWalker(base.NonRecursiveTreeWalker): # pylint:disable=unused-variable + """Given the particular ElementTree representation, this implementation, + to avoid using recursion, returns "nodes" as tuples with the following + content: + + 1. The current element + + 2. The index of the element relative to its parent + + 3. A stack of ancestor elements + + 4. A flag "text", "tail" or None to indicate if the current node is a + text node; either the text or tail of the current element (1) + """ + def getNodeDetails(self, node): + if isinstance(node, tuple): # It might be the root Element + elt, _, _, flag = node + if flag in ("text", "tail"): + return base.TEXT, getattr(elt, flag) + else: + node = elt + + if not(hasattr(node, "tag")): + node = node.getroot() + + if node.tag in ("DOCUMENT_ROOT", "DOCUMENT_FRAGMENT"): + return (base.DOCUMENT,) + + elif node.tag == "<!DOCTYPE>": + return (base.DOCTYPE, node.text, + node.get("publicId"), node.get("systemId")) + + elif node.tag == ElementTreeCommentType: + return base.COMMENT, node.text + + else: + assert isinstance(node.tag, string_types), type(node.tag) + # This is assumed to be an ordinary element + match = tag_regexp.match(node.tag) + if match: + namespace, tag = match.groups() + else: + namespace = None + tag = node.tag + attrs = OrderedDict() + for name, value in list(node.attrib.items()): + match = tag_regexp.match(name) + if match: + attrs[(match.group(1), match.group(2))] = value + else: + attrs[(None, name)] = value + return (base.ELEMENT, namespace, tag, + attrs, len(node) or node.text) + + def getFirstChild(self, node): + if isinstance(node, tuple): + element, key, parents, flag = node + else: + element, key, parents, flag = node, None, [], None + + if flag in ("text", "tail"): + return None + else: + if element.text: + return element, key, parents, "text" + elif len(element): + parents.append(element) + return element[0], 0, parents, None + else: + return None + + def getNextSibling(self, node): + if isinstance(node, tuple): + element, key, parents, flag = node + else: + return None + + if flag == "text": + if len(element): + parents.append(element) + return element[0], 0, parents, None + else: + return None + else: + if element.tail and flag != "tail": + return element, key, parents, "tail" + elif key < len(parents[-1]) - 1: + return parents[-1][key + 1], key + 1, parents, None + else: + return None + + def getParentNode(self, node): + if isinstance(node, tuple): + element, key, parents, flag = node + else: + return None + + if flag == "text": + if not parents: + return element + else: + return element, key, parents, None + else: + parent = parents.pop() + if not parents: + return parent + else: + assert list(parents[-1]).count(parent) == 1 + return parent, list(parents[-1]).index(parent), parents, None + + return locals() + +getETreeModule = moduleFactoryFactory(getETreeBuilder) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/etree_lxml.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/etree_lxml.py new file mode 100644 index 0000000..e81ddf3 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/etree_lxml.py @@ -0,0 +1,213 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +from lxml import etree +from ..treebuilders.etree import tag_regexp + +from . import base + +from .. import _ihatexml + + +def ensure_str(s): + if s is None: + return None + elif isinstance(s, text_type): + return s + else: + return s.decode("ascii", "strict") + + +class Root(object): + def __init__(self, et): + self.elementtree = et + self.children = [] + + try: + if et.docinfo.internalDTD: + self.children.append(Doctype(self, + ensure_str(et.docinfo.root_name), + ensure_str(et.docinfo.public_id), + ensure_str(et.docinfo.system_url))) + except AttributeError: + pass + + try: + node = et.getroot() + except AttributeError: + node = et + + while node.getprevious() is not None: + node = node.getprevious() + while node is not None: + self.children.append(node) + node = node.getnext() + + self.text = None + self.tail = None + + def __getitem__(self, key): + return self.children[key] + + def getnext(self): + return None + + def __len__(self): + return 1 + + +class Doctype(object): + def __init__(self, root_node, name, public_id, system_id): + self.root_node = root_node + self.name = name + self.public_id = public_id + self.system_id = system_id + + self.text = None + self.tail = None + + def getnext(self): + return self.root_node.children[1] + + +class FragmentRoot(Root): + def __init__(self, children): + self.children = [FragmentWrapper(self, child) for child in children] + self.text = self.tail = None + + def getnext(self): + return None + + +class FragmentWrapper(object): + def __init__(self, fragment_root, obj): + self.root_node = fragment_root + self.obj = obj + if hasattr(self.obj, 'text'): + self.text = ensure_str(self.obj.text) + else: + self.text = None + if hasattr(self.obj, 'tail'): + self.tail = ensure_str(self.obj.tail) + else: + self.tail = None + + def __getattr__(self, name): + return getattr(self.obj, name) + + def getnext(self): + siblings = self.root_node.children + idx = siblings.index(self) + if idx < len(siblings) - 1: + return siblings[idx + 1] + else: + return None + + def __getitem__(self, key): + return self.obj[key] + + def __bool__(self): + return bool(self.obj) + + def getparent(self): + return None + + def __str__(self): + return str(self.obj) + + def __unicode__(self): + return str(self.obj) + + def __len__(self): + return len(self.obj) + + +class TreeWalker(base.NonRecursiveTreeWalker): + def __init__(self, tree): + # pylint:disable=redefined-variable-type + if isinstance(tree, list): + self.fragmentChildren = set(tree) + tree = FragmentRoot(tree) + else: + self.fragmentChildren = set() + tree = Root(tree) + base.NonRecursiveTreeWalker.__init__(self, tree) + self.filter = _ihatexml.InfosetFilter() + + def getNodeDetails(self, node): + if isinstance(node, tuple): # Text node + node, key = node + assert key in ("text", "tail"), "Text nodes are text or tail, found %s" % key + return base.TEXT, ensure_str(getattr(node, key)) + + elif isinstance(node, Root): + return (base.DOCUMENT,) + + elif isinstance(node, Doctype): + return base.DOCTYPE, node.name, node.public_id, node.system_id + + elif isinstance(node, FragmentWrapper) and not hasattr(node, "tag"): + return base.TEXT, ensure_str(node.obj) + + elif node.tag == etree.Comment: + return base.COMMENT, ensure_str(node.text) + + elif node.tag == etree.Entity: + return base.ENTITY, ensure_str(node.text)[1:-1] # strip &; + + else: + # This is assumed to be an ordinary element + match = tag_regexp.match(ensure_str(node.tag)) + if match: + namespace, tag = match.groups() + else: + namespace = None + tag = ensure_str(node.tag) + attrs = {} + for name, value in list(node.attrib.items()): + name = ensure_str(name) + value = ensure_str(value) + match = tag_regexp.match(name) + if match: + attrs[(match.group(1), match.group(2))] = value + else: + attrs[(None, name)] = value + return (base.ELEMENT, namespace, self.filter.fromXmlName(tag), + attrs, len(node) > 0 or node.text) + + def getFirstChild(self, node): + assert not isinstance(node, tuple), "Text nodes have no children" + + assert len(node) or node.text, "Node has no children" + if node.text: + return (node, "text") + else: + return node[0] + + def getNextSibling(self, node): + if isinstance(node, tuple): # Text node + node, key = node + assert key in ("text", "tail"), "Text nodes are text or tail, found %s" % key + if key == "text": + # XXX: we cannot use a "bool(node) and node[0] or None" construct here + # because node[0] might evaluate to False if it has no child element + if len(node): + return node[0] + else: + return None + else: # tail + return node.getnext() + + return (node, "tail") if node.tail else node.getnext() + + def getParentNode(self, node): + if isinstance(node, tuple): # Text node + node, key = node + assert key in ("text", "tail"), "Text nodes are text or tail, found %s" % key + if key == "text": + return node + # else: fallback to "normal" processing + elif node in self.fragmentChildren: + return None + + return node.getparent() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/genshi.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/genshi.py new file mode 100644 index 0000000..7483be2 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/html5lib/treewalkers/genshi.py @@ -0,0 +1,69 @@ +from __future__ import absolute_import, division, unicode_literals + +from genshi.core import QName +from genshi.core import START, END, XML_NAMESPACE, DOCTYPE, TEXT +from genshi.core import START_NS, END_NS, START_CDATA, END_CDATA, PI, COMMENT + +from . import base + +from ..constants import voidElements, namespaces + + +class TreeWalker(base.TreeWalker): + def __iter__(self): + # Buffer the events so we can pass in the following one + previous = None + for event in self.tree: + if previous is not None: + for token in self.tokens(previous, event): + yield token + previous = event + + # Don't forget the final event! + if previous is not None: + for token in self.tokens(previous, None): + yield token + + def tokens(self, event, next): + kind, data, _ = event + if kind == START: + tag, attribs = data + name = tag.localname + namespace = tag.namespace + converted_attribs = {} + for k, v in attribs: + if isinstance(k, QName): + converted_attribs[(k.namespace, k.localname)] = v + else: + converted_attribs[(None, k)] = v + + if namespace == namespaces["html"] and name in voidElements: + for token in self.emptyTag(namespace, name, converted_attribs, + not next or next[0] != END or + next[1] != tag): + yield token + else: + yield self.startTag(namespace, name, converted_attribs) + + elif kind == END: + name = data.localname + namespace = data.namespace + if namespace != namespaces["html"] or name not in voidElements: + yield self.endTag(namespace, name) + + elif kind == COMMENT: + yield self.comment(data) + + elif kind == TEXT: + for token in self.text(data): + yield token + + elif kind == DOCTYPE: + yield self.doctype(*data) + + elif kind in (XML_NAMESPACE, DOCTYPE, START_NS, END_NS, + START_CDATA, END_CDATA, PI): + pass + + else: + yield self.unknown(kind) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/ipaddress.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/ipaddress.py new file mode 100644 index 0000000..9cf71a7 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/ipaddress.py @@ -0,0 +1,2425 @@ +# Copyright 2007 Google Inc. +# Licensed to PSF under a Contributor Agreement. + +"""A fast, lightweight IPv4/IPv6 manipulation library in Python. + +This library is used to create/poke/manipulate IPv4 and IPv6 addresses +and networks. + +""" + +from __future__ import unicode_literals + + +import itertools +import struct + +__version__ = '1.0.17' + +# Compatibility functions +_compat_int_types = (int,) +try: + _compat_int_types = (int, long) +except NameError: + pass +try: + _compat_str = unicode +except NameError: + _compat_str = str + assert bytes != str +if b'\0'[0] == 0: # Python 3 semantics + def _compat_bytes_to_byte_vals(byt): + return byt +else: + def _compat_bytes_to_byte_vals(byt): + return [struct.unpack(b'!B', b)[0] for b in byt] +try: + _compat_int_from_byte_vals = int.from_bytes +except AttributeError: + def _compat_int_from_byte_vals(bytvals, endianess): + assert endianess == 'big' + res = 0 + for bv in bytvals: + assert isinstance(bv, _compat_int_types) + res = (res << 8) + bv + return res + + +def _compat_to_bytes(intval, length, endianess): + assert isinstance(intval, _compat_int_types) + assert endianess == 'big' + if length == 4: + if intval < 0 or intval >= 2 ** 32: + raise struct.error("integer out of range for 'I' format code") + return struct.pack(b'!I', intval) + elif length == 16: + if intval < 0 or intval >= 2 ** 128: + raise struct.error("integer out of range for 'QQ' format code") + return struct.pack(b'!QQ', intval >> 64, intval & 0xffffffffffffffff) + else: + raise NotImplementedError() +if hasattr(int, 'bit_length'): + # Not int.bit_length , since that won't work in 2.7 where long exists + def _compat_bit_length(i): + return i.bit_length() +else: + def _compat_bit_length(i): + for res in itertools.count(): + if i >> res == 0: + return res + + +def _compat_range(start, end, step=1): + assert step > 0 + i = start + while i < end: + yield i + i += step + + +class _TotalOrderingMixin(object): + __slots__ = () + + # Helper that derives the other comparison operations from + # __lt__ and __eq__ + # We avoid functools.total_ordering because it doesn't handle + # NotImplemented correctly yet (http://bugs.python.org/issue10042) + def __eq__(self, other): + raise NotImplementedError + + def __ne__(self, other): + equal = self.__eq__(other) + if equal is NotImplemented: + return NotImplemented + return not equal + + def __lt__(self, other): + raise NotImplementedError + + def __le__(self, other): + less = self.__lt__(other) + if less is NotImplemented or not less: + return self.__eq__(other) + return less + + def __gt__(self, other): + less = self.__lt__(other) + if less is NotImplemented: + return NotImplemented + equal = self.__eq__(other) + if equal is NotImplemented: + return NotImplemented + return not (less or equal) + + def __ge__(self, other): + less = self.__lt__(other) + if less is NotImplemented: + return NotImplemented + return not less + + +IPV4LENGTH = 32 +IPV6LENGTH = 128 + + +class AddressValueError(ValueError): + """A Value Error related to the address.""" + + +class NetmaskValueError(ValueError): + """A Value Error related to the netmask.""" + + +def ip_address(address): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP address. Either IPv4 or + IPv6 addresses may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + + Returns: + An IPv4Address or IPv6Address object. + + Raises: + ValueError: if the *address* passed isn't either a v4 or a v6 + address + + """ + try: + return IPv4Address(address) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Address(address) + except (AddressValueError, NetmaskValueError): + pass + + if isinstance(address, bytes): + raise AddressValueError( + '%r does not appear to be an IPv4 or IPv6 address. ' + 'Did you pass in a bytes (str in Python 2) instead of' + ' a unicode object?' % address) + + raise ValueError('%r does not appear to be an IPv4 or IPv6 address' % + address) + + +def ip_network(address, strict=True): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP network. Either IPv4 or + IPv6 networks may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + + Returns: + An IPv4Network or IPv6Network object. + + Raises: + ValueError: if the string passed isn't either a v4 or a v6 + address. Or if the network has host bits set. + + """ + try: + return IPv4Network(address, strict) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Network(address, strict) + except (AddressValueError, NetmaskValueError): + pass + + if isinstance(address, bytes): + raise AddressValueError( + '%r does not appear to be an IPv4 or IPv6 network. ' + 'Did you pass in a bytes (str in Python 2) instead of' + ' a unicode object?' % address) + + raise ValueError('%r does not appear to be an IPv4 or IPv6 network' % + address) + + +def ip_interface(address): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP address. Either IPv4 or + IPv6 addresses may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + + Returns: + An IPv4Interface or IPv6Interface object. + + Raises: + ValueError: if the string passed isn't either a v4 or a v6 + address. + + Notes: + The IPv?Interface classes describe an Address on a particular + Network, so they're basically a combination of both the Address + and Network classes. + + """ + try: + return IPv4Interface(address) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Interface(address) + except (AddressValueError, NetmaskValueError): + pass + + raise ValueError('%r does not appear to be an IPv4 or IPv6 interface' % + address) + + +def v4_int_to_packed(address): + """Represent an address as 4 packed bytes in network (big-endian) order. + + Args: + address: An integer representation of an IPv4 IP address. + + Returns: + The integer address packed as 4 bytes in network (big-endian) order. + + Raises: + ValueError: If the integer is negative or too large to be an + IPv4 IP address. + + """ + try: + return _compat_to_bytes(address, 4, 'big') + except (struct.error, OverflowError): + raise ValueError("Address negative or too large for IPv4") + + +def v6_int_to_packed(address): + """Represent an address as 16 packed bytes in network (big-endian) order. + + Args: + address: An integer representation of an IPv6 IP address. + + Returns: + The integer address packed as 16 bytes in network (big-endian) order. + + """ + try: + return _compat_to_bytes(address, 16, 'big') + except (struct.error, OverflowError): + raise ValueError("Address negative or too large for IPv6") + + +def _split_optional_netmask(address): + """Helper to split the netmask and raise AddressValueError if needed""" + addr = _compat_str(address).split('/') + if len(addr) > 2: + raise AddressValueError("Only one '/' permitted in %r" % address) + return addr + + +def _find_address_range(addresses): + """Find a sequence of sorted deduplicated IPv#Address. + + Args: + addresses: a list of IPv#Address objects. + + Yields: + A tuple containing the first and last IP addresses in the sequence. + + """ + it = iter(addresses) + first = last = next(it) + for ip in it: + if ip._ip != last._ip + 1: + yield first, last + first = ip + last = ip + yield first, last + + +def _count_righthand_zero_bits(number, bits): + """Count the number of zero bits on the right hand side. + + Args: + number: an integer. + bits: maximum number of bits to count. + + Returns: + The number of zero bits on the right hand side of the number. + + """ + if number == 0: + return bits + return min(bits, _compat_bit_length(~number & (number - 1))) + + +def summarize_address_range(first, last): + """Summarize a network range given the first and last IP addresses. + + Example: + >>> list(summarize_address_range(IPv4Address('192.0.2.0'), + ... IPv4Address('192.0.2.130'))) + ... #doctest: +NORMALIZE_WHITESPACE + [IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/31'), + IPv4Network('192.0.2.130/32')] + + Args: + first: the first IPv4Address or IPv6Address in the range. + last: the last IPv4Address or IPv6Address in the range. + + Returns: + An iterator of the summarized IPv(4|6) network objects. + + Raise: + TypeError: + If the first and last objects are not IP addresses. + If the first and last objects are not the same version. + ValueError: + If the last object is not greater than the first. + If the version of the first address is not 4 or 6. + + """ + if (not (isinstance(first, _BaseAddress) and + isinstance(last, _BaseAddress))): + raise TypeError('first and last must be IP addresses, not networks') + if first.version != last.version: + raise TypeError("%s and %s are not of the same version" % ( + first, last)) + if first > last: + raise ValueError('last IP address must be greater than first') + + if first.version == 4: + ip = IPv4Network + elif first.version == 6: + ip = IPv6Network + else: + raise ValueError('unknown IP version') + + ip_bits = first._max_prefixlen + first_int = first._ip + last_int = last._ip + while first_int <= last_int: + nbits = min(_count_righthand_zero_bits(first_int, ip_bits), + _compat_bit_length(last_int - first_int + 1) - 1) + net = ip((first_int, ip_bits - nbits)) + yield net + first_int += 1 << nbits + if first_int - 1 == ip._ALL_ONES: + break + + +def _collapse_addresses_internal(addresses): + """Loops through the addresses, collapsing concurrent netblocks. + + Example: + + ip1 = IPv4Network('192.0.2.0/26') + ip2 = IPv4Network('192.0.2.64/26') + ip3 = IPv4Network('192.0.2.128/26') + ip4 = IPv4Network('192.0.2.192/26') + + _collapse_addresses_internal([ip1, ip2, ip3, ip4]) -> + [IPv4Network('192.0.2.0/24')] + + This shouldn't be called directly; it is called via + collapse_addresses([]). + + Args: + addresses: A list of IPv4Network's or IPv6Network's + + Returns: + A list of IPv4Network's or IPv6Network's depending on what we were + passed. + + """ + # First merge + to_merge = list(addresses) + subnets = {} + while to_merge: + net = to_merge.pop() + supernet = net.supernet() + existing = subnets.get(supernet) + if existing is None: + subnets[supernet] = net + elif existing != net: + # Merge consecutive subnets + del subnets[supernet] + to_merge.append(supernet) + # Then iterate over resulting networks, skipping subsumed subnets + last = None + for net in sorted(subnets.values()): + if last is not None: + # Since they are sorted, + # last.network_address <= net.network_address is a given. + if last.broadcast_address >= net.broadcast_address: + continue + yield net + last = net + + +def collapse_addresses(addresses): + """Collapse a list of IP objects. + + Example: + collapse_addresses([IPv4Network('192.0.2.0/25'), + IPv4Network('192.0.2.128/25')]) -> + [IPv4Network('192.0.2.0/24')] + + Args: + addresses: An iterator of IPv4Network or IPv6Network objects. + + Returns: + An iterator of the collapsed IPv(4|6)Network objects. + + Raises: + TypeError: If passed a list of mixed version objects. + + """ + addrs = [] + ips = [] + nets = [] + + # split IP addresses and networks + for ip in addresses: + if isinstance(ip, _BaseAddress): + if ips and ips[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + ip, ips[-1])) + ips.append(ip) + elif ip._prefixlen == ip._max_prefixlen: + if ips and ips[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + ip, ips[-1])) + try: + ips.append(ip.ip) + except AttributeError: + ips.append(ip.network_address) + else: + if nets and nets[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + ip, nets[-1])) + nets.append(ip) + + # sort and dedup + ips = sorted(set(ips)) + + # find consecutive address ranges in the sorted sequence and summarize them + if ips: + for first, last in _find_address_range(ips): + addrs.extend(summarize_address_range(first, last)) + + return _collapse_addresses_internal(addrs + nets) + + +def get_mixed_type_key(obj): + """Return a key suitable for sorting between networks and addresses. + + Address and Network objects are not sortable by default; they're + fundamentally different so the expression + + IPv4Address('192.0.2.0') <= IPv4Network('192.0.2.0/24') + + doesn't make any sense. There are some times however, where you may wish + to have ipaddress sort these for you anyway. If you need to do this, you + can use this function as the key= argument to sorted(). + + Args: + obj: either a Network or Address object. + Returns: + appropriate key. + + """ + if isinstance(obj, _BaseNetwork): + return obj._get_networks_key() + elif isinstance(obj, _BaseAddress): + return obj._get_address_key() + return NotImplemented + + +class _IPAddressBase(_TotalOrderingMixin): + + """The mother class.""" + + __slots__ = () + + @property + def exploded(self): + """Return the longhand version of the IP address as a string.""" + return self._explode_shorthand_ip_string() + + @property + def compressed(self): + """Return the shorthand version of the IP address as a string.""" + return _compat_str(self) + + @property + def reverse_pointer(self): + """The name of the reverse DNS pointer for the IP address, e.g.: + >>> ipaddress.ip_address("127.0.0.1").reverse_pointer + '1.0.0.127.in-addr.arpa' + >>> ipaddress.ip_address("2001:db8::1").reverse_pointer + '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa' + + """ + return self._reverse_pointer() + + @property + def version(self): + msg = '%200s has no version specified' % (type(self),) + raise NotImplementedError(msg) + + def _check_int_address(self, address): + if address < 0: + msg = "%d (< 0) is not permitted as an IPv%d address" + raise AddressValueError(msg % (address, self._version)) + if address > self._ALL_ONES: + msg = "%d (>= 2**%d) is not permitted as an IPv%d address" + raise AddressValueError(msg % (address, self._max_prefixlen, + self._version)) + + def _check_packed_address(self, address, expected_len): + address_len = len(address) + if address_len != expected_len: + msg = ( + '%r (len %d != %d) is not permitted as an IPv%d address. ' + 'Did you pass in a bytes (str in Python 2) instead of' + ' a unicode object?' + ) + raise AddressValueError(msg % (address, address_len, + expected_len, self._version)) + + @classmethod + def _ip_int_from_prefix(cls, prefixlen): + """Turn the prefix length into a bitwise netmask + + Args: + prefixlen: An integer, the prefix length. + + Returns: + An integer. + + """ + return cls._ALL_ONES ^ (cls._ALL_ONES >> prefixlen) + + @classmethod + def _prefix_from_ip_int(cls, ip_int): + """Return prefix length from the bitwise netmask. + + Args: + ip_int: An integer, the netmask in expanded bitwise format + + Returns: + An integer, the prefix length. + + Raises: + ValueError: If the input intermingles zeroes & ones + """ + trailing_zeroes = _count_righthand_zero_bits(ip_int, + cls._max_prefixlen) + prefixlen = cls._max_prefixlen - trailing_zeroes + leading_ones = ip_int >> trailing_zeroes + all_ones = (1 << prefixlen) - 1 + if leading_ones != all_ones: + byteslen = cls._max_prefixlen // 8 + details = _compat_to_bytes(ip_int, byteslen, 'big') + msg = 'Netmask pattern %r mixes zeroes & ones' + raise ValueError(msg % details) + return prefixlen + + @classmethod + def _report_invalid_netmask(cls, netmask_str): + msg = '%r is not a valid netmask' % netmask_str + raise NetmaskValueError(msg) + + @classmethod + def _prefix_from_prefix_string(cls, prefixlen_str): + """Return prefix length from a numeric string + + Args: + prefixlen_str: The string to be converted + + Returns: + An integer, the prefix length. + + Raises: + NetmaskValueError: If the input is not a valid netmask + """ + # int allows a leading +/- as well as surrounding whitespace, + # so we ensure that isn't the case + if not _BaseV4._DECIMAL_DIGITS.issuperset(prefixlen_str): + cls._report_invalid_netmask(prefixlen_str) + try: + prefixlen = int(prefixlen_str) + except ValueError: + cls._report_invalid_netmask(prefixlen_str) + if not (0 <= prefixlen <= cls._max_prefixlen): + cls._report_invalid_netmask(prefixlen_str) + return prefixlen + + @classmethod + def _prefix_from_ip_string(cls, ip_str): + """Turn a netmask/hostmask string into a prefix length + + Args: + ip_str: The netmask/hostmask to be converted + + Returns: + An integer, the prefix length. + + Raises: + NetmaskValueError: If the input is not a valid netmask/hostmask + """ + # Parse the netmask/hostmask like an IP address. + try: + ip_int = cls._ip_int_from_string(ip_str) + except AddressValueError: + cls._report_invalid_netmask(ip_str) + + # Try matching a netmask (this would be /1*0*/ as a bitwise regexp). + # Note that the two ambiguous cases (all-ones and all-zeroes) are + # treated as netmasks. + try: + return cls._prefix_from_ip_int(ip_int) + except ValueError: + pass + + # Invert the bits, and try matching a /0+1+/ hostmask instead. + ip_int ^= cls._ALL_ONES + try: + return cls._prefix_from_ip_int(ip_int) + except ValueError: + cls._report_invalid_netmask(ip_str) + + def __reduce__(self): + return self.__class__, (_compat_str(self),) + + +class _BaseAddress(_IPAddressBase): + + """A generic IP object. + + This IP class contains the version independent methods which are + used by single IP addresses. + """ + + __slots__ = () + + def __int__(self): + return self._ip + + def __eq__(self, other): + try: + return (self._ip == other._ip and + self._version == other._version) + except AttributeError: + return NotImplemented + + def __lt__(self, other): + if not isinstance(other, _IPAddressBase): + return NotImplemented + if not isinstance(other, _BaseAddress): + raise TypeError('%s and %s are not of the same type' % ( + self, other)) + if self._version != other._version: + raise TypeError('%s and %s are not of the same version' % ( + self, other)) + if self._ip != other._ip: + return self._ip < other._ip + return False + + # Shorthand for Integer addition and subtraction. This is not + # meant to ever support addition/subtraction of addresses. + def __add__(self, other): + if not isinstance(other, _compat_int_types): + return NotImplemented + return self.__class__(int(self) + other) + + def __sub__(self, other): + if not isinstance(other, _compat_int_types): + return NotImplemented + return self.__class__(int(self) - other) + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, _compat_str(self)) + + def __str__(self): + return _compat_str(self._string_from_ip_int(self._ip)) + + def __hash__(self): + return hash(hex(int(self._ip))) + + def _get_address_key(self): + return (self._version, self) + + def __reduce__(self): + return self.__class__, (self._ip,) + + +class _BaseNetwork(_IPAddressBase): + + """A generic IP network object. + + This IP class contains the version independent methods which are + used by networks. + + """ + def __init__(self, address): + self._cache = {} + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, _compat_str(self)) + + def __str__(self): + return '%s/%d' % (self.network_address, self.prefixlen) + + def hosts(self): + """Generate Iterator over usable hosts in a network. + + This is like __iter__ except it doesn't return the network + or broadcast addresses. + + """ + network = int(self.network_address) + broadcast = int(self.broadcast_address) + for x in _compat_range(network + 1, broadcast): + yield self._address_class(x) + + def __iter__(self): + network = int(self.network_address) + broadcast = int(self.broadcast_address) + for x in _compat_range(network, broadcast + 1): + yield self._address_class(x) + + def __getitem__(self, n): + network = int(self.network_address) + broadcast = int(self.broadcast_address) + if n >= 0: + if network + n > broadcast: + raise IndexError('address out of range') + return self._address_class(network + n) + else: + n += 1 + if broadcast + n < network: + raise IndexError('address out of range') + return self._address_class(broadcast + n) + + def __lt__(self, other): + if not isinstance(other, _IPAddressBase): + return NotImplemented + if not isinstance(other, _BaseNetwork): + raise TypeError('%s and %s are not of the same type' % ( + self, other)) + if self._version != other._version: + raise TypeError('%s and %s are not of the same version' % ( + self, other)) + if self.network_address != other.network_address: + return self.network_address < other.network_address + if self.netmask != other.netmask: + return self.netmask < other.netmask + return False + + def __eq__(self, other): + try: + return (self._version == other._version and + self.network_address == other.network_address and + int(self.netmask) == int(other.netmask)) + except AttributeError: + return NotImplemented + + def __hash__(self): + return hash(int(self.network_address) ^ int(self.netmask)) + + def __contains__(self, other): + # always false if one is v4 and the other is v6. + if self._version != other._version: + return False + # dealing with another network. + if isinstance(other, _BaseNetwork): + return False + # dealing with another address + else: + # address + return (int(self.network_address) <= int(other._ip) <= + int(self.broadcast_address)) + + def overlaps(self, other): + """Tell if self is partly contained in other.""" + return self.network_address in other or ( + self.broadcast_address in other or ( + other.network_address in self or ( + other.broadcast_address in self))) + + @property + def broadcast_address(self): + x = self._cache.get('broadcast_address') + if x is None: + x = self._address_class(int(self.network_address) | + int(self.hostmask)) + self._cache['broadcast_address'] = x + return x + + @property + def hostmask(self): + x = self._cache.get('hostmask') + if x is None: + x = self._address_class(int(self.netmask) ^ self._ALL_ONES) + self._cache['hostmask'] = x + return x + + @property + def with_prefixlen(self): + return '%s/%d' % (self.network_address, self._prefixlen) + + @property + def with_netmask(self): + return '%s/%s' % (self.network_address, self.netmask) + + @property + def with_hostmask(self): + return '%s/%s' % (self.network_address, self.hostmask) + + @property + def num_addresses(self): + """Number of hosts in the current subnet.""" + return int(self.broadcast_address) - int(self.network_address) + 1 + + @property + def _address_class(self): + # Returning bare address objects (rather than interfaces) allows for + # more consistent behaviour across the network address, broadcast + # address and individual host addresses. + msg = '%200s has no associated address class' % (type(self),) + raise NotImplementedError(msg) + + @property + def prefixlen(self): + return self._prefixlen + + def address_exclude(self, other): + """Remove an address from a larger block. + + For example: + + addr1 = ip_network('192.0.2.0/28') + addr2 = ip_network('192.0.2.1/32') + list(addr1.address_exclude(addr2)) = + [IPv4Network('192.0.2.0/32'), IPv4Network('192.0.2.2/31'), + IPv4Network('192.0.2.4/30'), IPv4Network('192.0.2.8/29')] + + or IPv6: + + addr1 = ip_network('2001:db8::1/32') + addr2 = ip_network('2001:db8::1/128') + list(addr1.address_exclude(addr2)) = + [ip_network('2001:db8::1/128'), + ip_network('2001:db8::2/127'), + ip_network('2001:db8::4/126'), + ip_network('2001:db8::8/125'), + ... + ip_network('2001:db8:8000::/33')] + + Args: + other: An IPv4Network or IPv6Network object of the same type. + + Returns: + An iterator of the IPv(4|6)Network objects which is self + minus other. + + Raises: + TypeError: If self and other are of differing address + versions, or if other is not a network object. + ValueError: If other is not completely contained by self. + + """ + if not self._version == other._version: + raise TypeError("%s and %s are not of the same version" % ( + self, other)) + + if not isinstance(other, _BaseNetwork): + raise TypeError("%s is not a network object" % other) + + if not other.subnet_of(self): + raise ValueError('%s not contained in %s' % (other, self)) + if other == self: + return + + # Make sure we're comparing the network of other. + other = other.__class__('%s/%s' % (other.network_address, + other.prefixlen)) + + s1, s2 = self.subnets() + while s1 != other and s2 != other: + if other.subnet_of(s1): + yield s2 + s1, s2 = s1.subnets() + elif other.subnet_of(s2): + yield s1 + s1, s2 = s2.subnets() + else: + # If we got here, there's a bug somewhere. + raise AssertionError('Error performing exclusion: ' + 's1: %s s2: %s other: %s' % + (s1, s2, other)) + if s1 == other: + yield s2 + elif s2 == other: + yield s1 + else: + # If we got here, there's a bug somewhere. + raise AssertionError('Error performing exclusion: ' + 's1: %s s2: %s other: %s' % + (s1, s2, other)) + + def compare_networks(self, other): + """Compare two IP objects. + + This is only concerned about the comparison of the integer + representation of the network addresses. This means that the + host bits aren't considered at all in this method. If you want + to compare host bits, you can easily enough do a + 'HostA._ip < HostB._ip' + + Args: + other: An IP object. + + Returns: + If the IP versions of self and other are the same, returns: + + -1 if self < other: + eg: IPv4Network('192.0.2.0/25') < IPv4Network('192.0.2.128/25') + IPv6Network('2001:db8::1000/124') < + IPv6Network('2001:db8::2000/124') + 0 if self == other + eg: IPv4Network('192.0.2.0/24') == IPv4Network('192.0.2.0/24') + IPv6Network('2001:db8::1000/124') == + IPv6Network('2001:db8::1000/124') + 1 if self > other + eg: IPv4Network('192.0.2.128/25') > IPv4Network('192.0.2.0/25') + IPv6Network('2001:db8::2000/124') > + IPv6Network('2001:db8::1000/124') + + Raises: + TypeError if the IP versions are different. + + """ + # does this need to raise a ValueError? + if self._version != other._version: + raise TypeError('%s and %s are not of the same type' % ( + self, other)) + # self._version == other._version below here: + if self.network_address < other.network_address: + return -1 + if self.network_address > other.network_address: + return 1 + # self.network_address == other.network_address below here: + if self.netmask < other.netmask: + return -1 + if self.netmask > other.netmask: + return 1 + return 0 + + def _get_networks_key(self): + """Network-only key function. + + Returns an object that identifies this address' network and + netmask. This function is a suitable "key" argument for sorted() + and list.sort(). + + """ + return (self._version, self.network_address, self.netmask) + + def subnets(self, prefixlen_diff=1, new_prefix=None): + """The subnets which join to make the current subnet. + + In the case that self contains only one IP + (self._prefixlen == 32 for IPv4 or self._prefixlen == 128 + for IPv6), yield an iterator with just ourself. + + Args: + prefixlen_diff: An integer, the amount the prefix length + should be increased by. This should not be set if + new_prefix is also set. + new_prefix: The desired new prefix length. This must be a + larger number (smaller prefix) than the existing prefix. + This should not be set if prefixlen_diff is also set. + + Returns: + An iterator of IPv(4|6) objects. + + Raises: + ValueError: The prefixlen_diff is too small or too large. + OR + prefixlen_diff and new_prefix are both set or new_prefix + is a smaller number than the current prefix (smaller + number means a larger network) + + """ + if self._prefixlen == self._max_prefixlen: + yield self + return + + if new_prefix is not None: + if new_prefix < self._prefixlen: + raise ValueError('new prefix must be longer') + if prefixlen_diff != 1: + raise ValueError('cannot set prefixlen_diff and new_prefix') + prefixlen_diff = new_prefix - self._prefixlen + + if prefixlen_diff < 0: + raise ValueError('prefix length diff must be > 0') + new_prefixlen = self._prefixlen + prefixlen_diff + + if new_prefixlen > self._max_prefixlen: + raise ValueError( + 'prefix length diff %d is invalid for netblock %s' % ( + new_prefixlen, self)) + + start = int(self.network_address) + end = int(self.broadcast_address) + 1 + step = (int(self.hostmask) + 1) >> prefixlen_diff + for new_addr in _compat_range(start, end, step): + current = self.__class__((new_addr, new_prefixlen)) + yield current + + def supernet(self, prefixlen_diff=1, new_prefix=None): + """The supernet containing the current network. + + Args: + prefixlen_diff: An integer, the amount the prefix length of + the network should be decreased by. For example, given a + /24 network and a prefixlen_diff of 3, a supernet with a + /21 netmask is returned. + + Returns: + An IPv4 network object. + + Raises: + ValueError: If self.prefixlen - prefixlen_diff < 0. I.e., you have + a negative prefix length. + OR + If prefixlen_diff and new_prefix are both set or new_prefix is a + larger number than the current prefix (larger number means a + smaller network) + + """ + if self._prefixlen == 0: + return self + + if new_prefix is not None: + if new_prefix > self._prefixlen: + raise ValueError('new prefix must be shorter') + if prefixlen_diff != 1: + raise ValueError('cannot set prefixlen_diff and new_prefix') + prefixlen_diff = self._prefixlen - new_prefix + + new_prefixlen = self.prefixlen - prefixlen_diff + if new_prefixlen < 0: + raise ValueError( + 'current prefixlen is %d, cannot have a prefixlen_diff of %d' % + (self.prefixlen, prefixlen_diff)) + return self.__class__(( + int(self.network_address) & (int(self.netmask) << prefixlen_diff), + new_prefixlen + )) + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is a multicast address. + See RFC 2373 2.7 for details. + + """ + return (self.network_address.is_multicast and + self.broadcast_address.is_multicast) + + def subnet_of(self, other): + # always false if one is v4 and the other is v6. + if self._version != other._version: + return False + # dealing with another network. + if (hasattr(other, 'network_address') and + hasattr(other, 'broadcast_address')): + return (other.network_address <= self.network_address and + other.broadcast_address >= self.broadcast_address) + # dealing with another address + else: + raise TypeError('Unable to test subnet containment with element ' + 'of type %s' % type(other)) + + def supernet_of(self, other): + # always false if one is v4 and the other is v6. + if self._version != other._version: + return False + # dealing with another network. + if (hasattr(other, 'network_address') and + hasattr(other, 'broadcast_address')): + return (other.network_address >= self.network_address and + other.broadcast_address <= self.broadcast_address) + # dealing with another address + else: + raise TypeError('Unable to test subnet containment with element ' + 'of type %s' % type(other)) + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within one of the + reserved IPv6 Network ranges. + + """ + return (self.network_address.is_reserved and + self.broadcast_address.is_reserved) + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is reserved per RFC 4291. + + """ + return (self.network_address.is_link_local and + self.broadcast_address.is_link_local) + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per + iana-ipv4-special-registry or iana-ipv6-special-registry. + + """ + return (self.network_address.is_private and + self.broadcast_address.is_private) + + @property + def is_global(self): + """Test if this address is allocated for public networks. + + Returns: + A boolean, True if the address is not reserved per + iana-ipv4-special-registry or iana-ipv6-special-registry. + + """ + return not self.is_private + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 2373 2.5.2. + + """ + return (self.network_address.is_unspecified and + self.broadcast_address.is_unspecified) + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback address as defined in + RFC 2373 2.5.3. + + """ + return (self.network_address.is_loopback and + self.broadcast_address.is_loopback) + + +class _BaseV4(object): + + """Base IPv4 object. + + The following methods are used by IPv4 objects in both single IP + addresses and networks. + + """ + + __slots__ = () + _version = 4 + # Equivalent to 255.255.255.255 or 32 bits of 1's. + _ALL_ONES = (2 ** IPV4LENGTH) - 1 + _DECIMAL_DIGITS = frozenset('0123456789') + + # the valid octets for host and netmasks. only useful for IPv4. + _valid_mask_octets = frozenset([255, 254, 252, 248, 240, 224, 192, 128, 0]) + + _max_prefixlen = IPV4LENGTH + # There are only a handful of valid v4 netmasks, so we cache them all + # when constructed (see _make_netmask()). + _netmask_cache = {} + + def _explode_shorthand_ip_string(self): + return _compat_str(self) + + @classmethod + def _make_netmask(cls, arg): + """Make a (netmask, prefix_len) tuple from the given argument. + + Argument can be: + - an integer (the prefix length) + - a string representing the prefix length (e.g. "24") + - a string representing the prefix netmask (e.g. "255.255.255.0") + """ + if arg not in cls._netmask_cache: + if isinstance(arg, _compat_int_types): + prefixlen = arg + else: + try: + # Check for a netmask in prefix length form + prefixlen = cls._prefix_from_prefix_string(arg) + except NetmaskValueError: + # Check for a netmask or hostmask in dotted-quad form. + # This may raise NetmaskValueError. + prefixlen = cls._prefix_from_ip_string(arg) + netmask = IPv4Address(cls._ip_int_from_prefix(prefixlen)) + cls._netmask_cache[arg] = netmask, prefixlen + return cls._netmask_cache[arg] + + @classmethod + def _ip_int_from_string(cls, ip_str): + """Turn the given IP string into an integer for comparison. + + Args: + ip_str: A string, the IP ip_str. + + Returns: + The IP ip_str as an integer. + + Raises: + AddressValueError: if ip_str isn't a valid IPv4 Address. + + """ + if not ip_str: + raise AddressValueError('Address cannot be empty') + + octets = ip_str.split('.') + if len(octets) != 4: + raise AddressValueError("Expected 4 octets in %r" % ip_str) + + try: + return _compat_int_from_byte_vals( + map(cls._parse_octet, octets), 'big') + except ValueError as exc: + raise AddressValueError("%s in %r" % (exc, ip_str)) + + @classmethod + def _parse_octet(cls, octet_str): + """Convert a decimal octet into an integer. + + Args: + octet_str: A string, the number to parse. + + Returns: + The octet as an integer. + + Raises: + ValueError: if the octet isn't strictly a decimal from [0..255]. + + """ + if not octet_str: + raise ValueError("Empty octet not permitted") + # Whitelist the characters, since int() allows a lot of bizarre stuff. + if not cls._DECIMAL_DIGITS.issuperset(octet_str): + msg = "Only decimal digits permitted in %r" + raise ValueError(msg % octet_str) + # We do the length check second, since the invalid character error + # is likely to be more informative for the user + if len(octet_str) > 3: + msg = "At most 3 characters permitted in %r" + raise ValueError(msg % octet_str) + # Convert to integer (we know digits are legal) + octet_int = int(octet_str, 10) + # Any octets that look like they *might* be written in octal, + # and which don't look exactly the same in both octal and + # decimal are rejected as ambiguous + if octet_int > 7 and octet_str[0] == '0': + msg = "Ambiguous (octal/decimal) value in %r not permitted" + raise ValueError(msg % octet_str) + if octet_int > 255: + raise ValueError("Octet %d (> 255) not permitted" % octet_int) + return octet_int + + @classmethod + def _string_from_ip_int(cls, ip_int): + """Turns a 32-bit integer into dotted decimal notation. + + Args: + ip_int: An integer, the IP address. + + Returns: + The IP address as a string in dotted decimal notation. + + """ + return '.'.join(_compat_str(struct.unpack(b'!B', b)[0] + if isinstance(b, bytes) + else b) + for b in _compat_to_bytes(ip_int, 4, 'big')) + + def _is_hostmask(self, ip_str): + """Test if the IP string is a hostmask (rather than a netmask). + + Args: + ip_str: A string, the potential hostmask. + + Returns: + A boolean, True if the IP string is a hostmask. + + """ + bits = ip_str.split('.') + try: + parts = [x for x in map(int, bits) if x in self._valid_mask_octets] + except ValueError: + return False + if len(parts) != len(bits): + return False + if parts[0] < parts[-1]: + return True + return False + + def _reverse_pointer(self): + """Return the reverse DNS pointer name for the IPv4 address. + + This implements the method described in RFC1035 3.5. + + """ + reverse_octets = _compat_str(self).split('.')[::-1] + return '.'.join(reverse_octets) + '.in-addr.arpa' + + @property + def max_prefixlen(self): + return self._max_prefixlen + + @property + def version(self): + return self._version + + +class IPv4Address(_BaseV4, _BaseAddress): + + """Represent and manipulate single IPv4 Addresses.""" + + __slots__ = ('_ip', '__weakref__') + + def __init__(self, address): + + """ + Args: + address: A string or integer representing the IP + + Additionally, an integer can be passed, so + IPv4Address('192.0.2.1') == IPv4Address(3221225985). + or, more generally + IPv4Address(int(IPv4Address('192.0.2.1'))) == + IPv4Address('192.0.2.1') + + Raises: + AddressValueError: If ipaddress isn't a valid IPv4 address. + + """ + # Efficient constructor from integer. + if isinstance(address, _compat_int_types): + self._check_int_address(address) + self._ip = address + return + + # Constructing from a packed address + if isinstance(address, bytes): + self._check_packed_address(address, 4) + bvs = _compat_bytes_to_byte_vals(address) + self._ip = _compat_int_from_byte_vals(bvs, 'big') + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP string. + addr_str = _compat_str(address) + if '/' in addr_str: + raise AddressValueError("Unexpected '/' in %r" % address) + self._ip = self._ip_int_from_string(addr_str) + + @property + def packed(self): + """The binary representation of this address.""" + return v4_int_to_packed(self._ip) + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within the + reserved IPv4 Network range. + + """ + return self in self._constants._reserved_network + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per + iana-ipv4-special-registry. + + """ + return any(self in net for net in self._constants._private_networks) + + @property + def is_global(self): + return ( + self not in self._constants._public_network and + not self.is_private) + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is multicast. + See RFC 3171 for details. + + """ + return self in self._constants._multicast_network + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 5735 3. + + """ + return self == self._constants._unspecified_address + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback per RFC 3330. + + """ + return self in self._constants._loopback_network + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is link-local per RFC 3927. + + """ + return self in self._constants._linklocal_network + + +class IPv4Interface(IPv4Address): + + def __init__(self, address): + if isinstance(address, (bytes, _compat_int_types)): + IPv4Address.__init__(self, address) + self.network = IPv4Network(self._ip) + self._prefixlen = self._max_prefixlen + return + + if isinstance(address, tuple): + IPv4Address.__init__(self, address[0]) + if len(address) > 1: + self._prefixlen = int(address[1]) + else: + self._prefixlen = self._max_prefixlen + + self.network = IPv4Network(address, strict=False) + self.netmask = self.network.netmask + self.hostmask = self.network.hostmask + return + + addr = _split_optional_netmask(address) + IPv4Address.__init__(self, addr[0]) + + self.network = IPv4Network(address, strict=False) + self._prefixlen = self.network._prefixlen + + self.netmask = self.network.netmask + self.hostmask = self.network.hostmask + + def __str__(self): + return '%s/%d' % (self._string_from_ip_int(self._ip), + self.network.prefixlen) + + def __eq__(self, other): + address_equal = IPv4Address.__eq__(self, other) + if not address_equal or address_equal is NotImplemented: + return address_equal + try: + return self.network == other.network + except AttributeError: + # An interface with an associated network is NOT the + # same as an unassociated address. That's why the hash + # takes the extra info into account. + return False + + def __lt__(self, other): + address_less = IPv4Address.__lt__(self, other) + if address_less is NotImplemented: + return NotImplemented + try: + return self.network < other.network + except AttributeError: + # We *do* allow addresses and interfaces to be sorted. The + # unassociated address is considered less than all interfaces. + return False + + def __hash__(self): + return self._ip ^ self._prefixlen ^ int(self.network.network_address) + + __reduce__ = _IPAddressBase.__reduce__ + + @property + def ip(self): + return IPv4Address(self._ip) + + @property + def with_prefixlen(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self._prefixlen) + + @property + def with_netmask(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self.netmask) + + @property + def with_hostmask(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self.hostmask) + + +class IPv4Network(_BaseV4, _BaseNetwork): + + """This class represents and manipulates 32-bit IPv4 network + addresses.. + + Attributes: [examples for IPv4Network('192.0.2.0/27')] + .network_address: IPv4Address('192.0.2.0') + .hostmask: IPv4Address('0.0.0.31') + .broadcast_address: IPv4Address('192.0.2.32') + .netmask: IPv4Address('255.255.255.224') + .prefixlen: 27 + + """ + # Class to use when creating address objects + _address_class = IPv4Address + + def __init__(self, address, strict=True): + + """Instantiate a new IPv4 network object. + + Args: + address: A string or integer representing the IP [& network]. + '192.0.2.0/24' + '192.0.2.0/255.255.255.0' + '192.0.0.2/0.0.0.255' + are all functionally the same in IPv4. Similarly, + '192.0.2.1' + '192.0.2.1/255.255.255.255' + '192.0.2.1/32' + are also functionally equivalent. That is to say, failing to + provide a subnetmask will create an object with a mask of /32. + + If the mask (portion after the / in the argument) is given in + dotted quad form, it is treated as a netmask if it starts with a + non-zero field (e.g. /255.0.0.0 == /8) and as a hostmask if it + starts with a zero field (e.g. 0.255.255.255 == /8), with the + single exception of an all-zero mask which is treated as a + netmask == /0. If no mask is given, a default of /32 is used. + + Additionally, an integer can be passed, so + IPv4Network('192.0.2.1') == IPv4Network(3221225985) + or, more generally + IPv4Interface(int(IPv4Interface('192.0.2.1'))) == + IPv4Interface('192.0.2.1') + + Raises: + AddressValueError: If ipaddress isn't a valid IPv4 address. + NetmaskValueError: If the netmask isn't valid for + an IPv4 address. + ValueError: If strict is True and a network address is not + supplied. + + """ + _BaseNetwork.__init__(self, address) + + # Constructing from a packed address or integer + if isinstance(address, (_compat_int_types, bytes)): + self.network_address = IPv4Address(address) + self.netmask, self._prefixlen = self._make_netmask( + self._max_prefixlen) + # fixme: address/network test here. + return + + if isinstance(address, tuple): + if len(address) > 1: + arg = address[1] + else: + # We weren't given an address[1] + arg = self._max_prefixlen + self.network_address = IPv4Address(address[0]) + self.netmask, self._prefixlen = self._make_netmask(arg) + packed = int(self.network_address) + if packed & int(self.netmask) != packed: + if strict: + raise ValueError('%s has host bits set' % self) + else: + self.network_address = IPv4Address(packed & + int(self.netmask)) + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP prefix string. + addr = _split_optional_netmask(address) + self.network_address = IPv4Address(self._ip_int_from_string(addr[0])) + + if len(addr) == 2: + arg = addr[1] + else: + arg = self._max_prefixlen + self.netmask, self._prefixlen = self._make_netmask(arg) + + if strict: + if (IPv4Address(int(self.network_address) & int(self.netmask)) != + self.network_address): + raise ValueError('%s has host bits set' % self) + self.network_address = IPv4Address(int(self.network_address) & + int(self.netmask)) + + if self._prefixlen == (self._max_prefixlen - 1): + self.hosts = self.__iter__ + + @property + def is_global(self): + """Test if this address is allocated for public networks. + + Returns: + A boolean, True if the address is not reserved per + iana-ipv4-special-registry. + + """ + return (not (self.network_address in IPv4Network('100.64.0.0/10') and + self.broadcast_address in IPv4Network('100.64.0.0/10')) and + not self.is_private) + + +class _IPv4Constants(object): + + _linklocal_network = IPv4Network('169.254.0.0/16') + + _loopback_network = IPv4Network('127.0.0.0/8') + + _multicast_network = IPv4Network('224.0.0.0/4') + + _public_network = IPv4Network('100.64.0.0/10') + + _private_networks = [ + IPv4Network('0.0.0.0/8'), + IPv4Network('10.0.0.0/8'), + IPv4Network('127.0.0.0/8'), + IPv4Network('169.254.0.0/16'), + IPv4Network('172.16.0.0/12'), + IPv4Network('192.0.0.0/29'), + IPv4Network('192.0.0.170/31'), + IPv4Network('192.0.2.0/24'), + IPv4Network('192.168.0.0/16'), + IPv4Network('198.18.0.0/15'), + IPv4Network('198.51.100.0/24'), + IPv4Network('203.0.113.0/24'), + IPv4Network('240.0.0.0/4'), + IPv4Network('255.255.255.255/32'), + ] + + _reserved_network = IPv4Network('240.0.0.0/4') + + _unspecified_address = IPv4Address('0.0.0.0') + + +IPv4Address._constants = _IPv4Constants + + +class _BaseV6(object): + + """Base IPv6 object. + + The following methods are used by IPv6 objects in both single IP + addresses and networks. + + """ + + __slots__ = () + _version = 6 + _ALL_ONES = (2 ** IPV6LENGTH) - 1 + _HEXTET_COUNT = 8 + _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef') + _max_prefixlen = IPV6LENGTH + + # There are only a bunch of valid v6 netmasks, so we cache them all + # when constructed (see _make_netmask()). + _netmask_cache = {} + + @classmethod + def _make_netmask(cls, arg): + """Make a (netmask, prefix_len) tuple from the given argument. + + Argument can be: + - an integer (the prefix length) + - a string representing the prefix length (e.g. "24") + - a string representing the prefix netmask (e.g. "255.255.255.0") + """ + if arg not in cls._netmask_cache: + if isinstance(arg, _compat_int_types): + prefixlen = arg + else: + prefixlen = cls._prefix_from_prefix_string(arg) + netmask = IPv6Address(cls._ip_int_from_prefix(prefixlen)) + cls._netmask_cache[arg] = netmask, prefixlen + return cls._netmask_cache[arg] + + @classmethod + def _ip_int_from_string(cls, ip_str): + """Turn an IPv6 ip_str into an integer. + + Args: + ip_str: A string, the IPv6 ip_str. + + Returns: + An int, the IPv6 address + + Raises: + AddressValueError: if ip_str isn't a valid IPv6 Address. + + """ + if not ip_str: + raise AddressValueError('Address cannot be empty') + + parts = ip_str.split(':') + + # An IPv6 address needs at least 2 colons (3 parts). + _min_parts = 3 + if len(parts) < _min_parts: + msg = "At least %d parts expected in %r" % (_min_parts, ip_str) + raise AddressValueError(msg) + + # If the address has an IPv4-style suffix, convert it to hexadecimal. + if '.' in parts[-1]: + try: + ipv4_int = IPv4Address(parts.pop())._ip + except AddressValueError as exc: + raise AddressValueError("%s in %r" % (exc, ip_str)) + parts.append('%x' % ((ipv4_int >> 16) & 0xFFFF)) + parts.append('%x' % (ipv4_int & 0xFFFF)) + + # An IPv6 address can't have more than 8 colons (9 parts). + # The extra colon comes from using the "::" notation for a single + # leading or trailing zero part. + _max_parts = cls._HEXTET_COUNT + 1 + if len(parts) > _max_parts: + msg = "At most %d colons permitted in %r" % ( + _max_parts - 1, ip_str) + raise AddressValueError(msg) + + # Disregarding the endpoints, find '::' with nothing in between. + # This indicates that a run of zeroes has been skipped. + skip_index = None + for i in _compat_range(1, len(parts) - 1): + if not parts[i]: + if skip_index is not None: + # Can't have more than one '::' + msg = "At most one '::' permitted in %r" % ip_str + raise AddressValueError(msg) + skip_index = i + + # parts_hi is the number of parts to copy from above/before the '::' + # parts_lo is the number of parts to copy from below/after the '::' + if skip_index is not None: + # If we found a '::', then check if it also covers the endpoints. + parts_hi = skip_index + parts_lo = len(parts) - skip_index - 1 + if not parts[0]: + parts_hi -= 1 + if parts_hi: + msg = "Leading ':' only permitted as part of '::' in %r" + raise AddressValueError(msg % ip_str) # ^: requires ^:: + if not parts[-1]: + parts_lo -= 1 + if parts_lo: + msg = "Trailing ':' only permitted as part of '::' in %r" + raise AddressValueError(msg % ip_str) # :$ requires ::$ + parts_skipped = cls._HEXTET_COUNT - (parts_hi + parts_lo) + if parts_skipped < 1: + msg = "Expected at most %d other parts with '::' in %r" + raise AddressValueError(msg % (cls._HEXTET_COUNT - 1, ip_str)) + else: + # Otherwise, allocate the entire address to parts_hi. The + # endpoints could still be empty, but _parse_hextet() will check + # for that. + if len(parts) != cls._HEXTET_COUNT: + msg = "Exactly %d parts expected without '::' in %r" + raise AddressValueError(msg % (cls._HEXTET_COUNT, ip_str)) + if not parts[0]: + msg = "Leading ':' only permitted as part of '::' in %r" + raise AddressValueError(msg % ip_str) # ^: requires ^:: + if not parts[-1]: + msg = "Trailing ':' only permitted as part of '::' in %r" + raise AddressValueError(msg % ip_str) # :$ requires ::$ + parts_hi = len(parts) + parts_lo = 0 + parts_skipped = 0 + + try: + # Now, parse the hextets into a 128-bit integer. + ip_int = 0 + for i in range(parts_hi): + ip_int <<= 16 + ip_int |= cls._parse_hextet(parts[i]) + ip_int <<= 16 * parts_skipped + for i in range(-parts_lo, 0): + ip_int <<= 16 + ip_int |= cls._parse_hextet(parts[i]) + return ip_int + except ValueError as exc: + raise AddressValueError("%s in %r" % (exc, ip_str)) + + @classmethod + def _parse_hextet(cls, hextet_str): + """Convert an IPv6 hextet string into an integer. + + Args: + hextet_str: A string, the number to parse. + + Returns: + The hextet as an integer. + + Raises: + ValueError: if the input isn't strictly a hex number from + [0..FFFF]. + + """ + # Whitelist the characters, since int() allows a lot of bizarre stuff. + if not cls._HEX_DIGITS.issuperset(hextet_str): + raise ValueError("Only hex digits permitted in %r" % hextet_str) + # We do the length check second, since the invalid character error + # is likely to be more informative for the user + if len(hextet_str) > 4: + msg = "At most 4 characters permitted in %r" + raise ValueError(msg % hextet_str) + # Length check means we can skip checking the integer value + return int(hextet_str, 16) + + @classmethod + def _compress_hextets(cls, hextets): + """Compresses a list of hextets. + + Compresses a list of strings, replacing the longest continuous + sequence of "0" in the list with "" and adding empty strings at + the beginning or at the end of the string such that subsequently + calling ":".join(hextets) will produce the compressed version of + the IPv6 address. + + Args: + hextets: A list of strings, the hextets to compress. + + Returns: + A list of strings. + + """ + best_doublecolon_start = -1 + best_doublecolon_len = 0 + doublecolon_start = -1 + doublecolon_len = 0 + for index, hextet in enumerate(hextets): + if hextet == '0': + doublecolon_len += 1 + if doublecolon_start == -1: + # Start of a sequence of zeros. + doublecolon_start = index + if doublecolon_len > best_doublecolon_len: + # This is the longest sequence of zeros so far. + best_doublecolon_len = doublecolon_len + best_doublecolon_start = doublecolon_start + else: + doublecolon_len = 0 + doublecolon_start = -1 + + if best_doublecolon_len > 1: + best_doublecolon_end = (best_doublecolon_start + + best_doublecolon_len) + # For zeros at the end of the address. + if best_doublecolon_end == len(hextets): + hextets += [''] + hextets[best_doublecolon_start:best_doublecolon_end] = [''] + # For zeros at the beginning of the address. + if best_doublecolon_start == 0: + hextets = [''] + hextets + + return hextets + + @classmethod + def _string_from_ip_int(cls, ip_int=None): + """Turns a 128-bit integer into hexadecimal notation. + + Args: + ip_int: An integer, the IP address. + + Returns: + A string, the hexadecimal representation of the address. + + Raises: + ValueError: The address is bigger than 128 bits of all ones. + + """ + if ip_int is None: + ip_int = int(cls._ip) + + if ip_int > cls._ALL_ONES: + raise ValueError('IPv6 address is too large') + + hex_str = '%032x' % ip_int + hextets = ['%x' % int(hex_str[x:x + 4], 16) for x in range(0, 32, 4)] + + hextets = cls._compress_hextets(hextets) + return ':'.join(hextets) + + def _explode_shorthand_ip_string(self): + """Expand a shortened IPv6 address. + + Args: + ip_str: A string, the IPv6 address. + + Returns: + A string, the expanded IPv6 address. + + """ + if isinstance(self, IPv6Network): + ip_str = _compat_str(self.network_address) + elif isinstance(self, IPv6Interface): + ip_str = _compat_str(self.ip) + else: + ip_str = _compat_str(self) + + ip_int = self._ip_int_from_string(ip_str) + hex_str = '%032x' % ip_int + parts = [hex_str[x:x + 4] for x in range(0, 32, 4)] + if isinstance(self, (_BaseNetwork, IPv6Interface)): + return '%s/%d' % (':'.join(parts), self._prefixlen) + return ':'.join(parts) + + def _reverse_pointer(self): + """Return the reverse DNS pointer name for the IPv6 address. + + This implements the method described in RFC3596 2.5. + + """ + reverse_chars = self.exploded[::-1].replace(':', '') + return '.'.join(reverse_chars) + '.ip6.arpa' + + @property + def max_prefixlen(self): + return self._max_prefixlen + + @property + def version(self): + return self._version + + +class IPv6Address(_BaseV6, _BaseAddress): + + """Represent and manipulate single IPv6 Addresses.""" + + __slots__ = ('_ip', '__weakref__') + + def __init__(self, address): + """Instantiate a new IPv6 address object. + + Args: + address: A string or integer representing the IP + + Additionally, an integer can be passed, so + IPv6Address('2001:db8::') == + IPv6Address(42540766411282592856903984951653826560) + or, more generally + IPv6Address(int(IPv6Address('2001:db8::'))) == + IPv6Address('2001:db8::') + + Raises: + AddressValueError: If address isn't a valid IPv6 address. + + """ + # Efficient constructor from integer. + if isinstance(address, _compat_int_types): + self._check_int_address(address) + self._ip = address + return + + # Constructing from a packed address + if isinstance(address, bytes): + self._check_packed_address(address, 16) + bvs = _compat_bytes_to_byte_vals(address) + self._ip = _compat_int_from_byte_vals(bvs, 'big') + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP string. + addr_str = _compat_str(address) + if '/' in addr_str: + raise AddressValueError("Unexpected '/' in %r" % address) + self._ip = self._ip_int_from_string(addr_str) + + @property + def packed(self): + """The binary representation of this address.""" + return v6_int_to_packed(self._ip) + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is a multicast address. + See RFC 2373 2.7 for details. + + """ + return self in self._constants._multicast_network + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within one of the + reserved IPv6 Network ranges. + + """ + return any(self in x for x in self._constants._reserved_networks) + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is reserved per RFC 4291. + + """ + return self in self._constants._linklocal_network + + @property + def is_site_local(self): + """Test if the address is reserved for site-local. + + Note that the site-local address space has been deprecated by RFC 3879. + Use is_private to test if this address is in the space of unique local + addresses as defined by RFC 4193. + + Returns: + A boolean, True if the address is reserved per RFC 3513 2.5.6. + + """ + return self in self._constants._sitelocal_network + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per + iana-ipv6-special-registry. + + """ + return any(self in net for net in self._constants._private_networks) + + @property + def is_global(self): + """Test if this address is allocated for public networks. + + Returns: + A boolean, true if the address is not reserved per + iana-ipv6-special-registry. + + """ + return not self.is_private + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 2373 2.5.2. + + """ + return self._ip == 0 + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback address as defined in + RFC 2373 2.5.3. + + """ + return self._ip == 1 + + @property + def ipv4_mapped(self): + """Return the IPv4 mapped address. + + Returns: + If the IPv6 address is a v4 mapped address, return the + IPv4 mapped address. Return None otherwise. + + """ + if (self._ip >> 32) != 0xFFFF: + return None + return IPv4Address(self._ip & 0xFFFFFFFF) + + @property + def teredo(self): + """Tuple of embedded teredo IPs. + + Returns: + Tuple of the (server, client) IPs or None if the address + doesn't appear to be a teredo address (doesn't start with + 2001::/32) + + """ + if (self._ip >> 96) != 0x20010000: + return None + return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF), + IPv4Address(~self._ip & 0xFFFFFFFF)) + + @property + def sixtofour(self): + """Return the IPv4 6to4 embedded address. + + Returns: + The IPv4 6to4-embedded address if present or None if the + address doesn't appear to contain a 6to4 embedded address. + + """ + if (self._ip >> 112) != 0x2002: + return None + return IPv4Address((self._ip >> 80) & 0xFFFFFFFF) + + +class IPv6Interface(IPv6Address): + + def __init__(self, address): + if isinstance(address, (bytes, _compat_int_types)): + IPv6Address.__init__(self, address) + self.network = IPv6Network(self._ip) + self._prefixlen = self._max_prefixlen + return + if isinstance(address, tuple): + IPv6Address.__init__(self, address[0]) + if len(address) > 1: + self._prefixlen = int(address[1]) + else: + self._prefixlen = self._max_prefixlen + self.network = IPv6Network(address, strict=False) + self.netmask = self.network.netmask + self.hostmask = self.network.hostmask + return + + addr = _split_optional_netmask(address) + IPv6Address.__init__(self, addr[0]) + self.network = IPv6Network(address, strict=False) + self.netmask = self.network.netmask + self._prefixlen = self.network._prefixlen + self.hostmask = self.network.hostmask + + def __str__(self): + return '%s/%d' % (self._string_from_ip_int(self._ip), + self.network.prefixlen) + + def __eq__(self, other): + address_equal = IPv6Address.__eq__(self, other) + if not address_equal or address_equal is NotImplemented: + return address_equal + try: + return self.network == other.network + except AttributeError: + # An interface with an associated network is NOT the + # same as an unassociated address. That's why the hash + # takes the extra info into account. + return False + + def __lt__(self, other): + address_less = IPv6Address.__lt__(self, other) + if address_less is NotImplemented: + return NotImplemented + try: + return self.network < other.network + except AttributeError: + # We *do* allow addresses and interfaces to be sorted. The + # unassociated address is considered less than all interfaces. + return False + + def __hash__(self): + return self._ip ^ self._prefixlen ^ int(self.network.network_address) + + __reduce__ = _IPAddressBase.__reduce__ + + @property + def ip(self): + return IPv6Address(self._ip) + + @property + def with_prefixlen(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self._prefixlen) + + @property + def with_netmask(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self.netmask) + + @property + def with_hostmask(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self.hostmask) + + @property + def is_unspecified(self): + return self._ip == 0 and self.network.is_unspecified + + @property + def is_loopback(self): + return self._ip == 1 and self.network.is_loopback + + +class IPv6Network(_BaseV6, _BaseNetwork): + + """This class represents and manipulates 128-bit IPv6 networks. + + Attributes: [examples for IPv6('2001:db8::1000/124')] + .network_address: IPv6Address('2001:db8::1000') + .hostmask: IPv6Address('::f') + .broadcast_address: IPv6Address('2001:db8::100f') + .netmask: IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0') + .prefixlen: 124 + + """ + + # Class to use when creating address objects + _address_class = IPv6Address + + def __init__(self, address, strict=True): + """Instantiate a new IPv6 Network object. + + Args: + address: A string or integer representing the IPv6 network or the + IP and prefix/netmask. + '2001:db8::/128' + '2001:db8:0000:0000:0000:0000:0000:0000/128' + '2001:db8::' + are all functionally the same in IPv6. That is to say, + failing to provide a subnetmask will create an object with + a mask of /128. + + Additionally, an integer can be passed, so + IPv6Network('2001:db8::') == + IPv6Network(42540766411282592856903984951653826560) + or, more generally + IPv6Network(int(IPv6Network('2001:db8::'))) == + IPv6Network('2001:db8::') + + strict: A boolean. If true, ensure that we have been passed + A true network address, eg, 2001:db8::1000/124 and not an + IP address on a network, eg, 2001:db8::1/124. + + Raises: + AddressValueError: If address isn't a valid IPv6 address. + NetmaskValueError: If the netmask isn't valid for + an IPv6 address. + ValueError: If strict was True and a network address was not + supplied. + + """ + _BaseNetwork.__init__(self, address) + + # Efficient constructor from integer or packed address + if isinstance(address, (bytes, _compat_int_types)): + self.network_address = IPv6Address(address) + self.netmask, self._prefixlen = self._make_netmask( + self._max_prefixlen) + return + + if isinstance(address, tuple): + if len(address) > 1: + arg = address[1] + else: + arg = self._max_prefixlen + self.netmask, self._prefixlen = self._make_netmask(arg) + self.network_address = IPv6Address(address[0]) + packed = int(self.network_address) + if packed & int(self.netmask) != packed: + if strict: + raise ValueError('%s has host bits set' % self) + else: + self.network_address = IPv6Address(packed & + int(self.netmask)) + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP prefix string. + addr = _split_optional_netmask(address) + + self.network_address = IPv6Address(self._ip_int_from_string(addr[0])) + + if len(addr) == 2: + arg = addr[1] + else: + arg = self._max_prefixlen + self.netmask, self._prefixlen = self._make_netmask(arg) + + if strict: + if (IPv6Address(int(self.network_address) & int(self.netmask)) != + self.network_address): + raise ValueError('%s has host bits set' % self) + self.network_address = IPv6Address(int(self.network_address) & + int(self.netmask)) + + if self._prefixlen == (self._max_prefixlen - 1): + self.hosts = self.__iter__ + + def hosts(self): + """Generate Iterator over usable hosts in a network. + + This is like __iter__ except it doesn't return the + Subnet-Router anycast address. + + """ + network = int(self.network_address) + broadcast = int(self.broadcast_address) + for x in _compat_range(network + 1, broadcast + 1): + yield self._address_class(x) + + @property + def is_site_local(self): + """Test if the address is reserved for site-local. + + Note that the site-local address space has been deprecated by RFC 3879. + Use is_private to test if this address is in the space of unique local + addresses as defined by RFC 4193. + + Returns: + A boolean, True if the address is reserved per RFC 3513 2.5.6. + + """ + return (self.network_address.is_site_local and + self.broadcast_address.is_site_local) + + +class _IPv6Constants(object): + + _linklocal_network = IPv6Network('fe80::/10') + + _multicast_network = IPv6Network('ff00::/8') + + _private_networks = [ + IPv6Network('::1/128'), + IPv6Network('::/128'), + IPv6Network('::ffff:0:0/96'), + IPv6Network('100::/64'), + IPv6Network('2001::/23'), + IPv6Network('2001:2::/48'), + IPv6Network('2001:db8::/32'), + IPv6Network('2001:10::/28'), + IPv6Network('fc00::/7'), + IPv6Network('fe80::/10'), + ] + + _reserved_networks = [ + IPv6Network('::/8'), IPv6Network('100::/8'), + IPv6Network('200::/7'), IPv6Network('400::/6'), + IPv6Network('800::/5'), IPv6Network('1000::/4'), + IPv6Network('4000::/3'), IPv6Network('6000::/3'), + IPv6Network('8000::/3'), IPv6Network('A000::/3'), + IPv6Network('C000::/3'), IPv6Network('E000::/4'), + IPv6Network('F000::/5'), IPv6Network('F800::/6'), + IPv6Network('FE00::/9'), + ] + + _sitelocal_network = IPv6Network('fec0::/10') + + +IPv6Address._constants = _IPv6Constants diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/__init__.py new file mode 100644 index 0000000..a6f44a5 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/__init__.py @@ -0,0 +1,347 @@ +# -*- coding: utf-8 -*- + +""" +lockfile.py - Platform-independent advisory file locks. + +Requires Python 2.5 unless you apply 2.4.diff +Locking is done on a per-thread basis instead of a per-process basis. + +Usage: + +>>> lock = LockFile('somefile') +>>> try: +... lock.acquire() +... except AlreadyLocked: +... print 'somefile', 'is locked already.' +... except LockFailed: +... print 'somefile', 'can\\'t be locked.' +... else: +... print 'got lock' +got lock +>>> print lock.is_locked() +True +>>> lock.release() + +>>> lock = LockFile('somefile') +>>> print lock.is_locked() +False +>>> with lock: +... print lock.is_locked() +True +>>> print lock.is_locked() +False + +>>> lock = LockFile('somefile') +>>> # It is okay to lock twice from the same thread... +>>> with lock: +... lock.acquire() +... +>>> # Though no counter is kept, so you can't unlock multiple times... +>>> print lock.is_locked() +False + +Exceptions: + + Error - base class for other exceptions + LockError - base class for all locking exceptions + AlreadyLocked - Another thread or process already holds the lock + LockFailed - Lock failed for some other reason + UnlockError - base class for all unlocking exceptions + AlreadyUnlocked - File was not locked. + NotMyLock - File was locked but not by the current thread/process +""" + +from __future__ import absolute_import + +import functools +import os +import socket +import threading +import warnings + +# Work with PEP8 and non-PEP8 versions of threading module. +if not hasattr(threading, "current_thread"): + threading.current_thread = threading.currentThread +if not hasattr(threading.Thread, "get_name"): + threading.Thread.get_name = threading.Thread.getName + +__all__ = ['Error', 'LockError', 'LockTimeout', 'AlreadyLocked', + 'LockFailed', 'UnlockError', 'NotLocked', 'NotMyLock', + 'LinkFileLock', 'MkdirFileLock', 'SQLiteFileLock', + 'LockBase', 'locked'] + + +class Error(Exception): + """ + Base class for other exceptions. + + >>> try: + ... raise Error + ... except Exception: + ... pass + """ + pass + + +class LockError(Error): + """ + Base class for error arising from attempts to acquire the lock. + + >>> try: + ... raise LockError + ... except Error: + ... pass + """ + pass + + +class LockTimeout(LockError): + """Raised when lock creation fails within a user-defined period of time. + + >>> try: + ... raise LockTimeout + ... except LockError: + ... pass + """ + pass + + +class AlreadyLocked(LockError): + """Some other thread/process is locking the file. + + >>> try: + ... raise AlreadyLocked + ... except LockError: + ... pass + """ + pass + + +class LockFailed(LockError): + """Lock file creation failed for some other reason. + + >>> try: + ... raise LockFailed + ... except LockError: + ... pass + """ + pass + + +class UnlockError(Error): + """ + Base class for errors arising from attempts to release the lock. + + >>> try: + ... raise UnlockError + ... except Error: + ... pass + """ + pass + + +class NotLocked(UnlockError): + """Raised when an attempt is made to unlock an unlocked file. + + >>> try: + ... raise NotLocked + ... except UnlockError: + ... pass + """ + pass + + +class NotMyLock(UnlockError): + """Raised when an attempt is made to unlock a file someone else locked. + + >>> try: + ... raise NotMyLock + ... except UnlockError: + ... pass + """ + pass + + +class _SharedBase(object): + def __init__(self, path): + self.path = path + + def acquire(self, timeout=None): + """ + Acquire the lock. + + * If timeout is omitted (or None), wait forever trying to lock the + file. + + * If timeout > 0, try to acquire the lock for that many seconds. If + the lock period expires and the file is still locked, raise + LockTimeout. + + * If timeout <= 0, raise AlreadyLocked immediately if the file is + already locked. + """ + raise NotImplemented("implement in subclass") + + def release(self): + """ + Release the lock. + + If the file is not locked, raise NotLocked. + """ + raise NotImplemented("implement in subclass") + + def __enter__(self): + """ + Context manager support. + """ + self.acquire() + return self + + def __exit__(self, *_exc): + """ + Context manager support. + """ + self.release() + + def __repr__(self): + return "<%s: %r>" % (self.__class__.__name__, self.path) + + +class LockBase(_SharedBase): + """Base class for platform-specific lock classes.""" + def __init__(self, path, threaded=True, timeout=None): + """ + >>> lock = LockBase('somefile') + >>> lock = LockBase('somefile', threaded=False) + """ + super(LockBase, self).__init__(path) + self.lock_file = os.path.abspath(path) + ".lock" + self.hostname = socket.gethostname() + self.pid = os.getpid() + if threaded: + t = threading.current_thread() + # Thread objects in Python 2.4 and earlier do not have ident + # attrs. Worm around that. + ident = getattr(t, "ident", hash(t)) + self.tname = "-%x" % (ident & 0xffffffff) + else: + self.tname = "" + dirname = os.path.dirname(self.lock_file) + + # unique name is mostly about the current process, but must + # also contain the path -- otherwise, two adjacent locked + # files conflict (one file gets locked, creating lock-file and + # unique file, the other one gets locked, creating lock-file + # and overwriting the already existing lock-file, then one + # gets unlocked, deleting both lock-file and unique file, + # finally the last lock errors out upon releasing. + self.unique_name = os.path.join(dirname, + "%s%s.%s%s" % (self.hostname, + self.tname, + self.pid, + hash(self.path))) + self.timeout = timeout + + def is_locked(self): + """ + Tell whether or not the file is locked. + """ + raise NotImplemented("implement in subclass") + + def i_am_locking(self): + """ + Return True if this object is locking the file. + """ + raise NotImplemented("implement in subclass") + + def break_lock(self): + """ + Remove a lock. Useful if a locking thread failed to unlock. + """ + raise NotImplemented("implement in subclass") + + def __repr__(self): + return "<%s: %r -- %r>" % (self.__class__.__name__, self.unique_name, + self.path) + + +def _fl_helper(cls, mod, *args, **kwds): + warnings.warn("Import from %s module instead of lockfile package" % mod, + DeprecationWarning, stacklevel=2) + # This is a bit funky, but it's only for awhile. The way the unit tests + # are constructed this function winds up as an unbound method, so it + # actually takes three args, not two. We want to toss out self. + if not isinstance(args[0], str): + # We are testing, avoid the first arg + args = args[1:] + if len(args) == 1 and not kwds: + kwds["threaded"] = True + return cls(*args, **kwds) + + +def LinkFileLock(*args, **kwds): + """Factory function provided for backwards compatibility. + + Do not use in new code. Instead, import LinkLockFile from the + lockfile.linklockfile module. + """ + from . import linklockfile + return _fl_helper(linklockfile.LinkLockFile, "lockfile.linklockfile", + *args, **kwds) + + +def MkdirFileLock(*args, **kwds): + """Factory function provided for backwards compatibility. + + Do not use in new code. Instead, import MkdirLockFile from the + lockfile.mkdirlockfile module. + """ + from . import mkdirlockfile + return _fl_helper(mkdirlockfile.MkdirLockFile, "lockfile.mkdirlockfile", + *args, **kwds) + + +def SQLiteFileLock(*args, **kwds): + """Factory function provided for backwards compatibility. + + Do not use in new code. Instead, import SQLiteLockFile from the + lockfile.mkdirlockfile module. + """ + from . import sqlitelockfile + return _fl_helper(sqlitelockfile.SQLiteLockFile, "lockfile.sqlitelockfile", + *args, **kwds) + + +def locked(path, timeout=None): + """Decorator which enables locks for decorated function. + + Arguments: + - path: path for lockfile. + - timeout (optional): Timeout for acquiring lock. + + Usage: + @locked('/var/run/myname', timeout=0) + def myname(...): + ... + """ + def decor(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + lock = FileLock(path, timeout=timeout) + lock.acquire() + try: + return func(*args, **kwargs) + finally: + lock.release() + return wrapper + return decor + + +if hasattr(os, "link"): + from . import linklockfile as _llf + LockFile = _llf.LinkLockFile +else: + from . import mkdirlockfile as _mlf + LockFile = _mlf.MkdirLockFile + +FileLock = LockFile diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/linklockfile.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/linklockfile.py new file mode 100644 index 0000000..2ca9be0 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/linklockfile.py @@ -0,0 +1,73 @@ +from __future__ import absolute_import + +import time +import os + +from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout, + AlreadyLocked) + + +class LinkLockFile(LockBase): + """Lock access to a file using atomic property of link(2). + + >>> lock = LinkLockFile('somefile') + >>> lock = LinkLockFile('somefile', threaded=False) + """ + + def acquire(self, timeout=None): + try: + open(self.unique_name, "wb").close() + except IOError: + raise LockFailed("failed to create %s" % self.unique_name) + + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + while True: + # Try and create a hard link to it. + try: + os.link(self.unique_name, self.lock_file) + except OSError: + # Link creation failed. Maybe we've double-locked? + nlinks = os.stat(self.unique_name).st_nlink + if nlinks == 2: + # The original link plus the one I created == 2. We're + # good to go. + return + else: + # Otherwise the lock creation failed. + if timeout is not None and time.time() > end_time: + os.unlink(self.unique_name) + if timeout > 0: + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + raise AlreadyLocked("%s is already locked" % + self.path) + time.sleep(timeout is not None and timeout / 10 or 0.1) + else: + # Link creation succeeded. We're good to go. + return + + def release(self): + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + elif not os.path.exists(self.unique_name): + raise NotMyLock("%s is locked, but not by me" % self.path) + os.unlink(self.unique_name) + os.unlink(self.lock_file) + + def is_locked(self): + return os.path.exists(self.lock_file) + + def i_am_locking(self): + return (self.is_locked() and + os.path.exists(self.unique_name) and + os.stat(self.unique_name).st_nlink == 2) + + def break_lock(self): + if os.path.exists(self.lock_file): + os.unlink(self.lock_file) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/mkdirlockfile.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/mkdirlockfile.py new file mode 100644 index 0000000..05a8c96 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/mkdirlockfile.py @@ -0,0 +1,84 @@ +from __future__ import absolute_import, division + +import time +import os +import sys +import errno + +from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout, + AlreadyLocked) + + +class MkdirLockFile(LockBase): + """Lock file by creating a directory.""" + def __init__(self, path, threaded=True, timeout=None): + """ + >>> lock = MkdirLockFile('somefile') + >>> lock = MkdirLockFile('somefile', threaded=False) + """ + LockBase.__init__(self, path, threaded, timeout) + # Lock file itself is a directory. Place the unique file name into + # it. + self.unique_name = os.path.join(self.lock_file, + "%s.%s%s" % (self.hostname, + self.tname, + self.pid)) + + def acquire(self, timeout=None): + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + if timeout is None: + wait = 0.1 + else: + wait = max(0, timeout / 10) + + while True: + try: + os.mkdir(self.lock_file) + except OSError: + err = sys.exc_info()[1] + if err.errno == errno.EEXIST: + # Already locked. + if os.path.exists(self.unique_name): + # Already locked by me. + return + if timeout is not None and time.time() > end_time: + if timeout > 0: + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + # Someone else has the lock. + raise AlreadyLocked("%s is already locked" % + self.path) + time.sleep(wait) + else: + # Couldn't create the lock for some other reason + raise LockFailed("failed to create %s" % self.lock_file) + else: + open(self.unique_name, "wb").close() + return + + def release(self): + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + elif not os.path.exists(self.unique_name): + raise NotMyLock("%s is locked, but not by me" % self.path) + os.unlink(self.unique_name) + os.rmdir(self.lock_file) + + def is_locked(self): + return os.path.exists(self.lock_file) + + def i_am_locking(self): + return (self.is_locked() and + os.path.exists(self.unique_name)) + + def break_lock(self): + if os.path.exists(self.lock_file): + for name in os.listdir(self.lock_file): + os.unlink(os.path.join(self.lock_file, name)) + os.rmdir(self.lock_file) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/pidlockfile.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/pidlockfile.py new file mode 100644 index 0000000..069e85b --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/pidlockfile.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- + +# pidlockfile.py +# +# Copyright © 2008–2009 Ben Finney <ben+python@benfinney.id.au> +# +# This is free software: you may copy, modify, and/or distribute this work +# under the terms of the Python Software Foundation License, version 2 or +# later as published by the Python Software Foundation. +# No warranty expressed or implied. See the file LICENSE.PSF-2 for details. + +""" Lockfile behaviour implemented via Unix PID files. + """ + +from __future__ import absolute_import + +import errno +import os +import time + +from . import (LockBase, AlreadyLocked, LockFailed, NotLocked, NotMyLock, + LockTimeout) + + +class PIDLockFile(LockBase): + """ Lockfile implemented as a Unix PID file. + + The lock file is a normal file named by the attribute `path`. + A lock's PID file contains a single line of text, containing + the process ID (PID) of the process that acquired the lock. + + >>> lock = PIDLockFile('somefile') + >>> lock = PIDLockFile('somefile') + """ + + def __init__(self, path, threaded=False, timeout=None): + # pid lockfiles don't support threaded operation, so always force + # False as the threaded arg. + LockBase.__init__(self, path, False, timeout) + self.unique_name = self.path + + def read_pid(self): + """ Get the PID from the lock file. + """ + return read_pid_from_pidfile(self.path) + + def is_locked(self): + """ Test if the lock is currently held. + + The lock is held if the PID file for this lock exists. + + """ + return os.path.exists(self.path) + + def i_am_locking(self): + """ Test if the lock is held by the current process. + + Returns ``True`` if the current process ID matches the + number stored in the PID file. + """ + return self.is_locked() and os.getpid() == self.read_pid() + + def acquire(self, timeout=None): + """ Acquire the lock. + + Creates the PID file for this lock, or raises an error if + the lock could not be acquired. + """ + + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + while True: + try: + write_pid_to_pidfile(self.path) + except OSError as exc: + if exc.errno == errno.EEXIST: + # The lock creation failed. Maybe sleep a bit. + if time.time() > end_time: + if timeout is not None and timeout > 0: + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + raise AlreadyLocked("%s is already locked" % + self.path) + time.sleep(timeout is not None and timeout / 10 or 0.1) + else: + raise LockFailed("failed to create %s" % self.path) + else: + return + + def release(self): + """ Release the lock. + + Removes the PID file to release the lock, or raises an + error if the current process does not hold the lock. + + """ + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + if not self.i_am_locking(): + raise NotMyLock("%s is locked, but not by me" % self.path) + remove_existing_pidfile(self.path) + + def break_lock(self): + """ Break an existing lock. + + Removes the PID file if it already exists, otherwise does + nothing. + + """ + remove_existing_pidfile(self.path) + + +def read_pid_from_pidfile(pidfile_path): + """ Read the PID recorded in the named PID file. + + Read and return the numeric PID recorded as text in the named + PID file. If the PID file cannot be read, or if the content is + not a valid PID, return ``None``. + + """ + pid = None + try: + pidfile = open(pidfile_path, 'r') + except IOError: + pass + else: + # According to the FHS 2.3 section on PID files in /var/run: + # + # The file must consist of the process identifier in + # ASCII-encoded decimal, followed by a newline character. + # + # Programs that read PID files should be somewhat flexible + # in what they accept; i.e., they should ignore extra + # whitespace, leading zeroes, absence of the trailing + # newline, or additional lines in the PID file. + + line = pidfile.readline().strip() + try: + pid = int(line) + except ValueError: + pass + pidfile.close() + + return pid + + +def write_pid_to_pidfile(pidfile_path): + """ Write the PID in the named PID file. + + Get the numeric process ID (“PID”) of the current process + and write it to the named file as a line of text. + + """ + open_flags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY) + open_mode = 0o644 + pidfile_fd = os.open(pidfile_path, open_flags, open_mode) + pidfile = os.fdopen(pidfile_fd, 'w') + + # According to the FHS 2.3 section on PID files in /var/run: + # + # The file must consist of the process identifier in + # ASCII-encoded decimal, followed by a newline character. For + # example, if crond was process number 25, /var/run/crond.pid + # would contain three characters: two, five, and newline. + + pid = os.getpid() + pidfile.write("%s\n" % pid) + pidfile.close() + + +def remove_existing_pidfile(pidfile_path): + """ Remove the named PID file if it exists. + + Removing a PID file that doesn't already exist puts us in the + desired state, so we ignore the condition if the file does not + exist. + + """ + try: + os.remove(pidfile_path) + except OSError as exc: + if exc.errno == errno.ENOENT: + pass + else: + raise diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/sqlitelockfile.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/sqlitelockfile.py new file mode 100644 index 0000000..f997e24 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/sqlitelockfile.py @@ -0,0 +1,156 @@ +from __future__ import absolute_import, division + +import time +import os + +try: + unicode +except NameError: + unicode = str + +from . import LockBase, NotLocked, NotMyLock, LockTimeout, AlreadyLocked + + +class SQLiteLockFile(LockBase): + "Demonstrate SQL-based locking." + + testdb = None + + def __init__(self, path, threaded=True, timeout=None): + """ + >>> lock = SQLiteLockFile('somefile') + >>> lock = SQLiteLockFile('somefile', threaded=False) + """ + LockBase.__init__(self, path, threaded, timeout) + self.lock_file = unicode(self.lock_file) + self.unique_name = unicode(self.unique_name) + + if SQLiteLockFile.testdb is None: + import tempfile + _fd, testdb = tempfile.mkstemp() + os.close(_fd) + os.unlink(testdb) + del _fd, tempfile + SQLiteLockFile.testdb = testdb + + import sqlite3 + self.connection = sqlite3.connect(SQLiteLockFile.testdb) + + c = self.connection.cursor() + try: + c.execute("create table locks" + "(" + " lock_file varchar(32)," + " unique_name varchar(32)" + ")") + except sqlite3.OperationalError: + pass + else: + self.connection.commit() + import atexit + atexit.register(os.unlink, SQLiteLockFile.testdb) + + def acquire(self, timeout=None): + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + if timeout is None: + wait = 0.1 + elif timeout <= 0: + wait = 0 + else: + wait = timeout / 10 + + cursor = self.connection.cursor() + + while True: + if not self.is_locked(): + # Not locked. Try to lock it. + cursor.execute("insert into locks" + " (lock_file, unique_name)" + " values" + " (?, ?)", + (self.lock_file, self.unique_name)) + self.connection.commit() + + # Check to see if we are the only lock holder. + cursor.execute("select * from locks" + " where unique_name = ?", + (self.unique_name,)) + rows = cursor.fetchall() + if len(rows) > 1: + # Nope. Someone else got there. Remove our lock. + cursor.execute("delete from locks" + " where unique_name = ?", + (self.unique_name,)) + self.connection.commit() + else: + # Yup. We're done, so go home. + return + else: + # Check to see if we are the only lock holder. + cursor.execute("select * from locks" + " where unique_name = ?", + (self.unique_name,)) + rows = cursor.fetchall() + if len(rows) == 1: + # We're the locker, so go home. + return + + # Maybe we should wait a bit longer. + if timeout is not None and time.time() > end_time: + if timeout > 0: + # No more waiting. + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + # Someone else has the lock and we are impatient.. + raise AlreadyLocked("%s is already locked" % self.path) + + # Well, okay. We'll give it a bit longer. + time.sleep(wait) + + def release(self): + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + if not self.i_am_locking(): + raise NotMyLock("%s is locked, but not by me (by %s)" % + (self.unique_name, self._who_is_locking())) + cursor = self.connection.cursor() + cursor.execute("delete from locks" + " where unique_name = ?", + (self.unique_name,)) + self.connection.commit() + + def _who_is_locking(self): + cursor = self.connection.cursor() + cursor.execute("select unique_name from locks" + " where lock_file = ?", + (self.lock_file,)) + return cursor.fetchone()[0] + + def is_locked(self): + cursor = self.connection.cursor() + cursor.execute("select * from locks" + " where lock_file = ?", + (self.lock_file,)) + rows = cursor.fetchall() + return not not rows + + def i_am_locking(self): + cursor = self.connection.cursor() + cursor.execute("select * from locks" + " where lock_file = ?" + " and unique_name = ?", + (self.lock_file, self.unique_name)) + return not not cursor.fetchall() + + def break_lock(self): + cursor = self.connection.cursor() + cursor.execute("delete from locks" + " where lock_file = ?", + (self.lock_file,)) + self.connection.commit() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/symlinklockfile.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/symlinklockfile.py new file mode 100644 index 0000000..23b41f5 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/lockfile/symlinklockfile.py @@ -0,0 +1,70 @@ +from __future__ import absolute_import + +import os +import time + +from . import (LockBase, NotLocked, NotMyLock, LockTimeout, + AlreadyLocked) + + +class SymlinkLockFile(LockBase): + """Lock access to a file using symlink(2).""" + + def __init__(self, path, threaded=True, timeout=None): + # super(SymlinkLockFile).__init(...) + LockBase.__init__(self, path, threaded, timeout) + # split it back! + self.unique_name = os.path.split(self.unique_name)[1] + + def acquire(self, timeout=None): + # Hopefully unnecessary for symlink. + # try: + # open(self.unique_name, "wb").close() + # except IOError: + # raise LockFailed("failed to create %s" % self.unique_name) + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + while True: + # Try and create a symbolic link to it. + try: + os.symlink(self.unique_name, self.lock_file) + except OSError: + # Link creation failed. Maybe we've double-locked? + if self.i_am_locking(): + # Linked to out unique name. Proceed. + return + else: + # Otherwise the lock creation failed. + if timeout is not None and time.time() > end_time: + if timeout > 0: + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + raise AlreadyLocked("%s is already locked" % + self.path) + time.sleep(timeout / 10 if timeout is not None else 0.1) + else: + # Link creation succeeded. We're good to go. + return + + def release(self): + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + elif not self.i_am_locking(): + raise NotMyLock("%s is locked, but not by me" % self.path) + os.unlink(self.lock_file) + + def is_locked(self): + return os.path.islink(self.lock_file) + + def i_am_locking(self): + return (os.path.islink(self.lock_file) + and os.readlink(self.lock_file) == self.unique_name) + + def break_lock(self): + if os.path.islink(self.lock_file): # exists && link + os.unlink(self.lock_file) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/ordereddict.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/ordereddict.py new file mode 100644 index 0000000..7242b50 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/ordereddict.py @@ -0,0 +1,127 @@ +# Copyright (c) 2009 Raymond Hettinger +# +# 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. + +from UserDict import DictMixin + +class OrderedDict(dict, DictMixin): + + def __init__(self, *args, **kwds): + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__end + except AttributeError: + self.clear() + self.update(*args, **kwds) + + def clear(self): + self.__end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.__map = {} # key --> [key, prev, next] + dict.clear(self) + + def __setitem__(self, key, value): + if key not in self: + end = self.__end + curr = end[1] + curr[2] = end[1] = self.__map[key] = [key, curr, end] + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + dict.__delitem__(self, key) + key, prev, next = self.__map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.__end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.__end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def popitem(self, last=True): + if not self: + raise KeyError('dictionary is empty') + if last: + key = reversed(self).next() + else: + key = iter(self).next() + value = self.pop(key) + return key, value + + def __reduce__(self): + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__end + del self.__map, self.__end + inst_dict = vars(self).copy() + self.__map, self.__end = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def keys(self): + return list(self) + + setdefault = DictMixin.setdefault + update = DictMixin.update + pop = DictMixin.pop + values = DictMixin.values + items = DictMixin.items + iterkeys = DictMixin.iterkeys + itervalues = DictMixin.itervalues + iteritems = DictMixin.iteritems + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + + def copy(self): + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + if isinstance(other, OrderedDict): + if len(self) != len(other): + return False + for p, q in zip(self.items(), other.items()): + if p != q: + return False + return True + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/__about__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/__about__.py new file mode 100644 index 0000000..95d330e --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/__about__.py @@ -0,0 +1,21 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +__all__ = [ + "__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__", "__license__", "__copyright__", +] + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "16.8" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD or Apache License, Version 2.0" +__copyright__ = "Copyright 2014-2016 %s" % __author__ diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/__init__.py new file mode 100644 index 0000000..5ee6220 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/__init__.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +from .__about__ import ( + __author__, __copyright__, __email__, __license__, __summary__, __title__, + __uri__, __version__ +) + +__all__ = [ + "__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__", "__license__", "__copyright__", +] diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/_compat.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/_compat.py new file mode 100644 index 0000000..210bb80 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/_compat.py @@ -0,0 +1,30 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import sys + + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + +# flake8: noqa + +if PY3: + string_types = str, +else: + string_types = basestring, + + +def with_metaclass(meta, *bases): + """ + Create a base class with a metaclass. + """ + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/_structures.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/_structures.py new file mode 100644 index 0000000..ccc2786 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/_structures.py @@ -0,0 +1,68 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + + +class Infinity(object): + + def __repr__(self): + return "Infinity" + + def __hash__(self): + return hash(repr(self)) + + def __lt__(self, other): + return False + + def __le__(self, other): + return False + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __ne__(self, other): + return not isinstance(other, self.__class__) + + def __gt__(self, other): + return True + + def __ge__(self, other): + return True + + def __neg__(self): + return NegativeInfinity + +Infinity = Infinity() + + +class NegativeInfinity(object): + + def __repr__(self): + return "-Infinity" + + def __hash__(self): + return hash(repr(self)) + + def __lt__(self, other): + return True + + def __le__(self, other): + return True + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __ne__(self, other): + return not isinstance(other, self.__class__) + + def __gt__(self, other): + return False + + def __ge__(self, other): + return False + + def __neg__(self): + return Infinity + +NegativeInfinity = NegativeInfinity() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/markers.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/markers.py new file mode 100644 index 0000000..f9ca1ff --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/markers.py @@ -0,0 +1,303 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import operator +import os +import platform +import sys + +from pip._vendor.pyparsing import ( + ParseException, ParseResults, stringStart, stringEnd, +) +from pip._vendor.pyparsing import ZeroOrMore, Group, Forward, QuotedString +from pip._vendor.pyparsing import Literal as L # noqa + +from ._compat import string_types +from .specifiers import Specifier, InvalidSpecifier + + +__all__ = [ + "InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName", + "Marker", "default_environment", +] + + +class InvalidMarker(ValueError): + """ + An invalid marker was found, users should refer to PEP 508. + """ + + +class UndefinedComparison(ValueError): + """ + An invalid operation was attempted on a value that doesn't support it. + """ + + +class UndefinedEnvironmentName(ValueError): + """ + A name was attempted to be used that does not exist inside of the + environment. + """ + + +class Node(object): + + def __init__(self, value): + self.value = value + + def __str__(self): + return str(self.value) + + def __repr__(self): + return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) + + def serialize(self): + raise NotImplementedError + + +class Variable(Node): + + def serialize(self): + return str(self) + + +class Value(Node): + + def serialize(self): + return '"{0}"'.format(self) + + +class Op(Node): + + def serialize(self): + return str(self) + + +VARIABLE = ( + L("implementation_version") | + L("platform_python_implementation") | + L("implementation_name") | + L("python_full_version") | + L("platform_release") | + L("platform_version") | + L("platform_machine") | + L("platform_system") | + L("python_version") | + L("sys_platform") | + L("os_name") | + L("os.name") | # PEP-345 + L("sys.platform") | # PEP-345 + L("platform.version") | # PEP-345 + L("platform.machine") | # PEP-345 + L("platform.python_implementation") | # PEP-345 + L("python_implementation") | # undocumented setuptools legacy + L("extra") +) +ALIASES = { + 'os.name': 'os_name', + 'sys.platform': 'sys_platform', + 'platform.version': 'platform_version', + 'platform.machine': 'platform_machine', + 'platform.python_implementation': 'platform_python_implementation', + 'python_implementation': 'platform_python_implementation' +} +VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) + +VERSION_CMP = ( + L("===") | + L("==") | + L(">=") | + L("<=") | + L("!=") | + L("~=") | + L(">") | + L("<") +) + +MARKER_OP = VERSION_CMP | L("not in") | L("in") +MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) + +MARKER_VALUE = QuotedString("'") | QuotedString('"') +MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) + +BOOLOP = L("and") | L("or") + +MARKER_VAR = VARIABLE | MARKER_VALUE + +MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) +MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) + +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() + +MARKER_EXPR = Forward() +MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) +MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) + +MARKER = stringStart + MARKER_EXPR + stringEnd + + +def _coerce_parse_result(results): + if isinstance(results, ParseResults): + return [_coerce_parse_result(i) for i in results] + else: + return results + + +def _format_marker(marker, first=True): + assert isinstance(marker, (list, tuple, string_types)) + + # Sometimes we have a structure like [[...]] which is a single item list + # where the single item is itself it's own list. In that case we want skip + # the rest of this function so that we don't get extraneous () on the + # outside. + if (isinstance(marker, list) and len(marker) == 1 and + isinstance(marker[0], (list, tuple))): + return _format_marker(marker[0]) + + if isinstance(marker, list): + inner = (_format_marker(m, first=False) for m in marker) + if first: + return " ".join(inner) + else: + return "(" + " ".join(inner) + ")" + elif isinstance(marker, tuple): + return " ".join([m.serialize() for m in marker]) + else: + return marker + + +_operators = { + "in": lambda lhs, rhs: lhs in rhs, + "not in": lambda lhs, rhs: lhs not in rhs, + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.ge, + ">": operator.gt, +} + + +def _eval_op(lhs, op, rhs): + try: + spec = Specifier("".join([op.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs) + + oper = _operators.get(op.serialize()) + if oper is None: + raise UndefinedComparison( + "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) + ) + + return oper(lhs, rhs) + + +_undefined = object() + + +def _get_env(environment, name): + value = environment.get(name, _undefined) + + if value is _undefined: + raise UndefinedEnvironmentName( + "{0!r} does not exist in evaluation environment.".format(name) + ) + + return value + + +def _evaluate_markers(markers, environment): + groups = [[]] + + for marker in markers: + assert isinstance(marker, (list, tuple, string_types)) + + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + lhs_value = _get_env(environment, lhs.value) + rhs_value = rhs.value + else: + lhs_value = lhs.value + rhs_value = _get_env(environment, rhs.value) + + groups[-1].append(_eval_op(lhs_value, op, rhs_value)) + else: + assert marker in ["and", "or"] + if marker == "or": + groups.append([]) + + return any(all(item) for item in groups) + + +def format_full_version(info): + version = '{0.major}.{0.minor}.{0.micro}'.format(info) + kind = info.releaselevel + if kind != 'final': + version += kind[0] + str(info.serial) + return version + + +def default_environment(): + if hasattr(sys, 'implementation'): + iver = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + else: + iver = '0' + implementation_name = '' + + return { + "implementation_name": implementation_name, + "implementation_version": iver, + "os_name": os.name, + "platform_machine": platform.machine(), + "platform_release": platform.release(), + "platform_system": platform.system(), + "platform_version": platform.version(), + "python_full_version": platform.python_version(), + "platform_python_implementation": platform.python_implementation(), + "python_version": platform.python_version()[:3], + "sys_platform": sys.platform, + } + + +class Marker(object): + + def __init__(self, marker): + try: + self._markers = _coerce_parse_result(MARKER.parseString(marker)) + except ParseException as e: + err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( + marker, marker[e.loc:e.loc + 8]) + raise InvalidMarker(err_str) + + def __str__(self): + return _format_marker(self._markers) + + def __repr__(self): + return "<Marker({0!r})>".format(str(self)) + + def evaluate(self, environment=None): + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + if environment is not None: + current_environment.update(environment) + + return _evaluate_markers(self._markers, current_environment) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/requirements.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/requirements.py new file mode 100644 index 0000000..49a4385 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/requirements.py @@ -0,0 +1,129 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import string +import re + +from pip._vendor.pyparsing import ( + stringStart, stringEnd, originalTextFor, ParseException +) +from pip._vendor.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine +from pip._vendor.pyparsing import Literal as L # noqa +from pip._vendor.six.moves.urllib import parse as urlparse + +from .markers import MARKER_EXPR, Marker +from .specifiers import LegacySpecifier, Specifier, SpecifierSet + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +ALPHANUM = Word(string.ascii_letters + string.digits) + +LBRACKET = L("[").suppress() +RBRACKET = L("]").suppress() +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() +COMMA = L(",").suppress() +SEMICOLON = L(";").suppress() +AT = L("@").suppress() + +PUNCTUATION = Word("-_.") +IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) +IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) + +NAME = IDENTIFIER("name") +EXTRA = IDENTIFIER + +URI = Regex(r'[^ ]+')("url") +URL = (AT + URI) + +EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) +EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") + +VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) +VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) + +VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY +VERSION_MANY = Combine(VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), + joinString=",", adjacent=False)("_raw_spec") +_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) +_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '') + +VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") +VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) + +MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") +MARKER_EXPR.setParseAction( + lambda s, l, t: Marker(s[t._original_start:t._original_end]) +) +MARKER_SEPERATOR = SEMICOLON +MARKER = MARKER_SEPERATOR + MARKER_EXPR + +VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) +URL_AND_MARKER = URL + Optional(MARKER) + +NAMED_REQUIREMENT = \ + NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) + +REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd + + +class Requirement(object): + """Parse a requirement. + + Parse a given requirement string into its parts, such as name, specifier, + URL, and extras. Raises InvalidRequirement on a badly-formed requirement + string. + """ + + # TODO: Can we test whether something is contained within a requirement? + # If so how do we do that? Do we need to test against the _name_ of + # the thing as well as the version? What about the markers? + # TODO: Can we normalize the name and extra name? + + def __init__(self, requirement_string): + try: + req = REQUIREMENT.parseString(requirement_string) + except ParseException as e: + raise InvalidRequirement( + "Invalid requirement, parse error at \"{0!r}\"".format( + requirement_string[e.loc:e.loc + 8])) + + self.name = req.name + if req.url: + parsed_url = urlparse.urlparse(req.url) + if not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc): + raise InvalidRequirement("Invalid URL given") + self.url = req.url + else: + self.url = None + self.extras = set(req.extras.asList() if req.extras else []) + self.specifier = SpecifierSet(req.specifier) + self.marker = req.marker if req.marker else None + + def __str__(self): + parts = [self.name] + + if self.extras: + parts.append("[{0}]".format(",".join(sorted(self.extras)))) + + if self.specifier: + parts.append(str(self.specifier)) + + if self.url: + parts.append("@ {0}".format(self.url)) + + if self.marker: + parts.append("; {0}".format(self.marker)) + + return "".join(parts) + + def __repr__(self): + return "<Requirement({0!r})>".format(str(self)) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/specifiers.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..7f5a76c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/specifiers.py @@ -0,0 +1,774 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import abc +import functools +import itertools +import re + +from ._compat import string_types, with_metaclass +from .version import Version, LegacyVersion, parse + + +class InvalidSpecifier(ValueError): + """ + An invalid specifier was found, users should refer to PEP 440. + """ + + +class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): + + @abc.abstractmethod + def __str__(self): + """ + Returns the str representation of this Specifier like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self): + """ + Returns a hash value for this Specifier like object. + """ + + @abc.abstractmethod + def __eq__(self, other): + """ + Returns a boolean representing whether or not the two Specifier like + objects are equal. + """ + + @abc.abstractmethod + def __ne__(self, other): + """ + Returns a boolean representing whether or not the two Specifier like + objects are not equal. + """ + + @abc.abstractproperty + def prereleases(self): + """ + Returns whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @prereleases.setter + def prereleases(self, value): + """ + Sets whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @abc.abstractmethod + def contains(self, item, prereleases=None): + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter(self, iterable, prereleases=None): + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class _IndividualSpecifier(BaseSpecifier): + + _operators = {} + + def __init__(self, spec="", prereleases=None): + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) + + self._spec = ( + match.group("operator").strip(), + match.group("version").strip(), + ) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + def __repr__(self): + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "<{0}({1!r}{2})>".format( + self.__class__.__name__, + str(self), + pre, + ) + + def __str__(self): + return "{0}{1}".format(*self._spec) + + def __hash__(self): + return hash(self._spec) + + def __eq__(self, other): + if isinstance(other, string_types): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec == other._spec + + def __ne__(self, other): + if isinstance(other, string_types): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec != other._spec + + def _get_operator(self, op): + return getattr(self, "_compare_{0}".format(self._operators[op])) + + def _coerce_version(self, version): + if not isinstance(version, (LegacyVersion, Version)): + version = parse(version) + return version + + @property + def operator(self): + return self._spec[0] + + @property + def version(self): + return self._spec[1] + + @property + def prereleases(self): + return self._prereleases + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version or LegacyVersion, this allows us to have + # a shortcut for ``"2.0" in Specifier(">=2") + item = self._coerce_version(item) + + # Determine if we should be supporting prereleases in this specifier + # or not, if we do not support prereleases than we can short circuit + # logic if this version is a prereleases. + if item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + return self._get_operator(self.operator)(item, self.version) + + def filter(self, iterable, prereleases=None): + yielded = False + found_prereleases = [] + + kw = {"prereleases": prereleases if prereleases is not None else True} + + # Attempt to iterate over all the values in the iterable and if any of + # them match, yield them. + for version in iterable: + parsed_version = self._coerce_version(version) + + if self.contains(parsed_version, **kw): + # If our version is a prerelease, and we were not set to allow + # prereleases, then we'll store it for later incase nothing + # else matches this specifier. + if (parsed_version.is_prerelease and not + (prereleases or self.prereleases)): + found_prereleases.append(version) + # Either this is not a prerelease, or we should have been + # accepting prereleases from the begining. + else: + yielded = True + yield version + + # Now that we've iterated over everything, determine if we've yielded + # any values, and if we have not and we have any prereleases stored up + # then we will go ahead and yield the prereleases. + if not yielded and found_prereleases: + for version in found_prereleases: + yield version + + +class LegacySpecifier(_IndividualSpecifier): + + _regex_str = ( + r""" + (?P<operator>(==|!=|<=|>=|<|>)) + \s* + (?P<version> + [^,;\s)]* # Since this is a "legacy" specifier, and the version + # string can be just about anything, we match everything + # except for whitespace, a semi-colon for marker support, + # a closing paren since versions can be enclosed in + # them, and a comma since it's a version separator. + ) + """ + ) + + _regex = re.compile( + r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + + _operators = { + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + } + + def _coerce_version(self, version): + if not isinstance(version, LegacyVersion): + version = LegacyVersion(str(version)) + return version + + def _compare_equal(self, prospective, spec): + return prospective == self._coerce_version(spec) + + def _compare_not_equal(self, prospective, spec): + return prospective != self._coerce_version(spec) + + def _compare_less_than_equal(self, prospective, spec): + return prospective <= self._coerce_version(spec) + + def _compare_greater_than_equal(self, prospective, spec): + return prospective >= self._coerce_version(spec) + + def _compare_less_than(self, prospective, spec): + return prospective < self._coerce_version(spec) + + def _compare_greater_than(self, prospective, spec): + return prospective > self._coerce_version(spec) + + +def _require_version_compare(fn): + @functools.wraps(fn) + def wrapped(self, prospective, spec): + if not isinstance(prospective, Version): + return False + return fn(self, prospective, spec) + return wrapped + + +class Specifier(_IndividualSpecifier): + + _regex_str = ( + r""" + (?P<operator>(~=|==|!=|<=|>=|<|>|===)) + (?P<version> + (?: + # The identity operators allow for an escape hatch that will + # do an exact string match of the version you wish to install. + # This will not be parsed by PEP 440 and we cannot determine + # any semantic meaning from it. This operator is discouraged + # but included entirely as an escape hatch. + (?<====) # Only match for the identity operator + \s* + [^\s]* # We just match everything, except for whitespace + # since we are only testing for strict identity. + ) + | + (?: + # The (non)equality operators allow for wild card and local + # versions to be specified so we have to define these two + # operators separately to enable that. + (?<===|!=) # Only match for equals and not equals + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + + # You cannot use a wild card and a dev or local version + # together so group them with a | and make them optional. + (?: + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + | + \.\* # Wild card syntax of .* + )? + ) + | + (?: + # The compatible operator requires at least two digits in the + # release segment. + (?<=~=) # Only match for the compatible operator + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + | + (?: + # All other operators only allow a sub set of what the + # (non)equality operators do. Specifically they do not allow + # local versions to be specified nor do they allow the prefix + # matching wild cards. + (?<!==|!=|~=) # We have special cases for these + # operators so we want to make sure they + # don't match here. + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + ) + """ + ) + + _regex = re.compile( + r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + + _operators = { + "~=": "compatible", + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + @_require_version_compare + def _compare_compatible(self, prospective, spec): + # Compatible releases have an equivalent combination of >= and ==. That + # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to + # implement this in terms of the other specifiers instead of + # implementing it ourselves. The only thing we need to do is construct + # the other specifiers. + + # We want everything but the last item in the version, but we want to + # ignore post and dev releases and we want to treat the pre-release as + # it's own separate segment. + prefix = ".".join( + list( + itertools.takewhile( + lambda x: (not x.startswith("post") and not + x.startswith("dev")), + _version_split(spec), + ) + )[:-1] + ) + + # Add the prefix notation to the end of our string + prefix += ".*" + + return (self._get_operator(">=")(prospective, spec) and + self._get_operator("==")(prospective, prefix)) + + @_require_version_compare + def _compare_equal(self, prospective, spec): + # We need special logic to handle prefix matching + if spec.endswith(".*"): + # In the case of prefix matching we want to ignore local segment. + prospective = Version(prospective.public) + # Split the spec out by dots, and pretend that there is an implicit + # dot in between a release segment and a pre-release segment. + spec = _version_split(spec[:-2]) # Remove the trailing .* + + # Split the prospective version out by dots, and pretend that there + # is an implicit dot in between a release segment and a pre-release + # segment. + prospective = _version_split(str(prospective)) + + # Shorten the prospective version to be the same length as the spec + # so that we can determine if the specifier is a prefix of the + # prospective version or not. + prospective = prospective[:len(spec)] + + # Pad out our two sides with zeros so that they both equal the same + # length. + spec, prospective = _pad_version(spec, prospective) + else: + # Convert our spec string into a Version + spec = Version(spec) + + # If the specifier does not have a local segment, then we want to + # act as if the prospective version also does not have a local + # segment. + if not spec.local: + prospective = Version(prospective.public) + + return prospective == spec + + @_require_version_compare + def _compare_not_equal(self, prospective, spec): + return not self._compare_equal(prospective, spec) + + @_require_version_compare + def _compare_less_than_equal(self, prospective, spec): + return prospective <= Version(spec) + + @_require_version_compare + def _compare_greater_than_equal(self, prospective, spec): + return prospective >= Version(spec) + + @_require_version_compare + def _compare_less_than(self, prospective, spec): + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec) + + # Check to see if the prospective version is less than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective < spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a pre-release version, that we do not accept pre-release + # versions for the version mentioned in the specifier (e.g. <3.1 should + # not match 3.1.dev0, but should match 3.0.dev0). + if not spec.is_prerelease and prospective.is_prerelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # less than the spec version *and* it's not a pre-release of the same + # version in the spec. + return True + + @_require_version_compare + def _compare_greater_than(self, prospective, spec): + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec) + + # Check to see if the prospective version is greater than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective > spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a post-release version, that we do not accept + # post-release versions for the version mentioned in the specifier + # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). + if not spec.is_postrelease and prospective.is_postrelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # Ensure that we do not allow a local version of the version mentioned + # in the specifier, which is techincally greater than, to match. + if prospective.local is not None: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # greater than the spec version *and* it's not a pre-release of the + # same version in the spec. + return True + + def _compare_arbitrary(self, prospective, spec): + return str(prospective).lower() == str(spec).lower() + + @property + def prereleases(self): + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Look at all of our specifiers and determine if they are inclusive + # operators, and if they are if they are including an explicit + # prerelease. + operator, version = self._spec + if operator in ["==", ">=", "<=", "~=", "==="]: + # The == specifier can include a trailing .*, if it does we + # want to remove before parsing. + if operator == "==" and version.endswith(".*"): + version = version[:-2] + + # Parse the version, and if it is a pre-release than this + # specifier allows pre-releases. + if parse(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version): + result = [] + for item in version.split("."): + match = _prefix_regex.search(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _pad_version(left, right): + left_split, right_split = [], [] + + # Get the release segment of our versions + left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) + right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) + + # Get the rest of our versions + left_split.append(left[len(left_split[0]):]) + right_split.append(right[len(right_split[0]):]) + + # Insert our padding + left_split.insert( + 1, + ["0"] * max(0, len(right_split[0]) - len(left_split[0])), + ) + right_split.insert( + 1, + ["0"] * max(0, len(left_split[0]) - len(right_split[0])), + ) + + return ( + list(itertools.chain(*left_split)), + list(itertools.chain(*right_split)), + ) + + +class SpecifierSet(BaseSpecifier): + + def __init__(self, specifiers="", prereleases=None): + # Split on , to break each indidivual specifier into it's own item, and + # strip each item to remove leading/trailing whitespace. + specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + # Parsed each individual specifier, attempting first to make it a + # Specifier and falling back to a LegacySpecifier. + parsed = set() + for specifier in specifiers: + try: + parsed.add(Specifier(specifier)) + except InvalidSpecifier: + parsed.add(LegacySpecifier(specifier)) + + # Turn our parsed specifiers into a frozen set and save them for later. + self._specs = frozenset(parsed) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + def __repr__(self): + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "<SpecifierSet({0!r}{1})>".format(str(self), pre) + + def __str__(self): + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self): + return hash(self._specs) + + def __and__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = frozenset(self._specs | other._specs) + + if self._prereleases is None and other._prereleases is not None: + specifier._prereleases = other._prereleases + elif self._prereleases is not None and other._prereleases is None: + specifier._prereleases = self._prereleases + elif self._prereleases == other._prereleases: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease " + "overrides." + ) + + return specifier + + def __eq__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __ne__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs != other._specs + + def __len__(self): + return len(self._specs) + + def __iter__(self): + return iter(self._specs) + + @property + def prereleases(self): + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Ensure that our item is a Version or LegacyVersion instance. + if not isinstance(item, (LegacyVersion, Version)): + item = parse(item) + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # We can determine if we're going to allow pre-releases by looking to + # see if any of the underlying items supports them. If none of them do + # and this item is a pre-release then we do not allow it and we can + # short circuit that here. + # Note: This means that 1.0.dev1 would not be contained in something + # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 + if not prereleases and item.is_prerelease: + return False + + # We simply dispatch to the underlying specs here to make sure that the + # given version is contained within all of them. + # Note: This use of all() here means that an empty set of specifiers + # will always return True, this is an explicit design decision. + return all( + s.contains(item, prereleases=prereleases) + for s in self._specs + ) + + def filter(self, iterable, prereleases=None): + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # If we have any specifiers, then we want to wrap our iterable in the + # filter method for each one, this will act as a logical AND amongst + # each specifier. + if self._specs: + for spec in self._specs: + iterable = spec.filter(iterable, prereleases=bool(prereleases)) + return iterable + # If we do not have any specifiers, then we need to have a rough filter + # which will filter out any pre-releases, unless there are no final + # releases, and which will filter out LegacyVersion in general. + else: + filtered = [] + found_prereleases = [] + + for item in iterable: + # Ensure that we some kind of Version class for this item. + if not isinstance(item, (LegacyVersion, Version)): + parsed_version = parse(item) + else: + parsed_version = item + + # Filter out any item which is parsed as a LegacyVersion + if isinstance(parsed_version, LegacyVersion): + continue + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return found_prereleases + + return filtered diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/utils.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/utils.py new file mode 100644 index 0000000..942387c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/utils.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import re + + +_canonicalize_regex = re.compile(r"[-_.]+") + + +def canonicalize_name(name): + # This is taken from PEP 503. + return _canonicalize_regex.sub("-", name).lower() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/version.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/version.py new file mode 100644 index 0000000..83b5ee8 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/packaging/version.py @@ -0,0 +1,393 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import collections +import itertools +import re + +from ._structures import Infinity + + +__all__ = [ + "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN" +] + + +_Version = collections.namedtuple( + "_Version", + ["epoch", "release", "dev", "pre", "post", "local"], +) + + +def parse(version): + """ + Parse the given version string and return either a :class:`Version` object + or a :class:`LegacyVersion` object depending on if the given version is + a valid PEP 440 version or a legacy version. + """ + try: + return Version(version) + except InvalidVersion: + return LegacyVersion(version) + + +class InvalidVersion(ValueError): + """ + An invalid version was found, users should refer to PEP 440. + """ + + +class _BaseVersion(object): + + def __hash__(self): + return hash(self._key) + + def __lt__(self, other): + return self._compare(other, lambda s, o: s < o) + + def __le__(self, other): + return self._compare(other, lambda s, o: s <= o) + + def __eq__(self, other): + return self._compare(other, lambda s, o: s == o) + + def __ge__(self, other): + return self._compare(other, lambda s, o: s >= o) + + def __gt__(self, other): + return self._compare(other, lambda s, o: s > o) + + def __ne__(self, other): + return self._compare(other, lambda s, o: s != o) + + def _compare(self, other, method): + if not isinstance(other, _BaseVersion): + return NotImplemented + + return method(self._key, other._key) + + +class LegacyVersion(_BaseVersion): + + def __init__(self, version): + self._version = str(version) + self._key = _legacy_cmpkey(self._version) + + def __str__(self): + return self._version + + def __repr__(self): + return "<LegacyVersion({0})>".format(repr(str(self))) + + @property + def public(self): + return self._version + + @property + def base_version(self): + return self._version + + @property + def local(self): + return None + + @property + def is_prerelease(self): + return False + + @property + def is_postrelease(self): + return False + + +_legacy_version_component_re = re.compile( + r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE, +) + +_legacy_version_replacement_map = { + "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@", +} + + +def _parse_version_parts(s): + for part in _legacy_version_component_re.split(s): + part = _legacy_version_replacement_map.get(part, part) + + if not part or part == ".": + continue + + if part[:1] in "0123456789": + # pad for numeric comparison + yield part.zfill(8) + else: + yield "*" + part + + # ensure that alpha/beta/candidate are before final + yield "*final" + + +def _legacy_cmpkey(version): + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch + # greater than or equal to 0. This will effectively put the LegacyVersion, + # which uses the defacto standard originally implemented by setuptools, + # as before all PEP 440 versions. + epoch = -1 + + # This scheme is taken from pkg_resources.parse_version setuptools prior to + # it's adoption of the packaging library. + parts = [] + for part in _parse_version_parts(version.lower()): + if part.startswith("*"): + # remove "-" before a prerelease tag + if part < "*final": + while parts and parts[-1] == "*final-": + parts.pop() + + # remove trailing zeros from each series of numeric parts + while parts and parts[-1] == "00000000": + parts.pop() + + parts.append(part) + parts = tuple(parts) + + return epoch, parts + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse +VERSION_PATTERN = r""" + v? + (?: + (?:(?P<epoch>[0-9]+)!)? # epoch + (?P<release>[0-9]+(?:\.[0-9]+)*) # release segment + (?P<pre> # pre-release + [-_\.]? + (?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview)) + [-_\.]? + (?P<pre_n>[0-9]+)? + )? + (?P<post> # post release + (?:-(?P<post_n1>[0-9]+)) + | + (?: + [-_\.]? + (?P<post_l>post|rev|r) + [-_\.]? + (?P<post_n2>[0-9]+)? + ) + )? + (?P<dev> # dev release + [-_\.]? + (?P<dev_l>dev) + [-_\.]? + (?P<dev_n>[0-9]+)? + )? + ) + (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version +""" + + +class Version(_BaseVersion): + + _regex = re.compile( + r"^\s*" + VERSION_PATTERN + r"\s*$", + re.VERBOSE | re.IGNORECASE, + ) + + def __init__(self, version): + # Validate the version and parse it into pieces + match = self._regex.search(version) + if not match: + raise InvalidVersion("Invalid version: '{0}'".format(version)) + + # Store the parsed out pieces of the version + self._version = _Version( + epoch=int(match.group("epoch")) if match.group("epoch") else 0, + release=tuple(int(i) for i in match.group("release").split(".")), + pre=_parse_letter_version( + match.group("pre_l"), + match.group("pre_n"), + ), + post=_parse_letter_version( + match.group("post_l"), + match.group("post_n1") or match.group("post_n2"), + ), + dev=_parse_letter_version( + match.group("dev_l"), + match.group("dev_n"), + ), + local=_parse_local_version(match.group("local")), + ) + + # Generate a key which will be used for sorting + self._key = _cmpkey( + self._version.epoch, + self._version.release, + self._version.pre, + self._version.post, + self._version.dev, + self._version.local, + ) + + def __repr__(self): + return "<Version({0})>".format(repr(str(self))) + + def __str__(self): + parts = [] + + # Epoch + if self._version.epoch != 0: + parts.append("{0}!".format(self._version.epoch)) + + # Release segment + parts.append(".".join(str(x) for x in self._version.release)) + + # Pre-release + if self._version.pre is not None: + parts.append("".join(str(x) for x in self._version.pre)) + + # Post-release + if self._version.post is not None: + parts.append(".post{0}".format(self._version.post[1])) + + # Development release + if self._version.dev is not None: + parts.append(".dev{0}".format(self._version.dev[1])) + + # Local version segment + if self._version.local is not None: + parts.append( + "+{0}".format(".".join(str(x) for x in self._version.local)) + ) + + return "".join(parts) + + @property + def public(self): + return str(self).split("+", 1)[0] + + @property + def base_version(self): + parts = [] + + # Epoch + if self._version.epoch != 0: + parts.append("{0}!".format(self._version.epoch)) + + # Release segment + parts.append(".".join(str(x) for x in self._version.release)) + + return "".join(parts) + + @property + def local(self): + version_string = str(self) + if "+" in version_string: + return version_string.split("+", 1)[1] + + @property + def is_prerelease(self): + return bool(self._version.dev or self._version.pre) + + @property + def is_postrelease(self): + return bool(self._version.post) + + +def _parse_letter_version(letter, number): + if letter: + # We consider there to be an implicit 0 in a pre-release if there is + # not a numeral associated with it. + if number is None: + number = 0 + + # We normalize any letters to their lower case form + letter = letter.lower() + + # We consider some words to be alternate spellings of other words and + # in those cases we want to normalize the spellings to our preferred + # spelling. + if letter == "alpha": + letter = "a" + elif letter == "beta": + letter = "b" + elif letter in ["c", "pre", "preview"]: + letter = "rc" + elif letter in ["rev", "r"]: + letter = "post" + + return letter, int(number) + if not letter and number: + # We assume if we are given a number, but we are not given a letter + # then this is using the implicit post release syntax (e.g. 1.0-1) + letter = "post" + + return letter, int(number) + + +_local_version_seperators = re.compile(r"[\._-]") + + +def _parse_local_version(local): + """ + Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). + """ + if local is not None: + return tuple( + part.lower() if not part.isdigit() else int(part) + for part in _local_version_seperators.split(local) + ) + + +def _cmpkey(epoch, release, pre, post, dev, local): + # When we compare a release version, we want to compare it with all of the + # trailing zeros removed. So we'll use a reverse the list, drop all the now + # leading zeros until we come to something non zero, then take the rest + # re-reverse it back into the correct order and make it a tuple and use + # that for our sorting key. + release = tuple( + reversed(list( + itertools.dropwhile( + lambda x: x == 0, + reversed(release), + ) + )) + ) + + # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0. + # We'll do this by abusing the pre segment, but we _only_ want to do this + # if there is not a pre or a post segment. If we have one of those then + # the normal sorting rules will handle this case correctly. + if pre is None and post is None and dev is not None: + pre = -Infinity + # Versions without a pre-release (except as noted above) should sort after + # those with one. + elif pre is None: + pre = Infinity + + # Versions without a post segment should sort before those with one. + if post is None: + post = -Infinity + + # Versions without a development segment should sort after those with one. + if dev is None: + dev = Infinity + + if local is None: + # Versions without a local segment should sort before those with one. + local = -Infinity + else: + # Versions with a local segment need that segment parsed to implement + # the sorting rules in PEP440. + # - Alpha numeric segments sort before numeric segments + # - Alpha numeric segments sort lexicographically + # - Numeric segments sort numerically + # - Shorter versions sort before longer versions when the prefixes + # match exactly + local = tuple( + (i, "") if isinstance(i, int) else (-Infinity, i) + for i in local + ) + + return epoch, release, pre, post, dev, local diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/pkg_resources/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/pkg_resources/__init__.py new file mode 100644 index 0000000..b8e598b --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/pkg_resources/__init__.py @@ -0,0 +1,3052 @@ +# coding: utf-8 +""" +Package resource API +-------------------- + +A resource is a logical file contained within a package, or a logical +subdirectory thereof. The package resource API expects resource names +to have their path parts separated with ``/``, *not* whatever the local +path separator is. Do not use os.path operations to manipulate resource +names being passed into the API. + +The package resource API is designed to work with normal filesystem packages, +.egg files, and unpacked .egg files. It can also work in a limited way with +.zip files and with custom PEP 302 loaders that support the ``get_data()`` +method. +""" + +from __future__ import absolute_import + +import sys +import os +import io +import time +import re +import types +import zipfile +import zipimport +import warnings +import stat +import functools +import pkgutil +import operator +import platform +import collections +import plistlib +import email.parser +import tempfile +import textwrap +import itertools +from pkgutil import get_importer + +try: + import _imp +except ImportError: + # Python 3.2 compatibility + import imp as _imp + +from pip._vendor import six +from pip._vendor.six.moves import urllib, map, filter + +# capture these to bypass sandboxing +from os import utime +try: + from os import mkdir, rename, unlink + WRITE_SUPPORT = True +except ImportError: + # no write support, probably under GAE + WRITE_SUPPORT = False + +from os import open as os_open +from os.path import isdir, split + +try: + import importlib.machinery as importlib_machinery + # access attribute to force import under delayed import mechanisms. + importlib_machinery.__name__ +except ImportError: + importlib_machinery = None + +from pip._vendor import appdirs +from pip._vendor import packaging +__import__('pip._vendor.packaging.version') +__import__('pip._vendor.packaging.specifiers') +__import__('pip._vendor.packaging.requirements') +__import__('pip._vendor.packaging.markers') + + +if (3, 0) < sys.version_info < (3, 3): + msg = ( + "Support for Python 3.0-3.2 has been dropped. Future versions " + "will fail here." + ) + warnings.warn(msg) + +# declare some globals that will be defined later to +# satisfy the linters. +require = None +working_set = None + + +class PEP440Warning(RuntimeWarning): + """ + Used when there is an issue with a version or specifier not complying with + PEP 440. + """ + + +class _SetuptoolsVersionMixin(object): + def __hash__(self): + return super(_SetuptoolsVersionMixin, self).__hash__() + + def __lt__(self, other): + if isinstance(other, tuple): + return tuple(self) < other + else: + return super(_SetuptoolsVersionMixin, self).__lt__(other) + + def __le__(self, other): + if isinstance(other, tuple): + return tuple(self) <= other + else: + return super(_SetuptoolsVersionMixin, self).__le__(other) + + def __eq__(self, other): + if isinstance(other, tuple): + return tuple(self) == other + else: + return super(_SetuptoolsVersionMixin, self).__eq__(other) + + def __ge__(self, other): + if isinstance(other, tuple): + return tuple(self) >= other + else: + return super(_SetuptoolsVersionMixin, self).__ge__(other) + + def __gt__(self, other): + if isinstance(other, tuple): + return tuple(self) > other + else: + return super(_SetuptoolsVersionMixin, self).__gt__(other) + + def __ne__(self, other): + if isinstance(other, tuple): + return tuple(self) != other + else: + return super(_SetuptoolsVersionMixin, self).__ne__(other) + + def __getitem__(self, key): + return tuple(self)[key] + + def __iter__(self): + component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE) + replace = { + 'pre': 'c', + 'preview': 'c', + '-': 'final-', + 'rc': 'c', + 'dev': '@', + }.get + + def _parse_version_parts(s): + for part in component_re.split(s): + part = replace(part, part) + if not part or part == '.': + continue + if part[:1] in '0123456789': + # pad for numeric comparison + yield part.zfill(8) + else: + yield '*' + part + + # ensure that alpha/beta/candidate are before final + yield '*final' + + def old_parse_version(s): + parts = [] + for part in _parse_version_parts(s.lower()): + if part.startswith('*'): + # remove '-' before a prerelease tag + if part < '*final': + while parts and parts[-1] == '*final-': + parts.pop() + # remove trailing zeros from each series of numeric parts + while parts and parts[-1] == '00000000': + parts.pop() + parts.append(part) + return tuple(parts) + + # Warn for use of this function + warnings.warn( + "You have iterated over the result of " + "pkg_resources.parse_version. This is a legacy behavior which is " + "inconsistent with the new version class introduced in setuptools " + "8.0. In most cases, conversion to a tuple is unnecessary. For " + "comparison of versions, sort the Version instances directly. If " + "you have another use case requiring the tuple, please file a " + "bug with the setuptools project describing that need.", + RuntimeWarning, + stacklevel=1, + ) + + for part in old_parse_version(str(self)): + yield part + + +class SetuptoolsVersion(_SetuptoolsVersionMixin, packaging.version.Version): + pass + + +class SetuptoolsLegacyVersion(_SetuptoolsVersionMixin, + packaging.version.LegacyVersion): + pass + + +def parse_version(v): + try: + return SetuptoolsVersion(v) + except packaging.version.InvalidVersion: + return SetuptoolsLegacyVersion(v) + + +_state_vars = {} + + +def _declare_state(vartype, **kw): + globals().update(kw) + _state_vars.update(dict.fromkeys(kw, vartype)) + + +def __getstate__(): + state = {} + g = globals() + for k, v in _state_vars.items(): + state[k] = g['_sget_' + v](g[k]) + return state + + +def __setstate__(state): + g = globals() + for k, v in state.items(): + g['_sset_' + _state_vars[k]](k, g[k], v) + return state + + +def _sget_dict(val): + return val.copy() + + +def _sset_dict(key, ob, state): + ob.clear() + ob.update(state) + + +def _sget_object(val): + return val.__getstate__() + + +def _sset_object(key, ob, state): + ob.__setstate__(state) + + +_sget_none = _sset_none = lambda *args: None + + +def get_supported_platform(): + """Return this platform's maximum compatible version. + + distutils.util.get_platform() normally reports the minimum version + of Mac OS X that would be required to *use* extensions produced by + distutils. But what we want when checking compatibility is to know the + version of Mac OS X that we are *running*. To allow usage of packages that + explicitly require a newer version of Mac OS X, we must also know the + current version of the OS. + + If this condition occurs for any other platform with a version in its + platform strings, this function should be extended accordingly. + """ + plat = get_build_platform() + m = macosVersionString.match(plat) + if m is not None and sys.platform == "darwin": + try: + plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3)) + except ValueError: + # not Mac OS X + pass + return plat + + +__all__ = [ + # Basic resource access and distribution/entry point discovery + 'require', 'run_script', 'get_provider', 'get_distribution', + 'load_entry_point', 'get_entry_map', 'get_entry_info', + 'iter_entry_points', + 'resource_string', 'resource_stream', 'resource_filename', + 'resource_listdir', 'resource_exists', 'resource_isdir', + + # Environmental control + 'declare_namespace', 'working_set', 'add_activation_listener', + 'find_distributions', 'set_extraction_path', 'cleanup_resources', + 'get_default_cache', + + # Primary implementation classes + 'Environment', 'WorkingSet', 'ResourceManager', + 'Distribution', 'Requirement', 'EntryPoint', + + # Exceptions + 'ResolutionError', 'VersionConflict', 'DistributionNotFound', + 'UnknownExtra', 'ExtractionError', + + # Warnings + 'PEP440Warning', + + # Parsing functions and string utilities + 'parse_requirements', 'parse_version', 'safe_name', 'safe_version', + 'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections', + 'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker', + + # filesystem utilities + 'ensure_directory', 'normalize_path', + + # Distribution "precedence" constants + 'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST', + + # "Provider" interfaces, implementations, and registration/lookup APIs + 'IMetadataProvider', 'IResourceProvider', 'FileMetadata', + 'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider', + 'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider', + 'register_finder', 'register_namespace_handler', 'register_loader_type', + 'fixup_namespace_packages', 'get_importer', + + # Deprecated/backward compatibility only + 'run_main', 'AvailableDistributions', +] + + +class ResolutionError(Exception): + """Abstract base for dependency resolution errors""" + + def __repr__(self): + return self.__class__.__name__ + repr(self.args) + + +class VersionConflict(ResolutionError): + """ + An already-installed version conflicts with the requested version. + + Should be initialized with the installed Distribution and the requested + Requirement. + """ + + _template = "{self.dist} is installed but {self.req} is required" + + @property + def dist(self): + return self.args[0] + + @property + def req(self): + return self.args[1] + + def report(self): + return self._template.format(**locals()) + + def with_context(self, required_by): + """ + If required_by is non-empty, return a version of self that is a + ContextualVersionConflict. + """ + if not required_by: + return self + args = self.args + (required_by,) + return ContextualVersionConflict(*args) + + +class ContextualVersionConflict(VersionConflict): + """ + A VersionConflict that accepts a third parameter, the set of the + requirements that required the installed Distribution. + """ + + _template = VersionConflict._template + ' by {self.required_by}' + + @property + def required_by(self): + return self.args[2] + + +class DistributionNotFound(ResolutionError): + """A requested distribution was not found""" + + _template = ("The '{self.req}' distribution was not found " + "and is required by {self.requirers_str}") + + @property + def req(self): + return self.args[0] + + @property + def requirers(self): + return self.args[1] + + @property + def requirers_str(self): + if not self.requirers: + return 'the application' + return ', '.join(self.requirers) + + def report(self): + return self._template.format(**locals()) + + def __str__(self): + return self.report() + + +class UnknownExtra(ResolutionError): + """Distribution doesn't have an "extra feature" of the given name""" + + +_provider_factories = {} + +PY_MAJOR = sys.version[:3] +EGG_DIST = 3 +BINARY_DIST = 2 +SOURCE_DIST = 1 +CHECKOUT_DIST = 0 +DEVELOP_DIST = -1 + + +def register_loader_type(loader_type, provider_factory): + """Register `provider_factory` to make providers for `loader_type` + + `loader_type` is the type or class of a PEP 302 ``module.__loader__``, + and `provider_factory` is a function that, passed a *module* object, + returns an ``IResourceProvider`` for that module. + """ + _provider_factories[loader_type] = provider_factory + + +def get_provider(moduleOrReq): + """Return an IResourceProvider for the named module or requirement""" + if isinstance(moduleOrReq, Requirement): + return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0] + try: + module = sys.modules[moduleOrReq] + except KeyError: + __import__(moduleOrReq) + module = sys.modules[moduleOrReq] + loader = getattr(module, '__loader__', None) + return _find_adapter(_provider_factories, loader)(module) + + +def _macosx_vers(_cache=[]): + if not _cache: + version = platform.mac_ver()[0] + # fallback for MacPorts + if version == '': + plist = '/System/Library/CoreServices/SystemVersion.plist' + if os.path.exists(plist): + if hasattr(plistlib, 'readPlist'): + plist_content = plistlib.readPlist(plist) + if 'ProductVersion' in plist_content: + version = plist_content['ProductVersion'] + + _cache.append(version.split('.')) + return _cache[0] + + +def _macosx_arch(machine): + return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) + + +def get_build_platform(): + """Return this platform's string for platform-specific distributions + + XXX Currently this is the same as ``distutils.util.get_platform()``, but it + needs some hacks for Linux and Mac OS X. + """ + try: + # Python 2.7 or >=3.2 + from sysconfig import get_platform + except ImportError: + from distutils.util import get_platform + + plat = get_platform() + if sys.platform == "darwin" and not plat.startswith('macosx-'): + try: + version = _macosx_vers() + machine = os.uname()[4].replace(" ", "_") + return "macosx-%d.%d-%s" % (int(version[0]), int(version[1]), + _macosx_arch(machine)) + except ValueError: + # if someone is running a non-Mac darwin system, this will fall + # through to the default implementation + pass + return plat + + +macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)") +darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)") +# XXX backward compat +get_platform = get_build_platform + + +def compatible_platforms(provided, required): + """Can code for the `provided` platform run on the `required` platform? + + Returns true if either platform is ``None``, or the platforms are equal. + + XXX Needs compatibility checks for Linux and other unixy OSes. + """ + if provided is None or required is None or provided == required: + # easy case + return True + + # Mac OS X special cases + reqMac = macosVersionString.match(required) + if reqMac: + provMac = macosVersionString.match(provided) + + # is this a Mac package? + if not provMac: + # this is backwards compatibility for packages built before + # setuptools 0.6. All packages built after this point will + # use the new macosx designation. + provDarwin = darwinVersionString.match(provided) + if provDarwin: + dversion = int(provDarwin.group(1)) + macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2)) + if dversion == 7 and macosversion >= "10.3" or \ + dversion == 8 and macosversion >= "10.4": + return True + # egg isn't macosx or legacy darwin + return False + + # are they the same major version and machine type? + if provMac.group(1) != reqMac.group(1) or \ + provMac.group(3) != reqMac.group(3): + return False + + # is the required OS major update >= the provided one? + if int(provMac.group(2)) > int(reqMac.group(2)): + return False + + return True + + # XXX Linux and other platforms' special cases should go here + return False + + +def run_script(dist_spec, script_name): + """Locate distribution `dist_spec` and run its `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + require(dist_spec)[0].run_script(script_name, ns) + + +# backward compatibility +run_main = run_script + + +def get_distribution(dist): + """Return a current distribution object for a Requirement or string""" + if isinstance(dist, six.string_types): + dist = Requirement.parse(dist) + if isinstance(dist, Requirement): + dist = get_provider(dist) + if not isinstance(dist, Distribution): + raise TypeError("Expected string, Requirement, or Distribution", dist) + return dist + + +def load_entry_point(dist, group, name): + """Return `name` entry point of `group` for `dist` or raise ImportError""" + return get_distribution(dist).load_entry_point(group, name) + + +def get_entry_map(dist, group=None): + """Return the entry point map for `group`, or the full entry map""" + return get_distribution(dist).get_entry_map(group) + + +def get_entry_info(dist, group, name): + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return get_distribution(dist).get_entry_info(group, name) + + +class IMetadataProvider: + def has_metadata(name): + """Does the package's distribution contain the named metadata?""" + + def get_metadata(name): + """The named metadata resource as a string""" + + def get_metadata_lines(name): + """Yield named metadata resource as list of non-blank non-comment lines + + Leading and trailing whitespace is stripped from each line, and lines + with ``#`` as the first non-blank character are omitted.""" + + def metadata_isdir(name): + """Is the named metadata a directory? (like ``os.path.isdir()``)""" + + def metadata_listdir(name): + """List of metadata names in the directory (like ``os.listdir()``)""" + + def run_script(script_name, namespace): + """Execute the named script in the supplied namespace dictionary""" + + +class IResourceProvider(IMetadataProvider): + """An object that provides access to package resources""" + + def get_resource_filename(manager, resource_name): + """Return a true filesystem path for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_stream(manager, resource_name): + """Return a readable file-like object for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_string(manager, resource_name): + """Return a string containing the contents of `resource_name` + + `manager` must be an ``IResourceManager``""" + + def has_resource(resource_name): + """Does the package contain the named resource?""" + + def resource_isdir(resource_name): + """Is the named resource a directory? (like ``os.path.isdir()``)""" + + def resource_listdir(resource_name): + """List of resource names in the directory (like ``os.listdir()``)""" + + +class WorkingSet(object): + """A collection of active distributions on sys.path (or a similar list)""" + + def __init__(self, entries=None): + """Create working set from list of path entries (default=sys.path)""" + self.entries = [] + self.entry_keys = {} + self.by_key = {} + self.callbacks = [] + + if entries is None: + entries = sys.path + + for entry in entries: + self.add_entry(entry) + + @classmethod + def _build_master(cls): + """ + Prepare the master working set. + """ + ws = cls() + try: + from __main__ import __requires__ + except ImportError: + # The main program does not list any requirements + return ws + + # ensure the requirements are met + try: + ws.require(__requires__) + except VersionConflict: + return cls._build_from_requirements(__requires__) + + return ws + + @classmethod + def _build_from_requirements(cls, req_spec): + """ + Build a working set from a requirement spec. Rewrites sys.path. + """ + # try it without defaults already on sys.path + # by starting with an empty path + ws = cls([]) + reqs = parse_requirements(req_spec) + dists = ws.resolve(reqs, Environment()) + for dist in dists: + ws.add(dist) + + # add any missing entries from sys.path + for entry in sys.path: + if entry not in ws.entries: + ws.add_entry(entry) + + # then copy back to sys.path + sys.path[:] = ws.entries + return ws + + def add_entry(self, entry): + """Add a path item to ``.entries``, finding any distributions on it + + ``find_distributions(entry, True)`` is used to find distributions + corresponding to the path entry, and they are added. `entry` is + always appended to ``.entries``, even if it is already present. + (This is because ``sys.path`` can contain the same value more than + once, and the ``.entries`` of the ``sys.path`` WorkingSet should always + equal ``sys.path``.) + """ + self.entry_keys.setdefault(entry, []) + self.entries.append(entry) + for dist in find_distributions(entry, True): + self.add(dist, entry, False) + + def __contains__(self, dist): + """True if `dist` is the active distribution for its project""" + return self.by_key.get(dist.key) == dist + + def find(self, req): + """Find a distribution matching requirement `req` + + If there is an active distribution for the requested project, this + returns it as long as it meets the version requirement specified by + `req`. But, if there is an active distribution for the project and it + does *not* meet the `req` requirement, ``VersionConflict`` is raised. + If there is no active distribution for the requested project, ``None`` + is returned. + """ + dist = self.by_key.get(req.key) + if dist is not None and dist not in req: + # XXX add more info + raise VersionConflict(dist, req) + return dist + + def iter_entry_points(self, group, name=None): + """Yield entry point objects from `group` matching `name` + + If `name` is None, yields all entry points in `group` from all + distributions in the working set, otherwise only ones matching + both `group` and `name` are yielded (in distribution order). + """ + for dist in self: + entries = dist.get_entry_map(group) + if name is None: + for ep in entries.values(): + yield ep + elif name in entries: + yield entries[name] + + def run_script(self, requires, script_name): + """Locate distribution for `requires` and run `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + self.require(requires)[0].run_script(script_name, ns) + + def __iter__(self): + """Yield distributions for non-duplicate projects in the working set + + The yield order is the order in which the items' path entries were + added to the working set. + """ + seen = {} + for item in self.entries: + if item not in self.entry_keys: + # workaround a cache issue + continue + + for key in self.entry_keys[item]: + if key not in seen: + seen[key] = 1 + yield self.by_key[key] + + def add(self, dist, entry=None, insert=True, replace=False): + """Add `dist` to working set, associated with `entry` + + If `entry` is unspecified, it defaults to the ``.location`` of `dist`. + On exit from this routine, `entry` is added to the end of the working + set's ``.entries`` (if it wasn't already present). + + `dist` is only added to the working set if it's for a project that + doesn't already have a distribution in the set, unless `replace=True`. + If it's added, any callbacks registered with the ``subscribe()`` method + will be called. + """ + if insert: + dist.insert_on(self.entries, entry, replace=replace) + + if entry is None: + entry = dist.location + keys = self.entry_keys.setdefault(entry, []) + keys2 = self.entry_keys.setdefault(dist.location, []) + if not replace and dist.key in self.by_key: + # ignore hidden distros + return + + self.by_key[dist.key] = dist + if dist.key not in keys: + keys.append(dist.key) + if dist.key not in keys2: + keys2.append(dist.key) + self._added_new(dist) + + def resolve(self, requirements, env=None, installer=None, + replace_conflicting=False): + """List all distributions needed to (recursively) meet `requirements` + + `requirements` must be a sequence of ``Requirement`` objects. `env`, + if supplied, should be an ``Environment`` instance. If + not supplied, it defaults to all distributions available within any + entry or distribution in the working set. `installer`, if supplied, + will be invoked with each requirement that cannot be met by an + already-installed distribution; it should return a ``Distribution`` or + ``None``. + + Unless `replace_conflicting=True`, raises a VersionConflict exception if + any requirements are found on the path that have the correct name but + the wrong version. Otherwise, if an `installer` is supplied it will be + invoked to obtain the correct version of the requirement and activate + it. + """ + + # set up the stack + requirements = list(requirements)[::-1] + # set of processed requirements + processed = {} + # key -> dist + best = {} + to_activate = [] + + req_extras = _ReqExtras() + + # Mapping of requirement to set of distributions that required it; + # useful for reporting info about conflicts. + required_by = collections.defaultdict(set) + + while requirements: + # process dependencies breadth-first + req = requirements.pop(0) + if req in processed: + # Ignore cyclic or redundant dependencies + continue + + if not req_extras.markers_pass(req): + continue + + dist = best.get(req.key) + if dist is None: + # Find the best distribution and add it to the map + dist = self.by_key.get(req.key) + if dist is None or (dist not in req and replace_conflicting): + ws = self + if env is None: + if dist is None: + env = Environment(self.entries) + else: + # Use an empty environment and workingset to avoid + # any further conflicts with the conflicting + # distribution + env = Environment([]) + ws = WorkingSet([]) + dist = best[req.key] = env.best_match(req, ws, installer) + if dist is None: + requirers = required_by.get(req, None) + raise DistributionNotFound(req, requirers) + to_activate.append(dist) + if dist not in req: + # Oops, the "best" so far conflicts with a dependency + dependent_req = required_by[req] + raise VersionConflict(dist, req).with_context(dependent_req) + + # push the new requirements onto the stack + new_requirements = dist.requires(req.extras)[::-1] + requirements.extend(new_requirements) + + # Register the new requirements needed by req + for new_requirement in new_requirements: + required_by[new_requirement].add(req.project_name) + req_extras[new_requirement] = req.extras + + processed[req] = True + + # return list of distros to activate + return to_activate + + def find_plugins(self, plugin_env, full_env=None, installer=None, + fallback=True): + """Find all activatable distributions in `plugin_env` + + Example usage:: + + distributions, errors = working_set.find_plugins( + Environment(plugin_dirlist) + ) + # add plugins+libs to sys.path + map(working_set.add, distributions) + # display errors + print('Could not load', errors) + + The `plugin_env` should be an ``Environment`` instance that contains + only distributions that are in the project's "plugin directory" or + directories. The `full_env`, if supplied, should be an ``Environment`` + contains all currently-available distributions. If `full_env` is not + supplied, one is created automatically from the ``WorkingSet`` this + method is called on, which will typically mean that every directory on + ``sys.path`` will be scanned for distributions. + + `installer` is a standard installer callback as used by the + ``resolve()`` method. The `fallback` flag indicates whether we should + attempt to resolve older versions of a plugin if the newest version + cannot be resolved. + + This method returns a 2-tuple: (`distributions`, `error_info`), where + `distributions` is a list of the distributions found in `plugin_env` + that were loadable, along with any other distributions that are needed + to resolve their dependencies. `error_info` is a dictionary mapping + unloadable plugin distributions to an exception instance describing the + error that occurred. Usually this will be a ``DistributionNotFound`` or + ``VersionConflict`` instance. + """ + + plugin_projects = list(plugin_env) + # scan project names in alphabetic order + plugin_projects.sort() + + error_info = {} + distributions = {} + + if full_env is None: + env = Environment(self.entries) + env += plugin_env + else: + env = full_env + plugin_env + + shadow_set = self.__class__([]) + # put all our entries in shadow_set + list(map(shadow_set.add, self)) + + for project_name in plugin_projects: + + for dist in plugin_env[project_name]: + + req = [dist.as_requirement()] + + try: + resolvees = shadow_set.resolve(req, env, installer) + + except ResolutionError as v: + # save error info + error_info[dist] = v + if fallback: + # try the next older version of project + continue + else: + # give up on this project, keep going + break + + else: + list(map(shadow_set.add, resolvees)) + distributions.update(dict.fromkeys(resolvees)) + + # success, no need to try any more versions of this project + break + + distributions = list(distributions) + distributions.sort() + + return distributions, error_info + + def require(self, *requirements): + """Ensure that distributions matching `requirements` are activated + + `requirements` must be a string or a (possibly-nested) sequence + thereof, specifying the distributions and versions required. The + return value is a sequence of the distributions that needed to be + activated to fulfill the requirements; all relevant distributions are + included, even if they were already activated in this working set. + """ + needed = self.resolve(parse_requirements(requirements)) + + for dist in needed: + self.add(dist) + + return needed + + def subscribe(self, callback, existing=True): + """Invoke `callback` for all distributions + + If `existing=True` (default), + call on all existing ones, as well. + """ + if callback in self.callbacks: + return + self.callbacks.append(callback) + if not existing: + return + for dist in self: + callback(dist) + + def _added_new(self, dist): + for callback in self.callbacks: + callback(dist) + + def __getstate__(self): + return ( + self.entries[:], self.entry_keys.copy(), self.by_key.copy(), + self.callbacks[:] + ) + + def __setstate__(self, e_k_b_c): + entries, keys, by_key, callbacks = e_k_b_c + self.entries = entries[:] + self.entry_keys = keys.copy() + self.by_key = by_key.copy() + self.callbacks = callbacks[:] + + +class _ReqExtras(dict): + """ + Map each requirement to the extras that demanded it. + """ + + def markers_pass(self, req): + """ + Evaluate markers for req against each extra that + demanded it. + + Return False if the req has a marker and fails + evaluation. Otherwise, return True. + """ + extra_evals = ( + req.marker.evaluate({'extra': extra}) + for extra in self.get(req, ()) + (None,) + ) + return not req.marker or any(extra_evals) + + +class Environment(object): + """Searchable snapshot of distributions on a search path""" + + def __init__(self, search_path=None, platform=get_supported_platform(), + python=PY_MAJOR): + """Snapshot distributions available on a search path + + Any distributions found on `search_path` are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. + + `platform` is an optional string specifying the name of the platform + that platform-specific distributions must be compatible with. If + unspecified, it defaults to the current platform. `python` is an + optional string naming the desired version of Python (e.g. ``'3.3'``); + it defaults to the current version. + + You may explicitly set `platform` (and/or `python`) to ``None`` if you + wish to map *all* distributions, not just those compatible with the + running platform or Python version. + """ + self._distmap = {} + self.platform = platform + self.python = python + self.scan(search_path) + + def can_add(self, dist): + """Is distribution `dist` acceptable for this environment? + + The distribution must match the platform and python version + requirements specified when this environment was created, or False + is returned. + """ + return (self.python is None or dist.py_version is None + or dist.py_version == self.python) \ + and compatible_platforms(dist.platform, self.platform) + + def remove(self, dist): + """Remove `dist` from the environment""" + self._distmap[dist.key].remove(dist) + + def scan(self, search_path=None): + """Scan `search_path` for distributions usable in this environment + + Any distributions found are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. Only distributions conforming to + the platform/python version defined at initialization are added. + """ + if search_path is None: + search_path = sys.path + + for item in search_path: + for dist in find_distributions(item): + self.add(dist) + + def __getitem__(self, project_name): + """Return a newest-to-oldest list of distributions for `project_name` + + Uses case-insensitive `project_name` comparison, assuming all the + project's distributions use their project's name converted to all + lowercase as their key. + + """ + distribution_key = project_name.lower() + return self._distmap.get(distribution_key, []) + + def add(self, dist): + """Add `dist` if we ``can_add()`` it and it has not already been added + """ + if self.can_add(dist) and dist.has_version(): + dists = self._distmap.setdefault(dist.key, []) + if dist not in dists: + dists.append(dist) + dists.sort(key=operator.attrgetter('hashcmp'), reverse=True) + + def best_match(self, req, working_set, installer=None): + """Find distribution best matching `req` and usable on `working_set` + + This calls the ``find(req)`` method of the `working_set` to see if a + suitable distribution is already active. (This may raise + ``VersionConflict`` if an unsuitable version of the project is already + active in the specified `working_set`.) If a suitable distribution + isn't active, this method returns the newest distribution in the + environment that meets the ``Requirement`` in `req`. If no suitable + distribution is found, and `installer` is supplied, then the result of + calling the environment's ``obtain(req, installer)`` method will be + returned. + """ + dist = working_set.find(req) + if dist is not None: + return dist + for dist in self[req.key]: + if dist in req: + return dist + # try to download/install + return self.obtain(req, installer) + + def obtain(self, requirement, installer=None): + """Obtain a distribution matching `requirement` (e.g. via download) + + Obtain a distro that matches requirement (e.g. via download). In the + base ``Environment`` class, this routine just returns + ``installer(requirement)``, unless `installer` is None, in which case + None is returned instead. This method is a hook that allows subclasses + to attempt other ways of obtaining a distribution before falling back + to the `installer` argument.""" + if installer is not None: + return installer(requirement) + + def __iter__(self): + """Yield the unique project names of the available distributions""" + for key in self._distmap.keys(): + if self[key]: + yield key + + def __iadd__(self, other): + """In-place addition of a distribution or environment""" + if isinstance(other, Distribution): + self.add(other) + elif isinstance(other, Environment): + for project in other: + for dist in other[project]: + self.add(dist) + else: + raise TypeError("Can't add %r to environment" % (other,)) + return self + + def __add__(self, other): + """Add an environment or distribution to an environment""" + new = self.__class__([], platform=None, python=None) + for env in self, other: + new += env + return new + + +# XXX backward compatibility +AvailableDistributions = Environment + + +class ExtractionError(RuntimeError): + """An error occurred extracting a resource + + The following attributes are available from instances of this exception: + + manager + The resource manager that raised this exception + + cache_path + The base directory for resource extraction + + original_error + The exception instance that caused extraction to fail + """ + + +class ResourceManager: + """Manage resource extraction and packages""" + extraction_path = None + + def __init__(self): + self.cached_files = {} + + def resource_exists(self, package_or_requirement, resource_name): + """Does the named resource exist?""" + return get_provider(package_or_requirement).has_resource(resource_name) + + def resource_isdir(self, package_or_requirement, resource_name): + """Is the named resource an existing directory?""" + return get_provider(package_or_requirement).resource_isdir( + resource_name + ) + + def resource_filename(self, package_or_requirement, resource_name): + """Return a true filesystem path for specified resource""" + return get_provider(package_or_requirement).get_resource_filename( + self, resource_name + ) + + def resource_stream(self, package_or_requirement, resource_name): + """Return a readable file-like object for specified resource""" + return get_provider(package_or_requirement).get_resource_stream( + self, resource_name + ) + + def resource_string(self, package_or_requirement, resource_name): + """Return specified resource as a string""" + return get_provider(package_or_requirement).get_resource_string( + self, resource_name + ) + + def resource_listdir(self, package_or_requirement, resource_name): + """List the contents of the named resource directory""" + return get_provider(package_or_requirement).resource_listdir( + resource_name + ) + + def extraction_error(self): + """Give an error message for problems extracting file(s)""" + + old_exc = sys.exc_info()[1] + cache_path = self.extraction_path or get_default_cache() + + tmpl = textwrap.dedent(""" + Can't extract file(s) to egg cache + + The following error occurred while trying to extract file(s) to the Python egg + cache: + + {old_exc} + + The Python egg cache directory is currently set to: + + {cache_path} + + Perhaps your account does not have write access to this directory? You can + change the cache directory by setting the PYTHON_EGG_CACHE environment + variable to point to an accessible directory. + """).lstrip() + err = ExtractionError(tmpl.format(**locals())) + err.manager = self + err.cache_path = cache_path + err.original_error = old_exc + raise err + + def get_cache_path(self, archive_name, names=()): + """Return absolute location in cache for `archive_name` and `names` + + The parent directory of the resulting path will be created if it does + not already exist. `archive_name` should be the base filename of the + enclosing egg (which may not be the name of the enclosing zipfile!), + including its ".egg" extension. `names`, if provided, should be a + sequence of path name parts "under" the egg's extraction location. + + This method should only be called by resource providers that need to + obtain an extraction location, and only for names they intend to + extract, as it tracks the generated names for possible cleanup later. + """ + extract_path = self.extraction_path or get_default_cache() + target_path = os.path.join(extract_path, archive_name + '-tmp', *names) + try: + _bypass_ensure_directory(target_path) + except: + self.extraction_error() + + self._warn_unsafe_extraction_path(extract_path) + + self.cached_files[target_path] = 1 + return target_path + + @staticmethod + def _warn_unsafe_extraction_path(path): + """ + If the default extraction path is overridden and set to an insecure + location, such as /tmp, it opens up an opportunity for an attacker to + replace an extracted file with an unauthorized payload. Warn the user + if a known insecure location is used. + + See Distribute #375 for more details. + """ + if os.name == 'nt' and not path.startswith(os.environ['windir']): + # On Windows, permissions are generally restrictive by default + # and temp directories are not writable by other users, so + # bypass the warning. + return + mode = os.stat(path).st_mode + if mode & stat.S_IWOTH or mode & stat.S_IWGRP: + msg = ("%s is writable by group/others and vulnerable to attack " + "when " + "used with get_resource_filename. Consider a more secure " + "location (set with .set_extraction_path or the " + "PYTHON_EGG_CACHE environment variable)." % path) + warnings.warn(msg, UserWarning) + + def postprocess(self, tempname, filename): + """Perform any platform-specific postprocessing of `tempname` + + This is where Mac header rewrites should be done; other platforms don't + have anything special they should do. + + Resource providers should call this method ONLY after successfully + extracting a compressed resource. They must NOT call it on resources + that are already in the filesystem. + + `tempname` is the current (temporary) name of the file, and `filename` + is the name it will be renamed to by the caller after this routine + returns. + """ + + if os.name == 'posix': + # Make the resource executable + mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777 + os.chmod(tempname, mode) + + def set_extraction_path(self, path): + """Set the base path where resources will be extracted to, if needed. + + If you do not call this routine before any extractions take place, the + path defaults to the return value of ``get_default_cache()``. (Which + is based on the ``PYTHON_EGG_CACHE`` environment variable, with various + platform-specific fallbacks. See that routine's documentation for more + details.) + + Resources are extracted to subdirectories of this path based upon + information given by the ``IResourceProvider``. You may set this to a + temporary directory, but then you must call ``cleanup_resources()`` to + delete the extracted files when done. There is no guarantee that + ``cleanup_resources()`` will be able to remove all extracted files. + + (Note: you may not change the extraction path for a given resource + manager once resources have been extracted, unless you first call + ``cleanup_resources()``.) + """ + if self.cached_files: + raise ValueError( + "Can't change extraction path, files already extracted" + ) + + self.extraction_path = path + + def cleanup_resources(self, force=False): + """ + Delete all extracted resource files and directories, returning a list + of the file and directory names that could not be successfully removed. + This function does not have any concurrency protection, so it should + generally only be called when the extraction path is a temporary + directory exclusive to a single process. This method is not + automatically called; you must call it explicitly or register it as an + ``atexit`` function if you wish to ensure cleanup of a temporary + directory used for extractions. + """ + # XXX + + +def get_default_cache(): + """ + Return the ``PYTHON_EGG_CACHE`` environment variable + or a platform-relevant user cache dir for an app + named "Python-Eggs". + """ + return ( + os.environ.get('PYTHON_EGG_CACHE') + or appdirs.user_cache_dir(appname='Python-Eggs') + ) + + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """ + Convert an arbitrary string to a standard version string + """ + try: + # normalize the version + return str(packaging.version.Version(version)) + except packaging.version.InvalidVersion: + version = version.replace(' ', '.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def safe_extra(extra): + """Convert an arbitrary string to a standard 'extra' name + + Any runs of non-alphanumeric characters are replaced with a single '_', + and the result is always lowercased. + """ + return re.sub('[^A-Za-z0-9.-]+', '_', extra).lower() + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-', '_') + + +def invalid_marker(text): + """ + Validate text as a PEP 508 environment marker; return an exception + if invalid or False otherwise. + """ + try: + evaluate_marker(text) + except SyntaxError as e: + e.filename = None + e.lineno = None + return e + return False + + +def evaluate_marker(text, extra=None): + """ + Evaluate a PEP 508 environment marker. + Return a boolean indicating the marker result in this environment. + Raise SyntaxError if marker is invalid. + + This implementation uses the 'pyparsing' module. + """ + try: + marker = packaging.markers.Marker(text) + return marker.evaluate() + except packaging.markers.InvalidMarker as e: + raise SyntaxError(e) + + +class NullProvider: + """Try to implement resources and metadata for arbitrary PEP 302 loaders""" + + egg_name = None + egg_info = None + loader = None + + def __init__(self, module): + self.loader = getattr(module, '__loader__', None) + self.module_path = os.path.dirname(getattr(module, '__file__', '')) + + def get_resource_filename(self, manager, resource_name): + return self._fn(self.module_path, resource_name) + + def get_resource_stream(self, manager, resource_name): + return io.BytesIO(self.get_resource_string(manager, resource_name)) + + def get_resource_string(self, manager, resource_name): + return self._get(self._fn(self.module_path, resource_name)) + + def has_resource(self, resource_name): + return self._has(self._fn(self.module_path, resource_name)) + + def has_metadata(self, name): + return self.egg_info and self._has(self._fn(self.egg_info, name)) + + def get_metadata(self, name): + if not self.egg_info: + return "" + value = self._get(self._fn(self.egg_info, name)) + return value.decode('utf-8') if six.PY3 else value + + def get_metadata_lines(self, name): + return yield_lines(self.get_metadata(name)) + + def resource_isdir(self, resource_name): + return self._isdir(self._fn(self.module_path, resource_name)) + + def metadata_isdir(self, name): + return self.egg_info and self._isdir(self._fn(self.egg_info, name)) + + def resource_listdir(self, resource_name): + return self._listdir(self._fn(self.module_path, resource_name)) + + def metadata_listdir(self, name): + if self.egg_info: + return self._listdir(self._fn(self.egg_info, name)) + return [] + + def run_script(self, script_name, namespace): + script = 'scripts/' + script_name + if not self.has_metadata(script): + raise ResolutionError("No script named %r" % script_name) + script_text = self.get_metadata(script).replace('\r\n', '\n') + script_text = script_text.replace('\r', '\n') + script_filename = self._fn(self.egg_info, script) + namespace['__file__'] = script_filename + if os.path.exists(script_filename): + source = open(script_filename).read() + code = compile(source, script_filename, 'exec') + exec(code, namespace, namespace) + else: + from linecache import cache + cache[script_filename] = ( + len(script_text), 0, script_text.split('\n'), script_filename + ) + script_code = compile(script_text, script_filename, 'exec') + exec(script_code, namespace, namespace) + + def _has(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _isdir(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _listdir(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _fn(self, base, resource_name): + if resource_name: + return os.path.join(base, *resource_name.split('/')) + return base + + def _get(self, path): + if hasattr(self.loader, 'get_data'): + return self.loader.get_data(path) + raise NotImplementedError( + "Can't perform this operation for loaders without 'get_data()'" + ) + + +register_loader_type(object, NullProvider) + + +class EggProvider(NullProvider): + """Provider based on a virtual filesystem""" + + def __init__(self, module): + NullProvider.__init__(self, module) + self._setup_prefix() + + def _setup_prefix(self): + # we assume here that our metadata may be nested inside a "basket" + # of multiple eggs; that's why we use module_path instead of .archive + path = self.module_path + old = None + while path != old: + if _is_unpacked_egg(path): + self.egg_name = os.path.basename(path) + self.egg_info = os.path.join(path, 'EGG-INFO') + self.egg_root = path + break + old = path + path, base = os.path.split(path) + + +class DefaultProvider(EggProvider): + """Provides access to package resources in the filesystem""" + + def _has(self, path): + return os.path.exists(path) + + def _isdir(self, path): + return os.path.isdir(path) + + def _listdir(self, path): + return os.listdir(path) + + def get_resource_stream(self, manager, resource_name): + return open(self._fn(self.module_path, resource_name), 'rb') + + def _get(self, path): + with open(path, 'rb') as stream: + return stream.read() + + @classmethod + def _register(cls): + loader_cls = getattr(importlib_machinery, 'SourceFileLoader', + type(None)) + register_loader_type(loader_cls, cls) + + +DefaultProvider._register() + + +class EmptyProvider(NullProvider): + """Provider that returns nothing for all requests""" + + _isdir = _has = lambda self, path: False + _get = lambda self, path: '' + _listdir = lambda self, path: [] + module_path = None + + def __init__(self): + pass + + +empty_provider = EmptyProvider() + + +class ZipManifests(dict): + """ + zip manifest builder + """ + + @classmethod + def build(cls, path): + """ + Build a dictionary similar to the zipimport directory + caches, except instead of tuples, store ZipInfo objects. + + Use a platform-specific path separator (os.sep) for the path keys + for compatibility with pypy on Windows. + """ + with ContextualZipFile(path) as zfile: + items = ( + ( + name.replace('/', os.sep), + zfile.getinfo(name), + ) + for name in zfile.namelist() + ) + return dict(items) + + load = build + + +class MemoizedZipManifests(ZipManifests): + """ + Memoized zipfile manifests. + """ + manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime') + + def load(self, path): + """ + Load a manifest at path or return a suitable manifest already loaded. + """ + path = os.path.normpath(path) + mtime = os.stat(path).st_mtime + + if path not in self or self[path].mtime != mtime: + manifest = self.build(path) + self[path] = self.manifest_mod(manifest, mtime) + + return self[path].manifest + + +class ContextualZipFile(zipfile.ZipFile): + """ + Supplement ZipFile class to support context manager for Python 2.6 + """ + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + def __new__(cls, *args, **kwargs): + """ + Construct a ZipFile or ContextualZipFile as appropriate + """ + if hasattr(zipfile.ZipFile, '__exit__'): + return zipfile.ZipFile(*args, **kwargs) + return super(ContextualZipFile, cls).__new__(cls) + + +class ZipProvider(EggProvider): + """Resource support for zips and eggs""" + + eagers = None + _zip_manifests = MemoizedZipManifests() + + def __init__(self, module): + EggProvider.__init__(self, module) + self.zip_pre = self.loader.archive + os.sep + + def _zipinfo_name(self, fspath): + # Convert a virtual filename (full path to file) into a zipfile subpath + # usable with the zipimport directory cache for our target archive + if fspath.startswith(self.zip_pre): + return fspath[len(self.zip_pre):] + raise AssertionError( + "%s is not a subpath of %s" % (fspath, self.zip_pre) + ) + + def _parts(self, zip_path): + # Convert a zipfile subpath into an egg-relative path part list. + # pseudo-fs path + fspath = self.zip_pre + zip_path + if fspath.startswith(self.egg_root + os.sep): + return fspath[len(self.egg_root) + 1:].split(os.sep) + raise AssertionError( + "%s is not a subpath of %s" % (fspath, self.egg_root) + ) + + @property + def zipinfo(self): + return self._zip_manifests.load(self.loader.archive) + + def get_resource_filename(self, manager, resource_name): + if not self.egg_name: + raise NotImplementedError( + "resource_filename() only supported for .egg, not .zip" + ) + # no need to lock for extraction, since we use temp names + zip_path = self._resource_to_zip(resource_name) + eagers = self._get_eager_resources() + if '/'.join(self._parts(zip_path)) in eagers: + for name in eagers: + self._extract_resource(manager, self._eager_to_zip(name)) + return self._extract_resource(manager, zip_path) + + @staticmethod + def _get_date_and_size(zip_stat): + size = zip_stat.file_size + # ymdhms+wday, yday, dst + date_time = zip_stat.date_time + (0, 0, -1) + # 1980 offset already done + timestamp = time.mktime(date_time) + return timestamp, size + + def _extract_resource(self, manager, zip_path): + + if zip_path in self._index(): + for name in self._index()[zip_path]: + last = self._extract_resource( + manager, os.path.join(zip_path, name) + ) + # return the extracted directory name + return os.path.dirname(last) + + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + + if not WRITE_SUPPORT: + raise IOError('"os.rename" and "os.unlink" are not supported ' + 'on this platform') + try: + + real_path = manager.get_cache_path( + self.egg_name, self._parts(zip_path) + ) + + if self._is_current(real_path, zip_path): + return real_path + + outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path)) + os.write(outf, self.loader.get_data(zip_path)) + os.close(outf) + utime(tmpnam, (timestamp, timestamp)) + manager.postprocess(tmpnam, real_path) + + try: + rename(tmpnam, real_path) + + except os.error: + if os.path.isfile(real_path): + if self._is_current(real_path, zip_path): + # the file became current since it was checked above, + # so proceed. + return real_path + # Windows, del old file and retry + elif os.name == 'nt': + unlink(real_path) + rename(tmpnam, real_path) + return real_path + raise + + except os.error: + # report a user-friendly error + manager.extraction_error() + + return real_path + + def _is_current(self, file_path, zip_path): + """ + Return True if the file_path is current for this zip_path + """ + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + if not os.path.isfile(file_path): + return False + stat = os.stat(file_path) + if stat.st_size != size or stat.st_mtime != timestamp: + return False + # check that the contents match + zip_contents = self.loader.get_data(zip_path) + with open(file_path, 'rb') as f: + file_contents = f.read() + return zip_contents == file_contents + + def _get_eager_resources(self): + if self.eagers is None: + eagers = [] + for name in ('native_libs.txt', 'eager_resources.txt'): + if self.has_metadata(name): + eagers.extend(self.get_metadata_lines(name)) + self.eagers = eagers + return self.eagers + + def _index(self): + try: + return self._dirindex + except AttributeError: + ind = {} + for path in self.zipinfo: + parts = path.split(os.sep) + while parts: + parent = os.sep.join(parts[:-1]) + if parent in ind: + ind[parent].append(parts[-1]) + break + else: + ind[parent] = [parts.pop()] + self._dirindex = ind + return ind + + def _has(self, fspath): + zip_path = self._zipinfo_name(fspath) + return zip_path in self.zipinfo or zip_path in self._index() + + def _isdir(self, fspath): + return self._zipinfo_name(fspath) in self._index() + + def _listdir(self, fspath): + return list(self._index().get(self._zipinfo_name(fspath), ())) + + def _eager_to_zip(self, resource_name): + return self._zipinfo_name(self._fn(self.egg_root, resource_name)) + + def _resource_to_zip(self, resource_name): + return self._zipinfo_name(self._fn(self.module_path, resource_name)) + + +register_loader_type(zipimport.zipimporter, ZipProvider) + + +class FileMetadata(EmptyProvider): + """Metadata handler for standalone PKG-INFO files + + Usage:: + + metadata = FileMetadata("/path/to/PKG-INFO") + + This provider rejects all data and metadata requests except for PKG-INFO, + which is treated as existing, and will be the contents of the file at + the provided location. + """ + + def __init__(self, path): + self.path = path + + def has_metadata(self, name): + return name == 'PKG-INFO' and os.path.isfile(self.path) + + def get_metadata(self, name): + if name != 'PKG-INFO': + raise KeyError("No metadata except PKG-INFO is available") + + with io.open(self.path, encoding='utf-8', errors="replace") as f: + metadata = f.read() + self._warn_on_replacement(metadata) + return metadata + + def _warn_on_replacement(self, metadata): + # Python 2.6 and 3.2 compat for: replacement_char = '�' + replacement_char = b'\xef\xbf\xbd'.decode('utf-8') + if replacement_char in metadata: + tmpl = "{self.path} could not be properly decoded in UTF-8" + msg = tmpl.format(**locals()) + warnings.warn(msg) + + def get_metadata_lines(self, name): + return yield_lines(self.get_metadata(name)) + + +class PathMetadata(DefaultProvider): + """Metadata provider for egg directories + + Usage:: + + # Development eggs: + + egg_info = "/path/to/PackageName.egg-info" + base_dir = os.path.dirname(egg_info) + metadata = PathMetadata(base_dir, egg_info) + dist_name = os.path.splitext(os.path.basename(egg_info))[0] + dist = Distribution(basedir, project_name=dist_name, metadata=metadata) + + # Unpacked egg directories: + + egg_path = "/path/to/PackageName-ver-pyver-etc.egg" + metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO')) + dist = Distribution.from_filename(egg_path, metadata=metadata) + """ + + def __init__(self, path, egg_info): + self.module_path = path + self.egg_info = egg_info + + +class EggMetadata(ZipProvider): + """Metadata provider for .egg files""" + + def __init__(self, importer): + """Create a metadata provider from a zipimporter""" + + self.zip_pre = importer.archive + os.sep + self.loader = importer + if importer.prefix: + self.module_path = os.path.join(importer.archive, importer.prefix) + else: + self.module_path = importer.archive + self._setup_prefix() + + +_declare_state('dict', _distribution_finders={}) + + +def register_finder(importer_type, distribution_finder): + """Register `distribution_finder` to find distributions in sys.path items + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `distribution_finder` is a callable that, passed a path + item and the importer instance, yields ``Distribution`` instances found on + that path item. See ``pkg_resources.find_on_path`` for an example.""" + _distribution_finders[importer_type] = distribution_finder + + +def find_distributions(path_item, only=False): + """Yield distributions accessible via `path_item`""" + importer = get_importer(path_item) + finder = _find_adapter(_distribution_finders, importer) + return finder(importer, path_item, only) + + +def find_eggs_in_zip(importer, path_item, only=False): + """ + Find eggs in zip files; possibly multiple nested eggs. + """ + if importer.archive.endswith('.whl'): + # wheels are not supported with this finder + # they don't have PKG-INFO metadata, and won't ever contain eggs + return + metadata = EggMetadata(importer) + if metadata.has_metadata('PKG-INFO'): + yield Distribution.from_filename(path_item, metadata=metadata) + if only: + # don't yield nested distros + return + for subitem in metadata.resource_listdir('/'): + if _is_unpacked_egg(subitem): + subpath = os.path.join(path_item, subitem) + for dist in find_eggs_in_zip(zipimport.zipimporter(subpath), subpath): + yield dist + + +register_finder(zipimport.zipimporter, find_eggs_in_zip) + + +def find_nothing(importer, path_item, only=False): + return () + + +register_finder(object, find_nothing) + + +def _by_version_descending(names): + """ + Given a list of filenames, return them in descending order + by version number. + + >>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg' + >>> _by_version_descending(names) + ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'foo', 'bar'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.post1.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg'] + """ + def _by_version(name): + """ + Parse each component of the filename + """ + name, ext = os.path.splitext(name) + parts = itertools.chain(name.split('-'), [ext]) + return [packaging.version.parse(part) for part in parts] + + return sorted(names, key=_by_version, reverse=True) + + +def find_on_path(importer, path_item, only=False): + """Yield distributions accessible on a sys.path directory""" + path_item = _normalize_cached(path_item) + + if os.path.isdir(path_item) and os.access(path_item, os.R_OK): + if _is_unpacked_egg(path_item): + yield Distribution.from_filename( + path_item, metadata=PathMetadata( + path_item, os.path.join(path_item, 'EGG-INFO') + ) + ) + else: + # scan for .egg and .egg-info in directory + path_item_entries = _by_version_descending(os.listdir(path_item)) + for entry in path_item_entries: + lower = entry.lower() + if lower.endswith('.egg-info') or lower.endswith('.dist-info'): + fullpath = os.path.join(path_item, entry) + if os.path.isdir(fullpath): + # egg-info directory, allow getting metadata + if len(os.listdir(fullpath)) == 0: + # Empty egg directory, skip. + continue + metadata = PathMetadata(path_item, fullpath) + else: + metadata = FileMetadata(fullpath) + yield Distribution.from_location( + path_item, entry, metadata, precedence=DEVELOP_DIST + ) + elif not only and _is_unpacked_egg(entry): + dists = find_distributions(os.path.join(path_item, entry)) + for dist in dists: + yield dist + elif not only and lower.endswith('.egg-link'): + with open(os.path.join(path_item, entry)) as entry_file: + entry_lines = entry_file.readlines() + for line in entry_lines: + if not line.strip(): + continue + path = os.path.join(path_item, line.rstrip()) + dists = find_distributions(path) + for item in dists: + yield item + break + + +register_finder(pkgutil.ImpImporter, find_on_path) + +if hasattr(importlib_machinery, 'FileFinder'): + register_finder(importlib_machinery.FileFinder, find_on_path) + +_declare_state('dict', _namespace_handlers={}) +_declare_state('dict', _namespace_packages={}) + + +def register_namespace_handler(importer_type, namespace_handler): + """Register `namespace_handler` to declare namespace packages + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `namespace_handler` is a callable like this:: + + def namespace_handler(importer, path_entry, moduleName, module): + # return a path_entry to use for child packages + + Namespace handlers are only called if the importer object has already + agreed that it can handle the relevant path item, and they should only + return a subpath if the module __path__ does not already contain an + equivalent subpath. For an example namespace handler, see + ``pkg_resources.file_ns_handler``. + """ + _namespace_handlers[importer_type] = namespace_handler + + +def _handle_ns(packageName, path_item): + """Ensure that named package includes a subpath of path_item (if needed)""" + + importer = get_importer(path_item) + if importer is None: + return None + loader = importer.find_module(packageName) + if loader is None: + return None + module = sys.modules.get(packageName) + if module is None: + module = sys.modules[packageName] = types.ModuleType(packageName) + module.__path__ = [] + _set_parent_ns(packageName) + elif not hasattr(module, '__path__'): + raise TypeError("Not a package:", packageName) + handler = _find_adapter(_namespace_handlers, importer) + subpath = handler(importer, path_item, packageName, module) + if subpath is not None: + path = module.__path__ + path.append(subpath) + loader.load_module(packageName) + _rebuild_mod_path(path, packageName, module) + return subpath + + +def _rebuild_mod_path(orig_path, package_name, module): + """ + Rebuild module.__path__ ensuring that all entries are ordered + corresponding to their sys.path order + """ + sys_path = [_normalize_cached(p) for p in sys.path] + + def safe_sys_path_index(entry): + """ + Workaround for #520 and #513. + """ + try: + return sys_path.index(entry) + except ValueError: + return float('inf') + + def position_in_sys_path(path): + """ + Return the ordinal of the path based on its position in sys.path + """ + path_parts = path.split(os.sep) + module_parts = package_name.count('.') + 1 + parts = path_parts[:-module_parts] + return safe_sys_path_index(_normalize_cached(os.sep.join(parts))) + + orig_path.sort(key=position_in_sys_path) + module.__path__[:] = [_normalize_cached(p) for p in orig_path] + + +def declare_namespace(packageName): + """Declare that package 'packageName' is a namespace package""" + + _imp.acquire_lock() + try: + if packageName in _namespace_packages: + return + + path, parent = sys.path, None + if '.' in packageName: + parent = '.'.join(packageName.split('.')[:-1]) + declare_namespace(parent) + if parent not in _namespace_packages: + __import__(parent) + try: + path = sys.modules[parent].__path__ + except AttributeError: + raise TypeError("Not a package:", parent) + + # Track what packages are namespaces, so when new path items are added, + # they can be updated + _namespace_packages.setdefault(parent, []).append(packageName) + _namespace_packages.setdefault(packageName, []) + + for path_item in path: + # Ensure all the parent's path items are reflected in the child, + # if they apply + _handle_ns(packageName, path_item) + + finally: + _imp.release_lock() + + +def fixup_namespace_packages(path_item, parent=None): + """Ensure that previously-declared namespace packages include path_item""" + _imp.acquire_lock() + try: + for package in _namespace_packages.get(parent, ()): + subpath = _handle_ns(package, path_item) + if subpath: + fixup_namespace_packages(subpath, package) + finally: + _imp.release_lock() + + +def file_ns_handler(importer, path_item, packageName, module): + """Compute an ns-package subpath for a filesystem or zipfile importer""" + + subpath = os.path.join(path_item, packageName.split('.')[-1]) + normalized = _normalize_cached(subpath) + for item in module.__path__: + if _normalize_cached(item) == normalized: + break + else: + # Only return the path if it's not already there + return subpath + + +register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) +register_namespace_handler(zipimport.zipimporter, file_ns_handler) + +if hasattr(importlib_machinery, 'FileFinder'): + register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler) + + +def null_ns_handler(importer, path_item, packageName, module): + return None + + +register_namespace_handler(object, null_ns_handler) + + +def normalize_path(filename): + """Normalize a file/dir name for comparison purposes""" + return os.path.normcase(os.path.realpath(filename)) + + +def _normalize_cached(filename, _cache={}): + try: + return _cache[filename] + except KeyError: + _cache[filename] = result = normalize_path(filename) + return result + + +def _is_unpacked_egg(path): + """ + Determine if given path appears to be an unpacked egg. + """ + return ( + path.lower().endswith('.egg') + ) + + +def _set_parent_ns(packageName): + parts = packageName.split('.') + name = parts.pop() + if parts: + parent = '.'.join(parts) + setattr(sys.modules[parent], name, sys.modules[packageName]) + + +def yield_lines(strs): + """Yield non-empty/non-comment lines of a string or sequence""" + if isinstance(strs, six.string_types): + for s in strs.splitlines(): + s = s.strip() + # skip blank lines/comments + if s and not s.startswith('#'): + yield s + else: + for ss in strs: + for s in yield_lines(ss): + yield s + + +MODULE = re.compile(r"\w+(\.\w+)*$").match +EGG_NAME = re.compile( + r""" + (?P<name>[^-]+) ( + -(?P<ver>[^-]+) ( + -py(?P<pyver>[^-]+) ( + -(?P<plat>.+) + )? + )? + )? + """, + re.VERBOSE | re.IGNORECASE, +).match + + +class EntryPoint(object): + """Object representing an advertised importable object""" + + def __init__(self, name, module_name, attrs=(), extras=(), dist=None): + if not MODULE(module_name): + raise ValueError("Invalid module name", module_name) + self.name = name + self.module_name = module_name + self.attrs = tuple(attrs) + self.extras = Requirement.parse(("x[%s]" % ','.join(extras))).extras + self.dist = dist + + def __str__(self): + s = "%s = %s" % (self.name, self.module_name) + if self.attrs: + s += ':' + '.'.join(self.attrs) + if self.extras: + s += ' [%s]' % ','.join(self.extras) + return s + + def __repr__(self): + return "EntryPoint.parse(%r)" % str(self) + + def load(self, require=True, *args, **kwargs): + """ + Require packages for this EntryPoint, then resolve it. + """ + if not require or args or kwargs: + warnings.warn( + "Parameters to load are deprecated. Call .resolve and " + ".require separately.", + DeprecationWarning, + stacklevel=2, + ) + if require: + self.require(*args, **kwargs) + return self.resolve() + + def resolve(self): + """ + Resolve the entry point from its module and attrs. + """ + module = __import__(self.module_name, fromlist=['__name__'], level=0) + try: + return functools.reduce(getattr, self.attrs, module) + except AttributeError as exc: + raise ImportError(str(exc)) + + def require(self, env=None, installer=None): + if self.extras and not self.dist: + raise UnknownExtra("Can't require() without a distribution", self) + reqs = self.dist.requires(self.extras) + items = working_set.resolve(reqs, env, installer) + list(map(working_set.add, items)) + + pattern = re.compile( + r'\s*' + r'(?P<name>.+?)\s*' + r'=\s*' + r'(?P<module>[\w.]+)\s*' + r'(:\s*(?P<attr>[\w.]+))?\s*' + r'(?P<extras>\[.*\])?\s*$' + ) + + @classmethod + def parse(cls, src, dist=None): + """Parse a single entry point from string `src` + + Entry point syntax follows the form:: + + name = some.module:some.attr [extra1, extra2] + + The entry name and module name are required, but the ``:attrs`` and + ``[extras]`` parts are optional + """ + m = cls.pattern.match(src) + if not m: + msg = "EntryPoint must be in 'name=module:attrs [extras]' format" + raise ValueError(msg, src) + res = m.groupdict() + extras = cls._parse_extras(res['extras']) + attrs = res['attr'].split('.') if res['attr'] else () + return cls(res['name'], res['module'], attrs, extras, dist) + + @classmethod + def _parse_extras(cls, extras_spec): + if not extras_spec: + return () + req = Requirement.parse('x' + extras_spec) + if req.specs: + raise ValueError() + return req.extras + + @classmethod + def parse_group(cls, group, lines, dist=None): + """Parse an entry point group""" + if not MODULE(group): + raise ValueError("Invalid group name", group) + this = {} + for line in yield_lines(lines): + ep = cls.parse(line, dist) + if ep.name in this: + raise ValueError("Duplicate entry point", group, ep.name) + this[ep.name] = ep + return this + + @classmethod + def parse_map(cls, data, dist=None): + """Parse a map of entry point groups""" + if isinstance(data, dict): + data = data.items() + else: + data = split_sections(data) + maps = {} + for group, lines in data: + if group is None: + if not lines: + continue + raise ValueError("Entry points must be listed in groups") + group = group.strip() + if group in maps: + raise ValueError("Duplicate group name", group) + maps[group] = cls.parse_group(group, lines, dist) + return maps + + +def _remove_md5_fragment(location): + if not location: + return '' + parsed = urllib.parse.urlparse(location) + if parsed[-1].startswith('md5='): + return urllib.parse.urlunparse(parsed[:-1] + ('',)) + return location + + +def _version_from_file(lines): + """ + Given an iterable of lines from a Metadata file, return + the value of the Version field, if present, or None otherwise. + """ + is_version_line = lambda line: line.lower().startswith('version:') + version_lines = filter(is_version_line, lines) + line = next(iter(version_lines), '') + _, _, value = line.partition(':') + return safe_version(value.strip()) or None + + +class Distribution(object): + """Wrap an actual or potential sys.path entry w/metadata""" + PKG_INFO = 'PKG-INFO' + + def __init__(self, location=None, metadata=None, project_name=None, + version=None, py_version=PY_MAJOR, platform=None, + precedence=EGG_DIST): + self.project_name = safe_name(project_name or 'Unknown') + if version is not None: + self._version = safe_version(version) + self.py_version = py_version + self.platform = platform + self.location = location + self.precedence = precedence + self._provider = metadata or empty_provider + + @classmethod + def from_location(cls, location, basename, metadata=None, **kw): + project_name, version, py_version, platform = [None] * 4 + basename, ext = os.path.splitext(basename) + if ext.lower() in _distributionImpl: + cls = _distributionImpl[ext.lower()] + + match = EGG_NAME(basename) + if match: + project_name, version, py_version, platform = match.group( + 'name', 'ver', 'pyver', 'plat' + ) + return cls( + location, metadata, project_name=project_name, version=version, + py_version=py_version, platform=platform, **kw + )._reload_version() + + def _reload_version(self): + return self + + @property + def hashcmp(self): + return ( + self.parsed_version, + self.precedence, + self.key, + _remove_md5_fragment(self.location), + self.py_version or '', + self.platform or '', + ) + + def __hash__(self): + return hash(self.hashcmp) + + def __lt__(self, other): + return self.hashcmp < other.hashcmp + + def __le__(self, other): + return self.hashcmp <= other.hashcmp + + def __gt__(self, other): + return self.hashcmp > other.hashcmp + + def __ge__(self, other): + return self.hashcmp >= other.hashcmp + + def __eq__(self, other): + if not isinstance(other, self.__class__): + # It's not a Distribution, so they are not equal + return False + return self.hashcmp == other.hashcmp + + def __ne__(self, other): + return not self == other + + # These properties have to be lazy so that we don't have to load any + # metadata until/unless it's actually needed. (i.e., some distributions + # may not know their name or version without loading PKG-INFO) + + @property + def key(self): + try: + return self._key + except AttributeError: + self._key = key = self.project_name.lower() + return key + + @property + def parsed_version(self): + if not hasattr(self, "_parsed_version"): + self._parsed_version = parse_version(self.version) + + return self._parsed_version + + def _warn_legacy_version(self): + LV = packaging.version.LegacyVersion + is_legacy = isinstance(self._parsed_version, LV) + if not is_legacy: + return + + # While an empty version is technically a legacy version and + # is not a valid PEP 440 version, it's also unlikely to + # actually come from someone and instead it is more likely that + # it comes from setuptools attempting to parse a filename and + # including it in the list. So for that we'll gate this warning + # on if the version is anything at all or not. + if not self.version: + return + + tmpl = textwrap.dedent(""" + '{project_name} ({version})' is being parsed as a legacy, + non PEP 440, + version. You may find odd behavior and sort order. + In particular it will be sorted as less than 0.0. It + is recommended to migrate to PEP 440 compatible + versions. + """).strip().replace('\n', ' ') + + warnings.warn(tmpl.format(**vars(self)), PEP440Warning) + + @property + def version(self): + try: + return self._version + except AttributeError: + version = _version_from_file(self._get_metadata(self.PKG_INFO)) + if version is None: + tmpl = "Missing 'Version:' header and/or %s file" + raise ValueError(tmpl % self.PKG_INFO, self) + return version + + @property + def _dep_map(self): + try: + return self.__dep_map + except AttributeError: + dm = self.__dep_map = {None: []} + for name in 'requires.txt', 'depends.txt': + for extra, reqs in split_sections(self._get_metadata(name)): + if extra: + if ':' in extra: + extra, marker = extra.split(':', 1) + if invalid_marker(marker): + # XXX warn + reqs = [] + elif not evaluate_marker(marker): + reqs = [] + extra = safe_extra(extra) or None + dm.setdefault(extra, []).extend(parse_requirements(reqs)) + return dm + + def requires(self, extras=()): + """List of Requirements needed for this distro if `extras` are used""" + dm = self._dep_map + deps = [] + deps.extend(dm.get(None, ())) + for ext in extras: + try: + deps.extend(dm[safe_extra(ext)]) + except KeyError: + raise UnknownExtra( + "%s has no such extra feature %r" % (self, ext) + ) + return deps + + def _get_metadata(self, name): + if self.has_metadata(name): + for line in self.get_metadata_lines(name): + yield line + + def activate(self, path=None, replace=False): + """Ensure distribution is importable on `path` (default=sys.path)""" + if path is None: + path = sys.path + self.insert_on(path, replace=replace) + if path is sys.path: + fixup_namespace_packages(self.location) + for pkg in self._get_metadata('namespace_packages.txt'): + if pkg in sys.modules: + declare_namespace(pkg) + + def egg_name(self): + """Return what this distribution's standard .egg filename should be""" + filename = "%s-%s-py%s" % ( + to_filename(self.project_name), to_filename(self.version), + self.py_version or PY_MAJOR + ) + + if self.platform: + filename += '-' + self.platform + return filename + + def __repr__(self): + if self.location: + return "%s (%s)" % (self, self.location) + else: + return str(self) + + def __str__(self): + try: + version = getattr(self, 'version', None) + except ValueError: + version = None + version = version or "[unknown version]" + return "%s %s" % (self.project_name, version) + + def __getattr__(self, attr): + """Delegate all unrecognized public attributes to .metadata provider""" + if attr.startswith('_'): + raise AttributeError(attr) + return getattr(self._provider, attr) + + @classmethod + def from_filename(cls, filename, metadata=None, **kw): + return cls.from_location( + _normalize_cached(filename), os.path.basename(filename), metadata, + **kw + ) + + def as_requirement(self): + """Return a ``Requirement`` that matches this distribution exactly""" + if isinstance(self.parsed_version, packaging.version.Version): + spec = "%s==%s" % (self.project_name, self.parsed_version) + else: + spec = "%s===%s" % (self.project_name, self.parsed_version) + + return Requirement.parse(spec) + + def load_entry_point(self, group, name): + """Return the `name` entry point of `group` or raise ImportError""" + ep = self.get_entry_info(group, name) + if ep is None: + raise ImportError("Entry point %r not found" % ((group, name),)) + return ep.load() + + def get_entry_map(self, group=None): + """Return the entry point map for `group`, or the full entry map""" + try: + ep_map = self._ep_map + except AttributeError: + ep_map = self._ep_map = EntryPoint.parse_map( + self._get_metadata('entry_points.txt'), self + ) + if group is not None: + return ep_map.get(group, {}) + return ep_map + + def get_entry_info(self, group, name): + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return self.get_entry_map(group).get(name) + + def insert_on(self, path, loc=None, replace=False): + """Ensure self.location is on path + + If replace=False (default): + - If location is already in path anywhere, do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent. + - Else: add to the end of path. + If replace=True: + - If location is already on path anywhere (not eggs) + or higher priority than its parent (eggs) + do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent, + removing any lower-priority entries. + - Else: add it to the front of path. + """ + + loc = loc or self.location + if not loc: + return + + nloc = _normalize_cached(loc) + bdir = os.path.dirname(nloc) + npath = [(p and _normalize_cached(p) or p) for p in path] + + for p, item in enumerate(npath): + if item == nloc: + if replace: + break + else: + # don't modify path (even removing duplicates) if found and not replace + return + elif item == bdir and self.precedence == EGG_DIST: + # if it's an .egg, give it precedence over its directory + # UNLESS it's already been added to sys.path and replace=False + if (not replace) and nloc in npath[p:]: + return + if path is sys.path: + self.check_version_conflict() + path.insert(p, loc) + npath.insert(p, nloc) + break + else: + if path is sys.path: + self.check_version_conflict() + if replace: + path.insert(0, loc) + else: + path.append(loc) + return + + # p is the spot where we found or inserted loc; now remove duplicates + while True: + try: + np = npath.index(nloc, p + 1) + except ValueError: + break + else: + del npath[np], path[np] + # ha! + p = np + + return + + def check_version_conflict(self): + if self.key == 'setuptools': + # ignore the inevitable setuptools self-conflicts :( + return + + nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt')) + loc = normalize_path(self.location) + for modname in self._get_metadata('top_level.txt'): + if (modname not in sys.modules or modname in nsp + or modname in _namespace_packages): + continue + if modname in ('pkg_resources', 'setuptools', 'site'): + continue + fn = getattr(sys.modules[modname], '__file__', None) + if fn and (normalize_path(fn).startswith(loc) or + fn.startswith(self.location)): + continue + issue_warning( + "Module %s was already imported from %s, but %s is being added" + " to sys.path" % (modname, fn, self.location), + ) + + def has_version(self): + try: + self.version + except ValueError: + issue_warning("Unbuilt egg for " + repr(self)) + return False + return True + + def clone(self, **kw): + """Copy this distribution, substituting in any changed keyword args""" + names = 'project_name version py_version platform location precedence' + for attr in names.split(): + kw.setdefault(attr, getattr(self, attr, None)) + kw.setdefault('metadata', self._provider) + return self.__class__(**kw) + + @property + def extras(self): + return [dep for dep in self._dep_map if dep] + + +class EggInfoDistribution(Distribution): + def _reload_version(self): + """ + Packages installed by distutils (e.g. numpy or scipy), + which uses an old safe_version, and so + their version numbers can get mangled when + converted to filenames (e.g., 1.11.0.dev0+2329eae to + 1.11.0.dev0_2329eae). These distributions will not be + parsed properly + downstream by Distribution and safe_version, so + take an extra step and try to get the version number from + the metadata file itself instead of the filename. + """ + md_version = _version_from_file(self._get_metadata(self.PKG_INFO)) + if md_version: + self._version = md_version + return self + + +class DistInfoDistribution(Distribution): + """Wrap an actual or potential sys.path entry w/metadata, .dist-info style""" + PKG_INFO = 'METADATA' + EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])") + + @property + def _parsed_pkg_info(self): + """Parse and cache metadata""" + try: + return self._pkg_info + except AttributeError: + metadata = self.get_metadata(self.PKG_INFO) + self._pkg_info = email.parser.Parser().parsestr(metadata) + return self._pkg_info + + @property + def _dep_map(self): + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._compute_dependencies() + return self.__dep_map + + def _compute_dependencies(self): + """Recompute this distribution's dependencies.""" + dm = self.__dep_map = {None: []} + + reqs = [] + # Including any condition expressions + for req in self._parsed_pkg_info.get_all('Requires-Dist') or []: + reqs.extend(parse_requirements(req)) + + def reqs_for_extra(extra): + for req in reqs: + if not req.marker or req.marker.evaluate({'extra': extra}): + yield req + + common = frozenset(reqs_for_extra(None)) + dm[None].extend(common) + + for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []: + s_extra = safe_extra(extra.strip()) + dm[s_extra] = list(frozenset(reqs_for_extra(extra)) - common) + + return dm + + +_distributionImpl = { + '.egg': Distribution, + '.egg-info': EggInfoDistribution, + '.dist-info': DistInfoDistribution, + } + + +def issue_warning(*args, **kw): + level = 1 + g = globals() + try: + # find the first stack frame that is *not* code in + # the pkg_resources module, to use for the warning + while sys._getframe(level).f_globals is g: + level += 1 + except ValueError: + pass + warnings.warn(stacklevel=level + 1, *args, **kw) + + +class RequirementParseError(ValueError): + def __str__(self): + return ' '.join(self.args) + + +def parse_requirements(strs): + """Yield ``Requirement`` objects for each specification in `strs` + + `strs` must be a string, or a (possibly-nested) iterable thereof. + """ + # create a steppable iterator, so we can handle \-continuations + lines = iter(yield_lines(strs)) + + for line in lines: + # Drop comments -- a hash without a space may be in a URL. + if ' #' in line: + line = line[:line.find(' #')] + # If there is a line continuation, drop it, and append the next line. + if line.endswith('\\'): + line = line[:-2].strip() + line += next(lines) + yield Requirement(line) + + +class Requirement(packaging.requirements.Requirement): + def __init__(self, requirement_string): + """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" + try: + super(Requirement, self).__init__(requirement_string) + except packaging.requirements.InvalidRequirement as e: + raise RequirementParseError(str(e)) + self.unsafe_name = self.name + project_name = safe_name(self.name) + self.project_name, self.key = project_name, project_name.lower() + self.specs = [ + (spec.operator, spec.version) for spec in self.specifier] + self.extras = tuple(map(safe_extra, self.extras)) + self.hashCmp = ( + self.key, + self.specifier, + frozenset(self.extras), + str(self.marker) if self.marker else None, + ) + self.__hash = hash(self.hashCmp) + + def __eq__(self, other): + return ( + isinstance(other, Requirement) and + self.hashCmp == other.hashCmp + ) + + def __ne__(self, other): + return not self == other + + def __contains__(self, item): + if isinstance(item, Distribution): + if item.key != self.key: + return False + + item = item.version + + # Allow prereleases always in order to match the previous behavior of + # this method. In the future this should be smarter and follow PEP 440 + # more accurately. + return self.specifier.contains(item, prereleases=True) + + def __hash__(self): + return self.__hash + + def __repr__(self): return "Requirement.parse(%r)" % str(self) + + @staticmethod + def parse(s): + req, = parse_requirements(s) + return req + + +def _get_mro(cls): + """Get an mro for a type or classic class""" + if not isinstance(cls, type): + + class cls(cls, object): + pass + + return cls.__mro__[1:] + return cls.__mro__ + + +def _find_adapter(registry, ob): + """Return an adapter factory for `ob` from `registry`""" + for t in _get_mro(getattr(ob, '__class__', type(ob))): + if t in registry: + return registry[t] + + +def ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + if not os.path.isdir(dirname): + os.makedirs(dirname) + + +def _bypass_ensure_directory(path): + """Sandbox-bypassing version of ensure_directory()""" + if not WRITE_SUPPORT: + raise IOError('"os.mkdir" not supported on this platform.') + dirname, filename = split(path) + if dirname and filename and not isdir(dirname): + _bypass_ensure_directory(dirname) + mkdir(dirname, 0o755) + + +def split_sections(s): + """Split a string or iterable thereof into (section, content) pairs + + Each ``section`` is a stripped version of the section header ("[section]") + and each ``content`` is a list of stripped lines excluding blank lines and + comment-only lines. If there are any such lines before the first section + header, they're returned in a first ``section`` of ``None``. + """ + section = None + content = [] + for line in yield_lines(s): + if line.startswith("["): + if line.endswith("]"): + if section or content: + yield section, content + section = line[1:-1].strip() + content = [] + else: + raise ValueError("Invalid section heading", line) + else: + content.append(line) + + # wrap up last segment + yield section, content + + +def _mkstemp(*args, **kw): + old_open = os.open + try: + # temporarily bypass sandboxing + os.open = os_open + return tempfile.mkstemp(*args, **kw) + finally: + # and then put it back + os.open = old_open + + +# Silence the PEP440Warning by default, so that end users don't get hit by it +# randomly just because they use pkg_resources. We want to append the rule +# because we want earlier uses of filterwarnings to take precedence over this +# one. +warnings.filterwarnings("ignore", category=PEP440Warning, append=True) + + +# from jaraco.functools 1.3 +def _call_aside(f, *args, **kwargs): + f(*args, **kwargs) + return f + + +@_call_aside +def _initialize(g=globals()): + "Set up global resource manager (deliberately not state-saved)" + manager = ResourceManager() + g['_manager'] = manager + for name in dir(manager): + if not name.startswith('_'): + g[name] = getattr(manager, name) + + +@_call_aside +def _initialize_master_working_set(): + """ + Prepare the master working set and make the ``require()`` + API available. + + This function has explicit effects on the global state + of pkg_resources. It is intended to be invoked once at + the initialization of this module. + + Invocation by other packages is unsupported and done + at their own risk. + """ + working_set = WorkingSet._build_master() + _declare_state('object', working_set=working_set) + + require = working_set.require + iter_entry_points = working_set.iter_entry_points + add_activation_listener = working_set.subscribe + run_script = working_set.run_script + # backward compatibility + run_main = run_script + # Activate all distributions already on sys.path with replace=False and + # ensure that all distributions added to the working set in the future + # (e.g. by calling ``require()``) will get activated as well, + # with higher priority (replace=True). + dist = None # ensure dist is defined for del dist below + for dist in working_set: + dist.activate(replace=False) + del dist + add_activation_listener(lambda dist: dist.activate(replace=True), existing=False) + working_set.entries = [] + # match order + list(map(working_set.add_entry, sys.path)) + globals().update(locals()) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/__init__.py new file mode 100644 index 0000000..5107bc0 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/__init__.py @@ -0,0 +1,123 @@ +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from __future__ import division + +from collections import deque +from datetime import timedelta +from math import ceil +from sys import stderr +from time import time + + +__version__ = '1.2' + + +class Infinite(object): + file = stderr + sma_window = 10 + + def __init__(self, *args, **kwargs): + self.index = 0 + self.start_ts = time() + self._ts = self.start_ts + self._dt = deque(maxlen=self.sma_window) + for key, val in kwargs.items(): + setattr(self, key, val) + + def __getitem__(self, key): + if key.startswith('_'): + return None + return getattr(self, key, None) + + @property + def avg(self): + return sum(self._dt) / len(self._dt) if self._dt else 0 + + @property + def elapsed(self): + return int(time() - self.start_ts) + + @property + def elapsed_td(self): + return timedelta(seconds=self.elapsed) + + def update(self): + pass + + def start(self): + pass + + def finish(self): + pass + + def next(self, n=1): + if n > 0: + now = time() + dt = (now - self._ts) / n + self._dt.append(dt) + self._ts = now + + self.index = self.index + n + self.update() + + def iter(self, it): + for x in it: + yield x + self.next() + self.finish() + + +class Progress(Infinite): + def __init__(self, *args, **kwargs): + super(Progress, self).__init__(*args, **kwargs) + self.max = kwargs.get('max', 100) + + @property + def eta(self): + return int(ceil(self.avg * self.remaining)) + + @property + def eta_td(self): + return timedelta(seconds=self.eta) + + @property + def percent(self): + return self.progress * 100 + + @property + def progress(self): + return min(1, self.index / self.max) + + @property + def remaining(self): + return max(self.max - self.index, 0) + + def start(self): + self.update() + + def goto(self, index): + incr = index - self.index + self.next(incr) + + def iter(self, it): + try: + self.max = len(it) + except TypeError: + pass + + for x in it: + yield x + self.next() + self.finish() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/bar.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/bar.py new file mode 100644 index 0000000..8ce1461 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/bar.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from . import Progress +from .helpers import WritelnMixin + + +class Bar(WritelnMixin, Progress): + width = 32 + message = '' + suffix = '%(index)d/%(max)d' + bar_prefix = ' |' + bar_suffix = '| ' + empty_fill = ' ' + fill = '#' + hide_cursor = True + + def update(self): + filled_length = int(self.width * self.progress) + empty_length = self.width - filled_length + + message = self.message % self + bar = self.fill * filled_length + empty = self.empty_fill * empty_length + suffix = self.suffix % self + line = ''.join([message, self.bar_prefix, bar, empty, self.bar_suffix, + suffix]) + self.writeln(line) + + +class ChargingBar(Bar): + suffix = '%(percent)d%%' + bar_prefix = ' ' + bar_suffix = ' ' + empty_fill = u'∙' + fill = u'█' + + +class FillingSquaresBar(ChargingBar): + empty_fill = u'▢' + fill = u'▣' + + +class FillingCirclesBar(ChargingBar): + empty_fill = u'◯' + fill = u'◉' + + +class IncrementalBar(Bar): + phases = (u' ', u'▏', u'▎', u'▍', u'▌', u'▋', u'▊', u'▉', u'█') + + def update(self): + nphases = len(self.phases) + expanded_length = int(nphases * self.width * self.progress) + filled_length = int(self.width * self.progress) + empty_length = self.width - filled_length + phase = expanded_length - (filled_length * nphases) + + message = self.message % self + bar = self.phases[-1] * filled_length + current = self.phases[phase] if phase > 0 else '' + empty = self.empty_fill * max(0, empty_length - len(current)) + suffix = self.suffix % self + line = ''.join([message, self.bar_prefix, bar, current, empty, + self.bar_suffix, suffix]) + self.writeln(line) + + +class ShadyBar(IncrementalBar): + phases = (u' ', u'░', u'▒', u'▓', u'█') diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/counter.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/counter.py new file mode 100644 index 0000000..caaddc6 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/counter.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from . import Infinite, Progress +from .helpers import WriteMixin + + +class Counter(WriteMixin, Infinite): + message = '' + hide_cursor = True + + def update(self): + self.write(str(self.index)) + + +class Countdown(WriteMixin, Progress): + hide_cursor = True + + def update(self): + self.write(str(self.remaining)) + + +class Stack(WriteMixin, Progress): + phases = (u' ', u'▁', u'▂', u'▃', u'▄', u'▅', u'▆', u'▇', u'█') + hide_cursor = True + + def update(self): + nphases = len(self.phases) + i = min(nphases - 1, int(self.progress * nphases)) + self.write(self.phases[i]) + + +class Pie(Stack): + phases = (u'○', u'◔', u'◑', u'◕', u'●') diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/helpers.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/helpers.py new file mode 100644 index 0000000..9ed90b2 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/helpers.py @@ -0,0 +1,91 @@ +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from __future__ import print_function + + +HIDE_CURSOR = '\x1b[?25l' +SHOW_CURSOR = '\x1b[?25h' + + +class WriteMixin(object): + hide_cursor = False + + def __init__(self, message=None, **kwargs): + super(WriteMixin, self).__init__(**kwargs) + self._width = 0 + if message: + self.message = message + + if self.file.isatty(): + if self.hide_cursor: + print(HIDE_CURSOR, end='', file=self.file) + print(self.message, end='', file=self.file) + self.file.flush() + + def write(self, s): + if self.file.isatty(): + b = '\b' * self._width + c = s.ljust(self._width) + print(b + c, end='', file=self.file) + self._width = max(self._width, len(s)) + self.file.flush() + + def finish(self): + if self.file.isatty() and self.hide_cursor: + print(SHOW_CURSOR, end='', file=self.file) + + +class WritelnMixin(object): + hide_cursor = False + + def __init__(self, message=None, **kwargs): + super(WritelnMixin, self).__init__(**kwargs) + if message: + self.message = message + + if self.file.isatty() and self.hide_cursor: + print(HIDE_CURSOR, end='', file=self.file) + + def clearln(self): + if self.file.isatty(): + print('\r\x1b[K', end='', file=self.file) + + def writeln(self, line): + if self.file.isatty(): + self.clearln() + print(line, end='', file=self.file) + self.file.flush() + + def finish(self): + if self.file.isatty(): + print(file=self.file) + if self.hide_cursor: + print(SHOW_CURSOR, end='', file=self.file) + + +from signal import signal, SIGINT +from sys import exit + + +class SigIntMixin(object): + """Registers a signal handler that calls finish on SIGINT""" + + def __init__(self, *args, **kwargs): + super(SigIntMixin, self).__init__(*args, **kwargs) + signal(SIGINT, self._sigint_handler) + + def _sigint_handler(self, signum, frame): + self.finish() + exit(0) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/spinner.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/spinner.py new file mode 100644 index 0000000..969bfbb --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/progress/spinner.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from . import Infinite +from .helpers import WriteMixin + + +class Spinner(WriteMixin, Infinite): + message = '' + phases = ('-', '\\', '|', '/') + hide_cursor = True + + def update(self): + i = self.index % len(self.phases) + self.write(self.phases[i]) + + +class PieSpinner(Spinner): + phases = [u'◷', u'◶', u'◵', u'◴'] + + +class MoonSpinner(Spinner): + phases = [u'◑', u'◒', u'◐', u'◓'] + + +class LineSpinner(Spinner): + phases = [u'⎺', u'⎻', u'⎼', u'⎽', u'⎼', u'⎻'] diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/pyparsing.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/pyparsing.py new file mode 100644 index 0000000..cb46d41 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/pyparsing.py @@ -0,0 +1,5696 @@ +# module pyparsing.py +# +# Copyright (c) 2003-2016 Paul T. McGuire +# +# 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. +# + +__doc__ = \ +""" +pyparsing module - Classes and methods to define and execute parsing grammars + +The pyparsing module is an alternative approach to creating and executing simple grammars, +vs. the traditional lex/yacc approach, or the use of regular expressions. With pyparsing, you +don't need to learn a new syntax for defining grammars or matching expressions - the parsing module +provides a library of classes that you use to construct the grammar directly in Python. + +Here is a program to parse "Hello, World!" (or any greeting of the form +C{"<salutation>, <addressee>!"}), built up using L{Word}, L{Literal}, and L{And} elements +(L{'+'<ParserElement.__add__>} operator gives L{And} expressions, strings are auto-converted to +L{Literal} expressions):: + + from pyparsing import Word, alphas + + # define grammar of a greeting + greet = Word(alphas) + "," + Word(alphas) + "!" + + hello = "Hello, World!" + print (hello, "->", greet.parseString(hello)) + +The program outputs the following:: + + Hello, World! -> ['Hello', ',', 'World', '!'] + +The Python representation of the grammar is quite readable, owing to the self-explanatory +class names, and the use of '+', '|' and '^' operators. + +The L{ParseResults} object returned from L{ParserElement.parseString<ParserElement.parseString>} can be accessed as a nested list, a dictionary, or an +object with named attributes. + +The pyparsing module handles some of the problems that are typically vexing when writing text parsers: + - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello , World !", etc.) + - quoted strings + - embedded comments +""" + +__version__ = "2.1.10" +__versionTime__ = "07 Oct 2016 01:31 UTC" +__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>" + +import string +from weakref import ref as wkref +import copy +import sys +import warnings +import re +import sre_constants +import collections +import pprint +import traceback +import types +from datetime import datetime + +try: + from _thread import RLock +except ImportError: + from threading import RLock + +try: + from collections import OrderedDict as _OrderedDict +except ImportError: + try: + from ordereddict import OrderedDict as _OrderedDict + except ImportError: + _OrderedDict = None + +#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) + +__all__ = [ +'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', +'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', +'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', +'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', +'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', +'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', +'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', +'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', +'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', +'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums', +'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno', +'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral', +'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables', +'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', +'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', +'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', +'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass', +'CloseMatch', 'tokenMap', 'pyparsing_common', +] + +system_version = tuple(sys.version_info)[:3] +PY_3 = system_version[0] == 3 +if PY_3: + _MAX_INT = sys.maxsize + basestring = str + unichr = chr + _ustr = str + + # build list of single arg builtins, that can be used as parse actions + singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max] + +else: + _MAX_INT = sys.maxint + range = xrange + + def _ustr(obj): + """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries + str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It + then < returns the unicode object | encodes it with the default encoding | ... >. + """ + if isinstance(obj,unicode): + return obj + + try: + # If this works, then _ustr(obj) has the same behaviour as str(obj), so + # it won't break any existing code. + return str(obj) + + except UnicodeEncodeError: + # Else encode it + ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace') + xmlcharref = Regex('&#\d+;') + xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:]) + return xmlcharref.transformString(ret) + + # build list of single arg builtins, tolerant of Python version, that can be used as parse actions + singleArgBuiltins = [] + import __builtin__ + for fname in "sum len sorted reversed list tuple set any all min max".split(): + try: + singleArgBuiltins.append(getattr(__builtin__,fname)) + except AttributeError: + continue + +_generatorType = type((y for y in range(1))) + +def _xml_escape(data): + """Escape &, <, >, ", ', etc. in a string of data.""" + + # ampersand must be replaced first + from_symbols = '&><"\'' + to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split()) + for from_,to_ in zip(from_symbols, to_symbols): + data = data.replace(from_, to_) + return data + +class _Constants(object): + pass + +alphas = string.ascii_uppercase + string.ascii_lowercase +nums = "0123456789" +hexnums = nums + "ABCDEFabcdef" +alphanums = alphas + nums +_bslash = chr(92) +printables = "".join(c for c in string.printable if c not in string.whitespace) + +class ParseBaseException(Exception): + """base exception class for all parsing runtime exceptions""" + # Performance tuning: we construct a *lot* of these, so keep this + # constructor as small and fast as possible + def __init__( self, pstr, loc=0, msg=None, elem=None ): + self.loc = loc + if msg is None: + self.msg = pstr + self.pstr = "" + else: + self.msg = msg + self.pstr = pstr + self.parserElement = elem + self.args = (pstr, loc, msg) + + @classmethod + def _from_exception(cls, pe): + """ + internal factory method to simplify creating one type of ParseException + from another - avoids having __init__ signature conflicts among subclasses + """ + return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) + + def __getattr__( self, aname ): + """supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + """ + if( aname == "lineno" ): + return lineno( self.loc, self.pstr ) + elif( aname in ("col", "column") ): + return col( self.loc, self.pstr ) + elif( aname == "line" ): + return line( self.loc, self.pstr ) + else: + raise AttributeError(aname) + + def __str__( self ): + return "%s (at char %d), (line:%d, col:%d)" % \ + ( self.msg, self.loc, self.lineno, self.column ) + def __repr__( self ): + return _ustr(self) + def markInputline( self, markerString = ">!<" ): + """Extracts the exception line from the input string, and marks + the location of the exception with a special symbol. + """ + line_str = self.line + line_column = self.column - 1 + if markerString: + line_str = "".join((line_str[:line_column], + markerString, line_str[line_column:])) + return line_str.strip() + def __dir__(self): + return "lineno col line".split() + dir(type(self)) + +class ParseException(ParseBaseException): + """ + Exception thrown when parse expressions don't match class; + supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + + Example:: + try: + Word(nums).setName("integer").parseString("ABC") + except ParseException as pe: + print(pe) + print("column: {}".format(pe.col)) + + prints:: + Expected integer (at char 0), (line:1, col:1) + column: 1 + """ + pass + +class ParseFatalException(ParseBaseException): + """user-throwable exception thrown when inconsistent parse content + is found; stops all parsing immediately""" + pass + +class ParseSyntaxException(ParseFatalException): + """just like L{ParseFatalException}, but thrown internally when an + L{ErrorStop<And._ErrorStop>} ('-' operator) indicates that parsing is to stop + immediately because an unbacktrackable syntax error has been found""" + pass + +#~ class ReparseException(ParseBaseException): + #~ """Experimental class - parse actions can raise this exception to cause + #~ pyparsing to reparse the input string: + #~ - with a modified input string, and/or + #~ - with a modified start location + #~ Set the values of the ReparseException in the constructor, and raise the + #~ exception in a parse action to cause pyparsing to use the new string/location. + #~ Setting the values as None causes no change to be made. + #~ """ + #~ def __init_( self, newstring, restartLoc ): + #~ self.newParseText = newstring + #~ self.reparseLoc = restartLoc + +class RecursiveGrammarException(Exception): + """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive""" + def __init__( self, parseElementList ): + self.parseElementTrace = parseElementList + + def __str__( self ): + return "RecursiveGrammarException: %s" % self.parseElementTrace + +class _ParseResultsWithOffset(object): + def __init__(self,p1,p2): + self.tup = (p1,p2) + def __getitem__(self,i): + return self.tup[i] + def __repr__(self): + return repr(self.tup[0]) + def setOffset(self,i): + self.tup = (self.tup[0],i) + +class ParseResults(object): + """ + Structured parse results, to provide multiple means of access to the parsed data: + - as a list (C{len(results)}) + - by list index (C{results[0], results[1]}, etc.) + - by attribute (C{results.<resultsName>} - see L{ParserElement.setResultsName}) + + Example:: + integer = Word(nums) + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + # equivalent form: + # date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + # parseString returns a ParseResults object + result = date_str.parseString("1999/12/31") + + def test(s, fn=repr): + print("%s -> %s" % (s, fn(eval(s)))) + test("list(result)") + test("result[0]") + test("result['month']") + test("result.day") + test("'month' in result") + test("'minutes' in result") + test("result.dump()", str) + prints:: + list(result) -> ['1999', '/', '12', '/', '31'] + result[0] -> '1999' + result['month'] -> '12' + result.day -> '31' + 'month' in result -> True + 'minutes' in result -> False + result.dump() -> ['1999', '/', '12', '/', '31'] + - day: 31 + - month: 12 + - year: 1999 + """ + def __new__(cls, toklist=None, name=None, asList=True, modal=True ): + if isinstance(toklist, cls): + return toklist + retobj = object.__new__(cls) + retobj.__doinit = True + return retobj + + # Performance tuning: we construct a *lot* of these, so keep this + # constructor as small and fast as possible + def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ): + if self.__doinit: + self.__doinit = False + self.__name = None + self.__parent = None + self.__accumNames = {} + self.__asList = asList + self.__modal = modal + if toklist is None: + toklist = [] + if isinstance(toklist, list): + self.__toklist = toklist[:] + elif isinstance(toklist, _generatorType): + self.__toklist = list(toklist) + else: + self.__toklist = [toklist] + self.__tokdict = dict() + + if name is not None and name: + if not modal: + self.__accumNames[name] = 0 + if isinstance(name,int): + name = _ustr(name) # will always return a str, but use _ustr for consistency + self.__name = name + if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])): + if isinstance(toklist,basestring): + toklist = [ toklist ] + if asList: + if isinstance(toklist,ParseResults): + self[name] = _ParseResultsWithOffset(toklist.copy(),0) + else: + self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0) + self[name].__name = name + else: + try: + self[name] = toklist[0] + except (KeyError,TypeError,IndexError): + self[name] = toklist + + def __getitem__( self, i ): + if isinstance( i, (int,slice) ): + return self.__toklist[i] + else: + if i not in self.__accumNames: + return self.__tokdict[i][-1][0] + else: + return ParseResults([ v[0] for v in self.__tokdict[i] ]) + + def __setitem__( self, k, v, isinstance=isinstance ): + if isinstance(v,_ParseResultsWithOffset): + self.__tokdict[k] = self.__tokdict.get(k,list()) + [v] + sub = v[0] + elif isinstance(k,(int,slice)): + self.__toklist[k] = v + sub = v + else: + self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)] + sub = v + if isinstance(sub,ParseResults): + sub.__parent = wkref(self) + + def __delitem__( self, i ): + if isinstance(i,(int,slice)): + mylen = len( self.__toklist ) + del self.__toklist[i] + + # convert int to slice + if isinstance(i, int): + if i < 0: + i += mylen + i = slice(i, i+1) + # get removed indices + removed = list(range(*i.indices(mylen))) + removed.reverse() + # fixup indices in token dictionary + for name,occurrences in self.__tokdict.items(): + for j in removed: + for k, (value, position) in enumerate(occurrences): + occurrences[k] = _ParseResultsWithOffset(value, position - (position > j)) + else: + del self.__tokdict[i] + + def __contains__( self, k ): + return k in self.__tokdict + + def __len__( self ): return len( self.__toklist ) + def __bool__(self): return ( not not self.__toklist ) + __nonzero__ = __bool__ + def __iter__( self ): return iter( self.__toklist ) + def __reversed__( self ): return iter( self.__toklist[::-1] ) + def _iterkeys( self ): + if hasattr(self.__tokdict, "iterkeys"): + return self.__tokdict.iterkeys() + else: + return iter(self.__tokdict) + + def _itervalues( self ): + return (self[k] for k in self._iterkeys()) + + def _iteritems( self ): + return ((k, self[k]) for k in self._iterkeys()) + + if PY_3: + keys = _iterkeys + """Returns an iterator of all named result keys (Python 3.x only).""" + + values = _itervalues + """Returns an iterator of all named result values (Python 3.x only).""" + + items = _iteritems + """Returns an iterator of all named result key-value tuples (Python 3.x only).""" + + else: + iterkeys = _iterkeys + """Returns an iterator of all named result keys (Python 2.x only).""" + + itervalues = _itervalues + """Returns an iterator of all named result values (Python 2.x only).""" + + iteritems = _iteritems + """Returns an iterator of all named result key-value tuples (Python 2.x only).""" + + def keys( self ): + """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x).""" + return list(self.iterkeys()) + + def values( self ): + """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x).""" + return list(self.itervalues()) + + def items( self ): + """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x).""" + return list(self.iteritems()) + + def haskeys( self ): + """Since keys() returns an iterator, this method is helpful in bypassing + code that looks for the existence of any defined results names.""" + return bool(self.__tokdict) + + def pop( self, *args, **kwargs): + """ + Removes and returns item at specified index (default=C{last}). + Supports both C{list} and C{dict} semantics for C{pop()}. If passed no + argument or an integer argument, it will use C{list} semantics + and pop tokens from the list of parsed tokens. If passed a + non-integer argument (most likely a string), it will use C{dict} + semantics and pop the corresponding value from any defined + results names. A second default return value argument is + supported, just as in C{dict.pop()}. + + Example:: + def remove_first(tokens): + tokens.pop(0) + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321'] + + label = Word(alphas) + patt = label("LABEL") + OneOrMore(Word(nums)) + print(patt.parseString("AAB 123 321").dump()) + + # Use pop() in a parse action to remove named result (note that corresponding value is not + # removed from list form of results) + def remove_LABEL(tokens): + tokens.pop("LABEL") + return tokens + patt.addParseAction(remove_LABEL) + print(patt.parseString("AAB 123 321").dump()) + prints:: + ['AAB', '123', '321'] + - LABEL: AAB + + ['AAB', '123', '321'] + """ + if not args: + args = [-1] + for k,v in kwargs.items(): + if k == 'default': + args = (args[0], v) + else: + raise TypeError("pop() got an unexpected keyword argument '%s'" % k) + if (isinstance(args[0], int) or + len(args) == 1 or + args[0] in self): + index = args[0] + ret = self[index] + del self[index] + return ret + else: + defaultvalue = args[1] + return defaultvalue + + def get(self, key, defaultValue=None): + """ + Returns named result matching the given key, or if there is no + such name, then returns the given C{defaultValue} or C{None} if no + C{defaultValue} is specified. + + Similar to C{dict.get()}. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString("1999/12/31") + print(result.get("year")) # -> '1999' + print(result.get("hour", "not specified")) # -> 'not specified' + print(result.get("hour")) # -> None + """ + if key in self: + return self[key] + else: + return defaultValue + + def insert( self, index, insStr ): + """ + Inserts new element at location index in the list of parsed tokens. + + Similar to C{list.insert()}. + + Example:: + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to insert the parse location in the front of the parsed results + def insert_locn(locn, tokens): + tokens.insert(0, locn) + print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321'] + """ + self.__toklist.insert(index, insStr) + # fixup indices in token dictionary + for name,occurrences in self.__tokdict.items(): + for k, (value, position) in enumerate(occurrences): + occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) + + def append( self, item ): + """ + Add single element to end of ParseResults list of elements. + + Example:: + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to compute the sum of the parsed integers, and add it to the end + def append_sum(tokens): + tokens.append(sum(map(int, tokens))) + print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444] + """ + self.__toklist.append(item) + + def extend( self, itemseq ): + """ + Add sequence of elements to end of ParseResults list of elements. + + Example:: + patt = OneOrMore(Word(alphas)) + + # use a parse action to append the reverse of the matched strings, to make a palindrome + def make_palindrome(tokens): + tokens.extend(reversed([t[::-1] for t in tokens])) + return ''.join(tokens) + print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' + """ + if isinstance(itemseq, ParseResults): + self += itemseq + else: + self.__toklist.extend(itemseq) + + def clear( self ): + """ + Clear all elements and results names. + """ + del self.__toklist[:] + self.__tokdict.clear() + + def __getattr__( self, name ): + try: + return self[name] + except KeyError: + return "" + + if name in self.__tokdict: + if name not in self.__accumNames: + return self.__tokdict[name][-1][0] + else: + return ParseResults([ v[0] for v in self.__tokdict[name] ]) + else: + return "" + + def __add__( self, other ): + ret = self.copy() + ret += other + return ret + + def __iadd__( self, other ): + if other.__tokdict: + offset = len(self.__toklist) + addoffset = lambda a: offset if a<0 else a+offset + otheritems = other.__tokdict.items() + otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) ) + for (k,vlist) in otheritems for v in vlist] + for k,v in otherdictitems: + self[k] = v + if isinstance(v[0],ParseResults): + v[0].__parent = wkref(self) + + self.__toklist += other.__toklist + self.__accumNames.update( other.__accumNames ) + return self + + def __radd__(self, other): + if isinstance(other,int) and other == 0: + # useful for merging many ParseResults using sum() builtin + return self.copy() + else: + # this may raise a TypeError - so be it + return other + self + + def __repr__( self ): + return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) ) + + def __str__( self ): + return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']' + + def _asStringList( self, sep='' ): + out = [] + for item in self.__toklist: + if out and sep: + out.append(sep) + if isinstance( item, ParseResults ): + out += item._asStringList() + else: + out.append( _ustr(item) ) + return out + + def asList( self ): + """ + Returns the parse results as a nested list of matching tokens, all converted to strings. + + Example:: + patt = OneOrMore(Word(alphas)) + result = patt.parseString("sldkj lsdkj sldkj") + # even though the result prints in string-like form, it is actually a pyparsing ParseResults + print(type(result), result) # -> <class 'pyparsing.ParseResults'> ['sldkj', 'lsdkj', 'sldkj'] + + # Use asList() to create an actual list + result_list = result.asList() + print(type(result_list), result_list) # -> <class 'list'> ['sldkj', 'lsdkj', 'sldkj'] + """ + return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist] + + def asDict( self ): + """ + Returns the named parse results as a nested dictionary. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(type(result), repr(result)) # -> <class 'pyparsing.ParseResults'> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) + + result_dict = result.asDict() + print(type(result_dict), repr(result_dict)) # -> <class 'dict'> {'day': '1999', 'year': '12', 'month': '31'} + + # even though a ParseResults supports dict-like access, sometime you just need to have a dict + import json + print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable + print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"} + """ + if PY_3: + item_fn = self.items + else: + item_fn = self.iteritems + + def toItem(obj): + if isinstance(obj, ParseResults): + if obj.haskeys(): + return obj.asDict() + else: + return [toItem(v) for v in obj] + else: + return obj + + return dict((k,toItem(v)) for k,v in item_fn()) + + def copy( self ): + """ + Returns a new copy of a C{ParseResults} object. + """ + ret = ParseResults( self.__toklist ) + ret.__tokdict = self.__tokdict.copy() + ret.__parent = self.__parent + ret.__accumNames.update( self.__accumNames ) + ret.__name = self.__name + return ret + + def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ): + """ + (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names. + """ + nl = "\n" + out = [] + namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items() + for v in vlist) + nextLevelIndent = indent + " " + + # collapse out indents if formatting is not desired + if not formatted: + indent = "" + nextLevelIndent = "" + nl = "" + + selfTag = None + if doctag is not None: + selfTag = doctag + else: + if self.__name: + selfTag = self.__name + + if not selfTag: + if namedItemsOnly: + return "" + else: + selfTag = "ITEM" + + out += [ nl, indent, "<", selfTag, ">" ] + + for i,res in enumerate(self.__toklist): + if isinstance(res,ParseResults): + if i in namedItems: + out += [ res.asXML(namedItems[i], + namedItemsOnly and doctag is None, + nextLevelIndent, + formatted)] + else: + out += [ res.asXML(None, + namedItemsOnly and doctag is None, + nextLevelIndent, + formatted)] + else: + # individual token, see if there is a name for it + resTag = None + if i in namedItems: + resTag = namedItems[i] + if not resTag: + if namedItemsOnly: + continue + else: + resTag = "ITEM" + xmlBodyText = _xml_escape(_ustr(res)) + out += [ nl, nextLevelIndent, "<", resTag, ">", + xmlBodyText, + "</", resTag, ">" ] + + out += [ nl, indent, "</", selfTag, ">" ] + return "".join(out) + + def __lookup(self,sub): + for k,vlist in self.__tokdict.items(): + for v,loc in vlist: + if sub is v: + return k + return None + + def getName(self): + """ + Returns the results name for this token expression. Useful when several + different expressions might match at a particular location. + + Example:: + integer = Word(nums) + ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") + house_number_expr = Suppress('#') + Word(nums, alphanums) + user_data = (Group(house_number_expr)("house_number") + | Group(ssn_expr)("ssn") + | Group(integer)("age")) + user_info = OneOrMore(user_data) + + result = user_info.parseString("22 111-22-3333 #221B") + for item in result: + print(item.getName(), ':', item[0]) + prints:: + age : 22 + ssn : 111-22-3333 + house_number : 221B + """ + if self.__name: + return self.__name + elif self.__parent: + par = self.__parent() + if par: + return par.__lookup(self) + else: + return None + elif (len(self) == 1 and + len(self.__tokdict) == 1 and + next(iter(self.__tokdict.values()))[0][1] in (0,-1)): + return next(iter(self.__tokdict.keys())) + else: + return None + + def dump(self, indent='', depth=0, full=True): + """ + Diagnostic method for listing out the contents of a C{ParseResults}. + Accepts an optional C{indent} argument so that this string can be embedded + in a nested display of other data. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(result.dump()) + prints:: + ['12', '/', '31', '/', '1999'] + - day: 1999 + - month: 31 + - year: 12 + """ + out = [] + NL = '\n' + out.append( indent+_ustr(self.asList()) ) + if full: + if self.haskeys(): + items = sorted((str(k), v) for k,v in self.items()) + for k,v in items: + if out: + out.append(NL) + out.append( "%s%s- %s: " % (indent,(' '*depth), k) ) + if isinstance(v,ParseResults): + if v: + out.append( v.dump(indent,depth+1) ) + else: + out.append(_ustr(v)) + else: + out.append(repr(v)) + elif any(isinstance(vv,ParseResults) for vv in self): + v = self + for i,vv in enumerate(v): + if isinstance(vv,ParseResults): + out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),vv.dump(indent,depth+1) )) + else: + out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),_ustr(vv))) + + return "".join(out) + + def pprint(self, *args, **kwargs): + """ + Pretty-printer for parsed results as a list, using the C{pprint} module. + Accepts additional positional or keyword args as defined for the + C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint}) + + Example:: + ident = Word(alphas, alphanums) + num = Word(nums) + func = Forward() + term = ident | num | Group('(' + func + ')') + func <<= ident + Group(Optional(delimitedList(term))) + result = func.parseString("fna a,b,(fnb c,d,200),100") + result.pprint(width=40) + prints:: + ['fna', + ['a', + 'b', + ['(', 'fnb', ['c', 'd', '200'], ')'], + '100']] + """ + pprint.pprint(self.asList(), *args, **kwargs) + + # add support for pickle protocol + def __getstate__(self): + return ( self.__toklist, + ( self.__tokdict.copy(), + self.__parent is not None and self.__parent() or None, + self.__accumNames, + self.__name ) ) + + def __setstate__(self,state): + self.__toklist = state[0] + (self.__tokdict, + par, + inAccumNames, + self.__name) = state[1] + self.__accumNames = {} + self.__accumNames.update(inAccumNames) + if par is not None: + self.__parent = wkref(par) + else: + self.__parent = None + + def __getnewargs__(self): + return self.__toklist, self.__name, self.__asList, self.__modal + + def __dir__(self): + return (dir(type(self)) + list(self.keys())) + +collections.MutableMapping.register(ParseResults) + +def col (loc,strg): + """Returns current column within a string, counting newlines as line separators. + The first column is number 1. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information + on parsing strings containing C{<TAB>}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + s = strg + return 1 if 0<loc<len(s) and s[loc-1] == '\n' else loc - s.rfind("\n", 0, loc) + +def lineno(loc,strg): + """Returns current line number within a string, counting newlines as line separators. + The first line is number 1. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information + on parsing strings containing C{<TAB>}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + return strg.count("\n",0,loc) + 1 + +def line( loc, strg ): + """Returns the line of text containing loc within a string, counting newlines as line separators. + """ + lastCR = strg.rfind("\n", 0, loc) + nextCR = strg.find("\n", loc) + if nextCR >= 0: + return strg[lastCR+1:nextCR] + else: + return strg[lastCR+1:] + +def _defaultStartDebugAction( instring, loc, expr ): + print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))) + +def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ): + print ("Matched " + _ustr(expr) + " -> " + str(toks.asList())) + +def _defaultExceptionDebugAction( instring, loc, expr, exc ): + print ("Exception raised:" + _ustr(exc)) + +def nullDebugAction(*args): + """'Do-nothing' debug action, to suppress debugging output during parsing.""" + pass + +# Only works on Python 3.x - nonlocal is toxic to Python 2 installs +#~ 'decorator to trim function calls to match the arity of the target' +#~ def _trim_arity(func, maxargs=3): + #~ if func in singleArgBuiltins: + #~ return lambda s,l,t: func(t) + #~ limit = 0 + #~ foundArity = False + #~ def wrapper(*args): + #~ nonlocal limit,foundArity + #~ while 1: + #~ try: + #~ ret = func(*args[limit:]) + #~ foundArity = True + #~ return ret + #~ except TypeError: + #~ if limit == maxargs or foundArity: + #~ raise + #~ limit += 1 + #~ continue + #~ return wrapper + +# this version is Python 2.x-3.x cross-compatible +'decorator to trim function calls to match the arity of the target' +def _trim_arity(func, maxargs=2): + if func in singleArgBuiltins: + return lambda s,l,t: func(t) + limit = [0] + foundArity = [False] + + # traceback return data structure changed in Py3.5 - normalize back to plain tuples + if system_version[:2] >= (3,5): + def extract_stack(limit=0): + # special handling for Python 3.5.0 - extra deep call stack by 1 + offset = -3 if system_version == (3,5,0) else -2 + frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset] + return [(frame_summary.filename, frame_summary.lineno)] + def extract_tb(tb, limit=0): + frames = traceback.extract_tb(tb, limit=limit) + frame_summary = frames[-1] + return [(frame_summary.filename, frame_summary.lineno)] + else: + extract_stack = traceback.extract_stack + extract_tb = traceback.extract_tb + + # synthesize what would be returned by traceback.extract_stack at the call to + # user's parse action 'func', so that we don't incur call penalty at parse time + + LINE_DIFF = 6 + # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND + # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!! + this_line = extract_stack(limit=2)[-1] + pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF) + + def wrapper(*args): + while 1: + try: + ret = func(*args[limit[0]:]) + foundArity[0] = True + return ret + except TypeError: + # re-raise TypeErrors if they did not come from our arity testing + if foundArity[0]: + raise + else: + try: + tb = sys.exc_info()[-1] + if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth: + raise + finally: + del tb + + if limit[0] <= maxargs: + limit[0] += 1 + continue + raise + + # copy func name to wrapper for sensible debug output + func_name = "<parse action>" + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + wrapper.__name__ = func_name + + return wrapper + +class ParserElement(object): + """Abstract base level parser element class.""" + DEFAULT_WHITE_CHARS = " \n\t\r" + verbose_stacktrace = False + + @staticmethod + def setDefaultWhitespaceChars( chars ): + r""" + Overrides the default whitespace chars + + Example:: + # default whitespace chars are space, <TAB> and newline + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def', 'ghi', 'jkl'] + + # change to just treat newline as significant + ParserElement.setDefaultWhitespaceChars(" \t") + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def'] + """ + ParserElement.DEFAULT_WHITE_CHARS = chars + + @staticmethod + def inlineLiteralsUsing(cls): + """ + Set class to be used for inclusion of string literals into a parser. + + Example:: + # default literal class used is Literal + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + + # change to Suppress + ParserElement.inlineLiteralsUsing(Suppress) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '12', '31'] + """ + ParserElement._literalStringClass = cls + + def __init__( self, savelist=False ): + self.parseAction = list() + self.failAction = None + #~ self.name = "<unknown>" # don't define self.name, let subclasses try/except upcall + self.strRepr = None + self.resultsName = None + self.saveAsList = savelist + self.skipWhitespace = True + self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS + self.copyDefaultWhiteChars = True + self.mayReturnEmpty = False # used when checking for left-recursion + self.keepTabs = False + self.ignoreExprs = list() + self.debug = False + self.streamlined = False + self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index + self.errmsg = "" + self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all) + self.debugActions = ( None, None, None ) #custom debug actions + self.re = None + self.callPreparse = True # used to avoid redundant calls to preParse + self.callDuringTry = False + + def copy( self ): + """ + Make a copy of this C{ParserElement}. Useful for defining different parse actions + for the same parsing pattern, using copies of the original parse element. + + Example:: + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K") + integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + + print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M")) + prints:: + [5120, 100, 655360, 268435456] + Equivalent form of C{expr.copy()} is just C{expr()}:: + integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + """ + cpy = copy.copy( self ) + cpy.parseAction = self.parseAction[:] + cpy.ignoreExprs = self.ignoreExprs[:] + if self.copyDefaultWhiteChars: + cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS + return cpy + + def setName( self, name ): + """ + Define name for this expression, makes debugging and exception messages clearer. + + Example:: + Word(nums).parseString("ABC") # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1) + Word(nums).setName("integer").parseString("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) + """ + self.name = name + self.errmsg = "Expected " + self.name + if hasattr(self,"exception"): + self.exception.msg = self.errmsg + return self + + def setResultsName( self, name, listAllMatches=False ): + """ + Define name for referencing matching tokens as a nested attribute + of the returned parse results. + NOTE: this returns a *copy* of the original C{ParserElement} object; + this is so that the client can define a basic element, such as an + integer, and reference it in multiple places with different names. + + You can also set results names using the abbreviated syntax, + C{expr("name")} in place of C{expr.setResultsName("name")} - + see L{I{__call__}<__call__>}. + + Example:: + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + + # equivalent form: + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + """ + newself = self.copy() + if name.endswith("*"): + name = name[:-1] + listAllMatches=True + newself.resultsName = name + newself.modalResults = not listAllMatches + return newself + + def setBreak(self,breakFlag = True): + """Method to invoke the Python pdb debugger when this element is + about to be parsed. Set C{breakFlag} to True to enable, False to + disable. + """ + if breakFlag: + _parseMethod = self._parse + def breaker(instring, loc, doActions=True, callPreParse=True): + import pdb + pdb.set_trace() + return _parseMethod( instring, loc, doActions, callPreParse ) + breaker._originalParseMethod = _parseMethod + self._parse = breaker + else: + if hasattr(self._parse,"_originalParseMethod"): + self._parse = self._parse._originalParseMethod + return self + + def setParseAction( self, *fns, **kwargs ): + """ + Define action to perform when successfully matching parse element definition. + Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)}, + C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where: + - s = the original string being parsed (see note below) + - loc = the location of the matching substring + - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object + If the functions in fns modify the tokens, they can return them as the return + value from fn, and the modified list of tokens will replace the original. + Otherwise, fn does not need to return any value. + + Optional keyword arguments: + - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{parseString}<parseString>} for more information + on parsing strings containing C{<TAB>}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + + Example:: + integer = Word(nums) + date_str = integer + '/' + integer + '/' + integer + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + # use parse action to convert to ints at parse time + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + date_str = integer + '/' + integer + '/' + integer + + # note that integer fields are now ints, not strings + date_str.parseString("1999/12/31") # -> [1999, '/', 12, '/', 31] + """ + self.parseAction = list(map(_trim_arity, list(fns))) + self.callDuringTry = kwargs.get("callDuringTry", False) + return self + + def addParseAction( self, *fns, **kwargs ): + """ + Add parse action to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}. + + See examples in L{I{copy}<copy>}. + """ + self.parseAction += list(map(_trim_arity, list(fns))) + self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) + return self + + def addCondition(self, *fns, **kwargs): + """Add a boolean predicate function to expression's list of parse actions. See + L{I{setParseAction}<setParseAction>} for function call signatures. Unlike C{setParseAction}, + functions passed to C{addCondition} need to return boolean success/fail of the condition. + + Optional keyword arguments: + - message = define a custom message to be used in the raised exception + - fatal = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException + + Example:: + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + year_int = integer.copy() + year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later") + date_str = year_int + '/' + integer + '/' + integer + + result = date_str.parseString("1999/12/31") # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1) + """ + msg = kwargs.get("message", "failed user-defined condition") + exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException + for fn in fns: + def pa(s,l,t): + if not bool(_trim_arity(fn)(s,l,t)): + raise exc_type(s,l,msg) + self.parseAction.append(pa) + self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) + return self + + def setFailAction( self, fn ): + """Define action to perform if parsing fails at this expression. + Fail acton fn is a callable function that takes the arguments + C{fn(s,loc,expr,err)} where: + - s = string being parsed + - loc = location where expression match was attempted and failed + - expr = the parse expression that failed + - err = the exception thrown + The function returns no value. It may throw C{L{ParseFatalException}} + if it is desired to stop parsing immediately.""" + self.failAction = fn + return self + + def _skipIgnorables( self, instring, loc ): + exprsFound = True + while exprsFound: + exprsFound = False + for e in self.ignoreExprs: + try: + while 1: + loc,dummy = e._parse( instring, loc ) + exprsFound = True + except ParseException: + pass + return loc + + def preParse( self, instring, loc ): + if self.ignoreExprs: + loc = self._skipIgnorables( instring, loc ) + + if self.skipWhitespace: + wt = self.whiteChars + instrlen = len(instring) + while loc < instrlen and instring[loc] in wt: + loc += 1 + + return loc + + def parseImpl( self, instring, loc, doActions=True ): + return loc, [] + + def postParse( self, instring, loc, tokenlist ): + return tokenlist + + #~ @profile + def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ): + debugging = ( self.debug ) #and doActions ) + + if debugging or self.failAction: + #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) + if (self.debugActions[0] ): + self.debugActions[0]( instring, loc, self ) + if callPreParse and self.callPreparse: + preloc = self.preParse( instring, loc ) + else: + preloc = loc + tokensStart = preloc + try: + try: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + except IndexError: + raise ParseException( instring, len(instring), self.errmsg, self ) + except ParseBaseException as err: + #~ print ("Exception raised:", err) + if self.debugActions[2]: + self.debugActions[2]( instring, tokensStart, self, err ) + if self.failAction: + self.failAction( instring, tokensStart, self, err ) + raise + else: + if callPreParse and self.callPreparse: + preloc = self.preParse( instring, loc ) + else: + preloc = loc + tokensStart = preloc + if self.mayIndexError or loc >= len(instring): + try: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + except IndexError: + raise ParseException( instring, len(instring), self.errmsg, self ) + else: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + + tokens = self.postParse( instring, loc, tokens ) + + retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults ) + if self.parseAction and (doActions or self.callDuringTry): + if debugging: + try: + for fn in self.parseAction: + tokens = fn( instring, tokensStart, retTokens ) + if tokens is not None: + retTokens = ParseResults( tokens, + self.resultsName, + asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), + modal=self.modalResults ) + except ParseBaseException as err: + #~ print "Exception raised in user parse action:", err + if (self.debugActions[2] ): + self.debugActions[2]( instring, tokensStart, self, err ) + raise + else: + for fn in self.parseAction: + tokens = fn( instring, tokensStart, retTokens ) + if tokens is not None: + retTokens = ParseResults( tokens, + self.resultsName, + asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), + modal=self.modalResults ) + + if debugging: + #~ print ("Matched",self,"->",retTokens.asList()) + if (self.debugActions[1] ): + self.debugActions[1]( instring, tokensStart, loc, self, retTokens ) + + return loc, retTokens + + def tryParse( self, instring, loc ): + try: + return self._parse( instring, loc, doActions=False )[0] + except ParseFatalException: + raise ParseException( instring, loc, self.errmsg, self) + + def canParseNext(self, instring, loc): + try: + self.tryParse(instring, loc) + except (ParseException, IndexError): + return False + else: + return True + + class _UnboundedCache(object): + def __init__(self): + cache = {} + self.not_in_cache = not_in_cache = object() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + + def clear(self): + cache.clear() + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + + if _OrderedDict is not None: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = _OrderedDict() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + if len(cache) > size: + cache.popitem(False) + + def clear(self): + cache.clear() + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + + else: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = {} + key_fifo = collections.deque([], size) + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + if len(cache) > size: + cache.pop(key_fifo.popleft(), None) + key_fifo.append(key) + + def clear(self): + cache.clear() + key_fifo.clear() + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + + # argument cache for optimizing repeated calls when backtracking through recursive expressions + packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail + packrat_cache_lock = RLock() + packrat_cache_stats = [0, 0] + + # this method gets repeatedly called during backtracking with the same arguments - + # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression + def _parseCache( self, instring, loc, doActions=True, callPreParse=True ): + HIT, MISS = 0, 1 + lookup = (self, instring, loc, callPreParse, doActions) + with ParserElement.packrat_cache_lock: + cache = ParserElement.packrat_cache + value = cache.get(lookup) + if value is cache.not_in_cache: + ParserElement.packrat_cache_stats[MISS] += 1 + try: + value = self._parseNoCache(instring, loc, doActions, callPreParse) + except ParseBaseException as pe: + # cache a copy of the exception, without the traceback + cache.set(lookup, pe.__class__(*pe.args)) + raise + else: + cache.set(lookup, (value[0], value[1].copy())) + return value + else: + ParserElement.packrat_cache_stats[HIT] += 1 + if isinstance(value, Exception): + raise value + return (value[0], value[1].copy()) + + _parse = _parseNoCache + + @staticmethod + def resetCache(): + ParserElement.packrat_cache.clear() + ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats) + + _packratEnabled = False + @staticmethod + def enablePackrat(cache_size_limit=128): + """Enables "packrat" parsing, which adds memoizing to the parsing logic. + Repeated parse attempts at the same string location (which happens + often in many complex grammars) can immediately return a cached value, + instead of re-executing parsing/validating code. Memoizing is done of + both valid results and parsing exceptions. + + Parameters: + - cache_size_limit - (default=C{128}) - if an integer value is provided + will limit the size of the packrat cache; if None is passed, then + the cache size will be unbounded; if 0 is passed, the cache will + be effectively disabled. + + This speedup may break existing programs that use parse actions that + have side-effects. For this reason, packrat parsing is disabled when + you first import pyparsing. To activate the packrat feature, your + program must call the class method C{ParserElement.enablePackrat()}. If + your program uses C{psyco} to "compile as you go", you must call + C{enablePackrat} before calling C{psyco.full()}. If you do not do this, + Python will crash. For best results, call C{enablePackrat()} immediately + after importing pyparsing. + + Example:: + import pyparsing + pyparsing.ParserElement.enablePackrat() + """ + if not ParserElement._packratEnabled: + ParserElement._packratEnabled = True + if cache_size_limit is None: + ParserElement.packrat_cache = ParserElement._UnboundedCache() + else: + ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit) + ParserElement._parse = ParserElement._parseCache + + def parseString( self, instring, parseAll=False ): + """ + Execute the parse expression with the given string. + This is the main interface to the client code, once the complete + expression has been built. + + If you want the grammar to require that the entire input string be + successfully parsed, then set C{parseAll} to True (equivalent to ending + the grammar with C{L{StringEnd()}}). + + Note: C{parseString} implicitly calls C{expandtabs()} on the input string, + in order to report proper column numbers in parse actions. + If the input string contains tabs and + the grammar uses parse actions that use the C{loc} argument to index into the + string being parsed, you can ensure you have a consistent view of the input + string by: + - calling C{parseWithTabs} on your grammar before calling C{parseString} + (see L{I{parseWithTabs}<parseWithTabs>}) + - define your parse action using the full C{(s,loc,toks)} signature, and + reference the input string using the parse action's C{s} argument + - explictly expand the tabs in your input string before calling + C{parseString} + + Example:: + Word('a').parseString('aaaaabaaa') # -> ['aaaaa'] + Word('a').parseString('aaaaabaaa', parseAll=True) # -> Exception: Expected end of text + """ + ParserElement.resetCache() + if not self.streamlined: + self.streamline() + #~ self.saveAsList = True + for e in self.ignoreExprs: + e.streamline() + if not self.keepTabs: + instring = instring.expandtabs() + try: + loc, tokens = self._parse( instring, 0 ) + if parseAll: + loc = self.preParse( instring, loc ) + se = Empty() + StringEnd() + se._parse( instring, loc ) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + else: + return tokens + + def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ): + """ + Scan the input string for expression matches. Each match will return the + matching tokens, start location, and end location. May be called with optional + C{maxMatches} argument, to clip scanning after 'n' matches are found. If + C{overlap} is specified, then overlapping matches will be reported. + + Note that the start and end locations are reported relative to the string + being parsed. See L{I{parseString}<parseString>} for more information on parsing + strings with embedded tabs. + + Example:: + source = "sldjf123lsdjjkf345sldkjf879lkjsfd987" + print(source) + for tokens,start,end in Word(alphas).scanString(source): + print(' '*start + '^'*(end-start)) + print(' '*start + tokens[0]) + + prints:: + + sldjf123lsdjjkf345sldkjf879lkjsfd987 + ^^^^^ + sldjf + ^^^^^^^ + lsdjjkf + ^^^^^^ + sldkjf + ^^^^^^ + lkjsfd + """ + if not self.streamlined: + self.streamline() + for e in self.ignoreExprs: + e.streamline() + + if not self.keepTabs: + instring = _ustr(instring).expandtabs() + instrlen = len(instring) + loc = 0 + preparseFn = self.preParse + parseFn = self._parse + ParserElement.resetCache() + matches = 0 + try: + while loc <= instrlen and matches < maxMatches: + try: + preloc = preparseFn( instring, loc ) + nextLoc,tokens = parseFn( instring, preloc, callPreParse=False ) + except ParseException: + loc = preloc+1 + else: + if nextLoc > loc: + matches += 1 + yield tokens, preloc, nextLoc + if overlap: + nextloc = preparseFn( instring, loc ) + if nextloc > loc: + loc = nextLoc + else: + loc += 1 + else: + loc = nextLoc + else: + loc = preloc+1 + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def transformString( self, instring ): + """ + Extension to C{L{scanString}}, to modify matching text with modified tokens that may + be returned from a parse action. To use C{transformString}, define a grammar and + attach a parse action to it that modifies the returned token list. + Invoking C{transformString()} on a target string will then scan for matches, + and replace the matched text patterns according to the logic in the parse + action. C{transformString()} returns the resulting transformed string. + + Example:: + wd = Word(alphas) + wd.setParseAction(lambda toks: toks[0].title()) + + print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york.")) + Prints:: + Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York. + """ + out = [] + lastE = 0 + # force preservation of <TAB>s, to minimize unwanted transformation of string, and to + # keep string locs straight between transformString and scanString + self.keepTabs = True + try: + for t,s,e in self.scanString( instring ): + out.append( instring[lastE:s] ) + if t: + if isinstance(t,ParseResults): + out += t.asList() + elif isinstance(t,list): + out += t + else: + out.append(t) + lastE = e + out.append(instring[lastE:]) + out = [o for o in out if o] + return "".join(map(_ustr,_flatten(out))) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def searchString( self, instring, maxMatches=_MAX_INT ): + """ + Another extension to C{L{scanString}}, simplifying the access to the tokens found + to match the given parse expression. May be called with optional + C{maxMatches} argument, to clip searching after 'n' matches are found. + + Example:: + # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters + cap_word = Word(alphas.upper(), alphas.lower()) + + print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")) + prints:: + ['More', 'Iron', 'Lead', 'Gold', 'I'] + """ + try: + return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ]) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False): + """ + Generator method to split a string using the given expression as a separator. + May be called with optional C{maxsplit} argument, to limit the number of splits; + and the optional C{includeSeparators} argument (default=C{False}), if the separating + matching text should be included in the split results. + + Example:: + punc = oneOf(list(".,;:/-!?")) + print(list(punc.split("This, this?, this sentence, is badly punctuated!"))) + prints:: + ['This', ' this', '', ' this sentence', ' is badly punctuated', ''] + """ + splits = 0 + last = 0 + for t,s,e in self.scanString(instring, maxMatches=maxsplit): + yield instring[last:s] + if includeSeparators: + yield t[0] + last = e + yield instring[last:] + + def __add__(self, other ): + """ + Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement + converts them to L{Literal}s by default. + + Example:: + greet = Word(alphas) + "," + Word(alphas) + "!" + hello = "Hello, World!" + print (hello, "->", greet.parseString(hello)) + Prints:: + Hello, World! -> ['Hello', ',', 'World', '!'] + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return And( [ self, other ] ) + + def __radd__(self, other ): + """ + Implementation of + operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other + self + + def __sub__(self, other): + """ + Implementation of - operator, returns C{L{And}} with error stop + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return And( [ self, And._ErrorStop(), other ] ) + + def __rsub__(self, other ): + """ + Implementation of - operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other - self + + def __mul__(self,other): + """ + Implementation of * operator, allows use of C{expr * 3} in place of + C{expr + expr + expr}. Expressions may also me multiplied by a 2-integer + tuple, similar to C{{min,max}} multipliers in regular expressions. Tuples + may also include C{None} as in: + - C{expr*(n,None)} or C{expr*(n,)} is equivalent + to C{expr*n + L{ZeroOrMore}(expr)} + (read as "at least n instances of C{expr}") + - C{expr*(None,n)} is equivalent to C{expr*(0,n)} + (read as "0 to n instances of C{expr}") + - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)} + - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)} + + Note that C{expr*(None,n)} does not raise an exception if + more than n exprs exist in the input stream; that is, + C{expr*(None,n)} does not enforce a maximum number of expr + occurrences. If this behavior is desired, then write + C{expr*(None,n) + ~expr} + """ + if isinstance(other,int): + minElements, optElements = other,0 + elif isinstance(other,tuple): + other = (other + (None, None))[:2] + if other[0] is None: + other = (0, other[1]) + if isinstance(other[0],int) and other[1] is None: + if other[0] == 0: + return ZeroOrMore(self) + if other[0] == 1: + return OneOrMore(self) + else: + return self*other[0] + ZeroOrMore(self) + elif isinstance(other[0],int) and isinstance(other[1],int): + minElements, optElements = other + optElements -= minElements + else: + raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1])) + else: + raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other)) + + if minElements < 0: + raise ValueError("cannot multiply ParserElement by negative value") + if optElements < 0: + raise ValueError("second tuple value must be greater or equal to first tuple value") + if minElements == optElements == 0: + raise ValueError("cannot multiply ParserElement by 0 or (0,0)") + + if (optElements): + def makeOptionalList(n): + if n>1: + return Optional(self + makeOptionalList(n-1)) + else: + return Optional(self) + if minElements: + if minElements == 1: + ret = self + makeOptionalList(optElements) + else: + ret = And([self]*minElements) + makeOptionalList(optElements) + else: + ret = makeOptionalList(optElements) + else: + if minElements == 1: + ret = self + else: + ret = And([self]*minElements) + return ret + + def __rmul__(self, other): + return self.__mul__(other) + + def __or__(self, other ): + """ + Implementation of | operator - returns C{L{MatchFirst}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return MatchFirst( [ self, other ] ) + + def __ror__(self, other ): + """ + Implementation of | operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other | self + + def __xor__(self, other ): + """ + Implementation of ^ operator - returns C{L{Or}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return Or( [ self, other ] ) + + def __rxor__(self, other ): + """ + Implementation of ^ operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other ^ self + + def __and__(self, other ): + """ + Implementation of & operator - returns C{L{Each}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return Each( [ self, other ] ) + + def __rand__(self, other ): + """ + Implementation of & operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other & self + + def __invert__( self ): + """ + Implementation of ~ operator - returns C{L{NotAny}} + """ + return NotAny( self ) + + def __call__(self, name=None): + """ + Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}. + + If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be + passed as C{True}. + + If C{name} is omitted, same as calling C{L{copy}}. + + Example:: + # these are equivalent + userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") + userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") + """ + if name is not None: + return self.setResultsName(name) + else: + return self.copy() + + def suppress( self ): + """ + Suppresses the output of this C{ParserElement}; useful to keep punctuation from + cluttering up returned output. + """ + return Suppress( self ) + + def leaveWhitespace( self ): + """ + Disables the skipping of whitespace before matching the characters in the + C{ParserElement}'s defined pattern. This is normally only used internally by + the pyparsing module, but may be needed in some whitespace-sensitive grammars. + """ + self.skipWhitespace = False + return self + + def setWhitespaceChars( self, chars ): + """ + Overrides the default whitespace chars + """ + self.skipWhitespace = True + self.whiteChars = chars + self.copyDefaultWhiteChars = False + return self + + def parseWithTabs( self ): + """ + Overrides default behavior to expand C{<TAB>}s to spaces before parsing the input string. + Must be called before C{parseString} when the input grammar contains elements that + match C{<TAB>} characters. + """ + self.keepTabs = True + return self + + def ignore( self, other ): + """ + Define expression to be ignored (e.g., comments) while doing pattern + matching; may be called repeatedly, to define multiple comment or other + ignorable patterns. + + Example:: + patt = OneOrMore(Word(alphas)) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj'] + + patt.ignore(cStyleComment) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd'] + """ + if isinstance(other, basestring): + other = Suppress(other) + + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + self.ignoreExprs.append(other) + else: + self.ignoreExprs.append( Suppress( other.copy() ) ) + return self + + def setDebugActions( self, startAction, successAction, exceptionAction ): + """ + Enable display of debugging messages while doing pattern matching. + """ + self.debugActions = (startAction or _defaultStartDebugAction, + successAction or _defaultSuccessDebugAction, + exceptionAction or _defaultExceptionDebugAction) + self.debug = True + return self + + def setDebug( self, flag=True ): + """ + Enable display of debugging messages while doing pattern matching. + Set C{flag} to True to enable, False to disable. + + Example:: + wd = Word(alphas).setName("alphaword") + integer = Word(nums).setName("numword") + term = wd | integer + + # turn on debugging for wd + wd.setDebug() + + OneOrMore(term).parseString("abc 123 xyz 890") + + prints:: + Match alphaword at loc 0(1,1) + Matched alphaword -> ['abc'] + Match alphaword at loc 3(1,4) + Exception raised:Expected alphaword (at char 4), (line:1, col:5) + Match alphaword at loc 7(1,8) + Matched alphaword -> ['xyz'] + Match alphaword at loc 11(1,12) + Exception raised:Expected alphaword (at char 12), (line:1, col:13) + Match alphaword at loc 15(1,16) + Exception raised:Expected alphaword (at char 15), (line:1, col:16) + + The output shown is that produced by the default debug actions - custom debug actions can be + specified using L{setDebugActions}. Prior to attempting + to match the C{wd} expression, the debugging message C{"Match <exprname> at loc <n>(<line>,<col>)"} + is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"} + message is shown. Also note the use of L{setName} to assign a human-readable name to the expression, + which makes debugging and exception messages easier to understand - for instance, the default + name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}. + """ + if flag: + self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction ) + else: + self.debug = False + return self + + def __str__( self ): + return self.name + + def __repr__( self ): + return _ustr(self) + + def streamline( self ): + self.streamlined = True + self.strRepr = None + return self + + def checkRecursion( self, parseElementList ): + pass + + def validate( self, validateTrace=[] ): + """ + Check defined expressions for valid structure, check for infinite recursive definitions. + """ + self.checkRecursion( [] ) + + def parseFile( self, file_or_filename, parseAll=False ): + """ + Execute the parse expression on the given file or filename. + If a filename is specified (instead of a file object), + the entire file is opened, read, and closed before parsing. + """ + try: + file_contents = file_or_filename.read() + except AttributeError: + with open(file_or_filename, "r") as f: + file_contents = f.read() + try: + return self.parseString(file_contents, parseAll) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def __eq__(self,other): + if isinstance(other, ParserElement): + return self is other or vars(self) == vars(other) + elif isinstance(other, basestring): + return self.matches(other) + else: + return super(ParserElement,self)==other + + def __ne__(self,other): + return not (self == other) + + def __hash__(self): + return hash(id(self)) + + def __req__(self,other): + return self == other + + def __rne__(self,other): + return not (self == other) + + def matches(self, testString, parseAll=True): + """ + Method for quick testing of a parser against a test string. Good for simple + inline microtests of sub expressions while building up larger parser. + + Parameters: + - testString - to test against this expression for a match + - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests + + Example:: + expr = Word(nums) + assert expr.matches("100") + """ + try: + self.parseString(_ustr(testString), parseAll=parseAll) + return True + except ParseBaseException: + return False + + def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False): + """ + Execute the parse expression on a series of test strings, showing each + test, the parsed results or where the parse failed. Quick and easy way to + run a parse expression against a list of sample strings. + + Parameters: + - tests - a list of separate test strings, or a multiline string of test strings + - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests + - comment - (default=C{'#'}) - expression for indicating embedded comments in the test + string; pass None to disable comment filtering + - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline; + if False, only dump nested list + - printResults - (default=C{True}) prints test output to stdout + - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing + + Returns: a (success, results) tuple, where success indicates that all tests succeeded + (or failed if C{failureTests} is True), and the results contain a list of lines of each + test's output + + Example:: + number_expr = pyparsing_common.number.copy() + + result = number_expr.runTests(''' + # unsigned integer + 100 + # negative integer + -100 + # float with scientific notation + 6.02e23 + # integer with scientific notation + 1e-12 + ''') + print("Success" if result[0] else "Failed!") + + result = number_expr.runTests(''' + # stray character + 100Z + # missing leading digit before '.' + -.100 + # too many '.' + 3.14.159 + ''', failureTests=True) + print("Success" if result[0] else "Failed!") + prints:: + # unsigned integer + 100 + [100] + + # negative integer + -100 + [-100] + + # float with scientific notation + 6.02e23 + [6.02e+23] + + # integer with scientific notation + 1e-12 + [1e-12] + + Success + + # stray character + 100Z + ^ + FAIL: Expected end of text (at char 3), (line:1, col:4) + + # missing leading digit before '.' + -.100 + ^ + FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1) + + # too many '.' + 3.14.159 + ^ + FAIL: Expected end of text (at char 4), (line:1, col:5) + + Success + + Each test string must be on a single line. If you want to test a string that spans multiple + lines, create a test like this:: + + expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines") + + (Note that this is a raw string literal, you must include the leading 'r'.) + """ + if isinstance(tests, basestring): + tests = list(map(str.strip, tests.rstrip().splitlines())) + if isinstance(comment, basestring): + comment = Literal(comment) + allResults = [] + comments = [] + success = True + for t in tests: + if comment is not None and comment.matches(t, False) or comments and not t: + comments.append(t) + continue + if not t: + continue + out = ['\n'.join(comments), t] + comments = [] + try: + t = t.replace(r'\n','\n') + result = self.parseString(t, parseAll=parseAll) + out.append(result.dump(full=fullDump)) + success = success and not failureTests + except ParseBaseException as pe: + fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else "" + if '\n' in t: + out.append(line(pe.loc, t)) + out.append(' '*(col(pe.loc,t)-1) + '^' + fatal) + else: + out.append(' '*pe.loc + '^' + fatal) + out.append("FAIL: " + str(pe)) + success = success and failureTests + result = pe + except Exception as exc: + out.append("FAIL-EXCEPTION: " + str(exc)) + success = success and failureTests + result = exc + + if printResults: + if fullDump: + out.append('') + print('\n'.join(out)) + + allResults.append((t, result)) + + return success, allResults + + +class Token(ParserElement): + """ + Abstract C{ParserElement} subclass, for defining atomic matching patterns. + """ + def __init__( self ): + super(Token,self).__init__( savelist=False ) + + +class Empty(Token): + """ + An empty token, will always match. + """ + def __init__( self ): + super(Empty,self).__init__() + self.name = "Empty" + self.mayReturnEmpty = True + self.mayIndexError = False + + +class NoMatch(Token): + """ + A token that will never match. + """ + def __init__( self ): + super(NoMatch,self).__init__() + self.name = "NoMatch" + self.mayReturnEmpty = True + self.mayIndexError = False + self.errmsg = "Unmatchable token" + + def parseImpl( self, instring, loc, doActions=True ): + raise ParseException(instring, loc, self.errmsg, self) + + +class Literal(Token): + """ + Token to exactly match a specified string. + + Example:: + Literal('blah').parseString('blah') # -> ['blah'] + Literal('blah').parseString('blahfooblah') # -> ['blah'] + Literal('blah').parseString('bla') # -> Exception: Expected "blah" + + For case-insensitive matching, use L{CaselessLiteral}. + + For keyword matching (force word break before and after the matched string), + use L{Keyword} or L{CaselessKeyword}. + """ + def __init__( self, matchString ): + super(Literal,self).__init__() + self.match = matchString + self.matchLen = len(matchString) + try: + self.firstMatchChar = matchString[0] + except IndexError: + warnings.warn("null string passed to Literal; use Empty() instead", + SyntaxWarning, stacklevel=2) + self.__class__ = Empty + self.name = '"%s"' % _ustr(self.match) + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = False + self.mayIndexError = False + + # Performance tuning: this routine gets called a *lot* + # if this is a single character match string and the first character matches, + # short-circuit as quickly as possible, and avoid calling startswith + #~ @profile + def parseImpl( self, instring, loc, doActions=True ): + if (instring[loc] == self.firstMatchChar and + (self.matchLen==1 or instring.startswith(self.match,loc)) ): + return loc+self.matchLen, self.match + raise ParseException(instring, loc, self.errmsg, self) +_L = Literal +ParserElement._literalStringClass = Literal + +class Keyword(Token): + """ + Token to exactly match a specified string as a keyword, that is, it must be + immediately followed by a non-keyword character. Compare with C{L{Literal}}: + - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}. + - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'} + Accepts two optional constructor arguments in addition to the keyword string: + - C{identChars} is a string of characters that would be valid identifier characters, + defaulting to all alphanumerics + "_" and "$" + - C{caseless} allows case-insensitive matching, default is C{False}. + + Example:: + Keyword("start").parseString("start") # -> ['start'] + Keyword("start").parseString("starting") # -> Exception + + For case-insensitive matching, use L{CaselessKeyword}. + """ + DEFAULT_KEYWORD_CHARS = alphanums+"_$" + + def __init__( self, matchString, identChars=None, caseless=False ): + super(Keyword,self).__init__() + if identChars is None: + identChars = Keyword.DEFAULT_KEYWORD_CHARS + self.match = matchString + self.matchLen = len(matchString) + try: + self.firstMatchChar = matchString[0] + except IndexError: + warnings.warn("null string passed to Keyword; use Empty() instead", + SyntaxWarning, stacklevel=2) + self.name = '"%s"' % self.match + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = False + self.mayIndexError = False + self.caseless = caseless + if caseless: + self.caselessmatch = matchString.upper() + identChars = identChars.upper() + self.identChars = set(identChars) + + def parseImpl( self, instring, loc, doActions=True ): + if self.caseless: + if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and + (loc == 0 or instring[loc-1].upper() not in self.identChars) ): + return loc+self.matchLen, self.match + else: + if (instring[loc] == self.firstMatchChar and + (self.matchLen==1 or instring.startswith(self.match,loc)) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and + (loc == 0 or instring[loc-1] not in self.identChars) ): + return loc+self.matchLen, self.match + raise ParseException(instring, loc, self.errmsg, self) + + def copy(self): + c = super(Keyword,self).copy() + c.identChars = Keyword.DEFAULT_KEYWORD_CHARS + return c + + @staticmethod + def setDefaultKeywordChars( chars ): + """Overrides the default Keyword chars + """ + Keyword.DEFAULT_KEYWORD_CHARS = chars + +class CaselessLiteral(Literal): + """ + Token to match a specified string, ignoring case of letters. + Note: the matched results will always be in the case of the given + match string, NOT the case of the input text. + + Example:: + OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD'] + + (Contrast with example for L{CaselessKeyword}.) + """ + def __init__( self, matchString ): + super(CaselessLiteral,self).__init__( matchString.upper() ) + # Preserve the defining literal. + self.returnString = matchString + self.name = "'%s'" % self.returnString + self.errmsg = "Expected " + self.name + + def parseImpl( self, instring, loc, doActions=True ): + if instring[ loc:loc+self.matchLen ].upper() == self.match: + return loc+self.matchLen, self.returnString + raise ParseException(instring, loc, self.errmsg, self) + +class CaselessKeyword(Keyword): + """ + Caseless version of L{Keyword}. + + Example:: + OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD'] + + (Contrast with example for L{CaselessLiteral}.) + """ + def __init__( self, matchString, identChars=None ): + super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) + + def parseImpl( self, instring, loc, doActions=True ): + if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ): + return loc+self.matchLen, self.match + raise ParseException(instring, loc, self.errmsg, self) + +class CloseMatch(Token): + """ + A variation on L{Literal} which matches "close" matches, that is, + strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters: + - C{match_string} - string to be matched + - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match + + The results from a successful parse will contain the matched text from the input string and the following named results: + - C{mismatches} - a list of the positions within the match_string where mismatches were found + - C{original} - the original match_string used to compare against the input string + + If C{mismatches} is an empty list, then the match was an exact match. + + Example:: + patt = CloseMatch("ATCATCGAATGGA") + patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']}) + patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1) + + # exact match + patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']}) + + # close match allowing up to 2 mismatches + patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2) + patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']}) + """ + def __init__(self, match_string, maxMismatches=1): + super(CloseMatch,self).__init__() + self.name = match_string + self.match_string = match_string + self.maxMismatches = maxMismatches + self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches) + self.mayIndexError = False + self.mayReturnEmpty = False + + def parseImpl( self, instring, loc, doActions=True ): + start = loc + instrlen = len(instring) + maxloc = start + len(self.match_string) + + if maxloc <= instrlen: + match_string = self.match_string + match_stringloc = 0 + mismatches = [] + maxMismatches = self.maxMismatches + + for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)): + src,mat = s_m + if src != mat: + mismatches.append(match_stringloc) + if len(mismatches) > maxMismatches: + break + else: + loc = match_stringloc + 1 + results = ParseResults([instring[start:loc]]) + results['original'] = self.match_string + results['mismatches'] = mismatches + return loc, results + + raise ParseException(instring, loc, self.errmsg, self) + + +class Word(Token): + """ + Token for matching words composed of allowed character sets. + Defined with string containing all allowed initial characters, + an optional string containing allowed body characters (if omitted, + defaults to the initial character set), and an optional minimum, + maximum, and/or exact length. The default value for C{min} is 1 (a + minimum value < 1 is not valid); the default values for C{max} and C{exact} + are 0, meaning no maximum or exact length restriction. An optional + C{excludeChars} parameter can list characters that might be found in + the input C{bodyChars} string; useful to define a word of all printables + except for one or two characters, for instance. + + L{srange} is useful for defining custom character set strings for defining + C{Word} expressions, using range notation from regular expression character sets. + + A common mistake is to use C{Word} to match a specific literal string, as in + C{Word("Address")}. Remember that C{Word} uses the string argument to define + I{sets} of matchable characters. This expression would match "Add", "AAA", + "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'. + To match an exact literal string, use L{Literal} or L{Keyword}. + + pyparsing includes helper strings for building Words: + - L{alphas} + - L{nums} + - L{alphanums} + - L{hexnums} + - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.) + - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.) + - L{printables} (any non-whitespace character) + + Example:: + # a word composed of digits + integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9")) + + # a word with a leading capital, and zero or more lowercase + capital_word = Word(alphas.upper(), alphas.lower()) + + # hostnames are alphanumeric, with leading alpha, and '-' + hostname = Word(alphas, alphanums+'-') + + # roman numeral (not a strict parser, accepts invalid mix of characters) + roman = Word("IVXLCDM") + + # any string of non-whitespace characters, except for ',' + csv_value = Word(printables, excludeChars=",") + """ + def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ): + super(Word,self).__init__() + if excludeChars: + initChars = ''.join(c for c in initChars if c not in excludeChars) + if bodyChars: + bodyChars = ''.join(c for c in bodyChars if c not in excludeChars) + self.initCharsOrig = initChars + self.initChars = set(initChars) + if bodyChars : + self.bodyCharsOrig = bodyChars + self.bodyChars = set(bodyChars) + else: + self.bodyCharsOrig = initChars + self.bodyChars = set(initChars) + + self.maxSpecified = max > 0 + + if min < 1: + raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted") + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayIndexError = False + self.asKeyword = asKeyword + + if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0): + if self.bodyCharsOrig == self.initCharsOrig: + self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig) + elif len(self.initCharsOrig) == 1: + self.reString = "%s[%s]*" % \ + (re.escape(self.initCharsOrig), + _escapeRegexRangeChars(self.bodyCharsOrig),) + else: + self.reString = "[%s][%s]*" % \ + (_escapeRegexRangeChars(self.initCharsOrig), + _escapeRegexRangeChars(self.bodyCharsOrig),) + if self.asKeyword: + self.reString = r"\b"+self.reString+r"\b" + try: + self.re = re.compile( self.reString ) + except Exception: + self.re = None + + def parseImpl( self, instring, loc, doActions=True ): + if self.re: + result = self.re.match(instring,loc) + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + return loc, result.group() + + if not(instring[ loc ] in self.initChars): + raise ParseException(instring, loc, self.errmsg, self) + + start = loc + loc += 1 + instrlen = len(instring) + bodychars = self.bodyChars + maxloc = start + self.maxLen + maxloc = min( maxloc, instrlen ) + while loc < maxloc and instring[loc] in bodychars: + loc += 1 + + throwException = False + if loc - start < self.minLen: + throwException = True + if self.maxSpecified and loc < instrlen and instring[loc] in bodychars: + throwException = True + if self.asKeyword: + if (start>0 and instring[start-1] in bodychars) or (loc<instrlen and instring[loc] in bodychars): + throwException = True + + if throwException: + raise ParseException(instring, loc, self.errmsg, self) + + return loc, instring[start:loc] + + def __str__( self ): + try: + return super(Word,self).__str__() + except Exception: + pass + + + if self.strRepr is None: + + def charsAsStr(s): + if len(s)>4: + return s[:4]+"..." + else: + return s + + if ( self.initCharsOrig != self.bodyCharsOrig ): + self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) ) + else: + self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig) + + return self.strRepr + + +class Regex(Token): + """ + Token for matching strings that match a given regular expression. + Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module. + If the given regex contains named groups (defined using C{(?P<name>...)}), these will be preserved as + named parse results. + + Example:: + realnum = Regex(r"[+-]?\d+\.\d*") + date = Regex(r'(?P<year>\d{4})-(?P<month>\d\d?)-(?P<day>\d\d?)') + # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression + roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})") + """ + compiledREtype = type(re.compile("[A-Z]")) + def __init__( self, pattern, flags=0): + """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags.""" + super(Regex,self).__init__() + + if isinstance(pattern, basestring): + if not pattern: + warnings.warn("null string passed to Regex; use Empty() instead", + SyntaxWarning, stacklevel=2) + + self.pattern = pattern + self.flags = flags + + try: + self.re = re.compile(self.pattern, self.flags) + self.reString = self.pattern + except sre_constants.error: + warnings.warn("invalid pattern (%s) passed to Regex" % pattern, + SyntaxWarning, stacklevel=2) + raise + + elif isinstance(pattern, Regex.compiledREtype): + self.re = pattern + self.pattern = \ + self.reString = str(pattern) + self.flags = flags + + else: + raise ValueError("Regex may only be constructed with a string or a compiled RE object") + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayIndexError = False + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + result = self.re.match(instring,loc) + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + d = result.groupdict() + ret = ParseResults(result.group()) + if d: + for k in d: + ret[k] = d[k] + return loc,ret + + def __str__( self ): + try: + return super(Regex,self).__str__() + except Exception: + pass + + if self.strRepr is None: + self.strRepr = "Re:(%s)" % repr(self.pattern) + + return self.strRepr + + +class QuotedString(Token): + r""" + Token for matching strings that are delimited by quoting characters. + + Defined with the following parameters: + - quoteChar - string of one or more characters defining the quote delimiting string + - escChar - character to escape quotes, typically backslash (default=C{None}) + - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None}) + - multiline - boolean indicating whether quotes can span multiple lines (default=C{False}) + - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True}) + - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar) + - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True}) + + Example:: + qs = QuotedString('"') + print(qs.searchString('lsjdf "This is the quote" sldjf')) + complex_qs = QuotedString('{{', endQuoteChar='}}') + print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf')) + sql_qs = QuotedString('"', escQuote='""') + print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf')) + prints:: + [['This is the quote']] + [['This is the "quote"']] + [['This is the quote with "embedded" quotes']] + """ + def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True): + super(QuotedString,self).__init__() + + # remove white space from quote chars - wont work anyway + quoteChar = quoteChar.strip() + if not quoteChar: + warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + if endQuoteChar is None: + endQuoteChar = quoteChar + else: + endQuoteChar = endQuoteChar.strip() + if not endQuoteChar: + warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + self.quoteChar = quoteChar + self.quoteCharLen = len(quoteChar) + self.firstQuoteChar = quoteChar[0] + self.endQuoteChar = endQuoteChar + self.endQuoteCharLen = len(endQuoteChar) + self.escChar = escChar + self.escQuote = escQuote + self.unquoteResults = unquoteResults + self.convertWhitespaceEscapes = convertWhitespaceEscapes + + if multiline: + self.flags = re.MULTILINE | re.DOTALL + self.pattern = r'%s(?:[^%s%s]' % \ + ( re.escape(self.quoteChar), + _escapeRegexRangeChars(self.endQuoteChar[0]), + (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) + else: + self.flags = 0 + self.pattern = r'%s(?:[^%s\n\r%s]' % \ + ( re.escape(self.quoteChar), + _escapeRegexRangeChars(self.endQuoteChar[0]), + (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) + if len(self.endQuoteChar) > 1: + self.pattern += ( + '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]), + _escapeRegexRangeChars(self.endQuoteChar[i])) + for i in range(len(self.endQuoteChar)-1,0,-1)) + ')' + ) + if escQuote: + self.pattern += (r'|(?:%s)' % re.escape(escQuote)) + if escChar: + self.pattern += (r'|(?:%s.)' % re.escape(escChar)) + self.escCharReplacePattern = re.escape(self.escChar)+"(.)" + self.pattern += (r')*%s' % re.escape(self.endQuoteChar)) + + try: + self.re = re.compile(self.pattern, self.flags) + self.reString = self.pattern + except sre_constants.error: + warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern, + SyntaxWarning, stacklevel=2) + raise + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayIndexError = False + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + ret = result.group() + + if self.unquoteResults: + + # strip off quotes + ret = ret[self.quoteCharLen:-self.endQuoteCharLen] + + if isinstance(ret,basestring): + # replace escaped whitespace + if '\\' in ret and self.convertWhitespaceEscapes: + ws_map = { + r'\t' : '\t', + r'\n' : '\n', + r'\f' : '\f', + r'\r' : '\r', + } + for wslit,wschar in ws_map.items(): + ret = ret.replace(wslit, wschar) + + # replace escaped characters + if self.escChar: + ret = re.sub(self.escCharReplacePattern,"\g<1>",ret) + + # replace escaped quotes + if self.escQuote: + ret = ret.replace(self.escQuote, self.endQuoteChar) + + return loc, ret + + def __str__( self ): + try: + return super(QuotedString,self).__str__() + except Exception: + pass + + if self.strRepr is None: + self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar) + + return self.strRepr + + +class CharsNotIn(Token): + """ + Token for matching words composed of characters I{not} in a given set (will + include whitespace in matched characters if not listed in the provided exclusion set - see example). + Defined with string containing all disallowed characters, and an optional + minimum, maximum, and/or exact length. The default value for C{min} is 1 (a + minimum value < 1 is not valid); the default values for C{max} and C{exact} + are 0, meaning no maximum or exact length restriction. + + Example:: + # define a comma-separated-value as anything that is not a ',' + csv_value = CharsNotIn(',') + print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213")) + prints:: + ['dkls', 'lsdkjf', 's12 34', '@!#', '213'] + """ + def __init__( self, notChars, min=1, max=0, exact=0 ): + super(CharsNotIn,self).__init__() + self.skipWhitespace = False + self.notChars = notChars + + if min < 1: + raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted") + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = ( self.minLen == 0 ) + self.mayIndexError = False + + def parseImpl( self, instring, loc, doActions=True ): + if instring[loc] in self.notChars: + raise ParseException(instring, loc, self.errmsg, self) + + start = loc + loc += 1 + notchars = self.notChars + maxlen = min( start+self.maxLen, len(instring) ) + while loc < maxlen and \ + (instring[loc] not in notchars): + loc += 1 + + if loc - start < self.minLen: + raise ParseException(instring, loc, self.errmsg, self) + + return loc, instring[start:loc] + + def __str__( self ): + try: + return super(CharsNotIn, self).__str__() + except Exception: + pass + + if self.strRepr is None: + if len(self.notChars) > 4: + self.strRepr = "!W:(%s...)" % self.notChars[:4] + else: + self.strRepr = "!W:(%s)" % self.notChars + + return self.strRepr + +class White(Token): + """ + Special matching class for matching whitespace. Normally, whitespace is ignored + by pyparsing grammars. This class is included when some whitespace structures + are significant. Define with a string containing the whitespace characters to be + matched; default is C{" \\t\\r\\n"}. Also takes optional C{min}, C{max}, and C{exact} arguments, + as defined for the C{L{Word}} class. + """ + whiteStrs = { + " " : "<SPC>", + "\t": "<TAB>", + "\n": "<LF>", + "\r": "<CR>", + "\f": "<FF>", + } + def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0): + super(White,self).__init__() + self.matchWhite = ws + self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) ) + #~ self.leaveWhitespace() + self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite)) + self.mayReturnEmpty = True + self.errmsg = "Expected " + self.name + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + def parseImpl( self, instring, loc, doActions=True ): + if not(instring[ loc ] in self.matchWhite): + raise ParseException(instring, loc, self.errmsg, self) + start = loc + loc += 1 + maxloc = start + self.maxLen + maxloc = min( maxloc, len(instring) ) + while loc < maxloc and instring[loc] in self.matchWhite: + loc += 1 + + if loc - start < self.minLen: + raise ParseException(instring, loc, self.errmsg, self) + + return loc, instring[start:loc] + + +class _PositionToken(Token): + def __init__( self ): + super(_PositionToken,self).__init__() + self.name=self.__class__.__name__ + self.mayReturnEmpty = True + self.mayIndexError = False + +class GoToColumn(_PositionToken): + """ + Token to advance to a specific column of input text; useful for tabular report scraping. + """ + def __init__( self, colno ): + super(GoToColumn,self).__init__() + self.col = colno + + def preParse( self, instring, loc ): + if col(loc,instring) != self.col: + instrlen = len(instring) + if self.ignoreExprs: + loc = self._skipIgnorables( instring, loc ) + while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col : + loc += 1 + return loc + + def parseImpl( self, instring, loc, doActions=True ): + thiscol = col( loc, instring ) + if thiscol > self.col: + raise ParseException( instring, loc, "Text not in expected column", self ) + newloc = loc + self.col - thiscol + ret = instring[ loc: newloc ] + return newloc, ret + + +class LineStart(_PositionToken): + """ + Matches if current position is at the beginning of a line within the parse string + + Example:: + + test = '''\ + AAA this line + AAA and this line + AAA but not this one + B AAA and definitely not this one + ''' + + for t in (LineStart() + 'AAA' + restOfLine).searchString(test): + print(t) + + Prints:: + ['AAA', ' this line'] + ['AAA', ' and this line'] + + """ + def __init__( self ): + super(LineStart,self).__init__() + self.errmsg = "Expected start of line" + + def parseImpl( self, instring, loc, doActions=True ): + if col(loc, instring) == 1: + return loc, [] + raise ParseException(instring, loc, self.errmsg, self) + +class LineEnd(_PositionToken): + """ + Matches if current position is at the end of a line within the parse string + """ + def __init__( self ): + super(LineEnd,self).__init__() + self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) + self.errmsg = "Expected end of line" + + def parseImpl( self, instring, loc, doActions=True ): + if loc<len(instring): + if instring[loc] == "\n": + return loc+1, "\n" + else: + raise ParseException(instring, loc, self.errmsg, self) + elif loc == len(instring): + return loc+1, [] + else: + raise ParseException(instring, loc, self.errmsg, self) + +class StringStart(_PositionToken): + """ + Matches if current position is at the beginning of the parse string + """ + def __init__( self ): + super(StringStart,self).__init__() + self.errmsg = "Expected start of text" + + def parseImpl( self, instring, loc, doActions=True ): + if loc != 0: + # see if entire string up to here is just whitespace and ignoreables + if loc != self.preParse( instring, 0 ): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + +class StringEnd(_PositionToken): + """ + Matches if current position is at the end of the parse string + """ + def __init__( self ): + super(StringEnd,self).__init__() + self.errmsg = "Expected end of text" + + def parseImpl( self, instring, loc, doActions=True ): + if loc < len(instring): + raise ParseException(instring, loc, self.errmsg, self) + elif loc == len(instring): + return loc+1, [] + elif loc > len(instring): + return loc, [] + else: + raise ParseException(instring, loc, self.errmsg, self) + +class WordStart(_PositionToken): + """ + Matches if the current position is at the beginning of a Word, and + is not preceded by any character in a given set of C{wordChars} + (default=C{printables}). To emulate the C{\b} behavior of regular expressions, + use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of + the string being parsed, or at the beginning of a line. + """ + def __init__(self, wordChars = printables): + super(WordStart,self).__init__() + self.wordChars = set(wordChars) + self.errmsg = "Not at the start of a word" + + def parseImpl(self, instring, loc, doActions=True ): + if loc != 0: + if (instring[loc-1] in self.wordChars or + instring[loc] not in self.wordChars): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + +class WordEnd(_PositionToken): + """ + Matches if the current position is at the end of a Word, and + is not followed by any character in a given set of C{wordChars} + (default=C{printables}). To emulate the C{\b} behavior of regular expressions, + use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of + the string being parsed, or at the end of a line. + """ + def __init__(self, wordChars = printables): + super(WordEnd,self).__init__() + self.wordChars = set(wordChars) + self.skipWhitespace = False + self.errmsg = "Not at the end of a word" + + def parseImpl(self, instring, loc, doActions=True ): + instrlen = len(instring) + if instrlen>0 and loc<instrlen: + if (instring[loc] in self.wordChars or + instring[loc-1] not in self.wordChars): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + + +class ParseExpression(ParserElement): + """ + Abstract subclass of ParserElement, for combining and post-processing parsed tokens. + """ + def __init__( self, exprs, savelist = False ): + super(ParseExpression,self).__init__(savelist) + if isinstance( exprs, _generatorType ): + exprs = list(exprs) + + if isinstance( exprs, basestring ): + self.exprs = [ ParserElement._literalStringClass( exprs ) ] + elif isinstance( exprs, collections.Iterable ): + exprs = list(exprs) + # if sequence of strings provided, wrap with Literal + if all(isinstance(expr, basestring) for expr in exprs): + exprs = map(ParserElement._literalStringClass, exprs) + self.exprs = list(exprs) + else: + try: + self.exprs = list( exprs ) + except TypeError: + self.exprs = [ exprs ] + self.callPreparse = False + + def __getitem__( self, i ): + return self.exprs[i] + + def append( self, other ): + self.exprs.append( other ) + self.strRepr = None + return self + + def leaveWhitespace( self ): + """Extends C{leaveWhitespace} defined in base class, and also invokes C{leaveWhitespace} on + all contained expressions.""" + self.skipWhitespace = False + self.exprs = [ e.copy() for e in self.exprs ] + for e in self.exprs: + e.leaveWhitespace() + return self + + def ignore( self, other ): + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + super( ParseExpression, self).ignore( other ) + for e in self.exprs: + e.ignore( self.ignoreExprs[-1] ) + else: + super( ParseExpression, self).ignore( other ) + for e in self.exprs: + e.ignore( self.ignoreExprs[-1] ) + return self + + def __str__( self ): + try: + return super(ParseExpression,self).__str__() + except Exception: + pass + + if self.strRepr is None: + self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.exprs) ) + return self.strRepr + + def streamline( self ): + super(ParseExpression,self).streamline() + + for e in self.exprs: + e.streamline() + + # collapse nested And's of the form And( And( And( a,b), c), d) to And( a,b,c,d ) + # but only if there are no parse actions or resultsNames on the nested And's + # (likewise for Or's and MatchFirst's) + if ( len(self.exprs) == 2 ): + other = self.exprs[0] + if ( isinstance( other, self.__class__ ) and + not(other.parseAction) and + other.resultsName is None and + not other.debug ): + self.exprs = other.exprs[:] + [ self.exprs[1] ] + self.strRepr = None + self.mayReturnEmpty |= other.mayReturnEmpty + self.mayIndexError |= other.mayIndexError + + other = self.exprs[-1] + if ( isinstance( other, self.__class__ ) and + not(other.parseAction) and + other.resultsName is None and + not other.debug ): + self.exprs = self.exprs[:-1] + other.exprs[:] + self.strRepr = None + self.mayReturnEmpty |= other.mayReturnEmpty + self.mayIndexError |= other.mayIndexError + + self.errmsg = "Expected " + _ustr(self) + + return self + + def setResultsName( self, name, listAllMatches=False ): + ret = super(ParseExpression,self).setResultsName(name,listAllMatches) + return ret + + def validate( self, validateTrace=[] ): + tmp = validateTrace[:]+[self] + for e in self.exprs: + e.validate(tmp) + self.checkRecursion( [] ) + + def copy(self): + ret = super(ParseExpression,self).copy() + ret.exprs = [e.copy() for e in self.exprs] + return ret + +class And(ParseExpression): + """ + Requires all given C{ParseExpression}s to be found in the given order. + Expressions may be separated by whitespace. + May be constructed using the C{'+'} operator. + May also be constructed using the C{'-'} operator, which will suppress backtracking. + + Example:: + integer = Word(nums) + name_expr = OneOrMore(Word(alphas)) + + expr = And([integer("id"),name_expr("name"),integer("age")]) + # more easily written as: + expr = integer("id") + name_expr("name") + integer("age") + """ + + class _ErrorStop(Empty): + def __init__(self, *args, **kwargs): + super(And._ErrorStop,self).__init__(*args, **kwargs) + self.name = '-' + self.leaveWhitespace() + + def __init__( self, exprs, savelist = True ): + super(And,self).__init__(exprs, savelist) + self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) + self.setWhitespaceChars( self.exprs[0].whiteChars ) + self.skipWhitespace = self.exprs[0].skipWhitespace + self.callPreparse = True + + def parseImpl( self, instring, loc, doActions=True ): + # pass False as last arg to _parse for first element, since we already + # pre-parsed the string as part of our And pre-parsing + loc, resultlist = self.exprs[0]._parse( instring, loc, doActions, callPreParse=False ) + errorStop = False + for e in self.exprs[1:]: + if isinstance(e, And._ErrorStop): + errorStop = True + continue + if errorStop: + try: + loc, exprtokens = e._parse( instring, loc, doActions ) + except ParseSyntaxException: + raise + except ParseBaseException as pe: + pe.__traceback__ = None + raise ParseSyntaxException._from_exception(pe) + except IndexError: + raise ParseSyntaxException(instring, len(instring), self.errmsg, self) + else: + loc, exprtokens = e._parse( instring, loc, doActions ) + if exprtokens or exprtokens.haskeys(): + resultlist += exprtokens + return loc, resultlist + + def __iadd__(self, other ): + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + return self.append( other ) #And( [ self, other ] ) + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + if not e.mayReturnEmpty: + break + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + +class Or(ParseExpression): + """ + Requires that at least one C{ParseExpression} is found. + If two expressions match, the expression that matches the longest string will be used. + May be constructed using the C{'^'} operator. + + Example:: + # construct Or using '^' operator + + number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) + prints:: + [['123'], ['3.1416'], ['789']] + """ + def __init__( self, exprs, savelist = False ): + super(Or,self).__init__(exprs, savelist) + if self.exprs: + self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) + else: + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + maxExcLoc = -1 + maxException = None + matches = [] + for e in self.exprs: + try: + loc2 = e.tryParse( instring, loc ) + except ParseException as err: + err.__traceback__ = None + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + except IndexError: + if len(instring) > maxExcLoc: + maxException = ParseException(instring,len(instring),e.errmsg,self) + maxExcLoc = len(instring) + else: + # save match among all matches, to retry longest to shortest + matches.append((loc2, e)) + + if matches: + matches.sort(key=lambda x: -x[0]) + for _,e in matches: + try: + return e._parse( instring, loc, doActions ) + except ParseException as err: + err.__traceback__ = None + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + + if maxException is not None: + maxException.msg = self.errmsg + raise maxException + else: + raise ParseException(instring, loc, "no defined alternatives to match", self) + + + def __ixor__(self, other ): + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + return self.append( other ) #Or( [ self, other ] ) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class MatchFirst(ParseExpression): + """ + Requires that at least one C{ParseExpression} is found. + If two expressions match, the first one listed is the one that will match. + May be constructed using the C{'|'} operator. + + Example:: + # construct MatchFirst using '|' operator + + # watch the order of expressions to match + number = Word(nums) | Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']] + + # put more selective expression first + number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums) + print(number.searchString("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']] + """ + def __init__( self, exprs, savelist = False ): + super(MatchFirst,self).__init__(exprs, savelist) + if self.exprs: + self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) + else: + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + maxExcLoc = -1 + maxException = None + for e in self.exprs: + try: + ret = e._parse( instring, loc, doActions ) + return ret + except ParseException as err: + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + except IndexError: + if len(instring) > maxExcLoc: + maxException = ParseException(instring,len(instring),e.errmsg,self) + maxExcLoc = len(instring) + + # only got here if no expression matched, raise exception for match that made it the furthest + else: + if maxException is not None: + maxException.msg = self.errmsg + raise maxException + else: + raise ParseException(instring, loc, "no defined alternatives to match", self) + + def __ior__(self, other ): + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + return self.append( other ) #MatchFirst( [ self, other ] ) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class Each(ParseExpression): + """ + Requires all given C{ParseExpression}s to be found, but in any order. + Expressions may be separated by whitespace. + May be constructed using the C{'&'} operator. + + Example:: + color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN") + shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON") + integer = Word(nums) + shape_attr = "shape:" + shape_type("shape") + posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn") + color_attr = "color:" + color("color") + size_attr = "size:" + integer("size") + + # use Each (using operator '&') to accept attributes in any order + # (shape and posn are required, color and size are optional) + shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr) + + shape_spec.runTests(''' + shape: SQUARE color: BLACK posn: 100, 120 + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + color:GREEN size:20 shape:TRIANGLE posn:20,40 + ''' + ) + prints:: + shape: SQUARE color: BLACK posn: 100, 120 + ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']] + - color: BLACK + - posn: ['100', ',', '120'] + - x: 100 + - y: 120 + - shape: SQUARE + + + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']] + - color: BLUE + - posn: ['50', ',', '80'] + - x: 50 + - y: 80 + - shape: CIRCLE + - size: 50 + + + color: GREEN size: 20 shape: TRIANGLE posn: 20,40 + ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']] + - color: GREEN + - posn: ['20', ',', '40'] + - x: 20 + - y: 40 + - shape: TRIANGLE + - size: 20 + """ + def __init__( self, exprs, savelist = True ): + super(Each,self).__init__(exprs, savelist) + self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) + self.skipWhitespace = True + self.initExprGroups = True + + def parseImpl( self, instring, loc, doActions=True ): + if self.initExprGroups: + self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional)) + opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ] + opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)] + self.optionals = opt1 + opt2 + self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ] + self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ] + self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ] + self.required += self.multirequired + self.initExprGroups = False + tmpLoc = loc + tmpReqd = self.required[:] + tmpOpt = self.optionals[:] + matchOrder = [] + + keepMatching = True + while keepMatching: + tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired + failed = [] + for e in tmpExprs: + try: + tmpLoc = e.tryParse( instring, tmpLoc ) + except ParseException: + failed.append(e) + else: + matchOrder.append(self.opt1map.get(id(e),e)) + if e in tmpReqd: + tmpReqd.remove(e) + elif e in tmpOpt: + tmpOpt.remove(e) + if len(failed) == len(tmpExprs): + keepMatching = False + + if tmpReqd: + missing = ", ".join(_ustr(e) for e in tmpReqd) + raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing ) + + # add any unmatched Optionals, in case they have default values defined + matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt] + + resultlist = [] + for e in matchOrder: + loc,results = e._parse(instring,loc,doActions) + resultlist.append(results) + + finalResults = sum(resultlist, ParseResults([])) + return loc, finalResults + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class ParseElementEnhance(ParserElement): + """ + Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens. + """ + def __init__( self, expr, savelist=False ): + super(ParseElementEnhance,self).__init__(savelist) + if isinstance( expr, basestring ): + if issubclass(ParserElement._literalStringClass, Token): + expr = ParserElement._literalStringClass(expr) + else: + expr = ParserElement._literalStringClass(Literal(expr)) + self.expr = expr + self.strRepr = None + if expr is not None: + self.mayIndexError = expr.mayIndexError + self.mayReturnEmpty = expr.mayReturnEmpty + self.setWhitespaceChars( expr.whiteChars ) + self.skipWhitespace = expr.skipWhitespace + self.saveAsList = expr.saveAsList + self.callPreparse = expr.callPreparse + self.ignoreExprs.extend(expr.ignoreExprs) + + def parseImpl( self, instring, loc, doActions=True ): + if self.expr is not None: + return self.expr._parse( instring, loc, doActions, callPreParse=False ) + else: + raise ParseException("",loc,self.errmsg,self) + + def leaveWhitespace( self ): + self.skipWhitespace = False + self.expr = self.expr.copy() + if self.expr is not None: + self.expr.leaveWhitespace() + return self + + def ignore( self, other ): + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + super( ParseElementEnhance, self).ignore( other ) + if self.expr is not None: + self.expr.ignore( self.ignoreExprs[-1] ) + else: + super( ParseElementEnhance, self).ignore( other ) + if self.expr is not None: + self.expr.ignore( self.ignoreExprs[-1] ) + return self + + def streamline( self ): + super(ParseElementEnhance,self).streamline() + if self.expr is not None: + self.expr.streamline() + return self + + def checkRecursion( self, parseElementList ): + if self in parseElementList: + raise RecursiveGrammarException( parseElementList+[self] ) + subRecCheckList = parseElementList[:] + [ self ] + if self.expr is not None: + self.expr.checkRecursion( subRecCheckList ) + + def validate( self, validateTrace=[] ): + tmp = validateTrace[:]+[self] + if self.expr is not None: + self.expr.validate(tmp) + self.checkRecursion( [] ) + + def __str__( self ): + try: + return super(ParseElementEnhance,self).__str__() + except Exception: + pass + + if self.strRepr is None and self.expr is not None: + self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) ) + return self.strRepr + + +class FollowedBy(ParseElementEnhance): + """ + Lookahead matching of the given parse expression. C{FollowedBy} + does I{not} advance the parsing position within the input string, it only + verifies that the specified parse expression matches at the current + position. C{FollowedBy} always returns a null token list. + + Example:: + # use FollowedBy to match a label only if it is followed by a ':' + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint() + prints:: + [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']] + """ + def __init__( self, expr ): + super(FollowedBy,self).__init__(expr) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + self.expr.tryParse( instring, loc ) + return loc, [] + + +class NotAny(ParseElementEnhance): + """ + Lookahead to disallow matching with the given parse expression. C{NotAny} + does I{not} advance the parsing position within the input string, it only + verifies that the specified parse expression does I{not} match at the current + position. Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny} + always returns a null token list. May be constructed using the '~' operator. + + Example:: + + """ + def __init__( self, expr ): + super(NotAny,self).__init__(expr) + #~ self.leaveWhitespace() + self.skipWhitespace = False # do NOT use self.leaveWhitespace(), don't want to propagate to exprs + self.mayReturnEmpty = True + self.errmsg = "Found unwanted token, "+_ustr(self.expr) + + def parseImpl( self, instring, loc, doActions=True ): + if self.expr.canParseNext(instring, loc): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "~{" + _ustr(self.expr) + "}" + + return self.strRepr + +class _MultipleMatch(ParseElementEnhance): + def __init__( self, expr, stopOn=None): + super(_MultipleMatch, self).__init__(expr) + self.saveAsList = True + ender = stopOn + if isinstance(ender, basestring): + ender = ParserElement._literalStringClass(ender) + self.not_ender = ~ender if ender is not None else None + + def parseImpl( self, instring, loc, doActions=True ): + self_expr_parse = self.expr._parse + self_skip_ignorables = self._skipIgnorables + check_ender = self.not_ender is not None + if check_ender: + try_not_ender = self.not_ender.tryParse + + # must be at least one (but first see if we are the stopOn sentinel; + # if so, fail) + if check_ender: + try_not_ender(instring, loc) + loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False ) + try: + hasIgnoreExprs = (not not self.ignoreExprs) + while 1: + if check_ender: + try_not_ender(instring, loc) + if hasIgnoreExprs: + preloc = self_skip_ignorables( instring, loc ) + else: + preloc = loc + loc, tmptokens = self_expr_parse( instring, preloc, doActions ) + if tmptokens or tmptokens.haskeys(): + tokens += tmptokens + except (ParseException,IndexError): + pass + + return loc, tokens + +class OneOrMore(_MultipleMatch): + """ + Repetition of one or more of the given expression. + + Parameters: + - expr - expression that must match one or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: BLACK" + OneOrMore(attr_expr).parseString(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] + + # use stopOn attribute for OneOrMore to avoid reading label string as part of the data + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] + + # could also be written as + (attr_expr * (1,)).parseString(text).pprint() + """ + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + _ustr(self.expr) + "}..." + + return self.strRepr + +class ZeroOrMore(_MultipleMatch): + """ + Optional repetition of zero or more of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example: similar to L{OneOrMore} + """ + def __init__( self, expr, stopOn=None): + super(ZeroOrMore,self).__init__(expr, stopOn=stopOn) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + try: + return super(ZeroOrMore, self).parseImpl(instring, loc, doActions) + except (ParseException,IndexError): + return loc, [] + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "[" + _ustr(self.expr) + "]..." + + return self.strRepr + +class _NullToken(object): + def __bool__(self): + return False + __nonzero__ = __bool__ + def __str__(self): + return "" + +_optionalNotMatched = _NullToken() +class Optional(ParseElementEnhance): + """ + Optional matching of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - default (optional) - value to be returned if the optional expression is not found. + + Example:: + # US postal code can be a 5-digit zip, plus optional 4-digit qualifier + zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4))) + zip.runTests(''' + # traditional ZIP code + 12345 + + # ZIP+4 form + 12101-0001 + + # invalid ZIP + 98765- + ''') + prints:: + # traditional ZIP code + 12345 + ['12345'] + + # ZIP+4 form + 12101-0001 + ['12101-0001'] + + # invalid ZIP + 98765- + ^ + FAIL: Expected end of text (at char 5), (line:1, col:6) + """ + def __init__( self, expr, default=_optionalNotMatched ): + super(Optional,self).__init__( expr, savelist=False ) + self.saveAsList = self.expr.saveAsList + self.defaultValue = default + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + try: + loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) + except (ParseException,IndexError): + if self.defaultValue is not _optionalNotMatched: + if self.expr.resultsName: + tokens = ParseResults([ self.defaultValue ]) + tokens[self.expr.resultsName] = self.defaultValue + else: + tokens = [ self.defaultValue ] + else: + tokens = [] + return loc, tokens + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "[" + _ustr(self.expr) + "]" + + return self.strRepr + +class SkipTo(ParseElementEnhance): + """ + Token for skipping over all undefined text until the matched expression is found. + + Parameters: + - expr - target expression marking the end of the data to be skipped + - include - (default=C{False}) if True, the target expression is also parsed + (the skipped text and target expression are returned as a 2-element list). + - ignore - (default=C{None}) used to define grammars (typically quoted strings and + comments) that might contain false matches to the target expression + - failOn - (default=C{None}) define expressions that are not allowed to be + included in the skipped test; if found before the target expression is found, + the SkipTo is not a match + + Example:: + report = ''' + Outstanding Issues Report - 1 Jan 2000 + + # | Severity | Description | Days Open + -----+----------+-------------------------------------------+----------- + 101 | Critical | Intermittent system crash | 6 + 94 | Cosmetic | Spelling error on Login ('log|n') | 14 + 79 | Minor | System slow when running too many reports | 47 + ''' + integer = Word(nums) + SEP = Suppress('|') + # use SkipTo to simply match everything up until the next SEP + # - ignore quoted strings, so that a '|' character inside a quoted string does not match + # - parse action will call token.strip() for each matched token, i.e., the description body + string_data = SkipTo(SEP, ignore=quotedString) + string_data.setParseAction(tokenMap(str.strip)) + ticket_expr = (integer("issue_num") + SEP + + string_data("sev") + SEP + + string_data("desc") + SEP + + integer("days_open")) + + for tkt in ticket_expr.searchString(report): + print tkt.dump() + prints:: + ['101', 'Critical', 'Intermittent system crash', '6'] + - days_open: 6 + - desc: Intermittent system crash + - issue_num: 101 + - sev: Critical + ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14'] + - days_open: 14 + - desc: Spelling error on Login ('log|n') + - issue_num: 94 + - sev: Cosmetic + ['79', 'Minor', 'System slow when running too many reports', '47'] + - days_open: 47 + - desc: System slow when running too many reports + - issue_num: 79 + - sev: Minor + """ + def __init__( self, other, include=False, ignore=None, failOn=None ): + super( SkipTo, self ).__init__( other ) + self.ignoreExpr = ignore + self.mayReturnEmpty = True + self.mayIndexError = False + self.includeMatch = include + self.asList = False + if isinstance(failOn, basestring): + self.failOn = ParserElement._literalStringClass(failOn) + else: + self.failOn = failOn + self.errmsg = "No match found for "+_ustr(self.expr) + + def parseImpl( self, instring, loc, doActions=True ): + startloc = loc + instrlen = len(instring) + expr = self.expr + expr_parse = self.expr._parse + self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None + self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None + + tmploc = loc + while tmploc <= instrlen: + if self_failOn_canParseNext is not None: + # break if failOn expression matches + if self_failOn_canParseNext(instring, tmploc): + break + + if self_ignoreExpr_tryParse is not None: + # advance past ignore expressions + while 1: + try: + tmploc = self_ignoreExpr_tryParse(instring, tmploc) + except ParseBaseException: + break + + try: + expr_parse(instring, tmploc, doActions=False, callPreParse=False) + except (ParseException, IndexError): + # no match, advance loc in string + tmploc += 1 + else: + # matched skipto expr, done + break + + else: + # ran off the end of the input string without matching skipto expr, fail + raise ParseException(instring, loc, self.errmsg, self) + + # build up return values + loc = tmploc + skiptext = instring[startloc:loc] + skipresult = ParseResults(skiptext) + + if self.includeMatch: + loc, mat = expr_parse(instring,loc,doActions,callPreParse=False) + skipresult += mat + + return loc, skipresult + +class Forward(ParseElementEnhance): + """ + Forward declaration of an expression to be defined later - + used for recursive grammars, such as algebraic infix notation. + When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator. + + Note: take care when assigning to C{Forward} not to overlook precedence of operators. + Specifically, '|' has a lower precedence than '<<', so that:: + fwdExpr << a | b | c + will actually be evaluated as:: + (fwdExpr << a) | b | c + thereby leaving b and c out as parseable alternatives. It is recommended that you + explicitly group the values inserted into the C{Forward}:: + fwdExpr << (a | b | c) + Converting to use the '<<=' operator instead will avoid this problem. + + See L{ParseResults.pprint} for an example of a recursive parser created using + C{Forward}. + """ + def __init__( self, other=None ): + super(Forward,self).__init__( other, savelist=False ) + + def __lshift__( self, other ): + if isinstance( other, basestring ): + other = ParserElement._literalStringClass(other) + self.expr = other + self.strRepr = None + self.mayIndexError = self.expr.mayIndexError + self.mayReturnEmpty = self.expr.mayReturnEmpty + self.setWhitespaceChars( self.expr.whiteChars ) + self.skipWhitespace = self.expr.skipWhitespace + self.saveAsList = self.expr.saveAsList + self.ignoreExprs.extend(self.expr.ignoreExprs) + return self + + def __ilshift__(self, other): + return self << other + + def leaveWhitespace( self ): + self.skipWhitespace = False + return self + + def streamline( self ): + if not self.streamlined: + self.streamlined = True + if self.expr is not None: + self.expr.streamline() + return self + + def validate( self, validateTrace=[] ): + if self not in validateTrace: + tmp = validateTrace[:]+[self] + if self.expr is not None: + self.expr.validate(tmp) + self.checkRecursion([]) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + return self.__class__.__name__ + ": ..." + + # stubbed out for now - creates awful memory and perf issues + self._revertClass = self.__class__ + self.__class__ = _ForwardNoRecurse + try: + if self.expr is not None: + retString = _ustr(self.expr) + else: + retString = "None" + finally: + self.__class__ = self._revertClass + return self.__class__.__name__ + ": " + retString + + def copy(self): + if self.expr is not None: + return super(Forward,self).copy() + else: + ret = Forward() + ret <<= self + return ret + +class _ForwardNoRecurse(Forward): + def __str__( self ): + return "..." + +class TokenConverter(ParseElementEnhance): + """ + Abstract subclass of C{ParseExpression}, for converting parsed results. + """ + def __init__( self, expr, savelist=False ): + super(TokenConverter,self).__init__( expr )#, savelist ) + self.saveAsList = False + +class Combine(TokenConverter): + """ + Converter to concatenate all matching tokens to a single string. + By default, the matching patterns must also be contiguous in the input string; + this can be disabled by specifying C{'adjacent=False'} in the constructor. + + Example:: + real = Word(nums) + '.' + Word(nums) + print(real.parseString('3.1416')) # -> ['3', '.', '1416'] + # will also erroneously match the following + print(real.parseString('3. 1416')) # -> ['3', '.', '1416'] + + real = Combine(Word(nums) + '.' + Word(nums)) + print(real.parseString('3.1416')) # -> ['3.1416'] + # no match when there are internal spaces + print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...) + """ + def __init__( self, expr, joinString="", adjacent=True ): + super(Combine,self).__init__( expr ) + # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself + if adjacent: + self.leaveWhitespace() + self.adjacent = adjacent + self.skipWhitespace = True + self.joinString = joinString + self.callPreparse = True + + def ignore( self, other ): + if self.adjacent: + ParserElement.ignore(self, other) + else: + super( Combine, self).ignore( other ) + return self + + def postParse( self, instring, loc, tokenlist ): + retToks = tokenlist.copy() + del retToks[:] + retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults) + + if self.resultsName and retToks.haskeys(): + return [ retToks ] + else: + return retToks + +class Group(TokenConverter): + """ + Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions. + + Example:: + ident = Word(alphas) + num = Word(nums) + term = ident | num + func = ident + Optional(delimitedList(term)) + print(func.parseString("fn a,b,100")) # -> ['fn', 'a', 'b', '100'] + + func = ident + Group(Optional(delimitedList(term))) + print(func.parseString("fn a,b,100")) # -> ['fn', ['a', 'b', '100']] + """ + def __init__( self, expr ): + super(Group,self).__init__( expr ) + self.saveAsList = True + + def postParse( self, instring, loc, tokenlist ): + return [ tokenlist ] + +class Dict(TokenConverter): + """ + Converter to return a repetitive expression as a list, but also as a dictionary. + Each element can also be referenced using the first token in the expression as its key. + Useful for tabular report scraping when the first column can be used as a item key. + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + # print attributes as plain groups + print(OneOrMore(attr_expr).parseString(text).dump()) + + # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names + result = Dict(OneOrMore(Group(attr_expr))).parseString(text) + print(result.dump()) + + # access named fields as dict entries, or output as dict + print(result['shape']) + print(result.asDict()) + prints:: + ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap'] + + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'} + See more examples at L{ParseResults} of accessing fields by results name. + """ + def __init__( self, expr ): + super(Dict,self).__init__( expr ) + self.saveAsList = True + + def postParse( self, instring, loc, tokenlist ): + for i,tok in enumerate(tokenlist): + if len(tok) == 0: + continue + ikey = tok[0] + if isinstance(ikey,int): + ikey = _ustr(tok[0]).strip() + if len(tok)==1: + tokenlist[ikey] = _ParseResultsWithOffset("",i) + elif len(tok)==2 and not isinstance(tok[1],ParseResults): + tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i) + else: + dictvalue = tok.copy() #ParseResults(i) + del dictvalue[0] + if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()): + tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i) + else: + tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i) + + if self.resultsName: + return [ tokenlist ] + else: + return tokenlist + + +class Suppress(TokenConverter): + """ + Converter for ignoring the results of a parsed expression. + + Example:: + source = "a, b, c,d" + wd = Word(alphas) + wd_list1 = wd + ZeroOrMore(',' + wd) + print(wd_list1.parseString(source)) + + # often, delimiters that are useful during parsing are just in the + # way afterward - use Suppress to keep them out of the parsed output + wd_list2 = wd + ZeroOrMore(Suppress(',') + wd) + print(wd_list2.parseString(source)) + prints:: + ['a', ',', 'b', ',', 'c', ',', 'd'] + ['a', 'b', 'c', 'd'] + (See also L{delimitedList}.) + """ + def postParse( self, instring, loc, tokenlist ): + return [] + + def suppress( self ): + return self + + +class OnlyOnce(object): + """ + Wrapper for parse actions, to ensure they are only called once. + """ + def __init__(self, methodCall): + self.callable = _trim_arity(methodCall) + self.called = False + def __call__(self,s,l,t): + if not self.called: + results = self.callable(s,l,t) + self.called = True + return results + raise ParseException(s,l,"") + def reset(self): + self.called = False + +def traceParseAction(f): + """ + Decorator for debugging parse actions. + + When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".} + When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised. + + Example:: + wd = Word(alphas) + + @traceParseAction + def remove_duplicate_chars(tokens): + return ''.join(sorted(set(''.join(tokens))) + + wds = OneOrMore(wd).setParseAction(remove_duplicate_chars) + print(wds.parseString("slkdjs sld sldd sdlf sdljf")) + prints:: + >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {})) + <<leaving remove_duplicate_chars (ret: 'dfjkls') + ['dfjkls'] + """ + f = _trim_arity(f) + def z(*paArgs): + thisFunc = f.__name__ + s,l,t = paArgs[-3:] + if len(paArgs)>3: + thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc + sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) ) + try: + ret = f(*paArgs) + except Exception as exc: + sys.stderr.write( "<<leaving %s (exception: %s)\n" % (thisFunc,exc) ) + raise + sys.stderr.write( "<<leaving %s (ret: %r)\n" % (thisFunc,ret) ) + return ret + try: + z.__name__ = f.__name__ + except AttributeError: + pass + return z + +# +# global helpers +# +def delimitedList( expr, delim=",", combine=False ): + """ + Helper to define a delimited list of expressions - the delimiter defaults to ','. + By default, the list elements and delimiters can have intervening whitespace, and + comments, but this can be overridden by passing C{combine=True} in the constructor. + If C{combine} is set to C{True}, the matching tokens are returned as a single token + string, with the delimiters included; otherwise, the matching tokens are returned + as a list of tokens, with the delimiters suppressed. + + Example:: + delimitedList(Word(alphas)).parseString("aa,bb,cc") # -> ['aa', 'bb', 'cc'] + delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] + """ + dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..." + if combine: + return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName) + else: + return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName) + +def countedArray( expr, intExpr=None ): + """ + Helper to define a counted list of expressions. + This helper defines a pattern of the form:: + integer expr expr expr... + where the leading integer tells how many expr expressions follow. + The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed. + + If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value. + + Example:: + countedArray(Word(alphas)).parseString('2 ab cd ef') # -> ['ab', 'cd'] + + # in this parser, the leading integer value is given in binary, + # '10' indicating that 2 values are in the array + binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2)) + countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef') # -> ['ab', 'cd'] + """ + arrayExpr = Forward() + def countFieldParseAction(s,l,t): + n = t[0] + arrayExpr << (n and Group(And([expr]*n)) or Group(empty)) + return [] + if intExpr is None: + intExpr = Word(nums).setParseAction(lambda t:int(t[0])) + else: + intExpr = intExpr.copy() + intExpr.setName("arrayLen") + intExpr.addParseAction(countFieldParseAction, callDuringTry=True) + return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...') + +def _flatten(L): + ret = [] + for i in L: + if isinstance(i,list): + ret.extend(_flatten(i)) + else: + ret.append(i) + return ret + +def matchPreviousLiteral(expr): + """ + Helper to define an expression that is indirectly defined from + the tokens matched in a previous expression, that is, it looks + for a 'repeat' of a previous expression. For example:: + first = Word(nums) + second = matchPreviousLiteral(first) + matchExpr = first + ":" + second + will match C{"1:1"}, but not C{"1:2"}. Because this matches a + previous literal, will also match the leading C{"1:1"} in C{"1:10"}. + If this is not desired, use C{matchPreviousExpr}. + Do I{not} use with packrat parsing enabled. + """ + rep = Forward() + def copyTokenToRepeater(s,l,t): + if t: + if len(t) == 1: + rep << t[0] + else: + # flatten t tokens + tflat = _flatten(t.asList()) + rep << And(Literal(tt) for tt in tflat) + else: + rep << Empty() + expr.addParseAction(copyTokenToRepeater, callDuringTry=True) + rep.setName('(prev) ' + _ustr(expr)) + return rep + +def matchPreviousExpr(expr): + """ + Helper to define an expression that is indirectly defined from + the tokens matched in a previous expression, that is, it looks + for a 'repeat' of a previous expression. For example:: + first = Word(nums) + second = matchPreviousExpr(first) + matchExpr = first + ":" + second + will match C{"1:1"}, but not C{"1:2"}. Because this matches by + expressions, will I{not} match the leading C{"1:1"} in C{"1:10"}; + the expressions are evaluated first, and then compared, so + C{"1"} is compared with C{"10"}. + Do I{not} use with packrat parsing enabled. + """ + rep = Forward() + e2 = expr.copy() + rep <<= e2 + def copyTokenToRepeater(s,l,t): + matchTokens = _flatten(t.asList()) + def mustMatchTheseTokens(s,l,t): + theseTokens = _flatten(t.asList()) + if theseTokens != matchTokens: + raise ParseException("",0,"") + rep.setParseAction( mustMatchTheseTokens, callDuringTry=True ) + expr.addParseAction(copyTokenToRepeater, callDuringTry=True) + rep.setName('(prev) ' + _ustr(expr)) + return rep + +def _escapeRegexRangeChars(s): + #~ escape these chars: ^-] + for c in r"\^-]": + s = s.replace(c,_bslash+c) + s = s.replace("\n",r"\n") + s = s.replace("\t",r"\t") + return _ustr(s) + +def oneOf( strs, caseless=False, useRegex=True ): + """ + Helper to quickly define a set of alternative Literals, and makes sure to do + longest-first testing when there is a conflict, regardless of the input order, + but returns a C{L{MatchFirst}} for best performance. + + Parameters: + - strs - a string of space-delimited literals, or a collection of string literals + - caseless - (default=C{False}) - treat all literals as caseless + - useRegex - (default=C{True}) - as an optimization, will generate a Regex + object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or + if creating a C{Regex} raises an exception) + + Example:: + comp_oper = oneOf("< = > <= >= !=") + var = Word(alphas) + number = Word(nums) + term = var | number + comparison_expr = term + comp_oper + term + print(comparison_expr.searchString("B = 12 AA=23 B<=AA AA>12")) + prints:: + [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] + """ + if caseless: + isequal = ( lambda a,b: a.upper() == b.upper() ) + masks = ( lambda a,b: b.upper().startswith(a.upper()) ) + parseElementClass = CaselessLiteral + else: + isequal = ( lambda a,b: a == b ) + masks = ( lambda a,b: b.startswith(a) ) + parseElementClass = Literal + + symbols = [] + if isinstance(strs,basestring): + symbols = strs.split() + elif isinstance(strs, collections.Iterable): + symbols = list(strs) + else: + warnings.warn("Invalid argument to oneOf, expected string or iterable", + SyntaxWarning, stacklevel=2) + if not symbols: + return NoMatch() + + i = 0 + while i < len(symbols)-1: + cur = symbols[i] + for j,other in enumerate(symbols[i+1:]): + if ( isequal(other, cur) ): + del symbols[i+j+1] + break + elif ( masks(cur, other) ): + del symbols[i+j+1] + symbols.insert(i,other) + cur = other + break + else: + i += 1 + + if not caseless and useRegex: + #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] )) + try: + if len(symbols)==len("".join(symbols)): + return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols)) + else: + return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols)) + except Exception: + warnings.warn("Exception creating Regex for oneOf, building MatchFirst", + SyntaxWarning, stacklevel=2) + + + # last resort, just use MatchFirst + return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols)) + +def dictOf( key, value ): + """ + Helper to easily and clearly define a dictionary by specifying the respective patterns + for the key and value. Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens + in the proper order. The key pattern can include delimiting markers or punctuation, + as long as they are suppressed, thereby leaving the significant key text. The value + pattern can include named results, so that the C{Dict} results can include named token + fields. + + Example:: + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + print(OneOrMore(attr_expr).parseString(text).dump()) + + attr_label = label + attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join) + + # similar to Dict, but simpler call format + result = dictOf(attr_label, attr_value).parseString(text) + print(result.dump()) + print(result['shape']) + print(result.shape) # object attribute access works too + print(result.asDict()) + prints:: + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + SQUARE + {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} + """ + return Dict( ZeroOrMore( Group ( key + value ) ) ) + +def originalTextFor(expr, asString=True): + """ + Helper to return the original, untokenized text for a given expression. Useful to + restore the parsed fields of an HTML start tag into the raw tag text itself, or to + revert separate tokens with intervening whitespace back to the original matching + input text. By default, returns astring containing the original parsed text. + + If the optional C{asString} argument is passed as C{False}, then the return value is a + C{L{ParseResults}} containing any results names that were originally matched, and a + single token containing the original matched text from the input string. So if + the expression passed to C{L{originalTextFor}} contains expressions with defined + results names, you must set C{asString} to C{False} if you want to preserve those + results name values. + + Example:: + src = "this is test <b> bold <i>text</i> </b> normal text " + for tag in ("b","i"): + opener,closer = makeHTMLTags(tag) + patt = originalTextFor(opener + SkipTo(closer) + closer) + print(patt.searchString(src)[0]) + prints:: + ['<b> bold <i>text</i> </b>'] + ['<i>text</i>'] + """ + locMarker = Empty().setParseAction(lambda s,loc,t: loc) + endlocMarker = locMarker.copy() + endlocMarker.callPreparse = False + matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") + if asString: + extractText = lambda s,l,t: s[t._original_start:t._original_end] + else: + def extractText(s,l,t): + t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]] + matchExpr.setParseAction(extractText) + matchExpr.ignoreExprs = expr.ignoreExprs + return matchExpr + +def ungroup(expr): + """ + Helper to undo pyparsing's default grouping of And expressions, even + if all but one are non-empty. + """ + return TokenConverter(expr).setParseAction(lambda t:t[0]) + +def locatedExpr(expr): + """ + Helper to decorate a returned token with its starting and ending locations in the input string. + This helper adds the following results names: + - locn_start = location where matched expression begins + - locn_end = location where matched expression ends + - value = the actual parsed results + + Be careful if the input text contains C{<TAB>} characters, you may want to call + C{L{ParserElement.parseWithTabs}} + + Example:: + wd = Word(alphas) + for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): + print(match) + prints:: + [[0, 'ljsdf', 5]] + [[8, 'lksdjjf', 15]] + [[18, 'lkkjj', 23]] + """ + locator = Empty().setParseAction(lambda s,l,t: l) + return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end")) + + +# convenience constants for positional expressions +empty = Empty().setName("empty") +lineStart = LineStart().setName("lineStart") +lineEnd = LineEnd().setName("lineEnd") +stringStart = StringStart().setName("stringStart") +stringEnd = StringEnd().setName("stringEnd") + +_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1]) +_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16))) +_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8))) +_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | Word(printables, excludeChars=r'\]', exact=1) | Regex(r"\w", re.UNICODE) +_charRange = Group(_singleChar + Suppress("-") + _singleChar) +_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]" + +def srange(s): + r""" + Helper to easily define string ranges for use in Word construction. Borrows + syntax from regexp '[]' string range definitions:: + srange("[0-9]") -> "0123456789" + srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" + srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" + The input string must be enclosed in []'s, and the returned string is the expanded + character set joined into a single string. + The values enclosed in the []'s may be: + - a single character + - an escaped character with a leading backslash (such as C{\-} or C{\]}) + - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) + (C{\0x##} is also supported for backwards compatibility) + - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character) + - a range of any of the above, separated by a dash (C{'a-z'}, etc.) + - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.) + """ + _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1)) + try: + return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body) + except Exception: + return "" + +def matchOnlyAtCol(n): + """ + Helper method for defining parse actions that require matching at a specific + column in the input text. + """ + def verifyCol(strg,locn,toks): + if col(locn,strg) != n: + raise ParseException(strg,locn,"matched token not at column %d" % n) + return verifyCol + +def replaceWith(replStr): + """ + Helper method for common parse actions that simply return a literal value. Especially + useful when used with C{L{transformString<ParserElement.transformString>}()}. + + Example:: + num = Word(nums).setParseAction(lambda toks: int(toks[0])) + na = oneOf("N/A NA").setParseAction(replaceWith(math.nan)) + term = na | num + + OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234] + """ + return lambda s,l,t: [replStr] + +def removeQuotes(s,l,t): + """ + Helper parse action for removing quotation marks from parsed quoted strings. + + Example:: + # by default, quotation marks are included in parsed results + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"] + + # use removeQuotes to strip quotation marks from parsed results + quotedString.setParseAction(removeQuotes) + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"] + """ + return t[0][1:-1] + +def tokenMap(func, *args): + """ + Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional + args are passed, they are forwarded to the given function as additional arguments after + the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the + parsed data to an integer using base 16. + + Example (compare the last to example in L{ParserElement.transformString}:: + hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16)) + hex_ints.runTests(''' + 00 11 22 aa FF 0a 0d 1a + ''') + + upperword = Word(alphas).setParseAction(tokenMap(str.upper)) + OneOrMore(upperword).runTests(''' + my kingdom for a horse + ''') + + wd = Word(alphas).setParseAction(tokenMap(str.title)) + OneOrMore(wd).setParseAction(' '.join).runTests(''' + now is the winter of our discontent made glorious summer by this sun of york + ''') + prints:: + 00 11 22 aa FF 0a 0d 1a + [0, 17, 34, 170, 255, 10, 13, 26] + + my kingdom for a horse + ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE'] + + now is the winter of our discontent made glorious summer by this sun of york + ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York'] + """ + def pa(s,l,t): + return [func(tokn, *args) for tokn in t] + + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + pa.__name__ = func_name + + return pa + +upcaseTokens = tokenMap(lambda t: _ustr(t).upper()) +"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}""" + +downcaseTokens = tokenMap(lambda t: _ustr(t).lower()) +"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}""" + +def _makeTags(tagStr, xml): + """Internal helper to construct opening and closing tag expressions, given a tag name""" + if isinstance(tagStr,basestring): + resname = tagStr + tagStr = Keyword(tagStr, caseless=not xml) + else: + resname = tagStr.name + + tagAttrName = Word(alphas,alphanums+"_-:") + if (xml): + tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes ) + openTag = Suppress("<") + tagStr("tag") + \ + Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \ + Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") + else: + printablesLessRAbrack = "".join(c for c in printables if c not in ">") + tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack) + openTag = Suppress("<") + tagStr("tag") + \ + Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \ + Optional( Suppress("=") + tagAttrValue ) ))) + \ + Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") + closeTag = Combine(_L("</") + tagStr + ">") + + openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname) + closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("</%s>" % resname) + openTag.tag = resname + closeTag.tag = resname + return openTag, closeTag + +def makeHTMLTags(tagStr): + """ + Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches + tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values. + + Example:: + text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>' + # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple + a,a_end = makeHTMLTags("A") + link_expr = a + SkipTo(a_end)("link_text") + a_end + + for link in link_expr.searchString(text): + # attributes in the <A> tag (like "href" shown here) are also accessible as named results + print(link.link_text, '->', link.href) + prints:: + pyparsing -> http://pyparsing.wikispaces.com + """ + return _makeTags( tagStr, False ) + +def makeXMLTags(tagStr): + """ + Helper to construct opening and closing tag expressions for XML, given a tag name. Matches + tags only in the given upper/lower case. + + Example: similar to L{makeHTMLTags} + """ + return _makeTags( tagStr, True ) + +def withAttribute(*args,**attrDict): + """ + Helper to create a validating parse action to be used with start tags created + with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag + with a required attribute value, to avoid false matches on common tags such as + C{<TD>} or C{<DIV>}. + + Call C{withAttribute} with a series of attribute names and values. Specify the list + of filter attributes names and values as: + - keyword arguments, as in C{(align="right")}, or + - as an explicit dict with C{**} operator, when an attribute name is also a Python + reserved word, as in C{**{"class":"Customer", "align":"right"}} + - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) + For attribute names with a namespace prefix, you must use the second form. Attribute + names are matched insensitive to upper/lower case. + + If just testing for C{class} (with or without a namespace), use C{L{withClass}}. + + To verify that the attribute exists, but without specifying a value, pass + C{withAttribute.ANY_VALUE} as the value. + + Example:: + html = ''' + <div> + Some text + <div type="grid">1 4 0 1 0</div> + <div type="graph">1,3 2,3 1,1</div> + <div>this has no type</div> + </div> + + ''' + div,div_end = makeHTMLTags("div") + + # only match div tag having a type attribute with value "grid" + div_grid = div().setParseAction(withAttribute(type="grid")) + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + # construct a match with any div tag having a type attribute, regardless of the value + div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + if args: + attrs = args[:] + else: + attrs = attrDict.items() + attrs = [(k,v) for k,v in attrs] + def pa(s,l,tokens): + for attrName,attrValue in attrs: + if attrName not in tokens: + raise ParseException(s,l,"no matching attribute " + attrName) + if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: + raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % + (attrName, tokens[attrName], attrValue)) + return pa +withAttribute.ANY_VALUE = object() + +def withClass(classname, namespace=''): + """ + Simplified version of C{L{withAttribute}} when matching on a div class - made + difficult because C{class} is a reserved word in Python. + + Example:: + html = ''' + <div> + Some text + <div class="grid">1 4 0 1 0</div> + <div class="graph">1,3 2,3 1,1</div> + <div>this <div> has no class</div> + </div> + + ''' + div,div_end = makeHTMLTags("div") + div_grid = div().setParseAction(withClass("grid")) + + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + classattr = "%s:class" % namespace if namespace else "class" + return withAttribute(**{classattr : classname}) + +opAssoc = _Constants() +opAssoc.LEFT = object() +opAssoc.RIGHT = object() + +def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): + """ + Helper method for constructing grammars of expressions made up of + operators working in a precedence hierarchy. Operators may be unary or + binary, left- or right-associative. Parse actions can also be attached + to operator expressions. The generated parser will also recognize the use + of parentheses to override operator precedences (see example below). + + Note: if you define a deep operator list, you may see performance issues + when using infixNotation. See L{ParserElement.enablePackrat} for a + mechanism to potentially improve your parser performance. + + Parameters: + - baseExpr - expression representing the most basic element for the nested + - opList - list of tuples, one for each operator precedence level in the + expression grammar; each tuple is of the form + (opExpr, numTerms, rightLeftAssoc, parseAction), where: + - opExpr is the pyparsing expression for the operator; + may also be a string, which will be converted to a Literal; + if numTerms is 3, opExpr is a tuple of two expressions, for the + two operators separating the 3 terms + - numTerms is the number of terms for this operator (must + be 1, 2, or 3) + - rightLeftAssoc is the indicator whether the operator is + right or left associative, using the pyparsing-defined + constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. + - parseAction is the parse action to be associated with + expressions matching this operator expression (the + parse action tuple member may be omitted) + - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) + - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) + + Example:: + # simple example of four-function arithmetic with ints and variable names + integer = pyparsing_common.signed_integer + varname = pyparsing_common.identifier + + arith_expr = infixNotation(integer | varname, + [ + ('-', 1, opAssoc.RIGHT), + (oneOf('* /'), 2, opAssoc.LEFT), + (oneOf('+ -'), 2, opAssoc.LEFT), + ]) + + arith_expr.runTests(''' + 5+3*6 + (5+3)*6 + -2--11 + ''', fullDump=False) + prints:: + 5+3*6 + [[5, '+', [3, '*', 6]]] + + (5+3)*6 + [[[5, '+', 3], '*', 6]] + + -2--11 + [[['-', 2], '-', ['-', 11]]] + """ + ret = Forward() + lastExpr = baseExpr | ( lpar + ret + rpar ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr + if arity == 3: + if opExpr is None or len(opExpr) != 2: + raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") + opExpr1, opExpr2 = opExpr + thisExpr = Forward().setName(termName) + if rightLeftAssoc == opAssoc.LEFT: + if arity == 1: + matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) + else: + matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ + Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + elif rightLeftAssoc == opAssoc.RIGHT: + if arity == 1: + # try to avoid LR with this extra test + if not isinstance(opExpr, Optional): + opExpr = Optional(opExpr) + matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) + else: + matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ + Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + else: + raise ValueError("operator must indicate right or left associativity") + if pa: + matchExpr.setParseAction( pa ) + thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) + lastExpr = thisExpr + ret <<= lastExpr + return ret + +operatorPrecedence = infixNotation +"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" + +dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") +sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") +quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| + Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") + +def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): + """ + Helper method for defining nested lists enclosed in opening and closing + delimiters ("(" and ")" are the default). + + Parameters: + - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression + - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression + - content - expression for items within the nested lists (default=C{None}) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString}) + + If an expression is not provided for the content argument, the nested + expression will capture all whitespace-delimited content between delimiters + as a list of separate values. + + Use the C{ignoreExpr} argument to define expressions that may contain + opening or closing characters that should not be treated as opening + or closing characters for nesting, such as quotedString or a comment + expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. + The default is L{quotedString}, but if no expressions are to be ignored, + then pass C{None} for this argument. + + Example:: + data_type = oneOf("void int short long char float double") + decl_data_type = Combine(data_type + Optional(Word('*'))) + ident = Word(alphas+'_', alphanums+'_') + number = pyparsing_common.number + arg = Group(decl_data_type + ident) + LPAR,RPAR = map(Suppress, "()") + + code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) + + c_function = (decl_data_type("type") + + ident("name") + + LPAR + Optional(delimitedList(arg), [])("args") + RPAR + + code_body("body")) + c_function.ignore(cStyleComment) + + source_code = ''' + int is_odd(int x) { + return (x%2); + } + + int dec_to_hex(char hchar) { + if (hchar >= '0' && hchar <= '9') { + return (ord(hchar)-ord('0')); + } else { + return (10+ord(hchar)-ord('A')); + } + } + ''' + for func in c_function.searchString(source_code): + print("%(name)s (%(type)s) args: %(args)s" % func) + + prints:: + is_odd (int) args: [['int', 'x']] + dec_to_hex (int) args: [['char', 'hchar']] + """ + if opener == closer: + raise ValueError("opening and closing strings cannot be the same") + if content is None: + if isinstance(opener,basestring) and isinstance(closer,basestring): + if len(opener) == 1 and len(closer)==1: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS + ).setParseAction(lambda t:t[0].strip())) + else: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + ~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + raise ValueError("opening and closing arguments must be strings if no content expression is given") + ret = Forward() + if ignoreExpr is not None: + ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) + else: + ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) + ret.setName('nested %s%s expression' % (opener,closer)) + return ret + +def indentedBlock(blockStatementExpr, indentStack, indent=True): + """ + Helper method for defining space-delimited indentation blocks, such as + those used to define block statements in Python source code. + + Parameters: + - blockStatementExpr - expression defining syntax of statement that + is repeated within the indented block + - indentStack - list created by caller to manage indentation stack + (multiple statementWithIndentedBlock expressions within a single grammar + should share a common indentStack) + - indent - boolean indicating whether block must be indented beyond the + the current level; set to False for block of left-most statements + (default=C{True}) + + A valid block must contain at least one C{blockStatement}. + + Example:: + data = ''' + def A(z): + A1 + B = 100 + G = A2 + A2 + A3 + B + def BB(a,b,c): + BB1 + def BBA(): + bba1 + bba2 + bba3 + C + D + def spam(x,y): + def eggs(z): + pass + ''' + + + indentStack = [1] + stmt = Forward() + + identifier = Word(alphas, alphanums) + funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") + func_body = indentedBlock(stmt, indentStack) + funcDef = Group( funcDecl + func_body ) + + rvalue = Forward() + funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") + rvalue << (funcCall | identifier | Word(nums)) + assignment = Group(identifier + "=" + rvalue) + stmt << ( funcDef | assignment | identifier ) + + module_body = OneOrMore(stmt) + + parseTree = module_body.parseString(data) + parseTree.pprint() + prints:: + [['def', + 'A', + ['(', 'z', ')'], + ':', + [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], + 'B', + ['def', + 'BB', + ['(', 'a', 'b', 'c', ')'], + ':', + [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], + 'C', + 'D', + ['def', + 'spam', + ['(', 'x', 'y', ')'], + ':', + [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] + """ + def checkPeerIndent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if curCol != indentStack[-1]: + if curCol > indentStack[-1]: + raise ParseFatalException(s,l,"illegal nesting") + raise ParseException(s,l,"not a peer entry") + + def checkSubIndent(s,l,t): + curCol = col(l,s) + if curCol > indentStack[-1]: + indentStack.append( curCol ) + else: + raise ParseException(s,l,"not a subentry") + + def checkUnindent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): + raise ParseException(s,l,"not an unindent") + indentStack.pop() + + NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) + INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') + PEER = Empty().setParseAction(checkPeerIndent).setName('') + UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') + if indent: + smExpr = Group( Optional(NL) + + #~ FollowedBy(blockStatementExpr) + + INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) + else: + smExpr = Group( Optional(NL) + + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) + blockStatementExpr.ignore(_bslash + LineEnd()) + return smExpr.setName('indented block') + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) +_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) +commonHTMLEntity = Regex('&(?P<entity>' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") +def replaceHTMLEntity(t): + """Helper parser action to replace common HTML entities with their special characters""" + return _htmlEntityMap.get(t.entity) + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") +"Comment of the form C{/* ... */}" + +htmlComment = Regex(r"<!--[\s\S]*?-->").setName("HTML comment") +"Comment of the form C{<!-- ... -->}" + +restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") +dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") +"Comment of the form C{// ... (to end of line)}" + +cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") +"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" + +javaStyleComment = cppStyleComment +"Same as C{L{cppStyleComment}}" + +pythonStyleComment = Regex(r"#.*").setName("Python style comment") +"Comment of the form C{# ... (to end of line)}" + +_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + + Optional( Word(" \t") + + ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") +commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") +"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. + This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" + +# some other useful expressions - using lower-case class name since we are really using this as a namespace +class pyparsing_common: + """ + Here are some common low-level expressions that may be useful in jump-starting parser development: + - numeric forms (L{integers<integer>}, L{reals<real>}, L{scientific notation<sci_real>}) + - common L{programming identifiers<identifier>} + - network addresses (L{MAC<mac_address>}, L{IPv4<ipv4_address>}, L{IPv6<ipv6_address>}) + - ISO8601 L{dates<iso8601_date>} and L{datetime<iso8601_datetime>} + - L{UUID<uuid>} + - L{comma-separated list<comma_separated_list>} + Parse actions: + - C{L{convertToInteger}} + - C{L{convertToFloat}} + - C{L{convertToDate}} + - C{L{convertToDatetime}} + - C{L{stripHTMLTags}} + - C{L{upcaseTokens}} + - C{L{downcaseTokens}} + + Example:: + pyparsing_common.number.runTests(''' + # any int or real number, returned as the appropriate type + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.fnumber.runTests(''' + # any int or real number, returned as float + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.hex_integer.runTests(''' + # hex numbers + 100 + FF + ''') + + pyparsing_common.fraction.runTests(''' + # fractions + 1/2 + -3/4 + ''') + + pyparsing_common.mixed_integer.runTests(''' + # mixed fractions + 1 + 1/2 + -3/4 + 1-3/4 + ''') + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(''' + # uuid + 12345678-1234-5678-1234-567812345678 + ''') + prints:: + # any int or real number, returned as the appropriate type + 100 + [100] + + -100 + [-100] + + +100 + [100] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # any int or real number, returned as float + 100 + [100.0] + + -100 + [-100.0] + + +100 + [100.0] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # hex numbers + 100 + [256] + + FF + [255] + + # fractions + 1/2 + [0.5] + + -3/4 + [-0.75] + + # mixed fractions + 1 + [1] + + 1/2 + [0.5] + + -3/4 + [-0.75] + + 1-3/4 + [1.75] + + # uuid + 12345678-1234-5678-1234-567812345678 + [UUID('12345678-1234-5678-1234-567812345678')] + """ + + convertToInteger = tokenMap(int) + """ + Parse action for converting parsed integers to Python int + """ + + convertToFloat = tokenMap(float) + """ + Parse action for converting parsed numbers to Python float + """ + + integer = Word(nums).setName("integer").setParseAction(convertToInteger) + """expression that parses an unsigned integer, returns an int""" + + hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) + """expression that parses a hexadecimal integer, returns an int""" + + signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) + """expression that parses an integer with optional leading sign, returns an int""" + + fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") + """fractional expression of an integer divided by an integer, returns a float""" + fraction.addParseAction(lambda t: t[0]/t[-1]) + + mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") + """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" + mixed_integer.addParseAction(sum) + + real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) + """expression that parses a floating point number and returns a float""" + + sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) + """expression that parses a floating point number with optional scientific notation and returns a float""" + + # streamlining this expression makes the docs nicer-looking + number = (sci_real | real | signed_integer).streamline() + """any numeric expression, returns the corresponding Python type""" + + fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) + """any int or real number, returned as float""" + + identifier = Word(alphas+'_', alphanums+'_').setName("identifier") + """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" + + ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") + "IPv4 address (C{0.0.0.0 - 255.255.255.255})" + + _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") + _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") + _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") + _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) + _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") + ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") + "IPv6 address (long, short, or mixed form)" + + mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") + "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" + + @staticmethod + def convertToDate(fmt="%Y-%m-%d"): + """ + Helper to create a parse action for converting parsed date string to Python datetime.date + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) + + Example:: + date_expr = pyparsing_common.iso8601_date.copy() + date_expr.setParseAction(pyparsing_common.convertToDate()) + print(date_expr.parseString("1999-12-31")) + prints:: + [datetime.date(1999, 12, 31)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt).date() + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + @staticmethod + def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): + """ + Helper to create a parse action for converting parsed datetime string to Python datetime.datetime + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) + + Example:: + dt_expr = pyparsing_common.iso8601_datetime.copy() + dt_expr.setParseAction(pyparsing_common.convertToDatetime()) + print(dt_expr.parseString("1999-12-31T23:59:59.999")) + prints:: + [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt) + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + iso8601_date = Regex(r'(?P<year>\d{4})(?:-(?P<month>\d\d)(?:-(?P<day>\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (C{yyyy-mm-dd})" + + iso8601_datetime = Regex(r'(?P<year>\d{4})-(?P<month>\d\d)-(?P<day>\d\d)[T ](?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d(\.\d*)?)?)?(?P<tz>Z|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") + "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" + + uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") + "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" + + _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() + @staticmethod + def stripHTMLTags(s, l, tokens): + """ + Parse action to remove HTML tags from web page HTML source + + Example:: + # strip HTML links from normal text + text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>' + td,td_end = makeHTMLTags("TD") + table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end + + print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' + """ + return pyparsing_common._html_stripper.transformString(tokens[0]) + + _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') + + Optional( White(" \t") ) ) ).streamline().setName("commaItem") + comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") + """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" + + upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) + """Parse action to convert tokens to upper case.""" + + downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) + """Parse action to convert tokens to lower case.""" + + +if __name__ == "__main__": + + selectToken = CaselessLiteral("select") + fromToken = CaselessLiteral("from") + + ident = Word(alphas, alphanums + "_$") + + columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + columnNameList = Group(delimitedList(columnName)).setName("columns") + columnSpec = ('*' | columnNameList) + + tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + tableNameList = Group(delimitedList(tableName)).setName("tables") + + simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") + + # demo runTests method, including embedded comments in test string + simpleSQL.runTests(""" + # '*' as column list and dotted table name + select * from SYS.XYZZY + + # caseless match on "SELECT", and casts back to "select" + SELECT * from XYZZY, ABC + + # list of column names, and mixed case SELECT keyword + Select AA,BB,CC from Sys.dual + + # multiple tables + Select A, B, C from Sys.dual, Table2 + + # invalid SELECT keyword - should fail + Xelect A, B, C from Sys.dual + + # incomplete command - should fail + Select + + # invalid column name - should fail + Select ^^^ frox Sys.dual + + """) + + pyparsing_common.number.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + # any int or real number, returned as float + pyparsing_common.fnumber.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + pyparsing_common.hex_integer.runTests(""" + 100 + FF + """) + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(""" + 12345678-1234-5678-1234-567812345678 + """) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/re-vendor.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/re-vendor.py new file mode 100644 index 0000000..0a52123 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/re-vendor.py @@ -0,0 +1,34 @@ +import os +import sys +import pip +import glob +import shutil + +here = os.path.abspath(os.path.dirname(__file__)) + +def usage(): + print("Usage: re-vendor.py [clean|vendor]") + sys.exit(1) + +def clean(): + for fn in os.listdir(here): + dirname = os.path.join(here, fn) + if os.path.isdir(dirname): + shutil.rmtree(dirname) + # six is a single file, not a package + os.unlink(os.path.join(here, 'six.py')) + +def vendor(): + pip.main(['install', '-t', here, '-r', 'vendor.txt']) + for dirname in glob.glob('*.egg-info'): + shutil.rmtree(dirname) + +if __name__ == '__main__': + if len(sys.argv) != 2: + usage() + if sys.argv[1] == 'clean': + clean() + elif sys.argv[1] == 'vendor': + vendor() + else: + usage() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/__init__.py new file mode 100644 index 0000000..44f6836 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/__init__.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + +# __ +# /__) _ _ _ _ _/ _ +# / ( (- (/ (/ (- _) / _) +# / + +""" +Requests HTTP library +~~~~~~~~~~~~~~~~~~~~~ + +Requests is an HTTP library, written in Python, for human beings. Basic GET +usage: + + >>> import requests + >>> r = requests.get('https://www.python.org') + >>> r.status_code + 200 + >>> 'Python is a programming language' in r.content + True + +... or POST: + + >>> payload = dict(key1='value1', key2='value2') + >>> r = requests.post('http://httpbin.org/post', data=payload) + >>> print(r.text) + { + ... + "form": { + "key2": "value2", + "key1": "value1" + }, + ... + } + +The other HTTP methods are supported - see `requests.api`. Full documentation +is at <http://python-requests.org>. + +:copyright: (c) 2016 by Kenneth Reitz. +:license: Apache 2.0, see LICENSE for more details. +""" + +__title__ = 'requests' +__version__ = '2.11.1' +__build__ = 0x021101 +__author__ = 'Kenneth Reitz' +__license__ = 'Apache 2.0' +__copyright__ = 'Copyright 2016 Kenneth Reitz' + +# Attempt to enable urllib3's SNI support, if possible +# Note: Patched by pip to prevent using the PyOpenSSL module. On Windows this +# prevents upgrading cryptography. +# try: +# from .packages.urllib3.contrib import pyopenssl +# pyopenssl.inject_into_urllib3() +# except ImportError: +# pass + +import warnings + +# urllib3's DependencyWarnings should be silenced. +from .packages.urllib3.exceptions import DependencyWarning +warnings.simplefilter('ignore', DependencyWarning) + +from . import utils +from .models import Request, Response, PreparedRequest +from .api import request, get, head, post, patch, put, delete, options +from .sessions import session, Session +from .status_codes import codes +from .exceptions import ( + RequestException, Timeout, URLRequired, + TooManyRedirects, HTTPError, ConnectionError, + FileModeWarning, ConnectTimeout, ReadTimeout +) + +# Set default logging handler to avoid "No handler found" warnings. +import logging +try: # Python 2.7+ + from logging import NullHandler +except ImportError: + class NullHandler(logging.Handler): + def emit(self, record): + pass + +logging.getLogger(__name__).addHandler(NullHandler()) + +# FileModeWarnings go off per the default. +warnings.simplefilter('default', FileModeWarning, append=True) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/adapters.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/adapters.py new file mode 100644 index 0000000..4a4c4e0 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/adapters.py @@ -0,0 +1,503 @@ +# -*- coding: utf-8 -*- + +""" +requests.adapters +~~~~~~~~~~~~~~~~~ + +This module contains the transport adapters that Requests uses to define +and maintain connections. +""" + +import os.path +import socket + +from .models import Response +from .packages.urllib3.poolmanager import PoolManager, proxy_from_url +from .packages.urllib3.response import HTTPResponse +from .packages.urllib3.util import Timeout as TimeoutSauce +from .packages.urllib3.util.retry import Retry +from .compat import urlparse, basestring +from .utils import (DEFAULT_CA_BUNDLE_PATH, get_encoding_from_headers, + prepend_scheme_if_needed, get_auth_from_url, urldefragauth, + select_proxy, to_native_string) +from .structures import CaseInsensitiveDict +from .packages.urllib3.exceptions import ClosedPoolError +from .packages.urllib3.exceptions import ConnectTimeoutError +from .packages.urllib3.exceptions import HTTPError as _HTTPError +from .packages.urllib3.exceptions import MaxRetryError +from .packages.urllib3.exceptions import NewConnectionError +from .packages.urllib3.exceptions import ProxyError as _ProxyError +from .packages.urllib3.exceptions import ProtocolError +from .packages.urllib3.exceptions import ReadTimeoutError +from .packages.urllib3.exceptions import SSLError as _SSLError +from .packages.urllib3.exceptions import ResponseError +from .cookies import extract_cookies_to_jar +from .exceptions import (ConnectionError, ConnectTimeout, ReadTimeout, SSLError, + ProxyError, RetryError, InvalidSchema) +from .auth import _basic_auth_str + +try: + from .packages.urllib3.contrib.socks import SOCKSProxyManager +except ImportError: + def SOCKSProxyManager(*args, **kwargs): + raise InvalidSchema("Missing dependencies for SOCKS support.") + +DEFAULT_POOLBLOCK = False +DEFAULT_POOLSIZE = 10 +DEFAULT_RETRIES = 0 +DEFAULT_POOL_TIMEOUT = None + + +class BaseAdapter(object): + """The Base Transport Adapter""" + + def __init__(self): + super(BaseAdapter, self).__init__() + + def send(self, request, stream=False, timeout=None, verify=True, + cert=None, proxies=None): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest <PreparedRequest>` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) <timeouts>` tuple. + :type timeout: float or tuple + :param verify: (optional) Whether to verify SSL certificates. + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + """ + raise NotImplementedError + + def close(self): + """Cleans up adapter specific items.""" + raise NotImplementedError + + +class HTTPAdapter(BaseAdapter): + """The built-in HTTP Adapter for urllib3. + + Provides a general-case interface for Requests sessions to contact HTTP and + HTTPS urls by implementing the Transport Adapter interface. This class will + usually be created by the :class:`Session <Session>` class under the + covers. + + :param pool_connections: The number of urllib3 connection pools to cache. + :param pool_maxsize: The maximum number of connections to save in the pool. + :param max_retries: The maximum number of retries each connection + should attempt. Note, this applies only to failed DNS lookups, socket + connections and connection timeouts, never to requests where data has + made it to the server. By default, Requests does not retry failed + connections. If you need granular control over the conditions under + which we retry a request, import urllib3's ``Retry`` class and pass + that instead. + :param pool_block: Whether the connection pool should block for connections. + + Usage:: + + >>> import requests + >>> s = requests.Session() + >>> a = requests.adapters.HTTPAdapter(max_retries=3) + >>> s.mount('http://', a) + """ + __attrs__ = ['max_retries', 'config', '_pool_connections', '_pool_maxsize', + '_pool_block'] + + def __init__(self, pool_connections=DEFAULT_POOLSIZE, + pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES, + pool_block=DEFAULT_POOLBLOCK): + if max_retries == DEFAULT_RETRIES: + self.max_retries = Retry(0, read=False) + else: + self.max_retries = Retry.from_int(max_retries) + self.config = {} + self.proxy_manager = {} + + super(HTTPAdapter, self).__init__() + + self._pool_connections = pool_connections + self._pool_maxsize = pool_maxsize + self._pool_block = pool_block + + self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block) + + def __getstate__(self): + return dict((attr, getattr(self, attr, None)) for attr in + self.__attrs__) + + def __setstate__(self, state): + # Can't handle by adding 'proxy_manager' to self.__attrs__ because + # self.poolmanager uses a lambda function, which isn't pickleable. + self.proxy_manager = {} + self.config = {} + + for attr, value in state.items(): + setattr(self, attr, value) + + self.init_poolmanager(self._pool_connections, self._pool_maxsize, + block=self._pool_block) + + def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs): + """Initializes a urllib3 PoolManager. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param connections: The number of urllib3 connection pools to cache. + :param maxsize: The maximum number of connections to save in the pool. + :param block: Block when no free connections are available. + :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager. + """ + # save these values for pickling + self._pool_connections = connections + self._pool_maxsize = maxsize + self._pool_block = block + + self.poolmanager = PoolManager(num_pools=connections, maxsize=maxsize, + block=block, strict=True, **pool_kwargs) + + def proxy_manager_for(self, proxy, **proxy_kwargs): + """Return urllib3 ProxyManager for the given proxy. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param proxy: The proxy to return a urllib3 ProxyManager for. + :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager. + :returns: ProxyManager + :rtype: requests.packages.urllib3.ProxyManager + """ + if proxy in self.proxy_manager: + manager = self.proxy_manager[proxy] + elif proxy.lower().startswith('socks'): + username, password = get_auth_from_url(proxy) + manager = self.proxy_manager[proxy] = SOCKSProxyManager( + proxy, + username=username, + password=password, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs + ) + else: + proxy_headers = self.proxy_headers(proxy) + manager = self.proxy_manager[proxy] = proxy_from_url( + proxy, + proxy_headers=proxy_headers, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs) + + return manager + + def cert_verify(self, conn, url, verify, cert): + """Verify a SSL certificate. This method should not be called from user + code, and is only exposed for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param conn: The urllib3 connection object associated with the cert. + :param url: The requested URL. + :param verify: Whether we should actually verify the certificate. + :param cert: The SSL certificate to verify. + """ + if url.lower().startswith('https') and verify: + + cert_loc = None + + # Allow self-specified cert location. + if verify is not True: + cert_loc = verify + + if not cert_loc: + cert_loc = DEFAULT_CA_BUNDLE_PATH + + if not cert_loc: + raise Exception("Could not find a suitable SSL CA certificate bundle.") + + conn.cert_reqs = 'CERT_REQUIRED' + + if not os.path.isdir(cert_loc): + conn.ca_certs = cert_loc + else: + conn.ca_cert_dir = cert_loc + else: + conn.cert_reqs = 'CERT_NONE' + conn.ca_certs = None + conn.ca_cert_dir = None + + if cert: + if not isinstance(cert, basestring): + conn.cert_file = cert[0] + conn.key_file = cert[1] + else: + conn.cert_file = cert + + def build_response(self, req, resp): + """Builds a :class:`Response <requests.Response>` object from a urllib3 + response. This should not be called from user code, and is only exposed + for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>` + + :param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response. + :param resp: The urllib3 response object. + :rtype: requests.Response + """ + response = Response() + + # Fallback to None if there's no status_code, for whatever reason. + response.status_code = getattr(resp, 'status', None) + + # Make headers case-insensitive. + response.headers = CaseInsensitiveDict(getattr(resp, 'headers', {})) + + # Set encoding. + response.encoding = get_encoding_from_headers(response.headers) + response.raw = resp + response.reason = response.raw.reason + + if isinstance(req.url, bytes): + response.url = req.url.decode('utf-8') + else: + response.url = req.url + + # Add new cookies from the server. + extract_cookies_to_jar(response.cookies, req, resp) + + # Give the Response some context. + response.request = req + response.connection = self + + return response + + def get_connection(self, url, proxies=None): + """Returns a urllib3 connection for the given URL. This should not be + called from user code, and is only exposed for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param url: The URL to connect to. + :param proxies: (optional) A Requests-style dictionary of proxies used on this request. + :rtype: requests.packages.urllib3.ConnectionPool + """ + proxy = select_proxy(url, proxies) + + if proxy: + proxy = prepend_scheme_if_needed(proxy, 'http') + proxy_manager = self.proxy_manager_for(proxy) + conn = proxy_manager.connection_from_url(url) + else: + # Only scheme should be lower case + parsed = urlparse(url) + url = parsed.geturl() + conn = self.poolmanager.connection_from_url(url) + + return conn + + def close(self): + """Disposes of any internal state. + + Currently, this closes the PoolManager and any active ProxyManager, + which closes any pooled connections. + """ + self.poolmanager.clear() + for proxy in self.proxy_manager.values(): + proxy.clear() + + def request_url(self, request, proxies): + """Obtain the url to use when making the final request. + + If the message is being sent through a HTTP proxy, the full URL has to + be used. Otherwise, we should only use the path portion of the URL. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param request: The :class:`PreparedRequest <PreparedRequest>` being sent. + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs. + :rtype: str + """ + proxy = select_proxy(request.url, proxies) + scheme = urlparse(request.url).scheme + + is_proxied_http_request = (proxy and scheme != 'https') + using_socks_proxy = False + if proxy: + proxy_scheme = urlparse(proxy).scheme.lower() + using_socks_proxy = proxy_scheme.startswith('socks') + + url = request.path_url + if is_proxied_http_request and not using_socks_proxy: + url = urldefragauth(request.url) + + return url + + def add_headers(self, request, **kwargs): + """Add any headers needed by the connection. As of v2.0 this does + nothing by default, but is left for overriding by users that subclass + the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param request: The :class:`PreparedRequest <PreparedRequest>` to add headers to. + :param kwargs: The keyword arguments from the call to send(). + """ + pass + + def proxy_headers(self, proxy): + """Returns a dictionary of the headers to add to any request sent + through a proxy. This works with urllib3 magic to ensure that they are + correctly sent to the proxy, rather than in a tunnelled request if + CONNECT is being used. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param proxies: The url of the proxy being used for this request. + :rtype: dict + """ + headers = {} + username, password = get_auth_from_url(proxy) + + if username and password: + headers['Proxy-Authorization'] = _basic_auth_str(username, + password) + + return headers + + def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest <PreparedRequest>` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) <timeouts>` tuple. + :type timeout: float or tuple + :param verify: (optional) Whether to verify SSL certificates. + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + conn = self.get_connection(request.url, proxies) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers(request) + + chunked = not (request.body is None or 'Content-Length' in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError as e: + # this may raise a string formatting error. + err = ("Invalid timeout {0}. Pass a (connect, read) " + "timeout tuple, or a single float to set " + "both timeouts to the same value".format(timeout)) + raise ValueError(err) + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + if not chunked: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout + ) + + # Send the request. + else: + if hasattr(conn, 'proxy_pool'): + conn = conn.proxy_pool + + low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT) + + try: + low_conn.putrequest(request.method, + url, + skip_accept_encoding=True) + + for header, value in request.headers.items(): + low_conn.putheader(header, value) + + low_conn.endheaders() + + for i in request.body: + low_conn.send(hex(len(i))[2:].encode('utf-8')) + low_conn.send(b'\r\n') + low_conn.send(i) + low_conn.send(b'\r\n') + low_conn.send(b'0\r\n\r\n') + + # Receive the response from the server + try: + # For Python 2.7+ versions, use buffering of HTTP + # responses + r = low_conn.getresponse(buffering=True) + except TypeError: + # For compatibility with Python 2.6 versions and back + r = low_conn.getresponse() + + resp = HTTPResponse.from_httplib( + r, + pool=conn, + connection=low_conn, + preload_content=False, + decode_content=False + ) + except: + # If we hit any problems here, clean up the connection. + # Then, reraise so that we can handle the actual exception. + low_conn.close() + raise + + except (ProtocolError, socket.error) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + raise ConnectionError(e, request=request) + + except ClosedPoolError as e: + raise ConnectionError(e, request=request) + + except _ProxyError as e: + raise ProxyError(e) + + except (_SSLError, _HTTPError) as e: + if isinstance(e, _SSLError): + raise SSLError(e, request=request) + elif isinstance(e, ReadTimeoutError): + raise ReadTimeout(e, request=request) + else: + raise + + return self.build_response(request, resp) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/api.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/api.py new file mode 100644 index 0000000..580b3f3 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/api.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- + +""" +requests.api +~~~~~~~~~~~~ + +This module implements the Requests API. + +:copyright: (c) 2012 by Kenneth Reitz. +:license: Apache2, see LICENSE for more details. +""" + +from . import sessions + + +def request(method, url, **kwargs): + """Constructs and sends a :class:`Request <Request>`. + + :param method: method for the new :class:`Request` object. + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. + :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. + :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. + :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. + ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` + or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string + defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers + to add for the file. + :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. + :param timeout: (optional) How long to wait for the server to send data + before giving up, as a float, or a :ref:`(connect timeout, read + timeout) <timeouts>` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. + :param verify: (optional) whether the SSL cert will be verified. A CA_BUNDLE path can also be provided. Defaults to ``True``. + :param stream: (optional) if ``False``, the response content will be immediately downloaded. + :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. + :return: :class:`Response <Response>` object + :rtype: requests.Response + + Usage:: + + >>> import requests + >>> req = requests.request('GET', 'http://httpbin.org/get') + <Response [200]> + """ + + # By using the 'with' statement we are sure the session is closed, thus we + # avoid leaving sockets open which can trigger a ResourceWarning in some + # cases, and look like a memory leak in others. + with sessions.Session() as session: + return session.request(method=method, url=url, **kwargs) + + +def get(url, params=None, **kwargs): + """Sends a GET request. + + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return request('get', url, params=params, **kwargs) + + +def options(url, **kwargs): + """Sends a OPTIONS request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return request('options', url, **kwargs) + + +def head(url, **kwargs): + """Sends a HEAD request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', False) + return request('head', url, **kwargs) + + +def post(url, data=None, json=None, **kwargs): + """Sends a POST request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + return request('post', url, data=data, json=json, **kwargs) + + +def put(url, data=None, **kwargs): + """Sends a PUT request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + return request('put', url, data=data, **kwargs) + + +def patch(url, data=None, **kwargs): + """Sends a PATCH request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + return request('patch', url, data=data, **kwargs) + + +def delete(url, **kwargs): + """Sends a DELETE request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + return request('delete', url, **kwargs) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/auth.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/auth.py new file mode 100644 index 0000000..49bcb24 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/auth.py @@ -0,0 +1,252 @@ +# -*- coding: utf-8 -*- + +""" +requests.auth +~~~~~~~~~~~~~ + +This module contains the authentication handlers for Requests. +""" + +import os +import re +import time +import hashlib +import threading + +from base64 import b64encode + +from .compat import urlparse, str +from .cookies import extract_cookies_to_jar +from .utils import parse_dict_header, to_native_string +from .status_codes import codes + +CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded' +CONTENT_TYPE_MULTI_PART = 'multipart/form-data' + + +def _basic_auth_str(username, password): + """Returns a Basic Auth string.""" + + authstr = 'Basic ' + to_native_string( + b64encode(('%s:%s' % (username, password)).encode('latin1')).strip() + ) + + return authstr + + +class AuthBase(object): + """Base class that all auth implementations derive from""" + + def __call__(self, r): + raise NotImplementedError('Auth hooks must be callable.') + + +class HTTPBasicAuth(AuthBase): + """Attaches HTTP Basic Authentication to the given Request object.""" + + def __init__(self, username, password): + self.username = username + self.password = password + + def __eq__(self, other): + return all([ + self.username == getattr(other, 'username', None), + self.password == getattr(other, 'password', None) + ]) + + def __ne__(self, other): + return not self == other + + def __call__(self, r): + r.headers['Authorization'] = _basic_auth_str(self.username, self.password) + return r + + +class HTTPProxyAuth(HTTPBasicAuth): + """Attaches HTTP Proxy Authentication to a given Request object.""" + + def __call__(self, r): + r.headers['Proxy-Authorization'] = _basic_auth_str(self.username, self.password) + return r + + +class HTTPDigestAuth(AuthBase): + """Attaches HTTP Digest Authentication to the given Request object.""" + + def __init__(self, username, password): + self.username = username + self.password = password + # Keep state in per-thread local storage + self._thread_local = threading.local() + + def init_per_thread_state(self): + # Ensure state is initialized just once per-thread + if not hasattr(self._thread_local, 'init'): + self._thread_local.init = True + self._thread_local.last_nonce = '' + self._thread_local.nonce_count = 0 + self._thread_local.chal = {} + self._thread_local.pos = None + self._thread_local.num_401_calls = None + + def build_digest_header(self, method, url): + """ + :rtype: str + """ + + realm = self._thread_local.chal['realm'] + nonce = self._thread_local.chal['nonce'] + qop = self._thread_local.chal.get('qop') + algorithm = self._thread_local.chal.get('algorithm') + opaque = self._thread_local.chal.get('opaque') + hash_utf8 = None + + if algorithm is None: + _algorithm = 'MD5' + else: + _algorithm = algorithm.upper() + # lambdas assume digest modules are imported at the top level + if _algorithm == 'MD5' or _algorithm == 'MD5-SESS': + def md5_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.md5(x).hexdigest() + hash_utf8 = md5_utf8 + elif _algorithm == 'SHA': + def sha_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.sha1(x).hexdigest() + hash_utf8 = sha_utf8 + + KD = lambda s, d: hash_utf8("%s:%s" % (s, d)) + + if hash_utf8 is None: + return None + + # XXX not implemented yet + entdig = None + p_parsed = urlparse(url) + #: path is request-uri defined in RFC 2616 which should not be empty + path = p_parsed.path or "/" + if p_parsed.query: + path += '?' + p_parsed.query + + A1 = '%s:%s:%s' % (self.username, realm, self.password) + A2 = '%s:%s' % (method, path) + + HA1 = hash_utf8(A1) + HA2 = hash_utf8(A2) + + if nonce == self._thread_local.last_nonce: + self._thread_local.nonce_count += 1 + else: + self._thread_local.nonce_count = 1 + ncvalue = '%08x' % self._thread_local.nonce_count + s = str(self._thread_local.nonce_count).encode('utf-8') + s += nonce.encode('utf-8') + s += time.ctime().encode('utf-8') + s += os.urandom(8) + + cnonce = (hashlib.sha1(s).hexdigest()[:16]) + if _algorithm == 'MD5-SESS': + HA1 = hash_utf8('%s:%s:%s' % (HA1, nonce, cnonce)) + + if not qop: + respdig = KD(HA1, "%s:%s" % (nonce, HA2)) + elif qop == 'auth' or 'auth' in qop.split(','): + noncebit = "%s:%s:%s:%s:%s" % ( + nonce, ncvalue, cnonce, 'auth', HA2 + ) + respdig = KD(HA1, noncebit) + else: + # XXX handle auth-int. + return None + + self._thread_local.last_nonce = nonce + + # XXX should the partial digests be encoded too? + base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \ + 'response="%s"' % (self.username, realm, nonce, path, respdig) + if opaque: + base += ', opaque="%s"' % opaque + if algorithm: + base += ', algorithm="%s"' % algorithm + if entdig: + base += ', digest="%s"' % entdig + if qop: + base += ', qop="auth", nc=%s, cnonce="%s"' % (ncvalue, cnonce) + + return 'Digest %s' % (base) + + def handle_redirect(self, r, **kwargs): + """Reset num_401_calls counter on redirects.""" + if r.is_redirect: + self._thread_local.num_401_calls = 1 + + def handle_401(self, r, **kwargs): + """ + Takes the given response and tries digest-auth, if needed. + + :rtype: requests.Response + """ + + if self._thread_local.pos is not None: + # Rewind the file position indicator of the body to where + # it was to resend the request. + r.request.body.seek(self._thread_local.pos) + s_auth = r.headers.get('www-authenticate', '') + + if 'digest' in s_auth.lower() and self._thread_local.num_401_calls < 2: + + self._thread_local.num_401_calls += 1 + pat = re.compile(r'digest ', flags=re.IGNORECASE) + self._thread_local.chal = parse_dict_header(pat.sub('', s_auth, count=1)) + + # Consume content and release the original connection + # to allow our new request to reuse the same one. + r.content + r.close() + prep = r.request.copy() + extract_cookies_to_jar(prep._cookies, r.request, r.raw) + prep.prepare_cookies(prep._cookies) + + prep.headers['Authorization'] = self.build_digest_header( + prep.method, prep.url) + _r = r.connection.send(prep, **kwargs) + _r.history.append(r) + _r.request = prep + + return _r + + self._thread_local.num_401_calls = 1 + return r + + def __call__(self, r): + # Initialize per-thread state, if needed + self.init_per_thread_state() + # If we have a saved nonce, skip the 401 + if self._thread_local.last_nonce: + r.headers['Authorization'] = self.build_digest_header(r.method, r.url) + try: + self._thread_local.pos = r.body.tell() + except AttributeError: + # In the case of HTTPDigestAuth being reused and the body of + # the previous request was a file-like object, pos has the + # file position of the previous body. Ensure it's set to + # None. + self._thread_local.pos = None + r.register_hook('response', self.handle_401) + r.register_hook('response', self.handle_redirect) + self._thread_local.num_401_calls = 1 + + return r + + def __eq__(self, other): + return all([ + self.username == getattr(other, 'username', None), + self.password == getattr(other, 'password', None) + ]) + + def __ne__(self, other): + return not self == other diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/cacert.pem b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/cacert.pem new file mode 100644 index 0000000..6a66daa --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/cacert.pem @@ -0,0 +1,5616 @@ + +# Issuer: O=Equifax OU=Equifax Secure Certificate Authority +# Subject: O=Equifax OU=Equifax Secure Certificate Authority +# Label: "Equifax Secure CA" +# Serial: 903804111 +# MD5 Fingerprint: 67:cb:9d:c0:13:24:8a:82:9b:b2:17:1e:d1:1b:ec:d4 +# SHA1 Fingerprint: d2:32:09:ad:23:d3:14:23:21:74:e4:0d:7f:9d:62:13:97:86:63:3a +# SHA256 Fingerprint: 08:29:7a:40:47:db:a2:36:80:c7:31:db:6e:31:76:53:ca:78:48:e1:be:bd:3a:0b:01:79:a7:07:f9:2c:f1:78 +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy +dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 +MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx +dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f +BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A +cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC +AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ +MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw +ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj +IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF +MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA +A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y +7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh +1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Label: "GlobalSign Root CA" +# Serial: 4835703278459707669005204 +# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a +# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c +# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Label: "GlobalSign Root CA - R2" +# Serial: 4835703278459682885658125 +# MD5 Fingerprint: 94:14:77:7e:3e:5e:fd:8f:30:bd:41:b0:cf:e7:d0:30 +# SHA1 Fingerprint: 75:e0:ab:b6:13:85:12:27:1c:04:f8:5f:dd:de:38:e4:b7:24:2e:fe +# SHA256 Fingerprint: ca:42:dd:41:74:5f:d0:b8:1e:b9:02:36:2c:f9:d8:bf:71:9d:a1:bd:1b:1e:fc:94:6f:5b:4c:99:f4:2c:1b:9e +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Label: "Verisign Class 3 Public Primary Certification Authority - G3" +# Serial: 206684696279472310254277870180966723415 +# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09 +# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6 +# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b +N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t +KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu +kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm +CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu +imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te +2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe +DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p +F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt +TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 4 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 4 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Label: "Verisign Class 4 Public Primary Certification Authority - G3" +# Serial: 314531972711909413743075096039378935511 +# MD5 Fingerprint: db:c8:f2:27:2e:b1:ea:6a:29:23:5d:fe:56:3e:33:df +# SHA1 Fingerprint: c8:ec:8c:87:92:69:cb:4b:ab:39:e9:8d:7e:57:67:f3:14:95:73:9d +# SHA256 Fingerprint: e3:89:36:0d:0f:db:ae:b3:d2:50:58:4b:47:30:31:4e:22:2f:39:c1:56:a0:20:14:4e:8d:96:05:61:79:15:06 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1 +GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ ++mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd +U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm +NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY +ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ +ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1 +CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq +g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c +2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/ +bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Premium 2048 Secure Server CA" +# Serial: 946069240 +# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 +# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 +# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Label: "Baltimore CyberTrust Root" +# Serial: 33554617 +# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 +# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 +# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Low-Value Services Root" +# Serial: 1 +# MD5 Fingerprint: 1e:42:95:02:33:92:6b:b9:5f:c0:7f:da:d6:b2:4b:fc +# SHA1 Fingerprint: cc:ab:0e:a0:4c:23:01:d6:69:7b:dd:37:9f:cd:12:eb:24:e3:94:9d +# SHA256 Fingerprint: 8c:72:09:27:9a:c0:4e:27:5e:16:d0:7f:d3:b7:75:e8:01:54:b5:96:80:46:e3:1f:52:dd:25:76:63:24:e9:a7 +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw +MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD +VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul +CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n +tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl +dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch +PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC ++Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O +BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl +MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk +ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X +7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz +43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl +pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA +WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Label: "AddTrust External Root" +# Serial: 1 +# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f +# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68 +# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2 +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs +IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 +MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h +bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt +H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 +uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX +mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX +a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN +E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 +WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD +VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 +Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU +cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx +IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN +AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH +YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC +Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX +c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a +mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Public Services Root" +# Serial: 1 +# MD5 Fingerprint: c1:62:3e:23:c5:82:73:9c:03:59:4b:2b:e9:77:49:7f +# SHA1 Fingerprint: 2a:b6:28:48:5e:78:fb:f3:ad:9e:79:10:dd:6b:df:99:72:2c:96:e5 +# SHA256 Fingerprint: 07:91:ca:07:49:b2:07:82:aa:d3:c7:d7:bd:0c:df:c9:48:58:35:84:3e:b2:d7:99:60:09:ce:43:ab:6c:69:27 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx +MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB +ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV +BAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV +6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX +GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP +dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH +1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF +62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW +BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL +MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU +cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv +b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6 +IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/ +iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh +4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm +XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Qualified Certificates Root" +# Serial: 1 +# MD5 Fingerprint: 27:ec:39:47:cd:da:5a:af:e2:9a:01:65:21:a9:4c:bb +# SHA1 Fingerprint: 4d:23:78:ec:91:95:39:b5:00:7f:75:8f:03:3b:21:1e:c5:4d:8b:cf +# SHA256 Fingerprint: 80:95:21:08:05:db:4b:bc:35:5e:44:28:d8:fd:6e:c2:cd:e3:ab:5f:b9:7a:99:42:98:8e:b8:f4:dc:d0:60:16 +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1 +MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK +EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh +BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq +xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G +87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i +2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U +WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1 +0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G +A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr +pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL +ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm +aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv +hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm +hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3 +P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y +iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no +xqE= +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Label: "Entrust Root Certification Authority" +# Serial: 1164660820 +# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 +# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 +# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +# Issuer: O=RSA Security Inc OU=RSA Security 2048 V3 +# Subject: O=RSA Security Inc OU=RSA Security 2048 V3 +# Label: "RSA Security 2048 v3" +# Serial: 13297492616345471454730593562152402946 +# MD5 Fingerprint: 77:0d:19:b1:21:fd:00:42:9c:3e:0c:a5:dd:0b:02:8e +# SHA1 Fingerprint: 25:01:90:19:cf:fb:d9:99:1c:b7:68:25:74:8d:94:5f:30:93:95:42 +# SHA256 Fingerprint: af:8b:67:62:a1:e5:28:22:81:61:a9:5d:5c:55:9e:e2:66:27:8f:75:d7:9e:83:01:89:a5:03:50:6a:bd:6b:4c +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6 +MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp +dHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAX +BgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAy +MDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt49VcdKA3Xtp +eafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7Jylg +/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGl +wSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnh +AMFRD0xS+ARaqn1y07iHKrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2 +PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpu +AWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NR +MKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYc +HnmYv/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/ +Zb5gEydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+ +f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVO +rSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEkllgVsRch +6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kApKnXwiJPZ9d3 +7CAFYd4= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Global CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Global CA O=GeoTrust Inc. +# Label: "GeoTrust Global CA" +# Serial: 144470 +# MD5 Fingerprint: f7:75:ab:29:fb:51:4e:b7:77:5e:ff:05:3c:99:8e:f5 +# SHA1 Fingerprint: de:28:f4:a4:ff:e5:b9:2f:a3:c5:03:d1:a3:49:a7:f9:96:2a:82:12 +# SHA256 Fingerprint: ff:85:6a:2d:25:1d:cd:88:d3:66:56:f4:50:12:67:98:cf:ab:aa:de:40:79:9c:72:2d:e4:d2:b5:db:36:a7:3a +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Global CA 2 O=GeoTrust Inc. +# Subject: CN=GeoTrust Global CA 2 O=GeoTrust Inc. +# Label: "GeoTrust Global CA 2" +# Serial: 1 +# MD5 Fingerprint: 0e:40:a7:6c:de:03:5d:8f:d1:0f:e4:d1:8d:f9:6c:a9 +# SHA1 Fingerprint: a9:e9:78:08:14:37:58:88:f2:05:19:b0:6d:2b:0d:2b:60:16:90:7d +# SHA256 Fingerprint: ca:2d:82:a0:86:77:07:2f:8a:b6:76:4f:f0:35:67:6c:fe:3e:5e:32:5e:01:21:72:df:3f:92:09:6d:b7:9b:85 +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs +IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A +PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8 +Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL +TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL +5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7 +S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe +2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap +EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td +EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv +/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN +A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0 +abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF +I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz +4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Label: "GeoTrust Universal CA" +# Serial: 1 +# MD5 Fingerprint: 92:65:58:8b:a2:1a:31:72:73:68:5c:b4:a5:7a:07:48 +# SHA1 Fingerprint: e6:21:f3:35:43:79:05:9a:4b:68:30:9d:8a:2f:74:22:15:87:ec:79 +# SHA256 Fingerprint: a0:45:9b:9f:63:b2:25:59:f5:fa:5d:4c:6d:b3:f9:f7:2f:f1:93:42:03:35:78:f0:73:bf:1d:1b:46:cb:b9:12 +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy +c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 +IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV +VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 +cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT +QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh +F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v +c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w +mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd +VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX +teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ +f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe +Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ +nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY +MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX +IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn +ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z +uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN +Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja +QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW +koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 +ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt +DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm +bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Label: "GeoTrust Universal CA 2" +# Serial: 1 +# MD5 Fingerprint: 34:fc:b8:d0:36:db:9e:14:b3:c2:f2:db:8f:e4:94:c7 +# SHA1 Fingerprint: 37:9a:19:7b:41:85:45:35:0c:a6:03:69:f3:3c:2e:af:47:4f:20:79 +# SHA256 Fingerprint: a0:23:4f:3b:c8:52:7c:a5:62:8e:ec:81:ad:5d:69:89:5d:a5:68:0d:c9:1d:1c:b8:47:7f:33:f8:78:b9:5b:0b +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy +c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD +VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 +c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 +WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG +FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq +XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL +se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb +KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd +IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 +y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt +hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc +QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 +Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV +HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ +KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ +L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr +Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo +ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY +T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz +GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m +1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV +OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH +6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX +QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +# Issuer: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association +# Subject: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association +# Label: "Visa eCommerce Root" +# Serial: 25952180776285836048024890241505565794 +# MD5 Fingerprint: fc:11:b8:d8:08:93:30:00:6d:23:f9:7e:eb:52:1e:02 +# SHA1 Fingerprint: 70:17:9b:86:8c:00:a4:fa:60:91:52:22:3f:9f:3e:32:bd:e0:05:62 +# SHA256 Fingerprint: 69:fa:c9:bd:55:fb:0a:c7:8d:53:bb:ee:5c:f1:d5:97:98:9f:d0:aa:ab:20:a2:51:51:bd:f1:73:3e:e7:d1:22 +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr +MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl +cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw +CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h +dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l +cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h +2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E +lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV +ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq +299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t +vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL +dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF +AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR +zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3 +LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd +7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw +++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +# Issuer: CN=Certum CA O=Unizeto Sp. z o.o. +# Subject: CN=Certum CA O=Unizeto Sp. z o.o. +# Label: "Certum Root CA" +# Serial: 65568 +# MD5 Fingerprint: 2c:8f:9f:66:1d:18:90:b1:47:26:9d:8e:86:82:8c:a9 +# SHA1 Fingerprint: 62:52:dc:40:f7:11:43:a2:2f:de:9e:f7:34:8e:06:42:51:b1:81:18 +# SHA256 Fingerprint: d8:e0:fe:bc:1d:b2:e3:8d:00:94:0f:37:d2:7d:41:34:4d:99:3e:73:4b:99:d5:65:6d:97:78:d4:d8:14:36:24 +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E +jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo +ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI +ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu +Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg +AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7 +HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA +uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa +TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg +xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q +CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x +O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs +6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +# Issuer: CN=AAA Certificate Services O=Comodo CA Limited +# Subject: CN=AAA Certificate Services O=Comodo CA Limited +# Label: "Comodo AAA Services root" +# Serial: 1 +# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 +# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 +# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +# Issuer: CN=Secure Certificate Services O=Comodo CA Limited +# Subject: CN=Secure Certificate Services O=Comodo CA Limited +# Label: "Comodo Secure Services root" +# Serial: 1 +# MD5 Fingerprint: d3:d9:bd:ae:9f:ac:67:24:b3:c8:1b:52:e1:b9:a9:bd +# SHA1 Fingerprint: 4a:65:d5:f4:1d:ef:39:b8:b8:90:4a:4a:d3:64:81:33:cf:c7:a1:d1 +# SHA256 Fingerprint: bd:81:ce:3b:4f:65:91:d1:1a:67:b5:fc:7a:47:fd:ef:25:52:1b:f9:aa:4e:18:b9:e3:df:2e:34:a7:80:3b:e8 +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp +ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow +fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV +BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM +cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S +HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996 +CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk +3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz +6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV +HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud +EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv +Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw +Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww +DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0 +5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI +gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ +aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl +izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk= +-----END CERTIFICATE----- + +# Issuer: CN=Trusted Certificate Services O=Comodo CA Limited +# Subject: CN=Trusted Certificate Services O=Comodo CA Limited +# Label: "Comodo Trusted Services root" +# Serial: 1 +# MD5 Fingerprint: 91:1b:3f:6e:cd:9e:ab:ee:07:fe:1f:71:d2:b3:61:27 +# SHA1 Fingerprint: e1:9f:e3:0e:8b:84:60:9e:80:9b:17:0d:72:a8:c5:ba:6e:14:09:bd +# SHA256 Fingerprint: 3f:06:e5:56:81:d4:96:f5:be:16:9e:b5:38:9f:9f:2b:8f:f6:1e:17:08:df:68:81:72:48:49:cd:5d:27:cb:69 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0 +aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla +MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO +BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD +VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW +fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt +TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL +fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW +1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7 +kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G +A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v +ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo +dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu +Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/ +HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS +jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+ +xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn +dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority +# Subject: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority +# Label: "QuoVadis Root CA" +# Serial: 985026699 +# MD5 Fingerprint: 27:de:36:fe:72:b7:00:03:00:9d:f4:f0:1e:6c:04:24 +# SHA1 Fingerprint: de:3f:40:bd:50:93:d3:9b:6c:60:f6:da:bc:07:62:01:00:89:76:c9 +# SHA256 Fingerprint: a4:5e:de:3b:bb:f0:9c:8a:e1:5c:72:ef:c0:72:68:d6:93:a2:1c:99:6f:d5:1e:67:ca:07:94:60:fd:6d:88:73 +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz +MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw +IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR +dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp +li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D +rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ +WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug +F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU +xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC +Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv +dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw +ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl +IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh +c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy +ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI +KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T +KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq +y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p +dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD +VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk +fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 +7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R +cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y +mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW +xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK +SnQ2+Q== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2" +# Serial: 1289 +# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b +# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7 +# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86 +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3" +# Serial: 1478 +# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf +# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85 +# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35 +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1 +# Subject: O=SECOM Trust.net OU=Security Communication RootCA1 +# Label: "Security Communication Root CA" +# Serial: 0 +# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a +# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7 +# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY +MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t +dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 +WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD +VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 +9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ +DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 +Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N +QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ +xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G +A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG +kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr +Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU +JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot +RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== +-----END CERTIFICATE----- + +# Issuer: CN=Sonera Class2 CA O=Sonera +# Subject: CN=Sonera Class2 CA O=Sonera +# Label: "Sonera Class 2 Root CA" +# Serial: 29 +# MD5 Fingerprint: a3:ec:75:0f:2e:88:df:fa:48:01:4e:0b:5c:48:6f:fb +# SHA1 Fingerprint: 37:f7:6d:e6:07:7c:90:c5:b1:3e:93:1a:b7:41:10:b4:f2:e4:9a:27 +# SHA256 Fingerprint: 79:08:b4:03:14:c1:38:10:0b:51:8d:07:35:80:7f:fb:fc:f8:51:8a:00:95:33:71:05:ba:38:6b:15:3d:d9:27 +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx +MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o +Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt +5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s +3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej +vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu +8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil +zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ +3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD +FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 +Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 +ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA" +# Serial: 10000010 +# MD5 Fingerprint: 60:84:7c:5a:ce:db:0c:d4:cb:a7:e9:fe:02:c6:a9:c0 +# SHA1 Fingerprint: 10:1d:fa:3f:d5:0b:cb:bb:9b:b5:60:0c:19:55:a4:1a:f4:73:3a:04 +# SHA256 Fingerprint: d4:1d:82:9e:8c:16:59:82:2a:f9:3f:ce:62:bf:fc:de:26:4f:c8:4e:8b:95:0c:5f:f2:75:d0:52:35:46:95:a3 +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJO +TDEeMBwGA1UEChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEy +MTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVk +ZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxhbmRlbiBSb290IENB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFtvszn +ExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw71 +9tV2U02PjLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MO +hXeiD+EwR+4A5zN9RGcaC1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+U +tFE5A3+y3qcym7RHjm+0Sq7lr7HcsBthvJly3uSJt3omXdozSVtSnA71iq3DuD3o +BmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn622r+I/q85Ej0ZytqERAh +SQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRVHSAAMDww +OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMv +cm9vdC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA +7Jbg0zTBLL9s+DANBgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k +/rvuFbQvBgwp8qiSpGEN/KtcCFtREytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzm +eafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbwMVcoEoJz6TMvplW0C5GUR5z6 +u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3ynGQI0DvDKcWy +7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR +iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== +-----END CERTIFICATE----- + +# Issuer: CN=UTN - DATACorp SGC O=The USERTRUST Network OU=http://www.usertrust.com +# Subject: CN=UTN - DATACorp SGC O=The USERTRUST Network OU=http://www.usertrust.com +# Label: "UTN DATACorp SGC Root CA" +# Serial: 91374294542884689855167577680241077609 +# MD5 Fingerprint: b3:a5:3e:77:21:6d:ac:4a:c0:c9:fb:d5:41:3d:ca:06 +# SHA1 Fingerprint: 58:11:9f:0e:12:82:87:ea:50:fd:d9:87:45:6f:4f:78:dc:fa:d6:d4 +# SHA256 Fingerprint: 85:fb:2f:91:dd:12:27:5a:01:45:b6:36:53:4f:84:02:4a:d6:8b:69:b8:ee:88:68:4f:f7:11:37:58:05:b3:48 +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB +kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw +IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD +VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu +dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 +E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ +D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK +4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq +lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW +bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB +o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT +MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js +LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr +BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB +AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj +j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH +KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv +2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 +mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +# Issuer: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com +# Subject: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com +# Label: "UTN USERFirst Hardware Root CA" +# Serial: 91374294542884704022267039221184531197 +# MD5 Fingerprint: 4c:56:41:e5:0d:bb:2b:e8:ca:a3:ed:18:08:ad:43:39 +# SHA1 Fingerprint: 04:83:ed:33:99:ac:36:08:05:87:22:ed:bc:5e:46:00:e3:be:f9:d7 +# SHA256 Fingerprint: 6e:a5:47:41:d0:04:66:7e:ed:1b:48:16:63:4a:a3:a7:9e:6e:4b:96:95:0f:82:79:da:fc:8d:9b:d8:81:21:37 +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB +lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt +SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG +A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe +MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v +d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh +cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn +0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ +M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a +MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd +oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI +DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy +oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 +dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy +bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF +BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli +CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE +CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t +3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS +KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== +-----END CERTIFICATE----- + +# Issuer: CN=Chambers of Commerce Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org +# Subject: CN=Chambers of Commerce Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org +# Label: "Camerfirma Chambers of Commerce Root" +# Serial: 0 +# MD5 Fingerprint: b0:01:ee:14:d9:af:29:18:94:76:8e:f1:69:33:2a:84 +# SHA1 Fingerprint: 6e:3a:55:a4:19:0c:19:5c:93:84:3c:c0:db:72:2e:31:30:61:f0:b1 +# SHA256 Fingerprint: 0c:25:8a:12:a5:67:4a:ef:25:f2:8b:a7:dc:fa:ec:ee:a3:48:e5:41:e6:f5:cc:4e:e6:3b:71:b3:61:60:6a:c3 +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn +MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL +ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg +b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa +MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB +ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw +IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B +AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb +unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d +BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq +7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3 +0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX +roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG +A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j +aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p +26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA +BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud +EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN +BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB +AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd +p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi +1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc +XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0 +eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu +tGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- + +# Issuer: CN=Global Chambersign Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org +# Subject: CN=Global Chambersign Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org +# Label: "Camerfirma Global Chambersign Root" +# Serial: 0 +# MD5 Fingerprint: c5:e6:7b:bf:06:d0:4f:43:ed:c4:7a:65:8a:fb:6b:19 +# SHA1 Fingerprint: 33:9b:6b:14:50:24:9b:55:7a:01:87:72:84:d9:e0:2f:c3:d2:d8:e9 +# SHA256 Fingerprint: ef:3c:b4:17:fc:8e:bf:6f:97:87:6c:9e:4e:ce:39:de:1e:a5:fe:64:91:41:d1:02:8b:7d:11:c0:b2:29:8c:ed +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn +MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL +ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo +YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9 +MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy +NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G +A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA +A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0 +Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s +QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV +eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795 +B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh +z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T +AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i +ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w +TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH +MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD +VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE +VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B +AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM +bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi +ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG +VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c +ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/ +AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Subject: CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Label: "NetLock Notary (Class A) Root" +# Serial: 259 +# MD5 Fingerprint: 86:38:6d:5e:49:63:6c:85:5c:db:6d:dc:94:b7:d0:f7 +# SHA1 Fingerprint: ac:ed:5f:65:53:fd:25:ce:01:5f:1f:7a:48:3b:6a:74:9f:61:78:c6 +# SHA256 Fingerprint: 7f:12:cd:5f:7e:5e:29:0e:c7:d8:51:79:d5:b7:2c:20:a5:be:75:08:ff:db:5b:f8:1a:b9:68:4a:7f:c9:f6:67 +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhV +MRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMe +TmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0 +dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBB +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0 +N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhC +dWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQu +MRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBL +b3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiD +zl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi +3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8 +WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LY +Oph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2Esi +NCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCC +ApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4 +QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0 +YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz +aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtm +ZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMg +ZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVs +amFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRv +IGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3 +Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6 +ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1 +YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg +dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRs +b2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0G +CSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQO +xmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP +0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQ +QeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxk +f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK +8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Label: "XRamp Global CA Root" +# Serial: 107108908803651509692980124233745014957 +# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1 +# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6 +# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2 +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Label: "Go Daddy Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 +# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 +# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Label: "Starfield Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 +# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a +# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +# Issuer: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing +# Subject: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing +# Label: "StartCom Certification Authority" +# Serial: 1 +# MD5 Fingerprint: 22:4d:8f:8a:fc:f7:35:c2:bb:57:34:90:7b:8b:22:16 +# SHA1 Fingerprint: 3e:2b:f7:f2:03:1b:96:f3:8c:e6:c4:d8:a8:5d:3e:2d:58:47:6a:0f +# SHA256 Fingerprint: c7:66:a9:be:f2:d4:07:1c:86:3a:31:aa:49:20:e8:13:b2:d1:98:60:8c:b7:b7:cf:e2:11:43:b8:36:df:09:ea +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j +ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js +LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM +BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy +dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh +cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh +YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg +dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp +bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ +YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT +TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ +9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8 +jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW +FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz +ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1 +ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L +EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu +L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC +O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V +um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh +NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= +-----END CERTIFICATE----- + +# Issuer: O=Government Root Certification Authority +# Subject: O=Government Root Certification Authority +# Label: "Taiwan GRCA" +# Serial: 42023070807708724159991140556527066870 +# MD5 Fingerprint: 37:85:44:53:32:45:1f:20:f0:f3:95:e1:25:c4:43:4e +# SHA1 Fingerprint: f4:8b:11:bf:de:ab:be:94:54:20:71:e6:41:de:6b:be:88:2b:40:b9 +# SHA256 Fingerprint: 76:00:29:5e:ef:e8:5b:9e:1f:d6:24:db:76:06:2a:aa:ae:59:81:8a:54:d2:77:4c:d4:c0:b2:c0:11:31:e1:b3 +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/ +MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow +PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR +IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q +gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy +yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts +F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2 +jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx +ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC +VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK +YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH +EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN +Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud +DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE +MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK +UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf +qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK +ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE +JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7 +hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1 +EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm +nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX +udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz +ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe +LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl +pYYsfPQS +-----END CERTIFICATE----- + +# Issuer: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services +# Subject: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services +# Label: "Swisscom Root CA 1" +# Serial: 122348795730808398873664200247279986742 +# MD5 Fingerprint: f8:38:7c:77:88:df:2c:16:68:2e:c2:e2:52:4b:b8:f9 +# SHA1 Fingerprint: 5f:3a:fc:0a:8b:64:f6:86:67:34:74:df:7e:a9:a2:fe:f9:fa:7a:51 +# SHA256 Fingerprint: 21:db:20:12:36:60:bb:2e:d4:18:20:5d:a1:1e:e7:a8:5a:65:e2:bc:6e:55:b5:af:7e:78:99:c8:a2:66:d9:2e +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBk +MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 +YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg +Q0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4MTgyMjA2MjBaMGQxCzAJBgNVBAYT +AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp +Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9 +m2BtRsiMMW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdih +FvkcxC7mlSpnzNApbjyFNDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/ +TilftKaNXXsLmREDA/7n29uj/x2lzZAeAR81sH8A25Bvxn570e56eqeqDFdvpG3F +EzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkCb6dJtDZd0KTeByy2dbco +kdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn7uHbHaBu +HYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNF +vJbNcA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo +19AOeCMgkckkKmUpWyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjC +L3UcPX7ape8eYIVpQtPM+GP+HkM5haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJW +bjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNYMUJDLXT5xp6mig/p/r+D5kNX +JLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw +FDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j +BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzc +K6FptWfUjNP9MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzf +ky9NfEBWMXrrpA9gzXrzvsMnjgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7Ik +Vh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQMbFamIp1TpBcahQq4FJHgmDmHtqB +sfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4HVtA4oJVwIHaM190e +3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtlvrsR +ls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ip +mXeascClOS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HH +b6D0jqTsNFFbjCYDcKF31QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksf +rK/7DZBaZmBwXarNeNQk7shBoJMBkpxqnvy5JMWzFYJ+vq6VK+uxwNrjAWALXmms +hFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCyx/yP2FS1k2Kdzs9Z+z0Y +zirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMWNY6E0F/6 +MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root CA" +# Serial: 17154717934120587862167794914071425081 +# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 +# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 +# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root CA" +# Serial: 10944719598952040374951832963794454346 +# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e +# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 +# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert High Assurance EV Root CA" +# Serial: 3553400076410547919724730734378100087 +# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a +# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 +# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +# Issuer: CN=Class 2 Primary CA O=Certplus +# Subject: CN=Class 2 Primary CA O=Certplus +# Label: "Certplus Class 2 Primary CA" +# Serial: 177770208045934040241468760488327595043 +# MD5 Fingerprint: 88:2c:8c:52:b8:a2:3c:f3:f7:bb:03:ea:ae:ac:42:0b +# SHA1 Fingerprint: 74:20:74:41:72:9c:dd:92:ec:79:31:d8:23:10:8d:c2:81:92:e2:bb +# SHA256 Fingerprint: 0f:99:3c:8a:ef:97:ba:af:56:87:14:0e:d5:9a:d1:82:1b:b4:af:ac:f0:aa:9a:58:b5:d5:7a:33:8a:3a:fb:cb +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw +PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz +cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9 +MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz +IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ +ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR +VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL +kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd +EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas +H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0 +HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud +DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4 +QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu +Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/ +AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8 +yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR +FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA +ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB +kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +# Issuer: CN=DST Root CA X3 O=Digital Signature Trust Co. +# Subject: CN=DST Root CA X3 O=Digital Signature Trust Co. +# Label: "DST Root CA X3" +# Serial: 91299735575339953335919266965803778155 +# MD5 Fingerprint: 41:03:52:dc:0f:f7:50:1b:16:f0:02:8e:ba:6f:45:c5 +# SHA1 Fingerprint: da:c9:02:4f:54:d8:f6:df:94:93:5f:b1:73:26:38:ca:6a:d7:7c:13 +# SHA256 Fingerprint: 06:87:26:03:31:a7:24:03:d9:09:f1:05:e6:9b:cf:0d:32:e1:bd:24:93:ff:c6:d9:20:6d:11:bc:d6:77:07:39 +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O +rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq +OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b +xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw +7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD +aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG +SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 +ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr +AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz +R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 +JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo +Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +# Issuer: CN=DST ACES CA X6 O=Digital Signature Trust OU=DST ACES +# Subject: CN=DST ACES CA X6 O=Digital Signature Trust OU=DST ACES +# Label: "DST ACES CA X6" +# Serial: 17771143917277623872238992636097467865 +# MD5 Fingerprint: 21:d8:4c:82:2b:99:09:33:a2:eb:14:24:8d:8e:5f:e8 +# SHA1 Fingerprint: 40:54:da:6f:1c:3f:40:74:ac:ed:0f:ec:cd:db:79:d1:53:fb:90:1d +# SHA256 Fingerprint: 76:7c:95:5a:76:41:2c:89:af:68:8e:90:a1:c7:0f:55:6c:fd:6b:60:25:db:ea:10:41:6d:7e:b6:83:1f:8c:40 +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx +ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w +MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD +VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx +FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu +ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7 +gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH +fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a +ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT +ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk +c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto +dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt +aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI +hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk +QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/ +h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR +rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2 +9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis= +-----END CERTIFICATE----- + +# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005 +# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005 +# Label: "TURKTRUST Certificate Services Provider Root 2" +# Serial: 1 +# MD5 Fingerprint: 37:a5:6e:d4:b1:25:84:97:b7:fd:56:15:7a:f9:a2:00 +# SHA1 Fingerprint: b4:35:d4:e1:11:9d:1c:66:90:a7:49:eb:b3:94:bd:63:7b:a7:82:b7 +# SHA256 Fingerprint: c4:70:cf:54:7e:23:02:b9:77:fb:29:dd:71:a8:9a:7b:6c:1f:60:77:7b:03:29:f5:60:17:f3:28:bf:4f:6b:e6 +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOc +UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xS +S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg +SGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcNMDUxMTA3MTAwNzU3 +WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVrdHJv +bmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJU +UjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSw +bGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWe +LiAoYykgS2FzxLFtIDIwMDUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqeLCDe2JAOCtFp0if7qnef +J1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKIx+XlZEdh +R3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJ +Qv2gQrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGX +JHpsmxcPbe9TmJEr5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1p +zpwACPI2/z7woQ8arBT9pmAPAgMBAAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58S +Fq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/nttRbj2hWyfIvwq +ECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 +Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFz +gw2lGh1uEpJ+hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotH +uFEJjOp9zYhys2AzsfAKRO8P9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LS +y3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5UrbnBEI= +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Label: "SwissSign Gold CA - G2" +# Serial: 13492815561806991280 +# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93 +# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61 +# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95 +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln +biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT +d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 +76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ +bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c +6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE +emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd +MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt +MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y +MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y +FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi +aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM +gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB +qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 +lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn +8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 +45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO +UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 +O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC +bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv +GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a +77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC +hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 +92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp +Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w +ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt +Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Label: "SwissSign Silver CA - G2" +# Serial: 5700383053117599563 +# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13 +# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb +# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5 +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Subject: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Label: "GeoTrust Primary Certification Authority" +# Serial: 32798226551256963324313806436981982369 +# MD5 Fingerprint: 02:26:c3:01:5e:08:30:37:43:a9:d0:7d:cf:37:e6:bf +# SHA1 Fingerprint: 32:3c:11:8e:1b:f7:b8:b6:52:54:e2:e2:10:0d:d6:02:90:37:f0:96 +# SHA256 Fingerprint: 37:d5:10:06:c5:12:ea:ab:62:64:21:f1:ec:8c:92:01:3f:c5:f8:2a:e9:8e:e5:33:eb:46:19:b8:de:b4:d0:6c +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx +MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 +AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA +ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 +7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W +kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI +mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ +KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 +6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl +4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K +oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj +UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU +AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA" +# Serial: 69529181992039203566298953787712940909 +# MD5 Fingerprint: 8c:ca:dc:0b:22:ce:f5:be:72:ac:41:1a:11:a8:d8:12 +# SHA1 Fingerprint: 91:c6:d6:ee:3e:8a:c8:63:84:e5:48:c2:99:29:5c:75:6c:81:7b:81 +# SHA256 Fingerprint: 8d:72:2f:81:a9:c1:13:c0:79:1d:f1:36:a2:96:6d:b2:6c:95:0a:97:1d:b4:6b:41:99:f4:ea:54:b7:8b:fb:9f +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G5" +# Serial: 33037644167568058970164719475676101450 +# MD5 Fingerprint: cb:17:e4:31:67:3e:e2:09:fe:45:57:93:f3:0a:fa:1c +# SHA1 Fingerprint: 4e:b6:d5:78:49:9b:1c:cf:5f:58:1e:ad:56:be:3d:9b:67:44:a5:e5 +# SHA256 Fingerprint: 9a:cf:ab:7e:43:c8:d8:80:d0:6b:26:2a:94:de:ee:e4:b4:65:99:89:c3:d0:ca:f1:9b:af:64:05:e4:1a:b7:df +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +# Issuer: CN=SecureTrust CA O=SecureTrust Corporation +# Subject: CN=SecureTrust CA O=SecureTrust Corporation +# Label: "SecureTrust CA" +# Serial: 17199774589125277788362757014266862032 +# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1 +# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11 +# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73 +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +# Issuer: CN=Secure Global CA O=SecureTrust Corporation +# Subject: CN=Secure Global CA O=SecureTrust Corporation +# Label: "Secure Global CA" +# Serial: 9751836167731051554232119481456978597 +# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de +# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b +# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69 +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO Certification Authority O=COMODO CA Limited +# Label: "COMODO Certification Authority" +# Serial: 104350513648249232941998508985834464573 +# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 +# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b +# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- + +# Issuer: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. +# Subject: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. +# Label: "Network Solutions Certificate Authority" +# Serial: 116697915152937497490437556386812487904 +# MD5 Fingerprint: d3:f3:a6:16:c0:fa:6b:1d:59:b1:2d:96:4d:0e:11:2e +# SHA1 Fingerprint: 74:f8:a3:c3:ef:e7:b3:90:06:4b:83:90:3c:21:64:60:20:e5:df:ce +# SHA256 Fingerprint: 15:f0:ba:00:a3:ac:7a:f3:ac:88:4c:07:2b:10:11:a0:77:bd:77:c0:97:f4:01:64:b2:f8:59:8a:bd:83:86:0c +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi +MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV +UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO +ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz +c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP +OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl +mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF +BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4 +qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw +gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu +bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp +dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8 +6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/ +h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH +/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN +pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +# Issuer: CN=WellsSecure Public Root Certificate Authority O=Wells Fargo WellsSecure OU=Wells Fargo Bank NA +# Subject: CN=WellsSecure Public Root Certificate Authority O=Wells Fargo WellsSecure OU=Wells Fargo Bank NA +# Label: "WellsSecure Public Root Certificate Authority" +# Serial: 1 +# MD5 Fingerprint: 15:ac:a5:c2:92:2d:79:bc:e8:7f:cb:67:ed:02:cf:36 +# SHA1 Fingerprint: e7:b4:f6:9d:61:ec:90:69:db:7e:90:a7:40:1a:3c:f4:7d:4f:e8:ee +# SHA256 Fingerprint: a7:12:72:ae:aa:a3:cf:e8:72:7f:7f:b3:9f:0f:b3:d1:e5:42:6e:90:60:b0:6e:e6:f1:3e:9a:3c:58:33:cd:43 +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMx +IDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxs +cyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9v +dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcxMjEzMTcwNzU0WhcNMjIxMjE0 +MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdl +bGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQD +DC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+r +WxxTkqxtnt3CxC5FlAM1iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjU +Dk/41itMpBb570OYj7OeUt9tkTmPOL13i0Nj67eT/DBMHAGTthP796EfvyXhdDcs +HqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8bJVhHlfXBIEyg1J55oNj +z7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiBK0HmOFaf +SZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/Slwxl +AgMBAAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqG +KGh0dHA6Ly9jcmwucGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0P +AQH/BAQDAgHGMB0GA1UdDgQWBBQmlRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0j +BIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGBi6SBiDCBhTELMAkGA1UEBhMC +VVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNX +ZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEB +ALkVsUSRzCPIK0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd +/ZDJPHV3V3p9+N701NX3leZ0bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pB +A4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSljqHyita04pO2t/caaH/+Xc/77szWn +k4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+esE2fDbbFwRnzVlhE9 +iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJtylv +2G0xffX8oRAHh84vWdw+WNs= +-----END CERTIFICATE----- + +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Issuer: CN=IGC/A O=PM/SGDN OU=DCSSI +# Subject: CN=IGC/A O=PM/SGDN OU=DCSSI +# Label: "IGC/A" +# Serial: 245102874772 +# MD5 Fingerprint: 0c:7f:dd:6a:f4:2a:b9:c8:9b:bd:20:7e:a9:db:5c:37 +# SHA1 Fingerprint: 60:d6:89:74:b5:c2:65:9e:8a:0f:c1:88:7c:88:d2:46:69:1b:18:2c +# SHA256 Fingerprint: b9:be:a7:86:0a:96:2e:a3:61:1d:ab:97:ab:6d:a3:e2:1c:10:68:b9:7d:55:57:5e:d0:e1:12:79:c1:1c:89:32 +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYT +AkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQ +TS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG +9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMB4XDTAyMTIxMzE0MjkyM1oXDTIw +MTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAM +BgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEO +MAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2 +LmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaI +s9z4iPf930Pfeo2aSVz2TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2 +xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCWSo7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4 +u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYyHF2fYPepraX/z9E0+X1b +F8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNdfrGoRpAx +Vs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGd +PDPQtQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNV +HSAEDjAMMAoGCCqBegF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAx +NjAfBgNVHSMEGDAWgBSjBS8YYFDCiQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUF +AAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RKq89toB9RlPhJy3Q2FLwV3duJ +L92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3QMZsyK10XZZOY +YLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg +Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2a +NjSaTFR+FwNIlQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R +0982gaEbeC9xs/FZTEYYKKuF0mBWWg== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication EV RootCA1 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication EV RootCA1 +# Label: "Security Communication EV RootCA1" +# Serial: 0 +# MD5 Fingerprint: 22:2d:a6:01:ea:7c:0a:f7:f0:6c:56:43:3f:77:76:d3 +# SHA1 Fingerprint: fe:b8:c4:32:dc:f9:76:9a:ce:ae:3d:d8:90:8f:fd:28:86:65:64:7d +# SHA256 Fingerprint: a2:2d:ba:68:1e:97:37:6e:2d:39:7d:72:8a:ae:3a:9b:62:96:b9:fd:ba:60:bc:2e:11:f6:47:f2:c6:75:fb:37 +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMh +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIz +MloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09N +IFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNlY3VyaXR5IENvbW11 +bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSE +RMqm4miO/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gO +zXppFodEtZDkBp2uoQSXWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5 +bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4zZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDF +MxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4bepJz11sS6/vmsJWXMY1 +VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK9U2vP9eC +OKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HW +tWS3irO4G8za+6xmiEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZ +q51ihPZRwSzJIxXYKLerJRO1RuGGAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDb +EJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnWmHyojf6GPgcWkuF75x3sM3Z+ +Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEWT1MKZPlO9L9O +VL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GA CA" +# Serial: 86718877871133159090080555911823548314 +# MD5 Fingerprint: bc:6c:51:33:a7:e9:d3:66:63:54:15:72:1b:21:92:93 +# SHA1 Fingerprint: 59:22:a1:e1:5a:ea:16:35:21:f8:98:39:6a:46:46:b0:44:1b:0f:a9 +# SHA256 Fingerprint: 41:c9:23:86:6a:b4:ca:d6:b7:ad:57:80:81:58:2e:02:07:97:a6:cb:df:4f:ff:78:ce:83:96:b3:89:37:d7:f5 +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB +ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly +aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w +NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G +A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX +SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR +VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2 +w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF +mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg +4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9 +4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw +EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx +SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2 +ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8 +vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi +Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ +/L7fCg0= +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA O=Microsec Ltd. OU=e-Szigno CA +# Subject: CN=Microsec e-Szigno Root CA O=Microsec Ltd. OU=e-Szigno CA +# Label: "Microsec e-Szigno Root CA" +# Serial: 272122594155480254301341951808045322001 +# MD5 Fingerprint: f0:96:b6:2f:c5:10:d5:67:8e:83:25:32:e8:5e:2e:e5 +# SHA1 Fingerprint: 23:88:c9:d3:71:cc:9e:96:3d:ff:7d:3c:a7:ce:fc:d6:25:ec:19:0d +# SHA256 Fingerprint: 32:7a:3d:76:1a:ba:de:a0:34:eb:99:84:06:27:5c:b1:a4:77:6e:fd:ae:2f:df:6d:01:68:ea:1c:4f:55:67:d0 +-----BEGIN CERTIFICATE----- +MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAw +cjELMAkGA1UEBhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNy +b3NlYyBMdGQuMRQwEgYDVQQLEwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9z +ZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0MDYxMjI4NDRaFw0xNzA0MDYxMjI4 +NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEWMBQGA1UEChMN +TWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMTGU1p +Y3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2u +uO/TEdyB5s87lozWbxXGd36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+ +LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/NoqdNAoI/gqyFxuEPkEeZlApxcpMqyabA +vjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjcQR/Ji3HWVBTji1R4P770 +Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJPqW+jqpx +62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcB +AQRbMFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3Aw +LQYIKwYBBQUHMAKGIWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAP +BgNVHRMBAf8EBTADAQH/MIIBcwYDVR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIB +AQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3LmUtc3ppZ25vLmh1L1NaU1ov +MIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0AdAB2AOEAbgB5 +ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn +AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABT +AHoAbwBsAGcA4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABh +ACAAcwB6AGUAcgBpAG4AdAAgAGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABo +AHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMAegBpAGcAbgBvAC4AaAB1AC8AUwBa +AFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6Ly93d3cuZS1zemln +bm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NOPU1p +Y3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxP +PU1pY3Jvc2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZv +Y2F0aW9uTGlzdDtiaW5hcnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuB +EGluZm9AZS1zemlnbm8uaHWkdzB1MSMwIQYDVQQDDBpNaWNyb3NlYyBlLVN6aWdu +w7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhTWjEWMBQGA1UEChMNTWlj +cm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhVMIGsBgNV +HSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJI +VTERMA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDAS +BgNVBAsTC2UtU3ppZ25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBS +b290IENBghEAzLjnv04pGv2i3GalHCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS +8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMTnGZjWS7KXHAM/IO8VbH0jgds +ZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FEaGAHQzAxQmHl +7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a +86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfR +hUZLphK3dehKyVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/ +MPMMNz7UwiiAc7EBt51alhQBS6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= +-----END CERTIFICATE----- + +# Issuer: CN=Certigna O=Dhimyotis +# Subject: CN=Certigna O=Dhimyotis +# Label: "Certigna" +# Serial: 18364802974209362175 +# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff +# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97 +# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +# Issuer: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center +# Subject: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center +# Label: "Deutsche Telekom Root CA 2" +# Serial: 38 +# MD5 Fingerprint: 74:01:4a:91:b1:08:c4:58:ce:47:cd:f0:dd:11:53:08 +# SHA1 Fingerprint: 85:a4:08:c0:9c:19:3e:5d:51:58:7d:cd:d6:13:30:fd:8c:de:37:bf +# SHA256 Fingerprint: b6:19:1a:50:d0:c3:97:7f:7d:a9:9b:cd:aa:c8:6a:22:7d:ae:b9:67:9e:c7:0b:a3:b0:c9:d9:22:71:c1:70:d3 +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc +MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj +IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB +IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE +RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl +U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290 +IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU +ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC +QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr +rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S +NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc +QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH +txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP +BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp +tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa +IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl +6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+ +xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc +# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc +# Label: "Cybertrust Global Root" +# Serial: 4835703278459682877484360 +# MD5 Fingerprint: 72:e4:4a:87:e3:69:40:80:77:ea:bc:e3:f4:ff:f0:e1 +# SHA1 Fingerprint: 5f:43:e5:b1:bf:f8:78:8c:ac:1c:c7:ca:4a:9a:c6:22:2b:cc:34:c6 +# SHA256 Fingerprint: 96:0a:df:00:63:e9:63:56:75:0c:29:65:dd:0a:08:67:da:0b:9c:bd:6e:77:71:4a:ea:fb:23:49:ab:39:3d:a3 +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG +A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh +bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE +ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS +b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5 +7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS +J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y +HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP +t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz +FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY +XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw +hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js +MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA +A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj +Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx +XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o +omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc +A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Label: "ePKI Root Certification Authority" +# Serial: 28956088682735189655030529057352760477 +# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3 +# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0 +# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5 +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +# Issuer: CN=TÜBİTAK UEKAE Kök Sertifika Hizmet Sağlayıcısı - Sürüm 3 O=Türkiye Bilimsel ve Teknolojik Araştırma Kurumu - TÜBİTAK OU=Ulusal Elektronik ve Kriptoloji Araştırma Enstitüsü - UEKAE/Kamu Sertifikasyon Merkezi +# Subject: CN=TÜBİTAK UEKAE Kök Sertifika Hizmet Sağlayıcısı - Sürüm 3 O=Türkiye Bilimsel ve Teknolojik Araştırma Kurumu - TÜBİTAK OU=Ulusal Elektronik ve Kriptoloji Araştırma Enstitüsü - UEKAE/Kamu Sertifikasyon Merkezi +# Label: "T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3" +# Serial: 17 +# MD5 Fingerprint: ed:41:f5:8c:50:c5:2b:9c:73:e6:ee:6c:eb:c2:a8:26 +# SHA1 Fingerprint: 1b:4b:39:61:26:27:6b:64:91:a2:68:6d:d7:02:43:21:2d:1f:1d:96 +# SHA256 Fingerprint: e4:c7:34:30:d7:a5:b5:09:25:df:43:37:0a:0d:21:6e:9a:79:b9:d6:db:83:73:a0:c6:9e:b1:cc:31:c7:c5:2a +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRS +MRgwFgYDVQQHDA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJp +bGltc2VsIHZlIFRla25vbG9qaWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSw +VEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ryb25payB2ZSBLcmlwdG9sb2ppIEFy +YcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNVBAsMGkthbXUgU2Vy +dGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUgS8O2 +ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAe +Fw0wNzA4MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIx +GDAWBgNVBAcMD0dlYnplIC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmls +aW1zZWwgdmUgVGVrbm9sb2ppayBBcmHFn3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBU +QUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZlIEtyaXB0b2xvamkgQXJh +xZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2FtdSBTZXJ0 +aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7Zr +IFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4h +gb46ezzb8R1Sf1n68yJMlaCQvEhOEav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yK +O7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1xnnRFDDtG1hba+818qEhTsXO +fJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR6Oqeyjh1jmKw +lZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQID +AQABo0IwQDAdBgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmP +NOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4N5EY3ATIZJkrGG2AA1nJrvhY0D7t +wyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLTy9LQQfMmNkqblWwM +7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYhLBOh +gLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5n +oN+J1q2MdqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUs +yZyQ2uypQjyttgI= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 CA 1 O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 CA 1 O=Buypass AS-983163327 +# Label: "Buypass Class 2 CA 1" +# Serial: 1 +# MD5 Fingerprint: b8:08:9a:f0:03:cc:1b:0d:c8:6c:0b:76:a1:75:64:23 +# SHA1 Fingerprint: a0:a1:ab:90:c9:fc:84:7b:3b:12:61:e8:97:7d:5f:d3:22:61:d3:cc +# SHA256 Fingerprint: 0f:4e:9c:dd:26:4b:02:55:50:d1:70:80:63:40:21:4f:e9:44:34:c9:b0:2f:69:7e:c7:10:fc:5f:ea:fb:5e:38 +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg +Q2xhc3MgMiBDQSAxMB4XDTA2MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzEL +MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD +VQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7McXA0 +ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLX +l18xoS830r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVB +HfCuuCkslFJgNJQ72uA40Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B +5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/RuFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3 +WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0PAQH/BAQD +AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLP +gcIV1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+ +DKhQ7SLHrQVMdvvt7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKu +BctN518fV4bVIJwo+28TOPX2EZL2fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHs +h7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5wwDX3OaJdZtB7WZ+oRxKaJyOk +LY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho +-----END CERTIFICATE----- + +# Issuer: CN=EBG Elektronik Sertifika Hizmet Sağlayıcısı O=EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. +# Subject: CN=EBG Elektronik Sertifika Hizmet Sağlayıcısı O=EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. +# Label: "EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1" +# Serial: 5525761995591021570 +# MD5 Fingerprint: 2c:20:26:9d:cb:1a:4a:00:85:b5:b7:5a:ae:c2:01:37 +# SHA1 Fingerprint: 8c:96:ba:eb:dd:2b:07:07:48:ee:30:32:66:a0:f3:98:6e:7c:ae:58 +# SHA256 Fingerprint: 35:ae:5b:dd:d8:f7:ae:63:5c:ff:ba:56:82:a8:f0:0b:95:f4:84:62:c7:10:8e:e9:a0:e5:29:2b:07:4a:af:b2 +-----BEGIN CERTIFICATE----- +MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNV +BAMML0VCRyBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMTcwNQYDVQQKDC5FQkcgQmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXpt +ZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAeFw0wNjA4MTcwMDIxMDlaFw0xNjA4 +MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25payBTZXJ0aWZpa2Eg +SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2ltIFRl +a25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h +4fuXd7hxlugTlkaDT7byX3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAk +tiHq6yOU/im/+4mRDGSaBUorzAzu8T2bgmmkTPiab+ci2hC6X5L8GCcKqKpE+i4s +tPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfreYteIAbTdgtsApWjluTL +dlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZTqNGFav4 +c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8Um +TDGyY5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z ++kI2sSXFCjEmN1ZnuqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0O +Lna9XvNRiYuoP1Vzv9s6xiQFlpJIqkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMW +OeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vmExH8nYQKE3vwO9D8owrXieqW +fo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0Nokb+Clsi7n2 +l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgw +FoAU587GT/wWZ5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+ +8ygjdsZs93/mQJ7ANtyVDR2tFcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI +6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgmzJNSroIBk5DKd8pNSe/iWtkqvTDO +TLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64kXPBfrAowzIpAoHME +wfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqTbCmY +Iai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJn +xk1Gj7sURT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4Q +DgZxGhBM/nV+/x5XOULK1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9q +Kd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11t +hie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQY9iJSrSq3RZj9W6+YKH4 +7ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9AahH3eU7 +QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT +-----END CERTIFICATE----- + +# Issuer: O=certSIGN OU=certSIGN ROOT CA +# Subject: O=certSIGN OU=certSIGN ROOT CA +# Label: "certSIGN ROOT CA" +# Serial: 35210227249154 +# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17 +# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b +# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +# Issuer: CN=CNNIC ROOT O=CNNIC +# Subject: CN=CNNIC ROOT O=CNNIC +# Label: "CNNIC ROOT" +# Serial: 1228079105 +# MD5 Fingerprint: 21:bc:82:ab:49:c4:13:3b:4b:b2:2b:5c:6b:90:9c:19 +# SHA1 Fingerprint: 8b:af:4c:9b:1d:f0:2a:92:f7:da:12:8e:b9:1b:ac:f4:98:60:4b:6f +# SHA256 Fingerprint: e2:83:93:77:3d:a8:45:a6:79:f2:08:0c:c7:fb:44:a3:b7:a1:c3:79:2c:b7:eb:77:29:fd:cb:6a:8d:99:ae:a7 +-----BEGIN CERTIFICATE----- +MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJD +TjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2 +MDcwOTE0WhcNMjcwNDE2MDcwOTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMF +Q05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzDo+/hn7E7SIX1mlwh +IhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tizVHa6 +dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZO +V/kbZKKTVrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrC +GHn2emU1z5DrvTOTn1OrczvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gN +v7Sg2Ca+I19zN38m5pIEo3/PIKe38zrKy5nLAgMBAAGjczBxMBEGCWCGSAGG+EIB +AQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscCwQ7vptU7ETAPBgNVHRMB +Af8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991SlgrHAsEO +76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnK +OOK5Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvH +ugDnuL8BV8F3RTIMO/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7Hgvi +yJA/qIYM/PmLXoXLT1tLYhFHxUV8BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fL +buXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2G8kS1sHNzYDzAgE8yGnLRUhj +2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5mmxE= +-----END CERTIFICATE----- + +# Issuer: O=Japanese Government OU=ApplicationCA +# Subject: O=Japanese Government OU=ApplicationCA +# Label: "ApplicationCA - Japanese Government" +# Serial: 49 +# MD5 Fingerprint: 7e:23:4e:5b:a7:a5:b4:25:e9:00:07:74:11:62:ae:d6 +# SHA1 Fingerprint: 7f:8a:b0:cf:d0:51:87:6a:66:f3:36:0f:47:c8:8d:8c:d3:35:fc:74 +# SHA256 Fingerprint: 2d:47:43:7d:e1:79:51:21:5a:12:f3:c5:8e:51:c7:29:a5:80:26:ef:1f:cc:0a:5f:b3:d9:dc:01:2f:60:0d:19 +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEc +MBoGA1UEChMTSmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRp +b25DQTAeFw0wNzEyMTIxNTAwMDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYT +AkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zlcm5tZW50MRYwFAYDVQQLEw1BcHBs +aWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp23gdE6H +j6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4fl+K +f5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55 +IrmTwcrNwVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cw +FO5cjFW6WY2H/CPek9AEjP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDiht +QWEjdnjDuGWk81quzMKq2edY3rZ+nYVunyoKb58DKTCXKB28t89UKU5RMfkntigm +/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRUWssmP3HMlEYNllPqa0jQ +k/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNVBAYTAkpQ +MRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOC +seODvOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADlqRHZ3ODrso2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJ +hyzjVOGjprIIC8CFqMjSnHH2HZ9g/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+ +eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYDio+nEhEMy/0/ecGc/WLuo89U +DNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmWdupwX3kSa+Sj +B1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL +rosot4LKGAfmt1t06SAZf7IbiVQ= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G3" +# Serial: 28809105769928564313984085209975885599 +# MD5 Fingerprint: b5:e8:34:36:c9:10:44:58:48:70:6d:2e:83:d4:b8:05 +# SHA1 Fingerprint: 03:9e:ed:b8:0b:e7:a0:3c:69:53:89:3b:20:d2:d9:32:3a:4c:2a:fd +# SHA256 Fingerprint: b4:78:b8:12:25:0d:f8:78:63:5c:2a:a7:ec:7d:15:5e:aa:62:5e:e8:29:16:e2:cd:29:43:61:88:6c:d1:fb:d4 +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ +BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 +BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz ++uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm +hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn +5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W +JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL +DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC +huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB +AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB +zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN +kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH +SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G +spki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G2" +# Serial: 71758320672825410020661621085256472406 +# MD5 Fingerprint: 74:9d:ea:60:24:c4:fd:22:53:3e:cc:3a:72:d9:29:4f +# SHA1 Fingerprint: aa:db:bc:22:23:8f:c4:01:a1:27:bb:38:dd:f4:1d:db:08:9e:f0:12 +# SHA256 Fingerprint: a4:31:0d:50:af:18:a6:44:71:90:37:2a:86:af:af:8b:95:1f:fb:43:1d:83:7f:1e:56:88:b4:59:71:ed:15:57 +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp +IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi +BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw +MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig +YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v +dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ +BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 +papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K +DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 +KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox +XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G3" +# Serial: 127614157056681299805556476275995414779 +# MD5 Fingerprint: fb:1b:5d:43:8a:94:cd:44:c6:76:f2:43:4b:47:e7:31 +# SHA1 Fingerprint: f1:8b:53:8d:1b:e9:03:b6:a6:f0:56:43:5b:17:15:89:ca:f3:6b:f2 +# SHA256 Fingerprint: 4b:03:f4:58:07:ad:70:f2:1b:fc:2c:ae:71:c9:fd:e4:60:4c:06:4c:f5:ff:b6:86:ba:e5:db:aa:d7:fd:d3:4c +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa +Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl +LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u +MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm +gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 +YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf +b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 +9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S +zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk +OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA +2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW +oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c +KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM +m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu +MdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G2" +# Serial: 80682863203381065782177908751794619243 +# MD5 Fingerprint: 01:5e:d8:6b:bd:6f:3d:8e:a1:31:f8:12:e0:98:73:6a +# SHA1 Fingerprint: 8d:17:84:d5:37:f3:03:7d:ec:70:fe:57:8b:51:9a:99:e6:10:d7:b0 +# SHA256 Fingerprint: 5e:db:7a:c4:3b:82:a0:6a:87:61:e8:d7:be:49:79:eb:f2:61:1f:7d:d7:9b:f9:1c:1c:6b:56:6a:21:9e:d7:66 +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL +MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj +KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 +MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw +NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV +BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL +So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal +tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG +CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT +qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz +rD6ogRLQy7rQkgu2npaqBA+K +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Universal Root Certification Authority" +# Serial: 85209574734084581917763752644031726877 +# MD5 Fingerprint: 8e:ad:b5:01:aa:4d:81:e4:8c:1d:d1:e1:14:00:95:19 +# SHA1 Fingerprint: 36:79:ca:35:66:87:72:30:4d:30:a5:fb:87:3b:0f:a7:7b:b7:0d:54 +# SHA256 Fingerprint: 23:99:56:11:27:a5:71:25:de:8c:ef:ea:61:0d:df:2f:a0:78:b5:c8:06:7f:4e:82:82:90:bf:b8:60:e8:4b:3c +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB +vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W +ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 +IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y +IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh +bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF +9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH +H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H +LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN +/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT +rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw +WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs +exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 +sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ +seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz +4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ +BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR +lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 +7M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G4" +# Serial: 63143484348153506665311985501458640051 +# MD5 Fingerprint: 3a:52:e1:e7:fd:6f:3a:e3:6f:f3:6f:99:1b:f9:22:41 +# SHA1 Fingerprint: 22:d5:d8:df:8f:02:31:d1:8d:f7:9d:b7:cf:8a:2d:64:c9:3f:6c:3a +# SHA256 Fingerprint: 69:dd:d7:ea:90:bb:57:c9:3e:13:5d:c8:5e:a6:fc:d5:48:0b:60:32:39:bd:c4:54:fc:75:8b:2a:26:cf:7f:79 +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp +U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg +SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln +biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm +GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve +fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ +aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj +aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW +kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC +4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga +FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Arany (Class Gold) Főtanúsítvány O=NetLock Kft. OU=Tanúsítványkiadók (Certification Services) +# Subject: CN=NetLock Arany (Class Gold) Főtanúsítvány O=NetLock Kft. OU=Tanúsítványkiadók (Certification Services) +# Label: "NetLock Arany (Class Gold) Főtanúsítvány" +# Serial: 80544274841616 +# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 +# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 +# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA - G2" +# Serial: 10000012 +# MD5 Fingerprint: 7c:a5:0f:f8:5b:9a:7d:6d:30:ae:54:5a:e3:42:a2:8a +# SHA1 Fingerprint: 59:af:82:79:91:86:c7:b4:75:07:cb:cf:03:57:46:eb:04:dd:b7:16 +# SHA256 Fingerprint: 66:8c:83:94:7d:a6:3b:72:4b:ec:e1:74:3c:31:a0:e6:ae:d0:db:8e:c5:b3:1b:e3:77:bb:78:4f:91:b6:71:6f +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX +DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 +qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp +uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU +Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE +pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp +5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M +UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN +GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy +5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv +6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK +eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 +B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ +BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov +L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG +SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS +CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen +5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 +IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK +gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL ++63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL +vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm +bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk +N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC +Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z +ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig O=Disig a.s. +# Subject: CN=CA Disig O=Disig a.s. +# Label: "CA Disig" +# Serial: 1 +# MD5 Fingerprint: 3f:45:96:39:e2:50:87:f7:bb:fe:98:0c:3c:20:98:e6 +# SHA1 Fingerprint: 2a:c8:d5:8b:57:ce:bf:2f:49:af:f2:fc:76:8f:51:14:62:90:7a:41 +# SHA256 Fingerprint: 92:bf:51:19:ab:ec:ca:d0:b1:33:2d:c4:e1:d0:5f:ba:75:b5:67:90:44:ee:0c:a2:6e:93:1f:74:4f:2f:33:cf +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzET +MBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UE +AxMIQ0EgRGlzaWcwHhcNMDYwMzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQsw +CQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcg +YS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgmGErE +Nx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnX +mjxUizkDPw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYD +XcDtab86wYqg6I7ZuUUohwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhW +S8+2rT+MitcE5eN4TPWGqvWP+j1scaMtymfraHtuM6kMgiioTGohQBUgDCZbg8Kp +FhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8wgfwwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0PAQH/BAQD +AgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cu +ZGlzaWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5z +ay9jYS9jcmwvY2FfZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2sv +Y2EvY3JsL2NhX2Rpc2lnLmNybDAaBgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEw +DQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59tWDYcPQuBDRIrRhCA/ec8J9B6 +yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3mkkp7M5+cTxq +EEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ +CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeB +EicTXxChds6KezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFN +PGO+I++MzVpQuGhU+QqZMxEA4Z7CRneC9VkGjCFMhwnN5ag= +-----END CERTIFICATE----- + +# Issuer: CN=Juur-SK O=AS Sertifitseerimiskeskus +# Subject: CN=Juur-SK O=AS Sertifitseerimiskeskus +# Label: "Juur-SK" +# Serial: 999181308 +# MD5 Fingerprint: aa:8e:5d:d9:f8:db:0a:58:b7:8d:26:87:6c:82:35:55 +# SHA1 Fingerprint: 40:9d:4b:d9:17:b5:5c:27:b6:9b:64:cb:98:22:44:0d:cd:09:b8:89 +# SHA256 Fingerprint: ec:c3:e9:c3:40:75:03:be:e0:91:aa:95:2f:41:34:8f:f8:8b:aa:86:3b:22:64:be:fa:c8:07:90:15:74:e9:39 +-----BEGIN CERTIFICATE----- +MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcN +AQkBFglwa2lAc2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZp +dHNlZXJpbWlza2Vza3VzMRAwDgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMw +MVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMQsw +CQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEQ +MA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOB +SvZiF3tfTQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkz +ABpTpyHhOEvWgxutr2TC+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvH +LCu3GFH+4Hv2qEivbDtPL+/40UceJlfwUR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMP +PbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDaTpxt4brNj3pssAki14sL +2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQFMAMBAf8w +ggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwIC +MIHDHoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDk +AGwAagBhAHMAdABhAHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0 +AHMAZQBlAHIAaQBtAGkAcwBrAGUAcwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABz +AGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABrAGkAbgBuAGkAdABhAG0AaQBz +AGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nwcy8wKwYDVR0f +BCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE +FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcY +P2/v6X2+MA4GA1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOi +CfP+JmeaUOTDBS8rNXiRTHyoERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+g +kcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyLabVAyJRld/JXIWY7zoVAtjNjGr95 +HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678IIbsSt4beDI3poHS +na9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkhMp6q +qIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0Z +TbvGRNs2yyqcjg== +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Label: "Hongkong Post Root CA 1" +# Serial: 1000 +# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca +# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58 +# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2 +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx +FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg +Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG +A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr +b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ +jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn +PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh +ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 +nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h +q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED +MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC +mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 +7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB +oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs +EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO +fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi +AmvZWg== +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Label: "SecureSign RootCA11" +# Serial: 1 +# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26 +# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3 +# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr +MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG +A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 +MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp +Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD +QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz +i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 +h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV +MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 +UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni +8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC +h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm +KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ +X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr +QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 +pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN +QSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +# Issuer: CN=ACEDICOM Root O=EDICOM OU=PKI +# Subject: CN=ACEDICOM Root O=EDICOM OU=PKI +# Label: "ACEDICOM Root" +# Serial: 7029493972724711941 +# MD5 Fingerprint: 42:81:a0:e2:1c:e3:55:10:de:55:89:42:65:96:22:e6 +# SHA1 Fingerprint: e0:b4:32:2e:b2:f6:a5:68:b6:54:53:84:48:18:4a:50:36:87:43:84 +# SHA256 Fingerprint: 03:95:0f:b4:9a:53:1f:3e:19:91:94:23:98:df:a9:e0:ea:32:d7:ba:1c:dd:9b:c8:5d:b5:7e:d9:40:0b:43:4a +-----BEGIN CERTIFICATE----- +MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UE +AwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00x +CzAJBgNVBAYTAkVTMB4XDTA4MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEW +MBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZF +RElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHkWLn7 +09gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7 +XBZXehuDYAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5P +Grjm6gSSrj0RuVFCPYewMYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAK +t0SdE3QrwqXrIhWYENiLxQSfHY9g5QYbm8+5eaA9oiM/Qj9r+hwDezCNzmzAv+Yb +X79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbkHQl/Sog4P75n/TSW9R28 +MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTTxKJxqvQU +fecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI +2Sf23EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyH +K9caUPgn6C9D4zq92Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEae +ZAwUswdbxcJzbPEHXEUkFDWug/FqTYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAP +BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz4SsrSbbXc6GqlPUB53NlTKxQ +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU9QHnc2VMrFAw +RAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv +bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWIm +fQwng4/F9tqgaHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3 +gvoFNTPhNahXwOf9jU8/kzJPeGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKe +I6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1PwkzQSulgUV1qzOMPPKC8W64iLgpq0i +5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1ThCojz2GuHURwCRi +ipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oIKiMn +MCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZ +o5NjEFIqnxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6 +zqylfDJKZ0DcMDQj3dcEI2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacN +GHk0vFQYXlPKNFHtRQrmjseCNj6nOGOpMCwXEGCSn1WHElkQwg9naRHMTh5+Spqt +r0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3otkYNbn5XOmeUwssfnHdK +Z05phkOTOPu220+DkdRgfks+KzgHVZhepA== +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Label: "Microsec e-Szigno Root CA 2009" +# Serial: 14014712776195784473 +# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 +# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e +# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 6047274297262753887 +# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3 +# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa +# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy +MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD +VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv +ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl +AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF +661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 +am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 +ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 +PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS +3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k +SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF +3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM +ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g +StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz +Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB +jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +# Issuer: CN=Izenpe.com O=IZENPE S.A. +# Subject: CN=Izenpe.com O=IZENPE S.A. +# Label: "Izenpe.com" +# Serial: 917563065490389241595536686991402621 +# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 +# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 +# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +# Issuer: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. +# Subject: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. +# Label: "Chambers of Commerce Root - 2008" +# Serial: 11806822484801597146 +# MD5 Fingerprint: 5e:80:9e:84:5a:0e:65:0b:17:02:f3:55:18:2a:3e:d7 +# SHA1 Fingerprint: 78:6a:74:ac:76:ab:14:7f:9c:6a:30:50:ba:9e:a8:7e:fe:9a:ce:3c +# SHA256 Fingerprint: 06:3e:4a:fa:c4:91:df:d3:32:f3:08:9b:85:42:e9:46:17:d8:93:d7:fe:94:4e:10:a7:93:7e:e2:9d:96:93:c0 +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz +IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz +MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj +dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw +EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp +MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9 +28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq +VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q +DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR +5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL +ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a +Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl +UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s ++12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5 +Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx +hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV +HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1 ++HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN +YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t +L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy +ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt +IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV +HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w +DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW +PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF +5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1 +glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH +FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2 +pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD +xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG +tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq +jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De +fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ +d0jQ +-----END CERTIFICATE----- + +# Issuer: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. +# Subject: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. +# Label: "Global Chambersign Root - 2008" +# Serial: 14541511773111788494 +# MD5 Fingerprint: 9e:80:ff:78:01:0c:2e:c1:36:bd:fe:96:90:6e:08:f3 +# SHA1 Fingerprint: 4a:bd:ee:ec:95:0d:35:9c:89:ae:c7:52:a1:2c:5b:29:f6:d6:aa:0c +# SHA256 Fingerprint: 13:63:35:43:93:34:a7:69:80:16:a0:d3:24:de:72:28:4e:07:9d:7b:52:20:bb:8f:bd:74:78:16:ee:be:ba:ca +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx +MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy +cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG +A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl +BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed +KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 +G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 +zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 +ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG +HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 +Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V +yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e +beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r +6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog +zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW +BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr +ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp +ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk +cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt +YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC +CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow +KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI +hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ +UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz +X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x +fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz +a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd +Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd +SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O +AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso +M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge +v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Services Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 +# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f +# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Commercial O=AffirmTrust +# Subject: CN=AffirmTrust Commercial O=AffirmTrust +# Label: "AffirmTrust Commercial" +# Serial: 8608355977964138876 +# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 +# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 +# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Networking O=AffirmTrust +# Subject: CN=AffirmTrust Networking O=AffirmTrust +# Label: "AffirmTrust Networking" +# Serial: 8957382827206547757 +# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f +# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f +# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium O=AffirmTrust +# Subject: CN=AffirmTrust Premium O=AffirmTrust +# Label: "AffirmTrust Premium" +# Serial: 7893706540734352110 +# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 +# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 +# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust +# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust +# Label: "AffirmTrust Premium ECC" +# Serial: 8401224907861490260 +# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d +# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb +# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +# Issuer: CN=Certinomis - Autorité Racine O=Certinomis OU=0002 433998903 +# Subject: CN=Certinomis - Autorité Racine O=Certinomis OU=0002 433998903 +# Label: "Certinomis - Autorité Racine" +# Serial: 1 +# MD5 Fingerprint: 7f:30:78:8c:03:e3:ca:c9:0a:e2:c9:ea:1e:aa:55:1a +# SHA1 Fingerprint: 2e:14:da:ec:28:f0:fa:1e:8e:38:9a:4e:ab:eb:26:c0:0a:d3:83:c3 +# SHA256 Fingerprint: fc:bf:e2:88:62:06:f7:2b:27:59:3c:8b:07:02:97:e1:2d:76:9e:d1:0e:d7:93:07:05:a8:09:8e:ff:c1:4d:17 +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAk +BgNVBAMMHUNlcnRpbm9taXMgLSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4 +Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNl +cnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYwJAYDVQQDDB1DZXJ0 +aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jY +F1AMnmHawE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N +8y4oH3DfVS9O7cdxbwlyLu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWe +rP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K +/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92NjMD2AR5vpTESOH2VwnHu +7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9qc1pkIuVC +28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6 +lSTClrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1E +nn1So2+WLhl+HPNbxxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB +0iSVL1N6aaLwD4ZFjliCK0wi1F6g530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql09 +5gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna4NH4+ej9Uji29YnfAgMBAAGj +WzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQN +jLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9s +ov3/4gbIOZ/xWqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZM +OH8oMDX/nyNTt7buFHAAQCvaR6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q +619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40nJ+U8/aGH88bc62UeYdocMMzpXDn +2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1BCxMjidPJC+iKunqj +o3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjvJL1v +nxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG +5ERQL1TEqkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWq +pdEdnV1j6CTmNhTih60bWfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZb +dsLLO7XSAPCjDuGtbkD326C00EauFddEwk01+dIL8hf2rGbVJLJP0RyZwG71fet0 +BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- + +# Issuer: CN=Root CA Generalitat Valenciana O=Generalitat Valenciana OU=PKIGVA +# Subject: CN=Root CA Generalitat Valenciana O=Generalitat Valenciana OU=PKIGVA +# Label: "Root CA Generalitat Valenciana" +# Serial: 994436456 +# MD5 Fingerprint: 2c:8c:17:5e:b1:54:ab:93:17:b5:36:5a:db:d1:c6:f2 +# SHA1 Fingerprint: a0:73:e5:c5:bd:43:61:0d:86:4c:21:13:0a:85:58:57:cc:9c:ea:46 +# SHA256 Fingerprint: 8c:4e:df:d0:43:48:f3:22:96:9e:7e:29:a4:cd:4d:ca:00:46:55:06:1c:16:e1:b0:76:42:2e:f3:42:ad:63:0e +-----BEGIN CERTIFICATE----- +MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJF +UzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJ +R1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcN +MDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3WjBoMQswCQYDVQQGEwJFUzEfMB0G +A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScw +JQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+ +WmmmO3I2F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKj +SgbwJ/BXufjpTjJ3Cj9BZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGl +u6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQD0EbtFpKd71ng+CT516nDOeB0/RSrFOy +A8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXteJajCq+TA81yc477OMUxk +Hl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMBAAGjggM7 +MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBr +aS5ndmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIIC +IwYKKwYBBAG/VQIBADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8A +cgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIA +YQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIAYQBsAGkAdABhAHQAIABWAGEA +bABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQByAGEAYwBpAPMA +bgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA +aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMA +aQBvAG4AYQBtAGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQA +ZQAgAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEA +YwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBuAHQAcgBhACAAZQBuACAAbABhACAA +ZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAAOgAvAC8AdwB3AHcA +LgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0dHA6 +Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+y +eAT8MIGVBgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQsw +CQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0G +A1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVu +Y2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRhTvW1yEICKrNcda3Fbcrn +lD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdzCkj+IHLt +b8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg +9J63NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XF +ducTZnV+ZfsBn5OHiJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmC +IoaZM3Fa6hlXPZHNqcCjbgcTpsnt+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= +-----END CERTIFICATE----- + +# Issuer: CN=A-Trust-nQual-03 O=A-Trust Ges. f. Sicherheitssysteme im elektr. Datenverkehr GmbH OU=A-Trust-nQual-03 +# Subject: CN=A-Trust-nQual-03 O=A-Trust Ges. f. Sicherheitssysteme im elektr. Datenverkehr GmbH OU=A-Trust-nQual-03 +# Label: "A-Trust-nQual-03" +# Serial: 93214 +# MD5 Fingerprint: 49:63:ae:27:f4:d5:95:3d:d8:db:24:86:b8:9c:07:53 +# SHA1 Fingerprint: d3:c0:63:f2:19:ed:07:3e:34:ad:5d:75:0b:32:76:29:ff:d5:9a:f2 +# SHA256 Fingerprint: 79:3c:bf:45:59:b9:fd:e3:8a:b2:2d:f1:68:69:f6:98:81:ae:14:c4:b0:13:9a:c7:88:a7:8a:1a:fc:ca:02:fb +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJB +VDFIMEYGA1UECgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBp +bSBlbGVrdHIuIERhdGVudmVya2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5R +dWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5RdWFsLTAzMB4XDTA1MDgxNzIyMDAw +MFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgwRgYDVQQKDD9BLVRy +dXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0ZW52 +ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMM +EEEtVHJ1c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCtPWFuA/OQO8BBC4SAzewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUj +lUC5B3ilJfYKvUWG6Nm9wASOhURh73+nyfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZ +znF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPESU7l0+m0iKsMrmKS1GWH +2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4iHQF63n1 +k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs +2e3Vcuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYD +VR0OBAoECERqlWdVeRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAVdRU0VlIXLOThaq/Yy/kgM40ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fG +KOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmrsQd7TZjTXLDR8KdCoLXEjq/+ +8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZdJXDRZslo+S4R +FGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS +mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmE +DNuxUCAKGkq6ahq97BvIxYSazQ== +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Label: "TWCA Root Certification Authority" +# Serial: 1 +# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 +# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 +# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Label: "Security Communication RootCA2" +# Serial: 0 +# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 +# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 +# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2011" +# Serial: 0 +# MD5 Fingerprint: 73:9f:4c:4b:73:5b:79:e9:fa:ba:1c:ef:6e:cb:d5:c9 +# SHA1 Fingerprint: fe:45:65:9b:79:03:5b:98:a1:61:b5:51:2e:ac:da:58:09:48:22:4d +# SHA256 Fingerprint: bc:10:4f:15:a4:8b:e7:09:dc:a5:42:a7:e1:d4:b9:df:6f:05:45:27:e8:02:ea:a9:2d:59:54:44:25:8a:fe:71 +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix +RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p +YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw +NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK +EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl +cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz +dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ +fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns +bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD +75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP +FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV +HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp +5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu +b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA +A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p +6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7 +dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys +Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI +l7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Label: "Actalis Authentication Root CA" +# Serial: 6271844772424770508 +# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 +# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac +# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +# Issuer: O=Trustis Limited OU=Trustis FPS Root CA +# Subject: O=Trustis Limited OU=Trustis FPS Root CA +# Label: "Trustis FPS Root CA" +# Serial: 36053640375399034304724988975563710553 +# MD5 Fingerprint: 30:c9:e7:1e:6b:e6:14:eb:65:b2:16:69:20:31:67:4d +# SHA1 Fingerprint: 3b:c0:38:0b:33:c3:f6:a6:0c:86:15:22:93:d9:df:f5:4b:81:c0:04 +# SHA256 Fingerprint: c1:b4:82:99:ab:a5:20:8f:e9:63:0a:ce:55:ca:68:a0:3e:da:5a:51:9c:88:02:a0:d3:a6:73:be:8f:8e:55:7d +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL +ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx +MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc +MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+ +AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH +iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj +vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA +0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB +OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/ +BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E +FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01 +GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW +zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4 +1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE +f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F +jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN +ZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +# Issuer: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing +# Subject: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing +# Label: "StartCom Certification Authority" +# Serial: 45 +# MD5 Fingerprint: c9:3b:0d:84:41:fc:a4:76:79:23:08:57:de:10:19:16 +# SHA1 Fingerprint: a3:f1:33:3f:e2:42:bf:cf:c5:d1:4e:8f:39:42:98:40:68:10:d1:a0 +# SHA256 Fingerprint: e1:78:90:ee:09:a3:fb:f4:f4:8b:9c:41:4a:17:d6:37:b7:a5:06:47:e9:bc:75:23:22:72:7f:cc:17:42:a9:11 +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul +F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC +ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w +ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk +aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0 +YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg +c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93 +d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG +CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF +wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS +Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst +0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc +pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl +CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF +P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK +1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm +KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ +8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm +fyWl8kgAwKQB2j8= +-----END CERTIFICATE----- + +# Issuer: CN=StartCom Certification Authority G2 O=StartCom Ltd. +# Subject: CN=StartCom Certification Authority G2 O=StartCom Ltd. +# Label: "StartCom Certification Authority G2" +# Serial: 59 +# MD5 Fingerprint: 78:4b:fb:9e:64:82:0a:d3:b8:4c:62:f3:64:f2:90:64 +# SHA1 Fingerprint: 31:f1:fd:68:22:63:20:ee:c6:3b:3f:9d:ea:4a:3e:53:7c:7c:39:17 +# SHA256 Fingerprint: c7:ba:65:67:de:93:a7:98:ae:1f:aa:79:1e:71:2d:37:8f:ae:1f:93:c4:39:7f:ea:44:1b:b7:cb:e6:fd:59:95 +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1 +OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG +A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ +JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD +vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo +D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/ +Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW +RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK +HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN +nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM +0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i +UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9 +Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg +TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL +BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K +2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX +UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl +6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK +9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ +HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI +wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY +XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l +IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo +hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr +so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 2 Root CA" +# Serial: 2 +# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 +# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 +# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 3 Root CA" +# Serial: 2 +# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec +# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 +# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 3" +# Serial: 1 +# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef +# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 +# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +# Issuer: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus +# Subject: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus +# Label: "EE Certification Centre Root CA" +# Serial: 112324828676200291871926431888494945866 +# MD5 Fingerprint: 43:5e:88:d4:7d:1a:4a:7e:fd:84:2e:52:eb:01:d4:6f +# SHA1 Fingerprint: c9:a8:b9:e7:55:80:5e:58:e3:53:77:a7:25:eb:af:c3:7b:27:cc:d7 +# SHA256 Fingerprint: 3e:84:ba:43:42:90:85:16:e7:75:73:c0:99:2f:09:79:ca:08:4e:46:85:68:1f:f1:95:cc:ba:8a:22:9b:8a:76 +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1 +MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1 +czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG +CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy +MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl +ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS +b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy +euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO +bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw +WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d +MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE +1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/ +zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB +BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF +BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV +v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG +E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW +iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v +GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0= +-----END CERTIFICATE----- + +# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Aralık 2007 +# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Aralık 2007 +# Label: "TURKTRUST Certificate Services Provider Root 2007" +# Serial: 1 +# MD5 Fingerprint: 2b:70:20:56:86:82:a0:18:c8:07:53:12:28:70:21:72 +# SHA1 Fingerprint: f1:7f:6f:b6:31:dc:99:e3:a3:c8:7f:fe:1c:f1:81:10:88:d9:60:33 +# SHA256 Fingerprint: 97:8c:d9:66:f2:fa:a0:7b:a7:aa:95:00:d9:c0:2e:9d:77:f2:cd:ad:a6:ad:6b:a7:4a:f4:b9:1c:66:59:3c:50 +-----BEGIN CERTIFICATE----- +MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOc +UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xS +S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg +SGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4XDTA3MTIyNTE4Mzcx +OVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxla3Ry +b25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMC +VFIxDzANBgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDE +sGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7F +ni4gKGMpIEFyYWzEsWsgMjAwNzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9NYvDdE3ePYakqtdTyuTFY +KTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQvKUmi8wUG ++7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveG +HtyaKhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6P +IzdezKKqdfcYbwnTrqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M +733WB2+Y8a+xwXrXgTW4qhe04MsCAwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHk +Yb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/sPx+EnWVUXKgW +AkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I +aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5 +mxRZNTZPz/OOXl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsa +XRik7r4EW5nVcV9VZWRi1aKbBFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZ +qxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAKpoRq0Tl9 +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 2009" +# Serial: 623603 +# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f +# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 +# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 EV 2009" +# Serial: 623604 +# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 +# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 +# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Raiz del Estado Venezolano O=Sistema Nacional de Certificacion Electronica OU=Superintendencia de Servicios de Certificacion Electronica +# Subject: CN=PSCProcert O=Sistema Nacional de Certificacion Electronica OU=Proveedor de Certificados PROCERT +# Label: "PSCProcert" +# Serial: 11 +# MD5 Fingerprint: e6:24:e9:12:01:ae:0c:de:8e:85:c4:ce:a3:12:dd:ec +# SHA1 Fingerprint: 70:c1:8d:74:b4:28:81:0a:e4:fd:a5:75:d7:01:9f:99:b0:3d:50:74 +# SHA256 Fingerprint: 3c:fc:3c:14:d1:f6:84:ff:17:e3:8c:43:ca:44:0c:00:b9:67:ec:93:3e:8b:fe:06:4c:a1:d7:2c:90:f2:ad:b0 +-----BEGIN CERTIFICATE----- +MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1 +dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9s +YW5vMQswCQYDVQQGEwJWRTEQMA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlz +dHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0 +aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBlcmludGVuZGVuY2lh +IGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUwIwYJ +KoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEw +MFoXDTIwMTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHBy +b2NlcnQubmV0LnZlMQ8wDQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGEx +KjAoBgNVBAsTIVByb3ZlZWRvciBkZSBDZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQG +A1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9u +aWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo9 +7BVCwfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74 +BCXfgI8Qhd19L3uA3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38G +ieU89RLAu9MLmV+QfI4tL3czkkohRqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9 +JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmOEO8GqQKJ/+MMbpfg353bIdD0 +PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG20qCZyFSTXai2 +0b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH +0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/ +6mnbVSKVUyqUtd+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1m +v6JpIzi4mWCZDlZTOpx+FIywBm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7 +K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvpr2uKGcfLFFb14dq12fy/czja+eev +bqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/AgEBMDcGA1UdEgQw +MC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAzNi0w +MB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFD +gBStuyIdxuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0 +b3JpZGFkIGRlIENlcnRpZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xh +bm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQHEwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0 +cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5hY2lvbmFsIGRlIENlcnRp +ZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5kZW5jaWEg +ZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkq +hkiG9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQD +AgEGME0GA1UdEQRGMESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0w +MDAwMDKgGwYFYIZeAgKgEgwQUklGLUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEag +RKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9sY3IvQ0VSVElGSUNBRE8t +UkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNyYWl6LnN1c2Nl +cnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v +Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsG +AQUFBwIBFh5odHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcN +AQELBQADggIBACtZ6yKZu4SqT96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS +1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmNg7+mvTV+LFwxNG9s2/NkAZiqlCxB +3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4quxtxj7mkoP3Yldmv +Wb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1n8Gh +HVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHm +pHmJWhSnFFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXz +sOfIt+FTvZLm8wyWuevo5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bE +qCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq3TNWOByyrYDT13K9mmyZY+gAu0F2Bbdb +mRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5poLWccret9W6aAjtmcz9 +opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3YeMLEYC/H +YvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km +-----END CERTIFICATE----- + +# Issuer: CN=China Internet Network Information Center EV Certificates Root O=China Internet Network Information Center +# Subject: CN=China Internet Network Information Center EV Certificates Root O=China Internet Network Information Center +# Label: "China Internet Network Information Center EV Certificates Root" +# Serial: 1218379777 +# MD5 Fingerprint: 55:5d:63:00:97:bd:6a:97:f5:67:ab:4b:fb:6e:63:15 +# SHA1 Fingerprint: 4f:99:aa:93:fb:2b:d1:37:26:a1:99:4a:ce:7f:f0:05:f2:93:5d:1e +# SHA256 Fingerprint: 1c:01:c6:f4:db:b2:fe:fc:22:55:8b:2b:ca:32:56:3f:49:84:4a:cf:c3:2b:7b:e4:b0:ff:59:9f:9e:8c:7a:f7 +-----BEGIN CERTIFICATE----- +MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC +Q04xMjAwBgNVBAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24g +Q2VudGVyMUcwRQYDVQQDDD5DaGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0 +aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMgUm9vdDAeFw0xMDA4MzEwNzExMjVa +Fw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAGA1UECgwpQ2hpbmEg +SW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMMPkNo +aW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRp +ZmljYXRlcyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z +7r07eKpkQ0H1UN+U8i6yjUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA// +DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV98YPjUesWgbdYavi7NifFy2cyjw1l1Vx +zUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2HklY0bBoQCxfVWhyXWIQ8 +hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23KzhmBsUs +4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54u +gQEC7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oY +NJKiyoOCWTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E +FgQUfHJLOcfA22KlT5uqGDSSosqDglkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3 +j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd50XPFtQO3WKwMVC/GVhMPMdoG +52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM7+czV0I664zB +echNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws +ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrI +zo9uoV1/A3U05K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATy +wy39FCqQmbkHzJ8= +-----END CERTIFICATE----- + +# Issuer: CN=Swisscom Root CA 2 O=Swisscom OU=Digital Certificate Services +# Subject: CN=Swisscom Root CA 2 O=Swisscom OU=Digital Certificate Services +# Label: "Swisscom Root CA 2" +# Serial: 40698052477090394928831521023204026294 +# MD5 Fingerprint: 5b:04:69:ec:a5:83:94:63:18:a7:86:d0:e4:f2:6e:19 +# SHA1 Fingerprint: 77:47:4f:c6:30:e4:0f:4c:47:64:3f:84:ba:b8:c6:95:4a:8a:41:ec +# SHA256 Fingerprint: f0:9b:12:2c:71:14:f4:a0:9b:d4:ea:4f:4a:99:d5:58:b4:6e:4c:25:cd:81:14:0d:29:c0:56:13:91:4c:38:41 +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBk +MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 +YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg +Q0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2MjUwNzM4MTRaMGQxCzAJBgNVBAYT +AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp +Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvEr +jw0DzpPMLgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r +0rk0X2s682Q2zsKwzxNoysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f +2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJwDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVP +ACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpHWrumnf2U5NGKpV+GY3aF +y6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1aSgJA/MTA +tukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL +6yxSNLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0 +uPoTXGiTOmekl9AbmbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrAL +acywlKinh/LTSlDcX3KwFnUey7QYYpqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velh +k6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3qPyZ7iVNTA6z00yPhOgpD/0Q +VAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw +FDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O +BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqh +b97iEoHF8TwuMA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4R +fbgZPnm3qKhyN2abGu2sEzsOv2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv +/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ82YqZh6NM4OKb3xuqFp1mrjX2lhI +REeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLzo9v/tdhZsnPdTSpx +srpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcsa0vv +aGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciAT +woCqISxxOQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99n +Bjx8Oto0QuFmtEYE3saWmA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5W +t6NlUe07qxS/TFED6F+KBZvuim6c779o+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N +8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TCrvJcwhbtkj6EPnNgiLx2 +9CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX5OfNeOI5 +wSsSnqaeG8XmDtkx2Q== +-----END CERTIFICATE----- + +# Issuer: CN=Swisscom Root EV CA 2 O=Swisscom OU=Digital Certificate Services +# Subject: CN=Swisscom Root EV CA 2 O=Swisscom OU=Digital Certificate Services +# Label: "Swisscom Root EV CA 2" +# Serial: 322973295377129385374608406479535262296 +# MD5 Fingerprint: 7b:30:34:9f:dd:0a:4b:6b:35:ca:31:51:28:5d:ae:ec +# SHA1 Fingerprint: e7:a1:90:29:d3:d5:52:dc:0d:0f:c6:92:d3:ea:88:0d:15:2e:1a:6b +# SHA256 Fingerprint: d9:5f:ea:3c:a4:ee:dc:e7:4c:d7:6e:75:fc:6d:1f:f6:2c:44:1f:0f:a8:bc:77:f0:34:b1:9e:5d:b2:58:01:5d +-----BEGIN CERTIFICATE----- +MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAw +ZzELMAkGA1UEBhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdp +dGFsIENlcnRpZmljYXRlIFNlcnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290 +IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcNMzEwNjI1MDg0NTA4WjBnMQswCQYD +VQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2Vy +dGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYgQ0Eg +MjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7Bx +UglgRCgzo3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD +1ycfMQ4jFrclyxy0uYAyXhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPH +oCE2G3pXKSinLr9xJZDzRINpUKTk4RtiGZQJo/PDvO/0vezbE53PnUgJUmfANykR +HvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8LiqG12W0OfvrSdsyaGOx9/ +5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaHZa0zKcQv +idm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHL +OdAGalNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaC +NYGu+HuB5ur+rPQam3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f +46Fq9mDU5zXNysRojddxyNMkM3OxbPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCB +UWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDixzgHcgplwLa7JSnaFp6LNYth +7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/BAQDAgGGMB0G +A1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED +MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWB +bj2ITY1x0kbBbkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6x +XCX5145v9Ydkn+0UjrgEjihLj6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98T +PLr+flaYC/NUn81ETm484T4VvwYmneTwkLbUwp4wLh/vx3rEUMfqe9pQy3omywC0 +Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7XwgiG/W9mR4U9s70 +WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH59yL +Gn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm +7JFe3VE/23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4S +nr8PyQUQ3nqjsTzyP6WqJ3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VN +vBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyAHmBR3NdUIR7KYndP+tiPsys6DXhyyWhB +WkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/giuMod89a2GQ+fYWVq6nTI +fI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuWl8PVP3wb +I+2ksx0WckNLIOFZfsLorSa/ovc= +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R1 O=Disig a.s. +# Subject: CN=CA Disig Root R1 O=Disig a.s. +# Label: "CA Disig Root R1" +# Serial: 14052245610670616104 +# MD5 Fingerprint: be:ec:11:93:9a:f5:69:21:bc:d7:c1:c0:67:89:cc:2a +# SHA1 Fingerprint: 8e:1c:74:f8:a6:20:b9:e5:8a:f4:61:fa:ec:2b:47:56:51:1a:52:c6 +# SHA256 Fingerprint: f9:6f:23:f4:c3:e7:9c:07:7a:46:98:8d:5a:f5:90:06:76:a0:f0:39:cb:64:5d:d1:75:49:b2:16:c8:24:40:ce +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQy +MDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjEw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy3QRk +D2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/o +OI7bm+V8u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3A +fQ+lekLZWnDZv6fXARz2m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJe +IgpFy4QxTaz+29FHuvlglzmxZcfe+5nkCiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8n +oc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTaYVKvJrT1cU/J19IG32PK +/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6vpmumwKj +rckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD +3AjLLhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE +7cderVC6xkGbrPAXZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkC +yC2fg69naQanMVXVz0tv/wQFx1isXxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLd +qvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ04IwDQYJKoZI +hvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR +xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaA +SfX8MPWbTx9BLxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXo +HqJPYNcHKfyyo6SdbhWSVhlMCrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpB +emOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5GfbVSUZP/3oNn6z4eGBrxEWi1CXYBmC +AMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85YmLLW1AL14FABZyb +7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKSds+x +DzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvk +F7mGnjixlAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqF +a3qdnom2piiZk4hA9z7NUaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsT +Q6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJa7+h89n07eLw4+1knj0vllJPgFOL +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R2 O=Disig a.s. +# Subject: CN=CA Disig Root R2 O=Disig a.s. +# Label: "CA Disig Root R2" +# Serial: 10572350602393338211 +# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 +# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 +# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Label: "ACCVRAIZ1" +# Serial: 6828503384748696800 +# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 +# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 +# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA Global Root CA" +# Serial: 3262 +# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 +# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 +# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Label: "TeliaSonera Root CA v1" +# Serial: 199041966741090107964904287217786801558 +# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c +# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 +# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +# Issuer: CN=E-Tugra Certification Authority O=E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. OU=E-Tugra Sertifikasyon Merkezi +# Subject: CN=E-Tugra Certification Authority O=E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. OU=E-Tugra Sertifikasyon Merkezi +# Label: "E-Tugra Certification Authority" +# Serial: 7667447206703254355 +# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49 +# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39 +# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC +aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV +BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 +Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz +MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ +BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp +em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY +B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH +D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF +Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo +q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D +k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH +fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut +dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM +ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 +zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX +U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 +Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 +XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF +Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR +HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY +GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c +77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 ++GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK +vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 +FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl +yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P +AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD +y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d +NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot 2011 O=Atos +# Subject: CN=Atos TrustedRoot 2011 O=Atos +# Label: "Atos TrustedRoot 2011" +# Serial: 6643877497813316402 +# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 +# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 +# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=Certification Authority of WoSign O=WoSign CA Limited +# Subject: CN=Certification Authority of WoSign O=WoSign CA Limited +# Label: "WoSign" +# Serial: 125491772294754854453622855443212256657 +# MD5 Fingerprint: a1:f2:f9:b5:d2:c8:7a:74:b8:f3:05:f1:d7:e1:84:8d +# SHA1 Fingerprint: b9:42:94:bf:91:ea:8f:b6:4b:e6:10:97:c7:fb:00:13:59:b6:76:cb +# SHA256 Fingerprint: 4b:22:d5:a6:ae:c9:9f:3c:db:79:aa:5e:c0:68:38:47:9c:d5:ec:ba:71:64:f7:f2:2d:c1:d6:5f:63:d8:57:08 +-----BEGIN CERTIFICATE----- +MIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBV +MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNV +BAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgw +MTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFX +b1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvcqN +rLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1U +fcIiePyOCbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcScc +f+Hb0v1naMQFXQoOXXDX2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2 +ZjC1vt7tj/id07sBMOby8w7gLJKA84X5KIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4M +x1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR+ScPewavVIMYe+HdVHpR +aG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ezEC8wQjch +zDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDar +uHqklWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221K +mYo0SLwX3OSACCK28jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvA +Sh0JWzko/amrzgD5LkhLJuYwTKVYyrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWv +HYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0CAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R8bNLtwYgFP6H +EtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1 +LOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJ +MuYhOZO9sxXqT2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2e +JXLOC62qx1ViC777Y7NhRCOjy+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VN +g64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC2nz4SNAzqfkHx5Xh9T71XXG68pWp +dIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes5cVAWubXbHssw1ab +R80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/EaEQ +PkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGce +xGATVdVhmVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+ +J7x6v+Db9NpSvd4MVHAxkUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMl +OtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGikpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWT +ee5Ehr7XHuQe+w== +-----END CERTIFICATE----- + +# Issuer: CN=CA 沃通根证书 O=WoSign CA Limited +# Subject: CN=CA 沃通根证书 O=WoSign CA Limited +# Label: "WoSign China" +# Serial: 106921963437422998931660691310149453965 +# MD5 Fingerprint: 78:83:5b:52:16:76:c4:24:3b:83:78:e8:ac:da:9a:93 +# SHA1 Fingerprint: 16:32:47:8d:89:f9:21:3a:92:00:85:63:f5:a4:a7:d3:12:40:8a:d6 +# SHA256 Fingerprint: d6:f0:34:bd:94:aa:23:3f:02:97:ec:a4:24:5b:28:39:73:e4:47:aa:59:0f:31:0c:77:f4:8f:df:83:11:22:54 +-----BEGIN CERTIFICATE----- +MIIFWDCCA0CgAwIBAgIQUHBrzdgT/BtOOzNy0hFIjTANBgkqhkiG9w0BAQsFADBG +MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNV +BAMMEkNBIOayg+mAmuagueivgeS5pjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgw +MTAwMDFaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRl +ZDEbMBkGA1UEAwwSQ0Eg5rKD6YCa5qC56K+B5LmmMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA0EkhHiX8h8EqwqzbdoYGTufQdDTc7WU1/FDWiD+k8H/r +D195L4mx/bxjWDeTmzj4t1up+thxx7S8gJeNbEvxUNUqKaqoGXqW5pWOdO2XCld1 +9AXbbQs5uQF/qvbW2mzmBeCkTVL829B0txGMe41P/4eDrv8FAxNXUDf+jJZSEExf +v5RxadmWPgxDT74wwJ85dE8GRV2j1lY5aAfMh09Qd5Nx2UQIsYo06Yms25tO4dnk +UkWMLhQfkWsZHWgpLFbE4h4TV2TwYeO5Ed+w4VegG63XX9Gv2ystP9Bojg/qnw+L +NVgbExz03jWhCl3W6t8Sb8D7aQdGctyB9gQjF+BNdeFyb7Ao65vh4YOhn0pdr8yb ++gIgthhid5E7o9Vlrdx8kHccREGkSovrlXLp9glk3Kgtn3R46MGiCWOc76DbT52V +qyBPt7D3h1ymoOQ3OMdc4zUPLK2jgKLsLl3Az+2LBcLmc272idX10kaO6m1jGx6K +yX2m+Jzr5dVjhU1zZmkR/sgO9MHHZklTfuQZa/HpelmjbX7FF+Ynxu8b22/8DU0G +AbQOXDBGVWCvOGU6yke6rCzMRh+yRpY/8+0mBe53oWprfi1tWFxK1I5nuPHa1UaK +J/kR8slC/k7e3x9cxKSGhxYzoacXGKUN5AXlK8IrC6KVkLn9YDxOiT7nnO4fuwEC +AwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFOBNv9ybQV0T6GTwp+kVpOGBwboxMA0GCSqGSIb3DQEBCwUAA4ICAQBqinA4 +WbbaixjIvirTthnVZil6Xc1bL3McJk6jfW+rtylNpumlEYOnOXOvEESS5iVdT2H6 +yAa+Tkvv/vMx/sZ8cApBWNromUuWyXi8mHwCKe0JgOYKOoICKuLJL8hWGSbueBwj +/feTZU7n85iYr83d2Z5AiDEoOqsuC7CsDCT6eiaY8xJhEPRdF/d+4niXVOKM6Cm6 +jBAyvd0zaziGfjk9DgNyp115j0WKWa5bIW4xRtVZjc8VX90xJc/bYNaBRHIpAlf2 +ltTW/+op2znFuCyKGo3Oy+dCMYYFaA6eFN0AkLppRQjbbpCBhqcqBT/mhDn4t/lX +X0ykeVoQDF7Va/81XwVRHmyjdanPUIPTfPRm94KNPQx96N97qA4bLJyuQHCH2u2n +FoJavjVsIE4iYdm8UXrNemHcSxH5/mc0zy4EZmFcV5cjjPOGG0jfKq+nwf/Yjj4D +u9gqsPoUJbJRa4ZDhS4HIxaAjUz7tGM7zMN07RujHv41D198HRaG9Q7DlfEvr10l +O1Hm13ZBONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Le +ie2uPAmvylezkolwQOQvT8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR1 +2KvxAmLBsX5VYc8T1yaw15zLKYs4SgsOkI26oQ== +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 14367148294922964480859022125800977897474 +# MD5 Fingerprint: 20:f0:27:68:d1:7e:a0:9d:0e:e6:2a:ca:df:5c:89:8e +# SHA1 Fingerprint: 69:69:56:2e:40:80:f4:24:a1:e7:19:9f:14:ba:f3:ee:58:ab:6a:bb +# SHA256 Fingerprint: be:c9:49:11:c2:95:56:76:db:6c:0a:55:09:86:d7:6e:3b:a0:05:66:7c:44:2c:97:62:b4:fb:b7:73:de:22:8c +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA - G3" +# Serial: 10003001 +# MD5 Fingerprint: 0b:46:67:07:db:10:2f:19:8c:35:50:60:d1:0b:f4:37 +# SHA1 Fingerprint: d8:eb:6b:41:51:92:59:e0:f3:e7:85:00:c0:3d:b6:88:97:c9:ee:fc +# SHA256 Fingerprint: 3c:4f:b0:b9:5a:b8:b3:00:32:f4:32:b8:6f:53:5f:e1:72:c1:85:d0:fd:39:86:58:37:cf:36:18:7f:a6:f4:28 +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX +DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP +cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW +IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX +xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy +KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR +9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az +5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 +6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 +Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP +bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt +BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt +XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd +INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp +LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 +Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp +gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh +/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw +0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A +fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq +4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR +1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ +QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM +94B7IWcnMFk= +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden +# Label: "Staat der Nederlanden EV Root CA" +# Serial: 10000013 +# MD5 Fingerprint: fc:06:af:7b:e8:1a:f1:9a:b4:e8:d2:70:1f:c0:f5:ba +# SHA1 Fingerprint: 76:e2:7e:c1:4f:db:82:c1:c0:a6:75:b5:05:be:3d:29:b4:ed:db:bb +# SHA256 Fingerprint: 4d:24:91:41:4c:fe:95:67:46:ec:4c:ef:a6:cf:6f:72:e2:8a:13:29:43:2f:9d:8a:90:7a:c4:cb:5d:ad:c1:5a +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y +MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg +TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS +b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS +M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC +UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d +Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p +rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l +pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb +j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC +KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS +/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X +cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH +1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP +px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 +MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u +2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS +v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC +wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy +CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e +vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 +Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa +Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL +eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 +FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc +7uzXLg== +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Label: "IdenTrust Commercial Root CA 1" +# Serial: 13298821034946342390520003877796839426 +# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 +# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 +# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Label: "IdenTrust Public Sector Root CA 1" +# Serial: 13298821034946342390521976156843933698 +# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba +# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd +# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G2" +# Serial: 1246989352 +# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 +# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 +# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - EC1" +# Serial: 51543124481930649114116133369 +# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc +# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 +# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority +# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority +# Label: "CFCA EV ROOT" +# Serial: 407555286 +# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 +# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 +# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5 O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5 O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +# Label: "TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5" +# Serial: 156233699172481 +# MD5 Fingerprint: da:70:8e:f0:22:df:93:26:f6:5f:9f:d3:15:06:52:4e +# SHA1 Fingerprint: c4:18:f6:4d:46:d1:df:00:3d:27:30:13:72:43:a9:12:11:c6:75:fb +# SHA256 Fingerprint: 49:35:1b:90:34:44:c1:85:cc:dc:5c:69:3d:24:d8:55:5c:b2:08:d6:a8:14:13:07:69:9f:4a:f0:63:19:9d:78 +-----BEGIN CERTIFICATE----- +MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UE +BhMCVFIxDzANBgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxn +aSDEsGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkg +QS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1QgRWxla3Ryb25payBTZXJ0aWZpa2Eg +SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAwODA3MDFaFw0yMzA0 +MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0wSwYD +VQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8 +dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApCUZ4WWe60ghUEoI5RHwWrom +/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537jVJp45wnEFPzpALFp/kR +Gml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1mep5Fimh3 +4khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z +5UNP9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0 +hO8EuPbJbKoCPrZV4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QID +AQABo0IwQDAdBgNVHQ4EFgQUVpkHHtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJ5FdnsX +SDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPoBP5yCccLqh0l +VX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq +URawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nf +peYVhDfwwvJllpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CF +Yv4HAqGEVka+lgqaE9chTLd8B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW ++qtB4Uu2NQvAmxU= +-----END CERTIFICATE----- + +# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6 O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6 O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +# Label: "TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6" +# Serial: 138134509972618 +# MD5 Fingerprint: f8:c5:ee:2a:6b:be:95:8d:08:f7:25:4a:ea:71:3e:46 +# SHA1 Fingerprint: 8a:5c:8c:ee:a5:03:e6:05:56:ba:d8:1b:d4:f6:c9:b0:ed:e5:2f:e0 +# SHA256 Fingerprint: 8d:e7:86:55:e1:be:7f:78:47:80:0b:93:f6:94:d2:1d:36:8c:c0:6e:03:3e:7f:ab:04:bb:5e:b9:9d:a6:b7:00 +-----BEGIN CERTIFICATE----- +MIIEJjCCAw6gAwIBAgIGfaHyZeyKMA0GCSqGSIb3DQEBCwUAMIGxMQswCQYDVQQG +EwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0wSwYDVQQKDERUw5xSS1RSVVNUIEJpbGdp +IMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBB +LsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBI +aXptZXQgU2HEn2xhecSxY8Sxc8SxIEg2MB4XDTEzMTIxODA5MDQxMFoXDTIzMTIx +NjA5MDQxMFowgbExCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExTTBLBgNV +BAoMRFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2 +ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMUIwQAYDVQQDDDlUw5xSS1RSVVNUIEVs +ZWt0cm9uaWsgU2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLEgSDYwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCdsGjW6L0UlqMACprx9MfMkU1x +eHe59yEmFXNRFpQJRwXiM/VomjX/3EsvMsew7eKC5W/a2uqsxgbPJQ1BgfbBOCK9 ++bGlprMBvD9QFyv26WZV1DOzXPhDIHiTVRZwGTLmiddk671IUP320EEDwnS3/faA +z1vFq6TWlRKb55cTMgPp1KtDWxbtMyJkKbbSk60vbNg9tvYdDjTu0n2pVQ8g9P0p +u5FbHH3GQjhtQiht1AH7zYiXSX6484P4tZgvsycLSF5W506jM7NE1qXyGJTtHB6p +lVxiSvgNZ1GpryHV+DKdeboaX+UEVU0TRv/yz3THGmNtwx8XEsMeED5gCLMxAgMB +AAGjQjBAMB0GA1UdDgQWBBTdVRcT9qzoSCHK77Wv0QAy7Z6MtTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAb1gNl0Oq +FlQ+v6nfkkU/hQu7VtMMUszIv3ZnXuaqs6fvuay0EBQNdH49ba3RfdCaqaXKGDsC +QC4qnFAUi/5XfldcEQlLNkVS9z2sFP1E34uXI9TDwe7UU5X+LEr+DXCqu4svLcsy +o4LyVN/Y8t3XSHLuSqMplsNEzm61kod2pLv0kmzOLBQJZo6NrRa1xxsJYTvjIKID +gI6tflEATseWhvtDmHd9KMeP2Cpu54Rvl0EpABZeTeIT6lnAY2c6RPuY/ATTMHKm +9ocJV612ph1jmv3XZch4gyt1O6VbuA1df74jrlZVlFjvH4GMKrLN5ptjnhi85WsG +tAuYSyher4hYyw== +-----END CERTIFICATE----- + +# Issuer: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903 +# Subject: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903 +# Label: "Certinomis - Root CA" +# Serial: 1 +# MD5 Fingerprint: 14:0a:fd:8d:a8:28:b5:38:69:db:56:7e:61:22:03:3f +# SHA1 Fingerprint: 9d:70:bb:01:a5:a4:a0:18:11:2e:f7:1c:01:b9:32:c5:34:e7:88:a8 +# SHA256 Fingerprint: 2a:99:f5:bc:11:74:b7:3c:bb:1d:62:08:84:e0:1c:34:e5:1c:cb:39:78:da:12:5f:0e:33:26:88:83:bf:41:58 +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb +BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz +MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx +FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g +Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2 +fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl +LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV +WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF +TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb +5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc +CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri +wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ +wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG +m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4 +F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng +WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0 +2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF +AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/ +0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw +F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS +g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj +qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN +h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/ +ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V +btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj +Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ +8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW +gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE= +-----END CERTIFICATE----- +# Issuer: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Secure Server CA" +# Serial: 927650371 +# MD5 Fingerprint: df:f2:80:73:cc:f1:e6:61:73:fc:f5:42:e9:c5:7c:ee +# SHA1 Fingerprint: 99:a6:9b:e6:1a:fe:88:6b:4d:2b:82:00:7c:b8:54:fc:31:7e:15:39 +# SHA256 Fingerprint: 62:f2:40:27:8c:56:4c:4d:d8:bf:7d:9d:4f:6f:36:6e:a8:94:d2:2f:5f:34:d9:89:a9:83:ac:ec:2f:ff:ed:50 +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC +VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u +ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc +KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u +ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 +MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE +ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j +b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg +U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA +A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ +I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 +wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC +AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb +oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 +BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p +dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk +MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp +b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 +MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi +E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa +MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI +hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN +95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd +2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority +# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority +# Label: "ValiCert Class 2 VA" +# Serial: 1 +# MD5 Fingerprint: a9:23:75:9b:ba:49:36:6e:31:c2:db:f2:e7:66:ba:87 +# SHA1 Fingerprint: 31:7a:2a:d0:7f:2b:33:5e:f5:a1:c3:4e:4b:57:e8:b7:d8:f1:fc:a6 +# SHA256 Fingerprint: 58:d0:17:27:9c:d4:dc:63:ab:dd:b1:96:a6:c9:90:6c:30:c4:e0:87:83:ea:e8:c1:60:99:54:d6:93:55:59:6b +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy +NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY +dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9 +WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS +v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v +UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu +IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC +W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Expressz (Class C) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Subject: CN=NetLock Expressz (Class C) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Label: "NetLock Express (Class C) Root" +# Serial: 104 +# MD5 Fingerprint: 4f:eb:f1:f0:70:c2:80:63:5d:58:9f:da:12:3c:a9:c4 +# SHA1 Fingerprint: e3:92:51:2f:0a:cf:f5:05:df:f6:de:06:7f:75:37:e1:65:ea:57:4b +# SHA256 Fingerprint: 0b:5e:ed:4e:84:64:03:cf:55:e0:65:84:84:40:ed:2a:82:75:8b:f5:b9:aa:1f:25:3d:46:13:cf:a0:80:ff:3f +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD +EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X +DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw +DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u +c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr +TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN +BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA +OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC +2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW +RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P +AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW +ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0 +YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz +b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO +ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB +IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs +b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s +YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg +a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g +SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0 +aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg +YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg +Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY +ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g +pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4 +Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Uzleti (Class B) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Subject: CN=NetLock Uzleti (Class B) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Label: "NetLock Business (Class B) Root" +# Serial: 105 +# MD5 Fingerprint: 39:16:aa:b9:6a:41:e1:14:69:df:9e:6c:3b:72:dc:b6 +# SHA1 Fingerprint: 87:9f:4b:ee:05:df:98:58:3b:e3:60:d6:33:e7:0d:3f:fe:98:71:af +# SHA256 Fingerprint: 39:df:7b:68:2b:7b:93:8f:84:71:54:81:cc:de:8d:60:d8:f2:2e:c5:98:87:7d:0a:aa:c1:2b:59:18:2b:03:12 +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD +EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05 +OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G +A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh +Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l +dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK +gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX +iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc +Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E +BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G +SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu +b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh +bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv +Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln +aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0 +IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph +biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo +ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP +UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj +YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA +bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06 +sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa +n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS +NitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority +# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority +# Label: "RSA Root Certificate 1" +# Serial: 1 +# MD5 Fingerprint: a2:6f:53:b7:ee:40:db:4a:68:e7:fa:18:d9:10:4b:72 +# SHA1 Fingerprint: 69:bd:8c:f4:9c:d3:00:fb:59:2e:17:93:ca:55:6a:f3:ec:aa:35:fb +# SHA256 Fingerprint: bc:23:f9:8a:31:3c:b9:2d:e3:bb:fc:3a:5a:9f:44:61:ac:39:49:4c:4a:e1:5a:9e:9d:f1:31:e9:9b:73:01:9a +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy +NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD +cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs +2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY +JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE +Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ +n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A +PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu +-----END CERTIFICATE----- + +# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority +# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority +# Label: "ValiCert Class 1 VA" +# Serial: 1 +# MD5 Fingerprint: 65:58:ab:15:ad:57:6c:1e:a8:a7:b5:69:ac:bf:ff:eb +# SHA1 Fingerprint: e5:df:74:3c:b6:01:c4:9b:98:43:dc:ab:8c:e8:6a:81:10:9f:e4:8e +# SHA256 Fingerprint: f4:c1:49:55:1a:30:13:a3:5b:c7:bf:fe:17:a7:f3:44:9b:c1:ab:5b:5a:0a:e7:4b:06:c2:3b:90:00:4c:01:04 +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy +NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y +LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+ +TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y +TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0 +LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW +I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw +nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI +-----END CERTIFICATE----- + +# Issuer: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc. +# Subject: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc. +# Label: "Equifax Secure eBusiness CA 1" +# Serial: 4 +# MD5 Fingerprint: 64:9c:ef:2e:44:fc:c6:8f:52:07:d0:51:73:8f:cb:3d +# SHA1 Fingerprint: da:40:18:8b:91:89:a3:ed:ee:ae:da:97:fe:2f:9d:f5:b7:d1:8a:41 +# SHA256 Fingerprint: cf:56:ff:46:a4:a1:86:10:9d:d9:65:84:b5:ee:b5:8a:51:0c:42:75:b0:e5:f9:4f:40:bb:ae:86:5e:19:f6:73 +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT +ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw +MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j +LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ +KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo +RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu +WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw +Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD +AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK +eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM +zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+ +WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN +/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +# Issuer: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc. +# Subject: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc. +# Label: "Equifax Secure Global eBusiness CA" +# Serial: 1 +# MD5 Fingerprint: 8f:5d:77:06:27:c4:98:3c:5b:93:78:e7:d7:7d:9b:cc +# SHA1 Fingerprint: 7e:78:4a:10:1c:82:65:cc:2d:e1:f1:6d:47:b4:40:ca:d9:0a:19:45 +# SHA256 Fingerprint: 5f:0b:62:ea:b5:e3:53:ea:65:21:65:16:58:fb:b6:53:59:f4:43:28:0a:4a:fb:d1:04:d7:7d:10:f9:f0:4c:07 +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT +ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw +MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj +dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l +c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC +UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc +58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/ +o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr +aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA +A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA +Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv +8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +# Issuer: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division +# Subject: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division +# Label: "Thawte Premium Server CA" +# Serial: 1 +# MD5 Fingerprint: 06:9f:69:79:16:66:90:02:1b:8c:8c:a2:c3:07:6f:3a +# SHA1 Fingerprint: 62:7f:8d:78:27:65:63:99:d2:7d:7f:90:44:c9:fe:b3:f3:3e:fa:9a +# SHA256 Fingerprint: ab:70:36:36:5c:71:54:aa:29:c2:c2:9f:5d:41:91:16:3b:16:2a:22:25:01:13:57:d5:6d:07:ff:a7:bc:1f:72 +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy +dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t +MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB +MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG +A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp +b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl +cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv +bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE +VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ +ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR +uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG +9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI +hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM +pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +# Issuer: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division +# Subject: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division +# Label: "Thawte Server CA" +# Serial: 1 +# MD5 Fingerprint: c5:70:c4:a2:ed:53:78:0c:c8:10:53:81:64:cb:d0:1d +# SHA1 Fingerprint: 23:e5:94:94:51:95:f2:41:48:03:b4:d5:64:d2:a3:a3:f5:d8:8b:8c +# SHA256 Fingerprint: b4:41:0b:73:e2:e6:ea:ca:47:fb:c4:2f:8f:a4:01:8a:f4:38:1d:c5:4c:fa:a8:44:50:46:1e:ed:09:45:4d:e9 +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm +MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx +MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3 +dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl +cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3 +DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD +gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91 +yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX +L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj +EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG +7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e +QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ +qdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority +# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority +# Label: "Verisign Class 3 Public Primary Certification Authority" +# Serial: 149843929435818692848040365716851702463 +# MD5 Fingerprint: 10:fc:63:5d:f6:26:3e:0d:f3:25:be:5f:79:cd:67:67 +# SHA1 Fingerprint: 74:2c:31:92:e6:07:e4:24:eb:45:49:54:2b:e1:bb:c5:3e:61:74:e2 +# SHA256 Fingerprint: e7:68:56:34:ef:ac:f6:9a:ce:93:9a:6b:25:5b:7b:4f:ab:ef:42:93:5b:50:a2:65:ac:b5:cb:60:27:e4:4e:70 +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do +lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc +AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k +-----END CERTIFICATE----- + +# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority +# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority +# Label: "Verisign Class 3 Public Primary Certification Authority" +# Serial: 80507572722862485515306429940691309246 +# MD5 Fingerprint: ef:5a:f1:33:ef:f1:cd:bb:51:02:ee:12:14:4b:96:c4 +# SHA1 Fingerprint: a1:db:63:93:91:6f:17:e4:18:55:09:40:04:15:c7:02:40:b0:ae:6b +# SHA256 Fingerprint: a4:b6:b3:99:6f:c2:f3:06:b3:fd:86:81:bd:63:41:3d:8c:50:09:cc:4f:a3:29:c2:cc:f0:e2:fa:1b:14:03:05 +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i +2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ +2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ +-----END CERTIFICATE----- + +# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network +# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network +# Label: "Verisign Class 3 Public Primary Certification Authority - G2" +# Serial: 167285380242319648451154478808036881606 +# MD5 Fingerprint: a2:33:9b:4c:74:78:73:d4:6c:e7:c1:f3:8d:cb:5c:e9 +# SHA1 Fingerprint: 85:37:1c:a6:e5:50:14:3d:ce:28:03:47:1b:de:3a:09:e8:f8:77:0f +# SHA256 Fingerprint: 83:ce:3c:12:29:68:8a:59:3d:48:5f:81:97:3c:0f:91:95:43:1e:da:37:cc:5e:36:43:0e:79:c7:a8:88:63:8b +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh +c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy +MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp +emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X +DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw +FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg +UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo +YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 +MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4 +pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0 +13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID +AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk +U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i +F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY +oJ2daZH9 +-----END CERTIFICATE----- + +# Issuer: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc. +# Subject: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc. +# Label: "GTE CyberTrust Global Root" +# Serial: 421 +# MD5 Fingerprint: ca:3d:d3:68:f1:03:5c:d0:32:fa:b8:2b:59:e8:5a:db +# SHA1 Fingerprint: 97:81:79:50:d8:1c:96:70:cc:34:d8:09:cf:79:44:31:36:7e:f4:74 +# SHA256 Fingerprint: a5:31:25:18:8d:21:10:aa:96:4b:02:c7:b7:c6:da:32:03:17:08:94:e5:fb:71:ff:fb:66:67:d5:e6:81:0a:36 +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD +VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv +bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv +b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV +UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU +cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds +b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH +iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS +r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4 +04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r +GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9 +3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P +lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/certs.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/certs.py new file mode 100644 index 0000000..f922b99 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/certs.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +requests.certs +~~~~~~~~~~~~~~ + +This module returns the preferred default CA certificate bundle. + +If you are packaging Requests, e.g., for a Linux distribution or a managed +environment, you can change the definition of where() to return a separately +packaged CA bundle. +""" +import os.path + +try: + from certifi import where +except ImportError: + def where(): + """Return the preferred certificate bundle.""" + # vendored bundle inside Requests + return os.path.join(os.path.dirname(__file__), 'cacert.pem') + +if __name__ == '__main__': + print(where()) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/compat.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/compat.py new file mode 100644 index 0000000..353ec29 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/compat.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +""" +requests.compat +~~~~~~~~~~~~~~~ + +This module handles import compatibility issues between Python 2 and +Python 3. +""" + +from .packages import chardet + +import sys + +# ------- +# Pythons +# ------- + +# Syntax sugar. +_ver = sys.version_info + +#: Python 2.x? +is_py2 = (_ver[0] == 2) + +#: Python 3.x? +is_py3 = (_ver[0] == 3) + +# Note: We've patched out simplejson support in pip because it prevents +# upgrading simplejson on Windows. +# try: +# import simplejson as json +# except (ImportError, SyntaxError): +# # simplejson does not support Python 3.2, it throws a SyntaxError +# # because of u'...' Unicode literals. +import json + +# --------- +# Specifics +# --------- + +if is_py2: + from urllib import quote, unquote, quote_plus, unquote_plus, urlencode, getproxies, proxy_bypass + from urlparse import urlparse, urlunparse, urljoin, urlsplit, urldefrag + from urllib2 import parse_http_list + import cookielib + from Cookie import Morsel + from StringIO import StringIO + from .packages.urllib3.packages.ordered_dict import OrderedDict + + builtin_str = str + bytes = str + str = unicode + basestring = basestring + numeric_types = (int, long, float) + +elif is_py3: + from urllib.parse import urlparse, urlunparse, urljoin, urlsplit, urlencode, quote, unquote, quote_plus, unquote_plus, urldefrag + from urllib.request import parse_http_list, getproxies, proxy_bypass + from http import cookiejar as cookielib + from http.cookies import Morsel + from io import StringIO + from collections import OrderedDict + + builtin_str = str + str = str + bytes = bytes + basestring = (str, bytes) + numeric_types = (int, float) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/cookies.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/cookies.py new file mode 100644 index 0000000..41a2fde --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/cookies.py @@ -0,0 +1,540 @@ +# -*- coding: utf-8 -*- + +""" +requests.cookies +~~~~~~~~~~~~~~~~ + +Compatibility code to be able to use `cookielib.CookieJar` with requests. + +requests.utils imports from here, so be careful with imports. +""" + +import copy +import time +import calendar +import collections +from .compat import cookielib, urlparse, urlunparse, Morsel + +try: + import threading + # grr, pyflakes: this fixes "redefinition of unused 'threading'" + threading +except ImportError: + import dummy_threading as threading + + +class MockRequest(object): + """Wraps a `requests.Request` to mimic a `urllib2.Request`. + + The code in `cookielib.CookieJar` expects this interface in order to correctly + manage cookie policies, i.e., determine whether a cookie can be set, given the + domains of the request and the cookie. + + The original request object is read-only. The client is responsible for collecting + the new headers via `get_new_headers()` and interpreting them appropriately. You + probably want `get_cookie_header`, defined below. + """ + + def __init__(self, request): + self._r = request + self._new_headers = {} + self.type = urlparse(self._r.url).scheme + + def get_type(self): + return self.type + + def get_host(self): + return urlparse(self._r.url).netloc + + def get_origin_req_host(self): + return self.get_host() + + def get_full_url(self): + # Only return the response's URL if the user hadn't set the Host + # header + if not self._r.headers.get('Host'): + return self._r.url + # If they did set it, retrieve it and reconstruct the expected domain + host = self._r.headers['Host'] + parsed = urlparse(self._r.url) + # Reconstruct the URL as we expect it + return urlunparse([ + parsed.scheme, host, parsed.path, parsed.params, parsed.query, + parsed.fragment + ]) + + def is_unverifiable(self): + return True + + def has_header(self, name): + return name in self._r.headers or name in self._new_headers + + def get_header(self, name, default=None): + return self._r.headers.get(name, self._new_headers.get(name, default)) + + def add_header(self, key, val): + """cookielib has no legitimate use for this method; add it back if you find one.""" + raise NotImplementedError("Cookie headers should be added with add_unredirected_header()") + + def add_unredirected_header(self, name, value): + self._new_headers[name] = value + + def get_new_headers(self): + return self._new_headers + + @property + def unverifiable(self): + return self.is_unverifiable() + + @property + def origin_req_host(self): + return self.get_origin_req_host() + + @property + def host(self): + return self.get_host() + + +class MockResponse(object): + """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`. + + ...what? Basically, expose the parsed HTTP headers from the server response + the way `cookielib` expects to see them. + """ + + def __init__(self, headers): + """Make a MockResponse for `cookielib` to read. + + :param headers: a httplib.HTTPMessage or analogous carrying the headers + """ + self._headers = headers + + def info(self): + return self._headers + + def getheaders(self, name): + self._headers.getheaders(name) + + +def extract_cookies_to_jar(jar, request, response): + """Extract the cookies from the response into a CookieJar. + + :param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar) + :param request: our own requests.Request object + :param response: urllib3.HTTPResponse object + """ + if not (hasattr(response, '_original_response') and + response._original_response): + return + # the _original_response field is the wrapped httplib.HTTPResponse object, + req = MockRequest(request) + # pull out the HTTPMessage with the headers and put it in the mock: + res = MockResponse(response._original_response.msg) + jar.extract_cookies(res, req) + + +def get_cookie_header(jar, request): + """ + Produce an appropriate Cookie header string to be sent with `request`, or None. + + :rtype: str + """ + r = MockRequest(request) + jar.add_cookie_header(r) + return r.get_new_headers().get('Cookie') + + +def remove_cookie_by_name(cookiejar, name, domain=None, path=None): + """Unsets a cookie by name, by default over all domains and paths. + + Wraps CookieJar.clear(), is O(n). + """ + clearables = [] + for cookie in cookiejar: + if cookie.name != name: + continue + if domain is not None and domain != cookie.domain: + continue + if path is not None and path != cookie.path: + continue + clearables.append((cookie.domain, cookie.path, cookie.name)) + + for domain, path, name in clearables: + cookiejar.clear(domain, path, name) + + +class CookieConflictError(RuntimeError): + """There are two cookies that meet the criteria specified in the cookie jar. + Use .get and .set and include domain and path args in order to be more specific. + """ + + +class RequestsCookieJar(cookielib.CookieJar, collections.MutableMapping): + """Compatibility class; is a cookielib.CookieJar, but exposes a dict + interface. + + This is the CookieJar we create by default for requests and sessions that + don't specify one, since some clients may expect response.cookies and + session.cookies to support dict operations. + + Requests does not use the dict interface internally; it's just for + compatibility with external client code. All requests code should work + out of the box with externally provided instances of ``CookieJar``, e.g. + ``LWPCookieJar`` and ``FileCookieJar``. + + Unlike a regular CookieJar, this class is pickleable. + + .. warning:: dictionary operations that are normally O(1) may be O(n). + """ + + def get(self, name, default=None, domain=None, path=None): + """Dict-like get() that also supports optional domain and path args in + order to resolve naming collisions from using one cookie jar over + multiple domains. + + .. warning:: operation is O(n), not O(1). + """ + try: + return self._find_no_duplicates(name, domain, path) + except KeyError: + return default + + def set(self, name, value, **kwargs): + """Dict-like set() that also supports optional domain and path args in + order to resolve naming collisions from using one cookie jar over + multiple domains. + """ + # support client code that unsets cookies by assignment of a None value: + if value is None: + remove_cookie_by_name(self, name, domain=kwargs.get('domain'), path=kwargs.get('path')) + return + + if isinstance(value, Morsel): + c = morsel_to_cookie(value) + else: + c = create_cookie(name, value, **kwargs) + self.set_cookie(c) + return c + + def iterkeys(self): + """Dict-like iterkeys() that returns an iterator of names of cookies + from the jar. + + .. seealso:: itervalues() and iteritems(). + """ + for cookie in iter(self): + yield cookie.name + + def keys(self): + """Dict-like keys() that returns a list of names of cookies from the + jar. + + .. seealso:: values() and items(). + """ + return list(self.iterkeys()) + + def itervalues(self): + """Dict-like itervalues() that returns an iterator of values of cookies + from the jar. + + .. seealso:: iterkeys() and iteritems(). + """ + for cookie in iter(self): + yield cookie.value + + def values(self): + """Dict-like values() that returns a list of values of cookies from the + jar. + + .. seealso:: keys() and items(). + """ + return list(self.itervalues()) + + def iteritems(self): + """Dict-like iteritems() that returns an iterator of name-value tuples + from the jar. + + .. seealso:: iterkeys() and itervalues(). + """ + for cookie in iter(self): + yield cookie.name, cookie.value + + def items(self): + """Dict-like items() that returns a list of name-value tuples from the + jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a + vanilla python dict of key value pairs. + + .. seealso:: keys() and values(). + """ + return list(self.iteritems()) + + def list_domains(self): + """Utility method to list all the domains in the jar.""" + domains = [] + for cookie in iter(self): + if cookie.domain not in domains: + domains.append(cookie.domain) + return domains + + def list_paths(self): + """Utility method to list all the paths in the jar.""" + paths = [] + for cookie in iter(self): + if cookie.path not in paths: + paths.append(cookie.path) + return paths + + def multiple_domains(self): + """Returns True if there are multiple domains in the jar. + Returns False otherwise. + + :rtype: bool + """ + domains = [] + for cookie in iter(self): + if cookie.domain is not None and cookie.domain in domains: + return True + domains.append(cookie.domain) + return False # there is only one domain in jar + + def get_dict(self, domain=None, path=None): + """Takes as an argument an optional domain and path and returns a plain + old Python dict of name-value pairs of cookies that meet the + requirements. + + :rtype: dict + """ + dictionary = {} + for cookie in iter(self): + if (domain is None or cookie.domain == domain) and (path is None + or cookie.path == path): + dictionary[cookie.name] = cookie.value + return dictionary + + def __contains__(self, name): + try: + return super(RequestsCookieJar, self).__contains__(name) + except CookieConflictError: + return True + + def __getitem__(self, name): + """Dict-like __getitem__() for compatibility with client code. Throws + exception if there are more than one cookie with name. In that case, + use the more explicit get() method instead. + + .. warning:: operation is O(n), not O(1). + """ + return self._find_no_duplicates(name) + + def __setitem__(self, name, value): + """Dict-like __setitem__ for compatibility with client code. Throws + exception if there is already a cookie of that name in the jar. In that + case, use the more explicit set() method instead. + """ + self.set(name, value) + + def __delitem__(self, name): + """Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s + ``remove_cookie_by_name()``. + """ + remove_cookie_by_name(self, name) + + def set_cookie(self, cookie, *args, **kwargs): + if hasattr(cookie.value, 'startswith') and cookie.value.startswith('"') and cookie.value.endswith('"'): + cookie.value = cookie.value.replace('\\"', '') + return super(RequestsCookieJar, self).set_cookie(cookie, *args, **kwargs) + + def update(self, other): + """Updates this jar with cookies from another CookieJar or dict-like""" + if isinstance(other, cookielib.CookieJar): + for cookie in other: + self.set_cookie(copy.copy(cookie)) + else: + super(RequestsCookieJar, self).update(other) + + def _find(self, name, domain=None, path=None): + """Requests uses this method internally to get cookie values. + + If there are conflicting cookies, _find arbitrarily chooses one. + See _find_no_duplicates if you want an exception thrown if there are + conflicting cookies. + + :param name: a string containing name of cookie + :param domain: (optional) string containing domain of cookie + :param path: (optional) string containing path of cookie + :return: cookie.value + """ + for cookie in iter(self): + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + return cookie.value + + raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) + + def _find_no_duplicates(self, name, domain=None, path=None): + """Both ``__get_item__`` and ``get`` call this function: it's never + used elsewhere in Requests. + + :param name: a string containing name of cookie + :param domain: (optional) string containing domain of cookie + :param path: (optional) string containing path of cookie + :raises KeyError: if cookie is not found + :raises CookieConflictError: if there are multiple cookies + that match name and optionally domain and path + :return: cookie.value + """ + toReturn = None + for cookie in iter(self): + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + if toReturn is not None: # if there are multiple cookies that meet passed in criteria + raise CookieConflictError('There are multiple cookies with name, %r' % (name)) + toReturn = cookie.value # we will eventually return this as long as no cookie conflict + + if toReturn: + return toReturn + raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) + + def __getstate__(self): + """Unlike a normal CookieJar, this class is pickleable.""" + state = self.__dict__.copy() + # remove the unpickleable RLock object + state.pop('_cookies_lock') + return state + + def __setstate__(self, state): + """Unlike a normal CookieJar, this class is pickleable.""" + self.__dict__.update(state) + if '_cookies_lock' not in self.__dict__: + self._cookies_lock = threading.RLock() + + def copy(self): + """Return a copy of this RequestsCookieJar.""" + new_cj = RequestsCookieJar() + new_cj.update(self) + return new_cj + + +def _copy_cookie_jar(jar): + if jar is None: + return None + + if hasattr(jar, 'copy'): + # We're dealing with an instance of RequestsCookieJar + return jar.copy() + # We're dealing with a generic CookieJar instance + new_jar = copy.copy(jar) + new_jar.clear() + for cookie in jar: + new_jar.set_cookie(copy.copy(cookie)) + return new_jar + + +def create_cookie(name, value, **kwargs): + """Make a cookie from underspecified parameters. + + By default, the pair of `name` and `value` will be set for the domain '' + and sent on every request (this is sometimes called a "supercookie"). + """ + result = dict( + version=0, + name=name, + value=value, + port=None, + domain='', + path='/', + secure=False, + expires=None, + discard=True, + comment=None, + comment_url=None, + rest={'HttpOnly': None}, + rfc2109=False,) + + badargs = set(kwargs) - set(result) + if badargs: + err = 'create_cookie() got unexpected keyword arguments: %s' + raise TypeError(err % list(badargs)) + + result.update(kwargs) + result['port_specified'] = bool(result['port']) + result['domain_specified'] = bool(result['domain']) + result['domain_initial_dot'] = result['domain'].startswith('.') + result['path_specified'] = bool(result['path']) + + return cookielib.Cookie(**result) + + +def morsel_to_cookie(morsel): + """Convert a Morsel object into a Cookie containing the one k/v pair.""" + + expires = None + if morsel['max-age']: + try: + expires = int(time.time() + int(morsel['max-age'])) + except ValueError: + raise TypeError('max-age: %s must be integer' % morsel['max-age']) + elif morsel['expires']: + time_template = '%a, %d-%b-%Y %H:%M:%S GMT' + expires = calendar.timegm( + time.strptime(morsel['expires'], time_template) + ) + return create_cookie( + comment=morsel['comment'], + comment_url=bool(morsel['comment']), + discard=False, + domain=morsel['domain'], + expires=expires, + name=morsel.key, + path=morsel['path'], + port=None, + rest={'HttpOnly': morsel['httponly']}, + rfc2109=False, + secure=bool(morsel['secure']), + value=morsel.value, + version=morsel['version'] or 0, + ) + + +def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True): + """Returns a CookieJar from a key/value dictionary. + + :param cookie_dict: Dict of key/values to insert into CookieJar. + :param cookiejar: (optional) A cookiejar to add the cookies to. + :param overwrite: (optional) If False, will not replace cookies + already in the jar with new ones. + """ + if cookiejar is None: + cookiejar = RequestsCookieJar() + + if cookie_dict is not None: + names_from_jar = [cookie.name for cookie in cookiejar] + for name in cookie_dict: + if overwrite or (name not in names_from_jar): + cookiejar.set_cookie(create_cookie(name, cookie_dict[name])) + + return cookiejar + + +def merge_cookies(cookiejar, cookies): + """Add cookies to cookiejar and returns a merged CookieJar. + + :param cookiejar: CookieJar object to add the cookies to. + :param cookies: Dictionary or CookieJar object to be added. + """ + if not isinstance(cookiejar, cookielib.CookieJar): + raise ValueError('You can only merge into CookieJar') + + if isinstance(cookies, dict): + cookiejar = cookiejar_from_dict( + cookies, cookiejar=cookiejar, overwrite=False) + elif isinstance(cookies, cookielib.CookieJar): + try: + cookiejar.update(cookies) + except AttributeError: + for cookie_in_jar in cookies: + cookiejar.set_cookie(cookie_in_jar) + + return cookiejar diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/exceptions.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/exceptions.py new file mode 100644 index 0000000..b89e0cc --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/exceptions.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- + +""" +requests.exceptions +~~~~~~~~~~~~~~~~~~~ + +This module contains the set of Requests' exceptions. +""" +from .packages.urllib3.exceptions import HTTPError as BaseHTTPError + + +class RequestException(IOError): + """There was an ambiguous exception that occurred while handling your + request. + """ + + def __init__(self, *args, **kwargs): + """Initialize RequestException with `request` and `response` objects.""" + response = kwargs.pop('response', None) + self.response = response + self.request = kwargs.pop('request', None) + if (response is not None and not self.request and + hasattr(response, 'request')): + self.request = self.response.request + super(RequestException, self).__init__(*args, **kwargs) + + +class HTTPError(RequestException): + """An HTTP error occurred.""" + + +class ConnectionError(RequestException): + """A Connection error occurred.""" + + +class ProxyError(ConnectionError): + """A proxy error occurred.""" + + +class SSLError(ConnectionError): + """An SSL error occurred.""" + + +class Timeout(RequestException): + """The request timed out. + + Catching this error will catch both + :exc:`~requests.exceptions.ConnectTimeout` and + :exc:`~requests.exceptions.ReadTimeout` errors. + """ + + +class ConnectTimeout(ConnectionError, Timeout): + """The request timed out while trying to connect to the remote server. + + Requests that produced this error are safe to retry. + """ + + +class ReadTimeout(Timeout): + """The server did not send any data in the allotted amount of time.""" + + +class URLRequired(RequestException): + """A valid URL is required to make a request.""" + + +class TooManyRedirects(RequestException): + """Too many redirects.""" + + +class MissingSchema(RequestException, ValueError): + """The URL schema (e.g. http or https) is missing.""" + + +class InvalidSchema(RequestException, ValueError): + """See defaults.py for valid schemas.""" + + +class InvalidURL(RequestException, ValueError): + """The URL provided was somehow invalid.""" + + +class InvalidHeader(RequestException, ValueError): + """The header value provided was somehow invalid.""" + + +class ChunkedEncodingError(RequestException): + """The server declared chunked encoding but sent an invalid chunk.""" + + +class ContentDecodingError(RequestException, BaseHTTPError): + """Failed to decode response content""" + + +class StreamConsumedError(RequestException, TypeError): + """The content for this response was already consumed""" + + +class RetryError(RequestException): + """Custom retries logic failed""" + + +# Warnings + + +class RequestsWarning(Warning): + """Base warning for Requests.""" + pass + + +class FileModeWarning(RequestsWarning, DeprecationWarning): + """A file was opened in text mode, but Requests determined its binary length.""" + pass diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/hooks.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/hooks.py new file mode 100644 index 0000000..32b32de --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/hooks.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +""" +requests.hooks +~~~~~~~~~~~~~~ + +This module provides the capabilities for the Requests hooks system. + +Available hooks: + +``response``: + The response generated from a Request. +""" +HOOKS = ['response'] + + +def default_hooks(): + return dict((event, []) for event in HOOKS) + +# TODO: response is the only one + + +def dispatch_hook(key, hooks, hook_data, **kwargs): + """Dispatches a hook dictionary on a given piece of data.""" + hooks = hooks or dict() + hooks = hooks.get(key) + if hooks: + if hasattr(hooks, '__call__'): + hooks = [hooks] + for hook in hooks: + _hook_data = hook(hook_data, **kwargs) + if _hook_data is not None: + hook_data = _hook_data + return hook_data diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/models.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/models.py new file mode 100644 index 0000000..11434ef --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/models.py @@ -0,0 +1,873 @@ +# -*- coding: utf-8 -*- + +""" +requests.models +~~~~~~~~~~~~~~~ + +This module contains the primary objects that power Requests. +""" + +import collections +import datetime + +from io import BytesIO, UnsupportedOperation +from .hooks import default_hooks +from .structures import CaseInsensitiveDict + +from .auth import HTTPBasicAuth +from .cookies import cookiejar_from_dict, get_cookie_header, _copy_cookie_jar +from .packages.urllib3.fields import RequestField +from .packages.urllib3.filepost import encode_multipart_formdata +from .packages.urllib3.util import parse_url +from .packages.urllib3.exceptions import ( + DecodeError, ReadTimeoutError, ProtocolError, LocationParseError) +from .exceptions import ( + HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError, + ContentDecodingError, ConnectionError, StreamConsumedError) +from .utils import ( + guess_filename, get_auth_from_url, requote_uri, + stream_decode_response_unicode, to_key_val_list, parse_header_links, + iter_slices, guess_json_utf, super_len, to_native_string, + check_header_validity) +from .compat import ( + cookielib, urlunparse, urlsplit, urlencode, str, bytes, StringIO, + is_py2, chardet, builtin_str, basestring) +from .compat import json as complexjson +from .status_codes import codes + +#: The set of HTTP status codes that indicate an automatically +#: processable redirect. +REDIRECT_STATI = ( + codes.moved, # 301 + codes.found, # 302 + codes.other, # 303 + codes.temporary_redirect, # 307 + codes.permanent_redirect, # 308 +) + +DEFAULT_REDIRECT_LIMIT = 30 +CONTENT_CHUNK_SIZE = 10 * 1024 +ITER_CHUNK_SIZE = 512 + + +class RequestEncodingMixin(object): + @property + def path_url(self): + """Build the path URL to use.""" + + url = [] + + p = urlsplit(self.url) + + path = p.path + if not path: + path = '/' + + url.append(path) + + query = p.query + if query: + url.append('?') + url.append(query) + + return ''.join(url) + + @staticmethod + def _encode_params(data): + """Encode parameters in a piece of data. + + Will successfully encode parameters when passed as a dict or a list of + 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary + if parameters are supplied as a dict. + """ + + if isinstance(data, (str, bytes)): + return data + elif hasattr(data, 'read'): + return data + elif hasattr(data, '__iter__'): + result = [] + for k, vs in to_key_val_list(data): + if isinstance(vs, basestring) or not hasattr(vs, '__iter__'): + vs = [vs] + for v in vs: + if v is not None: + result.append( + (k.encode('utf-8') if isinstance(k, str) else k, + v.encode('utf-8') if isinstance(v, str) else v)) + return urlencode(result, doseq=True) + else: + return data + + @staticmethod + def _encode_files(files, data): + """Build the body for a multipart/form-data request. + + Will successfully encode files when passed as a dict or a list of + tuples. Order is retained if data is a list of tuples but arbitrary + if parameters are supplied as a dict. + The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype) + or 4-tuples (filename, fileobj, contentype, custom_headers). + """ + if (not files): + raise ValueError("Files must be provided.") + elif isinstance(data, basestring): + raise ValueError("Data must not be a string.") + + new_fields = [] + fields = to_key_val_list(data or {}) + files = to_key_val_list(files or {}) + + for field, val in fields: + if isinstance(val, basestring) or not hasattr(val, '__iter__'): + val = [val] + for v in val: + if v is not None: + # Don't call str() on bytestrings: in Py3 it all goes wrong. + if not isinstance(v, bytes): + v = str(v) + + new_fields.append( + (field.decode('utf-8') if isinstance(field, bytes) else field, + v.encode('utf-8') if isinstance(v, str) else v)) + + for (k, v) in files: + # support for explicit filename + ft = None + fh = None + if isinstance(v, (tuple, list)): + if len(v) == 2: + fn, fp = v + elif len(v) == 3: + fn, fp, ft = v + else: + fn, fp, ft, fh = v + else: + fn = guess_filename(v) or k + fp = v + + if isinstance(fp, (str, bytes, bytearray)): + fdata = fp + else: + fdata = fp.read() + + rf = RequestField(name=k, data=fdata, filename=fn, headers=fh) + rf.make_multipart(content_type=ft) + new_fields.append(rf) + + body, content_type = encode_multipart_formdata(new_fields) + + return body, content_type + + +class RequestHooksMixin(object): + def register_hook(self, event, hook): + """Properly register a hook.""" + + if event not in self.hooks: + raise ValueError('Unsupported event specified, with event name "%s"' % (event)) + + if isinstance(hook, collections.Callable): + self.hooks[event].append(hook) + elif hasattr(hook, '__iter__'): + self.hooks[event].extend(h for h in hook if isinstance(h, collections.Callable)) + + def deregister_hook(self, event, hook): + """Deregister a previously registered hook. + Returns True if the hook existed, False if not. + """ + + try: + self.hooks[event].remove(hook) + return True + except ValueError: + return False + + +class Request(RequestHooksMixin): + """A user-created :class:`Request <Request>` object. + + Used to prepare a :class:`PreparedRequest <PreparedRequest>`, which is sent to the server. + + :param method: HTTP method to use. + :param url: URL to send. + :param headers: dictionary of headers to send. + :param files: dictionary of {filename: fileobject} files to multipart upload. + :param data: the body to attach to the request. If a dictionary is provided, form-encoding will take place. + :param json: json for the body to attach to the request (if files or data is not specified). + :param params: dictionary of URL parameters to append to the URL. + :param auth: Auth handler or (user, pass) tuple. + :param cookies: dictionary or CookieJar of cookies to attach to this request. + :param hooks: dictionary of callback hooks, for internal usage. + + Usage:: + + >>> import requests + >>> req = requests.Request('GET', 'http://httpbin.org/get') + >>> req.prepare() + <PreparedRequest [GET]> + """ + + def __init__(self, method=None, url=None, headers=None, files=None, + data=None, params=None, auth=None, cookies=None, hooks=None, json=None): + + # Default empty dicts for dict params. + data = [] if data is None else data + files = [] if files is None else files + headers = {} if headers is None else headers + params = {} if params is None else params + hooks = {} if hooks is None else hooks + + self.hooks = default_hooks() + for (k, v) in list(hooks.items()): + self.register_hook(event=k, hook=v) + + self.method = method + self.url = url + self.headers = headers + self.files = files + self.data = data + self.json = json + self.params = params + self.auth = auth + self.cookies = cookies + + def __repr__(self): + return '<Request [%s]>' % (self.method) + + def prepare(self): + """Constructs a :class:`PreparedRequest <PreparedRequest>` for transmission and returns it.""" + p = PreparedRequest() + p.prepare( + method=self.method, + url=self.url, + headers=self.headers, + files=self.files, + data=self.data, + json=self.json, + params=self.params, + auth=self.auth, + cookies=self.cookies, + hooks=self.hooks, + ) + return p + + +class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): + """The fully mutable :class:`PreparedRequest <PreparedRequest>` object, + containing the exact bytes that will be sent to the server. + + Generated from either a :class:`Request <Request>` object or manually. + + Usage:: + + >>> import requests + >>> req = requests.Request('GET', 'http://httpbin.org/get') + >>> r = req.prepare() + <PreparedRequest [GET]> + + >>> s = requests.Session() + >>> s.send(r) + <Response [200]> + """ + + def __init__(self): + #: HTTP verb to send to the server. + self.method = None + #: HTTP URL to send the request to. + self.url = None + #: dictionary of HTTP headers. + self.headers = None + # The `CookieJar` used to create the Cookie header will be stored here + # after prepare_cookies is called + self._cookies = None + #: request body to send to the server. + self.body = None + #: dictionary of callback hooks, for internal usage. + self.hooks = default_hooks() + + def prepare(self, method=None, url=None, headers=None, files=None, + data=None, params=None, auth=None, cookies=None, hooks=None, json=None): + """Prepares the entire request with the given parameters.""" + + self.prepare_method(method) + self.prepare_url(url, params) + self.prepare_headers(headers) + self.prepare_cookies(cookies) + self.prepare_body(data, files, json) + self.prepare_auth(auth, url) + + # Note that prepare_auth must be last to enable authentication schemes + # such as OAuth to work on a fully prepared request. + + # This MUST go after prepare_auth. Authenticators could add a hook + self.prepare_hooks(hooks) + + def __repr__(self): + return '<PreparedRequest [%s]>' % (self.method) + + def copy(self): + p = PreparedRequest() + p.method = self.method + p.url = self.url + p.headers = self.headers.copy() if self.headers is not None else None + p._cookies = _copy_cookie_jar(self._cookies) + p.body = self.body + p.hooks = self.hooks + return p + + def prepare_method(self, method): + """Prepares the given HTTP method.""" + self.method = method + if self.method is not None: + self.method = to_native_string(self.method.upper()) + + def prepare_url(self, url, params): + """Prepares the given HTTP URL.""" + #: Accept objects that have string representations. + #: We're unable to blindly call unicode/str functions + #: as this will include the bytestring indicator (b'') + #: on python 3.x. + #: https://github.com/kennethreitz/requests/pull/2238 + if isinstance(url, bytes): + url = url.decode('utf8') + else: + url = unicode(url) if is_py2 else str(url) + + # Don't do any URL preparation for non-HTTP schemes like `mailto`, + # `data` etc to work around exceptions from `url_parse`, which + # handles RFC 3986 only. + if ':' in url and not url.lower().startswith('http'): + self.url = url + return + + # Support for unicode domain names and paths. + try: + scheme, auth, host, port, path, query, fragment = parse_url(url) + except LocationParseError as e: + raise InvalidURL(*e.args) + + if not scheme: + error = ("Invalid URL {0!r}: No schema supplied. Perhaps you meant http://{0}?") + error = error.format(to_native_string(url, 'utf8')) + + raise MissingSchema(error) + + if not host: + raise InvalidURL("Invalid URL %r: No host supplied" % url) + + # Only want to apply IDNA to the hostname + try: + host = host.encode('idna').decode('utf-8') + except UnicodeError: + raise InvalidURL('URL has an invalid label.') + + # Carefully reconstruct the network location + netloc = auth or '' + if netloc: + netloc += '@' + netloc += host + if port: + netloc += ':' + str(port) + + # Bare domains aren't valid URLs. + if not path: + path = '/' + + if is_py2: + if isinstance(scheme, str): + scheme = scheme.encode('utf-8') + if isinstance(netloc, str): + netloc = netloc.encode('utf-8') + if isinstance(path, str): + path = path.encode('utf-8') + if isinstance(query, str): + query = query.encode('utf-8') + if isinstance(fragment, str): + fragment = fragment.encode('utf-8') + + if isinstance(params, (str, bytes)): + params = to_native_string(params) + + enc_params = self._encode_params(params) + if enc_params: + if query: + query = '%s&%s' % (query, enc_params) + else: + query = enc_params + + url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment])) + self.url = url + + def prepare_headers(self, headers): + """Prepares the given HTTP headers.""" + + self.headers = CaseInsensitiveDict() + if headers: + for header in headers.items(): + # Raise exception on invalid header value. + check_header_validity(header) + name, value = header + self.headers[to_native_string(name)] = value + + def prepare_body(self, data, files, json=None): + """Prepares the given HTTP body data.""" + + # Check if file, fo, generator, iterator. + # If not, run through normal process. + + # Nottin' on you. + body = None + content_type = None + length = None + + if not data and json is not None: + # urllib3 requires a bytes-like body. Python 2's json.dumps + # provides this natively, but Python 3 gives a Unicode string. + content_type = 'application/json' + body = complexjson.dumps(json) + if not isinstance(body, bytes): + body = body.encode('utf-8') + + is_stream = all([ + hasattr(data, '__iter__'), + not isinstance(data, (basestring, list, tuple, dict)) + ]) + + try: + length = super_len(data) + except (TypeError, AttributeError, UnsupportedOperation): + length = None + + if is_stream: + body = data + + if files: + raise NotImplementedError('Streamed bodies and files are mutually exclusive.') + + if length: + self.headers['Content-Length'] = builtin_str(length) + else: + self.headers['Transfer-Encoding'] = 'chunked' + else: + # Multi-part file uploads. + if files: + (body, content_type) = self._encode_files(files, data) + else: + if data: + body = self._encode_params(data) + if isinstance(data, basestring) or hasattr(data, 'read'): + content_type = None + else: + content_type = 'application/x-www-form-urlencoded' + + self.prepare_content_length(body) + + # Add content-type if it wasn't explicitly provided. + if content_type and ('content-type' not in self.headers): + self.headers['Content-Type'] = content_type + + self.body = body + + def prepare_content_length(self, body): + if hasattr(body, 'seek') and hasattr(body, 'tell'): + curr_pos = body.tell() + body.seek(0, 2) + end_pos = body.tell() + self.headers['Content-Length'] = builtin_str(max(0, end_pos - curr_pos)) + body.seek(curr_pos, 0) + elif body is not None: + l = super_len(body) + if l: + self.headers['Content-Length'] = builtin_str(l) + elif (self.method not in ('GET', 'HEAD')) and (self.headers.get('Content-Length') is None): + self.headers['Content-Length'] = '0' + + def prepare_auth(self, auth, url=''): + """Prepares the given HTTP auth data.""" + + # If no Auth is explicitly provided, extract it from the URL first. + if auth is None: + url_auth = get_auth_from_url(self.url) + auth = url_auth if any(url_auth) else None + + if auth: + if isinstance(auth, tuple) and len(auth) == 2: + # special-case basic HTTP auth + auth = HTTPBasicAuth(*auth) + + # Allow auth to make its changes. + r = auth(self) + + # Update self to reflect the auth changes. + self.__dict__.update(r.__dict__) + + # Recompute Content-Length + self.prepare_content_length(self.body) + + def prepare_cookies(self, cookies): + """Prepares the given HTTP cookie data. + + This function eventually generates a ``Cookie`` header from the + given cookies using cookielib. Due to cookielib's design, the header + will not be regenerated if it already exists, meaning this function + can only be called once for the life of the + :class:`PreparedRequest <PreparedRequest>` object. Any subsequent calls + to ``prepare_cookies`` will have no actual effect, unless the "Cookie" + header is removed beforehand. + """ + if isinstance(cookies, cookielib.CookieJar): + self._cookies = cookies + else: + self._cookies = cookiejar_from_dict(cookies) + + cookie_header = get_cookie_header(self._cookies, self) + if cookie_header is not None: + self.headers['Cookie'] = cookie_header + + def prepare_hooks(self, hooks): + """Prepares the given hooks.""" + # hooks can be passed as None to the prepare method and to this + # method. To prevent iterating over None, simply use an empty list + # if hooks is False-y + hooks = hooks or [] + for event in hooks: + self.register_hook(event, hooks[event]) + + +class Response(object): + """The :class:`Response <Response>` object, which contains a + server's response to an HTTP request. + """ + + __attrs__ = [ + '_content', 'status_code', 'headers', 'url', 'history', + 'encoding', 'reason', 'cookies', 'elapsed', 'request' + ] + + def __init__(self): + super(Response, self).__init__() + + self._content = False + self._content_consumed = False + + #: Integer Code of responded HTTP Status, e.g. 404 or 200. + self.status_code = None + + #: Case-insensitive Dictionary of Response Headers. + #: For example, ``headers['content-encoding']`` will return the + #: value of a ``'Content-Encoding'`` response header. + self.headers = CaseInsensitiveDict() + + #: File-like object representation of response (for advanced usage). + #: Use of ``raw`` requires that ``stream=True`` be set on the request. + # This requirement does not apply for use internally to Requests. + self.raw = None + + #: Final URL location of Response. + self.url = None + + #: Encoding to decode with when accessing r.text. + self.encoding = None + + #: A list of :class:`Response <Response>` objects from + #: the history of the Request. Any redirect responses will end + #: up here. The list is sorted from the oldest to the most recent request. + self.history = [] + + #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK". + self.reason = None + + #: A CookieJar of Cookies the server sent back. + self.cookies = cookiejar_from_dict({}) + + #: The amount of time elapsed between sending the request + #: and the arrival of the response (as a timedelta). + #: This property specifically measures the time taken between sending + #: the first byte of the request and finishing parsing the headers. It + #: is therefore unaffected by consuming the response content or the + #: value of the ``stream`` keyword argument. + self.elapsed = datetime.timedelta(0) + + #: The :class:`PreparedRequest <PreparedRequest>` object to which this + #: is a response. + self.request = None + + def __getstate__(self): + # Consume everything; accessing the content attribute makes + # sure the content has been fully read. + if not self._content_consumed: + self.content + + return dict( + (attr, getattr(self, attr, None)) + for attr in self.__attrs__ + ) + + def __setstate__(self, state): + for name, value in state.items(): + setattr(self, name, value) + + # pickled objects do not have .raw + setattr(self, '_content_consumed', True) + setattr(self, 'raw', None) + + def __repr__(self): + return '<Response [%s]>' % (self.status_code) + + def __bool__(self): + """Returns true if :attr:`status_code` is 'OK'.""" + return self.ok + + def __nonzero__(self): + """Returns true if :attr:`status_code` is 'OK'.""" + return self.ok + + def __iter__(self): + """Allows you to use a response as an iterator.""" + return self.iter_content(128) + + @property + def ok(self): + try: + self.raise_for_status() + except HTTPError: + return False + return True + + @property + def is_redirect(self): + """True if this Response is a well-formed HTTP redirect that could have + been processed automatically (by :meth:`Session.resolve_redirects`). + """ + return ('location' in self.headers and self.status_code in REDIRECT_STATI) + + @property + def is_permanent_redirect(self): + """True if this Response one of the permanent versions of redirect""" + return ('location' in self.headers and self.status_code in (codes.moved_permanently, codes.permanent_redirect)) + + @property + def apparent_encoding(self): + """The apparent encoding, provided by the chardet library""" + return chardet.detect(self.content)['encoding'] + + def iter_content(self, chunk_size=1, decode_unicode=False): + """Iterates over the response data. When stream=True is set on the + request, this avoids reading the content at once into memory for + large responses. The chunk size is the number of bytes it should + read into memory. This is not necessarily the length of each item + returned as decoding can take place. + + chunk_size must be of type int or None. A value of None will + function differently depending on the value of `stream`. + stream=True will read data as it arrives in whatever size the + chunks are received. If stream=False, data is returned as + a single chunk. + + If decode_unicode is True, content will be decoded using the best + available encoding based on the response. + """ + + def generate(): + # Special case for urllib3. + if hasattr(self.raw, 'stream'): + try: + for chunk in self.raw.stream(chunk_size, decode_content=True): + yield chunk + except ProtocolError as e: + raise ChunkedEncodingError(e) + except DecodeError as e: + raise ContentDecodingError(e) + except ReadTimeoutError as e: + raise ConnectionError(e) + else: + # Standard file-like object. + while True: + chunk = self.raw.read(chunk_size) + if not chunk: + break + yield chunk + + self._content_consumed = True + + if self._content_consumed and isinstance(self._content, bool): + raise StreamConsumedError() + elif chunk_size is not None and not isinstance(chunk_size, int): + raise TypeError("chunk_size must be an int, it is instead a %s." % type(chunk_size)) + # simulate reading small chunks of the content + reused_chunks = iter_slices(self._content, chunk_size) + + stream_chunks = generate() + + chunks = reused_chunks if self._content_consumed else stream_chunks + + if decode_unicode: + chunks = stream_decode_response_unicode(chunks, self) + + return chunks + + def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None, delimiter=None): + """Iterates over the response data, one line at a time. When + stream=True is set on the request, this avoids reading the + content at once into memory for large responses. + + .. note:: This method is not reentrant safe. + """ + + pending = None + + for chunk in self.iter_content(chunk_size=chunk_size, decode_unicode=decode_unicode): + + if pending is not None: + chunk = pending + chunk + + if delimiter: + lines = chunk.split(delimiter) + else: + lines = chunk.splitlines() + + if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]: + pending = lines.pop() + else: + pending = None + + for line in lines: + yield line + + if pending is not None: + yield pending + + @property + def content(self): + """Content of the response, in bytes.""" + + if self._content is False: + # Read the contents. + try: + if self._content_consumed: + raise RuntimeError( + 'The content for this response was already consumed') + + if self.status_code == 0: + self._content = None + else: + self._content = bytes().join(self.iter_content(CONTENT_CHUNK_SIZE)) or bytes() + + except AttributeError: + self._content = None + + self._content_consumed = True + # don't need to release the connection; that's been handled by urllib3 + # since we exhausted the data. + return self._content + + @property + def text(self): + """Content of the response, in unicode. + + If Response.encoding is None, encoding will be guessed using + ``chardet``. + + The encoding of the response content is determined based solely on HTTP + headers, following RFC 2616 to the letter. If you can take advantage of + non-HTTP knowledge to make a better guess at the encoding, you should + set ``r.encoding`` appropriately before accessing this property. + """ + + # Try charset from content-type + content = None + encoding = self.encoding + + if not self.content: + return str('') + + # Fallback to auto-detected encoding. + if self.encoding is None: + encoding = self.apparent_encoding + + # Decode unicode from given encoding. + try: + content = str(self.content, encoding, errors='replace') + except (LookupError, TypeError): + # A LookupError is raised if the encoding was not found which could + # indicate a misspelling or similar mistake. + # + # A TypeError can be raised if encoding is None + # + # So we try blindly encoding. + content = str(self.content, errors='replace') + + return content + + def json(self, **kwargs): + """Returns the json-encoded content of a response, if any. + + :param \*\*kwargs: Optional arguments that ``json.loads`` takes. + """ + + if not self.encoding and self.content and len(self.content) > 3: + # No encoding set. JSON RFC 4627 section 3 states we should expect + # UTF-8, -16 or -32. Detect which one to use; If the detection or + # decoding fails, fall back to `self.text` (using chardet to make + # a best guess). + encoding = guess_json_utf(self.content) + if encoding is not None: + try: + return complexjson.loads( + self.content.decode(encoding), **kwargs + ) + except UnicodeDecodeError: + # Wrong UTF codec detected; usually because it's not UTF-8 + # but some other 8-bit codec. This is an RFC violation, + # and the server didn't bother to tell us what codec *was* + # used. + pass + return complexjson.loads(self.text, **kwargs) + + @property + def links(self): + """Returns the parsed header links of the response, if any.""" + + header = self.headers.get('link') + + # l = MultiDict() + l = {} + + if header: + links = parse_header_links(header) + + for link in links: + key = link.get('rel') or link.get('url') + l[key] = link + + return l + + def raise_for_status(self): + """Raises stored :class:`HTTPError`, if one occurred.""" + + http_error_msg = '' + if isinstance(self.reason, bytes): + reason = self.reason.decode('utf-8', 'ignore') + else: + reason = self.reason + + if 400 <= self.status_code < 500: + http_error_msg = u'%s Client Error: %s for url: %s' % (self.status_code, reason, self.url) + + elif 500 <= self.status_code < 600: + http_error_msg = u'%s Server Error: %s for url: %s' % (self.status_code, reason, self.url) + + if http_error_msg: + raise HTTPError(http_error_msg, response=self) + + def close(self): + """Releases the connection back to the pool. Once this method has been + called the underlying ``raw`` object must not be accessed again. + + *Note: Should not normally need to be called explicitly.* + """ + if not self._content_consumed: + self.raw.close() + + return self.raw.release_conn() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/__init__.py new file mode 100644 index 0000000..971c2ad --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/__init__.py @@ -0,0 +1,36 @@ +''' +Debian and other distributions "unbundle" requests' vendored dependencies, and +rewrite all imports to use the global versions of ``urllib3`` and ``chardet``. +The problem with this is that not only requests itself imports those +dependencies, but third-party code outside of the distros' control too. + +In reaction to these problems, the distro maintainers replaced +``requests.packages`` with a magical "stub module" that imports the correct +modules. The implementations were varying in quality and all had severe +problems. For example, a symlink (or hardlink) that links the correct modules +into place introduces problems regarding object identity, since you now have +two modules in `sys.modules` with the same API, but different identities:: + + requests.packages.urllib3 is not urllib3 + +With version ``2.5.2``, requests started to maintain its own stub, so that +distro-specific breakage would be reduced to a minimum, even though the whole +issue is not requests' fault in the first place. See +https://github.com/kennethreitz/requests/pull/2375 for the corresponding pull +request. +''' + +from __future__ import absolute_import +import sys + +try: + from . import urllib3 +except ImportError: + import urllib3 + sys.modules['%s.urllib3' % __name__] = urllib3 + +try: + from . import chardet +except ImportError: + import chardet + sys.modules['%s.chardet' % __name__] = chardet diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/__init__.py new file mode 100644 index 0000000..82c2a48 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/__init__.py @@ -0,0 +1,32 @@ +######################## BEGIN LICENSE BLOCK ######################## +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +__version__ = "2.3.0" +from sys import version_info + + +def detect(aBuf): + if ((version_info < (3, 0) and isinstance(aBuf, unicode)) or + (version_info >= (3, 0) and not isinstance(aBuf, bytes))): + raise ValueError('Expected a bytes object, not a unicode object') + + from . import universaldetector + u = universaldetector.UniversalDetector() + u.reset() + u.feed(aBuf) + u.close() + return u.result diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/big5freq.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/big5freq.py new file mode 100644 index 0000000..65bffc0 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/big5freq.py @@ -0,0 +1,925 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Big5 frequency table +# by Taiwan's Mandarin Promotion Council +# <http://www.edu.tw:81/mandr/> +# +# 128 --> 0.42261 +# 256 --> 0.57851 +# 512 --> 0.74851 +# 1024 --> 0.89384 +# 2048 --> 0.97583 +# +# Ideal Distribution Ratio = 0.74851/(1-0.74851) =2.98 +# Random Distribution Ration = 512/(5401-512)=0.105 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR + +BIG5_TYPICAL_DISTRIBUTION_RATIO = 0.75 + +#Char to FreqOrder table +BIG5_TABLE_SIZE = 5376 + +Big5CharToFreqOrder = ( + 1,1801,1506, 255,1431, 198, 9, 82, 6,5008, 177, 202,3681,1256,2821, 110, # 16 +3814, 33,3274, 261, 76, 44,2114, 16,2946,2187,1176, 659,3971, 26,3451,2653, # 32 +1198,3972,3350,4202, 410,2215, 302, 590, 361,1964, 8, 204, 58,4510,5009,1932, # 48 + 63,5010,5011, 317,1614, 75, 222, 159,4203,2417,1480,5012,3555,3091, 224,2822, # 64 +3682, 3, 10,3973,1471, 29,2787,1135,2866,1940, 873, 130,3275,1123, 312,5013, # 80 +4511,2052, 507, 252, 682,5014, 142,1915, 124, 206,2947, 34,3556,3204, 64, 604, # 96 +5015,2501,1977,1978, 155,1991, 645, 641,1606,5016,3452, 337, 72, 406,5017, 80, # 112 + 630, 238,3205,1509, 263, 939,1092,2654, 756,1440,1094,3453, 449, 69,2987, 591, # 128 + 179,2096, 471, 115,2035,1844, 60, 50,2988, 134, 806,1869, 734,2036,3454, 180, # 144 + 995,1607, 156, 537,2907, 688,5018, 319,1305, 779,2145, 514,2379, 298,4512, 359, # 160 +2502, 90,2716,1338, 663, 11, 906,1099,2553, 20,2441, 182, 532,1716,5019, 732, # 176 +1376,4204,1311,1420,3206, 25,2317,1056, 113, 399, 382,1950, 242,3455,2474, 529, # 192 +3276, 475,1447,3683,5020, 117, 21, 656, 810,1297,2300,2334,3557,5021, 126,4205, # 208 + 706, 456, 150, 613,4513, 71,1118,2037,4206, 145,3092, 85, 835, 486,2115,1246, # 224 +1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,5022,2128,2359, 347,3815, 221, # 240 +3558,3135,5023,1956,1153,4207, 83, 296,1199,3093, 192, 624, 93,5024, 822,1898, # 256 +2823,3136, 795,2065, 991,1554,1542,1592, 27, 43,2867, 859, 139,1456, 860,4514, # 272 + 437, 712,3974, 164,2397,3137, 695, 211,3037,2097, 195,3975,1608,3559,3560,3684, # 288 +3976, 234, 811,2989,2098,3977,2233,1441,3561,1615,2380, 668,2077,1638, 305, 228, # 304 +1664,4515, 467, 415,5025, 262,2099,1593, 239, 108, 300, 200,1033, 512,1247,2078, # 320 +5026,5027,2176,3207,3685,2682, 593, 845,1062,3277, 88,1723,2038,3978,1951, 212, # 336 + 266, 152, 149, 468,1899,4208,4516, 77, 187,5028,3038, 37, 5,2990,5029,3979, # 352 +5030,5031, 39,2524,4517,2908,3208,2079, 55, 148, 74,4518, 545, 483,1474,1029, # 368 +1665, 217,1870,1531,3138,1104,2655,4209, 24, 172,3562, 900,3980,3563,3564,4519, # 384 + 32,1408,2824,1312, 329, 487,2360,2251,2717, 784,2683, 4,3039,3351,1427,1789, # 400 + 188, 109, 499,5032,3686,1717,1790, 888,1217,3040,4520,5033,3565,5034,3352,1520, # 416 +3687,3981, 196,1034, 775,5035,5036, 929,1816, 249, 439, 38,5037,1063,5038, 794, # 432 +3982,1435,2301, 46, 178,3278,2066,5039,2381,5040, 214,1709,4521, 804, 35, 707, # 448 + 324,3688,1601,2554, 140, 459,4210,5041,5042,1365, 839, 272, 978,2262,2580,3456, # 464 +2129,1363,3689,1423, 697, 100,3094, 48, 70,1231, 495,3139,2196,5043,1294,5044, # 480 +2080, 462, 586,1042,3279, 853, 256, 988, 185,2382,3457,1698, 434,1084,5045,3458, # 496 + 314,2625,2788,4522,2335,2336, 569,2285, 637,1817,2525, 757,1162,1879,1616,3459, # 512 + 287,1577,2116, 768,4523,1671,2868,3566,2526,1321,3816, 909,2418,5046,4211, 933, # 528 +3817,4212,2053,2361,1222,4524, 765,2419,1322, 786,4525,5047,1920,1462,1677,2909, # 544 +1699,5048,4526,1424,2442,3140,3690,2600,3353,1775,1941,3460,3983,4213, 309,1369, # 560 +1130,2825, 364,2234,1653,1299,3984,3567,3985,3986,2656, 525,1085,3041, 902,2001, # 576 +1475, 964,4527, 421,1845,1415,1057,2286, 940,1364,3141, 376,4528,4529,1381, 7, # 592 +2527, 983,2383, 336,1710,2684,1846, 321,3461, 559,1131,3042,2752,1809,1132,1313, # 608 + 265,1481,1858,5049, 352,1203,2826,3280, 167,1089, 420,2827, 776, 792,1724,3568, # 624 +4214,2443,3281,5050,4215,5051, 446, 229, 333,2753, 901,3818,1200,1557,4530,2657, # 640 +1921, 395,2754,2685,3819,4216,1836, 125, 916,3209,2626,4531,5052,5053,3820,5054, # 656 +5055,5056,4532,3142,3691,1133,2555,1757,3462,1510,2318,1409,3569,5057,2146, 438, # 672 +2601,2910,2384,3354,1068, 958,3043, 461, 311,2869,2686,4217,1916,3210,4218,1979, # 688 + 383, 750,2755,2627,4219, 274, 539, 385,1278,1442,5058,1154,1965, 384, 561, 210, # 704 + 98,1295,2556,3570,5059,1711,2420,1482,3463,3987,2911,1257, 129,5060,3821, 642, # 720 + 523,2789,2790,2658,5061, 141,2235,1333, 68, 176, 441, 876, 907,4220, 603,2602, # 736 + 710, 171,3464, 404, 549, 18,3143,2398,1410,3692,1666,5062,3571,4533,2912,4534, # 752 +5063,2991, 368,5064, 146, 366, 99, 871,3693,1543, 748, 807,1586,1185, 22,2263, # 768 + 379,3822,3211,5065,3212, 505,1942,2628,1992,1382,2319,5066, 380,2362, 218, 702, # 784 +1818,1248,3465,3044,3572,3355,3282,5067,2992,3694, 930,3283,3823,5068, 59,5069, # 800 + 585, 601,4221, 497,3466,1112,1314,4535,1802,5070,1223,1472,2177,5071, 749,1837, # 816 + 690,1900,3824,1773,3988,1476, 429,1043,1791,2236,2117, 917,4222, 447,1086,1629, # 832 +5072, 556,5073,5074,2021,1654, 844,1090, 105, 550, 966,1758,2828,1008,1783, 686, # 848 +1095,5075,2287, 793,1602,5076,3573,2603,4536,4223,2948,2302,4537,3825, 980,2503, # 864 + 544, 353, 527,4538, 908,2687,2913,5077, 381,2629,1943,1348,5078,1341,1252, 560, # 880 +3095,5079,3467,2870,5080,2054, 973, 886,2081, 143,4539,5081,5082, 157,3989, 496, # 896 +4224, 57, 840, 540,2039,4540,4541,3468,2118,1445, 970,2264,1748,1966,2082,4225, # 912 +3144,1234,1776,3284,2829,3695, 773,1206,2130,1066,2040,1326,3990,1738,1725,4226, # 928 + 279,3145, 51,1544,2604, 423,1578,2131,2067, 173,4542,1880,5083,5084,1583, 264, # 944 + 610,3696,4543,2444, 280, 154,5085,5086,5087,1739, 338,1282,3096, 693,2871,1411, # 960 +1074,3826,2445,5088,4544,5089,5090,1240, 952,2399,5091,2914,1538,2688, 685,1483, # 976 +4227,2475,1436, 953,4228,2055,4545, 671,2400, 79,4229,2446,3285, 608, 567,2689, # 992 +3469,4230,4231,1691, 393,1261,1792,2401,5092,4546,5093,5094,5095,5096,1383,1672, # 1008 +3827,3213,1464, 522,1119, 661,1150, 216, 675,4547,3991,1432,3574, 609,4548,2690, # 1024 +2402,5097,5098,5099,4232,3045, 0,5100,2476, 315, 231,2447, 301,3356,4549,2385, # 1040 +5101, 233,4233,3697,1819,4550,4551,5102, 96,1777,1315,2083,5103, 257,5104,1810, # 1056 +3698,2718,1139,1820,4234,2022,1124,2164,2791,1778,2659,5105,3097, 363,1655,3214, # 1072 +5106,2993,5107,5108,5109,3992,1567,3993, 718, 103,3215, 849,1443, 341,3357,2949, # 1088 +1484,5110,1712, 127, 67, 339,4235,2403, 679,1412, 821,5111,5112, 834, 738, 351, # 1104 +2994,2147, 846, 235,1497,1881, 418,1993,3828,2719, 186,1100,2148,2756,3575,1545, # 1120 +1355,2950,2872,1377, 583,3994,4236,2581,2995,5113,1298,3699,1078,2557,3700,2363, # 1136 + 78,3829,3830, 267,1289,2100,2002,1594,4237, 348, 369,1274,2197,2178,1838,4552, # 1152 +1821,2830,3701,2757,2288,2003,4553,2951,2758, 144,3358, 882,4554,3995,2759,3470, # 1168 +4555,2915,5114,4238,1726, 320,5115,3996,3046, 788,2996,5116,2831,1774,1327,2873, # 1184 +3997,2832,5117,1306,4556,2004,1700,3831,3576,2364,2660, 787,2023, 506, 824,3702, # 1200 + 534, 323,4557,1044,3359,2024,1901, 946,3471,5118,1779,1500,1678,5119,1882,4558, # 1216 + 165, 243,4559,3703,2528, 123, 683,4239, 764,4560, 36,3998,1793, 589,2916, 816, # 1232 + 626,1667,3047,2237,1639,1555,1622,3832,3999,5120,4000,2874,1370,1228,1933, 891, # 1248 +2084,2917, 304,4240,5121, 292,2997,2720,3577, 691,2101,4241,1115,4561, 118, 662, # 1264 +5122, 611,1156, 854,2386,1316,2875, 2, 386, 515,2918,5123,5124,3286, 868,2238, # 1280 +1486, 855,2661, 785,2216,3048,5125,1040,3216,3578,5126,3146, 448,5127,1525,5128, # 1296 +2165,4562,5129,3833,5130,4242,2833,3579,3147, 503, 818,4001,3148,1568, 814, 676, # 1312 +1444, 306,1749,5131,3834,1416,1030, 197,1428, 805,2834,1501,4563,5132,5133,5134, # 1328 +1994,5135,4564,5136,5137,2198, 13,2792,3704,2998,3149,1229,1917,5138,3835,2132, # 1344 +5139,4243,4565,2404,3580,5140,2217,1511,1727,1120,5141,5142, 646,3836,2448, 307, # 1360 +5143,5144,1595,3217,5145,5146,5147,3705,1113,1356,4002,1465,2529,2530,5148, 519, # 1376 +5149, 128,2133, 92,2289,1980,5150,4003,1512, 342,3150,2199,5151,2793,2218,1981, # 1392 +3360,4244, 290,1656,1317, 789, 827,2365,5152,3837,4566, 562, 581,4004,5153, 401, # 1408 +4567,2252, 94,4568,5154,1399,2794,5155,1463,2025,4569,3218,1944,5156, 828,1105, # 1424 +4245,1262,1394,5157,4246, 605,4570,5158,1784,2876,5159,2835, 819,2102, 578,2200, # 1440 +2952,5160,1502, 436,3287,4247,3288,2836,4005,2919,3472,3473,5161,2721,2320,5162, # 1456 +5163,2337,2068, 23,4571, 193, 826,3838,2103, 699,1630,4248,3098, 390,1794,1064, # 1472 +3581,5164,1579,3099,3100,1400,5165,4249,1839,1640,2877,5166,4572,4573, 137,4250, # 1488 + 598,3101,1967, 780, 104, 974,2953,5167, 278, 899, 253, 402, 572, 504, 493,1339, # 1504 +5168,4006,1275,4574,2582,2558,5169,3706,3049,3102,2253, 565,1334,2722, 863, 41, # 1520 +5170,5171,4575,5172,1657,2338, 19, 463,2760,4251, 606,5173,2999,3289,1087,2085, # 1536 +1323,2662,3000,5174,1631,1623,1750,4252,2691,5175,2878, 791,2723,2663,2339, 232, # 1552 +2421,5176,3001,1498,5177,2664,2630, 755,1366,3707,3290,3151,2026,1609, 119,1918, # 1568 +3474, 862,1026,4253,5178,4007,3839,4576,4008,4577,2265,1952,2477,5179,1125, 817, # 1584 +4254,4255,4009,1513,1766,2041,1487,4256,3050,3291,2837,3840,3152,5180,5181,1507, # 1600 +5182,2692, 733, 40,1632,1106,2879, 345,4257, 841,2531, 230,4578,3002,1847,3292, # 1616 +3475,5183,1263, 986,3476,5184, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562, # 1632 +4010,4011,2954, 967,2761,2665,1349, 592,2134,1692,3361,3003,1995,4258,1679,4012, # 1648 +1902,2188,5185, 739,3708,2724,1296,1290,5186,4259,2201,2202,1922,1563,2605,2559, # 1664 +1871,2762,3004,5187, 435,5188, 343,1108, 596, 17,1751,4579,2239,3477,3709,5189, # 1680 +4580, 294,3582,2955,1693, 477, 979, 281,2042,3583, 643,2043,3710,2631,2795,2266, # 1696 +1031,2340,2135,2303,3584,4581, 367,1249,2560,5190,3585,5191,4582,1283,3362,2005, # 1712 + 240,1762,3363,4583,4584, 836,1069,3153, 474,5192,2149,2532, 268,3586,5193,3219, # 1728 +1521,1284,5194,1658,1546,4260,5195,3587,3588,5196,4261,3364,2693,1685,4262, 961, # 1744 +1673,2632, 190,2006,2203,3841,4585,4586,5197, 570,2504,3711,1490,5198,4587,2633, # 1760 +3293,1957,4588, 584,1514, 396,1045,1945,5199,4589,1968,2449,5200,5201,4590,4013, # 1776 + 619,5202,3154,3294, 215,2007,2796,2561,3220,4591,3221,4592, 763,4263,3842,4593, # 1792 +5203,5204,1958,1767,2956,3365,3712,1174, 452,1477,4594,3366,3155,5205,2838,1253, # 1808 +2387,2189,1091,2290,4264, 492,5206, 638,1169,1825,2136,1752,4014, 648, 926,1021, # 1824 +1324,4595, 520,4596, 997, 847,1007, 892,4597,3843,2267,1872,3713,2405,1785,4598, # 1840 +1953,2957,3103,3222,1728,4265,2044,3714,4599,2008,1701,3156,1551, 30,2268,4266, # 1856 +5207,2027,4600,3589,5208, 501,5209,4267, 594,3478,2166,1822,3590,3479,3591,3223, # 1872 + 829,2839,4268,5210,1680,3157,1225,4269,5211,3295,4601,4270,3158,2341,5212,4602, # 1888 +4271,5213,4015,4016,5214,1848,2388,2606,3367,5215,4603, 374,4017, 652,4272,4273, # 1904 + 375,1140, 798,5216,5217,5218,2366,4604,2269, 546,1659, 138,3051,2450,4605,5219, # 1920 +2254, 612,1849, 910, 796,3844,1740,1371, 825,3845,3846,5220,2920,2562,5221, 692, # 1936 + 444,3052,2634, 801,4606,4274,5222,1491, 244,1053,3053,4275,4276, 340,5223,4018, # 1952 +1041,3005, 293,1168, 87,1357,5224,1539, 959,5225,2240, 721, 694,4277,3847, 219, # 1968 +1478, 644,1417,3368,2666,1413,1401,1335,1389,4019,5226,5227,3006,2367,3159,1826, # 1984 + 730,1515, 184,2840, 66,4607,5228,1660,2958, 246,3369, 378,1457, 226,3480, 975, # 2000 +4020,2959,1264,3592, 674, 696,5229, 163,5230,1141,2422,2167, 713,3593,3370,4608, # 2016 +4021,5231,5232,1186, 15,5233,1079,1070,5234,1522,3224,3594, 276,1050,2725, 758, # 2032 +1126, 653,2960,3296,5235,2342, 889,3595,4022,3104,3007, 903,1250,4609,4023,3481, # 2048 +3596,1342,1681,1718, 766,3297, 286, 89,2961,3715,5236,1713,5237,2607,3371,3008, # 2064 +5238,2962,2219,3225,2880,5239,4610,2505,2533, 181, 387,1075,4024, 731,2190,3372, # 2080 +5240,3298, 310, 313,3482,2304, 770,4278, 54,3054, 189,4611,3105,3848,4025,5241, # 2096 +1230,1617,1850, 355,3597,4279,4612,3373, 111,4280,3716,1350,3160,3483,3055,4281, # 2112 +2150,3299,3598,5242,2797,4026,4027,3009, 722,2009,5243,1071, 247,1207,2343,2478, # 2128 +1378,4613,2010, 864,1437,1214,4614, 373,3849,1142,2220, 667,4615, 442,2763,2563, # 2144 +3850,4028,1969,4282,3300,1840, 837, 170,1107, 934,1336,1883,5244,5245,2119,4283, # 2160 +2841, 743,1569,5246,4616,4284, 582,2389,1418,3484,5247,1803,5248, 357,1395,1729, # 2176 +3717,3301,2423,1564,2241,5249,3106,3851,1633,4617,1114,2086,4285,1532,5250, 482, # 2192 +2451,4618,5251,5252,1492, 833,1466,5253,2726,3599,1641,2842,5254,1526,1272,3718, # 2208 +4286,1686,1795, 416,2564,1903,1954,1804,5255,3852,2798,3853,1159,2321,5256,2881, # 2224 +4619,1610,1584,3056,2424,2764, 443,3302,1163,3161,5257,5258,4029,5259,4287,2506, # 2240 +3057,4620,4030,3162,2104,1647,3600,2011,1873,4288,5260,4289, 431,3485,5261, 250, # 2256 + 97, 81,4290,5262,1648,1851,1558, 160, 848,5263, 866, 740,1694,5264,2204,2843, # 2272 +3226,4291,4621,3719,1687, 950,2479, 426, 469,3227,3720,3721,4031,5265,5266,1188, # 2288 + 424,1996, 861,3601,4292,3854,2205,2694, 168,1235,3602,4293,5267,2087,1674,4622, # 2304 +3374,3303, 220,2565,1009,5268,3855, 670,3010, 332,1208, 717,5269,5270,3603,2452, # 2320 +4032,3375,5271, 513,5272,1209,2882,3376,3163,4623,1080,5273,5274,5275,5276,2534, # 2336 +3722,3604, 815,1587,4033,4034,5277,3605,3486,3856,1254,4624,1328,3058,1390,4035, # 2352 +1741,4036,3857,4037,5278, 236,3858,2453,3304,5279,5280,3723,3859,1273,3860,4625, # 2368 +5281, 308,5282,4626, 245,4627,1852,2480,1307,2583, 430, 715,2137,2454,5283, 270, # 2384 + 199,2883,4038,5284,3606,2727,1753, 761,1754, 725,1661,1841,4628,3487,3724,5285, # 2400 +5286, 587, 14,3305, 227,2608, 326, 480,2270, 943,2765,3607, 291, 650,1884,5287, # 2416 +1702,1226, 102,1547, 62,3488, 904,4629,3489,1164,4294,5288,5289,1224,1548,2766, # 2432 + 391, 498,1493,5290,1386,1419,5291,2056,1177,4630, 813, 880,1081,2368, 566,1145, # 2448 +4631,2291,1001,1035,2566,2609,2242, 394,1286,5292,5293,2069,5294, 86,1494,1730, # 2464 +4039, 491,1588, 745, 897,2963, 843,3377,4040,2767,2884,3306,1768, 998,2221,2070, # 2480 + 397,1827,1195,1970,3725,3011,3378, 284,5295,3861,2507,2138,2120,1904,5296,4041, # 2496 +2151,4042,4295,1036,3490,1905, 114,2567,4296, 209,1527,5297,5298,2964,2844,2635, # 2512 +2390,2728,3164, 812,2568,5299,3307,5300,1559, 737,1885,3726,1210, 885, 28,2695, # 2528 +3608,3862,5301,4297,1004,1780,4632,5302, 346,1982,2222,2696,4633,3863,1742, 797, # 2544 +1642,4043,1934,1072,1384,2152, 896,4044,3308,3727,3228,2885,3609,5303,2569,1959, # 2560 +4634,2455,1786,5304,5305,5306,4045,4298,1005,1308,3728,4299,2729,4635,4636,1528, # 2576 +2610, 161,1178,4300,1983, 987,4637,1101,4301, 631,4046,1157,3229,2425,1343,1241, # 2592 +1016,2243,2570, 372, 877,2344,2508,1160, 555,1935, 911,4047,5307, 466,1170, 169, # 2608 +1051,2921,2697,3729,2481,3012,1182,2012,2571,1251,2636,5308, 992,2345,3491,1540, # 2624 +2730,1201,2071,2406,1997,2482,5309,4638, 528,1923,2191,1503,1874,1570,2369,3379, # 2640 +3309,5310, 557,1073,5311,1828,3492,2088,2271,3165,3059,3107, 767,3108,2799,4639, # 2656 +1006,4302,4640,2346,1267,2179,3730,3230, 778,4048,3231,2731,1597,2667,5312,4641, # 2672 +5313,3493,5314,5315,5316,3310,2698,1433,3311, 131, 95,1504,4049, 723,4303,3166, # 2688 +1842,3610,2768,2192,4050,2028,2105,3731,5317,3013,4051,1218,5318,3380,3232,4052, # 2704 +4304,2584, 248,1634,3864, 912,5319,2845,3732,3060,3865, 654, 53,5320,3014,5321, # 2720 +1688,4642, 777,3494,1032,4053,1425,5322, 191, 820,2121,2846, 971,4643, 931,3233, # 2736 + 135, 664, 783,3866,1998, 772,2922,1936,4054,3867,4644,2923,3234, 282,2732, 640, # 2752 +1372,3495,1127, 922, 325,3381,5323,5324, 711,2045,5325,5326,4055,2223,2800,1937, # 2768 +4056,3382,2224,2255,3868,2305,5327,4645,3869,1258,3312,4057,3235,2139,2965,4058, # 2784 +4059,5328,2225, 258,3236,4646, 101,1227,5329,3313,1755,5330,1391,3314,5331,2924, # 2800 +2057, 893,5332,5333,5334,1402,4305,2347,5335,5336,3237,3611,5337,5338, 878,1325, # 2816 +1781,2801,4647, 259,1385,2585, 744,1183,2272,4648,5339,4060,2509,5340, 684,1024, # 2832 +4306,5341, 472,3612,3496,1165,3315,4061,4062, 322,2153, 881, 455,1695,1152,1340, # 2848 + 660, 554,2154,4649,1058,4650,4307, 830,1065,3383,4063,4651,1924,5342,1703,1919, # 2864 +5343, 932,2273, 122,5344,4652, 947, 677,5345,3870,2637, 297,1906,1925,2274,4653, # 2880 +2322,3316,5346,5347,4308,5348,4309, 84,4310, 112, 989,5349, 547,1059,4064, 701, # 2896 +3613,1019,5350,4311,5351,3497, 942, 639, 457,2306,2456, 993,2966, 407, 851, 494, # 2912 +4654,3384, 927,5352,1237,5353,2426,3385, 573,4312, 680, 921,2925,1279,1875, 285, # 2928 + 790,1448,1984, 719,2168,5354,5355,4655,4065,4066,1649,5356,1541, 563,5357,1077, # 2944 +5358,3386,3061,3498, 511,3015,4067,4068,3733,4069,1268,2572,3387,3238,4656,4657, # 2960 +5359, 535,1048,1276,1189,2926,2029,3167,1438,1373,2847,2967,1134,2013,5360,4313, # 2976 +1238,2586,3109,1259,5361, 700,5362,2968,3168,3734,4314,5363,4315,1146,1876,1907, # 2992 +4658,2611,4070, 781,2427, 132,1589, 203, 147, 273,2802,2407, 898,1787,2155,4071, # 3008 +4072,5364,3871,2803,5365,5366,4659,4660,5367,3239,5368,1635,3872, 965,5369,1805, # 3024 +2699,1516,3614,1121,1082,1329,3317,4073,1449,3873, 65,1128,2848,2927,2769,1590, # 3040 +3874,5370,5371, 12,2668, 45, 976,2587,3169,4661, 517,2535,1013,1037,3240,5372, # 3056 +3875,2849,5373,3876,5374,3499,5375,2612, 614,1999,2323,3877,3110,2733,2638,5376, # 3072 +2588,4316, 599,1269,5377,1811,3735,5378,2700,3111, 759,1060, 489,1806,3388,3318, # 3088 +1358,5379,5380,2391,1387,1215,2639,2256, 490,5381,5382,4317,1759,2392,2348,5383, # 3104 +4662,3878,1908,4074,2640,1807,3241,4663,3500,3319,2770,2349, 874,5384,5385,3501, # 3120 +3736,1859, 91,2928,3737,3062,3879,4664,5386,3170,4075,2669,5387,3502,1202,1403, # 3136 +3880,2969,2536,1517,2510,4665,3503,2511,5388,4666,5389,2701,1886,1495,1731,4076, # 3152 +2370,4667,5390,2030,5391,5392,4077,2702,1216, 237,2589,4318,2324,4078,3881,4668, # 3168 +4669,2703,3615,3504, 445,4670,5393,5394,5395,5396,2771, 61,4079,3738,1823,4080, # 3184 +5397, 687,2046, 935, 925, 405,2670, 703,1096,1860,2734,4671,4081,1877,1367,2704, # 3200 +3389, 918,2106,1782,2483, 334,3320,1611,1093,4672, 564,3171,3505,3739,3390, 945, # 3216 +2641,2058,4673,5398,1926, 872,4319,5399,3506,2705,3112, 349,4320,3740,4082,4674, # 3232 +3882,4321,3741,2156,4083,4675,4676,4322,4677,2408,2047, 782,4084, 400, 251,4323, # 3248 +1624,5400,5401, 277,3742, 299,1265, 476,1191,3883,2122,4324,4325,1109, 205,5402, # 3264 +2590,1000,2157,3616,1861,5403,5404,5405,4678,5406,4679,2573, 107,2484,2158,4085, # 3280 +3507,3172,5407,1533, 541,1301, 158, 753,4326,2886,3617,5408,1696, 370,1088,4327, # 3296 +4680,3618, 579, 327, 440, 162,2244, 269,1938,1374,3508, 968,3063, 56,1396,3113, # 3312 +2107,3321,3391,5409,1927,2159,4681,3016,5410,3619,5411,5412,3743,4682,2485,5413, # 3328 +2804,5414,1650,4683,5415,2613,5416,5417,4086,2671,3392,1149,3393,4087,3884,4088, # 3344 +5418,1076, 49,5419, 951,3242,3322,3323, 450,2850, 920,5420,1812,2805,2371,4328, # 3360 +1909,1138,2372,3885,3509,5421,3243,4684,1910,1147,1518,2428,4685,3886,5422,4686, # 3376 +2393,2614, 260,1796,3244,5423,5424,3887,3324, 708,5425,3620,1704,5426,3621,1351, # 3392 +1618,3394,3017,1887, 944,4329,3395,4330,3064,3396,4331,5427,3744, 422, 413,1714, # 3408 +3325, 500,2059,2350,4332,2486,5428,1344,1911, 954,5429,1668,5430,5431,4089,2409, # 3424 +4333,3622,3888,4334,5432,2307,1318,2512,3114, 133,3115,2887,4687, 629, 31,2851, # 3440 +2706,3889,4688, 850, 949,4689,4090,2970,1732,2089,4335,1496,1853,5433,4091, 620, # 3456 +3245, 981,1242,3745,3397,1619,3746,1643,3326,2140,2457,1971,1719,3510,2169,5434, # 3472 +3246,5435,5436,3398,1829,5437,1277,4690,1565,2048,5438,1636,3623,3116,5439, 869, # 3488 +2852, 655,3890,3891,3117,4092,3018,3892,1310,3624,4691,5440,5441,5442,1733, 558, # 3504 +4692,3747, 335,1549,3065,1756,4336,3748,1946,3511,1830,1291,1192, 470,2735,2108, # 3520 +2806, 913,1054,4093,5443,1027,5444,3066,4094,4693, 982,2672,3399,3173,3512,3247, # 3536 +3248,1947,2807,5445, 571,4694,5446,1831,5447,3625,2591,1523,2429,5448,2090, 984, # 3552 +4695,3749,1960,5449,3750, 852, 923,2808,3513,3751, 969,1519, 999,2049,2325,1705, # 3568 +5450,3118, 615,1662, 151, 597,4095,2410,2326,1049, 275,4696,3752,4337, 568,3753, # 3584 +3626,2487,4338,3754,5451,2430,2275, 409,3249,5452,1566,2888,3514,1002, 769,2853, # 3600 + 194,2091,3174,3755,2226,3327,4339, 628,1505,5453,5454,1763,2180,3019,4096, 521, # 3616 +1161,2592,1788,2206,2411,4697,4097,1625,4340,4341, 412, 42,3119, 464,5455,2642, # 3632 +4698,3400,1760,1571,2889,3515,2537,1219,2207,3893,2643,2141,2373,4699,4700,3328, # 3648 +1651,3401,3627,5456,5457,3628,2488,3516,5458,3756,5459,5460,2276,2092, 460,5461, # 3664 +4701,5462,3020, 962, 588,3629, 289,3250,2644,1116, 52,5463,3067,1797,5464,5465, # 3680 +5466,1467,5467,1598,1143,3757,4342,1985,1734,1067,4702,1280,3402, 465,4703,1572, # 3696 + 510,5468,1928,2245,1813,1644,3630,5469,4704,3758,5470,5471,2673,1573,1534,5472, # 3712 +5473, 536,1808,1761,3517,3894,3175,2645,5474,5475,5476,4705,3518,2929,1912,2809, # 3728 +5477,3329,1122, 377,3251,5478, 360,5479,5480,4343,1529, 551,5481,2060,3759,1769, # 3744 +2431,5482,2930,4344,3330,3120,2327,2109,2031,4706,1404, 136,1468,1479, 672,1171, # 3760 +3252,2308, 271,3176,5483,2772,5484,2050, 678,2736, 865,1948,4707,5485,2014,4098, # 3776 +2971,5486,2737,2227,1397,3068,3760,4708,4709,1735,2931,3403,3631,5487,3895, 509, # 3792 +2854,2458,2890,3896,5488,5489,3177,3178,4710,4345,2538,4711,2309,1166,1010, 552, # 3808 + 681,1888,5490,5491,2972,2973,4099,1287,1596,1862,3179, 358, 453, 736, 175, 478, # 3824 +1117, 905,1167,1097,5492,1854,1530,5493,1706,5494,2181,3519,2292,3761,3520,3632, # 3840 +4346,2093,4347,5495,3404,1193,2489,4348,1458,2193,2208,1863,1889,1421,3331,2932, # 3856 +3069,2182,3521, 595,2123,5496,4100,5497,5498,4349,1707,2646, 223,3762,1359, 751, # 3872 +3121, 183,3522,5499,2810,3021, 419,2374, 633, 704,3897,2394, 241,5500,5501,5502, # 3888 + 838,3022,3763,2277,2773,2459,3898,1939,2051,4101,1309,3122,2246,1181,5503,1136, # 3904 +2209,3899,2375,1446,4350,2310,4712,5504,5505,4351,1055,2615, 484,3764,5506,4102, # 3920 + 625,4352,2278,3405,1499,4353,4103,5507,4104,4354,3253,2279,2280,3523,5508,5509, # 3936 +2774, 808,2616,3765,3406,4105,4355,3123,2539, 526,3407,3900,4356, 955,5510,1620, # 3952 +4357,2647,2432,5511,1429,3766,1669,1832, 994, 928,5512,3633,1260,5513,5514,5515, # 3968 +1949,2293, 741,2933,1626,4358,2738,2460, 867,1184, 362,3408,1392,5516,5517,4106, # 3984 +4359,1770,1736,3254,2934,4713,4714,1929,2707,1459,1158,5518,3070,3409,2891,1292, # 4000 +1930,2513,2855,3767,1986,1187,2072,2015,2617,4360,5519,2574,2514,2170,3768,2490, # 4016 +3332,5520,3769,4715,5521,5522, 666,1003,3023,1022,3634,4361,5523,4716,1814,2257, # 4032 + 574,3901,1603, 295,1535, 705,3902,4362, 283, 858, 417,5524,5525,3255,4717,4718, # 4048 +3071,1220,1890,1046,2281,2461,4107,1393,1599, 689,2575, 388,4363,5526,2491, 802, # 4064 +5527,2811,3903,2061,1405,2258,5528,4719,3904,2110,1052,1345,3256,1585,5529, 809, # 4080 +5530,5531,5532, 575,2739,3524, 956,1552,1469,1144,2328,5533,2329,1560,2462,3635, # 4096 +3257,4108, 616,2210,4364,3180,2183,2294,5534,1833,5535,3525,4720,5536,1319,3770, # 4112 +3771,1211,3636,1023,3258,1293,2812,5537,5538,5539,3905, 607,2311,3906, 762,2892, # 4128 +1439,4365,1360,4721,1485,3072,5540,4722,1038,4366,1450,2062,2648,4367,1379,4723, # 4144 +2593,5541,5542,4368,1352,1414,2330,2935,1172,5543,5544,3907,3908,4724,1798,1451, # 4160 +5545,5546,5547,5548,2936,4109,4110,2492,2351, 411,4111,4112,3637,3333,3124,4725, # 4176 +1561,2674,1452,4113,1375,5549,5550, 47,2974, 316,5551,1406,1591,2937,3181,5552, # 4192 +1025,2142,3125,3182, 354,2740, 884,2228,4369,2412, 508,3772, 726,3638, 996,2433, # 4208 +3639, 729,5553, 392,2194,1453,4114,4726,3773,5554,5555,2463,3640,2618,1675,2813, # 4224 + 919,2352,2975,2353,1270,4727,4115, 73,5556,5557, 647,5558,3259,2856,2259,1550, # 4240 +1346,3024,5559,1332, 883,3526,5560,5561,5562,5563,3334,2775,5564,1212, 831,1347, # 4256 +4370,4728,2331,3909,1864,3073, 720,3910,4729,4730,3911,5565,4371,5566,5567,4731, # 4272 +5568,5569,1799,4732,3774,2619,4733,3641,1645,2376,4734,5570,2938, 669,2211,2675, # 4288 +2434,5571,2893,5572,5573,1028,3260,5574,4372,2413,5575,2260,1353,5576,5577,4735, # 4304 +3183, 518,5578,4116,5579,4373,1961,5580,2143,4374,5581,5582,3025,2354,2355,3912, # 4320 + 516,1834,1454,4117,2708,4375,4736,2229,2620,1972,1129,3642,5583,2776,5584,2976, # 4336 +1422, 577,1470,3026,1524,3410,5585,5586, 432,4376,3074,3527,5587,2594,1455,2515, # 4352 +2230,1973,1175,5588,1020,2741,4118,3528,4737,5589,2742,5590,1743,1361,3075,3529, # 4368 +2649,4119,4377,4738,2295, 895, 924,4378,2171, 331,2247,3076, 166,1627,3077,1098, # 4384 +5591,1232,2894,2231,3411,4739, 657, 403,1196,2377, 542,3775,3412,1600,4379,3530, # 4400 +5592,4740,2777,3261, 576, 530,1362,4741,4742,2540,2676,3776,4120,5593, 842,3913, # 4416 +5594,2814,2032,1014,4121, 213,2709,3413, 665, 621,4380,5595,3777,2939,2435,5596, # 4432 +2436,3335,3643,3414,4743,4381,2541,4382,4744,3644,1682,4383,3531,1380,5597, 724, # 4448 +2282, 600,1670,5598,1337,1233,4745,3126,2248,5599,1621,4746,5600, 651,4384,5601, # 4464 +1612,4385,2621,5602,2857,5603,2743,2312,3078,5604, 716,2464,3079, 174,1255,2710, # 4480 +4122,3645, 548,1320,1398, 728,4123,1574,5605,1891,1197,3080,4124,5606,3081,3082, # 4496 +3778,3646,3779, 747,5607, 635,4386,4747,5608,5609,5610,4387,5611,5612,4748,5613, # 4512 +3415,4749,2437, 451,5614,3780,2542,2073,4388,2744,4389,4125,5615,1764,4750,5616, # 4528 +4390, 350,4751,2283,2395,2493,5617,4391,4126,2249,1434,4127, 488,4752, 458,4392, # 4544 +4128,3781, 771,1330,2396,3914,2576,3184,2160,2414,1553,2677,3185,4393,5618,2494, # 4560 +2895,2622,1720,2711,4394,3416,4753,5619,2543,4395,5620,3262,4396,2778,5621,2016, # 4576 +2745,5622,1155,1017,3782,3915,5623,3336,2313, 201,1865,4397,1430,5624,4129,5625, # 4592 +5626,5627,5628,5629,4398,1604,5630, 414,1866, 371,2595,4754,4755,3532,2017,3127, # 4608 +4756,1708, 960,4399, 887, 389,2172,1536,1663,1721,5631,2232,4130,2356,2940,1580, # 4624 +5632,5633,1744,4757,2544,4758,4759,5634,4760,5635,2074,5636,4761,3647,3417,2896, # 4640 +4400,5637,4401,2650,3418,2815, 673,2712,2465, 709,3533,4131,3648,4402,5638,1148, # 4656 + 502, 634,5639,5640,1204,4762,3649,1575,4763,2623,3783,5641,3784,3128, 948,3263, # 4672 + 121,1745,3916,1110,5642,4403,3083,2516,3027,4132,3785,1151,1771,3917,1488,4133, # 4688 +1987,5643,2438,3534,5644,5645,2094,5646,4404,3918,1213,1407,2816, 531,2746,2545, # 4704 +3264,1011,1537,4764,2779,4405,3129,1061,5647,3786,3787,1867,2897,5648,2018, 120, # 4720 +4406,4407,2063,3650,3265,2314,3919,2678,3419,1955,4765,4134,5649,3535,1047,2713, # 4736 +1266,5650,1368,4766,2858, 649,3420,3920,2546,2747,1102,2859,2679,5651,5652,2000, # 4752 +5653,1111,3651,2977,5654,2495,3921,3652,2817,1855,3421,3788,5655,5656,3422,2415, # 4768 +2898,3337,3266,3653,5657,2577,5658,3654,2818,4135,1460, 856,5659,3655,5660,2899, # 4784 +2978,5661,2900,3922,5662,4408, 632,2517, 875,3923,1697,3924,2296,5663,5664,4767, # 4800 +3028,1239, 580,4768,4409,5665, 914, 936,2075,1190,4136,1039,2124,5666,5667,5668, # 4816 +5669,3423,1473,5670,1354,4410,3925,4769,2173,3084,4137, 915,3338,4411,4412,3339, # 4832 +1605,1835,5671,2748, 398,3656,4413,3926,4138, 328,1913,2860,4139,3927,1331,4414, # 4848 +3029, 937,4415,5672,3657,4140,4141,3424,2161,4770,3425, 524, 742, 538,3085,1012, # 4864 +5673,5674,3928,2466,5675, 658,1103, 225,3929,5676,5677,4771,5678,4772,5679,3267, # 4880 +1243,5680,4142, 963,2250,4773,5681,2714,3658,3186,5682,5683,2596,2332,5684,4774, # 4896 +5685,5686,5687,3536, 957,3426,2547,2033,1931,2941,2467, 870,2019,3659,1746,2780, # 4912 +2781,2439,2468,5688,3930,5689,3789,3130,3790,3537,3427,3791,5690,1179,3086,5691, # 4928 +3187,2378,4416,3792,2548,3188,3131,2749,4143,5692,3428,1556,2549,2297, 977,2901, # 4944 +2034,4144,1205,3429,5693,1765,3430,3189,2125,1271, 714,1689,4775,3538,5694,2333, # 4960 +3931, 533,4417,3660,2184, 617,5695,2469,3340,3539,2315,5696,5697,3190,5698,5699, # 4976 +3932,1988, 618, 427,2651,3540,3431,5700,5701,1244,1690,5702,2819,4418,4776,5703, # 4992 +3541,4777,5704,2284,1576, 473,3661,4419,3432, 972,5705,3662,5706,3087,5707,5708, # 5008 +4778,4779,5709,3793,4145,4146,5710, 153,4780, 356,5711,1892,2902,4420,2144, 408, # 5024 + 803,2357,5712,3933,5713,4421,1646,2578,2518,4781,4782,3934,5714,3935,4422,5715, # 5040 +2416,3433, 752,5716,5717,1962,3341,2979,5718, 746,3030,2470,4783,4423,3794, 698, # 5056 +4784,1893,4424,3663,2550,4785,3664,3936,5719,3191,3434,5720,1824,1302,4147,2715, # 5072 +3937,1974,4425,5721,4426,3192, 823,1303,1288,1236,2861,3542,4148,3435, 774,3938, # 5088 +5722,1581,4786,1304,2862,3939,4787,5723,2440,2162,1083,3268,4427,4149,4428, 344, # 5104 +1173, 288,2316, 454,1683,5724,5725,1461,4788,4150,2597,5726,5727,4789, 985, 894, # 5120 +5728,3436,3193,5729,1914,2942,3795,1989,5730,2111,1975,5731,4151,5732,2579,1194, # 5136 + 425,5733,4790,3194,1245,3796,4429,5734,5735,2863,5736, 636,4791,1856,3940, 760, # 5152 +1800,5737,4430,2212,1508,4792,4152,1894,1684,2298,5738,5739,4793,4431,4432,2213, # 5168 + 479,5740,5741, 832,5742,4153,2496,5743,2980,2497,3797, 990,3132, 627,1815,2652, # 5184 +4433,1582,4434,2126,2112,3543,4794,5744, 799,4435,3195,5745,4795,2113,1737,3031, # 5200 +1018, 543, 754,4436,3342,1676,4796,4797,4154,4798,1489,5746,3544,5747,2624,2903, # 5216 +4155,5748,5749,2981,5750,5751,5752,5753,3196,4799,4800,2185,1722,5754,3269,3270, # 5232 +1843,3665,1715, 481, 365,1976,1857,5755,5756,1963,2498,4801,5757,2127,3666,3271, # 5248 + 433,1895,2064,2076,5758, 602,2750,5759,5760,5761,5762,5763,3032,1628,3437,5764, # 5264 +3197,4802,4156,2904,4803,2519,5765,2551,2782,5766,5767,5768,3343,4804,2905,5769, # 5280 +4805,5770,2864,4806,4807,1221,2982,4157,2520,5771,5772,5773,1868,1990,5774,5775, # 5296 +5776,1896,5777,5778,4808,1897,4158, 318,5779,2095,4159,4437,5780,5781, 485,5782, # 5312 + 938,3941, 553,2680, 116,5783,3942,3667,5784,3545,2681,2783,3438,3344,2820,5785, # 5328 +3668,2943,4160,1747,2944,2983,5786,5787, 207,5788,4809,5789,4810,2521,5790,3033, # 5344 + 890,3669,3943,5791,1878,3798,3439,5792,2186,2358,3440,1652,5793,5794,5795, 941, # 5360 +2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, # 5376 #last 512 +#Everything below is of no interest for detection purpose +2522,1613,4812,5799,3345,3945,2523,5800,4162,5801,1637,4163,2471,4813,3946,5802, # 5392 +2500,3034,3800,5803,5804,2195,4814,5805,2163,5806,5807,5808,5809,5810,5811,5812, # 5408 +5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824,5825,5826,5827,5828, # 5424 +5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840,5841,5842,5843,5844, # 5440 +5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856,5857,5858,5859,5860, # 5456 +5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872,5873,5874,5875,5876, # 5472 +5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888,5889,5890,5891,5892, # 5488 +5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904,5905,5906,5907,5908, # 5504 +5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920,5921,5922,5923,5924, # 5520 +5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936,5937,5938,5939,5940, # 5536 +5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952,5953,5954,5955,5956, # 5552 +5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968,5969,5970,5971,5972, # 5568 +5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984,5985,5986,5987,5988, # 5584 +5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000,6001,6002,6003,6004, # 5600 +6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020, # 5616 +6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032,6033,6034,6035,6036, # 5632 +6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048,6049,6050,6051,6052, # 5648 +6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064,6065,6066,6067,6068, # 5664 +6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080,6081,6082,6083,6084, # 5680 +6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096,6097,6098,6099,6100, # 5696 +6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112,6113,6114,6115,6116, # 5712 +6117,6118,6119,6120,6121,6122,6123,6124,6125,6126,6127,6128,6129,6130,6131,6132, # 5728 +6133,6134,6135,6136,6137,6138,6139,6140,6141,6142,6143,6144,6145,6146,6147,6148, # 5744 +6149,6150,6151,6152,6153,6154,6155,6156,6157,6158,6159,6160,6161,6162,6163,6164, # 5760 +6165,6166,6167,6168,6169,6170,6171,6172,6173,6174,6175,6176,6177,6178,6179,6180, # 5776 +6181,6182,6183,6184,6185,6186,6187,6188,6189,6190,6191,6192,6193,6194,6195,6196, # 5792 +6197,6198,6199,6200,6201,6202,6203,6204,6205,6206,6207,6208,6209,6210,6211,6212, # 5808 +6213,6214,6215,6216,6217,6218,6219,6220,6221,6222,6223,3670,6224,6225,6226,6227, # 5824 +6228,6229,6230,6231,6232,6233,6234,6235,6236,6237,6238,6239,6240,6241,6242,6243, # 5840 +6244,6245,6246,6247,6248,6249,6250,6251,6252,6253,6254,6255,6256,6257,6258,6259, # 5856 +6260,6261,6262,6263,6264,6265,6266,6267,6268,6269,6270,6271,6272,6273,6274,6275, # 5872 +6276,6277,6278,6279,6280,6281,6282,6283,6284,6285,4815,6286,6287,6288,6289,6290, # 5888 +6291,6292,4816,6293,6294,6295,6296,6297,6298,6299,6300,6301,6302,6303,6304,6305, # 5904 +6306,6307,6308,6309,6310,6311,4817,4818,6312,6313,6314,6315,6316,6317,6318,4819, # 5920 +6319,6320,6321,6322,6323,6324,6325,6326,6327,6328,6329,6330,6331,6332,6333,6334, # 5936 +6335,6336,6337,4820,6338,6339,6340,6341,6342,6343,6344,6345,6346,6347,6348,6349, # 5952 +6350,6351,6352,6353,6354,6355,6356,6357,6358,6359,6360,6361,6362,6363,6364,6365, # 5968 +6366,6367,6368,6369,6370,6371,6372,6373,6374,6375,6376,6377,6378,6379,6380,6381, # 5984 +6382,6383,6384,6385,6386,6387,6388,6389,6390,6391,6392,6393,6394,6395,6396,6397, # 6000 +6398,6399,6400,6401,6402,6403,6404,6405,6406,6407,6408,6409,6410,3441,6411,6412, # 6016 +6413,6414,6415,6416,6417,6418,6419,6420,6421,6422,6423,6424,6425,4440,6426,6427, # 6032 +6428,6429,6430,6431,6432,6433,6434,6435,6436,6437,6438,6439,6440,6441,6442,6443, # 6048 +6444,6445,6446,6447,6448,6449,6450,6451,6452,6453,6454,4821,6455,6456,6457,6458, # 6064 +6459,6460,6461,6462,6463,6464,6465,6466,6467,6468,6469,6470,6471,6472,6473,6474, # 6080 +6475,6476,6477,3947,3948,6478,6479,6480,6481,3272,4441,6482,6483,6484,6485,4442, # 6096 +6486,6487,6488,6489,6490,6491,6492,6493,6494,6495,6496,4822,6497,6498,6499,6500, # 6112 +6501,6502,6503,6504,6505,6506,6507,6508,6509,6510,6511,6512,6513,6514,6515,6516, # 6128 +6517,6518,6519,6520,6521,6522,6523,6524,6525,6526,6527,6528,6529,6530,6531,6532, # 6144 +6533,6534,6535,6536,6537,6538,6539,6540,6541,6542,6543,6544,6545,6546,6547,6548, # 6160 +6549,6550,6551,6552,6553,6554,6555,6556,2784,6557,4823,6558,6559,6560,6561,6562, # 6176 +6563,6564,6565,6566,6567,6568,6569,3949,6570,6571,6572,4824,6573,6574,6575,6576, # 6192 +6577,6578,6579,6580,6581,6582,6583,4825,6584,6585,6586,3950,2785,6587,6588,6589, # 6208 +6590,6591,6592,6593,6594,6595,6596,6597,6598,6599,6600,6601,6602,6603,6604,6605, # 6224 +6606,6607,6608,6609,6610,6611,6612,4826,6613,6614,6615,4827,6616,6617,6618,6619, # 6240 +6620,6621,6622,6623,6624,6625,4164,6626,6627,6628,6629,6630,6631,6632,6633,6634, # 6256 +3547,6635,4828,6636,6637,6638,6639,6640,6641,6642,3951,2984,6643,6644,6645,6646, # 6272 +6647,6648,6649,4165,6650,4829,6651,6652,4830,6653,6654,6655,6656,6657,6658,6659, # 6288 +6660,6661,6662,4831,6663,6664,6665,6666,6667,6668,6669,6670,6671,4166,6672,4832, # 6304 +3952,6673,6674,6675,6676,4833,6677,6678,6679,4167,6680,6681,6682,3198,6683,6684, # 6320 +6685,6686,6687,6688,6689,6690,6691,6692,6693,6694,6695,6696,6697,4834,6698,6699, # 6336 +6700,6701,6702,6703,6704,6705,6706,6707,6708,6709,6710,6711,6712,6713,6714,6715, # 6352 +6716,6717,6718,6719,6720,6721,6722,6723,6724,6725,6726,6727,6728,6729,6730,6731, # 6368 +6732,6733,6734,4443,6735,6736,6737,6738,6739,6740,6741,6742,6743,6744,6745,4444, # 6384 +6746,6747,6748,6749,6750,6751,6752,6753,6754,6755,6756,6757,6758,6759,6760,6761, # 6400 +6762,6763,6764,6765,6766,6767,6768,6769,6770,6771,6772,6773,6774,6775,6776,6777, # 6416 +6778,6779,6780,6781,4168,6782,6783,3442,6784,6785,6786,6787,6788,6789,6790,6791, # 6432 +4169,6792,6793,6794,6795,6796,6797,6798,6799,6800,6801,6802,6803,6804,6805,6806, # 6448 +6807,6808,6809,6810,6811,4835,6812,6813,6814,4445,6815,6816,4446,6817,6818,6819, # 6464 +6820,6821,6822,6823,6824,6825,6826,6827,6828,6829,6830,6831,6832,6833,6834,6835, # 6480 +3548,6836,6837,6838,6839,6840,6841,6842,6843,6844,6845,6846,4836,6847,6848,6849, # 6496 +6850,6851,6852,6853,6854,3953,6855,6856,6857,6858,6859,6860,6861,6862,6863,6864, # 6512 +6865,6866,6867,6868,6869,6870,6871,6872,6873,6874,6875,6876,6877,3199,6878,6879, # 6528 +6880,6881,6882,4447,6883,6884,6885,6886,6887,6888,6889,6890,6891,6892,6893,6894, # 6544 +6895,6896,6897,6898,6899,6900,6901,6902,6903,6904,4170,6905,6906,6907,6908,6909, # 6560 +6910,6911,6912,6913,6914,6915,6916,6917,6918,6919,6920,6921,6922,6923,6924,6925, # 6576 +6926,6927,4837,6928,6929,6930,6931,6932,6933,6934,6935,6936,3346,6937,6938,4838, # 6592 +6939,6940,6941,4448,6942,6943,6944,6945,6946,4449,6947,6948,6949,6950,6951,6952, # 6608 +6953,6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6964,6965,6966,6967,6968, # 6624 +6969,6970,6971,6972,6973,6974,6975,6976,6977,6978,6979,6980,6981,6982,6983,6984, # 6640 +6985,6986,6987,6988,6989,6990,6991,6992,6993,6994,3671,6995,6996,6997,6998,4839, # 6656 +6999,7000,7001,7002,3549,7003,7004,7005,7006,7007,7008,7009,7010,7011,7012,7013, # 6672 +7014,7015,7016,7017,7018,7019,7020,7021,7022,7023,7024,7025,7026,7027,7028,7029, # 6688 +7030,4840,7031,7032,7033,7034,7035,7036,7037,7038,4841,7039,7040,7041,7042,7043, # 6704 +7044,7045,7046,7047,7048,7049,7050,7051,7052,7053,7054,7055,7056,7057,7058,7059, # 6720 +7060,7061,7062,7063,7064,7065,7066,7067,7068,7069,7070,2985,7071,7072,7073,7074, # 6736 +7075,7076,7077,7078,7079,7080,4842,7081,7082,7083,7084,7085,7086,7087,7088,7089, # 6752 +7090,7091,7092,7093,7094,7095,7096,7097,7098,7099,7100,7101,7102,7103,7104,7105, # 6768 +7106,7107,7108,7109,7110,7111,7112,7113,7114,7115,7116,7117,7118,4450,7119,7120, # 6784 +7121,7122,7123,7124,7125,7126,7127,7128,7129,7130,7131,7132,7133,7134,7135,7136, # 6800 +7137,7138,7139,7140,7141,7142,7143,4843,7144,7145,7146,7147,7148,7149,7150,7151, # 6816 +7152,7153,7154,7155,7156,7157,7158,7159,7160,7161,7162,7163,7164,7165,7166,7167, # 6832 +7168,7169,7170,7171,7172,7173,7174,7175,7176,7177,7178,7179,7180,7181,7182,7183, # 6848 +7184,7185,7186,7187,7188,4171,4172,7189,7190,7191,7192,7193,7194,7195,7196,7197, # 6864 +7198,7199,7200,7201,7202,7203,7204,7205,7206,7207,7208,7209,7210,7211,7212,7213, # 6880 +7214,7215,7216,7217,7218,7219,7220,7221,7222,7223,7224,7225,7226,7227,7228,7229, # 6896 +7230,7231,7232,7233,7234,7235,7236,7237,7238,7239,7240,7241,7242,7243,7244,7245, # 6912 +7246,7247,7248,7249,7250,7251,7252,7253,7254,7255,7256,7257,7258,7259,7260,7261, # 6928 +7262,7263,7264,7265,7266,7267,7268,7269,7270,7271,7272,7273,7274,7275,7276,7277, # 6944 +7278,7279,7280,7281,7282,7283,7284,7285,7286,7287,7288,7289,7290,7291,7292,7293, # 6960 +7294,7295,7296,4844,7297,7298,7299,7300,7301,7302,7303,7304,7305,7306,7307,7308, # 6976 +7309,7310,7311,7312,7313,7314,7315,7316,4451,7317,7318,7319,7320,7321,7322,7323, # 6992 +7324,7325,7326,7327,7328,7329,7330,7331,7332,7333,7334,7335,7336,7337,7338,7339, # 7008 +7340,7341,7342,7343,7344,7345,7346,7347,7348,7349,7350,7351,7352,7353,4173,7354, # 7024 +7355,4845,7356,7357,7358,7359,7360,7361,7362,7363,7364,7365,7366,7367,7368,7369, # 7040 +7370,7371,7372,7373,7374,7375,7376,7377,7378,7379,7380,7381,7382,7383,7384,7385, # 7056 +7386,7387,7388,4846,7389,7390,7391,7392,7393,7394,7395,7396,7397,7398,7399,7400, # 7072 +7401,7402,7403,7404,7405,3672,7406,7407,7408,7409,7410,7411,7412,7413,7414,7415, # 7088 +7416,7417,7418,7419,7420,7421,7422,7423,7424,7425,7426,7427,7428,7429,7430,7431, # 7104 +7432,7433,7434,7435,7436,7437,7438,7439,7440,7441,7442,7443,7444,7445,7446,7447, # 7120 +7448,7449,7450,7451,7452,7453,4452,7454,3200,7455,7456,7457,7458,7459,7460,7461, # 7136 +7462,7463,7464,7465,7466,7467,7468,7469,7470,7471,7472,7473,7474,4847,7475,7476, # 7152 +7477,3133,7478,7479,7480,7481,7482,7483,7484,7485,7486,7487,7488,7489,7490,7491, # 7168 +7492,7493,7494,7495,7496,7497,7498,7499,7500,7501,7502,3347,7503,7504,7505,7506, # 7184 +7507,7508,7509,7510,7511,7512,7513,7514,7515,7516,7517,7518,7519,7520,7521,4848, # 7200 +7522,7523,7524,7525,7526,7527,7528,7529,7530,7531,7532,7533,7534,7535,7536,7537, # 7216 +7538,7539,7540,7541,7542,7543,7544,7545,7546,7547,7548,7549,3801,4849,7550,7551, # 7232 +7552,7553,7554,7555,7556,7557,7558,7559,7560,7561,7562,7563,7564,7565,7566,7567, # 7248 +7568,7569,3035,7570,7571,7572,7573,7574,7575,7576,7577,7578,7579,7580,7581,7582, # 7264 +7583,7584,7585,7586,7587,7588,7589,7590,7591,7592,7593,7594,7595,7596,7597,7598, # 7280 +7599,7600,7601,7602,7603,7604,7605,7606,7607,7608,7609,7610,7611,7612,7613,7614, # 7296 +7615,7616,4850,7617,7618,3802,7619,7620,7621,7622,7623,7624,7625,7626,7627,7628, # 7312 +7629,7630,7631,7632,4851,7633,7634,7635,7636,7637,7638,7639,7640,7641,7642,7643, # 7328 +7644,7645,7646,7647,7648,7649,7650,7651,7652,7653,7654,7655,7656,7657,7658,7659, # 7344 +7660,7661,7662,7663,7664,7665,7666,7667,7668,7669,7670,4453,7671,7672,7673,7674, # 7360 +7675,7676,7677,7678,7679,7680,7681,7682,7683,7684,7685,7686,7687,7688,7689,7690, # 7376 +7691,7692,7693,7694,7695,7696,7697,3443,7698,7699,7700,7701,7702,4454,7703,7704, # 7392 +7705,7706,7707,7708,7709,7710,7711,7712,7713,2472,7714,7715,7716,7717,7718,7719, # 7408 +7720,7721,7722,7723,7724,7725,7726,7727,7728,7729,7730,7731,3954,7732,7733,7734, # 7424 +7735,7736,7737,7738,7739,7740,7741,7742,7743,7744,7745,7746,7747,7748,7749,7750, # 7440 +3134,7751,7752,4852,7753,7754,7755,4853,7756,7757,7758,7759,7760,4174,7761,7762, # 7456 +7763,7764,7765,7766,7767,7768,7769,7770,7771,7772,7773,7774,7775,7776,7777,7778, # 7472 +7779,7780,7781,7782,7783,7784,7785,7786,7787,7788,7789,7790,7791,7792,7793,7794, # 7488 +7795,7796,7797,7798,7799,7800,7801,7802,7803,7804,7805,4854,7806,7807,7808,7809, # 7504 +7810,7811,7812,7813,7814,7815,7816,7817,7818,7819,7820,7821,7822,7823,7824,7825, # 7520 +4855,7826,7827,7828,7829,7830,7831,7832,7833,7834,7835,7836,7837,7838,7839,7840, # 7536 +7841,7842,7843,7844,7845,7846,7847,3955,7848,7849,7850,7851,7852,7853,7854,7855, # 7552 +7856,7857,7858,7859,7860,3444,7861,7862,7863,7864,7865,7866,7867,7868,7869,7870, # 7568 +7871,7872,7873,7874,7875,7876,7877,7878,7879,7880,7881,7882,7883,7884,7885,7886, # 7584 +7887,7888,7889,7890,7891,4175,7892,7893,7894,7895,7896,4856,4857,7897,7898,7899, # 7600 +7900,2598,7901,7902,7903,7904,7905,7906,7907,7908,4455,7909,7910,7911,7912,7913, # 7616 +7914,3201,7915,7916,7917,7918,7919,7920,7921,4858,7922,7923,7924,7925,7926,7927, # 7632 +7928,7929,7930,7931,7932,7933,7934,7935,7936,7937,7938,7939,7940,7941,7942,7943, # 7648 +7944,7945,7946,7947,7948,7949,7950,7951,7952,7953,7954,7955,7956,7957,7958,7959, # 7664 +7960,7961,7962,7963,7964,7965,7966,7967,7968,7969,7970,7971,7972,7973,7974,7975, # 7680 +7976,7977,7978,7979,7980,7981,4859,7982,7983,7984,7985,7986,7987,7988,7989,7990, # 7696 +7991,7992,7993,7994,7995,7996,4860,7997,7998,7999,8000,8001,8002,8003,8004,8005, # 7712 +8006,8007,8008,8009,8010,8011,8012,8013,8014,8015,8016,4176,8017,8018,8019,8020, # 7728 +8021,8022,8023,4861,8024,8025,8026,8027,8028,8029,8030,8031,8032,8033,8034,8035, # 7744 +8036,4862,4456,8037,8038,8039,8040,4863,8041,8042,8043,8044,8045,8046,8047,8048, # 7760 +8049,8050,8051,8052,8053,8054,8055,8056,8057,8058,8059,8060,8061,8062,8063,8064, # 7776 +8065,8066,8067,8068,8069,8070,8071,8072,8073,8074,8075,8076,8077,8078,8079,8080, # 7792 +8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096, # 7808 +8097,8098,8099,4864,4177,8100,8101,8102,8103,8104,8105,8106,8107,8108,8109,8110, # 7824 +8111,8112,8113,8114,8115,8116,8117,8118,8119,8120,4178,8121,8122,8123,8124,8125, # 7840 +8126,8127,8128,8129,8130,8131,8132,8133,8134,8135,8136,8137,8138,8139,8140,8141, # 7856 +8142,8143,8144,8145,4865,4866,8146,8147,8148,8149,8150,8151,8152,8153,8154,8155, # 7872 +8156,8157,8158,8159,8160,8161,8162,8163,8164,8165,4179,8166,8167,8168,8169,8170, # 7888 +8171,8172,8173,8174,8175,8176,8177,8178,8179,8180,8181,4457,8182,8183,8184,8185, # 7904 +8186,8187,8188,8189,8190,8191,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201, # 7920 +8202,8203,8204,8205,8206,8207,8208,8209,8210,8211,8212,8213,8214,8215,8216,8217, # 7936 +8218,8219,8220,8221,8222,8223,8224,8225,8226,8227,8228,8229,8230,8231,8232,8233, # 7952 +8234,8235,8236,8237,8238,8239,8240,8241,8242,8243,8244,8245,8246,8247,8248,8249, # 7968 +8250,8251,8252,8253,8254,8255,8256,3445,8257,8258,8259,8260,8261,8262,4458,8263, # 7984 +8264,8265,8266,8267,8268,8269,8270,8271,8272,4459,8273,8274,8275,8276,3550,8277, # 8000 +8278,8279,8280,8281,8282,8283,8284,8285,8286,8287,8288,8289,4460,8290,8291,8292, # 8016 +8293,8294,8295,8296,8297,8298,8299,8300,8301,8302,8303,8304,8305,8306,8307,4867, # 8032 +8308,8309,8310,8311,8312,3551,8313,8314,8315,8316,8317,8318,8319,8320,8321,8322, # 8048 +8323,8324,8325,8326,4868,8327,8328,8329,8330,8331,8332,8333,8334,8335,8336,8337, # 8064 +8338,8339,8340,8341,8342,8343,8344,8345,8346,8347,8348,8349,8350,8351,8352,8353, # 8080 +8354,8355,8356,8357,8358,8359,8360,8361,8362,8363,4869,4461,8364,8365,8366,8367, # 8096 +8368,8369,8370,4870,8371,8372,8373,8374,8375,8376,8377,8378,8379,8380,8381,8382, # 8112 +8383,8384,8385,8386,8387,8388,8389,8390,8391,8392,8393,8394,8395,8396,8397,8398, # 8128 +8399,8400,8401,8402,8403,8404,8405,8406,8407,8408,8409,8410,4871,8411,8412,8413, # 8144 +8414,8415,8416,8417,8418,8419,8420,8421,8422,4462,8423,8424,8425,8426,8427,8428, # 8160 +8429,8430,8431,8432,8433,2986,8434,8435,8436,8437,8438,8439,8440,8441,8442,8443, # 8176 +8444,8445,8446,8447,8448,8449,8450,8451,8452,8453,8454,8455,8456,8457,8458,8459, # 8192 +8460,8461,8462,8463,8464,8465,8466,8467,8468,8469,8470,8471,8472,8473,8474,8475, # 8208 +8476,8477,8478,4180,8479,8480,8481,8482,8483,8484,8485,8486,8487,8488,8489,8490, # 8224 +8491,8492,8493,8494,8495,8496,8497,8498,8499,8500,8501,8502,8503,8504,8505,8506, # 8240 +8507,8508,8509,8510,8511,8512,8513,8514,8515,8516,8517,8518,8519,8520,8521,8522, # 8256 +8523,8524,8525,8526,8527,8528,8529,8530,8531,8532,8533,8534,8535,8536,8537,8538, # 8272 +8539,8540,8541,8542,8543,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554, # 8288 +8555,8556,8557,8558,8559,8560,8561,8562,8563,8564,4872,8565,8566,8567,8568,8569, # 8304 +8570,8571,8572,8573,4873,8574,8575,8576,8577,8578,8579,8580,8581,8582,8583,8584, # 8320 +8585,8586,8587,8588,8589,8590,8591,8592,8593,8594,8595,8596,8597,8598,8599,8600, # 8336 +8601,8602,8603,8604,8605,3803,8606,8607,8608,8609,8610,8611,8612,8613,4874,3804, # 8352 +8614,8615,8616,8617,8618,8619,8620,8621,3956,8622,8623,8624,8625,8626,8627,8628, # 8368 +8629,8630,8631,8632,8633,8634,8635,8636,8637,8638,2865,8639,8640,8641,8642,8643, # 8384 +8644,8645,8646,8647,8648,8649,8650,8651,8652,8653,8654,8655,8656,4463,8657,8658, # 8400 +8659,4875,4876,8660,8661,8662,8663,8664,8665,8666,8667,8668,8669,8670,8671,8672, # 8416 +8673,8674,8675,8676,8677,8678,8679,8680,8681,4464,8682,8683,8684,8685,8686,8687, # 8432 +8688,8689,8690,8691,8692,8693,8694,8695,8696,8697,8698,8699,8700,8701,8702,8703, # 8448 +8704,8705,8706,8707,8708,8709,2261,8710,8711,8712,8713,8714,8715,8716,8717,8718, # 8464 +8719,8720,8721,8722,8723,8724,8725,8726,8727,8728,8729,8730,8731,8732,8733,4181, # 8480 +8734,8735,8736,8737,8738,8739,8740,8741,8742,8743,8744,8745,8746,8747,8748,8749, # 8496 +8750,8751,8752,8753,8754,8755,8756,8757,8758,8759,8760,8761,8762,8763,4877,8764, # 8512 +8765,8766,8767,8768,8769,8770,8771,8772,8773,8774,8775,8776,8777,8778,8779,8780, # 8528 +8781,8782,8783,8784,8785,8786,8787,8788,4878,8789,4879,8790,8791,8792,4880,8793, # 8544 +8794,8795,8796,8797,8798,8799,8800,8801,4881,8802,8803,8804,8805,8806,8807,8808, # 8560 +8809,8810,8811,8812,8813,8814,8815,3957,8816,8817,8818,8819,8820,8821,8822,8823, # 8576 +8824,8825,8826,8827,8828,8829,8830,8831,8832,8833,8834,8835,8836,8837,8838,8839, # 8592 +8840,8841,8842,8843,8844,8845,8846,8847,4882,8848,8849,8850,8851,8852,8853,8854, # 8608 +8855,8856,8857,8858,8859,8860,8861,8862,8863,8864,8865,8866,8867,8868,8869,8870, # 8624 +8871,8872,8873,8874,8875,8876,8877,8878,8879,8880,8881,8882,8883,8884,3202,8885, # 8640 +8886,8887,8888,8889,8890,8891,8892,8893,8894,8895,8896,8897,8898,8899,8900,8901, # 8656 +8902,8903,8904,8905,8906,8907,8908,8909,8910,8911,8912,8913,8914,8915,8916,8917, # 8672 +8918,8919,8920,8921,8922,8923,8924,4465,8925,8926,8927,8928,8929,8930,8931,8932, # 8688 +4883,8933,8934,8935,8936,8937,8938,8939,8940,8941,8942,8943,2214,8944,8945,8946, # 8704 +8947,8948,8949,8950,8951,8952,8953,8954,8955,8956,8957,8958,8959,8960,8961,8962, # 8720 +8963,8964,8965,4884,8966,8967,8968,8969,8970,8971,8972,8973,8974,8975,8976,8977, # 8736 +8978,8979,8980,8981,8982,8983,8984,8985,8986,8987,8988,8989,8990,8991,8992,4885, # 8752 +8993,8994,8995,8996,8997,8998,8999,9000,9001,9002,9003,9004,9005,9006,9007,9008, # 8768 +9009,9010,9011,9012,9013,9014,9015,9016,9017,9018,9019,9020,9021,4182,9022,9023, # 8784 +9024,9025,9026,9027,9028,9029,9030,9031,9032,9033,9034,9035,9036,9037,9038,9039, # 8800 +9040,9041,9042,9043,9044,9045,9046,9047,9048,9049,9050,9051,9052,9053,9054,9055, # 8816 +9056,9057,9058,9059,9060,9061,9062,9063,4886,9064,9065,9066,9067,9068,9069,4887, # 8832 +9070,9071,9072,9073,9074,9075,9076,9077,9078,9079,9080,9081,9082,9083,9084,9085, # 8848 +9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9101, # 8864 +9102,9103,9104,9105,9106,9107,9108,9109,9110,9111,9112,9113,9114,9115,9116,9117, # 8880 +9118,9119,9120,9121,9122,9123,9124,9125,9126,9127,9128,9129,9130,9131,9132,9133, # 8896 +9134,9135,9136,9137,9138,9139,9140,9141,3958,9142,9143,9144,9145,9146,9147,9148, # 8912 +9149,9150,9151,4888,9152,9153,9154,9155,9156,9157,9158,9159,9160,9161,9162,9163, # 8928 +9164,9165,9166,9167,9168,9169,9170,9171,9172,9173,9174,9175,4889,9176,9177,9178, # 8944 +9179,9180,9181,9182,9183,9184,9185,9186,9187,9188,9189,9190,9191,9192,9193,9194, # 8960 +9195,9196,9197,9198,9199,9200,9201,9202,9203,4890,9204,9205,9206,9207,9208,9209, # 8976 +9210,9211,9212,9213,9214,9215,9216,9217,9218,9219,9220,9221,9222,4466,9223,9224, # 8992 +9225,9226,9227,9228,9229,9230,9231,9232,9233,9234,9235,9236,9237,9238,9239,9240, # 9008 +9241,9242,9243,9244,9245,4891,9246,9247,9248,9249,9250,9251,9252,9253,9254,9255, # 9024 +9256,9257,4892,9258,9259,9260,9261,4893,4894,9262,9263,9264,9265,9266,9267,9268, # 9040 +9269,9270,9271,9272,9273,4467,9274,9275,9276,9277,9278,9279,9280,9281,9282,9283, # 9056 +9284,9285,3673,9286,9287,9288,9289,9290,9291,9292,9293,9294,9295,9296,9297,9298, # 9072 +9299,9300,9301,9302,9303,9304,9305,9306,9307,9308,9309,9310,9311,9312,9313,9314, # 9088 +9315,9316,9317,9318,9319,9320,9321,9322,4895,9323,9324,9325,9326,9327,9328,9329, # 9104 +9330,9331,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,9342,9343,9344,9345, # 9120 +9346,9347,4468,9348,9349,9350,9351,9352,9353,9354,9355,9356,9357,9358,9359,9360, # 9136 +9361,9362,9363,9364,9365,9366,9367,9368,9369,9370,9371,9372,9373,4896,9374,4469, # 9152 +9375,9376,9377,9378,9379,4897,9380,9381,9382,9383,9384,9385,9386,9387,9388,9389, # 9168 +9390,9391,9392,9393,9394,9395,9396,9397,9398,9399,9400,9401,9402,9403,9404,9405, # 9184 +9406,4470,9407,2751,9408,9409,3674,3552,9410,9411,9412,9413,9414,9415,9416,9417, # 9200 +9418,9419,9420,9421,4898,9422,9423,9424,9425,9426,9427,9428,9429,3959,9430,9431, # 9216 +9432,9433,9434,9435,9436,4471,9437,9438,9439,9440,9441,9442,9443,9444,9445,9446, # 9232 +9447,9448,9449,9450,3348,9451,9452,9453,9454,9455,9456,9457,9458,9459,9460,9461, # 9248 +9462,9463,9464,9465,9466,9467,9468,9469,9470,9471,9472,4899,9473,9474,9475,9476, # 9264 +9477,4900,9478,9479,9480,9481,9482,9483,9484,9485,9486,9487,9488,3349,9489,9490, # 9280 +9491,9492,9493,9494,9495,9496,9497,9498,9499,9500,9501,9502,9503,9504,9505,9506, # 9296 +9507,9508,9509,9510,9511,9512,9513,9514,9515,9516,9517,9518,9519,9520,4901,9521, # 9312 +9522,9523,9524,9525,9526,4902,9527,9528,9529,9530,9531,9532,9533,9534,9535,9536, # 9328 +9537,9538,9539,9540,9541,9542,9543,9544,9545,9546,9547,9548,9549,9550,9551,9552, # 9344 +9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568, # 9360 +9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9581,9582,9583,9584, # 9376 +3805,9585,9586,9587,9588,9589,9590,9591,9592,9593,9594,9595,9596,9597,9598,9599, # 9392 +9600,9601,9602,4903,9603,9604,9605,9606,9607,4904,9608,9609,9610,9611,9612,9613, # 9408 +9614,4905,9615,9616,9617,9618,9619,9620,9621,9622,9623,9624,9625,9626,9627,9628, # 9424 +9629,9630,9631,9632,4906,9633,9634,9635,9636,9637,9638,9639,9640,9641,9642,9643, # 9440 +4907,9644,9645,9646,9647,9648,9649,9650,9651,9652,9653,9654,9655,9656,9657,9658, # 9456 +9659,9660,9661,9662,9663,9664,9665,9666,9667,9668,9669,9670,9671,9672,4183,9673, # 9472 +9674,9675,9676,9677,4908,9678,9679,9680,9681,4909,9682,9683,9684,9685,9686,9687, # 9488 +9688,9689,9690,4910,9691,9692,9693,3675,9694,9695,9696,2945,9697,9698,9699,9700, # 9504 +9701,9702,9703,9704,9705,4911,9706,9707,9708,9709,9710,9711,9712,9713,9714,9715, # 9520 +9716,9717,9718,9719,9720,9721,9722,9723,9724,9725,9726,9727,9728,9729,9730,9731, # 9536 +9732,9733,9734,9735,4912,9736,9737,9738,9739,9740,4913,9741,9742,9743,9744,9745, # 9552 +9746,9747,9748,9749,9750,9751,9752,9753,9754,9755,9756,9757,9758,4914,9759,9760, # 9568 +9761,9762,9763,9764,9765,9766,9767,9768,9769,9770,9771,9772,9773,9774,9775,9776, # 9584 +9777,9778,9779,9780,9781,9782,4915,9783,9784,9785,9786,9787,9788,9789,9790,9791, # 9600 +9792,9793,4916,9794,9795,9796,9797,9798,9799,9800,9801,9802,9803,9804,9805,9806, # 9616 +9807,9808,9809,9810,9811,9812,9813,9814,9815,9816,9817,9818,9819,9820,9821,9822, # 9632 +9823,9824,9825,9826,9827,9828,9829,9830,9831,9832,9833,9834,9835,9836,9837,9838, # 9648 +9839,9840,9841,9842,9843,9844,9845,9846,9847,9848,9849,9850,9851,9852,9853,9854, # 9664 +9855,9856,9857,9858,9859,9860,9861,9862,9863,9864,9865,9866,9867,9868,4917,9869, # 9680 +9870,9871,9872,9873,9874,9875,9876,9877,9878,9879,9880,9881,9882,9883,9884,9885, # 9696 +9886,9887,9888,9889,9890,9891,9892,4472,9893,9894,9895,9896,9897,3806,9898,9899, # 9712 +9900,9901,9902,9903,9904,9905,9906,9907,9908,9909,9910,9911,9912,9913,9914,4918, # 9728 +9915,9916,9917,4919,9918,9919,9920,9921,4184,9922,9923,9924,9925,9926,9927,9928, # 9744 +9929,9930,9931,9932,9933,9934,9935,9936,9937,9938,9939,9940,9941,9942,9943,9944, # 9760 +9945,9946,4920,9947,9948,9949,9950,9951,9952,9953,9954,9955,4185,9956,9957,9958, # 9776 +9959,9960,9961,9962,9963,9964,9965,4921,9966,9967,9968,4473,9969,9970,9971,9972, # 9792 +9973,9974,9975,9976,9977,4474,9978,9979,9980,9981,9982,9983,9984,9985,9986,9987, # 9808 +9988,9989,9990,9991,9992,9993,9994,9995,9996,9997,9998,9999,10000,10001,10002,10003, # 9824 +10004,10005,10006,10007,10008,10009,10010,10011,10012,10013,10014,10015,10016,10017,10018,10019, # 9840 +10020,10021,4922,10022,4923,10023,10024,10025,10026,10027,10028,10029,10030,10031,10032,10033, # 9856 +10034,10035,10036,10037,10038,10039,10040,10041,10042,10043,10044,10045,10046,10047,10048,4924, # 9872 +10049,10050,10051,10052,10053,10054,10055,10056,10057,10058,10059,10060,10061,10062,10063,10064, # 9888 +10065,10066,10067,10068,10069,10070,10071,10072,10073,10074,10075,10076,10077,10078,10079,10080, # 9904 +10081,10082,10083,10084,10085,10086,10087,4475,10088,10089,10090,10091,10092,10093,10094,10095, # 9920 +10096,10097,4476,10098,10099,10100,10101,10102,10103,10104,10105,10106,10107,10108,10109,10110, # 9936 +10111,2174,10112,10113,10114,10115,10116,10117,10118,10119,10120,10121,10122,10123,10124,10125, # 9952 +10126,10127,10128,10129,10130,10131,10132,10133,10134,10135,10136,10137,10138,10139,10140,3807, # 9968 +4186,4925,10141,10142,10143,10144,10145,10146,10147,4477,4187,10148,10149,10150,10151,10152, # 9984 +10153,4188,10154,10155,10156,10157,10158,10159,10160,10161,4926,10162,10163,10164,10165,10166, #10000 +10167,10168,10169,10170,10171,10172,10173,10174,10175,10176,10177,10178,10179,10180,10181,10182, #10016 +10183,10184,10185,10186,10187,10188,10189,10190,10191,10192,3203,10193,10194,10195,10196,10197, #10032 +10198,10199,10200,4478,10201,10202,10203,10204,4479,10205,10206,10207,10208,10209,10210,10211, #10048 +10212,10213,10214,10215,10216,10217,10218,10219,10220,10221,10222,10223,10224,10225,10226,10227, #10064 +10228,10229,10230,10231,10232,10233,10234,4927,10235,10236,10237,10238,10239,10240,10241,10242, #10080 +10243,10244,10245,10246,10247,10248,10249,10250,10251,10252,10253,10254,10255,10256,10257,10258, #10096 +10259,10260,10261,10262,10263,10264,10265,10266,10267,10268,10269,10270,10271,10272,10273,4480, #10112 +4928,4929,10274,10275,10276,10277,10278,10279,10280,10281,10282,10283,10284,10285,10286,10287, #10128 +10288,10289,10290,10291,10292,10293,10294,10295,10296,10297,10298,10299,10300,10301,10302,10303, #10144 +10304,10305,10306,10307,10308,10309,10310,10311,10312,10313,10314,10315,10316,10317,10318,10319, #10160 +10320,10321,10322,10323,10324,10325,10326,10327,10328,10329,10330,10331,10332,10333,10334,4930, #10176 +10335,10336,10337,10338,10339,10340,10341,10342,4931,10343,10344,10345,10346,10347,10348,10349, #10192 +10350,10351,10352,10353,10354,10355,3088,10356,2786,10357,10358,10359,10360,4189,10361,10362, #10208 +10363,10364,10365,10366,10367,10368,10369,10370,10371,10372,10373,10374,10375,4932,10376,10377, #10224 +10378,10379,10380,10381,10382,10383,10384,10385,10386,10387,10388,10389,10390,10391,10392,4933, #10240 +10393,10394,10395,4934,10396,10397,10398,10399,10400,10401,10402,10403,10404,10405,10406,10407, #10256 +10408,10409,10410,10411,10412,3446,10413,10414,10415,10416,10417,10418,10419,10420,10421,10422, #10272 +10423,4935,10424,10425,10426,10427,10428,10429,10430,4936,10431,10432,10433,10434,10435,10436, #10288 +10437,10438,10439,10440,10441,10442,10443,4937,10444,10445,10446,10447,4481,10448,10449,10450, #10304 +10451,10452,10453,10454,10455,10456,10457,10458,10459,10460,10461,10462,10463,10464,10465,10466, #10320 +10467,10468,10469,10470,10471,10472,10473,10474,10475,10476,10477,10478,10479,10480,10481,10482, #10336 +10483,10484,10485,10486,10487,10488,10489,10490,10491,10492,10493,10494,10495,10496,10497,10498, #10352 +10499,10500,10501,10502,10503,10504,10505,4938,10506,10507,10508,10509,10510,2552,10511,10512, #10368 +10513,10514,10515,10516,3447,10517,10518,10519,10520,10521,10522,10523,10524,10525,10526,10527, #10384 +10528,10529,10530,10531,10532,10533,10534,10535,10536,10537,10538,10539,10540,10541,10542,10543, #10400 +4482,10544,4939,10545,10546,10547,10548,10549,10550,10551,10552,10553,10554,10555,10556,10557, #10416 +10558,10559,10560,10561,10562,10563,10564,10565,10566,10567,3676,4483,10568,10569,10570,10571, #10432 +10572,3448,10573,10574,10575,10576,10577,10578,10579,10580,10581,10582,10583,10584,10585,10586, #10448 +10587,10588,10589,10590,10591,10592,10593,10594,10595,10596,10597,10598,10599,10600,10601,10602, #10464 +10603,10604,10605,10606,10607,10608,10609,10610,10611,10612,10613,10614,10615,10616,10617,10618, #10480 +10619,10620,10621,10622,10623,10624,10625,10626,10627,4484,10628,10629,10630,10631,10632,4940, #10496 +10633,10634,10635,10636,10637,10638,10639,10640,10641,10642,10643,10644,10645,10646,10647,10648, #10512 +10649,10650,10651,10652,10653,10654,10655,10656,4941,10657,10658,10659,2599,10660,10661,10662, #10528 +10663,10664,10665,10666,3089,10667,10668,10669,10670,10671,10672,10673,10674,10675,10676,10677, #10544 +10678,10679,10680,4942,10681,10682,10683,10684,10685,10686,10687,10688,10689,10690,10691,10692, #10560 +10693,10694,10695,10696,10697,4485,10698,10699,10700,10701,10702,10703,10704,4943,10705,3677, #10576 +10706,10707,10708,10709,10710,10711,10712,4944,10713,10714,10715,10716,10717,10718,10719,10720, #10592 +10721,10722,10723,10724,10725,10726,10727,10728,4945,10729,10730,10731,10732,10733,10734,10735, #10608 +10736,10737,10738,10739,10740,10741,10742,10743,10744,10745,10746,10747,10748,10749,10750,10751, #10624 +10752,10753,10754,10755,10756,10757,10758,10759,10760,10761,4946,10762,10763,10764,10765,10766, #10640 +10767,4947,4948,10768,10769,10770,10771,10772,10773,10774,10775,10776,10777,10778,10779,10780, #10656 +10781,10782,10783,10784,10785,10786,10787,10788,10789,10790,10791,10792,10793,10794,10795,10796, #10672 +10797,10798,10799,10800,10801,10802,10803,10804,10805,10806,10807,10808,10809,10810,10811,10812, #10688 +10813,10814,10815,10816,10817,10818,10819,10820,10821,10822,10823,10824,10825,10826,10827,10828, #10704 +10829,10830,10831,10832,10833,10834,10835,10836,10837,10838,10839,10840,10841,10842,10843,10844, #10720 +10845,10846,10847,10848,10849,10850,10851,10852,10853,10854,10855,10856,10857,10858,10859,10860, #10736 +10861,10862,10863,10864,10865,10866,10867,10868,10869,10870,10871,10872,10873,10874,10875,10876, #10752 +10877,10878,4486,10879,10880,10881,10882,10883,10884,10885,4949,10886,10887,10888,10889,10890, #10768 +10891,10892,10893,10894,10895,10896,10897,10898,10899,10900,10901,10902,10903,10904,10905,10906, #10784 +10907,10908,10909,10910,10911,10912,10913,10914,10915,10916,10917,10918,10919,4487,10920,10921, #10800 +10922,10923,10924,10925,10926,10927,10928,10929,10930,10931,10932,4950,10933,10934,10935,10936, #10816 +10937,10938,10939,10940,10941,10942,10943,10944,10945,10946,10947,10948,10949,4488,10950,10951, #10832 +10952,10953,10954,10955,10956,10957,10958,10959,4190,10960,10961,10962,10963,10964,10965,10966, #10848 +10967,10968,10969,10970,10971,10972,10973,10974,10975,10976,10977,10978,10979,10980,10981,10982, #10864 +10983,10984,10985,10986,10987,10988,10989,10990,10991,10992,10993,10994,10995,10996,10997,10998, #10880 +10999,11000,11001,11002,11003,11004,11005,11006,3960,11007,11008,11009,11010,11011,11012,11013, #10896 +11014,11015,11016,11017,11018,11019,11020,11021,11022,11023,11024,11025,11026,11027,11028,11029, #10912 +11030,11031,11032,4951,11033,11034,11035,11036,11037,11038,11039,11040,11041,11042,11043,11044, #10928 +11045,11046,11047,4489,11048,11049,11050,11051,4952,11052,11053,11054,11055,11056,11057,11058, #10944 +4953,11059,11060,11061,11062,11063,11064,11065,11066,11067,11068,11069,11070,11071,4954,11072, #10960 +11073,11074,11075,11076,11077,11078,11079,11080,11081,11082,11083,11084,11085,11086,11087,11088, #10976 +11089,11090,11091,11092,11093,11094,11095,11096,11097,11098,11099,11100,11101,11102,11103,11104, #10992 +11105,11106,11107,11108,11109,11110,11111,11112,11113,11114,11115,3808,11116,11117,11118,11119, #11008 +11120,11121,11122,11123,11124,11125,11126,11127,11128,11129,11130,11131,11132,11133,11134,4955, #11024 +11135,11136,11137,11138,11139,11140,11141,11142,11143,11144,11145,11146,11147,11148,11149,11150, #11040 +11151,11152,11153,11154,11155,11156,11157,11158,11159,11160,11161,4956,11162,11163,11164,11165, #11056 +11166,11167,11168,11169,11170,11171,11172,11173,11174,11175,11176,11177,11178,11179,11180,4957, #11072 +11181,11182,11183,11184,11185,11186,4958,11187,11188,11189,11190,11191,11192,11193,11194,11195, #11088 +11196,11197,11198,11199,11200,3678,11201,11202,11203,11204,11205,11206,4191,11207,11208,11209, #11104 +11210,11211,11212,11213,11214,11215,11216,11217,11218,11219,11220,11221,11222,11223,11224,11225, #11120 +11226,11227,11228,11229,11230,11231,11232,11233,11234,11235,11236,11237,11238,11239,11240,11241, #11136 +11242,11243,11244,11245,11246,11247,11248,11249,11250,11251,4959,11252,11253,11254,11255,11256, #11152 +11257,11258,11259,11260,11261,11262,11263,11264,11265,11266,11267,11268,11269,11270,11271,11272, #11168 +11273,11274,11275,11276,11277,11278,11279,11280,11281,11282,11283,11284,11285,11286,11287,11288, #11184 +11289,11290,11291,11292,11293,11294,11295,11296,11297,11298,11299,11300,11301,11302,11303,11304, #11200 +11305,11306,11307,11308,11309,11310,11311,11312,11313,11314,3679,11315,11316,11317,11318,4490, #11216 +11319,11320,11321,11322,11323,11324,11325,11326,11327,11328,11329,11330,11331,11332,11333,11334, #11232 +11335,11336,11337,11338,11339,11340,11341,11342,11343,11344,11345,11346,11347,4960,11348,11349, #11248 +11350,11351,11352,11353,11354,11355,11356,11357,11358,11359,11360,11361,11362,11363,11364,11365, #11264 +11366,11367,11368,11369,11370,11371,11372,11373,11374,11375,11376,11377,3961,4961,11378,11379, #11280 +11380,11381,11382,11383,11384,11385,11386,11387,11388,11389,11390,11391,11392,11393,11394,11395, #11296 +11396,11397,4192,11398,11399,11400,11401,11402,11403,11404,11405,11406,11407,11408,11409,11410, #11312 +11411,4962,11412,11413,11414,11415,11416,11417,11418,11419,11420,11421,11422,11423,11424,11425, #11328 +11426,11427,11428,11429,11430,11431,11432,11433,11434,11435,11436,11437,11438,11439,11440,11441, #11344 +11442,11443,11444,11445,11446,11447,11448,11449,11450,11451,11452,11453,11454,11455,11456,11457, #11360 +11458,11459,11460,11461,11462,11463,11464,11465,11466,11467,11468,11469,4963,11470,11471,4491, #11376 +11472,11473,11474,11475,4964,11476,11477,11478,11479,11480,11481,11482,11483,11484,11485,11486, #11392 +11487,11488,11489,11490,11491,11492,4965,11493,11494,11495,11496,11497,11498,11499,11500,11501, #11408 +11502,11503,11504,11505,11506,11507,11508,11509,11510,11511,11512,11513,11514,11515,11516,11517, #11424 +11518,11519,11520,11521,11522,11523,11524,11525,11526,11527,11528,11529,3962,11530,11531,11532, #11440 +11533,11534,11535,11536,11537,11538,11539,11540,11541,11542,11543,11544,11545,11546,11547,11548, #11456 +11549,11550,11551,11552,11553,11554,11555,11556,11557,11558,11559,11560,11561,11562,11563,11564, #11472 +4193,4194,11565,11566,11567,11568,11569,11570,11571,11572,11573,11574,11575,11576,11577,11578, #11488 +11579,11580,11581,11582,11583,11584,11585,11586,11587,11588,11589,11590,11591,4966,4195,11592, #11504 +11593,11594,11595,11596,11597,11598,11599,11600,11601,11602,11603,11604,3090,11605,11606,11607, #11520 +11608,11609,11610,4967,11611,11612,11613,11614,11615,11616,11617,11618,11619,11620,11621,11622, #11536 +11623,11624,11625,11626,11627,11628,11629,11630,11631,11632,11633,11634,11635,11636,11637,11638, #11552 +11639,11640,11641,11642,11643,11644,11645,11646,11647,11648,11649,11650,11651,11652,11653,11654, #11568 +11655,11656,11657,11658,11659,11660,11661,11662,11663,11664,11665,11666,11667,11668,11669,11670, #11584 +11671,11672,11673,11674,4968,11675,11676,11677,11678,11679,11680,11681,11682,11683,11684,11685, #11600 +11686,11687,11688,11689,11690,11691,11692,11693,3809,11694,11695,11696,11697,11698,11699,11700, #11616 +11701,11702,11703,11704,11705,11706,11707,11708,11709,11710,11711,11712,11713,11714,11715,11716, #11632 +11717,11718,3553,11719,11720,11721,11722,11723,11724,11725,11726,11727,11728,11729,11730,4969, #11648 +11731,11732,11733,11734,11735,11736,11737,11738,11739,11740,4492,11741,11742,11743,11744,11745, #11664 +11746,11747,11748,11749,11750,11751,11752,4970,11753,11754,11755,11756,11757,11758,11759,11760, #11680 +11761,11762,11763,11764,11765,11766,11767,11768,11769,11770,11771,11772,11773,11774,11775,11776, #11696 +11777,11778,11779,11780,11781,11782,11783,11784,11785,11786,11787,11788,11789,11790,4971,11791, #11712 +11792,11793,11794,11795,11796,11797,4972,11798,11799,11800,11801,11802,11803,11804,11805,11806, #11728 +11807,11808,11809,11810,4973,11811,11812,11813,11814,11815,11816,11817,11818,11819,11820,11821, #11744 +11822,11823,11824,11825,11826,11827,11828,11829,11830,11831,11832,11833,11834,3680,3810,11835, #11760 +11836,4974,11837,11838,11839,11840,11841,11842,11843,11844,11845,11846,11847,11848,11849,11850, #11776 +11851,11852,11853,11854,11855,11856,11857,11858,11859,11860,11861,11862,11863,11864,11865,11866, #11792 +11867,11868,11869,11870,11871,11872,11873,11874,11875,11876,11877,11878,11879,11880,11881,11882, #11808 +11883,11884,4493,11885,11886,11887,11888,11889,11890,11891,11892,11893,11894,11895,11896,11897, #11824 +11898,11899,11900,11901,11902,11903,11904,11905,11906,11907,11908,11909,11910,11911,11912,11913, #11840 +11914,11915,4975,11916,11917,11918,11919,11920,11921,11922,11923,11924,11925,11926,11927,11928, #11856 +11929,11930,11931,11932,11933,11934,11935,11936,11937,11938,11939,11940,11941,11942,11943,11944, #11872 +11945,11946,11947,11948,11949,4976,11950,11951,11952,11953,11954,11955,11956,11957,11958,11959, #11888 +11960,11961,11962,11963,11964,11965,11966,11967,11968,11969,11970,11971,11972,11973,11974,11975, #11904 +11976,11977,11978,11979,11980,11981,11982,11983,11984,11985,11986,11987,4196,11988,11989,11990, #11920 +11991,11992,4977,11993,11994,11995,11996,11997,11998,11999,12000,12001,12002,12003,12004,12005, #11936 +12006,12007,12008,12009,12010,12011,12012,12013,12014,12015,12016,12017,12018,12019,12020,12021, #11952 +12022,12023,12024,12025,12026,12027,12028,12029,12030,12031,12032,12033,12034,12035,12036,12037, #11968 +12038,12039,12040,12041,12042,12043,12044,12045,12046,12047,12048,12049,12050,12051,12052,12053, #11984 +12054,12055,12056,12057,12058,12059,12060,12061,4978,12062,12063,12064,12065,12066,12067,12068, #12000 +12069,12070,12071,12072,12073,12074,12075,12076,12077,12078,12079,12080,12081,12082,12083,12084, #12016 +12085,12086,12087,12088,12089,12090,12091,12092,12093,12094,12095,12096,12097,12098,12099,12100, #12032 +12101,12102,12103,12104,12105,12106,12107,12108,12109,12110,12111,12112,12113,12114,12115,12116, #12048 +12117,12118,12119,12120,12121,12122,12123,4979,12124,12125,12126,12127,12128,4197,12129,12130, #12064 +12131,12132,12133,12134,12135,12136,12137,12138,12139,12140,12141,12142,12143,12144,12145,12146, #12080 +12147,12148,12149,12150,12151,12152,12153,12154,4980,12155,12156,12157,12158,12159,12160,4494, #12096 +12161,12162,12163,12164,3811,12165,12166,12167,12168,12169,4495,12170,12171,4496,12172,12173, #12112 +12174,12175,12176,3812,12177,12178,12179,12180,12181,12182,12183,12184,12185,12186,12187,12188, #12128 +12189,12190,12191,12192,12193,12194,12195,12196,12197,12198,12199,12200,12201,12202,12203,12204, #12144 +12205,12206,12207,12208,12209,12210,12211,12212,12213,12214,12215,12216,12217,12218,12219,12220, #12160 +12221,4981,12222,12223,12224,12225,12226,12227,12228,12229,12230,12231,12232,12233,12234,12235, #12176 +4982,12236,12237,12238,12239,12240,12241,12242,12243,12244,12245,4983,12246,12247,12248,12249, #12192 +4984,12250,12251,12252,12253,12254,12255,12256,12257,12258,12259,12260,12261,12262,12263,12264, #12208 +4985,12265,4497,12266,12267,12268,12269,12270,12271,12272,12273,12274,12275,12276,12277,12278, #12224 +12279,12280,12281,12282,12283,12284,12285,12286,12287,4986,12288,12289,12290,12291,12292,12293, #12240 +12294,12295,12296,2473,12297,12298,12299,12300,12301,12302,12303,12304,12305,12306,12307,12308, #12256 +12309,12310,12311,12312,12313,12314,12315,12316,12317,12318,12319,3963,12320,12321,12322,12323, #12272 +12324,12325,12326,12327,12328,12329,12330,12331,12332,4987,12333,12334,12335,12336,12337,12338, #12288 +12339,12340,12341,12342,12343,12344,12345,12346,12347,12348,12349,12350,12351,12352,12353,12354, #12304 +12355,12356,12357,12358,12359,3964,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369, #12320 +12370,3965,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384, #12336 +12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400, #12352 +12401,12402,12403,12404,12405,12406,12407,12408,4988,12409,12410,12411,12412,12413,12414,12415, #12368 +12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431, #12384 +12432,12433,12434,12435,12436,12437,12438,3554,12439,12440,12441,12442,12443,12444,12445,12446, #12400 +12447,12448,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462, #12416 +12463,12464,4989,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477, #12432 +12478,12479,12480,4990,12481,12482,12483,12484,12485,12486,12487,12488,12489,4498,12490,12491, #12448 +12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507, #12464 +12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523, #12480 +12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,12535,12536,12537,12538,12539, #12496 +12540,12541,12542,12543,12544,12545,12546,12547,12548,12549,12550,12551,4991,12552,12553,12554, #12512 +12555,12556,12557,12558,12559,12560,12561,12562,12563,12564,12565,12566,12567,12568,12569,12570, #12528 +12571,12572,12573,12574,12575,12576,12577,12578,3036,12579,12580,12581,12582,12583,3966,12584, #12544 +12585,12586,12587,12588,12589,12590,12591,12592,12593,12594,12595,12596,12597,12598,12599,12600, #12560 +12601,12602,12603,12604,12605,12606,12607,12608,12609,12610,12611,12612,12613,12614,12615,12616, #12576 +12617,12618,12619,12620,12621,12622,12623,12624,12625,12626,12627,12628,12629,12630,12631,12632, #12592 +12633,12634,12635,12636,12637,12638,12639,12640,12641,12642,12643,12644,12645,12646,4499,12647, #12608 +12648,12649,12650,12651,12652,12653,12654,12655,12656,12657,12658,12659,12660,12661,12662,12663, #12624 +12664,12665,12666,12667,12668,12669,12670,12671,12672,12673,12674,12675,12676,12677,12678,12679, #12640 +12680,12681,12682,12683,12684,12685,12686,12687,12688,12689,12690,12691,12692,12693,12694,12695, #12656 +12696,12697,12698,4992,12699,12700,12701,12702,12703,12704,12705,12706,12707,12708,12709,12710, #12672 +12711,12712,12713,12714,12715,12716,12717,12718,12719,12720,12721,12722,12723,12724,12725,12726, #12688 +12727,12728,12729,12730,12731,12732,12733,12734,12735,12736,12737,12738,12739,12740,12741,12742, #12704 +12743,12744,12745,12746,12747,12748,12749,12750,12751,12752,12753,12754,12755,12756,12757,12758, #12720 +12759,12760,12761,12762,12763,12764,12765,12766,12767,12768,12769,12770,12771,12772,12773,12774, #12736 +12775,12776,12777,12778,4993,2175,12779,12780,12781,12782,12783,12784,12785,12786,4500,12787, #12752 +12788,12789,12790,12791,12792,12793,12794,12795,12796,12797,12798,12799,12800,12801,12802,12803, #12768 +12804,12805,12806,12807,12808,12809,12810,12811,12812,12813,12814,12815,12816,12817,12818,12819, #12784 +12820,12821,12822,12823,12824,12825,12826,4198,3967,12827,12828,12829,12830,12831,12832,12833, #12800 +12834,12835,12836,12837,12838,12839,12840,12841,12842,12843,12844,12845,12846,12847,12848,12849, #12816 +12850,12851,12852,12853,12854,12855,12856,12857,12858,12859,12860,12861,4199,12862,12863,12864, #12832 +12865,12866,12867,12868,12869,12870,12871,12872,12873,12874,12875,12876,12877,12878,12879,12880, #12848 +12881,12882,12883,12884,12885,12886,12887,4501,12888,12889,12890,12891,12892,12893,12894,12895, #12864 +12896,12897,12898,12899,12900,12901,12902,12903,12904,12905,12906,12907,12908,12909,12910,12911, #12880 +12912,4994,12913,12914,12915,12916,12917,12918,12919,12920,12921,12922,12923,12924,12925,12926, #12896 +12927,12928,12929,12930,12931,12932,12933,12934,12935,12936,12937,12938,12939,12940,12941,12942, #12912 +12943,12944,12945,12946,12947,12948,12949,12950,12951,12952,12953,12954,12955,12956,1772,12957, #12928 +12958,12959,12960,12961,12962,12963,12964,12965,12966,12967,12968,12969,12970,12971,12972,12973, #12944 +12974,12975,12976,12977,12978,12979,12980,12981,12982,12983,12984,12985,12986,12987,12988,12989, #12960 +12990,12991,12992,12993,12994,12995,12996,12997,4502,12998,4503,12999,13000,13001,13002,13003, #12976 +4504,13004,13005,13006,13007,13008,13009,13010,13011,13012,13013,13014,13015,13016,13017,13018, #12992 +13019,13020,13021,13022,13023,13024,13025,13026,13027,13028,13029,3449,13030,13031,13032,13033, #13008 +13034,13035,13036,13037,13038,13039,13040,13041,13042,13043,13044,13045,13046,13047,13048,13049, #13024 +13050,13051,13052,13053,13054,13055,13056,13057,13058,13059,13060,13061,13062,13063,13064,13065, #13040 +13066,13067,13068,13069,13070,13071,13072,13073,13074,13075,13076,13077,13078,13079,13080,13081, #13056 +13082,13083,13084,13085,13086,13087,13088,13089,13090,13091,13092,13093,13094,13095,13096,13097, #13072 +13098,13099,13100,13101,13102,13103,13104,13105,13106,13107,13108,13109,13110,13111,13112,13113, #13088 +13114,13115,13116,13117,13118,3968,13119,4995,13120,13121,13122,13123,13124,13125,13126,13127, #13104 +4505,13128,13129,13130,13131,13132,13133,13134,4996,4506,13135,13136,13137,13138,13139,4997, #13120 +13140,13141,13142,13143,13144,13145,13146,13147,13148,13149,13150,13151,13152,13153,13154,13155, #13136 +13156,13157,13158,13159,4998,13160,13161,13162,13163,13164,13165,13166,13167,13168,13169,13170, #13152 +13171,13172,13173,13174,13175,13176,4999,13177,13178,13179,13180,13181,13182,13183,13184,13185, #13168 +13186,13187,13188,13189,13190,13191,13192,13193,13194,13195,13196,13197,13198,13199,13200,13201, #13184 +13202,13203,13204,13205,13206,5000,13207,13208,13209,13210,13211,13212,13213,13214,13215,13216, #13200 +13217,13218,13219,13220,13221,13222,13223,13224,13225,13226,13227,4200,5001,13228,13229,13230, #13216 +13231,13232,13233,13234,13235,13236,13237,13238,13239,13240,3969,13241,13242,13243,13244,3970, #13232 +13245,13246,13247,13248,13249,13250,13251,13252,13253,13254,13255,13256,13257,13258,13259,13260, #13248 +13261,13262,13263,13264,13265,13266,13267,13268,3450,13269,13270,13271,13272,13273,13274,13275, #13264 +13276,5002,13277,13278,13279,13280,13281,13282,13283,13284,13285,13286,13287,13288,13289,13290, #13280 +13291,13292,13293,13294,13295,13296,13297,13298,13299,13300,13301,13302,3813,13303,13304,13305, #13296 +13306,13307,13308,13309,13310,13311,13312,13313,13314,13315,13316,13317,13318,13319,13320,13321, #13312 +13322,13323,13324,13325,13326,13327,13328,4507,13329,13330,13331,13332,13333,13334,13335,13336, #13328 +13337,13338,13339,13340,13341,5003,13342,13343,13344,13345,13346,13347,13348,13349,13350,13351, #13344 +13352,13353,13354,13355,13356,13357,13358,13359,13360,13361,13362,13363,13364,13365,13366,13367, #13360 +5004,13368,13369,13370,13371,13372,13373,13374,13375,13376,13377,13378,13379,13380,13381,13382, #13376 +13383,13384,13385,13386,13387,13388,13389,13390,13391,13392,13393,13394,13395,13396,13397,13398, #13392 +13399,13400,13401,13402,13403,13404,13405,13406,13407,13408,13409,13410,13411,13412,13413,13414, #13408 +13415,13416,13417,13418,13419,13420,13421,13422,13423,13424,13425,13426,13427,13428,13429,13430, #13424 +13431,13432,4508,13433,13434,13435,4201,13436,13437,13438,13439,13440,13441,13442,13443,13444, #13440 +13445,13446,13447,13448,13449,13450,13451,13452,13453,13454,13455,13456,13457,5005,13458,13459, #13456 +13460,13461,13462,13463,13464,13465,13466,13467,13468,13469,13470,4509,13471,13472,13473,13474, #13472 +13475,13476,13477,13478,13479,13480,13481,13482,13483,13484,13485,13486,13487,13488,13489,13490, #13488 +13491,13492,13493,13494,13495,13496,13497,13498,13499,13500,13501,13502,13503,13504,13505,13506, #13504 +13507,13508,13509,13510,13511,13512,13513,13514,13515,13516,13517,13518,13519,13520,13521,13522, #13520 +13523,13524,13525,13526,13527,13528,13529,13530,13531,13532,13533,13534,13535,13536,13537,13538, #13536 +13539,13540,13541,13542,13543,13544,13545,13546,13547,13548,13549,13550,13551,13552,13553,13554, #13552 +13555,13556,13557,13558,13559,13560,13561,13562,13563,13564,13565,13566,13567,13568,13569,13570, #13568 +13571,13572,13573,13574,13575,13576,13577,13578,13579,13580,13581,13582,13583,13584,13585,13586, #13584 +13587,13588,13589,13590,13591,13592,13593,13594,13595,13596,13597,13598,13599,13600,13601,13602, #13600 +13603,13604,13605,13606,13607,13608,13609,13610,13611,13612,13613,13614,13615,13616,13617,13618, #13616 +13619,13620,13621,13622,13623,13624,13625,13626,13627,13628,13629,13630,13631,13632,13633,13634, #13632 +13635,13636,13637,13638,13639,13640,13641,13642,5006,13643,13644,13645,13646,13647,13648,13649, #13648 +13650,13651,5007,13652,13653,13654,13655,13656,13657,13658,13659,13660,13661,13662,13663,13664, #13664 +13665,13666,13667,13668,13669,13670,13671,13672,13673,13674,13675,13676,13677,13678,13679,13680, #13680 +13681,13682,13683,13684,13685,13686,13687,13688,13689,13690,13691,13692,13693,13694,13695,13696, #13696 +13697,13698,13699,13700,13701,13702,13703,13704,13705,13706,13707,13708,13709,13710,13711,13712, #13712 +13713,13714,13715,13716,13717,13718,13719,13720,13721,13722,13723,13724,13725,13726,13727,13728, #13728 +13729,13730,13731,13732,13733,13734,13735,13736,13737,13738,13739,13740,13741,13742,13743,13744, #13744 +13745,13746,13747,13748,13749,13750,13751,13752,13753,13754,13755,13756,13757,13758,13759,13760, #13760 +13761,13762,13763,13764,13765,13766,13767,13768,13769,13770,13771,13772,13773,13774,3273,13775, #13776 +13776,13777,13778,13779,13780,13781,13782,13783,13784,13785,13786,13787,13788,13789,13790,13791, #13792 +13792,13793,13794,13795,13796,13797,13798,13799,13800,13801,13802,13803,13804,13805,13806,13807, #13808 +13808,13809,13810,13811,13812,13813,13814,13815,13816,13817,13818,13819,13820,13821,13822,13823, #13824 +13824,13825,13826,13827,13828,13829,13830,13831,13832,13833,13834,13835,13836,13837,13838,13839, #13840 +13840,13841,13842,13843,13844,13845,13846,13847,13848,13849,13850,13851,13852,13853,13854,13855, #13856 +13856,13857,13858,13859,13860,13861,13862,13863,13864,13865,13866,13867,13868,13869,13870,13871, #13872 +13872,13873,13874,13875,13876,13877,13878,13879,13880,13881,13882,13883,13884,13885,13886,13887, #13888 +13888,13889,13890,13891,13892,13893,13894,13895,13896,13897,13898,13899,13900,13901,13902,13903, #13904 +13904,13905,13906,13907,13908,13909,13910,13911,13912,13913,13914,13915,13916,13917,13918,13919, #13920 +13920,13921,13922,13923,13924,13925,13926,13927,13928,13929,13930,13931,13932,13933,13934,13935, #13936 +13936,13937,13938,13939,13940,13941,13942,13943,13944,13945,13946,13947,13948,13949,13950,13951, #13952 +13952,13953,13954,13955,13956,13957,13958,13959,13960,13961,13962,13963,13964,13965,13966,13967, #13968 +13968,13969,13970,13971,13972) #13973 + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/big5prober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/big5prober.py new file mode 100644 index 0000000..becce81 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/big5prober.py @@ -0,0 +1,42 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import Big5DistributionAnalysis +from .mbcssm import Big5SMModel + + +class Big5Prober(MultiByteCharSetProber): + def __init__(self): + MultiByteCharSetProber.__init__(self) + self._mCodingSM = CodingStateMachine(Big5SMModel) + self._mDistributionAnalyzer = Big5DistributionAnalysis() + self.reset() + + def get_charset_name(self): + return "Big5" diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/chardetect.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/chardetect.py new file mode 100644 index 0000000..ffe892f --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/chardetect.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +""" +Script which takes one or more file paths and reports on their detected +encodings + +Example:: + + % chardetect somefile someotherfile + somefile: windows-1252 with confidence 0.5 + someotherfile: ascii with confidence 1.0 + +If no paths are provided, it takes its input from stdin. + +""" + +from __future__ import absolute_import, print_function, unicode_literals + +import argparse +import sys +from io import open + +from chardet import __version__ +from chardet.universaldetector import UniversalDetector + + +def description_of(lines, name='stdin'): + """ + Return a string describing the probable encoding of a file or + list of strings. + + :param lines: The lines to get the encoding of. + :type lines: Iterable of bytes + :param name: Name of file or collection of lines + :type name: str + """ + u = UniversalDetector() + for line in lines: + u.feed(line) + u.close() + result = u.result + if result['encoding']: + return '{0}: {1} with confidence {2}'.format(name, result['encoding'], + result['confidence']) + else: + return '{0}: no result'.format(name) + + +def main(argv=None): + ''' + Handles command line arguments and gets things started. + + :param argv: List of arguments, as if specified on the command-line. + If None, ``sys.argv[1:]`` is used instead. + :type argv: list of str + ''' + # Get command line arguments + parser = argparse.ArgumentParser( + description="Takes one or more file paths and reports their detected \ + encodings", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + conflict_handler='resolve') + parser.add_argument('input', + help='File whose encoding we would like to determine.', + type=argparse.FileType('rb'), nargs='*', + default=[sys.stdin]) + parser.add_argument('--version', action='version', + version='%(prog)s {0}'.format(__version__)) + args = parser.parse_args(argv) + + for f in args.input: + if f.isatty(): + print("You are running chardetect interactively. Press " + + "CTRL-D twice at the start of a blank line to signal the " + + "end of your input. If you want help, run chardetect " + + "--help\n", file=sys.stderr) + print(description_of(f, f.name)) + + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/chardistribution.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/chardistribution.py new file mode 100644 index 0000000..4e64a00 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/chardistribution.py @@ -0,0 +1,231 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .euctwfreq import (EUCTWCharToFreqOrder, EUCTW_TABLE_SIZE, + EUCTW_TYPICAL_DISTRIBUTION_RATIO) +from .euckrfreq import (EUCKRCharToFreqOrder, EUCKR_TABLE_SIZE, + EUCKR_TYPICAL_DISTRIBUTION_RATIO) +from .gb2312freq import (GB2312CharToFreqOrder, GB2312_TABLE_SIZE, + GB2312_TYPICAL_DISTRIBUTION_RATIO) +from .big5freq import (Big5CharToFreqOrder, BIG5_TABLE_SIZE, + BIG5_TYPICAL_DISTRIBUTION_RATIO) +from .jisfreq import (JISCharToFreqOrder, JIS_TABLE_SIZE, + JIS_TYPICAL_DISTRIBUTION_RATIO) +from .compat import wrap_ord + +ENOUGH_DATA_THRESHOLD = 1024 +SURE_YES = 0.99 +SURE_NO = 0.01 +MINIMUM_DATA_THRESHOLD = 3 + + +class CharDistributionAnalysis: + def __init__(self): + # Mapping table to get frequency order from char order (get from + # GetOrder()) + self._mCharToFreqOrder = None + self._mTableSize = None # Size of above table + # This is a constant value which varies from language to language, + # used in calculating confidence. See + # http://www.mozilla.org/projects/intl/UniversalCharsetDetection.html + # for further detail. + self._mTypicalDistributionRatio = None + self.reset() + + def reset(self): + """reset analyser, clear any state""" + # If this flag is set to True, detection is done and conclusion has + # been made + self._mDone = False + self._mTotalChars = 0 # Total characters encountered + # The number of characters whose frequency order is less than 512 + self._mFreqChars = 0 + + def feed(self, aBuf, aCharLen): + """feed a character with known length""" + if aCharLen == 2: + # we only care about 2-bytes character in our distribution analysis + order = self.get_order(aBuf) + else: + order = -1 + if order >= 0: + self._mTotalChars += 1 + # order is valid + if order < self._mTableSize: + if 512 > self._mCharToFreqOrder[order]: + self._mFreqChars += 1 + + def get_confidence(self): + """return confidence based on existing data""" + # if we didn't receive any character in our consideration range, + # return negative answer + if self._mTotalChars <= 0 or self._mFreqChars <= MINIMUM_DATA_THRESHOLD: + return SURE_NO + + if self._mTotalChars != self._mFreqChars: + r = (self._mFreqChars / ((self._mTotalChars - self._mFreqChars) + * self._mTypicalDistributionRatio)) + if r < SURE_YES: + return r + + # normalize confidence (we don't want to be 100% sure) + return SURE_YES + + def got_enough_data(self): + # It is not necessary to receive all data to draw conclusion. + # For charset detection, certain amount of data is enough + return self._mTotalChars > ENOUGH_DATA_THRESHOLD + + def get_order(self, aBuf): + # We do not handle characters based on the original encoding string, + # but convert this encoding string to a number, here called order. + # This allows multiple encodings of a language to share one frequency + # table. + return -1 + + +class EUCTWDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + CharDistributionAnalysis.__init__(self) + self._mCharToFreqOrder = EUCTWCharToFreqOrder + self._mTableSize = EUCTW_TABLE_SIZE + self._mTypicalDistributionRatio = EUCTW_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, aBuf): + # for euc-TW encoding, we are interested + # first byte range: 0xc4 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char = wrap_ord(aBuf[0]) + if first_char >= 0xC4: + return 94 * (first_char - 0xC4) + wrap_ord(aBuf[1]) - 0xA1 + else: + return -1 + + +class EUCKRDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + CharDistributionAnalysis.__init__(self) + self._mCharToFreqOrder = EUCKRCharToFreqOrder + self._mTableSize = EUCKR_TABLE_SIZE + self._mTypicalDistributionRatio = EUCKR_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, aBuf): + # for euc-KR encoding, we are interested + # first byte range: 0xb0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char = wrap_ord(aBuf[0]) + if first_char >= 0xB0: + return 94 * (first_char - 0xB0) + wrap_ord(aBuf[1]) - 0xA1 + else: + return -1 + + +class GB2312DistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + CharDistributionAnalysis.__init__(self) + self._mCharToFreqOrder = GB2312CharToFreqOrder + self._mTableSize = GB2312_TABLE_SIZE + self._mTypicalDistributionRatio = GB2312_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, aBuf): + # for GB2312 encoding, we are interested + # first byte range: 0xb0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char, second_char = wrap_ord(aBuf[0]), wrap_ord(aBuf[1]) + if (first_char >= 0xB0) and (second_char >= 0xA1): + return 94 * (first_char - 0xB0) + second_char - 0xA1 + else: + return -1 + + +class Big5DistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + CharDistributionAnalysis.__init__(self) + self._mCharToFreqOrder = Big5CharToFreqOrder + self._mTableSize = BIG5_TABLE_SIZE + self._mTypicalDistributionRatio = BIG5_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, aBuf): + # for big5 encoding, we are interested + # first byte range: 0xa4 -- 0xfe + # second byte range: 0x40 -- 0x7e , 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char, second_char = wrap_ord(aBuf[0]), wrap_ord(aBuf[1]) + if first_char >= 0xA4: + if second_char >= 0xA1: + return 157 * (first_char - 0xA4) + second_char - 0xA1 + 63 + else: + return 157 * (first_char - 0xA4) + second_char - 0x40 + else: + return -1 + + +class SJISDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + CharDistributionAnalysis.__init__(self) + self._mCharToFreqOrder = JISCharToFreqOrder + self._mTableSize = JIS_TABLE_SIZE + self._mTypicalDistributionRatio = JIS_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, aBuf): + # for sjis encoding, we are interested + # first byte range: 0x81 -- 0x9f , 0xe0 -- 0xfe + # second byte range: 0x40 -- 0x7e, 0x81 -- oxfe + # no validation needed here. State machine has done that + first_char, second_char = wrap_ord(aBuf[0]), wrap_ord(aBuf[1]) + if (first_char >= 0x81) and (first_char <= 0x9F): + order = 188 * (first_char - 0x81) + elif (first_char >= 0xE0) and (first_char <= 0xEF): + order = 188 * (first_char - 0xE0 + 31) + else: + return -1 + order = order + second_char - 0x40 + if second_char > 0x7F: + order = -1 + return order + + +class EUCJPDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + CharDistributionAnalysis.__init__(self) + self._mCharToFreqOrder = JISCharToFreqOrder + self._mTableSize = JIS_TABLE_SIZE + self._mTypicalDistributionRatio = JIS_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, aBuf): + # for euc-JP encoding, we are interested + # first byte range: 0xa0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + char = wrap_ord(aBuf[0]) + if char >= 0xA0: + return 94 * (char - 0xA1) + wrap_ord(aBuf[1]) - 0xa1 + else: + return -1 diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/charsetgroupprober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/charsetgroupprober.py new file mode 100644 index 0000000..85e7a1c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/charsetgroupprober.py @@ -0,0 +1,106 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from . import constants +import sys +from .charsetprober import CharSetProber + + +class CharSetGroupProber(CharSetProber): + def __init__(self): + CharSetProber.__init__(self) + self._mActiveNum = 0 + self._mProbers = [] + self._mBestGuessProber = None + + def reset(self): + CharSetProber.reset(self) + self._mActiveNum = 0 + for prober in self._mProbers: + if prober: + prober.reset() + prober.active = True + self._mActiveNum += 1 + self._mBestGuessProber = None + + def get_charset_name(self): + if not self._mBestGuessProber: + self.get_confidence() + if not self._mBestGuessProber: + return None +# self._mBestGuessProber = self._mProbers[0] + return self._mBestGuessProber.get_charset_name() + + def feed(self, aBuf): + for prober in self._mProbers: + if not prober: + continue + if not prober.active: + continue + st = prober.feed(aBuf) + if not st: + continue + if st == constants.eFoundIt: + self._mBestGuessProber = prober + return self.get_state() + elif st == constants.eNotMe: + prober.active = False + self._mActiveNum -= 1 + if self._mActiveNum <= 0: + self._mState = constants.eNotMe + return self.get_state() + return self.get_state() + + def get_confidence(self): + st = self.get_state() + if st == constants.eFoundIt: + return 0.99 + elif st == constants.eNotMe: + return 0.01 + bestConf = 0.0 + self._mBestGuessProber = None + for prober in self._mProbers: + if not prober: + continue + if not prober.active: + if constants._debug: + sys.stderr.write(prober.get_charset_name() + + ' not active\n') + continue + cf = prober.get_confidence() + if constants._debug: + sys.stderr.write('%s confidence = %s\n' % + (prober.get_charset_name(), cf)) + if bestConf < cf: + bestConf = cf + self._mBestGuessProber = prober + if not self._mBestGuessProber: + return 0.0 + return bestConf +# else: +# self._mBestGuessProber = self._mProbers[0] +# return self._mBestGuessProber.get_confidence() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/charsetprober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/charsetprober.py new file mode 100644 index 0000000..9758171 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/charsetprober.py @@ -0,0 +1,62 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from . import constants +import re + + +class CharSetProber: + def __init__(self): + pass + + def reset(self): + self._mState = constants.eDetecting + + def get_charset_name(self): + return None + + def feed(self, aBuf): + pass + + def get_state(self): + return self._mState + + def get_confidence(self): + return 0.0 + + def filter_high_bit_only(self, aBuf): + aBuf = re.sub(b'([\x00-\x7F])+', b' ', aBuf) + return aBuf + + def filter_without_english_letters(self, aBuf): + aBuf = re.sub(b'([A-Za-z])+', b' ', aBuf) + return aBuf + + def filter_with_english_letters(self, aBuf): + # TODO + return aBuf diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/codingstatemachine.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/codingstatemachine.py new file mode 100644 index 0000000..8dd8c91 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/codingstatemachine.py @@ -0,0 +1,61 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .constants import eStart +from .compat import wrap_ord + + +class CodingStateMachine: + def __init__(self, sm): + self._mModel = sm + self._mCurrentBytePos = 0 + self._mCurrentCharLen = 0 + self.reset() + + def reset(self): + self._mCurrentState = eStart + + def next_state(self, c): + # for each byte we get its class + # if it is first byte, we also get byte length + # PY3K: aBuf is a byte stream, so c is an int, not a byte + byteCls = self._mModel['classTable'][wrap_ord(c)] + if self._mCurrentState == eStart: + self._mCurrentBytePos = 0 + self._mCurrentCharLen = self._mModel['charLenTable'][byteCls] + # from byte's class and stateTable, we get its next state + curr_state = (self._mCurrentState * self._mModel['classFactor'] + + byteCls) + self._mCurrentState = self._mModel['stateTable'][curr_state] + self._mCurrentBytePos += 1 + return self._mCurrentState + + def get_current_charlen(self): + return self._mCurrentCharLen + + def get_coding_state_machine(self): + return self._mModel['name'] diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/compat.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/compat.py new file mode 100644 index 0000000..d9e30ad --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/compat.py @@ -0,0 +1,34 @@ +######################## BEGIN LICENSE BLOCK ######################## +# Contributor(s): +# Ian Cordasco - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import sys + + +if sys.version_info < (3, 0): + base_str = (str, unicode) +else: + base_str = (bytes, str) + + +def wrap_ord(a): + if sys.version_info < (3, 0) and isinstance(a, base_str): + return ord(a) + else: + return a diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/constants.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/constants.py new file mode 100644 index 0000000..e4d148b --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/constants.py @@ -0,0 +1,39 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +_debug = 0 + +eDetecting = 0 +eFoundIt = 1 +eNotMe = 2 + +eStart = 0 +eError = 1 +eItsMe = 2 + +SHORTCUT_THRESHOLD = 0.95 diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/cp949prober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/cp949prober.py new file mode 100644 index 0000000..ff4272f --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/cp949prober.py @@ -0,0 +1,44 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import EUCKRDistributionAnalysis +from .mbcssm import CP949SMModel + + +class CP949Prober(MultiByteCharSetProber): + def __init__(self): + MultiByteCharSetProber.__init__(self) + self._mCodingSM = CodingStateMachine(CP949SMModel) + # NOTE: CP949 is a superset of EUC-KR, so the distribution should be + # not different. + self._mDistributionAnalyzer = EUCKRDistributionAnalysis() + self.reset() + + def get_charset_name(self): + return "CP949" diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/escprober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/escprober.py new file mode 100644 index 0000000..80a844f --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/escprober.py @@ -0,0 +1,86 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from . import constants +from .escsm import (HZSMModel, ISO2022CNSMModel, ISO2022JPSMModel, + ISO2022KRSMModel) +from .charsetprober import CharSetProber +from .codingstatemachine import CodingStateMachine +from .compat import wrap_ord + + +class EscCharSetProber(CharSetProber): + def __init__(self): + CharSetProber.__init__(self) + self._mCodingSM = [ + CodingStateMachine(HZSMModel), + CodingStateMachine(ISO2022CNSMModel), + CodingStateMachine(ISO2022JPSMModel), + CodingStateMachine(ISO2022KRSMModel) + ] + self.reset() + + def reset(self): + CharSetProber.reset(self) + for codingSM in self._mCodingSM: + if not codingSM: + continue + codingSM.active = True + codingSM.reset() + self._mActiveSM = len(self._mCodingSM) + self._mDetectedCharset = None + + def get_charset_name(self): + return self._mDetectedCharset + + def get_confidence(self): + if self._mDetectedCharset: + return 0.99 + else: + return 0.00 + + def feed(self, aBuf): + for c in aBuf: + # PY3K: aBuf is a byte array, so c is an int, not a byte + for codingSM in self._mCodingSM: + if not codingSM: + continue + if not codingSM.active: + continue + codingState = codingSM.next_state(wrap_ord(c)) + if codingState == constants.eError: + codingSM.active = False + self._mActiveSM -= 1 + if self._mActiveSM <= 0: + self._mState = constants.eNotMe + return self.get_state() + elif codingState == constants.eItsMe: + self._mState = constants.eFoundIt + self._mDetectedCharset = codingSM.get_coding_state_machine() # nopep8 + return self.get_state() + + return self.get_state() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/escsm.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/escsm.py new file mode 100644 index 0000000..bd302b4 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/escsm.py @@ -0,0 +1,242 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .constants import eStart, eError, eItsMe + +HZ_cls = ( +1,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,0,0, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,0,0,0,0, # 20 - 27 +0,0,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +0,0,0,0,0,0,0,0, # 40 - 47 +0,0,0,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,4,0,5,2,0, # 78 - 7f +1,1,1,1,1,1,1,1, # 80 - 87 +1,1,1,1,1,1,1,1, # 88 - 8f +1,1,1,1,1,1,1,1, # 90 - 97 +1,1,1,1,1,1,1,1, # 98 - 9f +1,1,1,1,1,1,1,1, # a0 - a7 +1,1,1,1,1,1,1,1, # a8 - af +1,1,1,1,1,1,1,1, # b0 - b7 +1,1,1,1,1,1,1,1, # b8 - bf +1,1,1,1,1,1,1,1, # c0 - c7 +1,1,1,1,1,1,1,1, # c8 - cf +1,1,1,1,1,1,1,1, # d0 - d7 +1,1,1,1,1,1,1,1, # d8 - df +1,1,1,1,1,1,1,1, # e0 - e7 +1,1,1,1,1,1,1,1, # e8 - ef +1,1,1,1,1,1,1,1, # f0 - f7 +1,1,1,1,1,1,1,1, # f8 - ff +) + +HZ_st = ( +eStart,eError, 3,eStart,eStart,eStart,eError,eError,# 00-07 +eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,# 08-0f +eItsMe,eItsMe,eError,eError,eStart,eStart, 4,eError,# 10-17 + 5,eError, 6,eError, 5, 5, 4,eError,# 18-1f + 4,eError, 4, 4, 4,eError, 4,eError,# 20-27 + 4,eItsMe,eStart,eStart,eStart,eStart,eStart,eStart,# 28-2f +) + +HZCharLenTable = (0, 0, 0, 0, 0, 0) + +HZSMModel = {'classTable': HZ_cls, + 'classFactor': 6, + 'stateTable': HZ_st, + 'charLenTable': HZCharLenTable, + 'name': "HZ-GB-2312"} + +ISO2022CN_cls = ( +2,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,0,0, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,0,0,0,0, # 20 - 27 +0,3,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +0,0,0,4,0,0,0,0, # 40 - 47 +0,0,0,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,0,0,0,0,0, # 78 - 7f +2,2,2,2,2,2,2,2, # 80 - 87 +2,2,2,2,2,2,2,2, # 88 - 8f +2,2,2,2,2,2,2,2, # 90 - 97 +2,2,2,2,2,2,2,2, # 98 - 9f +2,2,2,2,2,2,2,2, # a0 - a7 +2,2,2,2,2,2,2,2, # a8 - af +2,2,2,2,2,2,2,2, # b0 - b7 +2,2,2,2,2,2,2,2, # b8 - bf +2,2,2,2,2,2,2,2, # c0 - c7 +2,2,2,2,2,2,2,2, # c8 - cf +2,2,2,2,2,2,2,2, # d0 - d7 +2,2,2,2,2,2,2,2, # d8 - df +2,2,2,2,2,2,2,2, # e0 - e7 +2,2,2,2,2,2,2,2, # e8 - ef +2,2,2,2,2,2,2,2, # f0 - f7 +2,2,2,2,2,2,2,2, # f8 - ff +) + +ISO2022CN_st = ( +eStart, 3,eError,eStart,eStart,eStart,eStart,eStart,# 00-07 +eStart,eError,eError,eError,eError,eError,eError,eError,# 08-0f +eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,# 10-17 +eItsMe,eItsMe,eItsMe,eError,eError,eError, 4,eError,# 18-1f +eError,eError,eError,eItsMe,eError,eError,eError,eError,# 20-27 + 5, 6,eError,eError,eError,eError,eError,eError,# 28-2f +eError,eError,eError,eItsMe,eError,eError,eError,eError,# 30-37 +eError,eError,eError,eError,eError,eItsMe,eError,eStart,# 38-3f +) + +ISO2022CNCharLenTable = (0, 0, 0, 0, 0, 0, 0, 0, 0) + +ISO2022CNSMModel = {'classTable': ISO2022CN_cls, + 'classFactor': 9, + 'stateTable': ISO2022CN_st, + 'charLenTable': ISO2022CNCharLenTable, + 'name': "ISO-2022-CN"} + +ISO2022JP_cls = ( +2,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,2,2, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,7,0,0,0, # 20 - 27 +3,0,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +6,0,4,0,8,0,0,0, # 40 - 47 +0,9,5,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,0,0,0,0,0, # 78 - 7f +2,2,2,2,2,2,2,2, # 80 - 87 +2,2,2,2,2,2,2,2, # 88 - 8f +2,2,2,2,2,2,2,2, # 90 - 97 +2,2,2,2,2,2,2,2, # 98 - 9f +2,2,2,2,2,2,2,2, # a0 - a7 +2,2,2,2,2,2,2,2, # a8 - af +2,2,2,2,2,2,2,2, # b0 - b7 +2,2,2,2,2,2,2,2, # b8 - bf +2,2,2,2,2,2,2,2, # c0 - c7 +2,2,2,2,2,2,2,2, # c8 - cf +2,2,2,2,2,2,2,2, # d0 - d7 +2,2,2,2,2,2,2,2, # d8 - df +2,2,2,2,2,2,2,2, # e0 - e7 +2,2,2,2,2,2,2,2, # e8 - ef +2,2,2,2,2,2,2,2, # f0 - f7 +2,2,2,2,2,2,2,2, # f8 - ff +) + +ISO2022JP_st = ( +eStart, 3,eError,eStart,eStart,eStart,eStart,eStart,# 00-07 +eStart,eStart,eError,eError,eError,eError,eError,eError,# 08-0f +eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,# 10-17 +eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eError,eError,# 18-1f +eError, 5,eError,eError,eError, 4,eError,eError,# 20-27 +eError,eError,eError, 6,eItsMe,eError,eItsMe,eError,# 28-2f +eError,eError,eError,eError,eError,eError,eItsMe,eItsMe,# 30-37 +eError,eError,eError,eItsMe,eError,eError,eError,eError,# 38-3f +eError,eError,eError,eError,eItsMe,eError,eStart,eStart,# 40-47 +) + +ISO2022JPCharLenTable = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + +ISO2022JPSMModel = {'classTable': ISO2022JP_cls, + 'classFactor': 10, + 'stateTable': ISO2022JP_st, + 'charLenTable': ISO2022JPCharLenTable, + 'name': "ISO-2022-JP"} + +ISO2022KR_cls = ( +2,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,0,0, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,3,0,0,0, # 20 - 27 +0,4,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +0,0,0,5,0,0,0,0, # 40 - 47 +0,0,0,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,0,0,0,0,0, # 78 - 7f +2,2,2,2,2,2,2,2, # 80 - 87 +2,2,2,2,2,2,2,2, # 88 - 8f +2,2,2,2,2,2,2,2, # 90 - 97 +2,2,2,2,2,2,2,2, # 98 - 9f +2,2,2,2,2,2,2,2, # a0 - a7 +2,2,2,2,2,2,2,2, # a8 - af +2,2,2,2,2,2,2,2, # b0 - b7 +2,2,2,2,2,2,2,2, # b8 - bf +2,2,2,2,2,2,2,2, # c0 - c7 +2,2,2,2,2,2,2,2, # c8 - cf +2,2,2,2,2,2,2,2, # d0 - d7 +2,2,2,2,2,2,2,2, # d8 - df +2,2,2,2,2,2,2,2, # e0 - e7 +2,2,2,2,2,2,2,2, # e8 - ef +2,2,2,2,2,2,2,2, # f0 - f7 +2,2,2,2,2,2,2,2, # f8 - ff +) + +ISO2022KR_st = ( +eStart, 3,eError,eStart,eStart,eStart,eError,eError,# 00-07 +eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,# 08-0f +eItsMe,eItsMe,eError,eError,eError, 4,eError,eError,# 10-17 +eError,eError,eError,eError, 5,eError,eError,eError,# 18-1f +eError,eError,eError,eItsMe,eStart,eStart,eStart,eStart,# 20-27 +) + +ISO2022KRCharLenTable = (0, 0, 0, 0, 0, 0) + +ISO2022KRSMModel = {'classTable': ISO2022KR_cls, + 'classFactor': 6, + 'stateTable': ISO2022KR_st, + 'charLenTable': ISO2022KRCharLenTable, + 'name': "ISO-2022-KR"} + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/eucjpprober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/eucjpprober.py new file mode 100644 index 0000000..8e64fdc --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/eucjpprober.py @@ -0,0 +1,90 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import sys +from . import constants +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import EUCJPDistributionAnalysis +from .jpcntx import EUCJPContextAnalysis +from .mbcssm import EUCJPSMModel + + +class EUCJPProber(MultiByteCharSetProber): + def __init__(self): + MultiByteCharSetProber.__init__(self) + self._mCodingSM = CodingStateMachine(EUCJPSMModel) + self._mDistributionAnalyzer = EUCJPDistributionAnalysis() + self._mContextAnalyzer = EUCJPContextAnalysis() + self.reset() + + def reset(self): + MultiByteCharSetProber.reset(self) + self._mContextAnalyzer.reset() + + def get_charset_name(self): + return "EUC-JP" + + def feed(self, aBuf): + aLen = len(aBuf) + for i in range(0, aLen): + # PY3K: aBuf is a byte array, so aBuf[i] is an int, not a byte + codingState = self._mCodingSM.next_state(aBuf[i]) + if codingState == constants.eError: + if constants._debug: + sys.stderr.write(self.get_charset_name() + + ' prober hit error at byte ' + str(i) + + '\n') + self._mState = constants.eNotMe + break + elif codingState == constants.eItsMe: + self._mState = constants.eFoundIt + break + elif codingState == constants.eStart: + charLen = self._mCodingSM.get_current_charlen() + if i == 0: + self._mLastChar[1] = aBuf[0] + self._mContextAnalyzer.feed(self._mLastChar, charLen) + self._mDistributionAnalyzer.feed(self._mLastChar, charLen) + else: + self._mContextAnalyzer.feed(aBuf[i - 1:i + 1], charLen) + self._mDistributionAnalyzer.feed(aBuf[i - 1:i + 1], + charLen) + + self._mLastChar[0] = aBuf[aLen - 1] + + if self.get_state() == constants.eDetecting: + if (self._mContextAnalyzer.got_enough_data() and + (self.get_confidence() > constants.SHORTCUT_THRESHOLD)): + self._mState = constants.eFoundIt + + return self.get_state() + + def get_confidence(self): + contxtCf = self._mContextAnalyzer.get_confidence() + distribCf = self._mDistributionAnalyzer.get_confidence() + return max(contxtCf, distribCf) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euckrfreq.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euckrfreq.py new file mode 100644 index 0000000..a179e4c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euckrfreq.py @@ -0,0 +1,596 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Sampling from about 20M text materials include literature and computer technology + +# 128 --> 0.79 +# 256 --> 0.92 +# 512 --> 0.986 +# 1024 --> 0.99944 +# 2048 --> 0.99999 +# +# Idea Distribution Ratio = 0.98653 / (1-0.98653) = 73.24 +# Random Distribution Ration = 512 / (2350-512) = 0.279. +# +# Typical Distribution Ratio + +EUCKR_TYPICAL_DISTRIBUTION_RATIO = 6.0 + +EUCKR_TABLE_SIZE = 2352 + +# Char to FreqOrder table , +EUCKRCharToFreqOrder = ( \ + 13, 130, 120,1396, 481,1719,1720, 328, 609, 212,1721, 707, 400, 299,1722, 87, +1397,1723, 104, 536,1117,1203,1724,1267, 685,1268, 508,1725,1726,1727,1728,1398, +1399,1729,1730,1731, 141, 621, 326,1057, 368,1732, 267, 488, 20,1733,1269,1734, + 945,1400,1735, 47, 904,1270,1736,1737, 773, 248,1738, 409, 313, 786, 429,1739, + 116, 987, 813,1401, 683, 75,1204, 145,1740,1741,1742,1743, 16, 847, 667, 622, + 708,1744,1745,1746, 966, 787, 304, 129,1747, 60, 820, 123, 676,1748,1749,1750, +1751, 617,1752, 626,1753,1754,1755,1756, 653,1757,1758,1759,1760,1761,1762, 856, + 344,1763,1764,1765,1766, 89, 401, 418, 806, 905, 848,1767,1768,1769, 946,1205, + 709,1770,1118,1771, 241,1772,1773,1774,1271,1775, 569,1776, 999,1777,1778,1779, +1780, 337, 751,1058, 28, 628, 254,1781, 177, 906, 270, 349, 891,1079,1782, 19, +1783, 379,1784, 315,1785, 629, 754,1402, 559,1786, 636, 203,1206,1787, 710, 567, +1788, 935, 814,1789,1790,1207, 766, 528,1791,1792,1208,1793,1794,1795,1796,1797, +1403,1798,1799, 533,1059,1404,1405,1156,1406, 936, 884,1080,1800, 351,1801,1802, +1803,1804,1805, 801,1806,1807,1808,1119,1809,1157, 714, 474,1407,1810, 298, 899, + 885,1811,1120, 802,1158,1812, 892,1813,1814,1408, 659,1815,1816,1121,1817,1818, +1819,1820,1821,1822, 319,1823, 594, 545,1824, 815, 937,1209,1825,1826, 573,1409, +1022,1827,1210,1828,1829,1830,1831,1832,1833, 556, 722, 807,1122,1060,1834, 697, +1835, 900, 557, 715,1836,1410, 540,1411, 752,1159, 294, 597,1211, 976, 803, 770, +1412,1837,1838, 39, 794,1413, 358,1839, 371, 925,1840, 453, 661, 788, 531, 723, + 544,1023,1081, 869, 91,1841, 392, 430, 790, 602,1414, 677,1082, 457,1415,1416, +1842,1843, 475, 327,1024,1417, 795, 121,1844, 733, 403,1418,1845,1846,1847, 300, + 119, 711,1212, 627,1848,1272, 207,1849,1850, 796,1213, 382,1851, 519,1852,1083, + 893,1853,1854,1855, 367, 809, 487, 671,1856, 663,1857,1858, 956, 471, 306, 857, +1859,1860,1160,1084,1861,1862,1863,1864,1865,1061,1866,1867,1868,1869,1870,1871, + 282, 96, 574,1872, 502,1085,1873,1214,1874, 907,1875,1876, 827, 977,1419,1420, +1421, 268,1877,1422,1878,1879,1880, 308,1881, 2, 537,1882,1883,1215,1884,1885, + 127, 791,1886,1273,1423,1887, 34, 336, 404, 643,1888, 571, 654, 894, 840,1889, + 0, 886,1274, 122, 575, 260, 908, 938,1890,1275, 410, 316,1891,1892, 100,1893, +1894,1123, 48,1161,1124,1025,1895, 633, 901,1276,1896,1897, 115, 816,1898, 317, +1899, 694,1900, 909, 734,1424, 572, 866,1425, 691, 85, 524,1010, 543, 394, 841, +1901,1902,1903,1026,1904,1905,1906,1907,1908,1909, 30, 451, 651, 988, 310,1910, +1911,1426, 810,1216, 93,1912,1913,1277,1217,1914, 858, 759, 45, 58, 181, 610, + 269,1915,1916, 131,1062, 551, 443,1000, 821,1427, 957, 895,1086,1917,1918, 375, +1919, 359,1920, 687,1921, 822,1922, 293,1923,1924, 40, 662, 118, 692, 29, 939, + 887, 640, 482, 174,1925, 69,1162, 728,1428, 910,1926,1278,1218,1279, 386, 870, + 217, 854,1163, 823,1927,1928,1929,1930, 834,1931, 78,1932, 859,1933,1063,1934, +1935,1936,1937, 438,1164, 208, 595,1938,1939,1940,1941,1219,1125,1942, 280, 888, +1429,1430,1220,1431,1943,1944,1945,1946,1947,1280, 150, 510,1432,1948,1949,1950, +1951,1952,1953,1954,1011,1087,1955,1433,1043,1956, 881,1957, 614, 958,1064,1065, +1221,1958, 638,1001, 860, 967, 896,1434, 989, 492, 553,1281,1165,1959,1282,1002, +1283,1222,1960,1961,1962,1963, 36, 383, 228, 753, 247, 454,1964, 876, 678,1965, +1966,1284, 126, 464, 490, 835, 136, 672, 529, 940,1088,1435, 473,1967,1968, 467, + 50, 390, 227, 587, 279, 378, 598, 792, 968, 240, 151, 160, 849, 882,1126,1285, + 639,1044, 133, 140, 288, 360, 811, 563,1027, 561, 142, 523,1969,1970,1971, 7, + 103, 296, 439, 407, 506, 634, 990,1972,1973,1974,1975, 645,1976,1977,1978,1979, +1980,1981, 236,1982,1436,1983,1984,1089, 192, 828, 618, 518,1166, 333,1127,1985, + 818,1223,1986,1987,1988,1989,1990,1991,1992,1993, 342,1128,1286, 746, 842,1994, +1995, 560, 223,1287, 98, 8, 189, 650, 978,1288,1996,1437,1997, 17, 345, 250, + 423, 277, 234, 512, 226, 97, 289, 42, 167,1998, 201,1999,2000, 843, 836, 824, + 532, 338, 783,1090, 182, 576, 436,1438,1439, 527, 500,2001, 947, 889,2002,2003, +2004,2005, 262, 600, 314, 447,2006, 547,2007, 693, 738,1129,2008, 71,1440, 745, + 619, 688,2009, 829,2010,2011, 147,2012, 33, 948,2013,2014, 74, 224,2015, 61, + 191, 918, 399, 637,2016,1028,1130, 257, 902,2017,2018,2019,2020,2021,2022,2023, +2024,2025,2026, 837,2027,2028,2029,2030, 179, 874, 591, 52, 724, 246,2031,2032, +2033,2034,1167, 969,2035,1289, 630, 605, 911,1091,1168,2036,2037,2038,1441, 912, +2039, 623,2040,2041, 253,1169,1290,2042,1442, 146, 620, 611, 577, 433,2043,1224, + 719,1170, 959, 440, 437, 534, 84, 388, 480,1131, 159, 220, 198, 679,2044,1012, + 819,1066,1443, 113,1225, 194, 318,1003,1029,2045,2046,2047,2048,1067,2049,2050, +2051,2052,2053, 59, 913, 112,2054, 632,2055, 455, 144, 739,1291,2056, 273, 681, + 499,2057, 448,2058,2059, 760,2060,2061, 970, 384, 169, 245,1132,2062,2063, 414, +1444,2064,2065, 41, 235,2066, 157, 252, 877, 568, 919, 789, 580,2067, 725,2068, +2069,1292,2070,2071,1445,2072,1446,2073,2074, 55, 588, 66,1447, 271,1092,2075, +1226,2076, 960,1013, 372,2077,2078,2079,2080,2081,1293,2082,2083,2084,2085, 850, +2086,2087,2088,2089,2090, 186,2091,1068, 180,2092,2093,2094, 109,1227, 522, 606, +2095, 867,1448,1093, 991,1171, 926, 353,1133,2096, 581,2097,2098,2099,1294,1449, +1450,2100, 596,1172,1014,1228,2101,1451,1295,1173,1229,2102,2103,1296,1134,1452, + 949,1135,2104,2105,1094,1453,1454,1455,2106,1095,2107,2108,2109,2110,2111,2112, +2113,2114,2115,2116,2117, 804,2118,2119,1230,1231, 805,1456, 405,1136,2120,2121, +2122,2123,2124, 720, 701,1297, 992,1457, 927,1004,2125,2126,2127,2128,2129,2130, + 22, 417,2131, 303,2132, 385,2133, 971, 520, 513,2134,1174, 73,1096, 231, 274, + 962,1458, 673,2135,1459,2136, 152,1137,2137,2138,2139,2140,1005,1138,1460,1139, +2141,2142,2143,2144, 11, 374, 844,2145, 154,1232, 46,1461,2146, 838, 830, 721, +1233, 106,2147, 90, 428, 462, 578, 566,1175, 352,2148,2149, 538,1234, 124,1298, +2150,1462, 761, 565,2151, 686,2152, 649,2153, 72, 173,2154, 460, 415,2155,1463, +2156,1235, 305,2157,2158,2159,2160,2161,2162, 579,2163,2164,2165,2166,2167, 747, +2168,2169,2170,2171,1464, 669,2172,2173,2174,2175,2176,1465,2177, 23, 530, 285, +2178, 335, 729,2179, 397,2180,2181,2182,1030,2183,2184, 698,2185,2186, 325,2187, +2188, 369,2189, 799,1097,1015, 348,2190,1069, 680,2191, 851,1466,2192,2193, 10, +2194, 613, 424,2195, 979, 108, 449, 589, 27, 172, 81,1031, 80, 774, 281, 350, +1032, 525, 301, 582,1176,2196, 674,1045,2197,2198,1467, 730, 762,2199,2200,2201, +2202,1468,2203, 993,2204,2205, 266,1070, 963,1140,2206,2207,2208, 664,1098, 972, +2209,2210,2211,1177,1469,1470, 871,2212,2213,2214,2215,2216,1471,2217,2218,2219, +2220,2221,2222,2223,2224,2225,2226,2227,1472,1236,2228,2229,2230,2231,2232,2233, +2234,2235,1299,2236,2237, 200,2238, 477, 373,2239,2240, 731, 825, 777,2241,2242, +2243, 521, 486, 548,2244,2245,2246,1473,1300, 53, 549, 137, 875, 76, 158,2247, +1301,1474, 469, 396,1016, 278, 712,2248, 321, 442, 503, 767, 744, 941,1237,1178, +1475,2249, 82, 178,1141,1179, 973,2250,1302,2251, 297,2252,2253, 570,2254,2255, +2256, 18, 450, 206,2257, 290, 292,1142,2258, 511, 162, 99, 346, 164, 735,2259, +1476,1477, 4, 554, 343, 798,1099,2260,1100,2261, 43, 171,1303, 139, 215,2262, +2263, 717, 775,2264,1033, 322, 216,2265, 831,2266, 149,2267,1304,2268,2269, 702, +1238, 135, 845, 347, 309,2270, 484,2271, 878, 655, 238,1006,1478,2272, 67,2273, + 295,2274,2275, 461,2276, 478, 942, 412,2277,1034,2278,2279,2280, 265,2281, 541, +2282,2283,2284,2285,2286, 70, 852,1071,2287,2288,2289,2290, 21, 56, 509, 117, + 432,2291,2292, 331, 980, 552,1101, 148, 284, 105, 393,1180,1239, 755,2293, 187, +2294,1046,1479,2295, 340,2296, 63,1047, 230,2297,2298,1305, 763,1306, 101, 800, + 808, 494,2299,2300,2301, 903,2302, 37,1072, 14, 5,2303, 79, 675,2304, 312, +2305,2306,2307,2308,2309,1480, 6,1307,2310,2311,2312, 1, 470, 35, 24, 229, +2313, 695, 210, 86, 778, 15, 784, 592, 779, 32, 77, 855, 964,2314, 259,2315, + 501, 380,2316,2317, 83, 981, 153, 689,1308,1481,1482,1483,2318,2319, 716,1484, +2320,2321,2322,2323,2324,2325,1485,2326,2327, 128, 57, 68, 261,1048, 211, 170, +1240, 31,2328, 51, 435, 742,2329,2330,2331, 635,2332, 264, 456,2333,2334,2335, + 425,2336,1486, 143, 507, 263, 943,2337, 363, 920,1487, 256,1488,1102, 243, 601, +1489,2338,2339,2340,2341,2342,2343,2344, 861,2345,2346,2347,2348,2349,2350, 395, +2351,1490,1491, 62, 535, 166, 225,2352,2353, 668, 419,1241, 138, 604, 928,2354, +1181,2355,1492,1493,2356,2357,2358,1143,2359, 696,2360, 387, 307,1309, 682, 476, +2361,2362, 332, 12, 222, 156,2363, 232,2364, 641, 276, 656, 517,1494,1495,1035, + 416, 736,1496,2365,1017, 586,2366,2367,2368,1497,2369, 242,2370,2371,2372,1498, +2373, 965, 713,2374,2375,2376,2377, 740, 982,1499, 944,1500,1007,2378,2379,1310, +1501,2380,2381,2382, 785, 329,2383,2384,1502,2385,2386,2387, 932,2388,1503,2389, +2390,2391,2392,1242,2393,2394,2395,2396,2397, 994, 950,2398,2399,2400,2401,1504, +1311,2402,2403,2404,2405,1049, 749,2406,2407, 853, 718,1144,1312,2408,1182,1505, +2409,2410, 255, 516, 479, 564, 550, 214,1506,1507,1313, 413, 239, 444, 339,1145, +1036,1508,1509,1314,1037,1510,1315,2411,1511,2412,2413,2414, 176, 703, 497, 624, + 593, 921, 302,2415, 341, 165,1103,1512,2416,1513,2417,2418,2419, 376,2420, 700, +2421,2422,2423, 258, 768,1316,2424,1183,2425, 995, 608,2426,2427,2428,2429, 221, +2430,2431,2432,2433,2434,2435,2436,2437, 195, 323, 726, 188, 897, 983,1317, 377, + 644,1050, 879,2438, 452,2439,2440,2441,2442,2443,2444, 914,2445,2446,2447,2448, + 915, 489,2449,1514,1184,2450,2451, 515, 64, 427, 495,2452, 583,2453, 483, 485, +1038, 562, 213,1515, 748, 666,2454,2455,2456,2457, 334,2458, 780, 996,1008, 705, +1243,2459,2460,2461,2462,2463, 114,2464, 493,1146, 366, 163,1516, 961,1104,2465, + 291,2466,1318,1105,2467,1517, 365,2468, 355, 951,1244,2469,1319,2470, 631,2471, +2472, 218,1320, 364, 320, 756,1518,1519,1321,1520,1322,2473,2474,2475,2476, 997, +2477,2478,2479,2480, 665,1185,2481, 916,1521,2482,2483,2484, 584, 684,2485,2486, + 797,2487,1051,1186,2488,2489,2490,1522,2491,2492, 370,2493,1039,1187, 65,2494, + 434, 205, 463,1188,2495, 125, 812, 391, 402, 826, 699, 286, 398, 155, 781, 771, + 585,2496, 590, 505,1073,2497, 599, 244, 219, 917,1018, 952, 646,1523,2498,1323, +2499,2500, 49, 984, 354, 741,2501, 625,2502,1324,2503,1019, 190, 357, 757, 491, + 95, 782, 868,2504,2505,2506,2507,2508,2509, 134,1524,1074, 422,1525, 898,2510, + 161,2511,2512,2513,2514, 769,2515,1526,2516,2517, 411,1325,2518, 472,1527,2519, +2520,2521,2522,2523,2524, 985,2525,2526,2527,2528,2529,2530, 764,2531,1245,2532, +2533, 25, 204, 311,2534, 496,2535,1052,2536,2537,2538,2539,2540,2541,2542, 199, + 704, 504, 468, 758, 657,1528, 196, 44, 839,1246, 272, 750,2543, 765, 862,2544, +2545,1326,2546, 132, 615, 933,2547, 732,2548,2549,2550,1189,1529,2551, 283,1247, +1053, 607, 929,2552,2553,2554, 930, 183, 872, 616,1040,1147,2555,1148,1020, 441, + 249,1075,2556,2557,2558, 466, 743,2559,2560,2561, 92, 514, 426, 420, 526,2562, +2563,2564,2565,2566,2567,2568, 185,2569,2570,2571,2572, 776,1530, 658,2573, 362, +2574, 361, 922,1076, 793,2575,2576,2577,2578,2579,2580,1531, 251,2581,2582,2583, +2584,1532, 54, 612, 237,1327,2585,2586, 275, 408, 647, 111,2587,1533,1106, 465, + 3, 458, 9, 38,2588, 107, 110, 890, 209, 26, 737, 498,2589,1534,2590, 431, + 202, 88,1535, 356, 287,1107, 660,1149,2591, 381,1536, 986,1150, 445,1248,1151, + 974,2592,2593, 846,2594, 446, 953, 184,1249,1250, 727,2595, 923, 193, 883,2596, +2597,2598, 102, 324, 539, 817,2599, 421,1041,2600, 832,2601, 94, 175, 197, 406, +2602, 459,2603,2604,2605,2606,2607, 330, 555,2608,2609,2610, 706,1108, 389,2611, +2612,2613,2614, 233,2615, 833, 558, 931, 954,1251,2616,2617,1537, 546,2618,2619, +1009,2620,2621,2622,1538, 690,1328,2623, 955,2624,1539,2625,2626, 772,2627,2628, +2629,2630,2631, 924, 648, 863, 603,2632,2633, 934,1540, 864, 865,2634, 642,1042, + 670,1190,2635,2636,2637,2638, 168,2639, 652, 873, 542,1054,1541,2640,2641,2642, # 512, 256 +#Everything below is of no interest for detection purpose +2643,2644,2645,2646,2647,2648,2649,2650,2651,2652,2653,2654,2655,2656,2657,2658, +2659,2660,2661,2662,2663,2664,2665,2666,2667,2668,2669,2670,2671,2672,2673,2674, +2675,2676,2677,2678,2679,2680,2681,2682,2683,2684,2685,2686,2687,2688,2689,2690, +2691,2692,2693,2694,2695,2696,2697,2698,2699,1542, 880,2700,2701,2702,2703,2704, +2705,2706,2707,2708,2709,2710,2711,2712,2713,2714,2715,2716,2717,2718,2719,2720, +2721,2722,2723,2724,2725,1543,2726,2727,2728,2729,2730,2731,2732,1544,2733,2734, +2735,2736,2737,2738,2739,2740,2741,2742,2743,2744,2745,2746,2747,2748,2749,2750, +2751,2752,2753,2754,1545,2755,2756,2757,2758,2759,2760,2761,2762,2763,2764,2765, +2766,1546,2767,1547,2768,2769,2770,2771,2772,2773,2774,2775,2776,2777,2778,2779, +2780,2781,2782,2783,2784,2785,2786,1548,2787,2788,2789,1109,2790,2791,2792,2793, +2794,2795,2796,2797,2798,2799,2800,2801,2802,2803,2804,2805,2806,2807,2808,2809, +2810,2811,2812,1329,2813,2814,2815,2816,2817,2818,2819,2820,2821,2822,2823,2824, +2825,2826,2827,2828,2829,2830,2831,2832,2833,2834,2835,2836,2837,2838,2839,2840, +2841,2842,2843,2844,2845,2846,2847,2848,2849,2850,2851,2852,2853,2854,2855,2856, +1549,2857,2858,2859,2860,1550,2861,2862,1551,2863,2864,2865,2866,2867,2868,2869, +2870,2871,2872,2873,2874,1110,1330,2875,2876,2877,2878,2879,2880,2881,2882,2883, +2884,2885,2886,2887,2888,2889,2890,2891,2892,2893,2894,2895,2896,2897,2898,2899, +2900,2901,2902,2903,2904,2905,2906,2907,2908,2909,2910,2911,2912,2913,2914,2915, +2916,2917,2918,2919,2920,2921,2922,2923,2924,2925,2926,2927,2928,2929,2930,1331, +2931,2932,2933,2934,2935,2936,2937,2938,2939,2940,2941,2942,2943,1552,2944,2945, +2946,2947,2948,2949,2950,2951,2952,2953,2954,2955,2956,2957,2958,2959,2960,2961, +2962,2963,2964,1252,2965,2966,2967,2968,2969,2970,2971,2972,2973,2974,2975,2976, +2977,2978,2979,2980,2981,2982,2983,2984,2985,2986,2987,2988,2989,2990,2991,2992, +2993,2994,2995,2996,2997,2998,2999,3000,3001,3002,3003,3004,3005,3006,3007,3008, +3009,3010,3011,3012,1553,3013,3014,3015,3016,3017,1554,3018,1332,3019,3020,3021, +3022,3023,3024,3025,3026,3027,3028,3029,3030,3031,3032,3033,3034,3035,3036,3037, +3038,3039,3040,3041,3042,3043,3044,3045,3046,3047,3048,3049,3050,1555,3051,3052, +3053,1556,1557,3054,3055,3056,3057,3058,3059,3060,3061,3062,3063,3064,3065,3066, +3067,1558,3068,3069,3070,3071,3072,3073,3074,3075,3076,1559,3077,3078,3079,3080, +3081,3082,3083,1253,3084,3085,3086,3087,3088,3089,3090,3091,3092,3093,3094,3095, +3096,3097,3098,3099,3100,3101,3102,3103,3104,3105,3106,3107,3108,1152,3109,3110, +3111,3112,3113,1560,3114,3115,3116,3117,1111,3118,3119,3120,3121,3122,3123,3124, +3125,3126,3127,3128,3129,3130,3131,3132,3133,3134,3135,3136,3137,3138,3139,3140, +3141,3142,3143,3144,3145,3146,3147,3148,3149,3150,3151,3152,3153,3154,3155,3156, +3157,3158,3159,3160,3161,3162,3163,3164,3165,3166,3167,3168,3169,3170,3171,3172, +3173,3174,3175,3176,1333,3177,3178,3179,3180,3181,3182,3183,3184,3185,3186,3187, +3188,3189,1561,3190,3191,1334,3192,3193,3194,3195,3196,3197,3198,3199,3200,3201, +3202,3203,3204,3205,3206,3207,3208,3209,3210,3211,3212,3213,3214,3215,3216,3217, +3218,3219,3220,3221,3222,3223,3224,3225,3226,3227,3228,3229,3230,3231,3232,3233, +3234,1562,3235,3236,3237,3238,3239,3240,3241,3242,3243,3244,3245,3246,3247,3248, +3249,3250,3251,3252,3253,3254,3255,3256,3257,3258,3259,3260,3261,3262,3263,3264, +3265,3266,3267,3268,3269,3270,3271,3272,3273,3274,3275,3276,3277,1563,3278,3279, +3280,3281,3282,3283,3284,3285,3286,3287,3288,3289,3290,3291,3292,3293,3294,3295, +3296,3297,3298,3299,3300,3301,3302,3303,3304,3305,3306,3307,3308,3309,3310,3311, +3312,3313,3314,3315,3316,3317,3318,3319,3320,3321,3322,3323,3324,3325,3326,3327, +3328,3329,3330,3331,3332,3333,3334,3335,3336,3337,3338,3339,3340,3341,3342,3343, +3344,3345,3346,3347,3348,3349,3350,3351,3352,3353,3354,3355,3356,3357,3358,3359, +3360,3361,3362,3363,3364,1335,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374, +3375,3376,3377,3378,3379,3380,3381,3382,3383,3384,3385,3386,3387,1336,3388,3389, +3390,3391,3392,3393,3394,3395,3396,3397,3398,3399,3400,3401,3402,3403,3404,3405, +3406,3407,3408,3409,3410,3411,3412,3413,3414,1337,3415,3416,3417,3418,3419,1338, +3420,3421,3422,1564,1565,3423,3424,3425,3426,3427,3428,3429,3430,3431,1254,3432, +3433,3434,1339,3435,3436,3437,3438,3439,1566,3440,3441,3442,3443,3444,3445,3446, +3447,3448,3449,3450,3451,3452,3453,3454,1255,3455,3456,3457,3458,3459,1567,1191, +3460,1568,1569,3461,3462,3463,1570,3464,3465,3466,3467,3468,1571,3469,3470,3471, +3472,3473,1572,3474,3475,3476,3477,3478,3479,3480,3481,3482,3483,3484,3485,3486, +1340,3487,3488,3489,3490,3491,3492,1021,3493,3494,3495,3496,3497,3498,1573,3499, +1341,3500,3501,3502,3503,3504,3505,3506,3507,3508,3509,3510,3511,1342,3512,3513, +3514,3515,3516,1574,1343,3517,3518,3519,1575,3520,1576,3521,3522,3523,3524,3525, +3526,3527,3528,3529,3530,3531,3532,3533,3534,3535,3536,3537,3538,3539,3540,3541, +3542,3543,3544,3545,3546,3547,3548,3549,3550,3551,3552,3553,3554,3555,3556,3557, +3558,3559,3560,3561,3562,3563,3564,3565,3566,3567,3568,3569,3570,3571,3572,3573, +3574,3575,3576,3577,3578,3579,3580,1577,3581,3582,1578,3583,3584,3585,3586,3587, +3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603, +3604,1579,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,3617,3618, +3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,1580,3630,3631,1581,3632, +3633,3634,3635,3636,3637,3638,3639,3640,3641,3642,3643,3644,3645,3646,3647,3648, +3649,3650,3651,3652,3653,3654,3655,3656,1582,3657,3658,3659,3660,3661,3662,3663, +3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,3676,3677,3678,3679, +3680,3681,3682,3683,3684,3685,3686,3687,3688,3689,3690,3691,3692,3693,3694,3695, +3696,3697,3698,3699,3700,1192,3701,3702,3703,3704,1256,3705,3706,3707,3708,1583, +1257,3709,3710,3711,3712,3713,3714,3715,3716,1584,3717,3718,3719,3720,3721,3722, +3723,3724,3725,3726,3727,3728,3729,3730,3731,3732,3733,3734,3735,3736,3737,3738, +3739,3740,3741,3742,3743,3744,3745,1344,3746,3747,3748,3749,3750,3751,3752,3753, +3754,3755,3756,1585,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,1586,3767, +3768,3769,3770,3771,3772,3773,3774,3775,3776,3777,3778,1345,3779,3780,3781,3782, +3783,3784,3785,3786,3787,3788,3789,3790,3791,3792,3793,3794,3795,1346,1587,3796, +3797,1588,3798,3799,3800,3801,3802,3803,3804,3805,3806,1347,3807,3808,3809,3810, +3811,1589,3812,3813,3814,3815,3816,3817,3818,3819,3820,3821,1590,3822,3823,1591, +1348,3824,3825,3826,3827,3828,3829,3830,1592,3831,3832,1593,3833,3834,3835,3836, +3837,3838,3839,3840,3841,3842,3843,3844,1349,3845,3846,3847,3848,3849,3850,3851, +3852,3853,3854,3855,3856,3857,3858,1594,3859,3860,3861,3862,3863,3864,3865,3866, +3867,3868,3869,1595,3870,3871,3872,3873,1596,3874,3875,3876,3877,3878,3879,3880, +3881,3882,3883,3884,3885,3886,1597,3887,3888,3889,3890,3891,3892,3893,3894,3895, +1598,3896,3897,3898,1599,1600,3899,1350,3900,1351,3901,3902,1352,3903,3904,3905, +3906,3907,3908,3909,3910,3911,3912,3913,3914,3915,3916,3917,3918,3919,3920,3921, +3922,3923,3924,1258,3925,3926,3927,3928,3929,3930,3931,1193,3932,1601,3933,3934, +3935,3936,3937,3938,3939,3940,3941,3942,3943,1602,3944,3945,3946,3947,3948,1603, +3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959,3960,3961,3962,3963,3964, +3965,1604,3966,3967,3968,3969,3970,3971,3972,3973,3974,3975,3976,3977,1353,3978, +3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,3989,3990,3991,1354,3992,3993, +3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005,4006,4007,4008,4009, +4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021,4022,4023,1355,4024, +4025,4026,4027,4028,4029,4030,4031,4032,4033,4034,4035,4036,4037,4038,4039,4040, +1605,4041,4042,4043,4044,4045,4046,4047,4048,4049,4050,4051,4052,4053,4054,4055, +4056,4057,4058,4059,4060,1606,4061,4062,4063,4064,1607,4065,4066,4067,4068,4069, +4070,4071,4072,4073,4074,4075,4076,1194,4077,4078,1608,4079,4080,4081,4082,4083, +4084,4085,4086,4087,1609,4088,4089,4090,4091,4092,4093,4094,4095,4096,4097,4098, +4099,4100,4101,4102,4103,4104,4105,4106,4107,4108,1259,4109,4110,4111,4112,4113, +4114,4115,4116,4117,4118,4119,4120,4121,4122,4123,4124,1195,4125,4126,4127,1610, +4128,4129,4130,4131,4132,4133,4134,4135,4136,4137,1356,4138,4139,4140,4141,4142, +4143,4144,1611,4145,4146,4147,4148,4149,4150,4151,4152,4153,4154,4155,4156,4157, +4158,4159,4160,4161,4162,4163,4164,4165,4166,4167,4168,4169,4170,4171,4172,4173, +4174,4175,4176,4177,4178,4179,4180,4181,4182,4183,4184,4185,4186,4187,4188,4189, +4190,4191,4192,4193,4194,4195,4196,4197,4198,4199,4200,4201,4202,4203,4204,4205, +4206,4207,4208,4209,4210,4211,4212,4213,4214,4215,4216,4217,4218,4219,1612,4220, +4221,4222,4223,4224,4225,4226,4227,1357,4228,1613,4229,4230,4231,4232,4233,4234, +4235,4236,4237,4238,4239,4240,4241,4242,4243,1614,4244,4245,4246,4247,4248,4249, +4250,4251,4252,4253,4254,4255,4256,4257,4258,4259,4260,4261,4262,4263,4264,4265, +4266,4267,4268,4269,4270,1196,1358,4271,4272,4273,4274,4275,4276,4277,4278,4279, +4280,4281,4282,4283,4284,4285,4286,4287,1615,4288,4289,4290,4291,4292,4293,4294, +4295,4296,4297,4298,4299,4300,4301,4302,4303,4304,4305,4306,4307,4308,4309,4310, +4311,4312,4313,4314,4315,4316,4317,4318,4319,4320,4321,4322,4323,4324,4325,4326, +4327,4328,4329,4330,4331,4332,4333,4334,1616,4335,4336,4337,4338,4339,4340,4341, +4342,4343,4344,4345,4346,4347,4348,4349,4350,4351,4352,4353,4354,4355,4356,4357, +4358,4359,4360,1617,4361,4362,4363,4364,4365,1618,4366,4367,4368,4369,4370,4371, +4372,4373,4374,4375,4376,4377,4378,4379,4380,4381,4382,4383,4384,4385,4386,4387, +4388,4389,4390,4391,4392,4393,4394,4395,4396,4397,4398,4399,4400,4401,4402,4403, +4404,4405,4406,4407,4408,4409,4410,4411,4412,4413,4414,4415,4416,1619,4417,4418, +4419,4420,4421,4422,4423,4424,4425,1112,4426,4427,4428,4429,4430,1620,4431,4432, +4433,4434,4435,4436,4437,4438,4439,4440,4441,4442,1260,1261,4443,4444,4445,4446, +4447,4448,4449,4450,4451,4452,4453,4454,4455,1359,4456,4457,4458,4459,4460,4461, +4462,4463,4464,4465,1621,4466,4467,4468,4469,4470,4471,4472,4473,4474,4475,4476, +4477,4478,4479,4480,4481,4482,4483,4484,4485,4486,4487,4488,4489,1055,4490,4491, +4492,4493,4494,4495,4496,4497,4498,4499,4500,4501,4502,4503,4504,4505,4506,4507, +4508,4509,4510,4511,4512,4513,4514,4515,4516,4517,4518,1622,4519,4520,4521,1623, +4522,4523,4524,4525,4526,4527,4528,4529,4530,4531,4532,4533,4534,4535,1360,4536, +4537,4538,4539,4540,4541,4542,4543, 975,4544,4545,4546,4547,4548,4549,4550,4551, +4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,4567, +4568,4569,4570,4571,1624,4572,4573,4574,4575,4576,1625,4577,4578,4579,4580,4581, +4582,4583,4584,1626,4585,4586,4587,4588,4589,4590,4591,4592,4593,4594,4595,1627, +4596,4597,4598,4599,4600,4601,4602,4603,4604,4605,4606,4607,4608,4609,4610,4611, +4612,4613,4614,4615,1628,4616,4617,4618,4619,4620,4621,4622,4623,4624,4625,4626, +4627,4628,4629,4630,4631,4632,4633,4634,4635,4636,4637,4638,4639,4640,4641,4642, +4643,4644,4645,4646,4647,4648,4649,1361,4650,4651,4652,4653,4654,4655,4656,4657, +4658,4659,4660,4661,1362,4662,4663,4664,4665,4666,4667,4668,4669,4670,4671,4672, +4673,4674,4675,4676,4677,4678,4679,4680,4681,4682,1629,4683,4684,4685,4686,4687, +1630,4688,4689,4690,4691,1153,4692,4693,4694,1113,4695,4696,4697,4698,4699,4700, +4701,4702,4703,4704,4705,4706,4707,4708,4709,4710,4711,1197,4712,4713,4714,4715, +4716,4717,4718,4719,4720,4721,4722,4723,4724,4725,4726,4727,4728,4729,4730,4731, +4732,4733,4734,4735,1631,4736,1632,4737,4738,4739,4740,4741,4742,4743,4744,1633, +4745,4746,4747,4748,4749,1262,4750,4751,4752,4753,4754,1363,4755,4756,4757,4758, +4759,4760,4761,4762,4763,4764,4765,4766,4767,4768,1634,4769,4770,4771,4772,4773, +4774,4775,4776,4777,4778,1635,4779,4780,4781,4782,4783,4784,4785,4786,4787,4788, +4789,1636,4790,4791,4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803, +4804,4805,4806,1637,4807,4808,4809,1638,4810,4811,4812,4813,4814,4815,4816,4817, +4818,1639,4819,4820,4821,4822,4823,4824,4825,4826,4827,4828,4829,4830,4831,4832, +4833,1077,4834,4835,4836,4837,4838,4839,4840,4841,4842,4843,4844,4845,4846,4847, +4848,4849,4850,4851,4852,4853,4854,4855,4856,4857,4858,4859,4860,4861,4862,4863, +4864,4865,4866,4867,4868,4869,4870,4871,4872,4873,4874,4875,4876,4877,4878,4879, +4880,4881,4882,4883,1640,4884,4885,1641,4886,4887,4888,4889,4890,4891,4892,4893, +4894,4895,4896,4897,4898,4899,4900,4901,4902,4903,4904,4905,4906,4907,4908,4909, +4910,4911,1642,4912,4913,4914,1364,4915,4916,4917,4918,4919,4920,4921,4922,4923, +4924,4925,4926,4927,4928,4929,4930,4931,1643,4932,4933,4934,4935,4936,4937,4938, +4939,4940,4941,4942,4943,4944,4945,4946,4947,4948,4949,4950,4951,4952,4953,4954, +4955,4956,4957,4958,4959,4960,4961,4962,4963,4964,4965,4966,4967,4968,4969,4970, +4971,4972,4973,4974,4975,4976,4977,4978,4979,4980,1644,4981,4982,4983,4984,1645, +4985,4986,1646,4987,4988,4989,4990,4991,4992,4993,4994,4995,4996,4997,4998,4999, +5000,5001,5002,5003,5004,5005,1647,5006,1648,5007,5008,5009,5010,5011,5012,1078, +5013,5014,5015,5016,5017,5018,5019,5020,5021,5022,5023,5024,5025,5026,5027,5028, +1365,5029,5030,5031,5032,5033,5034,5035,5036,5037,5038,5039,1649,5040,5041,5042, +5043,5044,5045,1366,5046,5047,5048,5049,5050,5051,5052,5053,5054,5055,1650,5056, +5057,5058,5059,5060,5061,5062,5063,5064,5065,5066,5067,5068,5069,5070,5071,5072, +5073,5074,5075,5076,5077,1651,5078,5079,5080,5081,5082,5083,5084,5085,5086,5087, +5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102,5103, +5104,5105,5106,5107,5108,5109,5110,1652,5111,5112,5113,5114,5115,5116,5117,5118, +1367,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,1653,5130,5131,5132, +5133,5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148, +5149,1368,5150,1654,5151,1369,5152,5153,5154,5155,5156,5157,5158,5159,5160,5161, +5162,5163,5164,5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,5176,5177, +5178,1370,5179,5180,5181,5182,5183,5184,5185,5186,5187,5188,5189,5190,5191,5192, +5193,5194,5195,5196,5197,5198,1655,5199,5200,5201,5202,1656,5203,5204,5205,5206, +1371,5207,1372,5208,5209,5210,5211,1373,5212,5213,1374,5214,5215,5216,5217,5218, +5219,5220,5221,5222,5223,5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234, +5235,5236,5237,5238,5239,5240,5241,5242,5243,5244,5245,5246,5247,1657,5248,5249, +5250,5251,1658,1263,5252,5253,5254,5255,5256,1375,5257,5258,5259,5260,5261,5262, +5263,5264,5265,5266,5267,5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278, +5279,5280,5281,5282,5283,1659,5284,5285,5286,5287,5288,5289,5290,5291,5292,5293, +5294,5295,5296,5297,5298,5299,5300,1660,5301,5302,5303,5304,5305,5306,5307,5308, +5309,5310,5311,5312,5313,5314,5315,5316,5317,5318,5319,5320,5321,1376,5322,5323, +5324,5325,5326,5327,5328,5329,5330,5331,5332,5333,1198,5334,5335,5336,5337,5338, +5339,5340,5341,5342,5343,1661,5344,5345,5346,5347,5348,5349,5350,5351,5352,5353, +5354,5355,5356,5357,5358,5359,5360,5361,5362,5363,5364,5365,5366,5367,5368,5369, +5370,5371,5372,5373,5374,5375,5376,5377,5378,5379,5380,5381,5382,5383,5384,5385, +5386,5387,5388,5389,5390,5391,5392,5393,5394,5395,5396,5397,5398,1264,5399,5400, +5401,5402,5403,5404,5405,5406,5407,5408,5409,5410,5411,5412,1662,5413,5414,5415, +5416,1663,5417,5418,5419,5420,5421,5422,5423,5424,5425,5426,5427,5428,5429,5430, +5431,5432,5433,5434,5435,5436,5437,5438,1664,5439,5440,5441,5442,5443,5444,5445, +5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456,5457,5458,5459,5460,5461, +5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472,5473,5474,5475,5476,5477, +5478,1154,5479,5480,5481,5482,5483,5484,5485,1665,5486,5487,5488,5489,5490,5491, +5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504,5505,5506,5507, +5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520,5521,5522,5523, +5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536,5537,5538,5539, +5540,5541,5542,5543,5544,5545,5546,5547,5548,1377,5549,5550,5551,5552,5553,5554, +5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568,5569,5570, +1114,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584,5585, +5586,5587,5588,5589,5590,5591,5592,1378,5593,5594,5595,5596,5597,5598,5599,5600, +5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,1379,5615, +5616,5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631, +5632,5633,5634,1380,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646, +5647,5648,5649,1381,1056,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660, +1666,5661,5662,5663,5664,5665,5666,5667,5668,1667,5669,1668,5670,5671,5672,5673, +5674,5675,5676,5677,5678,1155,5679,5680,5681,5682,5683,5684,5685,5686,5687,5688, +5689,5690,5691,5692,5693,5694,5695,5696,5697,5698,1669,5699,5700,5701,5702,5703, +5704,5705,1670,5706,5707,5708,5709,5710,1671,5711,5712,5713,5714,1382,5715,5716, +5717,5718,5719,5720,5721,5722,5723,5724,5725,1672,5726,5727,1673,1674,5728,5729, +5730,5731,5732,5733,5734,5735,5736,1675,5737,5738,5739,5740,5741,5742,5743,5744, +1676,5745,5746,5747,5748,5749,5750,5751,1383,5752,5753,5754,5755,5756,5757,5758, +5759,5760,5761,5762,5763,5764,5765,5766,5767,5768,1677,5769,5770,5771,5772,5773, +1678,5774,5775,5776, 998,5777,5778,5779,5780,5781,5782,5783,5784,5785,1384,5786, +5787,5788,5789,5790,5791,5792,5793,5794,5795,5796,5797,5798,5799,5800,1679,5801, +5802,5803,1115,1116,5804,5805,5806,5807,5808,5809,5810,5811,5812,5813,5814,5815, +5816,5817,5818,5819,5820,5821,5822,5823,5824,5825,5826,5827,5828,5829,5830,5831, +5832,5833,5834,5835,5836,5837,5838,5839,5840,5841,5842,5843,5844,5845,5846,5847, +5848,5849,5850,5851,5852,5853,5854,5855,1680,5856,5857,5858,5859,5860,5861,5862, +5863,5864,1681,5865,5866,5867,1682,5868,5869,5870,5871,5872,5873,5874,5875,5876, +5877,5878,5879,1683,5880,1684,5881,5882,5883,5884,1685,5885,5886,5887,5888,5889, +5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904,5905, +5906,5907,1686,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920, +5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,1687, +5936,5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951, +5952,1688,1689,5953,1199,5954,5955,5956,5957,5958,5959,5960,5961,1690,5962,5963, +5964,5965,5966,5967,5968,5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979, +5980,5981,1385,5982,1386,5983,5984,5985,5986,5987,5988,5989,5990,5991,5992,5993, +5994,5995,5996,5997,5998,5999,6000,6001,6002,6003,6004,6005,6006,6007,6008,6009, +6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020,6021,6022,6023,6024,6025, +6026,6027,1265,6028,6029,1691,6030,6031,6032,6033,6034,6035,6036,6037,6038,6039, +6040,6041,6042,6043,6044,6045,6046,6047,6048,6049,6050,6051,6052,6053,6054,6055, +6056,6057,6058,6059,6060,6061,6062,6063,6064,6065,6066,6067,6068,6069,6070,6071, +6072,6073,6074,6075,6076,6077,6078,6079,6080,6081,6082,6083,6084,1692,6085,6086, +6087,6088,6089,6090,6091,6092,6093,6094,6095,6096,6097,6098,6099,6100,6101,6102, +6103,6104,6105,6106,6107,6108,6109,6110,6111,6112,6113,6114,6115,6116,6117,6118, +6119,6120,6121,6122,6123,6124,6125,6126,6127,6128,6129,6130,6131,1693,6132,6133, +6134,6135,6136,1694,6137,6138,6139,6140,6141,1695,6142,6143,6144,6145,6146,6147, +6148,6149,6150,6151,6152,6153,6154,6155,6156,6157,6158,6159,6160,6161,6162,6163, +6164,6165,6166,6167,6168,6169,6170,6171,6172,6173,6174,6175,6176,6177,6178,6179, +6180,6181,6182,6183,6184,6185,1696,6186,6187,6188,6189,6190,6191,6192,6193,6194, +6195,6196,6197,6198,6199,6200,6201,6202,6203,6204,6205,6206,6207,6208,6209,6210, +6211,6212,6213,6214,6215,6216,6217,6218,6219,1697,6220,6221,6222,6223,6224,6225, +6226,6227,6228,6229,6230,6231,6232,6233,6234,6235,6236,6237,6238,6239,6240,6241, +6242,6243,6244,6245,6246,6247,6248,6249,6250,6251,6252,6253,1698,6254,6255,6256, +6257,6258,6259,6260,6261,6262,6263,1200,6264,6265,6266,6267,6268,6269,6270,6271, #1024 +6272,6273,6274,6275,6276,6277,6278,6279,6280,6281,6282,6283,6284,6285,6286,6287, +6288,6289,6290,6291,6292,6293,6294,6295,6296,6297,6298,6299,6300,6301,6302,1699, +6303,6304,1700,6305,6306,6307,6308,6309,6310,6311,6312,6313,6314,6315,6316,6317, +6318,6319,6320,6321,6322,6323,6324,6325,6326,6327,6328,6329,6330,6331,6332,6333, +6334,6335,6336,6337,6338,6339,1701,6340,6341,6342,6343,6344,1387,6345,6346,6347, +6348,6349,6350,6351,6352,6353,6354,6355,6356,6357,6358,6359,6360,6361,6362,6363, +6364,6365,6366,6367,6368,6369,6370,6371,6372,6373,6374,6375,6376,6377,6378,6379, +6380,6381,6382,6383,6384,6385,6386,6387,6388,6389,6390,6391,6392,6393,6394,6395, +6396,6397,6398,6399,6400,6401,6402,6403,6404,6405,6406,6407,6408,6409,6410,6411, +6412,6413,1702,6414,6415,6416,6417,6418,6419,6420,6421,6422,1703,6423,6424,6425, +6426,6427,6428,6429,6430,6431,6432,6433,6434,6435,6436,6437,6438,1704,6439,6440, +6441,6442,6443,6444,6445,6446,6447,6448,6449,6450,6451,6452,6453,6454,6455,6456, +6457,6458,6459,6460,6461,6462,6463,6464,6465,6466,6467,6468,6469,6470,6471,6472, +6473,6474,6475,6476,6477,6478,6479,6480,6481,6482,6483,6484,6485,6486,6487,6488, +6489,6490,6491,6492,6493,6494,6495,6496,6497,6498,6499,6500,6501,6502,6503,1266, +6504,6505,6506,6507,6508,6509,6510,6511,6512,6513,6514,6515,6516,6517,6518,6519, +6520,6521,6522,6523,6524,6525,6526,6527,6528,6529,6530,6531,6532,6533,6534,6535, +6536,6537,6538,6539,6540,6541,6542,6543,6544,6545,6546,6547,6548,6549,6550,6551, +1705,1706,6552,6553,6554,6555,6556,6557,6558,6559,6560,6561,6562,6563,6564,6565, +6566,6567,6568,6569,6570,6571,6572,6573,6574,6575,6576,6577,6578,6579,6580,6581, +6582,6583,6584,6585,6586,6587,6588,6589,6590,6591,6592,6593,6594,6595,6596,6597, +6598,6599,6600,6601,6602,6603,6604,6605,6606,6607,6608,6609,6610,6611,6612,6613, +6614,6615,6616,6617,6618,6619,6620,6621,6622,6623,6624,6625,6626,6627,6628,6629, +6630,6631,6632,6633,6634,6635,6636,6637,1388,6638,6639,6640,6641,6642,6643,6644, +1707,6645,6646,6647,6648,6649,6650,6651,6652,6653,6654,6655,6656,6657,6658,6659, +6660,6661,6662,6663,1708,6664,6665,6666,6667,6668,6669,6670,6671,6672,6673,6674, +1201,6675,6676,6677,6678,6679,6680,6681,6682,6683,6684,6685,6686,6687,6688,6689, +6690,6691,6692,6693,6694,6695,6696,6697,6698,6699,6700,6701,6702,6703,6704,6705, +6706,6707,6708,6709,6710,6711,6712,6713,6714,6715,6716,6717,6718,6719,6720,6721, +6722,6723,6724,6725,1389,6726,6727,6728,6729,6730,6731,6732,6733,6734,6735,6736, +1390,1709,6737,6738,6739,6740,6741,6742,1710,6743,6744,6745,6746,1391,6747,6748, +6749,6750,6751,6752,6753,6754,6755,6756,6757,1392,6758,6759,6760,6761,6762,6763, +6764,6765,6766,6767,6768,6769,6770,6771,6772,6773,6774,6775,6776,6777,6778,6779, +6780,1202,6781,6782,6783,6784,6785,6786,6787,6788,6789,6790,6791,6792,6793,6794, +6795,6796,6797,6798,6799,6800,6801,6802,6803,6804,6805,6806,6807,6808,6809,1711, +6810,6811,6812,6813,6814,6815,6816,6817,6818,6819,6820,6821,6822,6823,6824,6825, +6826,6827,6828,6829,6830,6831,6832,6833,6834,6835,6836,1393,6837,6838,6839,6840, +6841,6842,6843,6844,6845,6846,6847,6848,6849,6850,6851,6852,6853,6854,6855,6856, +6857,6858,6859,6860,6861,6862,6863,6864,6865,6866,6867,6868,6869,6870,6871,6872, +6873,6874,6875,6876,6877,6878,6879,6880,6881,6882,6883,6884,6885,6886,6887,6888, +6889,6890,6891,6892,6893,6894,6895,6896,6897,6898,6899,6900,6901,6902,1712,6903, +6904,6905,6906,6907,6908,6909,6910,1713,6911,6912,6913,6914,6915,6916,6917,6918, +6919,6920,6921,6922,6923,6924,6925,6926,6927,6928,6929,6930,6931,6932,6933,6934, +6935,6936,6937,6938,6939,6940,6941,6942,6943,6944,6945,6946,6947,6948,6949,6950, +6951,6952,6953,6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6964,6965,6966, +6967,6968,6969,6970,6971,6972,6973,6974,1714,6975,6976,6977,6978,6979,6980,6981, +6982,6983,6984,6985,6986,6987,6988,1394,6989,6990,6991,6992,6993,6994,6995,6996, +6997,6998,6999,7000,1715,7001,7002,7003,7004,7005,7006,7007,7008,7009,7010,7011, +7012,7013,7014,7015,7016,7017,7018,7019,7020,7021,7022,7023,7024,7025,7026,7027, +7028,1716,7029,7030,7031,7032,7033,7034,7035,7036,7037,7038,7039,7040,7041,7042, +7043,7044,7045,7046,7047,7048,7049,7050,7051,7052,7053,7054,7055,7056,7057,7058, +7059,7060,7061,7062,7063,7064,7065,7066,7067,7068,7069,7070,7071,7072,7073,7074, +7075,7076,7077,7078,7079,7080,7081,7082,7083,7084,7085,7086,7087,7088,7089,7090, +7091,7092,7093,7094,7095,7096,7097,7098,7099,7100,7101,7102,7103,7104,7105,7106, +7107,7108,7109,7110,7111,7112,7113,7114,7115,7116,7117,7118,7119,7120,7121,7122, +7123,7124,7125,7126,7127,7128,7129,7130,7131,7132,7133,7134,7135,7136,7137,7138, +7139,7140,7141,7142,7143,7144,7145,7146,7147,7148,7149,7150,7151,7152,7153,7154, +7155,7156,7157,7158,7159,7160,7161,7162,7163,7164,7165,7166,7167,7168,7169,7170, +7171,7172,7173,7174,7175,7176,7177,7178,7179,7180,7181,7182,7183,7184,7185,7186, +7187,7188,7189,7190,7191,7192,7193,7194,7195,7196,7197,7198,7199,7200,7201,7202, +7203,7204,7205,7206,7207,1395,7208,7209,7210,7211,7212,7213,1717,7214,7215,7216, +7217,7218,7219,7220,7221,7222,7223,7224,7225,7226,7227,7228,7229,7230,7231,7232, +7233,7234,7235,7236,7237,7238,7239,7240,7241,7242,7243,7244,7245,7246,7247,7248, +7249,7250,7251,7252,7253,7254,7255,7256,7257,7258,7259,7260,7261,7262,7263,7264, +7265,7266,7267,7268,7269,7270,7271,7272,7273,7274,7275,7276,7277,7278,7279,7280, +7281,7282,7283,7284,7285,7286,7287,7288,7289,7290,7291,7292,7293,7294,7295,7296, +7297,7298,7299,7300,7301,7302,7303,7304,7305,7306,7307,7308,7309,7310,7311,7312, +7313,1718,7314,7315,7316,7317,7318,7319,7320,7321,7322,7323,7324,7325,7326,7327, +7328,7329,7330,7331,7332,7333,7334,7335,7336,7337,7338,7339,7340,7341,7342,7343, +7344,7345,7346,7347,7348,7349,7350,7351,7352,7353,7354,7355,7356,7357,7358,7359, +7360,7361,7362,7363,7364,7365,7366,7367,7368,7369,7370,7371,7372,7373,7374,7375, +7376,7377,7378,7379,7380,7381,7382,7383,7384,7385,7386,7387,7388,7389,7390,7391, +7392,7393,7394,7395,7396,7397,7398,7399,7400,7401,7402,7403,7404,7405,7406,7407, +7408,7409,7410,7411,7412,7413,7414,7415,7416,7417,7418,7419,7420,7421,7422,7423, +7424,7425,7426,7427,7428,7429,7430,7431,7432,7433,7434,7435,7436,7437,7438,7439, +7440,7441,7442,7443,7444,7445,7446,7447,7448,7449,7450,7451,7452,7453,7454,7455, +7456,7457,7458,7459,7460,7461,7462,7463,7464,7465,7466,7467,7468,7469,7470,7471, +7472,7473,7474,7475,7476,7477,7478,7479,7480,7481,7482,7483,7484,7485,7486,7487, +7488,7489,7490,7491,7492,7493,7494,7495,7496,7497,7498,7499,7500,7501,7502,7503, +7504,7505,7506,7507,7508,7509,7510,7511,7512,7513,7514,7515,7516,7517,7518,7519, +7520,7521,7522,7523,7524,7525,7526,7527,7528,7529,7530,7531,7532,7533,7534,7535, +7536,7537,7538,7539,7540,7541,7542,7543,7544,7545,7546,7547,7548,7549,7550,7551, +7552,7553,7554,7555,7556,7557,7558,7559,7560,7561,7562,7563,7564,7565,7566,7567, +7568,7569,7570,7571,7572,7573,7574,7575,7576,7577,7578,7579,7580,7581,7582,7583, +7584,7585,7586,7587,7588,7589,7590,7591,7592,7593,7594,7595,7596,7597,7598,7599, +7600,7601,7602,7603,7604,7605,7606,7607,7608,7609,7610,7611,7612,7613,7614,7615, +7616,7617,7618,7619,7620,7621,7622,7623,7624,7625,7626,7627,7628,7629,7630,7631, +7632,7633,7634,7635,7636,7637,7638,7639,7640,7641,7642,7643,7644,7645,7646,7647, +7648,7649,7650,7651,7652,7653,7654,7655,7656,7657,7658,7659,7660,7661,7662,7663, +7664,7665,7666,7667,7668,7669,7670,7671,7672,7673,7674,7675,7676,7677,7678,7679, +7680,7681,7682,7683,7684,7685,7686,7687,7688,7689,7690,7691,7692,7693,7694,7695, +7696,7697,7698,7699,7700,7701,7702,7703,7704,7705,7706,7707,7708,7709,7710,7711, +7712,7713,7714,7715,7716,7717,7718,7719,7720,7721,7722,7723,7724,7725,7726,7727, +7728,7729,7730,7731,7732,7733,7734,7735,7736,7737,7738,7739,7740,7741,7742,7743, +7744,7745,7746,7747,7748,7749,7750,7751,7752,7753,7754,7755,7756,7757,7758,7759, +7760,7761,7762,7763,7764,7765,7766,7767,7768,7769,7770,7771,7772,7773,7774,7775, +7776,7777,7778,7779,7780,7781,7782,7783,7784,7785,7786,7787,7788,7789,7790,7791, +7792,7793,7794,7795,7796,7797,7798,7799,7800,7801,7802,7803,7804,7805,7806,7807, +7808,7809,7810,7811,7812,7813,7814,7815,7816,7817,7818,7819,7820,7821,7822,7823, +7824,7825,7826,7827,7828,7829,7830,7831,7832,7833,7834,7835,7836,7837,7838,7839, +7840,7841,7842,7843,7844,7845,7846,7847,7848,7849,7850,7851,7852,7853,7854,7855, +7856,7857,7858,7859,7860,7861,7862,7863,7864,7865,7866,7867,7868,7869,7870,7871, +7872,7873,7874,7875,7876,7877,7878,7879,7880,7881,7882,7883,7884,7885,7886,7887, +7888,7889,7890,7891,7892,7893,7894,7895,7896,7897,7898,7899,7900,7901,7902,7903, +7904,7905,7906,7907,7908,7909,7910,7911,7912,7913,7914,7915,7916,7917,7918,7919, +7920,7921,7922,7923,7924,7925,7926,7927,7928,7929,7930,7931,7932,7933,7934,7935, +7936,7937,7938,7939,7940,7941,7942,7943,7944,7945,7946,7947,7948,7949,7950,7951, +7952,7953,7954,7955,7956,7957,7958,7959,7960,7961,7962,7963,7964,7965,7966,7967, +7968,7969,7970,7971,7972,7973,7974,7975,7976,7977,7978,7979,7980,7981,7982,7983, +7984,7985,7986,7987,7988,7989,7990,7991,7992,7993,7994,7995,7996,7997,7998,7999, +8000,8001,8002,8003,8004,8005,8006,8007,8008,8009,8010,8011,8012,8013,8014,8015, +8016,8017,8018,8019,8020,8021,8022,8023,8024,8025,8026,8027,8028,8029,8030,8031, +8032,8033,8034,8035,8036,8037,8038,8039,8040,8041,8042,8043,8044,8045,8046,8047, +8048,8049,8050,8051,8052,8053,8054,8055,8056,8057,8058,8059,8060,8061,8062,8063, +8064,8065,8066,8067,8068,8069,8070,8071,8072,8073,8074,8075,8076,8077,8078,8079, +8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095, +8096,8097,8098,8099,8100,8101,8102,8103,8104,8105,8106,8107,8108,8109,8110,8111, +8112,8113,8114,8115,8116,8117,8118,8119,8120,8121,8122,8123,8124,8125,8126,8127, +8128,8129,8130,8131,8132,8133,8134,8135,8136,8137,8138,8139,8140,8141,8142,8143, +8144,8145,8146,8147,8148,8149,8150,8151,8152,8153,8154,8155,8156,8157,8158,8159, +8160,8161,8162,8163,8164,8165,8166,8167,8168,8169,8170,8171,8172,8173,8174,8175, +8176,8177,8178,8179,8180,8181,8182,8183,8184,8185,8186,8187,8188,8189,8190,8191, +8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8203,8204,8205,8206,8207, +8208,8209,8210,8211,8212,8213,8214,8215,8216,8217,8218,8219,8220,8221,8222,8223, +8224,8225,8226,8227,8228,8229,8230,8231,8232,8233,8234,8235,8236,8237,8238,8239, +8240,8241,8242,8243,8244,8245,8246,8247,8248,8249,8250,8251,8252,8253,8254,8255, +8256,8257,8258,8259,8260,8261,8262,8263,8264,8265,8266,8267,8268,8269,8270,8271, +8272,8273,8274,8275,8276,8277,8278,8279,8280,8281,8282,8283,8284,8285,8286,8287, +8288,8289,8290,8291,8292,8293,8294,8295,8296,8297,8298,8299,8300,8301,8302,8303, +8304,8305,8306,8307,8308,8309,8310,8311,8312,8313,8314,8315,8316,8317,8318,8319, +8320,8321,8322,8323,8324,8325,8326,8327,8328,8329,8330,8331,8332,8333,8334,8335, +8336,8337,8338,8339,8340,8341,8342,8343,8344,8345,8346,8347,8348,8349,8350,8351, +8352,8353,8354,8355,8356,8357,8358,8359,8360,8361,8362,8363,8364,8365,8366,8367, +8368,8369,8370,8371,8372,8373,8374,8375,8376,8377,8378,8379,8380,8381,8382,8383, +8384,8385,8386,8387,8388,8389,8390,8391,8392,8393,8394,8395,8396,8397,8398,8399, +8400,8401,8402,8403,8404,8405,8406,8407,8408,8409,8410,8411,8412,8413,8414,8415, +8416,8417,8418,8419,8420,8421,8422,8423,8424,8425,8426,8427,8428,8429,8430,8431, +8432,8433,8434,8435,8436,8437,8438,8439,8440,8441,8442,8443,8444,8445,8446,8447, +8448,8449,8450,8451,8452,8453,8454,8455,8456,8457,8458,8459,8460,8461,8462,8463, +8464,8465,8466,8467,8468,8469,8470,8471,8472,8473,8474,8475,8476,8477,8478,8479, +8480,8481,8482,8483,8484,8485,8486,8487,8488,8489,8490,8491,8492,8493,8494,8495, +8496,8497,8498,8499,8500,8501,8502,8503,8504,8505,8506,8507,8508,8509,8510,8511, +8512,8513,8514,8515,8516,8517,8518,8519,8520,8521,8522,8523,8524,8525,8526,8527, +8528,8529,8530,8531,8532,8533,8534,8535,8536,8537,8538,8539,8540,8541,8542,8543, +8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554,8555,8556,8557,8558,8559, +8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,8570,8571,8572,8573,8574,8575, +8576,8577,8578,8579,8580,8581,8582,8583,8584,8585,8586,8587,8588,8589,8590,8591, +8592,8593,8594,8595,8596,8597,8598,8599,8600,8601,8602,8603,8604,8605,8606,8607, +8608,8609,8610,8611,8612,8613,8614,8615,8616,8617,8618,8619,8620,8621,8622,8623, +8624,8625,8626,8627,8628,8629,8630,8631,8632,8633,8634,8635,8636,8637,8638,8639, +8640,8641,8642,8643,8644,8645,8646,8647,8648,8649,8650,8651,8652,8653,8654,8655, +8656,8657,8658,8659,8660,8661,8662,8663,8664,8665,8666,8667,8668,8669,8670,8671, +8672,8673,8674,8675,8676,8677,8678,8679,8680,8681,8682,8683,8684,8685,8686,8687, +8688,8689,8690,8691,8692,8693,8694,8695,8696,8697,8698,8699,8700,8701,8702,8703, +8704,8705,8706,8707,8708,8709,8710,8711,8712,8713,8714,8715,8716,8717,8718,8719, +8720,8721,8722,8723,8724,8725,8726,8727,8728,8729,8730,8731,8732,8733,8734,8735, +8736,8737,8738,8739,8740,8741) + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euckrprober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euckrprober.py new file mode 100644 index 0000000..5982a46 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euckrprober.py @@ -0,0 +1,42 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import EUCKRDistributionAnalysis +from .mbcssm import EUCKRSMModel + + +class EUCKRProber(MultiByteCharSetProber): + def __init__(self): + MultiByteCharSetProber.__init__(self) + self._mCodingSM = CodingStateMachine(EUCKRSMModel) + self._mDistributionAnalyzer = EUCKRDistributionAnalysis() + self.reset() + + def get_charset_name(self): + return "EUC-KR" diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euctwfreq.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euctwfreq.py new file mode 100644 index 0000000..576e750 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euctwfreq.py @@ -0,0 +1,428 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# EUCTW frequency table +# Converted from big5 work +# by Taiwan's Mandarin Promotion Council +# <http:#www.edu.tw:81/mandr/> + +# 128 --> 0.42261 +# 256 --> 0.57851 +# 512 --> 0.74851 +# 1024 --> 0.89384 +# 2048 --> 0.97583 +# +# Idea Distribution Ratio = 0.74851/(1-0.74851) =2.98 +# Random Distribution Ration = 512/(5401-512)=0.105 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR + +EUCTW_TYPICAL_DISTRIBUTION_RATIO = 0.75 + +# Char to FreqOrder table , +EUCTW_TABLE_SIZE = 8102 + +EUCTWCharToFreqOrder = ( + 1,1800,1506, 255,1431, 198, 9, 82, 6,7310, 177, 202,3615,1256,2808, 110, # 2742 +3735, 33,3241, 261, 76, 44,2113, 16,2931,2184,1176, 659,3868, 26,3404,2643, # 2758 +1198,3869,3313,4060, 410,2211, 302, 590, 361,1963, 8, 204, 58,4296,7311,1931, # 2774 + 63,7312,7313, 317,1614, 75, 222, 159,4061,2412,1480,7314,3500,3068, 224,2809, # 2790 +3616, 3, 10,3870,1471, 29,2774,1135,2852,1939, 873, 130,3242,1123, 312,7315, # 2806 +4297,2051, 507, 252, 682,7316, 142,1914, 124, 206,2932, 34,3501,3173, 64, 604, # 2822 +7317,2494,1976,1977, 155,1990, 645, 641,1606,7318,3405, 337, 72, 406,7319, 80, # 2838 + 630, 238,3174,1509, 263, 939,1092,2644, 756,1440,1094,3406, 449, 69,2969, 591, # 2854 + 179,2095, 471, 115,2034,1843, 60, 50,2970, 134, 806,1868, 734,2035,3407, 180, # 2870 + 995,1607, 156, 537,2893, 688,7320, 319,1305, 779,2144, 514,2374, 298,4298, 359, # 2886 +2495, 90,2707,1338, 663, 11, 906,1099,2545, 20,2436, 182, 532,1716,7321, 732, # 2902 +1376,4062,1311,1420,3175, 25,2312,1056, 113, 399, 382,1949, 242,3408,2467, 529, # 2918 +3243, 475,1447,3617,7322, 117, 21, 656, 810,1297,2295,2329,3502,7323, 126,4063, # 2934 + 706, 456, 150, 613,4299, 71,1118,2036,4064, 145,3069, 85, 835, 486,2114,1246, # 2950 +1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,7324,2127,2354, 347,3736, 221, # 2966 +3503,3110,7325,1955,1153,4065, 83, 296,1199,3070, 192, 624, 93,7326, 822,1897, # 2982 +2810,3111, 795,2064, 991,1554,1542,1592, 27, 43,2853, 859, 139,1456, 860,4300, # 2998 + 437, 712,3871, 164,2392,3112, 695, 211,3017,2096, 195,3872,1608,3504,3505,3618, # 3014 +3873, 234, 811,2971,2097,3874,2229,1441,3506,1615,2375, 668,2076,1638, 305, 228, # 3030 +1664,4301, 467, 415,7327, 262,2098,1593, 239, 108, 300, 200,1033, 512,1247,2077, # 3046 +7328,7329,2173,3176,3619,2673, 593, 845,1062,3244, 88,1723,2037,3875,1950, 212, # 3062 + 266, 152, 149, 468,1898,4066,4302, 77, 187,7330,3018, 37, 5,2972,7331,3876, # 3078 +7332,7333, 39,2517,4303,2894,3177,2078, 55, 148, 74,4304, 545, 483,1474,1029, # 3094 +1665, 217,1869,1531,3113,1104,2645,4067, 24, 172,3507, 900,3877,3508,3509,4305, # 3110 + 32,1408,2811,1312, 329, 487,2355,2247,2708, 784,2674, 4,3019,3314,1427,1788, # 3126 + 188, 109, 499,7334,3620,1717,1789, 888,1217,3020,4306,7335,3510,7336,3315,1520, # 3142 +3621,3878, 196,1034, 775,7337,7338, 929,1815, 249, 439, 38,7339,1063,7340, 794, # 3158 +3879,1435,2296, 46, 178,3245,2065,7341,2376,7342, 214,1709,4307, 804, 35, 707, # 3174 + 324,3622,1601,2546, 140, 459,4068,7343,7344,1365, 839, 272, 978,2257,2572,3409, # 3190 +2128,1363,3623,1423, 697, 100,3071, 48, 70,1231, 495,3114,2193,7345,1294,7346, # 3206 +2079, 462, 586,1042,3246, 853, 256, 988, 185,2377,3410,1698, 434,1084,7347,3411, # 3222 + 314,2615,2775,4308,2330,2331, 569,2280, 637,1816,2518, 757,1162,1878,1616,3412, # 3238 + 287,1577,2115, 768,4309,1671,2854,3511,2519,1321,3737, 909,2413,7348,4069, 933, # 3254 +3738,7349,2052,2356,1222,4310, 765,2414,1322, 786,4311,7350,1919,1462,1677,2895, # 3270 +1699,7351,4312,1424,2437,3115,3624,2590,3316,1774,1940,3413,3880,4070, 309,1369, # 3286 +1130,2812, 364,2230,1653,1299,3881,3512,3882,3883,2646, 525,1085,3021, 902,2000, # 3302 +1475, 964,4313, 421,1844,1415,1057,2281, 940,1364,3116, 376,4314,4315,1381, 7, # 3318 +2520, 983,2378, 336,1710,2675,1845, 321,3414, 559,1131,3022,2742,1808,1132,1313, # 3334 + 265,1481,1857,7352, 352,1203,2813,3247, 167,1089, 420,2814, 776, 792,1724,3513, # 3350 +4071,2438,3248,7353,4072,7354, 446, 229, 333,2743, 901,3739,1200,1557,4316,2647, # 3366 +1920, 395,2744,2676,3740,4073,1835, 125, 916,3178,2616,4317,7355,7356,3741,7357, # 3382 +7358,7359,4318,3117,3625,1133,2547,1757,3415,1510,2313,1409,3514,7360,2145, 438, # 3398 +2591,2896,2379,3317,1068, 958,3023, 461, 311,2855,2677,4074,1915,3179,4075,1978, # 3414 + 383, 750,2745,2617,4076, 274, 539, 385,1278,1442,7361,1154,1964, 384, 561, 210, # 3430 + 98,1295,2548,3515,7362,1711,2415,1482,3416,3884,2897,1257, 129,7363,3742, 642, # 3446 + 523,2776,2777,2648,7364, 141,2231,1333, 68, 176, 441, 876, 907,4077, 603,2592, # 3462 + 710, 171,3417, 404, 549, 18,3118,2393,1410,3626,1666,7365,3516,4319,2898,4320, # 3478 +7366,2973, 368,7367, 146, 366, 99, 871,3627,1543, 748, 807,1586,1185, 22,2258, # 3494 + 379,3743,3180,7368,3181, 505,1941,2618,1991,1382,2314,7369, 380,2357, 218, 702, # 3510 +1817,1248,3418,3024,3517,3318,3249,7370,2974,3628, 930,3250,3744,7371, 59,7372, # 3526 + 585, 601,4078, 497,3419,1112,1314,4321,1801,7373,1223,1472,2174,7374, 749,1836, # 3542 + 690,1899,3745,1772,3885,1476, 429,1043,1790,2232,2116, 917,4079, 447,1086,1629, # 3558 +7375, 556,7376,7377,2020,1654, 844,1090, 105, 550, 966,1758,2815,1008,1782, 686, # 3574 +1095,7378,2282, 793,1602,7379,3518,2593,4322,4080,2933,2297,4323,3746, 980,2496, # 3590 + 544, 353, 527,4324, 908,2678,2899,7380, 381,2619,1942,1348,7381,1341,1252, 560, # 3606 +3072,7382,3420,2856,7383,2053, 973, 886,2080, 143,4325,7384,7385, 157,3886, 496, # 3622 +4081, 57, 840, 540,2038,4326,4327,3421,2117,1445, 970,2259,1748,1965,2081,4082, # 3638 +3119,1234,1775,3251,2816,3629, 773,1206,2129,1066,2039,1326,3887,1738,1725,4083, # 3654 + 279,3120, 51,1544,2594, 423,1578,2130,2066, 173,4328,1879,7386,7387,1583, 264, # 3670 + 610,3630,4329,2439, 280, 154,7388,7389,7390,1739, 338,1282,3073, 693,2857,1411, # 3686 +1074,3747,2440,7391,4330,7392,7393,1240, 952,2394,7394,2900,1538,2679, 685,1483, # 3702 +4084,2468,1436, 953,4085,2054,4331, 671,2395, 79,4086,2441,3252, 608, 567,2680, # 3718 +3422,4087,4088,1691, 393,1261,1791,2396,7395,4332,7396,7397,7398,7399,1383,1672, # 3734 +3748,3182,1464, 522,1119, 661,1150, 216, 675,4333,3888,1432,3519, 609,4334,2681, # 3750 +2397,7400,7401,7402,4089,3025, 0,7403,2469, 315, 231,2442, 301,3319,4335,2380, # 3766 +7404, 233,4090,3631,1818,4336,4337,7405, 96,1776,1315,2082,7406, 257,7407,1809, # 3782 +3632,2709,1139,1819,4091,2021,1124,2163,2778,1777,2649,7408,3074, 363,1655,3183, # 3798 +7409,2975,7410,7411,7412,3889,1567,3890, 718, 103,3184, 849,1443, 341,3320,2934, # 3814 +1484,7413,1712, 127, 67, 339,4092,2398, 679,1412, 821,7414,7415, 834, 738, 351, # 3830 +2976,2146, 846, 235,1497,1880, 418,1992,3749,2710, 186,1100,2147,2746,3520,1545, # 3846 +1355,2935,2858,1377, 583,3891,4093,2573,2977,7416,1298,3633,1078,2549,3634,2358, # 3862 + 78,3750,3751, 267,1289,2099,2001,1594,4094, 348, 369,1274,2194,2175,1837,4338, # 3878 +1820,2817,3635,2747,2283,2002,4339,2936,2748, 144,3321, 882,4340,3892,2749,3423, # 3894 +4341,2901,7417,4095,1726, 320,7418,3893,3026, 788,2978,7419,2818,1773,1327,2859, # 3910 +3894,2819,7420,1306,4342,2003,1700,3752,3521,2359,2650, 787,2022, 506, 824,3636, # 3926 + 534, 323,4343,1044,3322,2023,1900, 946,3424,7421,1778,1500,1678,7422,1881,4344, # 3942 + 165, 243,4345,3637,2521, 123, 683,4096, 764,4346, 36,3895,1792, 589,2902, 816, # 3958 + 626,1667,3027,2233,1639,1555,1622,3753,3896,7423,3897,2860,1370,1228,1932, 891, # 3974 +2083,2903, 304,4097,7424, 292,2979,2711,3522, 691,2100,4098,1115,4347, 118, 662, # 3990 +7425, 611,1156, 854,2381,1316,2861, 2, 386, 515,2904,7426,7427,3253, 868,2234, # 4006 +1486, 855,2651, 785,2212,3028,7428,1040,3185,3523,7429,3121, 448,7430,1525,7431, # 4022 +2164,4348,7432,3754,7433,4099,2820,3524,3122, 503, 818,3898,3123,1568, 814, 676, # 4038 +1444, 306,1749,7434,3755,1416,1030, 197,1428, 805,2821,1501,4349,7435,7436,7437, # 4054 +1993,7438,4350,7439,7440,2195, 13,2779,3638,2980,3124,1229,1916,7441,3756,2131, # 4070 +7442,4100,4351,2399,3525,7443,2213,1511,1727,1120,7444,7445, 646,3757,2443, 307, # 4086 +7446,7447,1595,3186,7448,7449,7450,3639,1113,1356,3899,1465,2522,2523,7451, 519, # 4102 +7452, 128,2132, 92,2284,1979,7453,3900,1512, 342,3125,2196,7454,2780,2214,1980, # 4118 +3323,7455, 290,1656,1317, 789, 827,2360,7456,3758,4352, 562, 581,3901,7457, 401, # 4134 +4353,2248, 94,4354,1399,2781,7458,1463,2024,4355,3187,1943,7459, 828,1105,4101, # 4150 +1262,1394,7460,4102, 605,4356,7461,1783,2862,7462,2822, 819,2101, 578,2197,2937, # 4166 +7463,1502, 436,3254,4103,3255,2823,3902,2905,3425,3426,7464,2712,2315,7465,7466, # 4182 +2332,2067, 23,4357, 193, 826,3759,2102, 699,1630,4104,3075, 390,1793,1064,3526, # 4198 +7467,1579,3076,3077,1400,7468,4105,1838,1640,2863,7469,4358,4359, 137,4106, 598, # 4214 +3078,1966, 780, 104, 974,2938,7470, 278, 899, 253, 402, 572, 504, 493,1339,7471, # 4230 +3903,1275,4360,2574,2550,7472,3640,3029,3079,2249, 565,1334,2713, 863, 41,7473, # 4246 +7474,4361,7475,1657,2333, 19, 463,2750,4107, 606,7476,2981,3256,1087,2084,1323, # 4262 +2652,2982,7477,1631,1623,1750,4108,2682,7478,2864, 791,2714,2653,2334, 232,2416, # 4278 +7479,2983,1498,7480,2654,2620, 755,1366,3641,3257,3126,2025,1609, 119,1917,3427, # 4294 + 862,1026,4109,7481,3904,3760,4362,3905,4363,2260,1951,2470,7482,1125, 817,4110, # 4310 +4111,3906,1513,1766,2040,1487,4112,3030,3258,2824,3761,3127,7483,7484,1507,7485, # 4326 +2683, 733, 40,1632,1106,2865, 345,4113, 841,2524, 230,4364,2984,1846,3259,3428, # 4342 +7486,1263, 986,3429,7487, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562,3907, # 4358 +3908,2939, 967,2751,2655,1349, 592,2133,1692,3324,2985,1994,4114,1679,3909,1901, # 4374 +2185,7488, 739,3642,2715,1296,1290,7489,4115,2198,2199,1921,1563,2595,2551,1870, # 4390 +2752,2986,7490, 435,7491, 343,1108, 596, 17,1751,4365,2235,3430,3643,7492,4366, # 4406 + 294,3527,2940,1693, 477, 979, 281,2041,3528, 643,2042,3644,2621,2782,2261,1031, # 4422 +2335,2134,2298,3529,4367, 367,1249,2552,7493,3530,7494,4368,1283,3325,2004, 240, # 4438 +1762,3326,4369,4370, 836,1069,3128, 474,7495,2148,2525, 268,3531,7496,3188,1521, # 4454 +1284,7497,1658,1546,4116,7498,3532,3533,7499,4117,3327,2684,1685,4118, 961,1673, # 4470 +2622, 190,2005,2200,3762,4371,4372,7500, 570,2497,3645,1490,7501,4373,2623,3260, # 4486 +1956,4374, 584,1514, 396,1045,1944,7502,4375,1967,2444,7503,7504,4376,3910, 619, # 4502 +7505,3129,3261, 215,2006,2783,2553,3189,4377,3190,4378, 763,4119,3763,4379,7506, # 4518 +7507,1957,1767,2941,3328,3646,1174, 452,1477,4380,3329,3130,7508,2825,1253,2382, # 4534 +2186,1091,2285,4120, 492,7509, 638,1169,1824,2135,1752,3911, 648, 926,1021,1324, # 4550 +4381, 520,4382, 997, 847,1007, 892,4383,3764,2262,1871,3647,7510,2400,1784,4384, # 4566 +1952,2942,3080,3191,1728,4121,2043,3648,4385,2007,1701,3131,1551, 30,2263,4122, # 4582 +7511,2026,4386,3534,7512, 501,7513,4123, 594,3431,2165,1821,3535,3432,3536,3192, # 4598 + 829,2826,4124,7514,1680,3132,1225,4125,7515,3262,4387,4126,3133,2336,7516,4388, # 4614 +4127,7517,3912,3913,7518,1847,2383,2596,3330,7519,4389, 374,3914, 652,4128,4129, # 4630 + 375,1140, 798,7520,7521,7522,2361,4390,2264, 546,1659, 138,3031,2445,4391,7523, # 4646 +2250, 612,1848, 910, 796,3765,1740,1371, 825,3766,3767,7524,2906,2554,7525, 692, # 4662 + 444,3032,2624, 801,4392,4130,7526,1491, 244,1053,3033,4131,4132, 340,7527,3915, # 4678 +1041,2987, 293,1168, 87,1357,7528,1539, 959,7529,2236, 721, 694,4133,3768, 219, # 4694 +1478, 644,1417,3331,2656,1413,1401,1335,1389,3916,7530,7531,2988,2362,3134,1825, # 4710 + 730,1515, 184,2827, 66,4393,7532,1660,2943, 246,3332, 378,1457, 226,3433, 975, # 4726 +3917,2944,1264,3537, 674, 696,7533, 163,7534,1141,2417,2166, 713,3538,3333,4394, # 4742 +3918,7535,7536,1186, 15,7537,1079,1070,7538,1522,3193,3539, 276,1050,2716, 758, # 4758 +1126, 653,2945,3263,7539,2337, 889,3540,3919,3081,2989, 903,1250,4395,3920,3434, # 4774 +3541,1342,1681,1718, 766,3264, 286, 89,2946,3649,7540,1713,7541,2597,3334,2990, # 4790 +7542,2947,2215,3194,2866,7543,4396,2498,2526, 181, 387,1075,3921, 731,2187,3335, # 4806 +7544,3265, 310, 313,3435,2299, 770,4134, 54,3034, 189,4397,3082,3769,3922,7545, # 4822 +1230,1617,1849, 355,3542,4135,4398,3336, 111,4136,3650,1350,3135,3436,3035,4137, # 4838 +2149,3266,3543,7546,2784,3923,3924,2991, 722,2008,7547,1071, 247,1207,2338,2471, # 4854 +1378,4399,2009, 864,1437,1214,4400, 373,3770,1142,2216, 667,4401, 442,2753,2555, # 4870 +3771,3925,1968,4138,3267,1839, 837, 170,1107, 934,1336,1882,7548,7549,2118,4139, # 4886 +2828, 743,1569,7550,4402,4140, 582,2384,1418,3437,7551,1802,7552, 357,1395,1729, # 4902 +3651,3268,2418,1564,2237,7553,3083,3772,1633,4403,1114,2085,4141,1532,7554, 482, # 4918 +2446,4404,7555,7556,1492, 833,1466,7557,2717,3544,1641,2829,7558,1526,1272,3652, # 4934 +4142,1686,1794, 416,2556,1902,1953,1803,7559,3773,2785,3774,1159,2316,7560,2867, # 4950 +4405,1610,1584,3036,2419,2754, 443,3269,1163,3136,7561,7562,3926,7563,4143,2499, # 4966 +3037,4406,3927,3137,2103,1647,3545,2010,1872,4144,7564,4145, 431,3438,7565, 250, # 4982 + 97, 81,4146,7566,1648,1850,1558, 160, 848,7567, 866, 740,1694,7568,2201,2830, # 4998 +3195,4147,4407,3653,1687, 950,2472, 426, 469,3196,3654,3655,3928,7569,7570,1188, # 5014 + 424,1995, 861,3546,4148,3775,2202,2685, 168,1235,3547,4149,7571,2086,1674,4408, # 5030 +3337,3270, 220,2557,1009,7572,3776, 670,2992, 332,1208, 717,7573,7574,3548,2447, # 5046 +3929,3338,7575, 513,7576,1209,2868,3339,3138,4409,1080,7577,7578,7579,7580,2527, # 5062 +3656,3549, 815,1587,3930,3931,7581,3550,3439,3777,1254,4410,1328,3038,1390,3932, # 5078 +1741,3933,3778,3934,7582, 236,3779,2448,3271,7583,7584,3657,3780,1273,3781,4411, # 5094 +7585, 308,7586,4412, 245,4413,1851,2473,1307,2575, 430, 715,2136,2449,7587, 270, # 5110 + 199,2869,3935,7588,3551,2718,1753, 761,1754, 725,1661,1840,4414,3440,3658,7589, # 5126 +7590, 587, 14,3272, 227,2598, 326, 480,2265, 943,2755,3552, 291, 650,1883,7591, # 5142 +1702,1226, 102,1547, 62,3441, 904,4415,3442,1164,4150,7592,7593,1224,1548,2756, # 5158 + 391, 498,1493,7594,1386,1419,7595,2055,1177,4416, 813, 880,1081,2363, 566,1145, # 5174 +4417,2286,1001,1035,2558,2599,2238, 394,1286,7596,7597,2068,7598, 86,1494,1730, # 5190 +3936, 491,1588, 745, 897,2948, 843,3340,3937,2757,2870,3273,1768, 998,2217,2069, # 5206 + 397,1826,1195,1969,3659,2993,3341, 284,7599,3782,2500,2137,2119,1903,7600,3938, # 5222 +2150,3939,4151,1036,3443,1904, 114,2559,4152, 209,1527,7601,7602,2949,2831,2625, # 5238 +2385,2719,3139, 812,2560,7603,3274,7604,1559, 737,1884,3660,1210, 885, 28,2686, # 5254 +3553,3783,7605,4153,1004,1779,4418,7606, 346,1981,2218,2687,4419,3784,1742, 797, # 5270 +1642,3940,1933,1072,1384,2151, 896,3941,3275,3661,3197,2871,3554,7607,2561,1958, # 5286 +4420,2450,1785,7608,7609,7610,3942,4154,1005,1308,3662,4155,2720,4421,4422,1528, # 5302 +2600, 161,1178,4156,1982, 987,4423,1101,4157, 631,3943,1157,3198,2420,1343,1241, # 5318 +1016,2239,2562, 372, 877,2339,2501,1160, 555,1934, 911,3944,7611, 466,1170, 169, # 5334 +1051,2907,2688,3663,2474,2994,1182,2011,2563,1251,2626,7612, 992,2340,3444,1540, # 5350 +2721,1201,2070,2401,1996,2475,7613,4424, 528,1922,2188,1503,1873,1570,2364,3342, # 5366 +3276,7614, 557,1073,7615,1827,3445,2087,2266,3140,3039,3084, 767,3085,2786,4425, # 5382 +1006,4158,4426,2341,1267,2176,3664,3199, 778,3945,3200,2722,1597,2657,7616,4427, # 5398 +7617,3446,7618,7619,7620,3277,2689,1433,3278, 131, 95,1504,3946, 723,4159,3141, # 5414 +1841,3555,2758,2189,3947,2027,2104,3665,7621,2995,3948,1218,7622,3343,3201,3949, # 5430 +4160,2576, 248,1634,3785, 912,7623,2832,3666,3040,3786, 654, 53,7624,2996,7625, # 5446 +1688,4428, 777,3447,1032,3950,1425,7626, 191, 820,2120,2833, 971,4429, 931,3202, # 5462 + 135, 664, 783,3787,1997, 772,2908,1935,3951,3788,4430,2909,3203, 282,2723, 640, # 5478 +1372,3448,1127, 922, 325,3344,7627,7628, 711,2044,7629,7630,3952,2219,2787,1936, # 5494 +3953,3345,2220,2251,3789,2300,7631,4431,3790,1258,3279,3954,3204,2138,2950,3955, # 5510 +3956,7632,2221, 258,3205,4432, 101,1227,7633,3280,1755,7634,1391,3281,7635,2910, # 5526 +2056, 893,7636,7637,7638,1402,4161,2342,7639,7640,3206,3556,7641,7642, 878,1325, # 5542 +1780,2788,4433, 259,1385,2577, 744,1183,2267,4434,7643,3957,2502,7644, 684,1024, # 5558 +4162,7645, 472,3557,3449,1165,3282,3958,3959, 322,2152, 881, 455,1695,1152,1340, # 5574 + 660, 554,2153,4435,1058,4436,4163, 830,1065,3346,3960,4437,1923,7646,1703,1918, # 5590 +7647, 932,2268, 122,7648,4438, 947, 677,7649,3791,2627, 297,1905,1924,2269,4439, # 5606 +2317,3283,7650,7651,4164,7652,4165, 84,4166, 112, 989,7653, 547,1059,3961, 701, # 5622 +3558,1019,7654,4167,7655,3450, 942, 639, 457,2301,2451, 993,2951, 407, 851, 494, # 5638 +4440,3347, 927,7656,1237,7657,2421,3348, 573,4168, 680, 921,2911,1279,1874, 285, # 5654 + 790,1448,1983, 719,2167,7658,7659,4441,3962,3963,1649,7660,1541, 563,7661,1077, # 5670 +7662,3349,3041,3451, 511,2997,3964,3965,3667,3966,1268,2564,3350,3207,4442,4443, # 5686 +7663, 535,1048,1276,1189,2912,2028,3142,1438,1373,2834,2952,1134,2012,7664,4169, # 5702 +1238,2578,3086,1259,7665, 700,7666,2953,3143,3668,4170,7667,4171,1146,1875,1906, # 5718 +4444,2601,3967, 781,2422, 132,1589, 203, 147, 273,2789,2402, 898,1786,2154,3968, # 5734 +3969,7668,3792,2790,7669,7670,4445,4446,7671,3208,7672,1635,3793, 965,7673,1804, # 5750 +2690,1516,3559,1121,1082,1329,3284,3970,1449,3794, 65,1128,2835,2913,2759,1590, # 5766 +3795,7674,7675, 12,2658, 45, 976,2579,3144,4447, 517,2528,1013,1037,3209,7676, # 5782 +3796,2836,7677,3797,7678,3452,7679,2602, 614,1998,2318,3798,3087,2724,2628,7680, # 5798 +2580,4172, 599,1269,7681,1810,3669,7682,2691,3088, 759,1060, 489,1805,3351,3285, # 5814 +1358,7683,7684,2386,1387,1215,2629,2252, 490,7685,7686,4173,1759,2387,2343,7687, # 5830 +4448,3799,1907,3971,2630,1806,3210,4449,3453,3286,2760,2344, 874,7688,7689,3454, # 5846 +3670,1858, 91,2914,3671,3042,3800,4450,7690,3145,3972,2659,7691,3455,1202,1403, # 5862 +3801,2954,2529,1517,2503,4451,3456,2504,7692,4452,7693,2692,1885,1495,1731,3973, # 5878 +2365,4453,7694,2029,7695,7696,3974,2693,1216, 237,2581,4174,2319,3975,3802,4454, # 5894 +4455,2694,3560,3457, 445,4456,7697,7698,7699,7700,2761, 61,3976,3672,1822,3977, # 5910 +7701, 687,2045, 935, 925, 405,2660, 703,1096,1859,2725,4457,3978,1876,1367,2695, # 5926 +3352, 918,2105,1781,2476, 334,3287,1611,1093,4458, 564,3146,3458,3673,3353, 945, # 5942 +2631,2057,4459,7702,1925, 872,4175,7703,3459,2696,3089, 349,4176,3674,3979,4460, # 5958 +3803,4177,3675,2155,3980,4461,4462,4178,4463,2403,2046, 782,3981, 400, 251,4179, # 5974 +1624,7704,7705, 277,3676, 299,1265, 476,1191,3804,2121,4180,4181,1109, 205,7706, # 5990 +2582,1000,2156,3561,1860,7707,7708,7709,4464,7710,4465,2565, 107,2477,2157,3982, # 6006 +3460,3147,7711,1533, 541,1301, 158, 753,4182,2872,3562,7712,1696, 370,1088,4183, # 6022 +4466,3563, 579, 327, 440, 162,2240, 269,1937,1374,3461, 968,3043, 56,1396,3090, # 6038 +2106,3288,3354,7713,1926,2158,4467,2998,7714,3564,7715,7716,3677,4468,2478,7717, # 6054 +2791,7718,1650,4469,7719,2603,7720,7721,3983,2661,3355,1149,3356,3984,3805,3985, # 6070 +7722,1076, 49,7723, 951,3211,3289,3290, 450,2837, 920,7724,1811,2792,2366,4184, # 6086 +1908,1138,2367,3806,3462,7725,3212,4470,1909,1147,1518,2423,4471,3807,7726,4472, # 6102 +2388,2604, 260,1795,3213,7727,7728,3808,3291, 708,7729,3565,1704,7730,3566,1351, # 6118 +1618,3357,2999,1886, 944,4185,3358,4186,3044,3359,4187,7731,3678, 422, 413,1714, # 6134 +3292, 500,2058,2345,4188,2479,7732,1344,1910, 954,7733,1668,7734,7735,3986,2404, # 6150 +4189,3567,3809,4190,7736,2302,1318,2505,3091, 133,3092,2873,4473, 629, 31,2838, # 6166 +2697,3810,4474, 850, 949,4475,3987,2955,1732,2088,4191,1496,1852,7737,3988, 620, # 6182 +3214, 981,1242,3679,3360,1619,3680,1643,3293,2139,2452,1970,1719,3463,2168,7738, # 6198 +3215,7739,7740,3361,1828,7741,1277,4476,1565,2047,7742,1636,3568,3093,7743, 869, # 6214 +2839, 655,3811,3812,3094,3989,3000,3813,1310,3569,4477,7744,7745,7746,1733, 558, # 6230 +4478,3681, 335,1549,3045,1756,4192,3682,1945,3464,1829,1291,1192, 470,2726,2107, # 6246 +2793, 913,1054,3990,7747,1027,7748,3046,3991,4479, 982,2662,3362,3148,3465,3216, # 6262 +3217,1946,2794,7749, 571,4480,7750,1830,7751,3570,2583,1523,2424,7752,2089, 984, # 6278 +4481,3683,1959,7753,3684, 852, 923,2795,3466,3685, 969,1519, 999,2048,2320,1705, # 6294 +7754,3095, 615,1662, 151, 597,3992,2405,2321,1049, 275,4482,3686,4193, 568,3687, # 6310 +3571,2480,4194,3688,7755,2425,2270, 409,3218,7756,1566,2874,3467,1002, 769,2840, # 6326 + 194,2090,3149,3689,2222,3294,4195, 628,1505,7757,7758,1763,2177,3001,3993, 521, # 6342 +1161,2584,1787,2203,2406,4483,3994,1625,4196,4197, 412, 42,3096, 464,7759,2632, # 6358 +4484,3363,1760,1571,2875,3468,2530,1219,2204,3814,2633,2140,2368,4485,4486,3295, # 6374 +1651,3364,3572,7760,7761,3573,2481,3469,7762,3690,7763,7764,2271,2091, 460,7765, # 6390 +4487,7766,3002, 962, 588,3574, 289,3219,2634,1116, 52,7767,3047,1796,7768,7769, # 6406 +7770,1467,7771,1598,1143,3691,4198,1984,1734,1067,4488,1280,3365, 465,4489,1572, # 6422 + 510,7772,1927,2241,1812,1644,3575,7773,4490,3692,7774,7775,2663,1573,1534,7776, # 6438 +7777,4199, 536,1807,1761,3470,3815,3150,2635,7778,7779,7780,4491,3471,2915,1911, # 6454 +2796,7781,3296,1122, 377,3220,7782, 360,7783,7784,4200,1529, 551,7785,2059,3693, # 6470 +1769,2426,7786,2916,4201,3297,3097,2322,2108,2030,4492,1404, 136,1468,1479, 672, # 6486 +1171,3221,2303, 271,3151,7787,2762,7788,2049, 678,2727, 865,1947,4493,7789,2013, # 6502 +3995,2956,7790,2728,2223,1397,3048,3694,4494,4495,1735,2917,3366,3576,7791,3816, # 6518 + 509,2841,2453,2876,3817,7792,7793,3152,3153,4496,4202,2531,4497,2304,1166,1010, # 6534 + 552, 681,1887,7794,7795,2957,2958,3996,1287,1596,1861,3154, 358, 453, 736, 175, # 6550 + 478,1117, 905,1167,1097,7796,1853,1530,7797,1706,7798,2178,3472,2287,3695,3473, # 6566 +3577,4203,2092,4204,7799,3367,1193,2482,4205,1458,2190,2205,1862,1888,1421,3298, # 6582 +2918,3049,2179,3474, 595,2122,7800,3997,7801,7802,4206,1707,2636, 223,3696,1359, # 6598 + 751,3098, 183,3475,7803,2797,3003, 419,2369, 633, 704,3818,2389, 241,7804,7805, # 6614 +7806, 838,3004,3697,2272,2763,2454,3819,1938,2050,3998,1309,3099,2242,1181,7807, # 6630 +1136,2206,3820,2370,1446,4207,2305,4498,7808,7809,4208,1055,2605, 484,3698,7810, # 6646 +3999, 625,4209,2273,3368,1499,4210,4000,7811,4001,4211,3222,2274,2275,3476,7812, # 6662 +7813,2764, 808,2606,3699,3369,4002,4212,3100,2532, 526,3370,3821,4213, 955,7814, # 6678 +1620,4214,2637,2427,7815,1429,3700,1669,1831, 994, 928,7816,3578,1260,7817,7818, # 6694 +7819,1948,2288, 741,2919,1626,4215,2729,2455, 867,1184, 362,3371,1392,7820,7821, # 6710 +4003,4216,1770,1736,3223,2920,4499,4500,1928,2698,1459,1158,7822,3050,3372,2877, # 6726 +1292,1929,2506,2842,3701,1985,1187,2071,2014,2607,4217,7823,2566,2507,2169,3702, # 6742 +2483,3299,7824,3703,4501,7825,7826, 666,1003,3005,1022,3579,4218,7827,4502,1813, # 6758 +2253, 574,3822,1603, 295,1535, 705,3823,4219, 283, 858, 417,7828,7829,3224,4503, # 6774 +4504,3051,1220,1889,1046,2276,2456,4004,1393,1599, 689,2567, 388,4220,7830,2484, # 6790 + 802,7831,2798,3824,2060,1405,2254,7832,4505,3825,2109,1052,1345,3225,1585,7833, # 6806 + 809,7834,7835,7836, 575,2730,3477, 956,1552,1469,1144,2323,7837,2324,1560,2457, # 6822 +3580,3226,4005, 616,2207,3155,2180,2289,7838,1832,7839,3478,4506,7840,1319,3704, # 6838 +3705,1211,3581,1023,3227,1293,2799,7841,7842,7843,3826, 607,2306,3827, 762,2878, # 6854 +1439,4221,1360,7844,1485,3052,7845,4507,1038,4222,1450,2061,2638,4223,1379,4508, # 6870 +2585,7846,7847,4224,1352,1414,2325,2921,1172,7848,7849,3828,3829,7850,1797,1451, # 6886 +7851,7852,7853,7854,2922,4006,4007,2485,2346, 411,4008,4009,3582,3300,3101,4509, # 6902 +1561,2664,1452,4010,1375,7855,7856, 47,2959, 316,7857,1406,1591,2923,3156,7858, # 6918 +1025,2141,3102,3157, 354,2731, 884,2224,4225,2407, 508,3706, 726,3583, 996,2428, # 6934 +3584, 729,7859, 392,2191,1453,4011,4510,3707,7860,7861,2458,3585,2608,1675,2800, # 6950 + 919,2347,2960,2348,1270,4511,4012, 73,7862,7863, 647,7864,3228,2843,2255,1550, # 6966 +1346,3006,7865,1332, 883,3479,7866,7867,7868,7869,3301,2765,7870,1212, 831,1347, # 6982 +4226,4512,2326,3830,1863,3053, 720,3831,4513,4514,3832,7871,4227,7872,7873,4515, # 6998 +7874,7875,1798,4516,3708,2609,4517,3586,1645,2371,7876,7877,2924, 669,2208,2665, # 7014 +2429,7878,2879,7879,7880,1028,3229,7881,4228,2408,7882,2256,1353,7883,7884,4518, # 7030 +3158, 518,7885,4013,7886,4229,1960,7887,2142,4230,7888,7889,3007,2349,2350,3833, # 7046 + 516,1833,1454,4014,2699,4231,4519,2225,2610,1971,1129,3587,7890,2766,7891,2961, # 7062 +1422, 577,1470,3008,1524,3373,7892,7893, 432,4232,3054,3480,7894,2586,1455,2508, # 7078 +2226,1972,1175,7895,1020,2732,4015,3481,4520,7896,2733,7897,1743,1361,3055,3482, # 7094 +2639,4016,4233,4521,2290, 895, 924,4234,2170, 331,2243,3056, 166,1627,3057,1098, # 7110 +7898,1232,2880,2227,3374,4522, 657, 403,1196,2372, 542,3709,3375,1600,4235,3483, # 7126 +7899,4523,2767,3230, 576, 530,1362,7900,4524,2533,2666,3710,4017,7901, 842,3834, # 7142 +7902,2801,2031,1014,4018, 213,2700,3376, 665, 621,4236,7903,3711,2925,2430,7904, # 7158 +2431,3302,3588,3377,7905,4237,2534,4238,4525,3589,1682,4239,3484,1380,7906, 724, # 7174 +2277, 600,1670,7907,1337,1233,4526,3103,2244,7908,1621,4527,7909, 651,4240,7910, # 7190 +1612,4241,2611,7911,2844,7912,2734,2307,3058,7913, 716,2459,3059, 174,1255,2701, # 7206 +4019,3590, 548,1320,1398, 728,4020,1574,7914,1890,1197,3060,4021,7915,3061,3062, # 7222 +3712,3591,3713, 747,7916, 635,4242,4528,7917,7918,7919,4243,7920,7921,4529,7922, # 7238 +3378,4530,2432, 451,7923,3714,2535,2072,4244,2735,4245,4022,7924,1764,4531,7925, # 7254 +4246, 350,7926,2278,2390,2486,7927,4247,4023,2245,1434,4024, 488,4532, 458,4248, # 7270 +4025,3715, 771,1330,2391,3835,2568,3159,2159,2409,1553,2667,3160,4249,7928,2487, # 7286 +2881,2612,1720,2702,4250,3379,4533,7929,2536,4251,7930,3231,4252,2768,7931,2015, # 7302 +2736,7932,1155,1017,3716,3836,7933,3303,2308, 201,1864,4253,1430,7934,4026,7935, # 7318 +7936,7937,7938,7939,4254,1604,7940, 414,1865, 371,2587,4534,4535,3485,2016,3104, # 7334 +4536,1708, 960,4255, 887, 389,2171,1536,1663,1721,7941,2228,4027,2351,2926,1580, # 7350 +7942,7943,7944,1744,7945,2537,4537,4538,7946,4539,7947,2073,7948,7949,3592,3380, # 7366 +2882,4256,7950,4257,2640,3381,2802, 673,2703,2460, 709,3486,4028,3593,4258,7951, # 7382 +1148, 502, 634,7952,7953,1204,4540,3594,1575,4541,2613,3717,7954,3718,3105, 948, # 7398 +3232, 121,1745,3837,1110,7955,4259,3063,2509,3009,4029,3719,1151,1771,3838,1488, # 7414 +4030,1986,7956,2433,3487,7957,7958,2093,7959,4260,3839,1213,1407,2803, 531,2737, # 7430 +2538,3233,1011,1537,7960,2769,4261,3106,1061,7961,3720,3721,1866,2883,7962,2017, # 7446 + 120,4262,4263,2062,3595,3234,2309,3840,2668,3382,1954,4542,7963,7964,3488,1047, # 7462 +2704,1266,7965,1368,4543,2845, 649,3383,3841,2539,2738,1102,2846,2669,7966,7967, # 7478 +1999,7968,1111,3596,2962,7969,2488,3842,3597,2804,1854,3384,3722,7970,7971,3385, # 7494 +2410,2884,3304,3235,3598,7972,2569,7973,3599,2805,4031,1460, 856,7974,3600,7975, # 7510 +2885,2963,7976,2886,3843,7977,4264, 632,2510, 875,3844,1697,3845,2291,7978,7979, # 7526 +4544,3010,1239, 580,4545,4265,7980, 914, 936,2074,1190,4032,1039,2123,7981,7982, # 7542 +7983,3386,1473,7984,1354,4266,3846,7985,2172,3064,4033, 915,3305,4267,4268,3306, # 7558 +1605,1834,7986,2739, 398,3601,4269,3847,4034, 328,1912,2847,4035,3848,1331,4270, # 7574 +3011, 937,4271,7987,3602,4036,4037,3387,2160,4546,3388, 524, 742, 538,3065,1012, # 7590 +7988,7989,3849,2461,7990, 658,1103, 225,3850,7991,7992,4547,7993,4548,7994,3236, # 7606 +1243,7995,4038, 963,2246,4549,7996,2705,3603,3161,7997,7998,2588,2327,7999,4550, # 7622 +8000,8001,8002,3489,3307, 957,3389,2540,2032,1930,2927,2462, 870,2018,3604,1746, # 7638 +2770,2771,2434,2463,8003,3851,8004,3723,3107,3724,3490,3390,3725,8005,1179,3066, # 7654 +8006,3162,2373,4272,3726,2541,3163,3108,2740,4039,8007,3391,1556,2542,2292, 977, # 7670 +2887,2033,4040,1205,3392,8008,1765,3393,3164,2124,1271,1689, 714,4551,3491,8009, # 7686 +2328,3852, 533,4273,3605,2181, 617,8010,2464,3308,3492,2310,8011,8012,3165,8013, # 7702 +8014,3853,1987, 618, 427,2641,3493,3394,8015,8016,1244,1690,8017,2806,4274,4552, # 7718 +8018,3494,8019,8020,2279,1576, 473,3606,4275,3395, 972,8021,3607,8022,3067,8023, # 7734 +8024,4553,4554,8025,3727,4041,4042,8026, 153,4555, 356,8027,1891,2888,4276,2143, # 7750 + 408, 803,2352,8028,3854,8029,4277,1646,2570,2511,4556,4557,3855,8030,3856,4278, # 7766 +8031,2411,3396, 752,8032,8033,1961,2964,8034, 746,3012,2465,8035,4279,3728, 698, # 7782 +4558,1892,4280,3608,2543,4559,3609,3857,8036,3166,3397,8037,1823,1302,4043,2706, # 7798 +3858,1973,4281,8038,4282,3167, 823,1303,1288,1236,2848,3495,4044,3398, 774,3859, # 7814 +8039,1581,4560,1304,2849,3860,4561,8040,2435,2161,1083,3237,4283,4045,4284, 344, # 7830 +1173, 288,2311, 454,1683,8041,8042,1461,4562,4046,2589,8043,8044,4563, 985, 894, # 7846 +8045,3399,3168,8046,1913,2928,3729,1988,8047,2110,1974,8048,4047,8049,2571,1194, # 7862 + 425,8050,4564,3169,1245,3730,4285,8051,8052,2850,8053, 636,4565,1855,3861, 760, # 7878 +1799,8054,4286,2209,1508,4566,4048,1893,1684,2293,8055,8056,8057,4287,4288,2210, # 7894 + 479,8058,8059, 832,8060,4049,2489,8061,2965,2490,3731, 990,3109, 627,1814,2642, # 7910 +4289,1582,4290,2125,2111,3496,4567,8062, 799,4291,3170,8063,4568,2112,1737,3013, # 7926 +1018, 543, 754,4292,3309,1676,4569,4570,4050,8064,1489,8065,3497,8066,2614,2889, # 7942 +4051,8067,8068,2966,8069,8070,8071,8072,3171,4571,4572,2182,1722,8073,3238,3239, # 7958 +1842,3610,1715, 481, 365,1975,1856,8074,8075,1962,2491,4573,8076,2126,3611,3240, # 7974 + 433,1894,2063,2075,8077, 602,2741,8078,8079,8080,8081,8082,3014,1628,3400,8083, # 7990 +3172,4574,4052,2890,4575,2512,8084,2544,2772,8085,8086,8087,3310,4576,2891,8088, # 8006 +4577,8089,2851,4578,4579,1221,2967,4053,2513,8090,8091,8092,1867,1989,8093,8094, # 8022 +8095,1895,8096,8097,4580,1896,4054, 318,8098,2094,4055,4293,8099,8100, 485,8101, # 8038 + 938,3862, 553,2670, 116,8102,3863,3612,8103,3498,2671,2773,3401,3311,2807,8104, # 8054 +3613,2929,4056,1747,2930,2968,8105,8106, 207,8107,8108,2672,4581,2514,8109,3015, # 8070 + 890,3614,3864,8110,1877,3732,3402,8111,2183,2353,3403,1652,8112,8113,8114, 941, # 8086 +2294, 208,3499,4057,2019, 330,4294,3865,2892,2492,3733,4295,8115,8116,8117,8118, # 8102 +#Everything below is of no interest for detection purpose +2515,1613,4582,8119,3312,3866,2516,8120,4058,8121,1637,4059,2466,4583,3867,8122, # 8118 +2493,3016,3734,8123,8124,2192,8125,8126,2162,8127,8128,8129,8130,8131,8132,8133, # 8134 +8134,8135,8136,8137,8138,8139,8140,8141,8142,8143,8144,8145,8146,8147,8148,8149, # 8150 +8150,8151,8152,8153,8154,8155,8156,8157,8158,8159,8160,8161,8162,8163,8164,8165, # 8166 +8166,8167,8168,8169,8170,8171,8172,8173,8174,8175,8176,8177,8178,8179,8180,8181, # 8182 +8182,8183,8184,8185,8186,8187,8188,8189,8190,8191,8192,8193,8194,8195,8196,8197, # 8198 +8198,8199,8200,8201,8202,8203,8204,8205,8206,8207,8208,8209,8210,8211,8212,8213, # 8214 +8214,8215,8216,8217,8218,8219,8220,8221,8222,8223,8224,8225,8226,8227,8228,8229, # 8230 +8230,8231,8232,8233,8234,8235,8236,8237,8238,8239,8240,8241,8242,8243,8244,8245, # 8246 +8246,8247,8248,8249,8250,8251,8252,8253,8254,8255,8256,8257,8258,8259,8260,8261, # 8262 +8262,8263,8264,8265,8266,8267,8268,8269,8270,8271,8272,8273,8274,8275,8276,8277, # 8278 +8278,8279,8280,8281,8282,8283,8284,8285,8286,8287,8288,8289,8290,8291,8292,8293, # 8294 +8294,8295,8296,8297,8298,8299,8300,8301,8302,8303,8304,8305,8306,8307,8308,8309, # 8310 +8310,8311,8312,8313,8314,8315,8316,8317,8318,8319,8320,8321,8322,8323,8324,8325, # 8326 +8326,8327,8328,8329,8330,8331,8332,8333,8334,8335,8336,8337,8338,8339,8340,8341, # 8342 +8342,8343,8344,8345,8346,8347,8348,8349,8350,8351,8352,8353,8354,8355,8356,8357, # 8358 +8358,8359,8360,8361,8362,8363,8364,8365,8366,8367,8368,8369,8370,8371,8372,8373, # 8374 +8374,8375,8376,8377,8378,8379,8380,8381,8382,8383,8384,8385,8386,8387,8388,8389, # 8390 +8390,8391,8392,8393,8394,8395,8396,8397,8398,8399,8400,8401,8402,8403,8404,8405, # 8406 +8406,8407,8408,8409,8410,8411,8412,8413,8414,8415,8416,8417,8418,8419,8420,8421, # 8422 +8422,8423,8424,8425,8426,8427,8428,8429,8430,8431,8432,8433,8434,8435,8436,8437, # 8438 +8438,8439,8440,8441,8442,8443,8444,8445,8446,8447,8448,8449,8450,8451,8452,8453, # 8454 +8454,8455,8456,8457,8458,8459,8460,8461,8462,8463,8464,8465,8466,8467,8468,8469, # 8470 +8470,8471,8472,8473,8474,8475,8476,8477,8478,8479,8480,8481,8482,8483,8484,8485, # 8486 +8486,8487,8488,8489,8490,8491,8492,8493,8494,8495,8496,8497,8498,8499,8500,8501, # 8502 +8502,8503,8504,8505,8506,8507,8508,8509,8510,8511,8512,8513,8514,8515,8516,8517, # 8518 +8518,8519,8520,8521,8522,8523,8524,8525,8526,8527,8528,8529,8530,8531,8532,8533, # 8534 +8534,8535,8536,8537,8538,8539,8540,8541,8542,8543,8544,8545,8546,8547,8548,8549, # 8550 +8550,8551,8552,8553,8554,8555,8556,8557,8558,8559,8560,8561,8562,8563,8564,8565, # 8566 +8566,8567,8568,8569,8570,8571,8572,8573,8574,8575,8576,8577,8578,8579,8580,8581, # 8582 +8582,8583,8584,8585,8586,8587,8588,8589,8590,8591,8592,8593,8594,8595,8596,8597, # 8598 +8598,8599,8600,8601,8602,8603,8604,8605,8606,8607,8608,8609,8610,8611,8612,8613, # 8614 +8614,8615,8616,8617,8618,8619,8620,8621,8622,8623,8624,8625,8626,8627,8628,8629, # 8630 +8630,8631,8632,8633,8634,8635,8636,8637,8638,8639,8640,8641,8642,8643,8644,8645, # 8646 +8646,8647,8648,8649,8650,8651,8652,8653,8654,8655,8656,8657,8658,8659,8660,8661, # 8662 +8662,8663,8664,8665,8666,8667,8668,8669,8670,8671,8672,8673,8674,8675,8676,8677, # 8678 +8678,8679,8680,8681,8682,8683,8684,8685,8686,8687,8688,8689,8690,8691,8692,8693, # 8694 +8694,8695,8696,8697,8698,8699,8700,8701,8702,8703,8704,8705,8706,8707,8708,8709, # 8710 +8710,8711,8712,8713,8714,8715,8716,8717,8718,8719,8720,8721,8722,8723,8724,8725, # 8726 +8726,8727,8728,8729,8730,8731,8732,8733,8734,8735,8736,8737,8738,8739,8740,8741) # 8742 + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euctwprober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euctwprober.py new file mode 100644 index 0000000..fe652fe --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/euctwprober.py @@ -0,0 +1,41 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import EUCTWDistributionAnalysis +from .mbcssm import EUCTWSMModel + +class EUCTWProber(MultiByteCharSetProber): + def __init__(self): + MultiByteCharSetProber.__init__(self) + self._mCodingSM = CodingStateMachine(EUCTWSMModel) + self._mDistributionAnalyzer = EUCTWDistributionAnalysis() + self.reset() + + def get_charset_name(self): + return "EUC-TW" diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/gb2312freq.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/gb2312freq.py new file mode 100644 index 0000000..1238f51 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/gb2312freq.py @@ -0,0 +1,472 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# GB2312 most frequently used character table +# +# Char to FreqOrder table , from hz6763 + +# 512 --> 0.79 -- 0.79 +# 1024 --> 0.92 -- 0.13 +# 2048 --> 0.98 -- 0.06 +# 6768 --> 1.00 -- 0.02 +# +# Ideal Distribution Ratio = 0.79135/(1-0.79135) = 3.79 +# Random Distribution Ration = 512 / (3755 - 512) = 0.157 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher that RDR + +GB2312_TYPICAL_DISTRIBUTION_RATIO = 0.9 + +GB2312_TABLE_SIZE = 3760 + +GB2312CharToFreqOrder = ( +1671, 749,1443,2364,3924,3807,2330,3921,1704,3463,2691,1511,1515, 572,3191,2205, +2361, 224,2558, 479,1711, 963,3162, 440,4060,1905,2966,2947,3580,2647,3961,3842, +2204, 869,4207, 970,2678,5626,2944,2956,1479,4048, 514,3595, 588,1346,2820,3409, + 249,4088,1746,1873,2047,1774, 581,1813, 358,1174,3590,1014,1561,4844,2245, 670, +1636,3112, 889,1286, 953, 556,2327,3060,1290,3141, 613, 185,3477,1367, 850,3820, +1715,2428,2642,2303,2732,3041,2562,2648,3566,3946,1349, 388,3098,2091,1360,3585, + 152,1687,1539, 738,1559, 59,1232,2925,2267,1388,1249,1741,1679,2960, 151,1566, +1125,1352,4271, 924,4296, 385,3166,4459, 310,1245,2850, 70,3285,2729,3534,3575, +2398,3298,3466,1960,2265, 217,3647, 864,1909,2084,4401,2773,1010,3269,5152, 853, +3051,3121,1244,4251,1895, 364,1499,1540,2313,1180,3655,2268, 562, 715,2417,3061, + 544, 336,3768,2380,1752,4075, 950, 280,2425,4382, 183,2759,3272, 333,4297,2155, +1688,2356,1444,1039,4540, 736,1177,3349,2443,2368,2144,2225, 565, 196,1482,3406, + 927,1335,4147, 692, 878,1311,1653,3911,3622,1378,4200,1840,2969,3149,2126,1816, +2534,1546,2393,2760, 737,2494, 13, 447, 245,2747, 38,2765,2129,2589,1079, 606, + 360, 471,3755,2890, 404, 848, 699,1785,1236, 370,2221,1023,3746,2074,2026,2023, +2388,1581,2119, 812,1141,3091,2536,1519, 804,2053, 406,1596,1090, 784, 548,4414, +1806,2264,2936,1100, 343,4114,5096, 622,3358, 743,3668,1510,1626,5020,3567,2513, +3195,4115,5627,2489,2991, 24,2065,2697,1087,2719, 48,1634, 315, 68, 985,2052, + 198,2239,1347,1107,1439, 597,2366,2172, 871,3307, 919,2487,2790,1867, 236,2570, +1413,3794, 906,3365,3381,1701,1982,1818,1524,2924,1205, 616,2586,2072,2004, 575, + 253,3099, 32,1365,1182, 197,1714,2454,1201, 554,3388,3224,2748, 756,2587, 250, +2567,1507,1517,3529,1922,2761,2337,3416,1961,1677,2452,2238,3153, 615, 911,1506, +1474,2495,1265,1906,2749,3756,3280,2161, 898,2714,1759,3450,2243,2444, 563, 26, +3286,2266,3769,3344,2707,3677, 611,1402, 531,1028,2871,4548,1375, 261,2948, 835, +1190,4134, 353, 840,2684,1900,3082,1435,2109,1207,1674, 329,1872,2781,4055,2686, +2104, 608,3318,2423,2957,2768,1108,3739,3512,3271,3985,2203,1771,3520,1418,2054, +1681,1153, 225,1627,2929, 162,2050,2511,3687,1954, 124,1859,2431,1684,3032,2894, + 585,4805,3969,2869,2704,2088,2032,2095,3656,2635,4362,2209, 256, 518,2042,2105, +3777,3657, 643,2298,1148,1779, 190, 989,3544, 414, 11,2135,2063,2979,1471, 403, +3678, 126, 770,1563, 671,2499,3216,2877, 600,1179, 307,2805,4937,1268,1297,2694, + 252,4032,1448,1494,1331,1394, 127,2256, 222,1647,1035,1481,3056,1915,1048, 873, +3651, 210, 33,1608,2516, 200,1520, 415, 102, 0,3389,1287, 817, 91,3299,2940, + 836,1814, 549,2197,1396,1669,2987,3582,2297,2848,4528,1070, 687, 20,1819, 121, +1552,1364,1461,1968,2617,3540,2824,2083, 177, 948,4938,2291, 110,4549,2066, 648, +3359,1755,2110,2114,4642,4845,1693,3937,3308,1257,1869,2123, 208,1804,3159,2992, +2531,2549,3361,2418,1350,2347,2800,2568,1291,2036,2680, 72, 842,1990, 212,1233, +1154,1586, 75,2027,3410,4900,1823,1337,2710,2676, 728,2810,1522,3026,4995, 157, + 755,1050,4022, 710, 785,1936,2194,2085,1406,2777,2400, 150,1250,4049,1206, 807, +1910, 534, 529,3309,1721,1660, 274, 39,2827, 661,2670,1578, 925,3248,3815,1094, +4278,4901,4252, 41,1150,3747,2572,2227,4501,3658,4902,3813,3357,3617,2884,2258, + 887, 538,4187,3199,1294,2439,3042,2329,2343,2497,1255, 107, 543,1527, 521,3478, +3568, 194,5062, 15, 961,3870,1241,1192,2664, 66,5215,3260,2111,1295,1127,2152, +3805,4135, 901,1164,1976, 398,1278, 530,1460, 748, 904,1054,1966,1426, 53,2909, + 509, 523,2279,1534, 536,1019, 239,1685, 460,2353, 673,1065,2401,3600,4298,2272, +1272,2363, 284,1753,3679,4064,1695, 81, 815,2677,2757,2731,1386, 859, 500,4221, +2190,2566, 757,1006,2519,2068,1166,1455, 337,2654,3203,1863,1682,1914,3025,1252, +1409,1366, 847, 714,2834,2038,3209, 964,2970,1901, 885,2553,1078,1756,3049, 301, +1572,3326, 688,2130,1996,2429,1805,1648,2930,3421,2750,3652,3088, 262,1158,1254, + 389,1641,1812, 526,1719, 923,2073,1073,1902, 468, 489,4625,1140, 857,2375,3070, +3319,2863, 380, 116,1328,2693,1161,2244, 273,1212,1884,2769,3011,1775,1142, 461, +3066,1200,2147,2212, 790, 702,2695,4222,1601,1058, 434,2338,5153,3640, 67,2360, +4099,2502, 618,3472,1329, 416,1132, 830,2782,1807,2653,3211,3510,1662, 192,2124, + 296,3979,1739,1611,3684, 23, 118, 324, 446,1239,1225, 293,2520,3814,3795,2535, +3116, 17,1074, 467,2692,2201, 387,2922, 45,1326,3055,1645,3659,2817, 958, 243, +1903,2320,1339,2825,1784,3289, 356, 576, 865,2315,2381,3377,3916,1088,3122,1713, +1655, 935, 628,4689,1034,1327, 441, 800, 720, 894,1979,2183,1528,5289,2702,1071, +4046,3572,2399,1571,3281, 79, 761,1103, 327, 134, 758,1899,1371,1615, 879, 442, + 215,2605,2579, 173,2048,2485,1057,2975,3317,1097,2253,3801,4263,1403,1650,2946, + 814,4968,3487,1548,2644,1567,1285, 2, 295,2636, 97, 946,3576, 832, 141,4257, +3273, 760,3821,3521,3156,2607, 949,1024,1733,1516,1803,1920,2125,2283,2665,3180, +1501,2064,3560,2171,1592, 803,3518,1416, 732,3897,4258,1363,1362,2458, 119,1427, + 602,1525,2608,1605,1639,3175, 694,3064, 10, 465, 76,2000,4846,4208, 444,3781, +1619,3353,2206,1273,3796, 740,2483, 320,1723,2377,3660,2619,1359,1137,1762,1724, +2345,2842,1850,1862, 912, 821,1866, 612,2625,1735,2573,3369,1093, 844, 89, 937, + 930,1424,3564,2413,2972,1004,3046,3019,2011, 711,3171,1452,4178, 428, 801,1943, + 432, 445,2811, 206,4136,1472, 730, 349, 73, 397,2802,2547, 998,1637,1167, 789, + 396,3217, 154,1218, 716,1120,1780,2819,4826,1931,3334,3762,2139,1215,2627, 552, +3664,3628,3232,1405,2383,3111,1356,2652,3577,3320,3101,1703, 640,1045,1370,1246, +4996, 371,1575,2436,1621,2210, 984,4033,1734,2638, 16,4529, 663,2755,3255,1451, +3917,2257,1253,1955,2234,1263,2951, 214,1229, 617, 485, 359,1831,1969, 473,2310, + 750,2058, 165, 80,2864,2419, 361,4344,2416,2479,1134, 796,3726,1266,2943, 860, +2715, 938, 390,2734,1313,1384, 248, 202, 877,1064,2854, 522,3907, 279,1602, 297, +2357, 395,3740, 137,2075, 944,4089,2584,1267,3802, 62,1533,2285, 178, 176, 780, +2440, 201,3707, 590, 478,1560,4354,2117,1075, 30, 74,4643,4004,1635,1441,2745, + 776,2596, 238,1077,1692,1912,2844, 605, 499,1742,3947, 241,3053, 980,1749, 936, +2640,4511,2582, 515,1543,2162,5322,2892,2993, 890,2148,1924, 665,1827,3581,1032, + 968,3163, 339,1044,1896, 270, 583,1791,1720,4367,1194,3488,3669, 43,2523,1657, + 163,2167, 290,1209,1622,3378, 550, 634,2508,2510, 695,2634,2384,2512,1476,1414, + 220,1469,2341,2138,2852,3183,2900,4939,2865,3502,1211,3680, 854,3227,1299,2976, +3172, 186,2998,1459, 443,1067,3251,1495, 321,1932,3054, 909, 753,1410,1828, 436, +2441,1119,1587,3164,2186,1258, 227, 231,1425,1890,3200,3942, 247, 959, 725,5254, +2741, 577,2158,2079, 929, 120, 174, 838,2813, 591,1115, 417,2024, 40,3240,1536, +1037, 291,4151,2354, 632,1298,2406,2500,3535,1825,1846,3451, 205,1171, 345,4238, + 18,1163, 811, 685,2208,1217, 425,1312,1508,1175,4308,2552,1033, 587,1381,3059, +2984,3482, 340,1316,4023,3972, 792,3176, 519, 777,4690, 918, 933,4130,2981,3741, + 90,3360,2911,2200,5184,4550, 609,3079,2030, 272,3379,2736, 363,3881,1130,1447, + 286, 779, 357,1169,3350,3137,1630,1220,2687,2391, 747,1277,3688,2618,2682,2601, +1156,3196,5290,4034,3102,1689,3596,3128, 874, 219,2783, 798, 508,1843,2461, 269, +1658,1776,1392,1913,2983,3287,2866,2159,2372, 829,4076, 46,4253,2873,1889,1894, + 915,1834,1631,2181,2318, 298, 664,2818,3555,2735, 954,3228,3117, 527,3511,2173, + 681,2712,3033,2247,2346,3467,1652, 155,2164,3382, 113,1994, 450, 899, 494, 994, +1237,2958,1875,2336,1926,3727, 545,1577,1550, 633,3473, 204,1305,3072,2410,1956, +2471, 707,2134, 841,2195,2196,2663,3843,1026,4940, 990,3252,4997, 368,1092, 437, +3212,3258,1933,1829, 675,2977,2893, 412, 943,3723,4644,3294,3283,2230,2373,5154, +2389,2241,2661,2323,1404,2524, 593, 787, 677,3008,1275,2059, 438,2709,2609,2240, +2269,2246,1446, 36,1568,1373,3892,1574,2301,1456,3962, 693,2276,5216,2035,1143, +2720,1919,1797,1811,2763,4137,2597,1830,1699,1488,1198,2090, 424,1694, 312,3634, +3390,4179,3335,2252,1214, 561,1059,3243,2295,2561, 975,5155,2321,2751,3772, 472, +1537,3282,3398,1047,2077,2348,2878,1323,3340,3076, 690,2906, 51, 369, 170,3541, +1060,2187,2688,3670,2541,1083,1683, 928,3918, 459, 109,4427, 599,3744,4286, 143, +2101,2730,2490, 82,1588,3036,2121, 281,1860, 477,4035,1238,2812,3020,2716,3312, +1530,2188,2055,1317, 843, 636,1808,1173,3495, 649, 181,1002, 147,3641,1159,2414, +3750,2289,2795, 813,3123,2610,1136,4368, 5,3391,4541,2174, 420, 429,1728, 754, +1228,2115,2219, 347,2223,2733, 735,1518,3003,2355,3134,1764,3948,3329,1888,2424, +1001,1234,1972,3321,3363,1672,1021,1450,1584, 226, 765, 655,2526,3404,3244,2302, +3665, 731, 594,2184, 319,1576, 621, 658,2656,4299,2099,3864,1279,2071,2598,2739, + 795,3086,3699,3908,1707,2352,2402,1382,3136,2475,1465,4847,3496,3865,1085,3004, +2591,1084, 213,2287,1963,3565,2250, 822, 793,4574,3187,1772,1789,3050, 595,1484, +1959,2770,1080,2650, 456, 422,2996, 940,3322,4328,4345,3092,2742, 965,2784, 739, +4124, 952,1358,2498,2949,2565, 332,2698,2378, 660,2260,2473,4194,3856,2919, 535, +1260,2651,1208,1428,1300,1949,1303,2942, 433,2455,2450,1251,1946, 614,1269, 641, +1306,1810,2737,3078,2912, 564,2365,1419,1415,1497,4460,2367,2185,1379,3005,1307, +3218,2175,1897,3063, 682,1157,4040,4005,1712,1160,1941,1399, 394, 402,2952,1573, +1151,2986,2404, 862, 299,2033,1489,3006, 346, 171,2886,3401,1726,2932, 168,2533, + 47,2507,1030,3735,1145,3370,1395,1318,1579,3609,4560,2857,4116,1457,2529,1965, + 504,1036,2690,2988,2405, 745,5871, 849,2397,2056,3081, 863,2359,3857,2096, 99, +1397,1769,2300,4428,1643,3455,1978,1757,3718,1440, 35,4879,3742,1296,4228,2280, + 160,5063,1599,2013, 166, 520,3479,1646,3345,3012, 490,1937,1545,1264,2182,2505, +1096,1188,1369,1436,2421,1667,2792,2460,1270,2122, 727,3167,2143, 806,1706,1012, +1800,3037, 960,2218,1882, 805, 139,2456,1139,1521, 851,1052,3093,3089, 342,2039, + 744,5097,1468,1502,1585,2087, 223, 939, 326,2140,2577, 892,2481,1623,4077, 982, +3708, 135,2131, 87,2503,3114,2326,1106, 876,1616, 547,2997,2831,2093,3441,4530, +4314, 9,3256,4229,4148, 659,1462,1986,1710,2046,2913,2231,4090,4880,5255,3392, +3274,1368,3689,4645,1477, 705,3384,3635,1068,1529,2941,1458,3782,1509, 100,1656, +2548, 718,2339, 408,1590,2780,3548,1838,4117,3719,1345,3530, 717,3442,2778,3220, +2898,1892,4590,3614,3371,2043,1998,1224,3483, 891, 635, 584,2559,3355, 733,1766, +1729,1172,3789,1891,2307, 781,2982,2271,1957,1580,5773,2633,2005,4195,3097,1535, +3213,1189,1934,5693,3262, 586,3118,1324,1598, 517,1564,2217,1868,1893,4445,3728, +2703,3139,1526,1787,1992,3882,2875,1549,1199,1056,2224,1904,2711,5098,4287, 338, +1993,3129,3489,2689,1809,2815,1997, 957,1855,3898,2550,3275,3057,1105,1319, 627, +1505,1911,1883,3526, 698,3629,3456,1833,1431, 746, 77,1261,2017,2296,1977,1885, + 125,1334,1600, 525,1798,1109,2222,1470,1945, 559,2236,1186,3443,2476,1929,1411, +2411,3135,1777,3372,2621,1841,1613,3229, 668,1430,1839,2643,2916, 195,1989,2671, +2358,1387, 629,3205,2293,5256,4439, 123,1310, 888,1879,4300,3021,3605,1003,1162, +3192,2910,2010, 140,2395,2859, 55,1082,2012,2901, 662, 419,2081,1438, 680,2774, +4654,3912,1620,1731,1625,5035,4065,2328, 512,1344, 802,5443,2163,2311,2537, 524, +3399, 98,1155,2103,1918,2606,3925,2816,1393,2465,1504,3773,2177,3963,1478,4346, + 180,1113,4655,3461,2028,1698, 833,2696,1235,1322,1594,4408,3623,3013,3225,2040, +3022, 541,2881, 607,3632,2029,1665,1219, 639,1385,1686,1099,2803,3231,1938,3188, +2858, 427, 676,2772,1168,2025, 454,3253,2486,3556, 230,1950, 580, 791,1991,1280, +1086,1974,2034, 630, 257,3338,2788,4903,1017, 86,4790, 966,2789,1995,1696,1131, + 259,3095,4188,1308, 179,1463,5257, 289,4107,1248, 42,3413,1725,2288, 896,1947, + 774,4474,4254, 604,3430,4264, 392,2514,2588, 452, 237,1408,3018, 988,4531,1970, +3034,3310, 540,2370,1562,1288,2990, 502,4765,1147, 4,1853,2708, 207, 294,2814, +4078,2902,2509, 684, 34,3105,3532,2551, 644, 709,2801,2344, 573,1727,3573,3557, +2021,1081,3100,4315,2100,3681, 199,2263,1837,2385, 146,3484,1195,2776,3949, 997, +1939,3973,1008,1091,1202,1962,1847,1149,4209,5444,1076, 493, 117,5400,2521, 972, +1490,2934,1796,4542,2374,1512,2933,2657, 413,2888,1135,2762,2314,2156,1355,2369, + 766,2007,2527,2170,3124,2491,2593,2632,4757,2437, 234,3125,3591,1898,1750,1376, +1942,3468,3138, 570,2127,2145,3276,4131, 962, 132,1445,4196, 19, 941,3624,3480, +3366,1973,1374,4461,3431,2629, 283,2415,2275, 808,2887,3620,2112,2563,1353,3610, + 955,1089,3103,1053, 96, 88,4097, 823,3808,1583, 399, 292,4091,3313, 421,1128, + 642,4006, 903,2539,1877,2082, 596, 29,4066,1790, 722,2157, 130, 995,1569, 769, +1485, 464, 513,2213, 288,1923,1101,2453,4316, 133, 486,2445, 50, 625, 487,2207, + 57, 423, 481,2962, 159,3729,1558, 491, 303, 482, 501, 240,2837, 112,3648,2392, +1783, 362, 8,3433,3422, 610,2793,3277,1390,1284,1654, 21,3823, 734, 367, 623, + 193, 287, 374,1009,1483, 816, 476, 313,2255,2340,1262,2150,2899,1146,2581, 782, +2116,1659,2018,1880, 255,3586,3314,1110,2867,2137,2564, 986,2767,5185,2006, 650, + 158, 926, 762, 881,3157,2717,2362,3587, 306,3690,3245,1542,3077,2427,1691,2478, +2118,2985,3490,2438, 539,2305, 983, 129,1754, 355,4201,2386, 827,2923, 104,1773, +2838,2771, 411,2905,3919, 376, 767, 122,1114, 828,2422,1817,3506, 266,3460,1007, +1609,4998, 945,2612,4429,2274, 726,1247,1964,2914,2199,2070,4002,4108, 657,3323, +1422, 579, 455,2764,4737,1222,2895,1670, 824,1223,1487,2525, 558, 861,3080, 598, +2659,2515,1967, 752,2583,2376,2214,4180, 977, 704,2464,4999,2622,4109,1210,2961, + 819,1541, 142,2284, 44, 418, 457,1126,3730,4347,4626,1644,1876,3671,1864, 302, +1063,5694, 624, 723,1984,3745,1314,1676,2488,1610,1449,3558,3569,2166,2098, 409, +1011,2325,3704,2306, 818,1732,1383,1824,1844,3757, 999,2705,3497,1216,1423,2683, +2426,2954,2501,2726,2229,1475,2554,5064,1971,1794,1666,2014,1343, 783, 724, 191, +2434,1354,2220,5065,1763,2752,2472,4152, 131, 175,2885,3434, 92,1466,4920,2616, +3871,3872,3866, 128,1551,1632, 669,1854,3682,4691,4125,1230, 188,2973,3290,1302, +1213, 560,3266, 917, 763,3909,3249,1760, 868,1958, 764,1782,2097, 145,2277,3774, +4462, 64,1491,3062, 971,2132,3606,2442, 221,1226,1617, 218, 323,1185,3207,3147, + 571, 619,1473,1005,1744,2281, 449,1887,2396,3685, 275, 375,3816,1743,3844,3731, + 845,1983,2350,4210,1377, 773, 967,3499,3052,3743,2725,4007,1697,1022,3943,1464, +3264,2855,2722,1952,1029,2839,2467, 84,4383,2215, 820,1391,2015,2448,3672, 377, +1948,2168, 797,2545,3536,2578,2645, 94,2874,1678, 405,1259,3071, 771, 546,1315, + 470,1243,3083, 895,2468, 981, 969,2037, 846,4181, 653,1276,2928, 14,2594, 557, +3007,2474, 156, 902,1338,1740,2574, 537,2518, 973,2282,2216,2433,1928, 138,2903, +1293,2631,1612, 646,3457, 839,2935, 111, 496,2191,2847, 589,3186, 149,3994,2060, +4031,2641,4067,3145,1870, 37,3597,2136,1025,2051,3009,3383,3549,1121,1016,3261, +1301, 251,2446,2599,2153, 872,3246, 637, 334,3705, 831, 884, 921,3065,3140,4092, +2198,1944, 246,2964, 108,2045,1152,1921,2308,1031, 203,3173,4170,1907,3890, 810, +1401,2003,1690, 506, 647,1242,2828,1761,1649,3208,2249,1589,3709,2931,5156,1708, + 498, 666,2613, 834,3817,1231, 184,2851,1124, 883,3197,2261,3710,1765,1553,2658, +1178,2639,2351, 93,1193, 942,2538,2141,4402, 235,1821, 870,1591,2192,1709,1871, +3341,1618,4126,2595,2334, 603, 651, 69, 701, 268,2662,3411,2555,1380,1606, 503, + 448, 254,2371,2646, 574,1187,2309,1770, 322,2235,1292,1801, 305, 566,1133, 229, +2067,2057, 706, 167, 483,2002,2672,3295,1820,3561,3067, 316, 378,2746,3452,1112, + 136,1981, 507,1651,2917,1117, 285,4591, 182,2580,3522,1304, 335,3303,1835,2504, +1795,1792,2248, 674,1018,2106,2449,1857,2292,2845, 976,3047,1781,2600,2727,1389, +1281, 52,3152, 153, 265,3950, 672,3485,3951,4463, 430,1183, 365, 278,2169, 27, +1407,1336,2304, 209,1340,1730,2202,1852,2403,2883, 979,1737,1062, 631,2829,2542, +3876,2592, 825,2086,2226,3048,3625, 352,1417,3724, 542, 991, 431,1351,3938,1861, +2294, 826,1361,2927,3142,3503,1738, 463,2462,2723, 582,1916,1595,2808, 400,3845, +3891,2868,3621,2254, 58,2492,1123, 910,2160,2614,1372,1603,1196,1072,3385,1700, +3267,1980, 696, 480,2430, 920, 799,1570,2920,1951,2041,4047,2540,1321,4223,2469, +3562,2228,1271,2602, 401,2833,3351,2575,5157, 907,2312,1256, 410, 263,3507,1582, + 996, 678,1849,2316,1480, 908,3545,2237, 703,2322, 667,1826,2849,1531,2604,2999, +2407,3146,2151,2630,1786,3711, 469,3542, 497,3899,2409, 858, 837,4446,3393,1274, + 786, 620,1845,2001,3311, 484, 308,3367,1204,1815,3691,2332,1532,2557,1842,2020, +2724,1927,2333,4440, 567, 22,1673,2728,4475,1987,1858,1144,1597, 101,1832,3601, + 12, 974,3783,4391, 951,1412, 1,3720, 453,4608,4041, 528,1041,1027,3230,2628, +1129, 875,1051,3291,1203,2262,1069,2860,2799,2149,2615,3278, 144,1758,3040, 31, + 475,1680, 366,2685,3184, 311,1642,4008,2466,5036,1593,1493,2809, 216,1420,1668, + 233, 304,2128,3284, 232,1429,1768,1040,2008,3407,2740,2967,2543, 242,2133, 778, +1565,2022,2620, 505,2189,2756,1098,2273, 372,1614, 708, 553,2846,2094,2278, 169, +3626,2835,4161, 228,2674,3165, 809,1454,1309, 466,1705,1095, 900,3423, 880,2667, +3751,5258,2317,3109,2571,4317,2766,1503,1342, 866,4447,1118, 63,2076, 314,1881, +1348,1061, 172, 978,3515,1747, 532, 511,3970, 6, 601, 905,2699,3300,1751, 276, +1467,3725,2668, 65,4239,2544,2779,2556,1604, 578,2451,1802, 992,2331,2624,1320, +3446, 713,1513,1013, 103,2786,2447,1661, 886,1702, 916, 654,3574,2031,1556, 751, +2178,2821,2179,1498,1538,2176, 271, 914,2251,2080,1325, 638,1953,2937,3877,2432, +2754, 95,3265,1716, 260,1227,4083, 775, 106,1357,3254, 426,1607, 555,2480, 772, +1985, 244,2546, 474, 495,1046,2611,1851,2061, 71,2089,1675,2590, 742,3758,2843, +3222,1433, 267,2180,2576,2826,2233,2092,3913,2435, 956,1745,3075, 856,2113,1116, + 451, 3,1988,2896,1398, 993,2463,1878,2049,1341,2718,2721,2870,2108, 712,2904, +4363,2753,2324, 277,2872,2349,2649, 384, 987, 435, 691,3000, 922, 164,3939, 652, +1500,1184,4153,2482,3373,2165,4848,2335,3775,3508,3154,2806,2830,1554,2102,1664, +2530,1434,2408, 893,1547,2623,3447,2832,2242,2532,3169,2856,3223,2078, 49,3770, +3469, 462, 318, 656,2259,3250,3069, 679,1629,2758, 344,1138,1104,3120,1836,1283, +3115,2154,1437,4448, 934, 759,1999, 794,2862,1038, 533,2560,1722,2342, 855,2626, +1197,1663,4476,3127, 85,4240,2528, 25,1111,1181,3673, 407,3470,4561,2679,2713, + 768,1925,2841,3986,1544,1165, 932, 373,1240,2146,1930,2673, 721,4766, 354,4333, + 391,2963, 187, 61,3364,1442,1102, 330,1940,1767, 341,3809,4118, 393,2496,2062, +2211, 105, 331, 300, 439, 913,1332, 626, 379,3304,1557, 328, 689,3952, 309,1555, + 931, 317,2517,3027, 325, 569, 686,2107,3084, 60,1042,1333,2794, 264,3177,4014, +1628, 258,3712, 7,4464,1176,1043,1778, 683, 114,1975, 78,1492, 383,1886, 510, + 386, 645,5291,2891,2069,3305,4138,3867,2939,2603,2493,1935,1066,1848,3588,1015, +1282,1289,4609, 697,1453,3044,2666,3611,1856,2412, 54, 719,1330, 568,3778,2459, +1748, 788, 492, 551,1191,1000, 488,3394,3763, 282,1799, 348,2016,1523,3155,2390, +1049, 382,2019,1788,1170, 729,2968,3523, 897,3926,2785,2938,3292, 350,2319,3238, +1718,1717,2655,3453,3143,4465, 161,2889,2980,2009,1421, 56,1908,1640,2387,2232, +1917,1874,2477,4921, 148, 83,3438, 592,4245,2882,1822,1055, 741, 115,1496,1624, + 381,1638,4592,1020, 516,3214, 458, 947,4575,1432, 211,1514,2926,1865,2142, 189, + 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, # last 512 +#Everything below is of no interest for detection purpose +5508,6484,3900,3414,3974,4441,4024,3537,4037,5628,5099,3633,6485,3148,6486,3636, +5509,3257,5510,5973,5445,5872,4941,4403,3174,4627,5873,6276,2286,4230,5446,5874, +5122,6102,6103,4162,5447,5123,5323,4849,6277,3980,3851,5066,4246,5774,5067,6278, +3001,2807,5695,3346,5775,5974,5158,5448,6487,5975,5976,5776,3598,6279,5696,4806, +4211,4154,6280,6488,6489,6490,6281,4212,5037,3374,4171,6491,4562,4807,4722,4827, +5977,6104,4532,4079,5159,5324,5160,4404,3858,5359,5875,3975,4288,4610,3486,4512, +5325,3893,5360,6282,6283,5560,2522,4231,5978,5186,5449,2569,3878,6284,5401,3578, +4415,6285,4656,5124,5979,2506,4247,4449,3219,3417,4334,4969,4329,6492,4576,4828, +4172,4416,4829,5402,6286,3927,3852,5361,4369,4830,4477,4867,5876,4173,6493,6105, +4657,6287,6106,5877,5450,6494,4155,4868,5451,3700,5629,4384,6288,6289,5878,3189, +4881,6107,6290,6495,4513,6496,4692,4515,4723,5100,3356,6497,6291,3810,4080,5561, +3570,4430,5980,6498,4355,5697,6499,4724,6108,6109,3764,4050,5038,5879,4093,3226, +6292,5068,5217,4693,3342,5630,3504,4831,4377,4466,4309,5698,4431,5777,6293,5778, +4272,3706,6110,5326,3752,4676,5327,4273,5403,4767,5631,6500,5699,5880,3475,5039, +6294,5562,5125,4348,4301,4482,4068,5126,4593,5700,3380,3462,5981,5563,3824,5404, +4970,5511,3825,4738,6295,6501,5452,4516,6111,5881,5564,6502,6296,5982,6503,4213, +4163,3454,6504,6112,4009,4450,6113,4658,6297,6114,3035,6505,6115,3995,4904,4739, +4563,4942,4110,5040,3661,3928,5362,3674,6506,5292,3612,4791,5565,4149,5983,5328, +5259,5021,4725,4577,4564,4517,4364,6298,5405,4578,5260,4594,4156,4157,5453,3592, +3491,6507,5127,5512,4709,4922,5984,5701,4726,4289,6508,4015,6116,5128,4628,3424, +4241,5779,6299,4905,6509,6510,5454,5702,5780,6300,4365,4923,3971,6511,5161,3270, +3158,5985,4100, 867,5129,5703,6117,5363,3695,3301,5513,4467,6118,6512,5455,4232, +4242,4629,6513,3959,4478,6514,5514,5329,5986,4850,5162,5566,3846,4694,6119,5456, +4869,5781,3779,6301,5704,5987,5515,4710,6302,5882,6120,4392,5364,5705,6515,6121, +6516,6517,3736,5988,5457,5989,4695,2457,5883,4551,5782,6303,6304,6305,5130,4971, +6122,5163,6123,4870,3263,5365,3150,4871,6518,6306,5783,5069,5706,3513,3498,4409, +5330,5632,5366,5458,5459,3991,5990,4502,3324,5991,5784,3696,4518,5633,4119,6519, +4630,5634,4417,5707,4832,5992,3418,6124,5993,5567,4768,5218,6520,4595,3458,5367, +6125,5635,6126,4202,6521,4740,4924,6307,3981,4069,4385,6308,3883,2675,4051,3834, +4302,4483,5568,5994,4972,4101,5368,6309,5164,5884,3922,6127,6522,6523,5261,5460, +5187,4164,5219,3538,5516,4111,3524,5995,6310,6311,5369,3181,3386,2484,5188,3464, +5569,3627,5708,6524,5406,5165,4677,4492,6312,4872,4851,5885,4468,5996,6313,5709, +5710,6128,2470,5886,6314,5293,4882,5785,3325,5461,5101,6129,5711,5786,6525,4906, +6526,6527,4418,5887,5712,4808,2907,3701,5713,5888,6528,3765,5636,5331,6529,6530, +3593,5889,3637,4943,3692,5714,5787,4925,6315,6130,5462,4405,6131,6132,6316,5262, +6531,6532,5715,3859,5716,5070,4696,5102,3929,5788,3987,4792,5997,6533,6534,3920, +4809,5000,5998,6535,2974,5370,6317,5189,5263,5717,3826,6536,3953,5001,4883,3190, +5463,5890,4973,5999,4741,6133,6134,3607,5570,6000,4711,3362,3630,4552,5041,6318, +6001,2950,2953,5637,4646,5371,4944,6002,2044,4120,3429,6319,6537,5103,4833,6538, +6539,4884,4647,3884,6003,6004,4758,3835,5220,5789,4565,5407,6540,6135,5294,4697, +4852,6320,6321,3206,4907,6541,6322,4945,6542,6136,6543,6323,6005,4631,3519,6544, +5891,6545,5464,3784,5221,6546,5571,4659,6547,6324,6137,5190,6548,3853,6549,4016, +4834,3954,6138,5332,3827,4017,3210,3546,4469,5408,5718,3505,4648,5790,5131,5638, +5791,5465,4727,4318,6325,6326,5792,4553,4010,4698,3439,4974,3638,4335,3085,6006, +5104,5042,5166,5892,5572,6327,4356,4519,5222,5573,5333,5793,5043,6550,5639,5071, +4503,6328,6139,6551,6140,3914,3901,5372,6007,5640,4728,4793,3976,3836,4885,6552, +4127,6553,4451,4102,5002,6554,3686,5105,6555,5191,5072,5295,4611,5794,5296,6556, +5893,5264,5894,4975,5466,5265,4699,4976,4370,4056,3492,5044,4886,6557,5795,4432, +4769,4357,5467,3940,4660,4290,6141,4484,4770,4661,3992,6329,4025,4662,5022,4632, +4835,4070,5297,4663,4596,5574,5132,5409,5895,6142,4504,5192,4664,5796,5896,3885, +5575,5797,5023,4810,5798,3732,5223,4712,5298,4084,5334,5468,6143,4052,4053,4336, +4977,4794,6558,5335,4908,5576,5224,4233,5024,4128,5469,5225,4873,6008,5045,4729, +4742,4633,3675,4597,6559,5897,5133,5577,5003,5641,5719,6330,6560,3017,2382,3854, +4406,4811,6331,4393,3964,4946,6561,2420,3722,6562,4926,4378,3247,1736,4442,6332, +5134,6333,5226,3996,2918,5470,4319,4003,4598,4743,4744,4485,3785,3902,5167,5004, +5373,4394,5898,6144,4874,1793,3997,6334,4085,4214,5106,5642,4909,5799,6009,4419, +4189,3330,5899,4165,4420,5299,5720,5227,3347,6145,4081,6335,2876,3930,6146,3293, +3786,3910,3998,5900,5300,5578,2840,6563,5901,5579,6147,3531,5374,6564,6565,5580, +4759,5375,6566,6148,3559,5643,6336,6010,5517,6337,6338,5721,5902,3873,6011,6339, +6567,5518,3868,3649,5722,6568,4771,4947,6569,6149,4812,6570,2853,5471,6340,6341, +5644,4795,6342,6012,5723,6343,5724,6013,4349,6344,3160,6150,5193,4599,4514,4493, +5168,4320,6345,4927,3666,4745,5169,5903,5005,4928,6346,5725,6014,4730,4203,5046, +4948,3395,5170,6015,4150,6016,5726,5519,6347,5047,3550,6151,6348,4197,4310,5904, +6571,5581,2965,6152,4978,3960,4291,5135,6572,5301,5727,4129,4026,5905,4853,5728, +5472,6153,6349,4533,2700,4505,5336,4678,3583,5073,2994,4486,3043,4554,5520,6350, +6017,5800,4487,6351,3931,4103,5376,6352,4011,4321,4311,4190,5136,6018,3988,3233, +4350,5906,5645,4198,6573,5107,3432,4191,3435,5582,6574,4139,5410,6353,5411,3944, +5583,5074,3198,6575,6354,4358,6576,5302,4600,5584,5194,5412,6577,6578,5585,5413, +5303,4248,5414,3879,4433,6579,4479,5025,4854,5415,6355,4760,4772,3683,2978,4700, +3797,4452,3965,3932,3721,4910,5801,6580,5195,3551,5907,3221,3471,3029,6019,3999, +5908,5909,5266,5267,3444,3023,3828,3170,4796,5646,4979,4259,6356,5647,5337,3694, +6357,5648,5338,4520,4322,5802,3031,3759,4071,6020,5586,4836,4386,5048,6581,3571, +4679,4174,4949,6154,4813,3787,3402,3822,3958,3215,3552,5268,4387,3933,4950,4359, +6021,5910,5075,3579,6358,4234,4566,5521,6359,3613,5049,6022,5911,3375,3702,3178, +4911,5339,4521,6582,6583,4395,3087,3811,5377,6023,6360,6155,4027,5171,5649,4421, +4249,2804,6584,2270,6585,4000,4235,3045,6156,5137,5729,4140,4312,3886,6361,4330, +6157,4215,6158,3500,3676,4929,4331,3713,4930,5912,4265,3776,3368,5587,4470,4855, +3038,4980,3631,6159,6160,4132,4680,6161,6362,3923,4379,5588,4255,6586,4121,6587, +6363,4649,6364,3288,4773,4774,6162,6024,6365,3543,6588,4274,3107,3737,5050,5803, +4797,4522,5589,5051,5730,3714,4887,5378,4001,4523,6163,5026,5522,4701,4175,2791, +3760,6589,5473,4224,4133,3847,4814,4815,4775,3259,5416,6590,2738,6164,6025,5304, +3733,5076,5650,4816,5590,6591,6165,6592,3934,5269,6593,3396,5340,6594,5804,3445, +3602,4042,4488,5731,5732,3525,5591,4601,5196,6166,6026,5172,3642,4612,3202,4506, +4798,6366,3818,5108,4303,5138,5139,4776,3332,4304,2915,3415,4434,5077,5109,4856, +2879,5305,4817,6595,5913,3104,3144,3903,4634,5341,3133,5110,5651,5805,6167,4057, +5592,2945,4371,5593,6596,3474,4182,6367,6597,6168,4507,4279,6598,2822,6599,4777, +4713,5594,3829,6169,3887,5417,6170,3653,5474,6368,4216,2971,5228,3790,4579,6369, +5733,6600,6601,4951,4746,4555,6602,5418,5475,6027,3400,4665,5806,6171,4799,6028, +5052,6172,3343,4800,4747,5006,6370,4556,4217,5476,4396,5229,5379,5477,3839,5914, +5652,5807,4714,3068,4635,5808,6173,5342,4192,5078,5419,5523,5734,6174,4557,6175, +4602,6371,6176,6603,5809,6372,5735,4260,3869,5111,5230,6029,5112,6177,3126,4681, +5524,5915,2706,3563,4748,3130,6178,4018,5525,6604,6605,5478,4012,4837,6606,4534, +4193,5810,4857,3615,5479,6030,4082,3697,3539,4086,5270,3662,4508,4931,5916,4912, +5811,5027,3888,6607,4397,3527,3302,3798,2775,2921,2637,3966,4122,4388,4028,4054, +1633,4858,5079,3024,5007,3982,3412,5736,6608,3426,3236,5595,3030,6179,3427,3336, +3279,3110,6373,3874,3039,5080,5917,5140,4489,3119,6374,5812,3405,4494,6031,4666, +4141,6180,4166,6032,5813,4981,6609,5081,4422,4982,4112,3915,5653,3296,3983,6375, +4266,4410,5654,6610,6181,3436,5082,6611,5380,6033,3819,5596,4535,5231,5306,5113, +6612,4952,5918,4275,3113,6613,6376,6182,6183,5814,3073,4731,4838,5008,3831,6614, +4888,3090,3848,4280,5526,5232,3014,5655,5009,5737,5420,5527,6615,5815,5343,5173, +5381,4818,6616,3151,4953,6617,5738,2796,3204,4360,2989,4281,5739,5174,5421,5197, +3132,5141,3849,5142,5528,5083,3799,3904,4839,5480,2880,4495,3448,6377,6184,5271, +5919,3771,3193,6034,6035,5920,5010,6036,5597,6037,6378,6038,3106,5422,6618,5423, +5424,4142,6619,4889,5084,4890,4313,5740,6620,3437,5175,5307,5816,4199,5198,5529, +5817,5199,5656,4913,5028,5344,3850,6185,2955,5272,5011,5818,4567,4580,5029,5921, +3616,5233,6621,6622,6186,4176,6039,6379,6380,3352,5200,5273,2908,5598,5234,3837, +5308,6623,6624,5819,4496,4323,5309,5201,6625,6626,4983,3194,3838,4167,5530,5922, +5274,6381,6382,3860,3861,5599,3333,4292,4509,6383,3553,5481,5820,5531,4778,6187, +3955,3956,4324,4389,4218,3945,4325,3397,2681,5923,4779,5085,4019,5482,4891,5382, +5383,6040,4682,3425,5275,4094,6627,5310,3015,5483,5657,4398,5924,3168,4819,6628, +5925,6629,5532,4932,4613,6041,6630,4636,6384,4780,4204,5658,4423,5821,3989,4683, +5822,6385,4954,6631,5345,6188,5425,5012,5384,3894,6386,4490,4104,6632,5741,5053, +6633,5823,5926,5659,5660,5927,6634,5235,5742,5824,4840,4933,4820,6387,4859,5928, +4955,6388,4143,3584,5825,5346,5013,6635,5661,6389,5014,5484,5743,4337,5176,5662, +6390,2836,6391,3268,6392,6636,6042,5236,6637,4158,6638,5744,5663,4471,5347,3663, +4123,5143,4293,3895,6639,6640,5311,5929,5826,3800,6189,6393,6190,5664,5348,3554, +3594,4749,4603,6641,5385,4801,6043,5827,4183,6642,5312,5426,4761,6394,5665,6191, +4715,2669,6643,6644,5533,3185,5427,5086,5930,5931,5386,6192,6044,6645,4781,4013, +5745,4282,4435,5534,4390,4267,6045,5746,4984,6046,2743,6193,3501,4087,5485,5932, +5428,4184,4095,5747,4061,5054,3058,3862,5933,5600,6646,5144,3618,6395,3131,5055, +5313,6396,4650,4956,3855,6194,3896,5202,4985,4029,4225,6195,6647,5828,5486,5829, +3589,3002,6648,6397,4782,5276,6649,6196,6650,4105,3803,4043,5237,5830,6398,4096, +3643,6399,3528,6651,4453,3315,4637,6652,3984,6197,5535,3182,3339,6653,3096,2660, +6400,6654,3449,5934,4250,4236,6047,6401,5831,6655,5487,3753,4062,5832,6198,6199, +6656,3766,6657,3403,4667,6048,6658,4338,2897,5833,3880,2797,3780,4326,6659,5748, +5015,6660,5387,4351,5601,4411,6661,3654,4424,5935,4339,4072,5277,4568,5536,6402, +6662,5238,6663,5349,5203,6200,5204,6201,5145,4536,5016,5056,4762,5834,4399,4957, +6202,6403,5666,5749,6664,4340,6665,5936,5177,5667,6666,6667,3459,4668,6404,6668, +6669,4543,6203,6670,4276,6405,4480,5537,6671,4614,5205,5668,6672,3348,2193,4763, +6406,6204,5937,5602,4177,5669,3419,6673,4020,6205,4443,4569,5388,3715,3639,6407, +6049,4058,6206,6674,5938,4544,6050,4185,4294,4841,4651,4615,5488,6207,6408,6051, +5178,3241,3509,5835,6208,4958,5836,4341,5489,5278,6209,2823,5538,5350,5206,5429, +6675,4638,4875,4073,3516,4684,4914,4860,5939,5603,5389,6052,5057,3237,5490,3791, +6676,6409,6677,4821,4915,4106,5351,5058,4243,5539,4244,5604,4842,4916,5239,3028, +3716,5837,5114,5605,5390,5940,5430,6210,4332,6678,5540,4732,3667,3840,6053,4305, +3408,5670,5541,6410,2744,5240,5750,6679,3234,5606,6680,5607,5671,3608,4283,4159, +4400,5352,4783,6681,6411,6682,4491,4802,6211,6412,5941,6413,6414,5542,5751,6683, +4669,3734,5942,6684,6415,5943,5059,3328,4670,4144,4268,6685,6686,6687,6688,4372, +3603,6689,5944,5491,4373,3440,6416,5543,4784,4822,5608,3792,4616,5838,5672,3514, +5391,6417,4892,6690,4639,6691,6054,5673,5839,6055,6692,6056,5392,6212,4038,5544, +5674,4497,6057,6693,5840,4284,5675,4021,4545,5609,6418,4454,6419,6213,4113,4472, +5314,3738,5087,5279,4074,5610,4959,4063,3179,4750,6058,6420,6214,3476,4498,4716, +5431,4960,4685,6215,5241,6694,6421,6216,6695,5841,5945,6422,3748,5946,5179,3905, +5752,5545,5947,4374,6217,4455,6423,4412,6218,4803,5353,6696,3832,5280,6219,4327, +4702,6220,6221,6059,4652,5432,6424,3749,4751,6425,5753,4986,5393,4917,5948,5030, +5754,4861,4733,6426,4703,6697,6222,4671,5949,4546,4961,5180,6223,5031,3316,5281, +6698,4862,4295,4934,5207,3644,6427,5842,5950,6428,6429,4570,5843,5282,6430,6224, +5088,3239,6060,6699,5844,5755,6061,6431,2701,5546,6432,5115,5676,4039,3993,3327, +4752,4425,5315,6433,3941,6434,5677,4617,4604,3074,4581,6225,5433,6435,6226,6062, +4823,5756,5116,6227,3717,5678,4717,5845,6436,5679,5846,6063,5847,6064,3977,3354, +6437,3863,5117,6228,5547,5394,4499,4524,6229,4605,6230,4306,4500,6700,5951,6065, +3693,5952,5089,4366,4918,6701,6231,5548,6232,6702,6438,4704,5434,6703,6704,5953, +4168,6705,5680,3420,6706,5242,4407,6066,3812,5757,5090,5954,4672,4525,3481,5681, +4618,5395,5354,5316,5955,6439,4962,6707,4526,6440,3465,4673,6067,6441,5682,6708, +5435,5492,5758,5683,4619,4571,4674,4804,4893,4686,5493,4753,6233,6068,4269,6442, +6234,5032,4705,5146,5243,5208,5848,6235,6443,4963,5033,4640,4226,6236,5849,3387, +6444,6445,4436,4437,5850,4843,5494,4785,4894,6709,4361,6710,5091,5956,3331,6237, +4987,5549,6069,6711,4342,3517,4473,5317,6070,6712,6071,4706,6446,5017,5355,6713, +6714,4988,5436,6447,4734,5759,6715,4735,4547,4456,4754,6448,5851,6449,6450,3547, +5852,5318,6451,6452,5092,4205,6716,6238,4620,4219,5611,6239,6072,4481,5760,5957, +5958,4059,6240,6453,4227,4537,6241,5761,4030,4186,5244,5209,3761,4457,4876,3337, +5495,5181,6242,5959,5319,5612,5684,5853,3493,5854,6073,4169,5613,5147,4895,6074, +5210,6717,5182,6718,3830,6243,2798,3841,6075,6244,5855,5614,3604,4606,5496,5685, +5118,5356,6719,6454,5960,5357,5961,6720,4145,3935,4621,5119,5962,4261,6721,6455, +4786,5963,4375,4582,6245,6246,6247,6076,5437,4877,5856,3376,4380,6248,4160,6722, +5148,6456,5211,6457,6723,4718,6458,6724,6249,5358,4044,3297,6459,6250,5857,5615, +5497,5245,6460,5498,6725,6251,6252,5550,3793,5499,2959,5396,6461,6462,4572,5093, +5500,5964,3806,4146,6463,4426,5762,5858,6077,6253,4755,3967,4220,5965,6254,4989, +5501,6464,4352,6726,6078,4764,2290,5246,3906,5438,5283,3767,4964,2861,5763,5094, +6255,6256,4622,5616,5859,5860,4707,6727,4285,4708,4824,5617,6257,5551,4787,5212, +4965,4935,4687,6465,6728,6466,5686,6079,3494,4413,2995,5247,5966,5618,6729,5967, +5764,5765,5687,5502,6730,6731,6080,5397,6467,4990,6258,6732,4538,5060,5619,6733, +4719,5688,5439,5018,5149,5284,5503,6734,6081,4607,6259,5120,3645,5861,4583,6260, +4584,4675,5620,4098,5440,6261,4863,2379,3306,4585,5552,5689,4586,5285,6735,4864, +6736,5286,6082,6737,4623,3010,4788,4381,4558,5621,4587,4896,3698,3161,5248,4353, +4045,6262,3754,5183,4588,6738,6263,6739,6740,5622,3936,6741,6468,6742,6264,5095, +6469,4991,5968,6743,4992,6744,6083,4897,6745,4256,5766,4307,3108,3968,4444,5287, +3889,4343,6084,4510,6085,4559,6086,4898,5969,6746,5623,5061,4919,5249,5250,5504, +5441,6265,5320,4878,3242,5862,5251,3428,6087,6747,4237,5624,5442,6266,5553,4539, +6748,2585,3533,5398,4262,6088,5150,4736,4438,6089,6267,5505,4966,6749,6268,6750, +6269,5288,5554,3650,6090,6091,4624,6092,5690,6751,5863,4270,5691,4277,5555,5864, +6752,5692,4720,4865,6470,5151,4688,4825,6753,3094,6754,6471,3235,4653,6755,5213, +5399,6756,3201,4589,5865,4967,6472,5866,6473,5019,3016,6757,5321,4756,3957,4573, +6093,4993,5767,4721,6474,6758,5625,6759,4458,6475,6270,6760,5556,4994,5214,5252, +6271,3875,5768,6094,5034,5506,4376,5769,6761,2120,6476,5253,5770,6762,5771,5970, +3990,5971,5557,5558,5772,6477,6095,2787,4641,5972,5121,6096,6097,6272,6763,3703, +5867,5507,6273,4206,6274,4789,6098,6764,3619,3646,3833,3804,2394,3788,4936,3978, +4866,4899,6099,6100,5559,6478,6765,3599,5868,6101,5869,5870,6275,6766,4527,6767) + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/gb2312prober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/gb2312prober.py new file mode 100644 index 0000000..0325a2d --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/gb2312prober.py @@ -0,0 +1,41 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import GB2312DistributionAnalysis +from .mbcssm import GB2312SMModel + +class GB2312Prober(MultiByteCharSetProber): + def __init__(self): + MultiByteCharSetProber.__init__(self) + self._mCodingSM = CodingStateMachine(GB2312SMModel) + self._mDistributionAnalyzer = GB2312DistributionAnalysis() + self.reset() + + def get_charset_name(self): + return "GB2312" diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/hebrewprober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/hebrewprober.py new file mode 100644 index 0000000..ba225c5 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/hebrewprober.py @@ -0,0 +1,283 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Shy Shalom +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .constants import eNotMe, eDetecting +from .compat import wrap_ord + +# This prober doesn't actually recognize a language or a charset. +# It is a helper prober for the use of the Hebrew model probers + +### General ideas of the Hebrew charset recognition ### +# +# Four main charsets exist in Hebrew: +# "ISO-8859-8" - Visual Hebrew +# "windows-1255" - Logical Hebrew +# "ISO-8859-8-I" - Logical Hebrew +# "x-mac-hebrew" - ?? Logical Hebrew ?? +# +# Both "ISO" charsets use a completely identical set of code points, whereas +# "windows-1255" and "x-mac-hebrew" are two different proper supersets of +# these code points. windows-1255 defines additional characters in the range +# 0x80-0x9F as some misc punctuation marks as well as some Hebrew-specific +# diacritics and additional 'Yiddish' ligature letters in the range 0xc0-0xd6. +# x-mac-hebrew defines similar additional code points but with a different +# mapping. +# +# As far as an average Hebrew text with no diacritics is concerned, all four +# charsets are identical with respect to code points. Meaning that for the +# main Hebrew alphabet, all four map the same values to all 27 Hebrew letters +# (including final letters). +# +# The dominant difference between these charsets is their directionality. +# "Visual" directionality means that the text is ordered as if the renderer is +# not aware of a BIDI rendering algorithm. The renderer sees the text and +# draws it from left to right. The text itself when ordered naturally is read +# backwards. A buffer of Visual Hebrew generally looks like so: +# "[last word of first line spelled backwards] [whole line ordered backwards +# and spelled backwards] [first word of first line spelled backwards] +# [end of line] [last word of second line] ... etc' " +# adding punctuation marks, numbers and English text to visual text is +# naturally also "visual" and from left to right. +# +# "Logical" directionality means the text is ordered "naturally" according to +# the order it is read. It is the responsibility of the renderer to display +# the text from right to left. A BIDI algorithm is used to place general +# punctuation marks, numbers and English text in the text. +# +# Texts in x-mac-hebrew are almost impossible to find on the Internet. From +# what little evidence I could find, it seems that its general directionality +# is Logical. +# +# To sum up all of the above, the Hebrew probing mechanism knows about two +# charsets: +# Visual Hebrew - "ISO-8859-8" - backwards text - Words and sentences are +# backwards while line order is natural. For charset recognition purposes +# the line order is unimportant (In fact, for this implementation, even +# word order is unimportant). +# Logical Hebrew - "windows-1255" - normal, naturally ordered text. +# +# "ISO-8859-8-I" is a subset of windows-1255 and doesn't need to be +# specifically identified. +# "x-mac-hebrew" is also identified as windows-1255. A text in x-mac-hebrew +# that contain special punctuation marks or diacritics is displayed with +# some unconverted characters showing as question marks. This problem might +# be corrected using another model prober for x-mac-hebrew. Due to the fact +# that x-mac-hebrew texts are so rare, writing another model prober isn't +# worth the effort and performance hit. +# +#### The Prober #### +# +# The prober is divided between two SBCharSetProbers and a HebrewProber, +# all of which are managed, created, fed data, inquired and deleted by the +# SBCSGroupProber. The two SBCharSetProbers identify that the text is in +# fact some kind of Hebrew, Logical or Visual. The final decision about which +# one is it is made by the HebrewProber by combining final-letter scores +# with the scores of the two SBCharSetProbers to produce a final answer. +# +# The SBCSGroupProber is responsible for stripping the original text of HTML +# tags, English characters, numbers, low-ASCII punctuation characters, spaces +# and new lines. It reduces any sequence of such characters to a single space. +# The buffer fed to each prober in the SBCS group prober is pure text in +# high-ASCII. +# The two SBCharSetProbers (model probers) share the same language model: +# Win1255Model. +# The first SBCharSetProber uses the model normally as any other +# SBCharSetProber does, to recognize windows-1255, upon which this model was +# built. The second SBCharSetProber is told to make the pair-of-letter +# lookup in the language model backwards. This in practice exactly simulates +# a visual Hebrew model using the windows-1255 logical Hebrew model. +# +# The HebrewProber is not using any language model. All it does is look for +# final-letter evidence suggesting the text is either logical Hebrew or visual +# Hebrew. Disjointed from the model probers, the results of the HebrewProber +# alone are meaningless. HebrewProber always returns 0.00 as confidence +# since it never identifies a charset by itself. Instead, the pointer to the +# HebrewProber is passed to the model probers as a helper "Name Prober". +# When the Group prober receives a positive identification from any prober, +# it asks for the name of the charset identified. If the prober queried is a +# Hebrew model prober, the model prober forwards the call to the +# HebrewProber to make the final decision. In the HebrewProber, the +# decision is made according to the final-letters scores maintained and Both +# model probers scores. The answer is returned in the form of the name of the +# charset identified, either "windows-1255" or "ISO-8859-8". + +# windows-1255 / ISO-8859-8 code points of interest +FINAL_KAF = 0xea +NORMAL_KAF = 0xeb +FINAL_MEM = 0xed +NORMAL_MEM = 0xee +FINAL_NUN = 0xef +NORMAL_NUN = 0xf0 +FINAL_PE = 0xf3 +NORMAL_PE = 0xf4 +FINAL_TSADI = 0xf5 +NORMAL_TSADI = 0xf6 + +# Minimum Visual vs Logical final letter score difference. +# If the difference is below this, don't rely solely on the final letter score +# distance. +MIN_FINAL_CHAR_DISTANCE = 5 + +# Minimum Visual vs Logical model score difference. +# If the difference is below this, don't rely at all on the model score +# distance. +MIN_MODEL_DISTANCE = 0.01 + +VISUAL_HEBREW_NAME = "ISO-8859-8" +LOGICAL_HEBREW_NAME = "windows-1255" + + +class HebrewProber(CharSetProber): + def __init__(self): + CharSetProber.__init__(self) + self._mLogicalProber = None + self._mVisualProber = None + self.reset() + + def reset(self): + self._mFinalCharLogicalScore = 0 + self._mFinalCharVisualScore = 0 + # The two last characters seen in the previous buffer, + # mPrev and mBeforePrev are initialized to space in order to simulate + # a word delimiter at the beginning of the data + self._mPrev = ' ' + self._mBeforePrev = ' ' + # These probers are owned by the group prober. + + def set_model_probers(self, logicalProber, visualProber): + self._mLogicalProber = logicalProber + self._mVisualProber = visualProber + + def is_final(self, c): + return wrap_ord(c) in [FINAL_KAF, FINAL_MEM, FINAL_NUN, FINAL_PE, + FINAL_TSADI] + + def is_non_final(self, c): + # The normal Tsadi is not a good Non-Final letter due to words like + # 'lechotet' (to chat) containing an apostrophe after the tsadi. This + # apostrophe is converted to a space in FilterWithoutEnglishLetters + # causing the Non-Final tsadi to appear at an end of a word even + # though this is not the case in the original text. + # The letters Pe and Kaf rarely display a related behavior of not being + # a good Non-Final letter. Words like 'Pop', 'Winamp' and 'Mubarak' + # for example legally end with a Non-Final Pe or Kaf. However, the + # benefit of these letters as Non-Final letters outweighs the damage + # since these words are quite rare. + return wrap_ord(c) in [NORMAL_KAF, NORMAL_MEM, NORMAL_NUN, NORMAL_PE] + + def feed(self, aBuf): + # Final letter analysis for logical-visual decision. + # Look for evidence that the received buffer is either logical Hebrew + # or visual Hebrew. + # The following cases are checked: + # 1) A word longer than 1 letter, ending with a final letter. This is + # an indication that the text is laid out "naturally" since the + # final letter really appears at the end. +1 for logical score. + # 2) A word longer than 1 letter, ending with a Non-Final letter. In + # normal Hebrew, words ending with Kaf, Mem, Nun, Pe or Tsadi, + # should not end with the Non-Final form of that letter. Exceptions + # to this rule are mentioned above in isNonFinal(). This is an + # indication that the text is laid out backwards. +1 for visual + # score + # 3) A word longer than 1 letter, starting with a final letter. Final + # letters should not appear at the beginning of a word. This is an + # indication that the text is laid out backwards. +1 for visual + # score. + # + # The visual score and logical score are accumulated throughout the + # text and are finally checked against each other in GetCharSetName(). + # No checking for final letters in the middle of words is done since + # that case is not an indication for either Logical or Visual text. + # + # We automatically filter out all 7-bit characters (replace them with + # spaces) so the word boundary detection works properly. [MAP] + + if self.get_state() == eNotMe: + # Both model probers say it's not them. No reason to continue. + return eNotMe + + aBuf = self.filter_high_bit_only(aBuf) + + for cur in aBuf: + if cur == ' ': + # We stand on a space - a word just ended + if self._mBeforePrev != ' ': + # next-to-last char was not a space so self._mPrev is not a + # 1 letter word + if self.is_final(self._mPrev): + # case (1) [-2:not space][-1:final letter][cur:space] + self._mFinalCharLogicalScore += 1 + elif self.is_non_final(self._mPrev): + # case (2) [-2:not space][-1:Non-Final letter][ + # cur:space] + self._mFinalCharVisualScore += 1 + else: + # Not standing on a space + if ((self._mBeforePrev == ' ') and + (self.is_final(self._mPrev)) and (cur != ' ')): + # case (3) [-2:space][-1:final letter][cur:not space] + self._mFinalCharVisualScore += 1 + self._mBeforePrev = self._mPrev + self._mPrev = cur + + # Forever detecting, till the end or until both model probers return + # eNotMe (handled above) + return eDetecting + + def get_charset_name(self): + # Make the decision: is it Logical or Visual? + # If the final letter score distance is dominant enough, rely on it. + finalsub = self._mFinalCharLogicalScore - self._mFinalCharVisualScore + if finalsub >= MIN_FINAL_CHAR_DISTANCE: + return LOGICAL_HEBREW_NAME + if finalsub <= -MIN_FINAL_CHAR_DISTANCE: + return VISUAL_HEBREW_NAME + + # It's not dominant enough, try to rely on the model scores instead. + modelsub = (self._mLogicalProber.get_confidence() + - self._mVisualProber.get_confidence()) + if modelsub > MIN_MODEL_DISTANCE: + return LOGICAL_HEBREW_NAME + if modelsub < -MIN_MODEL_DISTANCE: + return VISUAL_HEBREW_NAME + + # Still no good, back to final letter distance, maybe it'll save the + # day. + if finalsub < 0.0: + return VISUAL_HEBREW_NAME + + # (finalsub > 0 - Logical) or (don't know what to do) default to + # Logical. + return LOGICAL_HEBREW_NAME + + def get_state(self): + # Remain active as long as any of the model probers are active. + if (self._mLogicalProber.get_state() == eNotMe) and \ + (self._mVisualProber.get_state() == eNotMe): + return eNotMe + return eDetecting diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/jisfreq.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/jisfreq.py new file mode 100644 index 0000000..064345b --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/jisfreq.py @@ -0,0 +1,569 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Sampling from about 20M text materials include literature and computer technology +# +# Japanese frequency table, applied to both S-JIS and EUC-JP +# They are sorted in order. + +# 128 --> 0.77094 +# 256 --> 0.85710 +# 512 --> 0.92635 +# 1024 --> 0.97130 +# 2048 --> 0.99431 +# +# Ideal Distribution Ratio = 0.92635 / (1-0.92635) = 12.58 +# Random Distribution Ration = 512 / (2965+62+83+86-512) = 0.191 +# +# Typical Distribution Ratio, 25% of IDR + +JIS_TYPICAL_DISTRIBUTION_RATIO = 3.0 + +# Char to FreqOrder table , +JIS_TABLE_SIZE = 4368 + +JISCharToFreqOrder = ( + 40, 1, 6, 182, 152, 180, 295,2127, 285, 381,3295,4304,3068,4606,3165,3510, # 16 +3511,1822,2785,4607,1193,2226,5070,4608, 171,2996,1247, 18, 179,5071, 856,1661, # 32 +1262,5072, 619, 127,3431,3512,3230,1899,1700, 232, 228,1294,1298, 284, 283,2041, # 48 +2042,1061,1062, 48, 49, 44, 45, 433, 434,1040,1041, 996, 787,2997,1255,4305, # 64 +2108,4609,1684,1648,5073,5074,5075,5076,5077,5078,3687,5079,4610,5080,3927,3928, # 80 +5081,3296,3432, 290,2285,1471,2187,5082,2580,2825,1303,2140,1739,1445,2691,3375, # 96 +1691,3297,4306,4307,4611, 452,3376,1182,2713,3688,3069,4308,5083,5084,5085,5086, # 112 +5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102, # 128 +5103,5104,5105,5106,5107,5108,5109,5110,5111,5112,4097,5113,5114,5115,5116,5117, # 144 +5118,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,5130,5131,5132,5133, # 160 +5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149, # 176 +5150,5151,5152,4612,5153,5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164, # 192 +5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,1472, 598, 618, 820,1205, # 208 +1309,1412,1858,1307,1692,5176,5177,5178,5179,5180,5181,5182,1142,1452,1234,1172, # 224 +1875,2043,2149,1793,1382,2973, 925,2404,1067,1241, 960,1377,2935,1491, 919,1217, # 240 +1865,2030,1406,1499,2749,4098,5183,5184,5185,5186,5187,5188,2561,4099,3117,1804, # 256 +2049,3689,4309,3513,1663,5189,3166,3118,3298,1587,1561,3433,5190,3119,1625,2998, # 272 +3299,4613,1766,3690,2786,4614,5191,5192,5193,5194,2161, 26,3377, 2,3929, 20, # 288 +3691, 47,4100, 50, 17, 16, 35, 268, 27, 243, 42, 155, 24, 154, 29, 184, # 304 + 4, 91, 14, 92, 53, 396, 33, 289, 9, 37, 64, 620, 21, 39, 321, 5, # 320 + 12, 11, 52, 13, 3, 208, 138, 0, 7, 60, 526, 141, 151,1069, 181, 275, # 336 +1591, 83, 132,1475, 126, 331, 829, 15, 69, 160, 59, 22, 157, 55,1079, 312, # 352 + 109, 38, 23, 25, 10, 19, 79,5195, 61, 382,1124, 8, 30,5196,5197,5198, # 368 +5199,5200,5201,5202,5203,5204,5205,5206, 89, 62, 74, 34,2416, 112, 139, 196, # 384 + 271, 149, 84, 607, 131, 765, 46, 88, 153, 683, 76, 874, 101, 258, 57, 80, # 400 + 32, 364, 121,1508, 169,1547, 68, 235, 145,2999, 41, 360,3027, 70, 63, 31, # 416 + 43, 259, 262,1383, 99, 533, 194, 66, 93, 846, 217, 192, 56, 106, 58, 565, # 432 + 280, 272, 311, 256, 146, 82, 308, 71, 100, 128, 214, 655, 110, 261, 104,1140, # 448 + 54, 51, 36, 87, 67,3070, 185,2618,2936,2020, 28,1066,2390,2059,5207,5208, # 464 +5209,5210,5211,5212,5213,5214,5215,5216,4615,5217,5218,5219,5220,5221,5222,5223, # 480 +5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234,5235,5236,3514,5237,5238, # 496 +5239,5240,5241,5242,5243,5244,2297,2031,4616,4310,3692,5245,3071,5246,3598,5247, # 512 +4617,3231,3515,5248,4101,4311,4618,3808,4312,4102,5249,4103,4104,3599,5250,5251, # 528 +5252,5253,5254,5255,5256,5257,5258,5259,5260,5261,5262,5263,5264,5265,5266,5267, # 544 +5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278,5279,5280,5281,5282,5283, # 560 +5284,5285,5286,5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299, # 576 +5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315, # 592 +5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331, # 608 +5332,5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347, # 624 +5348,5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363, # 640 +5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379, # 656 +5380,5381, 363, 642,2787,2878,2788,2789,2316,3232,2317,3434,2011, 165,1942,3930, # 672 +3931,3932,3933,5382,4619,5383,4620,5384,5385,5386,5387,5388,5389,5390,5391,5392, # 688 +5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408, # 704 +5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424, # 720 +5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,5439,5440, # 736 +5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456, # 752 +5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472, # 768 +5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487,5488, # 784 +5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504, # 800 +5505,5506,5507,5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520, # 816 +5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536, # 832 +5537,5538,5539,5540,5541,5542,5543,5544,5545,5546,5547,5548,5549,5550,5551,5552, # 848 +5553,5554,5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568, # 864 +5569,5570,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584, # 880 +5585,5586,5587,5588,5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600, # 896 +5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616, # 912 +5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631,5632, # 928 +5633,5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648, # 944 +5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664, # 960 +5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680, # 976 +5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696, # 992 +5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712, # 1008 +5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728, # 1024 +5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744, # 1040 +5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,5760, # 1056 +5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776, # 1072 +5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5787,5788,5789,5790,5791,5792, # 1088 +5793,5794,5795,5796,5797,5798,5799,5800,5801,5802,5803,5804,5805,5806,5807,5808, # 1104 +5809,5810,5811,5812,5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824, # 1120 +5825,5826,5827,5828,5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840, # 1136 +5841,5842,5843,5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856, # 1152 +5857,5858,5859,5860,5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872, # 1168 +5873,5874,5875,5876,5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888, # 1184 +5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904, # 1200 +5905,5906,5907,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920, # 1216 +5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936, # 1232 +5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952, # 1248 +5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968, # 1264 +5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984, # 1280 +5985,5986,5987,5988,5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000, # 1296 +6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016, # 1312 +6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032, # 1328 +6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048, # 1344 +6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064, # 1360 +6065,6066,6067,6068,6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080, # 1376 +6081,6082,6083,6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096, # 1392 +6097,6098,6099,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112, # 1408 +6113,6114,2044,2060,4621, 997,1235, 473,1186,4622, 920,3378,6115,6116, 379,1108, # 1424 +4313,2657,2735,3934,6117,3809, 636,3233, 573,1026,3693,3435,2974,3300,2298,4105, # 1440 + 854,2937,2463, 393,2581,2417, 539, 752,1280,2750,2480, 140,1161, 440, 708,1569, # 1456 + 665,2497,1746,1291,1523,3000, 164,1603, 847,1331, 537,1997, 486, 508,1693,2418, # 1472 +1970,2227, 878,1220, 299,1030, 969, 652,2751, 624,1137,3301,2619, 65,3302,2045, # 1488 +1761,1859,3120,1930,3694,3516, 663,1767, 852, 835,3695, 269, 767,2826,2339,1305, # 1504 + 896,1150, 770,1616,6118, 506,1502,2075,1012,2519, 775,2520,2975,2340,2938,4314, # 1520 +3028,2086,1224,1943,2286,6119,3072,4315,2240,1273,1987,3935,1557, 175, 597, 985, # 1536 +3517,2419,2521,1416,3029, 585, 938,1931,1007,1052,1932,1685,6120,3379,4316,4623, # 1552 + 804, 599,3121,1333,2128,2539,1159,1554,2032,3810, 687,2033,2904, 952, 675,1467, # 1568 +3436,6121,2241,1096,1786,2440,1543,1924, 980,1813,2228, 781,2692,1879, 728,1918, # 1584 +3696,4624, 548,1950,4625,1809,1088,1356,3303,2522,1944, 502, 972, 373, 513,2827, # 1600 + 586,2377,2391,1003,1976,1631,6122,2464,1084, 648,1776,4626,2141, 324, 962,2012, # 1616 +2177,2076,1384, 742,2178,1448,1173,1810, 222, 102, 301, 445, 125,2420, 662,2498, # 1632 + 277, 200,1476,1165,1068, 224,2562,1378,1446, 450,1880, 659, 791, 582,4627,2939, # 1648 +3936,1516,1274, 555,2099,3697,1020,1389,1526,3380,1762,1723,1787,2229, 412,2114, # 1664 +1900,2392,3518, 512,2597, 427,1925,2341,3122,1653,1686,2465,2499, 697, 330, 273, # 1680 + 380,2162, 951, 832, 780, 991,1301,3073, 965,2270,3519, 668,2523,2636,1286, 535, # 1696 +1407, 518, 671, 957,2658,2378, 267, 611,2197,3030,6123, 248,2299, 967,1799,2356, # 1712 + 850,1418,3437,1876,1256,1480,2828,1718,6124,6125,1755,1664,2405,6126,4628,2879, # 1728 +2829, 499,2179, 676,4629, 557,2329,2214,2090, 325,3234, 464, 811,3001, 992,2342, # 1744 +2481,1232,1469, 303,2242, 466,1070,2163, 603,1777,2091,4630,2752,4631,2714, 322, # 1760 +2659,1964,1768, 481,2188,1463,2330,2857,3600,2092,3031,2421,4632,2318,2070,1849, # 1776 +2598,4633,1302,2254,1668,1701,2422,3811,2905,3032,3123,2046,4106,1763,1694,4634, # 1792 +1604, 943,1724,1454, 917, 868,2215,1169,2940, 552,1145,1800,1228,1823,1955, 316, # 1808 +1080,2510, 361,1807,2830,4107,2660,3381,1346,1423,1134,4108,6127, 541,1263,1229, # 1824 +1148,2540, 545, 465,1833,2880,3438,1901,3074,2482, 816,3937, 713,1788,2500, 122, # 1840 +1575, 195,1451,2501,1111,6128, 859, 374,1225,2243,2483,4317, 390,1033,3439,3075, # 1856 +2524,1687, 266, 793,1440,2599, 946, 779, 802, 507, 897,1081, 528,2189,1292, 711, # 1872 +1866,1725,1167,1640, 753, 398,2661,1053, 246, 348,4318, 137,1024,3440,1600,2077, # 1888 +2129, 825,4319, 698, 238, 521, 187,2300,1157,2423,1641,1605,1464,1610,1097,2541, # 1904 +1260,1436, 759,2255,1814,2150, 705,3235, 409,2563,3304, 561,3033,2005,2564, 726, # 1920 +1956,2343,3698,4109, 949,3812,3813,3520,1669, 653,1379,2525, 881,2198, 632,2256, # 1936 +1027, 778,1074, 733,1957, 514,1481,2466, 554,2180, 702,3938,1606,1017,1398,6129, # 1952 +1380,3521, 921, 993,1313, 594, 449,1489,1617,1166, 768,1426,1360, 495,1794,3601, # 1968 +1177,3602,1170,4320,2344, 476, 425,3167,4635,3168,1424, 401,2662,1171,3382,1998, # 1984 +1089,4110, 477,3169, 474,6130,1909, 596,2831,1842, 494, 693,1051,1028,1207,3076, # 2000 + 606,2115, 727,2790,1473,1115, 743,3522, 630, 805,1532,4321,2021, 366,1057, 838, # 2016 + 684,1114,2142,4322,2050,1492,1892,1808,2271,3814,2424,1971,1447,1373,3305,1090, # 2032 +1536,3939,3523,3306,1455,2199, 336, 369,2331,1035, 584,2393, 902, 718,2600,6131, # 2048 +2753, 463,2151,1149,1611,2467, 715,1308,3124,1268, 343,1413,3236,1517,1347,2663, # 2064 +2093,3940,2022,1131,1553,2100,2941,1427,3441,2942,1323,2484,6132,1980, 872,2368, # 2080 +2441,2943, 320,2369,2116,1082, 679,1933,3941,2791,3815, 625,1143,2023, 422,2200, # 2096 +3816,6133, 730,1695, 356,2257,1626,2301,2858,2637,1627,1778, 937, 883,2906,2693, # 2112 +3002,1769,1086, 400,1063,1325,3307,2792,4111,3077, 456,2345,1046, 747,6134,1524, # 2128 + 884,1094,3383,1474,2164,1059, 974,1688,2181,2258,1047, 345,1665,1187, 358, 875, # 2144 +3170, 305, 660,3524,2190,1334,1135,3171,1540,1649,2542,1527, 927, 968,2793, 885, # 2160 +1972,1850, 482, 500,2638,1218,1109,1085,2543,1654,2034, 876, 78,2287,1482,1277, # 2176 + 861,1675,1083,1779, 724,2754, 454, 397,1132,1612,2332, 893, 672,1237, 257,2259, # 2192 +2370, 135,3384, 337,2244, 547, 352, 340, 709,2485,1400, 788,1138,2511, 540, 772, # 2208 +1682,2260,2272,2544,2013,1843,1902,4636,1999,1562,2288,4637,2201,1403,1533, 407, # 2224 + 576,3308,1254,2071, 978,3385, 170, 136,1201,3125,2664,3172,2394, 213, 912, 873, # 2240 +3603,1713,2202, 699,3604,3699, 813,3442, 493, 531,1054, 468,2907,1483, 304, 281, # 2256 +4112,1726,1252,2094, 339,2319,2130,2639, 756,1563,2944, 748, 571,2976,1588,2425, # 2272 +2715,1851,1460,2426,1528,1392,1973,3237, 288,3309, 685,3386, 296, 892,2716,2216, # 2288 +1570,2245, 722,1747,2217, 905,3238,1103,6135,1893,1441,1965, 251,1805,2371,3700, # 2304 +2601,1919,1078, 75,2182,1509,1592,1270,2640,4638,2152,6136,3310,3817, 524, 706, # 2320 +1075, 292,3818,1756,2602, 317, 98,3173,3605,3525,1844,2218,3819,2502, 814, 567, # 2336 + 385,2908,1534,6137, 534,1642,3239, 797,6138,1670,1529, 953,4323, 188,1071, 538, # 2352 + 178, 729,3240,2109,1226,1374,2000,2357,2977, 731,2468,1116,2014,2051,6139,1261, # 2368 +1593, 803,2859,2736,3443, 556, 682, 823,1541,6140,1369,2289,1706,2794, 845, 462, # 2384 +2603,2665,1361, 387, 162,2358,1740, 739,1770,1720,1304,1401,3241,1049, 627,1571, # 2400 +2427,3526,1877,3942,1852,1500, 431,1910,1503, 677, 297,2795, 286,1433,1038,1198, # 2416 +2290,1133,1596,4113,4639,2469,1510,1484,3943,6141,2442, 108, 712,4640,2372, 866, # 2432 +3701,2755,3242,1348, 834,1945,1408,3527,2395,3243,1811, 824, 994,1179,2110,1548, # 2448 +1453, 790,3003, 690,4324,4325,2832,2909,3820,1860,3821, 225,1748, 310, 346,1780, # 2464 +2470, 821,1993,2717,2796, 828, 877,3528,2860,2471,1702,2165,2910,2486,1789, 453, # 2480 + 359,2291,1676, 73,1164,1461,1127,3311, 421, 604, 314,1037, 589, 116,2487, 737, # 2496 + 837,1180, 111, 244, 735,6142,2261,1861,1362, 986, 523, 418, 581,2666,3822, 103, # 2512 + 855, 503,1414,1867,2488,1091, 657,1597, 979, 605,1316,4641,1021,2443,2078,2001, # 2528 +1209, 96, 587,2166,1032, 260,1072,2153, 173, 94, 226,3244, 819,2006,4642,4114, # 2544 +2203, 231,1744, 782, 97,2667, 786,3387, 887, 391, 442,2219,4326,1425,6143,2694, # 2560 + 633,1544,1202, 483,2015, 592,2052,1958,2472,1655, 419, 129,4327,3444,3312,1714, # 2576 +1257,3078,4328,1518,1098, 865,1310,1019,1885,1512,1734, 469,2444, 148, 773, 436, # 2592 +1815,1868,1128,1055,4329,1245,2756,3445,2154,1934,1039,4643, 579,1238, 932,2320, # 2608 + 353, 205, 801, 115,2428, 944,2321,1881, 399,2565,1211, 678, 766,3944, 335,2101, # 2624 +1459,1781,1402,3945,2737,2131,1010, 844, 981,1326,1013, 550,1816,1545,2620,1335, # 2640 +1008, 371,2881, 936,1419,1613,3529,1456,1395,2273,1834,2604,1317,2738,2503, 416, # 2656 +1643,4330, 806,1126, 229, 591,3946,1314,1981,1576,1837,1666, 347,1790, 977,3313, # 2672 + 764,2861,1853, 688,2429,1920,1462, 77, 595, 415,2002,3034, 798,1192,4115,6144, # 2688 +2978,4331,3035,2695,2582,2072,2566, 430,2430,1727, 842,1396,3947,3702, 613, 377, # 2704 + 278, 236,1417,3388,3314,3174, 757,1869, 107,3530,6145,1194, 623,2262, 207,1253, # 2720 +2167,3446,3948, 492,1117,1935, 536,1838,2757,1246,4332, 696,2095,2406,1393,1572, # 2736 +3175,1782, 583, 190, 253,1390,2230, 830,3126,3389, 934,3245,1703,1749,2979,1870, # 2752 +2545,1656,2204, 869,2346,4116,3176,1817, 496,1764,4644, 942,1504, 404,1903,1122, # 2768 +1580,3606,2945,1022, 515, 372,1735, 955,2431,3036,6146,2797,1110,2302,2798, 617, # 2784 +6147, 441, 762,1771,3447,3607,3608,1904, 840,3037, 86, 939,1385, 572,1370,2445, # 2800 +1336, 114,3703, 898, 294, 203,3315, 703,1583,2274, 429, 961,4333,1854,1951,3390, # 2816 +2373,3704,4334,1318,1381, 966,1911,2322,1006,1155, 309, 989, 458,2718,1795,1372, # 2832 +1203, 252,1689,1363,3177, 517,1936, 168,1490, 562, 193,3823,1042,4117,1835, 551, # 2848 + 470,4645, 395, 489,3448,1871,1465,2583,2641, 417,1493, 279,1295, 511,1236,1119, # 2864 + 72,1231,1982,1812,3004, 871,1564, 984,3449,1667,2696,2096,4646,2347,2833,1673, # 2880 +3609, 695,3246,2668, 807,1183,4647, 890, 388,2333,1801,1457,2911,1765,1477,1031, # 2896 +3316,3317,1278,3391,2799,2292,2526, 163,3450,4335,2669,1404,1802,6148,2323,2407, # 2912 +1584,1728,1494,1824,1269, 298, 909,3318,1034,1632, 375, 776,1683,2061, 291, 210, # 2928 +1123, 809,1249,1002,2642,3038, 206,1011,2132, 144, 975, 882,1565, 342, 667, 754, # 2944 +1442,2143,1299,2303,2062, 447, 626,2205,1221,2739,2912,1144,1214,2206,2584, 760, # 2960 +1715, 614, 950,1281,2670,2621, 810, 577,1287,2546,4648, 242,2168, 250,2643, 691, # 2976 + 123,2644, 647, 313,1029, 689,1357,2946,1650, 216, 771,1339,1306, 808,2063, 549, # 2992 + 913,1371,2913,2914,6149,1466,1092,1174,1196,1311,2605,2396,1783,1796,3079, 406, # 3008 +2671,2117,3949,4649, 487,1825,2220,6150,2915, 448,2348,1073,6151,2397,1707, 130, # 3024 + 900,1598, 329, 176,1959,2527,1620,6152,2275,4336,3319,1983,2191,3705,3610,2155, # 3040 +3706,1912,1513,1614,6153,1988, 646, 392,2304,1589,3320,3039,1826,1239,1352,1340, # 3056 +2916, 505,2567,1709,1437,2408,2547, 906,6154,2672, 384,1458,1594,1100,1329, 710, # 3072 + 423,3531,2064,2231,2622,1989,2673,1087,1882, 333, 841,3005,1296,2882,2379, 580, # 3088 +1937,1827,1293,2585, 601, 574, 249,1772,4118,2079,1120, 645, 901,1176,1690, 795, # 3104 +2207, 478,1434, 516,1190,1530, 761,2080, 930,1264, 355, 435,1552, 644,1791, 987, # 3120 + 220,1364,1163,1121,1538, 306,2169,1327,1222, 546,2645, 218, 241, 610,1704,3321, # 3136 +1984,1839,1966,2528, 451,6155,2586,3707,2568, 907,3178, 254,2947, 186,1845,4650, # 3152 + 745, 432,1757, 428,1633, 888,2246,2221,2489,3611,2118,1258,1265, 956,3127,1784, # 3168 +4337,2490, 319, 510, 119, 457,3612, 274,2035,2007,4651,1409,3128, 970,2758, 590, # 3184 +2800, 661,2247,4652,2008,3950,1420,1549,3080,3322,3951,1651,1375,2111, 485,2491, # 3200 +1429,1156,6156,2548,2183,1495, 831,1840,2529,2446, 501,1657, 307,1894,3247,1341, # 3216 + 666, 899,2156,1539,2549,1559, 886, 349,2208,3081,2305,1736,3824,2170,2759,1014, # 3232 +1913,1386, 542,1397,2948, 490, 368, 716, 362, 159, 282,2569,1129,1658,1288,1750, # 3248 +2674, 276, 649,2016, 751,1496, 658,1818,1284,1862,2209,2087,2512,3451, 622,2834, # 3264 + 376, 117,1060,2053,1208,1721,1101,1443, 247,1250,3179,1792,3952,2760,2398,3953, # 3280 +6157,2144,3708, 446,2432,1151,2570,3452,2447,2761,2835,1210,2448,3082, 424,2222, # 3296 +1251,2449,2119,2836, 504,1581,4338, 602, 817, 857,3825,2349,2306, 357,3826,1470, # 3312 +1883,2883, 255, 958, 929,2917,3248, 302,4653,1050,1271,1751,2307,1952,1430,2697, # 3328 +2719,2359, 354,3180, 777, 158,2036,4339,1659,4340,4654,2308,2949,2248,1146,2232, # 3344 +3532,2720,1696,2623,3827,6158,3129,1550,2698,1485,1297,1428, 637, 931,2721,2145, # 3360 + 914,2550,2587, 81,2450, 612, 827,2646,1242,4655,1118,2884, 472,1855,3181,3533, # 3376 +3534, 569,1353,2699,1244,1758,2588,4119,2009,2762,2171,3709,1312,1531,6159,1152, # 3392 +1938, 134,1830, 471,3710,2276,1112,1535,3323,3453,3535, 982,1337,2950, 488, 826, # 3408 + 674,1058,1628,4120,2017, 522,2399, 211, 568,1367,3454, 350, 293,1872,1139,3249, # 3424 +1399,1946,3006,1300,2360,3324, 588, 736,6160,2606, 744, 669,3536,3828,6161,1358, # 3440 + 199, 723, 848, 933, 851,1939,1505,1514,1338,1618,1831,4656,1634,3613, 443,2740, # 3456 +3829, 717,1947, 491,1914,6162,2551,1542,4121,1025,6163,1099,1223, 198,3040,2722, # 3472 + 370, 410,1905,2589, 998,1248,3182,2380, 519,1449,4122,1710, 947, 928,1153,4341, # 3488 +2277, 344,2624,1511, 615, 105, 161,1212,1076,1960,3130,2054,1926,1175,1906,2473, # 3504 + 414,1873,2801,6164,2309, 315,1319,3325, 318,2018,2146,2157, 963, 631, 223,4342, # 3520 +4343,2675, 479,3711,1197,2625,3712,2676,2361,6165,4344,4123,6166,2451,3183,1886, # 3536 +2184,1674,1330,1711,1635,1506, 799, 219,3250,3083,3954,1677,3713,3326,2081,3614, # 3552 +1652,2073,4657,1147,3041,1752, 643,1961, 147,1974,3955,6167,1716,2037, 918,3007, # 3568 +1994, 120,1537, 118, 609,3184,4345, 740,3455,1219, 332,1615,3830,6168,1621,2980, # 3584 +1582, 783, 212, 553,2350,3714,1349,2433,2082,4124, 889,6169,2310,1275,1410, 973, # 3600 + 166,1320,3456,1797,1215,3185,2885,1846,2590,2763,4658, 629, 822,3008, 763, 940, # 3616 +1990,2862, 439,2409,1566,1240,1622, 926,1282,1907,2764, 654,2210,1607, 327,1130, # 3632 +3956,1678,1623,6170,2434,2192, 686, 608,3831,3715, 903,3957,3042,6171,2741,1522, # 3648 +1915,1105,1555,2552,1359, 323,3251,4346,3457, 738,1354,2553,2311,2334,1828,2003, # 3664 +3832,1753,2351,1227,6172,1887,4125,1478,6173,2410,1874,1712,1847, 520,1204,2607, # 3680 + 264,4659, 836,2677,2102, 600,4660,3833,2278,3084,6174,4347,3615,1342, 640, 532, # 3696 + 543,2608,1888,2400,2591,1009,4348,1497, 341,1737,3616,2723,1394, 529,3252,1321, # 3712 + 983,4661,1515,2120, 971,2592, 924, 287,1662,3186,4349,2700,4350,1519, 908,1948, # 3728 +2452, 156, 796,1629,1486,2223,2055, 694,4126,1259,1036,3392,1213,2249,2742,1889, # 3744 +1230,3958,1015, 910, 408, 559,3617,4662, 746, 725, 935,4663,3959,3009,1289, 563, # 3760 + 867,4664,3960,1567,2981,2038,2626, 988,2263,2381,4351, 143,2374, 704,1895,6175, # 3776 +1188,3716,2088, 673,3085,2362,4352, 484,1608,1921,2765,2918, 215, 904,3618,3537, # 3792 + 894, 509, 976,3043,2701,3961,4353,2837,2982, 498,6176,6177,1102,3538,1332,3393, # 3808 +1487,1636,1637, 233, 245,3962, 383, 650, 995,3044, 460,1520,1206,2352, 749,3327, # 3824 + 530, 700, 389,1438,1560,1773,3963,2264, 719,2951,2724,3834, 870,1832,1644,1000, # 3840 + 839,2474,3717, 197,1630,3394, 365,2886,3964,1285,2133, 734, 922, 818,1106, 732, # 3856 + 480,2083,1774,3458, 923,2279,1350, 221,3086, 85,2233,2234,3835,1585,3010,2147, # 3872 +1387,1705,2382,1619,2475, 133, 239,2802,1991,1016,2084,2383, 411,2838,1113, 651, # 3888 +1985,1160,3328, 990,1863,3087,1048,1276,2647, 265,2627,1599,3253,2056, 150, 638, # 3904 +2019, 656, 853, 326,1479, 680,1439,4354,1001,1759, 413,3459,3395,2492,1431, 459, # 3920 +4355,1125,3329,2265,1953,1450,2065,2863, 849, 351,2678,3131,3254,3255,1104,1577, # 3936 + 227,1351,1645,2453,2193,1421,2887, 812,2121, 634, 95,2435, 201,2312,4665,1646, # 3952 +1671,2743,1601,2554,2702,2648,2280,1315,1366,2089,3132,1573,3718,3965,1729,1189, # 3968 + 328,2679,1077,1940,1136, 558,1283, 964,1195, 621,2074,1199,1743,3460,3619,1896, # 3984 +1916,1890,3836,2952,1154,2112,1064, 862, 378,3011,2066,2113,2803,1568,2839,6178, # 4000 +3088,2919,1941,1660,2004,1992,2194, 142, 707,1590,1708,1624,1922,1023,1836,1233, # 4016 +1004,2313, 789, 741,3620,6179,1609,2411,1200,4127,3719,3720,4666,2057,3721, 593, # 4032 +2840, 367,2920,1878,6180,3461,1521, 628,1168, 692,2211,2649, 300, 720,2067,2571, # 4048 +2953,3396, 959,2504,3966,3539,3462,1977, 701,6181, 954,1043, 800, 681, 183,3722, # 4064 +1803,1730,3540,4128,2103, 815,2314, 174, 467, 230,2454,1093,2134, 755,3541,3397, # 4080 +1141,1162,6182,1738,2039, 270,3256,2513,1005,1647,2185,3837, 858,1679,1897,1719, # 4096 +2954,2324,1806, 402, 670, 167,4129,1498,2158,2104, 750,6183, 915, 189,1680,1551, # 4112 + 455,4356,1501,2455, 405,1095,2955, 338,1586,1266,1819, 570, 641,1324, 237,1556, # 4128 +2650,1388,3723,6184,1368,2384,1343,1978,3089,2436, 879,3724, 792,1191, 758,3012, # 4144 +1411,2135,1322,4357, 240,4667,1848,3725,1574,6185, 420,3045,1546,1391, 714,4358, # 4160 +1967, 941,1864, 863, 664, 426, 560,1731,2680,1785,2864,1949,2363, 403,3330,1415, # 4176 +1279,2136,1697,2335, 204, 721,2097,3838, 90,6186,2085,2505, 191,3967, 124,2148, # 4192 +1376,1798,1178,1107,1898,1405, 860,4359,1243,1272,2375,2983,1558,2456,1638, 113, # 4208 +3621, 578,1923,2609, 880, 386,4130, 784,2186,2266,1422,2956,2172,1722, 497, 263, # 4224 +2514,1267,2412,2610, 177,2703,3542, 774,1927,1344, 616,1432,1595,1018, 172,4360, # 4240 +2325, 911,4361, 438,1468,3622, 794,3968,2024,2173,1681,1829,2957, 945, 895,3090, # 4256 + 575,2212,2476, 475,2401,2681, 785,2744,1745,2293,2555,1975,3133,2865, 394,4668, # 4272 +3839, 635,4131, 639, 202,1507,2195,2766,1345,1435,2572,3726,1908,1184,1181,2457, # 4288 +3727,3134,4362, 843,2611, 437, 916,4669, 234, 769,1884,3046,3047,3623, 833,6187, # 4304 +1639,2250,2402,1355,1185,2010,2047, 999, 525,1732,1290,1488,2612, 948,1578,3728, # 4320 +2413,2477,1216,2725,2159, 334,3840,1328,3624,2921,1525,4132, 564,1056, 891,4363, # 4336 +1444,1698,2385,2251,3729,1365,2281,2235,1717,6188, 864,3841,2515, 444, 527,2767, # 4352 +2922,3625, 544, 461,6189, 566, 209,2437,3398,2098,1065,2068,3331,3626,3257,2137, # 4368 #last 512 +#Everything below is of no interest for detection purpose +2138,2122,3730,2888,1995,1820,1044,6190,6191,6192,6193,6194,6195,6196,6197,6198, # 4384 +6199,6200,6201,6202,6203,6204,6205,4670,6206,6207,6208,6209,6210,6211,6212,6213, # 4400 +6214,6215,6216,6217,6218,6219,6220,6221,6222,6223,6224,6225,6226,6227,6228,6229, # 4416 +6230,6231,6232,6233,6234,6235,6236,6237,3187,6238,6239,3969,6240,6241,6242,6243, # 4432 +6244,4671,6245,6246,4672,6247,6248,4133,6249,6250,4364,6251,2923,2556,2613,4673, # 4448 +4365,3970,6252,6253,6254,6255,4674,6256,6257,6258,2768,2353,4366,4675,4676,3188, # 4464 +4367,3463,6259,4134,4677,4678,6260,2267,6261,3842,3332,4368,3543,6262,6263,6264, # 4480 +3013,1954,1928,4135,4679,6265,6266,2478,3091,6267,4680,4369,6268,6269,1699,6270, # 4496 +3544,4136,4681,6271,4137,6272,4370,2804,6273,6274,2593,3971,3972,4682,6275,2236, # 4512 +4683,6276,6277,4684,6278,6279,4138,3973,4685,6280,6281,3258,6282,6283,6284,6285, # 4528 +3974,4686,2841,3975,6286,6287,3545,6288,6289,4139,4687,4140,6290,4141,6291,4142, # 4544 +6292,6293,3333,6294,6295,6296,4371,6297,3399,6298,6299,4372,3976,6300,6301,6302, # 4560 +4373,6303,6304,3843,3731,6305,4688,4374,6306,6307,3259,2294,6308,3732,2530,4143, # 4576 +6309,4689,6310,6311,6312,3048,6313,6314,4690,3733,2237,6315,6316,2282,3334,6317, # 4592 +6318,3844,6319,6320,4691,6321,3400,4692,6322,4693,6323,3049,6324,4375,6325,3977, # 4608 +6326,6327,6328,3546,6329,4694,3335,6330,4695,4696,6331,6332,6333,6334,4376,3978, # 4624 +6335,4697,3979,4144,6336,3980,4698,6337,6338,6339,6340,6341,4699,4700,4701,6342, # 4640 +6343,4702,6344,6345,4703,6346,6347,4704,6348,4705,4706,3135,6349,4707,6350,4708, # 4656 +6351,4377,6352,4709,3734,4145,6353,2506,4710,3189,6354,3050,4711,3981,6355,3547, # 4672 +3014,4146,4378,3735,2651,3845,3260,3136,2224,1986,6356,3401,6357,4712,2594,3627, # 4688 +3137,2573,3736,3982,4713,3628,4714,4715,2682,3629,4716,6358,3630,4379,3631,6359, # 4704 +6360,6361,3983,6362,6363,6364,6365,4147,3846,4717,6366,6367,3737,2842,6368,4718, # 4720 +2628,6369,3261,6370,2386,6371,6372,3738,3984,4719,3464,4720,3402,6373,2924,3336, # 4736 +4148,2866,6374,2805,3262,4380,2704,2069,2531,3138,2806,2984,6375,2769,6376,4721, # 4752 +4722,3403,6377,6378,3548,6379,6380,2705,3092,1979,4149,2629,3337,2889,6381,3338, # 4768 +4150,2557,3339,4381,6382,3190,3263,3739,6383,4151,4723,4152,2558,2574,3404,3191, # 4784 +6384,6385,4153,6386,4724,4382,6387,6388,4383,6389,6390,4154,6391,4725,3985,6392, # 4800 +3847,4155,6393,6394,6395,6396,6397,3465,6398,4384,6399,6400,6401,6402,6403,6404, # 4816 +4156,6405,6406,6407,6408,2123,6409,6410,2326,3192,4726,6411,6412,6413,6414,4385, # 4832 +4157,6415,6416,4158,6417,3093,3848,6418,3986,6419,6420,3849,6421,6422,6423,4159, # 4848 +6424,6425,4160,6426,3740,6427,6428,6429,6430,3987,6431,4727,6432,2238,6433,6434, # 4864 +4386,3988,6435,6436,3632,6437,6438,2843,6439,6440,6441,6442,3633,6443,2958,6444, # 4880 +6445,3466,6446,2364,4387,3850,6447,4388,2959,3340,6448,3851,6449,4728,6450,6451, # 4896 +3264,4729,6452,3193,6453,4389,4390,2706,3341,4730,6454,3139,6455,3194,6456,3051, # 4912 +2124,3852,1602,4391,4161,3853,1158,3854,4162,3989,4392,3990,4731,4732,4393,2040, # 4928 +4163,4394,3265,6457,2807,3467,3855,6458,6459,6460,3991,3468,4733,4734,6461,3140, # 4944 +2960,6462,4735,6463,6464,6465,6466,4736,4737,4738,4739,6467,6468,4164,2403,3856, # 4960 +6469,6470,2770,2844,6471,4740,6472,6473,6474,6475,6476,6477,6478,3195,6479,4741, # 4976 +4395,6480,2867,6481,4742,2808,6482,2493,4165,6483,6484,6485,6486,2295,4743,6487, # 4992 +6488,6489,3634,6490,6491,6492,6493,6494,6495,6496,2985,4744,6497,6498,4745,6499, # 5008 +6500,2925,3141,4166,6501,6502,4746,6503,6504,4747,6505,6506,6507,2890,6508,6509, # 5024 +6510,6511,6512,6513,6514,6515,6516,6517,6518,6519,3469,4167,6520,6521,6522,4748, # 5040 +4396,3741,4397,4749,4398,3342,2125,4750,6523,4751,4752,4753,3052,6524,2961,4168, # 5056 +6525,4754,6526,4755,4399,2926,4169,6527,3857,6528,4400,4170,6529,4171,6530,6531, # 5072 +2595,6532,6533,6534,6535,3635,6536,6537,6538,6539,6540,6541,6542,4756,6543,6544, # 5088 +6545,6546,6547,6548,4401,6549,6550,6551,6552,4402,3405,4757,4403,6553,6554,6555, # 5104 +4172,3742,6556,6557,6558,3992,3636,6559,6560,3053,2726,6561,3549,4173,3054,4404, # 5120 +6562,6563,3993,4405,3266,3550,2809,4406,6564,6565,6566,4758,4759,6567,3743,6568, # 5136 +4760,3744,4761,3470,6569,6570,6571,4407,6572,3745,4174,6573,4175,2810,4176,3196, # 5152 +4762,6574,4177,6575,6576,2494,2891,3551,6577,6578,3471,6579,4408,6580,3015,3197, # 5168 +6581,3343,2532,3994,3858,6582,3094,3406,4409,6583,2892,4178,4763,4410,3016,4411, # 5184 +6584,3995,3142,3017,2683,6585,4179,6586,6587,4764,4412,6588,6589,4413,6590,2986, # 5200 +6591,2962,3552,6592,2963,3472,6593,6594,4180,4765,6595,6596,2225,3267,4414,6597, # 5216 +3407,3637,4766,6598,6599,3198,6600,4415,6601,3859,3199,6602,3473,4767,2811,4416, # 5232 +1856,3268,3200,2575,3996,3997,3201,4417,6603,3095,2927,6604,3143,6605,2268,6606, # 5248 +3998,3860,3096,2771,6607,6608,3638,2495,4768,6609,3861,6610,3269,2745,4769,4181, # 5264 +3553,6611,2845,3270,6612,6613,6614,3862,6615,6616,4770,4771,6617,3474,3999,4418, # 5280 +4419,6618,3639,3344,6619,4772,4182,6620,2126,6621,6622,6623,4420,4773,6624,3018, # 5296 +6625,4774,3554,6626,4183,2025,3746,6627,4184,2707,6628,4421,4422,3097,1775,4185, # 5312 +3555,6629,6630,2868,6631,6632,4423,6633,6634,4424,2414,2533,2928,6635,4186,2387, # 5328 +6636,4775,6637,4187,6638,1891,4425,3202,3203,6639,6640,4776,6641,3345,6642,6643, # 5344 +3640,6644,3475,3346,3641,4000,6645,3144,6646,3098,2812,4188,3642,3204,6647,3863, # 5360 +3476,6648,3864,6649,4426,4001,6650,6651,6652,2576,6653,4189,4777,6654,6655,6656, # 5376 +2846,6657,3477,3205,4002,6658,4003,6659,3347,2252,6660,6661,6662,4778,6663,6664, # 5392 +6665,6666,6667,6668,6669,4779,4780,2048,6670,3478,3099,6671,3556,3747,4004,6672, # 5408 +6673,6674,3145,4005,3748,6675,6676,6677,6678,6679,3408,6680,6681,6682,6683,3206, # 5424 +3207,6684,6685,4781,4427,6686,4782,4783,4784,6687,6688,6689,4190,6690,6691,3479, # 5440 +6692,2746,6693,4428,6694,6695,6696,6697,6698,6699,4785,6700,6701,3208,2727,6702, # 5456 +3146,6703,6704,3409,2196,6705,4429,6706,6707,6708,2534,1996,6709,6710,6711,2747, # 5472 +6712,6713,6714,4786,3643,6715,4430,4431,6716,3557,6717,4432,4433,6718,6719,6720, # 5488 +6721,3749,6722,4006,4787,6723,6724,3644,4788,4434,6725,6726,4789,2772,6727,6728, # 5504 +6729,6730,6731,2708,3865,2813,4435,6732,6733,4790,4791,3480,6734,6735,6736,6737, # 5520 +4436,3348,6738,3410,4007,6739,6740,4008,6741,6742,4792,3411,4191,6743,6744,6745, # 5536 +6746,6747,3866,6748,3750,6749,6750,6751,6752,6753,6754,6755,3867,6756,4009,6757, # 5552 +4793,4794,6758,2814,2987,6759,6760,6761,4437,6762,6763,6764,6765,3645,6766,6767, # 5568 +3481,4192,6768,3751,6769,6770,2174,6771,3868,3752,6772,6773,6774,4193,4795,4438, # 5584 +3558,4796,4439,6775,4797,6776,6777,4798,6778,4799,3559,4800,6779,6780,6781,3482, # 5600 +6782,2893,6783,6784,4194,4801,4010,6785,6786,4440,6787,4011,6788,6789,6790,6791, # 5616 +6792,6793,4802,6794,6795,6796,4012,6797,6798,6799,6800,3349,4803,3483,6801,4804, # 5632 +4195,6802,4013,6803,6804,4196,6805,4014,4015,6806,2847,3271,2848,6807,3484,6808, # 5648 +6809,6810,4441,6811,4442,4197,4443,3272,4805,6812,3412,4016,1579,6813,6814,4017, # 5664 +6815,3869,6816,2964,6817,4806,6818,6819,4018,3646,6820,6821,4807,4019,4020,6822, # 5680 +6823,3560,6824,6825,4021,4444,6826,4198,6827,6828,4445,6829,6830,4199,4808,6831, # 5696 +6832,6833,3870,3019,2458,6834,3753,3413,3350,6835,4809,3871,4810,3561,4446,6836, # 5712 +6837,4447,4811,4812,6838,2459,4448,6839,4449,6840,6841,4022,3872,6842,4813,4814, # 5728 +6843,6844,4815,4200,4201,4202,6845,4023,6846,6847,4450,3562,3873,6848,6849,4816, # 5744 +4817,6850,4451,4818,2139,6851,3563,6852,6853,3351,6854,6855,3352,4024,2709,3414, # 5760 +4203,4452,6856,4204,6857,6858,3874,3875,6859,6860,4819,6861,6862,6863,6864,4453, # 5776 +3647,6865,6866,4820,6867,6868,6869,6870,4454,6871,2869,6872,6873,4821,6874,3754, # 5792 +6875,4822,4205,6876,6877,6878,3648,4206,4455,6879,4823,6880,4824,3876,6881,3055, # 5808 +4207,6882,3415,6883,6884,6885,4208,4209,6886,4210,3353,6887,3354,3564,3209,3485, # 5824 +2652,6888,2728,6889,3210,3755,6890,4025,4456,6891,4825,6892,6893,6894,6895,4211, # 5840 +6896,6897,6898,4826,6899,6900,4212,6901,4827,6902,2773,3565,6903,4828,6904,6905, # 5856 +6906,6907,3649,3650,6908,2849,3566,6909,3567,3100,6910,6911,6912,6913,6914,6915, # 5872 +4026,6916,3355,4829,3056,4457,3756,6917,3651,6918,4213,3652,2870,6919,4458,6920, # 5888 +2438,6921,6922,3757,2774,4830,6923,3356,4831,4832,6924,4833,4459,3653,2507,6925, # 5904 +4834,2535,6926,6927,3273,4027,3147,6928,3568,6929,6930,6931,4460,6932,3877,4461, # 5920 +2729,3654,6933,6934,6935,6936,2175,4835,2630,4214,4028,4462,4836,4215,6937,3148, # 5936 +4216,4463,4837,4838,4217,6938,6939,2850,4839,6940,4464,6941,6942,6943,4840,6944, # 5952 +4218,3274,4465,6945,6946,2710,6947,4841,4466,6948,6949,2894,6950,6951,4842,6952, # 5968 +4219,3057,2871,6953,6954,6955,6956,4467,6957,2711,6958,6959,6960,3275,3101,4843, # 5984 +6961,3357,3569,6962,4844,6963,6964,4468,4845,3570,6965,3102,4846,3758,6966,4847, # 6000 +3878,4848,4849,4029,6967,2929,3879,4850,4851,6968,6969,1733,6970,4220,6971,6972, # 6016 +6973,6974,6975,6976,4852,6977,6978,6979,6980,6981,6982,3759,6983,6984,6985,3486, # 6032 +3487,6986,3488,3416,6987,6988,6989,6990,6991,6992,6993,6994,6995,6996,6997,4853, # 6048 +6998,6999,4030,7000,7001,3211,7002,7003,4221,7004,7005,3571,4031,7006,3572,7007, # 6064 +2614,4854,2577,7008,7009,2965,3655,3656,4855,2775,3489,3880,4222,4856,3881,4032, # 6080 +3882,3657,2730,3490,4857,7010,3149,7011,4469,4858,2496,3491,4859,2283,7012,7013, # 6096 +7014,2365,4860,4470,7015,7016,3760,7017,7018,4223,1917,7019,7020,7021,4471,7022, # 6112 +2776,4472,7023,7024,7025,7026,4033,7027,3573,4224,4861,4034,4862,7028,7029,1929, # 6128 +3883,4035,7030,4473,3058,7031,2536,3761,3884,7032,4036,7033,2966,2895,1968,4474, # 6144 +3276,4225,3417,3492,4226,2105,7034,7035,1754,2596,3762,4227,4863,4475,3763,4864, # 6160 +3764,2615,2777,3103,3765,3658,3418,4865,2296,3766,2815,7036,7037,7038,3574,2872, # 6176 +3277,4476,7039,4037,4477,7040,7041,4038,7042,7043,7044,7045,7046,7047,2537,7048, # 6192 +7049,7050,7051,7052,7053,7054,4478,7055,7056,3767,3659,4228,3575,7057,7058,4229, # 6208 +7059,7060,7061,3660,7062,3212,7063,3885,4039,2460,7064,7065,7066,7067,7068,7069, # 6224 +7070,7071,7072,7073,7074,4866,3768,4867,7075,7076,7077,7078,4868,3358,3278,2653, # 6240 +7079,7080,4479,3886,7081,7082,4869,7083,7084,7085,7086,7087,7088,2538,7089,7090, # 6256 +7091,4040,3150,3769,4870,4041,2896,3359,4230,2930,7092,3279,7093,2967,4480,3213, # 6272 +4481,3661,7094,7095,7096,7097,7098,7099,7100,7101,7102,2461,3770,7103,7104,4231, # 6288 +3151,7105,7106,7107,4042,3662,7108,7109,4871,3663,4872,4043,3059,7110,7111,7112, # 6304 +3493,2988,7113,4873,7114,7115,7116,3771,4874,7117,7118,4232,4875,7119,3576,2336, # 6320 +4876,7120,4233,3419,4044,4877,4878,4482,4483,4879,4484,4234,7121,3772,4880,1045, # 6336 +3280,3664,4881,4882,7122,7123,7124,7125,4883,7126,2778,7127,4485,4486,7128,4884, # 6352 +3214,3887,7129,7130,3215,7131,4885,4045,7132,7133,4046,7134,7135,7136,7137,7138, # 6368 +7139,7140,7141,7142,7143,4235,7144,4886,7145,7146,7147,4887,7148,7149,7150,4487, # 6384 +4047,4488,7151,7152,4888,4048,2989,3888,7153,3665,7154,4049,7155,7156,7157,7158, # 6400 +7159,7160,2931,4889,4890,4489,7161,2631,3889,4236,2779,7162,7163,4891,7164,3060, # 6416 +7165,1672,4892,7166,4893,4237,3281,4894,7167,7168,3666,7169,3494,7170,7171,4050, # 6432 +7172,7173,3104,3360,3420,4490,4051,2684,4052,7174,4053,7175,7176,7177,2253,4054, # 6448 +7178,7179,4895,7180,3152,3890,3153,4491,3216,7181,7182,7183,2968,4238,4492,4055, # 6464 +7184,2990,7185,2479,7186,7187,4493,7188,7189,7190,7191,7192,4896,7193,4897,2969, # 6480 +4494,4898,7194,3495,7195,7196,4899,4495,7197,3105,2731,7198,4900,7199,7200,7201, # 6496 +4056,7202,3361,7203,7204,4496,4901,4902,7205,4497,7206,7207,2315,4903,7208,4904, # 6512 +7209,4905,2851,7210,7211,3577,7212,3578,4906,7213,4057,3667,4907,7214,4058,2354, # 6528 +3891,2376,3217,3773,7215,7216,7217,7218,7219,4498,7220,4908,3282,2685,7221,3496, # 6544 +4909,2632,3154,4910,7222,2337,7223,4911,7224,7225,7226,4912,4913,3283,4239,4499, # 6560 +7227,2816,7228,7229,7230,7231,7232,7233,7234,4914,4500,4501,7235,7236,7237,2686, # 6576 +7238,4915,7239,2897,4502,7240,4503,7241,2516,7242,4504,3362,3218,7243,7244,7245, # 6592 +4916,7246,7247,4505,3363,7248,7249,7250,7251,3774,4506,7252,7253,4917,7254,7255, # 6608 +3284,2991,4918,4919,3219,3892,4920,3106,3497,4921,7256,7257,7258,4922,7259,4923, # 6624 +3364,4507,4508,4059,7260,4240,3498,7261,7262,4924,7263,2992,3893,4060,3220,7264, # 6640 +7265,7266,7267,7268,7269,4509,3775,7270,2817,7271,4061,4925,4510,3776,7272,4241, # 6656 +4511,3285,7273,7274,3499,7275,7276,7277,4062,4512,4926,7278,3107,3894,7279,7280, # 6672 +4927,7281,4513,7282,7283,3668,7284,7285,4242,4514,4243,7286,2058,4515,4928,4929, # 6688 +4516,7287,3286,4244,7288,4517,7289,7290,7291,3669,7292,7293,4930,4931,4932,2355, # 6704 +4933,7294,2633,4518,7295,4245,7296,7297,4519,7298,7299,4520,4521,4934,7300,4246, # 6720 +4522,7301,7302,7303,3579,7304,4247,4935,7305,4936,7306,7307,7308,7309,3777,7310, # 6736 +4523,7311,7312,7313,4248,3580,7314,4524,3778,4249,7315,3581,7316,3287,7317,3221, # 6752 +7318,4937,7319,7320,7321,7322,7323,7324,4938,4939,7325,4525,7326,7327,7328,4063, # 6768 +7329,7330,4940,7331,7332,4941,7333,4526,7334,3500,2780,1741,4942,2026,1742,7335, # 6784 +7336,3582,4527,2388,7337,7338,7339,4528,7340,4250,4943,7341,7342,7343,4944,7344, # 6800 +7345,7346,3020,7347,4945,7348,7349,7350,7351,3895,7352,3896,4064,3897,7353,7354, # 6816 +7355,4251,7356,7357,3898,7358,3779,7359,3780,3288,7360,7361,4529,7362,4946,4530, # 6832 +2027,7363,3899,4531,4947,3222,3583,7364,4948,7365,7366,7367,7368,4949,3501,4950, # 6848 +3781,4951,4532,7369,2517,4952,4252,4953,3155,7370,4954,4955,4253,2518,4533,7371, # 6864 +7372,2712,4254,7373,7374,7375,3670,4956,3671,7376,2389,3502,4065,7377,2338,7378, # 6880 +7379,7380,7381,3061,7382,4957,7383,7384,7385,7386,4958,4534,7387,7388,2993,7389, # 6896 +3062,7390,4959,7391,7392,7393,4960,3108,4961,7394,4535,7395,4962,3421,4536,7396, # 6912 +4963,7397,4964,1857,7398,4965,7399,7400,2176,3584,4966,7401,7402,3422,4537,3900, # 6928 +3585,7403,3782,7404,2852,7405,7406,7407,4538,3783,2654,3423,4967,4539,7408,3784, # 6944 +3586,2853,4540,4541,7409,3901,7410,3902,7411,7412,3785,3109,2327,3903,7413,7414, # 6960 +2970,4066,2932,7415,7416,7417,3904,3672,3424,7418,4542,4543,4544,7419,4968,7420, # 6976 +7421,4255,7422,7423,7424,7425,7426,4067,7427,3673,3365,4545,7428,3110,2559,3674, # 6992 +7429,7430,3156,7431,7432,3503,7433,3425,4546,7434,3063,2873,7435,3223,4969,4547, # 7008 +4548,2898,4256,4068,7436,4069,3587,3786,2933,3787,4257,4970,4971,3788,7437,4972, # 7024 +3064,7438,4549,7439,7440,7441,7442,7443,4973,3905,7444,2874,7445,7446,7447,7448, # 7040 +3021,7449,4550,3906,3588,4974,7450,7451,3789,3675,7452,2578,7453,4070,7454,7455, # 7056 +7456,4258,3676,7457,4975,7458,4976,4259,3790,3504,2634,4977,3677,4551,4260,7459, # 7072 +7460,7461,7462,3907,4261,4978,7463,7464,7465,7466,4979,4980,7467,7468,2213,4262, # 7088 +7469,7470,7471,3678,4981,7472,2439,7473,4263,3224,3289,7474,3908,2415,4982,7475, # 7104 +4264,7476,4983,2655,7477,7478,2732,4552,2854,2875,7479,7480,4265,7481,4553,4984, # 7120 +7482,7483,4266,7484,3679,3366,3680,2818,2781,2782,3367,3589,4554,3065,7485,4071, # 7136 +2899,7486,7487,3157,2462,4072,4555,4073,4985,4986,3111,4267,2687,3368,4556,4074, # 7152 +3791,4268,7488,3909,2783,7489,2656,1962,3158,4557,4987,1963,3159,3160,7490,3112, # 7168 +4988,4989,3022,4990,4991,3792,2855,7491,7492,2971,4558,7493,7494,4992,7495,7496, # 7184 +7497,7498,4993,7499,3426,4559,4994,7500,3681,4560,4269,4270,3910,7501,4075,4995, # 7200 +4271,7502,7503,4076,7504,4996,7505,3225,4997,4272,4077,2819,3023,7506,7507,2733, # 7216 +4561,7508,4562,7509,3369,3793,7510,3590,2508,7511,7512,4273,3113,2994,2616,7513, # 7232 +7514,7515,7516,7517,7518,2820,3911,4078,2748,7519,7520,4563,4998,7521,7522,7523, # 7248 +7524,4999,4274,7525,4564,3682,2239,4079,4565,7526,7527,7528,7529,5000,7530,7531, # 7264 +5001,4275,3794,7532,7533,7534,3066,5002,4566,3161,7535,7536,4080,7537,3162,7538, # 7280 +7539,4567,7540,7541,7542,7543,7544,7545,5003,7546,4568,7547,7548,7549,7550,7551, # 7296 +7552,7553,7554,7555,7556,5004,7557,7558,7559,5005,7560,3795,7561,4569,7562,7563, # 7312 +7564,2821,3796,4276,4277,4081,7565,2876,7566,5006,7567,7568,2900,7569,3797,3912, # 7328 +7570,7571,7572,4278,7573,7574,7575,5007,7576,7577,5008,7578,7579,4279,2934,7580, # 7344 +7581,5009,7582,4570,7583,4280,7584,7585,7586,4571,4572,3913,7587,4573,3505,7588, # 7360 +5010,7589,7590,7591,7592,3798,4574,7593,7594,5011,7595,4281,7596,7597,7598,4282, # 7376 +5012,7599,7600,5013,3163,7601,5014,7602,3914,7603,7604,2734,4575,4576,4577,7605, # 7392 +7606,7607,7608,7609,3506,5015,4578,7610,4082,7611,2822,2901,2579,3683,3024,4579, # 7408 +3507,7612,4580,7613,3226,3799,5016,7614,7615,7616,7617,7618,7619,7620,2995,3290, # 7424 +7621,4083,7622,5017,7623,7624,7625,7626,7627,4581,3915,7628,3291,7629,5018,7630, # 7440 +7631,7632,7633,4084,7634,7635,3427,3800,7636,7637,4582,7638,5019,4583,5020,7639, # 7456 +3916,7640,3801,5021,4584,4283,7641,7642,3428,3591,2269,7643,2617,7644,4585,3592, # 7472 +7645,4586,2902,7646,7647,3227,5022,7648,4587,7649,4284,7650,7651,7652,4588,2284, # 7488 +7653,5023,7654,7655,7656,4589,5024,3802,7657,7658,5025,3508,4590,7659,7660,7661, # 7504 +1969,5026,7662,7663,3684,1821,2688,7664,2028,2509,4285,7665,2823,1841,7666,2689, # 7520 +3114,7667,3917,4085,2160,5027,5028,2972,7668,5029,7669,7670,7671,3593,4086,7672, # 7536 +4591,4087,5030,3803,7673,7674,7675,7676,7677,7678,7679,4286,2366,4592,4593,3067, # 7552 +2328,7680,7681,4594,3594,3918,2029,4287,7682,5031,3919,3370,4288,4595,2856,7683, # 7568 +3509,7684,7685,5032,5033,7686,7687,3804,2784,7688,7689,7690,7691,3371,7692,7693, # 7584 +2877,5034,7694,7695,3920,4289,4088,7696,7697,7698,5035,7699,5036,4290,5037,5038, # 7600 +5039,7700,7701,7702,5040,5041,3228,7703,1760,7704,5042,3229,4596,2106,4089,7705, # 7616 +4597,2824,5043,2107,3372,7706,4291,4090,5044,7707,4091,7708,5045,3025,3805,4598, # 7632 +4292,4293,4294,3373,7709,4599,7710,5046,7711,7712,5047,5048,3806,7713,7714,7715, # 7648 +5049,7716,7717,7718,7719,4600,5050,7720,7721,7722,5051,7723,4295,3429,7724,7725, # 7664 +7726,7727,3921,7728,3292,5052,4092,7729,7730,7731,7732,7733,7734,7735,5053,5054, # 7680 +7736,7737,7738,7739,3922,3685,7740,7741,7742,7743,2635,5055,7744,5056,4601,7745, # 7696 +7746,2560,7747,7748,7749,7750,3923,7751,7752,7753,7754,7755,4296,2903,7756,7757, # 7712 +7758,7759,7760,3924,7761,5057,4297,7762,7763,5058,4298,7764,4093,7765,7766,5059, # 7728 +3925,7767,7768,7769,7770,7771,7772,7773,7774,7775,7776,3595,7777,4299,5060,4094, # 7744 +7778,3293,5061,7779,7780,4300,7781,7782,4602,7783,3596,7784,7785,3430,2367,7786, # 7760 +3164,5062,5063,4301,7787,7788,4095,5064,5065,7789,3374,3115,7790,7791,7792,7793, # 7776 +7794,7795,7796,3597,4603,7797,7798,3686,3116,3807,5066,7799,7800,5067,7801,7802, # 7792 +4604,4302,5068,4303,4096,7803,7804,3294,7805,7806,5069,4605,2690,7807,3026,7808, # 7808 +7809,7810,7811,7812,7813,7814,7815,7816,7817,7818,7819,7820,7821,7822,7823,7824, # 7824 +7825,7826,7827,7828,7829,7830,7831,7832,7833,7834,7835,7836,7837,7838,7839,7840, # 7840 +7841,7842,7843,7844,7845,7846,7847,7848,7849,7850,7851,7852,7853,7854,7855,7856, # 7856 +7857,7858,7859,7860,7861,7862,7863,7864,7865,7866,7867,7868,7869,7870,7871,7872, # 7872 +7873,7874,7875,7876,7877,7878,7879,7880,7881,7882,7883,7884,7885,7886,7887,7888, # 7888 +7889,7890,7891,7892,7893,7894,7895,7896,7897,7898,7899,7900,7901,7902,7903,7904, # 7904 +7905,7906,7907,7908,7909,7910,7911,7912,7913,7914,7915,7916,7917,7918,7919,7920, # 7920 +7921,7922,7923,7924,3926,7925,7926,7927,7928,7929,7930,7931,7932,7933,7934,7935, # 7936 +7936,7937,7938,7939,7940,7941,7942,7943,7944,7945,7946,7947,7948,7949,7950,7951, # 7952 +7952,7953,7954,7955,7956,7957,7958,7959,7960,7961,7962,7963,7964,7965,7966,7967, # 7968 +7968,7969,7970,7971,7972,7973,7974,7975,7976,7977,7978,7979,7980,7981,7982,7983, # 7984 +7984,7985,7986,7987,7988,7989,7990,7991,7992,7993,7994,7995,7996,7997,7998,7999, # 8000 +8000,8001,8002,8003,8004,8005,8006,8007,8008,8009,8010,8011,8012,8013,8014,8015, # 8016 +8016,8017,8018,8019,8020,8021,8022,8023,8024,8025,8026,8027,8028,8029,8030,8031, # 8032 +8032,8033,8034,8035,8036,8037,8038,8039,8040,8041,8042,8043,8044,8045,8046,8047, # 8048 +8048,8049,8050,8051,8052,8053,8054,8055,8056,8057,8058,8059,8060,8061,8062,8063, # 8064 +8064,8065,8066,8067,8068,8069,8070,8071,8072,8073,8074,8075,8076,8077,8078,8079, # 8080 +8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095, # 8096 +8096,8097,8098,8099,8100,8101,8102,8103,8104,8105,8106,8107,8108,8109,8110,8111, # 8112 +8112,8113,8114,8115,8116,8117,8118,8119,8120,8121,8122,8123,8124,8125,8126,8127, # 8128 +8128,8129,8130,8131,8132,8133,8134,8135,8136,8137,8138,8139,8140,8141,8142,8143, # 8144 +8144,8145,8146,8147,8148,8149,8150,8151,8152,8153,8154,8155,8156,8157,8158,8159, # 8160 +8160,8161,8162,8163,8164,8165,8166,8167,8168,8169,8170,8171,8172,8173,8174,8175, # 8176 +8176,8177,8178,8179,8180,8181,8182,8183,8184,8185,8186,8187,8188,8189,8190,8191, # 8192 +8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8203,8204,8205,8206,8207, # 8208 +8208,8209,8210,8211,8212,8213,8214,8215,8216,8217,8218,8219,8220,8221,8222,8223, # 8224 +8224,8225,8226,8227,8228,8229,8230,8231,8232,8233,8234,8235,8236,8237,8238,8239, # 8240 +8240,8241,8242,8243,8244,8245,8246,8247,8248,8249,8250,8251,8252,8253,8254,8255, # 8256 +8256,8257,8258,8259,8260,8261,8262,8263,8264,8265,8266,8267,8268,8269,8270,8271) # 8272 + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/jpcntx.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/jpcntx.py new file mode 100644 index 0000000..59aeb6a --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/jpcntx.py @@ -0,0 +1,227 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .compat import wrap_ord + +NUM_OF_CATEGORY = 6 +DONT_KNOW = -1 +ENOUGH_REL_THRESHOLD = 100 +MAX_REL_THRESHOLD = 1000 +MINIMUM_DATA_THRESHOLD = 4 + +# This is hiragana 2-char sequence table, the number in each cell represents its frequency category +jp2CharContext = ( +(0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1), +(2,4,0,4,0,3,0,4,0,3,4,4,4,2,4,3,3,4,3,2,3,3,4,2,3,3,3,2,4,1,4,3,3,1,5,4,3,4,3,4,3,5,3,0,3,5,4,2,0,3,1,0,3,3,0,3,3,0,1,1,0,4,3,0,3,3,0,4,0,2,0,3,5,5,5,5,4,0,4,1,0,3,4), +(0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2), +(0,4,0,5,0,5,0,4,0,4,5,4,4,3,5,3,5,1,5,3,4,3,4,4,3,4,3,3,4,3,5,4,4,3,5,5,3,5,5,5,3,5,5,3,4,5,5,3,1,3,2,0,3,4,0,4,2,0,4,2,1,5,3,2,3,5,0,4,0,2,0,5,4,4,5,4,5,0,4,0,0,4,4), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,3,0,4,0,3,0,3,0,4,5,4,3,3,3,3,4,3,5,4,4,3,5,4,4,3,4,3,4,4,4,4,5,3,4,4,3,4,5,5,4,5,5,1,4,5,4,3,0,3,3,1,3,3,0,4,4,0,3,3,1,5,3,3,3,5,0,4,0,3,0,4,4,3,4,3,3,0,4,1,1,3,4), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,4,0,3,0,3,0,4,0,3,4,4,3,2,2,1,2,1,3,1,3,3,3,3,3,4,3,1,3,3,5,3,3,0,4,3,0,5,4,3,3,5,4,4,3,4,4,5,0,1,2,0,1,2,0,2,2,0,1,0,0,5,2,2,1,4,0,3,0,1,0,4,4,3,5,4,3,0,2,1,0,4,3), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,3,0,5,0,4,0,2,1,4,4,2,4,1,4,2,4,2,4,3,3,3,4,3,3,3,3,1,4,2,3,3,3,1,4,4,1,1,1,4,3,3,2,0,2,4,3,2,0,3,3,0,3,1,1,0,0,0,3,3,0,4,2,2,3,4,0,4,0,3,0,4,4,5,3,4,4,0,3,0,0,1,4), +(1,4,0,4,0,4,0,4,0,3,5,4,4,3,4,3,5,4,3,3,4,3,5,4,4,4,4,3,4,2,4,3,3,1,5,4,3,2,4,5,4,5,5,4,4,5,4,4,0,3,2,2,3,3,0,4,3,1,3,2,1,4,3,3,4,5,0,3,0,2,0,4,5,5,4,5,4,0,4,0,0,5,4), +(0,5,0,5,0,4,0,3,0,4,4,3,4,3,3,3,4,0,4,4,4,3,4,3,4,3,3,1,4,2,4,3,4,0,5,4,1,4,5,4,4,5,3,2,4,3,4,3,2,4,1,3,3,3,2,3,2,0,4,3,3,4,3,3,3,4,0,4,0,3,0,4,5,4,4,4,3,0,4,1,0,1,3), +(0,3,1,4,0,3,0,2,0,3,4,4,3,1,4,2,3,3,4,3,4,3,4,3,4,4,3,2,3,1,5,4,4,1,4,4,3,5,4,4,3,5,5,4,3,4,4,3,1,2,3,1,2,2,0,3,2,0,3,1,0,5,3,3,3,4,3,3,3,3,4,4,4,4,5,4,2,0,3,3,2,4,3), +(0,2,0,3,0,1,0,1,0,0,3,2,0,0,2,0,1,0,2,1,3,3,3,1,2,3,1,0,1,0,4,2,1,1,3,3,0,4,3,3,1,4,3,3,0,3,3,2,0,0,0,0,1,0,0,2,0,0,0,0,0,4,1,0,2,3,2,2,2,1,3,3,3,4,4,3,2,0,3,1,0,3,3), +(0,4,0,4,0,3,0,3,0,4,4,4,3,3,3,3,3,3,4,3,4,2,4,3,4,3,3,2,4,3,4,5,4,1,4,5,3,5,4,5,3,5,4,0,3,5,5,3,1,3,3,2,2,3,0,3,4,1,3,3,2,4,3,3,3,4,0,4,0,3,0,4,5,4,4,5,3,0,4,1,0,3,4), +(0,2,0,3,0,3,0,0,0,2,2,2,1,0,1,0,0,0,3,0,3,0,3,0,1,3,1,0,3,1,3,3,3,1,3,3,3,0,1,3,1,3,4,0,0,3,1,1,0,3,2,0,0,0,0,1,3,0,1,0,0,3,3,2,0,3,0,0,0,0,0,3,4,3,4,3,3,0,3,0,0,2,3), +(2,3,0,3,0,2,0,1,0,3,3,4,3,1,3,1,1,1,3,1,4,3,4,3,3,3,0,0,3,1,5,4,3,1,4,3,2,5,5,4,4,4,4,3,3,4,4,4,0,2,1,1,3,2,0,1,2,0,0,1,0,4,1,3,3,3,0,3,0,1,0,4,4,4,5,5,3,0,2,0,0,4,4), +(0,2,0,1,0,3,1,3,0,2,3,3,3,0,3,1,0,0,3,0,3,2,3,1,3,2,1,1,0,0,4,2,1,0,2,3,1,4,3,2,0,4,4,3,1,3,1,3,0,1,0,0,1,0,0,0,1,0,0,0,0,4,1,1,1,2,0,3,0,0,0,3,4,2,4,3,2,0,1,0,0,3,3), +(0,1,0,4,0,5,0,4,0,2,4,4,2,3,3,2,3,3,5,3,3,3,4,3,4,2,3,0,4,3,3,3,4,1,4,3,2,1,5,5,3,4,5,1,3,5,4,2,0,3,3,0,1,3,0,4,2,0,1,3,1,4,3,3,3,3,0,3,0,1,0,3,4,4,4,5,5,0,3,0,1,4,5), +(0,2,0,3,0,3,0,0,0,2,3,1,3,0,4,0,1,1,3,0,3,4,3,2,3,1,0,3,3,2,3,1,3,0,2,3,0,2,1,4,1,2,2,0,0,3,3,0,0,2,0,0,0,1,0,0,0,0,2,2,0,3,2,1,3,3,0,2,0,2,0,0,3,3,1,2,4,0,3,0,2,2,3), +(2,4,0,5,0,4,0,4,0,2,4,4,4,3,4,3,3,3,1,2,4,3,4,3,4,4,5,0,3,3,3,3,2,0,4,3,1,4,3,4,1,4,4,3,3,4,4,3,1,2,3,0,4,2,0,4,1,0,3,3,0,4,3,3,3,4,0,4,0,2,0,3,5,3,4,5,2,0,3,0,0,4,5), +(0,3,0,4,0,1,0,1,0,1,3,2,2,1,3,0,3,0,2,0,2,0,3,0,2,0,0,0,1,0,1,1,0,0,3,1,0,0,0,4,0,3,1,0,2,1,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,4,2,2,3,1,0,3,0,0,0,1,4,4,4,3,0,0,4,0,0,1,4), +(1,4,1,5,0,3,0,3,0,4,5,4,4,3,5,3,3,4,4,3,4,1,3,3,3,3,2,1,4,1,5,4,3,1,4,4,3,5,4,4,3,5,4,3,3,4,4,4,0,3,3,1,2,3,0,3,1,0,3,3,0,5,4,4,4,4,4,4,3,3,5,4,4,3,3,5,4,0,3,2,0,4,4), +(0,2,0,3,0,1,0,0,0,1,3,3,3,2,4,1,3,0,3,1,3,0,2,2,1,1,0,0,2,0,4,3,1,0,4,3,0,4,4,4,1,4,3,1,1,3,3,1,0,2,0,0,1,3,0,0,0,0,2,0,0,4,3,2,4,3,5,4,3,3,3,4,3,3,4,3,3,0,2,1,0,3,3), +(0,2,0,4,0,3,0,2,0,2,5,5,3,4,4,4,4,1,4,3,3,0,4,3,4,3,1,3,3,2,4,3,0,3,4,3,0,3,4,4,2,4,4,0,4,5,3,3,2,2,1,1,1,2,0,1,5,0,3,3,2,4,3,3,3,4,0,3,0,2,0,4,4,3,5,5,0,0,3,0,2,3,3), +(0,3,0,4,0,3,0,1,0,3,4,3,3,1,3,3,3,0,3,1,3,0,4,3,3,1,1,0,3,0,3,3,0,0,4,4,0,1,5,4,3,3,5,0,3,3,4,3,0,2,0,1,1,1,0,1,3,0,1,2,1,3,3,2,3,3,0,3,0,1,0,1,3,3,4,4,1,0,1,2,2,1,3), +(0,1,0,4,0,4,0,3,0,1,3,3,3,2,3,1,1,0,3,0,3,3,4,3,2,4,2,0,1,0,4,3,2,0,4,3,0,5,3,3,2,4,4,4,3,3,3,4,0,1,3,0,0,1,0,0,1,0,0,0,0,4,2,3,3,3,0,3,0,0,0,4,4,4,5,3,2,0,3,3,0,3,5), +(0,2,0,3,0,0,0,3,0,1,3,0,2,0,0,0,1,0,3,1,1,3,3,0,0,3,0,0,3,0,2,3,1,0,3,1,0,3,3,2,0,4,2,2,0,2,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,2,1,2,0,1,0,1,0,0,0,1,3,1,2,0,0,0,1,0,0,1,4), +(0,3,0,3,0,5,0,1,0,2,4,3,1,3,3,2,1,1,5,2,1,0,5,1,2,0,0,0,3,3,2,2,3,2,4,3,0,0,3,3,1,3,3,0,2,5,3,4,0,3,3,0,1,2,0,2,2,0,3,2,0,2,2,3,3,3,0,2,0,1,0,3,4,4,2,5,4,0,3,0,0,3,5), +(0,3,0,3,0,3,0,1,0,3,3,3,3,0,3,0,2,0,2,1,1,0,2,0,1,0,0,0,2,1,0,0,1,0,3,2,0,0,3,3,1,2,3,1,0,3,3,0,0,1,0,0,0,0,0,2,0,0,0,0,0,2,3,1,2,3,0,3,0,1,0,3,2,1,0,4,3,0,1,1,0,3,3), +(0,4,0,5,0,3,0,3,0,4,5,5,4,3,5,3,4,3,5,3,3,2,5,3,4,4,4,3,4,3,4,5,5,3,4,4,3,4,4,5,4,4,4,3,4,5,5,4,2,3,4,2,3,4,0,3,3,1,4,3,2,4,3,3,5,5,0,3,0,3,0,5,5,5,5,4,4,0,4,0,1,4,4), +(0,4,0,4,0,3,0,3,0,3,5,4,4,2,3,2,5,1,3,2,5,1,4,2,3,2,3,3,4,3,3,3,3,2,5,4,1,3,3,5,3,4,4,0,4,4,3,1,1,3,1,0,2,3,0,2,3,0,3,0,0,4,3,1,3,4,0,3,0,2,0,4,4,4,3,4,5,0,4,0,0,3,4), +(0,3,0,3,0,3,1,2,0,3,4,4,3,3,3,0,2,2,4,3,3,1,3,3,3,1,1,0,3,1,4,3,2,3,4,4,2,4,4,4,3,4,4,3,2,4,4,3,1,3,3,1,3,3,0,4,1,0,2,2,1,4,3,2,3,3,5,4,3,3,5,4,4,3,3,0,4,0,3,2,2,4,4), +(0,2,0,1,0,0,0,0,0,1,2,1,3,0,0,0,0,0,2,0,1,2,1,0,0,1,0,0,0,0,3,0,0,1,0,1,1,3,1,0,0,0,1,1,0,1,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,1,2,2,0,3,4,0,0,0,1,1,0,0,1,0,0,0,0,0,1,1), +(0,1,0,0,0,1,0,0,0,0,4,0,4,1,4,0,3,0,4,0,3,0,4,0,3,0,3,0,4,1,5,1,4,0,0,3,0,5,0,5,2,0,1,0,0,0,2,1,4,0,1,3,0,0,3,0,0,3,1,1,4,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0), +(1,4,0,5,0,3,0,2,0,3,5,4,4,3,4,3,5,3,4,3,3,0,4,3,3,3,3,3,3,2,4,4,3,1,3,4,4,5,4,4,3,4,4,1,3,5,4,3,3,3,1,2,2,3,3,1,3,1,3,3,3,5,3,3,4,5,0,3,0,3,0,3,4,3,4,4,3,0,3,0,2,4,3), +(0,1,0,4,0,0,0,0,0,1,4,0,4,1,4,2,4,0,3,0,1,0,1,0,0,0,0,0,2,0,3,1,1,1,0,3,0,0,0,1,2,1,0,0,1,1,1,1,0,1,0,0,0,1,0,0,3,0,0,0,0,3,2,0,2,2,0,1,0,0,0,2,3,2,3,3,0,0,0,0,2,1,0), +(0,5,1,5,0,3,0,3,0,5,4,4,5,1,5,3,3,0,4,3,4,3,5,3,4,3,3,2,4,3,4,3,3,0,3,3,1,4,4,3,4,4,4,3,4,5,5,3,2,3,1,1,3,3,1,3,1,1,3,3,2,4,5,3,3,5,0,4,0,3,0,4,4,3,5,3,3,0,3,4,0,4,3), +(0,5,0,5,0,3,0,2,0,4,4,3,5,2,4,3,3,3,4,4,4,3,5,3,5,3,3,1,4,0,4,3,3,0,3,3,0,4,4,4,4,5,4,3,3,5,5,3,2,3,1,2,3,2,0,1,0,0,3,2,2,4,4,3,1,5,0,4,0,3,0,4,3,1,3,2,1,0,3,3,0,3,3), +(0,4,0,5,0,5,0,4,0,4,5,5,5,3,4,3,3,2,5,4,4,3,5,3,5,3,4,0,4,3,4,4,3,2,4,4,3,4,5,4,4,5,5,0,3,5,5,4,1,3,3,2,3,3,1,3,1,0,4,3,1,4,4,3,4,5,0,4,0,2,0,4,3,4,4,3,3,0,4,0,0,5,5), +(0,4,0,4,0,5,0,1,1,3,3,4,4,3,4,1,3,0,5,1,3,0,3,1,3,1,1,0,3,0,3,3,4,0,4,3,0,4,4,4,3,4,4,0,3,5,4,1,0,3,0,0,2,3,0,3,1,0,3,1,0,3,2,1,3,5,0,3,0,1,0,3,2,3,3,4,4,0,2,2,0,4,4), +(2,4,0,5,0,4,0,3,0,4,5,5,4,3,5,3,5,3,5,3,5,2,5,3,4,3,3,4,3,4,5,3,2,1,5,4,3,2,3,4,5,3,4,1,2,5,4,3,0,3,3,0,3,2,0,2,3,0,4,1,0,3,4,3,3,5,0,3,0,1,0,4,5,5,5,4,3,0,4,2,0,3,5), +(0,5,0,4,0,4,0,2,0,5,4,3,4,3,4,3,3,3,4,3,4,2,5,3,5,3,4,1,4,3,4,4,4,0,3,5,0,4,4,4,4,5,3,1,3,4,5,3,3,3,3,3,3,3,0,2,2,0,3,3,2,4,3,3,3,5,3,4,1,3,3,5,3,2,0,0,0,0,4,3,1,3,3), +(0,1,0,3,0,3,0,1,0,1,3,3,3,2,3,3,3,0,3,0,0,0,3,1,3,0,0,0,2,2,2,3,0,0,3,2,0,1,2,4,1,3,3,0,0,3,3,3,0,1,0,0,2,1,0,0,3,0,3,1,0,3,0,0,1,3,0,2,0,1,0,3,3,1,3,3,0,0,1,1,0,3,3), +(0,2,0,3,0,2,1,4,0,2,2,3,1,1,3,1,1,0,2,0,3,1,2,3,1,3,0,0,1,0,4,3,2,3,3,3,1,4,2,3,3,3,3,1,0,3,1,4,0,1,1,0,1,2,0,1,1,0,1,1,0,3,1,3,2,2,0,1,0,0,0,2,3,3,3,1,0,0,0,0,0,2,3), +(0,5,0,4,0,5,0,2,0,4,5,5,3,3,4,3,3,1,5,4,4,2,4,4,4,3,4,2,4,3,5,5,4,3,3,4,3,3,5,5,4,5,5,1,3,4,5,3,1,4,3,1,3,3,0,3,3,1,4,3,1,4,5,3,3,5,0,4,0,3,0,5,3,3,1,4,3,0,4,0,1,5,3), +(0,5,0,5,0,4,0,2,0,4,4,3,4,3,3,3,3,3,5,4,4,4,4,4,4,5,3,3,5,2,4,4,4,3,4,4,3,3,4,4,5,5,3,3,4,3,4,3,3,4,3,3,3,3,1,2,2,1,4,3,3,5,4,4,3,4,0,4,0,3,0,4,4,4,4,4,1,0,4,2,0,2,4), +(0,4,0,4,0,3,0,1,0,3,5,2,3,0,3,0,2,1,4,2,3,3,4,1,4,3,3,2,4,1,3,3,3,0,3,3,0,0,3,3,3,5,3,3,3,3,3,2,0,2,0,0,2,0,0,2,0,0,1,0,0,3,1,2,2,3,0,3,0,2,0,4,4,3,3,4,1,0,3,0,0,2,4), +(0,0,0,4,0,0,0,0,0,0,1,0,1,0,2,0,0,0,0,0,1,0,2,0,1,0,0,0,0,0,3,1,3,0,3,2,0,0,0,1,0,3,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,0,2,0,0,0,0,0,0,2), +(0,2,1,3,0,2,0,2,0,3,3,3,3,1,3,1,3,3,3,3,3,3,4,2,2,1,2,1,4,0,4,3,1,3,3,3,2,4,3,5,4,3,3,3,3,3,3,3,0,1,3,0,2,0,0,1,0,0,1,0,0,4,2,0,2,3,0,3,3,0,3,3,4,2,3,1,4,0,1,2,0,2,3), +(0,3,0,3,0,1,0,3,0,2,3,3,3,0,3,1,2,0,3,3,2,3,3,2,3,2,3,1,3,0,4,3,2,0,3,3,1,4,3,3,2,3,4,3,1,3,3,1,1,0,1,1,0,1,0,1,0,1,0,0,0,4,1,1,0,3,0,3,1,0,2,3,3,3,3,3,1,0,0,2,0,3,3), +(0,0,0,0,0,0,0,0,0,0,3,0,2,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,3,0,3,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,2,0,2,3,0,0,0,0,0,0,0,0,3), +(0,2,0,3,1,3,0,3,0,2,3,3,3,1,3,1,3,1,3,1,3,3,3,1,3,0,2,3,1,1,4,3,3,2,3,3,1,2,2,4,1,3,3,0,1,4,2,3,0,1,3,0,3,0,0,1,3,0,2,0,0,3,3,2,1,3,0,3,0,2,0,3,4,4,4,3,1,0,3,0,0,3,3), +(0,2,0,1,0,2,0,0,0,1,3,2,2,1,3,0,1,1,3,0,3,2,3,1,2,0,2,0,1,1,3,3,3,0,3,3,1,1,2,3,2,3,3,1,2,3,2,0,0,1,0,0,0,0,0,0,3,0,1,0,0,2,1,2,1,3,0,3,0,0,0,3,4,4,4,3,2,0,2,0,0,2,4), +(0,0,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,3,1,0,0,0,0,0,0,0,3), +(0,3,0,3,0,2,0,3,0,3,3,3,2,3,2,2,2,0,3,1,3,3,3,2,3,3,0,0,3,0,3,2,2,0,2,3,1,4,3,4,3,3,2,3,1,5,4,4,0,3,1,2,1,3,0,3,1,1,2,0,2,3,1,3,1,3,0,3,0,1,0,3,3,4,4,2,1,0,2,1,0,2,4), +(0,1,0,3,0,1,0,2,0,1,4,2,5,1,4,0,2,0,2,1,3,1,4,0,2,1,0,0,2,1,4,1,1,0,3,3,0,5,1,3,2,3,3,1,0,3,2,3,0,1,0,0,0,0,0,0,1,0,0,0,0,4,0,1,0,3,0,2,0,1,0,3,3,3,4,3,3,0,0,0,0,2,3), +(0,0,0,1,0,0,0,0,0,0,2,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,1,0,0,0,0,0,3), +(0,1,0,3,0,4,0,3,0,2,4,3,1,0,3,2,2,1,3,1,2,2,3,1,1,1,2,1,3,0,1,2,0,1,3,2,1,3,0,5,5,1,0,0,1,3,2,1,0,3,0,0,1,0,0,0,0,0,3,4,0,1,1,1,3,2,0,2,0,1,0,2,3,3,1,2,3,0,1,0,1,0,4), +(0,0,0,1,0,3,0,3,0,2,2,1,0,0,4,0,3,0,3,1,3,0,3,0,3,0,1,0,3,0,3,1,3,0,3,3,0,0,1,2,1,1,1,0,1,2,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,2,2,1,2,0,0,2,0,0,0,0,2,3,3,3,3,0,0,0,0,1,4), +(0,0,0,3,0,3,0,0,0,0,3,1,1,0,3,0,1,0,2,0,1,0,0,0,0,0,0,0,1,0,3,0,2,0,2,3,0,0,2,2,3,1,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,2,3), +(2,4,0,5,0,5,0,4,0,3,4,3,3,3,4,3,3,3,4,3,4,4,5,4,5,5,5,2,3,0,5,5,4,1,5,4,3,1,5,4,3,4,4,3,3,4,3,3,0,3,2,0,2,3,0,3,0,0,3,3,0,5,3,2,3,3,0,3,0,3,0,3,4,5,4,5,3,0,4,3,0,3,4), +(0,3,0,3,0,3,0,3,0,3,3,4,3,2,3,2,3,0,4,3,3,3,3,3,3,3,3,0,3,2,4,3,3,1,3,4,3,4,4,4,3,4,4,3,2,4,4,1,0,2,0,0,1,1,0,2,0,0,3,1,0,5,3,2,1,3,0,3,0,1,2,4,3,2,4,3,3,0,3,2,0,4,4), +(0,3,0,3,0,1,0,0,0,1,4,3,3,2,3,1,3,1,4,2,3,2,4,2,3,4,3,0,2,2,3,3,3,0,3,3,3,0,3,4,1,3,3,0,3,4,3,3,0,1,1,0,1,0,0,0,4,0,3,0,0,3,1,2,1,3,0,4,0,1,0,4,3,3,4,3,3,0,2,0,0,3,3), +(0,3,0,4,0,1,0,3,0,3,4,3,3,0,3,3,3,1,3,1,3,3,4,3,3,3,0,0,3,1,5,3,3,1,3,3,2,5,4,3,3,4,5,3,2,5,3,4,0,1,0,0,0,0,0,2,0,0,1,1,0,4,2,2,1,3,0,3,0,2,0,4,4,3,5,3,2,0,1,1,0,3,4), +(0,5,0,4,0,5,0,2,0,4,4,3,3,2,3,3,3,1,4,3,4,1,5,3,4,3,4,0,4,2,4,3,4,1,5,4,0,4,4,4,4,5,4,1,3,5,4,2,1,4,1,1,3,2,0,3,1,0,3,2,1,4,3,3,3,4,0,4,0,3,0,4,4,4,3,3,3,0,4,2,0,3,4), +(1,4,0,4,0,3,0,1,0,3,3,3,1,1,3,3,2,2,3,3,1,0,3,2,2,1,2,0,3,1,2,1,2,0,3,2,0,2,2,3,3,4,3,0,3,3,1,2,0,1,1,3,1,2,0,0,3,0,1,1,0,3,2,2,3,3,0,3,0,0,0,2,3,3,4,3,3,0,1,0,0,1,4), +(0,4,0,4,0,4,0,0,0,3,4,4,3,1,4,2,3,2,3,3,3,1,4,3,4,0,3,0,4,2,3,3,2,2,5,4,2,1,3,4,3,4,3,1,3,3,4,2,0,2,1,0,3,3,0,0,2,0,3,1,0,4,4,3,4,3,0,4,0,1,0,2,4,4,4,4,4,0,3,2,0,3,3), +(0,0,0,1,0,4,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,3,2,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2), +(0,2,0,3,0,4,0,4,0,1,3,3,3,0,4,0,2,1,2,1,1,1,2,0,3,1,1,0,1,0,3,1,0,0,3,3,2,0,1,1,0,0,0,0,0,1,0,2,0,2,2,0,3,1,0,0,1,0,1,1,0,1,2,0,3,0,0,0,0,1,0,0,3,3,4,3,1,0,1,0,3,0,2), +(0,0,0,3,0,5,0,0,0,0,1,0,2,0,3,1,0,1,3,0,0,0,2,0,0,0,1,0,0,0,1,1,0,0,4,0,0,0,2,3,0,1,4,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,1,0,0,0,0,0,0,0,2,0,0,3,0,0,0,0,0,3), +(0,2,0,5,0,5,0,1,0,2,4,3,3,2,5,1,3,2,3,3,3,0,4,1,2,0,3,0,4,0,2,2,1,1,5,3,0,0,1,4,2,3,2,0,3,3,3,2,0,2,4,1,1,2,0,1,1,0,3,1,0,1,3,1,2,3,0,2,0,0,0,1,3,5,4,4,4,0,3,0,0,1,3), +(0,4,0,5,0,4,0,4,0,4,5,4,3,3,4,3,3,3,4,3,4,4,5,3,4,5,4,2,4,2,3,4,3,1,4,4,1,3,5,4,4,5,5,4,4,5,5,5,2,3,3,1,4,3,1,3,3,0,3,3,1,4,3,4,4,4,0,3,0,4,0,3,3,4,4,5,0,0,4,3,0,4,5), +(0,4,0,4,0,3,0,3,0,3,4,4,4,3,3,2,4,3,4,3,4,3,5,3,4,3,2,1,4,2,4,4,3,1,3,4,2,4,5,5,3,4,5,4,1,5,4,3,0,3,2,2,3,2,1,3,1,0,3,3,3,5,3,3,3,5,4,4,2,3,3,4,3,3,3,2,1,0,3,2,1,4,3), +(0,4,0,5,0,4,0,3,0,3,5,5,3,2,4,3,4,0,5,4,4,1,4,4,4,3,3,3,4,3,5,5,2,3,3,4,1,2,5,5,3,5,5,2,3,5,5,4,0,3,2,0,3,3,1,1,5,1,4,1,0,4,3,2,3,5,0,4,0,3,0,5,4,3,4,3,0,0,4,1,0,4,4), +(1,3,0,4,0,2,0,2,0,2,5,5,3,3,3,3,3,0,4,2,3,4,4,4,3,4,0,0,3,4,5,4,3,3,3,3,2,5,5,4,5,5,5,4,3,5,5,5,1,3,1,0,1,0,0,3,2,0,4,2,0,5,2,3,2,4,1,3,0,3,0,4,5,4,5,4,3,0,4,2,0,5,4), +(0,3,0,4,0,5,0,3,0,3,4,4,3,2,3,2,3,3,3,3,3,2,4,3,3,2,2,0,3,3,3,3,3,1,3,3,3,0,4,4,3,4,4,1,1,4,4,2,0,3,1,0,1,1,0,4,1,0,2,3,1,3,3,1,3,4,0,3,0,1,0,3,1,3,0,0,1,0,2,0,0,4,4), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,3,0,3,0,2,0,3,0,1,5,4,3,3,3,1,4,2,1,2,3,4,4,2,4,4,5,0,3,1,4,3,4,0,4,3,3,3,2,3,2,5,3,4,3,2,2,3,0,0,3,0,2,1,0,1,2,0,0,0,0,2,1,1,3,1,0,2,0,4,0,3,4,4,4,5,2,0,2,0,0,1,3), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,0,0,1,1,0,0,0,4,2,1,1,0,1,0,3,2,0,0,3,1,1,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,1,0,0,0,2,0,0,0,1,4,0,4,2,1,0,0,0,0,0,1), +(0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,1,0,0,0,0,3,1,0,0,0,2,0,2,1,0,0,1,2,1,0,1,1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,1,3,1,0,0,0,0,0,1,0,0,2,1,0,0,0,0,0,0,0,0,2), +(0,4,0,4,0,4,0,3,0,4,4,3,4,2,4,3,2,0,4,4,4,3,5,3,5,3,3,2,4,2,4,3,4,3,1,4,0,2,3,4,4,4,3,3,3,4,4,4,3,4,1,3,4,3,2,1,2,1,3,3,3,4,4,3,3,5,0,4,0,3,0,4,3,3,3,2,1,0,3,0,0,3,3), +(0,4,0,3,0,3,0,3,0,3,5,5,3,3,3,3,4,3,4,3,3,3,4,4,4,3,3,3,3,4,3,5,3,3,1,3,2,4,5,5,5,5,4,3,4,5,5,3,2,2,3,3,3,3,2,3,3,1,2,3,2,4,3,3,3,4,0,4,0,2,0,4,3,2,2,1,2,0,3,0,0,4,1), +) + +class JapaneseContextAnalysis: + def __init__(self): + self.reset() + + def reset(self): + self._mTotalRel = 0 # total sequence received + # category counters, each interger counts sequence in its category + self._mRelSample = [0] * NUM_OF_CATEGORY + # if last byte in current buffer is not the last byte of a character, + # we need to know how many bytes to skip in next buffer + self._mNeedToSkipCharNum = 0 + self._mLastCharOrder = -1 # The order of previous char + # If this flag is set to True, detection is done and conclusion has + # been made + self._mDone = False + + def feed(self, aBuf, aLen): + if self._mDone: + return + + # The buffer we got is byte oriented, and a character may span in more than one + # buffers. In case the last one or two byte in last buffer is not + # complete, we record how many byte needed to complete that character + # and skip these bytes here. We can choose to record those bytes as + # well and analyse the character once it is complete, but since a + # character will not make much difference, by simply skipping + # this character will simply our logic and improve performance. + i = self._mNeedToSkipCharNum + while i < aLen: + order, charLen = self.get_order(aBuf[i:i + 2]) + i += charLen + if i > aLen: + self._mNeedToSkipCharNum = i - aLen + self._mLastCharOrder = -1 + else: + if (order != -1) and (self._mLastCharOrder != -1): + self._mTotalRel += 1 + if self._mTotalRel > MAX_REL_THRESHOLD: + self._mDone = True + break + self._mRelSample[jp2CharContext[self._mLastCharOrder][order]] += 1 + self._mLastCharOrder = order + + def got_enough_data(self): + return self._mTotalRel > ENOUGH_REL_THRESHOLD + + def get_confidence(self): + # This is just one way to calculate confidence. It works well for me. + if self._mTotalRel > MINIMUM_DATA_THRESHOLD: + return (self._mTotalRel - self._mRelSample[0]) / self._mTotalRel + else: + return DONT_KNOW + + def get_order(self, aBuf): + return -1, 1 + +class SJISContextAnalysis(JapaneseContextAnalysis): + def __init__(self): + self.charset_name = "SHIFT_JIS" + + def get_charset_name(self): + return self.charset_name + + def get_order(self, aBuf): + if not aBuf: + return -1, 1 + # find out current char's byte length + first_char = wrap_ord(aBuf[0]) + if ((0x81 <= first_char <= 0x9F) or (0xE0 <= first_char <= 0xFC)): + charLen = 2 + if (first_char == 0x87) or (0xFA <= first_char <= 0xFC): + self.charset_name = "CP932" + else: + charLen = 1 + + # return its order if it is hiragana + if len(aBuf) > 1: + second_char = wrap_ord(aBuf[1]) + if (first_char == 202) and (0x9F <= second_char <= 0xF1): + return second_char - 0x9F, charLen + + return -1, charLen + +class EUCJPContextAnalysis(JapaneseContextAnalysis): + def get_order(self, aBuf): + if not aBuf: + return -1, 1 + # find out current char's byte length + first_char = wrap_ord(aBuf[0]) + if (first_char == 0x8E) or (0xA1 <= first_char <= 0xFE): + charLen = 2 + elif first_char == 0x8F: + charLen = 3 + else: + charLen = 1 + + # return its order if it is hiragana + if len(aBuf) > 1: + second_char = wrap_ord(aBuf[1]) + if (first_char == 0xA4) and (0xA1 <= second_char <= 0xF3): + return second_char - 0xA1, charLen + + return -1, charLen + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langbulgarianmodel.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langbulgarianmodel.py new file mode 100644 index 0000000..e5788fc --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langbulgarianmodel.py @@ -0,0 +1,229 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +# this table is modified base on win1251BulgarianCharToOrderMap, so +# only number <64 is sure valid + +Latin5_BulgarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 +110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 +253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 +116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 +194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209, # 80 +210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225, # 90 + 81,226,227,228,229,230,105,231,232,233,234,235,236, 45,237,238, # a0 + 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # b0 + 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,239, 67,240, 60, 56, # c0 + 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # d0 + 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,241, 42, 16, # e0 + 62,242,243,244, 58,245, 98,246,247,248,249,250,251, 91,252,253, # f0 +) + +win1251BulgarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 +110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 +253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 +116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 +206,207,208,209,210,211,212,213,120,214,215,216,217,218,219,220, # 80 +221, 78, 64, 83,121, 98,117,105,222,223,224,225,226,227,228,229, # 90 + 88,230,231,232,233,122, 89,106,234,235,236,237,238, 45,239,240, # a0 + 73, 80,118,114,241,242,243,244,245, 62, 58,246,247,248,249,250, # b0 + 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # c0 + 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,251, 67,252, 60, 56, # d0 + 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # e0 + 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,253, 42, 16, # f0 +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 96.9392% +# first 1024 sequences:3.0618% +# rest sequences: 0.2992% +# negative sequences: 0.0020% +BulgarianLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,2,2,1,2,2, +3,1,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,0,1, +0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3,3,0,3,1,0, +0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,2,2,1,3,3,3,3,2,2,2,1,1,2,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,2,3,2,2,3,3,1,1,2,3,3,2,3,3,3,3,2,1,2,0,2,0,3,0,0, +0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,1,3,3,3,3,3,2,3,2,3,3,3,3,3,2,3,3,1,3,0,3,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,1,3,3,2,3,3,3,1,3,3,2,3,2,2,2,0,0,2,0,2,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,3,3,1,2,2,3,2,1,1,2,0,2,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,2,3,3,1,2,3,2,2,2,3,3,3,3,3,2,2,3,1,2,0,2,1,2,0,0, +0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,1,3,3,3,3,3,2,3,3,3,2,3,3,2,3,2,2,2,3,1,2,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,1,1,1,2,2,1,3,1,3,2,2,3,0,0,1,0,1,0,1,0,0, +0,0,0,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,2,2,3,2,2,3,1,2,1,1,1,2,3,1,3,1,2,2,0,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,1,3,2,2,3,3,1,2,3,1,1,3,3,3,3,1,2,2,1,1,1,0,2,0,2,0,1, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,2,2,3,3,3,2,2,1,1,2,0,2,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,0,1,2,1,3,3,2,3,3,3,3,3,2,3,2,1,0,3,1,2,1,2,1,2,3,2,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,1,3,3,2,3,3,2,2,2,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,0,3,3,3,3,3,2,1,1,2,1,3,3,0,3,1,1,1,1,3,2,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,1,1,3,1,3,3,2,3,2,2,2,3,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,3,2,2,3,2,1,1,1,1,1,3,1,3,1,1,0,0,0,1,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,2,0,3,2,0,3,0,2,0,0,2,1,3,1,0,0,1,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,2,1,1,1,1,2,1,1,2,1,1,1,2,2,1,2,1,1,1,0,1,1,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,2,1,3,1,1,2,1,3,2,1,1,0,1,2,3,2,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,2,2,1,0,1,0,0,1,0,0,0,2,1,0,3,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,2,3,2,3,3,1,3,2,1,1,1,2,1,1,2,1,3,0,1,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,2,2,3,3,2,3,2,2,2,3,1,2,2,1,1,2,1,1,2,2,0,1,1,0,1,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,3,1,0,2,2,1,3,2,1,0,0,2,0,2,0,1,0,0,0,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,1,2,0,2,3,1,2,3,2,0,1,3,1,2,1,1,1,0,0,1,0,0,2,2,2,3, +2,2,2,2,1,2,1,1,2,2,1,1,2,0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,1,0,1, +3,3,3,3,3,2,1,2,2,1,2,0,2,0,1,0,1,2,1,2,1,1,0,0,0,1,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,2,3,3,1,1,3,1,0,3,2,1,0,0,0,1,2,0,2,0,1,0,0,0,1,0,1,2,1,2,2, +1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,0,1,2,1,1,1,0,0,0,0,0,1,1,0,0, +3,1,0,1,0,2,3,2,2,2,3,2,2,2,2,2,1,0,2,1,2,1,1,1,0,1,2,1,2,2,2,1, +1,1,2,2,2,2,1,2,1,1,0,1,2,1,2,2,2,1,1,1,0,1,1,1,1,2,0,1,0,0,0,0, +2,3,2,3,3,0,0,2,1,0,2,1,0,0,0,0,2,3,0,2,0,0,0,0,0,1,0,0,2,0,1,2, +2,1,2,1,2,2,1,1,1,2,1,1,1,0,1,2,2,1,1,1,1,1,0,1,1,1,0,0,1,2,0,0, +3,3,2,2,3,0,2,3,1,1,2,0,0,0,1,0,0,2,0,2,0,0,0,1,0,1,0,1,2,0,2,2, +1,1,1,1,2,1,0,1,2,2,2,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,1,0,0, +2,3,2,3,3,0,0,3,0,1,1,0,1,0,0,0,2,2,1,2,0,0,0,0,0,0,0,0,2,0,1,2, +2,2,1,1,1,1,1,2,2,2,1,0,2,0,1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0, +3,3,3,3,2,2,2,2,2,0,2,1,1,1,1,2,1,2,1,1,0,2,0,1,0,1,0,0,2,0,1,2, +1,1,1,1,1,1,1,2,2,1,1,0,2,0,1,0,2,0,0,1,1,1,0,0,2,0,0,0,1,1,0,0, +2,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0,0,0,0,1,2,0,1,2, +2,2,2,1,1,2,1,1,2,2,2,1,2,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,0,0, +2,3,3,3,3,0,2,2,0,2,1,0,0,0,1,1,1,2,0,2,0,0,0,3,0,0,0,0,2,0,2,2, +1,1,1,2,1,2,1,1,2,2,2,1,2,0,1,1,1,0,1,1,1,1,0,2,1,0,0,0,1,1,0,0, +2,3,3,3,3,0,2,1,0,0,2,0,0,0,0,0,1,2,0,2,0,0,0,0,0,0,0,0,2,0,1,2, +1,1,1,2,1,1,1,1,2,2,2,0,1,0,1,1,1,0,0,1,1,1,0,0,1,0,0,0,0,1,0,0, +3,3,2,2,3,0,1,0,1,0,0,0,0,0,0,0,1,1,0,3,0,0,0,0,0,0,0,0,1,0,2,2, +1,1,1,1,1,2,1,1,2,2,1,2,2,1,0,1,1,1,1,1,0,1,0,0,1,0,0,0,1,1,0,0, +3,1,0,1,0,2,2,2,2,3,2,1,1,1,2,3,0,0,1,0,2,1,1,0,1,1,1,1,2,1,1,1, +1,2,2,1,2,1,2,2,1,1,0,1,2,1,2,2,1,1,1,0,0,1,1,1,2,1,0,1,0,0,0,0, +2,1,0,1,0,3,1,2,2,2,2,1,2,2,1,1,1,0,2,1,2,2,1,1,2,1,1,0,2,1,1,1, +1,2,2,2,2,2,2,2,1,2,0,1,1,0,2,1,1,1,1,1,0,0,1,1,1,1,0,1,0,0,0,0, +2,1,1,1,1,2,2,2,2,1,2,2,2,1,2,2,1,1,2,1,2,3,2,2,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,3,2,0,1,2,0,1,2,1,1,0,1,0,1,2,1,2,0,0,0,1,1,0,0,0,1,0,0,2, +1,1,0,0,1,1,0,1,1,1,1,0,2,0,1,1,1,0,0,1,1,0,0,0,0,1,0,0,0,1,0,0, +2,0,0,0,0,1,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,2,1,1,1, +1,2,2,2,2,1,1,2,1,2,1,1,1,0,2,1,2,1,1,1,0,2,1,1,1,1,0,1,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, +1,1,0,1,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,3,2,0,0,0,0,1,0,0,0,0,0,0,1,1,0,2,0,0,0,0,0,0,0,0,1,0,1,2, +1,1,1,1,1,1,0,0,2,2,2,2,2,0,1,1,0,1,1,1,1,1,0,0,1,0,0,0,1,1,0,1, +2,3,1,2,1,0,1,1,0,2,2,2,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,1,2, +1,1,1,1,2,1,1,1,1,1,1,1,1,0,1,1,0,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0, +2,2,2,2,2,0,0,2,0,0,2,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,0,2,2, +1,1,1,1,1,0,0,1,2,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,2,0,1,1,0,0,0,1,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,1,1, +0,0,0,1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,3,2,0,0,1,0,0,1,0,0,0,0,0,0,1,0,2,0,0,0,1,0,0,0,0,0,0,0,2, +1,1,0,0,1,0,0,0,1,1,0,0,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,1,2,2,2,1,2,1,2,2,1,1,2,1,1,1,0,1,1,1,1,2,0,1,0,1,1,1,1,0,1,1, +1,1,2,1,1,1,1,1,1,0,0,1,2,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0, +1,0,0,1,3,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,1,0,0,1,0,2,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,2,0,0,1, +0,2,0,1,0,0,1,1,2,0,1,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,1,1,0,2,1,0,1,1,1,0,0,1,0,2,0,1,0,0,0,0,0,0,0,0,0,1, +0,1,0,0,1,0,0,0,1,1,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,2,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, +0,1,0,1,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,0,1,2,1,1,1,1,1,1,2,2,1,0,0,1,0,1,0,0,0,0,1,1,1,1,0,0,0, +1,1,2,1,1,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,1,2,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +0,1,1,0,1,1,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, +1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,2,0,0,2,0,1,0,0,1,0,0,1, +1,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, +1,1,1,1,1,1,1,2,0,0,0,0,0,0,2,1,0,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +) + +Latin5BulgarianModel = { + 'charToOrderMap': Latin5_BulgarianCharToOrderMap, + 'precedenceMatrix': BulgarianLangModel, + 'mTypicalPositiveRatio': 0.969392, + 'keepEnglishLetter': False, + 'charsetName': "ISO-8859-5" +} + +Win1251BulgarianModel = { + 'charToOrderMap': win1251BulgarianCharToOrderMap, + 'precedenceMatrix': BulgarianLangModel, + 'mTypicalPositiveRatio': 0.969392, + 'keepEnglishLetter': False, + 'charsetName': "windows-1251" +} + + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langcyrillicmodel.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langcyrillicmodel.py new file mode 100644 index 0000000..a86f54b --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langcyrillicmodel.py @@ -0,0 +1,329 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# KOI8-R language model +# Character Mapping Table: +KOI8R_CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, # 80 +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, # 90 +223,224,225, 68,226,227,228,229,230,231,232,233,234,235,236,237, # a0 +238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253, # b0 + 27, 3, 21, 28, 13, 2, 39, 19, 26, 4, 23, 11, 8, 12, 5, 1, # c0 + 15, 16, 9, 7, 6, 14, 24, 10, 17, 18, 20, 25, 30, 29, 22, 54, # d0 + 59, 37, 44, 58, 41, 48, 53, 46, 55, 42, 60, 36, 49, 38, 31, 34, # e0 + 35, 43, 45, 32, 40, 52, 56, 33, 61, 62, 51, 57, 47, 63, 50, 70, # f0 +) + +win1251_CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, +239,240,241,242,243,244,245,246, 68,247,248,249,250,251,252,253, + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +) + +latin5_CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, +) + +macCyrillic_CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, +239,240,241,242,243,244,245,246,247,248,249,250,251,252, 68, 16, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27,255, +) + +IBM855_CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194, 68,195,196,197,198,199,200,201,202,203,204,205, +206,207,208,209,210,211,212,213,214,215,216,217, 27, 59, 54, 70, + 3, 37, 21, 44, 28, 58, 13, 41, 2, 48, 39, 53, 19, 46,218,219, +220,221,222,223,224, 26, 55, 4, 42,225,226,227,228, 23, 60,229, +230,231,232,233,234,235, 11, 36,236,237,238,239,240,241,242,243, + 8, 49, 12, 38, 5, 31, 1, 34, 15,244,245,246,247, 35, 16,248, + 43, 9, 45, 7, 32, 6, 40, 14, 52, 24, 56, 10, 33, 17, 61,249, +250, 18, 62, 20, 51, 25, 57, 30, 47, 29, 63, 22, 50,251,252,255, +) + +IBM866_CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 97.6601% +# first 1024 sequences: 2.3389% +# rest sequences: 0.1237% +# negative sequences: 0.0009% +RussianLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,1,3,3,3,2,3,2,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,2,2,2,2,2,0,0,2, +3,3,3,2,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,2,3,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,2,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,2,3,3,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, +0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, +0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,2,2,2,3,1,3,3,1,3,3,3,3,2,2,3,0,2,2,2,3,3,2,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,3,3,2,2,3,2,3,3,3,2,1,2,2,0,1,2,2,2,2,2,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,3,0,2,2,3,3,2,1,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,1,2,3,2,2,3,2,3,3,3,3,2,2,3,0,3,2,2,3,1,1,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,3,3,3,3,2,2,2,0,3,3,3,2,2,2,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,2,3,2,2,0,1,3,2,1,2,2,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,2,1,1,3,0,1,1,1,1,2,1,1,0,2,2,2,1,2,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,2,2,2,2,1,3,2,3,2,3,2,1,2,2,0,1,1,2,1,2,1,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,2,3,3,3,2,2,2,2,0,2,2,2,2,3,1,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,2,3,2,2,3,3,3,3,3,3,3,3,3,1,3,2,0,0,3,3,3,3,2,3,3,3,3,2,3,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,3,2,2,3,3,0,2,1,0,3,2,3,2,3,0,0,1,2,0,0,1,0,1,2,1,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,3,0,2,3,3,3,3,2,3,3,3,3,1,2,2,0,0,2,3,2,2,2,3,2,3,2,2,3,0,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,0,2,3,2,3,0,1,2,3,3,2,0,2,3,0,0,2,3,2,2,0,1,3,1,3,2,2,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,3,0,2,3,3,3,3,3,3,3,3,2,1,3,2,0,0,2,2,3,3,3,2,3,3,0,2,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,2,2,2,3,3,0,0,1,1,1,1,1,2,0,0,1,1,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,0,3,2,3,3,2,3,2,0,2,1,0,1,1,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,2,2,2,2,3,1,3,2,3,1,1,2,1,0,2,2,2,2,1,3,1,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +2,2,3,3,3,3,3,1,2,2,1,3,1,0,3,0,0,3,0,0,0,1,1,0,1,2,1,0,0,0,0,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,2,1,1,3,3,3,2,2,1,2,2,3,1,1,2,0,0,2,2,1,3,0,0,2,1,1,2,1,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,3,3,3,1,2,2,2,1,2,1,3,3,1,1,2,1,2,1,2,2,0,2,0,0,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,3,2,1,3,2,2,3,2,0,3,2,0,3,0,1,0,1,1,0,0,1,1,1,1,0,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,3,3,3,2,2,2,3,3,1,2,1,2,1,0,1,0,1,1,0,1,0,0,2,1,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,1,1,2,1,2,3,3,2,2,1,2,2,3,0,2,1,0,0,2,2,3,2,1,2,2,2,2,2,3,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,1,1,0,1,1,2,2,1,1,3,0,0,1,3,1,1,1,0,0,0,1,0,1,1,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,3,3,3,2,0,0,0,2,1,0,1,0,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,0,2,3,2,2,2,1,2,2,2,1,2,1,0,0,1,1,1,0,2,0,1,1,1,0,0,1,1, +1,0,0,0,0,0,1,2,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,0,0,0,0,1,0,0,0,0,3,0,1,2,1,0,0,0,0,0,0,0,1,1,0,0,1,1, +1,0,1,0,1,2,0,0,1,1,2,1,0,1,1,1,1,0,1,1,1,1,0,1,0,0,1,0,0,1,1,0, +2,2,3,2,2,2,3,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,0,1,0,1,1,1,0,2,1, +1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,0,1,1,0, +3,3,3,2,2,2,2,3,2,2,1,1,2,2,2,2,1,1,3,1,2,1,2,0,0,1,1,0,1,0,2,1, +1,1,1,1,1,2,1,0,1,1,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,1,0, +2,0,0,1,0,3,2,2,2,2,1,2,1,2,1,2,0,0,0,2,1,2,2,1,1,2,2,0,1,1,0,2, +1,1,1,1,1,0,1,1,1,2,1,1,1,2,1,0,1,2,1,1,1,1,0,1,1,1,0,0,1,0,0,1, +1,3,2,2,2,1,1,1,2,3,0,0,0,0,2,0,2,2,1,0,0,0,0,0,0,1,0,0,0,0,1,1, +1,0,1,1,0,1,0,1,1,0,1,1,0,2,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, +2,3,2,3,2,1,2,2,2,2,1,0,0,0,2,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,2,1, +1,1,2,1,0,2,0,0,1,0,1,0,0,1,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,0, +3,0,0,1,0,2,2,2,3,2,2,2,2,2,2,2,0,0,0,2,1,2,1,1,1,2,2,0,0,0,1,2, +1,1,1,1,1,0,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1, +2,3,2,3,3,2,0,1,1,1,0,0,1,0,2,0,1,1,3,1,0,0,0,0,0,0,0,1,0,0,2,1, +1,1,1,1,1,1,1,0,1,0,1,1,1,1,0,1,1,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0, +2,3,3,3,3,1,2,2,2,2,0,1,1,0,2,1,1,1,2,1,0,1,1,0,0,1,0,1,0,0,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,2,0,0,1,1,2,2,1,0,0,2,0,1,1,3,0,0,1,0,0,0,0,0,1,0,1,2,1, +1,1,2,0,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,0,1,0,0,0,0,0,0,1,0,1,1,0, +1,3,2,3,2,1,0,0,2,2,2,0,1,0,2,0,1,1,1,0,1,0,0,0,3,0,1,1,0,0,2,1, +1,1,1,0,1,1,0,0,0,0,1,1,0,1,0,0,2,1,1,0,1,0,0,0,1,0,1,0,0,1,1,0, +3,1,2,1,1,2,2,2,2,2,2,1,2,2,1,1,0,0,0,2,2,2,0,0,0,1,2,1,0,1,0,1, +2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,2,1,1,1,0,1,0,1,1,0,1,1,1,0,0,1, +3,0,0,0,0,2,0,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,0,1,0,1,1,0,0,1,0,1, +1,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1, +1,3,3,2,2,0,0,0,2,2,0,0,0,1,2,0,1,1,2,0,0,0,0,0,0,0,0,1,0,0,2,1, +0,1,1,0,0,1,1,0,0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +2,3,2,3,2,0,0,0,0,1,1,0,0,0,2,0,2,0,2,0,0,0,0,0,1,0,0,1,0,0,1,1, +1,1,2,0,1,2,1,0,1,1,2,1,1,1,1,1,2,1,1,0,1,0,0,1,1,1,1,1,0,1,1,0, +1,3,2,2,2,1,0,0,2,2,1,0,1,2,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,1, +0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,0,2,3,1,2,2,2,2,2,2,1,1,0,0,0,1,0,1,0,2,1,1,1,0,0,0,0,1, +1,1,0,1,1,0,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +2,0,2,0,0,1,0,3,2,1,2,1,2,2,0,1,0,0,0,2,1,0,0,2,1,1,1,1,0,2,0,2, +2,1,1,1,1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,0,0,0,1,1,1,1,0,1,0,0,1, +1,2,2,2,2,1,0,0,1,0,0,0,0,0,2,0,1,1,1,1,0,0,0,0,1,0,1,2,0,0,2,0, +1,0,1,1,1,2,1,0,1,0,1,1,0,0,1,0,1,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0, +2,1,2,2,2,0,3,0,1,1,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,0,1,1,1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0, +1,2,2,3,2,2,0,0,1,1,2,0,1,2,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1, +0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, +2,2,1,1,2,1,2,2,2,2,2,1,2,2,0,1,0,0,0,1,2,2,2,1,2,1,1,1,1,1,2,1, +1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,0,1, +1,2,2,2,2,0,1,0,2,2,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +0,0,1,0,0,1,0,0,0,0,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,0,2,2,2,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1, +0,1,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,0,0,1,0,0,1,1,2,0,0,0,0,1,0,1,0,0,1,0,0,2,0,0,0,1, +0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,1,1,2,0,2,1,1,1,1,0,2,2,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1, +0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,0,2,1,2,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +1,0,0,0,0,2,0,1,2,1,0,1,1,1,0,1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1, +0,0,0,0,0,1,0,0,1,1,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1, +2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,1,0,1,0,1,0,0,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,0,1,1,0,1,0,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0, +0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, +) + +Koi8rModel = { + 'charToOrderMap': KOI8R_CharToOrderMap, + 'precedenceMatrix': RussianLangModel, + 'mTypicalPositiveRatio': 0.976601, + 'keepEnglishLetter': False, + 'charsetName': "KOI8-R" +} + +Win1251CyrillicModel = { + 'charToOrderMap': win1251_CharToOrderMap, + 'precedenceMatrix': RussianLangModel, + 'mTypicalPositiveRatio': 0.976601, + 'keepEnglishLetter': False, + 'charsetName': "windows-1251" +} + +Latin5CyrillicModel = { + 'charToOrderMap': latin5_CharToOrderMap, + 'precedenceMatrix': RussianLangModel, + 'mTypicalPositiveRatio': 0.976601, + 'keepEnglishLetter': False, + 'charsetName': "ISO-8859-5" +} + +MacCyrillicModel = { + 'charToOrderMap': macCyrillic_CharToOrderMap, + 'precedenceMatrix': RussianLangModel, + 'mTypicalPositiveRatio': 0.976601, + 'keepEnglishLetter': False, + 'charsetName': "MacCyrillic" +}; + +Ibm866Model = { + 'charToOrderMap': IBM866_CharToOrderMap, + 'precedenceMatrix': RussianLangModel, + 'mTypicalPositiveRatio': 0.976601, + 'keepEnglishLetter': False, + 'charsetName': "IBM866" +} + +Ibm855Model = { + 'charToOrderMap': IBM855_CharToOrderMap, + 'precedenceMatrix': RussianLangModel, + 'mTypicalPositiveRatio': 0.976601, + 'keepEnglishLetter': False, + 'charsetName': "IBM855" +} + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langgreekmodel.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langgreekmodel.py new file mode 100644 index 0000000..ddb5837 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langgreekmodel.py @@ -0,0 +1,225 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin7_CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 + 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 +253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 + 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 +253,233, 90,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 +253,253,253,253,247,248, 61, 36, 46, 71, 73,253, 54,253,108,123, # b0 +110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 + 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 +124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 + 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 +) + +win1253_CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 + 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 +253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 + 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 +253,233, 61,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 +253,253,253,253,247,253,253, 36, 46, 71, 73,253, 54,253,108,123, # b0 +110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 + 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 +124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 + 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 98.2851% +# first 1024 sequences:1.7001% +# rest sequences: 0.0359% +# negative sequences: 0.0148% +GreekLangModel = ( +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,2,2,3,3,3,3,3,3,3,3,1,3,3,3,0,2,2,3,3,0,3,0,3,2,0,3,3,3,0, +3,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,0,3,3,0,3,2,3,3,0,3,2,3,3,3,0,0,3,0,3,0,3,3,2,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,2,3,2,2,3,3,3,3,3,3,3,3,0,3,3,3,3,0,2,3,3,0,3,3,3,3,2,3,3,3,0, +2,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,2,1,3,3,3,3,2,3,3,2,3,3,2,0, +0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,2,3,3,0, +2,0,1,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,3,0,0,0,0,3,3,0,3,1,3,3,3,0,3,3,0,3,3,3,3,0,0,0,0, +2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,0,3,0,3,3,3,3,3,0,3,2,2,2,3,0,2,3,3,3,3,3,2,3,3,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,3,2,2,2,3,3,3,3,0,3,1,3,3,3,3,2,3,3,3,3,3,3,3,2,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,0,3,0,0,0,3,3,2,3,3,3,3,3,0,0,3,2,3,0,2,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,3,0,0,3,3,0,2,3,0,3,0,3,3,3,0,0,3,0,3,0,2,2,3,3,0,0, +0,0,1,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,0,3,2,3,3,3,3,0,3,3,3,3,3,0,3,3,2,3,2,3,3,2,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,2,3,2,3,3,3,3,3,3,0,2,3,2,3,2,2,2,3,2,3,3,2,3,0,2,2,2,3,0, +2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,0,3,3,3,2,3,3,0,0,3,0,3,0,0,0,3,2,0,3,0,3,0,0,2,0,2,0, +0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,0,0,0,3,3,0,3,3,3,0,0,1,2,3,0, +3,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,0,0,3,2,2,3,3,0,3,3,3,3,3,2,1,3,0,3,2,3,3,2,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,3,0,2,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,3,0,3,2,3,0,0,3,3,3,0, +3,0,0,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,0,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,2,0,3,2,3,0,0,3,2,3,0, +2,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,1,2,2,3,3,3,3,3,3,0,2,3,0,3,0,0,0,3,3,0,3,0,2,0,0,2,3,1,0, +2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,3,0,3,0,3,3,2,3,0,3,3,3,3,3,3,0,3,3,3,0,2,3,0,0,3,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,0,0,3,0,0,0,3,3,0,3,0,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,0,3,3,3,3,3,3,0,0,3,0,2,0,0,0,3,3,0,3,0,3,0,0,2,0,2,0, +0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,3,0,3,0,2,0,3,2,0,3,2,3,2,3,0,0,3,2,3,2,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,2,3,3,3,3,3,0,0,0,3,0,2,1,0,0,3,2,2,2,0,3,0,0,2,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,2,0,3,0,3,0,3,3,0,2,1,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,3,3,3,0,3,3,3,3,3,3,0,2,3,0,3,0,0,0,2,1,0,2,2,3,0,0,2,2,2,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,2,3,3,3,2,3,0,0,1,3,0,2,0,0,0,0,3,0,1,0,2,0,0,1,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,1,0,3,0,0,0,3,2,0,3,2,3,3,3,0,0,3,0,3,2,2,2,1,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,0,0,3,0,0,0,0,2,0,2,3,3,2,2,2,2,3,0,2,0,2,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,2,0,0,0,0,0,0,2,3,0,2,0,2,3,2,0,0,3,0,3,0,3,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,2,3,3,2,2,3,0,2,0,3,0,0,0,2,0,0,0,0,1,2,0,2,0,2,0, +0,2,0,2,0,2,2,0,0,1,0,2,2,2,0,2,2,2,0,2,2,2,0,0,2,0,0,1,0,0,0,0, +0,2,0,3,3,2,0,0,0,0,0,0,1,3,0,2,0,2,2,2,0,0,2,0,3,0,0,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,2,3,2,0,2,2,0,2,0,2,2,0,2,0,2,2,2,0,0,0,0,0,0,2,3,0,0,0,2, +0,1,2,0,0,0,0,2,2,0,0,0,2,1,0,2,2,0,0,0,0,0,0,1,0,2,0,0,0,0,0,0, +0,0,2,1,0,2,3,2,2,3,2,3,2,0,0,3,3,3,0,0,3,2,0,0,0,1,1,0,2,0,2,2, +0,2,0,2,0,2,2,0,0,2,0,2,2,2,0,2,2,2,2,0,0,2,0,0,0,2,0,1,0,0,0,0, +0,3,0,3,3,2,2,0,3,0,0,0,2,2,0,2,2,2,1,2,0,0,1,2,2,0,0,3,0,0,0,2, +0,1,2,0,0,0,1,2,0,0,0,0,0,0,0,2,2,0,1,0,0,2,0,0,0,2,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,3,3,2,2,0,0,0,2,0,2,3,3,0,2,0,0,0,0,0,0,2,2,2,0,2,2,0,2,0,2, +0,2,2,0,0,2,2,2,2,1,0,0,2,2,0,2,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0, +0,2,0,3,2,3,0,0,0,3,0,0,2,2,0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,0,2, +0,0,2,2,0,0,2,2,2,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,3,2,0,2,2,2,2,2,0,0,0,2,0,0,0,0,2,0,1,0,0,2,0,1,0,0,0, +0,2,2,2,0,2,2,0,1,2,0,2,2,2,0,2,2,2,2,1,2,2,0,0,2,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,2,0,2,0,2,2,0,0,0,0,1,2,1,0,0,2,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,3,2,3,0,0,2,0,0,0,2,2,0,2,0,0,0,1,0,0,2,0,2,0,2,2,0,0,0,0, +0,0,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0, +0,2,2,3,2,2,0,0,0,0,0,0,1,3,0,2,0,2,2,0,0,0,1,0,2,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,0,2,0,3,2,0,2,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,2,0,0,0,0,1,1,0,0,2,1,2,0,2,2,0,1,0,0,1,0,0,0,2,0,0,0,0,0,0, +0,3,0,2,2,2,0,0,2,0,0,0,2,0,0,0,2,3,0,2,0,0,0,0,0,0,2,2,0,0,0,2, +0,1,2,0,0,0,1,2,2,1,0,0,0,2,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,1,2,0,2,2,0,2,0,0,2,0,0,0,0,1,2,1,0,2,1,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,0,3,1,2,2,0,2,0,0,0,0,2,0,0,0,2,0,0,3,0,0,0,0,2,2,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,1,0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,2, +0,2,2,0,0,2,2,2,2,2,0,1,2,0,0,0,2,2,0,1,0,2,0,0,2,2,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,2, +0,1,2,0,0,0,0,2,2,1,0,1,0,1,0,2,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,2,0,0,2,2,0,0,0,0,1,0,0,0,0,0,0,2, +0,2,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0, +0,2,2,2,2,0,0,0,3,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,1, +0,0,2,0,0,0,0,1,2,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,2,2,2,0,0,0,2,0,0,0,0,0,0,0,0,2, +0,0,1,0,0,0,0,2,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,3,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,2, +0,0,2,0,0,0,0,2,2,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,0,2,2,1,0,0,0,0,0,0,2,0,0,2,0,2,2,2,0,0,0,0,0,0,2,0,0,0,0,2, +0,0,2,0,0,2,0,2,2,0,0,0,0,2,0,2,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0, +0,0,3,0,0,0,2,2,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0, +0,2,2,2,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1, +0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,2,0,0,0,2,0,0,0,0,0,1,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,2,0,0,0, +0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,2,0,2,0,0,0, +0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +) + +Latin7GreekModel = { + 'charToOrderMap': Latin7_CharToOrderMap, + 'precedenceMatrix': GreekLangModel, + 'mTypicalPositiveRatio': 0.982851, + 'keepEnglishLetter': False, + 'charsetName': "ISO-8859-7" +} + +Win1253GreekModel = { + 'charToOrderMap': win1253_CharToOrderMap, + 'precedenceMatrix': GreekLangModel, + 'mTypicalPositiveRatio': 0.982851, + 'keepEnglishLetter': False, + 'charsetName': "windows-1253" +} + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langhebrewmodel.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langhebrewmodel.py new file mode 100644 index 0000000..75f2bc7 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langhebrewmodel.py @@ -0,0 +1,201 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Simon Montagu +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Shoshannah Forbes - original C code (?) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Windows-1255 language model +# Character Mapping Table: +win1255_CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 69, 91, 79, 80, 92, 89, 97, 90, 68,111,112, 82, 73, 95, 85, # 40 + 78,121, 86, 71, 67,102,107, 84,114,103,115,253,253,253,253,253, # 50 +253, 50, 74, 60, 61, 42, 76, 70, 64, 53,105, 93, 56, 65, 54, 49, # 60 + 66,110, 51, 43, 44, 63, 81, 77, 98, 75,108,253,253,253,253,253, # 70 +124,202,203,204,205, 40, 58,206,207,208,209,210,211,212,213,214, +215, 83, 52, 47, 46, 72, 32, 94,216,113,217,109,218,219,220,221, + 34,116,222,118,100,223,224,117,119,104,125,225,226, 87, 99,227, +106,122,123,228, 55,229,230,101,231,232,120,233, 48, 39, 57,234, + 30, 59, 41, 88, 33, 37, 36, 31, 29, 35,235, 62, 28,236,126,237, +238, 38, 45,239,240,241,242,243,127,244,245,246,247,248,249,250, + 9, 8, 20, 16, 3, 2, 24, 14, 22, 1, 25, 15, 4, 11, 6, 23, + 12, 19, 13, 26, 18, 27, 21, 17, 7, 10, 5,251,252,128, 96,253, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 98.4004% +# first 1024 sequences: 1.5981% +# rest sequences: 0.087% +# negative sequences: 0.0015% +HebrewLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,3,2,1,2,0,1,0,0, +3,0,3,1,0,0,1,3,2,0,1,1,2,0,2,2,2,1,1,1,1,2,1,1,1,2,0,0,2,2,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, +1,2,1,2,1,2,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2, +1,2,1,3,1,1,0,0,2,0,0,0,1,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,1,2,2,1,3, +1,2,1,1,2,2,0,0,2,2,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,2,2,2,3,2, +1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,3,2,2,3,2,2,2,1,2,2,2,2, +1,2,1,1,2,2,0,1,2,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,0,2,2,2,2,2, +0,2,0,2,2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,0,2,2,2, +0,2,1,2,2,2,0,0,2,1,0,0,0,0,1,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1,2,3,2,2,2, +1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,2,0,2, +0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,2,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,2,2,3,2,1,2,1,1,1, +0,1,1,1,1,1,3,0,1,0,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,1,0,0,1,0,0,0,0, +0,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,2,1,2,3,3,2,3,3,3,3,2,3,2,1,2,0,2,1,2, +0,2,0,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,1,2,2,3,3,2,3,2,3,2,2,3,1,2,2,0,2,2,2, +0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,2,2,3,3,3,3,1,3,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,2,3,2,2,2,1,2,2,0,2,2,2,2, +0,2,0,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,1,3,2,3,3,2,3,3,2,2,1,2,2,2,2,2,2, +0,2,1,2,1,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,2,3,3,2,3,3,3,3,2,3,2,3,3,3,3,3,2,2,2,2,2,2,2,1, +0,2,0,1,2,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,1,2,3,3,3,3,3,3,3,2,3,2,3,2,1,2,3,0,2,1,2,2, +0,2,1,1,2,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,2,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,3,2,1,3,1,2,2,2,1,2,3,3,1,2,1,2,2,2,2, +0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,0,2,3,3,3,1,3,3,3,1,2,2,2,2,1,1,2,2,2,2,2,2, +0,2,0,1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,2,2,3,3,3,2,1,2,3,2,3,2,2,2,2,1,2,1,1,1,2,2, +0,2,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,0,0,0, +1,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,3,2,3,1,2,2,2,2,3,2,3,1,1,2,2,1,2,2,1,1,0,2,2,2,2, +0,1,0,1,2,2,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,0,0,1,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,0,1,0,1,1,0,1,1,0,0,0,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +3,2,2,1,2,2,2,2,2,2,2,1,2,2,1,2,2,1,1,1,1,1,1,1,1,2,1,1,0,3,3,3, +0,3,0,2,2,2,2,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,1,2,2,2,1,1,1,2,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,0,0,0,0, +0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,0,2,1,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,3,1,1,2,2,2,2,2,1,2,2,2,1,1,2,2,2,2,2,2,2,1,2,2,1,0,1,1,1,1,0, +0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,1,1,1,1,2,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,0,0, +2,1,1,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,1,2,1,2,1,1,1,1,0,0,0,0, +0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,1,2,1,1,2,1,1,1,2,1,2,1,2,0,1,0,1, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,1,2,2,2,1,2,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,2,1,2,1,1,0,1,0,1, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,1,1,1,1,1,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,1,1,1,0,1,0,0,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0, +0,1,1,1,2,1,2,2,2,0,2,0,2,0,1,1,2,1,1,1,1,2,1,0,1,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,1,0,0,0,0,0,1,0,1,2,2,0,1,0,0,1,1,2,2,1,2,0,2,0,0,0,1,2,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,2,1,2,0,2,0,0,1,1,1,1,1,1,0,1,0,0,0,1,0,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,1,2,2,0,0,1,0,0,0,1,0,0,1, +1,1,2,1,0,1,1,1,0,1,0,1,1,1,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,2,1, +0,2,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,0,0,1,0,1,1,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,1,0,0,0,1,1,0,1, +2,0,1,0,1,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,0,1,1,2,1,1,2,0,1,0,0,0,1,1,0,1, +1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,0,0,2,1,1,2,0,2,0,0,0,1,1,0,1, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,2,2,1,2,1,1,0,1,0,0,0,1,1,0,1, +2,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,1,0,1, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,2,1,1,1,0,2,1,1,0,0,0,2,1,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,0,2,1,1,0,1,0,0,0,1,1,0,1, +2,2,1,1,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,0,1,2,1,0,2,0,0,0,1,1,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0, +0,1,0,0,2,0,2,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,1,0,1,0,0,1,0,0,0,1,0,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,0,2,1,1,1,1,1,0,1,0,0,0,0,1,0,1, +0,1,1,1,2,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,1,0,0, +) + +Win1255HebrewModel = { + 'charToOrderMap': win1255_CharToOrderMap, + 'precedenceMatrix': HebrewLangModel, + 'mTypicalPositiveRatio': 0.984004, + 'keepEnglishLetter': False, + 'charsetName': "windows-1255" +} + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langhungarianmodel.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langhungarianmodel.py new file mode 100644 index 0000000..49d2f0f --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langhungarianmodel.py @@ -0,0 +1,225 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin2_HungarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, + 46, 71, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, +253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, + 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, +159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174, +175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190, +191,192,193,194,195,196,197, 75,198,199,200,201,202,203,204,205, + 79,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, +221, 51, 81,222, 78,223,224,225,226, 44,227,228,229, 61,230,231, +232,233,234, 58,235, 66, 59,236,237,238, 60, 69, 63,239,240,241, + 82, 14, 74,242, 70, 80,243, 72,244, 15, 83, 77, 84, 30, 76, 85, +245,246,247, 25, 73, 42, 24,248,249,250, 31, 56, 29,251,252,253, +) + +win1250HungarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, + 46, 72, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, +253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, + 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, +161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176, +177,178,179,180, 78,181, 69,182,183,184,185,186,187,188,189,190, +191,192,193,194,195,196,197, 76,198,199,200,201,202,203,204,205, + 81,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, +221, 51, 83,222, 80,223,224,225,226, 44,227,228,229, 61,230,231, +232,233,234, 58,235, 66, 59,236,237,238, 60, 70, 63,239,240,241, + 84, 14, 75,242, 71, 82,243, 73,244, 15, 85, 79, 86, 30, 77, 87, +245,246,247, 25, 74, 42, 24,248,249,250, 31, 56, 29,251,252,253, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 94.7368% +# first 1024 sequences:5.2623% +# rest sequences: 0.8894% +# negative sequences: 0.0009% +HungarianLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,2,3,3,1,1,2,2,2,2,2,1,2, +3,2,2,3,3,3,3,3,2,3,3,3,3,3,3,1,2,3,3,3,3,2,3,3,1,1,3,3,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +3,2,1,3,3,3,3,3,2,3,3,3,3,3,1,1,2,3,3,3,3,3,3,3,1,1,3,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,1,1,2,3,3,3,1,3,3,3,3,3,1,3,3,2,2,0,3,2,3, +0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,3,3,2,3,3,2,2,3,2,3,2,0,3,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,1,2,3,2,2,3,1,2,3,3,2,2,0,3,3,3, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,3,2,3,3,3,3,2,3,3,3,3,0,2,3,2, +0,0,0,1,1,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,1,1,1,3,3,2,1,3,2,2,3,2,1,3,2,2,1,0,3,3,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,2,2,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,3,2,2,3,1,1,3,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,1,3,3,3,3,3,2,2,1,3,3,3,0,1,1,2, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,2,0,3,2,3, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,1,3,2,2,2,3,1,1,3,3,1,1,0,3,3,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,2,3,3,3,3,3,1,2,3,2,2,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,1,3,3,2,2,1,3,3,3,1,1,3,1,2,3,2,3,2,2,2,1,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,2,2,3,2,1,0,3,2,0,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,1,0,3,3,3,3,0,2,3,0,0,2,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,2,2,2,2,3,3,0,1,2,3,2,3,2,2,3,2,1,2,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,3,3,3,3,3,1,2,3,3,3,2,1,2,3,3,2,2,2,3,2,3,3,1,3,3,1,1,0,2,3,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,1,2,2,2,2,3,3,3,1,1,1,3,3,1,1,3,1,1,3,2,1,2,3,1,1,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,1,2,1,1,3,3,1,1,1,1,3,3,1,1,2,2,1,2,1,1,2,2,1,1,0,2,2,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,1,1,2,1,1,3,3,1,0,1,1,3,3,2,0,1,1,2,3,1,0,2,2,1,0,0,1,3,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,2,1,3,3,3,3,3,1,2,3,2,3,3,2,1,1,3,2,3,2,1,2,2,0,1,2,1,0,0,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,2,2,2,2,3,1,2,2,1,1,3,3,0,3,2,1,2,3,2,1,3,3,1,1,0,2,1,3, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,2,3,3,3,2,1,1,3,3,1,1,1,2,2,3,2,3,2,2,2,1,0,2,2,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +1,0,0,3,3,3,3,3,0,0,3,3,2,3,0,0,0,2,3,3,1,0,1,2,0,0,1,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,2,3,3,3,3,3,1,2,3,3,2,2,1,1,0,3,3,2,2,1,2,2,1,0,2,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,2,1,3,1,2,3,3,2,2,1,1,2,2,1,1,1,1,3,2,1,1,1,1,2,1,0,1,2,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +2,3,3,1,1,1,1,1,3,3,3,0,1,1,3,3,1,1,1,1,1,2,2,0,3,1,1,2,0,2,1,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,1,0,1,2,1,2,2,0,1,2,3,1,2,0,0,0,2,1,1,1,1,1,2,0,0,1,1,0,0,0,0, +1,2,1,2,2,2,1,2,1,2,0,2,0,2,2,1,1,2,1,1,2,1,1,1,0,1,0,0,0,1,1,0, +1,1,1,2,3,2,3,3,0,1,2,2,3,1,0,1,0,2,1,2,2,0,1,1,0,0,1,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,3,3,2,2,1,0,0,3,2,3,2,0,0,0,1,1,3,0,0,1,1,0,0,2,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,2,2,3,3,1,0,1,3,2,3,1,1,1,0,1,1,1,1,1,3,1,0,0,2,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,1,2,2,2,1,0,1,2,3,3,2,0,0,0,2,1,1,1,2,1,1,1,0,1,1,1,0,0,0, +1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,2,1,1,1,1,1,1,0,1,1,1,0,0,1,1, +3,2,2,1,0,0,1,1,2,2,0,3,0,1,2,1,1,0,0,1,1,1,0,1,1,1,1,0,2,1,1,1, +2,2,1,1,1,2,1,2,1,1,1,1,1,1,1,2,1,1,1,2,3,1,1,1,1,1,1,1,1,1,0,1, +2,3,3,0,1,0,0,0,3,3,1,0,0,1,2,2,1,0,0,0,0,2,0,0,1,1,1,0,2,1,1,1, +2,1,1,1,1,1,1,2,1,1,0,1,1,0,1,1,1,0,1,2,1,1,0,1,1,1,1,1,1,1,0,1, +2,3,3,0,1,0,0,0,2,2,0,0,0,0,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,1,0, +2,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, +3,2,2,0,1,0,1,0,2,3,2,0,0,1,2,2,1,0,0,1,1,1,0,0,2,1,0,1,2,2,1,1, +2,1,1,1,1,1,1,2,1,1,1,1,1,1,0,2,1,0,1,1,0,1,1,1,0,1,1,2,1,1,0,1, +2,2,2,0,0,1,0,0,2,2,1,1,0,0,2,1,1,0,0,0,1,2,0,0,2,1,0,0,2,1,1,1, +2,1,1,1,1,2,1,2,1,1,1,2,2,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1, +1,2,3,0,0,0,1,0,3,2,1,0,0,1,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,2,1, +1,1,0,0,0,1,0,1,1,1,1,1,2,0,0,1,0,0,0,2,0,0,1,1,1,1,1,1,1,1,0,1, +3,0,0,2,1,2,2,1,0,0,2,1,2,2,0,0,0,2,1,1,1,0,1,1,0,0,1,1,2,0,0,0, +1,2,1,2,2,1,1,2,1,2,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,0,0,1, +1,3,2,0,0,0,1,0,2,2,2,0,0,0,2,2,1,0,0,0,0,3,1,1,1,1,0,0,2,1,1,1, +2,1,0,1,1,1,0,1,1,1,1,1,1,1,0,2,1,0,0,1,0,1,1,0,1,1,1,1,1,1,0,1, +2,3,2,0,0,0,1,0,2,2,0,0,0,0,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,1,0, +2,1,1,1,1,2,1,2,1,2,0,1,1,1,0,2,1,1,1,2,1,1,1,1,0,1,1,1,1,1,0,1, +3,1,1,2,2,2,3,2,1,1,2,2,1,1,0,1,0,2,2,1,1,1,1,1,0,0,1,1,0,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,0,0,0,0,0,2,2,0,0,0,0,2,2,1,0,0,0,1,1,0,0,1,2,0,0,2,1,1,1, +2,2,1,1,1,2,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,1,1,0,1,2,1,1,1,0,1, +1,0,0,1,2,3,2,1,0,0,2,0,1,1,0,0,0,1,1,1,1,0,1,1,0,0,1,0,0,0,0,0, +1,2,1,2,1,2,1,1,1,2,0,2,1,1,1,0,1,2,0,0,1,1,1,0,0,0,0,0,0,0,0,0, +2,3,2,0,0,0,0,0,1,1,2,1,0,0,1,1,1,0,0,0,0,2,0,0,1,1,0,0,2,1,1,1, +2,1,1,1,1,1,1,2,1,0,1,1,1,1,0,2,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1, +1,2,2,0,1,1,1,0,2,2,2,0,0,0,3,2,1,0,0,0,1,1,0,0,1,1,0,1,1,1,0,0, +1,1,0,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,0,0,1,1,1,0,1,0,1, +2,1,0,2,1,1,2,2,1,1,2,1,1,1,0,0,0,1,1,0,1,1,1,1,0,0,1,1,1,0,0,0, +1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,1,0, +1,2,3,0,0,0,1,0,2,2,0,0,0,0,2,2,0,0,0,0,0,1,0,0,1,0,0,0,2,0,1,0, +2,1,1,1,1,1,0,2,0,0,0,1,2,1,1,1,1,0,1,2,0,1,0,1,0,1,1,1,0,1,0,1, +2,2,2,0,0,0,1,0,2,1,2,0,0,0,1,1,2,0,0,0,0,1,0,0,1,1,0,0,2,1,0,1, +2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, +1,2,2,0,0,0,1,0,2,2,2,0,0,0,1,1,0,0,0,0,0,1,1,0,2,0,0,1,1,1,0,1, +1,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,0,0,1,1,0,1,0,1,1,1,1,1,0,0,0,1, +1,0,0,1,0,1,2,1,0,0,1,1,1,2,0,0,0,1,1,0,1,0,1,1,0,0,1,0,0,0,0,0, +0,2,1,2,1,1,1,1,1,2,0,2,0,1,1,0,1,2,1,0,1,1,1,0,0,0,0,0,0,1,0,0, +2,1,1,0,1,2,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,2,1,0,1, +2,2,1,1,1,1,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,0,1,0,1,1,1,1,1,0,1, +1,2,2,0,0,0,0,0,1,1,0,0,0,0,2,1,0,0,0,0,0,2,0,0,2,2,0,0,2,0,0,1, +2,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1, +1,1,2,0,0,3,1,0,2,1,1,1,0,0,1,1,1,0,0,0,1,1,0,0,0,1,0,0,1,0,1,0, +1,2,1,0,1,1,1,2,1,1,0,1,1,1,1,1,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,0, +2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,2,0,0,0, +2,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,1,0,1, +2,1,1,1,2,1,1,1,0,1,1,2,1,0,0,0,0,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,0,1,1,1,1,1,0,0,1,1,2,1,0,0,0,1,1,0,0,0,1,1,0,0,1,0,1,0,0,0, +1,2,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0, +2,0,0,0,1,1,1,1,0,0,1,1,0,0,0,0,0,1,1,1,2,0,0,1,0,0,1,0,1,0,0,0, +0,1,1,1,1,1,1,1,1,2,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,1,0,0,2,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0, +0,1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +0,1,1,1,1,1,0,0,1,1,0,1,0,1,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,1,0,0,0,0,0,0,1,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1,1,0,1,0,0,1,1,0,1,0,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +2,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,1,0,0,1,0,1,0,1,1,1,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1,1,1,1,1,0,1,1,0,1,0,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +) + +Latin2HungarianModel = { + 'charToOrderMap': Latin2_HungarianCharToOrderMap, + 'precedenceMatrix': HungarianLangModel, + 'mTypicalPositiveRatio': 0.947368, + 'keepEnglishLetter': True, + 'charsetName': "ISO-8859-2" +} + +Win1250HungarianModel = { + 'charToOrderMap': win1250HungarianCharToOrderMap, + 'precedenceMatrix': HungarianLangModel, + 'mTypicalPositiveRatio': 0.947368, + 'keepEnglishLetter': True, + 'charsetName': "windows-1250" +} + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langthaimodel.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langthaimodel.py new file mode 100644 index 0000000..0508b1b --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/langthaimodel.py @@ -0,0 +1,200 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# The following result for thai was collected from a limited sample (1M). + +# Character Mapping Table: +TIS620CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,182,106,107,100,183,184,185,101, 94,186,187,108,109,110,111, # 40 +188,189,190, 89, 95,112,113,191,192,193,194,253,253,253,253,253, # 50 +253, 64, 72, 73,114, 74,115,116,102, 81,201,117, 90,103, 78, 82, # 60 + 96,202, 91, 79, 84,104,105, 97, 98, 92,203,253,253,253,253,253, # 70 +209,210,211,212,213, 88,214,215,216,217,218,219,220,118,221,222, +223,224, 99, 85, 83,225,226,227,228,229,230,231,232,233,234,235, +236, 5, 30,237, 24,238, 75, 8, 26, 52, 34, 51,119, 47, 58, 57, + 49, 53, 55, 43, 20, 19, 44, 14, 48, 3, 17, 25, 39, 62, 31, 54, + 45, 9, 16, 2, 61, 15,239, 12, 42, 46, 18, 21, 76, 4, 66, 63, + 22, 10, 1, 36, 23, 13, 40, 27, 32, 35, 86,240,241,242,243,244, + 11, 28, 41, 29, 33,245, 50, 37, 6, 7, 67, 77, 38, 93,246,247, + 68, 56, 59, 65, 69, 60, 70, 80, 71, 87,248,249,250,251,252,253, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 92.6386% +# first 1024 sequences:7.3177% +# rest sequences: 1.0230% +# negative sequences: 0.0436% +ThaiLangModel = ( +0,1,3,3,3,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,0,0,3,3,3,0,3,3,3,3, +0,3,3,0,0,0,1,3,0,3,3,2,3,3,0,1,2,3,3,3,3,0,2,0,2,0,0,3,2,1,2,2, +3,0,3,3,2,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,0,3,2,3,0,2,2,2,3, +0,2,3,0,0,0,0,1,0,1,2,3,1,1,3,2,2,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1, +3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,3,3,2,3,2,3,3,2,2,2, +3,1,2,3,0,3,3,2,2,1,2,3,3,1,2,0,1,3,0,1,0,0,1,0,0,0,0,0,0,0,1,1, +3,3,2,2,3,3,3,3,1,2,3,3,3,3,3,2,2,2,2,3,3,2,2,3,3,2,2,3,2,3,2,2, +3,3,1,2,3,1,2,2,3,3,1,0,2,1,0,0,3,1,2,1,0,0,1,0,0,0,0,0,0,1,0,1, +3,3,3,3,3,3,2,2,3,3,3,3,2,3,2,2,3,3,2,2,3,2,2,2,2,1,1,3,1,2,1,1, +3,2,1,0,2,1,0,1,0,1,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,3,2,3,2,3,3,2,2,3,2,3,3,2,3,1,1,2,3,2,2,2,3,2,2,2,2,2,1,2,1, +2,2,1,1,3,3,2,1,0,1,2,2,0,1,3,0,0,0,1,1,0,0,0,0,0,2,3,0,0,2,1,1, +3,3,2,3,3,2,0,0,3,3,0,3,3,0,2,2,3,1,2,2,1,1,1,0,2,2,2,0,2,2,1,1, +0,2,1,0,2,0,0,2,0,1,0,0,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,2,3,3,2,0,0,3,3,0,2,3,0,2,1,2,2,2,2,1,2,0,0,2,2,2,0,2,2,1,1, +0,2,1,0,2,0,0,2,0,1,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,2,3,2,3,2,0,2,2,1,3,2,1,3,2,1,2,3,2,2,3,0,2,3,2,2,1,2,2,2,2, +1,2,2,0,0,0,0,2,0,1,2,0,1,1,1,0,1,0,3,1,1,0,0,0,0,0,0,0,0,0,1,0, +3,3,2,3,3,2,3,2,2,2,3,2,2,3,2,2,1,2,3,2,2,3,1,3,2,2,2,3,2,2,2,3, +3,2,1,3,0,1,1,1,0,2,1,1,1,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,0,2,0,0, +1,0,0,3,0,3,3,3,3,3,0,0,3,0,2,2,3,3,3,3,3,0,0,0,1,1,3,0,0,0,0,2, +0,0,1,0,0,0,0,0,0,0,2,3,0,0,0,3,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +2,0,3,3,3,3,0,0,2,3,0,0,3,0,3,3,2,3,3,3,3,3,0,0,3,3,3,0,0,0,3,3, +0,0,3,0,0,0,0,2,0,0,2,1,1,3,0,0,1,0,0,2,3,0,1,0,0,0,0,0,0,0,1,0, +3,3,3,3,2,3,3,3,3,3,3,3,1,2,1,3,3,2,2,1,2,2,2,3,1,1,2,0,2,1,2,1, +2,2,1,0,0,0,1,1,0,1,0,1,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0, +3,0,2,1,2,3,3,3,0,2,0,2,2,0,2,1,3,2,2,1,2,1,0,0,2,2,1,0,2,1,2,2, +0,1,1,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,3,3,1,1,3,0,2,3,1,1,3,2,1,1,2,0,2,2,3,2,1,1,1,1,1,2, +3,0,0,1,3,1,2,1,2,0,3,0,0,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, +3,3,1,1,3,2,3,3,3,1,3,2,1,3,2,1,3,2,2,2,2,1,3,3,1,2,1,3,1,2,3,0, +2,1,1,3,2,2,2,1,2,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, +3,3,2,3,2,3,3,2,3,2,3,2,3,3,2,1,0,3,2,2,2,1,2,2,2,1,2,2,1,2,1,1, +2,2,2,3,0,1,3,1,1,1,1,0,1,1,0,2,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,3,2,2,1,1,3,2,3,2,3,2,0,3,2,2,1,2,0,2,2,2,1,2,2,2,2,1, +3,2,1,2,2,1,0,2,0,1,0,0,1,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,2,3,1,2,3,3,2,2,3,0,1,1,2,0,3,3,2,2,3,0,1,1,3,0,0,0,0, +3,1,0,3,3,0,2,0,2,1,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,3,2,3,3,0,1,3,1,1,2,1,2,1,1,3,1,1,0,2,3,1,1,1,1,1,1,1,1, +3,1,1,2,2,2,2,1,1,1,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,2,2,1,1,2,1,3,3,2,3,2,2,3,2,2,3,1,2,2,1,2,0,3,2,1,2,2,2,2,2,1, +3,2,1,2,2,2,1,1,1,1,0,0,1,1,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,1,3,3,0,2,1,0,3,2,0,0,3,1,0,1,1,0,1,0,0,0,0,0,1, +1,0,0,1,0,3,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,2,2,2,3,0,0,1,3,0,3,2,0,3,2,2,3,3,3,3,3,1,0,2,2,2,0,2,2,1,2, +0,2,3,0,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,0,2,3,1,3,3,2,3,3,0,3,3,0,3,2,2,3,2,3,3,3,0,0,2,2,3,0,1,1,1,3, +0,0,3,0,0,0,2,2,0,1,3,0,1,2,2,2,3,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1, +3,2,3,3,2,0,3,3,2,2,3,1,3,2,1,3,2,0,1,2,2,0,2,3,2,1,0,3,0,0,0,0, +3,0,0,2,3,1,3,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,3,2,2,2,1,2,0,1,3,1,1,3,1,3,0,0,2,1,1,1,1,2,1,1,1,0,2,1,0,1, +1,2,0,0,0,3,1,1,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,0,3,1,0,0,0,1,0, +3,3,3,3,2,2,2,2,2,1,3,1,1,1,2,0,1,1,2,1,2,1,3,2,0,0,3,1,1,1,1,1, +3,1,0,2,3,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,2,3,0,3,3,0,2,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,3,1,3,0,0,1,2,0,0,2,0,3,3,2,3,3,3,2,3,0,0,2,2,2,0,0,0,2,2, +0,0,1,0,0,0,0,3,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +0,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,1,2,3,1,3,3,0,0,1,0,3,0,0,0,0,0, +0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,1,2,3,1,2,3,1,0,3,0,2,2,1,0,2,1,1,2,0,1,0,0,1,1,1,1,0,1,0,0, +1,0,0,0,0,1,1,0,3,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,0,1,1,1,3,1,2,2,2,2,2,2,1,1,1,1,0,3,1,0,1,3,1,1,1,1, +1,1,0,2,0,1,3,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,1, +3,0,2,2,1,3,3,2,3,3,0,1,1,0,2,2,1,2,1,3,3,1,0,0,3,2,0,0,0,0,2,1, +0,1,0,0,0,0,1,2,0,1,1,3,1,1,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,3,0,0,1,0,0,0,3,0,0,3,0,3,1,0,1,1,1,3,2,0,0,0,3,0,0,0,0,2,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,1,3,2,1,3,3,1,2,2,0,1,2,1,0,1,2,0,0,0,0,0,3,0,0,0,3,0,0,0,0, +3,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,2,0,3,3,3,2,2,0,1,1,0,1,3,0,0,0,2,2,0,0,0,0,3,1,0,1,0,0,0, +0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,2,3,1,2,0,0,2,1,0,3,1,0,1,2,0,1,1,1,1,3,0,0,3,1,1,0,2,2,1,1, +0,2,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,3,1,2,0,0,2,2,0,1,2,0,1,0,1,3,1,2,1,0,0,0,2,0,3,0,0,0,1,0, +0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,1,2,2,0,0,0,2,0,2,1,0,1,1,0,1,1,1,2,1,0,0,1,1,1,0,2,1,1,1, +0,1,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1, +0,0,0,2,0,1,3,1,1,1,1,0,0,0,0,3,2,0,1,0,0,0,1,2,0,0,0,1,0,0,0,0, +0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,2,3,2,2,0,0,0,1,0,0,0,0,2,3,2,1,2,2,3,0,0,0,2,3,1,0,0,0,1,1, +0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0, +3,3,2,2,0,1,0,0,0,0,2,0,2,0,1,0,0,0,1,1,0,0,0,2,1,0,1,0,1,1,0,0, +0,1,0,2,0,0,1,0,3,0,1,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,1,0,0,1,0,0,0,0,0,1,1,2,0,0,0,0,1,0,0,1,3,1,0,0,0,0,1,1,0,0, +0,1,0,0,0,0,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0, +3,3,1,1,1,1,2,3,0,0,2,1,1,1,1,1,0,2,1,1,0,0,0,2,1,0,1,2,1,1,0,1, +2,1,0,3,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,3,1,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1, +0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,0,0,0,0,1,2,1,0,1,1,0,2,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,2,0,0,0,1,3,0,1,0,0,0,2,0,0,0,0,0,0,0,1,2,0,0,0,0,0, +3,3,0,0,1,1,2,0,0,1,2,1,0,1,1,1,0,1,1,0,0,2,1,1,0,1,0,0,1,1,1,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,1,0,0,0,0,1,0,0,0,0,3,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,0,0,1,1,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,0,1,2,0,1,2,0,0,1,1,0,2,0,1,0,0,1,0,0,0,0,1,0,0,0,2,0,0,0,0, +1,0,0,1,0,1,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,0,0,0,0,0,0,0,1,1,0,1,1,0,2,1,3,0,0,0,0,1,1,0,0,0,0,0,0,0,3, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,1,0,0,2,0,0,2,0,0,1,1,2,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0, +1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,3,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0, +1,0,0,0,0,0,0,0,0,1,0,0,0,0,2,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,1,0,0,2,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +) + +TIS620ThaiModel = { + 'charToOrderMap': TIS620CharToOrderMap, + 'precedenceMatrix': ThaiLangModel, + 'mTypicalPositiveRatio': 0.926386, + 'keepEnglishLetter': False, + 'charsetName': "TIS-620" +} + +# flake8: noqa diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/latin1prober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/latin1prober.py new file mode 100644 index 0000000..eef3573 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/latin1prober.py @@ -0,0 +1,139 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .constants import eNotMe +from .compat import wrap_ord + +FREQ_CAT_NUM = 4 + +UDF = 0 # undefined +OTH = 1 # other +ASC = 2 # ascii capital letter +ASS = 3 # ascii small letter +ACV = 4 # accent capital vowel +ACO = 5 # accent capital other +ASV = 6 # accent small vowel +ASO = 7 # accent small other +CLASS_NUM = 8 # total classes + +Latin1_CharToClass = ( + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 00 - 07 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 08 - 0F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 10 - 17 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 18 - 1F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 20 - 27 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 28 - 2F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 30 - 37 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 38 - 3F + OTH, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 40 - 47 + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 48 - 4F + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 50 - 57 + ASC, ASC, ASC, OTH, OTH, OTH, OTH, OTH, # 58 - 5F + OTH, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 60 - 67 + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 68 - 6F + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 70 - 77 + ASS, ASS, ASS, OTH, OTH, OTH, OTH, OTH, # 78 - 7F + OTH, UDF, OTH, ASO, OTH, OTH, OTH, OTH, # 80 - 87 + OTH, OTH, ACO, OTH, ACO, UDF, ACO, UDF, # 88 - 8F + UDF, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 90 - 97 + OTH, OTH, ASO, OTH, ASO, UDF, ASO, ACO, # 98 - 9F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A0 - A7 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A8 - AF + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B0 - B7 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B8 - BF + ACV, ACV, ACV, ACV, ACV, ACV, ACO, ACO, # C0 - C7 + ACV, ACV, ACV, ACV, ACV, ACV, ACV, ACV, # C8 - CF + ACO, ACO, ACV, ACV, ACV, ACV, ACV, OTH, # D0 - D7 + ACV, ACV, ACV, ACV, ACV, ACO, ACO, ACO, # D8 - DF + ASV, ASV, ASV, ASV, ASV, ASV, ASO, ASO, # E0 - E7 + ASV, ASV, ASV, ASV, ASV, ASV, ASV, ASV, # E8 - EF + ASO, ASO, ASV, ASV, ASV, ASV, ASV, OTH, # F0 - F7 + ASV, ASV, ASV, ASV, ASV, ASO, ASO, ASO, # F8 - FF +) + +# 0 : illegal +# 1 : very unlikely +# 2 : normal +# 3 : very likely +Latin1ClassModel = ( + # UDF OTH ASC ASS ACV ACO ASV ASO + 0, 0, 0, 0, 0, 0, 0, 0, # UDF + 0, 3, 3, 3, 3, 3, 3, 3, # OTH + 0, 3, 3, 3, 3, 3, 3, 3, # ASC + 0, 3, 3, 3, 1, 1, 3, 3, # ASS + 0, 3, 3, 3, 1, 2, 1, 2, # ACV + 0, 3, 3, 3, 3, 3, 3, 3, # ACO + 0, 3, 1, 3, 1, 1, 1, 3, # ASV + 0, 3, 1, 3, 1, 1, 3, 3, # ASO +) + + +class Latin1Prober(CharSetProber): + def __init__(self): + CharSetProber.__init__(self) + self.reset() + + def reset(self): + self._mLastCharClass = OTH + self._mFreqCounter = [0] * FREQ_CAT_NUM + CharSetProber.reset(self) + + def get_charset_name(self): + return "windows-1252" + + def feed(self, aBuf): + aBuf = self.filter_with_english_letters(aBuf) + for c in aBuf: + charClass = Latin1_CharToClass[wrap_ord(c)] + freq = Latin1ClassModel[(self._mLastCharClass * CLASS_NUM) + + charClass] + if freq == 0: + self._mState = eNotMe + break + self._mFreqCounter[freq] += 1 + self._mLastCharClass = charClass + + return self.get_state() + + def get_confidence(self): + if self.get_state() == eNotMe: + return 0.01 + + total = sum(self._mFreqCounter) + if total < 0.01: + confidence = 0.0 + else: + confidence = ((self._mFreqCounter[3] - self._mFreqCounter[1] * 20.0) + / total) + if confidence < 0.0: + confidence = 0.0 + # lower the confidence of latin1 so that other more accurate + # detector can take priority. + confidence = confidence * 0.73 + return confidence diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/mbcharsetprober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/mbcharsetprober.py new file mode 100644 index 0000000..bb42f2f --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/mbcharsetprober.py @@ -0,0 +1,86 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Proofpoint, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import sys +from . import constants +from .charsetprober import CharSetProber + + +class MultiByteCharSetProber(CharSetProber): + def __init__(self): + CharSetProber.__init__(self) + self._mDistributionAnalyzer = None + self._mCodingSM = None + self._mLastChar = [0, 0] + + def reset(self): + CharSetProber.reset(self) + if self._mCodingSM: + self._mCodingSM.reset() + if self._mDistributionAnalyzer: + self._mDistributionAnalyzer.reset() + self._mLastChar = [0, 0] + + def get_charset_name(self): + pass + + def feed(self, aBuf): + aLen = len(aBuf) + for i in range(0, aLen): + codingState = self._mCodingSM.next_state(aBuf[i]) + if codingState == constants.eError: + if constants._debug: + sys.stderr.write(self.get_charset_name() + + ' prober hit error at byte ' + str(i) + + '\n') + self._mState = constants.eNotMe + break + elif codingState == constants.eItsMe: + self._mState = constants.eFoundIt + break + elif codingState == constants.eStart: + charLen = self._mCodingSM.get_current_charlen() + if i == 0: + self._mLastChar[1] = aBuf[0] + self._mDistributionAnalyzer.feed(self._mLastChar, charLen) + else: + self._mDistributionAnalyzer.feed(aBuf[i - 1:i + 1], + charLen) + + self._mLastChar[0] = aBuf[aLen - 1] + + if self.get_state() == constants.eDetecting: + if (self._mDistributionAnalyzer.got_enough_data() and + (self.get_confidence() > constants.SHORTCUT_THRESHOLD)): + self._mState = constants.eFoundIt + + return self.get_state() + + def get_confidence(self): + return self._mDistributionAnalyzer.get_confidence() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/mbcsgroupprober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/mbcsgroupprober.py new file mode 100644 index 0000000..03c9dcf --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/mbcsgroupprober.py @@ -0,0 +1,54 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Proofpoint, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetgroupprober import CharSetGroupProber +from .utf8prober import UTF8Prober +from .sjisprober import SJISProber +from .eucjpprober import EUCJPProber +from .gb2312prober import GB2312Prober +from .euckrprober import EUCKRProber +from .cp949prober import CP949Prober +from .big5prober import Big5Prober +from .euctwprober import EUCTWProber + + +class MBCSGroupProber(CharSetGroupProber): + def __init__(self): + CharSetGroupProber.__init__(self) + self._mProbers = [ + UTF8Prober(), + SJISProber(), + EUCJPProber(), + GB2312Prober(), + EUCKRProber(), + CP949Prober(), + Big5Prober(), + EUCTWProber() + ] + self.reset() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/mbcssm.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/mbcssm.py new file mode 100644 index 0000000..efe678c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/mbcssm.py @@ -0,0 +1,572 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .constants import eStart, eError, eItsMe + +# BIG5 + +BIG5_cls = ( + 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as legal value + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,1, # 78 - 7f + 4,4,4,4,4,4,4,4, # 80 - 87 + 4,4,4,4,4,4,4,4, # 88 - 8f + 4,4,4,4,4,4,4,4, # 90 - 97 + 4,4,4,4,4,4,4,4, # 98 - 9f + 4,3,3,3,3,3,3,3, # a0 - a7 + 3,3,3,3,3,3,3,3, # a8 - af + 3,3,3,3,3,3,3,3, # b0 - b7 + 3,3,3,3,3,3,3,3, # b8 - bf + 3,3,3,3,3,3,3,3, # c0 - c7 + 3,3,3,3,3,3,3,3, # c8 - cf + 3,3,3,3,3,3,3,3, # d0 - d7 + 3,3,3,3,3,3,3,3, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,3,3,3, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,3,3,0 # f8 - ff +) + +BIG5_st = ( + eError,eStart,eStart, 3,eError,eError,eError,eError,#00-07 + eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eError,#08-0f + eError,eStart,eStart,eStart,eStart,eStart,eStart,eStart#10-17 +) + +Big5CharLenTable = (0, 1, 1, 2, 0) + +Big5SMModel = {'classTable': BIG5_cls, + 'classFactor': 5, + 'stateTable': BIG5_st, + 'charLenTable': Big5CharLenTable, + 'name': 'Big5'} + +# CP949 + +CP949_cls = ( + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,0,0, # 00 - 0f + 1,1,1,1,1,1,1,1, 1,1,1,0,1,1,1,1, # 10 - 1f + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 20 - 2f + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 30 - 3f + 1,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4, # 40 - 4f + 4,4,5,5,5,5,5,5, 5,5,5,1,1,1,1,1, # 50 - 5f + 1,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5, # 60 - 6f + 5,5,5,5,5,5,5,5, 5,5,5,1,1,1,1,1, # 70 - 7f + 0,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6, # 80 - 8f + 6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6, # 90 - 9f + 6,7,7,7,7,7,7,7, 7,7,7,7,7,8,8,8, # a0 - af + 7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7, # b0 - bf + 7,7,7,7,7,7,9,2, 2,3,2,2,2,2,2,2, # c0 - cf + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, # d0 - df + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, # e0 - ef + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,0, # f0 - ff +) + +CP949_st = ( +#cls= 0 1 2 3 4 5 6 7 8 9 # previous state = + eError,eStart, 3,eError,eStart,eStart, 4, 5,eError, 6, # eStart + eError,eError,eError,eError,eError,eError,eError,eError,eError,eError, # eError + eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe, # eItsMe + eError,eError,eStart,eStart,eError,eError,eError,eStart,eStart,eStart, # 3 + eError,eError,eStart,eStart,eStart,eStart,eStart,eStart,eStart,eStart, # 4 + eError,eStart,eStart,eStart,eStart,eStart,eStart,eStart,eStart,eStart, # 5 + eError,eStart,eStart,eStart,eStart,eError,eError,eStart,eStart,eStart, # 6 +) + +CP949CharLenTable = (0, 1, 2, 0, 1, 1, 2, 2, 0, 2) + +CP949SMModel = {'classTable': CP949_cls, + 'classFactor': 10, + 'stateTable': CP949_st, + 'charLenTable': CP949CharLenTable, + 'name': 'CP949'} + +# EUC-JP + +EUCJP_cls = ( + 4,4,4,4,4,4,4,4, # 00 - 07 + 4,4,4,4,4,4,5,5, # 08 - 0f + 4,4,4,4,4,4,4,4, # 10 - 17 + 4,4,4,5,4,4,4,4, # 18 - 1f + 4,4,4,4,4,4,4,4, # 20 - 27 + 4,4,4,4,4,4,4,4, # 28 - 2f + 4,4,4,4,4,4,4,4, # 30 - 37 + 4,4,4,4,4,4,4,4, # 38 - 3f + 4,4,4,4,4,4,4,4, # 40 - 47 + 4,4,4,4,4,4,4,4, # 48 - 4f + 4,4,4,4,4,4,4,4, # 50 - 57 + 4,4,4,4,4,4,4,4, # 58 - 5f + 4,4,4,4,4,4,4,4, # 60 - 67 + 4,4,4,4,4,4,4,4, # 68 - 6f + 4,4,4,4,4,4,4,4, # 70 - 77 + 4,4,4,4,4,4,4,4, # 78 - 7f + 5,5,5,5,5,5,5,5, # 80 - 87 + 5,5,5,5,5,5,1,3, # 88 - 8f + 5,5,5,5,5,5,5,5, # 90 - 97 + 5,5,5,5,5,5,5,5, # 98 - 9f + 5,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,2,2,2, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,2,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,0,5 # f8 - ff +) + +EUCJP_st = ( + 3, 4, 3, 5,eStart,eError,eError,eError,#00-07 + eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,#08-0f + eItsMe,eItsMe,eStart,eError,eStart,eError,eError,eError,#10-17 + eError,eError,eStart,eError,eError,eError, 3,eError,#18-1f + 3,eError,eError,eError,eStart,eStart,eStart,eStart#20-27 +) + +EUCJPCharLenTable = (2, 2, 2, 3, 1, 0) + +EUCJPSMModel = {'classTable': EUCJP_cls, + 'classFactor': 6, + 'stateTable': EUCJP_st, + 'charLenTable': EUCJPCharLenTable, + 'name': 'EUC-JP'} + +# EUC-KR + +EUCKR_cls = ( + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 1,1,1,1,1,1,1,1, # 40 - 47 + 1,1,1,1,1,1,1,1, # 48 - 4f + 1,1,1,1,1,1,1,1, # 50 - 57 + 1,1,1,1,1,1,1,1, # 58 - 5f + 1,1,1,1,1,1,1,1, # 60 - 67 + 1,1,1,1,1,1,1,1, # 68 - 6f + 1,1,1,1,1,1,1,1, # 70 - 77 + 1,1,1,1,1,1,1,1, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,3,3,3, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,3,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 2,2,2,2,2,2,2,2, # e0 - e7 + 2,2,2,2,2,2,2,2, # e8 - ef + 2,2,2,2,2,2,2,2, # f0 - f7 + 2,2,2,2,2,2,2,0 # f8 - ff +) + +EUCKR_st = ( + eError,eStart, 3,eError,eError,eError,eError,eError,#00-07 + eItsMe,eItsMe,eItsMe,eItsMe,eError,eError,eStart,eStart #08-0f +) + +EUCKRCharLenTable = (0, 1, 2, 0) + +EUCKRSMModel = {'classTable': EUCKR_cls, + 'classFactor': 4, + 'stateTable': EUCKR_st, + 'charLenTable': EUCKRCharLenTable, + 'name': 'EUC-KR'} + +# EUC-TW + +EUCTW_cls = ( + 2,2,2,2,2,2,2,2, # 00 - 07 + 2,2,2,2,2,2,0,0, # 08 - 0f + 2,2,2,2,2,2,2,2, # 10 - 17 + 2,2,2,0,2,2,2,2, # 18 - 1f + 2,2,2,2,2,2,2,2, # 20 - 27 + 2,2,2,2,2,2,2,2, # 28 - 2f + 2,2,2,2,2,2,2,2, # 30 - 37 + 2,2,2,2,2,2,2,2, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,2, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,6,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,3,4,4,4,4,4,4, # a0 - a7 + 5,5,1,1,1,1,1,1, # a8 - af + 1,1,1,1,1,1,1,1, # b0 - b7 + 1,1,1,1,1,1,1,1, # b8 - bf + 1,1,3,1,3,3,3,3, # c0 - c7 + 3,3,3,3,3,3,3,3, # c8 - cf + 3,3,3,3,3,3,3,3, # d0 - d7 + 3,3,3,3,3,3,3,3, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,3,3,3, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,3,3,0 # f8 - ff +) + +EUCTW_st = ( + eError,eError,eStart, 3, 3, 3, 4,eError,#00-07 + eError,eError,eError,eError,eError,eError,eItsMe,eItsMe,#08-0f + eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eError,eStart,eError,#10-17 + eStart,eStart,eStart,eError,eError,eError,eError,eError,#18-1f + 5,eError,eError,eError,eStart,eError,eStart,eStart,#20-27 + eStart,eError,eStart,eStart,eStart,eStart,eStart,eStart #28-2f +) + +EUCTWCharLenTable = (0, 0, 1, 2, 2, 2, 3) + +EUCTWSMModel = {'classTable': EUCTW_cls, + 'classFactor': 7, + 'stateTable': EUCTW_st, + 'charLenTable': EUCTWCharLenTable, + 'name': 'x-euc-tw'} + +# GB2312 + +GB2312_cls = ( + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 3,3,3,3,3,3,3,3, # 30 - 37 + 3,3,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,4, # 78 - 7f + 5,6,6,6,6,6,6,6, # 80 - 87 + 6,6,6,6,6,6,6,6, # 88 - 8f + 6,6,6,6,6,6,6,6, # 90 - 97 + 6,6,6,6,6,6,6,6, # 98 - 9f + 6,6,6,6,6,6,6,6, # a0 - a7 + 6,6,6,6,6,6,6,6, # a8 - af + 6,6,6,6,6,6,6,6, # b0 - b7 + 6,6,6,6,6,6,6,6, # b8 - bf + 6,6,6,6,6,6,6,6, # c0 - c7 + 6,6,6,6,6,6,6,6, # c8 - cf + 6,6,6,6,6,6,6,6, # d0 - d7 + 6,6,6,6,6,6,6,6, # d8 - df + 6,6,6,6,6,6,6,6, # e0 - e7 + 6,6,6,6,6,6,6,6, # e8 - ef + 6,6,6,6,6,6,6,6, # f0 - f7 + 6,6,6,6,6,6,6,0 # f8 - ff +) + +GB2312_st = ( + eError,eStart,eStart,eStart,eStart,eStart, 3,eError,#00-07 + eError,eError,eError,eError,eError,eError,eItsMe,eItsMe,#08-0f + eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eError,eError,eStart,#10-17 + 4,eError,eStart,eStart,eError,eError,eError,eError,#18-1f + eError,eError, 5,eError,eError,eError,eItsMe,eError,#20-27 + eError,eError,eStart,eStart,eStart,eStart,eStart,eStart #28-2f +) + +# To be accurate, the length of class 6 can be either 2 or 4. +# But it is not necessary to discriminate between the two since +# it is used for frequency analysis only, and we are validing +# each code range there as well. So it is safe to set it to be +# 2 here. +GB2312CharLenTable = (0, 1, 1, 1, 1, 1, 2) + +GB2312SMModel = {'classTable': GB2312_cls, + 'classFactor': 7, + 'stateTable': GB2312_st, + 'charLenTable': GB2312CharLenTable, + 'name': 'GB2312'} + +# Shift_JIS + +SJIS_cls = ( + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,1, # 78 - 7f + 3,3,3,3,3,2,2,3, # 80 - 87 + 3,3,3,3,3,3,3,3, # 88 - 8f + 3,3,3,3,3,3,3,3, # 90 - 97 + 3,3,3,3,3,3,3,3, # 98 - 9f + #0xa0 is illegal in sjis encoding, but some pages does + #contain such byte. We need to be more error forgiven. + 2,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,2,2,2, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,2,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,4,4,4, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,0,0,0) # f8 - ff + + +SJIS_st = ( + eError,eStart,eStart, 3,eError,eError,eError,eError,#00-07 + eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,#08-0f + eItsMe,eItsMe,eError,eError,eStart,eStart,eStart,eStart #10-17 +) + +SJISCharLenTable = (0, 1, 1, 2, 0, 0) + +SJISSMModel = {'classTable': SJIS_cls, + 'classFactor': 6, + 'stateTable': SJIS_st, + 'charLenTable': SJISCharLenTable, + 'name': 'Shift_JIS'} + +# UCS2-BE + +UCS2BE_cls = ( + 0,0,0,0,0,0,0,0, # 00 - 07 + 0,0,1,0,0,2,0,0, # 08 - 0f + 0,0,0,0,0,0,0,0, # 10 - 17 + 0,0,0,3,0,0,0,0, # 18 - 1f + 0,0,0,0,0,0,0,0, # 20 - 27 + 0,3,3,3,3,3,0,0, # 28 - 2f + 0,0,0,0,0,0,0,0, # 30 - 37 + 0,0,0,0,0,0,0,0, # 38 - 3f + 0,0,0,0,0,0,0,0, # 40 - 47 + 0,0,0,0,0,0,0,0, # 48 - 4f + 0,0,0,0,0,0,0,0, # 50 - 57 + 0,0,0,0,0,0,0,0, # 58 - 5f + 0,0,0,0,0,0,0,0, # 60 - 67 + 0,0,0,0,0,0,0,0, # 68 - 6f + 0,0,0,0,0,0,0,0, # 70 - 77 + 0,0,0,0,0,0,0,0, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,0,0,0,0,0,0,0, # a0 - a7 + 0,0,0,0,0,0,0,0, # a8 - af + 0,0,0,0,0,0,0,0, # b0 - b7 + 0,0,0,0,0,0,0,0, # b8 - bf + 0,0,0,0,0,0,0,0, # c0 - c7 + 0,0,0,0,0,0,0,0, # c8 - cf + 0,0,0,0,0,0,0,0, # d0 - d7 + 0,0,0,0,0,0,0,0, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,4,5 # f8 - ff +) + +UCS2BE_st = ( + 5, 7, 7,eError, 4, 3,eError,eError,#00-07 + eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,#08-0f + eItsMe,eItsMe, 6, 6, 6, 6,eError,eError,#10-17 + 6, 6, 6, 6, 6,eItsMe, 6, 6,#18-1f + 6, 6, 6, 6, 5, 7, 7,eError,#20-27 + 5, 8, 6, 6,eError, 6, 6, 6,#28-2f + 6, 6, 6, 6,eError,eError,eStart,eStart #30-37 +) + +UCS2BECharLenTable = (2, 2, 2, 0, 2, 2) + +UCS2BESMModel = {'classTable': UCS2BE_cls, + 'classFactor': 6, + 'stateTable': UCS2BE_st, + 'charLenTable': UCS2BECharLenTable, + 'name': 'UTF-16BE'} + +# UCS2-LE + +UCS2LE_cls = ( + 0,0,0,0,0,0,0,0, # 00 - 07 + 0,0,1,0,0,2,0,0, # 08 - 0f + 0,0,0,0,0,0,0,0, # 10 - 17 + 0,0,0,3,0,0,0,0, # 18 - 1f + 0,0,0,0,0,0,0,0, # 20 - 27 + 0,3,3,3,3,3,0,0, # 28 - 2f + 0,0,0,0,0,0,0,0, # 30 - 37 + 0,0,0,0,0,0,0,0, # 38 - 3f + 0,0,0,0,0,0,0,0, # 40 - 47 + 0,0,0,0,0,0,0,0, # 48 - 4f + 0,0,0,0,0,0,0,0, # 50 - 57 + 0,0,0,0,0,0,0,0, # 58 - 5f + 0,0,0,0,0,0,0,0, # 60 - 67 + 0,0,0,0,0,0,0,0, # 68 - 6f + 0,0,0,0,0,0,0,0, # 70 - 77 + 0,0,0,0,0,0,0,0, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,0,0,0,0,0,0,0, # a0 - a7 + 0,0,0,0,0,0,0,0, # a8 - af + 0,0,0,0,0,0,0,0, # b0 - b7 + 0,0,0,0,0,0,0,0, # b8 - bf + 0,0,0,0,0,0,0,0, # c0 - c7 + 0,0,0,0,0,0,0,0, # c8 - cf + 0,0,0,0,0,0,0,0, # d0 - d7 + 0,0,0,0,0,0,0,0, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,4,5 # f8 - ff +) + +UCS2LE_st = ( + 6, 6, 7, 6, 4, 3,eError,eError,#00-07 + eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,#08-0f + eItsMe,eItsMe, 5, 5, 5,eError,eItsMe,eError,#10-17 + 5, 5, 5,eError, 5,eError, 6, 6,#18-1f + 7, 6, 8, 8, 5, 5, 5,eError,#20-27 + 5, 5, 5,eError,eError,eError, 5, 5,#28-2f + 5, 5, 5,eError, 5,eError,eStart,eStart #30-37 +) + +UCS2LECharLenTable = (2, 2, 2, 2, 2, 2) + +UCS2LESMModel = {'classTable': UCS2LE_cls, + 'classFactor': 6, + 'stateTable': UCS2LE_st, + 'charLenTable': UCS2LECharLenTable, + 'name': 'UTF-16LE'} + +# UTF-8 + +UTF8_cls = ( + 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as a legal value + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 1,1,1,1,1,1,1,1, # 40 - 47 + 1,1,1,1,1,1,1,1, # 48 - 4f + 1,1,1,1,1,1,1,1, # 50 - 57 + 1,1,1,1,1,1,1,1, # 58 - 5f + 1,1,1,1,1,1,1,1, # 60 - 67 + 1,1,1,1,1,1,1,1, # 68 - 6f + 1,1,1,1,1,1,1,1, # 70 - 77 + 1,1,1,1,1,1,1,1, # 78 - 7f + 2,2,2,2,3,3,3,3, # 80 - 87 + 4,4,4,4,4,4,4,4, # 88 - 8f + 4,4,4,4,4,4,4,4, # 90 - 97 + 4,4,4,4,4,4,4,4, # 98 - 9f + 5,5,5,5,5,5,5,5, # a0 - a7 + 5,5,5,5,5,5,5,5, # a8 - af + 5,5,5,5,5,5,5,5, # b0 - b7 + 5,5,5,5,5,5,5,5, # b8 - bf + 0,0,6,6,6,6,6,6, # c0 - c7 + 6,6,6,6,6,6,6,6, # c8 - cf + 6,6,6,6,6,6,6,6, # d0 - d7 + 6,6,6,6,6,6,6,6, # d8 - df + 7,8,8,8,8,8,8,8, # e0 - e7 + 8,8,8,8,8,9,8,8, # e8 - ef + 10,11,11,11,11,11,11,11, # f0 - f7 + 12,13,13,13,14,15,0,0 # f8 - ff +) + +UTF8_st = ( + eError,eStart,eError,eError,eError,eError, 12, 10,#00-07 + 9, 11, 8, 7, 6, 5, 4, 3,#08-0f + eError,eError,eError,eError,eError,eError,eError,eError,#10-17 + eError,eError,eError,eError,eError,eError,eError,eError,#18-1f + eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,#20-27 + eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,#28-2f + eError,eError, 5, 5, 5, 5,eError,eError,#30-37 + eError,eError,eError,eError,eError,eError,eError,eError,#38-3f + eError,eError,eError, 5, 5, 5,eError,eError,#40-47 + eError,eError,eError,eError,eError,eError,eError,eError,#48-4f + eError,eError, 7, 7, 7, 7,eError,eError,#50-57 + eError,eError,eError,eError,eError,eError,eError,eError,#58-5f + eError,eError,eError,eError, 7, 7,eError,eError,#60-67 + eError,eError,eError,eError,eError,eError,eError,eError,#68-6f + eError,eError, 9, 9, 9, 9,eError,eError,#70-77 + eError,eError,eError,eError,eError,eError,eError,eError,#78-7f + eError,eError,eError,eError,eError, 9,eError,eError,#80-87 + eError,eError,eError,eError,eError,eError,eError,eError,#88-8f + eError,eError, 12, 12, 12, 12,eError,eError,#90-97 + eError,eError,eError,eError,eError,eError,eError,eError,#98-9f + eError,eError,eError,eError,eError, 12,eError,eError,#a0-a7 + eError,eError,eError,eError,eError,eError,eError,eError,#a8-af + eError,eError, 12, 12, 12,eError,eError,eError,#b0-b7 + eError,eError,eError,eError,eError,eError,eError,eError,#b8-bf + eError,eError,eStart,eStart,eStart,eStart,eError,eError,#c0-c7 + eError,eError,eError,eError,eError,eError,eError,eError #c8-cf +) + +UTF8CharLenTable = (0, 1, 0, 0, 0, 0, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6) + +UTF8SMModel = {'classTable': UTF8_cls, + 'classFactor': 16, + 'stateTable': UTF8_st, + 'charLenTable': UTF8CharLenTable, + 'name': 'UTF-8'} diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/sbcharsetprober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/sbcharsetprober.py new file mode 100644 index 0000000..37291bd --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/sbcharsetprober.py @@ -0,0 +1,120 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import sys +from . import constants +from .charsetprober import CharSetProber +from .compat import wrap_ord + +SAMPLE_SIZE = 64 +SB_ENOUGH_REL_THRESHOLD = 1024 +POSITIVE_SHORTCUT_THRESHOLD = 0.95 +NEGATIVE_SHORTCUT_THRESHOLD = 0.05 +SYMBOL_CAT_ORDER = 250 +NUMBER_OF_SEQ_CAT = 4 +POSITIVE_CAT = NUMBER_OF_SEQ_CAT - 1 +#NEGATIVE_CAT = 0 + + +class SingleByteCharSetProber(CharSetProber): + def __init__(self, model, reversed=False, nameProber=None): + CharSetProber.__init__(self) + self._mModel = model + # TRUE if we need to reverse every pair in the model lookup + self._mReversed = reversed + # Optional auxiliary prober for name decision + self._mNameProber = nameProber + self.reset() + + def reset(self): + CharSetProber.reset(self) + # char order of last character + self._mLastOrder = 255 + self._mSeqCounters = [0] * NUMBER_OF_SEQ_CAT + self._mTotalSeqs = 0 + self._mTotalChar = 0 + # characters that fall in our sampling range + self._mFreqChar = 0 + + def get_charset_name(self): + if self._mNameProber: + return self._mNameProber.get_charset_name() + else: + return self._mModel['charsetName'] + + def feed(self, aBuf): + if not self._mModel['keepEnglishLetter']: + aBuf = self.filter_without_english_letters(aBuf) + aLen = len(aBuf) + if not aLen: + return self.get_state() + for c in aBuf: + order = self._mModel['charToOrderMap'][wrap_ord(c)] + if order < SYMBOL_CAT_ORDER: + self._mTotalChar += 1 + if order < SAMPLE_SIZE: + self._mFreqChar += 1 + if self._mLastOrder < SAMPLE_SIZE: + self._mTotalSeqs += 1 + if not self._mReversed: + i = (self._mLastOrder * SAMPLE_SIZE) + order + model = self._mModel['precedenceMatrix'][i] + else: # reverse the order of the letters in the lookup + i = (order * SAMPLE_SIZE) + self._mLastOrder + model = self._mModel['precedenceMatrix'][i] + self._mSeqCounters[model] += 1 + self._mLastOrder = order + + if self.get_state() == constants.eDetecting: + if self._mTotalSeqs > SB_ENOUGH_REL_THRESHOLD: + cf = self.get_confidence() + if cf > POSITIVE_SHORTCUT_THRESHOLD: + if constants._debug: + sys.stderr.write('%s confidence = %s, we have a' + 'winner\n' % + (self._mModel['charsetName'], cf)) + self._mState = constants.eFoundIt + elif cf < NEGATIVE_SHORTCUT_THRESHOLD: + if constants._debug: + sys.stderr.write('%s confidence = %s, below negative' + 'shortcut threshhold %s\n' % + (self._mModel['charsetName'], cf, + NEGATIVE_SHORTCUT_THRESHOLD)) + self._mState = constants.eNotMe + + return self.get_state() + + def get_confidence(self): + r = 0.01 + if self._mTotalSeqs > 0: + r = ((1.0 * self._mSeqCounters[POSITIVE_CAT]) / self._mTotalSeqs + / self._mModel['mTypicalPositiveRatio']) + r = r * self._mFreqChar / self._mTotalChar + if r >= 1.0: + r = 0.99 + return r diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/sbcsgroupprober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/sbcsgroupprober.py new file mode 100644 index 0000000..1b6196c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/sbcsgroupprober.py @@ -0,0 +1,69 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetgroupprober import CharSetGroupProber +from .sbcharsetprober import SingleByteCharSetProber +from .langcyrillicmodel import (Win1251CyrillicModel, Koi8rModel, + Latin5CyrillicModel, MacCyrillicModel, + Ibm866Model, Ibm855Model) +from .langgreekmodel import Latin7GreekModel, Win1253GreekModel +from .langbulgarianmodel import Latin5BulgarianModel, Win1251BulgarianModel +from .langhungarianmodel import Latin2HungarianModel, Win1250HungarianModel +from .langthaimodel import TIS620ThaiModel +from .langhebrewmodel import Win1255HebrewModel +from .hebrewprober import HebrewProber + + +class SBCSGroupProber(CharSetGroupProber): + def __init__(self): + CharSetGroupProber.__init__(self) + self._mProbers = [ + SingleByteCharSetProber(Win1251CyrillicModel), + SingleByteCharSetProber(Koi8rModel), + SingleByteCharSetProber(Latin5CyrillicModel), + SingleByteCharSetProber(MacCyrillicModel), + SingleByteCharSetProber(Ibm866Model), + SingleByteCharSetProber(Ibm855Model), + SingleByteCharSetProber(Latin7GreekModel), + SingleByteCharSetProber(Win1253GreekModel), + SingleByteCharSetProber(Latin5BulgarianModel), + SingleByteCharSetProber(Win1251BulgarianModel), + SingleByteCharSetProber(Latin2HungarianModel), + SingleByteCharSetProber(Win1250HungarianModel), + SingleByteCharSetProber(TIS620ThaiModel), + ] + hebrewProber = HebrewProber() + logicalHebrewProber = SingleByteCharSetProber(Win1255HebrewModel, + False, hebrewProber) + visualHebrewProber = SingleByteCharSetProber(Win1255HebrewModel, True, + hebrewProber) + hebrewProber.set_model_probers(logicalHebrewProber, visualHebrewProber) + self._mProbers.extend([hebrewProber, logicalHebrewProber, + visualHebrewProber]) + + self.reset() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/sjisprober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/sjisprober.py new file mode 100644 index 0000000..cd0e9e7 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/sjisprober.py @@ -0,0 +1,91 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import sys +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import SJISDistributionAnalysis +from .jpcntx import SJISContextAnalysis +from .mbcssm import SJISSMModel +from . import constants + + +class SJISProber(MultiByteCharSetProber): + def __init__(self): + MultiByteCharSetProber.__init__(self) + self._mCodingSM = CodingStateMachine(SJISSMModel) + self._mDistributionAnalyzer = SJISDistributionAnalysis() + self._mContextAnalyzer = SJISContextAnalysis() + self.reset() + + def reset(self): + MultiByteCharSetProber.reset(self) + self._mContextAnalyzer.reset() + + def get_charset_name(self): + return self._mContextAnalyzer.get_charset_name() + + def feed(self, aBuf): + aLen = len(aBuf) + for i in range(0, aLen): + codingState = self._mCodingSM.next_state(aBuf[i]) + if codingState == constants.eError: + if constants._debug: + sys.stderr.write(self.get_charset_name() + + ' prober hit error at byte ' + str(i) + + '\n') + self._mState = constants.eNotMe + break + elif codingState == constants.eItsMe: + self._mState = constants.eFoundIt + break + elif codingState == constants.eStart: + charLen = self._mCodingSM.get_current_charlen() + if i == 0: + self._mLastChar[1] = aBuf[0] + self._mContextAnalyzer.feed(self._mLastChar[2 - charLen:], + charLen) + self._mDistributionAnalyzer.feed(self._mLastChar, charLen) + else: + self._mContextAnalyzer.feed(aBuf[i + 1 - charLen:i + 3 + - charLen], charLen) + self._mDistributionAnalyzer.feed(aBuf[i - 1:i + 1], + charLen) + + self._mLastChar[0] = aBuf[aLen - 1] + + if self.get_state() == constants.eDetecting: + if (self._mContextAnalyzer.got_enough_data() and + (self.get_confidence() > constants.SHORTCUT_THRESHOLD)): + self._mState = constants.eFoundIt + + return self.get_state() + + def get_confidence(self): + contxtCf = self._mContextAnalyzer.get_confidence() + distribCf = self._mDistributionAnalyzer.get_confidence() + return max(contxtCf, distribCf) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/universaldetector.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/universaldetector.py new file mode 100644 index 0000000..476522b --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/universaldetector.py @@ -0,0 +1,170 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from . import constants +import sys +import codecs +from .latin1prober import Latin1Prober # windows-1252 +from .mbcsgroupprober import MBCSGroupProber # multi-byte character sets +from .sbcsgroupprober import SBCSGroupProber # single-byte character sets +from .escprober import EscCharSetProber # ISO-2122, etc. +import re + +MINIMUM_THRESHOLD = 0.20 +ePureAscii = 0 +eEscAscii = 1 +eHighbyte = 2 + + +class UniversalDetector: + def __init__(self): + self._highBitDetector = re.compile(b'[\x80-\xFF]') + self._escDetector = re.compile(b'(\033|~{)') + self._mEscCharSetProber = None + self._mCharSetProbers = [] + self.reset() + + def reset(self): + self.result = {'encoding': None, 'confidence': 0.0} + self.done = False + self._mStart = True + self._mGotData = False + self._mInputState = ePureAscii + self._mLastChar = b'' + if self._mEscCharSetProber: + self._mEscCharSetProber.reset() + for prober in self._mCharSetProbers: + prober.reset() + + def feed(self, aBuf): + if self.done: + return + + aLen = len(aBuf) + if not aLen: + return + + if not self._mGotData: + # If the data starts with BOM, we know it is UTF + if aBuf[:3] == codecs.BOM_UTF8: + # EF BB BF UTF-8 with BOM + self.result = {'encoding': "UTF-8-SIG", 'confidence': 1.0} + elif aBuf[:4] == codecs.BOM_UTF32_LE: + # FF FE 00 00 UTF-32, little-endian BOM + self.result = {'encoding': "UTF-32LE", 'confidence': 1.0} + elif aBuf[:4] == codecs.BOM_UTF32_BE: + # 00 00 FE FF UTF-32, big-endian BOM + self.result = {'encoding': "UTF-32BE", 'confidence': 1.0} + elif aBuf[:4] == b'\xFE\xFF\x00\x00': + # FE FF 00 00 UCS-4, unusual octet order BOM (3412) + self.result = { + 'encoding': "X-ISO-10646-UCS-4-3412", + 'confidence': 1.0 + } + elif aBuf[:4] == b'\x00\x00\xFF\xFE': + # 00 00 FF FE UCS-4, unusual octet order BOM (2143) + self.result = { + 'encoding': "X-ISO-10646-UCS-4-2143", + 'confidence': 1.0 + } + elif aBuf[:2] == codecs.BOM_LE: + # FF FE UTF-16, little endian BOM + self.result = {'encoding': "UTF-16LE", 'confidence': 1.0} + elif aBuf[:2] == codecs.BOM_BE: + # FE FF UTF-16, big endian BOM + self.result = {'encoding': "UTF-16BE", 'confidence': 1.0} + + self._mGotData = True + if self.result['encoding'] and (self.result['confidence'] > 0.0): + self.done = True + return + + if self._mInputState == ePureAscii: + if self._highBitDetector.search(aBuf): + self._mInputState = eHighbyte + elif ((self._mInputState == ePureAscii) and + self._escDetector.search(self._mLastChar + aBuf)): + self._mInputState = eEscAscii + + self._mLastChar = aBuf[-1:] + + if self._mInputState == eEscAscii: + if not self._mEscCharSetProber: + self._mEscCharSetProber = EscCharSetProber() + if self._mEscCharSetProber.feed(aBuf) == constants.eFoundIt: + self.result = {'encoding': self._mEscCharSetProber.get_charset_name(), + 'confidence': self._mEscCharSetProber.get_confidence()} + self.done = True + elif self._mInputState == eHighbyte: + if not self._mCharSetProbers: + self._mCharSetProbers = [MBCSGroupProber(), SBCSGroupProber(), + Latin1Prober()] + for prober in self._mCharSetProbers: + if prober.feed(aBuf) == constants.eFoundIt: + self.result = {'encoding': prober.get_charset_name(), + 'confidence': prober.get_confidence()} + self.done = True + break + + def close(self): + if self.done: + return + if not self._mGotData: + if constants._debug: + sys.stderr.write('no data received!\n') + return + self.done = True + + if self._mInputState == ePureAscii: + self.result = {'encoding': 'ascii', 'confidence': 1.0} + return self.result + + if self._mInputState == eHighbyte: + proberConfidence = None + maxProberConfidence = 0.0 + maxProber = None + for prober in self._mCharSetProbers: + if not prober: + continue + proberConfidence = prober.get_confidence() + if proberConfidence > maxProberConfidence: + maxProberConfidence = proberConfidence + maxProber = prober + if maxProber and (maxProberConfidence > MINIMUM_THRESHOLD): + self.result = {'encoding': maxProber.get_charset_name(), + 'confidence': maxProber.get_confidence()} + return self.result + + if constants._debug: + sys.stderr.write('no probers hit minimum threshhold\n') + for prober in self._mCharSetProbers[0].mProbers: + if not prober: + continue + sys.stderr.write('%s confidence = %s\n' % + (prober.get_charset_name(), + prober.get_confidence())) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/utf8prober.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/utf8prober.py new file mode 100644 index 0000000..1c0bb5d --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/chardet/utf8prober.py @@ -0,0 +1,76 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from . import constants +from .charsetprober import CharSetProber +from .codingstatemachine import CodingStateMachine +from .mbcssm import UTF8SMModel + +ONE_CHAR_PROB = 0.5 + + +class UTF8Prober(CharSetProber): + def __init__(self): + CharSetProber.__init__(self) + self._mCodingSM = CodingStateMachine(UTF8SMModel) + self.reset() + + def reset(self): + CharSetProber.reset(self) + self._mCodingSM.reset() + self._mNumOfMBChar = 0 + + def get_charset_name(self): + return "utf-8" + + def feed(self, aBuf): + for c in aBuf: + codingState = self._mCodingSM.next_state(c) + if codingState == constants.eError: + self._mState = constants.eNotMe + break + elif codingState == constants.eItsMe: + self._mState = constants.eFoundIt + break + elif codingState == constants.eStart: + if self._mCodingSM.get_current_charlen() >= 2: + self._mNumOfMBChar += 1 + + if self.get_state() == constants.eDetecting: + if self.get_confidence() > constants.SHORTCUT_THRESHOLD: + self._mState = constants.eFoundIt + + return self.get_state() + + def get_confidence(self): + unlike = 0.99 + if self._mNumOfMBChar < 6: + for i in range(0, self._mNumOfMBChar): + unlike = unlike * ONE_CHAR_PROB + return 1.0 - unlike + else: + return unlike diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/__init__.py new file mode 100644 index 0000000..c353674 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/__init__.py @@ -0,0 +1,96 @@ +""" +urllib3 - Thread-safe connection pooling and re-using. +""" + +from __future__ import absolute_import +import warnings + +from .connectionpool import ( + HTTPConnectionPool, + HTTPSConnectionPool, + connection_from_url +) + +from . import exceptions +from .filepost import encode_multipart_formdata +from .poolmanager import PoolManager, ProxyManager, proxy_from_url +from .response import HTTPResponse +from .util.request import make_headers +from .util.url import get_host +from .util.timeout import Timeout +from .util.retry import Retry + + +# Set default logging handler to avoid "No handler found" warnings. +import logging +try: # Python 2.7+ + from logging import NullHandler +except ImportError: + class NullHandler(logging.Handler): + def emit(self, record): + pass + +__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)' +__license__ = 'MIT' +__version__ = '1.16' + +__all__ = ( + 'HTTPConnectionPool', + 'HTTPSConnectionPool', + 'PoolManager', + 'ProxyManager', + 'HTTPResponse', + 'Retry', + 'Timeout', + 'add_stderr_logger', + 'connection_from_url', + 'disable_warnings', + 'encode_multipart_formdata', + 'get_host', + 'make_headers', + 'proxy_from_url', +) + +logging.getLogger(__name__).addHandler(NullHandler()) + + +def add_stderr_logger(level=logging.DEBUG): + """ + Helper for quickly adding a StreamHandler to the logger. Useful for + debugging. + + Returns the handler after adding it. + """ + # This method needs to be in this __init__.py to get the __name__ correct + # even if urllib3 is vendored within another package. + logger = logging.getLogger(__name__) + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s')) + logger.addHandler(handler) + logger.setLevel(level) + logger.debug('Added a stderr logging handler to logger: %s', __name__) + return handler + +# ... Clean up. +del NullHandler + + +# All warning filters *must* be appended unless you're really certain that they +# shouldn't be: otherwise, it's very hard for users to use most Python +# mechanisms to silence them. +# SecurityWarning's always go off by default. +warnings.simplefilter('always', exceptions.SecurityWarning, append=True) +# SubjectAltNameWarning's should go off once per host +warnings.simplefilter('default', exceptions.SubjectAltNameWarning, append=True) +# InsecurePlatformWarning's don't vary between requests, so we keep it default. +warnings.simplefilter('default', exceptions.InsecurePlatformWarning, + append=True) +# SNIMissingWarnings should go off only once. +warnings.simplefilter('default', exceptions.SNIMissingWarning, append=True) + + +def disable_warnings(category=exceptions.HTTPWarning): + """ + Helper for quickly disabling all urllib3 warnings. + """ + warnings.simplefilter('ignore', category) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/_collections.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/_collections.py new file mode 100644 index 0000000..77cee01 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/_collections.py @@ -0,0 +1,324 @@ +from __future__ import absolute_import +from collections import Mapping, MutableMapping +try: + from threading import RLock +except ImportError: # Platform-specific: No threads available + class RLock: + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_value, traceback): + pass + + +try: # Python 2.7+ + from collections import OrderedDict +except ImportError: + from .packages.ordered_dict import OrderedDict +from .packages.six import iterkeys, itervalues, PY3 + + +__all__ = ['RecentlyUsedContainer', 'HTTPHeaderDict'] + + +_Null = object() + + +class RecentlyUsedContainer(MutableMapping): + """ + Provides a thread-safe dict-like container which maintains up to + ``maxsize`` keys while throwing away the least-recently-used keys beyond + ``maxsize``. + + :param maxsize: + Maximum number of recent elements to retain. + + :param dispose_func: + Every time an item is evicted from the container, + ``dispose_func(value)`` is called. Callback which will get called + """ + + ContainerCls = OrderedDict + + def __init__(self, maxsize=10, dispose_func=None): + self._maxsize = maxsize + self.dispose_func = dispose_func + + self._container = self.ContainerCls() + self.lock = RLock() + + def __getitem__(self, key): + # Re-insert the item, moving it to the end of the eviction line. + with self.lock: + item = self._container.pop(key) + self._container[key] = item + return item + + def __setitem__(self, key, value): + evicted_value = _Null + with self.lock: + # Possibly evict the existing value of 'key' + evicted_value = self._container.get(key, _Null) + self._container[key] = value + + # If we didn't evict an existing value, we might have to evict the + # least recently used item from the beginning of the container. + if len(self._container) > self._maxsize: + _key, evicted_value = self._container.popitem(last=False) + + if self.dispose_func and evicted_value is not _Null: + self.dispose_func(evicted_value) + + def __delitem__(self, key): + with self.lock: + value = self._container.pop(key) + + if self.dispose_func: + self.dispose_func(value) + + def __len__(self): + with self.lock: + return len(self._container) + + def __iter__(self): + raise NotImplementedError('Iteration over this class is unlikely to be threadsafe.') + + def clear(self): + with self.lock: + # Copy pointers to all values, then wipe the mapping + values = list(itervalues(self._container)) + self._container.clear() + + if self.dispose_func: + for value in values: + self.dispose_func(value) + + def keys(self): + with self.lock: + return list(iterkeys(self._container)) + + +class HTTPHeaderDict(MutableMapping): + """ + :param headers: + An iterable of field-value pairs. Must not contain multiple field names + when compared case-insensitively. + + :param kwargs: + Additional field-value pairs to pass in to ``dict.update``. + + A ``dict`` like container for storing HTTP Headers. + + Field names are stored and compared case-insensitively in compliance with + RFC 7230. Iteration provides the first case-sensitive key seen for each + case-insensitive pair. + + Using ``__setitem__`` syntax overwrites fields that compare equal + case-insensitively in order to maintain ``dict``'s api. For fields that + compare equal, instead create a new ``HTTPHeaderDict`` and use ``.add`` + in a loop. + + If multiple fields that are equal case-insensitively are passed to the + constructor or ``.update``, the behavior is undefined and some will be + lost. + + >>> headers = HTTPHeaderDict() + >>> headers.add('Set-Cookie', 'foo=bar') + >>> headers.add('set-cookie', 'baz=quxx') + >>> headers['content-length'] = '7' + >>> headers['SET-cookie'] + 'foo=bar, baz=quxx' + >>> headers['Content-Length'] + '7' + """ + + def __init__(self, headers=None, **kwargs): + super(HTTPHeaderDict, self).__init__() + self._container = OrderedDict() + if headers is not None: + if isinstance(headers, HTTPHeaderDict): + self._copy_from(headers) + else: + self.extend(headers) + if kwargs: + self.extend(kwargs) + + def __setitem__(self, key, val): + self._container[key.lower()] = (key, val) + return self._container[key.lower()] + + def __getitem__(self, key): + val = self._container[key.lower()] + return ', '.join(val[1:]) + + def __delitem__(self, key): + del self._container[key.lower()] + + def __contains__(self, key): + return key.lower() in self._container + + def __eq__(self, other): + if not isinstance(other, Mapping) and not hasattr(other, 'keys'): + return False + if not isinstance(other, type(self)): + other = type(self)(other) + return (dict((k.lower(), v) for k, v in self.itermerged()) == + dict((k.lower(), v) for k, v in other.itermerged())) + + def __ne__(self, other): + return not self.__eq__(other) + + if not PY3: # Python 2 + iterkeys = MutableMapping.iterkeys + itervalues = MutableMapping.itervalues + + __marker = object() + + def __len__(self): + return len(self._container) + + def __iter__(self): + # Only provide the originally cased names + for vals in self._container.values(): + yield vals[0] + + def pop(self, key, default=__marker): + '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + ''' + # Using the MutableMapping function directly fails due to the private marker. + # Using ordinary dict.pop would expose the internal structures. + # So let's reinvent the wheel. + try: + value = self[key] + except KeyError: + if default is self.__marker: + raise + return default + else: + del self[key] + return value + + def discard(self, key): + try: + del self[key] + except KeyError: + pass + + def add(self, key, val): + """Adds a (name, value) pair, doesn't overwrite the value if it already + exists. + + >>> headers = HTTPHeaderDict(foo='bar') + >>> headers.add('Foo', 'baz') + >>> headers['foo'] + 'bar, baz' + """ + key_lower = key.lower() + new_vals = key, val + # Keep the common case aka no item present as fast as possible + vals = self._container.setdefault(key_lower, new_vals) + if new_vals is not vals: + # new_vals was not inserted, as there was a previous one + if isinstance(vals, list): + # If already several items got inserted, we have a list + vals.append(val) + else: + # vals should be a tuple then, i.e. only one item so far + # Need to convert the tuple to list for further extension + self._container[key_lower] = [vals[0], vals[1], val] + + def extend(self, *args, **kwargs): + """Generic import function for any type of header-like object. + Adapted version of MutableMapping.update in order to insert items + with self.add instead of self.__setitem__ + """ + if len(args) > 1: + raise TypeError("extend() takes at most 1 positional " + "arguments ({0} given)".format(len(args))) + other = args[0] if len(args) >= 1 else () + + if isinstance(other, HTTPHeaderDict): + for key, val in other.iteritems(): + self.add(key, val) + elif isinstance(other, Mapping): + for key in other: + self.add(key, other[key]) + elif hasattr(other, "keys"): + for key in other.keys(): + self.add(key, other[key]) + else: + for key, value in other: + self.add(key, value) + + for key, value in kwargs.items(): + self.add(key, value) + + def getlist(self, key): + """Returns a list of all the values for the named field. Returns an + empty list if the key doesn't exist.""" + try: + vals = self._container[key.lower()] + except KeyError: + return [] + else: + if isinstance(vals, tuple): + return [vals[1]] + else: + return vals[1:] + + # Backwards compatibility for httplib + getheaders = getlist + getallmatchingheaders = getlist + iget = getlist + + def __repr__(self): + return "%s(%s)" % (type(self).__name__, dict(self.itermerged())) + + def _copy_from(self, other): + for key in other: + val = other.getlist(key) + if isinstance(val, list): + # Don't need to convert tuples + val = list(val) + self._container[key.lower()] = [key] + val + + def copy(self): + clone = type(self)() + clone._copy_from(self) + return clone + + def iteritems(self): + """Iterate over all header lines, including duplicate ones.""" + for key in self: + vals = self._container[key.lower()] + for val in vals[1:]: + yield vals[0], val + + def itermerged(self): + """Iterate over all headers, merging duplicate ones together.""" + for key in self: + val = self._container[key.lower()] + yield val[0], ', '.join(val[1:]) + + def items(self): + return list(self.iteritems()) + + @classmethod + def from_httplib(cls, message): # Python 2 + """Read headers from a Python 2 httplib message object.""" + # python2.7 does not expose a proper API for exporting multiheaders + # efficiently. This function re-reads raw lines from the message + # object and extracts the multiheaders properly. + headers = [] + + for line in message.headers: + if line.startswith((' ', '\t')): + key, value = headers[-1] + headers[-1] = (key, value + '\r\n' + line.rstrip()) + continue + + key, value = line.split(':', 1) + headers.append((key, value.strip())) + + return cls(headers) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/connection.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/connection.py new file mode 100644 index 0000000..5ce0080 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/connection.py @@ -0,0 +1,330 @@ +from __future__ import absolute_import +import datetime +import logging +import os +import sys +import socket +from socket import error as SocketError, timeout as SocketTimeout +import warnings +from .packages import six + +try: # Python 3 + from http.client import HTTPConnection as _HTTPConnection + from http.client import HTTPException # noqa: unused in this module +except ImportError: + from httplib import HTTPConnection as _HTTPConnection + from httplib import HTTPException # noqa: unused in this module + +try: # Compiled with SSL? + import ssl + BaseSSLError = ssl.SSLError +except (ImportError, AttributeError): # Platform-specific: No SSL. + ssl = None + + class BaseSSLError(BaseException): + pass + + +try: # Python 3: + # Not a no-op, we're adding this to the namespace so it can be imported. + ConnectionError = ConnectionError +except NameError: # Python 2: + class ConnectionError(Exception): + pass + + +from .exceptions import ( + NewConnectionError, + ConnectTimeoutError, + SubjectAltNameWarning, + SystemTimeWarning, +) +from .packages.ssl_match_hostname import match_hostname, CertificateError + +from .util.ssl_ import ( + resolve_cert_reqs, + resolve_ssl_version, + ssl_wrap_socket, + assert_fingerprint, +) + + +from .util import connection + +from ._collections import HTTPHeaderDict + +log = logging.getLogger(__name__) + +port_by_scheme = { + 'http': 80, + 'https': 443, +} + +RECENT_DATE = datetime.date(2014, 1, 1) + + +class DummyConnection(object): + """Used to detect a failed ConnectionCls import.""" + pass + + +class HTTPConnection(_HTTPConnection, object): + """ + Based on httplib.HTTPConnection but provides an extra constructor + backwards-compatibility layer between older and newer Pythons. + + Additional keyword parameters are used to configure attributes of the connection. + Accepted parameters include: + + - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool` + - ``source_address``: Set the source address for the current connection. + + .. note:: This is ignored for Python 2.6. It is only applied for 2.7 and 3.x + + - ``socket_options``: Set specific options on the underlying socket. If not specified, then + defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling + Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. + + For example, if you wish to enable TCP Keep Alive in addition to the defaults, + you might pass:: + + HTTPConnection.default_socket_options + [ + (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), + ] + + Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). + """ + + default_port = port_by_scheme['http'] + + #: Disable Nagle's algorithm by default. + #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]`` + default_socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)] + + #: Whether this connection verifies the host's certificate. + is_verified = False + + def __init__(self, *args, **kw): + if six.PY3: # Python 3 + kw.pop('strict', None) + + # Pre-set source_address in case we have an older Python like 2.6. + self.source_address = kw.get('source_address') + + if sys.version_info < (2, 7): # Python 2.6 + # _HTTPConnection on Python 2.6 will balk at this keyword arg, but + # not newer versions. We can still use it when creating a + # connection though, so we pop it *after* we have saved it as + # self.source_address. + kw.pop('source_address', None) + + #: The socket options provided by the user. If no options are + #: provided, we use the default options. + self.socket_options = kw.pop('socket_options', self.default_socket_options) + + # Superclass also sets self.source_address in Python 2.7+. + _HTTPConnection.__init__(self, *args, **kw) + + def _new_conn(self): + """ Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + extra_kw = {} + if self.source_address: + extra_kw['source_address'] = self.source_address + + if self.socket_options: + extra_kw['socket_options'] = self.socket_options + + try: + conn = connection.create_connection( + (self.host, self.port), self.timeout, **extra_kw) + + except SocketTimeout as e: + raise ConnectTimeoutError( + self, "Connection to %s timed out. (connect timeout=%s)" % + (self.host, self.timeout)) + + except SocketError as e: + raise NewConnectionError( + self, "Failed to establish a new connection: %s" % e) + + return conn + + def _prepare_conn(self, conn): + self.sock = conn + # the _tunnel_host attribute was added in python 2.6.3 (via + # http://hg.python.org/cpython/rev/0f57b30a152f) so pythons 2.6(0-2) do + # not have them. + if getattr(self, '_tunnel_host', None): + # TODO: Fix tunnel so it doesn't depend on self.sock state. + self._tunnel() + # Mark this connection as not reusable + self.auto_open = 0 + + def connect(self): + conn = self._new_conn() + self._prepare_conn(conn) + + def request_chunked(self, method, url, body=None, headers=None): + """ + Alternative to the common request method, which sends the + body with chunked encoding and not as one block + """ + headers = HTTPHeaderDict(headers if headers is not None else {}) + skip_accept_encoding = 'accept-encoding' in headers + self.putrequest(method, url, skip_accept_encoding=skip_accept_encoding) + for header, value in headers.items(): + self.putheader(header, value) + if 'transfer-encoding' not in headers: + self.putheader('Transfer-Encoding', 'chunked') + self.endheaders() + + if body is not None: + stringish_types = six.string_types + (six.binary_type,) + if isinstance(body, stringish_types): + body = (body,) + for chunk in body: + if not chunk: + continue + if not isinstance(chunk, six.binary_type): + chunk = chunk.encode('utf8') + len_str = hex(len(chunk))[2:] + self.send(len_str.encode('utf-8')) + self.send(b'\r\n') + self.send(chunk) + self.send(b'\r\n') + + # After the if clause, to always have a closed body + self.send(b'0\r\n\r\n') + + +class HTTPSConnection(HTTPConnection): + default_port = port_by_scheme['https'] + + def __init__(self, host, port=None, key_file=None, cert_file=None, + strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, **kw): + + HTTPConnection.__init__(self, host, port, strict=strict, + timeout=timeout, **kw) + + self.key_file = key_file + self.cert_file = cert_file + + # Required property for Google AppEngine 1.9.0 which otherwise causes + # HTTPS requests to go out as HTTP. (See Issue #356) + self._protocol = 'https' + + def connect(self): + conn = self._new_conn() + self._prepare_conn(conn) + self.sock = ssl.wrap_socket(conn, self.key_file, self.cert_file) + + +class VerifiedHTTPSConnection(HTTPSConnection): + """ + Based on httplib.HTTPSConnection but wraps the socket with + SSL certification. + """ + cert_reqs = None + ca_certs = None + ca_cert_dir = None + ssl_version = None + assert_fingerprint = None + + def set_cert(self, key_file=None, cert_file=None, + cert_reqs=None, ca_certs=None, + assert_hostname=None, assert_fingerprint=None, + ca_cert_dir=None): + + if (ca_certs or ca_cert_dir) and cert_reqs is None: + cert_reqs = 'CERT_REQUIRED' + + self.key_file = key_file + self.cert_file = cert_file + self.cert_reqs = cert_reqs + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + self.ca_certs = ca_certs and os.path.expanduser(ca_certs) + self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + + def connect(self): + # Add certificate verification + conn = self._new_conn() + + resolved_cert_reqs = resolve_cert_reqs(self.cert_reqs) + resolved_ssl_version = resolve_ssl_version(self.ssl_version) + + hostname = self.host + if getattr(self, '_tunnel_host', None): + # _tunnel_host was added in Python 2.6.3 + # (See: http://hg.python.org/cpython/rev/0f57b30a152f) + + self.sock = conn + # Calls self._set_hostport(), so self.host is + # self._tunnel_host below. + self._tunnel() + # Mark this connection as not reusable + self.auto_open = 0 + + # Override the host with the one we're requesting data from. + hostname = self._tunnel_host + + is_time_off = datetime.date.today() < RECENT_DATE + if is_time_off: + warnings.warn(( + 'System time is way off (before {0}). This will probably ' + 'lead to SSL verification errors').format(RECENT_DATE), + SystemTimeWarning + ) + + # Wrap socket using verification with the root certs in + # trusted_root_certs + self.sock = ssl_wrap_socket(conn, self.key_file, self.cert_file, + cert_reqs=resolved_cert_reqs, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + server_hostname=hostname, + ssl_version=resolved_ssl_version) + + if self.assert_fingerprint: + assert_fingerprint(self.sock.getpeercert(binary_form=True), + self.assert_fingerprint) + elif resolved_cert_reqs != ssl.CERT_NONE \ + and self.assert_hostname is not False: + cert = self.sock.getpeercert() + if not cert.get('subjectAltName', ()): + warnings.warn(( + 'Certificate for {0} has no `subjectAltName`, falling back to check for a ' + '`commonName` for now. This feature is being removed by major browsers and ' + 'deprecated by RFC 2818. (See https://github.com/shazow/urllib3/issues/497 ' + 'for details.)'.format(hostname)), + SubjectAltNameWarning + ) + _match_hostname(cert, self.assert_hostname or hostname) + + self.is_verified = (resolved_cert_reqs == ssl.CERT_REQUIRED or + self.assert_fingerprint is not None) + + +def _match_hostname(cert, asserted_hostname): + try: + match_hostname(cert, asserted_hostname) + except CertificateError as e: + log.error( + 'Certificate did not match expected hostname: %s. ' + 'Certificate: %s', asserted_hostname, cert + ) + # Add cert to exception and reraise so client code can inspect + # the cert when catching the exception, if they want to + e._peer_cert = cert + raise + + +if ssl: + # Make a copy for testing. + UnverifiedHTTPSConnection = HTTPSConnection + HTTPSConnection = VerifiedHTTPSConnection +else: + HTTPSConnection = DummyConnection diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/connectionpool.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/connectionpool.py new file mode 100644 index 0000000..ab634cb --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/connectionpool.py @@ -0,0 +1,866 @@ +from __future__ import absolute_import +import errno +import logging +import sys +import warnings + +from socket import error as SocketError, timeout as SocketTimeout +import socket + +try: # Python 3 + from queue import LifoQueue, Empty, Full +except ImportError: + from Queue import LifoQueue, Empty, Full + # Queue is imported for side effects on MS Windows + import Queue as _unused_module_Queue # noqa: unused + + +from .exceptions import ( + ClosedPoolError, + ProtocolError, + EmptyPoolError, + HeaderParsingError, + HostChangedError, + LocationValueError, + MaxRetryError, + ProxyError, + ReadTimeoutError, + SSLError, + TimeoutError, + InsecureRequestWarning, + NewConnectionError, +) +from .packages.ssl_match_hostname import CertificateError +from .packages import six +from .connection import ( + port_by_scheme, + DummyConnection, + HTTPConnection, HTTPSConnection, VerifiedHTTPSConnection, + HTTPException, BaseSSLError, +) +from .request import RequestMethods +from .response import HTTPResponse + +from .util.connection import is_connection_dropped +from .util.response import assert_header_parsing +from .util.retry import Retry +from .util.timeout import Timeout +from .util.url import get_host, Url + + +xrange = six.moves.xrange + +log = logging.getLogger(__name__) + +_Default = object() + + +# Pool objects +class ConnectionPool(object): + """ + Base class for all connection pools, such as + :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`. + """ + + scheme = None + QueueCls = LifoQueue + + def __init__(self, host, port=None): + if not host: + raise LocationValueError("No host specified.") + + # httplib doesn't like it when we include brackets in ipv6 addresses + # Specifically, if we include brackets but also pass the port then + # httplib crazily doubles up the square brackets on the Host header. + # Instead, we need to make sure we never pass ``None`` as the port. + # However, for backward compatibility reasons we can't actually + # *assert* that. + self.host = host.strip('[]') + self.port = port + + def __str__(self): + return '%s(host=%r, port=%r)' % (type(self).__name__, + self.host, self.port) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + # Return False to re-raise any potential exceptions + return False + + def close(self): + """ + Close all pooled connections and disable the pool. + """ + pass + + +# This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252 +_blocking_errnos = set([errno.EAGAIN, errno.EWOULDBLOCK]) + + +class HTTPConnectionPool(ConnectionPool, RequestMethods): + """ + Thread-safe connection pool for one host. + + :param host: + Host used for this HTTP Connection (e.g. "localhost"), passed into + :class:`httplib.HTTPConnection`. + + :param port: + Port used for this HTTP Connection (None is equivalent to 80), passed + into :class:`httplib.HTTPConnection`. + + :param strict: + Causes BadStatusLine to be raised if the status line can't be parsed + as a valid HTTP/1.0 or 1.1 status line, passed into + :class:`httplib.HTTPConnection`. + + .. note:: + Only works in Python 2. This parameter is ignored in Python 3. + + :param timeout: + Socket timeout in seconds for each individual connection. This can + be a float or integer, which sets the timeout for the HTTP request, + or an instance of :class:`urllib3.util.Timeout` which gives you more + fine-grained control over request timeouts. After the constructor has + been parsed, this is always a `urllib3.util.Timeout` object. + + :param maxsize: + Number of connections to save that can be reused. More than 1 is useful + in multithreaded situations. If ``block`` is set to False, more + connections will be created but they will not be saved once they've + been used. + + :param block: + If set to True, no more than ``maxsize`` connections will be used at + a time. When no free connections are available, the call will block + until a connection has been released. This is a useful side effect for + particular multithreaded situations where one does not want to use more + than maxsize connections per host to prevent flooding. + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + + :param retries: + Retry configuration to use by default with requests in this pool. + + :param _proxy: + Parsed proxy URL, should not be used directly, instead, see + :class:`urllib3.connectionpool.ProxyManager`" + + :param _proxy_headers: + A dictionary with proxy headers, should not be used directly, + instead, see :class:`urllib3.connectionpool.ProxyManager`" + + :param \**conn_kw: + Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`, + :class:`urllib3.connection.HTTPSConnection` instances. + """ + + scheme = 'http' + ConnectionCls = HTTPConnection + ResponseCls = HTTPResponse + + def __init__(self, host, port=None, strict=False, + timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, block=False, + headers=None, retries=None, + _proxy=None, _proxy_headers=None, + **conn_kw): + ConnectionPool.__init__(self, host, port) + RequestMethods.__init__(self, headers) + + self.strict = strict + + if not isinstance(timeout, Timeout): + timeout = Timeout.from_float(timeout) + + if retries is None: + retries = Retry.DEFAULT + + self.timeout = timeout + self.retries = retries + + self.pool = self.QueueCls(maxsize) + self.block = block + + self.proxy = _proxy + self.proxy_headers = _proxy_headers or {} + + # Fill the queue up so that doing get() on it will block properly + for _ in xrange(maxsize): + self.pool.put(None) + + # These are mostly for testing and debugging purposes. + self.num_connections = 0 + self.num_requests = 0 + self.conn_kw = conn_kw + + if self.proxy: + # Enable Nagle's algorithm for proxies, to avoid packet fragmentation. + # We cannot know if the user has added default socket options, so we cannot replace the + # list. + self.conn_kw.setdefault('socket_options', []) + + def _new_conn(self): + """ + Return a fresh :class:`HTTPConnection`. + """ + self.num_connections += 1 + log.info("Starting new HTTP connection (%d): %s", + self.num_connections, self.host) + + conn = self.ConnectionCls(host=self.host, port=self.port, + timeout=self.timeout.connect_timeout, + strict=self.strict, **self.conn_kw) + return conn + + def _get_conn(self, timeout=None): + """ + Get a connection. Will return a pooled connection if one is available. + + If no connections are available and :prop:`.block` is ``False``, then a + fresh connection is returned. + + :param timeout: + Seconds to wait before giving up and raising + :class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and + :prop:`.block` is ``True``. + """ + conn = None + try: + conn = self.pool.get(block=self.block, timeout=timeout) + + except AttributeError: # self.pool is None + raise ClosedPoolError(self, "Pool is closed.") + + except Empty: + if self.block: + raise EmptyPoolError(self, + "Pool reached maximum size and no more " + "connections are allowed.") + pass # Oh well, we'll create a new connection then + + # If this is a persistent connection, check if it got disconnected + if conn and is_connection_dropped(conn): + log.info("Resetting dropped connection: %s", self.host) + conn.close() + if getattr(conn, 'auto_open', 1) == 0: + # This is a proxied connection that has been mutated by + # httplib._tunnel() and cannot be reused (since it would + # attempt to bypass the proxy) + conn = None + + return conn or self._new_conn() + + def _put_conn(self, conn): + """ + Put a connection back into the pool. + + :param conn: + Connection object for the current host and port as returned by + :meth:`._new_conn` or :meth:`._get_conn`. + + If the pool is already full, the connection is closed and discarded + because we exceeded maxsize. If connections are discarded frequently, + then maxsize should be increased. + + If the pool is closed, then the connection will be closed and discarded. + """ + try: + self.pool.put(conn, block=False) + return # Everything is dandy, done. + except AttributeError: + # self.pool is None. + pass + except Full: + # This should never happen if self.block == True + log.warning( + "Connection pool is full, discarding connection: %s", + self.host) + + # Connection never got put back into the pool, close it. + if conn: + conn.close() + + def _validate_conn(self, conn): + """ + Called right before a request is made, after the socket is created. + """ + pass + + def _prepare_proxy(self, conn): + # Nothing to do for HTTP connections. + pass + + def _get_timeout(self, timeout): + """ Helper that always returns a :class:`urllib3.util.Timeout` """ + if timeout is _Default: + return self.timeout.clone() + + if isinstance(timeout, Timeout): + return timeout.clone() + else: + # User passed us an int/float. This is for backwards compatibility, + # can be removed later + return Timeout.from_float(timeout) + + def _raise_timeout(self, err, url, timeout_value): + """Is the error actually a timeout? Will raise a ReadTimeout or pass""" + + if isinstance(err, SocketTimeout): + raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + + # See the above comment about EAGAIN in Python 3. In Python 2 we have + # to specifically catch it and throw the timeout error + if hasattr(err, 'errno') and err.errno in _blocking_errnos: + raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + + # Catch possible read timeouts thrown as SSL errors. If not the + # case, rethrow the original. We need to do this because of: + # http://bugs.python.org/issue10272 + if 'timed out' in str(err) or 'did not complete (read)' in str(err): # Python 2.6 + raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + + def _make_request(self, conn, method, url, timeout=_Default, chunked=False, + **httplib_request_kw): + """ + Perform a request on a given urllib connection object taken from our + pool. + + :param conn: + a connection from one of our connection pools + + :param timeout: + Socket timeout in seconds for the request. This can be a + float or integer, which will set the same timeout value for + the socket connect and the socket read, or an instance of + :class:`urllib3.util.Timeout`, which gives you more fine-grained + control over your timeouts. + """ + self.num_requests += 1 + + timeout_obj = self._get_timeout(timeout) + timeout_obj.start_connect() + conn.timeout = timeout_obj.connect_timeout + + # Trigger any extra validation we need to do. + try: + self._validate_conn(conn) + except (SocketTimeout, BaseSSLError) as e: + # Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout. + self._raise_timeout(err=e, url=url, timeout_value=conn.timeout) + raise + + # conn.request() calls httplib.*.request, not the method in + # urllib3.request. It also calls makefile (recv) on the socket. + if chunked: + conn.request_chunked(method, url, **httplib_request_kw) + else: + conn.request(method, url, **httplib_request_kw) + + # Reset the timeout for the recv() on the socket + read_timeout = timeout_obj.read_timeout + + # App Engine doesn't have a sock attr + if getattr(conn, 'sock', None): + # In Python 3 socket.py will catch EAGAIN and return None when you + # try and read into the file pointer created by http.client, which + # instead raises a BadStatusLine exception. Instead of catching + # the exception and assuming all BadStatusLine exceptions are read + # timeouts, check for a zero timeout before making the request. + if read_timeout == 0: + raise ReadTimeoutError( + self, url, "Read timed out. (read timeout=%s)" % read_timeout) + if read_timeout is Timeout.DEFAULT_TIMEOUT: + conn.sock.settimeout(socket.getdefaulttimeout()) + else: # None or a value + conn.sock.settimeout(read_timeout) + + # Receive the response from the server + try: + try: # Python 2.7, use buffering of HTTP responses + httplib_response = conn.getresponse(buffering=True) + except TypeError: # Python 2.6 and older, Python 3 + try: + httplib_response = conn.getresponse() + except Exception as e: + # Remove the TypeError from the exception chain in Python 3; + # otherwise it looks like a programming error was the cause. + six.raise_from(e, None) + except (SocketTimeout, BaseSSLError, SocketError) as e: + self._raise_timeout(err=e, url=url, timeout_value=read_timeout) + raise + + # AppEngine doesn't have a version attr. + http_version = getattr(conn, '_http_vsn_str', 'HTTP/?') + log.debug("\"%s %s %s\" %s %s", method, url, http_version, + httplib_response.status, httplib_response.length) + + try: + assert_header_parsing(httplib_response.msg) + except HeaderParsingError as hpe: # Platform-specific: Python 3 + log.warning( + 'Failed to parse headers (url=%s): %s', + self._absolute_url(url), hpe, exc_info=True) + + return httplib_response + + def _absolute_url(self, path): + return Url(scheme=self.scheme, host=self.host, port=self.port, path=path).url + + def close(self): + """ + Close all pooled connections and disable the pool. + """ + # Disable access to the pool + old_pool, self.pool = self.pool, None + + try: + while True: + conn = old_pool.get(block=False) + if conn: + conn.close() + + except Empty: + pass # Done. + + def is_same_host(self, url): + """ + Check if the given ``url`` is a member of the same host as this + connection pool. + """ + if url.startswith('/'): + return True + + # TODO: Add optional support for socket.gethostbyname checking. + scheme, host, port = get_host(url) + + # Use explicit default port for comparison when none is given + if self.port and not port: + port = port_by_scheme.get(scheme) + elif not self.port and port == port_by_scheme.get(scheme): + port = None + + return (scheme, host, port) == (self.scheme, self.host, self.port) + + def urlopen(self, method, url, body=None, headers=None, retries=None, + redirect=True, assert_same_host=True, timeout=_Default, + pool_timeout=None, release_conn=None, chunked=False, + **response_kw): + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method provided + by :class:`.RequestMethods`, such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param body: + Data to send in the request body (useful for creating + POST requests, see HTTPConnectionPool.post_url for + more convenience). + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + Pass ``None`` to retry until you receive a response. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When False, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of + ``response_kw.get('preload_content', True)``. + + :param chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param \**response_kw: + Additional parameters are passed to + :meth:`urllib3.response.HTTPResponse.from_httplib` + """ + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = response_kw.get('preload_content', True) + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] <https://github.com/shazow/urllib3/issues/651> + release_this_conn = release_conn + + # Merge the proxy headers. Only do this in HTTP. We have to copy the + # headers dict so we can safely change it without those changes being + # reflected in anyone else's copy. + if self.scheme == 'http': + headers = headers.copy() + headers.update(self.proxy_headers) + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout + + is_new_proxy_conn = self.proxy is not None and not getattr(conn, 'sock', None) + if is_new_proxy_conn: + self._prepare_proxy(conn) + + # Make the request on the httplib connection object. + httplib_response = self._make_request(conn, method, url, + timeout=timeout_obj, + body=body, headers=headers, + chunked=chunked) + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Import httplib's response into our own wrapper object + response = self.ResponseCls.from_httplib(httplib_response, + pool=self, + connection=response_conn, + **response_kw) + + # Everything went great! + clean_exit = True + + except Empty: + # Timed out by queue. + raise EmptyPoolError(self, "No pool connections are available.") + + except (BaseSSLError, CertificateError) as e: + # Close the connection. If a connection is reused on which there + # was a Certificate error, the next request will certainly raise + # another Certificate error. + clean_exit = False + raise SSLError(e) + + except SSLError: + # Treat SSLError separately from BaseSSLError to preserve + # traceback. + clean_exit = False + raise + + except (TimeoutError, HTTPException, SocketError, ProtocolError) as e: + # Discard the connection for these exceptions. It will be + # be replaced during the next _get_conn() call. + clean_exit = False + + if isinstance(e, (SocketError, NewConnectionError)) and self.proxy: + e = ProxyError('Cannot connect to proxy.', e) + elif isinstance(e, (SocketError, HTTPException)): + e = ProtocolError('Connection aborted.', e) + + retries = retries.increment(method, url, error=e, _pool=self, + _stacktrace=sys.exc_info()[2]) + retries.sleep() + + # Keep track of the error for the retry warning. + err = e + + finally: + if not clean_exit: + # We hit some kind of exception, handled or otherwise. We need + # to throw the connection away unless explicitly told not to. + # Close the connection, set the variable to None, and make sure + # we put the None back in the pool to avoid leaking it. + conn = conn and conn.close() + release_this_conn = True + + if release_this_conn: + # Put the connection back to be reused. If the connection is + # expired then it will be None, which will get replaced with a + # fresh connection during _get_conn. + self._put_conn(conn) + + if not conn: + # Try again + log.warning("Retrying (%r) after connection " + "broken by '%r': %s", retries, err, url) + return self.urlopen(method, url, body, headers, retries, + redirect, assert_same_host, + timeout=timeout, pool_timeout=pool_timeout, + release_conn=release_conn, **response_kw) + + # Handle redirect? + redirect_location = redirect and response.get_redirect_location() + if redirect_location: + if response.status == 303: + method = 'GET' + + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_redirect: + # Release the connection for this response, since we're not + # returning it to be released manually. + response.release_conn() + raise + return response + + log.info("Redirecting %s -> %s", url, redirect_location) + return self.urlopen( + method, redirect_location, body, headers, + retries=retries, redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, pool_timeout=pool_timeout, + release_conn=release_conn, **response_kw) + + # Check if we should retry the HTTP response. + if retries.is_forced_retry(method, status_code=response.status): + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_status: + # Release the connection for this response, since we're not + # returning it to be released manually. + response.release_conn() + raise + return response + retries.sleep() + log.info("Forced retry: %s", url) + return self.urlopen( + method, url, body, headers, + retries=retries, redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, pool_timeout=pool_timeout, + release_conn=release_conn, **response_kw) + + return response + + +class HTTPSConnectionPool(HTTPConnectionPool): + """ + Same as :class:`.HTTPConnectionPool`, but HTTPS. + + When Python is compiled with the :mod:`ssl` module, then + :class:`.VerifiedHTTPSConnection` is used, which *can* verify certificates, + instead of :class:`.HTTPSConnection`. + + :class:`.VerifiedHTTPSConnection` uses one of ``assert_fingerprint``, + ``assert_hostname`` and ``host`` in this order to verify connections. + If ``assert_hostname`` is False, no verification is done. + + The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``, + ``ca_cert_dir``, and ``ssl_version`` are only used if :mod:`ssl` is + available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade + the connection socket into an SSL socket. + """ + + scheme = 'https' + ConnectionCls = HTTPSConnection + + def __init__(self, host, port=None, + strict=False, timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, + block=False, headers=None, retries=None, + _proxy=None, _proxy_headers=None, + key_file=None, cert_file=None, cert_reqs=None, + ca_certs=None, ssl_version=None, + assert_hostname=None, assert_fingerprint=None, + ca_cert_dir=None, **conn_kw): + + HTTPConnectionPool.__init__(self, host, port, strict, timeout, maxsize, + block, headers, retries, _proxy, _proxy_headers, + **conn_kw) + + if ca_certs and cert_reqs is None: + cert_reqs = 'CERT_REQUIRED' + + self.key_file = key_file + self.cert_file = cert_file + self.cert_reqs = cert_reqs + self.ca_certs = ca_certs + self.ca_cert_dir = ca_cert_dir + self.ssl_version = ssl_version + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + + def _prepare_conn(self, conn): + """ + Prepare the ``connection`` for :meth:`urllib3.util.ssl_wrap_socket` + and establish the tunnel if proxy is used. + """ + + if isinstance(conn, VerifiedHTTPSConnection): + conn.set_cert(key_file=self.key_file, + cert_file=self.cert_file, + cert_reqs=self.cert_reqs, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + assert_hostname=self.assert_hostname, + assert_fingerprint=self.assert_fingerprint) + conn.ssl_version = self.ssl_version + + return conn + + def _prepare_proxy(self, conn): + """ + Establish tunnel connection early, because otherwise httplib + would improperly set Host: header to proxy's IP:port. + """ + # Python 2.7+ + try: + set_tunnel = conn.set_tunnel + except AttributeError: # Platform-specific: Python 2.6 + set_tunnel = conn._set_tunnel + + if sys.version_info <= (2, 6, 4) and not self.proxy_headers: # Python 2.6.4 and older + set_tunnel(self.host, self.port) + else: + set_tunnel(self.host, self.port, self.proxy_headers) + + conn.connect() + + def _new_conn(self): + """ + Return a fresh :class:`httplib.HTTPSConnection`. + """ + self.num_connections += 1 + log.info("Starting new HTTPS connection (%d): %s", + self.num_connections, self.host) + + if not self.ConnectionCls or self.ConnectionCls is DummyConnection: + raise SSLError("Can't connect to HTTPS URL because the SSL " + "module is not available.") + + actual_host = self.host + actual_port = self.port + if self.proxy is not None: + actual_host = self.proxy.host + actual_port = self.proxy.port + + conn = self.ConnectionCls(host=actual_host, port=actual_port, + timeout=self.timeout.connect_timeout, + strict=self.strict, **self.conn_kw) + + return self._prepare_conn(conn) + + def _validate_conn(self, conn): + """ + Called right before a request is made, after the socket is created. + """ + super(HTTPSConnectionPool, self)._validate_conn(conn) + + # Force connect early to allow us to validate the connection. + if not getattr(conn, 'sock', None): # AppEngine might not have `.sock` + conn.connect() + + if not conn.is_verified: + warnings.warn(( + 'Unverified HTTPS request is being made. ' + 'Adding certificate verification is strongly advised. See: ' + 'https://urllib3.readthedocs.io/en/latest/security.html'), + InsecureRequestWarning) + + +def connection_from_url(url, **kw): + """ + Given a url, return an :class:`.ConnectionPool` instance of its host. + + This is a shortcut for not having to parse out the scheme, host, and port + of the url before creating an :class:`.ConnectionPool` instance. + + :param url: + Absolute URL string that must include the scheme. Port is optional. + + :param \**kw: + Passes additional parameters to the constructor of the appropriate + :class:`.ConnectionPool`. Useful for specifying things like + timeout, maxsize, headers, etc. + + Example:: + + >>> conn = connection_from_url('http://google.com/') + >>> r = conn.request('GET', '/') + """ + scheme, host, port = get_host(url) + port = port or port_by_scheme.get(scheme, 80) + if scheme == 'https': + return HTTPSConnectionPool(host, port=port, **kw) + else: + return HTTPConnectionPool(host, port=port, **kw) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/appengine.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/appengine.py new file mode 100644 index 0000000..1579476 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/appengine.py @@ -0,0 +1,231 @@ +from __future__ import absolute_import +import logging +import os +import warnings + +from ..exceptions import ( + HTTPError, + HTTPWarning, + MaxRetryError, + ProtocolError, + TimeoutError, + SSLError +) + +from ..packages.six import BytesIO +from ..request import RequestMethods +from ..response import HTTPResponse +from ..util.timeout import Timeout +from ..util.retry import Retry + +try: + from google.appengine.api import urlfetch +except ImportError: + urlfetch = None + + +log = logging.getLogger(__name__) + + +class AppEnginePlatformWarning(HTTPWarning): + pass + + +class AppEnginePlatformError(HTTPError): + pass + + +class AppEngineManager(RequestMethods): + """ + Connection manager for Google App Engine sandbox applications. + + This manager uses the URLFetch service directly instead of using the + emulated httplib, and is subject to URLFetch limitations as described in + the App Engine documentation here: + + https://cloud.google.com/appengine/docs/python/urlfetch + + Notably it will raise an AppEnginePlatformError if: + * URLFetch is not available. + * If you attempt to use this on GAEv2 (Managed VMs), as full socket + support is available. + * If a request size is more than 10 megabytes. + * If a response size is more than 32 megabtyes. + * If you use an unsupported request method such as OPTIONS. + + Beyond those cases, it will raise normal urllib3 errors. + """ + + def __init__(self, headers=None, retries=None, validate_certificate=True): + if not urlfetch: + raise AppEnginePlatformError( + "URLFetch is not available in this environment.") + + if is_prod_appengine_mvms(): + raise AppEnginePlatformError( + "Use normal urllib3.PoolManager instead of AppEngineManager" + "on Managed VMs, as using URLFetch is not necessary in " + "this environment.") + + warnings.warn( + "urllib3 is using URLFetch on Google App Engine sandbox instead " + "of sockets. To use sockets directly instead of URLFetch see " + "https://urllib3.readthedocs.io/en/latest/contrib.html.", + AppEnginePlatformWarning) + + RequestMethods.__init__(self, headers) + self.validate_certificate = validate_certificate + + self.retries = retries or Retry.DEFAULT + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + # Return False to re-raise any potential exceptions + return False + + def urlopen(self, method, url, body=None, headers=None, + retries=None, redirect=True, timeout=Timeout.DEFAULT_TIMEOUT, + **response_kw): + + retries = self._get_retries(retries, redirect) + + try: + response = urlfetch.fetch( + url, + payload=body, + method=method, + headers=headers or {}, + allow_truncated=False, + follow_redirects=( + redirect and + retries.redirect != 0 and + retries.total), + deadline=self._get_absolute_timeout(timeout), + validate_certificate=self.validate_certificate, + ) + except urlfetch.DeadlineExceededError as e: + raise TimeoutError(self, e) + + except urlfetch.InvalidURLError as e: + if 'too large' in str(e): + raise AppEnginePlatformError( + "URLFetch request too large, URLFetch only " + "supports requests up to 10mb in size.", e) + raise ProtocolError(e) + + except urlfetch.DownloadError as e: + if 'Too many redirects' in str(e): + raise MaxRetryError(self, url, reason=e) + raise ProtocolError(e) + + except urlfetch.ResponseTooLargeError as e: + raise AppEnginePlatformError( + "URLFetch response too large, URLFetch only supports" + "responses up to 32mb in size.", e) + + except urlfetch.SSLCertificateError as e: + raise SSLError(e) + + except urlfetch.InvalidMethodError as e: + raise AppEnginePlatformError( + "URLFetch does not support method: %s" % method, e) + + http_response = self._urlfetch_response_to_http_response( + response, **response_kw) + + # Check for redirect response + if (http_response.get_redirect_location() and + retries.raise_on_redirect and redirect): + raise MaxRetryError(self, url, "too many redirects") + + # Check if we should retry the HTTP response. + if retries.is_forced_retry(method, status_code=http_response.status): + retries = retries.increment( + method, url, response=http_response, _pool=self) + log.info("Forced retry: %s", url) + retries.sleep() + return self.urlopen( + method, url, + body=body, headers=headers, + retries=retries, redirect=redirect, + timeout=timeout, **response_kw) + + return http_response + + def _urlfetch_response_to_http_response(self, urlfetch_resp, **response_kw): + + if is_prod_appengine(): + # Production GAE handles deflate encoding automatically, but does + # not remove the encoding header. + content_encoding = urlfetch_resp.headers.get('content-encoding') + + if content_encoding == 'deflate': + del urlfetch_resp.headers['content-encoding'] + + transfer_encoding = urlfetch_resp.headers.get('transfer-encoding') + # We have a full response's content, + # so let's make sure we don't report ourselves as chunked data. + if transfer_encoding == 'chunked': + encodings = transfer_encoding.split(",") + encodings.remove('chunked') + urlfetch_resp.headers['transfer-encoding'] = ','.join(encodings) + + return HTTPResponse( + # In order for decoding to work, we must present the content as + # a file-like object. + body=BytesIO(urlfetch_resp.content), + headers=urlfetch_resp.headers, + status=urlfetch_resp.status_code, + **response_kw + ) + + def _get_absolute_timeout(self, timeout): + if timeout is Timeout.DEFAULT_TIMEOUT: + return 5 # 5s is the default timeout for URLFetch. + if isinstance(timeout, Timeout): + if timeout._read is not timeout._connect: + warnings.warn( + "URLFetch does not support granular timeout settings, " + "reverting to total timeout.", AppEnginePlatformWarning) + return timeout.total + return timeout + + def _get_retries(self, retries, redirect): + if not isinstance(retries, Retry): + retries = Retry.from_int( + retries, redirect=redirect, default=self.retries) + + if retries.connect or retries.read or retries.redirect: + warnings.warn( + "URLFetch only supports total retries and does not " + "recognize connect, read, or redirect retry parameters.", + AppEnginePlatformWarning) + + return retries + + +def is_appengine(): + return (is_local_appengine() or + is_prod_appengine() or + is_prod_appengine_mvms()) + + +def is_appengine_sandbox(): + return is_appengine() and not is_prod_appengine_mvms() + + +def is_local_appengine(): + return ('APPENGINE_RUNTIME' in os.environ and + 'Development/' in os.environ['SERVER_SOFTWARE']) + + +def is_prod_appengine(): + return ('APPENGINE_RUNTIME' in os.environ and + 'Google App Engine/' in os.environ['SERVER_SOFTWARE'] and + not is_prod_appengine_mvms()) + + +def is_prod_appengine_mvms(): + return os.environ.get('GAE_VM', False) == 'true' diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.py new file mode 100644 index 0000000..11d0b5c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.py @@ -0,0 +1,115 @@ +""" +NTLM authenticating pool, contributed by erikcederstran + +Issue #10, see: http://code.google.com/p/urllib3/issues/detail?id=10 +""" +from __future__ import absolute_import + +try: + from http.client import HTTPSConnection +except ImportError: + from httplib import HTTPSConnection +from logging import getLogger +from ntlm import ntlm + +from urllib3 import HTTPSConnectionPool + + +log = getLogger(__name__) + + +class NTLMConnectionPool(HTTPSConnectionPool): + """ + Implements an NTLM authentication version of an urllib3 connection pool + """ + + scheme = 'https' + + def __init__(self, user, pw, authurl, *args, **kwargs): + """ + authurl is a random URL on the server that is protected by NTLM. + user is the Windows user, probably in the DOMAIN\\username format. + pw is the password for the user. + """ + super(NTLMConnectionPool, self).__init__(*args, **kwargs) + self.authurl = authurl + self.rawuser = user + user_parts = user.split('\\', 1) + self.domain = user_parts[0].upper() + self.user = user_parts[1] + self.pw = pw + + def _new_conn(self): + # Performs the NTLM handshake that secures the connection. The socket + # must be kept open while requests are performed. + self.num_connections += 1 + log.debug('Starting NTLM HTTPS connection no. %d: https://%s%s', + self.num_connections, self.host, self.authurl) + + headers = {} + headers['Connection'] = 'Keep-Alive' + req_header = 'Authorization' + resp_header = 'www-authenticate' + + conn = HTTPSConnection(host=self.host, port=self.port) + + # Send negotiation message + headers[req_header] = ( + 'NTLM %s' % ntlm.create_NTLM_NEGOTIATE_MESSAGE(self.rawuser)) + log.debug('Request headers: %s', headers) + conn.request('GET', self.authurl, None, headers) + res = conn.getresponse() + reshdr = dict(res.getheaders()) + log.debug('Response status: %s %s', res.status, res.reason) + log.debug('Response headers: %s', reshdr) + log.debug('Response data: %s [...]', res.read(100)) + + # Remove the reference to the socket, so that it can not be closed by + # the response object (we want to keep the socket open) + res.fp = None + + # Server should respond with a challenge message + auth_header_values = reshdr[resp_header].split(', ') + auth_header_value = None + for s in auth_header_values: + if s[:5] == 'NTLM ': + auth_header_value = s[5:] + if auth_header_value is None: + raise Exception('Unexpected %s response header: %s' % + (resp_header, reshdr[resp_header])) + + # Send authentication message + ServerChallenge, NegotiateFlags = \ + ntlm.parse_NTLM_CHALLENGE_MESSAGE(auth_header_value) + auth_msg = ntlm.create_NTLM_AUTHENTICATE_MESSAGE(ServerChallenge, + self.user, + self.domain, + self.pw, + NegotiateFlags) + headers[req_header] = 'NTLM %s' % auth_msg + log.debug('Request headers: %s', headers) + conn.request('GET', self.authurl, None, headers) + res = conn.getresponse() + log.debug('Response status: %s %s', res.status, res.reason) + log.debug('Response headers: %s', dict(res.getheaders())) + log.debug('Response data: %s [...]', res.read()[:100]) + if res.status != 200: + if res.status == 401: + raise Exception('Server rejected request: wrong ' + 'username or password') + raise Exception('Wrong server response: %s %s' % + (res.status, res.reason)) + + res.fp = None + log.debug('Connection established') + return conn + + def urlopen(self, method, url, body=None, headers=None, retries=3, + redirect=True, assert_same_host=True): + if headers is None: + headers = {} + headers['Connection'] = 'Keep-Alive' + return super(NTLMConnectionPool, self).urlopen(method, url, body, + headers, retries, + redirect, + assert_same_host) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py new file mode 100644 index 0000000..ed3b9cc --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py @@ -0,0 +1,358 @@ +'''SSL with SNI_-support for Python 2. Follow these instructions if you would +like to verify SSL certificates in Python 2. Note, the default libraries do +*not* do certificate checking; you need to do additional work to validate +certificates yourself. + +This needs the following packages installed: + +* pyOpenSSL (tested with 0.13) +* ndg-httpsclient (tested with 0.3.2) +* pyasn1 (tested with 0.1.6) + +You can install them with the following command: + + pip install pyopenssl ndg-httpsclient pyasn1 + +To activate certificate checking, call +:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code +before you begin making HTTP requests. This can be done in a ``sitecustomize`` +module, or at any other time before your application begins using ``urllib3``, +like this:: + + try: + import urllib3.contrib.pyopenssl + urllib3.contrib.pyopenssl.inject_into_urllib3() + except ImportError: + pass + +Now you can use :mod:`urllib3` as you normally would, and it will support SNI +when the required modules are installed. + +Activating this module also has the positive side effect of disabling SSL/TLS +compression in Python 2 (see `CRIME attack`_). + +If you want to configure the default list of supported cipher suites, you can +set the ``urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST`` variable. + +Module Variables +---------------- + +:var DEFAULT_SSL_CIPHER_LIST: The list of supported SSL/TLS cipher suites. + +.. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication +.. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit) + +''' +from __future__ import absolute_import + +try: + from ndg.httpsclient.ssl_peer_verification import SUBJ_ALT_NAME_SUPPORT + from ndg.httpsclient.subj_alt_name import SubjectAltName as BaseSubjectAltName +except SyntaxError as e: + raise ImportError(e) + +import OpenSSL.SSL +from pyasn1.codec.der import decoder as der_decoder +from pyasn1.type import univ, constraint +from socket import timeout, error as SocketError + +try: # Platform-specific: Python 2 + from socket import _fileobject +except ImportError: # Platform-specific: Python 3 + _fileobject = None + from urllib3.packages.backports.makefile import backport_makefile + +import ssl +import select +import six + +from .. import connection +from .. import util + +__all__ = ['inject_into_urllib3', 'extract_from_urllib3'] + +# SNI only *really* works if we can read the subjectAltName of certificates. +HAS_SNI = SUBJ_ALT_NAME_SUPPORT + +# Map from urllib3 to PyOpenSSL compatible parameter-values. +_openssl_versions = { + ssl.PROTOCOL_SSLv23: OpenSSL.SSL.SSLv23_METHOD, + ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, +} + +if hasattr(ssl, 'PROTOCOL_TLSv1_1') and hasattr(OpenSSL.SSL, 'TLSv1_1_METHOD'): + _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD + +if hasattr(ssl, 'PROTOCOL_TLSv1_2') and hasattr(OpenSSL.SSL, 'TLSv1_2_METHOD'): + _openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD + +try: + _openssl_versions.update({ssl.PROTOCOL_SSLv3: OpenSSL.SSL.SSLv3_METHOD}) +except AttributeError: + pass + +_openssl_verify = { + ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE, + ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER, + ssl.CERT_REQUIRED: + OpenSSL.SSL.VERIFY_PEER + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, +} + +DEFAULT_SSL_CIPHER_LIST = util.ssl_.DEFAULT_CIPHERS.encode('ascii') + +# OpenSSL will only write 16K at a time +SSL_WRITE_BLOCKSIZE = 16384 + +orig_util_HAS_SNI = util.HAS_SNI +orig_connection_ssl_wrap_socket = connection.ssl_wrap_socket + + +def inject_into_urllib3(): + 'Monkey-patch urllib3 with PyOpenSSL-backed SSL-support.' + + connection.ssl_wrap_socket = ssl_wrap_socket + util.HAS_SNI = HAS_SNI + util.IS_PYOPENSSL = True + + +def extract_from_urllib3(): + 'Undo monkey-patching by :func:`inject_into_urllib3`.' + + connection.ssl_wrap_socket = orig_connection_ssl_wrap_socket + util.HAS_SNI = orig_util_HAS_SNI + util.IS_PYOPENSSL = False + + +# Note: This is a slightly bug-fixed version of same from ndg-httpsclient. +class SubjectAltName(BaseSubjectAltName): + '''ASN.1 implementation for subjectAltNames support''' + + # There is no limit to how many SAN certificates a certificate may have, + # however this needs to have some limit so we'll set an arbitrarily high + # limit. + sizeSpec = univ.SequenceOf.sizeSpec + \ + constraint.ValueSizeConstraint(1, 1024) + + +# Note: This is a slightly bug-fixed version of same from ndg-httpsclient. +def get_subj_alt_name(peer_cert): + # Search through extensions + dns_name = [] + if not SUBJ_ALT_NAME_SUPPORT: + return dns_name + + general_names = SubjectAltName() + for i in range(peer_cert.get_extension_count()): + ext = peer_cert.get_extension(i) + ext_name = ext.get_short_name() + if ext_name != b'subjectAltName': + continue + + # PyOpenSSL returns extension data in ASN.1 encoded form + ext_dat = ext.get_data() + decoded_dat = der_decoder.decode(ext_dat, + asn1Spec=general_names) + + for name in decoded_dat: + if not isinstance(name, SubjectAltName): + continue + for entry in range(len(name)): + component = name.getComponentByPosition(entry) + if component.getName() != 'dNSName': + continue + dns_name.append(str(component.getComponent())) + + return dns_name + + +class WrappedSocket(object): + '''API-compatibility wrapper for Python OpenSSL's Connection-class. + + Note: _makefile_refs, _drop() and _reuse() are needed for the garbage + collector of pypy. + ''' + + def __init__(self, connection, socket, suppress_ragged_eofs=True): + self.connection = connection + self.socket = socket + self.suppress_ragged_eofs = suppress_ragged_eofs + self._makefile_refs = 0 + self._closed = False + + def fileno(self): + return self.socket.fileno() + + # Copy-pasted from Python 3.5 source code + def _decref_socketios(self): + if self._makefile_refs > 0: + self._makefile_refs -= 1 + if self._closed: + self.close() + + def recv(self, *args, **kwargs): + try: + data = self.connection.recv(*args, **kwargs) + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): + return b'' + else: + raise SocketError(str(e)) + except OpenSSL.SSL.ZeroReturnError as e: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return b'' + else: + raise + except OpenSSL.SSL.WantReadError: + rd, wd, ed = select.select( + [self.socket], [], [], self.socket.gettimeout()) + if not rd: + raise timeout('The read operation timed out') + else: + return self.recv(*args, **kwargs) + else: + return data + + def recv_into(self, *args, **kwargs): + try: + return self.connection.recv_into(*args, **kwargs) + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): + return 0 + else: + raise SocketError(str(e)) + except OpenSSL.SSL.ZeroReturnError as e: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return 0 + else: + raise + except OpenSSL.SSL.WantReadError: + rd, wd, ed = select.select( + [self.socket], [], [], self.socket.gettimeout()) + if not rd: + raise timeout('The read operation timed out') + else: + return self.recv_into(*args, **kwargs) + + def settimeout(self, timeout): + return self.socket.settimeout(timeout) + + def _send_until_done(self, data): + while True: + try: + return self.connection.send(data) + except OpenSSL.SSL.WantWriteError: + _, wlist, _ = select.select([], [self.socket], [], + self.socket.gettimeout()) + if not wlist: + raise timeout() + continue + + def sendall(self, data): + total_sent = 0 + while total_sent < len(data): + sent = self._send_until_done(data[total_sent:total_sent + SSL_WRITE_BLOCKSIZE]) + total_sent += sent + + def shutdown(self): + # FIXME rethrow compatible exceptions should we ever use this + self.connection.shutdown() + + def close(self): + if self._makefile_refs < 1: + try: + self._closed = True + return self.connection.close() + except OpenSSL.SSL.Error: + return + else: + self._makefile_refs -= 1 + + def getpeercert(self, binary_form=False): + x509 = self.connection.get_peer_certificate() + + if not x509: + return x509 + + if binary_form: + return OpenSSL.crypto.dump_certificate( + OpenSSL.crypto.FILETYPE_ASN1, + x509) + + return { + 'subject': ( + (('commonName', x509.get_subject().CN),), + ), + 'subjectAltName': [ + ('DNS', value) + for value in get_subj_alt_name(x509) + ] + } + + def _reuse(self): + self._makefile_refs += 1 + + def _drop(self): + if self._makefile_refs < 1: + self.close() + else: + self._makefile_refs -= 1 + + +if _fileobject: # Platform-specific: Python 2 + def makefile(self, mode, bufsize=-1): + self._makefile_refs += 1 + return _fileobject(self, mode, bufsize, close=True) +else: # Platform-specific: Python 3 + makefile = backport_makefile + +WrappedSocket.makefile = makefile + + +def _verify_callback(cnx, x509, err_no, err_depth, return_code): + return err_no == 0 + + +def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, + ca_certs=None, server_hostname=None, + ssl_version=None, ca_cert_dir=None): + ctx = OpenSSL.SSL.Context(_openssl_versions[ssl_version]) + if certfile: + keyfile = keyfile or certfile # Match behaviour of the normal python ssl library + ctx.use_certificate_file(certfile) + if keyfile: + ctx.use_privatekey_file(keyfile) + if cert_reqs != ssl.CERT_NONE: + ctx.set_verify(_openssl_verify[cert_reqs], _verify_callback) + if ca_certs or ca_cert_dir: + try: + ctx.load_verify_locations(ca_certs, ca_cert_dir) + except OpenSSL.SSL.Error as e: + raise ssl.SSLError('bad ca_certs: %r' % ca_certs, e) + else: + ctx.set_default_verify_paths() + + # Disable TLS compression to mitigate CRIME attack (issue #309) + OP_NO_COMPRESSION = 0x20000 + ctx.set_options(OP_NO_COMPRESSION) + + # Set list of supported ciphersuites. + ctx.set_cipher_list(DEFAULT_SSL_CIPHER_LIST) + + cnx = OpenSSL.SSL.Connection(ctx, sock) + if isinstance(server_hostname, six.text_type): # Platform-specific: Python 3 + server_hostname = server_hostname.encode('utf-8') + cnx.set_tlsext_host_name(server_hostname) + cnx.set_connect_state() + while True: + try: + cnx.do_handshake() + except OpenSSL.SSL.WantReadError: + rd, _, _ = select.select([sock], [], [], sock.gettimeout()) + if not rd: + raise timeout('select timed out') + continue + except OpenSSL.SSL.Error as e: + raise ssl.SSLError('bad handshake: %r' % e) + break + + return WrappedSocket(cnx, sock) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/socks.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/socks.py new file mode 100644 index 0000000..81970fa --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/contrib/socks.py @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- +""" +SOCKS support for urllib3 +~~~~~~~~~~~~~~~~~~~~~~~~~ + +This contrib module contains provisional support for SOCKS proxies from within +urllib3. This module supports SOCKS4 (specifically the SOCKS4A variant) and +SOCKS5. To enable its functionality, either install PySocks or install this +module with the ``socks`` extra. + +Known Limitations: + +- Currently PySocks does not support contacting remote websites via literal + IPv6 addresses. Any such connection attempt will fail. +- Currently PySocks does not support IPv6 connections to the SOCKS proxy. Any + such connection attempt will fail. +""" +from __future__ import absolute_import + +try: + import socks +except ImportError: + import warnings + from ..exceptions import DependencyWarning + + warnings.warn(( + 'SOCKS support in urllib3 requires the installation of optional ' + 'dependencies: specifically, PySocks. For more information, see ' + 'https://urllib3.readthedocs.io/en/latest/contrib.html#socks-proxies' + ), + DependencyWarning + ) + raise + +from socket import error as SocketError, timeout as SocketTimeout + +from ..connection import ( + HTTPConnection, HTTPSConnection +) +from ..connectionpool import ( + HTTPConnectionPool, HTTPSConnectionPool +) +from ..exceptions import ConnectTimeoutError, NewConnectionError +from ..poolmanager import PoolManager +from ..util.url import parse_url + +try: + import ssl +except ImportError: + ssl = None + + +class SOCKSConnection(HTTPConnection): + """ + A plain-text HTTP connection that connects via a SOCKS proxy. + """ + def __init__(self, *args, **kwargs): + self._socks_options = kwargs.pop('_socks_options') + super(SOCKSConnection, self).__init__(*args, **kwargs) + + def _new_conn(self): + """ + Establish a new connection via the SOCKS proxy. + """ + extra_kw = {} + if self.source_address: + extra_kw['source_address'] = self.source_address + + if self.socket_options: + extra_kw['socket_options'] = self.socket_options + + try: + conn = socks.create_connection( + (self.host, self.port), + proxy_type=self._socks_options['socks_version'], + proxy_addr=self._socks_options['proxy_host'], + proxy_port=self._socks_options['proxy_port'], + proxy_username=self._socks_options['username'], + proxy_password=self._socks_options['password'], + timeout=self.timeout, + **extra_kw + ) + + except SocketTimeout as e: + raise ConnectTimeoutError( + self, "Connection to %s timed out. (connect timeout=%s)" % + (self.host, self.timeout)) + + except socks.ProxyError as e: + # This is fragile as hell, but it seems to be the only way to raise + # useful errors here. + if e.socket_err: + error = e.socket_err + if isinstance(error, SocketTimeout): + raise ConnectTimeoutError( + self, + "Connection to %s timed out. (connect timeout=%s)" % + (self.host, self.timeout) + ) + else: + raise NewConnectionError( + self, + "Failed to establish a new connection: %s" % error + ) + else: + raise NewConnectionError( + self, + "Failed to establish a new connection: %s" % e + ) + + except SocketError as e: # Defensive: PySocks should catch all these. + raise NewConnectionError( + self, "Failed to establish a new connection: %s" % e) + + return conn + + +# We don't need to duplicate the Verified/Unverified distinction from +# urllib3/connection.py here because the HTTPSConnection will already have been +# correctly set to either the Verified or Unverified form by that module. This +# means the SOCKSHTTPSConnection will automatically be the correct type. +class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): + pass + + +class SOCKSHTTPConnectionPool(HTTPConnectionPool): + ConnectionCls = SOCKSConnection + + +class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): + ConnectionCls = SOCKSHTTPSConnection + + +class SOCKSProxyManager(PoolManager): + """ + A version of the urllib3 ProxyManager that routes connections via the + defined SOCKS proxy. + """ + pool_classes_by_scheme = { + 'http': SOCKSHTTPConnectionPool, + 'https': SOCKSHTTPSConnectionPool, + } + + def __init__(self, proxy_url, username=None, password=None, + num_pools=10, headers=None, **connection_pool_kw): + parsed = parse_url(proxy_url) + + if parsed.scheme == 'socks5': + socks_version = socks.PROXY_TYPE_SOCKS5 + elif parsed.scheme == 'socks4': + socks_version = socks.PROXY_TYPE_SOCKS4 + else: + raise ValueError( + "Unable to determine SOCKS version from %s" % proxy_url + ) + + self.proxy_url = proxy_url + + socks_options = { + 'socks_version': socks_version, + 'proxy_host': parsed.host, + 'proxy_port': parsed.port, + 'username': username, + 'password': password, + } + connection_pool_kw['_socks_options'] = socks_options + + super(SOCKSProxyManager, self).__init__( + num_pools, headers, **connection_pool_kw + ) + + self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/exceptions.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/exceptions.py new file mode 100644 index 0000000..f2e6591 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/exceptions.py @@ -0,0 +1,209 @@ +from __future__ import absolute_import +# Base Exceptions + + +class HTTPError(Exception): + "Base exception used by this module." + pass + + +class HTTPWarning(Warning): + "Base warning used by this module." + pass + + +class PoolError(HTTPError): + "Base exception for errors caused within a pool." + def __init__(self, pool, message): + self.pool = pool + HTTPError.__init__(self, "%s: %s" % (pool, message)) + + def __reduce__(self): + # For pickling purposes. + return self.__class__, (None, None) + + +class RequestError(PoolError): + "Base exception for PoolErrors that have associated URLs." + def __init__(self, pool, url, message): + self.url = url + PoolError.__init__(self, pool, message) + + def __reduce__(self): + # For pickling purposes. + return self.__class__, (None, self.url, None) + + +class SSLError(HTTPError): + "Raised when SSL certificate fails in an HTTPS connection." + pass + + +class ProxyError(HTTPError): + "Raised when the connection to a proxy fails." + pass + + +class DecodeError(HTTPError): + "Raised when automatic decoding based on Content-Type fails." + pass + + +class ProtocolError(HTTPError): + "Raised when something unexpected happens mid-request/response." + pass + + +#: Renamed to ProtocolError but aliased for backwards compatibility. +ConnectionError = ProtocolError + + +# Leaf Exceptions + +class MaxRetryError(RequestError): + """Raised when the maximum number of retries is exceeded. + + :param pool: The connection pool + :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool` + :param string url: The requested Url + :param exceptions.Exception reason: The underlying error + + """ + + def __init__(self, pool, url, reason=None): + self.reason = reason + + message = "Max retries exceeded with url: %s (Caused by %r)" % ( + url, reason) + + RequestError.__init__(self, pool, url, message) + + +class HostChangedError(RequestError): + "Raised when an existing pool gets a request for a foreign host." + + def __init__(self, pool, url, retries=3): + message = "Tried to open a foreign host with url: %s" % url + RequestError.__init__(self, pool, url, message) + self.retries = retries + + +class TimeoutStateError(HTTPError): + """ Raised when passing an invalid state to a timeout """ + pass + + +class TimeoutError(HTTPError): + """ Raised when a socket timeout error occurs. + + Catching this error will catch both :exc:`ReadTimeoutErrors + <ReadTimeoutError>` and :exc:`ConnectTimeoutErrors <ConnectTimeoutError>`. + """ + pass + + +class ReadTimeoutError(TimeoutError, RequestError): + "Raised when a socket timeout occurs while receiving data from a server" + pass + + +# This timeout error does not have a URL attached and needs to inherit from the +# base HTTPError +class ConnectTimeoutError(TimeoutError): + "Raised when a socket timeout occurs while connecting to a server" + pass + + +class NewConnectionError(ConnectTimeoutError, PoolError): + "Raised when we fail to establish a new connection. Usually ECONNREFUSED." + pass + + +class EmptyPoolError(PoolError): + "Raised when a pool runs out of connections and no more are allowed." + pass + + +class ClosedPoolError(PoolError): + "Raised when a request enters a pool after the pool has been closed." + pass + + +class LocationValueError(ValueError, HTTPError): + "Raised when there is something wrong with a given URL input." + pass + + +class LocationParseError(LocationValueError): + "Raised when get_host or similar fails to parse the URL input." + + def __init__(self, location): + message = "Failed to parse: %s" % location + HTTPError.__init__(self, message) + + self.location = location + + +class ResponseError(HTTPError): + "Used as a container for an error reason supplied in a MaxRetryError." + GENERIC_ERROR = 'too many error responses' + SPECIFIC_ERROR = 'too many {status_code} error responses' + + +class SecurityWarning(HTTPWarning): + "Warned when perfoming security reducing actions" + pass + + +class SubjectAltNameWarning(SecurityWarning): + "Warned when connecting to a host with a certificate missing a SAN." + pass + + +class InsecureRequestWarning(SecurityWarning): + "Warned when making an unverified HTTPS request." + pass + + +class SystemTimeWarning(SecurityWarning): + "Warned when system time is suspected to be wrong" + pass + + +class InsecurePlatformWarning(SecurityWarning): + "Warned when certain SSL configuration is not available on a platform." + pass + + +class SNIMissingWarning(HTTPWarning): + "Warned when making a HTTPS request without SNI available." + pass + + +class DependencyWarning(HTTPWarning): + """ + Warned when an attempt is made to import a module with missing optional + dependencies. + """ + pass + + +class ResponseNotChunked(ProtocolError, ValueError): + "Response needs to be chunked in order to read it as chunks." + pass + + +class ProxySchemeUnknown(AssertionError, ValueError): + "ProxyManager does not support the supplied scheme" + # TODO(t-8ch): Stop inheriting from AssertionError in v2.0. + + def __init__(self, scheme): + message = "Not supported proxy scheme %s" % scheme + super(ProxySchemeUnknown, self).__init__(message) + + +class HeaderParsingError(HTTPError): + "Raised by assert_header_parsing, but we convert it to a log.warning statement." + def __init__(self, defects, unparsed_data): + message = '%s, unparsed data: %r' % (defects or 'Unknown', unparsed_data) + super(HeaderParsingError, self).__init__(message) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/fields.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/fields.py new file mode 100644 index 0000000..8fa2a12 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/fields.py @@ -0,0 +1,178 @@ +from __future__ import absolute_import +import email.utils +import mimetypes + +from .packages import six + + +def guess_content_type(filename, default='application/octet-stream'): + """ + Guess the "Content-Type" of a file. + + :param filename: + The filename to guess the "Content-Type" of using :mod:`mimetypes`. + :param default: + If no "Content-Type" can be guessed, default to `default`. + """ + if filename: + return mimetypes.guess_type(filename)[0] or default + return default + + +def format_header_param(name, value): + """ + Helper function to format and quote a single header parameter. + + Particularly useful for header parameters which might contain + non-ASCII values, like file names. This follows RFC 2231, as + suggested by RFC 2388 Section 4.4. + + :param name: + The name of the parameter, a string expected to be ASCII only. + :param value: + The value of the parameter, provided as a unicode string. + """ + if not any(ch in value for ch in '"\\\r\n'): + result = '%s="%s"' % (name, value) + try: + result.encode('ascii') + except (UnicodeEncodeError, UnicodeDecodeError): + pass + else: + return result + if not six.PY3 and isinstance(value, six.text_type): # Python 2: + value = value.encode('utf-8') + value = email.utils.encode_rfc2231(value, 'utf-8') + value = '%s*=%s' % (name, value) + return value + + +class RequestField(object): + """ + A data container for request body parameters. + + :param name: + The name of this request field. + :param data: + The data/value body. + :param filename: + An optional filename of the request field. + :param headers: + An optional dict-like object of headers to initially use for the field. + """ + def __init__(self, name, data, filename=None, headers=None): + self._name = name + self._filename = filename + self.data = data + self.headers = {} + if headers: + self.headers = dict(headers) + + @classmethod + def from_tuples(cls, fieldname, value): + """ + A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters. + + Supports constructing :class:`~urllib3.fields.RequestField` from + parameter of key/value strings AND key/filetuple. A filetuple is a + (filename, data, MIME type) tuple where the MIME type is optional. + For example:: + + 'foo': 'bar', + 'fakefile': ('foofile.txt', 'contents of foofile'), + 'realfile': ('barfile.txt', open('realfile').read()), + 'typedfile': ('bazfile.bin', open('bazfile').read(), 'image/jpeg'), + 'nonamefile': 'contents of nonamefile field', + + Field names and filenames must be unicode. + """ + if isinstance(value, tuple): + if len(value) == 3: + filename, data, content_type = value + else: + filename, data = value + content_type = guess_content_type(filename) + else: + filename = None + content_type = None + data = value + + request_param = cls(fieldname, data, filename=filename) + request_param.make_multipart(content_type=content_type) + + return request_param + + def _render_part(self, name, value): + """ + Overridable helper function to format a single header parameter. + + :param name: + The name of the parameter, a string expected to be ASCII only. + :param value: + The value of the parameter, provided as a unicode string. + """ + return format_header_param(name, value) + + def _render_parts(self, header_parts): + """ + Helper function to format and quote a single header. + + Useful for single headers that are composed of multiple items. E.g., + 'Content-Disposition' fields. + + :param header_parts: + A sequence of (k, v) typles or a :class:`dict` of (k, v) to format + as `k1="v1"; k2="v2"; ...`. + """ + parts = [] + iterable = header_parts + if isinstance(header_parts, dict): + iterable = header_parts.items() + + for name, value in iterable: + if value: + parts.append(self._render_part(name, value)) + + return '; '.join(parts) + + def render_headers(self): + """ + Renders the headers for this request field. + """ + lines = [] + + sort_keys = ['Content-Disposition', 'Content-Type', 'Content-Location'] + for sort_key in sort_keys: + if self.headers.get(sort_key, False): + lines.append('%s: %s' % (sort_key, self.headers[sort_key])) + + for header_name, header_value in self.headers.items(): + if header_name not in sort_keys: + if header_value: + lines.append('%s: %s' % (header_name, header_value)) + + lines.append('\r\n') + return '\r\n'.join(lines) + + def make_multipart(self, content_disposition=None, content_type=None, + content_location=None): + """ + Makes this request field into a multipart request field. + + This method overrides "Content-Disposition", "Content-Type" and + "Content-Location" headers to the request parameter. + + :param content_type: + The 'Content-Type' of the request body. + :param content_location: + The 'Content-Location' of the request body. + + """ + self.headers['Content-Disposition'] = content_disposition or 'form-data' + self.headers['Content-Disposition'] += '; '.join([ + '', self._render_parts( + (('name', self._name), ('filename', self._filename)) + ) + ]) + self.headers['Content-Type'] = content_type + self.headers['Content-Location'] = content_location diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/filepost.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/filepost.py new file mode 100644 index 0000000..97a2843 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/filepost.py @@ -0,0 +1,94 @@ +from __future__ import absolute_import +import codecs + +from uuid import uuid4 +from io import BytesIO + +from .packages import six +from .packages.six import b +from .fields import RequestField + +writer = codecs.lookup('utf-8')[3] + + +def choose_boundary(): + """ + Our embarassingly-simple replacement for mimetools.choose_boundary. + """ + return uuid4().hex + + +def iter_field_objects(fields): + """ + Iterate over fields. + + Supports list of (k, v) tuples and dicts, and lists of + :class:`~urllib3.fields.RequestField`. + + """ + if isinstance(fields, dict): + i = six.iteritems(fields) + else: + i = iter(fields) + + for field in i: + if isinstance(field, RequestField): + yield field + else: + yield RequestField.from_tuples(*field) + + +def iter_fields(fields): + """ + .. deprecated:: 1.6 + + Iterate over fields. + + The addition of :class:`~urllib3.fields.RequestField` makes this function + obsolete. Instead, use :func:`iter_field_objects`, which returns + :class:`~urllib3.fields.RequestField` objects. + + Supports list of (k, v) tuples and dicts. + """ + if isinstance(fields, dict): + return ((k, v) for k, v in six.iteritems(fields)) + + return ((k, v) for k, v in fields) + + +def encode_multipart_formdata(fields, boundary=None): + """ + Encode a dictionary of ``fields`` using the multipart/form-data MIME format. + + :param fields: + Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`). + + :param boundary: + If not specified, then a random boundary will be generated using + :func:`mimetools.choose_boundary`. + """ + body = BytesIO() + if boundary is None: + boundary = choose_boundary() + + for field in iter_field_objects(fields): + body.write(b('--%s\r\n' % (boundary))) + + writer(body).write(field.render_headers()) + data = field.data + + if isinstance(data, int): + data = str(data) # Backwards compatibility + + if isinstance(data, six.text_type): + writer(body).write(data) + else: + body.write(data) + + body.write(b'\r\n') + + body.write(b('--%s--\r\n' % (boundary))) + + content_type = str('multipart/form-data; boundary=%s' % boundary) + + return body.getvalue(), content_type diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/__init__.py new file mode 100644 index 0000000..170e974 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/__init__.py @@ -0,0 +1,5 @@ +from __future__ import absolute_import + +from . import ssl_match_hostname + +__all__ = ('ssl_match_hostname', ) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py new file mode 100644 index 0000000..4479363 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py @@ -0,0 +1,259 @@ +# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. +# Passes Python2.7's test suite and incorporates all the latest updates. +# Copyright 2009 Raymond Hettinger, released under the MIT License. +# http://code.activestate.com/recipes/576693/ +try: + from thread import get_ident as _get_ident +except ImportError: + from dummy_thread import get_ident as _get_ident + +try: + from _abcoll import KeysView, ValuesView, ItemsView +except ImportError: + pass + + +class OrderedDict(dict): + 'Dictionary that remembers insertion order' + # An inherited dict maps keys to values. + # The inherited dict provides __getitem__, __len__, __contains__, and get. + # The remaining methods are order-aware. + # Big-O running times for all methods are the same as for regular dictionaries. + + # The internal self.__map dictionary maps keys to links in a doubly linked list. + # The circular doubly linked list starts and ends with a sentinel element. + # The sentinel element never gets deleted (this simplifies the algorithm). + # Each link is stored as a list of length three: [PREV, NEXT, KEY]. + + def __init__(self, *args, **kwds): + '''Initialize an ordered dictionary. Signature is the same as for + regular dictionaries, but keyword arguments are not recommended + because their insertion order is arbitrary. + + ''' + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__root + except AttributeError: + self.__root = root = [] # sentinel node + root[:] = [root, root, None] + self.__map = {} + self.__update(*args, **kwds) + + def __setitem__(self, key, value, dict_setitem=dict.__setitem__): + 'od.__setitem__(i, y) <==> od[i]=y' + # Setting a new item creates a new link which goes at the end of the linked + # list, and the inherited dictionary is updated with the new key/value pair. + if key not in self: + root = self.__root + last = root[0] + last[1] = root[0] = self.__map[key] = [last, root, key] + dict_setitem(self, key, value) + + def __delitem__(self, key, dict_delitem=dict.__delitem__): + 'od.__delitem__(y) <==> del od[y]' + # Deleting an existing item uses self.__map to find the link which is + # then removed by updating the links in the predecessor and successor nodes. + dict_delitem(self, key) + link_prev, link_next, key = self.__map.pop(key) + link_prev[1] = link_next + link_next[0] = link_prev + + def __iter__(self): + 'od.__iter__() <==> iter(od)' + root = self.__root + curr = root[1] + while curr is not root: + yield curr[2] + curr = curr[1] + + def __reversed__(self): + 'od.__reversed__() <==> reversed(od)' + root = self.__root + curr = root[0] + while curr is not root: + yield curr[2] + curr = curr[0] + + def clear(self): + 'od.clear() -> None. Remove all items from od.' + try: + for node in self.__map.itervalues(): + del node[:] + root = self.__root + root[:] = [root, root, None] + self.__map.clear() + except AttributeError: + pass + dict.clear(self) + + def popitem(self, last=True): + '''od.popitem() -> (k, v), return and remove a (key, value) pair. + Pairs are returned in LIFO order if last is true or FIFO order if false. + + ''' + if not self: + raise KeyError('dictionary is empty') + root = self.__root + if last: + link = root[0] + link_prev = link[0] + link_prev[1] = root + root[0] = link_prev + else: + link = root[1] + link_next = link[1] + root[1] = link_next + link_next[0] = root + key = link[2] + del self.__map[key] + value = dict.pop(self, key) + return key, value + + # -- the following methods do not depend on the internal structure -- + + def keys(self): + 'od.keys() -> list of keys in od' + return list(self) + + def values(self): + 'od.values() -> list of values in od' + return [self[key] for key in self] + + def items(self): + 'od.items() -> list of (key, value) pairs in od' + return [(key, self[key]) for key in self] + + def iterkeys(self): + 'od.iterkeys() -> an iterator over the keys in od' + return iter(self) + + def itervalues(self): + 'od.itervalues -> an iterator over the values in od' + for k in self: + yield self[k] + + def iteritems(self): + 'od.iteritems -> an iterator over the (key, value) items in od' + for k in self: + yield (k, self[k]) + + def update(*args, **kwds): + '''od.update(E, **F) -> None. Update od from dict/iterable E and F. + + If E is a dict instance, does: for k in E: od[k] = E[k] + If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] + Or if E is an iterable of items, does: for k, v in E: od[k] = v + In either case, this is followed by: for k, v in F.items(): od[k] = v + + ''' + if len(args) > 2: + raise TypeError('update() takes at most 2 positional ' + 'arguments (%d given)' % (len(args),)) + elif not args: + raise TypeError('update() takes at least 1 argument (0 given)') + self = args[0] + # Make progressively weaker assumptions about "other" + other = () + if len(args) == 2: + other = args[1] + if isinstance(other, dict): + for key in other: + self[key] = other[key] + elif hasattr(other, 'keys'): + for key in other.keys(): + self[key] = other[key] + else: + for key, value in other: + self[key] = value + for key, value in kwds.items(): + self[key] = value + + __update = update # let subclasses override update without breaking __init__ + + __marker = object() + + def pop(self, key, default=__marker): + '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + + ''' + if key in self: + result = self[key] + del self[key] + return result + if default is self.__marker: + raise KeyError(key) + return default + + def setdefault(self, key, default=None): + 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + if key in self: + return self[key] + self[key] = default + return default + + def __repr__(self, _repr_running={}): + 'od.__repr__() <==> repr(od)' + call_key = id(self), _get_ident() + if call_key in _repr_running: + return '...' + _repr_running[call_key] = 1 + try: + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + finally: + del _repr_running[call_key] + + def __reduce__(self): + 'Return state information for pickling' + items = [[k, self[k]] for k in self] + inst_dict = vars(self).copy() + for k in vars(OrderedDict()): + inst_dict.pop(k, None) + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def copy(self): + 'od.copy() -> a shallow copy of od' + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S + and values equal to v (which defaults to None). + + ''' + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + while comparison to a regular mapping is order-insensitive. + + ''' + if isinstance(other, OrderedDict): + return len(self)==len(other) and self.items() == other.items() + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other + + # -- the following methods are only used in Python 2.7 -- + + def viewkeys(self): + "od.viewkeys() -> a set-like object providing a view on od's keys" + return KeysView(self) + + def viewvalues(self): + "od.viewvalues() -> an object providing a view on od's values" + return ValuesView(self) + + def viewitems(self): + "od.viewitems() -> a set-like object providing a view on od's items" + return ItemsView(self) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/six.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/six.py new file mode 100644 index 0000000..190c023 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/six.py @@ -0,0 +1,868 @@ +"""Utilities for writing code that runs on Python 2 and 3""" + +# Copyright (c) 2010-2015 Benjamin Peterson +# +# 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. + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson <benjamin@python.org>" +__version__ = "1.10.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py new file mode 100644 index 0000000..dd59a75 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py @@ -0,0 +1,13 @@ +try: + # Python 3.2+ + from ssl import CertificateError, match_hostname +except ImportError: + try: + # Backport of the function from a pypi module + from backports.ssl_match_hostname import CertificateError, match_hostname + except ImportError: + # Our vendored copy + from ._implementation import CertificateError, match_hostname + +# Not needed, but documenting what we provide. +__all__ = ('CertificateError', 'match_hostname') diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py new file mode 100644 index 0000000..52f4287 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py @@ -0,0 +1,105 @@ +"""The match_hostname() function from Python 3.3.3, essential when using SSL.""" + +# Note: This file is under the PSF license as the code comes from the python +# stdlib. http://docs.python.org/3/license.html + +import re + +__version__ = '3.4.0.2' + +class CertificateError(ValueError): + pass + + +def _dnsname_match(dn, hostname, max_wildcards=1): + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + # Ported from python3-syntax: + # leftmost, *remainder = dn.split(r'.') + parts = dn.split(r'.') + leftmost = parts[0] + remainder = parts[1:] + + wildcards = leftmost.count('*') + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn)) + + # speed up common case w/o wildcards + if not wildcards: + return dn.lower() == hostname.lower() + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == '*': + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append('[^.]+') + elif leftmost.startswith('xn--') or hostname.startswith('xn--'): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + return pat.match(hostname) + + +def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError("empty or no certificate") + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get('subject', ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == 'commonName': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError("hostname %r " + "doesn't match either of %s" + % (hostname, ', '.join(map(repr, dnsnames)))) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r " + "doesn't match %r" + % (hostname, dnsnames[0])) + else: + raise CertificateError("no appropriate commonName or " + "subjectAltName fields were found") diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/poolmanager.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/poolmanager.py new file mode 100644 index 0000000..7ed00b1 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/poolmanager.py @@ -0,0 +1,367 @@ +from __future__ import absolute_import +import collections +import functools +import logging + +try: # Python 3 + from urllib.parse import urljoin +except ImportError: + from urlparse import urljoin + +from ._collections import RecentlyUsedContainer +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool +from .connectionpool import port_by_scheme +from .exceptions import LocationValueError, MaxRetryError, ProxySchemeUnknown +from .request import RequestMethods +from .util.url import parse_url +from .util.retry import Retry + + +__all__ = ['PoolManager', 'ProxyManager', 'proxy_from_url'] + + +log = logging.getLogger(__name__) + +SSL_KEYWORDS = ('key_file', 'cert_file', 'cert_reqs', 'ca_certs', + 'ssl_version', 'ca_cert_dir') + +# The base fields to use when determining what pool to get a connection from; +# these do not rely on the ``connection_pool_kw`` and can be determined by the +# URL and potentially the ``urllib3.connection.port_by_scheme`` dictionary. +# +# All custom key schemes should include the fields in this key at a minimum. +BasePoolKey = collections.namedtuple('BasePoolKey', ('scheme', 'host', 'port')) + +# The fields to use when determining what pool to get a HTTP and HTTPS +# connection from. All additional fields must be present in the PoolManager's +# ``connection_pool_kw`` instance variable. +HTTPPoolKey = collections.namedtuple( + 'HTTPPoolKey', BasePoolKey._fields + ('timeout', 'retries', 'strict', + 'block', 'source_address') +) +HTTPSPoolKey = collections.namedtuple( + 'HTTPSPoolKey', HTTPPoolKey._fields + SSL_KEYWORDS +) + + +def _default_key_normalizer(key_class, request_context): + """ + Create a pool key of type ``key_class`` for a request. + + According to RFC 3986, both the scheme and host are case-insensitive. + Therefore, this function normalizes both before constructing the pool + key for an HTTPS request. If you wish to change this behaviour, provide + alternate callables to ``key_fn_by_scheme``. + + :param key_class: + The class to use when constructing the key. This should be a namedtuple + with the ``scheme`` and ``host`` keys at a minimum. + + :param request_context: + A dictionary-like object that contain the context for a request. + It should contain a key for each field in the :class:`HTTPPoolKey` + """ + context = {} + for key in key_class._fields: + context[key] = request_context.get(key) + context['scheme'] = context['scheme'].lower() + context['host'] = context['host'].lower() + return key_class(**context) + + +# A dictionary that maps a scheme to a callable that creates a pool key. +# This can be used to alter the way pool keys are constructed, if desired. +# Each PoolManager makes a copy of this dictionary so they can be configured +# globally here, or individually on the instance. +key_fn_by_scheme = { + 'http': functools.partial(_default_key_normalizer, HTTPPoolKey), + 'https': functools.partial(_default_key_normalizer, HTTPSPoolKey), +} + +pool_classes_by_scheme = { + 'http': HTTPConnectionPool, + 'https': HTTPSConnectionPool, +} + + +class PoolManager(RequestMethods): + """ + Allows for arbitrary requests while transparently keeping track of + necessary connection pools for you. + + :param num_pools: + Number of connection pools to cache before discarding the least + recently used pool. + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + + :param \**connection_pool_kw: + Additional parameters are used to create fresh + :class:`urllib3.connectionpool.ConnectionPool` instances. + + Example:: + + >>> manager = PoolManager(num_pools=2) + >>> r = manager.request('GET', 'http://google.com/') + >>> r = manager.request('GET', 'http://google.com/mail') + >>> r = manager.request('GET', 'http://yahoo.com/') + >>> len(manager.pools) + 2 + + """ + + proxy = None + + def __init__(self, num_pools=10, headers=None, **connection_pool_kw): + RequestMethods.__init__(self, headers) + self.connection_pool_kw = connection_pool_kw + self.pools = RecentlyUsedContainer(num_pools, + dispose_func=lambda p: p.close()) + + # Locally set the pool classes and keys so other PoolManagers can + # override them. + self.pool_classes_by_scheme = pool_classes_by_scheme + self.key_fn_by_scheme = key_fn_by_scheme.copy() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.clear() + # Return False to re-raise any potential exceptions + return False + + def _new_pool(self, scheme, host, port): + """ + Create a new :class:`ConnectionPool` based on host, port and scheme. + + This method is used to actually create the connection pools handed out + by :meth:`connection_from_url` and companion methods. It is intended + to be overridden for customization. + """ + pool_cls = self.pool_classes_by_scheme[scheme] + kwargs = self.connection_pool_kw + if scheme == 'http': + kwargs = self.connection_pool_kw.copy() + for kw in SSL_KEYWORDS: + kwargs.pop(kw, None) + + return pool_cls(host, port, **kwargs) + + def clear(self): + """ + Empty our store of pools and direct them all to close. + + This will not affect in-flight connections, but they will not be + re-used after completion. + """ + self.pools.clear() + + def connection_from_host(self, host, port=None, scheme='http'): + """ + Get a :class:`ConnectionPool` based on the host, port, and scheme. + + If ``port`` isn't given, it will be derived from the ``scheme`` using + ``urllib3.connectionpool.port_by_scheme``. + """ + + if not host: + raise LocationValueError("No host specified.") + + request_context = self.connection_pool_kw.copy() + request_context['scheme'] = scheme or 'http' + if not port: + port = port_by_scheme.get(request_context['scheme'].lower(), 80) + request_context['port'] = port + request_context['host'] = host + + return self.connection_from_context(request_context) + + def connection_from_context(self, request_context): + """ + Get a :class:`ConnectionPool` based on the request context. + + ``request_context`` must at least contain the ``scheme`` key and its + value must be a key in ``key_fn_by_scheme`` instance variable. + """ + scheme = request_context['scheme'].lower() + pool_key_constructor = self.key_fn_by_scheme[scheme] + pool_key = pool_key_constructor(request_context) + + return self.connection_from_pool_key(pool_key) + + def connection_from_pool_key(self, pool_key): + """ + Get a :class:`ConnectionPool` based on the provided pool key. + + ``pool_key`` should be a namedtuple that only contains immutable + objects. At a minimum it must have the ``scheme``, ``host``, and + ``port`` fields. + """ + with self.pools.lock: + # If the scheme, host, or port doesn't match existing open + # connections, open a new ConnectionPool. + pool = self.pools.get(pool_key) + if pool: + return pool + + # Make a fresh ConnectionPool of the desired type + pool = self._new_pool(pool_key.scheme, pool_key.host, pool_key.port) + self.pools[pool_key] = pool + + return pool + + def connection_from_url(self, url): + """ + Similar to :func:`urllib3.connectionpool.connection_from_url` but + doesn't pass any additional parameters to the + :class:`urllib3.connectionpool.ConnectionPool` constructor. + + Additional parameters are taken from the :class:`.PoolManager` + constructor. + """ + u = parse_url(url) + return self.connection_from_host(u.host, port=u.port, scheme=u.scheme) + + def urlopen(self, method, url, redirect=True, **kw): + """ + Same as :meth:`urllib3.connectionpool.HTTPConnectionPool.urlopen` + with custom cross-host redirect logic and only sends the request-uri + portion of the ``url``. + + The given ``url`` parameter must be absolute, such that an appropriate + :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it. + """ + u = parse_url(url) + conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme) + + kw['assert_same_host'] = False + kw['redirect'] = False + if 'headers' not in kw: + kw['headers'] = self.headers + + if self.proxy is not None and u.scheme == "http": + response = conn.urlopen(method, url, **kw) + else: + response = conn.urlopen(method, u.request_uri, **kw) + + redirect_location = redirect and response.get_redirect_location() + if not redirect_location: + return response + + # Support relative URLs for redirecting. + redirect_location = urljoin(url, redirect_location) + + # RFC 7231, Section 6.4.4 + if response.status == 303: + method = 'GET' + + retries = kw.get('retries') + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect) + + try: + retries = retries.increment(method, url, response=response, _pool=conn) + except MaxRetryError: + if retries.raise_on_redirect: + raise + return response + + kw['retries'] = retries + kw['redirect'] = redirect + + log.info("Redirecting %s -> %s", url, redirect_location) + return self.urlopen(method, redirect_location, **kw) + + +class ProxyManager(PoolManager): + """ + Behaves just like :class:`PoolManager`, but sends all requests through + the defined proxy, using the CONNECT method for HTTPS URLs. + + :param proxy_url: + The URL of the proxy to be used. + + :param proxy_headers: + A dictionary contaning headers that will be sent to the proxy. In case + of HTTP they are being sent with each request, while in the + HTTPS/CONNECT case they are sent only once. Could be used for proxy + authentication. + + Example: + >>> proxy = urllib3.ProxyManager('http://localhost:3128/') + >>> r1 = proxy.request('GET', 'http://google.com/') + >>> r2 = proxy.request('GET', 'http://httpbin.org/') + >>> len(proxy.pools) + 1 + >>> r3 = proxy.request('GET', 'https://httpbin.org/') + >>> r4 = proxy.request('GET', 'https://twitter.com/') + >>> len(proxy.pools) + 3 + + """ + + def __init__(self, proxy_url, num_pools=10, headers=None, + proxy_headers=None, **connection_pool_kw): + + if isinstance(proxy_url, HTTPConnectionPool): + proxy_url = '%s://%s:%i' % (proxy_url.scheme, proxy_url.host, + proxy_url.port) + proxy = parse_url(proxy_url) + if not proxy.port: + port = port_by_scheme.get(proxy.scheme, 80) + proxy = proxy._replace(port=port) + + if proxy.scheme not in ("http", "https"): + raise ProxySchemeUnknown(proxy.scheme) + + self.proxy = proxy + self.proxy_headers = proxy_headers or {} + + connection_pool_kw['_proxy'] = self.proxy + connection_pool_kw['_proxy_headers'] = self.proxy_headers + + super(ProxyManager, self).__init__( + num_pools, headers, **connection_pool_kw) + + def connection_from_host(self, host, port=None, scheme='http'): + if scheme == "https": + return super(ProxyManager, self).connection_from_host( + host, port, scheme) + + return super(ProxyManager, self).connection_from_host( + self.proxy.host, self.proxy.port, self.proxy.scheme) + + def _set_proxy_headers(self, url, headers=None): + """ + Sets headers needed by proxies: specifically, the Accept and Host + headers. Only sets headers not provided by the user. + """ + headers_ = {'Accept': '*/*'} + + netloc = parse_url(url).netloc + if netloc: + headers_['Host'] = netloc + + if headers: + headers_.update(headers) + return headers_ + + def urlopen(self, method, url, redirect=True, **kw): + "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute." + u = parse_url(url) + + if u.scheme == "http": + # For proxied HTTPS requests, httplib sets the necessary headers + # on the CONNECT to the proxy. For HTTP, we'll definitely + # need to set 'Host' at the very least. + headers = kw.get('headers', self.headers) + kw['headers'] = self._set_proxy_headers(url, headers) + + return super(ProxyManager, self).urlopen(method, url, redirect=redirect, **kw) + + +def proxy_from_url(url, **kw): + return ProxyManager(proxy_url=url, **kw) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/request.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/request.py new file mode 100644 index 0000000..d5aa62d --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/request.py @@ -0,0 +1,151 @@ +from __future__ import absolute_import +try: + from urllib.parse import urlencode +except ImportError: + from urllib import urlencode + +from .filepost import encode_multipart_formdata + + +__all__ = ['RequestMethods'] + + +class RequestMethods(object): + """ + Convenience mixin for classes who implement a :meth:`urlopen` method, such + as :class:`~urllib3.connectionpool.HTTPConnectionPool` and + :class:`~urllib3.poolmanager.PoolManager`. + + Provides behavior for making common types of HTTP request methods and + decides which type of request field encoding to use. + + Specifically, + + :meth:`.request_encode_url` is for sending requests whose fields are + encoded in the URL (such as GET, HEAD, DELETE). + + :meth:`.request_encode_body` is for sending requests whose fields are + encoded in the *body* of the request using multipart or www-form-urlencoded + (such as for POST, PUT, PATCH). + + :meth:`.request` is for making any kind of request, it will look up the + appropriate encoding format and use one of the above two methods to make + the request. + + Initializer parameters: + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + """ + + _encode_url_methods = set(['DELETE', 'GET', 'HEAD', 'OPTIONS']) + + def __init__(self, headers=None): + self.headers = headers or {} + + def urlopen(self, method, url, body=None, headers=None, + encode_multipart=True, multipart_boundary=None, + **kw): # Abstract + raise NotImplemented("Classes extending RequestMethods must implement " + "their own ``urlopen`` method.") + + def request(self, method, url, fields=None, headers=None, **urlopen_kw): + """ + Make a request using :meth:`urlopen` with the appropriate encoding of + ``fields`` based on the ``method`` used. + + This is a convenience method that requires the least amount of manual + effort. It can be used in most situations, while still having the + option to drop down to more specific methods when necessary, such as + :meth:`request_encode_url`, :meth:`request_encode_body`, + or even the lowest level :meth:`urlopen`. + """ + method = method.upper() + + if method in self._encode_url_methods: + return self.request_encode_url(method, url, fields=fields, + headers=headers, + **urlopen_kw) + else: + return self.request_encode_body(method, url, fields=fields, + headers=headers, + **urlopen_kw) + + def request_encode_url(self, method, url, fields=None, headers=None, + **urlopen_kw): + """ + Make a request using :meth:`urlopen` with the ``fields`` encoded in + the url. This is useful for request methods like GET, HEAD, DELETE, etc. + """ + if headers is None: + headers = self.headers + + extra_kw = {'headers': headers} + extra_kw.update(urlopen_kw) + + if fields: + url += '?' + urlencode(fields) + + return self.urlopen(method, url, **extra_kw) + + def request_encode_body(self, method, url, fields=None, headers=None, + encode_multipart=True, multipart_boundary=None, + **urlopen_kw): + """ + Make a request using :meth:`urlopen` with the ``fields`` encoded in + the body. This is useful for request methods like POST, PUT, PATCH, etc. + + When ``encode_multipart=True`` (default), then + :meth:`urllib3.filepost.encode_multipart_formdata` is used to encode + the payload with the appropriate content type. Otherwise + :meth:`urllib.urlencode` is used with the + 'application/x-www-form-urlencoded' content type. + + Multipart encoding must be used when posting files, and it's reasonably + safe to use it in other times too. However, it may break request + signing, such as with OAuth. + + Supports an optional ``fields`` parameter of key/value strings AND + key/filetuple. A filetuple is a (filename, data, MIME type) tuple where + the MIME type is optional. For example:: + + fields = { + 'foo': 'bar', + 'fakefile': ('foofile.txt', 'contents of foofile'), + 'realfile': ('barfile.txt', open('realfile').read()), + 'typedfile': ('bazfile.bin', open('bazfile').read(), + 'image/jpeg'), + 'nonamefile': 'contents of nonamefile field', + } + + When uploading a file, providing a filename (the first parameter of the + tuple) is optional but recommended to best mimick behavior of browsers. + + Note that if ``headers`` are supplied, the 'Content-Type' header will + be overwritten because it depends on the dynamic random boundary string + which is used to compose the body of the request. The random boundary + string can be explicitly set with the ``multipart_boundary`` parameter. + """ + if headers is None: + headers = self.headers + + extra_kw = {'headers': {}} + + if fields: + if 'body' in urlopen_kw: + raise TypeError( + "request got values for both 'fields' and 'body', can only specify one.") + + if encode_multipart: + body, content_type = encode_multipart_formdata(fields, boundary=multipart_boundary) + else: + body, content_type = urlencode(fields), 'application/x-www-form-urlencoded' + + extra_kw['body'] = body + extra_kw['headers'] = {'Content-Type': content_type} + + extra_kw['headers'].update(headers) + extra_kw.update(urlopen_kw) + + return self.urlopen(method, url, **extra_kw) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/response.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/response.py new file mode 100644 index 0000000..5567903 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/response.py @@ -0,0 +1,530 @@ +from __future__ import absolute_import +from contextlib import contextmanager +import zlib +import io +from socket import timeout as SocketTimeout +from socket import error as SocketError + +from ._collections import HTTPHeaderDict +from .exceptions import ( + ProtocolError, DecodeError, ReadTimeoutError, ResponseNotChunked +) +from .packages.six import string_types as basestring, binary_type, PY3 +from .packages.six.moves import http_client as httplib +from .connection import HTTPException, BaseSSLError +from .util.response import is_fp_closed, is_response_to_head + + +class DeflateDecoder(object): + + def __init__(self): + self._first_try = True + self._data = binary_type() + self._obj = zlib.decompressobj() + + def __getattr__(self, name): + return getattr(self._obj, name) + + def decompress(self, data): + if not data: + return data + + if not self._first_try: + return self._obj.decompress(data) + + self._data += data + try: + return self._obj.decompress(data) + except zlib.error: + self._first_try = False + self._obj = zlib.decompressobj(-zlib.MAX_WBITS) + try: + return self.decompress(self._data) + finally: + self._data = None + + +class GzipDecoder(object): + + def __init__(self): + self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) + + def __getattr__(self, name): + return getattr(self._obj, name) + + def decompress(self, data): + if not data: + return data + return self._obj.decompress(data) + + +def _get_decoder(mode): + if mode == 'gzip': + return GzipDecoder() + + return DeflateDecoder() + + +class HTTPResponse(io.IOBase): + """ + HTTP Response container. + + Backwards-compatible to httplib's HTTPResponse but the response ``body`` is + loaded and decoded on-demand when the ``data`` property is accessed. This + class is also compatible with the Python standard library's :mod:`io` + module, and can hence be treated as a readable object in the context of that + framework. + + Extra parameters for behaviour not present in httplib.HTTPResponse: + + :param preload_content: + If True, the response's body will be preloaded during construction. + + :param decode_content: + If True, attempts to decode specific content-encoding's based on headers + (like 'gzip' and 'deflate') will be skipped and raw data will be used + instead. + + :param original_response: + When this HTTPResponse wrapper is generated from an httplib.HTTPResponse + object, it's convenient to include the original for debug purposes. It's + otherwise unused. + """ + + CONTENT_DECODERS = ['gzip', 'deflate'] + REDIRECT_STATUSES = [301, 302, 303, 307, 308] + + def __init__(self, body='', headers=None, status=0, version=0, reason=None, + strict=0, preload_content=True, decode_content=True, + original_response=None, pool=None, connection=None): + + if isinstance(headers, HTTPHeaderDict): + self.headers = headers + else: + self.headers = HTTPHeaderDict(headers) + self.status = status + self.version = version + self.reason = reason + self.strict = strict + self.decode_content = decode_content + + self._decoder = None + self._body = None + self._fp = None + self._original_response = original_response + self._fp_bytes_read = 0 + + if body and isinstance(body, (basestring, binary_type)): + self._body = body + + self._pool = pool + self._connection = connection + + if hasattr(body, 'read'): + self._fp = body + + # Are we using the chunked-style of transfer encoding? + self.chunked = False + self.chunk_left = None + tr_enc = self.headers.get('transfer-encoding', '').lower() + # Don't incur the penalty of creating a list and then discarding it + encodings = (enc.strip() for enc in tr_enc.split(",")) + if "chunked" in encodings: + self.chunked = True + + # If requested, preload the body. + if preload_content and not self._body: + self._body = self.read(decode_content=decode_content) + + def get_redirect_location(self): + """ + Should we redirect and where to? + + :returns: Truthy redirect location string if we got a redirect status + code and valid location. ``None`` if redirect status and no + location. ``False`` if not a redirect status code. + """ + if self.status in self.REDIRECT_STATUSES: + return self.headers.get('location') + + return False + + def release_conn(self): + if not self._pool or not self._connection: + return + + self._pool._put_conn(self._connection) + self._connection = None + + @property + def data(self): + # For backwords-compat with earlier urllib3 0.4 and earlier. + if self._body: + return self._body + + if self._fp: + return self.read(cache_content=True) + + @property + def connection(self): + return self._connection + + def tell(self): + """ + Obtain the number of bytes pulled over the wire so far. May differ from + the amount of content returned by :meth:``HTTPResponse.read`` if bytes + are encoded on the wire (e.g, compressed). + """ + return self._fp_bytes_read + + def _init_decoder(self): + """ + Set-up the _decoder attribute if necessar. + """ + # Note: content-encoding value should be case-insensitive, per RFC 7230 + # Section 3.2 + content_encoding = self.headers.get('content-encoding', '').lower() + if self._decoder is None and content_encoding in self.CONTENT_DECODERS: + self._decoder = _get_decoder(content_encoding) + + def _decode(self, data, decode_content, flush_decoder): + """ + Decode the data passed in and potentially flush the decoder. + """ + try: + if decode_content and self._decoder: + data = self._decoder.decompress(data) + except (IOError, zlib.error) as e: + content_encoding = self.headers.get('content-encoding', '').lower() + raise DecodeError( + "Received response with content-encoding: %s, but " + "failed to decode it." % content_encoding, e) + + if flush_decoder and decode_content: + data += self._flush_decoder() + + return data + + def _flush_decoder(self): + """ + Flushes the decoder. Should only be called if the decoder is actually + being used. + """ + if self._decoder: + buf = self._decoder.decompress(b'') + return buf + self._decoder.flush() + + return b'' + + @contextmanager + def _error_catcher(self): + """ + Catch low-level python exceptions, instead re-raising urllib3 + variants, so that low-level exceptions are not leaked in the + high-level api. + + On exit, release the connection back to the pool. + """ + clean_exit = False + + try: + try: + yield + + except SocketTimeout: + # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but + # there is yet no clean way to get at it from this context. + raise ReadTimeoutError(self._pool, None, 'Read timed out.') + + except BaseSSLError as e: + # FIXME: Is there a better way to differentiate between SSLErrors? + if 'read operation timed out' not in str(e): # Defensive: + # This shouldn't happen but just in case we're missing an edge + # case, let's avoid swallowing SSL errors. + raise + + raise ReadTimeoutError(self._pool, None, 'Read timed out.') + + except (HTTPException, SocketError) as e: + # This includes IncompleteRead. + raise ProtocolError('Connection broken: %r' % e, e) + + # If no exception is thrown, we should avoid cleaning up + # unnecessarily. + clean_exit = True + finally: + # If we didn't terminate cleanly, we need to throw away our + # connection. + if not clean_exit: + # The response may not be closed but we're not going to use it + # anymore so close it now to ensure that the connection is + # released back to the pool. + if self._original_response: + self._original_response.close() + + # Closing the response may not actually be sufficient to close + # everything, so if we have a hold of the connection close that + # too. + if self._connection: + self._connection.close() + + # If we hold the original response but it's closed now, we should + # return the connection back to the pool. + if self._original_response and self._original_response.isclosed(): + self.release_conn() + + def read(self, amt=None, decode_content=None, cache_content=False): + """ + Similar to :meth:`httplib.HTTPResponse.read`, but with two additional + parameters: ``decode_content`` and ``cache_content``. + + :param amt: + How much of the content to read. If specified, caching is skipped + because it doesn't make sense to cache partial content as the full + response. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param cache_content: + If True, will save the returned data such that the same result is + returned despite of the state of the underlying file object. This + is useful if you want the ``.data`` property to continue working + after having ``.read()`` the file object. (Overridden if ``amt`` is + set.) + """ + self._init_decoder() + if decode_content is None: + decode_content = self.decode_content + + if self._fp is None: + return + + flush_decoder = False + data = None + + with self._error_catcher(): + if amt is None: + # cStringIO doesn't like amt=None + data = self._fp.read() + flush_decoder = True + else: + cache_content = False + data = self._fp.read(amt) + if amt != 0 and not data: # Platform-specific: Buggy versions of Python. + # Close the connection when no data is returned + # + # This is redundant to what httplib/http.client _should_ + # already do. However, versions of python released before + # December 15, 2012 (http://bugs.python.org/issue16298) do + # not properly close the connection in all cases. There is + # no harm in redundantly calling close. + self._fp.close() + flush_decoder = True + + if data: + self._fp_bytes_read += len(data) + + data = self._decode(data, decode_content, flush_decoder) + + if cache_content: + self._body = data + + return data + + def stream(self, amt=2**16, decode_content=None): + """ + A generator wrapper for the read() method. A call will block until + ``amt`` bytes have been read from the connection or until the + connection is closed. + + :param amt: + How much of the content to read. The generator will return up to + much data per iteration, but may return less. This is particularly + likely when using compressed data. However, the empty string will + never be returned. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + if self.chunked: + for line in self.read_chunked(amt, decode_content=decode_content): + yield line + else: + while not is_fp_closed(self._fp): + data = self.read(amt=amt, decode_content=decode_content) + + if data: + yield data + + @classmethod + def from_httplib(ResponseCls, r, **response_kw): + """ + Given an :class:`httplib.HTTPResponse` instance ``r``, return a + corresponding :class:`urllib3.response.HTTPResponse` object. + + Remaining parameters are passed to the HTTPResponse constructor, along + with ``original_response=r``. + """ + headers = r.msg + + if not isinstance(headers, HTTPHeaderDict): + if PY3: # Python 3 + headers = HTTPHeaderDict(headers.items()) + else: # Python 2 + headers = HTTPHeaderDict.from_httplib(headers) + + # HTTPResponse objects in Python 3 don't have a .strict attribute + strict = getattr(r, 'strict', 0) + resp = ResponseCls(body=r, + headers=headers, + status=r.status, + version=r.version, + reason=r.reason, + strict=strict, + original_response=r, + **response_kw) + return resp + + # Backwards-compatibility methods for httplib.HTTPResponse + def getheaders(self): + return self.headers + + def getheader(self, name, default=None): + return self.headers.get(name, default) + + # Overrides from io.IOBase + def close(self): + if not self.closed: + self._fp.close() + + if self._connection: + self._connection.close() + + @property + def closed(self): + if self._fp is None: + return True + elif hasattr(self._fp, 'closed'): + return self._fp.closed + elif hasattr(self._fp, 'isclosed'): # Python 2 + return self._fp.isclosed() + else: + return True + + def fileno(self): + if self._fp is None: + raise IOError("HTTPResponse has no file to get a fileno from") + elif hasattr(self._fp, "fileno"): + return self._fp.fileno() + else: + raise IOError("The file-like object this HTTPResponse is wrapped " + "around has no file descriptor") + + def flush(self): + if self._fp is not None and hasattr(self._fp, 'flush'): + return self._fp.flush() + + def readable(self): + # This method is required for `io` module compatibility. + return True + + def readinto(self, b): + # This method is required for `io` module compatibility. + temp = self.read(len(b)) + if len(temp) == 0: + return 0 + else: + b[:len(temp)] = temp + return len(temp) + + def _update_chunk_length(self): + # First, we'll figure out length of a chunk and then + # we'll try to read it from socket. + if self.chunk_left is not None: + return + line = self._fp.fp.readline() + line = line.split(b';', 1)[0] + try: + self.chunk_left = int(line, 16) + except ValueError: + # Invalid chunked protocol response, abort. + self.close() + raise httplib.IncompleteRead(line) + + def _handle_chunk(self, amt): + returned_chunk = None + if amt is None: + chunk = self._fp._safe_read(self.chunk_left) + returned_chunk = chunk + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + elif amt < self.chunk_left: + value = self._fp._safe_read(amt) + self.chunk_left = self.chunk_left - amt + returned_chunk = value + elif amt == self.chunk_left: + value = self._fp._safe_read(amt) + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + returned_chunk = value + else: # amt > self.chunk_left + returned_chunk = self._fp._safe_read(self.chunk_left) + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + return returned_chunk + + def read_chunked(self, amt=None, decode_content=None): + """ + Similar to :meth:`HTTPResponse.read`, but with an additional + parameter: ``decode_content``. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + self._init_decoder() + # FIXME: Rewrite this method and make it a class with a better structured logic. + if not self.chunked: + raise ResponseNotChunked( + "Response is not chunked. " + "Header 'transfer-encoding: chunked' is missing.") + + # Don't bother reading the body of a HEAD request. + if self._original_response and is_response_to_head(self._original_response): + self._original_response.close() + return + + with self._error_catcher(): + while True: + self._update_chunk_length() + if self.chunk_left == 0: + break + chunk = self._handle_chunk(amt) + decoded = self._decode(chunk, decode_content=decode_content, + flush_decoder=False) + if decoded: + yield decoded + + if decode_content: + # On CPython and PyPy, we should never need to flush the + # decoder. However, on Jython we *might* need to, so + # lets defensively do it anyway. + decoded = self._flush_decoder() + if decoded: # Platform-specific: Jython. + yield decoded + + # Chunk content ends with \r\n: discard it. + while True: + line = self._fp.fp.readline() + if not line: + # Some sites may not end with '\r\n'. + break + if line == b'\r\n': + break + + # We read everything; close the "file". + if self._original_response: + self._original_response.close() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/__init__.py new file mode 100644 index 0000000..4778cf9 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/__init__.py @@ -0,0 +1,46 @@ +from __future__ import absolute_import +# For backwards compatibility, provide imports that used to be here. +from .connection import is_connection_dropped +from .request import make_headers +from .response import is_fp_closed +from .ssl_ import ( + SSLContext, + HAS_SNI, + IS_PYOPENSSL, + assert_fingerprint, + resolve_cert_reqs, + resolve_ssl_version, + ssl_wrap_socket, +) +from .timeout import ( + current_time, + Timeout, +) + +from .retry import Retry +from .url import ( + get_host, + parse_url, + split_first, + Url, +) + +__all__ = ( + 'HAS_SNI', + 'IS_PYOPENSSL', + 'SSLContext', + 'Retry', + 'Timeout', + 'Url', + 'assert_fingerprint', + 'current_time', + 'is_connection_dropped', + 'is_fp_closed', + 'get_host', + 'parse_url', + 'make_headers', + 'resolve_cert_reqs', + 'resolve_ssl_version', + 'split_first', + 'ssl_wrap_socket', +) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/connection.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/connection.py new file mode 100644 index 0000000..5e76135 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/connection.py @@ -0,0 +1,144 @@ +from __future__ import absolute_import +import socket +try: + from select import poll, POLLIN +except ImportError: # `poll` doesn't exist on OSX and other platforms + poll = False + try: + from select import select + except ImportError: # `select` doesn't exist on AppEngine. + select = False + + +def is_connection_dropped(conn): # Platform-specific + """ + Returns True if the connection is dropped and should be closed. + + :param conn: + :class:`httplib.HTTPConnection` object. + + Note: For platforms like AppEngine, this will always return ``False`` to + let the platform handle connection recycling transparently for us. + """ + sock = getattr(conn, 'sock', False) + if sock is False: # Platform-specific: AppEngine + return False + if sock is None: # Connection already closed (such as by httplib). + return True + + if not poll: + if not select: # Platform-specific: AppEngine + return False + + try: + return select([sock], [], [], 0.0)[0] + except socket.error: + return True + + # This version is better on platforms that support it. + p = poll() + p.register(sock, POLLIN) + for (fno, ev) in p.poll(0.0): + if fno == sock.fileno(): + # Either data is buffered (bad), or the connection is dropped. + return True + + +# This function is copied from socket.py in the Python 2.7 standard +# library test suite. Added to its signature is only `socket_options`. +# One additional modification is that we avoid binding to IPv6 servers +# discovered in DNS if the system doesn't have IPv6 functionality. +def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + source_address=None, socket_options=None): + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith('['): + host = host.strip('[]') + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + sock.connect(sa) + return sock + + except socket.error as e: + err = e + if sock is not None: + sock.close() + sock = None + + if err is not None: + raise err + + raise socket.error("getaddrinfo returns an empty list") + + +def _set_socket_options(sock, options): + if options is None: + return + + for opt in options: + sock.setsockopt(*opt) + + +def allowed_gai_family(): + """This function is designed to work in the context of + getaddrinfo, where family=socket.AF_UNSPEC is the default and + will perform a DNS search for both IPv6 and IPv4 records.""" + + family = socket.AF_INET + if HAS_IPV6: + family = socket.AF_UNSPEC + return family + + +def _has_ipv6(host): + """ Returns True if the system can bind an IPv6 address. """ + sock = None + has_ipv6 = False + + if socket.has_ipv6: + # has_ipv6 returns true if cPython was compiled with IPv6 support. + # It does not tell us if the system has IPv6 support enabled. To + # determine that we must bind to an IPv6 address. + # https://github.com/shazow/urllib3/pull/611 + # https://bugs.python.org/issue658327 + try: + sock = socket.socket(socket.AF_INET6) + sock.bind((host, 0)) + has_ipv6 = True + except Exception: + pass + + if sock: + sock.close() + return has_ipv6 + +HAS_IPV6 = _has_ipv6('::1') diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/request.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/request.py new file mode 100644 index 0000000..7377931 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/request.py @@ -0,0 +1,72 @@ +from __future__ import absolute_import +from base64 import b64encode + +from ..packages.six import b + +ACCEPT_ENCODING = 'gzip,deflate' + + +def make_headers(keep_alive=None, accept_encoding=None, user_agent=None, + basic_auth=None, proxy_basic_auth=None, disable_cache=None): + """ + Shortcuts for generating request headers. + + :param keep_alive: + If ``True``, adds 'connection: keep-alive' header. + + :param accept_encoding: + Can be a boolean, list, or string. + ``True`` translates to 'gzip,deflate'. + List will get joined by comma. + String will be used as provided. + + :param user_agent: + String representing the user-agent you want, such as + "python-urllib3/0.6" + + :param basic_auth: + Colon-separated username:password string for 'authorization: basic ...' + auth header. + + :param proxy_basic_auth: + Colon-separated username:password string for 'proxy-authorization: basic ...' + auth header. + + :param disable_cache: + If ``True``, adds 'cache-control: no-cache' header. + + Example:: + + >>> make_headers(keep_alive=True, user_agent="Batman/1.0") + {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} + >>> make_headers(accept_encoding=True) + {'accept-encoding': 'gzip,deflate'} + """ + headers = {} + if accept_encoding: + if isinstance(accept_encoding, str): + pass + elif isinstance(accept_encoding, list): + accept_encoding = ','.join(accept_encoding) + else: + accept_encoding = ACCEPT_ENCODING + headers['accept-encoding'] = accept_encoding + + if user_agent: + headers['user-agent'] = user_agent + + if keep_alive: + headers['connection'] = 'keep-alive' + + if basic_auth: + headers['authorization'] = 'Basic ' + \ + b64encode(b(basic_auth)).decode('utf-8') + + if proxy_basic_auth: + headers['proxy-authorization'] = 'Basic ' + \ + b64encode(b(proxy_basic_auth)).decode('utf-8') + + if disable_cache: + headers['cache-control'] = 'no-cache' + + return headers diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/response.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/response.py new file mode 100644 index 0000000..0b5c75c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/response.py @@ -0,0 +1,74 @@ +from __future__ import absolute_import +from ..packages.six.moves import http_client as httplib + +from ..exceptions import HeaderParsingError + + +def is_fp_closed(obj): + """ + Checks whether a given file-like object is closed. + + :param obj: + The file-like object to check. + """ + + try: + # Check via the official file-like-object way. + return obj.closed + except AttributeError: + pass + + try: + # Check if the object is a container for another file-like object that + # gets released on exhaustion (e.g. HTTPResponse). + return obj.fp is None + except AttributeError: + pass + + raise ValueError("Unable to determine whether fp is closed.") + + +def assert_header_parsing(headers): + """ + Asserts whether all headers have been successfully parsed. + Extracts encountered errors from the result of parsing headers. + + Only works on Python 3. + + :param headers: Headers to verify. + :type headers: `httplib.HTTPMessage`. + + :raises urllib3.exceptions.HeaderParsingError: + If parsing errors are found. + """ + + # This will fail silently if we pass in the wrong kind of parameter. + # To make debugging easier add an explicit check. + if not isinstance(headers, httplib.HTTPMessage): + raise TypeError('expected httplib.Message, got {0}.'.format( + type(headers))) + + defects = getattr(headers, 'defects', None) + get_payload = getattr(headers, 'get_payload', None) + + unparsed_data = None + if get_payload: # Platform-specific: Python 3. + unparsed_data = get_payload() + + if defects or unparsed_data: + raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) + + +def is_response_to_head(response): + """ + Checks whether the request of a response has been a HEAD-request. + Handles the quirks of AppEngine. + + :param conn: + :type conn: :class:`httplib.HTTPResponse` + """ + # FIXME: Can we do this somehow without accessing private httplib _method? + method = response._method + if isinstance(method, int): # Platform-specific: Appengine + return method == 3 + return method.upper() == 'HEAD' diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/retry.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/retry.py new file mode 100644 index 0000000..d379833 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/retry.py @@ -0,0 +1,300 @@ +from __future__ import absolute_import +import time +import logging + +from ..exceptions import ( + ConnectTimeoutError, + MaxRetryError, + ProtocolError, + ReadTimeoutError, + ResponseError, +) +from ..packages import six + + +log = logging.getLogger(__name__) + + +class Retry(object): + """ Retry configuration. + + Each retry attempt will create a new Retry object with updated values, so + they can be safely reused. + + Retries can be defined as a default for a pool:: + + retries = Retry(connect=5, read=2, redirect=5) + http = PoolManager(retries=retries) + response = http.request('GET', 'http://example.com/') + + Or per-request (which overrides the default for the pool):: + + response = http.request('GET', 'http://example.com/', retries=Retry(10)) + + Retries can be disabled by passing ``False``:: + + response = http.request('GET', 'http://example.com/', retries=False) + + Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless + retries are disabled, in which case the causing exception will be raised. + + :param int total: + Total number of retries to allow. Takes precedence over other counts. + + Set to ``None`` to remove this constraint and fall back on other + counts. It's a good idea to set this to some sensibly-high value to + account for unexpected edge cases and avoid infinite retry loops. + + Set to ``0`` to fail on the first retry. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param int connect: + How many connection-related errors to retry on. + + These are errors raised before the request is sent to the remote server, + which we assume has not triggered the server to process the request. + + Set to ``0`` to fail on the first retry of this type. + + :param int read: + How many times to retry on read errors. + + These errors are raised after the request was sent to the server, so the + request may have side-effects. + + Set to ``0`` to fail on the first retry of this type. + + :param int redirect: + How many redirects to perform. Limit this to avoid infinite redirect + loops. + + A redirect is a HTTP response with a status code 301, 302, 303, 307 or + 308. + + Set to ``0`` to fail on the first retry of this type. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param iterable method_whitelist: + Set of uppercased HTTP method verbs that we should retry on. + + By default, we only retry on methods which are considered to be + idempotent (multiple requests with the same parameters end with the + same state). See :attr:`Retry.DEFAULT_METHOD_WHITELIST`. + + Set to a ``False`` value to retry on any verb. + + :param iterable status_forcelist: + A set of integer HTTP status codes that we should force a retry on. + A retry is initiated if the request method is in ``method_whitelist`` + and the response status code is in ``status_forcelist``. + + By default, this is disabled with ``None``. + + :param float backoff_factor: + A backoff factor to apply between attempts after the second try + (most errors are resolved immediately by a second try without a + delay). urllib3 will sleep for:: + + {backoff factor} * (2 ^ ({number of total retries} - 1)) + + seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep + for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer + than :attr:`Retry.BACKOFF_MAX`. + + By default, backoff is disabled (set to 0). + + :param bool raise_on_redirect: Whether, if the number of redirects is + exhausted, to raise a MaxRetryError, or to return a response with a + response code in the 3xx range. + + :param bool raise_on_status: Similar meaning to ``raise_on_redirect``: + whether we should raise an exception, or return a response, + if status falls in ``status_forcelist`` range and retries have + been exhausted. + """ + + DEFAULT_METHOD_WHITELIST = frozenset([ + 'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE']) + + #: Maximum backoff time. + BACKOFF_MAX = 120 + + def __init__(self, total=10, connect=None, read=None, redirect=None, + method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None, + backoff_factor=0, raise_on_redirect=True, raise_on_status=True, + _observed_errors=0): + + self.total = total + self.connect = connect + self.read = read + + if redirect is False or total is False: + redirect = 0 + raise_on_redirect = False + + self.redirect = redirect + self.status_forcelist = status_forcelist or set() + self.method_whitelist = method_whitelist + self.backoff_factor = backoff_factor + self.raise_on_redirect = raise_on_redirect + self.raise_on_status = raise_on_status + self._observed_errors = _observed_errors # TODO: use .history instead? + + def new(self, **kw): + params = dict( + total=self.total, + connect=self.connect, read=self.read, redirect=self.redirect, + method_whitelist=self.method_whitelist, + status_forcelist=self.status_forcelist, + backoff_factor=self.backoff_factor, + raise_on_redirect=self.raise_on_redirect, + raise_on_status=self.raise_on_status, + _observed_errors=self._observed_errors, + ) + params.update(kw) + return type(self)(**params) + + @classmethod + def from_int(cls, retries, redirect=True, default=None): + """ Backwards-compatibility for the old retries format.""" + if retries is None: + retries = default if default is not None else cls.DEFAULT + + if isinstance(retries, Retry): + return retries + + redirect = bool(redirect) and None + new_retries = cls(retries, redirect=redirect) + log.debug("Converted retries value: %r -> %r", retries, new_retries) + return new_retries + + def get_backoff_time(self): + """ Formula for computing the current backoff + + :rtype: float + """ + if self._observed_errors <= 1: + return 0 + + backoff_value = self.backoff_factor * (2 ** (self._observed_errors - 1)) + return min(self.BACKOFF_MAX, backoff_value) + + def sleep(self): + """ Sleep between retry attempts using an exponential backoff. + + By default, the backoff factor is 0 and this method will return + immediately. + """ + backoff = self.get_backoff_time() + if backoff <= 0: + return + time.sleep(backoff) + + def _is_connection_error(self, err): + """ Errors when we're fairly sure that the server did not receive the + request, so it should be safe to retry. + """ + return isinstance(err, ConnectTimeoutError) + + def _is_read_error(self, err): + """ Errors that occur after the request has been started, so we should + assume that the server began processing it. + """ + return isinstance(err, (ReadTimeoutError, ProtocolError)) + + def is_forced_retry(self, method, status_code): + """ Is this method/status code retryable? (Based on method/codes whitelists) + """ + if self.method_whitelist and method.upper() not in self.method_whitelist: + return False + + return self.status_forcelist and status_code in self.status_forcelist + + def is_exhausted(self): + """ Are we out of retries? """ + retry_counts = (self.total, self.connect, self.read, self.redirect) + retry_counts = list(filter(None, retry_counts)) + if not retry_counts: + return False + + return min(retry_counts) < 0 + + def increment(self, method=None, url=None, response=None, error=None, + _pool=None, _stacktrace=None): + """ Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.HTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise six.reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + _observed_errors = self._observed_errors + connect = self.connect + read = self.read + redirect = self.redirect + cause = 'unknown' + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise six.reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + _observed_errors += 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False: + raise six.reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + _observed_errors += 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = 'too many redirects' + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and a the given method is in the whitelist + _observed_errors += 1 + cause = ResponseError.GENERIC_ERROR + if response and response.status: + cause = ResponseError.SPECIFIC_ERROR.format( + status_code=response.status) + + new_retry = self.new( + total=total, + connect=connect, read=read, redirect=redirect, + _observed_errors=_observed_errors) + + if new_retry.is_exhausted(): + raise MaxRetryError(_pool, url, error or ResponseError(cause)) + + log.debug("Incremented Retry for (url='%s'): %r", url, new_retry) + + return new_retry + + def __repr__(self): + return ('{cls.__name__}(total={self.total}, connect={self.connect}, ' + 'read={self.read}, redirect={self.redirect})').format( + cls=type(self), self=self) + + +# For backwards compatibility (equivalent to pre-v1.9): +Retry.DEFAULT = Retry(3) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/ssl_.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/ssl_.py new file mode 100644 index 0000000..4a64d7e --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/ssl_.py @@ -0,0 +1,320 @@ +from __future__ import absolute_import +import errno +import warnings +import hmac + +from binascii import hexlify, unhexlify +from hashlib import md5, sha1, sha256 + +from ..exceptions import SSLError, InsecurePlatformWarning, SNIMissingWarning + + +SSLContext = None +HAS_SNI = False +create_default_context = None +IS_PYOPENSSL = False + +# Maps the length of a digest to a possible hash function producing this digest +HASHFUNC_MAP = { + 32: md5, + 40: sha1, + 64: sha256, +} + + +def _const_compare_digest_backport(a, b): + """ + Compare two digests of equal length in constant time. + + The digests must be of type str/bytes. + Returns True if the digests match, and False otherwise. + """ + result = abs(len(a) - len(b)) + for l, r in zip(bytearray(a), bytearray(b)): + result |= l ^ r + return result == 0 + + +_const_compare_digest = getattr(hmac, 'compare_digest', + _const_compare_digest_backport) + + +try: # Test for SSL features + import ssl + from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23 + from ssl import HAS_SNI # Has SNI? +except ImportError: + pass + + +try: + from ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION +except ImportError: + OP_NO_SSLv2, OP_NO_SSLv3 = 0x1000000, 0x2000000 + OP_NO_COMPRESSION = 0x20000 + +# A secure default. +# Sources for more information on TLS ciphers: +# +# - https://wiki.mozilla.org/Security/Server_Side_TLS +# - https://www.ssllabs.com/projects/best-practices/index.html +# - https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ +# +# The general intent is: +# - Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE), +# - prefer ECDHE over DHE for better performance, +# - prefer any AES-GCM over any AES-CBC for better performance and security, +# - use 3DES as fallback which is secure but slow, +# - disable NULL authentication, MD5 MACs and DSS for security reasons. +DEFAULT_CIPHERS = ( + 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:' + 'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:' + '!eNULL:!MD5' +) + +try: + from ssl import SSLContext # Modern SSL? +except ImportError: + import sys + + class SSLContext(object): # Platform-specific: Python 2 & 3.1 + supports_set_ciphers = ((2, 7) <= sys.version_info < (3,) or + (3, 2) <= sys.version_info) + + def __init__(self, protocol_version): + self.protocol = protocol_version + # Use default values from a real SSLContext + self.check_hostname = False + self.verify_mode = ssl.CERT_NONE + self.ca_certs = None + self.options = 0 + self.certfile = None + self.keyfile = None + self.ciphers = None + + def load_cert_chain(self, certfile, keyfile): + self.certfile = certfile + self.keyfile = keyfile + + def load_verify_locations(self, cafile=None, capath=None): + self.ca_certs = cafile + + if capath is not None: + raise SSLError("CA directories not supported in older Pythons") + + def set_ciphers(self, cipher_suite): + if not self.supports_set_ciphers: + raise TypeError( + 'Your version of Python does not support setting ' + 'a custom cipher suite. Please upgrade to Python ' + '2.7, 3.2, or later if you need this functionality.' + ) + self.ciphers = cipher_suite + + def wrap_socket(self, socket, server_hostname=None, server_side=False): + warnings.warn( + 'A true SSLContext object is not available. This prevents ' + 'urllib3 from configuring SSL appropriately and may cause ' + 'certain SSL connections to fail. You can upgrade to a newer ' + 'version of Python to solve this. For more information, see ' + 'https://urllib3.readthedocs.io/en/latest/security.html' + '#insecureplatformwarning.', + InsecurePlatformWarning + ) + kwargs = { + 'keyfile': self.keyfile, + 'certfile': self.certfile, + 'ca_certs': self.ca_certs, + 'cert_reqs': self.verify_mode, + 'ssl_version': self.protocol, + 'server_side': server_side, + } + if self.supports_set_ciphers: # Platform-specific: Python 2.7+ + return wrap_socket(socket, ciphers=self.ciphers, **kwargs) + else: # Platform-specific: Python 2.6 + return wrap_socket(socket, **kwargs) + + +def assert_fingerprint(cert, fingerprint): + """ + Checks if given fingerprint matches the supplied certificate. + + :param cert: + Certificate as bytes object. + :param fingerprint: + Fingerprint as string of hexdigits, can be interspersed by colons. + """ + + fingerprint = fingerprint.replace(':', '').lower() + digest_length = len(fingerprint) + hashfunc = HASHFUNC_MAP.get(digest_length) + if not hashfunc: + raise SSLError( + 'Fingerprint of invalid length: {0}'.format(fingerprint)) + + # We need encode() here for py32; works on py2 and p33. + fingerprint_bytes = unhexlify(fingerprint.encode()) + + cert_digest = hashfunc(cert).digest() + + if not _const_compare_digest(cert_digest, fingerprint_bytes): + raise SSLError('Fingerprints did not match. Expected "{0}", got "{1}".' + .format(fingerprint, hexlify(cert_digest))) + + +def resolve_cert_reqs(candidate): + """ + Resolves the argument to a numeric constant, which can be passed to + the wrap_socket function/method from the ssl module. + Defaults to :data:`ssl.CERT_NONE`. + If given a string it is assumed to be the name of the constant in the + :mod:`ssl` module or its abbrevation. + (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. + If it's neither `None` nor a string we assume it is already the numeric + constant which can directly be passed to wrap_socket. + """ + if candidate is None: + return CERT_NONE + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, 'CERT_' + candidate) + return res + + return candidate + + +def resolve_ssl_version(candidate): + """ + like resolve_cert_reqs + """ + if candidate is None: + return PROTOCOL_SSLv23 + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, 'PROTOCOL_' + candidate) + return res + + return candidate + + +def create_urllib3_context(ssl_version=None, cert_reqs=None, + options=None, ciphers=None): + """All arguments have the same meaning as ``ssl_wrap_socket``. + + By default, this function does a lot of the same work that + ``ssl.create_default_context`` does on Python 3.4+. It: + + - Disables SSLv2, SSLv3, and compression + - Sets a restricted set of server ciphers + + If you wish to enable SSLv3, you can do:: + + from urllib3.util import ssl_ + context = ssl_.create_urllib3_context() + context.options &= ~ssl_.OP_NO_SSLv3 + + You can do the same to enable compression (substituting ``COMPRESSION`` + for ``SSLv3`` in the last line above). + + :param ssl_version: + The desired protocol version to use. This will default to + PROTOCOL_SSLv23 which will negotiate the highest protocol that both + the server and your installation of OpenSSL support. + :param cert_reqs: + Whether to require the certificate verification. This defaults to + ``ssl.CERT_REQUIRED``. + :param options: + Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``, + ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``. + :param ciphers: + Which cipher suites to allow the server to select. + :returns: + Constructed SSLContext object with specified options + :rtype: SSLContext + """ + context = SSLContext(ssl_version or ssl.PROTOCOL_SSLv23) + + # Setting the default here, as we may have no ssl module on import + cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs + + if options is None: + options = 0 + # SSLv2 is easily broken and is considered harmful and dangerous + options |= OP_NO_SSLv2 + # SSLv3 has several problems and is now dangerous + options |= OP_NO_SSLv3 + # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ + # (issue #309) + options |= OP_NO_COMPRESSION + + context.options |= options + + if getattr(context, 'supports_set_ciphers', True): # Platform-specific: Python 2.6 + context.set_ciphers(ciphers or DEFAULT_CIPHERS) + + context.verify_mode = cert_reqs + if getattr(context, 'check_hostname', None) is not None: # Platform-specific: Python 3.2 + # We do our own verification, including fingerprints and alternative + # hostnames. So disable it here + context.check_hostname = False + return context + + +def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, + ca_certs=None, server_hostname=None, + ssl_version=None, ciphers=None, ssl_context=None, + ca_cert_dir=None): + """ + All arguments except for server_hostname, ssl_context, and ca_cert_dir have + the same meaning as they do when using :func:`ssl.wrap_socket`. + + :param server_hostname: + When SNI is supported, the expected hostname of the certificate + :param ssl_context: + A pre-made :class:`SSLContext` object. If none is provided, one will + be created using :func:`create_urllib3_context`. + :param ciphers: + A string of ciphers we wish the client to support. This is not + supported on Python 2.6 as the ssl module does not support it. + :param ca_cert_dir: + A directory containing CA certificates in multiple separate files, as + supported by OpenSSL's -CApath flag or the capath argument to + SSLContext.load_verify_locations(). + """ + context = ssl_context + if context is None: + context = create_urllib3_context(ssl_version, cert_reqs, + ciphers=ciphers) + + if ca_certs or ca_cert_dir: + try: + context.load_verify_locations(ca_certs, ca_cert_dir) + except IOError as e: # Platform-specific: Python 2.6, 2.7, 3.2 + raise SSLError(e) + # Py33 raises FileNotFoundError which subclasses OSError + # These are not equivalent unless we check the errno attribute + except OSError as e: # Platform-specific: Python 3.3 and beyond + if e.errno == errno.ENOENT: + raise SSLError(e) + raise + + if certfile: + context.load_cert_chain(certfile, keyfile) + if HAS_SNI: # Platform-specific: OpenSSL with enabled SNI + return context.wrap_socket(sock, server_hostname=server_hostname) + + warnings.warn( + 'An HTTPS request has been made, but the SNI (Subject Name ' + 'Indication) extension to TLS is not available on this platform. ' + 'This may cause the server to present an incorrect TLS ' + 'certificate, which can cause validation failures. You can upgrade to ' + 'a newer version of Python to solve this. For more information, see ' + 'https://urllib3.readthedocs.io/en/latest/security.html' + '#snimissingwarning.', + SNIMissingWarning + ) + return context.wrap_socket(sock) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/timeout.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/timeout.py new file mode 100644 index 0000000..ff62f47 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/timeout.py @@ -0,0 +1,242 @@ +from __future__ import absolute_import +# The default socket timeout, used by httplib to indicate that no timeout was +# specified by the user +from socket import _GLOBAL_DEFAULT_TIMEOUT +import time + +from ..exceptions import TimeoutStateError + +# A sentinel value to indicate that no timeout was specified by the user in +# urllib3 +_Default = object() + + +def current_time(): + """ + Retrieve the current time. This function is mocked out in unit testing. + """ + return time.time() + + +class Timeout(object): + """ Timeout configuration. + + Timeouts can be defined as a default for a pool:: + + timeout = Timeout(connect=2.0, read=7.0) + http = PoolManager(timeout=timeout) + response = http.request('GET', 'http://example.com/') + + Or per-request (which overrides the default for the pool):: + + response = http.request('GET', 'http://example.com/', timeout=Timeout(10)) + + Timeouts can be disabled by setting all the parameters to ``None``:: + + no_timeout = Timeout(connect=None, read=None) + response = http.request('GET', 'http://example.com/, timeout=no_timeout) + + + :param total: + This combines the connect and read timeouts into one; the read timeout + will be set to the time leftover from the connect attempt. In the + event that both a connect timeout and a total are specified, or a read + timeout and a total are specified, the shorter timeout will be applied. + + Defaults to None. + + :type total: integer, float, or None + + :param connect: + The maximum amount of time to wait for a connection attempt to a server + to succeed. Omitting the parameter will default the connect timeout to + the system default, probably `the global default timeout in socket.py + <http://hg.python.org/cpython/file/603b4d593758/Lib/socket.py#l535>`_. + None will set an infinite timeout for connection attempts. + + :type connect: integer, float, or None + + :param read: + The maximum amount of time to wait between consecutive + read operations for a response from the server. Omitting + the parameter will default the read timeout to the system + default, probably `the global default timeout in socket.py + <http://hg.python.org/cpython/file/603b4d593758/Lib/socket.py#l535>`_. + None will set an infinite timeout. + + :type read: integer, float, or None + + .. note:: + + Many factors can affect the total amount of time for urllib3 to return + an HTTP response. + + For example, Python's DNS resolver does not obey the timeout specified + on the socket. Other factors that can affect total request time include + high CPU load, high swap, the program running at a low priority level, + or other behaviors. + + In addition, the read and total timeouts only measure the time between + read operations on the socket connecting the client and the server, + not the total amount of time for the request to return a complete + response. For most requests, the timeout is raised because the server + has not sent the first byte in the specified time. This is not always + the case; if a server streams one byte every fifteen seconds, a timeout + of 20 seconds will not trigger, even though the request will take + several minutes to complete. + + If your goal is to cut off any request after a set amount of wall clock + time, consider having a second "watcher" thread to cut off a slow + request. + """ + + #: A sentinel object representing the default timeout value + DEFAULT_TIMEOUT = _GLOBAL_DEFAULT_TIMEOUT + + def __init__(self, total=None, connect=_Default, read=_Default): + self._connect = self._validate_timeout(connect, 'connect') + self._read = self._validate_timeout(read, 'read') + self.total = self._validate_timeout(total, 'total') + self._start_connect = None + + def __str__(self): + return '%s(connect=%r, read=%r, total=%r)' % ( + type(self).__name__, self._connect, self._read, self.total) + + @classmethod + def _validate_timeout(cls, value, name): + """ Check that a timeout attribute is valid. + + :param value: The timeout value to validate + :param name: The name of the timeout attribute to validate. This is + used to specify in error messages. + :return: The validated and casted version of the given value. + :raises ValueError: If the type is not an integer or a float, or if it + is a numeric value less than zero. + """ + if value is _Default: + return cls.DEFAULT_TIMEOUT + + if value is None or value is cls.DEFAULT_TIMEOUT: + return value + + try: + float(value) + except (TypeError, ValueError): + raise ValueError("Timeout value %s was %s, but it must be an " + "int or float." % (name, value)) + + try: + if value < 0: + raise ValueError("Attempted to set %s timeout to %s, but the " + "timeout cannot be set to a value less " + "than 0." % (name, value)) + except TypeError: # Python 3 + raise ValueError("Timeout value %s was %s, but it must be an " + "int or float." % (name, value)) + + return value + + @classmethod + def from_float(cls, timeout): + """ Create a new Timeout from a legacy timeout value. + + The timeout value used by httplib.py sets the same timeout on the + connect(), and recv() socket requests. This creates a :class:`Timeout` + object that sets the individual timeouts to the ``timeout`` value + passed to this function. + + :param timeout: The legacy timeout value. + :type timeout: integer, float, sentinel default object, or None + :return: Timeout object + :rtype: :class:`Timeout` + """ + return Timeout(read=timeout, connect=timeout) + + def clone(self): + """ Create a copy of the timeout object + + Timeout properties are stored per-pool but each request needs a fresh + Timeout object to ensure each one has its own start/stop configured. + + :return: a copy of the timeout object + :rtype: :class:`Timeout` + """ + # We can't use copy.deepcopy because that will also create a new object + # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to + # detect the user default. + return Timeout(connect=self._connect, read=self._read, + total=self.total) + + def start_connect(self): + """ Start the timeout clock, used during a connect() attempt + + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to start a timer that has been started already. + """ + if self._start_connect is not None: + raise TimeoutStateError("Timeout timer has already been started.") + self._start_connect = current_time() + return self._start_connect + + def get_connect_duration(self): + """ Gets the time elapsed since the call to :meth:`start_connect`. + + :return: Elapsed time. + :rtype: float + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to get duration for a timer that hasn't been started. + """ + if self._start_connect is None: + raise TimeoutStateError("Can't get connect duration for timer " + "that has not started.") + return current_time() - self._start_connect + + @property + def connect_timeout(self): + """ Get the value to use when setting a connection timeout. + + This will be a positive float or integer, the value None + (never timeout), or the default system timeout. + + :return: Connect timeout. + :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None + """ + if self.total is None: + return self._connect + + if self._connect is None or self._connect is self.DEFAULT_TIMEOUT: + return self.total + + return min(self._connect, self.total) + + @property + def read_timeout(self): + """ Get the value for the read timeout. + + This assumes some time has elapsed in the connection timeout and + computes the read timeout appropriately. + + If self.total is set, the read timeout is dependent on the amount of + time taken by the connect timeout. If the connection time has not been + established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be + raised. + + :return: Value to use for the read timeout. + :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None + :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` + has not yet been called on this object. + """ + if (self.total is not None and + self.total is not self.DEFAULT_TIMEOUT and + self._read is not None and + self._read is not self.DEFAULT_TIMEOUT): + # In case the connect timeout has not yet been established. + if self._start_connect is None: + return self._read + return max(0, min(self.total - self.get_connect_duration(), + self._read)) + elif self.total is not None and self.total is not self.DEFAULT_TIMEOUT: + return max(0, self.total - self.get_connect_duration()) + else: + return self._read diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/url.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/url.py new file mode 100644 index 0000000..e996204 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/packages/urllib3/util/url.py @@ -0,0 +1,217 @@ +from __future__ import absolute_import +from collections import namedtuple + +from ..exceptions import LocationParseError + + +url_attrs = ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'] + + +class Url(namedtuple('Url', url_attrs)): + """ + Datastructure for representing an HTTP URL. Used as a return value for + :func:`parse_url`. + """ + slots = () + + def __new__(cls, scheme=None, auth=None, host=None, port=None, path=None, + query=None, fragment=None): + if path and not path.startswith('/'): + path = '/' + path + return super(Url, cls).__new__(cls, scheme, auth, host, port, path, + query, fragment) + + @property + def hostname(self): + """For backwards-compatibility with urlparse. We're nice like that.""" + return self.host + + @property + def request_uri(self): + """Absolute path including the query string.""" + uri = self.path or '/' + + if self.query is not None: + uri += '?' + self.query + + return uri + + @property + def netloc(self): + """Network location including host and port""" + if self.port: + return '%s:%d' % (self.host, self.port) + return self.host + + @property + def url(self): + """ + Convert self into a url + + This function should more or less round-trip with :func:`.parse_url`. The + returned url may not be exactly the same as the url inputted to + :func:`.parse_url`, but it should be equivalent by the RFC (e.g., urls + with a blank port will have : removed). + + Example: :: + + >>> U = parse_url('http://google.com/mail/') + >>> U.url + 'http://google.com/mail/' + >>> Url('http', 'username:password', 'host.com', 80, + ... '/path', 'query', 'fragment').url + 'http://username:password@host.com:80/path?query#fragment' + """ + scheme, auth, host, port, path, query, fragment = self + url = '' + + # We use "is not None" we want things to happen with empty strings (or 0 port) + if scheme is not None: + url += scheme + '://' + if auth is not None: + url += auth + '@' + if host is not None: + url += host + if port is not None: + url += ':' + str(port) + if path is not None: + url += path + if query is not None: + url += '?' + query + if fragment is not None: + url += '#' + fragment + + return url + + def __str__(self): + return self.url + + +def split_first(s, delims): + """ + Given a string and an iterable of delimiters, split on the first found + delimiter. Return two split parts and the matched delimiter. + + If not found, then the first part is the full input string. + + Example:: + + >>> split_first('foo/bar?baz', '?/=') + ('foo', 'bar?baz', '/') + >>> split_first('foo/bar?baz', '123') + ('foo/bar?baz', '', None) + + Scales linearly with number of delims. Not ideal for large number of delims. + """ + min_idx = None + min_delim = None + for d in delims: + idx = s.find(d) + if idx < 0: + continue + + if min_idx is None or idx < min_idx: + min_idx = idx + min_delim = d + + if min_idx is None or min_idx < 0: + return s, '', None + + return s[:min_idx], s[min_idx + 1:], min_delim + + +def parse_url(url): + """ + Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is + performed to parse incomplete urls. Fields not provided will be None. + + Partly backwards-compatible with :mod:`urlparse`. + + Example:: + + >>> parse_url('http://google.com/mail/') + Url(scheme='http', host='google.com', port=None, path='/mail/', ...) + >>> parse_url('google.com:80') + Url(scheme=None, host='google.com', port=80, path=None, ...) + >>> parse_url('/foo?bar') + Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) + """ + + # While this code has overlap with stdlib's urlparse, it is much + # simplified for our needs and less annoying. + # Additionally, this implementations does silly things to be optimal + # on CPython. + + if not url: + # Empty + return Url() + + scheme = None + auth = None + host = None + port = None + path = None + fragment = None + query = None + + # Scheme + if '://' in url: + scheme, url = url.split('://', 1) + + # Find the earliest Authority Terminator + # (http://tools.ietf.org/html/rfc3986#section-3.2) + url, path_, delim = split_first(url, ['/', '?', '#']) + + if delim: + # Reassemble the path + path = delim + path_ + + # Auth + if '@' in url: + # Last '@' denotes end of auth part + auth, url = url.rsplit('@', 1) + + # IPv6 + if url and url[0] == '[': + host, url = url.split(']', 1) + host += ']' + + # Port + if ':' in url: + _host, port = url.split(':', 1) + + if not host: + host = _host + + if port: + # If given, ports must be integers. + if not port.isdigit(): + raise LocationParseError(url) + port = int(port) + else: + # Blank ports are cool, too. (rfc3986#section-3.2.3) + port = None + + elif not host and url: + host = url + + if not path: + return Url(scheme, auth, host, port, path, query, fragment) + + # Fragment + if '#' in path: + path, fragment = path.split('#', 1) + + # Query + if '?' in path: + path, query = path.split('?', 1) + + return Url(scheme, auth, host, port, path, query, fragment) + + +def get_host(url): + """ + Deprecated. Use :func:`.parse_url` instead. + """ + p = parse_url(url) + return p.scheme or 'http', p.hostname, p.port diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/sessions.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/sessions.py new file mode 100644 index 0000000..bcbcc88 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/sessions.py @@ -0,0 +1,712 @@ +# -*- coding: utf-8 -*- + +""" +requests.session +~~~~~~~~~~~~~~~~ + +This module provides a Session object to manage and persist settings across +requests (cookies, auth, proxies). +""" +import os +from collections import Mapping +from datetime import datetime + +from .auth import _basic_auth_str +from .compat import cookielib, OrderedDict, urljoin, urlparse +from .cookies import ( + cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies) +from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT +from .hooks import default_hooks, dispatch_hook +from .utils import to_key_val_list, default_headers, to_native_string +from .exceptions import ( + TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError) +from .packages.urllib3._collections import RecentlyUsedContainer +from .structures import CaseInsensitiveDict + +from .adapters import HTTPAdapter + +from .utils import ( + requote_uri, get_environ_proxies, get_netrc_auth, should_bypass_proxies, + get_auth_from_url +) + +from .status_codes import codes + +# formerly defined here, reexposed here for backward compatibility +from .models import REDIRECT_STATI + +REDIRECT_CACHE_SIZE = 1000 + + +def merge_setting(request_setting, session_setting, dict_class=OrderedDict): + """Determines appropriate setting for a given request, taking into account + the explicit setting on that request, and the setting in the session. If a + setting is a dictionary, they will be merged together using `dict_class` + """ + + if session_setting is None: + return request_setting + + if request_setting is None: + return session_setting + + # Bypass if not a dictionary (e.g. verify) + if not ( + isinstance(session_setting, Mapping) and + isinstance(request_setting, Mapping) + ): + return request_setting + + merged_setting = dict_class(to_key_val_list(session_setting)) + merged_setting.update(to_key_val_list(request_setting)) + + # Remove keys that are set to None. Extract keys first to avoid altering + # the dictionary during iteration. + none_keys = [k for (k, v) in merged_setting.items() if v is None] + for key in none_keys: + del merged_setting[key] + + return merged_setting + + +def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict): + """Properly merges both requests and session hooks. + + This is necessary because when request_hooks == {'response': []}, the + merge breaks Session hooks entirely. + """ + if session_hooks is None or session_hooks.get('response') == []: + return request_hooks + + if request_hooks is None or request_hooks.get('response') == []: + return session_hooks + + return merge_setting(request_hooks, session_hooks, dict_class) + + +class SessionRedirectMixin(object): + def resolve_redirects(self, resp, req, stream=False, timeout=None, + verify=True, cert=None, proxies=None, **adapter_kwargs): + """Receives a Response. Returns a generator of Responses.""" + + i = 0 + hist = [] # keep track of history + + while resp.is_redirect: + prepared_request = req.copy() + + if i > 0: + # Update history and keep track of redirects. + hist.append(resp) + new_hist = list(hist) + resp.history = new_hist + + try: + resp.content # Consume socket so it can be released + except (ChunkedEncodingError, ContentDecodingError, RuntimeError): + resp.raw.read(decode_content=False) + + if i >= self.max_redirects: + raise TooManyRedirects('Exceeded %s redirects.' % self.max_redirects, response=resp) + + # Release the connection back into the pool. + resp.close() + + url = resp.headers['location'] + + # Handle redirection without scheme (see: RFC 1808 Section 4) + if url.startswith('//'): + parsed_rurl = urlparse(resp.url) + url = '%s:%s' % (parsed_rurl.scheme, url) + + # The scheme should be lower case... + parsed = urlparse(url) + url = parsed.geturl() + + # Facilitate relative 'location' headers, as allowed by RFC 7231. + # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') + # Compliant with RFC3986, we percent encode the url. + if not parsed.netloc: + url = urljoin(resp.url, requote_uri(url)) + else: + url = requote_uri(url) + + prepared_request.url = to_native_string(url) + # Cache the url, unless it redirects to itself. + if resp.is_permanent_redirect and req.url != prepared_request.url: + self.redirect_cache[req.url] = prepared_request.url + + self.rebuild_method(prepared_request, resp) + + # https://github.com/kennethreitz/requests/issues/1084 + if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect): + # https://github.com/kennethreitz/requests/issues/3490 + purged_headers = ('Content-Length', 'Content-Type', 'Transfer-Encoding') + for header in purged_headers: + prepared_request.headers.pop(header, None) + prepared_request.body = None + + headers = prepared_request.headers + try: + del headers['Cookie'] + except KeyError: + pass + + # Extract any cookies sent on the response to the cookiejar + # in the new request. Because we've mutated our copied prepared + # request, use the old one that we haven't yet touched. + extract_cookies_to_jar(prepared_request._cookies, req, resp.raw) + prepared_request._cookies.update(self.cookies) + prepared_request.prepare_cookies(prepared_request._cookies) + + # Rebuild auth and proxy information. + proxies = self.rebuild_proxies(prepared_request, proxies) + self.rebuild_auth(prepared_request, resp) + + # Override the original request. + req = prepared_request + + resp = self.send( + req, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + allow_redirects=False, + **adapter_kwargs + ) + + extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) + + i += 1 + yield resp + + def rebuild_auth(self, prepared_request, response): + """When being redirected we may want to strip authentication from the + request to avoid leaking credentials. This method intelligently removes + and reapplies authentication where possible to avoid credential loss. + """ + headers = prepared_request.headers + url = prepared_request.url + + if 'Authorization' in headers: + # If we get redirected to a new host, we should strip out any + # authentication headers. + original_parsed = urlparse(response.request.url) + redirect_parsed = urlparse(url) + + if (original_parsed.hostname != redirect_parsed.hostname): + del headers['Authorization'] + + # .netrc might have more auth for us on our new host. + new_auth = get_netrc_auth(url) if self.trust_env else None + if new_auth is not None: + prepared_request.prepare_auth(new_auth) + + return + + def rebuild_proxies(self, prepared_request, proxies): + """This method re-evaluates the proxy configuration by considering the + environment variables. If we are redirected to a URL covered by + NO_PROXY, we strip the proxy configuration. Otherwise, we set missing + proxy keys for this URL (in case they were stripped by a previous + redirect). + + This method also replaces the Proxy-Authorization header where + necessary. + + :rtype: dict + """ + headers = prepared_request.headers + url = prepared_request.url + scheme = urlparse(url).scheme + new_proxies = proxies.copy() if proxies is not None else {} + + if self.trust_env and not should_bypass_proxies(url): + environ_proxies = get_environ_proxies(url) + + proxy = environ_proxies.get('all', environ_proxies.get(scheme)) + + if proxy: + new_proxies.setdefault(scheme, proxy) + + if 'Proxy-Authorization' in headers: + del headers['Proxy-Authorization'] + + try: + username, password = get_auth_from_url(new_proxies[scheme]) + except KeyError: + username, password = None, None + + if username and password: + headers['Proxy-Authorization'] = _basic_auth_str(username, password) + + return new_proxies + + def rebuild_method(self, prepared_request, response): + """When being redirected we may want to change the method of the request + based on certain specs or browser behavior. + """ + method = prepared_request.method + + # http://tools.ietf.org/html/rfc7231#section-6.4.4 + if response.status_code == codes.see_other and method != 'HEAD': + method = 'GET' + + # Do what the browsers do, despite standards... + # First, turn 302s into GETs. + if response.status_code == codes.found and method != 'HEAD': + method = 'GET' + + # Second, if a POST is responded to with a 301, turn it into a GET. + # This bizarre behaviour is explained in Issue 1704. + if response.status_code == codes.moved and method == 'POST': + method = 'GET' + + prepared_request.method = method + + +class Session(SessionRedirectMixin): + """A Requests session. + + Provides cookie persistence, connection-pooling, and configuration. + + Basic Usage:: + + >>> import requests + >>> s = requests.Session() + >>> s.get('http://httpbin.org/get') + <Response [200]> + + Or as a context manager:: + + >>> with requests.Session() as s: + >>> s.get('http://httpbin.org/get') + <Response [200]> + """ + + __attrs__ = [ + 'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify', + 'cert', 'prefetch', 'adapters', 'stream', 'trust_env', + 'max_redirects', + ] + + def __init__(self): + + #: A case-insensitive dictionary of headers to be sent on each + #: :class:`Request <Request>` sent from this + #: :class:`Session <Session>`. + self.headers = default_headers() + + #: Default Authentication tuple or object to attach to + #: :class:`Request <Request>`. + self.auth = None + + #: Dictionary mapping protocol or protocol and host to the URL of the proxy + #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to + #: be used on each :class:`Request <Request>`. + self.proxies = {} + + #: Event-handling hooks. + self.hooks = default_hooks() + + #: Dictionary of querystring data to attach to each + #: :class:`Request <Request>`. The dictionary values may be lists for + #: representing multivalued query parameters. + self.params = {} + + #: Stream response content default. + self.stream = False + + #: SSL Verification default. + self.verify = True + + #: SSL certificate default. + self.cert = None + + #: Maximum number of redirects allowed. If the request exceeds this + #: limit, a :class:`TooManyRedirects` exception is raised. + #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is + #: 30. + self.max_redirects = DEFAULT_REDIRECT_LIMIT + + #: Trust environment settings for proxy configuration, default + #: authentication and similar. + self.trust_env = True + + #: A CookieJar containing all currently outstanding cookies set on this + #: session. By default it is a + #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but + #: may be any other ``cookielib.CookieJar`` compatible object. + self.cookies = cookiejar_from_dict({}) + + # Default connection adapters. + self.adapters = OrderedDict() + self.mount('https://', HTTPAdapter()) + self.mount('http://', HTTPAdapter()) + + # Only store 1000 redirects to prevent using infinite memory + self.redirect_cache = RecentlyUsedContainer(REDIRECT_CACHE_SIZE) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def prepare_request(self, request): + """Constructs a :class:`PreparedRequest <PreparedRequest>` for + transmission and returns it. The :class:`PreparedRequest` has settings + merged from the :class:`Request <Request>` instance and those of the + :class:`Session`. + + :param request: :class:`Request` instance to prepare with this + session's settings. + :rtype: requests.PreparedRequest + """ + cookies = request.cookies or {} + + # Bootstrap CookieJar. + if not isinstance(cookies, cookielib.CookieJar): + cookies = cookiejar_from_dict(cookies) + + # Merge with session cookies + merged_cookies = merge_cookies( + merge_cookies(RequestsCookieJar(), self.cookies), cookies) + + # Set environment's basic authentication if not explicitly set. + auth = request.auth + if self.trust_env and not auth and not self.auth: + auth = get_netrc_auth(request.url) + + p = PreparedRequest() + p.prepare( + method=request.method.upper(), + url=request.url, + files=request.files, + data=request.data, + json=request.json, + headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict), + params=merge_setting(request.params, self.params), + auth=merge_setting(auth, self.auth), + cookies=merged_cookies, + hooks=merge_hooks(request.hooks, self.hooks), + ) + return p + + def request(self, method, url, + params=None, + data=None, + headers=None, + cookies=None, + files=None, + auth=None, + timeout=None, + allow_redirects=True, + proxies=None, + hooks=None, + stream=None, + verify=None, + cert=None, + json=None): + """Constructs a :class:`Request <Request>`, prepares it and sends it. + Returns :class:`Response <Response>` object. + + :param method: method for the new :class:`Request` object. + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary or bytes to be sent in the query + string for the :class:`Request`. + :param data: (optional) Dictionary, bytes, or file-like object to send + in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the + :class:`Request`. + :param headers: (optional) Dictionary of HTTP Headers to send with the + :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with the + :class:`Request`. + :param files: (optional) Dictionary of ``'filename': file-like-objects`` + for multipart encoding upload. + :param auth: (optional) Auth tuple or callable to enable + Basic/Digest/Custom HTTP Auth. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) <timeouts>` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Set to True by default. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol or protocol and + hostname to the URL of the proxy. + :param stream: (optional) whether to immediately download the response + content. Defaults to ``False``. + :param verify: (optional) whether the SSL cert will be verified. + A CA_BUNDLE path can also be provided. Defaults to ``True``. + :param cert: (optional) if String, path to ssl client cert file (.pem). + If Tuple, ('cert', 'key') pair. + :rtype: requests.Response + """ + # Create the Request. + req = Request( + method = method.upper(), + url = url, + headers = headers, + files = files, + data = data or {}, + json = json, + params = params or {}, + auth = auth, + cookies = cookies, + hooks = hooks, + ) + prep = self.prepare_request(req) + + proxies = proxies or {} + + settings = self.merge_environment_settings( + prep.url, proxies, stream, verify, cert + ) + + # Send the request. + send_kwargs = { + 'timeout': timeout, + 'allow_redirects': allow_redirects, + } + send_kwargs.update(settings) + resp = self.send(prep, **send_kwargs) + + return resp + + def get(self, url, **kwargs): + """Sends a GET request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return self.request('GET', url, **kwargs) + + def options(self, url, **kwargs): + """Sends a OPTIONS request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return self.request('OPTIONS', url, **kwargs) + + def head(self, url, **kwargs): + """Sends a HEAD request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', False) + return self.request('HEAD', url, **kwargs) + + def post(self, url, data=None, json=None, **kwargs): + """Sends a POST request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('POST', url, data=data, json=json, **kwargs) + + def put(self, url, data=None, **kwargs): + """Sends a PUT request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('PUT', url, data=data, **kwargs) + + def patch(self, url, data=None, **kwargs): + """Sends a PATCH request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('PATCH', url, data=data, **kwargs) + + def delete(self, url, **kwargs): + """Sends a DELETE request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('DELETE', url, **kwargs) + + def send(self, request, **kwargs): + """ + Send a given PreparedRequest. + + :rtype: requests.Response + """ + # Set defaults that the hooks can utilize to ensure they always have + # the correct parameters to reproduce the previous request. + kwargs.setdefault('stream', self.stream) + kwargs.setdefault('verify', self.verify) + kwargs.setdefault('cert', self.cert) + kwargs.setdefault('proxies', self.proxies) + + # It's possible that users might accidentally send a Request object. + # Guard against that specific failure case. + if isinstance(request, Request): + raise ValueError('You can only send PreparedRequests.') + + # Set up variables needed for resolve_redirects and dispatching of hooks + allow_redirects = kwargs.pop('allow_redirects', True) + stream = kwargs.get('stream') + hooks = request.hooks + + # Resolve URL in redirect cache, if available. + if allow_redirects: + checked_urls = set() + while request.url in self.redirect_cache: + checked_urls.add(request.url) + new_url = self.redirect_cache.get(request.url) + if new_url in checked_urls: + break + request.url = new_url + + # Get the appropriate adapter to use + adapter = self.get_adapter(url=request.url) + + # Start time (approximately) of the request + start = datetime.utcnow() + + # Send the request + r = adapter.send(request, **kwargs) + + # Total elapsed time of the request (approximately) + r.elapsed = datetime.utcnow() - start + + # Response manipulation hooks + r = dispatch_hook('response', hooks, r, **kwargs) + + # Persist cookies + if r.history: + + # If the hooks create history then we want those cookies too + for resp in r.history: + extract_cookies_to_jar(self.cookies, resp.request, resp.raw) + + extract_cookies_to_jar(self.cookies, request, r.raw) + + # Redirect resolving generator. + gen = self.resolve_redirects(r, request, **kwargs) + + # Resolve redirects if allowed. + history = [resp for resp in gen] if allow_redirects else [] + + # Shuffle things around if there's history. + if history: + # Insert the first (original) request at the start + history.insert(0, r) + # Get the last request made + r = history.pop() + r.history = history + + if not stream: + r.content + + return r + + def merge_environment_settings(self, url, proxies, stream, verify, cert): + """ + Check the environment and merge it with some settings. + + :rtype: dict + """ + # Gather clues from the surrounding environment. + if self.trust_env: + # Set environment's proxies. + env_proxies = get_environ_proxies(url) or {} + for (k, v) in env_proxies.items(): + proxies.setdefault(k, v) + + # Look for requests environment configuration and be compatible + # with cURL. + if verify is True or verify is None: + verify = (os.environ.get('REQUESTS_CA_BUNDLE') or + os.environ.get('CURL_CA_BUNDLE')) + + # Merge all the kwargs. + proxies = merge_setting(proxies, self.proxies) + stream = merge_setting(stream, self.stream) + verify = merge_setting(verify, self.verify) + cert = merge_setting(cert, self.cert) + + return {'verify': verify, 'proxies': proxies, 'stream': stream, + 'cert': cert} + + def get_adapter(self, url): + """ + Returns the appropriate connection adapter for the given URL. + + :rtype: requests.adapters.BaseAdapter + """ + for (prefix, adapter) in self.adapters.items(): + + if url.lower().startswith(prefix): + return adapter + + # Nothing matches :-/ + raise InvalidSchema("No connection adapters were found for '%s'" % url) + + def close(self): + """Closes all adapters and as such the session""" + for v in self.adapters.values(): + v.close() + + def mount(self, prefix, adapter): + """Registers a connection adapter to a prefix. + + Adapters are sorted in descending order by key length. + """ + self.adapters[prefix] = adapter + keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] + + for key in keys_to_move: + self.adapters[key] = self.adapters.pop(key) + + def __getstate__(self): + state = dict((attr, getattr(self, attr, None)) for attr in self.__attrs__) + state['redirect_cache'] = dict(self.redirect_cache) + return state + + def __setstate__(self, state): + redirect_cache = state.pop('redirect_cache', {}) + for attr, value in state.items(): + setattr(self, attr, value) + + self.redirect_cache = RecentlyUsedContainer(REDIRECT_CACHE_SIZE) + for redirect, to in redirect_cache.items(): + self.redirect_cache[redirect] = to + + +def session(): + """ + Returns a :class:`Session` for context-management. + + :rtype: Session + """ + + return Session() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/status_codes.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/status_codes.py new file mode 100644 index 0000000..db2986b --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/status_codes.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +from .structures import LookupDict + +_codes = { + + # Informational. + 100: ('continue',), + 101: ('switching_protocols',), + 102: ('processing',), + 103: ('checkpoint',), + 122: ('uri_too_long', 'request_uri_too_long'), + 200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '✓'), + 201: ('created',), + 202: ('accepted',), + 203: ('non_authoritative_info', 'non_authoritative_information'), + 204: ('no_content',), + 205: ('reset_content', 'reset'), + 206: ('partial_content', 'partial'), + 207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'), + 208: ('already_reported',), + 226: ('im_used',), + + # Redirection. + 300: ('multiple_choices',), + 301: ('moved_permanently', 'moved', '\\o-'), + 302: ('found',), + 303: ('see_other', 'other'), + 304: ('not_modified',), + 305: ('use_proxy',), + 306: ('switch_proxy',), + 307: ('temporary_redirect', 'temporary_moved', 'temporary'), + 308: ('permanent_redirect', + 'resume_incomplete', 'resume',), # These 2 to be removed in 3.0 + + # Client Error. + 400: ('bad_request', 'bad'), + 401: ('unauthorized',), + 402: ('payment_required', 'payment'), + 403: ('forbidden',), + 404: ('not_found', '-o-'), + 405: ('method_not_allowed', 'not_allowed'), + 406: ('not_acceptable',), + 407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'), + 408: ('request_timeout', 'timeout'), + 409: ('conflict',), + 410: ('gone',), + 411: ('length_required',), + 412: ('precondition_failed', 'precondition'), + 413: ('request_entity_too_large',), + 414: ('request_uri_too_large',), + 415: ('unsupported_media_type', 'unsupported_media', 'media_type'), + 416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'), + 417: ('expectation_failed',), + 418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'), + 421: ('misdirected_request',), + 422: ('unprocessable_entity', 'unprocessable'), + 423: ('locked',), + 424: ('failed_dependency', 'dependency'), + 425: ('unordered_collection', 'unordered'), + 426: ('upgrade_required', 'upgrade'), + 428: ('precondition_required', 'precondition'), + 429: ('too_many_requests', 'too_many'), + 431: ('header_fields_too_large', 'fields_too_large'), + 444: ('no_response', 'none'), + 449: ('retry_with', 'retry'), + 450: ('blocked_by_windows_parental_controls', 'parental_controls'), + 451: ('unavailable_for_legal_reasons', 'legal_reasons'), + 499: ('client_closed_request',), + + # Server Error. + 500: ('internal_server_error', 'server_error', '/o\\', '✗'), + 501: ('not_implemented',), + 502: ('bad_gateway',), + 503: ('service_unavailable', 'unavailable'), + 504: ('gateway_timeout',), + 505: ('http_version_not_supported', 'http_version'), + 506: ('variant_also_negotiates',), + 507: ('insufficient_storage',), + 509: ('bandwidth_limit_exceeded', 'bandwidth'), + 510: ('not_extended',), + 511: ('network_authentication_required', 'network_auth', 'network_authentication'), +} + +codes = LookupDict(name='status_codes') + +for code, titles in _codes.items(): + for title in titles: + setattr(codes, title, code) + if not title.startswith('\\'): + setattr(codes, title.upper(), code) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/structures.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/structures.py new file mode 100644 index 0000000..05d2b3f --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/structures.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- + +""" +requests.structures +~~~~~~~~~~~~~~~~~~~ + +Data structures that power Requests. +""" + +import collections + +from .compat import OrderedDict + + +class CaseInsensitiveDict(collections.MutableMapping): + """A case-insensitive ``dict``-like object. + + Implements all methods and operations of + ``collections.MutableMapping`` as well as dict's ``copy``. Also + provides ``lower_items``. + + All keys are expected to be strings. The structure remembers the + case of the last key to be set, and ``iter(instance)``, + ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()`` + will contain case-sensitive keys. However, querying and contains + testing is case insensitive:: + + cid = CaseInsensitiveDict() + cid['Accept'] = 'application/json' + cid['aCCEPT'] == 'application/json' # True + list(cid) == ['Accept'] # True + + For example, ``headers['content-encoding']`` will return the + value of a ``'Content-Encoding'`` response header, regardless + of how the header name was originally stored. + + If the constructor, ``.update``, or equality comparison + operations are given keys that have equal ``.lower()``s, the + behavior is undefined. + """ + + def __init__(self, data=None, **kwargs): + self._store = OrderedDict() + if data is None: + data = {} + self.update(data, **kwargs) + + def __setitem__(self, key, value): + # Use the lowercased key for lookups, but store the actual + # key alongside the value. + self._store[key.lower()] = (key, value) + + def __getitem__(self, key): + return self._store[key.lower()][1] + + def __delitem__(self, key): + del self._store[key.lower()] + + def __iter__(self): + return (casedkey for casedkey, mappedvalue in self._store.values()) + + def __len__(self): + return len(self._store) + + def lower_items(self): + """Like iteritems(), but with all lowercase keys.""" + return ( + (lowerkey, keyval[1]) + for (lowerkey, keyval) + in self._store.items() + ) + + def __eq__(self, other): + if isinstance(other, collections.Mapping): + other = CaseInsensitiveDict(other) + else: + return NotImplemented + # Compare insensitively + return dict(self.lower_items()) == dict(other.lower_items()) + + # Copy is required + def copy(self): + return CaseInsensitiveDict(self._store.values()) + + def __repr__(self): + return str(dict(self.items())) + + +class LookupDict(dict): + """Dictionary lookup object.""" + + def __init__(self, name=None): + self.name = name + super(LookupDict, self).__init__() + + def __repr__(self): + return '<lookup \'%s\'>' % (self.name) + + def __getitem__(self, key): + # We allow fall-through here, so values default to None + + return self.__dict__.get(key, None) + + def get(self, key, default=None): + return self.__dict__.get(key, default) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/utils.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/utils.py new file mode 100644 index 0000000..30a03ca --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/requests/utils.py @@ -0,0 +1,817 @@ +# -*- coding: utf-8 -*- + +""" +requests.utils +~~~~~~~~~~~~~~ + +This module provides utility functions that are used within Requests +that are also useful for external consumption. +""" + +import cgi +import codecs +import collections +import io +import os +import re +import socket +import struct +import warnings + +from . import __version__ +from . import certs +from .compat import parse_http_list as _parse_list_header +from .compat import (quote, urlparse, bytes, str, OrderedDict, unquote, is_py2, + builtin_str, getproxies, proxy_bypass, urlunparse, + basestring) +from .cookies import RequestsCookieJar, cookiejar_from_dict +from .structures import CaseInsensitiveDict +from .exceptions import InvalidURL, InvalidHeader, FileModeWarning + +_hush_pyflakes = (RequestsCookieJar,) + +NETRC_FILES = ('.netrc', '_netrc') + +DEFAULT_CA_BUNDLE_PATH = certs.where() + + +def dict_to_sequence(d): + """Returns an internal sequence dictionary update.""" + + if hasattr(d, 'items'): + d = d.items() + + return d + + +def super_len(o): + total_length = 0 + current_position = 0 + + if hasattr(o, '__len__'): + total_length = len(o) + + elif hasattr(o, 'len'): + total_length = o.len + + elif hasattr(o, 'getvalue'): + # e.g. BytesIO, cStringIO.StringIO + total_length = len(o.getvalue()) + + elif hasattr(o, 'fileno'): + try: + fileno = o.fileno() + except io.UnsupportedOperation: + pass + else: + total_length = os.fstat(fileno).st_size + + # Having used fstat to determine the file length, we need to + # confirm that this file was opened up in binary mode. + if 'b' not in o.mode: + warnings.warn(( + "Requests has determined the content-length for this " + "request using the binary size of the file: however, the " + "file has been opened in text mode (i.e. without the 'b' " + "flag in the mode). This may lead to an incorrect " + "content-length. In Requests 3.0, support will be removed " + "for files in text mode."), + FileModeWarning + ) + + if hasattr(o, 'tell'): + try: + current_position = o.tell() + except (OSError, IOError): + # This can happen in some weird situations, such as when the file + # is actually a special file descriptor like stdin. In this + # instance, we don't know what the length is, so set it to zero and + # let requests chunk it instead. + current_position = total_length + + return max(0, total_length - current_position) + + +def get_netrc_auth(url, raise_errors=False): + """Returns the Requests tuple auth for a given url from netrc.""" + + try: + from netrc import netrc, NetrcParseError + + netrc_path = None + + for f in NETRC_FILES: + try: + loc = os.path.expanduser('~/{0}'.format(f)) + except KeyError: + # os.path.expanduser can fail when $HOME is undefined and + # getpwuid fails. See http://bugs.python.org/issue20164 & + # https://github.com/kennethreitz/requests/issues/1846 + return + + if os.path.exists(loc): + netrc_path = loc + break + + # Abort early if there isn't one. + if netrc_path is None: + return + + ri = urlparse(url) + + # Strip port numbers from netloc. This weird `if...encode`` dance is + # used for Python 3.2, which doesn't support unicode literals. + splitstr = b':' + if isinstance(url, str): + splitstr = splitstr.decode('ascii') + host = ri.netloc.split(splitstr)[0] + + try: + _netrc = netrc(netrc_path).authenticators(host) + if _netrc: + # Return with login / password + login_i = (0 if _netrc[0] else 1) + return (_netrc[login_i], _netrc[2]) + except (NetrcParseError, IOError): + # If there was a parsing error or a permissions issue reading the file, + # we'll just skip netrc auth unless explicitly asked to raise errors. + if raise_errors: + raise + + # AppEngine hackiness. + except (ImportError, AttributeError): + pass + + +def guess_filename(obj): + """Tries to guess the filename of the given object.""" + name = getattr(obj, 'name', None) + if (name and isinstance(name, basestring) and name[0] != '<' and + name[-1] != '>'): + return os.path.basename(name) + + +def from_key_val_list(value): + """Take an object and test to see if it can be represented as a + dictionary. Unless it can not be represented as such, return an + OrderedDict, e.g., + + :: + + >>> from_key_val_list([('key', 'val')]) + OrderedDict([('key', 'val')]) + >>> from_key_val_list('string') + ValueError: need more than 1 value to unpack + >>> from_key_val_list({'key': 'val'}) + OrderedDict([('key', 'val')]) + + :rtype: OrderedDict + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError('cannot encode objects that are not 2-tuples') + + return OrderedDict(value) + + +def to_key_val_list(value): + """Take an object and test to see if it can be represented as a + dictionary. If it can be, return a list of tuples, e.g., + + :: + + >>> to_key_val_list([('key', 'val')]) + [('key', 'val')] + >>> to_key_val_list({'key': 'val'}) + [('key', 'val')] + >>> to_key_val_list('string') + ValueError: cannot encode objects that are not 2-tuples. + + :rtype: list + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError('cannot encode objects that are not 2-tuples') + + if isinstance(value, collections.Mapping): + value = value.items() + + return list(value) + + +# From mitsuhiko/werkzeug (used with permission). +def parse_list_header(value): + """Parse lists as described by RFC 2068 Section 2. + + In particular, parse comma-separated lists where the elements of + the list may include quoted-strings. A quoted-string could + contain a comma. A non-quoted string could have quotes in the + middle. Quotes are removed automatically after parsing. + + It basically works like :func:`parse_set_header` just that items + may appear multiple times and case sensitivity is preserved. + + The return value is a standard :class:`list`: + + >>> parse_list_header('token, "quoted value"') + ['token', 'quoted value'] + + To create a header from the :class:`list` again, use the + :func:`dump_header` function. + + :param value: a string with a list header. + :return: :class:`list` + :rtype: list + """ + result = [] + for item in _parse_list_header(value): + if item[:1] == item[-1:] == '"': + item = unquote_header_value(item[1:-1]) + result.append(item) + return result + + +# From mitsuhiko/werkzeug (used with permission). +def parse_dict_header(value): + """Parse lists of key, value pairs as described by RFC 2068 Section 2 and + convert them into a python dict: + + >>> d = parse_dict_header('foo="is a fish", bar="as well"') + >>> type(d) is dict + True + >>> sorted(d.items()) + [('bar', 'as well'), ('foo', 'is a fish')] + + If there is no value for a key it will be `None`: + + >>> parse_dict_header('key_without_value') + {'key_without_value': None} + + To create a header from the :class:`dict` again, use the + :func:`dump_header` function. + + :param value: a string with a dict header. + :return: :class:`dict` + :rtype: dict + """ + result = {} + for item in _parse_list_header(value): + if '=' not in item: + result[item] = None + continue + name, value = item.split('=', 1) + if value[:1] == value[-1:] == '"': + value = unquote_header_value(value[1:-1]) + result[name] = value + return result + + +# From mitsuhiko/werkzeug (used with permission). +def unquote_header_value(value, is_filename=False): + r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). + This does not use the real unquoting but what browsers are actually + using for quoting. + + :param value: the header value to unquote. + :rtype: str + """ + if value and value[0] == value[-1] == '"': + # this is not the real unquoting, but fixing this so that the + # RFC is met will result in bugs with internet explorer and + # probably some other browsers as well. IE for example is + # uploading files with "C:\foo\bar.txt" as filename + value = value[1:-1] + + # if this is a filename and the starting characters look like + # a UNC path, then just return the value without quotes. Using the + # replace sequence below on a UNC path has the effect of turning + # the leading double slash into a single slash and then + # _fix_ie_filename() doesn't work correctly. See #458. + if not is_filename or value[:2] != '\\\\': + return value.replace('\\\\', '\\').replace('\\"', '"') + return value + + +def dict_from_cookiejar(cj): + """Returns a key/value dictionary from a CookieJar. + + :param cj: CookieJar object to extract cookies from. + :rtype: dict + """ + + cookie_dict = {} + + for cookie in cj: + cookie_dict[cookie.name] = cookie.value + + return cookie_dict + + +def add_dict_to_cookiejar(cj, cookie_dict): + """Returns a CookieJar from a key/value dictionary. + + :param cj: CookieJar to insert cookies into. + :param cookie_dict: Dict of key/values to insert into CookieJar. + :rtype: CookieJar + """ + + cj2 = cookiejar_from_dict(cookie_dict) + cj.update(cj2) + return cj + + +def get_encodings_from_content(content): + """Returns encodings from given content string. + + :param content: bytestring to extract encodings from. + """ + warnings.warn(( + 'In requests 3.0, get_encodings_from_content will be removed. For ' + 'more information, please see the discussion on issue #2266. (This' + ' warning should only appear once.)'), + DeprecationWarning) + + charset_re = re.compile(r'<meta.*?charset=["\']*(.+?)["\'>]', flags=re.I) + pragma_re = re.compile(r'<meta.*?content=["\']*;?charset=(.+?)["\'>]', flags=re.I) + xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]') + + return (charset_re.findall(content) + + pragma_re.findall(content) + + xml_re.findall(content)) + + +def get_encoding_from_headers(headers): + """Returns encodings from given HTTP Header Dict. + + :param headers: dictionary to extract encoding from. + :rtype: str + """ + + content_type = headers.get('content-type') + + if not content_type: + return None + + content_type, params = cgi.parse_header(content_type) + + if 'charset' in params: + return params['charset'].strip("'\"") + + if 'text' in content_type: + return 'ISO-8859-1' + + +def stream_decode_response_unicode(iterator, r): + """Stream decodes a iterator.""" + + if r.encoding is None: + for item in iterator: + yield item + return + + decoder = codecs.getincrementaldecoder(r.encoding)(errors='replace') + for chunk in iterator: + rv = decoder.decode(chunk) + if rv: + yield rv + rv = decoder.decode(b'', final=True) + if rv: + yield rv + + +def iter_slices(string, slice_length): + """Iterate over slices of a string.""" + pos = 0 + if slice_length is None or slice_length <= 0: + slice_length = len(string) + while pos < len(string): + yield string[pos:pos + slice_length] + pos += slice_length + + +def get_unicode_from_response(r): + """Returns the requested content back in unicode. + + :param r: Response object to get unicode content from. + + Tried: + + 1. charset from content-type + 2. fall back and replace all unicode characters + + :rtype: str + """ + warnings.warn(( + 'In requests 3.0, get_unicode_from_response will be removed. For ' + 'more information, please see the discussion on issue #2266. (This' + ' warning should only appear once.)'), + DeprecationWarning) + + tried_encodings = [] + + # Try charset from content-type + encoding = get_encoding_from_headers(r.headers) + + if encoding: + try: + return str(r.content, encoding) + except UnicodeError: + tried_encodings.append(encoding) + + # Fall back: + try: + return str(r.content, encoding, errors='replace') + except TypeError: + return r.content + + +# The unreserved URI characters (RFC 3986) +UNRESERVED_SET = frozenset( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + + "0123456789-._~") + + +def unquote_unreserved(uri): + """Un-escape any percent-escape sequences in a URI that are unreserved + characters. This leaves all reserved, illegal and non-ASCII bytes encoded. + + :rtype: str + """ + parts = uri.split('%') + for i in range(1, len(parts)): + h = parts[i][0:2] + if len(h) == 2 and h.isalnum(): + try: + c = chr(int(h, 16)) + except ValueError: + raise InvalidURL("Invalid percent-escape sequence: '%s'" % h) + + if c in UNRESERVED_SET: + parts[i] = c + parts[i][2:] + else: + parts[i] = '%' + parts[i] + else: + parts[i] = '%' + parts[i] + return ''.join(parts) + + +def requote_uri(uri): + """Re-quote the given URI. + + This function passes the given URI through an unquote/quote cycle to + ensure that it is fully and consistently quoted. + + :rtype: str + """ + safe_with_percent = "!#$%&'()*+,/:;=?@[]~" + safe_without_percent = "!#$&'()*+,/:;=?@[]~" + try: + # Unquote only the unreserved characters + # Then quote only illegal characters (do not quote reserved, + # unreserved, or '%') + return quote(unquote_unreserved(uri), safe=safe_with_percent) + except InvalidURL: + # We couldn't unquote the given URI, so let's try quoting it, but + # there may be unquoted '%'s in the URI. We need to make sure they're + # properly quoted so they do not cause issues elsewhere. + return quote(uri, safe=safe_without_percent) + + +def address_in_network(ip, net): + """This function allows you to check if on IP belongs to a network subnet + + Example: returns True if ip = 192.168.1.1 and net = 192.168.1.0/24 + returns False if ip = 192.168.1.1 and net = 192.168.100.0/24 + + :rtype: bool + """ + ipaddr = struct.unpack('=L', socket.inet_aton(ip))[0] + netaddr, bits = net.split('/') + netmask = struct.unpack('=L', socket.inet_aton(dotted_netmask(int(bits))))[0] + network = struct.unpack('=L', socket.inet_aton(netaddr))[0] & netmask + return (ipaddr & netmask) == (network & netmask) + + +def dotted_netmask(mask): + """Converts mask from /xx format to xxx.xxx.xxx.xxx + + Example: if mask is 24 function returns 255.255.255.0 + + :rtype: str + """ + bits = 0xffffffff ^ (1 << 32 - mask) - 1 + return socket.inet_ntoa(struct.pack('>I', bits)) + + +def is_ipv4_address(string_ip): + """ + :rtype: bool + """ + try: + socket.inet_aton(string_ip) + except socket.error: + return False + return True + + +def is_valid_cidr(string_network): + """ + Very simple check of the cidr format in no_proxy variable. + + :rtype: bool + """ + if string_network.count('/') == 1: + try: + mask = int(string_network.split('/')[1]) + except ValueError: + return False + + if mask < 1 or mask > 32: + return False + + try: + socket.inet_aton(string_network.split('/')[0]) + except socket.error: + return False + else: + return False + return True + + +def should_bypass_proxies(url): + """ + Returns whether we should bypass proxies or not. + + :rtype: bool + """ + get_proxy = lambda k: os.environ.get(k) or os.environ.get(k.upper()) + + # First check whether no_proxy is defined. If it is, check that the URL + # we're getting isn't in the no_proxy list. + no_proxy = get_proxy('no_proxy') + netloc = urlparse(url).netloc + + if no_proxy: + # We need to check whether we match here. We need to see if we match + # the end of the netloc, both with and without the port. + no_proxy = ( + host for host in no_proxy.replace(' ', '').split(',') if host + ) + + ip = netloc.split(':')[0] + if is_ipv4_address(ip): + for proxy_ip in no_proxy: + if is_valid_cidr(proxy_ip): + if address_in_network(ip, proxy_ip): + return True + elif ip == proxy_ip: + # If no_proxy ip was defined in plain IP notation instead of cidr notation & + # matches the IP of the index + return True + else: + for host in no_proxy: + if netloc.endswith(host) or netloc.split(':')[0].endswith(host): + # The URL does match something in no_proxy, so we don't want + # to apply the proxies on this URL. + return True + + # If the system proxy settings indicate that this URL should be bypassed, + # don't proxy. + # The proxy_bypass function is incredibly buggy on macOS in early versions + # of Python 2.6, so allow this call to fail. Only catch the specific + # exceptions we've seen, though: this call failing in other ways can reveal + # legitimate problems. + try: + bypass = proxy_bypass(netloc) + except (TypeError, socket.gaierror): + bypass = False + + if bypass: + return True + + return False + + +def get_environ_proxies(url): + """ + Return a dict of environment proxies. + + :rtype: dict + """ + if should_bypass_proxies(url): + return {} + else: + return getproxies() + + +def select_proxy(url, proxies): + """Select a proxy for the url, if applicable. + + :param url: The url being for the request + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs + """ + proxies = proxies or {} + urlparts = urlparse(url) + if urlparts.hostname is None: + return proxies.get('all', proxies.get(urlparts.scheme)) + + proxy_keys = [ + 'all://' + urlparts.hostname, + 'all', + urlparts.scheme + '://' + urlparts.hostname, + urlparts.scheme, + ] + proxy = None + for proxy_key in proxy_keys: + if proxy_key in proxies: + proxy = proxies[proxy_key] + break + + return proxy + + +def default_user_agent(name="python-requests"): + """ + Return a string representing the default user agent. + + :rtype: str + """ + return '%s/%s' % (name, __version__) + + +def default_headers(): + """ + :rtype: requests.structures.CaseInsensitiveDict + """ + return CaseInsensitiveDict({ + 'User-Agent': default_user_agent(), + 'Accept-Encoding': ', '.join(('gzip', 'deflate')), + 'Accept': '*/*', + 'Connection': 'keep-alive', + }) + + +def parse_header_links(value): + """Return a dict of parsed link headers proxies. + + i.e. Link: <http:/.../front.jpeg>; rel=front; type="image/jpeg",<http://.../back.jpeg>; rel=back;type="image/jpeg" + + :rtype: list + """ + + links = [] + + replace_chars = ' \'"' + + for val in re.split(', *<', value): + try: + url, params = val.split(';', 1) + except ValueError: + url, params = val, '' + + link = {'url': url.strip('<> \'"')} + + for param in params.split(';'): + try: + key, value = param.split('=') + except ValueError: + break + + link[key.strip(replace_chars)] = value.strip(replace_chars) + + links.append(link) + + return links + + +# Null bytes; no need to recreate these on each call to guess_json_utf +_null = '\x00'.encode('ascii') # encoding to ASCII for Python 3 +_null2 = _null * 2 +_null3 = _null * 3 + + +def guess_json_utf(data): + """ + :rtype: str + """ + # JSON always starts with two ASCII characters, so detection is as + # easy as counting the nulls and from their location and count + # determine the encoding. Also detect a BOM, if present. + sample = data[:4] + if sample in (codecs.BOM_UTF32_LE, codecs.BOM32_BE): + return 'utf-32' # BOM included + if sample[:3] == codecs.BOM_UTF8: + return 'utf-8-sig' # BOM included, MS style (discouraged) + if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE): + return 'utf-16' # BOM included + nullcount = sample.count(_null) + if nullcount == 0: + return 'utf-8' + if nullcount == 2: + if sample[::2] == _null2: # 1st and 3rd are null + return 'utf-16-be' + if sample[1::2] == _null2: # 2nd and 4th are null + return 'utf-16-le' + # Did not detect 2 valid UTF-16 ascii-range characters + if nullcount == 3: + if sample[:3] == _null3: + return 'utf-32-be' + if sample[1:] == _null3: + return 'utf-32-le' + # Did not detect a valid UTF-32 ascii-range character + return None + + +def prepend_scheme_if_needed(url, new_scheme): + """Given a URL that may or may not have a scheme, prepend the given scheme. + Does not replace a present scheme with the one provided as an argument. + + :rtype: str + """ + scheme, netloc, path, params, query, fragment = urlparse(url, new_scheme) + + # urlparse is a finicky beast, and sometimes decides that there isn't a + # netloc present. Assume that it's being over-cautious, and switch netloc + # and path if urlparse decided there was no netloc. + if not netloc: + netloc, path = path, netloc + + return urlunparse((scheme, netloc, path, params, query, fragment)) + + +def get_auth_from_url(url): + """Given a url with authentication components, extract them into a tuple of + username,password. + + :rtype: (str,str) + """ + parsed = urlparse(url) + + try: + auth = (unquote(parsed.username), unquote(parsed.password)) + except (AttributeError, TypeError): + auth = ('', '') + + return auth + + +def to_native_string(string, encoding='ascii'): + """Given a string object, regardless of type, returns a representation of + that string in the native string type, encoding and decoding where + necessary. This assumes ASCII unless told otherwise. + """ + if isinstance(string, builtin_str): + out = string + else: + if is_py2: + out = string.encode(encoding) + else: + out = string.decode(encoding) + + return out + + +# Moved outside of function to avoid recompile every call +_CLEAN_HEADER_REGEX_BYTE = re.compile(b'^\\S[^\\r\\n]*$|^$') +_CLEAN_HEADER_REGEX_STR = re.compile(r'^\S[^\r\n]*$|^$') + +def check_header_validity(header): + """Verifies that header value is a string which doesn't contain + leading whitespace or return characters. This prevents unintended + header injection. + + :param header: tuple, in the format (name, value). + """ + name, value = header + + if isinstance(value, bytes): + pat = _CLEAN_HEADER_REGEX_BYTE + else: + pat = _CLEAN_HEADER_REGEX_STR + try: + if not pat.match(value): + raise InvalidHeader("Invalid return character or leading space in header: %s" % name) + except TypeError: + raise InvalidHeader("Header value %s must be of type str or bytes, " + "not %s" % (value, type(value))) + + +def urldefragauth(url): + """ + Given a url remove the fragment and the authentication part. + + :rtype: str + """ + scheme, netloc, path, params, query, fragment = urlparse(url) + + # see func:`prepend_scheme_if_needed` + if not netloc: + netloc, path = path, netloc + + netloc = netloc.rsplit('@', 1)[-1] + + return urlunparse((scheme, netloc, path, params, query, '')) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/retrying.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/retrying.py new file mode 100644 index 0000000..6d1e627 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/retrying.py @@ -0,0 +1,267 @@ +## Copyright 2013-2014 Ray Holder +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +import random +from pip._vendor import six +import sys +import time +import traceback + + +# sys.maxint / 2, since Python 3.2 doesn't have a sys.maxint... +MAX_WAIT = 1073741823 + + +def retry(*dargs, **dkw): + """ + Decorator function that instantiates the Retrying object + @param *dargs: positional arguments passed to Retrying object + @param **dkw: keyword arguments passed to the Retrying object + """ + # support both @retry and @retry() as valid syntax + if len(dargs) == 1 and callable(dargs[0]): + def wrap_simple(f): + + @six.wraps(f) + def wrapped_f(*args, **kw): + return Retrying().call(f, *args, **kw) + + return wrapped_f + + return wrap_simple(dargs[0]) + + else: + def wrap(f): + + @six.wraps(f) + def wrapped_f(*args, **kw): + return Retrying(*dargs, **dkw).call(f, *args, **kw) + + return wrapped_f + + return wrap + + +class Retrying(object): + + def __init__(self, + stop=None, wait=None, + stop_max_attempt_number=None, + stop_max_delay=None, + wait_fixed=None, + wait_random_min=None, wait_random_max=None, + wait_incrementing_start=None, wait_incrementing_increment=None, + wait_exponential_multiplier=None, wait_exponential_max=None, + retry_on_exception=None, + retry_on_result=None, + wrap_exception=False, + stop_func=None, + wait_func=None, + wait_jitter_max=None): + + self._stop_max_attempt_number = 5 if stop_max_attempt_number is None else stop_max_attempt_number + self._stop_max_delay = 100 if stop_max_delay is None else stop_max_delay + self._wait_fixed = 1000 if wait_fixed is None else wait_fixed + self._wait_random_min = 0 if wait_random_min is None else wait_random_min + self._wait_random_max = 1000 if wait_random_max is None else wait_random_max + self._wait_incrementing_start = 0 if wait_incrementing_start is None else wait_incrementing_start + self._wait_incrementing_increment = 100 if wait_incrementing_increment is None else wait_incrementing_increment + self._wait_exponential_multiplier = 1 if wait_exponential_multiplier is None else wait_exponential_multiplier + self._wait_exponential_max = MAX_WAIT if wait_exponential_max is None else wait_exponential_max + self._wait_jitter_max = 0 if wait_jitter_max is None else wait_jitter_max + + # TODO add chaining of stop behaviors + # stop behavior + stop_funcs = [] + if stop_max_attempt_number is not None: + stop_funcs.append(self.stop_after_attempt) + + if stop_max_delay is not None: + stop_funcs.append(self.stop_after_delay) + + if stop_func is not None: + self.stop = stop_func + + elif stop is None: + self.stop = lambda attempts, delay: any(f(attempts, delay) for f in stop_funcs) + + else: + self.stop = getattr(self, stop) + + # TODO add chaining of wait behaviors + # wait behavior + wait_funcs = [lambda *args, **kwargs: 0] + if wait_fixed is not None: + wait_funcs.append(self.fixed_sleep) + + if wait_random_min is not None or wait_random_max is not None: + wait_funcs.append(self.random_sleep) + + if wait_incrementing_start is not None or wait_incrementing_increment is not None: + wait_funcs.append(self.incrementing_sleep) + + if wait_exponential_multiplier is not None or wait_exponential_max is not None: + wait_funcs.append(self.exponential_sleep) + + if wait_func is not None: + self.wait = wait_func + + elif wait is None: + self.wait = lambda attempts, delay: max(f(attempts, delay) for f in wait_funcs) + + else: + self.wait = getattr(self, wait) + + # retry on exception filter + if retry_on_exception is None: + self._retry_on_exception = self.always_reject + else: + self._retry_on_exception = retry_on_exception + + # TODO simplify retrying by Exception types + # retry on result filter + if retry_on_result is None: + self._retry_on_result = self.never_reject + else: + self._retry_on_result = retry_on_result + + self._wrap_exception = wrap_exception + + def stop_after_attempt(self, previous_attempt_number, delay_since_first_attempt_ms): + """Stop after the previous attempt >= stop_max_attempt_number.""" + return previous_attempt_number >= self._stop_max_attempt_number + + def stop_after_delay(self, previous_attempt_number, delay_since_first_attempt_ms): + """Stop after the time from the first attempt >= stop_max_delay.""" + return delay_since_first_attempt_ms >= self._stop_max_delay + + def no_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """Don't sleep at all before retrying.""" + return 0 + + def fixed_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """Sleep a fixed amount of time between each retry.""" + return self._wait_fixed + + def random_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """Sleep a random amount of time between wait_random_min and wait_random_max""" + return random.randint(self._wait_random_min, self._wait_random_max) + + def incrementing_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """ + Sleep an incremental amount of time after each attempt, starting at + wait_incrementing_start and incrementing by wait_incrementing_increment + """ + result = self._wait_incrementing_start + (self._wait_incrementing_increment * (previous_attempt_number - 1)) + if result < 0: + result = 0 + return result + + def exponential_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + exp = 2 ** previous_attempt_number + result = self._wait_exponential_multiplier * exp + if result > self._wait_exponential_max: + result = self._wait_exponential_max + if result < 0: + result = 0 + return result + + def never_reject(self, result): + return False + + def always_reject(self, result): + return True + + def should_reject(self, attempt): + reject = False + if attempt.has_exception: + reject |= self._retry_on_exception(attempt.value[1]) + else: + reject |= self._retry_on_result(attempt.value) + + return reject + + def call(self, fn, *args, **kwargs): + start_time = int(round(time.time() * 1000)) + attempt_number = 1 + while True: + try: + attempt = Attempt(fn(*args, **kwargs), attempt_number, False) + except: + tb = sys.exc_info() + attempt = Attempt(tb, attempt_number, True) + + if not self.should_reject(attempt): + return attempt.get(self._wrap_exception) + + delay_since_first_attempt_ms = int(round(time.time() * 1000)) - start_time + if self.stop(attempt_number, delay_since_first_attempt_ms): + if not self._wrap_exception and attempt.has_exception: + # get() on an attempt with an exception should cause it to be raised, but raise just in case + raise attempt.get() + else: + raise RetryError(attempt) + else: + sleep = self.wait(attempt_number, delay_since_first_attempt_ms) + if self._wait_jitter_max: + jitter = random.random() * self._wait_jitter_max + sleep = sleep + max(0, jitter) + time.sleep(sleep / 1000.0) + + attempt_number += 1 + + +class Attempt(object): + """ + An Attempt encapsulates a call to a target function that may end as a + normal return value from the function or an Exception depending on what + occurred during the execution. + """ + + def __init__(self, value, attempt_number, has_exception): + self.value = value + self.attempt_number = attempt_number + self.has_exception = has_exception + + def get(self, wrap_exception=False): + """ + Return the return value of this Attempt instance or raise an Exception. + If wrap_exception is true, this Attempt is wrapped inside of a + RetryError before being raised. + """ + if self.has_exception: + if wrap_exception: + raise RetryError(self) + else: + six.reraise(self.value[0], self.value[1], self.value[2]) + else: + return self.value + + def __repr__(self): + if self.has_exception: + return "Attempts: {0}, Error:\n{1}".format(self.attempt_number, "".join(traceback.format_tb(self.value[2]))) + else: + return "Attempts: {0}, Value: {1}".format(self.attempt_number, self.value) + + +class RetryError(Exception): + """ + A RetryError encapsulates the last Attempt instance right before giving up. + """ + + def __init__(self, last_attempt): + self.last_attempt = last_attempt + + def __str__(self): + return "RetryError[{0}]".format(self.last_attempt) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/six.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/six.py new file mode 100644 index 0000000..190c023 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/six.py @@ -0,0 +1,868 @@ +"""Utilities for writing code that runs on Python 2 and 3""" + +# Copyright (c) 2010-2015 Benjamin Peterson +# +# 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. + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson <benjamin@python.org>" +__version__ = "1.10.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/__init__.py new file mode 100644 index 0000000..03d5d35 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/__init__.py @@ -0,0 +1,342 @@ +# coding: utf8 +""" + + webencodings + ~~~~~~~~~~~~ + + This is a Python implementation of the `WHATWG Encoding standard + <http://encoding.spec.whatwg.org/>`. See README for details. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +from __future__ import unicode_literals + +import codecs + +from .labels import LABELS + + +VERSION = '0.5' + + +# Some names in Encoding are not valid Python aliases. Remap these. +PYTHON_NAMES = { + 'iso-8859-8-i': 'iso-8859-8', + 'x-mac-cyrillic': 'mac-cyrillic', + 'macintosh': 'mac-roman', + 'windows-874': 'cp874'} + +CACHE = {} + + +def ascii_lower(string): + r"""Transform (only) ASCII letters to lower case: A-Z is mapped to a-z. + + :param string: An Unicode string. + :returns: A new Unicode string. + + This is used for `ASCII case-insensitive + <http://encoding.spec.whatwg.org/#ascii-case-insensitive>`_ + matching of encoding labels. + The same matching is also used, among other things, + for `CSS keywords <http://dev.w3.org/csswg/css-values/#keywords>`_. + + This is different from the :meth:`~py:str.lower` method of Unicode strings + which also affect non-ASCII characters, + sometimes mapping them into the ASCII range: + + >>> keyword = u'Bac\N{KELVIN SIGN}ground' + >>> assert keyword.lower() == u'background' + >>> assert ascii_lower(keyword) != keyword.lower() + >>> assert ascii_lower(keyword) == u'bac\N{KELVIN SIGN}ground' + + """ + # This turns out to be faster than unicode.translate() + return string.encode('utf8').lower().decode('utf8') + + +def lookup(label): + """ + Look for an encoding by its label. + This is the spec’s `get an encoding + <http://encoding.spec.whatwg.org/#concept-encoding-get>`_ algorithm. + Supported labels are listed there. + + :param label: A string. + :returns: + An :class:`Encoding` object, or :obj:`None` for an unknown label. + + """ + # Only strip ASCII whitespace: U+0009, U+000A, U+000C, U+000D, and U+0020. + label = ascii_lower(label.strip('\t\n\f\r ')) + name = LABELS.get(label) + if name is None: + return None + encoding = CACHE.get(name) + if encoding is None: + if name == 'x-user-defined': + from .x_user_defined import codec_info + else: + python_name = PYTHON_NAMES.get(name, name) + # Any python_name value that gets to here should be valid. + codec_info = codecs.lookup(python_name) + encoding = Encoding(name, codec_info) + CACHE[name] = encoding + return encoding + + +def _get_encoding(encoding_or_label): + """ + Accept either an encoding object or label. + + :param encoding: An :class:`Encoding` object or a label string. + :returns: An :class:`Encoding` object. + :raises: :exc:`~exceptions.LookupError` for an unknown label. + + """ + if hasattr(encoding_or_label, 'codec_info'): + return encoding_or_label + + encoding = lookup(encoding_or_label) + if encoding is None: + raise LookupError('Unknown encoding label: %r' % encoding_or_label) + return encoding + + +class Encoding(object): + """Reresents a character encoding such as UTF-8, + that can be used for decoding or encoding. + + .. attribute:: name + + Canonical name of the encoding + + .. attribute:: codec_info + + The actual implementation of the encoding, + a stdlib :class:`~codecs.CodecInfo` object. + See :func:`codecs.register`. + + """ + def __init__(self, name, codec_info): + self.name = name + self.codec_info = codec_info + + def __repr__(self): + return '<Encoding %s>' % self.name + + +#: The UTF-8 encoding. Should be used for new content and formats. +UTF8 = lookup('utf-8') + +_UTF16LE = lookup('utf-16le') +_UTF16BE = lookup('utf-16be') + + +def decode(input, fallback_encoding, errors='replace'): + """ + Decode a single string. + + :param input: A byte string + :param fallback_encoding: + An :class:`Encoding` object or a label string. + The encoding to use if :obj:`input` does note have a BOM. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + :return: + A ``(output, encoding)`` tuple of an Unicode string + and an :obj:`Encoding`. + + """ + # Fail early if `encoding` is an invalid label. + fallback_encoding = _get_encoding(fallback_encoding) + bom_encoding, input = _detect_bom(input) + encoding = bom_encoding or fallback_encoding + return encoding.codec_info.decode(input, errors)[0], encoding + + +def _detect_bom(input): + """Return (bom_encoding, input), with any BOM removed from the input.""" + if input.startswith(b'\xFF\xFE'): + return _UTF16LE, input[2:] + if input.startswith(b'\xFE\xFF'): + return _UTF16BE, input[2:] + if input.startswith(b'\xEF\xBB\xBF'): + return UTF8, input[3:] + return None, input + + +def encode(input, encoding=UTF8, errors='strict'): + """ + Encode a single string. + + :param input: An Unicode string. + :param encoding: An :class:`Encoding` object or a label string. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + :return: A byte string. + + """ + return _get_encoding(encoding).codec_info.encode(input, errors)[0] + + +def iter_decode(input, fallback_encoding, errors='replace'): + """ + "Pull"-based decoder. + + :param input: + An iterable of byte strings. + + The input is first consumed just enough to determine the encoding + based on the precense of a BOM, + then consumed on demand when the return value is. + :param fallback_encoding: + An :class:`Encoding` object or a label string. + The encoding to use if :obj:`input` does note have a BOM. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + :returns: + An ``(output, encoding)`` tuple. + :obj:`output` is an iterable of Unicode strings, + :obj:`encoding` is the :obj:`Encoding` that is being used. + + """ + + decoder = IncrementalDecoder(fallback_encoding, errors) + generator = _iter_decode_generator(input, decoder) + encoding = next(generator) + return generator, encoding + + +def _iter_decode_generator(input, decoder): + """Return a generator that first yields the :obj:`Encoding`, + then yields output chukns as Unicode strings. + + """ + decode = decoder.decode + input = iter(input) + for chunck in input: + output = decode(chunck) + if output: + assert decoder.encoding is not None + yield decoder.encoding + yield output + break + else: + # Input exhausted without determining the encoding + output = decode(b'', final=True) + assert decoder.encoding is not None + yield decoder.encoding + if output: + yield output + return + + for chunck in input: + output = decode(chunck) + if output: + yield output + output = decode(b'', final=True) + if output: + yield output + + +def iter_encode(input, encoding=UTF8, errors='strict'): + """ + “Pull”-based encoder. + + :param input: An iterable of Unicode strings. + :param encoding: An :class:`Encoding` object or a label string. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + :returns: An iterable of byte strings. + + """ + # Fail early if `encoding` is an invalid label. + encode = IncrementalEncoder(encoding, errors).encode + return _iter_encode_generator(input, encode) + + +def _iter_encode_generator(input, encode): + for chunck in input: + output = encode(chunck) + if output: + yield output + output = encode('', final=True) + if output: + yield output + + +class IncrementalDecoder(object): + """ + “Push”-based decoder. + + :param fallback_encoding: + An :class:`Encoding` object or a label string. + The encoding to use if :obj:`input` does note have a BOM. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + + """ + def __init__(self, fallback_encoding, errors='replace'): + # Fail early if `encoding` is an invalid label. + self._fallback_encoding = _get_encoding(fallback_encoding) + self._errors = errors + self._buffer = b'' + self._decoder = None + #: The actual :class:`Encoding` that is being used, + #: or :obj:`None` if that is not determined yet. + #: (Ie. if there is not enough input yet to determine + #: if there is a BOM.) + self.encoding = None # Not known yet. + + def decode(self, input, final=False): + """Decode one chunk of the input. + + :param input: A byte string. + :param final: + Indicate that no more input is available. + Must be :obj:`True` if this is the last call. + :returns: An Unicode string. + + """ + decoder = self._decoder + if decoder is not None: + return decoder(input, final) + + input = self._buffer + input + encoding, input = _detect_bom(input) + if encoding is None: + if len(input) < 3 and not final: # Not enough data yet. + self._buffer = input + return '' + else: # No BOM + encoding = self._fallback_encoding + decoder = encoding.codec_info.incrementaldecoder(self._errors).decode + self._decoder = decoder + self.encoding = encoding + return decoder(input, final) + + +class IncrementalEncoder(object): + """ + “Push”-based encoder. + + :param encoding: An :class:`Encoding` object or a label string. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + + .. method:: encode(input, final=False) + + :param input: An Unicode string. + :param final: + Indicate that no more input is available. + Must be :obj:`True` if this is the last call. + :returns: A byte string. + + """ + def __init__(self, encoding=UTF8, errors='strict'): + encoding = _get_encoding(encoding) + self.encode = encoding.codec_info.incrementalencoder(errors).encode diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/labels.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/labels.py new file mode 100644 index 0000000..29cbf91 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/labels.py @@ -0,0 +1,231 @@ +""" + + webencodings.labels + ~~~~~~~~~~~~~~~~~~~ + + Map encoding labels to their name. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +# XXX Do not edit! +# This file is automatically generated by mklabels.py + +LABELS = { + 'unicode-1-1-utf-8': 'utf-8', + 'utf-8': 'utf-8', + 'utf8': 'utf-8', + '866': 'ibm866', + 'cp866': 'ibm866', + 'csibm866': 'ibm866', + 'ibm866': 'ibm866', + 'csisolatin2': 'iso-8859-2', + 'iso-8859-2': 'iso-8859-2', + 'iso-ir-101': 'iso-8859-2', + 'iso8859-2': 'iso-8859-2', + 'iso88592': 'iso-8859-2', + 'iso_8859-2': 'iso-8859-2', + 'iso_8859-2:1987': 'iso-8859-2', + 'l2': 'iso-8859-2', + 'latin2': 'iso-8859-2', + 'csisolatin3': 'iso-8859-3', + 'iso-8859-3': 'iso-8859-3', + 'iso-ir-109': 'iso-8859-3', + 'iso8859-3': 'iso-8859-3', + 'iso88593': 'iso-8859-3', + 'iso_8859-3': 'iso-8859-3', + 'iso_8859-3:1988': 'iso-8859-3', + 'l3': 'iso-8859-3', + 'latin3': 'iso-8859-3', + 'csisolatin4': 'iso-8859-4', + 'iso-8859-4': 'iso-8859-4', + 'iso-ir-110': 'iso-8859-4', + 'iso8859-4': 'iso-8859-4', + 'iso88594': 'iso-8859-4', + 'iso_8859-4': 'iso-8859-4', + 'iso_8859-4:1988': 'iso-8859-4', + 'l4': 'iso-8859-4', + 'latin4': 'iso-8859-4', + 'csisolatincyrillic': 'iso-8859-5', + 'cyrillic': 'iso-8859-5', + 'iso-8859-5': 'iso-8859-5', + 'iso-ir-144': 'iso-8859-5', + 'iso8859-5': 'iso-8859-5', + 'iso88595': 'iso-8859-5', + 'iso_8859-5': 'iso-8859-5', + 'iso_8859-5:1988': 'iso-8859-5', + 'arabic': 'iso-8859-6', + 'asmo-708': 'iso-8859-6', + 'csiso88596e': 'iso-8859-6', + 'csiso88596i': 'iso-8859-6', + 'csisolatinarabic': 'iso-8859-6', + 'ecma-114': 'iso-8859-6', + 'iso-8859-6': 'iso-8859-6', + 'iso-8859-6-e': 'iso-8859-6', + 'iso-8859-6-i': 'iso-8859-6', + 'iso-ir-127': 'iso-8859-6', + 'iso8859-6': 'iso-8859-6', + 'iso88596': 'iso-8859-6', + 'iso_8859-6': 'iso-8859-6', + 'iso_8859-6:1987': 'iso-8859-6', + 'csisolatingreek': 'iso-8859-7', + 'ecma-118': 'iso-8859-7', + 'elot_928': 'iso-8859-7', + 'greek': 'iso-8859-7', + 'greek8': 'iso-8859-7', + 'iso-8859-7': 'iso-8859-7', + 'iso-ir-126': 'iso-8859-7', + 'iso8859-7': 'iso-8859-7', + 'iso88597': 'iso-8859-7', + 'iso_8859-7': 'iso-8859-7', + 'iso_8859-7:1987': 'iso-8859-7', + 'sun_eu_greek': 'iso-8859-7', + 'csiso88598e': 'iso-8859-8', + 'csisolatinhebrew': 'iso-8859-8', + 'hebrew': 'iso-8859-8', + 'iso-8859-8': 'iso-8859-8', + 'iso-8859-8-e': 'iso-8859-8', + 'iso-ir-138': 'iso-8859-8', + 'iso8859-8': 'iso-8859-8', + 'iso88598': 'iso-8859-8', + 'iso_8859-8': 'iso-8859-8', + 'iso_8859-8:1988': 'iso-8859-8', + 'visual': 'iso-8859-8', + 'csiso88598i': 'iso-8859-8-i', + 'iso-8859-8-i': 'iso-8859-8-i', + 'logical': 'iso-8859-8-i', + 'csisolatin6': 'iso-8859-10', + 'iso-8859-10': 'iso-8859-10', + 'iso-ir-157': 'iso-8859-10', + 'iso8859-10': 'iso-8859-10', + 'iso885910': 'iso-8859-10', + 'l6': 'iso-8859-10', + 'latin6': 'iso-8859-10', + 'iso-8859-13': 'iso-8859-13', + 'iso8859-13': 'iso-8859-13', + 'iso885913': 'iso-8859-13', + 'iso-8859-14': 'iso-8859-14', + 'iso8859-14': 'iso-8859-14', + 'iso885914': 'iso-8859-14', + 'csisolatin9': 'iso-8859-15', + 'iso-8859-15': 'iso-8859-15', + 'iso8859-15': 'iso-8859-15', + 'iso885915': 'iso-8859-15', + 'iso_8859-15': 'iso-8859-15', + 'l9': 'iso-8859-15', + 'iso-8859-16': 'iso-8859-16', + 'cskoi8r': 'koi8-r', + 'koi': 'koi8-r', + 'koi8': 'koi8-r', + 'koi8-r': 'koi8-r', + 'koi8_r': 'koi8-r', + 'koi8-u': 'koi8-u', + 'csmacintosh': 'macintosh', + 'mac': 'macintosh', + 'macintosh': 'macintosh', + 'x-mac-roman': 'macintosh', + 'dos-874': 'windows-874', + 'iso-8859-11': 'windows-874', + 'iso8859-11': 'windows-874', + 'iso885911': 'windows-874', + 'tis-620': 'windows-874', + 'windows-874': 'windows-874', + 'cp1250': 'windows-1250', + 'windows-1250': 'windows-1250', + 'x-cp1250': 'windows-1250', + 'cp1251': 'windows-1251', + 'windows-1251': 'windows-1251', + 'x-cp1251': 'windows-1251', + 'ansi_x3.4-1968': 'windows-1252', + 'ascii': 'windows-1252', + 'cp1252': 'windows-1252', + 'cp819': 'windows-1252', + 'csisolatin1': 'windows-1252', + 'ibm819': 'windows-1252', + 'iso-8859-1': 'windows-1252', + 'iso-ir-100': 'windows-1252', + 'iso8859-1': 'windows-1252', + 'iso88591': 'windows-1252', + 'iso_8859-1': 'windows-1252', + 'iso_8859-1:1987': 'windows-1252', + 'l1': 'windows-1252', + 'latin1': 'windows-1252', + 'us-ascii': 'windows-1252', + 'windows-1252': 'windows-1252', + 'x-cp1252': 'windows-1252', + 'cp1253': 'windows-1253', + 'windows-1253': 'windows-1253', + 'x-cp1253': 'windows-1253', + 'cp1254': 'windows-1254', + 'csisolatin5': 'windows-1254', + 'iso-8859-9': 'windows-1254', + 'iso-ir-148': 'windows-1254', + 'iso8859-9': 'windows-1254', + 'iso88599': 'windows-1254', + 'iso_8859-9': 'windows-1254', + 'iso_8859-9:1989': 'windows-1254', + 'l5': 'windows-1254', + 'latin5': 'windows-1254', + 'windows-1254': 'windows-1254', + 'x-cp1254': 'windows-1254', + 'cp1255': 'windows-1255', + 'windows-1255': 'windows-1255', + 'x-cp1255': 'windows-1255', + 'cp1256': 'windows-1256', + 'windows-1256': 'windows-1256', + 'x-cp1256': 'windows-1256', + 'cp1257': 'windows-1257', + 'windows-1257': 'windows-1257', + 'x-cp1257': 'windows-1257', + 'cp1258': 'windows-1258', + 'windows-1258': 'windows-1258', + 'x-cp1258': 'windows-1258', + 'x-mac-cyrillic': 'x-mac-cyrillic', + 'x-mac-ukrainian': 'x-mac-cyrillic', + 'chinese': 'gbk', + 'csgb2312': 'gbk', + 'csiso58gb231280': 'gbk', + 'gb2312': 'gbk', + 'gb_2312': 'gbk', + 'gb_2312-80': 'gbk', + 'gbk': 'gbk', + 'iso-ir-58': 'gbk', + 'x-gbk': 'gbk', + 'gb18030': 'gb18030', + 'hz-gb-2312': 'hz-gb-2312', + 'big5': 'big5', + 'big5-hkscs': 'big5', + 'cn-big5': 'big5', + 'csbig5': 'big5', + 'x-x-big5': 'big5', + 'cseucpkdfmtjapanese': 'euc-jp', + 'euc-jp': 'euc-jp', + 'x-euc-jp': 'euc-jp', + 'csiso2022jp': 'iso-2022-jp', + 'iso-2022-jp': 'iso-2022-jp', + 'csshiftjis': 'shift_jis', + 'ms_kanji': 'shift_jis', + 'shift-jis': 'shift_jis', + 'shift_jis': 'shift_jis', + 'sjis': 'shift_jis', + 'windows-31j': 'shift_jis', + 'x-sjis': 'shift_jis', + 'cseuckr': 'euc-kr', + 'csksc56011987': 'euc-kr', + 'euc-kr': 'euc-kr', + 'iso-ir-149': 'euc-kr', + 'korean': 'euc-kr', + 'ks_c_5601-1987': 'euc-kr', + 'ks_c_5601-1989': 'euc-kr', + 'ksc5601': 'euc-kr', + 'ksc_5601': 'euc-kr', + 'windows-949': 'euc-kr', + 'csiso2022kr': 'iso-2022-kr', + 'iso-2022-kr': 'iso-2022-kr', + 'utf-16be': 'utf-16be', + 'utf-16': 'utf-16le', + 'utf-16le': 'utf-16le', + 'x-user-defined': 'x-user-defined', +} diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/mklabels.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/mklabels.py new file mode 100644 index 0000000..295dc92 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/mklabels.py @@ -0,0 +1,59 @@ +""" + + webencodings.mklabels + ~~~~~~~~~~~~~~~~~~~~~ + + Regenarate the webencodings.labels module. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +import json +try: + from urllib import urlopen +except ImportError: + from urllib.request import urlopen + + +def assert_lower(string): + assert string == string.lower() + return string + + +def generate(url): + parts = ['''\ +""" + + webencodings.labels + ~~~~~~~~~~~~~~~~~~~ + + Map encoding labels to their name. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +# XXX Do not edit! +# This file is automatically generated by mklabels.py + +LABELS = { +'''] + labels = [ + (repr(assert_lower(label)).lstrip('u'), + repr(encoding['name']).lstrip('u')) + for category in json.loads(urlopen(url).read().decode('ascii')) + for encoding in category['encodings'] + for label in encoding['labels']] + max_len = max(len(label) for label, name in labels) + parts.extend( + ' %s:%s %s,\n' % (label, ' ' * (max_len - len(label)), name) + for label, name in labels) + parts.append('}') + return ''.join(parts) + + +if __name__ == '__main__': + print(generate('http://encoding.spec.whatwg.org/encodings.json')) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/tests.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/tests.py new file mode 100644 index 0000000..b8c5653 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/tests.py @@ -0,0 +1,153 @@ +# coding: utf8 +""" + + webencodings.tests + ~~~~~~~~~~~~~~~~~~ + + A basic test suite for Encoding. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +from __future__ import unicode_literals + +from . import (lookup, LABELS, decode, encode, iter_decode, iter_encode, + IncrementalDecoder, IncrementalEncoder, UTF8) + + +def assert_raises(exception, function, *args, **kwargs): + try: + function(*args, **kwargs) + except exception: + return + else: # pragma: no cover + raise AssertionError('Did not raise %s.' % exception) + + +def test_labels(): + assert lookup('utf-8').name == 'utf-8' + assert lookup('Utf-8').name == 'utf-8' + assert lookup('UTF-8').name == 'utf-8' + assert lookup('utf8').name == 'utf-8' + assert lookup('utf8').name == 'utf-8' + assert lookup('utf8 ').name == 'utf-8' + assert lookup(' \r\nutf8\t').name == 'utf-8' + assert lookup('u8') is None # Python label. + assert lookup('utf-8 ') is None # Non-ASCII white space. + + assert lookup('US-ASCII').name == 'windows-1252' + assert lookup('iso-8859-1').name == 'windows-1252' + assert lookup('latin1').name == 'windows-1252' + assert lookup('LATIN1').name == 'windows-1252' + assert lookup('latin-1') is None + assert lookup('LATİN1') is None # ASCII-only case insensitivity. + + +def test_all_labels(): + for label in LABELS: + assert decode(b'', label) == ('', lookup(label)) + assert encode('', label) == b'' + for repeat in [0, 1, 12]: + output, _ = iter_decode([b''] * repeat, label) + assert list(output) == [] + assert list(iter_encode([''] * repeat, label)) == [] + decoder = IncrementalDecoder(label) + assert decoder.decode(b'') == '' + assert decoder.decode(b'', final=True) == '' + encoder = IncrementalEncoder(label) + assert encoder.encode('') == b'' + assert encoder.encode('', final=True) == b'' + # All encoding names are valid labels too: + for name in set(LABELS.values()): + assert lookup(name).name == name + + +def test_invalid_label(): + assert_raises(LookupError, decode, b'\xEF\xBB\xBF\xc3\xa9', 'invalid') + assert_raises(LookupError, encode, 'é', 'invalid') + assert_raises(LookupError, iter_decode, [], 'invalid') + assert_raises(LookupError, iter_encode, [], 'invalid') + assert_raises(LookupError, IncrementalDecoder, 'invalid') + assert_raises(LookupError, IncrementalEncoder, 'invalid') + + +def test_decode(): + assert decode(b'\x80', 'latin1') == ('€', lookup('latin1')) + assert decode(b'\x80', lookup('latin1')) == ('€', lookup('latin1')) + assert decode(b'\xc3\xa9', 'utf8') == ('é', lookup('utf8')) + assert decode(b'\xc3\xa9', UTF8) == ('é', lookup('utf8')) + assert decode(b'\xc3\xa9', 'ascii') == ('é', lookup('ascii')) + assert decode(b'\xEF\xBB\xBF\xc3\xa9', 'ascii') == ('é', lookup('utf8')) # UTF-8 with BOM + + assert decode(b'\xFE\xFF\x00\xe9', 'ascii') == ('é', lookup('utf-16be')) # UTF-16-BE with BOM + assert decode(b'\xFF\xFE\xe9\x00', 'ascii') == ('é', lookup('utf-16le')) # UTF-16-LE with BOM + assert decode(b'\xFE\xFF\xe9\x00', 'ascii') == ('\ue900', lookup('utf-16be')) + assert decode(b'\xFF\xFE\x00\xe9', 'ascii') == ('\ue900', lookup('utf-16le')) + + assert decode(b'\x00\xe9', 'UTF-16BE') == ('é', lookup('utf-16be')) + assert decode(b'\xe9\x00', 'UTF-16LE') == ('é', lookup('utf-16le')) + assert decode(b'\xe9\x00', 'UTF-16') == ('é', lookup('utf-16le')) + + assert decode(b'\xe9\x00', 'UTF-16BE') == ('\ue900', lookup('utf-16be')) + assert decode(b'\x00\xe9', 'UTF-16LE') == ('\ue900', lookup('utf-16le')) + assert decode(b'\x00\xe9', 'UTF-16') == ('\ue900', lookup('utf-16le')) + + +def test_encode(): + assert encode('é', 'latin1') == b'\xe9' + assert encode('é', 'utf8') == b'\xc3\xa9' + assert encode('é', 'utf8') == b'\xc3\xa9' + assert encode('é', 'utf-16') == b'\xe9\x00' + assert encode('é', 'utf-16le') == b'\xe9\x00' + assert encode('é', 'utf-16be') == b'\x00\xe9' + + +def test_iter_decode(): + def iter_decode_to_string(input, fallback_encoding): + output, _encoding = iter_decode(input, fallback_encoding) + return ''.join(output) + assert iter_decode_to_string([], 'latin1') == '' + assert iter_decode_to_string([b''], 'latin1') == '' + assert iter_decode_to_string([b'\xe9'], 'latin1') == 'é' + assert iter_decode_to_string([b'hello'], 'latin1') == 'hello' + assert iter_decode_to_string([b'he', b'llo'], 'latin1') == 'hello' + assert iter_decode_to_string([b'hell', b'o'], 'latin1') == 'hello' + assert iter_decode_to_string([b'\xc3\xa9'], 'latin1') == 'é' + assert iter_decode_to_string([b'\xEF\xBB\xBF\xc3\xa9'], 'latin1') == 'é' + assert iter_decode_to_string([ + b'\xEF\xBB\xBF', b'\xc3', b'\xa9'], 'latin1') == 'é' + assert iter_decode_to_string([ + b'\xEF\xBB\xBF', b'a', b'\xc3'], 'latin1') == 'a\uFFFD' + assert iter_decode_to_string([ + b'', b'\xEF', b'', b'', b'\xBB\xBF\xc3', b'\xa9'], 'latin1') == 'é' + assert iter_decode_to_string([b'\xEF\xBB\xBF'], 'latin1') == '' + assert iter_decode_to_string([b'\xEF\xBB'], 'latin1') == 'ï»' + assert iter_decode_to_string([b'\xFE\xFF\x00\xe9'], 'latin1') == 'é' + assert iter_decode_to_string([b'\xFF\xFE\xe9\x00'], 'latin1') == 'é' + assert iter_decode_to_string([ + b'', b'\xFF', b'', b'', b'\xFE\xe9', b'\x00'], 'latin1') == 'é' + assert iter_decode_to_string([ + b'', b'h\xe9', b'llo'], 'x-user-defined') == 'h\uF7E9llo' + + +def test_iter_encode(): + assert b''.join(iter_encode([], 'latin1')) == b'' + assert b''.join(iter_encode([''], 'latin1')) == b'' + assert b''.join(iter_encode(['é'], 'latin1')) == b'\xe9' + assert b''.join(iter_encode(['', 'é', '', ''], 'latin1')) == b'\xe9' + assert b''.join(iter_encode(['', 'é', '', ''], 'utf-16')) == b'\xe9\x00' + assert b''.join(iter_encode(['', 'é', '', ''], 'utf-16le')) == b'\xe9\x00' + assert b''.join(iter_encode(['', 'é', '', ''], 'utf-16be')) == b'\x00\xe9' + assert b''.join(iter_encode([ + '', 'h\uF7E9', '', 'llo'], 'x-user-defined')) == b'h\xe9llo' + + +def test_x_user_defined(): + encoded = b'2,\x0c\x0b\x1aO\xd9#\xcb\x0f\xc9\xbbt\xcf\xa8\xca' + decoded = '2,\x0c\x0b\x1aO\uf7d9#\uf7cb\x0f\uf7c9\uf7bbt\uf7cf\uf7a8\uf7ca' + encoded = b'aa' + decoded = 'aa' + assert decode(encoded, 'x-user-defined') == (decoded, lookup('x-user-defined')) + assert encode(decoded, 'x-user-defined') == encoded diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/x_user_defined.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/x_user_defined.py new file mode 100644 index 0000000..f0daa11 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/x_user_defined.py @@ -0,0 +1,325 @@ +# coding: utf8 +""" + + webencodings.x_user_defined + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + An implementation of the x-user-defined encoding. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +from __future__ import unicode_literals + +import codecs + + +### Codec APIs + +class Codec(codecs.Codec): + + def encode(self, input, errors='strict'): + return codecs.charmap_encode(input, errors, encoding_table) + + def decode(self, input, errors='strict'): + return codecs.charmap_decode(input, errors, decoding_table) + + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input, self.errors, encoding_table)[0] + + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input, self.errors, decoding_table)[0] + + +class StreamWriter(Codec, codecs.StreamWriter): + pass + + +class StreamReader(Codec, codecs.StreamReader): + pass + + +### encodings module API + +codec_info = codecs.CodecInfo( + name='x-user-defined', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, +) + + +### Decoding Table + +# Python 3: +# for c in range(256): print(' %r' % chr(c if c < 128 else c + 0xF700)) +decoding_table = ( + '\x00' + '\x01' + '\x02' + '\x03' + '\x04' + '\x05' + '\x06' + '\x07' + '\x08' + '\t' + '\n' + '\x0b' + '\x0c' + '\r' + '\x0e' + '\x0f' + '\x10' + '\x11' + '\x12' + '\x13' + '\x14' + '\x15' + '\x16' + '\x17' + '\x18' + '\x19' + '\x1a' + '\x1b' + '\x1c' + '\x1d' + '\x1e' + '\x1f' + ' ' + '!' + '"' + '#' + '$' + '%' + '&' + "'" + '(' + ')' + '*' + '+' + ',' + '-' + '.' + '/' + '0' + '1' + '2' + '3' + '4' + '5' + '6' + '7' + '8' + '9' + ':' + ';' + '<' + '=' + '>' + '?' + '@' + 'A' + 'B' + 'C' + 'D' + 'E' + 'F' + 'G' + 'H' + 'I' + 'J' + 'K' + 'L' + 'M' + 'N' + 'O' + 'P' + 'Q' + 'R' + 'S' + 'T' + 'U' + 'V' + 'W' + 'X' + 'Y' + 'Z' + '[' + '\\' + ']' + '^' + '_' + '`' + 'a' + 'b' + 'c' + 'd' + 'e' + 'f' + 'g' + 'h' + 'i' + 'j' + 'k' + 'l' + 'm' + 'n' + 'o' + 'p' + 'q' + 'r' + 's' + 't' + 'u' + 'v' + 'w' + 'x' + 'y' + 'z' + '{' + '|' + '}' + '~' + '\x7f' + '\uf780' + '\uf781' + '\uf782' + '\uf783' + '\uf784' + '\uf785' + '\uf786' + '\uf787' + '\uf788' + '\uf789' + '\uf78a' + '\uf78b' + '\uf78c' + '\uf78d' + '\uf78e' + '\uf78f' + '\uf790' + '\uf791' + '\uf792' + '\uf793' + '\uf794' + '\uf795' + '\uf796' + '\uf797' + '\uf798' + '\uf799' + '\uf79a' + '\uf79b' + '\uf79c' + '\uf79d' + '\uf79e' + '\uf79f' + '\uf7a0' + '\uf7a1' + '\uf7a2' + '\uf7a3' + '\uf7a4' + '\uf7a5' + '\uf7a6' + '\uf7a7' + '\uf7a8' + '\uf7a9' + '\uf7aa' + '\uf7ab' + '\uf7ac' + '\uf7ad' + '\uf7ae' + '\uf7af' + '\uf7b0' + '\uf7b1' + '\uf7b2' + '\uf7b3' + '\uf7b4' + '\uf7b5' + '\uf7b6' + '\uf7b7' + '\uf7b8' + '\uf7b9' + '\uf7ba' + '\uf7bb' + '\uf7bc' + '\uf7bd' + '\uf7be' + '\uf7bf' + '\uf7c0' + '\uf7c1' + '\uf7c2' + '\uf7c3' + '\uf7c4' + '\uf7c5' + '\uf7c6' + '\uf7c7' + '\uf7c8' + '\uf7c9' + '\uf7ca' + '\uf7cb' + '\uf7cc' + '\uf7cd' + '\uf7ce' + '\uf7cf' + '\uf7d0' + '\uf7d1' + '\uf7d2' + '\uf7d3' + '\uf7d4' + '\uf7d5' + '\uf7d6' + '\uf7d7' + '\uf7d8' + '\uf7d9' + '\uf7da' + '\uf7db' + '\uf7dc' + '\uf7dd' + '\uf7de' + '\uf7df' + '\uf7e0' + '\uf7e1' + '\uf7e2' + '\uf7e3' + '\uf7e4' + '\uf7e5' + '\uf7e6' + '\uf7e7' + '\uf7e8' + '\uf7e9' + '\uf7ea' + '\uf7eb' + '\uf7ec' + '\uf7ed' + '\uf7ee' + '\uf7ef' + '\uf7f0' + '\uf7f1' + '\uf7f2' + '\uf7f3' + '\uf7f4' + '\uf7f5' + '\uf7f6' + '\uf7f7' + '\uf7f8' + '\uf7f9' + '\uf7fa' + '\uf7fb' + '\uf7fc' + '\uf7fd' + '\uf7fe' + '\uf7ff' +) + +### Encoding table +encoding_table = codecs.charmap_build(decoding_table) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/basecommand.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/basecommand.py new file mode 100644 index 0000000..54c6706 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/basecommand.py @@ -0,0 +1,337 @@ +"""Base Command class, and related routines""" +from __future__ import absolute_import + +import logging +import os +import sys +import optparse +import warnings + +from pip import cmdoptions +from pip.index import PackageFinder +from pip.locations import running_under_virtualenv +from pip.download import PipSession +from pip.exceptions import (BadCommand, InstallationError, UninstallationError, + CommandError, PreviousBuildDirError) + +from pip.compat import logging_dictConfig +from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter +from pip.req import InstallRequirement, parse_requirements +from pip.status_codes import ( + SUCCESS, ERROR, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND, + PREVIOUS_BUILD_DIR_ERROR, +) +from pip.utils import deprecation, get_prog, normalize_path +from pip.utils.logging import IndentingFormatter +from pip.utils.outdated import pip_version_check + + +__all__ = ['Command'] + + +logger = logging.getLogger(__name__) + + +class Command(object): + name = None + usage = None + hidden = False + log_streams = ("ext://sys.stdout", "ext://sys.stderr") + + def __init__(self, isolated=False): + parser_kw = { + 'usage': self.usage, + 'prog': '%s %s' % (get_prog(), self.name), + 'formatter': UpdatingDefaultsHelpFormatter(), + 'add_help_option': False, + 'name': self.name, + 'description': self.__doc__, + 'isolated': isolated, + } + + self.parser = ConfigOptionParser(**parser_kw) + + # Commands should add options to this option group + optgroup_name = '%s Options' % self.name.capitalize() + self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name) + + # Add the general options + gen_opts = cmdoptions.make_option_group( + cmdoptions.general_group, + self.parser, + ) + self.parser.add_option_group(gen_opts) + + def _build_session(self, options, retries=None, timeout=None): + session = PipSession( + cache=( + normalize_path(os.path.join(options.cache_dir, "http")) + if options.cache_dir else None + ), + retries=retries if retries is not None else options.retries, + insecure_hosts=options.trusted_hosts, + ) + + # Handle custom ca-bundles from the user + if options.cert: + session.verify = options.cert + + # Handle SSL client certificate + if options.client_cert: + session.cert = options.client_cert + + # Handle timeouts + if options.timeout or timeout: + session.timeout = ( + timeout if timeout is not None else options.timeout + ) + + # Handle configured proxies + if options.proxy: + session.proxies = { + "http": options.proxy, + "https": options.proxy, + } + + # Determine if we can prompt the user for authentication or not + session.auth.prompting = not options.no_input + + return session + + def parse_args(self, args): + # factored out for testability + return self.parser.parse_args(args) + + def main(self, args): + options, args = self.parse_args(args) + + if options.quiet: + if options.quiet == 1: + level = "WARNING" + if options.quiet == 2: + level = "ERROR" + else: + level = "CRITICAL" + elif options.verbose: + level = "DEBUG" + else: + level = "INFO" + + # The root logger should match the "console" level *unless* we + # specified "--log" to send debug logs to a file. + root_level = level + if options.log: + root_level = "DEBUG" + + logging_dictConfig({ + "version": 1, + "disable_existing_loggers": False, + "filters": { + "exclude_warnings": { + "()": "pip.utils.logging.MaxLevelFilter", + "level": logging.WARNING, + }, + }, + "formatters": { + "indent": { + "()": IndentingFormatter, + "format": "%(message)s", + }, + }, + "handlers": { + "console": { + "level": level, + "class": "pip.utils.logging.ColorizedStreamHandler", + "stream": self.log_streams[0], + "filters": ["exclude_warnings"], + "formatter": "indent", + }, + "console_errors": { + "level": "WARNING", + "class": "pip.utils.logging.ColorizedStreamHandler", + "stream": self.log_streams[1], + "formatter": "indent", + }, + "user_log": { + "level": "DEBUG", + "class": "pip.utils.logging.BetterRotatingFileHandler", + "filename": options.log or "/dev/null", + "delay": True, + "formatter": "indent", + }, + }, + "root": { + "level": root_level, + "handlers": list(filter(None, [ + "console", + "console_errors", + "user_log" if options.log else None, + ])), + }, + # Disable any logging besides WARNING unless we have DEBUG level + # logging enabled. These use both pip._vendor and the bare names + # for the case where someone unbundles our libraries. + "loggers": dict( + ( + name, + { + "level": ( + "WARNING" + if level in ["INFO", "ERROR"] + else "DEBUG" + ), + }, + ) + for name in ["pip._vendor", "distlib", "requests", "urllib3"] + ), + }) + + if sys.version_info[:2] == (2, 6): + warnings.warn( + "Python 2.6 is no longer supported by the Python core team, " + "please upgrade your Python. A future version of pip will " + "drop support for Python 2.6", + deprecation.Python26DeprecationWarning + ) + + # TODO: try to get these passing down from the command? + # without resorting to os.environ to hold these. + + if options.no_input: + os.environ['PIP_NO_INPUT'] = '1' + + if options.exists_action: + os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action) + + if options.require_venv: + # If a venv is required check if it can really be found + if not running_under_virtualenv(): + logger.critical( + 'Could not find an activated virtualenv (required).' + ) + sys.exit(VIRTUALENV_NOT_FOUND) + + try: + status = self.run(options, args) + # FIXME: all commands should return an exit status + # and when it is done, isinstance is not needed anymore + if isinstance(status, int): + return status + except PreviousBuildDirError as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return PREVIOUS_BUILD_DIR_ERROR + except (InstallationError, UninstallationError, BadCommand) as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except CommandError as exc: + logger.critical('ERROR: %s', exc) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except KeyboardInterrupt: + logger.critical('Operation cancelled by user') + logger.debug('Exception information:', exc_info=True) + + return ERROR + except: + logger.critical('Exception:', exc_info=True) + + return UNKNOWN_ERROR + finally: + # Check if we're using the latest version of pip available + if (not options.disable_pip_version_check and not + getattr(options, "no_index", False)): + with self._build_session( + options, + retries=0, + timeout=min(5, options.timeout)) as session: + pip_version_check(session) + + return SUCCESS + + +class RequirementCommand(Command): + + @staticmethod + def populate_requirement_set(requirement_set, args, options, finder, + session, name, wheel_cache): + """ + Marshal cmd line args into a requirement set. + """ + for filename in options.constraints: + for req in parse_requirements( + filename, + constraint=True, finder=finder, options=options, + session=session, wheel_cache=wheel_cache): + requirement_set.add_requirement(req) + + for req in args: + requirement_set.add_requirement( + InstallRequirement.from_line( + req, None, isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + ) + + for req in options.editables: + requirement_set.add_requirement( + InstallRequirement.from_editable( + req, + default_vcs=options.default_vcs, + isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + ) + + found_req_in_file = False + for filename in options.requirements: + for req in parse_requirements( + filename, + finder=finder, options=options, session=session, + wheel_cache=wheel_cache): + found_req_in_file = True + requirement_set.add_requirement(req) + # If --require-hashes was a line in a requirements file, tell + # RequirementSet about it: + requirement_set.require_hashes = options.require_hashes + + if not (args or options.editables or found_req_in_file): + opts = {'name': name} + if options.find_links: + msg = ('You must give at least one requirement to ' + '%(name)s (maybe you meant "pip %(name)s ' + '%(links)s"?)' % + dict(opts, links=' '.join(options.find_links))) + else: + msg = ('You must give at least one requirement ' + 'to %(name)s (see "pip help %(name)s")' % opts) + logger.warning(msg) + + def _build_package_finder(self, options, session, + platform=None, python_versions=None, + abi=None, implementation=None): + """ + Create a package finder appropriate to this requirement command. + """ + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + return PackageFinder( + find_links=options.find_links, + format_control=options.format_control, + index_urls=index_urls, + trusted_hosts=options.trusted_hosts, + allow_all_prereleases=options.pre, + process_dependency_links=options.process_dependency_links, + session=session, + platform=platform, + versions=python_versions, + abi=abi, + implementation=implementation, + ) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/baseparser.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/baseparser.py new file mode 100644 index 0000000..2dd4533 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/baseparser.py @@ -0,0 +1,293 @@ +"""Base option parser setup""" +from __future__ import absolute_import + +import sys +import optparse +import os +import re +import textwrap +from distutils.util import strtobool + +from pip._vendor.six import string_types +from pip._vendor.six.moves import configparser +from pip.locations import ( + legacy_config_file, config_basename, running_under_virtualenv, + site_config_files +) +from pip.utils import appdirs, get_terminal_size + + +_environ_prefix_re = re.compile(r"^PIP_", re.I) + + +class PrettyHelpFormatter(optparse.IndentedHelpFormatter): + """A prettier/less verbose help formatter for optparse.""" + + def __init__(self, *args, **kwargs): + # help position must be aligned with __init__.parseopts.description + kwargs['max_help_position'] = 30 + kwargs['indent_increment'] = 1 + kwargs['width'] = get_terminal_size()[0] - 2 + optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs) + + def format_option_strings(self, option): + return self._format_option_strings(option, ' <%s>', ', ') + + def _format_option_strings(self, option, mvarfmt=' <%s>', optsep=', '): + """ + Return a comma-separated list of option strings and metavars. + + :param option: tuple of (short opt, long opt), e.g: ('-f', '--format') + :param mvarfmt: metavar format string - evaluated as mvarfmt % metavar + :param optsep: separator + """ + opts = [] + + if option._short_opts: + opts.append(option._short_opts[0]) + if option._long_opts: + opts.append(option._long_opts[0]) + if len(opts) > 1: + opts.insert(1, optsep) + + if option.takes_value(): + metavar = option.metavar or option.dest.lower() + opts.append(mvarfmt % metavar.lower()) + + return ''.join(opts) + + def format_heading(self, heading): + if heading == 'Options': + return '' + return heading + ':\n' + + def format_usage(self, usage): + """ + Ensure there is only one newline between usage and the first heading + if there is no description. + """ + msg = '\nUsage: %s\n' % self.indent_lines(textwrap.dedent(usage), " ") + return msg + + def format_description(self, description): + # leave full control over description to us + if description: + if hasattr(self.parser, 'main'): + label = 'Commands' + else: + label = 'Description' + # some doc strings have initial newlines, some don't + description = description.lstrip('\n') + # some doc strings have final newlines and spaces, some don't + description = description.rstrip() + # dedent, then reindent + description = self.indent_lines(textwrap.dedent(description), " ") + description = '%s:\n%s\n' % (label, description) + return description + else: + return '' + + def format_epilog(self, epilog): + # leave full control over epilog to us + if epilog: + return epilog + else: + return '' + + def indent_lines(self, text, indent): + new_lines = [indent + line for line in text.split('\n')] + return "\n".join(new_lines) + + +class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): + """Custom help formatter for use in ConfigOptionParser. + + This is updates the defaults before expanding them, allowing + them to show up correctly in the help listing. + """ + + def expand_default(self, option): + if self.parser is not None: + self.parser._update_defaults(self.parser.defaults) + return optparse.IndentedHelpFormatter.expand_default(self, option) + + +class CustomOptionParser(optparse.OptionParser): + + def insert_option_group(self, idx, *args, **kwargs): + """Insert an OptionGroup at a given position.""" + group = self.add_option_group(*args, **kwargs) + + self.option_groups.pop() + self.option_groups.insert(idx, group) + + return group + + @property + def option_list_all(self): + """Get a list of all options, including those in option groups.""" + res = self.option_list[:] + for i in self.option_groups: + res.extend(i.option_list) + + return res + + +class ConfigOptionParser(CustomOptionParser): + """Custom option parser which updates its defaults by checking the + configuration files and environmental variables""" + + isolated = False + + def __init__(self, *args, **kwargs): + self.config = configparser.RawConfigParser() + self.name = kwargs.pop('name') + self.isolated = kwargs.pop("isolated", False) + self.files = self.get_config_files() + if self.files: + self.config.read(self.files) + assert self.name + optparse.OptionParser.__init__(self, *args, **kwargs) + + def get_config_files(self): + # the files returned by this method will be parsed in order with the + # first files listed being overridden by later files in standard + # ConfigParser fashion + config_file = os.environ.get('PIP_CONFIG_FILE', False) + if config_file == os.devnull: + return [] + + # at the base we have any site-wide configuration + files = list(site_config_files) + + # per-user configuration next + if not self.isolated: + if config_file and os.path.exists(config_file): + files.append(config_file) + else: + # This is the legacy config file, we consider it to be a lower + # priority than the new file location. + files.append(legacy_config_file) + + # This is the new config file, we consider it to be a higher + # priority than the legacy file. + files.append( + os.path.join( + appdirs.user_config_dir("pip"), + config_basename, + ) + ) + + # finally virtualenv configuration first trumping others + if running_under_virtualenv(): + venv_config_file = os.path.join( + sys.prefix, + config_basename, + ) + if os.path.exists(venv_config_file): + files.append(venv_config_file) + + return files + + def check_default(self, option, key, val): + try: + return option.check_value(key, val) + except optparse.OptionValueError as exc: + print("An error occurred during configuration: %s" % exc) + sys.exit(3) + + def _update_defaults(self, defaults): + """Updates the given defaults with values from the config files and + the environ. Does a little special handling for certain types of + options (lists).""" + # Then go and look for the other sources of configuration: + config = {} + # 1. config files + for section in ('global', self.name): + config.update( + self.normalize_keys(self.get_config_section(section)) + ) + # 2. environmental variables + if not self.isolated: + config.update(self.normalize_keys(self.get_environ_vars())) + # Accumulate complex default state. + self.values = optparse.Values(self.defaults) + late_eval = set() + # Then set the options with those values + for key, val in config.items(): + # ignore empty values + if not val: + continue + + option = self.get_option(key) + # Ignore options not present in this parser. E.g. non-globals put + # in [global] by users that want them to apply to all applicable + # commands. + if option is None: + continue + + if option.action in ('store_true', 'store_false', 'count'): + val = strtobool(val) + elif option.action == 'append': + val = val.split() + val = [self.check_default(option, key, v) for v in val] + elif option.action == 'callback': + late_eval.add(option.dest) + opt_str = option.get_opt_string() + val = option.convert_value(opt_str, val) + # From take_action + args = option.callback_args or () + kwargs = option.callback_kwargs or {} + option.callback(option, opt_str, val, self, *args, **kwargs) + else: + val = self.check_default(option, key, val) + + defaults[option.dest] = val + + for key in late_eval: + defaults[key] = getattr(self.values, key) + self.values = None + return defaults + + def normalize_keys(self, items): + """Return a config dictionary with normalized keys regardless of + whether the keys were specified in environment variables or in config + files""" + normalized = {} + for key, val in items: + key = key.replace('_', '-') + if not key.startswith('--'): + key = '--%s' % key # only prefer long opts + normalized[key] = val + return normalized + + def get_config_section(self, name): + """Get a section of a configuration""" + if self.config.has_section(name): + return self.config.items(name) + return [] + + def get_environ_vars(self): + """Returns a generator with all environmental vars with prefix PIP_""" + for key, val in os.environ.items(): + if _environ_prefix_re.search(key): + yield (_environ_prefix_re.sub("", key).lower(), val) + + def get_default_values(self): + """Overriding to make updating the defaults after instantiation of + the option parser possible, _update_defaults() does the dirty work.""" + if not self.process_default_values: + # Old, pre-Optik 1.5 behaviour. + return optparse.Values(self.defaults) + + defaults = self._update_defaults(self.defaults.copy()) # ours + for option in self._get_all_options(): + default = defaults.get(option.dest) + if isinstance(default, string_types): + opt_str = option.get_opt_string() + defaults[option.dest] = option.check_value(opt_str, default) + return optparse.Values(defaults) + + def error(self, msg): + self.print_usage(sys.stderr) + self.exit(2, "%s\n" % msg) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/cmdoptions.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/cmdoptions.py new file mode 100644 index 0000000..f71488c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/cmdoptions.py @@ -0,0 +1,633 @@ +""" +shared options and groups + +The principle here is to define options once, but *not* instantiate them +globally. One reason being that options with action='append' can carry state +between parses. pip parses general options twice internally, and shouldn't +pass on state. To be consistent, all options will follow this design. + +""" +from __future__ import absolute_import + +from functools import partial +from optparse import OptionGroup, SUPPRESS_HELP, Option +import warnings + +from pip.index import ( + FormatControl, fmt_ctl_handle_mutual_exclude, fmt_ctl_no_binary, + fmt_ctl_no_use_wheel) +from pip.models import PyPI +from pip.locations import USER_CACHE_DIR, src_prefix +from pip.utils.hashes import STRONG_HASHES + + +def make_option_group(group, parser): + """ + Return an OptionGroup object + group -- assumed to be dict with 'name' and 'options' keys + parser -- an optparse Parser + """ + option_group = OptionGroup(parser, group['name']) + for option in group['options']: + option_group.add_option(option()) + return option_group + + +def resolve_wheel_no_use_binary(options): + if not options.use_wheel: + control = options.format_control + fmt_ctl_no_use_wheel(control) + + +def check_install_build_global(options, check_options=None): + """Disable wheels if per-setup.py call options are set. + + :param options: The OptionParser options to update. + :param check_options: The options to check, if not supplied defaults to + options. + """ + if check_options is None: + check_options = options + + def getname(n): + return getattr(check_options, n, None) + names = ["build_options", "global_options", "install_options"] + if any(map(getname, names)): + control = options.format_control + fmt_ctl_no_binary(control) + warnings.warn( + 'Disabling all use of wheels due to the use of --build-options ' + '/ --global-options / --install-options.', stacklevel=2) + + +########### +# options # +########### + +help_ = partial( + Option, + '-h', '--help', + dest='help', + action='help', + help='Show help.') + +isolated_mode = partial( + Option, + "--isolated", + dest="isolated_mode", + action="store_true", + default=False, + help=( + "Run pip in an isolated mode, ignoring environment variables and user " + "configuration." + ), +) + +require_virtualenv = partial( + Option, + # Run only if inside a virtualenv, bail if not. + '--require-virtualenv', '--require-venv', + dest='require_venv', + action='store_true', + default=False, + help=SUPPRESS_HELP) + +verbose = partial( + Option, + '-v', '--verbose', + dest='verbose', + action='count', + default=0, + help='Give more output. Option is additive, and can be used up to 3 times.' +) + +version = partial( + Option, + '-V', '--version', + dest='version', + action='store_true', + help='Show version and exit.') + +quiet = partial( + Option, + '-q', '--quiet', + dest='quiet', + action='count', + default=0, + help=('Give less output. Option is additive, and can be used up to 3' + ' times (corresponding to WARNING, ERROR, and CRITICAL logging' + ' levels).') +) + +log = partial( + Option, + "--log", "--log-file", "--local-log", + dest="log", + metavar="path", + help="Path to a verbose appending log." +) + +no_input = partial( + Option, + # Don't ask for input + '--no-input', + dest='no_input', + action='store_true', + default=False, + help=SUPPRESS_HELP) + +proxy = partial( + Option, + '--proxy', + dest='proxy', + type='str', + default='', + help="Specify a proxy in the form [user:passwd@]proxy.server:port.") + +retries = partial( + Option, + '--retries', + dest='retries', + type='int', + default=5, + help="Maximum number of retries each connection should attempt " + "(default %default times).") + +timeout = partial( + Option, + '--timeout', '--default-timeout', + metavar='sec', + dest='timeout', + type='float', + default=15, + help='Set the socket timeout (default %default seconds).') + +default_vcs = partial( + Option, + # The default version control system for editables, e.g. 'svn' + '--default-vcs', + dest='default_vcs', + type='str', + default='', + help=SUPPRESS_HELP) + +skip_requirements_regex = partial( + Option, + # A regex to be used to skip requirements + '--skip-requirements-regex', + dest='skip_requirements_regex', + type='str', + default='', + help=SUPPRESS_HELP) + + +def exists_action(): + return Option( + # Option when path already exist + '--exists-action', + dest='exists_action', + type='choice', + choices=['s', 'i', 'w', 'b', 'a'], + default=[], + action='append', + metavar='action', + help="Default action when a path already exists: " + "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.") + + +cert = partial( + Option, + '--cert', + dest='cert', + type='str', + metavar='path', + help="Path to alternate CA bundle.") + +client_cert = partial( + Option, + '--client-cert', + dest='client_cert', + type='str', + default=None, + metavar='path', + help="Path to SSL client certificate, a single file containing the " + "private key and the certificate in PEM format.") + +index_url = partial( + Option, + '-i', '--index-url', '--pypi-url', + dest='index_url', + metavar='URL', + default=PyPI.simple_url, + help="Base URL of Python Package Index (default %default). " + "This should point to a repository compliant with PEP 503 " + "(the simple repository API) or a local directory laid out " + "in the same format.") + + +def extra_index_url(): + return Option( + '--extra-index-url', + dest='extra_index_urls', + metavar='URL', + action='append', + default=[], + help="Extra URLs of package indexes to use in addition to " + "--index-url. Should follow the same rules as " + "--index-url." + ) + + +no_index = partial( + Option, + '--no-index', + dest='no_index', + action='store_true', + default=False, + help='Ignore package index (only looking at --find-links URLs instead).') + + +def find_links(): + return Option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='url', + help="If a url or path to an html file, then parse for links to " + "archives. If a local path or file:// url that's a directory, " + "then look for archives in the directory listing.") + + +def allow_external(): + return Option( + "--allow-external", + dest="allow_external", + action="append", + default=[], + metavar="PACKAGE", + help=SUPPRESS_HELP, + ) + + +allow_all_external = partial( + Option, + "--allow-all-external", + dest="allow_all_external", + action="store_true", + default=False, + help=SUPPRESS_HELP, +) + + +def trusted_host(): + return Option( + "--trusted-host", + dest="trusted_hosts", + action="append", + metavar="HOSTNAME", + default=[], + help="Mark this host as trusted, even though it does not have valid " + "or any HTTPS.", + ) + + +# Remove after 7.0 +no_allow_external = partial( + Option, + "--no-allow-external", + dest="allow_all_external", + action="store_false", + default=False, + help=SUPPRESS_HELP, +) + + +# Remove --allow-insecure after 7.0 +def allow_unsafe(): + return Option( + "--allow-unverified", "--allow-insecure", + dest="allow_unverified", + action="append", + default=[], + metavar="PACKAGE", + help=SUPPRESS_HELP, + ) + +# Remove after 7.0 +no_allow_unsafe = partial( + Option, + "--no-allow-insecure", + dest="allow_all_insecure", + action="store_false", + default=False, + help=SUPPRESS_HELP +) + +# Remove after 1.5 +process_dependency_links = partial( + Option, + "--process-dependency-links", + dest="process_dependency_links", + action="store_true", + default=False, + help="Enable the processing of dependency links.", +) + + +def constraints(): + return Option( + '-c', '--constraint', + dest='constraints', + action='append', + default=[], + metavar='file', + help='Constrain versions using the given constraints file. ' + 'This option can be used multiple times.') + + +def requirements(): + return Option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Install from the given requirements file. ' + 'This option can be used multiple times.') + + +def editable(): + return Option( + '-e', '--editable', + dest='editables', + action='append', + default=[], + metavar='path/url', + help=('Install a project in editable mode (i.e. setuptools ' + '"develop mode") from a local project path or a VCS url.'), + ) + +src = partial( + Option, + '--src', '--source', '--source-dir', '--source-directory', + dest='src_dir', + metavar='dir', + default=src_prefix, + help='Directory to check out editable projects into. ' + 'The default in a virtualenv is "<venv path>/src". ' + 'The default for global installs is "<current dir>/src".' +) + +# XXX: deprecated, remove in 9.0 +use_wheel = partial( + Option, + '--use-wheel', + dest='use_wheel', + action='store_true', + default=True, + help=SUPPRESS_HELP, +) + +# XXX: deprecated, remove in 9.0 +no_use_wheel = partial( + Option, + '--no-use-wheel', + dest='use_wheel', + action='store_false', + default=True, + help=('Do not Find and prefer wheel archives when searching indexes and ' + 'find-links locations. DEPRECATED in favour of --no-binary.'), +) + + +def _get_format_control(values, option): + """Get a format_control object.""" + return getattr(values, option.dest) + + +def _handle_no_binary(option, opt_str, value, parser): + existing = getattr(parser.values, option.dest) + fmt_ctl_handle_mutual_exclude( + value, existing.no_binary, existing.only_binary) + + +def _handle_only_binary(option, opt_str, value, parser): + existing = getattr(parser.values, option.dest) + fmt_ctl_handle_mutual_exclude( + value, existing.only_binary, existing.no_binary) + + +def no_binary(): + return Option( + "--no-binary", dest="format_control", action="callback", + callback=_handle_no_binary, type="str", + default=FormatControl(set(), set()), + help="Do not use binary packages. Can be supplied multiple times, and " + "each time adds to the existing value. Accepts either :all: to " + "disable all binary packages, :none: to empty the set, or one or " + "more package names with commas between them. Note that some " + "packages are tricky to compile and may fail to install when " + "this option is used on them.") + + +def only_binary(): + return Option( + "--only-binary", dest="format_control", action="callback", + callback=_handle_only_binary, type="str", + default=FormatControl(set(), set()), + help="Do not use source packages. Can be supplied multiple times, and " + "each time adds to the existing value. Accepts either :all: to " + "disable all source packages, :none: to empty the set, or one or " + "more package names with commas between them. Packages without " + "binary distributions will fail to install when this option is " + "used on them.") + + +cache_dir = partial( + Option, + "--cache-dir", + dest="cache_dir", + default=USER_CACHE_DIR, + metavar="dir", + help="Store the cache data in <dir>." +) + +no_cache = partial( + Option, + "--no-cache-dir", + dest="cache_dir", + action="store_false", + help="Disable the cache.", +) + +no_deps = partial( + Option, + '--no-deps', '--no-dependencies', + dest='ignore_dependencies', + action='store_true', + default=False, + help="Don't install package dependencies.") + +build_dir = partial( + Option, + '-b', '--build', '--build-dir', '--build-directory', + dest='build_dir', + metavar='dir', + help='Directory to unpack packages into and build in.' +) + +ignore_requires_python = partial( + Option, + '--ignore-requires-python', + dest='ignore_requires_python', + action='store_true', + help='Ignore the Requires-Python information.') + +install_options = partial( + Option, + '--install-option', + dest='install_options', + action='append', + metavar='options', + help="Extra arguments to be supplied to the setup.py install " + "command (use like --install-option=\"--install-scripts=/usr/local/" + "bin\"). Use multiple --install-option options to pass multiple " + "options to setup.py install. If you are using an option with a " + "directory path, be sure to use absolute path.") + +global_options = partial( + Option, + '--global-option', + dest='global_options', + action='append', + metavar='options', + help="Extra global options to be supplied to the setup.py " + "call before the install command.") + +no_clean = partial( + Option, + '--no-clean', + action='store_true', + default=False, + help="Don't clean up build directories.") + +pre = partial( + Option, + '--pre', + action='store_true', + default=False, + help="Include pre-release and development versions. By default, " + "pip only finds stable versions.") + +disable_pip_version_check = partial( + Option, + "--disable-pip-version-check", + dest="disable_pip_version_check", + action="store_true", + default=False, + help="Don't periodically check PyPI to determine whether a new version " + "of pip is available for download. Implied with --no-index.") + +# Deprecated, Remove later +always_unzip = partial( + Option, + '-Z', '--always-unzip', + dest='always_unzip', + action='store_true', + help=SUPPRESS_HELP, +) + + +def _merge_hash(option, opt_str, value, parser): + """Given a value spelled "algo:digest", append the digest to a list + pointed to in a dict by the algo name.""" + if not parser.values.hashes: + parser.values.hashes = {} + try: + algo, digest = value.split(':', 1) + except ValueError: + parser.error('Arguments to %s must be a hash name ' + 'followed by a value, like --hash=sha256:abcde...' % + opt_str) + if algo not in STRONG_HASHES: + parser.error('Allowed hash algorithms for %s are %s.' % + (opt_str, ', '.join(STRONG_HASHES))) + parser.values.hashes.setdefault(algo, []).append(digest) + + +hash = partial( + Option, + '--hash', + # Hash values eventually end up in InstallRequirement.hashes due to + # __dict__ copying in process_line(). + dest='hashes', + action='callback', + callback=_merge_hash, + type='string', + help="Verify that the package's archive matches this " + 'hash before installing. Example: --hash=sha256:abcdef...') + + +require_hashes = partial( + Option, + '--require-hashes', + dest='require_hashes', + action='store_true', + default=False, + help='Require a hash to check each requirement against, for ' + 'repeatable installs. This option is implied when any package in a ' + 'requirements file has a --hash option.') + + +########## +# groups # +########## + +general_group = { + 'name': 'General Options', + 'options': [ + help_, + isolated_mode, + require_virtualenv, + verbose, + version, + quiet, + log, + no_input, + proxy, + retries, + timeout, + default_vcs, + skip_requirements_regex, + exists_action, + trusted_host, + cert, + client_cert, + cache_dir, + no_cache, + disable_pip_version_check, + ] +} + +non_deprecated_index_group = { + 'name': 'Package Index Options', + 'options': [ + index_url, + extra_index_url, + no_index, + find_links, + process_dependency_links, + ] +} + +index_group = { + 'name': 'Package Index Options (including deprecated options)', + 'options': non_deprecated_index_group['options'] + [ + allow_external, + allow_all_external, + no_allow_external, + allow_unsafe, + no_allow_unsafe, + ] +} diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/__init__.py new file mode 100644 index 0000000..62c64eb --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/__init__.py @@ -0,0 +1,86 @@ +""" +Package containing all pip commands +""" +from __future__ import absolute_import + +from pip.commands.completion import CompletionCommand +from pip.commands.download import DownloadCommand +from pip.commands.freeze import FreezeCommand +from pip.commands.hash import HashCommand +from pip.commands.help import HelpCommand +from pip.commands.list import ListCommand +from pip.commands.check import CheckCommand +from pip.commands.search import SearchCommand +from pip.commands.show import ShowCommand +from pip.commands.install import InstallCommand +from pip.commands.uninstall import UninstallCommand +from pip.commands.wheel import WheelCommand + + +commands_dict = { + CompletionCommand.name: CompletionCommand, + FreezeCommand.name: FreezeCommand, + HashCommand.name: HashCommand, + HelpCommand.name: HelpCommand, + SearchCommand.name: SearchCommand, + ShowCommand.name: ShowCommand, + InstallCommand.name: InstallCommand, + UninstallCommand.name: UninstallCommand, + DownloadCommand.name: DownloadCommand, + ListCommand.name: ListCommand, + CheckCommand.name: CheckCommand, + WheelCommand.name: WheelCommand, +} + + +commands_order = [ + InstallCommand, + DownloadCommand, + UninstallCommand, + FreezeCommand, + ListCommand, + ShowCommand, + CheckCommand, + SearchCommand, + WheelCommand, + HashCommand, + CompletionCommand, + HelpCommand, +] + + +def get_summaries(ordered=True): + """Yields sorted (command name, command summary) tuples.""" + + if ordered: + cmditems = _sort_commands(commands_dict, commands_order) + else: + cmditems = commands_dict.items() + + for name, command_class in cmditems: + yield (name, command_class.summary) + + +def get_similar_commands(name): + """Command name auto-correct.""" + from difflib import get_close_matches + + name = name.lower() + + close_commands = get_close_matches(name, commands_dict.keys()) + + if close_commands: + return close_commands[0] + else: + return False + + +def _sort_commands(cmddict, order): + def keyfn(key): + try: + return order.index(key[1]) + except ValueError: + # unordered items should come last + return 0xff + + return sorted(cmddict.items(), key=keyfn) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/check.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/check.py new file mode 100644 index 0000000..70458ad --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/check.py @@ -0,0 +1,39 @@ +import logging + +from pip.basecommand import Command +from pip.operations.check import check_requirements +from pip.utils import get_installed_distributions + + +logger = logging.getLogger(__name__) + + +class CheckCommand(Command): + """Verify installed packages have compatible dependencies.""" + name = 'check' + usage = """ + %prog [options]""" + summary = 'Verify installed packages have compatible dependencies.' + + def run(self, options, args): + dists = get_installed_distributions(local_only=False, skip=()) + missing_reqs_dict, incompatible_reqs_dict = check_requirements(dists) + + for dist in dists: + key = '%s==%s' % (dist.project_name, dist.version) + + for requirement in missing_reqs_dict.get(key, []): + logger.info( + "%s %s requires %s, which is not installed.", + dist.project_name, dist.version, requirement.project_name) + + for requirement, actual in incompatible_reqs_dict.get(key, []): + logger.info( + "%s %s has requirement %s, but you have %s %s.", + dist.project_name, dist.version, requirement, + actual.project_name, actual.version) + + if missing_reqs_dict or incompatible_reqs_dict: + return 1 + else: + logger.info("No broken requirements found.") diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/completion.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/completion.py new file mode 100644 index 0000000..66e41a6 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/completion.py @@ -0,0 +1,81 @@ +from __future__ import absolute_import + +import sys +from pip.basecommand import Command + +BASE_COMPLETION = """ +# pip %(shell)s completion start%(script)s# pip %(shell)s completion end +""" + +COMPLETION_SCRIPTS = { + 'bash': """ +_pip_completion() +{ + COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \\ + COMP_CWORD=$COMP_CWORD \\ + PIP_AUTO_COMPLETE=1 $1 ) ) +} +complete -o default -F _pip_completion pip +""", 'zsh': """ +function _pip_completion { + local words cword + read -Ac words + read -cn cword + reply=( $( COMP_WORDS="$words[*]" \\ + COMP_CWORD=$(( cword-1 )) \\ + PIP_AUTO_COMPLETE=1 $words[1] ) ) +} +compctl -K _pip_completion pip +""", 'fish': """ +function __fish_complete_pip + set -lx COMP_WORDS (commandline -o) "" + set -lx COMP_CWORD (math (contains -i -- (commandline -t) $COMP_WORDS)-1) + set -lx PIP_AUTO_COMPLETE 1 + string split \ -- (eval $COMP_WORDS[1]) +end +complete -fa "(__fish_complete_pip)" -c pip +"""} + + +class CompletionCommand(Command): + """A helper command to be used for command completion.""" + name = 'completion' + summary = 'A helper command used for command completion.' + + def __init__(self, *args, **kw): + super(CompletionCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( + '--bash', '-b', + action='store_const', + const='bash', + dest='shell', + help='Emit completion code for bash') + cmd_opts.add_option( + '--zsh', '-z', + action='store_const', + const='zsh', + dest='shell', + help='Emit completion code for zsh') + cmd_opts.add_option( + '--fish', '-f', + action='store_const', + const='fish', + dest='shell', + help='Emit completion code for fish') + + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + """Prints the completion code of the given shell""" + shells = COMPLETION_SCRIPTS.keys() + shell_options = ['--' + shell for shell in sorted(shells)] + if options.shell in shells: + script = COMPLETION_SCRIPTS.get(options.shell, '') + print(BASE_COMPLETION % {'script': script, 'shell': options.shell}) + else: + sys.stderr.write( + 'ERROR: You must pass %s\n' % ' or '.join(shell_options) + ) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/download.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/download.py new file mode 100644 index 0000000..4bc0640 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/download.py @@ -0,0 +1,212 @@ +from __future__ import absolute_import + +import logging +import os + +from pip.exceptions import CommandError +from pip.index import FormatControl +from pip.req import RequirementSet +from pip.basecommand import RequirementCommand +from pip import cmdoptions +from pip.utils import ensure_dir, normalize_path +from pip.utils.build import BuildDirectory +from pip.utils.filesystem import check_path_owner + + +logger = logging.getLogger(__name__) + + +class DownloadCommand(RequirementCommand): + """ + Download packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports downloading from "requirements files", which provide + an easy way to specify a whole environment to be downloaded. + """ + name = 'download' + + usage = """ + %prog [options] <requirement specifier> [package-index-options] ... + %prog [options] -r <requirements file> [package-index-options] ... + %prog [options] [-e] <vcs project url> ... + %prog [options] [-e] <local project path> ... + %prog [options] <archive url/path> ...""" + + summary = 'Download packages.' + + def __init__(self, *args, **kw): + super(DownloadCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.build_dir()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.global_options()) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.src()) + cmd_opts.add_option(cmdoptions.pre()) + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + + cmd_opts.add_option( + '-d', '--dest', '--destination-dir', '--destination-directory', + dest='download_dir', + metavar='dir', + default=os.curdir, + help=("Download packages into <dir>."), + ) + + cmd_opts.add_option( + '--platform', + dest='platform', + metavar='platform', + default=None, + help=("Only download wheels compatible with <platform>. " + "Defaults to the platform of the running system."), + ) + + cmd_opts.add_option( + '--python-version', + dest='python_version', + metavar='python_version', + default=None, + help=("Only download wheels compatible with Python " + "interpreter version <version>. If not specified, then the " + "current system interpreter minor version is used. A major " + "version (e.g. '2') can be specified to match all " + "minor revs of that major version. A minor version " + "(e.g. '34') can also be specified."), + ) + + cmd_opts.add_option( + '--implementation', + dest='implementation', + metavar='implementation', + default=None, + help=("Only download wheels compatible with Python " + "implementation <implementation>, e.g. 'pp', 'jy', 'cp', " + " or 'ip'. If not specified, then the current " + "interpreter implementation is used. Use 'py' to force " + "implementation-agnostic wheels."), + ) + + cmd_opts.add_option( + '--abi', + dest='abi', + metavar='abi', + default=None, + help=("Only download wheels compatible with Python " + "abi <abi>, e.g. 'pypy_41'. If not specified, then the " + "current interpreter abi tag is used. Generally " + "you will need to specify --implementation, " + "--platform, and --python-version when using " + "this option."), + ) + + index_opts = cmdoptions.make_option_group( + cmdoptions.non_deprecated_index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + options.ignore_installed = True + + if options.python_version: + python_versions = [options.python_version] + else: + python_versions = None + + dist_restriction_set = any([ + options.python_version, + options.platform, + options.abi, + options.implementation, + ]) + binary_only = FormatControl(set(), set([':all:'])) + if dist_restriction_set and options.format_control != binary_only: + raise CommandError( + "--only-binary=:all: must be set and --no-binary must not " + "be set (or must be set to :none:) when restricting platform " + "and interpreter constraints using --python-version, " + "--platform, --abi, or --implementation." + ) + + options.src_dir = os.path.abspath(options.src_dir) + options.download_dir = normalize_path(options.download_dir) + + ensure_dir(options.download_dir) + + with self._build_session(options) as session: + finder = self._build_package_finder( + options=options, + session=session, + platform=options.platform, + python_versions=python_versions, + abi=options.abi, + implementation=options.implementation, + ) + build_delete = (not (options.no_clean or options.build_dir)) + if options.cache_dir and not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "by the current user and caching wheels has been " + "disabled. check the permissions and owner of that " + "directory. If executing pip with sudo, you may want " + "sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + with BuildDirectory(options.build_dir, + delete=build_delete) as build_dir: + + requirement_set = RequirementSet( + build_dir=build_dir, + src_dir=options.src_dir, + download_dir=options.download_dir, + ignore_installed=True, + ignore_dependencies=options.ignore_dependencies, + session=session, + isolated=options.isolated_mode, + require_hashes=options.require_hashes + ) + self.populate_requirement_set( + requirement_set, + args, + options, + finder, + session, + self.name, + None + ) + + if not requirement_set.has_requirements: + return + + requirement_set.prepare_files(finder) + + downloaded = ' '.join([ + req.name for req in requirement_set.successfully_downloaded + ]) + if downloaded: + logger.info( + 'Successfully downloaded %s', downloaded + ) + + # Clean up + if not options.no_clean: + requirement_set.cleanup_files() + + return requirement_set diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/freeze.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/freeze.py new file mode 100644 index 0000000..c198796 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/freeze.py @@ -0,0 +1,87 @@ +from __future__ import absolute_import + +import sys + +import pip +from pip.compat import stdlib_pkgs +from pip.basecommand import Command +from pip.operations.freeze import freeze +from pip.wheel import WheelCache + + +DEV_PKGS = ('pip', 'setuptools', 'distribute', 'wheel') + + +class FreezeCommand(Command): + """ + Output installed packages in requirements format. + + packages are listed in a case-insensitive sorted order. + """ + name = 'freeze' + usage = """ + %prog [options]""" + summary = 'Output installed packages in requirements format.' + log_streams = ("ext://sys.stderr", "ext://sys.stderr") + + def __init__(self, *args, **kw): + super(FreezeCommand, self).__init__(*args, **kw) + + self.cmd_opts.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help="Use the order in the given requirements file and its " + "comments when generating output. This option can be " + "used multiple times.") + self.cmd_opts.add_option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='URL', + help='URL for finding packages, which will be added to the ' + 'output.') + self.cmd_opts.add_option( + '-l', '--local', + dest='local', + action='store_true', + default=False, + help='If in a virtualenv that has global access, do not output ' + 'globally-installed packages.') + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + self.cmd_opts.add_option( + '--all', + dest='freeze_all', + action='store_true', + help='Do not skip these packages in the output:' + ' %s' % ', '.join(DEV_PKGS)) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + format_control = pip.index.FormatControl(set(), set()) + wheel_cache = WheelCache(options.cache_dir, format_control) + skip = set(stdlib_pkgs) + if not options.freeze_all: + skip.update(DEV_PKGS) + + freeze_kwargs = dict( + requirement=options.requirements, + find_links=options.find_links, + local_only=options.local, + user_only=options.user, + skip_regex=options.skip_requirements_regex, + isolated=options.isolated_mode, + wheel_cache=wheel_cache, + skip=skip) + + for line in freeze(**freeze_kwargs): + sys.stdout.write(line + '\n') diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/hash.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/hash.py new file mode 100644 index 0000000..27cca0b --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/hash.py @@ -0,0 +1,57 @@ +from __future__ import absolute_import + +import hashlib +import logging +import sys + +from pip.basecommand import Command +from pip.status_codes import ERROR +from pip.utils import read_chunks +from pip.utils.hashes import FAVORITE_HASH, STRONG_HASHES + + +logger = logging.getLogger(__name__) + + +class HashCommand(Command): + """ + Compute a hash of a local package archive. + + These can be used with --hash in a requirements file to do repeatable + installs. + + """ + name = 'hash' + usage = '%prog [options] <file> ...' + summary = 'Compute hashes of package archives.' + + def __init__(self, *args, **kw): + super(HashCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-a', '--algorithm', + dest='algorithm', + choices=STRONG_HASHES, + action='store', + default=FAVORITE_HASH, + help='The hash algorithm to use: one of %s' % + ', '.join(STRONG_HASHES)) + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + self.parser.print_usage(sys.stderr) + return ERROR + + algorithm = options.algorithm + for path in args: + logger.info('%s:\n--hash=%s:%s', + path, algorithm, _hash_of_file(path, algorithm)) + + +def _hash_of_file(path, algorithm): + """Return the hash digest of a file.""" + with open(path, 'rb') as archive: + hash = hashlib.new(algorithm) + for chunk in read_chunks(archive): + hash.update(chunk) + return hash.hexdigest() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/help.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/help.py new file mode 100644 index 0000000..11722f1 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/help.py @@ -0,0 +1,35 @@ +from __future__ import absolute_import + +from pip.basecommand import Command, SUCCESS +from pip.exceptions import CommandError + + +class HelpCommand(Command): + """Show help for commands""" + name = 'help' + usage = """ + %prog <command>""" + summary = 'Show help for commands.' + + def run(self, options, args): + from pip.commands import commands_dict, get_similar_commands + + try: + # 'pip help' with no args is handled by pip.__init__.parseopt() + cmd_name = args[0] # the command we need help for + except IndexError: + return SUCCESS + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "%s"' % cmd_name] + if guess: + msg.append('maybe you meant "%s"' % guess) + + raise CommandError(' - '.join(msg)) + + command = commands_dict[cmd_name]() + command.parser.print_help() + + return SUCCESS diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/install.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/install.py new file mode 100644 index 0000000..227c526 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/install.py @@ -0,0 +1,437 @@ +from __future__ import absolute_import + +import logging +import operator +import os +import tempfile +import shutil +import warnings +try: + import wheel +except ImportError: + wheel = None + +from pip.req import RequirementSet +from pip.basecommand import RequirementCommand +from pip.locations import virtualenv_no_global, distutils_scheme +from pip.exceptions import ( + InstallationError, CommandError, PreviousBuildDirError, +) +from pip import cmdoptions +from pip.utils import ensure_dir, get_installed_version +from pip.utils.build import BuildDirectory +from pip.utils.deprecation import RemovedInPip10Warning +from pip.utils.filesystem import check_path_owner +from pip.wheel import WheelCache, WheelBuilder + + +logger = logging.getLogger(__name__) + + +class InstallCommand(RequirementCommand): + """ + Install packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports installing from "requirements files", which provide + an easy way to specify a whole environment to be installed. + """ + name = 'install' + + usage = """ + %prog [options] <requirement specifier> [package-index-options] ... + %prog [options] -r <requirements file> [package-index-options] ... + %prog [options] [-e] <vcs project url> ... + %prog [options] [-e] <local project path> ... + %prog [options] <archive url/path> ...""" + + summary = 'Install packages.' + + def __init__(self, *args, **kw): + super(InstallCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.build_dir()) + + cmd_opts.add_option( + '-t', '--target', + dest='target_dir', + metavar='dir', + default=None, + help='Install packages into <dir>. ' + 'By default this will not replace existing files/folders in ' + '<dir>. Use --upgrade to replace existing packages in <dir> ' + 'with new versions.' + ) + + cmd_opts.add_option( + '-d', '--download', '--download-dir', '--download-directory', + dest='download_dir', + metavar='dir', + default=None, + help=("Download packages into <dir> instead of installing them, " + "regardless of what's already installed."), + ) + + cmd_opts.add_option(cmdoptions.src()) + + cmd_opts.add_option( + '-U', '--upgrade', + dest='upgrade', + action='store_true', + help='Upgrade all specified packages to the newest available ' + 'version. The handling of dependencies depends on the ' + 'upgrade-strategy used.' + ) + + cmd_opts.add_option( + '--upgrade-strategy', + dest='upgrade_strategy', + default='eager', + choices=['only-if-needed', 'eager'], + help='Determines how dependency upgrading should be handled. ' + '"eager" - dependencies are upgraded regardless of ' + 'whether the currently installed version satisfies the ' + 'requirements of the upgraded package(s). ' + '"only-if-needed" - are upgraded only when they do not ' + 'satisfy the requirements of the upgraded package(s).' + ) + + cmd_opts.add_option( + '--force-reinstall', + dest='force_reinstall', + action='store_true', + help='When upgrading, reinstall all packages even if they are ' + 'already up-to-date.') + + cmd_opts.add_option( + '-I', '--ignore-installed', + dest='ignore_installed', + action='store_true', + help='Ignore the installed packages (reinstalling instead).') + + cmd_opts.add_option(cmdoptions.ignore_requires_python()) + cmd_opts.add_option(cmdoptions.no_deps()) + + cmd_opts.add_option(cmdoptions.install_options()) + cmd_opts.add_option(cmdoptions.global_options()) + + cmd_opts.add_option( + '--user', + dest='use_user_site', + action='store_true', + help="Install to the Python user install directory for your " + "platform. Typically ~/.local/, or %APPDATA%\Python on " + "Windows. (See the Python documentation for site.USER_BASE " + "for full details.)") + + cmd_opts.add_option( + '--egg', + dest='as_egg', + action='store_true', + help="Install packages as eggs, not 'flat', like pip normally " + "does. This option is not about installing *from* eggs. " + "(WARNING: Because this option overrides pip's normal install" + " logic, requirements files may not behave as expected.)") + + cmd_opts.add_option( + '--root', + dest='root_path', + metavar='dir', + default=None, + help="Install everything relative to this alternate root " + "directory.") + + cmd_opts.add_option( + '--prefix', + dest='prefix_path', + metavar='dir', + default=None, + help="Installation prefix where lib, bin and other top-level " + "folders are placed") + + cmd_opts.add_option( + "--compile", + action="store_true", + dest="compile", + default=True, + help="Compile py files to pyc", + ) + + cmd_opts.add_option( + "--no-compile", + action="store_false", + dest="compile", + help="Do not compile py files to pyc", + ) + + cmd_opts.add_option(cmdoptions.use_wheel()) + cmd_opts.add_option(cmdoptions.no_use_wheel()) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.pre()) + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + cmdoptions.resolve_wheel_no_use_binary(options) + cmdoptions.check_install_build_global(options) + + if options.as_egg: + warnings.warn( + "--egg has been deprecated and will be removed in the future. " + "This flag is mutually exclusive with large parts of pip, and " + "actually using it invalidates pip's ability to manage the " + "installation process.", + RemovedInPip10Warning, + ) + + if options.allow_external: + warnings.warn( + "--allow-external has been deprecated and will be removed in " + "the future. Due to changes in the repository protocol, it no " + "longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_all_external: + warnings.warn( + "--allow-all-external has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_unverified: + warnings.warn( + "--allow-unverified has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if options.download_dir: + warnings.warn( + "pip install --download has been deprecated and will be " + "removed in the future. Pip now has a download command that " + "should be used instead.", + RemovedInPip10Warning, + ) + options.ignore_installed = True + + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + + options.src_dir = os.path.abspath(options.src_dir) + install_options = options.install_options or [] + if options.use_user_site: + if options.prefix_path: + raise CommandError( + "Can not combine '--user' and '--prefix' as they imply " + "different installation locations" + ) + if virtualenv_no_global(): + raise InstallationError( + "Can not perform a '--user' install. User site-packages " + "are not visible in this virtualenv." + ) + install_options.append('--user') + install_options.append('--prefix=') + + temp_target_dir = None + if options.target_dir: + options.ignore_installed = True + temp_target_dir = tempfile.mkdtemp() + options.target_dir = os.path.abspath(options.target_dir) + if (os.path.exists(options.target_dir) and not + os.path.isdir(options.target_dir)): + raise CommandError( + "Target path exists but is not a directory, will not " + "continue." + ) + install_options.append('--home=' + temp_target_dir) + + global_options = options.global_options or [] + + with self._build_session(options) as session: + + finder = self._build_package_finder(options, session) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + if options.cache_dir and not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "by the current user and caching wheels has been " + "disabled. check the permissions and owner of that " + "directory. If executing pip with sudo, you may want " + "sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + with BuildDirectory(options.build_dir, + delete=build_delete) as build_dir: + requirement_set = RequirementSet( + build_dir=build_dir, + src_dir=options.src_dir, + download_dir=options.download_dir, + upgrade=options.upgrade, + upgrade_strategy=options.upgrade_strategy, + as_egg=options.as_egg, + ignore_installed=options.ignore_installed, + ignore_dependencies=options.ignore_dependencies, + ignore_requires_python=options.ignore_requires_python, + force_reinstall=options.force_reinstall, + use_user_site=options.use_user_site, + target_dir=temp_target_dir, + session=session, + pycompile=options.compile, + isolated=options.isolated_mode, + wheel_cache=wheel_cache, + require_hashes=options.require_hashes, + ) + + self.populate_requirement_set( + requirement_set, args, options, finder, session, self.name, + wheel_cache + ) + + if not requirement_set.has_requirements: + return + + try: + if (options.download_dir or not wheel or not + options.cache_dir): + # on -d don't do complex things like building + # wheels, and don't try to build wheels when wheel is + # not installed. + requirement_set.prepare_files(finder) + else: + # build wheels before install. + wb = WheelBuilder( + requirement_set, + finder, + build_options=[], + global_options=[], + ) + # Ignore the result: a failed wheel will be + # installed from the sdist/vcs whatever. + wb.build(autobuilding=True) + + if not options.download_dir: + requirement_set.install( + install_options, + global_options, + root=options.root_path, + prefix=options.prefix_path, + ) + + possible_lib_locations = get_lib_location_guesses( + user=options.use_user_site, + home=temp_target_dir, + root=options.root_path, + prefix=options.prefix_path, + isolated=options.isolated_mode, + ) + reqs = sorted( + requirement_set.successfully_installed, + key=operator.attrgetter('name')) + items = [] + for req in reqs: + item = req.name + try: + installed_version = get_installed_version( + req.name, possible_lib_locations + ) + if installed_version: + item += '-' + installed_version + except Exception: + pass + items.append(item) + installed = ' '.join(items) + if installed: + logger.info('Successfully installed %s', installed) + else: + downloaded = ' '.join([ + req.name + for req in requirement_set.successfully_downloaded + ]) + if downloaded: + logger.info( + 'Successfully downloaded %s', downloaded + ) + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + # Clean up + if not options.no_clean: + requirement_set.cleanup_files() + + if options.target_dir: + ensure_dir(options.target_dir) + + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + lib_dir_list = [] + + purelib_dir = distutils_scheme('', home=temp_target_dir)['purelib'] + platlib_dir = distutils_scheme('', home=temp_target_dir)['platlib'] + + if os.path.exists(purelib_dir): + lib_dir_list.append(purelib_dir) + if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: + lib_dir_list.append(platlib_dir) + + for lib_dir in lib_dir_list: + for item in os.listdir(lib_dir): + target_item_dir = os.path.join(options.target_dir, item) + if os.path.exists(target_item_dir): + if not options.upgrade: + logger.warning( + 'Target directory %s already exists. Specify ' + '--upgrade to force replacement.', + target_item_dir + ) + continue + if os.path.islink(target_item_dir): + logger.warning( + 'Target directory %s already exists and is ' + 'a link. Pip will not automatically replace ' + 'links, please remove if replacement is ' + 'desired.', + target_item_dir + ) + continue + if os.path.isdir(target_item_dir): + shutil.rmtree(target_item_dir) + else: + os.remove(target_item_dir) + + shutil.move( + os.path.join(lib_dir, item), + target_item_dir + ) + shutil.rmtree(temp_target_dir) + return requirement_set + + +def get_lib_location_guesses(*args, **kwargs): + scheme = distutils_scheme('', *args, **kwargs) + return [scheme['purelib'], scheme['platlib']] diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/list.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/list.py new file mode 100644 index 0000000..6f6995d --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/list.py @@ -0,0 +1,337 @@ +from __future__ import absolute_import + +import json +import logging +import warnings +try: + from itertools import zip_longest +except ImportError: + from itertools import izip_longest as zip_longest + +from pip._vendor import six + +from pip.basecommand import Command +from pip.exceptions import CommandError +from pip.index import PackageFinder +from pip.utils import ( + get_installed_distributions, dist_is_editable) +from pip.utils.deprecation import RemovedInPip10Warning +from pip.cmdoptions import make_option_group, index_group + +logger = logging.getLogger(__name__) + + +class ListCommand(Command): + """ + List installed packages, including editables. + + Packages are listed in a case-insensitive sorted order. + """ + name = 'list' + usage = """ + %prog [options]""" + summary = 'List installed packages.' + + def __init__(self, *args, **kw): + super(ListCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( + '-o', '--outdated', + action='store_true', + default=False, + help='List outdated packages') + cmd_opts.add_option( + '-u', '--uptodate', + action='store_true', + default=False, + help='List uptodate packages') + cmd_opts.add_option( + '-e', '--editable', + action='store_true', + default=False, + help='List editable projects.') + cmd_opts.add_option( + '-l', '--local', + action='store_true', + default=False, + help=('If in a virtualenv that has global access, do not list ' + 'globally-installed packages.'), + ) + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + + cmd_opts.add_option( + '--pre', + action='store_true', + default=False, + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + cmd_opts.add_option( + '--format', + action='store', + dest='list_format', + choices=('legacy', 'columns', 'freeze', 'json'), + help="Select the output format among: legacy (default), columns, " + "freeze or json.", + ) + + cmd_opts.add_option( + '--not-required', + action='store_true', + dest='not_required', + help="List packages that are not dependencies of " + "installed packages.", + ) + + index_opts = make_option_group(index_group, self.parser) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def _build_package_finder(self, options, index_urls, session): + """ + Create a package finder appropriate to this list command. + """ + return PackageFinder( + find_links=options.find_links, + index_urls=index_urls, + allow_all_prereleases=options.pre, + trusted_hosts=options.trusted_hosts, + process_dependency_links=options.process_dependency_links, + session=session, + ) + + def run(self, options, args): + if options.allow_external: + warnings.warn( + "--allow-external has been deprecated and will be removed in " + "the future. Due to changes in the repository protocol, it no " + "longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_all_external: + warnings.warn( + "--allow-all-external has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_unverified: + warnings.warn( + "--allow-unverified has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if options.list_format is None: + warnings.warn( + "The default format will switch to columns in the future. " + "You can use --format=(legacy|columns) (or define a " + "format=(legacy|columns) in your pip.conf under the [list] " + "section) to disable this warning.", + RemovedInPip10Warning, + ) + + if options.outdated and options.uptodate: + raise CommandError( + "Options --outdated and --uptodate cannot be combined.") + + packages = get_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + ) + + if options.outdated: + packages = self.get_outdated(packages, options) + elif options.uptodate: + packages = self.get_uptodate(packages, options) + + if options.not_required: + packages = self.get_not_required(packages, options) + + self.output_package_listing(packages, options) + + def get_outdated(self, packages, options): + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version > dist.parsed_version + ] + + def get_uptodate(self, packages, options): + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version == dist.parsed_version + ] + + def get_not_required(self, packages, options): + dep_keys = set() + for dist in packages: + dep_keys.update(requirement.key for requirement in dist.requires()) + return set(pkg for pkg in packages if pkg.key not in dep_keys) + + def iter_packages_latest_infos(self, packages, options): + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + dependency_links = [] + for dist in packages: + if dist.has_metadata('dependency_links.txt'): + dependency_links.extend( + dist.get_metadata_lines('dependency_links.txt'), + ) + + with self._build_session(options) as session: + finder = self._build_package_finder(options, index_urls, session) + finder.add_dependency_links(dependency_links) + + for dist in packages: + typ = 'unknown' + all_candidates = finder.find_all_candidates(dist.key) + if not options.pre: + # Remove prereleases + all_candidates = [candidate for candidate in all_candidates + if not candidate.version.is_prerelease] + + if not all_candidates: + continue + best_candidate = max(all_candidates, + key=finder._candidate_sort_key) + remote_version = best_candidate.version + if best_candidate.location.is_wheel: + typ = 'wheel' + else: + typ = 'sdist' + # This is dirty but makes the rest of the code much cleaner + dist.latest_version = remote_version + dist.latest_filetype = typ + yield dist + + def output_legacy(self, dist): + if dist_is_editable(dist): + return '%s (%s, %s)' % ( + dist.project_name, + dist.version, + dist.location, + ) + else: + return '%s (%s)' % (dist.project_name, dist.version) + + def output_legacy_latest(self, dist): + return '%s - Latest: %s [%s]' % ( + self.output_legacy(dist), + dist.latest_version, + dist.latest_filetype, + ) + + def output_package_listing(self, packages, options): + packages = sorted( + packages, + key=lambda dist: dist.project_name.lower(), + ) + if options.list_format == 'columns' and packages: + data, header = format_for_columns(packages, options) + self.output_package_listing_columns(data, header) + elif options.list_format == 'freeze': + for dist in packages: + logger.info("%s==%s", dist.project_name, dist.version) + elif options.list_format == 'json': + logger.info(format_for_json(packages, options)) + else: # legacy + for dist in packages: + if options.outdated: + logger.info(self.output_legacy_latest(dist)) + else: + logger.info(self.output_legacy(dist)) + + def output_package_listing_columns(self, data, header): + # insert the header first: we need to know the size of column names + if len(data) > 0: + data.insert(0, header) + + pkg_strings, sizes = tabulate(data) + + # Create and add a separator. + if len(data) > 0: + pkg_strings.insert(1, " ".join(map(lambda x: '-' * x, sizes))) + + for val in pkg_strings: + logger.info(val) + + +def tabulate(vals): + # From pfmoore on GitHub: + # https://github.com/pypa/pip/issues/3651#issuecomment-216932564 + assert len(vals) > 0 + + sizes = [0] * max(len(x) for x in vals) + for row in vals: + sizes = [max(s, len(str(c))) for s, c in zip_longest(sizes, row)] + + result = [] + for row in vals: + display = " ".join([str(c).ljust(s) if c is not None else '' + for s, c in zip_longest(sizes, row)]) + result.append(display) + + return result, sizes + + +def format_for_columns(pkgs, options): + """ + Convert the package data into something usable + by output_package_listing_columns. + """ + running_outdated = options.outdated + # Adjust the header for the `pip list --outdated` case. + if running_outdated: + header = ["Package", "Version", "Latest", "Type"] + else: + header = ["Package", "Version"] + + data = [] + if any(dist_is_editable(x) for x in pkgs): + header.append("Location") + + for proj in pkgs: + # if we're working on the 'outdated' list, separate out the + # latest_version and type + row = [proj.project_name, proj.version] + + if running_outdated: + row.append(proj.latest_version) + row.append(proj.latest_filetype) + + if dist_is_editable(proj): + row.append(proj.location) + + data.append(row) + + return data, header + + +def format_for_json(packages, options): + data = [] + for dist in packages: + info = { + 'name': dist.project_name, + 'version': six.text_type(dist.version), + } + if options.outdated: + info['latest_version'] = six.text_type(dist.latest_version) + info['latest_filetype'] = dist.latest_filetype + data.append(info) + return json.dumps(data) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/search.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/search.py new file mode 100644 index 0000000..bd2ea8a --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/search.py @@ -0,0 +1,133 @@ +from __future__ import absolute_import + +import logging +import sys +import textwrap + +from pip.basecommand import Command, SUCCESS +from pip.compat import OrderedDict +from pip.download import PipXmlrpcTransport +from pip.models import PyPI +from pip.utils import get_terminal_size +from pip.utils.logging import indent_log +from pip.exceptions import CommandError +from pip.status_codes import NO_MATCHES_FOUND +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor import pkg_resources +from pip._vendor.six.moves import xmlrpc_client + + +logger = logging.getLogger(__name__) + + +class SearchCommand(Command): + """Search for PyPI packages whose name or summary contains <query>.""" + name = 'search' + usage = """ + %prog [options] <query>""" + summary = 'Search PyPI for packages.' + + def __init__(self, *args, **kw): + super(SearchCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-i', '--index', + dest='index', + metavar='URL', + default=PyPI.pypi_url, + help='Base URL of Python Package Index (default %default)') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + raise CommandError('Missing required argument (search query).') + query = args + pypi_hits = self.search(query, options) + hits = transform_hits(pypi_hits) + + terminal_width = None + if sys.stdout.isatty(): + terminal_width = get_terminal_size()[0] + + print_results(hits, terminal_width=terminal_width) + if pypi_hits: + return SUCCESS + return NO_MATCHES_FOUND + + def search(self, query, options): + index_url = options.index + with self._build_session(options) as session: + transport = PipXmlrpcTransport(index_url, session) + pypi = xmlrpc_client.ServerProxy(index_url, transport) + hits = pypi.search({'name': query, 'summary': query}, 'or') + return hits + + +def transform_hits(hits): + """ + The list from pypi is really a list of versions. We want a list of + packages with the list of versions stored inline. This converts the + list from pypi into one we can use. + """ + packages = OrderedDict() + for hit in hits: + name = hit['name'] + summary = hit['summary'] + version = hit['version'] + + if name not in packages.keys(): + packages[name] = { + 'name': name, + 'summary': summary, + 'versions': [version], + } + else: + packages[name]['versions'].append(version) + + # if this is the highest version, replace summary and score + if version == highest_version(packages[name]['versions']): + packages[name]['summary'] = summary + + return list(packages.values()) + + +def print_results(hits, name_column_width=None, terminal_width=None): + if not hits: + return + if name_column_width is None: + name_column_width = max([ + len(hit['name']) + len(hit.get('versions', ['-'])[-1]) + for hit in hits + ]) + 4 + + installed_packages = [p.project_name for p in pkg_resources.working_set] + for hit in hits: + name = hit['name'] + summary = hit['summary'] or '' + version = hit.get('versions', ['-'])[-1] + if terminal_width is not None: + target_width = terminal_width - name_column_width - 5 + if target_width > 10: + # wrap and indent summary to fit terminal + summary = textwrap.wrap(summary, target_width) + summary = ('\n' + ' ' * (name_column_width + 3)).join(summary) + + line = '%-*s - %s' % (name_column_width, + '%s (%s)' % (name, version), summary) + try: + logger.info(line) + if name in installed_packages: + dist = pkg_resources.get_distribution(name) + with indent_log(): + latest = highest_version(hit['versions']) + if dist.version == latest: + logger.info('INSTALLED: %s (latest)', dist.version) + else: + logger.info('INSTALLED: %s', dist.version) + logger.info('LATEST: %s', latest) + except UnicodeEncodeError: + pass + + +def highest_version(versions): + return max(versions, key=parse_version) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/show.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/show.py new file mode 100644 index 0000000..111c16d --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/show.py @@ -0,0 +1,154 @@ +from __future__ import absolute_import + +from email.parser import FeedParser +import logging +import os + +from pip.basecommand import Command +from pip.status_codes import SUCCESS, ERROR +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name + + +logger = logging.getLogger(__name__) + + +class ShowCommand(Command): + """Show information about one or more installed packages.""" + name = 'show' + usage = """ + %prog [options] <package> ...""" + summary = 'Show information about installed packages.' + + def __init__(self, *args, **kw): + super(ShowCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-f', '--files', + dest='files', + action='store_true', + default=False, + help='Show the full list of installed files for each package.') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + logger.warning('ERROR: Please provide a package name or names.') + return ERROR + query = args + + results = search_packages_info(query) + if not print_results( + results, list_files=options.files, verbose=options.verbose): + return ERROR + return SUCCESS + + +def search_packages_info(query): + """ + Gather details from installed distributions. Print distribution name, + version, location, and installed files. Installed files requires a + pip generated 'installed-files.txt' in the distributions '.egg-info' + directory. + """ + installed = {} + for p in pkg_resources.working_set: + installed[canonicalize_name(p.project_name)] = p + + query_names = [canonicalize_name(name) for name in query] + + for dist in [installed[pkg] for pkg in query_names if pkg in installed]: + package = { + 'name': dist.project_name, + 'version': dist.version, + 'location': dist.location, + 'requires': [dep.project_name for dep in dist.requires()], + } + file_list = None + metadata = None + if isinstance(dist, pkg_resources.DistInfoDistribution): + # RECORDs should be part of .dist-info metadatas + if dist.has_metadata('RECORD'): + lines = dist.get_metadata_lines('RECORD') + paths = [l.split(',')[0] for l in lines] + paths = [os.path.join(dist.location, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('METADATA'): + metadata = dist.get_metadata('METADATA') + else: + # Otherwise use pip's log for .egg-info's + if dist.has_metadata('installed-files.txt'): + paths = dist.get_metadata_lines('installed-files.txt') + paths = [os.path.join(dist.egg_info, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('PKG-INFO'): + metadata = dist.get_metadata('PKG-INFO') + + if dist.has_metadata('entry_points.txt'): + entry_points = dist.get_metadata_lines('entry_points.txt') + package['entry_points'] = entry_points + + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + package['installer'] = line.strip() + break + + # @todo: Should pkg_resources.Distribution have a + # `get_pkg_info` method? + feed_parser = FeedParser() + feed_parser.feed(metadata) + pkg_info_dict = feed_parser.close() + for key in ('metadata-version', 'summary', + 'home-page', 'author', 'author-email', 'license'): + package[key] = pkg_info_dict.get(key) + + # It looks like FeedParser cannot deal with repeated headers + classifiers = [] + for line in metadata.splitlines(): + if line.startswith('Classifier: '): + classifiers.append(line[len('Classifier: '):]) + package['classifiers'] = classifiers + + if file_list: + package['files'] = sorted(file_list) + yield package + + +def print_results(distributions, list_files=False, verbose=False): + """ + Print the informations from installed distributions found. + """ + results_printed = False + for i, dist in enumerate(distributions): + results_printed = True + if i > 0: + logger.info("---") + logger.info("Name: %s", dist.get('name', '')) + logger.info("Version: %s", dist.get('version', '')) + logger.info("Summary: %s", dist.get('summary', '')) + logger.info("Home-page: %s", dist.get('home-page', '')) + logger.info("Author: %s", dist.get('author', '')) + logger.info("Author-email: %s", dist.get('author-email', '')) + logger.info("License: %s", dist.get('license', '')) + logger.info("Location: %s", dist.get('location', '')) + logger.info("Requires: %s", ', '.join(dist.get('requires', []))) + if verbose: + logger.info("Metadata-Version: %s", + dist.get('metadata-version', '')) + logger.info("Installer: %s", dist.get('installer', '')) + logger.info("Classifiers:") + for classifier in dist.get('classifiers', []): + logger.info(" %s", classifier) + logger.info("Entry-points:") + for entry in dist.get('entry_points', []): + logger.info(" %s", entry.strip()) + if list_files: + logger.info("Files:") + for line in dist.get('files', []): + logger.info(" %s", line.strip()) + if "files" not in dist: + logger.info("Cannot locate installed-files.txt") + return results_printed diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/uninstall.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/uninstall.py new file mode 100644 index 0000000..8ba1a7c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/uninstall.py @@ -0,0 +1,76 @@ +from __future__ import absolute_import + +import pip +from pip.wheel import WheelCache +from pip.req import InstallRequirement, RequirementSet, parse_requirements +from pip.basecommand import Command +from pip.exceptions import InstallationError + + +class UninstallCommand(Command): + """ + Uninstall packages. + + pip is able to uninstall most installed packages. Known exceptions are: + + - Pure distutils packages installed with ``python setup.py install``, which + leave behind no metadata to determine what files were installed. + - Script wrappers installed by ``python setup.py develop``. + """ + name = 'uninstall' + usage = """ + %prog [options] <package> ... + %prog [options] -r <requirements file> ...""" + summary = 'Uninstall packages.' + + def __init__(self, *args, **kw): + super(UninstallCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Uninstall all the packages listed in the given requirements ' + 'file. This option can be used multiple times.', + ) + self.cmd_opts.add_option( + '-y', '--yes', + dest='yes', + action='store_true', + help="Don't ask for confirmation of uninstall deletions.") + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + with self._build_session(options) as session: + format_control = pip.index.FormatControl(set(), set()) + wheel_cache = WheelCache(options.cache_dir, format_control) + requirement_set = RequirementSet( + build_dir=None, + src_dir=None, + download_dir=None, + isolated=options.isolated_mode, + session=session, + wheel_cache=wheel_cache, + ) + for name in args: + requirement_set.add_requirement( + InstallRequirement.from_line( + name, isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + ) + for filename in options.requirements: + for req in parse_requirements( + filename, + options=options, + session=session, + wheel_cache=wheel_cache): + requirement_set.add_requirement(req) + if not requirement_set.has_requirements: + raise InstallationError( + 'You must give at least one requirement to %(name)s (see ' + '"pip help %(name)s")' % dict(name=self.name) + ) + requirement_set.uninstall(auto_confirm=options.yes) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/wheel.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/wheel.py new file mode 100644 index 0000000..70e95eb --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/commands/wheel.py @@ -0,0 +1,208 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import logging +import os +import warnings + +from pip.basecommand import RequirementCommand +from pip.exceptions import CommandError, PreviousBuildDirError +from pip.req import RequirementSet +from pip.utils import import_or_raise +from pip.utils.build import BuildDirectory +from pip.utils.deprecation import RemovedInPip10Warning +from pip.wheel import WheelCache, WheelBuilder +from pip import cmdoptions + + +logger = logging.getLogger(__name__) + + +class WheelCommand(RequirementCommand): + """ + Build Wheel archives for your requirements and dependencies. + + Wheel is a built-package format, and offers the advantage of not + recompiling your software during every install. For more details, see the + wheel docs: https://wheel.readthedocs.io/en/latest/ + + Requirements: setuptools>=0.8, and wheel. + + 'pip wheel' uses the bdist_wheel setuptools extension from the wheel + package to build individual wheels. + + """ + + name = 'wheel' + usage = """ + %prog [options] <requirement specifier> ... + %prog [options] -r <requirements file> ... + %prog [options] [-e] <vcs project url> ... + %prog [options] [-e] <local project path> ... + %prog [options] <archive url/path> ...""" + + summary = 'Build wheels from your requirements.' + + def __init__(self, *args, **kw): + super(WheelCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( + '-w', '--wheel-dir', + dest='wheel_dir', + metavar='dir', + default=os.curdir, + help=("Build wheels into <dir>, where the default is the " + "current working directory."), + ) + cmd_opts.add_option(cmdoptions.use_wheel()) + cmd_opts.add_option(cmdoptions.no_use_wheel()) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option( + '--build-option', + dest='build_options', + metavar='options', + action='append', + help="Extra arguments to be supplied to 'setup.py bdist_wheel'.") + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.src()) + cmd_opts.add_option(cmdoptions.ignore_requires_python()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.build_dir()) + + cmd_opts.add_option( + '--global-option', + dest='global_options', + action='append', + metavar='options', + help="Extra global options to be supplied to the setup.py " + "call before the 'bdist_wheel' command.") + + cmd_opts.add_option( + '--pre', + action='store_true', + default=False, + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def check_required_packages(self): + import_or_raise( + 'wheel.bdist_wheel', + CommandError, + "'pip wheel' requires the 'wheel' package. To fix this, run: " + "pip install wheel" + ) + pkg_resources = import_or_raise( + 'pkg_resources', + CommandError, + "'pip wheel' requires setuptools >= 0.8 for dist-info support." + " To fix this, run: pip install --upgrade setuptools" + ) + if not hasattr(pkg_resources, 'DistInfoDistribution'): + raise CommandError( + "'pip wheel' requires setuptools >= 0.8 for dist-info " + "support. To fix this, run: pip install --upgrade " + "setuptools" + ) + + def run(self, options, args): + self.check_required_packages() + cmdoptions.resolve_wheel_no_use_binary(options) + cmdoptions.check_install_build_global(options) + + if options.allow_external: + warnings.warn( + "--allow-external has been deprecated and will be removed in " + "the future. Due to changes in the repository protocol, it no " + "longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_all_external: + warnings.warn( + "--allow-all-external has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_unverified: + warnings.warn( + "--allow-unverified has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + + options.src_dir = os.path.abspath(options.src_dir) + + with self._build_session(options) as session: + finder = self._build_package_finder(options, session) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + with BuildDirectory(options.build_dir, + delete=build_delete) as build_dir: + requirement_set = RequirementSet( + build_dir=build_dir, + src_dir=options.src_dir, + download_dir=None, + ignore_dependencies=options.ignore_dependencies, + ignore_installed=True, + ignore_requires_python=options.ignore_requires_python, + isolated=options.isolated_mode, + session=session, + wheel_cache=wheel_cache, + wheel_download_dir=options.wheel_dir, + require_hashes=options.require_hashes + ) + + self.populate_requirement_set( + requirement_set, args, options, finder, session, self.name, + wheel_cache + ) + + if not requirement_set.has_requirements: + return + + try: + # build wheels + wb = WheelBuilder( + requirement_set, + finder, + build_options=options.build_options or [], + global_options=options.global_options or [], + ) + if not wb.build(): + raise CommandError( + "Failed to build one or more wheels" + ) + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + if not options.no_clean: + requirement_set.cleanup_files() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/compat/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/compat/__init__.py new file mode 100644 index 0000000..099672c --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/compat/__init__.py @@ -0,0 +1,164 @@ +"""Stuff that differs in different Python versions and platform +distributions.""" +from __future__ import absolute_import, division + +import os +import sys + +from pip._vendor.six import text_type + +try: + from logging.config import dictConfig as logging_dictConfig +except ImportError: + from pip.compat.dictconfig import dictConfig as logging_dictConfig + +try: + from collections import OrderedDict +except ImportError: + from pip._vendor.ordereddict import OrderedDict + +try: + import ipaddress +except ImportError: + try: + from pip._vendor import ipaddress + except ImportError: + import ipaddr as ipaddress + ipaddress.ip_address = ipaddress.IPAddress + ipaddress.ip_network = ipaddress.IPNetwork + + +try: + import sysconfig + + def get_stdlib(): + paths = [ + sysconfig.get_path("stdlib"), + sysconfig.get_path("platstdlib"), + ] + return set(filter(bool, paths)) +except ImportError: + from distutils import sysconfig + + def get_stdlib(): + paths = [ + sysconfig.get_python_lib(standard_lib=True), + sysconfig.get_python_lib(standard_lib=True, plat_specific=True), + ] + return set(filter(bool, paths)) + + +__all__ = [ + "logging_dictConfig", "ipaddress", "uses_pycache", "console_to_str", + "native_str", "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", + "OrderedDict", +] + + +if sys.version_info >= (3, 4): + uses_pycache = True + from importlib.util import cache_from_source +else: + import imp + uses_pycache = hasattr(imp, 'cache_from_source') + if uses_pycache: + cache_from_source = imp.cache_from_source + else: + cache_from_source = None + + +if sys.version_info >= (3,): + def console_to_str(s): + try: + return s.decode(sys.__stdout__.encoding) + except UnicodeDecodeError: + return s.decode('utf_8') + + def native_str(s, replace=False): + if isinstance(s, bytes): + return s.decode('utf-8', 'replace' if replace else 'strict') + return s + +else: + def console_to_str(s): + return s + + def native_str(s, replace=False): + # Replace is ignored -- unicode to UTF-8 can't fail + if isinstance(s, text_type): + return s.encode('utf-8') + return s + + +def total_seconds(td): + if hasattr(td, "total_seconds"): + return td.total_seconds() + else: + val = td.microseconds + (td.seconds + td.days * 24 * 3600) * 10 ** 6 + return val / 10 ** 6 + + +def get_path_uid(path): + """ + Return path's uid. + + Does not follow symlinks: + https://github.com/pypa/pip/pull/935#discussion_r5307003 + + Placed this function in compat due to differences on AIX and + Jython, that should eventually go away. + + :raises OSError: When path is a symlink or can't be read. + """ + if hasattr(os, 'O_NOFOLLOW'): + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + file_uid = os.fstat(fd).st_uid + os.close(fd) + else: # AIX and Jython + # WARNING: time of check vulnerability, but best we can do w/o NOFOLLOW + if not os.path.islink(path): + # older versions of Jython don't have `os.fstat` + file_uid = os.stat(path).st_uid + else: + # raise OSError for parity with os.O_NOFOLLOW above + raise OSError( + "%s is a symlink; Will not return uid for symlinks" % path + ) + return file_uid + + +def expanduser(path): + """ + Expand ~ and ~user constructions. + + Includes a workaround for http://bugs.python.org/issue14768 + """ + expanded = os.path.expanduser(path) + if path.startswith('~/') and expanded.startswith('//'): + expanded = expanded[1:] + return expanded + + +# packages in the stdlib that may have installation metadata, but should not be +# considered 'installed'. this theoretically could be determined based on +# dist.location (py27:`sysconfig.get_paths()['stdlib']`, +# py26:sysconfig.get_config_vars('LIBDEST')), but fear platform variation may +# make this ineffective, so hard-coding +stdlib_pkgs = ('python', 'wsgiref') +if sys.version_info >= (2, 7): + stdlib_pkgs += ('argparse',) + + +# windows detection, covers cpython and ironpython +WINDOWS = (sys.platform.startswith("win") or + (sys.platform == 'cli' and os.name == 'nt')) + + +def samefile(file1, file2): + """Provide an alternative for os.path.samefile on Windows/Python2""" + if hasattr(os.path, 'samefile'): + return os.path.samefile(file1, file2) + else: + path1 = os.path.normcase(os.path.abspath(file1)) + path2 = os.path.normcase(os.path.abspath(file2)) + return path1 == path2 diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/compat/dictconfig.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/compat/dictconfig.py new file mode 100644 index 0000000..ec684aa --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/compat/dictconfig.py @@ -0,0 +1,565 @@ +# This is a copy of the Python logging.config.dictconfig module, +# reproduced with permission. It is provided here for backwards +# compatibility for Python versions prior to 2.7. +# +# Copyright 2009-2010 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +from __future__ import absolute_import + +import logging.handlers +import re +import sys +import types + +from pip._vendor import six + +# flake8: noqa + +IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I) + + +def valid_ident(s): + m = IDENTIFIER.match(s) + if not m: + raise ValueError('Not a valid Python identifier: %r' % s) + return True + +# +# This function is defined in logging only in recent versions of Python +# +try: + from logging import _checkLevel +except ImportError: + def _checkLevel(level): + if isinstance(level, int): + rv = level + elif str(level) == level: + if level not in logging._levelNames: + raise ValueError('Unknown level: %r' % level) + rv = logging._levelNames[level] + else: + raise TypeError('Level not an integer or a ' + 'valid string: %r' % level) + return rv + +# The ConvertingXXX classes are wrappers around standard Python containers, +# and they serve to convert any suitable values in the container. The +# conversion converts base dicts, lists and tuples to their wrapped +# equivalents, whereas strings which match a conversion format are converted +# appropriately. +# +# Each wrapper should have a configurator attribute holding the actual +# configurator to use for conversion. + + +class ConvertingDict(dict): + """A converting dictionary wrapper.""" + + def __getitem__(self, key): + value = dict.__getitem__(self, key) + result = self.configurator.convert(value) + # If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def get(self, key, default=None): + value = dict.get(self, key, default) + result = self.configurator.convert(value) + # If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, key, default=None): + value = dict.pop(self, key, default) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + +class ConvertingList(list): + """A converting list wrapper.""" + def __getitem__(self, key): + value = list.__getitem__(self, key) + result = self.configurator.convert(value) + # If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, idx=-1): + value = list.pop(self, idx) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + return result + + +class ConvertingTuple(tuple): + """A converting tuple wrapper.""" + def __getitem__(self, key): + value = tuple.__getitem__(self, key) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + +class BaseConfigurator(object): + """ + The configurator base class which defines some useful defaults. + """ + + CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$') + + WORD_PATTERN = re.compile(r'^\s*(\w+)\s*') + DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*') + INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*') + DIGIT_PATTERN = re.compile(r'^\d+$') + + value_converters = { + 'ext' : 'ext_convert', + 'cfg' : 'cfg_convert', + } + + # We might want to use a different one, e.g. importlib + importer = __import__ + + def __init__(self, config): + self.config = ConvertingDict(config) + self.config.configurator = self + + def resolve(self, s): + """ + Resolve strings to objects using standard import and attribute + syntax. + """ + name = s.split('.') + used = name.pop(0) + try: + found = self.importer(used) + for frag in name: + used += '.' + frag + try: + found = getattr(found, frag) + except AttributeError: + self.importer(used) + found = getattr(found, frag) + return found + except ImportError: + e, tb = sys.exc_info()[1:] + v = ValueError('Cannot resolve %r: %s' % (s, e)) + v.__cause__, v.__traceback__ = e, tb + raise v + + def ext_convert(self, value): + """Default converter for the ext:// protocol.""" + return self.resolve(value) + + def cfg_convert(self, value): + """Default converter for the cfg:// protocol.""" + rest = value + m = self.WORD_PATTERN.match(rest) + if m is None: + raise ValueError("Unable to convert %r" % value) + else: + rest = rest[m.end():] + d = self.config[m.groups()[0]] + # print d, rest + while rest: + m = self.DOT_PATTERN.match(rest) + if m: + d = d[m.groups()[0]] + else: + m = self.INDEX_PATTERN.match(rest) + if m: + idx = m.groups()[0] + if not self.DIGIT_PATTERN.match(idx): + d = d[idx] + else: + try: + n = int(idx) # try as number first (most likely) + d = d[n] + except TypeError: + d = d[idx] + if m: + rest = rest[m.end():] + else: + raise ValueError('Unable to convert ' + '%r at %r' % (value, rest)) + # rest should be empty + return d + + def convert(self, value): + """ + Convert values to an appropriate type. dicts, lists and tuples are + replaced by their converting alternatives. Strings are checked to + see if they have a conversion format and are converted if they do. + """ + if not isinstance(value, ConvertingDict) and isinstance(value, dict): + value = ConvertingDict(value) + value.configurator = self + elif not isinstance(value, ConvertingList) and isinstance(value, list): + value = ConvertingList(value) + value.configurator = self + elif not isinstance(value, ConvertingTuple) and\ + isinstance(value, tuple): + value = ConvertingTuple(value) + value.configurator = self + elif isinstance(value, six.string_types): # str for py3k + m = self.CONVERT_PATTERN.match(value) + if m: + d = m.groupdict() + prefix = d['prefix'] + converter = self.value_converters.get(prefix, None) + if converter: + suffix = d['suffix'] + converter = getattr(self, converter) + value = converter(suffix) + return value + + def configure_custom(self, config): + """Configure an object with a user-supplied factory.""" + c = config.pop('()') + if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType: + c = self.resolve(c) + props = config.pop('.', None) + # Check for valid identifiers + kwargs = dict((k, config[k]) for k in config if valid_ident(k)) + result = c(**kwargs) + if props: + for name, value in props.items(): + setattr(result, name, value) + return result + + def as_tuple(self, value): + """Utility function which converts lists to tuples.""" + if isinstance(value, list): + value = tuple(value) + return value + + +class DictConfigurator(BaseConfigurator): + """ + Configure logging using a dictionary-like object to describe the + configuration. + """ + + def configure(self): + """Do the configuration.""" + + config = self.config + if 'version' not in config: + raise ValueError("dictionary doesn't specify a version") + if config['version'] != 1: + raise ValueError("Unsupported version: %s" % config['version']) + incremental = config.pop('incremental', False) + EMPTY_DICT = {} + logging._acquireLock() + try: + if incremental: + handlers = config.get('handlers', EMPTY_DICT) + # incremental handler config only if handler name + # ties in to logging._handlers (Python 2.7) + if sys.version_info[:2] == (2, 7): + for name in handlers: + if name not in logging._handlers: + raise ValueError('No handler found with ' + 'name %r' % name) + else: + try: + handler = logging._handlers[name] + handler_config = handlers[name] + level = handler_config.get('level', None) + if level: + handler.setLevel(_checkLevel(level)) + except StandardError as e: + raise ValueError('Unable to configure handler ' + '%r: %s' % (name, e)) + loggers = config.get('loggers', EMPTY_DICT) + for name in loggers: + try: + self.configure_logger(name, loggers[name], True) + except StandardError as e: + raise ValueError('Unable to configure logger ' + '%r: %s' % (name, e)) + root = config.get('root', None) + if root: + try: + self.configure_root(root, True) + except StandardError as e: + raise ValueError('Unable to configure root ' + 'logger: %s' % e) + else: + disable_existing = config.pop('disable_existing_loggers', True) + + logging._handlers.clear() + del logging._handlerList[:] + + # Do formatters first - they don't refer to anything else + formatters = config.get('formatters', EMPTY_DICT) + for name in formatters: + try: + formatters[name] = self.configure_formatter( + formatters[name]) + except StandardError as e: + raise ValueError('Unable to configure ' + 'formatter %r: %s' % (name, e)) + # Next, do filters - they don't refer to anything else, either + filters = config.get('filters', EMPTY_DICT) + for name in filters: + try: + filters[name] = self.configure_filter(filters[name]) + except StandardError as e: + raise ValueError('Unable to configure ' + 'filter %r: %s' % (name, e)) + + # Next, do handlers - they refer to formatters and filters + # As handlers can refer to other handlers, sort the keys + # to allow a deterministic order of configuration + handlers = config.get('handlers', EMPTY_DICT) + for name in sorted(handlers): + try: + handler = self.configure_handler(handlers[name]) + handler.name = name + handlers[name] = handler + except StandardError as e: + raise ValueError('Unable to configure handler ' + '%r: %s' % (name, e)) + # Next, do loggers - they refer to handlers and filters + + # we don't want to lose the existing loggers, + # since other threads may have pointers to them. + # existing is set to contain all existing loggers, + # and as we go through the new configuration we + # remove any which are configured. At the end, + # what's left in existing is the set of loggers + # which were in the previous configuration but + # which are not in the new configuration. + root = logging.root + existing = list(root.manager.loggerDict) + # The list needs to be sorted so that we can + # avoid disabling child loggers of explicitly + # named loggers. With a sorted list it is easier + # to find the child loggers. + existing.sort() + # We'll keep the list of existing loggers + # which are children of named loggers here... + child_loggers = [] + # now set up the new ones... + loggers = config.get('loggers', EMPTY_DICT) + for name in loggers: + if name in existing: + i = existing.index(name) + prefixed = name + "." + pflen = len(prefixed) + num_existing = len(existing) + i = i + 1 # look at the entry after name + while (i < num_existing) and\ + (existing[i][:pflen] == prefixed): + child_loggers.append(existing[i]) + i = i + 1 + existing.remove(name) + try: + self.configure_logger(name, loggers[name]) + except StandardError as e: + raise ValueError('Unable to configure logger ' + '%r: %s' % (name, e)) + + # Disable any old loggers. There's no point deleting + # them as other threads may continue to hold references + # and by disabling them, you stop them doing any logging. + # However, don't disable children of named loggers, as that's + # probably not what was intended by the user. + for log in existing: + logger = root.manager.loggerDict[log] + if log in child_loggers: + logger.level = logging.NOTSET + logger.handlers = [] + logger.propagate = True + elif disable_existing: + logger.disabled = True + + # And finally, do the root logger + root = config.get('root', None) + if root: + try: + self.configure_root(root) + except StandardError as e: + raise ValueError('Unable to configure root ' + 'logger: %s' % e) + finally: + logging._releaseLock() + + def configure_formatter(self, config): + """Configure a formatter from a dictionary.""" + if '()' in config: + factory = config['()'] # for use in exception handler + try: + result = self.configure_custom(config) + except TypeError as te: + if "'format'" not in str(te): + raise + # Name of parameter changed from fmt to format. + # Retry with old name. + # This is so that code can be used with older Python versions + #(e.g. by Django) + config['fmt'] = config.pop('format') + config['()'] = factory + result = self.configure_custom(config) + else: + fmt = config.get('format', None) + dfmt = config.get('datefmt', None) + result = logging.Formatter(fmt, dfmt) + return result + + def configure_filter(self, config): + """Configure a filter from a dictionary.""" + if '()' in config: + result = self.configure_custom(config) + else: + name = config.get('name', '') + result = logging.Filter(name) + return result + + def add_filters(self, filterer, filters): + """Add filters to a filterer from a list of names.""" + for f in filters: + try: + filterer.addFilter(self.config['filters'][f]) + except StandardError as e: + raise ValueError('Unable to add filter %r: %s' % (f, e)) + + def configure_handler(self, config): + """Configure a handler from a dictionary.""" + formatter = config.pop('formatter', None) + if formatter: + try: + formatter = self.config['formatters'][formatter] + except StandardError as e: + raise ValueError('Unable to set formatter ' + '%r: %s' % (formatter, e)) + level = config.pop('level', None) + filters = config.pop('filters', None) + if '()' in config: + c = config.pop('()') + if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType: + c = self.resolve(c) + factory = c + else: + klass = self.resolve(config.pop('class')) + # Special case for handler which refers to another handler + if issubclass(klass, logging.handlers.MemoryHandler) and\ + 'target' in config: + try: + config['target'] = self.config['handlers'][config['target']] + except StandardError as e: + raise ValueError('Unable to set target handler ' + '%r: %s' % (config['target'], e)) + elif issubclass(klass, logging.handlers.SMTPHandler) and\ + 'mailhost' in config: + config['mailhost'] = self.as_tuple(config['mailhost']) + elif issubclass(klass, logging.handlers.SysLogHandler) and\ + 'address' in config: + config['address'] = self.as_tuple(config['address']) + factory = klass + kwargs = dict((k, config[k]) for k in config if valid_ident(k)) + try: + result = factory(**kwargs) + except TypeError as te: + if "'stream'" not in str(te): + raise + # The argument name changed from strm to stream + # Retry with old name. + # This is so that code can be used with older Python versions + #(e.g. by Django) + kwargs['strm'] = kwargs.pop('stream') + result = factory(**kwargs) + if formatter: + result.setFormatter(formatter) + if level is not None: + result.setLevel(_checkLevel(level)) + if filters: + self.add_filters(result, filters) + return result + + def add_handlers(self, logger, handlers): + """Add handlers to a logger from a list of names.""" + for h in handlers: + try: + logger.addHandler(self.config['handlers'][h]) + except StandardError as e: + raise ValueError('Unable to add handler %r: %s' % (h, e)) + + def common_logger_config(self, logger, config, incremental=False): + """ + Perform configuration which is common to root and non-root loggers. + """ + level = config.get('level', None) + if level is not None: + logger.setLevel(_checkLevel(level)) + if not incremental: + # Remove any existing handlers + for h in logger.handlers[:]: + logger.removeHandler(h) + handlers = config.get('handlers', None) + if handlers: + self.add_handlers(logger, handlers) + filters = config.get('filters', None) + if filters: + self.add_filters(logger, filters) + + def configure_logger(self, name, config, incremental=False): + """Configure a non-root logger from a dictionary.""" + logger = logging.getLogger(name) + self.common_logger_config(logger, config, incremental) + propagate = config.get('propagate', None) + if propagate is not None: + logger.propagate = propagate + + def configure_root(self, config, incremental=False): + """Configure a root logger from a dictionary.""" + root = logging.getLogger() + self.common_logger_config(root, config, incremental) + +dictConfigClass = DictConfigurator + + +def dictConfig(config): + """Configure logging using a dictionary.""" + dictConfigClass(config).configure() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/download.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/download.py new file mode 100644 index 0000000..54d3131 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/download.py @@ -0,0 +1,906 @@ +from __future__ import absolute_import + +import cgi +import email.utils +import getpass +import json +import logging +import mimetypes +import os +import platform +import re +import shutil +import sys +import tempfile + +try: + import ssl # noqa + HAS_TLS = True +except ImportError: + HAS_TLS = False + +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +import pip + +from pip.exceptions import InstallationError, HashMismatch +from pip.models import PyPI +from pip.utils import (splitext, rmtree, format_size, display_path, + backup_dir, ask_path_exists, unpack_file, + ARCHIVE_EXTENSIONS, consume, call_subprocess) +from pip.utils.encoding import auto_decode +from pip.utils.filesystem import check_path_owner +from pip.utils.logging import indent_log +from pip.utils.setuptools_build import SETUPTOOLS_SHIM +from pip.utils.glibc import libc_ver +from pip.utils.ui import DownloadProgressBar, DownloadProgressSpinner +from pip.locations import write_delete_marker_file +from pip.vcs import vcs +from pip._vendor import requests, six +from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter +from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response +from pip._vendor.requests.utils import get_netrc_auth +from pip._vendor.requests.structures import CaseInsensitiveDict +from pip._vendor.requests.packages import urllib3 +from pip._vendor.cachecontrol import CacheControlAdapter +from pip._vendor.cachecontrol.caches import FileCache +from pip._vendor.lockfile import LockError +from pip._vendor.six.moves import xmlrpc_client + + +__all__ = ['get_file_content', + 'is_url', 'url_to_path', 'path_to_url', + 'is_archive_file', 'unpack_vcs_link', + 'unpack_file_url', 'is_vcs_url', 'is_file_url', + 'unpack_http_url', 'unpack_url'] + + +logger = logging.getLogger(__name__) + + +def user_agent(): + """ + Return a string representing the user agent. + """ + data = { + "installer": {"name": "pip", "version": pip.__version__}, + "python": platform.python_version(), + "implementation": { + "name": platform.python_implementation(), + }, + } + + if data["implementation"]["name"] == 'CPython': + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'PyPy': + if sys.pypy_version_info.releaselevel == 'final': + pypy_version_info = sys.pypy_version_info[:3] + else: + pypy_version_info = sys.pypy_version_info + data["implementation"]["version"] = ".".join( + [str(x) for x in pypy_version_info] + ) + elif data["implementation"]["name"] == 'Jython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'IronPython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + + if sys.platform.startswith("linux"): + from pip._vendor import distro + distro_infos = dict(filter( + lambda x: x[1], + zip(["name", "version", "id"], distro.linux_distribution()), + )) + libc = dict(filter( + lambda x: x[1], + zip(["lib", "version"], libc_ver()), + )) + if libc: + distro_infos["libc"] = libc + if distro_infos: + data["distro"] = distro_infos + + if sys.platform.startswith("darwin") and platform.mac_ver()[0]: + data["distro"] = {"name": "macOS", "version": platform.mac_ver()[0]} + + if platform.system(): + data.setdefault("system", {})["name"] = platform.system() + + if platform.release(): + data.setdefault("system", {})["release"] = platform.release() + + if platform.machine(): + data["cpu"] = platform.machine() + + # Python 2.6 doesn't have ssl.OPENSSL_VERSION. + if HAS_TLS and sys.version_info[:2] > (2, 6): + data["openssl_version"] = ssl.OPENSSL_VERSION + + return "{data[installer][name]}/{data[installer][version]} {json}".format( + data=data, + json=json.dumps(data, separators=(",", ":"), sort_keys=True), + ) + + +class MultiDomainBasicAuth(AuthBase): + + def __init__(self, prompting=True): + self.prompting = prompting + self.passwords = {} + + def __call__(self, req): + parsed = urllib_parse.urlparse(req.url) + + # Get the netloc without any embedded credentials + netloc = parsed.netloc.rsplit("@", 1)[-1] + + # Set the url of the request to the url without any credentials + req.url = urllib_parse.urlunparse(parsed[:1] + (netloc,) + parsed[2:]) + + # Use any stored credentials that we have for this netloc + username, password = self.passwords.get(netloc, (None, None)) + + # Extract credentials embedded in the url if we have none stored + if username is None: + username, password = self.parse_credentials(parsed.netloc) + + # Get creds from netrc if we still don't have them + if username is None and password is None: + netrc_auth = get_netrc_auth(req.url) + username, password = netrc_auth if netrc_auth else (None, None) + + if username or password: + # Store the username and password + self.passwords[netloc] = (username, password) + + # Send the basic auth with this request + req = HTTPBasicAuth(username or "", password or "")(req) + + # Attach a hook to handle 401 responses + req.register_hook("response", self.handle_401) + + return req + + def handle_401(self, resp, **kwargs): + # We only care about 401 responses, anything else we want to just + # pass through the actual response + if resp.status_code != 401: + return resp + + # We are not able to prompt the user so simply return the response + if not self.prompting: + return resp + + parsed = urllib_parse.urlparse(resp.url) + + # Prompt the user for a new username and password + username = six.moves.input("User for %s: " % parsed.netloc) + password = getpass.getpass("Password: ") + + # Store the new username and password to use for future requests + if username or password: + self.passwords[parsed.netloc] = (username, password) + + # Consume content and release the original connection to allow our new + # request to reuse the same one. + resp.content + resp.raw.release_conn() + + # Add our new username and password to the request + req = HTTPBasicAuth(username or "", password or "")(resp.request) + + # Send our new request + new_resp = resp.connection.send(req, **kwargs) + new_resp.history.append(resp) + + return new_resp + + def parse_credentials(self, netloc): + if "@" in netloc: + userinfo = netloc.rsplit("@", 1)[0] + if ":" in userinfo: + return userinfo.split(":", 1) + return userinfo, None + return None, None + + +class LocalFSAdapter(BaseAdapter): + + def send(self, request, stream=None, timeout=None, verify=None, cert=None, + proxies=None): + pathname = url_to_path(request.url) + + resp = Response() + resp.status_code = 200 + resp.url = request.url + + try: + stats = os.stat(pathname) + except OSError as exc: + resp.status_code = 404 + resp.raw = exc + else: + modified = email.utils.formatdate(stats.st_mtime, usegmt=True) + content_type = mimetypes.guess_type(pathname)[0] or "text/plain" + resp.headers = CaseInsensitiveDict({ + "Content-Type": content_type, + "Content-Length": stats.st_size, + "Last-Modified": modified, + }) + + resp.raw = open(pathname, "rb") + resp.close = resp.raw.close + + return resp + + def close(self): + pass + + +class SafeFileCache(FileCache): + """ + A file based cache which is safe to use even when the target directory may + not be accessible or writable. + """ + + def __init__(self, *args, **kwargs): + super(SafeFileCache, self).__init__(*args, **kwargs) + + # Check to ensure that the directory containing our cache directory + # is owned by the user current executing pip. If it does not exist + # we will check the parent directory until we find one that does exist. + # If it is not owned by the user executing pip then we will disable + # the cache and log a warning. + if not check_path_owner(self.directory): + logger.warning( + "The directory '%s' or its parent directory is not owned by " + "the current user and the cache has been disabled. Please " + "check the permissions and owner of that directory. If " + "executing pip with sudo, you may want sudo's -H flag.", + self.directory, + ) + + # Set our directory to None to disable the Cache + self.directory = None + + def get(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return + + try: + return super(SafeFileCache, self).get(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + def set(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return + + try: + return super(SafeFileCache, self).set(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + def delete(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return + + try: + return super(SafeFileCache, self).delete(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + +class InsecureHTTPAdapter(HTTPAdapter): + + def cert_verify(self, conn, url, verify, cert): + conn.cert_reqs = 'CERT_NONE' + conn.ca_certs = None + + +class PipSession(requests.Session): + + timeout = None + + def __init__(self, *args, **kwargs): + retries = kwargs.pop("retries", 0) + cache = kwargs.pop("cache", None) + insecure_hosts = kwargs.pop("insecure_hosts", []) + + super(PipSession, self).__init__(*args, **kwargs) + + # Attach our User Agent to the request + self.headers["User-Agent"] = user_agent() + + # Attach our Authentication handler to the session + self.auth = MultiDomainBasicAuth() + + # Create our urllib3.Retry instance which will allow us to customize + # how we handle retries. + retries = urllib3.Retry( + # Set the total number of retries that a particular request can + # have. + total=retries, + + # A 503 error from PyPI typically means that the Fastly -> Origin + # connection got interrupted in some way. A 503 error in general + # is typically considered a transient error so we'll go ahead and + # retry it. + status_forcelist=[503], + + # Add a small amount of back off between failed requests in + # order to prevent hammering the service. + backoff_factor=0.25, + ) + + # We want to _only_ cache responses on securely fetched origins. We do + # this because we can't validate the response of an insecurely fetched + # origin, and we don't want someone to be able to poison the cache and + # require manual eviction from the cache to fix it. + if cache: + secure_adapter = CacheControlAdapter( + cache=SafeFileCache(cache, use_dir_lock=True), + max_retries=retries, + ) + else: + secure_adapter = HTTPAdapter(max_retries=retries) + + # Our Insecure HTTPAdapter disables HTTPS validation. It does not + # support caching (see above) so we'll use it for all http:// URLs as + # well as any https:// host that we've marked as ignoring TLS errors + # for. + insecure_adapter = InsecureHTTPAdapter(max_retries=retries) + + self.mount("https://", secure_adapter) + self.mount("http://", insecure_adapter) + + # Enable file:// urls + self.mount("file://", LocalFSAdapter()) + + # We want to use a non-validating adapter for any requests which are + # deemed insecure. + for host in insecure_hosts: + self.mount("https://{0}/".format(host), insecure_adapter) + + def request(self, method, url, *args, **kwargs): + # Allow setting a default timeout on a session + kwargs.setdefault("timeout", self.timeout) + + # Dispatch the actual request + return super(PipSession, self).request(method, url, *args, **kwargs) + + +def get_file_content(url, comes_from=None, session=None): + """Gets the content of a file; it may be a filename, file: URL, or + http: URL. Returns (location, content). Content is unicode.""" + if session is None: + raise TypeError( + "get_file_content() missing 1 required keyword argument: 'session'" + ) + + match = _scheme_re.search(url) + if match: + scheme = match.group(1).lower() + if (scheme == 'file' and comes_from and + comes_from.startswith('http')): + raise InstallationError( + 'Requirements file %s references URL %s, which is local' + % (comes_from, url)) + if scheme == 'file': + path = url.split(':', 1)[1] + path = path.replace('\\', '/') + match = _url_slash_drive_re.match(path) + if match: + path = match.group(1) + ':' + path.split('|', 1)[1] + path = urllib_parse.unquote(path) + if path.startswith('/'): + path = '/' + path.lstrip('/') + url = path + else: + # FIXME: catch some errors + resp = session.get(url) + resp.raise_for_status() + return resp.url, resp.text + try: + with open(url, 'rb') as f: + content = auto_decode(f.read()) + except IOError as exc: + raise InstallationError( + 'Could not open requirements file: %s' % str(exc) + ) + return url, content + + +_scheme_re = re.compile(r'^(http|https|file):', re.I) +_url_slash_drive_re = re.compile(r'/*([a-z])\|', re.I) + + +def is_url(name): + """Returns true if the name looks like a URL""" + if ':' not in name: + return False + scheme = name.split(':', 1)[0].lower() + return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes + + +def url_to_path(url): + """ + Convert a file: URL to a path. + """ + assert url.startswith('file:'), ( + "You can only turn file: urls into filenames (not %r)" % url) + + _, netloc, path, _, _ = urllib_parse.urlsplit(url) + + # if we have a UNC path, prepend UNC share notation + if netloc: + netloc = '\\\\' + netloc + + path = urllib_request.url2pathname(netloc + path) + return path + + +def path_to_url(path): + """ + Convert a path to a file: URL. The path will be made absolute and have + quoted path parts. + """ + path = os.path.normpath(os.path.abspath(path)) + url = urllib_parse.urljoin('file:', urllib_request.pathname2url(path)) + return url + + +def is_archive_file(name): + """Return True if `name` is a considered as an archive file.""" + ext = splitext(name)[1].lower() + if ext in ARCHIVE_EXTENSIONS: + return True + return False + + +def unpack_vcs_link(link, location): + vcs_backend = _get_used_vcs_backend(link) + vcs_backend.unpack(location) + + +def _get_used_vcs_backend(link): + for backend in vcs.backends: + if link.scheme in backend.schemes: + vcs_backend = backend(link.url) + return vcs_backend + + +def is_vcs_url(link): + return bool(_get_used_vcs_backend(link)) + + +def is_file_url(link): + return link.url.lower().startswith('file:') + + +def is_dir_url(link): + """Return whether a file:// Link points to a directory. + + ``link`` must not have any other scheme but file://. Call is_file_url() + first. + + """ + link_path = url_to_path(link.url_without_fragment) + return os.path.isdir(link_path) + + +def _progress_indicator(iterable, *args, **kwargs): + return iterable + + +def _download_url(resp, link, content_file, hashes): + try: + total_length = int(resp.headers['content-length']) + except (ValueError, KeyError, TypeError): + total_length = 0 + + cached_resp = getattr(resp, "from_cache", False) + + if logger.getEffectiveLevel() > logging.INFO: + show_progress = False + elif cached_resp: + show_progress = False + elif total_length > (40 * 1000): + show_progress = True + elif not total_length: + show_progress = True + else: + show_progress = False + + show_url = link.show_url + + def resp_read(chunk_size): + try: + # Special case for urllib3. + for chunk in resp.raw.stream( + chunk_size, + # We use decode_content=False here because we don't + # want urllib3 to mess with the raw bytes we get + # from the server. If we decompress inside of + # urllib3 then we cannot verify the checksum + # because the checksum will be of the compressed + # file. This breakage will only occur if the + # server adds a Content-Encoding header, which + # depends on how the server was configured: + # - Some servers will notice that the file isn't a + # compressible file and will leave the file alone + # and with an empty Content-Encoding + # - Some servers will notice that the file is + # already compressed and will leave the file + # alone and will add a Content-Encoding: gzip + # header + # - Some servers won't notice anything at all and + # will take a file that's already been compressed + # and compress it again and set the + # Content-Encoding: gzip header + # + # By setting this not to decode automatically we + # hope to eliminate problems with the second case. + decode_content=False): + yield chunk + except AttributeError: + # Standard file-like object. + while True: + chunk = resp.raw.read(chunk_size) + if not chunk: + break + yield chunk + + def written_chunks(chunks): + for chunk in chunks: + content_file.write(chunk) + yield chunk + + progress_indicator = _progress_indicator + + if link.netloc == PyPI.netloc: + url = show_url + else: + url = link.url_without_fragment + + if show_progress: # We don't show progress on cached responses + if total_length: + logger.info("Downloading %s (%s)", url, format_size(total_length)) + progress_indicator = DownloadProgressBar(max=total_length).iter + else: + logger.info("Downloading %s", url) + progress_indicator = DownloadProgressSpinner().iter + elif cached_resp: + logger.info("Using cached %s", url) + else: + logger.info("Downloading %s", url) + + logger.debug('Downloading from URL %s', link) + + downloaded_chunks = written_chunks( + progress_indicator( + resp_read(CONTENT_CHUNK_SIZE), + CONTENT_CHUNK_SIZE + ) + ) + if hashes: + hashes.check_against_chunks(downloaded_chunks) + else: + consume(downloaded_chunks) + + +def _copy_file(filename, location, link): + copy = True + download_location = os.path.join(location, link.filename) + if os.path.exists(download_location): + response = ask_path_exists( + 'The file %s exists. (i)gnore, (w)ipe, (b)ackup, (a)abort' % + display_path(download_location), ('i', 'w', 'b', 'a')) + if response == 'i': + copy = False + elif response == 'w': + logger.warning('Deleting %s', display_path(download_location)) + os.remove(download_location) + elif response == 'b': + dest_file = backup_dir(download_location) + logger.warning( + 'Backing up %s to %s', + display_path(download_location), + display_path(dest_file), + ) + shutil.move(download_location, dest_file) + elif response == 'a': + sys.exit(-1) + if copy: + shutil.copy(filename, download_location) + logger.info('Saved %s', display_path(download_location)) + + +def unpack_http_url(link, location, download_dir=None, + session=None, hashes=None): + if session is None: + raise TypeError( + "unpack_http_url() missing 1 required keyword argument: 'session'" + ) + + temp_dir = tempfile.mkdtemp('-unpack', 'pip-') + + # If a download dir is specified, is the file already downloaded there? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir(link, + download_dir, + hashes) + + if already_downloaded_path: + from_path = already_downloaded_path + content_type = mimetypes.guess_type(from_path)[0] + else: + # let's download to a tmp dir + from_path, content_type = _download_http_url(link, + session, + temp_dir, + hashes) + + # unpack the archive to the build dir location. even when only downloading + # archives, they have to be unpacked to parse dependencies + unpack_file(from_path, location, content_type, link) + + # a download dir is specified; let's copy the archive there + if download_dir and not already_downloaded_path: + _copy_file(from_path, download_dir, link) + + if not already_downloaded_path: + os.unlink(from_path) + rmtree(temp_dir) + + +def unpack_file_url(link, location, download_dir=None, hashes=None): + """Unpack link into location. + + If download_dir is provided and link points to a file, make a copy + of the link file inside download_dir. + """ + link_path = url_to_path(link.url_without_fragment) + + # If it's a url to a local directory + if is_dir_url(link): + if os.path.isdir(location): + rmtree(location) + shutil.copytree(link_path, location, symlinks=True) + if download_dir: + logger.info('Link is a directory, ignoring download_dir') + return + + # If --require-hashes is off, `hashes` is either empty, the + # link's embedded hash, or MissingHashes; it is required to + # match. If --require-hashes is on, we are satisfied by any + # hash in `hashes` matching: a URL-based or an option-based + # one; no internet-sourced hash will be in `hashes`. + if hashes: + hashes.check_against_path(link_path) + + # If a download dir is specified, is the file already there and valid? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir(link, + download_dir, + hashes) + + if already_downloaded_path: + from_path = already_downloaded_path + else: + from_path = link_path + + content_type = mimetypes.guess_type(from_path)[0] + + # unpack the archive to the build dir location. even when only downloading + # archives, they have to be unpacked to parse dependencies + unpack_file(from_path, location, content_type, link) + + # a download dir is specified and not already downloaded + if download_dir and not already_downloaded_path: + _copy_file(from_path, download_dir, link) + + +def _copy_dist_from_dir(link_path, location): + """Copy distribution files in `link_path` to `location`. + + Invoked when user requests to install a local directory. E.g.: + + pip install . + pip install ~/dev/git-repos/python-prompt-toolkit + + """ + + # Note: This is currently VERY SLOW if you have a lot of data in the + # directory, because it copies everything with `shutil.copytree`. + # What it should really do is build an sdist and install that. + # See https://github.com/pypa/pip/issues/2195 + + if os.path.isdir(location): + rmtree(location) + + # build an sdist + setup_py = 'setup.py' + sdist_args = [sys.executable] + sdist_args.append('-c') + sdist_args.append(SETUPTOOLS_SHIM % setup_py) + sdist_args.append('sdist') + sdist_args += ['--dist-dir', location] + logger.info('Running setup.py sdist for %s', link_path) + + with indent_log(): + call_subprocess(sdist_args, cwd=link_path, show_stdout=False) + + # unpack sdist into `location` + sdist = os.path.join(location, os.listdir(location)[0]) + logger.info('Unpacking sdist %s into %s', sdist, location) + unpack_file(sdist, location, content_type=None, link=None) + + +class PipXmlrpcTransport(xmlrpc_client.Transport): + """Provide a `xmlrpclib.Transport` implementation via a `PipSession` + object. + """ + + def __init__(self, index_url, session, use_datetime=False): + xmlrpc_client.Transport.__init__(self, use_datetime) + index_parts = urllib_parse.urlparse(index_url) + self._scheme = index_parts.scheme + self._session = session + + def request(self, host, handler, request_body, verbose=False): + parts = (self._scheme, host, handler, None, None, None) + url = urllib_parse.urlunparse(parts) + try: + headers = {'Content-Type': 'text/xml'} + response = self._session.post(url, data=request_body, + headers=headers, stream=True) + response.raise_for_status() + self.verbose = verbose + return self.parse_response(response.raw) + except requests.HTTPError as exc: + logger.critical( + "HTTP error %s while getting %s", + exc.response.status_code, url, + ) + raise + + +def unpack_url(link, location, download_dir=None, + only_download=False, session=None, hashes=None): + """Unpack link. + If link is a VCS link: + if only_download, export into download_dir and ignore location + else unpack into location + for other types of link: + - unpack into location + - if download_dir, copy the file into download_dir + - if only_download, mark location for deletion + + :param hashes: A Hashes object, one of whose embedded hashes must match, + or HashMismatch will be raised. If the Hashes is empty, no matches are + required, and unhashable types of requirements (like VCS ones, which + would ordinarily raise HashUnsupported) are allowed. + """ + # non-editable vcs urls + if is_vcs_url(link): + unpack_vcs_link(link, location) + + # file urls + elif is_file_url(link): + unpack_file_url(link, location, download_dir, hashes=hashes) + + # http urls + else: + if session is None: + session = PipSession() + + unpack_http_url( + link, + location, + download_dir, + session, + hashes=hashes + ) + if only_download: + write_delete_marker_file(location) + + +def _download_http_url(link, session, temp_dir, hashes): + """Download link url into temp_dir using provided session""" + target_url = link.url.split('#', 1)[0] + try: + resp = session.get( + target_url, + # We use Accept-Encoding: identity here because requests + # defaults to accepting compressed responses. This breaks in + # a variety of ways depending on how the server is configured. + # - Some servers will notice that the file isn't a compressible + # file and will leave the file alone and with an empty + # Content-Encoding + # - Some servers will notice that the file is already + # compressed and will leave the file alone and will add a + # Content-Encoding: gzip header + # - Some servers won't notice anything at all and will take + # a file that's already been compressed and compress it again + # and set the Content-Encoding: gzip header + # By setting this to request only the identity encoding We're + # hoping to eliminate the third case. Hopefully there does not + # exist a server which when given a file will notice it is + # already compressed and that you're not asking for a + # compressed file and will then decompress it before sending + # because if that's the case I don't think it'll ever be + # possible to make this work. + headers={"Accept-Encoding": "identity"}, + stream=True, + ) + resp.raise_for_status() + except requests.HTTPError as exc: + logger.critical( + "HTTP error %s while getting %s", exc.response.status_code, link, + ) + raise + + content_type = resp.headers.get('content-type', '') + filename = link.filename # fallback + # Have a look at the Content-Disposition header for a better guess + content_disposition = resp.headers.get('content-disposition') + if content_disposition: + type, params = cgi.parse_header(content_disposition) + # We use ``or`` here because we don't want to use an "empty" value + # from the filename param. + filename = params.get('filename') or filename + ext = splitext(filename)[1] + if not ext: + ext = mimetypes.guess_extension(content_type) + if ext: + filename += ext + if not ext and link.url != resp.url: + ext = os.path.splitext(resp.url)[1] + if ext: + filename += ext + file_path = os.path.join(temp_dir, filename) + with open(file_path, 'wb') as content_file: + _download_url(resp, link, content_file, hashes) + return file_path, content_type + + +def _check_download_dir(link, download_dir, hashes): + """ Check download_dir for previously downloaded file with correct hash + If a correct file is found return its path else None + """ + download_path = os.path.join(download_dir, link.filename) + if os.path.exists(download_path): + # If already downloaded, does its hash match? + logger.info('File was already downloaded %s', download_path) + if hashes: + try: + hashes.check_against_path(download_path) + except HashMismatch: + logger.warning( + 'Previously-downloaded file %s has bad hash. ' + 'Re-downloading.', + download_path + ) + os.unlink(download_path) + return None + return download_path + return None diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/exceptions.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/exceptions.py new file mode 100644 index 0000000..50b527f --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/exceptions.py @@ -0,0 +1,244 @@ +"""Exceptions used throughout package""" +from __future__ import absolute_import + +from itertools import chain, groupby, repeat + +from pip._vendor.six import iteritems + + +class PipError(Exception): + """Base pip exception""" + + +class InstallationError(PipError): + """General exception during installation""" + + +class UninstallationError(PipError): + """General exception during uninstallation""" + + +class DistributionNotFound(InstallationError): + """Raised when a distribution cannot be found to satisfy a requirement""" + + +class RequirementsFileParseError(InstallationError): + """Raised when a general error occurs parsing a requirements file line.""" + + +class BestVersionAlreadyInstalled(PipError): + """Raised when the most up-to-date version of a package is already + installed.""" + + +class BadCommand(PipError): + """Raised when virtualenv or a command is not found""" + + +class CommandError(PipError): + """Raised when there is an error in command-line arguments""" + + +class PreviousBuildDirError(PipError): + """Raised when there's a previous conflicting build directory""" + + +class InvalidWheelFilename(InstallationError): + """Invalid wheel filename.""" + + +class UnsupportedWheel(InstallationError): + """Unsupported wheel.""" + + +class HashErrors(InstallationError): + """Multiple HashError instances rolled into one for reporting""" + + def __init__(self): + self.errors = [] + + def append(self, error): + self.errors.append(error) + + def __str__(self): + lines = [] + self.errors.sort(key=lambda e: e.order) + for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__): + lines.append(cls.head) + lines.extend(e.body() for e in errors_of_cls) + if lines: + return '\n'.join(lines) + + def __nonzero__(self): + return bool(self.errors) + + def __bool__(self): + return self.__nonzero__() + + +class HashError(InstallationError): + """ + A failure to verify a package against known-good hashes + + :cvar order: An int sorting hash exception classes by difficulty of + recovery (lower being harder), so the user doesn't bother fretting + about unpinned packages when he has deeper issues, like VCS + dependencies, to deal with. Also keeps error reports in a + deterministic order. + :cvar head: A section heading for display above potentially many + exceptions of this kind + :ivar req: The InstallRequirement that triggered this error. This is + pasted on after the exception is instantiated, because it's not + typically available earlier. + + """ + req = None + head = '' + + def body(self): + """Return a summary of me for display under the heading. + + This default implementation simply prints a description of the + triggering requirement. + + :param req: The InstallRequirement that provoked this error, with + populate_link() having already been called + + """ + return ' %s' % self._requirement_name() + + def __str__(self): + return '%s\n%s' % (self.head, self.body()) + + def _requirement_name(self): + """Return a description of the requirement that triggered me. + + This default implementation returns long description of the req, with + line numbers + + """ + return str(self.req) if self.req else 'unknown package' + + +class VcsHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 0 + head = ("Can't verify hashes for these requirements because we don't " + "have a way to hash version control repositories:") + + +class DirectoryUrlHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 1 + head = ("Can't verify hashes for these file:// requirements because they " + "point to directories:") + + +class HashMissing(HashError): + """A hash was needed for a requirement but is absent.""" + + order = 2 + head = ('Hashes are required in --require-hashes mode, but they are ' + 'missing from some requirements. Here is a list of those ' + 'requirements along with the hashes their downloaded archives ' + 'actually had. Add lines like these to your requirements files to ' + 'prevent tampering. (If you did not enable --require-hashes ' + 'manually, note that it turns on automatically when any package ' + 'has a hash.)') + + def __init__(self, gotten_hash): + """ + :param gotten_hash: The hash of the (possibly malicious) archive we + just downloaded + """ + self.gotten_hash = gotten_hash + + def body(self): + from pip.utils.hashes import FAVORITE_HASH # Dodge circular import. + + package = None + if self.req: + # In the case of URL-based requirements, display the original URL + # seen in the requirements file rather than the package name, + # so the output can be directly copied into the requirements file. + package = (self.req.original_link if self.req.original_link + # In case someone feeds something downright stupid + # to InstallRequirement's constructor. + else getattr(self.req, 'req', None)) + return ' %s --hash=%s:%s' % (package or 'unknown package', + FAVORITE_HASH, + self.gotten_hash) + + +class HashUnpinned(HashError): + """A requirement had a hash specified but was not pinned to a specific + version.""" + + order = 3 + head = ('In --require-hashes mode, all requirements must have their ' + 'versions pinned with ==. These do not:') + + +class HashMismatch(HashError): + """ + Distribution file hash values don't match. + + :ivar package_name: The name of the package that triggered the hash + mismatch. Feel free to write to this after the exception is raise to + improve its error message. + + """ + order = 4 + head = ('THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS ' + 'FILE. If you have updated the package versions, please update ' + 'the hashes. Otherwise, examine the package contents carefully; ' + 'someone may have tampered with them.') + + def __init__(self, allowed, gots): + """ + :param allowed: A dict of algorithm names pointing to lists of allowed + hex digests + :param gots: A dict of algorithm names pointing to hashes we + actually got from the files under suspicion + """ + self.allowed = allowed + self.gots = gots + + def body(self): + return ' %s:\n%s' % (self._requirement_name(), + self._hash_comparison()) + + def _hash_comparison(self): + """ + Return a comparison of actual and expected hash values. + + Example:: + + Expected sha256 abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde + or 123451234512345123451234512345123451234512345 + Got bcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdef + + """ + def hash_then_or(hash_name): + # For now, all the decent hashes have 6-char names, so we can get + # away with hard-coding space literals. + return chain([hash_name], repeat(' or')) + + lines = [] + for hash_name, expecteds in iteritems(self.allowed): + prefix = hash_then_or(hash_name) + lines.extend((' Expected %s %s' % (next(prefix), e)) + for e in expecteds) + lines.append(' Got %s\n' % + self.gots[hash_name].hexdigest()) + prefix = ' or' + return '\n'.join(lines) + + +class UnsupportedPythonVersion(InstallationError): + """Unsupported python version according to Requires-Python package + metadata.""" diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/index.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/index.py new file mode 100644 index 0000000..f653f6e --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/index.py @@ -0,0 +1,1102 @@ +"""Routines related to PyPI, indexes""" +from __future__ import absolute_import + +import logging +import cgi +from collections import namedtuple +import itertools +import sys +import os +import re +import mimetypes +import posixpath +import warnings + +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip.compat import ipaddress +from pip.utils import ( + cached_property, splitext, normalize_path, + ARCHIVE_EXTENSIONS, SUPPORTED_EXTENSIONS, +) +from pip.utils.deprecation import RemovedInPip10Warning +from pip.utils.logging import indent_log +from pip.utils.packaging import check_requires_python +from pip.exceptions import ( + DistributionNotFound, BestVersionAlreadyInstalled, InvalidWheelFilename, + UnsupportedWheel, +) +from pip.download import HAS_TLS, is_url, path_to_url, url_to_path +from pip.wheel import Wheel, wheel_ext +from pip.pep425tags import get_supported +from pip._vendor import html5lib, requests, six +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging import specifiers +from pip._vendor.requests.exceptions import SSLError +from pip._vendor.distlib.compat import unescape + + +__all__ = ['FormatControl', 'fmt_ctl_handle_mutual_exclude', 'PackageFinder'] + + +SECURE_ORIGINS = [ + # protocol, hostname, port + # Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC) + ("https", "*", "*"), + ("*", "localhost", "*"), + ("*", "127.0.0.0/8", "*"), + ("*", "::1/128", "*"), + ("file", "*", None), + # ssh is always secure. + ("ssh", "*", "*"), +] + + +logger = logging.getLogger(__name__) + + +class InstallationCandidate(object): + + def __init__(self, project, version, location): + self.project = project + self.version = parse_version(version) + self.location = location + self._key = (self.project, self.version, self.location) + + def __repr__(self): + return "<InstallationCandidate({0!r}, {1!r}, {2!r})>".format( + self.project, self.version, self.location, + ) + + def __hash__(self): + return hash(self._key) + + def __lt__(self, other): + return self._compare(other, lambda s, o: s < o) + + def __le__(self, other): + return self._compare(other, lambda s, o: s <= o) + + def __eq__(self, other): + return self._compare(other, lambda s, o: s == o) + + def __ge__(self, other): + return self._compare(other, lambda s, o: s >= o) + + def __gt__(self, other): + return self._compare(other, lambda s, o: s > o) + + def __ne__(self, other): + return self._compare(other, lambda s, o: s != o) + + def _compare(self, other, method): + if not isinstance(other, InstallationCandidate): + return NotImplemented + + return method(self._key, other._key) + + +class PackageFinder(object): + """This finds packages. + + This is meant to match easy_install's technique for looking for + packages, by reading pages and looking for appropriate links. + """ + + def __init__(self, find_links, index_urls, allow_all_prereleases=False, + trusted_hosts=None, process_dependency_links=False, + session=None, format_control=None, platform=None, + versions=None, abi=None, implementation=None): + """Create a PackageFinder. + + :param format_control: A FormatControl object or None. Used to control + the selection of source packages / binary packages when consulting + the index and links. + :param platform: A string or None. If None, searches for packages + that are supported by the current system. Otherwise, will find + packages that can be built on the platform passed in. These + packages will only be downloaded for distribution: they will + not be built locally. + :param versions: A list of strings or None. This is passed directly + to pep425tags.py in the get_supported() method. + :param abi: A string or None. This is passed directly + to pep425tags.py in the get_supported() method. + :param implementation: A string or None. This is passed directly + to pep425tags.py in the get_supported() method. + """ + if session is None: + raise TypeError( + "PackageFinder() missing 1 required keyword argument: " + "'session'" + ) + + # Build find_links. If an argument starts with ~, it may be + # a local file relative to a home directory. So try normalizing + # it and if it exists, use the normalized version. + # This is deliberately conservative - it might be fine just to + # blindly normalize anything starting with a ~... + self.find_links = [] + for link in find_links: + if link.startswith('~'): + new_link = normalize_path(link) + if os.path.exists(new_link): + link = new_link + self.find_links.append(link) + + self.index_urls = index_urls + self.dependency_links = [] + + # These are boring links that have already been logged somehow: + self.logged_links = set() + + self.format_control = format_control or FormatControl(set(), set()) + + # Domains that we won't emit warnings for when not using HTTPS + self.secure_origins = [ + ("*", host, "*") + for host in (trusted_hosts if trusted_hosts else []) + ] + + # Do we want to allow _all_ pre-releases? + self.allow_all_prereleases = allow_all_prereleases + + # Do we process dependency links? + self.process_dependency_links = process_dependency_links + + # The Session we'll use to make requests + self.session = session + + # The valid tags to check potential found wheel candidates against + self.valid_tags = get_supported( + versions=versions, + platform=platform, + abi=abi, + impl=implementation, + ) + + # If we don't have TLS enabled, then WARN if anyplace we're looking + # relies on TLS. + if not HAS_TLS: + for link in itertools.chain(self.index_urls, self.find_links): + parsed = urllib_parse.urlparse(link) + if parsed.scheme == "https": + logger.warning( + "pip is configured with locations that require " + "TLS/SSL, however the ssl module in Python is not " + "available." + ) + break + + def add_dependency_links(self, links): + # # FIXME: this shouldn't be global list this, it should only + # # apply to requirements of the package that specifies the + # # dependency_links value + # # FIXME: also, we should track comes_from (i.e., use Link) + if self.process_dependency_links: + warnings.warn( + "Dependency Links processing has been deprecated and will be " + "removed in a future release.", + RemovedInPip10Warning, + ) + self.dependency_links.extend(links) + + @staticmethod + def _sort_locations(locations, expand_dir=False): + """ + Sort locations into "files" (archives) and "urls", and return + a pair of lists (files,urls) + """ + files = [] + urls = [] + + # puts the url for the given file path into the appropriate list + def sort_path(path): + url = path_to_url(path) + if mimetypes.guess_type(url, strict=False)[0] == 'text/html': + urls.append(url) + else: + files.append(url) + + for url in locations: + + is_local_path = os.path.exists(url) + is_file_url = url.startswith('file:') + + if is_local_path or is_file_url: + if is_local_path: + path = url + else: + path = url_to_path(url) + if os.path.isdir(path): + if expand_dir: + path = os.path.realpath(path) + for item in os.listdir(path): + sort_path(os.path.join(path, item)) + elif is_file_url: + urls.append(url) + elif os.path.isfile(path): + sort_path(path) + else: + logger.warning( + "Url '%s' is ignored: it is neither a file " + "nor a directory.", url) + elif is_url(url): + # Only add url with clear scheme + urls.append(url) + else: + logger.warning( + "Url '%s' is ignored. It is either a non-existing " + "path or lacks a specific scheme.", url) + + return files, urls + + def _candidate_sort_key(self, candidate): + """ + Function used to generate link sort key for link tuples. + The greater the return value, the more preferred it is. + If not finding wheels, then sorted by version only. + If finding wheels, then the sort order is by version, then: + 1. existing installs + 2. wheels ordered via Wheel.support_index_min(self.valid_tags) + 3. source archives + Note: it was considered to embed this logic into the Link + comparison operators, but then different sdist links + with the same version, would have to be considered equal + """ + support_num = len(self.valid_tags) + if candidate.location.is_wheel: + # can raise InvalidWheelFilename + wheel = Wheel(candidate.location.filename) + if not wheel.supported(self.valid_tags): + raise UnsupportedWheel( + "%s is not a supported wheel for this platform. It " + "can't be sorted." % wheel.filename + ) + pri = -(wheel.support_index_min(self.valid_tags)) + else: # sdist + pri = -(support_num) + return (candidate.version, pri) + + def _validate_secure_origin(self, logger, location): + # Determine if this url used a secure transport mechanism + parsed = urllib_parse.urlparse(str(location)) + origin = (parsed.scheme, parsed.hostname, parsed.port) + + # The protocol to use to see if the protocol matches. + # Don't count the repository type as part of the protocol: in + # cases such as "git+ssh", only use "ssh". (I.e., Only verify against + # the last scheme.) + protocol = origin[0].rsplit('+', 1)[-1] + + # Determine if our origin is a secure origin by looking through our + # hardcoded list of secure origins, as well as any additional ones + # configured on this PackageFinder instance. + for secure_origin in (SECURE_ORIGINS + self.secure_origins): + if protocol != secure_origin[0] and secure_origin[0] != "*": + continue + + try: + # We need to do this decode dance to ensure that we have a + # unicode object, even on Python 2.x. + addr = ipaddress.ip_address( + origin[1] + if ( + isinstance(origin[1], six.text_type) or + origin[1] is None + ) + else origin[1].decode("utf8") + ) + network = ipaddress.ip_network( + secure_origin[1] + if isinstance(secure_origin[1], six.text_type) + else secure_origin[1].decode("utf8") + ) + except ValueError: + # We don't have both a valid address or a valid network, so + # we'll check this origin against hostnames. + if (origin[1] and + origin[1].lower() != secure_origin[1].lower() and + secure_origin[1] != "*"): + continue + else: + # We have a valid address and network, so see if the address + # is contained within the network. + if addr not in network: + continue + + # Check to see if the port patches + if (origin[2] != secure_origin[2] and + secure_origin[2] != "*" and + secure_origin[2] is not None): + continue + + # If we've gotten here, then this origin matches the current + # secure origin and we should return True + return True + + # If we've gotten to this point, then the origin isn't secure and we + # will not accept it as a valid location to search. We will however + # log a warning that we are ignoring it. + logger.warning( + "The repository located at %s is not a trusted or secure host and " + "is being ignored. If this repository is available via HTTPS it " + "is recommended to use HTTPS instead, otherwise you may silence " + "this warning and allow it anyways with '--trusted-host %s'.", + parsed.hostname, + parsed.hostname, + ) + + return False + + def _get_index_urls_locations(self, project_name): + """Returns the locations found via self.index_urls + + Checks the url_name on the main (first in the list) index and + use this url_name to produce all locations + """ + + def mkurl_pypi_url(url): + loc = posixpath.join( + url, + urllib_parse.quote(canonicalize_name(project_name))) + # For maximum compatibility with easy_install, ensure the path + # ends in a trailing slash. Although this isn't in the spec + # (and PyPI can handle it without the slash) some other index + # implementations might break if they relied on easy_install's + # behavior. + if not loc.endswith('/'): + loc = loc + '/' + return loc + + return [mkurl_pypi_url(url) for url in self.index_urls] + + def find_all_candidates(self, project_name): + """Find all available InstallationCandidate for project_name + + This checks index_urls, find_links and dependency_links. + All versions found are returned as an InstallationCandidate list. + + See _link_package_versions for details on which files are accepted + """ + index_locations = self._get_index_urls_locations(project_name) + index_file_loc, index_url_loc = self._sort_locations(index_locations) + fl_file_loc, fl_url_loc = self._sort_locations( + self.find_links, expand_dir=True) + dep_file_loc, dep_url_loc = self._sort_locations(self.dependency_links) + + file_locations = ( + Link(url) for url in itertools.chain( + index_file_loc, fl_file_loc, dep_file_loc) + ) + + # We trust every url that the user has given us whether it was given + # via --index-url or --find-links + # We explicitly do not trust links that came from dependency_links + # We want to filter out any thing which does not have a secure origin. + url_locations = [ + link for link in itertools.chain( + (Link(url) for url in index_url_loc), + (Link(url) for url in fl_url_loc), + (Link(url) for url in dep_url_loc), + ) + if self._validate_secure_origin(logger, link) + ] + + logger.debug('%d location(s) to search for versions of %s:', + len(url_locations), project_name) + + for location in url_locations: + logger.debug('* %s', location) + + canonical_name = canonicalize_name(project_name) + formats = fmt_ctl_formats(self.format_control, canonical_name) + search = Search(project_name, canonical_name, formats) + find_links_versions = self._package_versions( + # We trust every directly linked archive in find_links + (Link(url, '-f') for url in self.find_links), + search + ) + + page_versions = [] + for page in self._get_pages(url_locations, project_name): + logger.debug('Analyzing links from page %s', page.url) + with indent_log(): + page_versions.extend( + self._package_versions(page.links, search) + ) + + dependency_versions = self._package_versions( + (Link(url) for url in self.dependency_links), search + ) + if dependency_versions: + logger.debug( + 'dependency_links found: %s', + ', '.join([ + version.location.url for version in dependency_versions + ]) + ) + + file_versions = self._package_versions(file_locations, search) + if file_versions: + file_versions.sort(reverse=True) + logger.debug( + 'Local files found: %s', + ', '.join([ + url_to_path(candidate.location.url) + for candidate in file_versions + ]) + ) + + # This is an intentional priority ordering + return ( + file_versions + find_links_versions + page_versions + + dependency_versions + ) + + def find_requirement(self, req, upgrade): + """Try to find a Link matching req + + Expects req, an InstallRequirement and upgrade, a boolean + Returns a Link if found, + Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise + """ + all_candidates = self.find_all_candidates(req.name) + + # Filter out anything which doesn't match our specifier + compatible_versions = set( + req.specifier.filter( + # We turn the version object into a str here because otherwise + # when we're debundled but setuptools isn't, Python will see + # packaging.version.Version and + # pkg_resources._vendor.packaging.version.Version as different + # types. This way we'll use a str as a common data interchange + # format. If we stop using the pkg_resources provided specifier + # and start using our own, we can drop the cast to str(). + [str(c.version) for c in all_candidates], + prereleases=( + self.allow_all_prereleases + if self.allow_all_prereleases else None + ), + ) + ) + applicable_candidates = [ + # Again, converting to str to deal with debundling. + c for c in all_candidates if str(c.version) in compatible_versions + ] + + if applicable_candidates: + best_candidate = max(applicable_candidates, + key=self._candidate_sort_key) + else: + best_candidate = None + + if req.satisfied_by is not None: + installed_version = parse_version(req.satisfied_by.version) + else: + installed_version = None + + if installed_version is None and best_candidate is None: + logger.critical( + 'Could not find a version that satisfies the requirement %s ' + '(from versions: %s)', + req, + ', '.join( + sorted( + set(str(c.version) for c in all_candidates), + key=parse_version, + ) + ) + ) + + raise DistributionNotFound( + 'No matching distribution found for %s' % req + ) + + best_installed = False + if installed_version and ( + best_candidate is None or + best_candidate.version <= installed_version): + best_installed = True + + if not upgrade and installed_version is not None: + if best_installed: + logger.debug( + 'Existing installed version (%s) is most up-to-date and ' + 'satisfies requirement', + installed_version, + ) + else: + logger.debug( + 'Existing installed version (%s) satisfies requirement ' + '(most up-to-date version is %s)', + installed_version, + best_candidate.version, + ) + return None + + if best_installed: + # We have an existing version, and its the best version + logger.debug( + 'Installed version (%s) is most up-to-date (past versions: ' + '%s)', + installed_version, + ', '.join(sorted(compatible_versions, key=parse_version)) or + "none", + ) + raise BestVersionAlreadyInstalled + + logger.debug( + 'Using version %s (newest of versions: %s)', + best_candidate.version, + ', '.join(sorted(compatible_versions, key=parse_version)) + ) + return best_candidate.location + + def _get_pages(self, locations, project_name): + """ + Yields (page, page_url) from the given locations, skipping + locations that have errors. + """ + seen = set() + for location in locations: + if location in seen: + continue + seen.add(location) + + page = self._get_page(location) + if page is None: + continue + + yield page + + _py_version_re = re.compile(r'-py([123]\.?[0-9]?)$') + + def _sort_links(self, links): + """ + Returns elements of links in order, non-egg links first, egg links + second, while eliminating duplicates + """ + eggs, no_eggs = [], [] + seen = set() + for link in links: + if link not in seen: + seen.add(link) + if link.egg_fragment: + eggs.append(link) + else: + no_eggs.append(link) + return no_eggs + eggs + + def _package_versions(self, links, search): + result = [] + for link in self._sort_links(links): + v = self._link_package_versions(link, search) + if v is not None: + result.append(v) + return result + + def _log_skipped_link(self, link, reason): + if link not in self.logged_links: + logger.debug('Skipping link %s; %s', link, reason) + self.logged_links.add(link) + + def _link_package_versions(self, link, search): + """Return an InstallationCandidate or None""" + version = None + if link.egg_fragment: + egg_info = link.egg_fragment + ext = link.ext + else: + egg_info, ext = link.splitext() + if not ext: + self._log_skipped_link(link, 'not a file') + return + if ext not in SUPPORTED_EXTENSIONS: + self._log_skipped_link( + link, 'unsupported archive format: %s' % ext) + return + if "binary" not in search.formats and ext == wheel_ext: + self._log_skipped_link( + link, 'No binaries permitted for %s' % search.supplied) + return + if "macosx10" in link.path and ext == '.zip': + self._log_skipped_link(link, 'macosx10 one') + return + if ext == wheel_ext: + try: + wheel = Wheel(link.filename) + except InvalidWheelFilename: + self._log_skipped_link(link, 'invalid wheel filename') + return + if canonicalize_name(wheel.name) != search.canonical: + self._log_skipped_link( + link, 'wrong project name (not %s)' % search.supplied) + return + + if not wheel.supported(self.valid_tags): + self._log_skipped_link( + link, 'it is not compatible with this Python') + return + + version = wheel.version + + # This should be up by the search.ok_binary check, but see issue 2700. + if "source" not in search.formats and ext != wheel_ext: + self._log_skipped_link( + link, 'No sources permitted for %s' % search.supplied) + return + + if not version: + version = egg_info_matches(egg_info, search.supplied, link) + if version is None: + self._log_skipped_link( + link, 'wrong project name (not %s)' % search.supplied) + return + + match = self._py_version_re.search(version) + if match: + version = version[:match.start()] + py_version = match.group(1) + if py_version != sys.version[:3]: + self._log_skipped_link( + link, 'Python version is incorrect') + return + try: + support_this_python = check_requires_python(link.requires_python) + except specifiers.InvalidSpecifier: + logger.debug("Package %s has an invalid Requires-Python entry: %s", + link.filename, link.requires_python) + support_this_python = True + + if not support_this_python: + logger.debug("The package %s is incompatible with the python" + "version in use. Acceptable python versions are:%s", + link, link.requires_python) + return + logger.debug('Found link %s, version: %s', link, version) + + return InstallationCandidate(search.supplied, version, link) + + def _get_page(self, link): + return HTMLPage.get_page(link, session=self.session) + + +def egg_info_matches( + egg_info, search_name, link, + _egg_info_re=re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.I)): + """Pull the version part out of a string. + + :param egg_info: The string to parse. E.g. foo-2.1 + :param search_name: The name of the package this belongs to. None to + infer the name. Note that this cannot unambiguously parse strings + like foo-2-2 which might be foo, 2-2 or foo-2, 2. + :param link: The link the string came from, for logging on failure. + """ + match = _egg_info_re.search(egg_info) + if not match: + logger.debug('Could not parse version from link: %s', link) + return None + if search_name is None: + full_match = match.group(0) + return full_match[full_match.index('-'):] + name = match.group(0).lower() + # To match the "safe" name that pkg_resources creates: + name = name.replace('_', '-') + # project name and version must be separated by a dash + look_for = search_name.lower() + "-" + if name.startswith(look_for): + return match.group(0)[len(look_for):] + else: + return None + + +class HTMLPage(object): + """Represents one page, along with its URL""" + + def __init__(self, content, url, headers=None): + # Determine if we have any encoding information in our headers + encoding = None + if headers and "Content-Type" in headers: + content_type, params = cgi.parse_header(headers["Content-Type"]) + + if "charset" in params: + encoding = params['charset'] + + self.content = content + self.parsed = html5lib.parse( + self.content, + transport_encoding=encoding, + namespaceHTMLElements=False, + ) + self.url = url + self.headers = headers + + def __str__(self): + return self.url + + @classmethod + def get_page(cls, link, skip_archives=True, session=None): + if session is None: + raise TypeError( + "get_page() missing 1 required keyword argument: 'session'" + ) + + url = link.url + url = url.split('#', 1)[0] + + # Check for VCS schemes that do not support lookup as web pages. + from pip.vcs import VcsSupport + for scheme in VcsSupport.schemes: + if url.lower().startswith(scheme) and url[len(scheme)] in '+:': + logger.debug('Cannot look at %s URL %s', scheme, link) + return None + + try: + if skip_archives: + filename = link.filename + for bad_ext in ARCHIVE_EXTENSIONS: + if filename.endswith(bad_ext): + content_type = cls._get_content_type( + url, session=session, + ) + if content_type.lower().startswith('text/html'): + break + else: + logger.debug( + 'Skipping page %s because of Content-Type: %s', + link, + content_type, + ) + return + + logger.debug('Getting page %s', url) + + # Tack index.html onto file:// URLs that point to directories + (scheme, netloc, path, params, query, fragment) = \ + urllib_parse.urlparse(url) + if (scheme == 'file' and + os.path.isdir(urllib_request.url2pathname(path))): + # add trailing slash if not present so urljoin doesn't trim + # final segment + if not url.endswith('/'): + url += '/' + url = urllib_parse.urljoin(url, 'index.html') + logger.debug(' file: URL is directory, getting %s', url) + + resp = session.get( + url, + headers={ + "Accept": "text/html", + "Cache-Control": "max-age=600", + }, + ) + resp.raise_for_status() + + # The check for archives above only works if the url ends with + # something that looks like an archive. However that is not a + # requirement of an url. Unless we issue a HEAD request on every + # url we cannot know ahead of time for sure if something is HTML + # or not. However we can check after we've downloaded it. + content_type = resp.headers.get('Content-Type', 'unknown') + if not content_type.lower().startswith("text/html"): + logger.debug( + 'Skipping page %s because of Content-Type: %s', + link, + content_type, + ) + return + + inst = cls(resp.content, resp.url, resp.headers) + except requests.HTTPError as exc: + cls._handle_fail(link, exc, url) + except SSLError as exc: + reason = ("There was a problem confirming the ssl certificate: " + "%s" % exc) + cls._handle_fail(link, reason, url, meth=logger.info) + except requests.ConnectionError as exc: + cls._handle_fail(link, "connection error: %s" % exc, url) + except requests.Timeout: + cls._handle_fail(link, "timed out", url) + else: + return inst + + @staticmethod + def _handle_fail(link, reason, url, meth=None): + if meth is None: + meth = logger.debug + + meth("Could not fetch URL %s: %s - skipping", link, reason) + + @staticmethod + def _get_content_type(url, session): + """Get the Content-Type of the given url, using a HEAD request""" + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(url) + if scheme not in ('http', 'https'): + # FIXME: some warning or something? + # assertion error? + return '' + + resp = session.head(url, allow_redirects=True) + resp.raise_for_status() + + return resp.headers.get("Content-Type", "") + + @cached_property + def base_url(self): + bases = [ + x for x in self.parsed.findall(".//base") + if x.get("href") is not None + ] + if bases and bases[0].get("href"): + return bases[0].get("href") + else: + return self.url + + @property + def links(self): + """Yields all links in the page""" + for anchor in self.parsed.findall(".//a"): + if anchor.get("href"): + href = anchor.get("href") + url = self.clean_link( + urllib_parse.urljoin(self.base_url, href) + ) + pyrequire = anchor.get('data-requires-python') + pyrequire = unescape(pyrequire) if pyrequire else None + yield Link(url, self, requires_python=pyrequire) + + _clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) + + def clean_link(self, url): + """Makes sure a link is fully encoded. That is, if a ' ' shows up in + the link, it will be rewritten to %20 (while not over-quoting + % or other characters).""" + return self._clean_re.sub( + lambda match: '%%%2x' % ord(match.group(0)), url) + + +class Link(object): + + def __init__(self, url, comes_from=None, requires_python=None): + """ + Object representing a parsed link from https://pypi.python.org/simple/* + + url: + url of the resource pointed to (href of the link) + comes_from: + instance of HTMLPage where the link was found, or string. + requires_python: + String containing the `Requires-Python` metadata field, specified + in PEP 345. This may be specified by a data-requires-python + attribute in the HTML link tag, as described in PEP 503. + """ + + # url can be a UNC windows share + if url.startswith('\\\\'): + url = path_to_url(url) + + self.url = url + self.comes_from = comes_from + self.requires_python = requires_python if requires_python else None + + def __str__(self): + if self.requires_python: + rp = ' (requires-python:%s)' % self.requires_python + else: + rp = '' + if self.comes_from: + return '%s (from %s)%s' % (self.url, self.comes_from, rp) + else: + return str(self.url) + + def __repr__(self): + return '<Link %s>' % self + + def __eq__(self, other): + if not isinstance(other, Link): + return NotImplemented + return self.url == other.url + + def __ne__(self, other): + if not isinstance(other, Link): + return NotImplemented + return self.url != other.url + + def __lt__(self, other): + if not isinstance(other, Link): + return NotImplemented + return self.url < other.url + + def __le__(self, other): + if not isinstance(other, Link): + return NotImplemented + return self.url <= other.url + + def __gt__(self, other): + if not isinstance(other, Link): + return NotImplemented + return self.url > other.url + + def __ge__(self, other): + if not isinstance(other, Link): + return NotImplemented + return self.url >= other.url + + def __hash__(self): + return hash(self.url) + + @property + def filename(self): + _, netloc, path, _, _ = urllib_parse.urlsplit(self.url) + name = posixpath.basename(path.rstrip('/')) or netloc + name = urllib_parse.unquote(name) + assert name, ('URL %r produced no filename' % self.url) + return name + + @property + def scheme(self): + return urllib_parse.urlsplit(self.url)[0] + + @property + def netloc(self): + return urllib_parse.urlsplit(self.url)[1] + + @property + def path(self): + return urllib_parse.unquote(urllib_parse.urlsplit(self.url)[2]) + + def splitext(self): + return splitext(posixpath.basename(self.path.rstrip('/'))) + + @property + def ext(self): + return self.splitext()[1] + + @property + def url_without_fragment(self): + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(self.url) + return urllib_parse.urlunsplit((scheme, netloc, path, query, None)) + + _egg_fragment_re = re.compile(r'[#&]egg=([^&]*)') + + @property + def egg_fragment(self): + match = self._egg_fragment_re.search(self.url) + if not match: + return None + return match.group(1) + + _subdirectory_fragment_re = re.compile(r'[#&]subdirectory=([^&]*)') + + @property + def subdirectory_fragment(self): + match = self._subdirectory_fragment_re.search(self.url) + if not match: + return None + return match.group(1) + + _hash_re = re.compile( + r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)' + ) + + @property + def hash(self): + match = self._hash_re.search(self.url) + if match: + return match.group(2) + return None + + @property + def hash_name(self): + match = self._hash_re.search(self.url) + if match: + return match.group(1) + return None + + @property + def show_url(self): + return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0]) + + @property + def is_wheel(self): + return self.ext == wheel_ext + + @property + def is_artifact(self): + """ + Determines if this points to an actual artifact (e.g. a tarball) or if + it points to an "abstract" thing like a path or a VCS location. + """ + from pip.vcs import vcs + + if self.scheme in vcs.all_schemes: + return False + + return True + + +FormatControl = namedtuple('FormatControl', 'no_binary only_binary') +"""This object has two fields, no_binary and only_binary. + +If a field is falsy, it isn't set. If it is {':all:'}, it should match all +packages except those listed in the other field. Only one field can be set +to {':all:'} at a time. The rest of the time exact package name matches +are listed, with any given package only showing up in one field at a time. +""" + + +def fmt_ctl_handle_mutual_exclude(value, target, other): + new = value.split(',') + while ':all:' in new: + other.clear() + target.clear() + target.add(':all:') + del new[:new.index(':all:') + 1] + if ':none:' not in new: + # Without a none, we want to discard everything as :all: covers it + return + for name in new: + if name == ':none:': + target.clear() + continue + name = canonicalize_name(name) + other.discard(name) + target.add(name) + + +def fmt_ctl_formats(fmt_ctl, canonical_name): + result = set(["binary", "source"]) + if canonical_name in fmt_ctl.only_binary: + result.discard('source') + elif canonical_name in fmt_ctl.no_binary: + result.discard('binary') + elif ':all:' in fmt_ctl.only_binary: + result.discard('source') + elif ':all:' in fmt_ctl.no_binary: + result.discard('binary') + return frozenset(result) + + +def fmt_ctl_no_binary(fmt_ctl): + fmt_ctl_handle_mutual_exclude( + ':all:', fmt_ctl.no_binary, fmt_ctl.only_binary) + + +def fmt_ctl_no_use_wheel(fmt_ctl): + fmt_ctl_no_binary(fmt_ctl) + warnings.warn( + '--no-use-wheel is deprecated and will be removed in the future. ' + ' Please use --no-binary :all: instead.', RemovedInPip10Warning, + stacklevel=2) + + +Search = namedtuple('Search', 'supplied canonical formats') +"""Capture key aspects of a search. + +:attribute supplied: The user supplied package. +:attribute canonical: The canonical package name. +:attribute formats: The formats allowed for this package. Should be a set + with 'binary' or 'source' or both in it. +""" diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/locations.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/locations.py new file mode 100644 index 0000000..e598ef1 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/locations.py @@ -0,0 +1,182 @@ +"""Locations where we look for configs, install stuff, etc""" +from __future__ import absolute_import + +import os +import os.path +import site +import sys + +from distutils import sysconfig +from distutils.command.install import install, SCHEME_KEYS # noqa + +from pip.compat import WINDOWS, expanduser +from pip.utils import appdirs + + +# Application Directories +USER_CACHE_DIR = appdirs.user_cache_dir("pip") + + +DELETE_MARKER_MESSAGE = '''\ +This file is placed here by pip to indicate the source was put +here by pip. + +Once this package is successfully installed this source code will be +deleted (unless you remove this file). +''' +PIP_DELETE_MARKER_FILENAME = 'pip-delete-this-directory.txt' + + +def write_delete_marker_file(directory): + """ + Write the pip delete marker file into this directory. + """ + filepath = os.path.join(directory, PIP_DELETE_MARKER_FILENAME) + with open(filepath, 'w') as marker_fp: + marker_fp.write(DELETE_MARKER_MESSAGE) + + +def running_under_virtualenv(): + """ + Return True if we're running inside a virtualenv, False otherwise. + + """ + if hasattr(sys, 'real_prefix'): + return True + elif sys.prefix != getattr(sys, "base_prefix", sys.prefix): + return True + + return False + + +def virtualenv_no_global(): + """ + Return True if in a venv and no system site packages. + """ + # this mirrors the logic in virtualenv.py for locating the + # no-global-site-packages.txt file + site_mod_dir = os.path.dirname(os.path.abspath(site.__file__)) + no_global_file = os.path.join(site_mod_dir, 'no-global-site-packages.txt') + if running_under_virtualenv() and os.path.isfile(no_global_file): + return True + + +if running_under_virtualenv(): + src_prefix = os.path.join(sys.prefix, 'src') +else: + # FIXME: keep src in cwd for now (it is not a temporary folder) + try: + src_prefix = os.path.join(os.getcwd(), 'src') + except OSError: + # In case the current working directory has been renamed or deleted + sys.exit( + "The folder you are executing pip from can no longer be found." + ) + +# under macOS + virtualenv sys.prefix is not properly resolved +# it is something like /path/to/python/bin/.. +# Note: using realpath due to tmp dirs on OSX being symlinks +src_prefix = os.path.abspath(src_prefix) + +# FIXME doesn't account for venv linked to global site-packages + +site_packages = sysconfig.get_python_lib() +user_site = site.USER_SITE +user_dir = expanduser('~') +if WINDOWS: + bin_py = os.path.join(sys.prefix, 'Scripts') + bin_user = os.path.join(user_site, 'Scripts') + # buildout uses 'bin' on Windows too? + if not os.path.exists(bin_py): + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') + + config_basename = 'pip.ini' + + legacy_storage_dir = os.path.join(user_dir, 'pip') + legacy_config_file = os.path.join( + legacy_storage_dir, + config_basename, + ) +else: + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') + + config_basename = 'pip.conf' + + legacy_storage_dir = os.path.join(user_dir, '.pip') + legacy_config_file = os.path.join( + legacy_storage_dir, + config_basename, + ) + + # Forcing to use /usr/local/bin for standard macOS framework installs + # Also log to ~/Library/Logs/ for use with the Console.app log viewer + if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': + bin_py = '/usr/local/bin' + +site_config_files = [ + os.path.join(path, config_basename) + for path in appdirs.site_config_dirs('pip') +] + + +def distutils_scheme(dist_name, user=False, home=None, root=None, + isolated=False, prefix=None): + """ + Return a distutils install scheme + """ + from distutils.dist import Distribution + + scheme = {} + + if isolated: + extra_dist_args = {"script_args": ["--no-user-cfg"]} + else: + extra_dist_args = {} + dist_args = {'name': dist_name} + dist_args.update(extra_dist_args) + + d = Distribution(dist_args) + d.parse_config_files() + i = d.get_command_obj('install', create=True) + # NOTE: setting user or home has the side-effect of creating the home dir + # or user base for installations during finalize_options() + # ideally, we'd prefer a scheme class that has no side-effects. + assert not (user and prefix), "user={0} prefix={1}".format(user, prefix) + i.user = user or i.user + if user: + i.prefix = "" + i.prefix = prefix or i.prefix + i.home = home or i.home + i.root = root or i.root + i.finalize_options() + for key in SCHEME_KEYS: + scheme[key] = getattr(i, 'install_' + key) + + # install_lib specified in setup.cfg should install *everything* + # into there (i.e. it takes precedence over both purelib and + # platlib). Note, i.install_lib is *always* set after + # finalize_options(); we only want to override here if the user + # has explicitly requested it hence going back to the config + if 'install_lib' in d.get_option_dict('install'): + scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib)) + + if running_under_virtualenv(): + scheme['headers'] = os.path.join( + sys.prefix, + 'include', + 'site', + 'python' + sys.version[:3], + dist_name, + ) + + if root is not None: + path_no_drive = os.path.splitdrive( + os.path.abspath(scheme["headers"]))[1] + scheme["headers"] = os.path.join( + root, + path_no_drive[1:], + ) + + return scheme diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/models/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/models/__init__.py new file mode 100644 index 0000000..1d727d7 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/models/__init__.py @@ -0,0 +1,4 @@ +from pip.models.index import Index, PyPI + + +__all__ = ["Index", "PyPI"] diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/models/index.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/models/index.py new file mode 100644 index 0000000..be99119 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/models/index.py @@ -0,0 +1,16 @@ +from pip._vendor.six.moves.urllib import parse as urllib_parse + + +class Index(object): + def __init__(self, url): + self.url = url + self.netloc = urllib_parse.urlsplit(url).netloc + self.simple_url = self.url_to_path('simple') + self.pypi_url = self.url_to_path('pypi') + self.pip_json_url = self.url_to_path('pypi/pip/json') + + def url_to_path(self, path): + return urllib_parse.urljoin(self.url, path) + + +PyPI = Index('https://pypi.python.org/') diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/operations/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/operations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/operations/check.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/operations/check.py new file mode 100644 index 0000000..2cf67aa --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/operations/check.py @@ -0,0 +1,49 @@ + + +def check_requirements(installed_dists): + missing_reqs_dict = {} + incompatible_reqs_dict = {} + + for dist in installed_dists: + key = '%s==%s' % (dist.project_name, dist.version) + + missing_reqs = list(get_missing_reqs(dist, installed_dists)) + if missing_reqs: + missing_reqs_dict[key] = missing_reqs + + incompatible_reqs = list(get_incompatible_reqs( + dist, installed_dists)) + if incompatible_reqs: + incompatible_reqs_dict[key] = incompatible_reqs + + return (missing_reqs_dict, incompatible_reqs_dict) + + +def get_missing_reqs(dist, installed_dists): + """Return all of the requirements of `dist` that aren't present in + `installed_dists`. + + """ + installed_names = set(d.project_name.lower() for d in installed_dists) + missing_requirements = set() + + for requirement in dist.requires(): + if requirement.project_name.lower() not in installed_names: + missing_requirements.add(requirement) + yield requirement + + +def get_incompatible_reqs(dist, installed_dists): + """Return all of the requirements of `dist` that are present in + `installed_dists`, but have incompatible versions. + + """ + installed_dists_by_name = {} + for installed_dist in installed_dists: + installed_dists_by_name[installed_dist.project_name] = installed_dist + + for requirement in dist.requires(): + present_dist = installed_dists_by_name.get(requirement.project_name) + + if present_dist and present_dist not in requirement: + yield (requirement, present_dist) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/operations/freeze.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/operations/freeze.py new file mode 100644 index 0000000..920c2c1 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/operations/freeze.py @@ -0,0 +1,132 @@ +from __future__ import absolute_import + +import logging +import re + +import pip +from pip.req import InstallRequirement +from pip.req.req_file import COMMENT_RE +from pip.utils import get_installed_distributions +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError + + +logger = logging.getLogger(__name__) + + +def freeze( + requirement=None, + find_links=None, local_only=None, user_only=None, skip_regex=None, + default_vcs=None, + isolated=False, + wheel_cache=None, + skip=()): + find_links = find_links or [] + skip_match = None + + if skip_regex: + skip_match = re.compile(skip_regex).search + + dependency_links = [] + + for dist in pkg_resources.working_set: + if dist.has_metadata('dependency_links.txt'): + dependency_links.extend( + dist.get_metadata_lines('dependency_links.txt') + ) + for link in find_links: + if '#egg=' in link: + dependency_links.append(link) + for link in find_links: + yield '-f %s' % link + installations = {} + for dist in get_installed_distributions(local_only=local_only, + skip=(), + user_only=user_only): + try: + req = pip.FrozenRequirement.from_dist( + dist, + dependency_links + ) + except RequirementParseError: + logger.warning( + "Could not parse requirement: %s", + dist.project_name + ) + continue + installations[req.name] = req + + if requirement: + # the options that don't get turned into an InstallRequirement + # should only be emitted once, even if the same option is in multiple + # requirements files, so we need to keep track of what has been emitted + # so that we don't emit it again if it's seen again + emitted_options = set() + for req_file_path in requirement: + with open(req_file_path) as req_file: + for line in req_file: + if (not line.strip() or + line.strip().startswith('#') or + (skip_match and skip_match(line)) or + line.startswith(( + '-r', '--requirement', + '-Z', '--always-unzip', + '-f', '--find-links', + '-i', '--index-url', + '--pre', + '--trusted-host', + '--process-dependency-links', + '--extra-index-url'))): + line = line.rstrip() + if line not in emitted_options: + emitted_options.add(line) + yield line + continue + + if line.startswith('-e') or line.startswith('--editable'): + if line.startswith('-e'): + line = line[2:].strip() + else: + line = line[len('--editable'):].strip().lstrip('=') + line_req = InstallRequirement.from_editable( + line, + default_vcs=default_vcs, + isolated=isolated, + wheel_cache=wheel_cache, + ) + else: + line_req = InstallRequirement.from_line( + COMMENT_RE.sub('', line).strip(), + isolated=isolated, + wheel_cache=wheel_cache, + ) + + if not line_req.name: + logger.info( + "Skipping line in requirement file [%s] because " + "it's not clear what it would install: %s", + req_file_path, line.strip(), + ) + logger.info( + " (add #egg=PackageName to the URL to avoid" + " this warning)" + ) + elif line_req.name not in installations: + logger.warning( + "Requirement file [%s] contains %s, but that " + "package is not installed", + req_file_path, COMMENT_RE.sub('', line).strip(), + ) + else: + yield str(installations[line_req.name]).rstrip() + del installations[line_req.name] + + yield( + '## The following requirements were added by ' + 'pip freeze:' + ) + for installation in sorted( + installations.values(), key=lambda x: x.name.lower()): + if canonicalize_name(installation.name) not in skip: + yield str(installation).rstrip() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/pep425tags.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/pep425tags.py new file mode 100644 index 0000000..ad202ef --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/pep425tags.py @@ -0,0 +1,324 @@ +"""Generate and work with PEP 425 Compatibility Tags.""" +from __future__ import absolute_import + +import re +import sys +import warnings +import platform +import logging + +try: + import sysconfig +except ImportError: # pragma nocover + # Python < 2.7 + import distutils.sysconfig as sysconfig +import distutils.util + +from pip.compat import OrderedDict +import pip.utils.glibc + +logger = logging.getLogger(__name__) + +_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') + + +def get_config_var(var): + try: + return sysconfig.get_config_var(var) + except IOError as e: # Issue #1074 + warnings.warn("{0}".format(e), RuntimeWarning) + return None + + +def get_abbr_impl(): + """Return abbreviated implementation name.""" + if hasattr(sys, 'pypy_version_info'): + pyimpl = 'pp' + elif sys.platform.startswith('java'): + pyimpl = 'jy' + elif sys.platform == 'cli': + pyimpl = 'ip' + else: + pyimpl = 'cp' + return pyimpl + + +def get_impl_ver(): + """Return implementation version.""" + impl_ver = get_config_var("py_version_nodot") + if not impl_ver or get_abbr_impl() == 'pp': + impl_ver = ''.join(map(str, get_impl_version_info())) + return impl_ver + + +def get_impl_version_info(): + """Return sys.version_info-like tuple for use in decrementing the minor + version.""" + if get_abbr_impl() == 'pp': + # as per https://github.com/pypa/pip/issues/2882 + return (sys.version_info[0], sys.pypy_version_info.major, + sys.pypy_version_info.minor) + else: + return sys.version_info[0], sys.version_info[1] + + +def get_impl_tag(): + """ + Returns the Tag for this specific implementation. + """ + return "{0}{1}".format(get_abbr_impl(), get_impl_ver()) + + +def get_flag(var, fallback, expected=True, warn=True): + """Use a fallback method for determining SOABI flags if the needed config + var is unset or unavailable.""" + val = get_config_var(var) + if val is None: + if warn: + logger.debug("Config variable '%s' is unset, Python ABI tag may " + "be incorrect", var) + return fallback() + return val == expected + + +def get_abi_tag(): + """Return the ABI tag based on SOABI (if available) or emulate SOABI + (CPython 2, PyPy).""" + soabi = get_config_var('SOABI') + impl = get_abbr_impl() + if not soabi and impl in ('cp', 'pp') and hasattr(sys, 'maxunicode'): + d = '' + m = '' + u = '' + if get_flag('Py_DEBUG', + lambda: hasattr(sys, 'gettotalrefcount'), + warn=(impl == 'cp')): + d = 'd' + if get_flag('WITH_PYMALLOC', + lambda: impl == 'cp', + warn=(impl == 'cp')): + m = 'm' + if get_flag('Py_UNICODE_SIZE', + lambda: sys.maxunicode == 0x10ffff, + expected=4, + warn=(impl == 'cp' and + sys.version_info < (3, 3))) \ + and sys.version_info < (3, 3): + u = 'u' + abi = '%s%s%s%s%s' % (impl, get_impl_ver(), d, m, u) + elif soabi and soabi.startswith('cpython-'): + abi = 'cp' + soabi.split('-')[1] + elif soabi: + abi = soabi.replace('.', '_').replace('-', '_') + else: + abi = None + return abi + + +def _is_running_32bit(): + return sys.maxsize == 2147483647 + + +def get_platform(): + """Return our platform name 'win32', 'linux_x86_64'""" + if sys.platform == 'darwin': + # distutils.util.get_platform() returns the release based on the value + # of MACOSX_DEPLOYMENT_TARGET on which Python was built, which may + # be significantly older than the user's current machine. + release, _, machine = platform.mac_ver() + split_ver = release.split('.') + + if machine == "x86_64" and _is_running_32bit(): + machine = "i386" + elif machine == "ppc64" and _is_running_32bit(): + machine = "ppc" + + return 'macosx_{0}_{1}_{2}'.format(split_ver[0], split_ver[1], machine) + + # XXX remove distutils dependency + result = distutils.util.get_platform().replace('.', '_').replace('-', '_') + if result == "linux_x86_64" and _is_running_32bit(): + # 32 bit Python program (running on a 64 bit Linux): pip should only + # install and run 32 bit compiled extensions in that case. + result = "linux_i686" + + return result + + +def is_manylinux1_compatible(): + # Only Linux, and only x86-64 / i686 + if get_platform() not in ("linux_x86_64", "linux_i686"): + return False + + # Check for presence of _manylinux module + try: + import _manylinux + return bool(_manylinux.manylinux1_compatible) + except (ImportError, AttributeError): + # Fall through to heuristic check below + pass + + # Check glibc version. CentOS 5 uses glibc 2.5. + return pip.utils.glibc.have_compatible_glibc(2, 5) + + +def get_darwin_arches(major, minor, machine): + """Return a list of supported arches (including group arches) for + the given major, minor and machine architecture of an macOS machine. + """ + arches = [] + + def _supports_arch(major, minor, arch): + # Looking at the application support for macOS versions in the chart + # provided by https://en.wikipedia.org/wiki/OS_X#Versions it appears + # our timeline looks roughly like: + # + # 10.0 - Introduces ppc support. + # 10.4 - Introduces ppc64, i386, and x86_64 support, however the ppc64 + # and x86_64 support is CLI only, and cannot be used for GUI + # applications. + # 10.5 - Extends ppc64 and x86_64 support to cover GUI applications. + # 10.6 - Drops support for ppc64 + # 10.7 - Drops support for ppc + # + # Given that we do not know if we're installing a CLI or a GUI + # application, we must be conservative and assume it might be a GUI + # application and behave as if ppc64 and x86_64 support did not occur + # until 10.5. + # + # Note: The above information is taken from the "Application support" + # column in the chart not the "Processor support" since I believe + # that we care about what instruction sets an application can use + # not which processors the OS supports. + if arch == 'ppc': + return (major, minor) <= (10, 5) + if arch == 'ppc64': + return (major, minor) == (10, 5) + if arch == 'i386': + return (major, minor) >= (10, 4) + if arch == 'x86_64': + return (major, minor) >= (10, 5) + if arch in groups: + for garch in groups[arch]: + if _supports_arch(major, minor, garch): + return True + return False + + groups = OrderedDict([ + ("fat", ("i386", "ppc")), + ("intel", ("x86_64", "i386")), + ("fat64", ("x86_64", "ppc64")), + ("fat32", ("x86_64", "i386", "ppc")), + ]) + + if _supports_arch(major, minor, machine): + arches.append(machine) + + for garch in groups: + if machine in groups[garch] and _supports_arch(major, minor, garch): + arches.append(garch) + + arches.append('universal') + + return arches + + +def get_supported(versions=None, noarch=False, platform=None, + impl=None, abi=None): + """Return a list of supported tags for each version specified in + `versions`. + + :param versions: a list of string versions, of the form ["33", "32"], + or None. The first version will be assumed to support our ABI. + :param platform: specify the exact platform you want valid + tags for, or None. If None, use the local system platform. + :param impl: specify the exact implementation you want valid + tags for, or None. If None, use the local interpreter impl. + :param abi: specify the exact abi you want valid + tags for, or None. If None, use the local interpreter abi. + """ + supported = [] + + # Versions must be given with respect to the preference + if versions is None: + versions = [] + version_info = get_impl_version_info() + major = version_info[:-1] + # Support all previous minor Python versions. + for minor in range(version_info[-1], -1, -1): + versions.append(''.join(map(str, major + (minor,)))) + + impl = impl or get_abbr_impl() + + abis = [] + + abi = abi or get_abi_tag() + if abi: + abis[0:0] = [abi] + + abi3s = set() + import imp + for suffix in imp.get_suffixes(): + if suffix[0].startswith('.abi'): + abi3s.add(suffix[0].split('.', 2)[1]) + + abis.extend(sorted(list(abi3s))) + + abis.append('none') + + if not noarch: + arch = platform or get_platform() + if arch.startswith('macosx'): + # support macosx-10.6-intel on macosx-10.9-x86_64 + match = _osx_arch_pat.match(arch) + if match: + name, major, minor, actual_arch = match.groups() + tpl = '{0}_{1}_%i_%s'.format(name, major) + arches = [] + for m in reversed(range(int(minor) + 1)): + for a in get_darwin_arches(int(major), m, actual_arch): + arches.append(tpl % (m, a)) + else: + # arch pattern didn't match (?!) + arches = [arch] + elif platform is None and is_manylinux1_compatible(): + arches = [arch.replace('linux', 'manylinux1'), arch] + else: + arches = [arch] + + # Current version, current API (built specifically for our Python): + for abi in abis: + for arch in arches: + supported.append(('%s%s' % (impl, versions[0]), abi, arch)) + + # abi3 modules compatible with older version of Python + for version in versions[1:]: + # abi3 was introduced in Python 3.2 + if version in ('31', '30'): + break + for abi in abi3s: # empty set if not Python 3 + for arch in arches: + supported.append(("%s%s" % (impl, version), abi, arch)) + + # Has binaries, does not use the Python API: + for arch in arches: + supported.append(('py%s' % (versions[0][0]), 'none', arch)) + + # No abi / arch, but requires our implementation: + supported.append(('%s%s' % (impl, versions[0]), 'none', 'any')) + # Tagged specifically as being cross-version compatible + # (with just the major version specified) + supported.append(('%s%s' % (impl, versions[0][0]), 'none', 'any')) + + # No abi / arch, generic Python + for i, version in enumerate(versions): + supported.append(('py%s' % (version,), 'none', 'any')) + if i == 0: + supported.append(('py%s' % (version[0]), 'none', 'any')) + + return supported + +supported_tags = get_supported() +supported_tags_noarch = get_supported(noarch=True) + +implementation_tag = get_impl_tag() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/__init__.py new file mode 100644 index 0000000..00185a4 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/__init__.py @@ -0,0 +1,10 @@ +from __future__ import absolute_import + +from .req_install import InstallRequirement +from .req_set import RequirementSet, Requirements +from .req_file import parse_requirements + +__all__ = [ + "RequirementSet", "Requirements", "InstallRequirement", + "parse_requirements", +] diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_file.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_file.py new file mode 100644 index 0000000..821df22 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_file.py @@ -0,0 +1,342 @@ +""" +Requirements file parsing +""" + +from __future__ import absolute_import + +import os +import re +import shlex +import sys +import optparse +import warnings + +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves import filterfalse + +import pip +from pip.download import get_file_content +from pip.req.req_install import InstallRequirement +from pip.exceptions import (RequirementsFileParseError) +from pip.utils.deprecation import RemovedInPip10Warning +from pip import cmdoptions + +__all__ = ['parse_requirements'] + +SCHEME_RE = re.compile(r'^(http|https|file):', re.I) +COMMENT_RE = re.compile(r'(^|\s)+#.*$') + +SUPPORTED_OPTIONS = [ + cmdoptions.constraints, + cmdoptions.editable, + cmdoptions.requirements, + cmdoptions.no_index, + cmdoptions.index_url, + cmdoptions.find_links, + cmdoptions.extra_index_url, + cmdoptions.allow_external, + cmdoptions.allow_all_external, + cmdoptions.no_allow_external, + cmdoptions.allow_unsafe, + cmdoptions.no_allow_unsafe, + cmdoptions.use_wheel, + cmdoptions.no_use_wheel, + cmdoptions.always_unzip, + cmdoptions.no_binary, + cmdoptions.only_binary, + cmdoptions.pre, + cmdoptions.process_dependency_links, + cmdoptions.trusted_host, + cmdoptions.require_hashes, +] + +# options to be passed to requirements +SUPPORTED_OPTIONS_REQ = [ + cmdoptions.install_options, + cmdoptions.global_options, + cmdoptions.hash, +] + +# the 'dest' string values +SUPPORTED_OPTIONS_REQ_DEST = [o().dest for o in SUPPORTED_OPTIONS_REQ] + + +def parse_requirements(filename, finder=None, comes_from=None, options=None, + session=None, constraint=False, wheel_cache=None): + """Parse a requirements file and yield InstallRequirement instances. + + :param filename: Path or url of requirements file. + :param finder: Instance of pip.index.PackageFinder. + :param comes_from: Origin description of requirements. + :param options: cli options. + :param session: Instance of pip.download.PipSession. + :param constraint: If true, parsing a constraint file rather than + requirements file. + :param wheel_cache: Instance of pip.wheel.WheelCache + """ + if session is None: + raise TypeError( + "parse_requirements() missing 1 required keyword argument: " + "'session'" + ) + + _, content = get_file_content( + filename, comes_from=comes_from, session=session + ) + + lines_enum = preprocess(content, options) + + for line_number, line in lines_enum: + req_iter = process_line(line, filename, line_number, finder, + comes_from, options, session, wheel_cache, + constraint=constraint) + for req in req_iter: + yield req + + +def preprocess(content, options): + """Split, filter, and join lines, and return a line iterator + + :param content: the content of the requirements file + :param options: cli options + """ + lines_enum = enumerate(content.splitlines(), start=1) + lines_enum = join_lines(lines_enum) + lines_enum = ignore_comments(lines_enum) + lines_enum = skip_regex(lines_enum, options) + return lines_enum + + +def process_line(line, filename, line_number, finder=None, comes_from=None, + options=None, session=None, wheel_cache=None, + constraint=False): + """Process a single requirements line; This can result in creating/yielding + requirements, or updating the finder. + + For lines that contain requirements, the only options that have an effect + are from SUPPORTED_OPTIONS_REQ, and they are scoped to the + requirement. Other options from SUPPORTED_OPTIONS may be present, but are + ignored. + + For lines that do not contain requirements, the only options that have an + effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may + be present, but are ignored. These lines may contain multiple options + (although our docs imply only one is supported), and all our parsed and + affect the finder. + + :param constraint: If True, parsing a constraints file. + :param options: OptionParser options that we may update + """ + parser = build_parser() + defaults = parser.get_default_values() + defaults.index_url = None + if finder: + # `finder.format_control` will be updated during parsing + defaults.format_control = finder.format_control + args_str, options_str = break_args_options(line) + if sys.version_info < (2, 7, 3): + # Prior to 2.7.3, shlex cannot deal with unicode entries + options_str = options_str.encode('utf8') + opts, _ = parser.parse_args(shlex.split(options_str), defaults) + + # preserve for the nested code path + line_comes_from = '%s %s (line %s)' % ( + '-c' if constraint else '-r', filename, line_number) + + # yield a line requirement + if args_str: + isolated = options.isolated_mode if options else False + if options: + cmdoptions.check_install_build_global(options, opts) + # get the options that apply to requirements + req_options = {} + for dest in SUPPORTED_OPTIONS_REQ_DEST: + if dest in opts.__dict__ and opts.__dict__[dest]: + req_options[dest] = opts.__dict__[dest] + yield InstallRequirement.from_line( + args_str, line_comes_from, constraint=constraint, + isolated=isolated, options=req_options, wheel_cache=wheel_cache + ) + + # yield an editable requirement + elif opts.editables: + isolated = options.isolated_mode if options else False + default_vcs = options.default_vcs if options else None + yield InstallRequirement.from_editable( + opts.editables[0], comes_from=line_comes_from, + constraint=constraint, default_vcs=default_vcs, isolated=isolated, + wheel_cache=wheel_cache + ) + + # parse a nested requirements file + elif opts.requirements or opts.constraints: + if opts.requirements: + req_path = opts.requirements[0] + nested_constraint = False + else: + req_path = opts.constraints[0] + nested_constraint = True + # original file is over http + if SCHEME_RE.search(filename): + # do a url join so relative paths work + req_path = urllib_parse.urljoin(filename, req_path) + # original file and nested file are paths + elif not SCHEME_RE.search(req_path): + # do a join so relative paths work + req_path = os.path.join(os.path.dirname(filename), req_path) + # TODO: Why not use `comes_from='-r {} (line {})'` here as well? + parser = parse_requirements( + req_path, finder, comes_from, options, session, + constraint=nested_constraint, wheel_cache=wheel_cache + ) + for req in parser: + yield req + + # percolate hash-checking option upward + elif opts.require_hashes: + options.require_hashes = opts.require_hashes + + # set finder options + elif finder: + if opts.allow_external: + warnings.warn( + "--allow-external has been deprecated and will be removed in " + "the future. Due to changes in the repository protocol, it no " + "longer has any effect.", + RemovedInPip10Warning, + ) + + if opts.allow_all_external: + warnings.warn( + "--allow-all-external has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if opts.allow_unverified: + warnings.warn( + "--allow-unverified has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if opts.index_url: + finder.index_urls = [opts.index_url] + if opts.use_wheel is False: + finder.use_wheel = False + pip.index.fmt_ctl_no_use_wheel(finder.format_control) + if opts.no_index is True: + finder.index_urls = [] + if opts.extra_index_urls: + finder.index_urls.extend(opts.extra_index_urls) + if opts.find_links: + # FIXME: it would be nice to keep track of the source + # of the find_links: support a find-links local path + # relative to a requirements file. + value = opts.find_links[0] + req_dir = os.path.dirname(os.path.abspath(filename)) + relative_to_reqs_file = os.path.join(req_dir, value) + if os.path.exists(relative_to_reqs_file): + value = relative_to_reqs_file + finder.find_links.append(value) + if opts.pre: + finder.allow_all_prereleases = True + if opts.process_dependency_links: + finder.process_dependency_links = True + if opts.trusted_hosts: + finder.secure_origins.extend( + ("*", host, "*") for host in opts.trusted_hosts) + + +def break_args_options(line): + """Break up the line into an args and options string. We only want to shlex + (and then optparse) the options, not the args. args can contain markers + which are corrupted by shlex. + """ + tokens = line.split(' ') + args = [] + options = tokens[:] + for token in tokens: + if token.startswith('-') or token.startswith('--'): + break + else: + args.append(token) + options.pop(0) + return ' '.join(args), ' '.join(options) + + +def build_parser(): + """ + Return a parser for parsing requirement lines + """ + parser = optparse.OptionParser(add_help_option=False) + + option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ + for option_factory in option_factories: + option = option_factory() + parser.add_option(option) + + # By default optparse sys.exits on parsing errors. We want to wrap + # that in our own exception. + def parser_exit(self, msg): + raise RequirementsFileParseError(msg) + parser.exit = parser_exit + + return parser + + +def join_lines(lines_enum): + """Joins a line ending in '\' with the previous line (except when following + comments). The joined line takes on the index of the first line. + """ + primary_line_number = None + new_line = [] + for line_number, line in lines_enum: + if not line.endswith('\\') or COMMENT_RE.match(line): + if COMMENT_RE.match(line): + # this ensures comments are always matched later + line = ' ' + line + if new_line: + new_line.append(line) + yield primary_line_number, ''.join(new_line) + new_line = [] + else: + yield line_number, line + else: + if not new_line: + primary_line_number = line_number + new_line.append(line.strip('\\')) + + # last line contains \ + if new_line: + yield primary_line_number, ''.join(new_line) + + # TODO: handle space after '\'. + + +def ignore_comments(lines_enum): + """ + Strips comments and filter empty lines. + """ + for line_number, line in lines_enum: + line = COMMENT_RE.sub('', line) + line = line.strip() + if line: + yield line_number, line + + +def skip_regex(lines_enum, options): + """ + Skip lines that match '--skip-requirements-regex' pattern + + Note: the regex pattern is only built once + """ + skip_regex = options.skip_requirements_regex if options else None + if skip_regex: + pattern = re.compile(skip_regex) + lines_enum = filterfalse( + lambda e: pattern.search(e[1]), + lines_enum) + return lines_enum diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_install.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_install.py new file mode 100644 index 0000000..1a98f37 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_install.py @@ -0,0 +1,1204 @@ +from __future__ import absolute_import + +import logging +import os +import re +import shutil +import sys +import tempfile +import traceback +import warnings +import zipfile + +from distutils import sysconfig +from distutils.util import change_root +from email.parser import FeedParser + +from pip._vendor import pkg_resources, six +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.markers import Marker +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version, parse as parse_version +from pip._vendor.six.moves import configparser + +import pip.wheel + +from pip.compat import native_str, get_stdlib, WINDOWS +from pip.download import is_url, url_to_path, path_to_url, is_archive_file +from pip.exceptions import ( + InstallationError, UninstallationError, +) +from pip.locations import ( + bin_py, running_under_virtualenv, PIP_DELETE_MARKER_FILENAME, bin_user, +) +from pip.utils import ( + display_path, rmtree, ask_path_exists, backup_dir, is_installable_dir, + dist_in_usersite, dist_in_site_packages, egg_link_path, + call_subprocess, read_text_file, FakeFile, _make_build_dir, ensure_dir, + get_installed_version, normalize_path, dist_is_local, +) + +from pip.utils.hashes import Hashes +from pip.utils.deprecation import RemovedInPip10Warning +from pip.utils.logging import indent_log +from pip.utils.setuptools_build import SETUPTOOLS_SHIM +from pip.utils.ui import open_spinner +from pip.req.req_uninstall import UninstallPathSet +from pip.vcs import vcs +from pip.wheel import move_wheel_files, Wheel + + +logger = logging.getLogger(__name__) + +operators = specifiers.Specifier._operators.keys() + + +def _strip_extras(path): + m = re.match(r'^(.+)(\[[^\]]+\])$', path) + extras = None + if m: + path_no_extras = m.group(1) + extras = m.group(2) + else: + path_no_extras = path + + return path_no_extras, extras + + +def _safe_extras(extras): + return set(pkg_resources.safe_extra(extra) for extra in extras) + + +class InstallRequirement(object): + + def __init__(self, req, comes_from, source_dir=None, editable=False, + link=None, as_egg=False, update=True, + pycompile=True, markers=None, isolated=False, options=None, + wheel_cache=None, constraint=False): + self.extras = () + if isinstance(req, six.string_types): + try: + req = Requirement(req) + except InvalidRequirement: + if os.path.sep in req: + add_msg = "It looks like a path. Does it exist ?" + elif '=' in req and not any(op in req for op in operators): + add_msg = "= is not a valid operator. Did you mean == ?" + else: + add_msg = traceback.format_exc() + raise InstallationError( + "Invalid requirement: '%s'\n%s" % (req, add_msg)) + self.extras = _safe_extras(req.extras) + + self.req = req + self.comes_from = comes_from + self.constraint = constraint + self.source_dir = source_dir + self.editable = editable + + self._wheel_cache = wheel_cache + self.link = self.original_link = link + self.as_egg = as_egg + if markers is not None: + self.markers = markers + else: + self.markers = req and req.marker + self._egg_info_path = None + # This holds the pkg_resources.Distribution object if this requirement + # is already available: + self.satisfied_by = None + # This hold the pkg_resources.Distribution object if this requirement + # conflicts with another installed distribution: + self.conflicts_with = None + # Temporary build location + self._temp_build_dir = None + # Used to store the global directory where the _temp_build_dir should + # have been created. Cf _correct_build_location method. + self._ideal_build_dir = None + # True if the editable should be updated: + self.update = update + # Set to True after successful installation + self.install_succeeded = None + # UninstallPathSet of uninstalled distribution (for possible rollback) + self.uninstalled = None + # Set True if a legitimate do-nothing-on-uninstall has happened - e.g. + # system site packages, stdlib packages. + self.nothing_to_uninstall = False + self.use_user_site = False + self.target_dir = None + self.options = options if options else {} + self.pycompile = pycompile + # Set to True after successful preparation of this requirement + self.prepared = False + + self.isolated = isolated + + @classmethod + def from_editable(cls, editable_req, comes_from=None, default_vcs=None, + isolated=False, options=None, wheel_cache=None, + constraint=False): + from pip.index import Link + + name, url, extras_override = parse_editable( + editable_req, default_vcs) + if url.startswith('file:'): + source_dir = url_to_path(url) + else: + source_dir = None + + res = cls(name, comes_from, source_dir=source_dir, + editable=True, + link=Link(url), + constraint=constraint, + isolated=isolated, + options=options if options else {}, + wheel_cache=wheel_cache) + + if extras_override is not None: + res.extras = _safe_extras(extras_override) + + return res + + @classmethod + def from_line( + cls, name, comes_from=None, isolated=False, options=None, + wheel_cache=None, constraint=False): + """Creates an InstallRequirement from a name, which might be a + requirement, directory containing 'setup.py', filename, or URL. + """ + from pip.index import Link + + if is_url(name): + marker_sep = '; ' + else: + marker_sep = ';' + if marker_sep in name: + name, markers = name.split(marker_sep, 1) + markers = markers.strip() + if not markers: + markers = None + else: + markers = Marker(markers) + else: + markers = None + name = name.strip() + req = None + path = os.path.normpath(os.path.abspath(name)) + link = None + extras = None + + if is_url(name): + link = Link(name) + else: + p, extras = _strip_extras(path) + if (os.path.isdir(p) and + (os.path.sep in name or name.startswith('.'))): + + if not is_installable_dir(p): + raise InstallationError( + "Directory %r is not installable. File 'setup.py' " + "not found." % name + ) + link = Link(path_to_url(p)) + elif is_archive_file(p): + if not os.path.isfile(p): + logger.warning( + 'Requirement %r looks like a filename, but the ' + 'file does not exist', + name + ) + link = Link(path_to_url(p)) + + # it's a local file, dir, or url + if link: + # Handle relative file URLs + if link.scheme == 'file' and re.search(r'\.\./', link.url): + link = Link( + path_to_url(os.path.normpath(os.path.abspath(link.path)))) + # wheel file + if link.is_wheel: + wheel = Wheel(link.filename) # can raise InvalidWheelFilename + req = "%s==%s" % (wheel.name, wheel.version) + else: + # set the req to the egg fragment. when it's not there, this + # will become an 'unnamed' requirement + req = link.egg_fragment + + # a requirement specifier + else: + req = name + + options = options if options else {} + res = cls(req, comes_from, link=link, markers=markers, + isolated=isolated, options=options, + wheel_cache=wheel_cache, constraint=constraint) + + if extras: + res.extras = _safe_extras( + Requirement('placeholder' + extras).extras) + + return res + + def __str__(self): + if self.req: + s = str(self.req) + if self.link: + s += ' from %s' % self.link.url + else: + s = self.link.url if self.link else None + if self.satisfied_by is not None: + s += ' in %s' % display_path(self.satisfied_by.location) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += ' (from %s)' % comes_from + return s + + def __repr__(self): + return '<%s object: %s editable=%r>' % ( + self.__class__.__name__, str(self), self.editable) + + def populate_link(self, finder, upgrade, require_hashes): + """Ensure that if a link can be found for this, that it is found. + + Note that self.link may still be None - if Upgrade is False and the + requirement is already installed. + + If require_hashes is True, don't use the wheel cache, because cached + wheels, always built locally, have different hashes than the files + downloaded from the index server and thus throw false hash mismatches. + Furthermore, cached wheels at present have undeterministic contents due + to file modification times. + """ + if self.link is None: + self.link = finder.find_requirement(self, upgrade) + if self._wheel_cache is not None and not require_hashes: + old_link = self.link + self.link = self._wheel_cache.cached_wheel(self.link, self.name) + if old_link != self.link: + logger.debug('Using cached wheel link: %s', self.link) + + @property + def specifier(self): + return self.req.specifier + + @property + def is_pinned(self): + """Return whether I am pinned to an exact version. + + For example, some-package==1.2 is pinned; some-package>1.2 is not. + """ + specifiers = self.specifier + return (len(specifiers) == 1 and + next(iter(specifiers)).operator in ('==', '===')) + + def from_path(self): + if self.req is None: + return None + s = str(self.req) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += '->' + comes_from + return s + + def build_location(self, build_dir): + if self._temp_build_dir is not None: + return self._temp_build_dir + if self.req is None: + # for requirement via a path to a directory: the name of the + # package is not available yet so we create a temp directory + # Once run_egg_info will have run, we'll be able + # to fix it via _correct_build_location + # Some systems have /tmp as a symlink which confuses custom + # builds (such as numpy). Thus, we ensure that the real path + # is returned. + self._temp_build_dir = os.path.realpath( + tempfile.mkdtemp('-build', 'pip-') + ) + self._ideal_build_dir = build_dir + return self._temp_build_dir + if self.editable: + name = self.name.lower() + else: + name = self.name + # FIXME: Is there a better place to create the build_dir? (hg and bzr + # need this) + if not os.path.exists(build_dir): + logger.debug('Creating directory %s', build_dir) + _make_build_dir(build_dir) + return os.path.join(build_dir, name) + + def _correct_build_location(self): + """Move self._temp_build_dir to self._ideal_build_dir/self.req.name + + For some requirements (e.g. a path to a directory), the name of the + package is not available until we run egg_info, so the build_location + will return a temporary directory and store the _ideal_build_dir. + + This is only called by self.egg_info_path to fix the temporary build + directory. + """ + if self.source_dir is not None: + return + assert self.req is not None + assert self._temp_build_dir + assert self._ideal_build_dir + old_location = self._temp_build_dir + self._temp_build_dir = None + new_location = self.build_location(self._ideal_build_dir) + if os.path.exists(new_location): + raise InstallationError( + 'A package already exists in %s; please remove it to continue' + % display_path(new_location)) + logger.debug( + 'Moving package %s from %s to new location %s', + self, display_path(old_location), display_path(new_location), + ) + shutil.move(old_location, new_location) + self._temp_build_dir = new_location + self._ideal_build_dir = None + self.source_dir = new_location + self._egg_info_path = None + + @property + def name(self): + if self.req is None: + return None + return native_str(pkg_resources.safe_name(self.req.name)) + + @property + def setup_py_dir(self): + return os.path.join( + self.source_dir, + self.link and self.link.subdirectory_fragment or '') + + @property + def setup_py(self): + assert self.source_dir, "No source dir for %s" % self + try: + import setuptools # noqa + except ImportError: + if get_installed_version('setuptools') is None: + add_msg = "Please install setuptools." + else: + add_msg = traceback.format_exc() + # Setuptools is not available + raise InstallationError( + "Could not import setuptools which is required to " + "install from a source distribution.\n%s" % add_msg + ) + + setup_py = os.path.join(self.setup_py_dir, 'setup.py') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(setup_py, six.text_type): + setup_py = setup_py.encode(sys.getfilesystemencoding()) + + return setup_py + + def run_egg_info(self): + assert self.source_dir + if self.name: + logger.debug( + 'Running setup.py (path:%s) egg_info for package %s', + self.setup_py, self.name, + ) + else: + logger.debug( + 'Running setup.py (path:%s) egg_info for package from %s', + self.setup_py, self.link, + ) + + with indent_log(): + script = SETUPTOOLS_SHIM % self.setup_py + base_cmd = [sys.executable, '-c', script] + if self.isolated: + base_cmd += ["--no-user-cfg"] + egg_info_cmd = base_cmd + ['egg_info'] + # We can't put the .egg-info files at the root, because then the + # source code will be mistaken for an installed egg, causing + # problems + if self.editable: + egg_base_option = [] + else: + egg_info_dir = os.path.join(self.setup_py_dir, 'pip-egg-info') + ensure_dir(egg_info_dir) + egg_base_option = ['--egg-base', 'pip-egg-info'] + call_subprocess( + egg_info_cmd + egg_base_option, + cwd=self.setup_py_dir, + show_stdout=False, + command_desc='python setup.py egg_info') + + if not self.req: + if isinstance(parse_version(self.pkg_info()["Version"]), Version): + op = "==" + else: + op = "===" + self.req = Requirement( + "".join([ + self.pkg_info()["Name"], + op, + self.pkg_info()["Version"], + ]) + ) + self._correct_build_location() + else: + metadata_name = canonicalize_name(self.pkg_info()["Name"]) + if canonicalize_name(self.req.name) != metadata_name: + logger.warning( + 'Running setup.py (path:%s) egg_info for package %s ' + 'produced metadata for project name %s. Fix your ' + '#egg=%s fragments.', + self.setup_py, self.name, metadata_name, self.name + ) + self.req = Requirement(metadata_name) + + def egg_info_data(self, filename): + if self.satisfied_by is not None: + if not self.satisfied_by.has_metadata(filename): + return None + return self.satisfied_by.get_metadata(filename) + assert self.source_dir + filename = self.egg_info_path(filename) + if not os.path.exists(filename): + return None + data = read_text_file(filename) + return data + + def egg_info_path(self, filename): + if self._egg_info_path is None: + if self.editable: + base = self.source_dir + else: + base = os.path.join(self.setup_py_dir, 'pip-egg-info') + filenames = os.listdir(base) + if self.editable: + filenames = [] + for root, dirs, files in os.walk(base): + for dir in vcs.dirnames: + if dir in dirs: + dirs.remove(dir) + # Iterate over a copy of ``dirs``, since mutating + # a list while iterating over it can cause trouble. + # (See https://github.com/pypa/pip/pull/462.) + for dir in list(dirs): + # Don't search in anything that looks like a virtualenv + # environment + if ( + os.path.lexists( + os.path.join(root, dir, 'bin', 'python') + ) or + os.path.exists( + os.path.join( + root, dir, 'Scripts', 'Python.exe' + ) + )): + dirs.remove(dir) + # Also don't search through tests + elif dir == 'test' or dir == 'tests': + dirs.remove(dir) + filenames.extend([os.path.join(root, dir) + for dir in dirs]) + filenames = [f for f in filenames if f.endswith('.egg-info')] + + if not filenames: + raise InstallationError( + 'No files/directories in %s (from %s)' % (base, filename) + ) + assert filenames, \ + "No files/directories in %s (from %s)" % (base, filename) + + # if we have more than one match, we pick the toplevel one. This + # can easily be the case if there is a dist folder which contains + # an extracted tarball for testing purposes. + if len(filenames) > 1: + filenames.sort( + key=lambda x: x.count(os.path.sep) + + (os.path.altsep and x.count(os.path.altsep) or 0) + ) + self._egg_info_path = os.path.join(base, filenames[0]) + return os.path.join(self._egg_info_path, filename) + + def pkg_info(self): + p = FeedParser() + data = self.egg_info_data('PKG-INFO') + if not data: + logger.warning( + 'No PKG-INFO file found in %s', + display_path(self.egg_info_path('PKG-INFO')), + ) + p.feed(data or '') + return p.close() + + _requirements_section_re = re.compile(r'\[(.*?)\]') + + @property + def installed_version(self): + return get_installed_version(self.name) + + def assert_source_matches_version(self): + assert self.source_dir + version = self.pkg_info()['version'] + if self.req.specifier and version not in self.req.specifier: + logger.warning( + 'Requested %s, but installing version %s', + self, + self.installed_version, + ) + else: + logger.debug( + 'Source in %s has version %s, which satisfies requirement %s', + display_path(self.source_dir), + version, + self, + ) + + def update_editable(self, obtain=True): + if not self.link: + logger.debug( + "Cannot update repository at %s; repository location is " + "unknown", + self.source_dir, + ) + return + assert self.editable + assert self.source_dir + if self.link.scheme == 'file': + # Static paths don't get updated + return + assert '+' in self.link.url, "bad url: %r" % self.link.url + if not self.update: + return + vc_type, url = self.link.url.split('+', 1) + backend = vcs.get_backend(vc_type) + if backend: + vcs_backend = backend(self.link.url) + if obtain: + vcs_backend.obtain(self.source_dir) + else: + vcs_backend.export(self.source_dir) + else: + assert 0, ( + 'Unexpected version control type (in %s): %s' + % (self.link, vc_type)) + + def uninstall(self, auto_confirm=False): + """ + Uninstall the distribution currently satisfying this requirement. + + Prompts before removing or modifying files unless + ``auto_confirm`` is True. + + Refuses to delete or modify files outside of ``sys.prefix`` - + thus uninstallation within a virtual environment can only + modify that virtual environment, even if the virtualenv is + linked to global site-packages. + + """ + if not self.check_if_exists(): + raise UninstallationError( + "Cannot uninstall requirement %s, not installed" % (self.name,) + ) + dist = self.satisfied_by or self.conflicts_with + + dist_path = normalize_path(dist.location) + if not dist_is_local(dist): + logger.info( + "Not uninstalling %s at %s, outside environment %s", + dist.key, + dist_path, + sys.prefix, + ) + self.nothing_to_uninstall = True + return + + if dist_path in get_stdlib(): + logger.info( + "Not uninstalling %s at %s, as it is in the standard library.", + dist.key, + dist_path, + ) + self.nothing_to_uninstall = True + return + + paths_to_remove = UninstallPathSet(dist) + develop_egg_link = egg_link_path(dist) + develop_egg_link_egg_info = '{0}.egg-info'.format( + pkg_resources.to_filename(dist.project_name)) + egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info) + # Special case for distutils installed package + distutils_egg_info = getattr(dist._provider, 'path', None) + + # Uninstall cases order do matter as in the case of 2 installs of the + # same package, pip needs to uninstall the currently detected version + if (egg_info_exists and dist.egg_info.endswith('.egg-info') and + not dist.egg_info.endswith(develop_egg_link_egg_info)): + # if dist.egg_info.endswith(develop_egg_link_egg_info), we + # are in fact in the develop_egg_link case + paths_to_remove.add(dist.egg_info) + if dist.has_metadata('installed-files.txt'): + for installed_file in dist.get_metadata( + 'installed-files.txt').splitlines(): + path = os.path.normpath( + os.path.join(dist.egg_info, installed_file) + ) + paths_to_remove.add(path) + # FIXME: need a test for this elif block + # occurs with --single-version-externally-managed/--record outside + # of pip + elif dist.has_metadata('top_level.txt'): + if dist.has_metadata('namespace_packages.txt'): + namespaces = dist.get_metadata('namespace_packages.txt') + else: + namespaces = [] + for top_level_pkg in [ + p for p + in dist.get_metadata('top_level.txt').splitlines() + if p and p not in namespaces]: + path = os.path.join(dist.location, top_level_pkg) + paths_to_remove.add(path) + paths_to_remove.add(path + '.py') + paths_to_remove.add(path + '.pyc') + paths_to_remove.add(path + '.pyo') + + elif distutils_egg_info: + warnings.warn( + "Uninstalling a distutils installed project ({0}) has been " + "deprecated and will be removed in a future version. This is " + "due to the fact that uninstalling a distutils project will " + "only partially uninstall the project.".format(self.name), + RemovedInPip10Warning, + ) + paths_to_remove.add(distutils_egg_info) + + elif dist.location.endswith('.egg'): + # package installed by easy_install + # We cannot match on dist.egg_name because it can slightly vary + # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg + paths_to_remove.add(dist.location) + easy_install_egg = os.path.split(dist.location)[1] + easy_install_pth = os.path.join(os.path.dirname(dist.location), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) + + elif egg_info_exists and dist.egg_info.endswith('.dist-info'): + for path in pip.wheel.uninstallation_paths(dist): + paths_to_remove.add(path) + + elif develop_egg_link: + # develop egg + with open(develop_egg_link, 'r') as fh: + link_pointer = os.path.normcase(fh.readline().strip()) + assert (link_pointer == dist.location), ( + 'Egg-link %s does not match installed location of %s ' + '(at %s)' % (link_pointer, self.name, dist.location) + ) + paths_to_remove.add(develop_egg_link) + easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, dist.location) + + else: + logger.debug( + 'Not sure how to uninstall: %s - Check: %s', + dist, dist.location) + + # find distutils scripts= scripts + if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): + for script in dist.metadata_listdir('scripts'): + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + paths_to_remove.add(os.path.join(bin_dir, script)) + if WINDOWS: + paths_to_remove.add(os.path.join(bin_dir, script) + '.bat') + + # find console_scripts + if dist.has_metadata('entry_points.txt'): + if six.PY2: + options = {} + else: + options = {"delimiters": ('=', )} + config = configparser.SafeConfigParser(**options) + config.readfp( + FakeFile(dist.get_metadata_lines('entry_points.txt')) + ) + if config.has_section('console_scripts'): + for name, value in config.items('console_scripts'): + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + paths_to_remove.add(os.path.join(bin_dir, name)) + if WINDOWS: + paths_to_remove.add( + os.path.join(bin_dir, name) + '.exe' + ) + paths_to_remove.add( + os.path.join(bin_dir, name) + '.exe.manifest' + ) + paths_to_remove.add( + os.path.join(bin_dir, name) + '-script.py' + ) + + paths_to_remove.remove(auto_confirm) + self.uninstalled = paths_to_remove + + def rollback_uninstall(self): + if self.uninstalled: + self.uninstalled.rollback() + else: + logger.error( + "Can't rollback %s, nothing uninstalled.", self.name, + ) + + def commit_uninstall(self): + if self.uninstalled: + self.uninstalled.commit() + elif not self.nothing_to_uninstall: + logger.error( + "Can't commit %s, nothing uninstalled.", self.name, + ) + + def archive(self, build_dir): + assert self.source_dir + create_archive = True + archive_name = '%s-%s.zip' % (self.name, self.pkg_info()["version"]) + archive_path = os.path.join(build_dir, archive_name) + if os.path.exists(archive_path): + response = ask_path_exists( + 'The file %s exists. (i)gnore, (w)ipe, (b)ackup, (a)bort ' % + display_path(archive_path), ('i', 'w', 'b', 'a')) + if response == 'i': + create_archive = False + elif response == 'w': + logger.warning('Deleting %s', display_path(archive_path)) + os.remove(archive_path) + elif response == 'b': + dest_file = backup_dir(archive_path) + logger.warning( + 'Backing up %s to %s', + display_path(archive_path), + display_path(dest_file), + ) + shutil.move(archive_path, dest_file) + elif response == 'a': + sys.exit(-1) + if create_archive: + zip = zipfile.ZipFile( + archive_path, 'w', zipfile.ZIP_DEFLATED, + allowZip64=True + ) + dir = os.path.normcase(os.path.abspath(self.setup_py_dir)) + for dirpath, dirnames, filenames in os.walk(dir): + if 'pip-egg-info' in dirnames: + dirnames.remove('pip-egg-info') + for dirname in dirnames: + dirname = os.path.join(dirpath, dirname) + name = self._clean_zip_name(dirname, dir) + zipdir = zipfile.ZipInfo(self.name + '/' + name + '/') + zipdir.external_attr = 0x1ED << 16 # 0o755 + zip.writestr(zipdir, '') + for filename in filenames: + if filename == PIP_DELETE_MARKER_FILENAME: + continue + filename = os.path.join(dirpath, filename) + name = self._clean_zip_name(filename, dir) + zip.write(filename, self.name + '/' + name) + zip.close() + logger.info('Saved %s', display_path(archive_path)) + + def _clean_zip_name(self, name, prefix): + assert name.startswith(prefix + os.path.sep), ( + "name %r doesn't start with prefix %r" % (name, prefix) + ) + name = name[len(prefix) + 1:] + name = name.replace(os.path.sep, '/') + return name + + def match_markers(self, extras_requested=None): + if not extras_requested: + # Provide an extra to safely evaluate the markers + # without matching any extra + extras_requested = ('',) + if self.markers is not None: + return any( + self.markers.evaluate({'extra': extra}) + for extra in extras_requested) + else: + return True + + def install(self, install_options, global_options=[], root=None, + prefix=None): + if self.editable: + self.install_editable( + install_options, global_options, prefix=prefix) + return + if self.is_wheel: + version = pip.wheel.wheel_version(self.source_dir) + pip.wheel.check_compatibility(version, self.name) + + self.move_wheel_files(self.source_dir, root=root, prefix=prefix) + self.install_succeeded = True + return + + # Extend the list of global and install options passed on to + # the setup.py call with the ones from the requirements file. + # Options specified in requirements file override those + # specified on the command line, since the last option given + # to setup.py is the one that is used. + global_options += self.options.get('global_options', []) + install_options += self.options.get('install_options', []) + + if self.isolated: + global_options = list(global_options) + ["--no-user-cfg"] + + temp_location = tempfile.mkdtemp('-record', 'pip-') + record_filename = os.path.join(temp_location, 'install-record.txt') + try: + install_args = self.get_install_args( + global_options, record_filename, root, prefix) + msg = 'Running setup.py install for %s' % (self.name,) + with open_spinner(msg) as spinner: + with indent_log(): + call_subprocess( + install_args + install_options, + cwd=self.setup_py_dir, + show_stdout=False, + spinner=spinner, + ) + + if not os.path.exists(record_filename): + logger.debug('Record file %s not found', record_filename) + return + self.install_succeeded = True + if self.as_egg: + # there's no --always-unzip option we can pass to install + # command so we unable to save the installed-files.txt + return + + def prepend_root(path): + if root is None or not os.path.isabs(path): + return path + else: + return change_root(root, path) + + with open(record_filename) as f: + for line in f: + directory = os.path.dirname(line) + if directory.endswith('.egg-info'): + egg_info_dir = prepend_root(directory) + break + else: + logger.warning( + 'Could not find .egg-info directory in install record' + ' for %s', + self, + ) + # FIXME: put the record somewhere + # FIXME: should this be an error? + return + new_lines = [] + with open(record_filename) as f: + for line in f: + filename = line.strip() + if os.path.isdir(filename): + filename += os.path.sep + new_lines.append( + os.path.relpath( + prepend_root(filename), egg_info_dir) + ) + inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt') + with open(inst_files_path, 'w') as f: + f.write('\n'.join(new_lines) + '\n') + finally: + if os.path.exists(record_filename): + os.remove(record_filename) + rmtree(temp_location) + + def ensure_has_source_dir(self, parent_dir): + """Ensure that a source_dir is set. + + This will create a temporary build dir if the name of the requirement + isn't known yet. + + :param parent_dir: The ideal pip parent_dir for the source_dir. + Generally src_dir for editables and build_dir for sdists. + :return: self.source_dir + """ + if self.source_dir is None: + self.source_dir = self.build_location(parent_dir) + return self.source_dir + + def get_install_args(self, global_options, record_filename, root, prefix): + install_args = [sys.executable, "-u"] + install_args.append('-c') + install_args.append(SETUPTOOLS_SHIM % self.setup_py) + install_args += list(global_options) + \ + ['install', '--record', record_filename] + + if not self.as_egg: + install_args += ['--single-version-externally-managed'] + + if root is not None: + install_args += ['--root', root] + if prefix is not None: + install_args += ['--prefix', prefix] + + if self.pycompile: + install_args += ["--compile"] + else: + install_args += ["--no-compile"] + + if running_under_virtualenv(): + py_ver_str = 'python' + sysconfig.get_python_version() + install_args += ['--install-headers', + os.path.join(sys.prefix, 'include', 'site', + py_ver_str, self.name)] + + return install_args + + def remove_temporary_source(self): + """Remove the source files from this requirement, if they are marked + for deletion""" + if self.source_dir and os.path.exists( + os.path.join(self.source_dir, PIP_DELETE_MARKER_FILENAME)): + logger.debug('Removing source in %s', self.source_dir) + rmtree(self.source_dir) + self.source_dir = None + if self._temp_build_dir and os.path.exists(self._temp_build_dir): + rmtree(self._temp_build_dir) + self._temp_build_dir = None + + def install_editable(self, install_options, + global_options=(), prefix=None): + logger.info('Running setup.py develop for %s', self.name) + + if self.isolated: + global_options = list(global_options) + ["--no-user-cfg"] + + if prefix: + prefix_param = ['--prefix={0}'.format(prefix)] + install_options = list(install_options) + prefix_param + + with indent_log(): + # FIXME: should we do --install-headers here too? + call_subprocess( + [ + sys.executable, + '-c', + SETUPTOOLS_SHIM % self.setup_py + ] + + list(global_options) + + ['develop', '--no-deps'] + + list(install_options), + + cwd=self.setup_py_dir, + show_stdout=False) + + self.install_succeeded = True + + def check_if_exists(self): + """Find an installed distribution that satisfies or conflicts + with this requirement, and set self.satisfied_by or + self.conflicts_with appropriately. + """ + if self.req is None: + return False + try: + # get_distribution() will resolve the entire list of requirements + # anyway, and we've already determined that we need the requirement + # in question, so strip the marker so that we don't try to + # evaluate it. + no_marker = Requirement(str(self.req)) + no_marker.marker = None + self.satisfied_by = pkg_resources.get_distribution(str(no_marker)) + if self.editable and self.satisfied_by: + self.conflicts_with = self.satisfied_by + # when installing editables, nothing pre-existing should ever + # satisfy + self.satisfied_by = None + return True + except pkg_resources.DistributionNotFound: + return False + except pkg_resources.VersionConflict: + existing_dist = pkg_resources.get_distribution( + self.req.name + ) + if self.use_user_site: + if dist_in_usersite(existing_dist): + self.conflicts_with = existing_dist + elif (running_under_virtualenv() and + dist_in_site_packages(existing_dist)): + raise InstallationError( + "Will not install to the user site because it will " + "lack sys.path precedence to %s in %s" % + (existing_dist.project_name, existing_dist.location) + ) + else: + self.conflicts_with = existing_dist + return True + + @property + def is_wheel(self): + return self.link and self.link.is_wheel + + def move_wheel_files(self, wheeldir, root=None, prefix=None): + move_wheel_files( + self.name, self.req, wheeldir, + user=self.use_user_site, + home=self.target_dir, + root=root, + prefix=prefix, + pycompile=self.pycompile, + isolated=self.isolated, + ) + + def get_dist(self): + """Return a pkg_resources.Distribution built from self.egg_info_path""" + egg_info = self.egg_info_path('').rstrip('/') + base_dir = os.path.dirname(egg_info) + metadata = pkg_resources.PathMetadata(base_dir, egg_info) + dist_name = os.path.splitext(os.path.basename(egg_info))[0] + return pkg_resources.Distribution( + os.path.dirname(egg_info), + project_name=dist_name, + metadata=metadata) + + @property + def has_hash_options(self): + """Return whether any known-good hashes are specified as options. + + These activate --require-hashes mode; hashes specified as part of a + URL do not. + + """ + return bool(self.options.get('hashes', {})) + + def hashes(self, trust_internet=True): + """Return a hash-comparer that considers my option- and URL-based + hashes to be known-good. + + Hashes in URLs--ones embedded in the requirements file, not ones + downloaded from an index server--are almost peers with ones from + flags. They satisfy --require-hashes (whether it was implicitly or + explicitly activated) but do not activate it. md5 and sha224 are not + allowed in flags, which should nudge people toward good algos. We + always OR all hashes together, even ones from URLs. + + :param trust_internet: Whether to trust URL-based (#md5=...) hashes + downloaded from the internet, as by populate_link() + + """ + good_hashes = self.options.get('hashes', {}).copy() + link = self.link if trust_internet else self.original_link + if link and link.hash: + good_hashes.setdefault(link.hash_name, []).append(link.hash) + return Hashes(good_hashes) + + +def _strip_postfix(req): + """ + Strip req postfix ( -dev, 0.2, etc ) + """ + # FIXME: use package_to_requirement? + match = re.search(r'^(.*?)(?:-dev|-\d.*)$', req) + if match: + # Strip off -dev, -0.2, etc. + req = match.group(1) + return req + + +def parse_editable(editable_req, default_vcs=None): + """Parses an editable requirement into: + - a requirement name + - an URL + - extras + - editable options + Accepted requirements: + svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir + .[some_extra] + """ + + from pip.index import Link + + url = editable_req + extras = None + + # If a file path is specified with extras, strip off the extras. + m = re.match(r'^(.+)(\[[^\]]+\])$', url) + if m: + url_no_extras = m.group(1) + extras = m.group(2) + else: + url_no_extras = url + + if os.path.isdir(url_no_extras): + if not os.path.exists(os.path.join(url_no_extras, 'setup.py')): + raise InstallationError( + "Directory %r is not installable. File 'setup.py' not found." % + url_no_extras + ) + # Treating it as code that has already been checked out + url_no_extras = path_to_url(url_no_extras) + + if url_no_extras.lower().startswith('file:'): + package_name = Link(url_no_extras).egg_fragment + if extras: + return ( + package_name, + url_no_extras, + Requirement("placeholder" + extras.lower()).extras, + ) + else: + return package_name, url_no_extras, None + + for version_control in vcs: + if url.lower().startswith('%s:' % version_control): + url = '%s+%s' % (version_control, url) + break + + if '+' not in url: + if default_vcs: + warnings.warn( + "--default-vcs has been deprecated and will be removed in " + "the future.", + RemovedInPip10Warning, + ) + url = default_vcs + '+' + url + else: + raise InstallationError( + '%s should either be a path to a local project or a VCS url ' + 'beginning with svn+, git+, hg+, or bzr+' % + editable_req + ) + + vc_type = url.split('+', 1)[0].lower() + + if not vcs.get_backend(vc_type): + error_message = 'For --editable=%s only ' % editable_req + \ + ', '.join([backend.name + '+URL' for backend in vcs.backends]) + \ + ' is currently supported' + raise InstallationError(error_message) + + package_name = Link(url).egg_fragment + if not package_name: + raise InstallationError( + "Could not detect requirement name, please specify one with #egg=" + ) + if not package_name: + raise InstallationError( + '--editable=%s is not the right format; it must have ' + '#egg=Package' % editable_req + ) + return _strip_postfix(package_name), url, None diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_set.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_set.py new file mode 100644 index 0000000..76aec06 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_set.py @@ -0,0 +1,798 @@ +from __future__ import absolute_import + +from collections import defaultdict +from itertools import chain +import logging +import os + +from pip._vendor import pkg_resources +from pip._vendor import requests + +from pip.compat import expanduser +from pip.download import (is_file_url, is_dir_url, is_vcs_url, url_to_path, + unpack_url) +from pip.exceptions import (InstallationError, BestVersionAlreadyInstalled, + DistributionNotFound, PreviousBuildDirError, + HashError, HashErrors, HashUnpinned, + DirectoryUrlHashUnsupported, VcsHashUnsupported, + UnsupportedPythonVersion) +from pip.req.req_install import InstallRequirement +from pip.utils import ( + display_path, dist_in_usersite, ensure_dir, normalize_path) +from pip.utils.hashes import MissingHashes +from pip.utils.logging import indent_log +from pip.utils.packaging import check_dist_requires_python +from pip.vcs import vcs +from pip.wheel import Wheel + +logger = logging.getLogger(__name__) + + +class Requirements(object): + + def __init__(self): + self._keys = [] + self._dict = {} + + def keys(self): + return self._keys + + def values(self): + return [self._dict[key] for key in self._keys] + + def __contains__(self, item): + return item in self._keys + + def __setitem__(self, key, value): + if key not in self._keys: + self._keys.append(key) + self._dict[key] = value + + def __getitem__(self, key): + return self._dict[key] + + def __repr__(self): + values = ['%s: %s' % (repr(k), repr(self[k])) for k in self.keys()] + return 'Requirements({%s})' % ', '.join(values) + + +class DistAbstraction(object): + """Abstracts out the wheel vs non-wheel prepare_files logic. + + The requirements for anything installable are as follows: + - we must be able to determine the requirement name + (or we can't correctly handle the non-upgrade case). + - we must be able to generate a list of run-time dependencies + without installing any additional packages (or we would + have to either burn time by doing temporary isolated installs + or alternatively violate pips 'don't start installing unless + all requirements are available' rule - neither of which are + desirable). + - for packages with setup requirements, we must also be able + to determine their requirements without installing additional + packages (for the same reason as run-time dependencies) + - we must be able to create a Distribution object exposing the + above metadata. + """ + + def __init__(self, req_to_install): + self.req_to_install = req_to_install + + def dist(self, finder): + """Return a setuptools Dist object.""" + raise NotImplementedError(self.dist) + + def prep_for_dist(self): + """Ensure that we can get a Dist for this requirement.""" + raise NotImplementedError(self.dist) + + +def make_abstract_dist(req_to_install): + """Factory to make an abstract dist object. + + Preconditions: Either an editable req with a source_dir, or satisfied_by or + a wheel link, or a non-editable req with a source_dir. + + :return: A concrete DistAbstraction. + """ + if req_to_install.editable: + return IsSDist(req_to_install) + elif req_to_install.link and req_to_install.link.is_wheel: + return IsWheel(req_to_install) + else: + return IsSDist(req_to_install) + + +class IsWheel(DistAbstraction): + + def dist(self, finder): + return list(pkg_resources.find_distributions( + self.req_to_install.source_dir))[0] + + def prep_for_dist(self): + # FIXME:https://github.com/pypa/pip/issues/1112 + pass + + +class IsSDist(DistAbstraction): + + def dist(self, finder): + dist = self.req_to_install.get_dist() + # FIXME: shouldn't be globally added: + if dist.has_metadata('dependency_links.txt'): + finder.add_dependency_links( + dist.get_metadata_lines('dependency_links.txt') + ) + return dist + + def prep_for_dist(self): + self.req_to_install.run_egg_info() + self.req_to_install.assert_source_matches_version() + + +class Installed(DistAbstraction): + + def dist(self, finder): + return self.req_to_install.satisfied_by + + def prep_for_dist(self): + pass + + +class RequirementSet(object): + + def __init__(self, build_dir, src_dir, download_dir, upgrade=False, + upgrade_strategy=None, ignore_installed=False, as_egg=False, + target_dir=None, ignore_dependencies=False, + force_reinstall=False, use_user_site=False, session=None, + pycompile=True, isolated=False, wheel_download_dir=None, + wheel_cache=None, require_hashes=False, + ignore_requires_python=False): + """Create a RequirementSet. + + :param wheel_download_dir: Where still-packed .whl files should be + written to. If None they are written to the download_dir parameter. + Separate to download_dir to permit only keeping wheel archives for + pip wheel. + :param download_dir: Where still packed archives should be written to. + If None they are not saved, and are deleted immediately after + unpacking. + :param wheel_cache: The pip wheel cache, for passing to + InstallRequirement. + """ + if session is None: + raise TypeError( + "RequirementSet() missing 1 required keyword argument: " + "'session'" + ) + + self.build_dir = build_dir + self.src_dir = src_dir + # XXX: download_dir and wheel_download_dir overlap semantically and may + # be combined if we're willing to have non-wheel archives present in + # the wheelhouse output by 'pip wheel'. + self.download_dir = download_dir + self.upgrade = upgrade + self.upgrade_strategy = upgrade_strategy + self.ignore_installed = ignore_installed + self.force_reinstall = force_reinstall + self.requirements = Requirements() + # Mapping of alias: real_name + self.requirement_aliases = {} + self.unnamed_requirements = [] + self.ignore_dependencies = ignore_dependencies + self.ignore_requires_python = ignore_requires_python + self.successfully_downloaded = [] + self.successfully_installed = [] + self.reqs_to_cleanup = [] + self.as_egg = as_egg + self.use_user_site = use_user_site + self.target_dir = target_dir # set from --target option + self.session = session + self.pycompile = pycompile + self.isolated = isolated + if wheel_download_dir: + wheel_download_dir = normalize_path(wheel_download_dir) + self.wheel_download_dir = wheel_download_dir + self._wheel_cache = wheel_cache + self.require_hashes = require_hashes + # Maps from install_req -> dependencies_of_install_req + self._dependencies = defaultdict(list) + + def __str__(self): + reqs = [req for req in self.requirements.values() + if not req.comes_from] + reqs.sort(key=lambda req: req.name.lower()) + return ' '.join([str(req.req) for req in reqs]) + + def __repr__(self): + reqs = [req for req in self.requirements.values()] + reqs.sort(key=lambda req: req.name.lower()) + reqs_str = ', '.join([str(req.req) for req in reqs]) + return ('<%s object; %d requirement(s): %s>' + % (self.__class__.__name__, len(reqs), reqs_str)) + + def add_requirement(self, install_req, parent_req_name=None, + extras_requested=None): + """Add install_req as a requirement to install. + + :param parent_req_name: The name of the requirement that needed this + added. The name is used because when multiple unnamed requirements + resolve to the same name, we could otherwise end up with dependency + links that point outside the Requirements set. parent_req must + already be added. Note that None implies that this is a user + supplied requirement, vs an inferred one. + :param extras_requested: an iterable of extras used to evaluate the + environement markers. + :return: Additional requirements to scan. That is either [] if + the requirement is not applicable, or [install_req] if the + requirement is applicable and has just been added. + """ + name = install_req.name + if not install_req.match_markers(extras_requested): + logger.warning("Ignoring %s: markers '%s' don't match your " + "environment", install_req.name, + install_req.markers) + return [] + + # This check has to come after we filter requirements with the + # environment markers. + if install_req.link and install_req.link.is_wheel: + wheel = Wheel(install_req.link.filename) + if not wheel.supported(): + raise InstallationError( + "%s is not a supported wheel on this platform." % + wheel.filename + ) + + install_req.as_egg = self.as_egg + install_req.use_user_site = self.use_user_site + install_req.target_dir = self.target_dir + install_req.pycompile = self.pycompile + install_req.is_direct = (parent_req_name is None) + + if not name: + # url or path requirement w/o an egg fragment + self.unnamed_requirements.append(install_req) + return [install_req] + else: + try: + existing_req = self.get_requirement(name) + except KeyError: + existing_req = None + if (parent_req_name is None and existing_req and not + existing_req.constraint and + existing_req.extras == install_req.extras and not + existing_req.req.specifier == install_req.req.specifier): + raise InstallationError( + 'Double requirement given: %s (already in %s, name=%r)' + % (install_req, existing_req, name)) + if not existing_req: + # Add requirement + self.requirements[name] = install_req + # FIXME: what about other normalizations? E.g., _ vs. -? + if name.lower() != name: + self.requirement_aliases[name.lower()] = name + result = [install_req] + else: + # Assume there's no need to scan, and that we've already + # encountered this for scanning. + result = [] + if not install_req.constraint and existing_req.constraint: + if (install_req.link and not (existing_req.link and + install_req.link.path == existing_req.link.path)): + self.reqs_to_cleanup.append(install_req) + raise InstallationError( + "Could not satisfy constraints for '%s': " + "installation from path or url cannot be " + "constrained to a version" % name) + # If we're now installing a constraint, mark the existing + # object for real installation. + existing_req.constraint = False + existing_req.extras = tuple( + sorted(set(existing_req.extras).union( + set(install_req.extras)))) + logger.debug("Setting %s extras to: %s", + existing_req, existing_req.extras) + # And now we need to scan this. + result = [existing_req] + # Canonicalise to the already-added object for the backref + # check below. + install_req = existing_req + if parent_req_name: + parent_req = self.get_requirement(parent_req_name) + self._dependencies[parent_req].append(install_req) + return result + + def has_requirement(self, project_name): + name = project_name.lower() + if (name in self.requirements and + not self.requirements[name].constraint or + name in self.requirement_aliases and + not self.requirements[self.requirement_aliases[name]].constraint): + return True + return False + + @property + def has_requirements(self): + return list(req for req in self.requirements.values() if not + req.constraint) or self.unnamed_requirements + + @property + def is_download(self): + if self.download_dir: + self.download_dir = expanduser(self.download_dir) + if os.path.exists(self.download_dir): + return True + else: + logger.critical('Could not find download directory') + raise InstallationError( + "Could not find or access download directory '%s'" + % display_path(self.download_dir)) + return False + + def get_requirement(self, project_name): + for name in project_name, project_name.lower(): + if name in self.requirements: + return self.requirements[name] + if name in self.requirement_aliases: + return self.requirements[self.requirement_aliases[name]] + raise KeyError("No project with the name %r" % project_name) + + def uninstall(self, auto_confirm=False): + for req in self.requirements.values(): + if req.constraint: + continue + req.uninstall(auto_confirm=auto_confirm) + req.commit_uninstall() + + def prepare_files(self, finder): + """ + Prepare process. Create temp directories, download and/or unpack files. + """ + # make the wheelhouse + if self.wheel_download_dir: + ensure_dir(self.wheel_download_dir) + + # If any top-level requirement has a hash specified, enter + # hash-checking mode, which requires hashes from all. + root_reqs = self.unnamed_requirements + self.requirements.values() + require_hashes = (self.require_hashes or + any(req.has_hash_options for req in root_reqs)) + if require_hashes and self.as_egg: + raise InstallationError( + '--egg is not allowed with --require-hashes mode, since it ' + 'delegates dependency resolution to setuptools and could thus ' + 'result in installation of unhashed packages.') + + # Actually prepare the files, and collect any exceptions. Most hash + # exceptions cannot be checked ahead of time, because + # req.populate_link() needs to be called before we can make decisions + # based on link type. + discovered_reqs = [] + hash_errors = HashErrors() + for req in chain(root_reqs, discovered_reqs): + try: + discovered_reqs.extend(self._prepare_file( + finder, + req, + require_hashes=require_hashes, + ignore_dependencies=self.ignore_dependencies)) + except HashError as exc: + exc.req = req + hash_errors.append(exc) + + if hash_errors: + raise hash_errors + + def _is_upgrade_allowed(self, req): + return self.upgrade and ( + self.upgrade_strategy == "eager" or ( + self.upgrade_strategy == "only-if-needed" and req.is_direct + ) + ) + + def _check_skip_installed(self, req_to_install, finder): + """Check if req_to_install should be skipped. + + This will check if the req is installed, and whether we should upgrade + or reinstall it, taking into account all the relevant user options. + + After calling this req_to_install will only have satisfied_by set to + None if the req_to_install is to be upgraded/reinstalled etc. Any + other value will be a dist recording the current thing installed that + satisfies the requirement. + + Note that for vcs urls and the like we can't assess skipping in this + routine - we simply identify that we need to pull the thing down, + then later on it is pulled down and introspected to assess upgrade/ + reinstalls etc. + + :return: A text reason for why it was skipped, or None. + """ + # Check whether to upgrade/reinstall this req or not. + req_to_install.check_if_exists() + if req_to_install.satisfied_by: + upgrade_allowed = self._is_upgrade_allowed(req_to_install) + + # Is the best version is installed. + best_installed = False + + if upgrade_allowed: + # For link based requirements we have to pull the + # tree down and inspect to assess the version #, so + # its handled way down. + if not (self.force_reinstall or req_to_install.link): + try: + finder.find_requirement( + req_to_install, upgrade_allowed) + except BestVersionAlreadyInstalled: + best_installed = True + except DistributionNotFound: + # No distribution found, so we squash the + # error - it will be raised later when we + # re-try later to do the install. + # Why don't we just raise here? + pass + + if not best_installed: + # don't uninstall conflict if user install and + # conflict is not user install + if not (self.use_user_site and not + dist_in_usersite(req_to_install.satisfied_by)): + req_to_install.conflicts_with = \ + req_to_install.satisfied_by + req_to_install.satisfied_by = None + + # Figure out a nice message to say why we're skipping this. + if best_installed: + skip_reason = 'already up-to-date' + elif self.upgrade_strategy == "only-if-needed": + skip_reason = 'not upgraded as not directly required' + else: + skip_reason = 'already satisfied' + + return skip_reason + else: + return None + + def _prepare_file(self, + finder, + req_to_install, + require_hashes=False, + ignore_dependencies=False): + """Prepare a single requirements file. + + :return: A list of additional InstallRequirements to also install. + """ + # Tell user what we are doing for this requirement: + # obtain (editable), skipping, processing (local url), collecting + # (remote url or package name) + if req_to_install.constraint or req_to_install.prepared: + return [] + + req_to_install.prepared = True + + # ###################### # + # # print log messages # # + # ###################### # + if req_to_install.editable: + logger.info('Obtaining %s', req_to_install) + else: + # satisfied_by is only evaluated by calling _check_skip_installed, + # so it must be None here. + assert req_to_install.satisfied_by is None + if not self.ignore_installed: + skip_reason = self._check_skip_installed( + req_to_install, finder) + + if req_to_install.satisfied_by: + assert skip_reason is not None, ( + '_check_skip_installed returned None but ' + 'req_to_install.satisfied_by is set to %r' + % (req_to_install.satisfied_by,)) + logger.info( + 'Requirement %s: %s', skip_reason, + req_to_install) + else: + if (req_to_install.link and + req_to_install.link.scheme == 'file'): + path = url_to_path(req_to_install.link.url) + logger.info('Processing %s', display_path(path)) + else: + logger.info('Collecting %s', req_to_install) + + with indent_log(): + # ################################ # + # # vcs update or unpack archive # # + # ################################ # + if req_to_install.editable: + if require_hashes: + raise InstallationError( + 'The editable requirement %s cannot be installed when ' + 'requiring hashes, because there is no single file to ' + 'hash.' % req_to_install) + req_to_install.ensure_has_source_dir(self.src_dir) + req_to_install.update_editable(not self.is_download) + abstract_dist = make_abstract_dist(req_to_install) + abstract_dist.prep_for_dist() + if self.is_download: + req_to_install.archive(self.download_dir) + req_to_install.check_if_exists() + elif req_to_install.satisfied_by: + if require_hashes: + logger.debug( + 'Since it is already installed, we are trusting this ' + 'package without checking its hash. To ensure a ' + 'completely repeatable environment, install into an ' + 'empty virtualenv.') + abstract_dist = Installed(req_to_install) + else: + # @@ if filesystem packages are not marked + # editable in a req, a non deterministic error + # occurs when the script attempts to unpack the + # build directory + req_to_install.ensure_has_source_dir(self.build_dir) + # If a checkout exists, it's unwise to keep going. version + # inconsistencies are logged later, but do not fail the + # installation. + # FIXME: this won't upgrade when there's an existing + # package unpacked in `req_to_install.source_dir` + if os.path.exists( + os.path.join(req_to_install.source_dir, 'setup.py')): + raise PreviousBuildDirError( + "pip can't proceed with requirements '%s' due to a" + " pre-existing build directory (%s). This is " + "likely due to a previous installation that failed" + ". pip is being responsible and not assuming it " + "can delete this. Please delete it and try again." + % (req_to_install, req_to_install.source_dir) + ) + req_to_install.populate_link( + finder, + self._is_upgrade_allowed(req_to_install), + require_hashes + ) + # We can't hit this spot and have populate_link return None. + # req_to_install.satisfied_by is None here (because we're + # guarded) and upgrade has no impact except when satisfied_by + # is not None. + # Then inside find_requirement existing_applicable -> False + # If no new versions are found, DistributionNotFound is raised, + # otherwise a result is guaranteed. + assert req_to_install.link + link = req_to_install.link + + # Now that we have the real link, we can tell what kind of + # requirements we have and raise some more informative errors + # than otherwise. (For example, we can raise VcsHashUnsupported + # for a VCS URL rather than HashMissing.) + if require_hashes: + # We could check these first 2 conditions inside + # unpack_url and save repetition of conditions, but then + # we would report less-useful error messages for + # unhashable requirements, complaining that there's no + # hash provided. + if is_vcs_url(link): + raise VcsHashUnsupported() + elif is_file_url(link) and is_dir_url(link): + raise DirectoryUrlHashUnsupported() + if (not req_to_install.original_link and + not req_to_install.is_pinned): + # Unpinned packages are asking for trouble when a new + # version is uploaded. This isn't a security check, but + # it saves users a surprising hash mismatch in the + # future. + # + # file:/// URLs aren't pinnable, so don't complain + # about them not being pinned. + raise HashUnpinned() + hashes = req_to_install.hashes( + trust_internet=not require_hashes) + if require_hashes and not hashes: + # Known-good hashes are missing for this requirement, so + # shim it with a facade object that will provoke hash + # computation and then raise a HashMissing exception + # showing the user what the hash should be. + hashes = MissingHashes() + + try: + download_dir = self.download_dir + # We always delete unpacked sdists after pip ran. + autodelete_unpacked = True + if req_to_install.link.is_wheel \ + and self.wheel_download_dir: + # when doing 'pip wheel` we download wheels to a + # dedicated dir. + download_dir = self.wheel_download_dir + if req_to_install.link.is_wheel: + if download_dir: + # When downloading, we only unpack wheels to get + # metadata. + autodelete_unpacked = True + else: + # When installing a wheel, we use the unpacked + # wheel. + autodelete_unpacked = False + unpack_url( + req_to_install.link, req_to_install.source_dir, + download_dir, autodelete_unpacked, + session=self.session, hashes=hashes) + except requests.HTTPError as exc: + logger.critical( + 'Could not install requirement %s because ' + 'of error %s', + req_to_install, + exc, + ) + raise InstallationError( + 'Could not install requirement %s because ' + 'of HTTP error %s for URL %s' % + (req_to_install, exc, req_to_install.link) + ) + abstract_dist = make_abstract_dist(req_to_install) + abstract_dist.prep_for_dist() + if self.is_download: + # Make a .zip of the source_dir we already created. + if req_to_install.link.scheme in vcs.all_schemes: + req_to_install.archive(self.download_dir) + # req_to_install.req is only avail after unpack for URL + # pkgs repeat check_if_exists to uninstall-on-upgrade + # (#14) + if not self.ignore_installed: + req_to_install.check_if_exists() + if req_to_install.satisfied_by: + if self.upgrade or self.ignore_installed: + # don't uninstall conflict if user install and + # conflict is not user install + if not (self.use_user_site and not + dist_in_usersite( + req_to_install.satisfied_by)): + req_to_install.conflicts_with = \ + req_to_install.satisfied_by + req_to_install.satisfied_by = None + else: + logger.info( + 'Requirement already satisfied (use ' + '--upgrade to upgrade): %s', + req_to_install, + ) + + # ###################### # + # # parse dependencies # # + # ###################### # + dist = abstract_dist.dist(finder) + try: + check_dist_requires_python(dist) + except UnsupportedPythonVersion as e: + if self.ignore_requires_python: + logger.warning(e.args[0]) + else: + req_to_install.remove_temporary_source() + raise + more_reqs = [] + + def add_req(subreq, extras_requested): + sub_install_req = InstallRequirement( + str(subreq), + req_to_install, + isolated=self.isolated, + wheel_cache=self._wheel_cache, + ) + more_reqs.extend(self.add_requirement( + sub_install_req, req_to_install.name, + extras_requested=extras_requested)) + + # We add req_to_install before its dependencies, so that we + # can refer to it when adding dependencies. + if not self.has_requirement(req_to_install.name): + # 'unnamed' requirements will get added here + self.add_requirement(req_to_install, None) + + if not ignore_dependencies: + if (req_to_install.extras): + logger.debug( + "Installing extra requirements: %r", + ','.join(req_to_install.extras), + ) + missing_requested = sorted( + set(req_to_install.extras) - set(dist.extras) + ) + for missing in missing_requested: + logger.warning( + '%s does not provide the extra \'%s\'', + dist, missing + ) + + available_requested = sorted( + set(dist.extras) & set(req_to_install.extras) + ) + for subreq in dist.requires(available_requested): + add_req(subreq, extras_requested=available_requested) + + # cleanup tmp src + self.reqs_to_cleanup.append(req_to_install) + + if not req_to_install.editable and not req_to_install.satisfied_by: + # XXX: --no-install leads this to report 'Successfully + # downloaded' for only non-editable reqs, even though we took + # action on them. + self.successfully_downloaded.append(req_to_install) + + return more_reqs + + def cleanup_files(self): + """Clean up files, remove builds.""" + logger.debug('Cleaning up...') + with indent_log(): + for req in self.reqs_to_cleanup: + req.remove_temporary_source() + + def _to_install(self): + """Create the installation order. + + The installation order is topological - requirements are installed + before the requiring thing. We break cycles at an arbitrary point, + and make no other guarantees. + """ + # The current implementation, which we may change at any point + # installs the user specified things in the order given, except when + # dependencies must come earlier to achieve topological order. + order = [] + ordered_reqs = set() + + def schedule(req): + if req.satisfied_by or req in ordered_reqs: + return + if req.constraint: + return + ordered_reqs.add(req) + for dep in self._dependencies[req]: + schedule(dep) + order.append(req) + for install_req in self.requirements.values(): + schedule(install_req) + return order + + def install(self, install_options, global_options=(), *args, **kwargs): + """ + Install everything in this set (after having downloaded and unpacked + the packages) + """ + to_install = self._to_install() + + if to_install: + logger.info( + 'Installing collected packages: %s', + ', '.join([req.name for req in to_install]), + ) + + with indent_log(): + for requirement in to_install: + if requirement.conflicts_with: + logger.info( + 'Found existing installation: %s', + requirement.conflicts_with, + ) + with indent_log(): + requirement.uninstall(auto_confirm=True) + try: + requirement.install( + install_options, + global_options, + *args, + **kwargs + ) + except: + # if install did not succeed, rollback previous uninstall + if (requirement.conflicts_with and not + requirement.install_succeeded): + requirement.rollback_uninstall() + raise + else: + if (requirement.conflicts_with and + requirement.install_succeeded): + requirement.commit_uninstall() + requirement.remove_temporary_source() + + self.successfully_installed = to_install diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_uninstall.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_uninstall.py new file mode 100644 index 0000000..5248430 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/req/req_uninstall.py @@ -0,0 +1,195 @@ +from __future__ import absolute_import + +import logging +import os +import tempfile + +from pip.compat import uses_pycache, WINDOWS, cache_from_source +from pip.exceptions import UninstallationError +from pip.utils import rmtree, ask, is_local, renames, normalize_path +from pip.utils.logging import indent_log + + +logger = logging.getLogger(__name__) + + +class UninstallPathSet(object): + """A set of file paths to be removed in the uninstallation of a + requirement.""" + def __init__(self, dist): + self.paths = set() + self._refuse = set() + self.pth = {} + self.dist = dist + self.save_dir = None + self._moved_paths = [] + + def _permitted(self, path): + """ + Return True if the given path is one we are permitted to + remove/modify, False otherwise. + + """ + return is_local(path) + + def add(self, path): + head, tail = os.path.split(path) + + # we normalize the head to resolve parent directory symlinks, but not + # the tail, since we only want to uninstall symlinks, not their targets + path = os.path.join(normalize_path(head), os.path.normcase(tail)) + + if not os.path.exists(path): + return + if self._permitted(path): + self.paths.add(path) + else: + self._refuse.add(path) + + # __pycache__ files can show up after 'installed-files.txt' is created, + # due to imports + if os.path.splitext(path)[1] == '.py' and uses_pycache: + self.add(cache_from_source(path)) + + def add_pth(self, pth_file, entry): + pth_file = normalize_path(pth_file) + if self._permitted(pth_file): + if pth_file not in self.pth: + self.pth[pth_file] = UninstallPthEntries(pth_file) + self.pth[pth_file].add(entry) + else: + self._refuse.add(pth_file) + + def compact(self, paths): + """Compact a path set to contain the minimal number of paths + necessary to contain all paths in the set. If /a/path/ and + /a/path/to/a/file.txt are both in the set, leave only the + shorter path.""" + short_paths = set() + for path in sorted(paths, key=len): + if not any([ + (path.startswith(shortpath) and + path[len(shortpath.rstrip(os.path.sep))] == os.path.sep) + for shortpath in short_paths]): + short_paths.add(path) + return short_paths + + def _stash(self, path): + return os.path.join( + self.save_dir, os.path.splitdrive(path)[1].lstrip(os.path.sep)) + + def remove(self, auto_confirm=False): + """Remove paths in ``self.paths`` with confirmation (unless + ``auto_confirm`` is True).""" + if not self.paths: + logger.info( + "Can't uninstall '%s'. No files were found to uninstall.", + self.dist.project_name, + ) + return + logger.info( + 'Uninstalling %s-%s:', + self.dist.project_name, self.dist.version + ) + + with indent_log(): + paths = sorted(self.compact(self.paths)) + + if auto_confirm: + response = 'y' + else: + for path in paths: + logger.info(path) + response = ask('Proceed (y/n)? ', ('y', 'n')) + if self._refuse: + logger.info('Not removing or modifying (outside of prefix):') + for path in self.compact(self._refuse): + logger.info(path) + if response == 'y': + self.save_dir = tempfile.mkdtemp(suffix='-uninstall', + prefix='pip-') + for path in paths: + new_path = self._stash(path) + logger.debug('Removing file or directory %s', path) + self._moved_paths.append(path) + renames(path, new_path) + for pth in self.pth.values(): + pth.remove() + logger.info( + 'Successfully uninstalled %s-%s', + self.dist.project_name, self.dist.version + ) + + def rollback(self): + """Rollback the changes previously made by remove().""" + if self.save_dir is None: + logger.error( + "Can't roll back %s; was not uninstalled", + self.dist.project_name, + ) + return False + logger.info('Rolling back uninstall of %s', self.dist.project_name) + for path in self._moved_paths: + tmp_path = self._stash(path) + logger.debug('Replacing %s', path) + renames(tmp_path, path) + for pth in self.pth.values(): + pth.rollback() + + def commit(self): + """Remove temporary save dir: rollback will no longer be possible.""" + if self.save_dir is not None: + rmtree(self.save_dir) + self.save_dir = None + self._moved_paths = [] + + +class UninstallPthEntries(object): + def __init__(self, pth_file): + if not os.path.isfile(pth_file): + raise UninstallationError( + "Cannot remove entries from nonexistent file %s" % pth_file + ) + self.file = pth_file + self.entries = set() + self._saved_lines = None + + def add(self, entry): + entry = os.path.normcase(entry) + # On Windows, os.path.normcase converts the entry to use + # backslashes. This is correct for entries that describe absolute + # paths outside of site-packages, but all the others use forward + # slashes. + if WINDOWS and not os.path.splitdrive(entry)[0]: + entry = entry.replace('\\', '/') + self.entries.add(entry) + + def remove(self): + logger.debug('Removing pth entries from %s:', self.file) + with open(self.file, 'rb') as fh: + # windows uses '\r\n' with py3k, but uses '\n' with py2.x + lines = fh.readlines() + self._saved_lines = lines + if any(b'\r\n' in line for line in lines): + endline = '\r\n' + else: + endline = '\n' + for entry in self.entries: + try: + logger.debug('Removing entry: %s', entry) + lines.remove((entry + endline).encode("utf-8")) + except ValueError: + pass + with open(self.file, 'wb') as fh: + fh.writelines(lines) + + def rollback(self): + if self._saved_lines is None: + logger.error( + 'Cannot roll back changes to %s, none were made', self.file + ) + return False + logger.debug('Rolling %s back to previous state', self.file) + with open(self.file, 'wb') as fh: + fh.writelines(self._saved_lines) + return True diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/status_codes.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/status_codes.py new file mode 100644 index 0000000..275360a --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/status_codes.py @@ -0,0 +1,8 @@ +from __future__ import absolute_import + +SUCCESS = 0 +ERROR = 1 +UNKNOWN_ERROR = 2 +VIRTUALENV_NOT_FOUND = 3 +PREVIOUS_BUILD_DIR_ERROR = 4 +NO_MATCHES_FOUND = 23 diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/__init__.py new file mode 100644 index 0000000..815bd33 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/__init__.py @@ -0,0 +1,852 @@ +from __future__ import absolute_import + +from collections import deque +import contextlib +import errno +import io +import locale +# we have a submodule named 'logging' which would shadow this if we used the +# regular name: +import logging as std_logging +import re +import os +import posixpath +import shutil +import stat +import subprocess +import sys +import tarfile +import zipfile + +from pip.exceptions import InstallationError +from pip.compat import console_to_str, expanduser, stdlib_pkgs +from pip.locations import ( + site_packages, user_site, running_under_virtualenv, virtualenv_no_global, + write_delete_marker_file, +) +from pip._vendor import pkg_resources +from pip._vendor.six.moves import input +from pip._vendor.six import PY2 +from pip._vendor.retrying import retry + +if PY2: + from io import BytesIO as StringIO +else: + from io import StringIO + +__all__ = ['rmtree', 'display_path', 'backup_dir', + 'ask', 'splitext', + 'format_size', 'is_installable_dir', + 'is_svn_page', 'file_contents', + 'split_leading_dir', 'has_leading_dir', + 'normalize_path', + 'renames', 'get_terminal_size', 'get_prog', + 'unzip_file', 'untar_file', 'unpack_file', 'call_subprocess', + 'captured_stdout', 'ensure_dir', + 'ARCHIVE_EXTENSIONS', 'SUPPORTED_EXTENSIONS', + 'get_installed_version'] + + +logger = std_logging.getLogger(__name__) + +BZ2_EXTENSIONS = ('.tar.bz2', '.tbz') +XZ_EXTENSIONS = ('.tar.xz', '.txz', '.tlz', '.tar.lz', '.tar.lzma') +ZIP_EXTENSIONS = ('.zip', '.whl') +TAR_EXTENSIONS = ('.tar.gz', '.tgz', '.tar') +ARCHIVE_EXTENSIONS = ( + ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS) +SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS +try: + import bz2 # noqa + SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS +except ImportError: + logger.debug('bz2 module is not available') + +try: + # Only for Python 3.3+ + import lzma # noqa + SUPPORTED_EXTENSIONS += XZ_EXTENSIONS +except ImportError: + logger.debug('lzma module is not available') + + +def import_or_raise(pkg_or_module_string, ExceptionType, *args, **kwargs): + try: + return __import__(pkg_or_module_string) + except ImportError: + raise ExceptionType(*args, **kwargs) + + +def ensure_dir(path): + """os.path.makedirs without EEXIST.""" + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + +def get_prog(): + try: + if os.path.basename(sys.argv[0]) in ('__main__.py', '-c'): + return "%s -m pip" % sys.executable + except (AttributeError, TypeError, IndexError): + pass + return 'pip' + + +# Retry every half second for up to 3 seconds +@retry(stop_max_delay=3000, wait_fixed=500) +def rmtree(dir, ignore_errors=False): + shutil.rmtree(dir, ignore_errors=ignore_errors, + onerror=rmtree_errorhandler) + + +def rmtree_errorhandler(func, path, exc_info): + """On Windows, the files in .svn are read-only, so when rmtree() tries to + remove them, an exception is thrown. We catch that here, remove the + read-only attribute, and hopefully continue without problems.""" + # if file type currently read only + if os.stat(path).st_mode & stat.S_IREAD: + # convert to read/write + os.chmod(path, stat.S_IWRITE) + # use the original function to repeat the operation + func(path) + return + else: + raise + + +def display_path(path): + """Gives the display value for a given path, making it relative to cwd + if possible.""" + path = os.path.normcase(os.path.abspath(path)) + if sys.version_info[0] == 2: + path = path.decode(sys.getfilesystemencoding(), 'replace') + path = path.encode(sys.getdefaultencoding(), 'replace') + if path.startswith(os.getcwd() + os.path.sep): + path = '.' + path[len(os.getcwd()):] + return path + + +def backup_dir(dir, ext='.bak'): + """Figure out the name of a directory to back up the given dir to + (adding .bak, .bak2, etc)""" + n = 1 + extension = ext + while os.path.exists(dir + extension): + n += 1 + extension = ext + str(n) + return dir + extension + + +def ask_path_exists(message, options): + for action in os.environ.get('PIP_EXISTS_ACTION', '').split(): + if action in options: + return action + return ask(message, options) + + +def ask(message, options): + """Ask the message interactively, with the given possible responses""" + while 1: + if os.environ.get('PIP_NO_INPUT'): + raise Exception( + 'No input was expected ($PIP_NO_INPUT set); question: %s' % + message + ) + response = input(message) + response = response.strip().lower() + if response not in options: + print( + 'Your response (%r) was not one of the expected responses: ' + '%s' % (response, ', '.join(options)) + ) + else: + return response + + +def format_size(bytes): + if bytes > 1000 * 1000: + return '%.1fMB' % (bytes / 1000.0 / 1000) + elif bytes > 10 * 1000: + return '%ikB' % (bytes / 1000) + elif bytes > 1000: + return '%.1fkB' % (bytes / 1000.0) + else: + return '%ibytes' % bytes + + +def is_installable_dir(path): + """Return True if `path` is a directory containing a setup.py file.""" + if not os.path.isdir(path): + return False + setup_py = os.path.join(path, 'setup.py') + if os.path.isfile(setup_py): + return True + return False + + +def is_svn_page(html): + """ + Returns true if the page appears to be the index page of an svn repository + """ + return (re.search(r'<title>[^<]*Revision \d+:', html) and + re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I)) + + +def file_contents(filename): + with open(filename, 'rb') as fp: + return fp.read().decode('utf-8') + + +def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE): + """Yield pieces of data from a file-like object until EOF.""" + while True: + chunk = file.read(size) + if not chunk: + break + yield chunk + + +def split_leading_dir(path): + path = path.lstrip('/').lstrip('\\') + if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) or + '\\' not in path): + return path.split('/', 1) + elif '\\' in path: + return path.split('\\', 1) + else: + return path, '' + + +def has_leading_dir(paths): + """Returns true if all the paths have the same leading path name + (i.e., everything is in one subdirectory in an archive)""" + common_prefix = None + for path in paths: + prefix, rest = split_leading_dir(path) + if not prefix: + return False + elif common_prefix is None: + common_prefix = prefix + elif prefix != common_prefix: + return False + return True + + +def normalize_path(path, resolve_symlinks=True): + """ + Convert a path to its canonical, case-normalized, absolute version. + + """ + path = expanduser(path) + if resolve_symlinks: + path = os.path.realpath(path) + else: + path = os.path.abspath(path) + return os.path.normcase(path) + + +def splitext(path): + """Like os.path.splitext, but take off .tar too""" + base, ext = posixpath.splitext(path) + if base.lower().endswith('.tar'): + ext = base[-4:] + ext + base = base[:-4] + return base, ext + + +def renames(old, new): + """Like os.renames(), but handles renaming across devices.""" + # Implementation borrowed from os.renames(). + head, tail = os.path.split(new) + if head and tail and not os.path.exists(head): + os.makedirs(head) + + shutil.move(old, new) + + head, tail = os.path.split(old) + if head and tail: + try: + os.removedirs(head) + except OSError: + pass + + +def is_local(path): + """ + Return True if path is within sys.prefix, if we're running in a virtualenv. + + If we're not in a virtualenv, all paths are considered "local." + + """ + if not running_under_virtualenv(): + return True + return normalize_path(path).startswith(normalize_path(sys.prefix)) + + +def dist_is_local(dist): + """ + Return True if given Distribution object is installed locally + (i.e. within current virtualenv). + + Always True if we're not in a virtualenv. + + """ + return is_local(dist_location(dist)) + + +def dist_in_usersite(dist): + """ + Return True if given Distribution is installed in user site. + """ + norm_path = normalize_path(dist_location(dist)) + return norm_path.startswith(normalize_path(user_site)) + + +def dist_in_site_packages(dist): + """ + Return True if given Distribution is installed in + distutils.sysconfig.get_python_lib(). + """ + return normalize_path( + dist_location(dist) + ).startswith(normalize_path(site_packages)) + + +def dist_is_editable(dist): + """Is distribution an editable install?""" + for path_item in sys.path: + egg_link = os.path.join(path_item, dist.project_name + '.egg-link') + if os.path.isfile(egg_link): + return True + return False + + +def get_installed_distributions(local_only=True, + skip=stdlib_pkgs, + include_editables=True, + editables_only=False, + user_only=False): + """ + Return a list of installed Distribution objects. + + If ``local_only`` is True (default), only return installations + local to the current virtualenv, if in a virtualenv. + + ``skip`` argument is an iterable of lower-case project names to + ignore; defaults to stdlib_pkgs + + If ``editables`` is False, don't report editables. + + If ``editables_only`` is True , only report editables. + + If ``user_only`` is True , only report installations in the user + site directory. + + """ + if local_only: + local_test = dist_is_local + else: + def local_test(d): + return True + + if include_editables: + def editable_test(d): + return True + else: + def editable_test(d): + return not dist_is_editable(d) + + if editables_only: + def editables_only_test(d): + return dist_is_editable(d) + else: + def editables_only_test(d): + return True + + if user_only: + user_test = dist_in_usersite + else: + def user_test(d): + return True + + return [d for d in pkg_resources.working_set + if local_test(d) and + d.key not in skip and + editable_test(d) and + editables_only_test(d) and + user_test(d) + ] + + +def egg_link_path(dist): + """ + Return the path for the .egg-link file if it exists, otherwise, None. + + There's 3 scenarios: + 1) not in a virtualenv + try to find in site.USER_SITE, then site_packages + 2) in a no-global virtualenv + try to find in site_packages + 3) in a yes-global virtualenv + try to find in site_packages, then site.USER_SITE + (don't look in global location) + + For #1 and #3, there could be odd cases, where there's an egg-link in 2 + locations. + + This method will just return the first one found. + """ + sites = [] + if running_under_virtualenv(): + if virtualenv_no_global(): + sites.append(site_packages) + else: + sites.append(site_packages) + if user_site: + sites.append(user_site) + else: + if user_site: + sites.append(user_site) + sites.append(site_packages) + + for site in sites: + egglink = os.path.join(site, dist.project_name) + '.egg-link' + if os.path.isfile(egglink): + return egglink + + +def dist_location(dist): + """ + Get the site-packages location of this distribution. Generally + this is dist.location, except in the case of develop-installed + packages, where dist.location is the source code location, and we + want to know where the egg-link file is. + + """ + egg_link = egg_link_path(dist) + if egg_link: + return egg_link + return dist.location + + +def get_terminal_size(): + """Returns a tuple (x, y) representing the width(x) and the height(x) + in characters of the terminal window.""" + def ioctl_GWINSZ(fd): + try: + import fcntl + import termios + import struct + cr = struct.unpack( + 'hh', + fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234') + ) + except: + return None + if cr == (0, 0): + return None + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) + return int(cr[1]), int(cr[0]) + + +def current_umask(): + """Get the current umask which involves having to set it temporarily.""" + mask = os.umask(0) + os.umask(mask) + return mask + + +def unzip_file(filename, location, flatten=True): + """ + Unzip the file (with path `filename`) to the destination `location`. All + files are written based on system defaults and umask (i.e. permissions are + not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + zipfp = open(filename, 'rb') + try: + zip = zipfile.ZipFile(zipfp, allowZip64=True) + leading = has_leading_dir(zip.namelist()) and flatten + for info in zip.infolist(): + name = info.filename + data = zip.read(name) + fn = name + if leading: + fn = split_leading_dir(name)[1] + fn = os.path.join(location, fn) + dir = os.path.dirname(fn) + if fn.endswith('/') or fn.endswith('\\'): + # A directory + ensure_dir(fn) + else: + ensure_dir(dir) + fp = open(fn, 'wb') + try: + fp.write(data) + finally: + fp.close() + mode = info.external_attr >> 16 + # if mode and regular file and any execute permissions for + # user/group/world? + if mode and stat.S_ISREG(mode) and mode & 0o111: + # make dest file have execute for user/group/world + # (chmod +x) no-op on windows per python docs + os.chmod(fn, (0o777 - current_umask() | 0o111)) + finally: + zipfp.close() + + +def untar_file(filename, location): + """ + Untar the file (with path `filename`) to the destination `location`. + All files are written based on system defaults and umask (i.e. permissions + are not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): + mode = 'r:gz' + elif filename.lower().endswith(BZ2_EXTENSIONS): + mode = 'r:bz2' + elif filename.lower().endswith(XZ_EXTENSIONS): + mode = 'r:xz' + elif filename.lower().endswith('.tar'): + mode = 'r' + else: + logger.warning( + 'Cannot determine compression type for file %s', filename, + ) + mode = 'r:*' + tar = tarfile.open(filename, mode) + try: + # note: python<=2.5 doesn't seem to know about pax headers, filter them + leading = has_leading_dir([ + member.name for member in tar.getmembers() + if member.name != 'pax_global_header' + ]) + for member in tar.getmembers(): + fn = member.name + if fn == 'pax_global_header': + continue + if leading: + fn = split_leading_dir(fn)[1] + path = os.path.join(location, fn) + if member.isdir(): + ensure_dir(path) + elif member.issym(): + try: + tar._extract_member(member, path) + except Exception as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + else: + try: + fp = tar.extractfile(member) + except (KeyError, AttributeError) as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + ensure_dir(os.path.dirname(path)) + with open(path, 'wb') as destfp: + shutil.copyfileobj(fp, destfp) + fp.close() + # Update the timestamp (useful for cython compiled files) + tar.utime(member, path) + # member have any execute permissions for user/group/world? + if member.mode & 0o111: + # make dest file have execute for user/group/world + # no-op on windows per python docs + os.chmod(path, (0o777 - current_umask() | 0o111)) + finally: + tar.close() + + +def unpack_file(filename, location, content_type, link): + filename = os.path.realpath(filename) + if (content_type == 'application/zip' or + filename.lower().endswith(ZIP_EXTENSIONS) or + zipfile.is_zipfile(filename)): + unzip_file( + filename, + location, + flatten=not filename.endswith('.whl') + ) + elif (content_type == 'application/x-gzip' or + tarfile.is_tarfile(filename) or + filename.lower().endswith( + TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS)): + untar_file(filename, location) + elif (content_type and content_type.startswith('text/html') and + is_svn_page(file_contents(filename))): + # We don't really care about this + from pip.vcs.subversion import Subversion + Subversion('svn+' + link.url).unpack(location) + else: + # FIXME: handle? + # FIXME: magic signatures? + logger.critical( + 'Cannot unpack file %s (downloaded from %s, content-type: %s); ' + 'cannot detect archive format', + filename, location, content_type, + ) + raise InstallationError( + 'Cannot determine archive format of %s' % location + ) + + +def call_subprocess(cmd, show_stdout=True, cwd=None, + on_returncode='raise', + command_desc=None, + extra_environ=None, spinner=None): + # This function's handling of subprocess output is confusing and I + # previously broke it terribly, so as penance I will write a long comment + # explaining things. + # + # The obvious thing that affects output is the show_stdout= + # kwarg. show_stdout=True means, let the subprocess write directly to our + # stdout. Even though it is nominally the default, it is almost never used + # inside pip (and should not be used in new code without a very good + # reason); as of 2016-02-22 it is only used in a few places inside the VCS + # wrapper code. Ideally we should get rid of it entirely, because it + # creates a lot of complexity here for a rarely used feature. + # + # Most places in pip set show_stdout=False. What this means is: + # - We connect the child stdout to a pipe, which we read. + # - By default, we hide the output but show a spinner -- unless the + # subprocess exits with an error, in which case we show the output. + # - If the --verbose option was passed (= loglevel is DEBUG), then we show + # the output unconditionally. (But in this case we don't want to show + # the output a second time if it turns out that there was an error.) + # + # stderr is always merged with stdout (even if show_stdout=True). + if show_stdout: + stdout = None + else: + stdout = subprocess.PIPE + if command_desc is None: + cmd_parts = [] + for part in cmd: + if ' ' in part or '\n' in part or '"' in part or "'" in part: + part = '"%s"' % part.replace('"', '\\"') + cmd_parts.append(part) + command_desc = ' '.join(cmd_parts) + logger.debug("Running command %s", command_desc) + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + try: + proc = subprocess.Popen( + cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout, + cwd=cwd, env=env) + except Exception as exc: + logger.critical( + "Error %s while executing command %s", exc, command_desc, + ) + raise + if stdout is not None: + all_output = [] + while True: + line = console_to_str(proc.stdout.readline()) + if not line: + break + line = line.rstrip() + all_output.append(line + '\n') + if logger.getEffectiveLevel() <= std_logging.DEBUG: + # Show the line immediately + logger.debug(line) + else: + # Update the spinner + if spinner is not None: + spinner.spin() + proc.wait() + if spinner is not None: + if proc.returncode: + spinner.finish("error") + else: + spinner.finish("done") + if proc.returncode: + if on_returncode == 'raise': + if (logger.getEffectiveLevel() > std_logging.DEBUG and + not show_stdout): + logger.info( + 'Complete output from command %s:', command_desc, + ) + logger.info( + ''.join(all_output) + + '\n----------------------------------------' + ) + raise InstallationError( + 'Command "%s" failed with error code %s in %s' + % (command_desc, proc.returncode, cwd)) + elif on_returncode == 'warn': + logger.warning( + 'Command "%s" had error code %s in %s', + command_desc, proc.returncode, cwd, + ) + elif on_returncode == 'ignore': + pass + else: + raise ValueError('Invalid value: on_returncode=%s' % + repr(on_returncode)) + if not show_stdout: + return ''.join(all_output) + + +def read_text_file(filename): + """Return the contents of *filename*. + + Try to decode the file contents with utf-8, the preferred system encoding + (e.g., cp1252 on some Windows machines), and latin1, in that order. + Decoding a byte string with latin1 will never raise an error. In the worst + case, the returned string will contain some garbage characters. + + """ + with open(filename, 'rb') as fp: + data = fp.read() + + encodings = ['utf-8', locale.getpreferredencoding(False), 'latin1'] + for enc in encodings: + try: + data = data.decode(enc) + except UnicodeDecodeError: + continue + break + + assert type(data) != bytes # Latin1 should have worked. + return data + + +def _make_build_dir(build_dir): + os.makedirs(build_dir) + write_delete_marker_file(build_dir) + + +class FakeFile(object): + """Wrap a list of lines in an object with readline() to make + ConfigParser happy.""" + def __init__(self, lines): + self._gen = (l for l in lines) + + def readline(self): + try: + try: + return next(self._gen) + except NameError: + return self._gen.next() + except StopIteration: + return '' + + def __iter__(self): + return self._gen + + +class StreamWrapper(StringIO): + + @classmethod + def from_stream(cls, orig_stream): + cls.orig_stream = orig_stream + return cls() + + # compileall.compile_dir() needs stdout.encoding to print to stdout + @property + def encoding(self): + return self.orig_stream.encoding + + +@contextlib.contextmanager +def captured_output(stream_name): + """Return a context manager used by captured_stdout/stdin/stderr + that temporarily replaces the sys stream *stream_name* with a StringIO. + + Taken from Lib/support/__init__.py in the CPython repo. + """ + orig_stdout = getattr(sys, stream_name) + setattr(sys, stream_name, StreamWrapper.from_stream(orig_stdout)) + try: + yield getattr(sys, stream_name) + finally: + setattr(sys, stream_name, orig_stdout) + + +def captured_stdout(): + """Capture the output of sys.stdout: + + with captured_stdout() as stdout: + print('hello') + self.assertEqual(stdout.getvalue(), 'hello\n') + + Taken from Lib/support/__init__.py in the CPython repo. + """ + return captured_output('stdout') + + +class cached_property(object): + """A property that is only computed once per instance and then replaces + itself with an ordinary attribute. Deleting the attribute resets the + property. + + Source: https://github.com/bottlepy/bottle/blob/0.11.5/bottle.py#L175 + """ + + def __init__(self, func): + self.__doc__ = getattr(func, '__doc__') + self.func = func + + def __get__(self, obj, cls): + if obj is None: + # We're being accessed from the class itself, not from an object + return self + value = obj.__dict__[self.func.__name__] = self.func(obj) + return value + + +def get_installed_version(dist_name, lookup_dirs=None): + """Get the installed version of dist_name avoiding pkg_resources cache""" + # Create a requirement that we'll look for inside of setuptools. + req = pkg_resources.Requirement.parse(dist_name) + + # We want to avoid having this cached, so we need to construct a new + # working set each time. + if lookup_dirs is None: + working_set = pkg_resources.WorkingSet() + else: + working_set = pkg_resources.WorkingSet(lookup_dirs) + + # Get the installed distribution from our working set + dist = working_set.find(req) + + # Check to see if we got an installed distribution or not, if we did + # we want to return it's version. + return dist.version if dist else None + + +def consume(iterator): + """Consume an iterable at C speed.""" + deque(iterator, maxlen=0) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/appdirs.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/appdirs.py new file mode 100644 index 0000000..9b82801 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/appdirs.py @@ -0,0 +1,248 @@ +""" +This code was taken from https://github.com/ActiveState/appdirs and modified +to suit our purposes. +""" +from __future__ import absolute_import + +import os +import sys + +from pip.compat import WINDOWS, expanduser +from pip._vendor.six import PY2, text_type + + +def user_cache_dir(appname): + r""" + Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + + Typical user cache directories are: + macOS: ~/Library/Caches/<AppName> + Unix: ~/.cache/<AppName> (XDG default) + Windows: C:\Users\<username>\AppData\Local\<AppName>\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go + in the `CSIDL_LOCAL_APPDATA` directory. This is identical to the + non-roaming app data dir (the default returned by `user_data_dir`). Apps + typically put cache data somewhere *under* the given dir here. Some + examples: + ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache + ...\Acme\SuperApp\Cache\1.0 + + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + """ + if WINDOWS: + # Get the base path + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + + # When using Python 2, return paths as bytes on Windows like we do on + # other operating systems. See helper function docs for more details. + if PY2 and isinstance(path, text_type): + path = _win_path_to_bytes(path) + + # Add our app name and Cache directory to it + path = os.path.join(path, appname, "Cache") + elif sys.platform == "darwin": + # Get the base path + path = expanduser("~/Library/Caches") + + # Add our app name to it + path = os.path.join(path, appname) + else: + # Get the base path + path = os.getenv("XDG_CACHE_HOME", expanduser("~/.cache")) + + # Add our app name to it + path = os.path.join(path, appname) + + return path + + +def user_data_dir(appname, roaming=False): + """ + Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user data directories are: + macOS: ~/Library/Application Support/<AppName> + Unix: ~/.local/share/<AppName> # or in + $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\<username>\ ... + ...Application Data\<AppName> + Win XP (roaming): C:\Documents and Settings\<username>\Local ... + ...Settings\Application Data\<AppName> + Win 7 (not roaming): C:\\Users\<username>\AppData\Local\<AppName> + Win 7 (roaming): C:\\Users\<username>\AppData\Roaming\<AppName> + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/<AppName>". + """ + if WINDOWS: + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.join(os.path.normpath(_get_win_folder(const)), appname) + elif sys.platform == "darwin": + path = os.path.join( + expanduser('~/Library/Application Support/'), + appname, + ) + else: + path = os.path.join( + os.getenv('XDG_DATA_HOME', expanduser("~/.local/share")), + appname, + ) + + return path + + +def user_config_dir(appname, roaming=True): + """Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "roaming" (boolean, default True) can be set False to not use the + Windows roaming appdata directory. That means that for users on a + Windows network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user data directories are: + macOS: same as user_data_dir + Unix: ~/.config/<AppName> + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/<AppName>". + """ + if WINDOWS: + path = user_data_dir(appname, roaming=roaming) + elif sys.platform == "darwin": + path = user_data_dir(appname) + else: + path = os.getenv('XDG_CONFIG_HOME', expanduser("~/.config")) + path = os.path.join(path, appname) + + return path + + +# for the discussion regarding site_config_dirs locations +# see <https://github.com/pypa/pip/issues/1733> +def site_config_dirs(appname): + """Return a list of potential user-shared config dirs for this application. + + "appname" is the name of application. + + Typical user config directories are: + macOS: /Library/Application Support/<AppName>/ + Unix: /etc or $XDG_CONFIG_DIRS[i]/<AppName>/ for each value in + $XDG_CONFIG_DIRS + Win XP: C:\Documents and Settings\All Users\Application ... + ...Data\<AppName>\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory + on Vista.) + Win 7: Hidden, but writeable on Win 7: + C:\ProgramData\<AppName>\ + """ + if WINDOWS: + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + pathlist = [os.path.join(path, appname)] + elif sys.platform == 'darwin': + pathlist = [os.path.join('/Library/Application Support', appname)] + else: + # try looking in $XDG_CONFIG_DIRS + xdg_config_dirs = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + if xdg_config_dirs: + pathlist = [ + os.path.join(expanduser(x), appname) + for x in xdg_config_dirs.split(os.pathsep) + ] + else: + pathlist = [] + + # always look in /etc directly as well + pathlist.append('/etc') + + return pathlist + + +# -- Windows support functions -- + +def _get_win_folder_from_registry(csidl_name): + """ + This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" + ) + directory, _type = _winreg.QueryValueEx(key, shell_folder_name) + return directory + + +def _get_win_folder_with_ctypes(csidl_name): + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + +if WINDOWS: + try: + import ctypes + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +def _win_path_to_bytes(path): + """Encode Windows paths to bytes. Only used on Python 2. + + Motivation is to be consistent with other operating systems where paths + are also returned as bytes. This avoids problems mixing bytes and Unicode + elsewhere in the codebase. For more details and discussion see + <https://github.com/pypa/pip/issues/3463>. + + If encoding using ASCII and MBCS fails, return the original Unicode path. + """ + for encoding in ('ASCII', 'MBCS'): + try: + return path.encode(encoding) + except (UnicodeEncodeError, LookupError): + pass + return path diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/build.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/build.py new file mode 100644 index 0000000..fc65cfa --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/build.py @@ -0,0 +1,42 @@ +from __future__ import absolute_import + +import os.path +import tempfile + +from pip.utils import rmtree + + +class BuildDirectory(object): + + def __init__(self, name=None, delete=None): + # If we were not given an explicit directory, and we were not given an + # explicit delete option, then we'll default to deleting. + if name is None and delete is None: + delete = True + + if name is None: + # We realpath here because some systems have their default tmpdir + # symlinked to another directory. This tends to confuse build + # scripts, so we canonicalize the path by traversing potential + # symlinks here. + name = os.path.realpath(tempfile.mkdtemp(prefix="pip-build-")) + # If we were not given an explicit directory, and we were not given + # an explicit delete option, then we'll default to deleting. + if delete is None: + delete = True + + self.name = name + self.delete = delete + + def __repr__(self): + return "<{} {!r}>".format(self.__class__.__name__, self.name) + + def __enter__(self): + return self.name + + def __exit__(self, exc, value, tb): + self.cleanup() + + def cleanup(self): + if self.delete: + rmtree(self.name) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/deprecation.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/deprecation.py new file mode 100644 index 0000000..c3f799e --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/deprecation.py @@ -0,0 +1,76 @@ +""" +A module that implements tooling to enable easy warnings about deprecations. +""" +from __future__ import absolute_import + +import logging +import warnings + + +class PipDeprecationWarning(Warning): + pass + + +class Pending(object): + pass + + +class RemovedInPip10Warning(PipDeprecationWarning): + pass + + +class RemovedInPip11Warning(PipDeprecationWarning, Pending): + pass + + +class Python26DeprecationWarning(PipDeprecationWarning): + pass + + +# Warnings <-> Logging Integration + + +_warnings_showwarning = None + + +def _showwarning(message, category, filename, lineno, file=None, line=None): + if file is not None: + if _warnings_showwarning is not None: + _warnings_showwarning( + message, category, filename, lineno, file, line, + ) + else: + if issubclass(category, PipDeprecationWarning): + # We use a specially named logger which will handle all of the + # deprecation messages for pip. + logger = logging.getLogger("pip.deprecations") + + # This is purposely using the % formatter here instead of letting + # the logging module handle the interpolation. This is because we + # want it to appear as if someone typed this entire message out. + log_message = "DEPRECATION: %s" % message + + # PipDeprecationWarnings that are Pending still have at least 2 + # versions to go until they are removed so they can just be + # warnings. Otherwise, they will be removed in the very next + # version of pip. We want these to be more obvious so we use the + # ERROR logging level. + if issubclass(category, Pending): + logger.warning(log_message) + else: + logger.error(log_message) + else: + _warnings_showwarning( + message, category, filename, lineno, file, line, + ) + + +def install_warning_logger(): + # Enable our Deprecation Warnings + warnings.simplefilter("default", PipDeprecationWarning, append=True) + + global _warnings_showwarning + + if _warnings_showwarning is None: + _warnings_showwarning = warnings.showwarning + warnings.showwarning = _showwarning diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/encoding.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/encoding.py new file mode 100644 index 0000000..2483168 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/encoding.py @@ -0,0 +1,31 @@ +import codecs +import locale +import re + + +BOMS = [ + (codecs.BOM_UTF8, 'utf8'), + (codecs.BOM_UTF16, 'utf16'), + (codecs.BOM_UTF16_BE, 'utf16-be'), + (codecs.BOM_UTF16_LE, 'utf16-le'), + (codecs.BOM_UTF32, 'utf32'), + (codecs.BOM_UTF32_BE, 'utf32-be'), + (codecs.BOM_UTF32_LE, 'utf32-le'), +] + +ENCODING_RE = re.compile(b'coding[:=]\s*([-\w.]+)') + + +def auto_decode(data): + """Check a bytes string for a BOM to correctly detect the encoding + + Fallback to locale.getpreferredencoding(False) like open() on Python3""" + for bom, encoding in BOMS: + if data.startswith(bom): + return data[len(bom):].decode(encoding) + # Lets check the first two lines as in PEP263 + for line in data.split(b'\n')[:2]: + if line[0:1] == b'#' and ENCODING_RE.search(line): + encoding = ENCODING_RE.search(line).groups()[0].decode('ascii') + return data.decode(encoding) + return data.decode(locale.getpreferredencoding(False)) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/filesystem.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/filesystem.py new file mode 100644 index 0000000..25ad516 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/filesystem.py @@ -0,0 +1,28 @@ +import os +import os.path + +from pip.compat import get_path_uid + + +def check_path_owner(path): + # If we don't have a way to check the effective uid of this process, then + # we'll just assume that we own the directory. + if not hasattr(os, "geteuid"): + return True + + previous = None + while path != previous: + if os.path.lexists(path): + # Check if path is writable by current user. + if os.geteuid() == 0: + # Special handling for root user in order to handle properly + # cases where users use sudo without -H flag. + try: + path_uid = get_path_uid(path) + except OSError: + return False + return path_uid == 0 + else: + return os.access(path, os.W_OK) + else: + previous, path = path, os.path.dirname(path) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/glibc.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/glibc.py new file mode 100644 index 0000000..7847885 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/glibc.py @@ -0,0 +1,81 @@ +from __future__ import absolute_import + +import re +import ctypes +import platform +import warnings + + +def glibc_version_string(): + "Returns glibc version string, or None if not using glibc." + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + process_namespace = ctypes.CDLL(None) + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +# Separated out from have_compatible_glibc for easier unit testing +def check_glibc_version(version_str, required_major, minimum_minor): + # Parse string and check against requested version. + # + # We use a regexp instead of str.split because we want to discard any + # random junk that might come after the minor version -- this might happen + # in patched/forked versions of glibc (e.g. Linaro's version of glibc + # uses version strings like "2.20-2014.11"). See gh-3588. + m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str) + if not m: + warnings.warn("Expected glibc version with 2 components major.minor," + " got: %s" % version_str, RuntimeWarning) + return False + return (int(m.group("major")) == required_major and + int(m.group("minor")) >= minimum_minor) + + +def have_compatible_glibc(required_major, minimum_minor): + version_str = glibc_version_string() + if version_str is None: + return False + return check_glibc_version(version_str, required_major, minimum_minor) + + +# platform.libc_ver regularly returns completely nonsensical glibc +# versions. E.g. on my computer, platform says: +# +# ~$ python2.7 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.7') +# ~$ python3.5 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.9') +# +# But the truth is: +# +# ~$ ldd --version +# ldd (Debian GLIBC 2.22-11) 2.22 +# +# This is unfortunate, because it means that the linehaul data on libc +# versions that was generated by pip 8.1.2 and earlier is useless and +# misleading. Solution: instead of using platform, use our code that actually +# works. +def libc_ver(): + glibc_version = glibc_version_string() + if glibc_version is None: + # For non-glibc platforms, fall back on platform.libc_ver + return platform.libc_ver() + else: + return ("glibc", glibc_version) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/hashes.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/hashes.py new file mode 100644 index 0000000..9602970 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/hashes.py @@ -0,0 +1,92 @@ +from __future__ import absolute_import + +import hashlib + +from pip.exceptions import HashMismatch, HashMissing, InstallationError +from pip.utils import read_chunks +from pip._vendor.six import iteritems, iterkeys, itervalues + + +# The recommended hash algo of the moment. Change this whenever the state of +# the art changes; it won't hurt backward compatibility. +FAVORITE_HASH = 'sha256' + + +# Names of hashlib algorithms allowed by the --hash option and ``pip hash`` +# Currently, those are the ones at least as collision-resistant as sha256. +STRONG_HASHES = ['sha256', 'sha384', 'sha512'] + + +class Hashes(object): + """A wrapper that builds multiple hashes at once and checks them against + known-good values + + """ + def __init__(self, hashes=None): + """ + :param hashes: A dict of algorithm names pointing to lists of allowed + hex digests + """ + self._allowed = {} if hashes is None else hashes + + def check_against_chunks(self, chunks): + """Check good hashes against ones built from iterable of chunks of + data. + + Raise HashMismatch if none match. + + """ + gots = {} + for hash_name in iterkeys(self._allowed): + try: + gots[hash_name] = hashlib.new(hash_name) + except (ValueError, TypeError): + raise InstallationError('Unknown hash name: %s' % hash_name) + + for chunk in chunks: + for hash in itervalues(gots): + hash.update(chunk) + + for hash_name, got in iteritems(gots): + if got.hexdigest() in self._allowed[hash_name]: + return + self._raise(gots) + + def _raise(self, gots): + raise HashMismatch(self._allowed, gots) + + def check_against_file(self, file): + """Check good hashes against a file-like object + + Raise HashMismatch if none match. + + """ + return self.check_against_chunks(read_chunks(file)) + + def check_against_path(self, path): + with open(path, 'rb') as file: + return self.check_against_file(file) + + def __nonzero__(self): + """Return whether I know any known-good hashes.""" + return bool(self._allowed) + + def __bool__(self): + return self.__nonzero__() + + +class MissingHashes(Hashes): + """A workalike for Hashes used when we're missing a hash for a requirement + + It computes the actual hash of the requirement and raises a HashMissing + exception showing it to the user. + + """ + def __init__(self): + """Don't offer the ``hashes`` kwarg.""" + # Pass our favorite hash in to generate a "gotten hash". With the + # empty list, it will never match, so an error will always raise. + super(MissingHashes, self).__init__(hashes={FAVORITE_HASH: []}) + + def _raise(self, gots): + raise HashMissing(gots[FAVORITE_HASH].hexdigest()) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/logging.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/logging.py new file mode 100644 index 0000000..1c1053a --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/logging.py @@ -0,0 +1,130 @@ +from __future__ import absolute_import + +import contextlib +import logging +import logging.handlers +import os + +try: + import threading +except ImportError: + import dummy_threading as threading + +from pip.compat import WINDOWS +from pip.utils import ensure_dir + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + + +_log_state = threading.local() +_log_state.indentation = 0 + + +@contextlib.contextmanager +def indent_log(num=2): + """ + A context manager which will cause the log output to be indented for any + log messages emitted inside it. + """ + _log_state.indentation += num + try: + yield + finally: + _log_state.indentation -= num + + +def get_indentation(): + return getattr(_log_state, 'indentation', 0) + + +class IndentingFormatter(logging.Formatter): + + def format(self, record): + """ + Calls the standard formatter, but will indent all of the log messages + by our current indentation level. + """ + formatted = logging.Formatter.format(self, record) + formatted = "".join([ + (" " * get_indentation()) + line + for line in formatted.splitlines(True) + ]) + return formatted + + +def _color_wrap(*colors): + def wrapped(inp): + return "".join(list(colors) + [inp, colorama.Style.RESET_ALL]) + return wrapped + + +class ColorizedStreamHandler(logging.StreamHandler): + + # Don't build up a list of colors if we don't have colorama + if colorama: + COLORS = [ + # This needs to be in order from highest logging level to lowest. + (logging.ERROR, _color_wrap(colorama.Fore.RED)), + (logging.WARNING, _color_wrap(colorama.Fore.YELLOW)), + ] + else: + COLORS = [] + + def __init__(self, stream=None): + logging.StreamHandler.__init__(self, stream) + + if WINDOWS and colorama: + self.stream = colorama.AnsiToWin32(self.stream) + + def should_color(self): + # Don't colorize things if we do not have colorama + if not colorama: + return False + + real_stream = ( + self.stream if not isinstance(self.stream, colorama.AnsiToWin32) + else self.stream.wrapped + ) + + # If the stream is a tty we should color it + if hasattr(real_stream, "isatty") and real_stream.isatty(): + return True + + # If we have an ASNI term we should color it + if os.environ.get("TERM") == "ANSI": + return True + + # If anything else we should not color it + return False + + def format(self, record): + msg = logging.StreamHandler.format(self, record) + + if self.should_color(): + for level, color in self.COLORS: + if record.levelno >= level: + msg = color(msg) + break + + return msg + + +class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler): + + def _open(self): + ensure_dir(os.path.dirname(self.baseFilename)) + return logging.handlers.RotatingFileHandler._open(self) + + +class MaxLevelFilter(logging.Filter): + + def __init__(self, level): + self.level = level + + def filter(self, record): + return record.levelno < self.level diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/outdated.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/outdated.py new file mode 100644 index 0000000..2164cc3 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/outdated.py @@ -0,0 +1,162 @@ +from __future__ import absolute_import + +import datetime +import json +import logging +import os.path +import sys + +from pip._vendor import lockfile +from pip._vendor.packaging import version as packaging_version + +from pip.compat import total_seconds, WINDOWS +from pip.models import PyPI +from pip.locations import USER_CACHE_DIR, running_under_virtualenv +from pip.utils import ensure_dir, get_installed_version +from pip.utils.filesystem import check_path_owner + + +SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" + + +logger = logging.getLogger(__name__) + + +class VirtualenvSelfCheckState(object): + def __init__(self): + self.statefile_path = os.path.join(sys.prefix, "pip-selfcheck.json") + + # Load the existing state + try: + with open(self.statefile_path) as statefile: + self.state = json.load(statefile) + except (IOError, ValueError): + self.state = {} + + def save(self, pypi_version, current_time): + # Attempt to write out our version check file + with open(self.statefile_path, "w") as statefile: + json.dump( + { + "last_check": current_time.strftime(SELFCHECK_DATE_FMT), + "pypi_version": pypi_version, + }, + statefile, + sort_keys=True, + separators=(",", ":") + ) + + +class GlobalSelfCheckState(object): + def __init__(self): + self.statefile_path = os.path.join(USER_CACHE_DIR, "selfcheck.json") + + # Load the existing state + try: + with open(self.statefile_path) as statefile: + self.state = json.load(statefile)[sys.prefix] + except (IOError, ValueError, KeyError): + self.state = {} + + def save(self, pypi_version, current_time): + # Check to make sure that we own the directory + if not check_path_owner(os.path.dirname(self.statefile_path)): + return + + # Now that we've ensured the directory is owned by this user, we'll go + # ahead and make sure that all our directories are created. + ensure_dir(os.path.dirname(self.statefile_path)) + + # Attempt to write out our version check file + with lockfile.LockFile(self.statefile_path): + if os.path.exists(self.statefile_path): + with open(self.statefile_path) as statefile: + state = json.load(statefile) + else: + state = {} + + state[sys.prefix] = { + "last_check": current_time.strftime(SELFCHECK_DATE_FMT), + "pypi_version": pypi_version, + } + + with open(self.statefile_path, "w") as statefile: + json.dump(state, statefile, sort_keys=True, + separators=(",", ":")) + + +def load_selfcheck_statefile(): + if running_under_virtualenv(): + return VirtualenvSelfCheckState() + else: + return GlobalSelfCheckState() + + +def pip_version_check(session): + """Check for an update for pip. + + Limit the frequency of checks to once per week. State is stored either in + the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix + of the pip script path. + """ + installed_version = get_installed_version("pip") + if installed_version is None: + return + + pip_version = packaging_version.parse(installed_version) + pypi_version = None + + try: + state = load_selfcheck_statefile() + + current_time = datetime.datetime.utcnow() + # Determine if we need to refresh the state + if "last_check" in state.state and "pypi_version" in state.state: + last_check = datetime.datetime.strptime( + state.state["last_check"], + SELFCHECK_DATE_FMT + ) + if total_seconds(current_time - last_check) < 7 * 24 * 60 * 60: + pypi_version = state.state["pypi_version"] + + # Refresh the version if we need to or just see if we need to warn + if pypi_version is None: + resp = session.get( + PyPI.pip_json_url, + headers={"Accept": "application/json"}, + ) + resp.raise_for_status() + pypi_version = [ + v for v in sorted( + list(resp.json()["releases"]), + key=packaging_version.parse, + ) + if not packaging_version.parse(v).is_prerelease + ][-1] + + # save that we've performed a check + state.save(pypi_version, current_time) + + remote_version = packaging_version.parse(pypi_version) + + # Determine if our pypi_version is older + if (pip_version < remote_version and + pip_version.base_version != remote_version.base_version): + # Advise "python -m pip" on Windows to avoid issues + # with overwriting pip.exe. + if WINDOWS: + pip_cmd = "python -m pip" + else: + pip_cmd = "pip" + logger.warning( + "You are using pip version %s, however version %s is " + "available.\nYou should consider upgrading via the " + "'%s install --upgrade pip' command.", + pip_version, pypi_version, pip_cmd + ) + + except Exception: + logger.debug( + "There was an error checking the latest version of pip", + exc_info=True, + ) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/packaging.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/packaging.py new file mode 100644 index 0000000..e93b20d --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/packaging.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import + +from email.parser import FeedParser + +import logging +import sys + +from pip._vendor.packaging import specifiers +from pip._vendor.packaging import version +from pip._vendor import pkg_resources + +from pip import exceptions + +logger = logging.getLogger(__name__) + + +def check_requires_python(requires_python): + """ + Check if the python version in use match the `requires_python` specifier. + + Returns `True` if the version of python in use matches the requirement. + Returns `False` if the version of python in use does not matches the + requirement. + + Raises an InvalidSpecifier if `requires_python` have an invalid format. + """ + if requires_python is None: + # The package provides no information + return True + requires_python_specifier = specifiers.SpecifierSet(requires_python) + + # We only use major.minor.micro + python_version = version.parse('.'.join(map(str, sys.version_info[:3]))) + return python_version in requires_python_specifier + + +def get_metadata(dist): + if (isinstance(dist, pkg_resources.DistInfoDistribution) and + dist.has_metadata('METADATA')): + return dist.get_metadata('METADATA') + elif dist.has_metadata('PKG-INFO'): + return dist.get_metadata('PKG-INFO') + + +def check_dist_requires_python(dist): + metadata = get_metadata(dist) + feed_parser = FeedParser() + feed_parser.feed(metadata) + pkg_info_dict = feed_parser.close() + requires_python = pkg_info_dict.get('Requires-Python') + try: + if not check_requires_python(requires_python): + raise exceptions.UnsupportedPythonVersion( + "%s requires Python '%s' but the running Python is %s" % ( + dist.project_name, + requires_python, + '.'.join(map(str, sys.version_info[:3])),) + ) + except specifiers.InvalidSpecifier as e: + logger.warning( + "Package %s has an invalid Requires-Python entry %s - %s" % ( + dist.project_name, requires_python, e)) + return diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/setuptools_build.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/setuptools_build.py new file mode 100644 index 0000000..03973e9 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/setuptools_build.py @@ -0,0 +1,8 @@ +# Shim to wrap setup.py invocation with setuptools +SETUPTOOLS_SHIM = ( + "import setuptools, tokenize;__file__=%r;" + "f=getattr(tokenize, 'open', open)(__file__);" + "code=f.read().replace('\\r\\n', '\\n');" + "f.close();" + "exec(compile(code, __file__, 'exec'))" +) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/ui.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/ui.py new file mode 100644 index 0000000..bba73e3 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/utils/ui.py @@ -0,0 +1,344 @@ +from __future__ import absolute_import +from __future__ import division + +import itertools +import sys +from signal import signal, SIGINT, default_int_handler +import time +import contextlib +import logging + +from pip.compat import WINDOWS +from pip.utils import format_size +from pip.utils.logging import get_indentation +from pip._vendor import six +from pip._vendor.progress.bar import Bar, IncrementalBar +from pip._vendor.progress.helpers import (WritelnMixin, + HIDE_CURSOR, SHOW_CURSOR) +from pip._vendor.progress.spinner import Spinner + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + +logger = logging.getLogger(__name__) + + +def _select_progress_class(preferred, fallback): + encoding = getattr(preferred.file, "encoding", None) + + # If we don't know what encoding this file is in, then we'll just assume + # that it doesn't support unicode and use the ASCII bar. + if not encoding: + return fallback + + # Collect all of the possible characters we want to use with the preferred + # bar. + characters = [ + getattr(preferred, "empty_fill", six.text_type()), + getattr(preferred, "fill", six.text_type()), + ] + characters += list(getattr(preferred, "phases", [])) + + # Try to decode the characters we're using for the bar using the encoding + # of the given file, if this works then we'll assume that we can use the + # fancier bar and if not we'll fall back to the plaintext bar. + try: + six.text_type().join(characters).encode(encoding) + except UnicodeEncodeError: + return fallback + else: + return preferred + + +_BaseBar = _select_progress_class(IncrementalBar, Bar) + + +class InterruptibleMixin(object): + """ + Helper to ensure that self.finish() gets called on keyboard interrupt. + + This allows downloads to be interrupted without leaving temporary state + (like hidden cursors) behind. + + This class is similar to the progress library's existing SigIntMixin + helper, but as of version 1.2, that helper has the following problems: + + 1. It calls sys.exit(). + 2. It discards the existing SIGINT handler completely. + 3. It leaves its own handler in place even after an uninterrupted finish, + which will have unexpected delayed effects if the user triggers an + unrelated keyboard interrupt some time after a progress-displaying + download has already completed, for example. + """ + + def __init__(self, *args, **kwargs): + """ + Save the original SIGINT handler for later. + """ + super(InterruptibleMixin, self).__init__(*args, **kwargs) + + self.original_handler = signal(SIGINT, self.handle_sigint) + + # If signal() returns None, the previous handler was not installed from + # Python, and we cannot restore it. This probably should not happen, + # but if it does, we must restore something sensible instead, at least. + # The least bad option should be Python's default SIGINT handler, which + # just raises KeyboardInterrupt. + if self.original_handler is None: + self.original_handler = default_int_handler + + def finish(self): + """ + Restore the original SIGINT handler after finishing. + + This should happen regardless of whether the progress display finishes + normally, or gets interrupted. + """ + super(InterruptibleMixin, self).finish() + signal(SIGINT, self.original_handler) + + def handle_sigint(self, signum, frame): + """ + Call self.finish() before delegating to the original SIGINT handler. + + This handler should only be in place while the progress display is + active. + """ + self.finish() + self.original_handler(signum, frame) + + +class DownloadProgressMixin(object): + + def __init__(self, *args, **kwargs): + super(DownloadProgressMixin, self).__init__(*args, **kwargs) + self.message = (" " * (get_indentation() + 2)) + self.message + + @property + def downloaded(self): + return format_size(self.index) + + @property + def download_speed(self): + # Avoid zero division errors... + if self.avg == 0.0: + return "..." + return format_size(1 / self.avg) + "/s" + + @property + def pretty_eta(self): + if self.eta: + return "eta %s" % self.eta_td + return "" + + def iter(self, it, n=1): + for x in it: + yield x + self.next(n) + self.finish() + + +class WindowsMixin(object): + + def __init__(self, *args, **kwargs): + # The Windows terminal does not support the hide/show cursor ANSI codes + # even with colorama. So we'll ensure that hide_cursor is False on + # Windows. + # This call neds to go before the super() call, so that hide_cursor + # is set in time. The base progress bar class writes the "hide cursor" + # code to the terminal in its init, so if we don't set this soon + # enough, we get a "hide" with no corresponding "show"... + if WINDOWS and self.hide_cursor: + self.hide_cursor = False + + super(WindowsMixin, self).__init__(*args, **kwargs) + + # Check if we are running on Windows and we have the colorama module, + # if we do then wrap our file with it. + if WINDOWS and colorama: + self.file = colorama.AnsiToWin32(self.file) + # The progress code expects to be able to call self.file.isatty() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.isatty = lambda: self.file.wrapped.isatty() + # The progress code expects to be able to call self.file.flush() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.flush = lambda: self.file.wrapped.flush() + + +class DownloadProgressBar(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin, _BaseBar): + + file = sys.stdout + message = "%(percent)d%%" + suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" + + +class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin, WritelnMixin, Spinner): + + file = sys.stdout + suffix = "%(downloaded)s %(download_speed)s" + + def next_phase(self): + if not hasattr(self, "_phaser"): + self._phaser = itertools.cycle(self.phases) + return next(self._phaser) + + def update(self): + message = self.message % self + phase = self.next_phase() + suffix = self.suffix % self + line = ''.join([ + message, + " " if message else "", + phase, + " " if suffix else "", + suffix, + ]) + + self.writeln(line) + + +################################################################ +# Generic "something is happening" spinners +# +# We don't even try using progress.spinner.Spinner here because it's actually +# simpler to reimplement from scratch than to coerce their code into doing +# what we need. +################################################################ + +@contextlib.contextmanager +def hidden_cursor(file): + # The Windows terminal does not support the hide/show cursor ANSI codes, + # even via colorama. So don't even try. + if WINDOWS: + yield + # We don't want to clutter the output with control characters if we're + # writing to a file, or if the user is running with --quiet. + # See https://github.com/pypa/pip/issues/3418 + elif not file.isatty() or logger.getEffectiveLevel() > logging.INFO: + yield + else: + file.write(HIDE_CURSOR) + try: + yield + finally: + file.write(SHOW_CURSOR) + + +class RateLimiter(object): + def __init__(self, min_update_interval_seconds): + self._min_update_interval_seconds = min_update_interval_seconds + self._last_update = 0 + + def ready(self): + now = time.time() + delta = now - self._last_update + return delta >= self._min_update_interval_seconds + + def reset(self): + self._last_update = time.time() + + +class InteractiveSpinner(object): + def __init__(self, message, file=None, spin_chars="-\\|/", + # Empirically, 8 updates/second looks nice + min_update_interval_seconds=0.125): + self._message = message + if file is None: + file = sys.stdout + self._file = file + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._finished = False + + self._spin_cycle = itertools.cycle(spin_chars) + + self._file.write(" " * get_indentation() + self._message + " ... ") + self._width = 0 + + def _write(self, status): + assert not self._finished + # Erase what we wrote before by backspacing to the beginning, writing + # spaces to overwrite the old text, and then backspacing again + backup = "\b" * self._width + self._file.write(backup + " " * self._width + backup) + # Now we have a blank slate to add our status + self._file.write(status) + self._width = len(status) + self._file.flush() + self._rate_limiter.reset() + + def spin(self): + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._write(next(self._spin_cycle)) + + def finish(self, final_status): + if self._finished: + return + self._write(final_status) + self._file.write("\n") + self._file.flush() + self._finished = True + + +# Used for dumb terminals, non-interactive installs (no tty), etc. +# We still print updates occasionally (once every 60 seconds by default) to +# act as a keep-alive for systems like Travis-CI that take lack-of-output as +# an indication that a task has frozen. +class NonInteractiveSpinner(object): + def __init__(self, message, min_update_interval_seconds=60): + self._message = message + self._finished = False + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._update("started") + + def _update(self, status): + assert not self._finished + self._rate_limiter.reset() + logger.info("%s: %s", self._message, status) + + def spin(self): + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._update("still running...") + + def finish(self, final_status): + if self._finished: + return + self._update("finished with status '%s'" % (final_status,)) + self._finished = True + + +@contextlib.contextmanager +def open_spinner(message): + # Interactive spinner goes directly to sys.stdout rather than being routed + # through the logging system, but it acts like it has level INFO, + # i.e. it's only displayed if we're at level INFO or better. + # Non-interactive spinner goes through the logging system, so it is always + # in sync with logging configuration. + if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO: + spinner = InteractiveSpinner(message) + else: + spinner = NonInteractiveSpinner(message) + try: + with hidden_cursor(sys.stdout): + yield spinner + except KeyboardInterrupt: + spinner.finish("canceled") + raise + except Exception: + spinner.finish("error") + raise + else: + spinner.finish("done") diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/__init__.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/__init__.py new file mode 100644 index 0000000..8d3dbb2 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/__init__.py @@ -0,0 +1,366 @@ +"""Handles all VCS (version control) support""" +from __future__ import absolute_import + +import errno +import logging +import os +import shutil +import sys + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip.exceptions import BadCommand +from pip.utils import (display_path, backup_dir, call_subprocess, + rmtree, ask_path_exists) + + +__all__ = ['vcs', 'get_src_requirement'] + + +logger = logging.getLogger(__name__) + + +class VcsSupport(object): + _registry = {} + schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn'] + + def __init__(self): + # Register more schemes with urlparse for various version control + # systems + urllib_parse.uses_netloc.extend(self.schemes) + # Python >= 2.7.4, 3.3 doesn't have uses_fragment + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(self.schemes) + super(VcsSupport, self).__init__() + + def __iter__(self): + return self._registry.__iter__() + + @property + def backends(self): + return list(self._registry.values()) + + @property + def dirnames(self): + return [backend.dirname for backend in self.backends] + + @property + def all_schemes(self): + schemes = [] + for backend in self.backends: + schemes.extend(backend.schemes) + return schemes + + def register(self, cls): + if not hasattr(cls, 'name'): + logger.warning('Cannot register VCS %s', cls.__name__) + return + if cls.name not in self._registry: + self._registry[cls.name] = cls + logger.debug('Registered VCS backend: %s', cls.name) + + def unregister(self, cls=None, name=None): + if name in self._registry: + del self._registry[name] + elif cls in self._registry.values(): + del self._registry[cls.name] + else: + logger.warning('Cannot unregister because no class or name given') + + def get_backend_name(self, location): + """ + Return the name of the version control backend if found at given + location, e.g. vcs.get_backend_name('/path/to/vcs/checkout') + """ + for vc_type in self._registry.values(): + if vc_type.controls_location(location): + logger.debug('Determine that %s uses VCS: %s', + location, vc_type.name) + return vc_type.name + return None + + def get_backend(self, name): + name = name.lower() + if name in self._registry: + return self._registry[name] + + def get_backend_from_location(self, location): + vc_type = self.get_backend_name(location) + if vc_type: + return self.get_backend(vc_type) + return None + + +vcs = VcsSupport() + + +class VersionControl(object): + name = '' + dirname = '' + # List of supported schemes for this Version Control + schemes = () + + def __init__(self, url=None, *args, **kwargs): + self.url = url + super(VersionControl, self).__init__(*args, **kwargs) + + def _is_local_repository(self, repo): + """ + posix absolute paths start with os.path.sep, + win32 ones start with drive (like c:\\folder) + """ + drive, tail = os.path.splitdrive(repo) + return repo.startswith(os.path.sep) or drive + + # See issue #1083 for why this method was introduced: + # https://github.com/pypa/pip/issues/1083 + def translate_egg_surname(self, surname): + # For example, Django has branches of the form "stable/1.7.x". + return surname.replace('/', '_') + + def export(self, location): + """ + Export the repository at the url to the destination location + i.e. only download the files, without vcs informations + """ + raise NotImplementedError + + def get_url_rev(self): + """ + Returns the correct repository URL and revision by parsing the given + repository URL + """ + error_message = ( + "Sorry, '%s' is a malformed VCS url. " + "The format is <vcs>+<protocol>://<url>, " + "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp" + ) + assert '+' in self.url, error_message % self.url + url = self.url.split('+', 1)[1] + scheme, netloc, path, query, frag = urllib_parse.urlsplit(url) + rev = None + if '@' in path: + path, rev = path.rsplit('@', 1) + url = urllib_parse.urlunsplit((scheme, netloc, path, query, '')) + return url, rev + + def get_info(self, location): + """ + Returns (url, revision), where both are strings + """ + assert not location.rstrip('/').endswith(self.dirname), \ + 'Bad directory: %s' % location + return self.get_url(location), self.get_revision(location) + + def normalize_url(self, url): + """ + Normalize a URL for comparison by unquoting it and removing any + trailing slash. + """ + return urllib_parse.unquote(url).rstrip('/') + + def compare_urls(self, url1, url2): + """ + Compare two repo URLs for identity, ignoring incidental differences. + """ + return (self.normalize_url(url1) == self.normalize_url(url2)) + + def obtain(self, dest): + """ + Called when installing or updating an editable package, takes the + source path of the checkout. + """ + raise NotImplementedError + + def switch(self, dest, url, rev_options): + """ + Switch the repo at ``dest`` to point to ``URL``. + """ + raise NotImplementedError + + def update(self, dest, rev_options): + """ + Update an already-existing repo to the given ``rev_options``. + """ + raise NotImplementedError + + def check_version(self, dest, rev_options): + """ + Return True if the version is identical to what exists and + doesn't need to be updated. + """ + raise NotImplementedError + + def check_destination(self, dest, url, rev_options, rev_display): + """ + Prepare a location to receive a checkout/clone. + + Return True if the location is ready for (and requires) a + checkout/clone, False otherwise. + """ + checkout = True + prompt = False + if os.path.exists(dest): + checkout = False + if os.path.exists(os.path.join(dest, self.dirname)): + existing_url = self.get_url(dest) + if self.compare_urls(existing_url, url): + logger.debug( + '%s in %s exists, and has correct URL (%s)', + self.repo_name.title(), + display_path(dest), + url, + ) + if not self.check_version(dest, rev_options): + logger.info( + 'Updating %s %s%s', + display_path(dest), + self.repo_name, + rev_display, + ) + self.update(dest, rev_options) + else: + logger.info( + 'Skipping because already up-to-date.') + else: + logger.warning( + '%s %s in %s exists with URL %s', + self.name, + self.repo_name, + display_path(dest), + existing_url, + ) + prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', + ('s', 'i', 'w', 'b')) + else: + logger.warning( + 'Directory %s already exists, and is not a %s %s.', + dest, + self.name, + self.repo_name, + ) + prompt = ('(i)gnore, (w)ipe, (b)ackup ', ('i', 'w', 'b')) + if prompt: + logger.warning( + 'The plan is to install the %s repository %s', + self.name, + url, + ) + response = ask_path_exists('What to do? %s' % prompt[0], + prompt[1]) + + if response == 's': + logger.info( + 'Switching %s %s to %s%s', + self.repo_name, + display_path(dest), + url, + rev_display, + ) + self.switch(dest, url, rev_options) + elif response == 'i': + # do nothing + pass + elif response == 'w': + logger.warning('Deleting %s', display_path(dest)) + rmtree(dest) + checkout = True + elif response == 'b': + dest_dir = backup_dir(dest) + logger.warning( + 'Backing up %s to %s', display_path(dest), dest_dir, + ) + shutil.move(dest, dest_dir) + checkout = True + elif response == 'a': + sys.exit(-1) + return checkout + + def unpack(self, location): + """ + Clean up current location and download the url repository + (and vcs infos) into location + """ + if os.path.exists(location): + rmtree(location) + self.obtain(location) + + def get_src_requirement(self, dist, location): + """ + Return a string representing the requirement needed to + redownload the files currently present in location, something + like: + {repository_url}@{revision}#egg={project_name}-{version_identifier} + """ + raise NotImplementedError + + def get_url(self, location): + """ + Return the url used at location + Used in get_info or check_destination + """ + raise NotImplementedError + + def get_revision(self, location): + """ + Return the current revision of the files at location + Used in get_info + """ + raise NotImplementedError + + def run_command(self, cmd, show_stdout=True, cwd=None, + on_returncode='raise', + command_desc=None, + extra_environ=None, spinner=None): + """ + Run a VCS subcommand + This is simply a wrapper around call_subprocess that adds the VCS + command name, and checks that the VCS is available + """ + cmd = [self.name] + cmd + try: + return call_subprocess(cmd, show_stdout, cwd, + on_returncode, + command_desc, extra_environ, + spinner) + except OSError as e: + # errno.ENOENT = no such file or directory + # In other words, the VCS executable isn't available + if e.errno == errno.ENOENT: + raise BadCommand('Cannot find command %r' % self.name) + else: + raise # re-raise exception if a different error occurred + + @classmethod + def controls_location(cls, location): + """ + Check if a location is controlled by the vcs. + It is meant to be overridden to implement smarter detection + mechanisms for specific vcs. + """ + logger.debug('Checking in %s for %s (%s)...', + location, cls.dirname, cls.name) + path = os.path.join(location, cls.dirname) + return os.path.exists(path) + + +def get_src_requirement(dist, location): + version_control = vcs.get_backend_from_location(location) + if version_control: + try: + return version_control().get_src_requirement(dist, + location) + except BadCommand: + logger.warning( + 'cannot determine version of editable source in %s ' + '(%s command not found in path)', + location, + version_control.name, + ) + return dist.as_requirement() + logger.warning( + 'cannot determine version of editable source in %s (is not SVN ' + 'checkout, Git clone, Mercurial clone or Bazaar branch)', + location, + ) + return dist.as_requirement() diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/bazaar.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/bazaar.py new file mode 100644 index 0000000..0f09584 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/bazaar.py @@ -0,0 +1,116 @@ +from __future__ import absolute_import + +import logging +import os +import tempfile + +# TODO: Get this into six.moves.urllib.parse +try: + from urllib import parse as urllib_parse +except ImportError: + import urlparse as urllib_parse + +from pip.utils import rmtree, display_path +from pip.vcs import vcs, VersionControl +from pip.download import path_to_url + + +logger = logging.getLogger(__name__) + + +class Bazaar(VersionControl): + name = 'bzr' + dirname = '.bzr' + repo_name = 'branch' + schemes = ( + 'bzr', 'bzr+http', 'bzr+https', 'bzr+ssh', 'bzr+sftp', 'bzr+ftp', + 'bzr+lp', + ) + + def __init__(self, url=None, *args, **kwargs): + super(Bazaar, self).__init__(url, *args, **kwargs) + # Python >= 2.7.4, 3.3 doesn't have uses_fragment or non_hierarchical + # Register lp but do not expose as a scheme to support bzr+lp. + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(['lp']) + urllib_parse.non_hierarchical.extend(['lp']) + + def export(self, location): + """ + Export the Bazaar repository at the url to the destination location + """ + temp_dir = tempfile.mkdtemp('-export', 'pip-') + self.unpack(temp_dir) + if os.path.exists(location): + # Remove the location to make sure Bazaar can export it correctly + rmtree(location) + try: + self.run_command(['export', location], cwd=temp_dir, + show_stdout=False) + finally: + rmtree(temp_dir) + + def switch(self, dest, url, rev_options): + self.run_command(['switch', url], cwd=dest) + + def update(self, dest, rev_options): + self.run_command(['pull', '-q'] + rev_options, cwd=dest) + + def obtain(self, dest): + url, rev = self.get_url_rev() + if rev: + rev_options = ['-r', rev] + rev_display = ' (to revision %s)' % rev + else: + rev_options = [] + rev_display = '' + if self.check_destination(dest, url, rev_options, rev_display): + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(['branch', '-q'] + rev_options + [url, dest]) + + def get_url_rev(self): + # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it + url, rev = super(Bazaar, self).get_url_rev() + if url.startswith('ssh://'): + url = 'bzr+' + url + return url, rev + + def get_url(self, location): + urls = self.run_command(['info'], show_stdout=False, cwd=location) + for line in urls.splitlines(): + line = line.strip() + for x in ('checkout of branch: ', + 'parent branch: '): + if line.startswith(x): + repo = line.split(x)[1] + if self._is_local_repository(repo): + return path_to_url(repo) + return repo + return None + + def get_revision(self, location): + revision = self.run_command( + ['revno'], show_stdout=False, cwd=location) + return revision.splitlines()[-1] + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if not repo: + return None + if not repo.lower().startswith('bzr:'): + repo = 'bzr+' + repo + egg_project_name = dist.egg_name().split('-', 1)[0] + current_rev = self.get_revision(location) + return '%s@%s#egg=%s' % (repo, current_rev, egg_project_name) + + def check_version(self, dest, rev_options): + """Always assume the versions don't match""" + return False + + +vcs.register(Bazaar) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/git.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/git.py new file mode 100644 index 0000000..2187dd8 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/git.py @@ -0,0 +1,300 @@ +from __future__ import absolute_import + +import logging +import tempfile +import os.path + +from pip.compat import samefile +from pip.exceptions import BadCommand +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request +from pip._vendor.packaging.version import parse as parse_version + +from pip.utils import display_path, rmtree +from pip.vcs import vcs, VersionControl + + +urlsplit = urllib_parse.urlsplit +urlunsplit = urllib_parse.urlunsplit + + +logger = logging.getLogger(__name__) + + +class Git(VersionControl): + name = 'git' + dirname = '.git' + repo_name = 'clone' + schemes = ( + 'git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file', + ) + + def __init__(self, url=None, *args, **kwargs): + + # Works around an apparent Git bug + # (see http://article.gmane.org/gmane.comp.version-control.git/146500) + if url: + scheme, netloc, path, query, fragment = urlsplit(url) + if scheme.endswith('file'): + initial_slashes = path[:-len(path.lstrip('/'))] + newpath = ( + initial_slashes + + urllib_request.url2pathname(path) + .replace('\\', '/').lstrip('/') + ) + url = urlunsplit((scheme, netloc, newpath, query, fragment)) + after_plus = scheme.find('+') + 1 + url = scheme[:after_plus] + urlunsplit( + (scheme[after_plus:], netloc, newpath, query, fragment), + ) + + super(Git, self).__init__(url, *args, **kwargs) + + def get_git_version(self): + VERSION_PFX = 'git version ' + version = self.run_command(['version'], show_stdout=False) + if version.startswith(VERSION_PFX): + version = version[len(VERSION_PFX):] + else: + version = '' + # get first 3 positions of the git version becasue + # on windows it is x.y.z.windows.t, and this parses as + # LegacyVersion which always smaller than a Version. + version = '.'.join(version.split('.')[:3]) + return parse_version(version) + + def export(self, location): + """Export the Git repository at the url to the destination location""" + temp_dir = tempfile.mkdtemp('-export', 'pip-') + self.unpack(temp_dir) + try: + if not location.endswith('/'): + location = location + '/' + self.run_command( + ['checkout-index', '-a', '-f', '--prefix', location], + show_stdout=False, cwd=temp_dir) + finally: + rmtree(temp_dir) + + def check_rev_options(self, rev, dest, rev_options): + """Check the revision options before checkout to compensate that tags + and branches may need origin/ as a prefix. + Returns the SHA1 of the branch or tag if found. + """ + revisions = self.get_short_refs(dest) + + origin_rev = 'origin/%s' % rev + if origin_rev in revisions: + # remote branch + return [revisions[origin_rev]] + elif rev in revisions: + # a local tag or branch name + return [revisions[rev]] + else: + logger.warning( + "Could not find a tag or branch '%s', assuming commit.", rev, + ) + return rev_options + + def check_version(self, dest, rev_options): + """ + Compare the current sha to the ref. ref may be a branch or tag name, + but current rev will always point to a sha. This means that a branch + or tag will never compare as True. So this ultimately only matches + against exact shas. + """ + return self.get_revision(dest).startswith(rev_options[0]) + + def switch(self, dest, url, rev_options): + self.run_command(['config', 'remote.origin.url', url], cwd=dest) + self.run_command(['checkout', '-q'] + rev_options, cwd=dest) + + self.update_submodules(dest) + + def update(self, dest, rev_options): + # First fetch changes from the default remote + if self.get_git_version() >= parse_version('1.9.0'): + # fetch tags in addition to everything else + self.run_command(['fetch', '-q', '--tags'], cwd=dest) + else: + self.run_command(['fetch', '-q'], cwd=dest) + # Then reset to wanted revision (maybe even origin/master) + if rev_options: + rev_options = self.check_rev_options( + rev_options[0], dest, rev_options, + ) + self.run_command(['reset', '--hard', '-q'] + rev_options, cwd=dest) + #: update submodules + self.update_submodules(dest) + + def obtain(self, dest): + url, rev = self.get_url_rev() + if rev: + rev_options = [rev] + rev_display = ' (to %s)' % rev + else: + rev_options = ['origin/master'] + rev_display = '' + if self.check_destination(dest, url, rev_options, rev_display): + logger.info( + 'Cloning %s%s to %s', url, rev_display, display_path(dest), + ) + self.run_command(['clone', '-q', url, dest]) + + if rev: + rev_options = self.check_rev_options(rev, dest, rev_options) + # Only do a checkout if rev_options differs from HEAD + if not self.check_version(dest, rev_options): + self.run_command( + ['checkout', '-q'] + rev_options, + cwd=dest, + ) + #: repo may contain submodules + self.update_submodules(dest) + + def get_url(self, location): + """Return URL of the first remote encountered.""" + remotes = self.run_command( + ['config', '--get-regexp', 'remote\..*\.url'], + show_stdout=False, cwd=location) + remotes = remotes.splitlines() + found_remote = remotes[0] + for remote in remotes: + if remote.startswith('remote.origin.url '): + found_remote = remote + break + url = found_remote.split(' ')[1] + return url.strip() + + def get_revision(self, location): + current_rev = self.run_command( + ['rev-parse', 'HEAD'], show_stdout=False, cwd=location) + return current_rev.strip() + + def get_full_refs(self, location): + """Yields tuples of (commit, ref) for branches and tags""" + output = self.run_command(['show-ref'], + show_stdout=False, cwd=location) + for line in output.strip().splitlines(): + commit, ref = line.split(' ', 1) + yield commit.strip(), ref.strip() + + def is_ref_remote(self, ref): + return ref.startswith('refs/remotes/') + + def is_ref_branch(self, ref): + return ref.startswith('refs/heads/') + + def is_ref_tag(self, ref): + return ref.startswith('refs/tags/') + + def is_ref_commit(self, ref): + """A ref is a commit sha if it is not anything else""" + return not any(( + self.is_ref_remote(ref), + self.is_ref_branch(ref), + self.is_ref_tag(ref), + )) + + # Should deprecate `get_refs` since it's ambiguous + def get_refs(self, location): + return self.get_short_refs(location) + + def get_short_refs(self, location): + """Return map of named refs (branches or tags) to commit hashes.""" + rv = {} + for commit, ref in self.get_full_refs(location): + ref_name = None + if self.is_ref_remote(ref): + ref_name = ref[len('refs/remotes/'):] + elif self.is_ref_branch(ref): + ref_name = ref[len('refs/heads/'):] + elif self.is_ref_tag(ref): + ref_name = ref[len('refs/tags/'):] + if ref_name is not None: + rv[ref_name] = commit + return rv + + def _get_subdirectory(self, location): + """Return the relative path of setup.py to the git repo root.""" + # find the repo root + git_dir = self.run_command(['rev-parse', '--git-dir'], + show_stdout=False, cwd=location).strip() + if not os.path.isabs(git_dir): + git_dir = os.path.join(location, git_dir) + root_dir = os.path.join(git_dir, '..') + # find setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + # relative path of setup.py to repo root + if samefile(root_dir, location): + return None + return os.path.relpath(location, root_dir) + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if not repo.lower().startswith('git:'): + repo = 'git+' + repo + egg_project_name = dist.egg_name().split('-', 1)[0] + if not repo: + return None + current_rev = self.get_revision(location) + req = '%s@%s#egg=%s' % (repo, current_rev, egg_project_name) + subdirectory = self._get_subdirectory(location) + if subdirectory: + req += '&subdirectory=' + subdirectory + return req + + def get_url_rev(self): + """ + Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. + That's required because although they use SSH they sometimes doesn't + work with a ssh:// scheme (e.g. Github). But we need a scheme for + parsing. Hence we remove it again afterwards and return it as a stub. + """ + if '://' not in self.url: + assert 'file:' not in self.url + self.url = self.url.replace('git+', 'git+ssh://') + url, rev = super(Git, self).get_url_rev() + url = url.replace('ssh://', '') + else: + url, rev = super(Git, self).get_url_rev() + + return url, rev + + def update_submodules(self, location): + if not os.path.exists(os.path.join(location, '.gitmodules')): + return + self.run_command( + ['submodule', 'update', '--init', '--recursive', '-q'], + cwd=location, + ) + + @classmethod + def controls_location(cls, location): + if super(Git, cls).controls_location(location): + return True + try: + r = cls().run_command(['rev-parse'], + cwd=location, + show_stdout=False, + on_returncode='ignore') + return not r + except BadCommand: + logger.debug("could not determine if %s is under git control " + "because git is not available", location) + return False + + +vcs.register(Git) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/mercurial.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/mercurial.py new file mode 100644 index 0000000..1aa83b9 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/mercurial.py @@ -0,0 +1,103 @@ +from __future__ import absolute_import + +import logging +import os +import tempfile + +from pip.utils import display_path, rmtree +from pip.vcs import vcs, VersionControl +from pip.download import path_to_url +from pip._vendor.six.moves import configparser + + +logger = logging.getLogger(__name__) + + +class Mercurial(VersionControl): + name = 'hg' + dirname = '.hg' + repo_name = 'clone' + schemes = ('hg', 'hg+http', 'hg+https', 'hg+ssh', 'hg+static-http') + + def export(self, location): + """Export the Hg repository at the url to the destination location""" + temp_dir = tempfile.mkdtemp('-export', 'pip-') + self.unpack(temp_dir) + try: + self.run_command( + ['archive', location], show_stdout=False, cwd=temp_dir) + finally: + rmtree(temp_dir) + + def switch(self, dest, url, rev_options): + repo_config = os.path.join(dest, self.dirname, 'hgrc') + config = configparser.SafeConfigParser() + try: + config.read(repo_config) + config.set('paths', 'default', url) + with open(repo_config, 'w') as config_file: + config.write(config_file) + except (OSError, configparser.NoSectionError) as exc: + logger.warning( + 'Could not switch Mercurial repository to %s: %s', url, exc, + ) + else: + self.run_command(['update', '-q'] + rev_options, cwd=dest) + + def update(self, dest, rev_options): + self.run_command(['pull', '-q'], cwd=dest) + self.run_command(['update', '-q'] + rev_options, cwd=dest) + + def obtain(self, dest): + url, rev = self.get_url_rev() + if rev: + rev_options = [rev] + rev_display = ' (to revision %s)' % rev + else: + rev_options = [] + rev_display = '' + if self.check_destination(dest, url, rev_options, rev_display): + logger.info( + 'Cloning hg %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(['clone', '--noupdate', '-q', url, dest]) + self.run_command(['update', '-q'] + rev_options, cwd=dest) + + def get_url(self, location): + url = self.run_command( + ['showconfig', 'paths.default'], + show_stdout=False, cwd=location).strip() + if self._is_local_repository(url): + url = path_to_url(url) + return url.strip() + + def get_revision(self, location): + current_revision = self.run_command( + ['parents', '--template={rev}'], + show_stdout=False, cwd=location).strip() + return current_revision + + def get_revision_hash(self, location): + current_rev_hash = self.run_command( + ['parents', '--template={node}'], + show_stdout=False, cwd=location).strip() + return current_rev_hash + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if not repo.lower().startswith('hg:'): + repo = 'hg+' + repo + egg_project_name = dist.egg_name().split('-', 1)[0] + if not repo: + return None + current_rev_hash = self.get_revision_hash(location) + return '%s@%s#egg=%s' % (repo, current_rev_hash, egg_project_name) + + def check_version(self, dest, rev_options): + """Always assume the versions don't match""" + return False + +vcs.register(Mercurial) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/subversion.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/subversion.py new file mode 100644 index 0000000..4b23156 --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/vcs/subversion.py @@ -0,0 +1,269 @@ +from __future__ import absolute_import + +import logging +import os +import re + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip.index import Link +from pip.utils import rmtree, display_path +from pip.utils.logging import indent_log +from pip.vcs import vcs, VersionControl + +_svn_xml_url_re = re.compile('url="([^"]+)"') +_svn_rev_re = re.compile('committed-rev="(\d+)"') +_svn_url_re = re.compile(r'URL: (.+)') +_svn_revision_re = re.compile(r'Revision: (.+)') +_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"') +_svn_info_xml_url_re = re.compile(r'<url>(.*)</url>') + + +logger = logging.getLogger(__name__) + + +class Subversion(VersionControl): + name = 'svn' + dirname = '.svn' + repo_name = 'checkout' + schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn') + + def get_info(self, location): + """Returns (url, revision), where both are strings""" + assert not location.rstrip('/').endswith(self.dirname), \ + 'Bad directory: %s' % location + output = self.run_command( + ['info', location], + show_stdout=False, + extra_environ={'LANG': 'C'}, + ) + match = _svn_url_re.search(output) + if not match: + logger.warning( + 'Cannot determine URL of svn checkout %s', + display_path(location), + ) + logger.debug('Output that cannot be parsed: \n%s', output) + return None, None + url = match.group(1).strip() + match = _svn_revision_re.search(output) + if not match: + logger.warning( + 'Cannot determine revision of svn checkout %s', + display_path(location), + ) + logger.debug('Output that cannot be parsed: \n%s', output) + return url, None + return url, match.group(1) + + def export(self, location): + """Export the svn repository at the url to the destination location""" + url, rev = self.get_url_rev() + rev_options = get_rev_options(url, rev) + url = self.remove_auth_from_url(url) + logger.info('Exporting svn repository %s to %s', url, location) + with indent_log(): + if os.path.exists(location): + # Subversion doesn't like to check out over an existing + # directory --force fixes this, but was only added in svn 1.5 + rmtree(location) + self.run_command( + ['export'] + rev_options + [url, location], + show_stdout=False) + + def switch(self, dest, url, rev_options): + self.run_command(['switch'] + rev_options + [url, dest]) + + def update(self, dest, rev_options): + self.run_command(['update'] + rev_options + [dest]) + + def obtain(self, dest): + url, rev = self.get_url_rev() + rev_options = get_rev_options(url, rev) + url = self.remove_auth_from_url(url) + if rev: + rev_display = ' (to revision %s)' % rev + else: + rev_display = '' + if self.check_destination(dest, url, rev_options, rev_display): + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(['checkout', '-q'] + rev_options + [url, dest]) + + def get_location(self, dist, dependency_links): + for url in dependency_links: + egg_fragment = Link(url).egg_fragment + if not egg_fragment: + continue + if '-' in egg_fragment: + # FIXME: will this work when a package has - in the name? + key = '-'.join(egg_fragment.split('-')[:-1]).lower() + else: + key = egg_fragment + if key == dist.key: + return url.split('#', 1)[0] + return None + + def get_revision(self, location): + """ + Return the maximum revision for all files under a given location + """ + # Note: taken from setuptools.command.egg_info + revision = 0 + + for base, dirs, files in os.walk(location): + if self.dirname not in dirs: + dirs[:] = [] + continue # no sense walking uncontrolled subdirs + dirs.remove(self.dirname) + entries_fn = os.path.join(base, self.dirname, 'entries') + if not os.path.exists(entries_fn): + # FIXME: should we warn? + continue + + dirurl, localrev = self._get_svn_url_rev(base) + + if base == location: + base_url = dirurl + '/' # save the root url + elif not dirurl or not dirurl.startswith(base_url): + dirs[:] = [] + continue # not part of the same svn tree, skip it + revision = max(revision, localrev) + return revision + + def get_url_rev(self): + # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it + url, rev = super(Subversion, self).get_url_rev() + if url.startswith('ssh://'): + url = 'svn+' + url + return url, rev + + def get_url(self, location): + # In cases where the source is in a subdirectory, not alongside + # setup.py we have to look up in the location until we find a real + # setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + + return self._get_svn_url_rev(location)[0] + + def _get_svn_url_rev(self, location): + from pip.exceptions import InstallationError + + entries_path = os.path.join(location, self.dirname, 'entries') + if os.path.exists(entries_path): + with open(entries_path) as f: + data = f.read() + else: # subversion >= 1.7 does not have the 'entries' file + data = '' + + if (data.startswith('8') or + data.startswith('9') or + data.startswith('10')): + data = list(map(str.splitlines, data.split('\n\x0c\n'))) + del data[0][0] # get rid of the '8' + url = data[0][3] + revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0] + elif data.startswith('<?xml'): + match = _svn_xml_url_re.search(data) + if not match: + raise ValueError('Badly formatted data: %r' % data) + url = match.group(1) # get repository URL + revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0] + else: + try: + # subversion >= 1.7 + xml = self.run_command( + ['info', '--xml', location], + show_stdout=False, + ) + url = _svn_info_xml_url_re.search(xml).group(1) + revs = [ + int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml) + ] + except InstallationError: + url, revs = None, [] + + if revs: + rev = max(revs) + else: + rev = 0 + + return url, rev + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if repo is None: + return None + # FIXME: why not project name? + egg_project_name = dist.egg_name().split('-', 1)[0] + rev = self.get_revision(location) + return 'svn+%s@%s#egg=%s' % (repo, rev, egg_project_name) + + def check_version(self, dest, rev_options): + """Always assume the versions don't match""" + return False + + @staticmethod + def remove_auth_from_url(url): + # Return a copy of url with 'username:password@' removed. + # username/pass params are passed to subversion through flags + # and are not recognized in the url. + + # parsed url + purl = urllib_parse.urlsplit(url) + stripped_netloc = \ + purl.netloc.split('@')[-1] + + # stripped url + url_pieces = ( + purl.scheme, stripped_netloc, purl.path, purl.query, purl.fragment + ) + surl = urllib_parse.urlunsplit(url_pieces) + return surl + + +def get_rev_options(url, rev): + if rev: + rev_options = ['-r', rev] + else: + rev_options = [] + + r = urllib_parse.urlsplit(url) + if hasattr(r, 'username'): + # >= Python-2.5 + username, password = r.username, r.password + else: + netloc = r[1] + if '@' in netloc: + auth = netloc.split('@')[0] + if ':' in auth: + username, password = auth.split(':', 1) + else: + username, password = auth, None + else: + username, password = None, None + + if username: + rev_options += ['--username', username] + if password: + rev_options += ['--password', password] + return rev_options + + +vcs.register(Subversion) diff --git a/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/wheel.py b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/wheel.py new file mode 100644 index 0000000..9ac9dff --- /dev/null +++ b/venv/Lib/site-packages/pip-9.0.1-py3.6.egg/pip/wheel.py @@ -0,0 +1,853 @@ +""" +Support for installing and building the "wheel" binary package format. +""" +from __future__ import absolute_import + +import compileall +import csv +import errno +import functools +import hashlib +import logging +import os +import os.path +import re +import shutil +import stat +import sys +import tempfile +import warnings + +from base64 import urlsafe_b64encode +from email.parser import Parser + +from pip._vendor.six import StringIO + +import pip +from pip.compat import expanduser +from pip.download import path_to_url, unpack_url +from pip.exceptions import ( + InstallationError, InvalidWheelFilename, UnsupportedWheel) +from pip.locations import distutils_scheme, PIP_DELETE_MARKER_FILENAME +from pip import pep425tags +from pip.utils import ( + call_subprocess, ensure_dir, captured_stdout, rmtree, read_chunks, +) +from pip.utils.ui import open_spinner +from pip.utils.logging import indent_log +from pip.utils.setuptools_build import SETUPTOOLS_SHIM +from pip._vendor.distlib.scripts import ScriptMaker +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.six.moves import configparser + + +wheel_ext = '.whl' + +VERSION_COMPATIBLE = (1, 0) + + +logger = logging.getLogger(__name__) + + +class WheelCache(object): + """A cache of wheels for future installs.""" + + def __init__(self, cache_dir, format_control): + """Create a wheel cache. + + :param cache_dir: The root of the cache. + :param format_control: A pip.index.FormatControl object to limit + binaries being read from the cache. + """ + self._cache_dir = expanduser(cache_dir) if cache_dir else None + self._format_control = format_control + + def cached_wheel(self, link, package_name): + return cached_wheel( + self._cache_dir, link, self._format_control, package_name) + + +def _cache_for_link(cache_dir, link): + """ + Return a directory to store cached wheels in for link. + + Because there are M wheels for any one sdist, we provide a directory + to cache them in, and then consult that directory when looking up + cache hits. + + We only insert things into the cache if they have plausible version + numbers, so that we don't contaminate the cache with things that were not + unique. E.g. ./package might have dozens of installs done for it and build + a version of 0.0...and if we built and cached a wheel, we'd end up using + the same wheel even if the source has been edited. + + :param cache_dir: The cache_dir being used by pip. + :param link: The link of the sdist for which this will cache wheels. + """ + + # We want to generate an url to use as our cache key, we don't want to just + # re-use the URL because it might have other items in the fragment and we + # don't care about those. + key_parts = [link.url_without_fragment] + if link.hash_name is not None and link.hash is not None: + key_parts.append("=".join([link.hash_name, link.hash])) + key_url = "#".join(key_parts) + + # Encode our key url with sha224, we'll use this because it has similar + # security properties to sha256, but with a shorter total output (and thus + # less secure). However the differences don't make a lot of difference for + # our use case here. + hashed = hashlib.sha224(key_url.encode()).hexdigest() + + # We want to nest the directories some to prevent having a ton of top level + # directories where we might run out of sub directories on some FS. + parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]] + + # Inside of the base location for cached wheels, expand our parts and join + # them all together. + return os.path.join(cache_dir, "wheels", *parts) + + +def cached_wheel(cache_dir, link, format_control, package_name): + if not cache_dir: + return link + if not link: + return link + if link.is_wheel: + return link + if not link.is_artifact: + return link + if not package_name: + return link + canonical_name = canonicalize_name(package_name) + formats = pip.index.fmt_ctl_formats(format_control, canonical_name) + if "binary" not in formats: + return link + root = _cache_for_link(cache_dir, link) + try: + wheel_names = os.listdir(root) + except OSError as e: + if e.errno in (errno.ENOENT, errno.ENOTDIR): + return link + raise + candidates = [] + for wheel_name in wheel_names: + try: + wheel = Wheel(wheel_name) + except InvalidWheelFilename: + continue + if not wheel.supported(): + # Built for a different python/arch/etc + continue + candidates.append((wheel.support_index_min(), wheel_name)) + if not candidates: + return link + candidates.sort() + path = os.path.join(root, candidates[0][1]) + return pip.index.Link(path_to_url(path)) + + +def rehash(path, algo='sha256', blocksize=1 << 20): + """Return (hash, length) for path using hashlib.new(algo)""" + h = hashlib.new(algo) + length = 0 + with open(path, 'rb') as f: + for block in read_chunks(f, size=blocksize): + length += len(block) + h.update(block) + digest = 'sha256=' + urlsafe_b64encode( + h.digest() + ).decode('latin1').rstrip('=') + return (digest, length) + + +def open_for_csv(name, mode): + if sys.version_info[0] < 3: + nl = {} + bin = 'b' + else: + nl = {'newline': ''} + bin = '' + return open(name, mode + bin, **nl) + + +def fix_script(path): + """Replace #!python with #!/path/to/python + Return True if file was changed.""" + # XXX RECORD hashes will need to be updated + if os.path.isfile(path): + with open(path, 'rb') as script: + firstline = script.readline() + if not firstline.startswith(b'#!python'): + return False + exename = sys.executable.encode(sys.getfilesystemencoding()) + firstline = b'#!' + exename + os.linesep.encode("ascii") + rest = script.read() + with open(path, 'wb') as script: + script.write(firstline) + script.write(rest) + return True + +dist_info_re = re.compile(r"""^(?P<namever>(?P<name>.+?)(-(?P<ver>\d.+?))?) + \.dist-info$""", re.VERBOSE) + + +def root_is_purelib(name, wheeldir): + """ + Return True if the extracted wheel in wheeldir should go into purelib. + """ + name_folded = name.replace("-", "_") + for item in os.listdir(wheeldir): + match = dist_info_re.match(item) + if match and match.group('name') == name_folded: + with open(os.path.join(wheeldir, item, 'WHEEL')) as wheel: + for line in wheel: + line = line.lower().rstrip() + if line == "root-is-purelib: true": + return True + return False + + +def get_entrypoints(filename): + if not os.path.exists(filename): + return {}, {} + + # This is done because you can pass a string to entry_points wrappers which + # means that they may or may not be valid INI files. The attempt here is to + # strip leading and trailing whitespace in order to make them valid INI + # files. + with open(filename) as fp: + data = StringIO() + for line in fp: + data.write(line.strip()) + data.write("\n") + data.seek(0) + + cp = configparser.RawConfigParser() + cp.optionxform = lambda option: option + cp.readfp(data) + + console = {} + gui = {} + if cp.has_section('console_scripts'): + console = dict(cp.items('console_scripts')) + if cp.has_section('gui_scripts'): + gui = dict(cp.items('gui_scripts')) + return console, gui + + +def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None, + pycompile=True, scheme=None, isolated=False, prefix=None): + """Install a wheel""" + + if not scheme: + scheme = distutils_scheme( + name, user=user, home=home, root=root, isolated=isolated, + prefix=prefix, + ) + + if root_is_purelib(name, wheeldir): + lib_dir = scheme['purelib'] + else: + lib_dir = scheme['platlib'] + + info_dir = [] + data_dirs = [] + source = wheeldir.rstrip(os.path.sep) + os.path.sep + + # Record details of the files moved + # installed = files copied from the wheel to the destination + # changed = files changed while installing (scripts #! line typically) + # generated = files newly generated during the install (script wrappers) + installed = {} + changed = set() + generated = [] + + # Compile all of the pyc files that we're going to be installing + if pycompile: + with captured_stdout() as stdout: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore') + compileall.compile_dir(source, force=True, quiet=True) + logger.debug(stdout.getvalue()) + + def normpath(src, p): + return os.path.relpath(src, p).replace(os.path.sep, '/') + + def record_installed(srcfile, destfile, modified=False): + """Map archive RECORD paths to installation RECORD paths.""" + oldpath = normpath(srcfile, wheeldir) + newpath = normpath(destfile, lib_dir) + installed[oldpath] = newpath + if modified: + changed.add(destfile) + + def clobber(source, dest, is_base, fixer=None, filter=None): + ensure_dir(dest) # common for the 'include' path + + for dir, subdirs, files in os.walk(source): + basedir = dir[len(source):].lstrip(os.path.sep) + destdir = os.path.join(dest, basedir) + if is_base and basedir.split(os.path.sep, 1)[0].endswith('.data'): + continue + for s in subdirs: + destsubdir = os.path.join(dest, basedir, s) + if is_base and basedir == '' and destsubdir.endswith('.data'): + data_dirs.append(s) + continue + elif (is_base and + s.endswith('.dist-info') and + canonicalize_name(s).startswith( + canonicalize_name(req.name))): + assert not info_dir, ('Multiple .dist-info directories: ' + + destsubdir + ', ' + + ', '.join(info_dir)) + info_dir.append(destsubdir) + for f in files: + # Skip unwanted files + if filter and filter(f): + continue + srcfile = os.path.join(dir, f) + destfile = os.path.join(dest, basedir, f) + # directory creation is lazy and after the file filtering above + # to ensure we don't install empty dirs; empty dirs can't be + # uninstalled. + ensure_dir(destdir) + + # We use copyfile (not move, copy, or copy2) to be extra sure + # that we are not moving directories over (copyfile fails for + # directories) as well as to ensure that we are not copying + # over any metadata because we want more control over what + # metadata we actually copy over. + shutil.copyfile(srcfile, destfile) + + # Copy over the metadata for the file, currently this only + # includes the atime and mtime. + st = os.stat(srcfile) + if hasattr(os, "utime"): + os.utime(destfile, (st.st_atime, st.st_mtime)) + + # If our file is executable, then make our destination file + # executable. + if os.access(srcfile, os.X_OK): + st = os.stat(srcfile) + permissions = ( + st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + ) + os.chmod(destfile, permissions) + + changed = False + if fixer: + changed = fixer(destfile) + record_installed(srcfile, destfile, changed) + + clobber(source, lib_dir, True) + + assert info_dir, "%s .dist-info directory not found" % req + + # Get the defined entry points + ep_file = os.path.join(info_dir[0], 'entry_points.txt') + console, gui = get_entrypoints(ep_file) + + def is_entrypoint_wrapper(name): + # EP, EP.exe and EP-script.py are scripts generated for + # entry point EP by setuptools + if name.lower().endswith('.exe'): + matchname = name[:-4] + elif name.lower().endswith('-script.py'): + matchname = name[:-10] + elif name.lower().endswith(".pya"): + matchname = name[:-4] + else: + matchname = name + # Ignore setuptools-generated scripts + return (matchname in console or matchname in gui) + + for datadir in data_dirs: + fixer = None + filter = None + for subdir in os.listdir(os.path.join(wheeldir, datadir)): + fixer = None + if subdir == 'scripts': + fixer = fix_script + filter = is_entrypoint_wrapper + source = os.path.join(wheeldir, datadir, subdir) + dest = scheme[subdir] + clobber(source, dest, False, fixer=fixer, filter=filter) + + maker = ScriptMaker(None, scheme['scripts']) + + # Ensure old scripts are overwritten. + # See https://github.com/pypa/pip/issues/1800 + maker.clobber = True + + # Ensure we don't generate any variants for scripts because this is almost + # never what somebody wants. + # See https://bitbucket.org/pypa/distlib/issue/35/ + maker.variants = set(('', )) + + # This is required because otherwise distlib creates scripts that are not + # executable. + # See https://bitbucket.org/pypa/distlib/issue/32/ + maker.set_mode = True + + # Simplify the script and fix the fact that the default script swallows + # every single stack trace. + # See https://bitbucket.org/pypa/distlib/issue/34/ + # See https://bitbucket.org/pypa/distlib/issue/33/ + def _get_script_text(entry): + if entry.suffix is None: + raise InstallationError( + "Invalid script entry point: %s for req: %s - A callable " + "suffix is required. Cf https://packaging.python.org/en/" + "latest/distributing.html#console-scripts for more " + "information." % (entry, req) + ) + return maker.script_template % { + "module": entry.prefix, + "import_name": entry.suffix.split(".")[0], + "func": entry.suffix, + } + + maker._get_script_text = _get_script_text + maker.script_template = """# -*- coding: utf-8 -*- +import re +import sys + +from %(module)s import %(import_name)s + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(%(func)s()) +""" + + # Special case pip and setuptools to generate versioned wrappers + # + # The issue is that some projects (specifically, pip and setuptools) use + # code in setup.py to create "versioned" entry points - pip2.7 on Python + # 2.7, pip3.3 on Python 3.3, etc. But these entry points are baked into + # the wheel metadata at build time, and so if the wheel is installed with + # a *different* version of Python the entry points will be wrong. The + # correct fix for this is to enhance the metadata to be able to describe + # such versioned entry points, but that won't happen till Metadata 2.0 is + # available. + # In the meantime, projects using versioned entry points will either have + # incorrect versioned entry points, or they will not be able to distribute + # "universal" wheels (i.e., they will need a wheel per Python version). + # + # Because setuptools and pip are bundled with _ensurepip and virtualenv, + # we need to use universal wheels. So, as a stopgap until Metadata 2.0, we + # override the versioned entry points in the wheel and generate the + # correct ones. This code is purely a short-term measure until Metadata 2.0 + # is available. + # + # To add the level of hack in this section of code, in order to support + # ensurepip this code will look for an ``ENSUREPIP_OPTIONS`` environment + # variable which will control which version scripts get installed. + # + # ENSUREPIP_OPTIONS=altinstall + # - Only pipX.Y and easy_install-X.Y will be generated and installed + # ENSUREPIP_OPTIONS=install + # - pipX.Y, pipX, easy_install-X.Y will be generated and installed. Note + # that this option is technically if ENSUREPIP_OPTIONS is set and is + # not altinstall + # DEFAULT + # - The default behavior is to install pip, pipX, pipX.Y, easy_install + # and easy_install-X.Y. + pip_script = console.pop('pip', None) + if pip_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + spec = 'pip = ' + pip_script + generated.extend(maker.make(spec)) + + if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall": + spec = 'pip%s = %s' % (sys.version[:1], pip_script) + generated.extend(maker.make(spec)) + + spec = 'pip%s = %s' % (sys.version[:3], pip_script) + generated.extend(maker.make(spec)) + # Delete any other versioned pip entry points + pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)] + for k in pip_ep: + del console[k] + easy_install_script = console.pop('easy_install', None) + if easy_install_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + spec = 'easy_install = ' + easy_install_script + generated.extend(maker.make(spec)) + + spec = 'easy_install-%s = %s' % (sys.version[:3], easy_install_script) + generated.extend(maker.make(spec)) + # Delete any other versioned easy_install entry points + easy_install_ep = [ + k for k in console if re.match(r'easy_install(-\d\.\d)?$', k) + ] + for k in easy_install_ep: + del console[k] + + # Generate the console and GUI entry points specified in the wheel + if len(console) > 0: + generated.extend( + maker.make_multiple(['%s = %s' % kv for kv in console.items()]) + ) + if len(gui) > 0: + generated.extend( + maker.make_multiple( + ['%s = %s' % kv for kv in gui.items()], + {'gui': True} + ) + ) + + # Record pip as the installer + installer = os.path.join(info_dir[0], 'INSTALLER') + temp_installer = os.path.join(info_dir[0], 'INSTALLER.pip') + with open(temp_installer, 'wb') as installer_file: + installer_file.write(b'pip\n') + shutil.move(temp_installer, installer) + generated.append(installer) + + # Record details of all files installed + record = os.path.join(info_dir[0], 'RECORD') + temp_record = os.path.join(info_dir[0], 'RECORD.pip') + with open_for_csv(record, 'r') as record_in: + with open_for_csv(temp_record, 'w+') as record_out: + reader = csv.reader(record_in) + writer = csv.writer(record_out) + for row in reader: + row[0] = installed.pop(row[0], row[0]) + if row[0] in changed: + row[1], row[2] = rehash(row[0]) + writer.writerow(row) + for f in generated: + h, l = rehash(f) + writer.writerow((normpath(f, lib_dir), h, l)) + for f in installed: + writer.writerow((installed[f], '', '')) + shutil.move(temp_record, record) + + +def _unique(fn): + @functools.wraps(fn) + def unique(*args, **kw): + seen = set() + for item in fn(*args, **kw): + if item not in seen: + seen.add(item) + yield item + return unique + + +# TODO: this goes somewhere besides the wheel module +@_unique +def uninstallation_paths(dist): + """ + Yield all the uninstallation paths for dist based on RECORD-without-.pyc + + Yield paths to all the files in RECORD. For each .py file in RECORD, add + the .pyc in the same directory. + + UninstallPathSet.add() takes care of the __pycache__ .pyc. + """ + from pip.utils import FakeFile # circular import + r = csv.reader(FakeFile(dist.get_metadata_lines('RECORD'))) + for row in r: + path = os.path.join(dist.location, row[0]) + yield path + if path.endswith('.py'): + dn, fn = os.path.split(path) + base = fn[:-3] + path = os.path.join(dn, base + '.pyc') + yield path + + +def wheel_version(source_dir): + """ + Return the Wheel-Version of an extracted wheel, if possible. + + Otherwise, return False if we couldn't parse / extract it. + """ + try: + dist = [d for d in pkg_resources.find_on_path(None, source_dir)][0] + + wheel_data = dist.get_metadata('WHEEL') + wheel_data = Parser().parsestr(wheel_data) + + version = wheel_data['Wheel-Version'].strip() + version = tuple(map(int, version.split('.'))) + return version + except: + return False + + +def check_compatibility(version, name): + """ + Raises errors or warns if called with an incompatible Wheel-Version. + + Pip should refuse to install a Wheel-Version that's a major series + ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when + installing a version only minor version ahead (e.g 1.2 > 1.1). + + version: a 2-tuple representing a Wheel-Version (Major, Minor) + name: name of wheel or package to raise exception about + + :raises UnsupportedWheel: when an incompatible Wheel-Version is given + """ + if not version: + raise UnsupportedWheel( + "%s is in an unsupported or invalid wheel" % name + ) + if version[0] > VERSION_COMPATIBLE[0]: + raise UnsupportedWheel( + "%s's Wheel-Version (%s) is not compatible with this version " + "of pip" % (name, '.'.join(map(str, version))) + ) + elif version > VERSION_COMPATIBLE: + logger.warning( + 'Installing from a newer Wheel-Version (%s)', + '.'.join(map(str, version)), + ) + + +class Wheel(object): + """A wheel file""" + + # TODO: maybe move the install code into this class + + wheel_file_re = re.compile( + r"""^(?P<namever>(?P<name>.+?)-(?P<ver>\d.*?)) + ((-(?P<build>\d.*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?) + \.whl|\.dist-info)$""", + re.VERBOSE + ) + + def __init__(self, filename): + """ + :raises InvalidWheelFilename: when the filename is invalid for a wheel + """ + wheel_info = self.wheel_file_re.match(filename) + if not wheel_info: + raise InvalidWheelFilename( + "%s is not a valid wheel filename." % filename + ) + self.filename = filename + self.name = wheel_info.group('name').replace('_', '-') + # we'll assume "_" means "-" due to wheel naming scheme + # (https://github.com/pypa/pip/issues/1150) + self.version = wheel_info.group('ver').replace('_', '-') + self.pyversions = wheel_info.group('pyver').split('.') + self.abis = wheel_info.group('abi').split('.') + self.plats = wheel_info.group('plat').split('.') + + # All the tag combinations from this file + self.file_tags = set( + (x, y, z) for x in self.pyversions + for y in self.abis for z in self.plats + ) + + def support_index_min(self, tags=None): + """ + Return the lowest index that one of the wheel's file_tag combinations + achieves in the supported_tags list e.g. if there are 8 supported tags, + and one of the file tags is first in the list, then return 0. Returns + None is the wheel is not supported. + """ + if tags is None: # for mock + tags = pep425tags.supported_tags + indexes = [tags.index(c) for c in self.file_tags if c in tags] + return min(indexes) if indexes else None + + def supported(self, tags=None): + """Is this wheel supported on this system?""" + if tags is None: # for mock + tags = pep425tags.supported_tags + return bool(set(tags).intersection(self.file_tags)) + + +class WheelBuilder(object): + """Build wheels from a RequirementSet.""" + + def __init__(self, requirement_set, finder, build_options=None, + global_options=None): + self.requirement_set = requirement_set + self.finder = finder + self._cache_root = requirement_set._wheel_cache._cache_dir + self._wheel_dir = requirement_set.wheel_download_dir + self.build_options = build_options or [] + self.global_options = global_options or [] + + def _build_one(self, req, output_dir, python_tag=None): + """Build one wheel. + + :return: The filename of the built wheel, or None if the build failed. + """ + tempd = tempfile.mkdtemp('pip-wheel-') + try: + if self.__build_one(req, tempd, python_tag=python_tag): + try: + wheel_name = os.listdir(tempd)[0] + wheel_path = os.path.join(output_dir, wheel_name) + shutil.move(os.path.join(tempd, wheel_name), wheel_path) + logger.info('Stored in directory: %s', output_dir) + return wheel_path + except: + pass + # Ignore return, we can't do anything else useful. + self._clean_one(req) + return None + finally: + rmtree(tempd) + + def _base_setup_args(self, req): + return [ + sys.executable, "-u", '-c', + SETUPTOOLS_SHIM % req.setup_py + ] + list(self.global_options) + + def __build_one(self, req, tempd, python_tag=None): + base_args = self._base_setup_args(req) + + spin_message = 'Running setup.py bdist_wheel for %s' % (req.name,) + with open_spinner(spin_message) as spinner: + logger.debug('Destination directory: %s', tempd) + wheel_args = base_args + ['bdist_wheel', '-d', tempd] \ + + self.build_options + + if python_tag is not None: + wheel_args += ["--python-tag", python_tag] + + try: + call_subprocess(wheel_args, cwd=req.setup_py_dir, + show_stdout=False, spinner=spinner) + return True + except: + spinner.finish("error") + logger.error('Failed building wheel for %s', req.name) + return False + + def _clean_one(self, req): + base_args = self._base_setup_args(req) + + logger.info('Running setup.py clean for %s', req.name) + clean_args = base_args + ['clean', '--all'] + try: + call_subprocess(clean_args, cwd=req.source_dir, show_stdout=False) + return True + except: + logger.error('Failed cleaning build dir for %s', req.name) + return False + + def build(self, autobuilding=False): + """Build wheels. + + :param unpack: If True, replace the sdist we built from with the + newly built wheel, in preparation for installation. + :return: True if all the wheels built correctly. + """ + assert self._wheel_dir or (autobuilding and self._cache_root) + # unpack sdists and constructs req set + self.requirement_set.prepare_files(self.finder) + + reqset = self.requirement_set.requirements.values() + + buildset = [] + for req in reqset: + if req.constraint: + continue + if req.is_wheel: + if not autobuilding: + logger.info( + 'Skipping %s, due to already being wheel.', req.name) + elif autobuilding and req.editable: + pass + elif autobuilding and req.link and not req.link.is_artifact: + pass + elif autobuilding and not req.source_dir: + pass + else: + if autobuilding: + link = req.link + base, ext = link.splitext() + if pip.index.egg_info_matches(base, None, link) is None: + # Doesn't look like a package - don't autobuild a wheel + # because we'll have no way to lookup the result sanely + continue + if "binary" not in pip.index.fmt_ctl_formats( + self.finder.format_control, + canonicalize_name(req.name)): + logger.info( + "Skipping bdist_wheel for %s, due to binaries " + "being disabled for it.", req.name) + continue + buildset.append(req) + + if not buildset: + return True + + # Build the wheels. + logger.info( + 'Building wheels for collected packages: %s', + ', '.join([req.name for req in buildset]), + ) + with indent_log(): + build_success, build_failure = [], [] + for req in buildset: + python_tag = None + if autobuilding: + python_tag = pep425tags.implementation_tag + output_dir = _cache_for_link(self._cache_root, req.link) + try: + ensure_dir(output_dir) + except OSError as e: + logger.warning("Building wheel for %s failed: %s", + req.name, e) + build_failure.append(req) + continue + else: + output_dir = self._wheel_dir + wheel_file = self._build_one( + req, output_dir, + python_tag=python_tag, + ) + if wheel_file: + build_success.append(req) + if autobuilding: + # XXX: This is mildly duplicative with prepare_files, + # but not close enough to pull out to a single common + # method. + # The code below assumes temporary source dirs - + # prevent it doing bad things. + if req.source_dir and not os.path.exists(os.path.join( + req.source_dir, PIP_DELETE_MARKER_FILENAME)): + raise AssertionError( + "bad source dir - missing marker") + # Delete the source we built the wheel from + req.remove_temporary_source() + # set the build directory again - name is known from + # the work prepare_files did. + req.source_dir = req.build_location( + self.requirement_set.build_dir) + # Update the link for this. + req.link = pip.index.Link( + path_to_url(wheel_file)) + assert req.link.is_wheel + # extract the wheel into the dir + unpack_url( + req.link, req.source_dir, None, False, + session=self.requirement_set.session) + else: + build_failure.append(req) + + # notify success/failure + if build_success: + logger.info( + 'Successfully built %s', + ' '.join([req.name for req in build_success]), + ) + if build_failure: + logger.info( + 'Failed to build %s', + ' '.join([req.name for req in build_failure]), + ) + # Return True if all builds were successful + return len(build_failure) == 0 diff --git a/venv/Lib/site-packages/pip/__init__.py b/venv/Lib/site-packages/pip/__init__.py new file mode 100644 index 0000000..ae265fa --- /dev/null +++ b/venv/Lib/site-packages/pip/__init__.py @@ -0,0 +1 @@ +__version__ = "18.1" diff --git a/venv/Lib/site-packages/pip/__main__.py b/venv/Lib/site-packages/pip/__main__.py new file mode 100644 index 0000000..0c223f8 --- /dev/null +++ b/venv/Lib/site-packages/pip/__main__.py @@ -0,0 +1,19 @@ +from __future__ import absolute_import + +import os +import sys + +# If we are running from a wheel, add the wheel to sys.path +# This allows the usage python pip-*.whl/pip install pip-*.whl +if __package__ == '': + # __file__ is pip-*.whl/pip/__main__.py + # first dirname call strips of '/__main__.py', second strips off '/pip' + # Resulting path is the name of the wheel itself + # Add that to sys.path so we can import pip + path = os.path.dirname(os.path.dirname(__file__)) + sys.path.insert(0, path) + +from pip._internal import main as _main # isort:skip # noqa + +if __name__ == '__main__': + sys.exit(_main()) diff --git a/venv/Lib/site-packages/pip/_internal/__init__.py b/venv/Lib/site-packages/pip/_internal/__init__.py new file mode 100644 index 0000000..276124d --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/__init__.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +from __future__ import absolute_import + +import locale +import logging +import os +import warnings + +import sys + +# 2016-06-17 barry@debian.org: urllib3 1.14 added optional support for socks, +# but if invoked (i.e. imported), it will issue a warning to stderr if socks +# isn't available. requests unconditionally imports urllib3's socks contrib +# module, triggering this warning. The warning breaks DEP-8 tests (because of +# the stderr output) and is just plain annoying in normal usage. I don't want +# to add socks as yet another dependency for pip, nor do I want to allow-stder +# in the DEP-8 tests, so just suppress the warning. pdb tells me this has to +# be done before the import of pip.vcs. +from pip._vendor.urllib3.exceptions import DependencyWarning +warnings.filterwarnings("ignore", category=DependencyWarning) # noqa + +# We want to inject the use of SecureTransport as early as possible so that any +# references or sessions or what have you are ensured to have it, however we +# only want to do this in the case that we're running on macOS and the linked +# OpenSSL is too old to handle TLSv1.2 +try: + import ssl +except ImportError: + pass +else: + # Checks for OpenSSL 1.0.1 on MacOS + if sys.platform == "darwin" and ssl.OPENSSL_VERSION_NUMBER < 0x1000100f: + try: + from pip._vendor.urllib3.contrib import securetransport + except (ImportError, OSError): + pass + else: + securetransport.inject_into_urllib3() + +from pip._internal.cli.autocompletion import autocomplete +from pip._internal.cli.main_parser import parse_command +from pip._internal.commands import commands_dict +from pip._internal.exceptions import PipError +from pip._internal.utils import deprecation +from pip._internal.vcs import git, mercurial, subversion, bazaar # noqa +from pip._vendor.urllib3.exceptions import InsecureRequestWarning + +logger = logging.getLogger(__name__) + +# Hide the InsecureRequestWarning from urllib3 +warnings.filterwarnings("ignore", category=InsecureRequestWarning) + + +def main(args=None): + if args is None: + args = sys.argv[1:] + + # Configure our deprecation warnings to be sent through loggers + deprecation.install_warning_logger() + + autocomplete() + + try: + cmd_name, cmd_args = parse_command(args) + except PipError as exc: + sys.stderr.write("ERROR: %s" % exc) + sys.stderr.write(os.linesep) + sys.exit(1) + + # Needed for locale.getpreferredencoding(False) to work + # in pip._internal.utils.encoding.auto_decode + try: + locale.setlocale(locale.LC_ALL, '') + except locale.Error as e: + # setlocale can apparently crash if locale are uninitialized + logger.debug("Ignoring error %s when setting locale", e) + command = commands_dict[cmd_name](isolated=("--isolated" in cmd_args)) + return command.main(cmd_args) diff --git a/venv/Lib/site-packages/pip/_internal/build_env.py b/venv/Lib/site-packages/pip/_internal/build_env.py new file mode 100644 index 0000000..673409d --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/build_env.py @@ -0,0 +1,142 @@ +"""Build Environment used for isolation during sdist building +""" + +import logging +import os +import sys +from distutils.sysconfig import get_python_lib +from sysconfig import get_paths + +from pip._vendor.pkg_resources import Requirement, VersionConflict, WorkingSet + +from pip._internal.utils.misc import call_subprocess +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.ui import open_spinner + +logger = logging.getLogger(__name__) + + +class BuildEnvironment(object): + """Creates and manages an isolated environment to install build deps + """ + + def __init__(self): + self._temp_dir = TempDirectory(kind="build-env") + self._temp_dir.create() + + @property + def path(self): + return self._temp_dir.path + + def __enter__(self): + self.save_path = os.environ.get('PATH', None) + self.save_pythonpath = os.environ.get('PYTHONPATH', None) + self.save_nousersite = os.environ.get('PYTHONNOUSERSITE', None) + + install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix' + install_dirs = get_paths(install_scheme, vars={ + 'base': self.path, + 'platbase': self.path, + }) + + scripts = install_dirs['scripts'] + if self.save_path: + os.environ['PATH'] = scripts + os.pathsep + self.save_path + else: + os.environ['PATH'] = scripts + os.pathsep + os.defpath + + # Note: prefer distutils' sysconfig to get the + # library paths so PyPy is correctly supported. + purelib = get_python_lib(plat_specific=0, prefix=self.path) + platlib = get_python_lib(plat_specific=1, prefix=self.path) + if purelib == platlib: + lib_dirs = purelib + else: + lib_dirs = purelib + os.pathsep + platlib + if self.save_pythonpath: + os.environ['PYTHONPATH'] = lib_dirs + os.pathsep + \ + self.save_pythonpath + else: + os.environ['PYTHONPATH'] = lib_dirs + + os.environ['PYTHONNOUSERSITE'] = '1' + + return self.path + + def __exit__(self, exc_type, exc_val, exc_tb): + def restore_var(varname, old_value): + if old_value is None: + os.environ.pop(varname, None) + else: + os.environ[varname] = old_value + + restore_var('PATH', self.save_path) + restore_var('PYTHONPATH', self.save_pythonpath) + restore_var('PYTHONNOUSERSITE', self.save_nousersite) + + def cleanup(self): + self._temp_dir.cleanup() + + def missing_requirements(self, reqs): + """Return a list of the requirements from reqs that are not present + """ + missing = [] + with self: + ws = WorkingSet(os.environ["PYTHONPATH"].split(os.pathsep)) + for req in reqs: + try: + if ws.find(Requirement.parse(req)) is None: + missing.append(req) + except VersionConflict: + missing.append(req) + return missing + + def install_requirements(self, finder, requirements, message): + args = [ + sys.executable, '-m', 'pip', 'install', '--ignore-installed', + '--no-user', '--prefix', self.path, '--no-warn-script-location', + ] + if logger.getEffectiveLevel() <= logging.DEBUG: + args.append('-v') + for format_control in ('no_binary', 'only_binary'): + formats = getattr(finder.format_control, format_control) + args.extend(('--' + format_control.replace('_', '-'), + ','.join(sorted(formats or {':none:'})))) + if finder.index_urls: + args.extend(['-i', finder.index_urls[0]]) + for extra_index in finder.index_urls[1:]: + args.extend(['--extra-index-url', extra_index]) + else: + args.append('--no-index') + for link in finder.find_links: + args.extend(['--find-links', link]) + for _, host, _ in finder.secure_origins: + args.extend(['--trusted-host', host]) + if finder.allow_all_prereleases: + args.append('--pre') + if finder.process_dependency_links: + args.append('--process-dependency-links') + args.append('--') + args.extend(requirements) + with open_spinner(message) as spinner: + call_subprocess(args, show_stdout=False, spinner=spinner) + + +class NoOpBuildEnvironment(BuildEnvironment): + """A no-op drop-in replacement for BuildEnvironment + """ + + def __init__(self): + pass + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + def cleanup(self): + pass + + def install_requirements(self, finder, requirements, message): + raise NotImplementedError() diff --git a/venv/Lib/site-packages/pip/_internal/cache.py b/venv/Lib/site-packages/pip/_internal/cache.py new file mode 100644 index 0000000..33bec97 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/cache.py @@ -0,0 +1,202 @@ +"""Cache Management +""" + +import errno +import hashlib +import logging +import os + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.download import path_to_url +from pip._internal.models.link import Link +from pip._internal.utils.compat import expanduser +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.wheel import InvalidWheelFilename, Wheel + +logger = logging.getLogger(__name__) + + +class Cache(object): + """An abstract class - provides cache directories for data from links + + + :param cache_dir: The root of the cache. + :param format_control: An object of FormatControl class to limit + binaries being read from the cache. + :param allowed_formats: which formats of files the cache should store. + ('binary' and 'source' are the only allowed values) + """ + + def __init__(self, cache_dir, format_control, allowed_formats): + super(Cache, self).__init__() + self.cache_dir = expanduser(cache_dir) if cache_dir else None + self.format_control = format_control + self.allowed_formats = allowed_formats + + _valid_formats = {"source", "binary"} + assert self.allowed_formats.union(_valid_formats) == _valid_formats + + def _get_cache_path_parts(self, link): + """Get parts of part that must be os.path.joined with cache_dir + """ + + # We want to generate an url to use as our cache key, we don't want to + # just re-use the URL because it might have other items in the fragment + # and we don't care about those. + key_parts = [link.url_without_fragment] + if link.hash_name is not None and link.hash is not None: + key_parts.append("=".join([link.hash_name, link.hash])) + key_url = "#".join(key_parts) + + # Encode our key url with sha224, we'll use this because it has similar + # security properties to sha256, but with a shorter total output (and + # thus less secure). However the differences don't make a lot of + # difference for our use case here. + hashed = hashlib.sha224(key_url.encode()).hexdigest() + + # We want to nest the directories some to prevent having a ton of top + # level directories where we might run out of sub directories on some + # FS. + parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]] + + return parts + + def _get_candidates(self, link, package_name): + can_not_cache = ( + not self.cache_dir or + not package_name or + not link + ) + if can_not_cache: + return [] + + canonical_name = canonicalize_name(package_name) + formats = self.format_control.get_allowed_formats( + canonical_name + ) + if not self.allowed_formats.intersection(formats): + return [] + + root = self.get_path_for_link(link) + try: + return os.listdir(root) + except OSError as err: + if err.errno in {errno.ENOENT, errno.ENOTDIR}: + return [] + raise + + def get_path_for_link(self, link): + """Return a directory to store cached items in for link. + """ + raise NotImplementedError() + + def get(self, link, package_name): + """Returns a link to a cached item if it exists, otherwise returns the + passed link. + """ + raise NotImplementedError() + + def _link_for_candidate(self, link, candidate): + root = self.get_path_for_link(link) + path = os.path.join(root, candidate) + + return Link(path_to_url(path)) + + def cleanup(self): + pass + + +class SimpleWheelCache(Cache): + """A cache of wheels for future installs. + """ + + def __init__(self, cache_dir, format_control): + super(SimpleWheelCache, self).__init__( + cache_dir, format_control, {"binary"} + ) + + def get_path_for_link(self, link): + """Return a directory to store cached wheels for link + + Because there are M wheels for any one sdist, we provide a directory + to cache them in, and then consult that directory when looking up + cache hits. + + We only insert things into the cache if they have plausible version + numbers, so that we don't contaminate the cache with things that were + not unique. E.g. ./package might have dozens of installs done for it + and build a version of 0.0...and if we built and cached a wheel, we'd + end up using the same wheel even if the source has been edited. + + :param link: The link of the sdist for which this will cache wheels. + """ + parts = self._get_cache_path_parts(link) + + # Store wheels within the root cache_dir + return os.path.join(self.cache_dir, "wheels", *parts) + + def get(self, link, package_name): + candidates = [] + + for wheel_name in self._get_candidates(link, package_name): + try: + wheel = Wheel(wheel_name) + except InvalidWheelFilename: + continue + if not wheel.supported(): + # Built for a different python/arch/etc + continue + candidates.append((wheel.support_index_min(), wheel_name)) + + if not candidates: + return link + + return self._link_for_candidate(link, min(candidates)[1]) + + +class EphemWheelCache(SimpleWheelCache): + """A SimpleWheelCache that creates it's own temporary cache directory + """ + + def __init__(self, format_control): + self._temp_dir = TempDirectory(kind="ephem-wheel-cache") + self._temp_dir.create() + + super(EphemWheelCache, self).__init__( + self._temp_dir.path, format_control + ) + + def cleanup(self): + self._temp_dir.cleanup() + + +class WheelCache(Cache): + """Wraps EphemWheelCache and SimpleWheelCache into a single Cache + + This Cache allows for gracefully degradation, using the ephem wheel cache + when a certain link is not found in the simple wheel cache first. + """ + + def __init__(self, cache_dir, format_control): + super(WheelCache, self).__init__( + cache_dir, format_control, {'binary'} + ) + self._wheel_cache = SimpleWheelCache(cache_dir, format_control) + self._ephem_cache = EphemWheelCache(format_control) + + def get_path_for_link(self, link): + return self._wheel_cache.get_path_for_link(link) + + def get_ephem_path_for_link(self, link): + return self._ephem_cache.get_path_for_link(link) + + def get(self, link, package_name): + retval = self._wheel_cache.get(link, package_name) + if retval is link: + retval = self._ephem_cache.get(link, package_name) + return retval + + def cleanup(self): + self._wheel_cache.cleanup() + self._ephem_cache.cleanup() diff --git a/venv/Lib/site-packages/pip/_internal/cli/__init__.py b/venv/Lib/site-packages/pip/_internal/cli/__init__.py new file mode 100644 index 0000000..e589bb9 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/cli/__init__.py @@ -0,0 +1,4 @@ +"""Subpackage containing all of pip's command line interface related code +""" + +# This file intentionally does not import submodules diff --git a/venv/Lib/site-packages/pip/_internal/cli/autocompletion.py b/venv/Lib/site-packages/pip/_internal/cli/autocompletion.py new file mode 100644 index 0000000..0a04199 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/cli/autocompletion.py @@ -0,0 +1,152 @@ +"""Logic that powers autocompletion installed by ``pip completion``. +""" + +import optparse +import os +import sys + +from pip._internal.cli.main_parser import create_main_parser +from pip._internal.commands import commands_dict, get_summaries +from pip._internal.utils.misc import get_installed_distributions + + +def autocomplete(): + """Entry Point for completion of main and subcommand options. + """ + # Don't complete if user hasn't sourced bash_completion file. + if 'PIP_AUTO_COMPLETE' not in os.environ: + return + cwords = os.environ['COMP_WORDS'].split()[1:] + cword = int(os.environ['COMP_CWORD']) + try: + current = cwords[cword - 1] + except IndexError: + current = '' + + subcommands = [cmd for cmd, summary in get_summaries()] + options = [] + # subcommand + try: + subcommand_name = [w for w in cwords if w in subcommands][0] + except IndexError: + subcommand_name = None + + parser = create_main_parser() + # subcommand options + if subcommand_name: + # special case: 'help' subcommand has no options + if subcommand_name == 'help': + sys.exit(1) + # special case: list locally installed dists for show and uninstall + should_list_installed = ( + subcommand_name in ['show', 'uninstall'] and + not current.startswith('-') + ) + if should_list_installed: + installed = [] + lc = current.lower() + for dist in get_installed_distributions(local_only=True): + if dist.key.startswith(lc) and dist.key not in cwords[1:]: + installed.append(dist.key) + # if there are no dists installed, fall back to option completion + if installed: + for dist in installed: + print(dist) + sys.exit(1) + + subcommand = commands_dict[subcommand_name]() + + for opt in subcommand.parser.option_list_all: + if opt.help != optparse.SUPPRESS_HELP: + for opt_str in opt._long_opts + opt._short_opts: + options.append((opt_str, opt.nargs)) + + # filter out previously specified options from available options + prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]] + options = [(x, v) for (x, v) in options if x not in prev_opts] + # filter options by current input + options = [(k, v) for k, v in options if k.startswith(current)] + # get completion type given cwords and available subcommand options + completion_type = get_path_completion_type( + cwords, cword, subcommand.parser.option_list_all, + ) + # get completion files and directories if ``completion_type`` is + # ``<file>``, ``<dir>`` or ``<path>`` + if completion_type: + options = auto_complete_paths(current, completion_type) + options = ((opt, 0) for opt in options) + for option in options: + opt_label = option[0] + # append '=' to options which require args + if option[1] and option[0][:2] == "--": + opt_label += '=' + print(opt_label) + else: + # show main parser options only when necessary + + opts = [i.option_list for i in parser.option_groups] + opts.append(parser.option_list) + opts = (o for it in opts for o in it) + if current.startswith('-'): + for opt in opts: + if opt.help != optparse.SUPPRESS_HELP: + subcommands += opt._long_opts + opt._short_opts + else: + # get completion type given cwords and all available options + completion_type = get_path_completion_type(cwords, cword, opts) + if completion_type: + subcommands = auto_complete_paths(current, completion_type) + + print(' '.join([x for x in subcommands if x.startswith(current)])) + sys.exit(1) + + +def get_path_completion_type(cwords, cword, opts): + """Get the type of path completion (``file``, ``dir``, ``path`` or None) + + :param cwords: same as the environmental variable ``COMP_WORDS`` + :param cword: same as the environmental variable ``COMP_CWORD`` + :param opts: The available options to check + :return: path completion type (``file``, ``dir``, ``path`` or None) + """ + if cword < 2 or not cwords[cword - 2].startswith('-'): + return + for opt in opts: + if opt.help == optparse.SUPPRESS_HELP: + continue + for o in str(opt).split('/'): + if cwords[cword - 2].split('=')[0] == o: + if not opt.metavar or any( + x in ('path', 'file', 'dir') + for x in opt.metavar.split('/')): + return opt.metavar + + +def auto_complete_paths(current, completion_type): + """If ``completion_type`` is ``file`` or ``path``, list all regular files + and directories starting with ``current``; otherwise only list directories + starting with ``current``. + + :param current: The word to be completed + :param completion_type: path completion type(`file`, `path` or `dir`)i + :return: A generator of regular files and/or directories + """ + directory, filename = os.path.split(current) + current_path = os.path.abspath(directory) + # Don't complete paths if they can't be accessed + if not os.access(current_path, os.R_OK): + return + filename = os.path.normcase(filename) + # list all files that start with ``filename`` + file_list = (x for x in os.listdir(current_path) + if os.path.normcase(x).startswith(filename)) + for f in file_list: + opt = os.path.join(current_path, f) + comp_file = os.path.normcase(os.path.join(directory, f)) + # complete regular files when there is not ``<dir>`` after option + # complete directories when there is ``<file>``, ``<path>`` or + # ``<dir>``after option + if completion_type != 'dir' and os.path.isfile(opt): + yield comp_file + elif os.path.isdir(opt): + yield os.path.join(comp_file, '') diff --git a/venv/Lib/site-packages/pip/_internal/cli/base_command.py b/venv/Lib/site-packages/pip/_internal/cli/base_command.py new file mode 100644 index 0000000..dac4b05 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/cli/base_command.py @@ -0,0 +1,278 @@ +"""Base Command class, and related routines""" +from __future__ import absolute_import + +import logging +import logging.config +import optparse +import os +import sys + +from pip._internal.cli import cmdoptions +from pip._internal.cli.parser import ( + ConfigOptionParser, UpdatingDefaultsHelpFormatter, +) +from pip._internal.cli.status_codes import ( + ERROR, PREVIOUS_BUILD_DIR_ERROR, SUCCESS, UNKNOWN_ERROR, + VIRTUALENV_NOT_FOUND, +) +from pip._internal.download import PipSession +from pip._internal.exceptions import ( + BadCommand, CommandError, InstallationError, PreviousBuildDirError, + UninstallationError, +) +from pip._internal.index import PackageFinder +from pip._internal.locations import running_under_virtualenv +from pip._internal.req.constructors import ( + install_req_from_editable, install_req_from_line, +) +from pip._internal.req.req_file import parse_requirements +from pip._internal.utils.logging import setup_logging +from pip._internal.utils.misc import get_prog, normalize_path +from pip._internal.utils.outdated import pip_version_check +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional # noqa: F401 + +__all__ = ['Command'] + +logger = logging.getLogger(__name__) + + +class Command(object): + name = None # type: Optional[str] + usage = None # type: Optional[str] + hidden = False # type: bool + ignore_require_venv = False # type: bool + + def __init__(self, isolated=False): + parser_kw = { + 'usage': self.usage, + 'prog': '%s %s' % (get_prog(), self.name), + 'formatter': UpdatingDefaultsHelpFormatter(), + 'add_help_option': False, + 'name': self.name, + 'description': self.__doc__, + 'isolated': isolated, + } + + self.parser = ConfigOptionParser(**parser_kw) + + # Commands should add options to this option group + optgroup_name = '%s Options' % self.name.capitalize() + self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name) + + # Add the general options + gen_opts = cmdoptions.make_option_group( + cmdoptions.general_group, + self.parser, + ) + self.parser.add_option_group(gen_opts) + + def _build_session(self, options, retries=None, timeout=None): + session = PipSession( + cache=( + normalize_path(os.path.join(options.cache_dir, "http")) + if options.cache_dir else None + ), + retries=retries if retries is not None else options.retries, + insecure_hosts=options.trusted_hosts, + ) + + # Handle custom ca-bundles from the user + if options.cert: + session.verify = options.cert + + # Handle SSL client certificate + if options.client_cert: + session.cert = options.client_cert + + # Handle timeouts + if options.timeout or timeout: + session.timeout = ( + timeout if timeout is not None else options.timeout + ) + + # Handle configured proxies + if options.proxy: + session.proxies = { + "http": options.proxy, + "https": options.proxy, + } + + # Determine if we can prompt the user for authentication or not + session.auth.prompting = not options.no_input + + return session + + def parse_args(self, args): + # factored out for testability + return self.parser.parse_args(args) + + def main(self, args): + options, args = self.parse_args(args) + + # Set verbosity so that it can be used elsewhere. + self.verbosity = options.verbose - options.quiet + + setup_logging( + verbosity=self.verbosity, + no_color=options.no_color, + user_log_file=options.log, + ) + + # TODO: Try to get these passing down from the command? + # without resorting to os.environ to hold these. + # This also affects isolated builds and it should. + + if options.no_input: + os.environ['PIP_NO_INPUT'] = '1' + + if options.exists_action: + os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action) + + if options.require_venv and not self.ignore_require_venv: + # If a venv is required check if it can really be found + if not running_under_virtualenv(): + logger.critical( + 'Could not find an activated virtualenv (required).' + ) + sys.exit(VIRTUALENV_NOT_FOUND) + + try: + status = self.run(options, args) + # FIXME: all commands should return an exit status + # and when it is done, isinstance is not needed anymore + if isinstance(status, int): + return status + except PreviousBuildDirError as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return PREVIOUS_BUILD_DIR_ERROR + except (InstallationError, UninstallationError, BadCommand) as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except CommandError as exc: + logger.critical('ERROR: %s', exc) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except KeyboardInterrupt: + logger.critical('Operation cancelled by user') + logger.debug('Exception information:', exc_info=True) + + return ERROR + except BaseException: + logger.critical('Exception:', exc_info=True) + + return UNKNOWN_ERROR + finally: + allow_version_check = ( + # Does this command have the index_group options? + hasattr(options, "no_index") and + # Is this command allowed to perform this check? + not (options.disable_pip_version_check or options.no_index) + ) + # Check if we're using the latest version of pip available + if allow_version_check: + session = self._build_session( + options, + retries=0, + timeout=min(5, options.timeout) + ) + with session: + pip_version_check(session, options) + + # Shutdown the logging module + logging.shutdown() + + return SUCCESS + + +class RequirementCommand(Command): + + @staticmethod + def populate_requirement_set(requirement_set, args, options, finder, + session, name, wheel_cache): + """ + Marshal cmd line args into a requirement set. + """ + # NOTE: As a side-effect, options.require_hashes and + # requirement_set.require_hashes may be updated + + for filename in options.constraints: + for req_to_add in parse_requirements( + filename, + constraint=True, finder=finder, options=options, + session=session, wheel_cache=wheel_cache): + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + + for req in args: + req_to_add = install_req_from_line( + req, None, isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + + for req in options.editables: + req_to_add = install_req_from_editable( + req, + isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + + for filename in options.requirements: + for req_to_add in parse_requirements( + filename, + finder=finder, options=options, session=session, + wheel_cache=wheel_cache): + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + # If --require-hashes was a line in a requirements file, tell + # RequirementSet about it: + requirement_set.require_hashes = options.require_hashes + + if not (args or options.editables or options.requirements): + opts = {'name': name} + if options.find_links: + raise CommandError( + 'You must give at least one requirement to %(name)s ' + '(maybe you meant "pip %(name)s %(links)s"?)' % + dict(opts, links=' '.join(options.find_links))) + else: + raise CommandError( + 'You must give at least one requirement to %(name)s ' + '(see "pip help %(name)s")' % opts) + + def _build_package_finder(self, options, session, + platform=None, python_versions=None, + abi=None, implementation=None): + """ + Create a package finder appropriate to this requirement command. + """ + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + return PackageFinder( + find_links=options.find_links, + format_control=options.format_control, + index_urls=index_urls, + trusted_hosts=options.trusted_hosts, + allow_all_prereleases=options.pre, + process_dependency_links=options.process_dependency_links, + session=session, + platform=platform, + versions=python_versions, + abi=abi, + implementation=implementation, + prefer_binary=options.prefer_binary, + ) diff --git a/venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py b/venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py new file mode 100644 index 0000000..3033cd4 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py @@ -0,0 +1,714 @@ +""" +shared options and groups + +The principle here is to define options once, but *not* instantiate them +globally. One reason being that options with action='append' can carry state +between parses. pip parses general options twice internally, and shouldn't +pass on state. To be consistent, all options will follow this design. + +""" +from __future__ import absolute_import + +import warnings +from functools import partial +from optparse import SUPPRESS_HELP, Option, OptionGroup + +from pip._internal.exceptions import CommandError +from pip._internal.locations import USER_CACHE_DIR, src_prefix +from pip._internal.models.format_control import FormatControl +from pip._internal.models.index import PyPI +from pip._internal.utils.hashes import STRONG_HASHES +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.ui import BAR_TYPES + +if MYPY_CHECK_RUNNING: + from typing import Any # noqa: F401 + + +def make_option_group(group, parser): + """ + Return an OptionGroup object + group -- assumed to be dict with 'name' and 'options' keys + parser -- an optparse Parser + """ + option_group = OptionGroup(parser, group['name']) + for option in group['options']: + option_group.add_option(option()) + return option_group + + +def check_install_build_global(options, check_options=None): + """Disable wheels if per-setup.py call options are set. + + :param options: The OptionParser options to update. + :param check_options: The options to check, if not supplied defaults to + options. + """ + if check_options is None: + check_options = options + + def getname(n): + return getattr(check_options, n, None) + names = ["build_options", "global_options", "install_options"] + if any(map(getname, names)): + control = options.format_control + control.disallow_binaries() + warnings.warn( + 'Disabling all use of wheels due to the use of --build-options ' + '/ --global-options / --install-options.', stacklevel=2, + ) + + +def check_dist_restriction(options, check_target=False): + """Function for determining if custom platform options are allowed. + + :param options: The OptionParser options. + :param check_target: Whether or not to check if --target is being used. + """ + dist_restriction_set = any([ + options.python_version, + options.platform, + options.abi, + options.implementation, + ]) + + binary_only = FormatControl(set(), {':all:'}) + sdist_dependencies_allowed = ( + options.format_control != binary_only and + not options.ignore_dependencies + ) + + # Installations or downloads using dist restrictions must not combine + # source distributions and dist-specific wheels, as they are not + # gauranteed to be locally compatible. + if dist_restriction_set and sdist_dependencies_allowed: + raise CommandError( + "When restricting platform and interpreter constraints using " + "--python-version, --platform, --abi, or --implementation, " + "either --no-deps must be set, or --only-binary=:all: must be " + "set and --no-binary must not be set (or must be set to " + ":none:)." + ) + + if check_target: + if dist_restriction_set and not options.target_dir: + raise CommandError( + "Can not use any platform or abi specific options unless " + "installing via '--target'" + ) + + +########### +# options # +########### + +help_ = partial( + Option, + '-h', '--help', + dest='help', + action='help', + help='Show help.', +) # type: Any + +isolated_mode = partial( + Option, + "--isolated", + dest="isolated_mode", + action="store_true", + default=False, + help=( + "Run pip in an isolated mode, ignoring environment variables and user " + "configuration." + ), +) + +require_virtualenv = partial( + Option, + # Run only if inside a virtualenv, bail if not. + '--require-virtualenv', '--require-venv', + dest='require_venv', + action='store_true', + default=False, + help=SUPPRESS_HELP +) # type: Any + +verbose = partial( + Option, + '-v', '--verbose', + dest='verbose', + action='count', + default=0, + help='Give more output. Option is additive, and can be used up to 3 times.' +) + +no_color = partial( + Option, + '--no-color', + dest='no_color', + action='store_true', + default=False, + help="Suppress colored output", +) + +version = partial( + Option, + '-V', '--version', + dest='version', + action='store_true', + help='Show version and exit.', +) # type: Any + +quiet = partial( + Option, + '-q', '--quiet', + dest='quiet', + action='count', + default=0, + help=( + 'Give less output. Option is additive, and can be used up to 3' + ' times (corresponding to WARNING, ERROR, and CRITICAL logging' + ' levels).' + ), +) # type: Any + +progress_bar = partial( + Option, + '--progress-bar', + dest='progress_bar', + type='choice', + choices=list(BAR_TYPES.keys()), + default='on', + help=( + 'Specify type of progress to be displayed [' + + '|'.join(BAR_TYPES.keys()) + '] (default: %default)' + ), +) # type: Any + +log = partial( + Option, + "--log", "--log-file", "--local-log", + dest="log", + metavar="path", + help="Path to a verbose appending log." +) # type: Any + +no_input = partial( + Option, + # Don't ask for input + '--no-input', + dest='no_input', + action='store_true', + default=False, + help=SUPPRESS_HELP +) # type: Any + +proxy = partial( + Option, + '--proxy', + dest='proxy', + type='str', + default='', + help="Specify a proxy in the form [user:passwd@]proxy.server:port." +) # type: Any + +retries = partial( + Option, + '--retries', + dest='retries', + type='int', + default=5, + help="Maximum number of retries each connection should attempt " + "(default %default times).", +) # type: Any + +timeout = partial( + Option, + '--timeout', '--default-timeout', + metavar='sec', + dest='timeout', + type='float', + default=15, + help='Set the socket timeout (default %default seconds).', +) # type: Any + +skip_requirements_regex = partial( + Option, + # A regex to be used to skip requirements + '--skip-requirements-regex', + dest='skip_requirements_regex', + type='str', + default='', + help=SUPPRESS_HELP, +) # type: Any + + +def exists_action(): + return Option( + # Option when path already exist + '--exists-action', + dest='exists_action', + type='choice', + choices=['s', 'i', 'w', 'b', 'a'], + default=[], + action='append', + metavar='action', + help="Default action when a path already exists: " + "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort).", + ) + + +cert = partial( + Option, + '--cert', + dest='cert', + type='str', + metavar='path', + help="Path to alternate CA bundle.", +) # type: Any + +client_cert = partial( + Option, + '--client-cert', + dest='client_cert', + type='str', + default=None, + metavar='path', + help="Path to SSL client certificate, a single file containing the " + "private key and the certificate in PEM format.", +) # type: Any + +index_url = partial( + Option, + '-i', '--index-url', '--pypi-url', + dest='index_url', + metavar='URL', + default=PyPI.simple_url, + help="Base URL of Python Package Index (default %default). " + "This should point to a repository compliant with PEP 503 " + "(the simple repository API) or a local directory laid out " + "in the same format.", +) # type: Any + + +def extra_index_url(): + return Option( + '--extra-index-url', + dest='extra_index_urls', + metavar='URL', + action='append', + default=[], + help="Extra URLs of package indexes to use in addition to " + "--index-url. Should follow the same rules as " + "--index-url.", + ) + + +no_index = partial( + Option, + '--no-index', + dest='no_index', + action='store_true', + default=False, + help='Ignore package index (only looking at --find-links URLs instead).', +) # type: Any + + +def find_links(): + return Option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='url', + help="If a url or path to an html file, then parse for links to " + "archives. If a local path or file:// url that's a directory, " + "then look for archives in the directory listing.", + ) + + +def trusted_host(): + return Option( + "--trusted-host", + dest="trusted_hosts", + action="append", + metavar="HOSTNAME", + default=[], + help="Mark this host as trusted, even though it does not have valid " + "or any HTTPS.", + ) + + +# Remove after 1.5 +process_dependency_links = partial( + Option, + "--process-dependency-links", + dest="process_dependency_links", + action="store_true", + default=False, + help="Enable the processing of dependency links.", +) # type: Any + + +def constraints(): + return Option( + '-c', '--constraint', + dest='constraints', + action='append', + default=[], + metavar='file', + help='Constrain versions using the given constraints file. ' + 'This option can be used multiple times.' + ) + + +def requirements(): + return Option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Install from the given requirements file. ' + 'This option can be used multiple times.' + ) + + +def editable(): + return Option( + '-e', '--editable', + dest='editables', + action='append', + default=[], + metavar='path/url', + help=('Install a project in editable mode (i.e. setuptools ' + '"develop mode") from a local project path or a VCS url.'), + ) + + +src = partial( + Option, + '--src', '--source', '--source-dir', '--source-directory', + dest='src_dir', + metavar='dir', + default=src_prefix, + help='Directory to check out editable projects into. ' + 'The default in a virtualenv is "<venv path>/src". ' + 'The default for global installs is "<current dir>/src".' +) # type: Any + + +def _get_format_control(values, option): + """Get a format_control object.""" + return getattr(values, option.dest) + + +def _handle_no_binary(option, opt_str, value, parser): + existing = _get_format_control(parser.values, option) + FormatControl.handle_mutual_excludes( + value, existing.no_binary, existing.only_binary, + ) + + +def _handle_only_binary(option, opt_str, value, parser): + existing = _get_format_control(parser.values, option) + FormatControl.handle_mutual_excludes( + value, existing.only_binary, existing.no_binary, + ) + + +def no_binary(): + format_control = FormatControl(set(), set()) + return Option( + "--no-binary", dest="format_control", action="callback", + callback=_handle_no_binary, type="str", + default=format_control, + help="Do not use binary packages. Can be supplied multiple times, and " + "each time adds to the existing value. Accepts either :all: to " + "disable all binary packages, :none: to empty the set, or one or " + "more package names with commas between them. Note that some " + "packages are tricky to compile and may fail to install when " + "this option is used on them.", + ) + + +def only_binary(): + format_control = FormatControl(set(), set()) + return Option( + "--only-binary", dest="format_control", action="callback", + callback=_handle_only_binary, type="str", + default=format_control, + help="Do not use source packages. Can be supplied multiple times, and " + "each time adds to the existing value. Accepts either :all: to " + "disable all source packages, :none: to empty the set, or one or " + "more package names with commas between them. Packages without " + "binary distributions will fail to install when this option is " + "used on them.", + ) + + +platform = partial( + Option, + '--platform', + dest='platform', + metavar='platform', + default=None, + help=("Only use wheels compatible with <platform>. " + "Defaults to the platform of the running system."), +) + + +python_version = partial( + Option, + '--python-version', + dest='python_version', + metavar='python_version', + default=None, + help=("Only use wheels compatible with Python " + "interpreter version <version>. If not specified, then the " + "current system interpreter minor version is used. A major " + "version (e.g. '2') can be specified to match all " + "minor revs of that major version. A minor version " + "(e.g. '34') can also be specified."), +) + + +implementation = partial( + Option, + '--implementation', + dest='implementation', + metavar='implementation', + default=None, + help=("Only use wheels compatible with Python " + "implementation <implementation>, e.g. 'pp', 'jy', 'cp', " + " or 'ip'. If not specified, then the current " + "interpreter implementation is used. Use 'py' to force " + "implementation-agnostic wheels."), +) + + +abi = partial( + Option, + '--abi', + dest='abi', + metavar='abi', + default=None, + help=("Only use wheels compatible with Python " + "abi <abi>, e.g. 'pypy_41'. If not specified, then the " + "current interpreter abi tag is used. Generally " + "you will need to specify --implementation, " + "--platform, and --python-version when using " + "this option."), +) + + +def prefer_binary(): + return Option( + "--prefer-binary", + dest="prefer_binary", + action="store_true", + default=False, + help="Prefer older binary packages over newer source packages." + ) + + +cache_dir = partial( + Option, + "--cache-dir", + dest="cache_dir", + default=USER_CACHE_DIR, + metavar="dir", + help="Store the cache data in <dir>." +) + +no_cache = partial( + Option, + "--no-cache-dir", + dest="cache_dir", + action="store_false", + help="Disable the cache.", +) + +no_deps = partial( + Option, + '--no-deps', '--no-dependencies', + dest='ignore_dependencies', + action='store_true', + default=False, + help="Don't install package dependencies.", +) # type: Any + +build_dir = partial( + Option, + '-b', '--build', '--build-dir', '--build-directory', + dest='build_dir', + metavar='dir', + help='Directory to unpack packages into and build in. Note that ' + 'an initial build still takes place in a temporary directory. ' + 'The location of temporary directories can be controlled by setting ' + 'the TMPDIR environment variable (TEMP on Windows) appropriately. ' + 'When passed, build directories are not cleaned in case of failures.' +) # type: Any + +ignore_requires_python = partial( + Option, + '--ignore-requires-python', + dest='ignore_requires_python', + action='store_true', + help='Ignore the Requires-Python information.' +) # type: Any + +no_build_isolation = partial( + Option, + '--no-build-isolation', + dest='build_isolation', + action='store_false', + default=True, + help='Disable isolation when building a modern source distribution. ' + 'Build dependencies specified by PEP 518 must be already installed ' + 'if this option is used.' +) # type: Any + +install_options = partial( + Option, + '--install-option', + dest='install_options', + action='append', + metavar='options', + help="Extra arguments to be supplied to the setup.py install " + "command (use like --install-option=\"--install-scripts=/usr/local/" + "bin\"). Use multiple --install-option options to pass multiple " + "options to setup.py install. If you are using an option with a " + "directory path, be sure to use absolute path.", +) # type: Any + +global_options = partial( + Option, + '--global-option', + dest='global_options', + action='append', + metavar='options', + help="Extra global options to be supplied to the setup.py " + "call before the install command.", +) # type: Any + +no_clean = partial( + Option, + '--no-clean', + action='store_true', + default=False, + help="Don't clean up build directories." +) # type: Any + +pre = partial( + Option, + '--pre', + action='store_true', + default=False, + help="Include pre-release and development versions. By default, " + "pip only finds stable versions.", +) # type: Any + +disable_pip_version_check = partial( + Option, + "--disable-pip-version-check", + dest="disable_pip_version_check", + action="store_true", + default=False, + help="Don't periodically check PyPI to determine whether a new version " + "of pip is available for download. Implied with --no-index.", +) # type: Any + + +# Deprecated, Remove later +always_unzip = partial( + Option, + '-Z', '--always-unzip', + dest='always_unzip', + action='store_true', + help=SUPPRESS_HELP, +) # type: Any + + +def _merge_hash(option, opt_str, value, parser): + """Given a value spelled "algo:digest", append the digest to a list + pointed to in a dict by the algo name.""" + if not parser.values.hashes: + parser.values.hashes = {} + try: + algo, digest = value.split(':', 1) + except ValueError: + parser.error('Arguments to %s must be a hash name ' + 'followed by a value, like --hash=sha256:abcde...' % + opt_str) + if algo not in STRONG_HASHES: + parser.error('Allowed hash algorithms for %s are %s.' % + (opt_str, ', '.join(STRONG_HASHES))) + parser.values.hashes.setdefault(algo, []).append(digest) + + +hash = partial( + Option, + '--hash', + # Hash values eventually end up in InstallRequirement.hashes due to + # __dict__ copying in process_line(). + dest='hashes', + action='callback', + callback=_merge_hash, + type='string', + help="Verify that the package's archive matches this " + 'hash before installing. Example: --hash=sha256:abcdef...', +) # type: Any + + +require_hashes = partial( + Option, + '--require-hashes', + dest='require_hashes', + action='store_true', + default=False, + help='Require a hash to check each requirement against, for ' + 'repeatable installs. This option is implied when any package in a ' + 'requirements file has a --hash option.', +) # type: Any + + +########## +# groups # +########## + +general_group = { + 'name': 'General Options', + 'options': [ + help_, + isolated_mode, + require_virtualenv, + verbose, + version, + quiet, + log, + no_input, + proxy, + retries, + timeout, + skip_requirements_regex, + exists_action, + trusted_host, + cert, + client_cert, + cache_dir, + no_cache, + disable_pip_version_check, + no_color, + ] +} + +index_group = { + 'name': 'Package Index Options', + 'options': [ + index_url, + extra_index_url, + no_index, + find_links, + process_dependency_links, + ] +} diff --git a/venv/Lib/site-packages/pip/_internal/cli/main_parser.py b/venv/Lib/site-packages/pip/_internal/cli/main_parser.py new file mode 100644 index 0000000..1774a6b --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/cli/main_parser.py @@ -0,0 +1,96 @@ +"""A single place for constructing and exposing the main parser +""" + +import os +import sys + +from pip import __version__ +from pip._internal.cli import cmdoptions +from pip._internal.cli.parser import ( + ConfigOptionParser, UpdatingDefaultsHelpFormatter, +) +from pip._internal.commands import ( + commands_dict, get_similar_commands, get_summaries, +) +from pip._internal.exceptions import CommandError +from pip._internal.utils.misc import get_prog + +__all__ = ["create_main_parser", "parse_command"] + + +def create_main_parser(): + """Creates and returns the main parser for pip's CLI + """ + + parser_kw = { + 'usage': '\n%prog <command> [options]', + 'add_help_option': False, + 'formatter': UpdatingDefaultsHelpFormatter(), + 'name': 'global', + 'prog': get_prog(), + } + + parser = ConfigOptionParser(**parser_kw) + parser.disable_interspersed_args() + + pip_pkg_dir = os.path.abspath(os.path.join( + os.path.dirname(__file__), "..", "..", + )) + parser.version = 'pip %s from %s (python %s)' % ( + __version__, pip_pkg_dir, sys.version[:3], + ) + + # add the general options + gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser) + parser.add_option_group(gen_opts) + + parser.main = True # so the help formatter knows + + # create command listing for description + command_summaries = get_summaries() + description = [''] + ['%-27s %s' % (i, j) for i, j in command_summaries] + parser.description = '\n'.join(description) + + return parser + + +def parse_command(args): + parser = create_main_parser() + + # Note: parser calls disable_interspersed_args(), so the result of this + # call is to split the initial args into the general options before the + # subcommand and everything else. + # For example: + # args: ['--timeout=5', 'install', '--user', 'INITools'] + # general_options: ['--timeout==5'] + # args_else: ['install', '--user', 'INITools'] + general_options, args_else = parser.parse_args(args) + + # --version + if general_options.version: + sys.stdout.write(parser.version) + sys.stdout.write(os.linesep) + sys.exit() + + # pip || pip help -> print_help() + if not args_else or (args_else[0] == 'help' and len(args_else) == 1): + parser.print_help() + sys.exit() + + # the subcommand name + cmd_name = args_else[0] + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "%s"' % cmd_name] + if guess: + msg.append('maybe you meant "%s"' % guess) + + raise CommandError(' - '.join(msg)) + + # all the args without the subcommand + cmd_args = args[:] + cmd_args.remove(cmd_name) + + return cmd_name, cmd_args diff --git a/venv/Lib/site-packages/pip/_internal/cli/parser.py b/venv/Lib/site-packages/pip/_internal/cli/parser.py new file mode 100644 index 0000000..e1eaac4 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/cli/parser.py @@ -0,0 +1,261 @@ +"""Base option parser setup""" +from __future__ import absolute_import + +import logging +import optparse +import sys +import textwrap +from distutils.util import strtobool + +from pip._vendor.six import string_types + +from pip._internal.cli.status_codes import UNKNOWN_ERROR +from pip._internal.configuration import Configuration, ConfigurationError +from pip._internal.utils.compat import get_terminal_size + +logger = logging.getLogger(__name__) + + +class PrettyHelpFormatter(optparse.IndentedHelpFormatter): + """A prettier/less verbose help formatter for optparse.""" + + def __init__(self, *args, **kwargs): + # help position must be aligned with __init__.parseopts.description + kwargs['max_help_position'] = 30 + kwargs['indent_increment'] = 1 + kwargs['width'] = get_terminal_size()[0] - 2 + optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs) + + def format_option_strings(self, option): + return self._format_option_strings(option, ' <%s>', ', ') + + def _format_option_strings(self, option, mvarfmt=' <%s>', optsep=', '): + """ + Return a comma-separated list of option strings and metavars. + + :param option: tuple of (short opt, long opt), e.g: ('-f', '--format') + :param mvarfmt: metavar format string - evaluated as mvarfmt % metavar + :param optsep: separator + """ + opts = [] + + if option._short_opts: + opts.append(option._short_opts[0]) + if option._long_opts: + opts.append(option._long_opts[0]) + if len(opts) > 1: + opts.insert(1, optsep) + + if option.takes_value(): + metavar = option.metavar or option.dest.lower() + opts.append(mvarfmt % metavar.lower()) + + return ''.join(opts) + + def format_heading(self, heading): + if heading == 'Options': + return '' + return heading + ':\n' + + def format_usage(self, usage): + """ + Ensure there is only one newline between usage and the first heading + if there is no description. + """ + msg = '\nUsage: %s\n' % self.indent_lines(textwrap.dedent(usage), " ") + return msg + + def format_description(self, description): + # leave full control over description to us + if description: + if hasattr(self.parser, 'main'): + label = 'Commands' + else: + label = 'Description' + # some doc strings have initial newlines, some don't + description = description.lstrip('\n') + # some doc strings have final newlines and spaces, some don't + description = description.rstrip() + # dedent, then reindent + description = self.indent_lines(textwrap.dedent(description), " ") + description = '%s:\n%s\n' % (label, description) + return description + else: + return '' + + def format_epilog(self, epilog): + # leave full control over epilog to us + if epilog: + return epilog + else: + return '' + + def indent_lines(self, text, indent): + new_lines = [indent + line for line in text.split('\n')] + return "\n".join(new_lines) + + +class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): + """Custom help formatter for use in ConfigOptionParser. + + This is updates the defaults before expanding them, allowing + them to show up correctly in the help listing. + """ + + def expand_default(self, option): + if self.parser is not None: + self.parser._update_defaults(self.parser.defaults) + return optparse.IndentedHelpFormatter.expand_default(self, option) + + +class CustomOptionParser(optparse.OptionParser): + + def insert_option_group(self, idx, *args, **kwargs): + """Insert an OptionGroup at a given position.""" + group = self.add_option_group(*args, **kwargs) + + self.option_groups.pop() + self.option_groups.insert(idx, group) + + return group + + @property + def option_list_all(self): + """Get a list of all options, including those in option groups.""" + res = self.option_list[:] + for i in self.option_groups: + res.extend(i.option_list) + + return res + + +class ConfigOptionParser(CustomOptionParser): + """Custom option parser which updates its defaults by checking the + configuration files and environmental variables""" + + def __init__(self, *args, **kwargs): + self.name = kwargs.pop('name') + + isolated = kwargs.pop("isolated", False) + self.config = Configuration(isolated) + + assert self.name + optparse.OptionParser.__init__(self, *args, **kwargs) + + def check_default(self, option, key, val): + try: + return option.check_value(key, val) + except optparse.OptionValueError as exc: + print("An error occurred during configuration: %s" % exc) + sys.exit(3) + + def _get_ordered_configuration_items(self): + # Configuration gives keys in an unordered manner. Order them. + override_order = ["global", self.name, ":env:"] + + # Pool the options into different groups + section_items = {name: [] for name in override_order} + for section_key, val in self.config.items(): + # ignore empty values + if not val: + logger.debug( + "Ignoring configuration key '%s' as it's value is empty.", + section_key + ) + continue + + section, key = section_key.split(".", 1) + if section in override_order: + section_items[section].append((key, val)) + + # Yield each group in their override order + for section in override_order: + for key, val in section_items[section]: + yield key, val + + def _update_defaults(self, defaults): + """Updates the given defaults with values from the config files and + the environ. Does a little special handling for certain types of + options (lists).""" + + # Accumulate complex default state. + self.values = optparse.Values(self.defaults) + late_eval = set() + # Then set the options with those values + for key, val in self._get_ordered_configuration_items(): + # '--' because configuration supports only long names + option = self.get_option('--' + key) + + # Ignore options not present in this parser. E.g. non-globals put + # in [global] by users that want them to apply to all applicable + # commands. + if option is None: + continue + + if option.action in ('store_true', 'store_false', 'count'): + try: + val = strtobool(val) + except ValueError: + error_msg = invalid_config_error_message( + option.action, key, val + ) + self.error(error_msg) + + elif option.action == 'append': + val = val.split() + val = [self.check_default(option, key, v) for v in val] + elif option.action == 'callback': + late_eval.add(option.dest) + opt_str = option.get_opt_string() + val = option.convert_value(opt_str, val) + # From take_action + args = option.callback_args or () + kwargs = option.callback_kwargs or {} + option.callback(option, opt_str, val, self, *args, **kwargs) + else: + val = self.check_default(option, key, val) + + defaults[option.dest] = val + + for key in late_eval: + defaults[key] = getattr(self.values, key) + self.values = None + return defaults + + def get_default_values(self): + """Overriding to make updating the defaults after instantiation of + the option parser possible, _update_defaults() does the dirty work.""" + if not self.process_default_values: + # Old, pre-Optik 1.5 behaviour. + return optparse.Values(self.defaults) + + # Load the configuration, or error out in case of an error + try: + self.config.load() + except ConfigurationError as err: + self.exit(UNKNOWN_ERROR, str(err)) + + defaults = self._update_defaults(self.defaults.copy()) # ours + for option in self._get_all_options(): + default = defaults.get(option.dest) + if isinstance(default, string_types): + opt_str = option.get_opt_string() + defaults[option.dest] = option.check_value(opt_str, default) + return optparse.Values(defaults) + + def error(self, msg): + self.print_usage(sys.stderr) + self.exit(UNKNOWN_ERROR, "%s\n" % msg) + + +def invalid_config_error_message(action, key, val): + """Returns a better error message when invalid configuration option + is provided.""" + if action in ('store_true', 'store_false'): + return ("{0} is not a valid value for {1} option, " + "please specify a boolean value like yes/no, " + "true/false or 1/0 instead.").format(val, key) + + return ("{0} is not a valid value for {1} option, " + "please specify a numerical value like 1/0 " + "instead.").format(val, key) diff --git a/venv/Lib/site-packages/pip/_internal/cli/status_codes.py b/venv/Lib/site-packages/pip/_internal/cli/status_codes.py new file mode 100644 index 0000000..275360a --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/cli/status_codes.py @@ -0,0 +1,8 @@ +from __future__ import absolute_import + +SUCCESS = 0 +ERROR = 1 +UNKNOWN_ERROR = 2 +VIRTUALENV_NOT_FOUND = 3 +PREVIOUS_BUILD_DIR_ERROR = 4 +NO_MATCHES_FOUND = 23 diff --git a/venv/Lib/site-packages/pip/_internal/commands/__init__.py b/venv/Lib/site-packages/pip/_internal/commands/__init__.py new file mode 100644 index 0000000..c7d1da3 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/__init__.py @@ -0,0 +1,79 @@ +""" +Package containing all pip commands +""" +from __future__ import absolute_import + +from pip._internal.commands.completion import CompletionCommand +from pip._internal.commands.configuration import ConfigurationCommand +from pip._internal.commands.download import DownloadCommand +from pip._internal.commands.freeze import FreezeCommand +from pip._internal.commands.hash import HashCommand +from pip._internal.commands.help import HelpCommand +from pip._internal.commands.list import ListCommand +from pip._internal.commands.check import CheckCommand +from pip._internal.commands.search import SearchCommand +from pip._internal.commands.show import ShowCommand +from pip._internal.commands.install import InstallCommand +from pip._internal.commands.uninstall import UninstallCommand +from pip._internal.commands.wheel import WheelCommand + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Type # noqa: F401 + from pip._internal.cli.base_command import Command # noqa: F401 + +commands_order = [ + InstallCommand, + DownloadCommand, + UninstallCommand, + FreezeCommand, + ListCommand, + ShowCommand, + CheckCommand, + ConfigurationCommand, + SearchCommand, + WheelCommand, + HashCommand, + CompletionCommand, + HelpCommand, +] # type: List[Type[Command]] + +commands_dict = {c.name: c for c in commands_order} + + +def get_summaries(ordered=True): + """Yields sorted (command name, command summary) tuples.""" + + if ordered: + cmditems = _sort_commands(commands_dict, commands_order) + else: + cmditems = commands_dict.items() + + for name, command_class in cmditems: + yield (name, command_class.summary) + + +def get_similar_commands(name): + """Command name auto-correct.""" + from difflib import get_close_matches + + name = name.lower() + + close_commands = get_close_matches(name, commands_dict.keys()) + + if close_commands: + return close_commands[0] + else: + return False + + +def _sort_commands(cmddict, order): + def keyfn(key): + try: + return order.index(key[1]) + except ValueError: + # unordered items should come last + return 0xff + + return sorted(cmddict.items(), key=keyfn) diff --git a/venv/Lib/site-packages/pip/_internal/commands/check.py b/venv/Lib/site-packages/pip/_internal/commands/check.py new file mode 100644 index 0000000..1be3ec2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/check.py @@ -0,0 +1,41 @@ +import logging + +from pip._internal.cli.base_command import Command +from pip._internal.operations.check import ( + check_package_set, create_package_set_from_installed, +) + +logger = logging.getLogger(__name__) + + +class CheckCommand(Command): + """Verify installed packages have compatible dependencies.""" + name = 'check' + usage = """ + %prog [options]""" + summary = 'Verify installed packages have compatible dependencies.' + + def run(self, options, args): + package_set = create_package_set_from_installed() + missing, conflicting = check_package_set(package_set) + + for project_name in missing: + version = package_set[project_name].version + for dependency in missing[project_name]: + logger.info( + "%s %s requires %s, which is not installed.", + project_name, version, dependency[0], + ) + + for project_name in conflicting: + version = package_set[project_name].version + for dep_name, dep_version, req in conflicting[project_name]: + logger.info( + "%s %s has requirement %s, but you have %s %s.", + project_name, version, req, dep_name, dep_version, + ) + + if missing or conflicting: + return 1 + else: + logger.info("No broken requirements found.") diff --git a/venv/Lib/site-packages/pip/_internal/commands/completion.py b/venv/Lib/site-packages/pip/_internal/commands/completion.py new file mode 100644 index 0000000..2fcdd39 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/completion.py @@ -0,0 +1,94 @@ +from __future__ import absolute_import + +import sys +import textwrap + +from pip._internal.cli.base_command import Command +from pip._internal.utils.misc import get_prog + +BASE_COMPLETION = """ +# pip %(shell)s completion start%(script)s# pip %(shell)s completion end +""" + +COMPLETION_SCRIPTS = { + 'bash': """ + _pip_completion() + { + COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \\ + COMP_CWORD=$COMP_CWORD \\ + PIP_AUTO_COMPLETE=1 $1 ) ) + } + complete -o default -F _pip_completion %(prog)s + """, + 'zsh': """ + function _pip_completion { + local words cword + read -Ac words + read -cn cword + reply=( $( COMP_WORDS="$words[*]" \\ + COMP_CWORD=$(( cword-1 )) \\ + PIP_AUTO_COMPLETE=1 $words[1] ) ) + } + compctl -K _pip_completion %(prog)s + """, + 'fish': """ + function __fish_complete_pip + set -lx COMP_WORDS (commandline -o) "" + set -lx COMP_CWORD ( \\ + math (contains -i -- (commandline -t) $COMP_WORDS)-1 \\ + ) + set -lx PIP_AUTO_COMPLETE 1 + string split \\ -- (eval $COMP_WORDS[1]) + end + complete -fa "(__fish_complete_pip)" -c %(prog)s + """, +} + + +class CompletionCommand(Command): + """A helper command to be used for command completion.""" + name = 'completion' + summary = 'A helper command used for command completion.' + ignore_require_venv = True + + def __init__(self, *args, **kw): + super(CompletionCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( + '--bash', '-b', + action='store_const', + const='bash', + dest='shell', + help='Emit completion code for bash') + cmd_opts.add_option( + '--zsh', '-z', + action='store_const', + const='zsh', + dest='shell', + help='Emit completion code for zsh') + cmd_opts.add_option( + '--fish', '-f', + action='store_const', + const='fish', + dest='shell', + help='Emit completion code for fish') + + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + """Prints the completion code of the given shell""" + shells = COMPLETION_SCRIPTS.keys() + shell_options = ['--' + shell for shell in sorted(shells)] + if options.shell in shells: + script = textwrap.dedent( + COMPLETION_SCRIPTS.get(options.shell, '') % { + 'prog': get_prog(), + } + ) + print(BASE_COMPLETION % {'script': script, 'shell': options.shell}) + else: + sys.stderr.write( + 'ERROR: You must pass %s\n' % ' or '.join(shell_options) + ) diff --git a/venv/Lib/site-packages/pip/_internal/commands/configuration.py b/venv/Lib/site-packages/pip/_internal/commands/configuration.py new file mode 100644 index 0000000..826c08d --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/configuration.py @@ -0,0 +1,227 @@ +import logging +import os +import subprocess + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.configuration import Configuration, kinds +from pip._internal.exceptions import PipError +from pip._internal.locations import venv_config_file +from pip._internal.utils.misc import get_prog + +logger = logging.getLogger(__name__) + + +class ConfigurationCommand(Command): + """Manage local and global configuration. + + Subcommands: + + list: List the active configuration (or from the file specified) + edit: Edit the configuration file in an editor + get: Get the value associated with name + set: Set the name=value + unset: Unset the value associated with name + + If none of --user, --global and --venv are passed, a virtual + environment configuration file is used if one is active and the file + exists. Otherwise, all modifications happen on the to the user file by + default. + """ + + name = 'config' + usage = """ + %prog [<file-option>] list + %prog [<file-option>] [--editor <editor-path>] edit + + %prog [<file-option>] get name + %prog [<file-option>] set name value + %prog [<file-option>] unset name + """ + + summary = "Manage local and global configuration." + + def __init__(self, *args, **kwargs): + super(ConfigurationCommand, self).__init__(*args, **kwargs) + + self.configuration = None + + self.cmd_opts.add_option( + '--editor', + dest='editor', + action='store', + default=None, + help=( + 'Editor to use to edit the file. Uses VISUAL or EDITOR ' + 'environment variables if not provided.' + ) + ) + + self.cmd_opts.add_option( + '--global', + dest='global_file', + action='store_true', + default=False, + help='Use the system-wide configuration file only' + ) + + self.cmd_opts.add_option( + '--user', + dest='user_file', + action='store_true', + default=False, + help='Use the user configuration file only' + ) + + self.cmd_opts.add_option( + '--venv', + dest='venv_file', + action='store_true', + default=False, + help='Use the virtualenv configuration file only' + ) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + handlers = { + "list": self.list_values, + "edit": self.open_in_editor, + "get": self.get_name, + "set": self.set_name_value, + "unset": self.unset_name + } + + # Determine action + if not args or args[0] not in handlers: + logger.error("Need an action ({}) to perform.".format( + ", ".join(sorted(handlers))) + ) + return ERROR + + action = args[0] + + # Determine which configuration files are to be loaded + # Depends on whether the command is modifying. + try: + load_only = self._determine_file( + options, need_value=(action in ["get", "set", "unset", "edit"]) + ) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + # Load a new configuration + self.configuration = Configuration( + isolated=options.isolated_mode, load_only=load_only + ) + self.configuration.load() + + # Error handling happens here, not in the action-handlers. + try: + handlers[action](options, args[1:]) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + return SUCCESS + + def _determine_file(self, options, need_value): + file_options = { + kinds.USER: options.user_file, + kinds.GLOBAL: options.global_file, + kinds.VENV: options.venv_file + } + + if sum(file_options.values()) == 0: + if not need_value: + return None + # Default to user, unless there's a virtualenv file. + elif os.path.exists(venv_config_file): + return kinds.VENV + else: + return kinds.USER + elif sum(file_options.values()) == 1: + # There's probably a better expression for this. + return [key for key in file_options if file_options[key]][0] + + raise PipError( + "Need exactly one file to operate upon " + "(--user, --venv, --global) to perform." + ) + + def list_values(self, options, args): + self._get_n_args(args, "list", n=0) + + for key, value in sorted(self.configuration.items()): + logger.info("%s=%r", key, value) + + def get_name(self, options, args): + key = self._get_n_args(args, "get [name]", n=1) + value = self.configuration.get_value(key) + + logger.info("%s", value) + + def set_name_value(self, options, args): + key, value = self._get_n_args(args, "set [name] [value]", n=2) + self.configuration.set_value(key, value) + + self._save_configuration() + + def unset_name(self, options, args): + key = self._get_n_args(args, "unset [name]", n=1) + self.configuration.unset_value(key) + + self._save_configuration() + + def open_in_editor(self, options, args): + editor = self._determine_editor(options) + + fname = self.configuration.get_file_to_edit() + if fname is None: + raise PipError("Could not determine appropriate file.") + + try: + subprocess.check_call([editor, fname]) + except subprocess.CalledProcessError as e: + raise PipError( + "Editor Subprocess exited with exit code {}" + .format(e.returncode) + ) + + def _get_n_args(self, args, example, n): + """Helper to make sure the command got the right number of arguments + """ + if len(args) != n: + msg = ( + 'Got unexpected number of arguments, expected {}. ' + '(example: "{} config {}")' + ).format(n, get_prog(), example) + raise PipError(msg) + + if n == 1: + return args[0] + else: + return args + + def _save_configuration(self): + # We successfully ran a modifying command. Need to save the + # configuration. + try: + self.configuration.save() + except Exception: + logger.error( + "Unable to save configuration. Please report this as a bug.", + exc_info=1 + ) + raise PipError("Internal Error.") + + def _determine_editor(self, options): + if options.editor is not None: + return options.editor + elif "VISUAL" in os.environ: + return os.environ["VISUAL"] + elif "EDITOR" in os.environ: + return os.environ["EDITOR"] + else: + raise PipError("Could not determine editor to use.") diff --git a/venv/Lib/site-packages/pip/_internal/commands/download.py b/venv/Lib/site-packages/pip/_internal/commands/download.py new file mode 100644 index 0000000..b3f3c6e --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/download.py @@ -0,0 +1,174 @@ +from __future__ import absolute_import + +import logging +import os + +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import RequirementCommand +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req import RequirementSet +from pip._internal.req.req_tracker import RequirementTracker +from pip._internal.resolve import Resolver +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.misc import ensure_dir, normalize_path +from pip._internal.utils.temp_dir import TempDirectory + +logger = logging.getLogger(__name__) + + +class DownloadCommand(RequirementCommand): + """ + Download packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports downloading from "requirements files", which provide + an easy way to specify a whole environment to be downloaded. + """ + name = 'download' + + usage = """ + %prog [options] <requirement specifier> [package-index-options] ... + %prog [options] -r <requirements file> [package-index-options] ... + %prog [options] <vcs project url> ... + %prog [options] <local project path> ... + %prog [options] <archive url/path> ...""" + + summary = 'Download packages.' + + def __init__(self, *args, **kw): + super(DownloadCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.build_dir()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.global_options()) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.prefer_binary()) + cmd_opts.add_option(cmdoptions.src()) + cmd_opts.add_option(cmdoptions.pre()) + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + cmd_opts.add_option(cmdoptions.progress_bar()) + cmd_opts.add_option(cmdoptions.no_build_isolation()) + + cmd_opts.add_option( + '-d', '--dest', '--destination-dir', '--destination-directory', + dest='download_dir', + metavar='dir', + default=os.curdir, + help=("Download packages into <dir>."), + ) + + cmd_opts.add_option(cmdoptions.platform()) + cmd_opts.add_option(cmdoptions.python_version()) + cmd_opts.add_option(cmdoptions.implementation()) + cmd_opts.add_option(cmdoptions.abi()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + options.ignore_installed = True + # editable doesn't really make sense for `pip download`, but the bowels + # of the RequirementSet code require that property. + options.editables = [] + + if options.python_version: + python_versions = [options.python_version] + else: + python_versions = None + + cmdoptions.check_dist_restriction(options) + + options.src_dir = os.path.abspath(options.src_dir) + options.download_dir = normalize_path(options.download_dir) + + ensure_dir(options.download_dir) + + with self._build_session(options) as session: + finder = self._build_package_finder( + options=options, + session=session, + platform=options.platform, + python_versions=python_versions, + abi=options.abi, + implementation=options.implementation, + ) + build_delete = (not (options.no_clean or options.build_dir)) + if options.cache_dir and not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "by the current user and caching wheels has been " + "disabled. check the permissions and owner of that " + "directory. If executing pip with sudo, you may want " + "sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + with RequirementTracker() as req_tracker, TempDirectory( + options.build_dir, delete=build_delete, kind="download" + ) as directory: + + requirement_set = RequirementSet( + require_hashes=options.require_hashes, + ) + self.populate_requirement_set( + requirement_set, + args, + options, + finder, + session, + self.name, + None + ) + + preparer = RequirementPreparer( + build_dir=directory.path, + src_dir=options.src_dir, + download_dir=options.download_dir, + wheel_download_dir=None, + progress_bar=options.progress_bar, + build_isolation=options.build_isolation, + req_tracker=req_tracker, + ) + + resolver = Resolver( + preparer=preparer, + finder=finder, + session=session, + wheel_cache=None, + use_user_site=False, + upgrade_strategy="to-satisfy-only", + force_reinstall=False, + ignore_dependencies=options.ignore_dependencies, + ignore_requires_python=False, + ignore_installed=True, + isolated=options.isolated_mode, + ) + resolver.resolve(requirement_set) + + downloaded = ' '.join([ + req.name for req in requirement_set.successfully_downloaded + ]) + if downloaded: + logger.info('Successfully downloaded %s', downloaded) + + # Clean up + if not options.no_clean: + requirement_set.cleanup_files() + + return requirement_set diff --git a/venv/Lib/site-packages/pip/_internal/commands/freeze.py b/venv/Lib/site-packages/pip/_internal/commands/freeze.py new file mode 100644 index 0000000..dc9c53a --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/freeze.py @@ -0,0 +1,96 @@ +from __future__ import absolute_import + +import sys + +from pip._internal.cache import WheelCache +from pip._internal.cli.base_command import Command +from pip._internal.models.format_control import FormatControl +from pip._internal.operations.freeze import freeze +from pip._internal.utils.compat import stdlib_pkgs + +DEV_PKGS = {'pip', 'setuptools', 'distribute', 'wheel'} + + +class FreezeCommand(Command): + """ + Output installed packages in requirements format. + + packages are listed in a case-insensitive sorted order. + """ + name = 'freeze' + usage = """ + %prog [options]""" + summary = 'Output installed packages in requirements format.' + log_streams = ("ext://sys.stderr", "ext://sys.stderr") + + def __init__(self, *args, **kw): + super(FreezeCommand, self).__init__(*args, **kw) + + self.cmd_opts.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help="Use the order in the given requirements file and its " + "comments when generating output. This option can be " + "used multiple times.") + self.cmd_opts.add_option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='URL', + help='URL for finding packages, which will be added to the ' + 'output.') + self.cmd_opts.add_option( + '-l', '--local', + dest='local', + action='store_true', + default=False, + help='If in a virtualenv that has global access, do not output ' + 'globally-installed packages.') + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + self.cmd_opts.add_option( + '--all', + dest='freeze_all', + action='store_true', + help='Do not skip these packages in the output:' + ' %s' % ', '.join(DEV_PKGS)) + self.cmd_opts.add_option( + '--exclude-editable', + dest='exclude_editable', + action='store_true', + help='Exclude editable package from output.') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + format_control = FormatControl(set(), set()) + wheel_cache = WheelCache(options.cache_dir, format_control) + skip = set(stdlib_pkgs) + if not options.freeze_all: + skip.update(DEV_PKGS) + + freeze_kwargs = dict( + requirement=options.requirements, + find_links=options.find_links, + local_only=options.local, + user_only=options.user, + skip_regex=options.skip_requirements_regex, + isolated=options.isolated_mode, + wheel_cache=wheel_cache, + skip=skip, + exclude_editable=options.exclude_editable, + ) + + try: + for line in freeze(**freeze_kwargs): + sys.stdout.write(line + '\n') + finally: + wheel_cache.cleanup() diff --git a/venv/Lib/site-packages/pip/_internal/commands/hash.py b/venv/Lib/site-packages/pip/_internal/commands/hash.py new file mode 100644 index 0000000..423440e --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/hash.py @@ -0,0 +1,57 @@ +from __future__ import absolute_import + +import hashlib +import logging +import sys + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR +from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES +from pip._internal.utils.misc import read_chunks + +logger = logging.getLogger(__name__) + + +class HashCommand(Command): + """ + Compute a hash of a local package archive. + + These can be used with --hash in a requirements file to do repeatable + installs. + + """ + name = 'hash' + usage = '%prog [options] <file> ...' + summary = 'Compute hashes of package archives.' + ignore_require_venv = True + + def __init__(self, *args, **kw): + super(HashCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-a', '--algorithm', + dest='algorithm', + choices=STRONG_HASHES, + action='store', + default=FAVORITE_HASH, + help='The hash algorithm to use: one of %s' % + ', '.join(STRONG_HASHES)) + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + self.parser.print_usage(sys.stderr) + return ERROR + + algorithm = options.algorithm + for path in args: + logger.info('%s:\n--hash=%s:%s', + path, algorithm, _hash_of_file(path, algorithm)) + + +def _hash_of_file(path, algorithm): + """Return the hash digest of a file.""" + with open(path, 'rb') as archive: + hash = hashlib.new(algorithm) + for chunk in read_chunks(archive): + hash.update(chunk) + return hash.hexdigest() diff --git a/venv/Lib/site-packages/pip/_internal/commands/help.py b/venv/Lib/site-packages/pip/_internal/commands/help.py new file mode 100644 index 0000000..49a81cb --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/help.py @@ -0,0 +1,37 @@ +from __future__ import absolute_import + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError + + +class HelpCommand(Command): + """Show help for commands""" + name = 'help' + usage = """ + %prog <command>""" + summary = 'Show help for commands.' + ignore_require_venv = True + + def run(self, options, args): + from pip._internal.commands import commands_dict, get_similar_commands + + try: + # 'pip help' with no args is handled by pip.__init__.parseopt() + cmd_name = args[0] # the command we need help for + except IndexError: + return SUCCESS + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "%s"' % cmd_name] + if guess: + msg.append('maybe you meant "%s"' % guess) + + raise CommandError(' - '.join(msg)) + + command = commands_dict[cmd_name]() + command.parser.print_help() + + return SUCCESS diff --git a/venv/Lib/site-packages/pip/_internal/commands/install.py b/venv/Lib/site-packages/pip/_internal/commands/install.py new file mode 100644 index 0000000..6fc178f --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/install.py @@ -0,0 +1,535 @@ +from __future__ import absolute_import + +import errno +import logging +import operator +import os +import shutil +from optparse import SUPPRESS_HELP + +from pip._vendor import pkg_resources + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import RequirementCommand +from pip._internal.cli.status_codes import ERROR +from pip._internal.exceptions import ( + CommandError, InstallationError, PreviousBuildDirError, +) +from pip._internal.locations import distutils_scheme, virtualenv_no_global +from pip._internal.operations.check import check_install_conflicts +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req import RequirementSet, install_given_reqs +from pip._internal.req.req_tracker import RequirementTracker +from pip._internal.resolve import Resolver +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.misc import ( + ensure_dir, get_installed_version, + protect_pip_from_modification_on_windows, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.wheel import WheelBuilder + +try: + import wheel +except ImportError: + wheel = None + + +logger = logging.getLogger(__name__) + + +class InstallCommand(RequirementCommand): + """ + Install packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports installing from "requirements files", which provide + an easy way to specify a whole environment to be installed. + """ + name = 'install' + + usage = """ + %prog [options] <requirement specifier> [package-index-options] ... + %prog [options] -r <requirements file> [package-index-options] ... + %prog [options] [-e] <vcs project url> ... + %prog [options] [-e] <local project path> ... + %prog [options] <archive url/path> ...""" + + summary = 'Install packages.' + + def __init__(self, *args, **kw): + super(InstallCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.pre()) + + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option( + '-t', '--target', + dest='target_dir', + metavar='dir', + default=None, + help='Install packages into <dir>. ' + 'By default this will not replace existing files/folders in ' + '<dir>. Use --upgrade to replace existing packages in <dir> ' + 'with new versions.' + ) + cmd_opts.add_option(cmdoptions.platform()) + cmd_opts.add_option(cmdoptions.python_version()) + cmd_opts.add_option(cmdoptions.implementation()) + cmd_opts.add_option(cmdoptions.abi()) + + cmd_opts.add_option( + '--user', + dest='use_user_site', + action='store_true', + help="Install to the Python user install directory for your " + "platform. Typically ~/.local/, or %APPDATA%\\Python on " + "Windows. (See the Python documentation for site.USER_BASE " + "for full details.)") + cmd_opts.add_option( + '--no-user', + dest='use_user_site', + action='store_false', + help=SUPPRESS_HELP) + cmd_opts.add_option( + '--root', + dest='root_path', + metavar='dir', + default=None, + help="Install everything relative to this alternate root " + "directory.") + cmd_opts.add_option( + '--prefix', + dest='prefix_path', + metavar='dir', + default=None, + help="Installation prefix where lib, bin and other top-level " + "folders are placed") + + cmd_opts.add_option(cmdoptions.build_dir()) + + cmd_opts.add_option(cmdoptions.src()) + + cmd_opts.add_option( + '-U', '--upgrade', + dest='upgrade', + action='store_true', + help='Upgrade all specified packages to the newest available ' + 'version. The handling of dependencies depends on the ' + 'upgrade-strategy used.' + ) + + cmd_opts.add_option( + '--upgrade-strategy', + dest='upgrade_strategy', + default='only-if-needed', + choices=['only-if-needed', 'eager'], + help='Determines how dependency upgrading should be handled ' + '[default: %default]. ' + '"eager" - dependencies are upgraded regardless of ' + 'whether the currently installed version satisfies the ' + 'requirements of the upgraded package(s). ' + '"only-if-needed" - are upgraded only when they do not ' + 'satisfy the requirements of the upgraded package(s).' + ) + + cmd_opts.add_option( + '--force-reinstall', + dest='force_reinstall', + action='store_true', + help='Reinstall all packages even if they are already ' + 'up-to-date.') + + cmd_opts.add_option( + '-I', '--ignore-installed', + dest='ignore_installed', + action='store_true', + help='Ignore the installed packages (reinstalling instead).') + + cmd_opts.add_option(cmdoptions.ignore_requires_python()) + cmd_opts.add_option(cmdoptions.no_build_isolation()) + + cmd_opts.add_option(cmdoptions.install_options()) + cmd_opts.add_option(cmdoptions.global_options()) + + cmd_opts.add_option( + "--compile", + action="store_true", + dest="compile", + default=True, + help="Compile Python source files to bytecode", + ) + + cmd_opts.add_option( + "--no-compile", + action="store_false", + dest="compile", + help="Do not compile Python source files to bytecode", + ) + + cmd_opts.add_option( + "--no-warn-script-location", + action="store_false", + dest="warn_script_location", + default=True, + help="Do not warn when installing scripts outside PATH", + ) + cmd_opts.add_option( + "--no-warn-conflicts", + action="store_false", + dest="warn_about_conflicts", + default=True, + help="Do not warn about broken dependencies", + ) + + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.prefer_binary()) + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + cmd_opts.add_option(cmdoptions.progress_bar()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + cmdoptions.check_install_build_global(options) + upgrade_strategy = "to-satisfy-only" + if options.upgrade: + upgrade_strategy = options.upgrade_strategy + + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + + cmdoptions.check_dist_restriction(options, check_target=True) + + if options.python_version: + python_versions = [options.python_version] + else: + python_versions = None + + options.src_dir = os.path.abspath(options.src_dir) + install_options = options.install_options or [] + if options.use_user_site: + if options.prefix_path: + raise CommandError( + "Can not combine '--user' and '--prefix' as they imply " + "different installation locations" + ) + if virtualenv_no_global(): + raise InstallationError( + "Can not perform a '--user' install. User site-packages " + "are not visible in this virtualenv." + ) + install_options.append('--user') + install_options.append('--prefix=') + + target_temp_dir = TempDirectory(kind="target") + if options.target_dir: + options.ignore_installed = True + options.target_dir = os.path.abspath(options.target_dir) + if (os.path.exists(options.target_dir) and not + os.path.isdir(options.target_dir)): + raise CommandError( + "Target path exists but is not a directory, will not " + "continue." + ) + + # Create a target directory for using with the target option + target_temp_dir.create() + install_options.append('--home=' + target_temp_dir.path) + + global_options = options.global_options or [] + + with self._build_session(options) as session: + finder = self._build_package_finder( + options=options, + session=session, + platform=options.platform, + python_versions=python_versions, + abi=options.abi, + implementation=options.implementation, + ) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + + if options.cache_dir and not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "by the current user and caching wheels has been " + "disabled. check the permissions and owner of that " + "directory. If executing pip with sudo, you may want " + "sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + with RequirementTracker() as req_tracker, TempDirectory( + options.build_dir, delete=build_delete, kind="install" + ) as directory: + requirement_set = RequirementSet( + require_hashes=options.require_hashes, + check_supported_wheels=not options.target_dir, + ) + + try: + self.populate_requirement_set( + requirement_set, args, options, finder, session, + self.name, wheel_cache + ) + preparer = RequirementPreparer( + build_dir=directory.path, + src_dir=options.src_dir, + download_dir=None, + wheel_download_dir=None, + progress_bar=options.progress_bar, + build_isolation=options.build_isolation, + req_tracker=req_tracker, + ) + + resolver = Resolver( + preparer=preparer, + finder=finder, + session=session, + wheel_cache=wheel_cache, + use_user_site=options.use_user_site, + upgrade_strategy=upgrade_strategy, + force_reinstall=options.force_reinstall, + ignore_dependencies=options.ignore_dependencies, + ignore_requires_python=options.ignore_requires_python, + ignore_installed=options.ignore_installed, + isolated=options.isolated_mode, + ) + resolver.resolve(requirement_set) + + protect_pip_from_modification_on_windows( + modifying_pip=requirement_set.has_requirement("pip") + ) + + # If caching is disabled or wheel is not installed don't + # try to build wheels. + if wheel and options.cache_dir: + # build wheels before install. + wb = WheelBuilder( + finder, preparer, wheel_cache, + build_options=[], global_options=[], + ) + # Ignore the result: a failed wheel will be + # installed from the sdist/vcs whatever. + wb.build( + requirement_set.requirements.values(), + session=session, autobuilding=True + ) + + to_install = resolver.get_installation_order( + requirement_set + ) + + # Consistency Checking of the package set we're installing. + should_warn_about_conflicts = ( + not options.ignore_dependencies and + options.warn_about_conflicts + ) + if should_warn_about_conflicts: + self._warn_about_conflicts(to_install) + + # Don't warn about script install locations if + # --target has been specified + warn_script_location = options.warn_script_location + if options.target_dir: + warn_script_location = False + + installed = install_given_reqs( + to_install, + install_options, + global_options, + root=options.root_path, + home=target_temp_dir.path, + prefix=options.prefix_path, + pycompile=options.compile, + warn_script_location=warn_script_location, + use_user_site=options.use_user_site, + ) + + lib_locations = get_lib_location_guesses( + user=options.use_user_site, + home=target_temp_dir.path, + root=options.root_path, + prefix=options.prefix_path, + isolated=options.isolated_mode, + ) + working_set = pkg_resources.WorkingSet(lib_locations) + + reqs = sorted(installed, key=operator.attrgetter('name')) + items = [] + for req in reqs: + item = req.name + try: + installed_version = get_installed_version( + req.name, working_set=working_set + ) + if installed_version: + item += '-' + installed_version + except Exception: + pass + items.append(item) + installed = ' '.join(items) + if installed: + logger.info('Successfully installed %s', installed) + except EnvironmentError as error: + show_traceback = (self.verbosity >= 1) + + message = create_env_error_message( + error, show_traceback, options.use_user_site, + ) + logger.error(message, exc_info=show_traceback) + + return ERROR + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + # Clean up + if not options.no_clean: + requirement_set.cleanup_files() + wheel_cache.cleanup() + + if options.target_dir: + self._handle_target_dir( + options.target_dir, target_temp_dir, options.upgrade + ) + return requirement_set + + def _handle_target_dir(self, target_dir, target_temp_dir, upgrade): + ensure_dir(target_dir) + + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + lib_dir_list = [] + + with target_temp_dir: + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + scheme = distutils_scheme('', home=target_temp_dir.path) + purelib_dir = scheme['purelib'] + platlib_dir = scheme['platlib'] + data_dir = scheme['data'] + + if os.path.exists(purelib_dir): + lib_dir_list.append(purelib_dir) + if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: + lib_dir_list.append(platlib_dir) + if os.path.exists(data_dir): + lib_dir_list.append(data_dir) + + for lib_dir in lib_dir_list: + for item in os.listdir(lib_dir): + if lib_dir == data_dir: + ddir = os.path.join(data_dir, item) + if any(s.startswith(ddir) for s in lib_dir_list[:-1]): + continue + target_item_dir = os.path.join(target_dir, item) + if os.path.exists(target_item_dir): + if not upgrade: + logger.warning( + 'Target directory %s already exists. Specify ' + '--upgrade to force replacement.', + target_item_dir + ) + continue + if os.path.islink(target_item_dir): + logger.warning( + 'Target directory %s already exists and is ' + 'a link. Pip will not automatically replace ' + 'links, please remove if replacement is ' + 'desired.', + target_item_dir + ) + continue + if os.path.isdir(target_item_dir): + shutil.rmtree(target_item_dir) + else: + os.remove(target_item_dir) + + shutil.move( + os.path.join(lib_dir, item), + target_item_dir + ) + + def _warn_about_conflicts(self, to_install): + package_set, _dep_info = check_install_conflicts(to_install) + missing, conflicting = _dep_info + + # NOTE: There is some duplication here from pip check + for project_name in missing: + version = package_set[project_name][0] + for dependency in missing[project_name]: + logger.critical( + "%s %s requires %s, which is not installed.", + project_name, version, dependency[1], + ) + + for project_name in conflicting: + version = package_set[project_name][0] + for dep_name, dep_version, req in conflicting[project_name]: + logger.critical( + "%s %s has requirement %s, but you'll have %s %s which is " + "incompatible.", + project_name, version, req, dep_name, dep_version, + ) + + +def get_lib_location_guesses(*args, **kwargs): + scheme = distutils_scheme('', *args, **kwargs) + return [scheme['purelib'], scheme['platlib']] + + +def create_env_error_message(error, show_traceback, using_user_site): + """Format an error message for an EnvironmentError + + It may occur anytime during the execution of the install command. + """ + parts = [] + + # Mention the error if we are not going to show a traceback + parts.append("Could not install packages due to an EnvironmentError") + if not show_traceback: + parts.append(": ") + parts.append(str(error)) + else: + parts.append(".") + + # Spilt the error indication from a helper message (if any) + parts[-1] += "\n" + + # Suggest useful actions to the user: + # (1) using user site-packages or (2) verifying the permissions + if error.errno == errno.EACCES: + user_option_part = "Consider using the `--user` option" + permissions_part = "Check the permissions" + + if not using_user_site: + parts.extend([ + user_option_part, " or ", + permissions_part.lower(), + ]) + else: + parts.append(permissions_part) + parts.append(".\n") + + return "".join(parts).strip() + "\n" diff --git a/venv/Lib/site-packages/pip/_internal/commands/list.py b/venv/Lib/site-packages/pip/_internal/commands/list.py new file mode 100644 index 0000000..c6eeca7 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/list.py @@ -0,0 +1,306 @@ +from __future__ import absolute_import + +import json +import logging + +from pip._vendor import six +from pip._vendor.six.moves import zip_longest + +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.exceptions import CommandError +from pip._internal.index import PackageFinder +from pip._internal.utils.misc import ( + dist_is_editable, get_installed_distributions, +) +from pip._internal.utils.packaging import get_installer + +logger = logging.getLogger(__name__) + + +class ListCommand(Command): + """ + List installed packages, including editables. + + Packages are listed in a case-insensitive sorted order. + """ + name = 'list' + usage = """ + %prog [options]""" + summary = 'List installed packages.' + + def __init__(self, *args, **kw): + super(ListCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( + '-o', '--outdated', + action='store_true', + default=False, + help='List outdated packages') + cmd_opts.add_option( + '-u', '--uptodate', + action='store_true', + default=False, + help='List uptodate packages') + cmd_opts.add_option( + '-e', '--editable', + action='store_true', + default=False, + help='List editable projects.') + cmd_opts.add_option( + '-l', '--local', + action='store_true', + default=False, + help=('If in a virtualenv that has global access, do not list ' + 'globally-installed packages.'), + ) + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + + cmd_opts.add_option( + '--pre', + action='store_true', + default=False, + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + cmd_opts.add_option( + '--format', + action='store', + dest='list_format', + default="columns", + choices=('columns', 'freeze', 'json'), + help="Select the output format among: columns (default), freeze, " + "or json", + ) + + cmd_opts.add_option( + '--not-required', + action='store_true', + dest='not_required', + help="List packages that are not dependencies of " + "installed packages.", + ) + + cmd_opts.add_option( + '--exclude-editable', + action='store_false', + dest='include_editable', + help='Exclude editable package from output.', + ) + cmd_opts.add_option( + '--include-editable', + action='store_true', + dest='include_editable', + help='Include editable package from output.', + default=True, + ) + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, self.parser + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def _build_package_finder(self, options, index_urls, session): + """ + Create a package finder appropriate to this list command. + """ + return PackageFinder( + find_links=options.find_links, + index_urls=index_urls, + allow_all_prereleases=options.pre, + trusted_hosts=options.trusted_hosts, + process_dependency_links=options.process_dependency_links, + session=session, + ) + + def run(self, options, args): + if options.outdated and options.uptodate: + raise CommandError( + "Options --outdated and --uptodate cannot be combined.") + + packages = get_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + include_editables=options.include_editable, + ) + + if options.outdated: + packages = self.get_outdated(packages, options) + elif options.uptodate: + packages = self.get_uptodate(packages, options) + + if options.not_required: + packages = self.get_not_required(packages, options) + + self.output_package_listing(packages, options) + + def get_outdated(self, packages, options): + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version > dist.parsed_version + ] + + def get_uptodate(self, packages, options): + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version == dist.parsed_version + ] + + def get_not_required(self, packages, options): + dep_keys = set() + for dist in packages: + dep_keys.update(requirement.key for requirement in dist.requires()) + return {pkg for pkg in packages if pkg.key not in dep_keys} + + def iter_packages_latest_infos(self, packages, options): + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + dependency_links = [] + for dist in packages: + if dist.has_metadata('dependency_links.txt'): + dependency_links.extend( + dist.get_metadata_lines('dependency_links.txt'), + ) + + with self._build_session(options) as session: + finder = self._build_package_finder(options, index_urls, session) + finder.add_dependency_links(dependency_links) + + for dist in packages: + typ = 'unknown' + all_candidates = finder.find_all_candidates(dist.key) + if not options.pre: + # Remove prereleases + all_candidates = [candidate for candidate in all_candidates + if not candidate.version.is_prerelease] + + if not all_candidates: + continue + best_candidate = max(all_candidates, + key=finder._candidate_sort_key) + remote_version = best_candidate.version + if best_candidate.location.is_wheel: + typ = 'wheel' + else: + typ = 'sdist' + # This is dirty but makes the rest of the code much cleaner + dist.latest_version = remote_version + dist.latest_filetype = typ + yield dist + + def output_package_listing(self, packages, options): + packages = sorted( + packages, + key=lambda dist: dist.project_name.lower(), + ) + if options.list_format == 'columns' and packages: + data, header = format_for_columns(packages, options) + self.output_package_listing_columns(data, header) + elif options.list_format == 'freeze': + for dist in packages: + if options.verbose >= 1: + logger.info("%s==%s (%s)", dist.project_name, + dist.version, dist.location) + else: + logger.info("%s==%s", dist.project_name, dist.version) + elif options.list_format == 'json': + logger.info(format_for_json(packages, options)) + + def output_package_listing_columns(self, data, header): + # insert the header first: we need to know the size of column names + if len(data) > 0: + data.insert(0, header) + + pkg_strings, sizes = tabulate(data) + + # Create and add a separator. + if len(data) > 0: + pkg_strings.insert(1, " ".join(map(lambda x: '-' * x, sizes))) + + for val in pkg_strings: + logger.info(val) + + +def tabulate(vals): + # From pfmoore on GitHub: + # https://github.com/pypa/pip/issues/3651#issuecomment-216932564 + assert len(vals) > 0 + + sizes = [0] * max(len(x) for x in vals) + for row in vals: + sizes = [max(s, len(str(c))) for s, c in zip_longest(sizes, row)] + + result = [] + for row in vals: + display = " ".join([str(c).ljust(s) if c is not None else '' + for s, c in zip_longest(sizes, row)]) + result.append(display) + + return result, sizes + + +def format_for_columns(pkgs, options): + """ + Convert the package data into something usable + by output_package_listing_columns. + """ + running_outdated = options.outdated + # Adjust the header for the `pip list --outdated` case. + if running_outdated: + header = ["Package", "Version", "Latest", "Type"] + else: + header = ["Package", "Version"] + + data = [] + if options.verbose >= 1 or any(dist_is_editable(x) for x in pkgs): + header.append("Location") + if options.verbose >= 1: + header.append("Installer") + + for proj in pkgs: + # if we're working on the 'outdated' list, separate out the + # latest_version and type + row = [proj.project_name, proj.version] + + if running_outdated: + row.append(proj.latest_version) + row.append(proj.latest_filetype) + + if options.verbose >= 1 or dist_is_editable(proj): + row.append(proj.location) + if options.verbose >= 1: + row.append(get_installer(proj)) + + data.append(row) + + return data, header + + +def format_for_json(packages, options): + data = [] + for dist in packages: + info = { + 'name': dist.project_name, + 'version': six.text_type(dist.version), + } + if options.verbose >= 1: + info['location'] = dist.location + info['installer'] = get_installer(dist) + if options.outdated: + info['latest_version'] = six.text_type(dist.latest_version) + info['latest_filetype'] = dist.latest_filetype + data.append(info) + return json.dumps(data) diff --git a/venv/Lib/site-packages/pip/_internal/commands/search.py b/venv/Lib/site-packages/pip/_internal/commands/search.py new file mode 100644 index 0000000..c157a31 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/search.py @@ -0,0 +1,135 @@ +from __future__ import absolute_import + +import logging +import sys +import textwrap +from collections import OrderedDict + +from pip._vendor import pkg_resources +from pip._vendor.packaging.version import parse as parse_version +# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import +from pip._vendor.six.moves import xmlrpc_client # type: ignore + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS +from pip._internal.download import PipXmlrpcTransport +from pip._internal.exceptions import CommandError +from pip._internal.models.index import PyPI +from pip._internal.utils.compat import get_terminal_size +from pip._internal.utils.logging import indent_log + +logger = logging.getLogger(__name__) + + +class SearchCommand(Command): + """Search for PyPI packages whose name or summary contains <query>.""" + name = 'search' + usage = """ + %prog [options] <query>""" + summary = 'Search PyPI for packages.' + ignore_require_venv = True + + def __init__(self, *args, **kw): + super(SearchCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-i', '--index', + dest='index', + metavar='URL', + default=PyPI.pypi_url, + help='Base URL of Python Package Index (default %default)') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + raise CommandError('Missing required argument (search query).') + query = args + pypi_hits = self.search(query, options) + hits = transform_hits(pypi_hits) + + terminal_width = None + if sys.stdout.isatty(): + terminal_width = get_terminal_size()[0] + + print_results(hits, terminal_width=terminal_width) + if pypi_hits: + return SUCCESS + return NO_MATCHES_FOUND + + def search(self, query, options): + index_url = options.index + with self._build_session(options) as session: + transport = PipXmlrpcTransport(index_url, session) + pypi = xmlrpc_client.ServerProxy(index_url, transport) + hits = pypi.search({'name': query, 'summary': query}, 'or') + return hits + + +def transform_hits(hits): + """ + The list from pypi is really a list of versions. We want a list of + packages with the list of versions stored inline. This converts the + list from pypi into one we can use. + """ + packages = OrderedDict() + for hit in hits: + name = hit['name'] + summary = hit['summary'] + version = hit['version'] + + if name not in packages.keys(): + packages[name] = { + 'name': name, + 'summary': summary, + 'versions': [version], + } + else: + packages[name]['versions'].append(version) + + # if this is the highest version, replace summary and score + if version == highest_version(packages[name]['versions']): + packages[name]['summary'] = summary + + return list(packages.values()) + + +def print_results(hits, name_column_width=None, terminal_width=None): + if not hits: + return + if name_column_width is None: + name_column_width = max([ + len(hit['name']) + len(highest_version(hit.get('versions', ['-']))) + for hit in hits + ]) + 4 + + installed_packages = [p.project_name for p in pkg_resources.working_set] + for hit in hits: + name = hit['name'] + summary = hit['summary'] or '' + latest = highest_version(hit.get('versions', ['-'])) + if terminal_width is not None: + target_width = terminal_width - name_column_width - 5 + if target_width > 10: + # wrap and indent summary to fit terminal + summary = textwrap.wrap(summary, target_width) + summary = ('\n' + ' ' * (name_column_width + 3)).join(summary) + + line = '%-*s - %s' % (name_column_width, + '%s (%s)' % (name, latest), summary) + try: + logger.info(line) + if name in installed_packages: + dist = pkg_resources.get_distribution(name) + with indent_log(): + if dist.version == latest: + logger.info('INSTALLED: %s (latest)', dist.version) + else: + logger.info('INSTALLED: %s', dist.version) + logger.info('LATEST: %s', latest) + except UnicodeEncodeError: + pass + + +def highest_version(versions): + return max(versions, key=parse_version) diff --git a/venv/Lib/site-packages/pip/_internal/commands/show.py b/venv/Lib/site-packages/pip/_internal/commands/show.py new file mode 100644 index 0000000..f92c9bc --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/show.py @@ -0,0 +1,168 @@ +from __future__ import absolute_import + +import logging +import os +from email.parser import FeedParser # type: ignore + +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS + +logger = logging.getLogger(__name__) + + +class ShowCommand(Command): + """ + Show information about one or more installed packages. + + The output is in RFC-compliant mail header format. + """ + name = 'show' + usage = """ + %prog [options] <package> ...""" + summary = 'Show information about installed packages.' + ignore_require_venv = True + + def __init__(self, *args, **kw): + super(ShowCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-f', '--files', + dest='files', + action='store_true', + default=False, + help='Show the full list of installed files for each package.') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + logger.warning('ERROR: Please provide a package name or names.') + return ERROR + query = args + + results = search_packages_info(query) + if not print_results( + results, list_files=options.files, verbose=options.verbose): + return ERROR + return SUCCESS + + +def search_packages_info(query): + """ + Gather details from installed distributions. Print distribution name, + version, location, and installed files. Installed files requires a + pip generated 'installed-files.txt' in the distributions '.egg-info' + directory. + """ + installed = {} + for p in pkg_resources.working_set: + installed[canonicalize_name(p.project_name)] = p + + query_names = [canonicalize_name(name) for name in query] + + for dist in [installed[pkg] for pkg in query_names if pkg in installed]: + package = { + 'name': dist.project_name, + 'version': dist.version, + 'location': dist.location, + 'requires': [dep.project_name for dep in dist.requires()], + } + file_list = None + metadata = None + if isinstance(dist, pkg_resources.DistInfoDistribution): + # RECORDs should be part of .dist-info metadatas + if dist.has_metadata('RECORD'): + lines = dist.get_metadata_lines('RECORD') + paths = [l.split(',')[0] for l in lines] + paths = [os.path.join(dist.location, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('METADATA'): + metadata = dist.get_metadata('METADATA') + else: + # Otherwise use pip's log for .egg-info's + if dist.has_metadata('installed-files.txt'): + paths = dist.get_metadata_lines('installed-files.txt') + paths = [os.path.join(dist.egg_info, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('PKG-INFO'): + metadata = dist.get_metadata('PKG-INFO') + + if dist.has_metadata('entry_points.txt'): + entry_points = dist.get_metadata_lines('entry_points.txt') + package['entry_points'] = entry_points + + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + package['installer'] = line.strip() + break + + # @todo: Should pkg_resources.Distribution have a + # `get_pkg_info` method? + feed_parser = FeedParser() + feed_parser.feed(metadata) + pkg_info_dict = feed_parser.close() + for key in ('metadata-version', 'summary', + 'home-page', 'author', 'author-email', 'license'): + package[key] = pkg_info_dict.get(key) + + # It looks like FeedParser cannot deal with repeated headers + classifiers = [] + for line in metadata.splitlines(): + if line.startswith('Classifier: '): + classifiers.append(line[len('Classifier: '):]) + package['classifiers'] = classifiers + + if file_list: + package['files'] = sorted(file_list) + yield package + + +def print_results(distributions, list_files=False, verbose=False): + """ + Print the informations from installed distributions found. + """ + results_printed = False + for i, dist in enumerate(distributions): + results_printed = True + if i > 0: + logger.info("---") + + name = dist.get('name', '') + required_by = [ + pkg.project_name for pkg in pkg_resources.working_set + if name in [required.name for required in pkg.requires()] + ] + + logger.info("Name: %s", name) + logger.info("Version: %s", dist.get('version', '')) + logger.info("Summary: %s", dist.get('summary', '')) + logger.info("Home-page: %s", dist.get('home-page', '')) + logger.info("Author: %s", dist.get('author', '')) + logger.info("Author-email: %s", dist.get('author-email', '')) + logger.info("License: %s", dist.get('license', '')) + logger.info("Location: %s", dist.get('location', '')) + logger.info("Requires: %s", ', '.join(dist.get('requires', []))) + logger.info("Required-by: %s", ', '.join(required_by)) + + if verbose: + logger.info("Metadata-Version: %s", + dist.get('metadata-version', '')) + logger.info("Installer: %s", dist.get('installer', '')) + logger.info("Classifiers:") + for classifier in dist.get('classifiers', []): + logger.info(" %s", classifier) + logger.info("Entry-points:") + for entry in dist.get('entry_points', []): + logger.info(" %s", entry.strip()) + if list_files: + logger.info("Files:") + for line in dist.get('files', []): + logger.info(" %s", line.strip()) + if "files" not in dist: + logger.info("Cannot locate installed-files.txt") + return results_printed diff --git a/venv/Lib/site-packages/pip/_internal/commands/uninstall.py b/venv/Lib/site-packages/pip/_internal/commands/uninstall.py new file mode 100644 index 0000000..0cd6f54 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/uninstall.py @@ -0,0 +1,78 @@ +from __future__ import absolute_import + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.base_command import Command +from pip._internal.exceptions import InstallationError +from pip._internal.req import parse_requirements +from pip._internal.req.constructors import install_req_from_line +from pip._internal.utils.misc import protect_pip_from_modification_on_windows + + +class UninstallCommand(Command): + """ + Uninstall packages. + + pip is able to uninstall most installed packages. Known exceptions are: + + - Pure distutils packages installed with ``python setup.py install``, which + leave behind no metadata to determine what files were installed. + - Script wrappers installed by ``python setup.py develop``. + """ + name = 'uninstall' + usage = """ + %prog [options] <package> ... + %prog [options] -r <requirements file> ...""" + summary = 'Uninstall packages.' + + def __init__(self, *args, **kw): + super(UninstallCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Uninstall all the packages listed in the given requirements ' + 'file. This option can be used multiple times.', + ) + self.cmd_opts.add_option( + '-y', '--yes', + dest='yes', + action='store_true', + help="Don't ask for confirmation of uninstall deletions.") + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + with self._build_session(options) as session: + reqs_to_uninstall = {} + for name in args: + req = install_req_from_line( + name, isolated=options.isolated_mode, + ) + if req.name: + reqs_to_uninstall[canonicalize_name(req.name)] = req + for filename in options.requirements: + for req in parse_requirements( + filename, + options=options, + session=session): + if req.name: + reqs_to_uninstall[canonicalize_name(req.name)] = req + if not reqs_to_uninstall: + raise InstallationError( + 'You must give at least one requirement to %(name)s (see ' + '"pip help %(name)s")' % dict(name=self.name) + ) + + protect_pip_from_modification_on_windows( + modifying_pip="pip" in reqs_to_uninstall + ) + + for req in reqs_to_uninstall.values(): + uninstall_pathset = req.uninstall( + auto_confirm=options.yes, verbose=self.verbosity > 0, + ) + if uninstall_pathset: + uninstall_pathset.commit() diff --git a/venv/Lib/site-packages/pip/_internal/commands/wheel.py b/venv/Lib/site-packages/pip/_internal/commands/wheel.py new file mode 100644 index 0000000..9c1f149 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/commands/wheel.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import logging +import os + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import RequirementCommand +from pip._internal.exceptions import CommandError, PreviousBuildDirError +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req import RequirementSet +from pip._internal.req.req_tracker import RequirementTracker +from pip._internal.resolve import Resolver +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.wheel import WheelBuilder + +logger = logging.getLogger(__name__) + + +class WheelCommand(RequirementCommand): + """ + Build Wheel archives for your requirements and dependencies. + + Wheel is a built-package format, and offers the advantage of not + recompiling your software during every install. For more details, see the + wheel docs: https://wheel.readthedocs.io/en/latest/ + + Requirements: setuptools>=0.8, and wheel. + + 'pip wheel' uses the bdist_wheel setuptools extension from the wheel + package to build individual wheels. + + """ + + name = 'wheel' + usage = """ + %prog [options] <requirement specifier> ... + %prog [options] -r <requirements file> ... + %prog [options] [-e] <vcs project url> ... + %prog [options] [-e] <local project path> ... + %prog [options] <archive url/path> ...""" + + summary = 'Build wheels from your requirements.' + + def __init__(self, *args, **kw): + super(WheelCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( + '-w', '--wheel-dir', + dest='wheel_dir', + metavar='dir', + default=os.curdir, + help=("Build wheels into <dir>, where the default is the " + "current working directory."), + ) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.prefer_binary()) + cmd_opts.add_option( + '--build-option', + dest='build_options', + metavar='options', + action='append', + help="Extra arguments to be supplied to 'setup.py bdist_wheel'.", + ) + cmd_opts.add_option(cmdoptions.no_build_isolation()) + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.src()) + cmd_opts.add_option(cmdoptions.ignore_requires_python()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.build_dir()) + cmd_opts.add_option(cmdoptions.progress_bar()) + + cmd_opts.add_option( + '--global-option', + dest='global_options', + action='append', + metavar='options', + help="Extra global options to be supplied to the setup.py " + "call before the 'bdist_wheel' command.") + + cmd_opts.add_option( + '--pre', + action='store_true', + default=False, + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + cmdoptions.check_install_build_global(options) + + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + + options.src_dir = os.path.abspath(options.src_dir) + + with self._build_session(options) as session: + finder = self._build_package_finder(options, session) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + + with RequirementTracker() as req_tracker, TempDirectory( + options.build_dir, delete=build_delete, kind="wheel" + ) as directory: + + requirement_set = RequirementSet( + require_hashes=options.require_hashes, + ) + + try: + self.populate_requirement_set( + requirement_set, args, options, finder, session, + self.name, wheel_cache + ) + + preparer = RequirementPreparer( + build_dir=directory.path, + src_dir=options.src_dir, + download_dir=None, + wheel_download_dir=options.wheel_dir, + progress_bar=options.progress_bar, + build_isolation=options.build_isolation, + req_tracker=req_tracker, + ) + + resolver = Resolver( + preparer=preparer, + finder=finder, + session=session, + wheel_cache=wheel_cache, + use_user_site=False, + upgrade_strategy="to-satisfy-only", + force_reinstall=False, + ignore_dependencies=options.ignore_dependencies, + ignore_requires_python=options.ignore_requires_python, + ignore_installed=True, + isolated=options.isolated_mode, + ) + resolver.resolve(requirement_set) + + # build wheels + wb = WheelBuilder( + finder, preparer, wheel_cache, + build_options=options.build_options or [], + global_options=options.global_options or [], + no_clean=options.no_clean, + ) + wheels_built_successfully = wb.build( + requirement_set.requirements.values(), session=session, + ) + if not wheels_built_successfully: + raise CommandError( + "Failed to build one or more wheels" + ) + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + if not options.no_clean: + requirement_set.cleanup_files() + wheel_cache.cleanup() diff --git a/venv/Lib/site-packages/pip/_internal/configuration.py b/venv/Lib/site-packages/pip/_internal/configuration.py new file mode 100644 index 0000000..fe6df9b --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/configuration.py @@ -0,0 +1,387 @@ +"""Configuration management setup + +Some terminology: +- name + As written in config files. +- value + Value associated with a name +- key + Name combined with it's section (section.name) +- variant + A single word describing where the configuration key-value pair came from +""" + +import locale +import logging +import os + +from pip._vendor import six +from pip._vendor.six.moves import configparser + +from pip._internal.exceptions import ( + ConfigurationError, ConfigurationFileCouldNotBeLoaded, +) +from pip._internal.locations import ( + legacy_config_file, new_config_file, running_under_virtualenv, + site_config_files, venv_config_file, +) +from pip._internal.utils.misc import ensure_dir, enum +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( # noqa: F401 + Any, Dict, Iterable, List, NewType, Optional, Tuple + ) + + RawConfigParser = configparser.RawConfigParser # Shorthand + Kind = NewType("Kind", str) + +logger = logging.getLogger(__name__) + + +# NOTE: Maybe use the optionx attribute to normalize keynames. +def _normalize_name(name): + # type: (str) -> str + """Make a name consistent regardless of source (environment or file) + """ + name = name.lower().replace('_', '-') + if name.startswith('--'): + name = name[2:] # only prefer long opts + return name + + +def _disassemble_key(name): + # type: (str) -> List[str] + return name.split(".", 1) + + +# The kinds of configurations there are. +kinds = enum( + USER="user", # User Specific + GLOBAL="global", # System Wide + VENV="venv", # Virtual Environment Specific + ENV="env", # from PIP_CONFIG_FILE + ENV_VAR="env-var", # from Environment Variables +) + + +class Configuration(object): + """Handles management of configuration. + + Provides an interface to accessing and managing configuration files. + + This class converts provides an API that takes "section.key-name" style + keys and stores the value associated with it as "key-name" under the + section "section". + + This allows for a clean interface wherein the both the section and the + key-name are preserved in an easy to manage form in the configuration files + and the data stored is also nice. + """ + + def __init__(self, isolated, load_only=None): + # type: (bool, Kind) -> None + super(Configuration, self).__init__() + + _valid_load_only = [kinds.USER, kinds.GLOBAL, kinds.VENV, None] + if load_only not in _valid_load_only: + raise ConfigurationError( + "Got invalid value for load_only - should be one of {}".format( + ", ".join(map(repr, _valid_load_only[:-1])) + ) + ) + self.isolated = isolated # type: bool + self.load_only = load_only # type: Optional[Kind] + + # The order here determines the override order. + self._override_order = [ + kinds.GLOBAL, kinds.USER, kinds.VENV, kinds.ENV, kinds.ENV_VAR + ] + + self._ignore_env_names = ["version", "help"] + + # Because we keep track of where we got the data from + self._parsers = { + variant: [] for variant in self._override_order + } # type: Dict[Kind, List[Tuple[str, RawConfigParser]]] + self._config = { + variant: {} for variant in self._override_order + } # type: Dict[Kind, Dict[str, Any]] + self._modified_parsers = [] # type: List[Tuple[str, RawConfigParser]] + + def load(self): + # type: () -> None + """Loads configuration from configuration files and environment + """ + self._load_config_files() + if not self.isolated: + self._load_environment_vars() + + def get_file_to_edit(self): + # type: () -> Optional[str] + """Returns the file with highest priority in configuration + """ + assert self.load_only is not None, \ + "Need to be specified a file to be editing" + + try: + return self._get_parser_to_modify()[0] + except IndexError: + return None + + def items(self): + # type: () -> Iterable[Tuple[str, Any]] + """Returns key-value pairs like dict.items() representing the loaded + configuration + """ + return self._dictionary.items() + + def get_value(self, key): + # type: (str) -> Any + """Get a value from the configuration. + """ + try: + return self._dictionary[key] + except KeyError: + raise ConfigurationError("No such key - {}".format(key)) + + def set_value(self, key, value): + # type: (str, Any) -> None + """Modify a value in the configuration. + """ + self._ensure_have_load_only() + + fname, parser = self._get_parser_to_modify() + + if parser is not None: + section, name = _disassemble_key(key) + + # Modify the parser and the configuration + if not parser.has_section(section): + parser.add_section(section) + parser.set(section, name, value) + + self._config[self.load_only][key] = value + self._mark_as_modified(fname, parser) + + def unset_value(self, key): + # type: (str) -> None + """Unset a value in the configuration. + """ + self._ensure_have_load_only() + + if key not in self._config[self.load_only]: + raise ConfigurationError("No such key - {}".format(key)) + + fname, parser = self._get_parser_to_modify() + + if parser is not None: + section, name = _disassemble_key(key) + + # Remove the key in the parser + modified_something = False + if parser.has_section(section): + # Returns whether the option was removed or not + modified_something = parser.remove_option(section, name) + + if modified_something: + # name removed from parser, section may now be empty + section_iter = iter(parser.items(section)) + try: + val = six.next(section_iter) + except StopIteration: + val = None + + if val is None: + parser.remove_section(section) + + self._mark_as_modified(fname, parser) + else: + raise ConfigurationError( + "Fatal Internal error [id=1]. Please report as a bug." + ) + + del self._config[self.load_only][key] + + def save(self): + # type: () -> None + """Save the currentin-memory state. + """ + self._ensure_have_load_only() + + for fname, parser in self._modified_parsers: + logger.info("Writing to %s", fname) + + # Ensure directory exists. + ensure_dir(os.path.dirname(fname)) + + with open(fname, "w") as f: + parser.write(f) # type: ignore + + # + # Private routines + # + + def _ensure_have_load_only(self): + # type: () -> None + if self.load_only is None: + raise ConfigurationError("Needed a specific file to be modifying.") + logger.debug("Will be working with %s variant only", self.load_only) + + @property + def _dictionary(self): + # type: () -> Dict[str, Any] + """A dictionary representing the loaded configuration. + """ + # NOTE: Dictionaries are not populated if not loaded. So, conditionals + # are not needed here. + retval = {} + + for variant in self._override_order: + retval.update(self._config[variant]) + + return retval + + def _load_config_files(self): + # type: () -> None + """Loads configuration from configuration files + """ + config_files = dict(self._iter_config_files()) + if config_files[kinds.ENV][0:1] == [os.devnull]: + logger.debug( + "Skipping loading configuration files due to " + "environment's PIP_CONFIG_FILE being os.devnull" + ) + return + + for variant, files in config_files.items(): + for fname in files: + # If there's specific variant set in `load_only`, load only + # that variant, not the others. + if self.load_only is not None and variant != self.load_only: + logger.debug( + "Skipping file '%s' (variant: %s)", fname, variant + ) + continue + + parser = self._load_file(variant, fname) + + # Keeping track of the parsers used + self._parsers[variant].append((fname, parser)) + + def _load_file(self, variant, fname): + # type: (Kind, str) -> RawConfigParser + logger.debug("For variant '%s', will try loading '%s'", variant, fname) + parser = self._construct_parser(fname) + + for section in parser.sections(): + items = parser.items(section) + self._config[variant].update(self._normalized_keys(section, items)) + + return parser + + def _construct_parser(self, fname): + # type: (str) -> RawConfigParser + parser = configparser.RawConfigParser() + # If there is no such file, don't bother reading it but create the + # parser anyway, to hold the data. + # Doing this is useful when modifying and saving files, where we don't + # need to construct a parser. + if os.path.exists(fname): + try: + parser.read(fname) + except UnicodeDecodeError: + # See https://github.com/pypa/pip/issues/4963 + raise ConfigurationFileCouldNotBeLoaded( + reason="contains invalid {} characters".format( + locale.getpreferredencoding(False) + ), + fname=fname, + ) + except configparser.Error as error: + # See https://github.com/pypa/pip/issues/4893 + raise ConfigurationFileCouldNotBeLoaded(error=error) + return parser + + def _load_environment_vars(self): + # type: () -> None + """Loads configuration from environment variables + """ + self._config[kinds.ENV_VAR].update( + self._normalized_keys(":env:", self._get_environ_vars()) + ) + + def _normalized_keys(self, section, items): + # type: (str, Iterable[Tuple[str, Any]]) -> Dict[str, Any] + """Normalizes items to construct a dictionary with normalized keys. + + This routine is where the names become keys and are made the same + regardless of source - configuration files or environment. + """ + normalized = {} + for name, val in items: + key = section + "." + _normalize_name(name) + normalized[key] = val + return normalized + + def _get_environ_vars(self): + # type: () -> Iterable[Tuple[str, str]] + """Returns a generator with all environmental vars with prefix PIP_""" + for key, val in os.environ.items(): + should_be_yielded = ( + key.startswith("PIP_") and + key[4:].lower() not in self._ignore_env_names + ) + if should_be_yielded: + yield key[4:].lower(), val + + # XXX: This is patched in the tests. + def _iter_config_files(self): + # type: () -> Iterable[Tuple[Kind, List[str]]] + """Yields variant and configuration files associated with it. + + This should be treated like items of a dictionary. + """ + # SMELL: Move the conditions out of this function + + # environment variables have the lowest priority + config_file = os.environ.get('PIP_CONFIG_FILE', None) + if config_file is not None: + yield kinds.ENV, [config_file] + else: + yield kinds.ENV, [] + + # at the base we have any global configuration + yield kinds.GLOBAL, list(site_config_files) + + # per-user configuration next + should_load_user_config = not self.isolated and not ( + config_file and os.path.exists(config_file) + ) + if should_load_user_config: + # The legacy config file is overridden by the new config file + yield kinds.USER, [legacy_config_file, new_config_file] + + # finally virtualenv configuration first trumping others + if running_under_virtualenv(): + yield kinds.VENV, [venv_config_file] + + def _get_parser_to_modify(self): + # type: () -> Tuple[str, RawConfigParser] + # Determine which parser to modify + parsers = self._parsers[self.load_only] + if not parsers: + # This should not happen if everything works correctly. + raise ConfigurationError( + "Fatal Internal error [id=2]. Please report as a bug." + ) + + # Use the highest priority parser. + return parsers[-1] + + # XXX: This is patched in the tests. + def _mark_as_modified(self, fname, parser): + # type: (str, RawConfigParser) -> None + file_parser_tuple = (fname, parser) + if file_parser_tuple not in self._modified_parsers: + self._modified_parsers.append(file_parser_tuple) diff --git a/venv/Lib/site-packages/pip/_internal/download.py b/venv/Lib/site-packages/pip/_internal/download.py new file mode 100644 index 0000000..96f3b65 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/download.py @@ -0,0 +1,921 @@ +from __future__ import absolute_import + +import cgi +import email.utils +import getpass +import json +import logging +import mimetypes +import os +import platform +import re +import shutil +import sys + +from pip._vendor import requests, six, urllib3 +from pip._vendor.cachecontrol import CacheControlAdapter +from pip._vendor.cachecontrol.caches import FileCache +from pip._vendor.lockfile import LockError +from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter +from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response +from pip._vendor.requests.structures import CaseInsensitiveDict +from pip._vendor.requests.utils import get_netrc_auth +# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import +from pip._vendor.six.moves import xmlrpc_client # type: ignore +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request +from pip._vendor.six.moves.urllib.parse import unquote as urllib_unquote +from pip._vendor.urllib3.util import IS_PYOPENSSL + +import pip +from pip._internal.exceptions import HashMismatch, InstallationError +from pip._internal.locations import write_delete_marker_file +from pip._internal.models.index import PyPI +from pip._internal.utils.encoding import auto_decode +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.glibc import libc_ver +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + ARCHIVE_EXTENSIONS, ask_path_exists, backup_dir, call_subprocess, consume, + display_path, format_size, get_installed_version, rmtree, splitext, + unpack_file, +) +from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.ui import DownloadProgressProvider +from pip._internal.vcs import vcs + +try: + import ssl # noqa +except ImportError: + ssl = None + +HAS_TLS = (ssl is not None) or IS_PYOPENSSL + +__all__ = ['get_file_content', + 'is_url', 'url_to_path', 'path_to_url', + 'is_archive_file', 'unpack_vcs_link', + 'unpack_file_url', 'is_vcs_url', 'is_file_url', + 'unpack_http_url', 'unpack_url'] + + +logger = logging.getLogger(__name__) + + +def user_agent(): + """ + Return a string representing the user agent. + """ + data = { + "installer": {"name": "pip", "version": pip.__version__}, + "python": platform.python_version(), + "implementation": { + "name": platform.python_implementation(), + }, + } + + if data["implementation"]["name"] == 'CPython': + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'PyPy': + if sys.pypy_version_info.releaselevel == 'final': + pypy_version_info = sys.pypy_version_info[:3] + else: + pypy_version_info = sys.pypy_version_info + data["implementation"]["version"] = ".".join( + [str(x) for x in pypy_version_info] + ) + elif data["implementation"]["name"] == 'Jython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'IronPython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + + if sys.platform.startswith("linux"): + from pip._vendor import distro + distro_infos = dict(filter( + lambda x: x[1], + zip(["name", "version", "id"], distro.linux_distribution()), + )) + libc = dict(filter( + lambda x: x[1], + zip(["lib", "version"], libc_ver()), + )) + if libc: + distro_infos["libc"] = libc + if distro_infos: + data["distro"] = distro_infos + + if sys.platform.startswith("darwin") and platform.mac_ver()[0]: + data["distro"] = {"name": "macOS", "version": platform.mac_ver()[0]} + + if platform.system(): + data.setdefault("system", {})["name"] = platform.system() + + if platform.release(): + data.setdefault("system", {})["release"] = platform.release() + + if platform.machine(): + data["cpu"] = platform.machine() + + if HAS_TLS: + data["openssl_version"] = ssl.OPENSSL_VERSION + + setuptools_version = get_installed_version("setuptools") + if setuptools_version is not None: + data["setuptools_version"] = setuptools_version + + return "{data[installer][name]}/{data[installer][version]} {json}".format( + data=data, + json=json.dumps(data, separators=(",", ":"), sort_keys=True), + ) + + +class MultiDomainBasicAuth(AuthBase): + + def __init__(self, prompting=True): + self.prompting = prompting + self.passwords = {} + + def __call__(self, req): + parsed = urllib_parse.urlparse(req.url) + + # Get the netloc without any embedded credentials + netloc = parsed.netloc.rsplit("@", 1)[-1] + + # Set the url of the request to the url without any credentials + req.url = urllib_parse.urlunparse(parsed[:1] + (netloc,) + parsed[2:]) + + # Use any stored credentials that we have for this netloc + username, password = self.passwords.get(netloc, (None, None)) + + # Extract credentials embedded in the url if we have none stored + if username is None: + username, password = self.parse_credentials(parsed.netloc) + + # Get creds from netrc if we still don't have them + if username is None and password is None: + netrc_auth = get_netrc_auth(req.url) + username, password = netrc_auth if netrc_auth else (None, None) + + if username or password: + # Store the username and password + self.passwords[netloc] = (username, password) + + # Send the basic auth with this request + req = HTTPBasicAuth(username or "", password or "")(req) + + # Attach a hook to handle 401 responses + req.register_hook("response", self.handle_401) + + return req + + def handle_401(self, resp, **kwargs): + # We only care about 401 responses, anything else we want to just + # pass through the actual response + if resp.status_code != 401: + return resp + + # We are not able to prompt the user so simply return the response + if not self.prompting: + return resp + + parsed = urllib_parse.urlparse(resp.url) + + # Prompt the user for a new username and password + username = six.moves.input("User for %s: " % parsed.netloc) + password = getpass.getpass("Password: ") + + # Store the new username and password to use for future requests + if username or password: + self.passwords[parsed.netloc] = (username, password) + + # Consume content and release the original connection to allow our new + # request to reuse the same one. + resp.content + resp.raw.release_conn() + + # Add our new username and password to the request + req = HTTPBasicAuth(username or "", password or "")(resp.request) + + # Send our new request + new_resp = resp.connection.send(req, **kwargs) + new_resp.history.append(resp) + + return new_resp + + def parse_credentials(self, netloc): + if "@" in netloc: + userinfo = netloc.rsplit("@", 1)[0] + if ":" in userinfo: + user, pwd = userinfo.split(":", 1) + return (urllib_unquote(user), urllib_unquote(pwd)) + return urllib_unquote(userinfo), None + return None, None + + +class LocalFSAdapter(BaseAdapter): + + def send(self, request, stream=None, timeout=None, verify=None, cert=None, + proxies=None): + pathname = url_to_path(request.url) + + resp = Response() + resp.status_code = 200 + resp.url = request.url + + try: + stats = os.stat(pathname) + except OSError as exc: + resp.status_code = 404 + resp.raw = exc + else: + modified = email.utils.formatdate(stats.st_mtime, usegmt=True) + content_type = mimetypes.guess_type(pathname)[0] or "text/plain" + resp.headers = CaseInsensitiveDict({ + "Content-Type": content_type, + "Content-Length": stats.st_size, + "Last-Modified": modified, + }) + + resp.raw = open(pathname, "rb") + resp.close = resp.raw.close + + return resp + + def close(self): + pass + + +class SafeFileCache(FileCache): + """ + A file based cache which is safe to use even when the target directory may + not be accessible or writable. + """ + + def __init__(self, *args, **kwargs): + super(SafeFileCache, self).__init__(*args, **kwargs) + + # Check to ensure that the directory containing our cache directory + # is owned by the user current executing pip. If it does not exist + # we will check the parent directory until we find one that does exist. + # If it is not owned by the user executing pip then we will disable + # the cache and log a warning. + if not check_path_owner(self.directory): + logger.warning( + "The directory '%s' or its parent directory is not owned by " + "the current user and the cache has been disabled. Please " + "check the permissions and owner of that directory. If " + "executing pip with sudo, you may want sudo's -H flag.", + self.directory, + ) + + # Set our directory to None to disable the Cache + self.directory = None + + def get(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return + + try: + return super(SafeFileCache, self).get(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + def set(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return + + try: + return super(SafeFileCache, self).set(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + def delete(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return + + try: + return super(SafeFileCache, self).delete(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + +class InsecureHTTPAdapter(HTTPAdapter): + + def cert_verify(self, conn, url, verify, cert): + conn.cert_reqs = 'CERT_NONE' + conn.ca_certs = None + + +class PipSession(requests.Session): + + timeout = None + + def __init__(self, *args, **kwargs): + retries = kwargs.pop("retries", 0) + cache = kwargs.pop("cache", None) + insecure_hosts = kwargs.pop("insecure_hosts", []) + + super(PipSession, self).__init__(*args, **kwargs) + + # Attach our User Agent to the request + self.headers["User-Agent"] = user_agent() + + # Attach our Authentication handler to the session + self.auth = MultiDomainBasicAuth() + + # Create our urllib3.Retry instance which will allow us to customize + # how we handle retries. + retries = urllib3.Retry( + # Set the total number of retries that a particular request can + # have. + total=retries, + + # A 503 error from PyPI typically means that the Fastly -> Origin + # connection got interrupted in some way. A 503 error in general + # is typically considered a transient error so we'll go ahead and + # retry it. + # A 500 may indicate transient error in Amazon S3 + # A 520 or 527 - may indicate transient error in CloudFlare + status_forcelist=[500, 503, 520, 527], + + # Add a small amount of back off between failed requests in + # order to prevent hammering the service. + backoff_factor=0.25, + ) + + # We want to _only_ cache responses on securely fetched origins. We do + # this because we can't validate the response of an insecurely fetched + # origin, and we don't want someone to be able to poison the cache and + # require manual eviction from the cache to fix it. + if cache: + secure_adapter = CacheControlAdapter( + cache=SafeFileCache(cache, use_dir_lock=True), + max_retries=retries, + ) + else: + secure_adapter = HTTPAdapter(max_retries=retries) + + # Our Insecure HTTPAdapter disables HTTPS validation. It does not + # support caching (see above) so we'll use it for all http:// URLs as + # well as any https:// host that we've marked as ignoring TLS errors + # for. + insecure_adapter = InsecureHTTPAdapter(max_retries=retries) + + self.mount("https://", secure_adapter) + self.mount("http://", insecure_adapter) + + # Enable file:// urls + self.mount("file://", LocalFSAdapter()) + + # We want to use a non-validating adapter for any requests which are + # deemed insecure. + for host in insecure_hosts: + self.mount("https://{}/".format(host), insecure_adapter) + + def request(self, method, url, *args, **kwargs): + # Allow setting a default timeout on a session + kwargs.setdefault("timeout", self.timeout) + + # Dispatch the actual request + return super(PipSession, self).request(method, url, *args, **kwargs) + + +def get_file_content(url, comes_from=None, session=None): + """Gets the content of a file; it may be a filename, file: URL, or + http: URL. Returns (location, content). Content is unicode. + + :param url: File path or url. + :param comes_from: Origin description of requirements. + :param session: Instance of pip.download.PipSession. + """ + if session is None: + raise TypeError( + "get_file_content() missing 1 required keyword argument: 'session'" + ) + + match = _scheme_re.search(url) + if match: + scheme = match.group(1).lower() + if (scheme == 'file' and comes_from and + comes_from.startswith('http')): + raise InstallationError( + 'Requirements file %s references URL %s, which is local' + % (comes_from, url)) + if scheme == 'file': + path = url.split(':', 1)[1] + path = path.replace('\\', '/') + match = _url_slash_drive_re.match(path) + if match: + path = match.group(1) + ':' + path.split('|', 1)[1] + path = urllib_parse.unquote(path) + if path.startswith('/'): + path = '/' + path.lstrip('/') + url = path + else: + # FIXME: catch some errors + resp = session.get(url) + resp.raise_for_status() + return resp.url, resp.text + try: + with open(url, 'rb') as f: + content = auto_decode(f.read()) + except IOError as exc: + raise InstallationError( + 'Could not open requirements file: %s' % str(exc) + ) + return url, content + + +_scheme_re = re.compile(r'^(http|https|file):', re.I) +_url_slash_drive_re = re.compile(r'/*([a-z])\|', re.I) + + +def is_url(name): + """Returns true if the name looks like a URL""" + if ':' not in name: + return False + scheme = name.split(':', 1)[0].lower() + return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes + + +def url_to_path(url): + """ + Convert a file: URL to a path. + """ + assert url.startswith('file:'), ( + "You can only turn file: urls into filenames (not %r)" % url) + + _, netloc, path, _, _ = urllib_parse.urlsplit(url) + + # if we have a UNC path, prepend UNC share notation + if netloc: + netloc = '\\\\' + netloc + + path = urllib_request.url2pathname(netloc + path) + return path + + +def path_to_url(path): + """ + Convert a path to a file: URL. The path will be made absolute and have + quoted path parts. + """ + path = os.path.normpath(os.path.abspath(path)) + url = urllib_parse.urljoin('file:', urllib_request.pathname2url(path)) + return url + + +def is_archive_file(name): + """Return True if `name` is a considered as an archive file.""" + ext = splitext(name)[1].lower() + if ext in ARCHIVE_EXTENSIONS: + return True + return False + + +def unpack_vcs_link(link, location): + vcs_backend = _get_used_vcs_backend(link) + vcs_backend.unpack(location) + + +def _get_used_vcs_backend(link): + for backend in vcs.backends: + if link.scheme in backend.schemes: + vcs_backend = backend(link.url) + return vcs_backend + + +def is_vcs_url(link): + return bool(_get_used_vcs_backend(link)) + + +def is_file_url(link): + return link.url.lower().startswith('file:') + + +def is_dir_url(link): + """Return whether a file:// Link points to a directory. + + ``link`` must not have any other scheme but file://. Call is_file_url() + first. + + """ + link_path = url_to_path(link.url_without_fragment) + return os.path.isdir(link_path) + + +def _progress_indicator(iterable, *args, **kwargs): + return iterable + + +def _download_url(resp, link, content_file, hashes, progress_bar): + try: + total_length = int(resp.headers['content-length']) + except (ValueError, KeyError, TypeError): + total_length = 0 + + cached_resp = getattr(resp, "from_cache", False) + if logger.getEffectiveLevel() > logging.INFO: + show_progress = False + elif cached_resp: + show_progress = False + elif total_length > (40 * 1000): + show_progress = True + elif not total_length: + show_progress = True + else: + show_progress = False + + show_url = link.show_url + + def resp_read(chunk_size): + try: + # Special case for urllib3. + for chunk in resp.raw.stream( + chunk_size, + # We use decode_content=False here because we don't + # want urllib3 to mess with the raw bytes we get + # from the server. If we decompress inside of + # urllib3 then we cannot verify the checksum + # because the checksum will be of the compressed + # file. This breakage will only occur if the + # server adds a Content-Encoding header, which + # depends on how the server was configured: + # - Some servers will notice that the file isn't a + # compressible file and will leave the file alone + # and with an empty Content-Encoding + # - Some servers will notice that the file is + # already compressed and will leave the file + # alone and will add a Content-Encoding: gzip + # header + # - Some servers won't notice anything at all and + # will take a file that's already been compressed + # and compress it again and set the + # Content-Encoding: gzip header + # + # By setting this not to decode automatically we + # hope to eliminate problems with the second case. + decode_content=False): + yield chunk + except AttributeError: + # Standard file-like object. + while True: + chunk = resp.raw.read(chunk_size) + if not chunk: + break + yield chunk + + def written_chunks(chunks): + for chunk in chunks: + content_file.write(chunk) + yield chunk + + progress_indicator = _progress_indicator + + if link.netloc == PyPI.netloc: + url = show_url + else: + url = link.url_without_fragment + + if show_progress: # We don't show progress on cached responses + progress_indicator = DownloadProgressProvider(progress_bar, + max=total_length) + if total_length: + logger.info("Downloading %s (%s)", url, format_size(total_length)) + else: + logger.info("Downloading %s", url) + elif cached_resp: + logger.info("Using cached %s", url) + else: + logger.info("Downloading %s", url) + + logger.debug('Downloading from URL %s', link) + + downloaded_chunks = written_chunks( + progress_indicator( + resp_read(CONTENT_CHUNK_SIZE), + CONTENT_CHUNK_SIZE + ) + ) + if hashes: + hashes.check_against_chunks(downloaded_chunks) + else: + consume(downloaded_chunks) + + +def _copy_file(filename, location, link): + copy = True + download_location = os.path.join(location, link.filename) + if os.path.exists(download_location): + response = ask_path_exists( + 'The file %s exists. (i)gnore, (w)ipe, (b)ackup, (a)abort' % + display_path(download_location), ('i', 'w', 'b', 'a')) + if response == 'i': + copy = False + elif response == 'w': + logger.warning('Deleting %s', display_path(download_location)) + os.remove(download_location) + elif response == 'b': + dest_file = backup_dir(download_location) + logger.warning( + 'Backing up %s to %s', + display_path(download_location), + display_path(dest_file), + ) + shutil.move(download_location, dest_file) + elif response == 'a': + sys.exit(-1) + if copy: + shutil.copy(filename, download_location) + logger.info('Saved %s', display_path(download_location)) + + +def unpack_http_url(link, location, download_dir=None, + session=None, hashes=None, progress_bar="on"): + if session is None: + raise TypeError( + "unpack_http_url() missing 1 required keyword argument: 'session'" + ) + + with TempDirectory(kind="unpack") as temp_dir: + # If a download dir is specified, is the file already downloaded there? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir(link, + download_dir, + hashes) + + if already_downloaded_path: + from_path = already_downloaded_path + content_type = mimetypes.guess_type(from_path)[0] + else: + # let's download to a tmp dir + from_path, content_type = _download_http_url(link, + session, + temp_dir.path, + hashes, + progress_bar) + + # unpack the archive to the build dir location. even when only + # downloading archives, they have to be unpacked to parse dependencies + unpack_file(from_path, location, content_type, link) + + # a download dir is specified; let's copy the archive there + if download_dir and not already_downloaded_path: + _copy_file(from_path, download_dir, link) + + if not already_downloaded_path: + os.unlink(from_path) + + +def unpack_file_url(link, location, download_dir=None, hashes=None): + """Unpack link into location. + + If download_dir is provided and link points to a file, make a copy + of the link file inside download_dir. + """ + link_path = url_to_path(link.url_without_fragment) + + # If it's a url to a local directory + if is_dir_url(link): + if os.path.isdir(location): + rmtree(location) + shutil.copytree(link_path, location, symlinks=True) + if download_dir: + logger.info('Link is a directory, ignoring download_dir') + return + + # If --require-hashes is off, `hashes` is either empty, the + # link's embedded hash, or MissingHashes; it is required to + # match. If --require-hashes is on, we are satisfied by any + # hash in `hashes` matching: a URL-based or an option-based + # one; no internet-sourced hash will be in `hashes`. + if hashes: + hashes.check_against_path(link_path) + + # If a download dir is specified, is the file already there and valid? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir(link, + download_dir, + hashes) + + if already_downloaded_path: + from_path = already_downloaded_path + else: + from_path = link_path + + content_type = mimetypes.guess_type(from_path)[0] + + # unpack the archive to the build dir location. even when only downloading + # archives, they have to be unpacked to parse dependencies + unpack_file(from_path, location, content_type, link) + + # a download dir is specified and not already downloaded + if download_dir and not already_downloaded_path: + _copy_file(from_path, download_dir, link) + + +def _copy_dist_from_dir(link_path, location): + """Copy distribution files in `link_path` to `location`. + + Invoked when user requests to install a local directory. E.g.: + + pip install . + pip install ~/dev/git-repos/python-prompt-toolkit + + """ + + # Note: This is currently VERY SLOW if you have a lot of data in the + # directory, because it copies everything with `shutil.copytree`. + # What it should really do is build an sdist and install that. + # See https://github.com/pypa/pip/issues/2195 + + if os.path.isdir(location): + rmtree(location) + + # build an sdist + setup_py = 'setup.py' + sdist_args = [sys.executable] + sdist_args.append('-c') + sdist_args.append(SETUPTOOLS_SHIM % setup_py) + sdist_args.append('sdist') + sdist_args += ['--dist-dir', location] + logger.info('Running setup.py sdist for %s', link_path) + + with indent_log(): + call_subprocess(sdist_args, cwd=link_path, show_stdout=False) + + # unpack sdist into `location` + sdist = os.path.join(location, os.listdir(location)[0]) + logger.info('Unpacking sdist %s into %s', sdist, location) + unpack_file(sdist, location, content_type=None, link=None) + + +class PipXmlrpcTransport(xmlrpc_client.Transport): + """Provide a `xmlrpclib.Transport` implementation via a `PipSession` + object. + """ + + def __init__(self, index_url, session, use_datetime=False): + xmlrpc_client.Transport.__init__(self, use_datetime) + index_parts = urllib_parse.urlparse(index_url) + self._scheme = index_parts.scheme + self._session = session + + def request(self, host, handler, request_body, verbose=False): + parts = (self._scheme, host, handler, None, None, None) + url = urllib_parse.urlunparse(parts) + try: + headers = {'Content-Type': 'text/xml'} + response = self._session.post(url, data=request_body, + headers=headers, stream=True) + response.raise_for_status() + self.verbose = verbose + return self.parse_response(response.raw) + except requests.HTTPError as exc: + logger.critical( + "HTTP error %s while getting %s", + exc.response.status_code, url, + ) + raise + + +def unpack_url(link, location, download_dir=None, + only_download=False, session=None, hashes=None, + progress_bar="on"): + """Unpack link. + If link is a VCS link: + if only_download, export into download_dir and ignore location + else unpack into location + for other types of link: + - unpack into location + - if download_dir, copy the file into download_dir + - if only_download, mark location for deletion + + :param hashes: A Hashes object, one of whose embedded hashes must match, + or HashMismatch will be raised. If the Hashes is empty, no matches are + required, and unhashable types of requirements (like VCS ones, which + would ordinarily raise HashUnsupported) are allowed. + """ + # non-editable vcs urls + if is_vcs_url(link): + unpack_vcs_link(link, location) + + # file urls + elif is_file_url(link): + unpack_file_url(link, location, download_dir, hashes=hashes) + + # http urls + else: + if session is None: + session = PipSession() + + unpack_http_url( + link, + location, + download_dir, + session, + hashes=hashes, + progress_bar=progress_bar + ) + if only_download: + write_delete_marker_file(location) + + +def _download_http_url(link, session, temp_dir, hashes, progress_bar): + """Download link url into temp_dir using provided session""" + target_url = link.url.split('#', 1)[0] + try: + resp = session.get( + target_url, + # We use Accept-Encoding: identity here because requests + # defaults to accepting compressed responses. This breaks in + # a variety of ways depending on how the server is configured. + # - Some servers will notice that the file isn't a compressible + # file and will leave the file alone and with an empty + # Content-Encoding + # - Some servers will notice that the file is already + # compressed and will leave the file alone and will add a + # Content-Encoding: gzip header + # - Some servers won't notice anything at all and will take + # a file that's already been compressed and compress it again + # and set the Content-Encoding: gzip header + # By setting this to request only the identity encoding We're + # hoping to eliminate the third case. Hopefully there does not + # exist a server which when given a file will notice it is + # already compressed and that you're not asking for a + # compressed file and will then decompress it before sending + # because if that's the case I don't think it'll ever be + # possible to make this work. + headers={"Accept-Encoding": "identity"}, + stream=True, + ) + resp.raise_for_status() + except requests.HTTPError as exc: + logger.critical( + "HTTP error %s while getting %s", exc.response.status_code, link, + ) + raise + + content_type = resp.headers.get('content-type', '') + filename = link.filename # fallback + # Have a look at the Content-Disposition header for a better guess + content_disposition = resp.headers.get('content-disposition') + if content_disposition: + type, params = cgi.parse_header(content_disposition) + # We use ``or`` here because we don't want to use an "empty" value + # from the filename param. + filename = params.get('filename') or filename + ext = splitext(filename)[1] + if not ext: + ext = mimetypes.guess_extension(content_type) + if ext: + filename += ext + if not ext and link.url != resp.url: + ext = os.path.splitext(resp.url)[1] + if ext: + filename += ext + file_path = os.path.join(temp_dir, filename) + with open(file_path, 'wb') as content_file: + _download_url(resp, link, content_file, hashes, progress_bar) + return file_path, content_type + + +def _check_download_dir(link, download_dir, hashes): + """ Check download_dir for previously downloaded file with correct hash + If a correct file is found return its path else None + """ + download_path = os.path.join(download_dir, link.filename) + if os.path.exists(download_path): + # If already downloaded, does its hash match? + logger.info('File was already downloaded %s', download_path) + if hashes: + try: + hashes.check_against_path(download_path) + except HashMismatch: + logger.warning( + 'Previously-downloaded file %s has bad hash. ' + 'Re-downloading.', + download_path + ) + os.unlink(download_path) + return None + return download_path + return None diff --git a/venv/Lib/site-packages/pip/_internal/exceptions.py b/venv/Lib/site-packages/pip/_internal/exceptions.py new file mode 100644 index 0000000..f1ca6f3 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/exceptions.py @@ -0,0 +1,268 @@ +"""Exceptions used throughout package""" +from __future__ import absolute_import + +from itertools import chain, groupby, repeat + +from pip._vendor.six import iteritems + + +class PipError(Exception): + """Base pip exception""" + + +class ConfigurationError(PipError): + """General exception in configuration""" + + +class InstallationError(PipError): + """General exception during installation""" + + +class UninstallationError(PipError): + """General exception during uninstallation""" + + +class DistributionNotFound(InstallationError): + """Raised when a distribution cannot be found to satisfy a requirement""" + + +class RequirementsFileParseError(InstallationError): + """Raised when a general error occurs parsing a requirements file line.""" + + +class BestVersionAlreadyInstalled(PipError): + """Raised when the most up-to-date version of a package is already + installed.""" + + +class BadCommand(PipError): + """Raised when virtualenv or a command is not found""" + + +class CommandError(PipError): + """Raised when there is an error in command-line arguments""" + + +class PreviousBuildDirError(PipError): + """Raised when there's a previous conflicting build directory""" + + +class InvalidWheelFilename(InstallationError): + """Invalid wheel filename.""" + + +class UnsupportedWheel(InstallationError): + """Unsupported wheel.""" + + +class HashErrors(InstallationError): + """Multiple HashError instances rolled into one for reporting""" + + def __init__(self): + self.errors = [] + + def append(self, error): + self.errors.append(error) + + def __str__(self): + lines = [] + self.errors.sort(key=lambda e: e.order) + for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__): + lines.append(cls.head) + lines.extend(e.body() for e in errors_of_cls) + if lines: + return '\n'.join(lines) + + def __nonzero__(self): + return bool(self.errors) + + def __bool__(self): + return self.__nonzero__() + + +class HashError(InstallationError): + """ + A failure to verify a package against known-good hashes + + :cvar order: An int sorting hash exception classes by difficulty of + recovery (lower being harder), so the user doesn't bother fretting + about unpinned packages when he has deeper issues, like VCS + dependencies, to deal with. Also keeps error reports in a + deterministic order. + :cvar head: A section heading for display above potentially many + exceptions of this kind + :ivar req: The InstallRequirement that triggered this error. This is + pasted on after the exception is instantiated, because it's not + typically available earlier. + + """ + req = None + head = '' + + def body(self): + """Return a summary of me for display under the heading. + + This default implementation simply prints a description of the + triggering requirement. + + :param req: The InstallRequirement that provoked this error, with + populate_link() having already been called + + """ + return ' %s' % self._requirement_name() + + def __str__(self): + return '%s\n%s' % (self.head, self.body()) + + def _requirement_name(self): + """Return a description of the requirement that triggered me. + + This default implementation returns long description of the req, with + line numbers + + """ + return str(self.req) if self.req else 'unknown package' + + +class VcsHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 0 + head = ("Can't verify hashes for these requirements because we don't " + "have a way to hash version control repositories:") + + +class DirectoryUrlHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 1 + head = ("Can't verify hashes for these file:// requirements because they " + "point to directories:") + + +class HashMissing(HashError): + """A hash was needed for a requirement but is absent.""" + + order = 2 + head = ('Hashes are required in --require-hashes mode, but they are ' + 'missing from some requirements. Here is a list of those ' + 'requirements along with the hashes their downloaded archives ' + 'actually had. Add lines like these to your requirements files to ' + 'prevent tampering. (If you did not enable --require-hashes ' + 'manually, note that it turns on automatically when any package ' + 'has a hash.)') + + def __init__(self, gotten_hash): + """ + :param gotten_hash: The hash of the (possibly malicious) archive we + just downloaded + """ + self.gotten_hash = gotten_hash + + def body(self): + # Dodge circular import. + from pip._internal.utils.hashes import FAVORITE_HASH + + package = None + if self.req: + # In the case of URL-based requirements, display the original URL + # seen in the requirements file rather than the package name, + # so the output can be directly copied into the requirements file. + package = (self.req.original_link if self.req.original_link + # In case someone feeds something downright stupid + # to InstallRequirement's constructor. + else getattr(self.req, 'req', None)) + return ' %s --hash=%s:%s' % (package or 'unknown package', + FAVORITE_HASH, + self.gotten_hash) + + +class HashUnpinned(HashError): + """A requirement had a hash specified but was not pinned to a specific + version.""" + + order = 3 + head = ('In --require-hashes mode, all requirements must have their ' + 'versions pinned with ==. These do not:') + + +class HashMismatch(HashError): + """ + Distribution file hash values don't match. + + :ivar package_name: The name of the package that triggered the hash + mismatch. Feel free to write to this after the exception is raise to + improve its error message. + + """ + order = 4 + head = ('THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS ' + 'FILE. If you have updated the package versions, please update ' + 'the hashes. Otherwise, examine the package contents carefully; ' + 'someone may have tampered with them.') + + def __init__(self, allowed, gots): + """ + :param allowed: A dict of algorithm names pointing to lists of allowed + hex digests + :param gots: A dict of algorithm names pointing to hashes we + actually got from the files under suspicion + """ + self.allowed = allowed + self.gots = gots + + def body(self): + return ' %s:\n%s' % (self._requirement_name(), + self._hash_comparison()) + + def _hash_comparison(self): + """ + Return a comparison of actual and expected hash values. + + Example:: + + Expected sha256 abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde + or 123451234512345123451234512345123451234512345 + Got bcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdef + + """ + def hash_then_or(hash_name): + # For now, all the decent hashes have 6-char names, so we can get + # away with hard-coding space literals. + return chain([hash_name], repeat(' or')) + + lines = [] + for hash_name, expecteds in iteritems(self.allowed): + prefix = hash_then_or(hash_name) + lines.extend((' Expected %s %s' % (next(prefix), e)) + for e in expecteds) + lines.append(' Got %s\n' % + self.gots[hash_name].hexdigest()) + prefix = ' or' + return '\n'.join(lines) + + +class UnsupportedPythonVersion(InstallationError): + """Unsupported python version according to Requires-Python package + metadata.""" + + +class ConfigurationFileCouldNotBeLoaded(ConfigurationError): + """When there are errors while loading a configuration file + """ + + def __init__(self, reason="could not be loaded", fname=None, error=None): + super(ConfigurationFileCouldNotBeLoaded, self).__init__(error) + self.reason = reason + self.fname = fname + self.error = error + + def __str__(self): + if self.fname is not None: + message_part = " in {}.".format(self.fname) + else: + assert self.error is not None + message_part = ".\n{}\n".format(self.error.message) + return "Configuration file {}{}".format(self.reason, message_part) diff --git a/venv/Lib/site-packages/pip/_internal/index.py b/venv/Lib/site-packages/pip/_internal/index.py new file mode 100644 index 0000000..8c2f24f --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/index.py @@ -0,0 +1,899 @@ +"""Routines related to PyPI, indexes""" +from __future__ import absolute_import + +import cgi +import itertools +import logging +import mimetypes +import os +import posixpath +import re +import sys +from collections import namedtuple + +from pip._vendor import html5lib, requests, six +from pip._vendor.distlib.compat import unescape +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.requests.exceptions import SSLError +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip._internal.download import HAS_TLS, is_url, path_to_url, url_to_path +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, DistributionNotFound, InvalidWheelFilename, + UnsupportedWheel, +) +from pip._internal.models.candidate import InstallationCandidate +from pip._internal.models.format_control import FormatControl +from pip._internal.models.index import PyPI +from pip._internal.models.link import Link +from pip._internal.pep425tags import get_supported +from pip._internal.utils.compat import ipaddress +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + ARCHIVE_EXTENSIONS, SUPPORTED_EXTENSIONS, normalize_path, + remove_auth_from_url, +) +from pip._internal.utils.packaging import check_requires_python +from pip._internal.wheel import Wheel, wheel_ext + +__all__ = ['FormatControl', 'PackageFinder'] + + +SECURE_ORIGINS = [ + # protocol, hostname, port + # Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC) + ("https", "*", "*"), + ("*", "localhost", "*"), + ("*", "127.0.0.0/8", "*"), + ("*", "::1/128", "*"), + ("file", "*", None), + # ssh is always secure. + ("ssh", "*", "*"), +] + + +logger = logging.getLogger(__name__) + + +def _get_content_type(url, session): + """Get the Content-Type of the given url, using a HEAD request""" + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(url) + if scheme not in {'http', 'https'}: + # FIXME: some warning or something? + # assertion error? + return '' + + resp = session.head(url, allow_redirects=True) + resp.raise_for_status() + + return resp.headers.get("Content-Type", "") + + +def _handle_get_page_fail(link, reason, url, meth=None): + if meth is None: + meth = logger.debug + meth("Could not fetch URL %s: %s - skipping", link, reason) + + +def _get_html_page(link, session=None): + if session is None: + raise TypeError( + "_get_html_page() missing 1 required keyword argument: 'session'" + ) + + url = link.url + url = url.split('#', 1)[0] + + # Check for VCS schemes that do not support lookup as web pages. + from pip._internal.vcs import VcsSupport + for scheme in VcsSupport.schemes: + if url.lower().startswith(scheme) and url[len(scheme)] in '+:': + logger.debug('Cannot look at %s URL %s', scheme, link) + return None + + try: + filename = link.filename + for bad_ext in ARCHIVE_EXTENSIONS: + if filename.endswith(bad_ext): + content_type = _get_content_type(url, session=session) + if content_type.lower().startswith('text/html'): + break + else: + logger.debug( + 'Skipping page %s because of Content-Type: %s', + link, + content_type, + ) + return + + logger.debug('Getting page %s', url) + + # Tack index.html onto file:// URLs that point to directories + (scheme, netloc, path, params, query, fragment) = \ + urllib_parse.urlparse(url) + if (scheme == 'file' and + os.path.isdir(urllib_request.url2pathname(path))): + # add trailing slash if not present so urljoin doesn't trim + # final segment + if not url.endswith('/'): + url += '/' + url = urllib_parse.urljoin(url, 'index.html') + logger.debug(' file: URL is directory, getting %s', url) + + resp = session.get( + url, + headers={ + "Accept": "text/html", + # We don't want to blindly returned cached data for + # /simple/, because authors generally expecting that + # twine upload && pip install will function, but if + # they've done a pip install in the last ~10 minutes + # it won't. Thus by setting this to zero we will not + # blindly use any cached data, however the benefit of + # using max-age=0 instead of no-cache, is that we will + # still support conditional requests, so we will still + # minimize traffic sent in cases where the page hasn't + # changed at all, we will just always incur the round + # trip for the conditional GET now instead of only + # once per 10 minutes. + # For more information, please see pypa/pip#5670. + "Cache-Control": "max-age=0", + }, + ) + resp.raise_for_status() + + # The check for archives above only works if the url ends with + # something that looks like an archive. However that is not a + # requirement of an url. Unless we issue a HEAD request on every + # url we cannot know ahead of time for sure if something is HTML + # or not. However we can check after we've downloaded it. + content_type = resp.headers.get('Content-Type', 'unknown') + if not content_type.lower().startswith("text/html"): + logger.debug( + 'Skipping page %s because of Content-Type: %s', + link, + content_type, + ) + return + + inst = HTMLPage(resp.content, resp.url, resp.headers) + except requests.HTTPError as exc: + _handle_get_page_fail(link, exc, url) + except SSLError as exc: + reason = "There was a problem confirming the ssl certificate: " + reason += str(exc) + _handle_get_page_fail(link, reason, url, meth=logger.info) + except requests.ConnectionError as exc: + _handle_get_page_fail(link, "connection error: %s" % exc, url) + except requests.Timeout: + _handle_get_page_fail(link, "timed out", url) + else: + return inst + + +class PackageFinder(object): + """This finds packages. + + This is meant to match easy_install's technique for looking for + packages, by reading pages and looking for appropriate links. + """ + + def __init__(self, find_links, index_urls, allow_all_prereleases=False, + trusted_hosts=None, process_dependency_links=False, + session=None, format_control=None, platform=None, + versions=None, abi=None, implementation=None, + prefer_binary=False): + """Create a PackageFinder. + + :param format_control: A FormatControl object or None. Used to control + the selection of source packages / binary packages when consulting + the index and links. + :param platform: A string or None. If None, searches for packages + that are supported by the current system. Otherwise, will find + packages that can be built on the platform passed in. These + packages will only be downloaded for distribution: they will + not be built locally. + :param versions: A list of strings or None. This is passed directly + to pep425tags.py in the get_supported() method. + :param abi: A string or None. This is passed directly + to pep425tags.py in the get_supported() method. + :param implementation: A string or None. This is passed directly + to pep425tags.py in the get_supported() method. + """ + if session is None: + raise TypeError( + "PackageFinder() missing 1 required keyword argument: " + "'session'" + ) + + # Build find_links. If an argument starts with ~, it may be + # a local file relative to a home directory. So try normalizing + # it and if it exists, use the normalized version. + # This is deliberately conservative - it might be fine just to + # blindly normalize anything starting with a ~... + self.find_links = [] + for link in find_links: + if link.startswith('~'): + new_link = normalize_path(link) + if os.path.exists(new_link): + link = new_link + self.find_links.append(link) + + self.index_urls = index_urls + self.dependency_links = [] + + # These are boring links that have already been logged somehow: + self.logged_links = set() + + self.format_control = format_control or FormatControl(set(), set()) + + # Domains that we won't emit warnings for when not using HTTPS + self.secure_origins = [ + ("*", host, "*") + for host in (trusted_hosts if trusted_hosts else []) + ] + + # Do we want to allow _all_ pre-releases? + self.allow_all_prereleases = allow_all_prereleases + + # Do we process dependency links? + self.process_dependency_links = process_dependency_links + + # The Session we'll use to make requests + self.session = session + + # The valid tags to check potential found wheel candidates against + self.valid_tags = get_supported( + versions=versions, + platform=platform, + abi=abi, + impl=implementation, + ) + + # Do we prefer old, but valid, binary dist over new source dist + self.prefer_binary = prefer_binary + + # If we don't have TLS enabled, then WARN if anyplace we're looking + # relies on TLS. + if not HAS_TLS: + for link in itertools.chain(self.index_urls, self.find_links): + parsed = urllib_parse.urlparse(link) + if parsed.scheme == "https": + logger.warning( + "pip is configured with locations that require " + "TLS/SSL, however the ssl module in Python is not " + "available." + ) + break + + def get_formatted_locations(self): + lines = [] + if self.index_urls and self.index_urls != [PyPI.simple_url]: + lines.append( + "Looking in indexes: {}".format(", ".join( + remove_auth_from_url(url) for url in self.index_urls)) + ) + if self.find_links: + lines.append( + "Looking in links: {}".format(", ".join(self.find_links)) + ) + return "\n".join(lines) + + def add_dependency_links(self, links): + # FIXME: this shouldn't be global list this, it should only + # apply to requirements of the package that specifies the + # dependency_links value + # FIXME: also, we should track comes_from (i.e., use Link) + if self.process_dependency_links: + deprecated( + "Dependency Links processing has been deprecated and will be " + "removed in a future release.", + replacement="PEP 508 URL dependencies", + gone_in="18.2", + issue=4187, + ) + self.dependency_links.extend(links) + + @staticmethod + def _sort_locations(locations, expand_dir=False): + """ + Sort locations into "files" (archives) and "urls", and return + a pair of lists (files,urls) + """ + files = [] + urls = [] + + # puts the url for the given file path into the appropriate list + def sort_path(path): + url = path_to_url(path) + if mimetypes.guess_type(url, strict=False)[0] == 'text/html': + urls.append(url) + else: + files.append(url) + + for url in locations: + + is_local_path = os.path.exists(url) + is_file_url = url.startswith('file:') + + if is_local_path or is_file_url: + if is_local_path: + path = url + else: + path = url_to_path(url) + if os.path.isdir(path): + if expand_dir: + path = os.path.realpath(path) + for item in os.listdir(path): + sort_path(os.path.join(path, item)) + elif is_file_url: + urls.append(url) + elif os.path.isfile(path): + sort_path(path) + else: + logger.warning( + "Url '%s' is ignored: it is neither a file " + "nor a directory.", url, + ) + elif is_url(url): + # Only add url with clear scheme + urls.append(url) + else: + logger.warning( + "Url '%s' is ignored. It is either a non-existing " + "path or lacks a specific scheme.", url, + ) + + return files, urls + + def _candidate_sort_key(self, candidate): + """ + Function used to generate link sort key for link tuples. + The greater the return value, the more preferred it is. + If not finding wheels, then sorted by version only. + If finding wheels, then the sort order is by version, then: + 1. existing installs + 2. wheels ordered via Wheel.support_index_min(self.valid_tags) + 3. source archives + If prefer_binary was set, then all wheels are sorted above sources. + Note: it was considered to embed this logic into the Link + comparison operators, but then different sdist links + with the same version, would have to be considered equal + """ + support_num = len(self.valid_tags) + build_tag = tuple() + binary_preference = 0 + if candidate.location.is_wheel: + # can raise InvalidWheelFilename + wheel = Wheel(candidate.location.filename) + if not wheel.supported(self.valid_tags): + raise UnsupportedWheel( + "%s is not a supported wheel for this platform. It " + "can't be sorted." % wheel.filename + ) + if self.prefer_binary: + binary_preference = 1 + pri = -(wheel.support_index_min(self.valid_tags)) + if wheel.build_tag is not None: + match = re.match(r'^(\d+)(.*)$', wheel.build_tag) + build_tag_groups = match.groups() + build_tag = (int(build_tag_groups[0]), build_tag_groups[1]) + else: # sdist + pri = -(support_num) + return (binary_preference, candidate.version, build_tag, pri) + + def _validate_secure_origin(self, logger, location): + # Determine if this url used a secure transport mechanism + parsed = urllib_parse.urlparse(str(location)) + origin = (parsed.scheme, parsed.hostname, parsed.port) + + # The protocol to use to see if the protocol matches. + # Don't count the repository type as part of the protocol: in + # cases such as "git+ssh", only use "ssh". (I.e., Only verify against + # the last scheme.) + protocol = origin[0].rsplit('+', 1)[-1] + + # Determine if our origin is a secure origin by looking through our + # hardcoded list of secure origins, as well as any additional ones + # configured on this PackageFinder instance. + for secure_origin in (SECURE_ORIGINS + self.secure_origins): + if protocol != secure_origin[0] and secure_origin[0] != "*": + continue + + try: + # We need to do this decode dance to ensure that we have a + # unicode object, even on Python 2.x. + addr = ipaddress.ip_address( + origin[1] + if ( + isinstance(origin[1], six.text_type) or + origin[1] is None + ) + else origin[1].decode("utf8") + ) + network = ipaddress.ip_network( + secure_origin[1] + if isinstance(secure_origin[1], six.text_type) + else secure_origin[1].decode("utf8") + ) + except ValueError: + # We don't have both a valid address or a valid network, so + # we'll check this origin against hostnames. + if (origin[1] and + origin[1].lower() != secure_origin[1].lower() and + secure_origin[1] != "*"): + continue + else: + # We have a valid address and network, so see if the address + # is contained within the network. + if addr not in network: + continue + + # Check to see if the port patches + if (origin[2] != secure_origin[2] and + secure_origin[2] != "*" and + secure_origin[2] is not None): + continue + + # If we've gotten here, then this origin matches the current + # secure origin and we should return True + return True + + # If we've gotten to this point, then the origin isn't secure and we + # will not accept it as a valid location to search. We will however + # log a warning that we are ignoring it. + logger.warning( + "The repository located at %s is not a trusted or secure host and " + "is being ignored. If this repository is available via HTTPS we " + "recommend you use HTTPS instead, otherwise you may silence " + "this warning and allow it anyway with '--trusted-host %s'.", + parsed.hostname, + parsed.hostname, + ) + + return False + + def _get_index_urls_locations(self, project_name): + """Returns the locations found via self.index_urls + + Checks the url_name on the main (first in the list) index and + use this url_name to produce all locations + """ + + def mkurl_pypi_url(url): + loc = posixpath.join( + url, + urllib_parse.quote(canonicalize_name(project_name))) + # For maximum compatibility with easy_install, ensure the path + # ends in a trailing slash. Although this isn't in the spec + # (and PyPI can handle it without the slash) some other index + # implementations might break if they relied on easy_install's + # behavior. + if not loc.endswith('/'): + loc = loc + '/' + return loc + + return [mkurl_pypi_url(url) for url in self.index_urls] + + def find_all_candidates(self, project_name): + """Find all available InstallationCandidate for project_name + + This checks index_urls, find_links and dependency_links. + All versions found are returned as an InstallationCandidate list. + + See _link_package_versions for details on which files are accepted + """ + index_locations = self._get_index_urls_locations(project_name) + index_file_loc, index_url_loc = self._sort_locations(index_locations) + fl_file_loc, fl_url_loc = self._sort_locations( + self.find_links, expand_dir=True, + ) + dep_file_loc, dep_url_loc = self._sort_locations(self.dependency_links) + + file_locations = (Link(url) for url in itertools.chain( + index_file_loc, fl_file_loc, dep_file_loc, + )) + + # We trust every url that the user has given us whether it was given + # via --index-url or --find-links + # We explicitly do not trust links that came from dependency_links + # We want to filter out any thing which does not have a secure origin. + url_locations = [ + link for link in itertools.chain( + (Link(url) for url in index_url_loc), + (Link(url) for url in fl_url_loc), + (Link(url) for url in dep_url_loc), + ) + if self._validate_secure_origin(logger, link) + ] + + logger.debug('%d location(s) to search for versions of %s:', + len(url_locations), project_name) + + for location in url_locations: + logger.debug('* %s', location) + + canonical_name = canonicalize_name(project_name) + formats = self.format_control.get_allowed_formats(canonical_name) + search = Search(project_name, canonical_name, formats) + find_links_versions = self._package_versions( + # We trust every directly linked archive in find_links + (Link(url, '-f') for url in self.find_links), + search + ) + + page_versions = [] + for page in self._get_pages(url_locations, project_name): + logger.debug('Analyzing links from page %s', page.url) + with indent_log(): + page_versions.extend( + self._package_versions(page.iter_links(), search) + ) + + dependency_versions = self._package_versions( + (Link(url) for url in self.dependency_links), search + ) + if dependency_versions: + logger.debug( + 'dependency_links found: %s', + ', '.join([ + version.location.url for version in dependency_versions + ]) + ) + + file_versions = self._package_versions(file_locations, search) + if file_versions: + file_versions.sort(reverse=True) + logger.debug( + 'Local files found: %s', + ', '.join([ + url_to_path(candidate.location.url) + for candidate in file_versions + ]) + ) + + # This is an intentional priority ordering + return ( + file_versions + find_links_versions + page_versions + + dependency_versions + ) + + def find_requirement(self, req, upgrade): + """Try to find a Link matching req + + Expects req, an InstallRequirement and upgrade, a boolean + Returns a Link if found, + Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise + """ + all_candidates = self.find_all_candidates(req.name) + + # Filter out anything which doesn't match our specifier + compatible_versions = set( + req.specifier.filter( + # We turn the version object into a str here because otherwise + # when we're debundled but setuptools isn't, Python will see + # packaging.version.Version and + # pkg_resources._vendor.packaging.version.Version as different + # types. This way we'll use a str as a common data interchange + # format. If we stop using the pkg_resources provided specifier + # and start using our own, we can drop the cast to str(). + [str(c.version) for c in all_candidates], + prereleases=( + self.allow_all_prereleases + if self.allow_all_prereleases else None + ), + ) + ) + applicable_candidates = [ + # Again, converting to str to deal with debundling. + c for c in all_candidates if str(c.version) in compatible_versions + ] + + if applicable_candidates: + best_candidate = max(applicable_candidates, + key=self._candidate_sort_key) + else: + best_candidate = None + + if req.satisfied_by is not None: + installed_version = parse_version(req.satisfied_by.version) + else: + installed_version = None + + if installed_version is None and best_candidate is None: + logger.critical( + 'Could not find a version that satisfies the requirement %s ' + '(from versions: %s)', + req, + ', '.join( + sorted( + {str(c.version) for c in all_candidates}, + key=parse_version, + ) + ) + ) + + raise DistributionNotFound( + 'No matching distribution found for %s' % req + ) + + best_installed = False + if installed_version and ( + best_candidate is None or + best_candidate.version <= installed_version): + best_installed = True + + if not upgrade and installed_version is not None: + if best_installed: + logger.debug( + 'Existing installed version (%s) is most up-to-date and ' + 'satisfies requirement', + installed_version, + ) + else: + logger.debug( + 'Existing installed version (%s) satisfies requirement ' + '(most up-to-date version is %s)', + installed_version, + best_candidate.version, + ) + return None + + if best_installed: + # We have an existing version, and its the best version + logger.debug( + 'Installed version (%s) is most up-to-date (past versions: ' + '%s)', + installed_version, + ', '.join(sorted(compatible_versions, key=parse_version)) or + "none", + ) + raise BestVersionAlreadyInstalled + + logger.debug( + 'Using version %s (newest of versions: %s)', + best_candidate.version, + ', '.join(sorted(compatible_versions, key=parse_version)) + ) + return best_candidate.location + + def _get_pages(self, locations, project_name): + """ + Yields (page, page_url) from the given locations, skipping + locations that have errors. + """ + seen = set() + for location in locations: + if location in seen: + continue + seen.add(location) + + page = self._get_page(location) + if page is None: + continue + + yield page + + _py_version_re = re.compile(r'-py([123]\.?[0-9]?)$') + + def _sort_links(self, links): + """ + Returns elements of links in order, non-egg links first, egg links + second, while eliminating duplicates + """ + eggs, no_eggs = [], [] + seen = set() + for link in links: + if link not in seen: + seen.add(link) + if link.egg_fragment: + eggs.append(link) + else: + no_eggs.append(link) + return no_eggs + eggs + + def _package_versions(self, links, search): + result = [] + for link in self._sort_links(links): + v = self._link_package_versions(link, search) + if v is not None: + result.append(v) + return result + + def _log_skipped_link(self, link, reason): + if link not in self.logged_links: + logger.debug('Skipping link %s; %s', link, reason) + self.logged_links.add(link) + + def _link_package_versions(self, link, search): + """Return an InstallationCandidate or None""" + version = None + if link.egg_fragment: + egg_info = link.egg_fragment + ext = link.ext + else: + egg_info, ext = link.splitext() + if not ext: + self._log_skipped_link(link, 'not a file') + return + if ext not in SUPPORTED_EXTENSIONS: + self._log_skipped_link( + link, 'unsupported archive format: %s' % ext, + ) + return + if "binary" not in search.formats and ext == wheel_ext: + self._log_skipped_link( + link, 'No binaries permitted for %s' % search.supplied, + ) + return + if "macosx10" in link.path and ext == '.zip': + self._log_skipped_link(link, 'macosx10 one') + return + if ext == wheel_ext: + try: + wheel = Wheel(link.filename) + except InvalidWheelFilename: + self._log_skipped_link(link, 'invalid wheel filename') + return + if canonicalize_name(wheel.name) != search.canonical: + self._log_skipped_link( + link, 'wrong project name (not %s)' % search.supplied) + return + + if not wheel.supported(self.valid_tags): + self._log_skipped_link( + link, 'it is not compatible with this Python') + return + + version = wheel.version + + # This should be up by the search.ok_binary check, but see issue 2700. + if "source" not in search.formats and ext != wheel_ext: + self._log_skipped_link( + link, 'No sources permitted for %s' % search.supplied, + ) + return + + if not version: + version = egg_info_matches(egg_info, search.supplied, link) + if version is None: + self._log_skipped_link( + link, 'Missing project version for %s' % search.supplied) + return + + match = self._py_version_re.search(version) + if match: + version = version[:match.start()] + py_version = match.group(1) + if py_version != sys.version[:3]: + self._log_skipped_link( + link, 'Python version is incorrect') + return + try: + support_this_python = check_requires_python(link.requires_python) + except specifiers.InvalidSpecifier: + logger.debug("Package %s has an invalid Requires-Python entry: %s", + link.filename, link.requires_python) + support_this_python = True + + if not support_this_python: + logger.debug("The package %s is incompatible with the python" + "version in use. Acceptable python versions are:%s", + link, link.requires_python) + return + logger.debug('Found link %s, version: %s', link, version) + + return InstallationCandidate(search.supplied, version, link) + + def _get_page(self, link): + return _get_html_page(link, session=self.session) + + +def egg_info_matches( + egg_info, search_name, link, + _egg_info_re=re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.I)): + """Pull the version part out of a string. + + :param egg_info: The string to parse. E.g. foo-2.1 + :param search_name: The name of the package this belongs to. None to + infer the name. Note that this cannot unambiguously parse strings + like foo-2-2 which might be foo, 2-2 or foo-2, 2. + :param link: The link the string came from, for logging on failure. + """ + match = _egg_info_re.search(egg_info) + if not match: + logger.debug('Could not parse version from link: %s', link) + return None + if search_name is None: + full_match = match.group(0) + return full_match.split('-', 1)[-1] + name = match.group(0).lower() + # To match the "safe" name that pkg_resources creates: + name = name.replace('_', '-') + # project name and version must be separated by a dash + look_for = search_name.lower() + "-" + if name.startswith(look_for): + return match.group(0)[len(look_for):] + else: + return None + + +def _determine_base_url(document, page_url): + """Determine the HTML document's base URL. + + This looks for a ``<base>`` tag in the HTML document. If present, its href + attribute denotes the base URL of anchor tags in the document. If there is + no such tag (or if it does not have a valid href attribute), the HTML + file's URL is used as the base URL. + + :param document: An HTML document representation. The current + implementation expects the result of ``html5lib.parse()``. + :param page_url: The URL of the HTML document. + """ + for base in document.findall(".//base"): + href = base.get("href") + if href is not None: + return href + return page_url + + +def _get_encoding_from_headers(headers): + """Determine if we have any encoding information in our headers. + """ + if headers and "Content-Type" in headers: + content_type, params = cgi.parse_header(headers["Content-Type"]) + if "charset" in params: + return params['charset'] + return None + + +_CLEAN_LINK_RE = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) + + +def _clean_link(url): + """Makes sure a link is fully encoded. That is, if a ' ' shows up in + the link, it will be rewritten to %20 (while not over-quoting + % or other characters).""" + return _CLEAN_LINK_RE.sub(lambda match: '%%%2x' % ord(match.group(0)), url) + + +class HTMLPage(object): + """Represents one page, along with its URL""" + + def __init__(self, content, url, headers=None): + self.content = content + self.url = url + self.headers = headers + + def __str__(self): + return self.url + + def iter_links(self): + """Yields all links in the page""" + document = html5lib.parse( + self.content, + transport_encoding=_get_encoding_from_headers(self.headers), + namespaceHTMLElements=False, + ) + base_url = _determine_base_url(document, self.url) + for anchor in document.findall(".//a"): + if anchor.get("href"): + href = anchor.get("href") + url = _clean_link(urllib_parse.urljoin(base_url, href)) + pyrequire = anchor.get('data-requires-python') + pyrequire = unescape(pyrequire) if pyrequire else None + yield Link(url, self.url, requires_python=pyrequire) + + +Search = namedtuple('Search', 'supplied canonical formats') +"""Capture key aspects of a search. + +:attribute supplied: The user supplied package. +:attribute canonical: The canonical package name. +:attribute formats: The formats allowed for this package. Should be a set + with 'binary' or 'source' or both in it. +""" diff --git a/venv/Lib/site-packages/pip/_internal/locations.py b/venv/Lib/site-packages/pip/_internal/locations.py new file mode 100644 index 0000000..183aaa3 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/locations.py @@ -0,0 +1,194 @@ +"""Locations where we look for configs, install stuff, etc""" +from __future__ import absolute_import + +import os +import os.path +import platform +import site +import sys +import sysconfig +from distutils import sysconfig as distutils_sysconfig +from distutils.command.install import SCHEME_KEYS # type: ignore + +from pip._internal.utils import appdirs +from pip._internal.utils.compat import WINDOWS, expanduser + +# Application Directories +USER_CACHE_DIR = appdirs.user_cache_dir("pip") + + +DELETE_MARKER_MESSAGE = '''\ +This file is placed here by pip to indicate the source was put +here by pip. + +Once this package is successfully installed this source code will be +deleted (unless you remove this file). +''' +PIP_DELETE_MARKER_FILENAME = 'pip-delete-this-directory.txt' + + +def write_delete_marker_file(directory): + """ + Write the pip delete marker file into this directory. + """ + filepath = os.path.join(directory, PIP_DELETE_MARKER_FILENAME) + with open(filepath, 'w') as marker_fp: + marker_fp.write(DELETE_MARKER_MESSAGE) + + +def running_under_virtualenv(): + """ + Return True if we're running inside a virtualenv, False otherwise. + + """ + if hasattr(sys, 'real_prefix'): + return True + elif sys.prefix != getattr(sys, "base_prefix", sys.prefix): + return True + + return False + + +def virtualenv_no_global(): + """ + Return True if in a venv and no system site packages. + """ + # this mirrors the logic in virtualenv.py for locating the + # no-global-site-packages.txt file + site_mod_dir = os.path.dirname(os.path.abspath(site.__file__)) + no_global_file = os.path.join(site_mod_dir, 'no-global-site-packages.txt') + if running_under_virtualenv() and os.path.isfile(no_global_file): + return True + + +if running_under_virtualenv(): + src_prefix = os.path.join(sys.prefix, 'src') +else: + # FIXME: keep src in cwd for now (it is not a temporary folder) + try: + src_prefix = os.path.join(os.getcwd(), 'src') + except OSError: + # In case the current working directory has been renamed or deleted + sys.exit( + "The folder you are executing pip from can no longer be found." + ) + +# under macOS + virtualenv sys.prefix is not properly resolved +# it is something like /path/to/python/bin/.. +# Note: using realpath due to tmp dirs on OSX being symlinks +src_prefix = os.path.abspath(src_prefix) + +# FIXME doesn't account for venv linked to global site-packages + +site_packages = sysconfig.get_path("purelib") +# This is because of a bug in PyPy's sysconfig module, see +# https://bitbucket.org/pypy/pypy/issues/2506/sysconfig-returns-incorrect-paths +# for more information. +if platform.python_implementation().lower() == "pypy": + site_packages = distutils_sysconfig.get_python_lib() +try: + # Use getusersitepackages if this is present, as it ensures that the + # value is initialised properly. + user_site = site.getusersitepackages() +except AttributeError: + user_site = site.USER_SITE +user_dir = expanduser('~') +if WINDOWS: + bin_py = os.path.join(sys.prefix, 'Scripts') + bin_user = os.path.join(user_site, 'Scripts') + # buildout uses 'bin' on Windows too? + if not os.path.exists(bin_py): + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') + + config_basename = 'pip.ini' + + legacy_storage_dir = os.path.join(user_dir, 'pip') + legacy_config_file = os.path.join( + legacy_storage_dir, + config_basename, + ) +else: + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') + + config_basename = 'pip.conf' + + legacy_storage_dir = os.path.join(user_dir, '.pip') + legacy_config_file = os.path.join( + legacy_storage_dir, + config_basename, + ) + # Forcing to use /usr/local/bin for standard macOS framework installs + # Also log to ~/Library/Logs/ for use with the Console.app log viewer + if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': + bin_py = '/usr/local/bin' + +site_config_files = [ + os.path.join(path, config_basename) + for path in appdirs.site_config_dirs('pip') +] + +venv_config_file = os.path.join(sys.prefix, config_basename) +new_config_file = os.path.join(appdirs.user_config_dir("pip"), config_basename) + + +def distutils_scheme(dist_name, user=False, home=None, root=None, + isolated=False, prefix=None): + """ + Return a distutils install scheme + """ + from distutils.dist import Distribution + + scheme = {} + + if isolated: + extra_dist_args = {"script_args": ["--no-user-cfg"]} + else: + extra_dist_args = {} + dist_args = {'name': dist_name} + dist_args.update(extra_dist_args) + + d = Distribution(dist_args) + d.parse_config_files() + i = d.get_command_obj('install', create=True) + # NOTE: setting user or home has the side-effect of creating the home dir + # or user base for installations during finalize_options() + # ideally, we'd prefer a scheme class that has no side-effects. + assert not (user and prefix), "user={} prefix={}".format(user, prefix) + i.user = user or i.user + if user: + i.prefix = "" + i.prefix = prefix or i.prefix + i.home = home or i.home + i.root = root or i.root + i.finalize_options() + for key in SCHEME_KEYS: + scheme[key] = getattr(i, 'install_' + key) + + # install_lib specified in setup.cfg should install *everything* + # into there (i.e. it takes precedence over both purelib and + # platlib). Note, i.install_lib is *always* set after + # finalize_options(); we only want to override here if the user + # has explicitly requested it hence going back to the config + if 'install_lib' in d.get_option_dict('install'): + scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib)) + + if running_under_virtualenv(): + scheme['headers'] = os.path.join( + sys.prefix, + 'include', + 'site', + 'python' + sys.version[:3], + dist_name, + ) + + if root is not None: + path_no_drive = os.path.splitdrive( + os.path.abspath(scheme["headers"]))[1] + scheme["headers"] = os.path.join( + root, + path_no_drive[1:], + ) + + return scheme diff --git a/venv/Lib/site-packages/pip/_internal/models/__init__.py b/venv/Lib/site-packages/pip/_internal/models/__init__.py new file mode 100644 index 0000000..7855226 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/models/__init__.py @@ -0,0 +1,2 @@ +"""A package that contains models that represent entities. +""" diff --git a/venv/Lib/site-packages/pip/_internal/models/candidate.py b/venv/Lib/site-packages/pip/_internal/models/candidate.py new file mode 100644 index 0000000..c736de6 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/models/candidate.py @@ -0,0 +1,23 @@ +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.utils.models import KeyBasedCompareMixin + + +class InstallationCandidate(KeyBasedCompareMixin): + """Represents a potential "candidate" for installation. + """ + + def __init__(self, project, version, location): + self.project = project + self.version = parse_version(version) + self.location = location + + super(InstallationCandidate, self).__init__( + key=(self.project, self.version, self.location), + defining_class=InstallationCandidate + ) + + def __repr__(self): + return "<InstallationCandidate({!r}, {!r}, {!r})>".format( + self.project, self.version, self.location, + ) diff --git a/venv/Lib/site-packages/pip/_internal/models/format_control.py b/venv/Lib/site-packages/pip/_internal/models/format_control.py new file mode 100644 index 0000000..2748856 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/models/format_control.py @@ -0,0 +1,62 @@ +from pip._vendor.packaging.utils import canonicalize_name + + +class FormatControl(object): + """A helper class for controlling formats from which packages are installed. + If a field is falsy, it isn't set. If it is {':all:'}, it should match all + packages except those listed in the other field. Only one field can be set + to {':all:'} at a time. The rest of the time exact package name matches + are listed, with any given package only showing up in one field at a time. + """ + def __init__(self, no_binary=None, only_binary=None): + self.no_binary = set() if no_binary is None else no_binary + self.only_binary = set() if only_binary is None else only_binary + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return "{}({}, {})".format( + self.__class__.__name__, + self.no_binary, + self.only_binary + ) + + @staticmethod + def handle_mutual_excludes(value, target, other): + new = value.split(',') + while ':all:' in new: + other.clear() + target.clear() + target.add(':all:') + del new[:new.index(':all:') + 1] + # Without a none, we want to discard everything as :all: covers it + if ':none:' not in new: + return + for name in new: + if name == ':none:': + target.clear() + continue + name = canonicalize_name(name) + other.discard(name) + target.add(name) + + def get_allowed_formats(self, canonical_name): + result = {"binary", "source"} + if canonical_name in self.only_binary: + result.discard('source') + elif canonical_name in self.no_binary: + result.discard('binary') + elif ':all:' in self.only_binary: + result.discard('source') + elif ':all:' in self.no_binary: + result.discard('binary') + return frozenset(result) + + def disallow_binaries(self): + self.handle_mutual_excludes( + ':all:', self.no_binary, self.only_binary, + ) diff --git a/venv/Lib/site-packages/pip/_internal/models/index.py b/venv/Lib/site-packages/pip/_internal/models/index.py new file mode 100644 index 0000000..870a315 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/models/index.py @@ -0,0 +1,29 @@ +from pip._vendor.six.moves.urllib import parse as urllib_parse + + +class PackageIndex(object): + """Represents a Package Index and provides easier access to endpoints + """ + + def __init__(self, url, file_storage_domain): + super(PackageIndex, self).__init__() + self.url = url + self.netloc = urllib_parse.urlsplit(url).netloc + self.simple_url = self._url_for_path('simple') + self.pypi_url = self._url_for_path('pypi') + + # This is part of a temporary hack used to block installs of PyPI + # packages which depend on external urls only necessary until PyPI can + # block such packages themselves + self.file_storage_domain = file_storage_domain + + def _url_for_path(self, path): + return urllib_parse.urljoin(self.url, path) + + +PyPI = PackageIndex( + 'https://pypi.org/', file_storage_domain='files.pythonhosted.org' +) +TestPyPI = PackageIndex( + 'https://test.pypi.org/', file_storage_domain='test-files.pythonhosted.org' +) diff --git a/venv/Lib/site-packages/pip/_internal/models/link.py b/venv/Lib/site-packages/pip/_internal/models/link.py new file mode 100644 index 0000000..5decb7c --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/models/link.py @@ -0,0 +1,141 @@ +import posixpath +import re + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.download import path_to_url +from pip._internal.utils.misc import splitext +from pip._internal.utils.models import KeyBasedCompareMixin +from pip._internal.wheel import wheel_ext + + +class Link(KeyBasedCompareMixin): + """Represents a parsed link from a Package Index's simple URL + """ + + def __init__(self, url, comes_from=None, requires_python=None): + """ + url: + url of the resource pointed to (href of the link) + comes_from: + instance of HTMLPage where the link was found, or string. + requires_python: + String containing the `Requires-Python` metadata field, specified + in PEP 345. This may be specified by a data-requires-python + attribute in the HTML link tag, as described in PEP 503. + """ + + # url can be a UNC windows share + if url.startswith('\\\\'): + url = path_to_url(url) + + self.url = url + self.comes_from = comes_from + self.requires_python = requires_python if requires_python else None + + super(Link, self).__init__( + key=(self.url), + defining_class=Link + ) + + def __str__(self): + if self.requires_python: + rp = ' (requires-python:%s)' % self.requires_python + else: + rp = '' + if self.comes_from: + return '%s (from %s)%s' % (self.url, self.comes_from, rp) + else: + return str(self.url) + + def __repr__(self): + return '<Link %s>' % self + + @property + def filename(self): + _, netloc, path, _, _ = urllib_parse.urlsplit(self.url) + name = posixpath.basename(path.rstrip('/')) or netloc + name = urllib_parse.unquote(name) + assert name, ('URL %r produced no filename' % self.url) + return name + + @property + def scheme(self): + return urllib_parse.urlsplit(self.url)[0] + + @property + def netloc(self): + return urllib_parse.urlsplit(self.url)[1] + + @property + def path(self): + return urllib_parse.unquote(urllib_parse.urlsplit(self.url)[2]) + + def splitext(self): + return splitext(posixpath.basename(self.path.rstrip('/'))) + + @property + def ext(self): + return self.splitext()[1] + + @property + def url_without_fragment(self): + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(self.url) + return urllib_parse.urlunsplit((scheme, netloc, path, query, None)) + + _egg_fragment_re = re.compile(r'[#&]egg=([^&]*)') + + @property + def egg_fragment(self): + match = self._egg_fragment_re.search(self.url) + if not match: + return None + return match.group(1) + + _subdirectory_fragment_re = re.compile(r'[#&]subdirectory=([^&]*)') + + @property + def subdirectory_fragment(self): + match = self._subdirectory_fragment_re.search(self.url) + if not match: + return None + return match.group(1) + + _hash_re = re.compile( + r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)' + ) + + @property + def hash(self): + match = self._hash_re.search(self.url) + if match: + return match.group(2) + return None + + @property + def hash_name(self): + match = self._hash_re.search(self.url) + if match: + return match.group(1) + return None + + @property + def show_url(self): + return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0]) + + @property + def is_wheel(self): + return self.ext == wheel_ext + + @property + def is_artifact(self): + """ + Determines if this points to an actual artifact (e.g. a tarball) or if + it points to an "abstract" thing like a path or a VCS location. + """ + from pip._internal.vcs import vcs + + if self.scheme in vcs.all_schemes: + return False + + return True diff --git a/venv/Lib/site-packages/pip/_internal/operations/__init__.py b/venv/Lib/site-packages/pip/_internal/operations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pip/_internal/operations/check.py b/venv/Lib/site-packages/pip/_internal/operations/check.py new file mode 100644 index 0000000..799257a --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/operations/check.py @@ -0,0 +1,148 @@ +"""Validation of dependencies of packages +""" + +from collections import namedtuple + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.operations.prepare import make_abstract_dist +from pip._internal.utils.misc import get_installed_distributions +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from pip._internal.req.req_install import InstallRequirement # noqa: F401 + from typing import ( # noqa: F401 + Any, Callable, Dict, Iterator, Optional, Set, Tuple, List + ) + + # Shorthands + PackageSet = Dict[str, 'PackageDetails'] + Missing = Tuple[str, Any] + Conflicting = Tuple[str, str, Any] + + MissingDict = Dict[str, List[Missing]] + ConflictingDict = Dict[str, List[Conflicting]] + CheckResult = Tuple[MissingDict, ConflictingDict] + +PackageDetails = namedtuple('PackageDetails', ['version', 'requires']) + + +def create_package_set_from_installed(**kwargs): + # type: (**Any) -> PackageSet + """Converts a list of distributions into a PackageSet. + """ + # Default to using all packages installed on the system + if kwargs == {}: + kwargs = {"local_only": False, "skip": ()} + + package_set = {} + for dist in get_installed_distributions(**kwargs): + name = canonicalize_name(dist.project_name) + package_set[name] = PackageDetails(dist.version, dist.requires()) + return package_set + + +def check_package_set(package_set, should_ignore=None): + # type: (PackageSet, Optional[Callable[[str], bool]]) -> CheckResult + """Check if a package set is consistent + + If should_ignore is passed, it should be a callable that takes a + package name and returns a boolean. + """ + if should_ignore is None: + def should_ignore(name): + return False + + missing = dict() + conflicting = dict() + + for package_name in package_set: + # Info about dependencies of package_name + missing_deps = set() # type: Set[Missing] + conflicting_deps = set() # type: Set[Conflicting] + + if should_ignore(package_name): + continue + + for req in package_set[package_name].requires: + name = canonicalize_name(req.project_name) # type: str + + # Check if it's missing + if name not in package_set: + missed = True + if req.marker is not None: + missed = req.marker.evaluate() + if missed: + missing_deps.add((name, req)) + continue + + # Check if there's a conflict + version = package_set[name].version # type: str + if not req.specifier.contains(version, prereleases=True): + conflicting_deps.add((name, version, req)) + + if missing_deps: + missing[package_name] = sorted(missing_deps, key=str) + if conflicting_deps: + conflicting[package_name] = sorted(conflicting_deps, key=str) + + return missing, conflicting + + +def check_install_conflicts(to_install): + # type: (List[InstallRequirement]) -> Tuple[PackageSet, CheckResult] + """For checking if the dependency graph would be consistent after \ + installing given requirements + """ + # Start from the current state + package_set = create_package_set_from_installed() + # Install packages + would_be_installed = _simulate_installation_of(to_install, package_set) + + # Only warn about directly-dependent packages; create a whitelist of them + whitelist = _create_whitelist(would_be_installed, package_set) + + return ( + package_set, + check_package_set( + package_set, should_ignore=lambda name: name not in whitelist + ) + ) + + +# NOTE from @pradyunsg +# This required a minor update in dependency link handling logic over at +# operations.prepare.IsSDist.dist() to get it working +def _simulate_installation_of(to_install, package_set): + # type: (List[InstallRequirement], PackageSet) -> Set[str] + """Computes the version of packages after installing to_install. + """ + + # Keep track of packages that were installed + installed = set() + + # Modify it as installing requirement_set would (assuming no errors) + for inst_req in to_install: + dist = make_abstract_dist(inst_req).dist(finder=None) + name = canonicalize_name(dist.key) + package_set[name] = PackageDetails(dist.version, dist.requires()) + + installed.add(name) + + return installed + + +def _create_whitelist(would_be_installed, package_set): + # type: (Set[str], PackageSet) -> Set[str] + packages_affected = set(would_be_installed) + + for package_name in package_set: + if package_name in packages_affected: + continue + + for req in package_set[package_name].requires: + if canonicalize_name(req.name) in packages_affected: + packages_affected.add(package_name) + break + + return packages_affected diff --git a/venv/Lib/site-packages/pip/_internal/operations/freeze.py b/venv/Lib/site-packages/pip/_internal/operations/freeze.py new file mode 100644 index 0000000..beb2feb --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/operations/freeze.py @@ -0,0 +1,264 @@ +from __future__ import absolute_import + +import collections +import logging +import os +import re + +from pip._vendor import pkg_resources, six +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError + +from pip._internal.exceptions import InstallationError +from pip._internal.req.constructors import ( + install_req_from_editable, install_req_from_line, +) +from pip._internal.req.req_file import COMMENT_RE +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.misc import ( + dist_is_editable, get_installed_distributions, make_vcs_requirement_url, +) + +logger = logging.getLogger(__name__) + + +def freeze( + requirement=None, + find_links=None, local_only=None, user_only=None, skip_regex=None, + isolated=False, + wheel_cache=None, + exclude_editable=False, + skip=()): + find_links = find_links or [] + skip_match = None + + if skip_regex: + skip_match = re.compile(skip_regex).search + + dependency_links = [] + + for dist in pkg_resources.working_set: + if dist.has_metadata('dependency_links.txt'): + dependency_links.extend( + dist.get_metadata_lines('dependency_links.txt') + ) + for link in find_links: + if '#egg=' in link: + dependency_links.append(link) + for link in find_links: + yield '-f %s' % link + installations = {} + for dist in get_installed_distributions(local_only=local_only, + skip=(), + user_only=user_only): + try: + req = FrozenRequirement.from_dist( + dist, + dependency_links + ) + except RequirementParseError: + logger.warning( + "Could not parse requirement: %s", + dist.project_name + ) + continue + if exclude_editable and req.editable: + continue + installations[req.name] = req + + if requirement: + # the options that don't get turned into an InstallRequirement + # should only be emitted once, even if the same option is in multiple + # requirements files, so we need to keep track of what has been emitted + # so that we don't emit it again if it's seen again + emitted_options = set() + # keep track of which files a requirement is in so that we can + # give an accurate warning if a requirement appears multiple times. + req_files = collections.defaultdict(list) + for req_file_path in requirement: + with open(req_file_path) as req_file: + for line in req_file: + if (not line.strip() or + line.strip().startswith('#') or + (skip_match and skip_match(line)) or + line.startswith(( + '-r', '--requirement', + '-Z', '--always-unzip', + '-f', '--find-links', + '-i', '--index-url', + '--pre', + '--trusted-host', + '--process-dependency-links', + '--extra-index-url'))): + line = line.rstrip() + if line not in emitted_options: + emitted_options.add(line) + yield line + continue + + if line.startswith('-e') or line.startswith('--editable'): + if line.startswith('-e'): + line = line[2:].strip() + else: + line = line[len('--editable'):].strip().lstrip('=') + line_req = install_req_from_editable( + line, + isolated=isolated, + wheel_cache=wheel_cache, + ) + else: + line_req = install_req_from_line( + COMMENT_RE.sub('', line).strip(), + isolated=isolated, + wheel_cache=wheel_cache, + ) + + if not line_req.name: + logger.info( + "Skipping line in requirement file [%s] because " + "it's not clear what it would install: %s", + req_file_path, line.strip(), + ) + logger.info( + " (add #egg=PackageName to the URL to avoid" + " this warning)" + ) + elif line_req.name not in installations: + # either it's not installed, or it is installed + # but has been processed already + if not req_files[line_req.name]: + logger.warning( + "Requirement file [%s] contains %s, but that " + "package is not installed", + req_file_path, + COMMENT_RE.sub('', line).strip(), + ) + else: + req_files[line_req.name].append(req_file_path) + else: + yield str(installations[line_req.name]).rstrip() + del installations[line_req.name] + req_files[line_req.name].append(req_file_path) + + # Warn about requirements that were included multiple times (in a + # single requirements file or in different requirements files). + for name, files in six.iteritems(req_files): + if len(files) > 1: + logger.warning("Requirement %s included multiple times [%s]", + name, ', '.join(sorted(set(files)))) + + yield( + '## The following requirements were added by ' + 'pip freeze:' + ) + for installation in sorted( + installations.values(), key=lambda x: x.name.lower()): + if canonicalize_name(installation.name) not in skip: + yield str(installation).rstrip() + + +class FrozenRequirement(object): + def __init__(self, name, req, editable, comments=()): + self.name = name + self.req = req + self.editable = editable + self.comments = comments + + _rev_re = re.compile(r'-r(\d+)$') + _date_re = re.compile(r'-(20\d\d\d\d\d\d)$') + + @classmethod + def _init_args_from_dist(cls, dist, dependency_links): + """ + Compute and return arguments (req, editable, comments) to pass to + FrozenRequirement.__init__(). + + This method is for use in FrozenRequirement.from_dist(). + """ + location = os.path.normcase(os.path.abspath(dist.location)) + comments = [] + from pip._internal.vcs import vcs, get_src_requirement + if dist_is_editable(dist) and vcs.get_backend_name(location): + editable = True + try: + req = get_src_requirement(dist, location) + except InstallationError as exc: + logger.warning( + "Error when trying to get requirement for VCS system %s, " + "falling back to uneditable format", exc + ) + req = None + if req is None: + logger.warning( + 'Could not determine repository location of %s', location + ) + comments.append( + '## !! Could not determine repository location' + ) + req = dist.as_requirement() + editable = False + else: + editable = False + req = dist.as_requirement() + specs = req.specs + assert len(specs) == 1 and specs[0][0] in ["==", "==="], \ + 'Expected 1 spec with == or ===; specs = %r; dist = %r' % \ + (specs, dist) + version = specs[0][1] + ver_match = cls._rev_re.search(version) + date_match = cls._date_re.search(version) + if ver_match or date_match: + svn_backend = vcs.get_backend('svn') + if svn_backend: + svn_location = svn_backend().get_location( + dist, + dependency_links, + ) + if not svn_location: + logger.warning( + 'Warning: cannot find svn location for %s', req, + ) + comments.append( + '## FIXME: could not find svn URL in dependency_links ' + 'for this package:' + ) + else: + deprecated( + "SVN editable detection based on dependency links " + "will be dropped in the future.", + replacement=None, + gone_in="18.2", + issue=4187, + ) + comments.append( + '# Installing as editable to satisfy requirement %s:' % + req + ) + if ver_match: + rev = ver_match.group(1) + else: + rev = '{%s}' % date_match.group(1) + editable = True + egg_name = cls.egg_name(dist) + req = make_vcs_requirement_url(svn_location, rev, egg_name) + + return (req, editable, comments) + + @classmethod + def from_dist(cls, dist, dependency_links): + args = cls._init_args_from_dist(dist, dependency_links) + return cls(dist.project_name, *args) + + @staticmethod + def egg_name(dist): + name = dist.egg_name() + match = re.search(r'-py\d\.\d$', name) + if match: + name = name[:match.start()] + return name + + def __str__(self): + req = self.req + if self.editable: + req = '-e %s' % req + return '\n'.join(list(self.comments) + [str(req)]) + '\n' diff --git a/venv/Lib/site-packages/pip/_internal/operations/prepare.py b/venv/Lib/site-packages/pip/_internal/operations/prepare.py new file mode 100644 index 0000000..104bea3 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/operations/prepare.py @@ -0,0 +1,355 @@ +"""Prepares a distribution for installation +""" + +import logging +import os + +from pip._vendor import pkg_resources, requests + +from pip._internal.build_env import BuildEnvironment +from pip._internal.download import ( + is_dir_url, is_file_url, is_vcs_url, unpack_url, url_to_path, +) +from pip._internal.exceptions import ( + DirectoryUrlHashUnsupported, HashUnpinned, InstallationError, + PreviousBuildDirError, VcsHashUnsupported, +) +from pip._internal.utils.compat import expanduser +from pip._internal.utils.hashes import MissingHashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import display_path, normalize_path +from pip._internal.vcs import vcs + +logger = logging.getLogger(__name__) + + +def make_abstract_dist(req): + """Factory to make an abstract dist object. + + Preconditions: Either an editable req with a source_dir, or satisfied_by or + a wheel link, or a non-editable req with a source_dir. + + :return: A concrete DistAbstraction. + """ + if req.editable: + return IsSDist(req) + elif req.link and req.link.is_wheel: + return IsWheel(req) + else: + return IsSDist(req) + + +class DistAbstraction(object): + """Abstracts out the wheel vs non-wheel Resolver.resolve() logic. + + The requirements for anything installable are as follows: + - we must be able to determine the requirement name + (or we can't correctly handle the non-upgrade case). + - we must be able to generate a list of run-time dependencies + without installing any additional packages (or we would + have to either burn time by doing temporary isolated installs + or alternatively violate pips 'don't start installing unless + all requirements are available' rule - neither of which are + desirable). + - for packages with setup requirements, we must also be able + to determine their requirements without installing additional + packages (for the same reason as run-time dependencies) + - we must be able to create a Distribution object exposing the + above metadata. + """ + + def __init__(self, req): + self.req = req + + def dist(self, finder): + """Return a setuptools Dist object.""" + raise NotImplementedError(self.dist) + + def prep_for_dist(self, finder, build_isolation): + """Ensure that we can get a Dist for this requirement.""" + raise NotImplementedError(self.dist) + + +class IsWheel(DistAbstraction): + + def dist(self, finder): + return list(pkg_resources.find_distributions( + self.req.source_dir))[0] + + def prep_for_dist(self, finder, build_isolation): + # FIXME:https://github.com/pypa/pip/issues/1112 + pass + + +class IsSDist(DistAbstraction): + + def dist(self, finder): + dist = self.req.get_dist() + # FIXME: shouldn't be globally added. + if finder and dist.has_metadata('dependency_links.txt'): + finder.add_dependency_links( + dist.get_metadata_lines('dependency_links.txt') + ) + return dist + + def prep_for_dist(self, finder, build_isolation): + # Prepare for building. We need to: + # 1. Load pyproject.toml (if it exists) + # 2. Set up the build environment + + self.req.load_pyproject_toml() + should_isolate = self.req.use_pep517 and build_isolation + + if should_isolate: + # Isolate in a BuildEnvironment and install the build-time + # requirements. + self.req.build_env = BuildEnvironment() + self.req.build_env.install_requirements( + finder, self.req.pyproject_requires, + "Installing build dependencies" + ) + missing = [] + if self.req.requirements_to_check: + check = self.req.requirements_to_check + missing = self.req.build_env.missing_requirements(check) + if missing: + logger.warning( + "Missing build requirements in pyproject.toml for %s.", + self.req, + ) + logger.warning( + "The project does not specify a build backend, and pip " + "cannot fall back to setuptools without %s.", + " and ".join(map(repr, sorted(missing))) + ) + + self.req.run_egg_info() + self.req.assert_source_matches_version() + + +class Installed(DistAbstraction): + + def dist(self, finder): + return self.req.satisfied_by + + def prep_for_dist(self, finder, build_isolation): + pass + + +class RequirementPreparer(object): + """Prepares a Requirement + """ + + def __init__(self, build_dir, download_dir, src_dir, wheel_download_dir, + progress_bar, build_isolation, req_tracker): + super(RequirementPreparer, self).__init__() + + self.src_dir = src_dir + self.build_dir = build_dir + self.req_tracker = req_tracker + + # Where still packed archives should be written to. If None, they are + # not saved, and are deleted immediately after unpacking. + self.download_dir = download_dir + + # Where still-packed .whl files should be written to. If None, they are + # written to the download_dir parameter. Separate to download_dir to + # permit only keeping wheel archives for pip wheel. + if wheel_download_dir: + wheel_download_dir = normalize_path(wheel_download_dir) + self.wheel_download_dir = wheel_download_dir + + # NOTE + # download_dir and wheel_download_dir overlap semantically and may + # be combined if we're willing to have non-wheel archives present in + # the wheelhouse output by 'pip wheel'. + + self.progress_bar = progress_bar + + # Is build isolation allowed? + self.build_isolation = build_isolation + + @property + def _download_should_save(self): + # TODO: Modify to reduce indentation needed + if self.download_dir: + self.download_dir = expanduser(self.download_dir) + if os.path.exists(self.download_dir): + return True + else: + logger.critical('Could not find download directory') + raise InstallationError( + "Could not find or access download directory '%s'" + % display_path(self.download_dir)) + return False + + def prepare_linked_requirement(self, req, session, finder, + upgrade_allowed, require_hashes): + """Prepare a requirement that would be obtained from req.link + """ + # TODO: Breakup into smaller functions + if req.link and req.link.scheme == 'file': + path = url_to_path(req.link.url) + logger.info('Processing %s', display_path(path)) + else: + logger.info('Collecting %s', req) + + with indent_log(): + # @@ if filesystem packages are not marked + # editable in a req, a non deterministic error + # occurs when the script attempts to unpack the + # build directory + req.ensure_has_source_dir(self.build_dir) + # If a checkout exists, it's unwise to keep going. version + # inconsistencies are logged later, but do not fail the + # installation. + # FIXME: this won't upgrade when there's an existing + # package unpacked in `req.source_dir` + # package unpacked in `req.source_dir` + if os.path.exists(os.path.join(req.source_dir, 'setup.py')): + raise PreviousBuildDirError( + "pip can't proceed with requirements '%s' due to a" + " pre-existing build directory (%s). This is " + "likely due to a previous installation that failed" + ". pip is being responsible and not assuming it " + "can delete this. Please delete it and try again." + % (req, req.source_dir) + ) + req.populate_link(finder, upgrade_allowed, require_hashes) + + # We can't hit this spot and have populate_link return None. + # req.satisfied_by is None here (because we're + # guarded) and upgrade has no impact except when satisfied_by + # is not None. + # Then inside find_requirement existing_applicable -> False + # If no new versions are found, DistributionNotFound is raised, + # otherwise a result is guaranteed. + assert req.link + link = req.link + + # Now that we have the real link, we can tell what kind of + # requirements we have and raise some more informative errors + # than otherwise. (For example, we can raise VcsHashUnsupported + # for a VCS URL rather than HashMissing.) + if require_hashes: + # We could check these first 2 conditions inside + # unpack_url and save repetition of conditions, but then + # we would report less-useful error messages for + # unhashable requirements, complaining that there's no + # hash provided. + if is_vcs_url(link): + raise VcsHashUnsupported() + elif is_file_url(link) and is_dir_url(link): + raise DirectoryUrlHashUnsupported() + if not req.original_link and not req.is_pinned: + # Unpinned packages are asking for trouble when a new + # version is uploaded. This isn't a security check, but + # it saves users a surprising hash mismatch in the + # future. + # + # file:/// URLs aren't pinnable, so don't complain + # about them not being pinned. + raise HashUnpinned() + + hashes = req.hashes(trust_internet=not require_hashes) + if require_hashes and not hashes: + # Known-good hashes are missing for this requirement, so + # shim it with a facade object that will provoke hash + # computation and then raise a HashMissing exception + # showing the user what the hash should be. + hashes = MissingHashes() + + try: + download_dir = self.download_dir + # We always delete unpacked sdists after pip ran. + autodelete_unpacked = True + if req.link.is_wheel and self.wheel_download_dir: + # when doing 'pip wheel` we download wheels to a + # dedicated dir. + download_dir = self.wheel_download_dir + if req.link.is_wheel: + if download_dir: + # When downloading, we only unpack wheels to get + # metadata. + autodelete_unpacked = True + else: + # When installing a wheel, we use the unpacked + # wheel. + autodelete_unpacked = False + unpack_url( + req.link, req.source_dir, + download_dir, autodelete_unpacked, + session=session, hashes=hashes, + progress_bar=self.progress_bar + ) + except requests.HTTPError as exc: + logger.critical( + 'Could not install requirement %s because of error %s', + req, + exc, + ) + raise InstallationError( + 'Could not install requirement %s because of HTTP ' + 'error %s for URL %s' % + (req, exc, req.link) + ) + abstract_dist = make_abstract_dist(req) + with self.req_tracker.track(req): + abstract_dist.prep_for_dist(finder, self.build_isolation) + if self._download_should_save: + # Make a .zip of the source_dir we already created. + if req.link.scheme in vcs.all_schemes: + req.archive(self.download_dir) + return abstract_dist + + def prepare_editable_requirement(self, req, require_hashes, use_user_site, + finder): + """Prepare an editable requirement + """ + assert req.editable, "cannot prepare a non-editable req as editable" + + logger.info('Obtaining %s', req) + + with indent_log(): + if require_hashes: + raise InstallationError( + 'The editable requirement %s cannot be installed when ' + 'requiring hashes, because there is no single file to ' + 'hash.' % req + ) + req.ensure_has_source_dir(self.src_dir) + req.update_editable(not self._download_should_save) + + abstract_dist = make_abstract_dist(req) + with self.req_tracker.track(req): + abstract_dist.prep_for_dist(finder, self.build_isolation) + + if self._download_should_save: + req.archive(self.download_dir) + req.check_if_exists(use_user_site) + + return abstract_dist + + def prepare_installed_requirement(self, req, require_hashes, skip_reason): + """Prepare an already-installed requirement + """ + assert req.satisfied_by, "req should have been satisfied but isn't" + assert skip_reason is not None, ( + "did not get skip reason skipped but req.satisfied_by " + "is set to %r" % (req.satisfied_by,) + ) + logger.info( + 'Requirement %s: %s (%s)', + skip_reason, req, req.satisfied_by.version + ) + with indent_log(): + if require_hashes: + logger.debug( + 'Since it is already installed, we are trusting this ' + 'package without checking its hash. To ensure a ' + 'completely repeatable environment, install into an ' + 'empty virtualenv.' + ) + abstract_dist = Installed(req) + + return abstract_dist diff --git a/venv/Lib/site-packages/pip/_internal/pep425tags.py b/venv/Lib/site-packages/pip/_internal/pep425tags.py new file mode 100644 index 0000000..ab1a029 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/pep425tags.py @@ -0,0 +1,317 @@ +"""Generate and work with PEP 425 Compatibility Tags.""" +from __future__ import absolute_import + +import distutils.util +import logging +import platform +import re +import sys +import sysconfig +import warnings +from collections import OrderedDict + +import pip._internal.utils.glibc +from pip._internal.utils.compat import get_extension_suffixes + +logger = logging.getLogger(__name__) + +_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') + + +def get_config_var(var): + try: + return sysconfig.get_config_var(var) + except IOError as e: # Issue #1074 + warnings.warn("{}".format(e), RuntimeWarning) + return None + + +def get_abbr_impl(): + """Return abbreviated implementation name.""" + if hasattr(sys, 'pypy_version_info'): + pyimpl = 'pp' + elif sys.platform.startswith('java'): + pyimpl = 'jy' + elif sys.platform == 'cli': + pyimpl = 'ip' + else: + pyimpl = 'cp' + return pyimpl + + +def get_impl_ver(): + """Return implementation version.""" + impl_ver = get_config_var("py_version_nodot") + if not impl_ver or get_abbr_impl() == 'pp': + impl_ver = ''.join(map(str, get_impl_version_info())) + return impl_ver + + +def get_impl_version_info(): + """Return sys.version_info-like tuple for use in decrementing the minor + version.""" + if get_abbr_impl() == 'pp': + # as per https://github.com/pypa/pip/issues/2882 + return (sys.version_info[0], sys.pypy_version_info.major, + sys.pypy_version_info.minor) + else: + return sys.version_info[0], sys.version_info[1] + + +def get_impl_tag(): + """ + Returns the Tag for this specific implementation. + """ + return "{}{}".format(get_abbr_impl(), get_impl_ver()) + + +def get_flag(var, fallback, expected=True, warn=True): + """Use a fallback method for determining SOABI flags if the needed config + var is unset or unavailable.""" + val = get_config_var(var) + if val is None: + if warn: + logger.debug("Config variable '%s' is unset, Python ABI tag may " + "be incorrect", var) + return fallback() + return val == expected + + +def get_abi_tag(): + """Return the ABI tag based on SOABI (if available) or emulate SOABI + (CPython 2, PyPy).""" + soabi = get_config_var('SOABI') + impl = get_abbr_impl() + if not soabi and impl in {'cp', 'pp'} and hasattr(sys, 'maxunicode'): + d = '' + m = '' + u = '' + if get_flag('Py_DEBUG', + lambda: hasattr(sys, 'gettotalrefcount'), + warn=(impl == 'cp')): + d = 'd' + if get_flag('WITH_PYMALLOC', + lambda: impl == 'cp', + warn=(impl == 'cp')): + m = 'm' + if get_flag('Py_UNICODE_SIZE', + lambda: sys.maxunicode == 0x10ffff, + expected=4, + warn=(impl == 'cp' and + sys.version_info < (3, 3))) \ + and sys.version_info < (3, 3): + u = 'u' + abi = '%s%s%s%s%s' % (impl, get_impl_ver(), d, m, u) + elif soabi and soabi.startswith('cpython-'): + abi = 'cp' + soabi.split('-')[1] + elif soabi: + abi = soabi.replace('.', '_').replace('-', '_') + else: + abi = None + return abi + + +def _is_running_32bit(): + return sys.maxsize == 2147483647 + + +def get_platform(): + """Return our platform name 'win32', 'linux_x86_64'""" + if sys.platform == 'darwin': + # distutils.util.get_platform() returns the release based on the value + # of MACOSX_DEPLOYMENT_TARGET on which Python was built, which may + # be significantly older than the user's current machine. + release, _, machine = platform.mac_ver() + split_ver = release.split('.') + + if machine == "x86_64" and _is_running_32bit(): + machine = "i386" + elif machine == "ppc64" and _is_running_32bit(): + machine = "ppc" + + return 'macosx_{}_{}_{}'.format(split_ver[0], split_ver[1], machine) + + # XXX remove distutils dependency + result = distutils.util.get_platform().replace('.', '_').replace('-', '_') + if result == "linux_x86_64" and _is_running_32bit(): + # 32 bit Python program (running on a 64 bit Linux): pip should only + # install and run 32 bit compiled extensions in that case. + result = "linux_i686" + + return result + + +def is_manylinux1_compatible(): + # Only Linux, and only x86-64 / i686 + if get_platform() not in {"linux_x86_64", "linux_i686"}: + return False + + # Check for presence of _manylinux module + try: + import _manylinux + return bool(_manylinux.manylinux1_compatible) + except (ImportError, AttributeError): + # Fall through to heuristic check below + pass + + # Check glibc version. CentOS 5 uses glibc 2.5. + return pip._internal.utils.glibc.have_compatible_glibc(2, 5) + + +def get_darwin_arches(major, minor, machine): + """Return a list of supported arches (including group arches) for + the given major, minor and machine architecture of an macOS machine. + """ + arches = [] + + def _supports_arch(major, minor, arch): + # Looking at the application support for macOS versions in the chart + # provided by https://en.wikipedia.org/wiki/OS_X#Versions it appears + # our timeline looks roughly like: + # + # 10.0 - Introduces ppc support. + # 10.4 - Introduces ppc64, i386, and x86_64 support, however the ppc64 + # and x86_64 support is CLI only, and cannot be used for GUI + # applications. + # 10.5 - Extends ppc64 and x86_64 support to cover GUI applications. + # 10.6 - Drops support for ppc64 + # 10.7 - Drops support for ppc + # + # Given that we do not know if we're installing a CLI or a GUI + # application, we must be conservative and assume it might be a GUI + # application and behave as if ppc64 and x86_64 support did not occur + # until 10.5. + # + # Note: The above information is taken from the "Application support" + # column in the chart not the "Processor support" since I believe + # that we care about what instruction sets an application can use + # not which processors the OS supports. + if arch == 'ppc': + return (major, minor) <= (10, 5) + if arch == 'ppc64': + return (major, minor) == (10, 5) + if arch == 'i386': + return (major, minor) >= (10, 4) + if arch == 'x86_64': + return (major, minor) >= (10, 5) + if arch in groups: + for garch in groups[arch]: + if _supports_arch(major, minor, garch): + return True + return False + + groups = OrderedDict([ + ("fat", ("i386", "ppc")), + ("intel", ("x86_64", "i386")), + ("fat64", ("x86_64", "ppc64")), + ("fat32", ("x86_64", "i386", "ppc")), + ]) + + if _supports_arch(major, minor, machine): + arches.append(machine) + + for garch in groups: + if machine in groups[garch] and _supports_arch(major, minor, garch): + arches.append(garch) + + arches.append('universal') + + return arches + + +def get_supported(versions=None, noarch=False, platform=None, + impl=None, abi=None): + """Return a list of supported tags for each version specified in + `versions`. + + :param versions: a list of string versions, of the form ["33", "32"], + or None. The first version will be assumed to support our ABI. + :param platform: specify the exact platform you want valid + tags for, or None. If None, use the local system platform. + :param impl: specify the exact implementation you want valid + tags for, or None. If None, use the local interpreter impl. + :param abi: specify the exact abi you want valid + tags for, or None. If None, use the local interpreter abi. + """ + supported = [] + + # Versions must be given with respect to the preference + if versions is None: + versions = [] + version_info = get_impl_version_info() + major = version_info[:-1] + # Support all previous minor Python versions. + for minor in range(version_info[-1], -1, -1): + versions.append(''.join(map(str, major + (minor,)))) + + impl = impl or get_abbr_impl() + + abis = [] + + abi = abi or get_abi_tag() + if abi: + abis[0:0] = [abi] + + abi3s = set() + for suffix in get_extension_suffixes(): + if suffix.startswith('.abi'): + abi3s.add(suffix.split('.', 2)[1]) + + abis.extend(sorted(list(abi3s))) + + abis.append('none') + + if not noarch: + arch = platform or get_platform() + if arch.startswith('macosx'): + # support macosx-10.6-intel on macosx-10.9-x86_64 + match = _osx_arch_pat.match(arch) + if match: + name, major, minor, actual_arch = match.groups() + tpl = '{}_{}_%i_%s'.format(name, major) + arches = [] + for m in reversed(range(int(minor) + 1)): + for a in get_darwin_arches(int(major), m, actual_arch): + arches.append(tpl % (m, a)) + else: + # arch pattern didn't match (?!) + arches = [arch] + elif platform is None and is_manylinux1_compatible(): + arches = [arch.replace('linux', 'manylinux1'), arch] + else: + arches = [arch] + + # Current version, current API (built specifically for our Python): + for abi in abis: + for arch in arches: + supported.append(('%s%s' % (impl, versions[0]), abi, arch)) + + # abi3 modules compatible with older version of Python + for version in versions[1:]: + # abi3 was introduced in Python 3.2 + if version in {'31', '30'}: + break + for abi in abi3s: # empty set if not Python 3 + for arch in arches: + supported.append(("%s%s" % (impl, version), abi, arch)) + + # Has binaries, does not use the Python API: + for arch in arches: + supported.append(('py%s' % (versions[0][0]), 'none', arch)) + + # No abi / arch, but requires our implementation: + supported.append(('%s%s' % (impl, versions[0]), 'none', 'any')) + # Tagged specifically as being cross-version compatible + # (with just the major version specified) + supported.append(('%s%s' % (impl, versions[0][0]), 'none', 'any')) + + # No abi / arch, generic Python + for i, version in enumerate(versions): + supported.append(('py%s' % (version,), 'none', 'any')) + if i == 0: + supported.append(('py%s' % (version[0]), 'none', 'any')) + + return supported + + +implementation_tag = get_impl_tag() diff --git a/venv/Lib/site-packages/pip/_internal/pyproject.py b/venv/Lib/site-packages/pip/_internal/pyproject.py new file mode 100644 index 0000000..f938a76 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/pyproject.py @@ -0,0 +1,144 @@ +from __future__ import absolute_import + +import io +import os + +from pip._vendor import pytoml, six + +from pip._internal.exceptions import InstallationError + + +def _is_list_of_str(obj): + return ( + isinstance(obj, list) and + all(isinstance(item, six.string_types) for item in obj) + ) + + +def load_pyproject_toml(use_pep517, pyproject_toml, setup_py, req_name): + """Load the pyproject.toml file. + + Parameters: + use_pep517 - Has the user requested PEP 517 processing? None + means the user hasn't explicitly specified. + pyproject_toml - Location of the project's pyproject.toml file + setup_py - Location of the project's setup.py file + req_name - The name of the requirement we're processing (for + error reporting) + + Returns: + None if we should use the legacy code path, otherwise a tuple + ( + requirements from pyproject.toml, + name of PEP 517 backend, + requirements we should check are installed after setting + up the build environment + ) + """ + has_pyproject = os.path.isfile(pyproject_toml) + has_setup = os.path.isfile(setup_py) + + if has_pyproject: + with io.open(pyproject_toml, encoding="utf-8") as f: + pp_toml = pytoml.load(f) + build_system = pp_toml.get("build-system") + else: + build_system = None + + # The following cases must use PEP 517 + # We check for use_pep517 equalling False because that + # means the user explicitly requested --no-use-pep517 + if has_pyproject and not has_setup: + if use_pep517 is False: + raise InstallationError( + "Disabling PEP 517 processing is invalid: " + "project does not have a setup.py" + ) + use_pep517 = True + elif build_system and "build-backend" in build_system: + if use_pep517 is False: + raise InstallationError( + "Disabling PEP 517 processing is invalid: " + "project specifies a build backend of {} " + "in pyproject.toml".format( + build_system["build-backend"] + ) + ) + use_pep517 = True + + # If we haven't worked out whether to use PEP 517 yet, + # and the user hasn't explicitly stated a preference, + # we do so if the project has a pyproject.toml file. + elif use_pep517 is None: + use_pep517 = has_pyproject + + # At this point, we know whether we're going to use PEP 517. + assert use_pep517 is not None + + # If we're using the legacy code path, there is nothing further + # for us to do here. + if not use_pep517: + return None + + if build_system is None: + # Either the user has a pyproject.toml with no build-system + # section, or the user has no pyproject.toml, but has opted in + # explicitly via --use-pep517. + # In the absence of any explicit backend specification, we + # assume the setuptools backend, and require wheel and a version + # of setuptools that supports that backend. + build_system = { + "requires": ["setuptools>=38.2.5", "wheel"], + "build-backend": "setuptools.build_meta", + } + + # If we're using PEP 517, we have build system information (either + # from pyproject.toml, or defaulted by the code above). + # Note that at this point, we do not know if the user has actually + # specified a backend, though. + assert build_system is not None + + # Ensure that the build-system section in pyproject.toml conforms + # to PEP 518. + error_template = ( + "{package} has a pyproject.toml file that does not comply " + "with PEP 518: {reason}" + ) + + # Specifying the build-system table but not the requires key is invalid + if "requires" not in build_system: + raise InstallationError( + error_template.format(package=req_name, reason=( + "it has a 'build-system' table but not " + "'build-system.requires' which is mandatory in the table" + )) + ) + + # Error out if requires is not a list of strings + requires = build_system["requires"] + if not _is_list_of_str(requires): + raise InstallationError(error_template.format( + package=req_name, + reason="'build-system.requires' is not a list of strings.", + )) + + backend = build_system.get("build-backend") + check = [] + if backend is None: + # If the user didn't specify a backend, we assume they want to use + # the setuptools backend. But we can't be sure they have included + # a version of setuptools which supplies the backend, or wheel + # (which is neede by the backend) in their requirements. So we + # make a note to check that those requirements are present once + # we have set up the environment. + # TODO: Review this - it's quite a lot of work to check for a very + # specific case. The problem is, that case is potentially quite + # common - projects that adopted PEP 518 early for the ability to + # specify requirements to execute setup.py, but never considered + # needing to mention the build tools themselves. The original PEP + # 518 code had a similar check (but implemented in a different + # way). + backend = "setuptools.build_meta" + check = ["setuptools>=38.2.5", "wheel"] + + return (requires, backend, check) diff --git a/venv/Lib/site-packages/pip/_internal/req/__init__.py b/venv/Lib/site-packages/pip/_internal/req/__init__.py new file mode 100644 index 0000000..b270498 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/req/__init__.py @@ -0,0 +1,69 @@ +from __future__ import absolute_import + +import logging + +from .req_install import InstallRequirement +from .req_set import RequirementSet +from .req_file import parse_requirements +from pip._internal.utils.logging import indent_log + + +__all__ = [ + "RequirementSet", "InstallRequirement", + "parse_requirements", "install_given_reqs", +] + +logger = logging.getLogger(__name__) + + +def install_given_reqs(to_install, install_options, global_options=(), + *args, **kwargs): + """ + Install everything in the given list. + + (to be called after having downloaded and unpacked the packages) + """ + + if to_install: + logger.info( + 'Installing collected packages: %s', + ', '.join([req.name for req in to_install]), + ) + + with indent_log(): + for requirement in to_install: + if requirement.conflicts_with: + logger.info( + 'Found existing installation: %s', + requirement.conflicts_with, + ) + with indent_log(): + uninstalled_pathset = requirement.uninstall( + auto_confirm=True + ) + try: + requirement.install( + install_options, + global_options, + *args, + **kwargs + ) + except Exception: + should_rollback = ( + requirement.conflicts_with and + not requirement.install_succeeded + ) + # if install did not succeed, rollback previous uninstall + if should_rollback: + uninstalled_pathset.rollback() + raise + else: + should_commit = ( + requirement.conflicts_with and + requirement.install_succeeded + ) + if should_commit: + uninstalled_pathset.commit() + requirement.remove_temporary_source() + + return to_install diff --git a/venv/Lib/site-packages/pip/_internal/req/constructors.py b/venv/Lib/site-packages/pip/_internal/req/constructors.py new file mode 100644 index 0000000..4c4641d --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/req/constructors.py @@ -0,0 +1,298 @@ +"""Backing implementation for InstallRequirement's various constructors + +The idea here is that these formed a major chunk of InstallRequirement's size +so, moving them and support code dedicated to them outside of that class +helps creates for better understandability for the rest of the code. + +These are meant to be used elsewhere within pip to create instances of +InstallRequirement. +""" + +import logging +import os +import re +import traceback + +from pip._vendor.packaging.markers import Marker +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement +from pip._vendor.packaging.specifiers import Specifier +from pip._vendor.pkg_resources import RequirementParseError, parse_requirements + +from pip._internal.download import ( + is_archive_file, is_url, path_to_url, url_to_path, +) +from pip._internal.exceptions import InstallationError +from pip._internal.models.index import PyPI, TestPyPI +from pip._internal.models.link import Link +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.misc import is_installable_dir +from pip._internal.vcs import vcs +from pip._internal.wheel import Wheel + +__all__ = [ + "install_req_from_editable", "install_req_from_line", + "parse_editable" +] + +logger = logging.getLogger(__name__) +operators = Specifier._operators.keys() + + +def _strip_extras(path): + m = re.match(r'^(.+)(\[[^\]]+\])$', path) + extras = None + if m: + path_no_extras = m.group(1) + extras = m.group(2) + else: + path_no_extras = path + + return path_no_extras, extras + + +def parse_editable(editable_req): + """Parses an editable requirement into: + - a requirement name + - an URL + - extras + - editable options + Accepted requirements: + svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir + .[some_extra] + """ + + url = editable_req + + # If a file path is specified with extras, strip off the extras. + url_no_extras, extras = _strip_extras(url) + + if os.path.isdir(url_no_extras): + if not os.path.exists(os.path.join(url_no_extras, 'setup.py')): + raise InstallationError( + "Directory %r is not installable. File 'setup.py' not found." % + url_no_extras + ) + # Treating it as code that has already been checked out + url_no_extras = path_to_url(url_no_extras) + + if url_no_extras.lower().startswith('file:'): + package_name = Link(url_no_extras).egg_fragment + if extras: + return ( + package_name, + url_no_extras, + Requirement("placeholder" + extras.lower()).extras, + ) + else: + return package_name, url_no_extras, None + + for version_control in vcs: + if url.lower().startswith('%s:' % version_control): + url = '%s+%s' % (version_control, url) + break + + if '+' not in url: + raise InstallationError( + '%s should either be a path to a local project or a VCS url ' + 'beginning with svn+, git+, hg+, or bzr+' % + editable_req + ) + + vc_type = url.split('+', 1)[0].lower() + + if not vcs.get_backend(vc_type): + error_message = 'For --editable=%s only ' % editable_req + \ + ', '.join([backend.name + '+URL' for backend in vcs.backends]) + \ + ' is currently supported' + raise InstallationError(error_message) + + package_name = Link(url).egg_fragment + if not package_name: + raise InstallationError( + "Could not detect requirement name for '%s', please specify one " + "with #egg=your_package_name" % editable_req + ) + return package_name, url, None + + +def deduce_helpful_msg(req): + """Returns helpful msg in case requirements file does not exist, + or cannot be parsed. + + :params req: Requirements file path + """ + msg = "" + if os.path.exists(req): + msg = " It does exist." + # Try to parse and check if it is a requirements file. + try: + with open(req, 'r') as fp: + # parse first line only + next(parse_requirements(fp.read())) + msg += " The argument you provided " + \ + "(%s) appears to be a" % (req) + \ + " requirements file. If that is the" + \ + " case, use the '-r' flag to install" + \ + " the packages specified within it." + except RequirementParseError: + logger.debug("Cannot parse '%s' as requirements \ + file" % (req), exc_info=1) + else: + msg += " File '%s' does not exist." % (req) + return msg + + +# ---- The actual constructors follow ---- + + +def install_req_from_editable( + editable_req, comes_from=None, isolated=False, options=None, + wheel_cache=None, constraint=False +): + name, url, extras_override = parse_editable(editable_req) + if url.startswith('file:'): + source_dir = url_to_path(url) + else: + source_dir = None + + if name is not None: + try: + req = Requirement(name) + except InvalidRequirement: + raise InstallationError("Invalid requirement: '%s'" % name) + else: + req = None + return InstallRequirement( + req, comes_from, source_dir=source_dir, + editable=True, + link=Link(url), + constraint=constraint, + isolated=isolated, + options=options if options else {}, + wheel_cache=wheel_cache, + extras=extras_override or (), + ) + + +def install_req_from_line( + name, comes_from=None, isolated=False, options=None, wheel_cache=None, + constraint=False +): + """Creates an InstallRequirement from a name, which might be a + requirement, directory containing 'setup.py', filename, or URL. + """ + if is_url(name): + marker_sep = '; ' + else: + marker_sep = ';' + if marker_sep in name: + name, markers = name.split(marker_sep, 1) + markers = markers.strip() + if not markers: + markers = None + else: + markers = Marker(markers) + else: + markers = None + name = name.strip() + req = None + path = os.path.normpath(os.path.abspath(name)) + link = None + extras = None + + if is_url(name): + link = Link(name) + else: + p, extras = _strip_extras(path) + looks_like_dir = os.path.isdir(p) and ( + os.path.sep in name or + (os.path.altsep is not None and os.path.altsep in name) or + name.startswith('.') + ) + if looks_like_dir: + if not is_installable_dir(p): + raise InstallationError( + "Directory %r is not installable. Neither 'setup.py' " + "nor 'pyproject.toml' found." % name + ) + link = Link(path_to_url(p)) + elif is_archive_file(p): + if not os.path.isfile(p): + logger.warning( + 'Requirement %r looks like a filename, but the ' + 'file does not exist', + name + ) + link = Link(path_to_url(p)) + + # it's a local file, dir, or url + if link: + # Handle relative file URLs + if link.scheme == 'file' and re.search(r'\.\./', link.url): + link = Link( + path_to_url(os.path.normpath(os.path.abspath(link.path)))) + # wheel file + if link.is_wheel: + wheel = Wheel(link.filename) # can raise InvalidWheelFilename + req = "%s==%s" % (wheel.name, wheel.version) + else: + # set the req to the egg fragment. when it's not there, this + # will become an 'unnamed' requirement + req = link.egg_fragment + + # a requirement specifier + else: + req = name + + if extras: + extras = Requirement("placeholder" + extras.lower()).extras + else: + extras = () + if req is not None: + try: + req = Requirement(req) + except InvalidRequirement: + if os.path.sep in req: + add_msg = "It looks like a path." + add_msg += deduce_helpful_msg(req) + elif '=' in req and not any(op in req for op in operators): + add_msg = "= is not a valid operator. Did you mean == ?" + else: + add_msg = traceback.format_exc() + raise InstallationError( + "Invalid requirement: '%s'\n%s" % (req, add_msg) + ) + + return InstallRequirement( + req, comes_from, link=link, markers=markers, + isolated=isolated, + options=options if options else {}, + wheel_cache=wheel_cache, + constraint=constraint, + extras=extras, + ) + + +def install_req_from_req( + req, comes_from=None, isolated=False, wheel_cache=None +): + try: + req = Requirement(req) + except InvalidRequirement: + raise InstallationError("Invalid requirement: '%s'" % req) + + domains_not_allowed = [ + PyPI.file_storage_domain, + TestPyPI.file_storage_domain, + ] + if req.url and comes_from.link.netloc in domains_not_allowed: + # Explicitly disallow pypi packages that depend on external urls + raise InstallationError( + "Packages installed from PyPI cannot depend on packages " + "which are not also hosted on PyPI.\n" + "%s depends on %s " % (comes_from.name, req) + ) + + return InstallRequirement( + req, comes_from, isolated=isolated, wheel_cache=wheel_cache + ) diff --git a/venv/Lib/site-packages/pip/_internal/req/req_file.py b/venv/Lib/site-packages/pip/_internal/req/req_file.py new file mode 100644 index 0000000..e7acf7c --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/req/req_file.py @@ -0,0 +1,340 @@ +""" +Requirements file parsing +""" + +from __future__ import absolute_import + +import optparse +import os +import re +import shlex +import sys + +from pip._vendor.six.moves import filterfalse +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.cli import cmdoptions +from pip._internal.download import get_file_content +from pip._internal.exceptions import RequirementsFileParseError +from pip._internal.req.constructors import ( + install_req_from_editable, install_req_from_line, +) + +__all__ = ['parse_requirements'] + +SCHEME_RE = re.compile(r'^(http|https|file):', re.I) +COMMENT_RE = re.compile(r'(^|\s)+#.*$') + +# Matches environment variable-style values in '${MY_VARIABLE_1}' with the +# variable name consisting of only uppercase letters, digits or the '_' +# (underscore). This follows the POSIX standard defined in IEEE Std 1003.1, +# 2013 Edition. +ENV_VAR_RE = re.compile(r'(?P<var>\$\{(?P<name>[A-Z0-9_]+)\})') + +SUPPORTED_OPTIONS = [ + cmdoptions.constraints, + cmdoptions.editable, + cmdoptions.requirements, + cmdoptions.no_index, + cmdoptions.index_url, + cmdoptions.find_links, + cmdoptions.extra_index_url, + cmdoptions.always_unzip, + cmdoptions.no_binary, + cmdoptions.only_binary, + cmdoptions.pre, + cmdoptions.process_dependency_links, + cmdoptions.trusted_host, + cmdoptions.require_hashes, +] + +# options to be passed to requirements +SUPPORTED_OPTIONS_REQ = [ + cmdoptions.install_options, + cmdoptions.global_options, + cmdoptions.hash, +] + +# the 'dest' string values +SUPPORTED_OPTIONS_REQ_DEST = [o().dest for o in SUPPORTED_OPTIONS_REQ] + + +def parse_requirements(filename, finder=None, comes_from=None, options=None, + session=None, constraint=False, wheel_cache=None): + """Parse a requirements file and yield InstallRequirement instances. + + :param filename: Path or url of requirements file. + :param finder: Instance of pip.index.PackageFinder. + :param comes_from: Origin description of requirements. + :param options: cli options. + :param session: Instance of pip.download.PipSession. + :param constraint: If true, parsing a constraint file rather than + requirements file. + :param wheel_cache: Instance of pip.wheel.WheelCache + """ + if session is None: + raise TypeError( + "parse_requirements() missing 1 required keyword argument: " + "'session'" + ) + + _, content = get_file_content( + filename, comes_from=comes_from, session=session + ) + + lines_enum = preprocess(content, options) + + for line_number, line in lines_enum: + req_iter = process_line(line, filename, line_number, finder, + comes_from, options, session, wheel_cache, + constraint=constraint) + for req in req_iter: + yield req + + +def preprocess(content, options): + """Split, filter, and join lines, and return a line iterator + + :param content: the content of the requirements file + :param options: cli options + """ + lines_enum = enumerate(content.splitlines(), start=1) + lines_enum = join_lines(lines_enum) + lines_enum = ignore_comments(lines_enum) + lines_enum = skip_regex(lines_enum, options) + lines_enum = expand_env_variables(lines_enum) + return lines_enum + + +def process_line(line, filename, line_number, finder=None, comes_from=None, + options=None, session=None, wheel_cache=None, + constraint=False): + """Process a single requirements line; This can result in creating/yielding + requirements, or updating the finder. + + For lines that contain requirements, the only options that have an effect + are from SUPPORTED_OPTIONS_REQ, and they are scoped to the + requirement. Other options from SUPPORTED_OPTIONS may be present, but are + ignored. + + For lines that do not contain requirements, the only options that have an + effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may + be present, but are ignored. These lines may contain multiple options + (although our docs imply only one is supported), and all our parsed and + affect the finder. + + :param constraint: If True, parsing a constraints file. + :param options: OptionParser options that we may update + """ + parser = build_parser(line) + defaults = parser.get_default_values() + defaults.index_url = None + if finder: + # `finder.format_control` will be updated during parsing + defaults.format_control = finder.format_control + args_str, options_str = break_args_options(line) + if sys.version_info < (2, 7, 3): + # Prior to 2.7.3, shlex cannot deal with unicode entries + options_str = options_str.encode('utf8') + opts, _ = parser.parse_args(shlex.split(options_str), defaults) + + # preserve for the nested code path + line_comes_from = '%s %s (line %s)' % ( + '-c' if constraint else '-r', filename, line_number, + ) + + # yield a line requirement + if args_str: + isolated = options.isolated_mode if options else False + if options: + cmdoptions.check_install_build_global(options, opts) + # get the options that apply to requirements + req_options = {} + for dest in SUPPORTED_OPTIONS_REQ_DEST: + if dest in opts.__dict__ and opts.__dict__[dest]: + req_options[dest] = opts.__dict__[dest] + yield install_req_from_line( + args_str, line_comes_from, constraint=constraint, + isolated=isolated, options=req_options, wheel_cache=wheel_cache + ) + + # yield an editable requirement + elif opts.editables: + isolated = options.isolated_mode if options else False + yield install_req_from_editable( + opts.editables[0], comes_from=line_comes_from, + constraint=constraint, isolated=isolated, wheel_cache=wheel_cache + ) + + # parse a nested requirements file + elif opts.requirements or opts.constraints: + if opts.requirements: + req_path = opts.requirements[0] + nested_constraint = False + else: + req_path = opts.constraints[0] + nested_constraint = True + # original file is over http + if SCHEME_RE.search(filename): + # do a url join so relative paths work + req_path = urllib_parse.urljoin(filename, req_path) + # original file and nested file are paths + elif not SCHEME_RE.search(req_path): + # do a join so relative paths work + req_path = os.path.join(os.path.dirname(filename), req_path) + # TODO: Why not use `comes_from='-r {} (line {})'` here as well? + parser = parse_requirements( + req_path, finder, comes_from, options, session, + constraint=nested_constraint, wheel_cache=wheel_cache + ) + for req in parser: + yield req + + # percolate hash-checking option upward + elif opts.require_hashes: + options.require_hashes = opts.require_hashes + + # set finder options + elif finder: + if opts.index_url: + finder.index_urls = [opts.index_url] + if opts.no_index is True: + finder.index_urls = [] + if opts.extra_index_urls: + finder.index_urls.extend(opts.extra_index_urls) + if opts.find_links: + # FIXME: it would be nice to keep track of the source + # of the find_links: support a find-links local path + # relative to a requirements file. + value = opts.find_links[0] + req_dir = os.path.dirname(os.path.abspath(filename)) + relative_to_reqs_file = os.path.join(req_dir, value) + if os.path.exists(relative_to_reqs_file): + value = relative_to_reqs_file + finder.find_links.append(value) + if opts.pre: + finder.allow_all_prereleases = True + if opts.process_dependency_links: + finder.process_dependency_links = True + if opts.trusted_hosts: + finder.secure_origins.extend( + ("*", host, "*") for host in opts.trusted_hosts) + + +def break_args_options(line): + """Break up the line into an args and options string. We only want to shlex + (and then optparse) the options, not the args. args can contain markers + which are corrupted by shlex. + """ + tokens = line.split(' ') + args = [] + options = tokens[:] + for token in tokens: + if token.startswith('-') or token.startswith('--'): + break + else: + args.append(token) + options.pop(0) + return ' '.join(args), ' '.join(options) + + +def build_parser(line): + """ + Return a parser for parsing requirement lines + """ + parser = optparse.OptionParser(add_help_option=False) + + option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ + for option_factory in option_factories: + option = option_factory() + parser.add_option(option) + + # By default optparse sys.exits on parsing errors. We want to wrap + # that in our own exception. + def parser_exit(self, msg): + # add offending line + msg = 'Invalid requirement: %s\n%s' % (line, msg) + raise RequirementsFileParseError(msg) + parser.exit = parser_exit + + return parser + + +def join_lines(lines_enum): + """Joins a line ending in '\' with the previous line (except when following + comments). The joined line takes on the index of the first line. + """ + primary_line_number = None + new_line = [] + for line_number, line in lines_enum: + if not line.endswith('\\') or COMMENT_RE.match(line): + if COMMENT_RE.match(line): + # this ensures comments are always matched later + line = ' ' + line + if new_line: + new_line.append(line) + yield primary_line_number, ''.join(new_line) + new_line = [] + else: + yield line_number, line + else: + if not new_line: + primary_line_number = line_number + new_line.append(line.strip('\\')) + + # last line contains \ + if new_line: + yield primary_line_number, ''.join(new_line) + + # TODO: handle space after '\'. + + +def ignore_comments(lines_enum): + """ + Strips comments and filter empty lines. + """ + for line_number, line in lines_enum: + line = COMMENT_RE.sub('', line) + line = line.strip() + if line: + yield line_number, line + + +def skip_regex(lines_enum, options): + """ + Skip lines that match '--skip-requirements-regex' pattern + + Note: the regex pattern is only built once + """ + skip_regex = options.skip_requirements_regex if options else None + if skip_regex: + pattern = re.compile(skip_regex) + lines_enum = filterfalse(lambda e: pattern.search(e[1]), lines_enum) + return lines_enum + + +def expand_env_variables(lines_enum): + """Replace all environment variables that can be retrieved via `os.getenv`. + + The only allowed format for environment variables defined in the + requirement file is `${MY_VARIABLE_1}` to ensure two things: + + 1. Strings that contain a `$` aren't accidentally (partially) expanded. + 2. Ensure consistency across platforms for requirement files. + + These points are the result of a discusssion on the `github pull + request #3514 <https://github.com/pypa/pip/pull/3514>`_. + + Valid characters in variable names follow the `POSIX standard + <http://pubs.opengroup.org/onlinepubs/9699919799/>`_ and are limited + to uppercase letter, digits and the `_` (underscore). + """ + for line_number, line in lines_enum: + for env_var, var_name in ENV_VAR_RE.findall(line): + value = os.getenv(var_name) + if not value: + continue + + line = line.replace(env_var, value) + + yield line_number, line diff --git a/venv/Lib/site-packages/pip/_internal/req/req_install.py b/venv/Lib/site-packages/pip/_internal/req/req_install.py new file mode 100644 index 0000000..c2624fe --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/req/req_install.py @@ -0,0 +1,860 @@ +from __future__ import absolute_import + +import logging +import os +import shutil +import sys +import sysconfig +import zipfile +from distutils.util import change_root + +from pip._vendor import pkg_resources, six +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.pep517.wrappers import Pep517HookCaller + +from pip._internal import wheel +from pip._internal.build_env import NoOpBuildEnvironment +from pip._internal.exceptions import InstallationError +from pip._internal.locations import ( + PIP_DELETE_MARKER_FILENAME, running_under_virtualenv, +) +from pip._internal.models.link import Link +from pip._internal.pyproject import load_pyproject_toml +from pip._internal.req.req_uninstall import UninstallPathSet +from pip._internal.utils.compat import native_str +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + _make_build_dir, ask_path_exists, backup_dir, call_subprocess, + display_path, dist_in_site_packages, dist_in_usersite, ensure_dir, + get_installed_version, rmtree, +) +from pip._internal.utils.packaging import get_metadata +from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.ui import open_spinner +from pip._internal.vcs import vcs +from pip._internal.wheel import move_wheel_files + +logger = logging.getLogger(__name__) + + +class InstallRequirement(object): + """ + Represents something that may be installed later on, may have information + about where to fetch the relavant requirement and also contains logic for + installing the said requirement. + """ + + def __init__(self, req, comes_from, source_dir=None, editable=False, + link=None, update=True, markers=None, + isolated=False, options=None, wheel_cache=None, + constraint=False, extras=()): + assert req is None or isinstance(req, Requirement), req + self.req = req + self.comes_from = comes_from + self.constraint = constraint + if source_dir is not None: + self.source_dir = os.path.normpath(os.path.abspath(source_dir)) + else: + self.source_dir = None + self.editable = editable + + self._wheel_cache = wheel_cache + if link is not None: + self.link = self.original_link = link + else: + self.link = self.original_link = req and req.url and Link(req.url) + + if extras: + self.extras = extras + elif req: + self.extras = { + pkg_resources.safe_extra(extra) for extra in req.extras + } + else: + self.extras = set() + if markers is not None: + self.markers = markers + else: + self.markers = req and req.marker + self._egg_info_path = None + # This holds the pkg_resources.Distribution object if this requirement + # is already available: + self.satisfied_by = None + # This hold the pkg_resources.Distribution object if this requirement + # conflicts with another installed distribution: + self.conflicts_with = None + # Temporary build location + self._temp_build_dir = TempDirectory(kind="req-build") + # Used to store the global directory where the _temp_build_dir should + # have been created. Cf _correct_build_location method. + self._ideal_build_dir = None + # True if the editable should be updated: + self.update = update + # Set to True after successful installation + self.install_succeeded = None + # UninstallPathSet of uninstalled distribution (for possible rollback) + self.uninstalled_pathset = None + self.options = options if options else {} + # Set to True after successful preparation of this requirement + self.prepared = False + self.is_direct = False + + self.isolated = isolated + self.build_env = NoOpBuildEnvironment() + + # The static build requirements (from pyproject.toml) + self.pyproject_requires = None + + # Build requirements that we will check are available + # TODO: We don't do this for --no-build-isolation. Should we? + self.requirements_to_check = [] + + # The PEP 517 backend we should use to build the project + self.pep517_backend = None + + # Are we using PEP 517 for this requirement? + # After pyproject.toml has been loaded, the only valid values are True + # and False. Before loading, None is valid (meaning "use the default"). + # Setting an explicit value before loading pyproject.toml is supported, + # but after loading this flag should be treated as read only. + self.use_pep517 = None + + def __str__(self): + if self.req: + s = str(self.req) + if self.link: + s += ' from %s' % self.link.url + elif self.link: + s = self.link.url + else: + s = '<InstallRequirement>' + if self.satisfied_by is not None: + s += ' in %s' % display_path(self.satisfied_by.location) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += ' (from %s)' % comes_from + return s + + def __repr__(self): + return '<%s object: %s editable=%r>' % ( + self.__class__.__name__, str(self), self.editable) + + def populate_link(self, finder, upgrade, require_hashes): + """Ensure that if a link can be found for this, that it is found. + + Note that self.link may still be None - if Upgrade is False and the + requirement is already installed. + + If require_hashes is True, don't use the wheel cache, because cached + wheels, always built locally, have different hashes than the files + downloaded from the index server and thus throw false hash mismatches. + Furthermore, cached wheels at present have undeterministic contents due + to file modification times. + """ + if self.link is None: + self.link = finder.find_requirement(self, upgrade) + if self._wheel_cache is not None and not require_hashes: + old_link = self.link + self.link = self._wheel_cache.get(self.link, self.name) + if old_link != self.link: + logger.debug('Using cached wheel link: %s', self.link) + + # Things that are valid for all kinds of requirements? + @property + def name(self): + if self.req is None: + return None + return native_str(pkg_resources.safe_name(self.req.name)) + + @property + def specifier(self): + return self.req.specifier + + @property + def is_pinned(self): + """Return whether I am pinned to an exact version. + + For example, some-package==1.2 is pinned; some-package>1.2 is not. + """ + specifiers = self.specifier + return (len(specifiers) == 1 and + next(iter(specifiers)).operator in {'==', '==='}) + + @property + def installed_version(self): + return get_installed_version(self.name) + + def match_markers(self, extras_requested=None): + if not extras_requested: + # Provide an extra to safely evaluate the markers + # without matching any extra + extras_requested = ('',) + if self.markers is not None: + return any( + self.markers.evaluate({'extra': extra}) + for extra in extras_requested) + else: + return True + + @property + def has_hash_options(self): + """Return whether any known-good hashes are specified as options. + + These activate --require-hashes mode; hashes specified as part of a + URL do not. + + """ + return bool(self.options.get('hashes', {})) + + def hashes(self, trust_internet=True): + """Return a hash-comparer that considers my option- and URL-based + hashes to be known-good. + + Hashes in URLs--ones embedded in the requirements file, not ones + downloaded from an index server--are almost peers with ones from + flags. They satisfy --require-hashes (whether it was implicitly or + explicitly activated) but do not activate it. md5 and sha224 are not + allowed in flags, which should nudge people toward good algos. We + always OR all hashes together, even ones from URLs. + + :param trust_internet: Whether to trust URL-based (#md5=...) hashes + downloaded from the internet, as by populate_link() + + """ + good_hashes = self.options.get('hashes', {}).copy() + link = self.link if trust_internet else self.original_link + if link and link.hash: + good_hashes.setdefault(link.hash_name, []).append(link.hash) + return Hashes(good_hashes) + + def from_path(self): + """Format a nice indicator to show where this "comes from" + """ + if self.req is None: + return None + s = str(self.req) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += '->' + comes_from + return s + + def build_location(self, build_dir): + assert build_dir is not None + if self._temp_build_dir.path is not None: + return self._temp_build_dir.path + if self.req is None: + # for requirement via a path to a directory: the name of the + # package is not available yet so we create a temp directory + # Once run_egg_info will have run, we'll be able + # to fix it via _correct_build_location + # Some systems have /tmp as a symlink which confuses custom + # builds (such as numpy). Thus, we ensure that the real path + # is returned. + self._temp_build_dir.create() + self._ideal_build_dir = build_dir + + return self._temp_build_dir.path + if self.editable: + name = self.name.lower() + else: + name = self.name + # FIXME: Is there a better place to create the build_dir? (hg and bzr + # need this) + if not os.path.exists(build_dir): + logger.debug('Creating directory %s', build_dir) + _make_build_dir(build_dir) + return os.path.join(build_dir, name) + + def _correct_build_location(self): + """Move self._temp_build_dir to self._ideal_build_dir/self.req.name + + For some requirements (e.g. a path to a directory), the name of the + package is not available until we run egg_info, so the build_location + will return a temporary directory and store the _ideal_build_dir. + + This is only called by self.run_egg_info to fix the temporary build + directory. + """ + if self.source_dir is not None: + return + assert self.req is not None + assert self._temp_build_dir.path + assert self._ideal_build_dir.path + old_location = self._temp_build_dir.path + self._temp_build_dir.path = None + + new_location = self.build_location(self._ideal_build_dir) + if os.path.exists(new_location): + raise InstallationError( + 'A package already exists in %s; please remove it to continue' + % display_path(new_location)) + logger.debug( + 'Moving package %s from %s to new location %s', + self, display_path(old_location), display_path(new_location), + ) + shutil.move(old_location, new_location) + self._temp_build_dir.path = new_location + self._ideal_build_dir = None + self.source_dir = os.path.normpath(os.path.abspath(new_location)) + self._egg_info_path = None + + def remove_temporary_source(self): + """Remove the source files from this requirement, if they are marked + for deletion""" + if self.source_dir and os.path.exists( + os.path.join(self.source_dir, PIP_DELETE_MARKER_FILENAME)): + logger.debug('Removing source in %s', self.source_dir) + rmtree(self.source_dir) + self.source_dir = None + self._temp_build_dir.cleanup() + self.build_env.cleanup() + + def check_if_exists(self, use_user_site): + """Find an installed distribution that satisfies or conflicts + with this requirement, and set self.satisfied_by or + self.conflicts_with appropriately. + """ + if self.req is None: + return False + try: + # get_distribution() will resolve the entire list of requirements + # anyway, and we've already determined that we need the requirement + # in question, so strip the marker so that we don't try to + # evaluate it. + no_marker = Requirement(str(self.req)) + no_marker.marker = None + self.satisfied_by = pkg_resources.get_distribution(str(no_marker)) + if self.editable and self.satisfied_by: + self.conflicts_with = self.satisfied_by + # when installing editables, nothing pre-existing should ever + # satisfy + self.satisfied_by = None + return True + except pkg_resources.DistributionNotFound: + return False + except pkg_resources.VersionConflict: + existing_dist = pkg_resources.get_distribution( + self.req.name + ) + if use_user_site: + if dist_in_usersite(existing_dist): + self.conflicts_with = existing_dist + elif (running_under_virtualenv() and + dist_in_site_packages(existing_dist)): + raise InstallationError( + "Will not install to the user site because it will " + "lack sys.path precedence to %s in %s" % + (existing_dist.project_name, existing_dist.location) + ) + else: + self.conflicts_with = existing_dist + return True + + # Things valid for wheels + @property + def is_wheel(self): + return self.link and self.link.is_wheel + + def move_wheel_files(self, wheeldir, root=None, home=None, prefix=None, + warn_script_location=True, use_user_site=False, + pycompile=True): + move_wheel_files( + self.name, self.req, wheeldir, + user=use_user_site, + home=home, + root=root, + prefix=prefix, + pycompile=pycompile, + isolated=self.isolated, + warn_script_location=warn_script_location, + ) + + # Things valid for sdists + @property + def setup_py_dir(self): + return os.path.join( + self.source_dir, + self.link and self.link.subdirectory_fragment or '') + + @property + def setup_py(self): + assert self.source_dir, "No source dir for %s" % self + + setup_py = os.path.join(self.setup_py_dir, 'setup.py') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(setup_py, six.text_type): + setup_py = setup_py.encode(sys.getfilesystemencoding()) + + return setup_py + + @property + def pyproject_toml(self): + assert self.source_dir, "No source dir for %s" % self + + pp_toml = os.path.join(self.setup_py_dir, 'pyproject.toml') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(pp_toml, six.text_type): + pp_toml = pp_toml.encode(sys.getfilesystemencoding()) + + return pp_toml + + def load_pyproject_toml(self): + """Load the pyproject.toml file. + + After calling this routine, all of the attributes related to PEP 517 + processing for this requirement have been set. In particular, the + use_pep517 attribute can be used to determine whether we should + follow the PEP 517 or legacy (setup.py) code path. + """ + pep517_data = load_pyproject_toml( + self.use_pep517, + self.pyproject_toml, + self.setup_py, + str(self) + ) + + if pep517_data is None: + self.use_pep517 = False + else: + self.use_pep517 = True + requires, backend, check = pep517_data + self.requirements_to_check = check + self.pyproject_requires = requires + self.pep517_backend = Pep517HookCaller(self.setup_py_dir, backend) + + def run_egg_info(self): + assert self.source_dir + if self.name: + logger.debug( + 'Running setup.py (path:%s) egg_info for package %s', + self.setup_py, self.name, + ) + else: + logger.debug( + 'Running setup.py (path:%s) egg_info for package from %s', + self.setup_py, self.link, + ) + + with indent_log(): + script = SETUPTOOLS_SHIM % self.setup_py + base_cmd = [sys.executable, '-c', script] + if self.isolated: + base_cmd += ["--no-user-cfg"] + egg_info_cmd = base_cmd + ['egg_info'] + # We can't put the .egg-info files at the root, because then the + # source code will be mistaken for an installed egg, causing + # problems + if self.editable: + egg_base_option = [] + else: + egg_info_dir = os.path.join(self.setup_py_dir, 'pip-egg-info') + ensure_dir(egg_info_dir) + egg_base_option = ['--egg-base', 'pip-egg-info'] + with self.build_env: + call_subprocess( + egg_info_cmd + egg_base_option, + cwd=self.setup_py_dir, + show_stdout=False, + command_desc='python setup.py egg_info') + + if not self.req: + if isinstance(parse_version(self.metadata["Version"]), Version): + op = "==" + else: + op = "===" + self.req = Requirement( + "".join([ + self.metadata["Name"], + op, + self.metadata["Version"], + ]) + ) + self._correct_build_location() + else: + metadata_name = canonicalize_name(self.metadata["Name"]) + if canonicalize_name(self.req.name) != metadata_name: + logger.warning( + 'Running setup.py (path:%s) egg_info for package %s ' + 'produced metadata for project name %s. Fix your ' + '#egg=%s fragments.', + self.setup_py, self.name, metadata_name, self.name + ) + self.req = Requirement(metadata_name) + + @property + def egg_info_path(self): + if self._egg_info_path is None: + if self.editable: + base = self.source_dir + else: + base = os.path.join(self.setup_py_dir, 'pip-egg-info') + filenames = os.listdir(base) + if self.editable: + filenames = [] + for root, dirs, files in os.walk(base): + for dir in vcs.dirnames: + if dir in dirs: + dirs.remove(dir) + # Iterate over a copy of ``dirs``, since mutating + # a list while iterating over it can cause trouble. + # (See https://github.com/pypa/pip/pull/462.) + for dir in list(dirs): + # Don't search in anything that looks like a virtualenv + # environment + if ( + os.path.lexists( + os.path.join(root, dir, 'bin', 'python') + ) or + os.path.exists( + os.path.join( + root, dir, 'Scripts', 'Python.exe' + ) + )): + dirs.remove(dir) + # Also don't search through tests + elif dir == 'test' or dir == 'tests': + dirs.remove(dir) + filenames.extend([os.path.join(root, dir) + for dir in dirs]) + filenames = [f for f in filenames if f.endswith('.egg-info')] + + if not filenames: + raise InstallationError( + "Files/directories not found in %s" % base + ) + # if we have more than one match, we pick the toplevel one. This + # can easily be the case if there is a dist folder which contains + # an extracted tarball for testing purposes. + if len(filenames) > 1: + filenames.sort( + key=lambda x: x.count(os.path.sep) + + (os.path.altsep and x.count(os.path.altsep) or 0) + ) + self._egg_info_path = os.path.join(base, filenames[0]) + return self._egg_info_path + + @property + def metadata(self): + if not hasattr(self, '_metadata'): + self._metadata = get_metadata(self.get_dist()) + + return self._metadata + + def get_dist(self): + """Return a pkg_resources.Distribution built from self.egg_info_path""" + egg_info = self.egg_info_path.rstrip(os.path.sep) + base_dir = os.path.dirname(egg_info) + metadata = pkg_resources.PathMetadata(base_dir, egg_info) + dist_name = os.path.splitext(os.path.basename(egg_info))[0] + return pkg_resources.Distribution( + os.path.dirname(egg_info), + project_name=dist_name, + metadata=metadata, + ) + + def assert_source_matches_version(self): + assert self.source_dir + version = self.metadata['version'] + if self.req.specifier and version not in self.req.specifier: + logger.warning( + 'Requested %s, but installing version %s', + self, + version, + ) + else: + logger.debug( + 'Source in %s has version %s, which satisfies requirement %s', + display_path(self.source_dir), + version, + self, + ) + + # For both source distributions and editables + def ensure_has_source_dir(self, parent_dir): + """Ensure that a source_dir is set. + + This will create a temporary build dir if the name of the requirement + isn't known yet. + + :param parent_dir: The ideal pip parent_dir for the source_dir. + Generally src_dir for editables and build_dir for sdists. + :return: self.source_dir + """ + if self.source_dir is None: + self.source_dir = self.build_location(parent_dir) + return self.source_dir + + # For editable installations + def install_editable(self, install_options, + global_options=(), prefix=None): + logger.info('Running setup.py develop for %s', self.name) + + if self.isolated: + global_options = list(global_options) + ["--no-user-cfg"] + + if prefix: + prefix_param = ['--prefix={}'.format(prefix)] + install_options = list(install_options) + prefix_param + + with indent_log(): + # FIXME: should we do --install-headers here too? + with self.build_env: + call_subprocess( + [ + sys.executable, + '-c', + SETUPTOOLS_SHIM % self.setup_py + ] + + list(global_options) + + ['develop', '--no-deps'] + + list(install_options), + + cwd=self.setup_py_dir, + show_stdout=False, + ) + + self.install_succeeded = True + + def update_editable(self, obtain=True): + if not self.link: + logger.debug( + "Cannot update repository at %s; repository location is " + "unknown", + self.source_dir, + ) + return + assert self.editable + assert self.source_dir + if self.link.scheme == 'file': + # Static paths don't get updated + return + assert '+' in self.link.url, "bad url: %r" % self.link.url + if not self.update: + return + vc_type, url = self.link.url.split('+', 1) + backend = vcs.get_backend(vc_type) + if backend: + vcs_backend = backend(self.link.url) + if obtain: + vcs_backend.obtain(self.source_dir) + else: + vcs_backend.export(self.source_dir) + else: + assert 0, ( + 'Unexpected version control type (in %s): %s' + % (self.link, vc_type)) + + # Top-level Actions + def uninstall(self, auto_confirm=False, verbose=False, + use_user_site=False): + """ + Uninstall the distribution currently satisfying this requirement. + + Prompts before removing or modifying files unless + ``auto_confirm`` is True. + + Refuses to delete or modify files outside of ``sys.prefix`` - + thus uninstallation within a virtual environment can only + modify that virtual environment, even if the virtualenv is + linked to global site-packages. + + """ + if not self.check_if_exists(use_user_site): + logger.warning("Skipping %s as it is not installed.", self.name) + return + dist = self.satisfied_by or self.conflicts_with + + uninstalled_pathset = UninstallPathSet.from_dist(dist) + uninstalled_pathset.remove(auto_confirm, verbose) + return uninstalled_pathset + + def _clean_zip_name(self, name, prefix): # only used by archive. + assert name.startswith(prefix + os.path.sep), ( + "name %r doesn't start with prefix %r" % (name, prefix) + ) + name = name[len(prefix) + 1:] + name = name.replace(os.path.sep, '/') + return name + + # TODO: Investigate if this should be kept in InstallRequirement + # Seems to be used only when VCS + downloads + def archive(self, build_dir): + assert self.source_dir + create_archive = True + archive_name = '%s-%s.zip' % (self.name, self.metadata["version"]) + archive_path = os.path.join(build_dir, archive_name) + if os.path.exists(archive_path): + response = ask_path_exists( + 'The file %s exists. (i)gnore, (w)ipe, (b)ackup, (a)bort ' % + display_path(archive_path), ('i', 'w', 'b', 'a')) + if response == 'i': + create_archive = False + elif response == 'w': + logger.warning('Deleting %s', display_path(archive_path)) + os.remove(archive_path) + elif response == 'b': + dest_file = backup_dir(archive_path) + logger.warning( + 'Backing up %s to %s', + display_path(archive_path), + display_path(dest_file), + ) + shutil.move(archive_path, dest_file) + elif response == 'a': + sys.exit(-1) + if create_archive: + zip = zipfile.ZipFile( + archive_path, 'w', zipfile.ZIP_DEFLATED, + allowZip64=True + ) + dir = os.path.normcase(os.path.abspath(self.setup_py_dir)) + for dirpath, dirnames, filenames in os.walk(dir): + if 'pip-egg-info' in dirnames: + dirnames.remove('pip-egg-info') + for dirname in dirnames: + dirname = os.path.join(dirpath, dirname) + name = self._clean_zip_name(dirname, dir) + zipdir = zipfile.ZipInfo(self.name + '/' + name + '/') + zipdir.external_attr = 0x1ED << 16 # 0o755 + zip.writestr(zipdir, '') + for filename in filenames: + if filename == PIP_DELETE_MARKER_FILENAME: + continue + filename = os.path.join(dirpath, filename) + name = self._clean_zip_name(filename, dir) + zip.write(filename, self.name + '/' + name) + zip.close() + logger.info('Saved %s', display_path(archive_path)) + + def install(self, install_options, global_options=None, root=None, + home=None, prefix=None, warn_script_location=True, + use_user_site=False, pycompile=True): + global_options = global_options if global_options is not None else [] + if self.editable: + self.install_editable( + install_options, global_options, prefix=prefix, + ) + return + if self.is_wheel: + version = wheel.wheel_version(self.source_dir) + wheel.check_compatibility(version, self.name) + + self.move_wheel_files( + self.source_dir, root=root, prefix=prefix, home=home, + warn_script_location=warn_script_location, + use_user_site=use_user_site, pycompile=pycompile, + ) + self.install_succeeded = True + return + + # Extend the list of global and install options passed on to + # the setup.py call with the ones from the requirements file. + # Options specified in requirements file override those + # specified on the command line, since the last option given + # to setup.py is the one that is used. + global_options = list(global_options) + \ + self.options.get('global_options', []) + install_options = list(install_options) + \ + self.options.get('install_options', []) + + if self.isolated: + global_options = global_options + ["--no-user-cfg"] + + with TempDirectory(kind="record") as temp_dir: + record_filename = os.path.join(temp_dir.path, 'install-record.txt') + install_args = self.get_install_args( + global_options, record_filename, root, prefix, pycompile, + ) + msg = 'Running setup.py install for %s' % (self.name,) + with open_spinner(msg) as spinner: + with indent_log(): + with self.build_env: + call_subprocess( + install_args + install_options, + cwd=self.setup_py_dir, + show_stdout=False, + spinner=spinner, + ) + + if not os.path.exists(record_filename): + logger.debug('Record file %s not found', record_filename) + return + self.install_succeeded = True + + def prepend_root(path): + if root is None or not os.path.isabs(path): + return path + else: + return change_root(root, path) + + with open(record_filename) as f: + for line in f: + directory = os.path.dirname(line) + if directory.endswith('.egg-info'): + egg_info_dir = prepend_root(directory) + break + else: + logger.warning( + 'Could not find .egg-info directory in install record' + ' for %s', + self, + ) + # FIXME: put the record somewhere + # FIXME: should this be an error? + return + new_lines = [] + with open(record_filename) as f: + for line in f: + filename = line.strip() + if os.path.isdir(filename): + filename += os.path.sep + new_lines.append( + os.path.relpath(prepend_root(filename), egg_info_dir) + ) + new_lines.sort() + ensure_dir(egg_info_dir) + inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt') + with open(inst_files_path, 'w') as f: + f.write('\n'.join(new_lines) + '\n') + + def get_install_args(self, global_options, record_filename, root, prefix, + pycompile): + install_args = [sys.executable, "-u"] + install_args.append('-c') + install_args.append(SETUPTOOLS_SHIM % self.setup_py) + install_args += list(global_options) + \ + ['install', '--record', record_filename] + install_args += ['--single-version-externally-managed'] + + if root is not None: + install_args += ['--root', root] + if prefix is not None: + install_args += ['--prefix', prefix] + + if pycompile: + install_args += ["--compile"] + else: + install_args += ["--no-compile"] + + if running_under_virtualenv(): + py_ver_str = 'python' + sysconfig.get_python_version() + install_args += ['--install-headers', + os.path.join(sys.prefix, 'include', 'site', + py_ver_str, self.name)] + + return install_args diff --git a/venv/Lib/site-packages/pip/_internal/req/req_set.py b/venv/Lib/site-packages/pip/_internal/req/req_set.py new file mode 100644 index 0000000..b198317 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/req/req_set.py @@ -0,0 +1,181 @@ +from __future__ import absolute_import + +import logging +from collections import OrderedDict + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.logging import indent_log +from pip._internal.wheel import Wheel + +logger = logging.getLogger(__name__) + + +class RequirementSet(object): + + def __init__(self, require_hashes=False, check_supported_wheels=True): + """Create a RequirementSet. + """ + + self.requirements = OrderedDict() + self.require_hashes = require_hashes + self.check_supported_wheels = check_supported_wheels + + # Mapping of alias: real_name + self.requirement_aliases = {} + self.unnamed_requirements = [] + self.successfully_downloaded = [] + self.reqs_to_cleanup = [] + + def __str__(self): + reqs = [req for req in self.requirements.values() + if not req.comes_from] + reqs.sort(key=lambda req: req.name.lower()) + return ' '.join([str(req.req) for req in reqs]) + + def __repr__(self): + reqs = [req for req in self.requirements.values()] + reqs.sort(key=lambda req: req.name.lower()) + reqs_str = ', '.join([str(req.req) for req in reqs]) + return ('<%s object; %d requirement(s): %s>' + % (self.__class__.__name__, len(reqs), reqs_str)) + + def add_requirement(self, install_req, parent_req_name=None, + extras_requested=None): + """Add install_req as a requirement to install. + + :param parent_req_name: The name of the requirement that needed this + added. The name is used because when multiple unnamed requirements + resolve to the same name, we could otherwise end up with dependency + links that point outside the Requirements set. parent_req must + already be added. Note that None implies that this is a user + supplied requirement, vs an inferred one. + :param extras_requested: an iterable of extras used to evaluate the + environment markers. + :return: Additional requirements to scan. That is either [] if + the requirement is not applicable, or [install_req] if the + requirement is applicable and has just been added. + """ + name = install_req.name + + # If the markers do not match, ignore this requirement. + if not install_req.match_markers(extras_requested): + logger.info( + "Ignoring %s: markers '%s' don't match your environment", + name, install_req.markers, + ) + return [], None + + # If the wheel is not supported, raise an error. + # Should check this after filtering out based on environment markers to + # allow specifying different wheels based on the environment/OS, in a + # single requirements file. + if install_req.link and install_req.link.is_wheel: + wheel = Wheel(install_req.link.filename) + if self.check_supported_wheels and not wheel.supported(): + raise InstallationError( + "%s is not a supported wheel on this platform." % + wheel.filename + ) + + # This next bit is really a sanity check. + assert install_req.is_direct == (parent_req_name is None), ( + "a direct req shouldn't have a parent and also, " + "a non direct req should have a parent" + ) + + # Unnamed requirements are scanned again and the requirement won't be + # added as a dependency until after scanning. + if not name: + # url or path requirement w/o an egg fragment + self.unnamed_requirements.append(install_req) + return [install_req], None + + try: + existing_req = self.get_requirement(name) + except KeyError: + existing_req = None + + has_conflicting_requirement = ( + parent_req_name is None and + existing_req and + not existing_req.constraint and + existing_req.extras == install_req.extras and + existing_req.req.specifier != install_req.req.specifier + ) + if has_conflicting_requirement: + raise InstallationError( + "Double requirement given: %s (already in %s, name=%r)" + % (install_req, existing_req, name) + ) + + # When no existing requirement exists, add the requirement as a + # dependency and it will be scanned again after. + if not existing_req: + self.requirements[name] = install_req + # FIXME: what about other normalizations? E.g., _ vs. -? + if name.lower() != name: + self.requirement_aliases[name.lower()] = name + # We'd want to rescan this requirements later + return [install_req], install_req + + # Assume there's no need to scan, and that we've already + # encountered this for scanning. + if install_req.constraint or not existing_req.constraint: + return [], existing_req + + does_not_satisfy_constraint = ( + install_req.link and + not ( + existing_req.link and + install_req.link.path == existing_req.link.path + ) + ) + if does_not_satisfy_constraint: + self.reqs_to_cleanup.append(install_req) + raise InstallationError( + "Could not satisfy constraints for '%s': " + "installation from path or url cannot be " + "constrained to a version" % name, + ) + # If we're now installing a constraint, mark the existing + # object for real installation. + existing_req.constraint = False + existing_req.extras = tuple(sorted( + set(existing_req.extras) | set(install_req.extras) + )) + logger.debug( + "Setting %s extras to: %s", + existing_req, existing_req.extras, + ) + # Return the existing requirement for addition to the parent and + # scanning again. + return [existing_req], existing_req + + def has_requirement(self, project_name): + name = project_name.lower() + if (name in self.requirements and + not self.requirements[name].constraint or + name in self.requirement_aliases and + not self.requirements[self.requirement_aliases[name]].constraint): + return True + return False + + @property + def has_requirements(self): + return list(req for req in self.requirements.values() if not + req.constraint) or self.unnamed_requirements + + def get_requirement(self, project_name): + for name in project_name, project_name.lower(): + if name in self.requirements: + return self.requirements[name] + if name in self.requirement_aliases: + return self.requirements[self.requirement_aliases[name]] + raise KeyError("No project with the name %r" % project_name) + + def cleanup_files(self): + """Clean up files, remove builds.""" + logger.debug('Cleaning up...') + with indent_log(): + for req in self.reqs_to_cleanup: + req.remove_temporary_source() diff --git a/venv/Lib/site-packages/pip/_internal/req/req_tracker.py b/venv/Lib/site-packages/pip/_internal/req/req_tracker.py new file mode 100644 index 0000000..0a86f4c --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/req/req_tracker.py @@ -0,0 +1,76 @@ +from __future__ import absolute_import + +import contextlib +import errno +import hashlib +import logging +import os + +from pip._internal.utils.temp_dir import TempDirectory + +logger = logging.getLogger(__name__) + + +class RequirementTracker(object): + + def __init__(self): + self._root = os.environ.get('PIP_REQ_TRACKER') + if self._root is None: + self._temp_dir = TempDirectory(delete=False, kind='req-tracker') + self._temp_dir.create() + self._root = os.environ['PIP_REQ_TRACKER'] = self._temp_dir.path + logger.debug('Created requirements tracker %r', self._root) + else: + self._temp_dir = None + logger.debug('Re-using requirements tracker %r', self._root) + self._entries = set() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.cleanup() + + def _entry_path(self, link): + hashed = hashlib.sha224(link.url_without_fragment.encode()).hexdigest() + return os.path.join(self._root, hashed) + + def add(self, req): + link = req.link + info = str(req) + entry_path = self._entry_path(link) + try: + with open(entry_path) as fp: + # Error, these's already a build in progress. + raise LookupError('%s is already being built: %s' + % (link, fp.read())) + except IOError as e: + if e.errno != errno.ENOENT: + raise + assert req not in self._entries + with open(entry_path, 'w') as fp: + fp.write(info) + self._entries.add(req) + logger.debug('Added %s to build tracker %r', req, self._root) + + def remove(self, req): + link = req.link + self._entries.remove(req) + os.unlink(self._entry_path(link)) + logger.debug('Removed %s from build tracker %r', req, self._root) + + def cleanup(self): + for req in set(self._entries): + self.remove(req) + remove = self._temp_dir is not None + if remove: + self._temp_dir.cleanup() + logger.debug('%s build tracker %r', + 'Removed' if remove else 'Cleaned', + self._root) + + @contextlib.contextmanager + def track(self, req): + self.add(req) + yield + self.remove(req) diff --git a/venv/Lib/site-packages/pip/_internal/req/req_uninstall.py b/venv/Lib/site-packages/pip/_internal/req/req_uninstall.py new file mode 100644 index 0000000..a7d8230 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/req/req_uninstall.py @@ -0,0 +1,460 @@ +from __future__ import absolute_import + +import csv +import functools +import logging +import os +import sys +import sysconfig + +from pip._vendor import pkg_resources + +from pip._internal.exceptions import UninstallationError +from pip._internal.locations import bin_py, bin_user +from pip._internal.utils.compat import WINDOWS, cache_from_source, uses_pycache +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + FakeFile, ask, dist_in_usersite, dist_is_local, egg_link_path, is_local, + normalize_path, renames, +) +from pip._internal.utils.temp_dir import TempDirectory + +logger = logging.getLogger(__name__) + + +def _script_names(dist, script_name, is_gui): + """Create the fully qualified name of the files created by + {console,gui}_scripts for the given ``dist``. + Returns the list of file names + """ + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + exe_name = os.path.join(bin_dir, script_name) + paths_to_remove = [exe_name] + if WINDOWS: + paths_to_remove.append(exe_name + '.exe') + paths_to_remove.append(exe_name + '.exe.manifest') + if is_gui: + paths_to_remove.append(exe_name + '-script.pyw') + else: + paths_to_remove.append(exe_name + '-script.py') + return paths_to_remove + + +def _unique(fn): + @functools.wraps(fn) + def unique(*args, **kw): + seen = set() + for item in fn(*args, **kw): + if item not in seen: + seen.add(item) + yield item + return unique + + +@_unique +def uninstallation_paths(dist): + """ + Yield all the uninstallation paths for dist based on RECORD-without-.py[co] + + Yield paths to all the files in RECORD. For each .py file in RECORD, add + the .pyc and .pyo in the same directory. + + UninstallPathSet.add() takes care of the __pycache__ .py[co]. + """ + r = csv.reader(FakeFile(dist.get_metadata_lines('RECORD'))) + for row in r: + path = os.path.join(dist.location, row[0]) + yield path + if path.endswith('.py'): + dn, fn = os.path.split(path) + base = fn[:-3] + path = os.path.join(dn, base + '.pyc') + yield path + path = os.path.join(dn, base + '.pyo') + yield path + + +def compact(paths): + """Compact a path set to contain the minimal number of paths + necessary to contain all paths in the set. If /a/path/ and + /a/path/to/a/file.txt are both in the set, leave only the + shorter path.""" + + sep = os.path.sep + short_paths = set() + for path in sorted(paths, key=len): + should_add = any( + path.startswith(shortpath.rstrip("*")) and + path[len(shortpath.rstrip("*").rstrip(sep))] == sep + for shortpath in short_paths + ) + if not should_add: + short_paths.add(path) + return short_paths + + +def compress_for_output_listing(paths): + """Returns a tuple of 2 sets of which paths to display to user + + The first set contains paths that would be deleted. Files of a package + are not added and the top-level directory of the package has a '*' added + at the end - to signify that all it's contents are removed. + + The second set contains files that would have been skipped in the above + folders. + """ + + will_remove = list(paths) + will_skip = set() + + # Determine folders and files + folders = set() + files = set() + for path in will_remove: + if path.endswith(".pyc"): + continue + if path.endswith("__init__.py") or ".dist-info" in path: + folders.add(os.path.dirname(path)) + files.add(path) + + _normcased_files = set(map(os.path.normcase, files)) + + folders = compact(folders) + + # This walks the tree using os.walk to not miss extra folders + # that might get added. + for folder in folders: + for dirpath, _, dirfiles in os.walk(folder): + for fname in dirfiles: + if fname.endswith(".pyc"): + continue + + file_ = os.path.join(dirpath, fname) + if (os.path.isfile(file_) and + os.path.normcase(file_) not in _normcased_files): + # We are skipping this file. Add it to the set. + will_skip.add(file_) + + will_remove = files | { + os.path.join(folder, "*") for folder in folders + } + + return will_remove, will_skip + + +class UninstallPathSet(object): + """A set of file paths to be removed in the uninstallation of a + requirement.""" + def __init__(self, dist): + self.paths = set() + self._refuse = set() + self.pth = {} + self.dist = dist + self.save_dir = TempDirectory(kind="uninstall") + self._moved_paths = [] + + def _permitted(self, path): + """ + Return True if the given path is one we are permitted to + remove/modify, False otherwise. + + """ + return is_local(path) + + def add(self, path): + head, tail = os.path.split(path) + + # we normalize the head to resolve parent directory symlinks, but not + # the tail, since we only want to uninstall symlinks, not their targets + path = os.path.join(normalize_path(head), os.path.normcase(tail)) + + if not os.path.exists(path): + return + if self._permitted(path): + self.paths.add(path) + else: + self._refuse.add(path) + + # __pycache__ files can show up after 'installed-files.txt' is created, + # due to imports + if os.path.splitext(path)[1] == '.py' and uses_pycache: + self.add(cache_from_source(path)) + + def add_pth(self, pth_file, entry): + pth_file = normalize_path(pth_file) + if self._permitted(pth_file): + if pth_file not in self.pth: + self.pth[pth_file] = UninstallPthEntries(pth_file) + self.pth[pth_file].add(entry) + else: + self._refuse.add(pth_file) + + def _stash(self, path): + return os.path.join( + self.save_dir.path, os.path.splitdrive(path)[1].lstrip(os.path.sep) + ) + + def remove(self, auto_confirm=False, verbose=False): + """Remove paths in ``self.paths`` with confirmation (unless + ``auto_confirm`` is True).""" + + if not self.paths: + logger.info( + "Can't uninstall '%s'. No files were found to uninstall.", + self.dist.project_name, + ) + return + + dist_name_version = ( + self.dist.project_name + "-" + self.dist.version + ) + logger.info('Uninstalling %s:', dist_name_version) + + with indent_log(): + if auto_confirm or self._allowed_to_proceed(verbose): + self.save_dir.create() + + for path in sorted(compact(self.paths)): + new_path = self._stash(path) + logger.debug('Removing file or directory %s', path) + self._moved_paths.append(path) + renames(path, new_path) + for pth in self.pth.values(): + pth.remove() + + logger.info('Successfully uninstalled %s', dist_name_version) + + def _allowed_to_proceed(self, verbose): + """Display which files would be deleted and prompt for confirmation + """ + + def _display(msg, paths): + if not paths: + return + + logger.info(msg) + with indent_log(): + for path in sorted(compact(paths)): + logger.info(path) + + if not verbose: + will_remove, will_skip = compress_for_output_listing(self.paths) + else: + # In verbose mode, display all the files that are going to be + # deleted. + will_remove = list(self.paths) + will_skip = set() + + _display('Would remove:', will_remove) + _display('Would not remove (might be manually added):', will_skip) + _display('Would not remove (outside of prefix):', self._refuse) + + return ask('Proceed (y/n)? ', ('y', 'n')) == 'y' + + def rollback(self): + """Rollback the changes previously made by remove().""" + if self.save_dir.path is None: + logger.error( + "Can't roll back %s; was not uninstalled", + self.dist.project_name, + ) + return False + logger.info('Rolling back uninstall of %s', self.dist.project_name) + for path in self._moved_paths: + tmp_path = self._stash(path) + logger.debug('Replacing %s', path) + renames(tmp_path, path) + for pth in self.pth.values(): + pth.rollback() + + def commit(self): + """Remove temporary save dir: rollback will no longer be possible.""" + self.save_dir.cleanup() + self._moved_paths = [] + + @classmethod + def from_dist(cls, dist): + dist_path = normalize_path(dist.location) + if not dist_is_local(dist): + logger.info( + "Not uninstalling %s at %s, outside environment %s", + dist.key, + dist_path, + sys.prefix, + ) + return cls(dist) + + if dist_path in {p for p in {sysconfig.get_path("stdlib"), + sysconfig.get_path("platstdlib")} + if p}: + logger.info( + "Not uninstalling %s at %s, as it is in the standard library.", + dist.key, + dist_path, + ) + return cls(dist) + + paths_to_remove = cls(dist) + develop_egg_link = egg_link_path(dist) + develop_egg_link_egg_info = '{}.egg-info'.format( + pkg_resources.to_filename(dist.project_name)) + egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info) + # Special case for distutils installed package + distutils_egg_info = getattr(dist._provider, 'path', None) + + # Uninstall cases order do matter as in the case of 2 installs of the + # same package, pip needs to uninstall the currently detected version + if (egg_info_exists and dist.egg_info.endswith('.egg-info') and + not dist.egg_info.endswith(develop_egg_link_egg_info)): + # if dist.egg_info.endswith(develop_egg_link_egg_info), we + # are in fact in the develop_egg_link case + paths_to_remove.add(dist.egg_info) + if dist.has_metadata('installed-files.txt'): + for installed_file in dist.get_metadata( + 'installed-files.txt').splitlines(): + path = os.path.normpath( + os.path.join(dist.egg_info, installed_file) + ) + paths_to_remove.add(path) + # FIXME: need a test for this elif block + # occurs with --single-version-externally-managed/--record outside + # of pip + elif dist.has_metadata('top_level.txt'): + if dist.has_metadata('namespace_packages.txt'): + namespaces = dist.get_metadata('namespace_packages.txt') + else: + namespaces = [] + for top_level_pkg in [ + p for p + in dist.get_metadata('top_level.txt').splitlines() + if p and p not in namespaces]: + path = os.path.join(dist.location, top_level_pkg) + paths_to_remove.add(path) + paths_to_remove.add(path + '.py') + paths_to_remove.add(path + '.pyc') + paths_to_remove.add(path + '.pyo') + + elif distutils_egg_info: + raise UninstallationError( + "Cannot uninstall {!r}. It is a distutils installed project " + "and thus we cannot accurately determine which files belong " + "to it which would lead to only a partial uninstall.".format( + dist.project_name, + ) + ) + + elif dist.location.endswith('.egg'): + # package installed by easy_install + # We cannot match on dist.egg_name because it can slightly vary + # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg + paths_to_remove.add(dist.location) + easy_install_egg = os.path.split(dist.location)[1] + easy_install_pth = os.path.join(os.path.dirname(dist.location), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) + + elif egg_info_exists and dist.egg_info.endswith('.dist-info'): + for path in uninstallation_paths(dist): + paths_to_remove.add(path) + + elif develop_egg_link: + # develop egg + with open(develop_egg_link, 'r') as fh: + link_pointer = os.path.normcase(fh.readline().strip()) + assert (link_pointer == dist.location), ( + 'Egg-link %s does not match installed location of %s ' + '(at %s)' % (link_pointer, dist.project_name, dist.location) + ) + paths_to_remove.add(develop_egg_link) + easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, dist.location) + + else: + logger.debug( + 'Not sure how to uninstall: %s - Check: %s', + dist, dist.location, + ) + + # find distutils scripts= scripts + if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): + for script in dist.metadata_listdir('scripts'): + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + paths_to_remove.add(os.path.join(bin_dir, script)) + if WINDOWS: + paths_to_remove.add(os.path.join(bin_dir, script) + '.bat') + + # find console_scripts + _scripts_to_remove = [] + console_scripts = dist.get_entry_map(group='console_scripts') + for name in console_scripts.keys(): + _scripts_to_remove.extend(_script_names(dist, name, False)) + # find gui_scripts + gui_scripts = dist.get_entry_map(group='gui_scripts') + for name in gui_scripts.keys(): + _scripts_to_remove.extend(_script_names(dist, name, True)) + + for s in _scripts_to_remove: + paths_to_remove.add(s) + + return paths_to_remove + + +class UninstallPthEntries(object): + def __init__(self, pth_file): + if not os.path.isfile(pth_file): + raise UninstallationError( + "Cannot remove entries from nonexistent file %s" % pth_file + ) + self.file = pth_file + self.entries = set() + self._saved_lines = None + + def add(self, entry): + entry = os.path.normcase(entry) + # On Windows, os.path.normcase converts the entry to use + # backslashes. This is correct for entries that describe absolute + # paths outside of site-packages, but all the others use forward + # slashes. + if WINDOWS and not os.path.splitdrive(entry)[0]: + entry = entry.replace('\\', '/') + self.entries.add(entry) + + def remove(self): + logger.debug('Removing pth entries from %s:', self.file) + with open(self.file, 'rb') as fh: + # windows uses '\r\n' with py3k, but uses '\n' with py2.x + lines = fh.readlines() + self._saved_lines = lines + if any(b'\r\n' in line for line in lines): + endline = '\r\n' + else: + endline = '\n' + # handle missing trailing newline + if lines and not lines[-1].endswith(endline.encode("utf-8")): + lines[-1] = lines[-1] + endline.encode("utf-8") + for entry in self.entries: + try: + logger.debug('Removing entry: %s', entry) + lines.remove((entry + endline).encode("utf-8")) + except ValueError: + pass + with open(self.file, 'wb') as fh: + fh.writelines(lines) + + def rollback(self): + if self._saved_lines is None: + logger.error( + 'Cannot roll back changes to %s, none were made', self.file + ) + return False + logger.debug('Rolling %s back to previous state', self.file) + with open(self.file, 'wb') as fh: + fh.writelines(self._saved_lines) + return True diff --git a/venv/Lib/site-packages/pip/_internal/resolve.py b/venv/Lib/site-packages/pip/_internal/resolve.py new file mode 100644 index 0000000..2d9f1c5 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/resolve.py @@ -0,0 +1,353 @@ +"""Dependency Resolution + +The dependency resolution in pip is performed as follows: + +for top-level requirements: + a. only one spec allowed per project, regardless of conflicts or not. + otherwise a "double requirement" exception is raised + b. they override sub-dependency requirements. +for sub-dependencies + a. "first found, wins" (where the order is breadth first) +""" + +import logging +from collections import defaultdict +from itertools import chain + +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, DistributionNotFound, HashError, HashErrors, + UnsupportedPythonVersion, +) +from pip._internal.req.constructors import install_req_from_req +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import dist_in_usersite, ensure_dir +from pip._internal.utils.packaging import check_dist_requires_python + +logger = logging.getLogger(__name__) + + +class Resolver(object): + """Resolves which packages need to be installed/uninstalled to perform \ + the requested operation without breaking the requirements of any package. + """ + + _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + + def __init__(self, preparer, session, finder, wheel_cache, use_user_site, + ignore_dependencies, ignore_installed, ignore_requires_python, + force_reinstall, isolated, upgrade_strategy): + super(Resolver, self).__init__() + assert upgrade_strategy in self._allowed_strategies + + self.preparer = preparer + self.finder = finder + self.session = session + + # NOTE: This would eventually be replaced with a cache that can give + # information about both sdist and wheels transparently. + self.wheel_cache = wheel_cache + + self.require_hashes = None # This is set in resolve + + self.upgrade_strategy = upgrade_strategy + self.force_reinstall = force_reinstall + self.isolated = isolated + self.ignore_dependencies = ignore_dependencies + self.ignore_installed = ignore_installed + self.ignore_requires_python = ignore_requires_python + self.use_user_site = use_user_site + + self._discovered_dependencies = defaultdict(list) + + def resolve(self, requirement_set): + """Resolve what operations need to be done + + As a side-effect of this method, the packages (and their dependencies) + are downloaded, unpacked and prepared for installation. This + preparation is done by ``pip.operations.prepare``. + + Once PyPI has static dependency metadata available, it would be + possible to move the preparation to become a step separated from + dependency resolution. + """ + # make the wheelhouse + if self.preparer.wheel_download_dir: + ensure_dir(self.preparer.wheel_download_dir) + + # If any top-level requirement has a hash specified, enter + # hash-checking mode, which requires hashes from all. + root_reqs = ( + requirement_set.unnamed_requirements + + list(requirement_set.requirements.values()) + ) + self.require_hashes = ( + requirement_set.require_hashes or + any(req.has_hash_options for req in root_reqs) + ) + + # Display where finder is looking for packages + locations = self.finder.get_formatted_locations() + if locations: + logger.info(locations) + + # Actually prepare the files, and collect any exceptions. Most hash + # exceptions cannot be checked ahead of time, because + # req.populate_link() needs to be called before we can make decisions + # based on link type. + discovered_reqs = [] + hash_errors = HashErrors() + for req in chain(root_reqs, discovered_reqs): + try: + discovered_reqs.extend( + self._resolve_one(requirement_set, req) + ) + except HashError as exc: + exc.req = req + hash_errors.append(exc) + + if hash_errors: + raise hash_errors + + def _is_upgrade_allowed(self, req): + if self.upgrade_strategy == "to-satisfy-only": + return False + elif self.upgrade_strategy == "eager": + return True + else: + assert self.upgrade_strategy == "only-if-needed" + return req.is_direct + + def _set_req_to_reinstall(self, req): + """ + Set a requirement to be installed. + """ + # Don't uninstall the conflict if doing a user install and the + # conflict is not a user install. + if not self.use_user_site or dist_in_usersite(req.satisfied_by): + req.conflicts_with = req.satisfied_by + req.satisfied_by = None + + # XXX: Stop passing requirement_set for options + def _check_skip_installed(self, req_to_install): + """Check if req_to_install should be skipped. + + This will check if the req is installed, and whether we should upgrade + or reinstall it, taking into account all the relevant user options. + + After calling this req_to_install will only have satisfied_by set to + None if the req_to_install is to be upgraded/reinstalled etc. Any + other value will be a dist recording the current thing installed that + satisfies the requirement. + + Note that for vcs urls and the like we can't assess skipping in this + routine - we simply identify that we need to pull the thing down, + then later on it is pulled down and introspected to assess upgrade/ + reinstalls etc. + + :return: A text reason for why it was skipped, or None. + """ + if self.ignore_installed: + return None + + req_to_install.check_if_exists(self.use_user_site) + if not req_to_install.satisfied_by: + return None + + if self.force_reinstall: + self._set_req_to_reinstall(req_to_install) + return None + + if not self._is_upgrade_allowed(req_to_install): + if self.upgrade_strategy == "only-if-needed": + return 'already satisfied, skipping upgrade' + return 'already satisfied' + + # Check for the possibility of an upgrade. For link-based + # requirements we have to pull the tree down and inspect to assess + # the version #, so it's handled way down. + if not req_to_install.link: + try: + self.finder.find_requirement(req_to_install, upgrade=True) + except BestVersionAlreadyInstalled: + # Then the best version is installed. + return 'already up-to-date' + except DistributionNotFound: + # No distribution found, so we squash the error. It will + # be raised later when we re-try later to do the install. + # Why don't we just raise here? + pass + + self._set_req_to_reinstall(req_to_install) + return None + + def _get_abstract_dist_for(self, req): + """Takes a InstallRequirement and returns a single AbstractDist \ + representing a prepared variant of the same. + """ + assert self.require_hashes is not None, ( + "require_hashes should have been set in Resolver.resolve()" + ) + + if req.editable: + return self.preparer.prepare_editable_requirement( + req, self.require_hashes, self.use_user_site, self.finder, + ) + + # satisfied_by is only evaluated by calling _check_skip_installed, + # so it must be None here. + assert req.satisfied_by is None + skip_reason = self._check_skip_installed(req) + + if req.satisfied_by: + return self.preparer.prepare_installed_requirement( + req, self.require_hashes, skip_reason + ) + + upgrade_allowed = self._is_upgrade_allowed(req) + abstract_dist = self.preparer.prepare_linked_requirement( + req, self.session, self.finder, upgrade_allowed, + self.require_hashes + ) + + # NOTE + # The following portion is for determining if a certain package is + # going to be re-installed/upgraded or not and reporting to the user. + # This should probably get cleaned up in a future refactor. + + # req.req is only avail after unpack for URL + # pkgs repeat check_if_exists to uninstall-on-upgrade + # (#14) + if not self.ignore_installed: + req.check_if_exists(self.use_user_site) + + if req.satisfied_by: + should_modify = ( + self.upgrade_strategy != "to-satisfy-only" or + self.force_reinstall or + self.ignore_installed or + req.link.scheme == 'file' + ) + if should_modify: + self._set_req_to_reinstall(req) + else: + logger.info( + 'Requirement already satisfied (use --upgrade to upgrade):' + ' %s', req, + ) + + return abstract_dist + + def _resolve_one(self, requirement_set, req_to_install): + """Prepare a single requirements file. + + :return: A list of additional InstallRequirements to also install. + """ + # Tell user what we are doing for this requirement: + # obtain (editable), skipping, processing (local url), collecting + # (remote url or package name) + if req_to_install.constraint or req_to_install.prepared: + return [] + + req_to_install.prepared = True + + # register tmp src for cleanup in case something goes wrong + requirement_set.reqs_to_cleanup.append(req_to_install) + + abstract_dist = self._get_abstract_dist_for(req_to_install) + + # Parse and return dependencies + dist = abstract_dist.dist(self.finder) + try: + check_dist_requires_python(dist) + except UnsupportedPythonVersion as err: + if self.ignore_requires_python: + logger.warning(err.args[0]) + else: + raise + + more_reqs = [] + + def add_req(subreq, extras_requested): + sub_install_req = install_req_from_req( + str(subreq), + req_to_install, + isolated=self.isolated, + wheel_cache=self.wheel_cache, + ) + parent_req_name = req_to_install.name + to_scan_again, add_to_parent = requirement_set.add_requirement( + sub_install_req, + parent_req_name=parent_req_name, + extras_requested=extras_requested, + ) + if parent_req_name and add_to_parent: + self._discovered_dependencies[parent_req_name].append( + add_to_parent + ) + more_reqs.extend(to_scan_again) + + with indent_log(): + # We add req_to_install before its dependencies, so that we + # can refer to it when adding dependencies. + if not requirement_set.has_requirement(req_to_install.name): + # 'unnamed' requirements will get added here + req_to_install.is_direct = True + requirement_set.add_requirement( + req_to_install, parent_req_name=None, + ) + + if not self.ignore_dependencies: + if req_to_install.extras: + logger.debug( + "Installing extra requirements: %r", + ','.join(req_to_install.extras), + ) + missing_requested = sorted( + set(req_to_install.extras) - set(dist.extras) + ) + for missing in missing_requested: + logger.warning( + '%s does not provide the extra \'%s\'', + dist, missing + ) + + available_requested = sorted( + set(dist.extras) & set(req_to_install.extras) + ) + for subreq in dist.requires(available_requested): + add_req(subreq, extras_requested=available_requested) + + if not req_to_install.editable and not req_to_install.satisfied_by: + # XXX: --no-install leads this to report 'Successfully + # downloaded' for only non-editable reqs, even though we took + # action on them. + requirement_set.successfully_downloaded.append(req_to_install) + + return more_reqs + + def get_installation_order(self, req_set): + """Create the installation order. + + The installation order is topological - requirements are installed + before the requiring thing. We break cycles at an arbitrary point, + and make no other guarantees. + """ + # The current implementation, which we may change at any point + # installs the user specified things in the order given, except when + # dependencies must come earlier to achieve topological order. + order = [] + ordered_reqs = set() + + def schedule(req): + if req.satisfied_by or req in ordered_reqs: + return + if req.constraint: + return + ordered_reqs.add(req) + for dep in self._discovered_dependencies[req.name]: + schedule(dep) + order.append(req) + + for install_req in req_set.requirements.values(): + schedule(install_req) + return order diff --git a/venv/Lib/site-packages/pip/_internal/utils/__init__.py b/venv/Lib/site-packages/pip/_internal/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pip/_internal/utils/appdirs.py b/venv/Lib/site-packages/pip/_internal/utils/appdirs.py new file mode 100644 index 0000000..cc96f98 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/appdirs.py @@ -0,0 +1,258 @@ +""" +This code was taken from https://github.com/ActiveState/appdirs and modified +to suit our purposes. +""" +from __future__ import absolute_import + +import os +import sys + +from pip._vendor.six import PY2, text_type + +from pip._internal.utils.compat import WINDOWS, expanduser + + +def user_cache_dir(appname): + r""" + Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + + Typical user cache directories are: + macOS: ~/Library/Caches/<AppName> + Unix: ~/.cache/<AppName> (XDG default) + Windows: C:\Users\<username>\AppData\Local\<AppName>\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go + in the `CSIDL_LOCAL_APPDATA` directory. This is identical to the + non-roaming app data dir (the default returned by `user_data_dir`). Apps + typically put cache data somewhere *under* the given dir here. Some + examples: + ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache + ...\Acme\SuperApp\Cache\1.0 + + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + """ + if WINDOWS: + # Get the base path + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + + # When using Python 2, return paths as bytes on Windows like we do on + # other operating systems. See helper function docs for more details. + if PY2 and isinstance(path, text_type): + path = _win_path_to_bytes(path) + + # Add our app name and Cache directory to it + path = os.path.join(path, appname, "Cache") + elif sys.platform == "darwin": + # Get the base path + path = expanduser("~/Library/Caches") + + # Add our app name to it + path = os.path.join(path, appname) + else: + # Get the base path + path = os.getenv("XDG_CACHE_HOME", expanduser("~/.cache")) + + # Add our app name to it + path = os.path.join(path, appname) + + return path + + +def user_data_dir(appname, roaming=False): + r""" + Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user data directories are: + macOS: ~/Library/Application Support/<AppName> + if it exists, else ~/.config/<AppName> + Unix: ~/.local/share/<AppName> # or in + $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\<username>\ ... + ...Application Data\<AppName> + Win XP (roaming): C:\Documents and Settings\<username>\Local ... + ...Settings\Application Data\<AppName> + Win 7 (not roaming): C:\\Users\<username>\AppData\Local\<AppName> + Win 7 (roaming): C:\\Users\<username>\AppData\Roaming\<AppName> + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/<AppName>". + """ + if WINDOWS: + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.join(os.path.normpath(_get_win_folder(const)), appname) + elif sys.platform == "darwin": + path = os.path.join( + expanduser('~/Library/Application Support/'), + appname, + ) if os.path.isdir(os.path.join( + expanduser('~/Library/Application Support/'), + appname, + ) + ) else os.path.join( + expanduser('~/.config/'), + appname, + ) + else: + path = os.path.join( + os.getenv('XDG_DATA_HOME', expanduser("~/.local/share")), + appname, + ) + + return path + + +def user_config_dir(appname, roaming=True): + """Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "roaming" (boolean, default True) can be set False to not use the + Windows roaming appdata directory. That means that for users on a + Windows network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user data directories are: + macOS: same as user_data_dir + Unix: ~/.config/<AppName> + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/<AppName>". + """ + if WINDOWS: + path = user_data_dir(appname, roaming=roaming) + elif sys.platform == "darwin": + path = user_data_dir(appname) + else: + path = os.getenv('XDG_CONFIG_HOME', expanduser("~/.config")) + path = os.path.join(path, appname) + + return path + + +# for the discussion regarding site_config_dirs locations +# see <https://github.com/pypa/pip/issues/1733> +def site_config_dirs(appname): + r"""Return a list of potential user-shared config dirs for this application. + + "appname" is the name of application. + + Typical user config directories are: + macOS: /Library/Application Support/<AppName>/ + Unix: /etc or $XDG_CONFIG_DIRS[i]/<AppName>/ for each value in + $XDG_CONFIG_DIRS + Win XP: C:\Documents and Settings\All Users\Application ... + ...Data\<AppName>\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory + on Vista.) + Win 7: Hidden, but writeable on Win 7: + C:\ProgramData\<AppName>\ + """ + if WINDOWS: + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + pathlist = [os.path.join(path, appname)] + elif sys.platform == 'darwin': + pathlist = [os.path.join('/Library/Application Support', appname)] + else: + # try looking in $XDG_CONFIG_DIRS + xdg_config_dirs = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + if xdg_config_dirs: + pathlist = [ + os.path.join(expanduser(x), appname) + for x in xdg_config_dirs.split(os.pathsep) + ] + else: + pathlist = [] + + # always look in /etc directly as well + pathlist.append('/etc') + + return pathlist + + +# -- Windows support functions -- + +def _get_win_folder_from_registry(csidl_name): + """ + This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" + ) + directory, _type = _winreg.QueryValueEx(key, shell_folder_name) + return directory + + +def _get_win_folder_with_ctypes(csidl_name): + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + + +if WINDOWS: + try: + import ctypes + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +def _win_path_to_bytes(path): + """Encode Windows paths to bytes. Only used on Python 2. + + Motivation is to be consistent with other operating systems where paths + are also returned as bytes. This avoids problems mixing bytes and Unicode + elsewhere in the codebase. For more details and discussion see + <https://github.com/pypa/pip/issues/3463>. + + If encoding using ASCII and MBCS fails, return the original Unicode path. + """ + for encoding in ('ASCII', 'MBCS'): + try: + return path.encode(encoding) + except (UnicodeEncodeError, LookupError): + pass + return path diff --git a/venv/Lib/site-packages/pip/_internal/utils/compat.py b/venv/Lib/site-packages/pip/_internal/utils/compat.py new file mode 100644 index 0000000..3114f2d --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/compat.py @@ -0,0 +1,248 @@ +"""Stuff that differs in different Python versions and platform +distributions.""" +from __future__ import absolute_import, division + +import codecs +import locale +import logging +import os +import shutil +import sys + +from pip._vendor.six import text_type + +try: + import ipaddress +except ImportError: + try: + from pip._vendor import ipaddress # type: ignore + except ImportError: + import ipaddr as ipaddress # type: ignore + ipaddress.ip_address = ipaddress.IPAddress + ipaddress.ip_network = ipaddress.IPNetwork + + +__all__ = [ + "ipaddress", "uses_pycache", "console_to_str", "native_str", + "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", "get_terminal_size", + "get_extension_suffixes", +] + + +logger = logging.getLogger(__name__) + +if sys.version_info >= (3, 4): + uses_pycache = True + from importlib.util import cache_from_source +else: + import imp + + try: + cache_from_source = imp.cache_from_source # type: ignore + except AttributeError: + # does not use __pycache__ + cache_from_source = None + + uses_pycache = cache_from_source is not None + + +if sys.version_info >= (3, 5): + backslashreplace_decode = "backslashreplace" +else: + # In version 3.4 and older, backslashreplace exists + # but does not support use for decoding. + # We implement our own replace handler for this + # situation, so that we can consistently use + # backslash replacement for all versions. + def backslashreplace_decode_fn(err): + raw_bytes = (err.object[i] for i in range(err.start, err.end)) + if sys.version_info[0] == 2: + # Python 2 gave us characters - convert to numeric bytes + raw_bytes = (ord(b) for b in raw_bytes) + return u"".join(u"\\x%x" % c for c in raw_bytes), err.end + codecs.register_error( + "backslashreplace_decode", + backslashreplace_decode_fn, + ) + backslashreplace_decode = "backslashreplace_decode" + + +def console_to_str(data): + """Return a string, safe for output, of subprocess output. + + We assume the data is in the locale preferred encoding. + If it won't decode properly, we warn the user but decode as + best we can. + + We also ensure that the output can be safely written to + standard output without encoding errors. + """ + + # First, get the encoding we assume. This is the preferred + # encoding for the locale, unless that is not found, or + # it is ASCII, in which case assume UTF-8 + encoding = locale.getpreferredencoding() + if (not encoding) or codecs.lookup(encoding).name == "ascii": + encoding = "utf-8" + + # Now try to decode the data - if we fail, warn the user and + # decode with replacement. + try: + s = data.decode(encoding) + except UnicodeDecodeError: + logger.warning( + "Subprocess output does not appear to be encoded as %s", + encoding, + ) + s = data.decode(encoding, errors=backslashreplace_decode) + + # Make sure we can print the output, by encoding it to the output + # encoding with replacement of unencodable characters, and then + # decoding again. + # We use stderr's encoding because it's less likely to be + # redirected and if we don't find an encoding we skip this + # step (on the assumption that output is wrapped by something + # that won't fail). + # The double getattr is to deal with the possibility that we're + # being called in a situation where sys.__stderr__ doesn't exist, + # or doesn't have an encoding attribute. Neither of these cases + # should occur in normal pip use, but there's no harm in checking + # in case people use pip in (unsupported) unusual situations. + output_encoding = getattr(getattr(sys, "__stderr__", None), + "encoding", None) + + if output_encoding: + s = s.encode(output_encoding, errors="backslashreplace") + s = s.decode(output_encoding) + + return s + + +if sys.version_info >= (3,): + def native_str(s, replace=False): + if isinstance(s, bytes): + return s.decode('utf-8', 'replace' if replace else 'strict') + return s + +else: + def native_str(s, replace=False): + # Replace is ignored -- unicode to UTF-8 can't fail + if isinstance(s, text_type): + return s.encode('utf-8') + return s + + +def get_path_uid(path): + """ + Return path's uid. + + Does not follow symlinks: + https://github.com/pypa/pip/pull/935#discussion_r5307003 + + Placed this function in compat due to differences on AIX and + Jython, that should eventually go away. + + :raises OSError: When path is a symlink or can't be read. + """ + if hasattr(os, 'O_NOFOLLOW'): + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + file_uid = os.fstat(fd).st_uid + os.close(fd) + else: # AIX and Jython + # WARNING: time of check vulnerability, but best we can do w/o NOFOLLOW + if not os.path.islink(path): + # older versions of Jython don't have `os.fstat` + file_uid = os.stat(path).st_uid + else: + # raise OSError for parity with os.O_NOFOLLOW above + raise OSError( + "%s is a symlink; Will not return uid for symlinks" % path + ) + return file_uid + + +if sys.version_info >= (3, 4): + from importlib.machinery import EXTENSION_SUFFIXES + + def get_extension_suffixes(): + return EXTENSION_SUFFIXES +else: + from imp import get_suffixes + + def get_extension_suffixes(): + return [suffix[0] for suffix in get_suffixes()] + + +def expanduser(path): + """ + Expand ~ and ~user constructions. + + Includes a workaround for https://bugs.python.org/issue14768 + """ + expanded = os.path.expanduser(path) + if path.startswith('~/') and expanded.startswith('//'): + expanded = expanded[1:] + return expanded + + +# packages in the stdlib that may have installation metadata, but should not be +# considered 'installed'. this theoretically could be determined based on +# dist.location (py27:`sysconfig.get_paths()['stdlib']`, +# py26:sysconfig.get_config_vars('LIBDEST')), but fear platform variation may +# make this ineffective, so hard-coding +stdlib_pkgs = {"python", "wsgiref", "argparse"} + + +# windows detection, covers cpython and ironpython +WINDOWS = (sys.platform.startswith("win") or + (sys.platform == 'cli' and os.name == 'nt')) + + +def samefile(file1, file2): + """Provide an alternative for os.path.samefile on Windows/Python2""" + if hasattr(os.path, 'samefile'): + return os.path.samefile(file1, file2) + else: + path1 = os.path.normcase(os.path.abspath(file1)) + path2 = os.path.normcase(os.path.abspath(file2)) + return path1 == path2 + + +if hasattr(shutil, 'get_terminal_size'): + def get_terminal_size(): + """ + Returns a tuple (x, y) representing the width(x) and the height(y) + in characters of the terminal window. + """ + return tuple(shutil.get_terminal_size()) +else: + def get_terminal_size(): + """ + Returns a tuple (x, y) representing the width(x) and the height(y) + in characters of the terminal window. + """ + def ioctl_GWINSZ(fd): + try: + import fcntl + import termios + import struct + cr = struct.unpack_from( + 'hh', + fcntl.ioctl(fd, termios.TIOCGWINSZ, '12345678') + ) + except Exception: + return None + if cr == (0, 0): + return None + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except Exception: + pass + if not cr: + cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) + return int(cr[1]), int(cr[0]) diff --git a/venv/Lib/site-packages/pip/_internal/utils/deprecation.py b/venv/Lib/site-packages/pip/_internal/utils/deprecation.py new file mode 100644 index 0000000..bd744cf --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/deprecation.py @@ -0,0 +1,89 @@ +""" +A module that implements tooling to enable easy warnings about deprecations. +""" +from __future__ import absolute_import + +import logging +import warnings + +from pip._vendor.packaging.version import parse + +from pip import __version__ as current_version +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Optional # noqa: F401 + + +class PipDeprecationWarning(Warning): + pass + + +_original_showwarning = None # type: Any + + +# Warnings <-> Logging Integration +def _showwarning(message, category, filename, lineno, file=None, line=None): + if file is not None: + if _original_showwarning is not None: + _original_showwarning( + message, category, filename, lineno, file, line, + ) + elif issubclass(category, PipDeprecationWarning): + # We use a specially named logger which will handle all of the + # deprecation messages for pip. + logger = logging.getLogger("pip._internal.deprecations") + logger.warning(message) + else: + _original_showwarning( + message, category, filename, lineno, file, line, + ) + + +def install_warning_logger(): + # Enable our Deprecation Warnings + warnings.simplefilter("default", PipDeprecationWarning, append=True) + + global _original_showwarning + + if _original_showwarning is None: + _original_showwarning = warnings.showwarning + warnings.showwarning = _showwarning + + +def deprecated(reason, replacement, gone_in, issue=None): + # type: (str, Optional[str], Optional[str], Optional[int]) -> None + """Helper to deprecate existing functionality. + + reason: + Textual reason shown to the user about why this functionality has + been deprecated. + replacement: + Textual suggestion shown to the user about what alternative + functionality they can use. + gone_in: + The version of pip does this functionality should get removed in. + Raises errors if pip's current version is greater than or equal to + this. + issue: + Issue number on the tracker that would serve as a useful place for + users to find related discussion and provide feedback. + + Always pass replacement, gone_in and issue as keyword arguments for clarity + at the call site. + """ + + # Construct a nice message. + # This is purposely eagerly formatted as we want it to appear as if someone + # typed this entire message out. + message = "DEPRECATION: " + reason + if replacement is not None: + message += " A possible replacement is {}.".format(replacement) + if issue is not None: + url = "https://github.com/pypa/pip/issues/" + str(issue) + message += " You can find discussion regarding this at {}.".format(url) + + # Raise as an error if it has to be removed. + if gone_in is not None and parse(current_version) >= parse(gone_in): + raise PipDeprecationWarning(message) + warnings.warn(message, category=PipDeprecationWarning, stacklevel=2) diff --git a/venv/Lib/site-packages/pip/_internal/utils/encoding.py b/venv/Lib/site-packages/pip/_internal/utils/encoding.py new file mode 100644 index 0000000..56f6036 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/encoding.py @@ -0,0 +1,33 @@ +import codecs +import locale +import re +import sys + +BOMS = [ + (codecs.BOM_UTF8, 'utf8'), + (codecs.BOM_UTF16, 'utf16'), + (codecs.BOM_UTF16_BE, 'utf16-be'), + (codecs.BOM_UTF16_LE, 'utf16-le'), + (codecs.BOM_UTF32, 'utf32'), + (codecs.BOM_UTF32_BE, 'utf32-be'), + (codecs.BOM_UTF32_LE, 'utf32-le'), +] + +ENCODING_RE = re.compile(br'coding[:=]\s*([-\w.]+)') + + +def auto_decode(data): + """Check a bytes string for a BOM to correctly detect the encoding + + Fallback to locale.getpreferredencoding(False) like open() on Python3""" + for bom, encoding in BOMS: + if data.startswith(bom): + return data[len(bom):].decode(encoding) + # Lets check the first two lines as in PEP263 + for line in data.split(b'\n')[:2]: + if line[0:1] == b'#' and ENCODING_RE.search(line): + encoding = ENCODING_RE.search(line).groups()[0].decode('ascii') + return data.decode(encoding) + return data.decode( + locale.getpreferredencoding(False) or sys.getdefaultencoding(), + ) diff --git a/venv/Lib/site-packages/pip/_internal/utils/filesystem.py b/venv/Lib/site-packages/pip/_internal/utils/filesystem.py new file mode 100644 index 0000000..1e9cebd --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/filesystem.py @@ -0,0 +1,28 @@ +import os +import os.path + +from pip._internal.utils.compat import get_path_uid + + +def check_path_owner(path): + # If we don't have a way to check the effective uid of this process, then + # we'll just assume that we own the directory. + if not hasattr(os, "geteuid"): + return True + + previous = None + while path != previous: + if os.path.lexists(path): + # Check if path is writable by current user. + if os.geteuid() == 0: + # Special handling for root user in order to handle properly + # cases where users use sudo without -H flag. + try: + path_uid = get_path_uid(path) + except OSError: + return False + return path_uid == 0 + else: + return os.access(path, os.W_OK) + else: + previous, path = path, os.path.dirname(path) diff --git a/venv/Lib/site-packages/pip/_internal/utils/glibc.py b/venv/Lib/site-packages/pip/_internal/utils/glibc.py new file mode 100644 index 0000000..ebcfc5b --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/glibc.py @@ -0,0 +1,84 @@ +from __future__ import absolute_import + +import ctypes +import re +import warnings + + +def glibc_version_string(): + "Returns glibc version string, or None if not using glibc." + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + process_namespace = ctypes.CDLL(None) + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +# Separated out from have_compatible_glibc for easier unit testing +def check_glibc_version(version_str, required_major, minimum_minor): + # Parse string and check against requested version. + # + # We use a regexp instead of str.split because we want to discard any + # random junk that might come after the minor version -- this might happen + # in patched/forked versions of glibc (e.g. Linaro's version of glibc + # uses version strings like "2.20-2014.11"). See gh-3588. + m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str) + if not m: + warnings.warn("Expected glibc version with 2 components major.minor," + " got: %s" % version_str, RuntimeWarning) + return False + return (int(m.group("major")) == required_major and + int(m.group("minor")) >= minimum_minor) + + +def have_compatible_glibc(required_major, minimum_minor): + version_str = glibc_version_string() + if version_str is None: + return False + return check_glibc_version(version_str, required_major, minimum_minor) + + +# platform.libc_ver regularly returns completely nonsensical glibc +# versions. E.g. on my computer, platform says: +# +# ~$ python2.7 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.7') +# ~$ python3.5 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.9') +# +# But the truth is: +# +# ~$ ldd --version +# ldd (Debian GLIBC 2.22-11) 2.22 +# +# This is unfortunate, because it means that the linehaul data on libc +# versions that was generated by pip 8.1.2 and earlier is useless and +# misleading. Solution: instead of using platform, use our code that actually +# works. +def libc_ver(): + """Try to determine the glibc version + + Returns a tuple of strings (lib, version) which default to empty strings + in case the lookup fails. + """ + glibc_version = glibc_version_string() + if glibc_version is None: + return ("", "") + else: + return ("glibc", glibc_version) diff --git a/venv/Lib/site-packages/pip/_internal/utils/hashes.py b/venv/Lib/site-packages/pip/_internal/utils/hashes.py new file mode 100644 index 0000000..8b909ba --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/hashes.py @@ -0,0 +1,94 @@ +from __future__ import absolute_import + +import hashlib + +from pip._vendor.six import iteritems, iterkeys, itervalues + +from pip._internal.exceptions import ( + HashMismatch, HashMissing, InstallationError, +) +from pip._internal.utils.misc import read_chunks + +# The recommended hash algo of the moment. Change this whenever the state of +# the art changes; it won't hurt backward compatibility. +FAVORITE_HASH = 'sha256' + + +# Names of hashlib algorithms allowed by the --hash option and ``pip hash`` +# Currently, those are the ones at least as collision-resistant as sha256. +STRONG_HASHES = ['sha256', 'sha384', 'sha512'] + + +class Hashes(object): + """A wrapper that builds multiple hashes at once and checks them against + known-good values + + """ + def __init__(self, hashes=None): + """ + :param hashes: A dict of algorithm names pointing to lists of allowed + hex digests + """ + self._allowed = {} if hashes is None else hashes + + def check_against_chunks(self, chunks): + """Check good hashes against ones built from iterable of chunks of + data. + + Raise HashMismatch if none match. + + """ + gots = {} + for hash_name in iterkeys(self._allowed): + try: + gots[hash_name] = hashlib.new(hash_name) + except (ValueError, TypeError): + raise InstallationError('Unknown hash name: %s' % hash_name) + + for chunk in chunks: + for hash in itervalues(gots): + hash.update(chunk) + + for hash_name, got in iteritems(gots): + if got.hexdigest() in self._allowed[hash_name]: + return + self._raise(gots) + + def _raise(self, gots): + raise HashMismatch(self._allowed, gots) + + def check_against_file(self, file): + """Check good hashes against a file-like object + + Raise HashMismatch if none match. + + """ + return self.check_against_chunks(read_chunks(file)) + + def check_against_path(self, path): + with open(path, 'rb') as file: + return self.check_against_file(file) + + def __nonzero__(self): + """Return whether I know any known-good hashes.""" + return bool(self._allowed) + + def __bool__(self): + return self.__nonzero__() + + +class MissingHashes(Hashes): + """A workalike for Hashes used when we're missing a hash for a requirement + + It computes the actual hash of the requirement and raises a HashMissing + exception showing it to the user. + + """ + def __init__(self): + """Don't offer the ``hashes`` kwarg.""" + # Pass our favorite hash in to generate a "gotten hash". With the + # empty list, it will never match, so an error will always raise. + super(MissingHashes, self).__init__(hashes={FAVORITE_HASH: []}) + + def _raise(self, gots): + raise HashMissing(gots[FAVORITE_HASH].hexdigest()) diff --git a/venv/Lib/site-packages/pip/_internal/utils/logging.py b/venv/Lib/site-packages/pip/_internal/utils/logging.py new file mode 100644 index 0000000..d9b9541 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/logging.py @@ -0,0 +1,225 @@ +from __future__ import absolute_import + +import contextlib +import logging +import logging.handlers +import os + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.misc import ensure_dir + +try: + import threading +except ImportError: + import dummy_threading as threading # type: ignore + + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + + +_log_state = threading.local() +_log_state.indentation = 0 + + +@contextlib.contextmanager +def indent_log(num=2): + """ + A context manager which will cause the log output to be indented for any + log messages emitted inside it. + """ + _log_state.indentation += num + try: + yield + finally: + _log_state.indentation -= num + + +def get_indentation(): + return getattr(_log_state, 'indentation', 0) + + +class IndentingFormatter(logging.Formatter): + + def format(self, record): + """ + Calls the standard formatter, but will indent all of the log messages + by our current indentation level. + """ + formatted = logging.Formatter.format(self, record) + formatted = "".join([ + (" " * get_indentation()) + line + for line in formatted.splitlines(True) + ]) + return formatted + + +def _color_wrap(*colors): + def wrapped(inp): + return "".join(list(colors) + [inp, colorama.Style.RESET_ALL]) + return wrapped + + +class ColorizedStreamHandler(logging.StreamHandler): + + # Don't build up a list of colors if we don't have colorama + if colorama: + COLORS = [ + # This needs to be in order from highest logging level to lowest. + (logging.ERROR, _color_wrap(colorama.Fore.RED)), + (logging.WARNING, _color_wrap(colorama.Fore.YELLOW)), + ] + else: + COLORS = [] + + def __init__(self, stream=None, no_color=None): + logging.StreamHandler.__init__(self, stream) + self._no_color = no_color + + if WINDOWS and colorama: + self.stream = colorama.AnsiToWin32(self.stream) + + def should_color(self): + # Don't colorize things if we do not have colorama or if told not to + if not colorama or self._no_color: + return False + + real_stream = ( + self.stream if not isinstance(self.stream, colorama.AnsiToWin32) + else self.stream.wrapped + ) + + # If the stream is a tty we should color it + if hasattr(real_stream, "isatty") and real_stream.isatty(): + return True + + # If we have an ANSI term we should color it + if os.environ.get("TERM") == "ANSI": + return True + + # If anything else we should not color it + return False + + def format(self, record): + msg = logging.StreamHandler.format(self, record) + + if self.should_color(): + for level, color in self.COLORS: + if record.levelno >= level: + msg = color(msg) + break + + return msg + + +class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler): + + def _open(self): + ensure_dir(os.path.dirname(self.baseFilename)) + return logging.handlers.RotatingFileHandler._open(self) + + +class MaxLevelFilter(logging.Filter): + + def __init__(self, level): + self.level = level + + def filter(self, record): + return record.levelno < self.level + + +def setup_logging(verbosity, no_color, user_log_file): + """Configures and sets up all of the logging + """ + + # Determine the level to be logging at. + if verbosity >= 1: + level = "DEBUG" + elif verbosity == -1: + level = "WARNING" + elif verbosity == -2: + level = "ERROR" + elif verbosity <= -3: + level = "CRITICAL" + else: + level = "INFO" + + # The "root" logger should match the "console" level *unless* we also need + # to log to a user log file. + include_user_log = user_log_file is not None + if include_user_log: + additional_log_file = user_log_file + root_level = "DEBUG" + else: + additional_log_file = "/dev/null" + root_level = level + + # Disable any logging besides WARNING unless we have DEBUG level logging + # enabled for vendored libraries. + vendored_log_level = "WARNING" if level in ["INFO", "ERROR"] else "DEBUG" + + # Shorthands for clarity + log_streams = { + "stdout": "ext://sys.stdout", + "stderr": "ext://sys.stderr", + } + handler_classes = { + "stream": "pip._internal.utils.logging.ColorizedStreamHandler", + "file": "pip._internal.utils.logging.BetterRotatingFileHandler", + } + + logging.config.dictConfig({ + "version": 1, + "disable_existing_loggers": False, + "filters": { + "exclude_warnings": { + "()": "pip._internal.utils.logging.MaxLevelFilter", + "level": logging.WARNING, + }, + }, + "formatters": { + "indent": { + "()": IndentingFormatter, + "format": "%(message)s", + }, + }, + "handlers": { + "console": { + "level": level, + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stdout"], + "filters": ["exclude_warnings"], + "formatter": "indent", + }, + "console_errors": { + "level": "WARNING", + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stderr"], + "formatter": "indent", + }, + "user_log": { + "level": "DEBUG", + "class": handler_classes["file"], + "filename": additional_log_file, + "delay": True, + "formatter": "indent", + }, + }, + "root": { + "level": root_level, + "handlers": ["console", "console_errors"] + ( + ["user_log"] if include_user_log else [] + ), + }, + "loggers": { + "pip._vendor": { + "level": vendored_log_level + } + }, + }) diff --git a/venv/Lib/site-packages/pip/_internal/utils/misc.py b/venv/Lib/site-packages/pip/_internal/utils/misc.py new file mode 100644 index 0000000..84a421f --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/misc.py @@ -0,0 +1,940 @@ +from __future__ import absolute_import + +import contextlib +import errno +import io +import locale +# we have a submodule named 'logging' which would shadow this if we used the +# regular name: +import logging as std_logging +import os +import posixpath +import re +import shutil +import stat +import subprocess +import sys +import tarfile +import zipfile +from collections import deque + +from pip._vendor import pkg_resources +# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import. +from pip._vendor.retrying import retry # type: ignore +from pip._vendor.six import PY2 +from pip._vendor.six.moves import input +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.exceptions import CommandError, InstallationError +from pip._internal.locations import ( + running_under_virtualenv, site_packages, user_site, virtualenv_no_global, + write_delete_marker_file, +) +from pip._internal.utils.compat import ( + WINDOWS, console_to_str, expanduser, stdlib_pkgs, +) + +if PY2: + from io import BytesIO as StringIO +else: + from io import StringIO + +__all__ = ['rmtree', 'display_path', 'backup_dir', + 'ask', 'splitext', + 'format_size', 'is_installable_dir', + 'is_svn_page', 'file_contents', + 'split_leading_dir', 'has_leading_dir', + 'normalize_path', + 'renames', 'get_prog', + 'unzip_file', 'untar_file', 'unpack_file', 'call_subprocess', + 'captured_stdout', 'ensure_dir', + 'ARCHIVE_EXTENSIONS', 'SUPPORTED_EXTENSIONS', + 'get_installed_version', 'remove_auth_from_url'] + + +logger = std_logging.getLogger(__name__) + +BZ2_EXTENSIONS = ('.tar.bz2', '.tbz') +XZ_EXTENSIONS = ('.tar.xz', '.txz', '.tlz', '.tar.lz', '.tar.lzma') +ZIP_EXTENSIONS = ('.zip', '.whl') +TAR_EXTENSIONS = ('.tar.gz', '.tgz', '.tar') +ARCHIVE_EXTENSIONS = ( + ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS) +SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS +try: + import bz2 # noqa + SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS +except ImportError: + logger.debug('bz2 module is not available') + +try: + # Only for Python 3.3+ + import lzma # noqa + SUPPORTED_EXTENSIONS += XZ_EXTENSIONS +except ImportError: + logger.debug('lzma module is not available') + + +def import_or_raise(pkg_or_module_string, ExceptionType, *args, **kwargs): + try: + return __import__(pkg_or_module_string) + except ImportError: + raise ExceptionType(*args, **kwargs) + + +def ensure_dir(path): + """os.path.makedirs without EEXIST.""" + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + +def get_prog(): + try: + prog = os.path.basename(sys.argv[0]) + if prog in ('__main__.py', '-c'): + return "%s -m pip" % sys.executable + else: + return prog + except (AttributeError, TypeError, IndexError): + pass + return 'pip' + + +# Retry every half second for up to 3 seconds +@retry(stop_max_delay=3000, wait_fixed=500) +def rmtree(dir, ignore_errors=False): + shutil.rmtree(dir, ignore_errors=ignore_errors, + onerror=rmtree_errorhandler) + + +def rmtree_errorhandler(func, path, exc_info): + """On Windows, the files in .svn are read-only, so when rmtree() tries to + remove them, an exception is thrown. We catch that here, remove the + read-only attribute, and hopefully continue without problems.""" + # if file type currently read only + if os.stat(path).st_mode & stat.S_IREAD: + # convert to read/write + os.chmod(path, stat.S_IWRITE) + # use the original function to repeat the operation + func(path) + return + else: + raise + + +def display_path(path): + """Gives the display value for a given path, making it relative to cwd + if possible.""" + path = os.path.normcase(os.path.abspath(path)) + if sys.version_info[0] == 2: + path = path.decode(sys.getfilesystemencoding(), 'replace') + path = path.encode(sys.getdefaultencoding(), 'replace') + if path.startswith(os.getcwd() + os.path.sep): + path = '.' + path[len(os.getcwd()):] + return path + + +def backup_dir(dir, ext='.bak'): + """Figure out the name of a directory to back up the given dir to + (adding .bak, .bak2, etc)""" + n = 1 + extension = ext + while os.path.exists(dir + extension): + n += 1 + extension = ext + str(n) + return dir + extension + + +def ask_path_exists(message, options): + for action in os.environ.get('PIP_EXISTS_ACTION', '').split(): + if action in options: + return action + return ask(message, options) + + +def ask(message, options): + """Ask the message interactively, with the given possible responses""" + while 1: + if os.environ.get('PIP_NO_INPUT'): + raise Exception( + 'No input was expected ($PIP_NO_INPUT set); question: %s' % + message + ) + response = input(message) + response = response.strip().lower() + if response not in options: + print( + 'Your response (%r) was not one of the expected responses: ' + '%s' % (response, ', '.join(options)) + ) + else: + return response + + +def format_size(bytes): + if bytes > 1000 * 1000: + return '%.1fMB' % (bytes / 1000.0 / 1000) + elif bytes > 10 * 1000: + return '%ikB' % (bytes / 1000) + elif bytes > 1000: + return '%.1fkB' % (bytes / 1000.0) + else: + return '%ibytes' % bytes + + +def is_installable_dir(path): + """Is path is a directory containing setup.py or pyproject.toml? + """ + if not os.path.isdir(path): + return False + setup_py = os.path.join(path, 'setup.py') + if os.path.isfile(setup_py): + return True + pyproject_toml = os.path.join(path, 'pyproject.toml') + if os.path.isfile(pyproject_toml): + return True + return False + + +def is_svn_page(html): + """ + Returns true if the page appears to be the index page of an svn repository + """ + return (re.search(r'<title>[^<]*Revision \d+:', html) and + re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I)) + + +def file_contents(filename): + with open(filename, 'rb') as fp: + return fp.read().decode('utf-8') + + +def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE): + """Yield pieces of data from a file-like object until EOF.""" + while True: + chunk = file.read(size) + if not chunk: + break + yield chunk + + +def split_leading_dir(path): + path = path.lstrip('/').lstrip('\\') + if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) or + '\\' not in path): + return path.split('/', 1) + elif '\\' in path: + return path.split('\\', 1) + else: + return path, '' + + +def has_leading_dir(paths): + """Returns true if all the paths have the same leading path name + (i.e., everything is in one subdirectory in an archive)""" + common_prefix = None + for path in paths: + prefix, rest = split_leading_dir(path) + if not prefix: + return False + elif common_prefix is None: + common_prefix = prefix + elif prefix != common_prefix: + return False + return True + + +def normalize_path(path, resolve_symlinks=True): + """ + Convert a path to its canonical, case-normalized, absolute version. + + """ + path = expanduser(path) + if resolve_symlinks: + path = os.path.realpath(path) + else: + path = os.path.abspath(path) + return os.path.normcase(path) + + +def splitext(path): + """Like os.path.splitext, but take off .tar too""" + base, ext = posixpath.splitext(path) + if base.lower().endswith('.tar'): + ext = base[-4:] + ext + base = base[:-4] + return base, ext + + +def renames(old, new): + """Like os.renames(), but handles renaming across devices.""" + # Implementation borrowed from os.renames(). + head, tail = os.path.split(new) + if head and tail and not os.path.exists(head): + os.makedirs(head) + + shutil.move(old, new) + + head, tail = os.path.split(old) + if head and tail: + try: + os.removedirs(head) + except OSError: + pass + + +def is_local(path): + """ + Return True if path is within sys.prefix, if we're running in a virtualenv. + + If we're not in a virtualenv, all paths are considered "local." + + """ + if not running_under_virtualenv(): + return True + return normalize_path(path).startswith(normalize_path(sys.prefix)) + + +def dist_is_local(dist): + """ + Return True if given Distribution object is installed locally + (i.e. within current virtualenv). + + Always True if we're not in a virtualenv. + + """ + return is_local(dist_location(dist)) + + +def dist_in_usersite(dist): + """ + Return True if given Distribution is installed in user site. + """ + norm_path = normalize_path(dist_location(dist)) + return norm_path.startswith(normalize_path(user_site)) + + +def dist_in_site_packages(dist): + """ + Return True if given Distribution is installed in + sysconfig.get_python_lib(). + """ + return normalize_path( + dist_location(dist) + ).startswith(normalize_path(site_packages)) + + +def dist_is_editable(dist): + """Is distribution an editable install?""" + for path_item in sys.path: + egg_link = os.path.join(path_item, dist.project_name + '.egg-link') + if os.path.isfile(egg_link): + return True + return False + + +def get_installed_distributions(local_only=True, + skip=stdlib_pkgs, + include_editables=True, + editables_only=False, + user_only=False): + """ + Return a list of installed Distribution objects. + + If ``local_only`` is True (default), only return installations + local to the current virtualenv, if in a virtualenv. + + ``skip`` argument is an iterable of lower-case project names to + ignore; defaults to stdlib_pkgs + + If ``include_editables`` is False, don't report editables. + + If ``editables_only`` is True , only report editables. + + If ``user_only`` is True , only report installations in the user + site directory. + + """ + if local_only: + local_test = dist_is_local + else: + def local_test(d): + return True + + if include_editables: + def editable_test(d): + return True + else: + def editable_test(d): + return not dist_is_editable(d) + + if editables_only: + def editables_only_test(d): + return dist_is_editable(d) + else: + def editables_only_test(d): + return True + + if user_only: + user_test = dist_in_usersite + else: + def user_test(d): + return True + + return [d for d in pkg_resources.working_set + if local_test(d) and + d.key not in skip and + editable_test(d) and + editables_only_test(d) and + user_test(d) + ] + + +def egg_link_path(dist): + """ + Return the path for the .egg-link file if it exists, otherwise, None. + + There's 3 scenarios: + 1) not in a virtualenv + try to find in site.USER_SITE, then site_packages + 2) in a no-global virtualenv + try to find in site_packages + 3) in a yes-global virtualenv + try to find in site_packages, then site.USER_SITE + (don't look in global location) + + For #1 and #3, there could be odd cases, where there's an egg-link in 2 + locations. + + This method will just return the first one found. + """ + sites = [] + if running_under_virtualenv(): + if virtualenv_no_global(): + sites.append(site_packages) + else: + sites.append(site_packages) + if user_site: + sites.append(user_site) + else: + if user_site: + sites.append(user_site) + sites.append(site_packages) + + for site in sites: + egglink = os.path.join(site, dist.project_name) + '.egg-link' + if os.path.isfile(egglink): + return egglink + + +def dist_location(dist): + """ + Get the site-packages location of this distribution. Generally + this is dist.location, except in the case of develop-installed + packages, where dist.location is the source code location, and we + want to know where the egg-link file is. + + """ + egg_link = egg_link_path(dist) + if egg_link: + return egg_link + return dist.location + + +def current_umask(): + """Get the current umask which involves having to set it temporarily.""" + mask = os.umask(0) + os.umask(mask) + return mask + + +def unzip_file(filename, location, flatten=True): + """ + Unzip the file (with path `filename`) to the destination `location`. All + files are written based on system defaults and umask (i.e. permissions are + not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + zipfp = open(filename, 'rb') + try: + zip = zipfile.ZipFile(zipfp, allowZip64=True) + leading = has_leading_dir(zip.namelist()) and flatten + for info in zip.infolist(): + name = info.filename + data = zip.read(name) + fn = name + if leading: + fn = split_leading_dir(name)[1] + fn = os.path.join(location, fn) + dir = os.path.dirname(fn) + if fn.endswith('/') or fn.endswith('\\'): + # A directory + ensure_dir(fn) + else: + ensure_dir(dir) + fp = open(fn, 'wb') + try: + fp.write(data) + finally: + fp.close() + mode = info.external_attr >> 16 + # if mode and regular file and any execute permissions for + # user/group/world? + if mode and stat.S_ISREG(mode) and mode & 0o111: + # make dest file have execute for user/group/world + # (chmod +x) no-op on windows per python docs + os.chmod(fn, (0o777 - current_umask() | 0o111)) + finally: + zipfp.close() + + +def untar_file(filename, location): + """ + Untar the file (with path `filename`) to the destination `location`. + All files are written based on system defaults and umask (i.e. permissions + are not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): + mode = 'r:gz' + elif filename.lower().endswith(BZ2_EXTENSIONS): + mode = 'r:bz2' + elif filename.lower().endswith(XZ_EXTENSIONS): + mode = 'r:xz' + elif filename.lower().endswith('.tar'): + mode = 'r' + else: + logger.warning( + 'Cannot determine compression type for file %s', filename, + ) + mode = 'r:*' + tar = tarfile.open(filename, mode) + try: + # note: python<=2.5 doesn't seem to know about pax headers, filter them + leading = has_leading_dir([ + member.name for member in tar.getmembers() + if member.name != 'pax_global_header' + ]) + for member in tar.getmembers(): + fn = member.name + if fn == 'pax_global_header': + continue + if leading: + fn = split_leading_dir(fn)[1] + path = os.path.join(location, fn) + if member.isdir(): + ensure_dir(path) + elif member.issym(): + try: + tar._extract_member(member, path) + except Exception as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + else: + try: + fp = tar.extractfile(member) + except (KeyError, AttributeError) as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + ensure_dir(os.path.dirname(path)) + with open(path, 'wb') as destfp: + shutil.copyfileobj(fp, destfp) + fp.close() + # Update the timestamp (useful for cython compiled files) + tar.utime(member, path) + # member have any execute permissions for user/group/world? + if member.mode & 0o111: + # make dest file have execute for user/group/world + # no-op on windows per python docs + os.chmod(path, (0o777 - current_umask() | 0o111)) + finally: + tar.close() + + +def unpack_file(filename, location, content_type, link): + filename = os.path.realpath(filename) + if (content_type == 'application/zip' or + filename.lower().endswith(ZIP_EXTENSIONS) or + zipfile.is_zipfile(filename)): + unzip_file( + filename, + location, + flatten=not filename.endswith('.whl') + ) + elif (content_type == 'application/x-gzip' or + tarfile.is_tarfile(filename) or + filename.lower().endswith( + TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS)): + untar_file(filename, location) + elif (content_type and content_type.startswith('text/html') and + is_svn_page(file_contents(filename))): + # We don't really care about this + from pip._internal.vcs.subversion import Subversion + Subversion('svn+' + link.url).unpack(location) + else: + # FIXME: handle? + # FIXME: magic signatures? + logger.critical( + 'Cannot unpack file %s (downloaded from %s, content-type: %s); ' + 'cannot detect archive format', + filename, location, content_type, + ) + raise InstallationError( + 'Cannot determine archive format of %s' % location + ) + + +def call_subprocess(cmd, show_stdout=True, cwd=None, + on_returncode='raise', + command_desc=None, + extra_environ=None, unset_environ=None, spinner=None): + """ + Args: + unset_environ: an iterable of environment variable names to unset + prior to calling subprocess.Popen(). + """ + if unset_environ is None: + unset_environ = [] + # This function's handling of subprocess output is confusing and I + # previously broke it terribly, so as penance I will write a long comment + # explaining things. + # + # The obvious thing that affects output is the show_stdout= + # kwarg. show_stdout=True means, let the subprocess write directly to our + # stdout. Even though it is nominally the default, it is almost never used + # inside pip (and should not be used in new code without a very good + # reason); as of 2016-02-22 it is only used in a few places inside the VCS + # wrapper code. Ideally we should get rid of it entirely, because it + # creates a lot of complexity here for a rarely used feature. + # + # Most places in pip set show_stdout=False. What this means is: + # - We connect the child stdout to a pipe, which we read. + # - By default, we hide the output but show a spinner -- unless the + # subprocess exits with an error, in which case we show the output. + # - If the --verbose option was passed (= loglevel is DEBUG), then we show + # the output unconditionally. (But in this case we don't want to show + # the output a second time if it turns out that there was an error.) + # + # stderr is always merged with stdout (even if show_stdout=True). + if show_stdout: + stdout = None + else: + stdout = subprocess.PIPE + if command_desc is None: + cmd_parts = [] + for part in cmd: + if ' ' in part or '\n' in part or '"' in part or "'" in part: + part = '"%s"' % part.replace('"', '\\"') + cmd_parts.append(part) + command_desc = ' '.join(cmd_parts) + logger.debug("Running command %s", command_desc) + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + for name in unset_environ: + env.pop(name, None) + try: + proc = subprocess.Popen( + cmd, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, + stdout=stdout, cwd=cwd, env=env, + ) + proc.stdin.close() + except Exception as exc: + logger.critical( + "Error %s while executing command %s", exc, command_desc, + ) + raise + all_output = [] + if stdout is not None: + while True: + line = console_to_str(proc.stdout.readline()) + if not line: + break + line = line.rstrip() + all_output.append(line + '\n') + if logger.getEffectiveLevel() <= std_logging.DEBUG: + # Show the line immediately + logger.debug(line) + else: + # Update the spinner + if spinner is not None: + spinner.spin() + try: + proc.wait() + finally: + if proc.stdout: + proc.stdout.close() + if spinner is not None: + if proc.returncode: + spinner.finish("error") + else: + spinner.finish("done") + if proc.returncode: + if on_returncode == 'raise': + if (logger.getEffectiveLevel() > std_logging.DEBUG and + not show_stdout): + logger.info( + 'Complete output from command %s:', command_desc, + ) + logger.info( + ''.join(all_output) + + '\n----------------------------------------' + ) + raise InstallationError( + 'Command "%s" failed with error code %s in %s' + % (command_desc, proc.returncode, cwd)) + elif on_returncode == 'warn': + logger.warning( + 'Command "%s" had error code %s in %s', + command_desc, proc.returncode, cwd, + ) + elif on_returncode == 'ignore': + pass + else: + raise ValueError('Invalid value: on_returncode=%s' % + repr(on_returncode)) + if not show_stdout: + return ''.join(all_output) + + +def read_text_file(filename): + """Return the contents of *filename*. + + Try to decode the file contents with utf-8, the preferred system encoding + (e.g., cp1252 on some Windows machines), and latin1, in that order. + Decoding a byte string with latin1 will never raise an error. In the worst + case, the returned string will contain some garbage characters. + + """ + with open(filename, 'rb') as fp: + data = fp.read() + + encodings = ['utf-8', locale.getpreferredencoding(False), 'latin1'] + for enc in encodings: + try: + data = data.decode(enc) + except UnicodeDecodeError: + continue + break + + assert type(data) != bytes # Latin1 should have worked. + return data + + +def _make_build_dir(build_dir): + os.makedirs(build_dir) + write_delete_marker_file(build_dir) + + +class FakeFile(object): + """Wrap a list of lines in an object with readline() to make + ConfigParser happy.""" + def __init__(self, lines): + self._gen = (l for l in lines) + + def readline(self): + try: + try: + return next(self._gen) + except NameError: + return self._gen.next() + except StopIteration: + return '' + + def __iter__(self): + return self._gen + + +class StreamWrapper(StringIO): + + @classmethod + def from_stream(cls, orig_stream): + cls.orig_stream = orig_stream + return cls() + + # compileall.compile_dir() needs stdout.encoding to print to stdout + @property + def encoding(self): + return self.orig_stream.encoding + + +@contextlib.contextmanager +def captured_output(stream_name): + """Return a context manager used by captured_stdout/stdin/stderr + that temporarily replaces the sys stream *stream_name* with a StringIO. + + Taken from Lib/support/__init__.py in the CPython repo. + """ + orig_stdout = getattr(sys, stream_name) + setattr(sys, stream_name, StreamWrapper.from_stream(orig_stdout)) + try: + yield getattr(sys, stream_name) + finally: + setattr(sys, stream_name, orig_stdout) + + +def captured_stdout(): + """Capture the output of sys.stdout: + + with captured_stdout() as stdout: + print('hello') + self.assertEqual(stdout.getvalue(), 'hello\n') + + Taken from Lib/support/__init__.py in the CPython repo. + """ + return captured_output('stdout') + + +class cached_property(object): + """A property that is only computed once per instance and then replaces + itself with an ordinary attribute. Deleting the attribute resets the + property. + + Source: https://github.com/bottlepy/bottle/blob/0.11.5/bottle.py#L175 + """ + + def __init__(self, func): + self.__doc__ = getattr(func, '__doc__') + self.func = func + + def __get__(self, obj, cls): + if obj is None: + # We're being accessed from the class itself, not from an object + return self + value = obj.__dict__[self.func.__name__] = self.func(obj) + return value + + +def get_installed_version(dist_name, working_set=None): + """Get the installed version of dist_name avoiding pkg_resources cache""" + # Create a requirement that we'll look for inside of setuptools. + req = pkg_resources.Requirement.parse(dist_name) + + if working_set is None: + # We want to avoid having this cached, so we need to construct a new + # working set each time. + working_set = pkg_resources.WorkingSet() + + # Get the installed distribution from our working set + dist = working_set.find(req) + + # Check to see if we got an installed distribution or not, if we did + # we want to return it's version. + return dist.version if dist else None + + +def consume(iterator): + """Consume an iterable at C speed.""" + deque(iterator, maxlen=0) + + +# Simulates an enum +def enum(*sequential, **named): + enums = dict(zip(sequential, range(len(sequential))), **named) + reverse = {value: key for key, value in enums.items()} + enums['reverse_mapping'] = reverse + return type('Enum', (), enums) + + +def make_vcs_requirement_url(repo_url, rev, egg_project_name, subdir=None): + """ + Return the URL for a VCS requirement. + + Args: + repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+"). + """ + req = '{}@{}#egg={}'.format(repo_url, rev, egg_project_name) + if subdir: + req += '&subdirectory={}'.format(subdir) + + return req + + +def split_auth_from_netloc(netloc): + """ + Parse out and remove the auth information from a netloc. + + Returns: (netloc, (username, password)). + """ + if '@' not in netloc: + return netloc, (None, None) + + # Split from the right because that's how urllib.parse.urlsplit() + # behaves if more than one @ is present (which can be checked using + # the password attribute of urlsplit()'s return value). + auth, netloc = netloc.rsplit('@', 1) + if ':' in auth: + # Split from the left because that's how urllib.parse.urlsplit() + # behaves if more than one : is present (which again can be checked + # using the password attribute of the return value) + user_pass = tuple(auth.split(':', 1)) + else: + user_pass = auth, None + + return netloc, user_pass + + +def remove_auth_from_url(url): + # Return a copy of url with 'username:password@' removed. + # username/pass params are passed to subversion through flags + # and are not recognized in the url. + + # parsed url + purl = urllib_parse.urlsplit(url) + netloc, user_pass = split_auth_from_netloc(purl.netloc) + + # stripped url + url_pieces = ( + purl.scheme, netloc, purl.path, purl.query, purl.fragment + ) + surl = urllib_parse.urlunsplit(url_pieces) + return surl + + +def protect_pip_from_modification_on_windows(modifying_pip): + """Protection of pip.exe from modification on Windows + + On Windows, any operation modifying pip should be run as: + python -m pip ... + """ + pip_names = [ + "pip.exe", + "pip{}.exe".format(sys.version_info[0]), + "pip{}.{}.exe".format(*sys.version_info[:2]) + ] + + # See https://github.com/pypa/pip/issues/1299 for more discussion + should_show_use_python_msg = ( + modifying_pip and + WINDOWS and + os.path.basename(sys.argv[0]) in pip_names + ) + + if should_show_use_python_msg: + new_command = [ + sys.executable, "-m", "pip" + ] + sys.argv[1:] + raise CommandError( + 'To modify pip, please run the following command:\n{}' + .format(" ".join(new_command)) + ) diff --git a/venv/Lib/site-packages/pip/_internal/utils/models.py b/venv/Lib/site-packages/pip/_internal/utils/models.py new file mode 100644 index 0000000..d5cb80a --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/models.py @@ -0,0 +1,40 @@ +"""Utilities for defining models +""" + +import operator + + +class KeyBasedCompareMixin(object): + """Provides comparision capabilities that is based on a key + """ + + def __init__(self, key, defining_class): + self._compare_key = key + self._defining_class = defining_class + + def __hash__(self): + return hash(self._compare_key) + + def __lt__(self, other): + return self._compare(other, operator.__lt__) + + def __le__(self, other): + return self._compare(other, operator.__le__) + + def __gt__(self, other): + return self._compare(other, operator.__gt__) + + def __ge__(self, other): + return self._compare(other, operator.__ge__) + + def __eq__(self, other): + return self._compare(other, operator.__eq__) + + def __ne__(self, other): + return self._compare(other, operator.__ne__) + + def _compare(self, other, method): + if not isinstance(other, self._defining_class): + return NotImplemented + + return method(self._compare_key, other._compare_key) diff --git a/venv/Lib/site-packages/pip/_internal/utils/outdated.py b/venv/Lib/site-packages/pip/_internal/utils/outdated.py new file mode 100644 index 0000000..5bfbfe1 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/outdated.py @@ -0,0 +1,154 @@ +from __future__ import absolute_import + +import datetime +import json +import logging +import os.path +import sys + +from pip._vendor import lockfile, pkg_resources +from pip._vendor.packaging import version as packaging_version + +from pip._internal.index import PackageFinder +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.misc import ensure_dir, get_installed_version + +SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" + + +logger = logging.getLogger(__name__) + + +class SelfCheckState(object): + def __init__(self, cache_dir): + self.state = {} + self.statefile_path = None + + # Try to load the existing state + if cache_dir: + self.statefile_path = os.path.join(cache_dir, "selfcheck.json") + try: + with open(self.statefile_path) as statefile: + self.state = json.load(statefile)[sys.prefix] + except (IOError, ValueError, KeyError): + # Explicitly suppressing exceptions, since we don't want to + # error out if the cache file is invalid. + pass + + def save(self, pypi_version, current_time): + # If we do not have a path to cache in, don't bother saving. + if not self.statefile_path: + return + + # Check to make sure that we own the directory + if not check_path_owner(os.path.dirname(self.statefile_path)): + return + + # Now that we've ensured the directory is owned by this user, we'll go + # ahead and make sure that all our directories are created. + ensure_dir(os.path.dirname(self.statefile_path)) + + # Attempt to write out our version check file + with lockfile.LockFile(self.statefile_path): + if os.path.exists(self.statefile_path): + with open(self.statefile_path) as statefile: + state = json.load(statefile) + else: + state = {} + + state[sys.prefix] = { + "last_check": current_time.strftime(SELFCHECK_DATE_FMT), + "pypi_version": pypi_version, + } + + with open(self.statefile_path, "w") as statefile: + json.dump(state, statefile, sort_keys=True, + separators=(",", ":")) + + +def was_installed_by_pip(pkg): + """Checks whether pkg was installed by pip + + This is used not to display the upgrade message when pip is in fact + installed by system package manager, such as dnf on Fedora. + """ + try: + dist = pkg_resources.get_distribution(pkg) + return (dist.has_metadata('INSTALLER') and + 'pip' in dist.get_metadata_lines('INSTALLER')) + except pkg_resources.DistributionNotFound: + return False + + +def pip_version_check(session, options): + """Check for an update for pip. + + Limit the frequency of checks to once per week. State is stored either in + the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix + of the pip script path. + """ + installed_version = get_installed_version("pip") + if not installed_version: + return + + pip_version = packaging_version.parse(installed_version) + pypi_version = None + + try: + state = SelfCheckState(cache_dir=options.cache_dir) + + current_time = datetime.datetime.utcnow() + # Determine if we need to refresh the state + if "last_check" in state.state and "pypi_version" in state.state: + last_check = datetime.datetime.strptime( + state.state["last_check"], + SELFCHECK_DATE_FMT + ) + if (current_time - last_check).total_seconds() < 7 * 24 * 60 * 60: + pypi_version = state.state["pypi_version"] + + # Refresh the version if we need to or just see if we need to warn + if pypi_version is None: + # Lets use PackageFinder to see what the latest pip version is + finder = PackageFinder( + find_links=options.find_links, + index_urls=[options.index_url] + options.extra_index_urls, + allow_all_prereleases=False, # Explicitly set to False + trusted_hosts=options.trusted_hosts, + process_dependency_links=options.process_dependency_links, + session=session, + ) + all_candidates = finder.find_all_candidates("pip") + if not all_candidates: + return + pypi_version = str( + max(all_candidates, key=lambda c: c.version).version + ) + + # save that we've performed a check + state.save(pypi_version, current_time) + + remote_version = packaging_version.parse(pypi_version) + + # Determine if our pypi_version is older + if (pip_version < remote_version and + pip_version.base_version != remote_version.base_version and + was_installed_by_pip('pip')): + # Advise "python -m pip" on Windows to avoid issues + # with overwriting pip.exe. + if WINDOWS: + pip_cmd = "python -m pip" + else: + pip_cmd = "pip" + logger.warning( + "You are using pip version %s, however version %s is " + "available.\nYou should consider upgrading via the " + "'%s install --upgrade pip' command.", + pip_version, pypi_version, pip_cmd + ) + except Exception: + logger.debug( + "There was an error checking the latest version of pip", + exc_info=True, + ) diff --git a/venv/Lib/site-packages/pip/_internal/utils/packaging.py b/venv/Lib/site-packages/pip/_internal/utils/packaging.py new file mode 100644 index 0000000..c43142f --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/packaging.py @@ -0,0 +1,75 @@ +from __future__ import absolute_import + +import logging +import sys +from email.parser import FeedParser # type: ignore + +from pip._vendor import pkg_resources +from pip._vendor.packaging import specifiers, version + +from pip._internal import exceptions +from pip._internal.utils.misc import display_path + +logger = logging.getLogger(__name__) + + +def check_requires_python(requires_python): + """ + Check if the python version in use match the `requires_python` specifier. + + Returns `True` if the version of python in use matches the requirement. + Returns `False` if the version of python in use does not matches the + requirement. + + Raises an InvalidSpecifier if `requires_python` have an invalid format. + """ + if requires_python is None: + # The package provides no information + return True + requires_python_specifier = specifiers.SpecifierSet(requires_python) + + # We only use major.minor.micro + python_version = version.parse('.'.join(map(str, sys.version_info[:3]))) + return python_version in requires_python_specifier + + +def get_metadata(dist): + if (isinstance(dist, pkg_resources.DistInfoDistribution) and + dist.has_metadata('METADATA')): + metadata = dist.get_metadata('METADATA') + elif dist.has_metadata('PKG-INFO'): + metadata = dist.get_metadata('PKG-INFO') + else: + logger.warning("No metadata found in %s", display_path(dist.location)) + metadata = '' + + feed_parser = FeedParser() + feed_parser.feed(metadata) + return feed_parser.close() + + +def check_dist_requires_python(dist): + pkg_info_dict = get_metadata(dist) + requires_python = pkg_info_dict.get('Requires-Python') + try: + if not check_requires_python(requires_python): + raise exceptions.UnsupportedPythonVersion( + "%s requires Python '%s' but the running Python is %s" % ( + dist.project_name, + requires_python, + '.'.join(map(str, sys.version_info[:3])),) + ) + except specifiers.InvalidSpecifier as e: + logger.warning( + "Package %s has an invalid Requires-Python entry %s - %s", + dist.project_name, requires_python, e, + ) + return + + +def get_installer(dist): + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + return line.strip() + return '' diff --git a/venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py b/venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py new file mode 100644 index 0000000..03973e9 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py @@ -0,0 +1,8 @@ +# Shim to wrap setup.py invocation with setuptools +SETUPTOOLS_SHIM = ( + "import setuptools, tokenize;__file__=%r;" + "f=getattr(tokenize, 'open', open)(__file__);" + "code=f.read().replace('\\r\\n', '\\n');" + "f.close();" + "exec(compile(code, __file__, 'exec'))" +) diff --git a/venv/Lib/site-packages/pip/_internal/utils/temp_dir.py b/venv/Lib/site-packages/pip/_internal/utils/temp_dir.py new file mode 100644 index 0000000..edc506b --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/temp_dir.py @@ -0,0 +1,82 @@ +from __future__ import absolute_import + +import logging +import os.path +import tempfile + +from pip._internal.utils.misc import rmtree + +logger = logging.getLogger(__name__) + + +class TempDirectory(object): + """Helper class that owns and cleans up a temporary directory. + + This class can be used as a context manager or as an OO representation of a + temporary directory. + + Attributes: + path + Location to the created temporary directory or None + delete + Whether the directory should be deleted when exiting + (when used as a contextmanager) + + Methods: + create() + Creates a temporary directory and stores its path in the path + attribute. + cleanup() + Deletes the temporary directory and sets path attribute to None + + When used as a context manager, a temporary directory is created on + entering the context and, if the delete attribute is True, on exiting the + context the created directory is deleted. + """ + + def __init__(self, path=None, delete=None, kind="temp"): + super(TempDirectory, self).__init__() + + if path is None and delete is None: + # If we were not given an explicit directory, and we were not given + # an explicit delete option, then we'll default to deleting. + delete = True + + self.path = path + self.delete = delete + self.kind = kind + + def __repr__(self): + return "<{} {!r}>".format(self.__class__.__name__, self.path) + + def __enter__(self): + self.create() + return self + + def __exit__(self, exc, value, tb): + if self.delete: + self.cleanup() + + def create(self): + """Create a temporary directory and store it's path in self.path + """ + if self.path is not None: + logger.debug( + "Skipped creation of temporary directory: {}".format(self.path) + ) + return + # We realpath here because some systems have their default tmpdir + # symlinked to another directory. This tends to confuse build + # scripts, so we canonicalize the path by traversing potential + # symlinks here. + self.path = os.path.realpath( + tempfile.mkdtemp(prefix="pip-{}-".format(self.kind)) + ) + logger.debug("Created temporary directory: {}".format(self.path)) + + def cleanup(self): + """Remove the temporary directory created and reset state + """ + if self.path is not None and os.path.exists(self.path): + rmtree(self.path) + self.path = None diff --git a/venv/Lib/site-packages/pip/_internal/utils/typing.py b/venv/Lib/site-packages/pip/_internal/utils/typing.py new file mode 100644 index 0000000..e085cdf --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/typing.py @@ -0,0 +1,29 @@ +"""For neatly implementing static typing in pip. + +`mypy` - the static type analysis tool we use - uses the `typing` module, which +provides core functionality fundamental to mypy's functioning. + +Generally, `typing` would be imported at runtime and used in that fashion - +it acts as a no-op at runtime and does not have any run-time overhead by +design. + +As it turns out, `typing` is not vendorable - it uses separate sources for +Python 2/Python 3. Thus, this codebase can not expect it to be present. +To work around this, mypy allows the typing import to be behind a False-y +optional to prevent it from running at runtime and type-comments can be used +to remove the need for the types to be accessible directly during runtime. + +This module provides the False-y guard in a nicely named fashion so that a +curious maintainer can reach here to read this. + +In pip, all static-typing related imports should be guarded as follows: + + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + + if MYPY_CHECK_RUNNING: + from typing import ... # noqa: F401 + +Ref: https://github.com/python/mypy/issues/3216 +""" + +MYPY_CHECK_RUNNING = False diff --git a/venv/Lib/site-packages/pip/_internal/utils/ui.py b/venv/Lib/site-packages/pip/_internal/utils/ui.py new file mode 100644 index 0000000..6bab904 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/utils/ui.py @@ -0,0 +1,421 @@ +from __future__ import absolute_import, division + +import contextlib +import itertools +import logging +import sys +import time +from signal import SIGINT, default_int_handler, signal + +from pip._vendor import six +from pip._vendor.progress.bar import ( + Bar, ChargingBar, FillingCirclesBar, FillingSquaresBar, IncrementalBar, + ShadyBar, +) +from pip._vendor.progress.helpers import HIDE_CURSOR, SHOW_CURSOR, WritelnMixin +from pip._vendor.progress.spinner import Spinner + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.logging import get_indentation +from pip._internal.utils.misc import format_size +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any # noqa: F401 + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + +logger = logging.getLogger(__name__) + + +def _select_progress_class(preferred, fallback): + encoding = getattr(preferred.file, "encoding", None) + + # If we don't know what encoding this file is in, then we'll just assume + # that it doesn't support unicode and use the ASCII bar. + if not encoding: + return fallback + + # Collect all of the possible characters we want to use with the preferred + # bar. + characters = [ + getattr(preferred, "empty_fill", six.text_type()), + getattr(preferred, "fill", six.text_type()), + ] + characters += list(getattr(preferred, "phases", [])) + + # Try to decode the characters we're using for the bar using the encoding + # of the given file, if this works then we'll assume that we can use the + # fancier bar and if not we'll fall back to the plaintext bar. + try: + six.text_type().join(characters).encode(encoding) + except UnicodeEncodeError: + return fallback + else: + return preferred + + +_BaseBar = _select_progress_class(IncrementalBar, Bar) # type: Any + + +class InterruptibleMixin(object): + """ + Helper to ensure that self.finish() gets called on keyboard interrupt. + + This allows downloads to be interrupted without leaving temporary state + (like hidden cursors) behind. + + This class is similar to the progress library's existing SigIntMixin + helper, but as of version 1.2, that helper has the following problems: + + 1. It calls sys.exit(). + 2. It discards the existing SIGINT handler completely. + 3. It leaves its own handler in place even after an uninterrupted finish, + which will have unexpected delayed effects if the user triggers an + unrelated keyboard interrupt some time after a progress-displaying + download has already completed, for example. + """ + + def __init__(self, *args, **kwargs): + """ + Save the original SIGINT handler for later. + """ + super(InterruptibleMixin, self).__init__(*args, **kwargs) + + self.original_handler = signal(SIGINT, self.handle_sigint) + + # If signal() returns None, the previous handler was not installed from + # Python, and we cannot restore it. This probably should not happen, + # but if it does, we must restore something sensible instead, at least. + # The least bad option should be Python's default SIGINT handler, which + # just raises KeyboardInterrupt. + if self.original_handler is None: + self.original_handler = default_int_handler + + def finish(self): + """ + Restore the original SIGINT handler after finishing. + + This should happen regardless of whether the progress display finishes + normally, or gets interrupted. + """ + super(InterruptibleMixin, self).finish() + signal(SIGINT, self.original_handler) + + def handle_sigint(self, signum, frame): + """ + Call self.finish() before delegating to the original SIGINT handler. + + This handler should only be in place while the progress display is + active. + """ + self.finish() + self.original_handler(signum, frame) + + +class SilentBar(Bar): + + def update(self): + pass + + +class BlueEmojiBar(IncrementalBar): + + suffix = "%(percent)d%%" + bar_prefix = " " + bar_suffix = " " + phases = (u"\U0001F539", u"\U0001F537", u"\U0001F535") # type: Any + + +class DownloadProgressMixin(object): + + def __init__(self, *args, **kwargs): + super(DownloadProgressMixin, self).__init__(*args, **kwargs) + self.message = (" " * (get_indentation() + 2)) + self.message + + @property + def downloaded(self): + return format_size(self.index) + + @property + def download_speed(self): + # Avoid zero division errors... + if self.avg == 0.0: + return "..." + return format_size(1 / self.avg) + "/s" + + @property + def pretty_eta(self): + if self.eta: + return "eta %s" % self.eta_td + return "" + + def iter(self, it, n=1): + for x in it: + yield x + self.next(n) + self.finish() + + +class WindowsMixin(object): + + def __init__(self, *args, **kwargs): + # The Windows terminal does not support the hide/show cursor ANSI codes + # even with colorama. So we'll ensure that hide_cursor is False on + # Windows. + # This call neds to go before the super() call, so that hide_cursor + # is set in time. The base progress bar class writes the "hide cursor" + # code to the terminal in its init, so if we don't set this soon + # enough, we get a "hide" with no corresponding "show"... + if WINDOWS and self.hide_cursor: + self.hide_cursor = False + + super(WindowsMixin, self).__init__(*args, **kwargs) + + # Check if we are running on Windows and we have the colorama module, + # if we do then wrap our file with it. + if WINDOWS and colorama: + self.file = colorama.AnsiToWin32(self.file) + # The progress code expects to be able to call self.file.isatty() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.isatty = lambda: self.file.wrapped.isatty() + # The progress code expects to be able to call self.file.flush() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.flush = lambda: self.file.wrapped.flush() + + +class BaseDownloadProgressBar(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin): + + file = sys.stdout + message = "%(percent)d%%" + suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" + +# NOTE: The "type: ignore" comments on the following classes are there to +# work around https://github.com/python/typing/issues/241 + + +class DefaultDownloadProgressBar(BaseDownloadProgressBar, + _BaseBar): # type: ignore + pass + + +class DownloadSilentBar(BaseDownloadProgressBar, SilentBar): # type: ignore + pass + + +class DownloadIncrementalBar(BaseDownloadProgressBar, # type: ignore + IncrementalBar): + pass + + +class DownloadChargingBar(BaseDownloadProgressBar, # type: ignore + ChargingBar): + pass + + +class DownloadShadyBar(BaseDownloadProgressBar, ShadyBar): # type: ignore + pass + + +class DownloadFillingSquaresBar(BaseDownloadProgressBar, # type: ignore + FillingSquaresBar): + pass + + +class DownloadFillingCirclesBar(BaseDownloadProgressBar, # type: ignore + FillingCirclesBar): + pass + + +class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, # type: ignore + BlueEmojiBar): + pass + + +class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin, WritelnMixin, Spinner): + + file = sys.stdout + suffix = "%(downloaded)s %(download_speed)s" + + def next_phase(self): + if not hasattr(self, "_phaser"): + self._phaser = itertools.cycle(self.phases) + return next(self._phaser) + + def update(self): + message = self.message % self + phase = self.next_phase() + suffix = self.suffix % self + line = ''.join([ + message, + " " if message else "", + phase, + " " if suffix else "", + suffix, + ]) + + self.writeln(line) + + +BAR_TYPES = { + "off": (DownloadSilentBar, DownloadSilentBar), + "on": (DefaultDownloadProgressBar, DownloadProgressSpinner), + "ascii": (DownloadIncrementalBar, DownloadProgressSpinner), + "pretty": (DownloadFillingCirclesBar, DownloadProgressSpinner), + "emoji": (DownloadBlueEmojiProgressBar, DownloadProgressSpinner) +} + + +def DownloadProgressProvider(progress_bar, max=None): + if max is None or max == 0: + return BAR_TYPES[progress_bar][1]().iter + else: + return BAR_TYPES[progress_bar][0](max=max).iter + + +################################################################ +# Generic "something is happening" spinners +# +# We don't even try using progress.spinner.Spinner here because it's actually +# simpler to reimplement from scratch than to coerce their code into doing +# what we need. +################################################################ + +@contextlib.contextmanager +def hidden_cursor(file): + # The Windows terminal does not support the hide/show cursor ANSI codes, + # even via colorama. So don't even try. + if WINDOWS: + yield + # We don't want to clutter the output with control characters if we're + # writing to a file, or if the user is running with --quiet. + # See https://github.com/pypa/pip/issues/3418 + elif not file.isatty() or logger.getEffectiveLevel() > logging.INFO: + yield + else: + file.write(HIDE_CURSOR) + try: + yield + finally: + file.write(SHOW_CURSOR) + + +class RateLimiter(object): + def __init__(self, min_update_interval_seconds): + self._min_update_interval_seconds = min_update_interval_seconds + self._last_update = 0 + + def ready(self): + now = time.time() + delta = now - self._last_update + return delta >= self._min_update_interval_seconds + + def reset(self): + self._last_update = time.time() + + +class InteractiveSpinner(object): + def __init__(self, message, file=None, spin_chars="-\\|/", + # Empirically, 8 updates/second looks nice + min_update_interval_seconds=0.125): + self._message = message + if file is None: + file = sys.stdout + self._file = file + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._finished = False + + self._spin_cycle = itertools.cycle(spin_chars) + + self._file.write(" " * get_indentation() + self._message + " ... ") + self._width = 0 + + def _write(self, status): + assert not self._finished + # Erase what we wrote before by backspacing to the beginning, writing + # spaces to overwrite the old text, and then backspacing again + backup = "\b" * self._width + self._file.write(backup + " " * self._width + backup) + # Now we have a blank slate to add our status + self._file.write(status) + self._width = len(status) + self._file.flush() + self._rate_limiter.reset() + + def spin(self): + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._write(next(self._spin_cycle)) + + def finish(self, final_status): + if self._finished: + return + self._write(final_status) + self._file.write("\n") + self._file.flush() + self._finished = True + + +# Used for dumb terminals, non-interactive installs (no tty), etc. +# We still print updates occasionally (once every 60 seconds by default) to +# act as a keep-alive for systems like Travis-CI that take lack-of-output as +# an indication that a task has frozen. +class NonInteractiveSpinner(object): + def __init__(self, message, min_update_interval_seconds=60): + self._message = message + self._finished = False + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._update("started") + + def _update(self, status): + assert not self._finished + self._rate_limiter.reset() + logger.info("%s: %s", self._message, status) + + def spin(self): + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._update("still running...") + + def finish(self, final_status): + if self._finished: + return + self._update("finished with status '%s'" % (final_status,)) + self._finished = True + + +@contextlib.contextmanager +def open_spinner(message): + # Interactive spinner goes directly to sys.stdout rather than being routed + # through the logging system, but it acts like it has level INFO, + # i.e. it's only displayed if we're at level INFO or better. + # Non-interactive spinner goes through the logging system, so it is always + # in sync with logging configuration. + if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO: + spinner = InteractiveSpinner(message) + else: + spinner = NonInteractiveSpinner(message) + try: + with hidden_cursor(sys.stdout): + yield spinner + except KeyboardInterrupt: + spinner.finish("canceled") + raise + except Exception: + spinner.finish("error") + raise + else: + spinner.finish("done") diff --git a/venv/Lib/site-packages/pip/_internal/vcs/__init__.py b/venv/Lib/site-packages/pip/_internal/vcs/__init__.py new file mode 100644 index 0000000..794b35d --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/vcs/__init__.py @@ -0,0 +1,509 @@ +"""Handles all VCS (version control) support""" +from __future__ import absolute_import + +import errno +import logging +import os +import shutil +import sys + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.exceptions import BadCommand +from pip._internal.utils.misc import ( + display_path, backup_dir, call_subprocess, rmtree, ask_path_exists, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, Optional, Tuple # noqa: F401 + from pip._internal.cli.base_command import Command # noqa: F401 + +__all__ = ['vcs', 'get_src_requirement'] + + +logger = logging.getLogger(__name__) + + +class RevOptions(object): + + """ + Encapsulates a VCS-specific revision to install, along with any VCS + install options. + + Instances of this class should be treated as if immutable. + """ + + def __init__(self, vcs, rev=None, extra_args=None): + """ + Args: + vcs: a VersionControl object. + rev: the name of the revision to install. + extra_args: a list of extra options. + """ + if extra_args is None: + extra_args = [] + + self.extra_args = extra_args + self.rev = rev + self.vcs = vcs + + def __repr__(self): + return '<RevOptions {}: rev={!r}>'.format(self.vcs.name, self.rev) + + @property + def arg_rev(self): + if self.rev is None: + return self.vcs.default_arg_rev + + return self.rev + + def to_args(self): + """ + Return the VCS-specific command arguments. + """ + args = [] + rev = self.arg_rev + if rev is not None: + args += self.vcs.get_base_rev_args(rev) + args += self.extra_args + + return args + + def to_display(self): + if not self.rev: + return '' + + return ' (to revision {})'.format(self.rev) + + def make_new(self, rev): + """ + Make a copy of the current instance, but with a new rev. + + Args: + rev: the name of the revision for the new object. + """ + return self.vcs.make_rev_options(rev, extra_args=self.extra_args) + + +class VcsSupport(object): + _registry = {} # type: Dict[str, Command] + schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn'] + + def __init__(self): + # Register more schemes with urlparse for various version control + # systems + urllib_parse.uses_netloc.extend(self.schemes) + # Python >= 2.7.4, 3.3 doesn't have uses_fragment + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(self.schemes) + super(VcsSupport, self).__init__() + + def __iter__(self): + return self._registry.__iter__() + + @property + def backends(self): + return list(self._registry.values()) + + @property + def dirnames(self): + return [backend.dirname for backend in self.backends] + + @property + def all_schemes(self): + schemes = [] + for backend in self.backends: + schemes.extend(backend.schemes) + return schemes + + def register(self, cls): + if not hasattr(cls, 'name'): + logger.warning('Cannot register VCS %s', cls.__name__) + return + if cls.name not in self._registry: + self._registry[cls.name] = cls + logger.debug('Registered VCS backend: %s', cls.name) + + def unregister(self, cls=None, name=None): + if name in self._registry: + del self._registry[name] + elif cls in self._registry.values(): + del self._registry[cls.name] + else: + logger.warning('Cannot unregister because no class or name given') + + def get_backend_name(self, location): + """ + Return the name of the version control backend if found at given + location, e.g. vcs.get_backend_name('/path/to/vcs/checkout') + """ + for vc_type in self._registry.values(): + if vc_type.controls_location(location): + logger.debug('Determine that %s uses VCS: %s', + location, vc_type.name) + return vc_type.name + return None + + def get_backend(self, name): + name = name.lower() + if name in self._registry: + return self._registry[name] + + def get_backend_from_location(self, location): + vc_type = self.get_backend_name(location) + if vc_type: + return self.get_backend(vc_type) + return None + + +vcs = VcsSupport() + + +class VersionControl(object): + name = '' + dirname = '' + # List of supported schemes for this Version Control + schemes = () # type: Tuple[str, ...] + # Iterable of environment variable names to pass to call_subprocess(). + unset_environ = () # type: Tuple[str, ...] + default_arg_rev = None # type: Optional[str] + + def __init__(self, url=None, *args, **kwargs): + self.url = url + super(VersionControl, self).__init__(*args, **kwargs) + + def get_base_rev_args(self, rev): + """ + Return the base revision arguments for a vcs command. + + Args: + rev: the name of a revision to install. Cannot be None. + """ + raise NotImplementedError + + def make_rev_options(self, rev=None, extra_args=None): + """ + Return a RevOptions object. + + Args: + rev: the name of a revision to install. + extra_args: a list of extra options. + """ + return RevOptions(self, rev, extra_args=extra_args) + + def _is_local_repository(self, repo): + """ + posix absolute paths start with os.path.sep, + win32 ones start with drive (like c:\\folder) + """ + drive, tail = os.path.splitdrive(repo) + return repo.startswith(os.path.sep) or drive + + def export(self, location): + """ + Export the repository at the url to the destination location + i.e. only download the files, without vcs informations + """ + raise NotImplementedError + + def get_netloc_and_auth(self, netloc, scheme): + """ + Parse the repository URL's netloc, and return the new netloc to use + along with auth information. + + Args: + netloc: the original repository URL netloc. + scheme: the repository URL's scheme without the vcs prefix. + + This is mainly for the Subversion class to override, so that auth + information can be provided via the --username and --password options + instead of through the URL. For other subclasses like Git without + such an option, auth information must stay in the URL. + + Returns: (netloc, (username, password)). + """ + return netloc, (None, None) + + def get_url_rev_and_auth(self, url): + """ + Parse the repository URL to use, and return the URL, revision, + and auth info to use. + + Returns: (url, rev, (username, password)). + """ + scheme, netloc, path, query, frag = urllib_parse.urlsplit(url) + if '+' not in scheme: + raise ValueError( + "Sorry, {!r} is a malformed VCS url. " + "The format is <vcs>+<protocol>://<url>, " + "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp".format(url) + ) + # Remove the vcs prefix. + scheme = scheme.split('+', 1)[1] + netloc, user_pass = self.get_netloc_and_auth(netloc, scheme) + rev = None + if '@' in path: + path, rev = path.rsplit('@', 1) + url = urllib_parse.urlunsplit((scheme, netloc, path, query, '')) + return url, rev, user_pass + + def make_rev_args(self, username, password): + """ + Return the RevOptions "extra arguments" to use in obtain(). + """ + return [] + + def get_url_rev_options(self, url): + """ + Return the URL and RevOptions object to use in obtain() and in + some cases export(), as a tuple (url, rev_options). + """ + url, rev, user_pass = self.get_url_rev_and_auth(url) + username, password = user_pass + extra_args = self.make_rev_args(username, password) + rev_options = self.make_rev_options(rev, extra_args=extra_args) + + return url, rev_options + + def normalize_url(self, url): + """ + Normalize a URL for comparison by unquoting it and removing any + trailing slash. + """ + return urllib_parse.unquote(url).rstrip('/') + + def compare_urls(self, url1, url2): + """ + Compare two repo URLs for identity, ignoring incidental differences. + """ + return (self.normalize_url(url1) == self.normalize_url(url2)) + + def fetch_new(self, dest, url, rev_options): + """ + Fetch a revision from a repository, in the case that this is the + first fetch from the repository. + + Args: + dest: the directory to fetch the repository to. + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def switch(self, dest, url, rev_options): + """ + Switch the repo at ``dest`` to point to ``URL``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def update(self, dest, url, rev_options): + """ + Update an already-existing repo to the given ``rev_options``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def is_commit_id_equal(self, dest, name): + """ + Return whether the id of the current commit equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + raise NotImplementedError + + def obtain(self, dest): + """ + Install or update in editable mode the package represented by this + VersionControl object. + + Args: + dest: the repository directory in which to install or update. + """ + url, rev_options = self.get_url_rev_options(self.url) + + if not os.path.exists(dest): + self.fetch_new(dest, url, rev_options) + return + + rev_display = rev_options.to_display() + if self.is_repository_directory(dest): + existing_url = self.get_url(dest) + if self.compare_urls(existing_url, url): + logger.debug( + '%s in %s exists, and has correct URL (%s)', + self.repo_name.title(), + display_path(dest), + url, + ) + if not self.is_commit_id_equal(dest, rev_options.rev): + logger.info( + 'Updating %s %s%s', + display_path(dest), + self.repo_name, + rev_display, + ) + self.update(dest, url, rev_options) + else: + logger.info('Skipping because already up-to-date.') + return + + logger.warning( + '%s %s in %s exists with URL %s', + self.name, + self.repo_name, + display_path(dest), + existing_url, + ) + prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', + ('s', 'i', 'w', 'b')) + else: + logger.warning( + 'Directory %s already exists, and is not a %s %s.', + dest, + self.name, + self.repo_name, + ) + prompt = ('(i)gnore, (w)ipe, (b)ackup ', ('i', 'w', 'b')) + + logger.warning( + 'The plan is to install the %s repository %s', + self.name, + url, + ) + response = ask_path_exists('What to do? %s' % prompt[0], prompt[1]) + + if response == 'a': + sys.exit(-1) + + if response == 'w': + logger.warning('Deleting %s', display_path(dest)) + rmtree(dest) + self.fetch_new(dest, url, rev_options) + return + + if response == 'b': + dest_dir = backup_dir(dest) + logger.warning( + 'Backing up %s to %s', display_path(dest), dest_dir, + ) + shutil.move(dest, dest_dir) + self.fetch_new(dest, url, rev_options) + return + + # Do nothing if the response is "i". + if response == 's': + logger.info( + 'Switching %s %s to %s%s', + self.repo_name, + display_path(dest), + url, + rev_display, + ) + self.switch(dest, url, rev_options) + + def unpack(self, location): + """ + Clean up current location and download the url repository + (and vcs infos) into location + """ + if os.path.exists(location): + rmtree(location) + self.obtain(location) + + def get_src_requirement(self, dist, location): + """ + Return a string representing the requirement needed to + redownload the files currently present in location, something + like: + {repository_url}@{revision}#egg={project_name}-{version_identifier} + """ + raise NotImplementedError + + def get_url(self, location): + """ + Return the url used at location + """ + raise NotImplementedError + + def get_revision(self, location): + """ + Return the current commit id of the files at the given location. + """ + raise NotImplementedError + + def run_command(self, cmd, show_stdout=True, cwd=None, + on_returncode='raise', + command_desc=None, + extra_environ=None, spinner=None): + """ + Run a VCS subcommand + This is simply a wrapper around call_subprocess that adds the VCS + command name, and checks that the VCS is available + """ + cmd = [self.name] + cmd + try: + return call_subprocess(cmd, show_stdout, cwd, + on_returncode, + command_desc, extra_environ, + unset_environ=self.unset_environ, + spinner=spinner) + except OSError as e: + # errno.ENOENT = no such file or directory + # In other words, the VCS executable isn't available + if e.errno == errno.ENOENT: + raise BadCommand( + 'Cannot find command %r - do you have ' + '%r installed and in your ' + 'PATH?' % (self.name, self.name)) + else: + raise # re-raise exception if a different error occurred + + @classmethod + def is_repository_directory(cls, path): + """ + Return whether a directory path is a repository directory. + """ + logger.debug('Checking in %s for %s (%s)...', + path, cls.dirname, cls.name) + return os.path.exists(os.path.join(path, cls.dirname)) + + @classmethod + def controls_location(cls, location): + """ + Check if a location is controlled by the vcs. + It is meant to be overridden to implement smarter detection + mechanisms for specific vcs. + + This can do more than is_repository_directory() alone. For example, + the Git override checks that Git is actually available. + """ + return cls.is_repository_directory(location) + + +def get_src_requirement(dist, location): + version_control = vcs.get_backend_from_location(location) + if version_control: + try: + return version_control().get_src_requirement(dist, + location) + except BadCommand: + logger.warning( + 'cannot determine version of editable source in %s ' + '(%s command not found in path)', + location, + version_control.name, + ) + return dist.as_requirement() + logger.warning( + 'cannot determine version of editable source in %s (is not SVN ' + 'checkout, Git clone, Mercurial clone or Bazaar branch)', + location, + ) + return dist.as_requirement() diff --git a/venv/Lib/site-packages/pip/_internal/vcs/bazaar.py b/venv/Lib/site-packages/pip/_internal/vcs/bazaar.py new file mode 100644 index 0000000..3cc66c9 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/vcs/bazaar.py @@ -0,0 +1,112 @@ +from __future__ import absolute_import + +import logging +import os + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.download import path_to_url +from pip._internal.utils.misc import ( + display_path, make_vcs_requirement_url, rmtree, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.vcs import VersionControl, vcs + +logger = logging.getLogger(__name__) + + +class Bazaar(VersionControl): + name = 'bzr' + dirname = '.bzr' + repo_name = 'branch' + schemes = ( + 'bzr', 'bzr+http', 'bzr+https', 'bzr+ssh', 'bzr+sftp', 'bzr+ftp', + 'bzr+lp', + ) + + def __init__(self, url=None, *args, **kwargs): + super(Bazaar, self).__init__(url, *args, **kwargs) + # This is only needed for python <2.7.5 + # Register lp but do not expose as a scheme to support bzr+lp. + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(['lp']) + + def get_base_rev_args(self, rev): + return ['-r', rev] + + def export(self, location): + """ + Export the Bazaar repository at the url to the destination location + """ + # Remove the location to make sure Bazaar can export it correctly + if os.path.exists(location): + rmtree(location) + + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path) + + self.run_command( + ['export', location], + cwd=temp_dir.path, show_stdout=False, + ) + + def fetch_new(self, dest, url, rev_options): + rev_display = rev_options.to_display() + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cmd_args = ['branch', '-q'] + rev_options.to_args() + [url, dest] + self.run_command(cmd_args) + + def switch(self, dest, url, rev_options): + self.run_command(['switch', url], cwd=dest) + + def update(self, dest, url, rev_options): + cmd_args = ['pull', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + def get_url_rev_and_auth(self, url): + # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it + url, rev, user_pass = super(Bazaar, self).get_url_rev_and_auth(url) + if url.startswith('ssh://'): + url = 'bzr+' + url + return url, rev, user_pass + + def get_url(self, location): + urls = self.run_command(['info'], show_stdout=False, cwd=location) + for line in urls.splitlines(): + line = line.strip() + for x in ('checkout of branch: ', + 'parent branch: '): + if line.startswith(x): + repo = line.split(x)[1] + if self._is_local_repository(repo): + return path_to_url(repo) + return repo + return None + + def get_revision(self, location): + revision = self.run_command( + ['revno'], show_stdout=False, cwd=location, + ) + return revision.splitlines()[-1] + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if not repo: + return None + if not repo.lower().startswith('bzr:'): + repo = 'bzr+' + repo + current_rev = self.get_revision(location) + egg_project_name = dist.egg_name().split('-', 1)[0] + return make_vcs_requirement_url(repo, current_rev, egg_project_name) + + def is_commit_id_equal(self, dest, name): + """Always assume the versions don't match""" + return False + + +vcs.register(Bazaar) diff --git a/venv/Lib/site-packages/pip/_internal/vcs/git.py b/venv/Lib/site-packages/pip/_internal/vcs/git.py new file mode 100644 index 0000000..9778539 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/vcs/git.py @@ -0,0 +1,346 @@ +from __future__ import absolute_import + +import logging +import os.path +import re + +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip._internal.exceptions import BadCommand +from pip._internal.utils.compat import samefile +from pip._internal.utils.misc import display_path, make_vcs_requirement_url +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.vcs import VersionControl, vcs + +urlsplit = urllib_parse.urlsplit +urlunsplit = urllib_parse.urlunsplit + + +logger = logging.getLogger(__name__) + + +HASH_REGEX = re.compile('[a-fA-F0-9]{40}') + + +def looks_like_hash(sha): + return bool(HASH_REGEX.match(sha)) + + +class Git(VersionControl): + name = 'git' + dirname = '.git' + repo_name = 'clone' + schemes = ( + 'git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file', + ) + # Prevent the user's environment variables from interfering with pip: + # https://github.com/pypa/pip/issues/1130 + unset_environ = ('GIT_DIR', 'GIT_WORK_TREE') + default_arg_rev = 'HEAD' + + def __init__(self, url=None, *args, **kwargs): + + # Works around an apparent Git bug + # (see https://article.gmane.org/gmane.comp.version-control.git/146500) + if url: + scheme, netloc, path, query, fragment = urlsplit(url) + if scheme.endswith('file'): + initial_slashes = path[:-len(path.lstrip('/'))] + newpath = ( + initial_slashes + + urllib_request.url2pathname(path) + .replace('\\', '/').lstrip('/') + ) + url = urlunsplit((scheme, netloc, newpath, query, fragment)) + after_plus = scheme.find('+') + 1 + url = scheme[:after_plus] + urlunsplit( + (scheme[after_plus:], netloc, newpath, query, fragment), + ) + + super(Git, self).__init__(url, *args, **kwargs) + + def get_base_rev_args(self, rev): + return [rev] + + def get_git_version(self): + VERSION_PFX = 'git version ' + version = self.run_command(['version'], show_stdout=False) + if version.startswith(VERSION_PFX): + version = version[len(VERSION_PFX):].split()[0] + else: + version = '' + # get first 3 positions of the git version becasue + # on windows it is x.y.z.windows.t, and this parses as + # LegacyVersion which always smaller than a Version. + version = '.'.join(version.split('.')[:3]) + return parse_version(version) + + def get_branch(self, location): + """ + Return the current branch, or None if HEAD isn't at a branch + (e.g. detached HEAD). + """ + args = ['rev-parse', '--abbrev-ref', 'HEAD'] + output = self.run_command(args, show_stdout=False, cwd=location) + branch = output.strip() + + if branch == 'HEAD': + return None + + return branch + + def export(self, location): + """Export the Git repository at the url to the destination location""" + if not location.endswith('/'): + location = location + '/' + + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path) + self.run_command( + ['checkout-index', '-a', '-f', '--prefix', location], + show_stdout=False, cwd=temp_dir.path + ) + + def get_revision_sha(self, dest, rev): + """ + Return (sha_or_none, is_branch), where sha_or_none is a commit hash + if the revision names a remote branch or tag, otherwise None. + + Args: + dest: the repository directory. + rev: the revision name. + """ + # Pass rev to pre-filter the list. + output = self.run_command(['show-ref', rev], cwd=dest, + show_stdout=False, on_returncode='ignore') + refs = {} + for line in output.strip().splitlines(): + try: + sha, ref = line.split() + except ValueError: + # Include the offending line to simplify troubleshooting if + # this error ever occurs. + raise ValueError('unexpected show-ref line: {!r}'.format(line)) + + refs[ref] = sha + + branch_ref = 'refs/remotes/origin/{}'.format(rev) + tag_ref = 'refs/tags/{}'.format(rev) + + sha = refs.get(branch_ref) + if sha is not None: + return (sha, True) + + sha = refs.get(tag_ref) + + return (sha, False) + + def resolve_revision(self, dest, url, rev_options): + """ + Resolve a revision to a new RevOptions object with the SHA1 of the + branch, tag, or ref if found. + + Args: + rev_options: a RevOptions object. + """ + rev = rev_options.arg_rev + sha, is_branch = self.get_revision_sha(dest, rev) + + if sha is not None: + rev_options = rev_options.make_new(sha) + rev_options.branch_name = rev if is_branch else None + + return rev_options + + # Do not show a warning for the common case of something that has + # the form of a Git commit hash. + if not looks_like_hash(rev): + logger.warning( + "Did not find branch or tag '%s', assuming revision or ref.", + rev, + ) + + if not rev.startswith('refs/'): + return rev_options + + # If it looks like a ref, we have to fetch it explicitly. + self.run_command( + ['fetch', '-q', url] + rev_options.to_args(), + cwd=dest, + ) + # Change the revision to the SHA of the ref we fetched + sha = self.get_revision(dest, rev='FETCH_HEAD') + rev_options = rev_options.make_new(sha) + + return rev_options + + def is_commit_id_equal(self, dest, name): + """ + Return whether the current commit hash equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + if not name: + # Then avoid an unnecessary subprocess call. + return False + + return self.get_revision(dest) == name + + def fetch_new(self, dest, url, rev_options): + rev_display = rev_options.to_display() + logger.info( + 'Cloning %s%s to %s', url, rev_display, display_path(dest), + ) + self.run_command(['clone', '-q', url, dest]) + + if rev_options.rev: + # Then a specific revision was requested. + rev_options = self.resolve_revision(dest, url, rev_options) + branch_name = getattr(rev_options, 'branch_name', None) + if branch_name is None: + # Only do a checkout if the current commit id doesn't match + # the requested revision. + if not self.is_commit_id_equal(dest, rev_options.rev): + cmd_args = ['checkout', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + elif self.get_branch(dest) != branch_name: + # Then a specific branch was requested, and that branch + # is not yet checked out. + track_branch = 'origin/{}'.format(branch_name) + cmd_args = [ + 'checkout', '-b', branch_name, '--track', track_branch, + ] + self.run_command(cmd_args, cwd=dest) + + #: repo may contain submodules + self.update_submodules(dest) + + def switch(self, dest, url, rev_options): + self.run_command(['config', 'remote.origin.url', url], cwd=dest) + cmd_args = ['checkout', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + self.update_submodules(dest) + + def update(self, dest, url, rev_options): + # First fetch changes from the default remote + if self.get_git_version() >= parse_version('1.9.0'): + # fetch tags in addition to everything else + self.run_command(['fetch', '-q', '--tags'], cwd=dest) + else: + self.run_command(['fetch', '-q'], cwd=dest) + # Then reset to wanted revision (maybe even origin/master) + rev_options = self.resolve_revision(dest, url, rev_options) + cmd_args = ['reset', '--hard', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + #: update submodules + self.update_submodules(dest) + + def get_url(self, location): + """Return URL of the first remote encountered.""" + remotes = self.run_command( + ['config', '--get-regexp', r'remote\..*\.url'], + show_stdout=False, cwd=location, + ) + remotes = remotes.splitlines() + found_remote = remotes[0] + for remote in remotes: + if remote.startswith('remote.origin.url '): + found_remote = remote + break + url = found_remote.split(' ')[1] + return url.strip() + + def get_revision(self, location, rev=None): + if rev is None: + rev = 'HEAD' + current_rev = self.run_command( + ['rev-parse', rev], show_stdout=False, cwd=location, + ) + return current_rev.strip() + + def _get_subdirectory(self, location): + """Return the relative path of setup.py to the git repo root.""" + # find the repo root + git_dir = self.run_command(['rev-parse', '--git-dir'], + show_stdout=False, cwd=location).strip() + if not os.path.isabs(git_dir): + git_dir = os.path.join(location, git_dir) + root_dir = os.path.join(git_dir, '..') + # find setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + # relative path of setup.py to repo root + if samefile(root_dir, location): + return None + return os.path.relpath(location, root_dir) + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if not repo.lower().startswith('git:'): + repo = 'git+' + repo + current_rev = self.get_revision(location) + egg_project_name = dist.egg_name().split('-', 1)[0] + subdir = self._get_subdirectory(location) + req = make_vcs_requirement_url(repo, current_rev, egg_project_name, + subdir=subdir) + + return req + + def get_url_rev_and_auth(self, url): + """ + Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. + That's required because although they use SSH they sometimes don't + work with a ssh:// scheme (e.g. GitHub). But we need a scheme for + parsing. Hence we remove it again afterwards and return it as a stub. + """ + if '://' not in url: + assert 'file:' not in url + url = url.replace('git+', 'git+ssh://') + url, rev, user_pass = super(Git, self).get_url_rev_and_auth(url) + url = url.replace('ssh://', '') + else: + url, rev, user_pass = super(Git, self).get_url_rev_and_auth(url) + + return url, rev, user_pass + + def update_submodules(self, location): + if not os.path.exists(os.path.join(location, '.gitmodules')): + return + self.run_command( + ['submodule', 'update', '--init', '--recursive', '-q'], + cwd=location, + ) + + @classmethod + def controls_location(cls, location): + if super(Git, cls).controls_location(location): + return True + try: + r = cls().run_command(['rev-parse'], + cwd=location, + show_stdout=False, + on_returncode='ignore') + return not r + except BadCommand: + logger.debug("could not determine if %s is under git control " + "because git is not available", location) + return False + + +vcs.register(Git) diff --git a/venv/Lib/site-packages/pip/_internal/vcs/mercurial.py b/venv/Lib/site-packages/pip/_internal/vcs/mercurial.py new file mode 100644 index 0000000..17cfb67 --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/vcs/mercurial.py @@ -0,0 +1,101 @@ +from __future__ import absolute_import + +import logging +import os + +from pip._vendor.six.moves import configparser + +from pip._internal.download import path_to_url +from pip._internal.utils.misc import display_path, make_vcs_requirement_url +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.vcs import VersionControl, vcs + +logger = logging.getLogger(__name__) + + +class Mercurial(VersionControl): + name = 'hg' + dirname = '.hg' + repo_name = 'clone' + schemes = ('hg', 'hg+http', 'hg+https', 'hg+ssh', 'hg+static-http') + + def get_base_rev_args(self, rev): + return [rev] + + def export(self, location): + """Export the Hg repository at the url to the destination location""" + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path) + + self.run_command( + ['archive', location], show_stdout=False, cwd=temp_dir.path + ) + + def fetch_new(self, dest, url, rev_options): + rev_display = rev_options.to_display() + logger.info( + 'Cloning hg %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(['clone', '--noupdate', '-q', url, dest]) + cmd_args = ['update', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + def switch(self, dest, url, rev_options): + repo_config = os.path.join(dest, self.dirname, 'hgrc') + config = configparser.SafeConfigParser() + try: + config.read(repo_config) + config.set('paths', 'default', url) + with open(repo_config, 'w') as config_file: + config.write(config_file) + except (OSError, configparser.NoSectionError) as exc: + logger.warning( + 'Could not switch Mercurial repository to %s: %s', url, exc, + ) + else: + cmd_args = ['update', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + def update(self, dest, url, rev_options): + self.run_command(['pull', '-q'], cwd=dest) + cmd_args = ['update', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + def get_url(self, location): + url = self.run_command( + ['showconfig', 'paths.default'], + show_stdout=False, cwd=location).strip() + if self._is_local_repository(url): + url = path_to_url(url) + return url.strip() + + def get_revision(self, location): + current_revision = self.run_command( + ['parents', '--template={rev}'], + show_stdout=False, cwd=location).strip() + return current_revision + + def get_revision_hash(self, location): + current_rev_hash = self.run_command( + ['parents', '--template={node}'], + show_stdout=False, cwd=location).strip() + return current_rev_hash + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if not repo.lower().startswith('hg:'): + repo = 'hg+' + repo + current_rev_hash = self.get_revision_hash(location) + egg_project_name = dist.egg_name().split('-', 1)[0] + return make_vcs_requirement_url(repo, current_rev_hash, + egg_project_name) + + def is_commit_id_equal(self, dest, name): + """Always assume the versions don't match""" + return False + + +vcs.register(Mercurial) diff --git a/venv/Lib/site-packages/pip/_internal/vcs/subversion.py b/venv/Lib/site-packages/pip/_internal/vcs/subversion.py new file mode 100644 index 0000000..6f7cb5d --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/vcs/subversion.py @@ -0,0 +1,213 @@ +from __future__ import absolute_import + +import logging +import os +import re + +from pip._internal.models.link import Link +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + display_path, make_vcs_requirement_url, rmtree, split_auth_from_netloc, +) +from pip._internal.vcs import VersionControl, vcs + +_svn_xml_url_re = re.compile('url="([^"]+)"') +_svn_rev_re = re.compile(r'committed-rev="(\d+)"') +_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"') +_svn_info_xml_url_re = re.compile(r'<url>(.*)</url>') + + +logger = logging.getLogger(__name__) + + +class Subversion(VersionControl): + name = 'svn' + dirname = '.svn' + repo_name = 'checkout' + schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn') + + def get_base_rev_args(self, rev): + return ['-r', rev] + + def export(self, location): + """Export the svn repository at the url to the destination location""" + url, rev_options = self.get_url_rev_options(self.url) + + logger.info('Exporting svn repository %s to %s', url, location) + with indent_log(): + if os.path.exists(location): + # Subversion doesn't like to check out over an existing + # directory --force fixes this, but was only added in svn 1.5 + rmtree(location) + cmd_args = ['export'] + rev_options.to_args() + [url, location] + self.run_command(cmd_args, show_stdout=False) + + def fetch_new(self, dest, url, rev_options): + rev_display = rev_options.to_display() + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cmd_args = ['checkout', '-q'] + rev_options.to_args() + [url, dest] + self.run_command(cmd_args) + + def switch(self, dest, url, rev_options): + cmd_args = ['switch'] + rev_options.to_args() + [url, dest] + self.run_command(cmd_args) + + def update(self, dest, url, rev_options): + cmd_args = ['update'] + rev_options.to_args() + [dest] + self.run_command(cmd_args) + + def get_location(self, dist, dependency_links): + for url in dependency_links: + egg_fragment = Link(url).egg_fragment + if not egg_fragment: + continue + if '-' in egg_fragment: + # FIXME: will this work when a package has - in the name? + key = '-'.join(egg_fragment.split('-')[:-1]).lower() + else: + key = egg_fragment + if key == dist.key: + return url.split('#', 1)[0] + return None + + def get_revision(self, location): + """ + Return the maximum revision for all files under a given location + """ + # Note: taken from setuptools.command.egg_info + revision = 0 + + for base, dirs, files in os.walk(location): + if self.dirname not in dirs: + dirs[:] = [] + continue # no sense walking uncontrolled subdirs + dirs.remove(self.dirname) + entries_fn = os.path.join(base, self.dirname, 'entries') + if not os.path.exists(entries_fn): + # FIXME: should we warn? + continue + + dirurl, localrev = self._get_svn_url_rev(base) + + if base == location: + base = dirurl + '/' # save the root url + elif not dirurl or not dirurl.startswith(base): + dirs[:] = [] + continue # not part of the same svn tree, skip it + revision = max(revision, localrev) + return revision + + def get_netloc_and_auth(self, netloc, scheme): + """ + This override allows the auth information to be passed to svn via the + --username and --password options instead of via the URL. + """ + if scheme == 'ssh': + # The --username and --password options can't be used for + # svn+ssh URLs, so keep the auth information in the URL. + return super(Subversion, self).get_netloc_and_auth( + netloc, scheme) + + return split_auth_from_netloc(netloc) + + def get_url_rev_and_auth(self, url): + # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it + url, rev, user_pass = super(Subversion, self).get_url_rev_and_auth(url) + if url.startswith('ssh://'): + url = 'svn+' + url + return url, rev, user_pass + + def make_rev_args(self, username, password): + extra_args = [] + if username: + extra_args += ['--username', username] + if password: + extra_args += ['--password', password] + + return extra_args + + def get_url(self, location): + # In cases where the source is in a subdirectory, not alongside + # setup.py we have to look up in the location until we find a real + # setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + + return self._get_svn_url_rev(location)[0] + + def _get_svn_url_rev(self, location): + from pip._internal.exceptions import InstallationError + + entries_path = os.path.join(location, self.dirname, 'entries') + if os.path.exists(entries_path): + with open(entries_path) as f: + data = f.read() + else: # subversion >= 1.7 does not have the 'entries' file + data = '' + + if (data.startswith('8') or + data.startswith('9') or + data.startswith('10')): + data = list(map(str.splitlines, data.split('\n\x0c\n'))) + del data[0][0] # get rid of the '8' + url = data[0][3] + revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0] + elif data.startswith('<?xml'): + match = _svn_xml_url_re.search(data) + if not match: + raise ValueError('Badly formatted data: %r' % data) + url = match.group(1) # get repository URL + revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0] + else: + try: + # subversion >= 1.7 + xml = self.run_command( + ['info', '--xml', location], + show_stdout=False, + ) + url = _svn_info_xml_url_re.search(xml).group(1) + revs = [ + int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml) + ] + except InstallationError: + url, revs = None, [] + + if revs: + rev = max(revs) + else: + rev = 0 + + return url, rev + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if repo is None: + return None + repo = 'svn+' + repo + rev = self.get_revision(location) + # FIXME: why not project name? + egg_project_name = dist.egg_name().split('-', 1)[0] + return make_vcs_requirement_url(repo, rev, egg_project_name) + + def is_commit_id_equal(self, dest, name): + """Always assume the versions don't match""" + return False + + +vcs.register(Subversion) diff --git a/venv/Lib/site-packages/pip/_internal/wheel.py b/venv/Lib/site-packages/pip/_internal/wheel.py new file mode 100644 index 0000000..5ce890e --- /dev/null +++ b/venv/Lib/site-packages/pip/_internal/wheel.py @@ -0,0 +1,831 @@ +""" +Support for installing and building the "wheel" binary package format. +""" +from __future__ import absolute_import + +import collections +import compileall +import csv +import hashlib +import logging +import os.path +import re +import shutil +import stat +import sys +import warnings +from base64 import urlsafe_b64encode +from email.parser import Parser + +from pip._vendor import pkg_resources +from pip._vendor.distlib.scripts import ScriptMaker +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.six import StringIO + +from pip._internal import pep425tags +from pip._internal.download import path_to_url, unpack_url +from pip._internal.exceptions import ( + InstallationError, InvalidWheelFilename, UnsupportedWheel, +) +from pip._internal.locations import ( + PIP_DELETE_MARKER_FILENAME, distutils_scheme, +) +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + call_subprocess, captured_stdout, ensure_dir, read_chunks, +) +from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.ui import open_spinner + +if MYPY_CHECK_RUNNING: + from typing import Dict, List, Optional # noqa: F401 + +wheel_ext = '.whl' + +VERSION_COMPATIBLE = (1, 0) + + +logger = logging.getLogger(__name__) + + +def rehash(path, blocksize=1 << 20): + """Return (hash, length) for path using hashlib.sha256()""" + h = hashlib.sha256() + length = 0 + with open(path, 'rb') as f: + for block in read_chunks(f, size=blocksize): + length += len(block) + h.update(block) + digest = 'sha256=' + urlsafe_b64encode( + h.digest() + ).decode('latin1').rstrip('=') + return (digest, length) + + +def open_for_csv(name, mode): + if sys.version_info[0] < 3: + nl = {} + bin = 'b' + else: + nl = {'newline': ''} + bin = '' + return open(name, mode + bin, **nl) + + +def fix_script(path): + """Replace #!python with #!/path/to/python + Return True if file was changed.""" + # XXX RECORD hashes will need to be updated + if os.path.isfile(path): + with open(path, 'rb') as script: + firstline = script.readline() + if not firstline.startswith(b'#!python'): + return False + exename = sys.executable.encode(sys.getfilesystemencoding()) + firstline = b'#!' + exename + os.linesep.encode("ascii") + rest = script.read() + with open(path, 'wb') as script: + script.write(firstline) + script.write(rest) + return True + + +dist_info_re = re.compile(r"""^(?P<namever>(?P<name>.+?)(-(?P<ver>.+?))?) + \.dist-info$""", re.VERBOSE) + + +def root_is_purelib(name, wheeldir): + """ + Return True if the extracted wheel in wheeldir should go into purelib. + """ + name_folded = name.replace("-", "_") + for item in os.listdir(wheeldir): + match = dist_info_re.match(item) + if match and match.group('name') == name_folded: + with open(os.path.join(wheeldir, item, 'WHEEL')) as wheel: + for line in wheel: + line = line.lower().rstrip() + if line == "root-is-purelib: true": + return True + return False + + +def get_entrypoints(filename): + if not os.path.exists(filename): + return {}, {} + + # This is done because you can pass a string to entry_points wrappers which + # means that they may or may not be valid INI files. The attempt here is to + # strip leading and trailing whitespace in order to make them valid INI + # files. + with open(filename) as fp: + data = StringIO() + for line in fp: + data.write(line.strip()) + data.write("\n") + data.seek(0) + + # get the entry points and then the script names + entry_points = pkg_resources.EntryPoint.parse_map(data) + console = entry_points.get('console_scripts', {}) + gui = entry_points.get('gui_scripts', {}) + + def _split_ep(s): + """get the string representation of EntryPoint, remove space and split + on '='""" + return str(s).replace(" ", "").split("=") + + # convert the EntryPoint objects into strings with module:function + console = dict(_split_ep(v) for v in console.values()) + gui = dict(_split_ep(v) for v in gui.values()) + return console, gui + + +def message_about_scripts_not_on_PATH(scripts): + # type: (List[str]) -> Optional[str] + """Determine if any scripts are not on PATH and format a warning. + + Returns a warning message if one or more scripts are not on PATH, + otherwise None. + """ + if not scripts: + return None + + # Group scripts by the path they were installed in + grouped_by_dir = collections.defaultdict(set) # type: Dict[str, set] + for destfile in scripts: + parent_dir = os.path.dirname(destfile) + script_name = os.path.basename(destfile) + grouped_by_dir[parent_dir].add(script_name) + + # We don't want to warn for directories that are on PATH. + not_warn_dirs = [ + os.path.normcase(i).rstrip(os.sep) for i in + os.environ.get("PATH", "").split(os.pathsep) + ] + # If an executable sits with sys.executable, we don't warn for it. + # This covers the case of venv invocations without activating the venv. + not_warn_dirs.append(os.path.normcase(os.path.dirname(sys.executable))) + warn_for = { + parent_dir: scripts for parent_dir, scripts in grouped_by_dir.items() + if os.path.normcase(parent_dir) not in not_warn_dirs + } + if not warn_for: + return None + + # Format a message + msg_lines = [] + for parent_dir, scripts in warn_for.items(): + scripts = sorted(scripts) + if len(scripts) == 1: + start_text = "script {} is".format(scripts[0]) + else: + start_text = "scripts {} are".format( + ", ".join(scripts[:-1]) + " and " + scripts[-1] + ) + + msg_lines.append( + "The {} installed in '{}' which is not on PATH." + .format(start_text, parent_dir) + ) + + last_line_fmt = ( + "Consider adding {} to PATH or, if you prefer " + "to suppress this warning, use --no-warn-script-location." + ) + if len(msg_lines) == 1: + msg_lines.append(last_line_fmt.format("this directory")) + else: + msg_lines.append(last_line_fmt.format("these directories")) + + # Returns the formatted multiline message + return "\n".join(msg_lines) + + +def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None, + pycompile=True, scheme=None, isolated=False, prefix=None, + warn_script_location=True): + """Install a wheel""" + + if not scheme: + scheme = distutils_scheme( + name, user=user, home=home, root=root, isolated=isolated, + prefix=prefix, + ) + + if root_is_purelib(name, wheeldir): + lib_dir = scheme['purelib'] + else: + lib_dir = scheme['platlib'] + + info_dir = [] + data_dirs = [] + source = wheeldir.rstrip(os.path.sep) + os.path.sep + + # Record details of the files moved + # installed = files copied from the wheel to the destination + # changed = files changed while installing (scripts #! line typically) + # generated = files newly generated during the install (script wrappers) + installed = {} + changed = set() + generated = [] + + # Compile all of the pyc files that we're going to be installing + if pycompile: + with captured_stdout() as stdout: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore') + compileall.compile_dir(source, force=True, quiet=True) + logger.debug(stdout.getvalue()) + + def normpath(src, p): + return os.path.relpath(src, p).replace(os.path.sep, '/') + + def record_installed(srcfile, destfile, modified=False): + """Map archive RECORD paths to installation RECORD paths.""" + oldpath = normpath(srcfile, wheeldir) + newpath = normpath(destfile, lib_dir) + installed[oldpath] = newpath + if modified: + changed.add(destfile) + + def clobber(source, dest, is_base, fixer=None, filter=None): + ensure_dir(dest) # common for the 'include' path + + for dir, subdirs, files in os.walk(source): + basedir = dir[len(source):].lstrip(os.path.sep) + destdir = os.path.join(dest, basedir) + if is_base and basedir.split(os.path.sep, 1)[0].endswith('.data'): + continue + for s in subdirs: + destsubdir = os.path.join(dest, basedir, s) + if is_base and basedir == '' and destsubdir.endswith('.data'): + data_dirs.append(s) + continue + elif (is_base and + s.endswith('.dist-info') and + canonicalize_name(s).startswith( + canonicalize_name(req.name))): + assert not info_dir, ('Multiple .dist-info directories: ' + + destsubdir + ', ' + + ', '.join(info_dir)) + info_dir.append(destsubdir) + for f in files: + # Skip unwanted files + if filter and filter(f): + continue + srcfile = os.path.join(dir, f) + destfile = os.path.join(dest, basedir, f) + # directory creation is lazy and after the file filtering above + # to ensure we don't install empty dirs; empty dirs can't be + # uninstalled. + ensure_dir(destdir) + + # copyfile (called below) truncates the destination if it + # exists and then writes the new contents. This is fine in most + # cases, but can cause a segfault if pip has loaded a shared + # object (e.g. from pyopenssl through its vendored urllib3) + # Since the shared object is mmap'd an attempt to call a + # symbol in it will then cause a segfault. Unlinking the file + # allows writing of new contents while allowing the process to + # continue to use the old copy. + if os.path.exists(destfile): + os.unlink(destfile) + + # We use copyfile (not move, copy, or copy2) to be extra sure + # that we are not moving directories over (copyfile fails for + # directories) as well as to ensure that we are not copying + # over any metadata because we want more control over what + # metadata we actually copy over. + shutil.copyfile(srcfile, destfile) + + # Copy over the metadata for the file, currently this only + # includes the atime and mtime. + st = os.stat(srcfile) + if hasattr(os, "utime"): + os.utime(destfile, (st.st_atime, st.st_mtime)) + + # If our file is executable, then make our destination file + # executable. + if os.access(srcfile, os.X_OK): + st = os.stat(srcfile) + permissions = ( + st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + ) + os.chmod(destfile, permissions) + + changed = False + if fixer: + changed = fixer(destfile) + record_installed(srcfile, destfile, changed) + + clobber(source, lib_dir, True) + + assert info_dir, "%s .dist-info directory not found" % req + + # Get the defined entry points + ep_file = os.path.join(info_dir[0], 'entry_points.txt') + console, gui = get_entrypoints(ep_file) + + def is_entrypoint_wrapper(name): + # EP, EP.exe and EP-script.py are scripts generated for + # entry point EP by setuptools + if name.lower().endswith('.exe'): + matchname = name[:-4] + elif name.lower().endswith('-script.py'): + matchname = name[:-10] + elif name.lower().endswith(".pya"): + matchname = name[:-4] + else: + matchname = name + # Ignore setuptools-generated scripts + return (matchname in console or matchname in gui) + + for datadir in data_dirs: + fixer = None + filter = None + for subdir in os.listdir(os.path.join(wheeldir, datadir)): + fixer = None + if subdir == 'scripts': + fixer = fix_script + filter = is_entrypoint_wrapper + source = os.path.join(wheeldir, datadir, subdir) + dest = scheme[subdir] + clobber(source, dest, False, fixer=fixer, filter=filter) + + maker = ScriptMaker(None, scheme['scripts']) + + # Ensure old scripts are overwritten. + # See https://github.com/pypa/pip/issues/1800 + maker.clobber = True + + # Ensure we don't generate any variants for scripts because this is almost + # never what somebody wants. + # See https://bitbucket.org/pypa/distlib/issue/35/ + maker.variants = {''} + + # This is required because otherwise distlib creates scripts that are not + # executable. + # See https://bitbucket.org/pypa/distlib/issue/32/ + maker.set_mode = True + + # Simplify the script and fix the fact that the default script swallows + # every single stack trace. + # See https://bitbucket.org/pypa/distlib/issue/34/ + # See https://bitbucket.org/pypa/distlib/issue/33/ + def _get_script_text(entry): + if entry.suffix is None: + raise InstallationError( + "Invalid script entry point: %s for req: %s - A callable " + "suffix is required. Cf https://packaging.python.org/en/" + "latest/distributing.html#console-scripts for more " + "information." % (entry, req) + ) + return maker.script_template % { + "module": entry.prefix, + "import_name": entry.suffix.split(".")[0], + "func": entry.suffix, + } + + maker._get_script_text = _get_script_text + maker.script_template = r"""# -*- coding: utf-8 -*- +import re +import sys + +from %(module)s import %(import_name)s + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(%(func)s()) +""" + + # Special case pip and setuptools to generate versioned wrappers + # + # The issue is that some projects (specifically, pip and setuptools) use + # code in setup.py to create "versioned" entry points - pip2.7 on Python + # 2.7, pip3.3 on Python 3.3, etc. But these entry points are baked into + # the wheel metadata at build time, and so if the wheel is installed with + # a *different* version of Python the entry points will be wrong. The + # correct fix for this is to enhance the metadata to be able to describe + # such versioned entry points, but that won't happen till Metadata 2.0 is + # available. + # In the meantime, projects using versioned entry points will either have + # incorrect versioned entry points, or they will not be able to distribute + # "universal" wheels (i.e., they will need a wheel per Python version). + # + # Because setuptools and pip are bundled with _ensurepip and virtualenv, + # we need to use universal wheels. So, as a stopgap until Metadata 2.0, we + # override the versioned entry points in the wheel and generate the + # correct ones. This code is purely a short-term measure until Metadata 2.0 + # is available. + # + # To add the level of hack in this section of code, in order to support + # ensurepip this code will look for an ``ENSUREPIP_OPTIONS`` environment + # variable which will control which version scripts get installed. + # + # ENSUREPIP_OPTIONS=altinstall + # - Only pipX.Y and easy_install-X.Y will be generated and installed + # ENSUREPIP_OPTIONS=install + # - pipX.Y, pipX, easy_install-X.Y will be generated and installed. Note + # that this option is technically if ENSUREPIP_OPTIONS is set and is + # not altinstall + # DEFAULT + # - The default behavior is to install pip, pipX, pipX.Y, easy_install + # and easy_install-X.Y. + pip_script = console.pop('pip', None) + if pip_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + spec = 'pip = ' + pip_script + generated.extend(maker.make(spec)) + + if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall": + spec = 'pip%s = %s' % (sys.version[:1], pip_script) + generated.extend(maker.make(spec)) + + spec = 'pip%s = %s' % (sys.version[:3], pip_script) + generated.extend(maker.make(spec)) + # Delete any other versioned pip entry points + pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)] + for k in pip_ep: + del console[k] + easy_install_script = console.pop('easy_install', None) + if easy_install_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + spec = 'easy_install = ' + easy_install_script + generated.extend(maker.make(spec)) + + spec = 'easy_install-%s = %s' % (sys.version[:3], easy_install_script) + generated.extend(maker.make(spec)) + # Delete any other versioned easy_install entry points + easy_install_ep = [ + k for k in console if re.match(r'easy_install(-\d\.\d)?$', k) + ] + for k in easy_install_ep: + del console[k] + + # Generate the console and GUI entry points specified in the wheel + if len(console) > 0: + generated_console_scripts = maker.make_multiple( + ['%s = %s' % kv for kv in console.items()] + ) + generated.extend(generated_console_scripts) + + if warn_script_location: + msg = message_about_scripts_not_on_PATH(generated_console_scripts) + if msg is not None: + logger.warning(msg) + + if len(gui) > 0: + generated.extend( + maker.make_multiple( + ['%s = %s' % kv for kv in gui.items()], + {'gui': True} + ) + ) + + # Record pip as the installer + installer = os.path.join(info_dir[0], 'INSTALLER') + temp_installer = os.path.join(info_dir[0], 'INSTALLER.pip') + with open(temp_installer, 'wb') as installer_file: + installer_file.write(b'pip\n') + shutil.move(temp_installer, installer) + generated.append(installer) + + # Record details of all files installed + record = os.path.join(info_dir[0], 'RECORD') + temp_record = os.path.join(info_dir[0], 'RECORD.pip') + with open_for_csv(record, 'r') as record_in: + with open_for_csv(temp_record, 'w+') as record_out: + reader = csv.reader(record_in) + writer = csv.writer(record_out) + outrows = [] + for row in reader: + row[0] = installed.pop(row[0], row[0]) + if row[0] in changed: + row[1], row[2] = rehash(row[0]) + outrows.append(tuple(row)) + for f in generated: + digest, length = rehash(f) + outrows.append((normpath(f, lib_dir), digest, length)) + for f in installed: + outrows.append((installed[f], '', '')) + for row in sorted(outrows): + writer.writerow(row) + shutil.move(temp_record, record) + + +def wheel_version(source_dir): + """ + Return the Wheel-Version of an extracted wheel, if possible. + + Otherwise, return False if we couldn't parse / extract it. + """ + try: + dist = [d for d in pkg_resources.find_on_path(None, source_dir)][0] + + wheel_data = dist.get_metadata('WHEEL') + wheel_data = Parser().parsestr(wheel_data) + + version = wheel_data['Wheel-Version'].strip() + version = tuple(map(int, version.split('.'))) + return version + except Exception: + return False + + +def check_compatibility(version, name): + """ + Raises errors or warns if called with an incompatible Wheel-Version. + + Pip should refuse to install a Wheel-Version that's a major series + ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when + installing a version only minor version ahead (e.g 1.2 > 1.1). + + version: a 2-tuple representing a Wheel-Version (Major, Minor) + name: name of wheel or package to raise exception about + + :raises UnsupportedWheel: when an incompatible Wheel-Version is given + """ + if not version: + raise UnsupportedWheel( + "%s is in an unsupported or invalid wheel" % name + ) + if version[0] > VERSION_COMPATIBLE[0]: + raise UnsupportedWheel( + "%s's Wheel-Version (%s) is not compatible with this version " + "of pip" % (name, '.'.join(map(str, version))) + ) + elif version > VERSION_COMPATIBLE: + logger.warning( + 'Installing from a newer Wheel-Version (%s)', + '.'.join(map(str, version)), + ) + + +class Wheel(object): + """A wheel file""" + + # TODO: maybe move the install code into this class + + wheel_file_re = re.compile( + r"""^(?P<namever>(?P<name>.+?)-(?P<ver>.*?)) + ((-(?P<build>\d[^-]*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?) + \.whl|\.dist-info)$""", + re.VERBOSE + ) + + def __init__(self, filename): + """ + :raises InvalidWheelFilename: when the filename is invalid for a wheel + """ + wheel_info = self.wheel_file_re.match(filename) + if not wheel_info: + raise InvalidWheelFilename( + "%s is not a valid wheel filename." % filename + ) + self.filename = filename + self.name = wheel_info.group('name').replace('_', '-') + # we'll assume "_" means "-" due to wheel naming scheme + # (https://github.com/pypa/pip/issues/1150) + self.version = wheel_info.group('ver').replace('_', '-') + self.build_tag = wheel_info.group('build') + self.pyversions = wheel_info.group('pyver').split('.') + self.abis = wheel_info.group('abi').split('.') + self.plats = wheel_info.group('plat').split('.') + + # All the tag combinations from this file + self.file_tags = { + (x, y, z) for x in self.pyversions + for y in self.abis for z in self.plats + } + + def support_index_min(self, tags=None): + """ + Return the lowest index that one of the wheel's file_tag combinations + achieves in the supported_tags list e.g. if there are 8 supported tags, + and one of the file tags is first in the list, then return 0. Returns + None is the wheel is not supported. + """ + if tags is None: # for mock + tags = pep425tags.get_supported() + indexes = [tags.index(c) for c in self.file_tags if c in tags] + return min(indexes) if indexes else None + + def supported(self, tags=None): + """Is this wheel supported on this system?""" + if tags is None: # for mock + tags = pep425tags.get_supported() + return bool(set(tags).intersection(self.file_tags)) + + +class WheelBuilder(object): + """Build wheels from a RequirementSet.""" + + def __init__(self, finder, preparer, wheel_cache, + build_options=None, global_options=None, no_clean=False): + self.finder = finder + self.preparer = preparer + self.wheel_cache = wheel_cache + + self._wheel_dir = preparer.wheel_download_dir + + self.build_options = build_options or [] + self.global_options = global_options or [] + self.no_clean = no_clean + + def _build_one(self, req, output_dir, python_tag=None): + """Build one wheel. + + :return: The filename of the built wheel, or None if the build failed. + """ + # Install build deps into temporary directory (PEP 518) + with req.build_env: + return self._build_one_inside_env(req, output_dir, + python_tag=python_tag) + + def _build_one_inside_env(self, req, output_dir, python_tag=None): + with TempDirectory(kind="wheel") as temp_dir: + if self.__build_one(req, temp_dir.path, python_tag=python_tag): + try: + wheel_name = os.listdir(temp_dir.path)[0] + wheel_path = os.path.join(output_dir, wheel_name) + shutil.move( + os.path.join(temp_dir.path, wheel_name), wheel_path + ) + logger.info('Stored in directory: %s', output_dir) + return wheel_path + except Exception: + pass + # Ignore return, we can't do anything else useful. + self._clean_one(req) + return None + + def _base_setup_args(self, req): + # NOTE: Eventually, we'd want to also -S to the flags here, when we're + # isolating. Currently, it breaks Python in virtualenvs, because it + # relies on site.py to find parts of the standard library outside the + # virtualenv. + return [ + sys.executable, '-u', '-c', + SETUPTOOLS_SHIM % req.setup_py + ] + list(self.global_options) + + def __build_one(self, req, tempd, python_tag=None): + base_args = self._base_setup_args(req) + + spin_message = 'Running setup.py bdist_wheel for %s' % (req.name,) + with open_spinner(spin_message) as spinner: + logger.debug('Destination directory: %s', tempd) + wheel_args = base_args + ['bdist_wheel', '-d', tempd] \ + + self.build_options + + if python_tag is not None: + wheel_args += ["--python-tag", python_tag] + + try: + call_subprocess(wheel_args, cwd=req.setup_py_dir, + show_stdout=False, spinner=spinner) + return True + except Exception: + spinner.finish("error") + logger.error('Failed building wheel for %s', req.name) + return False + + def _clean_one(self, req): + base_args = self._base_setup_args(req) + + logger.info('Running setup.py clean for %s', req.name) + clean_args = base_args + ['clean', '--all'] + try: + call_subprocess(clean_args, cwd=req.source_dir, show_stdout=False) + return True + except Exception: + logger.error('Failed cleaning build dir for %s', req.name) + return False + + def build(self, requirements, session, autobuilding=False): + """Build wheels. + + :param unpack: If True, replace the sdist we built from with the + newly built wheel, in preparation for installation. + :return: True if all the wheels built correctly. + """ + from pip._internal import index + from pip._internal.models.link import Link + + building_is_possible = self._wheel_dir or ( + autobuilding and self.wheel_cache.cache_dir + ) + assert building_is_possible + + buildset = [] + format_control = self.finder.format_control + for req in requirements: + if req.constraint: + continue + if req.is_wheel: + if not autobuilding: + logger.info( + 'Skipping %s, due to already being wheel.', req.name, + ) + elif autobuilding and req.editable: + pass + elif autobuilding and not req.source_dir: + pass + elif autobuilding and req.link and not req.link.is_artifact: + # VCS checkout. Build wheel just for this run. + buildset.append((req, True)) + else: + ephem_cache = False + if autobuilding: + link = req.link + base, ext = link.splitext() + if index.egg_info_matches(base, None, link) is None: + # E.g. local directory. Build wheel just for this run. + ephem_cache = True + if "binary" not in format_control.get_allowed_formats( + canonicalize_name(req.name)): + logger.info( + "Skipping bdist_wheel for %s, due to binaries " + "being disabled for it.", req.name, + ) + continue + buildset.append((req, ephem_cache)) + + if not buildset: + return True + + # Build the wheels. + logger.info( + 'Building wheels for collected packages: %s', + ', '.join([req.name for (req, _) in buildset]), + ) + _cache = self.wheel_cache # shorter name + with indent_log(): + build_success, build_failure = [], [] + for req, ephem in buildset: + python_tag = None + if autobuilding: + python_tag = pep425tags.implementation_tag + if ephem: + output_dir = _cache.get_ephem_path_for_link(req.link) + else: + output_dir = _cache.get_path_for_link(req.link) + try: + ensure_dir(output_dir) + except OSError as e: + logger.warning("Building wheel for %s failed: %s", + req.name, e) + build_failure.append(req) + continue + else: + output_dir = self._wheel_dir + wheel_file = self._build_one( + req, output_dir, + python_tag=python_tag, + ) + if wheel_file: + build_success.append(req) + if autobuilding: + # XXX: This is mildly duplicative with prepare_files, + # but not close enough to pull out to a single common + # method. + # The code below assumes temporary source dirs - + # prevent it doing bad things. + if req.source_dir and not os.path.exists(os.path.join( + req.source_dir, PIP_DELETE_MARKER_FILENAME)): + raise AssertionError( + "bad source dir - missing marker") + # Delete the source we built the wheel from + req.remove_temporary_source() + # set the build directory again - name is known from + # the work prepare_files did. + req.source_dir = req.build_location( + self.preparer.build_dir + ) + # Update the link for this. + req.link = Link(path_to_url(wheel_file)) + assert req.link.is_wheel + # extract the wheel into the dir + unpack_url( + req.link, req.source_dir, None, False, + session=session, + ) + else: + build_failure.append(req) + + # notify success/failure + if build_success: + logger.info( + 'Successfully built %s', + ' '.join([req.name for req in build_success]), + ) + if build_failure: + logger.info( + 'Failed to build %s', + ' '.join([req.name for req in build_failure]), + ) + # Return True if all builds were successful + return len(build_failure) == 0 diff --git a/venv/Lib/site-packages/pip/_vendor/__init__.py b/venv/Lib/site-packages/pip/_vendor/__init__.py new file mode 100644 index 0000000..a0aae81 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/__init__.py @@ -0,0 +1,110 @@ +""" +pip._vendor is for vendoring dependencies of pip to prevent needing pip to +depend on something external. + +Files inside of pip._vendor should be considered immutable and should only be +updated to versions from upstream. +""" +from __future__ import absolute_import + +import glob +import os.path +import sys + +# Downstream redistributors which have debundled our dependencies should also +# patch this value to be true. This will trigger the additional patching +# to cause things like "six" to be available as pip. +DEBUNDLED = False + +# By default, look in this directory for a bunch of .whl files which we will +# add to the beginning of sys.path before attempting to import anything. This +# is done to support downstream re-distributors like Debian and Fedora who +# wish to create their own Wheels for our dependencies to aid in debundling. +WHEEL_DIR = os.path.abspath(os.path.dirname(__file__)) + + +# Define a small helper function to alias our vendored modules to the real ones +# if the vendored ones do not exist. This idea of this was taken from +# https://github.com/kennethreitz/requests/pull/2567. +def vendored(modulename): + vendored_name = "{0}.{1}".format(__name__, modulename) + + try: + __import__(vendored_name, globals(), locals(), level=0) + except ImportError: + try: + __import__(modulename, globals(), locals(), level=0) + except ImportError: + # We can just silently allow import failures to pass here. If we + # got to this point it means that ``import pip._vendor.whatever`` + # failed and so did ``import whatever``. Since we're importing this + # upfront in an attempt to alias imports, not erroring here will + # just mean we get a regular import error whenever pip *actually* + # tries to import one of these modules to use it, which actually + # gives us a better error message than we would have otherwise + # gotten. + pass + else: + sys.modules[vendored_name] = sys.modules[modulename] + base, head = vendored_name.rsplit(".", 1) + setattr(sys.modules[base], head, sys.modules[modulename]) + + +# If we're operating in a debundled setup, then we want to go ahead and trigger +# the aliasing of our vendored libraries as well as looking for wheels to add +# to our sys.path. This will cause all of this code to be a no-op typically +# however downstream redistributors can enable it in a consistent way across +# all platforms. +if DEBUNDLED: + # Actually look inside of WHEEL_DIR to find .whl files and add them to the + # front of our sys.path. + sys.path[:] = glob.glob(os.path.join(WHEEL_DIR, "*.whl")) + sys.path + + # Actually alias all of our vendored dependencies. + vendored("cachecontrol") + vendored("colorama") + vendored("distlib") + vendored("distro") + vendored("html5lib") + vendored("lockfile") + vendored("six") + vendored("six.moves") + vendored("six.moves.urllib") + vendored("six.moves.urllib.parse") + vendored("packaging") + vendored("packaging.version") + vendored("packaging.specifiers") + vendored("pkg_resources") + vendored("progress") + vendored("pytoml") + vendored("retrying") + vendored("requests") + vendored("requests.packages") + vendored("requests.packages.urllib3") + vendored("requests.packages.urllib3._collections") + vendored("requests.packages.urllib3.connection") + vendored("requests.packages.urllib3.connectionpool") + vendored("requests.packages.urllib3.contrib") + vendored("requests.packages.urllib3.contrib.ntlmpool") + vendored("requests.packages.urllib3.contrib.pyopenssl") + vendored("requests.packages.urllib3.exceptions") + vendored("requests.packages.urllib3.fields") + vendored("requests.packages.urllib3.filepost") + vendored("requests.packages.urllib3.packages") + vendored("requests.packages.urllib3.packages.ordered_dict") + vendored("requests.packages.urllib3.packages.six") + vendored("requests.packages.urllib3.packages.ssl_match_hostname") + vendored("requests.packages.urllib3.packages.ssl_match_hostname." + "_implementation") + vendored("requests.packages.urllib3.poolmanager") + vendored("requests.packages.urllib3.request") + vendored("requests.packages.urllib3.response") + vendored("requests.packages.urllib3.util") + vendored("requests.packages.urllib3.util.connection") + vendored("requests.packages.urllib3.util.request") + vendored("requests.packages.urllib3.util.response") + vendored("requests.packages.urllib3.util.retry") + vendored("requests.packages.urllib3.util.ssl_") + vendored("requests.packages.urllib3.util.timeout") + vendored("requests.packages.urllib3.util.url") + vendored("urllib3") diff --git a/venv/Lib/site-packages/pip/_vendor/appdirs.py b/venv/Lib/site-packages/pip/_vendor/appdirs.py new file mode 100644 index 0000000..2bd3911 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/appdirs.py @@ -0,0 +1,604 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2010 ActiveState Software Inc. +# Copyright (c) 2013 Eddy Petrișor + +"""Utilities for determining application-specific dirs. + +See <http://github.com/ActiveState/appdirs> for details and usage. +""" +# Dev Notes: +# - MSDN on where to store app data files: +# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 +# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html +# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + +__version_info__ = (1, 4, 3) +__version__ = '.'.join(map(str, __version_info__)) + + +import sys +import os + +PY3 = sys.version_info[0] == 3 + +if PY3: + unicode = str + +if sys.platform.startswith('java'): + import platform + os_name = platform.java_ver()[3][0] + if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. + system = 'win32' + elif os_name.startswith('Mac'): # "Mac OS X", etc. + system = 'darwin' + else: # "Linux", "SunOS", "FreeBSD", etc. + # Setting this to "linux2" is not ideal, but only Windows or Mac + # are actually checked for and the rest of the module expects + # *sys.platform* style strings. + system = 'linux2' +else: + system = sys.platform + + + +def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user data directories are: + Mac OS X: ~/Library/Application Support/<AppName> + Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName> + Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName> + Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName> + Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName> + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/<AppName>". + """ + if system == "win32": + if appauthor is None: + appauthor = appname + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(_get_win_folder(const)) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('~/Library/Application Support/') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of data dirs should be + returned. By default, the first item from XDG_DATA_DIRS is + returned, or '/usr/local/share/<AppName>', + if XDG_DATA_DIRS is not set + + Typical site data directories are: + Mac OS X: /Library/Application Support/<AppName> + Unix: /usr/local/share/<AppName> or /usr/share/<AppName> + Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName> + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7. + + For Unix, this is using the $XDG_DATA_DIRS[0] default. + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('/Library/Application Support') + if appname: + path = os.path.join(path, appname) + else: + # XDG default for $XDG_DATA_DIRS + # only first, if multipath is False + path = os.getenv('XDG_DATA_DIRS', + os.pathsep.join(['/usr/local/share', '/usr/share'])) + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + if appname and version: + path = os.path.join(path, version) + return path + + +def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user config directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/<AppName>". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of config dirs should be + returned. By default, the first item from XDG_CONFIG_DIRS is + returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set + + Typical site config directories are: + Mac OS X: same as site_data_dir + Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in + $XDG_CONFIG_DIRS + Win *: same as site_data_dir + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + + For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system in ["win32", "darwin"]: + path = site_data_dir(appname, appauthor) + if appname and version: + path = os.path.join(path, version) + else: + # XDG default for $XDG_CONFIG_DIRS + # only first, if multipath is False + path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + +def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Cache" to the base app data dir for Windows. See + discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Caches/<AppName> + Unix: ~/.cache/<AppName> (XDG default) + Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache + Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go in + the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming + app data dir (the default returned by `user_data_dir` above). Apps typically + put cache data somewhere *under* the given dir here. Some examples: + ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache + ...\Acme\SuperApp\Cache\1.0 + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + This can be disabled with the `opinion=False` option. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + if opinion: + path = os.path.join(path, "Cache") + elif system == 'darwin': + path = os.path.expanduser('~/Library/Caches') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific state dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user state directories are: + Mac OS X: same as user_data_dir + Unix: ~/.local/state/<AppName> # or in $XDG_STATE_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state> + to extend the XDG spec and support $XDG_STATE_HOME. + + That means, by default "~/.local/state/<AppName>". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific log dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Logs" to the base app data dir for Windows, and "log" to the + base cache dir for Unix. See discussion below. + + Typical user log directories are: + Mac OS X: ~/Library/Logs/<AppName> + Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs + Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs + + On Windows the only suggestion in the MSDN docs is that local settings + go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in + examples of what some windows apps use for a logs dir.) + + OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` + value for Windows and appends "log" to the user cache dir for Unix. + This can be disabled with the `opinion=False` option. + """ + if system == "darwin": + path = os.path.join( + os.path.expanduser('~/Library/Logs'), + appname) + elif system == "win32": + path = user_data_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "Logs") + else: + path = user_cache_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "log") + if appname and version: + path = os.path.join(path, version) + return path + + +class AppDirs(object): + """Convenience wrapper for getting application dirs.""" + def __init__(self, appname=None, appauthor=None, version=None, + roaming=False, multipath=False): + self.appname = appname + self.appauthor = appauthor + self.version = version + self.roaming = roaming + self.multipath = multipath + + @property + def user_data_dir(self): + return user_data_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_data_dir(self): + return site_data_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_config_dir(self): + return user_config_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_config_dir(self): + return site_config_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_cache_dir(self): + return user_cache_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_state_dir(self): + return user_state_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_log_dir(self): + return user_log_dir(self.appname, self.appauthor, + version=self.version) + + +#---- internal support stuff + +def _get_win_folder_from_registry(csidl_name): + """This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + if PY3: + import winreg as _winreg + else: + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" + ) + dir, type = _winreg.QueryValueEx(key, shell_folder_name) + return dir + + +def _get_win_folder_with_pywin32(csidl_name): + from win32com.shell import shellcon, shell + dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) + # Try to make this a unicode path because SHGetFolderPath does + # not return unicode strings when there is unicode data in the + # path. + try: + dir = unicode(dir) + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + try: + import win32api + dir = win32api.GetShortPathName(dir) + except ImportError: + pass + except UnicodeError: + pass + return dir + + +def _get_win_folder_with_ctypes(csidl_name): + import ctypes + + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + +def _get_win_folder_with_jna(csidl_name): + import array + from com.sun import jna + from com.sun.jna.platform import win32 + + buf_size = win32.WinDef.MAX_PATH * 2 + buf = array.zeros('c', buf_size) + shell = win32.Shell32.INSTANCE + shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf = array.zeros('c', buf_size) + kernel = win32.Kernel32.INSTANCE + if kernel.GetShortPathName(dir, buf, buf_size): + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + return dir + +if system == "win32": + try: + from ctypes import windll + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + try: + import com.sun.jna + _get_win_folder = _get_win_folder_with_jna + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +#---- self test code + +if __name__ == "__main__": + appname = "MyApp" + appauthor = "MyCompany" + + props = ("user_data_dir", + "user_config_dir", + "user_cache_dir", + "user_state_dir", + "user_log_dir", + "site_data_dir", + "site_config_dir") + + print("-- app dirs %s --" % __version__) + + print("-- app dirs (with optional 'version')") + dirs = AppDirs(appname, appauthor, version="1.0") + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'version')") + dirs = AppDirs(appname, appauthor) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'appauthor')") + dirs = AppDirs(appname) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (with disabled 'appauthor')") + dirs = AppDirs(appname, appauthor=False) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/__init__.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/__init__.py new file mode 100644 index 0000000..8fdee66 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/__init__.py @@ -0,0 +1,11 @@ +"""CacheControl import Interface. + +Make it easy to import from cachecontrol without long namespaces. +""" +__author__ = "Eric Larson" +__email__ = "eric@ionrock.org" +__version__ = "0.12.5" + +from .wrapper import CacheControl +from .adapter import CacheControlAdapter +from .controller import CacheController diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/_cmd.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/_cmd.py new file mode 100644 index 0000000..f1e0ad9 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/_cmd.py @@ -0,0 +1,57 @@ +import logging + +from pip._vendor import requests + +from pip._vendor.cachecontrol.adapter import CacheControlAdapter +from pip._vendor.cachecontrol.cache import DictCache +from pip._vendor.cachecontrol.controller import logger + +from argparse import ArgumentParser + + +def setup_logging(): + logger.setLevel(logging.DEBUG) + handler = logging.StreamHandler() + logger.addHandler(handler) + + +def get_session(): + adapter = CacheControlAdapter( + DictCache(), cache_etags=True, serializer=None, heuristic=None + ) + sess = requests.Session() + sess.mount("http://", adapter) + sess.mount("https://", adapter) + + sess.cache_controller = adapter.controller + return sess + + +def get_args(): + parser = ArgumentParser() + parser.add_argument("url", help="The URL to try and cache") + return parser.parse_args() + + +def main(args=None): + args = get_args() + sess = get_session() + + # Make a request to get a response + resp = sess.get(args.url) + + # Turn on logging + setup_logging() + + # try setting the cache + sess.cache_controller.cache_response(resp.request, resp.raw) + + # Now try to get it + if sess.cache_controller.cached_request(resp.request): + print("Cached!") + else: + print("Not cached :(") + + +if __name__ == "__main__": + main() diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/adapter.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/adapter.py new file mode 100644 index 0000000..780eb28 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/adapter.py @@ -0,0 +1,133 @@ +import types +import functools +import zlib + +from pip._vendor.requests.adapters import HTTPAdapter + +from .controller import CacheController +from .cache import DictCache +from .filewrapper import CallbackFileWrapper + + +class CacheControlAdapter(HTTPAdapter): + invalidating_methods = {"PUT", "DELETE"} + + def __init__( + self, + cache=None, + cache_etags=True, + controller_class=None, + serializer=None, + heuristic=None, + cacheable_methods=None, + *args, + **kw + ): + super(CacheControlAdapter, self).__init__(*args, **kw) + self.cache = cache or DictCache() + self.heuristic = heuristic + self.cacheable_methods = cacheable_methods or ("GET",) + + controller_factory = controller_class or CacheController + self.controller = controller_factory( + self.cache, cache_etags=cache_etags, serializer=serializer + ) + + def send(self, request, cacheable_methods=None, **kw): + """ + Send a request. Use the request information to see if it + exists in the cache and cache the response if we need to and can. + """ + cacheable = cacheable_methods or self.cacheable_methods + if request.method in cacheable: + try: + cached_response = self.controller.cached_request(request) + except zlib.error: + cached_response = None + if cached_response: + return self.build_response(request, cached_response, from_cache=True) + + # check for etags and add headers if appropriate + request.headers.update(self.controller.conditional_headers(request)) + + resp = super(CacheControlAdapter, self).send(request, **kw) + + return resp + + def build_response( + self, request, response, from_cache=False, cacheable_methods=None + ): + """ + Build a response by making a request or using the cache. + + This will end up calling send and returning a potentially + cached response + """ + cacheable = cacheable_methods or self.cacheable_methods + if not from_cache and request.method in cacheable: + # Check for any heuristics that might update headers + # before trying to cache. + if self.heuristic: + response = self.heuristic.apply(response) + + # apply any expiration heuristics + if response.status == 304: + # We must have sent an ETag request. This could mean + # that we've been expired already or that we simply + # have an etag. In either case, we want to try and + # update the cache if that is the case. + cached_response = self.controller.update_cached_response( + request, response + ) + + if cached_response is not response: + from_cache = True + + # We are done with the server response, read a + # possible response body (compliant servers will + # not return one, but we cannot be 100% sure) and + # release the connection back to the pool. + response.read(decode_content=False) + response.release_conn() + + response = cached_response + + # We always cache the 301 responses + elif response.status == 301: + self.controller.cache_response(request, response) + else: + # Wrap the response file with a wrapper that will cache the + # response when the stream has been consumed. + response._fp = CallbackFileWrapper( + response._fp, + functools.partial( + self.controller.cache_response, request, response + ), + ) + if response.chunked: + super_update_chunk_length = response._update_chunk_length + + def _update_chunk_length(self): + super_update_chunk_length() + if self.chunk_left == 0: + self._fp._close() + + response._update_chunk_length = types.MethodType( + _update_chunk_length, response + ) + + resp = super(CacheControlAdapter, self).build_response(request, response) + + # See if we should invalidate the cache. + if request.method in self.invalidating_methods and resp.ok: + cache_url = self.controller.cache_url(request.url) + self.cache.delete(cache_url) + + # Give the request a from_cache attr to let people use it + resp.from_cache = from_cache + + return resp + + def close(self): + self.cache.close() + super(CacheControlAdapter, self).close() diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/cache.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/cache.py new file mode 100644 index 0000000..94e0773 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/cache.py @@ -0,0 +1,39 @@ +""" +The cache object API for implementing caches. The default is a thread +safe in-memory dictionary. +""" +from threading import Lock + + +class BaseCache(object): + + def get(self, key): + raise NotImplementedError() + + def set(self, key, value): + raise NotImplementedError() + + def delete(self, key): + raise NotImplementedError() + + def close(self): + pass + + +class DictCache(BaseCache): + + def __init__(self, init_dict=None): + self.lock = Lock() + self.data = init_dict or {} + + def get(self, key): + return self.data.get(key, None) + + def set(self, key, value): + with self.lock: + self.data.update({key: value}) + + def delete(self, key): + with self.lock: + if key in self.data: + self.data.pop(key) diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__init__.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__init__.py new file mode 100644 index 0000000..0e1658f --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__init__.py @@ -0,0 +1,2 @@ +from .file_cache import FileCache # noqa +from .redis_cache import RedisCache # noqa diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py new file mode 100644 index 0000000..1ba0080 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py @@ -0,0 +1,146 @@ +import hashlib +import os +from textwrap import dedent + +from ..cache import BaseCache +from ..controller import CacheController + +try: + FileNotFoundError +except NameError: + # py2.X + FileNotFoundError = (IOError, OSError) + + +def _secure_open_write(filename, fmode): + # We only want to write to this file, so open it in write only mode + flags = os.O_WRONLY + + # os.O_CREAT | os.O_EXCL will fail if the file already exists, so we only + # will open *new* files. + # We specify this because we want to ensure that the mode we pass is the + # mode of the file. + flags |= os.O_CREAT | os.O_EXCL + + # Do not follow symlinks to prevent someone from making a symlink that + # we follow and insecurely open a cache file. + if hasattr(os, "O_NOFOLLOW"): + flags |= os.O_NOFOLLOW + + # On Windows we'll mark this file as binary + if hasattr(os, "O_BINARY"): + flags |= os.O_BINARY + + # Before we open our file, we want to delete any existing file that is + # there + try: + os.remove(filename) + except (IOError, OSError): + # The file must not exist already, so we can just skip ahead to opening + pass + + # Open our file, the use of os.O_CREAT | os.O_EXCL will ensure that if a + # race condition happens between the os.remove and this line, that an + # error will be raised. Because we utilize a lockfile this should only + # happen if someone is attempting to attack us. + fd = os.open(filename, flags, fmode) + try: + return os.fdopen(fd, "wb") + + except: + # An error occurred wrapping our FD in a file object + os.close(fd) + raise + + +class FileCache(BaseCache): + + def __init__( + self, + directory, + forever=False, + filemode=0o0600, + dirmode=0o0700, + use_dir_lock=None, + lock_class=None, + ): + + if use_dir_lock is not None and lock_class is not None: + raise ValueError("Cannot use use_dir_lock and lock_class together") + + try: + from pip._vendor.lockfile import LockFile + from pip._vendor.lockfile.mkdirlockfile import MkdirLockFile + except ImportError: + notice = dedent( + """ + NOTE: In order to use the FileCache you must have + lockfile installed. You can install it via pip: + pip install lockfile + """ + ) + raise ImportError(notice) + + else: + if use_dir_lock: + lock_class = MkdirLockFile + + elif lock_class is None: + lock_class = LockFile + + self.directory = directory + self.forever = forever + self.filemode = filemode + self.dirmode = dirmode + self.lock_class = lock_class + + @staticmethod + def encode(x): + return hashlib.sha224(x.encode()).hexdigest() + + def _fn(self, name): + # NOTE: This method should not change as some may depend on it. + # See: https://github.com/ionrock/cachecontrol/issues/63 + hashed = self.encode(name) + parts = list(hashed[:5]) + [hashed] + return os.path.join(self.directory, *parts) + + def get(self, key): + name = self._fn(key) + try: + with open(name, "rb") as fh: + return fh.read() + + except FileNotFoundError: + return None + + def set(self, key, value): + name = self._fn(key) + + # Make sure the directory exists + try: + os.makedirs(os.path.dirname(name), self.dirmode) + except (IOError, OSError): + pass + + with self.lock_class(name) as lock: + # Write our actual file + with _secure_open_write(lock.path, self.filemode) as fh: + fh.write(value) + + def delete(self, key): + name = self._fn(key) + if not self.forever: + try: + os.remove(name) + except FileNotFoundError: + pass + + +def url_to_file_path(url, filecache): + """Return the file cache path based on the URL. + + This does not ensure the file exists! + """ + key = CacheController.cache_url(url) + return filecache._fn(key) diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py new file mode 100644 index 0000000..ed705ce --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py @@ -0,0 +1,33 @@ +from __future__ import division + +from datetime import datetime +from pip._vendor.cachecontrol.cache import BaseCache + + +class RedisCache(BaseCache): + + def __init__(self, conn): + self.conn = conn + + def get(self, key): + return self.conn.get(key) + + def set(self, key, value, expires=None): + if not expires: + self.conn.set(key, value) + else: + expires = expires - datetime.utcnow() + self.conn.setex(key, int(expires.total_seconds()), value) + + def delete(self, key): + self.conn.delete(key) + + def clear(self): + """Helper for clearing all the keys in a database. Use with + caution!""" + for key in self.conn.keys(): + self.conn.delete(key) + + def close(self): + """Redis uses connection pooling, no need to close the connection.""" + pass diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/compat.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/compat.py new file mode 100644 index 0000000..33b5aed --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/compat.py @@ -0,0 +1,29 @@ +try: + from urllib.parse import urljoin +except ImportError: + from urlparse import urljoin + + +try: + import cPickle as pickle +except ImportError: + import pickle + + +# Handle the case where the requests module has been patched to not have +# urllib3 bundled as part of its source. +try: + from pip._vendor.requests.packages.urllib3.response import HTTPResponse +except ImportError: + from pip._vendor.urllib3.response import HTTPResponse + +try: + from pip._vendor.requests.packages.urllib3.util import is_fp_closed +except ImportError: + from pip._vendor.urllib3.util import is_fp_closed + +# Replicate some six behaviour +try: + text_type = unicode +except NameError: + text_type = str diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/controller.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/controller.py new file mode 100644 index 0000000..1b2b943 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/controller.py @@ -0,0 +1,367 @@ +""" +The httplib2 algorithms ported for use with requests. +""" +import logging +import re +import calendar +import time +from email.utils import parsedate_tz + +from pip._vendor.requests.structures import CaseInsensitiveDict + +from .cache import DictCache +from .serialize import Serializer + + +logger = logging.getLogger(__name__) + +URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?") + + +def parse_uri(uri): + """Parses a URI using the regex given in Appendix B of RFC 3986. + + (scheme, authority, path, query, fragment) = parse_uri(uri) + """ + groups = URI.match(uri).groups() + return (groups[1], groups[3], groups[4], groups[6], groups[8]) + + +class CacheController(object): + """An interface to see if request should cached or not. + """ + + def __init__( + self, cache=None, cache_etags=True, serializer=None, status_codes=None + ): + self.cache = cache or DictCache() + self.cache_etags = cache_etags + self.serializer = serializer or Serializer() + self.cacheable_status_codes = status_codes or (200, 203, 300, 301) + + @classmethod + def _urlnorm(cls, uri): + """Normalize the URL to create a safe key for the cache""" + (scheme, authority, path, query, fragment) = parse_uri(uri) + if not scheme or not authority: + raise Exception("Only absolute URIs are allowed. uri = %s" % uri) + + scheme = scheme.lower() + authority = authority.lower() + + if not path: + path = "/" + + # Could do syntax based normalization of the URI before + # computing the digest. See Section 6.2.2 of Std 66. + request_uri = query and "?".join([path, query]) or path + defrag_uri = scheme + "://" + authority + request_uri + + return defrag_uri + + @classmethod + def cache_url(cls, uri): + return cls._urlnorm(uri) + + def parse_cache_control(self, headers): + known_directives = { + # https://tools.ietf.org/html/rfc7234#section-5.2 + "max-age": (int, True), + "max-stale": (int, False), + "min-fresh": (int, True), + "no-cache": (None, False), + "no-store": (None, False), + "no-transform": (None, False), + "only-if-cached": (None, False), + "must-revalidate": (None, False), + "public": (None, False), + "private": (None, False), + "proxy-revalidate": (None, False), + "s-maxage": (int, True), + } + + cc_headers = headers.get("cache-control", headers.get("Cache-Control", "")) + + retval = {} + + for cc_directive in cc_headers.split(","): + if not cc_directive.strip(): + continue + + parts = cc_directive.split("=", 1) + directive = parts[0].strip() + + try: + typ, required = known_directives[directive] + except KeyError: + logger.debug("Ignoring unknown cache-control directive: %s", directive) + continue + + if not typ or not required: + retval[directive] = None + if typ: + try: + retval[directive] = typ(parts[1].strip()) + except IndexError: + if required: + logger.debug( + "Missing value for cache-control " "directive: %s", + directive, + ) + except ValueError: + logger.debug( + "Invalid value for cache-control directive " "%s, must be %s", + directive, + typ.__name__, + ) + + return retval + + def cached_request(self, request): + """ + Return a cached response if it exists in the cache, otherwise + return False. + """ + cache_url = self.cache_url(request.url) + logger.debug('Looking up "%s" in the cache', cache_url) + cc = self.parse_cache_control(request.headers) + + # Bail out if the request insists on fresh data + if "no-cache" in cc: + logger.debug('Request header has "no-cache", cache bypassed') + return False + + if "max-age" in cc and cc["max-age"] == 0: + logger.debug('Request header has "max_age" as 0, cache bypassed') + return False + + # Request allows serving from the cache, let's see if we find something + cache_data = self.cache.get(cache_url) + if cache_data is None: + logger.debug("No cache entry available") + return False + + # Check whether it can be deserialized + resp = self.serializer.loads(request, cache_data) + if not resp: + logger.warning("Cache entry deserialization failed, entry ignored") + return False + + # If we have a cached 301, return it immediately. We don't + # need to test our response for other headers b/c it is + # intrinsically "cacheable" as it is Permanent. + # See: + # https://tools.ietf.org/html/rfc7231#section-6.4.2 + # + # Client can try to refresh the value by repeating the request + # with cache busting headers as usual (ie no-cache). + if resp.status == 301: + msg = ( + 'Returning cached "301 Moved Permanently" response ' + "(ignoring date and etag information)" + ) + logger.debug(msg) + return resp + + headers = CaseInsensitiveDict(resp.headers) + if not headers or "date" not in headers: + if "etag" not in headers: + # Without date or etag, the cached response can never be used + # and should be deleted. + logger.debug("Purging cached response: no date or etag") + self.cache.delete(cache_url) + logger.debug("Ignoring cached response: no date") + return False + + now = time.time() + date = calendar.timegm(parsedate_tz(headers["date"])) + current_age = max(0, now - date) + logger.debug("Current age based on date: %i", current_age) + + # TODO: There is an assumption that the result will be a + # urllib3 response object. This may not be best since we + # could probably avoid instantiating or constructing the + # response until we know we need it. + resp_cc = self.parse_cache_control(headers) + + # determine freshness + freshness_lifetime = 0 + + # Check the max-age pragma in the cache control header + if "max-age" in resp_cc: + freshness_lifetime = resp_cc["max-age"] + logger.debug("Freshness lifetime from max-age: %i", freshness_lifetime) + + # If there isn't a max-age, check for an expires header + elif "expires" in headers: + expires = parsedate_tz(headers["expires"]) + if expires is not None: + expire_time = calendar.timegm(expires) - date + freshness_lifetime = max(0, expire_time) + logger.debug("Freshness lifetime from expires: %i", freshness_lifetime) + + # Determine if we are setting freshness limit in the + # request. Note, this overrides what was in the response. + if "max-age" in cc: + freshness_lifetime = cc["max-age"] + logger.debug( + "Freshness lifetime from request max-age: %i", freshness_lifetime + ) + + if "min-fresh" in cc: + min_fresh = cc["min-fresh"] + # adjust our current age by our min fresh + current_age += min_fresh + logger.debug("Adjusted current age from min-fresh: %i", current_age) + + # Return entry if it is fresh enough + if freshness_lifetime > current_age: + logger.debug('The response is "fresh", returning cached response') + logger.debug("%i > %i", freshness_lifetime, current_age) + return resp + + # we're not fresh. If we don't have an Etag, clear it out + if "etag" not in headers: + logger.debug('The cached response is "stale" with no etag, purging') + self.cache.delete(cache_url) + + # return the original handler + return False + + def conditional_headers(self, request): + cache_url = self.cache_url(request.url) + resp = self.serializer.loads(request, self.cache.get(cache_url)) + new_headers = {} + + if resp: + headers = CaseInsensitiveDict(resp.headers) + + if "etag" in headers: + new_headers["If-None-Match"] = headers["ETag"] + + if "last-modified" in headers: + new_headers["If-Modified-Since"] = headers["Last-Modified"] + + return new_headers + + def cache_response(self, request, response, body=None, status_codes=None): + """ + Algorithm for caching requests. + + This assumes a requests Response object. + """ + # From httplib2: Don't cache 206's since we aren't going to + # handle byte range requests + cacheable_status_codes = status_codes or self.cacheable_status_codes + if response.status not in cacheable_status_codes: + logger.debug( + "Status code %s not in %s", response.status, cacheable_status_codes + ) + return + + response_headers = CaseInsensitiveDict(response.headers) + + # If we've been given a body, our response has a Content-Length, that + # Content-Length is valid then we can check to see if the body we've + # been given matches the expected size, and if it doesn't we'll just + # skip trying to cache it. + if ( + body is not None + and "content-length" in response_headers + and response_headers["content-length"].isdigit() + and int(response_headers["content-length"]) != len(body) + ): + return + + cc_req = self.parse_cache_control(request.headers) + cc = self.parse_cache_control(response_headers) + + cache_url = self.cache_url(request.url) + logger.debug('Updating cache with response from "%s"', cache_url) + + # Delete it from the cache if we happen to have it stored there + no_store = False + if "no-store" in cc: + no_store = True + logger.debug('Response header has "no-store"') + if "no-store" in cc_req: + no_store = True + logger.debug('Request header has "no-store"') + if no_store and self.cache.get(cache_url): + logger.debug('Purging existing cache entry to honor "no-store"') + self.cache.delete(cache_url) + if no_store: + return + + # If we've been given an etag, then keep the response + if self.cache_etags and "etag" in response_headers: + logger.debug("Caching due to etag") + self.cache.set( + cache_url, self.serializer.dumps(request, response, body=body) + ) + + # Add to the cache any 301s. We do this before looking that + # the Date headers. + elif response.status == 301: + logger.debug("Caching permanant redirect") + self.cache.set(cache_url, self.serializer.dumps(request, response)) + + # Add to the cache if the response headers demand it. If there + # is no date header then we can't do anything about expiring + # the cache. + elif "date" in response_headers: + # cache when there is a max-age > 0 + if "max-age" in cc and cc["max-age"] > 0: + logger.debug("Caching b/c date exists and max-age > 0") + self.cache.set( + cache_url, self.serializer.dumps(request, response, body=body) + ) + + # If the request can expire, it means we should cache it + # in the meantime. + elif "expires" in response_headers: + if response_headers["expires"]: + logger.debug("Caching b/c of expires header") + self.cache.set( + cache_url, self.serializer.dumps(request, response, body=body) + ) + + def update_cached_response(self, request, response): + """On a 304 we will get a new set of headers that we want to + update our cached value with, assuming we have one. + + This should only ever be called when we've sent an ETag and + gotten a 304 as the response. + """ + cache_url = self.cache_url(request.url) + + cached_response = self.serializer.loads(request, self.cache.get(cache_url)) + + if not cached_response: + # we didn't have a cached response + return response + + # Lets update our headers with the headers from the new request: + # http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-4.1 + # + # The server isn't supposed to send headers that would make + # the cached body invalid. But... just in case, we'll be sure + # to strip out ones we know that might be problmatic due to + # typical assumptions. + excluded_headers = ["content-length"] + + cached_response.headers.update( + dict( + (k, v) + for k, v in response.headers.items() + if k.lower() not in excluded_headers + ) + ) + + # we want a 200 b/c we have content via the cache + cached_response.status = 200 + + # update our cache + self.cache.set(cache_url, self.serializer.dumps(request, cached_response)) + + return cached_response diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/filewrapper.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/filewrapper.py new file mode 100644 index 0000000..30ed4c5 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/filewrapper.py @@ -0,0 +1,80 @@ +from io import BytesIO + + +class CallbackFileWrapper(object): + """ + Small wrapper around a fp object which will tee everything read into a + buffer, and when that file is closed it will execute a callback with the + contents of that buffer. + + All attributes are proxied to the underlying file object. + + This class uses members with a double underscore (__) leading prefix so as + not to accidentally shadow an attribute. + """ + + def __init__(self, fp, callback): + self.__buf = BytesIO() + self.__fp = fp + self.__callback = callback + + def __getattr__(self, name): + # The vaguaries of garbage collection means that self.__fp is + # not always set. By using __getattribute__ and the private + # name[0] allows looking up the attribute value and raising an + # AttributeError when it doesn't exist. This stop thigns from + # infinitely recursing calls to getattr in the case where + # self.__fp hasn't been set. + # + # [0] https://docs.python.org/2/reference/expressions.html#atom-identifiers + fp = self.__getattribute__("_CallbackFileWrapper__fp") + return getattr(fp, name) + + def __is_fp_closed(self): + try: + return self.__fp.fp is None + + except AttributeError: + pass + + try: + return self.__fp.closed + + except AttributeError: + pass + + # We just don't cache it then. + # TODO: Add some logging here... + return False + + def _close(self): + if self.__callback: + self.__callback(self.__buf.getvalue()) + + # We assign this to None here, because otherwise we can get into + # really tricky problems where the CPython interpreter dead locks + # because the callback is holding a reference to something which + # has a __del__ method. Setting this to None breaks the cycle + # and allows the garbage collector to do it's thing normally. + self.__callback = None + + def read(self, amt=None): + data = self.__fp.read(amt) + self.__buf.write(data) + if self.__is_fp_closed(): + self._close() + + return data + + def _safe_read(self, amt): + data = self.__fp._safe_read(amt) + if amt == 2 and data == b"\r\n": + # urllib executes this read to toss the CRLF at the end + # of the chunk. + return data + + self.__buf.write(data) + if self.__is_fp_closed(): + self._close() + + return data diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/heuristics.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/heuristics.py new file mode 100644 index 0000000..6c0e979 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/heuristics.py @@ -0,0 +1,135 @@ +import calendar +import time + +from email.utils import formatdate, parsedate, parsedate_tz + +from datetime import datetime, timedelta + +TIME_FMT = "%a, %d %b %Y %H:%M:%S GMT" + + +def expire_after(delta, date=None): + date = date or datetime.utcnow() + return date + delta + + +def datetime_to_header(dt): + return formatdate(calendar.timegm(dt.timetuple())) + + +class BaseHeuristic(object): + + def warning(self, response): + """ + Return a valid 1xx warning header value describing the cache + adjustments. + + The response is provided too allow warnings like 113 + http://tools.ietf.org/html/rfc7234#section-5.5.4 where we need + to explicitly say response is over 24 hours old. + """ + return '110 - "Response is Stale"' + + def update_headers(self, response): + """Update the response headers with any new headers. + + NOTE: This SHOULD always include some Warning header to + signify that the response was cached by the client, not + by way of the provided headers. + """ + return {} + + def apply(self, response): + updated_headers = self.update_headers(response) + + if updated_headers: + response.headers.update(updated_headers) + warning_header_value = self.warning(response) + if warning_header_value is not None: + response.headers.update({"Warning": warning_header_value}) + + return response + + +class OneDayCache(BaseHeuristic): + """ + Cache the response by providing an expires 1 day in the + future. + """ + + def update_headers(self, response): + headers = {} + + if "expires" not in response.headers: + date = parsedate(response.headers["date"]) + expires = expire_after(timedelta(days=1), date=datetime(*date[:6])) + headers["expires"] = datetime_to_header(expires) + headers["cache-control"] = "public" + return headers + + +class ExpiresAfter(BaseHeuristic): + """ + Cache **all** requests for a defined time period. + """ + + def __init__(self, **kw): + self.delta = timedelta(**kw) + + def update_headers(self, response): + expires = expire_after(self.delta) + return {"expires": datetime_to_header(expires), "cache-control": "public"} + + def warning(self, response): + tmpl = "110 - Automatically cached for %s. Response might be stale" + return tmpl % self.delta + + +class LastModified(BaseHeuristic): + """ + If there is no Expires header already, fall back on Last-Modified + using the heuristic from + http://tools.ietf.org/html/rfc7234#section-4.2.2 + to calculate a reasonable value. + + Firefox also does something like this per + https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ + http://lxr.mozilla.org/mozilla-release/source/netwerk/protocol/http/nsHttpResponseHead.cpp#397 + Unlike mozilla we limit this to 24-hr. + """ + cacheable_by_default_statuses = { + 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501 + } + + def update_headers(self, resp): + headers = resp.headers + + if "expires" in headers: + return {} + + if "cache-control" in headers and headers["cache-control"] != "public": + return {} + + if resp.status not in self.cacheable_by_default_statuses: + return {} + + if "date" not in headers or "last-modified" not in headers: + return {} + + date = calendar.timegm(parsedate_tz(headers["date"])) + last_modified = parsedate(headers["last-modified"]) + if date is None or last_modified is None: + return {} + + now = time.time() + current_age = max(0, now - date) + delta = date - calendar.timegm(last_modified) + freshness_lifetime = max(0, min(delta / 10, 24 * 3600)) + if freshness_lifetime <= current_age: + return {} + + expires = date + freshness_lifetime + return {"expires": time.strftime(TIME_FMT, time.gmtime(expires))} + + def warning(self, resp): + return None diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py new file mode 100644 index 0000000..ec43ff2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py @@ -0,0 +1,186 @@ +import base64 +import io +import json +import zlib + +from pip._vendor import msgpack +from pip._vendor.requests.structures import CaseInsensitiveDict + +from .compat import HTTPResponse, pickle, text_type + + +def _b64_decode_bytes(b): + return base64.b64decode(b.encode("ascii")) + + +def _b64_decode_str(s): + return _b64_decode_bytes(s).decode("utf8") + + +class Serializer(object): + + def dumps(self, request, response, body=None): + response_headers = CaseInsensitiveDict(response.headers) + + if body is None: + body = response.read(decode_content=False) + + # NOTE: 99% sure this is dead code. I'm only leaving it + # here b/c I don't have a test yet to prove + # it. Basically, before using + # `cachecontrol.filewrapper.CallbackFileWrapper`, + # this made an effort to reset the file handle. The + # `CallbackFileWrapper` short circuits this code by + # setting the body as the content is consumed, the + # result being a `body` argument is *always* passed + # into cache_response, and in turn, + # `Serializer.dump`. + response._fp = io.BytesIO(body) + + # NOTE: This is all a bit weird, but it's really important that on + # Python 2.x these objects are unicode and not str, even when + # they contain only ascii. The problem here is that msgpack + # understands the difference between unicode and bytes and we + # have it set to differentiate between them, however Python 2 + # doesn't know the difference. Forcing these to unicode will be + # enough to have msgpack know the difference. + data = { + u"response": { + u"body": body, + u"headers": dict( + (text_type(k), text_type(v)) for k, v in response.headers.items() + ), + u"status": response.status, + u"version": response.version, + u"reason": text_type(response.reason), + u"strict": response.strict, + u"decode_content": response.decode_content, + } + } + + # Construct our vary headers + data[u"vary"] = {} + if u"vary" in response_headers: + varied_headers = response_headers[u"vary"].split(",") + for header in varied_headers: + header = text_type(header).strip() + header_value = request.headers.get(header, None) + if header_value is not None: + header_value = text_type(header_value) + data[u"vary"][header] = header_value + + return b",".join([b"cc=4", msgpack.dumps(data, use_bin_type=True)]) + + def loads(self, request, data): + # Short circuit if we've been given an empty set of data + if not data: + return + + # Determine what version of the serializer the data was serialized + # with + try: + ver, data = data.split(b",", 1) + except ValueError: + ver = b"cc=0" + + # Make sure that our "ver" is actually a version and isn't a false + # positive from a , being in the data stream. + if ver[:3] != b"cc=": + data = ver + data + ver = b"cc=0" + + # Get the version number out of the cc=N + ver = ver.split(b"=", 1)[-1].decode("ascii") + + # Dispatch to the actual load method for the given version + try: + return getattr(self, "_loads_v{}".format(ver))(request, data) + + except AttributeError: + # This is a version we don't have a loads function for, so we'll + # just treat it as a miss and return None + return + + def prepare_response(self, request, cached): + """Verify our vary headers match and construct a real urllib3 + HTTPResponse object. + """ + # Special case the '*' Vary value as it means we cannot actually + # determine if the cached response is suitable for this request. + if "*" in cached.get("vary", {}): + return + + # Ensure that the Vary headers for the cached response match our + # request + for header, value in cached.get("vary", {}).items(): + if request.headers.get(header, None) != value: + return + + body_raw = cached["response"].pop("body") + + headers = CaseInsensitiveDict(data=cached["response"]["headers"]) + if headers.get("transfer-encoding", "") == "chunked": + headers.pop("transfer-encoding") + + cached["response"]["headers"] = headers + + try: + body = io.BytesIO(body_raw) + except TypeError: + # This can happen if cachecontrol serialized to v1 format (pickle) + # using Python 2. A Python 2 str(byte string) will be unpickled as + # a Python 3 str (unicode string), which will cause the above to + # fail with: + # + # TypeError: 'str' does not support the buffer interface + body = io.BytesIO(body_raw.encode("utf8")) + + return HTTPResponse(body=body, preload_content=False, **cached["response"]) + + def _loads_v0(self, request, data): + # The original legacy cache data. This doesn't contain enough + # information to construct everything we need, so we'll treat this as + # a miss. + return + + def _loads_v1(self, request, data): + try: + cached = pickle.loads(data) + except ValueError: + return + + return self.prepare_response(request, cached) + + def _loads_v2(self, request, data): + try: + cached = json.loads(zlib.decompress(data).decode("utf8")) + except (ValueError, zlib.error): + return + + # We need to decode the items that we've base64 encoded + cached["response"]["body"] = _b64_decode_bytes(cached["response"]["body"]) + cached["response"]["headers"] = dict( + (_b64_decode_str(k), _b64_decode_str(v)) + for k, v in cached["response"]["headers"].items() + ) + cached["response"]["reason"] = _b64_decode_str(cached["response"]["reason"]) + cached["vary"] = dict( + (_b64_decode_str(k), _b64_decode_str(v) if v is not None else v) + for k, v in cached["vary"].items() + ) + + return self.prepare_response(request, cached) + + def _loads_v3(self, request, data): + # Due to Python 2 encoding issues, it's impossible to know for sure + # exactly how to load v3 entries, thus we'll treat these as a miss so + # that they get rewritten out as v4 entries. + return + + def _loads_v4(self, request, data): + try: + cached = msgpack.loads(data, encoding="utf-8") + except ValueError: + return + + return self.prepare_response(request, cached) diff --git a/venv/Lib/site-packages/pip/_vendor/cachecontrol/wrapper.py b/venv/Lib/site-packages/pip/_vendor/cachecontrol/wrapper.py new file mode 100644 index 0000000..265bfc8 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/cachecontrol/wrapper.py @@ -0,0 +1,29 @@ +from .adapter import CacheControlAdapter +from .cache import DictCache + + +def CacheControl( + sess, + cache=None, + cache_etags=True, + serializer=None, + heuristic=None, + controller_class=None, + adapter_class=None, + cacheable_methods=None, +): + + cache = cache or DictCache() + adapter_class = adapter_class or CacheControlAdapter + adapter = adapter_class( + cache, + cache_etags=cache_etags, + serializer=serializer, + heuristic=heuristic, + controller_class=controller_class, + cacheable_methods=cacheable_methods, + ) + sess.mount("http://", adapter) + sess.mount("https://", adapter) + + return sess diff --git a/venv/Lib/site-packages/pip/_vendor/certifi/__init__.py b/venv/Lib/site-packages/pip/_vendor/certifi/__init__.py new file mode 100644 index 0000000..aa329fb --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/certifi/__init__.py @@ -0,0 +1,3 @@ +from .core import where, old_where + +__version__ = "2018.08.24" diff --git a/venv/Lib/site-packages/pip/_vendor/certifi/__main__.py b/venv/Lib/site-packages/pip/_vendor/certifi/__main__.py new file mode 100644 index 0000000..ae2aff5 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/certifi/__main__.py @@ -0,0 +1,2 @@ +from pip._vendor.certifi import where +print(where()) diff --git a/venv/Lib/site-packages/pip/_vendor/certifi/cacert.pem b/venv/Lib/site-packages/pip/_vendor/certifi/cacert.pem new file mode 100644 index 0000000..85de024 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/certifi/cacert.pem @@ -0,0 +1,4300 @@ + +# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Label: "GlobalSign Root CA" +# Serial: 4835703278459707669005204 +# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a +# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c +# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Label: "GlobalSign Root CA - R2" +# Serial: 4835703278459682885658125 +# MD5 Fingerprint: 94:14:77:7e:3e:5e:fd:8f:30:bd:41:b0:cf:e7:d0:30 +# SHA1 Fingerprint: 75:e0:ab:b6:13:85:12:27:1c:04:f8:5f:dd:de:38:e4:b7:24:2e:fe +# SHA256 Fingerprint: ca:42:dd:41:74:5f:d0:b8:1e:b9:02:36:2c:f9:d8:bf:71:9d:a1:bd:1b:1e:fc:94:6f:5b:4c:99:f4:2c:1b:9e +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Label: "Verisign Class 3 Public Primary Certification Authority - G3" +# Serial: 206684696279472310254277870180966723415 +# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09 +# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6 +# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b +N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t +KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu +kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm +CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu +imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te +2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe +DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p +F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt +TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Premium 2048 Secure Server CA" +# Serial: 946069240 +# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 +# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 +# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Label: "Baltimore CyberTrust Root" +# Serial: 33554617 +# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 +# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 +# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Label: "AddTrust External Root" +# Serial: 1 +# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f +# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68 +# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2 +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs +IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 +MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h +bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt +H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 +uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX +mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX +a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN +E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 +WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD +VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 +Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU +cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx +IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN +AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH +YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC +Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX +c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a +mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Label: "Entrust Root Certification Authority" +# Serial: 1164660820 +# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 +# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 +# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Global CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Global CA O=GeoTrust Inc. +# Label: "GeoTrust Global CA" +# Serial: 144470 +# MD5 Fingerprint: f7:75:ab:29:fb:51:4e:b7:77:5e:ff:05:3c:99:8e:f5 +# SHA1 Fingerprint: de:28:f4:a4:ff:e5:b9:2f:a3:c5:03:d1:a3:49:a7:f9:96:2a:82:12 +# SHA256 Fingerprint: ff:85:6a:2d:25:1d:cd:88:d3:66:56:f4:50:12:67:98:cf:ab:aa:de:40:79:9c:72:2d:e4:d2:b5:db:36:a7:3a +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Label: "GeoTrust Universal CA" +# Serial: 1 +# MD5 Fingerprint: 92:65:58:8b:a2:1a:31:72:73:68:5c:b4:a5:7a:07:48 +# SHA1 Fingerprint: e6:21:f3:35:43:79:05:9a:4b:68:30:9d:8a:2f:74:22:15:87:ec:79 +# SHA256 Fingerprint: a0:45:9b:9f:63:b2:25:59:f5:fa:5d:4c:6d:b3:f9:f7:2f:f1:93:42:03:35:78:f0:73:bf:1d:1b:46:cb:b9:12 +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy +c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 +IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV +VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 +cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT +QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh +F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v +c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w +mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd +VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX +teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ +f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe +Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ +nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY +MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX +IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn +ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z +uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN +Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja +QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW +koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 +ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt +DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm +bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Label: "GeoTrust Universal CA 2" +# Serial: 1 +# MD5 Fingerprint: 34:fc:b8:d0:36:db:9e:14:b3:c2:f2:db:8f:e4:94:c7 +# SHA1 Fingerprint: 37:9a:19:7b:41:85:45:35:0c:a6:03:69:f3:3c:2e:af:47:4f:20:79 +# SHA256 Fingerprint: a0:23:4f:3b:c8:52:7c:a5:62:8e:ec:81:ad:5d:69:89:5d:a5:68:0d:c9:1d:1c:b8:47:7f:33:f8:78:b9:5b:0b +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy +c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD +VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 +c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 +WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG +FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq +XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL +se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb +KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd +IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 +y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt +hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc +QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 +Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV +HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ +KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ +L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr +Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo +ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY +T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz +GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m +1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV +OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH +6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX +QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +# Issuer: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association +# Subject: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association +# Label: "Visa eCommerce Root" +# Serial: 25952180776285836048024890241505565794 +# MD5 Fingerprint: fc:11:b8:d8:08:93:30:00:6d:23:f9:7e:eb:52:1e:02 +# SHA1 Fingerprint: 70:17:9b:86:8c:00:a4:fa:60:91:52:22:3f:9f:3e:32:bd:e0:05:62 +# SHA256 Fingerprint: 69:fa:c9:bd:55:fb:0a:c7:8d:53:bb:ee:5c:f1:d5:97:98:9f:d0:aa:ab:20:a2:51:51:bd:f1:73:3e:e7:d1:22 +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr +MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl +cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw +CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h +dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l +cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h +2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E +lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV +ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq +299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t +vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL +dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF +AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR +zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3 +LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd +7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw +++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +# Issuer: CN=AAA Certificate Services O=Comodo CA Limited +# Subject: CN=AAA Certificate Services O=Comodo CA Limited +# Label: "Comodo AAA Services root" +# Serial: 1 +# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 +# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 +# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority +# Subject: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority +# Label: "QuoVadis Root CA" +# Serial: 985026699 +# MD5 Fingerprint: 27:de:36:fe:72:b7:00:03:00:9d:f4:f0:1e:6c:04:24 +# SHA1 Fingerprint: de:3f:40:bd:50:93:d3:9b:6c:60:f6:da:bc:07:62:01:00:89:76:c9 +# SHA256 Fingerprint: a4:5e:de:3b:bb:f0:9c:8a:e1:5c:72:ef:c0:72:68:d6:93:a2:1c:99:6f:d5:1e:67:ca:07:94:60:fd:6d:88:73 +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz +MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw +IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR +dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp +li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D +rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ +WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug +F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU +xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC +Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv +dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw +ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl +IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh +c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy +ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI +KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T +KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq +y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p +dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD +VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk +fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 +7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R +cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y +mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW +xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK +SnQ2+Q== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2" +# Serial: 1289 +# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b +# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7 +# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86 +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3" +# Serial: 1478 +# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf +# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85 +# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35 +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1 +# Subject: O=SECOM Trust.net OU=Security Communication RootCA1 +# Label: "Security Communication Root CA" +# Serial: 0 +# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a +# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7 +# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY +MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t +dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 +WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD +VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 +9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ +DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 +Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N +QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ +xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G +A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG +kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr +Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU +JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot +RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== +-----END CERTIFICATE----- + +# Issuer: CN=Sonera Class2 CA O=Sonera +# Subject: CN=Sonera Class2 CA O=Sonera +# Label: "Sonera Class 2 Root CA" +# Serial: 29 +# MD5 Fingerprint: a3:ec:75:0f:2e:88:df:fa:48:01:4e:0b:5c:48:6f:fb +# SHA1 Fingerprint: 37:f7:6d:e6:07:7c:90:c5:b1:3e:93:1a:b7:41:10:b4:f2:e4:9a:27 +# SHA256 Fingerprint: 79:08:b4:03:14:c1:38:10:0b:51:8d:07:35:80:7f:fb:fc:f8:51:8a:00:95:33:71:05:ba:38:6b:15:3d:d9:27 +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx +MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o +Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt +5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s +3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej +vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu +8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil +zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ +3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD +FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 +Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 +ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M +-----END CERTIFICATE----- + +# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Label: "XRamp Global CA Root" +# Serial: 107108908803651509692980124233745014957 +# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1 +# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6 +# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2 +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Label: "Go Daddy Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 +# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 +# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Label: "Starfield Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 +# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a +# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +# Issuer: O=Government Root Certification Authority +# Subject: O=Government Root Certification Authority +# Label: "Taiwan GRCA" +# Serial: 42023070807708724159991140556527066870 +# MD5 Fingerprint: 37:85:44:53:32:45:1f:20:f0:f3:95:e1:25:c4:43:4e +# SHA1 Fingerprint: f4:8b:11:bf:de:ab:be:94:54:20:71:e6:41:de:6b:be:88:2b:40:b9 +# SHA256 Fingerprint: 76:00:29:5e:ef:e8:5b:9e:1f:d6:24:db:76:06:2a:aa:ae:59:81:8a:54:d2:77:4c:d4:c0:b2:c0:11:31:e1:b3 +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/ +MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow +PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR +IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q +gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy +yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts +F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2 +jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx +ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC +VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK +YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH +EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN +Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud +DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE +MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK +UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf +qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK +ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE +JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7 +hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1 +EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm +nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX +udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz +ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe +LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl +pYYsfPQS +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root CA" +# Serial: 17154717934120587862167794914071425081 +# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 +# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 +# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root CA" +# Serial: 10944719598952040374951832963794454346 +# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e +# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 +# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert High Assurance EV Root CA" +# Serial: 3553400076410547919724730734378100087 +# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a +# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 +# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +# Issuer: CN=Class 2 Primary CA O=Certplus +# Subject: CN=Class 2 Primary CA O=Certplus +# Label: "Certplus Class 2 Primary CA" +# Serial: 177770208045934040241468760488327595043 +# MD5 Fingerprint: 88:2c:8c:52:b8:a2:3c:f3:f7:bb:03:ea:ae:ac:42:0b +# SHA1 Fingerprint: 74:20:74:41:72:9c:dd:92:ec:79:31:d8:23:10:8d:c2:81:92:e2:bb +# SHA256 Fingerprint: 0f:99:3c:8a:ef:97:ba:af:56:87:14:0e:d5:9a:d1:82:1b:b4:af:ac:f0:aa:9a:58:b5:d5:7a:33:8a:3a:fb:cb +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw +PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz +cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9 +MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz +IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ +ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR +VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL +kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd +EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas +H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0 +HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud +DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4 +QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu +Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/ +AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8 +yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR +FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA +ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB +kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +# Issuer: CN=DST Root CA X3 O=Digital Signature Trust Co. +# Subject: CN=DST Root CA X3 O=Digital Signature Trust Co. +# Label: "DST Root CA X3" +# Serial: 91299735575339953335919266965803778155 +# MD5 Fingerprint: 41:03:52:dc:0f:f7:50:1b:16:f0:02:8e:ba:6f:45:c5 +# SHA1 Fingerprint: da:c9:02:4f:54:d8:f6:df:94:93:5f:b1:73:26:38:ca:6a:d7:7c:13 +# SHA256 Fingerprint: 06:87:26:03:31:a7:24:03:d9:09:f1:05:e6:9b:cf:0d:32:e1:bd:24:93:ff:c6:d9:20:6d:11:bc:d6:77:07:39 +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O +rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq +OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b +xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw +7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD +aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG +SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 +ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr +AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz +R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 +JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo +Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Label: "SwissSign Gold CA - G2" +# Serial: 13492815561806991280 +# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93 +# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61 +# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95 +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Label: "SwissSign Silver CA - G2" +# Serial: 5700383053117599563 +# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13 +# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb +# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5 +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Subject: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Label: "GeoTrust Primary Certification Authority" +# Serial: 32798226551256963324313806436981982369 +# MD5 Fingerprint: 02:26:c3:01:5e:08:30:37:43:a9:d0:7d:cf:37:e6:bf +# SHA1 Fingerprint: 32:3c:11:8e:1b:f7:b8:b6:52:54:e2:e2:10:0d:d6:02:90:37:f0:96 +# SHA256 Fingerprint: 37:d5:10:06:c5:12:ea:ab:62:64:21:f1:ec:8c:92:01:3f:c5:f8:2a:e9:8e:e5:33:eb:46:19:b8:de:b4:d0:6c +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx +MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 +AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA +ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 +7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W +kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI +mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ +KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 +6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl +4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K +oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj +UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU +AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA" +# Serial: 69529181992039203566298953787712940909 +# MD5 Fingerprint: 8c:ca:dc:0b:22:ce:f5:be:72:ac:41:1a:11:a8:d8:12 +# SHA1 Fingerprint: 91:c6:d6:ee:3e:8a:c8:63:84:e5:48:c2:99:29:5c:75:6c:81:7b:81 +# SHA256 Fingerprint: 8d:72:2f:81:a9:c1:13:c0:79:1d:f1:36:a2:96:6d:b2:6c:95:0a:97:1d:b4:6b:41:99:f4:ea:54:b7:8b:fb:9f +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G5" +# Serial: 33037644167568058970164719475676101450 +# MD5 Fingerprint: cb:17:e4:31:67:3e:e2:09:fe:45:57:93:f3:0a:fa:1c +# SHA1 Fingerprint: 4e:b6:d5:78:49:9b:1c:cf:5f:58:1e:ad:56:be:3d:9b:67:44:a5:e5 +# SHA256 Fingerprint: 9a:cf:ab:7e:43:c8:d8:80:d0:6b:26:2a:94:de:ee:e4:b4:65:99:89:c3:d0:ca:f1:9b:af:64:05:e4:1a:b7:df +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +# Issuer: CN=SecureTrust CA O=SecureTrust Corporation +# Subject: CN=SecureTrust CA O=SecureTrust Corporation +# Label: "SecureTrust CA" +# Serial: 17199774589125277788362757014266862032 +# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1 +# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11 +# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73 +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +# Issuer: CN=Secure Global CA O=SecureTrust Corporation +# Subject: CN=Secure Global CA O=SecureTrust Corporation +# Label: "Secure Global CA" +# Serial: 9751836167731051554232119481456978597 +# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de +# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b +# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69 +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO Certification Authority O=COMODO CA Limited +# Label: "COMODO Certification Authority" +# Serial: 104350513648249232941998508985834464573 +# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 +# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b +# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- + +# Issuer: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. +# Subject: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. +# Label: "Network Solutions Certificate Authority" +# Serial: 116697915152937497490437556386812487904 +# MD5 Fingerprint: d3:f3:a6:16:c0:fa:6b:1d:59:b1:2d:96:4d:0e:11:2e +# SHA1 Fingerprint: 74:f8:a3:c3:ef:e7:b3:90:06:4b:83:90:3c:21:64:60:20:e5:df:ce +# SHA256 Fingerprint: 15:f0:ba:00:a3:ac:7a:f3:ac:88:4c:07:2b:10:11:a0:77:bd:77:c0:97:f4:01:64:b2:f8:59:8a:bd:83:86:0c +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi +MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV +UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO +ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz +c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP +OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl +mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF +BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4 +qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw +gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu +bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp +dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8 +6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/ +h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH +/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN +pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GA CA" +# Serial: 86718877871133159090080555911823548314 +# MD5 Fingerprint: bc:6c:51:33:a7:e9:d3:66:63:54:15:72:1b:21:92:93 +# SHA1 Fingerprint: 59:22:a1:e1:5a:ea:16:35:21:f8:98:39:6a:46:46:b0:44:1b:0f:a9 +# SHA256 Fingerprint: 41:c9:23:86:6a:b4:ca:d6:b7:ad:57:80:81:58:2e:02:07:97:a6:cb:df:4f:ff:78:ce:83:96:b3:89:37:d7:f5 +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB +ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly +aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w +NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G +A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX +SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR +VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2 +w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF +mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg +4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9 +4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw +EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx +SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2 +ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8 +vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi +Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ +/L7fCg0= +-----END CERTIFICATE----- + +# Issuer: CN=Certigna O=Dhimyotis +# Subject: CN=Certigna O=Dhimyotis +# Label: "Certigna" +# Serial: 18364802974209362175 +# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff +# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97 +# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +# Issuer: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center +# Subject: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center +# Label: "Deutsche Telekom Root CA 2" +# Serial: 38 +# MD5 Fingerprint: 74:01:4a:91:b1:08:c4:58:ce:47:cd:f0:dd:11:53:08 +# SHA1 Fingerprint: 85:a4:08:c0:9c:19:3e:5d:51:58:7d:cd:d6:13:30:fd:8c:de:37:bf +# SHA256 Fingerprint: b6:19:1a:50:d0:c3:97:7f:7d:a9:9b:cd:aa:c8:6a:22:7d:ae:b9:67:9e:c7:0b:a3:b0:c9:d9:22:71:c1:70:d3 +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc +MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj +IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB +IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE +RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl +U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290 +IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU +ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC +QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr +rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S +NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc +QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH +txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP +BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp +tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa +IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl +6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+ +xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc +# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc +# Label: "Cybertrust Global Root" +# Serial: 4835703278459682877484360 +# MD5 Fingerprint: 72:e4:4a:87:e3:69:40:80:77:ea:bc:e3:f4:ff:f0:e1 +# SHA1 Fingerprint: 5f:43:e5:b1:bf:f8:78:8c:ac:1c:c7:ca:4a:9a:c6:22:2b:cc:34:c6 +# SHA256 Fingerprint: 96:0a:df:00:63:e9:63:56:75:0c:29:65:dd:0a:08:67:da:0b:9c:bd:6e:77:71:4a:ea:fb:23:49:ab:39:3d:a3 +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG +A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh +bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE +ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS +b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5 +7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS +J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y +HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP +t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz +FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY +XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw +hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js +MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA +A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj +Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx +XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o +omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc +A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Label: "ePKI Root Certification Authority" +# Serial: 28956088682735189655030529057352760477 +# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3 +# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0 +# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5 +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +# Issuer: O=certSIGN OU=certSIGN ROOT CA +# Subject: O=certSIGN OU=certSIGN ROOT CA +# Label: "certSIGN ROOT CA" +# Serial: 35210227249154 +# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17 +# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b +# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G3" +# Serial: 28809105769928564313984085209975885599 +# MD5 Fingerprint: b5:e8:34:36:c9:10:44:58:48:70:6d:2e:83:d4:b8:05 +# SHA1 Fingerprint: 03:9e:ed:b8:0b:e7:a0:3c:69:53:89:3b:20:d2:d9:32:3a:4c:2a:fd +# SHA256 Fingerprint: b4:78:b8:12:25:0d:f8:78:63:5c:2a:a7:ec:7d:15:5e:aa:62:5e:e8:29:16:e2:cd:29:43:61:88:6c:d1:fb:d4 +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ +BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 +BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz ++uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm +hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn +5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W +JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL +DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC +huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB +AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB +zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN +kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH +SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G +spki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G2" +# Serial: 71758320672825410020661621085256472406 +# MD5 Fingerprint: 74:9d:ea:60:24:c4:fd:22:53:3e:cc:3a:72:d9:29:4f +# SHA1 Fingerprint: aa:db:bc:22:23:8f:c4:01:a1:27:bb:38:dd:f4:1d:db:08:9e:f0:12 +# SHA256 Fingerprint: a4:31:0d:50:af:18:a6:44:71:90:37:2a:86:af:af:8b:95:1f:fb:43:1d:83:7f:1e:56:88:b4:59:71:ed:15:57 +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp +IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi +BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw +MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig +YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v +dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ +BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 +papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K +DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 +KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox +XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G3" +# Serial: 127614157056681299805556476275995414779 +# MD5 Fingerprint: fb:1b:5d:43:8a:94:cd:44:c6:76:f2:43:4b:47:e7:31 +# SHA1 Fingerprint: f1:8b:53:8d:1b:e9:03:b6:a6:f0:56:43:5b:17:15:89:ca:f3:6b:f2 +# SHA256 Fingerprint: 4b:03:f4:58:07:ad:70:f2:1b:fc:2c:ae:71:c9:fd:e4:60:4c:06:4c:f5:ff:b6:86:ba:e5:db:aa:d7:fd:d3:4c +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa +Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl +LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u +MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm +gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 +YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf +b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 +9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S +zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk +OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA +2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW +oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c +KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM +m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu +MdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G2" +# Serial: 80682863203381065782177908751794619243 +# MD5 Fingerprint: 01:5e:d8:6b:bd:6f:3d:8e:a1:31:f8:12:e0:98:73:6a +# SHA1 Fingerprint: 8d:17:84:d5:37:f3:03:7d:ec:70:fe:57:8b:51:9a:99:e6:10:d7:b0 +# SHA256 Fingerprint: 5e:db:7a:c4:3b:82:a0:6a:87:61:e8:d7:be:49:79:eb:f2:61:1f:7d:d7:9b:f9:1c:1c:6b:56:6a:21:9e:d7:66 +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL +MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj +KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 +MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw +NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV +BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL +So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal +tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG +CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT +qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz +rD6ogRLQy7rQkgu2npaqBA+K +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Universal Root Certification Authority" +# Serial: 85209574734084581917763752644031726877 +# MD5 Fingerprint: 8e:ad:b5:01:aa:4d:81:e4:8c:1d:d1:e1:14:00:95:19 +# SHA1 Fingerprint: 36:79:ca:35:66:87:72:30:4d:30:a5:fb:87:3b:0f:a7:7b:b7:0d:54 +# SHA256 Fingerprint: 23:99:56:11:27:a5:71:25:de:8c:ef:ea:61:0d:df:2f:a0:78:b5:c8:06:7f:4e:82:82:90:bf:b8:60:e8:4b:3c +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB +vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W +ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 +IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y +IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh +bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF +9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH +H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H +LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN +/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT +rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw +WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs +exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 +sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ +seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz +4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ +BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR +lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 +7M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G4" +# Serial: 63143484348153506665311985501458640051 +# MD5 Fingerprint: 3a:52:e1:e7:fd:6f:3a:e3:6f:f3:6f:99:1b:f9:22:41 +# SHA1 Fingerprint: 22:d5:d8:df:8f:02:31:d1:8d:f7:9d:b7:cf:8a:2d:64:c9:3f:6c:3a +# SHA256 Fingerprint: 69:dd:d7:ea:90:bb:57:c9:3e:13:5d:c8:5e:a6:fc:d5:48:0b:60:32:39:bd:c4:54:fc:75:8b:2a:26:cf:7f:79 +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp +U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg +SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln +biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm +GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve +fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ +aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj +aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW +kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC +4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga +FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny" +# Serial: 80544274841616 +# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 +# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 +# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA - G2" +# Serial: 10000012 +# MD5 Fingerprint: 7c:a5:0f:f8:5b:9a:7d:6d:30:ae:54:5a:e3:42:a2:8a +# SHA1 Fingerprint: 59:af:82:79:91:86:c7:b4:75:07:cb:cf:03:57:46:eb:04:dd:b7:16 +# SHA256 Fingerprint: 66:8c:83:94:7d:a6:3b:72:4b:ec:e1:74:3c:31:a0:e6:ae:d0:db:8e:c5:b3:1b:e3:77:bb:78:4f:91:b6:71:6f +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX +DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 +qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp +uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU +Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE +pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp +5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M +UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN +GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy +5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv +6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK +eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 +B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ +BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov +L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG +SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS +CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen +5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 +IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK +gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL ++63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL +vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm +bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk +N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC +Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z +ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Label: "Hongkong Post Root CA 1" +# Serial: 1000 +# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca +# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58 +# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2 +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx +FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg +Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG +A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr +b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ +jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn +PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh +ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 +nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h +q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED +MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC +mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 +7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB +oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs +EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO +fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi +AmvZWg== +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Label: "SecureSign RootCA11" +# Serial: 1 +# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26 +# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3 +# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr +MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG +A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 +MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp +Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD +QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz +i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 +h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV +MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 +UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni +8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC +h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm +KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ +X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr +QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 +pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN +QSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Label: "Microsec e-Szigno Root CA 2009" +# Serial: 14014712776195784473 +# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 +# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e +# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 6047274297262753887 +# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3 +# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa +# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy +MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD +VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv +ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl +AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF +661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 +am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 +ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 +PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS +3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k +SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF +3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM +ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g +StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz +Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB +jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +# Issuer: CN=Izenpe.com O=IZENPE S.A. +# Subject: CN=Izenpe.com O=IZENPE S.A. +# Label: "Izenpe.com" +# Serial: 917563065490389241595536686991402621 +# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 +# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 +# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +# Issuer: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. +# Subject: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. +# Label: "Chambers of Commerce Root - 2008" +# Serial: 11806822484801597146 +# MD5 Fingerprint: 5e:80:9e:84:5a:0e:65:0b:17:02:f3:55:18:2a:3e:d7 +# SHA1 Fingerprint: 78:6a:74:ac:76:ab:14:7f:9c:6a:30:50:ba:9e:a8:7e:fe:9a:ce:3c +# SHA256 Fingerprint: 06:3e:4a:fa:c4:91:df:d3:32:f3:08:9b:85:42:e9:46:17:d8:93:d7:fe:94:4e:10:a7:93:7e:e2:9d:96:93:c0 +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz +IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz +MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj +dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw +EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp +MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9 +28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq +VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q +DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR +5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL +ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a +Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl +UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s ++12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5 +Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx +hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV +HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1 ++HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN +YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t +L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy +ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt +IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV +HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w +DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW +PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF +5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1 +glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH +FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2 +pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD +xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG +tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq +jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De +fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ +d0jQ +-----END CERTIFICATE----- + +# Issuer: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. +# Subject: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. +# Label: "Global Chambersign Root - 2008" +# Serial: 14541511773111788494 +# MD5 Fingerprint: 9e:80:ff:78:01:0c:2e:c1:36:bd:fe:96:90:6e:08:f3 +# SHA1 Fingerprint: 4a:bd:ee:ec:95:0d:35:9c:89:ae:c7:52:a1:2c:5b:29:f6:d6:aa:0c +# SHA256 Fingerprint: 13:63:35:43:93:34:a7:69:80:16:a0:d3:24:de:72:28:4e:07:9d:7b:52:20:bb:8f:bd:74:78:16:ee:be:ba:ca +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx +MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy +cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG +A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl +BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed +KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 +G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 +zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 +ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG +HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 +Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V +yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e +beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r +6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog +zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW +BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr +ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp +ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk +cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt +YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC +CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow +KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI +hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ +UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz +X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x +fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz +a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd +Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd +SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O +AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso +M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge +v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Services Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 +# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f +# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Commercial O=AffirmTrust +# Subject: CN=AffirmTrust Commercial O=AffirmTrust +# Label: "AffirmTrust Commercial" +# Serial: 8608355977964138876 +# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 +# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 +# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Networking O=AffirmTrust +# Subject: CN=AffirmTrust Networking O=AffirmTrust +# Label: "AffirmTrust Networking" +# Serial: 8957382827206547757 +# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f +# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f +# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium O=AffirmTrust +# Subject: CN=AffirmTrust Premium O=AffirmTrust +# Label: "AffirmTrust Premium" +# Serial: 7893706540734352110 +# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 +# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 +# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust +# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust +# Label: "AffirmTrust Premium ECC" +# Serial: 8401224907861490260 +# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d +# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb +# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Label: "TWCA Root Certification Authority" +# Serial: 1 +# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 +# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 +# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Label: "Security Communication RootCA2" +# Serial: 0 +# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 +# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 +# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2011" +# Serial: 0 +# MD5 Fingerprint: 73:9f:4c:4b:73:5b:79:e9:fa:ba:1c:ef:6e:cb:d5:c9 +# SHA1 Fingerprint: fe:45:65:9b:79:03:5b:98:a1:61:b5:51:2e:ac:da:58:09:48:22:4d +# SHA256 Fingerprint: bc:10:4f:15:a4:8b:e7:09:dc:a5:42:a7:e1:d4:b9:df:6f:05:45:27:e8:02:ea:a9:2d:59:54:44:25:8a:fe:71 +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix +RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p +YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw +NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK +EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl +cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz +dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ +fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns +bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD +75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP +FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV +HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp +5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu +b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA +A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p +6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7 +dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys +Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI +l7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Label: "Actalis Authentication Root CA" +# Serial: 6271844772424770508 +# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 +# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac +# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +# Issuer: O=Trustis Limited OU=Trustis FPS Root CA +# Subject: O=Trustis Limited OU=Trustis FPS Root CA +# Label: "Trustis FPS Root CA" +# Serial: 36053640375399034304724988975563710553 +# MD5 Fingerprint: 30:c9:e7:1e:6b:e6:14:eb:65:b2:16:69:20:31:67:4d +# SHA1 Fingerprint: 3b:c0:38:0b:33:c3:f6:a6:0c:86:15:22:93:d9:df:f5:4b:81:c0:04 +# SHA256 Fingerprint: c1:b4:82:99:ab:a5:20:8f:e9:63:0a:ce:55:ca:68:a0:3e:da:5a:51:9c:88:02:a0:d3:a6:73:be:8f:8e:55:7d +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL +ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx +MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc +MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+ +AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH +iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj +vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA +0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB +OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/ +BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E +FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01 +GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW +zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4 +1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE +f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F +jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN +ZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 2 Root CA" +# Serial: 2 +# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 +# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 +# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 3 Root CA" +# Serial: 2 +# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec +# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 +# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 3" +# Serial: 1 +# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef +# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 +# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +# Issuer: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus +# Subject: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus +# Label: "EE Certification Centre Root CA" +# Serial: 112324828676200291871926431888494945866 +# MD5 Fingerprint: 43:5e:88:d4:7d:1a:4a:7e:fd:84:2e:52:eb:01:d4:6f +# SHA1 Fingerprint: c9:a8:b9:e7:55:80:5e:58:e3:53:77:a7:25:eb:af:c3:7b:27:cc:d7 +# SHA256 Fingerprint: 3e:84:ba:43:42:90:85:16:e7:75:73:c0:99:2f:09:79:ca:08:4e:46:85:68:1f:f1:95:cc:ba:8a:22:9b:8a:76 +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1 +MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1 +czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG +CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy +MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl +ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS +b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy +euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO +bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw +WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d +MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE +1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/ +zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB +BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF +BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV +v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG +E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW +iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v +GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 2009" +# Serial: 623603 +# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f +# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 +# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 EV 2009" +# Serial: 623604 +# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 +# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 +# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R2 O=Disig a.s. +# Subject: CN=CA Disig Root R2 O=Disig a.s. +# Label: "CA Disig Root R2" +# Serial: 10572350602393338211 +# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 +# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 +# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Label: "ACCVRAIZ1" +# Serial: 6828503384748696800 +# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 +# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 +# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA Global Root CA" +# Serial: 3262 +# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 +# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 +# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Label: "TeliaSonera Root CA v1" +# Serial: 199041966741090107964904287217786801558 +# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c +# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 +# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +# Issuer: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi +# Subject: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi +# Label: "E-Tugra Certification Authority" +# Serial: 7667447206703254355 +# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49 +# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39 +# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC +aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV +BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 +Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz +MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ +BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp +em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY +B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH +D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF +Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo +q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D +k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH +fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut +dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM +ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 +zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX +U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 +Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 +XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF +Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR +HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY +GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c +77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 ++GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK +vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 +FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl +yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P +AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD +y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d +NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot 2011 O=Atos +# Subject: CN=Atos TrustedRoot 2011 O=Atos +# Label: "Atos TrustedRoot 2011" +# Serial: 6643877497813316402 +# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 +# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 +# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 14367148294922964480859022125800977897474 +# MD5 Fingerprint: 20:f0:27:68:d1:7e:a0:9d:0e:e6:2a:ca:df:5c:89:8e +# SHA1 Fingerprint: 69:69:56:2e:40:80:f4:24:a1:e7:19:9f:14:ba:f3:ee:58:ab:6a:bb +# SHA256 Fingerprint: be:c9:49:11:c2:95:56:76:db:6c:0a:55:09:86:d7:6e:3b:a0:05:66:7c:44:2c:97:62:b4:fb:b7:73:de:22:8c +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA - G3" +# Serial: 10003001 +# MD5 Fingerprint: 0b:46:67:07:db:10:2f:19:8c:35:50:60:d1:0b:f4:37 +# SHA1 Fingerprint: d8:eb:6b:41:51:92:59:e0:f3:e7:85:00:c0:3d:b6:88:97:c9:ee:fc +# SHA256 Fingerprint: 3c:4f:b0:b9:5a:b8:b3:00:32:f4:32:b8:6f:53:5f:e1:72:c1:85:d0:fd:39:86:58:37:cf:36:18:7f:a6:f4:28 +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX +DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP +cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW +IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX +xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy +KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR +9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az +5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 +6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 +Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP +bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt +BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt +XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd +INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp +LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 +Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp +gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh +/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw +0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A +fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq +4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR +1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ +QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM +94B7IWcnMFk= +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden +# Label: "Staat der Nederlanden EV Root CA" +# Serial: 10000013 +# MD5 Fingerprint: fc:06:af:7b:e8:1a:f1:9a:b4:e8:d2:70:1f:c0:f5:ba +# SHA1 Fingerprint: 76:e2:7e:c1:4f:db:82:c1:c0:a6:75:b5:05:be:3d:29:b4:ed:db:bb +# SHA256 Fingerprint: 4d:24:91:41:4c:fe:95:67:46:ec:4c:ef:a6:cf:6f:72:e2:8a:13:29:43:2f:9d:8a:90:7a:c4:cb:5d:ad:c1:5a +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y +MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg +TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS +b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS +M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC +UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d +Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p +rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l +pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb +j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC +KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS +/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X +cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH +1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP +px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 +MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u +2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS +v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC +wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy +CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e +vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 +Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa +Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL +eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 +FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc +7uzXLg== +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Label: "IdenTrust Commercial Root CA 1" +# Serial: 13298821034946342390520003877796839426 +# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 +# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 +# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Label: "IdenTrust Public Sector Root CA 1" +# Serial: 13298821034946342390521976156843933698 +# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba +# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd +# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G2" +# Serial: 1246989352 +# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 +# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 +# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - EC1" +# Serial: 51543124481930649114116133369 +# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc +# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 +# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority +# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority +# Label: "CFCA EV ROOT" +# Serial: 407555286 +# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 +# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 +# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +# Issuer: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903 +# Subject: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903 +# Label: "Certinomis - Root CA" +# Serial: 1 +# MD5 Fingerprint: 14:0a:fd:8d:a8:28:b5:38:69:db:56:7e:61:22:03:3f +# SHA1 Fingerprint: 9d:70:bb:01:a5:a4:a0:18:11:2e:f7:1c:01:b9:32:c5:34:e7:88:a8 +# SHA256 Fingerprint: 2a:99:f5:bc:11:74:b7:3c:bb:1d:62:08:84:e0:1c:34:e5:1c:cb:39:78:da:12:5f:0e:33:26:88:83:bf:41:58 +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb +BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz +MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx +FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g +Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2 +fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl +LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV +WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF +TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb +5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc +CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri +wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ +wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG +m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4 +F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng +WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0 +2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF +AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/ +0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw +F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS +g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj +qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN +h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/ +ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V +btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj +Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ +8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW +gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GB CA" +# Serial: 157768595616588414422159278966750757568 +# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d +# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed +# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6 +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Label: "SZAFIR ROOT CA2" +# Serial: 357043034767186914217277344587386743377558296292 +# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99 +# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de +# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA 2" +# Serial: 44979900017204383099463764357512596969 +# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2 +# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92 +# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04 +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce +# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6 +# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36 +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef +# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66 +# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33 +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X1 O=Internet Security Research Group +# Subject: CN=ISRG Root X1 O=Internet Security Research Group +# Label: "ISRG Root X1" +# Serial: 172886928669790476064670243504169061120 +# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e +# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 +# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +# Issuer: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Subject: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Label: "AC RAIZ FNMT-RCM" +# Serial: 485876308206448804701554682760554759 +# MD5 Fingerprint: e2:09:04:b4:d3:bd:d1:a0:14:fd:1a:d2:47:c4:57:1d +# SHA1 Fingerprint: ec:50:35:07:b2:15:c4:95:62:19:e2:a8:9a:5b:42:99:2c:4c:2c:20 +# SHA256 Fingerprint: eb:c5:57:0c:29:01:8c:4d:67:b1:aa:12:7b:af:12:f7:03:b4:61:1e:bc:17:b7:da:b5:57:38:94:17:9b:93:fa +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 1 O=Amazon +# Subject: CN=Amazon Root CA 1 O=Amazon +# Label: "Amazon Root CA 1" +# Serial: 143266978916655856878034712317230054538369994 +# MD5 Fingerprint: 43:c6:bf:ae:ec:fe:ad:2f:18:c6:88:68:30:fc:c8:e6 +# SHA1 Fingerprint: 8d:a7:f9:65:ec:5e:fc:37:91:0f:1c:6e:59:fd:c1:cc:6a:6e:de:16 +# SHA256 Fingerprint: 8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 2 O=Amazon +# Subject: CN=Amazon Root CA 2 O=Amazon +# Label: "Amazon Root CA 2" +# Serial: 143266982885963551818349160658925006970653239 +# MD5 Fingerprint: c8:e5:8d:ce:a8:42:e2:7a:c0:2a:5c:7c:9e:26:bf:66 +# SHA1 Fingerprint: 5a:8c:ef:45:d7:a6:98:59:76:7a:8c:8b:44:96:b5:78:cf:47:4b:1a +# SHA256 Fingerprint: 1b:a5:b2:aa:8c:65:40:1a:82:96:01:18:f8:0b:ec:4f:62:30:4d:83:ce:c4:71:3a:19:c3:9c:01:1e:a4:6d:b4 +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 3 O=Amazon +# Subject: CN=Amazon Root CA 3 O=Amazon +# Label: "Amazon Root CA 3" +# Serial: 143266986699090766294700635381230934788665930 +# MD5 Fingerprint: a0:d4:ef:0b:f7:b5:d8:49:95:2a:ec:f5:c4:fc:81:87 +# SHA1 Fingerprint: 0d:44:dd:8c:3c:8c:1a:1a:58:75:64:81:e9:0f:2e:2a:ff:b3:d2:6e +# SHA256 Fingerprint: 18:ce:6c:fe:7b:f1:4e:60:b2:e3:47:b8:df:e8:68:cb:31:d0:2e:bb:3a:da:27:15:69:f5:03:43:b4:6d:b3:a4 +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 4 O=Amazon +# Subject: CN=Amazon Root CA 4 O=Amazon +# Label: "Amazon Root CA 4" +# Serial: 143266989758080763974105200630763877849284878 +# MD5 Fingerprint: 89:bc:27:d5:eb:17:8d:06:6a:69:d5:fd:89:47:b4:cd +# SHA1 Fingerprint: f6:10:84:07:d6:f8:bb:67:98:0c:c2:e2:44:c2:eb:ae:1c:ef:63:be +# SHA256 Fingerprint: e3:5d:28:41:9e:d0:20:25:cf:a6:90:38:cd:62:39:62:45:8d:a5:c6:95:fb:de:a3:c2:2b:0b:fb:25:89:70:92 +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +# Issuer: CN=LuxTrust Global Root 2 O=LuxTrust S.A. +# Subject: CN=LuxTrust Global Root 2 O=LuxTrust S.A. +# Label: "LuxTrust Global Root 2" +# Serial: 59914338225734147123941058376788110305822489521 +# MD5 Fingerprint: b2:e1:09:00:61:af:f7:f1:91:6f:c4:ad:8d:5e:3b:7c +# SHA1 Fingerprint: 1e:0e:56:19:0a:d1:8b:25:98:b2:04:44:ff:66:8a:04:17:99:5f:3f +# SHA256 Fingerprint: 54:45:5f:71:29:c2:0b:14:47:c4:18:f9:97:16:8f:24:c5:8f:c5:02:3b:f5:da:5b:e2:eb:6e:1d:d8:90:2e:d5 +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" +# Serial: 1 +# MD5 Fingerprint: dc:00:81:dc:69:2f:3e:2f:b0:3b:f6:3d:5a:91:8e:49 +# SHA1 Fingerprint: 31:43:64:9b:ec:ce:27:ec:ed:3a:3f:0b:8f:0d:e4:e8:91:dd:ee:ca +# SHA256 Fingerprint: 46:ed:c3:68:90:46:d5:3a:45:3f:b3:10:4a:b8:0d:ca:ec:65:8b:26:60:ea:16:29:dd:7e:86:79:90:64:87:16 +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +# Issuer: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Subject: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Label: "GDCA TrustAUTH R5 ROOT" +# Serial: 9009899650740120186 +# MD5 Fingerprint: 63:cc:d9:3d:34:35:5c:6f:53:a3:e2:08:70:48:1f:b4 +# SHA1 Fingerprint: 0f:36:38:5b:81:1a:25:c3:9b:31:4e:83:ca:e9:34:66:70:cc:74:b4 +# SHA256 Fingerprint: bf:ff:8f:d0:44:33:48:7d:6a:8a:a6:0c:1a:29:76:7a:9f:c2:bb:b0:5e:42:0f:71:3a:13:b9:92:89:1d:38:93 +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE +BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 +MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w +HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj +Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj +TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u +KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj +qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm +MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 +ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP +zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk +L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC +jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA +HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC +AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm +DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 +COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry +L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf +JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg +IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io +2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV +09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ +XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq +T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe +MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +# Issuer: CN=TrustCor RootCert CA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority +# Subject: CN=TrustCor RootCert CA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority +# Label: "TrustCor RootCert CA-1" +# Serial: 15752444095811006489 +# MD5 Fingerprint: 6e:85:f1:dc:1a:00:d3:22:d5:b2:b2:ac:6b:37:05:45 +# SHA1 Fingerprint: ff:bd:cd:e7:82:c8:43:5e:3c:6f:26:86:5c:ca:a8:3a:45:5b:c3:0a +# SHA256 Fingerprint: d4:0e:9c:86:cd:8f:e4:68:c1:77:69:59:f4:9e:a7:74:fa:54:86:84:b6:c4:06:f3:90:92:61:f4:dc:e2:57:5c +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYD +VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk +MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29y +IFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkxMjMxMTcyMzE2WjCB +pDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFuYW1h +IENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUG +A1UECwweVHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZU +cnVzdENvciBSb290Q2VydCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAv463leLCJhJrMxnHQFgKq1mqjQCj/IDHUHuO1CAmujIS2CNUSSUQIpid +RtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4pQa81QBeCQryJ3pS/C3V +seq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0JEsq1pme +9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CV +EY4hgLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorW +hnAbJN7+KIor0Gqw/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/ +DeOxCbeKyKsZn3MzUOcwHwYDVR0jBBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5mDo4Nvu7Zp5I +/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf +ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZ +yonnMlo2HD6CqFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djts +L1Ac59v2Z3kf9YKVmgenFK+P3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdN +zl/HHk484IkzlQsPpTLWPFp5LBk= +-----END CERTIFICATE----- + +# Issuer: CN=TrustCor RootCert CA-2 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority +# Subject: CN=TrustCor RootCert CA-2 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority +# Label: "TrustCor RootCert CA-2" +# Serial: 2711694510199101698 +# MD5 Fingerprint: a2:e1:f8:18:0b:ba:45:d5:c7:41:2a:bb:37:52:45:64 +# SHA1 Fingerprint: b8:be:6d:cb:56:f1:55:b9:63:d4:12:ca:4e:06:34:c7:94:b2:1c:c0 +# SHA256 Fingerprint: 07:53:e9:40:37:8c:1b:d5:e3:83:6e:39:5d:ae:a5:cb:83:9e:50:46:f1:bd:0e:ae:19:51:cf:10:fe:c7:c9:65 +-----BEGIN CERTIFICATE----- +MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNV +BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw +IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy +dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEfMB0GA1UEAwwWVHJ1c3RDb3Ig +Um9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEyMzExNzI2MzlaMIGk +MQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEg +Q2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYD +VQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRy +dXN0Q29yIFJvb3RDZXJ0IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCnIG7CKqJiJJWQdsg4foDSq8GbZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+ +QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9NkRvRUqdw6VC0xK5mC8tkq +1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1oYxOdqHp +2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nK +DOObXUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hape +az6LMvYHL1cEksr1/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF +3wP+TfSvPd9cW436cOGlfifHhi5qjxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88 +oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQPeSghYA2FFn3XVDjxklb9tTNM +g9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+CtgrKAmrhQhJ8Z3 +mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh +8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAd +BgNVHQ4EFgQU2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6U +nrybPZx9mCAZ5YwwYrIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw +DQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/hOsh80QA9z+LqBrWyOrsGS2h60COX +dKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnpkpfbsEZC89NiqpX+ +MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv2wnL +/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RX +CI/hOWB3S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYa +ZH9bDTMJBzN7Bj8RpFxwPIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW +2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dvDDqPys/cA8GiCcjl/YBeyGBCARsaU1q7 +N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYURpFHmygk71dSTlxCnKr3 +Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANExdqtvArB +As8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp +5KeXRKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu +1uwJ +-----END CERTIFICATE----- + +# Issuer: CN=TrustCor ECA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority +# Subject: CN=TrustCor ECA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority +# Label: "TrustCor ECA-1" +# Serial: 9548242946988625984 +# MD5 Fingerprint: 27:92:23:1d:0a:f5:40:7c:e9:e6:6b:9d:d8:f5:e7:6c +# SHA1 Fingerprint: 58:d1:df:95:95:67:6b:63:c0:f0:5b:1c:17:4d:8b:84:0b:c8:78:bd +# SHA256 Fingerprint: 5a:88:5d:b1:9c:01:d9:12:c5:75:93:88:93:8c:af:bb:df:03:1a:b2:d4:8e:91:ee:15:58:9b:42:97:1d:03:9c +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD +VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk +MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFzAVBgNVBAMMDlRydXN0Q29y +IEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3MjgwN1owgZwxCzAJBgNV +BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw +IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy +dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3Ig +RUNBLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb +3w9U73NjKYKtR8aja+3+XzP4Q1HpGjORMRegdMTUpwHmspI+ap3tDvl0mEDTPwOA +BoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23xFUfJ3zSCNV2HykVh0A5 +3ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmcp0yJF4Ou +owReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/ +wZ0+fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZF +ZtS6mFjBAgMBAAGjYzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAf +BgNVHSMEGDAWgBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEABT41XBVwm8nHc2Fv +civUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u/ukZMjgDfxT2 +AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F +hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50 +soIipX1TH0XsJ5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BI +WJZpTdwHjFGTot+fDz2LYLSCjaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1Wi +tJ/X5g== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Label: "SSL.com Root Certification Authority RSA" +# Serial: 8875640296558310041 +# MD5 Fingerprint: 86:69:12:c0:70:f1:ec:ac:ac:c2:d5:bc:a5:5b:a1:29 +# SHA1 Fingerprint: b7:ab:33:08:d1:ea:44:77:ba:14:80:12:5a:6f:bd:a9:36:49:0c:bb +# SHA256 Fingerprint: 85:66:6a:56:2e:e0:be:5c:e9:25:c1:d8:89:0a:6f:76:a8:7e:c1:6d:4d:7d:5f:29:ea:74:19:cf:20:12:3b:69 +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com Root Certification Authority ECC" +# Serial: 8495723813297216424 +# MD5 Fingerprint: 2e:da:e4:39:7f:9c:8f:37:d1:70:9f:26:17:51:3a:8e +# SHA1 Fingerprint: c3:19:7c:39:24:e6:54:af:1b:c4:ab:20:95:7a:e2:c3:0e:13:02:6a +# SHA256 Fingerprint: 34:17:bb:06:cc:60:07:da:1b:96:1c:92:0b:8a:b4:ce:3f:ad:82:0e:4a:a3:0b:9a:cb:c4:a7:4e:bd:ce:bc:65 +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority RSA R2" +# Serial: 6248227494352943350 +# MD5 Fingerprint: e1:1e:31:58:1a:ae:54:53:02:f6:17:6a:11:7b:4d:95 +# SHA1 Fingerprint: 74:3a:f0:52:9b:d0:32:a0:f4:4a:83:cd:d4:ba:a9:7b:7c:2e:c4:9a +# SHA256 Fingerprint: 2e:7b:f1:6c:c2:24:85:a7:bb:e2:aa:86:96:75:07:61:b0:ae:39:be:3b:2f:e9:d0:cc:6d:4e:f7:34:91:42:5c +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority ECC" +# Serial: 3182246526754555285 +# MD5 Fingerprint: 59:53:22:65:83:42:01:54:c0:ce:42:b9:5a:7c:f2:90 +# SHA1 Fingerprint: 4c:dd:51:a3:d1:f5:20:32:14:b0:c6:c5:32:23:03:91:c7:46:42:6d +# SHA256 Fingerprint: 22:a2:c1:f7:bd:ed:70:4c:c1:e7:01:b5:f4:08:c3:10:88:0f:e9:56:b5:de:2a:4a:44:f9:9c:87:3a:25:a7:c8 +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Label: "GlobalSign Root CA - R6" +# Serial: 1417766617973444989252670301619537 +# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae +# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1 +# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69 +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg +MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx +MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET +MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI +xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k +ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD +aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw +LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw +1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX +k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 +SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h +bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n +WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY +rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce +MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu +bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt +Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 +55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj +vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf +cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz +oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp +nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs +pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v +JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R +8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 +5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GC CA" +# Serial: 44084345621038548146064804565436152554 +# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23 +# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31 +# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw +CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 +bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg +Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ +BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu +ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS +b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni +eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W +p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T +rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV +57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg +Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- diff --git a/venv/Lib/site-packages/pip/_vendor/certifi/core.py b/venv/Lib/site-packages/pip/_vendor/certifi/core.py new file mode 100644 index 0000000..eab9d1d --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/certifi/core.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +certifi.py +~~~~~~~~~~ + +This module returns the installation location of cacert.pem. +""" +import os +import warnings + + +class DeprecatedBundleWarning(DeprecationWarning): + """ + The weak security bundle is being deprecated. Please bother your service + provider to get them to stop using cross-signed roots. + """ + + +def where(): + f = os.path.dirname(__file__) + + return os.path.join(f, 'cacert.pem') + + +def old_where(): + warnings.warn( + "The weak security bundle has been removed. certifi.old_where() is now an alias " + "of certifi.where(). Please update your code to use certifi.where() instead. " + "certifi.old_where() will be removed in 2018.", + DeprecatedBundleWarning + ) + return where() + +if __name__ == '__main__': + print(where()) diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/__init__.py b/venv/Lib/site-packages/pip/_vendor/chardet/__init__.py new file mode 100644 index 0000000..0f9f820 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/__init__.py @@ -0,0 +1,39 @@ +######################## BEGIN LICENSE BLOCK ######################## +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + + +from .compat import PY2, PY3 +from .universaldetector import UniversalDetector +from .version import __version__, VERSION + + +def detect(byte_str): + """ + Detect the encoding of the given byte string. + + :param byte_str: The byte sequence to examine. + :type byte_str: ``bytes`` or ``bytearray`` + """ + if not isinstance(byte_str, bytearray): + if not isinstance(byte_str, bytes): + raise TypeError('Expected object of type bytes or bytearray, got: ' + '{0}'.format(type(byte_str))) + else: + byte_str = bytearray(byte_str) + detector = UniversalDetector() + detector.feed(byte_str) + return detector.close() diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/big5freq.py b/venv/Lib/site-packages/pip/_vendor/chardet/big5freq.py new file mode 100644 index 0000000..38f3251 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/big5freq.py @@ -0,0 +1,386 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Big5 frequency table +# by Taiwan's Mandarin Promotion Council +# <http://www.edu.tw:81/mandr/> +# +# 128 --> 0.42261 +# 256 --> 0.57851 +# 512 --> 0.74851 +# 1024 --> 0.89384 +# 2048 --> 0.97583 +# +# Ideal Distribution Ratio = 0.74851/(1-0.74851) =2.98 +# Random Distribution Ration = 512/(5401-512)=0.105 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR + +BIG5_TYPICAL_DISTRIBUTION_RATIO = 0.75 + +#Char to FreqOrder table +BIG5_TABLE_SIZE = 5376 + +BIG5_CHAR_TO_FREQ_ORDER = ( + 1,1801,1506, 255,1431, 198, 9, 82, 6,5008, 177, 202,3681,1256,2821, 110, # 16 +3814, 33,3274, 261, 76, 44,2114, 16,2946,2187,1176, 659,3971, 26,3451,2653, # 32 +1198,3972,3350,4202, 410,2215, 302, 590, 361,1964, 8, 204, 58,4510,5009,1932, # 48 + 63,5010,5011, 317,1614, 75, 222, 159,4203,2417,1480,5012,3555,3091, 224,2822, # 64 +3682, 3, 10,3973,1471, 29,2787,1135,2866,1940, 873, 130,3275,1123, 312,5013, # 80 +4511,2052, 507, 252, 682,5014, 142,1915, 124, 206,2947, 34,3556,3204, 64, 604, # 96 +5015,2501,1977,1978, 155,1991, 645, 641,1606,5016,3452, 337, 72, 406,5017, 80, # 112 + 630, 238,3205,1509, 263, 939,1092,2654, 756,1440,1094,3453, 449, 69,2987, 591, # 128 + 179,2096, 471, 115,2035,1844, 60, 50,2988, 134, 806,1869, 734,2036,3454, 180, # 144 + 995,1607, 156, 537,2907, 688,5018, 319,1305, 779,2145, 514,2379, 298,4512, 359, # 160 +2502, 90,2716,1338, 663, 11, 906,1099,2553, 20,2441, 182, 532,1716,5019, 732, # 176 +1376,4204,1311,1420,3206, 25,2317,1056, 113, 399, 382,1950, 242,3455,2474, 529, # 192 +3276, 475,1447,3683,5020, 117, 21, 656, 810,1297,2300,2334,3557,5021, 126,4205, # 208 + 706, 456, 150, 613,4513, 71,1118,2037,4206, 145,3092, 85, 835, 486,2115,1246, # 224 +1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,5022,2128,2359, 347,3815, 221, # 240 +3558,3135,5023,1956,1153,4207, 83, 296,1199,3093, 192, 624, 93,5024, 822,1898, # 256 +2823,3136, 795,2065, 991,1554,1542,1592, 27, 43,2867, 859, 139,1456, 860,4514, # 272 + 437, 712,3974, 164,2397,3137, 695, 211,3037,2097, 195,3975,1608,3559,3560,3684, # 288 +3976, 234, 811,2989,2098,3977,2233,1441,3561,1615,2380, 668,2077,1638, 305, 228, # 304 +1664,4515, 467, 415,5025, 262,2099,1593, 239, 108, 300, 200,1033, 512,1247,2078, # 320 +5026,5027,2176,3207,3685,2682, 593, 845,1062,3277, 88,1723,2038,3978,1951, 212, # 336 + 266, 152, 149, 468,1899,4208,4516, 77, 187,5028,3038, 37, 5,2990,5029,3979, # 352 +5030,5031, 39,2524,4517,2908,3208,2079, 55, 148, 74,4518, 545, 483,1474,1029, # 368 +1665, 217,1870,1531,3138,1104,2655,4209, 24, 172,3562, 900,3980,3563,3564,4519, # 384 + 32,1408,2824,1312, 329, 487,2360,2251,2717, 784,2683, 4,3039,3351,1427,1789, # 400 + 188, 109, 499,5032,3686,1717,1790, 888,1217,3040,4520,5033,3565,5034,3352,1520, # 416 +3687,3981, 196,1034, 775,5035,5036, 929,1816, 249, 439, 38,5037,1063,5038, 794, # 432 +3982,1435,2301, 46, 178,3278,2066,5039,2381,5040, 214,1709,4521, 804, 35, 707, # 448 + 324,3688,1601,2554, 140, 459,4210,5041,5042,1365, 839, 272, 978,2262,2580,3456, # 464 +2129,1363,3689,1423, 697, 100,3094, 48, 70,1231, 495,3139,2196,5043,1294,5044, # 480 +2080, 462, 586,1042,3279, 853, 256, 988, 185,2382,3457,1698, 434,1084,5045,3458, # 496 + 314,2625,2788,4522,2335,2336, 569,2285, 637,1817,2525, 757,1162,1879,1616,3459, # 512 + 287,1577,2116, 768,4523,1671,2868,3566,2526,1321,3816, 909,2418,5046,4211, 933, # 528 +3817,4212,2053,2361,1222,4524, 765,2419,1322, 786,4525,5047,1920,1462,1677,2909, # 544 +1699,5048,4526,1424,2442,3140,3690,2600,3353,1775,1941,3460,3983,4213, 309,1369, # 560 +1130,2825, 364,2234,1653,1299,3984,3567,3985,3986,2656, 525,1085,3041, 902,2001, # 576 +1475, 964,4527, 421,1845,1415,1057,2286, 940,1364,3141, 376,4528,4529,1381, 7, # 592 +2527, 983,2383, 336,1710,2684,1846, 321,3461, 559,1131,3042,2752,1809,1132,1313, # 608 + 265,1481,1858,5049, 352,1203,2826,3280, 167,1089, 420,2827, 776, 792,1724,3568, # 624 +4214,2443,3281,5050,4215,5051, 446, 229, 333,2753, 901,3818,1200,1557,4530,2657, # 640 +1921, 395,2754,2685,3819,4216,1836, 125, 916,3209,2626,4531,5052,5053,3820,5054, # 656 +5055,5056,4532,3142,3691,1133,2555,1757,3462,1510,2318,1409,3569,5057,2146, 438, # 672 +2601,2910,2384,3354,1068, 958,3043, 461, 311,2869,2686,4217,1916,3210,4218,1979, # 688 + 383, 750,2755,2627,4219, 274, 539, 385,1278,1442,5058,1154,1965, 384, 561, 210, # 704 + 98,1295,2556,3570,5059,1711,2420,1482,3463,3987,2911,1257, 129,5060,3821, 642, # 720 + 523,2789,2790,2658,5061, 141,2235,1333, 68, 176, 441, 876, 907,4220, 603,2602, # 736 + 710, 171,3464, 404, 549, 18,3143,2398,1410,3692,1666,5062,3571,4533,2912,4534, # 752 +5063,2991, 368,5064, 146, 366, 99, 871,3693,1543, 748, 807,1586,1185, 22,2263, # 768 + 379,3822,3211,5065,3212, 505,1942,2628,1992,1382,2319,5066, 380,2362, 218, 702, # 784 +1818,1248,3465,3044,3572,3355,3282,5067,2992,3694, 930,3283,3823,5068, 59,5069, # 800 + 585, 601,4221, 497,3466,1112,1314,4535,1802,5070,1223,1472,2177,5071, 749,1837, # 816 + 690,1900,3824,1773,3988,1476, 429,1043,1791,2236,2117, 917,4222, 447,1086,1629, # 832 +5072, 556,5073,5074,2021,1654, 844,1090, 105, 550, 966,1758,2828,1008,1783, 686, # 848 +1095,5075,2287, 793,1602,5076,3573,2603,4536,4223,2948,2302,4537,3825, 980,2503, # 864 + 544, 353, 527,4538, 908,2687,2913,5077, 381,2629,1943,1348,5078,1341,1252, 560, # 880 +3095,5079,3467,2870,5080,2054, 973, 886,2081, 143,4539,5081,5082, 157,3989, 496, # 896 +4224, 57, 840, 540,2039,4540,4541,3468,2118,1445, 970,2264,1748,1966,2082,4225, # 912 +3144,1234,1776,3284,2829,3695, 773,1206,2130,1066,2040,1326,3990,1738,1725,4226, # 928 + 279,3145, 51,1544,2604, 423,1578,2131,2067, 173,4542,1880,5083,5084,1583, 264, # 944 + 610,3696,4543,2444, 280, 154,5085,5086,5087,1739, 338,1282,3096, 693,2871,1411, # 960 +1074,3826,2445,5088,4544,5089,5090,1240, 952,2399,5091,2914,1538,2688, 685,1483, # 976 +4227,2475,1436, 953,4228,2055,4545, 671,2400, 79,4229,2446,3285, 608, 567,2689, # 992 +3469,4230,4231,1691, 393,1261,1792,2401,5092,4546,5093,5094,5095,5096,1383,1672, # 1008 +3827,3213,1464, 522,1119, 661,1150, 216, 675,4547,3991,1432,3574, 609,4548,2690, # 1024 +2402,5097,5098,5099,4232,3045, 0,5100,2476, 315, 231,2447, 301,3356,4549,2385, # 1040 +5101, 233,4233,3697,1819,4550,4551,5102, 96,1777,1315,2083,5103, 257,5104,1810, # 1056 +3698,2718,1139,1820,4234,2022,1124,2164,2791,1778,2659,5105,3097, 363,1655,3214, # 1072 +5106,2993,5107,5108,5109,3992,1567,3993, 718, 103,3215, 849,1443, 341,3357,2949, # 1088 +1484,5110,1712, 127, 67, 339,4235,2403, 679,1412, 821,5111,5112, 834, 738, 351, # 1104 +2994,2147, 846, 235,1497,1881, 418,1993,3828,2719, 186,1100,2148,2756,3575,1545, # 1120 +1355,2950,2872,1377, 583,3994,4236,2581,2995,5113,1298,3699,1078,2557,3700,2363, # 1136 + 78,3829,3830, 267,1289,2100,2002,1594,4237, 348, 369,1274,2197,2178,1838,4552, # 1152 +1821,2830,3701,2757,2288,2003,4553,2951,2758, 144,3358, 882,4554,3995,2759,3470, # 1168 +4555,2915,5114,4238,1726, 320,5115,3996,3046, 788,2996,5116,2831,1774,1327,2873, # 1184 +3997,2832,5117,1306,4556,2004,1700,3831,3576,2364,2660, 787,2023, 506, 824,3702, # 1200 + 534, 323,4557,1044,3359,2024,1901, 946,3471,5118,1779,1500,1678,5119,1882,4558, # 1216 + 165, 243,4559,3703,2528, 123, 683,4239, 764,4560, 36,3998,1793, 589,2916, 816, # 1232 + 626,1667,3047,2237,1639,1555,1622,3832,3999,5120,4000,2874,1370,1228,1933, 891, # 1248 +2084,2917, 304,4240,5121, 292,2997,2720,3577, 691,2101,4241,1115,4561, 118, 662, # 1264 +5122, 611,1156, 854,2386,1316,2875, 2, 386, 515,2918,5123,5124,3286, 868,2238, # 1280 +1486, 855,2661, 785,2216,3048,5125,1040,3216,3578,5126,3146, 448,5127,1525,5128, # 1296 +2165,4562,5129,3833,5130,4242,2833,3579,3147, 503, 818,4001,3148,1568, 814, 676, # 1312 +1444, 306,1749,5131,3834,1416,1030, 197,1428, 805,2834,1501,4563,5132,5133,5134, # 1328 +1994,5135,4564,5136,5137,2198, 13,2792,3704,2998,3149,1229,1917,5138,3835,2132, # 1344 +5139,4243,4565,2404,3580,5140,2217,1511,1727,1120,5141,5142, 646,3836,2448, 307, # 1360 +5143,5144,1595,3217,5145,5146,5147,3705,1113,1356,4002,1465,2529,2530,5148, 519, # 1376 +5149, 128,2133, 92,2289,1980,5150,4003,1512, 342,3150,2199,5151,2793,2218,1981, # 1392 +3360,4244, 290,1656,1317, 789, 827,2365,5152,3837,4566, 562, 581,4004,5153, 401, # 1408 +4567,2252, 94,4568,5154,1399,2794,5155,1463,2025,4569,3218,1944,5156, 828,1105, # 1424 +4245,1262,1394,5157,4246, 605,4570,5158,1784,2876,5159,2835, 819,2102, 578,2200, # 1440 +2952,5160,1502, 436,3287,4247,3288,2836,4005,2919,3472,3473,5161,2721,2320,5162, # 1456 +5163,2337,2068, 23,4571, 193, 826,3838,2103, 699,1630,4248,3098, 390,1794,1064, # 1472 +3581,5164,1579,3099,3100,1400,5165,4249,1839,1640,2877,5166,4572,4573, 137,4250, # 1488 + 598,3101,1967, 780, 104, 974,2953,5167, 278, 899, 253, 402, 572, 504, 493,1339, # 1504 +5168,4006,1275,4574,2582,2558,5169,3706,3049,3102,2253, 565,1334,2722, 863, 41, # 1520 +5170,5171,4575,5172,1657,2338, 19, 463,2760,4251, 606,5173,2999,3289,1087,2085, # 1536 +1323,2662,3000,5174,1631,1623,1750,4252,2691,5175,2878, 791,2723,2663,2339, 232, # 1552 +2421,5176,3001,1498,5177,2664,2630, 755,1366,3707,3290,3151,2026,1609, 119,1918, # 1568 +3474, 862,1026,4253,5178,4007,3839,4576,4008,4577,2265,1952,2477,5179,1125, 817, # 1584 +4254,4255,4009,1513,1766,2041,1487,4256,3050,3291,2837,3840,3152,5180,5181,1507, # 1600 +5182,2692, 733, 40,1632,1106,2879, 345,4257, 841,2531, 230,4578,3002,1847,3292, # 1616 +3475,5183,1263, 986,3476,5184, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562, # 1632 +4010,4011,2954, 967,2761,2665,1349, 592,2134,1692,3361,3003,1995,4258,1679,4012, # 1648 +1902,2188,5185, 739,3708,2724,1296,1290,5186,4259,2201,2202,1922,1563,2605,2559, # 1664 +1871,2762,3004,5187, 435,5188, 343,1108, 596, 17,1751,4579,2239,3477,3709,5189, # 1680 +4580, 294,3582,2955,1693, 477, 979, 281,2042,3583, 643,2043,3710,2631,2795,2266, # 1696 +1031,2340,2135,2303,3584,4581, 367,1249,2560,5190,3585,5191,4582,1283,3362,2005, # 1712 + 240,1762,3363,4583,4584, 836,1069,3153, 474,5192,2149,2532, 268,3586,5193,3219, # 1728 +1521,1284,5194,1658,1546,4260,5195,3587,3588,5196,4261,3364,2693,1685,4262, 961, # 1744 +1673,2632, 190,2006,2203,3841,4585,4586,5197, 570,2504,3711,1490,5198,4587,2633, # 1760 +3293,1957,4588, 584,1514, 396,1045,1945,5199,4589,1968,2449,5200,5201,4590,4013, # 1776 + 619,5202,3154,3294, 215,2007,2796,2561,3220,4591,3221,4592, 763,4263,3842,4593, # 1792 +5203,5204,1958,1767,2956,3365,3712,1174, 452,1477,4594,3366,3155,5205,2838,1253, # 1808 +2387,2189,1091,2290,4264, 492,5206, 638,1169,1825,2136,1752,4014, 648, 926,1021, # 1824 +1324,4595, 520,4596, 997, 847,1007, 892,4597,3843,2267,1872,3713,2405,1785,4598, # 1840 +1953,2957,3103,3222,1728,4265,2044,3714,4599,2008,1701,3156,1551, 30,2268,4266, # 1856 +5207,2027,4600,3589,5208, 501,5209,4267, 594,3478,2166,1822,3590,3479,3591,3223, # 1872 + 829,2839,4268,5210,1680,3157,1225,4269,5211,3295,4601,4270,3158,2341,5212,4602, # 1888 +4271,5213,4015,4016,5214,1848,2388,2606,3367,5215,4603, 374,4017, 652,4272,4273, # 1904 + 375,1140, 798,5216,5217,5218,2366,4604,2269, 546,1659, 138,3051,2450,4605,5219, # 1920 +2254, 612,1849, 910, 796,3844,1740,1371, 825,3845,3846,5220,2920,2562,5221, 692, # 1936 + 444,3052,2634, 801,4606,4274,5222,1491, 244,1053,3053,4275,4276, 340,5223,4018, # 1952 +1041,3005, 293,1168, 87,1357,5224,1539, 959,5225,2240, 721, 694,4277,3847, 219, # 1968 +1478, 644,1417,3368,2666,1413,1401,1335,1389,4019,5226,5227,3006,2367,3159,1826, # 1984 + 730,1515, 184,2840, 66,4607,5228,1660,2958, 246,3369, 378,1457, 226,3480, 975, # 2000 +4020,2959,1264,3592, 674, 696,5229, 163,5230,1141,2422,2167, 713,3593,3370,4608, # 2016 +4021,5231,5232,1186, 15,5233,1079,1070,5234,1522,3224,3594, 276,1050,2725, 758, # 2032 +1126, 653,2960,3296,5235,2342, 889,3595,4022,3104,3007, 903,1250,4609,4023,3481, # 2048 +3596,1342,1681,1718, 766,3297, 286, 89,2961,3715,5236,1713,5237,2607,3371,3008, # 2064 +5238,2962,2219,3225,2880,5239,4610,2505,2533, 181, 387,1075,4024, 731,2190,3372, # 2080 +5240,3298, 310, 313,3482,2304, 770,4278, 54,3054, 189,4611,3105,3848,4025,5241, # 2096 +1230,1617,1850, 355,3597,4279,4612,3373, 111,4280,3716,1350,3160,3483,3055,4281, # 2112 +2150,3299,3598,5242,2797,4026,4027,3009, 722,2009,5243,1071, 247,1207,2343,2478, # 2128 +1378,4613,2010, 864,1437,1214,4614, 373,3849,1142,2220, 667,4615, 442,2763,2563, # 2144 +3850,4028,1969,4282,3300,1840, 837, 170,1107, 934,1336,1883,5244,5245,2119,4283, # 2160 +2841, 743,1569,5246,4616,4284, 582,2389,1418,3484,5247,1803,5248, 357,1395,1729, # 2176 +3717,3301,2423,1564,2241,5249,3106,3851,1633,4617,1114,2086,4285,1532,5250, 482, # 2192 +2451,4618,5251,5252,1492, 833,1466,5253,2726,3599,1641,2842,5254,1526,1272,3718, # 2208 +4286,1686,1795, 416,2564,1903,1954,1804,5255,3852,2798,3853,1159,2321,5256,2881, # 2224 +4619,1610,1584,3056,2424,2764, 443,3302,1163,3161,5257,5258,4029,5259,4287,2506, # 2240 +3057,4620,4030,3162,2104,1647,3600,2011,1873,4288,5260,4289, 431,3485,5261, 250, # 2256 + 97, 81,4290,5262,1648,1851,1558, 160, 848,5263, 866, 740,1694,5264,2204,2843, # 2272 +3226,4291,4621,3719,1687, 950,2479, 426, 469,3227,3720,3721,4031,5265,5266,1188, # 2288 + 424,1996, 861,3601,4292,3854,2205,2694, 168,1235,3602,4293,5267,2087,1674,4622, # 2304 +3374,3303, 220,2565,1009,5268,3855, 670,3010, 332,1208, 717,5269,5270,3603,2452, # 2320 +4032,3375,5271, 513,5272,1209,2882,3376,3163,4623,1080,5273,5274,5275,5276,2534, # 2336 +3722,3604, 815,1587,4033,4034,5277,3605,3486,3856,1254,4624,1328,3058,1390,4035, # 2352 +1741,4036,3857,4037,5278, 236,3858,2453,3304,5279,5280,3723,3859,1273,3860,4625, # 2368 +5281, 308,5282,4626, 245,4627,1852,2480,1307,2583, 430, 715,2137,2454,5283, 270, # 2384 + 199,2883,4038,5284,3606,2727,1753, 761,1754, 725,1661,1841,4628,3487,3724,5285, # 2400 +5286, 587, 14,3305, 227,2608, 326, 480,2270, 943,2765,3607, 291, 650,1884,5287, # 2416 +1702,1226, 102,1547, 62,3488, 904,4629,3489,1164,4294,5288,5289,1224,1548,2766, # 2432 + 391, 498,1493,5290,1386,1419,5291,2056,1177,4630, 813, 880,1081,2368, 566,1145, # 2448 +4631,2291,1001,1035,2566,2609,2242, 394,1286,5292,5293,2069,5294, 86,1494,1730, # 2464 +4039, 491,1588, 745, 897,2963, 843,3377,4040,2767,2884,3306,1768, 998,2221,2070, # 2480 + 397,1827,1195,1970,3725,3011,3378, 284,5295,3861,2507,2138,2120,1904,5296,4041, # 2496 +2151,4042,4295,1036,3490,1905, 114,2567,4296, 209,1527,5297,5298,2964,2844,2635, # 2512 +2390,2728,3164, 812,2568,5299,3307,5300,1559, 737,1885,3726,1210, 885, 28,2695, # 2528 +3608,3862,5301,4297,1004,1780,4632,5302, 346,1982,2222,2696,4633,3863,1742, 797, # 2544 +1642,4043,1934,1072,1384,2152, 896,4044,3308,3727,3228,2885,3609,5303,2569,1959, # 2560 +4634,2455,1786,5304,5305,5306,4045,4298,1005,1308,3728,4299,2729,4635,4636,1528, # 2576 +2610, 161,1178,4300,1983, 987,4637,1101,4301, 631,4046,1157,3229,2425,1343,1241, # 2592 +1016,2243,2570, 372, 877,2344,2508,1160, 555,1935, 911,4047,5307, 466,1170, 169, # 2608 +1051,2921,2697,3729,2481,3012,1182,2012,2571,1251,2636,5308, 992,2345,3491,1540, # 2624 +2730,1201,2071,2406,1997,2482,5309,4638, 528,1923,2191,1503,1874,1570,2369,3379, # 2640 +3309,5310, 557,1073,5311,1828,3492,2088,2271,3165,3059,3107, 767,3108,2799,4639, # 2656 +1006,4302,4640,2346,1267,2179,3730,3230, 778,4048,3231,2731,1597,2667,5312,4641, # 2672 +5313,3493,5314,5315,5316,3310,2698,1433,3311, 131, 95,1504,4049, 723,4303,3166, # 2688 +1842,3610,2768,2192,4050,2028,2105,3731,5317,3013,4051,1218,5318,3380,3232,4052, # 2704 +4304,2584, 248,1634,3864, 912,5319,2845,3732,3060,3865, 654, 53,5320,3014,5321, # 2720 +1688,4642, 777,3494,1032,4053,1425,5322, 191, 820,2121,2846, 971,4643, 931,3233, # 2736 + 135, 664, 783,3866,1998, 772,2922,1936,4054,3867,4644,2923,3234, 282,2732, 640, # 2752 +1372,3495,1127, 922, 325,3381,5323,5324, 711,2045,5325,5326,4055,2223,2800,1937, # 2768 +4056,3382,2224,2255,3868,2305,5327,4645,3869,1258,3312,4057,3235,2139,2965,4058, # 2784 +4059,5328,2225, 258,3236,4646, 101,1227,5329,3313,1755,5330,1391,3314,5331,2924, # 2800 +2057, 893,5332,5333,5334,1402,4305,2347,5335,5336,3237,3611,5337,5338, 878,1325, # 2816 +1781,2801,4647, 259,1385,2585, 744,1183,2272,4648,5339,4060,2509,5340, 684,1024, # 2832 +4306,5341, 472,3612,3496,1165,3315,4061,4062, 322,2153, 881, 455,1695,1152,1340, # 2848 + 660, 554,2154,4649,1058,4650,4307, 830,1065,3383,4063,4651,1924,5342,1703,1919, # 2864 +5343, 932,2273, 122,5344,4652, 947, 677,5345,3870,2637, 297,1906,1925,2274,4653, # 2880 +2322,3316,5346,5347,4308,5348,4309, 84,4310, 112, 989,5349, 547,1059,4064, 701, # 2896 +3613,1019,5350,4311,5351,3497, 942, 639, 457,2306,2456, 993,2966, 407, 851, 494, # 2912 +4654,3384, 927,5352,1237,5353,2426,3385, 573,4312, 680, 921,2925,1279,1875, 285, # 2928 + 790,1448,1984, 719,2168,5354,5355,4655,4065,4066,1649,5356,1541, 563,5357,1077, # 2944 +5358,3386,3061,3498, 511,3015,4067,4068,3733,4069,1268,2572,3387,3238,4656,4657, # 2960 +5359, 535,1048,1276,1189,2926,2029,3167,1438,1373,2847,2967,1134,2013,5360,4313, # 2976 +1238,2586,3109,1259,5361, 700,5362,2968,3168,3734,4314,5363,4315,1146,1876,1907, # 2992 +4658,2611,4070, 781,2427, 132,1589, 203, 147, 273,2802,2407, 898,1787,2155,4071, # 3008 +4072,5364,3871,2803,5365,5366,4659,4660,5367,3239,5368,1635,3872, 965,5369,1805, # 3024 +2699,1516,3614,1121,1082,1329,3317,4073,1449,3873, 65,1128,2848,2927,2769,1590, # 3040 +3874,5370,5371, 12,2668, 45, 976,2587,3169,4661, 517,2535,1013,1037,3240,5372, # 3056 +3875,2849,5373,3876,5374,3499,5375,2612, 614,1999,2323,3877,3110,2733,2638,5376, # 3072 +2588,4316, 599,1269,5377,1811,3735,5378,2700,3111, 759,1060, 489,1806,3388,3318, # 3088 +1358,5379,5380,2391,1387,1215,2639,2256, 490,5381,5382,4317,1759,2392,2348,5383, # 3104 +4662,3878,1908,4074,2640,1807,3241,4663,3500,3319,2770,2349, 874,5384,5385,3501, # 3120 +3736,1859, 91,2928,3737,3062,3879,4664,5386,3170,4075,2669,5387,3502,1202,1403, # 3136 +3880,2969,2536,1517,2510,4665,3503,2511,5388,4666,5389,2701,1886,1495,1731,4076, # 3152 +2370,4667,5390,2030,5391,5392,4077,2702,1216, 237,2589,4318,2324,4078,3881,4668, # 3168 +4669,2703,3615,3504, 445,4670,5393,5394,5395,5396,2771, 61,4079,3738,1823,4080, # 3184 +5397, 687,2046, 935, 925, 405,2670, 703,1096,1860,2734,4671,4081,1877,1367,2704, # 3200 +3389, 918,2106,1782,2483, 334,3320,1611,1093,4672, 564,3171,3505,3739,3390, 945, # 3216 +2641,2058,4673,5398,1926, 872,4319,5399,3506,2705,3112, 349,4320,3740,4082,4674, # 3232 +3882,4321,3741,2156,4083,4675,4676,4322,4677,2408,2047, 782,4084, 400, 251,4323, # 3248 +1624,5400,5401, 277,3742, 299,1265, 476,1191,3883,2122,4324,4325,1109, 205,5402, # 3264 +2590,1000,2157,3616,1861,5403,5404,5405,4678,5406,4679,2573, 107,2484,2158,4085, # 3280 +3507,3172,5407,1533, 541,1301, 158, 753,4326,2886,3617,5408,1696, 370,1088,4327, # 3296 +4680,3618, 579, 327, 440, 162,2244, 269,1938,1374,3508, 968,3063, 56,1396,3113, # 3312 +2107,3321,3391,5409,1927,2159,4681,3016,5410,3619,5411,5412,3743,4682,2485,5413, # 3328 +2804,5414,1650,4683,5415,2613,5416,5417,4086,2671,3392,1149,3393,4087,3884,4088, # 3344 +5418,1076, 49,5419, 951,3242,3322,3323, 450,2850, 920,5420,1812,2805,2371,4328, # 3360 +1909,1138,2372,3885,3509,5421,3243,4684,1910,1147,1518,2428,4685,3886,5422,4686, # 3376 +2393,2614, 260,1796,3244,5423,5424,3887,3324, 708,5425,3620,1704,5426,3621,1351, # 3392 +1618,3394,3017,1887, 944,4329,3395,4330,3064,3396,4331,5427,3744, 422, 413,1714, # 3408 +3325, 500,2059,2350,4332,2486,5428,1344,1911, 954,5429,1668,5430,5431,4089,2409, # 3424 +4333,3622,3888,4334,5432,2307,1318,2512,3114, 133,3115,2887,4687, 629, 31,2851, # 3440 +2706,3889,4688, 850, 949,4689,4090,2970,1732,2089,4335,1496,1853,5433,4091, 620, # 3456 +3245, 981,1242,3745,3397,1619,3746,1643,3326,2140,2457,1971,1719,3510,2169,5434, # 3472 +3246,5435,5436,3398,1829,5437,1277,4690,1565,2048,5438,1636,3623,3116,5439, 869, # 3488 +2852, 655,3890,3891,3117,4092,3018,3892,1310,3624,4691,5440,5441,5442,1733, 558, # 3504 +4692,3747, 335,1549,3065,1756,4336,3748,1946,3511,1830,1291,1192, 470,2735,2108, # 3520 +2806, 913,1054,4093,5443,1027,5444,3066,4094,4693, 982,2672,3399,3173,3512,3247, # 3536 +3248,1947,2807,5445, 571,4694,5446,1831,5447,3625,2591,1523,2429,5448,2090, 984, # 3552 +4695,3749,1960,5449,3750, 852, 923,2808,3513,3751, 969,1519, 999,2049,2325,1705, # 3568 +5450,3118, 615,1662, 151, 597,4095,2410,2326,1049, 275,4696,3752,4337, 568,3753, # 3584 +3626,2487,4338,3754,5451,2430,2275, 409,3249,5452,1566,2888,3514,1002, 769,2853, # 3600 + 194,2091,3174,3755,2226,3327,4339, 628,1505,5453,5454,1763,2180,3019,4096, 521, # 3616 +1161,2592,1788,2206,2411,4697,4097,1625,4340,4341, 412, 42,3119, 464,5455,2642, # 3632 +4698,3400,1760,1571,2889,3515,2537,1219,2207,3893,2643,2141,2373,4699,4700,3328, # 3648 +1651,3401,3627,5456,5457,3628,2488,3516,5458,3756,5459,5460,2276,2092, 460,5461, # 3664 +4701,5462,3020, 962, 588,3629, 289,3250,2644,1116, 52,5463,3067,1797,5464,5465, # 3680 +5466,1467,5467,1598,1143,3757,4342,1985,1734,1067,4702,1280,3402, 465,4703,1572, # 3696 + 510,5468,1928,2245,1813,1644,3630,5469,4704,3758,5470,5471,2673,1573,1534,5472, # 3712 +5473, 536,1808,1761,3517,3894,3175,2645,5474,5475,5476,4705,3518,2929,1912,2809, # 3728 +5477,3329,1122, 377,3251,5478, 360,5479,5480,4343,1529, 551,5481,2060,3759,1769, # 3744 +2431,5482,2930,4344,3330,3120,2327,2109,2031,4706,1404, 136,1468,1479, 672,1171, # 3760 +3252,2308, 271,3176,5483,2772,5484,2050, 678,2736, 865,1948,4707,5485,2014,4098, # 3776 +2971,5486,2737,2227,1397,3068,3760,4708,4709,1735,2931,3403,3631,5487,3895, 509, # 3792 +2854,2458,2890,3896,5488,5489,3177,3178,4710,4345,2538,4711,2309,1166,1010, 552, # 3808 + 681,1888,5490,5491,2972,2973,4099,1287,1596,1862,3179, 358, 453, 736, 175, 478, # 3824 +1117, 905,1167,1097,5492,1854,1530,5493,1706,5494,2181,3519,2292,3761,3520,3632, # 3840 +4346,2093,4347,5495,3404,1193,2489,4348,1458,2193,2208,1863,1889,1421,3331,2932, # 3856 +3069,2182,3521, 595,2123,5496,4100,5497,5498,4349,1707,2646, 223,3762,1359, 751, # 3872 +3121, 183,3522,5499,2810,3021, 419,2374, 633, 704,3897,2394, 241,5500,5501,5502, # 3888 + 838,3022,3763,2277,2773,2459,3898,1939,2051,4101,1309,3122,2246,1181,5503,1136, # 3904 +2209,3899,2375,1446,4350,2310,4712,5504,5505,4351,1055,2615, 484,3764,5506,4102, # 3920 + 625,4352,2278,3405,1499,4353,4103,5507,4104,4354,3253,2279,2280,3523,5508,5509, # 3936 +2774, 808,2616,3765,3406,4105,4355,3123,2539, 526,3407,3900,4356, 955,5510,1620, # 3952 +4357,2647,2432,5511,1429,3766,1669,1832, 994, 928,5512,3633,1260,5513,5514,5515, # 3968 +1949,2293, 741,2933,1626,4358,2738,2460, 867,1184, 362,3408,1392,5516,5517,4106, # 3984 +4359,1770,1736,3254,2934,4713,4714,1929,2707,1459,1158,5518,3070,3409,2891,1292, # 4000 +1930,2513,2855,3767,1986,1187,2072,2015,2617,4360,5519,2574,2514,2170,3768,2490, # 4016 +3332,5520,3769,4715,5521,5522, 666,1003,3023,1022,3634,4361,5523,4716,1814,2257, # 4032 + 574,3901,1603, 295,1535, 705,3902,4362, 283, 858, 417,5524,5525,3255,4717,4718, # 4048 +3071,1220,1890,1046,2281,2461,4107,1393,1599, 689,2575, 388,4363,5526,2491, 802, # 4064 +5527,2811,3903,2061,1405,2258,5528,4719,3904,2110,1052,1345,3256,1585,5529, 809, # 4080 +5530,5531,5532, 575,2739,3524, 956,1552,1469,1144,2328,5533,2329,1560,2462,3635, # 4096 +3257,4108, 616,2210,4364,3180,2183,2294,5534,1833,5535,3525,4720,5536,1319,3770, # 4112 +3771,1211,3636,1023,3258,1293,2812,5537,5538,5539,3905, 607,2311,3906, 762,2892, # 4128 +1439,4365,1360,4721,1485,3072,5540,4722,1038,4366,1450,2062,2648,4367,1379,4723, # 4144 +2593,5541,5542,4368,1352,1414,2330,2935,1172,5543,5544,3907,3908,4724,1798,1451, # 4160 +5545,5546,5547,5548,2936,4109,4110,2492,2351, 411,4111,4112,3637,3333,3124,4725, # 4176 +1561,2674,1452,4113,1375,5549,5550, 47,2974, 316,5551,1406,1591,2937,3181,5552, # 4192 +1025,2142,3125,3182, 354,2740, 884,2228,4369,2412, 508,3772, 726,3638, 996,2433, # 4208 +3639, 729,5553, 392,2194,1453,4114,4726,3773,5554,5555,2463,3640,2618,1675,2813, # 4224 + 919,2352,2975,2353,1270,4727,4115, 73,5556,5557, 647,5558,3259,2856,2259,1550, # 4240 +1346,3024,5559,1332, 883,3526,5560,5561,5562,5563,3334,2775,5564,1212, 831,1347, # 4256 +4370,4728,2331,3909,1864,3073, 720,3910,4729,4730,3911,5565,4371,5566,5567,4731, # 4272 +5568,5569,1799,4732,3774,2619,4733,3641,1645,2376,4734,5570,2938, 669,2211,2675, # 4288 +2434,5571,2893,5572,5573,1028,3260,5574,4372,2413,5575,2260,1353,5576,5577,4735, # 4304 +3183, 518,5578,4116,5579,4373,1961,5580,2143,4374,5581,5582,3025,2354,2355,3912, # 4320 + 516,1834,1454,4117,2708,4375,4736,2229,2620,1972,1129,3642,5583,2776,5584,2976, # 4336 +1422, 577,1470,3026,1524,3410,5585,5586, 432,4376,3074,3527,5587,2594,1455,2515, # 4352 +2230,1973,1175,5588,1020,2741,4118,3528,4737,5589,2742,5590,1743,1361,3075,3529, # 4368 +2649,4119,4377,4738,2295, 895, 924,4378,2171, 331,2247,3076, 166,1627,3077,1098, # 4384 +5591,1232,2894,2231,3411,4739, 657, 403,1196,2377, 542,3775,3412,1600,4379,3530, # 4400 +5592,4740,2777,3261, 576, 530,1362,4741,4742,2540,2676,3776,4120,5593, 842,3913, # 4416 +5594,2814,2032,1014,4121, 213,2709,3413, 665, 621,4380,5595,3777,2939,2435,5596, # 4432 +2436,3335,3643,3414,4743,4381,2541,4382,4744,3644,1682,4383,3531,1380,5597, 724, # 4448 +2282, 600,1670,5598,1337,1233,4745,3126,2248,5599,1621,4746,5600, 651,4384,5601, # 4464 +1612,4385,2621,5602,2857,5603,2743,2312,3078,5604, 716,2464,3079, 174,1255,2710, # 4480 +4122,3645, 548,1320,1398, 728,4123,1574,5605,1891,1197,3080,4124,5606,3081,3082, # 4496 +3778,3646,3779, 747,5607, 635,4386,4747,5608,5609,5610,4387,5611,5612,4748,5613, # 4512 +3415,4749,2437, 451,5614,3780,2542,2073,4388,2744,4389,4125,5615,1764,4750,5616, # 4528 +4390, 350,4751,2283,2395,2493,5617,4391,4126,2249,1434,4127, 488,4752, 458,4392, # 4544 +4128,3781, 771,1330,2396,3914,2576,3184,2160,2414,1553,2677,3185,4393,5618,2494, # 4560 +2895,2622,1720,2711,4394,3416,4753,5619,2543,4395,5620,3262,4396,2778,5621,2016, # 4576 +2745,5622,1155,1017,3782,3915,5623,3336,2313, 201,1865,4397,1430,5624,4129,5625, # 4592 +5626,5627,5628,5629,4398,1604,5630, 414,1866, 371,2595,4754,4755,3532,2017,3127, # 4608 +4756,1708, 960,4399, 887, 389,2172,1536,1663,1721,5631,2232,4130,2356,2940,1580, # 4624 +5632,5633,1744,4757,2544,4758,4759,5634,4760,5635,2074,5636,4761,3647,3417,2896, # 4640 +4400,5637,4401,2650,3418,2815, 673,2712,2465, 709,3533,4131,3648,4402,5638,1148, # 4656 + 502, 634,5639,5640,1204,4762,3649,1575,4763,2623,3783,5641,3784,3128, 948,3263, # 4672 + 121,1745,3916,1110,5642,4403,3083,2516,3027,4132,3785,1151,1771,3917,1488,4133, # 4688 +1987,5643,2438,3534,5644,5645,2094,5646,4404,3918,1213,1407,2816, 531,2746,2545, # 4704 +3264,1011,1537,4764,2779,4405,3129,1061,5647,3786,3787,1867,2897,5648,2018, 120, # 4720 +4406,4407,2063,3650,3265,2314,3919,2678,3419,1955,4765,4134,5649,3535,1047,2713, # 4736 +1266,5650,1368,4766,2858, 649,3420,3920,2546,2747,1102,2859,2679,5651,5652,2000, # 4752 +5653,1111,3651,2977,5654,2495,3921,3652,2817,1855,3421,3788,5655,5656,3422,2415, # 4768 +2898,3337,3266,3653,5657,2577,5658,3654,2818,4135,1460, 856,5659,3655,5660,2899, # 4784 +2978,5661,2900,3922,5662,4408, 632,2517, 875,3923,1697,3924,2296,5663,5664,4767, # 4800 +3028,1239, 580,4768,4409,5665, 914, 936,2075,1190,4136,1039,2124,5666,5667,5668, # 4816 +5669,3423,1473,5670,1354,4410,3925,4769,2173,3084,4137, 915,3338,4411,4412,3339, # 4832 +1605,1835,5671,2748, 398,3656,4413,3926,4138, 328,1913,2860,4139,3927,1331,4414, # 4848 +3029, 937,4415,5672,3657,4140,4141,3424,2161,4770,3425, 524, 742, 538,3085,1012, # 4864 +5673,5674,3928,2466,5675, 658,1103, 225,3929,5676,5677,4771,5678,4772,5679,3267, # 4880 +1243,5680,4142, 963,2250,4773,5681,2714,3658,3186,5682,5683,2596,2332,5684,4774, # 4896 +5685,5686,5687,3536, 957,3426,2547,2033,1931,2941,2467, 870,2019,3659,1746,2780, # 4912 +2781,2439,2468,5688,3930,5689,3789,3130,3790,3537,3427,3791,5690,1179,3086,5691, # 4928 +3187,2378,4416,3792,2548,3188,3131,2749,4143,5692,3428,1556,2549,2297, 977,2901, # 4944 +2034,4144,1205,3429,5693,1765,3430,3189,2125,1271, 714,1689,4775,3538,5694,2333, # 4960 +3931, 533,4417,3660,2184, 617,5695,2469,3340,3539,2315,5696,5697,3190,5698,5699, # 4976 +3932,1988, 618, 427,2651,3540,3431,5700,5701,1244,1690,5702,2819,4418,4776,5703, # 4992 +3541,4777,5704,2284,1576, 473,3661,4419,3432, 972,5705,3662,5706,3087,5707,5708, # 5008 +4778,4779,5709,3793,4145,4146,5710, 153,4780, 356,5711,1892,2902,4420,2144, 408, # 5024 + 803,2357,5712,3933,5713,4421,1646,2578,2518,4781,4782,3934,5714,3935,4422,5715, # 5040 +2416,3433, 752,5716,5717,1962,3341,2979,5718, 746,3030,2470,4783,4423,3794, 698, # 5056 +4784,1893,4424,3663,2550,4785,3664,3936,5719,3191,3434,5720,1824,1302,4147,2715, # 5072 +3937,1974,4425,5721,4426,3192, 823,1303,1288,1236,2861,3542,4148,3435, 774,3938, # 5088 +5722,1581,4786,1304,2862,3939,4787,5723,2440,2162,1083,3268,4427,4149,4428, 344, # 5104 +1173, 288,2316, 454,1683,5724,5725,1461,4788,4150,2597,5726,5727,4789, 985, 894, # 5120 +5728,3436,3193,5729,1914,2942,3795,1989,5730,2111,1975,5731,4151,5732,2579,1194, # 5136 + 425,5733,4790,3194,1245,3796,4429,5734,5735,2863,5736, 636,4791,1856,3940, 760, # 5152 +1800,5737,4430,2212,1508,4792,4152,1894,1684,2298,5738,5739,4793,4431,4432,2213, # 5168 + 479,5740,5741, 832,5742,4153,2496,5743,2980,2497,3797, 990,3132, 627,1815,2652, # 5184 +4433,1582,4434,2126,2112,3543,4794,5744, 799,4435,3195,5745,4795,2113,1737,3031, # 5200 +1018, 543, 754,4436,3342,1676,4796,4797,4154,4798,1489,5746,3544,5747,2624,2903, # 5216 +4155,5748,5749,2981,5750,5751,5752,5753,3196,4799,4800,2185,1722,5754,3269,3270, # 5232 +1843,3665,1715, 481, 365,1976,1857,5755,5756,1963,2498,4801,5757,2127,3666,3271, # 5248 + 433,1895,2064,2076,5758, 602,2750,5759,5760,5761,5762,5763,3032,1628,3437,5764, # 5264 +3197,4802,4156,2904,4803,2519,5765,2551,2782,5766,5767,5768,3343,4804,2905,5769, # 5280 +4805,5770,2864,4806,4807,1221,2982,4157,2520,5771,5772,5773,1868,1990,5774,5775, # 5296 +5776,1896,5777,5778,4808,1897,4158, 318,5779,2095,4159,4437,5780,5781, 485,5782, # 5312 + 938,3941, 553,2680, 116,5783,3942,3667,5784,3545,2681,2783,3438,3344,2820,5785, # 5328 +3668,2943,4160,1747,2944,2983,5786,5787, 207,5788,4809,5789,4810,2521,5790,3033, # 5344 + 890,3669,3943,5791,1878,3798,3439,5792,2186,2358,3440,1652,5793,5794,5795, 941, # 5360 +2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, # 5376 +) + diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/big5prober.py b/venv/Lib/site-packages/pip/_vendor/chardet/big5prober.py new file mode 100644 index 0000000..98f9970 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/big5prober.py @@ -0,0 +1,47 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import Big5DistributionAnalysis +from .mbcssm import BIG5_SM_MODEL + + +class Big5Prober(MultiByteCharSetProber): + def __init__(self): + super(Big5Prober, self).__init__() + self.coding_sm = CodingStateMachine(BIG5_SM_MODEL) + self.distribution_analyzer = Big5DistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "Big5" + + @property + def language(self): + return "Chinese" diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/chardistribution.py b/venv/Lib/site-packages/pip/_vendor/chardet/chardistribution.py new file mode 100644 index 0000000..c0395f4 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/chardistribution.py @@ -0,0 +1,233 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .euctwfreq import (EUCTW_CHAR_TO_FREQ_ORDER, EUCTW_TABLE_SIZE, + EUCTW_TYPICAL_DISTRIBUTION_RATIO) +from .euckrfreq import (EUCKR_CHAR_TO_FREQ_ORDER, EUCKR_TABLE_SIZE, + EUCKR_TYPICAL_DISTRIBUTION_RATIO) +from .gb2312freq import (GB2312_CHAR_TO_FREQ_ORDER, GB2312_TABLE_SIZE, + GB2312_TYPICAL_DISTRIBUTION_RATIO) +from .big5freq import (BIG5_CHAR_TO_FREQ_ORDER, BIG5_TABLE_SIZE, + BIG5_TYPICAL_DISTRIBUTION_RATIO) +from .jisfreq import (JIS_CHAR_TO_FREQ_ORDER, JIS_TABLE_SIZE, + JIS_TYPICAL_DISTRIBUTION_RATIO) + + +class CharDistributionAnalysis(object): + ENOUGH_DATA_THRESHOLD = 1024 + SURE_YES = 0.99 + SURE_NO = 0.01 + MINIMUM_DATA_THRESHOLD = 3 + + def __init__(self): + # Mapping table to get frequency order from char order (get from + # GetOrder()) + self._char_to_freq_order = None + self._table_size = None # Size of above table + # This is a constant value which varies from language to language, + # used in calculating confidence. See + # http://www.mozilla.org/projects/intl/UniversalCharsetDetection.html + # for further detail. + self.typical_distribution_ratio = None + self._done = None + self._total_chars = None + self._freq_chars = None + self.reset() + + def reset(self): + """reset analyser, clear any state""" + # If this flag is set to True, detection is done and conclusion has + # been made + self._done = False + self._total_chars = 0 # Total characters encountered + # The number of characters whose frequency order is less than 512 + self._freq_chars = 0 + + def feed(self, char, char_len): + """feed a character with known length""" + if char_len == 2: + # we only care about 2-bytes character in our distribution analysis + order = self.get_order(char) + else: + order = -1 + if order >= 0: + self._total_chars += 1 + # order is valid + if order < self._table_size: + if 512 > self._char_to_freq_order[order]: + self._freq_chars += 1 + + def get_confidence(self): + """return confidence based on existing data""" + # if we didn't receive any character in our consideration range, + # return negative answer + if self._total_chars <= 0 or self._freq_chars <= self.MINIMUM_DATA_THRESHOLD: + return self.SURE_NO + + if self._total_chars != self._freq_chars: + r = (self._freq_chars / ((self._total_chars - self._freq_chars) + * self.typical_distribution_ratio)) + if r < self.SURE_YES: + return r + + # normalize confidence (we don't want to be 100% sure) + return self.SURE_YES + + def got_enough_data(self): + # It is not necessary to receive all data to draw conclusion. + # For charset detection, certain amount of data is enough + return self._total_chars > self.ENOUGH_DATA_THRESHOLD + + def get_order(self, byte_str): + # We do not handle characters based on the original encoding string, + # but convert this encoding string to a number, here called order. + # This allows multiple encodings of a language to share one frequency + # table. + return -1 + + +class EUCTWDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(EUCTWDistributionAnalysis, self).__init__() + self._char_to_freq_order = EUCTW_CHAR_TO_FREQ_ORDER + self._table_size = EUCTW_TABLE_SIZE + self.typical_distribution_ratio = EUCTW_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for euc-TW encoding, we are interested + # first byte range: 0xc4 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char = byte_str[0] + if first_char >= 0xC4: + return 94 * (first_char - 0xC4) + byte_str[1] - 0xA1 + else: + return -1 + + +class EUCKRDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(EUCKRDistributionAnalysis, self).__init__() + self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER + self._table_size = EUCKR_TABLE_SIZE + self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for euc-KR encoding, we are interested + # first byte range: 0xb0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char = byte_str[0] + if first_char >= 0xB0: + return 94 * (first_char - 0xB0) + byte_str[1] - 0xA1 + else: + return -1 + + +class GB2312DistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(GB2312DistributionAnalysis, self).__init__() + self._char_to_freq_order = GB2312_CHAR_TO_FREQ_ORDER + self._table_size = GB2312_TABLE_SIZE + self.typical_distribution_ratio = GB2312_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for GB2312 encoding, we are interested + # first byte range: 0xb0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char, second_char = byte_str[0], byte_str[1] + if (first_char >= 0xB0) and (second_char >= 0xA1): + return 94 * (first_char - 0xB0) + second_char - 0xA1 + else: + return -1 + + +class Big5DistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(Big5DistributionAnalysis, self).__init__() + self._char_to_freq_order = BIG5_CHAR_TO_FREQ_ORDER + self._table_size = BIG5_TABLE_SIZE + self.typical_distribution_ratio = BIG5_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for big5 encoding, we are interested + # first byte range: 0xa4 -- 0xfe + # second byte range: 0x40 -- 0x7e , 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char, second_char = byte_str[0], byte_str[1] + if first_char >= 0xA4: + if second_char >= 0xA1: + return 157 * (first_char - 0xA4) + second_char - 0xA1 + 63 + else: + return 157 * (first_char - 0xA4) + second_char - 0x40 + else: + return -1 + + +class SJISDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(SJISDistributionAnalysis, self).__init__() + self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER + self._table_size = JIS_TABLE_SIZE + self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for sjis encoding, we are interested + # first byte range: 0x81 -- 0x9f , 0xe0 -- 0xfe + # second byte range: 0x40 -- 0x7e, 0x81 -- oxfe + # no validation needed here. State machine has done that + first_char, second_char = byte_str[0], byte_str[1] + if (first_char >= 0x81) and (first_char <= 0x9F): + order = 188 * (first_char - 0x81) + elif (first_char >= 0xE0) and (first_char <= 0xEF): + order = 188 * (first_char - 0xE0 + 31) + else: + return -1 + order = order + second_char - 0x40 + if second_char > 0x7F: + order = -1 + return order + + +class EUCJPDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(EUCJPDistributionAnalysis, self).__init__() + self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER + self._table_size = JIS_TABLE_SIZE + self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for euc-JP encoding, we are interested + # first byte range: 0xa0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + char = byte_str[0] + if char >= 0xA0: + return 94 * (char - 0xA1) + byte_str[1] - 0xa1 + else: + return -1 diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py new file mode 100644 index 0000000..8b3738e --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py @@ -0,0 +1,106 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .enums import ProbingState +from .charsetprober import CharSetProber + + +class CharSetGroupProber(CharSetProber): + def __init__(self, lang_filter=None): + super(CharSetGroupProber, self).__init__(lang_filter=lang_filter) + self._active_num = 0 + self.probers = [] + self._best_guess_prober = None + + def reset(self): + super(CharSetGroupProber, self).reset() + self._active_num = 0 + for prober in self.probers: + if prober: + prober.reset() + prober.active = True + self._active_num += 1 + self._best_guess_prober = None + + @property + def charset_name(self): + if not self._best_guess_prober: + self.get_confidence() + if not self._best_guess_prober: + return None + return self._best_guess_prober.charset_name + + @property + def language(self): + if not self._best_guess_prober: + self.get_confidence() + if not self._best_guess_prober: + return None + return self._best_guess_prober.language + + def feed(self, byte_str): + for prober in self.probers: + if not prober: + continue + if not prober.active: + continue + state = prober.feed(byte_str) + if not state: + continue + if state == ProbingState.FOUND_IT: + self._best_guess_prober = prober + return self.state + elif state == ProbingState.NOT_ME: + prober.active = False + self._active_num -= 1 + if self._active_num <= 0: + self._state = ProbingState.NOT_ME + return self.state + return self.state + + def get_confidence(self): + state = self.state + if state == ProbingState.FOUND_IT: + return 0.99 + elif state == ProbingState.NOT_ME: + return 0.01 + best_conf = 0.0 + self._best_guess_prober = None + for prober in self.probers: + if not prober: + continue + if not prober.active: + self.logger.debug('%s not active', prober.charset_name) + continue + conf = prober.get_confidence() + self.logger.debug('%s %s confidence = %s', prober.charset_name, prober.language, conf) + if best_conf < conf: + best_conf = conf + self._best_guess_prober = prober + if not self._best_guess_prober: + return 0.0 + return best_conf diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/charsetprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/charsetprober.py new file mode 100644 index 0000000..eac4e59 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/charsetprober.py @@ -0,0 +1,145 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import logging +import re + +from .enums import ProbingState + + +class CharSetProber(object): + + SHORTCUT_THRESHOLD = 0.95 + + def __init__(self, lang_filter=None): + self._state = None + self.lang_filter = lang_filter + self.logger = logging.getLogger(__name__) + + def reset(self): + self._state = ProbingState.DETECTING + + @property + def charset_name(self): + return None + + def feed(self, buf): + pass + + @property + def state(self): + return self._state + + def get_confidence(self): + return 0.0 + + @staticmethod + def filter_high_byte_only(buf): + buf = re.sub(b'([\x00-\x7F])+', b' ', buf) + return buf + + @staticmethod + def filter_international_words(buf): + """ + We define three types of bytes: + alphabet: english alphabets [a-zA-Z] + international: international characters [\x80-\xFF] + marker: everything else [^a-zA-Z\x80-\xFF] + + The input buffer can be thought to contain a series of words delimited + by markers. This function works to filter all words that contain at + least one international character. All contiguous sequences of markers + are replaced by a single space ascii character. + + This filter applies to all scripts which do not use English characters. + """ + filtered = bytearray() + + # This regex expression filters out only words that have at-least one + # international character. The word may include one marker character at + # the end. + words = re.findall(b'[a-zA-Z]*[\x80-\xFF]+[a-zA-Z]*[^a-zA-Z\x80-\xFF]?', + buf) + + for word in words: + filtered.extend(word[:-1]) + + # If the last character in the word is a marker, replace it with a + # space as markers shouldn't affect our analysis (they are used + # similarly across all languages and may thus have similar + # frequencies). + last_char = word[-1:] + if not last_char.isalpha() and last_char < b'\x80': + last_char = b' ' + filtered.extend(last_char) + + return filtered + + @staticmethod + def filter_with_english_letters(buf): + """ + Returns a copy of ``buf`` that retains only the sequences of English + alphabet and high byte characters that are not between <> characters. + Also retains English alphabet and high byte characters immediately + before occurrences of >. + + This filter can be applied to all scripts which contain both English + characters and extended ASCII characters, but is currently only used by + ``Latin1Prober``. + """ + filtered = bytearray() + in_tag = False + prev = 0 + + for curr in range(len(buf)): + # Slice here to get bytes instead of an int with Python 3 + buf_char = buf[curr:curr + 1] + # Check if we're coming out of or entering an HTML tag + if buf_char == b'>': + in_tag = False + elif buf_char == b'<': + in_tag = True + + # If current character is not extended-ASCII and not alphabetic... + if buf_char < b'\x80' and not buf_char.isalpha(): + # ...and we're not in a tag + if curr > prev and not in_tag: + # Keep everything after last non-extended-ASCII, + # non-alphabetic character + filtered.extend(buf[prev:curr]) + # Output a space to delimit stretch we kept + filtered.extend(b' ') + prev = curr + 1 + + # If we're not in a tag... + if not in_tag: + # Keep everything after last non-extended-ASCII, non-alphabetic + # character + filtered.extend(buf[prev:]) + + return filtered diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/cli/__init__.py b/venv/Lib/site-packages/pip/_vendor/chardet/cli/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/cli/__init__.py @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py b/venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py new file mode 100644 index 0000000..c61136b --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +""" +Script which takes one or more file paths and reports on their detected +encodings + +Example:: + + % chardetect somefile someotherfile + somefile: windows-1252 with confidence 0.5 + someotherfile: ascii with confidence 1.0 + +If no paths are provided, it takes its input from stdin. + +""" + +from __future__ import absolute_import, print_function, unicode_literals + +import argparse +import sys + +from pip._vendor.chardet import __version__ +from pip._vendor.chardet.compat import PY2 +from pip._vendor.chardet.universaldetector import UniversalDetector + + +def description_of(lines, name='stdin'): + """ + Return a string describing the probable encoding of a file or + list of strings. + + :param lines: The lines to get the encoding of. + :type lines: Iterable of bytes + :param name: Name of file or collection of lines + :type name: str + """ + u = UniversalDetector() + for line in lines: + line = bytearray(line) + u.feed(line) + # shortcut out of the loop to save reading further - particularly useful if we read a BOM. + if u.done: + break + u.close() + result = u.result + if PY2: + name = name.decode(sys.getfilesystemencoding(), 'ignore') + if result['encoding']: + return '{0}: {1} with confidence {2}'.format(name, result['encoding'], + result['confidence']) + else: + return '{0}: no result'.format(name) + + +def main(argv=None): + """ + Handles command line arguments and gets things started. + + :param argv: List of arguments, as if specified on the command-line. + If None, ``sys.argv[1:]`` is used instead. + :type argv: list of str + """ + # Get command line arguments + parser = argparse.ArgumentParser( + description="Takes one or more file paths and reports their detected \ + encodings") + parser.add_argument('input', + help='File whose encoding we would like to determine. \ + (default: stdin)', + type=argparse.FileType('rb'), nargs='*', + default=[sys.stdin if PY2 else sys.stdin.buffer]) + parser.add_argument('--version', action='version', + version='%(prog)s {0}'.format(__version__)) + args = parser.parse_args(argv) + + for f in args.input: + if f.isatty(): + print("You are running chardetect interactively. Press " + + "CTRL-D twice at the start of a blank line to signal the " + + "end of your input. If you want help, run chardetect " + + "--help\n", file=sys.stderr) + print(description_of(f, f.name)) + + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py b/venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py new file mode 100644 index 0000000..68fba44 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py @@ -0,0 +1,88 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import logging + +from .enums import MachineState + + +class CodingStateMachine(object): + """ + A state machine to verify a byte sequence for a particular encoding. For + each byte the detector receives, it will feed that byte to every active + state machine available, one byte at a time. The state machine changes its + state based on its previous state and the byte it receives. There are 3 + states in a state machine that are of interest to an auto-detector: + + START state: This is the state to start with, or a legal byte sequence + (i.e. a valid code point) for character has been identified. + + ME state: This indicates that the state machine identified a byte sequence + that is specific to the charset it is designed for and that + there is no other possible encoding which can contain this byte + sequence. This will to lead to an immediate positive answer for + the detector. + + ERROR state: This indicates the state machine identified an illegal byte + sequence for that encoding. This will lead to an immediate + negative answer for this encoding. Detector will exclude this + encoding from consideration from here on. + """ + def __init__(self, sm): + self._model = sm + self._curr_byte_pos = 0 + self._curr_char_len = 0 + self._curr_state = None + self.logger = logging.getLogger(__name__) + self.reset() + + def reset(self): + self._curr_state = MachineState.START + + def next_state(self, c): + # for each byte we get its class + # if it is first byte, we also get byte length + byte_class = self._model['class_table'][c] + if self._curr_state == MachineState.START: + self._curr_byte_pos = 0 + self._curr_char_len = self._model['char_len_table'][byte_class] + # from byte's class and state_table, we get its next state + curr_state = (self._curr_state * self._model['class_factor'] + + byte_class) + self._curr_state = self._model['state_table'][curr_state] + self._curr_byte_pos += 1 + return self._curr_state + + def get_current_charlen(self): + return self._curr_char_len + + def get_coding_state_machine(self): + return self._model['name'] + + @property + def language(self): + return self._model['language'] diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/compat.py b/venv/Lib/site-packages/pip/_vendor/chardet/compat.py new file mode 100644 index 0000000..ddd7468 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/compat.py @@ -0,0 +1,34 @@ +######################## BEGIN LICENSE BLOCK ######################## +# Contributor(s): +# Dan Blanchard +# Ian Cordasco +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import sys + + +if sys.version_info < (3, 0): + PY2 = True + PY3 = False + base_str = (str, unicode) + text_type = unicode +else: + PY2 = False + PY3 = True + base_str = (bytes, str) + text_type = str diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/cp949prober.py b/venv/Lib/site-packages/pip/_vendor/chardet/cp949prober.py new file mode 100644 index 0000000..efd793a --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/cp949prober.py @@ -0,0 +1,49 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .chardistribution import EUCKRDistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber +from .mbcssm import CP949_SM_MODEL + + +class CP949Prober(MultiByteCharSetProber): + def __init__(self): + super(CP949Prober, self).__init__() + self.coding_sm = CodingStateMachine(CP949_SM_MODEL) + # NOTE: CP949 is a superset of EUC-KR, so the distribution should be + # not different. + self.distribution_analyzer = EUCKRDistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "CP949" + + @property + def language(self): + return "Korean" diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/enums.py b/venv/Lib/site-packages/pip/_vendor/chardet/enums.py new file mode 100644 index 0000000..0451207 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/enums.py @@ -0,0 +1,76 @@ +""" +All of the Enums that are used throughout the chardet package. + +:author: Dan Blanchard (dan.blanchard@gmail.com) +""" + + +class InputState(object): + """ + This enum represents the different states a universal detector can be in. + """ + PURE_ASCII = 0 + ESC_ASCII = 1 + HIGH_BYTE = 2 + + +class LanguageFilter(object): + """ + This enum represents the different language filters we can apply to a + ``UniversalDetector``. + """ + CHINESE_SIMPLIFIED = 0x01 + CHINESE_TRADITIONAL = 0x02 + JAPANESE = 0x04 + KOREAN = 0x08 + NON_CJK = 0x10 + ALL = 0x1F + CHINESE = CHINESE_SIMPLIFIED | CHINESE_TRADITIONAL + CJK = CHINESE | JAPANESE | KOREAN + + +class ProbingState(object): + """ + This enum represents the different states a prober can be in. + """ + DETECTING = 0 + FOUND_IT = 1 + NOT_ME = 2 + + +class MachineState(object): + """ + This enum represents the different states a state machine can be in. + """ + START = 0 + ERROR = 1 + ITS_ME = 2 + + +class SequenceLikelihood(object): + """ + This enum represents the likelihood of a character following the previous one. + """ + NEGATIVE = 0 + UNLIKELY = 1 + LIKELY = 2 + POSITIVE = 3 + + @classmethod + def get_num_categories(cls): + """:returns: The number of likelihood categories in the enum.""" + return 4 + + +class CharacterCategory(object): + """ + This enum represents the different categories language models for + ``SingleByteCharsetProber`` put characters into. + + Anything less than CONTROL is considered a letter. + """ + UNDEFINED = 255 + LINE_BREAK = 254 + SYMBOL = 253 + DIGIT = 252 + CONTROL = 251 diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/escprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/escprober.py new file mode 100644 index 0000000..c70493f --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/escprober.py @@ -0,0 +1,101 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .codingstatemachine import CodingStateMachine +from .enums import LanguageFilter, ProbingState, MachineState +from .escsm import (HZ_SM_MODEL, ISO2022CN_SM_MODEL, ISO2022JP_SM_MODEL, + ISO2022KR_SM_MODEL) + + +class EscCharSetProber(CharSetProber): + """ + This CharSetProber uses a "code scheme" approach for detecting encodings, + whereby easily recognizable escape or shift sequences are relied on to + identify these encodings. + """ + + def __init__(self, lang_filter=None): + super(EscCharSetProber, self).__init__(lang_filter=lang_filter) + self.coding_sm = [] + if self.lang_filter & LanguageFilter.CHINESE_SIMPLIFIED: + self.coding_sm.append(CodingStateMachine(HZ_SM_MODEL)) + self.coding_sm.append(CodingStateMachine(ISO2022CN_SM_MODEL)) + if self.lang_filter & LanguageFilter.JAPANESE: + self.coding_sm.append(CodingStateMachine(ISO2022JP_SM_MODEL)) + if self.lang_filter & LanguageFilter.KOREAN: + self.coding_sm.append(CodingStateMachine(ISO2022KR_SM_MODEL)) + self.active_sm_count = None + self._detected_charset = None + self._detected_language = None + self._state = None + self.reset() + + def reset(self): + super(EscCharSetProber, self).reset() + for coding_sm in self.coding_sm: + if not coding_sm: + continue + coding_sm.active = True + coding_sm.reset() + self.active_sm_count = len(self.coding_sm) + self._detected_charset = None + self._detected_language = None + + @property + def charset_name(self): + return self._detected_charset + + @property + def language(self): + return self._detected_language + + def get_confidence(self): + if self._detected_charset: + return 0.99 + else: + return 0.00 + + def feed(self, byte_str): + for c in byte_str: + for coding_sm in self.coding_sm: + if not coding_sm or not coding_sm.active: + continue + coding_state = coding_sm.next_state(c) + if coding_state == MachineState.ERROR: + coding_sm.active = False + self.active_sm_count -= 1 + if self.active_sm_count <= 0: + self._state = ProbingState.NOT_ME + return self.state + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + self._detected_charset = coding_sm.get_coding_state_machine() + self._detected_language = coding_sm.language + return self.state + + return self.state diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/escsm.py b/venv/Lib/site-packages/pip/_vendor/chardet/escsm.py new file mode 100644 index 0000000..0069523 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/escsm.py @@ -0,0 +1,246 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .enums import MachineState + +HZ_CLS = ( +1,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,0,0, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,0,0,0,0, # 20 - 27 +0,0,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +0,0,0,0,0,0,0,0, # 40 - 47 +0,0,0,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,4,0,5,2,0, # 78 - 7f +1,1,1,1,1,1,1,1, # 80 - 87 +1,1,1,1,1,1,1,1, # 88 - 8f +1,1,1,1,1,1,1,1, # 90 - 97 +1,1,1,1,1,1,1,1, # 98 - 9f +1,1,1,1,1,1,1,1, # a0 - a7 +1,1,1,1,1,1,1,1, # a8 - af +1,1,1,1,1,1,1,1, # b0 - b7 +1,1,1,1,1,1,1,1, # b8 - bf +1,1,1,1,1,1,1,1, # c0 - c7 +1,1,1,1,1,1,1,1, # c8 - cf +1,1,1,1,1,1,1,1, # d0 - d7 +1,1,1,1,1,1,1,1, # d8 - df +1,1,1,1,1,1,1,1, # e0 - e7 +1,1,1,1,1,1,1,1, # e8 - ef +1,1,1,1,1,1,1,1, # f0 - f7 +1,1,1,1,1,1,1,1, # f8 - ff +) + +HZ_ST = ( +MachineState.START,MachineState.ERROR, 3,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,# 00-07 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 08-0f +MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START, 4,MachineState.ERROR,# 10-17 + 5,MachineState.ERROR, 6,MachineState.ERROR, 5, 5, 4,MachineState.ERROR,# 18-1f + 4,MachineState.ERROR, 4, 4, 4,MachineState.ERROR, 4,MachineState.ERROR,# 20-27 + 4,MachineState.ITS_ME,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 28-2f +) + +HZ_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) + +HZ_SM_MODEL = {'class_table': HZ_CLS, + 'class_factor': 6, + 'state_table': HZ_ST, + 'char_len_table': HZ_CHAR_LEN_TABLE, + 'name': "HZ-GB-2312", + 'language': 'Chinese'} + +ISO2022CN_CLS = ( +2,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,0,0, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,0,0,0,0, # 20 - 27 +0,3,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +0,0,0,4,0,0,0,0, # 40 - 47 +0,0,0,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,0,0,0,0,0, # 78 - 7f +2,2,2,2,2,2,2,2, # 80 - 87 +2,2,2,2,2,2,2,2, # 88 - 8f +2,2,2,2,2,2,2,2, # 90 - 97 +2,2,2,2,2,2,2,2, # 98 - 9f +2,2,2,2,2,2,2,2, # a0 - a7 +2,2,2,2,2,2,2,2, # a8 - af +2,2,2,2,2,2,2,2, # b0 - b7 +2,2,2,2,2,2,2,2, # b8 - bf +2,2,2,2,2,2,2,2, # c0 - c7 +2,2,2,2,2,2,2,2, # c8 - cf +2,2,2,2,2,2,2,2, # d0 - d7 +2,2,2,2,2,2,2,2, # d8 - df +2,2,2,2,2,2,2,2, # e0 - e7 +2,2,2,2,2,2,2,2, # e8 - ef +2,2,2,2,2,2,2,2, # f0 - f7 +2,2,2,2,2,2,2,2, # f8 - ff +) + +ISO2022CN_ST = ( +MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 00-07 +MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 08-0f +MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 10-17 +MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 4,MachineState.ERROR,# 18-1f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 20-27 + 5, 6,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 28-2f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 30-37 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,# 38-3f +) + +ISO2022CN_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0) + +ISO2022CN_SM_MODEL = {'class_table': ISO2022CN_CLS, + 'class_factor': 9, + 'state_table': ISO2022CN_ST, + 'char_len_table': ISO2022CN_CHAR_LEN_TABLE, + 'name': "ISO-2022-CN", + 'language': 'Chinese'} + +ISO2022JP_CLS = ( +2,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,2,2, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,7,0,0,0, # 20 - 27 +3,0,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +6,0,4,0,8,0,0,0, # 40 - 47 +0,9,5,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,0,0,0,0,0, # 78 - 7f +2,2,2,2,2,2,2,2, # 80 - 87 +2,2,2,2,2,2,2,2, # 88 - 8f +2,2,2,2,2,2,2,2, # 90 - 97 +2,2,2,2,2,2,2,2, # 98 - 9f +2,2,2,2,2,2,2,2, # a0 - a7 +2,2,2,2,2,2,2,2, # a8 - af +2,2,2,2,2,2,2,2, # b0 - b7 +2,2,2,2,2,2,2,2, # b8 - bf +2,2,2,2,2,2,2,2, # c0 - c7 +2,2,2,2,2,2,2,2, # c8 - cf +2,2,2,2,2,2,2,2, # d0 - d7 +2,2,2,2,2,2,2,2, # d8 - df +2,2,2,2,2,2,2,2, # e0 - e7 +2,2,2,2,2,2,2,2, # e8 - ef +2,2,2,2,2,2,2,2, # f0 - f7 +2,2,2,2,2,2,2,2, # f8 - ff +) + +ISO2022JP_ST = ( +MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 00-07 +MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 08-0f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 10-17 +MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,# 18-1f +MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 4,MachineState.ERROR,MachineState.ERROR,# 20-27 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 6,MachineState.ITS_ME,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,# 28-2f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,# 30-37 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 38-3f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,MachineState.START,# 40-47 +) + +ISO2022JP_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + +ISO2022JP_SM_MODEL = {'class_table': ISO2022JP_CLS, + 'class_factor': 10, + 'state_table': ISO2022JP_ST, + 'char_len_table': ISO2022JP_CHAR_LEN_TABLE, + 'name': "ISO-2022-JP", + 'language': 'Japanese'} + +ISO2022KR_CLS = ( +2,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,0,0, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,3,0,0,0, # 20 - 27 +0,4,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +0,0,0,5,0,0,0,0, # 40 - 47 +0,0,0,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,0,0,0,0,0, # 78 - 7f +2,2,2,2,2,2,2,2, # 80 - 87 +2,2,2,2,2,2,2,2, # 88 - 8f +2,2,2,2,2,2,2,2, # 90 - 97 +2,2,2,2,2,2,2,2, # 98 - 9f +2,2,2,2,2,2,2,2, # a0 - a7 +2,2,2,2,2,2,2,2, # a8 - af +2,2,2,2,2,2,2,2, # b0 - b7 +2,2,2,2,2,2,2,2, # b8 - bf +2,2,2,2,2,2,2,2, # c0 - c7 +2,2,2,2,2,2,2,2, # c8 - cf +2,2,2,2,2,2,2,2, # d0 - d7 +2,2,2,2,2,2,2,2, # d8 - df +2,2,2,2,2,2,2,2, # e0 - e7 +2,2,2,2,2,2,2,2, # e8 - ef +2,2,2,2,2,2,2,2, # f0 - f7 +2,2,2,2,2,2,2,2, # f8 - ff +) + +ISO2022KR_ST = ( +MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,# 00-07 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 08-0f +MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 4,MachineState.ERROR,MachineState.ERROR,# 10-17 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 18-1f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 20-27 +) + +ISO2022KR_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) + +ISO2022KR_SM_MODEL = {'class_table': ISO2022KR_CLS, + 'class_factor': 6, + 'state_table': ISO2022KR_ST, + 'char_len_table': ISO2022KR_CHAR_LEN_TABLE, + 'name': "ISO-2022-KR", + 'language': 'Korean'} + + diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py new file mode 100644 index 0000000..20ce8f7 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py @@ -0,0 +1,92 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .enums import ProbingState, MachineState +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import EUCJPDistributionAnalysis +from .jpcntx import EUCJPContextAnalysis +from .mbcssm import EUCJP_SM_MODEL + + +class EUCJPProber(MultiByteCharSetProber): + def __init__(self): + super(EUCJPProber, self).__init__() + self.coding_sm = CodingStateMachine(EUCJP_SM_MODEL) + self.distribution_analyzer = EUCJPDistributionAnalysis() + self.context_analyzer = EUCJPContextAnalysis() + self.reset() + + def reset(self): + super(EUCJPProber, self).reset() + self.context_analyzer.reset() + + @property + def charset_name(self): + return "EUC-JP" + + @property + def language(self): + return "Japanese" + + def feed(self, byte_str): + for i in range(len(byte_str)): + # PY3K: byte_str is a byte array, so byte_str[i] is an int, not a byte + coding_state = self.coding_sm.next_state(byte_str[i]) + if coding_state == MachineState.ERROR: + self.logger.debug('%s %s prober hit error at byte %s', + self.charset_name, self.language, i) + self._state = ProbingState.NOT_ME + break + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + elif coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() + if i == 0: + self._last_char[1] = byte_str[0] + self.context_analyzer.feed(self._last_char, char_len) + self.distribution_analyzer.feed(self._last_char, char_len) + else: + self.context_analyzer.feed(byte_str[i - 1:i + 1], + char_len) + self.distribution_analyzer.feed(byte_str[i - 1:i + 1], + char_len) + + self._last_char[0] = byte_str[-1] + + if self.state == ProbingState.DETECTING: + if (self.context_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self): + context_conf = self.context_analyzer.get_confidence() + distrib_conf = self.distribution_analyzer.get_confidence() + return max(context_conf, distrib_conf) diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/euckrfreq.py b/venv/Lib/site-packages/pip/_vendor/chardet/euckrfreq.py new file mode 100644 index 0000000..b68078c --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/euckrfreq.py @@ -0,0 +1,195 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Sampling from about 20M text materials include literature and computer technology + +# 128 --> 0.79 +# 256 --> 0.92 +# 512 --> 0.986 +# 1024 --> 0.99944 +# 2048 --> 0.99999 +# +# Idea Distribution Ratio = 0.98653 / (1-0.98653) = 73.24 +# Random Distribution Ration = 512 / (2350-512) = 0.279. +# +# Typical Distribution Ratio + +EUCKR_TYPICAL_DISTRIBUTION_RATIO = 6.0 + +EUCKR_TABLE_SIZE = 2352 + +# Char to FreqOrder table , +EUCKR_CHAR_TO_FREQ_ORDER = ( + 13, 130, 120,1396, 481,1719,1720, 328, 609, 212,1721, 707, 400, 299,1722, 87, +1397,1723, 104, 536,1117,1203,1724,1267, 685,1268, 508,1725,1726,1727,1728,1398, +1399,1729,1730,1731, 141, 621, 326,1057, 368,1732, 267, 488, 20,1733,1269,1734, + 945,1400,1735, 47, 904,1270,1736,1737, 773, 248,1738, 409, 313, 786, 429,1739, + 116, 987, 813,1401, 683, 75,1204, 145,1740,1741,1742,1743, 16, 847, 667, 622, + 708,1744,1745,1746, 966, 787, 304, 129,1747, 60, 820, 123, 676,1748,1749,1750, +1751, 617,1752, 626,1753,1754,1755,1756, 653,1757,1758,1759,1760,1761,1762, 856, + 344,1763,1764,1765,1766, 89, 401, 418, 806, 905, 848,1767,1768,1769, 946,1205, + 709,1770,1118,1771, 241,1772,1773,1774,1271,1775, 569,1776, 999,1777,1778,1779, +1780, 337, 751,1058, 28, 628, 254,1781, 177, 906, 270, 349, 891,1079,1782, 19, +1783, 379,1784, 315,1785, 629, 754,1402, 559,1786, 636, 203,1206,1787, 710, 567, +1788, 935, 814,1789,1790,1207, 766, 528,1791,1792,1208,1793,1794,1795,1796,1797, +1403,1798,1799, 533,1059,1404,1405,1156,1406, 936, 884,1080,1800, 351,1801,1802, +1803,1804,1805, 801,1806,1807,1808,1119,1809,1157, 714, 474,1407,1810, 298, 899, + 885,1811,1120, 802,1158,1812, 892,1813,1814,1408, 659,1815,1816,1121,1817,1818, +1819,1820,1821,1822, 319,1823, 594, 545,1824, 815, 937,1209,1825,1826, 573,1409, +1022,1827,1210,1828,1829,1830,1831,1832,1833, 556, 722, 807,1122,1060,1834, 697, +1835, 900, 557, 715,1836,1410, 540,1411, 752,1159, 294, 597,1211, 976, 803, 770, +1412,1837,1838, 39, 794,1413, 358,1839, 371, 925,1840, 453, 661, 788, 531, 723, + 544,1023,1081, 869, 91,1841, 392, 430, 790, 602,1414, 677,1082, 457,1415,1416, +1842,1843, 475, 327,1024,1417, 795, 121,1844, 733, 403,1418,1845,1846,1847, 300, + 119, 711,1212, 627,1848,1272, 207,1849,1850, 796,1213, 382,1851, 519,1852,1083, + 893,1853,1854,1855, 367, 809, 487, 671,1856, 663,1857,1858, 956, 471, 306, 857, +1859,1860,1160,1084,1861,1862,1863,1864,1865,1061,1866,1867,1868,1869,1870,1871, + 282, 96, 574,1872, 502,1085,1873,1214,1874, 907,1875,1876, 827, 977,1419,1420, +1421, 268,1877,1422,1878,1879,1880, 308,1881, 2, 537,1882,1883,1215,1884,1885, + 127, 791,1886,1273,1423,1887, 34, 336, 404, 643,1888, 571, 654, 894, 840,1889, + 0, 886,1274, 122, 575, 260, 908, 938,1890,1275, 410, 316,1891,1892, 100,1893, +1894,1123, 48,1161,1124,1025,1895, 633, 901,1276,1896,1897, 115, 816,1898, 317, +1899, 694,1900, 909, 734,1424, 572, 866,1425, 691, 85, 524,1010, 543, 394, 841, +1901,1902,1903,1026,1904,1905,1906,1907,1908,1909, 30, 451, 651, 988, 310,1910, +1911,1426, 810,1216, 93,1912,1913,1277,1217,1914, 858, 759, 45, 58, 181, 610, + 269,1915,1916, 131,1062, 551, 443,1000, 821,1427, 957, 895,1086,1917,1918, 375, +1919, 359,1920, 687,1921, 822,1922, 293,1923,1924, 40, 662, 118, 692, 29, 939, + 887, 640, 482, 174,1925, 69,1162, 728,1428, 910,1926,1278,1218,1279, 386, 870, + 217, 854,1163, 823,1927,1928,1929,1930, 834,1931, 78,1932, 859,1933,1063,1934, +1935,1936,1937, 438,1164, 208, 595,1938,1939,1940,1941,1219,1125,1942, 280, 888, +1429,1430,1220,1431,1943,1944,1945,1946,1947,1280, 150, 510,1432,1948,1949,1950, +1951,1952,1953,1954,1011,1087,1955,1433,1043,1956, 881,1957, 614, 958,1064,1065, +1221,1958, 638,1001, 860, 967, 896,1434, 989, 492, 553,1281,1165,1959,1282,1002, +1283,1222,1960,1961,1962,1963, 36, 383, 228, 753, 247, 454,1964, 876, 678,1965, +1966,1284, 126, 464, 490, 835, 136, 672, 529, 940,1088,1435, 473,1967,1968, 467, + 50, 390, 227, 587, 279, 378, 598, 792, 968, 240, 151, 160, 849, 882,1126,1285, + 639,1044, 133, 140, 288, 360, 811, 563,1027, 561, 142, 523,1969,1970,1971, 7, + 103, 296, 439, 407, 506, 634, 990,1972,1973,1974,1975, 645,1976,1977,1978,1979, +1980,1981, 236,1982,1436,1983,1984,1089, 192, 828, 618, 518,1166, 333,1127,1985, + 818,1223,1986,1987,1988,1989,1990,1991,1992,1993, 342,1128,1286, 746, 842,1994, +1995, 560, 223,1287, 98, 8, 189, 650, 978,1288,1996,1437,1997, 17, 345, 250, + 423, 277, 234, 512, 226, 97, 289, 42, 167,1998, 201,1999,2000, 843, 836, 824, + 532, 338, 783,1090, 182, 576, 436,1438,1439, 527, 500,2001, 947, 889,2002,2003, +2004,2005, 262, 600, 314, 447,2006, 547,2007, 693, 738,1129,2008, 71,1440, 745, + 619, 688,2009, 829,2010,2011, 147,2012, 33, 948,2013,2014, 74, 224,2015, 61, + 191, 918, 399, 637,2016,1028,1130, 257, 902,2017,2018,2019,2020,2021,2022,2023, +2024,2025,2026, 837,2027,2028,2029,2030, 179, 874, 591, 52, 724, 246,2031,2032, +2033,2034,1167, 969,2035,1289, 630, 605, 911,1091,1168,2036,2037,2038,1441, 912, +2039, 623,2040,2041, 253,1169,1290,2042,1442, 146, 620, 611, 577, 433,2043,1224, + 719,1170, 959, 440, 437, 534, 84, 388, 480,1131, 159, 220, 198, 679,2044,1012, + 819,1066,1443, 113,1225, 194, 318,1003,1029,2045,2046,2047,2048,1067,2049,2050, +2051,2052,2053, 59, 913, 112,2054, 632,2055, 455, 144, 739,1291,2056, 273, 681, + 499,2057, 448,2058,2059, 760,2060,2061, 970, 384, 169, 245,1132,2062,2063, 414, +1444,2064,2065, 41, 235,2066, 157, 252, 877, 568, 919, 789, 580,2067, 725,2068, +2069,1292,2070,2071,1445,2072,1446,2073,2074, 55, 588, 66,1447, 271,1092,2075, +1226,2076, 960,1013, 372,2077,2078,2079,2080,2081,1293,2082,2083,2084,2085, 850, +2086,2087,2088,2089,2090, 186,2091,1068, 180,2092,2093,2094, 109,1227, 522, 606, +2095, 867,1448,1093, 991,1171, 926, 353,1133,2096, 581,2097,2098,2099,1294,1449, +1450,2100, 596,1172,1014,1228,2101,1451,1295,1173,1229,2102,2103,1296,1134,1452, + 949,1135,2104,2105,1094,1453,1454,1455,2106,1095,2107,2108,2109,2110,2111,2112, +2113,2114,2115,2116,2117, 804,2118,2119,1230,1231, 805,1456, 405,1136,2120,2121, +2122,2123,2124, 720, 701,1297, 992,1457, 927,1004,2125,2126,2127,2128,2129,2130, + 22, 417,2131, 303,2132, 385,2133, 971, 520, 513,2134,1174, 73,1096, 231, 274, + 962,1458, 673,2135,1459,2136, 152,1137,2137,2138,2139,2140,1005,1138,1460,1139, +2141,2142,2143,2144, 11, 374, 844,2145, 154,1232, 46,1461,2146, 838, 830, 721, +1233, 106,2147, 90, 428, 462, 578, 566,1175, 352,2148,2149, 538,1234, 124,1298, +2150,1462, 761, 565,2151, 686,2152, 649,2153, 72, 173,2154, 460, 415,2155,1463, +2156,1235, 305,2157,2158,2159,2160,2161,2162, 579,2163,2164,2165,2166,2167, 747, +2168,2169,2170,2171,1464, 669,2172,2173,2174,2175,2176,1465,2177, 23, 530, 285, +2178, 335, 729,2179, 397,2180,2181,2182,1030,2183,2184, 698,2185,2186, 325,2187, +2188, 369,2189, 799,1097,1015, 348,2190,1069, 680,2191, 851,1466,2192,2193, 10, +2194, 613, 424,2195, 979, 108, 449, 589, 27, 172, 81,1031, 80, 774, 281, 350, +1032, 525, 301, 582,1176,2196, 674,1045,2197,2198,1467, 730, 762,2199,2200,2201, +2202,1468,2203, 993,2204,2205, 266,1070, 963,1140,2206,2207,2208, 664,1098, 972, +2209,2210,2211,1177,1469,1470, 871,2212,2213,2214,2215,2216,1471,2217,2218,2219, +2220,2221,2222,2223,2224,2225,2226,2227,1472,1236,2228,2229,2230,2231,2232,2233, +2234,2235,1299,2236,2237, 200,2238, 477, 373,2239,2240, 731, 825, 777,2241,2242, +2243, 521, 486, 548,2244,2245,2246,1473,1300, 53, 549, 137, 875, 76, 158,2247, +1301,1474, 469, 396,1016, 278, 712,2248, 321, 442, 503, 767, 744, 941,1237,1178, +1475,2249, 82, 178,1141,1179, 973,2250,1302,2251, 297,2252,2253, 570,2254,2255, +2256, 18, 450, 206,2257, 290, 292,1142,2258, 511, 162, 99, 346, 164, 735,2259, +1476,1477, 4, 554, 343, 798,1099,2260,1100,2261, 43, 171,1303, 139, 215,2262, +2263, 717, 775,2264,1033, 322, 216,2265, 831,2266, 149,2267,1304,2268,2269, 702, +1238, 135, 845, 347, 309,2270, 484,2271, 878, 655, 238,1006,1478,2272, 67,2273, + 295,2274,2275, 461,2276, 478, 942, 412,2277,1034,2278,2279,2280, 265,2281, 541, +2282,2283,2284,2285,2286, 70, 852,1071,2287,2288,2289,2290, 21, 56, 509, 117, + 432,2291,2292, 331, 980, 552,1101, 148, 284, 105, 393,1180,1239, 755,2293, 187, +2294,1046,1479,2295, 340,2296, 63,1047, 230,2297,2298,1305, 763,1306, 101, 800, + 808, 494,2299,2300,2301, 903,2302, 37,1072, 14, 5,2303, 79, 675,2304, 312, +2305,2306,2307,2308,2309,1480, 6,1307,2310,2311,2312, 1, 470, 35, 24, 229, +2313, 695, 210, 86, 778, 15, 784, 592, 779, 32, 77, 855, 964,2314, 259,2315, + 501, 380,2316,2317, 83, 981, 153, 689,1308,1481,1482,1483,2318,2319, 716,1484, +2320,2321,2322,2323,2324,2325,1485,2326,2327, 128, 57, 68, 261,1048, 211, 170, +1240, 31,2328, 51, 435, 742,2329,2330,2331, 635,2332, 264, 456,2333,2334,2335, + 425,2336,1486, 143, 507, 263, 943,2337, 363, 920,1487, 256,1488,1102, 243, 601, +1489,2338,2339,2340,2341,2342,2343,2344, 861,2345,2346,2347,2348,2349,2350, 395, +2351,1490,1491, 62, 535, 166, 225,2352,2353, 668, 419,1241, 138, 604, 928,2354, +1181,2355,1492,1493,2356,2357,2358,1143,2359, 696,2360, 387, 307,1309, 682, 476, +2361,2362, 332, 12, 222, 156,2363, 232,2364, 641, 276, 656, 517,1494,1495,1035, + 416, 736,1496,2365,1017, 586,2366,2367,2368,1497,2369, 242,2370,2371,2372,1498, +2373, 965, 713,2374,2375,2376,2377, 740, 982,1499, 944,1500,1007,2378,2379,1310, +1501,2380,2381,2382, 785, 329,2383,2384,1502,2385,2386,2387, 932,2388,1503,2389, +2390,2391,2392,1242,2393,2394,2395,2396,2397, 994, 950,2398,2399,2400,2401,1504, +1311,2402,2403,2404,2405,1049, 749,2406,2407, 853, 718,1144,1312,2408,1182,1505, +2409,2410, 255, 516, 479, 564, 550, 214,1506,1507,1313, 413, 239, 444, 339,1145, +1036,1508,1509,1314,1037,1510,1315,2411,1511,2412,2413,2414, 176, 703, 497, 624, + 593, 921, 302,2415, 341, 165,1103,1512,2416,1513,2417,2418,2419, 376,2420, 700, +2421,2422,2423, 258, 768,1316,2424,1183,2425, 995, 608,2426,2427,2428,2429, 221, +2430,2431,2432,2433,2434,2435,2436,2437, 195, 323, 726, 188, 897, 983,1317, 377, + 644,1050, 879,2438, 452,2439,2440,2441,2442,2443,2444, 914,2445,2446,2447,2448, + 915, 489,2449,1514,1184,2450,2451, 515, 64, 427, 495,2452, 583,2453, 483, 485, +1038, 562, 213,1515, 748, 666,2454,2455,2456,2457, 334,2458, 780, 996,1008, 705, +1243,2459,2460,2461,2462,2463, 114,2464, 493,1146, 366, 163,1516, 961,1104,2465, + 291,2466,1318,1105,2467,1517, 365,2468, 355, 951,1244,2469,1319,2470, 631,2471, +2472, 218,1320, 364, 320, 756,1518,1519,1321,1520,1322,2473,2474,2475,2476, 997, +2477,2478,2479,2480, 665,1185,2481, 916,1521,2482,2483,2484, 584, 684,2485,2486, + 797,2487,1051,1186,2488,2489,2490,1522,2491,2492, 370,2493,1039,1187, 65,2494, + 434, 205, 463,1188,2495, 125, 812, 391, 402, 826, 699, 286, 398, 155, 781, 771, + 585,2496, 590, 505,1073,2497, 599, 244, 219, 917,1018, 952, 646,1523,2498,1323, +2499,2500, 49, 984, 354, 741,2501, 625,2502,1324,2503,1019, 190, 357, 757, 491, + 95, 782, 868,2504,2505,2506,2507,2508,2509, 134,1524,1074, 422,1525, 898,2510, + 161,2511,2512,2513,2514, 769,2515,1526,2516,2517, 411,1325,2518, 472,1527,2519, +2520,2521,2522,2523,2524, 985,2525,2526,2527,2528,2529,2530, 764,2531,1245,2532, +2533, 25, 204, 311,2534, 496,2535,1052,2536,2537,2538,2539,2540,2541,2542, 199, + 704, 504, 468, 758, 657,1528, 196, 44, 839,1246, 272, 750,2543, 765, 862,2544, +2545,1326,2546, 132, 615, 933,2547, 732,2548,2549,2550,1189,1529,2551, 283,1247, +1053, 607, 929,2552,2553,2554, 930, 183, 872, 616,1040,1147,2555,1148,1020, 441, + 249,1075,2556,2557,2558, 466, 743,2559,2560,2561, 92, 514, 426, 420, 526,2562, +2563,2564,2565,2566,2567,2568, 185,2569,2570,2571,2572, 776,1530, 658,2573, 362, +2574, 361, 922,1076, 793,2575,2576,2577,2578,2579,2580,1531, 251,2581,2582,2583, +2584,1532, 54, 612, 237,1327,2585,2586, 275, 408, 647, 111,2587,1533,1106, 465, + 3, 458, 9, 38,2588, 107, 110, 890, 209, 26, 737, 498,2589,1534,2590, 431, + 202, 88,1535, 356, 287,1107, 660,1149,2591, 381,1536, 986,1150, 445,1248,1151, + 974,2592,2593, 846,2594, 446, 953, 184,1249,1250, 727,2595, 923, 193, 883,2596, +2597,2598, 102, 324, 539, 817,2599, 421,1041,2600, 832,2601, 94, 175, 197, 406, +2602, 459,2603,2604,2605,2606,2607, 330, 555,2608,2609,2610, 706,1108, 389,2611, +2612,2613,2614, 233,2615, 833, 558, 931, 954,1251,2616,2617,1537, 546,2618,2619, +1009,2620,2621,2622,1538, 690,1328,2623, 955,2624,1539,2625,2626, 772,2627,2628, +2629,2630,2631, 924, 648, 863, 603,2632,2633, 934,1540, 864, 865,2634, 642,1042, + 670,1190,2635,2636,2637,2638, 168,2639, 652, 873, 542,1054,1541,2640,2641,2642, # 512, 256 +) + diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/euckrprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/euckrprober.py new file mode 100644 index 0000000..345a060 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/euckrprober.py @@ -0,0 +1,47 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import EUCKRDistributionAnalysis +from .mbcssm import EUCKR_SM_MODEL + + +class EUCKRProber(MultiByteCharSetProber): + def __init__(self): + super(EUCKRProber, self).__init__() + self.coding_sm = CodingStateMachine(EUCKR_SM_MODEL) + self.distribution_analyzer = EUCKRDistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "EUC-KR" + + @property + def language(self): + return "Korean" diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/euctwfreq.py b/venv/Lib/site-packages/pip/_vendor/chardet/euctwfreq.py new file mode 100644 index 0000000..ed7a995 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/euctwfreq.py @@ -0,0 +1,387 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# EUCTW frequency table +# Converted from big5 work +# by Taiwan's Mandarin Promotion Council +# <http:#www.edu.tw:81/mandr/> + +# 128 --> 0.42261 +# 256 --> 0.57851 +# 512 --> 0.74851 +# 1024 --> 0.89384 +# 2048 --> 0.97583 +# +# Idea Distribution Ratio = 0.74851/(1-0.74851) =2.98 +# Random Distribution Ration = 512/(5401-512)=0.105 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR + +EUCTW_TYPICAL_DISTRIBUTION_RATIO = 0.75 + +# Char to FreqOrder table , +EUCTW_TABLE_SIZE = 5376 + +EUCTW_CHAR_TO_FREQ_ORDER = ( + 1,1800,1506, 255,1431, 198, 9, 82, 6,7310, 177, 202,3615,1256,2808, 110, # 2742 +3735, 33,3241, 261, 76, 44,2113, 16,2931,2184,1176, 659,3868, 26,3404,2643, # 2758 +1198,3869,3313,4060, 410,2211, 302, 590, 361,1963, 8, 204, 58,4296,7311,1931, # 2774 + 63,7312,7313, 317,1614, 75, 222, 159,4061,2412,1480,7314,3500,3068, 224,2809, # 2790 +3616, 3, 10,3870,1471, 29,2774,1135,2852,1939, 873, 130,3242,1123, 312,7315, # 2806 +4297,2051, 507, 252, 682,7316, 142,1914, 124, 206,2932, 34,3501,3173, 64, 604, # 2822 +7317,2494,1976,1977, 155,1990, 645, 641,1606,7318,3405, 337, 72, 406,7319, 80, # 2838 + 630, 238,3174,1509, 263, 939,1092,2644, 756,1440,1094,3406, 449, 69,2969, 591, # 2854 + 179,2095, 471, 115,2034,1843, 60, 50,2970, 134, 806,1868, 734,2035,3407, 180, # 2870 + 995,1607, 156, 537,2893, 688,7320, 319,1305, 779,2144, 514,2374, 298,4298, 359, # 2886 +2495, 90,2707,1338, 663, 11, 906,1099,2545, 20,2436, 182, 532,1716,7321, 732, # 2902 +1376,4062,1311,1420,3175, 25,2312,1056, 113, 399, 382,1949, 242,3408,2467, 529, # 2918 +3243, 475,1447,3617,7322, 117, 21, 656, 810,1297,2295,2329,3502,7323, 126,4063, # 2934 + 706, 456, 150, 613,4299, 71,1118,2036,4064, 145,3069, 85, 835, 486,2114,1246, # 2950 +1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,7324,2127,2354, 347,3736, 221, # 2966 +3503,3110,7325,1955,1153,4065, 83, 296,1199,3070, 192, 624, 93,7326, 822,1897, # 2982 +2810,3111, 795,2064, 991,1554,1542,1592, 27, 43,2853, 859, 139,1456, 860,4300, # 2998 + 437, 712,3871, 164,2392,3112, 695, 211,3017,2096, 195,3872,1608,3504,3505,3618, # 3014 +3873, 234, 811,2971,2097,3874,2229,1441,3506,1615,2375, 668,2076,1638, 305, 228, # 3030 +1664,4301, 467, 415,7327, 262,2098,1593, 239, 108, 300, 200,1033, 512,1247,2077, # 3046 +7328,7329,2173,3176,3619,2673, 593, 845,1062,3244, 88,1723,2037,3875,1950, 212, # 3062 + 266, 152, 149, 468,1898,4066,4302, 77, 187,7330,3018, 37, 5,2972,7331,3876, # 3078 +7332,7333, 39,2517,4303,2894,3177,2078, 55, 148, 74,4304, 545, 483,1474,1029, # 3094 +1665, 217,1869,1531,3113,1104,2645,4067, 24, 172,3507, 900,3877,3508,3509,4305, # 3110 + 32,1408,2811,1312, 329, 487,2355,2247,2708, 784,2674, 4,3019,3314,1427,1788, # 3126 + 188, 109, 499,7334,3620,1717,1789, 888,1217,3020,4306,7335,3510,7336,3315,1520, # 3142 +3621,3878, 196,1034, 775,7337,7338, 929,1815, 249, 439, 38,7339,1063,7340, 794, # 3158 +3879,1435,2296, 46, 178,3245,2065,7341,2376,7342, 214,1709,4307, 804, 35, 707, # 3174 + 324,3622,1601,2546, 140, 459,4068,7343,7344,1365, 839, 272, 978,2257,2572,3409, # 3190 +2128,1363,3623,1423, 697, 100,3071, 48, 70,1231, 495,3114,2193,7345,1294,7346, # 3206 +2079, 462, 586,1042,3246, 853, 256, 988, 185,2377,3410,1698, 434,1084,7347,3411, # 3222 + 314,2615,2775,4308,2330,2331, 569,2280, 637,1816,2518, 757,1162,1878,1616,3412, # 3238 + 287,1577,2115, 768,4309,1671,2854,3511,2519,1321,3737, 909,2413,7348,4069, 933, # 3254 +3738,7349,2052,2356,1222,4310, 765,2414,1322, 786,4311,7350,1919,1462,1677,2895, # 3270 +1699,7351,4312,1424,2437,3115,3624,2590,3316,1774,1940,3413,3880,4070, 309,1369, # 3286 +1130,2812, 364,2230,1653,1299,3881,3512,3882,3883,2646, 525,1085,3021, 902,2000, # 3302 +1475, 964,4313, 421,1844,1415,1057,2281, 940,1364,3116, 376,4314,4315,1381, 7, # 3318 +2520, 983,2378, 336,1710,2675,1845, 321,3414, 559,1131,3022,2742,1808,1132,1313, # 3334 + 265,1481,1857,7352, 352,1203,2813,3247, 167,1089, 420,2814, 776, 792,1724,3513, # 3350 +4071,2438,3248,7353,4072,7354, 446, 229, 333,2743, 901,3739,1200,1557,4316,2647, # 3366 +1920, 395,2744,2676,3740,4073,1835, 125, 916,3178,2616,4317,7355,7356,3741,7357, # 3382 +7358,7359,4318,3117,3625,1133,2547,1757,3415,1510,2313,1409,3514,7360,2145, 438, # 3398 +2591,2896,2379,3317,1068, 958,3023, 461, 311,2855,2677,4074,1915,3179,4075,1978, # 3414 + 383, 750,2745,2617,4076, 274, 539, 385,1278,1442,7361,1154,1964, 384, 561, 210, # 3430 + 98,1295,2548,3515,7362,1711,2415,1482,3416,3884,2897,1257, 129,7363,3742, 642, # 3446 + 523,2776,2777,2648,7364, 141,2231,1333, 68, 176, 441, 876, 907,4077, 603,2592, # 3462 + 710, 171,3417, 404, 549, 18,3118,2393,1410,3626,1666,7365,3516,4319,2898,4320, # 3478 +7366,2973, 368,7367, 146, 366, 99, 871,3627,1543, 748, 807,1586,1185, 22,2258, # 3494 + 379,3743,3180,7368,3181, 505,1941,2618,1991,1382,2314,7369, 380,2357, 218, 702, # 3510 +1817,1248,3418,3024,3517,3318,3249,7370,2974,3628, 930,3250,3744,7371, 59,7372, # 3526 + 585, 601,4078, 497,3419,1112,1314,4321,1801,7373,1223,1472,2174,7374, 749,1836, # 3542 + 690,1899,3745,1772,3885,1476, 429,1043,1790,2232,2116, 917,4079, 447,1086,1629, # 3558 +7375, 556,7376,7377,2020,1654, 844,1090, 105, 550, 966,1758,2815,1008,1782, 686, # 3574 +1095,7378,2282, 793,1602,7379,3518,2593,4322,4080,2933,2297,4323,3746, 980,2496, # 3590 + 544, 353, 527,4324, 908,2678,2899,7380, 381,2619,1942,1348,7381,1341,1252, 560, # 3606 +3072,7382,3420,2856,7383,2053, 973, 886,2080, 143,4325,7384,7385, 157,3886, 496, # 3622 +4081, 57, 840, 540,2038,4326,4327,3421,2117,1445, 970,2259,1748,1965,2081,4082, # 3638 +3119,1234,1775,3251,2816,3629, 773,1206,2129,1066,2039,1326,3887,1738,1725,4083, # 3654 + 279,3120, 51,1544,2594, 423,1578,2130,2066, 173,4328,1879,7386,7387,1583, 264, # 3670 + 610,3630,4329,2439, 280, 154,7388,7389,7390,1739, 338,1282,3073, 693,2857,1411, # 3686 +1074,3747,2440,7391,4330,7392,7393,1240, 952,2394,7394,2900,1538,2679, 685,1483, # 3702 +4084,2468,1436, 953,4085,2054,4331, 671,2395, 79,4086,2441,3252, 608, 567,2680, # 3718 +3422,4087,4088,1691, 393,1261,1791,2396,7395,4332,7396,7397,7398,7399,1383,1672, # 3734 +3748,3182,1464, 522,1119, 661,1150, 216, 675,4333,3888,1432,3519, 609,4334,2681, # 3750 +2397,7400,7401,7402,4089,3025, 0,7403,2469, 315, 231,2442, 301,3319,4335,2380, # 3766 +7404, 233,4090,3631,1818,4336,4337,7405, 96,1776,1315,2082,7406, 257,7407,1809, # 3782 +3632,2709,1139,1819,4091,2021,1124,2163,2778,1777,2649,7408,3074, 363,1655,3183, # 3798 +7409,2975,7410,7411,7412,3889,1567,3890, 718, 103,3184, 849,1443, 341,3320,2934, # 3814 +1484,7413,1712, 127, 67, 339,4092,2398, 679,1412, 821,7414,7415, 834, 738, 351, # 3830 +2976,2146, 846, 235,1497,1880, 418,1992,3749,2710, 186,1100,2147,2746,3520,1545, # 3846 +1355,2935,2858,1377, 583,3891,4093,2573,2977,7416,1298,3633,1078,2549,3634,2358, # 3862 + 78,3750,3751, 267,1289,2099,2001,1594,4094, 348, 369,1274,2194,2175,1837,4338, # 3878 +1820,2817,3635,2747,2283,2002,4339,2936,2748, 144,3321, 882,4340,3892,2749,3423, # 3894 +4341,2901,7417,4095,1726, 320,7418,3893,3026, 788,2978,7419,2818,1773,1327,2859, # 3910 +3894,2819,7420,1306,4342,2003,1700,3752,3521,2359,2650, 787,2022, 506, 824,3636, # 3926 + 534, 323,4343,1044,3322,2023,1900, 946,3424,7421,1778,1500,1678,7422,1881,4344, # 3942 + 165, 243,4345,3637,2521, 123, 683,4096, 764,4346, 36,3895,1792, 589,2902, 816, # 3958 + 626,1667,3027,2233,1639,1555,1622,3753,3896,7423,3897,2860,1370,1228,1932, 891, # 3974 +2083,2903, 304,4097,7424, 292,2979,2711,3522, 691,2100,4098,1115,4347, 118, 662, # 3990 +7425, 611,1156, 854,2381,1316,2861, 2, 386, 515,2904,7426,7427,3253, 868,2234, # 4006 +1486, 855,2651, 785,2212,3028,7428,1040,3185,3523,7429,3121, 448,7430,1525,7431, # 4022 +2164,4348,7432,3754,7433,4099,2820,3524,3122, 503, 818,3898,3123,1568, 814, 676, # 4038 +1444, 306,1749,7434,3755,1416,1030, 197,1428, 805,2821,1501,4349,7435,7436,7437, # 4054 +1993,7438,4350,7439,7440,2195, 13,2779,3638,2980,3124,1229,1916,7441,3756,2131, # 4070 +7442,4100,4351,2399,3525,7443,2213,1511,1727,1120,7444,7445, 646,3757,2443, 307, # 4086 +7446,7447,1595,3186,7448,7449,7450,3639,1113,1356,3899,1465,2522,2523,7451, 519, # 4102 +7452, 128,2132, 92,2284,1979,7453,3900,1512, 342,3125,2196,7454,2780,2214,1980, # 4118 +3323,7455, 290,1656,1317, 789, 827,2360,7456,3758,4352, 562, 581,3901,7457, 401, # 4134 +4353,2248, 94,4354,1399,2781,7458,1463,2024,4355,3187,1943,7459, 828,1105,4101, # 4150 +1262,1394,7460,4102, 605,4356,7461,1783,2862,7462,2822, 819,2101, 578,2197,2937, # 4166 +7463,1502, 436,3254,4103,3255,2823,3902,2905,3425,3426,7464,2712,2315,7465,7466, # 4182 +2332,2067, 23,4357, 193, 826,3759,2102, 699,1630,4104,3075, 390,1793,1064,3526, # 4198 +7467,1579,3076,3077,1400,7468,4105,1838,1640,2863,7469,4358,4359, 137,4106, 598, # 4214 +3078,1966, 780, 104, 974,2938,7470, 278, 899, 253, 402, 572, 504, 493,1339,7471, # 4230 +3903,1275,4360,2574,2550,7472,3640,3029,3079,2249, 565,1334,2713, 863, 41,7473, # 4246 +7474,4361,7475,1657,2333, 19, 463,2750,4107, 606,7476,2981,3256,1087,2084,1323, # 4262 +2652,2982,7477,1631,1623,1750,4108,2682,7478,2864, 791,2714,2653,2334, 232,2416, # 4278 +7479,2983,1498,7480,2654,2620, 755,1366,3641,3257,3126,2025,1609, 119,1917,3427, # 4294 + 862,1026,4109,7481,3904,3760,4362,3905,4363,2260,1951,2470,7482,1125, 817,4110, # 4310 +4111,3906,1513,1766,2040,1487,4112,3030,3258,2824,3761,3127,7483,7484,1507,7485, # 4326 +2683, 733, 40,1632,1106,2865, 345,4113, 841,2524, 230,4364,2984,1846,3259,3428, # 4342 +7486,1263, 986,3429,7487, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562,3907, # 4358 +3908,2939, 967,2751,2655,1349, 592,2133,1692,3324,2985,1994,4114,1679,3909,1901, # 4374 +2185,7488, 739,3642,2715,1296,1290,7489,4115,2198,2199,1921,1563,2595,2551,1870, # 4390 +2752,2986,7490, 435,7491, 343,1108, 596, 17,1751,4365,2235,3430,3643,7492,4366, # 4406 + 294,3527,2940,1693, 477, 979, 281,2041,3528, 643,2042,3644,2621,2782,2261,1031, # 4422 +2335,2134,2298,3529,4367, 367,1249,2552,7493,3530,7494,4368,1283,3325,2004, 240, # 4438 +1762,3326,4369,4370, 836,1069,3128, 474,7495,2148,2525, 268,3531,7496,3188,1521, # 4454 +1284,7497,1658,1546,4116,7498,3532,3533,7499,4117,3327,2684,1685,4118, 961,1673, # 4470 +2622, 190,2005,2200,3762,4371,4372,7500, 570,2497,3645,1490,7501,4373,2623,3260, # 4486 +1956,4374, 584,1514, 396,1045,1944,7502,4375,1967,2444,7503,7504,4376,3910, 619, # 4502 +7505,3129,3261, 215,2006,2783,2553,3189,4377,3190,4378, 763,4119,3763,4379,7506, # 4518 +7507,1957,1767,2941,3328,3646,1174, 452,1477,4380,3329,3130,7508,2825,1253,2382, # 4534 +2186,1091,2285,4120, 492,7509, 638,1169,1824,2135,1752,3911, 648, 926,1021,1324, # 4550 +4381, 520,4382, 997, 847,1007, 892,4383,3764,2262,1871,3647,7510,2400,1784,4384, # 4566 +1952,2942,3080,3191,1728,4121,2043,3648,4385,2007,1701,3131,1551, 30,2263,4122, # 4582 +7511,2026,4386,3534,7512, 501,7513,4123, 594,3431,2165,1821,3535,3432,3536,3192, # 4598 + 829,2826,4124,7514,1680,3132,1225,4125,7515,3262,4387,4126,3133,2336,7516,4388, # 4614 +4127,7517,3912,3913,7518,1847,2383,2596,3330,7519,4389, 374,3914, 652,4128,4129, # 4630 + 375,1140, 798,7520,7521,7522,2361,4390,2264, 546,1659, 138,3031,2445,4391,7523, # 4646 +2250, 612,1848, 910, 796,3765,1740,1371, 825,3766,3767,7524,2906,2554,7525, 692, # 4662 + 444,3032,2624, 801,4392,4130,7526,1491, 244,1053,3033,4131,4132, 340,7527,3915, # 4678 +1041,2987, 293,1168, 87,1357,7528,1539, 959,7529,2236, 721, 694,4133,3768, 219, # 4694 +1478, 644,1417,3331,2656,1413,1401,1335,1389,3916,7530,7531,2988,2362,3134,1825, # 4710 + 730,1515, 184,2827, 66,4393,7532,1660,2943, 246,3332, 378,1457, 226,3433, 975, # 4726 +3917,2944,1264,3537, 674, 696,7533, 163,7534,1141,2417,2166, 713,3538,3333,4394, # 4742 +3918,7535,7536,1186, 15,7537,1079,1070,7538,1522,3193,3539, 276,1050,2716, 758, # 4758 +1126, 653,2945,3263,7539,2337, 889,3540,3919,3081,2989, 903,1250,4395,3920,3434, # 4774 +3541,1342,1681,1718, 766,3264, 286, 89,2946,3649,7540,1713,7541,2597,3334,2990, # 4790 +7542,2947,2215,3194,2866,7543,4396,2498,2526, 181, 387,1075,3921, 731,2187,3335, # 4806 +7544,3265, 310, 313,3435,2299, 770,4134, 54,3034, 189,4397,3082,3769,3922,7545, # 4822 +1230,1617,1849, 355,3542,4135,4398,3336, 111,4136,3650,1350,3135,3436,3035,4137, # 4838 +2149,3266,3543,7546,2784,3923,3924,2991, 722,2008,7547,1071, 247,1207,2338,2471, # 4854 +1378,4399,2009, 864,1437,1214,4400, 373,3770,1142,2216, 667,4401, 442,2753,2555, # 4870 +3771,3925,1968,4138,3267,1839, 837, 170,1107, 934,1336,1882,7548,7549,2118,4139, # 4886 +2828, 743,1569,7550,4402,4140, 582,2384,1418,3437,7551,1802,7552, 357,1395,1729, # 4902 +3651,3268,2418,1564,2237,7553,3083,3772,1633,4403,1114,2085,4141,1532,7554, 482, # 4918 +2446,4404,7555,7556,1492, 833,1466,7557,2717,3544,1641,2829,7558,1526,1272,3652, # 4934 +4142,1686,1794, 416,2556,1902,1953,1803,7559,3773,2785,3774,1159,2316,7560,2867, # 4950 +4405,1610,1584,3036,2419,2754, 443,3269,1163,3136,7561,7562,3926,7563,4143,2499, # 4966 +3037,4406,3927,3137,2103,1647,3545,2010,1872,4144,7564,4145, 431,3438,7565, 250, # 4982 + 97, 81,4146,7566,1648,1850,1558, 160, 848,7567, 866, 740,1694,7568,2201,2830, # 4998 +3195,4147,4407,3653,1687, 950,2472, 426, 469,3196,3654,3655,3928,7569,7570,1188, # 5014 + 424,1995, 861,3546,4148,3775,2202,2685, 168,1235,3547,4149,7571,2086,1674,4408, # 5030 +3337,3270, 220,2557,1009,7572,3776, 670,2992, 332,1208, 717,7573,7574,3548,2447, # 5046 +3929,3338,7575, 513,7576,1209,2868,3339,3138,4409,1080,7577,7578,7579,7580,2527, # 5062 +3656,3549, 815,1587,3930,3931,7581,3550,3439,3777,1254,4410,1328,3038,1390,3932, # 5078 +1741,3933,3778,3934,7582, 236,3779,2448,3271,7583,7584,3657,3780,1273,3781,4411, # 5094 +7585, 308,7586,4412, 245,4413,1851,2473,1307,2575, 430, 715,2136,2449,7587, 270, # 5110 + 199,2869,3935,7588,3551,2718,1753, 761,1754, 725,1661,1840,4414,3440,3658,7589, # 5126 +7590, 587, 14,3272, 227,2598, 326, 480,2265, 943,2755,3552, 291, 650,1883,7591, # 5142 +1702,1226, 102,1547, 62,3441, 904,4415,3442,1164,4150,7592,7593,1224,1548,2756, # 5158 + 391, 498,1493,7594,1386,1419,7595,2055,1177,4416, 813, 880,1081,2363, 566,1145, # 5174 +4417,2286,1001,1035,2558,2599,2238, 394,1286,7596,7597,2068,7598, 86,1494,1730, # 5190 +3936, 491,1588, 745, 897,2948, 843,3340,3937,2757,2870,3273,1768, 998,2217,2069, # 5206 + 397,1826,1195,1969,3659,2993,3341, 284,7599,3782,2500,2137,2119,1903,7600,3938, # 5222 +2150,3939,4151,1036,3443,1904, 114,2559,4152, 209,1527,7601,7602,2949,2831,2625, # 5238 +2385,2719,3139, 812,2560,7603,3274,7604,1559, 737,1884,3660,1210, 885, 28,2686, # 5254 +3553,3783,7605,4153,1004,1779,4418,7606, 346,1981,2218,2687,4419,3784,1742, 797, # 5270 +1642,3940,1933,1072,1384,2151, 896,3941,3275,3661,3197,2871,3554,7607,2561,1958, # 5286 +4420,2450,1785,7608,7609,7610,3942,4154,1005,1308,3662,4155,2720,4421,4422,1528, # 5302 +2600, 161,1178,4156,1982, 987,4423,1101,4157, 631,3943,1157,3198,2420,1343,1241, # 5318 +1016,2239,2562, 372, 877,2339,2501,1160, 555,1934, 911,3944,7611, 466,1170, 169, # 5334 +1051,2907,2688,3663,2474,2994,1182,2011,2563,1251,2626,7612, 992,2340,3444,1540, # 5350 +2721,1201,2070,2401,1996,2475,7613,4424, 528,1922,2188,1503,1873,1570,2364,3342, # 5366 +3276,7614, 557,1073,7615,1827,3445,2087,2266,3140,3039,3084, 767,3085,2786,4425, # 5382 +1006,4158,4426,2341,1267,2176,3664,3199, 778,3945,3200,2722,1597,2657,7616,4427, # 5398 +7617,3446,7618,7619,7620,3277,2689,1433,3278, 131, 95,1504,3946, 723,4159,3141, # 5414 +1841,3555,2758,2189,3947,2027,2104,3665,7621,2995,3948,1218,7622,3343,3201,3949, # 5430 +4160,2576, 248,1634,3785, 912,7623,2832,3666,3040,3786, 654, 53,7624,2996,7625, # 5446 +1688,4428, 777,3447,1032,3950,1425,7626, 191, 820,2120,2833, 971,4429, 931,3202, # 5462 + 135, 664, 783,3787,1997, 772,2908,1935,3951,3788,4430,2909,3203, 282,2723, 640, # 5478 +1372,3448,1127, 922, 325,3344,7627,7628, 711,2044,7629,7630,3952,2219,2787,1936, # 5494 +3953,3345,2220,2251,3789,2300,7631,4431,3790,1258,3279,3954,3204,2138,2950,3955, # 5510 +3956,7632,2221, 258,3205,4432, 101,1227,7633,3280,1755,7634,1391,3281,7635,2910, # 5526 +2056, 893,7636,7637,7638,1402,4161,2342,7639,7640,3206,3556,7641,7642, 878,1325, # 5542 +1780,2788,4433, 259,1385,2577, 744,1183,2267,4434,7643,3957,2502,7644, 684,1024, # 5558 +4162,7645, 472,3557,3449,1165,3282,3958,3959, 322,2152, 881, 455,1695,1152,1340, # 5574 + 660, 554,2153,4435,1058,4436,4163, 830,1065,3346,3960,4437,1923,7646,1703,1918, # 5590 +7647, 932,2268, 122,7648,4438, 947, 677,7649,3791,2627, 297,1905,1924,2269,4439, # 5606 +2317,3283,7650,7651,4164,7652,4165, 84,4166, 112, 989,7653, 547,1059,3961, 701, # 5622 +3558,1019,7654,4167,7655,3450, 942, 639, 457,2301,2451, 993,2951, 407, 851, 494, # 5638 +4440,3347, 927,7656,1237,7657,2421,3348, 573,4168, 680, 921,2911,1279,1874, 285, # 5654 + 790,1448,1983, 719,2167,7658,7659,4441,3962,3963,1649,7660,1541, 563,7661,1077, # 5670 +7662,3349,3041,3451, 511,2997,3964,3965,3667,3966,1268,2564,3350,3207,4442,4443, # 5686 +7663, 535,1048,1276,1189,2912,2028,3142,1438,1373,2834,2952,1134,2012,7664,4169, # 5702 +1238,2578,3086,1259,7665, 700,7666,2953,3143,3668,4170,7667,4171,1146,1875,1906, # 5718 +4444,2601,3967, 781,2422, 132,1589, 203, 147, 273,2789,2402, 898,1786,2154,3968, # 5734 +3969,7668,3792,2790,7669,7670,4445,4446,7671,3208,7672,1635,3793, 965,7673,1804, # 5750 +2690,1516,3559,1121,1082,1329,3284,3970,1449,3794, 65,1128,2835,2913,2759,1590, # 5766 +3795,7674,7675, 12,2658, 45, 976,2579,3144,4447, 517,2528,1013,1037,3209,7676, # 5782 +3796,2836,7677,3797,7678,3452,7679,2602, 614,1998,2318,3798,3087,2724,2628,7680, # 5798 +2580,4172, 599,1269,7681,1810,3669,7682,2691,3088, 759,1060, 489,1805,3351,3285, # 5814 +1358,7683,7684,2386,1387,1215,2629,2252, 490,7685,7686,4173,1759,2387,2343,7687, # 5830 +4448,3799,1907,3971,2630,1806,3210,4449,3453,3286,2760,2344, 874,7688,7689,3454, # 5846 +3670,1858, 91,2914,3671,3042,3800,4450,7690,3145,3972,2659,7691,3455,1202,1403, # 5862 +3801,2954,2529,1517,2503,4451,3456,2504,7692,4452,7693,2692,1885,1495,1731,3973, # 5878 +2365,4453,7694,2029,7695,7696,3974,2693,1216, 237,2581,4174,2319,3975,3802,4454, # 5894 +4455,2694,3560,3457, 445,4456,7697,7698,7699,7700,2761, 61,3976,3672,1822,3977, # 5910 +7701, 687,2045, 935, 925, 405,2660, 703,1096,1859,2725,4457,3978,1876,1367,2695, # 5926 +3352, 918,2105,1781,2476, 334,3287,1611,1093,4458, 564,3146,3458,3673,3353, 945, # 5942 +2631,2057,4459,7702,1925, 872,4175,7703,3459,2696,3089, 349,4176,3674,3979,4460, # 5958 +3803,4177,3675,2155,3980,4461,4462,4178,4463,2403,2046, 782,3981, 400, 251,4179, # 5974 +1624,7704,7705, 277,3676, 299,1265, 476,1191,3804,2121,4180,4181,1109, 205,7706, # 5990 +2582,1000,2156,3561,1860,7707,7708,7709,4464,7710,4465,2565, 107,2477,2157,3982, # 6006 +3460,3147,7711,1533, 541,1301, 158, 753,4182,2872,3562,7712,1696, 370,1088,4183, # 6022 +4466,3563, 579, 327, 440, 162,2240, 269,1937,1374,3461, 968,3043, 56,1396,3090, # 6038 +2106,3288,3354,7713,1926,2158,4467,2998,7714,3564,7715,7716,3677,4468,2478,7717, # 6054 +2791,7718,1650,4469,7719,2603,7720,7721,3983,2661,3355,1149,3356,3984,3805,3985, # 6070 +7722,1076, 49,7723, 951,3211,3289,3290, 450,2837, 920,7724,1811,2792,2366,4184, # 6086 +1908,1138,2367,3806,3462,7725,3212,4470,1909,1147,1518,2423,4471,3807,7726,4472, # 6102 +2388,2604, 260,1795,3213,7727,7728,3808,3291, 708,7729,3565,1704,7730,3566,1351, # 6118 +1618,3357,2999,1886, 944,4185,3358,4186,3044,3359,4187,7731,3678, 422, 413,1714, # 6134 +3292, 500,2058,2345,4188,2479,7732,1344,1910, 954,7733,1668,7734,7735,3986,2404, # 6150 +4189,3567,3809,4190,7736,2302,1318,2505,3091, 133,3092,2873,4473, 629, 31,2838, # 6166 +2697,3810,4474, 850, 949,4475,3987,2955,1732,2088,4191,1496,1852,7737,3988, 620, # 6182 +3214, 981,1242,3679,3360,1619,3680,1643,3293,2139,2452,1970,1719,3463,2168,7738, # 6198 +3215,7739,7740,3361,1828,7741,1277,4476,1565,2047,7742,1636,3568,3093,7743, 869, # 6214 +2839, 655,3811,3812,3094,3989,3000,3813,1310,3569,4477,7744,7745,7746,1733, 558, # 6230 +4478,3681, 335,1549,3045,1756,4192,3682,1945,3464,1829,1291,1192, 470,2726,2107, # 6246 +2793, 913,1054,3990,7747,1027,7748,3046,3991,4479, 982,2662,3362,3148,3465,3216, # 6262 +3217,1946,2794,7749, 571,4480,7750,1830,7751,3570,2583,1523,2424,7752,2089, 984, # 6278 +4481,3683,1959,7753,3684, 852, 923,2795,3466,3685, 969,1519, 999,2048,2320,1705, # 6294 +7754,3095, 615,1662, 151, 597,3992,2405,2321,1049, 275,4482,3686,4193, 568,3687, # 6310 +3571,2480,4194,3688,7755,2425,2270, 409,3218,7756,1566,2874,3467,1002, 769,2840, # 6326 + 194,2090,3149,3689,2222,3294,4195, 628,1505,7757,7758,1763,2177,3001,3993, 521, # 6342 +1161,2584,1787,2203,2406,4483,3994,1625,4196,4197, 412, 42,3096, 464,7759,2632, # 6358 +4484,3363,1760,1571,2875,3468,2530,1219,2204,3814,2633,2140,2368,4485,4486,3295, # 6374 +1651,3364,3572,7760,7761,3573,2481,3469,7762,3690,7763,7764,2271,2091, 460,7765, # 6390 +4487,7766,3002, 962, 588,3574, 289,3219,2634,1116, 52,7767,3047,1796,7768,7769, # 6406 +7770,1467,7771,1598,1143,3691,4198,1984,1734,1067,4488,1280,3365, 465,4489,1572, # 6422 + 510,7772,1927,2241,1812,1644,3575,7773,4490,3692,7774,7775,2663,1573,1534,7776, # 6438 +7777,4199, 536,1807,1761,3470,3815,3150,2635,7778,7779,7780,4491,3471,2915,1911, # 6454 +2796,7781,3296,1122, 377,3220,7782, 360,7783,7784,4200,1529, 551,7785,2059,3693, # 6470 +1769,2426,7786,2916,4201,3297,3097,2322,2108,2030,4492,1404, 136,1468,1479, 672, # 6486 +1171,3221,2303, 271,3151,7787,2762,7788,2049, 678,2727, 865,1947,4493,7789,2013, # 6502 +3995,2956,7790,2728,2223,1397,3048,3694,4494,4495,1735,2917,3366,3576,7791,3816, # 6518 + 509,2841,2453,2876,3817,7792,7793,3152,3153,4496,4202,2531,4497,2304,1166,1010, # 6534 + 552, 681,1887,7794,7795,2957,2958,3996,1287,1596,1861,3154, 358, 453, 736, 175, # 6550 + 478,1117, 905,1167,1097,7796,1853,1530,7797,1706,7798,2178,3472,2287,3695,3473, # 6566 +3577,4203,2092,4204,7799,3367,1193,2482,4205,1458,2190,2205,1862,1888,1421,3298, # 6582 +2918,3049,2179,3474, 595,2122,7800,3997,7801,7802,4206,1707,2636, 223,3696,1359, # 6598 + 751,3098, 183,3475,7803,2797,3003, 419,2369, 633, 704,3818,2389, 241,7804,7805, # 6614 +7806, 838,3004,3697,2272,2763,2454,3819,1938,2050,3998,1309,3099,2242,1181,7807, # 6630 +1136,2206,3820,2370,1446,4207,2305,4498,7808,7809,4208,1055,2605, 484,3698,7810, # 6646 +3999, 625,4209,2273,3368,1499,4210,4000,7811,4001,4211,3222,2274,2275,3476,7812, # 6662 +7813,2764, 808,2606,3699,3369,4002,4212,3100,2532, 526,3370,3821,4213, 955,7814, # 6678 +1620,4214,2637,2427,7815,1429,3700,1669,1831, 994, 928,7816,3578,1260,7817,7818, # 6694 +7819,1948,2288, 741,2919,1626,4215,2729,2455, 867,1184, 362,3371,1392,7820,7821, # 6710 +4003,4216,1770,1736,3223,2920,4499,4500,1928,2698,1459,1158,7822,3050,3372,2877, # 6726 +1292,1929,2506,2842,3701,1985,1187,2071,2014,2607,4217,7823,2566,2507,2169,3702, # 6742 +2483,3299,7824,3703,4501,7825,7826, 666,1003,3005,1022,3579,4218,7827,4502,1813, # 6758 +2253, 574,3822,1603, 295,1535, 705,3823,4219, 283, 858, 417,7828,7829,3224,4503, # 6774 +4504,3051,1220,1889,1046,2276,2456,4004,1393,1599, 689,2567, 388,4220,7830,2484, # 6790 + 802,7831,2798,3824,2060,1405,2254,7832,4505,3825,2109,1052,1345,3225,1585,7833, # 6806 + 809,7834,7835,7836, 575,2730,3477, 956,1552,1469,1144,2323,7837,2324,1560,2457, # 6822 +3580,3226,4005, 616,2207,3155,2180,2289,7838,1832,7839,3478,4506,7840,1319,3704, # 6838 +3705,1211,3581,1023,3227,1293,2799,7841,7842,7843,3826, 607,2306,3827, 762,2878, # 6854 +1439,4221,1360,7844,1485,3052,7845,4507,1038,4222,1450,2061,2638,4223,1379,4508, # 6870 +2585,7846,7847,4224,1352,1414,2325,2921,1172,7848,7849,3828,3829,7850,1797,1451, # 6886 +7851,7852,7853,7854,2922,4006,4007,2485,2346, 411,4008,4009,3582,3300,3101,4509, # 6902 +1561,2664,1452,4010,1375,7855,7856, 47,2959, 316,7857,1406,1591,2923,3156,7858, # 6918 +1025,2141,3102,3157, 354,2731, 884,2224,4225,2407, 508,3706, 726,3583, 996,2428, # 6934 +3584, 729,7859, 392,2191,1453,4011,4510,3707,7860,7861,2458,3585,2608,1675,2800, # 6950 + 919,2347,2960,2348,1270,4511,4012, 73,7862,7863, 647,7864,3228,2843,2255,1550, # 6966 +1346,3006,7865,1332, 883,3479,7866,7867,7868,7869,3301,2765,7870,1212, 831,1347, # 6982 +4226,4512,2326,3830,1863,3053, 720,3831,4513,4514,3832,7871,4227,7872,7873,4515, # 6998 +7874,7875,1798,4516,3708,2609,4517,3586,1645,2371,7876,7877,2924, 669,2208,2665, # 7014 +2429,7878,2879,7879,7880,1028,3229,7881,4228,2408,7882,2256,1353,7883,7884,4518, # 7030 +3158, 518,7885,4013,7886,4229,1960,7887,2142,4230,7888,7889,3007,2349,2350,3833, # 7046 + 516,1833,1454,4014,2699,4231,4519,2225,2610,1971,1129,3587,7890,2766,7891,2961, # 7062 +1422, 577,1470,3008,1524,3373,7892,7893, 432,4232,3054,3480,7894,2586,1455,2508, # 7078 +2226,1972,1175,7895,1020,2732,4015,3481,4520,7896,2733,7897,1743,1361,3055,3482, # 7094 +2639,4016,4233,4521,2290, 895, 924,4234,2170, 331,2243,3056, 166,1627,3057,1098, # 7110 +7898,1232,2880,2227,3374,4522, 657, 403,1196,2372, 542,3709,3375,1600,4235,3483, # 7126 +7899,4523,2767,3230, 576, 530,1362,7900,4524,2533,2666,3710,4017,7901, 842,3834, # 7142 +7902,2801,2031,1014,4018, 213,2700,3376, 665, 621,4236,7903,3711,2925,2430,7904, # 7158 +2431,3302,3588,3377,7905,4237,2534,4238,4525,3589,1682,4239,3484,1380,7906, 724, # 7174 +2277, 600,1670,7907,1337,1233,4526,3103,2244,7908,1621,4527,7909, 651,4240,7910, # 7190 +1612,4241,2611,7911,2844,7912,2734,2307,3058,7913, 716,2459,3059, 174,1255,2701, # 7206 +4019,3590, 548,1320,1398, 728,4020,1574,7914,1890,1197,3060,4021,7915,3061,3062, # 7222 +3712,3591,3713, 747,7916, 635,4242,4528,7917,7918,7919,4243,7920,7921,4529,7922, # 7238 +3378,4530,2432, 451,7923,3714,2535,2072,4244,2735,4245,4022,7924,1764,4531,7925, # 7254 +4246, 350,7926,2278,2390,2486,7927,4247,4023,2245,1434,4024, 488,4532, 458,4248, # 7270 +4025,3715, 771,1330,2391,3835,2568,3159,2159,2409,1553,2667,3160,4249,7928,2487, # 7286 +2881,2612,1720,2702,4250,3379,4533,7929,2536,4251,7930,3231,4252,2768,7931,2015, # 7302 +2736,7932,1155,1017,3716,3836,7933,3303,2308, 201,1864,4253,1430,7934,4026,7935, # 7318 +7936,7937,7938,7939,4254,1604,7940, 414,1865, 371,2587,4534,4535,3485,2016,3104, # 7334 +4536,1708, 960,4255, 887, 389,2171,1536,1663,1721,7941,2228,4027,2351,2926,1580, # 7350 +7942,7943,7944,1744,7945,2537,4537,4538,7946,4539,7947,2073,7948,7949,3592,3380, # 7366 +2882,4256,7950,4257,2640,3381,2802, 673,2703,2460, 709,3486,4028,3593,4258,7951, # 7382 +1148, 502, 634,7952,7953,1204,4540,3594,1575,4541,2613,3717,7954,3718,3105, 948, # 7398 +3232, 121,1745,3837,1110,7955,4259,3063,2509,3009,4029,3719,1151,1771,3838,1488, # 7414 +4030,1986,7956,2433,3487,7957,7958,2093,7959,4260,3839,1213,1407,2803, 531,2737, # 7430 +2538,3233,1011,1537,7960,2769,4261,3106,1061,7961,3720,3721,1866,2883,7962,2017, # 7446 + 120,4262,4263,2062,3595,3234,2309,3840,2668,3382,1954,4542,7963,7964,3488,1047, # 7462 +2704,1266,7965,1368,4543,2845, 649,3383,3841,2539,2738,1102,2846,2669,7966,7967, # 7478 +1999,7968,1111,3596,2962,7969,2488,3842,3597,2804,1854,3384,3722,7970,7971,3385, # 7494 +2410,2884,3304,3235,3598,7972,2569,7973,3599,2805,4031,1460, 856,7974,3600,7975, # 7510 +2885,2963,7976,2886,3843,7977,4264, 632,2510, 875,3844,1697,3845,2291,7978,7979, # 7526 +4544,3010,1239, 580,4545,4265,7980, 914, 936,2074,1190,4032,1039,2123,7981,7982, # 7542 +7983,3386,1473,7984,1354,4266,3846,7985,2172,3064,4033, 915,3305,4267,4268,3306, # 7558 +1605,1834,7986,2739, 398,3601,4269,3847,4034, 328,1912,2847,4035,3848,1331,4270, # 7574 +3011, 937,4271,7987,3602,4036,4037,3387,2160,4546,3388, 524, 742, 538,3065,1012, # 7590 +7988,7989,3849,2461,7990, 658,1103, 225,3850,7991,7992,4547,7993,4548,7994,3236, # 7606 +1243,7995,4038, 963,2246,4549,7996,2705,3603,3161,7997,7998,2588,2327,7999,4550, # 7622 +8000,8001,8002,3489,3307, 957,3389,2540,2032,1930,2927,2462, 870,2018,3604,1746, # 7638 +2770,2771,2434,2463,8003,3851,8004,3723,3107,3724,3490,3390,3725,8005,1179,3066, # 7654 +8006,3162,2373,4272,3726,2541,3163,3108,2740,4039,8007,3391,1556,2542,2292, 977, # 7670 +2887,2033,4040,1205,3392,8008,1765,3393,3164,2124,1271,1689, 714,4551,3491,8009, # 7686 +2328,3852, 533,4273,3605,2181, 617,8010,2464,3308,3492,2310,8011,8012,3165,8013, # 7702 +8014,3853,1987, 618, 427,2641,3493,3394,8015,8016,1244,1690,8017,2806,4274,4552, # 7718 +8018,3494,8019,8020,2279,1576, 473,3606,4275,3395, 972,8021,3607,8022,3067,8023, # 7734 +8024,4553,4554,8025,3727,4041,4042,8026, 153,4555, 356,8027,1891,2888,4276,2143, # 7750 + 408, 803,2352,8028,3854,8029,4277,1646,2570,2511,4556,4557,3855,8030,3856,4278, # 7766 +8031,2411,3396, 752,8032,8033,1961,2964,8034, 746,3012,2465,8035,4279,3728, 698, # 7782 +4558,1892,4280,3608,2543,4559,3609,3857,8036,3166,3397,8037,1823,1302,4043,2706, # 7798 +3858,1973,4281,8038,4282,3167, 823,1303,1288,1236,2848,3495,4044,3398, 774,3859, # 7814 +8039,1581,4560,1304,2849,3860,4561,8040,2435,2161,1083,3237,4283,4045,4284, 344, # 7830 +1173, 288,2311, 454,1683,8041,8042,1461,4562,4046,2589,8043,8044,4563, 985, 894, # 7846 +8045,3399,3168,8046,1913,2928,3729,1988,8047,2110,1974,8048,4047,8049,2571,1194, # 7862 + 425,8050,4564,3169,1245,3730,4285,8051,8052,2850,8053, 636,4565,1855,3861, 760, # 7878 +1799,8054,4286,2209,1508,4566,4048,1893,1684,2293,8055,8056,8057,4287,4288,2210, # 7894 + 479,8058,8059, 832,8060,4049,2489,8061,2965,2490,3731, 990,3109, 627,1814,2642, # 7910 +4289,1582,4290,2125,2111,3496,4567,8062, 799,4291,3170,8063,4568,2112,1737,3013, # 7926 +1018, 543, 754,4292,3309,1676,4569,4570,4050,8064,1489,8065,3497,8066,2614,2889, # 7942 +4051,8067,8068,2966,8069,8070,8071,8072,3171,4571,4572,2182,1722,8073,3238,3239, # 7958 +1842,3610,1715, 481, 365,1975,1856,8074,8075,1962,2491,4573,8076,2126,3611,3240, # 7974 + 433,1894,2063,2075,8077, 602,2741,8078,8079,8080,8081,8082,3014,1628,3400,8083, # 7990 +3172,4574,4052,2890,4575,2512,8084,2544,2772,8085,8086,8087,3310,4576,2891,8088, # 8006 +4577,8089,2851,4578,4579,1221,2967,4053,2513,8090,8091,8092,1867,1989,8093,8094, # 8022 +8095,1895,8096,8097,4580,1896,4054, 318,8098,2094,4055,4293,8099,8100, 485,8101, # 8038 + 938,3862, 553,2670, 116,8102,3863,3612,8103,3498,2671,2773,3401,3311,2807,8104, # 8054 +3613,2929,4056,1747,2930,2968,8105,8106, 207,8107,8108,2672,4581,2514,8109,3015, # 8070 + 890,3614,3864,8110,1877,3732,3402,8111,2183,2353,3403,1652,8112,8113,8114, 941, # 8086 +2294, 208,3499,4057,2019, 330,4294,3865,2892,2492,3733,4295,8115,8116,8117,8118, # 8102 +) + diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/euctwprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/euctwprober.py new file mode 100644 index 0000000..35669cc --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/euctwprober.py @@ -0,0 +1,46 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import EUCTWDistributionAnalysis +from .mbcssm import EUCTW_SM_MODEL + +class EUCTWProber(MultiByteCharSetProber): + def __init__(self): + super(EUCTWProber, self).__init__() + self.coding_sm = CodingStateMachine(EUCTW_SM_MODEL) + self.distribution_analyzer = EUCTWDistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "EUC-TW" + + @property + def language(self): + return "Taiwan" diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/gb2312freq.py b/venv/Lib/site-packages/pip/_vendor/chardet/gb2312freq.py new file mode 100644 index 0000000..697837b --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/gb2312freq.py @@ -0,0 +1,283 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# GB2312 most frequently used character table +# +# Char to FreqOrder table , from hz6763 + +# 512 --> 0.79 -- 0.79 +# 1024 --> 0.92 -- 0.13 +# 2048 --> 0.98 -- 0.06 +# 6768 --> 1.00 -- 0.02 +# +# Ideal Distribution Ratio = 0.79135/(1-0.79135) = 3.79 +# Random Distribution Ration = 512 / (3755 - 512) = 0.157 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher that RDR + +GB2312_TYPICAL_DISTRIBUTION_RATIO = 0.9 + +GB2312_TABLE_SIZE = 3760 + +GB2312_CHAR_TO_FREQ_ORDER = ( +1671, 749,1443,2364,3924,3807,2330,3921,1704,3463,2691,1511,1515, 572,3191,2205, +2361, 224,2558, 479,1711, 963,3162, 440,4060,1905,2966,2947,3580,2647,3961,3842, +2204, 869,4207, 970,2678,5626,2944,2956,1479,4048, 514,3595, 588,1346,2820,3409, + 249,4088,1746,1873,2047,1774, 581,1813, 358,1174,3590,1014,1561,4844,2245, 670, +1636,3112, 889,1286, 953, 556,2327,3060,1290,3141, 613, 185,3477,1367, 850,3820, +1715,2428,2642,2303,2732,3041,2562,2648,3566,3946,1349, 388,3098,2091,1360,3585, + 152,1687,1539, 738,1559, 59,1232,2925,2267,1388,1249,1741,1679,2960, 151,1566, +1125,1352,4271, 924,4296, 385,3166,4459, 310,1245,2850, 70,3285,2729,3534,3575, +2398,3298,3466,1960,2265, 217,3647, 864,1909,2084,4401,2773,1010,3269,5152, 853, +3051,3121,1244,4251,1895, 364,1499,1540,2313,1180,3655,2268, 562, 715,2417,3061, + 544, 336,3768,2380,1752,4075, 950, 280,2425,4382, 183,2759,3272, 333,4297,2155, +1688,2356,1444,1039,4540, 736,1177,3349,2443,2368,2144,2225, 565, 196,1482,3406, + 927,1335,4147, 692, 878,1311,1653,3911,3622,1378,4200,1840,2969,3149,2126,1816, +2534,1546,2393,2760, 737,2494, 13, 447, 245,2747, 38,2765,2129,2589,1079, 606, + 360, 471,3755,2890, 404, 848, 699,1785,1236, 370,2221,1023,3746,2074,2026,2023, +2388,1581,2119, 812,1141,3091,2536,1519, 804,2053, 406,1596,1090, 784, 548,4414, +1806,2264,2936,1100, 343,4114,5096, 622,3358, 743,3668,1510,1626,5020,3567,2513, +3195,4115,5627,2489,2991, 24,2065,2697,1087,2719, 48,1634, 315, 68, 985,2052, + 198,2239,1347,1107,1439, 597,2366,2172, 871,3307, 919,2487,2790,1867, 236,2570, +1413,3794, 906,3365,3381,1701,1982,1818,1524,2924,1205, 616,2586,2072,2004, 575, + 253,3099, 32,1365,1182, 197,1714,2454,1201, 554,3388,3224,2748, 756,2587, 250, +2567,1507,1517,3529,1922,2761,2337,3416,1961,1677,2452,2238,3153, 615, 911,1506, +1474,2495,1265,1906,2749,3756,3280,2161, 898,2714,1759,3450,2243,2444, 563, 26, +3286,2266,3769,3344,2707,3677, 611,1402, 531,1028,2871,4548,1375, 261,2948, 835, +1190,4134, 353, 840,2684,1900,3082,1435,2109,1207,1674, 329,1872,2781,4055,2686, +2104, 608,3318,2423,2957,2768,1108,3739,3512,3271,3985,2203,1771,3520,1418,2054, +1681,1153, 225,1627,2929, 162,2050,2511,3687,1954, 124,1859,2431,1684,3032,2894, + 585,4805,3969,2869,2704,2088,2032,2095,3656,2635,4362,2209, 256, 518,2042,2105, +3777,3657, 643,2298,1148,1779, 190, 989,3544, 414, 11,2135,2063,2979,1471, 403, +3678, 126, 770,1563, 671,2499,3216,2877, 600,1179, 307,2805,4937,1268,1297,2694, + 252,4032,1448,1494,1331,1394, 127,2256, 222,1647,1035,1481,3056,1915,1048, 873, +3651, 210, 33,1608,2516, 200,1520, 415, 102, 0,3389,1287, 817, 91,3299,2940, + 836,1814, 549,2197,1396,1669,2987,3582,2297,2848,4528,1070, 687, 20,1819, 121, +1552,1364,1461,1968,2617,3540,2824,2083, 177, 948,4938,2291, 110,4549,2066, 648, +3359,1755,2110,2114,4642,4845,1693,3937,3308,1257,1869,2123, 208,1804,3159,2992, +2531,2549,3361,2418,1350,2347,2800,2568,1291,2036,2680, 72, 842,1990, 212,1233, +1154,1586, 75,2027,3410,4900,1823,1337,2710,2676, 728,2810,1522,3026,4995, 157, + 755,1050,4022, 710, 785,1936,2194,2085,1406,2777,2400, 150,1250,4049,1206, 807, +1910, 534, 529,3309,1721,1660, 274, 39,2827, 661,2670,1578, 925,3248,3815,1094, +4278,4901,4252, 41,1150,3747,2572,2227,4501,3658,4902,3813,3357,3617,2884,2258, + 887, 538,4187,3199,1294,2439,3042,2329,2343,2497,1255, 107, 543,1527, 521,3478, +3568, 194,5062, 15, 961,3870,1241,1192,2664, 66,5215,3260,2111,1295,1127,2152, +3805,4135, 901,1164,1976, 398,1278, 530,1460, 748, 904,1054,1966,1426, 53,2909, + 509, 523,2279,1534, 536,1019, 239,1685, 460,2353, 673,1065,2401,3600,4298,2272, +1272,2363, 284,1753,3679,4064,1695, 81, 815,2677,2757,2731,1386, 859, 500,4221, +2190,2566, 757,1006,2519,2068,1166,1455, 337,2654,3203,1863,1682,1914,3025,1252, +1409,1366, 847, 714,2834,2038,3209, 964,2970,1901, 885,2553,1078,1756,3049, 301, +1572,3326, 688,2130,1996,2429,1805,1648,2930,3421,2750,3652,3088, 262,1158,1254, + 389,1641,1812, 526,1719, 923,2073,1073,1902, 468, 489,4625,1140, 857,2375,3070, +3319,2863, 380, 116,1328,2693,1161,2244, 273,1212,1884,2769,3011,1775,1142, 461, +3066,1200,2147,2212, 790, 702,2695,4222,1601,1058, 434,2338,5153,3640, 67,2360, +4099,2502, 618,3472,1329, 416,1132, 830,2782,1807,2653,3211,3510,1662, 192,2124, + 296,3979,1739,1611,3684, 23, 118, 324, 446,1239,1225, 293,2520,3814,3795,2535, +3116, 17,1074, 467,2692,2201, 387,2922, 45,1326,3055,1645,3659,2817, 958, 243, +1903,2320,1339,2825,1784,3289, 356, 576, 865,2315,2381,3377,3916,1088,3122,1713, +1655, 935, 628,4689,1034,1327, 441, 800, 720, 894,1979,2183,1528,5289,2702,1071, +4046,3572,2399,1571,3281, 79, 761,1103, 327, 134, 758,1899,1371,1615, 879, 442, + 215,2605,2579, 173,2048,2485,1057,2975,3317,1097,2253,3801,4263,1403,1650,2946, + 814,4968,3487,1548,2644,1567,1285, 2, 295,2636, 97, 946,3576, 832, 141,4257, +3273, 760,3821,3521,3156,2607, 949,1024,1733,1516,1803,1920,2125,2283,2665,3180, +1501,2064,3560,2171,1592, 803,3518,1416, 732,3897,4258,1363,1362,2458, 119,1427, + 602,1525,2608,1605,1639,3175, 694,3064, 10, 465, 76,2000,4846,4208, 444,3781, +1619,3353,2206,1273,3796, 740,2483, 320,1723,2377,3660,2619,1359,1137,1762,1724, +2345,2842,1850,1862, 912, 821,1866, 612,2625,1735,2573,3369,1093, 844, 89, 937, + 930,1424,3564,2413,2972,1004,3046,3019,2011, 711,3171,1452,4178, 428, 801,1943, + 432, 445,2811, 206,4136,1472, 730, 349, 73, 397,2802,2547, 998,1637,1167, 789, + 396,3217, 154,1218, 716,1120,1780,2819,4826,1931,3334,3762,2139,1215,2627, 552, +3664,3628,3232,1405,2383,3111,1356,2652,3577,3320,3101,1703, 640,1045,1370,1246, +4996, 371,1575,2436,1621,2210, 984,4033,1734,2638, 16,4529, 663,2755,3255,1451, +3917,2257,1253,1955,2234,1263,2951, 214,1229, 617, 485, 359,1831,1969, 473,2310, + 750,2058, 165, 80,2864,2419, 361,4344,2416,2479,1134, 796,3726,1266,2943, 860, +2715, 938, 390,2734,1313,1384, 248, 202, 877,1064,2854, 522,3907, 279,1602, 297, +2357, 395,3740, 137,2075, 944,4089,2584,1267,3802, 62,1533,2285, 178, 176, 780, +2440, 201,3707, 590, 478,1560,4354,2117,1075, 30, 74,4643,4004,1635,1441,2745, + 776,2596, 238,1077,1692,1912,2844, 605, 499,1742,3947, 241,3053, 980,1749, 936, +2640,4511,2582, 515,1543,2162,5322,2892,2993, 890,2148,1924, 665,1827,3581,1032, + 968,3163, 339,1044,1896, 270, 583,1791,1720,4367,1194,3488,3669, 43,2523,1657, + 163,2167, 290,1209,1622,3378, 550, 634,2508,2510, 695,2634,2384,2512,1476,1414, + 220,1469,2341,2138,2852,3183,2900,4939,2865,3502,1211,3680, 854,3227,1299,2976, +3172, 186,2998,1459, 443,1067,3251,1495, 321,1932,3054, 909, 753,1410,1828, 436, +2441,1119,1587,3164,2186,1258, 227, 231,1425,1890,3200,3942, 247, 959, 725,5254, +2741, 577,2158,2079, 929, 120, 174, 838,2813, 591,1115, 417,2024, 40,3240,1536, +1037, 291,4151,2354, 632,1298,2406,2500,3535,1825,1846,3451, 205,1171, 345,4238, + 18,1163, 811, 685,2208,1217, 425,1312,1508,1175,4308,2552,1033, 587,1381,3059, +2984,3482, 340,1316,4023,3972, 792,3176, 519, 777,4690, 918, 933,4130,2981,3741, + 90,3360,2911,2200,5184,4550, 609,3079,2030, 272,3379,2736, 363,3881,1130,1447, + 286, 779, 357,1169,3350,3137,1630,1220,2687,2391, 747,1277,3688,2618,2682,2601, +1156,3196,5290,4034,3102,1689,3596,3128, 874, 219,2783, 798, 508,1843,2461, 269, +1658,1776,1392,1913,2983,3287,2866,2159,2372, 829,4076, 46,4253,2873,1889,1894, + 915,1834,1631,2181,2318, 298, 664,2818,3555,2735, 954,3228,3117, 527,3511,2173, + 681,2712,3033,2247,2346,3467,1652, 155,2164,3382, 113,1994, 450, 899, 494, 994, +1237,2958,1875,2336,1926,3727, 545,1577,1550, 633,3473, 204,1305,3072,2410,1956, +2471, 707,2134, 841,2195,2196,2663,3843,1026,4940, 990,3252,4997, 368,1092, 437, +3212,3258,1933,1829, 675,2977,2893, 412, 943,3723,4644,3294,3283,2230,2373,5154, +2389,2241,2661,2323,1404,2524, 593, 787, 677,3008,1275,2059, 438,2709,2609,2240, +2269,2246,1446, 36,1568,1373,3892,1574,2301,1456,3962, 693,2276,5216,2035,1143, +2720,1919,1797,1811,2763,4137,2597,1830,1699,1488,1198,2090, 424,1694, 312,3634, +3390,4179,3335,2252,1214, 561,1059,3243,2295,2561, 975,5155,2321,2751,3772, 472, +1537,3282,3398,1047,2077,2348,2878,1323,3340,3076, 690,2906, 51, 369, 170,3541, +1060,2187,2688,3670,2541,1083,1683, 928,3918, 459, 109,4427, 599,3744,4286, 143, +2101,2730,2490, 82,1588,3036,2121, 281,1860, 477,4035,1238,2812,3020,2716,3312, +1530,2188,2055,1317, 843, 636,1808,1173,3495, 649, 181,1002, 147,3641,1159,2414, +3750,2289,2795, 813,3123,2610,1136,4368, 5,3391,4541,2174, 420, 429,1728, 754, +1228,2115,2219, 347,2223,2733, 735,1518,3003,2355,3134,1764,3948,3329,1888,2424, +1001,1234,1972,3321,3363,1672,1021,1450,1584, 226, 765, 655,2526,3404,3244,2302, +3665, 731, 594,2184, 319,1576, 621, 658,2656,4299,2099,3864,1279,2071,2598,2739, + 795,3086,3699,3908,1707,2352,2402,1382,3136,2475,1465,4847,3496,3865,1085,3004, +2591,1084, 213,2287,1963,3565,2250, 822, 793,4574,3187,1772,1789,3050, 595,1484, +1959,2770,1080,2650, 456, 422,2996, 940,3322,4328,4345,3092,2742, 965,2784, 739, +4124, 952,1358,2498,2949,2565, 332,2698,2378, 660,2260,2473,4194,3856,2919, 535, +1260,2651,1208,1428,1300,1949,1303,2942, 433,2455,2450,1251,1946, 614,1269, 641, +1306,1810,2737,3078,2912, 564,2365,1419,1415,1497,4460,2367,2185,1379,3005,1307, +3218,2175,1897,3063, 682,1157,4040,4005,1712,1160,1941,1399, 394, 402,2952,1573, +1151,2986,2404, 862, 299,2033,1489,3006, 346, 171,2886,3401,1726,2932, 168,2533, + 47,2507,1030,3735,1145,3370,1395,1318,1579,3609,4560,2857,4116,1457,2529,1965, + 504,1036,2690,2988,2405, 745,5871, 849,2397,2056,3081, 863,2359,3857,2096, 99, +1397,1769,2300,4428,1643,3455,1978,1757,3718,1440, 35,4879,3742,1296,4228,2280, + 160,5063,1599,2013, 166, 520,3479,1646,3345,3012, 490,1937,1545,1264,2182,2505, +1096,1188,1369,1436,2421,1667,2792,2460,1270,2122, 727,3167,2143, 806,1706,1012, +1800,3037, 960,2218,1882, 805, 139,2456,1139,1521, 851,1052,3093,3089, 342,2039, + 744,5097,1468,1502,1585,2087, 223, 939, 326,2140,2577, 892,2481,1623,4077, 982, +3708, 135,2131, 87,2503,3114,2326,1106, 876,1616, 547,2997,2831,2093,3441,4530, +4314, 9,3256,4229,4148, 659,1462,1986,1710,2046,2913,2231,4090,4880,5255,3392, +3274,1368,3689,4645,1477, 705,3384,3635,1068,1529,2941,1458,3782,1509, 100,1656, +2548, 718,2339, 408,1590,2780,3548,1838,4117,3719,1345,3530, 717,3442,2778,3220, +2898,1892,4590,3614,3371,2043,1998,1224,3483, 891, 635, 584,2559,3355, 733,1766, +1729,1172,3789,1891,2307, 781,2982,2271,1957,1580,5773,2633,2005,4195,3097,1535, +3213,1189,1934,5693,3262, 586,3118,1324,1598, 517,1564,2217,1868,1893,4445,3728, +2703,3139,1526,1787,1992,3882,2875,1549,1199,1056,2224,1904,2711,5098,4287, 338, +1993,3129,3489,2689,1809,2815,1997, 957,1855,3898,2550,3275,3057,1105,1319, 627, +1505,1911,1883,3526, 698,3629,3456,1833,1431, 746, 77,1261,2017,2296,1977,1885, + 125,1334,1600, 525,1798,1109,2222,1470,1945, 559,2236,1186,3443,2476,1929,1411, +2411,3135,1777,3372,2621,1841,1613,3229, 668,1430,1839,2643,2916, 195,1989,2671, +2358,1387, 629,3205,2293,5256,4439, 123,1310, 888,1879,4300,3021,3605,1003,1162, +3192,2910,2010, 140,2395,2859, 55,1082,2012,2901, 662, 419,2081,1438, 680,2774, +4654,3912,1620,1731,1625,5035,4065,2328, 512,1344, 802,5443,2163,2311,2537, 524, +3399, 98,1155,2103,1918,2606,3925,2816,1393,2465,1504,3773,2177,3963,1478,4346, + 180,1113,4655,3461,2028,1698, 833,2696,1235,1322,1594,4408,3623,3013,3225,2040, +3022, 541,2881, 607,3632,2029,1665,1219, 639,1385,1686,1099,2803,3231,1938,3188, +2858, 427, 676,2772,1168,2025, 454,3253,2486,3556, 230,1950, 580, 791,1991,1280, +1086,1974,2034, 630, 257,3338,2788,4903,1017, 86,4790, 966,2789,1995,1696,1131, + 259,3095,4188,1308, 179,1463,5257, 289,4107,1248, 42,3413,1725,2288, 896,1947, + 774,4474,4254, 604,3430,4264, 392,2514,2588, 452, 237,1408,3018, 988,4531,1970, +3034,3310, 540,2370,1562,1288,2990, 502,4765,1147, 4,1853,2708, 207, 294,2814, +4078,2902,2509, 684, 34,3105,3532,2551, 644, 709,2801,2344, 573,1727,3573,3557, +2021,1081,3100,4315,2100,3681, 199,2263,1837,2385, 146,3484,1195,2776,3949, 997, +1939,3973,1008,1091,1202,1962,1847,1149,4209,5444,1076, 493, 117,5400,2521, 972, +1490,2934,1796,4542,2374,1512,2933,2657, 413,2888,1135,2762,2314,2156,1355,2369, + 766,2007,2527,2170,3124,2491,2593,2632,4757,2437, 234,3125,3591,1898,1750,1376, +1942,3468,3138, 570,2127,2145,3276,4131, 962, 132,1445,4196, 19, 941,3624,3480, +3366,1973,1374,4461,3431,2629, 283,2415,2275, 808,2887,3620,2112,2563,1353,3610, + 955,1089,3103,1053, 96, 88,4097, 823,3808,1583, 399, 292,4091,3313, 421,1128, + 642,4006, 903,2539,1877,2082, 596, 29,4066,1790, 722,2157, 130, 995,1569, 769, +1485, 464, 513,2213, 288,1923,1101,2453,4316, 133, 486,2445, 50, 625, 487,2207, + 57, 423, 481,2962, 159,3729,1558, 491, 303, 482, 501, 240,2837, 112,3648,2392, +1783, 362, 8,3433,3422, 610,2793,3277,1390,1284,1654, 21,3823, 734, 367, 623, + 193, 287, 374,1009,1483, 816, 476, 313,2255,2340,1262,2150,2899,1146,2581, 782, +2116,1659,2018,1880, 255,3586,3314,1110,2867,2137,2564, 986,2767,5185,2006, 650, + 158, 926, 762, 881,3157,2717,2362,3587, 306,3690,3245,1542,3077,2427,1691,2478, +2118,2985,3490,2438, 539,2305, 983, 129,1754, 355,4201,2386, 827,2923, 104,1773, +2838,2771, 411,2905,3919, 376, 767, 122,1114, 828,2422,1817,3506, 266,3460,1007, +1609,4998, 945,2612,4429,2274, 726,1247,1964,2914,2199,2070,4002,4108, 657,3323, +1422, 579, 455,2764,4737,1222,2895,1670, 824,1223,1487,2525, 558, 861,3080, 598, +2659,2515,1967, 752,2583,2376,2214,4180, 977, 704,2464,4999,2622,4109,1210,2961, + 819,1541, 142,2284, 44, 418, 457,1126,3730,4347,4626,1644,1876,3671,1864, 302, +1063,5694, 624, 723,1984,3745,1314,1676,2488,1610,1449,3558,3569,2166,2098, 409, +1011,2325,3704,2306, 818,1732,1383,1824,1844,3757, 999,2705,3497,1216,1423,2683, +2426,2954,2501,2726,2229,1475,2554,5064,1971,1794,1666,2014,1343, 783, 724, 191, +2434,1354,2220,5065,1763,2752,2472,4152, 131, 175,2885,3434, 92,1466,4920,2616, +3871,3872,3866, 128,1551,1632, 669,1854,3682,4691,4125,1230, 188,2973,3290,1302, +1213, 560,3266, 917, 763,3909,3249,1760, 868,1958, 764,1782,2097, 145,2277,3774, +4462, 64,1491,3062, 971,2132,3606,2442, 221,1226,1617, 218, 323,1185,3207,3147, + 571, 619,1473,1005,1744,2281, 449,1887,2396,3685, 275, 375,3816,1743,3844,3731, + 845,1983,2350,4210,1377, 773, 967,3499,3052,3743,2725,4007,1697,1022,3943,1464, +3264,2855,2722,1952,1029,2839,2467, 84,4383,2215, 820,1391,2015,2448,3672, 377, +1948,2168, 797,2545,3536,2578,2645, 94,2874,1678, 405,1259,3071, 771, 546,1315, + 470,1243,3083, 895,2468, 981, 969,2037, 846,4181, 653,1276,2928, 14,2594, 557, +3007,2474, 156, 902,1338,1740,2574, 537,2518, 973,2282,2216,2433,1928, 138,2903, +1293,2631,1612, 646,3457, 839,2935, 111, 496,2191,2847, 589,3186, 149,3994,2060, +4031,2641,4067,3145,1870, 37,3597,2136,1025,2051,3009,3383,3549,1121,1016,3261, +1301, 251,2446,2599,2153, 872,3246, 637, 334,3705, 831, 884, 921,3065,3140,4092, +2198,1944, 246,2964, 108,2045,1152,1921,2308,1031, 203,3173,4170,1907,3890, 810, +1401,2003,1690, 506, 647,1242,2828,1761,1649,3208,2249,1589,3709,2931,5156,1708, + 498, 666,2613, 834,3817,1231, 184,2851,1124, 883,3197,2261,3710,1765,1553,2658, +1178,2639,2351, 93,1193, 942,2538,2141,4402, 235,1821, 870,1591,2192,1709,1871, +3341,1618,4126,2595,2334, 603, 651, 69, 701, 268,2662,3411,2555,1380,1606, 503, + 448, 254,2371,2646, 574,1187,2309,1770, 322,2235,1292,1801, 305, 566,1133, 229, +2067,2057, 706, 167, 483,2002,2672,3295,1820,3561,3067, 316, 378,2746,3452,1112, + 136,1981, 507,1651,2917,1117, 285,4591, 182,2580,3522,1304, 335,3303,1835,2504, +1795,1792,2248, 674,1018,2106,2449,1857,2292,2845, 976,3047,1781,2600,2727,1389, +1281, 52,3152, 153, 265,3950, 672,3485,3951,4463, 430,1183, 365, 278,2169, 27, +1407,1336,2304, 209,1340,1730,2202,1852,2403,2883, 979,1737,1062, 631,2829,2542, +3876,2592, 825,2086,2226,3048,3625, 352,1417,3724, 542, 991, 431,1351,3938,1861, +2294, 826,1361,2927,3142,3503,1738, 463,2462,2723, 582,1916,1595,2808, 400,3845, +3891,2868,3621,2254, 58,2492,1123, 910,2160,2614,1372,1603,1196,1072,3385,1700, +3267,1980, 696, 480,2430, 920, 799,1570,2920,1951,2041,4047,2540,1321,4223,2469, +3562,2228,1271,2602, 401,2833,3351,2575,5157, 907,2312,1256, 410, 263,3507,1582, + 996, 678,1849,2316,1480, 908,3545,2237, 703,2322, 667,1826,2849,1531,2604,2999, +2407,3146,2151,2630,1786,3711, 469,3542, 497,3899,2409, 858, 837,4446,3393,1274, + 786, 620,1845,2001,3311, 484, 308,3367,1204,1815,3691,2332,1532,2557,1842,2020, +2724,1927,2333,4440, 567, 22,1673,2728,4475,1987,1858,1144,1597, 101,1832,3601, + 12, 974,3783,4391, 951,1412, 1,3720, 453,4608,4041, 528,1041,1027,3230,2628, +1129, 875,1051,3291,1203,2262,1069,2860,2799,2149,2615,3278, 144,1758,3040, 31, + 475,1680, 366,2685,3184, 311,1642,4008,2466,5036,1593,1493,2809, 216,1420,1668, + 233, 304,2128,3284, 232,1429,1768,1040,2008,3407,2740,2967,2543, 242,2133, 778, +1565,2022,2620, 505,2189,2756,1098,2273, 372,1614, 708, 553,2846,2094,2278, 169, +3626,2835,4161, 228,2674,3165, 809,1454,1309, 466,1705,1095, 900,3423, 880,2667, +3751,5258,2317,3109,2571,4317,2766,1503,1342, 866,4447,1118, 63,2076, 314,1881, +1348,1061, 172, 978,3515,1747, 532, 511,3970, 6, 601, 905,2699,3300,1751, 276, +1467,3725,2668, 65,4239,2544,2779,2556,1604, 578,2451,1802, 992,2331,2624,1320, +3446, 713,1513,1013, 103,2786,2447,1661, 886,1702, 916, 654,3574,2031,1556, 751, +2178,2821,2179,1498,1538,2176, 271, 914,2251,2080,1325, 638,1953,2937,3877,2432, +2754, 95,3265,1716, 260,1227,4083, 775, 106,1357,3254, 426,1607, 555,2480, 772, +1985, 244,2546, 474, 495,1046,2611,1851,2061, 71,2089,1675,2590, 742,3758,2843, +3222,1433, 267,2180,2576,2826,2233,2092,3913,2435, 956,1745,3075, 856,2113,1116, + 451, 3,1988,2896,1398, 993,2463,1878,2049,1341,2718,2721,2870,2108, 712,2904, +4363,2753,2324, 277,2872,2349,2649, 384, 987, 435, 691,3000, 922, 164,3939, 652, +1500,1184,4153,2482,3373,2165,4848,2335,3775,3508,3154,2806,2830,1554,2102,1664, +2530,1434,2408, 893,1547,2623,3447,2832,2242,2532,3169,2856,3223,2078, 49,3770, +3469, 462, 318, 656,2259,3250,3069, 679,1629,2758, 344,1138,1104,3120,1836,1283, +3115,2154,1437,4448, 934, 759,1999, 794,2862,1038, 533,2560,1722,2342, 855,2626, +1197,1663,4476,3127, 85,4240,2528, 25,1111,1181,3673, 407,3470,4561,2679,2713, + 768,1925,2841,3986,1544,1165, 932, 373,1240,2146,1930,2673, 721,4766, 354,4333, + 391,2963, 187, 61,3364,1442,1102, 330,1940,1767, 341,3809,4118, 393,2496,2062, +2211, 105, 331, 300, 439, 913,1332, 626, 379,3304,1557, 328, 689,3952, 309,1555, + 931, 317,2517,3027, 325, 569, 686,2107,3084, 60,1042,1333,2794, 264,3177,4014, +1628, 258,3712, 7,4464,1176,1043,1778, 683, 114,1975, 78,1492, 383,1886, 510, + 386, 645,5291,2891,2069,3305,4138,3867,2939,2603,2493,1935,1066,1848,3588,1015, +1282,1289,4609, 697,1453,3044,2666,3611,1856,2412, 54, 719,1330, 568,3778,2459, +1748, 788, 492, 551,1191,1000, 488,3394,3763, 282,1799, 348,2016,1523,3155,2390, +1049, 382,2019,1788,1170, 729,2968,3523, 897,3926,2785,2938,3292, 350,2319,3238, +1718,1717,2655,3453,3143,4465, 161,2889,2980,2009,1421, 56,1908,1640,2387,2232, +1917,1874,2477,4921, 148, 83,3438, 592,4245,2882,1822,1055, 741, 115,1496,1624, + 381,1638,4592,1020, 516,3214, 458, 947,4575,1432, 211,1514,2926,1865,2142, 189, + 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, #last 512 +) + diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py b/venv/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py new file mode 100644 index 0000000..8446d2d --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py @@ -0,0 +1,46 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import GB2312DistributionAnalysis +from .mbcssm import GB2312_SM_MODEL + +class GB2312Prober(MultiByteCharSetProber): + def __init__(self): + super(GB2312Prober, self).__init__() + self.coding_sm = CodingStateMachine(GB2312_SM_MODEL) + self.distribution_analyzer = GB2312DistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "GB2312" + + @property + def language(self): + return "Chinese" diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py new file mode 100644 index 0000000..b0e1bf4 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py @@ -0,0 +1,292 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Shy Shalom +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import ProbingState + +# This prober doesn't actually recognize a language or a charset. +# It is a helper prober for the use of the Hebrew model probers + +### General ideas of the Hebrew charset recognition ### +# +# Four main charsets exist in Hebrew: +# "ISO-8859-8" - Visual Hebrew +# "windows-1255" - Logical Hebrew +# "ISO-8859-8-I" - Logical Hebrew +# "x-mac-hebrew" - ?? Logical Hebrew ?? +# +# Both "ISO" charsets use a completely identical set of code points, whereas +# "windows-1255" and "x-mac-hebrew" are two different proper supersets of +# these code points. windows-1255 defines additional characters in the range +# 0x80-0x9F as some misc punctuation marks as well as some Hebrew-specific +# diacritics and additional 'Yiddish' ligature letters in the range 0xc0-0xd6. +# x-mac-hebrew defines similar additional code points but with a different +# mapping. +# +# As far as an average Hebrew text with no diacritics is concerned, all four +# charsets are identical with respect to code points. Meaning that for the +# main Hebrew alphabet, all four map the same values to all 27 Hebrew letters +# (including final letters). +# +# The dominant difference between these charsets is their directionality. +# "Visual" directionality means that the text is ordered as if the renderer is +# not aware of a BIDI rendering algorithm. The renderer sees the text and +# draws it from left to right. The text itself when ordered naturally is read +# backwards. A buffer of Visual Hebrew generally looks like so: +# "[last word of first line spelled backwards] [whole line ordered backwards +# and spelled backwards] [first word of first line spelled backwards] +# [end of line] [last word of second line] ... etc' " +# adding punctuation marks, numbers and English text to visual text is +# naturally also "visual" and from left to right. +# +# "Logical" directionality means the text is ordered "naturally" according to +# the order it is read. It is the responsibility of the renderer to display +# the text from right to left. A BIDI algorithm is used to place general +# punctuation marks, numbers and English text in the text. +# +# Texts in x-mac-hebrew are almost impossible to find on the Internet. From +# what little evidence I could find, it seems that its general directionality +# is Logical. +# +# To sum up all of the above, the Hebrew probing mechanism knows about two +# charsets: +# Visual Hebrew - "ISO-8859-8" - backwards text - Words and sentences are +# backwards while line order is natural. For charset recognition purposes +# the line order is unimportant (In fact, for this implementation, even +# word order is unimportant). +# Logical Hebrew - "windows-1255" - normal, naturally ordered text. +# +# "ISO-8859-8-I" is a subset of windows-1255 and doesn't need to be +# specifically identified. +# "x-mac-hebrew" is also identified as windows-1255. A text in x-mac-hebrew +# that contain special punctuation marks or diacritics is displayed with +# some unconverted characters showing as question marks. This problem might +# be corrected using another model prober for x-mac-hebrew. Due to the fact +# that x-mac-hebrew texts are so rare, writing another model prober isn't +# worth the effort and performance hit. +# +#### The Prober #### +# +# The prober is divided between two SBCharSetProbers and a HebrewProber, +# all of which are managed, created, fed data, inquired and deleted by the +# SBCSGroupProber. The two SBCharSetProbers identify that the text is in +# fact some kind of Hebrew, Logical or Visual. The final decision about which +# one is it is made by the HebrewProber by combining final-letter scores +# with the scores of the two SBCharSetProbers to produce a final answer. +# +# The SBCSGroupProber is responsible for stripping the original text of HTML +# tags, English characters, numbers, low-ASCII punctuation characters, spaces +# and new lines. It reduces any sequence of such characters to a single space. +# The buffer fed to each prober in the SBCS group prober is pure text in +# high-ASCII. +# The two SBCharSetProbers (model probers) share the same language model: +# Win1255Model. +# The first SBCharSetProber uses the model normally as any other +# SBCharSetProber does, to recognize windows-1255, upon which this model was +# built. The second SBCharSetProber is told to make the pair-of-letter +# lookup in the language model backwards. This in practice exactly simulates +# a visual Hebrew model using the windows-1255 logical Hebrew model. +# +# The HebrewProber is not using any language model. All it does is look for +# final-letter evidence suggesting the text is either logical Hebrew or visual +# Hebrew. Disjointed from the model probers, the results of the HebrewProber +# alone are meaningless. HebrewProber always returns 0.00 as confidence +# since it never identifies a charset by itself. Instead, the pointer to the +# HebrewProber is passed to the model probers as a helper "Name Prober". +# When the Group prober receives a positive identification from any prober, +# it asks for the name of the charset identified. If the prober queried is a +# Hebrew model prober, the model prober forwards the call to the +# HebrewProber to make the final decision. In the HebrewProber, the +# decision is made according to the final-letters scores maintained and Both +# model probers scores. The answer is returned in the form of the name of the +# charset identified, either "windows-1255" or "ISO-8859-8". + +class HebrewProber(CharSetProber): + # windows-1255 / ISO-8859-8 code points of interest + FINAL_KAF = 0xea + NORMAL_KAF = 0xeb + FINAL_MEM = 0xed + NORMAL_MEM = 0xee + FINAL_NUN = 0xef + NORMAL_NUN = 0xf0 + FINAL_PE = 0xf3 + NORMAL_PE = 0xf4 + FINAL_TSADI = 0xf5 + NORMAL_TSADI = 0xf6 + + # Minimum Visual vs Logical final letter score difference. + # If the difference is below this, don't rely solely on the final letter score + # distance. + MIN_FINAL_CHAR_DISTANCE = 5 + + # Minimum Visual vs Logical model score difference. + # If the difference is below this, don't rely at all on the model score + # distance. + MIN_MODEL_DISTANCE = 0.01 + + VISUAL_HEBREW_NAME = "ISO-8859-8" + LOGICAL_HEBREW_NAME = "windows-1255" + + def __init__(self): + super(HebrewProber, self).__init__() + self._final_char_logical_score = None + self._final_char_visual_score = None + self._prev = None + self._before_prev = None + self._logical_prober = None + self._visual_prober = None + self.reset() + + def reset(self): + self._final_char_logical_score = 0 + self._final_char_visual_score = 0 + # The two last characters seen in the previous buffer, + # mPrev and mBeforePrev are initialized to space in order to simulate + # a word delimiter at the beginning of the data + self._prev = ' ' + self._before_prev = ' ' + # These probers are owned by the group prober. + + def set_model_probers(self, logicalProber, visualProber): + self._logical_prober = logicalProber + self._visual_prober = visualProber + + def is_final(self, c): + return c in [self.FINAL_KAF, self.FINAL_MEM, self.FINAL_NUN, + self.FINAL_PE, self.FINAL_TSADI] + + def is_non_final(self, c): + # The normal Tsadi is not a good Non-Final letter due to words like + # 'lechotet' (to chat) containing an apostrophe after the tsadi. This + # apostrophe is converted to a space in FilterWithoutEnglishLetters + # causing the Non-Final tsadi to appear at an end of a word even + # though this is not the case in the original text. + # The letters Pe and Kaf rarely display a related behavior of not being + # a good Non-Final letter. Words like 'Pop', 'Winamp' and 'Mubarak' + # for example legally end with a Non-Final Pe or Kaf. However, the + # benefit of these letters as Non-Final letters outweighs the damage + # since these words are quite rare. + return c in [self.NORMAL_KAF, self.NORMAL_MEM, + self.NORMAL_NUN, self.NORMAL_PE] + + def feed(self, byte_str): + # Final letter analysis for logical-visual decision. + # Look for evidence that the received buffer is either logical Hebrew + # or visual Hebrew. + # The following cases are checked: + # 1) A word longer than 1 letter, ending with a final letter. This is + # an indication that the text is laid out "naturally" since the + # final letter really appears at the end. +1 for logical score. + # 2) A word longer than 1 letter, ending with a Non-Final letter. In + # normal Hebrew, words ending with Kaf, Mem, Nun, Pe or Tsadi, + # should not end with the Non-Final form of that letter. Exceptions + # to this rule are mentioned above in isNonFinal(). This is an + # indication that the text is laid out backwards. +1 for visual + # score + # 3) A word longer than 1 letter, starting with a final letter. Final + # letters should not appear at the beginning of a word. This is an + # indication that the text is laid out backwards. +1 for visual + # score. + # + # The visual score and logical score are accumulated throughout the + # text and are finally checked against each other in GetCharSetName(). + # No checking for final letters in the middle of words is done since + # that case is not an indication for either Logical or Visual text. + # + # We automatically filter out all 7-bit characters (replace them with + # spaces) so the word boundary detection works properly. [MAP] + + if self.state == ProbingState.NOT_ME: + # Both model probers say it's not them. No reason to continue. + return ProbingState.NOT_ME + + byte_str = self.filter_high_byte_only(byte_str) + + for cur in byte_str: + if cur == ' ': + # We stand on a space - a word just ended + if self._before_prev != ' ': + # next-to-last char was not a space so self._prev is not a + # 1 letter word + if self.is_final(self._prev): + # case (1) [-2:not space][-1:final letter][cur:space] + self._final_char_logical_score += 1 + elif self.is_non_final(self._prev): + # case (2) [-2:not space][-1:Non-Final letter][ + # cur:space] + self._final_char_visual_score += 1 + else: + # Not standing on a space + if ((self._before_prev == ' ') and + (self.is_final(self._prev)) and (cur != ' ')): + # case (3) [-2:space][-1:final letter][cur:not space] + self._final_char_visual_score += 1 + self._before_prev = self._prev + self._prev = cur + + # Forever detecting, till the end or until both model probers return + # ProbingState.NOT_ME (handled above) + return ProbingState.DETECTING + + @property + def charset_name(self): + # Make the decision: is it Logical or Visual? + # If the final letter score distance is dominant enough, rely on it. + finalsub = self._final_char_logical_score - self._final_char_visual_score + if finalsub >= self.MIN_FINAL_CHAR_DISTANCE: + return self.LOGICAL_HEBREW_NAME + if finalsub <= -self.MIN_FINAL_CHAR_DISTANCE: + return self.VISUAL_HEBREW_NAME + + # It's not dominant enough, try to rely on the model scores instead. + modelsub = (self._logical_prober.get_confidence() + - self._visual_prober.get_confidence()) + if modelsub > self.MIN_MODEL_DISTANCE: + return self.LOGICAL_HEBREW_NAME + if modelsub < -self.MIN_MODEL_DISTANCE: + return self.VISUAL_HEBREW_NAME + + # Still no good, back to final letter distance, maybe it'll save the + # day. + if finalsub < 0.0: + return self.VISUAL_HEBREW_NAME + + # (finalsub > 0 - Logical) or (don't know what to do) default to + # Logical. + return self.LOGICAL_HEBREW_NAME + + @property + def language(self): + return 'Hebrew' + + @property + def state(self): + # Remain active as long as any of the model probers are active. + if (self._logical_prober.state == ProbingState.NOT_ME) and \ + (self._visual_prober.state == ProbingState.NOT_ME): + return ProbingState.NOT_ME + return ProbingState.DETECTING diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/jisfreq.py b/venv/Lib/site-packages/pip/_vendor/chardet/jisfreq.py new file mode 100644 index 0000000..83fc082 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/jisfreq.py @@ -0,0 +1,325 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Sampling from about 20M text materials include literature and computer technology +# +# Japanese frequency table, applied to both S-JIS and EUC-JP +# They are sorted in order. + +# 128 --> 0.77094 +# 256 --> 0.85710 +# 512 --> 0.92635 +# 1024 --> 0.97130 +# 2048 --> 0.99431 +# +# Ideal Distribution Ratio = 0.92635 / (1-0.92635) = 12.58 +# Random Distribution Ration = 512 / (2965+62+83+86-512) = 0.191 +# +# Typical Distribution Ratio, 25% of IDR + +JIS_TYPICAL_DISTRIBUTION_RATIO = 3.0 + +# Char to FreqOrder table , +JIS_TABLE_SIZE = 4368 + +JIS_CHAR_TO_FREQ_ORDER = ( + 40, 1, 6, 182, 152, 180, 295,2127, 285, 381,3295,4304,3068,4606,3165,3510, # 16 +3511,1822,2785,4607,1193,2226,5070,4608, 171,2996,1247, 18, 179,5071, 856,1661, # 32 +1262,5072, 619, 127,3431,3512,3230,1899,1700, 232, 228,1294,1298, 284, 283,2041, # 48 +2042,1061,1062, 48, 49, 44, 45, 433, 434,1040,1041, 996, 787,2997,1255,4305, # 64 +2108,4609,1684,1648,5073,5074,5075,5076,5077,5078,3687,5079,4610,5080,3927,3928, # 80 +5081,3296,3432, 290,2285,1471,2187,5082,2580,2825,1303,2140,1739,1445,2691,3375, # 96 +1691,3297,4306,4307,4611, 452,3376,1182,2713,3688,3069,4308,5083,5084,5085,5086, # 112 +5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102, # 128 +5103,5104,5105,5106,5107,5108,5109,5110,5111,5112,4097,5113,5114,5115,5116,5117, # 144 +5118,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,5130,5131,5132,5133, # 160 +5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149, # 176 +5150,5151,5152,4612,5153,5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164, # 192 +5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,1472, 598, 618, 820,1205, # 208 +1309,1412,1858,1307,1692,5176,5177,5178,5179,5180,5181,5182,1142,1452,1234,1172, # 224 +1875,2043,2149,1793,1382,2973, 925,2404,1067,1241, 960,1377,2935,1491, 919,1217, # 240 +1865,2030,1406,1499,2749,4098,5183,5184,5185,5186,5187,5188,2561,4099,3117,1804, # 256 +2049,3689,4309,3513,1663,5189,3166,3118,3298,1587,1561,3433,5190,3119,1625,2998, # 272 +3299,4613,1766,3690,2786,4614,5191,5192,5193,5194,2161, 26,3377, 2,3929, 20, # 288 +3691, 47,4100, 50, 17, 16, 35, 268, 27, 243, 42, 155, 24, 154, 29, 184, # 304 + 4, 91, 14, 92, 53, 396, 33, 289, 9, 37, 64, 620, 21, 39, 321, 5, # 320 + 12, 11, 52, 13, 3, 208, 138, 0, 7, 60, 526, 141, 151,1069, 181, 275, # 336 +1591, 83, 132,1475, 126, 331, 829, 15, 69, 160, 59, 22, 157, 55,1079, 312, # 352 + 109, 38, 23, 25, 10, 19, 79,5195, 61, 382,1124, 8, 30,5196,5197,5198, # 368 +5199,5200,5201,5202,5203,5204,5205,5206, 89, 62, 74, 34,2416, 112, 139, 196, # 384 + 271, 149, 84, 607, 131, 765, 46, 88, 153, 683, 76, 874, 101, 258, 57, 80, # 400 + 32, 364, 121,1508, 169,1547, 68, 235, 145,2999, 41, 360,3027, 70, 63, 31, # 416 + 43, 259, 262,1383, 99, 533, 194, 66, 93, 846, 217, 192, 56, 106, 58, 565, # 432 + 280, 272, 311, 256, 146, 82, 308, 71, 100, 128, 214, 655, 110, 261, 104,1140, # 448 + 54, 51, 36, 87, 67,3070, 185,2618,2936,2020, 28,1066,2390,2059,5207,5208, # 464 +5209,5210,5211,5212,5213,5214,5215,5216,4615,5217,5218,5219,5220,5221,5222,5223, # 480 +5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234,5235,5236,3514,5237,5238, # 496 +5239,5240,5241,5242,5243,5244,2297,2031,4616,4310,3692,5245,3071,5246,3598,5247, # 512 +4617,3231,3515,5248,4101,4311,4618,3808,4312,4102,5249,4103,4104,3599,5250,5251, # 528 +5252,5253,5254,5255,5256,5257,5258,5259,5260,5261,5262,5263,5264,5265,5266,5267, # 544 +5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278,5279,5280,5281,5282,5283, # 560 +5284,5285,5286,5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299, # 576 +5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315, # 592 +5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331, # 608 +5332,5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347, # 624 +5348,5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363, # 640 +5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379, # 656 +5380,5381, 363, 642,2787,2878,2788,2789,2316,3232,2317,3434,2011, 165,1942,3930, # 672 +3931,3932,3933,5382,4619,5383,4620,5384,5385,5386,5387,5388,5389,5390,5391,5392, # 688 +5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408, # 704 +5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424, # 720 +5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,5439,5440, # 736 +5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456, # 752 +5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472, # 768 +5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487,5488, # 784 +5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504, # 800 +5505,5506,5507,5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520, # 816 +5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536, # 832 +5537,5538,5539,5540,5541,5542,5543,5544,5545,5546,5547,5548,5549,5550,5551,5552, # 848 +5553,5554,5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568, # 864 +5569,5570,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584, # 880 +5585,5586,5587,5588,5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600, # 896 +5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616, # 912 +5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631,5632, # 928 +5633,5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648, # 944 +5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664, # 960 +5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680, # 976 +5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696, # 992 +5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712, # 1008 +5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728, # 1024 +5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744, # 1040 +5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,5760, # 1056 +5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776, # 1072 +5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5787,5788,5789,5790,5791,5792, # 1088 +5793,5794,5795,5796,5797,5798,5799,5800,5801,5802,5803,5804,5805,5806,5807,5808, # 1104 +5809,5810,5811,5812,5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824, # 1120 +5825,5826,5827,5828,5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840, # 1136 +5841,5842,5843,5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856, # 1152 +5857,5858,5859,5860,5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872, # 1168 +5873,5874,5875,5876,5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888, # 1184 +5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904, # 1200 +5905,5906,5907,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920, # 1216 +5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936, # 1232 +5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952, # 1248 +5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968, # 1264 +5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984, # 1280 +5985,5986,5987,5988,5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000, # 1296 +6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016, # 1312 +6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032, # 1328 +6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048, # 1344 +6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064, # 1360 +6065,6066,6067,6068,6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080, # 1376 +6081,6082,6083,6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096, # 1392 +6097,6098,6099,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112, # 1408 +6113,6114,2044,2060,4621, 997,1235, 473,1186,4622, 920,3378,6115,6116, 379,1108, # 1424 +4313,2657,2735,3934,6117,3809, 636,3233, 573,1026,3693,3435,2974,3300,2298,4105, # 1440 + 854,2937,2463, 393,2581,2417, 539, 752,1280,2750,2480, 140,1161, 440, 708,1569, # 1456 + 665,2497,1746,1291,1523,3000, 164,1603, 847,1331, 537,1997, 486, 508,1693,2418, # 1472 +1970,2227, 878,1220, 299,1030, 969, 652,2751, 624,1137,3301,2619, 65,3302,2045, # 1488 +1761,1859,3120,1930,3694,3516, 663,1767, 852, 835,3695, 269, 767,2826,2339,1305, # 1504 + 896,1150, 770,1616,6118, 506,1502,2075,1012,2519, 775,2520,2975,2340,2938,4314, # 1520 +3028,2086,1224,1943,2286,6119,3072,4315,2240,1273,1987,3935,1557, 175, 597, 985, # 1536 +3517,2419,2521,1416,3029, 585, 938,1931,1007,1052,1932,1685,6120,3379,4316,4623, # 1552 + 804, 599,3121,1333,2128,2539,1159,1554,2032,3810, 687,2033,2904, 952, 675,1467, # 1568 +3436,6121,2241,1096,1786,2440,1543,1924, 980,1813,2228, 781,2692,1879, 728,1918, # 1584 +3696,4624, 548,1950,4625,1809,1088,1356,3303,2522,1944, 502, 972, 373, 513,2827, # 1600 + 586,2377,2391,1003,1976,1631,6122,2464,1084, 648,1776,4626,2141, 324, 962,2012, # 1616 +2177,2076,1384, 742,2178,1448,1173,1810, 222, 102, 301, 445, 125,2420, 662,2498, # 1632 + 277, 200,1476,1165,1068, 224,2562,1378,1446, 450,1880, 659, 791, 582,4627,2939, # 1648 +3936,1516,1274, 555,2099,3697,1020,1389,1526,3380,1762,1723,1787,2229, 412,2114, # 1664 +1900,2392,3518, 512,2597, 427,1925,2341,3122,1653,1686,2465,2499, 697, 330, 273, # 1680 + 380,2162, 951, 832, 780, 991,1301,3073, 965,2270,3519, 668,2523,2636,1286, 535, # 1696 +1407, 518, 671, 957,2658,2378, 267, 611,2197,3030,6123, 248,2299, 967,1799,2356, # 1712 + 850,1418,3437,1876,1256,1480,2828,1718,6124,6125,1755,1664,2405,6126,4628,2879, # 1728 +2829, 499,2179, 676,4629, 557,2329,2214,2090, 325,3234, 464, 811,3001, 992,2342, # 1744 +2481,1232,1469, 303,2242, 466,1070,2163, 603,1777,2091,4630,2752,4631,2714, 322, # 1760 +2659,1964,1768, 481,2188,1463,2330,2857,3600,2092,3031,2421,4632,2318,2070,1849, # 1776 +2598,4633,1302,2254,1668,1701,2422,3811,2905,3032,3123,2046,4106,1763,1694,4634, # 1792 +1604, 943,1724,1454, 917, 868,2215,1169,2940, 552,1145,1800,1228,1823,1955, 316, # 1808 +1080,2510, 361,1807,2830,4107,2660,3381,1346,1423,1134,4108,6127, 541,1263,1229, # 1824 +1148,2540, 545, 465,1833,2880,3438,1901,3074,2482, 816,3937, 713,1788,2500, 122, # 1840 +1575, 195,1451,2501,1111,6128, 859, 374,1225,2243,2483,4317, 390,1033,3439,3075, # 1856 +2524,1687, 266, 793,1440,2599, 946, 779, 802, 507, 897,1081, 528,2189,1292, 711, # 1872 +1866,1725,1167,1640, 753, 398,2661,1053, 246, 348,4318, 137,1024,3440,1600,2077, # 1888 +2129, 825,4319, 698, 238, 521, 187,2300,1157,2423,1641,1605,1464,1610,1097,2541, # 1904 +1260,1436, 759,2255,1814,2150, 705,3235, 409,2563,3304, 561,3033,2005,2564, 726, # 1920 +1956,2343,3698,4109, 949,3812,3813,3520,1669, 653,1379,2525, 881,2198, 632,2256, # 1936 +1027, 778,1074, 733,1957, 514,1481,2466, 554,2180, 702,3938,1606,1017,1398,6129, # 1952 +1380,3521, 921, 993,1313, 594, 449,1489,1617,1166, 768,1426,1360, 495,1794,3601, # 1968 +1177,3602,1170,4320,2344, 476, 425,3167,4635,3168,1424, 401,2662,1171,3382,1998, # 1984 +1089,4110, 477,3169, 474,6130,1909, 596,2831,1842, 494, 693,1051,1028,1207,3076, # 2000 + 606,2115, 727,2790,1473,1115, 743,3522, 630, 805,1532,4321,2021, 366,1057, 838, # 2016 + 684,1114,2142,4322,2050,1492,1892,1808,2271,3814,2424,1971,1447,1373,3305,1090, # 2032 +1536,3939,3523,3306,1455,2199, 336, 369,2331,1035, 584,2393, 902, 718,2600,6131, # 2048 +2753, 463,2151,1149,1611,2467, 715,1308,3124,1268, 343,1413,3236,1517,1347,2663, # 2064 +2093,3940,2022,1131,1553,2100,2941,1427,3441,2942,1323,2484,6132,1980, 872,2368, # 2080 +2441,2943, 320,2369,2116,1082, 679,1933,3941,2791,3815, 625,1143,2023, 422,2200, # 2096 +3816,6133, 730,1695, 356,2257,1626,2301,2858,2637,1627,1778, 937, 883,2906,2693, # 2112 +3002,1769,1086, 400,1063,1325,3307,2792,4111,3077, 456,2345,1046, 747,6134,1524, # 2128 + 884,1094,3383,1474,2164,1059, 974,1688,2181,2258,1047, 345,1665,1187, 358, 875, # 2144 +3170, 305, 660,3524,2190,1334,1135,3171,1540,1649,2542,1527, 927, 968,2793, 885, # 2160 +1972,1850, 482, 500,2638,1218,1109,1085,2543,1654,2034, 876, 78,2287,1482,1277, # 2176 + 861,1675,1083,1779, 724,2754, 454, 397,1132,1612,2332, 893, 672,1237, 257,2259, # 2192 +2370, 135,3384, 337,2244, 547, 352, 340, 709,2485,1400, 788,1138,2511, 540, 772, # 2208 +1682,2260,2272,2544,2013,1843,1902,4636,1999,1562,2288,4637,2201,1403,1533, 407, # 2224 + 576,3308,1254,2071, 978,3385, 170, 136,1201,3125,2664,3172,2394, 213, 912, 873, # 2240 +3603,1713,2202, 699,3604,3699, 813,3442, 493, 531,1054, 468,2907,1483, 304, 281, # 2256 +4112,1726,1252,2094, 339,2319,2130,2639, 756,1563,2944, 748, 571,2976,1588,2425, # 2272 +2715,1851,1460,2426,1528,1392,1973,3237, 288,3309, 685,3386, 296, 892,2716,2216, # 2288 +1570,2245, 722,1747,2217, 905,3238,1103,6135,1893,1441,1965, 251,1805,2371,3700, # 2304 +2601,1919,1078, 75,2182,1509,1592,1270,2640,4638,2152,6136,3310,3817, 524, 706, # 2320 +1075, 292,3818,1756,2602, 317, 98,3173,3605,3525,1844,2218,3819,2502, 814, 567, # 2336 + 385,2908,1534,6137, 534,1642,3239, 797,6138,1670,1529, 953,4323, 188,1071, 538, # 2352 + 178, 729,3240,2109,1226,1374,2000,2357,2977, 731,2468,1116,2014,2051,6139,1261, # 2368 +1593, 803,2859,2736,3443, 556, 682, 823,1541,6140,1369,2289,1706,2794, 845, 462, # 2384 +2603,2665,1361, 387, 162,2358,1740, 739,1770,1720,1304,1401,3241,1049, 627,1571, # 2400 +2427,3526,1877,3942,1852,1500, 431,1910,1503, 677, 297,2795, 286,1433,1038,1198, # 2416 +2290,1133,1596,4113,4639,2469,1510,1484,3943,6141,2442, 108, 712,4640,2372, 866, # 2432 +3701,2755,3242,1348, 834,1945,1408,3527,2395,3243,1811, 824, 994,1179,2110,1548, # 2448 +1453, 790,3003, 690,4324,4325,2832,2909,3820,1860,3821, 225,1748, 310, 346,1780, # 2464 +2470, 821,1993,2717,2796, 828, 877,3528,2860,2471,1702,2165,2910,2486,1789, 453, # 2480 + 359,2291,1676, 73,1164,1461,1127,3311, 421, 604, 314,1037, 589, 116,2487, 737, # 2496 + 837,1180, 111, 244, 735,6142,2261,1861,1362, 986, 523, 418, 581,2666,3822, 103, # 2512 + 855, 503,1414,1867,2488,1091, 657,1597, 979, 605,1316,4641,1021,2443,2078,2001, # 2528 +1209, 96, 587,2166,1032, 260,1072,2153, 173, 94, 226,3244, 819,2006,4642,4114, # 2544 +2203, 231,1744, 782, 97,2667, 786,3387, 887, 391, 442,2219,4326,1425,6143,2694, # 2560 + 633,1544,1202, 483,2015, 592,2052,1958,2472,1655, 419, 129,4327,3444,3312,1714, # 2576 +1257,3078,4328,1518,1098, 865,1310,1019,1885,1512,1734, 469,2444, 148, 773, 436, # 2592 +1815,1868,1128,1055,4329,1245,2756,3445,2154,1934,1039,4643, 579,1238, 932,2320, # 2608 + 353, 205, 801, 115,2428, 944,2321,1881, 399,2565,1211, 678, 766,3944, 335,2101, # 2624 +1459,1781,1402,3945,2737,2131,1010, 844, 981,1326,1013, 550,1816,1545,2620,1335, # 2640 +1008, 371,2881, 936,1419,1613,3529,1456,1395,2273,1834,2604,1317,2738,2503, 416, # 2656 +1643,4330, 806,1126, 229, 591,3946,1314,1981,1576,1837,1666, 347,1790, 977,3313, # 2672 + 764,2861,1853, 688,2429,1920,1462, 77, 595, 415,2002,3034, 798,1192,4115,6144, # 2688 +2978,4331,3035,2695,2582,2072,2566, 430,2430,1727, 842,1396,3947,3702, 613, 377, # 2704 + 278, 236,1417,3388,3314,3174, 757,1869, 107,3530,6145,1194, 623,2262, 207,1253, # 2720 +2167,3446,3948, 492,1117,1935, 536,1838,2757,1246,4332, 696,2095,2406,1393,1572, # 2736 +3175,1782, 583, 190, 253,1390,2230, 830,3126,3389, 934,3245,1703,1749,2979,1870, # 2752 +2545,1656,2204, 869,2346,4116,3176,1817, 496,1764,4644, 942,1504, 404,1903,1122, # 2768 +1580,3606,2945,1022, 515, 372,1735, 955,2431,3036,6146,2797,1110,2302,2798, 617, # 2784 +6147, 441, 762,1771,3447,3607,3608,1904, 840,3037, 86, 939,1385, 572,1370,2445, # 2800 +1336, 114,3703, 898, 294, 203,3315, 703,1583,2274, 429, 961,4333,1854,1951,3390, # 2816 +2373,3704,4334,1318,1381, 966,1911,2322,1006,1155, 309, 989, 458,2718,1795,1372, # 2832 +1203, 252,1689,1363,3177, 517,1936, 168,1490, 562, 193,3823,1042,4117,1835, 551, # 2848 + 470,4645, 395, 489,3448,1871,1465,2583,2641, 417,1493, 279,1295, 511,1236,1119, # 2864 + 72,1231,1982,1812,3004, 871,1564, 984,3449,1667,2696,2096,4646,2347,2833,1673, # 2880 +3609, 695,3246,2668, 807,1183,4647, 890, 388,2333,1801,1457,2911,1765,1477,1031, # 2896 +3316,3317,1278,3391,2799,2292,2526, 163,3450,4335,2669,1404,1802,6148,2323,2407, # 2912 +1584,1728,1494,1824,1269, 298, 909,3318,1034,1632, 375, 776,1683,2061, 291, 210, # 2928 +1123, 809,1249,1002,2642,3038, 206,1011,2132, 144, 975, 882,1565, 342, 667, 754, # 2944 +1442,2143,1299,2303,2062, 447, 626,2205,1221,2739,2912,1144,1214,2206,2584, 760, # 2960 +1715, 614, 950,1281,2670,2621, 810, 577,1287,2546,4648, 242,2168, 250,2643, 691, # 2976 + 123,2644, 647, 313,1029, 689,1357,2946,1650, 216, 771,1339,1306, 808,2063, 549, # 2992 + 913,1371,2913,2914,6149,1466,1092,1174,1196,1311,2605,2396,1783,1796,3079, 406, # 3008 +2671,2117,3949,4649, 487,1825,2220,6150,2915, 448,2348,1073,6151,2397,1707, 130, # 3024 + 900,1598, 329, 176,1959,2527,1620,6152,2275,4336,3319,1983,2191,3705,3610,2155, # 3040 +3706,1912,1513,1614,6153,1988, 646, 392,2304,1589,3320,3039,1826,1239,1352,1340, # 3056 +2916, 505,2567,1709,1437,2408,2547, 906,6154,2672, 384,1458,1594,1100,1329, 710, # 3072 + 423,3531,2064,2231,2622,1989,2673,1087,1882, 333, 841,3005,1296,2882,2379, 580, # 3088 +1937,1827,1293,2585, 601, 574, 249,1772,4118,2079,1120, 645, 901,1176,1690, 795, # 3104 +2207, 478,1434, 516,1190,1530, 761,2080, 930,1264, 355, 435,1552, 644,1791, 987, # 3120 + 220,1364,1163,1121,1538, 306,2169,1327,1222, 546,2645, 218, 241, 610,1704,3321, # 3136 +1984,1839,1966,2528, 451,6155,2586,3707,2568, 907,3178, 254,2947, 186,1845,4650, # 3152 + 745, 432,1757, 428,1633, 888,2246,2221,2489,3611,2118,1258,1265, 956,3127,1784, # 3168 +4337,2490, 319, 510, 119, 457,3612, 274,2035,2007,4651,1409,3128, 970,2758, 590, # 3184 +2800, 661,2247,4652,2008,3950,1420,1549,3080,3322,3951,1651,1375,2111, 485,2491, # 3200 +1429,1156,6156,2548,2183,1495, 831,1840,2529,2446, 501,1657, 307,1894,3247,1341, # 3216 + 666, 899,2156,1539,2549,1559, 886, 349,2208,3081,2305,1736,3824,2170,2759,1014, # 3232 +1913,1386, 542,1397,2948, 490, 368, 716, 362, 159, 282,2569,1129,1658,1288,1750, # 3248 +2674, 276, 649,2016, 751,1496, 658,1818,1284,1862,2209,2087,2512,3451, 622,2834, # 3264 + 376, 117,1060,2053,1208,1721,1101,1443, 247,1250,3179,1792,3952,2760,2398,3953, # 3280 +6157,2144,3708, 446,2432,1151,2570,3452,2447,2761,2835,1210,2448,3082, 424,2222, # 3296 +1251,2449,2119,2836, 504,1581,4338, 602, 817, 857,3825,2349,2306, 357,3826,1470, # 3312 +1883,2883, 255, 958, 929,2917,3248, 302,4653,1050,1271,1751,2307,1952,1430,2697, # 3328 +2719,2359, 354,3180, 777, 158,2036,4339,1659,4340,4654,2308,2949,2248,1146,2232, # 3344 +3532,2720,1696,2623,3827,6158,3129,1550,2698,1485,1297,1428, 637, 931,2721,2145, # 3360 + 914,2550,2587, 81,2450, 612, 827,2646,1242,4655,1118,2884, 472,1855,3181,3533, # 3376 +3534, 569,1353,2699,1244,1758,2588,4119,2009,2762,2171,3709,1312,1531,6159,1152, # 3392 +1938, 134,1830, 471,3710,2276,1112,1535,3323,3453,3535, 982,1337,2950, 488, 826, # 3408 + 674,1058,1628,4120,2017, 522,2399, 211, 568,1367,3454, 350, 293,1872,1139,3249, # 3424 +1399,1946,3006,1300,2360,3324, 588, 736,6160,2606, 744, 669,3536,3828,6161,1358, # 3440 + 199, 723, 848, 933, 851,1939,1505,1514,1338,1618,1831,4656,1634,3613, 443,2740, # 3456 +3829, 717,1947, 491,1914,6162,2551,1542,4121,1025,6163,1099,1223, 198,3040,2722, # 3472 + 370, 410,1905,2589, 998,1248,3182,2380, 519,1449,4122,1710, 947, 928,1153,4341, # 3488 +2277, 344,2624,1511, 615, 105, 161,1212,1076,1960,3130,2054,1926,1175,1906,2473, # 3504 + 414,1873,2801,6164,2309, 315,1319,3325, 318,2018,2146,2157, 963, 631, 223,4342, # 3520 +4343,2675, 479,3711,1197,2625,3712,2676,2361,6165,4344,4123,6166,2451,3183,1886, # 3536 +2184,1674,1330,1711,1635,1506, 799, 219,3250,3083,3954,1677,3713,3326,2081,3614, # 3552 +1652,2073,4657,1147,3041,1752, 643,1961, 147,1974,3955,6167,1716,2037, 918,3007, # 3568 +1994, 120,1537, 118, 609,3184,4345, 740,3455,1219, 332,1615,3830,6168,1621,2980, # 3584 +1582, 783, 212, 553,2350,3714,1349,2433,2082,4124, 889,6169,2310,1275,1410, 973, # 3600 + 166,1320,3456,1797,1215,3185,2885,1846,2590,2763,4658, 629, 822,3008, 763, 940, # 3616 +1990,2862, 439,2409,1566,1240,1622, 926,1282,1907,2764, 654,2210,1607, 327,1130, # 3632 +3956,1678,1623,6170,2434,2192, 686, 608,3831,3715, 903,3957,3042,6171,2741,1522, # 3648 +1915,1105,1555,2552,1359, 323,3251,4346,3457, 738,1354,2553,2311,2334,1828,2003, # 3664 +3832,1753,2351,1227,6172,1887,4125,1478,6173,2410,1874,1712,1847, 520,1204,2607, # 3680 + 264,4659, 836,2677,2102, 600,4660,3833,2278,3084,6174,4347,3615,1342, 640, 532, # 3696 + 543,2608,1888,2400,2591,1009,4348,1497, 341,1737,3616,2723,1394, 529,3252,1321, # 3712 + 983,4661,1515,2120, 971,2592, 924, 287,1662,3186,4349,2700,4350,1519, 908,1948, # 3728 +2452, 156, 796,1629,1486,2223,2055, 694,4126,1259,1036,3392,1213,2249,2742,1889, # 3744 +1230,3958,1015, 910, 408, 559,3617,4662, 746, 725, 935,4663,3959,3009,1289, 563, # 3760 + 867,4664,3960,1567,2981,2038,2626, 988,2263,2381,4351, 143,2374, 704,1895,6175, # 3776 +1188,3716,2088, 673,3085,2362,4352, 484,1608,1921,2765,2918, 215, 904,3618,3537, # 3792 + 894, 509, 976,3043,2701,3961,4353,2837,2982, 498,6176,6177,1102,3538,1332,3393, # 3808 +1487,1636,1637, 233, 245,3962, 383, 650, 995,3044, 460,1520,1206,2352, 749,3327, # 3824 + 530, 700, 389,1438,1560,1773,3963,2264, 719,2951,2724,3834, 870,1832,1644,1000, # 3840 + 839,2474,3717, 197,1630,3394, 365,2886,3964,1285,2133, 734, 922, 818,1106, 732, # 3856 + 480,2083,1774,3458, 923,2279,1350, 221,3086, 85,2233,2234,3835,1585,3010,2147, # 3872 +1387,1705,2382,1619,2475, 133, 239,2802,1991,1016,2084,2383, 411,2838,1113, 651, # 3888 +1985,1160,3328, 990,1863,3087,1048,1276,2647, 265,2627,1599,3253,2056, 150, 638, # 3904 +2019, 656, 853, 326,1479, 680,1439,4354,1001,1759, 413,3459,3395,2492,1431, 459, # 3920 +4355,1125,3329,2265,1953,1450,2065,2863, 849, 351,2678,3131,3254,3255,1104,1577, # 3936 + 227,1351,1645,2453,2193,1421,2887, 812,2121, 634, 95,2435, 201,2312,4665,1646, # 3952 +1671,2743,1601,2554,2702,2648,2280,1315,1366,2089,3132,1573,3718,3965,1729,1189, # 3968 + 328,2679,1077,1940,1136, 558,1283, 964,1195, 621,2074,1199,1743,3460,3619,1896, # 3984 +1916,1890,3836,2952,1154,2112,1064, 862, 378,3011,2066,2113,2803,1568,2839,6178, # 4000 +3088,2919,1941,1660,2004,1992,2194, 142, 707,1590,1708,1624,1922,1023,1836,1233, # 4016 +1004,2313, 789, 741,3620,6179,1609,2411,1200,4127,3719,3720,4666,2057,3721, 593, # 4032 +2840, 367,2920,1878,6180,3461,1521, 628,1168, 692,2211,2649, 300, 720,2067,2571, # 4048 +2953,3396, 959,2504,3966,3539,3462,1977, 701,6181, 954,1043, 800, 681, 183,3722, # 4064 +1803,1730,3540,4128,2103, 815,2314, 174, 467, 230,2454,1093,2134, 755,3541,3397, # 4080 +1141,1162,6182,1738,2039, 270,3256,2513,1005,1647,2185,3837, 858,1679,1897,1719, # 4096 +2954,2324,1806, 402, 670, 167,4129,1498,2158,2104, 750,6183, 915, 189,1680,1551, # 4112 + 455,4356,1501,2455, 405,1095,2955, 338,1586,1266,1819, 570, 641,1324, 237,1556, # 4128 +2650,1388,3723,6184,1368,2384,1343,1978,3089,2436, 879,3724, 792,1191, 758,3012, # 4144 +1411,2135,1322,4357, 240,4667,1848,3725,1574,6185, 420,3045,1546,1391, 714,4358, # 4160 +1967, 941,1864, 863, 664, 426, 560,1731,2680,1785,2864,1949,2363, 403,3330,1415, # 4176 +1279,2136,1697,2335, 204, 721,2097,3838, 90,6186,2085,2505, 191,3967, 124,2148, # 4192 +1376,1798,1178,1107,1898,1405, 860,4359,1243,1272,2375,2983,1558,2456,1638, 113, # 4208 +3621, 578,1923,2609, 880, 386,4130, 784,2186,2266,1422,2956,2172,1722, 497, 263, # 4224 +2514,1267,2412,2610, 177,2703,3542, 774,1927,1344, 616,1432,1595,1018, 172,4360, # 4240 +2325, 911,4361, 438,1468,3622, 794,3968,2024,2173,1681,1829,2957, 945, 895,3090, # 4256 + 575,2212,2476, 475,2401,2681, 785,2744,1745,2293,2555,1975,3133,2865, 394,4668, # 4272 +3839, 635,4131, 639, 202,1507,2195,2766,1345,1435,2572,3726,1908,1184,1181,2457, # 4288 +3727,3134,4362, 843,2611, 437, 916,4669, 234, 769,1884,3046,3047,3623, 833,6187, # 4304 +1639,2250,2402,1355,1185,2010,2047, 999, 525,1732,1290,1488,2612, 948,1578,3728, # 4320 +2413,2477,1216,2725,2159, 334,3840,1328,3624,2921,1525,4132, 564,1056, 891,4363, # 4336 +1444,1698,2385,2251,3729,1365,2281,2235,1717,6188, 864,3841,2515, 444, 527,2767, # 4352 +2922,3625, 544, 461,6189, 566, 209,2437,3398,2098,1065,2068,3331,3626,3257,2137, # 4368 #last 512 +) + + diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/jpcntx.py b/venv/Lib/site-packages/pip/_vendor/chardet/jpcntx.py new file mode 100644 index 0000000..20044e4 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/jpcntx.py @@ -0,0 +1,233 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + + +# This is hiragana 2-char sequence table, the number in each cell represents its frequency category +jp2CharContext = ( +(0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1), +(2,4,0,4,0,3,0,4,0,3,4,4,4,2,4,3,3,4,3,2,3,3,4,2,3,3,3,2,4,1,4,3,3,1,5,4,3,4,3,4,3,5,3,0,3,5,4,2,0,3,1,0,3,3,0,3,3,0,1,1,0,4,3,0,3,3,0,4,0,2,0,3,5,5,5,5,4,0,4,1,0,3,4), +(0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2), +(0,4,0,5,0,5,0,4,0,4,5,4,4,3,5,3,5,1,5,3,4,3,4,4,3,4,3,3,4,3,5,4,4,3,5,5,3,5,5,5,3,5,5,3,4,5,5,3,1,3,2,0,3,4,0,4,2,0,4,2,1,5,3,2,3,5,0,4,0,2,0,5,4,4,5,4,5,0,4,0,0,4,4), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,3,0,4,0,3,0,3,0,4,5,4,3,3,3,3,4,3,5,4,4,3,5,4,4,3,4,3,4,4,4,4,5,3,4,4,3,4,5,5,4,5,5,1,4,5,4,3,0,3,3,1,3,3,0,4,4,0,3,3,1,5,3,3,3,5,0,4,0,3,0,4,4,3,4,3,3,0,4,1,1,3,4), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,4,0,3,0,3,0,4,0,3,4,4,3,2,2,1,2,1,3,1,3,3,3,3,3,4,3,1,3,3,5,3,3,0,4,3,0,5,4,3,3,5,4,4,3,4,4,5,0,1,2,0,1,2,0,2,2,0,1,0,0,5,2,2,1,4,0,3,0,1,0,4,4,3,5,4,3,0,2,1,0,4,3), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,3,0,5,0,4,0,2,1,4,4,2,4,1,4,2,4,2,4,3,3,3,4,3,3,3,3,1,4,2,3,3,3,1,4,4,1,1,1,4,3,3,2,0,2,4,3,2,0,3,3,0,3,1,1,0,0,0,3,3,0,4,2,2,3,4,0,4,0,3,0,4,4,5,3,4,4,0,3,0,0,1,4), +(1,4,0,4,0,4,0,4,0,3,5,4,4,3,4,3,5,4,3,3,4,3,5,4,4,4,4,3,4,2,4,3,3,1,5,4,3,2,4,5,4,5,5,4,4,5,4,4,0,3,2,2,3,3,0,4,3,1,3,2,1,4,3,3,4,5,0,3,0,2,0,4,5,5,4,5,4,0,4,0,0,5,4), +(0,5,0,5,0,4,0,3,0,4,4,3,4,3,3,3,4,0,4,4,4,3,4,3,4,3,3,1,4,2,4,3,4,0,5,4,1,4,5,4,4,5,3,2,4,3,4,3,2,4,1,3,3,3,2,3,2,0,4,3,3,4,3,3,3,4,0,4,0,3,0,4,5,4,4,4,3,0,4,1,0,1,3), +(0,3,1,4,0,3,0,2,0,3,4,4,3,1,4,2,3,3,4,3,4,3,4,3,4,4,3,2,3,1,5,4,4,1,4,4,3,5,4,4,3,5,5,4,3,4,4,3,1,2,3,1,2,2,0,3,2,0,3,1,0,5,3,3,3,4,3,3,3,3,4,4,4,4,5,4,2,0,3,3,2,4,3), +(0,2,0,3,0,1,0,1,0,0,3,2,0,0,2,0,1,0,2,1,3,3,3,1,2,3,1,0,1,0,4,2,1,1,3,3,0,4,3,3,1,4,3,3,0,3,3,2,0,0,0,0,1,0,0,2,0,0,0,0,0,4,1,0,2,3,2,2,2,1,3,3,3,4,4,3,2,0,3,1,0,3,3), +(0,4,0,4,0,3,0,3,0,4,4,4,3,3,3,3,3,3,4,3,4,2,4,3,4,3,3,2,4,3,4,5,4,1,4,5,3,5,4,5,3,5,4,0,3,5,5,3,1,3,3,2,2,3,0,3,4,1,3,3,2,4,3,3,3,4,0,4,0,3,0,4,5,4,4,5,3,0,4,1,0,3,4), +(0,2,0,3,0,3,0,0,0,2,2,2,1,0,1,0,0,0,3,0,3,0,3,0,1,3,1,0,3,1,3,3,3,1,3,3,3,0,1,3,1,3,4,0,0,3,1,1,0,3,2,0,0,0,0,1,3,0,1,0,0,3,3,2,0,3,0,0,0,0,0,3,4,3,4,3,3,0,3,0,0,2,3), +(2,3,0,3,0,2,0,1,0,3,3,4,3,1,3,1,1,1,3,1,4,3,4,3,3,3,0,0,3,1,5,4,3,1,4,3,2,5,5,4,4,4,4,3,3,4,4,4,0,2,1,1,3,2,0,1,2,0,0,1,0,4,1,3,3,3,0,3,0,1,0,4,4,4,5,5,3,0,2,0,0,4,4), +(0,2,0,1,0,3,1,3,0,2,3,3,3,0,3,1,0,0,3,0,3,2,3,1,3,2,1,1,0,0,4,2,1,0,2,3,1,4,3,2,0,4,4,3,1,3,1,3,0,1,0,0,1,0,0,0,1,0,0,0,0,4,1,1,1,2,0,3,0,0,0,3,4,2,4,3,2,0,1,0,0,3,3), +(0,1,0,4,0,5,0,4,0,2,4,4,2,3,3,2,3,3,5,3,3,3,4,3,4,2,3,0,4,3,3,3,4,1,4,3,2,1,5,5,3,4,5,1,3,5,4,2,0,3,3,0,1,3,0,4,2,0,1,3,1,4,3,3,3,3,0,3,0,1,0,3,4,4,4,5,5,0,3,0,1,4,5), +(0,2,0,3,0,3,0,0,0,2,3,1,3,0,4,0,1,1,3,0,3,4,3,2,3,1,0,3,3,2,3,1,3,0,2,3,0,2,1,4,1,2,2,0,0,3,3,0,0,2,0,0,0,1,0,0,0,0,2,2,0,3,2,1,3,3,0,2,0,2,0,0,3,3,1,2,4,0,3,0,2,2,3), +(2,4,0,5,0,4,0,4,0,2,4,4,4,3,4,3,3,3,1,2,4,3,4,3,4,4,5,0,3,3,3,3,2,0,4,3,1,4,3,4,1,4,4,3,3,4,4,3,1,2,3,0,4,2,0,4,1,0,3,3,0,4,3,3,3,4,0,4,0,2,0,3,5,3,4,5,2,0,3,0,0,4,5), +(0,3,0,4,0,1,0,1,0,1,3,2,2,1,3,0,3,0,2,0,2,0,3,0,2,0,0,0,1,0,1,1,0,0,3,1,0,0,0,4,0,3,1,0,2,1,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,4,2,2,3,1,0,3,0,0,0,1,4,4,4,3,0,0,4,0,0,1,4), +(1,4,1,5,0,3,0,3,0,4,5,4,4,3,5,3,3,4,4,3,4,1,3,3,3,3,2,1,4,1,5,4,3,1,4,4,3,5,4,4,3,5,4,3,3,4,4,4,0,3,3,1,2,3,0,3,1,0,3,3,0,5,4,4,4,4,4,4,3,3,5,4,4,3,3,5,4,0,3,2,0,4,4), +(0,2,0,3,0,1,0,0,0,1,3,3,3,2,4,1,3,0,3,1,3,0,2,2,1,1,0,0,2,0,4,3,1,0,4,3,0,4,4,4,1,4,3,1,1,3,3,1,0,2,0,0,1,3,0,0,0,0,2,0,0,4,3,2,4,3,5,4,3,3,3,4,3,3,4,3,3,0,2,1,0,3,3), +(0,2,0,4,0,3,0,2,0,2,5,5,3,4,4,4,4,1,4,3,3,0,4,3,4,3,1,3,3,2,4,3,0,3,4,3,0,3,4,4,2,4,4,0,4,5,3,3,2,2,1,1,1,2,0,1,5,0,3,3,2,4,3,3,3,4,0,3,0,2,0,4,4,3,5,5,0,0,3,0,2,3,3), +(0,3,0,4,0,3,0,1,0,3,4,3,3,1,3,3,3,0,3,1,3,0,4,3,3,1,1,0,3,0,3,3,0,0,4,4,0,1,5,4,3,3,5,0,3,3,4,3,0,2,0,1,1,1,0,1,3,0,1,2,1,3,3,2,3,3,0,3,0,1,0,1,3,3,4,4,1,0,1,2,2,1,3), +(0,1,0,4,0,4,0,3,0,1,3,3,3,2,3,1,1,0,3,0,3,3,4,3,2,4,2,0,1,0,4,3,2,0,4,3,0,5,3,3,2,4,4,4,3,3,3,4,0,1,3,0,0,1,0,0,1,0,0,0,0,4,2,3,3,3,0,3,0,0,0,4,4,4,5,3,2,0,3,3,0,3,5), +(0,2,0,3,0,0,0,3,0,1,3,0,2,0,0,0,1,0,3,1,1,3,3,0,0,3,0,0,3,0,2,3,1,0,3,1,0,3,3,2,0,4,2,2,0,2,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,2,1,2,0,1,0,1,0,0,0,1,3,1,2,0,0,0,1,0,0,1,4), +(0,3,0,3,0,5,0,1,0,2,4,3,1,3,3,2,1,1,5,2,1,0,5,1,2,0,0,0,3,3,2,2,3,2,4,3,0,0,3,3,1,3,3,0,2,5,3,4,0,3,3,0,1,2,0,2,2,0,3,2,0,2,2,3,3,3,0,2,0,1,0,3,4,4,2,5,4,0,3,0,0,3,5), +(0,3,0,3,0,3,0,1,0,3,3,3,3,0,3,0,2,0,2,1,1,0,2,0,1,0,0,0,2,1,0,0,1,0,3,2,0,0,3,3,1,2,3,1,0,3,3,0,0,1,0,0,0,0,0,2,0,0,0,0,0,2,3,1,2,3,0,3,0,1,0,3,2,1,0,4,3,0,1,1,0,3,3), +(0,4,0,5,0,3,0,3,0,4,5,5,4,3,5,3,4,3,5,3,3,2,5,3,4,4,4,3,4,3,4,5,5,3,4,4,3,4,4,5,4,4,4,3,4,5,5,4,2,3,4,2,3,4,0,3,3,1,4,3,2,4,3,3,5,5,0,3,0,3,0,5,5,5,5,4,4,0,4,0,1,4,4), +(0,4,0,4,0,3,0,3,0,3,5,4,4,2,3,2,5,1,3,2,5,1,4,2,3,2,3,3,4,3,3,3,3,2,5,4,1,3,3,5,3,4,4,0,4,4,3,1,1,3,1,0,2,3,0,2,3,0,3,0,0,4,3,1,3,4,0,3,0,2,0,4,4,4,3,4,5,0,4,0,0,3,4), +(0,3,0,3,0,3,1,2,0,3,4,4,3,3,3,0,2,2,4,3,3,1,3,3,3,1,1,0,3,1,4,3,2,3,4,4,2,4,4,4,3,4,4,3,2,4,4,3,1,3,3,1,3,3,0,4,1,0,2,2,1,4,3,2,3,3,5,4,3,3,5,4,4,3,3,0,4,0,3,2,2,4,4), +(0,2,0,1,0,0,0,0,0,1,2,1,3,0,0,0,0,0,2,0,1,2,1,0,0,1,0,0,0,0,3,0,0,1,0,1,1,3,1,0,0,0,1,1,0,1,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,1,2,2,0,3,4,0,0,0,1,1,0,0,1,0,0,0,0,0,1,1), +(0,1,0,0,0,1,0,0,0,0,4,0,4,1,4,0,3,0,4,0,3,0,4,0,3,0,3,0,4,1,5,1,4,0,0,3,0,5,0,5,2,0,1,0,0,0,2,1,4,0,1,3,0,0,3,0,0,3,1,1,4,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0), +(1,4,0,5,0,3,0,2,0,3,5,4,4,3,4,3,5,3,4,3,3,0,4,3,3,3,3,3,3,2,4,4,3,1,3,4,4,5,4,4,3,4,4,1,3,5,4,3,3,3,1,2,2,3,3,1,3,1,3,3,3,5,3,3,4,5,0,3,0,3,0,3,4,3,4,4,3,0,3,0,2,4,3), +(0,1,0,4,0,0,0,0,0,1,4,0,4,1,4,2,4,0,3,0,1,0,1,0,0,0,0,0,2,0,3,1,1,1,0,3,0,0,0,1,2,1,0,0,1,1,1,1,0,1,0,0,0,1,0,0,3,0,0,0,0,3,2,0,2,2,0,1,0,0,0,2,3,2,3,3,0,0,0,0,2,1,0), +(0,5,1,5,0,3,0,3,0,5,4,4,5,1,5,3,3,0,4,3,4,3,5,3,4,3,3,2,4,3,4,3,3,0,3,3,1,4,4,3,4,4,4,3,4,5,5,3,2,3,1,1,3,3,1,3,1,1,3,3,2,4,5,3,3,5,0,4,0,3,0,4,4,3,5,3,3,0,3,4,0,4,3), +(0,5,0,5,0,3,0,2,0,4,4,3,5,2,4,3,3,3,4,4,4,3,5,3,5,3,3,1,4,0,4,3,3,0,3,3,0,4,4,4,4,5,4,3,3,5,5,3,2,3,1,2,3,2,0,1,0,0,3,2,2,4,4,3,1,5,0,4,0,3,0,4,3,1,3,2,1,0,3,3,0,3,3), +(0,4,0,5,0,5,0,4,0,4,5,5,5,3,4,3,3,2,5,4,4,3,5,3,5,3,4,0,4,3,4,4,3,2,4,4,3,4,5,4,4,5,5,0,3,5,5,4,1,3,3,2,3,3,1,3,1,0,4,3,1,4,4,3,4,5,0,4,0,2,0,4,3,4,4,3,3,0,4,0,0,5,5), +(0,4,0,4,0,5,0,1,1,3,3,4,4,3,4,1,3,0,5,1,3,0,3,1,3,1,1,0,3,0,3,3,4,0,4,3,0,4,4,4,3,4,4,0,3,5,4,1,0,3,0,0,2,3,0,3,1,0,3,1,0,3,2,1,3,5,0,3,0,1,0,3,2,3,3,4,4,0,2,2,0,4,4), +(2,4,0,5,0,4,0,3,0,4,5,5,4,3,5,3,5,3,5,3,5,2,5,3,4,3,3,4,3,4,5,3,2,1,5,4,3,2,3,4,5,3,4,1,2,5,4,3,0,3,3,0,3,2,0,2,3,0,4,1,0,3,4,3,3,5,0,3,0,1,0,4,5,5,5,4,3,0,4,2,0,3,5), +(0,5,0,4,0,4,0,2,0,5,4,3,4,3,4,3,3,3,4,3,4,2,5,3,5,3,4,1,4,3,4,4,4,0,3,5,0,4,4,4,4,5,3,1,3,4,5,3,3,3,3,3,3,3,0,2,2,0,3,3,2,4,3,3,3,5,3,4,1,3,3,5,3,2,0,0,0,0,4,3,1,3,3), +(0,1,0,3,0,3,0,1,0,1,3,3,3,2,3,3,3,0,3,0,0,0,3,1,3,0,0,0,2,2,2,3,0,0,3,2,0,1,2,4,1,3,3,0,0,3,3,3,0,1,0,0,2,1,0,0,3,0,3,1,0,3,0,0,1,3,0,2,0,1,0,3,3,1,3,3,0,0,1,1,0,3,3), +(0,2,0,3,0,2,1,4,0,2,2,3,1,1,3,1,1,0,2,0,3,1,2,3,1,3,0,0,1,0,4,3,2,3,3,3,1,4,2,3,3,3,3,1,0,3,1,4,0,1,1,0,1,2,0,1,1,0,1,1,0,3,1,3,2,2,0,1,0,0,0,2,3,3,3,1,0,0,0,0,0,2,3), +(0,5,0,4,0,5,0,2,0,4,5,5,3,3,4,3,3,1,5,4,4,2,4,4,4,3,4,2,4,3,5,5,4,3,3,4,3,3,5,5,4,5,5,1,3,4,5,3,1,4,3,1,3,3,0,3,3,1,4,3,1,4,5,3,3,5,0,4,0,3,0,5,3,3,1,4,3,0,4,0,1,5,3), +(0,5,0,5,0,4,0,2,0,4,4,3,4,3,3,3,3,3,5,4,4,4,4,4,4,5,3,3,5,2,4,4,4,3,4,4,3,3,4,4,5,5,3,3,4,3,4,3,3,4,3,3,3,3,1,2,2,1,4,3,3,5,4,4,3,4,0,4,0,3,0,4,4,4,4,4,1,0,4,2,0,2,4), +(0,4,0,4,0,3,0,1,0,3,5,2,3,0,3,0,2,1,4,2,3,3,4,1,4,3,3,2,4,1,3,3,3,0,3,3,0,0,3,3,3,5,3,3,3,3,3,2,0,2,0,0,2,0,0,2,0,0,1,0,0,3,1,2,2,3,0,3,0,2,0,4,4,3,3,4,1,0,3,0,0,2,4), +(0,0,0,4,0,0,0,0,0,0,1,0,1,0,2,0,0,0,0,0,1,0,2,0,1,0,0,0,0,0,3,1,3,0,3,2,0,0,0,1,0,3,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,0,2,0,0,0,0,0,0,2), +(0,2,1,3,0,2,0,2,0,3,3,3,3,1,3,1,3,3,3,3,3,3,4,2,2,1,2,1,4,0,4,3,1,3,3,3,2,4,3,5,4,3,3,3,3,3,3,3,0,1,3,0,2,0,0,1,0,0,1,0,0,4,2,0,2,3,0,3,3,0,3,3,4,2,3,1,4,0,1,2,0,2,3), +(0,3,0,3,0,1,0,3,0,2,3,3,3,0,3,1,2,0,3,3,2,3,3,2,3,2,3,1,3,0,4,3,2,0,3,3,1,4,3,3,2,3,4,3,1,3,3,1,1,0,1,1,0,1,0,1,0,1,0,0,0,4,1,1,0,3,0,3,1,0,2,3,3,3,3,3,1,0,0,2,0,3,3), +(0,0,0,0,0,0,0,0,0,0,3,0,2,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,3,0,3,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,2,0,2,3,0,0,0,0,0,0,0,0,3), +(0,2,0,3,1,3,0,3,0,2,3,3,3,1,3,1,3,1,3,1,3,3,3,1,3,0,2,3,1,1,4,3,3,2,3,3,1,2,2,4,1,3,3,0,1,4,2,3,0,1,3,0,3,0,0,1,3,0,2,0,0,3,3,2,1,3,0,3,0,2,0,3,4,4,4,3,1,0,3,0,0,3,3), +(0,2,0,1,0,2,0,0,0,1,3,2,2,1,3,0,1,1,3,0,3,2,3,1,2,0,2,0,1,1,3,3,3,0,3,3,1,1,2,3,2,3,3,1,2,3,2,0,0,1,0,0,0,0,0,0,3,0,1,0,0,2,1,2,1,3,0,3,0,0,0,3,4,4,4,3,2,0,2,0,0,2,4), +(0,0,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,3,1,0,0,0,0,0,0,0,3), +(0,3,0,3,0,2,0,3,0,3,3,3,2,3,2,2,2,0,3,1,3,3,3,2,3,3,0,0,3,0,3,2,2,0,2,3,1,4,3,4,3,3,2,3,1,5,4,4,0,3,1,2,1,3,0,3,1,1,2,0,2,3,1,3,1,3,0,3,0,1,0,3,3,4,4,2,1,0,2,1,0,2,4), +(0,1,0,3,0,1,0,2,0,1,4,2,5,1,4,0,2,0,2,1,3,1,4,0,2,1,0,0,2,1,4,1,1,0,3,3,0,5,1,3,2,3,3,1,0,3,2,3,0,1,0,0,0,0,0,0,1,0,0,0,0,4,0,1,0,3,0,2,0,1,0,3,3,3,4,3,3,0,0,0,0,2,3), +(0,0,0,1,0,0,0,0,0,0,2,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,1,0,0,0,0,0,3), +(0,1,0,3,0,4,0,3,0,2,4,3,1,0,3,2,2,1,3,1,2,2,3,1,1,1,2,1,3,0,1,2,0,1,3,2,1,3,0,5,5,1,0,0,1,3,2,1,0,3,0,0,1,0,0,0,0,0,3,4,0,1,1,1,3,2,0,2,0,1,0,2,3,3,1,2,3,0,1,0,1,0,4), +(0,0,0,1,0,3,0,3,0,2,2,1,0,0,4,0,3,0,3,1,3,0,3,0,3,0,1,0,3,0,3,1,3,0,3,3,0,0,1,2,1,1,1,0,1,2,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,2,2,1,2,0,0,2,0,0,0,0,2,3,3,3,3,0,0,0,0,1,4), +(0,0,0,3,0,3,0,0,0,0,3,1,1,0,3,0,1,0,2,0,1,0,0,0,0,0,0,0,1,0,3,0,2,0,2,3,0,0,2,2,3,1,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,2,3), +(2,4,0,5,0,5,0,4,0,3,4,3,3,3,4,3,3,3,4,3,4,4,5,4,5,5,5,2,3,0,5,5,4,1,5,4,3,1,5,4,3,4,4,3,3,4,3,3,0,3,2,0,2,3,0,3,0,0,3,3,0,5,3,2,3,3,0,3,0,3,0,3,4,5,4,5,3,0,4,3,0,3,4), +(0,3,0,3,0,3,0,3,0,3,3,4,3,2,3,2,3,0,4,3,3,3,3,3,3,3,3,0,3,2,4,3,3,1,3,4,3,4,4,4,3,4,4,3,2,4,4,1,0,2,0,0,1,1,0,2,0,0,3,1,0,5,3,2,1,3,0,3,0,1,2,4,3,2,4,3,3,0,3,2,0,4,4), +(0,3,0,3,0,1,0,0,0,1,4,3,3,2,3,1,3,1,4,2,3,2,4,2,3,4,3,0,2,2,3,3,3,0,3,3,3,0,3,4,1,3,3,0,3,4,3,3,0,1,1,0,1,0,0,0,4,0,3,0,0,3,1,2,1,3,0,4,0,1,0,4,3,3,4,3,3,0,2,0,0,3,3), +(0,3,0,4,0,1,0,3,0,3,4,3,3,0,3,3,3,1,3,1,3,3,4,3,3,3,0,0,3,1,5,3,3,1,3,3,2,5,4,3,3,4,5,3,2,5,3,4,0,1,0,0,0,0,0,2,0,0,1,1,0,4,2,2,1,3,0,3,0,2,0,4,4,3,5,3,2,0,1,1,0,3,4), +(0,5,0,4,0,5,0,2,0,4,4,3,3,2,3,3,3,1,4,3,4,1,5,3,4,3,4,0,4,2,4,3,4,1,5,4,0,4,4,4,4,5,4,1,3,5,4,2,1,4,1,1,3,2,0,3,1,0,3,2,1,4,3,3,3,4,0,4,0,3,0,4,4,4,3,3,3,0,4,2,0,3,4), +(1,4,0,4,0,3,0,1,0,3,3,3,1,1,3,3,2,2,3,3,1,0,3,2,2,1,2,0,3,1,2,1,2,0,3,2,0,2,2,3,3,4,3,0,3,3,1,2,0,1,1,3,1,2,0,0,3,0,1,1,0,3,2,2,3,3,0,3,0,0,0,2,3,3,4,3,3,0,1,0,0,1,4), +(0,4,0,4,0,4,0,0,0,3,4,4,3,1,4,2,3,2,3,3,3,1,4,3,4,0,3,0,4,2,3,3,2,2,5,4,2,1,3,4,3,4,3,1,3,3,4,2,0,2,1,0,3,3,0,0,2,0,3,1,0,4,4,3,4,3,0,4,0,1,0,2,4,4,4,4,4,0,3,2,0,3,3), +(0,0,0,1,0,4,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,3,2,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2), +(0,2,0,3,0,4,0,4,0,1,3,3,3,0,4,0,2,1,2,1,1,1,2,0,3,1,1,0,1,0,3,1,0,0,3,3,2,0,1,1,0,0,0,0,0,1,0,2,0,2,2,0,3,1,0,0,1,0,1,1,0,1,2,0,3,0,0,0,0,1,0,0,3,3,4,3,1,0,1,0,3,0,2), +(0,0,0,3,0,5,0,0,0,0,1,0,2,0,3,1,0,1,3,0,0,0,2,0,0,0,1,0,0,0,1,1,0,0,4,0,0,0,2,3,0,1,4,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,1,0,0,0,0,0,0,0,2,0,0,3,0,0,0,0,0,3), +(0,2,0,5,0,5,0,1,0,2,4,3,3,2,5,1,3,2,3,3,3,0,4,1,2,0,3,0,4,0,2,2,1,1,5,3,0,0,1,4,2,3,2,0,3,3,3,2,0,2,4,1,1,2,0,1,1,0,3,1,0,1,3,1,2,3,0,2,0,0,0,1,3,5,4,4,4,0,3,0,0,1,3), +(0,4,0,5,0,4,0,4,0,4,5,4,3,3,4,3,3,3,4,3,4,4,5,3,4,5,4,2,4,2,3,4,3,1,4,4,1,3,5,4,4,5,5,4,4,5,5,5,2,3,3,1,4,3,1,3,3,0,3,3,1,4,3,4,4,4,0,3,0,4,0,3,3,4,4,5,0,0,4,3,0,4,5), +(0,4,0,4,0,3,0,3,0,3,4,4,4,3,3,2,4,3,4,3,4,3,5,3,4,3,2,1,4,2,4,4,3,1,3,4,2,4,5,5,3,4,5,4,1,5,4,3,0,3,2,2,3,2,1,3,1,0,3,3,3,5,3,3,3,5,4,4,2,3,3,4,3,3,3,2,1,0,3,2,1,4,3), +(0,4,0,5,0,4,0,3,0,3,5,5,3,2,4,3,4,0,5,4,4,1,4,4,4,3,3,3,4,3,5,5,2,3,3,4,1,2,5,5,3,5,5,2,3,5,5,4,0,3,2,0,3,3,1,1,5,1,4,1,0,4,3,2,3,5,0,4,0,3,0,5,4,3,4,3,0,0,4,1,0,4,4), +(1,3,0,4,0,2,0,2,0,2,5,5,3,3,3,3,3,0,4,2,3,4,4,4,3,4,0,0,3,4,5,4,3,3,3,3,2,5,5,4,5,5,5,4,3,5,5,5,1,3,1,0,1,0,0,3,2,0,4,2,0,5,2,3,2,4,1,3,0,3,0,4,5,4,5,4,3,0,4,2,0,5,4), +(0,3,0,4,0,5,0,3,0,3,4,4,3,2,3,2,3,3,3,3,3,2,4,3,3,2,2,0,3,3,3,3,3,1,3,3,3,0,4,4,3,4,4,1,1,4,4,2,0,3,1,0,1,1,0,4,1,0,2,3,1,3,3,1,3,4,0,3,0,1,0,3,1,3,0,0,1,0,2,0,0,4,4), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,3,0,3,0,2,0,3,0,1,5,4,3,3,3,1,4,2,1,2,3,4,4,2,4,4,5,0,3,1,4,3,4,0,4,3,3,3,2,3,2,5,3,4,3,2,2,3,0,0,3,0,2,1,0,1,2,0,0,0,0,2,1,1,3,1,0,2,0,4,0,3,4,4,4,5,2,0,2,0,0,1,3), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,0,0,1,1,0,0,0,4,2,1,1,0,1,0,3,2,0,0,3,1,1,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,1,0,0,0,2,0,0,0,1,4,0,4,2,1,0,0,0,0,0,1), +(0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,1,0,0,0,0,3,1,0,0,0,2,0,2,1,0,0,1,2,1,0,1,1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,1,3,1,0,0,0,0,0,1,0,0,2,1,0,0,0,0,0,0,0,0,2), +(0,4,0,4,0,4,0,3,0,4,4,3,4,2,4,3,2,0,4,4,4,3,5,3,5,3,3,2,4,2,4,3,4,3,1,4,0,2,3,4,4,4,3,3,3,4,4,4,3,4,1,3,4,3,2,1,2,1,3,3,3,4,4,3,3,5,0,4,0,3,0,4,3,3,3,2,1,0,3,0,0,3,3), +(0,4,0,3,0,3,0,3,0,3,5,5,3,3,3,3,4,3,4,3,3,3,4,4,4,3,3,3,3,4,3,5,3,3,1,3,2,4,5,5,5,5,4,3,4,5,5,3,2,2,3,3,3,3,2,3,3,1,2,3,2,4,3,3,3,4,0,4,0,2,0,4,3,2,2,1,2,0,3,0,0,4,1), +) + +class JapaneseContextAnalysis(object): + NUM_OF_CATEGORY = 6 + DONT_KNOW = -1 + ENOUGH_REL_THRESHOLD = 100 + MAX_REL_THRESHOLD = 1000 + MINIMUM_DATA_THRESHOLD = 4 + + def __init__(self): + self._total_rel = None + self._rel_sample = None + self._need_to_skip_char_num = None + self._last_char_order = None + self._done = None + self.reset() + + def reset(self): + self._total_rel = 0 # total sequence received + # category counters, each integer counts sequence in its category + self._rel_sample = [0] * self.NUM_OF_CATEGORY + # if last byte in current buffer is not the last byte of a character, + # we need to know how many bytes to skip in next buffer + self._need_to_skip_char_num = 0 + self._last_char_order = -1 # The order of previous char + # If this flag is set to True, detection is done and conclusion has + # been made + self._done = False + + def feed(self, byte_str, num_bytes): + if self._done: + return + + # The buffer we got is byte oriented, and a character may span in more than one + # buffers. In case the last one or two byte in last buffer is not + # complete, we record how many byte needed to complete that character + # and skip these bytes here. We can choose to record those bytes as + # well and analyse the character once it is complete, but since a + # character will not make much difference, by simply skipping + # this character will simply our logic and improve performance. + i = self._need_to_skip_char_num + while i < num_bytes: + order, char_len = self.get_order(byte_str[i:i + 2]) + i += char_len + if i > num_bytes: + self._need_to_skip_char_num = i - num_bytes + self._last_char_order = -1 + else: + if (order != -1) and (self._last_char_order != -1): + self._total_rel += 1 + if self._total_rel > self.MAX_REL_THRESHOLD: + self._done = True + break + self._rel_sample[jp2CharContext[self._last_char_order][order]] += 1 + self._last_char_order = order + + def got_enough_data(self): + return self._total_rel > self.ENOUGH_REL_THRESHOLD + + def get_confidence(self): + # This is just one way to calculate confidence. It works well for me. + if self._total_rel > self.MINIMUM_DATA_THRESHOLD: + return (self._total_rel - self._rel_sample[0]) / self._total_rel + else: + return self.DONT_KNOW + + def get_order(self, byte_str): + return -1, 1 + +class SJISContextAnalysis(JapaneseContextAnalysis): + def __init__(self): + super(SJISContextAnalysis, self).__init__() + self._charset_name = "SHIFT_JIS" + + @property + def charset_name(self): + return self._charset_name + + def get_order(self, byte_str): + if not byte_str: + return -1, 1 + # find out current char's byte length + first_char = byte_str[0] + if (0x81 <= first_char <= 0x9F) or (0xE0 <= first_char <= 0xFC): + char_len = 2 + if (first_char == 0x87) or (0xFA <= first_char <= 0xFC): + self._charset_name = "CP932" + else: + char_len = 1 + + # return its order if it is hiragana + if len(byte_str) > 1: + second_char = byte_str[1] + if (first_char == 202) and (0x9F <= second_char <= 0xF1): + return second_char - 0x9F, char_len + + return -1, char_len + +class EUCJPContextAnalysis(JapaneseContextAnalysis): + def get_order(self, byte_str): + if not byte_str: + return -1, 1 + # find out current char's byte length + first_char = byte_str[0] + if (first_char == 0x8E) or (0xA1 <= first_char <= 0xFE): + char_len = 2 + elif first_char == 0x8F: + char_len = 3 + else: + char_len = 1 + + # return its order if it is hiragana + if len(byte_str) > 1: + second_char = byte_str[1] + if (first_char == 0xA4) and (0xA1 <= second_char <= 0xF3): + return second_char - 0xA1, char_len + + return -1, char_len + + diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py new file mode 100644 index 0000000..2aa4fb2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py @@ -0,0 +1,228 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +# this table is modified base on win1251BulgarianCharToOrderMap, so +# only number <64 is sure valid + +Latin5_BulgarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 +110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 +253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 +116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 +194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209, # 80 +210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225, # 90 + 81,226,227,228,229,230,105,231,232,233,234,235,236, 45,237,238, # a0 + 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # b0 + 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,239, 67,240, 60, 56, # c0 + 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # d0 + 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,241, 42, 16, # e0 + 62,242,243,244, 58,245, 98,246,247,248,249,250,251, 91,252,253, # f0 +) + +win1251BulgarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 +110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 +253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 +116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 +206,207,208,209,210,211,212,213,120,214,215,216,217,218,219,220, # 80 +221, 78, 64, 83,121, 98,117,105,222,223,224,225,226,227,228,229, # 90 + 88,230,231,232,233,122, 89,106,234,235,236,237,238, 45,239,240, # a0 + 73, 80,118,114,241,242,243,244,245, 62, 58,246,247,248,249,250, # b0 + 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # c0 + 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,251, 67,252, 60, 56, # d0 + 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # e0 + 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,253, 42, 16, # f0 +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 96.9392% +# first 1024 sequences:3.0618% +# rest sequences: 0.2992% +# negative sequences: 0.0020% +BulgarianLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,2,2,1,2,2, +3,1,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,0,1, +0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3,3,0,3,1,0, +0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,2,2,1,3,3,3,3,2,2,2,1,1,2,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,2,3,2,2,3,3,1,1,2,3,3,2,3,3,3,3,2,1,2,0,2,0,3,0,0, +0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,1,3,3,3,3,3,2,3,2,3,3,3,3,3,2,3,3,1,3,0,3,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,1,3,3,2,3,3,3,1,3,3,2,3,2,2,2,0,0,2,0,2,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,3,3,1,2,2,3,2,1,1,2,0,2,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,2,3,3,1,2,3,2,2,2,3,3,3,3,3,2,2,3,1,2,0,2,1,2,0,0, +0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,1,3,3,3,3,3,2,3,3,3,2,3,3,2,3,2,2,2,3,1,2,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,1,1,1,2,2,1,3,1,3,2,2,3,0,0,1,0,1,0,1,0,0, +0,0,0,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,2,2,3,2,2,3,1,2,1,1,1,2,3,1,3,1,2,2,0,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,1,3,2,2,3,3,1,2,3,1,1,3,3,3,3,1,2,2,1,1,1,0,2,0,2,0,1, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,2,2,3,3,3,2,2,1,1,2,0,2,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,0,1,2,1,3,3,2,3,3,3,3,3,2,3,2,1,0,3,1,2,1,2,1,2,3,2,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,1,3,3,2,3,3,2,2,2,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,0,3,3,3,3,3,2,1,1,2,1,3,3,0,3,1,1,1,1,3,2,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,1,1,3,1,3,3,2,3,2,2,2,3,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,3,2,2,3,2,1,1,1,1,1,3,1,3,1,1,0,0,0,1,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,2,0,3,2,0,3,0,2,0,0,2,1,3,1,0,0,1,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,2,1,1,1,1,2,1,1,2,1,1,1,2,2,1,2,1,1,1,0,1,1,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,2,1,3,1,1,2,1,3,2,1,1,0,1,2,3,2,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,2,2,1,0,1,0,0,1,0,0,0,2,1,0,3,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,2,3,2,3,3,1,3,2,1,1,1,2,1,1,2,1,3,0,1,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,2,2,3,3,2,3,2,2,2,3,1,2,2,1,1,2,1,1,2,2,0,1,1,0,1,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,3,1,0,2,2,1,3,2,1,0,0,2,0,2,0,1,0,0,0,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,1,2,0,2,3,1,2,3,2,0,1,3,1,2,1,1,1,0,0,1,0,0,2,2,2,3, +2,2,2,2,1,2,1,1,2,2,1,1,2,0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,1,0,1, +3,3,3,3,3,2,1,2,2,1,2,0,2,0,1,0,1,2,1,2,1,1,0,0,0,1,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,2,3,3,1,1,3,1,0,3,2,1,0,0,0,1,2,0,2,0,1,0,0,0,1,0,1,2,1,2,2, +1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,0,1,2,1,1,1,0,0,0,0,0,1,1,0,0, +3,1,0,1,0,2,3,2,2,2,3,2,2,2,2,2,1,0,2,1,2,1,1,1,0,1,2,1,2,2,2,1, +1,1,2,2,2,2,1,2,1,1,0,1,2,1,2,2,2,1,1,1,0,1,1,1,1,2,0,1,0,0,0,0, +2,3,2,3,3,0,0,2,1,0,2,1,0,0,0,0,2,3,0,2,0,0,0,0,0,1,0,0,2,0,1,2, +2,1,2,1,2,2,1,1,1,2,1,1,1,0,1,2,2,1,1,1,1,1,0,1,1,1,0,0,1,2,0,0, +3,3,2,2,3,0,2,3,1,1,2,0,0,0,1,0,0,2,0,2,0,0,0,1,0,1,0,1,2,0,2,2, +1,1,1,1,2,1,0,1,2,2,2,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,1,0,0, +2,3,2,3,3,0,0,3,0,1,1,0,1,0,0,0,2,2,1,2,0,0,0,0,0,0,0,0,2,0,1,2, +2,2,1,1,1,1,1,2,2,2,1,0,2,0,1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0, +3,3,3,3,2,2,2,2,2,0,2,1,1,1,1,2,1,2,1,1,0,2,0,1,0,1,0,0,2,0,1,2, +1,1,1,1,1,1,1,2,2,1,1,0,2,0,1,0,2,0,0,1,1,1,0,0,2,0,0,0,1,1,0,0, +2,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0,0,0,0,1,2,0,1,2, +2,2,2,1,1,2,1,1,2,2,2,1,2,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,0,0, +2,3,3,3,3,0,2,2,0,2,1,0,0,0,1,1,1,2,0,2,0,0,0,3,0,0,0,0,2,0,2,2, +1,1,1,2,1,2,1,1,2,2,2,1,2,0,1,1,1,0,1,1,1,1,0,2,1,0,0,0,1,1,0,0, +2,3,3,3,3,0,2,1,0,0,2,0,0,0,0,0,1,2,0,2,0,0,0,0,0,0,0,0,2,0,1,2, +1,1,1,2,1,1,1,1,2,2,2,0,1,0,1,1,1,0,0,1,1,1,0,0,1,0,0,0,0,1,0,0, +3,3,2,2,3,0,1,0,1,0,0,0,0,0,0,0,1,1,0,3,0,0,0,0,0,0,0,0,1,0,2,2, +1,1,1,1,1,2,1,1,2,2,1,2,2,1,0,1,1,1,1,1,0,1,0,0,1,0,0,0,1,1,0,0, +3,1,0,1,0,2,2,2,2,3,2,1,1,1,2,3,0,0,1,0,2,1,1,0,1,1,1,1,2,1,1,1, +1,2,2,1,2,1,2,2,1,1,0,1,2,1,2,2,1,1,1,0,0,1,1,1,2,1,0,1,0,0,0,0, +2,1,0,1,0,3,1,2,2,2,2,1,2,2,1,1,1,0,2,1,2,2,1,1,2,1,1,0,2,1,1,1, +1,2,2,2,2,2,2,2,1,2,0,1,1,0,2,1,1,1,1,1,0,0,1,1,1,1,0,1,0,0,0,0, +2,1,1,1,1,2,2,2,2,1,2,2,2,1,2,2,1,1,2,1,2,3,2,2,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,3,2,0,1,2,0,1,2,1,1,0,1,0,1,2,1,2,0,0,0,1,1,0,0,0,1,0,0,2, +1,1,0,0,1,1,0,1,1,1,1,0,2,0,1,1,1,0,0,1,1,0,0,0,0,1,0,0,0,1,0,0, +2,0,0,0,0,1,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,2,1,1,1, +1,2,2,2,2,1,1,2,1,2,1,1,1,0,2,1,2,1,1,1,0,2,1,1,1,1,0,1,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, +1,1,0,1,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,3,2,0,0,0,0,1,0,0,0,0,0,0,1,1,0,2,0,0,0,0,0,0,0,0,1,0,1,2, +1,1,1,1,1,1,0,0,2,2,2,2,2,0,1,1,0,1,1,1,1,1,0,0,1,0,0,0,1,1,0,1, +2,3,1,2,1,0,1,1,0,2,2,2,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,1,2, +1,1,1,1,2,1,1,1,1,1,1,1,1,0,1,1,0,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0, +2,2,2,2,2,0,0,2,0,0,2,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,0,2,2, +1,1,1,1,1,0,0,1,2,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,2,0,1,1,0,0,0,1,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,1,1, +0,0,0,1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,3,2,0,0,1,0,0,1,0,0,0,0,0,0,1,0,2,0,0,0,1,0,0,0,0,0,0,0,2, +1,1,0,0,1,0,0,0,1,1,0,0,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,1,2,2,2,1,2,1,2,2,1,1,2,1,1,1,0,1,1,1,1,2,0,1,0,1,1,1,1,0,1,1, +1,1,2,1,1,1,1,1,1,0,0,1,2,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0, +1,0,0,1,3,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,1,0,0,1,0,2,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,2,0,0,1, +0,2,0,1,0,0,1,1,2,0,1,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,1,1,0,2,1,0,1,1,1,0,0,1,0,2,0,1,0,0,0,0,0,0,0,0,0,1, +0,1,0,0,1,0,0,0,1,1,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,2,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, +0,1,0,1,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,0,1,2,1,1,1,1,1,1,2,2,1,0,0,1,0,1,0,0,0,0,1,1,1,1,0,0,0, +1,1,2,1,1,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,1,2,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +0,1,1,0,1,1,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, +1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,2,0,0,2,0,1,0,0,1,0,0,1, +1,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, +1,1,1,1,1,1,1,2,0,0,0,0,0,0,2,1,0,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +) + +Latin5BulgarianModel = { + 'char_to_order_map': Latin5_BulgarianCharToOrderMap, + 'precedence_matrix': BulgarianLangModel, + 'typical_positive_ratio': 0.969392, + 'keep_english_letter': False, + 'charset_name': "ISO-8859-5", + 'language': 'Bulgairan', +} + +Win1251BulgarianModel = { + 'char_to_order_map': win1251BulgarianCharToOrderMap, + 'precedence_matrix': BulgarianLangModel, + 'typical_positive_ratio': 0.969392, + 'keep_english_letter': False, + 'charset_name': "windows-1251", + 'language': 'Bulgarian', +} diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langcyrillicmodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langcyrillicmodel.py new file mode 100644 index 0000000..e5f9a1f --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langcyrillicmodel.py @@ -0,0 +1,333 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# KOI8-R language model +# Character Mapping Table: +KOI8R_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, # 80 +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, # 90 +223,224,225, 68,226,227,228,229,230,231,232,233,234,235,236,237, # a0 +238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253, # b0 + 27, 3, 21, 28, 13, 2, 39, 19, 26, 4, 23, 11, 8, 12, 5, 1, # c0 + 15, 16, 9, 7, 6, 14, 24, 10, 17, 18, 20, 25, 30, 29, 22, 54, # d0 + 59, 37, 44, 58, 41, 48, 53, 46, 55, 42, 60, 36, 49, 38, 31, 34, # e0 + 35, 43, 45, 32, 40, 52, 56, 33, 61, 62, 51, 57, 47, 63, 50, 70, # f0 +) + +win1251_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, +239,240,241,242,243,244,245,246, 68,247,248,249,250,251,252,253, + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +) + +latin5_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, +) + +macCyrillic_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, +239,240,241,242,243,244,245,246,247,248,249,250,251,252, 68, 16, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27,255, +) + +IBM855_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194, 68,195,196,197,198,199,200,201,202,203,204,205, +206,207,208,209,210,211,212,213,214,215,216,217, 27, 59, 54, 70, + 3, 37, 21, 44, 28, 58, 13, 41, 2, 48, 39, 53, 19, 46,218,219, +220,221,222,223,224, 26, 55, 4, 42,225,226,227,228, 23, 60,229, +230,231,232,233,234,235, 11, 36,236,237,238,239,240,241,242,243, + 8, 49, 12, 38, 5, 31, 1, 34, 15,244,245,246,247, 35, 16,248, + 43, 9, 45, 7, 32, 6, 40, 14, 52, 24, 56, 10, 33, 17, 61,249, +250, 18, 62, 20, 51, 25, 57, 30, 47, 29, 63, 22, 50,251,252,255, +) + +IBM866_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 97.6601% +# first 1024 sequences: 2.3389% +# rest sequences: 0.1237% +# negative sequences: 0.0009% +RussianLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,1,3,3,3,2,3,2,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,2,2,2,2,2,0,0,2, +3,3,3,2,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,2,3,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,2,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,2,3,3,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, +0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, +0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,2,2,2,3,1,3,3,1,3,3,3,3,2,2,3,0,2,2,2,3,3,2,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,3,3,2,2,3,2,3,3,3,2,1,2,2,0,1,2,2,2,2,2,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,3,0,2,2,3,3,2,1,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,1,2,3,2,2,3,2,3,3,3,3,2,2,3,0,3,2,2,3,1,1,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,3,3,3,3,2,2,2,0,3,3,3,2,2,2,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,2,3,2,2,0,1,3,2,1,2,2,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,2,1,1,3,0,1,1,1,1,2,1,1,0,2,2,2,1,2,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,2,2,2,2,1,3,2,3,2,3,2,1,2,2,0,1,1,2,1,2,1,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,2,3,3,3,2,2,2,2,0,2,2,2,2,3,1,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,2,3,2,2,3,3,3,3,3,3,3,3,3,1,3,2,0,0,3,3,3,3,2,3,3,3,3,2,3,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,3,2,2,3,3,0,2,1,0,3,2,3,2,3,0,0,1,2,0,0,1,0,1,2,1,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,3,0,2,3,3,3,3,2,3,3,3,3,1,2,2,0,0,2,3,2,2,2,3,2,3,2,2,3,0,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,0,2,3,2,3,0,1,2,3,3,2,0,2,3,0,0,2,3,2,2,0,1,3,1,3,2,2,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,3,0,2,3,3,3,3,3,3,3,3,2,1,3,2,0,0,2,2,3,3,3,2,3,3,0,2,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,2,2,2,3,3,0,0,1,1,1,1,1,2,0,0,1,1,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,0,3,2,3,3,2,3,2,0,2,1,0,1,1,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,2,2,2,2,3,1,3,2,3,1,1,2,1,0,2,2,2,2,1,3,1,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +2,2,3,3,3,3,3,1,2,2,1,3,1,0,3,0,0,3,0,0,0,1,1,0,1,2,1,0,0,0,0,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,2,1,1,3,3,3,2,2,1,2,2,3,1,1,2,0,0,2,2,1,3,0,0,2,1,1,2,1,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,3,3,3,1,2,2,2,1,2,1,3,3,1,1,2,1,2,1,2,2,0,2,0,0,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,3,2,1,3,2,2,3,2,0,3,2,0,3,0,1,0,1,1,0,0,1,1,1,1,0,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,3,3,3,2,2,2,3,3,1,2,1,2,1,0,1,0,1,1,0,1,0,0,2,1,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,1,1,2,1,2,3,3,2,2,1,2,2,3,0,2,1,0,0,2,2,3,2,1,2,2,2,2,2,3,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,1,1,0,1,1,2,2,1,1,3,0,0,1,3,1,1,1,0,0,0,1,0,1,1,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,3,3,3,2,0,0,0,2,1,0,1,0,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,0,2,3,2,2,2,1,2,2,2,1,2,1,0,0,1,1,1,0,2,0,1,1,1,0,0,1,1, +1,0,0,0,0,0,1,2,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,0,0,0,0,1,0,0,0,0,3,0,1,2,1,0,0,0,0,0,0,0,1,1,0,0,1,1, +1,0,1,0,1,2,0,0,1,1,2,1,0,1,1,1,1,0,1,1,1,1,0,1,0,0,1,0,0,1,1,0, +2,2,3,2,2,2,3,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,0,1,0,1,1,1,0,2,1, +1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,0,1,1,0, +3,3,3,2,2,2,2,3,2,2,1,1,2,2,2,2,1,1,3,1,2,1,2,0,0,1,1,0,1,0,2,1, +1,1,1,1,1,2,1,0,1,1,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,1,0, +2,0,0,1,0,3,2,2,2,2,1,2,1,2,1,2,0,0,0,2,1,2,2,1,1,2,2,0,1,1,0,2, +1,1,1,1,1,0,1,1,1,2,1,1,1,2,1,0,1,2,1,1,1,1,0,1,1,1,0,0,1,0,0,1, +1,3,2,2,2,1,1,1,2,3,0,0,0,0,2,0,2,2,1,0,0,0,0,0,0,1,0,0,0,0,1,1, +1,0,1,1,0,1,0,1,1,0,1,1,0,2,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, +2,3,2,3,2,1,2,2,2,2,1,0,0,0,2,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,2,1, +1,1,2,1,0,2,0,0,1,0,1,0,0,1,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,0, +3,0,0,1,0,2,2,2,3,2,2,2,2,2,2,2,0,0,0,2,1,2,1,1,1,2,2,0,0,0,1,2, +1,1,1,1,1,0,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1, +2,3,2,3,3,2,0,1,1,1,0,0,1,0,2,0,1,1,3,1,0,0,0,0,0,0,0,1,0,0,2,1, +1,1,1,1,1,1,1,0,1,0,1,1,1,1,0,1,1,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0, +2,3,3,3,3,1,2,2,2,2,0,1,1,0,2,1,1,1,2,1,0,1,1,0,0,1,0,1,0,0,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,2,0,0,1,1,2,2,1,0,0,2,0,1,1,3,0,0,1,0,0,0,0,0,1,0,1,2,1, +1,1,2,0,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,0,1,0,0,0,0,0,0,1,0,1,1,0, +1,3,2,3,2,1,0,0,2,2,2,0,1,0,2,0,1,1,1,0,1,0,0,0,3,0,1,1,0,0,2,1, +1,1,1,0,1,1,0,0,0,0,1,1,0,1,0,0,2,1,1,0,1,0,0,0,1,0,1,0,0,1,1,0, +3,1,2,1,1,2,2,2,2,2,2,1,2,2,1,1,0,0,0,2,2,2,0,0,0,1,2,1,0,1,0,1, +2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,2,1,1,1,0,1,0,1,1,0,1,1,1,0,0,1, +3,0,0,0,0,2,0,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,0,1,0,1,1,0,0,1,0,1, +1,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1, +1,3,3,2,2,0,0,0,2,2,0,0,0,1,2,0,1,1,2,0,0,0,0,0,0,0,0,1,0,0,2,1, +0,1,1,0,0,1,1,0,0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +2,3,2,3,2,0,0,0,0,1,1,0,0,0,2,0,2,0,2,0,0,0,0,0,1,0,0,1,0,0,1,1, +1,1,2,0,1,2,1,0,1,1,2,1,1,1,1,1,2,1,1,0,1,0,0,1,1,1,1,1,0,1,1,0, +1,3,2,2,2,1,0,0,2,2,1,0,1,2,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,1, +0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,0,2,3,1,2,2,2,2,2,2,1,1,0,0,0,1,0,1,0,2,1,1,1,0,0,0,0,1, +1,1,0,1,1,0,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +2,0,2,0,0,1,0,3,2,1,2,1,2,2,0,1,0,0,0,2,1,0,0,2,1,1,1,1,0,2,0,2, +2,1,1,1,1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,0,0,0,1,1,1,1,0,1,0,0,1, +1,2,2,2,2,1,0,0,1,0,0,0,0,0,2,0,1,1,1,1,0,0,0,0,1,0,1,2,0,0,2,0, +1,0,1,1,1,2,1,0,1,0,1,1,0,0,1,0,1,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0, +2,1,2,2,2,0,3,0,1,1,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,0,1,1,1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0, +1,2,2,3,2,2,0,0,1,1,2,0,1,2,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1, +0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, +2,2,1,1,2,1,2,2,2,2,2,1,2,2,0,1,0,0,0,1,2,2,2,1,2,1,1,1,1,1,2,1, +1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,0,1, +1,2,2,2,2,0,1,0,2,2,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +0,0,1,0,0,1,0,0,0,0,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,0,2,2,2,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1, +0,1,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,0,0,1,0,0,1,1,2,0,0,0,0,1,0,1,0,0,1,0,0,2,0,0,0,1, +0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,1,1,2,0,2,1,1,1,1,0,2,2,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1, +0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,0,2,1,2,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +1,0,0,0,0,2,0,1,2,1,0,1,1,1,0,1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1, +0,0,0,0,0,1,0,0,1,1,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1, +2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,1,0,1,0,1,0,0,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,0,1,1,0,1,0,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0, +0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, +) + +Koi8rModel = { + 'char_to_order_map': KOI8R_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "KOI8-R", + 'language': 'Russian', +} + +Win1251CyrillicModel = { + 'char_to_order_map': win1251_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "windows-1251", + 'language': 'Russian', +} + +Latin5CyrillicModel = { + 'char_to_order_map': latin5_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "ISO-8859-5", + 'language': 'Russian', +} + +MacCyrillicModel = { + 'char_to_order_map': macCyrillic_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "MacCyrillic", + 'language': 'Russian', +} + +Ibm866Model = { + 'char_to_order_map': IBM866_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "IBM866", + 'language': 'Russian', +} + +Ibm855Model = { + 'char_to_order_map': IBM855_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "IBM855", + 'language': 'Russian', +} diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py new file mode 100644 index 0000000..5332221 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py @@ -0,0 +1,225 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin7_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 + 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 +253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 + 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 +253,233, 90,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 +253,253,253,253,247,248, 61, 36, 46, 71, 73,253, 54,253,108,123, # b0 +110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 + 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 +124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 + 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 +) + +win1253_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 + 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 +253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 + 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 +253,233, 61,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 +253,253,253,253,247,253,253, 36, 46, 71, 73,253, 54,253,108,123, # b0 +110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 + 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 +124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 + 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 98.2851% +# first 1024 sequences:1.7001% +# rest sequences: 0.0359% +# negative sequences: 0.0148% +GreekLangModel = ( +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,2,2,3,3,3,3,3,3,3,3,1,3,3,3,0,2,2,3,3,0,3,0,3,2,0,3,3,3,0, +3,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,0,3,3,0,3,2,3,3,0,3,2,3,3,3,0,0,3,0,3,0,3,3,2,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,2,3,2,2,3,3,3,3,3,3,3,3,0,3,3,3,3,0,2,3,3,0,3,3,3,3,2,3,3,3,0, +2,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,2,1,3,3,3,3,2,3,3,2,3,3,2,0, +0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,2,3,3,0, +2,0,1,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,3,0,0,0,0,3,3,0,3,1,3,3,3,0,3,3,0,3,3,3,3,0,0,0,0, +2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,0,3,0,3,3,3,3,3,0,3,2,2,2,3,0,2,3,3,3,3,3,2,3,3,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,3,2,2,2,3,3,3,3,0,3,1,3,3,3,3,2,3,3,3,3,3,3,3,2,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,0,3,0,0,0,3,3,2,3,3,3,3,3,0,0,3,2,3,0,2,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,3,0,0,3,3,0,2,3,0,3,0,3,3,3,0,0,3,0,3,0,2,2,3,3,0,0, +0,0,1,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,0,3,2,3,3,3,3,0,3,3,3,3,3,0,3,3,2,3,2,3,3,2,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,2,3,2,3,3,3,3,3,3,0,2,3,2,3,2,2,2,3,2,3,3,2,3,0,2,2,2,3,0, +2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,0,3,3,3,2,3,3,0,0,3,0,3,0,0,0,3,2,0,3,0,3,0,0,2,0,2,0, +0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,0,0,0,3,3,0,3,3,3,0,0,1,2,3,0, +3,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,0,0,3,2,2,3,3,0,3,3,3,3,3,2,1,3,0,3,2,3,3,2,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,3,0,2,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,3,0,3,2,3,0,0,3,3,3,0, +3,0,0,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,0,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,2,0,3,2,3,0,0,3,2,3,0, +2,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,1,2,2,3,3,3,3,3,3,0,2,3,0,3,0,0,0,3,3,0,3,0,2,0,0,2,3,1,0, +2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,3,0,3,0,3,3,2,3,0,3,3,3,3,3,3,0,3,3,3,0,2,3,0,0,3,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,0,0,3,0,0,0,3,3,0,3,0,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,0,3,3,3,3,3,3,0,0,3,0,2,0,0,0,3,3,0,3,0,3,0,0,2,0,2,0, +0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,3,0,3,0,2,0,3,2,0,3,2,3,2,3,0,0,3,2,3,2,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,2,3,3,3,3,3,0,0,0,3,0,2,1,0,0,3,2,2,2,0,3,0,0,2,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,2,0,3,0,3,0,3,3,0,2,1,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,3,3,3,0,3,3,3,3,3,3,0,2,3,0,3,0,0,0,2,1,0,2,2,3,0,0,2,2,2,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,2,3,3,3,2,3,0,0,1,3,0,2,0,0,0,0,3,0,1,0,2,0,0,1,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,1,0,3,0,0,0,3,2,0,3,2,3,3,3,0,0,3,0,3,2,2,2,1,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,0,0,3,0,0,0,0,2,0,2,3,3,2,2,2,2,3,0,2,0,2,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,2,0,0,0,0,0,0,2,3,0,2,0,2,3,2,0,0,3,0,3,0,3,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,2,3,3,2,2,3,0,2,0,3,0,0,0,2,0,0,0,0,1,2,0,2,0,2,0, +0,2,0,2,0,2,2,0,0,1,0,2,2,2,0,2,2,2,0,2,2,2,0,0,2,0,0,1,0,0,0,0, +0,2,0,3,3,2,0,0,0,0,0,0,1,3,0,2,0,2,2,2,0,0,2,0,3,0,0,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,2,3,2,0,2,2,0,2,0,2,2,0,2,0,2,2,2,0,0,0,0,0,0,2,3,0,0,0,2, +0,1,2,0,0,0,0,2,2,0,0,0,2,1,0,2,2,0,0,0,0,0,0,1,0,2,0,0,0,0,0,0, +0,0,2,1,0,2,3,2,2,3,2,3,2,0,0,3,3,3,0,0,3,2,0,0,0,1,1,0,2,0,2,2, +0,2,0,2,0,2,2,0,0,2,0,2,2,2,0,2,2,2,2,0,0,2,0,0,0,2,0,1,0,0,0,0, +0,3,0,3,3,2,2,0,3,0,0,0,2,2,0,2,2,2,1,2,0,0,1,2,2,0,0,3,0,0,0,2, +0,1,2,0,0,0,1,2,0,0,0,0,0,0,0,2,2,0,1,0,0,2,0,0,0,2,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,3,3,2,2,0,0,0,2,0,2,3,3,0,2,0,0,0,0,0,0,2,2,2,0,2,2,0,2,0,2, +0,2,2,0,0,2,2,2,2,1,0,0,2,2,0,2,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0, +0,2,0,3,2,3,0,0,0,3,0,0,2,2,0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,0,2, +0,0,2,2,0,0,2,2,2,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,3,2,0,2,2,2,2,2,0,0,0,2,0,0,0,0,2,0,1,0,0,2,0,1,0,0,0, +0,2,2,2,0,2,2,0,1,2,0,2,2,2,0,2,2,2,2,1,2,2,0,0,2,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,2,0,2,0,2,2,0,0,0,0,1,2,1,0,0,2,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,3,2,3,0,0,2,0,0,0,2,2,0,2,0,0,0,1,0,0,2,0,2,0,2,2,0,0,0,0, +0,0,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0, +0,2,2,3,2,2,0,0,0,0,0,0,1,3,0,2,0,2,2,0,0,0,1,0,2,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,0,2,0,3,2,0,2,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,2,0,0,0,0,1,1,0,0,2,1,2,0,2,2,0,1,0,0,1,0,0,0,2,0,0,0,0,0,0, +0,3,0,2,2,2,0,0,2,0,0,0,2,0,0,0,2,3,0,2,0,0,0,0,0,0,2,2,0,0,0,2, +0,1,2,0,0,0,1,2,2,1,0,0,0,2,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,1,2,0,2,2,0,2,0,0,2,0,0,0,0,1,2,1,0,2,1,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,0,3,1,2,2,0,2,0,0,0,0,2,0,0,0,2,0,0,3,0,0,0,0,2,2,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,1,0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,2, +0,2,2,0,0,2,2,2,2,2,0,1,2,0,0,0,2,2,0,1,0,2,0,0,2,2,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,2, +0,1,2,0,0,0,0,2,2,1,0,1,0,1,0,2,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,2,0,0,2,2,0,0,0,0,1,0,0,0,0,0,0,2, +0,2,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0, +0,2,2,2,2,0,0,0,3,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,1, +0,0,2,0,0,0,0,1,2,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,2,2,2,0,0,0,2,0,0,0,0,0,0,0,0,2, +0,0,1,0,0,0,0,2,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,3,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,2, +0,0,2,0,0,0,0,2,2,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,0,2,2,1,0,0,0,0,0,0,2,0,0,2,0,2,2,2,0,0,0,0,0,0,2,0,0,0,0,2, +0,0,2,0,0,2,0,2,2,0,0,0,0,2,0,2,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0, +0,0,3,0,0,0,2,2,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0, +0,2,2,2,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1, +0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,2,0,0,0,2,0,0,0,0,0,1,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,2,0,0,0, +0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,2,0,2,0,0,0, +0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +) + +Latin7GreekModel = { + 'char_to_order_map': Latin7_char_to_order_map, + 'precedence_matrix': GreekLangModel, + 'typical_positive_ratio': 0.982851, + 'keep_english_letter': False, + 'charset_name': "ISO-8859-7", + 'language': 'Greek', +} + +Win1253GreekModel = { + 'char_to_order_map': win1253_char_to_order_map, + 'precedence_matrix': GreekLangModel, + 'typical_positive_ratio': 0.982851, + 'keep_english_letter': False, + 'charset_name': "windows-1253", + 'language': 'Greek', +} diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py new file mode 100644 index 0000000..58f4c87 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py @@ -0,0 +1,200 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Simon Montagu +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Shoshannah Forbes - original C code (?) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Windows-1255 language model +# Character Mapping Table: +WIN1255_CHAR_TO_ORDER_MAP = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 69, 91, 79, 80, 92, 89, 97, 90, 68,111,112, 82, 73, 95, 85, # 40 + 78,121, 86, 71, 67,102,107, 84,114,103,115,253,253,253,253,253, # 50 +253, 50, 74, 60, 61, 42, 76, 70, 64, 53,105, 93, 56, 65, 54, 49, # 60 + 66,110, 51, 43, 44, 63, 81, 77, 98, 75,108,253,253,253,253,253, # 70 +124,202,203,204,205, 40, 58,206,207,208,209,210,211,212,213,214, +215, 83, 52, 47, 46, 72, 32, 94,216,113,217,109,218,219,220,221, + 34,116,222,118,100,223,224,117,119,104,125,225,226, 87, 99,227, +106,122,123,228, 55,229,230,101,231,232,120,233, 48, 39, 57,234, + 30, 59, 41, 88, 33, 37, 36, 31, 29, 35,235, 62, 28,236,126,237, +238, 38, 45,239,240,241,242,243,127,244,245,246,247,248,249,250, + 9, 8, 20, 16, 3, 2, 24, 14, 22, 1, 25, 15, 4, 11, 6, 23, + 12, 19, 13, 26, 18, 27, 21, 17, 7, 10, 5,251,252,128, 96,253, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 98.4004% +# first 1024 sequences: 1.5981% +# rest sequences: 0.087% +# negative sequences: 0.0015% +HEBREW_LANG_MODEL = ( +0,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,3,2,1,2,0,1,0,0, +3,0,3,1,0,0,1,3,2,0,1,1,2,0,2,2,2,1,1,1,1,2,1,1,1,2,0,0,2,2,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, +1,2,1,2,1,2,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2, +1,2,1,3,1,1,0,0,2,0,0,0,1,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,1,2,2,1,3, +1,2,1,1,2,2,0,0,2,2,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,2,2,2,3,2, +1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,3,2,2,3,2,2,2,1,2,2,2,2, +1,2,1,1,2,2,0,1,2,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,0,2,2,2,2,2, +0,2,0,2,2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,0,2,2,2, +0,2,1,2,2,2,0,0,2,1,0,0,0,0,1,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1,2,3,2,2,2, +1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,2,0,2, +0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,2,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,2,2,3,2,1,2,1,1,1, +0,1,1,1,1,1,3,0,1,0,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,1,0,0,1,0,0,0,0, +0,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,2,1,2,3,3,2,3,3,3,3,2,3,2,1,2,0,2,1,2, +0,2,0,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,1,2,2,3,3,2,3,2,3,2,2,3,1,2,2,0,2,2,2, +0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,2,2,3,3,3,3,1,3,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,2,3,2,2,2,1,2,2,0,2,2,2,2, +0,2,0,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,1,3,2,3,3,2,3,3,2,2,1,2,2,2,2,2,2, +0,2,1,2,1,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,2,3,3,2,3,3,3,3,2,3,2,3,3,3,3,3,2,2,2,2,2,2,2,1, +0,2,0,1,2,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,1,2,3,3,3,3,3,3,3,2,3,2,3,2,1,2,3,0,2,1,2,2, +0,2,1,1,2,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,2,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,3,2,1,3,1,2,2,2,1,2,3,3,1,2,1,2,2,2,2, +0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,0,2,3,3,3,1,3,3,3,1,2,2,2,2,1,1,2,2,2,2,2,2, +0,2,0,1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,2,2,3,3,3,2,1,2,3,2,3,2,2,2,2,1,2,1,1,1,2,2, +0,2,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,0,0,0, +1,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,3,2,3,1,2,2,2,2,3,2,3,1,1,2,2,1,2,2,1,1,0,2,2,2,2, +0,1,0,1,2,2,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,0,0,1,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,0,1,0,1,1,0,1,1,0,0,0,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +3,2,2,1,2,2,2,2,2,2,2,1,2,2,1,2,2,1,1,1,1,1,1,1,1,2,1,1,0,3,3,3, +0,3,0,2,2,2,2,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,1,2,2,2,1,1,1,2,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,0,0,0,0, +0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,0,2,1,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,3,1,1,2,2,2,2,2,1,2,2,2,1,1,2,2,2,2,2,2,2,1,2,2,1,0,1,1,1,1,0, +0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,1,1,1,1,2,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,0,0, +2,1,1,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,1,2,1,2,1,1,1,1,0,0,0,0, +0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,1,2,1,1,2,1,1,1,2,1,2,1,2,0,1,0,1, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,1,2,2,2,1,2,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,2,1,2,1,1,0,1,0,1, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,1,1,1,1,1,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,1,1,1,0,1,0,0,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0, +0,1,1,1,2,1,2,2,2,0,2,0,2,0,1,1,2,1,1,1,1,2,1,0,1,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,1,0,0,0,0,0,1,0,1,2,2,0,1,0,0,1,1,2,2,1,2,0,2,0,0,0,1,2,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,2,1,2,0,2,0,0,1,1,1,1,1,1,0,1,0,0,0,1,0,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,1,2,2,0,0,1,0,0,0,1,0,0,1, +1,1,2,1,0,1,1,1,0,1,0,1,1,1,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,2,1, +0,2,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,0,0,1,0,1,1,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,1,0,0,0,1,1,0,1, +2,0,1,0,1,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,0,1,1,2,1,1,2,0,1,0,0,0,1,1,0,1, +1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,0,0,2,1,1,2,0,2,0,0,0,1,1,0,1, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,2,2,1,2,1,1,0,1,0,0,0,1,1,0,1, +2,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,1,0,1, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,2,1,1,1,0,2,1,1,0,0,0,2,1,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,0,2,1,1,0,1,0,0,0,1,1,0,1, +2,2,1,1,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,0,1,2,1,0,2,0,0,0,1,1,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0, +0,1,0,0,2,0,2,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,1,0,1,0,0,1,0,0,0,1,0,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,0,2,1,1,1,1,1,0,1,0,0,0,0,1,0,1, +0,1,1,1,2,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,1,0,0, +) + +Win1255HebrewModel = { + 'char_to_order_map': WIN1255_CHAR_TO_ORDER_MAP, + 'precedence_matrix': HEBREW_LANG_MODEL, + 'typical_positive_ratio': 0.984004, + 'keep_english_letter': False, + 'charset_name': "windows-1255", + 'language': 'Hebrew', +} diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py new file mode 100644 index 0000000..bb7c095 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py @@ -0,0 +1,225 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin2_HungarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, + 46, 71, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, +253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, + 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, +159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174, +175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190, +191,192,193,194,195,196,197, 75,198,199,200,201,202,203,204,205, + 79,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, +221, 51, 81,222, 78,223,224,225,226, 44,227,228,229, 61,230,231, +232,233,234, 58,235, 66, 59,236,237,238, 60, 69, 63,239,240,241, + 82, 14, 74,242, 70, 80,243, 72,244, 15, 83, 77, 84, 30, 76, 85, +245,246,247, 25, 73, 42, 24,248,249,250, 31, 56, 29,251,252,253, +) + +win1250HungarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, + 46, 72, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, +253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, + 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, +161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176, +177,178,179,180, 78,181, 69,182,183,184,185,186,187,188,189,190, +191,192,193,194,195,196,197, 76,198,199,200,201,202,203,204,205, + 81,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, +221, 51, 83,222, 80,223,224,225,226, 44,227,228,229, 61,230,231, +232,233,234, 58,235, 66, 59,236,237,238, 60, 70, 63,239,240,241, + 84, 14, 75,242, 71, 82,243, 73,244, 15, 85, 79, 86, 30, 77, 87, +245,246,247, 25, 74, 42, 24,248,249,250, 31, 56, 29,251,252,253, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 94.7368% +# first 1024 sequences:5.2623% +# rest sequences: 0.8894% +# negative sequences: 0.0009% +HungarianLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,2,3,3,1,1,2,2,2,2,2,1,2, +3,2,2,3,3,3,3,3,2,3,3,3,3,3,3,1,2,3,3,3,3,2,3,3,1,1,3,3,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +3,2,1,3,3,3,3,3,2,3,3,3,3,3,1,1,2,3,3,3,3,3,3,3,1,1,3,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,1,1,2,3,3,3,1,3,3,3,3,3,1,3,3,2,2,0,3,2,3, +0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,3,3,2,3,3,2,2,3,2,3,2,0,3,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,1,2,3,2,2,3,1,2,3,3,2,2,0,3,3,3, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,3,2,3,3,3,3,2,3,3,3,3,0,2,3,2, +0,0,0,1,1,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,1,1,1,3,3,2,1,3,2,2,3,2,1,3,2,2,1,0,3,3,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,2,2,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,3,2,2,3,1,1,3,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,1,3,3,3,3,3,2,2,1,3,3,3,0,1,1,2, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,2,0,3,2,3, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,1,3,2,2,2,3,1,1,3,3,1,1,0,3,3,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,2,3,3,3,3,3,1,2,3,2,2,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,1,3,3,2,2,1,3,3,3,1,1,3,1,2,3,2,3,2,2,2,1,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,2,2,3,2,1,0,3,2,0,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,1,0,3,3,3,3,0,2,3,0,0,2,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,2,2,2,2,3,3,0,1,2,3,2,3,2,2,3,2,1,2,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,3,3,3,3,3,1,2,3,3,3,2,1,2,3,3,2,2,2,3,2,3,3,1,3,3,1,1,0,2,3,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,1,2,2,2,2,3,3,3,1,1,1,3,3,1,1,3,1,1,3,2,1,2,3,1,1,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,1,2,1,1,3,3,1,1,1,1,3,3,1,1,2,2,1,2,1,1,2,2,1,1,0,2,2,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,1,1,2,1,1,3,3,1,0,1,1,3,3,2,0,1,1,2,3,1,0,2,2,1,0,0,1,3,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,2,1,3,3,3,3,3,1,2,3,2,3,3,2,1,1,3,2,3,2,1,2,2,0,1,2,1,0,0,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,2,2,2,2,3,1,2,2,1,1,3,3,0,3,2,1,2,3,2,1,3,3,1,1,0,2,1,3, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,2,3,3,3,2,1,1,3,3,1,1,1,2,2,3,2,3,2,2,2,1,0,2,2,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +1,0,0,3,3,3,3,3,0,0,3,3,2,3,0,0,0,2,3,3,1,0,1,2,0,0,1,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,2,3,3,3,3,3,1,2,3,3,2,2,1,1,0,3,3,2,2,1,2,2,1,0,2,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,2,1,3,1,2,3,3,2,2,1,1,2,2,1,1,1,1,3,2,1,1,1,1,2,1,0,1,2,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +2,3,3,1,1,1,1,1,3,3,3,0,1,1,3,3,1,1,1,1,1,2,2,0,3,1,1,2,0,2,1,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,1,0,1,2,1,2,2,0,1,2,3,1,2,0,0,0,2,1,1,1,1,1,2,0,0,1,1,0,0,0,0, +1,2,1,2,2,2,1,2,1,2,0,2,0,2,2,1,1,2,1,1,2,1,1,1,0,1,0,0,0,1,1,0, +1,1,1,2,3,2,3,3,0,1,2,2,3,1,0,1,0,2,1,2,2,0,1,1,0,0,1,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,3,3,2,2,1,0,0,3,2,3,2,0,0,0,1,1,3,0,0,1,1,0,0,2,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,2,2,3,3,1,0,1,3,2,3,1,1,1,0,1,1,1,1,1,3,1,0,0,2,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,1,2,2,2,1,0,1,2,3,3,2,0,0,0,2,1,1,1,2,1,1,1,0,1,1,1,0,0,0, +1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,2,1,1,1,1,1,1,0,1,1,1,0,0,1,1, +3,2,2,1,0,0,1,1,2,2,0,3,0,1,2,1,1,0,0,1,1,1,0,1,1,1,1,0,2,1,1,1, +2,2,1,1,1,2,1,2,1,1,1,1,1,1,1,2,1,1,1,2,3,1,1,1,1,1,1,1,1,1,0,1, +2,3,3,0,1,0,0,0,3,3,1,0,0,1,2,2,1,0,0,0,0,2,0,0,1,1,1,0,2,1,1,1, +2,1,1,1,1,1,1,2,1,1,0,1,1,0,1,1,1,0,1,2,1,1,0,1,1,1,1,1,1,1,0,1, +2,3,3,0,1,0,0,0,2,2,0,0,0,0,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,1,0, +2,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, +3,2,2,0,1,0,1,0,2,3,2,0,0,1,2,2,1,0,0,1,1,1,0,0,2,1,0,1,2,2,1,1, +2,1,1,1,1,1,1,2,1,1,1,1,1,1,0,2,1,0,1,1,0,1,1,1,0,1,1,2,1,1,0,1, +2,2,2,0,0,1,0,0,2,2,1,1,0,0,2,1,1,0,0,0,1,2,0,0,2,1,0,0,2,1,1,1, +2,1,1,1,1,2,1,2,1,1,1,2,2,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1, +1,2,3,0,0,0,1,0,3,2,1,0,0,1,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,2,1, +1,1,0,0,0,1,0,1,1,1,1,1,2,0,0,1,0,0,0,2,0,0,1,1,1,1,1,1,1,1,0,1, +3,0,0,2,1,2,2,1,0,0,2,1,2,2,0,0,0,2,1,1,1,0,1,1,0,0,1,1,2,0,0,0, +1,2,1,2,2,1,1,2,1,2,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,0,0,1, +1,3,2,0,0,0,1,0,2,2,2,0,0,0,2,2,1,0,0,0,0,3,1,1,1,1,0,0,2,1,1,1, +2,1,0,1,1,1,0,1,1,1,1,1,1,1,0,2,1,0,0,1,0,1,1,0,1,1,1,1,1,1,0,1, +2,3,2,0,0,0,1,0,2,2,0,0,0,0,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,1,0, +2,1,1,1,1,2,1,2,1,2,0,1,1,1,0,2,1,1,1,2,1,1,1,1,0,1,1,1,1,1,0,1, +3,1,1,2,2,2,3,2,1,1,2,2,1,1,0,1,0,2,2,1,1,1,1,1,0,0,1,1,0,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,0,0,0,0,0,2,2,0,0,0,0,2,2,1,0,0,0,1,1,0,0,1,2,0,0,2,1,1,1, +2,2,1,1,1,2,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,1,1,0,1,2,1,1,1,0,1, +1,0,0,1,2,3,2,1,0,0,2,0,1,1,0,0,0,1,1,1,1,0,1,1,0,0,1,0,0,0,0,0, +1,2,1,2,1,2,1,1,1,2,0,2,1,1,1,0,1,2,0,0,1,1,1,0,0,0,0,0,0,0,0,0, +2,3,2,0,0,0,0,0,1,1,2,1,0,0,1,1,1,0,0,0,0,2,0,0,1,1,0,0,2,1,1,1, +2,1,1,1,1,1,1,2,1,0,1,1,1,1,0,2,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1, +1,2,2,0,1,1,1,0,2,2,2,0,0,0,3,2,1,0,0,0,1,1,0,0,1,1,0,1,1,1,0,0, +1,1,0,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,0,0,1,1,1,0,1,0,1, +2,1,0,2,1,1,2,2,1,1,2,1,1,1,0,0,0,1,1,0,1,1,1,1,0,0,1,1,1,0,0,0, +1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,1,0, +1,2,3,0,0,0,1,0,2,2,0,0,0,0,2,2,0,0,0,0,0,1,0,0,1,0,0,0,2,0,1,0, +2,1,1,1,1,1,0,2,0,0,0,1,2,1,1,1,1,0,1,2,0,1,0,1,0,1,1,1,0,1,0,1, +2,2,2,0,0,0,1,0,2,1,2,0,0,0,1,1,2,0,0,0,0,1,0,0,1,1,0,0,2,1,0,1, +2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, +1,2,2,0,0,0,1,0,2,2,2,0,0,0,1,1,0,0,0,0,0,1,1,0,2,0,0,1,1,1,0,1, +1,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,0,0,1,1,0,1,0,1,1,1,1,1,0,0,0,1, +1,0,0,1,0,1,2,1,0,0,1,1,1,2,0,0,0,1,1,0,1,0,1,1,0,0,1,0,0,0,0,0, +0,2,1,2,1,1,1,1,1,2,0,2,0,1,1,0,1,2,1,0,1,1,1,0,0,0,0,0,0,1,0,0, +2,1,1,0,1,2,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,2,1,0,1, +2,2,1,1,1,1,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,0,1,0,1,1,1,1,1,0,1, +1,2,2,0,0,0,0,0,1,1,0,0,0,0,2,1,0,0,0,0,0,2,0,0,2,2,0,0,2,0,0,1, +2,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1, +1,1,2,0,0,3,1,0,2,1,1,1,0,0,1,1,1,0,0,0,1,1,0,0,0,1,0,0,1,0,1,0, +1,2,1,0,1,1,1,2,1,1,0,1,1,1,1,1,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,0, +2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,2,0,0,0, +2,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,1,0,1, +2,1,1,1,2,1,1,1,0,1,1,2,1,0,0,0,0,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,0,1,1,1,1,1,0,0,1,1,2,1,0,0,0,1,1,0,0,0,1,1,0,0,1,0,1,0,0,0, +1,2,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0, +2,0,0,0,1,1,1,1,0,0,1,1,0,0,0,0,0,1,1,1,2,0,0,1,0,0,1,0,1,0,0,0, +0,1,1,1,1,1,1,1,1,2,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,1,0,0,2,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0, +0,1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +0,1,1,1,1,1,0,0,1,1,0,1,0,1,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,1,0,0,0,0,0,0,1,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1,1,0,1,0,0,1,1,0,1,0,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +2,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,1,0,0,1,0,1,0,1,1,1,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1,1,1,1,1,0,1,1,0,1,0,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +) + +Latin2HungarianModel = { + 'char_to_order_map': Latin2_HungarianCharToOrderMap, + 'precedence_matrix': HungarianLangModel, + 'typical_positive_ratio': 0.947368, + 'keep_english_letter': True, + 'charset_name': "ISO-8859-2", + 'language': 'Hungarian', +} + +Win1250HungarianModel = { + 'char_to_order_map': win1250HungarianCharToOrderMap, + 'precedence_matrix': HungarianLangModel, + 'typical_positive_ratio': 0.947368, + 'keep_english_letter': True, + 'charset_name': "windows-1250", + 'language': 'Hungarian', +} diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py new file mode 100644 index 0000000..15f94c2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py @@ -0,0 +1,199 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# The following result for thai was collected from a limited sample (1M). + +# Character Mapping Table: +TIS620CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,182,106,107,100,183,184,185,101, 94,186,187,108,109,110,111, # 40 +188,189,190, 89, 95,112,113,191,192,193,194,253,253,253,253,253, # 50 +253, 64, 72, 73,114, 74,115,116,102, 81,201,117, 90,103, 78, 82, # 60 + 96,202, 91, 79, 84,104,105, 97, 98, 92,203,253,253,253,253,253, # 70 +209,210,211,212,213, 88,214,215,216,217,218,219,220,118,221,222, +223,224, 99, 85, 83,225,226,227,228,229,230,231,232,233,234,235, +236, 5, 30,237, 24,238, 75, 8, 26, 52, 34, 51,119, 47, 58, 57, + 49, 53, 55, 43, 20, 19, 44, 14, 48, 3, 17, 25, 39, 62, 31, 54, + 45, 9, 16, 2, 61, 15,239, 12, 42, 46, 18, 21, 76, 4, 66, 63, + 22, 10, 1, 36, 23, 13, 40, 27, 32, 35, 86,240,241,242,243,244, + 11, 28, 41, 29, 33,245, 50, 37, 6, 7, 67, 77, 38, 93,246,247, + 68, 56, 59, 65, 69, 60, 70, 80, 71, 87,248,249,250,251,252,253, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 92.6386% +# first 1024 sequences:7.3177% +# rest sequences: 1.0230% +# negative sequences: 0.0436% +ThaiLangModel = ( +0,1,3,3,3,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,0,0,3,3,3,0,3,3,3,3, +0,3,3,0,0,0,1,3,0,3,3,2,3,3,0,1,2,3,3,3,3,0,2,0,2,0,0,3,2,1,2,2, +3,0,3,3,2,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,0,3,2,3,0,2,2,2,3, +0,2,3,0,0,0,0,1,0,1,2,3,1,1,3,2,2,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1, +3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,3,3,2,3,2,3,3,2,2,2, +3,1,2,3,0,3,3,2,2,1,2,3,3,1,2,0,1,3,0,1,0,0,1,0,0,0,0,0,0,0,1,1, +3,3,2,2,3,3,3,3,1,2,3,3,3,3,3,2,2,2,2,3,3,2,2,3,3,2,2,3,2,3,2,2, +3,3,1,2,3,1,2,2,3,3,1,0,2,1,0,0,3,1,2,1,0,0,1,0,0,0,0,0,0,1,0,1, +3,3,3,3,3,3,2,2,3,3,3,3,2,3,2,2,3,3,2,2,3,2,2,2,2,1,1,3,1,2,1,1, +3,2,1,0,2,1,0,1,0,1,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,3,2,3,2,3,3,2,2,3,2,3,3,2,3,1,1,2,3,2,2,2,3,2,2,2,2,2,1,2,1, +2,2,1,1,3,3,2,1,0,1,2,2,0,1,3,0,0,0,1,1,0,0,0,0,0,2,3,0,0,2,1,1, +3,3,2,3,3,2,0,0,3,3,0,3,3,0,2,2,3,1,2,2,1,1,1,0,2,2,2,0,2,2,1,1, +0,2,1,0,2,0,0,2,0,1,0,0,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,2,3,3,2,0,0,3,3,0,2,3,0,2,1,2,2,2,2,1,2,0,0,2,2,2,0,2,2,1,1, +0,2,1,0,2,0,0,2,0,1,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,2,3,2,3,2,0,2,2,1,3,2,1,3,2,1,2,3,2,2,3,0,2,3,2,2,1,2,2,2,2, +1,2,2,0,0,0,0,2,0,1,2,0,1,1,1,0,1,0,3,1,1,0,0,0,0,0,0,0,0,0,1,0, +3,3,2,3,3,2,3,2,2,2,3,2,2,3,2,2,1,2,3,2,2,3,1,3,2,2,2,3,2,2,2,3, +3,2,1,3,0,1,1,1,0,2,1,1,1,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,0,2,0,0, +1,0,0,3,0,3,3,3,3,3,0,0,3,0,2,2,3,3,3,3,3,0,0,0,1,1,3,0,0,0,0,2, +0,0,1,0,0,0,0,0,0,0,2,3,0,0,0,3,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +2,0,3,3,3,3,0,0,2,3,0,0,3,0,3,3,2,3,3,3,3,3,0,0,3,3,3,0,0,0,3,3, +0,0,3,0,0,0,0,2,0,0,2,1,1,3,0,0,1,0,0,2,3,0,1,0,0,0,0,0,0,0,1,0, +3,3,3,3,2,3,3,3,3,3,3,3,1,2,1,3,3,2,2,1,2,2,2,3,1,1,2,0,2,1,2,1, +2,2,1,0,0,0,1,1,0,1,0,1,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0, +3,0,2,1,2,3,3,3,0,2,0,2,2,0,2,1,3,2,2,1,2,1,0,0,2,2,1,0,2,1,2,2, +0,1,1,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,3,3,1,1,3,0,2,3,1,1,3,2,1,1,2,0,2,2,3,2,1,1,1,1,1,2, +3,0,0,1,3,1,2,1,2,0,3,0,0,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, +3,3,1,1,3,2,3,3,3,1,3,2,1,3,2,1,3,2,2,2,2,1,3,3,1,2,1,3,1,2,3,0, +2,1,1,3,2,2,2,1,2,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, +3,3,2,3,2,3,3,2,3,2,3,2,3,3,2,1,0,3,2,2,2,1,2,2,2,1,2,2,1,2,1,1, +2,2,2,3,0,1,3,1,1,1,1,0,1,1,0,2,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,3,2,2,1,1,3,2,3,2,3,2,0,3,2,2,1,2,0,2,2,2,1,2,2,2,2,1, +3,2,1,2,2,1,0,2,0,1,0,0,1,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,2,3,1,2,3,3,2,2,3,0,1,1,2,0,3,3,2,2,3,0,1,1,3,0,0,0,0, +3,1,0,3,3,0,2,0,2,1,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,3,2,3,3,0,1,3,1,1,2,1,2,1,1,3,1,1,0,2,3,1,1,1,1,1,1,1,1, +3,1,1,2,2,2,2,1,1,1,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,2,2,1,1,2,1,3,3,2,3,2,2,3,2,2,3,1,2,2,1,2,0,3,2,1,2,2,2,2,2,1, +3,2,1,2,2,2,1,1,1,1,0,0,1,1,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,1,3,3,0,2,1,0,3,2,0,0,3,1,0,1,1,0,1,0,0,0,0,0,1, +1,0,0,1,0,3,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,2,2,2,3,0,0,1,3,0,3,2,0,3,2,2,3,3,3,3,3,1,0,2,2,2,0,2,2,1,2, +0,2,3,0,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,0,2,3,1,3,3,2,3,3,0,3,3,0,3,2,2,3,2,3,3,3,0,0,2,2,3,0,1,1,1,3, +0,0,3,0,0,0,2,2,0,1,3,0,1,2,2,2,3,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1, +3,2,3,3,2,0,3,3,2,2,3,1,3,2,1,3,2,0,1,2,2,0,2,3,2,1,0,3,0,0,0,0, +3,0,0,2,3,1,3,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,3,2,2,2,1,2,0,1,3,1,1,3,1,3,0,0,2,1,1,1,1,2,1,1,1,0,2,1,0,1, +1,2,0,0,0,3,1,1,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,0,3,1,0,0,0,1,0, +3,3,3,3,2,2,2,2,2,1,3,1,1,1,2,0,1,1,2,1,2,1,3,2,0,0,3,1,1,1,1,1, +3,1,0,2,3,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,2,3,0,3,3,0,2,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,3,1,3,0,0,1,2,0,0,2,0,3,3,2,3,3,3,2,3,0,0,2,2,2,0,0,0,2,2, +0,0,1,0,0,0,0,3,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +0,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,1,2,3,1,3,3,0,0,1,0,3,0,0,0,0,0, +0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,1,2,3,1,2,3,1,0,3,0,2,2,1,0,2,1,1,2,0,1,0,0,1,1,1,1,0,1,0,0, +1,0,0,0,0,1,1,0,3,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,0,1,1,1,3,1,2,2,2,2,2,2,1,1,1,1,0,3,1,0,1,3,1,1,1,1, +1,1,0,2,0,1,3,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,1, +3,0,2,2,1,3,3,2,3,3,0,1,1,0,2,2,1,2,1,3,3,1,0,0,3,2,0,0,0,0,2,1, +0,1,0,0,0,0,1,2,0,1,1,3,1,1,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,3,0,0,1,0,0,0,3,0,0,3,0,3,1,0,1,1,1,3,2,0,0,0,3,0,0,0,0,2,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,1,3,2,1,3,3,1,2,2,0,1,2,1,0,1,2,0,0,0,0,0,3,0,0,0,3,0,0,0,0, +3,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,2,0,3,3,3,2,2,0,1,1,0,1,3,0,0,0,2,2,0,0,0,0,3,1,0,1,0,0,0, +0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,2,3,1,2,0,0,2,1,0,3,1,0,1,2,0,1,1,1,1,3,0,0,3,1,1,0,2,2,1,1, +0,2,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,3,1,2,0,0,2,2,0,1,2,0,1,0,1,3,1,2,1,0,0,0,2,0,3,0,0,0,1,0, +0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,1,2,2,0,0,0,2,0,2,1,0,1,1,0,1,1,1,2,1,0,0,1,1,1,0,2,1,1,1, +0,1,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1, +0,0,0,2,0,1,3,1,1,1,1,0,0,0,0,3,2,0,1,0,0,0,1,2,0,0,0,1,0,0,0,0, +0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,2,3,2,2,0,0,0,1,0,0,0,0,2,3,2,1,2,2,3,0,0,0,2,3,1,0,0,0,1,1, +0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0, +3,3,2,2,0,1,0,0,0,0,2,0,2,0,1,0,0,0,1,1,0,0,0,2,1,0,1,0,1,1,0,0, +0,1,0,2,0,0,1,0,3,0,1,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,1,0,0,1,0,0,0,0,0,1,1,2,0,0,0,0,1,0,0,1,3,1,0,0,0,0,1,1,0,0, +0,1,0,0,0,0,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0, +3,3,1,1,1,1,2,3,0,0,2,1,1,1,1,1,0,2,1,1,0,0,0,2,1,0,1,2,1,1,0,1, +2,1,0,3,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,3,1,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1, +0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,0,0,0,0,1,2,1,0,1,1,0,2,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,2,0,0,0,1,3,0,1,0,0,0,2,0,0,0,0,0,0,0,1,2,0,0,0,0,0, +3,3,0,0,1,1,2,0,0,1,2,1,0,1,1,1,0,1,1,0,0,2,1,1,0,1,0,0,1,1,1,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,1,0,0,0,0,1,0,0,0,0,3,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,0,0,1,1,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,0,1,2,0,1,2,0,0,1,1,0,2,0,1,0,0,1,0,0,0,0,1,0,0,0,2,0,0,0,0, +1,0,0,1,0,1,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,0,0,0,0,0,0,0,1,1,0,1,1,0,2,1,3,0,0,0,0,1,1,0,0,0,0,0,0,0,3, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,1,0,0,2,0,0,2,0,0,1,1,2,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0, +1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,3,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0, +1,0,0,0,0,0,0,0,0,1,0,0,0,0,2,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,1,0,0,2,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +) + +TIS620ThaiModel = { + 'char_to_order_map': TIS620CharToOrderMap, + 'precedence_matrix': ThaiLangModel, + 'typical_positive_ratio': 0.926386, + 'keep_english_letter': False, + 'charset_name': "TIS-620", + 'language': 'Thai', +} diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py new file mode 100644 index 0000000..a427a45 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Özgür Baskın - Turkish Language Model +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin5_TurkishCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255, 23, 37, 47, 39, 29, 52, 36, 45, 53, 60, 16, 49, 20, 46, 42, + 48, 69, 44, 35, 31, 51, 38, 62, 65, 43, 56,255,255,255,255,255, +255, 1, 21, 28, 12, 2, 18, 27, 25, 3, 24, 10, 5, 13, 4, 15, + 26, 64, 7, 8, 9, 14, 32, 57, 58, 11, 22,255,255,255,255,255, +180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165, +164,163,162,161,160,159,101,158,157,156,155,154,153,152,151,106, +150,149,148,147,146,145,144,100,143,142,141,140,139,138,137,136, + 94, 80, 93,135,105,134,133, 63,132,131,130,129,128,127,126,125, +124,104, 73, 99, 79, 85,123, 54,122, 98, 92,121,120, 91,103,119, + 68,118,117, 97,116,115, 50, 90,114,113,112,111, 55, 41, 40, 86, + 89, 70, 59, 78, 71, 82, 88, 33, 77, 66, 84, 83,110, 75, 61, 96, + 30, 67,109, 74, 87,102, 34, 95, 81,108, 76, 72, 17, 6, 19,107, +) + +TurkishLangModel = ( +3,2,3,3,3,1,3,3,3,3,3,3,3,3,2,1,1,3,3,1,3,3,0,3,3,3,3,3,0,3,1,3, +3,2,1,0,0,1,1,0,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,2,2,0,0,1,0,0,1, +3,2,2,3,3,0,3,3,3,3,3,3,3,2,3,1,0,3,3,1,3,3,0,3,3,3,3,3,0,3,0,3, +3,1,1,0,1,0,1,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,2,2,0,0,0,1,0,1, +3,3,2,3,3,0,3,3,3,3,3,3,3,2,3,1,1,3,3,0,3,3,1,2,3,3,3,3,0,3,0,3, +3,1,1,0,0,0,1,0,0,0,0,1,1,0,1,2,1,0,0,0,1,0,0,0,0,2,0,0,0,0,0,1, +3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,1,3,3,2,0,3,2,1,2,2,1,3,3,0,0,0,2, +2,2,0,1,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1, +3,3,3,2,3,3,1,2,3,3,3,3,3,3,3,1,3,2,1,0,3,2,0,1,2,3,3,2,1,0,0,2, +2,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0, +1,0,1,3,3,1,3,3,3,3,3,3,3,1,2,0,0,2,3,0,2,3,0,0,2,2,2,3,0,3,0,1, +2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,0,3,2,0,2,3,2,3,3,1,0,0,2, +3,2,0,0,1,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,2,0,0,1, +3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,0,3,3,0,0,2,1,0,0,2,3,2,2,0,0,0,2, +2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,1,0,2,0,0,1, +3,3,3,2,3,3,3,3,3,3,3,2,3,3,3,0,3,2,0,1,3,2,1,1,3,2,3,2,1,0,0,2, +2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, +3,3,3,2,3,3,3,3,3,3,3,2,3,3,3,0,3,2,2,0,2,3,0,0,2,2,2,2,0,0,0,2, +3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,1,0,0,0, +3,3,3,3,3,3,3,2,2,2,2,3,2,3,3,0,3,3,1,1,2,2,0,0,2,2,3,2,0,0,1,3, +0,3,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1, +3,3,3,2,3,3,3,2,1,2,2,3,2,3,3,0,3,2,0,0,1,1,0,1,1,2,1,2,0,0,0,1, +0,3,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0, +3,3,3,2,3,3,2,3,2,2,2,3,3,3,3,1,3,1,1,0,3,2,1,1,3,3,2,3,1,0,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,0,1, +3,2,2,3,3,0,3,3,3,3,3,3,3,2,2,1,0,3,3,1,3,3,0,1,3,3,2,3,0,3,0,3, +2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +2,2,2,3,3,0,3,3,3,3,3,3,3,3,3,0,0,3,2,0,3,3,0,3,2,3,3,3,0,3,1,3, +2,0,0,0,0,0,0,0,0,0,0,1,0,1,2,0,1,0,0,0,0,0,0,0,2,2,0,0,1,0,0,1, +3,3,3,1,2,3,3,1,0,0,1,0,0,3,3,2,3,0,0,2,0,0,2,0,2,0,0,0,2,0,2,0, +0,3,1,0,1,0,0,0,2,2,1,0,1,1,2,1,2,2,2,0,2,1,1,0,0,0,2,0,0,0,0,0, +1,2,1,3,3,0,3,3,3,3,3,2,3,0,0,0,0,2,3,0,2,3,1,0,2,3,1,3,0,3,0,2, +3,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,1,3,3,2,2,3,2,2,0,1,2,3,0,1,2,1,0,1,0,0,0,1,0,2,2,0,0,0,1, +1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0, +3,3,3,1,3,3,1,1,3,3,1,1,3,3,1,0,2,1,2,0,2,1,0,0,1,1,2,1,0,0,0,2, +2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,1,0,2,1,3,0,0,2,0,0,3,3,0,3,0,0,1,0,1,2,0,0,1,1,2,2,0,1,0, +0,1,2,1,1,0,1,0,1,1,1,1,1,0,1,1,1,2,2,1,2,0,1,0,0,0,0,0,0,1,0,0, +3,3,3,2,3,2,3,3,0,2,2,2,3,3,3,0,3,0,0,0,2,2,0,1,2,1,1,1,0,0,0,1, +0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +3,3,3,3,3,3,2,1,2,2,3,3,3,3,2,0,2,0,0,0,2,2,0,0,2,1,3,3,0,0,1,1, +1,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0, +1,1,2,3,3,0,3,3,3,3,3,3,2,2,0,2,0,2,3,2,3,2,2,2,2,2,2,2,1,3,2,3, +2,0,2,1,2,2,2,2,1,1,2,2,1,2,2,1,2,0,0,2,1,1,0,2,1,0,0,1,0,0,0,1, +2,3,3,1,1,1,0,1,1,1,2,3,2,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0, +0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,2,3,2,2,1,3,3,3,0,2,1,2,0,2,1,0,0,1,1,1,1,1,0,0,1, +2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,2,0,1,0,0,0, +3,3,3,2,3,3,3,3,3,2,3,1,2,3,3,1,2,0,0,0,0,0,0,0,3,2,1,1,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +3,3,3,2,2,3,3,2,1,1,1,1,1,3,3,0,3,1,0,0,1,1,0,0,3,1,2,1,0,0,0,0, +0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0, +3,3,3,2,2,3,2,2,2,3,2,1,1,3,3,0,3,0,0,0,0,1,0,0,3,1,1,2,0,0,0,1, +1,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,1,3,3,0,3,3,3,3,3,2,2,2,1,2,0,2,1,2,2,1,1,0,1,2,2,2,2,2,2,2, +0,0,2,1,2,1,2,1,0,1,1,3,1,2,1,1,2,0,0,2,0,1,0,1,0,1,0,0,0,1,0,1, +3,3,3,1,3,3,3,0,1,1,0,2,2,3,1,0,3,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,2,2,1,0,0,1,0,0,3,3,1,3,0,0,1,1,0,2,0,3,0,0,0,2,0,1,1, +0,1,2,0,1,2,2,0,2,2,2,2,1,0,2,1,1,0,2,0,2,1,2,0,0,0,0,0,0,0,0,0, +3,3,3,1,3,2,3,2,0,2,2,2,1,3,2,0,2,1,2,0,1,2,0,0,1,0,2,2,0,0,0,2, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0, +3,3,3,0,3,3,1,1,2,3,1,0,3,2,3,0,3,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0, +1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,3,3,0,3,3,2,3,3,2,2,0,0,0,0,1,2,0,1,3,0,0,0,3,1,1,0,3,0,2, +2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,1,2,2,1,0,3,1,1,1,1,3,3,2,3,0,0,1,0,1,2,0,2,2,0,2,2,0,2,1, +0,2,2,1,1,1,1,0,2,1,1,0,1,1,1,1,2,1,2,1,2,0,1,0,1,0,0,0,0,0,0,0, +3,3,3,0,1,1,3,0,0,1,1,0,0,2,2,0,3,0,0,1,1,0,1,0,0,0,0,0,2,0,0,0, +0,3,1,0,1,0,1,0,2,0,0,1,0,1,0,1,1,1,2,1,1,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,0,2,0,2,0,1,1,1,0,0,3,3,0,2,0,0,1,0,0,2,1,1,0,1,0,1,0,1,0, +0,2,0,1,2,0,2,0,2,1,1,0,1,0,2,1,1,0,2,1,1,0,1,0,0,0,1,1,0,0,0,0, +3,2,3,0,1,0,0,0,0,0,0,0,0,1,2,0,1,0,0,1,0,0,1,0,0,0,0,0,2,0,0,0, +0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,2,1,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,0,0,2,3,0,0,1,0,1,0,2,3,2,3,0,0,1,3,0,2,1,0,0,0,0,2,0,1,0, +0,2,1,0,0,1,1,0,2,1,0,0,1,0,0,1,1,0,1,1,2,0,1,0,0,0,0,1,0,0,0,0, +3,2,2,0,0,1,1,0,0,0,0,0,0,3,1,1,1,0,0,0,0,0,1,0,0,0,0,0,2,0,1,0, +0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,3,3,0,2,3,2,2,1,2,2,1,1,2,0,1,3,2,2,2,0,0,2,2,0,0,0,1,2,1, +3,0,2,1,1,0,1,1,1,0,1,2,2,2,1,1,2,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0, +0,1,1,2,3,0,3,3,3,2,2,2,2,1,0,1,0,1,0,1,2,2,0,0,2,2,1,3,1,1,2,1, +0,0,1,1,2,0,1,1,0,0,1,2,0,2,1,1,2,0,0,1,0,0,0,1,0,1,0,1,0,0,0,0, +3,3,2,0,0,3,1,0,0,0,0,0,0,3,2,1,2,0,0,1,0,0,2,0,0,0,0,0,2,0,1,0, +0,2,1,1,0,0,1,0,1,2,0,0,1,1,0,0,2,1,1,1,1,0,2,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,1,0,0,0,0,1,0,0,3,3,2,2,0,0,1,0,0,2,0,1,0,0,0,2,0,1,0, +0,0,1,1,0,0,2,0,2,1,0,0,1,1,2,1,2,0,2,1,2,1,1,1,0,0,1,1,0,0,0,0, +3,3,2,0,0,2,2,0,0,0,1,1,0,2,2,1,3,1,0,1,0,1,2,0,0,0,0,0,1,0,1,0, +0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,0,0,0,1,0,0,1,0,0,2,3,1,2,0,0,1,0,0,2,0,0,0,1,0,2,0,2,0, +0,1,1,2,2,1,2,0,2,1,1,0,0,1,1,0,1,1,1,1,2,1,1,0,0,0,0,0,0,0,0,0, +3,3,3,0,2,1,2,1,0,0,1,1,0,3,3,1,2,0,0,1,0,0,2,0,2,0,1,1,2,0,0,0, +0,0,1,1,1,1,2,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,1,0,0,0,1,0,0,0,0,0, +3,3,3,0,2,2,3,2,0,0,1,0,0,2,3,1,0,0,0,0,0,0,2,0,2,0,0,0,2,0,0,0, +0,1,1,0,0,0,1,0,0,1,0,1,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,0,0,0,0,0,0,0,1,0,0,2,2,2,2,0,0,1,0,0,2,0,0,0,0,0,2,0,1,0, +0,0,2,1,1,0,1,0,2,1,1,0,0,1,1,2,1,0,2,0,2,0,1,0,0,0,2,0,0,0,0,0, +0,0,0,2,2,0,2,1,1,1,1,2,2,0,0,1,0,1,0,0,1,3,0,0,0,0,1,0,0,2,1,0, +0,0,1,0,1,0,0,0,0,0,2,1,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +2,0,0,2,3,0,2,3,1,2,2,0,2,0,0,2,0,2,1,1,1,2,1,0,0,1,2,1,1,2,1,0, +1,0,2,0,1,0,1,1,0,0,2,2,1,2,1,1,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,0,2,1,2,0,0,0,1,0,0,3,2,0,1,0,0,1,0,0,2,0,0,0,1,2,1,0,1,0, +0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,2,2,0,2,2,1,1,0,1,1,1,1,1,0,0,1,2,1,1,1,0,1,0,0,0,1,1,1,1, +0,0,2,1,0,1,1,1,0,1,1,2,1,2,1,1,2,0,1,1,2,1,0,2,0,0,0,0,0,0,0,0, +3,2,2,0,0,2,0,0,0,0,0,0,0,2,2,0,2,0,0,1,0,0,2,0,0,0,0,0,2,0,0,0, +0,2,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,3,2,0,2,2,0,1,1,0,1,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0, +2,0,1,0,1,0,1,1,0,0,1,2,0,1,0,1,1,0,0,1,0,1,0,2,0,0,0,0,0,0,0,0, +2,2,2,0,1,1,0,0,0,1,0,0,0,1,2,0,1,0,0,1,0,0,1,0,0,0,0,1,2,0,1,0, +0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,1,0,1,1,1,0,0,0,0,1,2,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +1,1,2,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,1, +0,0,1,2,2,0,2,1,2,1,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +2,2,2,0,0,0,1,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,0,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +) + +Latin5TurkishModel = { + 'char_to_order_map': Latin5_TurkishCharToOrderMap, + 'precedence_matrix': TurkishLangModel, + 'typical_positive_ratio': 0.970290, + 'keep_english_letter': True, + 'charset_name': "ISO-8859-9", + 'language': 'Turkish', +} diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/latin1prober.py b/venv/Lib/site-packages/pip/_vendor/chardet/latin1prober.py new file mode 100644 index 0000000..7d1e8c2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/latin1prober.py @@ -0,0 +1,145 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import ProbingState + +FREQ_CAT_NUM = 4 + +UDF = 0 # undefined +OTH = 1 # other +ASC = 2 # ascii capital letter +ASS = 3 # ascii small letter +ACV = 4 # accent capital vowel +ACO = 5 # accent capital other +ASV = 6 # accent small vowel +ASO = 7 # accent small other +CLASS_NUM = 8 # total classes + +Latin1_CharToClass = ( + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 00 - 07 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 08 - 0F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 10 - 17 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 18 - 1F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 20 - 27 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 28 - 2F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 30 - 37 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 38 - 3F + OTH, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 40 - 47 + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 48 - 4F + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 50 - 57 + ASC, ASC, ASC, OTH, OTH, OTH, OTH, OTH, # 58 - 5F + OTH, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 60 - 67 + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 68 - 6F + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 70 - 77 + ASS, ASS, ASS, OTH, OTH, OTH, OTH, OTH, # 78 - 7F + OTH, UDF, OTH, ASO, OTH, OTH, OTH, OTH, # 80 - 87 + OTH, OTH, ACO, OTH, ACO, UDF, ACO, UDF, # 88 - 8F + UDF, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 90 - 97 + OTH, OTH, ASO, OTH, ASO, UDF, ASO, ACO, # 98 - 9F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A0 - A7 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A8 - AF + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B0 - B7 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B8 - BF + ACV, ACV, ACV, ACV, ACV, ACV, ACO, ACO, # C0 - C7 + ACV, ACV, ACV, ACV, ACV, ACV, ACV, ACV, # C8 - CF + ACO, ACO, ACV, ACV, ACV, ACV, ACV, OTH, # D0 - D7 + ACV, ACV, ACV, ACV, ACV, ACO, ACO, ACO, # D8 - DF + ASV, ASV, ASV, ASV, ASV, ASV, ASO, ASO, # E0 - E7 + ASV, ASV, ASV, ASV, ASV, ASV, ASV, ASV, # E8 - EF + ASO, ASO, ASV, ASV, ASV, ASV, ASV, OTH, # F0 - F7 + ASV, ASV, ASV, ASV, ASV, ASO, ASO, ASO, # F8 - FF +) + +# 0 : illegal +# 1 : very unlikely +# 2 : normal +# 3 : very likely +Latin1ClassModel = ( +# UDF OTH ASC ASS ACV ACO ASV ASO + 0, 0, 0, 0, 0, 0, 0, 0, # UDF + 0, 3, 3, 3, 3, 3, 3, 3, # OTH + 0, 3, 3, 3, 3, 3, 3, 3, # ASC + 0, 3, 3, 3, 1, 1, 3, 3, # ASS + 0, 3, 3, 3, 1, 2, 1, 2, # ACV + 0, 3, 3, 3, 3, 3, 3, 3, # ACO + 0, 3, 1, 3, 1, 1, 1, 3, # ASV + 0, 3, 1, 3, 1, 1, 3, 3, # ASO +) + + +class Latin1Prober(CharSetProber): + def __init__(self): + super(Latin1Prober, self).__init__() + self._last_char_class = None + self._freq_counter = None + self.reset() + + def reset(self): + self._last_char_class = OTH + self._freq_counter = [0] * FREQ_CAT_NUM + CharSetProber.reset(self) + + @property + def charset_name(self): + return "ISO-8859-1" + + @property + def language(self): + return "" + + def feed(self, byte_str): + byte_str = self.filter_with_english_letters(byte_str) + for c in byte_str: + char_class = Latin1_CharToClass[c] + freq = Latin1ClassModel[(self._last_char_class * CLASS_NUM) + + char_class] + if freq == 0: + self._state = ProbingState.NOT_ME + break + self._freq_counter[freq] += 1 + self._last_char_class = char_class + + return self.state + + def get_confidence(self): + if self.state == ProbingState.NOT_ME: + return 0.01 + + total = sum(self._freq_counter) + if total < 0.01: + confidence = 0.0 + else: + confidence = ((self._freq_counter[3] - self._freq_counter[1] * 20.0) + / total) + if confidence < 0.0: + confidence = 0.0 + # lower the confidence of latin1 so that other more accurate + # detector can take priority. + confidence = confidence * 0.73 + return confidence diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py new file mode 100644 index 0000000..6256ecf --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py @@ -0,0 +1,91 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Proofpoint, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import ProbingState, MachineState + + +class MultiByteCharSetProber(CharSetProber): + """ + MultiByteCharSetProber + """ + + def __init__(self, lang_filter=None): + super(MultiByteCharSetProber, self).__init__(lang_filter=lang_filter) + self.distribution_analyzer = None + self.coding_sm = None + self._last_char = [0, 0] + + def reset(self): + super(MultiByteCharSetProber, self).reset() + if self.coding_sm: + self.coding_sm.reset() + if self.distribution_analyzer: + self.distribution_analyzer.reset() + self._last_char = [0, 0] + + @property + def charset_name(self): + raise NotImplementedError + + @property + def language(self): + raise NotImplementedError + + def feed(self, byte_str): + for i in range(len(byte_str)): + coding_state = self.coding_sm.next_state(byte_str[i]) + if coding_state == MachineState.ERROR: + self.logger.debug('%s %s prober hit error at byte %s', + self.charset_name, self.language, i) + self._state = ProbingState.NOT_ME + break + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + elif coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() + if i == 0: + self._last_char[1] = byte_str[0] + self.distribution_analyzer.feed(self._last_char, char_len) + else: + self.distribution_analyzer.feed(byte_str[i - 1:i + 1], + char_len) + + self._last_char[0] = byte_str[-1] + + if self.state == ProbingState.DETECTING: + if (self.distribution_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self): + return self.distribution_analyzer.get_confidence() diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py new file mode 100644 index 0000000..530abe7 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py @@ -0,0 +1,54 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Proofpoint, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetgroupprober import CharSetGroupProber +from .utf8prober import UTF8Prober +from .sjisprober import SJISProber +from .eucjpprober import EUCJPProber +from .gb2312prober import GB2312Prober +from .euckrprober import EUCKRProber +from .cp949prober import CP949Prober +from .big5prober import Big5Prober +from .euctwprober import EUCTWProber + + +class MBCSGroupProber(CharSetGroupProber): + def __init__(self, lang_filter=None): + super(MBCSGroupProber, self).__init__(lang_filter=lang_filter) + self.probers = [ + UTF8Prober(), + SJISProber(), + EUCJPProber(), + GB2312Prober(), + EUCKRProber(), + CP949Prober(), + Big5Prober(), + EUCTWProber() + ] + self.reset() diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/mbcssm.py b/venv/Lib/site-packages/pip/_vendor/chardet/mbcssm.py new file mode 100644 index 0000000..8360d0f --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/mbcssm.py @@ -0,0 +1,572 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .enums import MachineState + +# BIG5 + +BIG5_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as legal value + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,1, # 78 - 7f + 4,4,4,4,4,4,4,4, # 80 - 87 + 4,4,4,4,4,4,4,4, # 88 - 8f + 4,4,4,4,4,4,4,4, # 90 - 97 + 4,4,4,4,4,4,4,4, # 98 - 9f + 4,3,3,3,3,3,3,3, # a0 - a7 + 3,3,3,3,3,3,3,3, # a8 - af + 3,3,3,3,3,3,3,3, # b0 - b7 + 3,3,3,3,3,3,3,3, # b8 - bf + 3,3,3,3,3,3,3,3, # c0 - c7 + 3,3,3,3,3,3,3,3, # c8 - cf + 3,3,3,3,3,3,3,3, # d0 - d7 + 3,3,3,3,3,3,3,3, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,3,3,3, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,3,3,0 # f8 - ff +) + +BIG5_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,#08-0f + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START#10-17 +) + +BIG5_CHAR_LEN_TABLE = (0, 1, 1, 2, 0) + +BIG5_SM_MODEL = {'class_table': BIG5_CLS, + 'class_factor': 5, + 'state_table': BIG5_ST, + 'char_len_table': BIG5_CHAR_LEN_TABLE, + 'name': 'Big5'} + +# CP949 + +CP949_CLS = ( + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,0,0, # 00 - 0f + 1,1,1,1,1,1,1,1, 1,1,1,0,1,1,1,1, # 10 - 1f + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 20 - 2f + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 30 - 3f + 1,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4, # 40 - 4f + 4,4,5,5,5,5,5,5, 5,5,5,1,1,1,1,1, # 50 - 5f + 1,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5, # 60 - 6f + 5,5,5,5,5,5,5,5, 5,5,5,1,1,1,1,1, # 70 - 7f + 0,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6, # 80 - 8f + 6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6, # 90 - 9f + 6,7,7,7,7,7,7,7, 7,7,7,7,7,8,8,8, # a0 - af + 7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7, # b0 - bf + 7,7,7,7,7,7,9,2, 2,3,2,2,2,2,2,2, # c0 - cf + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, # d0 - df + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, # e0 - ef + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,0, # f0 - ff +) + +CP949_ST = ( +#cls= 0 1 2 3 4 5 6 7 8 9 # previous state = + MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START, 4, 5,MachineState.ERROR, 6, # MachineState.START + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, # MachineState.ERROR + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME, # MachineState.ITS_ME + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 3 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 4 + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 5 + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 6 +) + +CP949_CHAR_LEN_TABLE = (0, 1, 2, 0, 1, 1, 2, 2, 0, 2) + +CP949_SM_MODEL = {'class_table': CP949_CLS, + 'class_factor': 10, + 'state_table': CP949_ST, + 'char_len_table': CP949_CHAR_LEN_TABLE, + 'name': 'CP949'} + +# EUC-JP + +EUCJP_CLS = ( + 4,4,4,4,4,4,4,4, # 00 - 07 + 4,4,4,4,4,4,5,5, # 08 - 0f + 4,4,4,4,4,4,4,4, # 10 - 17 + 4,4,4,5,4,4,4,4, # 18 - 1f + 4,4,4,4,4,4,4,4, # 20 - 27 + 4,4,4,4,4,4,4,4, # 28 - 2f + 4,4,4,4,4,4,4,4, # 30 - 37 + 4,4,4,4,4,4,4,4, # 38 - 3f + 4,4,4,4,4,4,4,4, # 40 - 47 + 4,4,4,4,4,4,4,4, # 48 - 4f + 4,4,4,4,4,4,4,4, # 50 - 57 + 4,4,4,4,4,4,4,4, # 58 - 5f + 4,4,4,4,4,4,4,4, # 60 - 67 + 4,4,4,4,4,4,4,4, # 68 - 6f + 4,4,4,4,4,4,4,4, # 70 - 77 + 4,4,4,4,4,4,4,4, # 78 - 7f + 5,5,5,5,5,5,5,5, # 80 - 87 + 5,5,5,5,5,5,1,3, # 88 - 8f + 5,5,5,5,5,5,5,5, # 90 - 97 + 5,5,5,5,5,5,5,5, # 98 - 9f + 5,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,2,2,2, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,2,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,0,5 # f8 - ff +) + +EUCJP_ST = ( + 3, 4, 3, 5,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 3,MachineState.ERROR,#18-1f + 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START#20-27 +) + +EUCJP_CHAR_LEN_TABLE = (2, 2, 2, 3, 1, 0) + +EUCJP_SM_MODEL = {'class_table': EUCJP_CLS, + 'class_factor': 6, + 'state_table': EUCJP_ST, + 'char_len_table': EUCJP_CHAR_LEN_TABLE, + 'name': 'EUC-JP'} + +# EUC-KR + +EUCKR_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 1,1,1,1,1,1,1,1, # 40 - 47 + 1,1,1,1,1,1,1,1, # 48 - 4f + 1,1,1,1,1,1,1,1, # 50 - 57 + 1,1,1,1,1,1,1,1, # 58 - 5f + 1,1,1,1,1,1,1,1, # 60 - 67 + 1,1,1,1,1,1,1,1, # 68 - 6f + 1,1,1,1,1,1,1,1, # 70 - 77 + 1,1,1,1,1,1,1,1, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,3,3,3, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,3,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 2,2,2,2,2,2,2,2, # e0 - e7 + 2,2,2,2,2,2,2,2, # e8 - ef + 2,2,2,2,2,2,2,2, # f0 - f7 + 2,2,2,2,2,2,2,0 # f8 - ff +) + +EUCKR_ST = ( + MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #08-0f +) + +EUCKR_CHAR_LEN_TABLE = (0, 1, 2, 0) + +EUCKR_SM_MODEL = {'class_table': EUCKR_CLS, + 'class_factor': 4, + 'state_table': EUCKR_ST, + 'char_len_table': EUCKR_CHAR_LEN_TABLE, + 'name': 'EUC-KR'} + +# EUC-TW + +EUCTW_CLS = ( + 2,2,2,2,2,2,2,2, # 00 - 07 + 2,2,2,2,2,2,0,0, # 08 - 0f + 2,2,2,2,2,2,2,2, # 10 - 17 + 2,2,2,0,2,2,2,2, # 18 - 1f + 2,2,2,2,2,2,2,2, # 20 - 27 + 2,2,2,2,2,2,2,2, # 28 - 2f + 2,2,2,2,2,2,2,2, # 30 - 37 + 2,2,2,2,2,2,2,2, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,2, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,6,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,3,4,4,4,4,4,4, # a0 - a7 + 5,5,1,1,1,1,1,1, # a8 - af + 1,1,1,1,1,1,1,1, # b0 - b7 + 1,1,1,1,1,1,1,1, # b8 - bf + 1,1,3,1,3,3,3,3, # c0 - c7 + 3,3,3,3,3,3,3,3, # c8 - cf + 3,3,3,3,3,3,3,3, # d0 - d7 + 3,3,3,3,3,3,3,3, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,3,3,3, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,3,3,0 # f8 - ff +) + +EUCTW_ST = ( + MachineState.ERROR,MachineState.ERROR,MachineState.START, 3, 3, 3, 4,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,MachineState.ERROR,#10-17 + MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,#20-27 + MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f +) + +EUCTW_CHAR_LEN_TABLE = (0, 0, 1, 2, 2, 2, 3) + +EUCTW_SM_MODEL = {'class_table': EUCTW_CLS, + 'class_factor': 7, + 'state_table': EUCTW_ST, + 'char_len_table': EUCTW_CHAR_LEN_TABLE, + 'name': 'x-euc-tw'} + +# GB2312 + +GB2312_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 3,3,3,3,3,3,3,3, # 30 - 37 + 3,3,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,4, # 78 - 7f + 5,6,6,6,6,6,6,6, # 80 - 87 + 6,6,6,6,6,6,6,6, # 88 - 8f + 6,6,6,6,6,6,6,6, # 90 - 97 + 6,6,6,6,6,6,6,6, # 98 - 9f + 6,6,6,6,6,6,6,6, # a0 - a7 + 6,6,6,6,6,6,6,6, # a8 - af + 6,6,6,6,6,6,6,6, # b0 - b7 + 6,6,6,6,6,6,6,6, # b8 - bf + 6,6,6,6,6,6,6,6, # c0 - c7 + 6,6,6,6,6,6,6,6, # c8 - cf + 6,6,6,6,6,6,6,6, # d0 - d7 + 6,6,6,6,6,6,6,6, # d8 - df + 6,6,6,6,6,6,6,6, # e0 - e7 + 6,6,6,6,6,6,6,6, # e8 - ef + 6,6,6,6,6,6,6,6, # f0 - f7 + 6,6,6,6,6,6,6,0 # f8 - ff +) + +GB2312_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, 3,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,#10-17 + 4,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + MachineState.ERROR,MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#20-27 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f +) + +# To be accurate, the length of class 6 can be either 2 or 4. +# But it is not necessary to discriminate between the two since +# it is used for frequency analysis only, and we are validating +# each code range there as well. So it is safe to set it to be +# 2 here. +GB2312_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 1, 2) + +GB2312_SM_MODEL = {'class_table': GB2312_CLS, + 'class_factor': 7, + 'state_table': GB2312_ST, + 'char_len_table': GB2312_CHAR_LEN_TABLE, + 'name': 'GB2312'} + +# Shift_JIS + +SJIS_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,1, # 78 - 7f + 3,3,3,3,3,2,2,3, # 80 - 87 + 3,3,3,3,3,3,3,3, # 88 - 8f + 3,3,3,3,3,3,3,3, # 90 - 97 + 3,3,3,3,3,3,3,3, # 98 - 9f + #0xa0 is illegal in sjis encoding, but some pages does + #contain such byte. We need to be more error forgiven. + 2,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,2,2,2, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,2,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,4,4,4, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,0,0,0) # f8 - ff + + +SJIS_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START #10-17 +) + +SJIS_CHAR_LEN_TABLE = (0, 1, 1, 2, 0, 0) + +SJIS_SM_MODEL = {'class_table': SJIS_CLS, + 'class_factor': 6, + 'state_table': SJIS_ST, + 'char_len_table': SJIS_CHAR_LEN_TABLE, + 'name': 'Shift_JIS'} + +# UCS2-BE + +UCS2BE_CLS = ( + 0,0,0,0,0,0,0,0, # 00 - 07 + 0,0,1,0,0,2,0,0, # 08 - 0f + 0,0,0,0,0,0,0,0, # 10 - 17 + 0,0,0,3,0,0,0,0, # 18 - 1f + 0,0,0,0,0,0,0,0, # 20 - 27 + 0,3,3,3,3,3,0,0, # 28 - 2f + 0,0,0,0,0,0,0,0, # 30 - 37 + 0,0,0,0,0,0,0,0, # 38 - 3f + 0,0,0,0,0,0,0,0, # 40 - 47 + 0,0,0,0,0,0,0,0, # 48 - 4f + 0,0,0,0,0,0,0,0, # 50 - 57 + 0,0,0,0,0,0,0,0, # 58 - 5f + 0,0,0,0,0,0,0,0, # 60 - 67 + 0,0,0,0,0,0,0,0, # 68 - 6f + 0,0,0,0,0,0,0,0, # 70 - 77 + 0,0,0,0,0,0,0,0, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,0,0,0,0,0,0,0, # a0 - a7 + 0,0,0,0,0,0,0,0, # a8 - af + 0,0,0,0,0,0,0,0, # b0 - b7 + 0,0,0,0,0,0,0,0, # b8 - bf + 0,0,0,0,0,0,0,0, # c0 - c7 + 0,0,0,0,0,0,0,0, # c8 - cf + 0,0,0,0,0,0,0,0, # d0 - d7 + 0,0,0,0,0,0,0,0, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,4,5 # f8 - ff +) + +UCS2BE_ST = ( + 5, 7, 7,MachineState.ERROR, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME, 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,#10-17 + 6, 6, 6, 6, 6,MachineState.ITS_ME, 6, 6,#18-1f + 6, 6, 6, 6, 5, 7, 7,MachineState.ERROR,#20-27 + 5, 8, 6, 6,MachineState.ERROR, 6, 6, 6,#28-2f + 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #30-37 +) + +UCS2BE_CHAR_LEN_TABLE = (2, 2, 2, 0, 2, 2) + +UCS2BE_SM_MODEL = {'class_table': UCS2BE_CLS, + 'class_factor': 6, + 'state_table': UCS2BE_ST, + 'char_len_table': UCS2BE_CHAR_LEN_TABLE, + 'name': 'UTF-16BE'} + +# UCS2-LE + +UCS2LE_CLS = ( + 0,0,0,0,0,0,0,0, # 00 - 07 + 0,0,1,0,0,2,0,0, # 08 - 0f + 0,0,0,0,0,0,0,0, # 10 - 17 + 0,0,0,3,0,0,0,0, # 18 - 1f + 0,0,0,0,0,0,0,0, # 20 - 27 + 0,3,3,3,3,3,0,0, # 28 - 2f + 0,0,0,0,0,0,0,0, # 30 - 37 + 0,0,0,0,0,0,0,0, # 38 - 3f + 0,0,0,0,0,0,0,0, # 40 - 47 + 0,0,0,0,0,0,0,0, # 48 - 4f + 0,0,0,0,0,0,0,0, # 50 - 57 + 0,0,0,0,0,0,0,0, # 58 - 5f + 0,0,0,0,0,0,0,0, # 60 - 67 + 0,0,0,0,0,0,0,0, # 68 - 6f + 0,0,0,0,0,0,0,0, # 70 - 77 + 0,0,0,0,0,0,0,0, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,0,0,0,0,0,0,0, # a0 - a7 + 0,0,0,0,0,0,0,0, # a8 - af + 0,0,0,0,0,0,0,0, # b0 - b7 + 0,0,0,0,0,0,0,0, # b8 - bf + 0,0,0,0,0,0,0,0, # c0 - c7 + 0,0,0,0,0,0,0,0, # c8 - cf + 0,0,0,0,0,0,0,0, # d0 - d7 + 0,0,0,0,0,0,0,0, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,4,5 # f8 - ff +) + +UCS2LE_ST = ( + 6, 6, 7, 6, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME, 5, 5, 5,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#10-17 + 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR, 6, 6,#18-1f + 7, 6, 8, 8, 5, 5, 5,MachineState.ERROR,#20-27 + 5, 5, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5,#28-2f + 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR,MachineState.START,MachineState.START #30-37 +) + +UCS2LE_CHAR_LEN_TABLE = (2, 2, 2, 2, 2, 2) + +UCS2LE_SM_MODEL = {'class_table': UCS2LE_CLS, + 'class_factor': 6, + 'state_table': UCS2LE_ST, + 'char_len_table': UCS2LE_CHAR_LEN_TABLE, + 'name': 'UTF-16LE'} + +# UTF-8 + +UTF8_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as a legal value + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 1,1,1,1,1,1,1,1, # 40 - 47 + 1,1,1,1,1,1,1,1, # 48 - 4f + 1,1,1,1,1,1,1,1, # 50 - 57 + 1,1,1,1,1,1,1,1, # 58 - 5f + 1,1,1,1,1,1,1,1, # 60 - 67 + 1,1,1,1,1,1,1,1, # 68 - 6f + 1,1,1,1,1,1,1,1, # 70 - 77 + 1,1,1,1,1,1,1,1, # 78 - 7f + 2,2,2,2,3,3,3,3, # 80 - 87 + 4,4,4,4,4,4,4,4, # 88 - 8f + 4,4,4,4,4,4,4,4, # 90 - 97 + 4,4,4,4,4,4,4,4, # 98 - 9f + 5,5,5,5,5,5,5,5, # a0 - a7 + 5,5,5,5,5,5,5,5, # a8 - af + 5,5,5,5,5,5,5,5, # b0 - b7 + 5,5,5,5,5,5,5,5, # b8 - bf + 0,0,6,6,6,6,6,6, # c0 - c7 + 6,6,6,6,6,6,6,6, # c8 - cf + 6,6,6,6,6,6,6,6, # d0 - d7 + 6,6,6,6,6,6,6,6, # d8 - df + 7,8,8,8,8,8,8,8, # e0 - e7 + 8,8,8,8,8,9,8,8, # e8 - ef + 10,11,11,11,11,11,11,11, # f0 - f7 + 12,13,13,13,14,15,0,0 # f8 - ff +) + +UTF8_ST = ( + MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12, 10,#00-07 + 9, 11, 8, 7, 6, 5, 4, 3,#08-0f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#20-27 + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#28-2f + MachineState.ERROR,MachineState.ERROR, 5, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#30-37 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#38-3f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#40-47 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#48-4f + MachineState.ERROR,MachineState.ERROR, 7, 7, 7, 7,MachineState.ERROR,MachineState.ERROR,#50-57 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#58-5f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 7, 7,MachineState.ERROR,MachineState.ERROR,#60-67 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#68-6f + MachineState.ERROR,MachineState.ERROR, 9, 9, 9, 9,MachineState.ERROR,MachineState.ERROR,#70-77 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#78-7f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 9,MachineState.ERROR,MachineState.ERROR,#80-87 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#88-8f + MachineState.ERROR,MachineState.ERROR, 12, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,#90-97 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#98-9f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12,MachineState.ERROR,MachineState.ERROR,#a0-a7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#a8-af + MachineState.ERROR,MachineState.ERROR, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b0-b7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b8-bf + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,#c0-c7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR #c8-cf +) + +UTF8_CHAR_LEN_TABLE = (0, 1, 0, 0, 0, 0, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6) + +UTF8_SM_MODEL = {'class_table': UTF8_CLS, + 'class_factor': 16, + 'state_table': UTF8_ST, + 'char_len_table': UTF8_CHAR_LEN_TABLE, + 'name': 'UTF-8'} diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py new file mode 100644 index 0000000..0adb51d --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py @@ -0,0 +1,132 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import CharacterCategory, ProbingState, SequenceLikelihood + + +class SingleByteCharSetProber(CharSetProber): + SAMPLE_SIZE = 64 + SB_ENOUGH_REL_THRESHOLD = 1024 # 0.25 * SAMPLE_SIZE^2 + POSITIVE_SHORTCUT_THRESHOLD = 0.95 + NEGATIVE_SHORTCUT_THRESHOLD = 0.05 + + def __init__(self, model, reversed=False, name_prober=None): + super(SingleByteCharSetProber, self).__init__() + self._model = model + # TRUE if we need to reverse every pair in the model lookup + self._reversed = reversed + # Optional auxiliary prober for name decision + self._name_prober = name_prober + self._last_order = None + self._seq_counters = None + self._total_seqs = None + self._total_char = None + self._freq_char = None + self.reset() + + def reset(self): + super(SingleByteCharSetProber, self).reset() + # char order of last character + self._last_order = 255 + self._seq_counters = [0] * SequenceLikelihood.get_num_categories() + self._total_seqs = 0 + self._total_char = 0 + # characters that fall in our sampling range + self._freq_char = 0 + + @property + def charset_name(self): + if self._name_prober: + return self._name_prober.charset_name + else: + return self._model['charset_name'] + + @property + def language(self): + if self._name_prober: + return self._name_prober.language + else: + return self._model.get('language') + + def feed(self, byte_str): + if not self._model['keep_english_letter']: + byte_str = self.filter_international_words(byte_str) + if not byte_str: + return self.state + char_to_order_map = self._model['char_to_order_map'] + for i, c in enumerate(byte_str): + # XXX: Order is in range 1-64, so one would think we want 0-63 here, + # but that leads to 27 more test failures than before. + order = char_to_order_map[c] + # XXX: This was SYMBOL_CAT_ORDER before, with a value of 250, but + # CharacterCategory.SYMBOL is actually 253, so we use CONTROL + # to make it closer to the original intent. The only difference + # is whether or not we count digits and control characters for + # _total_char purposes. + if order < CharacterCategory.CONTROL: + self._total_char += 1 + if order < self.SAMPLE_SIZE: + self._freq_char += 1 + if self._last_order < self.SAMPLE_SIZE: + self._total_seqs += 1 + if not self._reversed: + i = (self._last_order * self.SAMPLE_SIZE) + order + model = self._model['precedence_matrix'][i] + else: # reverse the order of the letters in the lookup + i = (order * self.SAMPLE_SIZE) + self._last_order + model = self._model['precedence_matrix'][i] + self._seq_counters[model] += 1 + self._last_order = order + + charset_name = self._model['charset_name'] + if self.state == ProbingState.DETECTING: + if self._total_seqs > self.SB_ENOUGH_REL_THRESHOLD: + confidence = self.get_confidence() + if confidence > self.POSITIVE_SHORTCUT_THRESHOLD: + self.logger.debug('%s confidence = %s, we have a winner', + charset_name, confidence) + self._state = ProbingState.FOUND_IT + elif confidence < self.NEGATIVE_SHORTCUT_THRESHOLD: + self.logger.debug('%s confidence = %s, below negative ' + 'shortcut threshhold %s', charset_name, + confidence, + self.NEGATIVE_SHORTCUT_THRESHOLD) + self._state = ProbingState.NOT_ME + + return self.state + + def get_confidence(self): + r = 0.01 + if self._total_seqs > 0: + r = ((1.0 * self._seq_counters[SequenceLikelihood.POSITIVE]) / + self._total_seqs / self._model['typical_positive_ratio']) + r = r * self._freq_char / self._total_char + if r >= 1.0: + r = 0.99 + return r diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py new file mode 100644 index 0000000..98e95dc --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py @@ -0,0 +1,73 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetgroupprober import CharSetGroupProber +from .sbcharsetprober import SingleByteCharSetProber +from .langcyrillicmodel import (Win1251CyrillicModel, Koi8rModel, + Latin5CyrillicModel, MacCyrillicModel, + Ibm866Model, Ibm855Model) +from .langgreekmodel import Latin7GreekModel, Win1253GreekModel +from .langbulgarianmodel import Latin5BulgarianModel, Win1251BulgarianModel +# from .langhungarianmodel import Latin2HungarianModel, Win1250HungarianModel +from .langthaimodel import TIS620ThaiModel +from .langhebrewmodel import Win1255HebrewModel +from .hebrewprober import HebrewProber +from .langturkishmodel import Latin5TurkishModel + + +class SBCSGroupProber(CharSetGroupProber): + def __init__(self): + super(SBCSGroupProber, self).__init__() + self.probers = [ + SingleByteCharSetProber(Win1251CyrillicModel), + SingleByteCharSetProber(Koi8rModel), + SingleByteCharSetProber(Latin5CyrillicModel), + SingleByteCharSetProber(MacCyrillicModel), + SingleByteCharSetProber(Ibm866Model), + SingleByteCharSetProber(Ibm855Model), + SingleByteCharSetProber(Latin7GreekModel), + SingleByteCharSetProber(Win1253GreekModel), + SingleByteCharSetProber(Latin5BulgarianModel), + SingleByteCharSetProber(Win1251BulgarianModel), + # TODO: Restore Hungarian encodings (iso-8859-2 and windows-1250) + # after we retrain model. + # SingleByteCharSetProber(Latin2HungarianModel), + # SingleByteCharSetProber(Win1250HungarianModel), + SingleByteCharSetProber(TIS620ThaiModel), + SingleByteCharSetProber(Latin5TurkishModel), + ] + hebrew_prober = HebrewProber() + logical_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, + False, hebrew_prober) + visual_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, True, + hebrew_prober) + hebrew_prober.set_model_probers(logical_hebrew_prober, visual_hebrew_prober) + self.probers.extend([hebrew_prober, logical_hebrew_prober, + visual_hebrew_prober]) + + self.reset() diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/sjisprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/sjisprober.py new file mode 100644 index 0000000..9e29623 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/sjisprober.py @@ -0,0 +1,92 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import SJISDistributionAnalysis +from .jpcntx import SJISContextAnalysis +from .mbcssm import SJIS_SM_MODEL +from .enums import ProbingState, MachineState + + +class SJISProber(MultiByteCharSetProber): + def __init__(self): + super(SJISProber, self).__init__() + self.coding_sm = CodingStateMachine(SJIS_SM_MODEL) + self.distribution_analyzer = SJISDistributionAnalysis() + self.context_analyzer = SJISContextAnalysis() + self.reset() + + def reset(self): + super(SJISProber, self).reset() + self.context_analyzer.reset() + + @property + def charset_name(self): + return self.context_analyzer.charset_name + + @property + def language(self): + return "Japanese" + + def feed(self, byte_str): + for i in range(len(byte_str)): + coding_state = self.coding_sm.next_state(byte_str[i]) + if coding_state == MachineState.ERROR: + self.logger.debug('%s %s prober hit error at byte %s', + self.charset_name, self.language, i) + self._state = ProbingState.NOT_ME + break + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + elif coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() + if i == 0: + self._last_char[1] = byte_str[0] + self.context_analyzer.feed(self._last_char[2 - char_len:], + char_len) + self.distribution_analyzer.feed(self._last_char, char_len) + else: + self.context_analyzer.feed(byte_str[i + 1 - char_len:i + 3 + - char_len], char_len) + self.distribution_analyzer.feed(byte_str[i - 1:i + 1], + char_len) + + self._last_char[0] = byte_str[-1] + + if self.state == ProbingState.DETECTING: + if (self.context_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self): + context_conf = self.context_analyzer.get_confidence() + distrib_conf = self.distribution_analyzer.get_confidence() + return max(context_conf, distrib_conf) diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py b/venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py new file mode 100644 index 0000000..7b4e92d --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py @@ -0,0 +1,286 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### +""" +Module containing the UniversalDetector detector class, which is the primary +class a user of ``chardet`` should use. + +:author: Mark Pilgrim (initial port to Python) +:author: Shy Shalom (original C code) +:author: Dan Blanchard (major refactoring for 3.0) +:author: Ian Cordasco +""" + + +import codecs +import logging +import re + +from .charsetgroupprober import CharSetGroupProber +from .enums import InputState, LanguageFilter, ProbingState +from .escprober import EscCharSetProber +from .latin1prober import Latin1Prober +from .mbcsgroupprober import MBCSGroupProber +from .sbcsgroupprober import SBCSGroupProber + + +class UniversalDetector(object): + """ + The ``UniversalDetector`` class underlies the ``chardet.detect`` function + and coordinates all of the different charset probers. + + To get a ``dict`` containing an encoding and its confidence, you can simply + run: + + .. code:: + + u = UniversalDetector() + u.feed(some_bytes) + u.close() + detected = u.result + + """ + + MINIMUM_THRESHOLD = 0.20 + HIGH_BYTE_DETECTOR = re.compile(b'[\x80-\xFF]') + ESC_DETECTOR = re.compile(b'(\033|~{)') + WIN_BYTE_DETECTOR = re.compile(b'[\x80-\x9F]') + ISO_WIN_MAP = {'iso-8859-1': 'Windows-1252', + 'iso-8859-2': 'Windows-1250', + 'iso-8859-5': 'Windows-1251', + 'iso-8859-6': 'Windows-1256', + 'iso-8859-7': 'Windows-1253', + 'iso-8859-8': 'Windows-1255', + 'iso-8859-9': 'Windows-1254', + 'iso-8859-13': 'Windows-1257'} + + def __init__(self, lang_filter=LanguageFilter.ALL): + self._esc_charset_prober = None + self._charset_probers = [] + self.result = None + self.done = None + self._got_data = None + self._input_state = None + self._last_char = None + self.lang_filter = lang_filter + self.logger = logging.getLogger(__name__) + self._has_win_bytes = None + self.reset() + + def reset(self): + """ + Reset the UniversalDetector and all of its probers back to their + initial states. This is called by ``__init__``, so you only need to + call this directly in between analyses of different documents. + """ + self.result = {'encoding': None, 'confidence': 0.0, 'language': None} + self.done = False + self._got_data = False + self._has_win_bytes = False + self._input_state = InputState.PURE_ASCII + self._last_char = b'' + if self._esc_charset_prober: + self._esc_charset_prober.reset() + for prober in self._charset_probers: + prober.reset() + + def feed(self, byte_str): + """ + Takes a chunk of a document and feeds it through all of the relevant + charset probers. + + After calling ``feed``, you can check the value of the ``done`` + attribute to see if you need to continue feeding the + ``UniversalDetector`` more data, or if it has made a prediction + (in the ``result`` attribute). + + .. note:: + You should always call ``close`` when you're done feeding in your + document if ``done`` is not already ``True``. + """ + if self.done: + return + + if not len(byte_str): + return + + if not isinstance(byte_str, bytearray): + byte_str = bytearray(byte_str) + + # First check for known BOMs, since these are guaranteed to be correct + if not self._got_data: + # If the data starts with BOM, we know it is UTF + if byte_str.startswith(codecs.BOM_UTF8): + # EF BB BF UTF-8 with BOM + self.result = {'encoding': "UTF-8-SIG", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith((codecs.BOM_UTF32_LE, + codecs.BOM_UTF32_BE)): + # FF FE 00 00 UTF-32, little-endian BOM + # 00 00 FE FF UTF-32, big-endian BOM + self.result = {'encoding': "UTF-32", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith(b'\xFE\xFF\x00\x00'): + # FE FF 00 00 UCS-4, unusual octet order BOM (3412) + self.result = {'encoding': "X-ISO-10646-UCS-4-3412", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith(b'\x00\x00\xFF\xFE'): + # 00 00 FF FE UCS-4, unusual octet order BOM (2143) + self.result = {'encoding': "X-ISO-10646-UCS-4-2143", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith((codecs.BOM_LE, codecs.BOM_BE)): + # FF FE UTF-16, little endian BOM + # FE FF UTF-16, big endian BOM + self.result = {'encoding': "UTF-16", + 'confidence': 1.0, + 'language': ''} + + self._got_data = True + if self.result['encoding'] is not None: + self.done = True + return + + # If none of those matched and we've only see ASCII so far, check + # for high bytes and escape sequences + if self._input_state == InputState.PURE_ASCII: + if self.HIGH_BYTE_DETECTOR.search(byte_str): + self._input_state = InputState.HIGH_BYTE + elif self._input_state == InputState.PURE_ASCII and \ + self.ESC_DETECTOR.search(self._last_char + byte_str): + self._input_state = InputState.ESC_ASCII + + self._last_char = byte_str[-1:] + + # If we've seen escape sequences, use the EscCharSetProber, which + # uses a simple state machine to check for known escape sequences in + # HZ and ISO-2022 encodings, since those are the only encodings that + # use such sequences. + if self._input_state == InputState.ESC_ASCII: + if not self._esc_charset_prober: + self._esc_charset_prober = EscCharSetProber(self.lang_filter) + if self._esc_charset_prober.feed(byte_str) == ProbingState.FOUND_IT: + self.result = {'encoding': + self._esc_charset_prober.charset_name, + 'confidence': + self._esc_charset_prober.get_confidence(), + 'language': + self._esc_charset_prober.language} + self.done = True + # If we've seen high bytes (i.e., those with values greater than 127), + # we need to do more complicated checks using all our multi-byte and + # single-byte probers that are left. The single-byte probers + # use character bigram distributions to determine the encoding, whereas + # the multi-byte probers use a combination of character unigram and + # bigram distributions. + elif self._input_state == InputState.HIGH_BYTE: + if not self._charset_probers: + self._charset_probers = [MBCSGroupProber(self.lang_filter)] + # If we're checking non-CJK encodings, use single-byte prober + if self.lang_filter & LanguageFilter.NON_CJK: + self._charset_probers.append(SBCSGroupProber()) + self._charset_probers.append(Latin1Prober()) + for prober in self._charset_probers: + if prober.feed(byte_str) == ProbingState.FOUND_IT: + self.result = {'encoding': prober.charset_name, + 'confidence': prober.get_confidence(), + 'language': prober.language} + self.done = True + break + if self.WIN_BYTE_DETECTOR.search(byte_str): + self._has_win_bytes = True + + def close(self): + """ + Stop analyzing the current document and come up with a final + prediction. + + :returns: The ``result`` attribute, a ``dict`` with the keys + `encoding`, `confidence`, and `language`. + """ + # Don't bother with checks if we're already done + if self.done: + return self.result + self.done = True + + if not self._got_data: + self.logger.debug('no data received!') + + # Default to ASCII if it is all we've seen so far + elif self._input_state == InputState.PURE_ASCII: + self.result = {'encoding': 'ascii', + 'confidence': 1.0, + 'language': ''} + + # If we have seen non-ASCII, return the best that met MINIMUM_THRESHOLD + elif self._input_state == InputState.HIGH_BYTE: + prober_confidence = None + max_prober_confidence = 0.0 + max_prober = None + for prober in self._charset_probers: + if not prober: + continue + prober_confidence = prober.get_confidence() + if prober_confidence > max_prober_confidence: + max_prober_confidence = prober_confidence + max_prober = prober + if max_prober and (max_prober_confidence > self.MINIMUM_THRESHOLD): + charset_name = max_prober.charset_name + lower_charset_name = max_prober.charset_name.lower() + confidence = max_prober.get_confidence() + # Use Windows encoding name instead of ISO-8859 if we saw any + # extra Windows-specific bytes + if lower_charset_name.startswith('iso-8859'): + if self._has_win_bytes: + charset_name = self.ISO_WIN_MAP.get(lower_charset_name, + charset_name) + self.result = {'encoding': charset_name, + 'confidence': confidence, + 'language': max_prober.language} + + # Log all prober confidences if none met MINIMUM_THRESHOLD + if self.logger.getEffectiveLevel() == logging.DEBUG: + if self.result['encoding'] is None: + self.logger.debug('no probers hit minimum threshold') + for group_prober in self._charset_probers: + if not group_prober: + continue + if isinstance(group_prober, CharSetGroupProber): + for prober in group_prober.probers: + self.logger.debug('%s %s confidence = %s', + prober.charset_name, + prober.language, + prober.get_confidence()) + else: + self.logger.debug('%s %s confidence = %s', + prober.charset_name, + prober.language, + prober.get_confidence()) + return self.result diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/utf8prober.py b/venv/Lib/site-packages/pip/_vendor/chardet/utf8prober.py new file mode 100644 index 0000000..6c3196c --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/utf8prober.py @@ -0,0 +1,82 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import ProbingState, MachineState +from .codingstatemachine import CodingStateMachine +from .mbcssm import UTF8_SM_MODEL + + + +class UTF8Prober(CharSetProber): + ONE_CHAR_PROB = 0.5 + + def __init__(self): + super(UTF8Prober, self).__init__() + self.coding_sm = CodingStateMachine(UTF8_SM_MODEL) + self._num_mb_chars = None + self.reset() + + def reset(self): + super(UTF8Prober, self).reset() + self.coding_sm.reset() + self._num_mb_chars = 0 + + @property + def charset_name(self): + return "utf-8" + + @property + def language(self): + return "" + + def feed(self, byte_str): + for c in byte_str: + coding_state = self.coding_sm.next_state(c) + if coding_state == MachineState.ERROR: + self._state = ProbingState.NOT_ME + break + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + elif coding_state == MachineState.START: + if self.coding_sm.get_current_charlen() >= 2: + self._num_mb_chars += 1 + + if self.state == ProbingState.DETECTING: + if self.get_confidence() > self.SHORTCUT_THRESHOLD: + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self): + unlike = 0.99 + if self._num_mb_chars < 6: + unlike *= self.ONE_CHAR_PROB ** self._num_mb_chars + return 1.0 - unlike + else: + return unlike diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/version.py b/venv/Lib/site-packages/pip/_vendor/chardet/version.py new file mode 100644 index 0000000..bb2a34a --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/chardet/version.py @@ -0,0 +1,9 @@ +""" +This module exists only to simplify retrieving the version number of chardet +from within setup.py and from chardet subpackages. + +:author: Dan Blanchard (dan.blanchard@gmail.com) +""" + +__version__ = "3.0.4" +VERSION = __version__.split('.') diff --git a/venv/Lib/site-packages/pip/_vendor/colorama/__init__.py b/venv/Lib/site-packages/pip/_vendor/colorama/__init__.py new file mode 100644 index 0000000..f4d9ce2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/colorama/__init__.py @@ -0,0 +1,7 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit, colorama_text +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.3.9' + diff --git a/venv/Lib/site-packages/pip/_vendor/colorama/ansi.py b/venv/Lib/site-packages/pip/_vendor/colorama/ansi.py new file mode 100644 index 0000000..7877658 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/colorama/ansi.py @@ -0,0 +1,102 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' +OSC = '\033]' +BEL = '\007' + + +def code_to_chars(code): + return CSI + str(code) + 'm' + +def set_title(title): + return OSC + '2;' + title + BEL + +def clear_screen(mode=2): + return CSI + str(mode) + 'J' + +def clear_line(mode=2): + return CSI + str(mode) + 'K' + + +class AnsiCodes(object): + def __init__(self): + # the subclasses declare class attributes which are numbers. + # Upon instantiation we define instance attributes, which are the same + # as the class attributes but wrapped with the ANSI escape sequence + for name in dir(self): + if not name.startswith('_'): + value = getattr(self, name) + setattr(self, name, code_to_chars(value)) + + +class AnsiCursor(object): + def UP(self, n=1): + return CSI + str(n) + 'A' + def DOWN(self, n=1): + return CSI + str(n) + 'B' + def FORWARD(self, n=1): + return CSI + str(n) + 'C' + def BACK(self, n=1): + return CSI + str(n) + 'D' + def POS(self, x=1, y=1): + return CSI + str(y) + ';' + str(x) + 'H' + + +class AnsiFore(AnsiCodes): + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 90 + LIGHTRED_EX = 91 + LIGHTGREEN_EX = 92 + LIGHTYELLOW_EX = 93 + LIGHTBLUE_EX = 94 + LIGHTMAGENTA_EX = 95 + LIGHTCYAN_EX = 96 + LIGHTWHITE_EX = 97 + + +class AnsiBack(AnsiCodes): + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 100 + LIGHTRED_EX = 101 + LIGHTGREEN_EX = 102 + LIGHTYELLOW_EX = 103 + LIGHTBLUE_EX = 104 + LIGHTMAGENTA_EX = 105 + LIGHTCYAN_EX = 106 + LIGHTWHITE_EX = 107 + + +class AnsiStyle(AnsiCodes): + BRIGHT = 1 + DIM = 2 + NORMAL = 22 + RESET_ALL = 0 + +Fore = AnsiFore() +Back = AnsiBack() +Style = AnsiStyle() +Cursor = AnsiCursor() diff --git a/venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py b/venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py new file mode 100644 index 0000000..1d6e605 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py @@ -0,0 +1,236 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import re +import sys +import os + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style +from .winterm import WinTerm, WinColor, WinStyle +from .win32 import windll, winapi_test + + +winterm = None +if windll is not None: + winterm = WinTerm() + + +def is_stream_closed(stream): + return not hasattr(stream, 'closed') or stream.closed + + +def is_a_tty(stream): + return hasattr(stream, 'isatty') and stream.isatty() + + +class StreamWrapper(object): + ''' + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()', which is delegated to our + Converter instance. + ''' + def __init__(self, wrapped, converter): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + self.__convertor = converter + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def write(self, text): + self.__convertor.write(text) + + +class AnsiToWin32(object): + ''' + Implements a 'write()' method which, on Windows, will strip ANSI character + sequences from the text, and if outputting to a tty, will convert them into + win32 function calls. + ''' + ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer + ANSI_OSC_RE = re.compile('\001?\033\\]((?:.|;)*?)(\x07)\002?') # Operating System Command + + def __init__(self, wrapped, convert=None, strip=None, autoreset=False): + # The wrapped stream (normally sys.stdout or sys.stderr) + self.wrapped = wrapped + + # should we reset colors to defaults after every .write() + self.autoreset = autoreset + + # create the proxy wrapping our output stream + self.stream = StreamWrapper(wrapped, self) + + on_windows = os.name == 'nt' + # We test if the WinAPI works, because even if we are on Windows + # we may be using a terminal that doesn't support the WinAPI + # (e.g. Cygwin Terminal). In this case it's up to the terminal + # to support the ANSI codes. + conversion_supported = on_windows and winapi_test() + + # should we strip ANSI sequences from our output? + if strip is None: + strip = conversion_supported or (not is_stream_closed(wrapped) and not is_a_tty(wrapped)) + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = conversion_supported and not is_stream_closed(wrapped) and is_a_tty(wrapped) + self.convert = convert + + # dict of ansi codes to win32 functions and parameters + self.win32_calls = self.get_win32_calls() + + # are we wrapping stderr? + self.on_stderr = self.wrapped is sys.stderr + + def should_wrap(self): + ''' + True if this class is actually needed. If false, then the output + stream will not be affected, nor will win32 calls be issued, so + wrapping stdout is not actually required. This will generally be + False on non-Windows platforms, unless optional functionality like + autoreset has been requested using kwargs to init() + ''' + return self.convert or self.strip or self.autoreset + + def get_win32_calls(self): + if self.convert and winterm: + return { + AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), + AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), + AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), + AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), + AnsiFore.RED: (winterm.fore, WinColor.RED), + AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), + AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), + AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), + AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), + AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), + AnsiFore.WHITE: (winterm.fore, WinColor.GREY), + AnsiFore.RESET: (winterm.fore, ), + AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), + AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), + AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), + AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), + AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), + AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), + AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), + AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), + AnsiBack.BLACK: (winterm.back, WinColor.BLACK), + AnsiBack.RED: (winterm.back, WinColor.RED), + AnsiBack.GREEN: (winterm.back, WinColor.GREEN), + AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), + AnsiBack.BLUE: (winterm.back, WinColor.BLUE), + AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), + AnsiBack.CYAN: (winterm.back, WinColor.CYAN), + AnsiBack.WHITE: (winterm.back, WinColor.GREY), + AnsiBack.RESET: (winterm.back, ), + AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), + AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), + AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), + AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), + AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), + AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), + AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), + AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), + } + return dict() + + def write(self, text): + if self.strip or self.convert: + self.write_and_convert(text) + else: + self.wrapped.write(text) + self.wrapped.flush() + if self.autoreset: + self.reset_all() + + + def reset_all(self): + if self.convert: + self.call_win32('m', (0,)) + elif not self.strip and not is_stream_closed(self.wrapped): + self.wrapped.write(Style.RESET_ALL) + + + def write_and_convert(self, text): + ''' + Write the given text to our wrapped stream, stripping any ANSI + sequences from the text, and optionally converting them into win32 + calls. + ''' + cursor = 0 + text = self.convert_osc(text) + for match in self.ANSI_CSI_RE.finditer(text): + start, end = match.span() + self.write_plain_text(text, cursor, start) + self.convert_ansi(*match.groups()) + cursor = end + self.write_plain_text(text, cursor, len(text)) + + + def write_plain_text(self, text, start, end): + if start < end: + self.wrapped.write(text[start:end]) + self.wrapped.flush() + + + def convert_ansi(self, paramstring, command): + if self.convert: + params = self.extract_params(command, paramstring) + self.call_win32(command, params) + + + def extract_params(self, command, paramstring): + if command in 'Hf': + params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) + while len(params) < 2: + # defaults: + params = params + (1,) + else: + params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) + if len(params) == 0: + # defaults: + if command in 'JKm': + params = (0,) + elif command in 'ABCD': + params = (1,) + + return params + + + def call_win32(self, command, params): + if command == 'm': + for param in params: + if param in self.win32_calls: + func_args = self.win32_calls[param] + func = func_args[0] + args = func_args[1:] + kwargs = dict(on_stderr=self.on_stderr) + func(*args, **kwargs) + elif command in 'J': + winterm.erase_screen(params[0], on_stderr=self.on_stderr) + elif command in 'K': + winterm.erase_line(params[0], on_stderr=self.on_stderr) + elif command in 'Hf': # cursor position - absolute + winterm.set_cursor_position(params, on_stderr=self.on_stderr) + elif command in 'ABCD': # cursor position - relative + n = params[0] + # A - up, B - down, C - forward, D - back + x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] + winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) + + + def convert_osc(self, text): + for match in self.ANSI_OSC_RE.finditer(text): + start, end = match.span() + text = text[:start] + text[end:] + paramstring, command = match.groups() + if command in '\x07': # \x07 = BEL + params = paramstring.split(";") + # 0 - change title and icon (we will only change title) + # 1 - change icon (we don't support this) + # 2 - change title + if params[0] in '02': + winterm.set_title(params[1]) + return text diff --git a/venv/Lib/site-packages/pip/_vendor/colorama/initialise.py b/venv/Lib/site-packages/pip/_vendor/colorama/initialise.py new file mode 100644 index 0000000..834962a --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/colorama/initialise.py @@ -0,0 +1,82 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import atexit +import contextlib +import sys + +from .ansitowin32 import AnsiToWin32 + + +orig_stdout = None +orig_stderr = None + +wrapped_stdout = None +wrapped_stderr = None + +atexit_done = False + + +def reset_all(): + if AnsiToWin32 is not None: # Issue #74: objects might become None at exit + AnsiToWin32(orig_stdout).reset_all() + + +def init(autoreset=False, convert=None, strip=None, wrap=True): + + if not wrap and any([autoreset, convert, strip]): + raise ValueError('wrap=False conflicts with any other arg=True') + + global wrapped_stdout, wrapped_stderr + global orig_stdout, orig_stderr + + orig_stdout = sys.stdout + orig_stderr = sys.stderr + + if sys.stdout is None: + wrapped_stdout = None + else: + sys.stdout = wrapped_stdout = \ + wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + if sys.stderr is None: + wrapped_stderr = None + else: + sys.stderr = wrapped_stderr = \ + wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + + global atexit_done + if not atexit_done: + atexit.register(reset_all) + atexit_done = True + + +def deinit(): + if orig_stdout is not None: + sys.stdout = orig_stdout + if orig_stderr is not None: + sys.stderr = orig_stderr + + +@contextlib.contextmanager +def colorama_text(*args, **kwargs): + init(*args, **kwargs) + try: + yield + finally: + deinit() + + +def reinit(): + if wrapped_stdout is not None: + sys.stdout = wrapped_stdout + if wrapped_stderr is not None: + sys.stderr = wrapped_stderr + + +def wrap_stream(stream, convert, strip, autoreset, wrap): + if wrap: + wrapper = AnsiToWin32(stream, + convert=convert, strip=strip, autoreset=autoreset) + if wrapper.should_wrap(): + stream = wrapper.stream + return stream + + diff --git a/venv/Lib/site-packages/pip/_vendor/colorama/win32.py b/venv/Lib/site-packages/pip/_vendor/colorama/win32.py new file mode 100644 index 0000000..8262e35 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/colorama/win32.py @@ -0,0 +1,156 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +try: + import ctypes + from ctypes import LibraryLoader + windll = LibraryLoader(ctypes.WinDLL) + from ctypes import wintypes +except (AttributeError, ImportError): + windll = None + SetConsoleTextAttribute = lambda *_: None + winapi_test = lambda *_: None +else: + from ctypes import byref, Structure, c_char, POINTER + + COORD = wintypes._COORD + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", wintypes.WORD), + ("srWindow", wintypes.SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + def __str__(self): + return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( + self.dwSize.Y, self.dwSize.X + , self.dwCursorPosition.Y, self.dwCursorPosition.X + , self.wAttributes + , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right + , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X + ) + + _GetStdHandle = windll.kernel32.GetStdHandle + _GetStdHandle.argtypes = [ + wintypes.DWORD, + ] + _GetStdHandle.restype = wintypes.HANDLE + + _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo + _GetConsoleScreenBufferInfo.argtypes = [ + wintypes.HANDLE, + POINTER(CONSOLE_SCREEN_BUFFER_INFO), + ] + _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + + _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute + _SetConsoleTextAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + ] + _SetConsoleTextAttribute.restype = wintypes.BOOL + + _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition + _SetConsoleCursorPosition.argtypes = [ + wintypes.HANDLE, + COORD, + ] + _SetConsoleCursorPosition.restype = wintypes.BOOL + + _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA + _FillConsoleOutputCharacterA.argtypes = [ + wintypes.HANDLE, + c_char, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputCharacterA.restype = wintypes.BOOL + + _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute + _FillConsoleOutputAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputAttribute.restype = wintypes.BOOL + + _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW + _SetConsoleTitleW.argtypes = [ + wintypes.LPCWSTR + ] + _SetConsoleTitleW.restype = wintypes.BOOL + + handles = { + STDOUT: _GetStdHandle(STDOUT), + STDERR: _GetStdHandle(STDERR), + } + + def _winapi_test(handle): + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return bool(success) + + def winapi_test(): + return any(_winapi_test(h) for h in handles.values()) + + def GetConsoleScreenBufferInfo(stream_id=STDOUT): + handle = handles[stream_id] + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = handles[stream_id] + return _SetConsoleTextAttribute(handle, attrs) + + def SetConsoleCursorPosition(stream_id, position, adjust=True): + position = COORD(*position) + # If the position is out of range, do nothing. + if position.Y <= 0 or position.X <= 0: + return + # Adjust for Windows' SetConsoleCursorPosition: + # 1. being 0-based, while ANSI is 1-based. + # 2. expecting (x,y), while ANSI uses (y,x). + adjusted_position = COORD(position.Y - 1, position.X - 1) + if adjust: + # Adjust for viewport's scroll position + sr = GetConsoleScreenBufferInfo(STDOUT).srWindow + adjusted_position.Y += sr.Top + adjusted_position.X += sr.Left + # Resume normal processing + handle = handles[stream_id] + return _SetConsoleCursorPosition(handle, adjusted_position) + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = handles[stream_id] + char = c_char(char.encode()) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + success = _FillConsoleOutputCharacterA( + handle, char, length, start, byref(num_written)) + return num_written.value + + def FillConsoleOutputAttribute(stream_id, attr, length, start): + ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' + handle = handles[stream_id] + attribute = wintypes.WORD(attr) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + return _FillConsoleOutputAttribute( + handle, attribute, length, start, byref(num_written)) + + def SetConsoleTitle(title): + return _SetConsoleTitleW(title) diff --git a/venv/Lib/site-packages/pip/_vendor/colorama/winterm.py b/venv/Lib/site-packages/pip/_vendor/colorama/winterm.py new file mode 100644 index 0000000..60309d3 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/colorama/winterm.py @@ -0,0 +1,162 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from . import win32 + + +# from wincon.h +class WinColor(object): + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 + MAGENTA = 5 + YELLOW = 6 + GREY = 7 + +# from wincon.h +class WinStyle(object): + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + BRIGHT_BACKGROUND = 0x80 # dim text, bright background + +class WinTerm(object): + + def __init__(self): + self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes + self.set_attrs(self._default) + self._default_fore = self._fore + self._default_back = self._back + self._default_style = self._style + # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style. + # So that LIGHT_EX colors and BRIGHT style do not clobber each other, + # we track them separately, since LIGHT_EX is overwritten by Fore/Back + # and BRIGHT is overwritten by Style codes. + self._light = 0 + + def get_attrs(self): + return self._fore + self._back * 16 + (self._style | self._light) + + def set_attrs(self, value): + self._fore = value & 7 + self._back = (value >> 4) & 7 + self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + + def fore(self, fore=None, light=False, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + # Emulate LIGHT_EX with BRIGHT Style + if light: + self._light |= WinStyle.BRIGHT + else: + self._light &= ~WinStyle.BRIGHT + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, light=False, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style + if light: + self._light |= WinStyle.BRIGHT_BACKGROUND + else: + self._light &= ~WinStyle.BRIGHT_BACKGROUND + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + + def get_position(self, handle): + position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition + # Because Windows coordinates are 0-based, + # and win32.SetConsoleCursorPosition expects 1-based. + position.X += 1 + position.Y += 1 + return position + + def set_cursor_position(self, position=None, on_stderr=False): + if position is None: + # I'm not currently tracking the position, so there is no default. + # position = self.get_position() + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleCursorPosition(handle, position) + + def cursor_adjust(self, x, y, on_stderr=False): + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + position = self.get_position(handle) + adjusted_position = (position.Y + y, position.X + x) + win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) + + def erase_screen(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the screen. + # 1 should clear from the cursor to the beginning of the screen. + # 2 should clear the entire screen, and move cursor to (1,1) + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + # get the number of character cells in the current buffer + cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y + # get number of character cells before current cursor position + cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = cells_in_screen - cells_before_cursor + if mode == 1: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_before_cursor + elif mode == 2: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_in_screen + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + if mode == 2: + # put the cursor where needed + win32.SetConsoleCursorPosition(handle, (1, 1)) + + def erase_line(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the line. + # 1 should clear from the cursor to the beginning of the line. + # 2 should clear the entire line. + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X + if mode == 1: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwCursorPosition.X + elif mode == 2: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwSize.X + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + + def set_title(self, title): + win32.SetConsoleTitle(title) diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/__init__.py b/venv/Lib/site-packages/pip/_vendor/distlib/__init__.py new file mode 100644 index 0000000..d4aab45 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2017 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import logging + +__version__ = '0.2.7' + +class DistlibException(Exception): + pass + +try: + from logging import NullHandler +except ImportError: # pragma: no cover + class NullHandler(logging.Handler): + def handle(self, record): pass + def emit(self, record): pass + def createLock(self): self.lock = None + +logger = logging.getLogger(__name__) +logger.addHandler(NullHandler()) diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/_backport/__init__.py b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/__init__.py new file mode 100644 index 0000000..f7dbf4c --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/__init__.py @@ -0,0 +1,6 @@ +"""Modules copied from Python 3 standard libraries, for internal use only. + +Individual classes and functions are found in d2._backport.misc. Intended +usage is to always import things missing from 3.1 from that module: the +built-in/stdlib objects will be used if found. +""" diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/_backport/misc.py b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/misc.py new file mode 100644 index 0000000..cfb318d --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/misc.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Backports for individual classes and functions.""" + +import os +import sys + +__all__ = ['cache_from_source', 'callable', 'fsencode'] + + +try: + from imp import cache_from_source +except ImportError: + def cache_from_source(py_file, debug=__debug__): + ext = debug and 'c' or 'o' + return py_file + ext + + +try: + callable = callable +except NameError: + from collections import Callable + + def callable(obj): + return isinstance(obj, Callable) + + +try: + fsencode = os.fsencode +except AttributeError: + def fsencode(filename): + if isinstance(filename, bytes): + return filename + elif isinstance(filename, str): + return filename.encode(sys.getfilesystemencoding()) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/_backport/shutil.py b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/shutil.py new file mode 100644 index 0000000..159e49e --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/shutil.py @@ -0,0 +1,761 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Utility functions for copying and archiving files and directory trees. + +XXX The functions here don't copy the resource fork or other metadata on Mac. + +""" + +import os +import sys +import stat +from os.path import abspath +import fnmatch +import collections +import errno +from . import tarfile + +try: + import bz2 + _BZ2_SUPPORTED = True +except ImportError: + _BZ2_SUPPORTED = False + +try: + from pwd import getpwnam +except ImportError: + getpwnam = None + +try: + from grp import getgrnam +except ImportError: + getgrnam = None + +__all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2", + "copytree", "move", "rmtree", "Error", "SpecialFileError", + "ExecError", "make_archive", "get_archive_formats", + "register_archive_format", "unregister_archive_format", + "get_unpack_formats", "register_unpack_format", + "unregister_unpack_format", "unpack_archive", "ignore_patterns"] + +class Error(EnvironmentError): + pass + +class SpecialFileError(EnvironmentError): + """Raised when trying to do a kind of operation (e.g. copying) which is + not supported on a special file (e.g. a named pipe)""" + +class ExecError(EnvironmentError): + """Raised when a command could not be executed""" + +class ReadError(EnvironmentError): + """Raised when an archive cannot be read""" + +class RegistryError(Exception): + """Raised when a registry operation with the archiving + and unpacking registries fails""" + + +try: + WindowsError +except NameError: + WindowsError = None + +def copyfileobj(fsrc, fdst, length=16*1024): + """copy data from file-like object fsrc to file-like object fdst""" + while 1: + buf = fsrc.read(length) + if not buf: + break + fdst.write(buf) + +def _samefile(src, dst): + # Macintosh, Unix. + if hasattr(os.path, 'samefile'): + try: + return os.path.samefile(src, dst) + except OSError: + return False + + # All other platforms: check for same pathname. + return (os.path.normcase(os.path.abspath(src)) == + os.path.normcase(os.path.abspath(dst))) + +def copyfile(src, dst): + """Copy data from src to dst""" + if _samefile(src, dst): + raise Error("`%s` and `%s` are the same file" % (src, dst)) + + for fn in [src, dst]: + try: + st = os.stat(fn) + except OSError: + # File most likely does not exist + pass + else: + # XXX What about other special files? (sockets, devices...) + if stat.S_ISFIFO(st.st_mode): + raise SpecialFileError("`%s` is a named pipe" % fn) + + with open(src, 'rb') as fsrc: + with open(dst, 'wb') as fdst: + copyfileobj(fsrc, fdst) + +def copymode(src, dst): + """Copy mode bits from src to dst""" + if hasattr(os, 'chmod'): + st = os.stat(src) + mode = stat.S_IMODE(st.st_mode) + os.chmod(dst, mode) + +def copystat(src, dst): + """Copy all stat info (mode bits, atime, mtime, flags) from src to dst""" + st = os.stat(src) + mode = stat.S_IMODE(st.st_mode) + if hasattr(os, 'utime'): + os.utime(dst, (st.st_atime, st.st_mtime)) + if hasattr(os, 'chmod'): + os.chmod(dst, mode) + if hasattr(os, 'chflags') and hasattr(st, 'st_flags'): + try: + os.chflags(dst, st.st_flags) + except OSError as why: + if (not hasattr(errno, 'EOPNOTSUPP') or + why.errno != errno.EOPNOTSUPP): + raise + +def copy(src, dst): + """Copy data and mode bits ("cp src dst"). + + The destination may be a directory. + + """ + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + copyfile(src, dst) + copymode(src, dst) + +def copy2(src, dst): + """Copy data and all stat info ("cp -p src dst"). + + The destination may be a directory. + + """ + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + copyfile(src, dst) + copystat(src, dst) + +def ignore_patterns(*patterns): + """Function that can be used as copytree() ignore parameter. + + Patterns is a sequence of glob-style patterns + that are used to exclude files""" + def _ignore_patterns(path, names): + ignored_names = [] + for pattern in patterns: + ignored_names.extend(fnmatch.filter(names, pattern)) + return set(ignored_names) + return _ignore_patterns + +def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, + ignore_dangling_symlinks=False): + """Recursively copy a directory tree. + + The destination directory must not already exist. + If exception(s) occur, an Error is raised with a list of reasons. + + If the optional symlinks flag is true, symbolic links in the + source tree result in symbolic links in the destination tree; if + it is false, the contents of the files pointed to by symbolic + links are copied. If the file pointed by the symlink doesn't + exist, an exception will be added in the list of errors raised in + an Error exception at the end of the copy process. + + You can set the optional ignore_dangling_symlinks flag to true if you + want to silence this exception. Notice that this has no effect on + platforms that don't support os.symlink. + + The optional ignore argument is a callable. If given, it + is called with the `src` parameter, which is the directory + being visited by copytree(), and `names` which is the list of + `src` contents, as returned by os.listdir(): + + callable(src, names) -> ignored_names + + Since copytree() is called recursively, the callable will be + called once for each directory that is copied. It returns a + list of names relative to the `src` directory that should + not be copied. + + The optional copy_function argument is a callable that will be used + to copy each file. It will be called with the source path and the + destination path as arguments. By default, copy2() is used, but any + function that supports the same signature (like copy()) can be used. + + """ + names = os.listdir(src) + if ignore is not None: + ignored_names = ignore(src, names) + else: + ignored_names = set() + + os.makedirs(dst) + errors = [] + for name in names: + if name in ignored_names: + continue + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + try: + if os.path.islink(srcname): + linkto = os.readlink(srcname) + if symlinks: + os.symlink(linkto, dstname) + else: + # ignore dangling symlink if the flag is on + if not os.path.exists(linkto) and ignore_dangling_symlinks: + continue + # otherwise let the copy occurs. copy2 will raise an error + copy_function(srcname, dstname) + elif os.path.isdir(srcname): + copytree(srcname, dstname, symlinks, ignore, copy_function) + else: + # Will raise a SpecialFileError for unsupported file types + copy_function(srcname, dstname) + # catch the Error from the recursive copytree so that we can + # continue with other files + except Error as err: + errors.extend(err.args[0]) + except EnvironmentError as why: + errors.append((srcname, dstname, str(why))) + try: + copystat(src, dst) + except OSError as why: + if WindowsError is not None and isinstance(why, WindowsError): + # Copying file access times may fail on Windows + pass + else: + errors.extend((src, dst, str(why))) + if errors: + raise Error(errors) + +def rmtree(path, ignore_errors=False, onerror=None): + """Recursively delete a directory tree. + + If ignore_errors is set, errors are ignored; otherwise, if onerror + is set, it is called to handle the error with arguments (func, + path, exc_info) where func is os.listdir, os.remove, or os.rmdir; + path is the argument to that function that caused it to fail; and + exc_info is a tuple returned by sys.exc_info(). If ignore_errors + is false and onerror is None, an exception is raised. + + """ + if ignore_errors: + def onerror(*args): + pass + elif onerror is None: + def onerror(*args): + raise + try: + if os.path.islink(path): + # symlinks to directories are forbidden, see bug #1669 + raise OSError("Cannot call rmtree on a symbolic link") + except OSError: + onerror(os.path.islink, path, sys.exc_info()) + # can't continue even if onerror hook returns + return + names = [] + try: + names = os.listdir(path) + except os.error: + onerror(os.listdir, path, sys.exc_info()) + for name in names: + fullname = os.path.join(path, name) + try: + mode = os.lstat(fullname).st_mode + except os.error: + mode = 0 + if stat.S_ISDIR(mode): + rmtree(fullname, ignore_errors, onerror) + else: + try: + os.remove(fullname) + except os.error: + onerror(os.remove, fullname, sys.exc_info()) + try: + os.rmdir(path) + except os.error: + onerror(os.rmdir, path, sys.exc_info()) + + +def _basename(path): + # A basename() variant which first strips the trailing slash, if present. + # Thus we always get the last component of the path, even for directories. + return os.path.basename(path.rstrip(os.path.sep)) + +def move(src, dst): + """Recursively move a file or directory to another location. This is + similar to the Unix "mv" command. + + If the destination is a directory or a symlink to a directory, the source + is moved inside the directory. The destination path must not already + exist. + + If the destination already exists but is not a directory, it may be + overwritten depending on os.rename() semantics. + + If the destination is on our current filesystem, then rename() is used. + Otherwise, src is copied to the destination and then removed. + A lot more could be done here... A look at a mv.c shows a lot of + the issues this implementation glosses over. + + """ + real_dst = dst + if os.path.isdir(dst): + if _samefile(src, dst): + # We might be on a case insensitive filesystem, + # perform the rename anyway. + os.rename(src, dst) + return + + real_dst = os.path.join(dst, _basename(src)) + if os.path.exists(real_dst): + raise Error("Destination path '%s' already exists" % real_dst) + try: + os.rename(src, real_dst) + except OSError: + if os.path.isdir(src): + if _destinsrc(src, dst): + raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst)) + copytree(src, real_dst, symlinks=True) + rmtree(src) + else: + copy2(src, real_dst) + os.unlink(src) + +def _destinsrc(src, dst): + src = abspath(src) + dst = abspath(dst) + if not src.endswith(os.path.sep): + src += os.path.sep + if not dst.endswith(os.path.sep): + dst += os.path.sep + return dst.startswith(src) + +def _get_gid(name): + """Returns a gid, given a group name.""" + if getgrnam is None or name is None: + return None + try: + result = getgrnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _get_uid(name): + """Returns an uid, given a user name.""" + if getpwnam is None or name is None: + return None + try: + result = getpwnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, + owner=None, group=None, logger=None): + """Create a (possibly compressed) tar file from all the files under + 'base_dir'. + + 'compress' must be "gzip" (the default), "bzip2", or None. + + 'owner' and 'group' can be used to define an owner and a group for the + archive that is being built. If not provided, the current owner and group + will be used. + + The output tar file will be named 'base_name' + ".tar", possibly plus + the appropriate compression extension (".gz", or ".bz2"). + + Returns the output filename. + """ + tar_compression = {'gzip': 'gz', None: ''} + compress_ext = {'gzip': '.gz'} + + if _BZ2_SUPPORTED: + tar_compression['bzip2'] = 'bz2' + compress_ext['bzip2'] = '.bz2' + + # flags for compression program, each element of list will be an argument + if compress is not None and compress not in compress_ext: + raise ValueError("bad value for 'compress', or compression format not " + "supported : {0}".format(compress)) + + archive_name = base_name + '.tar' + compress_ext.get(compress, '') + archive_dir = os.path.dirname(archive_name) + + if not os.path.exists(archive_dir): + if logger is not None: + logger.info("creating %s", archive_dir) + if not dry_run: + os.makedirs(archive_dir) + + # creating the tarball + if logger is not None: + logger.info('Creating tar archive') + + uid = _get_uid(owner) + gid = _get_gid(group) + + def _set_uid_gid(tarinfo): + if gid is not None: + tarinfo.gid = gid + tarinfo.gname = group + if uid is not None: + tarinfo.uid = uid + tarinfo.uname = owner + return tarinfo + + if not dry_run: + tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) + try: + tar.add(base_dir, filter=_set_uid_gid) + finally: + tar.close() + + return archive_name + +def _call_external_zip(base_dir, zip_filename, verbose=False, dry_run=False): + # XXX see if we want to keep an external call here + if verbose: + zipoptions = "-r" + else: + zipoptions = "-rq" + from distutils.errors import DistutilsExecError + from distutils.spawn import spawn + try: + spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run) + except DistutilsExecError: + # XXX really should distinguish between "couldn't find + # external 'zip' command" and "zip failed". + raise ExecError("unable to create zip file '%s': " + "could neither import the 'zipfile' module nor " + "find a standalone zip utility") % zip_filename + +def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None): + """Create a zip file from all the files under 'base_dir'. + + The output zip file will be named 'base_name' + ".zip". Uses either the + "zipfile" Python module (if available) or the InfoZIP "zip" utility + (if installed and found on the default search path). If neither tool is + available, raises ExecError. Returns the name of the output zip + file. + """ + zip_filename = base_name + ".zip" + archive_dir = os.path.dirname(base_name) + + if not os.path.exists(archive_dir): + if logger is not None: + logger.info("creating %s", archive_dir) + if not dry_run: + os.makedirs(archive_dir) + + # If zipfile module is not available, try spawning an external 'zip' + # command. + try: + import zipfile + except ImportError: + zipfile = None + + if zipfile is None: + _call_external_zip(base_dir, zip_filename, verbose, dry_run) + else: + if logger is not None: + logger.info("creating '%s' and adding '%s' to it", + zip_filename, base_dir) + + if not dry_run: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) + + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zip.write(path, path) + if logger is not None: + logger.info("adding '%s'", path) + zip.close() + + return zip_filename + +_ARCHIVE_FORMATS = { + 'gztar': (_make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), + 'bztar': (_make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), + 'tar': (_make_tarball, [('compress', None)], "uncompressed tar file"), + 'zip': (_make_zipfile, [], "ZIP file"), + } + +if _BZ2_SUPPORTED: + _ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')], + "bzip2'ed tar-file") + +def get_archive_formats(): + """Returns a list of supported formats for archiving and unarchiving. + + Each element of the returned sequence is a tuple (name, description) + """ + formats = [(name, registry[2]) for name, registry in + _ARCHIVE_FORMATS.items()] + formats.sort() + return formats + +def register_archive_format(name, function, extra_args=None, description=''): + """Registers an archive format. + + name is the name of the format. function is the callable that will be + used to create archives. If provided, extra_args is a sequence of + (name, value) tuples that will be passed as arguments to the callable. + description can be provided to describe the format, and will be returned + by the get_archive_formats() function. + """ + if extra_args is None: + extra_args = [] + if not isinstance(function, collections.Callable): + raise TypeError('The %s object is not callable' % function) + if not isinstance(extra_args, (tuple, list)): + raise TypeError('extra_args needs to be a sequence') + for element in extra_args: + if not isinstance(element, (tuple, list)) or len(element) !=2: + raise TypeError('extra_args elements are : (arg_name, value)') + + _ARCHIVE_FORMATS[name] = (function, extra_args, description) + +def unregister_archive_format(name): + del _ARCHIVE_FORMATS[name] + +def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, + dry_run=0, owner=None, group=None, logger=None): + """Create an archive file (eg. zip or tar). + + 'base_name' is the name of the file to create, minus any format-specific + extension; 'format' is the archive format: one of "zip", "tar", "bztar" + or "gztar". + + 'root_dir' is a directory that will be the root directory of the + archive; ie. we typically chdir into 'root_dir' before creating the + archive. 'base_dir' is the directory where we start archiving from; + ie. 'base_dir' will be the common prefix of all files and + directories in the archive. 'root_dir' and 'base_dir' both default + to the current directory. Returns the name of the archive file. + + 'owner' and 'group' are used when creating a tar archive. By default, + uses the current owner and group. + """ + save_cwd = os.getcwd() + if root_dir is not None: + if logger is not None: + logger.debug("changing into '%s'", root_dir) + base_name = os.path.abspath(base_name) + if not dry_run: + os.chdir(root_dir) + + if base_dir is None: + base_dir = os.curdir + + kwargs = {'dry_run': dry_run, 'logger': logger} + + try: + format_info = _ARCHIVE_FORMATS[format] + except KeyError: + raise ValueError("unknown archive format '%s'" % format) + + func = format_info[0] + for arg, val in format_info[1]: + kwargs[arg] = val + + if format != 'zip': + kwargs['owner'] = owner + kwargs['group'] = group + + try: + filename = func(base_name, base_dir, **kwargs) + finally: + if root_dir is not None: + if logger is not None: + logger.debug("changing back to '%s'", save_cwd) + os.chdir(save_cwd) + + return filename + + +def get_unpack_formats(): + """Returns a list of supported formats for unpacking. + + Each element of the returned sequence is a tuple + (name, extensions, description) + """ + formats = [(name, info[0], info[3]) for name, info in + _UNPACK_FORMATS.items()] + formats.sort() + return formats + +def _check_unpack_options(extensions, function, extra_args): + """Checks what gets registered as an unpacker.""" + # first make sure no other unpacker is registered for this extension + existing_extensions = {} + for name, info in _UNPACK_FORMATS.items(): + for ext in info[0]: + existing_extensions[ext] = name + + for extension in extensions: + if extension in existing_extensions: + msg = '%s is already registered for "%s"' + raise RegistryError(msg % (extension, + existing_extensions[extension])) + + if not isinstance(function, collections.Callable): + raise TypeError('The registered function must be a callable') + + +def register_unpack_format(name, extensions, function, extra_args=None, + description=''): + """Registers an unpack format. + + `name` is the name of the format. `extensions` is a list of extensions + corresponding to the format. + + `function` is the callable that will be + used to unpack archives. The callable will receive archives to unpack. + If it's unable to handle an archive, it needs to raise a ReadError + exception. + + If provided, `extra_args` is a sequence of + (name, value) tuples that will be passed as arguments to the callable. + description can be provided to describe the format, and will be returned + by the get_unpack_formats() function. + """ + if extra_args is None: + extra_args = [] + _check_unpack_options(extensions, function, extra_args) + _UNPACK_FORMATS[name] = extensions, function, extra_args, description + +def unregister_unpack_format(name): + """Removes the pack format from the registry.""" + del _UNPACK_FORMATS[name] + +def _ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + if not os.path.isdir(dirname): + os.makedirs(dirname) + +def _unpack_zipfile(filename, extract_dir): + """Unpack zip `filename` to `extract_dir` + """ + try: + import zipfile + except ImportError: + raise ReadError('zlib not supported, cannot unpack this archive.') + + if not zipfile.is_zipfile(filename): + raise ReadError("%s is not a zip file" % filename) + + zip = zipfile.ZipFile(filename) + try: + for info in zip.infolist(): + name = info.filename + + # don't extract absolute paths or ones with .. in them + if name.startswith('/') or '..' in name: + continue + + target = os.path.join(extract_dir, *name.split('/')) + if not target: + continue + + _ensure_directory(target) + if not name.endswith('/'): + # file + data = zip.read(info.filename) + f = open(target, 'wb') + try: + f.write(data) + finally: + f.close() + del data + finally: + zip.close() + +def _unpack_tarfile(filename, extract_dir): + """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir` + """ + try: + tarobj = tarfile.open(filename) + except tarfile.TarError: + raise ReadError( + "%s is not a compressed or uncompressed tar file" % filename) + try: + tarobj.extractall(extract_dir) + finally: + tarobj.close() + +_UNPACK_FORMATS = { + 'gztar': (['.tar.gz', '.tgz'], _unpack_tarfile, [], "gzip'ed tar-file"), + 'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"), + 'zip': (['.zip'], _unpack_zipfile, [], "ZIP file") + } + +if _BZ2_SUPPORTED: + _UNPACK_FORMATS['bztar'] = (['.bz2'], _unpack_tarfile, [], + "bzip2'ed tar-file") + +def _find_unpack_format(filename): + for name, info in _UNPACK_FORMATS.items(): + for extension in info[0]: + if filename.endswith(extension): + return name + return None + +def unpack_archive(filename, extract_dir=None, format=None): + """Unpack an archive. + + `filename` is the name of the archive. + + `extract_dir` is the name of the target directory, where the archive + is unpacked. If not provided, the current working directory is used. + + `format` is the archive format: one of "zip", "tar", or "gztar". Or any + other registered format. If not provided, unpack_archive will use the + filename extension and see if an unpacker was registered for that + extension. + + In case none is found, a ValueError is raised. + """ + if extract_dir is None: + extract_dir = os.getcwd() + + if format is not None: + try: + format_info = _UNPACK_FORMATS[format] + except KeyError: + raise ValueError("Unknown unpack format '{0}'".format(format)) + + func = format_info[1] + func(filename, extract_dir, **dict(format_info[2])) + else: + # we need to look at the registered unpackers supported extensions + format = _find_unpack_format(filename) + if format is None: + raise ReadError("Unknown archive format '{0}'".format(filename)) + + func = _UNPACK_FORMATS[format][1] + kwargs = dict(_UNPACK_FORMATS[format][2]) + func(filename, extract_dir, **kwargs) diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg new file mode 100644 index 0000000..1746bd0 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg @@ -0,0 +1,84 @@ +[posix_prefix] +# Configuration directories. Some of these come straight out of the +# configure script. They are for implementing the other variables, not to +# be used directly in [resource_locations]. +confdir = /etc +datadir = /usr/share +libdir = /usr/lib +statedir = /var +# User resource directory +local = ~/.local/{distribution.name} + +stdlib = {base}/lib/python{py_version_short} +platstdlib = {platbase}/lib/python{py_version_short} +purelib = {base}/lib/python{py_version_short}/site-packages +platlib = {platbase}/lib/python{py_version_short}/site-packages +include = {base}/include/python{py_version_short}{abiflags} +platinclude = {platbase}/include/python{py_version_short}{abiflags} +data = {base} + +[posix_home] +stdlib = {base}/lib/python +platstdlib = {base}/lib/python +purelib = {base}/lib/python +platlib = {base}/lib/python +include = {base}/include/python +platinclude = {base}/include/python +scripts = {base}/bin +data = {base} + +[nt] +stdlib = {base}/Lib +platstdlib = {base}/Lib +purelib = {base}/Lib/site-packages +platlib = {base}/Lib/site-packages +include = {base}/Include +platinclude = {base}/Include +scripts = {base}/Scripts +data = {base} + +[os2] +stdlib = {base}/Lib +platstdlib = {base}/Lib +purelib = {base}/Lib/site-packages +platlib = {base}/Lib/site-packages +include = {base}/Include +platinclude = {base}/Include +scripts = {base}/Scripts +data = {base} + +[os2_home] +stdlib = {userbase}/lib/python{py_version_short} +platstdlib = {userbase}/lib/python{py_version_short} +purelib = {userbase}/lib/python{py_version_short}/site-packages +platlib = {userbase}/lib/python{py_version_short}/site-packages +include = {userbase}/include/python{py_version_short} +scripts = {userbase}/bin +data = {userbase} + +[nt_user] +stdlib = {userbase}/Python{py_version_nodot} +platstdlib = {userbase}/Python{py_version_nodot} +purelib = {userbase}/Python{py_version_nodot}/site-packages +platlib = {userbase}/Python{py_version_nodot}/site-packages +include = {userbase}/Python{py_version_nodot}/Include +scripts = {userbase}/Scripts +data = {userbase} + +[posix_user] +stdlib = {userbase}/lib/python{py_version_short} +platstdlib = {userbase}/lib/python{py_version_short} +purelib = {userbase}/lib/python{py_version_short}/site-packages +platlib = {userbase}/lib/python{py_version_short}/site-packages +include = {userbase}/include/python{py_version_short} +scripts = {userbase}/bin +data = {userbase} + +[osx_framework_user] +stdlib = {userbase}/lib/python +platstdlib = {userbase}/lib/python +purelib = {userbase}/lib/python/site-packages +platlib = {userbase}/lib/python/site-packages +include = {userbase}/include +scripts = {userbase}/bin +data = {userbase} diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/_backport/sysconfig.py b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/sysconfig.py new file mode 100644 index 0000000..1df3aba --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/sysconfig.py @@ -0,0 +1,788 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Access to Python's configuration information.""" + +import codecs +import os +import re +import sys +from os.path import pardir, realpath +try: + import configparser +except ImportError: + import ConfigParser as configparser + + +__all__ = [ + 'get_config_h_filename', + 'get_config_var', + 'get_config_vars', + 'get_makefile_filename', + 'get_path', + 'get_path_names', + 'get_paths', + 'get_platform', + 'get_python_version', + 'get_scheme_names', + 'parse_config_h', +] + + +def _safe_realpath(path): + try: + return realpath(path) + except OSError: + return path + + +if sys.executable: + _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable)) +else: + # sys.executable can be empty if argv[0] has been changed and Python is + # unable to retrieve the real program name + _PROJECT_BASE = _safe_realpath(os.getcwd()) + +if os.name == "nt" and "pcbuild" in _PROJECT_BASE[-8:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir)) +# PC/VS7.1 +if os.name == "nt" and "\\pc\\v" in _PROJECT_BASE[-10:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) +# PC/AMD64 +if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) + + +def is_python_build(): + for fn in ("Setup.dist", "Setup.local"): + if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)): + return True + return False + +_PYTHON_BUILD = is_python_build() + +_cfg_read = False + +def _ensure_cfg_read(): + global _cfg_read + if not _cfg_read: + from ..resources import finder + backport_package = __name__.rsplit('.', 1)[0] + _finder = finder(backport_package) + _cfgfile = _finder.find('sysconfig.cfg') + assert _cfgfile, 'sysconfig.cfg exists' + with _cfgfile.as_stream() as s: + _SCHEMES.readfp(s) + if _PYTHON_BUILD: + for scheme in ('posix_prefix', 'posix_home'): + _SCHEMES.set(scheme, 'include', '{srcdir}/Include') + _SCHEMES.set(scheme, 'platinclude', '{projectbase}/.') + + _cfg_read = True + + +_SCHEMES = configparser.RawConfigParser() +_VAR_REPL = re.compile(r'\{([^{]*?)\}') + +def _expand_globals(config): + _ensure_cfg_read() + if config.has_section('globals'): + globals = config.items('globals') + else: + globals = tuple() + + sections = config.sections() + for section in sections: + if section == 'globals': + continue + for option, value in globals: + if config.has_option(section, option): + continue + config.set(section, option, value) + config.remove_section('globals') + + # now expanding local variables defined in the cfg file + # + for section in config.sections(): + variables = dict(config.items(section)) + + def _replacer(matchobj): + name = matchobj.group(1) + if name in variables: + return variables[name] + return matchobj.group(0) + + for option, value in config.items(section): + config.set(section, option, _VAR_REPL.sub(_replacer, value)) + +#_expand_globals(_SCHEMES) + + # FIXME don't rely on sys.version here, its format is an implementation detail + # of CPython, use sys.version_info or sys.hexversion +_PY_VERSION = sys.version.split()[0] +_PY_VERSION_SHORT = sys.version[:3] +_PY_VERSION_SHORT_NO_DOT = _PY_VERSION[0] + _PY_VERSION[2] +_PREFIX = os.path.normpath(sys.prefix) +_EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +_CONFIG_VARS = None +_USER_BASE = None + + +def _subst_vars(path, local_vars): + """In the string `path`, replace tokens like {some.thing} with the + corresponding value from the map `local_vars`. + + If there is no corresponding value, leave the token unchanged. + """ + def _replacer(matchobj): + name = matchobj.group(1) + if name in local_vars: + return local_vars[name] + elif name in os.environ: + return os.environ[name] + return matchobj.group(0) + return _VAR_REPL.sub(_replacer, path) + + +def _extend_dict(target_dict, other_dict): + target_keys = target_dict.keys() + for key, value in other_dict.items(): + if key in target_keys: + continue + target_dict[key] = value + + +def _expand_vars(scheme, vars): + res = {} + if vars is None: + vars = {} + _extend_dict(vars, get_config_vars()) + + for key, value in _SCHEMES.items(scheme): + if os.name in ('posix', 'nt'): + value = os.path.expanduser(value) + res[key] = os.path.normpath(_subst_vars(value, vars)) + return res + + +def format_value(value, vars): + def _replacer(matchobj): + name = matchobj.group(1) + if name in vars: + return vars[name] + return matchobj.group(0) + return _VAR_REPL.sub(_replacer, value) + + +def _get_default_scheme(): + if os.name == 'posix': + # the default scheme for posix is posix_prefix + return 'posix_prefix' + return os.name + + +def _getuserbase(): + env_base = os.environ.get("PYTHONUSERBASE", None) + + def joinuser(*args): + return os.path.expanduser(os.path.join(*args)) + + # what about 'os2emx', 'riscos' ? + if os.name == "nt": + base = os.environ.get("APPDATA") or "~" + if env_base: + return env_base + else: + return joinuser(base, "Python") + + if sys.platform == "darwin": + framework = get_config_var("PYTHONFRAMEWORK") + if framework: + if env_base: + return env_base + else: + return joinuser("~", "Library", framework, "%d.%d" % + sys.version_info[:2]) + + if env_base: + return env_base + else: + return joinuser("~", ".local") + + +def _parse_makefile(filename, vars=None): + """Parse a Makefile-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + # Regexes needed for parsing Makefile (and similar syntaxes, + # like old-style Setup files). + _variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") + _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") + _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") + + if vars is None: + vars = {} + done = {} + notdone = {} + + with codecs.open(filename, encoding='utf-8', errors="surrogateescape") as f: + lines = f.readlines() + + for line in lines: + if line.startswith('#') or line.strip() == '': + continue + m = _variable_rx.match(line) + if m: + n, v = m.group(1, 2) + v = v.strip() + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: + notdone[n] = v + else: + try: + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v + + # do variable interpolation here + variables = list(notdone.keys()) + + # Variables with a 'PY_' prefix in the makefile. These need to + # be made available without that prefix through sysconfig. + # Special care is needed to ensure that variable expansion works, even + # if the expansion uses the name without a prefix. + renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') + + while len(variables) > 0: + for name in tuple(variables): + value = notdone[name] + m = _findvar1_rx.search(value) or _findvar2_rx.search(value) + if m is not None: + n = m.group(1) + found = True + if n in done: + item = str(done[n]) + elif n in notdone: + # get it on a subsequent round + found = False + elif n in os.environ: + # do it like make: fall back to environment + item = os.environ[n] + + elif n in renamed_variables: + if (name.startswith('PY_') and + name[3:] in renamed_variables): + item = "" + + elif 'PY_' + n in notdone: + found = False + + else: + item = str(done['PY_' + n]) + + else: + done[n] = item = "" + + if found: + after = value[m.end():] + value = value[:m.start()] + item + after + if "$" in after: + notdone[name] = value + else: + try: + value = int(value) + except ValueError: + done[name] = value.strip() + else: + done[name] = value + variables.remove(name) + + if (name.startswith('PY_') and + name[3:] in renamed_variables): + + name = name[3:] + if name not in done: + done[name] = value + + else: + # bogus variable reference (e.g. "prefix=$/opt/python"); + # just drop it since we can't deal + done[name] = value + variables.remove(name) + + # strip spurious spaces + for k, v in done.items(): + if isinstance(v, str): + done[k] = v.strip() + + # save the results in the global dictionary + vars.update(done) + return vars + + +def get_makefile_filename(): + """Return the path of the Makefile.""" + if _PYTHON_BUILD: + return os.path.join(_PROJECT_BASE, "Makefile") + if hasattr(sys, 'abiflags'): + config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, sys.abiflags) + else: + config_dir_name = 'config' + return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') + + +def _init_posix(vars): + """Initialize the module as appropriate for POSIX systems.""" + # load the installed Makefile: + makefile = get_makefile_filename() + try: + _parse_makefile(makefile, vars) + except IOError as e: + msg = "invalid Python installation: unable to open %s" % makefile + if hasattr(e, "strerror"): + msg = msg + " (%s)" % e.strerror + raise IOError(msg) + # load the installed pyconfig.h: + config_h = get_config_h_filename() + try: + with open(config_h) as f: + parse_config_h(f, vars) + except IOError as e: + msg = "invalid Python installation: unable to open %s" % config_h + if hasattr(e, "strerror"): + msg = msg + " (%s)" % e.strerror + raise IOError(msg) + # On AIX, there are wrong paths to the linker scripts in the Makefile + # -- these paths are relative to the Python source, but when installed + # the scripts are in another directory. + if _PYTHON_BUILD: + vars['LDSHARED'] = vars['BLDSHARED'] + + +def _init_non_posix(vars): + """Initialize the module as appropriate for NT""" + # set basic install directories + vars['LIBDEST'] = get_path('stdlib') + vars['BINLIBDEST'] = get_path('platstdlib') + vars['INCLUDEPY'] = get_path('include') + vars['SO'] = '.pyd' + vars['EXE'] = '.exe' + vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT + vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) + +# +# public APIs +# + + +def parse_config_h(fp, vars=None): + """Parse a config.h-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + if vars is None: + vars = {} + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") + + while True: + line = fp.readline() + if not line: + break + m = define_rx.match(line) + if m: + n, v = m.group(1, 2) + try: + v = int(v) + except ValueError: + pass + vars[n] = v + else: + m = undef_rx.match(line) + if m: + vars[m.group(1)] = 0 + return vars + + +def get_config_h_filename(): + """Return the path of pyconfig.h.""" + if _PYTHON_BUILD: + if os.name == "nt": + inc_dir = os.path.join(_PROJECT_BASE, "PC") + else: + inc_dir = _PROJECT_BASE + else: + inc_dir = get_path('platinclude') + return os.path.join(inc_dir, 'pyconfig.h') + + +def get_scheme_names(): + """Return a tuple containing the schemes names.""" + return tuple(sorted(_SCHEMES.sections())) + + +def get_path_names(): + """Return a tuple containing the paths names.""" + # xxx see if we want a static list + return _SCHEMES.options('posix_prefix') + + +def get_paths(scheme=_get_default_scheme(), vars=None, expand=True): + """Return a mapping containing an install scheme. + + ``scheme`` is the install scheme name. If not provided, it will + return the default scheme for the current platform. + """ + _ensure_cfg_read() + if expand: + return _expand_vars(scheme, vars) + else: + return dict(_SCHEMES.items(scheme)) + + +def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True): + """Return a path corresponding to the scheme. + + ``scheme`` is the install scheme name. + """ + return get_paths(scheme, vars, expand)[name] + + +def get_config_vars(*args): + """With no arguments, return a dictionary of all configuration + variables relevant for the current platform. + + On Unix, this means every variable defined in Python's installed Makefile; + On Windows and Mac OS it's a much smaller set. + + With arguments, return a list of values that result from looking up + each argument in the configuration variable dictionary. + """ + global _CONFIG_VARS + if _CONFIG_VARS is None: + _CONFIG_VARS = {} + # Normalized versions of prefix and exec_prefix are handy to have; + # in fact, these are the standard versions used most places in the + # distutils2 module. + _CONFIG_VARS['prefix'] = _PREFIX + _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX + _CONFIG_VARS['py_version'] = _PY_VERSION + _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT + _CONFIG_VARS['py_version_nodot'] = _PY_VERSION[0] + _PY_VERSION[2] + _CONFIG_VARS['base'] = _PREFIX + _CONFIG_VARS['platbase'] = _EXEC_PREFIX + _CONFIG_VARS['projectbase'] = _PROJECT_BASE + try: + _CONFIG_VARS['abiflags'] = sys.abiflags + except AttributeError: + # sys.abiflags may not be defined on all platforms. + _CONFIG_VARS['abiflags'] = '' + + if os.name in ('nt', 'os2'): + _init_non_posix(_CONFIG_VARS) + if os.name == 'posix': + _init_posix(_CONFIG_VARS) + # Setting 'userbase' is done below the call to the + # init function to enable using 'get_config_var' in + # the init-function. + if sys.version >= '2.6': + _CONFIG_VARS['userbase'] = _getuserbase() + + if 'srcdir' not in _CONFIG_VARS: + _CONFIG_VARS['srcdir'] = _PROJECT_BASE + else: + _CONFIG_VARS['srcdir'] = _safe_realpath(_CONFIG_VARS['srcdir']) + + # Convert srcdir into an absolute path if it appears necessary. + # Normally it is relative to the build directory. However, during + # testing, for example, we might be running a non-installed python + # from a different directory. + if _PYTHON_BUILD and os.name == "posix": + base = _PROJECT_BASE + try: + cwd = os.getcwd() + except OSError: + cwd = None + if (not os.path.isabs(_CONFIG_VARS['srcdir']) and + base != cwd): + # srcdir is relative and we are not in the same directory + # as the executable. Assume executable is in the build + # directory and make srcdir absolute. + srcdir = os.path.join(base, _CONFIG_VARS['srcdir']) + _CONFIG_VARS['srcdir'] = os.path.normpath(srcdir) + + if sys.platform == 'darwin': + kernel_version = os.uname()[2] # Kernel version (8.4.3) + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # On Mac OS X before 10.4, check if -arch and -isysroot + # are in CFLAGS or LDFLAGS and remove them if they are. + # This is needed when building extensions on a 10.3 system + # using a universal build of python. + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + flags = _CONFIG_VARS[key] + flags = re.sub(r'-arch\s+\w+\s', ' ', flags) + flags = re.sub('-isysroot [^ \t]*', ' ', flags) + _CONFIG_VARS[key] = flags + else: + # Allow the user to override the architecture flags using + # an environment variable. + # NOTE: This name was introduced by Apple in OSX 10.5 and + # is used by several scripting languages distributed with + # that OS release. + if 'ARCHFLAGS' in os.environ: + arch = os.environ['ARCHFLAGS'] + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _CONFIG_VARS[key] + flags = re.sub(r'-arch\s+\w+\s', ' ', flags) + flags = flags + ' ' + arch + _CONFIG_VARS[key] = flags + + # If we're on OSX 10.5 or later and the user tries to + # compiles an extension using an SDK that is not present + # on the current machine it is better to not use an SDK + # than to fail. + # + # The major usecase for this is users using a Python.org + # binary installer on OSX 10.6: that installer uses + # the 10.4u SDK, but that SDK is not installed by default + # when you install Xcode. + # + CFLAGS = _CONFIG_VARS.get('CFLAGS', '') + m = re.search(r'-isysroot\s+(\S+)', CFLAGS) + if m is not None: + sdk = m.group(1) + if not os.path.exists(sdk): + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _CONFIG_VARS[key] + flags = re.sub(r'-isysroot\s+\S+(\s|$)', ' ', flags) + _CONFIG_VARS[key] = flags + + if args: + vals = [] + for name in args: + vals.append(_CONFIG_VARS.get(name)) + return vals + else: + return _CONFIG_VARS + + +def get_config_var(name): + """Return the value of a single variable using the dictionary returned by + 'get_config_vars()'. + + Equivalent to get_config_vars().get(name) + """ + return get_config_vars().get(name) + + +def get_platform(): + """Return a string that identifies the current platform. + + This is used mainly to distinguish platform-specific build directories and + platform-specific built distributions. Typically includes the OS name + and version and the architecture (as supplied by 'os.uname()'), + although the exact information included depends on the OS; eg. for IRIX + the architecture isn't particularly important (IRIX only runs on SGI + hardware), but for Linux the kernel version isn't particularly + important. + + Examples of returned values: + linux-i586 + linux-alpha (?) + solaris-2.6-sun4u + irix-5.3 + irix64-6.2 + + Windows will return one of: + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win-ia64 (64bit Windows on Itanium) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. + """ + if os.name == 'nt': + # sniff sys.version for architecture. + prefix = " bit (" + i = sys.version.find(prefix) + if i == -1: + return sys.platform + j = sys.version.find(")", i) + look = sys.version[i+len(prefix):j].lower() + if look == 'amd64': + return 'win-amd64' + if look == 'itanium': + return 'win-ia64' + return sys.platform + + if os.name != "posix" or not hasattr(os, 'uname'): + # XXX what about the architecture? NT is Intel or Alpha, + # Mac OS is M68k or PPC, etc. + return sys.platform + + # Try to distinguish various flavours of Unix + osname, host, release, version, machine = os.uname() + + # Convert the OS name to lowercase, remove '/' characters + # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") + osname = osname.lower().replace('/', '') + machine = machine.replace(' ', '_') + machine = machine.replace('/', '-') + + if osname[:5] == "linux": + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return "%s-%s" % (osname, machine) + elif osname[:5] == "sunos": + if release[0] >= "5": # SunOS 5 == Solaris 2 + osname = "solaris" + release = "%d.%s" % (int(release[0]) - 3, release[2:]) + # fall through to standard osname-release-machine representation + elif osname[:4] == "irix": # could be "irix64"! + return "%s-%s" % (osname, release) + elif osname[:3] == "aix": + return "%s-%s.%s" % (osname, version, release) + elif osname[:6] == "cygwin": + osname = "cygwin" + rel_re = re.compile(r'[\d.]+') + m = rel_re.match(release) + if m: + release = m.group() + elif osname[:6] == "darwin": + # + # For our purposes, we'll assume that the system version from + # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set + # to. This makes the compatibility story a bit more sane because the + # machine is going to compile and link as if it were + # MACOSX_DEPLOYMENT_TARGET. + cfgvars = get_config_vars() + macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') + + if True: + # Always calculate the release of the running machine, + # needed to determine if we can build fat binaries or not. + + macrelease = macver + # Get the system version. Reading this plist is a documented + # way to get the system version (see the documentation for + # the Gestalt Manager) + try: + f = open('/System/Library/CoreServices/SystemVersion.plist') + except IOError: + # We're on a plain darwin box, fall back to the default + # behaviour. + pass + else: + try: + m = re.search(r'<key>ProductUserVisibleVersion</key>\s*' + r'<string>(.*?)</string>', f.read()) + finally: + f.close() + if m is not None: + macrelease = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + + if not macver: + macver = macrelease + + if macver: + release = macver + osname = "macosx" + + if ((macrelease + '.') >= '10.4.' and + '-arch' in get_config_vars().get('CFLAGS', '').strip()): + # The universal build will build fat binaries, but not on + # systems before 10.4 + # + # Try to detect 4-way universal builds, those have machine-type + # 'universal' instead of 'fat'. + + machine = 'fat' + cflags = get_config_vars().get('CFLAGS') + + archs = re.findall(r'-arch\s+(\S+)', cflags) + archs = tuple(sorted(set(archs))) + + if len(archs) == 1: + machine = archs[0] + elif archs == ('i386', 'ppc'): + machine = 'fat' + elif archs == ('i386', 'x86_64'): + machine = 'intel' + elif archs == ('i386', 'ppc', 'x86_64'): + machine = 'fat3' + elif archs == ('ppc64', 'x86_64'): + machine = 'fat64' + elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): + machine = 'universal' + else: + raise ValueError( + "Don't know machine value for archs=%r" % (archs,)) + + elif machine == 'i386': + # On OSX the machine type returned by uname is always the + # 32-bit variant, even if the executable architecture is + # the 64-bit variant + if sys.maxsize >= 2**32: + machine = 'x86_64' + + elif machine in ('PowerPC', 'Power_Macintosh'): + # Pick a sane name for the PPC architecture. + # See 'i386' case + if sys.maxsize >= 2**32: + machine = 'ppc64' + else: + machine = 'ppc' + + return "%s-%s-%s" % (osname, release, machine) + + +def get_python_version(): + return _PY_VERSION_SHORT + + +def _print_dict(title, data): + for index, (key, value) in enumerate(sorted(data.items())): + if index == 0: + print('%s: ' % (title)) + print('\t%s = "%s"' % (key, value)) + + +def _main(): + """Display all information sysconfig detains.""" + print('Platform: "%s"' % get_platform()) + print('Python version: "%s"' % get_python_version()) + print('Current installation scheme: "%s"' % _get_default_scheme()) + print() + _print_dict('Paths', get_paths()) + print() + _print_dict('Variables', get_config_vars()) + + +if __name__ == '__main__': + _main() diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/_backport/tarfile.py b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/tarfile.py new file mode 100644 index 0000000..d66d856 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/tarfile.py @@ -0,0 +1,2607 @@ +#------------------------------------------------------------------- +# tarfile.py +#------------------------------------------------------------------- +# Copyright (C) 2002 Lars Gustaebel <lars@gustaebel.de> +# All rights reserved. +# +# 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. +# +from __future__ import print_function + +"""Read from and write to tar format archives. +""" + +__version__ = "$Revision$" + +version = "0.9.0" +__author__ = "Lars Gust\u00e4bel (lars@gustaebel.de)" +__date__ = "$Date: 2011-02-25 17:42:01 +0200 (Fri, 25 Feb 2011) $" +__cvsid__ = "$Id: tarfile.py 88586 2011-02-25 15:42:01Z marc-andre.lemburg $" +__credits__ = "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend." + +#--------- +# Imports +#--------- +import sys +import os +import stat +import errno +import time +import struct +import copy +import re + +try: + import grp, pwd +except ImportError: + grp = pwd = None + +# os.symlink on Windows prior to 6.0 raises NotImplementedError +symlink_exception = (AttributeError, NotImplementedError) +try: + # WindowsError (1314) will be raised if the caller does not hold the + # SeCreateSymbolicLinkPrivilege privilege + symlink_exception += (WindowsError,) +except NameError: + pass + +# from tarfile import * +__all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError"] + +if sys.version_info[0] < 3: + import __builtin__ as builtins +else: + import builtins + +_open = builtins.open # Since 'open' is TarFile.open + +#--------------------------------------------------------- +# tar constants +#--------------------------------------------------------- +NUL = b"\0" # the null character +BLOCKSIZE = 512 # length of processing blocks +RECORDSIZE = BLOCKSIZE * 20 # length of records +GNU_MAGIC = b"ustar \0" # magic gnu tar string +POSIX_MAGIC = b"ustar\x0000" # magic posix tar string + +LENGTH_NAME = 100 # maximum length of a filename +LENGTH_LINK = 100 # maximum length of a linkname +LENGTH_PREFIX = 155 # maximum length of the prefix field + +REGTYPE = b"0" # regular file +AREGTYPE = b"\0" # regular file +LNKTYPE = b"1" # link (inside tarfile) +SYMTYPE = b"2" # symbolic link +CHRTYPE = b"3" # character special device +BLKTYPE = b"4" # block special device +DIRTYPE = b"5" # directory +FIFOTYPE = b"6" # fifo special device +CONTTYPE = b"7" # contiguous file + +GNUTYPE_LONGNAME = b"L" # GNU tar longname +GNUTYPE_LONGLINK = b"K" # GNU tar longlink +GNUTYPE_SPARSE = b"S" # GNU tar sparse file + +XHDTYPE = b"x" # POSIX.1-2001 extended header +XGLTYPE = b"g" # POSIX.1-2001 global header +SOLARIS_XHDTYPE = b"X" # Solaris extended header + +USTAR_FORMAT = 0 # POSIX.1-1988 (ustar) format +GNU_FORMAT = 1 # GNU tar format +PAX_FORMAT = 2 # POSIX.1-2001 (pax) format +DEFAULT_FORMAT = GNU_FORMAT + +#--------------------------------------------------------- +# tarfile constants +#--------------------------------------------------------- +# File types that tarfile supports: +SUPPORTED_TYPES = (REGTYPE, AREGTYPE, LNKTYPE, + SYMTYPE, DIRTYPE, FIFOTYPE, + CONTTYPE, CHRTYPE, BLKTYPE, + GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE) + +# File types that will be treated as a regular file. +REGULAR_TYPES = (REGTYPE, AREGTYPE, + CONTTYPE, GNUTYPE_SPARSE) + +# File types that are part of the GNU tar format. +GNU_TYPES = (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE) + +# Fields from a pax header that override a TarInfo attribute. +PAX_FIELDS = ("path", "linkpath", "size", "mtime", + "uid", "gid", "uname", "gname") + +# Fields from a pax header that are affected by hdrcharset. +PAX_NAME_FIELDS = set(("path", "linkpath", "uname", "gname")) + +# Fields in a pax header that are numbers, all other fields +# are treated as strings. +PAX_NUMBER_FIELDS = { + "atime": float, + "ctime": float, + "mtime": float, + "uid": int, + "gid": int, + "size": int +} + +#--------------------------------------------------------- +# Bits used in the mode field, values in octal. +#--------------------------------------------------------- +S_IFLNK = 0o120000 # symbolic link +S_IFREG = 0o100000 # regular file +S_IFBLK = 0o060000 # block device +S_IFDIR = 0o040000 # directory +S_IFCHR = 0o020000 # character device +S_IFIFO = 0o010000 # fifo + +TSUID = 0o4000 # set UID on execution +TSGID = 0o2000 # set GID on execution +TSVTX = 0o1000 # reserved + +TUREAD = 0o400 # read by owner +TUWRITE = 0o200 # write by owner +TUEXEC = 0o100 # execute/search by owner +TGREAD = 0o040 # read by group +TGWRITE = 0o020 # write by group +TGEXEC = 0o010 # execute/search by group +TOREAD = 0o004 # read by other +TOWRITE = 0o002 # write by other +TOEXEC = 0o001 # execute/search by other + +#--------------------------------------------------------- +# initialization +#--------------------------------------------------------- +if os.name in ("nt", "ce"): + ENCODING = "utf-8" +else: + ENCODING = sys.getfilesystemencoding() + +#--------------------------------------------------------- +# Some useful functions +#--------------------------------------------------------- + +def stn(s, length, encoding, errors): + """Convert a string to a null-terminated bytes object. + """ + s = s.encode(encoding, errors) + return s[:length] + (length - len(s)) * NUL + +def nts(s, encoding, errors): + """Convert a null-terminated bytes object to a string. + """ + p = s.find(b"\0") + if p != -1: + s = s[:p] + return s.decode(encoding, errors) + +def nti(s): + """Convert a number field to a python number. + """ + # There are two possible encodings for a number field, see + # itn() below. + if s[0] != chr(0o200): + try: + n = int(nts(s, "ascii", "strict") or "0", 8) + except ValueError: + raise InvalidHeaderError("invalid header") + else: + n = 0 + for i in range(len(s) - 1): + n <<= 8 + n += ord(s[i + 1]) + return n + +def itn(n, digits=8, format=DEFAULT_FORMAT): + """Convert a python number to a number field. + """ + # POSIX 1003.1-1988 requires numbers to be encoded as a string of + # octal digits followed by a null-byte, this allows values up to + # (8**(digits-1))-1. GNU tar allows storing numbers greater than + # that if necessary. A leading 0o200 byte indicates this particular + # encoding, the following digits-1 bytes are a big-endian + # representation. This allows values up to (256**(digits-1))-1. + if 0 <= n < 8 ** (digits - 1): + s = ("%0*o" % (digits - 1, n)).encode("ascii") + NUL + else: + if format != GNU_FORMAT or n >= 256 ** (digits - 1): + raise ValueError("overflow in number field") + + if n < 0: + # XXX We mimic GNU tar's behaviour with negative numbers, + # this could raise OverflowError. + n = struct.unpack("L", struct.pack("l", n))[0] + + s = bytearray() + for i in range(digits - 1): + s.insert(0, n & 0o377) + n >>= 8 + s.insert(0, 0o200) + return s + +def calc_chksums(buf): + """Calculate the checksum for a member's header by summing up all + characters except for the chksum field which is treated as if + it was filled with spaces. According to the GNU tar sources, + some tars (Sun and NeXT) calculate chksum with signed char, + which will be different if there are chars in the buffer with + the high bit set. So we calculate two checksums, unsigned and + signed. + """ + unsigned_chksum = 256 + sum(struct.unpack("148B", buf[:148]) + struct.unpack("356B", buf[156:512])) + signed_chksum = 256 + sum(struct.unpack("148b", buf[:148]) + struct.unpack("356b", buf[156:512])) + return unsigned_chksum, signed_chksum + +def copyfileobj(src, dst, length=None): + """Copy length bytes from fileobj src to fileobj dst. + If length is None, copy the entire content. + """ + if length == 0: + return + if length is None: + while True: + buf = src.read(16*1024) + if not buf: + break + dst.write(buf) + return + + BUFSIZE = 16 * 1024 + blocks, remainder = divmod(length, BUFSIZE) + for b in range(blocks): + buf = src.read(BUFSIZE) + if len(buf) < BUFSIZE: + raise IOError("end of file reached") + dst.write(buf) + + if remainder != 0: + buf = src.read(remainder) + if len(buf) < remainder: + raise IOError("end of file reached") + dst.write(buf) + return + +filemode_table = ( + ((S_IFLNK, "l"), + (S_IFREG, "-"), + (S_IFBLK, "b"), + (S_IFDIR, "d"), + (S_IFCHR, "c"), + (S_IFIFO, "p")), + + ((TUREAD, "r"),), + ((TUWRITE, "w"),), + ((TUEXEC|TSUID, "s"), + (TSUID, "S"), + (TUEXEC, "x")), + + ((TGREAD, "r"),), + ((TGWRITE, "w"),), + ((TGEXEC|TSGID, "s"), + (TSGID, "S"), + (TGEXEC, "x")), + + ((TOREAD, "r"),), + ((TOWRITE, "w"),), + ((TOEXEC|TSVTX, "t"), + (TSVTX, "T"), + (TOEXEC, "x")) +) + +def filemode(mode): + """Convert a file's mode to a string of the form + -rwxrwxrwx. + Used by TarFile.list() + """ + perm = [] + for table in filemode_table: + for bit, char in table: + if mode & bit == bit: + perm.append(char) + break + else: + perm.append("-") + return "".join(perm) + +class TarError(Exception): + """Base exception.""" + pass +class ExtractError(TarError): + """General exception for extract errors.""" + pass +class ReadError(TarError): + """Exception for unreadable tar archives.""" + pass +class CompressionError(TarError): + """Exception for unavailable compression methods.""" + pass +class StreamError(TarError): + """Exception for unsupported operations on stream-like TarFiles.""" + pass +class HeaderError(TarError): + """Base exception for header errors.""" + pass +class EmptyHeaderError(HeaderError): + """Exception for empty headers.""" + pass +class TruncatedHeaderError(HeaderError): + """Exception for truncated headers.""" + pass +class EOFHeaderError(HeaderError): + """Exception for end of file headers.""" + pass +class InvalidHeaderError(HeaderError): + """Exception for invalid headers.""" + pass +class SubsequentHeaderError(HeaderError): + """Exception for missing and invalid extended headers.""" + pass + +#--------------------------- +# internal stream interface +#--------------------------- +class _LowLevelFile(object): + """Low-level file object. Supports reading and writing. + It is used instead of a regular file object for streaming + access. + """ + + def __init__(self, name, mode): + mode = { + "r": os.O_RDONLY, + "w": os.O_WRONLY | os.O_CREAT | os.O_TRUNC, + }[mode] + if hasattr(os, "O_BINARY"): + mode |= os.O_BINARY + self.fd = os.open(name, mode, 0o666) + + def close(self): + os.close(self.fd) + + def read(self, size): + return os.read(self.fd, size) + + def write(self, s): + os.write(self.fd, s) + +class _Stream(object): + """Class that serves as an adapter between TarFile and + a stream-like object. The stream-like object only + needs to have a read() or write() method and is accessed + blockwise. Use of gzip or bzip2 compression is possible. + A stream-like object could be for example: sys.stdin, + sys.stdout, a socket, a tape device etc. + + _Stream is intended to be used only internally. + """ + + def __init__(self, name, mode, comptype, fileobj, bufsize): + """Construct a _Stream object. + """ + self._extfileobj = True + if fileobj is None: + fileobj = _LowLevelFile(name, mode) + self._extfileobj = False + + if comptype == '*': + # Enable transparent compression detection for the + # stream interface + fileobj = _StreamProxy(fileobj) + comptype = fileobj.getcomptype() + + self.name = name or "" + self.mode = mode + self.comptype = comptype + self.fileobj = fileobj + self.bufsize = bufsize + self.buf = b"" + self.pos = 0 + self.closed = False + + try: + if comptype == "gz": + try: + import zlib + except ImportError: + raise CompressionError("zlib module is not available") + self.zlib = zlib + self.crc = zlib.crc32(b"") + if mode == "r": + self._init_read_gz() + else: + self._init_write_gz() + + if comptype == "bz2": + try: + import bz2 + except ImportError: + raise CompressionError("bz2 module is not available") + if mode == "r": + self.dbuf = b"" + self.cmp = bz2.BZ2Decompressor() + else: + self.cmp = bz2.BZ2Compressor() + except: + if not self._extfileobj: + self.fileobj.close() + self.closed = True + raise + + def __del__(self): + if hasattr(self, "closed") and not self.closed: + self.close() + + def _init_write_gz(self): + """Initialize for writing with gzip compression. + """ + self.cmp = self.zlib.compressobj(9, self.zlib.DEFLATED, + -self.zlib.MAX_WBITS, + self.zlib.DEF_MEM_LEVEL, + 0) + timestamp = struct.pack("<L", int(time.time())) + self.__write(b"\037\213\010\010" + timestamp + b"\002\377") + if self.name.endswith(".gz"): + self.name = self.name[:-3] + # RFC1952 says we must use ISO-8859-1 for the FNAME field. + self.__write(self.name.encode("iso-8859-1", "replace") + NUL) + + def write(self, s): + """Write string s to the stream. + """ + if self.comptype == "gz": + self.crc = self.zlib.crc32(s, self.crc) + self.pos += len(s) + if self.comptype != "tar": + s = self.cmp.compress(s) + self.__write(s) + + def __write(self, s): + """Write string s to the stream if a whole new block + is ready to be written. + """ + self.buf += s + while len(self.buf) > self.bufsize: + self.fileobj.write(self.buf[:self.bufsize]) + self.buf = self.buf[self.bufsize:] + + def close(self): + """Close the _Stream object. No operation should be + done on it afterwards. + """ + if self.closed: + return + + if self.mode == "w" and self.comptype != "tar": + self.buf += self.cmp.flush() + + if self.mode == "w" and self.buf: + self.fileobj.write(self.buf) + self.buf = b"" + if self.comptype == "gz": + # The native zlib crc is an unsigned 32-bit integer, but + # the Python wrapper implicitly casts that to a signed C + # long. So, on a 32-bit box self.crc may "look negative", + # while the same crc on a 64-bit box may "look positive". + # To avoid irksome warnings from the `struct` module, force + # it to look positive on all boxes. + self.fileobj.write(struct.pack("<L", self.crc & 0xffffffff)) + self.fileobj.write(struct.pack("<L", self.pos & 0xffffFFFF)) + + if not self._extfileobj: + self.fileobj.close() + + self.closed = True + + def _init_read_gz(self): + """Initialize for reading a gzip compressed fileobj. + """ + self.cmp = self.zlib.decompressobj(-self.zlib.MAX_WBITS) + self.dbuf = b"" + + # taken from gzip.GzipFile with some alterations + if self.__read(2) != b"\037\213": + raise ReadError("not a gzip file") + if self.__read(1) != b"\010": + raise CompressionError("unsupported compression method") + + flag = ord(self.__read(1)) + self.__read(6) + + if flag & 4: + xlen = ord(self.__read(1)) + 256 * ord(self.__read(1)) + self.read(xlen) + if flag & 8: + while True: + s = self.__read(1) + if not s or s == NUL: + break + if flag & 16: + while True: + s = self.__read(1) + if not s or s == NUL: + break + if flag & 2: + self.__read(2) + + def tell(self): + """Return the stream's file pointer position. + """ + return self.pos + + def seek(self, pos=0): + """Set the stream's file pointer to pos. Negative seeking + is forbidden. + """ + if pos - self.pos >= 0: + blocks, remainder = divmod(pos - self.pos, self.bufsize) + for i in range(blocks): + self.read(self.bufsize) + self.read(remainder) + else: + raise StreamError("seeking backwards is not allowed") + return self.pos + + def read(self, size=None): + """Return the next size number of bytes from the stream. + If size is not defined, return all bytes of the stream + up to EOF. + """ + if size is None: + t = [] + while True: + buf = self._read(self.bufsize) + if not buf: + break + t.append(buf) + buf = "".join(t) + else: + buf = self._read(size) + self.pos += len(buf) + return buf + + def _read(self, size): + """Return size bytes from the stream. + """ + if self.comptype == "tar": + return self.__read(size) + + c = len(self.dbuf) + while c < size: + buf = self.__read(self.bufsize) + if not buf: + break + try: + buf = self.cmp.decompress(buf) + except IOError: + raise ReadError("invalid compressed data") + self.dbuf += buf + c += len(buf) + buf = self.dbuf[:size] + self.dbuf = self.dbuf[size:] + return buf + + def __read(self, size): + """Return size bytes from stream. If internal buffer is empty, + read another block from the stream. + """ + c = len(self.buf) + while c < size: + buf = self.fileobj.read(self.bufsize) + if not buf: + break + self.buf += buf + c += len(buf) + buf = self.buf[:size] + self.buf = self.buf[size:] + return buf +# class _Stream + +class _StreamProxy(object): + """Small proxy class that enables transparent compression + detection for the Stream interface (mode 'r|*'). + """ + + def __init__(self, fileobj): + self.fileobj = fileobj + self.buf = self.fileobj.read(BLOCKSIZE) + + def read(self, size): + self.read = self.fileobj.read + return self.buf + + def getcomptype(self): + if self.buf.startswith(b"\037\213\010"): + return "gz" + if self.buf.startswith(b"BZh91"): + return "bz2" + return "tar" + + def close(self): + self.fileobj.close() +# class StreamProxy + +class _BZ2Proxy(object): + """Small proxy class that enables external file object + support for "r:bz2" and "w:bz2" modes. This is actually + a workaround for a limitation in bz2 module's BZ2File + class which (unlike gzip.GzipFile) has no support for + a file object argument. + """ + + blocksize = 16 * 1024 + + def __init__(self, fileobj, mode): + self.fileobj = fileobj + self.mode = mode + self.name = getattr(self.fileobj, "name", None) + self.init() + + def init(self): + import bz2 + self.pos = 0 + if self.mode == "r": + self.bz2obj = bz2.BZ2Decompressor() + self.fileobj.seek(0) + self.buf = b"" + else: + self.bz2obj = bz2.BZ2Compressor() + + def read(self, size): + x = len(self.buf) + while x < size: + raw = self.fileobj.read(self.blocksize) + if not raw: + break + data = self.bz2obj.decompress(raw) + self.buf += data + x += len(data) + + buf = self.buf[:size] + self.buf = self.buf[size:] + self.pos += len(buf) + return buf + + def seek(self, pos): + if pos < self.pos: + self.init() + self.read(pos - self.pos) + + def tell(self): + return self.pos + + def write(self, data): + self.pos += len(data) + raw = self.bz2obj.compress(data) + self.fileobj.write(raw) + + def close(self): + if self.mode == "w": + raw = self.bz2obj.flush() + self.fileobj.write(raw) +# class _BZ2Proxy + +#------------------------ +# Extraction file object +#------------------------ +class _FileInFile(object): + """A thin wrapper around an existing file object that + provides a part of its data as an individual file + object. + """ + + def __init__(self, fileobj, offset, size, blockinfo=None): + self.fileobj = fileobj + self.offset = offset + self.size = size + self.position = 0 + + if blockinfo is None: + blockinfo = [(0, size)] + + # Construct a map with data and zero blocks. + self.map_index = 0 + self.map = [] + lastpos = 0 + realpos = self.offset + for offset, size in blockinfo: + if offset > lastpos: + self.map.append((False, lastpos, offset, None)) + self.map.append((True, offset, offset + size, realpos)) + realpos += size + lastpos = offset + size + if lastpos < self.size: + self.map.append((False, lastpos, self.size, None)) + + def seekable(self): + if not hasattr(self.fileobj, "seekable"): + # XXX gzip.GzipFile and bz2.BZ2File + return True + return self.fileobj.seekable() + + def tell(self): + """Return the current file position. + """ + return self.position + + def seek(self, position): + """Seek to a position in the file. + """ + self.position = position + + def read(self, size=None): + """Read data from the file. + """ + if size is None: + size = self.size - self.position + else: + size = min(size, self.size - self.position) + + buf = b"" + while size > 0: + while True: + data, start, stop, offset = self.map[self.map_index] + if start <= self.position < stop: + break + else: + self.map_index += 1 + if self.map_index == len(self.map): + self.map_index = 0 + length = min(size, stop - self.position) + if data: + self.fileobj.seek(offset + (self.position - start)) + buf += self.fileobj.read(length) + else: + buf += NUL * length + size -= length + self.position += length + return buf +#class _FileInFile + + +class ExFileObject(object): + """File-like object for reading an archive member. + Is returned by TarFile.extractfile(). + """ + blocksize = 1024 + + def __init__(self, tarfile, tarinfo): + self.fileobj = _FileInFile(tarfile.fileobj, + tarinfo.offset_data, + tarinfo.size, + tarinfo.sparse) + self.name = tarinfo.name + self.mode = "r" + self.closed = False + self.size = tarinfo.size + + self.position = 0 + self.buffer = b"" + + def readable(self): + return True + + def writable(self): + return False + + def seekable(self): + return self.fileobj.seekable() + + def read(self, size=None): + """Read at most size bytes from the file. If size is not + present or None, read all data until EOF is reached. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + buf = b"" + if self.buffer: + if size is None: + buf = self.buffer + self.buffer = b"" + else: + buf = self.buffer[:size] + self.buffer = self.buffer[size:] + + if size is None: + buf += self.fileobj.read() + else: + buf += self.fileobj.read(size - len(buf)) + + self.position += len(buf) + return buf + + # XXX TextIOWrapper uses the read1() method. + read1 = read + + def readline(self, size=-1): + """Read one entire line from the file. If size is present + and non-negative, return a string with at most that + size, which may be an incomplete line. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + pos = self.buffer.find(b"\n") + 1 + if pos == 0: + # no newline found. + while True: + buf = self.fileobj.read(self.blocksize) + self.buffer += buf + if not buf or b"\n" in buf: + pos = self.buffer.find(b"\n") + 1 + if pos == 0: + # no newline found. + pos = len(self.buffer) + break + + if size != -1: + pos = min(size, pos) + + buf = self.buffer[:pos] + self.buffer = self.buffer[pos:] + self.position += len(buf) + return buf + + def readlines(self): + """Return a list with all remaining lines. + """ + result = [] + while True: + line = self.readline() + if not line: break + result.append(line) + return result + + def tell(self): + """Return the current file position. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + return self.position + + def seek(self, pos, whence=os.SEEK_SET): + """Seek to a position in the file. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + if whence == os.SEEK_SET: + self.position = min(max(pos, 0), self.size) + elif whence == os.SEEK_CUR: + if pos < 0: + self.position = max(self.position + pos, 0) + else: + self.position = min(self.position + pos, self.size) + elif whence == os.SEEK_END: + self.position = max(min(self.size + pos, self.size), 0) + else: + raise ValueError("Invalid argument") + + self.buffer = b"" + self.fileobj.seek(self.position) + + def close(self): + """Close the file object. + """ + self.closed = True + + def __iter__(self): + """Get an iterator over the file's lines. + """ + while True: + line = self.readline() + if not line: + break + yield line +#class ExFileObject + +#------------------ +# Exported Classes +#------------------ +class TarInfo(object): + """Informational class which holds the details about an + archive member given by a tar header block. + TarInfo objects are returned by TarFile.getmember(), + TarFile.getmembers() and TarFile.gettarinfo() and are + usually created internally. + """ + + __slots__ = ("name", "mode", "uid", "gid", "size", "mtime", + "chksum", "type", "linkname", "uname", "gname", + "devmajor", "devminor", + "offset", "offset_data", "pax_headers", "sparse", + "tarfile", "_sparse_structs", "_link_target") + + def __init__(self, name=""): + """Construct a TarInfo object. name is the optional name + of the member. + """ + self.name = name # member name + self.mode = 0o644 # file permissions + self.uid = 0 # user id + self.gid = 0 # group id + self.size = 0 # file size + self.mtime = 0 # modification time + self.chksum = 0 # header checksum + self.type = REGTYPE # member type + self.linkname = "" # link name + self.uname = "" # user name + self.gname = "" # group name + self.devmajor = 0 # device major number + self.devminor = 0 # device minor number + + self.offset = 0 # the tar header starts here + self.offset_data = 0 # the file's data starts here + + self.sparse = None # sparse member information + self.pax_headers = {} # pax header information + + # In pax headers the "name" and "linkname" field are called + # "path" and "linkpath". + def _getpath(self): + return self.name + def _setpath(self, name): + self.name = name + path = property(_getpath, _setpath) + + def _getlinkpath(self): + return self.linkname + def _setlinkpath(self, linkname): + self.linkname = linkname + linkpath = property(_getlinkpath, _setlinkpath) + + def __repr__(self): + return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) + + def get_info(self): + """Return the TarInfo's attributes as a dictionary. + """ + info = { + "name": self.name, + "mode": self.mode & 0o7777, + "uid": self.uid, + "gid": self.gid, + "size": self.size, + "mtime": self.mtime, + "chksum": self.chksum, + "type": self.type, + "linkname": self.linkname, + "uname": self.uname, + "gname": self.gname, + "devmajor": self.devmajor, + "devminor": self.devminor + } + + if info["type"] == DIRTYPE and not info["name"].endswith("/"): + info["name"] += "/" + + return info + + def tobuf(self, format=DEFAULT_FORMAT, encoding=ENCODING, errors="surrogateescape"): + """Return a tar header as a string of 512 byte blocks. + """ + info = self.get_info() + + if format == USTAR_FORMAT: + return self.create_ustar_header(info, encoding, errors) + elif format == GNU_FORMAT: + return self.create_gnu_header(info, encoding, errors) + elif format == PAX_FORMAT: + return self.create_pax_header(info, encoding) + else: + raise ValueError("invalid format") + + def create_ustar_header(self, info, encoding, errors): + """Return the object as a ustar header block. + """ + info["magic"] = POSIX_MAGIC + + if len(info["linkname"]) > LENGTH_LINK: + raise ValueError("linkname is too long") + + if len(info["name"]) > LENGTH_NAME: + info["prefix"], info["name"] = self._posix_split_name(info["name"]) + + return self._create_header(info, USTAR_FORMAT, encoding, errors) + + def create_gnu_header(self, info, encoding, errors): + """Return the object as a GNU header block sequence. + """ + info["magic"] = GNU_MAGIC + + buf = b"" + if len(info["linkname"]) > LENGTH_LINK: + buf += self._create_gnu_long_header(info["linkname"], GNUTYPE_LONGLINK, encoding, errors) + + if len(info["name"]) > LENGTH_NAME: + buf += self._create_gnu_long_header(info["name"], GNUTYPE_LONGNAME, encoding, errors) + + return buf + self._create_header(info, GNU_FORMAT, encoding, errors) + + def create_pax_header(self, info, encoding): + """Return the object as a ustar header block. If it cannot be + represented this way, prepend a pax extended header sequence + with supplement information. + """ + info["magic"] = POSIX_MAGIC + pax_headers = self.pax_headers.copy() + + # Test string fields for values that exceed the field length or cannot + # be represented in ASCII encoding. + for name, hname, length in ( + ("name", "path", LENGTH_NAME), ("linkname", "linkpath", LENGTH_LINK), + ("uname", "uname", 32), ("gname", "gname", 32)): + + if hname in pax_headers: + # The pax header has priority. + continue + + # Try to encode the string as ASCII. + try: + info[name].encode("ascii", "strict") + except UnicodeEncodeError: + pax_headers[hname] = info[name] + continue + + if len(info[name]) > length: + pax_headers[hname] = info[name] + + # Test number fields for values that exceed the field limit or values + # that like to be stored as float. + for name, digits in (("uid", 8), ("gid", 8), ("size", 12), ("mtime", 12)): + if name in pax_headers: + # The pax header has priority. Avoid overflow. + info[name] = 0 + continue + + val = info[name] + if not 0 <= val < 8 ** (digits - 1) or isinstance(val, float): + pax_headers[name] = str(val) + info[name] = 0 + + # Create a pax extended header if necessary. + if pax_headers: + buf = self._create_pax_generic_header(pax_headers, XHDTYPE, encoding) + else: + buf = b"" + + return buf + self._create_header(info, USTAR_FORMAT, "ascii", "replace") + + @classmethod + def create_pax_global_header(cls, pax_headers): + """Return the object as a pax global header block sequence. + """ + return cls._create_pax_generic_header(pax_headers, XGLTYPE, "utf8") + + def _posix_split_name(self, name): + """Split a name longer than 100 chars into a prefix + and a name part. + """ + prefix = name[:LENGTH_PREFIX + 1] + while prefix and prefix[-1] != "/": + prefix = prefix[:-1] + + name = name[len(prefix):] + prefix = prefix[:-1] + + if not prefix or len(name) > LENGTH_NAME: + raise ValueError("name is too long") + return prefix, name + + @staticmethod + def _create_header(info, format, encoding, errors): + """Return a header block. info is a dictionary with file + information, format must be one of the *_FORMAT constants. + """ + parts = [ + stn(info.get("name", ""), 100, encoding, errors), + itn(info.get("mode", 0) & 0o7777, 8, format), + itn(info.get("uid", 0), 8, format), + itn(info.get("gid", 0), 8, format), + itn(info.get("size", 0), 12, format), + itn(info.get("mtime", 0), 12, format), + b" ", # checksum field + info.get("type", REGTYPE), + stn(info.get("linkname", ""), 100, encoding, errors), + info.get("magic", POSIX_MAGIC), + stn(info.get("uname", ""), 32, encoding, errors), + stn(info.get("gname", ""), 32, encoding, errors), + itn(info.get("devmajor", 0), 8, format), + itn(info.get("devminor", 0), 8, format), + stn(info.get("prefix", ""), 155, encoding, errors) + ] + + buf = struct.pack("%ds" % BLOCKSIZE, b"".join(parts)) + chksum = calc_chksums(buf[-BLOCKSIZE:])[0] + buf = buf[:-364] + ("%06o\0" % chksum).encode("ascii") + buf[-357:] + return buf + + @staticmethod + def _create_payload(payload): + """Return the string payload filled with zero bytes + up to the next 512 byte border. + """ + blocks, remainder = divmod(len(payload), BLOCKSIZE) + if remainder > 0: + payload += (BLOCKSIZE - remainder) * NUL + return payload + + @classmethod + def _create_gnu_long_header(cls, name, type, encoding, errors): + """Return a GNUTYPE_LONGNAME or GNUTYPE_LONGLINK sequence + for name. + """ + name = name.encode(encoding, errors) + NUL + + info = {} + info["name"] = "././@LongLink" + info["type"] = type + info["size"] = len(name) + info["magic"] = GNU_MAGIC + + # create extended header + name blocks. + return cls._create_header(info, USTAR_FORMAT, encoding, errors) + \ + cls._create_payload(name) + + @classmethod + def _create_pax_generic_header(cls, pax_headers, type, encoding): + """Return a POSIX.1-2008 extended or global header sequence + that contains a list of keyword, value pairs. The values + must be strings. + """ + # Check if one of the fields contains surrogate characters and thereby + # forces hdrcharset=BINARY, see _proc_pax() for more information. + binary = False + for keyword, value in pax_headers.items(): + try: + value.encode("utf8", "strict") + except UnicodeEncodeError: + binary = True + break + + records = b"" + if binary: + # Put the hdrcharset field at the beginning of the header. + records += b"21 hdrcharset=BINARY\n" + + for keyword, value in pax_headers.items(): + keyword = keyword.encode("utf8") + if binary: + # Try to restore the original byte representation of `value'. + # Needless to say, that the encoding must match the string. + value = value.encode(encoding, "surrogateescape") + else: + value = value.encode("utf8") + + l = len(keyword) + len(value) + 3 # ' ' + '=' + '\n' + n = p = 0 + while True: + n = l + len(str(p)) + if n == p: + break + p = n + records += bytes(str(p), "ascii") + b" " + keyword + b"=" + value + b"\n" + + # We use a hardcoded "././@PaxHeader" name like star does + # instead of the one that POSIX recommends. + info = {} + info["name"] = "././@PaxHeader" + info["type"] = type + info["size"] = len(records) + info["magic"] = POSIX_MAGIC + + # Create pax header + record blocks. + return cls._create_header(info, USTAR_FORMAT, "ascii", "replace") + \ + cls._create_payload(records) + + @classmethod + def frombuf(cls, buf, encoding, errors): + """Construct a TarInfo object from a 512 byte bytes object. + """ + if len(buf) == 0: + raise EmptyHeaderError("empty header") + if len(buf) != BLOCKSIZE: + raise TruncatedHeaderError("truncated header") + if buf.count(NUL) == BLOCKSIZE: + raise EOFHeaderError("end of file header") + + chksum = nti(buf[148:156]) + if chksum not in calc_chksums(buf): + raise InvalidHeaderError("bad checksum") + + obj = cls() + obj.name = nts(buf[0:100], encoding, errors) + obj.mode = nti(buf[100:108]) + obj.uid = nti(buf[108:116]) + obj.gid = nti(buf[116:124]) + obj.size = nti(buf[124:136]) + obj.mtime = nti(buf[136:148]) + obj.chksum = chksum + obj.type = buf[156:157] + obj.linkname = nts(buf[157:257], encoding, errors) + obj.uname = nts(buf[265:297], encoding, errors) + obj.gname = nts(buf[297:329], encoding, errors) + obj.devmajor = nti(buf[329:337]) + obj.devminor = nti(buf[337:345]) + prefix = nts(buf[345:500], encoding, errors) + + # Old V7 tar format represents a directory as a regular + # file with a trailing slash. + if obj.type == AREGTYPE and obj.name.endswith("/"): + obj.type = DIRTYPE + + # The old GNU sparse format occupies some of the unused + # space in the buffer for up to 4 sparse structures. + # Save the them for later processing in _proc_sparse(). + if obj.type == GNUTYPE_SPARSE: + pos = 386 + structs = [] + for i in range(4): + try: + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) + except ValueError: + break + structs.append((offset, numbytes)) + pos += 24 + isextended = bool(buf[482]) + origsize = nti(buf[483:495]) + obj._sparse_structs = (structs, isextended, origsize) + + # Remove redundant slashes from directories. + if obj.isdir(): + obj.name = obj.name.rstrip("/") + + # Reconstruct a ustar longname. + if prefix and obj.type not in GNU_TYPES: + obj.name = prefix + "/" + obj.name + return obj + + @classmethod + def fromtarfile(cls, tarfile): + """Return the next TarInfo object from TarFile object + tarfile. + """ + buf = tarfile.fileobj.read(BLOCKSIZE) + obj = cls.frombuf(buf, tarfile.encoding, tarfile.errors) + obj.offset = tarfile.fileobj.tell() - BLOCKSIZE + return obj._proc_member(tarfile) + + #-------------------------------------------------------------------------- + # The following are methods that are called depending on the type of a + # member. The entry point is _proc_member() which can be overridden in a + # subclass to add custom _proc_*() methods. A _proc_*() method MUST + # implement the following + # operations: + # 1. Set self.offset_data to the position where the data blocks begin, + # if there is data that follows. + # 2. Set tarfile.offset to the position where the next member's header will + # begin. + # 3. Return self or another valid TarInfo object. + def _proc_member(self, tarfile): + """Choose the right processing method depending on + the type and call it. + """ + if self.type in (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK): + return self._proc_gnulong(tarfile) + elif self.type == GNUTYPE_SPARSE: + return self._proc_sparse(tarfile) + elif self.type in (XHDTYPE, XGLTYPE, SOLARIS_XHDTYPE): + return self._proc_pax(tarfile) + else: + return self._proc_builtin(tarfile) + + def _proc_builtin(self, tarfile): + """Process a builtin type or an unknown type which + will be treated as a regular file. + """ + self.offset_data = tarfile.fileobj.tell() + offset = self.offset_data + if self.isreg() or self.type not in SUPPORTED_TYPES: + # Skip the following data blocks. + offset += self._block(self.size) + tarfile.offset = offset + + # Patch the TarInfo object with saved global + # header information. + self._apply_pax_info(tarfile.pax_headers, tarfile.encoding, tarfile.errors) + + return self + + def _proc_gnulong(self, tarfile): + """Process the blocks that hold a GNU longname + or longlink member. + """ + buf = tarfile.fileobj.read(self._block(self.size)) + + # Fetch the next header and process it. + try: + next = self.fromtarfile(tarfile) + except HeaderError: + raise SubsequentHeaderError("missing or bad subsequent header") + + # Patch the TarInfo object from the next header with + # the longname information. + next.offset = self.offset + if self.type == GNUTYPE_LONGNAME: + next.name = nts(buf, tarfile.encoding, tarfile.errors) + elif self.type == GNUTYPE_LONGLINK: + next.linkname = nts(buf, tarfile.encoding, tarfile.errors) + + return next + + def _proc_sparse(self, tarfile): + """Process a GNU sparse header plus extra headers. + """ + # We already collected some sparse structures in frombuf(). + structs, isextended, origsize = self._sparse_structs + del self._sparse_structs + + # Collect sparse structures from extended header blocks. + while isextended: + buf = tarfile.fileobj.read(BLOCKSIZE) + pos = 0 + for i in range(21): + try: + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) + except ValueError: + break + if offset and numbytes: + structs.append((offset, numbytes)) + pos += 24 + isextended = bool(buf[504]) + self.sparse = structs + + self.offset_data = tarfile.fileobj.tell() + tarfile.offset = self.offset_data + self._block(self.size) + self.size = origsize + return self + + def _proc_pax(self, tarfile): + """Process an extended or global header as described in + POSIX.1-2008. + """ + # Read the header information. + buf = tarfile.fileobj.read(self._block(self.size)) + + # A pax header stores supplemental information for either + # the following file (extended) or all following files + # (global). + if self.type == XGLTYPE: + pax_headers = tarfile.pax_headers + else: + pax_headers = tarfile.pax_headers.copy() + + # Check if the pax header contains a hdrcharset field. This tells us + # the encoding of the path, linkpath, uname and gname fields. Normally, + # these fields are UTF-8 encoded but since POSIX.1-2008 tar + # implementations are allowed to store them as raw binary strings if + # the translation to UTF-8 fails. + match = re.search(br"\d+ hdrcharset=([^\n]+)\n", buf) + if match is not None: + pax_headers["hdrcharset"] = match.group(1).decode("utf8") + + # For the time being, we don't care about anything other than "BINARY". + # The only other value that is currently allowed by the standard is + # "ISO-IR 10646 2000 UTF-8" in other words UTF-8. + hdrcharset = pax_headers.get("hdrcharset") + if hdrcharset == "BINARY": + encoding = tarfile.encoding + else: + encoding = "utf8" + + # Parse pax header information. A record looks like that: + # "%d %s=%s\n" % (length, keyword, value). length is the size + # of the complete record including the length field itself and + # the newline. keyword and value are both UTF-8 encoded strings. + regex = re.compile(br"(\d+) ([^=]+)=") + pos = 0 + while True: + match = regex.match(buf, pos) + if not match: + break + + length, keyword = match.groups() + length = int(length) + value = buf[match.end(2) + 1:match.start(1) + length - 1] + + # Normally, we could just use "utf8" as the encoding and "strict" + # as the error handler, but we better not take the risk. For + # example, GNU tar <= 1.23 is known to store filenames it cannot + # translate to UTF-8 as raw strings (unfortunately without a + # hdrcharset=BINARY header). + # We first try the strict standard encoding, and if that fails we + # fall back on the user's encoding and error handler. + keyword = self._decode_pax_field(keyword, "utf8", "utf8", + tarfile.errors) + if keyword in PAX_NAME_FIELDS: + value = self._decode_pax_field(value, encoding, tarfile.encoding, + tarfile.errors) + else: + value = self._decode_pax_field(value, "utf8", "utf8", + tarfile.errors) + + pax_headers[keyword] = value + pos += length + + # Fetch the next header. + try: + next = self.fromtarfile(tarfile) + except HeaderError: + raise SubsequentHeaderError("missing or bad subsequent header") + + # Process GNU sparse information. + if "GNU.sparse.map" in pax_headers: + # GNU extended sparse format version 0.1. + self._proc_gnusparse_01(next, pax_headers) + + elif "GNU.sparse.size" in pax_headers: + # GNU extended sparse format version 0.0. + self._proc_gnusparse_00(next, pax_headers, buf) + + elif pax_headers.get("GNU.sparse.major") == "1" and pax_headers.get("GNU.sparse.minor") == "0": + # GNU extended sparse format version 1.0. + self._proc_gnusparse_10(next, pax_headers, tarfile) + + if self.type in (XHDTYPE, SOLARIS_XHDTYPE): + # Patch the TarInfo object with the extended header info. + next._apply_pax_info(pax_headers, tarfile.encoding, tarfile.errors) + next.offset = self.offset + + if "size" in pax_headers: + # If the extended header replaces the size field, + # we need to recalculate the offset where the next + # header starts. + offset = next.offset_data + if next.isreg() or next.type not in SUPPORTED_TYPES: + offset += next._block(next.size) + tarfile.offset = offset + + return next + + def _proc_gnusparse_00(self, next, pax_headers, buf): + """Process a GNU tar extended sparse header, version 0.0. + """ + offsets = [] + for match in re.finditer(br"\d+ GNU.sparse.offset=(\d+)\n", buf): + offsets.append(int(match.group(1))) + numbytes = [] + for match in re.finditer(br"\d+ GNU.sparse.numbytes=(\d+)\n", buf): + numbytes.append(int(match.group(1))) + next.sparse = list(zip(offsets, numbytes)) + + def _proc_gnusparse_01(self, next, pax_headers): + """Process a GNU tar extended sparse header, version 0.1. + """ + sparse = [int(x) for x in pax_headers["GNU.sparse.map"].split(",")] + next.sparse = list(zip(sparse[::2], sparse[1::2])) + + def _proc_gnusparse_10(self, next, pax_headers, tarfile): + """Process a GNU tar extended sparse header, version 1.0. + """ + fields = None + sparse = [] + buf = tarfile.fileobj.read(BLOCKSIZE) + fields, buf = buf.split(b"\n", 1) + fields = int(fields) + while len(sparse) < fields * 2: + if b"\n" not in buf: + buf += tarfile.fileobj.read(BLOCKSIZE) + number, buf = buf.split(b"\n", 1) + sparse.append(int(number)) + next.offset_data = tarfile.fileobj.tell() + next.sparse = list(zip(sparse[::2], sparse[1::2])) + + def _apply_pax_info(self, pax_headers, encoding, errors): + """Replace fields with supplemental information from a previous + pax extended or global header. + """ + for keyword, value in pax_headers.items(): + if keyword == "GNU.sparse.name": + setattr(self, "path", value) + elif keyword == "GNU.sparse.size": + setattr(self, "size", int(value)) + elif keyword == "GNU.sparse.realsize": + setattr(self, "size", int(value)) + elif keyword in PAX_FIELDS: + if keyword in PAX_NUMBER_FIELDS: + try: + value = PAX_NUMBER_FIELDS[keyword](value) + except ValueError: + value = 0 + if keyword == "path": + value = value.rstrip("/") + setattr(self, keyword, value) + + self.pax_headers = pax_headers.copy() + + def _decode_pax_field(self, value, encoding, fallback_encoding, fallback_errors): + """Decode a single field from a pax record. + """ + try: + return value.decode(encoding, "strict") + except UnicodeDecodeError: + return value.decode(fallback_encoding, fallback_errors) + + def _block(self, count): + """Round up a byte count by BLOCKSIZE and return it, + e.g. _block(834) => 1024. + """ + blocks, remainder = divmod(count, BLOCKSIZE) + if remainder: + blocks += 1 + return blocks * BLOCKSIZE + + def isreg(self): + return self.type in REGULAR_TYPES + def isfile(self): + return self.isreg() + def isdir(self): + return self.type == DIRTYPE + def issym(self): + return self.type == SYMTYPE + def islnk(self): + return self.type == LNKTYPE + def ischr(self): + return self.type == CHRTYPE + def isblk(self): + return self.type == BLKTYPE + def isfifo(self): + return self.type == FIFOTYPE + def issparse(self): + return self.sparse is not None + def isdev(self): + return self.type in (CHRTYPE, BLKTYPE, FIFOTYPE) +# class TarInfo + +class TarFile(object): + """The TarFile Class provides an interface to tar archives. + """ + + debug = 0 # May be set from 0 (no msgs) to 3 (all msgs) + + dereference = False # If true, add content of linked file to the + # tar file, else the link. + + ignore_zeros = False # If true, skips empty or invalid blocks and + # continues processing. + + errorlevel = 1 # If 0, fatal errors only appear in debug + # messages (if debug >= 0). If > 0, errors + # are passed to the caller as exceptions. + + format = DEFAULT_FORMAT # The format to use when creating an archive. + + encoding = ENCODING # Encoding for 8-bit character strings. + + errors = None # Error handler for unicode conversion. + + tarinfo = TarInfo # The default TarInfo class to use. + + fileobject = ExFileObject # The default ExFileObject class to use. + + def __init__(self, name=None, mode="r", fileobj=None, format=None, + tarinfo=None, dereference=None, ignore_zeros=None, encoding=None, + errors="surrogateescape", pax_headers=None, debug=None, errorlevel=None): + """Open an (uncompressed) tar archive `name'. `mode' is either 'r' to + read from an existing archive, 'a' to append data to an existing + file or 'w' to create a new file overwriting an existing one. `mode' + defaults to 'r'. + If `fileobj' is given, it is used for reading or writing data. If it + can be determined, `mode' is overridden by `fileobj's mode. + `fileobj' is not closed, when TarFile is closed. + """ + if len(mode) > 1 or mode not in "raw": + raise ValueError("mode must be 'r', 'a' or 'w'") + self.mode = mode + self._mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode] + + if not fileobj: + if self.mode == "a" and not os.path.exists(name): + # Create nonexistent files in append mode. + self.mode = "w" + self._mode = "wb" + fileobj = bltn_open(name, self._mode) + self._extfileobj = False + else: + if name is None and hasattr(fileobj, "name"): + name = fileobj.name + if hasattr(fileobj, "mode"): + self._mode = fileobj.mode + self._extfileobj = True + self.name = os.path.abspath(name) if name else None + self.fileobj = fileobj + + # Init attributes. + if format is not None: + self.format = format + if tarinfo is not None: + self.tarinfo = tarinfo + if dereference is not None: + self.dereference = dereference + if ignore_zeros is not None: + self.ignore_zeros = ignore_zeros + if encoding is not None: + self.encoding = encoding + self.errors = errors + + if pax_headers is not None and self.format == PAX_FORMAT: + self.pax_headers = pax_headers + else: + self.pax_headers = {} + + if debug is not None: + self.debug = debug + if errorlevel is not None: + self.errorlevel = errorlevel + + # Init datastructures. + self.closed = False + self.members = [] # list of members as TarInfo objects + self._loaded = False # flag if all members have been read + self.offset = self.fileobj.tell() + # current position in the archive file + self.inodes = {} # dictionary caching the inodes of + # archive members already added + + try: + if self.mode == "r": + self.firstmember = None + self.firstmember = self.next() + + if self.mode == "a": + # Move to the end of the archive, + # before the first empty block. + while True: + self.fileobj.seek(self.offset) + try: + tarinfo = self.tarinfo.fromtarfile(self) + self.members.append(tarinfo) + except EOFHeaderError: + self.fileobj.seek(self.offset) + break + except HeaderError as e: + raise ReadError(str(e)) + + if self.mode in "aw": + self._loaded = True + + if self.pax_headers: + buf = self.tarinfo.create_pax_global_header(self.pax_headers.copy()) + self.fileobj.write(buf) + self.offset += len(buf) + except: + if not self._extfileobj: + self.fileobj.close() + self.closed = True + raise + + #-------------------------------------------------------------------------- + # Below are the classmethods which act as alternate constructors to the + # TarFile class. The open() method is the only one that is needed for + # public use; it is the "super"-constructor and is able to select an + # adequate "sub"-constructor for a particular compression using the mapping + # from OPEN_METH. + # + # This concept allows one to subclass TarFile without losing the comfort of + # the super-constructor. A sub-constructor is registered and made available + # by adding it to the mapping in OPEN_METH. + + @classmethod + def open(cls, name=None, mode="r", fileobj=None, bufsize=RECORDSIZE, **kwargs): + """Open a tar archive for reading, writing or appending. Return + an appropriate TarFile class. + + mode: + 'r' or 'r:*' open for reading with transparent compression + 'r:' open for reading exclusively uncompressed + 'r:gz' open for reading with gzip compression + 'r:bz2' open for reading with bzip2 compression + 'a' or 'a:' open for appending, creating the file if necessary + 'w' or 'w:' open for writing without compression + 'w:gz' open for writing with gzip compression + 'w:bz2' open for writing with bzip2 compression + + 'r|*' open a stream of tar blocks with transparent compression + 'r|' open an uncompressed stream of tar blocks for reading + 'r|gz' open a gzip compressed stream of tar blocks + 'r|bz2' open a bzip2 compressed stream of tar blocks + 'w|' open an uncompressed stream for writing + 'w|gz' open a gzip compressed stream for writing + 'w|bz2' open a bzip2 compressed stream for writing + """ + + if not name and not fileobj: + raise ValueError("nothing to open") + + if mode in ("r", "r:*"): + # Find out which *open() is appropriate for opening the file. + for comptype in cls.OPEN_METH: + func = getattr(cls, cls.OPEN_METH[comptype]) + if fileobj is not None: + saved_pos = fileobj.tell() + try: + return func(name, "r", fileobj, **kwargs) + except (ReadError, CompressionError) as e: + if fileobj is not None: + fileobj.seek(saved_pos) + continue + raise ReadError("file could not be opened successfully") + + elif ":" in mode: + filemode, comptype = mode.split(":", 1) + filemode = filemode or "r" + comptype = comptype or "tar" + + # Select the *open() function according to + # given compression. + if comptype in cls.OPEN_METH: + func = getattr(cls, cls.OPEN_METH[comptype]) + else: + raise CompressionError("unknown compression type %r" % comptype) + return func(name, filemode, fileobj, **kwargs) + + elif "|" in mode: + filemode, comptype = mode.split("|", 1) + filemode = filemode or "r" + comptype = comptype or "tar" + + if filemode not in "rw": + raise ValueError("mode must be 'r' or 'w'") + + stream = _Stream(name, filemode, comptype, fileobj, bufsize) + try: + t = cls(name, filemode, stream, **kwargs) + except: + stream.close() + raise + t._extfileobj = False + return t + + elif mode in "aw": + return cls.taropen(name, mode, fileobj, **kwargs) + + raise ValueError("undiscernible mode") + + @classmethod + def taropen(cls, name, mode="r", fileobj=None, **kwargs): + """Open uncompressed tar archive name for reading or writing. + """ + if len(mode) > 1 or mode not in "raw": + raise ValueError("mode must be 'r', 'a' or 'w'") + return cls(name, mode, fileobj, **kwargs) + + @classmethod + def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): + """Open gzip compressed tar archive name for reading or writing. + Appending is not allowed. + """ + if len(mode) > 1 or mode not in "rw": + raise ValueError("mode must be 'r' or 'w'") + + try: + import gzip + gzip.GzipFile + except (ImportError, AttributeError): + raise CompressionError("gzip module is not available") + + extfileobj = fileobj is not None + try: + fileobj = gzip.GzipFile(name, mode + "b", compresslevel, fileobj) + t = cls.taropen(name, mode, fileobj, **kwargs) + except IOError: + if not extfileobj and fileobj is not None: + fileobj.close() + if fileobj is None: + raise + raise ReadError("not a gzip file") + except: + if not extfileobj and fileobj is not None: + fileobj.close() + raise + t._extfileobj = extfileobj + return t + + @classmethod + def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): + """Open bzip2 compressed tar archive name for reading or writing. + Appending is not allowed. + """ + if len(mode) > 1 or mode not in "rw": + raise ValueError("mode must be 'r' or 'w'.") + + try: + import bz2 + except ImportError: + raise CompressionError("bz2 module is not available") + + if fileobj is not None: + fileobj = _BZ2Proxy(fileobj, mode) + else: + fileobj = bz2.BZ2File(name, mode, compresslevel=compresslevel) + + try: + t = cls.taropen(name, mode, fileobj, **kwargs) + except (IOError, EOFError): + fileobj.close() + raise ReadError("not a bzip2 file") + t._extfileobj = False + return t + + # All *open() methods are registered here. + OPEN_METH = { + "tar": "taropen", # uncompressed tar + "gz": "gzopen", # gzip compressed tar + "bz2": "bz2open" # bzip2 compressed tar + } + + #-------------------------------------------------------------------------- + # The public methods which TarFile provides: + + def close(self): + """Close the TarFile. In write-mode, two finishing zero blocks are + appended to the archive. + """ + if self.closed: + return + + if self.mode in "aw": + self.fileobj.write(NUL * (BLOCKSIZE * 2)) + self.offset += (BLOCKSIZE * 2) + # fill up the end with zero-blocks + # (like option -b20 for tar does) + blocks, remainder = divmod(self.offset, RECORDSIZE) + if remainder > 0: + self.fileobj.write(NUL * (RECORDSIZE - remainder)) + + if not self._extfileobj: + self.fileobj.close() + self.closed = True + + def getmember(self, name): + """Return a TarInfo object for member `name'. If `name' can not be + found in the archive, KeyError is raised. If a member occurs more + than once in the archive, its last occurrence is assumed to be the + most up-to-date version. + """ + tarinfo = self._getmember(name) + if tarinfo is None: + raise KeyError("filename %r not found" % name) + return tarinfo + + def getmembers(self): + """Return the members of the archive as a list of TarInfo objects. The + list has the same order as the members in the archive. + """ + self._check() + if not self._loaded: # if we want to obtain a list of + self._load() # all members, we first have to + # scan the whole archive. + return self.members + + def getnames(self): + """Return the members of the archive as a list of their names. It has + the same order as the list returned by getmembers(). + """ + return [tarinfo.name for tarinfo in self.getmembers()] + + def gettarinfo(self, name=None, arcname=None, fileobj=None): + """Create a TarInfo object for either the file `name' or the file + object `fileobj' (using os.fstat on its file descriptor). You can + modify some of the TarInfo's attributes before you add it using + addfile(). If given, `arcname' specifies an alternative name for the + file in the archive. + """ + self._check("aw") + + # When fileobj is given, replace name by + # fileobj's real name. + if fileobj is not None: + name = fileobj.name + + # Building the name of the member in the archive. + # Backward slashes are converted to forward slashes, + # Absolute paths are turned to relative paths. + if arcname is None: + arcname = name + drv, arcname = os.path.splitdrive(arcname) + arcname = arcname.replace(os.sep, "/") + arcname = arcname.lstrip("/") + + # Now, fill the TarInfo object with + # information specific for the file. + tarinfo = self.tarinfo() + tarinfo.tarfile = self + + # Use os.stat or os.lstat, depending on platform + # and if symlinks shall be resolved. + if fileobj is None: + if hasattr(os, "lstat") and not self.dereference: + statres = os.lstat(name) + else: + statres = os.stat(name) + else: + statres = os.fstat(fileobj.fileno()) + linkname = "" + + stmd = statres.st_mode + if stat.S_ISREG(stmd): + inode = (statres.st_ino, statres.st_dev) + if not self.dereference and statres.st_nlink > 1 and \ + inode in self.inodes and arcname != self.inodes[inode]: + # Is it a hardlink to an already + # archived file? + type = LNKTYPE + linkname = self.inodes[inode] + else: + # The inode is added only if its valid. + # For win32 it is always 0. + type = REGTYPE + if inode[0]: + self.inodes[inode] = arcname + elif stat.S_ISDIR(stmd): + type = DIRTYPE + elif stat.S_ISFIFO(stmd): + type = FIFOTYPE + elif stat.S_ISLNK(stmd): + type = SYMTYPE + linkname = os.readlink(name) + elif stat.S_ISCHR(stmd): + type = CHRTYPE + elif stat.S_ISBLK(stmd): + type = BLKTYPE + else: + return None + + # Fill the TarInfo object with all + # information we can get. + tarinfo.name = arcname + tarinfo.mode = stmd + tarinfo.uid = statres.st_uid + tarinfo.gid = statres.st_gid + if type == REGTYPE: + tarinfo.size = statres.st_size + else: + tarinfo.size = 0 + tarinfo.mtime = statres.st_mtime + tarinfo.type = type + tarinfo.linkname = linkname + if pwd: + try: + tarinfo.uname = pwd.getpwuid(tarinfo.uid)[0] + except KeyError: + pass + if grp: + try: + tarinfo.gname = grp.getgrgid(tarinfo.gid)[0] + except KeyError: + pass + + if type in (CHRTYPE, BLKTYPE): + if hasattr(os, "major") and hasattr(os, "minor"): + tarinfo.devmajor = os.major(statres.st_rdev) + tarinfo.devminor = os.minor(statres.st_rdev) + return tarinfo + + def list(self, verbose=True): + """Print a table of contents to sys.stdout. If `verbose' is False, only + the names of the members are printed. If it is True, an `ls -l'-like + output is produced. + """ + self._check() + + for tarinfo in self: + if verbose: + print(filemode(tarinfo.mode), end=' ') + print("%s/%s" % (tarinfo.uname or tarinfo.uid, + tarinfo.gname or tarinfo.gid), end=' ') + if tarinfo.ischr() or tarinfo.isblk(): + print("%10s" % ("%d,%d" \ + % (tarinfo.devmajor, tarinfo.devminor)), end=' ') + else: + print("%10d" % tarinfo.size, end=' ') + print("%d-%02d-%02d %02d:%02d:%02d" \ + % time.localtime(tarinfo.mtime)[:6], end=' ') + + print(tarinfo.name + ("/" if tarinfo.isdir() else ""), end=' ') + + if verbose: + if tarinfo.issym(): + print("->", tarinfo.linkname, end=' ') + if tarinfo.islnk(): + print("link to", tarinfo.linkname, end=' ') + print() + + def add(self, name, arcname=None, recursive=True, exclude=None, filter=None): + """Add the file `name' to the archive. `name' may be any type of file + (directory, fifo, symbolic link, etc.). If given, `arcname' + specifies an alternative name for the file in the archive. + Directories are added recursively by default. This can be avoided by + setting `recursive' to False. `exclude' is a function that should + return True for each filename to be excluded. `filter' is a function + that expects a TarInfo object argument and returns the changed + TarInfo object, if it returns None the TarInfo object will be + excluded from the archive. + """ + self._check("aw") + + if arcname is None: + arcname = name + + # Exclude pathnames. + if exclude is not None: + import warnings + warnings.warn("use the filter argument instead", + DeprecationWarning, 2) + if exclude(name): + self._dbg(2, "tarfile: Excluded %r" % name) + return + + # Skip if somebody tries to archive the archive... + if self.name is not None and os.path.abspath(name) == self.name: + self._dbg(2, "tarfile: Skipped %r" % name) + return + + self._dbg(1, name) + + # Create a TarInfo object from the file. + tarinfo = self.gettarinfo(name, arcname) + + if tarinfo is None: + self._dbg(1, "tarfile: Unsupported type %r" % name) + return + + # Change or exclude the TarInfo object. + if filter is not None: + tarinfo = filter(tarinfo) + if tarinfo is None: + self._dbg(2, "tarfile: Excluded %r" % name) + return + + # Append the tar header and data to the archive. + if tarinfo.isreg(): + f = bltn_open(name, "rb") + self.addfile(tarinfo, f) + f.close() + + elif tarinfo.isdir(): + self.addfile(tarinfo) + if recursive: + for f in os.listdir(name): + self.add(os.path.join(name, f), os.path.join(arcname, f), + recursive, exclude, filter=filter) + + else: + self.addfile(tarinfo) + + def addfile(self, tarinfo, fileobj=None): + """Add the TarInfo object `tarinfo' to the archive. If `fileobj' is + given, tarinfo.size bytes are read from it and added to the archive. + You can create TarInfo objects using gettarinfo(). + On Windows platforms, `fileobj' should always be opened with mode + 'rb' to avoid irritation about the file size. + """ + self._check("aw") + + tarinfo = copy.copy(tarinfo) + + buf = tarinfo.tobuf(self.format, self.encoding, self.errors) + self.fileobj.write(buf) + self.offset += len(buf) + + # If there's data to follow, append it. + if fileobj is not None: + copyfileobj(fileobj, self.fileobj, tarinfo.size) + blocks, remainder = divmod(tarinfo.size, BLOCKSIZE) + if remainder > 0: + self.fileobj.write(NUL * (BLOCKSIZE - remainder)) + blocks += 1 + self.offset += blocks * BLOCKSIZE + + self.members.append(tarinfo) + + def extractall(self, path=".", members=None): + """Extract all members from the archive to the current working + directory and set owner, modification time and permissions on + directories afterwards. `path' specifies a different directory + to extract to. `members' is optional and must be a subset of the + list returned by getmembers(). + """ + directories = [] + + if members is None: + members = self + + for tarinfo in members: + if tarinfo.isdir(): + # Extract directories with a safe mode. + directories.append(tarinfo) + tarinfo = copy.copy(tarinfo) + tarinfo.mode = 0o700 + # Do not set_attrs directories, as we will do that further down + self.extract(tarinfo, path, set_attrs=not tarinfo.isdir()) + + # Reverse sort directories. + directories.sort(key=lambda a: a.name) + directories.reverse() + + # Set correct owner, mtime and filemode on directories. + for tarinfo in directories: + dirpath = os.path.join(path, tarinfo.name) + try: + self.chown(tarinfo, dirpath) + self.utime(tarinfo, dirpath) + self.chmod(tarinfo, dirpath) + except ExtractError as e: + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + def extract(self, member, path="", set_attrs=True): + """Extract a member from the archive to the current working directory, + using its full name. Its file information is extracted as accurately + as possible. `member' may be a filename or a TarInfo object. You can + specify a different directory using `path'. File attributes (owner, + mtime, mode) are set unless `set_attrs' is False. + """ + self._check("r") + + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + # Prepare the link target for makelink(). + if tarinfo.islnk(): + tarinfo._link_target = os.path.join(path, tarinfo.linkname) + + try: + self._extract_member(tarinfo, os.path.join(path, tarinfo.name), + set_attrs=set_attrs) + except EnvironmentError as e: + if self.errorlevel > 0: + raise + else: + if e.filename is None: + self._dbg(1, "tarfile: %s" % e.strerror) + else: + self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename)) + except ExtractError as e: + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + def extractfile(self, member): + """Extract a member from the archive as a file object. `member' may be + a filename or a TarInfo object. If `member' is a regular file, a + file-like object is returned. If `member' is a link, a file-like + object is constructed from the link's target. If `member' is none of + the above, None is returned. + The file-like object is read-only and provides the following + methods: read(), readline(), readlines(), seek() and tell() + """ + self._check("r") + + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + if tarinfo.isreg(): + return self.fileobject(self, tarinfo) + + elif tarinfo.type not in SUPPORTED_TYPES: + # If a member's type is unknown, it is treated as a + # regular file. + return self.fileobject(self, tarinfo) + + elif tarinfo.islnk() or tarinfo.issym(): + if isinstance(self.fileobj, _Stream): + # A small but ugly workaround for the case that someone tries + # to extract a (sym)link as a file-object from a non-seekable + # stream of tar blocks. + raise StreamError("cannot extract (sym)link as file object") + else: + # A (sym)link's file object is its target's file object. + return self.extractfile(self._find_link_target(tarinfo)) + else: + # If there's no data associated with the member (directory, chrdev, + # blkdev, etc.), return None instead of a file object. + return None + + def _extract_member(self, tarinfo, targetpath, set_attrs=True): + """Extract the TarInfo object tarinfo to a physical + file called targetpath. + """ + # Fetch the TarInfo object for the given name + # and build the destination pathname, replacing + # forward slashes to platform specific separators. + targetpath = targetpath.rstrip("/") + targetpath = targetpath.replace("/", os.sep) + + # Create all upper directories. + upperdirs = os.path.dirname(targetpath) + if upperdirs and not os.path.exists(upperdirs): + # Create directories that are not part of the archive with + # default permissions. + os.makedirs(upperdirs) + + if tarinfo.islnk() or tarinfo.issym(): + self._dbg(1, "%s -> %s" % (tarinfo.name, tarinfo.linkname)) + else: + self._dbg(1, tarinfo.name) + + if tarinfo.isreg(): + self.makefile(tarinfo, targetpath) + elif tarinfo.isdir(): + self.makedir(tarinfo, targetpath) + elif tarinfo.isfifo(): + self.makefifo(tarinfo, targetpath) + elif tarinfo.ischr() or tarinfo.isblk(): + self.makedev(tarinfo, targetpath) + elif tarinfo.islnk() or tarinfo.issym(): + self.makelink(tarinfo, targetpath) + elif tarinfo.type not in SUPPORTED_TYPES: + self.makeunknown(tarinfo, targetpath) + else: + self.makefile(tarinfo, targetpath) + + if set_attrs: + self.chown(tarinfo, targetpath) + if not tarinfo.issym(): + self.chmod(tarinfo, targetpath) + self.utime(tarinfo, targetpath) + + #-------------------------------------------------------------------------- + # Below are the different file methods. They are called via + # _extract_member() when extract() is called. They can be replaced in a + # subclass to implement other functionality. + + def makedir(self, tarinfo, targetpath): + """Make a directory called targetpath. + """ + try: + # Use a safe mode for the directory, the real mode is set + # later in _extract_member(). + os.mkdir(targetpath, 0o700) + except EnvironmentError as e: + if e.errno != errno.EEXIST: + raise + + def makefile(self, tarinfo, targetpath): + """Make a file called targetpath. + """ + source = self.fileobj + source.seek(tarinfo.offset_data) + target = bltn_open(targetpath, "wb") + if tarinfo.sparse is not None: + for offset, size in tarinfo.sparse: + target.seek(offset) + copyfileobj(source, target, size) + else: + copyfileobj(source, target, tarinfo.size) + target.seek(tarinfo.size) + target.truncate() + target.close() + + def makeunknown(self, tarinfo, targetpath): + """Make a file from a TarInfo object with an unknown type + at targetpath. + """ + self.makefile(tarinfo, targetpath) + self._dbg(1, "tarfile: Unknown file type %r, " \ + "extracted as regular file." % tarinfo.type) + + def makefifo(self, tarinfo, targetpath): + """Make a fifo called targetpath. + """ + if hasattr(os, "mkfifo"): + os.mkfifo(targetpath) + else: + raise ExtractError("fifo not supported by system") + + def makedev(self, tarinfo, targetpath): + """Make a character or block device called targetpath. + """ + if not hasattr(os, "mknod") or not hasattr(os, "makedev"): + raise ExtractError("special devices not supported by system") + + mode = tarinfo.mode + if tarinfo.isblk(): + mode |= stat.S_IFBLK + else: + mode |= stat.S_IFCHR + + os.mknod(targetpath, mode, + os.makedev(tarinfo.devmajor, tarinfo.devminor)) + + def makelink(self, tarinfo, targetpath): + """Make a (symbolic) link called targetpath. If it cannot be created + (platform limitation), we try to make a copy of the referenced file + instead of a link. + """ + try: + # For systems that support symbolic and hard links. + if tarinfo.issym(): + os.symlink(tarinfo.linkname, targetpath) + else: + # See extract(). + if os.path.exists(tarinfo._link_target): + os.link(tarinfo._link_target, targetpath) + else: + self._extract_member(self._find_link_target(tarinfo), + targetpath) + except symlink_exception: + if tarinfo.issym(): + linkpath = os.path.join(os.path.dirname(tarinfo.name), + tarinfo.linkname) + else: + linkpath = tarinfo.linkname + else: + try: + self._extract_member(self._find_link_target(tarinfo), + targetpath) + except KeyError: + raise ExtractError("unable to resolve link inside archive") + + def chown(self, tarinfo, targetpath): + """Set owner of targetpath according to tarinfo. + """ + if pwd and hasattr(os, "geteuid") and os.geteuid() == 0: + # We have to be root to do so. + try: + g = grp.getgrnam(tarinfo.gname)[2] + except KeyError: + g = tarinfo.gid + try: + u = pwd.getpwnam(tarinfo.uname)[2] + except KeyError: + u = tarinfo.uid + try: + if tarinfo.issym() and hasattr(os, "lchown"): + os.lchown(targetpath, u, g) + else: + if sys.platform != "os2emx": + os.chown(targetpath, u, g) + except EnvironmentError as e: + raise ExtractError("could not change owner") + + def chmod(self, tarinfo, targetpath): + """Set file permissions of targetpath according to tarinfo. + """ + if hasattr(os, 'chmod'): + try: + os.chmod(targetpath, tarinfo.mode) + except EnvironmentError as e: + raise ExtractError("could not change mode") + + def utime(self, tarinfo, targetpath): + """Set modification time of targetpath according to tarinfo. + """ + if not hasattr(os, 'utime'): + return + try: + os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime)) + except EnvironmentError as e: + raise ExtractError("could not change modification time") + + #-------------------------------------------------------------------------- + def next(self): + """Return the next member of the archive as a TarInfo object, when + TarFile is opened for reading. Return None if there is no more + available. + """ + self._check("ra") + if self.firstmember is not None: + m = self.firstmember + self.firstmember = None + return m + + # Read the next block. + self.fileobj.seek(self.offset) + tarinfo = None + while True: + try: + tarinfo = self.tarinfo.fromtarfile(self) + except EOFHeaderError as e: + if self.ignore_zeros: + self._dbg(2, "0x%X: %s" % (self.offset, e)) + self.offset += BLOCKSIZE + continue + except InvalidHeaderError as e: + if self.ignore_zeros: + self._dbg(2, "0x%X: %s" % (self.offset, e)) + self.offset += BLOCKSIZE + continue + elif self.offset == 0: + raise ReadError(str(e)) + except EmptyHeaderError: + if self.offset == 0: + raise ReadError("empty file") + except TruncatedHeaderError as e: + if self.offset == 0: + raise ReadError(str(e)) + except SubsequentHeaderError as e: + raise ReadError(str(e)) + break + + if tarinfo is not None: + self.members.append(tarinfo) + else: + self._loaded = True + + return tarinfo + + #-------------------------------------------------------------------------- + # Little helper methods: + + def _getmember(self, name, tarinfo=None, normalize=False): + """Find an archive member by name from bottom to top. + If tarinfo is given, it is used as the starting point. + """ + # Ensure that all members have been loaded. + members = self.getmembers() + + # Limit the member search list up to tarinfo. + if tarinfo is not None: + members = members[:members.index(tarinfo)] + + if normalize: + name = os.path.normpath(name) + + for member in reversed(members): + if normalize: + member_name = os.path.normpath(member.name) + else: + member_name = member.name + + if name == member_name: + return member + + def _load(self): + """Read through the entire archive file and look for readable + members. + """ + while True: + tarinfo = self.next() + if tarinfo is None: + break + self._loaded = True + + def _check(self, mode=None): + """Check if TarFile is still open, and if the operation's mode + corresponds to TarFile's mode. + """ + if self.closed: + raise IOError("%s is closed" % self.__class__.__name__) + if mode is not None and self.mode not in mode: + raise IOError("bad operation for mode %r" % self.mode) + + def _find_link_target(self, tarinfo): + """Find the target member of a symlink or hardlink member in the + archive. + """ + if tarinfo.issym(): + # Always search the entire archive. + linkname = os.path.dirname(tarinfo.name) + "/" + tarinfo.linkname + limit = None + else: + # Search the archive before the link, because a hard link is + # just a reference to an already archived file. + linkname = tarinfo.linkname + limit = tarinfo + + member = self._getmember(linkname, tarinfo=limit, normalize=True) + if member is None: + raise KeyError("linkname %r not found" % linkname) + return member + + def __iter__(self): + """Provide an iterator object. + """ + if self._loaded: + return iter(self.members) + else: + return TarIter(self) + + def _dbg(self, level, msg): + """Write debugging output to sys.stderr. + """ + if level <= self.debug: + print(msg, file=sys.stderr) + + def __enter__(self): + self._check() + return self + + def __exit__(self, type, value, traceback): + if type is None: + self.close() + else: + # An exception occurred. We must not call close() because + # it would try to write end-of-archive blocks and padding. + if not self._extfileobj: + self.fileobj.close() + self.closed = True +# class TarFile + +class TarIter(object): + """Iterator Class. + + for tarinfo in TarFile(...): + suite... + """ + + def __init__(self, tarfile): + """Construct a TarIter object. + """ + self.tarfile = tarfile + self.index = 0 + def __iter__(self): + """Return iterator object. + """ + return self + + def __next__(self): + """Return the next item using TarFile's next() method. + When all members have been read, set TarFile as _loaded. + """ + # Fix for SF #1100429: Under rare circumstances it can + # happen that getmembers() is called during iteration, + # which will cause TarIter to stop prematurely. + if not self.tarfile._loaded: + tarinfo = self.tarfile.next() + if not tarinfo: + self.tarfile._loaded = True + raise StopIteration + else: + try: + tarinfo = self.tarfile.members[self.index] + except IndexError: + raise StopIteration + self.index += 1 + return tarinfo + + next = __next__ # for Python 2.x + +#-------------------- +# exported functions +#-------------------- +def is_tarfile(name): + """Return True if name points to a tar archive that we + are able to handle, else return False. + """ + try: + t = open(name) + t.close() + return True + except TarError: + return False + +bltn_open = open +open = TarFile.open diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/compat.py b/venv/Lib/site-packages/pip/_vendor/distlib/compat.py new file mode 100644 index 0000000..ff328c8 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/compat.py @@ -0,0 +1,1120 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2017 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import absolute_import + +import os +import re +import sys + +try: + import ssl +except ImportError: # pragma: no cover + ssl = None + +if sys.version_info[0] < 3: # pragma: no cover + from StringIO import StringIO + string_types = basestring, + text_type = unicode + from types import FileType as file_type + import __builtin__ as builtins + import ConfigParser as configparser + from ._backport import shutil + from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit + from urllib import (urlretrieve, quote as _quote, unquote, url2pathname, + pathname2url, ContentTooShortError, splittype) + + def quote(s): + if isinstance(s, unicode): + s = s.encode('utf-8') + return _quote(s) + + import urllib2 + from urllib2 import (Request, urlopen, URLError, HTTPError, + HTTPBasicAuthHandler, HTTPPasswordMgr, + HTTPHandler, HTTPRedirectHandler, + build_opener) + if ssl: + from urllib2 import HTTPSHandler + import httplib + import xmlrpclib + import Queue as queue + from HTMLParser import HTMLParser + import htmlentitydefs + raw_input = raw_input + from itertools import ifilter as filter + from itertools import ifilterfalse as filterfalse + + _userprog = None + def splituser(host): + """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" + global _userprog + if _userprog is None: + import re + _userprog = re.compile('^(.*)@(.*)$') + + match = _userprog.match(host) + if match: return match.group(1, 2) + return None, host + +else: # pragma: no cover + from io import StringIO + string_types = str, + text_type = str + from io import TextIOWrapper as file_type + import builtins + import configparser + import shutil + from urllib.parse import (urlparse, urlunparse, urljoin, splituser, quote, + unquote, urlsplit, urlunsplit, splittype) + from urllib.request import (urlopen, urlretrieve, Request, url2pathname, + pathname2url, + HTTPBasicAuthHandler, HTTPPasswordMgr, + HTTPHandler, HTTPRedirectHandler, + build_opener) + if ssl: + from urllib.request import HTTPSHandler + from urllib.error import HTTPError, URLError, ContentTooShortError + import http.client as httplib + import urllib.request as urllib2 + import xmlrpc.client as xmlrpclib + import queue + from html.parser import HTMLParser + import html.entities as htmlentitydefs + raw_input = input + from itertools import filterfalse + filter = filter + +try: + from ssl import match_hostname, CertificateError +except ImportError: # pragma: no cover + class CertificateError(ValueError): + pass + + + def _dnsname_match(dn, hostname, max_wildcards=1): + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + parts = dn.split('.') + leftmost, remainder = parts[0], parts[1:] + + wildcards = leftmost.count('*') + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn)) + + # speed up common case w/o wildcards + if not wildcards: + return dn.lower() == hostname.lower() + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == '*': + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append('[^.]+') + elif leftmost.startswith('xn--') or hostname.startswith('xn--'): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + return pat.match(hostname) + + + def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError("empty or no certificate, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED") + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get('subject', ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == 'commonName': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError("hostname %r " + "doesn't match either of %s" + % (hostname, ', '.join(map(repr, dnsnames)))) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r " + "doesn't match %r" + % (hostname, dnsnames[0])) + else: + raise CertificateError("no appropriate commonName or " + "subjectAltName fields were found") + + +try: + from types import SimpleNamespace as Container +except ImportError: # pragma: no cover + class Container(object): + """ + A generic container for when multiple values need to be returned + """ + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + +try: + from shutil import which +except ImportError: # pragma: no cover + # Implementation from Python 3.3 + def which(cmd, mode=os.F_OK | os.X_OK, path=None): + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + + """ + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) + and not os.path.isdir(fn)) + + # If we're given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get("PATH", os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if not os.curdir in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if not normdir in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None + + +# ZipFile is a context manager in 2.7, but not in 2.6 + +from zipfile import ZipFile as BaseZipFile + +if hasattr(BaseZipFile, '__enter__'): # pragma: no cover + ZipFile = BaseZipFile +else: # pragma: no cover + from zipfile import ZipExtFile as BaseZipExtFile + + class ZipExtFile(BaseZipExtFile): + def __init__(self, base): + self.__dict__.update(base.__dict__) + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.close() + # return None, so if an exception occurred, it will propagate + + class ZipFile(BaseZipFile): + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.close() + # return None, so if an exception occurred, it will propagate + + def open(self, *args, **kwargs): + base = BaseZipFile.open(self, *args, **kwargs) + return ZipExtFile(base) + +try: + from platform import python_implementation +except ImportError: # pragma: no cover + def python_implementation(): + """Return a string identifying the Python implementation.""" + if 'PyPy' in sys.version: + return 'PyPy' + if os.name == 'java': + return 'Jython' + if sys.version.startswith('IronPython'): + return 'IronPython' + return 'CPython' + +try: + import sysconfig +except ImportError: # pragma: no cover + from ._backport import sysconfig + +try: + callable = callable +except NameError: # pragma: no cover + from collections import Callable + + def callable(obj): + return isinstance(obj, Callable) + + +try: + fsencode = os.fsencode + fsdecode = os.fsdecode +except AttributeError: # pragma: no cover + # Issue #99: on some systems (e.g. containerised), + # sys.getfilesystemencoding() returns None, and we need a real value, + # so fall back to utf-8. From the CPython 2.7 docs relating to Unix and + # sys.getfilesystemencoding(): the return value is "the user’s preference + # according to the result of nl_langinfo(CODESET), or None if the + # nl_langinfo(CODESET) failed." + _fsencoding = sys.getfilesystemencoding() or 'utf-8' + if _fsencoding == 'mbcs': + _fserrors = 'strict' + else: + _fserrors = 'surrogateescape' + + def fsencode(filename): + if isinstance(filename, bytes): + return filename + elif isinstance(filename, text_type): + return filename.encode(_fsencoding, _fserrors) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) + + def fsdecode(filename): + if isinstance(filename, text_type): + return filename + elif isinstance(filename, bytes): + return filename.decode(_fsencoding, _fserrors) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) + +try: + from tokenize import detect_encoding +except ImportError: # pragma: no cover + from codecs import BOM_UTF8, lookup + import re + + cookie_re = re.compile(r"coding[:=]\s*([-\w.]+)") + + def _get_normal_name(orig_enc): + """Imitates get_normal_name in tokenizer.c.""" + # Only care about the first 12 characters. + enc = orig_enc[:12].lower().replace("_", "-") + if enc == "utf-8" or enc.startswith("utf-8-"): + return "utf-8" + if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ + enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): + return "iso-8859-1" + return orig_enc + + def detect_encoding(readline): + """ + The detect_encoding() function is used to detect the encoding that should + be used to decode a Python source file. It requires one argument, readline, + in the same way as the tokenize() generator. + + It will call readline a maximum of twice, and return the encoding used + (as a string) and a list of any lines (left as bytes) it has read in. + + It detects the encoding from the presence of a utf-8 bom or an encoding + cookie as specified in pep-0263. If both a bom and a cookie are present, + but disagree, a SyntaxError will be raised. If the encoding cookie is an + invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, + 'utf-8-sig' is returned. + + If no encoding is specified, then the default of 'utf-8' will be returned. + """ + try: + filename = readline.__self__.name + except AttributeError: + filename = None + bom_found = False + encoding = None + default = 'utf-8' + def read_or_stop(): + try: + return readline() + except StopIteration: + return b'' + + def find_cookie(line): + try: + # Decode as UTF-8. Either the line is an encoding declaration, + # in which case it should be pure ASCII, or it must be UTF-8 + # per default encoding. + line_string = line.decode('utf-8') + except UnicodeDecodeError: + msg = "invalid or missing encoding declaration" + if filename is not None: + msg = '{} for {!r}'.format(msg, filename) + raise SyntaxError(msg) + + matches = cookie_re.findall(line_string) + if not matches: + return None + encoding = _get_normal_name(matches[0]) + try: + codec = lookup(encoding) + except LookupError: + # This behaviour mimics the Python interpreter + if filename is None: + msg = "unknown encoding: " + encoding + else: + msg = "unknown encoding for {!r}: {}".format(filename, + encoding) + raise SyntaxError(msg) + + if bom_found: + if codec.name != 'utf-8': + # This behaviour mimics the Python interpreter + if filename is None: + msg = 'encoding problem: utf-8' + else: + msg = 'encoding problem for {!r}: utf-8'.format(filename) + raise SyntaxError(msg) + encoding += '-sig' + return encoding + + first = read_or_stop() + if first.startswith(BOM_UTF8): + bom_found = True + first = first[3:] + default = 'utf-8-sig' + if not first: + return default, [] + + encoding = find_cookie(first) + if encoding: + return encoding, [first] + + second = read_or_stop() + if not second: + return default, [first] + + encoding = find_cookie(second) + if encoding: + return encoding, [first, second] + + return default, [first, second] + +# For converting & <-> & etc. +try: + from html import escape +except ImportError: + from cgi import escape +if sys.version_info[:2] < (3, 4): + unescape = HTMLParser().unescape +else: + from html import unescape + +try: + from collections import ChainMap +except ImportError: # pragma: no cover + from collections import MutableMapping + + try: + from reprlib import recursive_repr as _recursive_repr + except ImportError: + def _recursive_repr(fillvalue='...'): + ''' + Decorator to make a repr function return fillvalue for a recursive + call + ''' + + def decorating_function(user_function): + repr_running = set() + + def wrapper(self): + key = id(self), get_ident() + if key in repr_running: + return fillvalue + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + + # Can't use functools.wraps() here because of bootstrap issues + wrapper.__module__ = getattr(user_function, '__module__') + wrapper.__doc__ = getattr(user_function, '__doc__') + wrapper.__name__ = getattr(user_function, '__name__') + wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) + return wrapper + + return decorating_function + + class ChainMap(MutableMapping): + ''' A ChainMap groups multiple dicts (or other mappings) together + to create a single, updateable view. + + The underlying mappings are stored in a list. That list is public and can + accessed or updated using the *maps* attribute. There is no other state. + + Lookups search the underlying mappings successively until a key is found. + In contrast, writes, updates, and deletions only operate on the first + mapping. + + ''' + + def __init__(self, *maps): + '''Initialize a ChainMap by setting *maps* to the given mappings. + If no mappings are provided, a single empty dictionary is used. + + ''' + self.maps = list(maps) or [{}] # always at least one map + + def __missing__(self, key): + raise KeyError(key) + + def __getitem__(self, key): + for mapping in self.maps: + try: + return mapping[key] # can't use 'key in mapping' with defaultdict + except KeyError: + pass + return self.__missing__(key) # support subclasses that define __missing__ + + def get(self, key, default=None): + return self[key] if key in self else default + + def __len__(self): + return len(set().union(*self.maps)) # reuses stored hash values if possible + + def __iter__(self): + return iter(set().union(*self.maps)) + + def __contains__(self, key): + return any(key in m for m in self.maps) + + def __bool__(self): + return any(self.maps) + + @_recursive_repr() + def __repr__(self): + return '{0.__class__.__name__}({1})'.format( + self, ', '.join(map(repr, self.maps))) + + @classmethod + def fromkeys(cls, iterable, *args): + 'Create a ChainMap with a single dict created from the iterable.' + return cls(dict.fromkeys(iterable, *args)) + + def copy(self): + 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]' + return self.__class__(self.maps[0].copy(), *self.maps[1:]) + + __copy__ = copy + + def new_child(self): # like Django's Context.push() + 'New ChainMap with a new dict followed by all previous maps.' + return self.__class__({}, *self.maps) + + @property + def parents(self): # like Django's Context.pop() + 'New ChainMap from maps[1:].' + return self.__class__(*self.maps[1:]) + + def __setitem__(self, key, value): + self.maps[0][key] = value + + def __delitem__(self, key): + try: + del self.maps[0][key] + except KeyError: + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + + def popitem(self): + 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' + try: + return self.maps[0].popitem() + except KeyError: + raise KeyError('No keys found in the first mapping.') + + def pop(self, key, *args): + 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].' + try: + return self.maps[0].pop(key, *args) + except KeyError: + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + + def clear(self): + 'Clear maps[0], leaving maps[1:] intact.' + self.maps[0].clear() + +try: + from importlib.util import cache_from_source # Python >= 3.4 +except ImportError: # pragma: no cover + try: + from imp import cache_from_source + except ImportError: # pragma: no cover + def cache_from_source(path, debug_override=None): + assert path.endswith('.py') + if debug_override is None: + debug_override = __debug__ + if debug_override: + suffix = 'c' + else: + suffix = 'o' + return path + suffix + +try: + from collections import OrderedDict +except ImportError: # pragma: no cover +## {{{ http://code.activestate.com/recipes/576693/ (r9) +# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. +# Passes Python2.7's test suite and incorporates all the latest updates. + try: + from thread import get_ident as _get_ident + except ImportError: + from dummy_thread import get_ident as _get_ident + + try: + from _abcoll import KeysView, ValuesView, ItemsView + except ImportError: + pass + + + class OrderedDict(dict): + 'Dictionary that remembers insertion order' + # An inherited dict maps keys to values. + # The inherited dict provides __getitem__, __len__, __contains__, and get. + # The remaining methods are order-aware. + # Big-O running times for all methods are the same as for regular dictionaries. + + # The internal self.__map dictionary maps keys to links in a doubly linked list. + # The circular doubly linked list starts and ends with a sentinel element. + # The sentinel element never gets deleted (this simplifies the algorithm). + # Each link is stored as a list of length three: [PREV, NEXT, KEY]. + + def __init__(self, *args, **kwds): + '''Initialize an ordered dictionary. Signature is the same as for + regular dictionaries, but keyword arguments are not recommended + because their insertion order is arbitrary. + + ''' + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__root + except AttributeError: + self.__root = root = [] # sentinel node + root[:] = [root, root, None] + self.__map = {} + self.__update(*args, **kwds) + + def __setitem__(self, key, value, dict_setitem=dict.__setitem__): + 'od.__setitem__(i, y) <==> od[i]=y' + # Setting a new item creates a new link which goes at the end of the linked + # list, and the inherited dictionary is updated with the new key/value pair. + if key not in self: + root = self.__root + last = root[0] + last[1] = root[0] = self.__map[key] = [last, root, key] + dict_setitem(self, key, value) + + def __delitem__(self, key, dict_delitem=dict.__delitem__): + 'od.__delitem__(y) <==> del od[y]' + # Deleting an existing item uses self.__map to find the link which is + # then removed by updating the links in the predecessor and successor nodes. + dict_delitem(self, key) + link_prev, link_next, key = self.__map.pop(key) + link_prev[1] = link_next + link_next[0] = link_prev + + def __iter__(self): + 'od.__iter__() <==> iter(od)' + root = self.__root + curr = root[1] + while curr is not root: + yield curr[2] + curr = curr[1] + + def __reversed__(self): + 'od.__reversed__() <==> reversed(od)' + root = self.__root + curr = root[0] + while curr is not root: + yield curr[2] + curr = curr[0] + + def clear(self): + 'od.clear() -> None. Remove all items from od.' + try: + for node in self.__map.itervalues(): + del node[:] + root = self.__root + root[:] = [root, root, None] + self.__map.clear() + except AttributeError: + pass + dict.clear(self) + + def popitem(self, last=True): + '''od.popitem() -> (k, v), return and remove a (key, value) pair. + Pairs are returned in LIFO order if last is true or FIFO order if false. + + ''' + if not self: + raise KeyError('dictionary is empty') + root = self.__root + if last: + link = root[0] + link_prev = link[0] + link_prev[1] = root + root[0] = link_prev + else: + link = root[1] + link_next = link[1] + root[1] = link_next + link_next[0] = root + key = link[2] + del self.__map[key] + value = dict.pop(self, key) + return key, value + + # -- the following methods do not depend on the internal structure -- + + def keys(self): + 'od.keys() -> list of keys in od' + return list(self) + + def values(self): + 'od.values() -> list of values in od' + return [self[key] for key in self] + + def items(self): + 'od.items() -> list of (key, value) pairs in od' + return [(key, self[key]) for key in self] + + def iterkeys(self): + 'od.iterkeys() -> an iterator over the keys in od' + return iter(self) + + def itervalues(self): + 'od.itervalues -> an iterator over the values in od' + for k in self: + yield self[k] + + def iteritems(self): + 'od.iteritems -> an iterator over the (key, value) items in od' + for k in self: + yield (k, self[k]) + + def update(*args, **kwds): + '''od.update(E, **F) -> None. Update od from dict/iterable E and F. + + If E is a dict instance, does: for k in E: od[k] = E[k] + If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] + Or if E is an iterable of items, does: for k, v in E: od[k] = v + In either case, this is followed by: for k, v in F.items(): od[k] = v + + ''' + if len(args) > 2: + raise TypeError('update() takes at most 2 positional ' + 'arguments (%d given)' % (len(args),)) + elif not args: + raise TypeError('update() takes at least 1 argument (0 given)') + self = args[0] + # Make progressively weaker assumptions about "other" + other = () + if len(args) == 2: + other = args[1] + if isinstance(other, dict): + for key in other: + self[key] = other[key] + elif hasattr(other, 'keys'): + for key in other.keys(): + self[key] = other[key] + else: + for key, value in other: + self[key] = value + for key, value in kwds.items(): + self[key] = value + + __update = update # let subclasses override update without breaking __init__ + + __marker = object() + + def pop(self, key, default=__marker): + '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + + ''' + if key in self: + result = self[key] + del self[key] + return result + if default is self.__marker: + raise KeyError(key) + return default + + def setdefault(self, key, default=None): + 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + if key in self: + return self[key] + self[key] = default + return default + + def __repr__(self, _repr_running=None): + 'od.__repr__() <==> repr(od)' + if not _repr_running: _repr_running = {} + call_key = id(self), _get_ident() + if call_key in _repr_running: + return '...' + _repr_running[call_key] = 1 + try: + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + finally: + del _repr_running[call_key] + + def __reduce__(self): + 'Return state information for pickling' + items = [[k, self[k]] for k in self] + inst_dict = vars(self).copy() + for k in vars(OrderedDict()): + inst_dict.pop(k, None) + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def copy(self): + 'od.copy() -> a shallow copy of od' + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S + and values equal to v (which defaults to None). + + ''' + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + while comparison to a regular mapping is order-insensitive. + + ''' + if isinstance(other, OrderedDict): + return len(self)==len(other) and self.items() == other.items() + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other + + # -- the following methods are only used in Python 2.7 -- + + def viewkeys(self): + "od.viewkeys() -> a set-like object providing a view on od's keys" + return KeysView(self) + + def viewvalues(self): + "od.viewvalues() -> an object providing a view on od's values" + return ValuesView(self) + + def viewitems(self): + "od.viewitems() -> a set-like object providing a view on od's items" + return ItemsView(self) + +try: + from logging.config import BaseConfigurator, valid_ident +except ImportError: # pragma: no cover + IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I) + + + def valid_ident(s): + m = IDENTIFIER.match(s) + if not m: + raise ValueError('Not a valid Python identifier: %r' % s) + return True + + + # The ConvertingXXX classes are wrappers around standard Python containers, + # and they serve to convert any suitable values in the container. The + # conversion converts base dicts, lists and tuples to their wrapped + # equivalents, whereas strings which match a conversion format are converted + # appropriately. + # + # Each wrapper should have a configurator attribute holding the actual + # configurator to use for conversion. + + class ConvertingDict(dict): + """A converting dictionary wrapper.""" + + def __getitem__(self, key): + value = dict.__getitem__(self, key) + result = self.configurator.convert(value) + #If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def get(self, key, default=None): + value = dict.get(self, key, default) + result = self.configurator.convert(value) + #If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, key, default=None): + value = dict.pop(self, key, default) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + class ConvertingList(list): + """A converting list wrapper.""" + def __getitem__(self, key): + value = list.__getitem__(self, key) + result = self.configurator.convert(value) + #If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, idx=-1): + value = list.pop(self, idx) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + return result + + class ConvertingTuple(tuple): + """A converting tuple wrapper.""" + def __getitem__(self, key): + value = tuple.__getitem__(self, key) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + class BaseConfigurator(object): + """ + The configurator base class which defines some useful defaults. + """ + + CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$') + + WORD_PATTERN = re.compile(r'^\s*(\w+)\s*') + DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*') + INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*') + DIGIT_PATTERN = re.compile(r'^\d+$') + + value_converters = { + 'ext' : 'ext_convert', + 'cfg' : 'cfg_convert', + } + + # We might want to use a different one, e.g. importlib + importer = staticmethod(__import__) + + def __init__(self, config): + self.config = ConvertingDict(config) + self.config.configurator = self + + def resolve(self, s): + """ + Resolve strings to objects using standard import and attribute + syntax. + """ + name = s.split('.') + used = name.pop(0) + try: + found = self.importer(used) + for frag in name: + used += '.' + frag + try: + found = getattr(found, frag) + except AttributeError: + self.importer(used) + found = getattr(found, frag) + return found + except ImportError: + e, tb = sys.exc_info()[1:] + v = ValueError('Cannot resolve %r: %s' % (s, e)) + v.__cause__, v.__traceback__ = e, tb + raise v + + def ext_convert(self, value): + """Default converter for the ext:// protocol.""" + return self.resolve(value) + + def cfg_convert(self, value): + """Default converter for the cfg:// protocol.""" + rest = value + m = self.WORD_PATTERN.match(rest) + if m is None: + raise ValueError("Unable to convert %r" % value) + else: + rest = rest[m.end():] + d = self.config[m.groups()[0]] + #print d, rest + while rest: + m = self.DOT_PATTERN.match(rest) + if m: + d = d[m.groups()[0]] + else: + m = self.INDEX_PATTERN.match(rest) + if m: + idx = m.groups()[0] + if not self.DIGIT_PATTERN.match(idx): + d = d[idx] + else: + try: + n = int(idx) # try as number first (most likely) + d = d[n] + except TypeError: + d = d[idx] + if m: + rest = rest[m.end():] + else: + raise ValueError('Unable to convert ' + '%r at %r' % (value, rest)) + #rest should be empty + return d + + def convert(self, value): + """ + Convert values to an appropriate type. dicts, lists and tuples are + replaced by their converting alternatives. Strings are checked to + see if they have a conversion format and are converted if they do. + """ + if not isinstance(value, ConvertingDict) and isinstance(value, dict): + value = ConvertingDict(value) + value.configurator = self + elif not isinstance(value, ConvertingList) and isinstance(value, list): + value = ConvertingList(value) + value.configurator = self + elif not isinstance(value, ConvertingTuple) and\ + isinstance(value, tuple): + value = ConvertingTuple(value) + value.configurator = self + elif isinstance(value, string_types): + m = self.CONVERT_PATTERN.match(value) + if m: + d = m.groupdict() + prefix = d['prefix'] + converter = self.value_converters.get(prefix, None) + if converter: + suffix = d['suffix'] + converter = getattr(self, converter) + value = converter(suffix) + return value + + def configure_custom(self, config): + """Configure an object with a user-supplied factory.""" + c = config.pop('()') + if not callable(c): + c = self.resolve(c) + props = config.pop('.', None) + # Check for valid identifiers + kwargs = dict([(k, config[k]) for k in config if valid_ident(k)]) + result = c(**kwargs) + if props: + for name, value in props.items(): + setattr(result, name, value) + return result + + def as_tuple(self, value): + """Utility function which converts lists to tuples.""" + if isinstance(value, list): + value = tuple(value) + return value diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/database.py b/venv/Lib/site-packages/pip/_vendor/distlib/database.py new file mode 100644 index 0000000..a19905e --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/database.py @@ -0,0 +1,1336 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2017 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""PEP 376 implementation.""" + +from __future__ import unicode_literals + +import base64 +import codecs +import contextlib +import hashlib +import logging +import os +import posixpath +import sys +import zipimport + +from . import DistlibException, resources +from .compat import StringIO +from .version import get_scheme, UnsupportedVersionError +from .metadata import Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME +from .util import (parse_requirement, cached_property, parse_name_and_version, + read_exports, write_exports, CSVReader, CSVWriter) + + +__all__ = ['Distribution', 'BaseInstalledDistribution', + 'InstalledDistribution', 'EggInfoDistribution', + 'DistributionPath'] + + +logger = logging.getLogger(__name__) + +EXPORTS_FILENAME = 'pydist-exports.json' +COMMANDS_FILENAME = 'pydist-commands.json' + +DIST_FILES = ('INSTALLER', METADATA_FILENAME, 'RECORD', 'REQUESTED', + 'RESOURCES', EXPORTS_FILENAME, 'SHARED') + +DISTINFO_EXT = '.dist-info' + + +class _Cache(object): + """ + A simple cache mapping names and .dist-info paths to distributions + """ + def __init__(self): + """ + Initialise an instance. There is normally one for each DistributionPath. + """ + self.name = {} + self.path = {} + self.generated = False + + def clear(self): + """ + Clear the cache, setting it to its initial state. + """ + self.name.clear() + self.path.clear() + self.generated = False + + def add(self, dist): + """ + Add a distribution to the cache. + :param dist: The distribution to add. + """ + if dist.path not in self.path: + self.path[dist.path] = dist + self.name.setdefault(dist.key, []).append(dist) + + +class DistributionPath(object): + """ + Represents a set of distributions installed on a path (typically sys.path). + """ + def __init__(self, path=None, include_egg=False): + """ + Create an instance from a path, optionally including legacy (distutils/ + setuptools/distribute) distributions. + :param path: The path to use, as a list of directories. If not specified, + sys.path is used. + :param include_egg: If True, this instance will look for and return legacy + distributions as well as those based on PEP 376. + """ + if path is None: + path = sys.path + self.path = path + self._include_dist = True + self._include_egg = include_egg + + self._cache = _Cache() + self._cache_egg = _Cache() + self._cache_enabled = True + self._scheme = get_scheme('default') + + def _get_cache_enabled(self): + return self._cache_enabled + + def _set_cache_enabled(self, value): + self._cache_enabled = value + + cache_enabled = property(_get_cache_enabled, _set_cache_enabled) + + def clear_cache(self): + """ + Clears the internal cache. + """ + self._cache.clear() + self._cache_egg.clear() + + + def _yield_distributions(self): + """ + Yield .dist-info and/or .egg(-info) distributions. + """ + # We need to check if we've seen some resources already, because on + # some Linux systems (e.g. some Debian/Ubuntu variants) there are + # symlinks which alias other files in the environment. + seen = set() + for path in self.path: + finder = resources.finder_for_path(path) + if finder is None: + continue + r = finder.find('') + if not r or not r.is_container: + continue + rset = sorted(r.resources) + for entry in rset: + r = finder.find(entry) + if not r or r.path in seen: + continue + if self._include_dist and entry.endswith(DISTINFO_EXT): + possible_filenames = [METADATA_FILENAME, WHEEL_METADATA_FILENAME] + for metadata_filename in possible_filenames: + metadata_path = posixpath.join(entry, metadata_filename) + pydist = finder.find(metadata_path) + if pydist: + break + else: + continue + + with contextlib.closing(pydist.as_stream()) as stream: + metadata = Metadata(fileobj=stream, scheme='legacy') + logger.debug('Found %s', r.path) + seen.add(r.path) + yield new_dist_class(r.path, metadata=metadata, + env=self) + elif self._include_egg and entry.endswith(('.egg-info', + '.egg')): + logger.debug('Found %s', r.path) + seen.add(r.path) + yield old_dist_class(r.path, self) + + def _generate_cache(self): + """ + Scan the path for distributions and populate the cache with + those that are found. + """ + gen_dist = not self._cache.generated + gen_egg = self._include_egg and not self._cache_egg.generated + if gen_dist or gen_egg: + for dist in self._yield_distributions(): + if isinstance(dist, InstalledDistribution): + self._cache.add(dist) + else: + self._cache_egg.add(dist) + + if gen_dist: + self._cache.generated = True + if gen_egg: + self._cache_egg.generated = True + + @classmethod + def distinfo_dirname(cls, name, version): + """ + The *name* and *version* parameters are converted into their + filename-escaped form, i.e. any ``'-'`` characters are replaced + with ``'_'`` other than the one in ``'dist-info'`` and the one + separating the name from the version number. + + :parameter name: is converted to a standard distribution name by replacing + any runs of non- alphanumeric characters with a single + ``'-'``. + :type name: string + :parameter version: is converted to a standard version string. Spaces + become dots, and all other non-alphanumeric characters + (except dots) become dashes, with runs of multiple + dashes condensed to a single dash. + :type version: string + :returns: directory name + :rtype: string""" + name = name.replace('-', '_') + return '-'.join([name, version]) + DISTINFO_EXT + + def get_distributions(self): + """ + Provides an iterator that looks for distributions and returns + :class:`InstalledDistribution` or + :class:`EggInfoDistribution` instances for each one of them. + + :rtype: iterator of :class:`InstalledDistribution` and + :class:`EggInfoDistribution` instances + """ + if not self._cache_enabled: + for dist in self._yield_distributions(): + yield dist + else: + self._generate_cache() + + for dist in self._cache.path.values(): + yield dist + + if self._include_egg: + for dist in self._cache_egg.path.values(): + yield dist + + def get_distribution(self, name): + """ + Looks for a named distribution on the path. + + This function only returns the first result found, as no more than one + value is expected. If nothing is found, ``None`` is returned. + + :rtype: :class:`InstalledDistribution`, :class:`EggInfoDistribution` + or ``None`` + """ + result = None + name = name.lower() + if not self._cache_enabled: + for dist in self._yield_distributions(): + if dist.key == name: + result = dist + break + else: + self._generate_cache() + + if name in self._cache.name: + result = self._cache.name[name][0] + elif self._include_egg and name in self._cache_egg.name: + result = self._cache_egg.name[name][0] + return result + + def provides_distribution(self, name, version=None): + """ + Iterates over all distributions to find which distributions provide *name*. + If a *version* is provided, it will be used to filter the results. + + This function only returns the first result found, since no more than + one values are expected. If the directory is not found, returns ``None``. + + :parameter version: a version specifier that indicates the version + required, conforming to the format in ``PEP-345`` + + :type name: string + :type version: string + """ + matcher = None + if version is not None: + try: + matcher = self._scheme.matcher('%s (%s)' % (name, version)) + except ValueError: + raise DistlibException('invalid name or version: %r, %r' % + (name, version)) + + for dist in self.get_distributions(): + # We hit a problem on Travis where enum34 was installed and doesn't + # have a provides attribute ... + if not hasattr(dist, 'provides'): + logger.debug('No "provides": %s', dist) + else: + provided = dist.provides + + for p in provided: + p_name, p_ver = parse_name_and_version(p) + if matcher is None: + if p_name == name: + yield dist + break + else: + if p_name == name and matcher.match(p_ver): + yield dist + break + + def get_file_path(self, name, relative_path): + """ + Return the path to a resource file. + """ + dist = self.get_distribution(name) + if dist is None: + raise LookupError('no distribution named %r found' % name) + return dist.get_resource_path(relative_path) + + def get_exported_entries(self, category, name=None): + """ + Return all of the exported entries in a particular category. + + :param category: The category to search for entries. + :param name: If specified, only entries with that name are returned. + """ + for dist in self.get_distributions(): + r = dist.exports + if category in r: + d = r[category] + if name is not None: + if name in d: + yield d[name] + else: + for v in d.values(): + yield v + + +class Distribution(object): + """ + A base class for distributions, whether installed or from indexes. + Either way, it must have some metadata, so that's all that's needed + for construction. + """ + + build_time_dependency = False + """ + Set to True if it's known to be only a build-time dependency (i.e. + not needed after installation). + """ + + requested = False + """A boolean that indicates whether the ``REQUESTED`` metadata file is + present (in other words, whether the package was installed by user + request or it was installed as a dependency).""" + + def __init__(self, metadata): + """ + Initialise an instance. + :param metadata: The instance of :class:`Metadata` describing this + distribution. + """ + self.metadata = metadata + self.name = metadata.name + self.key = self.name.lower() # for case-insensitive comparisons + self.version = metadata.version + self.locator = None + self.digest = None + self.extras = None # additional features requested + self.context = None # environment marker overrides + self.download_urls = set() + self.digests = {} + + @property + def source_url(self): + """ + The source archive download URL for this distribution. + """ + return self.metadata.source_url + + download_url = source_url # Backward compatibility + + @property + def name_and_version(self): + """ + A utility property which displays the name and version in parentheses. + """ + return '%s (%s)' % (self.name, self.version) + + @property + def provides(self): + """ + A set of distribution names and versions provided by this distribution. + :return: A set of "name (version)" strings. + """ + plist = self.metadata.provides + s = '%s (%s)' % (self.name, self.version) + if s not in plist: + plist.append(s) + return plist + + def _get_requirements(self, req_attr): + md = self.metadata + logger.debug('Getting requirements from metadata %r', md.todict()) + reqts = getattr(md, req_attr) + return set(md.get_requirements(reqts, extras=self.extras, + env=self.context)) + + @property + def run_requires(self): + return self._get_requirements('run_requires') + + @property + def meta_requires(self): + return self._get_requirements('meta_requires') + + @property + def build_requires(self): + return self._get_requirements('build_requires') + + @property + def test_requires(self): + return self._get_requirements('test_requires') + + @property + def dev_requires(self): + return self._get_requirements('dev_requires') + + def matches_requirement(self, req): + """ + Say if this instance matches (fulfills) a requirement. + :param req: The requirement to match. + :rtype req: str + :return: True if it matches, else False. + """ + # Requirement may contain extras - parse to lose those + # from what's passed to the matcher + r = parse_requirement(req) + scheme = get_scheme(self.metadata.scheme) + try: + matcher = scheme.matcher(r.requirement) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + logger.warning('could not read version %r - using name only', + req) + name = req.split()[0] + matcher = scheme.matcher(name) + + name = matcher.key # case-insensitive + + result = False + for p in self.provides: + p_name, p_ver = parse_name_and_version(p) + if p_name != name: + continue + try: + result = matcher.match(p_ver) + break + except UnsupportedVersionError: + pass + return result + + def __repr__(self): + """ + Return a textual representation of this instance, + """ + if self.source_url: + suffix = ' [%s]' % self.source_url + else: + suffix = '' + return '<Distribution %s (%s)%s>' % (self.name, self.version, suffix) + + def __eq__(self, other): + """ + See if this distribution is the same as another. + :param other: The distribution to compare with. To be equal to one + another. distributions must have the same type, name, + version and source_url. + :return: True if it is the same, else False. + """ + if type(other) is not type(self): + result = False + else: + result = (self.name == other.name and + self.version == other.version and + self.source_url == other.source_url) + return result + + def __hash__(self): + """ + Compute hash in a way which matches the equality test. + """ + return hash(self.name) + hash(self.version) + hash(self.source_url) + + +class BaseInstalledDistribution(Distribution): + """ + This is the base class for installed distributions (whether PEP 376 or + legacy). + """ + + hasher = None + + def __init__(self, metadata, path, env=None): + """ + Initialise an instance. + :param metadata: An instance of :class:`Metadata` which describes the + distribution. This will normally have been initialised + from a metadata file in the ``path``. + :param path: The path of the ``.dist-info`` or ``.egg-info`` + directory for the distribution. + :param env: This is normally the :class:`DistributionPath` + instance where this distribution was found. + """ + super(BaseInstalledDistribution, self).__init__(metadata) + self.path = path + self.dist_path = env + + def get_hash(self, data, hasher=None): + """ + Get the hash of some data, using a particular hash algorithm, if + specified. + + :param data: The data to be hashed. + :type data: bytes + :param hasher: The name of a hash implementation, supported by hashlib, + or ``None``. Examples of valid values are ``'sha1'``, + ``'sha224'``, ``'sha384'``, '``sha256'``, ``'md5'`` and + ``'sha512'``. If no hasher is specified, the ``hasher`` + attribute of the :class:`InstalledDistribution` instance + is used. If the hasher is determined to be ``None``, MD5 + is used as the hashing algorithm. + :returns: The hash of the data. If a hasher was explicitly specified, + the returned hash will be prefixed with the specified hasher + followed by '='. + :rtype: str + """ + if hasher is None: + hasher = self.hasher + if hasher is None: + hasher = hashlib.md5 + prefix = '' + else: + hasher = getattr(hashlib, hasher) + prefix = '%s=' % self.hasher + digest = hasher(data).digest() + digest = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('ascii') + return '%s%s' % (prefix, digest) + + +class InstalledDistribution(BaseInstalledDistribution): + """ + Created with the *path* of the ``.dist-info`` directory provided to the + constructor. It reads the metadata contained in ``pydist.json`` when it is + instantiated., or uses a passed in Metadata instance (useful for when + dry-run mode is being used). + """ + + hasher = 'sha256' + + def __init__(self, path, metadata=None, env=None): + self.modules = [] + self.finder = finder = resources.finder_for_path(path) + if finder is None: + raise ValueError('finder unavailable for %s' % path) + if env and env._cache_enabled and path in env._cache.path: + metadata = env._cache.path[path].metadata + elif metadata is None: + r = finder.find(METADATA_FILENAME) + # Temporary - for Wheel 0.23 support + if r is None: + r = finder.find(WHEEL_METADATA_FILENAME) + # Temporary - for legacy support + if r is None: + r = finder.find('METADATA') + if r is None: + raise ValueError('no %s found in %s' % (METADATA_FILENAME, + path)) + with contextlib.closing(r.as_stream()) as stream: + metadata = Metadata(fileobj=stream, scheme='legacy') + + super(InstalledDistribution, self).__init__(metadata, path, env) + + if env and env._cache_enabled: + env._cache.add(self) + + r = finder.find('REQUESTED') + self.requested = r is not None + p = os.path.join(path, 'top_level.txt') + if os.path.exists(p): + with open(p, 'rb') as f: + data = f.read() + self.modules = data.splitlines() + + def __repr__(self): + return '<InstalledDistribution %r %s at %r>' % ( + self.name, self.version, self.path) + + def __str__(self): + return "%s %s" % (self.name, self.version) + + def _get_records(self): + """ + Get the list of installed files for the distribution + :return: A list of tuples of path, hash and size. Note that hash and + size might be ``None`` for some entries. The path is exactly + as stored in the file (which is as in PEP 376). + """ + results = [] + r = self.get_distinfo_resource('RECORD') + with contextlib.closing(r.as_stream()) as stream: + with CSVReader(stream=stream) as record_reader: + # Base location is parent dir of .dist-info dir + #base_location = os.path.dirname(self.path) + #base_location = os.path.abspath(base_location) + for row in record_reader: + missing = [None for i in range(len(row), 3)] + path, checksum, size = row + missing + #if not os.path.isabs(path): + # path = path.replace('/', os.sep) + # path = os.path.join(base_location, path) + results.append((path, checksum, size)) + return results + + @cached_property + def exports(self): + """ + Return the information exported by this distribution. + :return: A dictionary of exports, mapping an export category to a dict + of :class:`ExportEntry` instances describing the individual + export entries, and keyed by name. + """ + result = {} + r = self.get_distinfo_resource(EXPORTS_FILENAME) + if r: + result = self.read_exports() + return result + + def read_exports(self): + """ + Read exports data from a file in .ini format. + + :return: A dictionary of exports, mapping an export category to a list + of :class:`ExportEntry` instances describing the individual + export entries. + """ + result = {} + r = self.get_distinfo_resource(EXPORTS_FILENAME) + if r: + with contextlib.closing(r.as_stream()) as stream: + result = read_exports(stream) + return result + + def write_exports(self, exports): + """ + Write a dictionary of exports to a file in .ini format. + :param exports: A dictionary of exports, mapping an export category to + a list of :class:`ExportEntry` instances describing the + individual export entries. + """ + rf = self.get_distinfo_file(EXPORTS_FILENAME) + with open(rf, 'w') as f: + write_exports(exports, f) + + def get_resource_path(self, relative_path): + """ + NOTE: This API may change in the future. + + Return the absolute path to a resource file with the given relative + path. + + :param relative_path: The path, relative to .dist-info, of the resource + of interest. + :return: The absolute path where the resource is to be found. + """ + r = self.get_distinfo_resource('RESOURCES') + with contextlib.closing(r.as_stream()) as stream: + with CSVReader(stream=stream) as resources_reader: + for relative, destination in resources_reader: + if relative == relative_path: + return destination + raise KeyError('no resource file with relative path %r ' + 'is installed' % relative_path) + + def list_installed_files(self): + """ + Iterates over the ``RECORD`` entries and returns a tuple + ``(path, hash, size)`` for each line. + + :returns: iterator of (path, hash, size) + """ + for result in self._get_records(): + yield result + + def write_installed_files(self, paths, prefix, dry_run=False): + """ + Writes the ``RECORD`` file, using the ``paths`` iterable passed in. Any + existing ``RECORD`` file is silently overwritten. + + prefix is used to determine when to write absolute paths. + """ + prefix = os.path.join(prefix, '') + base = os.path.dirname(self.path) + base_under_prefix = base.startswith(prefix) + base = os.path.join(base, '') + record_path = self.get_distinfo_file('RECORD') + logger.info('creating %s', record_path) + if dry_run: + return None + with CSVWriter(record_path) as writer: + for path in paths: + if os.path.isdir(path) or path.endswith(('.pyc', '.pyo')): + # do not put size and hash, as in PEP-376 + hash_value = size = '' + else: + size = '%d' % os.path.getsize(path) + with open(path, 'rb') as fp: + hash_value = self.get_hash(fp.read()) + if path.startswith(base) or (base_under_prefix and + path.startswith(prefix)): + path = os.path.relpath(path, base) + writer.writerow((path, hash_value, size)) + + # add the RECORD file itself + if record_path.startswith(base): + record_path = os.path.relpath(record_path, base) + writer.writerow((record_path, '', '')) + return record_path + + def check_installed_files(self): + """ + Checks that the hashes and sizes of the files in ``RECORD`` are + matched by the files themselves. Returns a (possibly empty) list of + mismatches. Each entry in the mismatch list will be a tuple consisting + of the path, 'exists', 'size' or 'hash' according to what didn't match + (existence is checked first, then size, then hash), the expected + value and the actual value. + """ + mismatches = [] + base = os.path.dirname(self.path) + record_path = self.get_distinfo_file('RECORD') + for path, hash_value, size in self.list_installed_files(): + if not os.path.isabs(path): + path = os.path.join(base, path) + if path == record_path: + continue + if not os.path.exists(path): + mismatches.append((path, 'exists', True, False)) + elif os.path.isfile(path): + actual_size = str(os.path.getsize(path)) + if size and actual_size != size: + mismatches.append((path, 'size', size, actual_size)) + elif hash_value: + if '=' in hash_value: + hasher = hash_value.split('=', 1)[0] + else: + hasher = None + + with open(path, 'rb') as f: + actual_hash = self.get_hash(f.read(), hasher) + if actual_hash != hash_value: + mismatches.append((path, 'hash', hash_value, actual_hash)) + return mismatches + + @cached_property + def shared_locations(self): + """ + A dictionary of shared locations whose keys are in the set 'prefix', + 'purelib', 'platlib', 'scripts', 'headers', 'data' and 'namespace'. + The corresponding value is the absolute path of that category for + this distribution, and takes into account any paths selected by the + user at installation time (e.g. via command-line arguments). In the + case of the 'namespace' key, this would be a list of absolute paths + for the roots of namespace packages in this distribution. + + The first time this property is accessed, the relevant information is + read from the SHARED file in the .dist-info directory. + """ + result = {} + shared_path = os.path.join(self.path, 'SHARED') + if os.path.isfile(shared_path): + with codecs.open(shared_path, 'r', encoding='utf-8') as f: + lines = f.read().splitlines() + for line in lines: + key, value = line.split('=', 1) + if key == 'namespace': + result.setdefault(key, []).append(value) + else: + result[key] = value + return result + + def write_shared_locations(self, paths, dry_run=False): + """ + Write shared location information to the SHARED file in .dist-info. + :param paths: A dictionary as described in the documentation for + :meth:`shared_locations`. + :param dry_run: If True, the action is logged but no file is actually + written. + :return: The path of the file written to. + """ + shared_path = os.path.join(self.path, 'SHARED') + logger.info('creating %s', shared_path) + if dry_run: + return None + lines = [] + for key in ('prefix', 'lib', 'headers', 'scripts', 'data'): + path = paths[key] + if os.path.isdir(paths[key]): + lines.append('%s=%s' % (key, path)) + for ns in paths.get('namespace', ()): + lines.append('namespace=%s' % ns) + + with codecs.open(shared_path, 'w', encoding='utf-8') as f: + f.write('\n'.join(lines)) + return shared_path + + def get_distinfo_resource(self, path): + if path not in DIST_FILES: + raise DistlibException('invalid path for a dist-info file: ' + '%r at %r' % (path, self.path)) + finder = resources.finder_for_path(self.path) + if finder is None: + raise DistlibException('Unable to get a finder for %s' % self.path) + return finder.find(path) + + def get_distinfo_file(self, path): + """ + Returns a path located under the ``.dist-info`` directory. Returns a + string representing the path. + + :parameter path: a ``'/'``-separated path relative to the + ``.dist-info`` directory or an absolute path; + If *path* is an absolute path and doesn't start + with the ``.dist-info`` directory path, + a :class:`DistlibException` is raised + :type path: str + :rtype: str + """ + # Check if it is an absolute path # XXX use relpath, add tests + if path.find(os.sep) >= 0: + # it's an absolute path? + distinfo_dirname, path = path.split(os.sep)[-2:] + if distinfo_dirname != self.path.split(os.sep)[-1]: + raise DistlibException( + 'dist-info file %r does not belong to the %r %s ' + 'distribution' % (path, self.name, self.version)) + + # The file must be relative + if path not in DIST_FILES: + raise DistlibException('invalid path for a dist-info file: ' + '%r at %r' % (path, self.path)) + + return os.path.join(self.path, path) + + def list_distinfo_files(self): + """ + Iterates over the ``RECORD`` entries and returns paths for each line if + the path is pointing to a file located in the ``.dist-info`` directory + or one of its subdirectories. + + :returns: iterator of paths + """ + base = os.path.dirname(self.path) + for path, checksum, size in self._get_records(): + # XXX add separator or use real relpath algo + if not os.path.isabs(path): + path = os.path.join(base, path) + if path.startswith(self.path): + yield path + + def __eq__(self, other): + return (isinstance(other, InstalledDistribution) and + self.path == other.path) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + + +class EggInfoDistribution(BaseInstalledDistribution): + """Created with the *path* of the ``.egg-info`` directory or file provided + to the constructor. It reads the metadata contained in the file itself, or + if the given path happens to be a directory, the metadata is read from the + file ``PKG-INFO`` under that directory.""" + + requested = True # as we have no way of knowing, assume it was + shared_locations = {} + + def __init__(self, path, env=None): + def set_name_and_version(s, n, v): + s.name = n + s.key = n.lower() # for case-insensitive comparisons + s.version = v + + self.path = path + self.dist_path = env + if env and env._cache_enabled and path in env._cache_egg.path: + metadata = env._cache_egg.path[path].metadata + set_name_and_version(self, metadata.name, metadata.version) + else: + metadata = self._get_metadata(path) + + # Need to be set before caching + set_name_and_version(self, metadata.name, metadata.version) + + if env and env._cache_enabled: + env._cache_egg.add(self) + super(EggInfoDistribution, self).__init__(metadata, path, env) + + def _get_metadata(self, path): + requires = None + + def parse_requires_data(data): + """Create a list of dependencies from a requires.txt file. + + *data*: the contents of a setuptools-produced requires.txt file. + """ + reqs = [] + lines = data.splitlines() + for line in lines: + line = line.strip() + if line.startswith('['): + logger.warning('Unexpected line: quitting requirement scan: %r', + line) + break + r = parse_requirement(line) + if not r: + logger.warning('Not recognised as a requirement: %r', line) + continue + if r.extras: + logger.warning('extra requirements in requires.txt are ' + 'not supported') + if not r.constraints: + reqs.append(r.name) + else: + cons = ', '.join('%s%s' % c for c in r.constraints) + reqs.append('%s (%s)' % (r.name, cons)) + return reqs + + def parse_requires_path(req_path): + """Create a list of dependencies from a requires.txt file. + + *req_path*: the path to a setuptools-produced requires.txt file. + """ + + reqs = [] + try: + with codecs.open(req_path, 'r', 'utf-8') as fp: + reqs = parse_requires_data(fp.read()) + except IOError: + pass + return reqs + + tl_path = tl_data = None + if path.endswith('.egg'): + if os.path.isdir(path): + p = os.path.join(path, 'EGG-INFO') + meta_path = os.path.join(p, 'PKG-INFO') + metadata = Metadata(path=meta_path, scheme='legacy') + req_path = os.path.join(p, 'requires.txt') + tl_path = os.path.join(p, 'top_level.txt') + requires = parse_requires_path(req_path) + else: + # FIXME handle the case where zipfile is not available + zipf = zipimport.zipimporter(path) + fileobj = StringIO( + zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8')) + metadata = Metadata(fileobj=fileobj, scheme='legacy') + try: + data = zipf.get_data('EGG-INFO/requires.txt') + tl_data = zipf.get_data('EGG-INFO/top_level.txt').decode('utf-8') + requires = parse_requires_data(data.decode('utf-8')) + except IOError: + requires = None + elif path.endswith('.egg-info'): + if os.path.isdir(path): + req_path = os.path.join(path, 'requires.txt') + requires = parse_requires_path(req_path) + path = os.path.join(path, 'PKG-INFO') + tl_path = os.path.join(path, 'top_level.txt') + metadata = Metadata(path=path, scheme='legacy') + else: + raise DistlibException('path must end with .egg-info or .egg, ' + 'got %r' % path) + + if requires: + metadata.add_requirements(requires) + # look for top-level modules in top_level.txt, if present + if tl_data is None: + if tl_path is not None and os.path.exists(tl_path): + with open(tl_path, 'rb') as f: + tl_data = f.read().decode('utf-8') + if not tl_data: + tl_data = [] + else: + tl_data = tl_data.splitlines() + self.modules = tl_data + return metadata + + def __repr__(self): + return '<EggInfoDistribution %r %s at %r>' % ( + self.name, self.version, self.path) + + def __str__(self): + return "%s %s" % (self.name, self.version) + + def check_installed_files(self): + """ + Checks that the hashes and sizes of the files in ``RECORD`` are + matched by the files themselves. Returns a (possibly empty) list of + mismatches. Each entry in the mismatch list will be a tuple consisting + of the path, 'exists', 'size' or 'hash' according to what didn't match + (existence is checked first, then size, then hash), the expected + value and the actual value. + """ + mismatches = [] + record_path = os.path.join(self.path, 'installed-files.txt') + if os.path.exists(record_path): + for path, _, _ in self.list_installed_files(): + if path == record_path: + continue + if not os.path.exists(path): + mismatches.append((path, 'exists', True, False)) + return mismatches + + def list_installed_files(self): + """ + Iterates over the ``installed-files.txt`` entries and returns a tuple + ``(path, hash, size)`` for each line. + + :returns: a list of (path, hash, size) + """ + + def _md5(path): + f = open(path, 'rb') + try: + content = f.read() + finally: + f.close() + return hashlib.md5(content).hexdigest() + + def _size(path): + return os.stat(path).st_size + + record_path = os.path.join(self.path, 'installed-files.txt') + result = [] + if os.path.exists(record_path): + with codecs.open(record_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + p = os.path.normpath(os.path.join(self.path, line)) + # "./" is present as a marker between installed files + # and installation metadata files + if not os.path.exists(p): + logger.warning('Non-existent file: %s', p) + if p.endswith(('.pyc', '.pyo')): + continue + #otherwise fall through and fail + if not os.path.isdir(p): + result.append((p, _md5(p), _size(p))) + result.append((record_path, None, None)) + return result + + def list_distinfo_files(self, absolute=False): + """ + Iterates over the ``installed-files.txt`` entries and returns paths for + each line if the path is pointing to a file located in the + ``.egg-info`` directory or one of its subdirectories. + + :parameter absolute: If *absolute* is ``True``, each returned path is + transformed into a local absolute path. Otherwise the + raw value from ``installed-files.txt`` is returned. + :type absolute: boolean + :returns: iterator of paths + """ + record_path = os.path.join(self.path, 'installed-files.txt') + if os.path.exists(record_path): + skip = True + with codecs.open(record_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + if line == './': + skip = False + continue + if not skip: + p = os.path.normpath(os.path.join(self.path, line)) + if p.startswith(self.path): + if absolute: + yield p + else: + yield line + + def __eq__(self, other): + return (isinstance(other, EggInfoDistribution) and + self.path == other.path) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + +new_dist_class = InstalledDistribution +old_dist_class = EggInfoDistribution + + +class DependencyGraph(object): + """ + Represents a dependency graph between distributions. + + The dependency relationships are stored in an ``adjacency_list`` that maps + distributions to a list of ``(other, label)`` tuples where ``other`` + is a distribution and the edge is labeled with ``label`` (i.e. the version + specifier, if such was provided). Also, for more efficient traversal, for + every distribution ``x``, a list of predecessors is kept in + ``reverse_list[x]``. An edge from distribution ``a`` to + distribution ``b`` means that ``a`` depends on ``b``. If any missing + dependencies are found, they are stored in ``missing``, which is a + dictionary that maps distributions to a list of requirements that were not + provided by any other distributions. + """ + + def __init__(self): + self.adjacency_list = {} + self.reverse_list = {} + self.missing = {} + + def add_distribution(self, distribution): + """Add the *distribution* to the graph. + + :type distribution: :class:`distutils2.database.InstalledDistribution` + or :class:`distutils2.database.EggInfoDistribution` + """ + self.adjacency_list[distribution] = [] + self.reverse_list[distribution] = [] + #self.missing[distribution] = [] + + def add_edge(self, x, y, label=None): + """Add an edge from distribution *x* to distribution *y* with the given + *label*. + + :type x: :class:`distutils2.database.InstalledDistribution` or + :class:`distutils2.database.EggInfoDistribution` + :type y: :class:`distutils2.database.InstalledDistribution` or + :class:`distutils2.database.EggInfoDistribution` + :type label: ``str`` or ``None`` + """ + self.adjacency_list[x].append((y, label)) + # multiple edges are allowed, so be careful + if x not in self.reverse_list[y]: + self.reverse_list[y].append(x) + + def add_missing(self, distribution, requirement): + """ + Add a missing *requirement* for the given *distribution*. + + :type distribution: :class:`distutils2.database.InstalledDistribution` + or :class:`distutils2.database.EggInfoDistribution` + :type requirement: ``str`` + """ + logger.debug('%s missing %r', distribution, requirement) + self.missing.setdefault(distribution, []).append(requirement) + + def _repr_dist(self, dist): + return '%s %s' % (dist.name, dist.version) + + def repr_node(self, dist, level=1): + """Prints only a subgraph""" + output = [self._repr_dist(dist)] + for other, label in self.adjacency_list[dist]: + dist = self._repr_dist(other) + if label is not None: + dist = '%s [%s]' % (dist, label) + output.append(' ' * level + str(dist)) + suboutput = self.repr_node(other, level + 1) + subs = suboutput.split('\n') + output.extend(subs[1:]) + return '\n'.join(output) + + def to_dot(self, f, skip_disconnected=True): + """Writes a DOT output for the graph to the provided file *f*. + + If *skip_disconnected* is set to ``True``, then all distributions + that are not dependent on any other distribution are skipped. + + :type f: has to support ``file``-like operations + :type skip_disconnected: ``bool`` + """ + disconnected = [] + + f.write("digraph dependencies {\n") + for dist, adjs in self.adjacency_list.items(): + if len(adjs) == 0 and not skip_disconnected: + disconnected.append(dist) + for other, label in adjs: + if not label is None: + f.write('"%s" -> "%s" [label="%s"]\n' % + (dist.name, other.name, label)) + else: + f.write('"%s" -> "%s"\n' % (dist.name, other.name)) + if not skip_disconnected and len(disconnected) > 0: + f.write('subgraph disconnected {\n') + f.write('label = "Disconnected"\n') + f.write('bgcolor = red\n') + + for dist in disconnected: + f.write('"%s"' % dist.name) + f.write('\n') + f.write('}\n') + f.write('}\n') + + def topological_sort(self): + """ + Perform a topological sort of the graph. + :return: A tuple, the first element of which is a topologically sorted + list of distributions, and the second element of which is a + list of distributions that cannot be sorted because they have + circular dependencies and so form a cycle. + """ + result = [] + # Make a shallow copy of the adjacency list + alist = {} + for k, v in self.adjacency_list.items(): + alist[k] = v[:] + while True: + # See what we can remove in this run + to_remove = [] + for k, v in list(alist.items())[:]: + if not v: + to_remove.append(k) + del alist[k] + if not to_remove: + # What's left in alist (if anything) is a cycle. + break + # Remove from the adjacency list of others + for k, v in alist.items(): + alist[k] = [(d, r) for d, r in v if d not in to_remove] + logger.debug('Moving to result: %s', + ['%s (%s)' % (d.name, d.version) for d in to_remove]) + result.extend(to_remove) + return result, list(alist.keys()) + + def __repr__(self): + """Representation of the graph""" + output = [] + for dist, adjs in self.adjacency_list.items(): + output.append(self.repr_node(dist)) + return '\n'.join(output) + + +def make_graph(dists, scheme='default'): + """Makes a dependency graph from the given distributions. + + :parameter dists: a list of distributions + :type dists: list of :class:`distutils2.database.InstalledDistribution` and + :class:`distutils2.database.EggInfoDistribution` instances + :rtype: a :class:`DependencyGraph` instance + """ + scheme = get_scheme(scheme) + graph = DependencyGraph() + provided = {} # maps names to lists of (version, dist) tuples + + # first, build the graph and find out what's provided + for dist in dists: + graph.add_distribution(dist) + + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Add to provided: %s, %s, %s', name, version, dist) + provided.setdefault(name, []).append((version, dist)) + + # now make the edges + for dist in dists: + requires = (dist.run_requires | dist.meta_requires | + dist.build_requires | dist.dev_requires) + for req in requires: + try: + matcher = scheme.matcher(req) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + logger.warning('could not read version %r - using name only', + req) + name = req.split()[0] + matcher = scheme.matcher(name) + + name = matcher.key # case-insensitive + + matched = False + if name in provided: + for version, provider in provided[name]: + try: + match = matcher.match(version) + except UnsupportedVersionError: + match = False + + if match: + graph.add_edge(dist, provider, req) + matched = True + break + if not matched: + graph.add_missing(dist, req) + return graph + + +def get_dependent_dists(dists, dist): + """Recursively generate a list of distributions from *dists* that are + dependent on *dist*. + + :param dists: a list of distributions + :param dist: a distribution, member of *dists* for which we are interested + """ + if dist not in dists: + raise DistlibException('given distribution %r is not a member ' + 'of the list' % dist.name) + graph = make_graph(dists) + + dep = [dist] # dependent distributions + todo = graph.reverse_list[dist] # list of nodes we should inspect + + while todo: + d = todo.pop() + dep.append(d) + for succ in graph.reverse_list[d]: + if succ not in dep: + todo.append(succ) + + dep.pop(0) # remove dist from dep, was there to prevent infinite loops + return dep + + +def get_required_dists(dists, dist): + """Recursively generate a list of distributions from *dists* that are + required by *dist*. + + :param dists: a list of distributions + :param dist: a distribution, member of *dists* for which we are interested + """ + if dist not in dists: + raise DistlibException('given distribution %r is not a member ' + 'of the list' % dist.name) + graph = make_graph(dists) + + req = [] # required distributions + todo = graph.adjacency_list[dist] # list of nodes we should inspect + + while todo: + d = todo.pop()[0] + req.append(d) + for pred in graph.adjacency_list[d]: + if pred not in req: + todo.append(pred) + + return req + + +def make_dist(name, version, **kwargs): + """ + A convenience method for making a dist given just a name and version. + """ + summary = kwargs.pop('summary', 'Placeholder for summary') + md = Metadata(**kwargs) + md.name = name + md.version = version + md.summary = summary or 'Placeholder for summary' + return Distribution(md) diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/index.py b/venv/Lib/site-packages/pip/_vendor/distlib/index.py new file mode 100644 index 0000000..2406be2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/index.py @@ -0,0 +1,516 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import hashlib +import logging +import os +import shutil +import subprocess +import tempfile +try: + from threading import Thread +except ImportError: + from dummy_threading import Thread + +from . import DistlibException +from .compat import (HTTPBasicAuthHandler, Request, HTTPPasswordMgr, + urlparse, build_opener, string_types) +from .util import cached_property, zip_dir, ServerProxy + +logger = logging.getLogger(__name__) + +DEFAULT_INDEX = 'https://pypi.python.org/pypi' +DEFAULT_REALM = 'pypi' + +class PackageIndex(object): + """ + This class represents a package index compatible with PyPI, the Python + Package Index. + """ + + boundary = b'----------ThIs_Is_tHe_distlib_index_bouNdaRY_$' + + def __init__(self, url=None): + """ + Initialise an instance. + + :param url: The URL of the index. If not specified, the URL for PyPI is + used. + """ + self.url = url or DEFAULT_INDEX + self.read_configuration() + scheme, netloc, path, params, query, frag = urlparse(self.url) + if params or query or frag or scheme not in ('http', 'https'): + raise DistlibException('invalid repository: %s' % self.url) + self.password_handler = None + self.ssl_verifier = None + self.gpg = None + self.gpg_home = None + with open(os.devnull, 'w') as sink: + # Use gpg by default rather than gpg2, as gpg2 insists on + # prompting for passwords + for s in ('gpg', 'gpg2'): + try: + rc = subprocess.check_call([s, '--version'], stdout=sink, + stderr=sink) + if rc == 0: + self.gpg = s + break + except OSError: + pass + + def _get_pypirc_command(self): + """ + Get the distutils command for interacting with PyPI configurations. + :return: the command. + """ + from distutils.core import Distribution + from distutils.config import PyPIRCCommand + d = Distribution() + return PyPIRCCommand(d) + + def read_configuration(self): + """ + Read the PyPI access configuration as supported by distutils, getting + PyPI to do the actual work. This populates ``username``, ``password``, + ``realm`` and ``url`` attributes from the configuration. + """ + # get distutils to do the work + c = self._get_pypirc_command() + c.repository = self.url + cfg = c._read_pypirc() + self.username = cfg.get('username') + self.password = cfg.get('password') + self.realm = cfg.get('realm', 'pypi') + self.url = cfg.get('repository', self.url) + + def save_configuration(self): + """ + Save the PyPI access configuration. You must have set ``username`` and + ``password`` attributes before calling this method. + + Again, distutils is used to do the actual work. + """ + self.check_credentials() + # get distutils to do the work + c = self._get_pypirc_command() + c._store_pypirc(self.username, self.password) + + def check_credentials(self): + """ + Check that ``username`` and ``password`` have been set, and raise an + exception if not. + """ + if self.username is None or self.password is None: + raise DistlibException('username and password must be set') + pm = HTTPPasswordMgr() + _, netloc, _, _, _, _ = urlparse(self.url) + pm.add_password(self.realm, netloc, self.username, self.password) + self.password_handler = HTTPBasicAuthHandler(pm) + + def register(self, metadata): + """ + Register a distribution on PyPI, using the provided metadata. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the distribution to be + registered. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + metadata.validate() + d = metadata.todict() + d[':action'] = 'verify' + request = self.encode_request(d.items(), []) + response = self.send_request(request) + d[':action'] = 'submit' + request = self.encode_request(d.items(), []) + return self.send_request(request) + + def _reader(self, name, stream, outbuf): + """ + Thread runner for reading lines of from a subprocess into a buffer. + + :param name: The logical name of the stream (used for logging only). + :param stream: The stream to read from. This will typically a pipe + connected to the output stream of a subprocess. + :param outbuf: The list to append the read lines to. + """ + while True: + s = stream.readline() + if not s: + break + s = s.decode('utf-8').rstrip() + outbuf.append(s) + logger.debug('%s: %s' % (name, s)) + stream.close() + + def get_sign_command(self, filename, signer, sign_password, + keystore=None): + """ + Return a suitable command for signing a file. + + :param filename: The pathname to the file to be signed. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: The signing command as a list suitable to be + passed to :class:`subprocess.Popen`. + """ + cmd = [self.gpg, '--status-fd', '2', '--no-tty'] + if keystore is None: + keystore = self.gpg_home + if keystore: + cmd.extend(['--homedir', keystore]) + if sign_password is not None: + cmd.extend(['--batch', '--passphrase-fd', '0']) + td = tempfile.mkdtemp() + sf = os.path.join(td, os.path.basename(filename) + '.asc') + cmd.extend(['--detach-sign', '--armor', '--local-user', + signer, '--output', sf, filename]) + logger.debug('invoking: %s', ' '.join(cmd)) + return cmd, sf + + def run_command(self, cmd, input_data=None): + """ + Run a command in a child process , passing it any input data specified. + + :param cmd: The command to run. + :param input_data: If specified, this must be a byte string containing + data to be sent to the child process. + :return: A tuple consisting of the subprocess' exit code, a list of + lines read from the subprocess' ``stdout``, and a list of + lines read from the subprocess' ``stderr``. + """ + kwargs = { + 'stdout': subprocess.PIPE, + 'stderr': subprocess.PIPE, + } + if input_data is not None: + kwargs['stdin'] = subprocess.PIPE + stdout = [] + stderr = [] + p = subprocess.Popen(cmd, **kwargs) + # We don't use communicate() here because we may need to + # get clever with interacting with the command + t1 = Thread(target=self._reader, args=('stdout', p.stdout, stdout)) + t1.start() + t2 = Thread(target=self._reader, args=('stderr', p.stderr, stderr)) + t2.start() + if input_data is not None: + p.stdin.write(input_data) + p.stdin.close() + + p.wait() + t1.join() + t2.join() + return p.returncode, stdout, stderr + + def sign_file(self, filename, signer, sign_password, keystore=None): + """ + Sign a file. + + :param filename: The pathname to the file to be signed. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param keystore: The path to a directory which contains the keys + used in signing. If not specified, the instance's + ``gpg_home`` attribute is used instead. + :return: The absolute pathname of the file where the signature is + stored. + """ + cmd, sig_file = self.get_sign_command(filename, signer, sign_password, + keystore) + rc, stdout, stderr = self.run_command(cmd, + sign_password.encode('utf-8')) + if rc != 0: + raise DistlibException('sign command failed with error ' + 'code %s' % rc) + return sig_file + + def upload_file(self, metadata, filename, signer=None, sign_password=None, + filetype='sdist', pyversion='source', keystore=None): + """ + Upload a release file to the index. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the file to be uploaded. + :param filename: The pathname of the file to be uploaded. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param filetype: The type of the file being uploaded. This is the + distutils command which produced that file, e.g. + ``sdist`` or ``bdist_wheel``. + :param pyversion: The version of Python which the release relates + to. For code compatible with any Python, this would + be ``source``, otherwise it would be e.g. ``3.2``. + :param keystore: The path to a directory which contains the keys + used in signing. If not specified, the instance's + ``gpg_home`` attribute is used instead. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + if not os.path.exists(filename): + raise DistlibException('not found: %s' % filename) + metadata.validate() + d = metadata.todict() + sig_file = None + if signer: + if not self.gpg: + logger.warning('no signing program available - not signed') + else: + sig_file = self.sign_file(filename, signer, sign_password, + keystore) + with open(filename, 'rb') as f: + file_data = f.read() + md5_digest = hashlib.md5(file_data).hexdigest() + sha256_digest = hashlib.sha256(file_data).hexdigest() + d.update({ + ':action': 'file_upload', + 'protocol_version': '1', + 'filetype': filetype, + 'pyversion': pyversion, + 'md5_digest': md5_digest, + 'sha256_digest': sha256_digest, + }) + files = [('content', os.path.basename(filename), file_data)] + if sig_file: + with open(sig_file, 'rb') as f: + sig_data = f.read() + files.append(('gpg_signature', os.path.basename(sig_file), + sig_data)) + shutil.rmtree(os.path.dirname(sig_file)) + request = self.encode_request(d.items(), files) + return self.send_request(request) + + def upload_documentation(self, metadata, doc_dir): + """ + Upload documentation to the index. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the documentation to be + uploaded. + :param doc_dir: The pathname of the directory which contains the + documentation. This should be the directory that + contains the ``index.html`` for the documentation. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + if not os.path.isdir(doc_dir): + raise DistlibException('not a directory: %r' % doc_dir) + fn = os.path.join(doc_dir, 'index.html') + if not os.path.exists(fn): + raise DistlibException('not found: %r' % fn) + metadata.validate() + name, version = metadata.name, metadata.version + zip_data = zip_dir(doc_dir).getvalue() + fields = [(':action', 'doc_upload'), + ('name', name), ('version', version)] + files = [('content', name, zip_data)] + request = self.encode_request(fields, files) + return self.send_request(request) + + def get_verify_command(self, signature_filename, data_filename, + keystore=None): + """ + Return a suitable command for verifying a file. + + :param signature_filename: The pathname to the file containing the + signature. + :param data_filename: The pathname to the file containing the + signed data. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: The verifying command as a list suitable to be + passed to :class:`subprocess.Popen`. + """ + cmd = [self.gpg, '--status-fd', '2', '--no-tty'] + if keystore is None: + keystore = self.gpg_home + if keystore: + cmd.extend(['--homedir', keystore]) + cmd.extend(['--verify', signature_filename, data_filename]) + logger.debug('invoking: %s', ' '.join(cmd)) + return cmd + + def verify_signature(self, signature_filename, data_filename, + keystore=None): + """ + Verify a signature for a file. + + :param signature_filename: The pathname to the file containing the + signature. + :param data_filename: The pathname to the file containing the + signed data. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: True if the signature was verified, else False. + """ + if not self.gpg: + raise DistlibException('verification unavailable because gpg ' + 'unavailable') + cmd = self.get_verify_command(signature_filename, data_filename, + keystore) + rc, stdout, stderr = self.run_command(cmd) + if rc not in (0, 1): + raise DistlibException('verify command failed with error ' + 'code %s' % rc) + return rc == 0 + + def download_file(self, url, destfile, digest=None, reporthook=None): + """ + This is a convenience method for downloading a file from an URL. + Normally, this will be a file from the index, though currently + no check is made for this (i.e. a file can be downloaded from + anywhere). + + The method is just like the :func:`urlretrieve` function in the + standard library, except that it allows digest computation to be + done during download and checking that the downloaded data + matched any expected value. + + :param url: The URL of the file to be downloaded (assumed to be + available via an HTTP GET request). + :param destfile: The pathname where the downloaded file is to be + saved. + :param digest: If specified, this must be a (hasher, value) + tuple, where hasher is the algorithm used (e.g. + ``'md5'``) and ``value`` is the expected value. + :param reporthook: The same as for :func:`urlretrieve` in the + standard library. + """ + if digest is None: + digester = None + logger.debug('No digest specified') + else: + if isinstance(digest, (list, tuple)): + hasher, digest = digest + else: + hasher = 'md5' + digester = getattr(hashlib, hasher)() + logger.debug('Digest specified: %s' % digest) + # The following code is equivalent to urlretrieve. + # We need to do it this way so that we can compute the + # digest of the file as we go. + with open(destfile, 'wb') as dfp: + # addinfourl is not a context manager on 2.x + # so we have to use try/finally + sfp = self.send_request(Request(url)) + try: + headers = sfp.info() + blocksize = 8192 + size = -1 + read = 0 + blocknum = 0 + if "content-length" in headers: + size = int(headers["Content-Length"]) + if reporthook: + reporthook(blocknum, blocksize, size) + while True: + block = sfp.read(blocksize) + if not block: + break + read += len(block) + dfp.write(block) + if digester: + digester.update(block) + blocknum += 1 + if reporthook: + reporthook(blocknum, blocksize, size) + finally: + sfp.close() + + # check that we got the whole file, if we can + if size >= 0 and read < size: + raise DistlibException( + 'retrieval incomplete: got only %d out of %d bytes' + % (read, size)) + # if we have a digest, it must match. + if digester: + actual = digester.hexdigest() + if digest != actual: + raise DistlibException('%s digest mismatch for %s: expected ' + '%s, got %s' % (hasher, destfile, + digest, actual)) + logger.debug('Digest verified: %s', digest) + + def send_request(self, req): + """ + Send a standard library :class:`Request` to PyPI and return its + response. + + :param req: The request to send. + :return: The HTTP response from PyPI (a standard library HTTPResponse). + """ + handlers = [] + if self.password_handler: + handlers.append(self.password_handler) + if self.ssl_verifier: + handlers.append(self.ssl_verifier) + opener = build_opener(*handlers) + return opener.open(req) + + def encode_request(self, fields, files): + """ + Encode fields and files for posting to an HTTP server. + + :param fields: The fields to send as a list of (fieldname, value) + tuples. + :param files: The files to send as a list of (fieldname, filename, + file_bytes) tuple. + """ + # Adapted from packaging, which in turn was adapted from + # http://code.activestate.com/recipes/146306 + + parts = [] + boundary = self.boundary + for k, values in fields: + if not isinstance(values, (list, tuple)): + values = [values] + + for v in values: + parts.extend(( + b'--' + boundary, + ('Content-Disposition: form-data; name="%s"' % + k).encode('utf-8'), + b'', + v.encode('utf-8'))) + for key, filename, value in files: + parts.extend(( + b'--' + boundary, + ('Content-Disposition: form-data; name="%s"; filename="%s"' % + (key, filename)).encode('utf-8'), + b'', + value)) + + parts.extend((b'--' + boundary + b'--', b'')) + + body = b'\r\n'.join(parts) + ct = b'multipart/form-data; boundary=' + boundary + headers = { + 'Content-type': ct, + 'Content-length': str(len(body)) + } + return Request(self.url, body, headers) + + def search(self, terms, operator=None): + if isinstance(terms, string_types): + terms = {'name': terms} + rpc_proxy = ServerProxy(self.url, timeout=3.0) + try: + return rpc_proxy.search(terms, operator or 'and') + finally: + rpc_proxy('close')() diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/locators.py b/venv/Lib/site-packages/pip/_vendor/distlib/locators.py new file mode 100644 index 0000000..11d2636 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/locators.py @@ -0,0 +1,1292 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2015 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# + +import gzip +from io import BytesIO +import json +import logging +import os +import posixpath +import re +try: + import threading +except ImportError: # pragma: no cover + import dummy_threading as threading +import zlib + +from . import DistlibException +from .compat import (urljoin, urlparse, urlunparse, url2pathname, pathname2url, + queue, quote, unescape, string_types, build_opener, + HTTPRedirectHandler as BaseRedirectHandler, text_type, + Request, HTTPError, URLError) +from .database import Distribution, DistributionPath, make_dist +from .metadata import Metadata, MetadataInvalidError +from .util import (cached_property, parse_credentials, ensure_slash, + split_filename, get_project_data, parse_requirement, + parse_name_and_version, ServerProxy, normalize_name) +from .version import get_scheme, UnsupportedVersionError +from .wheel import Wheel, is_compatible + +logger = logging.getLogger(__name__) + +HASHER_HASH = re.compile(r'^(\w+)=([a-f0-9]+)') +CHARSET = re.compile(r';\s*charset\s*=\s*(.*)\s*$', re.I) +HTML_CONTENT_TYPE = re.compile('text/html|application/x(ht)?ml') +DEFAULT_INDEX = 'https://pypi.python.org/pypi' + +def get_all_distribution_names(url=None): + """ + Return all distribution names known by an index. + :param url: The URL of the index. + :return: A list of all known distribution names. + """ + if url is None: + url = DEFAULT_INDEX + client = ServerProxy(url, timeout=3.0) + try: + return client.list_packages() + finally: + client('close')() + +class RedirectHandler(BaseRedirectHandler): + """ + A class to work around a bug in some Python 3.2.x releases. + """ + # There's a bug in the base version for some 3.2.x + # (e.g. 3.2.2 on Ubuntu Oneiric). If a Location header + # returns e.g. /abc, it bails because it says the scheme '' + # is bogus, when actually it should use the request's + # URL for the scheme. See Python issue #13696. + def http_error_302(self, req, fp, code, msg, headers): + # Some servers (incorrectly) return multiple Location headers + # (so probably same goes for URI). Use first header. + newurl = None + for key in ('location', 'uri'): + if key in headers: + newurl = headers[key] + break + if newurl is None: # pragma: no cover + return + urlparts = urlparse(newurl) + if urlparts.scheme == '': + newurl = urljoin(req.get_full_url(), newurl) + if hasattr(headers, 'replace_header'): + headers.replace_header(key, newurl) + else: + headers[key] = newurl + return BaseRedirectHandler.http_error_302(self, req, fp, code, msg, + headers) + + http_error_301 = http_error_303 = http_error_307 = http_error_302 + +class Locator(object): + """ + A base class for locators - things that locate distributions. + """ + source_extensions = ('.tar.gz', '.tar.bz2', '.tar', '.zip', '.tgz', '.tbz') + binary_extensions = ('.egg', '.exe', '.whl') + excluded_extensions = ('.pdf',) + + # A list of tags indicating which wheels you want to match. The default + # value of None matches against the tags compatible with the running + # Python. If you want to match other values, set wheel_tags on a locator + # instance to a list of tuples (pyver, abi, arch) which you want to match. + wheel_tags = None + + downloadable_extensions = source_extensions + ('.whl',) + + def __init__(self, scheme='default'): + """ + Initialise an instance. + :param scheme: Because locators look for most recent versions, they + need to know the version scheme to use. This specifies + the current PEP-recommended scheme - use ``'legacy'`` + if you need to support existing distributions on PyPI. + """ + self._cache = {} + self.scheme = scheme + # Because of bugs in some of the handlers on some of the platforms, + # we use our own opener rather than just using urlopen. + self.opener = build_opener(RedirectHandler()) + # If get_project() is called from locate(), the matcher instance + # is set from the requirement passed to locate(). See issue #18 for + # why this can be useful to know. + self.matcher = None + self.errors = queue.Queue() + + def get_errors(self): + """ + Return any errors which have occurred. + """ + result = [] + while not self.errors.empty(): # pragma: no cover + try: + e = self.errors.get(False) + result.append(e) + except self.errors.Empty: + continue + self.errors.task_done() + return result + + def clear_errors(self): + """ + Clear any errors which may have been logged. + """ + # Just get the errors and throw them away + self.get_errors() + + def clear_cache(self): + self._cache.clear() + + def _get_scheme(self): + return self._scheme + + def _set_scheme(self, value): + self._scheme = value + + scheme = property(_get_scheme, _set_scheme) + + def _get_project(self, name): + """ + For a given project, get a dictionary mapping available versions to Distribution + instances. + + This should be implemented in subclasses. + + If called from a locate() request, self.matcher will be set to a + matcher for the requirement to satisfy, otherwise it will be None. + """ + raise NotImplementedError('Please implement in the subclass') + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Please implement in the subclass') + + def get_project(self, name): + """ + For a given project, get a dictionary mapping available versions to Distribution + instances. + + This calls _get_project to do all the work, and just implements a caching layer on top. + """ + if self._cache is None: # pragma: no cover + result = self._get_project(name) + elif name in self._cache: + result = self._cache[name] + else: + self.clear_errors() + result = self._get_project(name) + self._cache[name] = result + return result + + def score_url(self, url): + """ + Give an url a score which can be used to choose preferred URLs + for a given project release. + """ + t = urlparse(url) + basename = posixpath.basename(t.path) + compatible = True + is_wheel = basename.endswith('.whl') + is_downloadable = basename.endswith(self.downloadable_extensions) + if is_wheel: + compatible = is_compatible(Wheel(basename), self.wheel_tags) + return (t.scheme == 'https', 'pypi.python.org' in t.netloc, + is_downloadable, is_wheel, compatible, basename) + + def prefer_url(self, url1, url2): + """ + Choose one of two URLs where both are candidates for distribution + archives for the same version of a distribution (for example, + .tar.gz vs. zip). + + The current implementation favours https:// URLs over http://, archives + from PyPI over those from other locations, wheel compatibility (if a + wheel) and then the archive name. + """ + result = url2 + if url1: + s1 = self.score_url(url1) + s2 = self.score_url(url2) + if s1 > s2: + result = url1 + if result != url2: + logger.debug('Not replacing %r with %r', url1, url2) + else: + logger.debug('Replacing %r with %r', url1, url2) + return result + + def split_filename(self, filename, project_name): + """ + Attempt to split a filename in project name, version and Python version. + """ + return split_filename(filename, project_name) + + def convert_url_to_download_info(self, url, project_name): + """ + See if a URL is a candidate for a download URL for a project (the URL + has typically been scraped from an HTML page). + + If it is, a dictionary is returned with keys "name", "version", + "filename" and "url"; otherwise, None is returned. + """ + def same_project(name1, name2): + return normalize_name(name1) == normalize_name(name2) + + result = None + scheme, netloc, path, params, query, frag = urlparse(url) + if frag.lower().startswith('egg='): # pragma: no cover + logger.debug('%s: version hint in fragment: %r', + project_name, frag) + m = HASHER_HASH.match(frag) + if m: + algo, digest = m.groups() + else: + algo, digest = None, None + origpath = path + if path and path[-1] == '/': # pragma: no cover + path = path[:-1] + if path.endswith('.whl'): + try: + wheel = Wheel(path) + if is_compatible(wheel, self.wheel_tags): + if project_name is None: + include = True + else: + include = same_project(wheel.name, project_name) + if include: + result = { + 'name': wheel.name, + 'version': wheel.version, + 'filename': wheel.filename, + 'url': urlunparse((scheme, netloc, origpath, + params, query, '')), + 'python-version': ', '.join( + ['.'.join(list(v[2:])) for v in wheel.pyver]), + } + except Exception as e: # pragma: no cover + logger.warning('invalid path for wheel: %s', path) + elif not path.endswith(self.downloadable_extensions): # pragma: no cover + logger.debug('Not downloadable: %s', path) + else: # downloadable extension + path = filename = posixpath.basename(path) + for ext in self.downloadable_extensions: + if path.endswith(ext): + path = path[:-len(ext)] + t = self.split_filename(path, project_name) + if not t: # pragma: no cover + logger.debug('No match for project/version: %s', path) + else: + name, version, pyver = t + if not project_name or same_project(project_name, name): + result = { + 'name': name, + 'version': version, + 'filename': filename, + 'url': urlunparse((scheme, netloc, origpath, + params, query, '')), + #'packagetype': 'sdist', + } + if pyver: # pragma: no cover + result['python-version'] = pyver + break + if result and algo: + result['%s_digest' % algo] = digest + return result + + def _get_digest(self, info): + """ + Get a digest from a dictionary by looking at keys of the form + 'algo_digest'. + + Returns a 2-tuple (algo, digest) if found, else None. Currently + looks only for SHA256, then MD5. + """ + result = None + for algo in ('sha256', 'md5'): + key = '%s_digest' % algo + if key in info: + result = (algo, info[key]) + break + return result + + def _update_version_data(self, result, info): + """ + Update a result dictionary (the final result from _get_project) with a + dictionary for a specific version, which typically holds information + gleaned from a filename or URL for an archive for the distribution. + """ + name = info.pop('name') + version = info.pop('version') + if version in result: + dist = result[version] + md = dist.metadata + else: + dist = make_dist(name, version, scheme=self.scheme) + md = dist.metadata + dist.digest = digest = self._get_digest(info) + url = info['url'] + result['digests'][url] = digest + if md.source_url != info['url']: + md.source_url = self.prefer_url(md.source_url, url) + result['urls'].setdefault(version, set()).add(url) + dist.locator = self + result[version] = dist + + def locate(self, requirement, prereleases=False): + """ + Find the most recent distribution which matches the given + requirement. + + :param requirement: A requirement of the form 'foo (1.0)' or perhaps + 'foo (>= 1.0, < 2.0, != 1.3)' + :param prereleases: If ``True``, allow pre-release versions + to be located. Otherwise, pre-release versions + are not returned. + :return: A :class:`Distribution` instance, or ``None`` if no such + distribution could be located. + """ + result = None + r = parse_requirement(requirement) + if r is None: # pragma: no cover + raise DistlibException('Not a valid requirement: %r' % requirement) + scheme = get_scheme(self.scheme) + self.matcher = matcher = scheme.matcher(r.requirement) + logger.debug('matcher: %s (%s)', matcher, type(matcher).__name__) + versions = self.get_project(r.name) + if len(versions) > 2: # urls and digests keys are present + # sometimes, versions are invalid + slist = [] + vcls = matcher.version_class + for k in versions: + if k in ('urls', 'digests'): + continue + try: + if not matcher.match(k): + logger.debug('%s did not match %r', matcher, k) + else: + if prereleases or not vcls(k).is_prerelease: + slist.append(k) + else: + logger.debug('skipping pre-release ' + 'version %s of %s', k, matcher.name) + except Exception: # pragma: no cover + logger.warning('error matching %s with %r', matcher, k) + pass # slist.append(k) + if len(slist) > 1: + slist = sorted(slist, key=scheme.key) + if slist: + logger.debug('sorted list: %s', slist) + version = slist[-1] + result = versions[version] + if result: + if r.extras: + result.extras = r.extras + result.download_urls = versions.get('urls', {}).get(version, set()) + d = {} + sd = versions.get('digests', {}) + for url in result.download_urls: + if url in sd: # pragma: no cover + d[url] = sd[url] + result.digests = d + self.matcher = None + return result + + +class PyPIRPCLocator(Locator): + """ + This locator uses XML-RPC to locate distributions. It therefore + cannot be used with simple mirrors (that only mirror file content). + """ + def __init__(self, url, **kwargs): + """ + Initialise an instance. + + :param url: The URL to use for XML-RPC. + :param kwargs: Passed to the superclass constructor. + """ + super(PyPIRPCLocator, self).__init__(**kwargs) + self.base_url = url + self.client = ServerProxy(url, timeout=3.0) + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + return set(self.client.list_packages()) + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + versions = self.client.package_releases(name, True) + for v in versions: + urls = self.client.release_urls(name, v) + data = self.client.release_data(name, v) + metadata = Metadata(scheme=self.scheme) + metadata.name = data['name'] + metadata.version = data['version'] + metadata.license = data.get('license') + metadata.keywords = data.get('keywords', []) + metadata.summary = data.get('summary') + dist = Distribution(metadata) + if urls: + info = urls[0] + metadata.source_url = info['url'] + dist.digest = self._get_digest(info) + dist.locator = self + result[v] = dist + for info in urls: + url = info['url'] + digest = self._get_digest(info) + result['urls'].setdefault(v, set()).add(url) + result['digests'][url] = digest + return result + +class PyPIJSONLocator(Locator): + """ + This locator uses PyPI's JSON interface. It's very limited in functionality + and probably not worth using. + """ + def __init__(self, url, **kwargs): + super(PyPIJSONLocator, self).__init__(**kwargs) + self.base_url = ensure_slash(url) + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Not available from this locator') + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + url = urljoin(self.base_url, '%s/json' % quote(name)) + try: + resp = self.opener.open(url) + data = resp.read().decode() # for now + d = json.loads(data) + md = Metadata(scheme=self.scheme) + data = d['info'] + md.name = data['name'] + md.version = data['version'] + md.license = data.get('license') + md.keywords = data.get('keywords', []) + md.summary = data.get('summary') + dist = Distribution(md) + dist.locator = self + urls = d['urls'] + result[md.version] = dist + for info in d['urls']: + url = info['url'] + dist.download_urls.add(url) + dist.digests[url] = self._get_digest(info) + result['urls'].setdefault(md.version, set()).add(url) + result['digests'][url] = self._get_digest(info) + # Now get other releases + for version, infos in d['releases'].items(): + if version == md.version: + continue # already done + omd = Metadata(scheme=self.scheme) + omd.name = md.name + omd.version = version + odist = Distribution(omd) + odist.locator = self + result[version] = odist + for info in infos: + url = info['url'] + odist.download_urls.add(url) + odist.digests[url] = self._get_digest(info) + result['urls'].setdefault(version, set()).add(url) + result['digests'][url] = self._get_digest(info) +# for info in urls: +# md.source_url = info['url'] +# dist.digest = self._get_digest(info) +# dist.locator = self +# for info in urls: +# url = info['url'] +# result['urls'].setdefault(md.version, set()).add(url) +# result['digests'][url] = self._get_digest(info) + except Exception as e: + self.errors.put(text_type(e)) + logger.exception('JSON fetch failed: %s', e) + return result + + +class Page(object): + """ + This class represents a scraped HTML page. + """ + # The following slightly hairy-looking regex just looks for the contents of + # an anchor link, which has an attribute "href" either immediately preceded + # or immediately followed by a "rel" attribute. The attribute values can be + # declared with double quotes, single quotes or no quotes - which leads to + # the length of the expression. + _href = re.compile(""" +(rel\\s*=\\s*(?:"(?P<rel1>[^"]*)"|'(?P<rel2>[^']*)'|(?P<rel3>[^>\\s\n]*))\\s+)? +href\\s*=\\s*(?:"(?P<url1>[^"]*)"|'(?P<url2>[^']*)'|(?P<url3>[^>\\s\n]*)) +(\\s+rel\\s*=\\s*(?:"(?P<rel4>[^"]*)"|'(?P<rel5>[^']*)'|(?P<rel6>[^>\\s\n]*)))? +""", re.I | re.S | re.X) + _base = re.compile(r"""<base\s+href\s*=\s*['"]?([^'">]+)""", re.I | re.S) + + def __init__(self, data, url): + """ + Initialise an instance with the Unicode page contents and the URL they + came from. + """ + self.data = data + self.base_url = self.url = url + m = self._base.search(self.data) + if m: + self.base_url = m.group(1) + + _clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) + + @cached_property + def links(self): + """ + Return the URLs of all the links on a page together with information + about their "rel" attribute, for determining which ones to treat as + downloads and which ones to queue for further scraping. + """ + def clean(url): + "Tidy up an URL." + scheme, netloc, path, params, query, frag = urlparse(url) + return urlunparse((scheme, netloc, quote(path), + params, query, frag)) + + result = set() + for match in self._href.finditer(self.data): + d = match.groupdict('') + rel = (d['rel1'] or d['rel2'] or d['rel3'] or + d['rel4'] or d['rel5'] or d['rel6']) + url = d['url1'] or d['url2'] or d['url3'] + url = urljoin(self.base_url, url) + url = unescape(url) + url = self._clean_re.sub(lambda m: '%%%2x' % ord(m.group(0)), url) + result.add((url, rel)) + # We sort the result, hoping to bring the most recent versions + # to the front + result = sorted(result, key=lambda t: t[0], reverse=True) + return result + + +class SimpleScrapingLocator(Locator): + """ + A locator which scrapes HTML pages to locate downloads for a distribution. + This runs multiple threads to do the I/O; performance is at least as good + as pip's PackageFinder, which works in an analogous fashion. + """ + + # These are used to deal with various Content-Encoding schemes. + decoders = { + 'deflate': zlib.decompress, + 'gzip': lambda b: gzip.GzipFile(fileobj=BytesIO(d)).read(), + 'none': lambda b: b, + } + + def __init__(self, url, timeout=None, num_workers=10, **kwargs): + """ + Initialise an instance. + :param url: The root URL to use for scraping. + :param timeout: The timeout, in seconds, to be applied to requests. + This defaults to ``None`` (no timeout specified). + :param num_workers: The number of worker threads you want to do I/O, + This defaults to 10. + :param kwargs: Passed to the superclass. + """ + super(SimpleScrapingLocator, self).__init__(**kwargs) + self.base_url = ensure_slash(url) + self.timeout = timeout + self._page_cache = {} + self._seen = set() + self._to_fetch = queue.Queue() + self._bad_hosts = set() + self.skip_externals = False + self.num_workers = num_workers + self._lock = threading.RLock() + # See issue #45: we need to be resilient when the locator is used + # in a thread, e.g. with concurrent.futures. We can't use self._lock + # as it is for coordinating our internal threads - the ones created + # in _prepare_threads. + self._gplock = threading.RLock() + + def _prepare_threads(self): + """ + Threads are created only when get_project is called, and terminate + before it returns. They are there primarily to parallelise I/O (i.e. + fetching web pages). + """ + self._threads = [] + for i in range(self.num_workers): + t = threading.Thread(target=self._fetch) + t.setDaemon(True) + t.start() + self._threads.append(t) + + def _wait_threads(self): + """ + Tell all the threads to terminate (by sending a sentinel value) and + wait for them to do so. + """ + # Note that you need two loops, since you can't say which + # thread will get each sentinel + for t in self._threads: + self._to_fetch.put(None) # sentinel + for t in self._threads: + t.join() + self._threads = [] + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + with self._gplock: + self.result = result + self.project_name = name + url = urljoin(self.base_url, '%s/' % quote(name)) + self._seen.clear() + self._page_cache.clear() + self._prepare_threads() + try: + logger.debug('Queueing %s', url) + self._to_fetch.put(url) + self._to_fetch.join() + finally: + self._wait_threads() + del self.result + return result + + platform_dependent = re.compile(r'\b(linux-(i\d86|x86_64|arm\w+)|' + r'win(32|-amd64)|macosx-?\d+)\b', re.I) + + def _is_platform_dependent(self, url): + """ + Does an URL refer to a platform-specific download? + """ + return self.platform_dependent.search(url) + + def _process_download(self, url): + """ + See if an URL is a suitable download for a project. + + If it is, register information in the result dictionary (for + _get_project) about the specific version it's for. + + Note that the return value isn't actually used other than as a boolean + value. + """ + if self._is_platform_dependent(url): + info = None + else: + info = self.convert_url_to_download_info(url, self.project_name) + logger.debug('process_download: %s -> %s', url, info) + if info: + with self._lock: # needed because self.result is shared + self._update_version_data(self.result, info) + return info + + def _should_queue(self, link, referrer, rel): + """ + Determine whether a link URL from a referring page and with a + particular "rel" attribute should be queued for scraping. + """ + scheme, netloc, path, _, _, _ = urlparse(link) + if path.endswith(self.source_extensions + self.binary_extensions + + self.excluded_extensions): + result = False + elif self.skip_externals and not link.startswith(self.base_url): + result = False + elif not referrer.startswith(self.base_url): + result = False + elif rel not in ('homepage', 'download'): + result = False + elif scheme not in ('http', 'https', 'ftp'): + result = False + elif self._is_platform_dependent(link): + result = False + else: + host = netloc.split(':', 1)[0] + if host.lower() == 'localhost': + result = False + else: + result = True + logger.debug('should_queue: %s (%s) from %s -> %s', link, rel, + referrer, result) + return result + + def _fetch(self): + """ + Get a URL to fetch from the work queue, get the HTML page, examine its + links for download candidates and candidates for further scraping. + + This is a handy method to run in a thread. + """ + while True: + url = self._to_fetch.get() + try: + if url: + page = self.get_page(url) + if page is None: # e.g. after an error + continue + for link, rel in page.links: + if link not in self._seen: + try: + self._seen.add(link) + if (not self._process_download(link) and + self._should_queue(link, url, rel)): + logger.debug('Queueing %s from %s', link, url) + self._to_fetch.put(link) + except MetadataInvalidError: # e.g. invalid versions + pass + except Exception as e: # pragma: no cover + self.errors.put(text_type(e)) + finally: + # always do this, to avoid hangs :-) + self._to_fetch.task_done() + if not url: + #logger.debug('Sentinel seen, quitting.') + break + + def get_page(self, url): + """ + Get the HTML for an URL, possibly from an in-memory cache. + + XXX TODO Note: this cache is never actually cleared. It's assumed that + the data won't get stale over the lifetime of a locator instance (not + necessarily true for the default_locator). + """ + # http://peak.telecommunity.com/DevCenter/EasyInstall#package-index-api + scheme, netloc, path, _, _, _ = urlparse(url) + if scheme == 'file' and os.path.isdir(url2pathname(path)): + url = urljoin(ensure_slash(url), 'index.html') + + if url in self._page_cache: + result = self._page_cache[url] + logger.debug('Returning %s from cache: %s', url, result) + else: + host = netloc.split(':', 1)[0] + result = None + if host in self._bad_hosts: + logger.debug('Skipping %s due to bad host %s', url, host) + else: + req = Request(url, headers={'Accept-encoding': 'identity'}) + try: + logger.debug('Fetching %s', url) + resp = self.opener.open(req, timeout=self.timeout) + logger.debug('Fetched %s', url) + headers = resp.info() + content_type = headers.get('Content-Type', '') + if HTML_CONTENT_TYPE.match(content_type): + final_url = resp.geturl() + data = resp.read() + encoding = headers.get('Content-Encoding') + if encoding: + decoder = self.decoders[encoding] # fail if not found + data = decoder(data) + encoding = 'utf-8' + m = CHARSET.search(content_type) + if m: + encoding = m.group(1) + try: + data = data.decode(encoding) + except UnicodeError: # pragma: no cover + data = data.decode('latin-1') # fallback + result = Page(data, final_url) + self._page_cache[final_url] = result + except HTTPError as e: + if e.code != 404: + logger.exception('Fetch failed: %s: %s', url, e) + except URLError as e: # pragma: no cover + logger.exception('Fetch failed: %s: %s', url, e) + with self._lock: + self._bad_hosts.add(host) + except Exception as e: # pragma: no cover + logger.exception('Fetch failed: %s: %s', url, e) + finally: + self._page_cache[url] = result # even if None (failure) + return result + + _distname_re = re.compile('<a href=[^>]*>([^<]+)<') + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + page = self.get_page(self.base_url) + if not page: + raise DistlibException('Unable to get %s' % self.base_url) + for match in self._distname_re.finditer(page.data): + result.add(match.group(1)) + return result + +class DirectoryLocator(Locator): + """ + This class locates distributions in a directory tree. + """ + + def __init__(self, path, **kwargs): + """ + Initialise an instance. + :param path: The root of the directory tree to search. + :param kwargs: Passed to the superclass constructor, + except for: + * recursive - if True (the default), subdirectories are + recursed into. If False, only the top-level directory + is searched, + """ + self.recursive = kwargs.pop('recursive', True) + super(DirectoryLocator, self).__init__(**kwargs) + path = os.path.abspath(path) + if not os.path.isdir(path): # pragma: no cover + raise DistlibException('Not a directory: %r' % path) + self.base_dir = path + + def should_include(self, filename, parent): + """ + Should a filename be considered as a candidate for a distribution + archive? As well as the filename, the directory which contains it + is provided, though not used by the current implementation. + """ + return filename.endswith(self.downloadable_extensions) + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + for root, dirs, files in os.walk(self.base_dir): + for fn in files: + if self.should_include(fn, root): + fn = os.path.join(root, fn) + url = urlunparse(('file', '', + pathname2url(os.path.abspath(fn)), + '', '', '')) + info = self.convert_url_to_download_info(url, name) + if info: + self._update_version_data(result, info) + if not self.recursive: + break + return result + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + for root, dirs, files in os.walk(self.base_dir): + for fn in files: + if self.should_include(fn, root): + fn = os.path.join(root, fn) + url = urlunparse(('file', '', + pathname2url(os.path.abspath(fn)), + '', '', '')) + info = self.convert_url_to_download_info(url, None) + if info: + result.add(info['name']) + if not self.recursive: + break + return result + +class JSONLocator(Locator): + """ + This locator uses special extended metadata (not available on PyPI) and is + the basis of performant dependency resolution in distlib. Other locators + require archive downloads before dependencies can be determined! As you + might imagine, that can be slow. + """ + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Not available from this locator') + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + data = get_project_data(name) + if data: + for info in data.get('files', []): + if info['ptype'] != 'sdist' or info['pyversion'] != 'source': + continue + # We don't store summary in project metadata as it makes + # the data bigger for no benefit during dependency + # resolution + dist = make_dist(data['name'], info['version'], + summary=data.get('summary', + 'Placeholder for summary'), + scheme=self.scheme) + md = dist.metadata + md.source_url = info['url'] + # TODO SHA256 digest + if 'digest' in info and info['digest']: + dist.digest = ('md5', info['digest']) + md.dependencies = info.get('requirements', {}) + dist.exports = info.get('exports', {}) + result[dist.version] = dist + result['urls'].setdefault(dist.version, set()).add(info['url']) + return result + +class DistPathLocator(Locator): + """ + This locator finds installed distributions in a path. It can be useful for + adding to an :class:`AggregatingLocator`. + """ + def __init__(self, distpath, **kwargs): + """ + Initialise an instance. + + :param distpath: A :class:`DistributionPath` instance to search. + """ + super(DistPathLocator, self).__init__(**kwargs) + assert isinstance(distpath, DistributionPath) + self.distpath = distpath + + def _get_project(self, name): + dist = self.distpath.get_distribution(name) + if dist is None: + result = {'urls': {}, 'digests': {}} + else: + result = { + dist.version: dist, + 'urls': {dist.version: set([dist.source_url])}, + 'digests': {dist.version: set([None])} + } + return result + + +class AggregatingLocator(Locator): + """ + This class allows you to chain and/or merge a list of locators. + """ + def __init__(self, *locators, **kwargs): + """ + Initialise an instance. + + :param locators: The list of locators to search. + :param kwargs: Passed to the superclass constructor, + except for: + * merge - if False (the default), the first successful + search from any of the locators is returned. If True, + the results from all locators are merged (this can be + slow). + """ + self.merge = kwargs.pop('merge', False) + self.locators = locators + super(AggregatingLocator, self).__init__(**kwargs) + + def clear_cache(self): + super(AggregatingLocator, self).clear_cache() + for locator in self.locators: + locator.clear_cache() + + def _set_scheme(self, value): + self._scheme = value + for locator in self.locators: + locator.scheme = value + + scheme = property(Locator.scheme.fget, _set_scheme) + + def _get_project(self, name): + result = {} + for locator in self.locators: + d = locator.get_project(name) + if d: + if self.merge: + files = result.get('urls', {}) + digests = result.get('digests', {}) + # next line could overwrite result['urls'], result['digests'] + result.update(d) + df = result.get('urls') + if files and df: + for k, v in files.items(): + if k in df: + df[k] |= v + else: + df[k] = v + dd = result.get('digests') + if digests and dd: + dd.update(digests) + else: + # See issue #18. If any dists are found and we're looking + # for specific constraints, we only return something if + # a match is found. For example, if a DirectoryLocator + # returns just foo (1.0) while we're looking for + # foo (>= 2.0), we'll pretend there was nothing there so + # that subsequent locators can be queried. Otherwise we + # would just return foo (1.0) which would then lead to a + # failure to find foo (>= 2.0), because other locators + # weren't searched. Note that this only matters when + # merge=False. + if self.matcher is None: + found = True + else: + found = False + for k in d: + if self.matcher.match(k): + found = True + break + if found: + result = d + break + return result + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + for locator in self.locators: + try: + result |= locator.get_distribution_names() + except NotImplementedError: + pass + return result + + +# We use a legacy scheme simply because most of the dists on PyPI use legacy +# versions which don't conform to PEP 426 / PEP 440. +default_locator = AggregatingLocator( + JSONLocator(), + SimpleScrapingLocator('https://pypi.python.org/simple/', + timeout=3.0), + scheme='legacy') + +locate = default_locator.locate + +NAME_VERSION_RE = re.compile(r'(?P<name>[\w-]+)\s*' + r'\(\s*(==\s*)?(?P<ver>[^)]+)\)$') + +class DependencyFinder(object): + """ + Locate dependencies for distributions. + """ + + def __init__(self, locator=None): + """ + Initialise an instance, using the specified locator + to locate distributions. + """ + self.locator = locator or default_locator + self.scheme = get_scheme(self.locator.scheme) + + def add_distribution(self, dist): + """ + Add a distribution to the finder. This will update internal information + about who provides what. + :param dist: The distribution to add. + """ + logger.debug('adding distribution %s', dist) + name = dist.key + self.dists_by_name[name] = dist + self.dists[(name, dist.version)] = dist + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Add to provided: %s, %s, %s', name, version, dist) + self.provided.setdefault(name, set()).add((version, dist)) + + def remove_distribution(self, dist): + """ + Remove a distribution from the finder. This will update internal + information about who provides what. + :param dist: The distribution to remove. + """ + logger.debug('removing distribution %s', dist) + name = dist.key + del self.dists_by_name[name] + del self.dists[(name, dist.version)] + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Remove from provided: %s, %s, %s', name, version, dist) + s = self.provided[name] + s.remove((version, dist)) + if not s: + del self.provided[name] + + def get_matcher(self, reqt): + """ + Get a version matcher for a requirement. + :param reqt: The requirement + :type reqt: str + :return: A version matcher (an instance of + :class:`distlib.version.Matcher`). + """ + try: + matcher = self.scheme.matcher(reqt) + except UnsupportedVersionError: # pragma: no cover + # XXX compat-mode if cannot read the version + name = reqt.split()[0] + matcher = self.scheme.matcher(name) + return matcher + + def find_providers(self, reqt): + """ + Find the distributions which can fulfill a requirement. + + :param reqt: The requirement. + :type reqt: str + :return: A set of distribution which can fulfill the requirement. + """ + matcher = self.get_matcher(reqt) + name = matcher.key # case-insensitive + result = set() + provided = self.provided + if name in provided: + for version, provider in provided[name]: + try: + match = matcher.match(version) + except UnsupportedVersionError: + match = False + + if match: + result.add(provider) + break + return result + + def try_to_replace(self, provider, other, problems): + """ + Attempt to replace one provider with another. This is typically used + when resolving dependencies from multiple sources, e.g. A requires + (B >= 1.0) while C requires (B >= 1.1). + + For successful replacement, ``provider`` must meet all the requirements + which ``other`` fulfills. + + :param provider: The provider we are trying to replace with. + :param other: The provider we're trying to replace. + :param problems: If False is returned, this will contain what + problems prevented replacement. This is currently + a tuple of the literal string 'cantreplace', + ``provider``, ``other`` and the set of requirements + that ``provider`` couldn't fulfill. + :return: True if we can replace ``other`` with ``provider``, else + False. + """ + rlist = self.reqts[other] + unmatched = set() + for s in rlist: + matcher = self.get_matcher(s) + if not matcher.match(provider.version): + unmatched.add(s) + if unmatched: + # can't replace other with provider + problems.add(('cantreplace', provider, other, + frozenset(unmatched))) + result = False + else: + # can replace other with provider + self.remove_distribution(other) + del self.reqts[other] + for s in rlist: + self.reqts.setdefault(provider, set()).add(s) + self.add_distribution(provider) + result = True + return result + + def find(self, requirement, meta_extras=None, prereleases=False): + """ + Find a distribution and all distributions it depends on. + + :param requirement: The requirement specifying the distribution to + find, or a Distribution instance. + :param meta_extras: A list of meta extras such as :test:, :build: and + so on. + :param prereleases: If ``True``, allow pre-release versions to be + returned - otherwise, don't return prereleases + unless they're all that's available. + + Return a set of :class:`Distribution` instances and a set of + problems. + + The distributions returned should be such that they have the + :attr:`required` attribute set to ``True`` if they were + from the ``requirement`` passed to ``find()``, and they have the + :attr:`build_time_dependency` attribute set to ``True`` unless they + are post-installation dependencies of the ``requirement``. + + The problems should be a tuple consisting of the string + ``'unsatisfied'`` and the requirement which couldn't be satisfied + by any distribution known to the locator. + """ + + self.provided = {} + self.dists = {} + self.dists_by_name = {} + self.reqts = {} + + meta_extras = set(meta_extras or []) + if ':*:' in meta_extras: + meta_extras.remove(':*:') + # :meta: and :run: are implicitly included + meta_extras |= set([':test:', ':build:', ':dev:']) + + if isinstance(requirement, Distribution): + dist = odist = requirement + logger.debug('passed %s as requirement', odist) + else: + dist = odist = self.locator.locate(requirement, + prereleases=prereleases) + if dist is None: + raise DistlibException('Unable to locate %r' % requirement) + logger.debug('located %s', odist) + dist.requested = True + problems = set() + todo = set([dist]) + install_dists = set([odist]) + while todo: + dist = todo.pop() + name = dist.key # case-insensitive + if name not in self.dists_by_name: + self.add_distribution(dist) + else: + #import pdb; pdb.set_trace() + other = self.dists_by_name[name] + if other != dist: + self.try_to_replace(dist, other, problems) + + ireqts = dist.run_requires | dist.meta_requires + sreqts = dist.build_requires + ereqts = set() + if meta_extras and dist in install_dists: + for key in ('test', 'build', 'dev'): + e = ':%s:' % key + if e in meta_extras: + ereqts |= getattr(dist, '%s_requires' % key) + all_reqts = ireqts | sreqts | ereqts + for r in all_reqts: + providers = self.find_providers(r) + if not providers: + logger.debug('No providers found for %r', r) + provider = self.locator.locate(r, prereleases=prereleases) + # If no provider is found and we didn't consider + # prereleases, consider them now. + if provider is None and not prereleases: + provider = self.locator.locate(r, prereleases=True) + if provider is None: + logger.debug('Cannot satisfy %r', r) + problems.add(('unsatisfied', r)) + else: + n, v = provider.key, provider.version + if (n, v) not in self.dists: + todo.add(provider) + providers.add(provider) + if r in ireqts and dist in install_dists: + install_dists.add(provider) + logger.debug('Adding %s to install_dists', + provider.name_and_version) + for p in providers: + name = p.key + if name not in self.dists_by_name: + self.reqts.setdefault(p, set()).add(r) + else: + other = self.dists_by_name[name] + if other != p: + # see if other can be replaced by p + self.try_to_replace(p, other, problems) + + dists = set(self.dists.values()) + for dist in dists: + dist.build_time_dependency = dist not in install_dists + if dist.build_time_dependency: + logger.debug('%s is a build-time dependency only.', + dist.name_and_version) + logger.debug('find done for %s', odist) + return dists, problems diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/manifest.py b/venv/Lib/site-packages/pip/_vendor/distlib/manifest.py new file mode 100644 index 0000000..ca0fe44 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/manifest.py @@ -0,0 +1,393 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +""" +Class representing the list of files in a distribution. + +Equivalent to distutils.filelist, but fixes some problems. +""" +import fnmatch +import logging +import os +import re +import sys + +from . import DistlibException +from .compat import fsdecode +from .util import convert_path + + +__all__ = ['Manifest'] + +logger = logging.getLogger(__name__) + +# a \ followed by some spaces + EOL +_COLLAPSE_PATTERN = re.compile('\\\\w*\n', re.M) +_COMMENTED_LINE = re.compile('#.*?(?=\n)|\n(?=$)', re.M | re.S) + +# +# Due to the different results returned by fnmatch.translate, we need +# to do slightly different processing for Python 2.7 and 3.2 ... this needed +# to be brought in for Python 3.6 onwards. +# +_PYTHON_VERSION = sys.version_info[:2] + +class Manifest(object): + """A list of files built by on exploring the filesystem and filtered by + applying various patterns to what we find there. + """ + + def __init__(self, base=None): + """ + Initialise an instance. + + :param base: The base directory to explore under. + """ + self.base = os.path.abspath(os.path.normpath(base or os.getcwd())) + self.prefix = self.base + os.sep + self.allfiles = None + self.files = set() + + # + # Public API + # + + def findall(self): + """Find all files under the base and set ``allfiles`` to the absolute + pathnames of files found. + """ + from stat import S_ISREG, S_ISDIR, S_ISLNK + + self.allfiles = allfiles = [] + root = self.base + stack = [root] + pop = stack.pop + push = stack.append + + while stack: + root = pop() + names = os.listdir(root) + + for name in names: + fullname = os.path.join(root, name) + + # Avoid excess stat calls -- just one will do, thank you! + stat = os.stat(fullname) + mode = stat.st_mode + if S_ISREG(mode): + allfiles.append(fsdecode(fullname)) + elif S_ISDIR(mode) and not S_ISLNK(mode): + push(fullname) + + def add(self, item): + """ + Add a file to the manifest. + + :param item: The pathname to add. This can be relative to the base. + """ + if not item.startswith(self.prefix): + item = os.path.join(self.base, item) + self.files.add(os.path.normpath(item)) + + def add_many(self, items): + """ + Add a list of files to the manifest. + + :param items: The pathnames to add. These can be relative to the base. + """ + for item in items: + self.add(item) + + def sorted(self, wantdirs=False): + """ + Return sorted files in directory order + """ + + def add_dir(dirs, d): + dirs.add(d) + logger.debug('add_dir added %s', d) + if d != self.base: + parent, _ = os.path.split(d) + assert parent not in ('', '/') + add_dir(dirs, parent) + + result = set(self.files) # make a copy! + if wantdirs: + dirs = set() + for f in result: + add_dir(dirs, os.path.dirname(f)) + result |= dirs + return [os.path.join(*path_tuple) for path_tuple in + sorted(os.path.split(path) for path in result)] + + def clear(self): + """Clear all collected files.""" + self.files = set() + self.allfiles = [] + + def process_directive(self, directive): + """ + Process a directive which either adds some files from ``allfiles`` to + ``files``, or removes some files from ``files``. + + :param directive: The directive to process. This should be in a format + compatible with distutils ``MANIFEST.in`` files: + + http://docs.python.org/distutils/sourcedist.html#commands + """ + # Parse the line: split it up, make sure the right number of words + # is there, and return the relevant words. 'action' is always + # defined: it's the first word of the line. Which of the other + # three are defined depends on the action; it'll be either + # patterns, (dir and patterns), or (dirpattern). + action, patterns, thedir, dirpattern = self._parse_directive(directive) + + # OK, now we know that the action is valid and we have the + # right number of words on the line for that action -- so we + # can proceed with minimal error-checking. + if action == 'include': + for pattern in patterns: + if not self._include_pattern(pattern, anchor=True): + logger.warning('no files found matching %r', pattern) + + elif action == 'exclude': + for pattern in patterns: + found = self._exclude_pattern(pattern, anchor=True) + #if not found: + # logger.warning('no previously-included files ' + # 'found matching %r', pattern) + + elif action == 'global-include': + for pattern in patterns: + if not self._include_pattern(pattern, anchor=False): + logger.warning('no files found matching %r ' + 'anywhere in distribution', pattern) + + elif action == 'global-exclude': + for pattern in patterns: + found = self._exclude_pattern(pattern, anchor=False) + #if not found: + # logger.warning('no previously-included files ' + # 'matching %r found anywhere in ' + # 'distribution', pattern) + + elif action == 'recursive-include': + for pattern in patterns: + if not self._include_pattern(pattern, prefix=thedir): + logger.warning('no files found matching %r ' + 'under directory %r', pattern, thedir) + + elif action == 'recursive-exclude': + for pattern in patterns: + found = self._exclude_pattern(pattern, prefix=thedir) + #if not found: + # logger.warning('no previously-included files ' + # 'matching %r found under directory %r', + # pattern, thedir) + + elif action == 'graft': + if not self._include_pattern(None, prefix=dirpattern): + logger.warning('no directories found matching %r', + dirpattern) + + elif action == 'prune': + if not self._exclude_pattern(None, prefix=dirpattern): + logger.warning('no previously-included directories found ' + 'matching %r', dirpattern) + else: # pragma: no cover + # This should never happen, as it should be caught in + # _parse_template_line + raise DistlibException( + 'invalid action %r' % action) + + # + # Private API + # + + def _parse_directive(self, directive): + """ + Validate a directive. + :param directive: The directive to validate. + :return: A tuple of action, patterns, thedir, dir_patterns + """ + words = directive.split() + if len(words) == 1 and words[0] not in ('include', 'exclude', + 'global-include', + 'global-exclude', + 'recursive-include', + 'recursive-exclude', + 'graft', 'prune'): + # no action given, let's use the default 'include' + words.insert(0, 'include') + + action = words[0] + patterns = thedir = dir_pattern = None + + if action in ('include', 'exclude', + 'global-include', 'global-exclude'): + if len(words) < 2: + raise DistlibException( + '%r expects <pattern1> <pattern2> ...' % action) + + patterns = [convert_path(word) for word in words[1:]] + + elif action in ('recursive-include', 'recursive-exclude'): + if len(words) < 3: + raise DistlibException( + '%r expects <dir> <pattern1> <pattern2> ...' % action) + + thedir = convert_path(words[1]) + patterns = [convert_path(word) for word in words[2:]] + + elif action in ('graft', 'prune'): + if len(words) != 2: + raise DistlibException( + '%r expects a single <dir_pattern>' % action) + + dir_pattern = convert_path(words[1]) + + else: + raise DistlibException('unknown action %r' % action) + + return action, patterns, thedir, dir_pattern + + def _include_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Select strings (presumably filenames) from 'self.files' that + match 'pattern', a Unix-style wildcard (glob) pattern. + + Patterns are not quite the same as implemented by the 'fnmatch' + module: '*' and '?' match non-special characters, where "special" + is platform-dependent: slash on Unix; colon, slash, and backslash on + DOS/Windows; and colon on Mac OS. + + If 'anchor' is true (the default), then the pattern match is more + stringent: "*.py" will match "foo.py" but not "foo/bar.py". If + 'anchor' is false, both of these will match. + + If 'prefix' is supplied, then only filenames starting with 'prefix' + (itself a pattern) and ending with 'pattern', with anything in between + them, will match. 'anchor' is ignored in this case. + + If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and + 'pattern' is assumed to be either a string containing a regex or a + regex object -- no translation is done, the regex is just compiled + and used as-is. + + Selected strings will be added to self.files. + + Return True if files are found. + """ + # XXX docstring lying about what the special chars are? + found = False + pattern_re = self._translate_pattern(pattern, anchor, prefix, is_regex) + + # delayed loading of allfiles list + if self.allfiles is None: + self.findall() + + for name in self.allfiles: + if pattern_re.search(name): + self.files.add(name) + found = True + return found + + def _exclude_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Remove strings (presumably filenames) from 'files' that match + 'pattern'. + + Other parameters are the same as for 'include_pattern()', above. + The list 'self.files' is modified in place. Return True if files are + found. + + This API is public to allow e.g. exclusion of SCM subdirs, e.g. when + packaging source distributions + """ + found = False + pattern_re = self._translate_pattern(pattern, anchor, prefix, is_regex) + for f in list(self.files): + if pattern_re.search(f): + self.files.remove(f) + found = True + return found + + def _translate_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Translate a shell-like wildcard pattern to a compiled regular + expression. + + Return the compiled regex. If 'is_regex' true, + then 'pattern' is directly compiled to a regex (if it's a string) + or just returned as-is (assumes it's a regex object). + """ + if is_regex: + if isinstance(pattern, str): + return re.compile(pattern) + else: + return pattern + + if _PYTHON_VERSION > (3, 2): + # ditch start and end characters + start, _, end = self._glob_to_re('_').partition('_') + + if pattern: + pattern_re = self._glob_to_re(pattern) + if _PYTHON_VERSION > (3, 2): + assert pattern_re.startswith(start) and pattern_re.endswith(end) + else: + pattern_re = '' + + base = re.escape(os.path.join(self.base, '')) + if prefix is not None: + # ditch end of pattern character + if _PYTHON_VERSION <= (3, 2): + empty_pattern = self._glob_to_re('') + prefix_re = self._glob_to_re(prefix)[:-len(empty_pattern)] + else: + prefix_re = self._glob_to_re(prefix) + assert prefix_re.startswith(start) and prefix_re.endswith(end) + prefix_re = prefix_re[len(start): len(prefix_re) - len(end)] + sep = os.sep + if os.sep == '\\': + sep = r'\\' + if _PYTHON_VERSION <= (3, 2): + pattern_re = '^' + base + sep.join((prefix_re, + '.*' + pattern_re)) + else: + pattern_re = pattern_re[len(start): len(pattern_re) - len(end)] + pattern_re = r'%s%s%s%s.*%s%s' % (start, base, prefix_re, sep, + pattern_re, end) + else: # no prefix -- respect anchor flag + if anchor: + if _PYTHON_VERSION <= (3, 2): + pattern_re = '^' + base + pattern_re + else: + pattern_re = r'%s%s%s' % (start, base, pattern_re[len(start):]) + + return re.compile(pattern_re) + + def _glob_to_re(self, pattern): + """Translate a shell-like glob pattern to a regular expression. + + Return a string containing the regex. Differs from + 'fnmatch.translate()' in that '*' does not match "special characters" + (which are platform-specific). + """ + pattern_re = fnmatch.translate(pattern) + + # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which + # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, + # and by extension they shouldn't match such "special characters" under + # any OS. So change all non-escaped dots in the RE to match any + # character except the special characters (currently: just os.sep). + sep = os.sep + if os.sep == '\\': + # we're using a regex to manipulate a regex, so we need + # to escape the backslash twice + sep = r'\\\\' + escaped = r'\1[^%s]' % sep + pattern_re = re.sub(r'((?<!\\)(\\\\)*)\.', escaped, pattern_re) + return pattern_re diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/markers.py b/venv/Lib/site-packages/pip/_vendor/distlib/markers.py new file mode 100644 index 0000000..ee1f3e2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/markers.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2017 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +""" +Parser for the environment markers micro-language defined in PEP 508. +""" + +# Note: In PEP 345, the micro-language was Python compatible, so the ast +# module could be used to parse it. However, PEP 508 introduced operators such +# as ~= and === which aren't in Python, necessitating a different approach. + +import os +import sys +import platform +import re + +from .compat import python_implementation, urlparse, string_types +from .util import in_venv, parse_marker + +__all__ = ['interpret'] + +def _is_literal(o): + if not isinstance(o, string_types) or not o: + return False + return o[0] in '\'"' + +class Evaluator(object): + """ + This class is used to evaluate marker expessions. + """ + + operations = { + '==': lambda x, y: x == y, + '===': lambda x, y: x == y, + '~=': lambda x, y: x == y or x > y, + '!=': lambda x, y: x != y, + '<': lambda x, y: x < y, + '<=': lambda x, y: x == y or x < y, + '>': lambda x, y: x > y, + '>=': lambda x, y: x == y or x > y, + 'and': lambda x, y: x and y, + 'or': lambda x, y: x or y, + 'in': lambda x, y: x in y, + 'not in': lambda x, y: x not in y, + } + + def evaluate(self, expr, context): + """ + Evaluate a marker expression returned by the :func:`parse_requirement` + function in the specified context. + """ + if isinstance(expr, string_types): + if expr[0] in '\'"': + result = expr[1:-1] + else: + if expr not in context: + raise SyntaxError('unknown variable: %s' % expr) + result = context[expr] + else: + assert isinstance(expr, dict) + op = expr['op'] + if op not in self.operations: + raise NotImplementedError('op not implemented: %s' % op) + elhs = expr['lhs'] + erhs = expr['rhs'] + if _is_literal(expr['lhs']) and _is_literal(expr['rhs']): + raise SyntaxError('invalid comparison: %s %s %s' % (elhs, op, erhs)) + + lhs = self.evaluate(elhs, context) + rhs = self.evaluate(erhs, context) + result = self.operations[op](lhs, rhs) + return result + +def default_context(): + def format_full_version(info): + version = '%s.%s.%s' % (info.major, info.minor, info.micro) + kind = info.releaselevel + if kind != 'final': + version += kind[0] + str(info.serial) + return version + + if hasattr(sys, 'implementation'): + implementation_version = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + else: + implementation_version = '0' + implementation_name = '' + + result = { + 'implementation_name': implementation_name, + 'implementation_version': implementation_version, + 'os_name': os.name, + 'platform_machine': platform.machine(), + 'platform_python_implementation': platform.python_implementation(), + 'platform_release': platform.release(), + 'platform_system': platform.system(), + 'platform_version': platform.version(), + 'platform_in_venv': str(in_venv()), + 'python_full_version': platform.python_version(), + 'python_version': platform.python_version()[:3], + 'sys_platform': sys.platform, + } + return result + +DEFAULT_CONTEXT = default_context() +del default_context + +evaluator = Evaluator() + +def interpret(marker, execution_context=None): + """ + Interpret a marker and return a result depending on environment. + + :param marker: The marker to interpret. + :type marker: str + :param execution_context: The context used for name lookup. + :type execution_context: mapping + """ + try: + expr, rest = parse_marker(marker) + except Exception as e: + raise SyntaxError('Unable to interpret marker syntax: %s: %s' % (marker, e)) + if rest and rest[0] != '#': + raise SyntaxError('unexpected trailing data in marker: %s: %s' % (marker, rest)) + context = dict(DEFAULT_CONTEXT) + if execution_context: + context.update(execution_context) + return evaluator.evaluate(expr, context) diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/metadata.py b/venv/Lib/site-packages/pip/_vendor/distlib/metadata.py new file mode 100644 index 0000000..6d6470f --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/metadata.py @@ -0,0 +1,1091 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Implementation of the Metadata for Python packages PEPs. + +Supports all metadata formats (1.0, 1.1, 1.2, and 2.0 experimental). +""" +from __future__ import unicode_literals + +import codecs +from email import message_from_file +import json +import logging +import re + + +from . import DistlibException, __version__ +from .compat import StringIO, string_types, text_type +from .markers import interpret +from .util import extract_by_key, get_extras +from .version import get_scheme, PEP440_VERSION_RE + +logger = logging.getLogger(__name__) + + +class MetadataMissingError(DistlibException): + """A required metadata is missing""" + + +class MetadataConflictError(DistlibException): + """Attempt to read or write metadata fields that are conflictual.""" + + +class MetadataUnrecognizedVersionError(DistlibException): + """Unknown metadata version number.""" + + +class MetadataInvalidError(DistlibException): + """A metadata value is invalid""" + +# public API of this module +__all__ = ['Metadata', 'PKG_INFO_ENCODING', 'PKG_INFO_PREFERRED_VERSION'] + +# Encoding used for the PKG-INFO files +PKG_INFO_ENCODING = 'utf-8' + +# preferred version. Hopefully will be changed +# to 1.2 once PEP 345 is supported everywhere +PKG_INFO_PREFERRED_VERSION = '1.1' + +_LINE_PREFIX_1_2 = re.compile('\n \\|') +_LINE_PREFIX_PRE_1_2 = re.compile('\n ') +_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'License') + +_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'License', 'Classifier', 'Download-URL', 'Obsoletes', + 'Provides', 'Requires') + +_314_MARKERS = ('Obsoletes', 'Provides', 'Requires', 'Classifier', + 'Download-URL') + +_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'Maintainer', 'Maintainer-email', 'License', + 'Classifier', 'Download-URL', 'Obsoletes-Dist', + 'Project-URL', 'Provides-Dist', 'Requires-Dist', + 'Requires-Python', 'Requires-External') + +_345_MARKERS = ('Provides-Dist', 'Requires-Dist', 'Requires-Python', + 'Obsoletes-Dist', 'Requires-External', 'Maintainer', + 'Maintainer-email', 'Project-URL') + +_426_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'Maintainer', 'Maintainer-email', 'License', + 'Classifier', 'Download-URL', 'Obsoletes-Dist', + 'Project-URL', 'Provides-Dist', 'Requires-Dist', + 'Requires-Python', 'Requires-External', 'Private-Version', + 'Obsoleted-By', 'Setup-Requires-Dist', 'Extension', + 'Provides-Extra') + +_426_MARKERS = ('Private-Version', 'Provides-Extra', 'Obsoleted-By', + 'Setup-Requires-Dist', 'Extension') + +_566_FIELDS = _426_FIELDS + ('Description-Content-Type',) + +_566_MARKERS = ('Description-Content-Type',) + +_ALL_FIELDS = set() +_ALL_FIELDS.update(_241_FIELDS) +_ALL_FIELDS.update(_314_FIELDS) +_ALL_FIELDS.update(_345_FIELDS) +_ALL_FIELDS.update(_426_FIELDS) +_ALL_FIELDS.update(_566_FIELDS) + +EXTRA_RE = re.compile(r'''extra\s*==\s*("([^"]+)"|'([^']+)')''') + + +def _version2fieldlist(version): + if version == '1.0': + return _241_FIELDS + elif version == '1.1': + return _314_FIELDS + elif version == '1.2': + return _345_FIELDS + elif version in ('1.3', '2.1'): + return _345_FIELDS + _566_FIELDS + elif version == '2.0': + return _426_FIELDS + raise MetadataUnrecognizedVersionError(version) + + +def _best_version(fields): + """Detect the best version depending on the fields used.""" + def _has_marker(keys, markers): + for marker in markers: + if marker in keys: + return True + return False + + keys = [] + for key, value in fields.items(): + if value in ([], 'UNKNOWN', None): + continue + keys.append(key) + + possible_versions = ['1.0', '1.1', '1.2', '1.3', '2.0', '2.1'] + + # first let's try to see if a field is not part of one of the version + for key in keys: + if key not in _241_FIELDS and '1.0' in possible_versions: + possible_versions.remove('1.0') + logger.debug('Removed 1.0 due to %s', key) + if key not in _314_FIELDS and '1.1' in possible_versions: + possible_versions.remove('1.1') + logger.debug('Removed 1.1 due to %s', key) + if key not in _345_FIELDS and '1.2' in possible_versions: + possible_versions.remove('1.2') + logger.debug('Removed 1.2 due to %s', key) + if key not in _566_FIELDS and '1.3' in possible_versions: + possible_versions.remove('1.3') + logger.debug('Removed 1.3 due to %s', key) + if key not in _566_FIELDS and '2.1' in possible_versions: + if key != 'Description': # In 2.1, description allowed after headers + possible_versions.remove('2.1') + logger.debug('Removed 2.1 due to %s', key) + if key not in _426_FIELDS and '2.0' in possible_versions: + possible_versions.remove('2.0') + logger.debug('Removed 2.0 due to %s', key) + + # possible_version contains qualified versions + if len(possible_versions) == 1: + return possible_versions[0] # found ! + elif len(possible_versions) == 0: + logger.debug('Out of options - unknown metadata set: %s', fields) + raise MetadataConflictError('Unknown metadata set') + + # let's see if one unique marker is found + is_1_1 = '1.1' in possible_versions and _has_marker(keys, _314_MARKERS) + is_1_2 = '1.2' in possible_versions and _has_marker(keys, _345_MARKERS) + is_2_1 = '2.1' in possible_versions and _has_marker(keys, _566_MARKERS) + is_2_0 = '2.0' in possible_versions and _has_marker(keys, _426_MARKERS) + if int(is_1_1) + int(is_1_2) + int(is_2_1) + int(is_2_0) > 1: + raise MetadataConflictError('You used incompatible 1.1/1.2/2.0/2.1 fields') + + # we have the choice, 1.0, or 1.2, or 2.0 + # - 1.0 has a broken Summary field but works with all tools + # - 1.1 is to avoid + # - 1.2 fixes Summary but has little adoption + # - 2.0 adds more features and is very new + if not is_1_1 and not is_1_2 and not is_2_1 and not is_2_0: + # we couldn't find any specific marker + if PKG_INFO_PREFERRED_VERSION in possible_versions: + return PKG_INFO_PREFERRED_VERSION + if is_1_1: + return '1.1' + if is_1_2: + return '1.2' + if is_2_1: + return '2.1' + + return '2.0' + +_ATTR2FIELD = { + 'metadata_version': 'Metadata-Version', + 'name': 'Name', + 'version': 'Version', + 'platform': 'Platform', + 'supported_platform': 'Supported-Platform', + 'summary': 'Summary', + 'description': 'Description', + 'keywords': 'Keywords', + 'home_page': 'Home-page', + 'author': 'Author', + 'author_email': 'Author-email', + 'maintainer': 'Maintainer', + 'maintainer_email': 'Maintainer-email', + 'license': 'License', + 'classifier': 'Classifier', + 'download_url': 'Download-URL', + 'obsoletes_dist': 'Obsoletes-Dist', + 'provides_dist': 'Provides-Dist', + 'requires_dist': 'Requires-Dist', + 'setup_requires_dist': 'Setup-Requires-Dist', + 'requires_python': 'Requires-Python', + 'requires_external': 'Requires-External', + 'requires': 'Requires', + 'provides': 'Provides', + 'obsoletes': 'Obsoletes', + 'project_url': 'Project-URL', + 'private_version': 'Private-Version', + 'obsoleted_by': 'Obsoleted-By', + 'extension': 'Extension', + 'provides_extra': 'Provides-Extra', +} + +_PREDICATE_FIELDS = ('Requires-Dist', 'Obsoletes-Dist', 'Provides-Dist') +_VERSIONS_FIELDS = ('Requires-Python',) +_VERSION_FIELDS = ('Version',) +_LISTFIELDS = ('Platform', 'Classifier', 'Obsoletes', + 'Requires', 'Provides', 'Obsoletes-Dist', + 'Provides-Dist', 'Requires-Dist', 'Requires-External', + 'Project-URL', 'Supported-Platform', 'Setup-Requires-Dist', + 'Provides-Extra', 'Extension') +_LISTTUPLEFIELDS = ('Project-URL',) + +_ELEMENTSFIELD = ('Keywords',) + +_UNICODEFIELDS = ('Author', 'Maintainer', 'Summary', 'Description') + +_MISSING = object() + +_FILESAFE = re.compile('[^A-Za-z0-9.]+') + + +def _get_name_and_version(name, version, for_filename=False): + """Return the distribution name with version. + + If for_filename is true, return a filename-escaped form.""" + if for_filename: + # For both name and version any runs of non-alphanumeric or '.' + # characters are replaced with a single '-'. Additionally any + # spaces in the version string become '.' + name = _FILESAFE.sub('-', name) + version = _FILESAFE.sub('-', version.replace(' ', '.')) + return '%s-%s' % (name, version) + + +class LegacyMetadata(object): + """The legacy metadata of a release. + + Supports versions 1.0, 1.1 and 1.2 (auto-detected). You can + instantiate the class with one of these arguments (or none): + - *path*, the path to a metadata file + - *fileobj* give a file-like object with metadata as content + - *mapping* is a dict-like object + - *scheme* is a version scheme name + """ + # TODO document the mapping API and UNKNOWN default key + + def __init__(self, path=None, fileobj=None, mapping=None, + scheme='default'): + if [path, fileobj, mapping].count(None) < 2: + raise TypeError('path, fileobj and mapping are exclusive') + self._fields = {} + self.requires_files = [] + self._dependencies = None + self.scheme = scheme + if path is not None: + self.read(path) + elif fileobj is not None: + self.read_file(fileobj) + elif mapping is not None: + self.update(mapping) + self.set_metadata_version() + + def set_metadata_version(self): + self._fields['Metadata-Version'] = _best_version(self._fields) + + def _write_field(self, fileobj, name, value): + fileobj.write('%s: %s\n' % (name, value)) + + def __getitem__(self, name): + return self.get(name) + + def __setitem__(self, name, value): + return self.set(name, value) + + def __delitem__(self, name): + field_name = self._convert_name(name) + try: + del self._fields[field_name] + except KeyError: + raise KeyError(name) + + def __contains__(self, name): + return (name in self._fields or + self._convert_name(name) in self._fields) + + def _convert_name(self, name): + if name in _ALL_FIELDS: + return name + name = name.replace('-', '_').lower() + return _ATTR2FIELD.get(name, name) + + def _default_value(self, name): + if name in _LISTFIELDS or name in _ELEMENTSFIELD: + return [] + return 'UNKNOWN' + + def _remove_line_prefix(self, value): + if self.metadata_version in ('1.0', '1.1'): + return _LINE_PREFIX_PRE_1_2.sub('\n', value) + else: + return _LINE_PREFIX_1_2.sub('\n', value) + + def __getattr__(self, name): + if name in _ATTR2FIELD: + return self[name] + raise AttributeError(name) + + # + # Public API + # + +# dependencies = property(_get_dependencies, _set_dependencies) + + def get_fullname(self, filesafe=False): + """Return the distribution name with version. + + If filesafe is true, return a filename-escaped form.""" + return _get_name_and_version(self['Name'], self['Version'], filesafe) + + def is_field(self, name): + """return True if name is a valid metadata key""" + name = self._convert_name(name) + return name in _ALL_FIELDS + + def is_multi_field(self, name): + name = self._convert_name(name) + return name in _LISTFIELDS + + def read(self, filepath): + """Read the metadata values from a file path.""" + fp = codecs.open(filepath, 'r', encoding='utf-8') + try: + self.read_file(fp) + finally: + fp.close() + + def read_file(self, fileob): + """Read the metadata values from a file object.""" + msg = message_from_file(fileob) + self._fields['Metadata-Version'] = msg['metadata-version'] + + # When reading, get all the fields we can + for field in _ALL_FIELDS: + if field not in msg: + continue + if field in _LISTFIELDS: + # we can have multiple lines + values = msg.get_all(field) + if field in _LISTTUPLEFIELDS and values is not None: + values = [tuple(value.split(',')) for value in values] + self.set(field, values) + else: + # single line + value = msg[field] + if value is not None and value != 'UNKNOWN': + self.set(field, value) + logger.debug('Attempting to set metadata for %s', self) + self.set_metadata_version() + + def write(self, filepath, skip_unknown=False): + """Write the metadata fields to filepath.""" + fp = codecs.open(filepath, 'w', encoding='utf-8') + try: + self.write_file(fp, skip_unknown) + finally: + fp.close() + + def write_file(self, fileobject, skip_unknown=False): + """Write the PKG-INFO format data to a file object.""" + self.set_metadata_version() + + for field in _version2fieldlist(self['Metadata-Version']): + values = self.get(field) + if skip_unknown and values in ('UNKNOWN', [], ['UNKNOWN']): + continue + if field in _ELEMENTSFIELD: + self._write_field(fileobject, field, ','.join(values)) + continue + if field not in _LISTFIELDS: + if field == 'Description': + if self.metadata_version in ('1.0', '1.1'): + values = values.replace('\n', '\n ') + else: + values = values.replace('\n', '\n |') + values = [values] + + if field in _LISTTUPLEFIELDS: + values = [','.join(value) for value in values] + + for value in values: + self._write_field(fileobject, field, value) + + def update(self, other=None, **kwargs): + """Set metadata values from the given iterable `other` and kwargs. + + Behavior is like `dict.update`: If `other` has a ``keys`` method, + they are looped over and ``self[key]`` is assigned ``other[key]``. + Else, ``other`` is an iterable of ``(key, value)`` iterables. + + Keys that don't match a metadata field or that have an empty value are + dropped. + """ + def _set(key, value): + if key in _ATTR2FIELD and value: + self.set(self._convert_name(key), value) + + if not other: + # other is None or empty container + pass + elif hasattr(other, 'keys'): + for k in other.keys(): + _set(k, other[k]) + else: + for k, v in other: + _set(k, v) + + if kwargs: + for k, v in kwargs.items(): + _set(k, v) + + def set(self, name, value): + """Control then set a metadata field.""" + name = self._convert_name(name) + + if ((name in _ELEMENTSFIELD or name == 'Platform') and + not isinstance(value, (list, tuple))): + if isinstance(value, string_types): + value = [v.strip() for v in value.split(',')] + else: + value = [] + elif (name in _LISTFIELDS and + not isinstance(value, (list, tuple))): + if isinstance(value, string_types): + value = [value] + else: + value = [] + + if logger.isEnabledFor(logging.WARNING): + project_name = self['Name'] + + scheme = get_scheme(self.scheme) + if name in _PREDICATE_FIELDS and value is not None: + for v in value: + # check that the values are valid + if not scheme.is_valid_matcher(v.split(';')[0]): + logger.warning( + "'%s': '%s' is not valid (field '%s')", + project_name, v, name) + # FIXME this rejects UNKNOWN, is that right? + elif name in _VERSIONS_FIELDS and value is not None: + if not scheme.is_valid_constraint_list(value): + logger.warning("'%s': '%s' is not a valid version (field '%s')", + project_name, value, name) + elif name in _VERSION_FIELDS and value is not None: + if not scheme.is_valid_version(value): + logger.warning("'%s': '%s' is not a valid version (field '%s')", + project_name, value, name) + + if name in _UNICODEFIELDS: + if name == 'Description': + value = self._remove_line_prefix(value) + + self._fields[name] = value + + def get(self, name, default=_MISSING): + """Get a metadata field.""" + name = self._convert_name(name) + if name not in self._fields: + if default is _MISSING: + default = self._default_value(name) + return default + if name in _UNICODEFIELDS: + value = self._fields[name] + return value + elif name in _LISTFIELDS: + value = self._fields[name] + if value is None: + return [] + res = [] + for val in value: + if name not in _LISTTUPLEFIELDS: + res.append(val) + else: + # That's for Project-URL + res.append((val[0], val[1])) + return res + + elif name in _ELEMENTSFIELD: + value = self._fields[name] + if isinstance(value, string_types): + return value.split(',') + return self._fields[name] + + def check(self, strict=False): + """Check if the metadata is compliant. If strict is True then raise if + no Name or Version are provided""" + self.set_metadata_version() + + # XXX should check the versions (if the file was loaded) + missing, warnings = [], [] + + for attr in ('Name', 'Version'): # required by PEP 345 + if attr not in self: + missing.append(attr) + + if strict and missing != []: + msg = 'missing required metadata: %s' % ', '.join(missing) + raise MetadataMissingError(msg) + + for attr in ('Home-page', 'Author'): + if attr not in self: + missing.append(attr) + + # checking metadata 1.2 (XXX needs to check 1.1, 1.0) + if self['Metadata-Version'] != '1.2': + return missing, warnings + + scheme = get_scheme(self.scheme) + + def are_valid_constraints(value): + for v in value: + if not scheme.is_valid_matcher(v.split(';')[0]): + return False + return True + + for fields, controller in ((_PREDICATE_FIELDS, are_valid_constraints), + (_VERSIONS_FIELDS, + scheme.is_valid_constraint_list), + (_VERSION_FIELDS, + scheme.is_valid_version)): + for field in fields: + value = self.get(field, None) + if value is not None and not controller(value): + warnings.append("Wrong value for '%s': %s" % (field, value)) + + return missing, warnings + + def todict(self, skip_missing=False): + """Return fields as a dict. + + Field names will be converted to use the underscore-lowercase style + instead of hyphen-mixed case (i.e. home_page instead of Home-page). + """ + self.set_metadata_version() + + mapping_1_0 = ( + ('metadata_version', 'Metadata-Version'), + ('name', 'Name'), + ('version', 'Version'), + ('summary', 'Summary'), + ('home_page', 'Home-page'), + ('author', 'Author'), + ('author_email', 'Author-email'), + ('license', 'License'), + ('description', 'Description'), + ('keywords', 'Keywords'), + ('platform', 'Platform'), + ('classifiers', 'Classifier'), + ('download_url', 'Download-URL'), + ) + + data = {} + for key, field_name in mapping_1_0: + if not skip_missing or field_name in self._fields: + data[key] = self[field_name] + + if self['Metadata-Version'] == '1.2': + mapping_1_2 = ( + ('requires_dist', 'Requires-Dist'), + ('requires_python', 'Requires-Python'), + ('requires_external', 'Requires-External'), + ('provides_dist', 'Provides-Dist'), + ('obsoletes_dist', 'Obsoletes-Dist'), + ('project_url', 'Project-URL'), + ('maintainer', 'Maintainer'), + ('maintainer_email', 'Maintainer-email'), + ) + for key, field_name in mapping_1_2: + if not skip_missing or field_name in self._fields: + if key != 'project_url': + data[key] = self[field_name] + else: + data[key] = [','.join(u) for u in self[field_name]] + + elif self['Metadata-Version'] == '1.1': + mapping_1_1 = ( + ('provides', 'Provides'), + ('requires', 'Requires'), + ('obsoletes', 'Obsoletes'), + ) + for key, field_name in mapping_1_1: + if not skip_missing or field_name in self._fields: + data[key] = self[field_name] + + return data + + def add_requirements(self, requirements): + if self['Metadata-Version'] == '1.1': + # we can't have 1.1 metadata *and* Setuptools requires + for field in ('Obsoletes', 'Requires', 'Provides'): + if field in self: + del self[field] + self['Requires-Dist'] += requirements + + # Mapping API + # TODO could add iter* variants + + def keys(self): + return list(_version2fieldlist(self['Metadata-Version'])) + + def __iter__(self): + for key in self.keys(): + yield key + + def values(self): + return [self[key] for key in self.keys()] + + def items(self): + return [(key, self[key]) for key in self.keys()] + + def __repr__(self): + return '<%s %s %s>' % (self.__class__.__name__, self.name, + self.version) + + +METADATA_FILENAME = 'pydist.json' +WHEEL_METADATA_FILENAME = 'metadata.json' + + +class Metadata(object): + """ + The metadata of a release. This implementation uses 2.0 (JSON) + metadata where possible. If not possible, it wraps a LegacyMetadata + instance which handles the key-value metadata format. + """ + + METADATA_VERSION_MATCHER = re.compile(r'^\d+(\.\d+)*$') + + NAME_MATCHER = re.compile('^[0-9A-Z]([0-9A-Z_.-]*[0-9A-Z])?$', re.I) + + VERSION_MATCHER = PEP440_VERSION_RE + + SUMMARY_MATCHER = re.compile('.{1,2047}') + + METADATA_VERSION = '2.0' + + GENERATOR = 'distlib (%s)' % __version__ + + MANDATORY_KEYS = { + 'name': (), + 'version': (), + 'summary': ('legacy',), + } + + INDEX_KEYS = ('name version license summary description author ' + 'author_email keywords platform home_page classifiers ' + 'download_url') + + DEPENDENCY_KEYS = ('extras run_requires test_requires build_requires ' + 'dev_requires provides meta_requires obsoleted_by ' + 'supports_environments') + + SYNTAX_VALIDATORS = { + 'metadata_version': (METADATA_VERSION_MATCHER, ()), + 'name': (NAME_MATCHER, ('legacy',)), + 'version': (VERSION_MATCHER, ('legacy',)), + 'summary': (SUMMARY_MATCHER, ('legacy',)), + } + + __slots__ = ('_legacy', '_data', 'scheme') + + def __init__(self, path=None, fileobj=None, mapping=None, + scheme='default'): + if [path, fileobj, mapping].count(None) < 2: + raise TypeError('path, fileobj and mapping are exclusive') + self._legacy = None + self._data = None + self.scheme = scheme + #import pdb; pdb.set_trace() + if mapping is not None: + try: + self._validate_mapping(mapping, scheme) + self._data = mapping + except MetadataUnrecognizedVersionError: + self._legacy = LegacyMetadata(mapping=mapping, scheme=scheme) + self.validate() + else: + data = None + if path: + with open(path, 'rb') as f: + data = f.read() + elif fileobj: + data = fileobj.read() + if data is None: + # Initialised with no args - to be added + self._data = { + 'metadata_version': self.METADATA_VERSION, + 'generator': self.GENERATOR, + } + else: + if not isinstance(data, text_type): + data = data.decode('utf-8') + try: + self._data = json.loads(data) + self._validate_mapping(self._data, scheme) + except ValueError: + # Note: MetadataUnrecognizedVersionError does not + # inherit from ValueError (it's a DistlibException, + # which should not inherit from ValueError). + # The ValueError comes from the json.load - if that + # succeeds and we get a validation error, we want + # that to propagate + self._legacy = LegacyMetadata(fileobj=StringIO(data), + scheme=scheme) + self.validate() + + common_keys = set(('name', 'version', 'license', 'keywords', 'summary')) + + none_list = (None, list) + none_dict = (None, dict) + + mapped_keys = { + 'run_requires': ('Requires-Dist', list), + 'build_requires': ('Setup-Requires-Dist', list), + 'dev_requires': none_list, + 'test_requires': none_list, + 'meta_requires': none_list, + 'extras': ('Provides-Extra', list), + 'modules': none_list, + 'namespaces': none_list, + 'exports': none_dict, + 'commands': none_dict, + 'classifiers': ('Classifier', list), + 'source_url': ('Download-URL', None), + 'metadata_version': ('Metadata-Version', None), + } + + del none_list, none_dict + + def __getattribute__(self, key): + common = object.__getattribute__(self, 'common_keys') + mapped = object.__getattribute__(self, 'mapped_keys') + if key in mapped: + lk, maker = mapped[key] + if self._legacy: + if lk is None: + result = None if maker is None else maker() + else: + result = self._legacy.get(lk) + else: + value = None if maker is None else maker() + if key not in ('commands', 'exports', 'modules', 'namespaces', + 'classifiers'): + result = self._data.get(key, value) + else: + # special cases for PEP 459 + sentinel = object() + result = sentinel + d = self._data.get('extensions') + if d: + if key == 'commands': + result = d.get('python.commands', value) + elif key == 'classifiers': + d = d.get('python.details') + if d: + result = d.get(key, value) + else: + d = d.get('python.exports') + if not d: + d = self._data.get('python.exports') + if d: + result = d.get(key, value) + if result is sentinel: + result = value + elif key not in common: + result = object.__getattribute__(self, key) + elif self._legacy: + result = self._legacy.get(key) + else: + result = self._data.get(key) + return result + + def _validate_value(self, key, value, scheme=None): + if key in self.SYNTAX_VALIDATORS: + pattern, exclusions = self.SYNTAX_VALIDATORS[key] + if (scheme or self.scheme) not in exclusions: + m = pattern.match(value) + if not m: + raise MetadataInvalidError("'%s' is an invalid value for " + "the '%s' property" % (value, + key)) + + def __setattr__(self, key, value): + self._validate_value(key, value) + common = object.__getattribute__(self, 'common_keys') + mapped = object.__getattribute__(self, 'mapped_keys') + if key in mapped: + lk, _ = mapped[key] + if self._legacy: + if lk is None: + raise NotImplementedError + self._legacy[lk] = value + elif key not in ('commands', 'exports', 'modules', 'namespaces', + 'classifiers'): + self._data[key] = value + else: + # special cases for PEP 459 + d = self._data.setdefault('extensions', {}) + if key == 'commands': + d['python.commands'] = value + elif key == 'classifiers': + d = d.setdefault('python.details', {}) + d[key] = value + else: + d = d.setdefault('python.exports', {}) + d[key] = value + elif key not in common: + object.__setattr__(self, key, value) + else: + if key == 'keywords': + if isinstance(value, string_types): + value = value.strip() + if value: + value = value.split() + else: + value = [] + if self._legacy: + self._legacy[key] = value + else: + self._data[key] = value + + @property + def name_and_version(self): + return _get_name_and_version(self.name, self.version, True) + + @property + def provides(self): + if self._legacy: + result = self._legacy['Provides-Dist'] + else: + result = self._data.setdefault('provides', []) + s = '%s (%s)' % (self.name, self.version) + if s not in result: + result.append(s) + return result + + @provides.setter + def provides(self, value): + if self._legacy: + self._legacy['Provides-Dist'] = value + else: + self._data['provides'] = value + + def get_requirements(self, reqts, extras=None, env=None): + """ + Base method to get dependencies, given a set of extras + to satisfy and an optional environment context. + :param reqts: A list of sometimes-wanted dependencies, + perhaps dependent on extras and environment. + :param extras: A list of optional components being requested. + :param env: An optional environment for marker evaluation. + """ + if self._legacy: + result = reqts + else: + result = [] + extras = get_extras(extras or [], self.extras) + for d in reqts: + if 'extra' not in d and 'environment' not in d: + # unconditional + include = True + else: + if 'extra' not in d: + # Not extra-dependent - only environment-dependent + include = True + else: + include = d.get('extra') in extras + if include: + # Not excluded because of extras, check environment + marker = d.get('environment') + if marker: + include = interpret(marker, env) + if include: + result.extend(d['requires']) + for key in ('build', 'dev', 'test'): + e = ':%s:' % key + if e in extras: + extras.remove(e) + # A recursive call, but it should terminate since 'test' + # has been removed from the extras + reqts = self._data.get('%s_requires' % key, []) + result.extend(self.get_requirements(reqts, extras=extras, + env=env)) + return result + + @property + def dictionary(self): + if self._legacy: + return self._from_legacy() + return self._data + + @property + def dependencies(self): + if self._legacy: + raise NotImplementedError + else: + return extract_by_key(self._data, self.DEPENDENCY_KEYS) + + @dependencies.setter + def dependencies(self, value): + if self._legacy: + raise NotImplementedError + else: + self._data.update(value) + + def _validate_mapping(self, mapping, scheme): + if mapping.get('metadata_version') != self.METADATA_VERSION: + raise MetadataUnrecognizedVersionError() + missing = [] + for key, exclusions in self.MANDATORY_KEYS.items(): + if key not in mapping: + if scheme not in exclusions: + missing.append(key) + if missing: + msg = 'Missing metadata items: %s' % ', '.join(missing) + raise MetadataMissingError(msg) + for k, v in mapping.items(): + self._validate_value(k, v, scheme) + + def validate(self): + if self._legacy: + missing, warnings = self._legacy.check(True) + if missing or warnings: + logger.warning('Metadata: missing: %s, warnings: %s', + missing, warnings) + else: + self._validate_mapping(self._data, self.scheme) + + def todict(self): + if self._legacy: + return self._legacy.todict(True) + else: + result = extract_by_key(self._data, self.INDEX_KEYS) + return result + + def _from_legacy(self): + assert self._legacy and not self._data + result = { + 'metadata_version': self.METADATA_VERSION, + 'generator': self.GENERATOR, + } + lmd = self._legacy.todict(True) # skip missing ones + for k in ('name', 'version', 'license', 'summary', 'description', + 'classifier'): + if k in lmd: + if k == 'classifier': + nk = 'classifiers' + else: + nk = k + result[nk] = lmd[k] + kw = lmd.get('Keywords', []) + if kw == ['']: + kw = [] + result['keywords'] = kw + keys = (('requires_dist', 'run_requires'), + ('setup_requires_dist', 'build_requires')) + for ok, nk in keys: + if ok in lmd and lmd[ok]: + result[nk] = [{'requires': lmd[ok]}] + result['provides'] = self.provides + author = {} + maintainer = {} + return result + + LEGACY_MAPPING = { + 'name': 'Name', + 'version': 'Version', + 'license': 'License', + 'summary': 'Summary', + 'description': 'Description', + 'classifiers': 'Classifier', + } + + def _to_legacy(self): + def process_entries(entries): + reqts = set() + for e in entries: + extra = e.get('extra') + env = e.get('environment') + rlist = e['requires'] + for r in rlist: + if not env and not extra: + reqts.add(r) + else: + marker = '' + if extra: + marker = 'extra == "%s"' % extra + if env: + if marker: + marker = '(%s) and %s' % (env, marker) + else: + marker = env + reqts.add(';'.join((r, marker))) + return reqts + + assert self._data and not self._legacy + result = LegacyMetadata() + nmd = self._data + for nk, ok in self.LEGACY_MAPPING.items(): + if nk in nmd: + result[ok] = nmd[nk] + r1 = process_entries(self.run_requires + self.meta_requires) + r2 = process_entries(self.build_requires + self.dev_requires) + if self.extras: + result['Provides-Extra'] = sorted(self.extras) + result['Requires-Dist'] = sorted(r1) + result['Setup-Requires-Dist'] = sorted(r2) + # TODO: other fields such as contacts + return result + + def write(self, path=None, fileobj=None, legacy=False, skip_unknown=True): + if [path, fileobj].count(None) != 1: + raise ValueError('Exactly one of path and fileobj is needed') + self.validate() + if legacy: + if self._legacy: + legacy_md = self._legacy + else: + legacy_md = self._to_legacy() + if path: + legacy_md.write(path, skip_unknown=skip_unknown) + else: + legacy_md.write_file(fileobj, skip_unknown=skip_unknown) + else: + if self._legacy: + d = self._from_legacy() + else: + d = self._data + if fileobj: + json.dump(d, fileobj, ensure_ascii=True, indent=2, + sort_keys=True) + else: + with codecs.open(path, 'w', 'utf-8') as f: + json.dump(d, f, ensure_ascii=True, indent=2, + sort_keys=True) + + def add_requirements(self, requirements): + if self._legacy: + self._legacy.add_requirements(requirements) + else: + run_requires = self._data.setdefault('run_requires', []) + always = None + for entry in run_requires: + if 'environment' not in entry and 'extra' not in entry: + always = entry + break + if always is None: + always = { 'requires': requirements } + run_requires.insert(0, always) + else: + rset = set(always['requires']) | set(requirements) + always['requires'] = sorted(rset) + + def __repr__(self): + name = self.name or '(no name)' + version = self.version or 'no version' + return '<%s %s %s (%s)>' % (self.__class__.__name__, + self.metadata_version, name, version) diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/resources.py b/venv/Lib/site-packages/pip/_vendor/distlib/resources.py new file mode 100644 index 0000000..1884016 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/resources.py @@ -0,0 +1,355 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2017 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import unicode_literals + +import bisect +import io +import logging +import os +import pkgutil +import shutil +import sys +import types +import zipimport + +from . import DistlibException +from .util import cached_property, get_cache_base, path_to_cache_dir, Cache + +logger = logging.getLogger(__name__) + + +cache = None # created when needed + + +class ResourceCache(Cache): + def __init__(self, base=None): + if base is None: + # Use native string to avoid issues on 2.x: see Python #20140. + base = os.path.join(get_cache_base(), str('resource-cache')) + super(ResourceCache, self).__init__(base) + + def is_stale(self, resource, path): + """ + Is the cache stale for the given resource? + + :param resource: The :class:`Resource` being cached. + :param path: The path of the resource in the cache. + :return: True if the cache is stale. + """ + # Cache invalidation is a hard problem :-) + return True + + def get(self, resource): + """ + Get a resource into the cache, + + :param resource: A :class:`Resource` instance. + :return: The pathname of the resource in the cache. + """ + prefix, path = resource.finder.get_cache_info(resource) + if prefix is None: + result = path + else: + result = os.path.join(self.base, self.prefix_to_dir(prefix), path) + dirname = os.path.dirname(result) + if not os.path.isdir(dirname): + os.makedirs(dirname) + if not os.path.exists(result): + stale = True + else: + stale = self.is_stale(resource, path) + if stale: + # write the bytes of the resource to the cache location + with open(result, 'wb') as f: + f.write(resource.bytes) + return result + + +class ResourceBase(object): + def __init__(self, finder, name): + self.finder = finder + self.name = name + + +class Resource(ResourceBase): + """ + A class representing an in-package resource, such as a data file. This is + not normally instantiated by user code, but rather by a + :class:`ResourceFinder` which manages the resource. + """ + is_container = False # Backwards compatibility + + def as_stream(self): + """ + Get the resource as a stream. + + This is not a property to make it obvious that it returns a new stream + each time. + """ + return self.finder.get_stream(self) + + @cached_property + def file_path(self): + global cache + if cache is None: + cache = ResourceCache() + return cache.get(self) + + @cached_property + def bytes(self): + return self.finder.get_bytes(self) + + @cached_property + def size(self): + return self.finder.get_size(self) + + +class ResourceContainer(ResourceBase): + is_container = True # Backwards compatibility + + @cached_property + def resources(self): + return self.finder.get_resources(self) + + +class ResourceFinder(object): + """ + Resource finder for file system resources. + """ + + if sys.platform.startswith('java'): + skipped_extensions = ('.pyc', '.pyo', '.class') + else: + skipped_extensions = ('.pyc', '.pyo') + + def __init__(self, module): + self.module = module + self.loader = getattr(module, '__loader__', None) + self.base = os.path.dirname(getattr(module, '__file__', '')) + + def _adjust_path(self, path): + return os.path.realpath(path) + + def _make_path(self, resource_name): + # Issue #50: need to preserve type of path on Python 2.x + # like os.path._get_sep + if isinstance(resource_name, bytes): # should only happen on 2.x + sep = b'/' + else: + sep = '/' + parts = resource_name.split(sep) + parts.insert(0, self.base) + result = os.path.join(*parts) + return self._adjust_path(result) + + def _find(self, path): + return os.path.exists(path) + + def get_cache_info(self, resource): + return None, resource.path + + def find(self, resource_name): + path = self._make_path(resource_name) + if not self._find(path): + result = None + else: + if self._is_directory(path): + result = ResourceContainer(self, resource_name) + else: + result = Resource(self, resource_name) + result.path = path + return result + + def get_stream(self, resource): + return open(resource.path, 'rb') + + def get_bytes(self, resource): + with open(resource.path, 'rb') as f: + return f.read() + + def get_size(self, resource): + return os.path.getsize(resource.path) + + def get_resources(self, resource): + def allowed(f): + return (f != '__pycache__' and not + f.endswith(self.skipped_extensions)) + return set([f for f in os.listdir(resource.path) if allowed(f)]) + + def is_container(self, resource): + return self._is_directory(resource.path) + + _is_directory = staticmethod(os.path.isdir) + + def iterator(self, resource_name): + resource = self.find(resource_name) + if resource is not None: + todo = [resource] + while todo: + resource = todo.pop(0) + yield resource + if resource.is_container: + rname = resource.name + for name in resource.resources: + if not rname: + new_name = name + else: + new_name = '/'.join([rname, name]) + child = self.find(new_name) + if child.is_container: + todo.append(child) + else: + yield child + + +class ZipResourceFinder(ResourceFinder): + """ + Resource finder for resources in .zip files. + """ + def __init__(self, module): + super(ZipResourceFinder, self).__init__(module) + archive = self.loader.archive + self.prefix_len = 1 + len(archive) + # PyPy doesn't have a _files attr on zipimporter, and you can't set one + if hasattr(self.loader, '_files'): + self._files = self.loader._files + else: + self._files = zipimport._zip_directory_cache[archive] + self.index = sorted(self._files) + + def _adjust_path(self, path): + return path + + def _find(self, path): + path = path[self.prefix_len:] + if path in self._files: + result = True + else: + if path and path[-1] != os.sep: + path = path + os.sep + i = bisect.bisect(self.index, path) + try: + result = self.index[i].startswith(path) + except IndexError: + result = False + if not result: + logger.debug('_find failed: %r %r', path, self.loader.prefix) + else: + logger.debug('_find worked: %r %r', path, self.loader.prefix) + return result + + def get_cache_info(self, resource): + prefix = self.loader.archive + path = resource.path[1 + len(prefix):] + return prefix, path + + def get_bytes(self, resource): + return self.loader.get_data(resource.path) + + def get_stream(self, resource): + return io.BytesIO(self.get_bytes(resource)) + + def get_size(self, resource): + path = resource.path[self.prefix_len:] + return self._files[path][3] + + def get_resources(self, resource): + path = resource.path[self.prefix_len:] + if path and path[-1] != os.sep: + path += os.sep + plen = len(path) + result = set() + i = bisect.bisect(self.index, path) + while i < len(self.index): + if not self.index[i].startswith(path): + break + s = self.index[i][plen:] + result.add(s.split(os.sep, 1)[0]) # only immediate children + i += 1 + return result + + def _is_directory(self, path): + path = path[self.prefix_len:] + if path and path[-1] != os.sep: + path += os.sep + i = bisect.bisect(self.index, path) + try: + result = self.index[i].startswith(path) + except IndexError: + result = False + return result + +_finder_registry = { + type(None): ResourceFinder, + zipimport.zipimporter: ZipResourceFinder +} + +try: + # In Python 3.6, _frozen_importlib -> _frozen_importlib_external + try: + import _frozen_importlib_external as _fi + except ImportError: + import _frozen_importlib as _fi + _finder_registry[_fi.SourceFileLoader] = ResourceFinder + _finder_registry[_fi.FileFinder] = ResourceFinder + del _fi +except (ImportError, AttributeError): + pass + + +def register_finder(loader, finder_maker): + _finder_registry[type(loader)] = finder_maker + +_finder_cache = {} + + +def finder(package): + """ + Return a resource finder for a package. + :param package: The name of the package. + :return: A :class:`ResourceFinder` instance for the package. + """ + if package in _finder_cache: + result = _finder_cache[package] + else: + if package not in sys.modules: + __import__(package) + module = sys.modules[package] + path = getattr(module, '__path__', None) + if path is None: + raise DistlibException('You cannot get a finder for a module, ' + 'only for a package') + loader = getattr(module, '__loader__', None) + finder_maker = _finder_registry.get(type(loader)) + if finder_maker is None: + raise DistlibException('Unable to locate finder for %r' % package) + result = finder_maker(module) + _finder_cache[package] = result + return result + + +_dummy_module = types.ModuleType(str('__dummy__')) + + +def finder_for_path(path): + """ + Return a resource finder for a path, which should represent a container. + + :param path: The path. + :return: A :class:`ResourceFinder` instance for the path. + """ + result = None + # calls any path hooks, gets importer into cache + pkgutil.get_importer(path) + loader = sys.path_importer_cache.get(path) + finder = _finder_registry.get(type(loader)) + if finder: + module = _dummy_module + module.__file__ = os.path.join(path, '') + module.__loader__ = loader + result = finder(module) + return result diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/scripts.py b/venv/Lib/site-packages/pip/_vendor/distlib/scripts.py new file mode 100644 index 0000000..0b7c3d0 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/scripts.py @@ -0,0 +1,415 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2015 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from io import BytesIO +import logging +import os +import re +import struct +import sys + +from .compat import sysconfig, detect_encoding, ZipFile +from .resources import finder +from .util import (FileOperator, get_export_entry, convert_path, + get_executable, in_venv) + +logger = logging.getLogger(__name__) + +_DEFAULT_MANIFEST = ''' +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <assemblyIdentity version="1.0.0.0" + processorArchitecture="X86" + name="%s" + type="win32"/> + + <!-- Identify the application security requirements. --> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker" uiAccess="false"/> + </requestedPrivileges> + </security> + </trustInfo> +</assembly>'''.strip() + +# check if Python is called on the first line with this expression +FIRST_LINE_RE = re.compile(b'^#!.*pythonw?[0-9.]*([ \t].*)?$') +SCRIPT_TEMPLATE = r'''# -*- coding: utf-8 -*- +if __name__ == '__main__': + import sys, re + + def _resolve(module, func): + __import__(module) + mod = sys.modules[module] + parts = func.split('.') + result = getattr(mod, parts.pop(0)) + for p in parts: + result = getattr(result, p) + return result + + try: + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + + func = _resolve('%(module)s', '%(func)s') + rc = func() # None interpreted as 0 + except Exception as e: # only supporting Python >= 2.6 + sys.stderr.write('%%s\n' %% e) + rc = 1 + sys.exit(rc) +''' + + +def _enquote_executable(executable): + if ' ' in executable: + # make sure we quote only the executable in case of env + # for example /usr/bin/env "/dir with spaces/bin/jython" + # instead of "/usr/bin/env /dir with spaces/bin/jython" + # otherwise whole + if executable.startswith('/usr/bin/env '): + env, _executable = executable.split(' ', 1) + if ' ' in _executable and not _executable.startswith('"'): + executable = '%s "%s"' % (env, _executable) + else: + if not executable.startswith('"'): + executable = '"%s"' % executable + return executable + + +class ScriptMaker(object): + """ + A class to copy or create scripts from source scripts or callable + specifications. + """ + script_template = SCRIPT_TEMPLATE + + executable = None # for shebangs + + def __init__(self, source_dir, target_dir, add_launchers=True, + dry_run=False, fileop=None): + self.source_dir = source_dir + self.target_dir = target_dir + self.add_launchers = add_launchers + self.force = False + self.clobber = False + # It only makes sense to set mode bits on POSIX. + self.set_mode = (os.name == 'posix') or (os.name == 'java' and + os._name == 'posix') + self.variants = set(('', 'X.Y')) + self._fileop = fileop or FileOperator(dry_run) + + self._is_nt = os.name == 'nt' or ( + os.name == 'java' and os._name == 'nt') + + def _get_alternate_executable(self, executable, options): + if options.get('gui', False) and self._is_nt: # pragma: no cover + dn, fn = os.path.split(executable) + fn = fn.replace('python', 'pythonw') + executable = os.path.join(dn, fn) + return executable + + if sys.platform.startswith('java'): # pragma: no cover + def _is_shell(self, executable): + """ + Determine if the specified executable is a script + (contains a #! line) + """ + try: + with open(executable) as fp: + return fp.read(2) == '#!' + except (OSError, IOError): + logger.warning('Failed to open %s', executable) + return False + + def _fix_jython_executable(self, executable): + if self._is_shell(executable): + # Workaround for Jython is not needed on Linux systems. + import java + + if java.lang.System.getProperty('os.name') == 'Linux': + return executable + elif executable.lower().endswith('jython.exe'): + # Use wrapper exe for Jython on Windows + return executable + return '/usr/bin/env %s' % executable + + def _build_shebang(self, executable, post_interp): + """ + Build a shebang line. In the simple case (on Windows, or a shebang line + which is not too long or contains spaces) use a simple formulation for + the shebang. Otherwise, use /bin/sh as the executable, with a contrived + shebang which allows the script to run either under Python or sh, using + suitable quoting. Thanks to Harald Nordgren for his input. + + See also: http://www.in-ulm.de/~mascheck/various/shebang/#length + https://hg.mozilla.org/mozilla-central/file/tip/mach + """ + if os.name != 'posix': + simple_shebang = True + else: + # Add 3 for '#!' prefix and newline suffix. + shebang_length = len(executable) + len(post_interp) + 3 + if sys.platform == 'darwin': + max_shebang_length = 512 + else: + max_shebang_length = 127 + simple_shebang = ((b' ' not in executable) and + (shebang_length <= max_shebang_length)) + + if simple_shebang: + result = b'#!' + executable + post_interp + b'\n' + else: + result = b'#!/bin/sh\n' + result += b"'''exec' " + executable + post_interp + b' "$0" "$@"\n' + result += b"' '''" + return result + + def _get_shebang(self, encoding, post_interp=b'', options=None): + enquote = True + if self.executable: + executable = self.executable + enquote = False # assume this will be taken care of + elif not sysconfig.is_python_build(): + executable = get_executable() + elif in_venv(): # pragma: no cover + executable = os.path.join(sysconfig.get_path('scripts'), + 'python%s' % sysconfig.get_config_var('EXE')) + else: # pragma: no cover + executable = os.path.join( + sysconfig.get_config_var('BINDIR'), + 'python%s%s' % (sysconfig.get_config_var('VERSION'), + sysconfig.get_config_var('EXE'))) + if options: + executable = self._get_alternate_executable(executable, options) + + if sys.platform.startswith('java'): # pragma: no cover + executable = self._fix_jython_executable(executable) + # Normalise case for Windows + executable = os.path.normcase(executable) + # If the user didn't specify an executable, it may be necessary to + # cater for executable paths with spaces (not uncommon on Windows) + if enquote: + executable = _enquote_executable(executable) + # Issue #51: don't use fsencode, since we later try to + # check that the shebang is decodable using utf-8. + executable = executable.encode('utf-8') + # in case of IronPython, play safe and enable frames support + if (sys.platform == 'cli' and '-X:Frames' not in post_interp + and '-X:FullFrames' not in post_interp): # pragma: no cover + post_interp += b' -X:Frames' + shebang = self._build_shebang(executable, post_interp) + # Python parser starts to read a script using UTF-8 until + # it gets a #coding:xxx cookie. The shebang has to be the + # first line of a file, the #coding:xxx cookie cannot be + # written before. So the shebang has to be decodable from + # UTF-8. + try: + shebang.decode('utf-8') + except UnicodeDecodeError: # pragma: no cover + raise ValueError( + 'The shebang (%r) is not decodable from utf-8' % shebang) + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be decodable from + # the script encoding too. + if encoding != 'utf-8': + try: + shebang.decode(encoding) + except UnicodeDecodeError: # pragma: no cover + raise ValueError( + 'The shebang (%r) is not decodable ' + 'from the script encoding (%r)' % (shebang, encoding)) + return shebang + + def _get_script_text(self, entry): + return self.script_template % dict(module=entry.prefix, + func=entry.suffix) + + manifest = _DEFAULT_MANIFEST + + def get_manifest(self, exename): + base = os.path.basename(exename) + return self.manifest % base + + def _write_script(self, names, shebang, script_bytes, filenames, ext): + use_launcher = self.add_launchers and self._is_nt + linesep = os.linesep.encode('utf-8') + if not use_launcher: + script_bytes = shebang + linesep + script_bytes + else: # pragma: no cover + if ext == 'py': + launcher = self._get_launcher('t') + else: + launcher = self._get_launcher('w') + stream = BytesIO() + with ZipFile(stream, 'w') as zf: + zf.writestr('__main__.py', script_bytes) + zip_data = stream.getvalue() + script_bytes = launcher + shebang + linesep + zip_data + for name in names: + outname = os.path.join(self.target_dir, name) + if use_launcher: # pragma: no cover + n, e = os.path.splitext(outname) + if e.startswith('.py'): + outname = n + outname = '%s.exe' % outname + try: + self._fileop.write_binary_file(outname, script_bytes) + except Exception: + # Failed writing an executable - it might be in use. + logger.warning('Failed to write executable - trying to ' + 'use .deleteme logic') + dfname = '%s.deleteme' % outname + if os.path.exists(dfname): + os.remove(dfname) # Not allowed to fail here + os.rename(outname, dfname) # nor here + self._fileop.write_binary_file(outname, script_bytes) + logger.debug('Able to replace executable using ' + '.deleteme logic') + try: + os.remove(dfname) + except Exception: + pass # still in use - ignore error + else: + if self._is_nt and not outname.endswith('.' + ext): # pragma: no cover + outname = '%s.%s' % (outname, ext) + if os.path.exists(outname) and not self.clobber: + logger.warning('Skipping existing file %s', outname) + continue + self._fileop.write_binary_file(outname, script_bytes) + if self.set_mode: + self._fileop.set_executable_mode([outname]) + filenames.append(outname) + + def _make_script(self, entry, filenames, options=None): + post_interp = b'' + if options: + args = options.get('interpreter_args', []) + if args: + args = ' %s' % ' '.join(args) + post_interp = args.encode('utf-8') + shebang = self._get_shebang('utf-8', post_interp, options=options) + script = self._get_script_text(entry).encode('utf-8') + name = entry.name + scriptnames = set() + if '' in self.variants: + scriptnames.add(name) + if 'X' in self.variants: + scriptnames.add('%s%s' % (name, sys.version[0])) + if 'X.Y' in self.variants: + scriptnames.add('%s-%s' % (name, sys.version[:3])) + if options and options.get('gui', False): + ext = 'pyw' + else: + ext = 'py' + self._write_script(scriptnames, shebang, script, filenames, ext) + + def _copy_script(self, script, filenames): + adjust = False + script = os.path.join(self.source_dir, convert_path(script)) + outname = os.path.join(self.target_dir, os.path.basename(script)) + if not self.force and not self._fileop.newer(script, outname): + logger.debug('not copying %s (up-to-date)', script) + return + + # Always open the file, but ignore failures in dry-run mode -- + # that way, we'll get accurate feedback if we can read the + # script. + try: + f = open(script, 'rb') + except IOError: # pragma: no cover + if not self.dry_run: + raise + f = None + else: + first_line = f.readline() + if not first_line: # pragma: no cover + logger.warning('%s: %s is an empty file (skipping)', + self.get_command_name(), script) + return + + match = FIRST_LINE_RE.match(first_line.replace(b'\r\n', b'\n')) + if match: + adjust = True + post_interp = match.group(1) or b'' + + if not adjust: + if f: + f.close() + self._fileop.copy_file(script, outname) + if self.set_mode: + self._fileop.set_executable_mode([outname]) + filenames.append(outname) + else: + logger.info('copying and adjusting %s -> %s', script, + self.target_dir) + if not self._fileop.dry_run: + encoding, lines = detect_encoding(f.readline) + f.seek(0) + shebang = self._get_shebang(encoding, post_interp) + if b'pythonw' in first_line: # pragma: no cover + ext = 'pyw' + else: + ext = 'py' + n = os.path.basename(outname) + self._write_script([n], shebang, f.read(), filenames, ext) + if f: + f.close() + + @property + def dry_run(self): + return self._fileop.dry_run + + @dry_run.setter + def dry_run(self, value): + self._fileop.dry_run = value + + if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'): # pragma: no cover + # Executable launcher support. + # Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/ + + def _get_launcher(self, kind): + if struct.calcsize('P') == 8: # 64-bit + bits = '64' + else: + bits = '32' + name = '%s%s.exe' % (kind, bits) + # Issue 31: don't hardcode an absolute package name, but + # determine it relative to the current package + distlib_package = __name__.rsplit('.', 1)[0] + result = finder(distlib_package).find(name).bytes + return result + + # Public API follows + + def make(self, specification, options=None): + """ + Make a script. + + :param specification: The specification, which is either a valid export + entry specification (to make a script from a + callable) or a filename (to make a script by + copying from a source location). + :param options: A dictionary of options controlling script generation. + :return: A list of all absolute pathnames written to. + """ + filenames = [] + entry = get_export_entry(specification) + if entry is None: + self._copy_script(specification, filenames) + else: + self._make_script(entry, filenames, options=options) + return filenames + + def make_multiple(self, specifications, options=None): + """ + Take a list of specifications and make scripts from them, + :param specifications: A list of specifications. + :return: A list of all absolute pathnames written to, + """ + filenames = [] + for specification in specifications: + filenames.extend(self.make(specification, options)) + return filenames diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/t32.exe b/venv/Lib/site-packages/pip/_vendor/distlib/t32.exe new file mode 100644 index 0000000000000000000000000000000000000000..a09d926872d84ae22a617dfe9ebb560d420b37de GIT binary patch literal 92672 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/t64.exe b/venv/Lib/site-packages/pip/_vendor/distlib/t64.exe new file mode 100644 index 0000000000000000000000000000000000000000..9da9b40de922fb203df6b9a1d0ad4139af536850 GIT binary patch literal 102400 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�vlnW`{LoXpTySn$@;x8yoz96jn?+Kk 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�?dYNUd4WaURX_A__M4W}D0!0n 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/util.py b/venv/Lib/site-packages/pip/_vendor/distlib/util.py new file mode 100644 index 0000000..0b14a93 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/util.py @@ -0,0 +1,1755 @@ +# +# Copyright (C) 2012-2017 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import codecs +from collections import deque +import contextlib +import csv +from glob import iglob as std_iglob +import io +import json +import logging +import os +import py_compile +import re +import socket +try: + import ssl +except ImportError: # pragma: no cover + ssl = None +import subprocess +import sys +import tarfile +import tempfile +import textwrap + +try: + import threading +except ImportError: # pragma: no cover + import dummy_threading as threading +import time + +from . import DistlibException +from .compat import (string_types, text_type, shutil, raw_input, StringIO, + cache_from_source, urlopen, urljoin, httplib, xmlrpclib, + splittype, HTTPHandler, BaseConfigurator, valid_ident, + Container, configparser, URLError, ZipFile, fsdecode, + unquote, urlparse) + +logger = logging.getLogger(__name__) + +# +# Requirement parsing code as per PEP 508 +# + +IDENTIFIER = re.compile(r'^([\w\.-]+)\s*') +VERSION_IDENTIFIER = re.compile(r'^([\w\.*+-]+)\s*') +COMPARE_OP = re.compile(r'^(<=?|>=?|={2,3}|[~!]=)\s*') +MARKER_OP = re.compile(r'^((<=?)|(>=?)|={2,3}|[~!]=|in|not\s+in)\s*') +OR = re.compile(r'^or\b\s*') +AND = re.compile(r'^and\b\s*') +NON_SPACE = re.compile(r'(\S+)\s*') +STRING_CHUNK = re.compile(r'([\s\w\.{}()*+#:;,/?!~`@$%^&=|<>\[\]-]+)') + + +def parse_marker(marker_string): + """ + Parse a marker string and return a dictionary containing a marker expression. + + The dictionary will contain keys "op", "lhs" and "rhs" for non-terminals in + the expression grammar, or strings. A string contained in quotes is to be + interpreted as a literal string, and a string not contained in quotes is a + variable (such as os_name). + """ + def marker_var(remaining): + # either identifier, or literal string + m = IDENTIFIER.match(remaining) + if m: + result = m.groups()[0] + remaining = remaining[m.end():] + elif not remaining: + raise SyntaxError('unexpected end of input') + else: + q = remaining[0] + if q not in '\'"': + raise SyntaxError('invalid expression: %s' % remaining) + oq = '\'"'.replace(q, '') + remaining = remaining[1:] + parts = [q] + while remaining: + # either a string chunk, or oq, or q to terminate + if remaining[0] == q: + break + elif remaining[0] == oq: + parts.append(oq) + remaining = remaining[1:] + else: + m = STRING_CHUNK.match(remaining) + if not m: + raise SyntaxError('error in string literal: %s' % remaining) + parts.append(m.groups()[0]) + remaining = remaining[m.end():] + else: + s = ''.join(parts) + raise SyntaxError('unterminated string: %s' % s) + parts.append(q) + result = ''.join(parts) + remaining = remaining[1:].lstrip() # skip past closing quote + return result, remaining + + def marker_expr(remaining): + if remaining and remaining[0] == '(': + result, remaining = marker(remaining[1:].lstrip()) + if remaining[0] != ')': + raise SyntaxError('unterminated parenthesis: %s' % remaining) + remaining = remaining[1:].lstrip() + else: + lhs, remaining = marker_var(remaining) + while remaining: + m = MARKER_OP.match(remaining) + if not m: + break + op = m.groups()[0] + remaining = remaining[m.end():] + rhs, remaining = marker_var(remaining) + lhs = {'op': op, 'lhs': lhs, 'rhs': rhs} + result = lhs + return result, remaining + + def marker_and(remaining): + lhs, remaining = marker_expr(remaining) + while remaining: + m = AND.match(remaining) + if not m: + break + remaining = remaining[m.end():] + rhs, remaining = marker_expr(remaining) + lhs = {'op': 'and', 'lhs': lhs, 'rhs': rhs} + return lhs, remaining + + def marker(remaining): + lhs, remaining = marker_and(remaining) + while remaining: + m = OR.match(remaining) + if not m: + break + remaining = remaining[m.end():] + rhs, remaining = marker_and(remaining) + lhs = {'op': 'or', 'lhs': lhs, 'rhs': rhs} + return lhs, remaining + + return marker(marker_string) + + +def parse_requirement(req): + """ + Parse a requirement passed in as a string. Return a Container + whose attributes contain the various parts of the requirement. + """ + remaining = req.strip() + if not remaining or remaining.startswith('#'): + return None + m = IDENTIFIER.match(remaining) + if not m: + raise SyntaxError('name expected: %s' % remaining) + distname = m.groups()[0] + remaining = remaining[m.end():] + extras = mark_expr = versions = uri = None + if remaining and remaining[0] == '[': + i = remaining.find(']', 1) + if i < 0: + raise SyntaxError('unterminated extra: %s' % remaining) + s = remaining[1:i] + remaining = remaining[i + 1:].lstrip() + extras = [] + while s: + m = IDENTIFIER.match(s) + if not m: + raise SyntaxError('malformed extra: %s' % s) + extras.append(m.groups()[0]) + s = s[m.end():] + if not s: + break + if s[0] != ',': + raise SyntaxError('comma expected in extras: %s' % s) + s = s[1:].lstrip() + if not extras: + extras = None + if remaining: + if remaining[0] == '@': + # it's a URI + remaining = remaining[1:].lstrip() + m = NON_SPACE.match(remaining) + if not m: + raise SyntaxError('invalid URI: %s' % remaining) + uri = m.groups()[0] + t = urlparse(uri) + # there are issues with Python and URL parsing, so this test + # is a bit crude. See bpo-20271, bpo-23505. Python doesn't + # always parse invalid URLs correctly - it should raise + # exceptions for malformed URLs + if not (t.scheme and t.netloc): + raise SyntaxError('Invalid URL: %s' % uri) + remaining = remaining[m.end():].lstrip() + else: + + def get_versions(ver_remaining): + """ + Return a list of operator, version tuples if any are + specified, else None. + """ + m = COMPARE_OP.match(ver_remaining) + versions = None + if m: + versions = [] + while True: + op = m.groups()[0] + ver_remaining = ver_remaining[m.end():] + m = VERSION_IDENTIFIER.match(ver_remaining) + if not m: + raise SyntaxError('invalid version: %s' % ver_remaining) + v = m.groups()[0] + versions.append((op, v)) + ver_remaining = ver_remaining[m.end():] + if not ver_remaining or ver_remaining[0] != ',': + break + ver_remaining = ver_remaining[1:].lstrip() + m = COMPARE_OP.match(ver_remaining) + if not m: + raise SyntaxError('invalid constraint: %s' % ver_remaining) + if not versions: + versions = None + return versions, ver_remaining + + if remaining[0] != '(': + versions, remaining = get_versions(remaining) + else: + i = remaining.find(')', 1) + if i < 0: + raise SyntaxError('unterminated parenthesis: %s' % remaining) + s = remaining[1:i] + remaining = remaining[i + 1:].lstrip() + # As a special diversion from PEP 508, allow a version number + # a.b.c in parentheses as a synonym for ~= a.b.c (because this + # is allowed in earlier PEPs) + if COMPARE_OP.match(s): + versions, _ = get_versions(s) + else: + m = VERSION_IDENTIFIER.match(s) + if not m: + raise SyntaxError('invalid constraint: %s' % s) + v = m.groups()[0] + s = s[m.end():].lstrip() + if s: + raise SyntaxError('invalid constraint: %s' % s) + versions = [('~=', v)] + + if remaining: + if remaining[0] != ';': + raise SyntaxError('invalid requirement: %s' % remaining) + remaining = remaining[1:].lstrip() + + mark_expr, remaining = parse_marker(remaining) + + if remaining and remaining[0] != '#': + raise SyntaxError('unexpected trailing data: %s' % remaining) + + if not versions: + rs = distname + else: + rs = '%s %s' % (distname, ', '.join(['%s %s' % con for con in versions])) + return Container(name=distname, extras=extras, constraints=versions, + marker=mark_expr, url=uri, requirement=rs) + + +def get_resources_dests(resources_root, rules): + """Find destinations for resources files""" + + def get_rel_path(root, path): + # normalizes and returns a lstripped-/-separated path + root = root.replace(os.path.sep, '/') + path = path.replace(os.path.sep, '/') + assert path.startswith(root) + return path[len(root):].lstrip('/') + + destinations = {} + for base, suffix, dest in rules: + prefix = os.path.join(resources_root, base) + for abs_base in iglob(prefix): + abs_glob = os.path.join(abs_base, suffix) + for abs_path in iglob(abs_glob): + resource_file = get_rel_path(resources_root, abs_path) + if dest is None: # remove the entry if it was here + destinations.pop(resource_file, None) + else: + rel_path = get_rel_path(abs_base, abs_path) + rel_dest = dest.replace(os.path.sep, '/').rstrip('/') + destinations[resource_file] = rel_dest + '/' + rel_path + return destinations + + +def in_venv(): + if hasattr(sys, 'real_prefix'): + # virtualenv venvs + result = True + else: + # PEP 405 venvs + result = sys.prefix != getattr(sys, 'base_prefix', sys.prefix) + return result + + +def get_executable(): +# The __PYVENV_LAUNCHER__ dance is apparently no longer needed, as +# changes to the stub launcher mean that sys.executable always points +# to the stub on OS X +# if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__' +# in os.environ): +# result = os.environ['__PYVENV_LAUNCHER__'] +# else: +# result = sys.executable +# return result + result = os.path.normcase(sys.executable) + if not isinstance(result, text_type): + result = fsdecode(result) + return result + + +def proceed(prompt, allowed_chars, error_prompt=None, default=None): + p = prompt + while True: + s = raw_input(p) + p = prompt + if not s and default: + s = default + if s: + c = s[0].lower() + if c in allowed_chars: + break + if error_prompt: + p = '%c: %s\n%s' % (c, error_prompt, prompt) + return c + + +def extract_by_key(d, keys): + if isinstance(keys, string_types): + keys = keys.split() + result = {} + for key in keys: + if key in d: + result[key] = d[key] + return result + +def read_exports(stream): + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getreader('utf-8')(stream) + # Try to load as JSON, falling back on legacy format + data = stream.read() + stream = StringIO(data) + try: + jdata = json.load(stream) + result = jdata['extensions']['python.exports']['exports'] + for group, entries in result.items(): + for k, v in entries.items(): + s = '%s = %s' % (k, v) + entry = get_export_entry(s) + assert entry is not None + entries[k] = entry + return result + except Exception: + stream.seek(0, 0) + + def read_stream(cp, stream): + if hasattr(cp, 'read_file'): + cp.read_file(stream) + else: + cp.readfp(stream) + + cp = configparser.ConfigParser() + try: + read_stream(cp, stream) + except configparser.MissingSectionHeaderError: + stream.close() + data = textwrap.dedent(data) + stream = StringIO(data) + read_stream(cp, stream) + + result = {} + for key in cp.sections(): + result[key] = entries = {} + for name, value in cp.items(key): + s = '%s = %s' % (name, value) + entry = get_export_entry(s) + assert entry is not None + #entry.dist = self + entries[name] = entry + return result + + +def write_exports(exports, stream): + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getwriter('utf-8')(stream) + cp = configparser.ConfigParser() + for k, v in exports.items(): + # TODO check k, v for valid values + cp.add_section(k) + for entry in v.values(): + if entry.suffix is None: + s = entry.prefix + else: + s = '%s:%s' % (entry.prefix, entry.suffix) + if entry.flags: + s = '%s [%s]' % (s, ', '.join(entry.flags)) + cp.set(k, entry.name, s) + cp.write(stream) + + +@contextlib.contextmanager +def tempdir(): + td = tempfile.mkdtemp() + try: + yield td + finally: + shutil.rmtree(td) + +@contextlib.contextmanager +def chdir(d): + cwd = os.getcwd() + try: + os.chdir(d) + yield + finally: + os.chdir(cwd) + + +@contextlib.contextmanager +def socket_timeout(seconds=15): + cto = socket.getdefaulttimeout() + try: + socket.setdefaulttimeout(seconds) + yield + finally: + socket.setdefaulttimeout(cto) + + +class cached_property(object): + def __init__(self, func): + self.func = func + #for attr in ('__name__', '__module__', '__doc__'): + # setattr(self, attr, getattr(func, attr, None)) + + def __get__(self, obj, cls=None): + if obj is None: + return self + value = self.func(obj) + object.__setattr__(obj, self.func.__name__, value) + #obj.__dict__[self.func.__name__] = value = self.func(obj) + return value + +def convert_path(pathname): + """Return 'pathname' as a name that will work on the native filesystem. + + The path is split on '/' and put back together again using the current + directory separator. Needed because filenames in the setup script are + always supplied in Unix style, and have to be converted to the local + convention before we can actually use them in the filesystem. Raises + ValueError on non-Unix-ish systems if 'pathname' either starts or + ends with a slash. + """ + if os.sep == '/': + return pathname + if not pathname: + return pathname + if pathname[0] == '/': + raise ValueError("path '%s' cannot be absolute" % pathname) + if pathname[-1] == '/': + raise ValueError("path '%s' cannot end with '/'" % pathname) + + paths = pathname.split('/') + while os.curdir in paths: + paths.remove(os.curdir) + if not paths: + return os.curdir + return os.path.join(*paths) + + +class FileOperator(object): + def __init__(self, dry_run=False): + self.dry_run = dry_run + self.ensured = set() + self._init_record() + + def _init_record(self): + self.record = False + self.files_written = set() + self.dirs_created = set() + + def record_as_written(self, path): + if self.record: + self.files_written.add(path) + + def newer(self, source, target): + """Tell if the target is newer than the source. + + Returns true if 'source' exists and is more recently modified than + 'target', or if 'source' exists and 'target' doesn't. + + Returns false if both exist and 'target' is the same age or younger + than 'source'. Raise PackagingFileError if 'source' does not exist. + + Note that this test is not very accurate: files created in the same + second will have the same "age". + """ + if not os.path.exists(source): + raise DistlibException("file '%r' does not exist" % + os.path.abspath(source)) + if not os.path.exists(target): + return True + + return os.stat(source).st_mtime > os.stat(target).st_mtime + + def copy_file(self, infile, outfile, check=True): + """Copy a file respecting dry-run and force flags. + """ + self.ensure_dir(os.path.dirname(outfile)) + logger.info('Copying %s to %s', infile, outfile) + if not self.dry_run: + msg = None + if check: + if os.path.islink(outfile): + msg = '%s is a symlink' % outfile + elif os.path.exists(outfile) and not os.path.isfile(outfile): + msg = '%s is a non-regular file' % outfile + if msg: + raise ValueError(msg + ' which would be overwritten') + shutil.copyfile(infile, outfile) + self.record_as_written(outfile) + + def copy_stream(self, instream, outfile, encoding=None): + assert not os.path.isdir(outfile) + self.ensure_dir(os.path.dirname(outfile)) + logger.info('Copying stream %s to %s', instream, outfile) + if not self.dry_run: + if encoding is None: + outstream = open(outfile, 'wb') + else: + outstream = codecs.open(outfile, 'w', encoding=encoding) + try: + shutil.copyfileobj(instream, outstream) + finally: + outstream.close() + self.record_as_written(outfile) + + def write_binary_file(self, path, data): + self.ensure_dir(os.path.dirname(path)) + if not self.dry_run: + with open(path, 'wb') as f: + f.write(data) + self.record_as_written(path) + + def write_text_file(self, path, data, encoding): + self.ensure_dir(os.path.dirname(path)) + if not self.dry_run: + with open(path, 'wb') as f: + f.write(data.encode(encoding)) + self.record_as_written(path) + + def set_mode(self, bits, mask, files): + if os.name == 'posix' or (os.name == 'java' and os._name == 'posix'): + # Set the executable bits (owner, group, and world) on + # all the files specified. + for f in files: + if self.dry_run: + logger.info("changing mode of %s", f) + else: + mode = (os.stat(f).st_mode | bits) & mask + logger.info("changing mode of %s to %o", f, mode) + os.chmod(f, mode) + + set_executable_mode = lambda s, f: s.set_mode(0o555, 0o7777, f) + + def ensure_dir(self, path): + path = os.path.abspath(path) + if path not in self.ensured and not os.path.exists(path): + self.ensured.add(path) + d, f = os.path.split(path) + self.ensure_dir(d) + logger.info('Creating %s' % path) + if not self.dry_run: + os.mkdir(path) + if self.record: + self.dirs_created.add(path) + + def byte_compile(self, path, optimize=False, force=False, prefix=None): + dpath = cache_from_source(path, not optimize) + logger.info('Byte-compiling %s to %s', path, dpath) + if not self.dry_run: + if force or self.newer(path, dpath): + if not prefix: + diagpath = None + else: + assert path.startswith(prefix) + diagpath = path[len(prefix):] + py_compile.compile(path, dpath, diagpath, True) # raise error + self.record_as_written(dpath) + return dpath + + def ensure_removed(self, path): + if os.path.exists(path): + if os.path.isdir(path) and not os.path.islink(path): + logger.debug('Removing directory tree at %s', path) + if not self.dry_run: + shutil.rmtree(path) + if self.record: + if path in self.dirs_created: + self.dirs_created.remove(path) + else: + if os.path.islink(path): + s = 'link' + else: + s = 'file' + logger.debug('Removing %s %s', s, path) + if not self.dry_run: + os.remove(path) + if self.record: + if path in self.files_written: + self.files_written.remove(path) + + def is_writable(self, path): + result = False + while not result: + if os.path.exists(path): + result = os.access(path, os.W_OK) + break + parent = os.path.dirname(path) + if parent == path: + break + path = parent + return result + + def commit(self): + """ + Commit recorded changes, turn off recording, return + changes. + """ + assert self.record + result = self.files_written, self.dirs_created + self._init_record() + return result + + def rollback(self): + if not self.dry_run: + for f in list(self.files_written): + if os.path.exists(f): + os.remove(f) + # dirs should all be empty now, except perhaps for + # __pycache__ subdirs + # reverse so that subdirs appear before their parents + dirs = sorted(self.dirs_created, reverse=True) + for d in dirs: + flist = os.listdir(d) + if flist: + assert flist == ['__pycache__'] + sd = os.path.join(d, flist[0]) + os.rmdir(sd) + os.rmdir(d) # should fail if non-empty + self._init_record() + +def resolve(module_name, dotted_path): + if module_name in sys.modules: + mod = sys.modules[module_name] + else: + mod = __import__(module_name) + if dotted_path is None: + result = mod + else: + parts = dotted_path.split('.') + result = getattr(mod, parts.pop(0)) + for p in parts: + result = getattr(result, p) + return result + + +class ExportEntry(object): + def __init__(self, name, prefix, suffix, flags): + self.name = name + self.prefix = prefix + self.suffix = suffix + self.flags = flags + + @cached_property + def value(self): + return resolve(self.prefix, self.suffix) + + def __repr__(self): # pragma: no cover + return '<ExportEntry %s = %s:%s %s>' % (self.name, self.prefix, + self.suffix, self.flags) + + def __eq__(self, other): + if not isinstance(other, ExportEntry): + result = False + else: + result = (self.name == other.name and + self.prefix == other.prefix and + self.suffix == other.suffix and + self.flags == other.flags) + return result + + __hash__ = object.__hash__ + + +ENTRY_RE = re.compile(r'''(?P<name>(\w|[-.+])+) + \s*=\s*(?P<callable>(\w+)([:\.]\w+)*) + \s*(\[\s*(?P<flags>\w+(=\w+)?(,\s*\w+(=\w+)?)*)\s*\])? + ''', re.VERBOSE) + +def get_export_entry(specification): + m = ENTRY_RE.search(specification) + if not m: + result = None + if '[' in specification or ']' in specification: + raise DistlibException("Invalid specification " + "'%s'" % specification) + else: + d = m.groupdict() + name = d['name'] + path = d['callable'] + colons = path.count(':') + if colons == 0: + prefix, suffix = path, None + else: + if colons != 1: + raise DistlibException("Invalid specification " + "'%s'" % specification) + prefix, suffix = path.split(':') + flags = d['flags'] + if flags is None: + if '[' in specification or ']' in specification: + raise DistlibException("Invalid specification " + "'%s'" % specification) + flags = [] + else: + flags = [f.strip() for f in flags.split(',')] + result = ExportEntry(name, prefix, suffix, flags) + return result + + +def get_cache_base(suffix=None): + """ + Return the default base location for distlib caches. If the directory does + not exist, it is created. Use the suffix provided for the base directory, + and default to '.distlib' if it isn't provided. + + On Windows, if LOCALAPPDATA is defined in the environment, then it is + assumed to be a directory, and will be the parent directory of the result. + On POSIX, and on Windows if LOCALAPPDATA is not defined, the user's home + directory - using os.expanduser('~') - will be the parent directory of + the result. + + The result is just the directory '.distlib' in the parent directory as + determined above, or with the name specified with ``suffix``. + """ + if suffix is None: + suffix = '.distlib' + if os.name == 'nt' and 'LOCALAPPDATA' in os.environ: + result = os.path.expandvars('$localappdata') + else: + # Assume posix, or old Windows + result = os.path.expanduser('~') + # we use 'isdir' instead of 'exists', because we want to + # fail if there's a file with that name + if os.path.isdir(result): + usable = os.access(result, os.W_OK) + if not usable: + logger.warning('Directory exists but is not writable: %s', result) + else: + try: + os.makedirs(result) + usable = True + except OSError: + logger.warning('Unable to create %s', result, exc_info=True) + usable = False + if not usable: + result = tempfile.mkdtemp() + logger.warning('Default location unusable, using %s', result) + return os.path.join(result, suffix) + + +def path_to_cache_dir(path): + """ + Convert an absolute path to a directory name for use in a cache. + + The algorithm used is: + + #. On Windows, any ``':'`` in the drive is replaced with ``'---'``. + #. Any occurrence of ``os.sep`` is replaced with ``'--'``. + #. ``'.cache'`` is appended. + """ + d, p = os.path.splitdrive(os.path.abspath(path)) + if d: + d = d.replace(':', '---') + p = p.replace(os.sep, '--') + return d + p + '.cache' + + +def ensure_slash(s): + if not s.endswith('/'): + return s + '/' + return s + + +def parse_credentials(netloc): + username = password = None + if '@' in netloc: + prefix, netloc = netloc.split('@', 1) + if ':' not in prefix: + username = prefix + else: + username, password = prefix.split(':', 1) + return username, password, netloc + + +def get_process_umask(): + result = os.umask(0o22) + os.umask(result) + return result + +def is_string_sequence(seq): + result = True + i = None + for i, s in enumerate(seq): + if not isinstance(s, string_types): + result = False + break + assert i is not None + return result + +PROJECT_NAME_AND_VERSION = re.compile('([a-z0-9_]+([.-][a-z_][a-z0-9_]*)*)-' + '([a-z0-9_.+-]+)', re.I) +PYTHON_VERSION = re.compile(r'-py(\d\.?\d?)') + + +def split_filename(filename, project_name=None): + """ + Extract name, version, python version from a filename (no extension) + + Return name, version, pyver or None + """ + result = None + pyver = None + filename = unquote(filename).replace(' ', '-') + m = PYTHON_VERSION.search(filename) + if m: + pyver = m.group(1) + filename = filename[:m.start()] + if project_name and len(filename) > len(project_name) + 1: + m = re.match(re.escape(project_name) + r'\b', filename) + if m: + n = m.end() + result = filename[:n], filename[n + 1:], pyver + if result is None: + m = PROJECT_NAME_AND_VERSION.match(filename) + if m: + result = m.group(1), m.group(3), pyver + return result + +# Allow spaces in name because of legacy dists like "Twisted Core" +NAME_VERSION_RE = re.compile(r'(?P<name>[\w .-]+)\s*' + r'\(\s*(?P<ver>[^\s)]+)\)$') + +def parse_name_and_version(p): + """ + A utility method used to get name and version from a string. + + From e.g. a Provides-Dist value. + + :param p: A value in a form 'foo (1.0)' + :return: The name and version as a tuple. + """ + m = NAME_VERSION_RE.match(p) + if not m: + raise DistlibException('Ill-formed name/version string: \'%s\'' % p) + d = m.groupdict() + return d['name'].strip().lower(), d['ver'] + +def get_extras(requested, available): + result = set() + requested = set(requested or []) + available = set(available or []) + if '*' in requested: + requested.remove('*') + result |= available + for r in requested: + if r == '-': + result.add(r) + elif r.startswith('-'): + unwanted = r[1:] + if unwanted not in available: + logger.warning('undeclared extra: %s' % unwanted) + if unwanted in result: + result.remove(unwanted) + else: + if r not in available: + logger.warning('undeclared extra: %s' % r) + result.add(r) + return result +# +# Extended metadata functionality +# + +def _get_external_data(url): + result = {} + try: + # urlopen might fail if it runs into redirections, + # because of Python issue #13696. Fixed in locators + # using a custom redirect handler. + resp = urlopen(url) + headers = resp.info() + ct = headers.get('Content-Type') + if not ct.startswith('application/json'): + logger.debug('Unexpected response for JSON request: %s', ct) + else: + reader = codecs.getreader('utf-8')(resp) + #data = reader.read().decode('utf-8') + #result = json.loads(data) + result = json.load(reader) + except Exception as e: + logger.exception('Failed to get external data for %s: %s', url, e) + return result + +_external_data_base_url = 'https://www.red-dove.com/pypi/projects/' + +def get_project_data(name): + url = '%s/%s/project.json' % (name[0].upper(), name) + url = urljoin(_external_data_base_url, url) + result = _get_external_data(url) + return result + +def get_package_data(name, version): + url = '%s/%s/package-%s.json' % (name[0].upper(), name, version) + url = urljoin(_external_data_base_url, url) + return _get_external_data(url) + + +class Cache(object): + """ + A class implementing a cache for resources that need to live in the file system + e.g. shared libraries. This class was moved from resources to here because it + could be used by other modules, e.g. the wheel module. + """ + + def __init__(self, base): + """ + Initialise an instance. + + :param base: The base directory where the cache should be located. + """ + # we use 'isdir' instead of 'exists', because we want to + # fail if there's a file with that name + if not os.path.isdir(base): # pragma: no cover + os.makedirs(base) + if (os.stat(base).st_mode & 0o77) != 0: + logger.warning('Directory \'%s\' is not private', base) + self.base = os.path.abspath(os.path.normpath(base)) + + def prefix_to_dir(self, prefix): + """ + Converts a resource prefix to a directory name in the cache. + """ + return path_to_cache_dir(prefix) + + def clear(self): + """ + Clear the cache. + """ + not_removed = [] + for fn in os.listdir(self.base): + fn = os.path.join(self.base, fn) + try: + if os.path.islink(fn) or os.path.isfile(fn): + os.remove(fn) + elif os.path.isdir(fn): + shutil.rmtree(fn) + except Exception: + not_removed.append(fn) + return not_removed + + +class EventMixin(object): + """ + A very simple publish/subscribe system. + """ + def __init__(self): + self._subscribers = {} + + def add(self, event, subscriber, append=True): + """ + Add a subscriber for an event. + + :param event: The name of an event. + :param subscriber: The subscriber to be added (and called when the + event is published). + :param append: Whether to append or prepend the subscriber to an + existing subscriber list for the event. + """ + subs = self._subscribers + if event not in subs: + subs[event] = deque([subscriber]) + else: + sq = subs[event] + if append: + sq.append(subscriber) + else: + sq.appendleft(subscriber) + + def remove(self, event, subscriber): + """ + Remove a subscriber for an event. + + :param event: The name of an event. + :param subscriber: The subscriber to be removed. + """ + subs = self._subscribers + if event not in subs: + raise ValueError('No subscribers: %r' % event) + subs[event].remove(subscriber) + + def get_subscribers(self, event): + """ + Return an iterator for the subscribers for an event. + :param event: The event to return subscribers for. + """ + return iter(self._subscribers.get(event, ())) + + def publish(self, event, *args, **kwargs): + """ + Publish a event and return a list of values returned by its + subscribers. + + :param event: The event to publish. + :param args: The positional arguments to pass to the event's + subscribers. + :param kwargs: The keyword arguments to pass to the event's + subscribers. + """ + result = [] + for subscriber in self.get_subscribers(event): + try: + value = subscriber(event, *args, **kwargs) + except Exception: + logger.exception('Exception during event publication') + value = None + result.append(value) + logger.debug('publish %s: args = %s, kwargs = %s, result = %s', + event, args, kwargs, result) + return result + +# +# Simple sequencing +# +class Sequencer(object): + def __init__(self): + self._preds = {} + self._succs = {} + self._nodes = set() # nodes with no preds/succs + + def add_node(self, node): + self._nodes.add(node) + + def remove_node(self, node, edges=False): + if node in self._nodes: + self._nodes.remove(node) + if edges: + for p in set(self._preds.get(node, ())): + self.remove(p, node) + for s in set(self._succs.get(node, ())): + self.remove(node, s) + # Remove empties + for k, v in list(self._preds.items()): + if not v: + del self._preds[k] + for k, v in list(self._succs.items()): + if not v: + del self._succs[k] + + def add(self, pred, succ): + assert pred != succ + self._preds.setdefault(succ, set()).add(pred) + self._succs.setdefault(pred, set()).add(succ) + + def remove(self, pred, succ): + assert pred != succ + try: + preds = self._preds[succ] + succs = self._succs[pred] + except KeyError: # pragma: no cover + raise ValueError('%r not a successor of anything' % succ) + try: + preds.remove(pred) + succs.remove(succ) + except KeyError: # pragma: no cover + raise ValueError('%r not a successor of %r' % (succ, pred)) + + def is_step(self, step): + return (step in self._preds or step in self._succs or + step in self._nodes) + + def get_steps(self, final): + if not self.is_step(final): + raise ValueError('Unknown: %r' % final) + result = [] + todo = [] + seen = set() + todo.append(final) + while todo: + step = todo.pop(0) + if step in seen: + # if a step was already seen, + # move it to the end (so it will appear earlier + # when reversed on return) ... but not for the + # final step, as that would be confusing for + # users + if step != final: + result.remove(step) + result.append(step) + else: + seen.add(step) + result.append(step) + preds = self._preds.get(step, ()) + todo.extend(preds) + return reversed(result) + + @property + def strong_connections(self): + #http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm + index_counter = [0] + stack = [] + lowlinks = {} + index = {} + result = [] + + graph = self._succs + + def strongconnect(node): + # set the depth index for this node to the smallest unused index + index[node] = index_counter[0] + lowlinks[node] = index_counter[0] + index_counter[0] += 1 + stack.append(node) + + # Consider successors + try: + successors = graph[node] + except Exception: + successors = [] + for successor in successors: + if successor not in lowlinks: + # Successor has not yet been visited + strongconnect(successor) + lowlinks[node] = min(lowlinks[node],lowlinks[successor]) + elif successor in stack: + # the successor is in the stack and hence in the current + # strongly connected component (SCC) + lowlinks[node] = min(lowlinks[node],index[successor]) + + # If `node` is a root node, pop the stack and generate an SCC + if lowlinks[node] == index[node]: + connected_component = [] + + while True: + successor = stack.pop() + connected_component.append(successor) + if successor == node: break + component = tuple(connected_component) + # storing the result + result.append(component) + + for node in graph: + if node not in lowlinks: + strongconnect(node) + + return result + + @property + def dot(self): + result = ['digraph G {'] + for succ in self._preds: + preds = self._preds[succ] + for pred in preds: + result.append(' %s -> %s;' % (pred, succ)) + for node in self._nodes: + result.append(' %s;' % node) + result.append('}') + return '\n'.join(result) + +# +# Unarchiving functionality for zip, tar, tgz, tbz, whl +# + +ARCHIVE_EXTENSIONS = ('.tar.gz', '.tar.bz2', '.tar', '.zip', + '.tgz', '.tbz', '.whl') + +def unarchive(archive_filename, dest_dir, format=None, check=True): + + def check_path(path): + if not isinstance(path, text_type): + path = path.decode('utf-8') + p = os.path.abspath(os.path.join(dest_dir, path)) + if not p.startswith(dest_dir) or p[plen] != os.sep: + raise ValueError('path outside destination: %r' % p) + + dest_dir = os.path.abspath(dest_dir) + plen = len(dest_dir) + archive = None + if format is None: + if archive_filename.endswith(('.zip', '.whl')): + format = 'zip' + elif archive_filename.endswith(('.tar.gz', '.tgz')): + format = 'tgz' + mode = 'r:gz' + elif archive_filename.endswith(('.tar.bz2', '.tbz')): + format = 'tbz' + mode = 'r:bz2' + elif archive_filename.endswith('.tar'): + format = 'tar' + mode = 'r' + else: # pragma: no cover + raise ValueError('Unknown format for %r' % archive_filename) + try: + if format == 'zip': + archive = ZipFile(archive_filename, 'r') + if check: + names = archive.namelist() + for name in names: + check_path(name) + else: + archive = tarfile.open(archive_filename, mode) + if check: + names = archive.getnames() + for name in names: + check_path(name) + if format != 'zip' and sys.version_info[0] < 3: + # See Python issue 17153. If the dest path contains Unicode, + # tarfile extraction fails on Python 2.x if a member path name + # contains non-ASCII characters - it leads to an implicit + # bytes -> unicode conversion using ASCII to decode. + for tarinfo in archive.getmembers(): + if not isinstance(tarinfo.name, text_type): + tarinfo.name = tarinfo.name.decode('utf-8') + archive.extractall(dest_dir) + + finally: + if archive: + archive.close() + + +def zip_dir(directory): + """zip a directory tree into a BytesIO object""" + result = io.BytesIO() + dlen = len(directory) + with ZipFile(result, "w") as zf: + for root, dirs, files in os.walk(directory): + for name in files: + full = os.path.join(root, name) + rel = root[dlen:] + dest = os.path.join(rel, name) + zf.write(full, dest) + return result + +# +# Simple progress bar +# + +UNITS = ('', 'K', 'M', 'G','T','P') + + +class Progress(object): + unknown = 'UNKNOWN' + + def __init__(self, minval=0, maxval=100): + assert maxval is None or maxval >= minval + self.min = self.cur = minval + self.max = maxval + self.started = None + self.elapsed = 0 + self.done = False + + def update(self, curval): + assert self.min <= curval + assert self.max is None or curval <= self.max + self.cur = curval + now = time.time() + if self.started is None: + self.started = now + else: + self.elapsed = now - self.started + + def increment(self, incr): + assert incr >= 0 + self.update(self.cur + incr) + + def start(self): + self.update(self.min) + return self + + def stop(self): + if self.max is not None: + self.update(self.max) + self.done = True + + @property + def maximum(self): + return self.unknown if self.max is None else self.max + + @property + def percentage(self): + if self.done: + result = '100 %' + elif self.max is None: + result = ' ?? %' + else: + v = 100.0 * (self.cur - self.min) / (self.max - self.min) + result = '%3d %%' % v + return result + + def format_duration(self, duration): + if (duration <= 0) and self.max is None or self.cur == self.min: + result = '??:??:??' + #elif duration < 1: + # result = '--:--:--' + else: + result = time.strftime('%H:%M:%S', time.gmtime(duration)) + return result + + @property + def ETA(self): + if self.done: + prefix = 'Done' + t = self.elapsed + #import pdb; pdb.set_trace() + else: + prefix = 'ETA ' + if self.max is None: + t = -1 + elif self.elapsed == 0 or (self.cur == self.min): + t = 0 + else: + #import pdb; pdb.set_trace() + t = float(self.max - self.min) + t /= self.cur - self.min + t = (t - 1) * self.elapsed + return '%s: %s' % (prefix, self.format_duration(t)) + + @property + def speed(self): + if self.elapsed == 0: + result = 0.0 + else: + result = (self.cur - self.min) / self.elapsed + for unit in UNITS: + if result < 1000: + break + result /= 1000.0 + return '%d %sB/s' % (result, unit) + +# +# Glob functionality +# + +RICH_GLOB = re.compile(r'\{([^}]*)\}') +_CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]') +_CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$') + + +def iglob(path_glob): + """Extended globbing function that supports ** and {opt1,opt2,opt3}.""" + if _CHECK_RECURSIVE_GLOB.search(path_glob): + msg = """invalid glob %r: recursive glob "**" must be used alone""" + raise ValueError(msg % path_glob) + if _CHECK_MISMATCH_SET.search(path_glob): + msg = """invalid glob %r: mismatching set marker '{' or '}'""" + raise ValueError(msg % path_glob) + return _iglob(path_glob) + + +def _iglob(path_glob): + rich_path_glob = RICH_GLOB.split(path_glob, 1) + if len(rich_path_glob) > 1: + assert len(rich_path_glob) == 3, rich_path_glob + prefix, set, suffix = rich_path_glob + for item in set.split(','): + for path in _iglob(''.join((prefix, item, suffix))): + yield path + else: + if '**' not in path_glob: + for item in std_iglob(path_glob): + yield item + else: + prefix, radical = path_glob.split('**', 1) + if prefix == '': + prefix = '.' + if radical == '': + radical = '*' + else: + # we support both + radical = radical.lstrip('/') + radical = radical.lstrip('\\') + for path, dir, files in os.walk(prefix): + path = os.path.normpath(path) + for fn in _iglob(os.path.join(path, radical)): + yield fn + +if ssl: + from .compat import (HTTPSHandler as BaseHTTPSHandler, match_hostname, + CertificateError) + + +# +# HTTPSConnection which verifies certificates/matches domains +# + + class HTTPSConnection(httplib.HTTPSConnection): + ca_certs = None # set this to the path to the certs file (.pem) + check_domain = True # only used if ca_certs is not None + + # noinspection PyPropertyAccess + def connect(self): + sock = socket.create_connection((self.host, self.port), self.timeout) + if getattr(self, '_tunnel_host', False): + self.sock = sock + self._tunnel() + + if not hasattr(ssl, 'SSLContext'): + # For 2.x + if self.ca_certs: + cert_reqs = ssl.CERT_REQUIRED + else: + cert_reqs = ssl.CERT_NONE + self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, + cert_reqs=cert_reqs, + ssl_version=ssl.PROTOCOL_SSLv23, + ca_certs=self.ca_certs) + else: # pragma: no cover + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.options |= ssl.OP_NO_SSLv2 + if self.cert_file: + context.load_cert_chain(self.cert_file, self.key_file) + kwargs = {} + if self.ca_certs: + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(cafile=self.ca_certs) + if getattr(ssl, 'HAS_SNI', False): + kwargs['server_hostname'] = self.host + self.sock = context.wrap_socket(sock, **kwargs) + if self.ca_certs and self.check_domain: + try: + match_hostname(self.sock.getpeercert(), self.host) + logger.debug('Host verified: %s', self.host) + except CertificateError: # pragma: no cover + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + raise + + class HTTPSHandler(BaseHTTPSHandler): + def __init__(self, ca_certs, check_domain=True): + BaseHTTPSHandler.__init__(self) + self.ca_certs = ca_certs + self.check_domain = check_domain + + def _conn_maker(self, *args, **kwargs): + """ + This is called to create a connection instance. Normally you'd + pass a connection class to do_open, but it doesn't actually check for + a class, and just expects a callable. As long as we behave just as a + constructor would have, we should be OK. If it ever changes so that + we *must* pass a class, we'll create an UnsafeHTTPSConnection class + which just sets check_domain to False in the class definition, and + choose which one to pass to do_open. + """ + result = HTTPSConnection(*args, **kwargs) + if self.ca_certs: + result.ca_certs = self.ca_certs + result.check_domain = self.check_domain + return result + + def https_open(self, req): + try: + return self.do_open(self._conn_maker, req) + except URLError as e: + if 'certificate verify failed' in str(e.reason): + raise CertificateError('Unable to verify server certificate ' + 'for %s' % req.host) + else: + raise + + # + # To prevent against mixing HTTP traffic with HTTPS (examples: A Man-In-The- + # Middle proxy using HTTP listens on port 443, or an index mistakenly serves + # HTML containing a http://xyz link when it should be https://xyz), + # you can use the following handler class, which does not allow HTTP traffic. + # + # It works by inheriting from HTTPHandler - so build_opener won't add a + # handler for HTTP itself. + # + class HTTPSOnlyHandler(HTTPSHandler, HTTPHandler): + def http_open(self, req): + raise URLError('Unexpected HTTP request on what should be a secure ' + 'connection: %s' % req) + +# +# XML-RPC with timeouts +# + +_ver_info = sys.version_info[:2] + +if _ver_info == (2, 6): + class HTTP(httplib.HTTP): + def __init__(self, host='', port=None, **kwargs): + if port == 0: # 0 means use port 0, not the default port + port = None + self._setup(self._connection_class(host, port, **kwargs)) + + + if ssl: + class HTTPS(httplib.HTTPS): + def __init__(self, host='', port=None, **kwargs): + if port == 0: # 0 means use port 0, not the default port + port = None + self._setup(self._connection_class(host, port, **kwargs)) + + +class Transport(xmlrpclib.Transport): + def __init__(self, timeout, use_datetime=0): + self.timeout = timeout + xmlrpclib.Transport.__init__(self, use_datetime) + + def make_connection(self, host): + h, eh, x509 = self.get_host_info(host) + if _ver_info == (2, 6): + result = HTTP(h, timeout=self.timeout) + else: + if not self._connection or host != self._connection[0]: + self._extra_headers = eh + self._connection = host, httplib.HTTPConnection(h) + result = self._connection[1] + return result + +if ssl: + class SafeTransport(xmlrpclib.SafeTransport): + def __init__(self, timeout, use_datetime=0): + self.timeout = timeout + xmlrpclib.SafeTransport.__init__(self, use_datetime) + + def make_connection(self, host): + h, eh, kwargs = self.get_host_info(host) + if not kwargs: + kwargs = {} + kwargs['timeout'] = self.timeout + if _ver_info == (2, 6): + result = HTTPS(host, None, **kwargs) + else: + if not self._connection or host != self._connection[0]: + self._extra_headers = eh + self._connection = host, httplib.HTTPSConnection(h, None, + **kwargs) + result = self._connection[1] + return result + + +class ServerProxy(xmlrpclib.ServerProxy): + def __init__(self, uri, **kwargs): + self.timeout = timeout = kwargs.pop('timeout', None) + # The above classes only come into play if a timeout + # is specified + if timeout is not None: + scheme, _ = splittype(uri) + use_datetime = kwargs.get('use_datetime', 0) + if scheme == 'https': + tcls = SafeTransport + else: + tcls = Transport + kwargs['transport'] = t = tcls(timeout, use_datetime=use_datetime) + self.transport = t + xmlrpclib.ServerProxy.__init__(self, uri, **kwargs) + +# +# CSV functionality. This is provided because on 2.x, the csv module can't +# handle Unicode. However, we need to deal with Unicode in e.g. RECORD files. +# + +def _csv_open(fn, mode, **kwargs): + if sys.version_info[0] < 3: + mode += 'b' + else: + kwargs['newline'] = '' + # Python 3 determines encoding from locale. Force 'utf-8' + # file encoding to match other forced utf-8 encoding + kwargs['encoding'] = 'utf-8' + return open(fn, mode, **kwargs) + + +class CSVBase(object): + defaults = { + 'delimiter': str(','), # The strs are used because we need native + 'quotechar': str('"'), # str in the csv API (2.x won't take + 'lineterminator': str('\n') # Unicode) + } + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.stream.close() + + +class CSVReader(CSVBase): + def __init__(self, **kwargs): + if 'stream' in kwargs: + stream = kwargs['stream'] + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getreader('utf-8')(stream) + self.stream = stream + else: + self.stream = _csv_open(kwargs['path'], 'r') + self.reader = csv.reader(self.stream, **self.defaults) + + def __iter__(self): + return self + + def next(self): + result = next(self.reader) + if sys.version_info[0] < 3: + for i, item in enumerate(result): + if not isinstance(item, text_type): + result[i] = item.decode('utf-8') + return result + + __next__ = next + +class CSVWriter(CSVBase): + def __init__(self, fn, **kwargs): + self.stream = _csv_open(fn, 'w') + self.writer = csv.writer(self.stream, **self.defaults) + + def writerow(self, row): + if sys.version_info[0] < 3: + r = [] + for item in row: + if isinstance(item, text_type): + item = item.encode('utf-8') + r.append(item) + row = r + self.writer.writerow(row) + +# +# Configurator functionality +# + +class Configurator(BaseConfigurator): + + value_converters = dict(BaseConfigurator.value_converters) + value_converters['inc'] = 'inc_convert' + + def __init__(self, config, base=None): + super(Configurator, self).__init__(config) + self.base = base or os.getcwd() + + def configure_custom(self, config): + def convert(o): + if isinstance(o, (list, tuple)): + result = type(o)([convert(i) for i in o]) + elif isinstance(o, dict): + if '()' in o: + result = self.configure_custom(o) + else: + result = {} + for k in o: + result[k] = convert(o[k]) + else: + result = self.convert(o) + return result + + c = config.pop('()') + if not callable(c): + c = self.resolve(c) + props = config.pop('.', None) + # Check for valid identifiers + args = config.pop('[]', ()) + if args: + args = tuple([convert(o) for o in args]) + items = [(k, convert(config[k])) for k in config if valid_ident(k)] + kwargs = dict(items) + result = c(*args, **kwargs) + if props: + for n, v in props.items(): + setattr(result, n, convert(v)) + return result + + def __getitem__(self, key): + result = self.config[key] + if isinstance(result, dict) and '()' in result: + self.config[key] = result = self.configure_custom(result) + return result + + def inc_convert(self, value): + """Default converter for the inc:// protocol.""" + if not os.path.isabs(value): + value = os.path.join(self.base, value) + with codecs.open(value, 'r', encoding='utf-8') as f: + result = json.load(f) + return result + + +class SubprocessMixin(object): + """ + Mixin for running subprocesses and capturing their output + """ + def __init__(self, verbose=False, progress=None): + self.verbose = verbose + self.progress = progress + + def reader(self, stream, context): + """ + Read lines from a subprocess' output stream and either pass to a progress + callable (if specified) or write progress information to sys.stderr. + """ + progress = self.progress + verbose = self.verbose + while True: + s = stream.readline() + if not s: + break + if progress is not None: + progress(s, context) + else: + if not verbose: + sys.stderr.write('.') + else: + sys.stderr.write(s.decode('utf-8')) + sys.stderr.flush() + stream.close() + + def run_command(self, cmd, **kwargs): + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, **kwargs) + t1 = threading.Thread(target=self.reader, args=(p.stdout, 'stdout')) + t1.start() + t2 = threading.Thread(target=self.reader, args=(p.stderr, 'stderr')) + t2.start() + p.wait() + t1.join() + t2.join() + if self.progress is not None: + self.progress('done.', 'main') + elif self.verbose: + sys.stderr.write('done.\n') + return p + + +def normalize_name(name): + """Normalize a python package name a la PEP 503""" + # https://www.python.org/dev/peps/pep-0503/#normalized-names + return re.sub('[-_.]+', '-', name).lower() diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/version.py b/venv/Lib/site-packages/pip/_vendor/distlib/version.py new file mode 100644 index 0000000..3eebe18 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/version.py @@ -0,0 +1,736 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2017 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +""" +Implementation of a flexible versioning scheme providing support for PEP-440, +setuptools-compatible and semantic versioning. +""" + +import logging +import re + +from .compat import string_types +from .util import parse_requirement + +__all__ = ['NormalizedVersion', 'NormalizedMatcher', + 'LegacyVersion', 'LegacyMatcher', + 'SemanticVersion', 'SemanticMatcher', + 'UnsupportedVersionError', 'get_scheme'] + +logger = logging.getLogger(__name__) + + +class UnsupportedVersionError(ValueError): + """This is an unsupported version.""" + pass + + +class Version(object): + def __init__(self, s): + self._string = s = s.strip() + self._parts = parts = self.parse(s) + assert isinstance(parts, tuple) + assert len(parts) > 0 + + def parse(self, s): + raise NotImplementedError('please implement in a subclass') + + def _check_compatible(self, other): + if type(self) != type(other): + raise TypeError('cannot compare %r and %r' % (self, other)) + + def __eq__(self, other): + self._check_compatible(other) + return self._parts == other._parts + + def __ne__(self, other): + return not self.__eq__(other) + + def __lt__(self, other): + self._check_compatible(other) + return self._parts < other._parts + + def __gt__(self, other): + return not (self.__lt__(other) or self.__eq__(other)) + + def __le__(self, other): + return self.__lt__(other) or self.__eq__(other) + + def __ge__(self, other): + return self.__gt__(other) or self.__eq__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + def __hash__(self): + return hash(self._parts) + + def __repr__(self): + return "%s('%s')" % (self.__class__.__name__, self._string) + + def __str__(self): + return self._string + + @property + def is_prerelease(self): + raise NotImplementedError('Please implement in subclasses.') + + +class Matcher(object): + version_class = None + + # value is either a callable or the name of a method + _operators = { + '<': lambda v, c, p: v < c, + '>': lambda v, c, p: v > c, + '<=': lambda v, c, p: v == c or v < c, + '>=': lambda v, c, p: v == c or v > c, + '==': lambda v, c, p: v == c, + '===': lambda v, c, p: v == c, + # by default, compatible => >=. + '~=': lambda v, c, p: v == c or v > c, + '!=': lambda v, c, p: v != c, + } + + # this is a method only to support alternative implementations + # via overriding + def parse_requirement(self, s): + return parse_requirement(s) + + def __init__(self, s): + if self.version_class is None: + raise ValueError('Please specify a version class') + self._string = s = s.strip() + r = self.parse_requirement(s) + if not r: + raise ValueError('Not valid: %r' % s) + self.name = r.name + self.key = self.name.lower() # for case-insensitive comparisons + clist = [] + if r.constraints: + # import pdb; pdb.set_trace() + for op, s in r.constraints: + if s.endswith('.*'): + if op not in ('==', '!='): + raise ValueError('\'.*\' not allowed for ' + '%r constraints' % op) + # Could be a partial version (e.g. for '2.*') which + # won't parse as a version, so keep it as a string + vn, prefix = s[:-2], True + # Just to check that vn is a valid version + self.version_class(vn) + else: + # Should parse as a version, so we can create an + # instance for the comparison + vn, prefix = self.version_class(s), False + clist.append((op, vn, prefix)) + self._parts = tuple(clist) + + def match(self, version): + """ + Check if the provided version matches the constraints. + + :param version: The version to match against this instance. + :type version: String or :class:`Version` instance. + """ + if isinstance(version, string_types): + version = self.version_class(version) + for operator, constraint, prefix in self._parts: + f = self._operators.get(operator) + if isinstance(f, string_types): + f = getattr(self, f) + if not f: + msg = ('%r not implemented ' + 'for %s' % (operator, self.__class__.__name__)) + raise NotImplementedError(msg) + if not f(version, constraint, prefix): + return False + return True + + @property + def exact_version(self): + result = None + if len(self._parts) == 1 and self._parts[0][0] in ('==', '==='): + result = self._parts[0][1] + return result + + def _check_compatible(self, other): + if type(self) != type(other) or self.name != other.name: + raise TypeError('cannot compare %s and %s' % (self, other)) + + def __eq__(self, other): + self._check_compatible(other) + return self.key == other.key and self._parts == other._parts + + def __ne__(self, other): + return not self.__eq__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + def __hash__(self): + return hash(self.key) + hash(self._parts) + + def __repr__(self): + return "%s(%r)" % (self.__class__.__name__, self._string) + + def __str__(self): + return self._string + + +PEP440_VERSION_RE = re.compile(r'^v?(\d+!)?(\d+(\.\d+)*)((a|b|c|rc)(\d+))?' + r'(\.(post)(\d+))?(\.(dev)(\d+))?' + r'(\+([a-zA-Z\d]+(\.[a-zA-Z\d]+)?))?$') + + +def _pep_440_key(s): + s = s.strip() + m = PEP440_VERSION_RE.match(s) + if not m: + raise UnsupportedVersionError('Not a valid version: %s' % s) + groups = m.groups() + nums = tuple(int(v) for v in groups[1].split('.')) + while len(nums) > 1 and nums[-1] == 0: + nums = nums[:-1] + + if not groups[0]: + epoch = 0 + else: + epoch = int(groups[0]) + pre = groups[4:6] + post = groups[7:9] + dev = groups[10:12] + local = groups[13] + if pre == (None, None): + pre = () + else: + pre = pre[0], int(pre[1]) + if post == (None, None): + post = () + else: + post = post[0], int(post[1]) + if dev == (None, None): + dev = () + else: + dev = dev[0], int(dev[1]) + if local is None: + local = () + else: + parts = [] + for part in local.split('.'): + # to ensure that numeric compares as > lexicographic, avoid + # comparing them directly, but encode a tuple which ensures + # correct sorting + if part.isdigit(): + part = (1, int(part)) + else: + part = (0, part) + parts.append(part) + local = tuple(parts) + if not pre: + # either before pre-release, or final release and after + if not post and dev: + # before pre-release + pre = ('a', -1) # to sort before a0 + else: + pre = ('z',) # to sort after all pre-releases + # now look at the state of post and dev. + if not post: + post = ('_',) # sort before 'a' + if not dev: + dev = ('final',) + + #print('%s -> %s' % (s, m.groups())) + return epoch, nums, pre, post, dev, local + + +_normalized_key = _pep_440_key + + +class NormalizedVersion(Version): + """A rational version. + + Good: + 1.2 # equivalent to "1.2.0" + 1.2.0 + 1.2a1 + 1.2.3a2 + 1.2.3b1 + 1.2.3c1 + 1.2.3.4 + TODO: fill this out + + Bad: + 1 # minimum two numbers + 1.2a # release level must have a release serial + 1.2.3b + """ + def parse(self, s): + result = _normalized_key(s) + # _normalized_key loses trailing zeroes in the release + # clause, since that's needed to ensure that X.Y == X.Y.0 == X.Y.0.0 + # However, PEP 440 prefix matching needs it: for example, + # (~= 1.4.5.0) matches differently to (~= 1.4.5.0.0). + m = PEP440_VERSION_RE.match(s) # must succeed + groups = m.groups() + self._release_clause = tuple(int(v) for v in groups[1].split('.')) + return result + + PREREL_TAGS = set(['a', 'b', 'c', 'rc', 'dev']) + + @property + def is_prerelease(self): + return any(t[0] in self.PREREL_TAGS for t in self._parts if t) + + +def _match_prefix(x, y): + x = str(x) + y = str(y) + if x == y: + return True + if not x.startswith(y): + return False + n = len(y) + return x[n] == '.' + + +class NormalizedMatcher(Matcher): + version_class = NormalizedVersion + + # value is either a callable or the name of a method + _operators = { + '~=': '_match_compatible', + '<': '_match_lt', + '>': '_match_gt', + '<=': '_match_le', + '>=': '_match_ge', + '==': '_match_eq', + '===': '_match_arbitrary', + '!=': '_match_ne', + } + + def _adjust_local(self, version, constraint, prefix): + if prefix: + strip_local = '+' not in constraint and version._parts[-1] + else: + # both constraint and version are + # NormalizedVersion instances. + # If constraint does not have a local component, + # ensure the version doesn't, either. + strip_local = not constraint._parts[-1] and version._parts[-1] + if strip_local: + s = version._string.split('+', 1)[0] + version = self.version_class(s) + return version, constraint + + def _match_lt(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if version >= constraint: + return False + release_clause = constraint._release_clause + pfx = '.'.join([str(i) for i in release_clause]) + return not _match_prefix(version, pfx) + + def _match_gt(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if version <= constraint: + return False + release_clause = constraint._release_clause + pfx = '.'.join([str(i) for i in release_clause]) + return not _match_prefix(version, pfx) + + def _match_le(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + return version <= constraint + + def _match_ge(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + return version >= constraint + + def _match_eq(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if not prefix: + result = (version == constraint) + else: + result = _match_prefix(version, constraint) + return result + + def _match_arbitrary(self, version, constraint, prefix): + return str(version) == str(constraint) + + def _match_ne(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if not prefix: + result = (version != constraint) + else: + result = not _match_prefix(version, constraint) + return result + + def _match_compatible(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if version == constraint: + return True + if version < constraint: + return False +# if not prefix: +# return True + release_clause = constraint._release_clause + if len(release_clause) > 1: + release_clause = release_clause[:-1] + pfx = '.'.join([str(i) for i in release_clause]) + return _match_prefix(version, pfx) + +_REPLACEMENTS = ( + (re.compile('[.+-]$'), ''), # remove trailing puncts + (re.compile(r'^[.](\d)'), r'0.\1'), # .N -> 0.N at start + (re.compile('^[.-]'), ''), # remove leading puncts + (re.compile(r'^\((.*)\)$'), r'\1'), # remove parentheses + (re.compile(r'^v(ersion)?\s*(\d+)'), r'\2'), # remove leading v(ersion) + (re.compile(r'^r(ev)?\s*(\d+)'), r'\2'), # remove leading v(ersion) + (re.compile('[.]{2,}'), '.'), # multiple runs of '.' + (re.compile(r'\b(alfa|apha)\b'), 'alpha'), # misspelt alpha + (re.compile(r'\b(pre-alpha|prealpha)\b'), + 'pre.alpha'), # standardise + (re.compile(r'\(beta\)$'), 'beta'), # remove parentheses +) + +_SUFFIX_REPLACEMENTS = ( + (re.compile('^[:~._+-]+'), ''), # remove leading puncts + (re.compile('[,*")([\\]]'), ''), # remove unwanted chars + (re.compile('[~:+_ -]'), '.'), # replace illegal chars + (re.compile('[.]{2,}'), '.'), # multiple runs of '.' + (re.compile(r'\.$'), ''), # trailing '.' +) + +_NUMERIC_PREFIX = re.compile(r'(\d+(\.\d+)*)') + + +def _suggest_semantic_version(s): + """ + Try to suggest a semantic form for a version for which + _suggest_normalized_version couldn't come up with anything. + """ + result = s.strip().lower() + for pat, repl in _REPLACEMENTS: + result = pat.sub(repl, result) + if not result: + result = '0.0.0' + + # Now look for numeric prefix, and separate it out from + # the rest. + #import pdb; pdb.set_trace() + m = _NUMERIC_PREFIX.match(result) + if not m: + prefix = '0.0.0' + suffix = result + else: + prefix = m.groups()[0].split('.') + prefix = [int(i) for i in prefix] + while len(prefix) < 3: + prefix.append(0) + if len(prefix) == 3: + suffix = result[m.end():] + else: + suffix = '.'.join([str(i) for i in prefix[3:]]) + result[m.end():] + prefix = prefix[:3] + prefix = '.'.join([str(i) for i in prefix]) + suffix = suffix.strip() + if suffix: + #import pdb; pdb.set_trace() + # massage the suffix. + for pat, repl in _SUFFIX_REPLACEMENTS: + suffix = pat.sub(repl, suffix) + + if not suffix: + result = prefix + else: + sep = '-' if 'dev' in suffix else '+' + result = prefix + sep + suffix + if not is_semver(result): + result = None + return result + + +def _suggest_normalized_version(s): + """Suggest a normalized version close to the given version string. + + If you have a version string that isn't rational (i.e. NormalizedVersion + doesn't like it) then you might be able to get an equivalent (or close) + rational version from this function. + + This does a number of simple normalizations to the given string, based + on observation of versions currently in use on PyPI. Given a dump of + those version during PyCon 2009, 4287 of them: + - 2312 (53.93%) match NormalizedVersion without change + with the automatic suggestion + - 3474 (81.04%) match when using this suggestion method + + @param s {str} An irrational version string. + @returns A rational version string, or None, if couldn't determine one. + """ + try: + _normalized_key(s) + return s # already rational + except UnsupportedVersionError: + pass + + rs = s.lower() + + # part of this could use maketrans + for orig, repl in (('-alpha', 'a'), ('-beta', 'b'), ('alpha', 'a'), + ('beta', 'b'), ('rc', 'c'), ('-final', ''), + ('-pre', 'c'), + ('-release', ''), ('.release', ''), ('-stable', ''), + ('+', '.'), ('_', '.'), (' ', ''), ('.final', ''), + ('final', '')): + rs = rs.replace(orig, repl) + + # if something ends with dev or pre, we add a 0 + rs = re.sub(r"pre$", r"pre0", rs) + rs = re.sub(r"dev$", r"dev0", rs) + + # if we have something like "b-2" or "a.2" at the end of the + # version, that is probably beta, alpha, etc + # let's remove the dash or dot + rs = re.sub(r"([abc]|rc)[\-\.](\d+)$", r"\1\2", rs) + + # 1.0-dev-r371 -> 1.0.dev371 + # 0.1-dev-r79 -> 0.1.dev79 + rs = re.sub(r"[\-\.](dev)[\-\.]?r?(\d+)$", r".\1\2", rs) + + # Clean: 2.0.a.3, 2.0.b1, 0.9.0~c1 + rs = re.sub(r"[.~]?([abc])\.?", r"\1", rs) + + # Clean: v0.3, v1.0 + if rs.startswith('v'): + rs = rs[1:] + + # Clean leading '0's on numbers. + #TODO: unintended side-effect on, e.g., "2003.05.09" + # PyPI stats: 77 (~2%) better + rs = re.sub(r"\b0+(\d+)(?!\d)", r"\1", rs) + + # Clean a/b/c with no version. E.g. "1.0a" -> "1.0a0". Setuptools infers + # zero. + # PyPI stats: 245 (7.56%) better + rs = re.sub(r"(\d+[abc])$", r"\g<1>0", rs) + + # the 'dev-rNNN' tag is a dev tag + rs = re.sub(r"\.?(dev-r|dev\.r)\.?(\d+)$", r".dev\2", rs) + + # clean the - when used as a pre delimiter + rs = re.sub(r"-(a|b|c)(\d+)$", r"\1\2", rs) + + # a terminal "dev" or "devel" can be changed into ".dev0" + rs = re.sub(r"[\.\-](dev|devel)$", r".dev0", rs) + + # a terminal "dev" can be changed into ".dev0" + rs = re.sub(r"(?![\.\-])dev$", r".dev0", rs) + + # a terminal "final" or "stable" can be removed + rs = re.sub(r"(final|stable)$", "", rs) + + # The 'r' and the '-' tags are post release tags + # 0.4a1.r10 -> 0.4a1.post10 + # 0.9.33-17222 -> 0.9.33.post17222 + # 0.9.33-r17222 -> 0.9.33.post17222 + rs = re.sub(r"\.?(r|-|-r)\.?(\d+)$", r".post\2", rs) + + # Clean 'r' instead of 'dev' usage: + # 0.9.33+r17222 -> 0.9.33.dev17222 + # 1.0dev123 -> 1.0.dev123 + # 1.0.git123 -> 1.0.dev123 + # 1.0.bzr123 -> 1.0.dev123 + # 0.1a0dev.123 -> 0.1a0.dev123 + # PyPI stats: ~150 (~4%) better + rs = re.sub(r"\.?(dev|git|bzr)\.?(\d+)$", r".dev\2", rs) + + # Clean '.pre' (normalized from '-pre' above) instead of 'c' usage: + # 0.2.pre1 -> 0.2c1 + # 0.2-c1 -> 0.2c1 + # 1.0preview123 -> 1.0c123 + # PyPI stats: ~21 (0.62%) better + rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs) + + # Tcl/Tk uses "px" for their post release markers + rs = re.sub(r"p(\d+)$", r".post\1", rs) + + try: + _normalized_key(rs) + except UnsupportedVersionError: + rs = None + return rs + +# +# Legacy version processing (distribute-compatible) +# + +_VERSION_PART = re.compile(r'([a-z]+|\d+|[\.-])', re.I) +_VERSION_REPLACE = { + 'pre': 'c', + 'preview': 'c', + '-': 'final-', + 'rc': 'c', + 'dev': '@', + '': None, + '.': None, +} + + +def _legacy_key(s): + def get_parts(s): + result = [] + for p in _VERSION_PART.split(s.lower()): + p = _VERSION_REPLACE.get(p, p) + if p: + if '0' <= p[:1] <= '9': + p = p.zfill(8) + else: + p = '*' + p + result.append(p) + result.append('*final') + return result + + result = [] + for p in get_parts(s): + if p.startswith('*'): + if p < '*final': + while result and result[-1] == '*final-': + result.pop() + while result and result[-1] == '00000000': + result.pop() + result.append(p) + return tuple(result) + + +class LegacyVersion(Version): + def parse(self, s): + return _legacy_key(s) + + @property + def is_prerelease(self): + result = False + for x in self._parts: + if (isinstance(x, string_types) and x.startswith('*') and + x < '*final'): + result = True + break + return result + + +class LegacyMatcher(Matcher): + version_class = LegacyVersion + + _operators = dict(Matcher._operators) + _operators['~='] = '_match_compatible' + + numeric_re = re.compile(r'^(\d+(\.\d+)*)') + + def _match_compatible(self, version, constraint, prefix): + if version < constraint: + return False + m = self.numeric_re.match(str(constraint)) + if not m: + logger.warning('Cannot compute compatible match for version %s ' + ' and constraint %s', version, constraint) + return True + s = m.groups()[0] + if '.' in s: + s = s.rsplit('.', 1)[0] + return _match_prefix(version, s) + +# +# Semantic versioning +# + +_SEMVER_RE = re.compile(r'^(\d+)\.(\d+)\.(\d+)' + r'(-[a-z0-9]+(\.[a-z0-9-]+)*)?' + r'(\+[a-z0-9]+(\.[a-z0-9-]+)*)?$', re.I) + + +def is_semver(s): + return _SEMVER_RE.match(s) + + +def _semantic_key(s): + def make_tuple(s, absent): + if s is None: + result = (absent,) + else: + parts = s[1:].split('.') + # We can't compare ints and strings on Python 3, so fudge it + # by zero-filling numeric values so simulate a numeric comparison + result = tuple([p.zfill(8) if p.isdigit() else p for p in parts]) + return result + + m = is_semver(s) + if not m: + raise UnsupportedVersionError(s) + groups = m.groups() + major, minor, patch = [int(i) for i in groups[:3]] + # choose the '|' and '*' so that versions sort correctly + pre, build = make_tuple(groups[3], '|'), make_tuple(groups[5], '*') + return (major, minor, patch), pre, build + + +class SemanticVersion(Version): + def parse(self, s): + return _semantic_key(s) + + @property + def is_prerelease(self): + return self._parts[1][0] != '|' + + +class SemanticMatcher(Matcher): + version_class = SemanticVersion + + +class VersionScheme(object): + def __init__(self, key, matcher, suggester=None): + self.key = key + self.matcher = matcher + self.suggester = suggester + + def is_valid_version(self, s): + try: + self.matcher.version_class(s) + result = True + except UnsupportedVersionError: + result = False + return result + + def is_valid_matcher(self, s): + try: + self.matcher(s) + result = True + except UnsupportedVersionError: + result = False + return result + + def is_valid_constraint_list(self, s): + """ + Used for processing some metadata fields + """ + return self.is_valid_matcher('dummy_name (%s)' % s) + + def suggest(self, s): + if self.suggester is None: + result = None + else: + result = self.suggester(s) + return result + +_SCHEMES = { + 'normalized': VersionScheme(_normalized_key, NormalizedMatcher, + _suggest_normalized_version), + 'legacy': VersionScheme(_legacy_key, LegacyMatcher, lambda self, s: s), + 'semantic': VersionScheme(_semantic_key, SemanticMatcher, + _suggest_semantic_version), +} + +_SCHEMES['default'] = _SCHEMES['normalized'] + + +def get_scheme(name): + if name not in _SCHEMES: + raise ValueError('unknown scheme name: %r' % name) + return _SCHEMES[name] diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/w32.exe b/venv/Lib/site-packages/pip/_vendor/distlib/w32.exe new file mode 100644 index 0000000000000000000000000000000000000000..732215a9d34ccb7b417d637a7646d9b843ecafa8 GIT binary patch literal 89088 zcmeFae|S?>wm*FGqitH!CO`{C3REl(SahnPNDYM`O{q#T7)Xdvz$<hz5gjb&0FD$| zPs=nt#$jgk-tk`NUb*PJIy$3wMg@OiORy=5PEkZ0P^;6cyPm2=Y8#~Xyq~pC(iU~@ z_dd`2ywCg3TS)e=wSTX@_S$Q$y;FJf9>F9Cf*Bu86NCeB>CeT#|L3nblC!40kR?2m z{>H@z3`^g*ct!B1Tk<#8{Ol(+x7?n8>n(TO@iQ_1pEl;#NO$D_^p5<p8@>6r|7^p? zD>5@P3KB)%InlPUc<U{5lb_$uJ2!U@!dIxT&CTYnHuoCtzCL#`-1%8w%`JjkymkHD zpYr&>Cg9H}+(GW%^wV2|ROUbGfyXTfOART)i?<!WISJD#7!6|#8G`TvV*Xu^z32+K zc6>T0?9%;4K}Zn{6fx`yPa}*$9C+R!7zI~72c&$InY+UdMGBkF3c`HyxD3K09`bzW z?_q;rO&5ec#{?noJ4vI19bbHBt~vx^h2FH$V8i|^#EsiUg#JboP3@w-(&Uf&%NK<8 zSJZ5{MZ852W)~s>WeT(LIf&1wKNqULLI)GN_(-E-D)X~ZK=1;t<%*guHMhdg`-(mb zHzDv1KBN9zR9?--O+N$ReOXAr81V9z!X5SJ5`=3<1^<8V|AP@&sr2}Qvp;hQT24iW zOHg|EiZd1ojV;oo#(r^ba2`^8T22{~_UQ@YMZp7O1R*2@?SerFE~TuJB_wDaYC0h8 zfONF1t%{=H`W`bdYp+>YBsg9Ty9ec3iy+O3xa}TIvPK#Q&udyx1MvwG0(#iaGC|N| zJ#3?<Y4YLRkU`54s9BYRjyISA>){9$tW;Y34lPnX=&>D4X~|k7c$TwEfOzs@Yh#Nz z`FV;`(w!E`sKg@`2E}bDY>ku^4XS@tV(WO*<eu67;0Clk;vRHp!Qf<+5w0B!*Y>g? zYH=KK#%%Yu1~&m>IlB^#2^syGG|2AB1(}4aOcaC%!)}%`I7AIC2(Ro3yW`GSttng^ z_xb=ECor!L{-PNO>_ulJ3%fm=O0X!s%)$GZ?~I94l-^KEAX0n$?4wGpr7&i4#~)OB zQD%2NrWUKtZuYT1Vnu}AeF`cSgx>Rkk@}Lg{WltgT76VeA2aic`cTnpXrt2WXmJkM z9%u<Rp-*9{$HQ)>Xm?McyDyZ28Ux7mpxy?mnmvzMMr-85vkRrJLaDRx>|I7je+cM+ zj{RJ(3Vrgke;W@#D!y%U%fQLtlPKTAzWtVuOQdXpwsy6eRjt^cZ%0D4bF7$F;f!th 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ðeLyi^XKZ@T)O?Rs!5wIFTl%?8Q=3bg%o36c5r4;Y4ycajn+NC8bv2_T% zc!;Bj2`||-6#*wY>mNg{QObQ%LH^V5KzX&$Pj)C`PL3m39-<OsbezPe4z0%YKtX;k zy@yv8<mcf=ucxwz`Wh7sr@7-sljc~dk}6a&;;=1oXnjl|eu5o(i>WU#AYJkuSR$}j znMO4xV6<u<CkKww!%mu%Tg-Yt11EZ7xLh4zM0J3`KDL&6fr2{d0&*;_l!~N4!>4cq z?#p`Ho!Qv73km`iByTbY#cV0wK_smZcB=-yCveh*Mk_?9O%&WfgkP=#c#PeZe;5gR zFU#jKce_>dkCGInEfEF<jR>avwoHFFzO^=6+i46rza4!c)3)K2W!N-jc;#bdt-wdV zbbkzu=Ar69H{~D7X}i!Pza+cmg(LPu;=<8t^7QJo3OHYK9I;_Rwaz_5IFuxGuq&N> zL5WaJsc`IUvfI|6i$c24cGjUvSt{j}Uy{>+-qt=2Rm-si0jtOTK#qO8cflG>FmU+N zMgPSO+!arkH6LA8To`%QYu7LxG4KKBhA7sv7_8GFS`8iha{axnw@X#~9qM<Nn|Ekg zXjHP@4?`WSSjYV2&t6Ky9mF|!O7&Ii08RCkHH^wd==z}u`->J<%gSE-EV2U6X{ghx z8`(M#MS&OAMp|+8DeJ)R&-5p+GJGZjgPi6EJ<t9Hd8f7FUWx`Uy=O<t&u?#v@4hR_ zI=Qwk8_i>!?S4T)7gIS%>@6&FPRJsr<tToVziU)urQ{{HP^l1S2Sm^W%oB&@_SJ4Z zka#ikDQmAaZRo*zk8y52S2OXi?ZuR9U`ZZZZP+Mfj-!a2F34YnUwq3V3waZp+MqD< z9I{--2N>d<U-m{hQnT9z`K!0qzOsnEH853iNQ^UEWzDp(K-l(N*)4W;=!-1tphZCa zX1+R6SUt&pyyAmcI6x-~^OPD<S8(mCvo#4jVK07~Sy@}jBHlRtmLkA<^=^_w^(&UU zFH?O0SWZk@h4neV{6J}v;{V$!ax;4|ZyK8N3xQ7^U2IKtS55D~yofVt82qh%AXKbe z9V$uy9sdU77QhO43ve2K`hbW#$9Q<*V3rF9ZrS5_x4S4ue|Aq+W5@PM58{JGk8V5r zu=eMRvQjl=Jr?J{svQS0mxs||JXV=jIzQ`xg4mDg;oY{D&Mue?)xKVE>~?%w7EL7= zQ;A&zxDfQ5HVcS=t+d5cs7#Gwovk>NTDJdc3$1>`d?D7*^0Zd0ZrJm4sBn5923Lwr zJ%(+VFmA#S0iP_rfq7oLSvc3g)p6MA_^5O(Y5_jA!L$QE56$Y}T&0JzRfTi5!)@6? zwN#`qNDVKrBvku!8N-25{XOho;?w0==S0w2;}Z%@P3qIPTubmNl_a-mI#w{@WLbM> ztLhwX(lXUhHWo}YWd)5q=9qBOIT!xwV9viW3vJl=#wpx8<B@3fz)mj!te-E)_x*;A zlb`za#;-pXOL2bx6||^ea%bmrm~F*2U@PG0L$!Xm<+#j&AR5Cj#}#(eK2mkpj-gvV z%CB3!y$3E9tm%d{mlcNK7at#5vG0VNttV_R7Y=e^cqiuwrUicYsZRdDBj}xWLfYpe z{IK7SiHFK^3-5Oz#bC@tXE1uM=>3}V9)o7(DX<Maa!q+F@Zii^i@F<{h65W%pkXv4 zSA1X)>u{I84IMwpHoY+M&aaS7d3Pan;7``3ehM{HUM!wvn{M!TtWhjck+F4P{%vFm zb_=-o7W8Kd@<Z{sJfHNSYn?v>>T!7!M-i#?DZ%C!T#%XH5l@Z=?z`>tp_|(*kM&(P z;oP#AU+j1JVVM?HP}xY+JKJNO2QZ%DCl3LRLgwl9fqM*A_lE}VG2Oc!2_tNN1m5El zgz+2&;nQ!fUiuq)ec@6eQg#!*SI`2*I=x-Oa5E52@}$-;j5!AzVPPQ?8VmhIp6IbP zLxlF>%Me7s76yviSgg4|7wgC;Q4RstY1<5w<Bbu_p|W-qQd-=%7vxiR{ENsY(~9%x zUD}IDKe`88Dpr*y7xq;rx-0Trvl<C+QW2Smn`y;HJji^eFIDQLcBB-9oxX$W*}9mv z_+V?E%i#2O?$)4?saH@$`U(n}vmS7qRPUS}x`WBdfr^u+{3zz560N7>X}uL&C&U&g zf9DU~Tmy3up<$q519j!E@-r>H2w^PsgukQt@>W`yW#di0j^+gsCcn}%kS3qK6iq*h z=2|@F7E@->+b5zJOR?4}N%O~6C-?q_5RMOg&U-0AD9j&$K-dE%yT|fY*sEj>3oZ8& zwkOf>*d>~U4#OVA!<Jl@t+4jxV@J6b#S7bO6z2CrXq2H!r0!x2XLc%_o8krLA65kN z-A*s15U{m^X=xhRU)sG2b?kY=0(C)p1pMslSs{SChB-x+xZg?S!%bUSxgU4mr{Ql0 z{vF(kSgNVbCMOrPb11UhcexcY3GFwzpoROcCB=MknUuZpM=47|y{S!EBHy`EHk_1= zR3WvxuIf0;!N!+`eAEeZA?u<}R-oe{CG1;na8pw6zK^8g#4-|fRdO~!p=Fi$oS!&x z=7eS#DHfJv(dyX3!VyT;Yr7i!#X7d*1Xpc$6q*Qm#IPyv5xo|vQUw{iVhi&IGICu- zYi;UY#DIavb3Q18&5Yl}Yod)eQyi*#YelTpTUF3az>cyI;KG>PoJSUsEz;M|sQ5T+ zM@fr)J*5{Zvd-5`->%vaVN0_Yt1#9+*igh@RY7d5G#Z8otvPj0#BG6hS%_Xe@4(}v zvi^p)p)wCeUq*>xu3rabXzb=}uHttDE1Wmt)^1kcQ8jfaM7)%$pw$#-*xl(oZDj`r zUnMx%%+oMZ^?l?~SOyT+xecy{4%;!BscL*5`I;W=(W+q1K-^x>_mK;3fC6#-K2mSl z<DBUgY`&UmyN(1`r#wu{tgi1RpM#h7z2tT#t2OA_Ns(%&c4sV1tvI{SvmlJSb~dv| zHTB#b){fn+vX*%8C=6>2FT6rc6^<}Zdm_Huchgrt>U|!Qa}(?X8aKEU(xsbogY)4k zFQ-3l%Z?j!sQR)?Or>I17B>(3$xvfWrT%e`T9LpFTAT7T=y+2;_xH!AmTv*$t;YOq z--*sw`GV84T_-x9!@cX)V-MrGdh0Pv*6v&$+O1no5cYp^f}n{`n-*DGkx+_DLQ`uc z6myk?@NMQ(Dxuq45;D6a)Vfwe9#<rUJppsAQ)!)#JeOKOmR}(`m){J<s3wdWzJcl@ z%BJGe$VKIgy|D}0{2LtDaBRpq?2gr3-w~}jSUBVyw!bdKXM)<cdV7WkE-GJmAQ@#; zX0{LWxIDj^150(WWu%+ELA|nO+>jIza*+{sp@raU`~T0O<g0!j_KHI%sQ!2?^+7{v zY~PP{1gc25XPUK*|I`-N7JfH#<8V)`%g`3G;L~Y*v!S}&<<79Vs63J7{<pWb-K+&_ zVA|Xq%UxVI7Dtbz>;uwm+Kkhq-vvMUS-N!*w;EXX5+3ar-4=I8Gq=#}bI5-x776bd z2Y!v6nA%8YVuO4xJIOCWXfB&+5vZbMnf>l`!%E*=#z$tRyksaOeXoGhJ8<v(74`&e zw<=d;Sf2yo&2=nXjvR}ekTgyKuaTQ6KLCNs-clnHuDDj~HqiU#x6CNIYGKCG1FpLm zY(uNP8LMSt${huJfE0F42<t9#(XhL2W1z3JV2YmVXbc`wZdi^qcOsVqHz~38f>Dd7 z5H|HWbm1q*5!`vgEqCm^kc9Vp9J}bcA1oV=`zd(CZG8~tVClin4}O2}NRz#`a~IA! z$`J~N4fwRU@U8p7tmc6TH_tQ66o{~M8;$mY$4%qAVbfdOdE&0=g`=22IVv3Q<FLig zn9rR%T4)=6Vh;^krsl&k)q#9-dSPB@(xf*j!pYdWr#=>BV>w_0ojKPqNQD~)sezCh z(-vB!VzHk**RJ4G(R#2+VGm&%rCOwh;QeB}pQNl(2kNp)%}1xRdhnvwe!)Tss}zb2 zYrP5!U&nlQLD6CUt~tfu{Fl^kh59|Be$T1jW9oOK`aOVOXF@i9HaZyRNjxj7)LK01 zL!AiR<`$R0M_Hxb#*JZ>`T-M}uozvo?l)~|i%B(j6%?ITzwfHw4g7uRBz}_vKQZM! zi|{zG&i!IR@zZ$cuVR>1%*SikjIa#@twvjFTaAjCmS#2bcC{MuHLXTt$kK|2ru&*9 z9oUS#He-YXn-R$cO^?DU$k*uA_@Hq>;|w`adJ@A1<Xh8bgm26=o6%<YHY#H9(k&cc z6!O-i^u?-sx6-F@zeK%9Z4jla7(+dl=HoWm4x5pe16z)H7==SbFqkMvu1W+ZBfc!< zFbC5(xTS$-i*7PfUj7Wco@9yAK1RCTYotq0rpuFIbq<u)Qz_ENHAOy`e#iND5MB|5 zZJ#P;T0`v>uo~f<Zyc`;FO4K+H7dqqvOOft*3$i={u)he>2r9kF{DN5<5l<O(v`S3 zZNWM0gKX`S?K$;)Z8u=nQ;!H9mCk113LnjPfabft0yxPW;5A11!d5g)-RQ<5PTuGF zsy8qfd8r%Ni@K^C7(CkZW<A9M96StXT%nS%-T5qKw7tUFmeskmoOU0Q4ZBZ&^b1UR z+wiOGK3r77L&#zGfdJeAz`2U1{4X)^wbSnNsGgE0)ea?O8UsQLi!0%&P0tsX;}!=I z;}`om7OX__yYtC%bb&?baXengC$*ZjXfIxBiI_`^abN9cYpB0>6f-t9qq5cCMkYa) z&Z`v6nn*b&R~D>crl!1$&=pve*+jRN@TGk{F%ga;KER~7vL)!@r{y@92zQ$LfcYJk z_lB3K@X&Z+69TCS@GoZMg$GKHAz@`%IsyD?ReFecI~$tv&Y{U5o8CeQ(-sQ4&-q~n zenkfm-4Og|ac>>EEPoH&!u;3qi#|LZY+Hz5AvdzQD^&iNrX)vkRHW)RVvcfwsi0^h zUX?XWE>M12h=P35rKz6MLBPSKPN=yBWpnb;*ji<CVjSxY7<N?4N9j)-8?PtAqVzl5 zyYMj9+2#<1=KzDy;`9wf1tWa?+ACmjdPlv7#pydv(!k=BPK#4FWpR22ei}P+3yP=? zt<&_n08W-otWpdpMQ7**RfY5Bqabt^DmxRkEA}m<ok=NL_uWdVQLIZZuw<0NfrW|b zLlf#!Byx%6V+iJc+!0>kh^s997!S4^pp|CW;x(ycB4r~3_uw=jC8dr%OD?X%yTyYG zH)*z^Qhd1UP?{XJA*yxo6}F*jzGDs?wjl~BBRbr5+t5y=xC$>F;jj%oh#S4oVFkj2 zvJEkgCLWlZp{NzA81b&Qp*5^5v<-a?a+Ga|vG6&Io*WLVtF{d#+l$Eq8izi24dG%O z3Q@)(CQ4ht@B&p|<4fB^jth#I^lsIDLQjaYZpuJ(4>Sj5{z>+ZXdv2$h+3$g2&I>< z#(ii;@O&Xcrg~#v%lr?KphG2SFByyHeR#%M>G&<>tfVfNh-J#>b9LRecGS`h)%Zg@ zQMj@bg;zdS)>iieYYkhGYWnrp*1GIP2E}@ot58x^_9Dem+KYIjBm(v#MlkF}d`~MP zUBg~PA_|Lsg~&V9d#N;{tI3E(p_z>8p9gI;8LeOfN2*LlFX2%$8Rat}Wi?WWqH<_D zv_L5?>zxp~hK^mie%{tNSxDJ)C<)Z`)Gpy&7NiuPW*x8gudd)_jm9Y3QUmwxFeaV# zQqd1qXt~&nHzG+C!Uj<9P}&tAVSC}@42YaZYM-*^*v;4@)vK%lcBO8eYMzu_P{^X) zolh>1V~*k@xbsp9)2o<o3zR5sXlm<LcC*$frJA)?X^eBZGEuNHV*vwgM8{<j<G_%@ z-!+HvTd0HwnvlVq_bD3@c|#jfKdq!aY&{$*m$DA2-dfs@onxH0)lU5@d+`Cp6t-E8 zHK%cd?HChbbj4i_MQmtbYU=pdk<MK|v*w|$v(#W%>L!cmr4gfL=PZF)#l4sTCgp#g zv$3o%R_x$GmUY)^v__FsrD)Kl2->XMhxi~C`sf5%d+P*U!LoRShh>v#>nyVuZ(v*+ zh<M{{I};(+t9O$es+;LhwF12n?4PHUV{+gwh|z)4IpC1L>Rn>qpqTvzScu&4vX1Vq zrgW=@nU3kYg=ojiIGp#zP;m}0=A}^a1mJnV4}eqf)4p)PNHrdl3X?S3N%mOnV_KR< z3^R!dTU6SEnoHY2{fIiQJhR<w$Ck=1xc0=>DlYY0nP*(;FJ<m=spoO#A7{rJ7IbXj zQ!=^iMrU~rJM8G`31<av?PksnX}c0(wI1e2w9g_wSU5Sb8SOXOagObnXK?(aaL^1F z+z9-<+9!69J;+;Hy^YiII(dCyZ4qVljJgUI_NEOm+AM41Os&Qu3%A%2!FBI<41{Q5 zYUiA&VdxC33XX6!rd>KX2k2qiR)ulCwHOlh@s%GUpO5gw-nRd8czCMAS3J*R&g)i0 z<F}IBKvZodTD^-XzK&*H#pXMAdfEo!An`ZRty0I6<GKA68+c;xnyII-ho@m|@<v?j z=5Ot!($;J}`nH;d5q{i`a}`GO+=t_P;JXvsk1p~=CilQ_sZU>l4_D41;p<(-eGqX$ zMZ1~3NX2xdz*#YVf_yBsi8F?Yz*MVtkmZ~%-u=pV__P@3ecNTIsW1?)9lpg}0mvo% z{OTSDA2>YJoBHhi1eTlZ599UuVK^#&vVAdDQThSHSD6sDFT$QB>fKFOvZ6lhZe{M= zU=^DqIeQ@u`=Vp`q_*%*Y;3LQW}4QU@IXTByF$zCI9(eXM<V?NOUMBg;alvw>V~1T zmxkx|%MHNHll9%pal9Cg;+D1r!%J_IXg;|!?Q0l)V=pQmv}H{!Y^vzM9FVq4t)u0V z9g3zM*==`)Vk3P|xhu4OV{Xrd{b&)X?2GN+)yFmjnk4MUGD)nE)XQrZ1t;FQ2aOK7 z>ft&8$*ROc?7j$v2sUrcCHBdtGbLkDb+Dol5pwX&)K@v%oeiyn^$+}#O*w9WuonC= z^T(S%q5O&E&t3e<EG)=H@*`{quyS~?V+U42T1{o{FgU~4tuU&^2z;*emC4%CvBT%{ z?cdk4dKR=VjPi)aRz)Y>tqqp74;x{-H2L<Ojw2U7x9@D>^^s4>ILAl$tQUT6VbQir z+shaSDS3>vRoqcJfvm)Kd*09)>$v$b76fB*IF=$N7NVkPo9DH1zHpDBML3DH7C5?0 z3g6&#RCzo5;<oJQRX3?_fay>->%KT=8V6j)WN-t>Mt^Ke4Nc2^0DXZDxY$IWqsQ)f zI40~AL<(oIw|DkO4miQCz|+~fDqGhA(u}^RoO$roW{96+UfK*XCS9@P^kQsmh@Ep! zKZf@>8Q2loQ9nVZo%4u#hwt_}K&o&$x~(09@*^AWq3Z|C%5HS*^V)-rf$QB~)&$$> z?lnDDQWzoJurymKb?m@af#qmr-Oy3RL{a!lsH$qSjuwsvm>I)*vmw@-`7vL6wK*jS z{lu}$t3Kmwuc&w72-B&4E-Tko2H;p=d}p{H9|ex`Z;18i+dV>Ye2wkG#L!faSm%b# z;0ewx#|BfFrtwT|<ZPS>+8o@ct&P{^Sm(Fs_^XqW9Ug-z(m&wLV=Qpy*UKt>%zi@q zOyo|o=djgzJLek<SmHOLq@9y-%VMW!#^E3k=z+lFR!i!TIOoQx$QS!9VX9|B;0fzk ze8}iOub$d@r(!KB3anp=Ut446Ac)#J^=6lC{#tJLhF!X?K4@|KId6X%e%$5KnFVHH zG#Bsu>P6mIM?1Z%@L-d{Lt*@F6{urSX2A2NR1fEjB2c=HDI&3>1>XLO{kB%{8l}9n zT&>#*yd7$zP9Xo97q?ufJP7q!?i_}$HHZkjY+CUfNc8tH<^2M;(0407Svr}(3DZgZ zXijbZftDV}=fJGs=5wAD%uB5|8Q)-4I@<u_N$xviogI;>&XYUfLBESHXnyg|?;h5r z!Ib|E^1$?~&)!0h;g~kY1g`te3!mMz7yJF}8H-J&gS1*;vd5g0&RgfRIC>*WyfX$U zO#&CJ=oM5tL-2&6n*{2x{r^$YGTLK!L35z*-@tYjx)s}~ne3zCq<UaDL~b~50FFv1 zu}}>js$fdJaED}V;KZVO7fuuouZK-dR^%haAzB)D<2E+cLr-Jh>naV-v?VfE?Bdq+ zje?d1b3DkN{#u%!!3X=MO@wu0E)oI0_upyYi<J~!jL3tQtH0tag+FGKv8Tbg<r7-x zjd$yW-VV2Q$B|7g^UJs31PXk43mL&Vx1n`TG<&G2W*qhUIC4f$)tI>E=iE(Dqn*pJ z&k^RJX^26s!2wF1&YvLAlh9>}i7)fUJGWt|{Vtz)hoVdzJbM04|N7lJ;G>eziO!*m z5e0a3!hybXKbYtoz-Q-tkZT;*%kGwQ23|PIjZKZ(;t9KVmVM<MQ{%m~srQcEW$!D# z;A2^I&;mc0TYau@3!E$5!6o&wFq|vg0-Iv|glT;%2=jDCVo;7#Dv7((6U1v<qFGx9 z9?x#!b#`K^a6bWU`?M-QZd7J$Rc$}r4!djM5H>e{;OL2SRqzF>Gx2lu*dm>3r<{Tb zIBr5(C-I;sl@t6Ndv19j?By|0Vcd*jQ46o~n=YA_?Si!Cc;Z-ibqe8Dn%?5DQD&Rl z(F1^%PlJQ?O;AQ{ozWm$<CZf7w+;cE_U@d?ISS^|2DP0nJvhIxagIme*(?voPT0J5 zLBpbgc{=+XQ?wAZL60%_`3-VDLiz1?@Y_>-E#m1s2yA#XSS6&wKw&0O@cCSw2;w|y zMG}7XZB3lTxYPE9vhS17hh@cqzvj$Yh-|ZGfoQ!ka7G`M(m3VHlotUuGaIj8z<N+O z1xah?z>j#WAhP_H9oVl7=-F;{-dUBrGa0vrOkn$ZTlp=vbJ(lIl*c{JkrlStax&2q zpSfnmI)%DJN6CosEs9!iRM=YUvp3e3SGn(79)=@$;!T?uc;E{fZ3*qciY32_P>fw; z;ey51p>Q#nwf3M}PgrlJi!D)x<VJbqIvN*_>fdd7%iOP)<i78=tYulxns1lpzF)7* znVaa5Q`W57C$~#(KF^7>HB@`UP2EXmbQdK!M3Y(TbrFgS<=@5`4O4^O+We3OMRnut z2+-^<Sm+91%$T|}-my#dtW5da1+?Zt`<FS}zi()@;GC`}@B-bVj0S+w=w4+^G~?$^ zcCQ)&#^6;>JYMA_;8jL)D1Opo@iX@>{4CCd#|*s7$-ujud3cwTlUcYq7iku5<8QQW z9hRj>;n1`1J<us@>zZFq!@{{O3UgXS+s@#~IBPeIoNx?8GsleuZ3}U%bdE<97R=%U z9CvFY?f^1uc$DqJ?RO?j-T4-(g77-v_5N7L4kN&g%i5{{+nqI%7-2g*sk8~}u&yR` zBPBjNYJt<uTH-9gX5b=hl=TYQL2=!qJ-DO4t5ko^+*Z!huzhMD`>(<sK0SkCXh+N4 zqDgpEHcV6gDxd_LYU?*)(V|If?jYDfO-Q~;JPwWD@uaF?&b3HDYloZ<Gc9!lbB&G4 z5vxbwuk*~#xy-feodw>`4!ENsG9N1qX(B2`n=Y5(xCWx#K-*eQ88U-Nryj-|_k}S{ z$Hv7^#QLb1)??!erZ!bZQLg_$#MU@s%9O8m3yARy{J>gV;8ymgreEz9cboEKkpPrW z9z;t9{%X2+7F?2Y;R!`b>0~@Brm~rES!DPC>J|1~(j|1PeaJ?%n3u7+X#%F5*7AY% zShHSZEd@?(s^luN7*8xAYZIXwZR=OGPU~GO{fxkG&<a(b5!l`tpGi(~G{M6Ilr6k; z2=tsfB``pFc7<ozmzY~>%R>ctMqnY{)H)-OhlF9!6_;8M#|2Jqva{+KwA-BXIOS2I z9TYfA-B%qHc;gAEDO5ci6u9F!ZmSOpv@;9spul?iL4jT$;G{sDg`l4lSp0=zAar-x zX#EH_b`a21UH?geZQ{6aajbLs$JLq*>&TJsR2cHxm387`z)Gn1rVbokg;!H6osm`f z`s2vJCRJ=JYv|b79aB9nX9l9pL}REH$Y7gws?fP>Ax@cYsr4p0hff5u+-?*b6m*6k zwl$B1!CdR4d!XrUs%`kyGM#!>wT`-(8XcARiE*&RH`!~Aft?i=)-7SL-Lcus??ihv zKDE4|wI{xgyaiu2VymKn@3I;Md_AvTy>A0{<8CMJb_JZoj_5c;=Ah5sfRolnZ-Q?! ztKNmr9NQ<eFRs=ly2qxv&qOJN&g}SoC>ks7Y_^Vax6_b5D^@#R#wWkpmg&_-bQ-~F zI&ZQEzfEn8@a%EnOW3gEE_|t*(P0vI*9_`kx_>koUb6esXsZ5uT@ME;@hy8}j14*h zdIKT=;{Zv3`GA#x7XZ5e#{fSAnx7Y<8^8}R0&qJZ6>vA;Z-8e3jnUj*x+KIA+@A(i z0z5AW(HhVL;0G8DxC>wftOC3O*a0{S_!-avAJ_K=3<8V>+zChp%m>^HSOwSscmuEx z@GanHfVe0`Q^1XYt^i*^1RxeL4Uh>~3V0H*8So+CD?lZnDRAxz2m(X^#sTgGqyusR zs{k(nN&trdrvV*7E3XpJSo`ws7b;#3$9_>`u!q%Kq2hR2XjPEhJ)z=D_}9L+&N_St z-$6Nx7(lx-fG@BNI3p`^eu_0VD`UDPJ6nj^B3n!s86rzq@Yh!)i8TBPVaB*~K43cj z5KK%E<Ha!1NBFti_ZNf2C=rP_^dBvvM7S6uBGj9a_#KCLkqF@<1|a1K+>KIUhoxl? z%d%Jm+&GJMVq%(2-4A!U4@*nSn9jgi!Y0y%Ripq@i!cMS@JD>jDy?RMwmSjrMj`Jw z{JDnFeB*%GXfeDFACwPb#b85chPX1ON*Z(w65gP~M=-rU2!|mK{w%;L6RsJMfnOUq zMI2**MHKKcE6ygVcgYIt*(&TzP?L?jMEo+wY%vWyUw}7BNFxir>0$<+(h-7`&s8a~ zl(e+ypQ3yc5idpDt%u9N`^Mmekv_vE!GF5)x9EAAk(*7WPk%G=%}}`#pG^EQMJ+}O ze9}>JW>7;aHLPdjZMJ%5$XUQm!`+H}Nj)Vg8!4u#Fc~5l@0cFt%AhP!<!tb&kKz+~ znSwAD;A|GXL_fSuLu$9;DH-Xd<C)wbPJBZtAU_iUS8^&{;YjIWEKOdt-|CnV3a1#P zK)m{@(q)Mo<vpOPywi~aIbcCJa%%=?H6w>i#GS6FPS*2CgR4<XUNBwFXEs6_*c&oy z<kP<@pStCWsnjfQrfu*$9QcmMGs}g%*L*Y%lUKyA?|(!tYSZCeMTdct!MD07NUFu} z0njZ*{bFe+s#>AZ!`f9>8fGPPlv@Lr>hc>P23MtL;BkHBHxGH$o#T~d=$hZ4Kan46 z66@%n$!}m)ewmO-t?cI_tt_Nz1OL*%wJU3mQ5UN@rpe&|;aip7_46wMn5RR+<|AJ# z>MS{AJW&#jQmoF+s9*hse^q{Um3Ib8mF1;rQP$@Sr5&gxEzpV7QWlj0%Yc_gNB>%K zL~NK6^?;^ZsHZ41*Vgy_UF3)OU75~oaG6?;l+RTBPgJ@~(*d)7H~l{&FEw*YQv6QT zxnR&=T@T59Y6UCwDfQ?~glEe$4b&LC)ig6XW0aAWj<-wsT)#XpRZRvd7i_cHdTS{g z=~mbC>rXckRLlUsjTE!1ur<ov8@&wX!}d8D<&vRzL#@HKG!xIfrxs`H#FkFu3|kLP zyK8YMUDdpxT(J+8do+}GGtmn!N2doq3q@Yo&;Tx{Exh5j<ohF`;s(Bl+lueuwu4*p zX{cxqHxq6LxB~7?a8`YSPbJ}o!o|{TA>ej~<M(kWFhzQYLq!+3Cl6tdP`GcyT>$rK zxOc<NgUd~$7Q*Ee#T2+Ps&24-WSvP?G*84k(yeKQaN&hNGinSan-Z+`u14W*8aPdE zGZ*y16_o-l2>;Nh1>czR3>Av)FEzCmn`zBb+?<EsU=_c14v~nb_20Uvv}0?e7NBSz zAx4M*q&XYE>>=?=(*JHlh#38knjlH=%>w<8ecc#nA-?aY-Z5@0-th|4-3XL0bqD)h z>`(F<4Vu``a(y>O>5Po3P;u~Qf6p}(tT;+8smFOm<C*;-<DPnhSD4}(`!~dq{GE>4 zh?4^0PmZ%>44y~hKHcSwhOdF6K#!HW4AaI6AEr$ljJ!2BUC)P@Xn7m?8hJ7t^I<yF z+H66nPc+<z;VH%Cc|5RXh-{a4@yI_3DX_n1#2k-MuFo;xD|?|vx^eKY`OLhD6Z2y~ zp83F@qtfOx(<AOohqy96cjpM0Jv)FS7{~^$+srYz%)D8EOT@(VOG@g88(gs{{xCp< zWx8edG>a<m(HV2A?#E2GX7F{KCDUrW471I!WdpHjTbfEE+Lo>gGAuJIMU`P<)^t^V zmT6hG#4IgLV!FCTJ_6|rq{}n~(qcM*bP<!dKp+jcSz?%FnutzJ7jcPJTUHW0a1F~! zQFr5K+Oo3sckz~_bc_Bl-lpBfWTY$KIGgS!S{YX$o<MkkZ~|cj{DoURcaQoF8hSQr z+@xu<=B5@cZ)nxJ&5do_weN6K$4)nQHh1ZIOSkSldiJ`tx7Tfb`u6kgKfuR#;Gn^N z{sDnOLxMv>!-j@OL=GE1A}V_1sF=}XV#kh)8$V%U{OyzOm^@|boe6g(PMe-&NuDt? zC3RNX?DUMxIa%3O+uV8c7u<c%!bOX7NY?+;ml+zW=3g(g$J$@b_jNywKnruNzdUi} zU(N7KMD?GWxV}Hv6qbtWzj|m7ujkK*Z~lL}>^0)AEZ3R;(-Pper0UP@IxnLH=`MAv z{;TJ9eSb#$E2;mZZ+@=^FsIh|Gjilp^?eI$7yd`C+TpK{+Uig(MD@LP=jKK^$X&A3 zo|k{`eai}#J605~ynof|zdi8aLk~al=wpvR@#LDnKlSw5XVyKt{<-Hj{NsfeUwZkK ze{S6L>T9nTZQk<6n{RD>yZD`LCEIuGeD}RwyZ7vU|AP<zwQv7{gNHsk{P8EB9{KF( z=f}P{e&XbpUwwV*n{U5!mVRG$`iCFSl>hYe*>k^~zff`U(yx`j;nbAB+7Mh_(O*|X z@V`y}|91X=ng74n5MSM2HN^kh^w-BQ2pco1s}t_=6^6PVcDXOaJ;zzRR=V6{OSr1{ zt6c6^yWIcH<^BPedmc+_q*>&0?~Xh;mP2}?DF74D`n-62N_tYpyf|y3H3fYryq}P5 z$r>{))iT{0m7biDHCuVaYjH*;W?RFiTT|v*Vly&rnJOLaSxZL5V5U8BhGl5R{CLch z$Elg|+H>N)(5vOhAr0enUcJ(7X=&Q6z(-IG&}c)Wxi=6E>b3BQjEt<$YoIsAHVka` zi@m*}rx@(*9UqI2FwAB}uIF7d@4^LRN9KH7o(O?6-8?<vc;b(b>S~aidvETNTx8&W zZ|=QIb6eN1j~w#z^HE^l-np^4%MiL(PrumsT*OBvv9Ze#(cCL{SuSD;WHWQ-Oi#~- z^D{H&WoFh$pB9190NM|_A*>CI^cm1MDGi<IYQBt5nQh6iS;Nr(O|&ManIp5ZGP2Am z>E=<1w)E*U(KpY+D2dmw#FR8klG&PJ&d9W+n=SJ#({0woX=&`7Pe|tt{gP3B=EQV! zVkQD5Av}BQD4@PBVX`cVN#^9NjM)gSqKjL)aI6>xOh=%MZikE2bj%c2bjCE`Q&sfl zIW|kyf|T?b=46G?75>>4tJTF@WLuL!PLeG%EoHhA9wQ>sh98Kp+Y^44tgO2Is_C5$ zVyqT(re@VK+p?K;&+cj2{MN>WXEQ8Tuq?%Do}Q6pF=r$jF*v^G{1&+=Fmk3PBMa^{ zjLFjBYO^}Z6F-sjGf@$=QqVY!^k&aa1kb^3W(`t%_C?wB<X!em%d|wiOiM|(1ek#o z)43vzc{5Y2mh8;L=@xThvelAh?%B_loz-tzN_sy_`rMvI#A=#V&ei45oDeW2D<gfj zCEaR-ubG!6Yj#RH*bi*LatKSGIkpTi4&~}1N>@XAyi0T~-n<m+Ocq_XH8D%2`bUy; zDHlbH&Zjzf19m99wL0O-&l-G8vZ!Kp_18sV!uVl*{Az?%IrhAzr#Vr_p3>kNv#v7g z*`+5~1=HL-C0~eLt9a|cy=Dp3$l(eZNX(jHW8r0+ds*h9cBdqpEweMN3vRu3KGPsF zdc{wq-H=tm5J(E0dA4PC28QTeMR(}eY`A9q&RoYk%q>u8{^aGs*_Jb*f^EyVxQMu* z^t;xFmHCa%9em5Dr#JoFgCDep2TWmml09W2gmJ+Xv^i{8vZrLH%+5@+B&6v&Z%R^1 zwsp!pXv@qbP5J3K#SX?iqCe&g7uAg<R_bsnP#o4><JsCFR7EhJOKSPw(W$0??f6H! zU)#USt=IPN+56i5k9%F)f5B}v{g=7mf2>caC<pK|=sV!z&-dGWL+Moc*QRe>U`_uc zF7YM&+=FWR*XG~pAvOJ7<0D(~Oh{-|-kV(RYllk;s}7gxa$g(Y))80FZ);?z*azTc z@S}G82_tLzf9OIl<MkX<)4z877su7~ug&iciP!cow$${moqyB}&0pYZgL{LH+WFj- zRx_R2{_k3A`q$3qfrZ!hFIiL5zjpeo*4Ol}&8Jas)b!s|i_b0J()<Okm^ZIZ=c`*o z#fGyCrCqO{hkHPXbLWgVSciglqqwNIj_becbpHM8blSVQE~mNA4P%9HtuHhREl<4Y z<Pm3K=QB3ZIx}9(!ISF<0=-IYR3WChaY@XmlxbO<Ox_^ERgcaXj$kmOQnqDfp?TEb zii;lci7D1$8Ch{?j?*l9N;42L-I9eyFgznYJ0r~!Zp})IM01MS>>EW`c6Q2)bUlUf z8Py1g6U>3cgrje0WFcDD!`qmQBwLzg7`n-$6K8`cvEF?w`@b$@q3VQYxX0MitSLhm zSS{l-;!~0=;WIJU{g6&kRXFtZlbCj#HE9HTj_iTg<1<H7s{4<=1fPjXSW;4!TCEi1 zJ}M(IN#_z$=tF+Gkg27Vh0nAi^q#gwPkfvRPs_+Qo<>HF8yz{y7>7i<#aXlP9|g+d zMvRINi;YqPly=p$YpKBheOeL7pp2Asbj!s4dLt}}nQA$c_(0ujOP5kK+OkkG;}>L- zXZO~UjWF?%3SUzqgqW_yM2FIrNe0JbpltL!tIOphj|u5B72lE~=TC>>0s~bP5K$8A z$(5^nyGLb5Sf<%#%&=s|f~^#cA4Mdo3C~KgVtA4khwcamzQW&qlqGSl<%;)T*NaL= z(8RQqyK8%fr6<K@rlf~w*pTk`NY|BH?L5X=(<Y>&`<NsSf|F?$NNKH1LtM)ke0$Gh zd|I}?9&9)7Ar#u0u|0eOsoK*nKB<B@1MENzdV;eoNm1z-9I;c0aTfXFB46*h)T!|^ zIUbBk!qQ^b*eGlxcK7=6z*N-&@h??!S{=n2HI+^*3ro!8eONe#<%3y{V<KaC-xxdx zmMKZ9ZecD!^l={*9+Q{}se^=o$`bc+$e&eAhz#PXNu0(;ah9plld>d|>CY!a2t}gO z9+B0<A1&SNcyW|5>+%q-s->H0v1X%2wXAPc+3^`w^;kTw)%RFSmKp)4Pls+n`9jzp zU@GHNrq9wb-L801Lr;OJ3zSSR$cA~1L;bL9MGNl4s~&M_7TCz{*YyKE;>BUDW<a`x zcwD8Zt1|HNC6{L?CWN?;{BY4D9^kgFz%sIiVPtH}LdCHtNso6MmS)SI$!vz&l9SO3 zr)?Q3J2NrMQdJu9OwomF6BDyiIK+q-hZJVxESf;ZY2lK_Br`$G5|B<kYw888an+<o z*HI|w{~umlA2Isd(5;)=Z;K##B|RWWx~t86t4B^?U2!q4+ZAuCU(lcM-|N2|xLyuW zo^Avfvcs`B?>zuqi&G!a48XB?BS1p{=YKgLN|*2Ga(xf|IA?A6F<r)`YXUG`iZ$PH z{m%_L@O>8m-wy=vnYDt?;rcU_nTj(`e;=p2)Dld85`cMQs#L|B4c82ST2X6;(9|nj zNe9E{0*Lbx-CYWowAcak&jT=j%$ciM=D+LDYvB^VX93Lbc>u$00g$GB0Q!Flpnp3s zh5R9qFTC6|r^?kItDX)2tG&OP{~rx+q+_^7{QpfC!##C%TN)~^2mZ6Twz%r0<!ip` z^`FJ-e=7B>8!<Huu5Lv8pTh7zpU+iAKyRV1Dgx&JJZ}GkIs6|Nh1mLGFe!U@K8ptK zuB8i>cF|e)+zIVw?Rw2!^3+K#5f!0}bCtUR;kU0ly3VY5U-7%-vi2%ecZ;qwe3%}7 zGs5qG=F~GC#5d14pP791^cSyHF~*`n9((l8qh^tCH1+6!E5m<u<oL;pLiAs(Va9pD zaD;mDM5uTa@E~9nU<F_qAP=w*U<ITB?gESli~@uLf&hL1Uw}8@Hb5^xH-H(?5zr3M z8qfmZ38*}dO{)N>0Stc%?lHh&z#c#e;B~+Tz_Wm-0S^Ll0aidGbUutWDGkp_fGL3S zfKh-5KoH<IKu17RK*blx2XGir0(b+k5wHR9G~iLdD!?*8E?_<&6EG8S7a$f81n>rQ z12B9$KvO{FF>Gc7I0ASVuo18U@HAirAPEo);AP;g9{!C#*2ZBqQv0jV#IrW8e$A`% zFn<Xezd>1l2VNoXlTE@zx35EM;P8L(;rG@y)vI;6>z6?*`O+19U?(M4`<%}3`ctmC z{sZk@BeL)4diBKv!5d{W3Y!`H9^~kkFT%$n{MCSE&GKj3bd76#De4!%xC}dj@hnEx zXBYav*I(oSR2(jM{9#E7h9k}4Vyu$OG1oG<aMY!RYpBZTX}qU=yaAW-4#1_q6E4ST z2$ajhX$O~MF&syo3rAgAI&Ny!!iYNFQwHv;<2@tKcM%XH9ll?7mG_Tc<^6`Myno{= z@Aq8g{gJD@ch>QqZAk^*dk%FIPB8D3Mt~@fa1*`o-gs^|%uQ^-0L^$V8SN%!f~m%H zukrBjUj2Mv0iMwVH2mi;M|h-dJfB>NXYj~)9&x{$xWgsCUhp#mLIG|<M+*0KU48rZ z6~l)Q7xD4&A|)k7*laeD%YeDL_|FxOKKiJ5{`u#{n{U1;N=iz^r=NZ*E?>S(9i>94 zz<4O<Q&VyOhq+CKY4^N&`_<!qd8RVAY300m3l}oL8Tn!5e)-`Ac<;uuCmzlqE}rMj z<Fj)=b}~@Ucq@e`e$L3sWaU3^VY%mNM3%es=Xv`*D;PgH8R_%NVfYXCCnxVm{_0t! zpPZbA_Xv~2^ef@d3t?1v#^;rG0ne4lfB!D^j7*sR3Ffb^3&<ZEGwi3)lJAIL<^GE& z@N}L56#h#qmw#CK;d!RP=gZ@!L?J+B8q&ve{kQkFzV*ou_k)CF^2hVY%S&330k|so z{9FEjmZLu0j}R*m9O?h#@&0~V0QFq)ul>(<ed$9Lmi~x$IxI+5AiA0Z0RH>djsLKs za(`|K{lOpk+k|ZumCBpp!Qav+E2Q#P{AUtQ&70xXP7+5nZ<W5njfpe8W%^a39z|v6 zlAM$7xgohibanxE+`9pkXk5jmKf}4kV>)#~4&KXL;RbLAGz6Fcoh9YG1;7mWJvsOF z^~JX+_|iEpPTYO>-3m8D##gOcC0=;p1@ZdpuPYh<<daXtH{X0yCFhx(ZrKPOx^YWG zO8PyS_sGr3+eFT`Gux79Wr!RUQSvtIZoaK70g^B)=f__XHsdDe@84h-mtT_??fquL zgbCY{NBO=8H(^%voH0@sCCJ4EJ-1DskRWp8x@|=hnBJQ!nSPFxKU}!5Q79?C2isY9 z?krn0>@0=54xk;Zs~e0k3{Nm=d?JrR6`!Dpu5oqm-d%Wkd11Yww-_>HhzJYAH~CRf zig#njjuqG|4f|A05>uy66^oOGi5W9yh}6_nF?;rGk(Gt-r=UL;Em|bj+Cs#OcMlP- zEe;g+8IfYetZ?yQMwoaaD^zS<5+GjAjS`P9nI^V8nkjbOKT;f6H%s(-M~d5aNYQ_n z6d~_QG4OpUg7!%<<zG^SAC_X|Q7OirkRlr};VUU7e<#KClTu7OEya>0OGH6IfmpqI zwRq%_N5o@~Jtm%f@=5XZ(@%@_>({ID-MDe1C@Lyay6o+@-xeS3+$fg*Bt=oV6z{$F zp4hv0ulU!${v{3`JSYwyJ}izNJu1HV;tO%+n<L_*pQZTn%P*B&l$Mr?b7#I6Utg4> zyu4goxNt$q3fWq(ALch>F{cTk@mz&@z>N?dRQBfOkW+bs7$W<N$#Se%Am@lj<!VtR zH(`CyK|`O4)`;IV7VT@M5JOgB4Z%jtKO+8&a<rAQrC5&mPa^&+h+l&EhY<giOZ*;) z9~vvf9hpK{S7Gh$Mj`%z_&dslIEeU15dS#he~tL1h<^t0&%4C$*8^)W!yxOEu^tz; zx{WI_hWv*R<vURc549BXNPi)}7%SwdIYNHFTFCNELY_ZZg@3=+h~E|Qy%9ec@naAl zX_coS{(_c5F7Geonz2IubB>VPS0l|$LVk0wIzBodu^x!u0P!(tx)J^0{m?(<=)_3# z4MGO@7jnv2A@80e<Rhzv+`LK1!w0M5i{@B6+7|ttE?7_Og=#xU?1$u+WA6mn@(v;U zX9_v?ULof^Ddg%`h1|49$b%<b;+qkF5aN$Q{Aq|kAMsZr{<Dbx8sfi$_`4Awd9FT& z_}?IYc~$(|uu5|zQb<7x_aTM#Na0<i@Fh|xZz;vk{iQfJR*Lg;q`0tJii?}1`1K$T z5Rj@p6*nP%AH)wq{Bejs9r5QP{xZaWtfdss^_SxHu~O`qBgOvJQXJbPMcKjX_@0PQ z4zxl1n-ISX;`c!O+Ympvr4+aKmtxLXDOSuuTC1hlwn>Vk2dm@PR1FE*<@z!_d}!Fv zun6^PRPSEhyLIi_V|Z17u%XdWQ4tZ*;UOVm5ea>I_3YWb+wkGHB<KMmqTr4HDBMIu zBzWD*0K<oyb^qv?@W|*8Bp(?b7BVy};Wh^7-o0xV-9IWOG%PwSJR%(NiCN#<yu5;S z92q1!Cfc}$-PEpaLSF=63f;PPG2a3tqnLhlbW})mLZ?>k+rb|Jdg%drBp`lB7!wGO zPPnO6tM;0|!m(RdGmwm`N<X1Z>sGBs4p9ksDJr|bANWTwepod8Tes>oQpasnZ=!(s z2_a!IqoboEqZ2wXf%fg%w!N`w)21Cq5l8y-<H;E0lF*?>fCRFE{^5}^QPI&ckujZX z1b~0f-YWf}F%i*`F%dDtZtl=V&jL9l+}f+pDA3RJ!&Mdr*Ajx-0R=vRe=7a{fiYll zOk{M7B0}M4@E`amMD!0O3ehn!2_1$FYt^b1h^X>U7}Ovkv|l8$7!CiMhYeFycC7Lb zH`O=!HTRB;j)(!5hjms|b{eLOM@T?e*Z_A^eeaNjn1q<9$QTVvs1WUn3illv-p|9W z@o3$Hyo!o|y3+j<{QI?N)WFTH<p^L%6l0=8AV}5zVS@*H`!sU*ydesn;X@(IzP{n% z)&2<yL&L@dHg8ye5ON4lVCqp3)%hocQ|`tDHmlb#l>Xr%5Z7oUI`~KaK)^zqv}`^m zAv`7|5VBU~4S(Yr(W0e)Oc?kW<>F2LN2m~ZKiDTc5LLz1TcwZkjgEjIMnw(`sO9aF zJ_<T2ETCp=)Q_51OpU~$Uu8*7g()}WBsv!*fLsm!N`Ec~Y19T;S6?nygw*QKUAv!R z^ykKn8>jjsi!H;e`X0~E4;IT(BE_Rw;o|kBkz(WHi^bqQ=u<$?jrdfGiRe49&oCeE z!w)|!o_OL3v1ZL0v3BiR@$9qDs=mT&uf3-FgKxd{mSA7u<C5pa6X<VjL|@_Eci$EJ z_U%)BgHvCf5MO=ul{j_klqf4J6Q@s~7C--VM)eJT`Q;b!+i$;#)fc4LgucSTUtRQU zzb<I#2BV=vy<9aN4c!7XboZm7drq{JuZ#Zj9Whq!5p(26VzoRbHpy?$epk0Mq8}Q> z*mj7I#&*?hh(8GNLlHk3@h2nx9K^p5@z)^!tBAiB@lRB>Gym<UfPMe%r~IGnr>K29 z1oo8c&;gZ5i1uohvqQV?{rdIm&G+UG9olv1(z|nu=FM+Lf4F_uUfsL*?B1_s3)8L5 z@Z6!xEj@c8Y|9p%`+4>A5*@qt?$xhT<9;m>pn0=qH+SsPy;r{`jeFg!zc8D7_3r2C z*|Znp+<HUvW*yqK?cJ}5r)T3v_3Jmi)nw}6(V#=`PE9-;^F_ZFo!c~YYuFWG`}OP8 zs6Nxbv0c4ZcxrM><3^3VFb?R_{U-O0@I{nPct-wS9b5Oj#j9UGub#jk`S<qn>gMIu z#Y@rG5f?3iUAm}8kz>OC9}N)@ePso1aO=AG-U!3+h$lL$IYEK$xws49Cd~N#OIJO_ zMvq-B<3O)nx%J^P=l_Z8YSdpVZ{pho(57?e&RyUVx&eALYSaiLAbgIZIE5}WwmAee z0~j2~?Z4$Bf0!;tH`jHMw=Dn^fAdpMJr$33XX}|WXTJUM#~;5#+w;{AKm70w+!N={ zojZeZ>8WGKj&0h#d$$9239q1_AV@w-UoE|1I$V-MKKv2>v8>rJ-_WB+k6Yl9SDaqH zo~zyD$&*6{;X>e>FpqdY#?f-ywr%pe@4l00PbKDEI6);(oH!vd{+9Fy4H9GUFPu(i zDQNxj(4j+r-@bi&&d{MleUJ{zsUx5*fJ<bE3(K{!4!kFi>Bprvod9hySK(7pQE>_P zXgIGM|MuYbe$+u<rZIT%;2y*ecq#nffB$`nIZ_Gy<Qs3iq435WlKl4DZ{>jl2PEbc zR5->#AA#>rA<k2qHf>t8Wy_XD==Me0?e;O5nVAtQSFVgjznZw+3h-*%wyh85HtrZO zU;ug7E+QhL7t_W4AHx6a*|QRTnbo)4a?3#QaOL;ke=jkoTgB)7`}fPwKKo4JiSkjn zvusYDJSji_{B!k8nhZdnN8Ll_&i(Y$PZf~6Uy-Ks(xprC=bwK*`|-ygzxnmoU!Oo; z7a0e1K7jf*5IC%4+&_vx`co2lRRQrgfP6K8WnuvFC$H!l_k1Q8bkHR}1^{Pi0Lok% zV7xQjKZF0yojX^c9J@j$@<_{$9Xk{aln=}|NtOZTnUuT`N7f0{VdZ}G(MOWNy20|G z%lg5(!#E#&@PVR%Wr+GOSq_-1=M-+$h38-KzLNf^!!+_=Z~bk5{``4~W=P4upPye3 z$}xdufO#{yb?a7%GMDHxt2)dwV3}ZEO#yM=vuBTFT{&{(h`J{Y)B%(?))STyaohZ( zloo7ZAF>apneUad-+MgAPRhO|I3M$UDPO=b=D(aj{}Jj6>;Ls!3jcTBd1rOEZrui= z55E%nQldN*4b(X-1M-#huq=o>?^*uGj~`dSd-9z8Cccyf(n7q+f6C}Hr=^VeM9R=Z z*xDOY@PlD9@Yw?z-j%X9Xy{QaW!JZ)T!~`=Dl03WydLBcM&S=#FAX661{h^wxPPXe zupUre;*U!?93R7lWB2P2(tt7;^nsKEKtuYcuy<5qzWX3f0ewe_Rr&Sek9ktc@(Ldx zpRSOFJko)3Q1lpd&?PS^N92E9bdc}lIm?K0L;0uP#h6VYGzR#O{9L19C_VxU2Cf01 z0rSZ+6Lb;(l#itB_O@0nt`~o_8Pd@Ilz+5Gl3<hvUE*)ZIpv?_LAj!A7__jCQ_d*& ztQ&^QdciC1ODRW#1|H!s4E!byL-4Vi|GzXEyipf0PcD0HQ^Bc6ujgt{{Gp3iV~&3y z%3&qz8FfotbWq-1%fyf?y5v9o*)A|H^^EH^@f7MPY4}3QD9{iI8aDpm8se(=Kf&km zs}R%v;>C*+?Mw|I4F(u=)Kw-1Ev|I@`s=Uq;G1jY`i0T*@i`&#;fzp)*d5<WIpJ$5 z$DWijhBV-O3ss*XGeJgurcOeArcUaCdcgYJ2lB)^eHnJPI%rV%qr6vO?xHL7dLC(^ zKBQ~VK$mh)xvouzAzzer%F>AsUzg9%3zF+>L*(DHgB1-VU}`D0%-3m%Jt5^N;4}g> zRMltFpx5WV)Jf2(-Jz4ZfQHU*Xu1{2EU86<!oO?Ru2uRUZ9LogE6RiQm+gU39;^?1 zHtGrShu)JVk0i+#7X-=Y<_(e0fQB`ofmb5*=M>OzJ9PZ`uW+c(iR$`H8jSi(oz#Cf z_`XxhTS)`>Z^oYWW1y#S?W+ra=zAsqsB0_PA7Y&|=rCwu8L&NYEfa$ly2PJ)vS>+| zeC3`%`2uKI4;t25HT<*BNO@NoXmH|42hhO!Oq~=Bon+K!(qPnQ(qPnQ>ZD#<75*1$ z<Ij4=_67Z{Dj*F8)TYCzE3S0#o;2{<lH)I5T^Jx=xjRt)V?Jn@J4F6c{=NJZ)tNLT zfCkvuWV}m#rcPpg4ntj_PV$3JV*709q@LSwVuFT$ZN1c<_@jTnV&K4mUC~F$BOTO> zbSX2WgMA*lq=oO;)*AgX`mw!bpMlp~x&CtVVt=^_G`tKNHh_k=AIp&E&Omss<0=K6 zvr#5~Y@b1c(Z}fdjuyE#|EVtw`40#P=)tlufc2Gl6F1^+&_dl$ea~?X^(NaYwr#xL zS?VX>1Pw)t1LVeqf$}BLP`f@q1f8^cc9>i-Yp7h75-#mC!evfUgml>MkjFngAW4I3 zA4An==p<fs;SYVU<RAE~B<{qEE^#8>tPkX~Q6|*!uKi>7he#vkf9JAbSqvK901dA% z0u87Otk0x@^_e>9G3Zs&u!=OK4wd)K1P#d%a&c0mqJem_kHPlY=wl2-`-bZZ`KQic z8^(SFVZ(+E^2HZll<b!pV3Y^lYt<9X%gO_PPn6s3gXP;xKm+Q+YoOtudVQu&T5AiI zYqEmnqnRP{!Sqmhe_EJyfQEZP!&1<&2sH4j-N(2SbpzKG`iJ;K7L@!$uJcICz4zWL zUwY{!MUO!P-K)_-nkZY0>pb|H+>tj}mVkz>ph4AV&_JDJ)aR$6lb*~9k&k4C$_GHh zO3<(zG~`2$mRKSbqSQ%YE`5yGQ15YFq5p}$A^(Aafjx)=@x-c-DjHZWu5?_BmfwD> zlwWRtT)wv=TyC@b$*p>QrcN^I^Rv)Nq~QsjhQC3MR?Z5O1t}U0L{#-L(BB>fSOa}^ zS;`M;$vz+3v&_-{uEsis==AjT_lYa(BkRE8#fudUuJTozmfB^)XX*sjh2OhAefO2B zJ_hS^p<bVB#k*$zhxoJXM~oQ3a*+S{$3Ilxi+vi*56HQ5=T^~hEjq4MCd7w2>2>O) z->c7{fma>8uIp8M@(F8gwnKNyo;`cYgoFf%wHcEAy6o(1X|-DAiWMtVy~dP}(mU)6 z)kO<=&$fZOihUvWM>!tg#rnMeIh(A!gz?6@xvG73?PENq*XLEJ3%u$=)_8}x3KRPJ z<MFKU@6)HxAS~@k$5yvx=trw^c;=aB<d`vIB-R$m!oorco05F+!3PyS9N&}Qq=9V# z+X>1QfnylbKz&L%WLrocz3YgOhn^??=jHp)&aLiapf0eF!TL;{^!WT3qW`<&FU<WF zz!bWK@&j34jdA}#tb=(8by&hrVox%OIwAQya^y(W@4<Q)r9X)~ab+2i1_Stx{HE-2 zoWs5y%YyZRbz0SDlmpvmqdrq7xz^_ow>-su;1dR~ujTst`#*>^J!&3=dFypqVvQnC zaFm$SlDFP^t3=;g;X*vv-*5%eV1PjfU8YB%Oc`lYxBhF*H2HRZDB5SOkHPl&#YN$A z%L6my=O1jA=YRR-Bb4`b&p%)-rOeCAJBu|+a_Q2gzua-h9WpH~P4OM$N+nCAf%Ss( zKE#c5)TV`V5NwAj_v|CFu5dnL8^)7-$37C<3DQ!>1r}U9&hq-B^FK@rb@yq^QJ%Qx zo_j76PplhNz&dTv(7ShUiM4C09N1=1=Gpf#=peqtpE6H6IX0ln`oKEE`e2j^UCJQ) zCPv%Cu{X!hke5F+|H-;V`G+j6#d@*Btf#Pu%Y_RUs&XI=)T@|Vmbc%2yL!(uAPokP z24mdIF(v!hlzZYymoh}!4Ef@F_NmzChA#OV=AH&&o#Fi)L;M;1Dfa}*66c#q6LI65 zGuGaz`Wqe|E<-~@C03`Xa-cus7&Oo|<jN=$qn@yS5O>3+9C6G*xne(%WoOK90Dqd> zngXupQu-5guYd`lD|nd4`dnOGtY{#AIB$shp=d}<Oq8QWjZ(4&o4mrGye7{{3-M+> zp#I@|ju9w7>}zjBJzyPRonX4u!NC18(#dPru3ad~{Ta+VIe|27KD6sx&!zegh77X* zWAK&uoH%hp;e)c3=v#1Av8oR!ZwYx*GQc*9d}eq8pGgDtD0xc#|LUu+%2!@_MYVzd z{O3PaJkAk-PfqBzsc0h?0{0zwcOQ_Z-Q#-gH|>cp`#;qGsE;f8%=$)`xUk%*7a*sq zF9AD=(ygfLs(qubp?p*3h(GHA>m|#Ca>6p;d=~wUi}y^IWyJDedk%Wn18?e0+6k}s zQurHkO4n6}NrM5zjdW3lSQqZQ?>-5gCD~59Uc}7+#-W~}F5*S`;h2a(+)39{Pd&vs z#;5Ta&mYB|G%Ea!{y+GgM;Z*hXwYKNK$rZcZbI9w#tD=uwzaPOC9ZrX{wy1#4?(*Q z<({%cp0f@?XFLi#{tWI__`AwKb%bkq)Kw;=i*xK#rc9Aom#gI4;JK^IaFiv=I^VMm zAnt~J1Y@K%e*t%eKl3ogzi4|_QZCsBkq*{D@`?I`x{mFUYnhO@n>TNkk3asnl4tVU z0Mbp_VclWBiO-Z7rpLAbI`esa>h)*&PTYC5CqAz2BJnqXG#GHLGGQ866Rpyrj4=*r zBfgZaZP1;h#lRi?)93!AylYrj|El>P)GsyuMf;scTF8I81|4(_I_Q$8)Gw?H1eOio zu^gy7Sx;GJ)J3!<&;~_X$am!NXY@=p#x?I0{)YUcuh4^hB3!FX*uQ80hjh^{N1$xc z-L-3%(!1mV>nr7*__H2B9-l?_e^q8&DQHiA8uOpf6IJs+q=9WG`!CdglmoU$Y&*$g zjycJDmIM0&Mmg}AI-k0hdJJO<W!C`B|8Cq#hmwC+*{Ch&#<NjxU0upLpBaW_L769? zNh4()a&#Ge1SiUh?JCz;{E@ba`2F5>0|Iaj!haH&2iHDuoyK3jWG)()ZS&?;iiYxX z$h>(c@qJFY=HgC0|NC5bJv}$w#n&&$57%FGjr5H4UGUCj@^~)M-IIUC&C&6cn(L;! z?z-DhcTKw6S$9KpH%E7+=DN}C1$@kaP{5yDr^WR>?B5b7&sXg$qNJAqFbgiv16(gY z)1p#)J`l;(c`>hc7z=u%&5yQNEMw8%kHDON7~XpW*ynBosFG8r{Y8b8b7AWl@rh<* z<6JOp3Ikx<nS#0DPkvS?T?+@&+7Y|4560MT4(fCT^%?q{zd+xXqAfTLy>t})^Jg$m zmWn=b1j6?RbVYyT%naB#XeZ-bXej1yY4fJN`0!aN_hVKL-y`w*5#g_Pb%G9Ghde8& zKdB=*ULp<z!-k>v`7cASo`uf+2zv7^CQO?t?Txfs)4swvaoUGIJ}2b?(0(1Z-p<t1 zm{&-|+|xwjz`hg5o@^hf*Vz_PSF<n6ehU3~&ucPlRkV51)<zo-?VYqy(Y`~wKW%Kh zu0}lGcjg!XW6*Sr7gE{&vR&gim-(_EO`hoVtG<(#qV`LB*&Ud>rM-`~ve(O9ee1lZ z5zAE`RQ?<fvX4L@4g~6V0(rvr>xXZS%IED9m0g1K;k>5(0DIT>n%y)GIMD9PH4vQJ zr_I-pN7{R7XAFY-aZQ=*%r*t%*F^MlClU_>7nVQ!z{G{^&(_Cgt2u4joH-|N<WC&# z1P-)&(tfVw5jfD+MjH?1kv1yYI%y}IggSz2k6tF7*^Xo0B^}@2rLt`%7&x%cL7uR0 z{mSBSW$UAThVzKLm^Xp8SK5zh^K+F)u4SQ(hPFQ1m?)24#VYD~BY*aT4ErM6Rrb@2 z{+oddV{l%XbAjtHpGli9zNM2(P{&+xpgc~jC68FYuj&NX(oi02+37knf7nwJF|RPu z6%Xc3Ij75u_BPr`XfLFlYV#wRziTu6{t`|?KLML3aNv3#)(P5dc^UMxUx72v(sOch zQrQP37<h2rguLLz`E}ZwXyc%*jCLm4H)toLjfu7m+Q?{MS~y*UhnJ>E;-KUac2~pp z&GcOJrw$;nZ$og!gSfE#x8D~cY43U!IB-srbK|tJP#$ULqJ4$7MrGsD^Iw!Sj9IBV zVaVeU@Bvp<`Lk~ZJ47PByPQZ}!0|r&=cJpskOy=RtxHjML)xooyQR&Lw&$mGdm-xt z?IW}m=E6p0@PL`Q>S)?-aaGAb>lVK4O^5zZWnHA+;kbZfJMx6Ohx|C_{7k<8K&-M8 zaDJNdNE<%qrj0s5I}>evw3FrNI54x=6Pg`=3i3X7fp4mGP-pfpDTAzs?DNwl9t84( zKs?ANmczBKk9Pc1*-UAx%L5Lym)5$j(0|O|;cz7W{qKLDNZSzm<HUnJ=eU|<YR;Py z5AuTgh<?0izvVgrqfXQ%j|`{FT3t?O_OUTvk&gM6+2q6h_usGd9`!EUKk9$V0{KB% zWZuMu7wg22Umcb|eDw+HL@jwlouEAOsw-nd{y84!JO}4=$zzVMP#4vBi*=JY8Rg?z zHY|(xR>#WnQ-|f5uRp1lN7M=0uPKjDES|*d>ump_TiM_4%=}@C_%bAfy}EhIHjO<M z<nraq)%cD$Q7^I{5D)Tz<wM?_bsm-b*3Ob|+QZaZ07D+1Sujk#`cS4UJB7|kwXD@( zLvV1g59WIpkPg@}Gp0?Owj1AX%UQE#sd-?mkyLXR#@L5ECqKx4j_)W7<OlJueKAdh zI|SKsZEI`f&j;AWCZOJ{aUk1z+GS{4LO)Al-M6CK7=IBD0{KBaYL^Z1W4lG&!Eq^) z9|U0k?pjx8jzQ7?_@1^z+9mLvspR}CahNk_j^aD@0oy^QZO8!gqFylSD%y!k*kxuw zmb1}Ex{OS@=IQswRGrBy@O~8z;n_tS59jFd!M|i#(7uLtS>?n20Qt`R3G9=xk7l@N zt1f|`_aLpOV6XTAd2=Z6dzX?w)<LGlz8(3^xm?O1=hA7{VErJT#@rRlfO>)Phzof^ zy-l4-JhyGzc0V$1{(G6jU)6uuZxity-9+ZiG+EBX0s90<>?<Hgj~=aXBMt<HVI5#N z$_@KT)E8Wrh`I2!NS9-U>$#NvrT)P>`c$TO0(0_g^Qm)aTfm+L%ErukLHu};55^oX z`^&Vcu-^_Eo<yeCn>X=O`5W>~z7mKBf%&mL!X61qH*l>gX{XGwyvS?PO`VLnm*@Uq z-YS3Q16`x^e_ePOePDcZAo0DUnk#0%kaJFy$G?!b%AfQa?Js4C^fFBXbsp;+>o)16 zjB))A*IwYe0Hq)PB;CxLS7*{+oA0jtXBiNvd#U?a&e#)z^7jMg!wQfF=TH9Z#cxeo z{7GQ_`aS)2RlF)Y<C|T#s_%6>=YkgiH-NYfU^pXAXG9@{;GF?ExF^WmI`&}6z2duF zv$)!YwOUNO&I|Gw{?t=Xy$&1AdFZGgu`c9q&pr2C4ZQ;!z!{{G@xlu)sP_otv2NYE zD9mTS^YY6ttMWA3FqSiP`X{cL(Vm9&&zxud6=|G={GDSx<k*<<`U=J%ap>1^U1JEw zR!?9Yh--_H2E$UO*@rXwGe(`FE=$0;iDM?tAsj-t#uw{4aT)z%LHhyoVqK$NB2Zo# zmscW&CmaKB(Yj>no&NI++|ATYtVhI^eFw_+fw!NPt24B|;dFic#_=P^uN*6Itm2Ka z(qq7jwpNbGi96+=`Tk>Zl<MQ0#Te`f;LJ2Q7U$TDa}gZ(aop*LaVO`f*{34j)LXnb zhT$0MwTI?Nrr~;(LB(=Thw}&=vyp!+C$>TC2l3i^Z=~vPas0ut2gg3_>vFvD(vt;} zX>c4q0(h|eu=k9zb5n<r4=3MUD>vs3QR7MW-Pv#FxPfD*MV8?zkGK<3Zos$}-+9ZN zoE)`AgmyKSCFRB7zY2_>1KT&Me*Yqjee{dO7Lk8w7nI(jO^)ja7}t=wO^bu&_a{G6 z{NxxaKSkw#&X6C<9(6tS4$BhXt}C8WSF<dS?|4SOx^%c&&%I{;NcsL-&olcXBR;-e zQGGS)FUk$|Hv3#G59(7_TzJp6+=xq;;f?Yq?~H!e3AB^UpY0LH2~3-M8Et=#YkcZy z;>~o(3;3)0#5SArNsP;JHrsC4Vrs-?I*S{=6xISD+;tFeuLedKh9{U9)(sPxW;4!( zg|g(1Yi<HN=&C2~w^k2+eCMflTXTn>_Qbu`Jg}n?pUi5fwfcHUJRwX2J)~Lfver-p zC1z*i{PDB}=J~VJ(zAzjwPmFTWKW-InVp#3XLic;tc>i8WNRNBZ5EK2J-hGR{$0&D zbT1{@l5L%cgA8zD$B?f5`+9c`Zr0dr4zgz90CS#6ah<98Xz>s;8)quwsEY+E9W$<= zEDO#%#OV!5u~{i|anivIOLmo~t9PV2juD8A!uf4!<~05e>6(}wl|DCPmL;pJ*_IME zeL9YE8PYX5F)iEDH8`kWRoXQpSEcU~;)>jZ`qjWSs9zP)h#u6>;Ae1bSZr8CMAYcv zx8JVc{P+3a#}D>CsBG$cH}Sr~yR&x>Z!hmq?^y5Y-p_dN@c!7l(z`+boBRLVf82ms z16~}kb-<nhKM%M(pt(<k&m<p<&te~k&sv{%d_ML$?PKz7?K{YKr0-1MeBTFspYeUi zcen2szNdVD^u6TUXkhb!=7D_&`V72p;Nt`L4)hrmJSbsM{-EQ7P7i7|IAU<j;PHb~ z2ImicV(_8C=La|N8|k;$Z?E48zbyZU{9p6m=^q;Kb3jFaTcBrP@4%-5pAUR1@V&tI z1CIrM8(0?DFsNlv&!7Q8qk`gt5`r><Y(e)1Z425PbSCJ0P>UgLhYTH3I^@g{x8Qq% z?ZJ-*uM2)R1Qi;mGzl@=`$<r_$@{(j@dLXL8ZxM8@U6)84S&CY(15W4Qv+-P`vY19 zIf6DKz1=~FgH8sW4*EIBV@Ok^dgqYoLuL<oXNY(3px~h3p}`}9M+Z*`o)SDgI5l`q z@ciJM;QNB_4}Jvsy%fADcuVm1;P-<+4*oLu+u-xTzXrR7Gz{q!(k;X*q<_fBkUK(V zgk*=<Lzag;7P3C%)sRm@oFV5!fFQIF)Q@Q5-Nrl6JKcMc_r2aPcz@>Y^j<&U(*frP zwDFnm^Mp^a&mN!4KFxjoe4~6P`cCzo?VID9=X==qTi;y+4-EWb;7x<N4(c~(;GnEQ zD+g^F^zNXa1~nf%ZE%oZgx?gud4Bu+e(`hjZyMkanxg}z1Y`#+3Aiudg@8=~?*|+Y zI2G_ifC&8m+PnUrs>(PF6Acv$4Ga99VxgkGU(Px2Iqz4Th-fs?X}XE&4E1Ut*wom> zb!CL8=){<!q23r0GL~p&WXkRc4V^A6GjsSQ!?dzAXKKW<&&59@?k{({+d1$1JfG)z zK0ABQJ~v)uiUx5>+z@ecsEh}$89b&>kkh3fxZWbalb2<Tic`bXNR_CtGAf|<s{@MZ z6yRi`_5l}L^e!C$E)MB0I=Q&0TXmcM71-#|J<anb&WtdVfC;||nv-y1_|SBi&>>bl z;O~b<gCb9(-_av99y9F6MYsZgi1*_I_y|6Rzr?5Td3*_9!8h?AxE(*i5u_J+mc)=a zGL*y<N~Vx$WH!klnPe$hK~|Hsq=b}`9V9@4<RrOD!f7OprhfV!-9mTMAU#1((;umA zaci!%0Q9`cDz#i&+0*Q7dmU)_Z#&8v;w*C3I_sTEr`D-=8lC&j6DNjE0&YKMt*pD- z*PZRobC<XsuIHtDL4Kb17O`TuK*AFf#XF)vl!$%efM^0050*3JBH;196slI8Qd&>d z^K_2hsJ8)c2SGbO>%00d{jct3`WeqG1ih>`--a}j9u^illp2nfp+>Y07<q+EAT!7& zQfeKwKDVRUi|nHN#O=+8^9KG4UoPX-ICVq)seF2so@!>A2PQPsVJqB&piESPuHz{r zhpZxdNgMf%gwe@#I?bnL^boyBdswkninY|rvFfek;O<A(D0{lS#@XZScg{EiS%Ev( z%l2BmIG(`Y;;Xrzm+}f&_W>U+=8L6bou~!ZM#yXOrqudXJrf*Kt*`3adX%w@Hq$@> zu#w!(nX+L6@EA<7hc!;d^-$?=ptdQ1Z86zEwv$RyOAe7na*Nz0?PLHQLC4d@G>5LC zhrtu4V6V?v1L5_zS=Jj+!EM$KYoE0O)Stm}*cKLGpRf~9rIBtsA0%dpd{H5)#c|Mh zcX>!As#3LE_1ELI3w}PT@8}5A8&n!=2AN@IipepXO_?b-6((S+OpU2C^`_l~9-KnC z5{4pBB#J_BgJa9lUQ~r@P#ro8>K%m<wy=vOP6AA+I1O(HB?oX7uEDWn2x$g1_sL@t zN&C<t^ktez9V%%GO{1^V`E(he%cBK!GdTVR?Vx^8;%57x-EP0{yveSxFn6b0D7VWR zc~qY1OsW@Dg5oL-(kfRKs4`Wp8q{eODs~!dGaPo-1Zk3nGx0`TY3;N}vuw7L9c8<{ zp0W?*YJV9g2g{e_NV!+m$rJJ?IaEznmz4+Z?a+5ZwF*4dUw_D_R(ymM(a-2vNTadV zX{*DEw=3-)&M0T1lj#&WUpuj^iJfIDAlY~D0<lXpimzbaAB>yp)L(Cuf;Zq#@%K1L z$3SLLhdIJAaP?$ouCv^E*Qs)@fzNue(QGZ-#~RsX_Ka(}#?5xuxP@-Bd(rLZF)!K6 z^7eSuUYmD`U*WfSUxCFEu|kB)1eqbT<Tz!iBsE24s1MXf>WFGm*}7T(pd-y#^G^s< z5N4&oS2*f}l2Hci>NQB1b9fAyL#jy*EwZcZhjur}Rn8``Tz56BzRukStHVX-p8f~z z0^rn#vv4lXhrGG~>D0#WfDbx&goqL`Vi2TiqOgP%$s$e66d91QSz;yVun=+q?sNU= z5RoVo4f<+<jsU;ids??hlz>u^4;7+fP*w|SgBnKS1W;Bw&cJ!N442~uSn)Av%7r~= z5`SmXT?aQ0qe-A9AI!lB4~v0pjYhF3!A`W1ZP~6Z?Ib(dPPNnQJTH&ugMSNoF)!g; zc^NMUCkJ>Hui<sPo*xERALBvZ%v<;wevV(@mv}3`4jtn*;JgP&9|P7f5dl3rN<;(V zSin3C($5kupiTnZsen8ku+IVX3jn_l7{~<<@_+?D@K6j)Y!zjq9K0D2RiZ}JiF$Du zdeJcv6wRVVoDt{51?Wt!OqFw_Pv*%AP=6hC?8CA_9+N@UtX$~NNje!ibec|w#F+zK p+NZO0u3ia!I$!&Bp)Q8rR25QU*QbktE(W?7=whIYf&T>q{{c%Cm0JJ+ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/w64.exe b/venv/Lib/site-packages/pip/_vendor/distlib/w64.exe new file mode 100644 index 0000000000000000000000000000000000000000..c41bd0a011fd760ce20ba795d9e535e0d2c39876 GIT binary patch literal 99328 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 B=L-M; literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/wheel.py b/venv/Lib/site-packages/pip/_vendor/distlib/wheel.py new file mode 100644 index 0000000..7737223 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distlib/wheel.py @@ -0,0 +1,984 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2017 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import unicode_literals + +import base64 +import codecs +import datetime +import distutils.util +from email import message_from_file +import hashlib +import imp +import json +import logging +import os +import posixpath +import re +import shutil +import sys +import tempfile +import zipfile + +from . import __version__, DistlibException +from .compat import sysconfig, ZipFile, fsdecode, text_type, filter +from .database import InstalledDistribution +from .metadata import Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME +from .util import (FileOperator, convert_path, CSVReader, CSVWriter, Cache, + cached_property, get_cache_base, read_exports, tempdir) +from .version import NormalizedVersion, UnsupportedVersionError + +logger = logging.getLogger(__name__) + +cache = None # created when needed + +if hasattr(sys, 'pypy_version_info'): # pragma: no cover + IMP_PREFIX = 'pp' +elif sys.platform.startswith('java'): # pragma: no cover + IMP_PREFIX = 'jy' +elif sys.platform == 'cli': # pragma: no cover + IMP_PREFIX = 'ip' +else: + IMP_PREFIX = 'cp' + +VER_SUFFIX = sysconfig.get_config_var('py_version_nodot') +if not VER_SUFFIX: # pragma: no cover + VER_SUFFIX = '%s%s' % sys.version_info[:2] +PYVER = 'py' + VER_SUFFIX +IMPVER = IMP_PREFIX + VER_SUFFIX + +ARCH = distutils.util.get_platform().replace('-', '_').replace('.', '_') + +ABI = sysconfig.get_config_var('SOABI') +if ABI and ABI.startswith('cpython-'): + ABI = ABI.replace('cpython-', 'cp') +else: + def _derive_abi(): + parts = ['cp', VER_SUFFIX] + if sysconfig.get_config_var('Py_DEBUG'): + parts.append('d') + if sysconfig.get_config_var('WITH_PYMALLOC'): + parts.append('m') + if sysconfig.get_config_var('Py_UNICODE_SIZE') == 4: + parts.append('u') + return ''.join(parts) + ABI = _derive_abi() + del _derive_abi + +FILENAME_RE = re.compile(r''' +(?P<nm>[^-]+) +-(?P<vn>\d+[^-]*) +(-(?P<bn>\d+[^-]*))? +-(?P<py>\w+\d+(\.\w+\d+)*) +-(?P<bi>\w+) +-(?P<ar>\w+(\.\w+)*) +\.whl$ +''', re.IGNORECASE | re.VERBOSE) + +NAME_VERSION_RE = re.compile(r''' +(?P<nm>[^-]+) +-(?P<vn>\d+[^-]*) +(-(?P<bn>\d+[^-]*))?$ +''', re.IGNORECASE | re.VERBOSE) + +SHEBANG_RE = re.compile(br'\s*#![^\r\n]*') +SHEBANG_DETAIL_RE = re.compile(br'^(\s*#!("[^"]+"|\S+))\s+(.*)$') +SHEBANG_PYTHON = b'#!python' +SHEBANG_PYTHONW = b'#!pythonw' + +if os.sep == '/': + to_posix = lambda o: o +else: + to_posix = lambda o: o.replace(os.sep, '/') + + +class Mounter(object): + def __init__(self): + self.impure_wheels = {} + self.libs = {} + + def add(self, pathname, extensions): + self.impure_wheels[pathname] = extensions + self.libs.update(extensions) + + def remove(self, pathname): + extensions = self.impure_wheels.pop(pathname) + for k, v in extensions: + if k in self.libs: + del self.libs[k] + + def find_module(self, fullname, path=None): + if fullname in self.libs: + result = self + else: + result = None + return result + + def load_module(self, fullname): + if fullname in sys.modules: + result = sys.modules[fullname] + else: + if fullname not in self.libs: + raise ImportError('unable to find extension for %s' % fullname) + result = imp.load_dynamic(fullname, self.libs[fullname]) + result.__loader__ = self + parts = fullname.rsplit('.', 1) + if len(parts) > 1: + result.__package__ = parts[0] + return result + +_hook = Mounter() + + +class Wheel(object): + """ + Class to build and install from Wheel files (PEP 427). + """ + + wheel_version = (1, 1) + hash_kind = 'sha256' + + def __init__(self, filename=None, sign=False, verify=False): + """ + Initialise an instance using a (valid) filename. + """ + self.sign = sign + self.should_verify = verify + self.buildver = '' + self.pyver = [PYVER] + self.abi = ['none'] + self.arch = ['any'] + self.dirname = os.getcwd() + if filename is None: + self.name = 'dummy' + self.version = '0.1' + self._filename = self.filename + else: + m = NAME_VERSION_RE.match(filename) + if m: + info = m.groupdict('') + self.name = info['nm'] + # Reinstate the local version separator + self.version = info['vn'].replace('_', '-') + self.buildver = info['bn'] + self._filename = self.filename + else: + dirname, filename = os.path.split(filename) + m = FILENAME_RE.match(filename) + if not m: + raise DistlibException('Invalid name or ' + 'filename: %r' % filename) + if dirname: + self.dirname = os.path.abspath(dirname) + self._filename = filename + info = m.groupdict('') + self.name = info['nm'] + self.version = info['vn'] + self.buildver = info['bn'] + self.pyver = info['py'].split('.') + self.abi = info['bi'].split('.') + self.arch = info['ar'].split('.') + + @property + def filename(self): + """ + Build and return a filename from the various components. + """ + if self.buildver: + buildver = '-' + self.buildver + else: + buildver = '' + pyver = '.'.join(self.pyver) + abi = '.'.join(self.abi) + arch = '.'.join(self.arch) + # replace - with _ as a local version separator + version = self.version.replace('-', '_') + return '%s-%s%s-%s-%s-%s.whl' % (self.name, version, buildver, + pyver, abi, arch) + + @property + def exists(self): + path = os.path.join(self.dirname, self.filename) + return os.path.isfile(path) + + @property + def tags(self): + for pyver in self.pyver: + for abi in self.abi: + for arch in self.arch: + yield pyver, abi, arch + + @cached_property + def metadata(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + wrapper = codecs.getreader('utf-8') + with ZipFile(pathname, 'r') as zf: + wheel_metadata = self.get_wheel_metadata(zf) + wv = wheel_metadata['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + if file_version < (1, 1): + fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME, 'METADATA'] + else: + fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME] + result = None + for fn in fns: + try: + metadata_filename = posixpath.join(info_dir, fn) + with zf.open(metadata_filename) as bf: + wf = wrapper(bf) + result = Metadata(fileobj=wf) + if result: + break + except KeyError: + pass + if not result: + raise ValueError('Invalid wheel, because metadata is ' + 'missing: looked in %s' % ', '.join(fns)) + return result + + def get_wheel_metadata(self, zf): + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + metadata_filename = posixpath.join(info_dir, 'WHEEL') + with zf.open(metadata_filename) as bf: + wf = codecs.getreader('utf-8')(bf) + message = message_from_file(wf) + return dict(message) + + @cached_property + def info(self): + pathname = os.path.join(self.dirname, self.filename) + with ZipFile(pathname, 'r') as zf: + result = self.get_wheel_metadata(zf) + return result + + def process_shebang(self, data): + m = SHEBANG_RE.match(data) + if m: + end = m.end() + shebang, data_after_shebang = data[:end], data[end:] + # Preserve any arguments after the interpreter + if b'pythonw' in shebang.lower(): + shebang_python = SHEBANG_PYTHONW + else: + shebang_python = SHEBANG_PYTHON + m = SHEBANG_DETAIL_RE.match(shebang) + if m: + args = b' ' + m.groups()[-1] + else: + args = b'' + shebang = shebang_python + args + data = shebang + data_after_shebang + else: + cr = data.find(b'\r') + lf = data.find(b'\n') + if cr < 0 or cr > lf: + term = b'\n' + else: + if data[cr:cr + 2] == b'\r\n': + term = b'\r\n' + else: + term = b'\r' + data = SHEBANG_PYTHON + term + data + return data + + def get_hash(self, data, hash_kind=None): + if hash_kind is None: + hash_kind = self.hash_kind + try: + hasher = getattr(hashlib, hash_kind) + except AttributeError: + raise DistlibException('Unsupported hash algorithm: %r' % hash_kind) + result = hasher(data).digest() + result = base64.urlsafe_b64encode(result).rstrip(b'=').decode('ascii') + return hash_kind, result + + def write_record(self, records, record_path, base): + records = list(records) # make a copy for sorting + p = to_posix(os.path.relpath(record_path, base)) + records.append((p, '', '')) + records.sort() + with CSVWriter(record_path) as writer: + for row in records: + writer.writerow(row) + + def write_records(self, info, libdir, archive_paths): + records = [] + distinfo, info_dir = info + hasher = getattr(hashlib, self.hash_kind) + for ap, p in archive_paths: + with open(p, 'rb') as f: + data = f.read() + digest = '%s=%s' % self.get_hash(data) + size = os.path.getsize(p) + records.append((ap, digest, size)) + + p = os.path.join(distinfo, 'RECORD') + self.write_record(records, p, libdir) + ap = to_posix(os.path.join(info_dir, 'RECORD')) + archive_paths.append((ap, p)) + + def build_zip(self, pathname, archive_paths): + with ZipFile(pathname, 'w', zipfile.ZIP_DEFLATED) as zf: + for ap, p in archive_paths: + logger.debug('Wrote %s to %s in wheel', p, ap) + zf.write(p, ap) + + def build(self, paths, tags=None, wheel_version=None): + """ + Build a wheel from files in specified paths, and use any specified tags + when determining the name of the wheel. + """ + if tags is None: + tags = {} + + libkey = list(filter(lambda o: o in paths, ('purelib', 'platlib')))[0] + if libkey == 'platlib': + is_pure = 'false' + default_pyver = [IMPVER] + default_abi = [ABI] + default_arch = [ARCH] + else: + is_pure = 'true' + default_pyver = [PYVER] + default_abi = ['none'] + default_arch = ['any'] + + self.pyver = tags.get('pyver', default_pyver) + self.abi = tags.get('abi', default_abi) + self.arch = tags.get('arch', default_arch) + + libdir = paths[libkey] + + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + archive_paths = [] + + # First, stuff which is not in site-packages + for key in ('data', 'headers', 'scripts'): + if key not in paths: + continue + path = paths[key] + if os.path.isdir(path): + for root, dirs, files in os.walk(path): + for fn in files: + p = fsdecode(os.path.join(root, fn)) + rp = os.path.relpath(p, path) + ap = to_posix(os.path.join(data_dir, key, rp)) + archive_paths.append((ap, p)) + if key == 'scripts' and not p.endswith('.exe'): + with open(p, 'rb') as f: + data = f.read() + data = self.process_shebang(data) + with open(p, 'wb') as f: + f.write(data) + + # Now, stuff which is in site-packages, other than the + # distinfo stuff. + path = libdir + distinfo = None + for root, dirs, files in os.walk(path): + if root == path: + # At the top level only, save distinfo for later + # and skip it for now + for i, dn in enumerate(dirs): + dn = fsdecode(dn) + if dn.endswith('.dist-info'): + distinfo = os.path.join(root, dn) + del dirs[i] + break + assert distinfo, '.dist-info directory expected, not found' + + for fn in files: + # comment out next suite to leave .pyc files in + if fsdecode(fn).endswith(('.pyc', '.pyo')): + continue + p = os.path.join(root, fn) + rp = to_posix(os.path.relpath(p, path)) + archive_paths.append((rp, p)) + + # Now distinfo. Assumed to be flat, i.e. os.listdir is enough. + files = os.listdir(distinfo) + for fn in files: + if fn not in ('RECORD', 'INSTALLER', 'SHARED', 'WHEEL'): + p = fsdecode(os.path.join(distinfo, fn)) + ap = to_posix(os.path.join(info_dir, fn)) + archive_paths.append((ap, p)) + + wheel_metadata = [ + 'Wheel-Version: %d.%d' % (wheel_version or self.wheel_version), + 'Generator: distlib %s' % __version__, + 'Root-Is-Purelib: %s' % is_pure, + ] + for pyver, abi, arch in self.tags: + wheel_metadata.append('Tag: %s-%s-%s' % (pyver, abi, arch)) + p = os.path.join(distinfo, 'WHEEL') + with open(p, 'w') as f: + f.write('\n'.join(wheel_metadata)) + ap = to_posix(os.path.join(info_dir, 'WHEEL')) + archive_paths.append((ap, p)) + + # Now, at last, RECORD. + # Paths in here are archive paths - nothing else makes sense. + self.write_records((distinfo, info_dir), libdir, archive_paths) + # Now, ready to build the zip file + pathname = os.path.join(self.dirname, self.filename) + self.build_zip(pathname, archive_paths) + return pathname + + def install(self, paths, maker, **kwargs): + """ + Install a wheel to the specified paths. If kwarg ``warner`` is + specified, it should be a callable, which will be called with two + tuples indicating the wheel version of this software and the wheel + version in the file, if there is a discrepancy in the versions. + This can be used to issue any warnings to raise any exceptions. + If kwarg ``lib_only`` is True, only the purelib/platlib files are + installed, and the headers, scripts, data and dist-info metadata are + not written. + + The return value is a :class:`InstalledDistribution` instance unless + ``options.lib_only`` is True, in which case the return value is ``None``. + """ + + dry_run = maker.dry_run + warner = kwargs.get('warner') + lib_only = kwargs.get('lib_only', False) + + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + metadata_name = posixpath.join(info_dir, METADATA_FILENAME) + wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') + record_name = posixpath.join(info_dir, 'RECORD') + + wrapper = codecs.getreader('utf-8') + + with ZipFile(pathname, 'r') as zf: + with zf.open(wheel_metadata_name) as bwf: + wf = wrapper(bwf) + message = message_from_file(wf) + wv = message['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + if (file_version != self.wheel_version) and warner: + warner(self.wheel_version, file_version) + + if message['Root-Is-Purelib'] == 'true': + libdir = paths['purelib'] + else: + libdir = paths['platlib'] + + records = {} + with zf.open(record_name) as bf: + with CSVReader(stream=bf) as reader: + for row in reader: + p = row[0] + records[p] = row + + data_pfx = posixpath.join(data_dir, '') + info_pfx = posixpath.join(info_dir, '') + script_pfx = posixpath.join(data_dir, 'scripts', '') + + # make a new instance rather than a copy of maker's, + # as we mutate it + fileop = FileOperator(dry_run=dry_run) + fileop.record = True # so we can rollback if needed + + bc = not sys.dont_write_bytecode # Double negatives. Lovely! + + outfiles = [] # for RECORD writing + + # for script copying/shebang processing + workdir = tempfile.mkdtemp() + # set target dir later + # we default add_launchers to False, as the + # Python Launcher should be used instead + maker.source_dir = workdir + maker.target_dir = None + try: + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + # The signature file won't be in RECORD, + # and we don't currently don't do anything with it + if u_arcname.endswith('/RECORD.jws'): + continue + row = records[u_arcname] + if row[2] and str(zinfo.file_size) != row[2]: + raise DistlibException('size mismatch for ' + '%s' % u_arcname) + if row[1]: + kind, value = row[1].split('=', 1) + with zf.open(arcname) as bf: + data = bf.read() + _, digest = self.get_hash(data, kind) + if digest != value: + raise DistlibException('digest mismatch for ' + '%s' % arcname) + + if lib_only and u_arcname.startswith((info_pfx, data_pfx)): + logger.debug('lib_only: skipping %s', u_arcname) + continue + is_script = (u_arcname.startswith(script_pfx) + and not u_arcname.endswith('.exe')) + + if u_arcname.startswith(data_pfx): + _, where, rp = u_arcname.split('/', 2) + outfile = os.path.join(paths[where], convert_path(rp)) + else: + # meant for site-packages. + if u_arcname in (wheel_metadata_name, record_name): + continue + outfile = os.path.join(libdir, convert_path(u_arcname)) + if not is_script: + with zf.open(arcname) as bf: + fileop.copy_stream(bf, outfile) + outfiles.append(outfile) + # Double check the digest of the written file + if not dry_run and row[1]: + with open(outfile, 'rb') as bf: + data = bf.read() + _, newdigest = self.get_hash(data, kind) + if newdigest != digest: + raise DistlibException('digest mismatch ' + 'on write for ' + '%s' % outfile) + if bc and outfile.endswith('.py'): + try: + pyc = fileop.byte_compile(outfile) + outfiles.append(pyc) + except Exception: + # Don't give up if byte-compilation fails, + # but log it and perhaps warn the user + logger.warning('Byte-compilation failed', + exc_info=True) + else: + fn = os.path.basename(convert_path(arcname)) + workname = os.path.join(workdir, fn) + with zf.open(arcname) as bf: + fileop.copy_stream(bf, workname) + + dn, fn = os.path.split(outfile) + maker.target_dir = dn + filenames = maker.make(fn) + fileop.set_executable_mode(filenames) + outfiles.extend(filenames) + + if lib_only: + logger.debug('lib_only: returning None') + dist = None + else: + # Generate scripts + + # Try to get pydist.json so we can see if there are + # any commands to generate. If this fails (e.g. because + # of a legacy wheel), log a warning but don't give up. + commands = None + file_version = self.info['Wheel-Version'] + if file_version == '1.0': + # Use legacy info + ep = posixpath.join(info_dir, 'entry_points.txt') + try: + with zf.open(ep) as bwf: + epdata = read_exports(bwf) + commands = {} + for key in ('console', 'gui'): + k = '%s_scripts' % key + if k in epdata: + commands['wrap_%s' % key] = d = {} + for v in epdata[k].values(): + s = '%s:%s' % (v.prefix, v.suffix) + if v.flags: + s += ' %s' % v.flags + d[v.name] = s + except Exception: + logger.warning('Unable to read legacy script ' + 'metadata, so cannot generate ' + 'scripts') + else: + try: + with zf.open(metadata_name) as bwf: + wf = wrapper(bwf) + commands = json.load(wf).get('extensions') + if commands: + commands = commands.get('python.commands') + except Exception: + logger.warning('Unable to read JSON metadata, so ' + 'cannot generate scripts') + if commands: + console_scripts = commands.get('wrap_console', {}) + gui_scripts = commands.get('wrap_gui', {}) + if console_scripts or gui_scripts: + script_dir = paths.get('scripts', '') + if not os.path.isdir(script_dir): + raise ValueError('Valid script path not ' + 'specified') + maker.target_dir = script_dir + for k, v in console_scripts.items(): + script = '%s = %s' % (k, v) + filenames = maker.make(script) + fileop.set_executable_mode(filenames) + + if gui_scripts: + options = {'gui': True } + for k, v in gui_scripts.items(): + script = '%s = %s' % (k, v) + filenames = maker.make(script, options) + fileop.set_executable_mode(filenames) + + p = os.path.join(libdir, info_dir) + dist = InstalledDistribution(p) + + # Write SHARED + paths = dict(paths) # don't change passed in dict + del paths['purelib'] + del paths['platlib'] + paths['lib'] = libdir + p = dist.write_shared_locations(paths, dry_run) + if p: + outfiles.append(p) + + # Write RECORD + dist.write_installed_files(outfiles, paths['prefix'], + dry_run) + return dist + except Exception: # pragma: no cover + logger.exception('installation failed.') + fileop.rollback() + raise + finally: + shutil.rmtree(workdir) + + def _get_dylib_cache(self): + global cache + if cache is None: + # Use native string to avoid issues on 2.x: see Python #20140. + base = os.path.join(get_cache_base(), str('dylib-cache'), + sys.version[:3]) + cache = Cache(base) + return cache + + def _get_extensions(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + arcname = posixpath.join(info_dir, 'EXTENSIONS') + wrapper = codecs.getreader('utf-8') + result = [] + with ZipFile(pathname, 'r') as zf: + try: + with zf.open(arcname) as bf: + wf = wrapper(bf) + extensions = json.load(wf) + cache = self._get_dylib_cache() + prefix = cache.prefix_to_dir(pathname) + cache_base = os.path.join(cache.base, prefix) + if not os.path.isdir(cache_base): + os.makedirs(cache_base) + for name, relpath in extensions.items(): + dest = os.path.join(cache_base, convert_path(relpath)) + if not os.path.exists(dest): + extract = True + else: + file_time = os.stat(dest).st_mtime + file_time = datetime.datetime.fromtimestamp(file_time) + info = zf.getinfo(relpath) + wheel_time = datetime.datetime(*info.date_time) + extract = wheel_time > file_time + if extract: + zf.extract(relpath, cache_base) + result.append((name, dest)) + except KeyError: + pass + return result + + def is_compatible(self): + """ + Determine if a wheel is compatible with the running system. + """ + return is_compatible(self) + + def is_mountable(self): + """ + Determine if a wheel is asserted as mountable by its metadata. + """ + return True # for now - metadata details TBD + + def mount(self, append=False): + pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) + if not self.is_compatible(): + msg = 'Wheel %s not compatible with this Python.' % pathname + raise DistlibException(msg) + if not self.is_mountable(): + msg = 'Wheel %s is marked as not mountable.' % pathname + raise DistlibException(msg) + if pathname in sys.path: + logger.debug('%s already in path', pathname) + else: + if append: + sys.path.append(pathname) + else: + sys.path.insert(0, pathname) + extensions = self._get_extensions() + if extensions: + if _hook not in sys.meta_path: + sys.meta_path.append(_hook) + _hook.add(pathname, extensions) + + def unmount(self): + pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) + if pathname not in sys.path: + logger.debug('%s not in path', pathname) + else: + sys.path.remove(pathname) + if pathname in _hook.impure_wheels: + _hook.remove(pathname) + if not _hook.impure_wheels: + if _hook in sys.meta_path: + sys.meta_path.remove(_hook) + + def verify(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + metadata_name = posixpath.join(info_dir, METADATA_FILENAME) + wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') + record_name = posixpath.join(info_dir, 'RECORD') + + wrapper = codecs.getreader('utf-8') + + with ZipFile(pathname, 'r') as zf: + with zf.open(wheel_metadata_name) as bwf: + wf = wrapper(bwf) + message = message_from_file(wf) + wv = message['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + # TODO version verification + + records = {} + with zf.open(record_name) as bf: + with CSVReader(stream=bf) as reader: + for row in reader: + p = row[0] + records[p] = row + + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + if '..' in u_arcname: + raise DistlibException('invalid entry in ' + 'wheel: %r' % u_arcname) + + # The signature file won't be in RECORD, + # and we don't currently don't do anything with it + if u_arcname.endswith('/RECORD.jws'): + continue + row = records[u_arcname] + if row[2] and str(zinfo.file_size) != row[2]: + raise DistlibException('size mismatch for ' + '%s' % u_arcname) + if row[1]: + kind, value = row[1].split('=', 1) + with zf.open(arcname) as bf: + data = bf.read() + _, digest = self.get_hash(data, kind) + if digest != value: + raise DistlibException('digest mismatch for ' + '%s' % arcname) + + def update(self, modifier, dest_dir=None, **kwargs): + """ + Update the contents of a wheel in a generic way. The modifier should + be a callable which expects a dictionary argument: its keys are + archive-entry paths, and its values are absolute filesystem paths + where the contents the corresponding archive entries can be found. The + modifier is free to change the contents of the files pointed to, add + new entries and remove entries, before returning. This method will + extract the entire contents of the wheel to a temporary location, call + the modifier, and then use the passed (and possibly updated) + dictionary to write a new wheel. If ``dest_dir`` is specified, the new + wheel is written there -- otherwise, the original wheel is overwritten. + + The modifier should return True if it updated the wheel, else False. + This method returns the same value the modifier returns. + """ + + def get_version(path_map, info_dir): + version = path = None + key = '%s/%s' % (info_dir, METADATA_FILENAME) + if key not in path_map: + key = '%s/PKG-INFO' % info_dir + if key in path_map: + path = path_map[key] + version = Metadata(path=path).version + return version, path + + def update_version(version, path): + updated = None + try: + v = NormalizedVersion(version) + i = version.find('-') + if i < 0: + updated = '%s+1' % version + else: + parts = [int(s) for s in version[i + 1:].split('.')] + parts[-1] += 1 + updated = '%s+%s' % (version[:i], + '.'.join(str(i) for i in parts)) + except UnsupportedVersionError: + logger.debug('Cannot update non-compliant (PEP-440) ' + 'version %r', version) + if updated: + md = Metadata(path=path) + md.version = updated + legacy = not path.endswith(METADATA_FILENAME) + md.write(path=path, legacy=legacy) + logger.debug('Version updated from %r to %r', version, + updated) + + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + record_name = posixpath.join(info_dir, 'RECORD') + with tempdir() as workdir: + with ZipFile(pathname, 'r') as zf: + path_map = {} + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + if u_arcname == record_name: + continue + if '..' in u_arcname: + raise DistlibException('invalid entry in ' + 'wheel: %r' % u_arcname) + zf.extract(zinfo, workdir) + path = os.path.join(workdir, convert_path(u_arcname)) + path_map[u_arcname] = path + + # Remember the version. + original_version, _ = get_version(path_map, info_dir) + # Files extracted. Call the modifier. + modified = modifier(path_map, **kwargs) + if modified: + # Something changed - need to build a new wheel. + current_version, path = get_version(path_map, info_dir) + if current_version and (current_version == original_version): + # Add or update local version to signify changes. + update_version(current_version, path) + # Decide where the new wheel goes. + if dest_dir is None: + fd, newpath = tempfile.mkstemp(suffix='.whl', + prefix='wheel-update-', + dir=workdir) + os.close(fd) + else: + if not os.path.isdir(dest_dir): + raise DistlibException('Not a directory: %r' % dest_dir) + newpath = os.path.join(dest_dir, self.filename) + archive_paths = list(path_map.items()) + distinfo = os.path.join(workdir, info_dir) + info = distinfo, info_dir + self.write_records(info, workdir, archive_paths) + self.build_zip(newpath, archive_paths) + if dest_dir is None: + shutil.copyfile(newpath, pathname) + return modified + +def compatible_tags(): + """ + Return (pyver, abi, arch) tuples compatible with this Python. + """ + versions = [VER_SUFFIX] + major = VER_SUFFIX[0] + for minor in range(sys.version_info[1] - 1, - 1, -1): + versions.append(''.join([major, str(minor)])) + + abis = [] + for suffix, _, _ in imp.get_suffixes(): + if suffix.startswith('.abi'): + abis.append(suffix.split('.', 2)[1]) + abis.sort() + if ABI != 'none': + abis.insert(0, ABI) + abis.append('none') + result = [] + + arches = [ARCH] + if sys.platform == 'darwin': + m = re.match(r'(\w+)_(\d+)_(\d+)_(\w+)$', ARCH) + if m: + name, major, minor, arch = m.groups() + minor = int(minor) + matches = [arch] + if arch in ('i386', 'ppc'): + matches.append('fat') + if arch in ('i386', 'ppc', 'x86_64'): + matches.append('fat3') + if arch in ('ppc64', 'x86_64'): + matches.append('fat64') + if arch in ('i386', 'x86_64'): + matches.append('intel') + if arch in ('i386', 'x86_64', 'intel', 'ppc', 'ppc64'): + matches.append('universal') + while minor >= 0: + for match in matches: + s = '%s_%s_%s_%s' % (name, major, minor, match) + if s != ARCH: # already there + arches.append(s) + minor -= 1 + + # Most specific - our Python version, ABI and arch + for abi in abis: + for arch in arches: + result.append((''.join((IMP_PREFIX, versions[0])), abi, arch)) + + # where no ABI / arch dependency, but IMP_PREFIX dependency + for i, version in enumerate(versions): + result.append((''.join((IMP_PREFIX, version)), 'none', 'any')) + if i == 0: + result.append((''.join((IMP_PREFIX, version[0])), 'none', 'any')) + + # no IMP_PREFIX, ABI or arch dependency + for i, version in enumerate(versions): + result.append((''.join(('py', version)), 'none', 'any')) + if i == 0: + result.append((''.join(('py', version[0])), 'none', 'any')) + return set(result) + + +COMPATIBLE_TAGS = compatible_tags() + +del compatible_tags + + +def is_compatible(wheel, tags=None): + if not isinstance(wheel, Wheel): + wheel = Wheel(wheel) # assume it's a filename + result = False + if tags is None: + tags = COMPATIBLE_TAGS + for ver, abi, arch in tags: + if ver in wheel.pyver and abi in wheel.abi and arch in wheel.arch: + result = True + break + return result diff --git a/venv/Lib/site-packages/pip/_vendor/distro.py b/venv/Lib/site-packages/pip/_vendor/distro.py new file mode 100644 index 0000000..aa4defc --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/distro.py @@ -0,0 +1,1197 @@ +# Copyright 2015,2016,2017 Nir Cohen +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +The ``distro`` package (``distro`` stands for Linux Distribution) provides +information about the Linux distribution it runs on, such as a reliable +machine-readable distro ID, or version information. + +It is a renewed alternative implementation for Python's original +:py:func:`platform.linux_distribution` function, but it provides much more +functionality. An alternative implementation became necessary because Python +3.5 deprecated this function, and Python 3.7 is expected to remove it +altogether. Its predecessor function :py:func:`platform.dist` was already +deprecated since Python 2.6 and is also expected to be removed in Python 3.7. +Still, there are many cases in which access to OS distribution information +is needed. See `Python issue 1322 <https://bugs.python.org/issue1322>`_ for +more information. +""" + +import os +import re +import sys +import json +import shlex +import logging +import argparse +import subprocess + + +_UNIXCONFDIR = os.environ.get('UNIXCONFDIR', '/etc') +_OS_RELEASE_BASENAME = 'os-release' + +#: Translation table for normalizing the "ID" attribute defined in os-release +#: files, for use by the :func:`distro.id` method. +#: +#: * Key: Value as defined in the os-release file, translated to lower case, +#: with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_OS_ID = {} + +#: Translation table for normalizing the "Distributor ID" attribute returned by +#: the lsb_release command, for use by the :func:`distro.id` method. +#: +#: * Key: Value as returned by the lsb_release command, translated to lower +#: case, with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_LSB_ID = { + 'enterpriseenterprise': 'oracle', # Oracle Enterprise Linux + 'redhatenterpriseworkstation': 'rhel', # RHEL 6, 7 Workstation + 'redhatenterpriseserver': 'rhel', # RHEL 6, 7 Server +} + +#: Translation table for normalizing the distro ID derived from the file name +#: of distro release files, for use by the :func:`distro.id` method. +#: +#: * Key: Value as derived from the file name of a distro release file, +#: translated to lower case, with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_DISTRO_ID = { + 'redhat': 'rhel', # RHEL 6.x, 7.x +} + +# Pattern for content of distro release file (reversed) +_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( + r'(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)') + +# Pattern for base file name of distro release file +_DISTRO_RELEASE_BASENAME_PATTERN = re.compile( + r'(\w+)[-_](release|version)$') + +# Base file names to be ignored when searching for distro release file +_DISTRO_RELEASE_IGNORE_BASENAMES = ( + 'debian_version', + 'lsb-release', + 'oem-release', + _OS_RELEASE_BASENAME, + 'system-release' +) + + +def linux_distribution(full_distribution_name=True): + """ + Return information about the current OS distribution as a tuple + ``(id_name, version, codename)`` with items as follows: + + * ``id_name``: If *full_distribution_name* is false, the result of + :func:`distro.id`. Otherwise, the result of :func:`distro.name`. + + * ``version``: The result of :func:`distro.version`. + + * ``codename``: The result of :func:`distro.codename`. + + The interface of this function is compatible with the original + :py:func:`platform.linux_distribution` function, supporting a subset of + its parameters. + + The data it returns may not exactly be the same, because it uses more data + sources than the original function, and that may lead to different data if + the OS distribution is not consistent across multiple data sources it + provides (there are indeed such distributions ...). + + Another reason for differences is the fact that the :func:`distro.id` + method normalizes the distro ID string to a reliable machine-readable value + for a number of popular OS distributions. + """ + return _distro.linux_distribution(full_distribution_name) + + +def id(): + """ + Return the distro ID of the current distribution, as a + machine-readable string. + + For a number of OS distributions, the returned distro ID value is + *reliable*, in the sense that it is documented and that it does not change + across releases of the distribution. + + This package maintains the following reliable distro ID values: + + ============== ========================================= + Distro ID Distribution + ============== ========================================= + "ubuntu" Ubuntu + "debian" Debian + "rhel" RedHat Enterprise Linux + "centos" CentOS + "fedora" Fedora + "sles" SUSE Linux Enterprise Server + "opensuse" openSUSE + "amazon" Amazon Linux + "arch" Arch Linux + "cloudlinux" CloudLinux OS + "exherbo" Exherbo Linux + "gentoo" GenToo Linux + "ibm_powerkvm" IBM PowerKVM + "kvmibm" KVM for IBM z Systems + "linuxmint" Linux Mint + "mageia" Mageia + "mandriva" Mandriva Linux + "parallels" Parallels + "pidora" Pidora + "raspbian" Raspbian + "oracle" Oracle Linux (and Oracle Enterprise Linux) + "scientific" Scientific Linux + "slackware" Slackware + "xenserver" XenServer + "openbsd" OpenBSD + "netbsd" NetBSD + "freebsd" FreeBSD + ============== ========================================= + + If you have a need to get distros for reliable IDs added into this set, + or if you find that the :func:`distro.id` function returns a different + distro ID for one of the listed distros, please create an issue in the + `distro issue tracker`_. + + **Lookup hierarchy and transformations:** + + First, the ID is obtained from the following sources, in the specified + order. The first available and non-empty value is used: + + * the value of the "ID" attribute of the os-release file, + + * the value of the "Distributor ID" attribute returned by the lsb_release + command, + + * the first part of the file name of the distro release file, + + The so determined ID value then passes the following transformations, + before it is returned by this method: + + * it is translated to lower case, + + * blanks (which should not be there anyway) are translated to underscores, + + * a normalization of the ID is performed, based upon + `normalization tables`_. The purpose of this normalization is to ensure + that the ID is as reliable as possible, even across incompatible changes + in the OS distributions. A common reason for an incompatible change is + the addition of an os-release file, or the addition of the lsb_release + command, with ID values that differ from what was previously determined + from the distro release file name. + """ + return _distro.id() + + +def name(pretty=False): + """ + Return the name of the current OS distribution, as a human-readable + string. + + If *pretty* is false, the name is returned without version or codename. + (e.g. "CentOS Linux") + + If *pretty* is true, the version and codename are appended. + (e.g. "CentOS Linux 7.1.1503 (Core)") + + **Lookup hierarchy:** + + The name is obtained from the following sources, in the specified order. + The first available and non-empty value is used: + + * If *pretty* is false: + + - the value of the "NAME" attribute of the os-release file, + + - the value of the "Distributor ID" attribute returned by the lsb_release + command, + + - the value of the "<name>" field of the distro release file. + + * If *pretty* is true: + + - the value of the "PRETTY_NAME" attribute of the os-release file, + + - the value of the "Description" attribute returned by the lsb_release + command, + + - the value of the "<name>" field of the distro release file, appended + with the value of the pretty version ("<version_id>" and "<codename>" + fields) of the distro release file, if available. + """ + return _distro.name(pretty) + + +def version(pretty=False, best=False): + """ + Return the version of the current OS distribution, as a human-readable + string. + + If *pretty* is false, the version is returned without codename (e.g. + "7.0"). + + If *pretty* is true, the codename in parenthesis is appended, if the + codename is non-empty (e.g. "7.0 (Maipo)"). + + Some distributions provide version numbers with different precisions in + the different sources of distribution information. Examining the different + sources in a fixed priority order does not always yield the most precise + version (e.g. for Debian 8.2, or CentOS 7.1). + + The *best* parameter can be used to control the approach for the returned + version: + + If *best* is false, the first non-empty version number in priority order of + the examined sources is returned. + + If *best* is true, the most precise version number out of all examined + sources is returned. + + **Lookup hierarchy:** + + In all cases, the version number is obtained from the following sources. + If *best* is false, this order represents the priority order: + + * the value of the "VERSION_ID" attribute of the os-release file, + * the value of the "Release" attribute returned by the lsb_release + command, + * the version number parsed from the "<version_id>" field of the first line + of the distro release file, + * the version number parsed from the "PRETTY_NAME" attribute of the + os-release file, if it follows the format of the distro release files. + * the version number parsed from the "Description" attribute returned by + the lsb_release command, if it follows the format of the distro release + files. + """ + return _distro.version(pretty, best) + + +def version_parts(best=False): + """ + Return the version of the current OS distribution as a tuple + ``(major, minor, build_number)`` with items as follows: + + * ``major``: The result of :func:`distro.major_version`. + + * ``minor``: The result of :func:`distro.minor_version`. + + * ``build_number``: The result of :func:`distro.build_number`. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.version_parts(best) + + +def major_version(best=False): + """ + Return the major version of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The major version is the first + part of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.major_version(best) + + +def minor_version(best=False): + """ + Return the minor version of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The minor version is the second + part of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.minor_version(best) + + +def build_number(best=False): + """ + Return the build number of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The build number is the third part + of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.build_number(best) + + +def like(): + """ + Return a space-separated list of distro IDs of distributions that are + closely related to the current OS distribution in regards to packaging + and programming interfaces, for example distributions the current + distribution is a derivative from. + + **Lookup hierarchy:** + + This information item is only provided by the os-release file. + For details, see the description of the "ID_LIKE" attribute in the + `os-release man page + <http://www.freedesktop.org/software/systemd/man/os-release.html>`_. + """ + return _distro.like() + + +def codename(): + """ + Return the codename for the release of the current OS distribution, + as a string. + + If the distribution does not have a codename, an empty string is returned. + + Note that the returned codename is not always really a codename. For + example, openSUSE returns "x86_64". This function does not handle such + cases in any special way and just returns the string it finds, if any. + + **Lookup hierarchy:** + + * the codename within the "VERSION" attribute of the os-release file, if + provided, + + * the value of the "Codename" attribute returned by the lsb_release + command, + + * the value of the "<codename>" field of the distro release file. + """ + return _distro.codename() + + +def info(pretty=False, best=False): + """ + Return certain machine-readable information items about the current OS + distribution in a dictionary, as shown in the following example: + + .. sourcecode:: python + + { + 'id': 'rhel', + 'version': '7.0', + 'version_parts': { + 'major': '7', + 'minor': '0', + 'build_number': '' + }, + 'like': 'fedora', + 'codename': 'Maipo' + } + + The dictionary structure and keys are always the same, regardless of which + information items are available in the underlying data sources. The values + for the various keys are as follows: + + * ``id``: The result of :func:`distro.id`. + + * ``version``: The result of :func:`distro.version`. + + * ``version_parts -> major``: The result of :func:`distro.major_version`. + + * ``version_parts -> minor``: The result of :func:`distro.minor_version`. + + * ``version_parts -> build_number``: The result of + :func:`distro.build_number`. + + * ``like``: The result of :func:`distro.like`. + + * ``codename``: The result of :func:`distro.codename`. + + For a description of the *pretty* and *best* parameters, see the + :func:`distro.version` method. + """ + return _distro.info(pretty, best) + + +def os_release_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the os-release file data source of the current OS distribution. + + See `os-release file`_ for details about these information items. + """ + return _distro.os_release_info() + + +def lsb_release_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the lsb_release command data source of the current OS distribution. + + See `lsb_release command output`_ for details about these information + items. + """ + return _distro.lsb_release_info() + + +def distro_release_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the distro release file data source of the current OS distribution. + + See `distro release file`_ for details about these information items. + """ + return _distro.distro_release_info() + + +def uname_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the distro release file data source of the current OS distribution. + """ + return _distro.uname_info() + + +def os_release_attr(attribute): + """ + Return a single named information item from the os-release file data source + of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `os-release file`_ for details about these information items. + """ + return _distro.os_release_attr(attribute) + + +def lsb_release_attr(attribute): + """ + Return a single named information item from the lsb_release command output + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `lsb_release command output`_ for details about these information + items. + """ + return _distro.lsb_release_attr(attribute) + + +def distro_release_attr(attribute): + """ + Return a single named information item from the distro release file + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `distro release file`_ for details about these information items. + """ + return _distro.distro_release_attr(attribute) + + +def uname_attr(attribute): + """ + Return a single named information item from the distro release file + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + """ + return _distro.uname_attr(attribute) + + +class cached_property(object): + """A version of @property which caches the value. On access, it calls the + underlying function and sets the value in `__dict__` so future accesses + will not re-call the property. + """ + def __init__(self, f): + self._fname = f.__name__ + self._f = f + + def __get__(self, obj, owner): + assert obj is not None, 'call {} on an instance'.format(self._fname) + ret = obj.__dict__[self._fname] = self._f(obj) + return ret + + +class LinuxDistribution(object): + """ + Provides information about a OS distribution. + + This package creates a private module-global instance of this class with + default initialization arguments, that is used by the + `consolidated accessor functions`_ and `single source accessor functions`_. + By using default initialization arguments, that module-global instance + returns data about the current OS distribution (i.e. the distro this + package runs on). + + Normally, it is not necessary to create additional instances of this class. + However, in situations where control is needed over the exact data sources + that are used, instances of this class can be created with a specific + distro release file, or a specific os-release file, or without invoking the + lsb_release command. + """ + + def __init__(self, + include_lsb=True, + os_release_file='', + distro_release_file='', + include_uname=True): + """ + The initialization method of this class gathers information from the + available data sources, and stores that in private instance attributes. + Subsequent access to the information items uses these private instance + attributes, so that the data sources are read only once. + + Parameters: + + * ``include_lsb`` (bool): Controls whether the + `lsb_release command output`_ is included as a data source. + + If the lsb_release command is not available in the program execution + path, the data source for the lsb_release command will be empty. + + * ``os_release_file`` (string): The path name of the + `os-release file`_ that is to be used as a data source. + + An empty string (the default) will cause the default path name to + be used (see `os-release file`_ for details). + + If the specified or defaulted os-release file does not exist, the + data source for the os-release file will be empty. + + * ``distro_release_file`` (string): The path name of the + `distro release file`_ that is to be used as a data source. + + An empty string (the default) will cause a default search algorithm + to be used (see `distro release file`_ for details). + + If the specified distro release file does not exist, or if no default + distro release file can be found, the data source for the distro + release file will be empty. + + * ``include_name`` (bool): Controls whether uname command output is + included as a data source. If the uname command is not available in + the program execution path the data source for the uname command will + be empty. + + Public instance attributes: + + * ``os_release_file`` (string): The path name of the + `os-release file`_ that is actually used as a data source. The + empty string if no distro release file is used as a data source. + + * ``distro_release_file`` (string): The path name of the + `distro release file`_ that is actually used as a data source. The + empty string if no distro release file is used as a data source. + + * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter. + This controls whether the lsb information will be loaded. + + * ``include_uname`` (bool): The result of the ``include_uname`` + parameter. This controls whether the uname information will + be loaded. + + Raises: + + * :py:exc:`IOError`: Some I/O issue with an os-release file or distro + release file. + + * :py:exc:`subprocess.CalledProcessError`: The lsb_release command had + some issue (other than not being available in the program execution + path). + + * :py:exc:`UnicodeError`: A data source has unexpected characters or + uses an unexpected encoding. + """ + self.os_release_file = os_release_file or \ + os.path.join(_UNIXCONFDIR, _OS_RELEASE_BASENAME) + self.distro_release_file = distro_release_file or '' # updated later + self.include_lsb = include_lsb + self.include_uname = include_uname + + def __repr__(self): + """Return repr of all info + """ + return \ + "LinuxDistribution(" \ + "os_release_file={self.os_release_file!r}, " \ + "distro_release_file={self.distro_release_file!r}, " \ + "include_lsb={self.include_lsb!r}, " \ + "include_uname={self.include_uname!r}, " \ + "_os_release_info={self._os_release_info!r}, " \ + "_lsb_release_info={self._lsb_release_info!r}, " \ + "_distro_release_info={self._distro_release_info!r}, " \ + "_uname_info={self._uname_info!r})".format( + self=self) + + def linux_distribution(self, full_distribution_name=True): + """ + Return information about the OS distribution that is compatible + with Python's :func:`platform.linux_distribution`, supporting a subset + of its parameters. + + For details, see :func:`distro.linux_distribution`. + """ + return ( + self.name() if full_distribution_name else self.id(), + self.version(), + self.codename() + ) + + def id(self): + """Return the distro ID of the OS distribution, as a string. + + For details, see :func:`distro.id`. + """ + def normalize(distro_id, table): + distro_id = distro_id.lower().replace(' ', '_') + return table.get(distro_id, distro_id) + + distro_id = self.os_release_attr('id') + if distro_id: + return normalize(distro_id, NORMALIZED_OS_ID) + + distro_id = self.lsb_release_attr('distributor_id') + if distro_id: + return normalize(distro_id, NORMALIZED_LSB_ID) + + distro_id = self.distro_release_attr('id') + if distro_id: + return normalize(distro_id, NORMALIZED_DISTRO_ID) + + distro_id = self.uname_attr('id') + if distro_id: + return normalize(distro_id, NORMALIZED_DISTRO_ID) + + return '' + + def name(self, pretty=False): + """ + Return the name of the OS distribution, as a string. + + For details, see :func:`distro.name`. + """ + name = self.os_release_attr('name') \ + or self.lsb_release_attr('distributor_id') \ + or self.distro_release_attr('name') \ + or self.uname_attr('name') + if pretty: + name = self.os_release_attr('pretty_name') \ + or self.lsb_release_attr('description') + if not name: + name = self.distro_release_attr('name') \ + or self.uname_attr('name') + version = self.version(pretty=True) + if version: + name = name + ' ' + version + return name or '' + + def version(self, pretty=False, best=False): + """ + Return the version of the OS distribution, as a string. + + For details, see :func:`distro.version`. + """ + versions = [ + self.os_release_attr('version_id'), + self.lsb_release_attr('release'), + self.distro_release_attr('version_id'), + self._parse_distro_release_content( + self.os_release_attr('pretty_name')).get('version_id', ''), + self._parse_distro_release_content( + self.lsb_release_attr('description')).get('version_id', ''), + self.uname_attr('release') + ] + version = '' + if best: + # This algorithm uses the last version in priority order that has + # the best precision. If the versions are not in conflict, that + # does not matter; otherwise, using the last one instead of the + # first one might be considered a surprise. + for v in versions: + if v.count(".") > version.count(".") or version == '': + version = v + else: + for v in versions: + if v != '': + version = v + break + if pretty and version and self.codename(): + version = u'{0} ({1})'.format(version, self.codename()) + return version + + def version_parts(self, best=False): + """ + Return the version of the OS distribution, as a tuple of version + numbers. + + For details, see :func:`distro.version_parts`. + """ + version_str = self.version(best=best) + if version_str: + version_regex = re.compile(r'(\d+)\.?(\d+)?\.?(\d+)?') + matches = version_regex.match(version_str) + if matches: + major, minor, build_number = matches.groups() + return major, minor or '', build_number or '' + return '', '', '' + + def major_version(self, best=False): + """ + Return the major version number of the current distribution. + + For details, see :func:`distro.major_version`. + """ + return self.version_parts(best)[0] + + def minor_version(self, best=False): + """ + Return the minor version number of the current distribution. + + For details, see :func:`distro.minor_version`. + """ + return self.version_parts(best)[1] + + def build_number(self, best=False): + """ + Return the build number of the current distribution. + + For details, see :func:`distro.build_number`. + """ + return self.version_parts(best)[2] + + def like(self): + """ + Return the IDs of distributions that are like the OS distribution. + + For details, see :func:`distro.like`. + """ + return self.os_release_attr('id_like') or '' + + def codename(self): + """ + Return the codename of the OS distribution. + + For details, see :func:`distro.codename`. + """ + return self.os_release_attr('codename') \ + or self.lsb_release_attr('codename') \ + or self.distro_release_attr('codename') \ + or '' + + def info(self, pretty=False, best=False): + """ + Return certain machine-readable information about the OS + distribution. + + For details, see :func:`distro.info`. + """ + return dict( + id=self.id(), + version=self.version(pretty, best), + version_parts=dict( + major=self.major_version(best), + minor=self.minor_version(best), + build_number=self.build_number(best) + ), + like=self.like(), + codename=self.codename(), + ) + + def os_release_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the os-release file data source of the OS distribution. + + For details, see :func:`distro.os_release_info`. + """ + return self._os_release_info + + def lsb_release_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the lsb_release command data source of the OS + distribution. + + For details, see :func:`distro.lsb_release_info`. + """ + return self._lsb_release_info + + def distro_release_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the distro release file data source of the OS + distribution. + + For details, see :func:`distro.distro_release_info`. + """ + return self._distro_release_info + + def uname_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the uname command data source of the OS distribution. + + For details, see :func:`distro.uname_info`. + """ + + def os_release_attr(self, attribute): + """ + Return a single named information item from the os-release file data + source of the OS distribution. + + For details, see :func:`distro.os_release_attr`. + """ + return self._os_release_info.get(attribute, '') + + def lsb_release_attr(self, attribute): + """ + Return a single named information item from the lsb_release command + output data source of the OS distribution. + + For details, see :func:`distro.lsb_release_attr`. + """ + return self._lsb_release_info.get(attribute, '') + + def distro_release_attr(self, attribute): + """ + Return a single named information item from the distro release file + data source of the OS distribution. + + For details, see :func:`distro.distro_release_attr`. + """ + return self._distro_release_info.get(attribute, '') + + def uname_attr(self, attribute): + """ + Return a single named information item from the uname command + output data source of the OS distribution. + + For details, see :func:`distro.uname_release_attr`. + """ + return self._uname_info.get(attribute, '') + + @cached_property + def _os_release_info(self): + """ + Get the information items from the specified os-release file. + + Returns: + A dictionary containing all information items. + """ + if os.path.isfile(self.os_release_file): + with open(self.os_release_file) as release_file: + return self._parse_os_release_content(release_file) + return {} + + @staticmethod + def _parse_os_release_content(lines): + """ + Parse the lines of an os-release file. + + Parameters: + + * lines: Iterable through the lines in the os-release file. + Each line must be a unicode string or a UTF-8 encoded byte + string. + + Returns: + A dictionary containing all information items. + """ + props = {} + lexer = shlex.shlex(lines, posix=True) + lexer.whitespace_split = True + + # The shlex module defines its `wordchars` variable using literals, + # making it dependent on the encoding of the Python source file. + # In Python 2.6 and 2.7, the shlex source file is encoded in + # 'iso-8859-1', and the `wordchars` variable is defined as a byte + # string. This causes a UnicodeDecodeError to be raised when the + # parsed content is a unicode object. The following fix resolves that + # (... but it should be fixed in shlex...): + if sys.version_info[0] == 2 and isinstance(lexer.wordchars, bytes): + lexer.wordchars = lexer.wordchars.decode('iso-8859-1') + + tokens = list(lexer) + for token in tokens: + # At this point, all shell-like parsing has been done (i.e. + # comments processed, quotes and backslash escape sequences + # processed, multi-line values assembled, trailing newlines + # stripped, etc.), so the tokens are now either: + # * variable assignments: var=value + # * commands or their arguments (not allowed in os-release) + if '=' in token: + k, v = token.split('=', 1) + if isinstance(v, bytes): + v = v.decode('utf-8') + props[k.lower()] = v + if k == 'VERSION': + # this handles cases in which the codename is in + # the `(CODENAME)` (rhel, centos, fedora) format + # or in the `, CODENAME` format (Ubuntu). + codename = re.search(r'(\(\D+\))|,(\s+)?\D+', v) + if codename: + codename = codename.group() + codename = codename.strip('()') + codename = codename.strip(',') + codename = codename.strip() + # codename appears within paranthese. + props['codename'] = codename + else: + props['codename'] = '' + else: + # Ignore any tokens that are not variable assignments + pass + return props + + @cached_property + def _lsb_release_info(self): + """ + Get the information items from the lsb_release command output. + + Returns: + A dictionary containing all information items. + """ + if not self.include_lsb: + return {} + with open(os.devnull, 'w') as devnull: + try: + cmd = ('lsb_release', '-a') + stdout = subprocess.check_output(cmd, stderr=devnull) + except OSError: # Command not found + return {} + content = stdout.decode(sys.getfilesystemencoding()).splitlines() + return self._parse_lsb_release_content(content) + + @staticmethod + def _parse_lsb_release_content(lines): + """ + Parse the output of the lsb_release command. + + Parameters: + + * lines: Iterable through the lines of the lsb_release output. + Each line must be a unicode string or a UTF-8 encoded byte + string. + + Returns: + A dictionary containing all information items. + """ + props = {} + for line in lines: + kv = line.strip('\n').split(':', 1) + if len(kv) != 2: + # Ignore lines without colon. + continue + k, v = kv + props.update({k.replace(' ', '_').lower(): v.strip()}) + return props + + @cached_property + def _uname_info(self): + with open(os.devnull, 'w') as devnull: + try: + cmd = ('uname', '-rs') + stdout = subprocess.check_output(cmd, stderr=devnull) + except OSError: + return {} + content = stdout.decode(sys.getfilesystemencoding()).splitlines() + return self._parse_uname_content(content) + + @staticmethod + def _parse_uname_content(lines): + props = {} + match = re.search(r'^([^\s]+)\s+([\d\.]+)', lines[0].strip()) + if match: + name, version = match.groups() + + # This is to prevent the Linux kernel version from + # appearing as the 'best' version on otherwise + # identifiable distributions. + if name == 'Linux': + return {} + props['id'] = name.lower() + props['name'] = name + props['release'] = version + return props + + @cached_property + def _distro_release_info(self): + """ + Get the information items from the specified distro release file. + + Returns: + A dictionary containing all information items. + """ + if self.distro_release_file: + # If it was specified, we use it and parse what we can, even if + # its file name or content does not match the expected pattern. + distro_info = self._parse_distro_release_file( + self.distro_release_file) + basename = os.path.basename(self.distro_release_file) + # The file name pattern for user-specified distro release files + # is somewhat more tolerant (compared to when searching for the + # file), because we want to use what was specified as best as + # possible. + match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) + if match: + distro_info['id'] = match.group(1) + return distro_info + else: + try: + basenames = os.listdir(_UNIXCONFDIR) + # We sort for repeatability in cases where there are multiple + # distro specific files; e.g. CentOS, Oracle, Enterprise all + # containing `redhat-release` on top of their own. + basenames.sort() + except OSError: + # This may occur when /etc is not readable but we can't be + # sure about the *-release files. Check common entries of + # /etc for information. If they turn out to not be there the + # error is handled in `_parse_distro_release_file()`. + basenames = ['SuSE-release', + 'arch-release', + 'base-release', + 'centos-release', + 'fedora-release', + 'gentoo-release', + 'mageia-release', + 'mandrake-release', + 'mandriva-release', + 'mandrivalinux-release', + 'manjaro-release', + 'oracle-release', + 'redhat-release', + 'sl-release', + 'slackware-version'] + for basename in basenames: + if basename in _DISTRO_RELEASE_IGNORE_BASENAMES: + continue + match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) + if match: + filepath = os.path.join(_UNIXCONFDIR, basename) + distro_info = self._parse_distro_release_file(filepath) + if 'name' in distro_info: + # The name is always present if the pattern matches + self.distro_release_file = filepath + distro_info['id'] = match.group(1) + return distro_info + return {} + + def _parse_distro_release_file(self, filepath): + """ + Parse a distro release file. + + Parameters: + + * filepath: Path name of the distro release file. + + Returns: + A dictionary containing all information items. + """ + try: + with open(filepath) as fp: + # Only parse the first line. For instance, on SLES there + # are multiple lines. We don't want them... + return self._parse_distro_release_content(fp.readline()) + except (OSError, IOError): + # Ignore not being able to read a specific, seemingly version + # related file. + # See https://github.com/nir0s/distro/issues/162 + return {} + + @staticmethod + def _parse_distro_release_content(line): + """ + Parse a line from a distro release file. + + Parameters: + * line: Line from the distro release file. Must be a unicode string + or a UTF-8 encoded byte string. + + Returns: + A dictionary containing all information items. + """ + if isinstance(line, bytes): + line = line.decode('utf-8') + matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match( + line.strip()[::-1]) + distro_info = {} + if matches: + # regexp ensures non-None + distro_info['name'] = matches.group(3)[::-1] + if matches.group(2): + distro_info['version_id'] = matches.group(2)[::-1] + if matches.group(1): + distro_info['codename'] = matches.group(1)[::-1] + elif line: + distro_info['name'] = line.strip() + return distro_info + + +_distro = LinuxDistribution() + + +def main(): + logger = logging.getLogger(__name__) + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler(sys.stdout)) + + parser = argparse.ArgumentParser(description="OS distro info tool") + parser.add_argument( + '--json', + '-j', + help="Output in machine readable format", + action="store_true") + args = parser.parse_args() + + if args.json: + logger.info(json.dumps(info(), indent=4, sort_keys=True)) + else: + logger.info('Name: %s', name(pretty=True)) + distribution_version = version(pretty=True) + logger.info('Version: %s', distribution_version) + distribution_codename = codename() + logger.info('Codename: %s', distribution_codename) + + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/__init__.py b/venv/Lib/site-packages/pip/_vendor/html5lib/__init__.py new file mode 100644 index 0000000..0491234 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/__init__.py @@ -0,0 +1,35 @@ +""" +HTML parsing library based on the `WHATWG HTML specification +<https://whatwg.org/html>`_. The parser is designed to be compatible with +existing HTML found in the wild and implements well-defined error recovery that +is largely compatible with modern desktop web browsers. + +Example usage:: + + from pip._vendor import html5lib + with open("my_document.html", "rb") as f: + tree = html5lib.parse(f) + +For convenience, this module re-exports the following names: + +* :func:`~.html5parser.parse` +* :func:`~.html5parser.parseFragment` +* :class:`~.html5parser.HTMLParser` +* :func:`~.treebuilders.getTreeBuilder` +* :func:`~.treewalkers.getTreeWalker` +* :func:`~.serializer.serialize` +""" + +from __future__ import absolute_import, division, unicode_literals + +from .html5parser import HTMLParser, parse, parseFragment +from .treebuilders import getTreeBuilder +from .treewalkers import getTreeWalker +from .serializer import serialize + +__all__ = ["HTMLParser", "parse", "parseFragment", "getTreeBuilder", + "getTreeWalker", "serialize"] + +# this has to be at the top level, see how setup.py parses this +#: Distribution version number. +__version__ = "1.0.1" diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_ihatexml.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_ihatexml.py new file mode 100644 index 0000000..4c77717 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/_ihatexml.py @@ -0,0 +1,288 @@ +from __future__ import absolute_import, division, unicode_literals + +import re +import warnings + +from .constants import DataLossWarning + +baseChar = """ +[#x0041-#x005A] | [#x0061-#x007A] | [#x00C0-#x00D6] | [#x00D8-#x00F6] | +[#x00F8-#x00FF] | [#x0100-#x0131] | [#x0134-#x013E] | [#x0141-#x0148] | +[#x014A-#x017E] | [#x0180-#x01C3] | [#x01CD-#x01F0] | [#x01F4-#x01F5] | +[#x01FA-#x0217] | [#x0250-#x02A8] | [#x02BB-#x02C1] | #x0386 | +[#x0388-#x038A] | #x038C | [#x038E-#x03A1] | [#x03A3-#x03CE] | +[#x03D0-#x03D6] | #x03DA | #x03DC | #x03DE | #x03E0 | [#x03E2-#x03F3] | +[#x0401-#x040C] | [#x040E-#x044F] | [#x0451-#x045C] | [#x045E-#x0481] | +[#x0490-#x04C4] | [#x04C7-#x04C8] | [#x04CB-#x04CC] | [#x04D0-#x04EB] | +[#x04EE-#x04F5] | [#x04F8-#x04F9] | [#x0531-#x0556] | #x0559 | +[#x0561-#x0586] | [#x05D0-#x05EA] | [#x05F0-#x05F2] | [#x0621-#x063A] | +[#x0641-#x064A] | [#x0671-#x06B7] | [#x06BA-#x06BE] | [#x06C0-#x06CE] | +[#x06D0-#x06D3] | #x06D5 | [#x06E5-#x06E6] | [#x0905-#x0939] | #x093D | +[#x0958-#x0961] | [#x0985-#x098C] | [#x098F-#x0990] | [#x0993-#x09A8] | +[#x09AA-#x09B0] | #x09B2 | [#x09B6-#x09B9] | [#x09DC-#x09DD] | +[#x09DF-#x09E1] | [#x09F0-#x09F1] | [#x0A05-#x0A0A] | [#x0A0F-#x0A10] | +[#x0A13-#x0A28] | [#x0A2A-#x0A30] | [#x0A32-#x0A33] | [#x0A35-#x0A36] | +[#x0A38-#x0A39] | [#x0A59-#x0A5C] | #x0A5E | [#x0A72-#x0A74] | +[#x0A85-#x0A8B] | #x0A8D | [#x0A8F-#x0A91] | [#x0A93-#x0AA8] | +[#x0AAA-#x0AB0] | [#x0AB2-#x0AB3] | [#x0AB5-#x0AB9] | #x0ABD | #x0AE0 | +[#x0B05-#x0B0C] | [#x0B0F-#x0B10] | [#x0B13-#x0B28] | [#x0B2A-#x0B30] | +[#x0B32-#x0B33] | [#x0B36-#x0B39] | #x0B3D | [#x0B5C-#x0B5D] | +[#x0B5F-#x0B61] | [#x0B85-#x0B8A] | [#x0B8E-#x0B90] | [#x0B92-#x0B95] | +[#x0B99-#x0B9A] | #x0B9C | [#x0B9E-#x0B9F] | [#x0BA3-#x0BA4] | +[#x0BA8-#x0BAA] | [#x0BAE-#x0BB5] | [#x0BB7-#x0BB9] | [#x0C05-#x0C0C] | +[#x0C0E-#x0C10] | [#x0C12-#x0C28] | [#x0C2A-#x0C33] | [#x0C35-#x0C39] | +[#x0C60-#x0C61] | [#x0C85-#x0C8C] | [#x0C8E-#x0C90] | [#x0C92-#x0CA8] | +[#x0CAA-#x0CB3] | [#x0CB5-#x0CB9] | #x0CDE | [#x0CE0-#x0CE1] | +[#x0D05-#x0D0C] | [#x0D0E-#x0D10] | [#x0D12-#x0D28] | [#x0D2A-#x0D39] | +[#x0D60-#x0D61] | [#x0E01-#x0E2E] | #x0E30 | [#x0E32-#x0E33] | +[#x0E40-#x0E45] | [#x0E81-#x0E82] | #x0E84 | [#x0E87-#x0E88] | #x0E8A | +#x0E8D | [#x0E94-#x0E97] | [#x0E99-#x0E9F] | [#x0EA1-#x0EA3] | #x0EA5 | +#x0EA7 | [#x0EAA-#x0EAB] | [#x0EAD-#x0EAE] | #x0EB0 | [#x0EB2-#x0EB3] | +#x0EBD | [#x0EC0-#x0EC4] | [#x0F40-#x0F47] | [#x0F49-#x0F69] | +[#x10A0-#x10C5] | [#x10D0-#x10F6] | #x1100 | [#x1102-#x1103] | +[#x1105-#x1107] | #x1109 | [#x110B-#x110C] | [#x110E-#x1112] | #x113C | +#x113E | #x1140 | #x114C | #x114E | #x1150 | [#x1154-#x1155] | #x1159 | +[#x115F-#x1161] | #x1163 | #x1165 | #x1167 | #x1169 | [#x116D-#x116E] | +[#x1172-#x1173] | #x1175 | #x119E | #x11A8 | #x11AB | [#x11AE-#x11AF] | +[#x11B7-#x11B8] | #x11BA | [#x11BC-#x11C2] | #x11EB | #x11F0 | #x11F9 | +[#x1E00-#x1E9B] | [#x1EA0-#x1EF9] | [#x1F00-#x1F15] | [#x1F18-#x1F1D] | +[#x1F20-#x1F45] | [#x1F48-#x1F4D] | [#x1F50-#x1F57] | #x1F59 | #x1F5B | +#x1F5D | [#x1F5F-#x1F7D] | [#x1F80-#x1FB4] | [#x1FB6-#x1FBC] | #x1FBE | +[#x1FC2-#x1FC4] | [#x1FC6-#x1FCC] | [#x1FD0-#x1FD3] | [#x1FD6-#x1FDB] | +[#x1FE0-#x1FEC] | [#x1FF2-#x1FF4] | [#x1FF6-#x1FFC] | #x2126 | +[#x212A-#x212B] | #x212E | [#x2180-#x2182] | [#x3041-#x3094] | +[#x30A1-#x30FA] | [#x3105-#x312C] | [#xAC00-#xD7A3]""" + +ideographic = """[#x4E00-#x9FA5] | #x3007 | [#x3021-#x3029]""" + +combiningCharacter = """ +[#x0300-#x0345] | [#x0360-#x0361] | [#x0483-#x0486] | [#x0591-#x05A1] | +[#x05A3-#x05B9] | [#x05BB-#x05BD] | #x05BF | [#x05C1-#x05C2] | #x05C4 | +[#x064B-#x0652] | #x0670 | [#x06D6-#x06DC] | [#x06DD-#x06DF] | +[#x06E0-#x06E4] | [#x06E7-#x06E8] | [#x06EA-#x06ED] | [#x0901-#x0903] | +#x093C | [#x093E-#x094C] | #x094D | [#x0951-#x0954] | [#x0962-#x0963] | +[#x0981-#x0983] | #x09BC | #x09BE | #x09BF | [#x09C0-#x09C4] | +[#x09C7-#x09C8] | [#x09CB-#x09CD] | #x09D7 | [#x09E2-#x09E3] | #x0A02 | +#x0A3C | #x0A3E | #x0A3F | [#x0A40-#x0A42] | [#x0A47-#x0A48] | +[#x0A4B-#x0A4D] | [#x0A70-#x0A71] | [#x0A81-#x0A83] | #x0ABC | +[#x0ABE-#x0AC5] | [#x0AC7-#x0AC9] | [#x0ACB-#x0ACD] | [#x0B01-#x0B03] | +#x0B3C | [#x0B3E-#x0B43] | [#x0B47-#x0B48] | [#x0B4B-#x0B4D] | +[#x0B56-#x0B57] | [#x0B82-#x0B83] | [#x0BBE-#x0BC2] | [#x0BC6-#x0BC8] | +[#x0BCA-#x0BCD] | #x0BD7 | [#x0C01-#x0C03] | [#x0C3E-#x0C44] | +[#x0C46-#x0C48] | [#x0C4A-#x0C4D] | [#x0C55-#x0C56] | [#x0C82-#x0C83] | +[#x0CBE-#x0CC4] | [#x0CC6-#x0CC8] | [#x0CCA-#x0CCD] | [#x0CD5-#x0CD6] | +[#x0D02-#x0D03] | [#x0D3E-#x0D43] | [#x0D46-#x0D48] | [#x0D4A-#x0D4D] | +#x0D57 | #x0E31 | [#x0E34-#x0E3A] | [#x0E47-#x0E4E] | #x0EB1 | +[#x0EB4-#x0EB9] | [#x0EBB-#x0EBC] | [#x0EC8-#x0ECD] | [#x0F18-#x0F19] | +#x0F35 | #x0F37 | #x0F39 | #x0F3E | #x0F3F | [#x0F71-#x0F84] | +[#x0F86-#x0F8B] | [#x0F90-#x0F95] | #x0F97 | [#x0F99-#x0FAD] | +[#x0FB1-#x0FB7] | #x0FB9 | [#x20D0-#x20DC] | #x20E1 | [#x302A-#x302F] | +#x3099 | #x309A""" + +digit = """ +[#x0030-#x0039] | [#x0660-#x0669] | [#x06F0-#x06F9] | [#x0966-#x096F] | +[#x09E6-#x09EF] | [#x0A66-#x0A6F] | [#x0AE6-#x0AEF] | [#x0B66-#x0B6F] | +[#x0BE7-#x0BEF] | [#x0C66-#x0C6F] | [#x0CE6-#x0CEF] | [#x0D66-#x0D6F] | +[#x0E50-#x0E59] | [#x0ED0-#x0ED9] | [#x0F20-#x0F29]""" + +extender = """ +#x00B7 | #x02D0 | #x02D1 | #x0387 | #x0640 | #x0E46 | #x0EC6 | #x3005 | +#[#x3031-#x3035] | [#x309D-#x309E] | [#x30FC-#x30FE]""" + +letter = " | ".join([baseChar, ideographic]) + +# Without the +name = " | ".join([letter, digit, ".", "-", "_", combiningCharacter, + extender]) +nameFirst = " | ".join([letter, "_"]) + +reChar = re.compile(r"#x([\d|A-F]{4,4})") +reCharRange = re.compile(r"\[#x([\d|A-F]{4,4})-#x([\d|A-F]{4,4})\]") + + +def charStringToList(chars): + charRanges = [item.strip() for item in chars.split(" | ")] + rv = [] + for item in charRanges: + foundMatch = False + for regexp in (reChar, reCharRange): + match = regexp.match(item) + if match is not None: + rv.append([hexToInt(item) for item in match.groups()]) + if len(rv[-1]) == 1: + rv[-1] = rv[-1] * 2 + foundMatch = True + break + if not foundMatch: + assert len(item) == 1 + + rv.append([ord(item)] * 2) + rv = normaliseCharList(rv) + return rv + + +def normaliseCharList(charList): + charList = sorted(charList) + for item in charList: + assert item[1] >= item[0] + rv = [] + i = 0 + while i < len(charList): + j = 1 + rv.append(charList[i]) + while i + j < len(charList) and charList[i + j][0] <= rv[-1][1] + 1: + rv[-1][1] = charList[i + j][1] + j += 1 + i += j + return rv + +# We don't really support characters above the BMP :( +max_unicode = int("FFFF", 16) + + +def missingRanges(charList): + rv = [] + if charList[0] != 0: + rv.append([0, charList[0][0] - 1]) + for i, item in enumerate(charList[:-1]): + rv.append([item[1] + 1, charList[i + 1][0] - 1]) + if charList[-1][1] != max_unicode: + rv.append([charList[-1][1] + 1, max_unicode]) + return rv + + +def listToRegexpStr(charList): + rv = [] + for item in charList: + if item[0] == item[1]: + rv.append(escapeRegexp(chr(item[0]))) + else: + rv.append(escapeRegexp(chr(item[0])) + "-" + + escapeRegexp(chr(item[1]))) + return "[%s]" % "".join(rv) + + +def hexToInt(hex_str): + return int(hex_str, 16) + + +def escapeRegexp(string): + specialCharacters = (".", "^", "$", "*", "+", "?", "{", "}", + "[", "]", "|", "(", ")", "-") + for char in specialCharacters: + string = string.replace(char, "\\" + char) + + return string + +# output from the above +nonXmlNameBMPRegexp = re.compile('[\x00-,/:-@\\[-\\^`\\{-\xb6\xb8-\xbf\xd7\xf7\u0132-\u0133\u013f-\u0140\u0149\u017f\u01c4-\u01cc\u01f1-\u01f3\u01f6-\u01f9\u0218-\u024f\u02a9-\u02ba\u02c2-\u02cf\u02d2-\u02ff\u0346-\u035f\u0362-\u0385\u038b\u038d\u03a2\u03cf\u03d7-\u03d9\u03db\u03dd\u03df\u03e1\u03f4-\u0400\u040d\u0450\u045d\u0482\u0487-\u048f\u04c5-\u04c6\u04c9-\u04ca\u04cd-\u04cf\u04ec-\u04ed\u04f6-\u04f7\u04fa-\u0530\u0557-\u0558\u055a-\u0560\u0587-\u0590\u05a2\u05ba\u05be\u05c0\u05c3\u05c5-\u05cf\u05eb-\u05ef\u05f3-\u0620\u063b-\u063f\u0653-\u065f\u066a-\u066f\u06b8-\u06b9\u06bf\u06cf\u06d4\u06e9\u06ee-\u06ef\u06fa-\u0900\u0904\u093a-\u093b\u094e-\u0950\u0955-\u0957\u0964-\u0965\u0970-\u0980\u0984\u098d-\u098e\u0991-\u0992\u09a9\u09b1\u09b3-\u09b5\u09ba-\u09bb\u09bd\u09c5-\u09c6\u09c9-\u09ca\u09ce-\u09d6\u09d8-\u09db\u09de\u09e4-\u09e5\u09f2-\u0a01\u0a03-\u0a04\u0a0b-\u0a0e\u0a11-\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a-\u0a3b\u0a3d\u0a43-\u0a46\u0a49-\u0a4a\u0a4e-\u0a58\u0a5d\u0a5f-\u0a65\u0a75-\u0a80\u0a84\u0a8c\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba-\u0abb\u0ac6\u0aca\u0ace-\u0adf\u0ae1-\u0ae5\u0af0-\u0b00\u0b04\u0b0d-\u0b0e\u0b11-\u0b12\u0b29\u0b31\u0b34-\u0b35\u0b3a-\u0b3b\u0b44-\u0b46\u0b49-\u0b4a\u0b4e-\u0b55\u0b58-\u0b5b\u0b5e\u0b62-\u0b65\u0b70-\u0b81\u0b84\u0b8b-\u0b8d\u0b91\u0b96-\u0b98\u0b9b\u0b9d\u0ba0-\u0ba2\u0ba5-\u0ba7\u0bab-\u0bad\u0bb6\u0bba-\u0bbd\u0bc3-\u0bc5\u0bc9\u0bce-\u0bd6\u0bd8-\u0be6\u0bf0-\u0c00\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a-\u0c3d\u0c45\u0c49\u0c4e-\u0c54\u0c57-\u0c5f\u0c62-\u0c65\u0c70-\u0c81\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba-\u0cbd\u0cc5\u0cc9\u0cce-\u0cd4\u0cd7-\u0cdd\u0cdf\u0ce2-\u0ce5\u0cf0-\u0d01\u0d04\u0d0d\u0d11\u0d29\u0d3a-\u0d3d\u0d44-\u0d45\u0d49\u0d4e-\u0d56\u0d58-\u0d5f\u0d62-\u0d65\u0d70-\u0e00\u0e2f\u0e3b-\u0e3f\u0e4f\u0e5a-\u0e80\u0e83\u0e85-\u0e86\u0e89\u0e8b-\u0e8c\u0e8e-\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8-\u0ea9\u0eac\u0eaf\u0eba\u0ebe-\u0ebf\u0ec5\u0ec7\u0ece-\u0ecf\u0eda-\u0f17\u0f1a-\u0f1f\u0f2a-\u0f34\u0f36\u0f38\u0f3a-\u0f3d\u0f48\u0f6a-\u0f70\u0f85\u0f8c-\u0f8f\u0f96\u0f98\u0fae-\u0fb0\u0fb8\u0fba-\u109f\u10c6-\u10cf\u10f7-\u10ff\u1101\u1104\u1108\u110a\u110d\u1113-\u113b\u113d\u113f\u1141-\u114b\u114d\u114f\u1151-\u1153\u1156-\u1158\u115a-\u115e\u1162\u1164\u1166\u1168\u116a-\u116c\u116f-\u1171\u1174\u1176-\u119d\u119f-\u11a7\u11a9-\u11aa\u11ac-\u11ad\u11b0-\u11b6\u11b9\u11bb\u11c3-\u11ea\u11ec-\u11ef\u11f1-\u11f8\u11fa-\u1dff\u1e9c-\u1e9f\u1efa-\u1eff\u1f16-\u1f17\u1f1e-\u1f1f\u1f46-\u1f47\u1f4e-\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e-\u1f7f\u1fb5\u1fbd\u1fbf-\u1fc1\u1fc5\u1fcd-\u1fcf\u1fd4-\u1fd5\u1fdc-\u1fdf\u1fed-\u1ff1\u1ff5\u1ffd-\u20cf\u20dd-\u20e0\u20e2-\u2125\u2127-\u2129\u212c-\u212d\u212f-\u217f\u2183-\u3004\u3006\u3008-\u3020\u3030\u3036-\u3040\u3095-\u3098\u309b-\u309c\u309f-\u30a0\u30fb\u30ff-\u3104\u312d-\u4dff\u9fa6-\uabff\ud7a4-\uffff]') # noqa + +nonXmlNameFirstBMPRegexp = re.compile('[\x00-@\\[-\\^`\\{-\xbf\xd7\xf7\u0132-\u0133\u013f-\u0140\u0149\u017f\u01c4-\u01cc\u01f1-\u01f3\u01f6-\u01f9\u0218-\u024f\u02a9-\u02ba\u02c2-\u0385\u0387\u038b\u038d\u03a2\u03cf\u03d7-\u03d9\u03db\u03dd\u03df\u03e1\u03f4-\u0400\u040d\u0450\u045d\u0482-\u048f\u04c5-\u04c6\u04c9-\u04ca\u04cd-\u04cf\u04ec-\u04ed\u04f6-\u04f7\u04fa-\u0530\u0557-\u0558\u055a-\u0560\u0587-\u05cf\u05eb-\u05ef\u05f3-\u0620\u063b-\u0640\u064b-\u0670\u06b8-\u06b9\u06bf\u06cf\u06d4\u06d6-\u06e4\u06e7-\u0904\u093a-\u093c\u093e-\u0957\u0962-\u0984\u098d-\u098e\u0991-\u0992\u09a9\u09b1\u09b3-\u09b5\u09ba-\u09db\u09de\u09e2-\u09ef\u09f2-\u0a04\u0a0b-\u0a0e\u0a11-\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a-\u0a58\u0a5d\u0a5f-\u0a71\u0a75-\u0a84\u0a8c\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba-\u0abc\u0abe-\u0adf\u0ae1-\u0b04\u0b0d-\u0b0e\u0b11-\u0b12\u0b29\u0b31\u0b34-\u0b35\u0b3a-\u0b3c\u0b3e-\u0b5b\u0b5e\u0b62-\u0b84\u0b8b-\u0b8d\u0b91\u0b96-\u0b98\u0b9b\u0b9d\u0ba0-\u0ba2\u0ba5-\u0ba7\u0bab-\u0bad\u0bb6\u0bba-\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a-\u0c5f\u0c62-\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba-\u0cdd\u0cdf\u0ce2-\u0d04\u0d0d\u0d11\u0d29\u0d3a-\u0d5f\u0d62-\u0e00\u0e2f\u0e31\u0e34-\u0e3f\u0e46-\u0e80\u0e83\u0e85-\u0e86\u0e89\u0e8b-\u0e8c\u0e8e-\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8-\u0ea9\u0eac\u0eaf\u0eb1\u0eb4-\u0ebc\u0ebe-\u0ebf\u0ec5-\u0f3f\u0f48\u0f6a-\u109f\u10c6-\u10cf\u10f7-\u10ff\u1101\u1104\u1108\u110a\u110d\u1113-\u113b\u113d\u113f\u1141-\u114b\u114d\u114f\u1151-\u1153\u1156-\u1158\u115a-\u115e\u1162\u1164\u1166\u1168\u116a-\u116c\u116f-\u1171\u1174\u1176-\u119d\u119f-\u11a7\u11a9-\u11aa\u11ac-\u11ad\u11b0-\u11b6\u11b9\u11bb\u11c3-\u11ea\u11ec-\u11ef\u11f1-\u11f8\u11fa-\u1dff\u1e9c-\u1e9f\u1efa-\u1eff\u1f16-\u1f17\u1f1e-\u1f1f\u1f46-\u1f47\u1f4e-\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e-\u1f7f\u1fb5\u1fbd\u1fbf-\u1fc1\u1fc5\u1fcd-\u1fcf\u1fd4-\u1fd5\u1fdc-\u1fdf\u1fed-\u1ff1\u1ff5\u1ffd-\u2125\u2127-\u2129\u212c-\u212d\u212f-\u217f\u2183-\u3006\u3008-\u3020\u302a-\u3040\u3095-\u30a0\u30fb-\u3104\u312d-\u4dff\u9fa6-\uabff\ud7a4-\uffff]') # noqa + +# Simpler things +nonPubidCharRegexp = re.compile("[^\x20\x0D\x0Aa-zA-Z0-9\\-'()+,./:=?;!*#@$_%]") + + +class InfosetFilter(object): + replacementRegexp = re.compile(r"U[\dA-F]{5,5}") + + def __init__(self, + dropXmlnsLocalName=False, + dropXmlnsAttrNs=False, + preventDoubleDashComments=False, + preventDashAtCommentEnd=False, + replaceFormFeedCharacters=True, + preventSingleQuotePubid=False): + + self.dropXmlnsLocalName = dropXmlnsLocalName + self.dropXmlnsAttrNs = dropXmlnsAttrNs + + self.preventDoubleDashComments = preventDoubleDashComments + self.preventDashAtCommentEnd = preventDashAtCommentEnd + + self.replaceFormFeedCharacters = replaceFormFeedCharacters + + self.preventSingleQuotePubid = preventSingleQuotePubid + + self.replaceCache = {} + + def coerceAttribute(self, name, namespace=None): + if self.dropXmlnsLocalName and name.startswith("xmlns:"): + warnings.warn("Attributes cannot begin with xmlns", DataLossWarning) + return None + elif (self.dropXmlnsAttrNs and + namespace == "http://www.w3.org/2000/xmlns/"): + warnings.warn("Attributes cannot be in the xml namespace", DataLossWarning) + return None + else: + return self.toXmlName(name) + + def coerceElement(self, name): + return self.toXmlName(name) + + def coerceComment(self, data): + if self.preventDoubleDashComments: + while "--" in data: + warnings.warn("Comments cannot contain adjacent dashes", DataLossWarning) + data = data.replace("--", "- -") + if data.endswith("-"): + warnings.warn("Comments cannot end in a dash", DataLossWarning) + data += " " + return data + + def coerceCharacters(self, data): + if self.replaceFormFeedCharacters: + for _ in range(data.count("\x0C")): + warnings.warn("Text cannot contain U+000C", DataLossWarning) + data = data.replace("\x0C", " ") + # Other non-xml characters + return data + + def coercePubid(self, data): + dataOutput = data + for char in nonPubidCharRegexp.findall(data): + warnings.warn("Coercing non-XML pubid", DataLossWarning) + replacement = self.getReplacementCharacter(char) + dataOutput = dataOutput.replace(char, replacement) + if self.preventSingleQuotePubid and dataOutput.find("'") >= 0: + warnings.warn("Pubid cannot contain single quote", DataLossWarning) + dataOutput = dataOutput.replace("'", self.getReplacementCharacter("'")) + return dataOutput + + def toXmlName(self, name): + nameFirst = name[0] + nameRest = name[1:] + m = nonXmlNameFirstBMPRegexp.match(nameFirst) + if m: + warnings.warn("Coercing non-XML name", DataLossWarning) + nameFirstOutput = self.getReplacementCharacter(nameFirst) + else: + nameFirstOutput = nameFirst + + nameRestOutput = nameRest + replaceChars = set(nonXmlNameBMPRegexp.findall(nameRest)) + for char in replaceChars: + warnings.warn("Coercing non-XML name", DataLossWarning) + replacement = self.getReplacementCharacter(char) + nameRestOutput = nameRestOutput.replace(char, replacement) + return nameFirstOutput + nameRestOutput + + def getReplacementCharacter(self, char): + if char in self.replaceCache: + replacement = self.replaceCache[char] + else: + replacement = self.escapeChar(char) + return replacement + + def fromXmlName(self, name): + for item in set(self.replacementRegexp.findall(name)): + name = name.replace(item, self.unescapeChar(item)) + return name + + def escapeChar(self, char): + replacement = "U%05X" % ord(char) + self.replaceCache[char] = replacement + return replacement + + def unescapeChar(self, charcode): + return chr(int(charcode[1:], 16)) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_inputstream.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_inputstream.py new file mode 100644 index 0000000..a65e55f --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/_inputstream.py @@ -0,0 +1,923 @@ +from __future__ import absolute_import, division, unicode_literals + +from pip._vendor.six import text_type, binary_type +from pip._vendor.six.moves import http_client, urllib + +import codecs +import re + +from pip._vendor import webencodings + +from .constants import EOF, spaceCharacters, asciiLetters, asciiUppercase +from .constants import _ReparseException +from . import _utils + +from io import StringIO + +try: + from io import BytesIO +except ImportError: + BytesIO = StringIO + +# Non-unicode versions of constants for use in the pre-parser +spaceCharactersBytes = frozenset([item.encode("ascii") for item in spaceCharacters]) +asciiLettersBytes = frozenset([item.encode("ascii") for item in asciiLetters]) +asciiUppercaseBytes = frozenset([item.encode("ascii") for item in asciiUppercase]) +spacesAngleBrackets = spaceCharactersBytes | frozenset([b">", b"<"]) + + +invalid_unicode_no_surrogate = "[\u0001-\u0008\u000B\u000E-\u001F\u007F-\u009F\uFDD0-\uFDEF\uFFFE\uFFFF\U0001FFFE\U0001FFFF\U0002FFFE\U0002FFFF\U0003FFFE\U0003FFFF\U0004FFFE\U0004FFFF\U0005FFFE\U0005FFFF\U0006FFFE\U0006FFFF\U0007FFFE\U0007FFFF\U0008FFFE\U0008FFFF\U0009FFFE\U0009FFFF\U000AFFFE\U000AFFFF\U000BFFFE\U000BFFFF\U000CFFFE\U000CFFFF\U000DFFFE\U000DFFFF\U000EFFFE\U000EFFFF\U000FFFFE\U000FFFFF\U0010FFFE\U0010FFFF]" # noqa + +if _utils.supports_lone_surrogates: + # Use one extra step of indirection and create surrogates with + # eval. Not using this indirection would introduce an illegal + # unicode literal on platforms not supporting such lone + # surrogates. + assert invalid_unicode_no_surrogate[-1] == "]" and invalid_unicode_no_surrogate.count("]") == 1 + invalid_unicode_re = re.compile(invalid_unicode_no_surrogate[:-1] + + eval('"\\uD800-\\uDFFF"') + # pylint:disable=eval-used + "]") +else: + invalid_unicode_re = re.compile(invalid_unicode_no_surrogate) + +non_bmp_invalid_codepoints = set([0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, + 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, + 0x6FFFE, 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, + 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, + 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, + 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, + 0x10FFFE, 0x10FFFF]) + +ascii_punctuation_re = re.compile("[\u0009-\u000D\u0020-\u002F\u003A-\u0040\u005C\u005B-\u0060\u007B-\u007E]") + +# Cache for charsUntil() +charsUntilRegEx = {} + + +class BufferedStream(object): + """Buffering for streams that do not have buffering of their own + + The buffer is implemented as a list of chunks on the assumption that + joining many strings will be slow since it is O(n**2) + """ + + def __init__(self, stream): + self.stream = stream + self.buffer = [] + self.position = [-1, 0] # chunk number, offset + + def tell(self): + pos = 0 + for chunk in self.buffer[:self.position[0]]: + pos += len(chunk) + pos += self.position[1] + return pos + + def seek(self, pos): + assert pos <= self._bufferedBytes() + offset = pos + i = 0 + while len(self.buffer[i]) < offset: + offset -= len(self.buffer[i]) + i += 1 + self.position = [i, offset] + + def read(self, bytes): + if not self.buffer: + return self._readStream(bytes) + elif (self.position[0] == len(self.buffer) and + self.position[1] == len(self.buffer[-1])): + return self._readStream(bytes) + else: + return self._readFromBuffer(bytes) + + def _bufferedBytes(self): + return sum([len(item) for item in self.buffer]) + + def _readStream(self, bytes): + data = self.stream.read(bytes) + self.buffer.append(data) + self.position[0] += 1 + self.position[1] = len(data) + return data + + def _readFromBuffer(self, bytes): + remainingBytes = bytes + rv = [] + bufferIndex = self.position[0] + bufferOffset = self.position[1] + while bufferIndex < len(self.buffer) and remainingBytes != 0: + assert remainingBytes > 0 + bufferedData = self.buffer[bufferIndex] + + if remainingBytes <= len(bufferedData) - bufferOffset: + bytesToRead = remainingBytes + self.position = [bufferIndex, bufferOffset + bytesToRead] + else: + bytesToRead = len(bufferedData) - bufferOffset + self.position = [bufferIndex, len(bufferedData)] + bufferIndex += 1 + rv.append(bufferedData[bufferOffset:bufferOffset + bytesToRead]) + remainingBytes -= bytesToRead + + bufferOffset = 0 + + if remainingBytes: + rv.append(self._readStream(remainingBytes)) + + return b"".join(rv) + + +def HTMLInputStream(source, **kwargs): + # Work around Python bug #20007: read(0) closes the connection. + # http://bugs.python.org/issue20007 + if (isinstance(source, http_client.HTTPResponse) or + # Also check for addinfourl wrapping HTTPResponse + (isinstance(source, urllib.response.addbase) and + isinstance(source.fp, http_client.HTTPResponse))): + isUnicode = False + elif hasattr(source, "read"): + isUnicode = isinstance(source.read(0), text_type) + else: + isUnicode = isinstance(source, text_type) + + if isUnicode: + encodings = [x for x in kwargs if x.endswith("_encoding")] + if encodings: + raise TypeError("Cannot set an encoding with a unicode input, set %r" % encodings) + + return HTMLUnicodeInputStream(source, **kwargs) + else: + return HTMLBinaryInputStream(source, **kwargs) + + +class HTMLUnicodeInputStream(object): + """Provides a unicode stream of characters to the HTMLTokenizer. + + This class takes care of character encoding and removing or replacing + incorrect byte-sequences and also provides column and line tracking. + + """ + + _defaultChunkSize = 10240 + + def __init__(self, source): + """Initialises the HTMLInputStream. + + HTMLInputStream(source, [encoding]) -> Normalized stream from source + for use by html5lib. + + source can be either a file-object, local filename or a string. + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + """ + + if not _utils.supports_lone_surrogates: + # Such platforms will have already checked for such + # surrogate errors, so no need to do this checking. + self.reportCharacterErrors = None + elif len("\U0010FFFF") == 1: + self.reportCharacterErrors = self.characterErrorsUCS4 + else: + self.reportCharacterErrors = self.characterErrorsUCS2 + + # List of where new lines occur + self.newLines = [0] + + self.charEncoding = (lookupEncoding("utf-8"), "certain") + self.dataStream = self.openStream(source) + + self.reset() + + def reset(self): + self.chunk = "" + self.chunkSize = 0 + self.chunkOffset = 0 + self.errors = [] + + # number of (complete) lines in previous chunks + self.prevNumLines = 0 + # number of columns in the last line of the previous chunk + self.prevNumCols = 0 + + # Deal with CR LF and surrogates split over chunk boundaries + self._bufferedCharacter = None + + def openStream(self, source): + """Produces a file object from source. + + source can be either a file object, local filename or a string. + + """ + # Already a file object + if hasattr(source, 'read'): + stream = source + else: + stream = StringIO(source) + + return stream + + def _position(self, offset): + chunk = self.chunk + nLines = chunk.count('\n', 0, offset) + positionLine = self.prevNumLines + nLines + lastLinePos = chunk.rfind('\n', 0, offset) + if lastLinePos == -1: + positionColumn = self.prevNumCols + offset + else: + positionColumn = offset - (lastLinePos + 1) + return (positionLine, positionColumn) + + def position(self): + """Returns (line, col) of the current position in the stream.""" + line, col = self._position(self.chunkOffset) + return (line + 1, col) + + def char(self): + """ Read one character from the stream or queue if available. Return + EOF when EOF is reached. + """ + # Read a new chunk from the input stream if necessary + if self.chunkOffset >= self.chunkSize: + if not self.readChunk(): + return EOF + + chunkOffset = self.chunkOffset + char = self.chunk[chunkOffset] + self.chunkOffset = chunkOffset + 1 + + return char + + def readChunk(self, chunkSize=None): + if chunkSize is None: + chunkSize = self._defaultChunkSize + + self.prevNumLines, self.prevNumCols = self._position(self.chunkSize) + + self.chunk = "" + self.chunkSize = 0 + self.chunkOffset = 0 + + data = self.dataStream.read(chunkSize) + + # Deal with CR LF and surrogates broken across chunks + if self._bufferedCharacter: + data = self._bufferedCharacter + data + self._bufferedCharacter = None + elif not data: + # We have no more data, bye-bye stream + return False + + if len(data) > 1: + lastv = ord(data[-1]) + if lastv == 0x0D or 0xD800 <= lastv <= 0xDBFF: + self._bufferedCharacter = data[-1] + data = data[:-1] + + if self.reportCharacterErrors: + self.reportCharacterErrors(data) + + # Replace invalid characters + data = data.replace("\r\n", "\n") + data = data.replace("\r", "\n") + + self.chunk = data + self.chunkSize = len(data) + + return True + + def characterErrorsUCS4(self, data): + for _ in range(len(invalid_unicode_re.findall(data))): + self.errors.append("invalid-codepoint") + + def characterErrorsUCS2(self, data): + # Someone picked the wrong compile option + # You lose + skip = False + for match in invalid_unicode_re.finditer(data): + if skip: + continue + codepoint = ord(match.group()) + pos = match.start() + # Pretty sure there should be endianness issues here + if _utils.isSurrogatePair(data[pos:pos + 2]): + # We have a surrogate pair! + char_val = _utils.surrogatePairToCodepoint(data[pos:pos + 2]) + if char_val in non_bmp_invalid_codepoints: + self.errors.append("invalid-codepoint") + skip = True + elif (codepoint >= 0xD800 and codepoint <= 0xDFFF and + pos == len(data) - 1): + self.errors.append("invalid-codepoint") + else: + skip = False + self.errors.append("invalid-codepoint") + + def charsUntil(self, characters, opposite=False): + """ Returns a string of characters from the stream up to but not + including any character in 'characters' or EOF. 'characters' must be + a container that supports the 'in' method and iteration over its + characters. + """ + + # Use a cache of regexps to find the required characters + try: + chars = charsUntilRegEx[(characters, opposite)] + except KeyError: + if __debug__: + for c in characters: + assert(ord(c) < 128) + regex = "".join(["\\x%02x" % ord(c) for c in characters]) + if not opposite: + regex = "^%s" % regex + chars = charsUntilRegEx[(characters, opposite)] = re.compile("[%s]+" % regex) + + rv = [] + + while True: + # Find the longest matching prefix + m = chars.match(self.chunk, self.chunkOffset) + if m is None: + # If nothing matched, and it wasn't because we ran out of chunk, + # then stop + if self.chunkOffset != self.chunkSize: + break + else: + end = m.end() + # If not the whole chunk matched, return everything + # up to the part that didn't match + if end != self.chunkSize: + rv.append(self.chunk[self.chunkOffset:end]) + self.chunkOffset = end + break + # If the whole remainder of the chunk matched, + # use it all and read the next chunk + rv.append(self.chunk[self.chunkOffset:]) + if not self.readChunk(): + # Reached EOF + break + + r = "".join(rv) + return r + + def unget(self, char): + # Only one character is allowed to be ungotten at once - it must + # be consumed again before any further call to unget + if char is not None: + if self.chunkOffset == 0: + # unget is called quite rarely, so it's a good idea to do + # more work here if it saves a bit of work in the frequently + # called char and charsUntil. + # So, just prepend the ungotten character onto the current + # chunk: + self.chunk = char + self.chunk + self.chunkSize += 1 + else: + self.chunkOffset -= 1 + assert self.chunk[self.chunkOffset] == char + + +class HTMLBinaryInputStream(HTMLUnicodeInputStream): + """Provides a unicode stream of characters to the HTMLTokenizer. + + This class takes care of character encoding and removing or replacing + incorrect byte-sequences and also provides column and line tracking. + + """ + + def __init__(self, source, override_encoding=None, transport_encoding=None, + same_origin_parent_encoding=None, likely_encoding=None, + default_encoding="windows-1252", useChardet=True): + """Initialises the HTMLInputStream. + + HTMLInputStream(source, [encoding]) -> Normalized stream from source + for use by html5lib. + + source can be either a file-object, local filename or a string. + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + """ + # Raw Stream - for unicode objects this will encode to utf-8 and set + # self.charEncoding as appropriate + self.rawStream = self.openStream(source) + + HTMLUnicodeInputStream.__init__(self, self.rawStream) + + # Encoding Information + # Number of bytes to use when looking for a meta element with + # encoding information + self.numBytesMeta = 1024 + # Number of bytes to use when using detecting encoding using chardet + self.numBytesChardet = 100 + # Things from args + self.override_encoding = override_encoding + self.transport_encoding = transport_encoding + self.same_origin_parent_encoding = same_origin_parent_encoding + self.likely_encoding = likely_encoding + self.default_encoding = default_encoding + + # Determine encoding + self.charEncoding = self.determineEncoding(useChardet) + assert self.charEncoding[0] is not None + + # Call superclass + self.reset() + + def reset(self): + self.dataStream = self.charEncoding[0].codec_info.streamreader(self.rawStream, 'replace') + HTMLUnicodeInputStream.reset(self) + + def openStream(self, source): + """Produces a file object from source. + + source can be either a file object, local filename or a string. + + """ + # Already a file object + if hasattr(source, 'read'): + stream = source + else: + stream = BytesIO(source) + + try: + stream.seek(stream.tell()) + except: # pylint:disable=bare-except + stream = BufferedStream(stream) + + return stream + + def determineEncoding(self, chardet=True): + # BOMs take precedence over everything + # This will also read past the BOM if present + charEncoding = self.detectBOM(), "certain" + if charEncoding[0] is not None: + return charEncoding + + # If we've been overriden, we've been overriden + charEncoding = lookupEncoding(self.override_encoding), "certain" + if charEncoding[0] is not None: + return charEncoding + + # Now check the transport layer + charEncoding = lookupEncoding(self.transport_encoding), "certain" + if charEncoding[0] is not None: + return charEncoding + + # Look for meta elements with encoding information + charEncoding = self.detectEncodingMeta(), "tentative" + if charEncoding[0] is not None: + return charEncoding + + # Parent document encoding + charEncoding = lookupEncoding(self.same_origin_parent_encoding), "tentative" + if charEncoding[0] is not None and not charEncoding[0].name.startswith("utf-16"): + return charEncoding + + # "likely" encoding + charEncoding = lookupEncoding(self.likely_encoding), "tentative" + if charEncoding[0] is not None: + return charEncoding + + # Guess with chardet, if available + if chardet: + try: + from pip._vendor.chardet.universaldetector import UniversalDetector + except ImportError: + pass + else: + buffers = [] + detector = UniversalDetector() + while not detector.done: + buffer = self.rawStream.read(self.numBytesChardet) + assert isinstance(buffer, bytes) + if not buffer: + break + buffers.append(buffer) + detector.feed(buffer) + detector.close() + encoding = lookupEncoding(detector.result['encoding']) + self.rawStream.seek(0) + if encoding is not None: + return encoding, "tentative" + + # Try the default encoding + charEncoding = lookupEncoding(self.default_encoding), "tentative" + if charEncoding[0] is not None: + return charEncoding + + # Fallback to html5lib's default if even that hasn't worked + return lookupEncoding("windows-1252"), "tentative" + + def changeEncoding(self, newEncoding): + assert self.charEncoding[1] != "certain" + newEncoding = lookupEncoding(newEncoding) + if newEncoding is None: + return + if newEncoding.name in ("utf-16be", "utf-16le"): + newEncoding = lookupEncoding("utf-8") + assert newEncoding is not None + elif newEncoding == self.charEncoding[0]: + self.charEncoding = (self.charEncoding[0], "certain") + else: + self.rawStream.seek(0) + self.charEncoding = (newEncoding, "certain") + self.reset() + raise _ReparseException("Encoding changed from %s to %s" % (self.charEncoding[0], newEncoding)) + + def detectBOM(self): + """Attempts to detect at BOM at the start of the stream. If + an encoding can be determined from the BOM return the name of the + encoding otherwise return None""" + bomDict = { + codecs.BOM_UTF8: 'utf-8', + codecs.BOM_UTF16_LE: 'utf-16le', codecs.BOM_UTF16_BE: 'utf-16be', + codecs.BOM_UTF32_LE: 'utf-32le', codecs.BOM_UTF32_BE: 'utf-32be' + } + + # Go to beginning of file and read in 4 bytes + string = self.rawStream.read(4) + assert isinstance(string, bytes) + + # Try detecting the BOM using bytes from the string + encoding = bomDict.get(string[:3]) # UTF-8 + seek = 3 + if not encoding: + # Need to detect UTF-32 before UTF-16 + encoding = bomDict.get(string) # UTF-32 + seek = 4 + if not encoding: + encoding = bomDict.get(string[:2]) # UTF-16 + seek = 2 + + # Set the read position past the BOM if one was found, otherwise + # set it to the start of the stream + if encoding: + self.rawStream.seek(seek) + return lookupEncoding(encoding) + else: + self.rawStream.seek(0) + return None + + def detectEncodingMeta(self): + """Report the encoding declared by the meta element + """ + buffer = self.rawStream.read(self.numBytesMeta) + assert isinstance(buffer, bytes) + parser = EncodingParser(buffer) + self.rawStream.seek(0) + encoding = parser.getEncoding() + + if encoding is not None and encoding.name in ("utf-16be", "utf-16le"): + encoding = lookupEncoding("utf-8") + + return encoding + + +class EncodingBytes(bytes): + """String-like object with an associated position and various extra methods + If the position is ever greater than the string length then an exception is + raised""" + def __new__(self, value): + assert isinstance(value, bytes) + return bytes.__new__(self, value.lower()) + + def __init__(self, value): + # pylint:disable=unused-argument + self._position = -1 + + def __iter__(self): + return self + + def __next__(self): + p = self._position = self._position + 1 + if p >= len(self): + raise StopIteration + elif p < 0: + raise TypeError + return self[p:p + 1] + + def next(self): + # Py2 compat + return self.__next__() + + def previous(self): + p = self._position + if p >= len(self): + raise StopIteration + elif p < 0: + raise TypeError + self._position = p = p - 1 + return self[p:p + 1] + + def setPosition(self, position): + if self._position >= len(self): + raise StopIteration + self._position = position + + def getPosition(self): + if self._position >= len(self): + raise StopIteration + if self._position >= 0: + return self._position + else: + return None + + position = property(getPosition, setPosition) + + def getCurrentByte(self): + return self[self.position:self.position + 1] + + currentByte = property(getCurrentByte) + + def skip(self, chars=spaceCharactersBytes): + """Skip past a list of characters""" + p = self.position # use property for the error-checking + while p < len(self): + c = self[p:p + 1] + if c not in chars: + self._position = p + return c + p += 1 + self._position = p + return None + + def skipUntil(self, chars): + p = self.position + while p < len(self): + c = self[p:p + 1] + if c in chars: + self._position = p + return c + p += 1 + self._position = p + return None + + def matchBytes(self, bytes): + """Look for a sequence of bytes at the start of a string. If the bytes + are found return True and advance the position to the byte after the + match. Otherwise return False and leave the position alone""" + p = self.position + data = self[p:p + len(bytes)] + rv = data.startswith(bytes) + if rv: + self.position += len(bytes) + return rv + + def jumpTo(self, bytes): + """Look for the next sequence of bytes matching a given sequence. If + a match is found advance the position to the last byte of the match""" + newPosition = self[self.position:].find(bytes) + if newPosition > -1: + # XXX: This is ugly, but I can't see a nicer way to fix this. + if self._position == -1: + self._position = 0 + self._position += (newPosition + len(bytes) - 1) + return True + else: + raise StopIteration + + +class EncodingParser(object): + """Mini parser for detecting character encoding from meta elements""" + + def __init__(self, data): + """string - the data to work on for encoding detection""" + self.data = EncodingBytes(data) + self.encoding = None + + def getEncoding(self): + methodDispatch = ( + (b"<!--", self.handleComment), + (b"<meta", self.handleMeta), + (b"</", self.handlePossibleEndTag), + (b"<!", self.handleOther), + (b"<?", self.handleOther), + (b"<", self.handlePossibleStartTag)) + for _ in self.data: + keepParsing = True + for key, method in methodDispatch: + if self.data.matchBytes(key): + try: + keepParsing = method() + break + except StopIteration: + keepParsing = False + break + if not keepParsing: + break + + return self.encoding + + def handleComment(self): + """Skip over comments""" + return self.data.jumpTo(b"-->") + + def handleMeta(self): + if self.data.currentByte not in spaceCharactersBytes: + # if we have <meta not followed by a space so just keep going + return True + # We have a valid meta element we want to search for attributes + hasPragma = False + pendingEncoding = None + while True: + # Try to find the next attribute after the current position + attr = self.getAttribute() + if attr is None: + return True + else: + if attr[0] == b"http-equiv": + hasPragma = attr[1] == b"content-type" + if hasPragma and pendingEncoding is not None: + self.encoding = pendingEncoding + return False + elif attr[0] == b"charset": + tentativeEncoding = attr[1] + codec = lookupEncoding(tentativeEncoding) + if codec is not None: + self.encoding = codec + return False + elif attr[0] == b"content": + contentParser = ContentAttrParser(EncodingBytes(attr[1])) + tentativeEncoding = contentParser.parse() + if tentativeEncoding is not None: + codec = lookupEncoding(tentativeEncoding) + if codec is not None: + if hasPragma: + self.encoding = codec + return False + else: + pendingEncoding = codec + + def handlePossibleStartTag(self): + return self.handlePossibleTag(False) + + def handlePossibleEndTag(self): + next(self.data) + return self.handlePossibleTag(True) + + def handlePossibleTag(self, endTag): + data = self.data + if data.currentByte not in asciiLettersBytes: + # If the next byte is not an ascii letter either ignore this + # fragment (possible start tag case) or treat it according to + # handleOther + if endTag: + data.previous() + self.handleOther() + return True + + c = data.skipUntil(spacesAngleBrackets) + if c == b"<": + # return to the first step in the overall "two step" algorithm + # reprocessing the < byte + data.previous() + else: + # Read all attributes + attr = self.getAttribute() + while attr is not None: + attr = self.getAttribute() + return True + + def handleOther(self): + return self.data.jumpTo(b">") + + def getAttribute(self): + """Return a name,value pair for the next attribute in the stream, + if one is found, or None""" + data = self.data + # Step 1 (skip chars) + c = data.skip(spaceCharactersBytes | frozenset([b"/"])) + assert c is None or len(c) == 1 + # Step 2 + if c in (b">", None): + return None + # Step 3 + attrName = [] + attrValue = [] + # Step 4 attribute name + while True: + if c == b"=" and attrName: + break + elif c in spaceCharactersBytes: + # Step 6! + c = data.skip() + break + elif c in (b"/", b">"): + return b"".join(attrName), b"" + elif c in asciiUppercaseBytes: + attrName.append(c.lower()) + elif c is None: + return None + else: + attrName.append(c) + # Step 5 + c = next(data) + # Step 7 + if c != b"=": + data.previous() + return b"".join(attrName), b"" + # Step 8 + next(data) + # Step 9 + c = data.skip() + # Step 10 + if c in (b"'", b'"'): + # 10.1 + quoteChar = c + while True: + # 10.2 + c = next(data) + # 10.3 + if c == quoteChar: + next(data) + return b"".join(attrName), b"".join(attrValue) + # 10.4 + elif c in asciiUppercaseBytes: + attrValue.append(c.lower()) + # 10.5 + else: + attrValue.append(c) + elif c == b">": + return b"".join(attrName), b"" + elif c in asciiUppercaseBytes: + attrValue.append(c.lower()) + elif c is None: + return None + else: + attrValue.append(c) + # Step 11 + while True: + c = next(data) + if c in spacesAngleBrackets: + return b"".join(attrName), b"".join(attrValue) + elif c in asciiUppercaseBytes: + attrValue.append(c.lower()) + elif c is None: + return None + else: + attrValue.append(c) + + +class ContentAttrParser(object): + def __init__(self, data): + assert isinstance(data, bytes) + self.data = data + + def parse(self): + try: + # Check if the attr name is charset + # otherwise return + self.data.jumpTo(b"charset") + self.data.position += 1 + self.data.skip() + if not self.data.currentByte == b"=": + # If there is no = sign keep looking for attrs + return None + self.data.position += 1 + self.data.skip() + # Look for an encoding between matching quote marks + if self.data.currentByte in (b'"', b"'"): + quoteMark = self.data.currentByte + self.data.position += 1 + oldPosition = self.data.position + if self.data.jumpTo(quoteMark): + return self.data[oldPosition:self.data.position] + else: + return None + else: + # Unquoted value + oldPosition = self.data.position + try: + self.data.skipUntil(spaceCharactersBytes) + return self.data[oldPosition:self.data.position] + except StopIteration: + # Return the whole remaining value + return self.data[oldPosition:] + except StopIteration: + return None + + +def lookupEncoding(encoding): + """Return the python codec name corresponding to an encoding or None if the + string doesn't correspond to a valid encoding.""" + if isinstance(encoding, binary_type): + try: + encoding = encoding.decode("ascii") + except UnicodeDecodeError: + return None + + if encoding is not None: + try: + return webencodings.lookup(encoding) + except AttributeError: + return None + else: + return None diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_tokenizer.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_tokenizer.py new file mode 100644 index 0000000..178f6e7 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/_tokenizer.py @@ -0,0 +1,1721 @@ +from __future__ import absolute_import, division, unicode_literals + +from pip._vendor.six import unichr as chr + +from collections import deque + +from .constants import spaceCharacters +from .constants import entities +from .constants import asciiLetters, asciiUpper2Lower +from .constants import digits, hexDigits, EOF +from .constants import tokenTypes, tagTokenTypes +from .constants import replacementCharacters + +from ._inputstream import HTMLInputStream + +from ._trie import Trie + +entitiesTrie = Trie(entities) + + +class HTMLTokenizer(object): + """ This class takes care of tokenizing HTML. + + * self.currentToken + Holds the token that is currently being processed. + + * self.state + Holds a reference to the method to be invoked... XXX + + * self.stream + Points to HTMLInputStream object. + """ + + def __init__(self, stream, parser=None, **kwargs): + + self.stream = HTMLInputStream(stream, **kwargs) + self.parser = parser + + # Setup the initial tokenizer state + self.escapeFlag = False + self.lastFourChars = [] + self.state = self.dataState + self.escape = False + + # The current token being created + self.currentToken = None + super(HTMLTokenizer, self).__init__() + + def __iter__(self): + """ This is where the magic happens. + + We do our usually processing through the states and when we have a token + to return we yield the token which pauses processing until the next token + is requested. + """ + self.tokenQueue = deque([]) + # Start processing. When EOF is reached self.state will return False + # instead of True and the loop will terminate. + while self.state(): + while self.stream.errors: + yield {"type": tokenTypes["ParseError"], "data": self.stream.errors.pop(0)} + while self.tokenQueue: + yield self.tokenQueue.popleft() + + def consumeNumberEntity(self, isHex): + """This function returns either U+FFFD or the character based on the + decimal or hexadecimal representation. It also discards ";" if present. + If not present self.tokenQueue.append({"type": tokenTypes["ParseError"]}) is invoked. + """ + + allowed = digits + radix = 10 + if isHex: + allowed = hexDigits + radix = 16 + + charStack = [] + + # Consume all the characters that are in range while making sure we + # don't hit an EOF. + c = self.stream.char() + while c in allowed and c is not EOF: + charStack.append(c) + c = self.stream.char() + + # Convert the set of characters consumed to an int. + charAsInt = int("".join(charStack), radix) + + # Certain characters get replaced with others + if charAsInt in replacementCharacters: + char = replacementCharacters[charAsInt] + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "illegal-codepoint-for-numeric-entity", + "datavars": {"charAsInt": charAsInt}}) + elif ((0xD800 <= charAsInt <= 0xDFFF) or + (charAsInt > 0x10FFFF)): + char = "\uFFFD" + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "illegal-codepoint-for-numeric-entity", + "datavars": {"charAsInt": charAsInt}}) + else: + # Should speed up this check somehow (e.g. move the set to a constant) + if ((0x0001 <= charAsInt <= 0x0008) or + (0x000E <= charAsInt <= 0x001F) or + (0x007F <= charAsInt <= 0x009F) or + (0xFDD0 <= charAsInt <= 0xFDEF) or + charAsInt in frozenset([0x000B, 0xFFFE, 0xFFFF, 0x1FFFE, + 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, + 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, + 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE, + 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, + 0x9FFFF, 0xAFFFE, 0xAFFFF, 0xBFFFE, + 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, + 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, + 0xFFFFF, 0x10FFFE, 0x10FFFF])): + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": + "illegal-codepoint-for-numeric-entity", + "datavars": {"charAsInt": charAsInt}}) + try: + # Try/except needed as UCS-2 Python builds' unichar only works + # within the BMP. + char = chr(charAsInt) + except ValueError: + v = charAsInt - 0x10000 + char = chr(0xD800 | (v >> 10)) + chr(0xDC00 | (v & 0x3FF)) + + # Discard the ; if present. Otherwise, put it back on the queue and + # invoke parseError on parser. + if c != ";": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "numeric-entity-without-semicolon"}) + self.stream.unget(c) + + return char + + def consumeEntity(self, allowedChar=None, fromAttribute=False): + # Initialise to the default output for when no entity is matched + output = "&" + + charStack = [self.stream.char()] + if (charStack[0] in spaceCharacters or charStack[0] in (EOF, "<", "&") or + (allowedChar is not None and allowedChar == charStack[0])): + self.stream.unget(charStack[0]) + + elif charStack[0] == "#": + # Read the next character to see if it's hex or decimal + hex = False + charStack.append(self.stream.char()) + if charStack[-1] in ("x", "X"): + hex = True + charStack.append(self.stream.char()) + + # charStack[-1] should be the first digit + if (hex and charStack[-1] in hexDigits) \ + or (not hex and charStack[-1] in digits): + # At least one digit found, so consume the whole number + self.stream.unget(charStack[-1]) + output = self.consumeNumberEntity(hex) + else: + # No digits found + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "expected-numeric-entity"}) + self.stream.unget(charStack.pop()) + output = "&" + "".join(charStack) + + else: + # At this point in the process might have named entity. Entities + # are stored in the global variable "entities". + # + # Consume characters and compare to these to a substring of the + # entity names in the list until the substring no longer matches. + while (charStack[-1] is not EOF): + if not entitiesTrie.has_keys_with_prefix("".join(charStack)): + break + charStack.append(self.stream.char()) + + # At this point we have a string that starts with some characters + # that may match an entity + # Try to find the longest entity the string will match to take care + # of ¬i for instance. + try: + entityName = entitiesTrie.longest_prefix("".join(charStack[:-1])) + entityLength = len(entityName) + except KeyError: + entityName = None + + if entityName is not None: + if entityName[-1] != ";": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "named-entity-without-semicolon"}) + if (entityName[-1] != ";" and fromAttribute and + (charStack[entityLength] in asciiLetters or + charStack[entityLength] in digits or + charStack[entityLength] == "=")): + self.stream.unget(charStack.pop()) + output = "&" + "".join(charStack) + else: + output = entities[entityName] + self.stream.unget(charStack.pop()) + output += "".join(charStack[entityLength:]) + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-named-entity"}) + self.stream.unget(charStack.pop()) + output = "&" + "".join(charStack) + + if fromAttribute: + self.currentToken["data"][-1][1] += output + else: + if output in spaceCharacters: + tokenType = "SpaceCharacters" + else: + tokenType = "Characters" + self.tokenQueue.append({"type": tokenTypes[tokenType], "data": output}) + + def processEntityInAttribute(self, allowedChar): + """This method replaces the need for "entityInAttributeValueState". + """ + self.consumeEntity(allowedChar=allowedChar, fromAttribute=True) + + def emitCurrentToken(self): + """This method is a generic handler for emitting the tags. It also sets + the state to "data" because that's what's needed after a token has been + emitted. + """ + token = self.currentToken + # Add token to the queue to be yielded + if (token["type"] in tagTokenTypes): + token["name"] = token["name"].translate(asciiUpper2Lower) + if token["type"] == tokenTypes["EndTag"]: + if token["data"]: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "attributes-in-end-tag"}) + if token["selfClosing"]: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "self-closing-flag-on-end-tag"}) + self.tokenQueue.append(token) + self.state = self.dataState + + # Below are the various tokenizer states worked out. + def dataState(self): + data = self.stream.char() + if data == "&": + self.state = self.entityDataState + elif data == "<": + self.state = self.tagOpenState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\u0000"}) + elif data is EOF: + # Tokenization ends. + return False + elif data in spaceCharacters: + # Directly after emitting a token you switch back to the "data + # state". At that point spaceCharacters are important so they are + # emitted separately. + self.tokenQueue.append({"type": tokenTypes["SpaceCharacters"], "data": + data + self.stream.charsUntil(spaceCharacters, True)}) + # No need to update lastFourChars here, since the first space will + # have already been appended to lastFourChars and will have broken + # any <!-- or --> sequences + else: + chars = self.stream.charsUntil(("&", "<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def entityDataState(self): + self.consumeEntity() + self.state = self.dataState + return True + + def rcdataState(self): + data = self.stream.char() + if data == "&": + self.state = self.characterReferenceInRcdata + elif data == "<": + self.state = self.rcdataLessThanSignState + elif data == EOF: + # Tokenization ends. + return False + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data in spaceCharacters: + # Directly after emitting a token you switch back to the "data + # state". At that point spaceCharacters are important so they are + # emitted separately. + self.tokenQueue.append({"type": tokenTypes["SpaceCharacters"], "data": + data + self.stream.charsUntil(spaceCharacters, True)}) + # No need to update lastFourChars here, since the first space will + # have already been appended to lastFourChars and will have broken + # any <!-- or --> sequences + else: + chars = self.stream.charsUntil(("&", "<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def characterReferenceInRcdata(self): + self.consumeEntity() + self.state = self.rcdataState + return True + + def rawtextState(self): + data = self.stream.char() + if data == "<": + self.state = self.rawtextLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + # Tokenization ends. + return False + else: + chars = self.stream.charsUntil(("<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def scriptDataState(self): + data = self.stream.char() + if data == "<": + self.state = self.scriptDataLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + # Tokenization ends. + return False + else: + chars = self.stream.charsUntil(("<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def plaintextState(self): + data = self.stream.char() + if data == EOF: + # Tokenization ends. + return False + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + self.stream.charsUntil("\u0000")}) + return True + + def tagOpenState(self): + data = self.stream.char() + if data == "!": + self.state = self.markupDeclarationOpenState + elif data == "/": + self.state = self.closeTagOpenState + elif data in asciiLetters: + self.currentToken = {"type": tokenTypes["StartTag"], + "name": data, "data": [], + "selfClosing": False, + "selfClosingAcknowledged": False} + self.state = self.tagNameState + elif data == ">": + # XXX In theory it could be something besides a tag name. But + # do we really care? + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-tag-name-but-got-right-bracket"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<>"}) + self.state = self.dataState + elif data == "?": + # XXX In theory it could be something besides a tag name. But + # do we really care? + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-tag-name-but-got-question-mark"}) + self.stream.unget(data) + self.state = self.bogusCommentState + else: + # XXX + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-tag-name"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.dataState + return True + + def closeTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.currentToken = {"type": tokenTypes["EndTag"], "name": data, + "data": [], "selfClosing": False} + self.state = self.tagNameState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-closing-tag-but-got-right-bracket"}) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-closing-tag-but-got-eof"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.state = self.dataState + else: + # XXX data can be _'_... + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-closing-tag-but-got-char", + "datavars": {"data": data}}) + self.stream.unget(data) + self.state = self.bogusCommentState + return True + + def tagNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeAttributeNameState + elif data == ">": + self.emitCurrentToken() + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-tag-name"}) + self.state = self.dataState + elif data == "/": + self.state = self.selfClosingStartTagState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["name"] += "\uFFFD" + else: + self.currentToken["name"] += data + # (Don't use charsUntil here, because tag names are + # very short and it's faster to not do anything fancy) + return True + + def rcdataLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.rcdataEndTagOpenState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.rcdataState + return True + + def rcdataEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer += data + self.state = self.rcdataEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.stream.unget(data) + self.state = self.rcdataState + return True + + def rcdataEndTagNameState(self): + appropriate = self.currentToken and self.currentToken["name"].lower() == self.temporaryBuffer.lower() + data = self.stream.char() + if data in spaceCharacters and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.beforeAttributeNameState + elif data == "/" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.selfClosingStartTagState + elif data == ">" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "</" + self.temporaryBuffer}) + self.stream.unget(data) + self.state = self.rcdataState + return True + + def rawtextLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.rawtextEndTagOpenState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.rawtextState + return True + + def rawtextEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer += data + self.state = self.rawtextEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.stream.unget(data) + self.state = self.rawtextState + return True + + def rawtextEndTagNameState(self): + appropriate = self.currentToken and self.currentToken["name"].lower() == self.temporaryBuffer.lower() + data = self.stream.char() + if data in spaceCharacters and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.beforeAttributeNameState + elif data == "/" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.selfClosingStartTagState + elif data == ">" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "</" + self.temporaryBuffer}) + self.stream.unget(data) + self.state = self.rawtextState + return True + + def scriptDataLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.scriptDataEndTagOpenState + elif data == "!": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<!"}) + self.state = self.scriptDataEscapeStartState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer += data + self.state = self.scriptDataEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEndTagNameState(self): + appropriate = self.currentToken and self.currentToken["name"].lower() == self.temporaryBuffer.lower() + data = self.stream.char() + if data in spaceCharacters and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.beforeAttributeNameState + elif data == "/" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.selfClosingStartTagState + elif data == ">" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "</" + self.temporaryBuffer}) + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEscapeStartState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataEscapeStartDashState + else: + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEscapeStartDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataEscapedDashDashState + else: + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEscapedState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataEscapedDashState + elif data == "<": + self.state = self.scriptDataEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + self.state = self.dataState + else: + chars = self.stream.charsUntil(("<", "-", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def scriptDataEscapedDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataEscapedDashDashState + elif data == "<": + self.state = self.scriptDataEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataEscapedState + elif data == EOF: + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedDashDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + elif data == "<": + self.state = self.scriptDataEscapedLessThanSignState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": ">"}) + self.state = self.scriptDataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataEscapedState + elif data == EOF: + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.scriptDataEscapedEndTagOpenState + elif data in asciiLetters: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<" + data}) + self.temporaryBuffer = data + self.state = self.scriptDataDoubleEscapeStartState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer = data + self.state = self.scriptDataEscapedEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedEndTagNameState(self): + appropriate = self.currentToken and self.currentToken["name"].lower() == self.temporaryBuffer.lower() + data = self.stream.char() + if data in spaceCharacters and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.beforeAttributeNameState + elif data == "/" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.selfClosingStartTagState + elif data == ">" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "</" + self.temporaryBuffer}) + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataDoubleEscapeStartState(self): + data = self.stream.char() + if data in (spaceCharacters | frozenset(("/", ">"))): + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + if self.temporaryBuffer.lower() == "script": + self.state = self.scriptDataDoubleEscapedState + else: + self.state = self.scriptDataEscapedState + elif data in asciiLetters: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.temporaryBuffer += data + else: + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataDoubleEscapedState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataDoubleEscapedDashState + elif data == "<": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.state = self.scriptDataDoubleEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-script-in-script"}) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + return True + + def scriptDataDoubleEscapedDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataDoubleEscapedDashDashState + elif data == "<": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.state = self.scriptDataDoubleEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataDoubleEscapedState + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-script-in-script"}) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataDoubleEscapedState + return True + + def scriptDataDoubleEscapedDashDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + elif data == "<": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.state = self.scriptDataDoubleEscapedLessThanSignState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": ">"}) + self.state = self.scriptDataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataDoubleEscapedState + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-script-in-script"}) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataDoubleEscapedState + return True + + def scriptDataDoubleEscapedLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "/"}) + self.temporaryBuffer = "" + self.state = self.scriptDataDoubleEscapeEndState + else: + self.stream.unget(data) + self.state = self.scriptDataDoubleEscapedState + return True + + def scriptDataDoubleEscapeEndState(self): + data = self.stream.char() + if data in (spaceCharacters | frozenset(("/", ">"))): + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + if self.temporaryBuffer.lower() == "script": + self.state = self.scriptDataEscapedState + else: + self.state = self.scriptDataDoubleEscapedState + elif data in asciiLetters: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.temporaryBuffer += data + else: + self.stream.unget(data) + self.state = self.scriptDataDoubleEscapedState + return True + + def beforeAttributeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.stream.charsUntil(spaceCharacters, True) + elif data in asciiLetters: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data == ">": + self.emitCurrentToken() + elif data == "/": + self.state = self.selfClosingStartTagState + elif data in ("'", '"', "=", "<"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "invalid-character-in-attribute-name"}) + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"].append(["\uFFFD", ""]) + self.state = self.attributeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-attribute-name-but-got-eof"}) + self.state = self.dataState + else: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + return True + + def attributeNameState(self): + data = self.stream.char() + leavingThisState = True + emitToken = False + if data == "=": + self.state = self.beforeAttributeValueState + elif data in asciiLetters: + self.currentToken["data"][-1][0] += data +\ + self.stream.charsUntil(asciiLetters, True) + leavingThisState = False + elif data == ">": + # XXX If we emit here the attributes are converted to a dict + # without being checked and when the code below runs we error + # because data is a dict not a list + emitToken = True + elif data in spaceCharacters: + self.state = self.afterAttributeNameState + elif data == "/": + self.state = self.selfClosingStartTagState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][0] += "\uFFFD" + leavingThisState = False + elif data in ("'", '"', "<"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": + "invalid-character-in-attribute-name"}) + self.currentToken["data"][-1][0] += data + leavingThisState = False + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "eof-in-attribute-name"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][0] += data + leavingThisState = False + + if leavingThisState: + # Attributes are not dropped at this stage. That happens when the + # start tag token is emitted so values can still be safely appended + # to attributes, but we do want to report the parse error in time. + self.currentToken["data"][-1][0] = ( + self.currentToken["data"][-1][0].translate(asciiUpper2Lower)) + for name, _ in self.currentToken["data"][:-1]: + if self.currentToken["data"][-1][0] == name: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "duplicate-attribute"}) + break + # XXX Fix for above XXX + if emitToken: + self.emitCurrentToken() + return True + + def afterAttributeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.stream.charsUntil(spaceCharacters, True) + elif data == "=": + self.state = self.beforeAttributeValueState + elif data == ">": + self.emitCurrentToken() + elif data in asciiLetters: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data == "/": + self.state = self.selfClosingStartTagState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"].append(["\uFFFD", ""]) + self.state = self.attributeNameState + elif data in ("'", '"', "<"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "invalid-character-after-attribute-name"}) + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-end-of-tag-but-got-eof"}) + self.state = self.dataState + else: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + return True + + def beforeAttributeValueState(self): + data = self.stream.char() + if data in spaceCharacters: + self.stream.charsUntil(spaceCharacters, True) + elif data == "\"": + self.state = self.attributeValueDoubleQuotedState + elif data == "&": + self.state = self.attributeValueUnQuotedState + self.stream.unget(data) + elif data == "'": + self.state = self.attributeValueSingleQuotedState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-attribute-value-but-got-right-bracket"}) + self.emitCurrentToken() + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + self.state = self.attributeValueUnQuotedState + elif data in ("=", "<", "`"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "equals-in-unquoted-attribute-value"}) + self.currentToken["data"][-1][1] += data + self.state = self.attributeValueUnQuotedState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-attribute-value-but-got-eof"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data + self.state = self.attributeValueUnQuotedState + return True + + def attributeValueDoubleQuotedState(self): + data = self.stream.char() + if data == "\"": + self.state = self.afterAttributeValueState + elif data == "&": + self.processEntityInAttribute('"') + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-attribute-value-double-quote"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data +\ + self.stream.charsUntil(("\"", "&", "\u0000")) + return True + + def attributeValueSingleQuotedState(self): + data = self.stream.char() + if data == "'": + self.state = self.afterAttributeValueState + elif data == "&": + self.processEntityInAttribute("'") + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-attribute-value-single-quote"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data +\ + self.stream.charsUntil(("'", "&", "\u0000")) + return True + + def attributeValueUnQuotedState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeAttributeNameState + elif data == "&": + self.processEntityInAttribute(">") + elif data == ">": + self.emitCurrentToken() + elif data in ('"', "'", "=", "<", "`"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-character-in-unquoted-attribute-value"}) + self.currentToken["data"][-1][1] += data + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-attribute-value-no-quotes"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data + self.stream.charsUntil( + frozenset(("&", ">", '"', "'", "=", "<", "`", "\u0000")) | spaceCharacters) + return True + + def afterAttributeValueState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeAttributeNameState + elif data == ">": + self.emitCurrentToken() + elif data == "/": + self.state = self.selfClosingStartTagState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-EOF-after-attribute-value"}) + self.stream.unget(data) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-character-after-attribute-value"}) + self.stream.unget(data) + self.state = self.beforeAttributeNameState + return True + + def selfClosingStartTagState(self): + data = self.stream.char() + if data == ">": + self.currentToken["selfClosing"] = True + self.emitCurrentToken() + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": + "unexpected-EOF-after-solidus-in-tag"}) + self.stream.unget(data) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-character-after-solidus-in-tag"}) + self.stream.unget(data) + self.state = self.beforeAttributeNameState + return True + + def bogusCommentState(self): + # Make a new comment token and give it as value all the characters + # until the first > or EOF (charsUntil checks for EOF automatically) + # and emit it. + data = self.stream.charsUntil(">") + data = data.replace("\u0000", "\uFFFD") + self.tokenQueue.append( + {"type": tokenTypes["Comment"], "data": data}) + + # Eat the character directly after the bogus comment which is either a + # ">" or an EOF. + self.stream.char() + self.state = self.dataState + return True + + def markupDeclarationOpenState(self): + charStack = [self.stream.char()] + if charStack[-1] == "-": + charStack.append(self.stream.char()) + if charStack[-1] == "-": + self.currentToken = {"type": tokenTypes["Comment"], "data": ""} + self.state = self.commentStartState + return True + elif charStack[-1] in ('d', 'D'): + matched = True + for expected in (('o', 'O'), ('c', 'C'), ('t', 'T'), + ('y', 'Y'), ('p', 'P'), ('e', 'E')): + charStack.append(self.stream.char()) + if charStack[-1] not in expected: + matched = False + break + if matched: + self.currentToken = {"type": tokenTypes["Doctype"], + "name": "", + "publicId": None, "systemId": None, + "correct": True} + self.state = self.doctypeState + return True + elif (charStack[-1] == "[" and + self.parser is not None and + self.parser.tree.openElements and + self.parser.tree.openElements[-1].namespace != self.parser.tree.defaultNamespace): + matched = True + for expected in ["C", "D", "A", "T", "A", "["]: + charStack.append(self.stream.char()) + if charStack[-1] != expected: + matched = False + break + if matched: + self.state = self.cdataSectionState + return True + + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-dashes-or-doctype"}) + + while charStack: + self.stream.unget(charStack.pop()) + self.state = self.bogusCommentState + return True + + def commentStartState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentStartDashState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "incorrect-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += data + self.state = self.commentState + return True + + def commentStartDashState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentEndState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "-\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "incorrect-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += "-" + data + self.state = self.commentState + return True + + def commentState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentEndDashState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "eof-in-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += data + \ + self.stream.charsUntil(("-", "\u0000")) + return True + + def commentEndDashState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentEndState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "-\uFFFD" + self.state = self.commentState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment-end-dash"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += "-" + data + self.state = self.commentState + return True + + def commentEndState(self): + data = self.stream.char() + if data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "--\uFFFD" + self.state = self.commentState + elif data == "!": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-bang-after-double-dash-in-comment"}) + self.state = self.commentEndBangState + elif data == "-": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-dash-after-double-dash-in-comment"}) + self.currentToken["data"] += data + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment-double-dash"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + # XXX + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-comment"}) + self.currentToken["data"] += "--" + data + self.state = self.commentState + return True + + def commentEndBangState(self): + data = self.stream.char() + if data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "-": + self.currentToken["data"] += "--!" + self.state = self.commentEndDashState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "--!\uFFFD" + self.state = self.commentState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment-end-bang-state"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += "--!" + data + self.state = self.commentState + return True + + def doctypeState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeDoctypeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-doctype-name-but-got-eof"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "need-space-after-doctype"}) + self.stream.unget(data) + self.state = self.beforeDoctypeNameState + return True + + def beforeDoctypeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-doctype-name-but-got-right-bracket"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["name"] = "\uFFFD" + self.state = self.doctypeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-doctype-name-but-got-eof"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["name"] = data + self.state = self.doctypeNameState + return True + + def doctypeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.state = self.afterDoctypeNameState + elif data == ">": + self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["name"] += "\uFFFD" + self.state = self.doctypeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype-name"}) + self.currentToken["correct"] = False + self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["name"] += data + return True + + def afterDoctypeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.currentToken["correct"] = False + self.stream.unget(data) + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + if data in ("p", "P"): + matched = True + for expected in (("u", "U"), ("b", "B"), ("l", "L"), + ("i", "I"), ("c", "C")): + data = self.stream.char() + if data not in expected: + matched = False + break + if matched: + self.state = self.afterDoctypePublicKeywordState + return True + elif data in ("s", "S"): + matched = True + for expected in (("y", "Y"), ("s", "S"), ("t", "T"), + ("e", "E"), ("m", "M")): + data = self.stream.char() + if data not in expected: + matched = False + break + if matched: + self.state = self.afterDoctypeSystemKeywordState + return True + + # All the characters read before the current 'data' will be + # [a-zA-Z], so they're garbage in the bogus doctype and can be + # discarded; only the latest character might be '>' or EOF + # and needs to be ungetted + self.stream.unget(data) + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-space-or-right-bracket-in-doctype", "datavars": + {"data": data}}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + + return True + + def afterDoctypePublicKeywordState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeDoctypePublicIdentifierState + elif data in ("'", '"'): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.stream.unget(data) + self.state = self.beforeDoctypePublicIdentifierState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.stream.unget(data) + self.state = self.beforeDoctypePublicIdentifierState + return True + + def beforeDoctypePublicIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == "\"": + self.currentToken["publicId"] = "" + self.state = self.doctypePublicIdentifierDoubleQuotedState + elif data == "'": + self.currentToken["publicId"] = "" + self.state = self.doctypePublicIdentifierSingleQuotedState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def doctypePublicIdentifierDoubleQuotedState(self): + data = self.stream.char() + if data == "\"": + self.state = self.afterDoctypePublicIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["publicId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["publicId"] += data + return True + + def doctypePublicIdentifierSingleQuotedState(self): + data = self.stream.char() + if data == "'": + self.state = self.afterDoctypePublicIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["publicId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["publicId"] += data + return True + + def afterDoctypePublicIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.betweenDoctypePublicAndSystemIdentifiersState + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == '"': + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierDoubleQuotedState + elif data == "'": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierSingleQuotedState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def betweenDoctypePublicAndSystemIdentifiersState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == '"': + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierDoubleQuotedState + elif data == "'": + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierSingleQuotedState + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def afterDoctypeSystemKeywordState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeDoctypeSystemIdentifierState + elif data in ("'", '"'): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.stream.unget(data) + self.state = self.beforeDoctypeSystemIdentifierState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.stream.unget(data) + self.state = self.beforeDoctypeSystemIdentifierState + return True + + def beforeDoctypeSystemIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == "\"": + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierDoubleQuotedState + elif data == "'": + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierSingleQuotedState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def doctypeSystemIdentifierDoubleQuotedState(self): + data = self.stream.char() + if data == "\"": + self.state = self.afterDoctypeSystemIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["systemId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["systemId"] += data + return True + + def doctypeSystemIdentifierSingleQuotedState(self): + data = self.stream.char() + if data == "'": + self.state = self.afterDoctypeSystemIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["systemId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["systemId"] += data + return True + + def afterDoctypeSystemIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.state = self.bogusDoctypeState + return True + + def bogusDoctypeState(self): + data = self.stream.char() + if data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + # XXX EMIT + self.stream.unget(data) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + pass + return True + + def cdataSectionState(self): + data = [] + while True: + data.append(self.stream.charsUntil("]")) + data.append(self.stream.charsUntil(">")) + char = self.stream.char() + if char == EOF: + break + else: + assert char == ">" + if data[-1][-2:] == "]]": + data[-1] = data[-1][:-2] + break + else: + data.append(char) + + data = "".join(data) # pylint:disable=redefined-variable-type + # Deal with null here rather than in the parser + nullCount = data.count("\u0000") + if nullCount > 0: + for _ in range(nullCount): + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + data = data.replace("\u0000", "\uFFFD") + if data: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": data}) + self.state = self.dataState + return True diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/__init__.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/__init__.py new file mode 100644 index 0000000..a5ba4bf --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/__init__.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import, division, unicode_literals + +from .py import Trie as PyTrie + +Trie = PyTrie + +# pylint:disable=wrong-import-position +try: + from .datrie import Trie as DATrie +except ImportError: + pass +else: + Trie = DATrie +# pylint:enable=wrong-import-position diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/_base.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/_base.py new file mode 100644 index 0000000..a1158bb --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/_base.py @@ -0,0 +1,37 @@ +from __future__ import absolute_import, division, unicode_literals + +from collections import Mapping + + +class Trie(Mapping): + """Abstract base class for tries""" + + def keys(self, prefix=None): + # pylint:disable=arguments-differ + keys = super(Trie, self).keys() + + if prefix is None: + return set(keys) + + return {x for x in keys if x.startswith(prefix)} + + def has_keys_with_prefix(self, prefix): + for key in self.keys(): + if key.startswith(prefix): + return True + + return False + + def longest_prefix(self, prefix): + if prefix in self: + return prefix + + for i in range(1, len(prefix) + 1): + if prefix[:-i] in self: + return prefix[:-i] + + raise KeyError(prefix) + + def longest_prefix_item(self, prefix): + lprefix = self.longest_prefix(prefix) + return (lprefix, self[lprefix]) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/datrie.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/datrie.py new file mode 100644 index 0000000..e2e5f86 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/datrie.py @@ -0,0 +1,44 @@ +from __future__ import absolute_import, division, unicode_literals + +from datrie import Trie as DATrie +from pip._vendor.six import text_type + +from ._base import Trie as ABCTrie + + +class Trie(ABCTrie): + def __init__(self, data): + chars = set() + for key in data.keys(): + if not isinstance(key, text_type): + raise TypeError("All keys must be strings") + for char in key: + chars.add(char) + + self._data = DATrie("".join(chars)) + for key, value in data.items(): + self._data[key] = value + + def __contains__(self, key): + return key in self._data + + def __len__(self): + return len(self._data) + + def __iter__(self): + raise NotImplementedError() + + def __getitem__(self, key): + return self._data[key] + + def keys(self, prefix=None): + return self._data.keys(prefix) + + def has_keys_with_prefix(self, prefix): + return self._data.has_keys_with_prefix(prefix) + + def longest_prefix(self, prefix): + return self._data.longest_prefix(prefix) + + def longest_prefix_item(self, prefix): + return self._data.longest_prefix_item(prefix) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/py.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/py.py new file mode 100644 index 0000000..c178b21 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/py.py @@ -0,0 +1,67 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +from bisect import bisect_left + +from ._base import Trie as ABCTrie + + +class Trie(ABCTrie): + def __init__(self, data): + if not all(isinstance(x, text_type) for x in data.keys()): + raise TypeError("All keys must be strings") + + self._data = data + self._keys = sorted(data.keys()) + self._cachestr = "" + self._cachepoints = (0, len(data)) + + def __contains__(self, key): + return key in self._data + + def __len__(self): + return len(self._data) + + def __iter__(self): + return iter(self._data) + + def __getitem__(self, key): + return self._data[key] + + def keys(self, prefix=None): + if prefix is None or prefix == "" or not self._keys: + return set(self._keys) + + if prefix.startswith(self._cachestr): + lo, hi = self._cachepoints + start = i = bisect_left(self._keys, prefix, lo, hi) + else: + start = i = bisect_left(self._keys, prefix) + + keys = set() + if start == len(self._keys): + return keys + + while self._keys[i].startswith(prefix): + keys.add(self._keys[i]) + i += 1 + + self._cachestr = prefix + self._cachepoints = (start, i) + + return keys + + def has_keys_with_prefix(self, prefix): + if prefix in self._data: + return True + + if prefix.startswith(self._cachestr): + lo, hi = self._cachepoints + i = bisect_left(self._keys, prefix, lo, hi) + else: + i = bisect_left(self._keys, prefix) + + if i == len(self._keys): + return False + + return self._keys[i].startswith(prefix) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_utils.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_utils.py new file mode 100644 index 0000000..0703afb --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/_utils.py @@ -0,0 +1,124 @@ +from __future__ import absolute_import, division, unicode_literals + +from types import ModuleType + +from pip._vendor.six import text_type + +try: + import xml.etree.cElementTree as default_etree +except ImportError: + import xml.etree.ElementTree as default_etree + + +__all__ = ["default_etree", "MethodDispatcher", "isSurrogatePair", + "surrogatePairToCodepoint", "moduleFactoryFactory", + "supports_lone_surrogates"] + + +# Platforms not supporting lone surrogates (\uD800-\uDFFF) should be +# caught by the below test. In general this would be any platform +# using UTF-16 as its encoding of unicode strings, such as +# Jython. This is because UTF-16 itself is based on the use of such +# surrogates, and there is no mechanism to further escape such +# escapes. +try: + _x = eval('"\\uD800"') # pylint:disable=eval-used + if not isinstance(_x, text_type): + # We need this with u"" because of http://bugs.jython.org/issue2039 + _x = eval('u"\\uD800"') # pylint:disable=eval-used + assert isinstance(_x, text_type) +except: # pylint:disable=bare-except + supports_lone_surrogates = False +else: + supports_lone_surrogates = True + + +class MethodDispatcher(dict): + """Dict with 2 special properties: + + On initiation, keys that are lists, sets or tuples are converted to + multiple keys so accessing any one of the items in the original + list-like object returns the matching value + + md = MethodDispatcher({("foo", "bar"):"baz"}) + md["foo"] == "baz" + + A default value which can be set through the default attribute. + """ + + def __init__(self, items=()): + # Using _dictEntries instead of directly assigning to self is about + # twice as fast. Please do careful performance testing before changing + # anything here. + _dictEntries = [] + for name, value in items: + if isinstance(name, (list, tuple, frozenset, set)): + for item in name: + _dictEntries.append((item, value)) + else: + _dictEntries.append((name, value)) + dict.__init__(self, _dictEntries) + assert len(self) == len(_dictEntries) + self.default = None + + def __getitem__(self, key): + return dict.get(self, key, self.default) + + +# Some utility functions to deal with weirdness around UCS2 vs UCS4 +# python builds + +def isSurrogatePair(data): + return (len(data) == 2 and + ord(data[0]) >= 0xD800 and ord(data[0]) <= 0xDBFF and + ord(data[1]) >= 0xDC00 and ord(data[1]) <= 0xDFFF) + + +def surrogatePairToCodepoint(data): + char_val = (0x10000 + (ord(data[0]) - 0xD800) * 0x400 + + (ord(data[1]) - 0xDC00)) + return char_val + +# Module Factory Factory (no, this isn't Java, I know) +# Here to stop this being duplicated all over the place. + + +def moduleFactoryFactory(factory): + moduleCache = {} + + def moduleFactory(baseModule, *args, **kwargs): + if isinstance(ModuleType.__name__, type("")): + name = "_%s_factory" % baseModule.__name__ + else: + name = b"_%s_factory" % baseModule.__name__ + + kwargs_tuple = tuple(kwargs.items()) + + try: + return moduleCache[name][args][kwargs_tuple] + except KeyError: + mod = ModuleType(name) + objs = factory(baseModule, *args, **kwargs) + mod.__dict__.update(objs) + if "name" not in moduleCache: + moduleCache[name] = {} + if "args" not in moduleCache[name]: + moduleCache[name][args] = {} + if "kwargs" not in moduleCache[name][args]: + moduleCache[name][args][kwargs_tuple] = {} + moduleCache[name][args][kwargs_tuple] = mod + return mod + + return moduleFactory + + +def memoize(func): + cache = {} + + def wrapped(*args, **kwargs): + key = (tuple(args), tuple(kwargs.items())) + if key not in cache: + cache[key] = func(*args, **kwargs) + return cache[key] + + return wrapped diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/constants.py b/venv/Lib/site-packages/pip/_vendor/html5lib/constants.py new file mode 100644 index 0000000..1ff8041 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/constants.py @@ -0,0 +1,2947 @@ +from __future__ import absolute_import, division, unicode_literals + +import string + +EOF = None + +E = { + "null-character": + "Null character in input stream, replaced with U+FFFD.", + "invalid-codepoint": + "Invalid codepoint in stream.", + "incorrectly-placed-solidus": + "Solidus (/) incorrectly placed in tag.", + "incorrect-cr-newline-entity": + "Incorrect CR newline entity, replaced with LF.", + "illegal-windows-1252-entity": + "Entity used with illegal number (windows-1252 reference).", + "cant-convert-numeric-entity": + "Numeric entity couldn't be converted to character " + "(codepoint U+%(charAsInt)08x).", + "illegal-codepoint-for-numeric-entity": + "Numeric entity represents an illegal codepoint: " + "U+%(charAsInt)08x.", + "numeric-entity-without-semicolon": + "Numeric entity didn't end with ';'.", + "expected-numeric-entity-but-got-eof": + "Numeric entity expected. Got end of file instead.", + "expected-numeric-entity": + "Numeric entity expected but none found.", + "named-entity-without-semicolon": + "Named entity didn't end with ';'.", + "expected-named-entity": + "Named entity expected. Got none.", + "attributes-in-end-tag": + "End tag contains unexpected attributes.", + 'self-closing-flag-on-end-tag': + "End tag contains unexpected self-closing flag.", + "expected-tag-name-but-got-right-bracket": + "Expected tag name. Got '>' instead.", + "expected-tag-name-but-got-question-mark": + "Expected tag name. Got '?' instead. (HTML doesn't " + "support processing instructions.)", + "expected-tag-name": + "Expected tag name. Got something else instead", + "expected-closing-tag-but-got-right-bracket": + "Expected closing tag. Got '>' instead. Ignoring '</>'.", + "expected-closing-tag-but-got-eof": + "Expected closing tag. Unexpected end of file.", + "expected-closing-tag-but-got-char": + "Expected closing tag. Unexpected character '%(data)s' found.", + "eof-in-tag-name": + "Unexpected end of file in the tag name.", + "expected-attribute-name-but-got-eof": + "Unexpected end of file. Expected attribute name instead.", + "eof-in-attribute-name": + "Unexpected end of file in attribute name.", + "invalid-character-in-attribute-name": + "Invalid character in attribute name", + "duplicate-attribute": + "Dropped duplicate attribute on tag.", + "expected-end-of-tag-name-but-got-eof": + "Unexpected end of file. Expected = or end of tag.", + "expected-attribute-value-but-got-eof": + "Unexpected end of file. Expected attribute value.", + "expected-attribute-value-but-got-right-bracket": + "Expected attribute value. Got '>' instead.", + 'equals-in-unquoted-attribute-value': + "Unexpected = in unquoted attribute", + 'unexpected-character-in-unquoted-attribute-value': + "Unexpected character in unquoted attribute", + "invalid-character-after-attribute-name": + "Unexpected character after attribute name.", + "unexpected-character-after-attribute-value": + "Unexpected character after attribute value.", + "eof-in-attribute-value-double-quote": + "Unexpected end of file in attribute value (\").", + "eof-in-attribute-value-single-quote": + "Unexpected end of file in attribute value (').", + "eof-in-attribute-value-no-quotes": + "Unexpected end of file in attribute value.", + "unexpected-EOF-after-solidus-in-tag": + "Unexpected end of file in tag. Expected >", + "unexpected-character-after-solidus-in-tag": + "Unexpected character after / in tag. Expected >", + "expected-dashes-or-doctype": + "Expected '--' or 'DOCTYPE'. Not found.", + "unexpected-bang-after-double-dash-in-comment": + "Unexpected ! after -- in comment", + "unexpected-space-after-double-dash-in-comment": + "Unexpected space after -- in comment", + "incorrect-comment": + "Incorrect comment.", + "eof-in-comment": + "Unexpected end of file in comment.", + "eof-in-comment-end-dash": + "Unexpected end of file in comment (-)", + "unexpected-dash-after-double-dash-in-comment": + "Unexpected '-' after '--' found in comment.", + "eof-in-comment-double-dash": + "Unexpected end of file in comment (--).", + "eof-in-comment-end-space-state": + "Unexpected end of file in comment.", + "eof-in-comment-end-bang-state": + "Unexpected end of file in comment.", + "unexpected-char-in-comment": + "Unexpected character in comment found.", + "need-space-after-doctype": + "No space after literal string 'DOCTYPE'.", + "expected-doctype-name-but-got-right-bracket": + "Unexpected > character. Expected DOCTYPE name.", + "expected-doctype-name-but-got-eof": + "Unexpected end of file. Expected DOCTYPE name.", + "eof-in-doctype-name": + "Unexpected end of file in DOCTYPE name.", + "eof-in-doctype": + "Unexpected end of file in DOCTYPE.", + "expected-space-or-right-bracket-in-doctype": + "Expected space or '>'. Got '%(data)s'", + "unexpected-end-of-doctype": + "Unexpected end of DOCTYPE.", + "unexpected-char-in-doctype": + "Unexpected character in DOCTYPE.", + "eof-in-innerhtml": + "XXX innerHTML EOF", + "unexpected-doctype": + "Unexpected DOCTYPE. Ignored.", + "non-html-root": + "html needs to be the first start tag.", + "expected-doctype-but-got-eof": + "Unexpected End of file. Expected DOCTYPE.", + "unknown-doctype": + "Erroneous DOCTYPE.", + "expected-doctype-but-got-chars": + "Unexpected non-space characters. Expected DOCTYPE.", + "expected-doctype-but-got-start-tag": + "Unexpected start tag (%(name)s). Expected DOCTYPE.", + "expected-doctype-but-got-end-tag": + "Unexpected end tag (%(name)s). Expected DOCTYPE.", + "end-tag-after-implied-root": + "Unexpected end tag (%(name)s) after the (implied) root element.", + "expected-named-closing-tag-but-got-eof": + "Unexpected end of file. Expected end tag (%(name)s).", + "two-heads-are-not-better-than-one": + "Unexpected start tag head in existing head. Ignored.", + "unexpected-end-tag": + "Unexpected end tag (%(name)s). Ignored.", + "unexpected-start-tag-out-of-my-head": + "Unexpected start tag (%(name)s) that can be in head. Moved.", + "unexpected-start-tag": + "Unexpected start tag (%(name)s).", + "missing-end-tag": + "Missing end tag (%(name)s).", + "missing-end-tags": + "Missing end tags (%(name)s).", + "unexpected-start-tag-implies-end-tag": + "Unexpected start tag (%(startName)s) " + "implies end tag (%(endName)s).", + "unexpected-start-tag-treated-as": + "Unexpected start tag (%(originalName)s). Treated as %(newName)s.", + "deprecated-tag": + "Unexpected start tag %(name)s. Don't use it!", + "unexpected-start-tag-ignored": + "Unexpected start tag %(name)s. Ignored.", + "expected-one-end-tag-but-got-another": + "Unexpected end tag (%(gotName)s). " + "Missing end tag (%(expectedName)s).", + "end-tag-too-early": + "End tag (%(name)s) seen too early. Expected other end tag.", + "end-tag-too-early-named": + "Unexpected end tag (%(gotName)s). Expected end tag (%(expectedName)s).", + "end-tag-too-early-ignored": + "End tag (%(name)s) seen too early. Ignored.", + "adoption-agency-1.1": + "End tag (%(name)s) violates step 1, " + "paragraph 1 of the adoption agency algorithm.", + "adoption-agency-1.2": + "End tag (%(name)s) violates step 1, " + "paragraph 2 of the adoption agency algorithm.", + "adoption-agency-1.3": + "End tag (%(name)s) violates step 1, " + "paragraph 3 of the adoption agency algorithm.", + "adoption-agency-4.4": + "End tag (%(name)s) violates step 4, " + "paragraph 4 of the adoption agency algorithm.", + "unexpected-end-tag-treated-as": + "Unexpected end tag (%(originalName)s). Treated as %(newName)s.", + "no-end-tag": + "This element (%(name)s) has no end tag.", + "unexpected-implied-end-tag-in-table": + "Unexpected implied end tag (%(name)s) in the table phase.", + "unexpected-implied-end-tag-in-table-body": + "Unexpected implied end tag (%(name)s) in the table body phase.", + "unexpected-char-implies-table-voodoo": + "Unexpected non-space characters in " + "table context caused voodoo mode.", + "unexpected-hidden-input-in-table": + "Unexpected input with type hidden in table context.", + "unexpected-form-in-table": + "Unexpected form in table context.", + "unexpected-start-tag-implies-table-voodoo": + "Unexpected start tag (%(name)s) in " + "table context caused voodoo mode.", + "unexpected-end-tag-implies-table-voodoo": + "Unexpected end tag (%(name)s) in " + "table context caused voodoo mode.", + "unexpected-cell-in-table-body": + "Unexpected table cell start tag (%(name)s) " + "in the table body phase.", + "unexpected-cell-end-tag": + "Got table cell end tag (%(name)s) " + "while required end tags are missing.", + "unexpected-end-tag-in-table-body": + "Unexpected end tag (%(name)s) in the table body phase. Ignored.", + "unexpected-implied-end-tag-in-table-row": + "Unexpected implied end tag (%(name)s) in the table row phase.", + "unexpected-end-tag-in-table-row": + "Unexpected end tag (%(name)s) in the table row phase. Ignored.", + "unexpected-select-in-select": + "Unexpected select start tag in the select phase " + "treated as select end tag.", + "unexpected-input-in-select": + "Unexpected input start tag in the select phase.", + "unexpected-start-tag-in-select": + "Unexpected start tag token (%(name)s in the select phase. " + "Ignored.", + "unexpected-end-tag-in-select": + "Unexpected end tag (%(name)s) in the select phase. Ignored.", + "unexpected-table-element-start-tag-in-select-in-table": + "Unexpected table element start tag (%(name)s) in the select in table phase.", + "unexpected-table-element-end-tag-in-select-in-table": + "Unexpected table element end tag (%(name)s) in the select in table phase.", + "unexpected-char-after-body": + "Unexpected non-space characters in the after body phase.", + "unexpected-start-tag-after-body": + "Unexpected start tag token (%(name)s)" + " in the after body phase.", + "unexpected-end-tag-after-body": + "Unexpected end tag token (%(name)s)" + " in the after body phase.", + "unexpected-char-in-frameset": + "Unexpected characters in the frameset phase. Characters ignored.", + "unexpected-start-tag-in-frameset": + "Unexpected start tag token (%(name)s)" + " in the frameset phase. Ignored.", + "unexpected-frameset-in-frameset-innerhtml": + "Unexpected end tag token (frameset) " + "in the frameset phase (innerHTML).", + "unexpected-end-tag-in-frameset": + "Unexpected end tag token (%(name)s)" + " in the frameset phase. Ignored.", + "unexpected-char-after-frameset": + "Unexpected non-space characters in the " + "after frameset phase. Ignored.", + "unexpected-start-tag-after-frameset": + "Unexpected start tag (%(name)s)" + " in the after frameset phase. Ignored.", + "unexpected-end-tag-after-frameset": + "Unexpected end tag (%(name)s)" + " in the after frameset phase. Ignored.", + "unexpected-end-tag-after-body-innerhtml": + "Unexpected end tag after body(innerHtml)", + "expected-eof-but-got-char": + "Unexpected non-space characters. Expected end of file.", + "expected-eof-but-got-start-tag": + "Unexpected start tag (%(name)s)" + ". Expected end of file.", + "expected-eof-but-got-end-tag": + "Unexpected end tag (%(name)s)" + ". Expected end of file.", + "eof-in-table": + "Unexpected end of file. Expected table content.", + "eof-in-select": + "Unexpected end of file. Expected select content.", + "eof-in-frameset": + "Unexpected end of file. Expected frameset content.", + "eof-in-script-in-script": + "Unexpected end of file. Expected script content.", + "eof-in-foreign-lands": + "Unexpected end of file. Expected foreign content", + "non-void-element-with-trailing-solidus": + "Trailing solidus not allowed on element %(name)s", + "unexpected-html-element-in-foreign-content": + "Element %(name)s not allowed in a non-html context", + "unexpected-end-tag-before-html": + "Unexpected end tag (%(name)s) before html.", + "unexpected-inhead-noscript-tag": + "Element %(name)s not allowed in a inhead-noscript context", + "eof-in-head-noscript": + "Unexpected end of file. Expected inhead-noscript content", + "char-in-head-noscript": + "Unexpected non-space character. Expected inhead-noscript content", + "XXX-undefined-error": + "Undefined error (this sucks and should be fixed)", +} + +namespaces = { + "html": "http://www.w3.org/1999/xhtml", + "mathml": "http://www.w3.org/1998/Math/MathML", + "svg": "http://www.w3.org/2000/svg", + "xlink": "http://www.w3.org/1999/xlink", + "xml": "http://www.w3.org/XML/1998/namespace", + "xmlns": "http://www.w3.org/2000/xmlns/" +} + +scopingElements = frozenset([ + (namespaces["html"], "applet"), + (namespaces["html"], "caption"), + (namespaces["html"], "html"), + (namespaces["html"], "marquee"), + (namespaces["html"], "object"), + (namespaces["html"], "table"), + (namespaces["html"], "td"), + (namespaces["html"], "th"), + (namespaces["mathml"], "mi"), + (namespaces["mathml"], "mo"), + (namespaces["mathml"], "mn"), + (namespaces["mathml"], "ms"), + (namespaces["mathml"], "mtext"), + (namespaces["mathml"], "annotation-xml"), + (namespaces["svg"], "foreignObject"), + (namespaces["svg"], "desc"), + (namespaces["svg"], "title"), +]) + +formattingElements = frozenset([ + (namespaces["html"], "a"), + (namespaces["html"], "b"), + (namespaces["html"], "big"), + (namespaces["html"], "code"), + (namespaces["html"], "em"), + (namespaces["html"], "font"), + (namespaces["html"], "i"), + (namespaces["html"], "nobr"), + (namespaces["html"], "s"), + (namespaces["html"], "small"), + (namespaces["html"], "strike"), + (namespaces["html"], "strong"), + (namespaces["html"], "tt"), + (namespaces["html"], "u") +]) + +specialElements = frozenset([ + (namespaces["html"], "address"), + (namespaces["html"], "applet"), + (namespaces["html"], "area"), + (namespaces["html"], "article"), + (namespaces["html"], "aside"), + (namespaces["html"], "base"), + (namespaces["html"], "basefont"), + (namespaces["html"], "bgsound"), + (namespaces["html"], "blockquote"), + (namespaces["html"], "body"), + (namespaces["html"], "br"), + (namespaces["html"], "button"), + (namespaces["html"], "caption"), + (namespaces["html"], "center"), + (namespaces["html"], "col"), + (namespaces["html"], "colgroup"), + (namespaces["html"], "command"), + (namespaces["html"], "dd"), + (namespaces["html"], "details"), + (namespaces["html"], "dir"), + (namespaces["html"], "div"), + (namespaces["html"], "dl"), + (namespaces["html"], "dt"), + (namespaces["html"], "embed"), + (namespaces["html"], "fieldset"), + (namespaces["html"], "figure"), + (namespaces["html"], "footer"), + (namespaces["html"], "form"), + (namespaces["html"], "frame"), + (namespaces["html"], "frameset"), + (namespaces["html"], "h1"), + (namespaces["html"], "h2"), + (namespaces["html"], "h3"), + (namespaces["html"], "h4"), + (namespaces["html"], "h5"), + (namespaces["html"], "h6"), + (namespaces["html"], "head"), + (namespaces["html"], "header"), + (namespaces["html"], "hr"), + (namespaces["html"], "html"), + (namespaces["html"], "iframe"), + # Note that image is commented out in the spec as "this isn't an + # element that can end up on the stack, so it doesn't matter," + (namespaces["html"], "image"), + (namespaces["html"], "img"), + (namespaces["html"], "input"), + (namespaces["html"], "isindex"), + (namespaces["html"], "li"), + (namespaces["html"], "link"), + (namespaces["html"], "listing"), + (namespaces["html"], "marquee"), + (namespaces["html"], "menu"), + (namespaces["html"], "meta"), + (namespaces["html"], "nav"), + (namespaces["html"], "noembed"), + (namespaces["html"], "noframes"), + (namespaces["html"], "noscript"), + (namespaces["html"], "object"), + (namespaces["html"], "ol"), + (namespaces["html"], "p"), + (namespaces["html"], "param"), + (namespaces["html"], "plaintext"), + (namespaces["html"], "pre"), + (namespaces["html"], "script"), + (namespaces["html"], "section"), + (namespaces["html"], "select"), + (namespaces["html"], "style"), + (namespaces["html"], "table"), + (namespaces["html"], "tbody"), + (namespaces["html"], "td"), + (namespaces["html"], "textarea"), + (namespaces["html"], "tfoot"), + (namespaces["html"], "th"), + (namespaces["html"], "thead"), + (namespaces["html"], "title"), + (namespaces["html"], "tr"), + (namespaces["html"], "ul"), + (namespaces["html"], "wbr"), + (namespaces["html"], "xmp"), + (namespaces["svg"], "foreignObject") +]) + +htmlIntegrationPointElements = frozenset([ + (namespaces["mathml"], "annotation-xml"), + (namespaces["svg"], "foreignObject"), + (namespaces["svg"], "desc"), + (namespaces["svg"], "title") +]) + +mathmlTextIntegrationPointElements = frozenset([ + (namespaces["mathml"], "mi"), + (namespaces["mathml"], "mo"), + (namespaces["mathml"], "mn"), + (namespaces["mathml"], "ms"), + (namespaces["mathml"], "mtext") +]) + +adjustSVGAttributes = { + "attributename": "attributeName", + "attributetype": "attributeType", + "basefrequency": "baseFrequency", + "baseprofile": "baseProfile", + "calcmode": "calcMode", + "clippathunits": "clipPathUnits", + "contentscripttype": "contentScriptType", + "contentstyletype": "contentStyleType", + "diffuseconstant": "diffuseConstant", + "edgemode": "edgeMode", + "externalresourcesrequired": "externalResourcesRequired", + "filterres": "filterRes", + "filterunits": "filterUnits", + "glyphref": "glyphRef", + "gradienttransform": "gradientTransform", + "gradientunits": "gradientUnits", + "kernelmatrix": "kernelMatrix", + "kernelunitlength": "kernelUnitLength", + "keypoints": "keyPoints", + "keysplines": "keySplines", + "keytimes": "keyTimes", + "lengthadjust": "lengthAdjust", + "limitingconeangle": "limitingConeAngle", + "markerheight": "markerHeight", + "markerunits": "markerUnits", + "markerwidth": "markerWidth", + "maskcontentunits": "maskContentUnits", + "maskunits": "maskUnits", + "numoctaves": "numOctaves", + "pathlength": "pathLength", + "patterncontentunits": "patternContentUnits", + "patterntransform": "patternTransform", + "patternunits": "patternUnits", + "pointsatx": "pointsAtX", + "pointsaty": "pointsAtY", + "pointsatz": "pointsAtZ", + "preservealpha": "preserveAlpha", + "preserveaspectratio": "preserveAspectRatio", + "primitiveunits": "primitiveUnits", + "refx": "refX", + "refy": "refY", + "repeatcount": "repeatCount", + "repeatdur": "repeatDur", + "requiredextensions": "requiredExtensions", + "requiredfeatures": "requiredFeatures", + "specularconstant": "specularConstant", + "specularexponent": "specularExponent", + "spreadmethod": "spreadMethod", + "startoffset": "startOffset", + "stddeviation": "stdDeviation", + "stitchtiles": "stitchTiles", + "surfacescale": "surfaceScale", + "systemlanguage": "systemLanguage", + "tablevalues": "tableValues", + "targetx": "targetX", + "targety": "targetY", + "textlength": "textLength", + "viewbox": "viewBox", + "viewtarget": "viewTarget", + "xchannelselector": "xChannelSelector", + "ychannelselector": "yChannelSelector", + "zoomandpan": "zoomAndPan" +} + +adjustMathMLAttributes = {"definitionurl": "definitionURL"} + +adjustForeignAttributes = { + "xlink:actuate": ("xlink", "actuate", namespaces["xlink"]), + "xlink:arcrole": ("xlink", "arcrole", namespaces["xlink"]), + "xlink:href": ("xlink", "href", namespaces["xlink"]), + "xlink:role": ("xlink", "role", namespaces["xlink"]), + "xlink:show": ("xlink", "show", namespaces["xlink"]), + "xlink:title": ("xlink", "title", namespaces["xlink"]), + "xlink:type": ("xlink", "type", namespaces["xlink"]), + "xml:base": ("xml", "base", namespaces["xml"]), + "xml:lang": ("xml", "lang", namespaces["xml"]), + "xml:space": ("xml", "space", namespaces["xml"]), + "xmlns": (None, "xmlns", namespaces["xmlns"]), + "xmlns:xlink": ("xmlns", "xlink", namespaces["xmlns"]) +} + +unadjustForeignAttributes = dict([((ns, local), qname) for qname, (prefix, local, ns) in + adjustForeignAttributes.items()]) + +spaceCharacters = frozenset([ + "\t", + "\n", + "\u000C", + " ", + "\r" +]) + +tableInsertModeElements = frozenset([ + "table", + "tbody", + "tfoot", + "thead", + "tr" +]) + +asciiLowercase = frozenset(string.ascii_lowercase) +asciiUppercase = frozenset(string.ascii_uppercase) +asciiLetters = frozenset(string.ascii_letters) +digits = frozenset(string.digits) +hexDigits = frozenset(string.hexdigits) + +asciiUpper2Lower = dict([(ord(c), ord(c.lower())) + for c in string.ascii_uppercase]) + +# Heading elements need to be ordered +headingElements = ( + "h1", + "h2", + "h3", + "h4", + "h5", + "h6" +) + +voidElements = frozenset([ + "base", + "command", + "event-source", + "link", + "meta", + "hr", + "br", + "img", + "embed", + "param", + "area", + "col", + "input", + "source", + "track" +]) + +cdataElements = frozenset(['title', 'textarea']) + +rcdataElements = frozenset([ + 'style', + 'script', + 'xmp', + 'iframe', + 'noembed', + 'noframes', + 'noscript' +]) + +booleanAttributes = { + "": frozenset(["irrelevant", "itemscope"]), + "style": frozenset(["scoped"]), + "img": frozenset(["ismap"]), + "audio": frozenset(["autoplay", "controls"]), + "video": frozenset(["autoplay", "controls"]), + "script": frozenset(["defer", "async"]), + "details": frozenset(["open"]), + "datagrid": frozenset(["multiple", "disabled"]), + "command": frozenset(["hidden", "disabled", "checked", "default"]), + "hr": frozenset(["noshade"]), + "menu": frozenset(["autosubmit"]), + "fieldset": frozenset(["disabled", "readonly"]), + "option": frozenset(["disabled", "readonly", "selected"]), + "optgroup": frozenset(["disabled", "readonly"]), + "button": frozenset(["disabled", "autofocus"]), + "input": frozenset(["disabled", "readonly", "required", "autofocus", "checked", "ismap"]), + "select": frozenset(["disabled", "readonly", "autofocus", "multiple"]), + "output": frozenset(["disabled", "readonly"]), + "iframe": frozenset(["seamless"]), +} + +# entitiesWindows1252 has to be _ordered_ and needs to have an index. It +# therefore can't be a frozenset. +entitiesWindows1252 = ( + 8364, # 0x80 0x20AC EURO SIGN + 65533, # 0x81 UNDEFINED + 8218, # 0x82 0x201A SINGLE LOW-9 QUOTATION MARK + 402, # 0x83 0x0192 LATIN SMALL LETTER F WITH HOOK + 8222, # 0x84 0x201E DOUBLE LOW-9 QUOTATION MARK + 8230, # 0x85 0x2026 HORIZONTAL ELLIPSIS + 8224, # 0x86 0x2020 DAGGER + 8225, # 0x87 0x2021 DOUBLE DAGGER + 710, # 0x88 0x02C6 MODIFIER LETTER CIRCUMFLEX ACCENT + 8240, # 0x89 0x2030 PER MILLE SIGN + 352, # 0x8A 0x0160 LATIN CAPITAL LETTER S WITH CARON + 8249, # 0x8B 0x2039 SINGLE LEFT-POINTING ANGLE QUOTATION MARK + 338, # 0x8C 0x0152 LATIN CAPITAL LIGATURE OE + 65533, # 0x8D UNDEFINED + 381, # 0x8E 0x017D LATIN CAPITAL LETTER Z WITH CARON + 65533, # 0x8F UNDEFINED + 65533, # 0x90 UNDEFINED + 8216, # 0x91 0x2018 LEFT SINGLE QUOTATION MARK + 8217, # 0x92 0x2019 RIGHT SINGLE QUOTATION MARK + 8220, # 0x93 0x201C LEFT DOUBLE QUOTATION MARK + 8221, # 0x94 0x201D RIGHT DOUBLE QUOTATION MARK + 8226, # 0x95 0x2022 BULLET + 8211, # 0x96 0x2013 EN DASH + 8212, # 0x97 0x2014 EM DASH + 732, # 0x98 0x02DC SMALL TILDE + 8482, # 0x99 0x2122 TRADE MARK SIGN + 353, # 0x9A 0x0161 LATIN SMALL LETTER S WITH CARON + 8250, # 0x9B 0x203A SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + 339, # 0x9C 0x0153 LATIN SMALL LIGATURE OE + 65533, # 0x9D UNDEFINED + 382, # 0x9E 0x017E LATIN SMALL LETTER Z WITH CARON + 376 # 0x9F 0x0178 LATIN CAPITAL LETTER Y WITH DIAERESIS +) + +xmlEntities = frozenset(['lt;', 'gt;', 'amp;', 'apos;', 'quot;']) + +entities = { + "AElig": "\xc6", + "AElig;": "\xc6", + "AMP": "&", + "AMP;": "&", + "Aacute": "\xc1", + "Aacute;": "\xc1", + "Abreve;": "\u0102", + "Acirc": "\xc2", + "Acirc;": "\xc2", + "Acy;": "\u0410", + "Afr;": "\U0001d504", + "Agrave": "\xc0", + "Agrave;": "\xc0", + "Alpha;": "\u0391", + "Amacr;": "\u0100", + "And;": "\u2a53", + "Aogon;": "\u0104", + "Aopf;": "\U0001d538", + "ApplyFunction;": "\u2061", + "Aring": "\xc5", + "Aring;": "\xc5", + "Ascr;": "\U0001d49c", + "Assign;": "\u2254", + "Atilde": "\xc3", + "Atilde;": "\xc3", + "Auml": "\xc4", + "Auml;": "\xc4", + "Backslash;": "\u2216", + "Barv;": "\u2ae7", + "Barwed;": "\u2306", + "Bcy;": "\u0411", + "Because;": "\u2235", + "Bernoullis;": "\u212c", + "Beta;": "\u0392", + "Bfr;": "\U0001d505", + "Bopf;": "\U0001d539", + "Breve;": "\u02d8", + "Bscr;": "\u212c", + "Bumpeq;": "\u224e", + "CHcy;": "\u0427", + "COPY": "\xa9", + "COPY;": "\xa9", + "Cacute;": "\u0106", + "Cap;": "\u22d2", + "CapitalDifferentialD;": "\u2145", + "Cayleys;": "\u212d", + "Ccaron;": "\u010c", + "Ccedil": "\xc7", + "Ccedil;": "\xc7", + "Ccirc;": "\u0108", + "Cconint;": "\u2230", + "Cdot;": "\u010a", + "Cedilla;": "\xb8", + "CenterDot;": "\xb7", + "Cfr;": "\u212d", + "Chi;": "\u03a7", + "CircleDot;": "\u2299", + "CircleMinus;": "\u2296", + "CirclePlus;": "\u2295", + "CircleTimes;": "\u2297", + "ClockwiseContourIntegral;": "\u2232", + "CloseCurlyDoubleQuote;": "\u201d", + "CloseCurlyQuote;": "\u2019", + "Colon;": "\u2237", + "Colone;": "\u2a74", + "Congruent;": "\u2261", + "Conint;": "\u222f", + "ContourIntegral;": "\u222e", + "Copf;": "\u2102", + "Coproduct;": "\u2210", + "CounterClockwiseContourIntegral;": "\u2233", + "Cross;": "\u2a2f", + "Cscr;": "\U0001d49e", + "Cup;": "\u22d3", + "CupCap;": "\u224d", + "DD;": "\u2145", + "DDotrahd;": "\u2911", + "DJcy;": "\u0402", + "DScy;": "\u0405", + "DZcy;": "\u040f", + "Dagger;": "\u2021", + "Darr;": "\u21a1", + "Dashv;": "\u2ae4", + "Dcaron;": "\u010e", + "Dcy;": "\u0414", + "Del;": "\u2207", + "Delta;": "\u0394", + "Dfr;": "\U0001d507", + "DiacriticalAcute;": "\xb4", + "DiacriticalDot;": "\u02d9", + "DiacriticalDoubleAcute;": "\u02dd", + "DiacriticalGrave;": "`", + "DiacriticalTilde;": "\u02dc", + "Diamond;": "\u22c4", + "DifferentialD;": "\u2146", + "Dopf;": "\U0001d53b", + "Dot;": "\xa8", + "DotDot;": "\u20dc", + "DotEqual;": "\u2250", + "DoubleContourIntegral;": "\u222f", + "DoubleDot;": "\xa8", + "DoubleDownArrow;": "\u21d3", + "DoubleLeftArrow;": "\u21d0", + "DoubleLeftRightArrow;": "\u21d4", + "DoubleLeftTee;": "\u2ae4", + "DoubleLongLeftArrow;": "\u27f8", + "DoubleLongLeftRightArrow;": "\u27fa", + "DoubleLongRightArrow;": "\u27f9", + "DoubleRightArrow;": "\u21d2", + "DoubleRightTee;": "\u22a8", + "DoubleUpArrow;": "\u21d1", + "DoubleUpDownArrow;": "\u21d5", + "DoubleVerticalBar;": "\u2225", + "DownArrow;": "\u2193", + "DownArrowBar;": "\u2913", + "DownArrowUpArrow;": "\u21f5", + "DownBreve;": "\u0311", + "DownLeftRightVector;": "\u2950", + "DownLeftTeeVector;": "\u295e", + "DownLeftVector;": "\u21bd", + "DownLeftVectorBar;": "\u2956", + "DownRightTeeVector;": "\u295f", + "DownRightVector;": "\u21c1", + "DownRightVectorBar;": "\u2957", + "DownTee;": "\u22a4", + "DownTeeArrow;": "\u21a7", + "Downarrow;": "\u21d3", + "Dscr;": "\U0001d49f", + "Dstrok;": "\u0110", + "ENG;": "\u014a", + "ETH": "\xd0", + "ETH;": "\xd0", + "Eacute": "\xc9", + "Eacute;": "\xc9", + "Ecaron;": "\u011a", + "Ecirc": "\xca", + "Ecirc;": "\xca", + "Ecy;": "\u042d", + "Edot;": "\u0116", + "Efr;": "\U0001d508", + "Egrave": "\xc8", + "Egrave;": "\xc8", + "Element;": "\u2208", + "Emacr;": "\u0112", + "EmptySmallSquare;": "\u25fb", + "EmptyVerySmallSquare;": "\u25ab", + "Eogon;": "\u0118", + "Eopf;": "\U0001d53c", + "Epsilon;": "\u0395", + "Equal;": "\u2a75", + "EqualTilde;": "\u2242", + "Equilibrium;": "\u21cc", + "Escr;": "\u2130", + "Esim;": "\u2a73", + "Eta;": "\u0397", + "Euml": "\xcb", + "Euml;": "\xcb", + "Exists;": "\u2203", + "ExponentialE;": "\u2147", + "Fcy;": "\u0424", + "Ffr;": "\U0001d509", + "FilledSmallSquare;": "\u25fc", + "FilledVerySmallSquare;": "\u25aa", + "Fopf;": "\U0001d53d", + "ForAll;": "\u2200", + "Fouriertrf;": "\u2131", + "Fscr;": "\u2131", + "GJcy;": "\u0403", + "GT": ">", + "GT;": ">", + "Gamma;": "\u0393", + "Gammad;": "\u03dc", + "Gbreve;": "\u011e", + "Gcedil;": "\u0122", + "Gcirc;": "\u011c", + "Gcy;": "\u0413", + "Gdot;": "\u0120", + "Gfr;": "\U0001d50a", + "Gg;": "\u22d9", + "Gopf;": "\U0001d53e", + "GreaterEqual;": "\u2265", + "GreaterEqualLess;": "\u22db", + "GreaterFullEqual;": "\u2267", + "GreaterGreater;": "\u2aa2", + "GreaterLess;": "\u2277", + "GreaterSlantEqual;": "\u2a7e", + "GreaterTilde;": "\u2273", + "Gscr;": "\U0001d4a2", + "Gt;": "\u226b", + "HARDcy;": "\u042a", + "Hacek;": "\u02c7", + "Hat;": "^", + "Hcirc;": "\u0124", + "Hfr;": "\u210c", + "HilbertSpace;": "\u210b", + "Hopf;": "\u210d", + "HorizontalLine;": "\u2500", + "Hscr;": "\u210b", + "Hstrok;": "\u0126", + "HumpDownHump;": "\u224e", + "HumpEqual;": "\u224f", + "IEcy;": "\u0415", + "IJlig;": "\u0132", + "IOcy;": "\u0401", + "Iacute": "\xcd", + "Iacute;": "\xcd", + "Icirc": "\xce", + "Icirc;": "\xce", + "Icy;": "\u0418", + "Idot;": "\u0130", + "Ifr;": "\u2111", + "Igrave": "\xcc", + "Igrave;": "\xcc", + "Im;": "\u2111", + "Imacr;": "\u012a", + "ImaginaryI;": "\u2148", + "Implies;": "\u21d2", + "Int;": "\u222c", + "Integral;": "\u222b", + "Intersection;": "\u22c2", + "InvisibleComma;": "\u2063", + "InvisibleTimes;": "\u2062", + "Iogon;": "\u012e", + "Iopf;": "\U0001d540", + "Iota;": "\u0399", + "Iscr;": "\u2110", + "Itilde;": "\u0128", + "Iukcy;": "\u0406", + "Iuml": "\xcf", + "Iuml;": "\xcf", + "Jcirc;": "\u0134", + "Jcy;": "\u0419", + "Jfr;": "\U0001d50d", + "Jopf;": "\U0001d541", + "Jscr;": "\U0001d4a5", + "Jsercy;": "\u0408", + "Jukcy;": "\u0404", + "KHcy;": "\u0425", + "KJcy;": "\u040c", + "Kappa;": "\u039a", + "Kcedil;": "\u0136", + "Kcy;": "\u041a", + "Kfr;": "\U0001d50e", + "Kopf;": "\U0001d542", + "Kscr;": "\U0001d4a6", + "LJcy;": "\u0409", + "LT": "<", + "LT;": "<", + "Lacute;": "\u0139", + "Lambda;": "\u039b", + "Lang;": "\u27ea", + "Laplacetrf;": "\u2112", + "Larr;": "\u219e", + "Lcaron;": "\u013d", + "Lcedil;": "\u013b", + "Lcy;": "\u041b", + "LeftAngleBracket;": "\u27e8", + "LeftArrow;": "\u2190", + "LeftArrowBar;": "\u21e4", + "LeftArrowRightArrow;": "\u21c6", + "LeftCeiling;": "\u2308", + "LeftDoubleBracket;": "\u27e6", + "LeftDownTeeVector;": "\u2961", + "LeftDownVector;": "\u21c3", + "LeftDownVectorBar;": "\u2959", + "LeftFloor;": "\u230a", + "LeftRightArrow;": "\u2194", + "LeftRightVector;": "\u294e", + "LeftTee;": "\u22a3", + "LeftTeeArrow;": "\u21a4", + "LeftTeeVector;": "\u295a", + "LeftTriangle;": "\u22b2", + "LeftTriangleBar;": "\u29cf", + "LeftTriangleEqual;": "\u22b4", + "LeftUpDownVector;": "\u2951", + "LeftUpTeeVector;": "\u2960", + "LeftUpVector;": "\u21bf", + "LeftUpVectorBar;": "\u2958", + "LeftVector;": "\u21bc", + "LeftVectorBar;": "\u2952", + "Leftarrow;": "\u21d0", + "Leftrightarrow;": "\u21d4", + "LessEqualGreater;": "\u22da", + "LessFullEqual;": "\u2266", + "LessGreater;": "\u2276", + "LessLess;": "\u2aa1", + "LessSlantEqual;": "\u2a7d", + "LessTilde;": "\u2272", + "Lfr;": "\U0001d50f", + "Ll;": "\u22d8", + "Lleftarrow;": "\u21da", + "Lmidot;": "\u013f", + "LongLeftArrow;": "\u27f5", + "LongLeftRightArrow;": "\u27f7", + "LongRightArrow;": "\u27f6", + "Longleftarrow;": "\u27f8", + "Longleftrightarrow;": "\u27fa", + "Longrightarrow;": "\u27f9", + "Lopf;": "\U0001d543", + "LowerLeftArrow;": "\u2199", + "LowerRightArrow;": "\u2198", + "Lscr;": "\u2112", + "Lsh;": "\u21b0", + "Lstrok;": "\u0141", + "Lt;": "\u226a", + "Map;": "\u2905", + "Mcy;": "\u041c", + "MediumSpace;": "\u205f", + "Mellintrf;": "\u2133", + "Mfr;": "\U0001d510", + "MinusPlus;": "\u2213", + "Mopf;": "\U0001d544", + "Mscr;": "\u2133", + "Mu;": "\u039c", + "NJcy;": "\u040a", + "Nacute;": "\u0143", + "Ncaron;": "\u0147", + "Ncedil;": "\u0145", + "Ncy;": "\u041d", + "NegativeMediumSpace;": "\u200b", + "NegativeThickSpace;": "\u200b", + "NegativeThinSpace;": "\u200b", + "NegativeVeryThinSpace;": "\u200b", + "NestedGreaterGreater;": "\u226b", + "NestedLessLess;": "\u226a", + "NewLine;": "\n", + "Nfr;": "\U0001d511", + "NoBreak;": "\u2060", + "NonBreakingSpace;": "\xa0", + "Nopf;": "\u2115", + "Not;": "\u2aec", + "NotCongruent;": "\u2262", + "NotCupCap;": "\u226d", + "NotDoubleVerticalBar;": "\u2226", + "NotElement;": "\u2209", + "NotEqual;": "\u2260", + "NotEqualTilde;": "\u2242\u0338", + "NotExists;": "\u2204", + "NotGreater;": "\u226f", + "NotGreaterEqual;": "\u2271", + "NotGreaterFullEqual;": "\u2267\u0338", + "NotGreaterGreater;": "\u226b\u0338", + "NotGreaterLess;": "\u2279", + "NotGreaterSlantEqual;": "\u2a7e\u0338", + "NotGreaterTilde;": "\u2275", + "NotHumpDownHump;": "\u224e\u0338", + "NotHumpEqual;": "\u224f\u0338", + "NotLeftTriangle;": "\u22ea", + "NotLeftTriangleBar;": "\u29cf\u0338", + "NotLeftTriangleEqual;": "\u22ec", + "NotLess;": "\u226e", + "NotLessEqual;": "\u2270", + "NotLessGreater;": "\u2278", + "NotLessLess;": "\u226a\u0338", + "NotLessSlantEqual;": "\u2a7d\u0338", + "NotLessTilde;": "\u2274", + "NotNestedGreaterGreater;": "\u2aa2\u0338", + "NotNestedLessLess;": "\u2aa1\u0338", + "NotPrecedes;": "\u2280", + "NotPrecedesEqual;": "\u2aaf\u0338", + "NotPrecedesSlantEqual;": "\u22e0", + "NotReverseElement;": "\u220c", + "NotRightTriangle;": "\u22eb", + "NotRightTriangleBar;": "\u29d0\u0338", + "NotRightTriangleEqual;": "\u22ed", + "NotSquareSubset;": "\u228f\u0338", + "NotSquareSubsetEqual;": "\u22e2", + "NotSquareSuperset;": "\u2290\u0338", + "NotSquareSupersetEqual;": "\u22e3", + "NotSubset;": "\u2282\u20d2", + "NotSubsetEqual;": "\u2288", + "NotSucceeds;": "\u2281", + "NotSucceedsEqual;": "\u2ab0\u0338", + "NotSucceedsSlantEqual;": "\u22e1", + "NotSucceedsTilde;": "\u227f\u0338", + "NotSuperset;": "\u2283\u20d2", + "NotSupersetEqual;": "\u2289", + "NotTilde;": "\u2241", + "NotTildeEqual;": "\u2244", + "NotTildeFullEqual;": "\u2247", + "NotTildeTilde;": "\u2249", + "NotVerticalBar;": "\u2224", + "Nscr;": "\U0001d4a9", + "Ntilde": "\xd1", + "Ntilde;": "\xd1", + "Nu;": "\u039d", + "OElig;": "\u0152", + "Oacute": "\xd3", + "Oacute;": "\xd3", + "Ocirc": "\xd4", + "Ocirc;": "\xd4", + "Ocy;": "\u041e", + "Odblac;": "\u0150", + "Ofr;": "\U0001d512", + "Ograve": "\xd2", + "Ograve;": "\xd2", + "Omacr;": "\u014c", + "Omega;": "\u03a9", + "Omicron;": "\u039f", + "Oopf;": "\U0001d546", + "OpenCurlyDoubleQuote;": "\u201c", + "OpenCurlyQuote;": "\u2018", + "Or;": "\u2a54", + "Oscr;": "\U0001d4aa", + "Oslash": "\xd8", + "Oslash;": "\xd8", + "Otilde": "\xd5", + "Otilde;": "\xd5", + "Otimes;": "\u2a37", + "Ouml": "\xd6", + "Ouml;": "\xd6", + "OverBar;": "\u203e", + "OverBrace;": "\u23de", + "OverBracket;": "\u23b4", + "OverParenthesis;": "\u23dc", + "PartialD;": "\u2202", + "Pcy;": "\u041f", + "Pfr;": "\U0001d513", + "Phi;": "\u03a6", + "Pi;": "\u03a0", + "PlusMinus;": "\xb1", + "Poincareplane;": "\u210c", + "Popf;": "\u2119", + "Pr;": "\u2abb", + "Precedes;": "\u227a", + "PrecedesEqual;": "\u2aaf", + "PrecedesSlantEqual;": "\u227c", + "PrecedesTilde;": "\u227e", + "Prime;": "\u2033", + "Product;": "\u220f", + "Proportion;": "\u2237", + "Proportional;": "\u221d", + "Pscr;": "\U0001d4ab", + "Psi;": "\u03a8", + "QUOT": "\"", + "QUOT;": "\"", + "Qfr;": "\U0001d514", + "Qopf;": "\u211a", + "Qscr;": "\U0001d4ac", + "RBarr;": "\u2910", + "REG": "\xae", + "REG;": "\xae", + "Racute;": "\u0154", + "Rang;": "\u27eb", + "Rarr;": "\u21a0", + "Rarrtl;": "\u2916", + "Rcaron;": "\u0158", + "Rcedil;": "\u0156", + "Rcy;": "\u0420", + "Re;": "\u211c", + "ReverseElement;": "\u220b", + "ReverseEquilibrium;": "\u21cb", + "ReverseUpEquilibrium;": "\u296f", + "Rfr;": "\u211c", + "Rho;": "\u03a1", + "RightAngleBracket;": "\u27e9", + "RightArrow;": "\u2192", + "RightArrowBar;": "\u21e5", + "RightArrowLeftArrow;": "\u21c4", + "RightCeiling;": "\u2309", + "RightDoubleBracket;": "\u27e7", + "RightDownTeeVector;": "\u295d", + "RightDownVector;": "\u21c2", + "RightDownVectorBar;": "\u2955", + "RightFloor;": "\u230b", + "RightTee;": "\u22a2", + "RightTeeArrow;": "\u21a6", + "RightTeeVector;": "\u295b", + "RightTriangle;": "\u22b3", + "RightTriangleBar;": "\u29d0", + "RightTriangleEqual;": "\u22b5", + "RightUpDownVector;": "\u294f", + "RightUpTeeVector;": "\u295c", + "RightUpVector;": "\u21be", + "RightUpVectorBar;": "\u2954", + "RightVector;": "\u21c0", + "RightVectorBar;": "\u2953", + "Rightarrow;": "\u21d2", + "Ropf;": "\u211d", + "RoundImplies;": "\u2970", + "Rrightarrow;": "\u21db", + "Rscr;": "\u211b", + "Rsh;": "\u21b1", + "RuleDelayed;": "\u29f4", + "SHCHcy;": "\u0429", + "SHcy;": "\u0428", + "SOFTcy;": "\u042c", + "Sacute;": "\u015a", + "Sc;": "\u2abc", + "Scaron;": "\u0160", + "Scedil;": "\u015e", + "Scirc;": "\u015c", + "Scy;": "\u0421", + "Sfr;": "\U0001d516", + "ShortDownArrow;": "\u2193", + "ShortLeftArrow;": "\u2190", + "ShortRightArrow;": "\u2192", + "ShortUpArrow;": "\u2191", + "Sigma;": "\u03a3", + "SmallCircle;": "\u2218", + "Sopf;": "\U0001d54a", + "Sqrt;": "\u221a", + "Square;": "\u25a1", + "SquareIntersection;": "\u2293", + "SquareSubset;": "\u228f", + "SquareSubsetEqual;": "\u2291", + "SquareSuperset;": "\u2290", + "SquareSupersetEqual;": "\u2292", + "SquareUnion;": "\u2294", + "Sscr;": "\U0001d4ae", + "Star;": "\u22c6", + "Sub;": "\u22d0", + "Subset;": "\u22d0", + "SubsetEqual;": "\u2286", + "Succeeds;": "\u227b", + "SucceedsEqual;": "\u2ab0", + "SucceedsSlantEqual;": "\u227d", + "SucceedsTilde;": "\u227f", + "SuchThat;": "\u220b", + "Sum;": "\u2211", + "Sup;": "\u22d1", + "Superset;": "\u2283", + "SupersetEqual;": "\u2287", + "Supset;": "\u22d1", + "THORN": "\xde", + "THORN;": "\xde", + "TRADE;": "\u2122", + "TSHcy;": "\u040b", + "TScy;": "\u0426", + "Tab;": "\t", + "Tau;": "\u03a4", + "Tcaron;": "\u0164", + "Tcedil;": "\u0162", + "Tcy;": "\u0422", + "Tfr;": "\U0001d517", + "Therefore;": "\u2234", + "Theta;": "\u0398", + "ThickSpace;": "\u205f\u200a", + "ThinSpace;": "\u2009", + "Tilde;": "\u223c", + "TildeEqual;": "\u2243", + "TildeFullEqual;": "\u2245", + "TildeTilde;": "\u2248", + "Topf;": "\U0001d54b", + "TripleDot;": "\u20db", + "Tscr;": "\U0001d4af", + "Tstrok;": "\u0166", + "Uacute": "\xda", + "Uacute;": "\xda", + "Uarr;": "\u219f", + "Uarrocir;": "\u2949", + "Ubrcy;": "\u040e", + "Ubreve;": "\u016c", + "Ucirc": "\xdb", + "Ucirc;": "\xdb", + "Ucy;": "\u0423", + "Udblac;": "\u0170", + "Ufr;": "\U0001d518", + "Ugrave": "\xd9", + "Ugrave;": "\xd9", + "Umacr;": "\u016a", + "UnderBar;": "_", + "UnderBrace;": "\u23df", + "UnderBracket;": "\u23b5", + "UnderParenthesis;": "\u23dd", + "Union;": "\u22c3", + "UnionPlus;": "\u228e", + "Uogon;": "\u0172", + "Uopf;": "\U0001d54c", + "UpArrow;": "\u2191", + "UpArrowBar;": "\u2912", + "UpArrowDownArrow;": "\u21c5", + "UpDownArrow;": "\u2195", + "UpEquilibrium;": "\u296e", + "UpTee;": "\u22a5", + "UpTeeArrow;": "\u21a5", + "Uparrow;": "\u21d1", + "Updownarrow;": "\u21d5", + "UpperLeftArrow;": "\u2196", + "UpperRightArrow;": "\u2197", + "Upsi;": "\u03d2", + "Upsilon;": "\u03a5", + "Uring;": "\u016e", + "Uscr;": "\U0001d4b0", + "Utilde;": "\u0168", + "Uuml": "\xdc", + "Uuml;": "\xdc", + "VDash;": "\u22ab", + "Vbar;": "\u2aeb", + "Vcy;": "\u0412", + "Vdash;": "\u22a9", + "Vdashl;": "\u2ae6", + "Vee;": "\u22c1", + "Verbar;": "\u2016", + "Vert;": "\u2016", + "VerticalBar;": "\u2223", + "VerticalLine;": "|", + "VerticalSeparator;": "\u2758", + "VerticalTilde;": "\u2240", + "VeryThinSpace;": "\u200a", + "Vfr;": "\U0001d519", + "Vopf;": "\U0001d54d", + "Vscr;": "\U0001d4b1", + "Vvdash;": "\u22aa", + "Wcirc;": "\u0174", + "Wedge;": "\u22c0", + "Wfr;": "\U0001d51a", + "Wopf;": "\U0001d54e", + "Wscr;": "\U0001d4b2", + "Xfr;": "\U0001d51b", + "Xi;": "\u039e", + "Xopf;": "\U0001d54f", + "Xscr;": "\U0001d4b3", + "YAcy;": "\u042f", + "YIcy;": "\u0407", + "YUcy;": "\u042e", + "Yacute": "\xdd", + "Yacute;": "\xdd", + "Ycirc;": "\u0176", + "Ycy;": "\u042b", + "Yfr;": "\U0001d51c", + "Yopf;": "\U0001d550", + "Yscr;": "\U0001d4b4", + "Yuml;": "\u0178", + "ZHcy;": "\u0416", + "Zacute;": "\u0179", + "Zcaron;": "\u017d", + "Zcy;": "\u0417", + "Zdot;": "\u017b", + "ZeroWidthSpace;": "\u200b", + "Zeta;": "\u0396", + "Zfr;": "\u2128", + "Zopf;": "\u2124", + "Zscr;": "\U0001d4b5", + "aacute": "\xe1", + "aacute;": "\xe1", + "abreve;": "\u0103", + "ac;": "\u223e", + "acE;": "\u223e\u0333", + "acd;": "\u223f", + "acirc": "\xe2", + "acirc;": "\xe2", + "acute": "\xb4", + "acute;": "\xb4", + "acy;": "\u0430", + "aelig": "\xe6", + "aelig;": "\xe6", + "af;": "\u2061", + "afr;": "\U0001d51e", + "agrave": "\xe0", + "agrave;": "\xe0", + "alefsym;": "\u2135", + "aleph;": "\u2135", + "alpha;": "\u03b1", + "amacr;": "\u0101", + "amalg;": "\u2a3f", + "amp": "&", + "amp;": "&", + "and;": "\u2227", + "andand;": "\u2a55", + "andd;": "\u2a5c", + "andslope;": "\u2a58", + "andv;": "\u2a5a", + "ang;": "\u2220", + "ange;": "\u29a4", + "angle;": "\u2220", + "angmsd;": "\u2221", + "angmsdaa;": "\u29a8", + "angmsdab;": "\u29a9", + "angmsdac;": "\u29aa", + "angmsdad;": "\u29ab", + "angmsdae;": "\u29ac", + "angmsdaf;": "\u29ad", + "angmsdag;": "\u29ae", + "angmsdah;": "\u29af", + "angrt;": "\u221f", + "angrtvb;": "\u22be", + "angrtvbd;": "\u299d", + "angsph;": "\u2222", + "angst;": "\xc5", + "angzarr;": "\u237c", + "aogon;": "\u0105", + "aopf;": "\U0001d552", + "ap;": "\u2248", + "apE;": "\u2a70", + "apacir;": "\u2a6f", + "ape;": "\u224a", + "apid;": "\u224b", + "apos;": "'", + "approx;": "\u2248", + "approxeq;": "\u224a", + "aring": "\xe5", + "aring;": "\xe5", + "ascr;": "\U0001d4b6", + "ast;": "*", + "asymp;": "\u2248", + "asympeq;": "\u224d", + "atilde": "\xe3", + "atilde;": "\xe3", + "auml": "\xe4", + "auml;": "\xe4", + "awconint;": "\u2233", + "awint;": "\u2a11", + "bNot;": "\u2aed", + "backcong;": "\u224c", + "backepsilon;": "\u03f6", + "backprime;": "\u2035", + "backsim;": "\u223d", + "backsimeq;": "\u22cd", + "barvee;": "\u22bd", + "barwed;": "\u2305", + "barwedge;": "\u2305", + "bbrk;": "\u23b5", + "bbrktbrk;": "\u23b6", + "bcong;": "\u224c", + "bcy;": "\u0431", + "bdquo;": "\u201e", + "becaus;": "\u2235", + "because;": "\u2235", + "bemptyv;": "\u29b0", + "bepsi;": "\u03f6", + "bernou;": "\u212c", + "beta;": "\u03b2", + "beth;": "\u2136", + "between;": "\u226c", + "bfr;": "\U0001d51f", + "bigcap;": "\u22c2", + "bigcirc;": "\u25ef", + "bigcup;": "\u22c3", + "bigodot;": "\u2a00", + "bigoplus;": "\u2a01", + "bigotimes;": "\u2a02", + "bigsqcup;": "\u2a06", + "bigstar;": "\u2605", + "bigtriangledown;": "\u25bd", + "bigtriangleup;": "\u25b3", + "biguplus;": "\u2a04", + "bigvee;": "\u22c1", + "bigwedge;": "\u22c0", + "bkarow;": "\u290d", + "blacklozenge;": "\u29eb", + "blacksquare;": "\u25aa", + "blacktriangle;": "\u25b4", + "blacktriangledown;": "\u25be", + "blacktriangleleft;": "\u25c2", + "blacktriangleright;": "\u25b8", + "blank;": "\u2423", + "blk12;": "\u2592", + "blk14;": "\u2591", + "blk34;": "\u2593", + "block;": "\u2588", + "bne;": "=\u20e5", + "bnequiv;": "\u2261\u20e5", + "bnot;": "\u2310", + "bopf;": "\U0001d553", + "bot;": "\u22a5", + "bottom;": "\u22a5", + "bowtie;": "\u22c8", + "boxDL;": "\u2557", + "boxDR;": "\u2554", + "boxDl;": "\u2556", + "boxDr;": "\u2553", + "boxH;": "\u2550", + "boxHD;": "\u2566", + "boxHU;": "\u2569", + "boxHd;": "\u2564", + "boxHu;": "\u2567", + "boxUL;": "\u255d", + "boxUR;": "\u255a", + "boxUl;": "\u255c", + "boxUr;": "\u2559", + "boxV;": "\u2551", + "boxVH;": "\u256c", + "boxVL;": "\u2563", + "boxVR;": "\u2560", + "boxVh;": "\u256b", + "boxVl;": "\u2562", + "boxVr;": "\u255f", + "boxbox;": "\u29c9", + "boxdL;": "\u2555", + "boxdR;": "\u2552", + "boxdl;": "\u2510", + "boxdr;": "\u250c", + "boxh;": "\u2500", + "boxhD;": "\u2565", + "boxhU;": "\u2568", + "boxhd;": "\u252c", + "boxhu;": "\u2534", + "boxminus;": "\u229f", + "boxplus;": "\u229e", + "boxtimes;": "\u22a0", + "boxuL;": "\u255b", + "boxuR;": "\u2558", + "boxul;": "\u2518", + "boxur;": "\u2514", + "boxv;": "\u2502", + "boxvH;": "\u256a", + "boxvL;": "\u2561", + "boxvR;": "\u255e", + "boxvh;": "\u253c", + "boxvl;": "\u2524", + "boxvr;": "\u251c", + "bprime;": "\u2035", + "breve;": "\u02d8", + "brvbar": "\xa6", + "brvbar;": "\xa6", + "bscr;": "\U0001d4b7", + "bsemi;": "\u204f", + "bsim;": "\u223d", + "bsime;": "\u22cd", + "bsol;": "\\", + "bsolb;": "\u29c5", + "bsolhsub;": "\u27c8", + "bull;": "\u2022", + "bullet;": "\u2022", + "bump;": "\u224e", + "bumpE;": "\u2aae", + "bumpe;": "\u224f", + "bumpeq;": "\u224f", + "cacute;": "\u0107", + "cap;": "\u2229", + "capand;": "\u2a44", + "capbrcup;": "\u2a49", + "capcap;": "\u2a4b", + "capcup;": "\u2a47", + "capdot;": "\u2a40", + "caps;": "\u2229\ufe00", + "caret;": "\u2041", + "caron;": "\u02c7", + "ccaps;": "\u2a4d", + "ccaron;": "\u010d", + "ccedil": "\xe7", + "ccedil;": "\xe7", + "ccirc;": "\u0109", + "ccups;": "\u2a4c", + "ccupssm;": "\u2a50", + "cdot;": "\u010b", + "cedil": "\xb8", + "cedil;": "\xb8", + "cemptyv;": "\u29b2", + "cent": "\xa2", + "cent;": "\xa2", + "centerdot;": "\xb7", + "cfr;": "\U0001d520", + "chcy;": "\u0447", + "check;": "\u2713", + "checkmark;": "\u2713", + "chi;": "\u03c7", + "cir;": "\u25cb", + "cirE;": "\u29c3", + "circ;": "\u02c6", + "circeq;": "\u2257", + "circlearrowleft;": "\u21ba", + "circlearrowright;": "\u21bb", + "circledR;": "\xae", + "circledS;": "\u24c8", + "circledast;": "\u229b", + "circledcirc;": "\u229a", + "circleddash;": "\u229d", + "cire;": "\u2257", + "cirfnint;": "\u2a10", + "cirmid;": "\u2aef", + "cirscir;": "\u29c2", + "clubs;": "\u2663", + "clubsuit;": "\u2663", + "colon;": ":", + "colone;": "\u2254", + "coloneq;": "\u2254", + "comma;": ",", + "commat;": "@", + "comp;": "\u2201", + "compfn;": "\u2218", + "complement;": "\u2201", + "complexes;": "\u2102", + "cong;": "\u2245", + "congdot;": "\u2a6d", + "conint;": "\u222e", + "copf;": "\U0001d554", + "coprod;": "\u2210", + "copy": "\xa9", + "copy;": "\xa9", + "copysr;": "\u2117", + "crarr;": "\u21b5", + "cross;": "\u2717", + "cscr;": "\U0001d4b8", + "csub;": "\u2acf", + "csube;": "\u2ad1", + "csup;": "\u2ad0", + "csupe;": "\u2ad2", + "ctdot;": "\u22ef", + "cudarrl;": "\u2938", + "cudarrr;": "\u2935", + "cuepr;": "\u22de", + "cuesc;": "\u22df", + "cularr;": "\u21b6", + "cularrp;": "\u293d", + "cup;": "\u222a", + "cupbrcap;": "\u2a48", + "cupcap;": "\u2a46", + "cupcup;": "\u2a4a", + "cupdot;": "\u228d", + "cupor;": "\u2a45", + "cups;": "\u222a\ufe00", + "curarr;": "\u21b7", + "curarrm;": "\u293c", + "curlyeqprec;": "\u22de", + "curlyeqsucc;": "\u22df", + "curlyvee;": "\u22ce", + "curlywedge;": "\u22cf", + "curren": "\xa4", + "curren;": "\xa4", + "curvearrowleft;": "\u21b6", + "curvearrowright;": "\u21b7", + "cuvee;": "\u22ce", + "cuwed;": "\u22cf", + "cwconint;": "\u2232", + "cwint;": "\u2231", + "cylcty;": "\u232d", + "dArr;": "\u21d3", + "dHar;": "\u2965", + "dagger;": "\u2020", + "daleth;": "\u2138", + "darr;": "\u2193", + "dash;": "\u2010", + "dashv;": "\u22a3", + "dbkarow;": "\u290f", + "dblac;": "\u02dd", + "dcaron;": "\u010f", + "dcy;": "\u0434", + "dd;": "\u2146", + "ddagger;": "\u2021", + "ddarr;": "\u21ca", + "ddotseq;": "\u2a77", + "deg": "\xb0", + "deg;": "\xb0", + "delta;": "\u03b4", + "demptyv;": "\u29b1", + "dfisht;": "\u297f", + "dfr;": "\U0001d521", + "dharl;": "\u21c3", + "dharr;": "\u21c2", + "diam;": "\u22c4", + "diamond;": "\u22c4", + "diamondsuit;": "\u2666", + "diams;": "\u2666", + "die;": "\xa8", + "digamma;": "\u03dd", + "disin;": "\u22f2", + "div;": "\xf7", + "divide": "\xf7", + "divide;": "\xf7", + "divideontimes;": "\u22c7", + "divonx;": "\u22c7", + "djcy;": "\u0452", + "dlcorn;": "\u231e", + "dlcrop;": "\u230d", + "dollar;": "$", + "dopf;": "\U0001d555", + "dot;": "\u02d9", + "doteq;": "\u2250", + "doteqdot;": "\u2251", + "dotminus;": "\u2238", + "dotplus;": "\u2214", + "dotsquare;": "\u22a1", + "doublebarwedge;": "\u2306", + "downarrow;": "\u2193", + "downdownarrows;": "\u21ca", + "downharpoonleft;": "\u21c3", + "downharpoonright;": "\u21c2", + "drbkarow;": "\u2910", + "drcorn;": "\u231f", + "drcrop;": "\u230c", + "dscr;": "\U0001d4b9", + "dscy;": "\u0455", + "dsol;": "\u29f6", + "dstrok;": "\u0111", + "dtdot;": "\u22f1", + "dtri;": "\u25bf", + "dtrif;": "\u25be", + "duarr;": "\u21f5", + "duhar;": "\u296f", + "dwangle;": "\u29a6", + "dzcy;": "\u045f", + "dzigrarr;": "\u27ff", + "eDDot;": "\u2a77", + "eDot;": "\u2251", + "eacute": "\xe9", + "eacute;": "\xe9", + "easter;": "\u2a6e", + "ecaron;": "\u011b", + "ecir;": "\u2256", + "ecirc": "\xea", + "ecirc;": "\xea", + "ecolon;": "\u2255", + "ecy;": "\u044d", + "edot;": "\u0117", + "ee;": "\u2147", + "efDot;": "\u2252", + "efr;": "\U0001d522", + "eg;": "\u2a9a", + "egrave": "\xe8", + "egrave;": "\xe8", + "egs;": "\u2a96", + "egsdot;": "\u2a98", + "el;": "\u2a99", + "elinters;": "\u23e7", + "ell;": "\u2113", + "els;": "\u2a95", + "elsdot;": "\u2a97", + "emacr;": "\u0113", + "empty;": "\u2205", + "emptyset;": "\u2205", + "emptyv;": "\u2205", + "emsp13;": "\u2004", + "emsp14;": "\u2005", + "emsp;": "\u2003", + "eng;": "\u014b", + "ensp;": "\u2002", + "eogon;": "\u0119", + "eopf;": "\U0001d556", + "epar;": "\u22d5", + "eparsl;": "\u29e3", + "eplus;": "\u2a71", + "epsi;": "\u03b5", + "epsilon;": "\u03b5", + "epsiv;": "\u03f5", + "eqcirc;": "\u2256", + "eqcolon;": "\u2255", + "eqsim;": "\u2242", + "eqslantgtr;": "\u2a96", + "eqslantless;": "\u2a95", + "equals;": "=", + "equest;": "\u225f", + "equiv;": "\u2261", + "equivDD;": "\u2a78", + "eqvparsl;": "\u29e5", + "erDot;": "\u2253", + "erarr;": "\u2971", + "escr;": "\u212f", + "esdot;": "\u2250", + "esim;": "\u2242", + "eta;": "\u03b7", + "eth": "\xf0", + "eth;": "\xf0", + "euml": "\xeb", + "euml;": "\xeb", + "euro;": "\u20ac", + "excl;": "!", + "exist;": "\u2203", + "expectation;": "\u2130", + "exponentiale;": "\u2147", + "fallingdotseq;": "\u2252", + "fcy;": "\u0444", + "female;": "\u2640", + "ffilig;": "\ufb03", + "fflig;": "\ufb00", + "ffllig;": "\ufb04", + "ffr;": "\U0001d523", + "filig;": "\ufb01", + "fjlig;": "fj", + "flat;": "\u266d", + "fllig;": "\ufb02", + "fltns;": "\u25b1", + "fnof;": "\u0192", + "fopf;": "\U0001d557", + "forall;": "\u2200", + "fork;": "\u22d4", + "forkv;": "\u2ad9", + "fpartint;": "\u2a0d", + "frac12": "\xbd", + "frac12;": "\xbd", + "frac13;": "\u2153", + "frac14": "\xbc", + "frac14;": "\xbc", + "frac15;": "\u2155", + "frac16;": "\u2159", + "frac18;": "\u215b", + "frac23;": "\u2154", + "frac25;": "\u2156", + "frac34": "\xbe", + "frac34;": "\xbe", + "frac35;": "\u2157", + "frac38;": "\u215c", + "frac45;": "\u2158", + "frac56;": "\u215a", + "frac58;": "\u215d", + "frac78;": "\u215e", + "frasl;": "\u2044", + "frown;": "\u2322", + "fscr;": "\U0001d4bb", + "gE;": "\u2267", + "gEl;": "\u2a8c", + "gacute;": "\u01f5", + "gamma;": "\u03b3", + "gammad;": "\u03dd", + "gap;": "\u2a86", + "gbreve;": "\u011f", + "gcirc;": "\u011d", + "gcy;": "\u0433", + "gdot;": "\u0121", + "ge;": "\u2265", + "gel;": "\u22db", + "geq;": "\u2265", + "geqq;": "\u2267", + "geqslant;": "\u2a7e", + "ges;": "\u2a7e", + "gescc;": "\u2aa9", + "gesdot;": "\u2a80", + "gesdoto;": "\u2a82", + "gesdotol;": "\u2a84", + "gesl;": "\u22db\ufe00", + "gesles;": "\u2a94", + "gfr;": "\U0001d524", + "gg;": "\u226b", + "ggg;": "\u22d9", + "gimel;": "\u2137", + "gjcy;": "\u0453", + "gl;": "\u2277", + "glE;": "\u2a92", + "gla;": "\u2aa5", + "glj;": "\u2aa4", + "gnE;": "\u2269", + "gnap;": "\u2a8a", + "gnapprox;": "\u2a8a", + "gne;": "\u2a88", + "gneq;": "\u2a88", + "gneqq;": "\u2269", + "gnsim;": "\u22e7", + "gopf;": "\U0001d558", + "grave;": "`", + "gscr;": "\u210a", + "gsim;": "\u2273", + "gsime;": "\u2a8e", + "gsiml;": "\u2a90", + "gt": ">", + "gt;": ">", + "gtcc;": "\u2aa7", + "gtcir;": "\u2a7a", + "gtdot;": "\u22d7", + "gtlPar;": "\u2995", + "gtquest;": "\u2a7c", + "gtrapprox;": "\u2a86", + "gtrarr;": "\u2978", + "gtrdot;": "\u22d7", + "gtreqless;": "\u22db", + "gtreqqless;": "\u2a8c", + "gtrless;": "\u2277", + "gtrsim;": "\u2273", + "gvertneqq;": "\u2269\ufe00", + "gvnE;": "\u2269\ufe00", + "hArr;": "\u21d4", + "hairsp;": "\u200a", + "half;": "\xbd", + "hamilt;": "\u210b", + "hardcy;": "\u044a", + "harr;": "\u2194", + "harrcir;": "\u2948", + "harrw;": "\u21ad", + "hbar;": "\u210f", + "hcirc;": "\u0125", + "hearts;": "\u2665", + "heartsuit;": "\u2665", + "hellip;": "\u2026", + "hercon;": "\u22b9", + "hfr;": "\U0001d525", + "hksearow;": "\u2925", + "hkswarow;": "\u2926", + "hoarr;": "\u21ff", + "homtht;": "\u223b", + "hookleftarrow;": "\u21a9", + "hookrightarrow;": "\u21aa", + "hopf;": "\U0001d559", + "horbar;": "\u2015", + "hscr;": "\U0001d4bd", + "hslash;": "\u210f", + "hstrok;": "\u0127", + "hybull;": "\u2043", + "hyphen;": "\u2010", + "iacute": "\xed", + "iacute;": "\xed", + "ic;": "\u2063", + "icirc": "\xee", + "icirc;": "\xee", + "icy;": "\u0438", + "iecy;": "\u0435", + "iexcl": "\xa1", + "iexcl;": "\xa1", + "iff;": "\u21d4", + "ifr;": "\U0001d526", + "igrave": "\xec", + "igrave;": "\xec", + "ii;": "\u2148", + "iiiint;": "\u2a0c", + "iiint;": "\u222d", + "iinfin;": "\u29dc", + "iiota;": "\u2129", + "ijlig;": "\u0133", + "imacr;": "\u012b", + "image;": "\u2111", + "imagline;": "\u2110", + "imagpart;": "\u2111", + "imath;": "\u0131", + "imof;": "\u22b7", + "imped;": "\u01b5", + "in;": "\u2208", + "incare;": "\u2105", + "infin;": "\u221e", + "infintie;": "\u29dd", + "inodot;": "\u0131", + "int;": "\u222b", + "intcal;": "\u22ba", + "integers;": "\u2124", + "intercal;": "\u22ba", + "intlarhk;": "\u2a17", + "intprod;": "\u2a3c", + "iocy;": "\u0451", + "iogon;": "\u012f", + "iopf;": "\U0001d55a", + "iota;": "\u03b9", + "iprod;": "\u2a3c", + "iquest": "\xbf", + "iquest;": "\xbf", + "iscr;": "\U0001d4be", + "isin;": "\u2208", + "isinE;": "\u22f9", + "isindot;": "\u22f5", + "isins;": "\u22f4", + "isinsv;": "\u22f3", + "isinv;": "\u2208", + "it;": "\u2062", + "itilde;": "\u0129", + "iukcy;": "\u0456", + "iuml": "\xef", + "iuml;": "\xef", + "jcirc;": "\u0135", + "jcy;": "\u0439", + "jfr;": "\U0001d527", + "jmath;": "\u0237", + "jopf;": "\U0001d55b", + "jscr;": "\U0001d4bf", + "jsercy;": "\u0458", + "jukcy;": "\u0454", + "kappa;": "\u03ba", + "kappav;": "\u03f0", + "kcedil;": "\u0137", + "kcy;": "\u043a", + "kfr;": "\U0001d528", + "kgreen;": "\u0138", + "khcy;": "\u0445", + "kjcy;": "\u045c", + "kopf;": "\U0001d55c", + "kscr;": "\U0001d4c0", + "lAarr;": "\u21da", + "lArr;": "\u21d0", + "lAtail;": "\u291b", + "lBarr;": "\u290e", + "lE;": "\u2266", + "lEg;": "\u2a8b", + "lHar;": "\u2962", + "lacute;": "\u013a", + "laemptyv;": "\u29b4", + "lagran;": "\u2112", + "lambda;": "\u03bb", + "lang;": "\u27e8", + "langd;": "\u2991", + "langle;": "\u27e8", + "lap;": "\u2a85", + "laquo": "\xab", + "laquo;": "\xab", + "larr;": "\u2190", + "larrb;": "\u21e4", + "larrbfs;": "\u291f", + "larrfs;": "\u291d", + "larrhk;": "\u21a9", + "larrlp;": "\u21ab", + "larrpl;": "\u2939", + "larrsim;": "\u2973", + "larrtl;": "\u21a2", + "lat;": "\u2aab", + "latail;": "\u2919", + "late;": "\u2aad", + "lates;": "\u2aad\ufe00", + "lbarr;": "\u290c", + "lbbrk;": "\u2772", + "lbrace;": "{", + "lbrack;": "[", + "lbrke;": "\u298b", + "lbrksld;": "\u298f", + "lbrkslu;": "\u298d", + "lcaron;": "\u013e", + "lcedil;": "\u013c", + "lceil;": "\u2308", + "lcub;": "{", + "lcy;": "\u043b", + "ldca;": "\u2936", + "ldquo;": "\u201c", + "ldquor;": "\u201e", + "ldrdhar;": "\u2967", + "ldrushar;": "\u294b", + "ldsh;": "\u21b2", + "le;": "\u2264", + "leftarrow;": "\u2190", + "leftarrowtail;": "\u21a2", + "leftharpoondown;": "\u21bd", + "leftharpoonup;": "\u21bc", + "leftleftarrows;": "\u21c7", + "leftrightarrow;": "\u2194", + "leftrightarrows;": "\u21c6", + "leftrightharpoons;": "\u21cb", + "leftrightsquigarrow;": "\u21ad", + "leftthreetimes;": "\u22cb", + "leg;": "\u22da", + "leq;": "\u2264", + "leqq;": "\u2266", + "leqslant;": "\u2a7d", + "les;": "\u2a7d", + "lescc;": "\u2aa8", + "lesdot;": "\u2a7f", + "lesdoto;": "\u2a81", + "lesdotor;": "\u2a83", + "lesg;": "\u22da\ufe00", + "lesges;": "\u2a93", + "lessapprox;": "\u2a85", + "lessdot;": "\u22d6", + "lesseqgtr;": "\u22da", + "lesseqqgtr;": "\u2a8b", + "lessgtr;": "\u2276", + "lesssim;": "\u2272", + "lfisht;": "\u297c", + "lfloor;": "\u230a", + "lfr;": "\U0001d529", + "lg;": "\u2276", + "lgE;": "\u2a91", + "lhard;": "\u21bd", + "lharu;": "\u21bc", + "lharul;": "\u296a", + "lhblk;": "\u2584", + "ljcy;": "\u0459", + "ll;": "\u226a", + "llarr;": "\u21c7", + "llcorner;": "\u231e", + "llhard;": "\u296b", + "lltri;": "\u25fa", + "lmidot;": "\u0140", + "lmoust;": "\u23b0", + "lmoustache;": "\u23b0", + "lnE;": "\u2268", + "lnap;": "\u2a89", + "lnapprox;": "\u2a89", + "lne;": "\u2a87", + "lneq;": "\u2a87", + "lneqq;": "\u2268", + "lnsim;": "\u22e6", + "loang;": "\u27ec", + "loarr;": "\u21fd", + "lobrk;": "\u27e6", + "longleftarrow;": "\u27f5", + "longleftrightarrow;": "\u27f7", + "longmapsto;": "\u27fc", + "longrightarrow;": "\u27f6", + "looparrowleft;": "\u21ab", + "looparrowright;": "\u21ac", + "lopar;": "\u2985", + "lopf;": "\U0001d55d", + "loplus;": "\u2a2d", + "lotimes;": "\u2a34", + "lowast;": "\u2217", + "lowbar;": "_", + "loz;": "\u25ca", + "lozenge;": "\u25ca", + "lozf;": "\u29eb", + "lpar;": "(", + "lparlt;": "\u2993", + "lrarr;": "\u21c6", + "lrcorner;": "\u231f", + "lrhar;": "\u21cb", + "lrhard;": "\u296d", + "lrm;": "\u200e", + "lrtri;": "\u22bf", + "lsaquo;": "\u2039", + "lscr;": "\U0001d4c1", + "lsh;": "\u21b0", + "lsim;": "\u2272", + "lsime;": "\u2a8d", + "lsimg;": "\u2a8f", + "lsqb;": "[", + "lsquo;": "\u2018", + "lsquor;": "\u201a", + "lstrok;": "\u0142", + "lt": "<", + "lt;": "<", + "ltcc;": "\u2aa6", + "ltcir;": "\u2a79", + "ltdot;": "\u22d6", + "lthree;": "\u22cb", + "ltimes;": "\u22c9", + "ltlarr;": "\u2976", + "ltquest;": "\u2a7b", + "ltrPar;": "\u2996", + "ltri;": "\u25c3", + "ltrie;": "\u22b4", + "ltrif;": "\u25c2", + "lurdshar;": "\u294a", + "luruhar;": "\u2966", + "lvertneqq;": "\u2268\ufe00", + "lvnE;": "\u2268\ufe00", + "mDDot;": "\u223a", + "macr": "\xaf", + "macr;": "\xaf", + "male;": "\u2642", + "malt;": "\u2720", + "maltese;": "\u2720", + "map;": "\u21a6", + "mapsto;": "\u21a6", + "mapstodown;": "\u21a7", + "mapstoleft;": "\u21a4", + "mapstoup;": "\u21a5", + "marker;": "\u25ae", + "mcomma;": "\u2a29", + "mcy;": "\u043c", + "mdash;": "\u2014", + "measuredangle;": "\u2221", + "mfr;": "\U0001d52a", + "mho;": "\u2127", + "micro": "\xb5", + "micro;": "\xb5", + "mid;": "\u2223", + "midast;": "*", + "midcir;": "\u2af0", + "middot": "\xb7", + "middot;": "\xb7", + "minus;": "\u2212", + "minusb;": "\u229f", + "minusd;": "\u2238", + "minusdu;": "\u2a2a", + "mlcp;": "\u2adb", + "mldr;": "\u2026", + "mnplus;": "\u2213", + "models;": "\u22a7", + "mopf;": "\U0001d55e", + "mp;": "\u2213", + "mscr;": "\U0001d4c2", + "mstpos;": "\u223e", + "mu;": "\u03bc", + "multimap;": "\u22b8", + "mumap;": "\u22b8", + "nGg;": "\u22d9\u0338", + "nGt;": "\u226b\u20d2", + "nGtv;": "\u226b\u0338", + "nLeftarrow;": "\u21cd", + "nLeftrightarrow;": "\u21ce", + "nLl;": "\u22d8\u0338", + "nLt;": "\u226a\u20d2", + "nLtv;": "\u226a\u0338", + "nRightarrow;": "\u21cf", + "nVDash;": "\u22af", + "nVdash;": "\u22ae", + "nabla;": "\u2207", + "nacute;": "\u0144", + "nang;": "\u2220\u20d2", + "nap;": "\u2249", + "napE;": "\u2a70\u0338", + "napid;": "\u224b\u0338", + "napos;": "\u0149", + "napprox;": "\u2249", + "natur;": "\u266e", + "natural;": "\u266e", + "naturals;": "\u2115", + "nbsp": "\xa0", + "nbsp;": "\xa0", + "nbump;": "\u224e\u0338", + "nbumpe;": "\u224f\u0338", + "ncap;": "\u2a43", + "ncaron;": "\u0148", + "ncedil;": "\u0146", + "ncong;": "\u2247", + "ncongdot;": "\u2a6d\u0338", + "ncup;": "\u2a42", + "ncy;": "\u043d", + "ndash;": "\u2013", + "ne;": "\u2260", + "neArr;": "\u21d7", + "nearhk;": "\u2924", + "nearr;": "\u2197", + "nearrow;": "\u2197", + "nedot;": "\u2250\u0338", + "nequiv;": "\u2262", + "nesear;": "\u2928", + "nesim;": "\u2242\u0338", + "nexist;": "\u2204", + "nexists;": "\u2204", + "nfr;": "\U0001d52b", + "ngE;": "\u2267\u0338", + "nge;": "\u2271", + "ngeq;": "\u2271", + "ngeqq;": "\u2267\u0338", + "ngeqslant;": "\u2a7e\u0338", + "nges;": "\u2a7e\u0338", + "ngsim;": "\u2275", + "ngt;": "\u226f", + "ngtr;": "\u226f", + "nhArr;": "\u21ce", + "nharr;": "\u21ae", + "nhpar;": "\u2af2", + "ni;": "\u220b", + "nis;": "\u22fc", + "nisd;": "\u22fa", + "niv;": "\u220b", + "njcy;": "\u045a", + "nlArr;": "\u21cd", + "nlE;": "\u2266\u0338", + "nlarr;": "\u219a", + "nldr;": "\u2025", + "nle;": "\u2270", + "nleftarrow;": "\u219a", + "nleftrightarrow;": "\u21ae", + "nleq;": "\u2270", + "nleqq;": "\u2266\u0338", + "nleqslant;": "\u2a7d\u0338", + "nles;": "\u2a7d\u0338", + "nless;": "\u226e", + "nlsim;": "\u2274", + "nlt;": "\u226e", + "nltri;": "\u22ea", + "nltrie;": "\u22ec", + "nmid;": "\u2224", + "nopf;": "\U0001d55f", + "not": "\xac", + "not;": "\xac", + "notin;": "\u2209", + "notinE;": "\u22f9\u0338", + "notindot;": "\u22f5\u0338", + "notinva;": "\u2209", + "notinvb;": "\u22f7", + "notinvc;": "\u22f6", + "notni;": "\u220c", + "notniva;": "\u220c", + "notnivb;": "\u22fe", + "notnivc;": "\u22fd", + "npar;": "\u2226", + "nparallel;": "\u2226", + "nparsl;": "\u2afd\u20e5", + "npart;": "\u2202\u0338", + "npolint;": "\u2a14", + "npr;": "\u2280", + "nprcue;": "\u22e0", + "npre;": "\u2aaf\u0338", + "nprec;": "\u2280", + "npreceq;": "\u2aaf\u0338", + "nrArr;": "\u21cf", + "nrarr;": "\u219b", + "nrarrc;": "\u2933\u0338", + "nrarrw;": "\u219d\u0338", + "nrightarrow;": "\u219b", + "nrtri;": "\u22eb", + "nrtrie;": "\u22ed", + "nsc;": "\u2281", + "nsccue;": "\u22e1", + "nsce;": "\u2ab0\u0338", + "nscr;": "\U0001d4c3", + "nshortmid;": "\u2224", + "nshortparallel;": "\u2226", + "nsim;": "\u2241", + "nsime;": "\u2244", + "nsimeq;": "\u2244", + "nsmid;": "\u2224", + "nspar;": "\u2226", + "nsqsube;": "\u22e2", + "nsqsupe;": "\u22e3", + "nsub;": "\u2284", + "nsubE;": "\u2ac5\u0338", + "nsube;": "\u2288", + "nsubset;": "\u2282\u20d2", + "nsubseteq;": "\u2288", + "nsubseteqq;": "\u2ac5\u0338", + "nsucc;": "\u2281", + "nsucceq;": "\u2ab0\u0338", + "nsup;": "\u2285", + "nsupE;": "\u2ac6\u0338", + "nsupe;": "\u2289", + "nsupset;": "\u2283\u20d2", + "nsupseteq;": "\u2289", + "nsupseteqq;": "\u2ac6\u0338", + "ntgl;": "\u2279", + "ntilde": "\xf1", + "ntilde;": "\xf1", + "ntlg;": "\u2278", + "ntriangleleft;": "\u22ea", + "ntrianglelefteq;": "\u22ec", + "ntriangleright;": "\u22eb", + "ntrianglerighteq;": "\u22ed", + "nu;": "\u03bd", + "num;": "#", + "numero;": "\u2116", + "numsp;": "\u2007", + "nvDash;": "\u22ad", + "nvHarr;": "\u2904", + "nvap;": "\u224d\u20d2", + "nvdash;": "\u22ac", + "nvge;": "\u2265\u20d2", + "nvgt;": ">\u20d2", + "nvinfin;": "\u29de", + "nvlArr;": "\u2902", + "nvle;": "\u2264\u20d2", + "nvlt;": "<\u20d2", + "nvltrie;": "\u22b4\u20d2", + "nvrArr;": "\u2903", + "nvrtrie;": "\u22b5\u20d2", + "nvsim;": "\u223c\u20d2", + "nwArr;": "\u21d6", + "nwarhk;": "\u2923", + "nwarr;": "\u2196", + "nwarrow;": "\u2196", + "nwnear;": "\u2927", + "oS;": "\u24c8", + "oacute": "\xf3", + "oacute;": "\xf3", + "oast;": "\u229b", + "ocir;": "\u229a", + "ocirc": "\xf4", + "ocirc;": "\xf4", + "ocy;": "\u043e", + "odash;": "\u229d", + "odblac;": "\u0151", + "odiv;": "\u2a38", + "odot;": "\u2299", + "odsold;": "\u29bc", + "oelig;": "\u0153", + "ofcir;": "\u29bf", + "ofr;": "\U0001d52c", + "ogon;": "\u02db", + "ograve": "\xf2", + "ograve;": "\xf2", + "ogt;": "\u29c1", + "ohbar;": "\u29b5", + "ohm;": "\u03a9", + "oint;": "\u222e", + "olarr;": "\u21ba", + "olcir;": "\u29be", + "olcross;": "\u29bb", + "oline;": "\u203e", + "olt;": "\u29c0", + "omacr;": "\u014d", + "omega;": "\u03c9", + "omicron;": "\u03bf", + "omid;": "\u29b6", + "ominus;": "\u2296", + "oopf;": "\U0001d560", + "opar;": "\u29b7", + "operp;": "\u29b9", + "oplus;": "\u2295", + "or;": "\u2228", + "orarr;": "\u21bb", + "ord;": "\u2a5d", + "order;": "\u2134", + "orderof;": "\u2134", + "ordf": "\xaa", + "ordf;": "\xaa", + "ordm": "\xba", + "ordm;": "\xba", + "origof;": "\u22b6", + "oror;": "\u2a56", + "orslope;": "\u2a57", + "orv;": "\u2a5b", + "oscr;": "\u2134", + "oslash": "\xf8", + "oslash;": "\xf8", + "osol;": "\u2298", + "otilde": "\xf5", + "otilde;": "\xf5", + "otimes;": "\u2297", + "otimesas;": "\u2a36", + "ouml": "\xf6", + "ouml;": "\xf6", + "ovbar;": "\u233d", + "par;": "\u2225", + "para": "\xb6", + "para;": "\xb6", + "parallel;": "\u2225", + "parsim;": "\u2af3", + "parsl;": "\u2afd", + "part;": "\u2202", + "pcy;": "\u043f", + "percnt;": "%", + "period;": ".", + "permil;": "\u2030", + "perp;": "\u22a5", + "pertenk;": "\u2031", + "pfr;": "\U0001d52d", + "phi;": "\u03c6", + "phiv;": "\u03d5", + "phmmat;": "\u2133", + "phone;": "\u260e", + "pi;": "\u03c0", + "pitchfork;": "\u22d4", + "piv;": "\u03d6", + "planck;": "\u210f", + "planckh;": "\u210e", + "plankv;": "\u210f", + "plus;": "+", + "plusacir;": "\u2a23", + "plusb;": "\u229e", + "pluscir;": "\u2a22", + "plusdo;": "\u2214", + "plusdu;": "\u2a25", + "pluse;": "\u2a72", + "plusmn": "\xb1", + "plusmn;": "\xb1", + "plussim;": "\u2a26", + "plustwo;": "\u2a27", + "pm;": "\xb1", + "pointint;": "\u2a15", + "popf;": "\U0001d561", + "pound": "\xa3", + "pound;": "\xa3", + "pr;": "\u227a", + "prE;": "\u2ab3", + "prap;": "\u2ab7", + "prcue;": "\u227c", + "pre;": "\u2aaf", + "prec;": "\u227a", + "precapprox;": "\u2ab7", + "preccurlyeq;": "\u227c", + "preceq;": "\u2aaf", + "precnapprox;": "\u2ab9", + "precneqq;": "\u2ab5", + "precnsim;": "\u22e8", + "precsim;": "\u227e", + "prime;": "\u2032", + "primes;": "\u2119", + "prnE;": "\u2ab5", + "prnap;": "\u2ab9", + "prnsim;": "\u22e8", + "prod;": "\u220f", + "profalar;": "\u232e", + "profline;": "\u2312", + "profsurf;": "\u2313", + "prop;": "\u221d", + "propto;": "\u221d", + "prsim;": "\u227e", + "prurel;": "\u22b0", + "pscr;": "\U0001d4c5", + "psi;": "\u03c8", + "puncsp;": "\u2008", + "qfr;": "\U0001d52e", + "qint;": "\u2a0c", + "qopf;": "\U0001d562", + "qprime;": "\u2057", + "qscr;": "\U0001d4c6", + "quaternions;": "\u210d", + "quatint;": "\u2a16", + "quest;": "?", + "questeq;": "\u225f", + "quot": "\"", + "quot;": "\"", + "rAarr;": "\u21db", + "rArr;": "\u21d2", + "rAtail;": "\u291c", + "rBarr;": "\u290f", + "rHar;": "\u2964", + "race;": "\u223d\u0331", + "racute;": "\u0155", + "radic;": "\u221a", + "raemptyv;": "\u29b3", + "rang;": "\u27e9", + "rangd;": "\u2992", + "range;": "\u29a5", + "rangle;": "\u27e9", + "raquo": "\xbb", + "raquo;": "\xbb", + "rarr;": "\u2192", + "rarrap;": "\u2975", + "rarrb;": "\u21e5", + "rarrbfs;": "\u2920", + "rarrc;": "\u2933", + "rarrfs;": "\u291e", + "rarrhk;": "\u21aa", + "rarrlp;": "\u21ac", + "rarrpl;": "\u2945", + "rarrsim;": "\u2974", + "rarrtl;": "\u21a3", + "rarrw;": "\u219d", + "ratail;": "\u291a", + "ratio;": "\u2236", + "rationals;": "\u211a", + "rbarr;": "\u290d", + "rbbrk;": "\u2773", + "rbrace;": "}", + "rbrack;": "]", + "rbrke;": "\u298c", + "rbrksld;": "\u298e", + "rbrkslu;": "\u2990", + "rcaron;": "\u0159", + "rcedil;": "\u0157", + "rceil;": "\u2309", + "rcub;": "}", + "rcy;": "\u0440", + "rdca;": "\u2937", + "rdldhar;": "\u2969", + "rdquo;": "\u201d", + "rdquor;": "\u201d", + "rdsh;": "\u21b3", + "real;": "\u211c", + "realine;": "\u211b", + "realpart;": "\u211c", + "reals;": "\u211d", + "rect;": "\u25ad", + "reg": "\xae", + "reg;": "\xae", + "rfisht;": "\u297d", + "rfloor;": "\u230b", + "rfr;": "\U0001d52f", + "rhard;": "\u21c1", + "rharu;": "\u21c0", + "rharul;": "\u296c", + "rho;": "\u03c1", + "rhov;": "\u03f1", + "rightarrow;": "\u2192", + "rightarrowtail;": "\u21a3", + "rightharpoondown;": "\u21c1", + "rightharpoonup;": "\u21c0", + "rightleftarrows;": "\u21c4", + "rightleftharpoons;": "\u21cc", + "rightrightarrows;": "\u21c9", + "rightsquigarrow;": "\u219d", + "rightthreetimes;": "\u22cc", + "ring;": "\u02da", + "risingdotseq;": "\u2253", + "rlarr;": "\u21c4", + "rlhar;": "\u21cc", + "rlm;": "\u200f", + "rmoust;": "\u23b1", + "rmoustache;": "\u23b1", + "rnmid;": "\u2aee", + "roang;": "\u27ed", + "roarr;": "\u21fe", + "robrk;": "\u27e7", + "ropar;": "\u2986", + "ropf;": "\U0001d563", + "roplus;": "\u2a2e", + "rotimes;": "\u2a35", + "rpar;": ")", + "rpargt;": "\u2994", + "rppolint;": "\u2a12", + "rrarr;": "\u21c9", + "rsaquo;": "\u203a", + "rscr;": "\U0001d4c7", + "rsh;": "\u21b1", + "rsqb;": "]", + "rsquo;": "\u2019", + "rsquor;": "\u2019", + "rthree;": "\u22cc", + "rtimes;": "\u22ca", + "rtri;": "\u25b9", + "rtrie;": "\u22b5", + "rtrif;": "\u25b8", + "rtriltri;": "\u29ce", + "ruluhar;": "\u2968", + "rx;": "\u211e", + "sacute;": "\u015b", + "sbquo;": "\u201a", + "sc;": "\u227b", + "scE;": "\u2ab4", + "scap;": "\u2ab8", + "scaron;": "\u0161", + "sccue;": "\u227d", + "sce;": "\u2ab0", + "scedil;": "\u015f", + "scirc;": "\u015d", + "scnE;": "\u2ab6", + "scnap;": "\u2aba", + "scnsim;": "\u22e9", + "scpolint;": "\u2a13", + "scsim;": "\u227f", + "scy;": "\u0441", + "sdot;": "\u22c5", + "sdotb;": "\u22a1", + "sdote;": "\u2a66", + "seArr;": "\u21d8", + "searhk;": "\u2925", + "searr;": "\u2198", + "searrow;": "\u2198", + "sect": "\xa7", + "sect;": "\xa7", + "semi;": ";", + "seswar;": "\u2929", + "setminus;": "\u2216", + "setmn;": "\u2216", + "sext;": "\u2736", + "sfr;": "\U0001d530", + "sfrown;": "\u2322", + "sharp;": "\u266f", + "shchcy;": "\u0449", + "shcy;": "\u0448", + "shortmid;": "\u2223", + "shortparallel;": "\u2225", + "shy": "\xad", + "shy;": "\xad", + "sigma;": "\u03c3", + "sigmaf;": "\u03c2", + "sigmav;": "\u03c2", + "sim;": "\u223c", + "simdot;": "\u2a6a", + "sime;": "\u2243", + "simeq;": "\u2243", + "simg;": "\u2a9e", + "simgE;": "\u2aa0", + "siml;": "\u2a9d", + "simlE;": "\u2a9f", + "simne;": "\u2246", + "simplus;": "\u2a24", + "simrarr;": "\u2972", + "slarr;": "\u2190", + "smallsetminus;": "\u2216", + "smashp;": "\u2a33", + "smeparsl;": "\u29e4", + "smid;": "\u2223", + "smile;": "\u2323", + "smt;": "\u2aaa", + "smte;": "\u2aac", + "smtes;": "\u2aac\ufe00", + "softcy;": "\u044c", + "sol;": "/", + "solb;": "\u29c4", + "solbar;": "\u233f", + "sopf;": "\U0001d564", + "spades;": "\u2660", + "spadesuit;": "\u2660", + "spar;": "\u2225", + "sqcap;": "\u2293", + "sqcaps;": "\u2293\ufe00", + "sqcup;": "\u2294", + "sqcups;": "\u2294\ufe00", + "sqsub;": "\u228f", + "sqsube;": "\u2291", + "sqsubset;": "\u228f", + "sqsubseteq;": "\u2291", + "sqsup;": "\u2290", + "sqsupe;": "\u2292", + "sqsupset;": "\u2290", + "sqsupseteq;": "\u2292", + "squ;": "\u25a1", + "square;": "\u25a1", + "squarf;": "\u25aa", + "squf;": "\u25aa", + "srarr;": "\u2192", + "sscr;": "\U0001d4c8", + "ssetmn;": "\u2216", + "ssmile;": "\u2323", + "sstarf;": "\u22c6", + "star;": "\u2606", + "starf;": "\u2605", + "straightepsilon;": "\u03f5", + "straightphi;": "\u03d5", + "strns;": "\xaf", + "sub;": "\u2282", + "subE;": "\u2ac5", + "subdot;": "\u2abd", + "sube;": "\u2286", + "subedot;": "\u2ac3", + "submult;": "\u2ac1", + "subnE;": "\u2acb", + "subne;": "\u228a", + "subplus;": "\u2abf", + "subrarr;": "\u2979", + "subset;": "\u2282", + "subseteq;": "\u2286", + "subseteqq;": "\u2ac5", + "subsetneq;": "\u228a", + "subsetneqq;": "\u2acb", + "subsim;": "\u2ac7", + "subsub;": "\u2ad5", + "subsup;": "\u2ad3", + "succ;": "\u227b", + "succapprox;": "\u2ab8", + "succcurlyeq;": "\u227d", + "succeq;": "\u2ab0", + "succnapprox;": "\u2aba", + "succneqq;": "\u2ab6", + "succnsim;": "\u22e9", + "succsim;": "\u227f", + "sum;": "\u2211", + "sung;": "\u266a", + "sup1": "\xb9", + "sup1;": "\xb9", + "sup2": "\xb2", + "sup2;": "\xb2", + "sup3": "\xb3", + "sup3;": "\xb3", + "sup;": "\u2283", + "supE;": "\u2ac6", + "supdot;": "\u2abe", + "supdsub;": "\u2ad8", + "supe;": "\u2287", + "supedot;": "\u2ac4", + "suphsol;": "\u27c9", + "suphsub;": "\u2ad7", + "suplarr;": "\u297b", + "supmult;": "\u2ac2", + "supnE;": "\u2acc", + "supne;": "\u228b", + "supplus;": "\u2ac0", + "supset;": "\u2283", + "supseteq;": "\u2287", + "supseteqq;": "\u2ac6", + "supsetneq;": "\u228b", + "supsetneqq;": "\u2acc", + "supsim;": "\u2ac8", + "supsub;": "\u2ad4", + "supsup;": "\u2ad6", + "swArr;": "\u21d9", + "swarhk;": "\u2926", + "swarr;": "\u2199", + "swarrow;": "\u2199", + "swnwar;": "\u292a", + "szlig": "\xdf", + "szlig;": "\xdf", + "target;": "\u2316", + "tau;": "\u03c4", + "tbrk;": "\u23b4", + "tcaron;": "\u0165", + "tcedil;": "\u0163", + "tcy;": "\u0442", + "tdot;": "\u20db", + "telrec;": "\u2315", + "tfr;": "\U0001d531", + "there4;": "\u2234", + "therefore;": "\u2234", + "theta;": "\u03b8", + "thetasym;": "\u03d1", + "thetav;": "\u03d1", + "thickapprox;": "\u2248", + "thicksim;": "\u223c", + "thinsp;": "\u2009", + "thkap;": "\u2248", + "thksim;": "\u223c", + "thorn": "\xfe", + "thorn;": "\xfe", + "tilde;": "\u02dc", + "times": "\xd7", + "times;": "\xd7", + "timesb;": "\u22a0", + "timesbar;": "\u2a31", + "timesd;": "\u2a30", + "tint;": "\u222d", + "toea;": "\u2928", + "top;": "\u22a4", + "topbot;": "\u2336", + "topcir;": "\u2af1", + "topf;": "\U0001d565", + "topfork;": "\u2ada", + "tosa;": "\u2929", + "tprime;": "\u2034", + "trade;": "\u2122", + "triangle;": "\u25b5", + "triangledown;": "\u25bf", + "triangleleft;": "\u25c3", + "trianglelefteq;": "\u22b4", + "triangleq;": "\u225c", + "triangleright;": "\u25b9", + "trianglerighteq;": "\u22b5", + "tridot;": "\u25ec", + "trie;": "\u225c", + "triminus;": "\u2a3a", + "triplus;": "\u2a39", + "trisb;": "\u29cd", + "tritime;": "\u2a3b", + "trpezium;": "\u23e2", + "tscr;": "\U0001d4c9", + "tscy;": "\u0446", + "tshcy;": "\u045b", + "tstrok;": "\u0167", + "twixt;": "\u226c", + "twoheadleftarrow;": "\u219e", + "twoheadrightarrow;": "\u21a0", + "uArr;": "\u21d1", + "uHar;": "\u2963", + "uacute": "\xfa", + "uacute;": "\xfa", + "uarr;": "\u2191", + "ubrcy;": "\u045e", + "ubreve;": "\u016d", + "ucirc": "\xfb", + "ucirc;": "\xfb", + "ucy;": "\u0443", + "udarr;": "\u21c5", + "udblac;": "\u0171", + "udhar;": "\u296e", + "ufisht;": "\u297e", + "ufr;": "\U0001d532", + "ugrave": "\xf9", + "ugrave;": "\xf9", + "uharl;": "\u21bf", + "uharr;": "\u21be", + "uhblk;": "\u2580", + "ulcorn;": "\u231c", + "ulcorner;": "\u231c", + "ulcrop;": "\u230f", + "ultri;": "\u25f8", + "umacr;": "\u016b", + "uml": "\xa8", + "uml;": "\xa8", + "uogon;": "\u0173", + "uopf;": "\U0001d566", + "uparrow;": "\u2191", + "updownarrow;": "\u2195", + "upharpoonleft;": "\u21bf", + "upharpoonright;": "\u21be", + "uplus;": "\u228e", + "upsi;": "\u03c5", + "upsih;": "\u03d2", + "upsilon;": "\u03c5", + "upuparrows;": "\u21c8", + "urcorn;": "\u231d", + "urcorner;": "\u231d", + "urcrop;": "\u230e", + "uring;": "\u016f", + "urtri;": "\u25f9", + "uscr;": "\U0001d4ca", + "utdot;": "\u22f0", + "utilde;": "\u0169", + "utri;": "\u25b5", + "utrif;": "\u25b4", + "uuarr;": "\u21c8", + "uuml": "\xfc", + "uuml;": "\xfc", + "uwangle;": "\u29a7", + "vArr;": "\u21d5", + "vBar;": "\u2ae8", + "vBarv;": "\u2ae9", + "vDash;": "\u22a8", + "vangrt;": "\u299c", + "varepsilon;": "\u03f5", + "varkappa;": "\u03f0", + "varnothing;": "\u2205", + "varphi;": "\u03d5", + "varpi;": "\u03d6", + "varpropto;": "\u221d", + "varr;": "\u2195", + "varrho;": "\u03f1", + "varsigma;": "\u03c2", + "varsubsetneq;": "\u228a\ufe00", + "varsubsetneqq;": "\u2acb\ufe00", + "varsupsetneq;": "\u228b\ufe00", + "varsupsetneqq;": "\u2acc\ufe00", + "vartheta;": "\u03d1", + "vartriangleleft;": "\u22b2", + "vartriangleright;": "\u22b3", + "vcy;": "\u0432", + "vdash;": "\u22a2", + "vee;": "\u2228", + "veebar;": "\u22bb", + "veeeq;": "\u225a", + "vellip;": "\u22ee", + "verbar;": "|", + "vert;": "|", + "vfr;": "\U0001d533", + "vltri;": "\u22b2", + "vnsub;": "\u2282\u20d2", + "vnsup;": "\u2283\u20d2", + "vopf;": "\U0001d567", + "vprop;": "\u221d", + "vrtri;": "\u22b3", + "vscr;": "\U0001d4cb", + "vsubnE;": "\u2acb\ufe00", + "vsubne;": "\u228a\ufe00", + "vsupnE;": "\u2acc\ufe00", + "vsupne;": "\u228b\ufe00", + "vzigzag;": "\u299a", + "wcirc;": "\u0175", + "wedbar;": "\u2a5f", + "wedge;": "\u2227", + "wedgeq;": "\u2259", + "weierp;": "\u2118", + "wfr;": "\U0001d534", + "wopf;": "\U0001d568", + "wp;": "\u2118", + "wr;": "\u2240", + "wreath;": "\u2240", + "wscr;": "\U0001d4cc", + "xcap;": "\u22c2", + "xcirc;": "\u25ef", + "xcup;": "\u22c3", + "xdtri;": "\u25bd", + "xfr;": "\U0001d535", + "xhArr;": "\u27fa", + "xharr;": "\u27f7", + "xi;": "\u03be", + "xlArr;": "\u27f8", + "xlarr;": "\u27f5", + "xmap;": "\u27fc", + "xnis;": "\u22fb", + "xodot;": "\u2a00", + "xopf;": "\U0001d569", + "xoplus;": "\u2a01", + "xotime;": "\u2a02", + "xrArr;": "\u27f9", + "xrarr;": "\u27f6", + "xscr;": "\U0001d4cd", + "xsqcup;": "\u2a06", + "xuplus;": "\u2a04", + "xutri;": "\u25b3", + "xvee;": "\u22c1", + "xwedge;": "\u22c0", + "yacute": "\xfd", + "yacute;": "\xfd", + "yacy;": "\u044f", + "ycirc;": "\u0177", + "ycy;": "\u044b", + "yen": "\xa5", + "yen;": "\xa5", + "yfr;": "\U0001d536", + "yicy;": "\u0457", + "yopf;": "\U0001d56a", + "yscr;": "\U0001d4ce", + "yucy;": "\u044e", + "yuml": "\xff", + "yuml;": "\xff", + "zacute;": "\u017a", + "zcaron;": "\u017e", + "zcy;": "\u0437", + "zdot;": "\u017c", + "zeetrf;": "\u2128", + "zeta;": "\u03b6", + "zfr;": "\U0001d537", + "zhcy;": "\u0436", + "zigrarr;": "\u21dd", + "zopf;": "\U0001d56b", + "zscr;": "\U0001d4cf", + "zwj;": "\u200d", + "zwnj;": "\u200c", +} + +replacementCharacters = { + 0x0: "\uFFFD", + 0x0d: "\u000D", + 0x80: "\u20AC", + 0x81: "\u0081", + 0x82: "\u201A", + 0x83: "\u0192", + 0x84: "\u201E", + 0x85: "\u2026", + 0x86: "\u2020", + 0x87: "\u2021", + 0x88: "\u02C6", + 0x89: "\u2030", + 0x8A: "\u0160", + 0x8B: "\u2039", + 0x8C: "\u0152", + 0x8D: "\u008D", + 0x8E: "\u017D", + 0x8F: "\u008F", + 0x90: "\u0090", + 0x91: "\u2018", + 0x92: "\u2019", + 0x93: "\u201C", + 0x94: "\u201D", + 0x95: "\u2022", + 0x96: "\u2013", + 0x97: "\u2014", + 0x98: "\u02DC", + 0x99: "\u2122", + 0x9A: "\u0161", + 0x9B: "\u203A", + 0x9C: "\u0153", + 0x9D: "\u009D", + 0x9E: "\u017E", + 0x9F: "\u0178", +} + +tokenTypes = { + "Doctype": 0, + "Characters": 1, + "SpaceCharacters": 2, + "StartTag": 3, + "EndTag": 4, + "EmptyTag": 5, + "Comment": 6, + "ParseError": 7 +} + +tagTokenTypes = frozenset([tokenTypes["StartTag"], tokenTypes["EndTag"], + tokenTypes["EmptyTag"]]) + + +prefixes = dict([(v, k) for k, v in namespaces.items()]) +prefixes["http://www.w3.org/1998/Math/MathML"] = "math" + + +class DataLossWarning(UserWarning): + """Raised when the current tree is unable to represent the input data""" + pass + + +class _ReparseException(Exception): + pass diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/filters/__init__.py b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py new file mode 100644 index 0000000..5ba926e --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py @@ -0,0 +1,29 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import base + +from collections import OrderedDict + + +def _attr_key(attr): + """Return an appropriate key for an attribute for sorting + + Attributes have a namespace that can be either ``None`` or a string. We + can't compare the two because they're different types, so we convert + ``None`` to an empty string first. + + """ + return (attr[0][0] or ''), attr[0][1] + + +class Filter(base.Filter): + """Alphabetizes attributes for elements""" + def __iter__(self): + for token in base.Filter.__iter__(self): + if token["type"] in ("StartTag", "EmptyTag"): + attrs = OrderedDict() + for name, value in sorted(token["data"].items(), + key=_attr_key): + attrs[name] = value + token["data"] = attrs + yield token diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/filters/base.py b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/base.py new file mode 100644 index 0000000..c7dbaed --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/base.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import, division, unicode_literals + + +class Filter(object): + def __init__(self, source): + self.source = source + + def __iter__(self): + return iter(self.source) + + def __getattr__(self, name): + return getattr(self.source, name) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py new file mode 100644 index 0000000..aefb5c8 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py @@ -0,0 +1,73 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import base + + +class Filter(base.Filter): + """Injects ``<meta charset=ENCODING>`` tag into head of document""" + def __init__(self, source, encoding): + """Creates a Filter + + :arg source: the source token stream + + :arg encoding: the encoding to set + + """ + base.Filter.__init__(self, source) + self.encoding = encoding + + def __iter__(self): + state = "pre_head" + meta_found = (self.encoding is None) + pending = [] + + for token in base.Filter.__iter__(self): + type = token["type"] + if type == "StartTag": + if token["name"].lower() == "head": + state = "in_head" + + elif type == "EmptyTag": + if token["name"].lower() == "meta": + # replace charset with actual encoding + has_http_equiv_content_type = False + for (namespace, name), value in token["data"].items(): + if namespace is not None: + continue + elif name.lower() == 'charset': + token["data"][(namespace, name)] = self.encoding + meta_found = True + break + elif name == 'http-equiv' and value.lower() == 'content-type': + has_http_equiv_content_type = True + else: + if has_http_equiv_content_type and (None, "content") in token["data"]: + token["data"][(None, "content")] = 'text/html; charset=%s' % self.encoding + meta_found = True + + elif token["name"].lower() == "head" and not meta_found: + # insert meta into empty head + yield {"type": "StartTag", "name": "head", + "data": token["data"]} + yield {"type": "EmptyTag", "name": "meta", + "data": {(None, "charset"): self.encoding}} + yield {"type": "EndTag", "name": "head"} + meta_found = True + continue + + elif type == "EndTag": + if token["name"].lower() == "head" and pending: + # insert meta into head (if necessary) and flush pending queue + yield pending.pop(0) + if not meta_found: + yield {"type": "EmptyTag", "name": "meta", + "data": {(None, "charset"): self.encoding}} + while pending: + yield pending.pop(0) + meta_found = True + state = "post_head" + + if state == "in_head": + pending.append(token) + else: + yield token diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/filters/lint.py b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/lint.py new file mode 100644 index 0000000..fcc07ee --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/lint.py @@ -0,0 +1,93 @@ +from __future__ import absolute_import, division, unicode_literals + +from pip._vendor.six import text_type + +from . import base +from ..constants import namespaces, voidElements + +from ..constants import spaceCharacters +spaceCharacters = "".join(spaceCharacters) + + +class Filter(base.Filter): + """Lints the token stream for errors + + If it finds any errors, it'll raise an ``AssertionError``. + + """ + def __init__(self, source, require_matching_tags=True): + """Creates a Filter + + :arg source: the source token stream + + :arg require_matching_tags: whether or not to require matching tags + + """ + super(Filter, self).__init__(source) + self.require_matching_tags = require_matching_tags + + def __iter__(self): + open_elements = [] + for token in base.Filter.__iter__(self): + type = token["type"] + if type in ("StartTag", "EmptyTag"): + namespace = token["namespace"] + name = token["name"] + assert namespace is None or isinstance(namespace, text_type) + assert namespace != "" + assert isinstance(name, text_type) + assert name != "" + assert isinstance(token["data"], dict) + if (not namespace or namespace == namespaces["html"]) and name in voidElements: + assert type == "EmptyTag" + else: + assert type == "StartTag" + if type == "StartTag" and self.require_matching_tags: + open_elements.append((namespace, name)) + for (namespace, name), value in token["data"].items(): + assert namespace is None or isinstance(namespace, text_type) + assert namespace != "" + assert isinstance(name, text_type) + assert name != "" + assert isinstance(value, text_type) + + elif type == "EndTag": + namespace = token["namespace"] + name = token["name"] + assert namespace is None or isinstance(namespace, text_type) + assert namespace != "" + assert isinstance(name, text_type) + assert name != "" + if (not namespace or namespace == namespaces["html"]) and name in voidElements: + assert False, "Void element reported as EndTag token: %(tag)s" % {"tag": name} + elif self.require_matching_tags: + start = open_elements.pop() + assert start == (namespace, name) + + elif type == "Comment": + data = token["data"] + assert isinstance(data, text_type) + + elif type in ("Characters", "SpaceCharacters"): + data = token["data"] + assert isinstance(data, text_type) + assert data != "" + if type == "SpaceCharacters": + assert data.strip(spaceCharacters) == "" + + elif type == "Doctype": + name = token["name"] + assert name is None or isinstance(name, text_type) + assert token["publicId"] is None or isinstance(name, text_type) + assert token["systemId"] is None or isinstance(name, text_type) + + elif type == "Entity": + assert isinstance(token["name"], text_type) + + elif type == "SerializerError": + assert isinstance(token["data"], text_type) + + else: + assert False, "Unknown token type: %(type)s" % {"type": type} + + yield token diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/filters/optionaltags.py b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/optionaltags.py new file mode 100644 index 0000000..4a86501 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/optionaltags.py @@ -0,0 +1,207 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import base + + +class Filter(base.Filter): + """Removes optional tags from the token stream""" + def slider(self): + previous1 = previous2 = None + for token in self.source: + if previous1 is not None: + yield previous2, previous1, token + previous2 = previous1 + previous1 = token + if previous1 is not None: + yield previous2, previous1, None + + def __iter__(self): + for previous, token, next in self.slider(): + type = token["type"] + if type == "StartTag": + if (token["data"] or + not self.is_optional_start(token["name"], previous, next)): + yield token + elif type == "EndTag": + if not self.is_optional_end(token["name"], next): + yield token + else: + yield token + + def is_optional_start(self, tagname, previous, next): + type = next and next["type"] or None + if tagname in 'html': + # An html element's start tag may be omitted if the first thing + # inside the html element is not a space character or a comment. + return type not in ("Comment", "SpaceCharacters") + elif tagname == 'head': + # A head element's start tag may be omitted if the first thing + # inside the head element is an element. + # XXX: we also omit the start tag if the head element is empty + if type in ("StartTag", "EmptyTag"): + return True + elif type == "EndTag": + return next["name"] == "head" + elif tagname == 'body': + # A body element's start tag may be omitted if the first thing + # inside the body element is not a space character or a comment, + # except if the first thing inside the body element is a script + # or style element and the node immediately preceding the body + # element is a head element whose end tag has been omitted. + if type in ("Comment", "SpaceCharacters"): + return False + elif type == "StartTag": + # XXX: we do not look at the preceding event, so we never omit + # the body element's start tag if it's followed by a script or + # a style element. + return next["name"] not in ('script', 'style') + else: + return True + elif tagname == 'colgroup': + # A colgroup element's start tag may be omitted if the first thing + # inside the colgroup element is a col element, and if the element + # is not immediately preceded by another colgroup element whose + # end tag has been omitted. + if type in ("StartTag", "EmptyTag"): + # XXX: we do not look at the preceding event, so instead we never + # omit the colgroup element's end tag when it is immediately + # followed by another colgroup element. See is_optional_end. + return next["name"] == "col" + else: + return False + elif tagname == 'tbody': + # A tbody element's start tag may be omitted if the first thing + # inside the tbody element is a tr element, and if the element is + # not immediately preceded by a tbody, thead, or tfoot element + # whose end tag has been omitted. + if type == "StartTag": + # omit the thead and tfoot elements' end tag when they are + # immediately followed by a tbody element. See is_optional_end. + if previous and previous['type'] == 'EndTag' and \ + previous['name'] in ('tbody', 'thead', 'tfoot'): + return False + return next["name"] == 'tr' + else: + return False + return False + + def is_optional_end(self, tagname, next): + type = next and next["type"] or None + if tagname in ('html', 'head', 'body'): + # An html element's end tag may be omitted if the html element + # is not immediately followed by a space character or a comment. + return type not in ("Comment", "SpaceCharacters") + elif tagname in ('li', 'optgroup', 'tr'): + # A li element's end tag may be omitted if the li element is + # immediately followed by another li element or if there is + # no more content in the parent element. + # An optgroup element's end tag may be omitted if the optgroup + # element is immediately followed by another optgroup element, + # or if there is no more content in the parent element. + # A tr element's end tag may be omitted if the tr element is + # immediately followed by another tr element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] == tagname + else: + return type == "EndTag" or type is None + elif tagname in ('dt', 'dd'): + # A dt element's end tag may be omitted if the dt element is + # immediately followed by another dt element or a dd element. + # A dd element's end tag may be omitted if the dd element is + # immediately followed by another dd element or a dt element, + # or if there is no more content in the parent element. + if type == "StartTag": + return next["name"] in ('dt', 'dd') + elif tagname == 'dd': + return type == "EndTag" or type is None + else: + return False + elif tagname == 'p': + # A p element's end tag may be omitted if the p element is + # immediately followed by an address, article, aside, + # blockquote, datagrid, dialog, dir, div, dl, fieldset, + # footer, form, h1, h2, h3, h4, h5, h6, header, hr, menu, + # nav, ol, p, pre, section, table, or ul, element, or if + # there is no more content in the parent element. + if type in ("StartTag", "EmptyTag"): + return next["name"] in ('address', 'article', 'aside', + 'blockquote', 'datagrid', 'dialog', + 'dir', 'div', 'dl', 'fieldset', 'footer', + 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'header', 'hr', 'menu', 'nav', 'ol', + 'p', 'pre', 'section', 'table', 'ul') + else: + return type == "EndTag" or type is None + elif tagname == 'option': + # An option element's end tag may be omitted if the option + # element is immediately followed by another option element, + # or if it is immediately followed by an <code>optgroup</code> + # element, or if there is no more content in the parent + # element. + if type == "StartTag": + return next["name"] in ('option', 'optgroup') + else: + return type == "EndTag" or type is None + elif tagname in ('rt', 'rp'): + # An rt element's end tag may be omitted if the rt element is + # immediately followed by an rt or rp element, or if there is + # no more content in the parent element. + # An rp element's end tag may be omitted if the rp element is + # immediately followed by an rt or rp element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] in ('rt', 'rp') + else: + return type == "EndTag" or type is None + elif tagname == 'colgroup': + # A colgroup element's end tag may be omitted if the colgroup + # element is not immediately followed by a space character or + # a comment. + if type in ("Comment", "SpaceCharacters"): + return False + elif type == "StartTag": + # XXX: we also look for an immediately following colgroup + # element. See is_optional_start. + return next["name"] != 'colgroup' + else: + return True + elif tagname in ('thead', 'tbody'): + # A thead element's end tag may be omitted if the thead element + # is immediately followed by a tbody or tfoot element. + # A tbody element's end tag may be omitted if the tbody element + # is immediately followed by a tbody or tfoot element, or if + # there is no more content in the parent element. + # A tfoot element's end tag may be omitted if the tfoot element + # is immediately followed by a tbody element, or if there is no + # more content in the parent element. + # XXX: we never omit the end tag when the following element is + # a tbody. See is_optional_start. + if type == "StartTag": + return next["name"] in ['tbody', 'tfoot'] + elif tagname == 'tbody': + return type == "EndTag" or type is None + else: + return False + elif tagname == 'tfoot': + # A tfoot element's end tag may be omitted if the tfoot element + # is immediately followed by a tbody element, or if there is no + # more content in the parent element. + # XXX: we never omit the end tag when the following element is + # a tbody. See is_optional_start. + if type == "StartTag": + return next["name"] == 'tbody' + else: + return type == "EndTag" or type is None + elif tagname in ('td', 'th'): + # A td element's end tag may be omitted if the td element is + # immediately followed by a td or th element, or if there is + # no more content in the parent element. + # A th element's end tag may be omitted if the th element is + # immediately followed by a td or th element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] in ('td', 'th') + else: + return type == "EndTag" or type is None + return False diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/filters/sanitizer.py b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/sanitizer.py new file mode 100644 index 0000000..af8e77b --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/sanitizer.py @@ -0,0 +1,896 @@ +from __future__ import absolute_import, division, unicode_literals + +import re +from xml.sax.saxutils import escape, unescape + +from pip._vendor.six.moves import urllib_parse as urlparse + +from . import base +from ..constants import namespaces, prefixes + +__all__ = ["Filter"] + + +allowed_elements = frozenset(( + (namespaces['html'], 'a'), + (namespaces['html'], 'abbr'), + (namespaces['html'], 'acronym'), + (namespaces['html'], 'address'), + (namespaces['html'], 'area'), + (namespaces['html'], 'article'), + (namespaces['html'], 'aside'), + (namespaces['html'], 'audio'), + (namespaces['html'], 'b'), + (namespaces['html'], 'big'), + (namespaces['html'], 'blockquote'), + (namespaces['html'], 'br'), + (namespaces['html'], 'button'), + (namespaces['html'], 'canvas'), + (namespaces['html'], 'caption'), + (namespaces['html'], 'center'), + (namespaces['html'], 'cite'), + (namespaces['html'], 'code'), + (namespaces['html'], 'col'), + (namespaces['html'], 'colgroup'), + (namespaces['html'], 'command'), + (namespaces['html'], 'datagrid'), + (namespaces['html'], 'datalist'), + (namespaces['html'], 'dd'), + (namespaces['html'], 'del'), + (namespaces['html'], 'details'), + (namespaces['html'], 'dfn'), + (namespaces['html'], 'dialog'), + (namespaces['html'], 'dir'), + (namespaces['html'], 'div'), + (namespaces['html'], 'dl'), + (namespaces['html'], 'dt'), + (namespaces['html'], 'em'), + (namespaces['html'], 'event-source'), + (namespaces['html'], 'fieldset'), + (namespaces['html'], 'figcaption'), + (namespaces['html'], 'figure'), + (namespaces['html'], 'footer'), + (namespaces['html'], 'font'), + (namespaces['html'], 'form'), + (namespaces['html'], 'header'), + (namespaces['html'], 'h1'), + (namespaces['html'], 'h2'), + (namespaces['html'], 'h3'), + (namespaces['html'], 'h4'), + (namespaces['html'], 'h5'), + (namespaces['html'], 'h6'), + (namespaces['html'], 'hr'), + (namespaces['html'], 'i'), + (namespaces['html'], 'img'), + (namespaces['html'], 'input'), + (namespaces['html'], 'ins'), + (namespaces['html'], 'keygen'), + (namespaces['html'], 'kbd'), + (namespaces['html'], 'label'), + (namespaces['html'], 'legend'), + (namespaces['html'], 'li'), + (namespaces['html'], 'm'), + (namespaces['html'], 'map'), + (namespaces['html'], 'menu'), + (namespaces['html'], 'meter'), + (namespaces['html'], 'multicol'), + (namespaces['html'], 'nav'), + (namespaces['html'], 'nextid'), + (namespaces['html'], 'ol'), + (namespaces['html'], 'output'), + (namespaces['html'], 'optgroup'), + (namespaces['html'], 'option'), + (namespaces['html'], 'p'), + (namespaces['html'], 'pre'), + (namespaces['html'], 'progress'), + (namespaces['html'], 'q'), + (namespaces['html'], 's'), + (namespaces['html'], 'samp'), + (namespaces['html'], 'section'), + (namespaces['html'], 'select'), + (namespaces['html'], 'small'), + (namespaces['html'], 'sound'), + (namespaces['html'], 'source'), + (namespaces['html'], 'spacer'), + (namespaces['html'], 'span'), + (namespaces['html'], 'strike'), + (namespaces['html'], 'strong'), + (namespaces['html'], 'sub'), + (namespaces['html'], 'sup'), + (namespaces['html'], 'table'), + (namespaces['html'], 'tbody'), + (namespaces['html'], 'td'), + (namespaces['html'], 'textarea'), + (namespaces['html'], 'time'), + (namespaces['html'], 'tfoot'), + (namespaces['html'], 'th'), + (namespaces['html'], 'thead'), + (namespaces['html'], 'tr'), + (namespaces['html'], 'tt'), + (namespaces['html'], 'u'), + (namespaces['html'], 'ul'), + (namespaces['html'], 'var'), + (namespaces['html'], 'video'), + (namespaces['mathml'], 'maction'), + (namespaces['mathml'], 'math'), + (namespaces['mathml'], 'merror'), + (namespaces['mathml'], 'mfrac'), + (namespaces['mathml'], 'mi'), + (namespaces['mathml'], 'mmultiscripts'), + (namespaces['mathml'], 'mn'), + (namespaces['mathml'], 'mo'), + (namespaces['mathml'], 'mover'), + (namespaces['mathml'], 'mpadded'), + (namespaces['mathml'], 'mphantom'), + (namespaces['mathml'], 'mprescripts'), + (namespaces['mathml'], 'mroot'), + (namespaces['mathml'], 'mrow'), + (namespaces['mathml'], 'mspace'), + (namespaces['mathml'], 'msqrt'), + (namespaces['mathml'], 'mstyle'), + (namespaces['mathml'], 'msub'), + (namespaces['mathml'], 'msubsup'), + (namespaces['mathml'], 'msup'), + (namespaces['mathml'], 'mtable'), + (namespaces['mathml'], 'mtd'), + (namespaces['mathml'], 'mtext'), + (namespaces['mathml'], 'mtr'), + (namespaces['mathml'], 'munder'), + (namespaces['mathml'], 'munderover'), + (namespaces['mathml'], 'none'), + (namespaces['svg'], 'a'), + (namespaces['svg'], 'animate'), + (namespaces['svg'], 'animateColor'), + (namespaces['svg'], 'animateMotion'), + (namespaces['svg'], 'animateTransform'), + (namespaces['svg'], 'clipPath'), + (namespaces['svg'], 'circle'), + (namespaces['svg'], 'defs'), + (namespaces['svg'], 'desc'), + (namespaces['svg'], 'ellipse'), + (namespaces['svg'], 'font-face'), + (namespaces['svg'], 'font-face-name'), + (namespaces['svg'], 'font-face-src'), + (namespaces['svg'], 'g'), + (namespaces['svg'], 'glyph'), + (namespaces['svg'], 'hkern'), + (namespaces['svg'], 'linearGradient'), + (namespaces['svg'], 'line'), + (namespaces['svg'], 'marker'), + (namespaces['svg'], 'metadata'), + (namespaces['svg'], 'missing-glyph'), + (namespaces['svg'], 'mpath'), + (namespaces['svg'], 'path'), + (namespaces['svg'], 'polygon'), + (namespaces['svg'], 'polyline'), + (namespaces['svg'], 'radialGradient'), + (namespaces['svg'], 'rect'), + (namespaces['svg'], 'set'), + (namespaces['svg'], 'stop'), + (namespaces['svg'], 'svg'), + (namespaces['svg'], 'switch'), + (namespaces['svg'], 'text'), + (namespaces['svg'], 'title'), + (namespaces['svg'], 'tspan'), + (namespaces['svg'], 'use'), +)) + +allowed_attributes = frozenset(( + # HTML attributes + (None, 'abbr'), + (None, 'accept'), + (None, 'accept-charset'), + (None, 'accesskey'), + (None, 'action'), + (None, 'align'), + (None, 'alt'), + (None, 'autocomplete'), + (None, 'autofocus'), + (None, 'axis'), + (None, 'background'), + (None, 'balance'), + (None, 'bgcolor'), + (None, 'bgproperties'), + (None, 'border'), + (None, 'bordercolor'), + (None, 'bordercolordark'), + (None, 'bordercolorlight'), + (None, 'bottompadding'), + (None, 'cellpadding'), + (None, 'cellspacing'), + (None, 'ch'), + (None, 'challenge'), + (None, 'char'), + (None, 'charoff'), + (None, 'choff'), + (None, 'charset'), + (None, 'checked'), + (None, 'cite'), + (None, 'class'), + (None, 'clear'), + (None, 'color'), + (None, 'cols'), + (None, 'colspan'), + (None, 'compact'), + (None, 'contenteditable'), + (None, 'controls'), + (None, 'coords'), + (None, 'data'), + (None, 'datafld'), + (None, 'datapagesize'), + (None, 'datasrc'), + (None, 'datetime'), + (None, 'default'), + (None, 'delay'), + (None, 'dir'), + (None, 'disabled'), + (None, 'draggable'), + (None, 'dynsrc'), + (None, 'enctype'), + (None, 'end'), + (None, 'face'), + (None, 'for'), + (None, 'form'), + (None, 'frame'), + (None, 'galleryimg'), + (None, 'gutter'), + (None, 'headers'), + (None, 'height'), + (None, 'hidefocus'), + (None, 'hidden'), + (None, 'high'), + (None, 'href'), + (None, 'hreflang'), + (None, 'hspace'), + (None, 'icon'), + (None, 'id'), + (None, 'inputmode'), + (None, 'ismap'), + (None, 'keytype'), + (None, 'label'), + (None, 'leftspacing'), + (None, 'lang'), + (None, 'list'), + (None, 'longdesc'), + (None, 'loop'), + (None, 'loopcount'), + (None, 'loopend'), + (None, 'loopstart'), + (None, 'low'), + (None, 'lowsrc'), + (None, 'max'), + (None, 'maxlength'), + (None, 'media'), + (None, 'method'), + (None, 'min'), + (None, 'multiple'), + (None, 'name'), + (None, 'nohref'), + (None, 'noshade'), + (None, 'nowrap'), + (None, 'open'), + (None, 'optimum'), + (None, 'pattern'), + (None, 'ping'), + (None, 'point-size'), + (None, 'poster'), + (None, 'pqg'), + (None, 'preload'), + (None, 'prompt'), + (None, 'radiogroup'), + (None, 'readonly'), + (None, 'rel'), + (None, 'repeat-max'), + (None, 'repeat-min'), + (None, 'replace'), + (None, 'required'), + (None, 'rev'), + (None, 'rightspacing'), + (None, 'rows'), + (None, 'rowspan'), + (None, 'rules'), + (None, 'scope'), + (None, 'selected'), + (None, 'shape'), + (None, 'size'), + (None, 'span'), + (None, 'src'), + (None, 'start'), + (None, 'step'), + (None, 'style'), + (None, 'summary'), + (None, 'suppress'), + (None, 'tabindex'), + (None, 'target'), + (None, 'template'), + (None, 'title'), + (None, 'toppadding'), + (None, 'type'), + (None, 'unselectable'), + (None, 'usemap'), + (None, 'urn'), + (None, 'valign'), + (None, 'value'), + (None, 'variable'), + (None, 'volume'), + (None, 'vspace'), + (None, 'vrml'), + (None, 'width'), + (None, 'wrap'), + (namespaces['xml'], 'lang'), + # MathML attributes + (None, 'actiontype'), + (None, 'align'), + (None, 'columnalign'), + (None, 'columnalign'), + (None, 'columnalign'), + (None, 'columnlines'), + (None, 'columnspacing'), + (None, 'columnspan'), + (None, 'depth'), + (None, 'display'), + (None, 'displaystyle'), + (None, 'equalcolumns'), + (None, 'equalrows'), + (None, 'fence'), + (None, 'fontstyle'), + (None, 'fontweight'), + (None, 'frame'), + (None, 'height'), + (None, 'linethickness'), + (None, 'lspace'), + (None, 'mathbackground'), + (None, 'mathcolor'), + (None, 'mathvariant'), + (None, 'mathvariant'), + (None, 'maxsize'), + (None, 'minsize'), + (None, 'other'), + (None, 'rowalign'), + (None, 'rowalign'), + (None, 'rowalign'), + (None, 'rowlines'), + (None, 'rowspacing'), + (None, 'rowspan'), + (None, 'rspace'), + (None, 'scriptlevel'), + (None, 'selection'), + (None, 'separator'), + (None, 'stretchy'), + (None, 'width'), + (None, 'width'), + (namespaces['xlink'], 'href'), + (namespaces['xlink'], 'show'), + (namespaces['xlink'], 'type'), + # SVG attributes + (None, 'accent-height'), + (None, 'accumulate'), + (None, 'additive'), + (None, 'alphabetic'), + (None, 'arabic-form'), + (None, 'ascent'), + (None, 'attributeName'), + (None, 'attributeType'), + (None, 'baseProfile'), + (None, 'bbox'), + (None, 'begin'), + (None, 'by'), + (None, 'calcMode'), + (None, 'cap-height'), + (None, 'class'), + (None, 'clip-path'), + (None, 'color'), + (None, 'color-rendering'), + (None, 'content'), + (None, 'cx'), + (None, 'cy'), + (None, 'd'), + (None, 'dx'), + (None, 'dy'), + (None, 'descent'), + (None, 'display'), + (None, 'dur'), + (None, 'end'), + (None, 'fill'), + (None, 'fill-opacity'), + (None, 'fill-rule'), + (None, 'font-family'), + (None, 'font-size'), + (None, 'font-stretch'), + (None, 'font-style'), + (None, 'font-variant'), + (None, 'font-weight'), + (None, 'from'), + (None, 'fx'), + (None, 'fy'), + (None, 'g1'), + (None, 'g2'), + (None, 'glyph-name'), + (None, 'gradientUnits'), + (None, 'hanging'), + (None, 'height'), + (None, 'horiz-adv-x'), + (None, 'horiz-origin-x'), + (None, 'id'), + (None, 'ideographic'), + (None, 'k'), + (None, 'keyPoints'), + (None, 'keySplines'), + (None, 'keyTimes'), + (None, 'lang'), + (None, 'marker-end'), + (None, 'marker-mid'), + (None, 'marker-start'), + (None, 'markerHeight'), + (None, 'markerUnits'), + (None, 'markerWidth'), + (None, 'mathematical'), + (None, 'max'), + (None, 'min'), + (None, 'name'), + (None, 'offset'), + (None, 'opacity'), + (None, 'orient'), + (None, 'origin'), + (None, 'overline-position'), + (None, 'overline-thickness'), + (None, 'panose-1'), + (None, 'path'), + (None, 'pathLength'), + (None, 'points'), + (None, 'preserveAspectRatio'), + (None, 'r'), + (None, 'refX'), + (None, 'refY'), + (None, 'repeatCount'), + (None, 'repeatDur'), + (None, 'requiredExtensions'), + (None, 'requiredFeatures'), + (None, 'restart'), + (None, 'rotate'), + (None, 'rx'), + (None, 'ry'), + (None, 'slope'), + (None, 'stemh'), + (None, 'stemv'), + (None, 'stop-color'), + (None, 'stop-opacity'), + (None, 'strikethrough-position'), + (None, 'strikethrough-thickness'), + (None, 'stroke'), + (None, 'stroke-dasharray'), + (None, 'stroke-dashoffset'), + (None, 'stroke-linecap'), + (None, 'stroke-linejoin'), + (None, 'stroke-miterlimit'), + (None, 'stroke-opacity'), + (None, 'stroke-width'), + (None, 'systemLanguage'), + (None, 'target'), + (None, 'text-anchor'), + (None, 'to'), + (None, 'transform'), + (None, 'type'), + (None, 'u1'), + (None, 'u2'), + (None, 'underline-position'), + (None, 'underline-thickness'), + (None, 'unicode'), + (None, 'unicode-range'), + (None, 'units-per-em'), + (None, 'values'), + (None, 'version'), + (None, 'viewBox'), + (None, 'visibility'), + (None, 'width'), + (None, 'widths'), + (None, 'x'), + (None, 'x-height'), + (None, 'x1'), + (None, 'x2'), + (namespaces['xlink'], 'actuate'), + (namespaces['xlink'], 'arcrole'), + (namespaces['xlink'], 'href'), + (namespaces['xlink'], 'role'), + (namespaces['xlink'], 'show'), + (namespaces['xlink'], 'title'), + (namespaces['xlink'], 'type'), + (namespaces['xml'], 'base'), + (namespaces['xml'], 'lang'), + (namespaces['xml'], 'space'), + (None, 'y'), + (None, 'y1'), + (None, 'y2'), + (None, 'zoomAndPan'), +)) + +attr_val_is_uri = frozenset(( + (None, 'href'), + (None, 'src'), + (None, 'cite'), + (None, 'action'), + (None, 'longdesc'), + (None, 'poster'), + (None, 'background'), + (None, 'datasrc'), + (None, 'dynsrc'), + (None, 'lowsrc'), + (None, 'ping'), + (namespaces['xlink'], 'href'), + (namespaces['xml'], 'base'), +)) + +svg_attr_val_allows_ref = frozenset(( + (None, 'clip-path'), + (None, 'color-profile'), + (None, 'cursor'), + (None, 'fill'), + (None, 'filter'), + (None, 'marker'), + (None, 'marker-start'), + (None, 'marker-mid'), + (None, 'marker-end'), + (None, 'mask'), + (None, 'stroke'), +)) + +svg_allow_local_href = frozenset(( + (None, 'altGlyph'), + (None, 'animate'), + (None, 'animateColor'), + (None, 'animateMotion'), + (None, 'animateTransform'), + (None, 'cursor'), + (None, 'feImage'), + (None, 'filter'), + (None, 'linearGradient'), + (None, 'pattern'), + (None, 'radialGradient'), + (None, 'textpath'), + (None, 'tref'), + (None, 'set'), + (None, 'use') +)) + +allowed_css_properties = frozenset(( + 'azimuth', + 'background-color', + 'border-bottom-color', + 'border-collapse', + 'border-color', + 'border-left-color', + 'border-right-color', + 'border-top-color', + 'clear', + 'color', + 'cursor', + 'direction', + 'display', + 'elevation', + 'float', + 'font', + 'font-family', + 'font-size', + 'font-style', + 'font-variant', + 'font-weight', + 'height', + 'letter-spacing', + 'line-height', + 'overflow', + 'pause', + 'pause-after', + 'pause-before', + 'pitch', + 'pitch-range', + 'richness', + 'speak', + 'speak-header', + 'speak-numeral', + 'speak-punctuation', + 'speech-rate', + 'stress', + 'text-align', + 'text-decoration', + 'text-indent', + 'unicode-bidi', + 'vertical-align', + 'voice-family', + 'volume', + 'white-space', + 'width', +)) + +allowed_css_keywords = frozenset(( + 'auto', + 'aqua', + 'black', + 'block', + 'blue', + 'bold', + 'both', + 'bottom', + 'brown', + 'center', + 'collapse', + 'dashed', + 'dotted', + 'fuchsia', + 'gray', + 'green', + '!important', + 'italic', + 'left', + 'lime', + 'maroon', + 'medium', + 'none', + 'navy', + 'normal', + 'nowrap', + 'olive', + 'pointer', + 'purple', + 'red', + 'right', + 'solid', + 'silver', + 'teal', + 'top', + 'transparent', + 'underline', + 'white', + 'yellow', +)) + +allowed_svg_properties = frozenset(( + 'fill', + 'fill-opacity', + 'fill-rule', + 'stroke', + 'stroke-width', + 'stroke-linecap', + 'stroke-linejoin', + 'stroke-opacity', +)) + +allowed_protocols = frozenset(( + 'ed2k', + 'ftp', + 'http', + 'https', + 'irc', + 'mailto', + 'news', + 'gopher', + 'nntp', + 'telnet', + 'webcal', + 'xmpp', + 'callto', + 'feed', + 'urn', + 'aim', + 'rsync', + 'tag', + 'ssh', + 'sftp', + 'rtsp', + 'afs', + 'data', +)) + +allowed_content_types = frozenset(( + 'image/png', + 'image/jpeg', + 'image/gif', + 'image/webp', + 'image/bmp', + 'text/plain', +)) + + +data_content_type = re.compile(r''' + ^ + # Match a content type <application>/<type> + (?P<content_type>[-a-zA-Z0-9.]+/[-a-zA-Z0-9.]+) + # Match any character set and encoding + (?:(?:;charset=(?:[-a-zA-Z0-9]+)(?:;(?:base64))?) + |(?:;(?:base64))?(?:;charset=(?:[-a-zA-Z0-9]+))?) + # Assume the rest is data + ,.* + $ + ''', + re.VERBOSE) + + +class Filter(base.Filter): + """Sanitizes token stream of XHTML+MathML+SVG and of inline style attributes""" + def __init__(self, + source, + allowed_elements=allowed_elements, + allowed_attributes=allowed_attributes, + allowed_css_properties=allowed_css_properties, + allowed_css_keywords=allowed_css_keywords, + allowed_svg_properties=allowed_svg_properties, + allowed_protocols=allowed_protocols, + allowed_content_types=allowed_content_types, + attr_val_is_uri=attr_val_is_uri, + svg_attr_val_allows_ref=svg_attr_val_allows_ref, + svg_allow_local_href=svg_allow_local_href): + """Creates a Filter + + :arg allowed_elements: set of elements to allow--everything else will + be escaped + + :arg allowed_attributes: set of attributes to allow in + elements--everything else will be stripped + + :arg allowed_css_properties: set of CSS properties to allow--everything + else will be stripped + + :arg allowed_css_keywords: set of CSS keywords to allow--everything + else will be stripped + + :arg allowed_svg_properties: set of SVG properties to allow--everything + else will be removed + + :arg allowed_protocols: set of allowed protocols for URIs + + :arg allowed_content_types: set of allowed content types for ``data`` URIs. + + :arg attr_val_is_uri: set of attributes that have URI values--values + that have a scheme not listed in ``allowed_protocols`` are removed + + :arg svg_attr_val_allows_ref: set of SVG attributes that can have + references + + :arg svg_allow_local_href: set of SVG elements that can have local + hrefs--these are removed + + """ + super(Filter, self).__init__(source) + self.allowed_elements = allowed_elements + self.allowed_attributes = allowed_attributes + self.allowed_css_properties = allowed_css_properties + self.allowed_css_keywords = allowed_css_keywords + self.allowed_svg_properties = allowed_svg_properties + self.allowed_protocols = allowed_protocols + self.allowed_content_types = allowed_content_types + self.attr_val_is_uri = attr_val_is_uri + self.svg_attr_val_allows_ref = svg_attr_val_allows_ref + self.svg_allow_local_href = svg_allow_local_href + + def __iter__(self): + for token in base.Filter.__iter__(self): + token = self.sanitize_token(token) + if token: + yield token + + # Sanitize the +html+, escaping all elements not in ALLOWED_ELEMENTS, and + # stripping out all attributes not in ALLOWED_ATTRIBUTES. Style attributes + # are parsed, and a restricted set, specified by ALLOWED_CSS_PROPERTIES and + # ALLOWED_CSS_KEYWORDS, are allowed through. attributes in ATTR_VAL_IS_URI + # are scanned, and only URI schemes specified in ALLOWED_PROTOCOLS are + # allowed. + # + # sanitize_html('<script> do_nasty_stuff() </script>') + # => <script> do_nasty_stuff() </script> + # sanitize_html('<a href="javascript: sucker();">Click here for $100</a>') + # => <a>Click here for $100</a> + def sanitize_token(self, token): + + # accommodate filters which use token_type differently + token_type = token["type"] + if token_type in ("StartTag", "EndTag", "EmptyTag"): + name = token["name"] + namespace = token["namespace"] + if ((namespace, name) in self.allowed_elements or + (namespace is None and + (namespaces["html"], name) in self.allowed_elements)): + return self.allowed_token(token) + else: + return self.disallowed_token(token) + elif token_type == "Comment": + pass + else: + return token + + def allowed_token(self, token): + if "data" in token: + attrs = token["data"] + attr_names = set(attrs.keys()) + + # Remove forbidden attributes + for to_remove in (attr_names - self.allowed_attributes): + del token["data"][to_remove] + attr_names.remove(to_remove) + + # Remove attributes with disallowed URL values + for attr in (attr_names & self.attr_val_is_uri): + assert attr in attrs + # I don't have a clue where this regexp comes from or why it matches those + # characters, nor why we call unescape. I just know it's always been here. + # Should you be worried by this comment in a sanitizer? Yes. On the other hand, all + # this will do is remove *more* than it otherwise would. + val_unescaped = re.sub("[`\x00-\x20\x7f-\xa0\\s]+", '', + unescape(attrs[attr])).lower() + # remove replacement characters from unescaped characters + val_unescaped = val_unescaped.replace("\ufffd", "") + try: + uri = urlparse.urlparse(val_unescaped) + except ValueError: + uri = None + del attrs[attr] + if uri and uri.scheme: + if uri.scheme not in self.allowed_protocols: + del attrs[attr] + if uri.scheme == 'data': + m = data_content_type.match(uri.path) + if not m: + del attrs[attr] + elif m.group('content_type') not in self.allowed_content_types: + del attrs[attr] + + for attr in self.svg_attr_val_allows_ref: + if attr in attrs: + attrs[attr] = re.sub(r'url\s*\(\s*[^#\s][^)]+?\)', + ' ', + unescape(attrs[attr])) + if (token["name"] in self.svg_allow_local_href and + (namespaces['xlink'], 'href') in attrs and re.search(r'^\s*[^#\s].*', + attrs[(namespaces['xlink'], 'href')])): + del attrs[(namespaces['xlink'], 'href')] + if (None, 'style') in attrs: + attrs[(None, 'style')] = self.sanitize_css(attrs[(None, 'style')]) + token["data"] = attrs + return token + + def disallowed_token(self, token): + token_type = token["type"] + if token_type == "EndTag": + token["data"] = "</%s>" % token["name"] + elif token["data"]: + assert token_type in ("StartTag", "EmptyTag") + attrs = [] + for (ns, name), v in token["data"].items(): + attrs.append(' %s="%s"' % (name if ns is None else "%s:%s" % (prefixes[ns], name), escape(v))) + token["data"] = "<%s%s>" % (token["name"], ''.join(attrs)) + else: + token["data"] = "<%s>" % token["name"] + if token.get("selfClosing"): + token["data"] = token["data"][:-1] + "/>" + + token["type"] = "Characters" + + del token["name"] + return token + + def sanitize_css(self, style): + # disallow urls + style = re.compile(r'url\s*\(\s*[^\s)]+?\s*\)\s*').sub(' ', style) + + # gauntlet + if not re.match(r"""^([:,;#%.\sa-zA-Z0-9!]|\w-\w|'[\s\w]+'|"[\s\w]+"|\([\d,\s]+\))*$""", style): + return '' + if not re.match(r"^\s*([-\w]+\s*:[^:;]*(;\s*|$))*$", style): + return '' + + clean = [] + for prop, value in re.findall(r"([-\w]+)\s*:\s*([^:;]*)", style): + if not value: + continue + if prop.lower() in self.allowed_css_properties: + clean.append(prop + ': ' + value + ';') + elif prop.split('-')[0].lower() in ['background', 'border', 'margin', + 'padding']: + for keyword in value.split(): + if keyword not in self.allowed_css_keywords and \ + not re.match(r"^(#[0-9a-fA-F]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$", keyword): # noqa + break + else: + clean.append(prop + ': ' + value + ';') + elif prop.lower() in self.allowed_svg_properties: + clean.append(prop + ': ' + value + ';') + + return ' '.join(clean) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/filters/whitespace.py b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/whitespace.py new file mode 100644 index 0000000..0d12584 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/whitespace.py @@ -0,0 +1,38 @@ +from __future__ import absolute_import, division, unicode_literals + +import re + +from . import base +from ..constants import rcdataElements, spaceCharacters +spaceCharacters = "".join(spaceCharacters) + +SPACES_REGEX = re.compile("[%s]+" % spaceCharacters) + + +class Filter(base.Filter): + """Collapses whitespace except in pre, textarea, and script elements""" + spacePreserveElements = frozenset(["pre", "textarea"] + list(rcdataElements)) + + def __iter__(self): + preserve = 0 + for token in base.Filter.__iter__(self): + type = token["type"] + if type == "StartTag" \ + and (preserve or token["name"] in self.spacePreserveElements): + preserve += 1 + + elif type == "EndTag" and preserve: + preserve -= 1 + + elif not preserve and type == "SpaceCharacters" and token["data"]: + # Test on token["data"] above to not introduce spaces where there were not + token["data"] = " " + + elif not preserve and type == "Characters": + token["data"] = collapse_spaces(token["data"]) + + yield token + + +def collapse_spaces(text): + return SPACES_REGEX.sub(' ', text) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/html5parser.py b/venv/Lib/site-packages/pip/_vendor/html5lib/html5parser.py new file mode 100644 index 0000000..ae41a13 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/html5parser.py @@ -0,0 +1,2791 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import with_metaclass, viewkeys + +import types +from collections import OrderedDict + +from . import _inputstream +from . import _tokenizer + +from . import treebuilders +from .treebuilders.base import Marker + +from . import _utils +from .constants import ( + spaceCharacters, asciiUpper2Lower, + specialElements, headingElements, cdataElements, rcdataElements, + tokenTypes, tagTokenTypes, + namespaces, + htmlIntegrationPointElements, mathmlTextIntegrationPointElements, + adjustForeignAttributes as adjustForeignAttributesMap, + adjustMathMLAttributes, adjustSVGAttributes, + E, + _ReparseException +) + + +def parse(doc, treebuilder="etree", namespaceHTMLElements=True, **kwargs): + """Parse an HTML document as a string or file-like object into a tree + + :arg doc: the document to parse as a string or file-like object + + :arg treebuilder: the treebuilder to use when parsing + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :returns: parsed tree + + Example: + + >>> from html5lib.html5parser import parse + >>> parse('<html><body><p>This is a doc</p></body></html>') + <Element u'{http://www.w3.org/1999/xhtml}html' at 0x7feac4909db0> + + """ + tb = treebuilders.getTreeBuilder(treebuilder) + p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) + return p.parse(doc, **kwargs) + + +def parseFragment(doc, container="div", treebuilder="etree", namespaceHTMLElements=True, **kwargs): + """Parse an HTML fragment as a string or file-like object into a tree + + :arg doc: the fragment to parse as a string or file-like object + + :arg container: the container context to parse the fragment in + + :arg treebuilder: the treebuilder to use when parsing + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :returns: parsed tree + + Example: + + >>> from html5lib.html5libparser import parseFragment + >>> parseFragment('<b>this is a fragment</b>') + <Element u'DOCUMENT_FRAGMENT' at 0x7feac484b090> + + """ + tb = treebuilders.getTreeBuilder(treebuilder) + p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) + return p.parseFragment(doc, container=container, **kwargs) + + +def method_decorator_metaclass(function): + class Decorated(type): + def __new__(meta, classname, bases, classDict): + for attributeName, attribute in classDict.items(): + if isinstance(attribute, types.FunctionType): + attribute = function(attribute) + + classDict[attributeName] = attribute + return type.__new__(meta, classname, bases, classDict) + return Decorated + + +class HTMLParser(object): + """HTML parser + + Generates a tree structure from a stream of (possibly malformed) HTML. + + """ + + def __init__(self, tree=None, strict=False, namespaceHTMLElements=True, debug=False): + """ + :arg tree: a treebuilder class controlling the type of tree that will be + returned. Built in treebuilders can be accessed through + html5lib.treebuilders.getTreeBuilder(treeType) + + :arg strict: raise an exception when a parse error is encountered + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :arg debug: whether or not to enable debug mode which logs things + + Example: + + >>> from html5lib.html5parser import HTMLParser + >>> parser = HTMLParser() # generates parser with etree builder + >>> parser = HTMLParser('lxml', strict=True) # generates parser with lxml builder which is strict + + """ + + # Raise an exception on the first error encountered + self.strict = strict + + if tree is None: + tree = treebuilders.getTreeBuilder("etree") + self.tree = tree(namespaceHTMLElements) + self.errors = [] + + self.phases = dict([(name, cls(self, self.tree)) for name, cls in + getPhases(debug).items()]) + + def _parse(self, stream, innerHTML=False, container="div", scripting=False, **kwargs): + + self.innerHTMLMode = innerHTML + self.container = container + self.scripting = scripting + self.tokenizer = _tokenizer.HTMLTokenizer(stream, parser=self, **kwargs) + self.reset() + + try: + self.mainLoop() + except _ReparseException: + self.reset() + self.mainLoop() + + def reset(self): + self.tree.reset() + self.firstStartTag = False + self.errors = [] + self.log = [] # only used with debug mode + # "quirks" / "limited quirks" / "no quirks" + self.compatMode = "no quirks" + + if self.innerHTMLMode: + self.innerHTML = self.container.lower() + + if self.innerHTML in cdataElements: + self.tokenizer.state = self.tokenizer.rcdataState + elif self.innerHTML in rcdataElements: + self.tokenizer.state = self.tokenizer.rawtextState + elif self.innerHTML == 'plaintext': + self.tokenizer.state = self.tokenizer.plaintextState + else: + # state already is data state + # self.tokenizer.state = self.tokenizer.dataState + pass + self.phase = self.phases["beforeHtml"] + self.phase.insertHtmlElement() + self.resetInsertionMode() + else: + self.innerHTML = False # pylint:disable=redefined-variable-type + self.phase = self.phases["initial"] + + self.lastPhase = None + + self.beforeRCDataPhase = None + + self.framesetOK = True + + @property + def documentEncoding(self): + """Name of the character encoding that was used to decode the input stream, or + :obj:`None` if that is not determined yet + + """ + if not hasattr(self, 'tokenizer'): + return None + return self.tokenizer.stream.charEncoding[0].name + + def isHTMLIntegrationPoint(self, element): + if (element.name == "annotation-xml" and + element.namespace == namespaces["mathml"]): + return ("encoding" in element.attributes and + element.attributes["encoding"].translate( + asciiUpper2Lower) in + ("text/html", "application/xhtml+xml")) + else: + return (element.namespace, element.name) in htmlIntegrationPointElements + + def isMathMLTextIntegrationPoint(self, element): + return (element.namespace, element.name) in mathmlTextIntegrationPointElements + + def mainLoop(self): + CharactersToken = tokenTypes["Characters"] + SpaceCharactersToken = tokenTypes["SpaceCharacters"] + StartTagToken = tokenTypes["StartTag"] + EndTagToken = tokenTypes["EndTag"] + CommentToken = tokenTypes["Comment"] + DoctypeToken = tokenTypes["Doctype"] + ParseErrorToken = tokenTypes["ParseError"] + + for token in self.normalizedTokens(): + prev_token = None + new_token = token + while new_token is not None: + prev_token = new_token + currentNode = self.tree.openElements[-1] if self.tree.openElements else None + currentNodeNamespace = currentNode.namespace if currentNode else None + currentNodeName = currentNode.name if currentNode else None + + type = new_token["type"] + + if type == ParseErrorToken: + self.parseError(new_token["data"], new_token.get("datavars", {})) + new_token = None + else: + if (len(self.tree.openElements) == 0 or + currentNodeNamespace == self.tree.defaultNamespace or + (self.isMathMLTextIntegrationPoint(currentNode) and + ((type == StartTagToken and + token["name"] not in frozenset(["mglyph", "malignmark"])) or + type in (CharactersToken, SpaceCharactersToken))) or + (currentNodeNamespace == namespaces["mathml"] and + currentNodeName == "annotation-xml" and + type == StartTagToken and + token["name"] == "svg") or + (self.isHTMLIntegrationPoint(currentNode) and + type in (StartTagToken, CharactersToken, SpaceCharactersToken))): + phase = self.phase + else: + phase = self.phases["inForeignContent"] + + if type == CharactersToken: + new_token = phase.processCharacters(new_token) + elif type == SpaceCharactersToken: + new_token = phase.processSpaceCharacters(new_token) + elif type == StartTagToken: + new_token = phase.processStartTag(new_token) + elif type == EndTagToken: + new_token = phase.processEndTag(new_token) + elif type == CommentToken: + new_token = phase.processComment(new_token) + elif type == DoctypeToken: + new_token = phase.processDoctype(new_token) + + if (type == StartTagToken and prev_token["selfClosing"] and + not prev_token["selfClosingAcknowledged"]): + self.parseError("non-void-element-with-trailing-solidus", + {"name": prev_token["name"]}) + + # When the loop finishes it's EOF + reprocess = True + phases = [] + while reprocess: + phases.append(self.phase) + reprocess = self.phase.processEOF() + if reprocess: + assert self.phase not in phases + + def normalizedTokens(self): + for token in self.tokenizer: + yield self.normalizeToken(token) + + def parse(self, stream, *args, **kwargs): + """Parse a HTML document into a well-formed tree + + :arg stream: a file-like object or string containing the HTML to be parsed + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element). + + :arg scripting: treat noscript elements as if JavaScript was turned on + + :returns: parsed tree + + Example: + + >>> from html5lib.html5parser import HTMLParser + >>> parser = HTMLParser() + >>> parser.parse('<html><body><p>This is a doc</p></body></html>') + <Element u'{http://www.w3.org/1999/xhtml}html' at 0x7feac4909db0> + + """ + self._parse(stream, False, None, *args, **kwargs) + return self.tree.getDocument() + + def parseFragment(self, stream, *args, **kwargs): + """Parse a HTML fragment into a well-formed tree fragment + + :arg container: name of the element we're setting the innerHTML + property if set to None, default to 'div' + + :arg stream: a file-like object or string containing the HTML to be parsed + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + :arg scripting: treat noscript elements as if JavaScript was turned on + + :returns: parsed tree + + Example: + + >>> from html5lib.html5libparser import HTMLParser + >>> parser = HTMLParser() + >>> parser.parseFragment('<b>this is a fragment</b>') + <Element u'DOCUMENT_FRAGMENT' at 0x7feac484b090> + + """ + self._parse(stream, True, *args, **kwargs) + return self.tree.getFragment() + + def parseError(self, errorcode="XXX-undefined-error", datavars=None): + # XXX The idea is to make errorcode mandatory. + if datavars is None: + datavars = {} + self.errors.append((self.tokenizer.stream.position(), errorcode, datavars)) + if self.strict: + raise ParseError(E[errorcode] % datavars) + + def normalizeToken(self, token): + # HTML5 specific normalizations to the token stream + if token["type"] == tokenTypes["StartTag"]: + raw = token["data"] + token["data"] = OrderedDict(raw) + if len(raw) > len(token["data"]): + # we had some duplicated attribute, fix so first wins + token["data"].update(raw[::-1]) + + return token + + def adjustMathMLAttributes(self, token): + adjust_attributes(token, adjustMathMLAttributes) + + def adjustSVGAttributes(self, token): + adjust_attributes(token, adjustSVGAttributes) + + def adjustForeignAttributes(self, token): + adjust_attributes(token, adjustForeignAttributesMap) + + def reparseTokenNormal(self, token): + # pylint:disable=unused-argument + self.parser.phase() + + def resetInsertionMode(self): + # The name of this method is mostly historical. (It's also used in the + # specification.) + last = False + newModes = { + "select": "inSelect", + "td": "inCell", + "th": "inCell", + "tr": "inRow", + "tbody": "inTableBody", + "thead": "inTableBody", + "tfoot": "inTableBody", + "caption": "inCaption", + "colgroup": "inColumnGroup", + "table": "inTable", + "head": "inBody", + "body": "inBody", + "frameset": "inFrameset", + "html": "beforeHead" + } + for node in self.tree.openElements[::-1]: + nodeName = node.name + new_phase = None + if node == self.tree.openElements[0]: + assert self.innerHTML + last = True + nodeName = self.innerHTML + # Check for conditions that should only happen in the innerHTML + # case + if nodeName in ("select", "colgroup", "head", "html"): + assert self.innerHTML + + if not last and node.namespace != self.tree.defaultNamespace: + continue + + if nodeName in newModes: + new_phase = self.phases[newModes[nodeName]] + break + elif last: + new_phase = self.phases["inBody"] + break + + self.phase = new_phase + + def parseRCDataRawtext(self, token, contentType): + # Generic RCDATA/RAWTEXT Parsing algorithm + assert contentType in ("RAWTEXT", "RCDATA") + + self.tree.insertElement(token) + + if contentType == "RAWTEXT": + self.tokenizer.state = self.tokenizer.rawtextState + else: + self.tokenizer.state = self.tokenizer.rcdataState + + self.originalPhase = self.phase + + self.phase = self.phases["text"] + + +@_utils.memoize +def getPhases(debug): + def log(function): + """Logger that records which phase processes each token""" + type_names = dict((value, key) for key, value in + tokenTypes.items()) + + def wrapped(self, *args, **kwargs): + if function.__name__.startswith("process") and len(args) > 0: + token = args[0] + try: + info = {"type": type_names[token['type']]} + except: + raise + if token['type'] in tagTokenTypes: + info["name"] = token['name'] + + self.parser.log.append((self.parser.tokenizer.state.__name__, + self.parser.phase.__class__.__name__, + self.__class__.__name__, + function.__name__, + info)) + return function(self, *args, **kwargs) + else: + return function(self, *args, **kwargs) + return wrapped + + def getMetaclass(use_metaclass, metaclass_func): + if use_metaclass: + return method_decorator_metaclass(metaclass_func) + else: + return type + + # pylint:disable=unused-argument + class Phase(with_metaclass(getMetaclass(debug, log))): + """Base class for helper object that implements each phase of processing + """ + + def __init__(self, parser, tree): + self.parser = parser + self.tree = tree + + def processEOF(self): + raise NotImplementedError + + def processComment(self, token): + # For most phases the following is correct. Where it's not it will be + # overridden. + self.tree.insertComment(token, self.tree.openElements[-1]) + + def processDoctype(self, token): + self.parser.parseError("unexpected-doctype") + + def processCharacters(self, token): + self.tree.insertText(token["data"]) + + def processSpaceCharacters(self, token): + self.tree.insertText(token["data"]) + + def processStartTag(self, token): + return self.startTagHandler[token["name"]](token) + + def startTagHtml(self, token): + if not self.parser.firstStartTag and token["name"] == "html": + self.parser.parseError("non-html-root") + # XXX Need a check here to see if the first start tag token emitted is + # this token... If it's not, invoke self.parser.parseError(). + for attr, value in token["data"].items(): + if attr not in self.tree.openElements[0].attributes: + self.tree.openElements[0].attributes[attr] = value + self.parser.firstStartTag = False + + def processEndTag(self, token): + return self.endTagHandler[token["name"]](token) + + class InitialPhase(Phase): + def processSpaceCharacters(self, token): + pass + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + correct = token["correct"] + + if (name != "html" or publicId is not None or + systemId is not None and systemId != "about:legacy-compat"): + self.parser.parseError("unknown-doctype") + + if publicId is None: + publicId = "" + + self.tree.insertDoctype(token) + + if publicId != "": + publicId = publicId.translate(asciiUpper2Lower) + + if (not correct or token["name"] != "html" or + publicId.startswith( + ("+//silmaril//dtd html pro v0r11 19970101//", + "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", + "-//as//dtd html 3.0 aswedit + extensions//", + "-//ietf//dtd html 2.0 level 1//", + "-//ietf//dtd html 2.0 level 2//", + "-//ietf//dtd html 2.0 strict level 1//", + "-//ietf//dtd html 2.0 strict level 2//", + "-//ietf//dtd html 2.0 strict//", + "-//ietf//dtd html 2.0//", + "-//ietf//dtd html 2.1e//", + "-//ietf//dtd html 3.0//", + "-//ietf//dtd html 3.2 final//", + "-//ietf//dtd html 3.2//", + "-//ietf//dtd html 3//", + "-//ietf//dtd html level 0//", + "-//ietf//dtd html level 1//", + "-//ietf//dtd html level 2//", + "-//ietf//dtd html level 3//", + "-//ietf//dtd html strict level 0//", + "-//ietf//dtd html strict level 1//", + "-//ietf//dtd html strict level 2//", + "-//ietf//dtd html strict level 3//", + "-//ietf//dtd html strict//", + "-//ietf//dtd html//", + "-//metrius//dtd metrius presentational//", + "-//microsoft//dtd internet explorer 2.0 html strict//", + "-//microsoft//dtd internet explorer 2.0 html//", + "-//microsoft//dtd internet explorer 2.0 tables//", + "-//microsoft//dtd internet explorer 3.0 html strict//", + "-//microsoft//dtd internet explorer 3.0 html//", + "-//microsoft//dtd internet explorer 3.0 tables//", + "-//netscape comm. corp.//dtd html//", + "-//netscape comm. corp.//dtd strict html//", + "-//o'reilly and associates//dtd html 2.0//", + "-//o'reilly and associates//dtd html extended 1.0//", + "-//o'reilly and associates//dtd html extended relaxed 1.0//", + "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", + "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", + "-//spyglass//dtd html 2.0 extended//", + "-//sq//dtd html 2.0 hotmetal + extensions//", + "-//sun microsystems corp.//dtd hotjava html//", + "-//sun microsystems corp.//dtd hotjava strict html//", + "-//w3c//dtd html 3 1995-03-24//", + "-//w3c//dtd html 3.2 draft//", + "-//w3c//dtd html 3.2 final//", + "-//w3c//dtd html 3.2//", + "-//w3c//dtd html 3.2s draft//", + "-//w3c//dtd html 4.0 frameset//", + "-//w3c//dtd html 4.0 transitional//", + "-//w3c//dtd html experimental 19960712//", + "-//w3c//dtd html experimental 970421//", + "-//w3c//dtd w3 html//", + "-//w3o//dtd w3 html 3.0//", + "-//webtechs//dtd mozilla html 2.0//", + "-//webtechs//dtd mozilla html//")) or + publicId in ("-//w3o//dtd w3 html strict 3.0//en//", + "-/w3c/dtd html 4.0 transitional/en", + "html") or + publicId.startswith( + ("-//w3c//dtd html 4.01 frameset//", + "-//w3c//dtd html 4.01 transitional//")) and + systemId is None or + systemId and systemId.lower() == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"): + self.parser.compatMode = "quirks" + elif (publicId.startswith( + ("-//w3c//dtd xhtml 1.0 frameset//", + "-//w3c//dtd xhtml 1.0 transitional//")) or + publicId.startswith( + ("-//w3c//dtd html 4.01 frameset//", + "-//w3c//dtd html 4.01 transitional//")) and + systemId is not None): + self.parser.compatMode = "limited quirks" + + self.parser.phase = self.parser.phases["beforeHtml"] + + def anythingElse(self): + self.parser.compatMode = "quirks" + self.parser.phase = self.parser.phases["beforeHtml"] + + def processCharacters(self, token): + self.parser.parseError("expected-doctype-but-got-chars") + self.anythingElse() + return token + + def processStartTag(self, token): + self.parser.parseError("expected-doctype-but-got-start-tag", + {"name": token["name"]}) + self.anythingElse() + return token + + def processEndTag(self, token): + self.parser.parseError("expected-doctype-but-got-end-tag", + {"name": token["name"]}) + self.anythingElse() + return token + + def processEOF(self): + self.parser.parseError("expected-doctype-but-got-eof") + self.anythingElse() + return True + + class BeforeHtmlPhase(Phase): + # helper methods + def insertHtmlElement(self): + self.tree.insertRoot(impliedTagToken("html", "StartTag")) + self.parser.phase = self.parser.phases["beforeHead"] + + # other + def processEOF(self): + self.insertHtmlElement() + return True + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processSpaceCharacters(self, token): + pass + + def processCharacters(self, token): + self.insertHtmlElement() + return token + + def processStartTag(self, token): + if token["name"] == "html": + self.parser.firstStartTag = True + self.insertHtmlElement() + return token + + def processEndTag(self, token): + if token["name"] not in ("head", "body", "html", "br"): + self.parser.parseError("unexpected-end-tag-before-html", + {"name": token["name"]}) + else: + self.insertHtmlElement() + return token + + class BeforeHeadPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("head", self.startTagHead) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + (("head", "body", "html", "br"), self.endTagImplyHead) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.startTagHead(impliedTagToken("head", "StartTag")) + return True + + def processSpaceCharacters(self, token): + pass + + def processCharacters(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagHead(self, token): + self.tree.insertElement(token) + self.tree.headPointer = self.tree.openElements[-1] + self.parser.phase = self.parser.phases["inHead"] + + def startTagOther(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def endTagImplyHead(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def endTagOther(self, token): + self.parser.parseError("end-tag-after-implied-root", + {"name": token["name"]}) + + class InHeadPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("title", self.startTagTitle), + (("noframes", "style"), self.startTagNoFramesStyle), + ("noscript", self.startTagNoscript), + ("script", self.startTagScript), + (("base", "basefont", "bgsound", "command", "link"), + self.startTagBaseLinkCommand), + ("meta", self.startTagMeta), + ("head", self.startTagHead) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("head", self.endTagHead), + (("br", "html", "body"), self.endTagHtmlBodyBr) + ]) + self.endTagHandler.default = self.endTagOther + + # the real thing + def processEOF(self): + self.anythingElse() + return True + + def processCharacters(self, token): + self.anythingElse() + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagHead(self, token): + self.parser.parseError("two-heads-are-not-better-than-one") + + def startTagBaseLinkCommand(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagMeta(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + attributes = token["data"] + if self.parser.tokenizer.stream.charEncoding[1] == "tentative": + if "charset" in attributes: + self.parser.tokenizer.stream.changeEncoding(attributes["charset"]) + elif ("content" in attributes and + "http-equiv" in attributes and + attributes["http-equiv"].lower() == "content-type"): + # Encoding it as UTF-8 here is a hack, as really we should pass + # the abstract Unicode string, and just use the + # ContentAttrParser on that, but using UTF-8 allows all chars + # to be encoded and as a ASCII-superset works. + data = _inputstream.EncodingBytes(attributes["content"].encode("utf-8")) + parser = _inputstream.ContentAttrParser(data) + codec = parser.parse() + self.parser.tokenizer.stream.changeEncoding(codec) + + def startTagTitle(self, token): + self.parser.parseRCDataRawtext(token, "RCDATA") + + def startTagNoFramesStyle(self, token): + # Need to decide whether to implement the scripting-disabled case + self.parser.parseRCDataRawtext(token, "RAWTEXT") + + def startTagNoscript(self, token): + if self.parser.scripting: + self.parser.parseRCDataRawtext(token, "RAWTEXT") + else: + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inHeadNoscript"] + + def startTagScript(self, token): + self.tree.insertElement(token) + self.parser.tokenizer.state = self.parser.tokenizer.scriptDataState + self.parser.originalPhase = self.parser.phase + self.parser.phase = self.parser.phases["text"] + + def startTagOther(self, token): + self.anythingElse() + return token + + def endTagHead(self, token): + node = self.parser.tree.openElements.pop() + assert node.name == "head", "Expected head got %s" % node.name + self.parser.phase = self.parser.phases["afterHead"] + + def endTagHtmlBodyBr(self, token): + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + self.endTagHead(impliedTagToken("head")) + + class InHeadNoscriptPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("basefont", "bgsound", "link", "meta", "noframes", "style"), self.startTagBaseLinkCommand), + (("head", "noscript"), self.startTagHeadNoscript), + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("noscript", self.endTagNoscript), + ("br", self.endTagBr), + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.parser.parseError("eof-in-head-noscript") + self.anythingElse() + return True + + def processComment(self, token): + return self.parser.phases["inHead"].processComment(token) + + def processCharacters(self, token): + self.parser.parseError("char-in-head-noscript") + self.anythingElse() + return token + + def processSpaceCharacters(self, token): + return self.parser.phases["inHead"].processSpaceCharacters(token) + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagBaseLinkCommand(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagHeadNoscript(self, token): + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + + def startTagOther(self, token): + self.parser.parseError("unexpected-inhead-noscript-tag", {"name": token["name"]}) + self.anythingElse() + return token + + def endTagNoscript(self, token): + node = self.parser.tree.openElements.pop() + assert node.name == "noscript", "Expected noscript got %s" % node.name + self.parser.phase = self.parser.phases["inHead"] + + def endTagBr(self, token): + self.parser.parseError("unexpected-inhead-noscript-tag", {"name": token["name"]}) + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + # Caller must raise parse error first! + self.endTagNoscript(impliedTagToken("noscript")) + + class AfterHeadPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("body", self.startTagBody), + ("frameset", self.startTagFrameset), + (("base", "basefont", "bgsound", "link", "meta", "noframes", "script", + "style", "title"), + self.startTagFromHead), + ("head", self.startTagHead) + ]) + self.startTagHandler.default = self.startTagOther + self.endTagHandler = _utils.MethodDispatcher([(("body", "html", "br"), + self.endTagHtmlBodyBr)]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.anythingElse() + return True + + def processCharacters(self, token): + self.anythingElse() + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagBody(self, token): + self.parser.framesetOK = False + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inBody"] + + def startTagFrameset(self, token): + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inFrameset"] + + def startTagFromHead(self, token): + self.parser.parseError("unexpected-start-tag-out-of-my-head", + {"name": token["name"]}) + self.tree.openElements.append(self.tree.headPointer) + self.parser.phases["inHead"].processStartTag(token) + for node in self.tree.openElements[::-1]: + if node.name == "head": + self.tree.openElements.remove(node) + break + + def startTagHead(self, token): + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + + def startTagOther(self, token): + self.anythingElse() + return token + + def endTagHtmlBodyBr(self, token): + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + self.tree.insertElement(impliedTagToken("body", "StartTag")) + self.parser.phase = self.parser.phases["inBody"] + self.parser.framesetOK = True + + class InBodyPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#parsing-main-inbody + # the really-really-really-very crazy mode + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + # Set this to the default handler + self.processSpaceCharacters = self.processSpaceCharactersNonPre + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("base", "basefont", "bgsound", "command", "link", "meta", + "script", "style", "title"), + self.startTagProcessInHead), + ("body", self.startTagBody), + ("frameset", self.startTagFrameset), + (("address", "article", "aside", "blockquote", "center", "details", + "dir", "div", "dl", "fieldset", "figcaption", "figure", + "footer", "header", "hgroup", "main", "menu", "nav", "ol", "p", + "section", "summary", "ul"), + self.startTagCloseP), + (headingElements, self.startTagHeading), + (("pre", "listing"), self.startTagPreListing), + ("form", self.startTagForm), + (("li", "dd", "dt"), self.startTagListItem), + ("plaintext", self.startTagPlaintext), + ("a", self.startTagA), + (("b", "big", "code", "em", "font", "i", "s", "small", "strike", + "strong", "tt", "u"), self.startTagFormatting), + ("nobr", self.startTagNobr), + ("button", self.startTagButton), + (("applet", "marquee", "object"), self.startTagAppletMarqueeObject), + ("xmp", self.startTagXmp), + ("table", self.startTagTable), + (("area", "br", "embed", "img", "keygen", "wbr"), + self.startTagVoidFormatting), + (("param", "source", "track"), self.startTagParamSource), + ("input", self.startTagInput), + ("hr", self.startTagHr), + ("image", self.startTagImage), + ("isindex", self.startTagIsIndex), + ("textarea", self.startTagTextarea), + ("iframe", self.startTagIFrame), + ("noscript", self.startTagNoscript), + (("noembed", "noframes"), self.startTagRawtext), + ("select", self.startTagSelect), + (("rp", "rt"), self.startTagRpRt), + (("option", "optgroup"), self.startTagOpt), + (("math"), self.startTagMath), + (("svg"), self.startTagSvg), + (("caption", "col", "colgroup", "frame", "head", + "tbody", "td", "tfoot", "th", "thead", + "tr"), self.startTagMisplaced) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("body", self.endTagBody), + ("html", self.endTagHtml), + (("address", "article", "aside", "blockquote", "button", "center", + "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", + "footer", "header", "hgroup", "listing", "main", "menu", "nav", "ol", "pre", + "section", "summary", "ul"), self.endTagBlock), + ("form", self.endTagForm), + ("p", self.endTagP), + (("dd", "dt", "li"), self.endTagListItem), + (headingElements, self.endTagHeading), + (("a", "b", "big", "code", "em", "font", "i", "nobr", "s", "small", + "strike", "strong", "tt", "u"), self.endTagFormatting), + (("applet", "marquee", "object"), self.endTagAppletMarqueeObject), + ("br", self.endTagBr), + ]) + self.endTagHandler.default = self.endTagOther + + def isMatchingFormattingElement(self, node1, node2): + return (node1.name == node2.name and + node1.namespace == node2.namespace and + node1.attributes == node2.attributes) + + # helper + def addFormattingElement(self, token): + self.tree.insertElement(token) + element = self.tree.openElements[-1] + + matchingElements = [] + for node in self.tree.activeFormattingElements[::-1]: + if node is Marker: + break + elif self.isMatchingFormattingElement(node, element): + matchingElements.append(node) + + assert len(matchingElements) <= 3 + if len(matchingElements) == 3: + self.tree.activeFormattingElements.remove(matchingElements[-1]) + self.tree.activeFormattingElements.append(element) + + # the real deal + def processEOF(self): + allowed_elements = frozenset(("dd", "dt", "li", "p", "tbody", "td", + "tfoot", "th", "thead", "tr", "body", + "html")) + for node in self.tree.openElements[::-1]: + if node.name not in allowed_elements: + self.parser.parseError("expected-closing-tag-but-got-eof") + break + # Stop parsing + + def processSpaceCharactersDropNewline(self, token): + # Sometimes (start of <pre>, <listing>, and <textarea> blocks) we + # want to drop leading newlines + data = token["data"] + self.processSpaceCharacters = self.processSpaceCharactersNonPre + if (data.startswith("\n") and + self.tree.openElements[-1].name in ("pre", "listing", "textarea") and + not self.tree.openElements[-1].hasContent()): + data = data[1:] + if data: + self.tree.reconstructActiveFormattingElements() + self.tree.insertText(data) + + def processCharacters(self, token): + if token["data"] == "\u0000": + # The tokenizer should always emit null on its own + return + self.tree.reconstructActiveFormattingElements() + self.tree.insertText(token["data"]) + # This must be bad for performance + if (self.parser.framesetOK and + any([char not in spaceCharacters + for char in token["data"]])): + self.parser.framesetOK = False + + def processSpaceCharactersNonPre(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertText(token["data"]) + + def startTagProcessInHead(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagBody(self, token): + self.parser.parseError("unexpected-start-tag", {"name": "body"}) + if (len(self.tree.openElements) == 1 or + self.tree.openElements[1].name != "body"): + assert self.parser.innerHTML + else: + self.parser.framesetOK = False + for attr, value in token["data"].items(): + if attr not in self.tree.openElements[1].attributes: + self.tree.openElements[1].attributes[attr] = value + + def startTagFrameset(self, token): + self.parser.parseError("unexpected-start-tag", {"name": "frameset"}) + if (len(self.tree.openElements) == 1 or self.tree.openElements[1].name != "body"): + assert self.parser.innerHTML + elif not self.parser.framesetOK: + pass + else: + if self.tree.openElements[1].parent: + self.tree.openElements[1].parent.removeChild(self.tree.openElements[1]) + while self.tree.openElements[-1].name != "html": + self.tree.openElements.pop() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inFrameset"] + + def startTagCloseP(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + + def startTagPreListing(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + self.parser.framesetOK = False + self.processSpaceCharacters = self.processSpaceCharactersDropNewline + + def startTagForm(self, token): + if self.tree.formPointer: + self.parser.parseError("unexpected-start-tag", {"name": "form"}) + else: + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + self.tree.formPointer = self.tree.openElements[-1] + + def startTagListItem(self, token): + self.parser.framesetOK = False + + stopNamesMap = {"li": ["li"], + "dt": ["dt", "dd"], + "dd": ["dt", "dd"]} + stopNames = stopNamesMap[token["name"]] + for node in reversed(self.tree.openElements): + if node.name in stopNames: + self.parser.phase.processEndTag( + impliedTagToken(node.name, "EndTag")) + break + if (node.nameTuple in specialElements and + node.name not in ("address", "div", "p")): + break + + if self.tree.elementInScope("p", variant="button"): + self.parser.phase.processEndTag( + impliedTagToken("p", "EndTag")) + + self.tree.insertElement(token) + + def startTagPlaintext(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + self.parser.tokenizer.state = self.parser.tokenizer.plaintextState + + def startTagHeading(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + if self.tree.openElements[-1].name in headingElements: + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + self.tree.openElements.pop() + self.tree.insertElement(token) + + def startTagA(self, token): + afeAElement = self.tree.elementInActiveFormattingElements("a") + if afeAElement: + self.parser.parseError("unexpected-start-tag-implies-end-tag", + {"startName": "a", "endName": "a"}) + self.endTagFormatting(impliedTagToken("a")) + if afeAElement in self.tree.openElements: + self.tree.openElements.remove(afeAElement) + if afeAElement in self.tree.activeFormattingElements: + self.tree.activeFormattingElements.remove(afeAElement) + self.tree.reconstructActiveFormattingElements() + self.addFormattingElement(token) + + def startTagFormatting(self, token): + self.tree.reconstructActiveFormattingElements() + self.addFormattingElement(token) + + def startTagNobr(self, token): + self.tree.reconstructActiveFormattingElements() + if self.tree.elementInScope("nobr"): + self.parser.parseError("unexpected-start-tag-implies-end-tag", + {"startName": "nobr", "endName": "nobr"}) + self.processEndTag(impliedTagToken("nobr")) + # XXX Need tests that trigger the following + self.tree.reconstructActiveFormattingElements() + self.addFormattingElement(token) + + def startTagButton(self, token): + if self.tree.elementInScope("button"): + self.parser.parseError("unexpected-start-tag-implies-end-tag", + {"startName": "button", "endName": "button"}) + self.processEndTag(impliedTagToken("button")) + return token + else: + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + self.parser.framesetOK = False + + def startTagAppletMarqueeObject(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + self.tree.activeFormattingElements.append(Marker) + self.parser.framesetOK = False + + def startTagXmp(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.reconstructActiveFormattingElements() + self.parser.framesetOK = False + self.parser.parseRCDataRawtext(token, "RAWTEXT") + + def startTagTable(self, token): + if self.parser.compatMode != "quirks": + if self.tree.elementInScope("p", variant="button"): + self.processEndTag(impliedTagToken("p")) + self.tree.insertElement(token) + self.parser.framesetOK = False + self.parser.phase = self.parser.phases["inTable"] + + def startTagVoidFormatting(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + self.parser.framesetOK = False + + def startTagInput(self, token): + framesetOK = self.parser.framesetOK + self.startTagVoidFormatting(token) + if ("type" in token["data"] and + token["data"]["type"].translate(asciiUpper2Lower) == "hidden"): + # input type=hidden doesn't change framesetOK + self.parser.framesetOK = framesetOK + + def startTagParamSource(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagHr(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + self.parser.framesetOK = False + + def startTagImage(self, token): + # No really... + self.parser.parseError("unexpected-start-tag-treated-as", + {"originalName": "image", "newName": "img"}) + self.processStartTag(impliedTagToken("img", "StartTag", + attributes=token["data"], + selfClosing=token["selfClosing"])) + + def startTagIsIndex(self, token): + self.parser.parseError("deprecated-tag", {"name": "isindex"}) + if self.tree.formPointer: + return + form_attrs = {} + if "action" in token["data"]: + form_attrs["action"] = token["data"]["action"] + self.processStartTag(impliedTagToken("form", "StartTag", + attributes=form_attrs)) + self.processStartTag(impliedTagToken("hr", "StartTag")) + self.processStartTag(impliedTagToken("label", "StartTag")) + # XXX Localization ... + if "prompt" in token["data"]: + prompt = token["data"]["prompt"] + else: + prompt = "This is a searchable index. Enter search keywords: " + self.processCharacters( + {"type": tokenTypes["Characters"], "data": prompt}) + attributes = token["data"].copy() + if "action" in attributes: + del attributes["action"] + if "prompt" in attributes: + del attributes["prompt"] + attributes["name"] = "isindex" + self.processStartTag(impliedTagToken("input", "StartTag", + attributes=attributes, + selfClosing=token["selfClosing"])) + self.processEndTag(impliedTagToken("label")) + self.processStartTag(impliedTagToken("hr", "StartTag")) + self.processEndTag(impliedTagToken("form")) + + def startTagTextarea(self, token): + self.tree.insertElement(token) + self.parser.tokenizer.state = self.parser.tokenizer.rcdataState + self.processSpaceCharacters = self.processSpaceCharactersDropNewline + self.parser.framesetOK = False + + def startTagIFrame(self, token): + self.parser.framesetOK = False + self.startTagRawtext(token) + + def startTagNoscript(self, token): + if self.parser.scripting: + self.startTagRawtext(token) + else: + self.startTagOther(token) + + def startTagRawtext(self, token): + """iframe, noembed noframes, noscript(if scripting enabled)""" + self.parser.parseRCDataRawtext(token, "RAWTEXT") + + def startTagOpt(self, token): + if self.tree.openElements[-1].name == "option": + self.parser.phase.processEndTag(impliedTagToken("option")) + self.tree.reconstructActiveFormattingElements() + self.parser.tree.insertElement(token) + + def startTagSelect(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + self.parser.framesetOK = False + if self.parser.phase in (self.parser.phases["inTable"], + self.parser.phases["inCaption"], + self.parser.phases["inColumnGroup"], + self.parser.phases["inTableBody"], + self.parser.phases["inRow"], + self.parser.phases["inCell"]): + self.parser.phase = self.parser.phases["inSelectInTable"] + else: + self.parser.phase = self.parser.phases["inSelect"] + + def startTagRpRt(self, token): + if self.tree.elementInScope("ruby"): + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != "ruby": + self.parser.parseError() + self.tree.insertElement(token) + + def startTagMath(self, token): + self.tree.reconstructActiveFormattingElements() + self.parser.adjustMathMLAttributes(token) + self.parser.adjustForeignAttributes(token) + token["namespace"] = namespaces["mathml"] + self.tree.insertElement(token) + # Need to get the parse error right for the case where the token + # has a namespace not equal to the xmlns attribute + if token["selfClosing"]: + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagSvg(self, token): + self.tree.reconstructActiveFormattingElements() + self.parser.adjustSVGAttributes(token) + self.parser.adjustForeignAttributes(token) + token["namespace"] = namespaces["svg"] + self.tree.insertElement(token) + # Need to get the parse error right for the case where the token + # has a namespace not equal to the xmlns attribute + if token["selfClosing"]: + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagMisplaced(self, token): + """ Elements that should be children of other elements that have a + different insertion mode; here they are ignored + "caption", "col", "colgroup", "frame", "frameset", "head", + "option", "optgroup", "tbody", "td", "tfoot", "th", "thead", + "tr", "noscript" + """ + self.parser.parseError("unexpected-start-tag-ignored", {"name": token["name"]}) + + def startTagOther(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + + def endTagP(self, token): + if not self.tree.elementInScope("p", variant="button"): + self.startTagCloseP(impliedTagToken("p", "StartTag")) + self.parser.parseError("unexpected-end-tag", {"name": "p"}) + self.endTagP(impliedTagToken("p", "EndTag")) + else: + self.tree.generateImpliedEndTags("p") + if self.tree.openElements[-1].name != "p": + self.parser.parseError("unexpected-end-tag", {"name": "p"}) + node = self.tree.openElements.pop() + while node.name != "p": + node = self.tree.openElements.pop() + + def endTagBody(self, token): + if not self.tree.elementInScope("body"): + self.parser.parseError() + return + elif self.tree.openElements[-1].name != "body": + for node in self.tree.openElements[2:]: + if node.name not in frozenset(("dd", "dt", "li", "optgroup", + "option", "p", "rp", "rt", + "tbody", "td", "tfoot", + "th", "thead", "tr", "body", + "html")): + # Not sure this is the correct name for the parse error + self.parser.parseError( + "expected-one-end-tag-but-got-another", + {"gotName": "body", "expectedName": node.name}) + break + self.parser.phase = self.parser.phases["afterBody"] + + def endTagHtml(self, token): + # We repeat the test for the body end tag token being ignored here + if self.tree.elementInScope("body"): + self.endTagBody(impliedTagToken("body")) + return token + + def endTagBlock(self, token): + # Put us back in the right whitespace handling mode + if token["name"] == "pre": + self.processSpaceCharacters = self.processSpaceCharactersNonPre + inScope = self.tree.elementInScope(token["name"]) + if inScope: + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("end-tag-too-early", {"name": token["name"]}) + if inScope: + node = self.tree.openElements.pop() + while node.name != token["name"]: + node = self.tree.openElements.pop() + + def endTagForm(self, token): + node = self.tree.formPointer + self.tree.formPointer = None + if node is None or not self.tree.elementInScope(node): + self.parser.parseError("unexpected-end-tag", + {"name": "form"}) + else: + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1] != node: + self.parser.parseError("end-tag-too-early-ignored", + {"name": "form"}) + self.tree.openElements.remove(node) + + def endTagListItem(self, token): + if token["name"] == "li": + variant = "list" + else: + variant = None + if not self.tree.elementInScope(token["name"], variant=variant): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + else: + self.tree.generateImpliedEndTags(exclude=token["name"]) + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError( + "end-tag-too-early", + {"name": token["name"]}) + node = self.tree.openElements.pop() + while node.name != token["name"]: + node = self.tree.openElements.pop() + + def endTagHeading(self, token): + for item in headingElements: + if self.tree.elementInScope(item): + self.tree.generateImpliedEndTags() + break + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("end-tag-too-early", {"name": token["name"]}) + + for item in headingElements: + if self.tree.elementInScope(item): + item = self.tree.openElements.pop() + while item.name not in headingElements: + item = self.tree.openElements.pop() + break + + def endTagFormatting(self, token): + """The much-feared adoption agency algorithm""" + # http://svn.whatwg.org/webapps/complete.html#adoptionAgency revision 7867 + # XXX Better parseError messages appreciated. + + # Step 1 + outerLoopCounter = 0 + + # Step 2 + while outerLoopCounter < 8: + + # Step 3 + outerLoopCounter += 1 + + # Step 4: + + # Let the formatting element be the last element in + # the list of active formatting elements that: + # - is between the end of the list and the last scope + # marker in the list, if any, or the start of the list + # otherwise, and + # - has the same tag name as the token. + formattingElement = self.tree.elementInActiveFormattingElements( + token["name"]) + if (not formattingElement or + (formattingElement in self.tree.openElements and + not self.tree.elementInScope(formattingElement.name))): + # If there is no such node, then abort these steps + # and instead act as described in the "any other + # end tag" entry below. + self.endTagOther(token) + return + + # Otherwise, if there is such a node, but that node is + # not in the stack of open elements, then this is a + # parse error; remove the element from the list, and + # abort these steps. + elif formattingElement not in self.tree.openElements: + self.parser.parseError("adoption-agency-1.2", {"name": token["name"]}) + self.tree.activeFormattingElements.remove(formattingElement) + return + + # Otherwise, if there is such a node, and that node is + # also in the stack of open elements, but the element + # is not in scope, then this is a parse error; ignore + # the token, and abort these steps. + elif not self.tree.elementInScope(formattingElement.name): + self.parser.parseError("adoption-agency-4.4", {"name": token["name"]}) + return + + # Otherwise, there is a formatting element and that + # element is in the stack and is in scope. If the + # element is not the current node, this is a parse + # error. In any case, proceed with the algorithm as + # written in the following steps. + else: + if formattingElement != self.tree.openElements[-1]: + self.parser.parseError("adoption-agency-1.3", {"name": token["name"]}) + + # Step 5: + + # Let the furthest block be the topmost node in the + # stack of open elements that is lower in the stack + # than the formatting element, and is an element in + # the special category. There might not be one. + afeIndex = self.tree.openElements.index(formattingElement) + furthestBlock = None + for element in self.tree.openElements[afeIndex:]: + if element.nameTuple in specialElements: + furthestBlock = element + break + + # Step 6: + + # If there is no furthest block, then the UA must + # first pop all the nodes from the bottom of the stack + # of open elements, from the current node up to and + # including the formatting element, then remove the + # formatting element from the list of active + # formatting elements, and finally abort these steps. + if furthestBlock is None: + element = self.tree.openElements.pop() + while element != formattingElement: + element = self.tree.openElements.pop() + self.tree.activeFormattingElements.remove(element) + return + + # Step 7 + commonAncestor = self.tree.openElements[afeIndex - 1] + + # Step 8: + # The bookmark is supposed to help us identify where to reinsert + # nodes in step 15. We have to ensure that we reinsert nodes after + # the node before the active formatting element. Note the bookmark + # can move in step 9.7 + bookmark = self.tree.activeFormattingElements.index(formattingElement) + + # Step 9 + lastNode = node = furthestBlock + innerLoopCounter = 0 + + index = self.tree.openElements.index(node) + while innerLoopCounter < 3: + innerLoopCounter += 1 + # Node is element before node in open elements + index -= 1 + node = self.tree.openElements[index] + if node not in self.tree.activeFormattingElements: + self.tree.openElements.remove(node) + continue + # Step 9.6 + if node == formattingElement: + break + # Step 9.7 + if lastNode == furthestBlock: + bookmark = self.tree.activeFormattingElements.index(node) + 1 + # Step 9.8 + clone = node.cloneNode() + # Replace node with clone + self.tree.activeFormattingElements[ + self.tree.activeFormattingElements.index(node)] = clone + self.tree.openElements[ + self.tree.openElements.index(node)] = clone + node = clone + # Step 9.9 + # Remove lastNode from its parents, if any + if lastNode.parent: + lastNode.parent.removeChild(lastNode) + node.appendChild(lastNode) + # Step 9.10 + lastNode = node + + # Step 10 + # Foster parent lastNode if commonAncestor is a + # table, tbody, tfoot, thead, or tr we need to foster + # parent the lastNode + if lastNode.parent: + lastNode.parent.removeChild(lastNode) + + if commonAncestor.name in frozenset(("table", "tbody", "tfoot", "thead", "tr")): + parent, insertBefore = self.tree.getTableMisnestedNodePosition() + parent.insertBefore(lastNode, insertBefore) + else: + commonAncestor.appendChild(lastNode) + + # Step 11 + clone = formattingElement.cloneNode() + + # Step 12 + furthestBlock.reparentChildren(clone) + + # Step 13 + furthestBlock.appendChild(clone) + + # Step 14 + self.tree.activeFormattingElements.remove(formattingElement) + self.tree.activeFormattingElements.insert(bookmark, clone) + + # Step 15 + self.tree.openElements.remove(formattingElement) + self.tree.openElements.insert( + self.tree.openElements.index(furthestBlock) + 1, clone) + + def endTagAppletMarqueeObject(self, token): + if self.tree.elementInScope(token["name"]): + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("end-tag-too-early", {"name": token["name"]}) + + if self.tree.elementInScope(token["name"]): + element = self.tree.openElements.pop() + while element.name != token["name"]: + element = self.tree.openElements.pop() + self.tree.clearActiveFormattingElements() + + def endTagBr(self, token): + self.parser.parseError("unexpected-end-tag-treated-as", + {"originalName": "br", "newName": "br element"}) + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(impliedTagToken("br", "StartTag")) + self.tree.openElements.pop() + + def endTagOther(self, token): + for node in self.tree.openElements[::-1]: + if node.name == token["name"]: + self.tree.generateImpliedEndTags(exclude=token["name"]) + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + while self.tree.openElements.pop() != node: + pass + break + else: + if node.nameTuple in specialElements: + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + break + + class TextPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([]) + self.startTagHandler.default = self.startTagOther + self.endTagHandler = _utils.MethodDispatcher([ + ("script", self.endTagScript)]) + self.endTagHandler.default = self.endTagOther + + def processCharacters(self, token): + self.tree.insertText(token["data"]) + + def processEOF(self): + self.parser.parseError("expected-named-closing-tag-but-got-eof", + {"name": self.tree.openElements[-1].name}) + self.tree.openElements.pop() + self.parser.phase = self.parser.originalPhase + return True + + def startTagOther(self, token): + assert False, "Tried to process start tag %s in RCDATA/RAWTEXT mode" % token['name'] + + def endTagScript(self, token): + node = self.tree.openElements.pop() + assert node.name == "script" + self.parser.phase = self.parser.originalPhase + # The rest of this method is all stuff that only happens if + # document.write works + + def endTagOther(self, token): + self.tree.openElements.pop() + self.parser.phase = self.parser.originalPhase + + class InTablePhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-table + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("caption", self.startTagCaption), + ("colgroup", self.startTagColgroup), + ("col", self.startTagCol), + (("tbody", "tfoot", "thead"), self.startTagRowGroup), + (("td", "th", "tr"), self.startTagImplyTbody), + ("table", self.startTagTable), + (("style", "script"), self.startTagStyleScript), + ("input", self.startTagInput), + ("form", self.startTagForm) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("table", self.endTagTable), + (("body", "caption", "col", "colgroup", "html", "tbody", "td", + "tfoot", "th", "thead", "tr"), self.endTagIgnore) + ]) + self.endTagHandler.default = self.endTagOther + + # helper methods + def clearStackToTableContext(self): + # "clear the stack back to a table context" + while self.tree.openElements[-1].name not in ("table", "html"): + # self.parser.parseError("unexpected-implied-end-tag-in-table", + # {"name": self.tree.openElements[-1].name}) + self.tree.openElements.pop() + # When the current node is <html> it's an innerHTML case + + # processing methods + def processEOF(self): + if self.tree.openElements[-1].name != "html": + self.parser.parseError("eof-in-table") + else: + assert self.parser.innerHTML + # Stop parsing + + def processSpaceCharacters(self, token): + originalPhase = self.parser.phase + self.parser.phase = self.parser.phases["inTableText"] + self.parser.phase.originalPhase = originalPhase + self.parser.phase.processSpaceCharacters(token) + + def processCharacters(self, token): + originalPhase = self.parser.phase + self.parser.phase = self.parser.phases["inTableText"] + self.parser.phase.originalPhase = originalPhase + self.parser.phase.processCharacters(token) + + def insertText(self, token): + # If we get here there must be at least one non-whitespace character + # Do the table magic! + self.tree.insertFromTable = True + self.parser.phases["inBody"].processCharacters(token) + self.tree.insertFromTable = False + + def startTagCaption(self, token): + self.clearStackToTableContext() + self.tree.activeFormattingElements.append(Marker) + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inCaption"] + + def startTagColgroup(self, token): + self.clearStackToTableContext() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inColumnGroup"] + + def startTagCol(self, token): + self.startTagColgroup(impliedTagToken("colgroup", "StartTag")) + return token + + def startTagRowGroup(self, token): + self.clearStackToTableContext() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inTableBody"] + + def startTagImplyTbody(self, token): + self.startTagRowGroup(impliedTagToken("tbody", "StartTag")) + return token + + def startTagTable(self, token): + self.parser.parseError("unexpected-start-tag-implies-end-tag", + {"startName": "table", "endName": "table"}) + self.parser.phase.processEndTag(impliedTagToken("table")) + if not self.parser.innerHTML: + return token + + def startTagStyleScript(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagInput(self, token): + if ("type" in token["data"] and + token["data"]["type"].translate(asciiUpper2Lower) == "hidden"): + self.parser.parseError("unexpected-hidden-input-in-table") + self.tree.insertElement(token) + # XXX associate with form + self.tree.openElements.pop() + else: + self.startTagOther(token) + + def startTagForm(self, token): + self.parser.parseError("unexpected-form-in-table") + if self.tree.formPointer is None: + self.tree.insertElement(token) + self.tree.formPointer = self.tree.openElements[-1] + self.tree.openElements.pop() + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-implies-table-voodoo", {"name": token["name"]}) + # Do the table magic! + self.tree.insertFromTable = True + self.parser.phases["inBody"].processStartTag(token) + self.tree.insertFromTable = False + + def endTagTable(self, token): + if self.tree.elementInScope("table", variant="table"): + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != "table": + self.parser.parseError("end-tag-too-early-named", + {"gotName": "table", + "expectedName": self.tree.openElements[-1].name}) + while self.tree.openElements[-1].name != "table": + self.tree.openElements.pop() + self.tree.openElements.pop() + self.parser.resetInsertionMode() + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-implies-table-voodoo", {"name": token["name"]}) + # Do the table magic! + self.tree.insertFromTable = True + self.parser.phases["inBody"].processEndTag(token) + self.tree.insertFromTable = False + + class InTableTextPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.originalPhase = None + self.characterTokens = [] + + def flushCharacters(self): + data = "".join([item["data"] for item in self.characterTokens]) + if any([item not in spaceCharacters for item in data]): + token = {"type": tokenTypes["Characters"], "data": data} + self.parser.phases["inTable"].insertText(token) + elif data: + self.tree.insertText(data) + self.characterTokens = [] + + def processComment(self, token): + self.flushCharacters() + self.parser.phase = self.originalPhase + return token + + def processEOF(self): + self.flushCharacters() + self.parser.phase = self.originalPhase + return True + + def processCharacters(self, token): + if token["data"] == "\u0000": + return + self.characterTokens.append(token) + + def processSpaceCharacters(self, token): + # pretty sure we should never reach here + self.characterTokens.append(token) + # assert False + + def processStartTag(self, token): + self.flushCharacters() + self.parser.phase = self.originalPhase + return token + + def processEndTag(self, token): + self.flushCharacters() + self.parser.phase = self.originalPhase + return token + + class InCaptionPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-caption + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", + "thead", "tr"), self.startTagTableElement) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("caption", self.endTagCaption), + ("table", self.endTagTable), + (("body", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", + "thead", "tr"), self.endTagIgnore) + ]) + self.endTagHandler.default = self.endTagOther + + def ignoreEndTagCaption(self): + return not self.tree.elementInScope("caption", variant="table") + + def processEOF(self): + self.parser.phases["inBody"].processEOF() + + def processCharacters(self, token): + return self.parser.phases["inBody"].processCharacters(token) + + def startTagTableElement(self, token): + self.parser.parseError() + # XXX Have to duplicate logic here to find out if the tag is ignored + ignoreEndTag = self.ignoreEndTagCaption() + self.parser.phase.processEndTag(impliedTagToken("caption")) + if not ignoreEndTag: + return token + + def startTagOther(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def endTagCaption(self, token): + if not self.ignoreEndTagCaption(): + # AT this code is quite similar to endTagTable in "InTable" + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != "caption": + self.parser.parseError("expected-one-end-tag-but-got-another", + {"gotName": "caption", + "expectedName": self.tree.openElements[-1].name}) + while self.tree.openElements[-1].name != "caption": + self.tree.openElements.pop() + self.tree.openElements.pop() + self.tree.clearActiveFormattingElements() + self.parser.phase = self.parser.phases["inTable"] + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagTable(self, token): + self.parser.parseError() + ignoreEndTag = self.ignoreEndTagCaption() + self.parser.phase.processEndTag(impliedTagToken("caption")) + if not ignoreEndTag: + return token + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def endTagOther(self, token): + return self.parser.phases["inBody"].processEndTag(token) + + class InColumnGroupPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-column + + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("col", self.startTagCol) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("colgroup", self.endTagColgroup), + ("col", self.endTagCol) + ]) + self.endTagHandler.default = self.endTagOther + + def ignoreEndTagColgroup(self): + return self.tree.openElements[-1].name == "html" + + def processEOF(self): + if self.tree.openElements[-1].name == "html": + assert self.parser.innerHTML + return + else: + ignoreEndTag = self.ignoreEndTagColgroup() + self.endTagColgroup(impliedTagToken("colgroup")) + if not ignoreEndTag: + return True + + def processCharacters(self, token): + ignoreEndTag = self.ignoreEndTagColgroup() + self.endTagColgroup(impliedTagToken("colgroup")) + if not ignoreEndTag: + return token + + def startTagCol(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagOther(self, token): + ignoreEndTag = self.ignoreEndTagColgroup() + self.endTagColgroup(impliedTagToken("colgroup")) + if not ignoreEndTag: + return token + + def endTagColgroup(self, token): + if self.ignoreEndTagColgroup(): + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + else: + self.tree.openElements.pop() + self.parser.phase = self.parser.phases["inTable"] + + def endTagCol(self, token): + self.parser.parseError("no-end-tag", {"name": "col"}) + + def endTagOther(self, token): + ignoreEndTag = self.ignoreEndTagColgroup() + self.endTagColgroup(impliedTagToken("colgroup")) + if not ignoreEndTag: + return token + + class InTableBodyPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-table0 + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("tr", self.startTagTr), + (("td", "th"), self.startTagTableCell), + (("caption", "col", "colgroup", "tbody", "tfoot", "thead"), + self.startTagTableOther) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + (("tbody", "tfoot", "thead"), self.endTagTableRowGroup), + ("table", self.endTagTable), + (("body", "caption", "col", "colgroup", "html", "td", "th", + "tr"), self.endTagIgnore) + ]) + self.endTagHandler.default = self.endTagOther + + # helper methods + def clearStackToTableBodyContext(self): + while self.tree.openElements[-1].name not in ("tbody", "tfoot", + "thead", "html"): + # self.parser.parseError("unexpected-implied-end-tag-in-table", + # {"name": self.tree.openElements[-1].name}) + self.tree.openElements.pop() + if self.tree.openElements[-1].name == "html": + assert self.parser.innerHTML + + # the rest + def processEOF(self): + self.parser.phases["inTable"].processEOF() + + def processSpaceCharacters(self, token): + return self.parser.phases["inTable"].processSpaceCharacters(token) + + def processCharacters(self, token): + return self.parser.phases["inTable"].processCharacters(token) + + def startTagTr(self, token): + self.clearStackToTableBodyContext() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inRow"] + + def startTagTableCell(self, token): + self.parser.parseError("unexpected-cell-in-table-body", + {"name": token["name"]}) + self.startTagTr(impliedTagToken("tr", "StartTag")) + return token + + def startTagTableOther(self, token): + # XXX AT Any ideas on how to share this with endTagTable? + if (self.tree.elementInScope("tbody", variant="table") or + self.tree.elementInScope("thead", variant="table") or + self.tree.elementInScope("tfoot", variant="table")): + self.clearStackToTableBodyContext() + self.endTagTableRowGroup( + impliedTagToken(self.tree.openElements[-1].name)) + return token + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def startTagOther(self, token): + return self.parser.phases["inTable"].processStartTag(token) + + def endTagTableRowGroup(self, token): + if self.tree.elementInScope(token["name"], variant="table"): + self.clearStackToTableBodyContext() + self.tree.openElements.pop() + self.parser.phase = self.parser.phases["inTable"] + else: + self.parser.parseError("unexpected-end-tag-in-table-body", + {"name": token["name"]}) + + def endTagTable(self, token): + if (self.tree.elementInScope("tbody", variant="table") or + self.tree.elementInScope("thead", variant="table") or + self.tree.elementInScope("tfoot", variant="table")): + self.clearStackToTableBodyContext() + self.endTagTableRowGroup( + impliedTagToken(self.tree.openElements[-1].name)) + return token + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag-in-table-body", + {"name": token["name"]}) + + def endTagOther(self, token): + return self.parser.phases["inTable"].processEndTag(token) + + class InRowPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-row + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("td", "th"), self.startTagTableCell), + (("caption", "col", "colgroup", "tbody", "tfoot", "thead", + "tr"), self.startTagTableOther) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("tr", self.endTagTr), + ("table", self.endTagTable), + (("tbody", "tfoot", "thead"), self.endTagTableRowGroup), + (("body", "caption", "col", "colgroup", "html", "td", "th"), + self.endTagIgnore) + ]) + self.endTagHandler.default = self.endTagOther + + # helper methods (XXX unify this with other table helper methods) + def clearStackToTableRowContext(self): + while self.tree.openElements[-1].name not in ("tr", "html"): + self.parser.parseError("unexpected-implied-end-tag-in-table-row", + {"name": self.tree.openElements[-1].name}) + self.tree.openElements.pop() + + def ignoreEndTagTr(self): + return not self.tree.elementInScope("tr", variant="table") + + # the rest + def processEOF(self): + self.parser.phases["inTable"].processEOF() + + def processSpaceCharacters(self, token): + return self.parser.phases["inTable"].processSpaceCharacters(token) + + def processCharacters(self, token): + return self.parser.phases["inTable"].processCharacters(token) + + def startTagTableCell(self, token): + self.clearStackToTableRowContext() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inCell"] + self.tree.activeFormattingElements.append(Marker) + + def startTagTableOther(self, token): + ignoreEndTag = self.ignoreEndTagTr() + self.endTagTr(impliedTagToken("tr")) + # XXX how are we sure it's always ignored in the innerHTML case? + if not ignoreEndTag: + return token + + def startTagOther(self, token): + return self.parser.phases["inTable"].processStartTag(token) + + def endTagTr(self, token): + if not self.ignoreEndTagTr(): + self.clearStackToTableRowContext() + self.tree.openElements.pop() + self.parser.phase = self.parser.phases["inTableBody"] + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagTable(self, token): + ignoreEndTag = self.ignoreEndTagTr() + self.endTagTr(impliedTagToken("tr")) + # Reprocess the current tag if the tr end tag was not ignored + # XXX how are we sure it's always ignored in the innerHTML case? + if not ignoreEndTag: + return token + + def endTagTableRowGroup(self, token): + if self.tree.elementInScope(token["name"], variant="table"): + self.endTagTr(impliedTagToken("tr")) + return token + else: + self.parser.parseError() + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag-in-table-row", + {"name": token["name"]}) + + def endTagOther(self, token): + return self.parser.phases["inTable"].processEndTag(token) + + class InCellPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-cell + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", + "thead", "tr"), self.startTagTableOther) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + (("td", "th"), self.endTagTableCell), + (("body", "caption", "col", "colgroup", "html"), self.endTagIgnore), + (("table", "tbody", "tfoot", "thead", "tr"), self.endTagImply) + ]) + self.endTagHandler.default = self.endTagOther + + # helper + def closeCell(self): + if self.tree.elementInScope("td", variant="table"): + self.endTagTableCell(impliedTagToken("td")) + elif self.tree.elementInScope("th", variant="table"): + self.endTagTableCell(impliedTagToken("th")) + + # the rest + def processEOF(self): + self.parser.phases["inBody"].processEOF() + + def processCharacters(self, token): + return self.parser.phases["inBody"].processCharacters(token) + + def startTagTableOther(self, token): + if (self.tree.elementInScope("td", variant="table") or + self.tree.elementInScope("th", variant="table")): + self.closeCell() + return token + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def startTagOther(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def endTagTableCell(self, token): + if self.tree.elementInScope(token["name"], variant="table"): + self.tree.generateImpliedEndTags(token["name"]) + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("unexpected-cell-end-tag", + {"name": token["name"]}) + while True: + node = self.tree.openElements.pop() + if node.name == token["name"]: + break + else: + self.tree.openElements.pop() + self.tree.clearActiveFormattingElements() + self.parser.phase = self.parser.phases["inRow"] + else: + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def endTagImply(self, token): + if self.tree.elementInScope(token["name"], variant="table"): + self.closeCell() + return token + else: + # sometimes innerHTML case + self.parser.parseError() + + def endTagOther(self, token): + return self.parser.phases["inBody"].processEndTag(token) + + class InSelectPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("option", self.startTagOption), + ("optgroup", self.startTagOptgroup), + ("select", self.startTagSelect), + (("input", "keygen", "textarea"), self.startTagInput), + ("script", self.startTagScript) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("option", self.endTagOption), + ("optgroup", self.endTagOptgroup), + ("select", self.endTagSelect) + ]) + self.endTagHandler.default = self.endTagOther + + # http://www.whatwg.org/specs/web-apps/current-work/#in-select + def processEOF(self): + if self.tree.openElements[-1].name != "html": + self.parser.parseError("eof-in-select") + else: + assert self.parser.innerHTML + + def processCharacters(self, token): + if token["data"] == "\u0000": + return + self.tree.insertText(token["data"]) + + def startTagOption(self, token): + # We need to imply </option> if <option> is the current node. + if self.tree.openElements[-1].name == "option": + self.tree.openElements.pop() + self.tree.insertElement(token) + + def startTagOptgroup(self, token): + if self.tree.openElements[-1].name == "option": + self.tree.openElements.pop() + if self.tree.openElements[-1].name == "optgroup": + self.tree.openElements.pop() + self.tree.insertElement(token) + + def startTagSelect(self, token): + self.parser.parseError("unexpected-select-in-select") + self.endTagSelect(impliedTagToken("select")) + + def startTagInput(self, token): + self.parser.parseError("unexpected-input-in-select") + if self.tree.elementInScope("select", variant="select"): + self.endTagSelect(impliedTagToken("select")) + return token + else: + assert self.parser.innerHTML + + def startTagScript(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-in-select", + {"name": token["name"]}) + + def endTagOption(self, token): + if self.tree.openElements[-1].name == "option": + self.tree.openElements.pop() + else: + self.parser.parseError("unexpected-end-tag-in-select", + {"name": "option"}) + + def endTagOptgroup(self, token): + # </optgroup> implicitly closes <option> + if (self.tree.openElements[-1].name == "option" and + self.tree.openElements[-2].name == "optgroup"): + self.tree.openElements.pop() + # It also closes </optgroup> + if self.tree.openElements[-1].name == "optgroup": + self.tree.openElements.pop() + # But nothing else + else: + self.parser.parseError("unexpected-end-tag-in-select", + {"name": "optgroup"}) + + def endTagSelect(self, token): + if self.tree.elementInScope("select", variant="select"): + node = self.tree.openElements.pop() + while node.name != "select": + node = self.tree.openElements.pop() + self.parser.resetInsertionMode() + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-in-select", + {"name": token["name"]}) + + class InSelectInTablePhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), + self.startTagTable) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), + self.endTagTable) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.parser.phases["inSelect"].processEOF() + + def processCharacters(self, token): + return self.parser.phases["inSelect"].processCharacters(token) + + def startTagTable(self, token): + self.parser.parseError("unexpected-table-element-start-tag-in-select-in-table", {"name": token["name"]}) + self.endTagOther(impliedTagToken("select")) + return token + + def startTagOther(self, token): + return self.parser.phases["inSelect"].processStartTag(token) + + def endTagTable(self, token): + self.parser.parseError("unexpected-table-element-end-tag-in-select-in-table", {"name": token["name"]}) + if self.tree.elementInScope(token["name"], variant="table"): + self.endTagOther(impliedTagToken("select")) + return token + + def endTagOther(self, token): + return self.parser.phases["inSelect"].processEndTag(token) + + class InForeignContentPhase(Phase): + breakoutElements = frozenset(["b", "big", "blockquote", "body", "br", + "center", "code", "dd", "div", "dl", "dt", + "em", "embed", "h1", "h2", "h3", + "h4", "h5", "h6", "head", "hr", "i", "img", + "li", "listing", "menu", "meta", "nobr", + "ol", "p", "pre", "ruby", "s", "small", + "span", "strong", "strike", "sub", "sup", + "table", "tt", "u", "ul", "var"]) + + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + def adjustSVGTagNames(self, token): + replacements = {"altglyph": "altGlyph", + "altglyphdef": "altGlyphDef", + "altglyphitem": "altGlyphItem", + "animatecolor": "animateColor", + "animatemotion": "animateMotion", + "animatetransform": "animateTransform", + "clippath": "clipPath", + "feblend": "feBlend", + "fecolormatrix": "feColorMatrix", + "fecomponenttransfer": "feComponentTransfer", + "fecomposite": "feComposite", + "feconvolvematrix": "feConvolveMatrix", + "fediffuselighting": "feDiffuseLighting", + "fedisplacementmap": "feDisplacementMap", + "fedistantlight": "feDistantLight", + "feflood": "feFlood", + "fefunca": "feFuncA", + "fefuncb": "feFuncB", + "fefuncg": "feFuncG", + "fefuncr": "feFuncR", + "fegaussianblur": "feGaussianBlur", + "feimage": "feImage", + "femerge": "feMerge", + "femergenode": "feMergeNode", + "femorphology": "feMorphology", + "feoffset": "feOffset", + "fepointlight": "fePointLight", + "fespecularlighting": "feSpecularLighting", + "fespotlight": "feSpotLight", + "fetile": "feTile", + "feturbulence": "feTurbulence", + "foreignobject": "foreignObject", + "glyphref": "glyphRef", + "lineargradient": "linearGradient", + "radialgradient": "radialGradient", + "textpath": "textPath"} + + if token["name"] in replacements: + token["name"] = replacements[token["name"]] + + def processCharacters(self, token): + if token["data"] == "\u0000": + token["data"] = "\uFFFD" + elif (self.parser.framesetOK and + any(char not in spaceCharacters for char in token["data"])): + self.parser.framesetOK = False + Phase.processCharacters(self, token) + + def processStartTag(self, token): + currentNode = self.tree.openElements[-1] + if (token["name"] in self.breakoutElements or + (token["name"] == "font" and + set(token["data"].keys()) & set(["color", "face", "size"]))): + self.parser.parseError("unexpected-html-element-in-foreign-content", + {"name": token["name"]}) + while (self.tree.openElements[-1].namespace != + self.tree.defaultNamespace and + not self.parser.isHTMLIntegrationPoint(self.tree.openElements[-1]) and + not self.parser.isMathMLTextIntegrationPoint(self.tree.openElements[-1])): + self.tree.openElements.pop() + return token + + else: + if currentNode.namespace == namespaces["mathml"]: + self.parser.adjustMathMLAttributes(token) + elif currentNode.namespace == namespaces["svg"]: + self.adjustSVGTagNames(token) + self.parser.adjustSVGAttributes(token) + self.parser.adjustForeignAttributes(token) + token["namespace"] = currentNode.namespace + self.tree.insertElement(token) + if token["selfClosing"]: + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def processEndTag(self, token): + nodeIndex = len(self.tree.openElements) - 1 + node = self.tree.openElements[-1] + if node.name.translate(asciiUpper2Lower) != token["name"]: + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + while True: + if node.name.translate(asciiUpper2Lower) == token["name"]: + # XXX this isn't in the spec but it seems necessary + if self.parser.phase == self.parser.phases["inTableText"]: + self.parser.phase.flushCharacters() + self.parser.phase = self.parser.phase.originalPhase + while self.tree.openElements.pop() != node: + assert self.tree.openElements + new_token = None + break + nodeIndex -= 1 + + node = self.tree.openElements[nodeIndex] + if node.namespace != self.tree.defaultNamespace: + continue + else: + new_token = self.parser.phase.processEndTag(token) + break + return new_token + + class AfterBodyPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([("html", self.endTagHtml)]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + # Stop parsing + pass + + def processComment(self, token): + # This is needed because data is to be appended to the <html> element + # here and not to whatever is currently open. + self.tree.insertComment(token, self.tree.openElements[0]) + + def processCharacters(self, token): + self.parser.parseError("unexpected-char-after-body") + self.parser.phase = self.parser.phases["inBody"] + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-after-body", + {"name": token["name"]}) + self.parser.phase = self.parser.phases["inBody"] + return token + + def endTagHtml(self, name): + if self.parser.innerHTML: + self.parser.parseError("unexpected-end-tag-after-body-innerhtml") + else: + self.parser.phase = self.parser.phases["afterAfterBody"] + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-after-body", + {"name": token["name"]}) + self.parser.phase = self.parser.phases["inBody"] + return token + + class InFramesetPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-frameset + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("frameset", self.startTagFrameset), + ("frame", self.startTagFrame), + ("noframes", self.startTagNoframes) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("frameset", self.endTagFrameset) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + if self.tree.openElements[-1].name != "html": + self.parser.parseError("eof-in-frameset") + else: + assert self.parser.innerHTML + + def processCharacters(self, token): + self.parser.parseError("unexpected-char-in-frameset") + + def startTagFrameset(self, token): + self.tree.insertElement(token) + + def startTagFrame(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + + def startTagNoframes(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-in-frameset", + {"name": token["name"]}) + + def endTagFrameset(self, token): + if self.tree.openElements[-1].name == "html": + # innerHTML case + self.parser.parseError("unexpected-frameset-in-frameset-innerhtml") + else: + self.tree.openElements.pop() + if (not self.parser.innerHTML and + self.tree.openElements[-1].name != "frameset"): + # If we're not in innerHTML mode and the current node is not a + # "frameset" element (anymore) then switch. + self.parser.phase = self.parser.phases["afterFrameset"] + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-in-frameset", + {"name": token["name"]}) + + class AfterFramesetPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#after3 + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("noframes", self.startTagNoframes) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("html", self.endTagHtml) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + # Stop parsing + pass + + def processCharacters(self, token): + self.parser.parseError("unexpected-char-after-frameset") + + def startTagNoframes(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-after-frameset", + {"name": token["name"]}) + + def endTagHtml(self, token): + self.parser.phase = self.parser.phases["afterAfterFrameset"] + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-after-frameset", + {"name": token["name"]}) + + class AfterAfterBodyPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml) + ]) + self.startTagHandler.default = self.startTagOther + + def processEOF(self): + pass + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processSpaceCharacters(self, token): + return self.parser.phases["inBody"].processSpaceCharacters(token) + + def processCharacters(self, token): + self.parser.parseError("expected-eof-but-got-char") + self.parser.phase = self.parser.phases["inBody"] + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("expected-eof-but-got-start-tag", + {"name": token["name"]}) + self.parser.phase = self.parser.phases["inBody"] + return token + + def processEndTag(self, token): + self.parser.parseError("expected-eof-but-got-end-tag", + {"name": token["name"]}) + self.parser.phase = self.parser.phases["inBody"] + return token + + class AfterAfterFramesetPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("noframes", self.startTagNoFrames) + ]) + self.startTagHandler.default = self.startTagOther + + def processEOF(self): + pass + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processSpaceCharacters(self, token): + return self.parser.phases["inBody"].processSpaceCharacters(token) + + def processCharacters(self, token): + self.parser.parseError("expected-eof-but-got-char") + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagNoFrames(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("expected-eof-but-got-start-tag", + {"name": token["name"]}) + + def processEndTag(self, token): + self.parser.parseError("expected-eof-but-got-end-tag", + {"name": token["name"]}) + # pylint:enable=unused-argument + + return { + "initial": InitialPhase, + "beforeHtml": BeforeHtmlPhase, + "beforeHead": BeforeHeadPhase, + "inHead": InHeadPhase, + "inHeadNoscript": InHeadNoscriptPhase, + "afterHead": AfterHeadPhase, + "inBody": InBodyPhase, + "text": TextPhase, + "inTable": InTablePhase, + "inTableText": InTableTextPhase, + "inCaption": InCaptionPhase, + "inColumnGroup": InColumnGroupPhase, + "inTableBody": InTableBodyPhase, + "inRow": InRowPhase, + "inCell": InCellPhase, + "inSelect": InSelectPhase, + "inSelectInTable": InSelectInTablePhase, + "inForeignContent": InForeignContentPhase, + "afterBody": AfterBodyPhase, + "inFrameset": InFramesetPhase, + "afterFrameset": AfterFramesetPhase, + "afterAfterBody": AfterAfterBodyPhase, + "afterAfterFrameset": AfterAfterFramesetPhase, + # XXX after after frameset + } + + +def adjust_attributes(token, replacements): + needs_adjustment = viewkeys(token['data']) & viewkeys(replacements) + if needs_adjustment: + token['data'] = OrderedDict((replacements.get(k, k), v) + for k, v in token['data'].items()) + + +def impliedTagToken(name, type="EndTag", attributes=None, + selfClosing=False): + if attributes is None: + attributes = {} + return {"type": tokenTypes[type], "name": name, "data": attributes, + "selfClosing": selfClosing} + + +class ParseError(Exception): + """Error in parsed document""" + pass diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/serializer.py b/venv/Lib/site-packages/pip/_vendor/html5lib/serializer.py new file mode 100644 index 0000000..53f4d44 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/serializer.py @@ -0,0 +1,409 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +import re + +from codecs import register_error, xmlcharrefreplace_errors + +from .constants import voidElements, booleanAttributes, spaceCharacters +from .constants import rcdataElements, entities, xmlEntities +from . import treewalkers, _utils +from xml.sax.saxutils import escape + +_quoteAttributeSpecChars = "".join(spaceCharacters) + "\"'=<>`" +_quoteAttributeSpec = re.compile("[" + _quoteAttributeSpecChars + "]") +_quoteAttributeLegacy = re.compile("[" + _quoteAttributeSpecChars + + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n" + "\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15" + "\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x2f\x60\xa0\u1680\u180e\u180f\u2000" + "\u2001\u2002\u2003\u2004\u2005\u2006\u2007" + "\u2008\u2009\u200a\u2028\u2029\u202f\u205f" + "\u3000]") + + +_encode_entity_map = {} +_is_ucs4 = len("\U0010FFFF") == 1 +for k, v in list(entities.items()): + # skip multi-character entities + if ((_is_ucs4 and len(v) > 1) or + (not _is_ucs4 and len(v) > 2)): + continue + if v != "&": + if len(v) == 2: + v = _utils.surrogatePairToCodepoint(v) + else: + v = ord(v) + if v not in _encode_entity_map or k.islower(): + # prefer < over < and similarly for &, >, etc. + _encode_entity_map[v] = k + + +def htmlentityreplace_errors(exc): + if isinstance(exc, (UnicodeEncodeError, UnicodeTranslateError)): + res = [] + codepoints = [] + skip = False + for i, c in enumerate(exc.object[exc.start:exc.end]): + if skip: + skip = False + continue + index = i + exc.start + if _utils.isSurrogatePair(exc.object[index:min([exc.end, index + 2])]): + codepoint = _utils.surrogatePairToCodepoint(exc.object[index:index + 2]) + skip = True + else: + codepoint = ord(c) + codepoints.append(codepoint) + for cp in codepoints: + e = _encode_entity_map.get(cp) + if e: + res.append("&") + res.append(e) + if not e.endswith(";"): + res.append(";") + else: + res.append("&#x%s;" % (hex(cp)[2:])) + return ("".join(res), exc.end) + else: + return xmlcharrefreplace_errors(exc) + + +register_error("htmlentityreplace", htmlentityreplace_errors) + + +def serialize(input, tree="etree", encoding=None, **serializer_opts): + """Serializes the input token stream using the specified treewalker + + :arg input: the token stream to serialize + + :arg tree: the treewalker to use + + :arg encoding: the encoding to use + + :arg serializer_opts: any options to pass to the + :py:class:`html5lib.serializer.HTMLSerializer` that gets created + + :returns: the tree serialized as a string + + Example: + + >>> from html5lib.html5parser import parse + >>> from html5lib.serializer import serialize + >>> token_stream = parse('<html><body><p>Hi!</p></body></html>') + >>> serialize(token_stream, omit_optional_tags=False) + '<html><head></head><body><p>Hi!</p></body></html>' + + """ + # XXX: Should we cache this? + walker = treewalkers.getTreeWalker(tree) + s = HTMLSerializer(**serializer_opts) + return s.render(walker(input), encoding) + + +class HTMLSerializer(object): + + # attribute quoting options + quote_attr_values = "legacy" # be secure by default + quote_char = '"' + use_best_quote_char = True + + # tag syntax options + omit_optional_tags = True + minimize_boolean_attributes = True + use_trailing_solidus = False + space_before_trailing_solidus = True + + # escaping options + escape_lt_in_attrs = False + escape_rcdata = False + resolve_entities = True + + # miscellaneous options + alphabetical_attributes = False + inject_meta_charset = True + strip_whitespace = False + sanitize = False + + options = ("quote_attr_values", "quote_char", "use_best_quote_char", + "omit_optional_tags", "minimize_boolean_attributes", + "use_trailing_solidus", "space_before_trailing_solidus", + "escape_lt_in_attrs", "escape_rcdata", "resolve_entities", + "alphabetical_attributes", "inject_meta_charset", + "strip_whitespace", "sanitize") + + def __init__(self, **kwargs): + """Initialize HTMLSerializer + + :arg inject_meta_charset: Whether or not to inject the meta charset. + + Defaults to ``True``. + + :arg quote_attr_values: Whether to quote attribute values that don't + require quoting per legacy browser behavior (``"legacy"``), when + required by the standard (``"spec"``), or always (``"always"``). + + Defaults to ``"legacy"``. + + :arg quote_char: Use given quote character for attribute quoting. + + Defaults to ``"`` which will use double quotes unless attribute + value contains a double quote, in which case single quotes are + used. + + :arg escape_lt_in_attrs: Whether or not to escape ``<`` in attribute + values. + + Defaults to ``False``. + + :arg escape_rcdata: Whether to escape characters that need to be + escaped within normal elements within rcdata elements such as + style. + + Defaults to ``False``. + + :arg resolve_entities: Whether to resolve named character entities that + appear in the source tree. The XML predefined entities < > + & " ' are unaffected by this setting. + + Defaults to ``True``. + + :arg strip_whitespace: Whether to remove semantically meaningless + whitespace. (This compresses all whitespace to a single space + except within ``pre``.) + + Defaults to ``False``. + + :arg minimize_boolean_attributes: Shortens boolean attributes to give + just the attribute value, for example:: + + <input disabled="disabled"> + + becomes:: + + <input disabled> + + Defaults to ``True``. + + :arg use_trailing_solidus: Includes a close-tag slash at the end of the + start tag of void elements (empty elements whose end tag is + forbidden). E.g. ``<hr/>``. + + Defaults to ``False``. + + :arg space_before_trailing_solidus: Places a space immediately before + the closing slash in a tag using a trailing solidus. E.g. + ``<hr />``. Requires ``use_trailing_solidus=True``. + + Defaults to ``True``. + + :arg sanitize: Strip all unsafe or unknown constructs from output. + See :py:class:`html5lib.filters.sanitizer.Filter`. + + Defaults to ``False``. + + :arg omit_optional_tags: Omit start/end tags that are optional. + + Defaults to ``True``. + + :arg alphabetical_attributes: Reorder attributes to be in alphabetical order. + + Defaults to ``False``. + + """ + unexpected_args = frozenset(kwargs) - frozenset(self.options) + if len(unexpected_args) > 0: + raise TypeError("__init__() got an unexpected keyword argument '%s'" % next(iter(unexpected_args))) + if 'quote_char' in kwargs: + self.use_best_quote_char = False + for attr in self.options: + setattr(self, attr, kwargs.get(attr, getattr(self, attr))) + self.errors = [] + self.strict = False + + def encode(self, string): + assert(isinstance(string, text_type)) + if self.encoding: + return string.encode(self.encoding, "htmlentityreplace") + else: + return string + + def encodeStrict(self, string): + assert(isinstance(string, text_type)) + if self.encoding: + return string.encode(self.encoding, "strict") + else: + return string + + def serialize(self, treewalker, encoding=None): + # pylint:disable=too-many-nested-blocks + self.encoding = encoding + in_cdata = False + self.errors = [] + + if encoding and self.inject_meta_charset: + from .filters.inject_meta_charset import Filter + treewalker = Filter(treewalker, encoding) + # Alphabetical attributes is here under the assumption that none of + # the later filters add or change order of attributes; it needs to be + # before the sanitizer so escaped elements come out correctly + if self.alphabetical_attributes: + from .filters.alphabeticalattributes import Filter + treewalker = Filter(treewalker) + # WhitespaceFilter should be used before OptionalTagFilter + # for maximum efficiently of this latter filter + if self.strip_whitespace: + from .filters.whitespace import Filter + treewalker = Filter(treewalker) + if self.sanitize: + from .filters.sanitizer import Filter + treewalker = Filter(treewalker) + if self.omit_optional_tags: + from .filters.optionaltags import Filter + treewalker = Filter(treewalker) + + for token in treewalker: + type = token["type"] + if type == "Doctype": + doctype = "<!DOCTYPE %s" % token["name"] + + if token["publicId"]: + doctype += ' PUBLIC "%s"' % token["publicId"] + elif token["systemId"]: + doctype += " SYSTEM" + if token["systemId"]: + if token["systemId"].find('"') >= 0: + if token["systemId"].find("'") >= 0: + self.serializeError("System identifer contains both single and double quote characters") + quote_char = "'" + else: + quote_char = '"' + doctype += " %s%s%s" % (quote_char, token["systemId"], quote_char) + + doctype += ">" + yield self.encodeStrict(doctype) + + elif type in ("Characters", "SpaceCharacters"): + if type == "SpaceCharacters" or in_cdata: + if in_cdata and token["data"].find("</") >= 0: + self.serializeError("Unexpected </ in CDATA") + yield self.encode(token["data"]) + else: + yield self.encode(escape(token["data"])) + + elif type in ("StartTag", "EmptyTag"): + name = token["name"] + yield self.encodeStrict("<%s" % name) + if name in rcdataElements and not self.escape_rcdata: + in_cdata = True + elif in_cdata: + self.serializeError("Unexpected child element of a CDATA element") + for (_, attr_name), attr_value in token["data"].items(): + # TODO: Add namespace support here + k = attr_name + v = attr_value + yield self.encodeStrict(' ') + + yield self.encodeStrict(k) + if not self.minimize_boolean_attributes or \ + (k not in booleanAttributes.get(name, tuple()) and + k not in booleanAttributes.get("", tuple())): + yield self.encodeStrict("=") + if self.quote_attr_values == "always" or len(v) == 0: + quote_attr = True + elif self.quote_attr_values == "spec": + quote_attr = _quoteAttributeSpec.search(v) is not None + elif self.quote_attr_values == "legacy": + quote_attr = _quoteAttributeLegacy.search(v) is not None + else: + raise ValueError("quote_attr_values must be one of: " + "'always', 'spec', or 'legacy'") + v = v.replace("&", "&") + if self.escape_lt_in_attrs: + v = v.replace("<", "<") + if quote_attr: + quote_char = self.quote_char + if self.use_best_quote_char: + if "'" in v and '"' not in v: + quote_char = '"' + elif '"' in v and "'" not in v: + quote_char = "'" + if quote_char == "'": + v = v.replace("'", "'") + else: + v = v.replace('"', """) + yield self.encodeStrict(quote_char) + yield self.encode(v) + yield self.encodeStrict(quote_char) + else: + yield self.encode(v) + if name in voidElements and self.use_trailing_solidus: + if self.space_before_trailing_solidus: + yield self.encodeStrict(" /") + else: + yield self.encodeStrict("/") + yield self.encode(">") + + elif type == "EndTag": + name = token["name"] + if name in rcdataElements: + in_cdata = False + elif in_cdata: + self.serializeError("Unexpected child element of a CDATA element") + yield self.encodeStrict("</%s>" % name) + + elif type == "Comment": + data = token["data"] + if data.find("--") >= 0: + self.serializeError("Comment contains --") + yield self.encodeStrict("<!--%s-->" % token["data"]) + + elif type == "Entity": + name = token["name"] + key = name + ";" + if key not in entities: + self.serializeError("Entity %s not recognized" % name) + if self.resolve_entities and key not in xmlEntities: + data = entities[key] + else: + data = "&%s;" % name + yield self.encodeStrict(data) + + else: + self.serializeError(token["data"]) + + def render(self, treewalker, encoding=None): + """Serializes the stream from the treewalker into a string + + :arg treewalker: the treewalker to serialize + + :arg encoding: the string encoding to use + + :returns: the serialized tree + + Example: + + >>> from html5lib import parse, getTreeWalker + >>> from html5lib.serializer import HTMLSerializer + >>> token_stream = parse('<html><body>Hi!</body></html>') + >>> walker = getTreeWalker('etree') + >>> serializer = HTMLSerializer(omit_optional_tags=False) + >>> serializer.render(walker(token_stream)) + '<html><head></head><body>Hi!</body></html>' + + """ + if encoding: + return b"".join(list(self.serialize(treewalker, encoding))) + else: + return "".join(list(self.serialize(treewalker))) + + def serializeError(self, data="XXX ERROR MESSAGE NEEDED"): + # XXX The idea is to make data mandatory. + self.errors.append(data) + if self.strict: + raise SerializeError + + +class SerializeError(Exception): + """Error in serialized tree""" + pass diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py new file mode 100644 index 0000000..7ef5959 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py @@ -0,0 +1,30 @@ +"""Tree adapters let you convert from one tree structure to another + +Example: + +.. code-block:: python + + from pip._vendor import html5lib + from pip._vendor.html5lib.treeadapters import genshi + + doc = '<html><body>Hi!</body></html>' + treebuilder = html5lib.getTreeBuilder('etree') + parser = html5lib.HTMLParser(tree=treebuilder) + tree = parser.parse(doc) + TreeWalker = html5lib.getTreeWalker('etree') + + genshi_tree = genshi.to_genshi(TreeWalker(tree)) + +""" +from __future__ import absolute_import, division, unicode_literals + +from . import sax + +__all__ = ["sax"] + +try: + from . import genshi # noqa +except ImportError: + pass +else: + __all__.append("genshi") diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py new file mode 100644 index 0000000..61d5fb6 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py @@ -0,0 +1,54 @@ +from __future__ import absolute_import, division, unicode_literals + +from genshi.core import QName, Attrs +from genshi.core import START, END, TEXT, COMMENT, DOCTYPE + + +def to_genshi(walker): + """Convert a tree to a genshi tree + + :arg walker: the treewalker to use to walk the tree to convert it + + :returns: generator of genshi nodes + + """ + text = [] + for token in walker: + type = token["type"] + if type in ("Characters", "SpaceCharacters"): + text.append(token["data"]) + elif text: + yield TEXT, "".join(text), (None, -1, -1) + text = [] + + if type in ("StartTag", "EmptyTag"): + if token["namespace"]: + name = "{%s}%s" % (token["namespace"], token["name"]) + else: + name = token["name"] + attrs = Attrs([(QName("{%s}%s" % attr if attr[0] is not None else attr[1]), value) + for attr, value in token["data"].items()]) + yield (START, (QName(name), attrs), (None, -1, -1)) + if type == "EmptyTag": + type = "EndTag" + + if type == "EndTag": + if token["namespace"]: + name = "{%s}%s" % (token["namespace"], token["name"]) + else: + name = token["name"] + + yield END, QName(name), (None, -1, -1) + + elif type == "Comment": + yield COMMENT, token["data"], (None, -1, -1) + + elif type == "Doctype": + yield DOCTYPE, (token["name"], token["publicId"], + token["systemId"]), (None, -1, -1) + + else: + pass # FIXME: What to do? + + if text: + yield TEXT, "".join(text), (None, -1, -1) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/sax.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/sax.py new file mode 100644 index 0000000..f4ccea5 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/sax.py @@ -0,0 +1,50 @@ +from __future__ import absolute_import, division, unicode_literals + +from xml.sax.xmlreader import AttributesNSImpl + +from ..constants import adjustForeignAttributes, unadjustForeignAttributes + +prefix_mapping = {} +for prefix, localName, namespace in adjustForeignAttributes.values(): + if prefix is not None: + prefix_mapping[prefix] = namespace + + +def to_sax(walker, handler): + """Call SAX-like content handler based on treewalker walker + + :arg walker: the treewalker to use to walk the tree to convert it + + :arg handler: SAX handler to use + + """ + handler.startDocument() + for prefix, namespace in prefix_mapping.items(): + handler.startPrefixMapping(prefix, namespace) + + for token in walker: + type = token["type"] + if type == "Doctype": + continue + elif type in ("StartTag", "EmptyTag"): + attrs = AttributesNSImpl(token["data"], + unadjustForeignAttributes) + handler.startElementNS((token["namespace"], token["name"]), + token["name"], + attrs) + if type == "EmptyTag": + handler.endElementNS((token["namespace"], token["name"]), + token["name"]) + elif type == "EndTag": + handler.endElementNS((token["namespace"], token["name"]), + token["name"]) + elif type in ("Characters", "SpaceCharacters"): + handler.characters(token["data"]) + elif type == "Comment": + pass + else: + assert False, "Unknown token type" + + for prefix, namespace in prefix_mapping.items(): + handler.endPrefixMapping(prefix) + handler.endDocument() diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py new file mode 100644 index 0000000..d44447e --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py @@ -0,0 +1,88 @@ +"""A collection of modules for building different kinds of trees from HTML +documents. + +To create a treebuilder for a new type of tree, you need to do +implement several things: + +1. A set of classes for various types of elements: Document, Doctype, Comment, + Element. These must implement the interface of ``base.treebuilders.Node`` + (although comment nodes have a different signature for their constructor, + see ``treebuilders.etree.Comment``) Textual content may also be implemented + as another node type, or not, as your tree implementation requires. + +2. A treebuilder object (called ``TreeBuilder`` by convention) that inherits + from ``treebuilders.base.TreeBuilder``. This has 4 required attributes: + + * ``documentClass`` - the class to use for the bottommost node of a document + * ``elementClass`` - the class to use for HTML Elements + * ``commentClass`` - the class to use for comments + * ``doctypeClass`` - the class to use for doctypes + + It also has one required method: + + * ``getDocument`` - Returns the root node of the complete document tree + +3. If you wish to run the unit tests, you must also create a ``testSerializer`` + method on your treebuilder which accepts a node and returns a string + containing Node and its children serialized according to the format used in + the unittests + +""" + +from __future__ import absolute_import, division, unicode_literals + +from .._utils import default_etree + +treeBuilderCache = {} + + +def getTreeBuilder(treeType, implementation=None, **kwargs): + """Get a TreeBuilder class for various types of trees with built-in support + + :arg treeType: the name of the tree type required (case-insensitive). Supported + values are: + + * "dom" - A generic builder for DOM implementations, defaulting to a + xml.dom.minidom based implementation. + * "etree" - A generic builder for tree implementations exposing an + ElementTree-like interface, defaulting to xml.etree.cElementTree if + available and xml.etree.ElementTree if not. + * "lxml" - A etree-based builder for lxml.etree, handling limitations + of lxml's implementation. + + :arg implementation: (Currently applies to the "etree" and "dom" tree + types). A module implementing the tree type e.g. xml.etree.ElementTree + or xml.etree.cElementTree. + + :arg kwargs: Any additional options to pass to the TreeBuilder when + creating it. + + Example: + + >>> from html5lib.treebuilders import getTreeBuilder + >>> builder = getTreeBuilder('etree') + + """ + + treeType = treeType.lower() + if treeType not in treeBuilderCache: + if treeType == "dom": + from . import dom + # Come up with a sane default (pref. from the stdlib) + if implementation is None: + from xml.dom import minidom + implementation = minidom + # NEVER cache here, caching is done in the dom submodule + return dom.getDomModule(implementation, **kwargs).TreeBuilder + elif treeType == "lxml": + from . import etree_lxml + treeBuilderCache[treeType] = etree_lxml.TreeBuilder + elif treeType == "etree": + from . import etree + if implementation is None: + implementation = default_etree + # NEVER cache here, caching is done in the etree submodule + return etree.getETreeModule(implementation, **kwargs).TreeBuilder + else: + raise ValueError("""Unrecognised treebuilder "%s" """ % treeType) + return treeBuilderCache.get(treeType) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/base.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/base.py new file mode 100644 index 0000000..73973db --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/base.py @@ -0,0 +1,417 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +from ..constants import scopingElements, tableInsertModeElements, namespaces + +# The scope markers are inserted when entering object elements, +# marquees, table cells, and table captions, and are used to prevent formatting +# from "leaking" into tables, object elements, and marquees. +Marker = None + +listElementsMap = { + None: (frozenset(scopingElements), False), + "button": (frozenset(scopingElements | set([(namespaces["html"], "button")])), False), + "list": (frozenset(scopingElements | set([(namespaces["html"], "ol"), + (namespaces["html"], "ul")])), False), + "table": (frozenset([(namespaces["html"], "html"), + (namespaces["html"], "table")]), False), + "select": (frozenset([(namespaces["html"], "optgroup"), + (namespaces["html"], "option")]), True) +} + + +class Node(object): + """Represents an item in the tree""" + def __init__(self, name): + """Creates a Node + + :arg name: The tag name associated with the node + + """ + # The tag name assocaited with the node + self.name = name + # The parent of the current node (or None for the document node) + self.parent = None + # The value of the current node (applies to text nodes and comments) + self.value = None + # A dict holding name -> value pairs for attributes of the node + self.attributes = {} + # A list of child nodes of the current node. This must include all + # elements but not necessarily other node types. + self.childNodes = [] + # A list of miscellaneous flags that can be set on the node. + self._flags = [] + + def __str__(self): + attributesStr = " ".join(["%s=\"%s\"" % (name, value) + for name, value in + self.attributes.items()]) + if attributesStr: + return "<%s %s>" % (self.name, attributesStr) + else: + return "<%s>" % (self.name) + + def __repr__(self): + return "<%s>" % (self.name) + + def appendChild(self, node): + """Insert node as a child of the current node + + :arg node: the node to insert + + """ + raise NotImplementedError + + def insertText(self, data, insertBefore=None): + """Insert data as text in the current node, positioned before the + start of node insertBefore or to the end of the node's text. + + :arg data: the data to insert + + :arg insertBefore: True if you want to insert the text before the node + and False if you want to insert it after the node + + """ + raise NotImplementedError + + def insertBefore(self, node, refNode): + """Insert node as a child of the current node, before refNode in the + list of child nodes. Raises ValueError if refNode is not a child of + the current node + + :arg node: the node to insert + + :arg refNode: the child node to insert the node before + + """ + raise NotImplementedError + + def removeChild(self, node): + """Remove node from the children of the current node + + :arg node: the child node to remove + + """ + raise NotImplementedError + + def reparentChildren(self, newParent): + """Move all the children of the current node to newParent. + This is needed so that trees that don't store text as nodes move the + text in the correct way + + :arg newParent: the node to move all this node's children to + + """ + # XXX - should this method be made more general? + for child in self.childNodes: + newParent.appendChild(child) + self.childNodes = [] + + def cloneNode(self): + """Return a shallow copy of the current node i.e. a node with the same + name and attributes but with no parent or child nodes + """ + raise NotImplementedError + + def hasContent(self): + """Return true if the node has children or text, false otherwise + """ + raise NotImplementedError + + +class ActiveFormattingElements(list): + def append(self, node): + equalCount = 0 + if node != Marker: + for element in self[::-1]: + if element == Marker: + break + if self.nodesEqual(element, node): + equalCount += 1 + if equalCount == 3: + self.remove(element) + break + list.append(self, node) + + def nodesEqual(self, node1, node2): + if not node1.nameTuple == node2.nameTuple: + return False + + if not node1.attributes == node2.attributes: + return False + + return True + + +class TreeBuilder(object): + """Base treebuilder implementation + + * documentClass - the class to use for the bottommost node of a document + * elementClass - the class to use for HTML Elements + * commentClass - the class to use for comments + * doctypeClass - the class to use for doctypes + + """ + # pylint:disable=not-callable + + # Document class + documentClass = None + + # The class to use for creating a node + elementClass = None + + # The class to use for creating comments + commentClass = None + + # The class to use for creating doctypes + doctypeClass = None + + # Fragment class + fragmentClass = None + + def __init__(self, namespaceHTMLElements): + """Create a TreeBuilder + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + """ + if namespaceHTMLElements: + self.defaultNamespace = "http://www.w3.org/1999/xhtml" + else: + self.defaultNamespace = None + self.reset() + + def reset(self): + self.openElements = [] + self.activeFormattingElements = ActiveFormattingElements() + + # XXX - rename these to headElement, formElement + self.headPointer = None + self.formPointer = None + + self.insertFromTable = False + + self.document = self.documentClass() + + def elementInScope(self, target, variant=None): + + # If we pass a node in we match that. if we pass a string + # match any node with that name + exactNode = hasattr(target, "nameTuple") + if not exactNode: + if isinstance(target, text_type): + target = (namespaces["html"], target) + assert isinstance(target, tuple) + + listElements, invert = listElementsMap[variant] + + for node in reversed(self.openElements): + if exactNode and node == target: + return True + elif not exactNode and node.nameTuple == target: + return True + elif (invert ^ (node.nameTuple in listElements)): + return False + + assert False # We should never reach this point + + def reconstructActiveFormattingElements(self): + # Within this algorithm the order of steps described in the + # specification is not quite the same as the order of steps in the + # code. It should still do the same though. + + # Step 1: stop the algorithm when there's nothing to do. + if not self.activeFormattingElements: + return + + # Step 2 and step 3: we start with the last element. So i is -1. + i = len(self.activeFormattingElements) - 1 + entry = self.activeFormattingElements[i] + if entry == Marker or entry in self.openElements: + return + + # Step 6 + while entry != Marker and entry not in self.openElements: + if i == 0: + # This will be reset to 0 below + i = -1 + break + i -= 1 + # Step 5: let entry be one earlier in the list. + entry = self.activeFormattingElements[i] + + while True: + # Step 7 + i += 1 + + # Step 8 + entry = self.activeFormattingElements[i] + clone = entry.cloneNode() # Mainly to get a new copy of the attributes + + # Step 9 + element = self.insertElement({"type": "StartTag", + "name": clone.name, + "namespace": clone.namespace, + "data": clone.attributes}) + + # Step 10 + self.activeFormattingElements[i] = element + + # Step 11 + if element == self.activeFormattingElements[-1]: + break + + def clearActiveFormattingElements(self): + entry = self.activeFormattingElements.pop() + while self.activeFormattingElements and entry != Marker: + entry = self.activeFormattingElements.pop() + + def elementInActiveFormattingElements(self, name): + """Check if an element exists between the end of the active + formatting elements and the last marker. If it does, return it, else + return false""" + + for item in self.activeFormattingElements[::-1]: + # Check for Marker first because if it's a Marker it doesn't have a + # name attribute. + if item == Marker: + break + elif item.name == name: + return item + return False + + def insertRoot(self, token): + element = self.createElement(token) + self.openElements.append(element) + self.document.appendChild(element) + + def insertDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + + doctype = self.doctypeClass(name, publicId, systemId) + self.document.appendChild(doctype) + + def insertComment(self, token, parent=None): + if parent is None: + parent = self.openElements[-1] + parent.appendChild(self.commentClass(token["data"])) + + def createElement(self, token): + """Create an element but don't insert it anywhere""" + name = token["name"] + namespace = token.get("namespace", self.defaultNamespace) + element = self.elementClass(name, namespace) + element.attributes = token["data"] + return element + + def _getInsertFromTable(self): + return self._insertFromTable + + def _setInsertFromTable(self, value): + """Switch the function used to insert an element from the + normal one to the misnested table one and back again""" + self._insertFromTable = value + if value: + self.insertElement = self.insertElementTable + else: + self.insertElement = self.insertElementNormal + + insertFromTable = property(_getInsertFromTable, _setInsertFromTable) + + def insertElementNormal(self, token): + name = token["name"] + assert isinstance(name, text_type), "Element %s not unicode" % name + namespace = token.get("namespace", self.defaultNamespace) + element = self.elementClass(name, namespace) + element.attributes = token["data"] + self.openElements[-1].appendChild(element) + self.openElements.append(element) + return element + + def insertElementTable(self, token): + """Create an element and insert it into the tree""" + element = self.createElement(token) + if self.openElements[-1].name not in tableInsertModeElements: + return self.insertElementNormal(token) + else: + # We should be in the InTable mode. This means we want to do + # special magic element rearranging + parent, insertBefore = self.getTableMisnestedNodePosition() + if insertBefore is None: + parent.appendChild(element) + else: + parent.insertBefore(element, insertBefore) + self.openElements.append(element) + return element + + def insertText(self, data, parent=None): + """Insert text data.""" + if parent is None: + parent = self.openElements[-1] + + if (not self.insertFromTable or (self.insertFromTable and + self.openElements[-1].name + not in tableInsertModeElements)): + parent.insertText(data) + else: + # We should be in the InTable mode. This means we want to do + # special magic element rearranging + parent, insertBefore = self.getTableMisnestedNodePosition() + parent.insertText(data, insertBefore) + + def getTableMisnestedNodePosition(self): + """Get the foster parent element, and sibling to insert before + (or None) when inserting a misnested table node""" + # The foster parent element is the one which comes before the most + # recently opened table element + # XXX - this is really inelegant + lastTable = None + fosterParent = None + insertBefore = None + for elm in self.openElements[::-1]: + if elm.name == "table": + lastTable = elm + break + if lastTable: + # XXX - we should really check that this parent is actually a + # node here + if lastTable.parent: + fosterParent = lastTable.parent + insertBefore = lastTable + else: + fosterParent = self.openElements[ + self.openElements.index(lastTable) - 1] + else: + fosterParent = self.openElements[0] + return fosterParent, insertBefore + + def generateImpliedEndTags(self, exclude=None): + name = self.openElements[-1].name + # XXX td, th and tr are not actually needed + if (name in frozenset(("dd", "dt", "li", "option", "optgroup", "p", "rp", "rt")) and + name != exclude): + self.openElements.pop() + # XXX This is not entirely what the specification says. We should + # investigate it more closely. + self.generateImpliedEndTags(exclude) + + def getDocument(self): + """Return the final tree""" + return self.document + + def getFragment(self): + """Return the final fragment""" + # assert self.innerHTML + fragment = self.fragmentClass() + self.openElements[0].reparentChildren(fragment) + return fragment + + def testSerializer(self, node): + """Serialize the subtree of node in the format required by unit tests + + :arg node: the node from which to start serializing + + """ + raise NotImplementedError diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/dom.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/dom.py new file mode 100644 index 0000000..dcfac22 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/dom.py @@ -0,0 +1,236 @@ +from __future__ import absolute_import, division, unicode_literals + + +from collections import MutableMapping +from xml.dom import minidom, Node +import weakref + +from . import base +from .. import constants +from ..constants import namespaces +from .._utils import moduleFactoryFactory + + +def getDomBuilder(DomImplementation): + Dom = DomImplementation + + class AttrList(MutableMapping): + def __init__(self, element): + self.element = element + + def __iter__(self): + return iter(self.element.attributes.keys()) + + def __setitem__(self, name, value): + if isinstance(name, tuple): + raise NotImplementedError + else: + attr = self.element.ownerDocument.createAttribute(name) + attr.value = value + self.element.attributes[name] = attr + + def __len__(self): + return len(self.element.attributes) + + def items(self): + return list(self.element.attributes.items()) + + def values(self): + return list(self.element.attributes.values()) + + def __getitem__(self, name): + if isinstance(name, tuple): + raise NotImplementedError + else: + return self.element.attributes[name].value + + def __delitem__(self, name): + if isinstance(name, tuple): + raise NotImplementedError + else: + del self.element.attributes[name] + + class NodeBuilder(base.Node): + def __init__(self, element): + base.Node.__init__(self, element.nodeName) + self.element = element + + namespace = property(lambda self: hasattr(self.element, "namespaceURI") and + self.element.namespaceURI or None) + + def appendChild(self, node): + node.parent = self + self.element.appendChild(node.element) + + def insertText(self, data, insertBefore=None): + text = self.element.ownerDocument.createTextNode(data) + if insertBefore: + self.element.insertBefore(text, insertBefore.element) + else: + self.element.appendChild(text) + + def insertBefore(self, node, refNode): + self.element.insertBefore(node.element, refNode.element) + node.parent = self + + def removeChild(self, node): + if node.element.parentNode == self.element: + self.element.removeChild(node.element) + node.parent = None + + def reparentChildren(self, newParent): + while self.element.hasChildNodes(): + child = self.element.firstChild + self.element.removeChild(child) + newParent.element.appendChild(child) + self.childNodes = [] + + def getAttributes(self): + return AttrList(self.element) + + def setAttributes(self, attributes): + if attributes: + for name, value in list(attributes.items()): + if isinstance(name, tuple): + if name[0] is not None: + qualifiedName = (name[0] + ":" + name[1]) + else: + qualifiedName = name[1] + self.element.setAttributeNS(name[2], qualifiedName, + value) + else: + self.element.setAttribute( + name, value) + attributes = property(getAttributes, setAttributes) + + def cloneNode(self): + return NodeBuilder(self.element.cloneNode(False)) + + def hasContent(self): + return self.element.hasChildNodes() + + def getNameTuple(self): + if self.namespace is None: + return namespaces["html"], self.name + else: + return self.namespace, self.name + + nameTuple = property(getNameTuple) + + class TreeBuilder(base.TreeBuilder): # pylint:disable=unused-variable + def documentClass(self): + self.dom = Dom.getDOMImplementation().createDocument(None, None, None) + return weakref.proxy(self) + + def insertDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + + domimpl = Dom.getDOMImplementation() + doctype = domimpl.createDocumentType(name, publicId, systemId) + self.document.appendChild(NodeBuilder(doctype)) + if Dom == minidom: + doctype.ownerDocument = self.dom + + def elementClass(self, name, namespace=None): + if namespace is None and self.defaultNamespace is None: + node = self.dom.createElement(name) + else: + node = self.dom.createElementNS(namespace, name) + + return NodeBuilder(node) + + def commentClass(self, data): + return NodeBuilder(self.dom.createComment(data)) + + def fragmentClass(self): + return NodeBuilder(self.dom.createDocumentFragment()) + + def appendChild(self, node): + self.dom.appendChild(node.element) + + def testSerializer(self, element): + return testSerializer(element) + + def getDocument(self): + return self.dom + + def getFragment(self): + return base.TreeBuilder.getFragment(self).element + + def insertText(self, data, parent=None): + data = data + if parent != self: + base.TreeBuilder.insertText(self, data, parent) + else: + # HACK: allow text nodes as children of the document node + if hasattr(self.dom, '_child_node_types'): + # pylint:disable=protected-access + if Node.TEXT_NODE not in self.dom._child_node_types: + self.dom._child_node_types = list(self.dom._child_node_types) + self.dom._child_node_types.append(Node.TEXT_NODE) + self.dom.appendChild(self.dom.createTextNode(data)) + + implementation = DomImplementation + name = None + + def testSerializer(element): + element.normalize() + rv = [] + + def serializeElement(element, indent=0): + if element.nodeType == Node.DOCUMENT_TYPE_NODE: + if element.name: + if element.publicId or element.systemId: + publicId = element.publicId or "" + systemId = element.systemId or "" + rv.append("""|%s<!DOCTYPE %s "%s" "%s">""" % + (' ' * indent, element.name, publicId, systemId)) + else: + rv.append("|%s<!DOCTYPE %s>" % (' ' * indent, element.name)) + else: + rv.append("|%s<!DOCTYPE >" % (' ' * indent,)) + elif element.nodeType == Node.DOCUMENT_NODE: + rv.append("#document") + elif element.nodeType == Node.DOCUMENT_FRAGMENT_NODE: + rv.append("#document-fragment") + elif element.nodeType == Node.COMMENT_NODE: + rv.append("|%s<!-- %s -->" % (' ' * indent, element.nodeValue)) + elif element.nodeType == Node.TEXT_NODE: + rv.append("|%s\"%s\"" % (' ' * indent, element.nodeValue)) + else: + if (hasattr(element, "namespaceURI") and + element.namespaceURI is not None): + name = "%s %s" % (constants.prefixes[element.namespaceURI], + element.nodeName) + else: + name = element.nodeName + rv.append("|%s<%s>" % (' ' * indent, name)) + if element.hasAttributes(): + attributes = [] + for i in range(len(element.attributes)): + attr = element.attributes.item(i) + name = attr.nodeName + value = attr.value + ns = attr.namespaceURI + if ns: + name = "%s %s" % (constants.prefixes[ns], attr.localName) + else: + name = attr.nodeName + attributes.append((name, value)) + + for name, value in sorted(attributes): + rv.append('|%s%s="%s"' % (' ' * (indent + 2), name, value)) + indent += 2 + for child in element.childNodes: + serializeElement(child, indent) + serializeElement(element, 0) + + return "\n".join(rv) + + return locals() + + +# The actual means to get a module! +getDomModule = moduleFactoryFactory(getDomBuilder) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree.py new file mode 100644 index 0000000..0dedf44 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree.py @@ -0,0 +1,340 @@ +from __future__ import absolute_import, division, unicode_literals +# pylint:disable=protected-access + +from pip._vendor.six import text_type + +import re + +from . import base +from .. import _ihatexml +from .. import constants +from ..constants import namespaces +from .._utils import moduleFactoryFactory + +tag_regexp = re.compile("{([^}]*)}(.*)") + + +def getETreeBuilder(ElementTreeImplementation, fullTree=False): + ElementTree = ElementTreeImplementation + ElementTreeCommentType = ElementTree.Comment("asd").tag + + class Element(base.Node): + def __init__(self, name, namespace=None): + self._name = name + self._namespace = namespace + self._element = ElementTree.Element(self._getETreeTag(name, + namespace)) + if namespace is None: + self.nameTuple = namespaces["html"], self._name + else: + self.nameTuple = self._namespace, self._name + self.parent = None + self._childNodes = [] + self._flags = [] + + def _getETreeTag(self, name, namespace): + if namespace is None: + etree_tag = name + else: + etree_tag = "{%s}%s" % (namespace, name) + return etree_tag + + def _setName(self, name): + self._name = name + self._element.tag = self._getETreeTag(self._name, self._namespace) + + def _getName(self): + return self._name + + name = property(_getName, _setName) + + def _setNamespace(self, namespace): + self._namespace = namespace + self._element.tag = self._getETreeTag(self._name, self._namespace) + + def _getNamespace(self): + return self._namespace + + namespace = property(_getNamespace, _setNamespace) + + def _getAttributes(self): + return self._element.attrib + + def _setAttributes(self, attributes): + # Delete existing attributes first + # XXX - there may be a better way to do this... + for key in list(self._element.attrib.keys()): + del self._element.attrib[key] + for key, value in attributes.items(): + if isinstance(key, tuple): + name = "{%s}%s" % (key[2], key[1]) + else: + name = key + self._element.set(name, value) + + attributes = property(_getAttributes, _setAttributes) + + def _getChildNodes(self): + return self._childNodes + + def _setChildNodes(self, value): + del self._element[:] + self._childNodes = [] + for element in value: + self.insertChild(element) + + childNodes = property(_getChildNodes, _setChildNodes) + + def hasContent(self): + """Return true if the node has children or text""" + return bool(self._element.text or len(self._element)) + + def appendChild(self, node): + self._childNodes.append(node) + self._element.append(node._element) + node.parent = self + + def insertBefore(self, node, refNode): + index = list(self._element).index(refNode._element) + self._element.insert(index, node._element) + node.parent = self + + def removeChild(self, node): + self._childNodes.remove(node) + self._element.remove(node._element) + node.parent = None + + def insertText(self, data, insertBefore=None): + if not(len(self._element)): + if not self._element.text: + self._element.text = "" + self._element.text += data + elif insertBefore is None: + # Insert the text as the tail of the last child element + if not self._element[-1].tail: + self._element[-1].tail = "" + self._element[-1].tail += data + else: + # Insert the text before the specified node + children = list(self._element) + index = children.index(insertBefore._element) + if index > 0: + if not self._element[index - 1].tail: + self._element[index - 1].tail = "" + self._element[index - 1].tail += data + else: + if not self._element.text: + self._element.text = "" + self._element.text += data + + def cloneNode(self): + element = type(self)(self.name, self.namespace) + for name, value in self.attributes.items(): + element.attributes[name] = value + return element + + def reparentChildren(self, newParent): + if newParent.childNodes: + newParent.childNodes[-1]._element.tail += self._element.text + else: + if not newParent._element.text: + newParent._element.text = "" + if self._element.text is not None: + newParent._element.text += self._element.text + self._element.text = "" + base.Node.reparentChildren(self, newParent) + + class Comment(Element): + def __init__(self, data): + # Use the superclass constructor to set all properties on the + # wrapper element + self._element = ElementTree.Comment(data) + self.parent = None + self._childNodes = [] + self._flags = [] + + def _getData(self): + return self._element.text + + def _setData(self, value): + self._element.text = value + + data = property(_getData, _setData) + + class DocumentType(Element): + def __init__(self, name, publicId, systemId): + Element.__init__(self, "<!DOCTYPE>") + self._element.text = name + self.publicId = publicId + self.systemId = systemId + + def _getPublicId(self): + return self._element.get("publicId", "") + + def _setPublicId(self, value): + if value is not None: + self._element.set("publicId", value) + + publicId = property(_getPublicId, _setPublicId) + + def _getSystemId(self): + return self._element.get("systemId", "") + + def _setSystemId(self, value): + if value is not None: + self._element.set("systemId", value) + + systemId = property(_getSystemId, _setSystemId) + + class Document(Element): + def __init__(self): + Element.__init__(self, "DOCUMENT_ROOT") + + class DocumentFragment(Element): + def __init__(self): + Element.__init__(self, "DOCUMENT_FRAGMENT") + + def testSerializer(element): + rv = [] + + def serializeElement(element, indent=0): + if not(hasattr(element, "tag")): + element = element.getroot() + if element.tag == "<!DOCTYPE>": + if element.get("publicId") or element.get("systemId"): + publicId = element.get("publicId") or "" + systemId = element.get("systemId") or "" + rv.append("""<!DOCTYPE %s "%s" "%s">""" % + (element.text, publicId, systemId)) + else: + rv.append("<!DOCTYPE %s>" % (element.text,)) + elif element.tag == "DOCUMENT_ROOT": + rv.append("#document") + if element.text is not None: + rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text)) + if element.tail is not None: + raise TypeError("Document node cannot have tail") + if hasattr(element, "attrib") and len(element.attrib): + raise TypeError("Document node cannot have attributes") + elif element.tag == ElementTreeCommentType: + rv.append("|%s<!-- %s -->" % (' ' * indent, element.text)) + else: + assert isinstance(element.tag, text_type), \ + "Expected unicode, got %s, %s" % (type(element.tag), element.tag) + nsmatch = tag_regexp.match(element.tag) + + if nsmatch is None: + name = element.tag + else: + ns, name = nsmatch.groups() + prefix = constants.prefixes[ns] + name = "%s %s" % (prefix, name) + rv.append("|%s<%s>" % (' ' * indent, name)) + + if hasattr(element, "attrib"): + attributes = [] + for name, value in element.attrib.items(): + nsmatch = tag_regexp.match(name) + if nsmatch is not None: + ns, name = nsmatch.groups() + prefix = constants.prefixes[ns] + attr_string = "%s %s" % (prefix, name) + else: + attr_string = name + attributes.append((attr_string, value)) + + for name, value in sorted(attributes): + rv.append('|%s%s="%s"' % (' ' * (indent + 2), name, value)) + if element.text: + rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text)) + indent += 2 + for child in element: + serializeElement(child, indent) + if element.tail: + rv.append("|%s\"%s\"" % (' ' * (indent - 2), element.tail)) + serializeElement(element, 0) + + return "\n".join(rv) + + def tostring(element): # pylint:disable=unused-variable + """Serialize an element and its child nodes to a string""" + rv = [] + filter = _ihatexml.InfosetFilter() + + def serializeElement(element): + if isinstance(element, ElementTree.ElementTree): + element = element.getroot() + + if element.tag == "<!DOCTYPE>": + if element.get("publicId") or element.get("systemId"): + publicId = element.get("publicId") or "" + systemId = element.get("systemId") or "" + rv.append("""<!DOCTYPE %s PUBLIC "%s" "%s">""" % + (element.text, publicId, systemId)) + else: + rv.append("<!DOCTYPE %s>" % (element.text,)) + elif element.tag == "DOCUMENT_ROOT": + if element.text is not None: + rv.append(element.text) + if element.tail is not None: + raise TypeError("Document node cannot have tail") + if hasattr(element, "attrib") and len(element.attrib): + raise TypeError("Document node cannot have attributes") + + for child in element: + serializeElement(child) + + elif element.tag == ElementTreeCommentType: + rv.append("<!--%s-->" % (element.text,)) + else: + # This is assumed to be an ordinary element + if not element.attrib: + rv.append("<%s>" % (filter.fromXmlName(element.tag),)) + else: + attr = " ".join(["%s=\"%s\"" % ( + filter.fromXmlName(name), value) + for name, value in element.attrib.items()]) + rv.append("<%s %s>" % (element.tag, attr)) + if element.text: + rv.append(element.text) + + for child in element: + serializeElement(child) + + rv.append("</%s>" % (element.tag,)) + + if element.tail: + rv.append(element.tail) + + serializeElement(element) + + return "".join(rv) + + class TreeBuilder(base.TreeBuilder): # pylint:disable=unused-variable + documentClass = Document + doctypeClass = DocumentType + elementClass = Element + commentClass = Comment + fragmentClass = DocumentFragment + implementation = ElementTreeImplementation + + def testSerializer(self, element): + return testSerializer(element) + + def getDocument(self): + if fullTree: + return self.document._element + else: + if self.defaultNamespace is not None: + return self.document._element.find( + "{%s}html" % self.defaultNamespace) + else: + return self.document._element.find("html") + + def getFragment(self): + return base.TreeBuilder.getFragment(self)._element + + return locals() + + +getETreeModule = moduleFactoryFactory(getETreeBuilder) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py new file mode 100644 index 0000000..ca12a99 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py @@ -0,0 +1,366 @@ +"""Module for supporting the lxml.etree library. The idea here is to use as much +of the native library as possible, without using fragile hacks like custom element +names that break between releases. The downside of this is that we cannot represent +all possible trees; specifically the following are known to cause problems: + +Text or comments as siblings of the root element +Docypes with no name + +When any of these things occur, we emit a DataLossWarning +""" + +from __future__ import absolute_import, division, unicode_literals +# pylint:disable=protected-access + +import warnings +import re +import sys + +from . import base +from ..constants import DataLossWarning +from .. import constants +from . import etree as etree_builders +from .. import _ihatexml + +import lxml.etree as etree + + +fullTree = True +tag_regexp = re.compile("{([^}]*)}(.*)") + +comment_type = etree.Comment("asd").tag + + +class DocumentType(object): + def __init__(self, name, publicId, systemId): + self.name = name + self.publicId = publicId + self.systemId = systemId + + +class Document(object): + def __init__(self): + self._elementTree = None + self._childNodes = [] + + def appendChild(self, element): + self._elementTree.getroot().addnext(element._element) + + def _getChildNodes(self): + return self._childNodes + + childNodes = property(_getChildNodes) + + +def testSerializer(element): + rv = [] + infosetFilter = _ihatexml.InfosetFilter(preventDoubleDashComments=True) + + def serializeElement(element, indent=0): + if not hasattr(element, "tag"): + if hasattr(element, "getroot"): + # Full tree case + rv.append("#document") + if element.docinfo.internalDTD: + if not (element.docinfo.public_id or + element.docinfo.system_url): + dtd_str = "<!DOCTYPE %s>" % element.docinfo.root_name + else: + dtd_str = """<!DOCTYPE %s "%s" "%s">""" % ( + element.docinfo.root_name, + element.docinfo.public_id, + element.docinfo.system_url) + rv.append("|%s%s" % (' ' * (indent + 2), dtd_str)) + next_element = element.getroot() + while next_element.getprevious() is not None: + next_element = next_element.getprevious() + while next_element is not None: + serializeElement(next_element, indent + 2) + next_element = next_element.getnext() + elif isinstance(element, str) or isinstance(element, bytes): + # Text in a fragment + assert isinstance(element, str) or sys.version_info[0] == 2 + rv.append("|%s\"%s\"" % (' ' * indent, element)) + else: + # Fragment case + rv.append("#document-fragment") + for next_element in element: + serializeElement(next_element, indent + 2) + elif element.tag == comment_type: + rv.append("|%s<!-- %s -->" % (' ' * indent, element.text)) + if hasattr(element, "tail") and element.tail: + rv.append("|%s\"%s\"" % (' ' * indent, element.tail)) + else: + assert isinstance(element, etree._Element) + nsmatch = etree_builders.tag_regexp.match(element.tag) + if nsmatch is not None: + ns = nsmatch.group(1) + tag = nsmatch.group(2) + prefix = constants.prefixes[ns] + rv.append("|%s<%s %s>" % (' ' * indent, prefix, + infosetFilter.fromXmlName(tag))) + else: + rv.append("|%s<%s>" % (' ' * indent, + infosetFilter.fromXmlName(element.tag))) + + if hasattr(element, "attrib"): + attributes = [] + for name, value in element.attrib.items(): + nsmatch = tag_regexp.match(name) + if nsmatch is not None: + ns, name = nsmatch.groups() + name = infosetFilter.fromXmlName(name) + prefix = constants.prefixes[ns] + attr_string = "%s %s" % (prefix, name) + else: + attr_string = infosetFilter.fromXmlName(name) + attributes.append((attr_string, value)) + + for name, value in sorted(attributes): + rv.append('|%s%s="%s"' % (' ' * (indent + 2), name, value)) + + if element.text: + rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text)) + indent += 2 + for child in element: + serializeElement(child, indent) + if hasattr(element, "tail") and element.tail: + rv.append("|%s\"%s\"" % (' ' * (indent - 2), element.tail)) + serializeElement(element, 0) + + return "\n".join(rv) + + +def tostring(element): + """Serialize an element and its child nodes to a string""" + rv = [] + + def serializeElement(element): + if not hasattr(element, "tag"): + if element.docinfo.internalDTD: + if element.docinfo.doctype: + dtd_str = element.docinfo.doctype + else: + dtd_str = "<!DOCTYPE %s>" % element.docinfo.root_name + rv.append(dtd_str) + serializeElement(element.getroot()) + + elif element.tag == comment_type: + rv.append("<!--%s-->" % (element.text,)) + + else: + # This is assumed to be an ordinary element + if not element.attrib: + rv.append("<%s>" % (element.tag,)) + else: + attr = " ".join(["%s=\"%s\"" % (name, value) + for name, value in element.attrib.items()]) + rv.append("<%s %s>" % (element.tag, attr)) + if element.text: + rv.append(element.text) + + for child in element: + serializeElement(child) + + rv.append("</%s>" % (element.tag,)) + + if hasattr(element, "tail") and element.tail: + rv.append(element.tail) + + serializeElement(element) + + return "".join(rv) + + +class TreeBuilder(base.TreeBuilder): + documentClass = Document + doctypeClass = DocumentType + elementClass = None + commentClass = None + fragmentClass = Document + implementation = etree + + def __init__(self, namespaceHTMLElements, fullTree=False): + builder = etree_builders.getETreeModule(etree, fullTree=fullTree) + infosetFilter = self.infosetFilter = _ihatexml.InfosetFilter(preventDoubleDashComments=True) + self.namespaceHTMLElements = namespaceHTMLElements + + class Attributes(dict): + def __init__(self, element, value=None): + if value is None: + value = {} + self._element = element + dict.__init__(self, value) # pylint:disable=non-parent-init-called + for key, value in self.items(): + if isinstance(key, tuple): + name = "{%s}%s" % (key[2], infosetFilter.coerceAttribute(key[1])) + else: + name = infosetFilter.coerceAttribute(key) + self._element._element.attrib[name] = value + + def __setitem__(self, key, value): + dict.__setitem__(self, key, value) + if isinstance(key, tuple): + name = "{%s}%s" % (key[2], infosetFilter.coerceAttribute(key[1])) + else: + name = infosetFilter.coerceAttribute(key) + self._element._element.attrib[name] = value + + class Element(builder.Element): + def __init__(self, name, namespace): + name = infosetFilter.coerceElement(name) + builder.Element.__init__(self, name, namespace=namespace) + self._attributes = Attributes(self) + + def _setName(self, name): + self._name = infosetFilter.coerceElement(name) + self._element.tag = self._getETreeTag( + self._name, self._namespace) + + def _getName(self): + return infosetFilter.fromXmlName(self._name) + + name = property(_getName, _setName) + + def _getAttributes(self): + return self._attributes + + def _setAttributes(self, attributes): + self._attributes = Attributes(self, attributes) + + attributes = property(_getAttributes, _setAttributes) + + def insertText(self, data, insertBefore=None): + data = infosetFilter.coerceCharacters(data) + builder.Element.insertText(self, data, insertBefore) + + def appendChild(self, child): + builder.Element.appendChild(self, child) + + class Comment(builder.Comment): + def __init__(self, data): + data = infosetFilter.coerceComment(data) + builder.Comment.__init__(self, data) + + def _setData(self, data): + data = infosetFilter.coerceComment(data) + self._element.text = data + + def _getData(self): + return self._element.text + + data = property(_getData, _setData) + + self.elementClass = Element + self.commentClass = Comment + # self.fragmentClass = builder.DocumentFragment + base.TreeBuilder.__init__(self, namespaceHTMLElements) + + def reset(self): + base.TreeBuilder.reset(self) + self.insertComment = self.insertCommentInitial + self.initial_comments = [] + self.doctype = None + + def testSerializer(self, element): + return testSerializer(element) + + def getDocument(self): + if fullTree: + return self.document._elementTree + else: + return self.document._elementTree.getroot() + + def getFragment(self): + fragment = [] + element = self.openElements[0]._element + if element.text: + fragment.append(element.text) + fragment.extend(list(element)) + if element.tail: + fragment.append(element.tail) + return fragment + + def insertDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + + if not name: + warnings.warn("lxml cannot represent empty doctype", DataLossWarning) + self.doctype = None + else: + coercedName = self.infosetFilter.coerceElement(name) + if coercedName != name: + warnings.warn("lxml cannot represent non-xml doctype", DataLossWarning) + + doctype = self.doctypeClass(coercedName, publicId, systemId) + self.doctype = doctype + + def insertCommentInitial(self, data, parent=None): + assert parent is None or parent is self.document + assert self.document._elementTree is None + self.initial_comments.append(data) + + def insertCommentMain(self, data, parent=None): + if (parent == self.document and + self.document._elementTree.getroot()[-1].tag == comment_type): + warnings.warn("lxml cannot represent adjacent comments beyond the root elements", DataLossWarning) + super(TreeBuilder, self).insertComment(data, parent) + + def insertRoot(self, token): + # Because of the way libxml2 works, it doesn't seem to be possible to + # alter information like the doctype after the tree has been parsed. + # Therefore we need to use the built-in parser to create our initial + # tree, after which we can add elements like normal + docStr = "" + if self.doctype: + assert self.doctype.name + docStr += "<!DOCTYPE %s" % self.doctype.name + if (self.doctype.publicId is not None or + self.doctype.systemId is not None): + docStr += (' PUBLIC "%s" ' % + (self.infosetFilter.coercePubid(self.doctype.publicId or ""))) + if self.doctype.systemId: + sysid = self.doctype.systemId + if sysid.find("'") >= 0 and sysid.find('"') >= 0: + warnings.warn("DOCTYPE system cannot contain single and double quotes", DataLossWarning) + sysid = sysid.replace("'", 'U00027') + if sysid.find("'") >= 0: + docStr += '"%s"' % sysid + else: + docStr += "'%s'" % sysid + else: + docStr += "''" + docStr += ">" + if self.doctype.name != token["name"]: + warnings.warn("lxml cannot represent doctype with a different name to the root element", DataLossWarning) + docStr += "<THIS_SHOULD_NEVER_APPEAR_PUBLICLY/>" + root = etree.fromstring(docStr) + + # Append the initial comments: + for comment_token in self.initial_comments: + comment = self.commentClass(comment_token["data"]) + root.addprevious(comment._element) + + # Create the root document and add the ElementTree to it + self.document = self.documentClass() + self.document._elementTree = root.getroottree() + + # Give the root element the right name + name = token["name"] + namespace = token.get("namespace", self.defaultNamespace) + if namespace is None: + etree_tag = name + else: + etree_tag = "{%s}%s" % (namespace, name) + root.tag = etree_tag + + # Add the root element to the internal child/open data structures + root_element = self.elementClass(name, namespace) + root_element._element = root + self.document._childNodes.append(root_element) + self.openElements.append(root_element) + + # Reset to the default insert comment function + self.insertComment = self.insertCommentMain diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py new file mode 100644 index 0000000..9bec207 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py @@ -0,0 +1,154 @@ +"""A collection of modules for iterating through different kinds of +tree, generating tokens identical to those produced by the tokenizer +module. + +To create a tree walker for a new type of tree, you need to do +implement a tree walker object (called TreeWalker by convention) that +implements a 'serialize' method taking a tree as sole argument and +returning an iterator generating tokens. +""" + +from __future__ import absolute_import, division, unicode_literals + +from .. import constants +from .._utils import default_etree + +__all__ = ["getTreeWalker", "pprint"] + +treeWalkerCache = {} + + +def getTreeWalker(treeType, implementation=None, **kwargs): + """Get a TreeWalker class for various types of tree with built-in support + + :arg str treeType: the name of the tree type required (case-insensitive). + Supported values are: + + * "dom": The xml.dom.minidom DOM implementation + * "etree": A generic walker for tree implementations exposing an + elementtree-like interface (known to work with ElementTree, + cElementTree and lxml.etree). + * "lxml": Optimized walker for lxml.etree + * "genshi": a Genshi stream + + :arg implementation: A module implementing the tree type e.g. + xml.etree.ElementTree or cElementTree (Currently applies to the "etree" + tree type only). + + :arg kwargs: keyword arguments passed to the etree walker--for other + walkers, this has no effect + + :returns: a TreeWalker class + + """ + + treeType = treeType.lower() + if treeType not in treeWalkerCache: + if treeType == "dom": + from . import dom + treeWalkerCache[treeType] = dom.TreeWalker + elif treeType == "genshi": + from . import genshi + treeWalkerCache[treeType] = genshi.TreeWalker + elif treeType == "lxml": + from . import etree_lxml + treeWalkerCache[treeType] = etree_lxml.TreeWalker + elif treeType == "etree": + from . import etree + if implementation is None: + implementation = default_etree + # XXX: NEVER cache here, caching is done in the etree submodule + return etree.getETreeModule(implementation, **kwargs).TreeWalker + return treeWalkerCache.get(treeType) + + +def concatenateCharacterTokens(tokens): + pendingCharacters = [] + for token in tokens: + type = token["type"] + if type in ("Characters", "SpaceCharacters"): + pendingCharacters.append(token["data"]) + else: + if pendingCharacters: + yield {"type": "Characters", "data": "".join(pendingCharacters)} + pendingCharacters = [] + yield token + if pendingCharacters: + yield {"type": "Characters", "data": "".join(pendingCharacters)} + + +def pprint(walker): + """Pretty printer for tree walkers + + Takes a TreeWalker instance and pretty prints the output of walking the tree. + + :arg walker: a TreeWalker instance + + """ + output = [] + indent = 0 + for token in concatenateCharacterTokens(walker): + type = token["type"] + if type in ("StartTag", "EmptyTag"): + # tag name + if token["namespace"] and token["namespace"] != constants.namespaces["html"]: + if token["namespace"] in constants.prefixes: + ns = constants.prefixes[token["namespace"]] + else: + ns = token["namespace"] + name = "%s %s" % (ns, token["name"]) + else: + name = token["name"] + output.append("%s<%s>" % (" " * indent, name)) + indent += 2 + # attributes (sorted for consistent ordering) + attrs = token["data"] + for (namespace, localname), value in sorted(attrs.items()): + if namespace: + if namespace in constants.prefixes: + ns = constants.prefixes[namespace] + else: + ns = namespace + name = "%s %s" % (ns, localname) + else: + name = localname + output.append("%s%s=\"%s\"" % (" " * indent, name, value)) + # self-closing + if type == "EmptyTag": + indent -= 2 + + elif type == "EndTag": + indent -= 2 + + elif type == "Comment": + output.append("%s<!-- %s -->" % (" " * indent, token["data"])) + + elif type == "Doctype": + if token["name"]: + if token["publicId"]: + output.append("""%s<!DOCTYPE %s "%s" "%s">""" % + (" " * indent, + token["name"], + token["publicId"], + token["systemId"] if token["systemId"] else "")) + elif token["systemId"]: + output.append("""%s<!DOCTYPE %s "" "%s">""" % + (" " * indent, + token["name"], + token["systemId"])) + else: + output.append("%s<!DOCTYPE %s>" % (" " * indent, + token["name"])) + else: + output.append("%s<!DOCTYPE >" % (" " * indent,)) + + elif type == "Characters": + output.append("%s\"%s\"" % (" " * indent, token["data"])) + + elif type == "SpaceCharacters": + assert False, "concatenateCharacterTokens should have got rid of all Space tokens" + + else: + raise ValueError("Unknown token type, %s" % type) + + return "\n".join(output) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/base.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/base.py new file mode 100644 index 0000000..80c474c --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/base.py @@ -0,0 +1,252 @@ +from __future__ import absolute_import, division, unicode_literals + +from xml.dom import Node +from ..constants import namespaces, voidElements, spaceCharacters + +__all__ = ["DOCUMENT", "DOCTYPE", "TEXT", "ELEMENT", "COMMENT", "ENTITY", "UNKNOWN", + "TreeWalker", "NonRecursiveTreeWalker"] + +DOCUMENT = Node.DOCUMENT_NODE +DOCTYPE = Node.DOCUMENT_TYPE_NODE +TEXT = Node.TEXT_NODE +ELEMENT = Node.ELEMENT_NODE +COMMENT = Node.COMMENT_NODE +ENTITY = Node.ENTITY_NODE +UNKNOWN = "<#UNKNOWN#>" + +spaceCharacters = "".join(spaceCharacters) + + +class TreeWalker(object): + """Walks a tree yielding tokens + + Tokens are dicts that all have a ``type`` field specifying the type of the + token. + + """ + def __init__(self, tree): + """Creates a TreeWalker + + :arg tree: the tree to walk + + """ + self.tree = tree + + def __iter__(self): + raise NotImplementedError + + def error(self, msg): + """Generates an error token with the given message + + :arg msg: the error message + + :returns: SerializeError token + + """ + return {"type": "SerializeError", "data": msg} + + def emptyTag(self, namespace, name, attrs, hasChildren=False): + """Generates an EmptyTag token + + :arg namespace: the namespace of the token--can be ``None`` + + :arg name: the name of the element + + :arg attrs: the attributes of the element as a dict + + :arg hasChildren: whether or not to yield a SerializationError because + this tag shouldn't have children + + :returns: EmptyTag token + + """ + yield {"type": "EmptyTag", "name": name, + "namespace": namespace, + "data": attrs} + if hasChildren: + yield self.error("Void element has children") + + def startTag(self, namespace, name, attrs): + """Generates a StartTag token + + :arg namespace: the namespace of the token--can be ``None`` + + :arg name: the name of the element + + :arg attrs: the attributes of the element as a dict + + :returns: StartTag token + + """ + return {"type": "StartTag", + "name": name, + "namespace": namespace, + "data": attrs} + + def endTag(self, namespace, name): + """Generates an EndTag token + + :arg namespace: the namespace of the token--can be ``None`` + + :arg name: the name of the element + + :returns: EndTag token + + """ + return {"type": "EndTag", + "name": name, + "namespace": namespace} + + def text(self, data): + """Generates SpaceCharacters and Characters tokens + + Depending on what's in the data, this generates one or more + ``SpaceCharacters`` and ``Characters`` tokens. + + For example: + + >>> from html5lib.treewalkers.base import TreeWalker + >>> # Give it an empty tree just so it instantiates + >>> walker = TreeWalker([]) + >>> list(walker.text('')) + [] + >>> list(walker.text(' ')) + [{u'data': ' ', u'type': u'SpaceCharacters'}] + >>> list(walker.text(' abc ')) # doctest: +NORMALIZE_WHITESPACE + [{u'data': ' ', u'type': u'SpaceCharacters'}, + {u'data': u'abc', u'type': u'Characters'}, + {u'data': u' ', u'type': u'SpaceCharacters'}] + + :arg data: the text data + + :returns: one or more ``SpaceCharacters`` and ``Characters`` tokens + + """ + data = data + middle = data.lstrip(spaceCharacters) + left = data[:len(data) - len(middle)] + if left: + yield {"type": "SpaceCharacters", "data": left} + data = middle + middle = data.rstrip(spaceCharacters) + right = data[len(middle):] + if middle: + yield {"type": "Characters", "data": middle} + if right: + yield {"type": "SpaceCharacters", "data": right} + + def comment(self, data): + """Generates a Comment token + + :arg data: the comment + + :returns: Comment token + + """ + return {"type": "Comment", "data": data} + + def doctype(self, name, publicId=None, systemId=None): + """Generates a Doctype token + + :arg name: + + :arg publicId: + + :arg systemId: + + :returns: the Doctype token + + """ + return {"type": "Doctype", + "name": name, + "publicId": publicId, + "systemId": systemId} + + def entity(self, name): + """Generates an Entity token + + :arg name: the entity name + + :returns: an Entity token + + """ + return {"type": "Entity", "name": name} + + def unknown(self, nodeType): + """Handles unknown node types""" + return self.error("Unknown node type: " + nodeType) + + +class NonRecursiveTreeWalker(TreeWalker): + def getNodeDetails(self, node): + raise NotImplementedError + + def getFirstChild(self, node): + raise NotImplementedError + + def getNextSibling(self, node): + raise NotImplementedError + + def getParentNode(self, node): + raise NotImplementedError + + def __iter__(self): + currentNode = self.tree + while currentNode is not None: + details = self.getNodeDetails(currentNode) + type, details = details[0], details[1:] + hasChildren = False + + if type == DOCTYPE: + yield self.doctype(*details) + + elif type == TEXT: + for token in self.text(*details): + yield token + + elif type == ELEMENT: + namespace, name, attributes, hasChildren = details + if (not namespace or namespace == namespaces["html"]) and name in voidElements: + for token in self.emptyTag(namespace, name, attributes, + hasChildren): + yield token + hasChildren = False + else: + yield self.startTag(namespace, name, attributes) + + elif type == COMMENT: + yield self.comment(details[0]) + + elif type == ENTITY: + yield self.entity(details[0]) + + elif type == DOCUMENT: + hasChildren = True + + else: + yield self.unknown(details[0]) + + if hasChildren: + firstChild = self.getFirstChild(currentNode) + else: + firstChild = None + + if firstChild is not None: + currentNode = firstChild + else: + while currentNode is not None: + details = self.getNodeDetails(currentNode) + type, details = details[0], details[1:] + if type == ELEMENT: + namespace, name, attributes, hasChildren = details + if (namespace and namespace != namespaces["html"]) or name not in voidElements: + yield self.endTag(namespace, name) + if self.tree is currentNode: + currentNode = None + break + nextSibling = self.getNextSibling(currentNode) + if nextSibling is not None: + currentNode = nextSibling + break + else: + currentNode = self.getParentNode(currentNode) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/dom.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/dom.py new file mode 100644 index 0000000..b0c89b0 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/dom.py @@ -0,0 +1,43 @@ +from __future__ import absolute_import, division, unicode_literals + +from xml.dom import Node + +from . import base + + +class TreeWalker(base.NonRecursiveTreeWalker): + def getNodeDetails(self, node): + if node.nodeType == Node.DOCUMENT_TYPE_NODE: + return base.DOCTYPE, node.name, node.publicId, node.systemId + + elif node.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE): + return base.TEXT, node.nodeValue + + elif node.nodeType == Node.ELEMENT_NODE: + attrs = {} + for attr in list(node.attributes.keys()): + attr = node.getAttributeNode(attr) + if attr.namespaceURI: + attrs[(attr.namespaceURI, attr.localName)] = attr.value + else: + attrs[(None, attr.name)] = attr.value + return (base.ELEMENT, node.namespaceURI, node.nodeName, + attrs, node.hasChildNodes()) + + elif node.nodeType == Node.COMMENT_NODE: + return base.COMMENT, node.nodeValue + + elif node.nodeType in (Node.DOCUMENT_NODE, Node.DOCUMENT_FRAGMENT_NODE): + return (base.DOCUMENT,) + + else: + return base.UNKNOWN, node.nodeType + + def getFirstChild(self, node): + return node.firstChild + + def getNextSibling(self, node): + return node.nextSibling + + def getParentNode(self, node): + return node.parentNode diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree.py new file mode 100644 index 0000000..95fc0c1 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree.py @@ -0,0 +1,130 @@ +from __future__ import absolute_import, division, unicode_literals + +from collections import OrderedDict +import re + +from pip._vendor.six import string_types + +from . import base +from .._utils import moduleFactoryFactory + +tag_regexp = re.compile("{([^}]*)}(.*)") + + +def getETreeBuilder(ElementTreeImplementation): + ElementTree = ElementTreeImplementation + ElementTreeCommentType = ElementTree.Comment("asd").tag + + class TreeWalker(base.NonRecursiveTreeWalker): # pylint:disable=unused-variable + """Given the particular ElementTree representation, this implementation, + to avoid using recursion, returns "nodes" as tuples with the following + content: + + 1. The current element + + 2. The index of the element relative to its parent + + 3. A stack of ancestor elements + + 4. A flag "text", "tail" or None to indicate if the current node is a + text node; either the text or tail of the current element (1) + """ + def getNodeDetails(self, node): + if isinstance(node, tuple): # It might be the root Element + elt, _, _, flag = node + if flag in ("text", "tail"): + return base.TEXT, getattr(elt, flag) + else: + node = elt + + if not(hasattr(node, "tag")): + node = node.getroot() + + if node.tag in ("DOCUMENT_ROOT", "DOCUMENT_FRAGMENT"): + return (base.DOCUMENT,) + + elif node.tag == "<!DOCTYPE>": + return (base.DOCTYPE, node.text, + node.get("publicId"), node.get("systemId")) + + elif node.tag == ElementTreeCommentType: + return base.COMMENT, node.text + + else: + assert isinstance(node.tag, string_types), type(node.tag) + # This is assumed to be an ordinary element + match = tag_regexp.match(node.tag) + if match: + namespace, tag = match.groups() + else: + namespace = None + tag = node.tag + attrs = OrderedDict() + for name, value in list(node.attrib.items()): + match = tag_regexp.match(name) + if match: + attrs[(match.group(1), match.group(2))] = value + else: + attrs[(None, name)] = value + return (base.ELEMENT, namespace, tag, + attrs, len(node) or node.text) + + def getFirstChild(self, node): + if isinstance(node, tuple): + element, key, parents, flag = node + else: + element, key, parents, flag = node, None, [], None + + if flag in ("text", "tail"): + return None + else: + if element.text: + return element, key, parents, "text" + elif len(element): + parents.append(element) + return element[0], 0, parents, None + else: + return None + + def getNextSibling(self, node): + if isinstance(node, tuple): + element, key, parents, flag = node + else: + return None + + if flag == "text": + if len(element): + parents.append(element) + return element[0], 0, parents, None + else: + return None + else: + if element.tail and flag != "tail": + return element, key, parents, "tail" + elif key < len(parents[-1]) - 1: + return parents[-1][key + 1], key + 1, parents, None + else: + return None + + def getParentNode(self, node): + if isinstance(node, tuple): + element, key, parents, flag = node + else: + return None + + if flag == "text": + if not parents: + return element + else: + return element, key, parents, None + else: + parent = parents.pop() + if not parents: + return parent + else: + assert list(parents[-1]).count(parent) == 1 + return parent, list(parents[-1]).index(parent), parents, None + + return locals() + +getETreeModule = moduleFactoryFactory(getETreeBuilder) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py new file mode 100644 index 0000000..e81ddf3 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py @@ -0,0 +1,213 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +from lxml import etree +from ..treebuilders.etree import tag_regexp + +from . import base + +from .. import _ihatexml + + +def ensure_str(s): + if s is None: + return None + elif isinstance(s, text_type): + return s + else: + return s.decode("ascii", "strict") + + +class Root(object): + def __init__(self, et): + self.elementtree = et + self.children = [] + + try: + if et.docinfo.internalDTD: + self.children.append(Doctype(self, + ensure_str(et.docinfo.root_name), + ensure_str(et.docinfo.public_id), + ensure_str(et.docinfo.system_url))) + except AttributeError: + pass + + try: + node = et.getroot() + except AttributeError: + node = et + + while node.getprevious() is not None: + node = node.getprevious() + while node is not None: + self.children.append(node) + node = node.getnext() + + self.text = None + self.tail = None + + def __getitem__(self, key): + return self.children[key] + + def getnext(self): + return None + + def __len__(self): + return 1 + + +class Doctype(object): + def __init__(self, root_node, name, public_id, system_id): + self.root_node = root_node + self.name = name + self.public_id = public_id + self.system_id = system_id + + self.text = None + self.tail = None + + def getnext(self): + return self.root_node.children[1] + + +class FragmentRoot(Root): + def __init__(self, children): + self.children = [FragmentWrapper(self, child) for child in children] + self.text = self.tail = None + + def getnext(self): + return None + + +class FragmentWrapper(object): + def __init__(self, fragment_root, obj): + self.root_node = fragment_root + self.obj = obj + if hasattr(self.obj, 'text'): + self.text = ensure_str(self.obj.text) + else: + self.text = None + if hasattr(self.obj, 'tail'): + self.tail = ensure_str(self.obj.tail) + else: + self.tail = None + + def __getattr__(self, name): + return getattr(self.obj, name) + + def getnext(self): + siblings = self.root_node.children + idx = siblings.index(self) + if idx < len(siblings) - 1: + return siblings[idx + 1] + else: + return None + + def __getitem__(self, key): + return self.obj[key] + + def __bool__(self): + return bool(self.obj) + + def getparent(self): + return None + + def __str__(self): + return str(self.obj) + + def __unicode__(self): + return str(self.obj) + + def __len__(self): + return len(self.obj) + + +class TreeWalker(base.NonRecursiveTreeWalker): + def __init__(self, tree): + # pylint:disable=redefined-variable-type + if isinstance(tree, list): + self.fragmentChildren = set(tree) + tree = FragmentRoot(tree) + else: + self.fragmentChildren = set() + tree = Root(tree) + base.NonRecursiveTreeWalker.__init__(self, tree) + self.filter = _ihatexml.InfosetFilter() + + def getNodeDetails(self, node): + if isinstance(node, tuple): # Text node + node, key = node + assert key in ("text", "tail"), "Text nodes are text or tail, found %s" % key + return base.TEXT, ensure_str(getattr(node, key)) + + elif isinstance(node, Root): + return (base.DOCUMENT,) + + elif isinstance(node, Doctype): + return base.DOCTYPE, node.name, node.public_id, node.system_id + + elif isinstance(node, FragmentWrapper) and not hasattr(node, "tag"): + return base.TEXT, ensure_str(node.obj) + + elif node.tag == etree.Comment: + return base.COMMENT, ensure_str(node.text) + + elif node.tag == etree.Entity: + return base.ENTITY, ensure_str(node.text)[1:-1] # strip &; + + else: + # This is assumed to be an ordinary element + match = tag_regexp.match(ensure_str(node.tag)) + if match: + namespace, tag = match.groups() + else: + namespace = None + tag = ensure_str(node.tag) + attrs = {} + for name, value in list(node.attrib.items()): + name = ensure_str(name) + value = ensure_str(value) + match = tag_regexp.match(name) + if match: + attrs[(match.group(1), match.group(2))] = value + else: + attrs[(None, name)] = value + return (base.ELEMENT, namespace, self.filter.fromXmlName(tag), + attrs, len(node) > 0 or node.text) + + def getFirstChild(self, node): + assert not isinstance(node, tuple), "Text nodes have no children" + + assert len(node) or node.text, "Node has no children" + if node.text: + return (node, "text") + else: + return node[0] + + def getNextSibling(self, node): + if isinstance(node, tuple): # Text node + node, key = node + assert key in ("text", "tail"), "Text nodes are text or tail, found %s" % key + if key == "text": + # XXX: we cannot use a "bool(node) and node[0] or None" construct here + # because node[0] might evaluate to False if it has no child element + if len(node): + return node[0] + else: + return None + else: # tail + return node.getnext() + + return (node, "tail") if node.tail else node.getnext() + + def getParentNode(self, node): + if isinstance(node, tuple): # Text node + node, key = node + assert key in ("text", "tail"), "Text nodes are text or tail, found %s" % key + if key == "text": + return node + # else: fallback to "normal" processing + elif node in self.fragmentChildren: + return None + + return node.getparent() diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py new file mode 100644 index 0000000..7483be2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py @@ -0,0 +1,69 @@ +from __future__ import absolute_import, division, unicode_literals + +from genshi.core import QName +from genshi.core import START, END, XML_NAMESPACE, DOCTYPE, TEXT +from genshi.core import START_NS, END_NS, START_CDATA, END_CDATA, PI, COMMENT + +from . import base + +from ..constants import voidElements, namespaces + + +class TreeWalker(base.TreeWalker): + def __iter__(self): + # Buffer the events so we can pass in the following one + previous = None + for event in self.tree: + if previous is not None: + for token in self.tokens(previous, event): + yield token + previous = event + + # Don't forget the final event! + if previous is not None: + for token in self.tokens(previous, None): + yield token + + def tokens(self, event, next): + kind, data, _ = event + if kind == START: + tag, attribs = data + name = tag.localname + namespace = tag.namespace + converted_attribs = {} + for k, v in attribs: + if isinstance(k, QName): + converted_attribs[(k.namespace, k.localname)] = v + else: + converted_attribs[(None, k)] = v + + if namespace == namespaces["html"] and name in voidElements: + for token in self.emptyTag(namespace, name, converted_attribs, + not next or next[0] != END or + next[1] != tag): + yield token + else: + yield self.startTag(namespace, name, converted_attribs) + + elif kind == END: + name = data.localname + namespace = data.namespace + if namespace != namespaces["html"] or name not in voidElements: + yield self.endTag(namespace, name) + + elif kind == COMMENT: + yield self.comment(data) + + elif kind == TEXT: + for token in self.text(data): + yield token + + elif kind == DOCTYPE: + yield self.doctype(*data) + + elif kind in (XML_NAMESPACE, DOCTYPE, START_NS, END_NS, + START_CDATA, END_CDATA, PI): + pass + + else: + yield self.unknown(kind) diff --git a/venv/Lib/site-packages/pip/_vendor/idna/__init__.py b/venv/Lib/site-packages/pip/_vendor/idna/__init__.py new file mode 100644 index 0000000..847bf93 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/idna/__init__.py @@ -0,0 +1,2 @@ +from .package_data import __version__ +from .core import * diff --git a/venv/Lib/site-packages/pip/_vendor/idna/codec.py b/venv/Lib/site-packages/pip/_vendor/idna/codec.py new file mode 100644 index 0000000..98c65ea --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/idna/codec.py @@ -0,0 +1,118 @@ +from .core import encode, decode, alabel, ulabel, IDNAError +import codecs +import re + +_unicode_dots_re = re.compile(u'[\u002e\u3002\uff0e\uff61]') + +class Codec(codecs.Codec): + + def encode(self, data, errors='strict'): + + if errors != 'strict': + raise IDNAError("Unsupported error handling \"{0}\"".format(errors)) + + if not data: + return "", 0 + + return encode(data), len(data) + + def decode(self, data, errors='strict'): + + if errors != 'strict': + raise IDNAError("Unsupported error handling \"{0}\"".format(errors)) + + if not data: + return u"", 0 + + return decode(data), len(data) + +class IncrementalEncoder(codecs.BufferedIncrementalEncoder): + def _buffer_encode(self, data, errors, final): + if errors != 'strict': + raise IDNAError("Unsupported error handling \"{0}\"".format(errors)) + + if not data: + return ("", 0) + + labels = _unicode_dots_re.split(data) + trailing_dot = u'' + if labels: + if not labels[-1]: + trailing_dot = '.' + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = '.' + + result = [] + size = 0 + for label in labels: + result.append(alabel(label)) + if size: + size += 1 + size += len(label) + + # Join with U+002E + result = ".".join(result) + trailing_dot + size += len(trailing_dot) + return (result, size) + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, data, errors, final): + if errors != 'strict': + raise IDNAError("Unsupported error handling \"{0}\"".format(errors)) + + if not data: + return (u"", 0) + + # IDNA allows decoding to operate on Unicode strings, too. + if isinstance(data, unicode): + labels = _unicode_dots_re.split(data) + else: + # Must be ASCII string + data = str(data) + unicode(data, "ascii") + labels = data.split(".") + + trailing_dot = u'' + if labels: + if not labels[-1]: + trailing_dot = u'.' + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = u'.' + + result = [] + size = 0 + for label in labels: + result.append(ulabel(label)) + if size: + size += 1 + size += len(label) + + result = u".".join(result) + trailing_dot + size += len(trailing_dot) + return (result, size) + + +class StreamWriter(Codec, codecs.StreamWriter): + pass + +class StreamReader(Codec, codecs.StreamReader): + pass + +def getregentry(): + return codecs.CodecInfo( + name='idna', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/venv/Lib/site-packages/pip/_vendor/idna/compat.py b/venv/Lib/site-packages/pip/_vendor/idna/compat.py new file mode 100644 index 0000000..4d47f33 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/idna/compat.py @@ -0,0 +1,12 @@ +from .core import * +from .codec import * + +def ToASCII(label): + return encode(label) + +def ToUnicode(label): + return decode(label) + +def nameprep(s): + raise NotImplementedError("IDNA 2008 does not utilise nameprep protocol") + diff --git a/venv/Lib/site-packages/pip/_vendor/idna/core.py b/venv/Lib/site-packages/pip/_vendor/idna/core.py new file mode 100644 index 0000000..090c2c1 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/idna/core.py @@ -0,0 +1,399 @@ +from . import idnadata +import bisect +import unicodedata +import re +import sys +from .intranges import intranges_contain + +_virama_combining_class = 9 +_alabel_prefix = b'xn--' +_unicode_dots_re = re.compile(u'[\u002e\u3002\uff0e\uff61]') + +if sys.version_info[0] == 3: + unicode = str + unichr = chr + +class IDNAError(UnicodeError): + """ Base exception for all IDNA-encoding related problems """ + pass + + +class IDNABidiError(IDNAError): + """ Exception when bidirectional requirements are not satisfied """ + pass + + +class InvalidCodepoint(IDNAError): + """ Exception when a disallowed or unallocated codepoint is used """ + pass + + +class InvalidCodepointContext(IDNAError): + """ Exception when the codepoint is not valid in the context it is used """ + pass + + +def _combining_class(cp): + v = unicodedata.combining(unichr(cp)) + if v == 0: + if not unicodedata.name(unichr(cp)): + raise ValueError("Unknown character in unicodedata") + return v + +def _is_script(cp, script): + return intranges_contain(ord(cp), idnadata.scripts[script]) + +def _punycode(s): + return s.encode('punycode') + +def _unot(s): + return 'U+{0:04X}'.format(s) + + +def valid_label_length(label): + + if len(label) > 63: + return False + return True + + +def valid_string_length(label, trailing_dot): + + if len(label) > (254 if trailing_dot else 253): + return False + return True + + +def check_bidi(label, check_ltr=False): + + # Bidi rules should only be applied if string contains RTL characters + bidi_label = False + for (idx, cp) in enumerate(label, 1): + direction = unicodedata.bidirectional(cp) + if direction == '': + # String likely comes from a newer version of Unicode + raise IDNABidiError('Unknown directionality in label {0} at position {1}'.format(repr(label), idx)) + if direction in ['R', 'AL', 'AN']: + bidi_label = True + if not bidi_label and not check_ltr: + return True + + # Bidi rule 1 + direction = unicodedata.bidirectional(label[0]) + if direction in ['R', 'AL']: + rtl = True + elif direction == 'L': + rtl = False + else: + raise IDNABidiError('First codepoint in label {0} must be directionality L, R or AL'.format(repr(label))) + + valid_ending = False + number_type = False + for (idx, cp) in enumerate(label, 1): + direction = unicodedata.bidirectional(cp) + + if rtl: + # Bidi rule 2 + if not direction in ['R', 'AL', 'AN', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']: + raise IDNABidiError('Invalid direction for codepoint at position {0} in a right-to-left label'.format(idx)) + # Bidi rule 3 + if direction in ['R', 'AL', 'EN', 'AN']: + valid_ending = True + elif direction != 'NSM': + valid_ending = False + # Bidi rule 4 + if direction in ['AN', 'EN']: + if not number_type: + number_type = direction + else: + if number_type != direction: + raise IDNABidiError('Can not mix numeral types in a right-to-left label') + else: + # Bidi rule 5 + if not direction in ['L', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']: + raise IDNABidiError('Invalid direction for codepoint at position {0} in a left-to-right label'.format(idx)) + # Bidi rule 6 + if direction in ['L', 'EN']: + valid_ending = True + elif direction != 'NSM': + valid_ending = False + + if not valid_ending: + raise IDNABidiError('Label ends with illegal codepoint directionality') + + return True + + +def check_initial_combiner(label): + + if unicodedata.category(label[0])[0] == 'M': + raise IDNAError('Label begins with an illegal combining character') + return True + + +def check_hyphen_ok(label): + + if label[2:4] == '--': + raise IDNAError('Label has disallowed hyphens in 3rd and 4th position') + if label[0] == '-' or label[-1] == '-': + raise IDNAError('Label must not start or end with a hyphen') + return True + + +def check_nfc(label): + + if unicodedata.normalize('NFC', label) != label: + raise IDNAError('Label must be in Normalization Form C') + + +def valid_contextj(label, pos): + + cp_value = ord(label[pos]) + + if cp_value == 0x200c: + + if pos > 0: + if _combining_class(ord(label[pos - 1])) == _virama_combining_class: + return True + + ok = False + for i in range(pos-1, -1, -1): + joining_type = idnadata.joining_types.get(ord(label[i])) + if joining_type == ord('T'): + continue + if joining_type in [ord('L'), ord('D')]: + ok = True + break + + if not ok: + return False + + ok = False + for i in range(pos+1, len(label)): + joining_type = idnadata.joining_types.get(ord(label[i])) + if joining_type == ord('T'): + continue + if joining_type in [ord('R'), ord('D')]: + ok = True + break + return ok + + if cp_value == 0x200d: + + if pos > 0: + if _combining_class(ord(label[pos - 1])) == _virama_combining_class: + return True + return False + + else: + + return False + + +def valid_contexto(label, pos, exception=False): + + cp_value = ord(label[pos]) + + if cp_value == 0x00b7: + if 0 < pos < len(label)-1: + if ord(label[pos - 1]) == 0x006c and ord(label[pos + 1]) == 0x006c: + return True + return False + + elif cp_value == 0x0375: + if pos < len(label)-1 and len(label) > 1: + return _is_script(label[pos + 1], 'Greek') + return False + + elif cp_value == 0x05f3 or cp_value == 0x05f4: + if pos > 0: + return _is_script(label[pos - 1], 'Hebrew') + return False + + elif cp_value == 0x30fb: + for cp in label: + if cp == u'\u30fb': + continue + if _is_script(cp, 'Hiragana') or _is_script(cp, 'Katakana') or _is_script(cp, 'Han'): + return True + return False + + elif 0x660 <= cp_value <= 0x669: + for cp in label: + if 0x6f0 <= ord(cp) <= 0x06f9: + return False + return True + + elif 0x6f0 <= cp_value <= 0x6f9: + for cp in label: + if 0x660 <= ord(cp) <= 0x0669: + return False + return True + + +def check_label(label): + + if isinstance(label, (bytes, bytearray)): + label = label.decode('utf-8') + if len(label) == 0: + raise IDNAError('Empty Label') + + check_nfc(label) + check_hyphen_ok(label) + check_initial_combiner(label) + + for (pos, cp) in enumerate(label): + cp_value = ord(cp) + if intranges_contain(cp_value, idnadata.codepoint_classes['PVALID']): + continue + elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTJ']): + try: + if not valid_contextj(label, pos): + raise InvalidCodepointContext('Joiner {0} not allowed at position {1} in {2}'.format( + _unot(cp_value), pos+1, repr(label))) + except ValueError: + raise IDNAError('Unknown codepoint adjacent to joiner {0} at position {1} in {2}'.format( + _unot(cp_value), pos+1, repr(label))) + elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTO']): + if not valid_contexto(label, pos): + raise InvalidCodepointContext('Codepoint {0} not allowed at position {1} in {2}'.format(_unot(cp_value), pos+1, repr(label))) + else: + raise InvalidCodepoint('Codepoint {0} at position {1} of {2} not allowed'.format(_unot(cp_value), pos+1, repr(label))) + + check_bidi(label) + + +def alabel(label): + + try: + label = label.encode('ascii') + try: + ulabel(label) + except IDNAError: + raise IDNAError('The label {0} is not a valid A-label'.format(label)) + if not valid_label_length(label): + raise IDNAError('Label too long') + return label + except UnicodeEncodeError: + pass + + if not label: + raise IDNAError('No Input') + + label = unicode(label) + check_label(label) + label = _punycode(label) + label = _alabel_prefix + label + + if not valid_label_length(label): + raise IDNAError('Label too long') + + return label + + +def ulabel(label): + + if not isinstance(label, (bytes, bytearray)): + try: + label = label.encode('ascii') + except UnicodeEncodeError: + check_label(label) + return label + + label = label.lower() + if label.startswith(_alabel_prefix): + label = label[len(_alabel_prefix):] + else: + check_label(label) + return label.decode('ascii') + + label = label.decode('punycode') + check_label(label) + return label + + +def uts46_remap(domain, std3_rules=True, transitional=False): + """Re-map the characters in the string according to UTS46 processing.""" + from .uts46data import uts46data + output = u"" + try: + for pos, char in enumerate(domain): + code_point = ord(char) + uts46row = uts46data[code_point if code_point < 256 else + bisect.bisect_left(uts46data, (code_point, "Z")) - 1] + status = uts46row[1] + replacement = uts46row[2] if len(uts46row) == 3 else None + if (status == "V" or + (status == "D" and not transitional) or + (status == "3" and not std3_rules and replacement is None)): + output += char + elif replacement is not None and (status == "M" or + (status == "3" and not std3_rules) or + (status == "D" and transitional)): + output += replacement + elif status != "I": + raise IndexError() + return unicodedata.normalize("NFC", output) + except IndexError: + raise InvalidCodepoint( + "Codepoint {0} not allowed at position {1} in {2}".format( + _unot(code_point), pos + 1, repr(domain))) + + +def encode(s, strict=False, uts46=False, std3_rules=False, transitional=False): + + if isinstance(s, (bytes, bytearray)): + s = s.decode("ascii") + if uts46: + s = uts46_remap(s, std3_rules, transitional) + trailing_dot = False + result = [] + if strict: + labels = s.split('.') + else: + labels = _unicode_dots_re.split(s) + if not labels or labels == ['']: + raise IDNAError('Empty domain') + if labels[-1] == '': + del labels[-1] + trailing_dot = True + for label in labels: + s = alabel(label) + if s: + result.append(s) + else: + raise IDNAError('Empty label') + if trailing_dot: + result.append(b'') + s = b'.'.join(result) + if not valid_string_length(s, trailing_dot): + raise IDNAError('Domain too long') + return s + + +def decode(s, strict=False, uts46=False, std3_rules=False): + + if isinstance(s, (bytes, bytearray)): + s = s.decode("ascii") + if uts46: + s = uts46_remap(s, std3_rules, False) + trailing_dot = False + result = [] + if not strict: + labels = _unicode_dots_re.split(s) + else: + labels = s.split(u'.') + if not labels or labels == ['']: + raise IDNAError('Empty domain') + if not labels[-1]: + del labels[-1] + trailing_dot = True + for label in labels: + s = ulabel(label) + if s: + result.append(s) + else: + raise IDNAError('Empty label') + if trailing_dot: + result.append(u'') + return u'.'.join(result) diff --git a/venv/Lib/site-packages/pip/_vendor/idna/idnadata.py b/venv/Lib/site-packages/pip/_vendor/idna/idnadata.py new file mode 100644 index 0000000..17974e2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/idna/idnadata.py @@ -0,0 +1,1893 @@ +# This file is automatically generated by tools/idna-data + +__version__ = "10.0.0" +scripts = { + 'Greek': ( + 0x37000000374, + 0x37500000378, + 0x37a0000037e, + 0x37f00000380, + 0x38400000385, + 0x38600000387, + 0x3880000038b, + 0x38c0000038d, + 0x38e000003a2, + 0x3a3000003e2, + 0x3f000000400, + 0x1d2600001d2b, + 0x1d5d00001d62, + 0x1d6600001d6b, + 0x1dbf00001dc0, + 0x1f0000001f16, + 0x1f1800001f1e, + 0x1f2000001f46, + 0x1f4800001f4e, + 0x1f5000001f58, + 0x1f5900001f5a, + 0x1f5b00001f5c, + 0x1f5d00001f5e, + 0x1f5f00001f7e, + 0x1f8000001fb5, + 0x1fb600001fc5, + 0x1fc600001fd4, + 0x1fd600001fdc, + 0x1fdd00001ff0, + 0x1ff200001ff5, + 0x1ff600001fff, + 0x212600002127, + 0xab650000ab66, + 0x101400001018f, + 0x101a0000101a1, + 0x1d2000001d246, + ), + 'Han': ( + 0x2e8000002e9a, + 0x2e9b00002ef4, + 0x2f0000002fd6, + 0x300500003006, + 0x300700003008, + 0x30210000302a, + 0x30380000303c, + 0x340000004db6, + 0x4e0000009feb, + 0xf9000000fa6e, + 0xfa700000fada, + 0x200000002a6d7, + 0x2a7000002b735, + 0x2b7400002b81e, + 0x2b8200002cea2, + 0x2ceb00002ebe1, + 0x2f8000002fa1e, + ), + 'Hebrew': ( + 0x591000005c8, + 0x5d0000005eb, + 0x5f0000005f5, + 0xfb1d0000fb37, + 0xfb380000fb3d, + 0xfb3e0000fb3f, + 0xfb400000fb42, + 0xfb430000fb45, + 0xfb460000fb50, + ), + 'Hiragana': ( + 0x304100003097, + 0x309d000030a0, + 0x1b0010001b11f, + 0x1f2000001f201, + ), + 'Katakana': ( + 0x30a1000030fb, + 0x30fd00003100, + 0x31f000003200, + 0x32d0000032ff, + 0x330000003358, + 0xff660000ff70, + 0xff710000ff9e, + 0x1b0000001b001, + ), +} +joining_types = { + 0x600: 85, + 0x601: 85, + 0x602: 85, + 0x603: 85, + 0x604: 85, + 0x605: 85, + 0x608: 85, + 0x60b: 85, + 0x620: 68, + 0x621: 85, + 0x622: 82, + 0x623: 82, + 0x624: 82, + 0x625: 82, + 0x626: 68, + 0x627: 82, + 0x628: 68, + 0x629: 82, + 0x62a: 68, + 0x62b: 68, + 0x62c: 68, + 0x62d: 68, + 0x62e: 68, + 0x62f: 82, + 0x630: 82, + 0x631: 82, + 0x632: 82, + 0x633: 68, + 0x634: 68, + 0x635: 68, + 0x636: 68, + 0x637: 68, + 0x638: 68, + 0x639: 68, + 0x63a: 68, + 0x63b: 68, + 0x63c: 68, + 0x63d: 68, + 0x63e: 68, + 0x63f: 68, + 0x640: 67, + 0x641: 68, + 0x642: 68, + 0x643: 68, + 0x644: 68, + 0x645: 68, + 0x646: 68, + 0x647: 68, + 0x648: 82, + 0x649: 68, + 0x64a: 68, + 0x66e: 68, + 0x66f: 68, + 0x671: 82, + 0x672: 82, + 0x673: 82, + 0x674: 85, + 0x675: 82, + 0x676: 82, + 0x677: 82, + 0x678: 68, + 0x679: 68, + 0x67a: 68, + 0x67b: 68, + 0x67c: 68, + 0x67d: 68, + 0x67e: 68, + 0x67f: 68, + 0x680: 68, + 0x681: 68, + 0x682: 68, + 0x683: 68, + 0x684: 68, + 0x685: 68, + 0x686: 68, + 0x687: 68, + 0x688: 82, + 0x689: 82, + 0x68a: 82, + 0x68b: 82, + 0x68c: 82, + 0x68d: 82, + 0x68e: 82, + 0x68f: 82, + 0x690: 82, + 0x691: 82, + 0x692: 82, + 0x693: 82, + 0x694: 82, + 0x695: 82, + 0x696: 82, + 0x697: 82, + 0x698: 82, + 0x699: 82, + 0x69a: 68, + 0x69b: 68, + 0x69c: 68, + 0x69d: 68, + 0x69e: 68, + 0x69f: 68, + 0x6a0: 68, + 0x6a1: 68, + 0x6a2: 68, + 0x6a3: 68, + 0x6a4: 68, + 0x6a5: 68, + 0x6a6: 68, + 0x6a7: 68, + 0x6a8: 68, + 0x6a9: 68, + 0x6aa: 68, + 0x6ab: 68, + 0x6ac: 68, + 0x6ad: 68, + 0x6ae: 68, + 0x6af: 68, + 0x6b0: 68, + 0x6b1: 68, + 0x6b2: 68, + 0x6b3: 68, + 0x6b4: 68, + 0x6b5: 68, + 0x6b6: 68, + 0x6b7: 68, + 0x6b8: 68, + 0x6b9: 68, + 0x6ba: 68, + 0x6bb: 68, + 0x6bc: 68, + 0x6bd: 68, + 0x6be: 68, + 0x6bf: 68, + 0x6c0: 82, + 0x6c1: 68, + 0x6c2: 68, + 0x6c3: 82, + 0x6c4: 82, + 0x6c5: 82, + 0x6c6: 82, + 0x6c7: 82, + 0x6c8: 82, + 0x6c9: 82, + 0x6ca: 82, + 0x6cb: 82, + 0x6cc: 68, + 0x6cd: 82, + 0x6ce: 68, + 0x6cf: 82, + 0x6d0: 68, + 0x6d1: 68, + 0x6d2: 82, + 0x6d3: 82, + 0x6d5: 82, + 0x6dd: 85, + 0x6ee: 82, + 0x6ef: 82, + 0x6fa: 68, + 0x6fb: 68, + 0x6fc: 68, + 0x6ff: 68, + 0x710: 82, + 0x712: 68, + 0x713: 68, + 0x714: 68, + 0x715: 82, + 0x716: 82, + 0x717: 82, + 0x718: 82, + 0x719: 82, + 0x71a: 68, + 0x71b: 68, + 0x71c: 68, + 0x71d: 68, + 0x71e: 82, + 0x71f: 68, + 0x720: 68, + 0x721: 68, + 0x722: 68, + 0x723: 68, + 0x724: 68, + 0x725: 68, + 0x726: 68, + 0x727: 68, + 0x728: 82, + 0x729: 68, + 0x72a: 82, + 0x72b: 68, + 0x72c: 82, + 0x72d: 68, + 0x72e: 68, + 0x72f: 82, + 0x74d: 82, + 0x74e: 68, + 0x74f: 68, + 0x750: 68, + 0x751: 68, + 0x752: 68, + 0x753: 68, + 0x754: 68, + 0x755: 68, + 0x756: 68, + 0x757: 68, + 0x758: 68, + 0x759: 82, + 0x75a: 82, + 0x75b: 82, + 0x75c: 68, + 0x75d: 68, + 0x75e: 68, + 0x75f: 68, + 0x760: 68, + 0x761: 68, + 0x762: 68, + 0x763: 68, + 0x764: 68, + 0x765: 68, + 0x766: 68, + 0x767: 68, + 0x768: 68, + 0x769: 68, + 0x76a: 68, + 0x76b: 82, + 0x76c: 82, + 0x76d: 68, + 0x76e: 68, + 0x76f: 68, + 0x770: 68, + 0x771: 82, + 0x772: 68, + 0x773: 82, + 0x774: 82, + 0x775: 68, + 0x776: 68, + 0x777: 68, + 0x778: 82, + 0x779: 82, + 0x77a: 68, + 0x77b: 68, + 0x77c: 68, + 0x77d: 68, + 0x77e: 68, + 0x77f: 68, + 0x7ca: 68, + 0x7cb: 68, + 0x7cc: 68, + 0x7cd: 68, + 0x7ce: 68, + 0x7cf: 68, + 0x7d0: 68, + 0x7d1: 68, + 0x7d2: 68, + 0x7d3: 68, + 0x7d4: 68, + 0x7d5: 68, + 0x7d6: 68, + 0x7d7: 68, + 0x7d8: 68, + 0x7d9: 68, + 0x7da: 68, + 0x7db: 68, + 0x7dc: 68, + 0x7dd: 68, + 0x7de: 68, + 0x7df: 68, + 0x7e0: 68, + 0x7e1: 68, + 0x7e2: 68, + 0x7e3: 68, + 0x7e4: 68, + 0x7e5: 68, + 0x7e6: 68, + 0x7e7: 68, + 0x7e8: 68, + 0x7e9: 68, + 0x7ea: 68, + 0x7fa: 67, + 0x840: 82, + 0x841: 68, + 0x842: 68, + 0x843: 68, + 0x844: 68, + 0x845: 68, + 0x846: 82, + 0x847: 82, + 0x848: 68, + 0x849: 82, + 0x84a: 68, + 0x84b: 68, + 0x84c: 68, + 0x84d: 68, + 0x84e: 68, + 0x84f: 68, + 0x850: 68, + 0x851: 68, + 0x852: 68, + 0x853: 68, + 0x854: 82, + 0x855: 68, + 0x856: 85, + 0x857: 85, + 0x858: 85, + 0x860: 68, + 0x861: 85, + 0x862: 68, + 0x863: 68, + 0x864: 68, + 0x865: 68, + 0x866: 85, + 0x867: 82, + 0x868: 68, + 0x869: 82, + 0x86a: 82, + 0x8a0: 68, + 0x8a1: 68, + 0x8a2: 68, + 0x8a3: 68, + 0x8a4: 68, + 0x8a5: 68, + 0x8a6: 68, + 0x8a7: 68, + 0x8a8: 68, + 0x8a9: 68, + 0x8aa: 82, + 0x8ab: 82, + 0x8ac: 82, + 0x8ad: 85, + 0x8ae: 82, + 0x8af: 68, + 0x8b0: 68, + 0x8b1: 82, + 0x8b2: 82, + 0x8b3: 68, + 0x8b4: 68, + 0x8b6: 68, + 0x8b7: 68, + 0x8b8: 68, + 0x8b9: 82, + 0x8ba: 68, + 0x8bb: 68, + 0x8bc: 68, + 0x8bd: 68, + 0x8e2: 85, + 0x1806: 85, + 0x1807: 68, + 0x180a: 67, + 0x180e: 85, + 0x1820: 68, + 0x1821: 68, + 0x1822: 68, + 0x1823: 68, + 0x1824: 68, + 0x1825: 68, + 0x1826: 68, + 0x1827: 68, + 0x1828: 68, + 0x1829: 68, + 0x182a: 68, + 0x182b: 68, + 0x182c: 68, + 0x182d: 68, + 0x182e: 68, + 0x182f: 68, + 0x1830: 68, + 0x1831: 68, + 0x1832: 68, + 0x1833: 68, + 0x1834: 68, + 0x1835: 68, + 0x1836: 68, + 0x1837: 68, + 0x1838: 68, + 0x1839: 68, + 0x183a: 68, + 0x183b: 68, + 0x183c: 68, + 0x183d: 68, + 0x183e: 68, + 0x183f: 68, + 0x1840: 68, + 0x1841: 68, + 0x1842: 68, + 0x1843: 68, + 0x1844: 68, + 0x1845: 68, + 0x1846: 68, + 0x1847: 68, + 0x1848: 68, + 0x1849: 68, + 0x184a: 68, + 0x184b: 68, + 0x184c: 68, + 0x184d: 68, + 0x184e: 68, + 0x184f: 68, + 0x1850: 68, + 0x1851: 68, + 0x1852: 68, + 0x1853: 68, + 0x1854: 68, + 0x1855: 68, + 0x1856: 68, + 0x1857: 68, + 0x1858: 68, + 0x1859: 68, + 0x185a: 68, + 0x185b: 68, + 0x185c: 68, + 0x185d: 68, + 0x185e: 68, + 0x185f: 68, + 0x1860: 68, + 0x1861: 68, + 0x1862: 68, + 0x1863: 68, + 0x1864: 68, + 0x1865: 68, + 0x1866: 68, + 0x1867: 68, + 0x1868: 68, + 0x1869: 68, + 0x186a: 68, + 0x186b: 68, + 0x186c: 68, + 0x186d: 68, + 0x186e: 68, + 0x186f: 68, + 0x1870: 68, + 0x1871: 68, + 0x1872: 68, + 0x1873: 68, + 0x1874: 68, + 0x1875: 68, + 0x1876: 68, + 0x1877: 68, + 0x1880: 85, + 0x1881: 85, + 0x1882: 85, + 0x1883: 85, + 0x1884: 85, + 0x1885: 84, + 0x1886: 84, + 0x1887: 68, + 0x1888: 68, + 0x1889: 68, + 0x188a: 68, + 0x188b: 68, + 0x188c: 68, + 0x188d: 68, + 0x188e: 68, + 0x188f: 68, + 0x1890: 68, + 0x1891: 68, + 0x1892: 68, + 0x1893: 68, + 0x1894: 68, + 0x1895: 68, + 0x1896: 68, + 0x1897: 68, + 0x1898: 68, + 0x1899: 68, + 0x189a: 68, + 0x189b: 68, + 0x189c: 68, + 0x189d: 68, + 0x189e: 68, + 0x189f: 68, + 0x18a0: 68, + 0x18a1: 68, + 0x18a2: 68, + 0x18a3: 68, + 0x18a4: 68, + 0x18a5: 68, + 0x18a6: 68, + 0x18a7: 68, + 0x18a8: 68, + 0x18aa: 68, + 0x200c: 85, + 0x200d: 67, + 0x202f: 85, + 0x2066: 85, + 0x2067: 85, + 0x2068: 85, + 0x2069: 85, + 0xa840: 68, + 0xa841: 68, + 0xa842: 68, + 0xa843: 68, + 0xa844: 68, + 0xa845: 68, + 0xa846: 68, + 0xa847: 68, + 0xa848: 68, + 0xa849: 68, + 0xa84a: 68, + 0xa84b: 68, + 0xa84c: 68, + 0xa84d: 68, + 0xa84e: 68, + 0xa84f: 68, + 0xa850: 68, + 0xa851: 68, + 0xa852: 68, + 0xa853: 68, + 0xa854: 68, + 0xa855: 68, + 0xa856: 68, + 0xa857: 68, + 0xa858: 68, + 0xa859: 68, + 0xa85a: 68, + 0xa85b: 68, + 0xa85c: 68, + 0xa85d: 68, + 0xa85e: 68, + 0xa85f: 68, + 0xa860: 68, + 0xa861: 68, + 0xa862: 68, + 0xa863: 68, + 0xa864: 68, + 0xa865: 68, + 0xa866: 68, + 0xa867: 68, + 0xa868: 68, + 0xa869: 68, + 0xa86a: 68, + 0xa86b: 68, + 0xa86c: 68, + 0xa86d: 68, + 0xa86e: 68, + 0xa86f: 68, + 0xa870: 68, + 0xa871: 68, + 0xa872: 76, + 0xa873: 85, + 0x10ac0: 68, + 0x10ac1: 68, + 0x10ac2: 68, + 0x10ac3: 68, + 0x10ac4: 68, + 0x10ac5: 82, + 0x10ac6: 85, + 0x10ac7: 82, + 0x10ac8: 85, + 0x10ac9: 82, + 0x10aca: 82, + 0x10acb: 85, + 0x10acc: 85, + 0x10acd: 76, + 0x10ace: 82, + 0x10acf: 82, + 0x10ad0: 82, + 0x10ad1: 82, + 0x10ad2: 82, + 0x10ad3: 68, + 0x10ad4: 68, + 0x10ad5: 68, + 0x10ad6: 68, + 0x10ad7: 76, + 0x10ad8: 68, + 0x10ad9: 68, + 0x10ada: 68, + 0x10adb: 68, + 0x10adc: 68, + 0x10add: 82, + 0x10ade: 68, + 0x10adf: 68, + 0x10ae0: 68, + 0x10ae1: 82, + 0x10ae2: 85, + 0x10ae3: 85, + 0x10ae4: 82, + 0x10aeb: 68, + 0x10aec: 68, + 0x10aed: 68, + 0x10aee: 68, + 0x10aef: 82, + 0x10b80: 68, + 0x10b81: 82, + 0x10b82: 68, + 0x10b83: 82, + 0x10b84: 82, + 0x10b85: 82, + 0x10b86: 68, + 0x10b87: 68, + 0x10b88: 68, + 0x10b89: 82, + 0x10b8a: 68, + 0x10b8b: 68, + 0x10b8c: 82, + 0x10b8d: 68, + 0x10b8e: 82, + 0x10b8f: 82, + 0x10b90: 68, + 0x10b91: 82, + 0x10ba9: 82, + 0x10baa: 82, + 0x10bab: 82, + 0x10bac: 82, + 0x10bad: 68, + 0x10bae: 68, + 0x10baf: 85, + 0x1e900: 68, + 0x1e901: 68, + 0x1e902: 68, + 0x1e903: 68, + 0x1e904: 68, + 0x1e905: 68, + 0x1e906: 68, + 0x1e907: 68, + 0x1e908: 68, + 0x1e909: 68, + 0x1e90a: 68, + 0x1e90b: 68, + 0x1e90c: 68, + 0x1e90d: 68, + 0x1e90e: 68, + 0x1e90f: 68, + 0x1e910: 68, + 0x1e911: 68, + 0x1e912: 68, + 0x1e913: 68, + 0x1e914: 68, + 0x1e915: 68, + 0x1e916: 68, + 0x1e917: 68, + 0x1e918: 68, + 0x1e919: 68, + 0x1e91a: 68, + 0x1e91b: 68, + 0x1e91c: 68, + 0x1e91d: 68, + 0x1e91e: 68, + 0x1e91f: 68, + 0x1e920: 68, + 0x1e921: 68, + 0x1e922: 68, + 0x1e923: 68, + 0x1e924: 68, + 0x1e925: 68, + 0x1e926: 68, + 0x1e927: 68, + 0x1e928: 68, + 0x1e929: 68, + 0x1e92a: 68, + 0x1e92b: 68, + 0x1e92c: 68, + 0x1e92d: 68, + 0x1e92e: 68, + 0x1e92f: 68, + 0x1e930: 68, + 0x1e931: 68, + 0x1e932: 68, + 0x1e933: 68, + 0x1e934: 68, + 0x1e935: 68, + 0x1e936: 68, + 0x1e937: 68, + 0x1e938: 68, + 0x1e939: 68, + 0x1e93a: 68, + 0x1e93b: 68, + 0x1e93c: 68, + 0x1e93d: 68, + 0x1e93e: 68, + 0x1e93f: 68, + 0x1e940: 68, + 0x1e941: 68, + 0x1e942: 68, + 0x1e943: 68, +} +codepoint_classes = { + 'PVALID': ( + 0x2d0000002e, + 0x300000003a, + 0x610000007b, + 0xdf000000f7, + 0xf800000100, + 0x10100000102, + 0x10300000104, + 0x10500000106, + 0x10700000108, + 0x1090000010a, + 0x10b0000010c, + 0x10d0000010e, + 0x10f00000110, + 0x11100000112, + 0x11300000114, + 0x11500000116, + 0x11700000118, + 0x1190000011a, + 0x11b0000011c, + 0x11d0000011e, + 0x11f00000120, + 0x12100000122, + 0x12300000124, + 0x12500000126, + 0x12700000128, + 0x1290000012a, + 0x12b0000012c, + 0x12d0000012e, + 0x12f00000130, + 0x13100000132, + 0x13500000136, + 0x13700000139, + 0x13a0000013b, + 0x13c0000013d, + 0x13e0000013f, + 0x14200000143, + 0x14400000145, + 0x14600000147, + 0x14800000149, + 0x14b0000014c, + 0x14d0000014e, + 0x14f00000150, + 0x15100000152, + 0x15300000154, + 0x15500000156, + 0x15700000158, + 0x1590000015a, + 0x15b0000015c, + 0x15d0000015e, + 0x15f00000160, + 0x16100000162, + 0x16300000164, + 0x16500000166, + 0x16700000168, + 0x1690000016a, + 0x16b0000016c, + 0x16d0000016e, + 0x16f00000170, + 0x17100000172, + 0x17300000174, + 0x17500000176, + 0x17700000178, + 0x17a0000017b, + 0x17c0000017d, + 0x17e0000017f, + 0x18000000181, + 0x18300000184, + 0x18500000186, + 0x18800000189, + 0x18c0000018e, + 0x19200000193, + 0x19500000196, + 0x1990000019c, + 0x19e0000019f, + 0x1a1000001a2, + 0x1a3000001a4, + 0x1a5000001a6, + 0x1a8000001a9, + 0x1aa000001ac, + 0x1ad000001ae, + 0x1b0000001b1, + 0x1b4000001b5, + 0x1b6000001b7, + 0x1b9000001bc, + 0x1bd000001c4, + 0x1ce000001cf, + 0x1d0000001d1, + 0x1d2000001d3, + 0x1d4000001d5, + 0x1d6000001d7, + 0x1d8000001d9, + 0x1da000001db, + 0x1dc000001de, + 0x1df000001e0, + 0x1e1000001e2, + 0x1e3000001e4, + 0x1e5000001e6, + 0x1e7000001e8, + 0x1e9000001ea, + 0x1eb000001ec, + 0x1ed000001ee, + 0x1ef000001f1, + 0x1f5000001f6, + 0x1f9000001fa, + 0x1fb000001fc, + 0x1fd000001fe, + 0x1ff00000200, + 0x20100000202, + 0x20300000204, + 0x20500000206, + 0x20700000208, + 0x2090000020a, + 0x20b0000020c, + 0x20d0000020e, + 0x20f00000210, + 0x21100000212, + 0x21300000214, + 0x21500000216, + 0x21700000218, + 0x2190000021a, + 0x21b0000021c, + 0x21d0000021e, + 0x21f00000220, + 0x22100000222, + 0x22300000224, + 0x22500000226, + 0x22700000228, + 0x2290000022a, + 0x22b0000022c, + 0x22d0000022e, + 0x22f00000230, + 0x23100000232, + 0x2330000023a, + 0x23c0000023d, + 0x23f00000241, + 0x24200000243, + 0x24700000248, + 0x2490000024a, + 0x24b0000024c, + 0x24d0000024e, + 0x24f000002b0, + 0x2b9000002c2, + 0x2c6000002d2, + 0x2ec000002ed, + 0x2ee000002ef, + 0x30000000340, + 0x34200000343, + 0x3460000034f, + 0x35000000370, + 0x37100000372, + 0x37300000374, + 0x37700000378, + 0x37b0000037e, + 0x39000000391, + 0x3ac000003cf, + 0x3d7000003d8, + 0x3d9000003da, + 0x3db000003dc, + 0x3dd000003de, + 0x3df000003e0, + 0x3e1000003e2, + 0x3e3000003e4, + 0x3e5000003e6, + 0x3e7000003e8, + 0x3e9000003ea, + 0x3eb000003ec, + 0x3ed000003ee, + 0x3ef000003f0, + 0x3f3000003f4, + 0x3f8000003f9, + 0x3fb000003fd, + 0x43000000460, + 0x46100000462, + 0x46300000464, + 0x46500000466, + 0x46700000468, + 0x4690000046a, + 0x46b0000046c, + 0x46d0000046e, + 0x46f00000470, + 0x47100000472, + 0x47300000474, + 0x47500000476, + 0x47700000478, + 0x4790000047a, + 0x47b0000047c, + 0x47d0000047e, + 0x47f00000480, + 0x48100000482, + 0x48300000488, + 0x48b0000048c, + 0x48d0000048e, + 0x48f00000490, + 0x49100000492, + 0x49300000494, + 0x49500000496, + 0x49700000498, + 0x4990000049a, + 0x49b0000049c, + 0x49d0000049e, + 0x49f000004a0, + 0x4a1000004a2, + 0x4a3000004a4, + 0x4a5000004a6, + 0x4a7000004a8, + 0x4a9000004aa, + 0x4ab000004ac, + 0x4ad000004ae, + 0x4af000004b0, + 0x4b1000004b2, + 0x4b3000004b4, + 0x4b5000004b6, + 0x4b7000004b8, + 0x4b9000004ba, + 0x4bb000004bc, + 0x4bd000004be, + 0x4bf000004c0, + 0x4c2000004c3, + 0x4c4000004c5, + 0x4c6000004c7, + 0x4c8000004c9, + 0x4ca000004cb, + 0x4cc000004cd, + 0x4ce000004d0, + 0x4d1000004d2, + 0x4d3000004d4, + 0x4d5000004d6, + 0x4d7000004d8, + 0x4d9000004da, + 0x4db000004dc, + 0x4dd000004de, + 0x4df000004e0, + 0x4e1000004e2, + 0x4e3000004e4, + 0x4e5000004e6, + 0x4e7000004e8, + 0x4e9000004ea, + 0x4eb000004ec, + 0x4ed000004ee, + 0x4ef000004f0, + 0x4f1000004f2, + 0x4f3000004f4, + 0x4f5000004f6, + 0x4f7000004f8, + 0x4f9000004fa, + 0x4fb000004fc, + 0x4fd000004fe, + 0x4ff00000500, + 0x50100000502, + 0x50300000504, + 0x50500000506, + 0x50700000508, + 0x5090000050a, + 0x50b0000050c, + 0x50d0000050e, + 0x50f00000510, + 0x51100000512, + 0x51300000514, + 0x51500000516, + 0x51700000518, + 0x5190000051a, + 0x51b0000051c, + 0x51d0000051e, + 0x51f00000520, + 0x52100000522, + 0x52300000524, + 0x52500000526, + 0x52700000528, + 0x5290000052a, + 0x52b0000052c, + 0x52d0000052e, + 0x52f00000530, + 0x5590000055a, + 0x56100000587, + 0x591000005be, + 0x5bf000005c0, + 0x5c1000005c3, + 0x5c4000005c6, + 0x5c7000005c8, + 0x5d0000005eb, + 0x5f0000005f3, + 0x6100000061b, + 0x62000000640, + 0x64100000660, + 0x66e00000675, + 0x679000006d4, + 0x6d5000006dd, + 0x6df000006e9, + 0x6ea000006f0, + 0x6fa00000700, + 0x7100000074b, + 0x74d000007b2, + 0x7c0000007f6, + 0x8000000082e, + 0x8400000085c, + 0x8600000086b, + 0x8a0000008b5, + 0x8b6000008be, + 0x8d4000008e2, + 0x8e300000958, + 0x96000000964, + 0x96600000970, + 0x97100000984, + 0x9850000098d, + 0x98f00000991, + 0x993000009a9, + 0x9aa000009b1, + 0x9b2000009b3, + 0x9b6000009ba, + 0x9bc000009c5, + 0x9c7000009c9, + 0x9cb000009cf, + 0x9d7000009d8, + 0x9e0000009e4, + 0x9e6000009f2, + 0x9fc000009fd, + 0xa0100000a04, + 0xa0500000a0b, + 0xa0f00000a11, + 0xa1300000a29, + 0xa2a00000a31, + 0xa3200000a33, + 0xa3500000a36, + 0xa3800000a3a, + 0xa3c00000a3d, + 0xa3e00000a43, + 0xa4700000a49, + 0xa4b00000a4e, + 0xa5100000a52, + 0xa5c00000a5d, + 0xa6600000a76, + 0xa8100000a84, + 0xa8500000a8e, + 0xa8f00000a92, + 0xa9300000aa9, + 0xaaa00000ab1, + 0xab200000ab4, + 0xab500000aba, + 0xabc00000ac6, + 0xac700000aca, + 0xacb00000ace, + 0xad000000ad1, + 0xae000000ae4, + 0xae600000af0, + 0xaf900000b00, + 0xb0100000b04, + 0xb0500000b0d, + 0xb0f00000b11, + 0xb1300000b29, + 0xb2a00000b31, + 0xb3200000b34, + 0xb3500000b3a, + 0xb3c00000b45, + 0xb4700000b49, + 0xb4b00000b4e, + 0xb5600000b58, + 0xb5f00000b64, + 0xb6600000b70, + 0xb7100000b72, + 0xb8200000b84, + 0xb8500000b8b, + 0xb8e00000b91, + 0xb9200000b96, + 0xb9900000b9b, + 0xb9c00000b9d, + 0xb9e00000ba0, + 0xba300000ba5, + 0xba800000bab, + 0xbae00000bba, + 0xbbe00000bc3, + 0xbc600000bc9, + 0xbca00000bce, + 0xbd000000bd1, + 0xbd700000bd8, + 0xbe600000bf0, + 0xc0000000c04, + 0xc0500000c0d, + 0xc0e00000c11, + 0xc1200000c29, + 0xc2a00000c3a, + 0xc3d00000c45, + 0xc4600000c49, + 0xc4a00000c4e, + 0xc5500000c57, + 0xc5800000c5b, + 0xc6000000c64, + 0xc6600000c70, + 0xc8000000c84, + 0xc8500000c8d, + 0xc8e00000c91, + 0xc9200000ca9, + 0xcaa00000cb4, + 0xcb500000cba, + 0xcbc00000cc5, + 0xcc600000cc9, + 0xcca00000cce, + 0xcd500000cd7, + 0xcde00000cdf, + 0xce000000ce4, + 0xce600000cf0, + 0xcf100000cf3, + 0xd0000000d04, + 0xd0500000d0d, + 0xd0e00000d11, + 0xd1200000d45, + 0xd4600000d49, + 0xd4a00000d4f, + 0xd5400000d58, + 0xd5f00000d64, + 0xd6600000d70, + 0xd7a00000d80, + 0xd8200000d84, + 0xd8500000d97, + 0xd9a00000db2, + 0xdb300000dbc, + 0xdbd00000dbe, + 0xdc000000dc7, + 0xdca00000dcb, + 0xdcf00000dd5, + 0xdd600000dd7, + 0xdd800000de0, + 0xde600000df0, + 0xdf200000df4, + 0xe0100000e33, + 0xe3400000e3b, + 0xe4000000e4f, + 0xe5000000e5a, + 0xe8100000e83, + 0xe8400000e85, + 0xe8700000e89, + 0xe8a00000e8b, + 0xe8d00000e8e, + 0xe9400000e98, + 0xe9900000ea0, + 0xea100000ea4, + 0xea500000ea6, + 0xea700000ea8, + 0xeaa00000eac, + 0xead00000eb3, + 0xeb400000eba, + 0xebb00000ebe, + 0xec000000ec5, + 0xec600000ec7, + 0xec800000ece, + 0xed000000eda, + 0xede00000ee0, + 0xf0000000f01, + 0xf0b00000f0c, + 0xf1800000f1a, + 0xf2000000f2a, + 0xf3500000f36, + 0xf3700000f38, + 0xf3900000f3a, + 0xf3e00000f43, + 0xf4400000f48, + 0xf4900000f4d, + 0xf4e00000f52, + 0xf5300000f57, + 0xf5800000f5c, + 0xf5d00000f69, + 0xf6a00000f6d, + 0xf7100000f73, + 0xf7400000f75, + 0xf7a00000f81, + 0xf8200000f85, + 0xf8600000f93, + 0xf9400000f98, + 0xf9900000f9d, + 0xf9e00000fa2, + 0xfa300000fa7, + 0xfa800000fac, + 0xfad00000fb9, + 0xfba00000fbd, + 0xfc600000fc7, + 0x10000000104a, + 0x10500000109e, + 0x10d0000010fb, + 0x10fd00001100, + 0x120000001249, + 0x124a0000124e, + 0x125000001257, + 0x125800001259, + 0x125a0000125e, + 0x126000001289, + 0x128a0000128e, + 0x1290000012b1, + 0x12b2000012b6, + 0x12b8000012bf, + 0x12c0000012c1, + 0x12c2000012c6, + 0x12c8000012d7, + 0x12d800001311, + 0x131200001316, + 0x13180000135b, + 0x135d00001360, + 0x138000001390, + 0x13a0000013f6, + 0x14010000166d, + 0x166f00001680, + 0x16810000169b, + 0x16a0000016eb, + 0x16f1000016f9, + 0x17000000170d, + 0x170e00001715, + 0x172000001735, + 0x174000001754, + 0x17600000176d, + 0x176e00001771, + 0x177200001774, + 0x1780000017b4, + 0x17b6000017d4, + 0x17d7000017d8, + 0x17dc000017de, + 0x17e0000017ea, + 0x18100000181a, + 0x182000001878, + 0x1880000018ab, + 0x18b0000018f6, + 0x19000000191f, + 0x19200000192c, + 0x19300000193c, + 0x19460000196e, + 0x197000001975, + 0x1980000019ac, + 0x19b0000019ca, + 0x19d0000019da, + 0x1a0000001a1c, + 0x1a2000001a5f, + 0x1a6000001a7d, + 0x1a7f00001a8a, + 0x1a9000001a9a, + 0x1aa700001aa8, + 0x1ab000001abe, + 0x1b0000001b4c, + 0x1b5000001b5a, + 0x1b6b00001b74, + 0x1b8000001bf4, + 0x1c0000001c38, + 0x1c4000001c4a, + 0x1c4d00001c7e, + 0x1cd000001cd3, + 0x1cd400001cfa, + 0x1d0000001d2c, + 0x1d2f00001d30, + 0x1d3b00001d3c, + 0x1d4e00001d4f, + 0x1d6b00001d78, + 0x1d7900001d9b, + 0x1dc000001dfa, + 0x1dfb00001e00, + 0x1e0100001e02, + 0x1e0300001e04, + 0x1e0500001e06, + 0x1e0700001e08, + 0x1e0900001e0a, + 0x1e0b00001e0c, + 0x1e0d00001e0e, + 0x1e0f00001e10, + 0x1e1100001e12, + 0x1e1300001e14, + 0x1e1500001e16, + 0x1e1700001e18, + 0x1e1900001e1a, + 0x1e1b00001e1c, + 0x1e1d00001e1e, + 0x1e1f00001e20, + 0x1e2100001e22, + 0x1e2300001e24, + 0x1e2500001e26, + 0x1e2700001e28, + 0x1e2900001e2a, + 0x1e2b00001e2c, + 0x1e2d00001e2e, + 0x1e2f00001e30, + 0x1e3100001e32, + 0x1e3300001e34, + 0x1e3500001e36, + 0x1e3700001e38, + 0x1e3900001e3a, + 0x1e3b00001e3c, + 0x1e3d00001e3e, + 0x1e3f00001e40, + 0x1e4100001e42, + 0x1e4300001e44, + 0x1e4500001e46, + 0x1e4700001e48, + 0x1e4900001e4a, + 0x1e4b00001e4c, + 0x1e4d00001e4e, + 0x1e4f00001e50, + 0x1e5100001e52, + 0x1e5300001e54, + 0x1e5500001e56, + 0x1e5700001e58, + 0x1e5900001e5a, + 0x1e5b00001e5c, + 0x1e5d00001e5e, + 0x1e5f00001e60, + 0x1e6100001e62, + 0x1e6300001e64, + 0x1e6500001e66, + 0x1e6700001e68, + 0x1e6900001e6a, + 0x1e6b00001e6c, + 0x1e6d00001e6e, + 0x1e6f00001e70, + 0x1e7100001e72, + 0x1e7300001e74, + 0x1e7500001e76, + 0x1e7700001e78, + 0x1e7900001e7a, + 0x1e7b00001e7c, + 0x1e7d00001e7e, + 0x1e7f00001e80, + 0x1e8100001e82, + 0x1e8300001e84, + 0x1e8500001e86, + 0x1e8700001e88, + 0x1e8900001e8a, + 0x1e8b00001e8c, + 0x1e8d00001e8e, + 0x1e8f00001e90, + 0x1e9100001e92, + 0x1e9300001e94, + 0x1e9500001e9a, + 0x1e9c00001e9e, + 0x1e9f00001ea0, + 0x1ea100001ea2, + 0x1ea300001ea4, + 0x1ea500001ea6, + 0x1ea700001ea8, + 0x1ea900001eaa, + 0x1eab00001eac, + 0x1ead00001eae, + 0x1eaf00001eb0, + 0x1eb100001eb2, + 0x1eb300001eb4, + 0x1eb500001eb6, + 0x1eb700001eb8, + 0x1eb900001eba, + 0x1ebb00001ebc, + 0x1ebd00001ebe, + 0x1ebf00001ec0, + 0x1ec100001ec2, + 0x1ec300001ec4, + 0x1ec500001ec6, + 0x1ec700001ec8, + 0x1ec900001eca, + 0x1ecb00001ecc, + 0x1ecd00001ece, + 0x1ecf00001ed0, + 0x1ed100001ed2, + 0x1ed300001ed4, + 0x1ed500001ed6, + 0x1ed700001ed8, + 0x1ed900001eda, + 0x1edb00001edc, + 0x1edd00001ede, + 0x1edf00001ee0, + 0x1ee100001ee2, + 0x1ee300001ee4, + 0x1ee500001ee6, + 0x1ee700001ee8, + 0x1ee900001eea, + 0x1eeb00001eec, + 0x1eed00001eee, + 0x1eef00001ef0, + 0x1ef100001ef2, + 0x1ef300001ef4, + 0x1ef500001ef6, + 0x1ef700001ef8, + 0x1ef900001efa, + 0x1efb00001efc, + 0x1efd00001efe, + 0x1eff00001f08, + 0x1f1000001f16, + 0x1f2000001f28, + 0x1f3000001f38, + 0x1f4000001f46, + 0x1f5000001f58, + 0x1f6000001f68, + 0x1f7000001f71, + 0x1f7200001f73, + 0x1f7400001f75, + 0x1f7600001f77, + 0x1f7800001f79, + 0x1f7a00001f7b, + 0x1f7c00001f7d, + 0x1fb000001fb2, + 0x1fb600001fb7, + 0x1fc600001fc7, + 0x1fd000001fd3, + 0x1fd600001fd8, + 0x1fe000001fe3, + 0x1fe400001fe8, + 0x1ff600001ff7, + 0x214e0000214f, + 0x218400002185, + 0x2c3000002c5f, + 0x2c6100002c62, + 0x2c6500002c67, + 0x2c6800002c69, + 0x2c6a00002c6b, + 0x2c6c00002c6d, + 0x2c7100002c72, + 0x2c7300002c75, + 0x2c7600002c7c, + 0x2c8100002c82, + 0x2c8300002c84, + 0x2c8500002c86, + 0x2c8700002c88, + 0x2c8900002c8a, + 0x2c8b00002c8c, + 0x2c8d00002c8e, + 0x2c8f00002c90, + 0x2c9100002c92, + 0x2c9300002c94, + 0x2c9500002c96, + 0x2c9700002c98, + 0x2c9900002c9a, + 0x2c9b00002c9c, + 0x2c9d00002c9e, + 0x2c9f00002ca0, + 0x2ca100002ca2, + 0x2ca300002ca4, + 0x2ca500002ca6, + 0x2ca700002ca8, + 0x2ca900002caa, + 0x2cab00002cac, + 0x2cad00002cae, + 0x2caf00002cb0, + 0x2cb100002cb2, + 0x2cb300002cb4, + 0x2cb500002cb6, + 0x2cb700002cb8, + 0x2cb900002cba, + 0x2cbb00002cbc, + 0x2cbd00002cbe, + 0x2cbf00002cc0, + 0x2cc100002cc2, + 0x2cc300002cc4, + 0x2cc500002cc6, + 0x2cc700002cc8, + 0x2cc900002cca, + 0x2ccb00002ccc, + 0x2ccd00002cce, + 0x2ccf00002cd0, + 0x2cd100002cd2, + 0x2cd300002cd4, + 0x2cd500002cd6, + 0x2cd700002cd8, + 0x2cd900002cda, + 0x2cdb00002cdc, + 0x2cdd00002cde, + 0x2cdf00002ce0, + 0x2ce100002ce2, + 0x2ce300002ce5, + 0x2cec00002ced, + 0x2cee00002cf2, + 0x2cf300002cf4, + 0x2d0000002d26, + 0x2d2700002d28, + 0x2d2d00002d2e, + 0x2d3000002d68, + 0x2d7f00002d97, + 0x2da000002da7, + 0x2da800002daf, + 0x2db000002db7, + 0x2db800002dbf, + 0x2dc000002dc7, + 0x2dc800002dcf, + 0x2dd000002dd7, + 0x2dd800002ddf, + 0x2de000002e00, + 0x2e2f00002e30, + 0x300500003008, + 0x302a0000302e, + 0x303c0000303d, + 0x304100003097, + 0x30990000309b, + 0x309d0000309f, + 0x30a1000030fb, + 0x30fc000030ff, + 0x31050000312f, + 0x31a0000031bb, + 0x31f000003200, + 0x340000004db6, + 0x4e0000009feb, + 0xa0000000a48d, + 0xa4d00000a4fe, + 0xa5000000a60d, + 0xa6100000a62c, + 0xa6410000a642, + 0xa6430000a644, + 0xa6450000a646, + 0xa6470000a648, + 0xa6490000a64a, + 0xa64b0000a64c, + 0xa64d0000a64e, + 0xa64f0000a650, + 0xa6510000a652, + 0xa6530000a654, + 0xa6550000a656, + 0xa6570000a658, + 0xa6590000a65a, + 0xa65b0000a65c, + 0xa65d0000a65e, + 0xa65f0000a660, + 0xa6610000a662, + 0xa6630000a664, + 0xa6650000a666, + 0xa6670000a668, + 0xa6690000a66a, + 0xa66b0000a66c, + 0xa66d0000a670, + 0xa6740000a67e, + 0xa67f0000a680, + 0xa6810000a682, + 0xa6830000a684, + 0xa6850000a686, + 0xa6870000a688, + 0xa6890000a68a, + 0xa68b0000a68c, + 0xa68d0000a68e, + 0xa68f0000a690, + 0xa6910000a692, + 0xa6930000a694, + 0xa6950000a696, + 0xa6970000a698, + 0xa6990000a69a, + 0xa69b0000a69c, + 0xa69e0000a6e6, + 0xa6f00000a6f2, + 0xa7170000a720, + 0xa7230000a724, + 0xa7250000a726, + 0xa7270000a728, + 0xa7290000a72a, + 0xa72b0000a72c, + 0xa72d0000a72e, + 0xa72f0000a732, + 0xa7330000a734, + 0xa7350000a736, + 0xa7370000a738, + 0xa7390000a73a, + 0xa73b0000a73c, + 0xa73d0000a73e, + 0xa73f0000a740, + 0xa7410000a742, + 0xa7430000a744, + 0xa7450000a746, + 0xa7470000a748, + 0xa7490000a74a, + 0xa74b0000a74c, + 0xa74d0000a74e, + 0xa74f0000a750, + 0xa7510000a752, + 0xa7530000a754, + 0xa7550000a756, + 0xa7570000a758, + 0xa7590000a75a, + 0xa75b0000a75c, + 0xa75d0000a75e, + 0xa75f0000a760, + 0xa7610000a762, + 0xa7630000a764, + 0xa7650000a766, + 0xa7670000a768, + 0xa7690000a76a, + 0xa76b0000a76c, + 0xa76d0000a76e, + 0xa76f0000a770, + 0xa7710000a779, + 0xa77a0000a77b, + 0xa77c0000a77d, + 0xa77f0000a780, + 0xa7810000a782, + 0xa7830000a784, + 0xa7850000a786, + 0xa7870000a789, + 0xa78c0000a78d, + 0xa78e0000a790, + 0xa7910000a792, + 0xa7930000a796, + 0xa7970000a798, + 0xa7990000a79a, + 0xa79b0000a79c, + 0xa79d0000a79e, + 0xa79f0000a7a0, + 0xa7a10000a7a2, + 0xa7a30000a7a4, + 0xa7a50000a7a6, + 0xa7a70000a7a8, + 0xa7a90000a7aa, + 0xa7b50000a7b6, + 0xa7b70000a7b8, + 0xa7f70000a7f8, + 0xa7fa0000a828, + 0xa8400000a874, + 0xa8800000a8c6, + 0xa8d00000a8da, + 0xa8e00000a8f8, + 0xa8fb0000a8fc, + 0xa8fd0000a8fe, + 0xa9000000a92e, + 0xa9300000a954, + 0xa9800000a9c1, + 0xa9cf0000a9da, + 0xa9e00000a9ff, + 0xaa000000aa37, + 0xaa400000aa4e, + 0xaa500000aa5a, + 0xaa600000aa77, + 0xaa7a0000aac3, + 0xaadb0000aade, + 0xaae00000aaf0, + 0xaaf20000aaf7, + 0xab010000ab07, + 0xab090000ab0f, + 0xab110000ab17, + 0xab200000ab27, + 0xab280000ab2f, + 0xab300000ab5b, + 0xab600000ab66, + 0xabc00000abeb, + 0xabec0000abee, + 0xabf00000abfa, + 0xac000000d7a4, + 0xfa0e0000fa10, + 0xfa110000fa12, + 0xfa130000fa15, + 0xfa1f0000fa20, + 0xfa210000fa22, + 0xfa230000fa25, + 0xfa270000fa2a, + 0xfb1e0000fb1f, + 0xfe200000fe30, + 0xfe730000fe74, + 0x100000001000c, + 0x1000d00010027, + 0x100280001003b, + 0x1003c0001003e, + 0x1003f0001004e, + 0x100500001005e, + 0x10080000100fb, + 0x101fd000101fe, + 0x102800001029d, + 0x102a0000102d1, + 0x102e0000102e1, + 0x1030000010320, + 0x1032d00010341, + 0x103420001034a, + 0x103500001037b, + 0x103800001039e, + 0x103a0000103c4, + 0x103c8000103d0, + 0x104280001049e, + 0x104a0000104aa, + 0x104d8000104fc, + 0x1050000010528, + 0x1053000010564, + 0x1060000010737, + 0x1074000010756, + 0x1076000010768, + 0x1080000010806, + 0x1080800010809, + 0x1080a00010836, + 0x1083700010839, + 0x1083c0001083d, + 0x1083f00010856, + 0x1086000010877, + 0x108800001089f, + 0x108e0000108f3, + 0x108f4000108f6, + 0x1090000010916, + 0x109200001093a, + 0x10980000109b8, + 0x109be000109c0, + 0x10a0000010a04, + 0x10a0500010a07, + 0x10a0c00010a14, + 0x10a1500010a18, + 0x10a1900010a34, + 0x10a3800010a3b, + 0x10a3f00010a40, + 0x10a6000010a7d, + 0x10a8000010a9d, + 0x10ac000010ac8, + 0x10ac900010ae7, + 0x10b0000010b36, + 0x10b4000010b56, + 0x10b6000010b73, + 0x10b8000010b92, + 0x10c0000010c49, + 0x10cc000010cf3, + 0x1100000011047, + 0x1106600011070, + 0x1107f000110bb, + 0x110d0000110e9, + 0x110f0000110fa, + 0x1110000011135, + 0x1113600011140, + 0x1115000011174, + 0x1117600011177, + 0x11180000111c5, + 0x111ca000111cd, + 0x111d0000111db, + 0x111dc000111dd, + 0x1120000011212, + 0x1121300011238, + 0x1123e0001123f, + 0x1128000011287, + 0x1128800011289, + 0x1128a0001128e, + 0x1128f0001129e, + 0x1129f000112a9, + 0x112b0000112eb, + 0x112f0000112fa, + 0x1130000011304, + 0x113050001130d, + 0x1130f00011311, + 0x1131300011329, + 0x1132a00011331, + 0x1133200011334, + 0x113350001133a, + 0x1133c00011345, + 0x1134700011349, + 0x1134b0001134e, + 0x1135000011351, + 0x1135700011358, + 0x1135d00011364, + 0x113660001136d, + 0x1137000011375, + 0x114000001144b, + 0x114500001145a, + 0x11480000114c6, + 0x114c7000114c8, + 0x114d0000114da, + 0x11580000115b6, + 0x115b8000115c1, + 0x115d8000115de, + 0x1160000011641, + 0x1164400011645, + 0x116500001165a, + 0x11680000116b8, + 0x116c0000116ca, + 0x117000001171a, + 0x1171d0001172c, + 0x117300001173a, + 0x118c0000118ea, + 0x118ff00011900, + 0x11a0000011a3f, + 0x11a4700011a48, + 0x11a5000011a84, + 0x11a8600011a9a, + 0x11ac000011af9, + 0x11c0000011c09, + 0x11c0a00011c37, + 0x11c3800011c41, + 0x11c5000011c5a, + 0x11c7200011c90, + 0x11c9200011ca8, + 0x11ca900011cb7, + 0x11d0000011d07, + 0x11d0800011d0a, + 0x11d0b00011d37, + 0x11d3a00011d3b, + 0x11d3c00011d3e, + 0x11d3f00011d48, + 0x11d5000011d5a, + 0x120000001239a, + 0x1248000012544, + 0x130000001342f, + 0x1440000014647, + 0x1680000016a39, + 0x16a4000016a5f, + 0x16a6000016a6a, + 0x16ad000016aee, + 0x16af000016af5, + 0x16b0000016b37, + 0x16b4000016b44, + 0x16b5000016b5a, + 0x16b6300016b78, + 0x16b7d00016b90, + 0x16f0000016f45, + 0x16f5000016f7f, + 0x16f8f00016fa0, + 0x16fe000016fe2, + 0x17000000187ed, + 0x1880000018af3, + 0x1b0000001b11f, + 0x1b1700001b2fc, + 0x1bc000001bc6b, + 0x1bc700001bc7d, + 0x1bc800001bc89, + 0x1bc900001bc9a, + 0x1bc9d0001bc9f, + 0x1da000001da37, + 0x1da3b0001da6d, + 0x1da750001da76, + 0x1da840001da85, + 0x1da9b0001daa0, + 0x1daa10001dab0, + 0x1e0000001e007, + 0x1e0080001e019, + 0x1e01b0001e022, + 0x1e0230001e025, + 0x1e0260001e02b, + 0x1e8000001e8c5, + 0x1e8d00001e8d7, + 0x1e9220001e94b, + 0x1e9500001e95a, + 0x200000002a6d7, + 0x2a7000002b735, + 0x2b7400002b81e, + 0x2b8200002cea2, + 0x2ceb00002ebe1, + ), + 'CONTEXTJ': ( + 0x200c0000200e, + ), + 'CONTEXTO': ( + 0xb7000000b8, + 0x37500000376, + 0x5f3000005f5, + 0x6600000066a, + 0x6f0000006fa, + 0x30fb000030fc, + ), +} diff --git a/venv/Lib/site-packages/pip/_vendor/idna/intranges.py b/venv/Lib/site-packages/pip/_vendor/idna/intranges.py new file mode 100644 index 0000000..fa8a735 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/idna/intranges.py @@ -0,0 +1,53 @@ +""" +Given a list of integers, made up of (hopefully) a small number of long runs +of consecutive integers, compute a representation of the form +((start1, end1), (start2, end2) ...). Then answer the question "was x present +in the original list?" in time O(log(# runs)). +""" + +import bisect + +def intranges_from_list(list_): + """Represent a list of integers as a sequence of ranges: + ((start_0, end_0), (start_1, end_1), ...), such that the original + integers are exactly those x such that start_i <= x < end_i for some i. + + Ranges are encoded as single integers (start << 32 | end), not as tuples. + """ + + sorted_list = sorted(list_) + ranges = [] + last_write = -1 + for i in range(len(sorted_list)): + if i+1 < len(sorted_list): + if sorted_list[i] == sorted_list[i+1]-1: + continue + current_range = sorted_list[last_write+1:i+1] + ranges.append(_encode_range(current_range[0], current_range[-1] + 1)) + last_write = i + + return tuple(ranges) + +def _encode_range(start, end): + return (start << 32) | end + +def _decode_range(r): + return (r >> 32), (r & ((1 << 32) - 1)) + + +def intranges_contain(int_, ranges): + """Determine if `int_` falls into one of the ranges in `ranges`.""" + tuple_ = _encode_range(int_, 0) + pos = bisect.bisect_left(ranges, tuple_) + # we could be immediately ahead of a tuple (start, end) + # with start < int_ <= end + if pos > 0: + left, right = _decode_range(ranges[pos-1]) + if left <= int_ < right: + return True + # or we could be immediately behind a tuple (int_, end) + if pos < len(ranges): + left, _ = _decode_range(ranges[pos]) + if left == int_: + return True + return False diff --git a/venv/Lib/site-packages/pip/_vendor/idna/package_data.py b/venv/Lib/site-packages/pip/_vendor/idna/package_data.py new file mode 100644 index 0000000..39c192b --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/idna/package_data.py @@ -0,0 +1,2 @@ +__version__ = '2.7' + diff --git a/venv/Lib/site-packages/pip/_vendor/idna/uts46data.py b/venv/Lib/site-packages/pip/_vendor/idna/uts46data.py new file mode 100644 index 0000000..79731cb --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/idna/uts46data.py @@ -0,0 +1,8179 @@ +# This file is automatically generated by tools/idna-data +# vim: set fileencoding=utf-8 : + +"""IDNA Mapping Table from UTS46.""" + + +__version__ = "10.0.0" +def _seg_0(): + return [ + (0x0, '3'), + (0x1, '3'), + (0x2, '3'), + (0x3, '3'), + (0x4, '3'), + (0x5, '3'), + (0x6, '3'), + (0x7, '3'), + (0x8, '3'), + (0x9, '3'), + (0xA, '3'), + (0xB, '3'), + (0xC, '3'), + (0xD, '3'), + (0xE, '3'), + (0xF, '3'), + (0x10, '3'), + (0x11, '3'), + (0x12, '3'), + (0x13, '3'), + (0x14, '3'), + (0x15, '3'), + (0x16, '3'), + (0x17, '3'), + (0x18, '3'), + (0x19, '3'), + (0x1A, '3'), + (0x1B, '3'), + (0x1C, '3'), + (0x1D, '3'), + (0x1E, '3'), + (0x1F, '3'), + (0x20, '3'), + (0x21, '3'), + (0x22, '3'), + (0x23, '3'), + (0x24, '3'), + (0x25, '3'), + (0x26, '3'), + (0x27, '3'), + (0x28, '3'), + (0x29, '3'), + (0x2A, '3'), + (0x2B, '3'), + (0x2C, '3'), + (0x2D, 'V'), + (0x2E, 'V'), + (0x2F, '3'), + (0x30, 'V'), + (0x31, 'V'), + (0x32, 'V'), + (0x33, 'V'), + (0x34, 'V'), + (0x35, 'V'), + (0x36, 'V'), + (0x37, 'V'), + (0x38, 'V'), + (0x39, 'V'), + (0x3A, '3'), + (0x3B, '3'), + (0x3C, '3'), + (0x3D, '3'), + (0x3E, '3'), + (0x3F, '3'), + (0x40, '3'), + (0x41, 'M', u'a'), + (0x42, 'M', u'b'), + (0x43, 'M', u'c'), + (0x44, 'M', u'd'), + (0x45, 'M', u'e'), + (0x46, 'M', u'f'), + (0x47, 'M', u'g'), + (0x48, 'M', u'h'), + (0x49, 'M', u'i'), + (0x4A, 'M', u'j'), + (0x4B, 'M', u'k'), + (0x4C, 'M', u'l'), + (0x4D, 'M', u'm'), + (0x4E, 'M', u'n'), + (0x4F, 'M', u'o'), + (0x50, 'M', u'p'), + (0x51, 'M', u'q'), + (0x52, 'M', u'r'), + (0x53, 'M', u's'), + (0x54, 'M', u't'), + (0x55, 'M', u'u'), + (0x56, 'M', u'v'), + (0x57, 'M', u'w'), + (0x58, 'M', u'x'), + (0x59, 'M', u'y'), + (0x5A, 'M', u'z'), + (0x5B, '3'), + (0x5C, '3'), + (0x5D, '3'), + (0x5E, '3'), + (0x5F, '3'), + (0x60, '3'), + (0x61, 'V'), + (0x62, 'V'), + (0x63, 'V'), + ] + +def _seg_1(): + return [ + (0x64, 'V'), + (0x65, 'V'), + (0x66, 'V'), + (0x67, 'V'), + (0x68, 'V'), + (0x69, 'V'), + (0x6A, 'V'), + (0x6B, 'V'), + (0x6C, 'V'), + (0x6D, 'V'), + (0x6E, 'V'), + (0x6F, 'V'), + (0x70, 'V'), + (0x71, 'V'), + (0x72, 'V'), + (0x73, 'V'), + (0x74, 'V'), + (0x75, 'V'), + (0x76, 'V'), + (0x77, 'V'), + (0x78, 'V'), + (0x79, 'V'), + (0x7A, 'V'), + (0x7B, '3'), + (0x7C, '3'), + (0x7D, '3'), + (0x7E, '3'), + (0x7F, '3'), + (0x80, 'X'), + (0x81, 'X'), + (0x82, 'X'), + (0x83, 'X'), + (0x84, 'X'), + (0x85, 'X'), + (0x86, 'X'), + (0x87, 'X'), + (0x88, 'X'), + (0x89, 'X'), + (0x8A, 'X'), + (0x8B, 'X'), + (0x8C, 'X'), + (0x8D, 'X'), + (0x8E, 'X'), + (0x8F, 'X'), + (0x90, 'X'), + (0x91, 'X'), + (0x92, 'X'), + (0x93, 'X'), + (0x94, 'X'), + (0x95, 'X'), + (0x96, 'X'), + (0x97, 'X'), + (0x98, 'X'), + (0x99, 'X'), + (0x9A, 'X'), + (0x9B, 'X'), + (0x9C, 'X'), + (0x9D, 'X'), + (0x9E, 'X'), + (0x9F, 'X'), + (0xA0, '3', u' '), + (0xA1, 'V'), + (0xA2, 'V'), + (0xA3, 'V'), + (0xA4, 'V'), + (0xA5, 'V'), + (0xA6, 'V'), + (0xA7, 'V'), + (0xA8, '3', u' ̈'), + (0xA9, 'V'), + (0xAA, 'M', u'a'), + (0xAB, 'V'), + (0xAC, 'V'), + (0xAD, 'I'), + (0xAE, 'V'), + (0xAF, '3', u' ̄'), + (0xB0, 'V'), + (0xB1, 'V'), + (0xB2, 'M', u'2'), + (0xB3, 'M', u'3'), + (0xB4, '3', u' ́'), + (0xB5, 'M', u'μ'), + (0xB6, 'V'), + (0xB7, 'V'), + (0xB8, '3', u' ̧'), + (0xB9, 'M', u'1'), + (0xBA, 'M', u'o'), + (0xBB, 'V'), + (0xBC, 'M', u'1⁄4'), + (0xBD, 'M', u'1⁄2'), + (0xBE, 'M', u'3⁄4'), + (0xBF, 'V'), + (0xC0, 'M', u'à'), + (0xC1, 'M', u'á'), + (0xC2, 'M', u'â'), + (0xC3, 'M', u'ã'), + (0xC4, 'M', u'ä'), + (0xC5, 'M', u'å'), + (0xC6, 'M', u'æ'), + (0xC7, 'M', u'ç'), + ] + +def _seg_2(): + return [ + (0xC8, 'M', u'è'), + (0xC9, 'M', u'é'), + (0xCA, 'M', u'ê'), + (0xCB, 'M', u'ë'), + (0xCC, 'M', u'ì'), + (0xCD, 'M', u'í'), + (0xCE, 'M', u'î'), + (0xCF, 'M', u'ï'), + (0xD0, 'M', u'ð'), + (0xD1, 'M', u'ñ'), + (0xD2, 'M', u'ò'), + (0xD3, 'M', u'ó'), + (0xD4, 'M', u'ô'), + (0xD5, 'M', u'õ'), + (0xD6, 'M', u'ö'), + (0xD7, 'V'), + (0xD8, 'M', u'ø'), + (0xD9, 'M', u'ù'), + (0xDA, 'M', u'ú'), + (0xDB, 'M', u'û'), + (0xDC, 'M', u'ü'), + (0xDD, 'M', u'ý'), + (0xDE, 'M', u'þ'), + (0xDF, 'D', u'ss'), + (0xE0, 'V'), + (0xE1, 'V'), + (0xE2, 'V'), + (0xE3, 'V'), + (0xE4, 'V'), + (0xE5, 'V'), + (0xE6, 'V'), + (0xE7, 'V'), + (0xE8, 'V'), + (0xE9, 'V'), + (0xEA, 'V'), + (0xEB, 'V'), + (0xEC, 'V'), + (0xED, 'V'), + (0xEE, 'V'), + (0xEF, 'V'), + (0xF0, 'V'), + (0xF1, 'V'), + (0xF2, 'V'), + (0xF3, 'V'), + (0xF4, 'V'), + (0xF5, 'V'), + (0xF6, 'V'), + (0xF7, 'V'), + (0xF8, 'V'), + (0xF9, 'V'), + (0xFA, 'V'), + (0xFB, 'V'), + (0xFC, 'V'), + (0xFD, 'V'), + (0xFE, 'V'), + (0xFF, 'V'), + (0x100, 'M', u'ā'), + (0x101, 'V'), + (0x102, 'M', u'ă'), + (0x103, 'V'), + (0x104, 'M', u'ą'), + (0x105, 'V'), + (0x106, 'M', u'ć'), + (0x107, 'V'), + (0x108, 'M', u'ĉ'), + (0x109, 'V'), + (0x10A, 'M', u'ċ'), + (0x10B, 'V'), + (0x10C, 'M', u'č'), + (0x10D, 'V'), + (0x10E, 'M', u'ď'), + (0x10F, 'V'), + (0x110, 'M', u'đ'), + (0x111, 'V'), + (0x112, 'M', u'ē'), + (0x113, 'V'), + (0x114, 'M', u'ĕ'), + (0x115, 'V'), + (0x116, 'M', u'ė'), + (0x117, 'V'), + (0x118, 'M', u'ę'), + (0x119, 'V'), + (0x11A, 'M', u'ě'), + (0x11B, 'V'), + (0x11C, 'M', u'ĝ'), + (0x11D, 'V'), + (0x11E, 'M', u'ğ'), + (0x11F, 'V'), + (0x120, 'M', u'ġ'), + (0x121, 'V'), + (0x122, 'M', u'ģ'), + (0x123, 'V'), + (0x124, 'M', u'ĥ'), + (0x125, 'V'), + (0x126, 'M', u'ħ'), + (0x127, 'V'), + (0x128, 'M', u'ĩ'), + (0x129, 'V'), + (0x12A, 'M', u'ī'), + (0x12B, 'V'), + ] + +def _seg_3(): + return [ + (0x12C, 'M', u'ĭ'), + (0x12D, 'V'), + (0x12E, 'M', u'į'), + (0x12F, 'V'), + (0x130, 'M', u'i̇'), + (0x131, 'V'), + (0x132, 'M', u'ij'), + (0x134, 'M', u'ĵ'), + (0x135, 'V'), + (0x136, 'M', u'ķ'), + (0x137, 'V'), + (0x139, 'M', u'ĺ'), + (0x13A, 'V'), + (0x13B, 'M', u'ļ'), + (0x13C, 'V'), + (0x13D, 'M', u'ľ'), + (0x13E, 'V'), + (0x13F, 'M', u'l·'), + (0x141, 'M', u'ł'), + (0x142, 'V'), + (0x143, 'M', u'ń'), + (0x144, 'V'), + (0x145, 'M', u'ņ'), + (0x146, 'V'), + (0x147, 'M', u'ň'), + (0x148, 'V'), + (0x149, 'M', u'ʼn'), + (0x14A, 'M', u'ŋ'), + (0x14B, 'V'), + (0x14C, 'M', u'ō'), + (0x14D, 'V'), + (0x14E, 'M', u'ŏ'), + (0x14F, 'V'), + (0x150, 'M', u'ő'), + (0x151, 'V'), + (0x152, 'M', u'œ'), + (0x153, 'V'), + (0x154, 'M', u'ŕ'), + (0x155, 'V'), + (0x156, 'M', u'ŗ'), + (0x157, 'V'), + (0x158, 'M', u'ř'), + (0x159, 'V'), + (0x15A, 'M', u'ś'), + (0x15B, 'V'), + (0x15C, 'M', u'ŝ'), + (0x15D, 'V'), + (0x15E, 'M', u'ş'), + (0x15F, 'V'), + (0x160, 'M', u'š'), + (0x161, 'V'), + (0x162, 'M', u'ţ'), + (0x163, 'V'), + (0x164, 'M', u'ť'), + (0x165, 'V'), + (0x166, 'M', u'ŧ'), + (0x167, 'V'), + (0x168, 'M', u'ũ'), + (0x169, 'V'), + (0x16A, 'M', u'ū'), + (0x16B, 'V'), + (0x16C, 'M', u'ŭ'), + (0x16D, 'V'), + (0x16E, 'M', u'ů'), + (0x16F, 'V'), + (0x170, 'M', u'ű'), + (0x171, 'V'), + (0x172, 'M', u'ų'), + (0x173, 'V'), + (0x174, 'M', u'ŵ'), + (0x175, 'V'), + (0x176, 'M', u'ŷ'), + (0x177, 'V'), + (0x178, 'M', u'ÿ'), + (0x179, 'M', u'ź'), + (0x17A, 'V'), + (0x17B, 'M', u'ż'), + (0x17C, 'V'), + (0x17D, 'M', u'ž'), + (0x17E, 'V'), + (0x17F, 'M', u's'), + (0x180, 'V'), + (0x181, 'M', u'ɓ'), + (0x182, 'M', u'ƃ'), + (0x183, 'V'), + (0x184, 'M', u'ƅ'), + (0x185, 'V'), + (0x186, 'M', u'ɔ'), + (0x187, 'M', u'ƈ'), + (0x188, 'V'), + (0x189, 'M', u'ɖ'), + (0x18A, 'M', u'ɗ'), + (0x18B, 'M', u'ƌ'), + (0x18C, 'V'), + (0x18E, 'M', u'ǝ'), + (0x18F, 'M', u'ə'), + (0x190, 'M', u'ɛ'), + (0x191, 'M', u'ƒ'), + (0x192, 'V'), + (0x193, 'M', u'ɠ'), + ] + +def _seg_4(): + return [ + (0x194, 'M', u'ɣ'), + (0x195, 'V'), + (0x196, 'M', u'ɩ'), + (0x197, 'M', u'ɨ'), + (0x198, 'M', u'ƙ'), + (0x199, 'V'), + (0x19C, 'M', u'ɯ'), + (0x19D, 'M', u'ɲ'), + (0x19E, 'V'), + (0x19F, 'M', u'ɵ'), + (0x1A0, 'M', u'ơ'), + (0x1A1, 'V'), + (0x1A2, 'M', u'ƣ'), + (0x1A3, 'V'), + (0x1A4, 'M', u'ƥ'), + (0x1A5, 'V'), + (0x1A6, 'M', u'ʀ'), + (0x1A7, 'M', u'ƨ'), + (0x1A8, 'V'), + (0x1A9, 'M', u'ʃ'), + (0x1AA, 'V'), + (0x1AC, 'M', u'ƭ'), + (0x1AD, 'V'), + (0x1AE, 'M', u'ʈ'), + (0x1AF, 'M', u'ư'), + (0x1B0, 'V'), + (0x1B1, 'M', u'ʊ'), + (0x1B2, 'M', u'ʋ'), + (0x1B3, 'M', u'ƴ'), + (0x1B4, 'V'), + (0x1B5, 'M', u'ƶ'), + (0x1B6, 'V'), + (0x1B7, 'M', u'ʒ'), + (0x1B8, 'M', u'ƹ'), + (0x1B9, 'V'), + (0x1BC, 'M', u'ƽ'), + (0x1BD, 'V'), + (0x1C4, 'M', u'dž'), + (0x1C7, 'M', u'lj'), + (0x1CA, 'M', u'nj'), + (0x1CD, 'M', u'ǎ'), + (0x1CE, 'V'), + (0x1CF, 'M', u'ǐ'), + (0x1D0, 'V'), + (0x1D1, 'M', u'ǒ'), + (0x1D2, 'V'), + (0x1D3, 'M', u'ǔ'), + (0x1D4, 'V'), + (0x1D5, 'M', u'ǖ'), + (0x1D6, 'V'), + (0x1D7, 'M', u'ǘ'), + (0x1D8, 'V'), + (0x1D9, 'M', u'ǚ'), + (0x1DA, 'V'), + (0x1DB, 'M', u'ǜ'), + (0x1DC, 'V'), + (0x1DE, 'M', u'ǟ'), + (0x1DF, 'V'), + (0x1E0, 'M', u'ǡ'), + (0x1E1, 'V'), + (0x1E2, 'M', u'ǣ'), + (0x1E3, 'V'), + (0x1E4, 'M', u'ǥ'), + (0x1E5, 'V'), + (0x1E6, 'M', u'ǧ'), + (0x1E7, 'V'), + (0x1E8, 'M', u'ǩ'), + (0x1E9, 'V'), + (0x1EA, 'M', u'ǫ'), + (0x1EB, 'V'), + (0x1EC, 'M', u'ǭ'), + (0x1ED, 'V'), + (0x1EE, 'M', u'ǯ'), + (0x1EF, 'V'), + (0x1F1, 'M', u'dz'), + (0x1F4, 'M', u'ǵ'), + (0x1F5, 'V'), + (0x1F6, 'M', u'ƕ'), + (0x1F7, 'M', u'ƿ'), + (0x1F8, 'M', u'ǹ'), + (0x1F9, 'V'), + (0x1FA, 'M', u'ǻ'), + (0x1FB, 'V'), + (0x1FC, 'M', u'ǽ'), + (0x1FD, 'V'), + (0x1FE, 'M', u'ǿ'), + (0x1FF, 'V'), + (0x200, 'M', u'ȁ'), + (0x201, 'V'), + (0x202, 'M', u'ȃ'), + (0x203, 'V'), + (0x204, 'M', u'ȅ'), + (0x205, 'V'), + (0x206, 'M', u'ȇ'), + (0x207, 'V'), + (0x208, 'M', u'ȉ'), + (0x209, 'V'), + (0x20A, 'M', u'ȋ'), + (0x20B, 'V'), + (0x20C, 'M', u'ȍ'), + ] + +def _seg_5(): + return [ + (0x20D, 'V'), + (0x20E, 'M', u'ȏ'), + (0x20F, 'V'), + (0x210, 'M', u'ȑ'), + (0x211, 'V'), + (0x212, 'M', u'ȓ'), + (0x213, 'V'), + (0x214, 'M', u'ȕ'), + (0x215, 'V'), + (0x216, 'M', u'ȗ'), + (0x217, 'V'), + (0x218, 'M', u'ș'), + (0x219, 'V'), + (0x21A, 'M', u'ț'), + (0x21B, 'V'), + (0x21C, 'M', u'ȝ'), + (0x21D, 'V'), + (0x21E, 'M', u'ȟ'), + (0x21F, 'V'), + (0x220, 'M', u'ƞ'), + (0x221, 'V'), + (0x222, 'M', u'ȣ'), + (0x223, 'V'), + (0x224, 'M', u'ȥ'), + (0x225, 'V'), + (0x226, 'M', u'ȧ'), + (0x227, 'V'), + (0x228, 'M', u'ȩ'), + (0x229, 'V'), + (0x22A, 'M', u'ȫ'), + (0x22B, 'V'), + (0x22C, 'M', u'ȭ'), + (0x22D, 'V'), + (0x22E, 'M', u'ȯ'), + (0x22F, 'V'), + (0x230, 'M', u'ȱ'), + (0x231, 'V'), + (0x232, 'M', u'ȳ'), + (0x233, 'V'), + (0x23A, 'M', u'ⱥ'), + (0x23B, 'M', u'ȼ'), + (0x23C, 'V'), + (0x23D, 'M', u'ƚ'), + (0x23E, 'M', u'ⱦ'), + (0x23F, 'V'), + (0x241, 'M', u'ɂ'), + (0x242, 'V'), + (0x243, 'M', u'ƀ'), + (0x244, 'M', u'ʉ'), + (0x245, 'M', u'ʌ'), + (0x246, 'M', u'ɇ'), + (0x247, 'V'), + (0x248, 'M', u'ɉ'), + (0x249, 'V'), + (0x24A, 'M', u'ɋ'), + (0x24B, 'V'), + (0x24C, 'M', u'ɍ'), + (0x24D, 'V'), + (0x24E, 'M', u'ɏ'), + (0x24F, 'V'), + (0x2B0, 'M', u'h'), + (0x2B1, 'M', u'ɦ'), + (0x2B2, 'M', u'j'), + (0x2B3, 'M', u'r'), + (0x2B4, 'M', u'ɹ'), + (0x2B5, 'M', u'ɻ'), + (0x2B6, 'M', u'ʁ'), + (0x2B7, 'M', u'w'), + (0x2B8, 'M', u'y'), + (0x2B9, 'V'), + (0x2D8, '3', u' ̆'), + (0x2D9, '3', u' ̇'), + (0x2DA, '3', u' ̊'), + (0x2DB, '3', u' ̨'), + (0x2DC, '3', u' ̃'), + (0x2DD, '3', u' ̋'), + (0x2DE, 'V'), + (0x2E0, 'M', u'ɣ'), + (0x2E1, 'M', u'l'), + (0x2E2, 'M', u's'), + (0x2E3, 'M', u'x'), + (0x2E4, 'M', u'ʕ'), + (0x2E5, 'V'), + (0x340, 'M', u'̀'), + (0x341, 'M', u'́'), + (0x342, 'V'), + (0x343, 'M', u'̓'), + (0x344, 'M', u'̈́'), + (0x345, 'M', u'ι'), + (0x346, 'V'), + (0x34F, 'I'), + (0x350, 'V'), + (0x370, 'M', u'ͱ'), + (0x371, 'V'), + (0x372, 'M', u'ͳ'), + (0x373, 'V'), + (0x374, 'M', u'ʹ'), + (0x375, 'V'), + (0x376, 'M', u'ͷ'), + (0x377, 'V'), + ] + +def _seg_6(): + return [ + (0x378, 'X'), + (0x37A, '3', u' ι'), + (0x37B, 'V'), + (0x37E, '3', u';'), + (0x37F, 'M', u'ϳ'), + (0x380, 'X'), + (0x384, '3', u' ́'), + (0x385, '3', u' ̈́'), + (0x386, 'M', u'ά'), + (0x387, 'M', u'·'), + (0x388, 'M', u'έ'), + (0x389, 'M', u'ή'), + (0x38A, 'M', u'ί'), + (0x38B, 'X'), + (0x38C, 'M', u'ό'), + (0x38D, 'X'), + (0x38E, 'M', u'ύ'), + (0x38F, 'M', u'ώ'), + (0x390, 'V'), + (0x391, 'M', u'α'), + (0x392, 'M', u'β'), + (0x393, 'M', u'γ'), + (0x394, 'M', u'δ'), + (0x395, 'M', u'ε'), + (0x396, 'M', u'ζ'), + (0x397, 'M', u'η'), + (0x398, 'M', u'θ'), + (0x399, 'M', u'ι'), + (0x39A, 'M', u'κ'), + (0x39B, 'M', u'λ'), + (0x39C, 'M', u'μ'), + (0x39D, 'M', u'ν'), + (0x39E, 'M', u'ξ'), + (0x39F, 'M', u'ο'), + (0x3A0, 'M', u'π'), + (0x3A1, 'M', u'ρ'), + (0x3A2, 'X'), + (0x3A3, 'M', u'σ'), + (0x3A4, 'M', u'τ'), + (0x3A5, 'M', u'υ'), + (0x3A6, 'M', u'φ'), + (0x3A7, 'M', u'χ'), + (0x3A8, 'M', u'ψ'), + (0x3A9, 'M', u'ω'), + (0x3AA, 'M', u'ϊ'), + (0x3AB, 'M', u'ϋ'), + (0x3AC, 'V'), + (0x3C2, 'D', u'σ'), + (0x3C3, 'V'), + (0x3CF, 'M', u'ϗ'), + (0x3D0, 'M', u'β'), + (0x3D1, 'M', u'θ'), + (0x3D2, 'M', u'υ'), + (0x3D3, 'M', u'ύ'), + (0x3D4, 'M', u'ϋ'), + (0x3D5, 'M', u'φ'), + (0x3D6, 'M', u'π'), + (0x3D7, 'V'), + (0x3D8, 'M', u'ϙ'), + (0x3D9, 'V'), + (0x3DA, 'M', u'ϛ'), + (0x3DB, 'V'), + (0x3DC, 'M', u'ϝ'), + (0x3DD, 'V'), + (0x3DE, 'M', u'ϟ'), + (0x3DF, 'V'), + (0x3E0, 'M', u'ϡ'), + (0x3E1, 'V'), + (0x3E2, 'M', u'ϣ'), + (0x3E3, 'V'), + (0x3E4, 'M', u'ϥ'), + (0x3E5, 'V'), + (0x3E6, 'M', u'ϧ'), + (0x3E7, 'V'), + (0x3E8, 'M', u'ϩ'), + (0x3E9, 'V'), + (0x3EA, 'M', u'ϫ'), + (0x3EB, 'V'), + (0x3EC, 'M', u'ϭ'), + (0x3ED, 'V'), + (0x3EE, 'M', u'ϯ'), + (0x3EF, 'V'), + (0x3F0, 'M', u'κ'), + (0x3F1, 'M', u'ρ'), + (0x3F2, 'M', u'σ'), + (0x3F3, 'V'), + (0x3F4, 'M', u'θ'), + (0x3F5, 'M', u'ε'), + (0x3F6, 'V'), + (0x3F7, 'M', u'ϸ'), + (0x3F8, 'V'), + (0x3F9, 'M', u'σ'), + (0x3FA, 'M', u'ϻ'), + (0x3FB, 'V'), + (0x3FD, 'M', u'ͻ'), + (0x3FE, 'M', u'ͼ'), + (0x3FF, 'M', u'ͽ'), + (0x400, 'M', u'ѐ'), + (0x401, 'M', u'ё'), + (0x402, 'M', u'ђ'), + ] + +def _seg_7(): + return [ + (0x403, 'M', u'ѓ'), + (0x404, 'M', u'є'), + (0x405, 'M', u'ѕ'), + (0x406, 'M', u'і'), + (0x407, 'M', u'ї'), + (0x408, 'M', u'ј'), + (0x409, 'M', u'љ'), + (0x40A, 'M', u'њ'), + (0x40B, 'M', u'ћ'), + (0x40C, 'M', u'ќ'), + (0x40D, 'M', u'ѝ'), + (0x40E, 'M', u'ў'), + (0x40F, 'M', u'џ'), + (0x410, 'M', u'а'), + (0x411, 'M', u'б'), + (0x412, 'M', u'в'), + (0x413, 'M', u'г'), + (0x414, 'M', u'д'), + (0x415, 'M', u'е'), + (0x416, 'M', u'ж'), + (0x417, 'M', u'з'), + (0x418, 'M', u'и'), + (0x419, 'M', u'й'), + (0x41A, 'M', u'к'), + (0x41B, 'M', u'л'), + (0x41C, 'M', u'м'), + (0x41D, 'M', u'н'), + (0x41E, 'M', u'о'), + (0x41F, 'M', u'п'), + (0x420, 'M', u'р'), + (0x421, 'M', u'с'), + (0x422, 'M', u'т'), + (0x423, 'M', u'у'), + (0x424, 'M', u'ф'), + (0x425, 'M', u'х'), + (0x426, 'M', u'ц'), + (0x427, 'M', u'ч'), + (0x428, 'M', u'ш'), + (0x429, 'M', u'щ'), + (0x42A, 'M', u'ъ'), + (0x42B, 'M', u'ы'), + (0x42C, 'M', u'ь'), + (0x42D, 'M', u'э'), + (0x42E, 'M', u'ю'), + (0x42F, 'M', u'я'), + (0x430, 'V'), + (0x460, 'M', u'ѡ'), + (0x461, 'V'), + (0x462, 'M', u'ѣ'), + (0x463, 'V'), + (0x464, 'M', u'ѥ'), + (0x465, 'V'), + (0x466, 'M', u'ѧ'), + (0x467, 'V'), + (0x468, 'M', u'ѩ'), + (0x469, 'V'), + (0x46A, 'M', u'ѫ'), + (0x46B, 'V'), + (0x46C, 'M', u'ѭ'), + (0x46D, 'V'), + (0x46E, 'M', u'ѯ'), + (0x46F, 'V'), + (0x470, 'M', u'ѱ'), + (0x471, 'V'), + (0x472, 'M', u'ѳ'), + (0x473, 'V'), + (0x474, 'M', u'ѵ'), + (0x475, 'V'), + (0x476, 'M', u'ѷ'), + (0x477, 'V'), + (0x478, 'M', u'ѹ'), + (0x479, 'V'), + (0x47A, 'M', u'ѻ'), + (0x47B, 'V'), + (0x47C, 'M', u'ѽ'), + (0x47D, 'V'), + (0x47E, 'M', u'ѿ'), + (0x47F, 'V'), + (0x480, 'M', u'ҁ'), + (0x481, 'V'), + (0x48A, 'M', u'ҋ'), + (0x48B, 'V'), + (0x48C, 'M', u'ҍ'), + (0x48D, 'V'), + (0x48E, 'M', u'ҏ'), + (0x48F, 'V'), + (0x490, 'M', u'ґ'), + (0x491, 'V'), + (0x492, 'M', u'ғ'), + (0x493, 'V'), + (0x494, 'M', u'ҕ'), + (0x495, 'V'), + (0x496, 'M', u'җ'), + (0x497, 'V'), + (0x498, 'M', u'ҙ'), + (0x499, 'V'), + (0x49A, 'M', u'қ'), + (0x49B, 'V'), + (0x49C, 'M', u'ҝ'), + (0x49D, 'V'), + ] + +def _seg_8(): + return [ + (0x49E, 'M', u'ҟ'), + (0x49F, 'V'), + (0x4A0, 'M', u'ҡ'), + (0x4A1, 'V'), + (0x4A2, 'M', u'ң'), + (0x4A3, 'V'), + (0x4A4, 'M', u'ҥ'), + (0x4A5, 'V'), + (0x4A6, 'M', u'ҧ'), + (0x4A7, 'V'), + (0x4A8, 'M', u'ҩ'), + (0x4A9, 'V'), + (0x4AA, 'M', u'ҫ'), + (0x4AB, 'V'), + (0x4AC, 'M', u'ҭ'), + (0x4AD, 'V'), + (0x4AE, 'M', u'ү'), + (0x4AF, 'V'), + (0x4B0, 'M', u'ұ'), + (0x4B1, 'V'), + (0x4B2, 'M', u'ҳ'), + (0x4B3, 'V'), + (0x4B4, 'M', u'ҵ'), + (0x4B5, 'V'), + (0x4B6, 'M', u'ҷ'), + (0x4B7, 'V'), + (0x4B8, 'M', u'ҹ'), + (0x4B9, 'V'), + (0x4BA, 'M', u'һ'), + (0x4BB, 'V'), + (0x4BC, 'M', u'ҽ'), + (0x4BD, 'V'), + (0x4BE, 'M', u'ҿ'), + (0x4BF, 'V'), + (0x4C0, 'X'), + (0x4C1, 'M', u'ӂ'), + (0x4C2, 'V'), + (0x4C3, 'M', u'ӄ'), + (0x4C4, 'V'), + (0x4C5, 'M', u'ӆ'), + (0x4C6, 'V'), + (0x4C7, 'M', u'ӈ'), + (0x4C8, 'V'), + (0x4C9, 'M', u'ӊ'), + (0x4CA, 'V'), + (0x4CB, 'M', u'ӌ'), + (0x4CC, 'V'), + (0x4CD, 'M', u'ӎ'), + (0x4CE, 'V'), + (0x4D0, 'M', u'ӑ'), + (0x4D1, 'V'), + (0x4D2, 'M', u'ӓ'), + (0x4D3, 'V'), + (0x4D4, 'M', u'ӕ'), + (0x4D5, 'V'), + (0x4D6, 'M', u'ӗ'), + (0x4D7, 'V'), + (0x4D8, 'M', u'ә'), + (0x4D9, 'V'), + (0x4DA, 'M', u'ӛ'), + (0x4DB, 'V'), + (0x4DC, 'M', u'ӝ'), + (0x4DD, 'V'), + (0x4DE, 'M', u'ӟ'), + (0x4DF, 'V'), + (0x4E0, 'M', u'ӡ'), + (0x4E1, 'V'), + (0x4E2, 'M', u'ӣ'), + (0x4E3, 'V'), + (0x4E4, 'M', u'ӥ'), + (0x4E5, 'V'), + (0x4E6, 'M', u'ӧ'), + (0x4E7, 'V'), + (0x4E8, 'M', u'ө'), + (0x4E9, 'V'), + (0x4EA, 'M', u'ӫ'), + (0x4EB, 'V'), + (0x4EC, 'M', u'ӭ'), + (0x4ED, 'V'), + (0x4EE, 'M', u'ӯ'), + (0x4EF, 'V'), + (0x4F0, 'M', u'ӱ'), + (0x4F1, 'V'), + (0x4F2, 'M', u'ӳ'), + (0x4F3, 'V'), + (0x4F4, 'M', u'ӵ'), + (0x4F5, 'V'), + (0x4F6, 'M', u'ӷ'), + (0x4F7, 'V'), + (0x4F8, 'M', u'ӹ'), + (0x4F9, 'V'), + (0x4FA, 'M', u'ӻ'), + (0x4FB, 'V'), + (0x4FC, 'M', u'ӽ'), + (0x4FD, 'V'), + (0x4FE, 'M', u'ӿ'), + (0x4FF, 'V'), + (0x500, 'M', u'ԁ'), + (0x501, 'V'), + (0x502, 'M', u'ԃ'), + ] + +def _seg_9(): + return [ + (0x503, 'V'), + (0x504, 'M', u'ԅ'), + (0x505, 'V'), + (0x506, 'M', u'ԇ'), + (0x507, 'V'), + (0x508, 'M', u'ԉ'), + (0x509, 'V'), + (0x50A, 'M', u'ԋ'), + (0x50B, 'V'), + (0x50C, 'M', u'ԍ'), + (0x50D, 'V'), + (0x50E, 'M', u'ԏ'), + (0x50F, 'V'), + (0x510, 'M', u'ԑ'), + (0x511, 'V'), + (0x512, 'M', u'ԓ'), + (0x513, 'V'), + (0x514, 'M', u'ԕ'), + (0x515, 'V'), + (0x516, 'M', u'ԗ'), + (0x517, 'V'), + (0x518, 'M', u'ԙ'), + (0x519, 'V'), + (0x51A, 'M', u'ԛ'), + (0x51B, 'V'), + (0x51C, 'M', u'ԝ'), + (0x51D, 'V'), + (0x51E, 'M', u'ԟ'), + (0x51F, 'V'), + (0x520, 'M', u'ԡ'), + (0x521, 'V'), + (0x522, 'M', u'ԣ'), + (0x523, 'V'), + (0x524, 'M', u'ԥ'), + (0x525, 'V'), + (0x526, 'M', u'ԧ'), + (0x527, 'V'), + (0x528, 'M', u'ԩ'), + (0x529, 'V'), + (0x52A, 'M', u'ԫ'), + (0x52B, 'V'), + (0x52C, 'M', u'ԭ'), + (0x52D, 'V'), + (0x52E, 'M', u'ԯ'), + (0x52F, 'V'), + (0x530, 'X'), + (0x531, 'M', u'ա'), + (0x532, 'M', u'բ'), + (0x533, 'M', u'գ'), + (0x534, 'M', u'դ'), + (0x535, 'M', u'ե'), + (0x536, 'M', u'զ'), + (0x537, 'M', u'է'), + (0x538, 'M', u'ը'), + (0x539, 'M', u'թ'), + (0x53A, 'M', u'ժ'), + (0x53B, 'M', u'ի'), + (0x53C, 'M', u'լ'), + (0x53D, 'M', u'խ'), + (0x53E, 'M', u'ծ'), + (0x53F, 'M', u'կ'), + (0x540, 'M', u'հ'), + (0x541, 'M', u'ձ'), + (0x542, 'M', u'ղ'), + (0x543, 'M', u'ճ'), + (0x544, 'M', u'մ'), + (0x545, 'M', u'յ'), + (0x546, 'M', u'ն'), + (0x547, 'M', u'շ'), + (0x548, 'M', u'ո'), + (0x549, 'M', u'չ'), + (0x54A, 'M', u'պ'), + (0x54B, 'M', u'ջ'), + (0x54C, 'M', u'ռ'), + (0x54D, 'M', u'ս'), + (0x54E, 'M', u'վ'), + (0x54F, 'M', u'տ'), + (0x550, 'M', u'ր'), + (0x551, 'M', u'ց'), + (0x552, 'M', u'ւ'), + (0x553, 'M', u'փ'), + (0x554, 'M', u'ք'), + (0x555, 'M', u'օ'), + (0x556, 'M', u'ֆ'), + (0x557, 'X'), + (0x559, 'V'), + (0x560, 'X'), + (0x561, 'V'), + (0x587, 'M', u'եւ'), + (0x588, 'X'), + (0x589, 'V'), + (0x58B, 'X'), + (0x58D, 'V'), + (0x590, 'X'), + (0x591, 'V'), + (0x5C8, 'X'), + (0x5D0, 'V'), + (0x5EB, 'X'), + (0x5F0, 'V'), + (0x5F5, 'X'), + ] + +def _seg_10(): + return [ + (0x606, 'V'), + (0x61C, 'X'), + (0x61E, 'V'), + (0x675, 'M', u'اٴ'), + (0x676, 'M', u'وٴ'), + (0x677, 'M', u'ۇٴ'), + (0x678, 'M', u'يٴ'), + (0x679, 'V'), + (0x6DD, 'X'), + (0x6DE, 'V'), + (0x70E, 'X'), + (0x710, 'V'), + (0x74B, 'X'), + (0x74D, 'V'), + (0x7B2, 'X'), + (0x7C0, 'V'), + (0x7FB, 'X'), + (0x800, 'V'), + (0x82E, 'X'), + (0x830, 'V'), + (0x83F, 'X'), + (0x840, 'V'), + (0x85C, 'X'), + (0x85E, 'V'), + (0x85F, 'X'), + (0x860, 'V'), + (0x86B, 'X'), + (0x8A0, 'V'), + (0x8B5, 'X'), + (0x8B6, 'V'), + (0x8BE, 'X'), + (0x8D4, 'V'), + (0x8E2, 'X'), + (0x8E3, 'V'), + (0x958, 'M', u'क़'), + (0x959, 'M', u'ख़'), + (0x95A, 'M', u'ग़'), + (0x95B, 'M', u'ज़'), + (0x95C, 'M', u'ड़'), + (0x95D, 'M', u'ढ़'), + (0x95E, 'M', u'फ़'), + (0x95F, 'M', u'य़'), + (0x960, 'V'), + (0x984, 'X'), + (0x985, 'V'), + (0x98D, 'X'), + (0x98F, 'V'), + (0x991, 'X'), + (0x993, 'V'), + (0x9A9, 'X'), + (0x9AA, 'V'), + (0x9B1, 'X'), + (0x9B2, 'V'), + (0x9B3, 'X'), + (0x9B6, 'V'), + (0x9BA, 'X'), + (0x9BC, 'V'), + (0x9C5, 'X'), + (0x9C7, 'V'), + (0x9C9, 'X'), + (0x9CB, 'V'), + (0x9CF, 'X'), + (0x9D7, 'V'), + (0x9D8, 'X'), + (0x9DC, 'M', u'ড়'), + (0x9DD, 'M', u'ঢ়'), + (0x9DE, 'X'), + (0x9DF, 'M', u'য়'), + (0x9E0, 'V'), + (0x9E4, 'X'), + (0x9E6, 'V'), + (0x9FE, 'X'), + (0xA01, 'V'), + (0xA04, 'X'), + (0xA05, 'V'), + (0xA0B, 'X'), + (0xA0F, 'V'), + (0xA11, 'X'), + (0xA13, 'V'), + (0xA29, 'X'), + (0xA2A, 'V'), + (0xA31, 'X'), + (0xA32, 'V'), + (0xA33, 'M', u'ਲ਼'), + (0xA34, 'X'), + (0xA35, 'V'), + (0xA36, 'M', u'ਸ਼'), + (0xA37, 'X'), + (0xA38, 'V'), + (0xA3A, 'X'), + (0xA3C, 'V'), + (0xA3D, 'X'), + (0xA3E, 'V'), + (0xA43, 'X'), + (0xA47, 'V'), + (0xA49, 'X'), + (0xA4B, 'V'), + (0xA4E, 'X'), + (0xA51, 'V'), + (0xA52, 'X'), + ] + +def _seg_11(): + return [ + (0xA59, 'M', u'ਖ਼'), + (0xA5A, 'M', u'ਗ਼'), + (0xA5B, 'M', u'ਜ਼'), + (0xA5C, 'V'), + (0xA5D, 'X'), + (0xA5E, 'M', u'ਫ਼'), + (0xA5F, 'X'), + (0xA66, 'V'), + (0xA76, 'X'), + (0xA81, 'V'), + (0xA84, 'X'), + (0xA85, 'V'), + (0xA8E, 'X'), + (0xA8F, 'V'), + (0xA92, 'X'), + (0xA93, 'V'), + (0xAA9, 'X'), + (0xAAA, 'V'), + (0xAB1, 'X'), + (0xAB2, 'V'), + (0xAB4, 'X'), + (0xAB5, 'V'), + (0xABA, 'X'), + (0xABC, 'V'), + (0xAC6, 'X'), + (0xAC7, 'V'), + (0xACA, 'X'), + (0xACB, 'V'), + (0xACE, 'X'), + (0xAD0, 'V'), + (0xAD1, 'X'), + (0xAE0, 'V'), + (0xAE4, 'X'), + (0xAE6, 'V'), + (0xAF2, 'X'), + (0xAF9, 'V'), + (0xB00, 'X'), + (0xB01, 'V'), + (0xB04, 'X'), + (0xB05, 'V'), + (0xB0D, 'X'), + (0xB0F, 'V'), + (0xB11, 'X'), + (0xB13, 'V'), + (0xB29, 'X'), + (0xB2A, 'V'), + (0xB31, 'X'), + (0xB32, 'V'), + (0xB34, 'X'), + (0xB35, 'V'), + (0xB3A, 'X'), + (0xB3C, 'V'), + (0xB45, 'X'), + (0xB47, 'V'), + (0xB49, 'X'), + (0xB4B, 'V'), + (0xB4E, 'X'), + (0xB56, 'V'), + (0xB58, 'X'), + (0xB5C, 'M', u'ଡ଼'), + (0xB5D, 'M', u'ଢ଼'), + (0xB5E, 'X'), + (0xB5F, 'V'), + (0xB64, 'X'), + (0xB66, 'V'), + (0xB78, 'X'), + (0xB82, 'V'), + (0xB84, 'X'), + (0xB85, 'V'), + (0xB8B, 'X'), + (0xB8E, 'V'), + (0xB91, 'X'), + (0xB92, 'V'), + (0xB96, 'X'), + (0xB99, 'V'), + (0xB9B, 'X'), + (0xB9C, 'V'), + (0xB9D, 'X'), + (0xB9E, 'V'), + (0xBA0, 'X'), + (0xBA3, 'V'), + (0xBA5, 'X'), + (0xBA8, 'V'), + (0xBAB, 'X'), + (0xBAE, 'V'), + (0xBBA, 'X'), + (0xBBE, 'V'), + (0xBC3, 'X'), + (0xBC6, 'V'), + (0xBC9, 'X'), + (0xBCA, 'V'), + (0xBCE, 'X'), + (0xBD0, 'V'), + (0xBD1, 'X'), + (0xBD7, 'V'), + (0xBD8, 'X'), + (0xBE6, 'V'), + (0xBFB, 'X'), + (0xC00, 'V'), + (0xC04, 'X'), + ] + +def _seg_12(): + return [ + (0xC05, 'V'), + (0xC0D, 'X'), + (0xC0E, 'V'), + (0xC11, 'X'), + (0xC12, 'V'), + (0xC29, 'X'), + (0xC2A, 'V'), + (0xC3A, 'X'), + (0xC3D, 'V'), + (0xC45, 'X'), + (0xC46, 'V'), + (0xC49, 'X'), + (0xC4A, 'V'), + (0xC4E, 'X'), + (0xC55, 'V'), + (0xC57, 'X'), + (0xC58, 'V'), + (0xC5B, 'X'), + (0xC60, 'V'), + (0xC64, 'X'), + (0xC66, 'V'), + (0xC70, 'X'), + (0xC78, 'V'), + (0xC84, 'X'), + (0xC85, 'V'), + (0xC8D, 'X'), + (0xC8E, 'V'), + (0xC91, 'X'), + (0xC92, 'V'), + (0xCA9, 'X'), + (0xCAA, 'V'), + (0xCB4, 'X'), + (0xCB5, 'V'), + (0xCBA, 'X'), + (0xCBC, 'V'), + (0xCC5, 'X'), + (0xCC6, 'V'), + (0xCC9, 'X'), + (0xCCA, 'V'), + (0xCCE, 'X'), + (0xCD5, 'V'), + (0xCD7, 'X'), + (0xCDE, 'V'), + (0xCDF, 'X'), + (0xCE0, 'V'), + (0xCE4, 'X'), + (0xCE6, 'V'), + (0xCF0, 'X'), + (0xCF1, 'V'), + (0xCF3, 'X'), + (0xD00, 'V'), + (0xD04, 'X'), + (0xD05, 'V'), + (0xD0D, 'X'), + (0xD0E, 'V'), + (0xD11, 'X'), + (0xD12, 'V'), + (0xD45, 'X'), + (0xD46, 'V'), + (0xD49, 'X'), + (0xD4A, 'V'), + (0xD50, 'X'), + (0xD54, 'V'), + (0xD64, 'X'), + (0xD66, 'V'), + (0xD80, 'X'), + (0xD82, 'V'), + (0xD84, 'X'), + (0xD85, 'V'), + (0xD97, 'X'), + (0xD9A, 'V'), + (0xDB2, 'X'), + (0xDB3, 'V'), + (0xDBC, 'X'), + (0xDBD, 'V'), + (0xDBE, 'X'), + (0xDC0, 'V'), + (0xDC7, 'X'), + (0xDCA, 'V'), + (0xDCB, 'X'), + (0xDCF, 'V'), + (0xDD5, 'X'), + (0xDD6, 'V'), + (0xDD7, 'X'), + (0xDD8, 'V'), + (0xDE0, 'X'), + (0xDE6, 'V'), + (0xDF0, 'X'), + (0xDF2, 'V'), + (0xDF5, 'X'), + (0xE01, 'V'), + (0xE33, 'M', u'ํา'), + (0xE34, 'V'), + (0xE3B, 'X'), + (0xE3F, 'V'), + (0xE5C, 'X'), + (0xE81, 'V'), + (0xE83, 'X'), + (0xE84, 'V'), + (0xE85, 'X'), + ] + +def _seg_13(): + return [ + (0xE87, 'V'), + (0xE89, 'X'), + (0xE8A, 'V'), + (0xE8B, 'X'), + (0xE8D, 'V'), + (0xE8E, 'X'), + (0xE94, 'V'), + (0xE98, 'X'), + (0xE99, 'V'), + (0xEA0, 'X'), + (0xEA1, 'V'), + (0xEA4, 'X'), + (0xEA5, 'V'), + (0xEA6, 'X'), + (0xEA7, 'V'), + (0xEA8, 'X'), + (0xEAA, 'V'), + (0xEAC, 'X'), + (0xEAD, 'V'), + (0xEB3, 'M', u'ໍາ'), + (0xEB4, 'V'), + (0xEBA, 'X'), + (0xEBB, 'V'), + (0xEBE, 'X'), + (0xEC0, 'V'), + (0xEC5, 'X'), + (0xEC6, 'V'), + (0xEC7, 'X'), + (0xEC8, 'V'), + (0xECE, 'X'), + (0xED0, 'V'), + (0xEDA, 'X'), + (0xEDC, 'M', u'ຫນ'), + (0xEDD, 'M', u'ຫມ'), + (0xEDE, 'V'), + (0xEE0, 'X'), + (0xF00, 'V'), + (0xF0C, 'M', u'་'), + (0xF0D, 'V'), + (0xF43, 'M', u'གྷ'), + (0xF44, 'V'), + (0xF48, 'X'), + (0xF49, 'V'), + (0xF4D, 'M', u'ཌྷ'), + (0xF4E, 'V'), + (0xF52, 'M', u'དྷ'), + (0xF53, 'V'), + (0xF57, 'M', u'བྷ'), + (0xF58, 'V'), + (0xF5C, 'M', u'ཛྷ'), + (0xF5D, 'V'), + (0xF69, 'M', u'ཀྵ'), + (0xF6A, 'V'), + (0xF6D, 'X'), + (0xF71, 'V'), + (0xF73, 'M', u'ཱི'), + (0xF74, 'V'), + (0xF75, 'M', u'ཱུ'), + (0xF76, 'M', u'ྲྀ'), + (0xF77, 'M', u'ྲཱྀ'), + (0xF78, 'M', u'ླྀ'), + (0xF79, 'M', u'ླཱྀ'), + (0xF7A, 'V'), + (0xF81, 'M', u'ཱྀ'), + (0xF82, 'V'), + (0xF93, 'M', u'ྒྷ'), + (0xF94, 'V'), + (0xF98, 'X'), + (0xF99, 'V'), + (0xF9D, 'M', u'ྜྷ'), + (0xF9E, 'V'), + (0xFA2, 'M', u'ྡྷ'), + (0xFA3, 'V'), + (0xFA7, 'M', u'ྦྷ'), + (0xFA8, 'V'), + (0xFAC, 'M', u'ྫྷ'), + (0xFAD, 'V'), + (0xFB9, 'M', u'ྐྵ'), + (0xFBA, 'V'), + (0xFBD, 'X'), + (0xFBE, 'V'), + (0xFCD, 'X'), + (0xFCE, 'V'), + (0xFDB, 'X'), + (0x1000, 'V'), + (0x10A0, 'X'), + (0x10C7, 'M', u'ⴧ'), + (0x10C8, 'X'), + (0x10CD, 'M', u'ⴭ'), + (0x10CE, 'X'), + (0x10D0, 'V'), + (0x10FC, 'M', u'ნ'), + (0x10FD, 'V'), + (0x115F, 'X'), + (0x1161, 'V'), + (0x1249, 'X'), + (0x124A, 'V'), + (0x124E, 'X'), + (0x1250, 'V'), + (0x1257, 'X'), + ] + +def _seg_14(): + return [ + (0x1258, 'V'), + (0x1259, 'X'), + (0x125A, 'V'), + (0x125E, 'X'), + (0x1260, 'V'), + (0x1289, 'X'), + (0x128A, 'V'), + (0x128E, 'X'), + (0x1290, 'V'), + (0x12B1, 'X'), + (0x12B2, 'V'), + (0x12B6, 'X'), + (0x12B8, 'V'), + (0x12BF, 'X'), + (0x12C0, 'V'), + (0x12C1, 'X'), + (0x12C2, 'V'), + (0x12C6, 'X'), + (0x12C8, 'V'), + (0x12D7, 'X'), + (0x12D8, 'V'), + (0x1311, 'X'), + (0x1312, 'V'), + (0x1316, 'X'), + (0x1318, 'V'), + (0x135B, 'X'), + (0x135D, 'V'), + (0x137D, 'X'), + (0x1380, 'V'), + (0x139A, 'X'), + (0x13A0, 'V'), + (0x13F6, 'X'), + (0x13F8, 'M', u'Ᏸ'), + (0x13F9, 'M', u'Ᏹ'), + (0x13FA, 'M', u'Ᏺ'), + (0x13FB, 'M', u'Ᏻ'), + (0x13FC, 'M', u'Ᏼ'), + (0x13FD, 'M', u'Ᏽ'), + (0x13FE, 'X'), + (0x1400, 'V'), + (0x1680, 'X'), + (0x1681, 'V'), + (0x169D, 'X'), + (0x16A0, 'V'), + (0x16F9, 'X'), + (0x1700, 'V'), + (0x170D, 'X'), + (0x170E, 'V'), + (0x1715, 'X'), + (0x1720, 'V'), + (0x1737, 'X'), + (0x1740, 'V'), + (0x1754, 'X'), + (0x1760, 'V'), + (0x176D, 'X'), + (0x176E, 'V'), + (0x1771, 'X'), + (0x1772, 'V'), + (0x1774, 'X'), + (0x1780, 'V'), + (0x17B4, 'X'), + (0x17B6, 'V'), + (0x17DE, 'X'), + (0x17E0, 'V'), + (0x17EA, 'X'), + (0x17F0, 'V'), + (0x17FA, 'X'), + (0x1800, 'V'), + (0x1806, 'X'), + (0x1807, 'V'), + (0x180B, 'I'), + (0x180E, 'X'), + (0x1810, 'V'), + (0x181A, 'X'), + (0x1820, 'V'), + (0x1878, 'X'), + (0x1880, 'V'), + (0x18AB, 'X'), + (0x18B0, 'V'), + (0x18F6, 'X'), + (0x1900, 'V'), + (0x191F, 'X'), + (0x1920, 'V'), + (0x192C, 'X'), + (0x1930, 'V'), + (0x193C, 'X'), + (0x1940, 'V'), + (0x1941, 'X'), + (0x1944, 'V'), + (0x196E, 'X'), + (0x1970, 'V'), + (0x1975, 'X'), + (0x1980, 'V'), + (0x19AC, 'X'), + (0x19B0, 'V'), + (0x19CA, 'X'), + (0x19D0, 'V'), + (0x19DB, 'X'), + (0x19DE, 'V'), + (0x1A1C, 'X'), + ] + +def _seg_15(): + return [ + (0x1A1E, 'V'), + (0x1A5F, 'X'), + (0x1A60, 'V'), + (0x1A7D, 'X'), + (0x1A7F, 'V'), + (0x1A8A, 'X'), + (0x1A90, 'V'), + (0x1A9A, 'X'), + (0x1AA0, 'V'), + (0x1AAE, 'X'), + (0x1AB0, 'V'), + (0x1ABF, 'X'), + (0x1B00, 'V'), + (0x1B4C, 'X'), + (0x1B50, 'V'), + (0x1B7D, 'X'), + (0x1B80, 'V'), + (0x1BF4, 'X'), + (0x1BFC, 'V'), + (0x1C38, 'X'), + (0x1C3B, 'V'), + (0x1C4A, 'X'), + (0x1C4D, 'V'), + (0x1C80, 'M', u'в'), + (0x1C81, 'M', u'д'), + (0x1C82, 'M', u'о'), + (0x1C83, 'M', u'с'), + (0x1C84, 'M', u'т'), + (0x1C86, 'M', u'ъ'), + (0x1C87, 'M', u'ѣ'), + (0x1C88, 'M', u'ꙋ'), + (0x1C89, 'X'), + (0x1CC0, 'V'), + (0x1CC8, 'X'), + (0x1CD0, 'V'), + (0x1CFA, 'X'), + (0x1D00, 'V'), + (0x1D2C, 'M', u'a'), + (0x1D2D, 'M', u'æ'), + (0x1D2E, 'M', u'b'), + (0x1D2F, 'V'), + (0x1D30, 'M', u'd'), + (0x1D31, 'M', u'e'), + (0x1D32, 'M', u'ǝ'), + (0x1D33, 'M', u'g'), + (0x1D34, 'M', u'h'), + (0x1D35, 'M', u'i'), + (0x1D36, 'M', u'j'), + (0x1D37, 'M', u'k'), + (0x1D38, 'M', u'l'), + (0x1D39, 'M', u'm'), + (0x1D3A, 'M', u'n'), + (0x1D3B, 'V'), + (0x1D3C, 'M', u'o'), + (0x1D3D, 'M', u'ȣ'), + (0x1D3E, 'M', u'p'), + (0x1D3F, 'M', u'r'), + (0x1D40, 'M', u't'), + (0x1D41, 'M', u'u'), + (0x1D42, 'M', u'w'), + (0x1D43, 'M', u'a'), + (0x1D44, 'M', u'ɐ'), + (0x1D45, 'M', u'ɑ'), + (0x1D46, 'M', u'ᴂ'), + (0x1D47, 'M', u'b'), + (0x1D48, 'M', u'd'), + (0x1D49, 'M', u'e'), + (0x1D4A, 'M', u'ə'), + (0x1D4B, 'M', u'ɛ'), + (0x1D4C, 'M', u'ɜ'), + (0x1D4D, 'M', u'g'), + (0x1D4E, 'V'), + (0x1D4F, 'M', u'k'), + (0x1D50, 'M', u'm'), + (0x1D51, 'M', u'ŋ'), + (0x1D52, 'M', u'o'), + (0x1D53, 'M', u'ɔ'), + (0x1D54, 'M', u'ᴖ'), + (0x1D55, 'M', u'ᴗ'), + (0x1D56, 'M', u'p'), + (0x1D57, 'M', u't'), + (0x1D58, 'M', u'u'), + (0x1D59, 'M', u'ᴝ'), + (0x1D5A, 'M', u'ɯ'), + (0x1D5B, 'M', u'v'), + (0x1D5C, 'M', u'ᴥ'), + (0x1D5D, 'M', u'β'), + (0x1D5E, 'M', u'γ'), + (0x1D5F, 'M', u'δ'), + (0x1D60, 'M', u'φ'), + (0x1D61, 'M', u'χ'), + (0x1D62, 'M', u'i'), + (0x1D63, 'M', u'r'), + (0x1D64, 'M', u'u'), + (0x1D65, 'M', u'v'), + (0x1D66, 'M', u'β'), + (0x1D67, 'M', u'γ'), + (0x1D68, 'M', u'ρ'), + (0x1D69, 'M', u'φ'), + (0x1D6A, 'M', u'χ'), + ] + +def _seg_16(): + return [ + (0x1D6B, 'V'), + (0x1D78, 'M', u'н'), + (0x1D79, 'V'), + (0x1D9B, 'M', u'ɒ'), + (0x1D9C, 'M', u'c'), + (0x1D9D, 'M', u'ɕ'), + (0x1D9E, 'M', u'ð'), + (0x1D9F, 'M', u'ɜ'), + (0x1DA0, 'M', u'f'), + (0x1DA1, 'M', u'ɟ'), + (0x1DA2, 'M', u'ɡ'), + (0x1DA3, 'M', u'ɥ'), + (0x1DA4, 'M', u'ɨ'), + (0x1DA5, 'M', u'ɩ'), + (0x1DA6, 'M', u'ɪ'), + (0x1DA7, 'M', u'ᵻ'), + (0x1DA8, 'M', u'ʝ'), + (0x1DA9, 'M', u'ɭ'), + (0x1DAA, 'M', u'ᶅ'), + (0x1DAB, 'M', u'ʟ'), + (0x1DAC, 'M', u'ɱ'), + (0x1DAD, 'M', u'ɰ'), + (0x1DAE, 'M', u'ɲ'), + (0x1DAF, 'M', u'ɳ'), + (0x1DB0, 'M', u'ɴ'), + (0x1DB1, 'M', u'ɵ'), + (0x1DB2, 'M', u'ɸ'), + (0x1DB3, 'M', u'ʂ'), + (0x1DB4, 'M', u'ʃ'), + (0x1DB5, 'M', u'ƫ'), + (0x1DB6, 'M', u'ʉ'), + (0x1DB7, 'M', u'ʊ'), + (0x1DB8, 'M', u'ᴜ'), + (0x1DB9, 'M', u'ʋ'), + (0x1DBA, 'M', u'ʌ'), + (0x1DBB, 'M', u'z'), + (0x1DBC, 'M', u'ʐ'), + (0x1DBD, 'M', u'ʑ'), + (0x1DBE, 'M', u'ʒ'), + (0x1DBF, 'M', u'θ'), + (0x1DC0, 'V'), + (0x1DFA, 'X'), + (0x1DFB, 'V'), + (0x1E00, 'M', u'ḁ'), + (0x1E01, 'V'), + (0x1E02, 'M', u'ḃ'), + (0x1E03, 'V'), + (0x1E04, 'M', u'ḅ'), + (0x1E05, 'V'), + (0x1E06, 'M', u'ḇ'), + (0x1E07, 'V'), + (0x1E08, 'M', u'ḉ'), + (0x1E09, 'V'), + (0x1E0A, 'M', u'ḋ'), + (0x1E0B, 'V'), + (0x1E0C, 'M', u'ḍ'), + (0x1E0D, 'V'), + (0x1E0E, 'M', u'ḏ'), + (0x1E0F, 'V'), + (0x1E10, 'M', u'ḑ'), + (0x1E11, 'V'), + (0x1E12, 'M', u'ḓ'), + (0x1E13, 'V'), + (0x1E14, 'M', u'ḕ'), + (0x1E15, 'V'), + (0x1E16, 'M', u'ḗ'), + (0x1E17, 'V'), + (0x1E18, 'M', u'ḙ'), + (0x1E19, 'V'), + (0x1E1A, 'M', u'ḛ'), + (0x1E1B, 'V'), + (0x1E1C, 'M', u'ḝ'), + (0x1E1D, 'V'), + (0x1E1E, 'M', u'ḟ'), + (0x1E1F, 'V'), + (0x1E20, 'M', u'ḡ'), + (0x1E21, 'V'), + (0x1E22, 'M', u'ḣ'), + (0x1E23, 'V'), + (0x1E24, 'M', u'ḥ'), + (0x1E25, 'V'), + (0x1E26, 'M', u'ḧ'), + (0x1E27, 'V'), + (0x1E28, 'M', u'ḩ'), + (0x1E29, 'V'), + (0x1E2A, 'M', u'ḫ'), + (0x1E2B, 'V'), + (0x1E2C, 'M', u'ḭ'), + (0x1E2D, 'V'), + (0x1E2E, 'M', u'ḯ'), + (0x1E2F, 'V'), + (0x1E30, 'M', u'ḱ'), + (0x1E31, 'V'), + (0x1E32, 'M', u'ḳ'), + (0x1E33, 'V'), + (0x1E34, 'M', u'ḵ'), + (0x1E35, 'V'), + (0x1E36, 'M', u'ḷ'), + (0x1E37, 'V'), + (0x1E38, 'M', u'ḹ'), + ] + +def _seg_17(): + return [ + (0x1E39, 'V'), + (0x1E3A, 'M', u'ḻ'), + (0x1E3B, 'V'), + (0x1E3C, 'M', u'ḽ'), + (0x1E3D, 'V'), + (0x1E3E, 'M', u'ḿ'), + (0x1E3F, 'V'), + (0x1E40, 'M', u'ṁ'), + (0x1E41, 'V'), + (0x1E42, 'M', u'ṃ'), + (0x1E43, 'V'), + (0x1E44, 'M', u'ṅ'), + (0x1E45, 'V'), + (0x1E46, 'M', u'ṇ'), + (0x1E47, 'V'), + (0x1E48, 'M', u'ṉ'), + (0x1E49, 'V'), + (0x1E4A, 'M', u'ṋ'), + (0x1E4B, 'V'), + (0x1E4C, 'M', u'ṍ'), + (0x1E4D, 'V'), + (0x1E4E, 'M', u'ṏ'), + (0x1E4F, 'V'), + (0x1E50, 'M', u'ṑ'), + (0x1E51, 'V'), + (0x1E52, 'M', u'ṓ'), + (0x1E53, 'V'), + (0x1E54, 'M', u'ṕ'), + (0x1E55, 'V'), + (0x1E56, 'M', u'ṗ'), + (0x1E57, 'V'), + (0x1E58, 'M', u'ṙ'), + (0x1E59, 'V'), + (0x1E5A, 'M', u'ṛ'), + (0x1E5B, 'V'), + (0x1E5C, 'M', u'ṝ'), + (0x1E5D, 'V'), + (0x1E5E, 'M', u'ṟ'), + (0x1E5F, 'V'), + (0x1E60, 'M', u'ṡ'), + (0x1E61, 'V'), + (0x1E62, 'M', u'ṣ'), + (0x1E63, 'V'), + (0x1E64, 'M', u'ṥ'), + (0x1E65, 'V'), + (0x1E66, 'M', u'ṧ'), + (0x1E67, 'V'), + (0x1E68, 'M', u'ṩ'), + (0x1E69, 'V'), + (0x1E6A, 'M', u'ṫ'), + (0x1E6B, 'V'), + (0x1E6C, 'M', u'ṭ'), + (0x1E6D, 'V'), + (0x1E6E, 'M', u'ṯ'), + (0x1E6F, 'V'), + (0x1E70, 'M', u'ṱ'), + (0x1E71, 'V'), + (0x1E72, 'M', u'ṳ'), + (0x1E73, 'V'), + (0x1E74, 'M', u'ṵ'), + (0x1E75, 'V'), + (0x1E76, 'M', u'ṷ'), + (0x1E77, 'V'), + (0x1E78, 'M', u'ṹ'), + (0x1E79, 'V'), + (0x1E7A, 'M', u'ṻ'), + (0x1E7B, 'V'), + (0x1E7C, 'M', u'ṽ'), + (0x1E7D, 'V'), + (0x1E7E, 'M', u'ṿ'), + (0x1E7F, 'V'), + (0x1E80, 'M', u'ẁ'), + (0x1E81, 'V'), + (0x1E82, 'M', u'ẃ'), + (0x1E83, 'V'), + (0x1E84, 'M', u'ẅ'), + (0x1E85, 'V'), + (0x1E86, 'M', u'ẇ'), + (0x1E87, 'V'), + (0x1E88, 'M', u'ẉ'), + (0x1E89, 'V'), + (0x1E8A, 'M', u'ẋ'), + (0x1E8B, 'V'), + (0x1E8C, 'M', u'ẍ'), + (0x1E8D, 'V'), + (0x1E8E, 'M', u'ẏ'), + (0x1E8F, 'V'), + (0x1E90, 'M', u'ẑ'), + (0x1E91, 'V'), + (0x1E92, 'M', u'ẓ'), + (0x1E93, 'V'), + (0x1E94, 'M', u'ẕ'), + (0x1E95, 'V'), + (0x1E9A, 'M', u'aʾ'), + (0x1E9B, 'M', u'ṡ'), + (0x1E9C, 'V'), + (0x1E9E, 'M', u'ss'), + (0x1E9F, 'V'), + (0x1EA0, 'M', u'ạ'), + (0x1EA1, 'V'), + ] + +def _seg_18(): + return [ + (0x1EA2, 'M', u'ả'), + (0x1EA3, 'V'), + (0x1EA4, 'M', u'ấ'), + (0x1EA5, 'V'), + (0x1EA6, 'M', u'ầ'), + (0x1EA7, 'V'), + (0x1EA8, 'M', u'ẩ'), + (0x1EA9, 'V'), + (0x1EAA, 'M', u'ẫ'), + (0x1EAB, 'V'), + (0x1EAC, 'M', u'ậ'), + (0x1EAD, 'V'), + (0x1EAE, 'M', u'ắ'), + (0x1EAF, 'V'), + (0x1EB0, 'M', u'ằ'), + (0x1EB1, 'V'), + (0x1EB2, 'M', u'ẳ'), + (0x1EB3, 'V'), + (0x1EB4, 'M', u'ẵ'), + (0x1EB5, 'V'), + (0x1EB6, 'M', u'ặ'), + (0x1EB7, 'V'), + (0x1EB8, 'M', u'ẹ'), + (0x1EB9, 'V'), + (0x1EBA, 'M', u'ẻ'), + (0x1EBB, 'V'), + (0x1EBC, 'M', u'ẽ'), + (0x1EBD, 'V'), + (0x1EBE, 'M', u'ế'), + (0x1EBF, 'V'), + (0x1EC0, 'M', u'ề'), + (0x1EC1, 'V'), + (0x1EC2, 'M', u'ể'), + (0x1EC3, 'V'), + (0x1EC4, 'M', u'ễ'), + (0x1EC5, 'V'), + (0x1EC6, 'M', u'ệ'), + (0x1EC7, 'V'), + (0x1EC8, 'M', u'ỉ'), + (0x1EC9, 'V'), + (0x1ECA, 'M', u'ị'), + (0x1ECB, 'V'), + (0x1ECC, 'M', u'ọ'), + (0x1ECD, 'V'), + (0x1ECE, 'M', u'ỏ'), + (0x1ECF, 'V'), + (0x1ED0, 'M', u'ố'), + (0x1ED1, 'V'), + (0x1ED2, 'M', u'ồ'), + (0x1ED3, 'V'), + (0x1ED4, 'M', u'ổ'), + (0x1ED5, 'V'), + (0x1ED6, 'M', u'ỗ'), + (0x1ED7, 'V'), + (0x1ED8, 'M', u'ộ'), + (0x1ED9, 'V'), + (0x1EDA, 'M', u'ớ'), + (0x1EDB, 'V'), + (0x1EDC, 'M', u'ờ'), + (0x1EDD, 'V'), + (0x1EDE, 'M', u'ở'), + (0x1EDF, 'V'), + (0x1EE0, 'M', u'ỡ'), + (0x1EE1, 'V'), + (0x1EE2, 'M', u'ợ'), + (0x1EE3, 'V'), + (0x1EE4, 'M', u'ụ'), + (0x1EE5, 'V'), + (0x1EE6, 'M', u'ủ'), + (0x1EE7, 'V'), + (0x1EE8, 'M', u'ứ'), + (0x1EE9, 'V'), + (0x1EEA, 'M', u'ừ'), + (0x1EEB, 'V'), + (0x1EEC, 'M', u'ử'), + (0x1EED, 'V'), + (0x1EEE, 'M', u'ữ'), + (0x1EEF, 'V'), + (0x1EF0, 'M', u'ự'), + (0x1EF1, 'V'), + (0x1EF2, 'M', u'ỳ'), + (0x1EF3, 'V'), + (0x1EF4, 'M', u'ỵ'), + (0x1EF5, 'V'), + (0x1EF6, 'M', u'ỷ'), + (0x1EF7, 'V'), + (0x1EF8, 'M', u'ỹ'), + (0x1EF9, 'V'), + (0x1EFA, 'M', u'ỻ'), + (0x1EFB, 'V'), + (0x1EFC, 'M', u'ỽ'), + (0x1EFD, 'V'), + (0x1EFE, 'M', u'ỿ'), + (0x1EFF, 'V'), + (0x1F08, 'M', u'ἀ'), + (0x1F09, 'M', u'ἁ'), + (0x1F0A, 'M', u'ἂ'), + (0x1F0B, 'M', u'ἃ'), + (0x1F0C, 'M', u'ἄ'), + (0x1F0D, 'M', u'ἅ'), + ] + +def _seg_19(): + return [ + (0x1F0E, 'M', u'ἆ'), + (0x1F0F, 'M', u'ἇ'), + (0x1F10, 'V'), + (0x1F16, 'X'), + (0x1F18, 'M', u'ἐ'), + (0x1F19, 'M', u'ἑ'), + (0x1F1A, 'M', u'ἒ'), + (0x1F1B, 'M', u'ἓ'), + (0x1F1C, 'M', u'ἔ'), + (0x1F1D, 'M', u'ἕ'), + (0x1F1E, 'X'), + (0x1F20, 'V'), + (0x1F28, 'M', u'ἠ'), + (0x1F29, 'M', u'ἡ'), + (0x1F2A, 'M', u'ἢ'), + (0x1F2B, 'M', u'ἣ'), + (0x1F2C, 'M', u'ἤ'), + (0x1F2D, 'M', u'ἥ'), + (0x1F2E, 'M', u'ἦ'), + (0x1F2F, 'M', u'ἧ'), + (0x1F30, 'V'), + (0x1F38, 'M', u'ἰ'), + (0x1F39, 'M', u'ἱ'), + (0x1F3A, 'M', u'ἲ'), + (0x1F3B, 'M', u'ἳ'), + (0x1F3C, 'M', u'ἴ'), + (0x1F3D, 'M', u'ἵ'), + (0x1F3E, 'M', u'ἶ'), + (0x1F3F, 'M', u'ἷ'), + (0x1F40, 'V'), + (0x1F46, 'X'), + (0x1F48, 'M', u'ὀ'), + (0x1F49, 'M', u'ὁ'), + (0x1F4A, 'M', u'ὂ'), + (0x1F4B, 'M', u'ὃ'), + (0x1F4C, 'M', u'ὄ'), + (0x1F4D, 'M', u'ὅ'), + (0x1F4E, 'X'), + (0x1F50, 'V'), + (0x1F58, 'X'), + (0x1F59, 'M', u'ὑ'), + (0x1F5A, 'X'), + (0x1F5B, 'M', u'ὓ'), + (0x1F5C, 'X'), + (0x1F5D, 'M', u'ὕ'), + (0x1F5E, 'X'), + (0x1F5F, 'M', u'ὗ'), + (0x1F60, 'V'), + (0x1F68, 'M', u'ὠ'), + (0x1F69, 'M', u'ὡ'), + (0x1F6A, 'M', u'ὢ'), + (0x1F6B, 'M', u'ὣ'), + (0x1F6C, 'M', u'ὤ'), + (0x1F6D, 'M', u'ὥ'), + (0x1F6E, 'M', u'ὦ'), + (0x1F6F, 'M', u'ὧ'), + (0x1F70, 'V'), + (0x1F71, 'M', u'ά'), + (0x1F72, 'V'), + (0x1F73, 'M', u'έ'), + (0x1F74, 'V'), + (0x1F75, 'M', u'ή'), + (0x1F76, 'V'), + (0x1F77, 'M', u'ί'), + (0x1F78, 'V'), + (0x1F79, 'M', u'ό'), + (0x1F7A, 'V'), + (0x1F7B, 'M', u'ύ'), + (0x1F7C, 'V'), + (0x1F7D, 'M', u'ώ'), + (0x1F7E, 'X'), + (0x1F80, 'M', u'ἀι'), + (0x1F81, 'M', u'ἁι'), + (0x1F82, 'M', u'ἂι'), + (0x1F83, 'M', u'ἃι'), + (0x1F84, 'M', u'ἄι'), + (0x1F85, 'M', u'ἅι'), + (0x1F86, 'M', u'ἆι'), + (0x1F87, 'M', u'ἇι'), + (0x1F88, 'M', u'ἀι'), + (0x1F89, 'M', u'ἁι'), + (0x1F8A, 'M', u'ἂι'), + (0x1F8B, 'M', u'ἃι'), + (0x1F8C, 'M', u'ἄι'), + (0x1F8D, 'M', u'ἅι'), + (0x1F8E, 'M', u'ἆι'), + (0x1F8F, 'M', u'ἇι'), + (0x1F90, 'M', u'ἠι'), + (0x1F91, 'M', u'ἡι'), + (0x1F92, 'M', u'ἢι'), + (0x1F93, 'M', u'ἣι'), + (0x1F94, 'M', u'ἤι'), + (0x1F95, 'M', u'ἥι'), + (0x1F96, 'M', u'ἦι'), + (0x1F97, 'M', u'ἧι'), + (0x1F98, 'M', u'ἠι'), + (0x1F99, 'M', u'ἡι'), + (0x1F9A, 'M', u'ἢι'), + (0x1F9B, 'M', u'ἣι'), + (0x1F9C, 'M', u'ἤι'), + ] + +def _seg_20(): + return [ + (0x1F9D, 'M', u'ἥι'), + (0x1F9E, 'M', u'ἦι'), + (0x1F9F, 'M', u'ἧι'), + (0x1FA0, 'M', u'ὠι'), + (0x1FA1, 'M', u'ὡι'), + (0x1FA2, 'M', u'ὢι'), + (0x1FA3, 'M', u'ὣι'), + (0x1FA4, 'M', u'ὤι'), + (0x1FA5, 'M', u'ὥι'), + (0x1FA6, 'M', u'ὦι'), + (0x1FA7, 'M', u'ὧι'), + (0x1FA8, 'M', u'ὠι'), + (0x1FA9, 'M', u'ὡι'), + (0x1FAA, 'M', u'ὢι'), + (0x1FAB, 'M', u'ὣι'), + (0x1FAC, 'M', u'ὤι'), + (0x1FAD, 'M', u'ὥι'), + (0x1FAE, 'M', u'ὦι'), + (0x1FAF, 'M', u'ὧι'), + (0x1FB0, 'V'), + (0x1FB2, 'M', u'ὰι'), + (0x1FB3, 'M', u'αι'), + (0x1FB4, 'M', u'άι'), + (0x1FB5, 'X'), + (0x1FB6, 'V'), + (0x1FB7, 'M', u'ᾶι'), + (0x1FB8, 'M', u'ᾰ'), + (0x1FB9, 'M', u'ᾱ'), + (0x1FBA, 'M', u'ὰ'), + (0x1FBB, 'M', u'ά'), + (0x1FBC, 'M', u'αι'), + (0x1FBD, '3', u' ̓'), + (0x1FBE, 'M', u'ι'), + (0x1FBF, '3', u' ̓'), + (0x1FC0, '3', u' ͂'), + (0x1FC1, '3', u' ̈͂'), + (0x1FC2, 'M', u'ὴι'), + (0x1FC3, 'M', u'ηι'), + (0x1FC4, 'M', u'ήι'), + (0x1FC5, 'X'), + (0x1FC6, 'V'), + (0x1FC7, 'M', u'ῆι'), + (0x1FC8, 'M', u'ὲ'), + (0x1FC9, 'M', u'έ'), + (0x1FCA, 'M', u'ὴ'), + (0x1FCB, 'M', u'ή'), + (0x1FCC, 'M', u'ηι'), + (0x1FCD, '3', u' ̓̀'), + (0x1FCE, '3', u' ̓́'), + (0x1FCF, '3', u' ̓͂'), + (0x1FD0, 'V'), + (0x1FD3, 'M', u'ΐ'), + (0x1FD4, 'X'), + (0x1FD6, 'V'), + (0x1FD8, 'M', u'ῐ'), + (0x1FD9, 'M', u'ῑ'), + (0x1FDA, 'M', u'ὶ'), + (0x1FDB, 'M', u'ί'), + (0x1FDC, 'X'), + (0x1FDD, '3', u' ̔̀'), + (0x1FDE, '3', u' ̔́'), + (0x1FDF, '3', u' ̔͂'), + (0x1FE0, 'V'), + (0x1FE3, 'M', u'ΰ'), + (0x1FE4, 'V'), + (0x1FE8, 'M', u'ῠ'), + (0x1FE9, 'M', u'ῡ'), + (0x1FEA, 'M', u'ὺ'), + (0x1FEB, 'M', u'ύ'), + (0x1FEC, 'M', u'ῥ'), + (0x1FED, '3', u' ̈̀'), + (0x1FEE, '3', u' ̈́'), + (0x1FEF, '3', u'`'), + (0x1FF0, 'X'), + (0x1FF2, 'M', u'ὼι'), + (0x1FF3, 'M', u'ωι'), + (0x1FF4, 'M', u'ώι'), + (0x1FF5, 'X'), + (0x1FF6, 'V'), + (0x1FF7, 'M', u'ῶι'), + (0x1FF8, 'M', u'ὸ'), + (0x1FF9, 'M', u'ό'), + (0x1FFA, 'M', u'ὼ'), + (0x1FFB, 'M', u'ώ'), + (0x1FFC, 'M', u'ωι'), + (0x1FFD, '3', u' ́'), + (0x1FFE, '3', u' ̔'), + (0x1FFF, 'X'), + (0x2000, '3', u' '), + (0x200B, 'I'), + (0x200C, 'D', u''), + (0x200E, 'X'), + (0x2010, 'V'), + (0x2011, 'M', u'‐'), + (0x2012, 'V'), + (0x2017, '3', u' ̳'), + (0x2018, 'V'), + (0x2024, 'X'), + (0x2027, 'V'), + (0x2028, 'X'), + ] + +def _seg_21(): + return [ + (0x202F, '3', u' '), + (0x2030, 'V'), + (0x2033, 'M', u'′′'), + (0x2034, 'M', u'′′′'), + (0x2035, 'V'), + (0x2036, 'M', u'‵‵'), + (0x2037, 'M', u'‵‵‵'), + (0x2038, 'V'), + (0x203C, '3', u'!!'), + (0x203D, 'V'), + (0x203E, '3', u' ̅'), + (0x203F, 'V'), + (0x2047, '3', u'??'), + (0x2048, '3', u'?!'), + (0x2049, '3', u'!?'), + (0x204A, 'V'), + (0x2057, 'M', u'′′′′'), + (0x2058, 'V'), + (0x205F, '3', u' '), + (0x2060, 'I'), + (0x2061, 'X'), + (0x2064, 'I'), + (0x2065, 'X'), + (0x2070, 'M', u'0'), + (0x2071, 'M', u'i'), + (0x2072, 'X'), + (0x2074, 'M', u'4'), + (0x2075, 'M', u'5'), + (0x2076, 'M', u'6'), + (0x2077, 'M', u'7'), + (0x2078, 'M', u'8'), + (0x2079, 'M', u'9'), + (0x207A, '3', u'+'), + (0x207B, 'M', u'−'), + (0x207C, '3', u'='), + (0x207D, '3', u'('), + (0x207E, '3', u')'), + (0x207F, 'M', u'n'), + (0x2080, 'M', u'0'), + (0x2081, 'M', u'1'), + (0x2082, 'M', u'2'), + (0x2083, 'M', u'3'), + (0x2084, 'M', u'4'), + (0x2085, 'M', u'5'), + (0x2086, 'M', u'6'), + (0x2087, 'M', u'7'), + (0x2088, 'M', u'8'), + (0x2089, 'M', u'9'), + (0x208A, '3', u'+'), + (0x208B, 'M', u'−'), + (0x208C, '3', u'='), + (0x208D, '3', u'('), + (0x208E, '3', u')'), + (0x208F, 'X'), + (0x2090, 'M', u'a'), + (0x2091, 'M', u'e'), + (0x2092, 'M', u'o'), + (0x2093, 'M', u'x'), + (0x2094, 'M', u'ə'), + (0x2095, 'M', u'h'), + (0x2096, 'M', u'k'), + (0x2097, 'M', u'l'), + (0x2098, 'M', u'm'), + (0x2099, 'M', u'n'), + (0x209A, 'M', u'p'), + (0x209B, 'M', u's'), + (0x209C, 'M', u't'), + (0x209D, 'X'), + (0x20A0, 'V'), + (0x20A8, 'M', u'rs'), + (0x20A9, 'V'), + (0x20C0, 'X'), + (0x20D0, 'V'), + (0x20F1, 'X'), + (0x2100, '3', u'a/c'), + (0x2101, '3', u'a/s'), + (0x2102, 'M', u'c'), + (0x2103, 'M', u'°c'), + (0x2104, 'V'), + (0x2105, '3', u'c/o'), + (0x2106, '3', u'c/u'), + (0x2107, 'M', u'ɛ'), + (0x2108, 'V'), + (0x2109, 'M', u'°f'), + (0x210A, 'M', u'g'), + (0x210B, 'M', u'h'), + (0x210F, 'M', u'ħ'), + (0x2110, 'M', u'i'), + (0x2112, 'M', u'l'), + (0x2114, 'V'), + (0x2115, 'M', u'n'), + (0x2116, 'M', u'no'), + (0x2117, 'V'), + (0x2119, 'M', u'p'), + (0x211A, 'M', u'q'), + (0x211B, 'M', u'r'), + (0x211E, 'V'), + (0x2120, 'M', u'sm'), + (0x2121, 'M', u'tel'), + (0x2122, 'M', u'tm'), + ] + +def _seg_22(): + return [ + (0x2123, 'V'), + (0x2124, 'M', u'z'), + (0x2125, 'V'), + (0x2126, 'M', u'ω'), + (0x2127, 'V'), + (0x2128, 'M', u'z'), + (0x2129, 'V'), + (0x212A, 'M', u'k'), + (0x212B, 'M', u'å'), + (0x212C, 'M', u'b'), + (0x212D, 'M', u'c'), + (0x212E, 'V'), + (0x212F, 'M', u'e'), + (0x2131, 'M', u'f'), + (0x2132, 'X'), + (0x2133, 'M', u'm'), + (0x2134, 'M', u'o'), + (0x2135, 'M', u'א'), + (0x2136, 'M', u'ב'), + (0x2137, 'M', u'ג'), + (0x2138, 'M', u'ד'), + (0x2139, 'M', u'i'), + (0x213A, 'V'), + (0x213B, 'M', u'fax'), + (0x213C, 'M', u'π'), + (0x213D, 'M', u'γ'), + (0x213F, 'M', u'π'), + (0x2140, 'M', u'∑'), + (0x2141, 'V'), + (0x2145, 'M', u'd'), + (0x2147, 'M', u'e'), + (0x2148, 'M', u'i'), + (0x2149, 'M', u'j'), + (0x214A, 'V'), + (0x2150, 'M', u'1⁄7'), + (0x2151, 'M', u'1⁄9'), + (0x2152, 'M', u'1⁄10'), + (0x2153, 'M', u'1⁄3'), + (0x2154, 'M', u'2⁄3'), + (0x2155, 'M', u'1⁄5'), + (0x2156, 'M', u'2⁄5'), + (0x2157, 'M', u'3⁄5'), + (0x2158, 'M', u'4⁄5'), + (0x2159, 'M', u'1⁄6'), + (0x215A, 'M', u'5⁄6'), + (0x215B, 'M', u'1⁄8'), + (0x215C, 'M', u'3⁄8'), + (0x215D, 'M', u'5⁄8'), + (0x215E, 'M', u'7⁄8'), + (0x215F, 'M', u'1⁄'), + (0x2160, 'M', u'i'), + (0x2161, 'M', u'ii'), + (0x2162, 'M', u'iii'), + (0x2163, 'M', u'iv'), + (0x2164, 'M', u'v'), + (0x2165, 'M', u'vi'), + (0x2166, 'M', u'vii'), + (0x2167, 'M', u'viii'), + (0x2168, 'M', u'ix'), + (0x2169, 'M', u'x'), + (0x216A, 'M', u'xi'), + (0x216B, 'M', u'xii'), + (0x216C, 'M', u'l'), + (0x216D, 'M', u'c'), + (0x216E, 'M', u'd'), + (0x216F, 'M', u'm'), + (0x2170, 'M', u'i'), + (0x2171, 'M', u'ii'), + (0x2172, 'M', u'iii'), + (0x2173, 'M', u'iv'), + (0x2174, 'M', u'v'), + (0x2175, 'M', u'vi'), + (0x2176, 'M', u'vii'), + (0x2177, 'M', u'viii'), + (0x2178, 'M', u'ix'), + (0x2179, 'M', u'x'), + (0x217A, 'M', u'xi'), + (0x217B, 'M', u'xii'), + (0x217C, 'M', u'l'), + (0x217D, 'M', u'c'), + (0x217E, 'M', u'd'), + (0x217F, 'M', u'm'), + (0x2180, 'V'), + (0x2183, 'X'), + (0x2184, 'V'), + (0x2189, 'M', u'0⁄3'), + (0x218A, 'V'), + (0x218C, 'X'), + (0x2190, 'V'), + (0x222C, 'M', u'∫∫'), + (0x222D, 'M', u'∫∫∫'), + (0x222E, 'V'), + (0x222F, 'M', u'∮∮'), + (0x2230, 'M', u'∮∮∮'), + (0x2231, 'V'), + (0x2260, '3'), + (0x2261, 'V'), + (0x226E, '3'), + (0x2270, 'V'), + (0x2329, 'M', u'〈'), + ] + +def _seg_23(): + return [ + (0x232A, 'M', u'〉'), + (0x232B, 'V'), + (0x2427, 'X'), + (0x2440, 'V'), + (0x244B, 'X'), + (0x2460, 'M', u'1'), + (0x2461, 'M', u'2'), + (0x2462, 'M', u'3'), + (0x2463, 'M', u'4'), + (0x2464, 'M', u'5'), + (0x2465, 'M', u'6'), + (0x2466, 'M', u'7'), + (0x2467, 'M', u'8'), + (0x2468, 'M', u'9'), + (0x2469, 'M', u'10'), + (0x246A, 'M', u'11'), + (0x246B, 'M', u'12'), + (0x246C, 'M', u'13'), + (0x246D, 'M', u'14'), + (0x246E, 'M', u'15'), + (0x246F, 'M', u'16'), + (0x2470, 'M', u'17'), + (0x2471, 'M', u'18'), + (0x2472, 'M', u'19'), + (0x2473, 'M', u'20'), + (0x2474, '3', u'(1)'), + (0x2475, '3', u'(2)'), + (0x2476, '3', u'(3)'), + (0x2477, '3', u'(4)'), + (0x2478, '3', u'(5)'), + (0x2479, '3', u'(6)'), + (0x247A, '3', u'(7)'), + (0x247B, '3', u'(8)'), + (0x247C, '3', u'(9)'), + (0x247D, '3', u'(10)'), + (0x247E, '3', u'(11)'), + (0x247F, '3', u'(12)'), + (0x2480, '3', u'(13)'), + (0x2481, '3', u'(14)'), + (0x2482, '3', u'(15)'), + (0x2483, '3', u'(16)'), + (0x2484, '3', u'(17)'), + (0x2485, '3', u'(18)'), + (0x2486, '3', u'(19)'), + (0x2487, '3', u'(20)'), + (0x2488, 'X'), + (0x249C, '3', u'(a)'), + (0x249D, '3', u'(b)'), + (0x249E, '3', u'(c)'), + (0x249F, '3', u'(d)'), + (0x24A0, '3', u'(e)'), + (0x24A1, '3', u'(f)'), + (0x24A2, '3', u'(g)'), + (0x24A3, '3', u'(h)'), + (0x24A4, '3', u'(i)'), + (0x24A5, '3', u'(j)'), + (0x24A6, '3', u'(k)'), + (0x24A7, '3', u'(l)'), + (0x24A8, '3', u'(m)'), + (0x24A9, '3', u'(n)'), + (0x24AA, '3', u'(o)'), + (0x24AB, '3', u'(p)'), + (0x24AC, '3', u'(q)'), + (0x24AD, '3', u'(r)'), + (0x24AE, '3', u'(s)'), + (0x24AF, '3', u'(t)'), + (0x24B0, '3', u'(u)'), + (0x24B1, '3', u'(v)'), + (0x24B2, '3', u'(w)'), + (0x24B3, '3', u'(x)'), + (0x24B4, '3', u'(y)'), + (0x24B5, '3', u'(z)'), + (0x24B6, 'M', u'a'), + (0x24B7, 'M', u'b'), + (0x24B8, 'M', u'c'), + (0x24B9, 'M', u'd'), + (0x24BA, 'M', u'e'), + (0x24BB, 'M', u'f'), + (0x24BC, 'M', u'g'), + (0x24BD, 'M', u'h'), + (0x24BE, 'M', u'i'), + (0x24BF, 'M', u'j'), + (0x24C0, 'M', u'k'), + (0x24C1, 'M', u'l'), + (0x24C2, 'M', u'm'), + (0x24C3, 'M', u'n'), + (0x24C4, 'M', u'o'), + (0x24C5, 'M', u'p'), + (0x24C6, 'M', u'q'), + (0x24C7, 'M', u'r'), + (0x24C8, 'M', u's'), + (0x24C9, 'M', u't'), + (0x24CA, 'M', u'u'), + (0x24CB, 'M', u'v'), + (0x24CC, 'M', u'w'), + (0x24CD, 'M', u'x'), + (0x24CE, 'M', u'y'), + (0x24CF, 'M', u'z'), + (0x24D0, 'M', u'a'), + (0x24D1, 'M', u'b'), + ] + +def _seg_24(): + return [ + (0x24D2, 'M', u'c'), + (0x24D3, 'M', u'd'), + (0x24D4, 'M', u'e'), + (0x24D5, 'M', u'f'), + (0x24D6, 'M', u'g'), + (0x24D7, 'M', u'h'), + (0x24D8, 'M', u'i'), + (0x24D9, 'M', u'j'), + (0x24DA, 'M', u'k'), + (0x24DB, 'M', u'l'), + (0x24DC, 'M', u'm'), + (0x24DD, 'M', u'n'), + (0x24DE, 'M', u'o'), + (0x24DF, 'M', u'p'), + (0x24E0, 'M', u'q'), + (0x24E1, 'M', u'r'), + (0x24E2, 'M', u's'), + (0x24E3, 'M', u't'), + (0x24E4, 'M', u'u'), + (0x24E5, 'M', u'v'), + (0x24E6, 'M', u'w'), + (0x24E7, 'M', u'x'), + (0x24E8, 'M', u'y'), + (0x24E9, 'M', u'z'), + (0x24EA, 'M', u'0'), + (0x24EB, 'V'), + (0x2A0C, 'M', u'∫∫∫∫'), + (0x2A0D, 'V'), + (0x2A74, '3', u'::='), + (0x2A75, '3', u'=='), + (0x2A76, '3', u'==='), + (0x2A77, 'V'), + (0x2ADC, 'M', u'⫝̸'), + (0x2ADD, 'V'), + (0x2B74, 'X'), + (0x2B76, 'V'), + (0x2B96, 'X'), + (0x2B98, 'V'), + (0x2BBA, 'X'), + (0x2BBD, 'V'), + (0x2BC9, 'X'), + (0x2BCA, 'V'), + (0x2BD3, 'X'), + (0x2BEC, 'V'), + (0x2BF0, 'X'), + (0x2C00, 'M', u'ⰰ'), + (0x2C01, 'M', u'ⰱ'), + (0x2C02, 'M', u'ⰲ'), + (0x2C03, 'M', u'ⰳ'), + (0x2C04, 'M', u'ⰴ'), + (0x2C05, 'M', u'ⰵ'), + (0x2C06, 'M', u'ⰶ'), + (0x2C07, 'M', u'ⰷ'), + (0x2C08, 'M', u'ⰸ'), + (0x2C09, 'M', u'ⰹ'), + (0x2C0A, 'M', u'ⰺ'), + (0x2C0B, 'M', u'ⰻ'), + (0x2C0C, 'M', u'ⰼ'), + (0x2C0D, 'M', u'ⰽ'), + (0x2C0E, 'M', u'ⰾ'), + (0x2C0F, 'M', u'ⰿ'), + (0x2C10, 'M', u'ⱀ'), + (0x2C11, 'M', u'ⱁ'), + (0x2C12, 'M', u'ⱂ'), + (0x2C13, 'M', u'ⱃ'), + (0x2C14, 'M', u'ⱄ'), + (0x2C15, 'M', u'ⱅ'), + (0x2C16, 'M', u'ⱆ'), + (0x2C17, 'M', u'ⱇ'), + (0x2C18, 'M', u'ⱈ'), + (0x2C19, 'M', u'ⱉ'), + (0x2C1A, 'M', u'ⱊ'), + (0x2C1B, 'M', u'ⱋ'), + (0x2C1C, 'M', u'ⱌ'), + (0x2C1D, 'M', u'ⱍ'), + (0x2C1E, 'M', u'ⱎ'), + (0x2C1F, 'M', u'ⱏ'), + (0x2C20, 'M', u'ⱐ'), + (0x2C21, 'M', u'ⱑ'), + (0x2C22, 'M', u'ⱒ'), + (0x2C23, 'M', u'ⱓ'), + (0x2C24, 'M', u'ⱔ'), + (0x2C25, 'M', u'ⱕ'), + (0x2C26, 'M', u'ⱖ'), + (0x2C27, 'M', u'ⱗ'), + (0x2C28, 'M', u'ⱘ'), + (0x2C29, 'M', u'ⱙ'), + (0x2C2A, 'M', u'ⱚ'), + (0x2C2B, 'M', u'ⱛ'), + (0x2C2C, 'M', u'ⱜ'), + (0x2C2D, 'M', u'ⱝ'), + (0x2C2E, 'M', u'ⱞ'), + (0x2C2F, 'X'), + (0x2C30, 'V'), + (0x2C5F, 'X'), + (0x2C60, 'M', u'ⱡ'), + (0x2C61, 'V'), + (0x2C62, 'M', u'ɫ'), + (0x2C63, 'M', u'ᵽ'), + (0x2C64, 'M', u'ɽ'), + ] + +def _seg_25(): + return [ + (0x2C65, 'V'), + (0x2C67, 'M', u'ⱨ'), + (0x2C68, 'V'), + (0x2C69, 'M', u'ⱪ'), + (0x2C6A, 'V'), + (0x2C6B, 'M', u'ⱬ'), + (0x2C6C, 'V'), + (0x2C6D, 'M', u'ɑ'), + (0x2C6E, 'M', u'ɱ'), + (0x2C6F, 'M', u'ɐ'), + (0x2C70, 'M', u'ɒ'), + (0x2C71, 'V'), + (0x2C72, 'M', u'ⱳ'), + (0x2C73, 'V'), + (0x2C75, 'M', u'ⱶ'), + (0x2C76, 'V'), + (0x2C7C, 'M', u'j'), + (0x2C7D, 'M', u'v'), + (0x2C7E, 'M', u'ȿ'), + (0x2C7F, 'M', u'ɀ'), + (0x2C80, 'M', u'ⲁ'), + (0x2C81, 'V'), + (0x2C82, 'M', u'ⲃ'), + (0x2C83, 'V'), + (0x2C84, 'M', u'ⲅ'), + (0x2C85, 'V'), + (0x2C86, 'M', u'ⲇ'), + (0x2C87, 'V'), + (0x2C88, 'M', u'ⲉ'), + (0x2C89, 'V'), + (0x2C8A, 'M', u'ⲋ'), + (0x2C8B, 'V'), + (0x2C8C, 'M', u'ⲍ'), + (0x2C8D, 'V'), + (0x2C8E, 'M', u'ⲏ'), + (0x2C8F, 'V'), + (0x2C90, 'M', u'ⲑ'), + (0x2C91, 'V'), + (0x2C92, 'M', u'ⲓ'), + (0x2C93, 'V'), + (0x2C94, 'M', u'ⲕ'), + (0x2C95, 'V'), + (0x2C96, 'M', u'ⲗ'), + (0x2C97, 'V'), + (0x2C98, 'M', u'ⲙ'), + (0x2C99, 'V'), + (0x2C9A, 'M', u'ⲛ'), + (0x2C9B, 'V'), + (0x2C9C, 'M', u'ⲝ'), + (0x2C9D, 'V'), + (0x2C9E, 'M', u'ⲟ'), + (0x2C9F, 'V'), + (0x2CA0, 'M', u'ⲡ'), + (0x2CA1, 'V'), + (0x2CA2, 'M', u'ⲣ'), + (0x2CA3, 'V'), + (0x2CA4, 'M', u'ⲥ'), + (0x2CA5, 'V'), + (0x2CA6, 'M', u'ⲧ'), + (0x2CA7, 'V'), + (0x2CA8, 'M', u'ⲩ'), + (0x2CA9, 'V'), + (0x2CAA, 'M', u'ⲫ'), + (0x2CAB, 'V'), + (0x2CAC, 'M', u'ⲭ'), + (0x2CAD, 'V'), + (0x2CAE, 'M', u'ⲯ'), + (0x2CAF, 'V'), + (0x2CB0, 'M', u'ⲱ'), + (0x2CB1, 'V'), + (0x2CB2, 'M', u'ⲳ'), + (0x2CB3, 'V'), + (0x2CB4, 'M', u'ⲵ'), + (0x2CB5, 'V'), + (0x2CB6, 'M', u'ⲷ'), + (0x2CB7, 'V'), + (0x2CB8, 'M', u'ⲹ'), + (0x2CB9, 'V'), + (0x2CBA, 'M', u'ⲻ'), + (0x2CBB, 'V'), + (0x2CBC, 'M', u'ⲽ'), + (0x2CBD, 'V'), + (0x2CBE, 'M', u'ⲿ'), + (0x2CBF, 'V'), + (0x2CC0, 'M', u'ⳁ'), + (0x2CC1, 'V'), + (0x2CC2, 'M', u'ⳃ'), + (0x2CC3, 'V'), + (0x2CC4, 'M', u'ⳅ'), + (0x2CC5, 'V'), + (0x2CC6, 'M', u'ⳇ'), + (0x2CC7, 'V'), + (0x2CC8, 'M', u'ⳉ'), + (0x2CC9, 'V'), + (0x2CCA, 'M', u'ⳋ'), + (0x2CCB, 'V'), + (0x2CCC, 'M', u'ⳍ'), + (0x2CCD, 'V'), + (0x2CCE, 'M', u'ⳏ'), + (0x2CCF, 'V'), + ] + +def _seg_26(): + return [ + (0x2CD0, 'M', u'ⳑ'), + (0x2CD1, 'V'), + (0x2CD2, 'M', u'ⳓ'), + (0x2CD3, 'V'), + (0x2CD4, 'M', u'ⳕ'), + (0x2CD5, 'V'), + (0x2CD6, 'M', u'ⳗ'), + (0x2CD7, 'V'), + (0x2CD8, 'M', u'ⳙ'), + (0x2CD9, 'V'), + (0x2CDA, 'M', u'ⳛ'), + (0x2CDB, 'V'), + (0x2CDC, 'M', u'ⳝ'), + (0x2CDD, 'V'), + (0x2CDE, 'M', u'ⳟ'), + (0x2CDF, 'V'), + (0x2CE0, 'M', u'ⳡ'), + (0x2CE1, 'V'), + (0x2CE2, 'M', u'ⳣ'), + (0x2CE3, 'V'), + (0x2CEB, 'M', u'ⳬ'), + (0x2CEC, 'V'), + (0x2CED, 'M', u'ⳮ'), + (0x2CEE, 'V'), + (0x2CF2, 'M', u'ⳳ'), + (0x2CF3, 'V'), + (0x2CF4, 'X'), + (0x2CF9, 'V'), + (0x2D26, 'X'), + (0x2D27, 'V'), + (0x2D28, 'X'), + (0x2D2D, 'V'), + (0x2D2E, 'X'), + (0x2D30, 'V'), + (0x2D68, 'X'), + (0x2D6F, 'M', u'ⵡ'), + (0x2D70, 'V'), + (0x2D71, 'X'), + (0x2D7F, 'V'), + (0x2D97, 'X'), + (0x2DA0, 'V'), + (0x2DA7, 'X'), + (0x2DA8, 'V'), + (0x2DAF, 'X'), + (0x2DB0, 'V'), + (0x2DB7, 'X'), + (0x2DB8, 'V'), + (0x2DBF, 'X'), + (0x2DC0, 'V'), + (0x2DC7, 'X'), + (0x2DC8, 'V'), + (0x2DCF, 'X'), + (0x2DD0, 'V'), + (0x2DD7, 'X'), + (0x2DD8, 'V'), + (0x2DDF, 'X'), + (0x2DE0, 'V'), + (0x2E4A, 'X'), + (0x2E80, 'V'), + (0x2E9A, 'X'), + (0x2E9B, 'V'), + (0x2E9F, 'M', u'母'), + (0x2EA0, 'V'), + (0x2EF3, 'M', u'龟'), + (0x2EF4, 'X'), + (0x2F00, 'M', u'一'), + (0x2F01, 'M', u'丨'), + (0x2F02, 'M', u'丶'), + (0x2F03, 'M', u'丿'), + (0x2F04, 'M', u'乙'), + (0x2F05, 'M', u'亅'), + (0x2F06, 'M', u'二'), + (0x2F07, 'M', u'亠'), + (0x2F08, 'M', u'人'), + (0x2F09, 'M', u'儿'), + (0x2F0A, 'M', u'入'), + (0x2F0B, 'M', u'八'), + (0x2F0C, 'M', u'冂'), + (0x2F0D, 'M', u'冖'), + (0x2F0E, 'M', u'冫'), + (0x2F0F, 'M', u'几'), + (0x2F10, 'M', u'凵'), + (0x2F11, 'M', u'刀'), + (0x2F12, 'M', u'力'), + (0x2F13, 'M', u'勹'), + (0x2F14, 'M', u'匕'), + (0x2F15, 'M', u'匚'), + (0x2F16, 'M', u'匸'), + (0x2F17, 'M', u'十'), + (0x2F18, 'M', u'卜'), + (0x2F19, 'M', u'卩'), + (0x2F1A, 'M', u'厂'), + (0x2F1B, 'M', u'厶'), + (0x2F1C, 'M', u'又'), + (0x2F1D, 'M', u'口'), + (0x2F1E, 'M', u'囗'), + (0x2F1F, 'M', u'土'), + (0x2F20, 'M', u'士'), + (0x2F21, 'M', u'夂'), + (0x2F22, 'M', u'夊'), + ] + +def _seg_27(): + return [ + (0x2F23, 'M', u'夕'), + (0x2F24, 'M', u'大'), + (0x2F25, 'M', u'女'), + (0x2F26, 'M', u'子'), + (0x2F27, 'M', u'宀'), + (0x2F28, 'M', u'寸'), + (0x2F29, 'M', u'小'), + (0x2F2A, 'M', u'尢'), + (0x2F2B, 'M', u'尸'), + (0x2F2C, 'M', u'屮'), + (0x2F2D, 'M', u'山'), + (0x2F2E, 'M', u'巛'), + (0x2F2F, 'M', u'工'), + (0x2F30, 'M', u'己'), + (0x2F31, 'M', u'巾'), + (0x2F32, 'M', u'干'), + (0x2F33, 'M', u'幺'), + (0x2F34, 'M', u'广'), + (0x2F35, 'M', u'廴'), + (0x2F36, 'M', u'廾'), + (0x2F37, 'M', u'弋'), + (0x2F38, 'M', u'弓'), + (0x2F39, 'M', u'彐'), + (0x2F3A, 'M', u'彡'), + (0x2F3B, 'M', u'彳'), + (0x2F3C, 'M', u'心'), + (0x2F3D, 'M', u'戈'), + (0x2F3E, 'M', u'戶'), + (0x2F3F, 'M', u'手'), + (0x2F40, 'M', u'支'), + (0x2F41, 'M', u'攴'), + (0x2F42, 'M', u'文'), + (0x2F43, 'M', u'斗'), + (0x2F44, 'M', u'斤'), + (0x2F45, 'M', u'方'), + (0x2F46, 'M', u'无'), + (0x2F47, 'M', u'日'), + (0x2F48, 'M', u'曰'), + (0x2F49, 'M', u'月'), + (0x2F4A, 'M', u'木'), + (0x2F4B, 'M', u'欠'), + (0x2F4C, 'M', u'止'), + (0x2F4D, 'M', u'歹'), + (0x2F4E, 'M', u'殳'), + (0x2F4F, 'M', u'毋'), + (0x2F50, 'M', u'比'), + (0x2F51, 'M', u'毛'), + (0x2F52, 'M', u'氏'), + (0x2F53, 'M', u'气'), + (0x2F54, 'M', u'水'), + (0x2F55, 'M', u'火'), + (0x2F56, 'M', u'爪'), + (0x2F57, 'M', u'父'), + (0x2F58, 'M', u'爻'), + (0x2F59, 'M', u'爿'), + (0x2F5A, 'M', u'片'), + (0x2F5B, 'M', u'牙'), + (0x2F5C, 'M', u'牛'), + (0x2F5D, 'M', u'犬'), + (0x2F5E, 'M', u'玄'), + (0x2F5F, 'M', u'玉'), + (0x2F60, 'M', u'瓜'), + (0x2F61, 'M', u'瓦'), + (0x2F62, 'M', u'甘'), + (0x2F63, 'M', u'生'), + (0x2F64, 'M', u'用'), + (0x2F65, 'M', u'田'), + (0x2F66, 'M', u'疋'), + (0x2F67, 'M', u'疒'), + (0x2F68, 'M', u'癶'), + (0x2F69, 'M', u'白'), + (0x2F6A, 'M', u'皮'), + (0x2F6B, 'M', u'皿'), + (0x2F6C, 'M', u'目'), + (0x2F6D, 'M', u'矛'), + (0x2F6E, 'M', u'矢'), + (0x2F6F, 'M', u'石'), + (0x2F70, 'M', u'示'), + (0x2F71, 'M', u'禸'), + (0x2F72, 'M', u'禾'), + (0x2F73, 'M', u'穴'), + (0x2F74, 'M', u'立'), + (0x2F75, 'M', u'竹'), + (0x2F76, 'M', u'米'), + (0x2F77, 'M', u'糸'), + (0x2F78, 'M', u'缶'), + (0x2F79, 'M', u'网'), + (0x2F7A, 'M', u'羊'), + (0x2F7B, 'M', u'羽'), + (0x2F7C, 'M', u'老'), + (0x2F7D, 'M', u'而'), + (0x2F7E, 'M', u'耒'), + (0x2F7F, 'M', u'耳'), + (0x2F80, 'M', u'聿'), + (0x2F81, 'M', u'肉'), + (0x2F82, 'M', u'臣'), + (0x2F83, 'M', u'自'), + (0x2F84, 'M', u'至'), + (0x2F85, 'M', u'臼'), + (0x2F86, 'M', u'舌'), + ] + +def _seg_28(): + return [ + (0x2F87, 'M', u'舛'), + (0x2F88, 'M', u'舟'), + (0x2F89, 'M', u'艮'), + (0x2F8A, 'M', u'色'), + (0x2F8B, 'M', u'艸'), + (0x2F8C, 'M', u'虍'), + (0x2F8D, 'M', u'虫'), + (0x2F8E, 'M', u'血'), + (0x2F8F, 'M', u'行'), + (0x2F90, 'M', u'衣'), + (0x2F91, 'M', u'襾'), + (0x2F92, 'M', u'見'), + (0x2F93, 'M', u'角'), + (0x2F94, 'M', u'言'), + (0x2F95, 'M', u'谷'), + (0x2F96, 'M', u'豆'), + (0x2F97, 'M', u'豕'), + (0x2F98, 'M', u'豸'), + (0x2F99, 'M', u'貝'), + (0x2F9A, 'M', u'赤'), + (0x2F9B, 'M', u'走'), + (0x2F9C, 'M', u'足'), + (0x2F9D, 'M', u'身'), + (0x2F9E, 'M', u'車'), + (0x2F9F, 'M', u'辛'), + (0x2FA0, 'M', u'辰'), + (0x2FA1, 'M', u'辵'), + (0x2FA2, 'M', u'邑'), + (0x2FA3, 'M', u'酉'), + (0x2FA4, 'M', u'釆'), + (0x2FA5, 'M', u'里'), + (0x2FA6, 'M', u'金'), + (0x2FA7, 'M', u'長'), + (0x2FA8, 'M', u'門'), + (0x2FA9, 'M', u'阜'), + (0x2FAA, 'M', u'隶'), + (0x2FAB, 'M', u'隹'), + (0x2FAC, 'M', u'雨'), + (0x2FAD, 'M', u'靑'), + (0x2FAE, 'M', u'非'), + (0x2FAF, 'M', u'面'), + (0x2FB0, 'M', u'革'), + (0x2FB1, 'M', u'韋'), + (0x2FB2, 'M', u'韭'), + (0x2FB3, 'M', u'音'), + (0x2FB4, 'M', u'頁'), + (0x2FB5, 'M', u'風'), + (0x2FB6, 'M', u'飛'), + (0x2FB7, 'M', u'食'), + (0x2FB8, 'M', u'首'), + (0x2FB9, 'M', u'香'), + (0x2FBA, 'M', u'馬'), + (0x2FBB, 'M', u'骨'), + (0x2FBC, 'M', u'高'), + (0x2FBD, 'M', u'髟'), + (0x2FBE, 'M', u'鬥'), + (0x2FBF, 'M', u'鬯'), + (0x2FC0, 'M', u'鬲'), + (0x2FC1, 'M', u'鬼'), + (0x2FC2, 'M', u'魚'), + (0x2FC3, 'M', u'鳥'), + (0x2FC4, 'M', u'鹵'), + (0x2FC5, 'M', u'鹿'), + (0x2FC6, 'M', u'麥'), + (0x2FC7, 'M', u'麻'), + (0x2FC8, 'M', u'黃'), + (0x2FC9, 'M', u'黍'), + (0x2FCA, 'M', u'黑'), + (0x2FCB, 'M', u'黹'), + (0x2FCC, 'M', u'黽'), + (0x2FCD, 'M', u'鼎'), + (0x2FCE, 'M', u'鼓'), + (0x2FCF, 'M', u'鼠'), + (0x2FD0, 'M', u'鼻'), + (0x2FD1, 'M', u'齊'), + (0x2FD2, 'M', u'齒'), + (0x2FD3, 'M', u'龍'), + (0x2FD4, 'M', u'龜'), + (0x2FD5, 'M', u'龠'), + (0x2FD6, 'X'), + (0x3000, '3', u' '), + (0x3001, 'V'), + (0x3002, 'M', u'.'), + (0x3003, 'V'), + (0x3036, 'M', u'〒'), + (0x3037, 'V'), + (0x3038, 'M', u'十'), + (0x3039, 'M', u'卄'), + (0x303A, 'M', u'卅'), + (0x303B, 'V'), + (0x3040, 'X'), + (0x3041, 'V'), + (0x3097, 'X'), + (0x3099, 'V'), + (0x309B, '3', u' ゙'), + (0x309C, '3', u' ゚'), + (0x309D, 'V'), + (0x309F, 'M', u'より'), + (0x30A0, 'V'), + (0x30FF, 'M', u'コト'), + ] + +def _seg_29(): + return [ + (0x3100, 'X'), + (0x3105, 'V'), + (0x312F, 'X'), + (0x3131, 'M', u'ᄀ'), + (0x3132, 'M', u'ᄁ'), + (0x3133, 'M', u'ᆪ'), + (0x3134, 'M', u'ᄂ'), + (0x3135, 'M', u'ᆬ'), + (0x3136, 'M', u'ᆭ'), + (0x3137, 'M', u'ᄃ'), + (0x3138, 'M', u'ᄄ'), + (0x3139, 'M', u'ᄅ'), + (0x313A, 'M', u'ᆰ'), + (0x313B, 'M', u'ᆱ'), + (0x313C, 'M', u'ᆲ'), + (0x313D, 'M', u'ᆳ'), + (0x313E, 'M', u'ᆴ'), + (0x313F, 'M', u'ᆵ'), + (0x3140, 'M', u'ᄚ'), + (0x3141, 'M', u'ᄆ'), + (0x3142, 'M', u'ᄇ'), + (0x3143, 'M', u'ᄈ'), + (0x3144, 'M', u'ᄡ'), + (0x3145, 'M', u'ᄉ'), + (0x3146, 'M', u'ᄊ'), + (0x3147, 'M', u'ᄋ'), + (0x3148, 'M', u'ᄌ'), + (0x3149, 'M', u'ᄍ'), + (0x314A, 'M', u'ᄎ'), + (0x314B, 'M', u'ᄏ'), + (0x314C, 'M', u'ᄐ'), + (0x314D, 'M', u'ᄑ'), + (0x314E, 'M', u'ᄒ'), + (0x314F, 'M', u'ᅡ'), + (0x3150, 'M', u'ᅢ'), + (0x3151, 'M', u'ᅣ'), + (0x3152, 'M', u'ᅤ'), + (0x3153, 'M', u'ᅥ'), + (0x3154, 'M', u'ᅦ'), + (0x3155, 'M', u'ᅧ'), + (0x3156, 'M', u'ᅨ'), + (0x3157, 'M', u'ᅩ'), + (0x3158, 'M', u'ᅪ'), + (0x3159, 'M', u'ᅫ'), + (0x315A, 'M', u'ᅬ'), + (0x315B, 'M', u'ᅭ'), + (0x315C, 'M', u'ᅮ'), + (0x315D, 'M', u'ᅯ'), + (0x315E, 'M', u'ᅰ'), + (0x315F, 'M', u'ᅱ'), + (0x3160, 'M', u'ᅲ'), + (0x3161, 'M', u'ᅳ'), + (0x3162, 'M', u'ᅴ'), + (0x3163, 'M', u'ᅵ'), + (0x3164, 'X'), + (0x3165, 'M', u'ᄔ'), + (0x3166, 'M', u'ᄕ'), + (0x3167, 'M', u'ᇇ'), + (0x3168, 'M', u'ᇈ'), + (0x3169, 'M', u'ᇌ'), + (0x316A, 'M', u'ᇎ'), + (0x316B, 'M', u'ᇓ'), + (0x316C, 'M', u'ᇗ'), + (0x316D, 'M', u'ᇙ'), + (0x316E, 'M', u'ᄜ'), + (0x316F, 'M', u'ᇝ'), + (0x3170, 'M', u'ᇟ'), + (0x3171, 'M', u'ᄝ'), + (0x3172, 'M', u'ᄞ'), + (0x3173, 'M', u'ᄠ'), + (0x3174, 'M', u'ᄢ'), + (0x3175, 'M', u'ᄣ'), + (0x3176, 'M', u'ᄧ'), + (0x3177, 'M', u'ᄩ'), + (0x3178, 'M', u'ᄫ'), + (0x3179, 'M', u'ᄬ'), + (0x317A, 'M', u'ᄭ'), + (0x317B, 'M', u'ᄮ'), + (0x317C, 'M', u'ᄯ'), + (0x317D, 'M', u'ᄲ'), + (0x317E, 'M', u'ᄶ'), + (0x317F, 'M', u'ᅀ'), + (0x3180, 'M', u'ᅇ'), + (0x3181, 'M', u'ᅌ'), + (0x3182, 'M', u'ᇱ'), + (0x3183, 'M', u'ᇲ'), + (0x3184, 'M', u'ᅗ'), + (0x3185, 'M', u'ᅘ'), + (0x3186, 'M', u'ᅙ'), + (0x3187, 'M', u'ᆄ'), + (0x3188, 'M', u'ᆅ'), + (0x3189, 'M', u'ᆈ'), + (0x318A, 'M', u'ᆑ'), + (0x318B, 'M', u'ᆒ'), + (0x318C, 'M', u'ᆔ'), + (0x318D, 'M', u'ᆞ'), + (0x318E, 'M', u'ᆡ'), + (0x318F, 'X'), + (0x3190, 'V'), + (0x3192, 'M', u'一'), + ] + +def _seg_30(): + return [ + (0x3193, 'M', u'二'), + (0x3194, 'M', u'三'), + (0x3195, 'M', u'四'), + (0x3196, 'M', u'上'), + (0x3197, 'M', u'中'), + (0x3198, 'M', u'下'), + (0x3199, 'M', u'甲'), + (0x319A, 'M', u'乙'), + (0x319B, 'M', u'丙'), + (0x319C, 'M', u'丁'), + (0x319D, 'M', u'天'), + (0x319E, 'M', u'地'), + (0x319F, 'M', u'人'), + (0x31A0, 'V'), + (0x31BB, 'X'), + (0x31C0, 'V'), + (0x31E4, 'X'), + (0x31F0, 'V'), + (0x3200, '3', u'(ᄀ)'), + (0x3201, '3', u'(ᄂ)'), + (0x3202, '3', u'(ᄃ)'), + (0x3203, '3', u'(ᄅ)'), + (0x3204, '3', u'(ᄆ)'), + (0x3205, '3', u'(ᄇ)'), + (0x3206, '3', u'(ᄉ)'), + (0x3207, '3', u'(ᄋ)'), + (0x3208, '3', u'(ᄌ)'), + (0x3209, '3', u'(ᄎ)'), + (0x320A, '3', u'(ᄏ)'), + (0x320B, '3', u'(ᄐ)'), + (0x320C, '3', u'(ᄑ)'), + (0x320D, '3', u'(ᄒ)'), + (0x320E, '3', u'(가)'), + (0x320F, '3', u'(나)'), + (0x3210, '3', u'(다)'), + (0x3211, '3', u'(라)'), + (0x3212, '3', u'(마)'), + (0x3213, '3', u'(바)'), + (0x3214, '3', u'(사)'), + (0x3215, '3', u'(아)'), + (0x3216, '3', u'(자)'), + (0x3217, '3', u'(차)'), + (0x3218, '3', u'(카)'), + (0x3219, '3', u'(타)'), + (0x321A, '3', u'(파)'), + (0x321B, '3', u'(하)'), + (0x321C, '3', u'(주)'), + (0x321D, '3', u'(오전)'), + (0x321E, '3', u'(오후)'), + (0x321F, 'X'), + (0x3220, '3', u'(一)'), + (0x3221, '3', u'(二)'), + (0x3222, '3', u'(三)'), + (0x3223, '3', u'(四)'), + (0x3224, '3', u'(五)'), + (0x3225, '3', u'(六)'), + (0x3226, '3', u'(七)'), + (0x3227, '3', u'(八)'), + (0x3228, '3', u'(九)'), + (0x3229, '3', u'(十)'), + (0x322A, '3', u'(月)'), + (0x322B, '3', u'(火)'), + (0x322C, '3', u'(水)'), + (0x322D, '3', u'(木)'), + (0x322E, '3', u'(金)'), + (0x322F, '3', u'(土)'), + (0x3230, '3', u'(日)'), + (0x3231, '3', u'(株)'), + (0x3232, '3', u'(有)'), + (0x3233, '3', u'(社)'), + (0x3234, '3', u'(名)'), + (0x3235, '3', u'(特)'), + (0x3236, '3', u'(財)'), + (0x3237, '3', u'(祝)'), + (0x3238, '3', u'(労)'), + (0x3239, '3', u'(代)'), + (0x323A, '3', u'(呼)'), + (0x323B, '3', u'(学)'), + (0x323C, '3', u'(監)'), + (0x323D, '3', u'(企)'), + (0x323E, '3', u'(資)'), + (0x323F, '3', u'(協)'), + (0x3240, '3', u'(祭)'), + (0x3241, '3', u'(休)'), + (0x3242, '3', u'(自)'), + (0x3243, '3', u'(至)'), + (0x3244, 'M', u'問'), + (0x3245, 'M', u'幼'), + (0x3246, 'M', u'文'), + (0x3247, 'M', u'箏'), + (0x3248, 'V'), + (0x3250, 'M', u'pte'), + (0x3251, 'M', u'21'), + (0x3252, 'M', u'22'), + (0x3253, 'M', u'23'), + (0x3254, 'M', u'24'), + (0x3255, 'M', u'25'), + (0x3256, 'M', u'26'), + (0x3257, 'M', u'27'), + (0x3258, 'M', u'28'), + ] + +def _seg_31(): + return [ + (0x3259, 'M', u'29'), + (0x325A, 'M', u'30'), + (0x325B, 'M', u'31'), + (0x325C, 'M', u'32'), + (0x325D, 'M', u'33'), + (0x325E, 'M', u'34'), + (0x325F, 'M', u'35'), + (0x3260, 'M', u'ᄀ'), + (0x3261, 'M', u'ᄂ'), + (0x3262, 'M', u'ᄃ'), + (0x3263, 'M', u'ᄅ'), + (0x3264, 'M', u'ᄆ'), + (0x3265, 'M', u'ᄇ'), + (0x3266, 'M', u'ᄉ'), + (0x3267, 'M', u'ᄋ'), + (0x3268, 'M', u'ᄌ'), + (0x3269, 'M', u'ᄎ'), + (0x326A, 'M', u'ᄏ'), + (0x326B, 'M', u'ᄐ'), + (0x326C, 'M', u'ᄑ'), + (0x326D, 'M', u'ᄒ'), + (0x326E, 'M', u'가'), + (0x326F, 'M', u'나'), + (0x3270, 'M', u'다'), + (0x3271, 'M', u'라'), + (0x3272, 'M', u'마'), + (0x3273, 'M', u'바'), + (0x3274, 'M', u'사'), + (0x3275, 'M', u'아'), + (0x3276, 'M', u'자'), + (0x3277, 'M', u'차'), + (0x3278, 'M', u'카'), + (0x3279, 'M', u'타'), + (0x327A, 'M', u'파'), + (0x327B, 'M', u'하'), + (0x327C, 'M', u'참고'), + (0x327D, 'M', u'주의'), + (0x327E, 'M', u'우'), + (0x327F, 'V'), + (0x3280, 'M', u'一'), + (0x3281, 'M', u'二'), + (0x3282, 'M', u'三'), + (0x3283, 'M', u'四'), + (0x3284, 'M', u'五'), + (0x3285, 'M', u'六'), + (0x3286, 'M', u'七'), + (0x3287, 'M', u'八'), + (0x3288, 'M', u'九'), + (0x3289, 'M', u'十'), + (0x328A, 'M', u'月'), + (0x328B, 'M', u'火'), + (0x328C, 'M', u'水'), + (0x328D, 'M', u'木'), + (0x328E, 'M', u'金'), + (0x328F, 'M', u'土'), + (0x3290, 'M', u'日'), + (0x3291, 'M', u'株'), + (0x3292, 'M', u'有'), + (0x3293, 'M', u'社'), + (0x3294, 'M', u'名'), + (0x3295, 'M', u'特'), + (0x3296, 'M', u'財'), + (0x3297, 'M', u'祝'), + (0x3298, 'M', u'労'), + (0x3299, 'M', u'秘'), + (0x329A, 'M', u'男'), + (0x329B, 'M', u'女'), + (0x329C, 'M', u'適'), + (0x329D, 'M', u'優'), + (0x329E, 'M', u'印'), + (0x329F, 'M', u'注'), + (0x32A0, 'M', u'項'), + (0x32A1, 'M', u'休'), + (0x32A2, 'M', u'写'), + (0x32A3, 'M', u'正'), + (0x32A4, 'M', u'上'), + (0x32A5, 'M', u'中'), + (0x32A6, 'M', u'下'), + (0x32A7, 'M', u'左'), + (0x32A8, 'M', u'右'), + (0x32A9, 'M', u'医'), + (0x32AA, 'M', u'宗'), + (0x32AB, 'M', u'学'), + (0x32AC, 'M', u'監'), + (0x32AD, 'M', u'企'), + (0x32AE, 'M', u'資'), + (0x32AF, 'M', u'協'), + (0x32B0, 'M', u'夜'), + (0x32B1, 'M', u'36'), + (0x32B2, 'M', u'37'), + (0x32B3, 'M', u'38'), + (0x32B4, 'M', u'39'), + (0x32B5, 'M', u'40'), + (0x32B6, 'M', u'41'), + (0x32B7, 'M', u'42'), + (0x32B8, 'M', u'43'), + (0x32B9, 'M', u'44'), + (0x32BA, 'M', u'45'), + (0x32BB, 'M', u'46'), + (0x32BC, 'M', u'47'), + ] + +def _seg_32(): + return [ + (0x32BD, 'M', u'48'), + (0x32BE, 'M', u'49'), + (0x32BF, 'M', u'50'), + (0x32C0, 'M', u'1月'), + (0x32C1, 'M', u'2月'), + (0x32C2, 'M', u'3月'), + (0x32C3, 'M', u'4月'), + (0x32C4, 'M', u'5月'), + (0x32C5, 'M', u'6月'), + (0x32C6, 'M', u'7月'), + (0x32C7, 'M', u'8月'), + (0x32C8, 'M', u'9月'), + (0x32C9, 'M', u'10月'), + (0x32CA, 'M', u'11月'), + (0x32CB, 'M', u'12月'), + (0x32CC, 'M', u'hg'), + (0x32CD, 'M', u'erg'), + (0x32CE, 'M', u'ev'), + (0x32CF, 'M', u'ltd'), + (0x32D0, 'M', u'ア'), + (0x32D1, 'M', u'イ'), + (0x32D2, 'M', u'ウ'), + (0x32D3, 'M', u'エ'), + (0x32D4, 'M', u'オ'), + (0x32D5, 'M', u'カ'), + (0x32D6, 'M', u'キ'), + (0x32D7, 'M', u'ク'), + (0x32D8, 'M', u'ケ'), + (0x32D9, 'M', u'コ'), + (0x32DA, 'M', u'サ'), + (0x32DB, 'M', u'シ'), + (0x32DC, 'M', u'ス'), + (0x32DD, 'M', u'セ'), + (0x32DE, 'M', u'ソ'), + (0x32DF, 'M', u'タ'), + (0x32E0, 'M', u'チ'), + (0x32E1, 'M', u'ツ'), + (0x32E2, 'M', u'テ'), + (0x32E3, 'M', u'ト'), + (0x32E4, 'M', u'ナ'), + (0x32E5, 'M', u'ニ'), + (0x32E6, 'M', u'ヌ'), + (0x32E7, 'M', u'ネ'), + (0x32E8, 'M', u'ノ'), + (0x32E9, 'M', u'ハ'), + (0x32EA, 'M', u'ヒ'), + (0x32EB, 'M', u'フ'), + (0x32EC, 'M', u'ヘ'), + (0x32ED, 'M', u'ホ'), + (0x32EE, 'M', u'マ'), + (0x32EF, 'M', u'ミ'), + (0x32F0, 'M', u'ム'), + (0x32F1, 'M', u'メ'), + (0x32F2, 'M', u'モ'), + (0x32F3, 'M', u'ヤ'), + (0x32F4, 'M', u'ユ'), + (0x32F5, 'M', u'ヨ'), + (0x32F6, 'M', u'ラ'), + (0x32F7, 'M', u'リ'), + (0x32F8, 'M', u'ル'), + (0x32F9, 'M', u'レ'), + (0x32FA, 'M', u'ロ'), + (0x32FB, 'M', u'ワ'), + (0x32FC, 'M', u'ヰ'), + (0x32FD, 'M', u'ヱ'), + (0x32FE, 'M', u'ヲ'), + (0x32FF, 'X'), + (0x3300, 'M', u'アパート'), + (0x3301, 'M', u'アルファ'), + (0x3302, 'M', u'アンペア'), + (0x3303, 'M', u'アール'), + (0x3304, 'M', u'イニング'), + (0x3305, 'M', u'インチ'), + (0x3306, 'M', u'ウォン'), + (0x3307, 'M', u'エスクード'), + (0x3308, 'M', u'エーカー'), + (0x3309, 'M', u'オンス'), + (0x330A, 'M', u'オーム'), + (0x330B, 'M', u'カイリ'), + (0x330C, 'M', u'カラット'), + (0x330D, 'M', u'カロリー'), + (0x330E, 'M', u'ガロン'), + (0x330F, 'M', u'ガンマ'), + (0x3310, 'M', u'ギガ'), + (0x3311, 'M', u'ギニー'), + (0x3312, 'M', u'キュリー'), + (0x3313, 'M', u'ギルダー'), + (0x3314, 'M', u'キロ'), + (0x3315, 'M', u'キログラム'), + (0x3316, 'M', u'キロメートル'), + (0x3317, 'M', u'キロワット'), + (0x3318, 'M', u'グラム'), + (0x3319, 'M', u'グラムトン'), + (0x331A, 'M', u'クルゼイロ'), + (0x331B, 'M', u'クローネ'), + (0x331C, 'M', u'ケース'), + (0x331D, 'M', u'コルナ'), + (0x331E, 'M', u'コーポ'), + (0x331F, 'M', u'サイクル'), + (0x3320, 'M', u'サンチーム'), + ] + +def _seg_33(): + return [ + (0x3321, 'M', u'シリング'), + (0x3322, 'M', u'センチ'), + (0x3323, 'M', u'セント'), + (0x3324, 'M', u'ダース'), + (0x3325, 'M', u'デシ'), + (0x3326, 'M', u'ドル'), + (0x3327, 'M', u'トン'), + (0x3328, 'M', u'ナノ'), + (0x3329, 'M', u'ノット'), + (0x332A, 'M', u'ハイツ'), + (0x332B, 'M', u'パーセント'), + (0x332C, 'M', u'パーツ'), + (0x332D, 'M', u'バーレル'), + (0x332E, 'M', u'ピアストル'), + (0x332F, 'M', u'ピクル'), + (0x3330, 'M', u'ピコ'), + (0x3331, 'M', u'ビル'), + (0x3332, 'M', u'ファラッド'), + (0x3333, 'M', u'フィート'), + (0x3334, 'M', u'ブッシェル'), + (0x3335, 'M', u'フラン'), + (0x3336, 'M', u'ヘクタール'), + (0x3337, 'M', u'ペソ'), + (0x3338, 'M', u'ペニヒ'), + (0x3339, 'M', u'ヘルツ'), + (0x333A, 'M', u'ペンス'), + (0x333B, 'M', u'ページ'), + (0x333C, 'M', u'ベータ'), + (0x333D, 'M', u'ポイント'), + (0x333E, 'M', u'ボルト'), + (0x333F, 'M', u'ホン'), + (0x3340, 'M', u'ポンド'), + (0x3341, 'M', u'ホール'), + (0x3342, 'M', u'ホーン'), + (0x3343, 'M', u'マイクロ'), + (0x3344, 'M', u'マイル'), + (0x3345, 'M', u'マッハ'), + (0x3346, 'M', u'マルク'), + (0x3347, 'M', u'マンション'), + (0x3348, 'M', u'ミクロン'), + (0x3349, 'M', u'ミリ'), + (0x334A, 'M', u'ミリバール'), + (0x334B, 'M', u'メガ'), + (0x334C, 'M', u'メガトン'), + (0x334D, 'M', u'メートル'), + (0x334E, 'M', u'ヤード'), + (0x334F, 'M', u'ヤール'), + (0x3350, 'M', u'ユアン'), + (0x3351, 'M', u'リットル'), + (0x3352, 'M', u'リラ'), + (0x3353, 'M', u'ルピー'), + (0x3354, 'M', u'ルーブル'), + (0x3355, 'M', u'レム'), + (0x3356, 'M', u'レントゲン'), + (0x3357, 'M', u'ワット'), + (0x3358, 'M', u'0点'), + (0x3359, 'M', u'1点'), + (0x335A, 'M', u'2点'), + (0x335B, 'M', u'3点'), + (0x335C, 'M', u'4点'), + (0x335D, 'M', u'5点'), + (0x335E, 'M', u'6点'), + (0x335F, 'M', u'7点'), + (0x3360, 'M', u'8点'), + (0x3361, 'M', u'9点'), + (0x3362, 'M', u'10点'), + (0x3363, 'M', u'11点'), + (0x3364, 'M', u'12点'), + (0x3365, 'M', u'13点'), + (0x3366, 'M', u'14点'), + (0x3367, 'M', u'15点'), + (0x3368, 'M', u'16点'), + (0x3369, 'M', u'17点'), + (0x336A, 'M', u'18点'), + (0x336B, 'M', u'19点'), + (0x336C, 'M', u'20点'), + (0x336D, 'M', u'21点'), + (0x336E, 'M', u'22点'), + (0x336F, 'M', u'23点'), + (0x3370, 'M', u'24点'), + (0x3371, 'M', u'hpa'), + (0x3372, 'M', u'da'), + (0x3373, 'M', u'au'), + (0x3374, 'M', u'bar'), + (0x3375, 'M', u'ov'), + (0x3376, 'M', u'pc'), + (0x3377, 'M', u'dm'), + (0x3378, 'M', u'dm2'), + (0x3379, 'M', u'dm3'), + (0x337A, 'M', u'iu'), + (0x337B, 'M', u'平成'), + (0x337C, 'M', u'昭和'), + (0x337D, 'M', u'大正'), + (0x337E, 'M', u'明治'), + (0x337F, 'M', u'株式会社'), + (0x3380, 'M', u'pa'), + (0x3381, 'M', u'na'), + (0x3382, 'M', u'μa'), + (0x3383, 'M', u'ma'), + (0x3384, 'M', u'ka'), + ] + +def _seg_34(): + return [ + (0x3385, 'M', u'kb'), + (0x3386, 'M', u'mb'), + (0x3387, 'M', u'gb'), + (0x3388, 'M', u'cal'), + (0x3389, 'M', u'kcal'), + (0x338A, 'M', u'pf'), + (0x338B, 'M', u'nf'), + (0x338C, 'M', u'μf'), + (0x338D, 'M', u'μg'), + (0x338E, 'M', u'mg'), + (0x338F, 'M', u'kg'), + (0x3390, 'M', u'hz'), + (0x3391, 'M', u'khz'), + (0x3392, 'M', u'mhz'), + (0x3393, 'M', u'ghz'), + (0x3394, 'M', u'thz'), + (0x3395, 'M', u'μl'), + (0x3396, 'M', u'ml'), + (0x3397, 'M', u'dl'), + (0x3398, 'M', u'kl'), + (0x3399, 'M', u'fm'), + (0x339A, 'M', u'nm'), + (0x339B, 'M', u'μm'), + (0x339C, 'M', u'mm'), + (0x339D, 'M', u'cm'), + (0x339E, 'M', u'km'), + (0x339F, 'M', u'mm2'), + (0x33A0, 'M', u'cm2'), + (0x33A1, 'M', u'm2'), + (0x33A2, 'M', u'km2'), + (0x33A3, 'M', u'mm3'), + (0x33A4, 'M', u'cm3'), + (0x33A5, 'M', u'm3'), + (0x33A6, 'M', u'km3'), + (0x33A7, 'M', u'm∕s'), + (0x33A8, 'M', u'm∕s2'), + (0x33A9, 'M', u'pa'), + (0x33AA, 'M', u'kpa'), + (0x33AB, 'M', u'mpa'), + (0x33AC, 'M', u'gpa'), + (0x33AD, 'M', u'rad'), + (0x33AE, 'M', u'rad∕s'), + (0x33AF, 'M', u'rad∕s2'), + (0x33B0, 'M', u'ps'), + (0x33B1, 'M', u'ns'), + (0x33B2, 'M', u'μs'), + (0x33B3, 'M', u'ms'), + (0x33B4, 'M', u'pv'), + (0x33B5, 'M', u'nv'), + (0x33B6, 'M', u'μv'), + (0x33B7, 'M', u'mv'), + (0x33B8, 'M', u'kv'), + (0x33B9, 'M', u'mv'), + (0x33BA, 'M', u'pw'), + (0x33BB, 'M', u'nw'), + (0x33BC, 'M', u'μw'), + (0x33BD, 'M', u'mw'), + (0x33BE, 'M', u'kw'), + (0x33BF, 'M', u'mw'), + (0x33C0, 'M', u'kω'), + (0x33C1, 'M', u'mω'), + (0x33C2, 'X'), + (0x33C3, 'M', u'bq'), + (0x33C4, 'M', u'cc'), + (0x33C5, 'M', u'cd'), + (0x33C6, 'M', u'c∕kg'), + (0x33C7, 'X'), + (0x33C8, 'M', u'db'), + (0x33C9, 'M', u'gy'), + (0x33CA, 'M', u'ha'), + (0x33CB, 'M', u'hp'), + (0x33CC, 'M', u'in'), + (0x33CD, 'M', u'kk'), + (0x33CE, 'M', u'km'), + (0x33CF, 'M', u'kt'), + (0x33D0, 'M', u'lm'), + (0x33D1, 'M', u'ln'), + (0x33D2, 'M', u'log'), + (0x33D3, 'M', u'lx'), + (0x33D4, 'M', u'mb'), + (0x33D5, 'M', u'mil'), + (0x33D6, 'M', u'mol'), + (0x33D7, 'M', u'ph'), + (0x33D8, 'X'), + (0x33D9, 'M', u'ppm'), + (0x33DA, 'M', u'pr'), + (0x33DB, 'M', u'sr'), + (0x33DC, 'M', u'sv'), + (0x33DD, 'M', u'wb'), + (0x33DE, 'M', u'v∕m'), + (0x33DF, 'M', u'a∕m'), + (0x33E0, 'M', u'1日'), + (0x33E1, 'M', u'2日'), + (0x33E2, 'M', u'3日'), + (0x33E3, 'M', u'4日'), + (0x33E4, 'M', u'5日'), + (0x33E5, 'M', u'6日'), + (0x33E6, 'M', u'7日'), + (0x33E7, 'M', u'8日'), + (0x33E8, 'M', u'9日'), + ] + +def _seg_35(): + return [ + (0x33E9, 'M', u'10日'), + (0x33EA, 'M', u'11日'), + (0x33EB, 'M', u'12日'), + (0x33EC, 'M', u'13日'), + (0x33ED, 'M', u'14日'), + (0x33EE, 'M', u'15日'), + (0x33EF, 'M', u'16日'), + (0x33F0, 'M', u'17日'), + (0x33F1, 'M', u'18日'), + (0x33F2, 'M', u'19日'), + (0x33F3, 'M', u'20日'), + (0x33F4, 'M', u'21日'), + (0x33F5, 'M', u'22日'), + (0x33F6, 'M', u'23日'), + (0x33F7, 'M', u'24日'), + (0x33F8, 'M', u'25日'), + (0x33F9, 'M', u'26日'), + (0x33FA, 'M', u'27日'), + (0x33FB, 'M', u'28日'), + (0x33FC, 'M', u'29日'), + (0x33FD, 'M', u'30日'), + (0x33FE, 'M', u'31日'), + (0x33FF, 'M', u'gal'), + (0x3400, 'V'), + (0x4DB6, 'X'), + (0x4DC0, 'V'), + (0x9FEB, 'X'), + (0xA000, 'V'), + (0xA48D, 'X'), + (0xA490, 'V'), + (0xA4C7, 'X'), + (0xA4D0, 'V'), + (0xA62C, 'X'), + (0xA640, 'M', u'ꙁ'), + (0xA641, 'V'), + (0xA642, 'M', u'ꙃ'), + (0xA643, 'V'), + (0xA644, 'M', u'ꙅ'), + (0xA645, 'V'), + (0xA646, 'M', u'ꙇ'), + (0xA647, 'V'), + (0xA648, 'M', u'ꙉ'), + (0xA649, 'V'), + (0xA64A, 'M', u'ꙋ'), + (0xA64B, 'V'), + (0xA64C, 'M', u'ꙍ'), + (0xA64D, 'V'), + (0xA64E, 'M', u'ꙏ'), + (0xA64F, 'V'), + (0xA650, 'M', u'ꙑ'), + (0xA651, 'V'), + (0xA652, 'M', u'ꙓ'), + (0xA653, 'V'), + (0xA654, 'M', u'ꙕ'), + (0xA655, 'V'), + (0xA656, 'M', u'ꙗ'), + (0xA657, 'V'), + (0xA658, 'M', u'ꙙ'), + (0xA659, 'V'), + (0xA65A, 'M', u'ꙛ'), + (0xA65B, 'V'), + (0xA65C, 'M', u'ꙝ'), + (0xA65D, 'V'), + (0xA65E, 'M', u'ꙟ'), + (0xA65F, 'V'), + (0xA660, 'M', u'ꙡ'), + (0xA661, 'V'), + (0xA662, 'M', u'ꙣ'), + (0xA663, 'V'), + (0xA664, 'M', u'ꙥ'), + (0xA665, 'V'), + (0xA666, 'M', u'ꙧ'), + (0xA667, 'V'), + (0xA668, 'M', u'ꙩ'), + (0xA669, 'V'), + (0xA66A, 'M', u'ꙫ'), + (0xA66B, 'V'), + (0xA66C, 'M', u'ꙭ'), + (0xA66D, 'V'), + (0xA680, 'M', u'ꚁ'), + (0xA681, 'V'), + (0xA682, 'M', u'ꚃ'), + (0xA683, 'V'), + (0xA684, 'M', u'ꚅ'), + (0xA685, 'V'), + (0xA686, 'M', u'ꚇ'), + (0xA687, 'V'), + (0xA688, 'M', u'ꚉ'), + (0xA689, 'V'), + (0xA68A, 'M', u'ꚋ'), + (0xA68B, 'V'), + (0xA68C, 'M', u'ꚍ'), + (0xA68D, 'V'), + (0xA68E, 'M', u'ꚏ'), + (0xA68F, 'V'), + (0xA690, 'M', u'ꚑ'), + (0xA691, 'V'), + (0xA692, 'M', u'ꚓ'), + (0xA693, 'V'), + (0xA694, 'M', u'ꚕ'), + ] + +def _seg_36(): + return [ + (0xA695, 'V'), + (0xA696, 'M', u'ꚗ'), + (0xA697, 'V'), + (0xA698, 'M', u'ꚙ'), + (0xA699, 'V'), + (0xA69A, 'M', u'ꚛ'), + (0xA69B, 'V'), + (0xA69C, 'M', u'ъ'), + (0xA69D, 'M', u'ь'), + (0xA69E, 'V'), + (0xA6F8, 'X'), + (0xA700, 'V'), + (0xA722, 'M', u'ꜣ'), + (0xA723, 'V'), + (0xA724, 'M', u'ꜥ'), + (0xA725, 'V'), + (0xA726, 'M', u'ꜧ'), + (0xA727, 'V'), + (0xA728, 'M', u'ꜩ'), + (0xA729, 'V'), + (0xA72A, 'M', u'ꜫ'), + (0xA72B, 'V'), + (0xA72C, 'M', u'ꜭ'), + (0xA72D, 'V'), + (0xA72E, 'M', u'ꜯ'), + (0xA72F, 'V'), + (0xA732, 'M', u'ꜳ'), + (0xA733, 'V'), + (0xA734, 'M', u'ꜵ'), + (0xA735, 'V'), + (0xA736, 'M', u'ꜷ'), + (0xA737, 'V'), + (0xA738, 'M', u'ꜹ'), + (0xA739, 'V'), + (0xA73A, 'M', u'ꜻ'), + (0xA73B, 'V'), + (0xA73C, 'M', u'ꜽ'), + (0xA73D, 'V'), + (0xA73E, 'M', u'ꜿ'), + (0xA73F, 'V'), + (0xA740, 'M', u'ꝁ'), + (0xA741, 'V'), + (0xA742, 'M', u'ꝃ'), + (0xA743, 'V'), + (0xA744, 'M', u'ꝅ'), + (0xA745, 'V'), + (0xA746, 'M', u'ꝇ'), + (0xA747, 'V'), + (0xA748, 'M', u'ꝉ'), + (0xA749, 'V'), + (0xA74A, 'M', u'ꝋ'), + (0xA74B, 'V'), + (0xA74C, 'M', u'ꝍ'), + (0xA74D, 'V'), + (0xA74E, 'M', u'ꝏ'), + (0xA74F, 'V'), + (0xA750, 'M', u'ꝑ'), + (0xA751, 'V'), + (0xA752, 'M', u'ꝓ'), + (0xA753, 'V'), + (0xA754, 'M', u'ꝕ'), + (0xA755, 'V'), + (0xA756, 'M', u'ꝗ'), + (0xA757, 'V'), + (0xA758, 'M', u'ꝙ'), + (0xA759, 'V'), + (0xA75A, 'M', u'ꝛ'), + (0xA75B, 'V'), + (0xA75C, 'M', u'ꝝ'), + (0xA75D, 'V'), + (0xA75E, 'M', u'ꝟ'), + (0xA75F, 'V'), + (0xA760, 'M', u'ꝡ'), + (0xA761, 'V'), + (0xA762, 'M', u'ꝣ'), + (0xA763, 'V'), + (0xA764, 'M', u'ꝥ'), + (0xA765, 'V'), + (0xA766, 'M', u'ꝧ'), + (0xA767, 'V'), + (0xA768, 'M', u'ꝩ'), + (0xA769, 'V'), + (0xA76A, 'M', u'ꝫ'), + (0xA76B, 'V'), + (0xA76C, 'M', u'ꝭ'), + (0xA76D, 'V'), + (0xA76E, 'M', u'ꝯ'), + (0xA76F, 'V'), + (0xA770, 'M', u'ꝯ'), + (0xA771, 'V'), + (0xA779, 'M', u'ꝺ'), + (0xA77A, 'V'), + (0xA77B, 'M', u'ꝼ'), + (0xA77C, 'V'), + (0xA77D, 'M', u'ᵹ'), + (0xA77E, 'M', u'ꝿ'), + (0xA77F, 'V'), + (0xA780, 'M', u'ꞁ'), + (0xA781, 'V'), + (0xA782, 'M', u'ꞃ'), + ] + +def _seg_37(): + return [ + (0xA783, 'V'), + (0xA784, 'M', u'ꞅ'), + (0xA785, 'V'), + (0xA786, 'M', u'ꞇ'), + (0xA787, 'V'), + (0xA78B, 'M', u'ꞌ'), + (0xA78C, 'V'), + (0xA78D, 'M', u'ɥ'), + (0xA78E, 'V'), + (0xA790, 'M', u'ꞑ'), + (0xA791, 'V'), + (0xA792, 'M', u'ꞓ'), + (0xA793, 'V'), + (0xA796, 'M', u'ꞗ'), + (0xA797, 'V'), + (0xA798, 'M', u'ꞙ'), + (0xA799, 'V'), + (0xA79A, 'M', u'ꞛ'), + (0xA79B, 'V'), + (0xA79C, 'M', u'ꞝ'), + (0xA79D, 'V'), + (0xA79E, 'M', u'ꞟ'), + (0xA79F, 'V'), + (0xA7A0, 'M', u'ꞡ'), + (0xA7A1, 'V'), + (0xA7A2, 'M', u'ꞣ'), + (0xA7A3, 'V'), + (0xA7A4, 'M', u'ꞥ'), + (0xA7A5, 'V'), + (0xA7A6, 'M', u'ꞧ'), + (0xA7A7, 'V'), + (0xA7A8, 'M', u'ꞩ'), + (0xA7A9, 'V'), + (0xA7AA, 'M', u'ɦ'), + (0xA7AB, 'M', u'ɜ'), + (0xA7AC, 'M', u'ɡ'), + (0xA7AD, 'M', u'ɬ'), + (0xA7AE, 'M', u'ɪ'), + (0xA7AF, 'X'), + (0xA7B0, 'M', u'ʞ'), + (0xA7B1, 'M', u'ʇ'), + (0xA7B2, 'M', u'ʝ'), + (0xA7B3, 'M', u'ꭓ'), + (0xA7B4, 'M', u'ꞵ'), + (0xA7B5, 'V'), + (0xA7B6, 'M', u'ꞷ'), + (0xA7B7, 'V'), + (0xA7B8, 'X'), + (0xA7F7, 'V'), + (0xA7F8, 'M', u'ħ'), + (0xA7F9, 'M', u'œ'), + (0xA7FA, 'V'), + (0xA82C, 'X'), + (0xA830, 'V'), + (0xA83A, 'X'), + (0xA840, 'V'), + (0xA878, 'X'), + (0xA880, 'V'), + (0xA8C6, 'X'), + (0xA8CE, 'V'), + (0xA8DA, 'X'), + (0xA8E0, 'V'), + (0xA8FE, 'X'), + (0xA900, 'V'), + (0xA954, 'X'), + (0xA95F, 'V'), + (0xA97D, 'X'), + (0xA980, 'V'), + (0xA9CE, 'X'), + (0xA9CF, 'V'), + (0xA9DA, 'X'), + (0xA9DE, 'V'), + (0xA9FF, 'X'), + (0xAA00, 'V'), + (0xAA37, 'X'), + (0xAA40, 'V'), + (0xAA4E, 'X'), + (0xAA50, 'V'), + (0xAA5A, 'X'), + (0xAA5C, 'V'), + (0xAAC3, 'X'), + (0xAADB, 'V'), + (0xAAF7, 'X'), + (0xAB01, 'V'), + (0xAB07, 'X'), + (0xAB09, 'V'), + (0xAB0F, 'X'), + (0xAB11, 'V'), + (0xAB17, 'X'), + (0xAB20, 'V'), + (0xAB27, 'X'), + (0xAB28, 'V'), + (0xAB2F, 'X'), + (0xAB30, 'V'), + (0xAB5C, 'M', u'ꜧ'), + (0xAB5D, 'M', u'ꬷ'), + (0xAB5E, 'M', u'ɫ'), + (0xAB5F, 'M', u'ꭒ'), + (0xAB60, 'V'), + (0xAB66, 'X'), + ] + +def _seg_38(): + return [ + (0xAB70, 'M', u'Ꭰ'), + (0xAB71, 'M', u'Ꭱ'), + (0xAB72, 'M', u'Ꭲ'), + (0xAB73, 'M', u'Ꭳ'), + (0xAB74, 'M', u'Ꭴ'), + (0xAB75, 'M', u'Ꭵ'), + (0xAB76, 'M', u'Ꭶ'), + (0xAB77, 'M', u'Ꭷ'), + (0xAB78, 'M', u'Ꭸ'), + (0xAB79, 'M', u'Ꭹ'), + (0xAB7A, 'M', u'Ꭺ'), + (0xAB7B, 'M', u'Ꭻ'), + (0xAB7C, 'M', u'Ꭼ'), + (0xAB7D, 'M', u'Ꭽ'), + (0xAB7E, 'M', u'Ꭾ'), + (0xAB7F, 'M', u'Ꭿ'), + (0xAB80, 'M', u'Ꮀ'), + (0xAB81, 'M', u'Ꮁ'), + (0xAB82, 'M', u'Ꮂ'), + (0xAB83, 'M', u'Ꮃ'), + (0xAB84, 'M', u'Ꮄ'), + (0xAB85, 'M', u'Ꮅ'), + (0xAB86, 'M', u'Ꮆ'), + (0xAB87, 'M', u'Ꮇ'), + (0xAB88, 'M', u'Ꮈ'), + (0xAB89, 'M', u'Ꮉ'), + (0xAB8A, 'M', u'Ꮊ'), + (0xAB8B, 'M', u'Ꮋ'), + (0xAB8C, 'M', u'Ꮌ'), + (0xAB8D, 'M', u'Ꮍ'), + (0xAB8E, 'M', u'Ꮎ'), + (0xAB8F, 'M', u'Ꮏ'), + (0xAB90, 'M', u'Ꮐ'), + (0xAB91, 'M', u'Ꮑ'), + (0xAB92, 'M', u'Ꮒ'), + (0xAB93, 'M', u'Ꮓ'), + (0xAB94, 'M', u'Ꮔ'), + (0xAB95, 'M', u'Ꮕ'), + (0xAB96, 'M', u'Ꮖ'), + (0xAB97, 'M', u'Ꮗ'), + (0xAB98, 'M', u'Ꮘ'), + (0xAB99, 'M', u'Ꮙ'), + (0xAB9A, 'M', u'Ꮚ'), + (0xAB9B, 'M', u'Ꮛ'), + (0xAB9C, 'M', u'Ꮜ'), + (0xAB9D, 'M', u'Ꮝ'), + (0xAB9E, 'M', u'Ꮞ'), + (0xAB9F, 'M', u'Ꮟ'), + (0xABA0, 'M', u'Ꮠ'), + (0xABA1, 'M', u'Ꮡ'), + (0xABA2, 'M', u'Ꮢ'), + (0xABA3, 'M', u'Ꮣ'), + (0xABA4, 'M', u'Ꮤ'), + (0xABA5, 'M', u'Ꮥ'), + (0xABA6, 'M', u'Ꮦ'), + (0xABA7, 'M', u'Ꮧ'), + (0xABA8, 'M', u'Ꮨ'), + (0xABA9, 'M', u'Ꮩ'), + (0xABAA, 'M', u'Ꮪ'), + (0xABAB, 'M', u'Ꮫ'), + (0xABAC, 'M', u'Ꮬ'), + (0xABAD, 'M', u'Ꮭ'), + (0xABAE, 'M', u'Ꮮ'), + (0xABAF, 'M', u'Ꮯ'), + (0xABB0, 'M', u'Ꮰ'), + (0xABB1, 'M', u'Ꮱ'), + (0xABB2, 'M', u'Ꮲ'), + (0xABB3, 'M', u'Ꮳ'), + (0xABB4, 'M', u'Ꮴ'), + (0xABB5, 'M', u'Ꮵ'), + (0xABB6, 'M', u'Ꮶ'), + (0xABB7, 'M', u'Ꮷ'), + (0xABB8, 'M', u'Ꮸ'), + (0xABB9, 'M', u'Ꮹ'), + (0xABBA, 'M', u'Ꮺ'), + (0xABBB, 'M', u'Ꮻ'), + (0xABBC, 'M', u'Ꮼ'), + (0xABBD, 'M', u'Ꮽ'), + (0xABBE, 'M', u'Ꮾ'), + (0xABBF, 'M', u'Ꮿ'), + (0xABC0, 'V'), + (0xABEE, 'X'), + (0xABF0, 'V'), + (0xABFA, 'X'), + (0xAC00, 'V'), + (0xD7A4, 'X'), + (0xD7B0, 'V'), + (0xD7C7, 'X'), + (0xD7CB, 'V'), + (0xD7FC, 'X'), + (0xF900, 'M', u'豈'), + (0xF901, 'M', u'更'), + (0xF902, 'M', u'車'), + (0xF903, 'M', u'賈'), + (0xF904, 'M', u'滑'), + (0xF905, 'M', u'串'), + (0xF906, 'M', u'句'), + (0xF907, 'M', u'龜'), + (0xF909, 'M', u'契'), + (0xF90A, 'M', u'金'), + ] + +def _seg_39(): + return [ + (0xF90B, 'M', u'喇'), + (0xF90C, 'M', u'奈'), + (0xF90D, 'M', u'懶'), + (0xF90E, 'M', u'癩'), + (0xF90F, 'M', u'羅'), + (0xF910, 'M', u'蘿'), + (0xF911, 'M', u'螺'), + (0xF912, 'M', u'裸'), + (0xF913, 'M', u'邏'), + (0xF914, 'M', u'樂'), + (0xF915, 'M', u'洛'), + (0xF916, 'M', u'烙'), + (0xF917, 'M', u'珞'), + (0xF918, 'M', u'落'), + (0xF919, 'M', u'酪'), + (0xF91A, 'M', u'駱'), + (0xF91B, 'M', u'亂'), + (0xF91C, 'M', u'卵'), + (0xF91D, 'M', u'欄'), + (0xF91E, 'M', u'爛'), + (0xF91F, 'M', u'蘭'), + (0xF920, 'M', u'鸞'), + (0xF921, 'M', u'嵐'), + (0xF922, 'M', u'濫'), + (0xF923, 'M', u'藍'), + (0xF924, 'M', u'襤'), + (0xF925, 'M', u'拉'), + (0xF926, 'M', u'臘'), + (0xF927, 'M', u'蠟'), + (0xF928, 'M', u'廊'), + (0xF929, 'M', u'朗'), + (0xF92A, 'M', u'浪'), + (0xF92B, 'M', u'狼'), + (0xF92C, 'M', u'郎'), + (0xF92D, 'M', u'來'), + (0xF92E, 'M', u'冷'), + (0xF92F, 'M', u'勞'), + (0xF930, 'M', u'擄'), + (0xF931, 'M', u'櫓'), + (0xF932, 'M', u'爐'), + (0xF933, 'M', u'盧'), + (0xF934, 'M', u'老'), + (0xF935, 'M', u'蘆'), + (0xF936, 'M', u'虜'), + (0xF937, 'M', u'路'), + (0xF938, 'M', u'露'), + (0xF939, 'M', u'魯'), + (0xF93A, 'M', u'鷺'), + (0xF93B, 'M', u'碌'), + (0xF93C, 'M', u'祿'), + (0xF93D, 'M', u'綠'), + (0xF93E, 'M', u'菉'), + (0xF93F, 'M', u'錄'), + (0xF940, 'M', u'鹿'), + (0xF941, 'M', u'論'), + (0xF942, 'M', u'壟'), + (0xF943, 'M', u'弄'), + (0xF944, 'M', u'籠'), + (0xF945, 'M', u'聾'), + (0xF946, 'M', u'牢'), + (0xF947, 'M', u'磊'), + (0xF948, 'M', u'賂'), + (0xF949, 'M', u'雷'), + (0xF94A, 'M', u'壘'), + (0xF94B, 'M', u'屢'), + (0xF94C, 'M', u'樓'), + (0xF94D, 'M', u'淚'), + (0xF94E, 'M', u'漏'), + (0xF94F, 'M', u'累'), + (0xF950, 'M', u'縷'), + (0xF951, 'M', u'陋'), + (0xF952, 'M', u'勒'), + (0xF953, 'M', u'肋'), + (0xF954, 'M', u'凜'), + (0xF955, 'M', u'凌'), + (0xF956, 'M', u'稜'), + (0xF957, 'M', u'綾'), + (0xF958, 'M', u'菱'), + (0xF959, 'M', u'陵'), + (0xF95A, 'M', u'讀'), + (0xF95B, 'M', u'拏'), + (0xF95C, 'M', u'樂'), + (0xF95D, 'M', u'諾'), + (0xF95E, 'M', u'丹'), + (0xF95F, 'M', u'寧'), + (0xF960, 'M', u'怒'), + (0xF961, 'M', u'率'), + (0xF962, 'M', u'異'), + (0xF963, 'M', u'北'), + (0xF964, 'M', u'磻'), + (0xF965, 'M', u'便'), + (0xF966, 'M', u'復'), + (0xF967, 'M', u'不'), + (0xF968, 'M', u'泌'), + (0xF969, 'M', u'數'), + (0xF96A, 'M', u'索'), + (0xF96B, 'M', u'參'), + (0xF96C, 'M', u'塞'), + (0xF96D, 'M', u'省'), + (0xF96E, 'M', u'葉'), + ] + +def _seg_40(): + return [ + (0xF96F, 'M', u'說'), + (0xF970, 'M', u'殺'), + (0xF971, 'M', u'辰'), + (0xF972, 'M', u'沈'), + (0xF973, 'M', u'拾'), + (0xF974, 'M', u'若'), + (0xF975, 'M', u'掠'), + (0xF976, 'M', u'略'), + (0xF977, 'M', u'亮'), + (0xF978, 'M', u'兩'), + (0xF979, 'M', u'凉'), + (0xF97A, 'M', u'梁'), + (0xF97B, 'M', u'糧'), + (0xF97C, 'M', u'良'), + (0xF97D, 'M', u'諒'), + (0xF97E, 'M', u'量'), + (0xF97F, 'M', u'勵'), + (0xF980, 'M', u'呂'), + (0xF981, 'M', u'女'), + (0xF982, 'M', u'廬'), + (0xF983, 'M', u'旅'), + (0xF984, 'M', u'濾'), + (0xF985, 'M', u'礪'), + (0xF986, 'M', u'閭'), + (0xF987, 'M', u'驪'), + (0xF988, 'M', u'麗'), + (0xF989, 'M', u'黎'), + (0xF98A, 'M', u'力'), + (0xF98B, 'M', u'曆'), + (0xF98C, 'M', u'歷'), + (0xF98D, 'M', u'轢'), + (0xF98E, 'M', u'年'), + (0xF98F, 'M', u'憐'), + (0xF990, 'M', u'戀'), + (0xF991, 'M', u'撚'), + (0xF992, 'M', u'漣'), + (0xF993, 'M', u'煉'), + (0xF994, 'M', u'璉'), + (0xF995, 'M', u'秊'), + (0xF996, 'M', u'練'), + (0xF997, 'M', u'聯'), + (0xF998, 'M', u'輦'), + (0xF999, 'M', u'蓮'), + (0xF99A, 'M', u'連'), + (0xF99B, 'M', u'鍊'), + (0xF99C, 'M', u'列'), + (0xF99D, 'M', u'劣'), + (0xF99E, 'M', u'咽'), + (0xF99F, 'M', u'烈'), + (0xF9A0, 'M', u'裂'), + (0xF9A1, 'M', u'說'), + (0xF9A2, 'M', u'廉'), + (0xF9A3, 'M', u'念'), + (0xF9A4, 'M', u'捻'), + (0xF9A5, 'M', u'殮'), + (0xF9A6, 'M', u'簾'), + (0xF9A7, 'M', u'獵'), + (0xF9A8, 'M', u'令'), + (0xF9A9, 'M', u'囹'), + (0xF9AA, 'M', u'寧'), + (0xF9AB, 'M', u'嶺'), + (0xF9AC, 'M', u'怜'), + (0xF9AD, 'M', u'玲'), + (0xF9AE, 'M', u'瑩'), + (0xF9AF, 'M', u'羚'), + (0xF9B0, 'M', u'聆'), + (0xF9B1, 'M', u'鈴'), + (0xF9B2, 'M', u'零'), + (0xF9B3, 'M', u'靈'), + (0xF9B4, 'M', u'領'), + (0xF9B5, 'M', u'例'), + (0xF9B6, 'M', u'禮'), + (0xF9B7, 'M', u'醴'), + (0xF9B8, 'M', u'隸'), + (0xF9B9, 'M', u'惡'), + (0xF9BA, 'M', u'了'), + (0xF9BB, 'M', u'僚'), + (0xF9BC, 'M', u'寮'), + (0xF9BD, 'M', u'尿'), + (0xF9BE, 'M', u'料'), + (0xF9BF, 'M', u'樂'), + (0xF9C0, 'M', u'燎'), + (0xF9C1, 'M', u'療'), + (0xF9C2, 'M', u'蓼'), + (0xF9C3, 'M', u'遼'), + (0xF9C4, 'M', u'龍'), + (0xF9C5, 'M', u'暈'), + (0xF9C6, 'M', u'阮'), + (0xF9C7, 'M', u'劉'), + (0xF9C8, 'M', u'杻'), + (0xF9C9, 'M', u'柳'), + (0xF9CA, 'M', u'流'), + (0xF9CB, 'M', u'溜'), + (0xF9CC, 'M', u'琉'), + (0xF9CD, 'M', u'留'), + (0xF9CE, 'M', u'硫'), + (0xF9CF, 'M', u'紐'), + (0xF9D0, 'M', u'類'), + (0xF9D1, 'M', u'六'), + (0xF9D2, 'M', u'戮'), + ] + +def _seg_41(): + return [ + (0xF9D3, 'M', u'陸'), + (0xF9D4, 'M', u'倫'), + (0xF9D5, 'M', u'崙'), + (0xF9D6, 'M', u'淪'), + (0xF9D7, 'M', u'輪'), + (0xF9D8, 'M', u'律'), + (0xF9D9, 'M', u'慄'), + (0xF9DA, 'M', u'栗'), + (0xF9DB, 'M', u'率'), + (0xF9DC, 'M', u'隆'), + (0xF9DD, 'M', u'利'), + (0xF9DE, 'M', u'吏'), + (0xF9DF, 'M', u'履'), + (0xF9E0, 'M', u'易'), + (0xF9E1, 'M', u'李'), + (0xF9E2, 'M', u'梨'), + (0xF9E3, 'M', u'泥'), + (0xF9E4, 'M', u'理'), + (0xF9E5, 'M', u'痢'), + (0xF9E6, 'M', u'罹'), + (0xF9E7, 'M', u'裏'), + (0xF9E8, 'M', u'裡'), + (0xF9E9, 'M', u'里'), + (0xF9EA, 'M', u'離'), + (0xF9EB, 'M', u'匿'), + (0xF9EC, 'M', u'溺'), + (0xF9ED, 'M', u'吝'), + (0xF9EE, 'M', u'燐'), + (0xF9EF, 'M', u'璘'), + (0xF9F0, 'M', u'藺'), + (0xF9F1, 'M', u'隣'), + (0xF9F2, 'M', u'鱗'), + (0xF9F3, 'M', u'麟'), + (0xF9F4, 'M', u'林'), + (0xF9F5, 'M', u'淋'), + (0xF9F6, 'M', u'臨'), + (0xF9F7, 'M', u'立'), + (0xF9F8, 'M', u'笠'), + (0xF9F9, 'M', u'粒'), + (0xF9FA, 'M', u'狀'), + (0xF9FB, 'M', u'炙'), + (0xF9FC, 'M', u'識'), + (0xF9FD, 'M', u'什'), + (0xF9FE, 'M', u'茶'), + (0xF9FF, 'M', u'刺'), + (0xFA00, 'M', u'切'), + (0xFA01, 'M', u'度'), + (0xFA02, 'M', u'拓'), + (0xFA03, 'M', u'糖'), + (0xFA04, 'M', u'宅'), + (0xFA05, 'M', u'洞'), + (0xFA06, 'M', u'暴'), + (0xFA07, 'M', u'輻'), + (0xFA08, 'M', u'行'), + (0xFA09, 'M', u'降'), + (0xFA0A, 'M', u'見'), + (0xFA0B, 'M', u'廓'), + (0xFA0C, 'M', u'兀'), + (0xFA0D, 'M', u'嗀'), + (0xFA0E, 'V'), + (0xFA10, 'M', u'塚'), + (0xFA11, 'V'), + (0xFA12, 'M', u'晴'), + (0xFA13, 'V'), + (0xFA15, 'M', u'凞'), + (0xFA16, 'M', u'猪'), + (0xFA17, 'M', u'益'), + (0xFA18, 'M', u'礼'), + (0xFA19, 'M', u'神'), + (0xFA1A, 'M', u'祥'), + (0xFA1B, 'M', u'福'), + (0xFA1C, 'M', u'靖'), + (0xFA1D, 'M', u'精'), + (0xFA1E, 'M', u'羽'), + (0xFA1F, 'V'), + (0xFA20, 'M', u'蘒'), + (0xFA21, 'V'), + (0xFA22, 'M', u'諸'), + (0xFA23, 'V'), + (0xFA25, 'M', u'逸'), + (0xFA26, 'M', u'都'), + (0xFA27, 'V'), + (0xFA2A, 'M', u'飯'), + (0xFA2B, 'M', u'飼'), + (0xFA2C, 'M', u'館'), + (0xFA2D, 'M', u'鶴'), + (0xFA2E, 'M', u'郞'), + (0xFA2F, 'M', u'隷'), + (0xFA30, 'M', u'侮'), + (0xFA31, 'M', u'僧'), + (0xFA32, 'M', u'免'), + (0xFA33, 'M', u'勉'), + (0xFA34, 'M', u'勤'), + (0xFA35, 'M', u'卑'), + (0xFA36, 'M', u'喝'), + (0xFA37, 'M', u'嘆'), + (0xFA38, 'M', u'器'), + (0xFA39, 'M', u'塀'), + (0xFA3A, 'M', u'墨'), + (0xFA3B, 'M', u'層'), + ] + +def _seg_42(): + return [ + (0xFA3C, 'M', u'屮'), + (0xFA3D, 'M', u'悔'), + (0xFA3E, 'M', u'慨'), + (0xFA3F, 'M', u'憎'), + (0xFA40, 'M', u'懲'), + (0xFA41, 'M', u'敏'), + (0xFA42, 'M', u'既'), + (0xFA43, 'M', u'暑'), + (0xFA44, 'M', u'梅'), + (0xFA45, 'M', u'海'), + (0xFA46, 'M', u'渚'), + (0xFA47, 'M', u'漢'), + (0xFA48, 'M', u'煮'), + (0xFA49, 'M', u'爫'), + (0xFA4A, 'M', u'琢'), + (0xFA4B, 'M', u'碑'), + (0xFA4C, 'M', u'社'), + (0xFA4D, 'M', u'祉'), + (0xFA4E, 'M', u'祈'), + (0xFA4F, 'M', u'祐'), + (0xFA50, 'M', u'祖'), + (0xFA51, 'M', u'祝'), + (0xFA52, 'M', u'禍'), + (0xFA53, 'M', u'禎'), + (0xFA54, 'M', u'穀'), + (0xFA55, 'M', u'突'), + (0xFA56, 'M', u'節'), + (0xFA57, 'M', u'練'), + (0xFA58, 'M', u'縉'), + (0xFA59, 'M', u'繁'), + (0xFA5A, 'M', u'署'), + (0xFA5B, 'M', u'者'), + (0xFA5C, 'M', u'臭'), + (0xFA5D, 'M', u'艹'), + (0xFA5F, 'M', u'著'), + (0xFA60, 'M', u'褐'), + (0xFA61, 'M', u'視'), + (0xFA62, 'M', u'謁'), + (0xFA63, 'M', u'謹'), + (0xFA64, 'M', u'賓'), + (0xFA65, 'M', u'贈'), + (0xFA66, 'M', u'辶'), + (0xFA67, 'M', u'逸'), + (0xFA68, 'M', u'難'), + (0xFA69, 'M', u'響'), + (0xFA6A, 'M', u'頻'), + (0xFA6B, 'M', u'恵'), + (0xFA6C, 'M', u'𤋮'), + (0xFA6D, 'M', u'舘'), + (0xFA6E, 'X'), + (0xFA70, 'M', u'並'), + (0xFA71, 'M', u'况'), + (0xFA72, 'M', u'全'), + (0xFA73, 'M', u'侀'), + (0xFA74, 'M', u'充'), + (0xFA75, 'M', u'冀'), + (0xFA76, 'M', u'勇'), + (0xFA77, 'M', u'勺'), + (0xFA78, 'M', u'喝'), + (0xFA79, 'M', u'啕'), + (0xFA7A, 'M', u'喙'), + (0xFA7B, 'M', u'嗢'), + (0xFA7C, 'M', u'塚'), + (0xFA7D, 'M', u'墳'), + (0xFA7E, 'M', u'奄'), + (0xFA7F, 'M', u'奔'), + (0xFA80, 'M', u'婢'), + (0xFA81, 'M', u'嬨'), + (0xFA82, 'M', u'廒'), + (0xFA83, 'M', u'廙'), + (0xFA84, 'M', u'彩'), + (0xFA85, 'M', u'徭'), + (0xFA86, 'M', u'惘'), + (0xFA87, 'M', u'慎'), + (0xFA88, 'M', u'愈'), + (0xFA89, 'M', u'憎'), + (0xFA8A, 'M', u'慠'), + (0xFA8B, 'M', u'懲'), + (0xFA8C, 'M', u'戴'), + (0xFA8D, 'M', u'揄'), + (0xFA8E, 'M', u'搜'), + (0xFA8F, 'M', u'摒'), + (0xFA90, 'M', u'敖'), + (0xFA91, 'M', u'晴'), + (0xFA92, 'M', u'朗'), + (0xFA93, 'M', u'望'), + (0xFA94, 'M', u'杖'), + (0xFA95, 'M', u'歹'), + (0xFA96, 'M', u'殺'), + (0xFA97, 'M', u'流'), + (0xFA98, 'M', u'滛'), + (0xFA99, 'M', u'滋'), + (0xFA9A, 'M', u'漢'), + (0xFA9B, 'M', u'瀞'), + (0xFA9C, 'M', u'煮'), + (0xFA9D, 'M', u'瞧'), + (0xFA9E, 'M', u'爵'), + (0xFA9F, 'M', u'犯'), + (0xFAA0, 'M', u'猪'), + (0xFAA1, 'M', u'瑱'), + ] + +def _seg_43(): + return [ + (0xFAA2, 'M', u'甆'), + (0xFAA3, 'M', u'画'), + (0xFAA4, 'M', u'瘝'), + (0xFAA5, 'M', u'瘟'), + (0xFAA6, 'M', u'益'), + (0xFAA7, 'M', u'盛'), + (0xFAA8, 'M', u'直'), + (0xFAA9, 'M', u'睊'), + (0xFAAA, 'M', u'着'), + (0xFAAB, 'M', u'磌'), + (0xFAAC, 'M', u'窱'), + (0xFAAD, 'M', u'節'), + (0xFAAE, 'M', u'类'), + (0xFAAF, 'M', u'絛'), + (0xFAB0, 'M', u'練'), + (0xFAB1, 'M', u'缾'), + (0xFAB2, 'M', u'者'), + (0xFAB3, 'M', u'荒'), + (0xFAB4, 'M', u'華'), + (0xFAB5, 'M', u'蝹'), + (0xFAB6, 'M', u'襁'), + (0xFAB7, 'M', u'覆'), + (0xFAB8, 'M', u'視'), + (0xFAB9, 'M', u'調'), + (0xFABA, 'M', u'諸'), + (0xFABB, 'M', u'請'), + (0xFABC, 'M', u'謁'), + (0xFABD, 'M', u'諾'), + (0xFABE, 'M', u'諭'), + (0xFABF, 'M', u'謹'), + (0xFAC0, 'M', u'變'), + (0xFAC1, 'M', u'贈'), + (0xFAC2, 'M', u'輸'), + (0xFAC3, 'M', u'遲'), + (0xFAC4, 'M', u'醙'), + (0xFAC5, 'M', u'鉶'), + (0xFAC6, 'M', u'陼'), + (0xFAC7, 'M', u'難'), + (0xFAC8, 'M', u'靖'), + (0xFAC9, 'M', u'韛'), + (0xFACA, 'M', u'響'), + (0xFACB, 'M', u'頋'), + (0xFACC, 'M', u'頻'), + (0xFACD, 'M', u'鬒'), + (0xFACE, 'M', u'龜'), + (0xFACF, 'M', u'𢡊'), + (0xFAD0, 'M', u'𢡄'), + (0xFAD1, 'M', u'𣏕'), + (0xFAD2, 'M', u'㮝'), + (0xFAD3, 'M', u'䀘'), + (0xFAD4, 'M', u'䀹'), + (0xFAD5, 'M', u'𥉉'), + (0xFAD6, 'M', u'𥳐'), + (0xFAD7, 'M', u'𧻓'), + (0xFAD8, 'M', u'齃'), + (0xFAD9, 'M', u'龎'), + (0xFADA, 'X'), + (0xFB00, 'M', u'ff'), + (0xFB01, 'M', u'fi'), + (0xFB02, 'M', u'fl'), + (0xFB03, 'M', u'ffi'), + (0xFB04, 'M', u'ffl'), + (0xFB05, 'M', u'st'), + (0xFB07, 'X'), + (0xFB13, 'M', u'մն'), + (0xFB14, 'M', u'մե'), + (0xFB15, 'M', u'մի'), + (0xFB16, 'M', u'վն'), + (0xFB17, 'M', u'մխ'), + (0xFB18, 'X'), + (0xFB1D, 'M', u'יִ'), + (0xFB1E, 'V'), + (0xFB1F, 'M', u'ײַ'), + (0xFB20, 'M', u'ע'), + (0xFB21, 'M', u'א'), + (0xFB22, 'M', u'ד'), + (0xFB23, 'M', u'ה'), + (0xFB24, 'M', u'כ'), + (0xFB25, 'M', u'ל'), + (0xFB26, 'M', u'ם'), + (0xFB27, 'M', u'ר'), + (0xFB28, 'M', u'ת'), + (0xFB29, '3', u'+'), + (0xFB2A, 'M', u'שׁ'), + (0xFB2B, 'M', u'שׂ'), + (0xFB2C, 'M', u'שּׁ'), + (0xFB2D, 'M', u'שּׂ'), + (0xFB2E, 'M', u'אַ'), + (0xFB2F, 'M', u'אָ'), + (0xFB30, 'M', u'אּ'), + (0xFB31, 'M', u'בּ'), + (0xFB32, 'M', u'גּ'), + (0xFB33, 'M', u'דּ'), + (0xFB34, 'M', u'הּ'), + (0xFB35, 'M', u'וּ'), + (0xFB36, 'M', u'זּ'), + (0xFB37, 'X'), + (0xFB38, 'M', u'טּ'), + (0xFB39, 'M', u'יּ'), + (0xFB3A, 'M', u'ךּ'), + ] + +def _seg_44(): + return [ + (0xFB3B, 'M', u'כּ'), + (0xFB3C, 'M', u'לּ'), + (0xFB3D, 'X'), + (0xFB3E, 'M', u'מּ'), + (0xFB3F, 'X'), + (0xFB40, 'M', u'נּ'), + (0xFB41, 'M', u'סּ'), + (0xFB42, 'X'), + (0xFB43, 'M', u'ףּ'), + (0xFB44, 'M', u'פּ'), + (0xFB45, 'X'), + (0xFB46, 'M', u'צּ'), + (0xFB47, 'M', u'קּ'), + (0xFB48, 'M', u'רּ'), + (0xFB49, 'M', u'שּ'), + (0xFB4A, 'M', u'תּ'), + (0xFB4B, 'M', u'וֹ'), + (0xFB4C, 'M', u'בֿ'), + (0xFB4D, 'M', u'כֿ'), + (0xFB4E, 'M', u'פֿ'), + (0xFB4F, 'M', u'אל'), + (0xFB50, 'M', u'ٱ'), + (0xFB52, 'M', u'ٻ'), + (0xFB56, 'M', u'پ'), + (0xFB5A, 'M', u'ڀ'), + (0xFB5E, 'M', u'ٺ'), + (0xFB62, 'M', u'ٿ'), + (0xFB66, 'M', u'ٹ'), + (0xFB6A, 'M', u'ڤ'), + (0xFB6E, 'M', u'ڦ'), + (0xFB72, 'M', u'ڄ'), + (0xFB76, 'M', u'ڃ'), + (0xFB7A, 'M', u'چ'), + (0xFB7E, 'M', u'ڇ'), + (0xFB82, 'M', u'ڍ'), + (0xFB84, 'M', u'ڌ'), + (0xFB86, 'M', u'ڎ'), + (0xFB88, 'M', u'ڈ'), + (0xFB8A, 'M', u'ژ'), + (0xFB8C, 'M', u'ڑ'), + (0xFB8E, 'M', u'ک'), + (0xFB92, 'M', u'گ'), + (0xFB96, 'M', u'ڳ'), + (0xFB9A, 'M', u'ڱ'), + (0xFB9E, 'M', u'ں'), + (0xFBA0, 'M', u'ڻ'), + (0xFBA4, 'M', u'ۀ'), + (0xFBA6, 'M', u'ہ'), + (0xFBAA, 'M', u'ھ'), + (0xFBAE, 'M', u'ے'), + (0xFBB0, 'M', u'ۓ'), + (0xFBB2, 'V'), + (0xFBC2, 'X'), + (0xFBD3, 'M', u'ڭ'), + (0xFBD7, 'M', u'ۇ'), + (0xFBD9, 'M', u'ۆ'), + (0xFBDB, 'M', u'ۈ'), + (0xFBDD, 'M', u'ۇٴ'), + (0xFBDE, 'M', u'ۋ'), + (0xFBE0, 'M', u'ۅ'), + (0xFBE2, 'M', u'ۉ'), + (0xFBE4, 'M', u'ې'), + (0xFBE8, 'M', u'ى'), + (0xFBEA, 'M', u'ئا'), + (0xFBEC, 'M', u'ئە'), + (0xFBEE, 'M', u'ئو'), + (0xFBF0, 'M', u'ئۇ'), + (0xFBF2, 'M', u'ئۆ'), + (0xFBF4, 'M', u'ئۈ'), + (0xFBF6, 'M', u'ئې'), + (0xFBF9, 'M', u'ئى'), + (0xFBFC, 'M', u'ی'), + (0xFC00, 'M', u'ئج'), + (0xFC01, 'M', u'ئح'), + (0xFC02, 'M', u'ئم'), + (0xFC03, 'M', u'ئى'), + (0xFC04, 'M', u'ئي'), + (0xFC05, 'M', u'بج'), + (0xFC06, 'M', u'بح'), + (0xFC07, 'M', u'بخ'), + (0xFC08, 'M', u'بم'), + (0xFC09, 'M', u'بى'), + (0xFC0A, 'M', u'بي'), + (0xFC0B, 'M', u'تج'), + (0xFC0C, 'M', u'تح'), + (0xFC0D, 'M', u'تخ'), + (0xFC0E, 'M', u'تم'), + (0xFC0F, 'M', u'تى'), + (0xFC10, 'M', u'تي'), + (0xFC11, 'M', u'ثج'), + (0xFC12, 'M', u'ثم'), + (0xFC13, 'M', u'ثى'), + (0xFC14, 'M', u'ثي'), + (0xFC15, 'M', u'جح'), + (0xFC16, 'M', u'جم'), + (0xFC17, 'M', u'حج'), + (0xFC18, 'M', u'حم'), + (0xFC19, 'M', u'خج'), + (0xFC1A, 'M', u'خح'), + (0xFC1B, 'M', u'خم'), + ] + +def _seg_45(): + return [ + (0xFC1C, 'M', u'سج'), + (0xFC1D, 'M', u'سح'), + (0xFC1E, 'M', u'سخ'), + (0xFC1F, 'M', u'سم'), + (0xFC20, 'M', u'صح'), + (0xFC21, 'M', u'صم'), + (0xFC22, 'M', u'ضج'), + (0xFC23, 'M', u'ضح'), + (0xFC24, 'M', u'ضخ'), + (0xFC25, 'M', u'ضم'), + (0xFC26, 'M', u'طح'), + (0xFC27, 'M', u'طم'), + (0xFC28, 'M', u'ظم'), + (0xFC29, 'M', u'عج'), + (0xFC2A, 'M', u'عم'), + (0xFC2B, 'M', u'غج'), + (0xFC2C, 'M', u'غم'), + (0xFC2D, 'M', u'فج'), + (0xFC2E, 'M', u'فح'), + (0xFC2F, 'M', u'فخ'), + (0xFC30, 'M', u'فم'), + (0xFC31, 'M', u'فى'), + (0xFC32, 'M', u'في'), + (0xFC33, 'M', u'قح'), + (0xFC34, 'M', u'قم'), + (0xFC35, 'M', u'قى'), + (0xFC36, 'M', u'قي'), + (0xFC37, 'M', u'كا'), + (0xFC38, 'M', u'كج'), + (0xFC39, 'M', u'كح'), + (0xFC3A, 'M', u'كخ'), + (0xFC3B, 'M', u'كل'), + (0xFC3C, 'M', u'كم'), + (0xFC3D, 'M', u'كى'), + (0xFC3E, 'M', u'كي'), + (0xFC3F, 'M', u'لج'), + (0xFC40, 'M', u'لح'), + (0xFC41, 'M', u'لخ'), + (0xFC42, 'M', u'لم'), + (0xFC43, 'M', u'لى'), + (0xFC44, 'M', u'لي'), + (0xFC45, 'M', u'مج'), + (0xFC46, 'M', u'مح'), + (0xFC47, 'M', u'مخ'), + (0xFC48, 'M', u'مم'), + (0xFC49, 'M', u'مى'), + (0xFC4A, 'M', u'مي'), + (0xFC4B, 'M', u'نج'), + (0xFC4C, 'M', u'نح'), + (0xFC4D, 'M', u'نخ'), + (0xFC4E, 'M', u'نم'), + (0xFC4F, 'M', u'نى'), + (0xFC50, 'M', u'ني'), + (0xFC51, 'M', u'هج'), + (0xFC52, 'M', u'هم'), + (0xFC53, 'M', u'هى'), + (0xFC54, 'M', u'هي'), + (0xFC55, 'M', u'يج'), + (0xFC56, 'M', u'يح'), + (0xFC57, 'M', u'يخ'), + (0xFC58, 'M', u'يم'), + (0xFC59, 'M', u'يى'), + (0xFC5A, 'M', u'يي'), + (0xFC5B, 'M', u'ذٰ'), + (0xFC5C, 'M', u'رٰ'), + (0xFC5D, 'M', u'ىٰ'), + (0xFC5E, '3', u' ٌّ'), + (0xFC5F, '3', u' ٍّ'), + (0xFC60, '3', u' َّ'), + (0xFC61, '3', u' ُّ'), + (0xFC62, '3', u' ِّ'), + (0xFC63, '3', u' ّٰ'), + (0xFC64, 'M', u'ئر'), + (0xFC65, 'M', u'ئز'), + (0xFC66, 'M', u'ئم'), + (0xFC67, 'M', u'ئن'), + (0xFC68, 'M', u'ئى'), + (0xFC69, 'M', u'ئي'), + (0xFC6A, 'M', u'بر'), + (0xFC6B, 'M', u'بز'), + (0xFC6C, 'M', u'بم'), + (0xFC6D, 'M', u'بن'), + (0xFC6E, 'M', u'بى'), + (0xFC6F, 'M', u'بي'), + (0xFC70, 'M', u'تر'), + (0xFC71, 'M', u'تز'), + (0xFC72, 'M', u'تم'), + (0xFC73, 'M', u'تن'), + (0xFC74, 'M', u'تى'), + (0xFC75, 'M', u'تي'), + (0xFC76, 'M', u'ثر'), + (0xFC77, 'M', u'ثز'), + (0xFC78, 'M', u'ثم'), + (0xFC79, 'M', u'ثن'), + (0xFC7A, 'M', u'ثى'), + (0xFC7B, 'M', u'ثي'), + (0xFC7C, 'M', u'فى'), + (0xFC7D, 'M', u'في'), + (0xFC7E, 'M', u'قى'), + (0xFC7F, 'M', u'قي'), + ] + +def _seg_46(): + return [ + (0xFC80, 'M', u'كا'), + (0xFC81, 'M', u'كل'), + (0xFC82, 'M', u'كم'), + (0xFC83, 'M', u'كى'), + (0xFC84, 'M', u'كي'), + (0xFC85, 'M', u'لم'), + (0xFC86, 'M', u'لى'), + (0xFC87, 'M', u'لي'), + (0xFC88, 'M', u'ما'), + (0xFC89, 'M', u'مم'), + (0xFC8A, 'M', u'نر'), + (0xFC8B, 'M', u'نز'), + (0xFC8C, 'M', u'نم'), + (0xFC8D, 'M', u'نن'), + (0xFC8E, 'M', u'نى'), + (0xFC8F, 'M', u'ني'), + (0xFC90, 'M', u'ىٰ'), + (0xFC91, 'M', u'ير'), + (0xFC92, 'M', u'يز'), + (0xFC93, 'M', u'يم'), + (0xFC94, 'M', u'ين'), + (0xFC95, 'M', u'يى'), + (0xFC96, 'M', u'يي'), + (0xFC97, 'M', u'ئج'), + (0xFC98, 'M', u'ئح'), + (0xFC99, 'M', u'ئخ'), + (0xFC9A, 'M', u'ئم'), + (0xFC9B, 'M', u'ئه'), + (0xFC9C, 'M', u'بج'), + (0xFC9D, 'M', u'بح'), + (0xFC9E, 'M', u'بخ'), + (0xFC9F, 'M', u'بم'), + (0xFCA0, 'M', u'به'), + (0xFCA1, 'M', u'تج'), + (0xFCA2, 'M', u'تح'), + (0xFCA3, 'M', u'تخ'), + (0xFCA4, 'M', u'تم'), + (0xFCA5, 'M', u'ته'), + (0xFCA6, 'M', u'ثم'), + (0xFCA7, 'M', u'جح'), + (0xFCA8, 'M', u'جم'), + (0xFCA9, 'M', u'حج'), + (0xFCAA, 'M', u'حم'), + (0xFCAB, 'M', u'خج'), + (0xFCAC, 'M', u'خم'), + (0xFCAD, 'M', u'سج'), + (0xFCAE, 'M', u'سح'), + (0xFCAF, 'M', u'سخ'), + (0xFCB0, 'M', u'سم'), + (0xFCB1, 'M', u'صح'), + (0xFCB2, 'M', u'صخ'), + (0xFCB3, 'M', u'صم'), + (0xFCB4, 'M', u'ضج'), + (0xFCB5, 'M', u'ضح'), + (0xFCB6, 'M', u'ضخ'), + (0xFCB7, 'M', u'ضم'), + (0xFCB8, 'M', u'طح'), + (0xFCB9, 'M', u'ظم'), + (0xFCBA, 'M', u'عج'), + (0xFCBB, 'M', u'عم'), + (0xFCBC, 'M', u'غج'), + (0xFCBD, 'M', u'غم'), + (0xFCBE, 'M', u'فج'), + (0xFCBF, 'M', u'فح'), + (0xFCC0, 'M', u'فخ'), + (0xFCC1, 'M', u'فم'), + (0xFCC2, 'M', u'قح'), + (0xFCC3, 'M', u'قم'), + (0xFCC4, 'M', u'كج'), + (0xFCC5, 'M', u'كح'), + (0xFCC6, 'M', u'كخ'), + (0xFCC7, 'M', u'كل'), + (0xFCC8, 'M', u'كم'), + (0xFCC9, 'M', u'لج'), + (0xFCCA, 'M', u'لح'), + (0xFCCB, 'M', u'لخ'), + (0xFCCC, 'M', u'لم'), + (0xFCCD, 'M', u'له'), + (0xFCCE, 'M', u'مج'), + (0xFCCF, 'M', u'مح'), + (0xFCD0, 'M', u'مخ'), + (0xFCD1, 'M', u'مم'), + (0xFCD2, 'M', u'نج'), + (0xFCD3, 'M', u'نح'), + (0xFCD4, 'M', u'نخ'), + (0xFCD5, 'M', u'نم'), + (0xFCD6, 'M', u'نه'), + (0xFCD7, 'M', u'هج'), + (0xFCD8, 'M', u'هم'), + (0xFCD9, 'M', u'هٰ'), + (0xFCDA, 'M', u'يج'), + (0xFCDB, 'M', u'يح'), + (0xFCDC, 'M', u'يخ'), + (0xFCDD, 'M', u'يم'), + (0xFCDE, 'M', u'يه'), + (0xFCDF, 'M', u'ئم'), + (0xFCE0, 'M', u'ئه'), + (0xFCE1, 'M', u'بم'), + (0xFCE2, 'M', u'به'), + (0xFCE3, 'M', u'تم'), + ] + +def _seg_47(): + return [ + (0xFCE4, 'M', u'ته'), + (0xFCE5, 'M', u'ثم'), + (0xFCE6, 'M', u'ثه'), + (0xFCE7, 'M', u'سم'), + (0xFCE8, 'M', u'سه'), + (0xFCE9, 'M', u'شم'), + (0xFCEA, 'M', u'شه'), + (0xFCEB, 'M', u'كل'), + (0xFCEC, 'M', u'كم'), + (0xFCED, 'M', u'لم'), + (0xFCEE, 'M', u'نم'), + (0xFCEF, 'M', u'نه'), + (0xFCF0, 'M', u'يم'), + (0xFCF1, 'M', u'يه'), + (0xFCF2, 'M', u'ـَّ'), + (0xFCF3, 'M', u'ـُّ'), + (0xFCF4, 'M', u'ـِّ'), + (0xFCF5, 'M', u'طى'), + (0xFCF6, 'M', u'طي'), + (0xFCF7, 'M', u'عى'), + (0xFCF8, 'M', u'عي'), + (0xFCF9, 'M', u'غى'), + (0xFCFA, 'M', u'غي'), + (0xFCFB, 'M', u'سى'), + (0xFCFC, 'M', u'سي'), + (0xFCFD, 'M', u'شى'), + (0xFCFE, 'M', u'شي'), + (0xFCFF, 'M', u'حى'), + (0xFD00, 'M', u'حي'), + (0xFD01, 'M', u'جى'), + (0xFD02, 'M', u'جي'), + (0xFD03, 'M', u'خى'), + (0xFD04, 'M', u'خي'), + (0xFD05, 'M', u'صى'), + (0xFD06, 'M', u'صي'), + (0xFD07, 'M', u'ضى'), + (0xFD08, 'M', u'ضي'), + (0xFD09, 'M', u'شج'), + (0xFD0A, 'M', u'شح'), + (0xFD0B, 'M', u'شخ'), + (0xFD0C, 'M', u'شم'), + (0xFD0D, 'M', u'شر'), + (0xFD0E, 'M', u'سر'), + (0xFD0F, 'M', u'صر'), + (0xFD10, 'M', u'ضر'), + (0xFD11, 'M', u'طى'), + (0xFD12, 'M', u'طي'), + (0xFD13, 'M', u'عى'), + (0xFD14, 'M', u'عي'), + (0xFD15, 'M', u'غى'), + (0xFD16, 'M', u'غي'), + (0xFD17, 'M', u'سى'), + (0xFD18, 'M', u'سي'), + (0xFD19, 'M', u'شى'), + (0xFD1A, 'M', u'شي'), + (0xFD1B, 'M', u'حى'), + (0xFD1C, 'M', u'حي'), + (0xFD1D, 'M', u'جى'), + (0xFD1E, 'M', u'جي'), + (0xFD1F, 'M', u'خى'), + (0xFD20, 'M', u'خي'), + (0xFD21, 'M', u'صى'), + (0xFD22, 'M', u'صي'), + (0xFD23, 'M', u'ضى'), + (0xFD24, 'M', u'ضي'), + (0xFD25, 'M', u'شج'), + (0xFD26, 'M', u'شح'), + (0xFD27, 'M', u'شخ'), + (0xFD28, 'M', u'شم'), + (0xFD29, 'M', u'شر'), + (0xFD2A, 'M', u'سر'), + (0xFD2B, 'M', u'صر'), + (0xFD2C, 'M', u'ضر'), + (0xFD2D, 'M', u'شج'), + (0xFD2E, 'M', u'شح'), + (0xFD2F, 'M', u'شخ'), + (0xFD30, 'M', u'شم'), + (0xFD31, 'M', u'سه'), + (0xFD32, 'M', u'شه'), + (0xFD33, 'M', u'طم'), + (0xFD34, 'M', u'سج'), + (0xFD35, 'M', u'سح'), + (0xFD36, 'M', u'سخ'), + (0xFD37, 'M', u'شج'), + (0xFD38, 'M', u'شح'), + (0xFD39, 'M', u'شخ'), + (0xFD3A, 'M', u'طم'), + (0xFD3B, 'M', u'ظم'), + (0xFD3C, 'M', u'اً'), + (0xFD3E, 'V'), + (0xFD40, 'X'), + (0xFD50, 'M', u'تجم'), + (0xFD51, 'M', u'تحج'), + (0xFD53, 'M', u'تحم'), + (0xFD54, 'M', u'تخم'), + (0xFD55, 'M', u'تمج'), + (0xFD56, 'M', u'تمح'), + (0xFD57, 'M', u'تمخ'), + (0xFD58, 'M', u'جمح'), + (0xFD5A, 'M', u'حمي'), + ] + +def _seg_48(): + return [ + (0xFD5B, 'M', u'حمى'), + (0xFD5C, 'M', u'سحج'), + (0xFD5D, 'M', u'سجح'), + (0xFD5E, 'M', u'سجى'), + (0xFD5F, 'M', u'سمح'), + (0xFD61, 'M', u'سمج'), + (0xFD62, 'M', u'سمم'), + (0xFD64, 'M', u'صحح'), + (0xFD66, 'M', u'صمم'), + (0xFD67, 'M', u'شحم'), + (0xFD69, 'M', u'شجي'), + (0xFD6A, 'M', u'شمخ'), + (0xFD6C, 'M', u'شمم'), + (0xFD6E, 'M', u'ضحى'), + (0xFD6F, 'M', u'ضخم'), + (0xFD71, 'M', u'طمح'), + (0xFD73, 'M', u'طمم'), + (0xFD74, 'M', u'طمي'), + (0xFD75, 'M', u'عجم'), + (0xFD76, 'M', u'عمم'), + (0xFD78, 'M', u'عمى'), + (0xFD79, 'M', u'غمم'), + (0xFD7A, 'M', u'غمي'), + (0xFD7B, 'M', u'غمى'), + (0xFD7C, 'M', u'فخم'), + (0xFD7E, 'M', u'قمح'), + (0xFD7F, 'M', u'قمم'), + (0xFD80, 'M', u'لحم'), + (0xFD81, 'M', u'لحي'), + (0xFD82, 'M', u'لحى'), + (0xFD83, 'M', u'لجج'), + (0xFD85, 'M', u'لخم'), + (0xFD87, 'M', u'لمح'), + (0xFD89, 'M', u'محج'), + (0xFD8A, 'M', u'محم'), + (0xFD8B, 'M', u'محي'), + (0xFD8C, 'M', u'مجح'), + (0xFD8D, 'M', u'مجم'), + (0xFD8E, 'M', u'مخج'), + (0xFD8F, 'M', u'مخم'), + (0xFD90, 'X'), + (0xFD92, 'M', u'مجخ'), + (0xFD93, 'M', u'همج'), + (0xFD94, 'M', u'همم'), + (0xFD95, 'M', u'نحم'), + (0xFD96, 'M', u'نحى'), + (0xFD97, 'M', u'نجم'), + (0xFD99, 'M', u'نجى'), + (0xFD9A, 'M', u'نمي'), + (0xFD9B, 'M', u'نمى'), + (0xFD9C, 'M', u'يمم'), + (0xFD9E, 'M', u'بخي'), + (0xFD9F, 'M', u'تجي'), + (0xFDA0, 'M', u'تجى'), + (0xFDA1, 'M', u'تخي'), + (0xFDA2, 'M', u'تخى'), + (0xFDA3, 'M', u'تمي'), + (0xFDA4, 'M', u'تمى'), + (0xFDA5, 'M', u'جمي'), + (0xFDA6, 'M', u'جحى'), + (0xFDA7, 'M', u'جمى'), + (0xFDA8, 'M', u'سخى'), + (0xFDA9, 'M', u'صحي'), + (0xFDAA, 'M', u'شحي'), + (0xFDAB, 'M', u'ضحي'), + (0xFDAC, 'M', u'لجي'), + (0xFDAD, 'M', u'لمي'), + (0xFDAE, 'M', u'يحي'), + (0xFDAF, 'M', u'يجي'), + (0xFDB0, 'M', u'يمي'), + (0xFDB1, 'M', u'ممي'), + (0xFDB2, 'M', u'قمي'), + (0xFDB3, 'M', u'نحي'), + (0xFDB4, 'M', u'قمح'), + (0xFDB5, 'M', u'لحم'), + (0xFDB6, 'M', u'عمي'), + (0xFDB7, 'M', u'كمي'), + (0xFDB8, 'M', u'نجح'), + (0xFDB9, 'M', u'مخي'), + (0xFDBA, 'M', u'لجم'), + (0xFDBB, 'M', u'كمم'), + (0xFDBC, 'M', u'لجم'), + (0xFDBD, 'M', u'نجح'), + (0xFDBE, 'M', u'جحي'), + (0xFDBF, 'M', u'حجي'), + (0xFDC0, 'M', u'مجي'), + (0xFDC1, 'M', u'فمي'), + (0xFDC2, 'M', u'بحي'), + (0xFDC3, 'M', u'كمم'), + (0xFDC4, 'M', u'عجم'), + (0xFDC5, 'M', u'صمم'), + (0xFDC6, 'M', u'سخي'), + (0xFDC7, 'M', u'نجي'), + (0xFDC8, 'X'), + (0xFDF0, 'M', u'صلے'), + (0xFDF1, 'M', u'قلے'), + (0xFDF2, 'M', u'الله'), + (0xFDF3, 'M', u'اكبر'), + (0xFDF4, 'M', u'محمد'), + (0xFDF5, 'M', u'صلعم'), + ] + +def _seg_49(): + return [ + (0xFDF6, 'M', u'رسول'), + (0xFDF7, 'M', u'عليه'), + (0xFDF8, 'M', u'وسلم'), + (0xFDF9, 'M', u'صلى'), + (0xFDFA, '3', u'صلى الله عليه وسلم'), + (0xFDFB, '3', u'جل جلاله'), + (0xFDFC, 'M', u'ریال'), + (0xFDFD, 'V'), + (0xFDFE, 'X'), + (0xFE00, 'I'), + (0xFE10, '3', u','), + (0xFE11, 'M', u'、'), + (0xFE12, 'X'), + (0xFE13, '3', u':'), + (0xFE14, '3', u';'), + (0xFE15, '3', u'!'), + (0xFE16, '3', u'?'), + (0xFE17, 'M', u'〖'), + (0xFE18, 'M', u'〗'), + (0xFE19, 'X'), + (0xFE20, 'V'), + (0xFE30, 'X'), + (0xFE31, 'M', u'—'), + (0xFE32, 'M', u'–'), + (0xFE33, '3', u'_'), + (0xFE35, '3', u'('), + (0xFE36, '3', u')'), + (0xFE37, '3', u'{'), + (0xFE38, '3', u'}'), + (0xFE39, 'M', u'〔'), + (0xFE3A, 'M', u'〕'), + (0xFE3B, 'M', u'【'), + (0xFE3C, 'M', u'】'), + (0xFE3D, 'M', u'《'), + (0xFE3E, 'M', u'》'), + (0xFE3F, 'M', u'〈'), + (0xFE40, 'M', u'〉'), + (0xFE41, 'M', u'「'), + (0xFE42, 'M', u'」'), + (0xFE43, 'M', u'『'), + (0xFE44, 'M', u'』'), + (0xFE45, 'V'), + (0xFE47, '3', u'['), + (0xFE48, '3', u']'), + (0xFE49, '3', u' ̅'), + (0xFE4D, '3', u'_'), + (0xFE50, '3', u','), + (0xFE51, 'M', u'、'), + (0xFE52, 'X'), + (0xFE54, '3', u';'), + (0xFE55, '3', u':'), + (0xFE56, '3', u'?'), + (0xFE57, '3', u'!'), + (0xFE58, 'M', u'—'), + (0xFE59, '3', u'('), + (0xFE5A, '3', u')'), + (0xFE5B, '3', u'{'), + (0xFE5C, '3', u'}'), + (0xFE5D, 'M', u'〔'), + (0xFE5E, 'M', u'〕'), + (0xFE5F, '3', u'#'), + (0xFE60, '3', u'&'), + (0xFE61, '3', u'*'), + (0xFE62, '3', u'+'), + (0xFE63, 'M', u'-'), + (0xFE64, '3', u'<'), + (0xFE65, '3', u'>'), + (0xFE66, '3', u'='), + (0xFE67, 'X'), + (0xFE68, '3', u'\\'), + (0xFE69, '3', u'$'), + (0xFE6A, '3', u'%'), + (0xFE6B, '3', u'@'), + (0xFE6C, 'X'), + (0xFE70, '3', u' ً'), + (0xFE71, 'M', u'ـً'), + (0xFE72, '3', u' ٌ'), + (0xFE73, 'V'), + (0xFE74, '3', u' ٍ'), + (0xFE75, 'X'), + (0xFE76, '3', u' َ'), + (0xFE77, 'M', u'ـَ'), + (0xFE78, '3', u' ُ'), + (0xFE79, 'M', u'ـُ'), + (0xFE7A, '3', u' ِ'), + (0xFE7B, 'M', u'ـِ'), + (0xFE7C, '3', u' ّ'), + (0xFE7D, 'M', u'ـّ'), + (0xFE7E, '3', u' ْ'), + (0xFE7F, 'M', u'ـْ'), + (0xFE80, 'M', u'ء'), + (0xFE81, 'M', u'آ'), + (0xFE83, 'M', u'أ'), + (0xFE85, 'M', u'ؤ'), + (0xFE87, 'M', u'إ'), + (0xFE89, 'M', u'ئ'), + (0xFE8D, 'M', u'ا'), + (0xFE8F, 'M', u'ب'), + (0xFE93, 'M', u'ة'), + (0xFE95, 'M', u'ت'), + ] + +def _seg_50(): + return [ + (0xFE99, 'M', u'ث'), + (0xFE9D, 'M', u'ج'), + (0xFEA1, 'M', u'ح'), + (0xFEA5, 'M', u'خ'), + (0xFEA9, 'M', u'د'), + (0xFEAB, 'M', u'ذ'), + (0xFEAD, 'M', u'ر'), + (0xFEAF, 'M', u'ز'), + (0xFEB1, 'M', u'س'), + (0xFEB5, 'M', u'ش'), + (0xFEB9, 'M', u'ص'), + (0xFEBD, 'M', u'ض'), + (0xFEC1, 'M', u'ط'), + (0xFEC5, 'M', u'ظ'), + (0xFEC9, 'M', u'ع'), + (0xFECD, 'M', u'غ'), + (0xFED1, 'M', u'ف'), + (0xFED5, 'M', u'ق'), + (0xFED9, 'M', u'ك'), + (0xFEDD, 'M', u'ل'), + (0xFEE1, 'M', u'م'), + (0xFEE5, 'M', u'ن'), + (0xFEE9, 'M', u'ه'), + (0xFEED, 'M', u'و'), + (0xFEEF, 'M', u'ى'), + (0xFEF1, 'M', u'ي'), + (0xFEF5, 'M', u'لآ'), + (0xFEF7, 'M', u'لأ'), + (0xFEF9, 'M', u'لإ'), + (0xFEFB, 'M', u'لا'), + (0xFEFD, 'X'), + (0xFEFF, 'I'), + (0xFF00, 'X'), + (0xFF01, '3', u'!'), + (0xFF02, '3', u'"'), + (0xFF03, '3', u'#'), + (0xFF04, '3', u'$'), + (0xFF05, '3', u'%'), + (0xFF06, '3', u'&'), + (0xFF07, '3', u'\''), + (0xFF08, '3', u'('), + (0xFF09, '3', u')'), + (0xFF0A, '3', u'*'), + (0xFF0B, '3', u'+'), + (0xFF0C, '3', u','), + (0xFF0D, 'M', u'-'), + (0xFF0E, 'M', u'.'), + (0xFF0F, '3', u'/'), + (0xFF10, 'M', u'0'), + (0xFF11, 'M', u'1'), + (0xFF12, 'M', u'2'), + (0xFF13, 'M', u'3'), + (0xFF14, 'M', u'4'), + (0xFF15, 'M', u'5'), + (0xFF16, 'M', u'6'), + (0xFF17, 'M', u'7'), + (0xFF18, 'M', u'8'), + (0xFF19, 'M', u'9'), + (0xFF1A, '3', u':'), + (0xFF1B, '3', u';'), + (0xFF1C, '3', u'<'), + (0xFF1D, '3', u'='), + (0xFF1E, '3', u'>'), + (0xFF1F, '3', u'?'), + (0xFF20, '3', u'@'), + (0xFF21, 'M', u'a'), + (0xFF22, 'M', u'b'), + (0xFF23, 'M', u'c'), + (0xFF24, 'M', u'd'), + (0xFF25, 'M', u'e'), + (0xFF26, 'M', u'f'), + (0xFF27, 'M', u'g'), + (0xFF28, 'M', u'h'), + (0xFF29, 'M', u'i'), + (0xFF2A, 'M', u'j'), + (0xFF2B, 'M', u'k'), + (0xFF2C, 'M', u'l'), + (0xFF2D, 'M', u'm'), + (0xFF2E, 'M', u'n'), + (0xFF2F, 'M', u'o'), + (0xFF30, 'M', u'p'), + (0xFF31, 'M', u'q'), + (0xFF32, 'M', u'r'), + (0xFF33, 'M', u's'), + (0xFF34, 'M', u't'), + (0xFF35, 'M', u'u'), + (0xFF36, 'M', u'v'), + (0xFF37, 'M', u'w'), + (0xFF38, 'M', u'x'), + (0xFF39, 'M', u'y'), + (0xFF3A, 'M', u'z'), + (0xFF3B, '3', u'['), + (0xFF3C, '3', u'\\'), + (0xFF3D, '3', u']'), + (0xFF3E, '3', u'^'), + (0xFF3F, '3', u'_'), + (0xFF40, '3', u'`'), + (0xFF41, 'M', u'a'), + (0xFF42, 'M', u'b'), + (0xFF43, 'M', u'c'), + ] + +def _seg_51(): + return [ + (0xFF44, 'M', u'd'), + (0xFF45, 'M', u'e'), + (0xFF46, 'M', u'f'), + (0xFF47, 'M', u'g'), + (0xFF48, 'M', u'h'), + (0xFF49, 'M', u'i'), + (0xFF4A, 'M', u'j'), + (0xFF4B, 'M', u'k'), + (0xFF4C, 'M', u'l'), + (0xFF4D, 'M', u'm'), + (0xFF4E, 'M', u'n'), + (0xFF4F, 'M', u'o'), + (0xFF50, 'M', u'p'), + (0xFF51, 'M', u'q'), + (0xFF52, 'M', u'r'), + (0xFF53, 'M', u's'), + (0xFF54, 'M', u't'), + (0xFF55, 'M', u'u'), + (0xFF56, 'M', u'v'), + (0xFF57, 'M', u'w'), + (0xFF58, 'M', u'x'), + (0xFF59, 'M', u'y'), + (0xFF5A, 'M', u'z'), + (0xFF5B, '3', u'{'), + (0xFF5C, '3', u'|'), + (0xFF5D, '3', u'}'), + (0xFF5E, '3', u'~'), + (0xFF5F, 'M', u'⦅'), + (0xFF60, 'M', u'⦆'), + (0xFF61, 'M', u'.'), + (0xFF62, 'M', u'「'), + (0xFF63, 'M', u'」'), + (0xFF64, 'M', u'、'), + (0xFF65, 'M', u'・'), + (0xFF66, 'M', u'ヲ'), + (0xFF67, 'M', u'ァ'), + (0xFF68, 'M', u'ィ'), + (0xFF69, 'M', u'ゥ'), + (0xFF6A, 'M', u'ェ'), + (0xFF6B, 'M', u'ォ'), + (0xFF6C, 'M', u'ャ'), + (0xFF6D, 'M', u'ュ'), + (0xFF6E, 'M', u'ョ'), + (0xFF6F, 'M', u'ッ'), + (0xFF70, 'M', u'ー'), + (0xFF71, 'M', u'ア'), + (0xFF72, 'M', u'イ'), + (0xFF73, 'M', u'ウ'), + (0xFF74, 'M', u'エ'), + (0xFF75, 'M', u'オ'), + (0xFF76, 'M', u'カ'), + (0xFF77, 'M', u'キ'), + (0xFF78, 'M', u'ク'), + (0xFF79, 'M', u'ケ'), + (0xFF7A, 'M', u'コ'), + (0xFF7B, 'M', u'サ'), + (0xFF7C, 'M', u'シ'), + (0xFF7D, 'M', u'ス'), + (0xFF7E, 'M', u'セ'), + (0xFF7F, 'M', u'ソ'), + (0xFF80, 'M', u'タ'), + (0xFF81, 'M', u'チ'), + (0xFF82, 'M', u'ツ'), + (0xFF83, 'M', u'テ'), + (0xFF84, 'M', u'ト'), + (0xFF85, 'M', u'ナ'), + (0xFF86, 'M', u'ニ'), + (0xFF87, 'M', u'ヌ'), + (0xFF88, 'M', u'ネ'), + (0xFF89, 'M', u'ノ'), + (0xFF8A, 'M', u'ハ'), + (0xFF8B, 'M', u'ヒ'), + (0xFF8C, 'M', u'フ'), + (0xFF8D, 'M', u'ヘ'), + (0xFF8E, 'M', u'ホ'), + (0xFF8F, 'M', u'マ'), + (0xFF90, 'M', u'ミ'), + (0xFF91, 'M', u'ム'), + (0xFF92, 'M', u'メ'), + (0xFF93, 'M', u'モ'), + (0xFF94, 'M', u'ヤ'), + (0xFF95, 'M', u'ユ'), + (0xFF96, 'M', u'ヨ'), + (0xFF97, 'M', u'ラ'), + (0xFF98, 'M', u'リ'), + (0xFF99, 'M', u'ル'), + (0xFF9A, 'M', u'レ'), + (0xFF9B, 'M', u'ロ'), + (0xFF9C, 'M', u'ワ'), + (0xFF9D, 'M', u'ン'), + (0xFF9E, 'M', u'゙'), + (0xFF9F, 'M', u'゚'), + (0xFFA0, 'X'), + (0xFFA1, 'M', u'ᄀ'), + (0xFFA2, 'M', u'ᄁ'), + (0xFFA3, 'M', u'ᆪ'), + (0xFFA4, 'M', u'ᄂ'), + (0xFFA5, 'M', u'ᆬ'), + (0xFFA6, 'M', u'ᆭ'), + (0xFFA7, 'M', u'ᄃ'), + ] + +def _seg_52(): + return [ + (0xFFA8, 'M', u'ᄄ'), + (0xFFA9, 'M', u'ᄅ'), + (0xFFAA, 'M', u'ᆰ'), + (0xFFAB, 'M', u'ᆱ'), + (0xFFAC, 'M', u'ᆲ'), + (0xFFAD, 'M', u'ᆳ'), + (0xFFAE, 'M', u'ᆴ'), + (0xFFAF, 'M', u'ᆵ'), + (0xFFB0, 'M', u'ᄚ'), + (0xFFB1, 'M', u'ᄆ'), + (0xFFB2, 'M', u'ᄇ'), + (0xFFB3, 'M', u'ᄈ'), + (0xFFB4, 'M', u'ᄡ'), + (0xFFB5, 'M', u'ᄉ'), + (0xFFB6, 'M', u'ᄊ'), + (0xFFB7, 'M', u'ᄋ'), + (0xFFB8, 'M', u'ᄌ'), + (0xFFB9, 'M', u'ᄍ'), + (0xFFBA, 'M', u'ᄎ'), + (0xFFBB, 'M', u'ᄏ'), + (0xFFBC, 'M', u'ᄐ'), + (0xFFBD, 'M', u'ᄑ'), + (0xFFBE, 'M', u'ᄒ'), + (0xFFBF, 'X'), + (0xFFC2, 'M', u'ᅡ'), + (0xFFC3, 'M', u'ᅢ'), + (0xFFC4, 'M', u'ᅣ'), + (0xFFC5, 'M', u'ᅤ'), + (0xFFC6, 'M', u'ᅥ'), + (0xFFC7, 'M', u'ᅦ'), + (0xFFC8, 'X'), + (0xFFCA, 'M', u'ᅧ'), + (0xFFCB, 'M', u'ᅨ'), + (0xFFCC, 'M', u'ᅩ'), + (0xFFCD, 'M', u'ᅪ'), + (0xFFCE, 'M', u'ᅫ'), + (0xFFCF, 'M', u'ᅬ'), + (0xFFD0, 'X'), + (0xFFD2, 'M', u'ᅭ'), + (0xFFD3, 'M', u'ᅮ'), + (0xFFD4, 'M', u'ᅯ'), + (0xFFD5, 'M', u'ᅰ'), + (0xFFD6, 'M', u'ᅱ'), + (0xFFD7, 'M', u'ᅲ'), + (0xFFD8, 'X'), + (0xFFDA, 'M', u'ᅳ'), + (0xFFDB, 'M', u'ᅴ'), + (0xFFDC, 'M', u'ᅵ'), + (0xFFDD, 'X'), + (0xFFE0, 'M', u'¢'), + (0xFFE1, 'M', u'£'), + (0xFFE2, 'M', u'¬'), + (0xFFE3, '3', u' ̄'), + (0xFFE4, 'M', u'¦'), + (0xFFE5, 'M', u'¥'), + (0xFFE6, 'M', u'₩'), + (0xFFE7, 'X'), + (0xFFE8, 'M', u'│'), + (0xFFE9, 'M', u'←'), + (0xFFEA, 'M', u'↑'), + (0xFFEB, 'M', u'→'), + (0xFFEC, 'M', u'↓'), + (0xFFED, 'M', u'■'), + (0xFFEE, 'M', u'○'), + (0xFFEF, 'X'), + (0x10000, 'V'), + (0x1000C, 'X'), + (0x1000D, 'V'), + (0x10027, 'X'), + (0x10028, 'V'), + (0x1003B, 'X'), + (0x1003C, 'V'), + (0x1003E, 'X'), + (0x1003F, 'V'), + (0x1004E, 'X'), + (0x10050, 'V'), + (0x1005E, 'X'), + (0x10080, 'V'), + (0x100FB, 'X'), + (0x10100, 'V'), + (0x10103, 'X'), + (0x10107, 'V'), + (0x10134, 'X'), + (0x10137, 'V'), + (0x1018F, 'X'), + (0x10190, 'V'), + (0x1019C, 'X'), + (0x101A0, 'V'), + (0x101A1, 'X'), + (0x101D0, 'V'), + (0x101FE, 'X'), + (0x10280, 'V'), + (0x1029D, 'X'), + (0x102A0, 'V'), + (0x102D1, 'X'), + (0x102E0, 'V'), + (0x102FC, 'X'), + (0x10300, 'V'), + (0x10324, 'X'), + (0x1032D, 'V'), + ] + +def _seg_53(): + return [ + (0x1034B, 'X'), + (0x10350, 'V'), + (0x1037B, 'X'), + (0x10380, 'V'), + (0x1039E, 'X'), + (0x1039F, 'V'), + (0x103C4, 'X'), + (0x103C8, 'V'), + (0x103D6, 'X'), + (0x10400, 'M', u'𐐨'), + (0x10401, 'M', u'𐐩'), + (0x10402, 'M', u'𐐪'), + (0x10403, 'M', u'𐐫'), + (0x10404, 'M', u'𐐬'), + (0x10405, 'M', u'𐐭'), + (0x10406, 'M', u'𐐮'), + (0x10407, 'M', u'𐐯'), + (0x10408, 'M', u'𐐰'), + (0x10409, 'M', u'𐐱'), + (0x1040A, 'M', u'𐐲'), + (0x1040B, 'M', u'𐐳'), + (0x1040C, 'M', u'𐐴'), + (0x1040D, 'M', u'𐐵'), + (0x1040E, 'M', u'𐐶'), + (0x1040F, 'M', u'𐐷'), + (0x10410, 'M', u'𐐸'), + (0x10411, 'M', u'𐐹'), + (0x10412, 'M', u'𐐺'), + (0x10413, 'M', u'𐐻'), + (0x10414, 'M', u'𐐼'), + (0x10415, 'M', u'𐐽'), + (0x10416, 'M', u'𐐾'), + (0x10417, 'M', u'𐐿'), + (0x10418, 'M', u'𐑀'), + (0x10419, 'M', u'𐑁'), + (0x1041A, 'M', u'𐑂'), + (0x1041B, 'M', u'𐑃'), + (0x1041C, 'M', u'𐑄'), + (0x1041D, 'M', u'𐑅'), + (0x1041E, 'M', u'𐑆'), + (0x1041F, 'M', u'𐑇'), + (0x10420, 'M', u'𐑈'), + (0x10421, 'M', u'𐑉'), + (0x10422, 'M', u'𐑊'), + (0x10423, 'M', u'𐑋'), + (0x10424, 'M', u'𐑌'), + (0x10425, 'M', u'𐑍'), + (0x10426, 'M', u'𐑎'), + (0x10427, 'M', u'𐑏'), + (0x10428, 'V'), + (0x1049E, 'X'), + (0x104A0, 'V'), + (0x104AA, 'X'), + (0x104B0, 'M', u'𐓘'), + (0x104B1, 'M', u'𐓙'), + (0x104B2, 'M', u'𐓚'), + (0x104B3, 'M', u'𐓛'), + (0x104B4, 'M', u'𐓜'), + (0x104B5, 'M', u'𐓝'), + (0x104B6, 'M', u'𐓞'), + (0x104B7, 'M', u'𐓟'), + (0x104B8, 'M', u'𐓠'), + (0x104B9, 'M', u'𐓡'), + (0x104BA, 'M', u'𐓢'), + (0x104BB, 'M', u'𐓣'), + (0x104BC, 'M', u'𐓤'), + (0x104BD, 'M', u'𐓥'), + (0x104BE, 'M', u'𐓦'), + (0x104BF, 'M', u'𐓧'), + (0x104C0, 'M', u'𐓨'), + (0x104C1, 'M', u'𐓩'), + (0x104C2, 'M', u'𐓪'), + (0x104C3, 'M', u'𐓫'), + (0x104C4, 'M', u'𐓬'), + (0x104C5, 'M', u'𐓭'), + (0x104C6, 'M', u'𐓮'), + (0x104C7, 'M', u'𐓯'), + (0x104C8, 'M', u'𐓰'), + (0x104C9, 'M', u'𐓱'), + (0x104CA, 'M', u'𐓲'), + (0x104CB, 'M', u'𐓳'), + (0x104CC, 'M', u'𐓴'), + (0x104CD, 'M', u'𐓵'), + (0x104CE, 'M', u'𐓶'), + (0x104CF, 'M', u'𐓷'), + (0x104D0, 'M', u'𐓸'), + (0x104D1, 'M', u'𐓹'), + (0x104D2, 'M', u'𐓺'), + (0x104D3, 'M', u'𐓻'), + (0x104D4, 'X'), + (0x104D8, 'V'), + (0x104FC, 'X'), + (0x10500, 'V'), + (0x10528, 'X'), + (0x10530, 'V'), + (0x10564, 'X'), + (0x1056F, 'V'), + (0x10570, 'X'), + (0x10600, 'V'), + (0x10737, 'X'), + ] + +def _seg_54(): + return [ + (0x10740, 'V'), + (0x10756, 'X'), + (0x10760, 'V'), + (0x10768, 'X'), + (0x10800, 'V'), + (0x10806, 'X'), + (0x10808, 'V'), + (0x10809, 'X'), + (0x1080A, 'V'), + (0x10836, 'X'), + (0x10837, 'V'), + (0x10839, 'X'), + (0x1083C, 'V'), + (0x1083D, 'X'), + (0x1083F, 'V'), + (0x10856, 'X'), + (0x10857, 'V'), + (0x1089F, 'X'), + (0x108A7, 'V'), + (0x108B0, 'X'), + (0x108E0, 'V'), + (0x108F3, 'X'), + (0x108F4, 'V'), + (0x108F6, 'X'), + (0x108FB, 'V'), + (0x1091C, 'X'), + (0x1091F, 'V'), + (0x1093A, 'X'), + (0x1093F, 'V'), + (0x10940, 'X'), + (0x10980, 'V'), + (0x109B8, 'X'), + (0x109BC, 'V'), + (0x109D0, 'X'), + (0x109D2, 'V'), + (0x10A04, 'X'), + (0x10A05, 'V'), + (0x10A07, 'X'), + (0x10A0C, 'V'), + (0x10A14, 'X'), + (0x10A15, 'V'), + (0x10A18, 'X'), + (0x10A19, 'V'), + (0x10A34, 'X'), + (0x10A38, 'V'), + (0x10A3B, 'X'), + (0x10A3F, 'V'), + (0x10A48, 'X'), + (0x10A50, 'V'), + (0x10A59, 'X'), + (0x10A60, 'V'), + (0x10AA0, 'X'), + (0x10AC0, 'V'), + (0x10AE7, 'X'), + (0x10AEB, 'V'), + (0x10AF7, 'X'), + (0x10B00, 'V'), + (0x10B36, 'X'), + (0x10B39, 'V'), + (0x10B56, 'X'), + (0x10B58, 'V'), + (0x10B73, 'X'), + (0x10B78, 'V'), + (0x10B92, 'X'), + (0x10B99, 'V'), + (0x10B9D, 'X'), + (0x10BA9, 'V'), + (0x10BB0, 'X'), + (0x10C00, 'V'), + (0x10C49, 'X'), + (0x10C80, 'M', u'𐳀'), + (0x10C81, 'M', u'𐳁'), + (0x10C82, 'M', u'𐳂'), + (0x10C83, 'M', u'𐳃'), + (0x10C84, 'M', u'𐳄'), + (0x10C85, 'M', u'𐳅'), + (0x10C86, 'M', u'𐳆'), + (0x10C87, 'M', u'𐳇'), + (0x10C88, 'M', u'𐳈'), + (0x10C89, 'M', u'𐳉'), + (0x10C8A, 'M', u'𐳊'), + (0x10C8B, 'M', u'𐳋'), + (0x10C8C, 'M', u'𐳌'), + (0x10C8D, 'M', u'𐳍'), + (0x10C8E, 'M', u'𐳎'), + (0x10C8F, 'M', u'𐳏'), + (0x10C90, 'M', u'𐳐'), + (0x10C91, 'M', u'𐳑'), + (0x10C92, 'M', u'𐳒'), + (0x10C93, 'M', u'𐳓'), + (0x10C94, 'M', u'𐳔'), + (0x10C95, 'M', u'𐳕'), + (0x10C96, 'M', u'𐳖'), + (0x10C97, 'M', u'𐳗'), + (0x10C98, 'M', u'𐳘'), + (0x10C99, 'M', u'𐳙'), + (0x10C9A, 'M', u'𐳚'), + (0x10C9B, 'M', u'𐳛'), + (0x10C9C, 'M', u'𐳜'), + (0x10C9D, 'M', u'𐳝'), + ] + +def _seg_55(): + return [ + (0x10C9E, 'M', u'𐳞'), + (0x10C9F, 'M', u'𐳟'), + (0x10CA0, 'M', u'𐳠'), + (0x10CA1, 'M', u'𐳡'), + (0x10CA2, 'M', u'𐳢'), + (0x10CA3, 'M', u'𐳣'), + (0x10CA4, 'M', u'𐳤'), + (0x10CA5, 'M', u'𐳥'), + (0x10CA6, 'M', u'𐳦'), + (0x10CA7, 'M', u'𐳧'), + (0x10CA8, 'M', u'𐳨'), + (0x10CA9, 'M', u'𐳩'), + (0x10CAA, 'M', u'𐳪'), + (0x10CAB, 'M', u'𐳫'), + (0x10CAC, 'M', u'𐳬'), + (0x10CAD, 'M', u'𐳭'), + (0x10CAE, 'M', u'𐳮'), + (0x10CAF, 'M', u'𐳯'), + (0x10CB0, 'M', u'𐳰'), + (0x10CB1, 'M', u'𐳱'), + (0x10CB2, 'M', u'𐳲'), + (0x10CB3, 'X'), + (0x10CC0, 'V'), + (0x10CF3, 'X'), + (0x10CFA, 'V'), + (0x10D00, 'X'), + (0x10E60, 'V'), + (0x10E7F, 'X'), + (0x11000, 'V'), + (0x1104E, 'X'), + (0x11052, 'V'), + (0x11070, 'X'), + (0x1107F, 'V'), + (0x110BD, 'X'), + (0x110BE, 'V'), + (0x110C2, 'X'), + (0x110D0, 'V'), + (0x110E9, 'X'), + (0x110F0, 'V'), + (0x110FA, 'X'), + (0x11100, 'V'), + (0x11135, 'X'), + (0x11136, 'V'), + (0x11144, 'X'), + (0x11150, 'V'), + (0x11177, 'X'), + (0x11180, 'V'), + (0x111CE, 'X'), + (0x111D0, 'V'), + (0x111E0, 'X'), + (0x111E1, 'V'), + (0x111F5, 'X'), + (0x11200, 'V'), + (0x11212, 'X'), + (0x11213, 'V'), + (0x1123F, 'X'), + (0x11280, 'V'), + (0x11287, 'X'), + (0x11288, 'V'), + (0x11289, 'X'), + (0x1128A, 'V'), + (0x1128E, 'X'), + (0x1128F, 'V'), + (0x1129E, 'X'), + (0x1129F, 'V'), + (0x112AA, 'X'), + (0x112B0, 'V'), + (0x112EB, 'X'), + (0x112F0, 'V'), + (0x112FA, 'X'), + (0x11300, 'V'), + (0x11304, 'X'), + (0x11305, 'V'), + (0x1130D, 'X'), + (0x1130F, 'V'), + (0x11311, 'X'), + (0x11313, 'V'), + (0x11329, 'X'), + (0x1132A, 'V'), + (0x11331, 'X'), + (0x11332, 'V'), + (0x11334, 'X'), + (0x11335, 'V'), + (0x1133A, 'X'), + (0x1133C, 'V'), + (0x11345, 'X'), + (0x11347, 'V'), + (0x11349, 'X'), + (0x1134B, 'V'), + (0x1134E, 'X'), + (0x11350, 'V'), + (0x11351, 'X'), + (0x11357, 'V'), + (0x11358, 'X'), + (0x1135D, 'V'), + (0x11364, 'X'), + (0x11366, 'V'), + (0x1136D, 'X'), + (0x11370, 'V'), + (0x11375, 'X'), + ] + +def _seg_56(): + return [ + (0x11400, 'V'), + (0x1145A, 'X'), + (0x1145B, 'V'), + (0x1145C, 'X'), + (0x1145D, 'V'), + (0x1145E, 'X'), + (0x11480, 'V'), + (0x114C8, 'X'), + (0x114D0, 'V'), + (0x114DA, 'X'), + (0x11580, 'V'), + (0x115B6, 'X'), + (0x115B8, 'V'), + (0x115DE, 'X'), + (0x11600, 'V'), + (0x11645, 'X'), + (0x11650, 'V'), + (0x1165A, 'X'), + (0x11660, 'V'), + (0x1166D, 'X'), + (0x11680, 'V'), + (0x116B8, 'X'), + (0x116C0, 'V'), + (0x116CA, 'X'), + (0x11700, 'V'), + (0x1171A, 'X'), + (0x1171D, 'V'), + (0x1172C, 'X'), + (0x11730, 'V'), + (0x11740, 'X'), + (0x118A0, 'M', u'𑣀'), + (0x118A1, 'M', u'𑣁'), + (0x118A2, 'M', u'𑣂'), + (0x118A3, 'M', u'𑣃'), + (0x118A4, 'M', u'𑣄'), + (0x118A5, 'M', u'𑣅'), + (0x118A6, 'M', u'𑣆'), + (0x118A7, 'M', u'𑣇'), + (0x118A8, 'M', u'𑣈'), + (0x118A9, 'M', u'𑣉'), + (0x118AA, 'M', u'𑣊'), + (0x118AB, 'M', u'𑣋'), + (0x118AC, 'M', u'𑣌'), + (0x118AD, 'M', u'𑣍'), + (0x118AE, 'M', u'𑣎'), + (0x118AF, 'M', u'𑣏'), + (0x118B0, 'M', u'𑣐'), + (0x118B1, 'M', u'𑣑'), + (0x118B2, 'M', u'𑣒'), + (0x118B3, 'M', u'𑣓'), + (0x118B4, 'M', u'𑣔'), + (0x118B5, 'M', u'𑣕'), + (0x118B6, 'M', u'𑣖'), + (0x118B7, 'M', u'𑣗'), + (0x118B8, 'M', u'𑣘'), + (0x118B9, 'M', u'𑣙'), + (0x118BA, 'M', u'𑣚'), + (0x118BB, 'M', u'𑣛'), + (0x118BC, 'M', u'𑣜'), + (0x118BD, 'M', u'𑣝'), + (0x118BE, 'M', u'𑣞'), + (0x118BF, 'M', u'𑣟'), + (0x118C0, 'V'), + (0x118F3, 'X'), + (0x118FF, 'V'), + (0x11900, 'X'), + (0x11A00, 'V'), + (0x11A48, 'X'), + (0x11A50, 'V'), + (0x11A84, 'X'), + (0x11A86, 'V'), + (0x11A9D, 'X'), + (0x11A9E, 'V'), + (0x11AA3, 'X'), + (0x11AC0, 'V'), + (0x11AF9, 'X'), + (0x11C00, 'V'), + (0x11C09, 'X'), + (0x11C0A, 'V'), + (0x11C37, 'X'), + (0x11C38, 'V'), + (0x11C46, 'X'), + (0x11C50, 'V'), + (0x11C6D, 'X'), + (0x11C70, 'V'), + (0x11C90, 'X'), + (0x11C92, 'V'), + (0x11CA8, 'X'), + (0x11CA9, 'V'), + (0x11CB7, 'X'), + (0x11D00, 'V'), + (0x11D07, 'X'), + (0x11D08, 'V'), + (0x11D0A, 'X'), + (0x11D0B, 'V'), + (0x11D37, 'X'), + (0x11D3A, 'V'), + (0x11D3B, 'X'), + (0x11D3C, 'V'), + (0x11D3E, 'X'), + ] + +def _seg_57(): + return [ + (0x11D3F, 'V'), + (0x11D48, 'X'), + (0x11D50, 'V'), + (0x11D5A, 'X'), + (0x12000, 'V'), + (0x1239A, 'X'), + (0x12400, 'V'), + (0x1246F, 'X'), + (0x12470, 'V'), + (0x12475, 'X'), + (0x12480, 'V'), + (0x12544, 'X'), + (0x13000, 'V'), + (0x1342F, 'X'), + (0x14400, 'V'), + (0x14647, 'X'), + (0x16800, 'V'), + (0x16A39, 'X'), + (0x16A40, 'V'), + (0x16A5F, 'X'), + (0x16A60, 'V'), + (0x16A6A, 'X'), + (0x16A6E, 'V'), + (0x16A70, 'X'), + (0x16AD0, 'V'), + (0x16AEE, 'X'), + (0x16AF0, 'V'), + (0x16AF6, 'X'), + (0x16B00, 'V'), + (0x16B46, 'X'), + (0x16B50, 'V'), + (0x16B5A, 'X'), + (0x16B5B, 'V'), + (0x16B62, 'X'), + (0x16B63, 'V'), + (0x16B78, 'X'), + (0x16B7D, 'V'), + (0x16B90, 'X'), + (0x16F00, 'V'), + (0x16F45, 'X'), + (0x16F50, 'V'), + (0x16F7F, 'X'), + (0x16F8F, 'V'), + (0x16FA0, 'X'), + (0x16FE0, 'V'), + (0x16FE2, 'X'), + (0x17000, 'V'), + (0x187ED, 'X'), + (0x18800, 'V'), + (0x18AF3, 'X'), + (0x1B000, 'V'), + (0x1B11F, 'X'), + (0x1B170, 'V'), + (0x1B2FC, 'X'), + (0x1BC00, 'V'), + (0x1BC6B, 'X'), + (0x1BC70, 'V'), + (0x1BC7D, 'X'), + (0x1BC80, 'V'), + (0x1BC89, 'X'), + (0x1BC90, 'V'), + (0x1BC9A, 'X'), + (0x1BC9C, 'V'), + (0x1BCA0, 'I'), + (0x1BCA4, 'X'), + (0x1D000, 'V'), + (0x1D0F6, 'X'), + (0x1D100, 'V'), + (0x1D127, 'X'), + (0x1D129, 'V'), + (0x1D15E, 'M', u'𝅗𝅥'), + (0x1D15F, 'M', u'𝅘𝅥'), + (0x1D160, 'M', u'𝅘𝅥𝅮'), + (0x1D161, 'M', u'𝅘𝅥𝅯'), + (0x1D162, 'M', u'𝅘𝅥𝅰'), + (0x1D163, 'M', u'𝅘𝅥𝅱'), + (0x1D164, 'M', u'𝅘𝅥𝅲'), + (0x1D165, 'V'), + (0x1D173, 'X'), + (0x1D17B, 'V'), + (0x1D1BB, 'M', u'𝆹𝅥'), + (0x1D1BC, 'M', u'𝆺𝅥'), + (0x1D1BD, 'M', u'𝆹𝅥𝅮'), + (0x1D1BE, 'M', u'𝆺𝅥𝅮'), + (0x1D1BF, 'M', u'𝆹𝅥𝅯'), + (0x1D1C0, 'M', u'𝆺𝅥𝅯'), + (0x1D1C1, 'V'), + (0x1D1E9, 'X'), + (0x1D200, 'V'), + (0x1D246, 'X'), + (0x1D300, 'V'), + (0x1D357, 'X'), + (0x1D360, 'V'), + (0x1D372, 'X'), + (0x1D400, 'M', u'a'), + (0x1D401, 'M', u'b'), + (0x1D402, 'M', u'c'), + (0x1D403, 'M', u'd'), + (0x1D404, 'M', u'e'), + (0x1D405, 'M', u'f'), + ] + +def _seg_58(): + return [ + (0x1D406, 'M', u'g'), + (0x1D407, 'M', u'h'), + (0x1D408, 'M', u'i'), + (0x1D409, 'M', u'j'), + (0x1D40A, 'M', u'k'), + (0x1D40B, 'M', u'l'), + (0x1D40C, 'M', u'm'), + (0x1D40D, 'M', u'n'), + (0x1D40E, 'M', u'o'), + (0x1D40F, 'M', u'p'), + (0x1D410, 'M', u'q'), + (0x1D411, 'M', u'r'), + (0x1D412, 'M', u's'), + (0x1D413, 'M', u't'), + (0x1D414, 'M', u'u'), + (0x1D415, 'M', u'v'), + (0x1D416, 'M', u'w'), + (0x1D417, 'M', u'x'), + (0x1D418, 'M', u'y'), + (0x1D419, 'M', u'z'), + (0x1D41A, 'M', u'a'), + (0x1D41B, 'M', u'b'), + (0x1D41C, 'M', u'c'), + (0x1D41D, 'M', u'd'), + (0x1D41E, 'M', u'e'), + (0x1D41F, 'M', u'f'), + (0x1D420, 'M', u'g'), + (0x1D421, 'M', u'h'), + (0x1D422, 'M', u'i'), + (0x1D423, 'M', u'j'), + (0x1D424, 'M', u'k'), + (0x1D425, 'M', u'l'), + (0x1D426, 'M', u'm'), + (0x1D427, 'M', u'n'), + (0x1D428, 'M', u'o'), + (0x1D429, 'M', u'p'), + (0x1D42A, 'M', u'q'), + (0x1D42B, 'M', u'r'), + (0x1D42C, 'M', u's'), + (0x1D42D, 'M', u't'), + (0x1D42E, 'M', u'u'), + (0x1D42F, 'M', u'v'), + (0x1D430, 'M', u'w'), + (0x1D431, 'M', u'x'), + (0x1D432, 'M', u'y'), + (0x1D433, 'M', u'z'), + (0x1D434, 'M', u'a'), + (0x1D435, 'M', u'b'), + (0x1D436, 'M', u'c'), + (0x1D437, 'M', u'd'), + (0x1D438, 'M', u'e'), + (0x1D439, 'M', u'f'), + (0x1D43A, 'M', u'g'), + (0x1D43B, 'M', u'h'), + (0x1D43C, 'M', u'i'), + (0x1D43D, 'M', u'j'), + (0x1D43E, 'M', u'k'), + (0x1D43F, 'M', u'l'), + (0x1D440, 'M', u'm'), + (0x1D441, 'M', u'n'), + (0x1D442, 'M', u'o'), + (0x1D443, 'M', u'p'), + (0x1D444, 'M', u'q'), + (0x1D445, 'M', u'r'), + (0x1D446, 'M', u's'), + (0x1D447, 'M', u't'), + (0x1D448, 'M', u'u'), + (0x1D449, 'M', u'v'), + (0x1D44A, 'M', u'w'), + (0x1D44B, 'M', u'x'), + (0x1D44C, 'M', u'y'), + (0x1D44D, 'M', u'z'), + (0x1D44E, 'M', u'a'), + (0x1D44F, 'M', u'b'), + (0x1D450, 'M', u'c'), + (0x1D451, 'M', u'd'), + (0x1D452, 'M', u'e'), + (0x1D453, 'M', u'f'), + (0x1D454, 'M', u'g'), + (0x1D455, 'X'), + (0x1D456, 'M', u'i'), + (0x1D457, 'M', u'j'), + (0x1D458, 'M', u'k'), + (0x1D459, 'M', u'l'), + (0x1D45A, 'M', u'm'), + (0x1D45B, 'M', u'n'), + (0x1D45C, 'M', u'o'), + (0x1D45D, 'M', u'p'), + (0x1D45E, 'M', u'q'), + (0x1D45F, 'M', u'r'), + (0x1D460, 'M', u's'), + (0x1D461, 'M', u't'), + (0x1D462, 'M', u'u'), + (0x1D463, 'M', u'v'), + (0x1D464, 'M', u'w'), + (0x1D465, 'M', u'x'), + (0x1D466, 'M', u'y'), + (0x1D467, 'M', u'z'), + (0x1D468, 'M', u'a'), + (0x1D469, 'M', u'b'), + ] + +def _seg_59(): + return [ + (0x1D46A, 'M', u'c'), + (0x1D46B, 'M', u'd'), + (0x1D46C, 'M', u'e'), + (0x1D46D, 'M', u'f'), + (0x1D46E, 'M', u'g'), + (0x1D46F, 'M', u'h'), + (0x1D470, 'M', u'i'), + (0x1D471, 'M', u'j'), + (0x1D472, 'M', u'k'), + (0x1D473, 'M', u'l'), + (0x1D474, 'M', u'm'), + (0x1D475, 'M', u'n'), + (0x1D476, 'M', u'o'), + (0x1D477, 'M', u'p'), + (0x1D478, 'M', u'q'), + (0x1D479, 'M', u'r'), + (0x1D47A, 'M', u's'), + (0x1D47B, 'M', u't'), + (0x1D47C, 'M', u'u'), + (0x1D47D, 'M', u'v'), + (0x1D47E, 'M', u'w'), + (0x1D47F, 'M', u'x'), + (0x1D480, 'M', u'y'), + (0x1D481, 'M', u'z'), + (0x1D482, 'M', u'a'), + (0x1D483, 'M', u'b'), + (0x1D484, 'M', u'c'), + (0x1D485, 'M', u'd'), + (0x1D486, 'M', u'e'), + (0x1D487, 'M', u'f'), + (0x1D488, 'M', u'g'), + (0x1D489, 'M', u'h'), + (0x1D48A, 'M', u'i'), + (0x1D48B, 'M', u'j'), + (0x1D48C, 'M', u'k'), + (0x1D48D, 'M', u'l'), + (0x1D48E, 'M', u'm'), + (0x1D48F, 'M', u'n'), + (0x1D490, 'M', u'o'), + (0x1D491, 'M', u'p'), + (0x1D492, 'M', u'q'), + (0x1D493, 'M', u'r'), + (0x1D494, 'M', u's'), + (0x1D495, 'M', u't'), + (0x1D496, 'M', u'u'), + (0x1D497, 'M', u'v'), + (0x1D498, 'M', u'w'), + (0x1D499, 'M', u'x'), + (0x1D49A, 'M', u'y'), + (0x1D49B, 'M', u'z'), + (0x1D49C, 'M', u'a'), + (0x1D49D, 'X'), + (0x1D49E, 'M', u'c'), + (0x1D49F, 'M', u'd'), + (0x1D4A0, 'X'), + (0x1D4A2, 'M', u'g'), + (0x1D4A3, 'X'), + (0x1D4A5, 'M', u'j'), + (0x1D4A6, 'M', u'k'), + (0x1D4A7, 'X'), + (0x1D4A9, 'M', u'n'), + (0x1D4AA, 'M', u'o'), + (0x1D4AB, 'M', u'p'), + (0x1D4AC, 'M', u'q'), + (0x1D4AD, 'X'), + (0x1D4AE, 'M', u's'), + (0x1D4AF, 'M', u't'), + (0x1D4B0, 'M', u'u'), + (0x1D4B1, 'M', u'v'), + (0x1D4B2, 'M', u'w'), + (0x1D4B3, 'M', u'x'), + (0x1D4B4, 'M', u'y'), + (0x1D4B5, 'M', u'z'), + (0x1D4B6, 'M', u'a'), + (0x1D4B7, 'M', u'b'), + (0x1D4B8, 'M', u'c'), + (0x1D4B9, 'M', u'd'), + (0x1D4BA, 'X'), + (0x1D4BB, 'M', u'f'), + (0x1D4BC, 'X'), + (0x1D4BD, 'M', u'h'), + (0x1D4BE, 'M', u'i'), + (0x1D4BF, 'M', u'j'), + (0x1D4C0, 'M', u'k'), + (0x1D4C1, 'M', u'l'), + (0x1D4C2, 'M', u'm'), + (0x1D4C3, 'M', u'n'), + (0x1D4C4, 'X'), + (0x1D4C5, 'M', u'p'), + (0x1D4C6, 'M', u'q'), + (0x1D4C7, 'M', u'r'), + (0x1D4C8, 'M', u's'), + (0x1D4C9, 'M', u't'), + (0x1D4CA, 'M', u'u'), + (0x1D4CB, 'M', u'v'), + (0x1D4CC, 'M', u'w'), + (0x1D4CD, 'M', u'x'), + (0x1D4CE, 'M', u'y'), + (0x1D4CF, 'M', u'z'), + (0x1D4D0, 'M', u'a'), + ] + +def _seg_60(): + return [ + (0x1D4D1, 'M', u'b'), + (0x1D4D2, 'M', u'c'), + (0x1D4D3, 'M', u'd'), + (0x1D4D4, 'M', u'e'), + (0x1D4D5, 'M', u'f'), + (0x1D4D6, 'M', u'g'), + (0x1D4D7, 'M', u'h'), + (0x1D4D8, 'M', u'i'), + (0x1D4D9, 'M', u'j'), + (0x1D4DA, 'M', u'k'), + (0x1D4DB, 'M', u'l'), + (0x1D4DC, 'M', u'm'), + (0x1D4DD, 'M', u'n'), + (0x1D4DE, 'M', u'o'), + (0x1D4DF, 'M', u'p'), + (0x1D4E0, 'M', u'q'), + (0x1D4E1, 'M', u'r'), + (0x1D4E2, 'M', u's'), + (0x1D4E3, 'M', u't'), + (0x1D4E4, 'M', u'u'), + (0x1D4E5, 'M', u'v'), + (0x1D4E6, 'M', u'w'), + (0x1D4E7, 'M', u'x'), + (0x1D4E8, 'M', u'y'), + (0x1D4E9, 'M', u'z'), + (0x1D4EA, 'M', u'a'), + (0x1D4EB, 'M', u'b'), + (0x1D4EC, 'M', u'c'), + (0x1D4ED, 'M', u'd'), + (0x1D4EE, 'M', u'e'), + (0x1D4EF, 'M', u'f'), + (0x1D4F0, 'M', u'g'), + (0x1D4F1, 'M', u'h'), + (0x1D4F2, 'M', u'i'), + (0x1D4F3, 'M', u'j'), + (0x1D4F4, 'M', u'k'), + (0x1D4F5, 'M', u'l'), + (0x1D4F6, 'M', u'm'), + (0x1D4F7, 'M', u'n'), + (0x1D4F8, 'M', u'o'), + (0x1D4F9, 'M', u'p'), + (0x1D4FA, 'M', u'q'), + (0x1D4FB, 'M', u'r'), + (0x1D4FC, 'M', u's'), + (0x1D4FD, 'M', u't'), + (0x1D4FE, 'M', u'u'), + (0x1D4FF, 'M', u'v'), + (0x1D500, 'M', u'w'), + (0x1D501, 'M', u'x'), + (0x1D502, 'M', u'y'), + (0x1D503, 'M', u'z'), + (0x1D504, 'M', u'a'), + (0x1D505, 'M', u'b'), + (0x1D506, 'X'), + (0x1D507, 'M', u'd'), + (0x1D508, 'M', u'e'), + (0x1D509, 'M', u'f'), + (0x1D50A, 'M', u'g'), + (0x1D50B, 'X'), + (0x1D50D, 'M', u'j'), + (0x1D50E, 'M', u'k'), + (0x1D50F, 'M', u'l'), + (0x1D510, 'M', u'm'), + (0x1D511, 'M', u'n'), + (0x1D512, 'M', u'o'), + (0x1D513, 'M', u'p'), + (0x1D514, 'M', u'q'), + (0x1D515, 'X'), + (0x1D516, 'M', u's'), + (0x1D517, 'M', u't'), + (0x1D518, 'M', u'u'), + (0x1D519, 'M', u'v'), + (0x1D51A, 'M', u'w'), + (0x1D51B, 'M', u'x'), + (0x1D51C, 'M', u'y'), + (0x1D51D, 'X'), + (0x1D51E, 'M', u'a'), + (0x1D51F, 'M', u'b'), + (0x1D520, 'M', u'c'), + (0x1D521, 'M', u'd'), + (0x1D522, 'M', u'e'), + (0x1D523, 'M', u'f'), + (0x1D524, 'M', u'g'), + (0x1D525, 'M', u'h'), + (0x1D526, 'M', u'i'), + (0x1D527, 'M', u'j'), + (0x1D528, 'M', u'k'), + (0x1D529, 'M', u'l'), + (0x1D52A, 'M', u'm'), + (0x1D52B, 'M', u'n'), + (0x1D52C, 'M', u'o'), + (0x1D52D, 'M', u'p'), + (0x1D52E, 'M', u'q'), + (0x1D52F, 'M', u'r'), + (0x1D530, 'M', u's'), + (0x1D531, 'M', u't'), + (0x1D532, 'M', u'u'), + (0x1D533, 'M', u'v'), + (0x1D534, 'M', u'w'), + (0x1D535, 'M', u'x'), + ] + +def _seg_61(): + return [ + (0x1D536, 'M', u'y'), + (0x1D537, 'M', u'z'), + (0x1D538, 'M', u'a'), + (0x1D539, 'M', u'b'), + (0x1D53A, 'X'), + (0x1D53B, 'M', u'd'), + (0x1D53C, 'M', u'e'), + (0x1D53D, 'M', u'f'), + (0x1D53E, 'M', u'g'), + (0x1D53F, 'X'), + (0x1D540, 'M', u'i'), + (0x1D541, 'M', u'j'), + (0x1D542, 'M', u'k'), + (0x1D543, 'M', u'l'), + (0x1D544, 'M', u'm'), + (0x1D545, 'X'), + (0x1D546, 'M', u'o'), + (0x1D547, 'X'), + (0x1D54A, 'M', u's'), + (0x1D54B, 'M', u't'), + (0x1D54C, 'M', u'u'), + (0x1D54D, 'M', u'v'), + (0x1D54E, 'M', u'w'), + (0x1D54F, 'M', u'x'), + (0x1D550, 'M', u'y'), + (0x1D551, 'X'), + (0x1D552, 'M', u'a'), + (0x1D553, 'M', u'b'), + (0x1D554, 'M', u'c'), + (0x1D555, 'M', u'd'), + (0x1D556, 'M', u'e'), + (0x1D557, 'M', u'f'), + (0x1D558, 'M', u'g'), + (0x1D559, 'M', u'h'), + (0x1D55A, 'M', u'i'), + (0x1D55B, 'M', u'j'), + (0x1D55C, 'M', u'k'), + (0x1D55D, 'M', u'l'), + (0x1D55E, 'M', u'm'), + (0x1D55F, 'M', u'n'), + (0x1D560, 'M', u'o'), + (0x1D561, 'M', u'p'), + (0x1D562, 'M', u'q'), + (0x1D563, 'M', u'r'), + (0x1D564, 'M', u's'), + (0x1D565, 'M', u't'), + (0x1D566, 'M', u'u'), + (0x1D567, 'M', u'v'), + (0x1D568, 'M', u'w'), + (0x1D569, 'M', u'x'), + (0x1D56A, 'M', u'y'), + (0x1D56B, 'M', u'z'), + (0x1D56C, 'M', u'a'), + (0x1D56D, 'M', u'b'), + (0x1D56E, 'M', u'c'), + (0x1D56F, 'M', u'd'), + (0x1D570, 'M', u'e'), + (0x1D571, 'M', u'f'), + (0x1D572, 'M', u'g'), + (0x1D573, 'M', u'h'), + (0x1D574, 'M', u'i'), + (0x1D575, 'M', u'j'), + (0x1D576, 'M', u'k'), + (0x1D577, 'M', u'l'), + (0x1D578, 'M', u'm'), + (0x1D579, 'M', u'n'), + (0x1D57A, 'M', u'o'), + (0x1D57B, 'M', u'p'), + (0x1D57C, 'M', u'q'), + (0x1D57D, 'M', u'r'), + (0x1D57E, 'M', u's'), + (0x1D57F, 'M', u't'), + (0x1D580, 'M', u'u'), + (0x1D581, 'M', u'v'), + (0x1D582, 'M', u'w'), + (0x1D583, 'M', u'x'), + (0x1D584, 'M', u'y'), + (0x1D585, 'M', u'z'), + (0x1D586, 'M', u'a'), + (0x1D587, 'M', u'b'), + (0x1D588, 'M', u'c'), + (0x1D589, 'M', u'd'), + (0x1D58A, 'M', u'e'), + (0x1D58B, 'M', u'f'), + (0x1D58C, 'M', u'g'), + (0x1D58D, 'M', u'h'), + (0x1D58E, 'M', u'i'), + (0x1D58F, 'M', u'j'), + (0x1D590, 'M', u'k'), + (0x1D591, 'M', u'l'), + (0x1D592, 'M', u'm'), + (0x1D593, 'M', u'n'), + (0x1D594, 'M', u'o'), + (0x1D595, 'M', u'p'), + (0x1D596, 'M', u'q'), + (0x1D597, 'M', u'r'), + (0x1D598, 'M', u's'), + (0x1D599, 'M', u't'), + (0x1D59A, 'M', u'u'), + (0x1D59B, 'M', u'v'), + ] + +def _seg_62(): + return [ + (0x1D59C, 'M', u'w'), + (0x1D59D, 'M', u'x'), + (0x1D59E, 'M', u'y'), + (0x1D59F, 'M', u'z'), + (0x1D5A0, 'M', u'a'), + (0x1D5A1, 'M', u'b'), + (0x1D5A2, 'M', u'c'), + (0x1D5A3, 'M', u'd'), + (0x1D5A4, 'M', u'e'), + (0x1D5A5, 'M', u'f'), + (0x1D5A6, 'M', u'g'), + (0x1D5A7, 'M', u'h'), + (0x1D5A8, 'M', u'i'), + (0x1D5A9, 'M', u'j'), + (0x1D5AA, 'M', u'k'), + (0x1D5AB, 'M', u'l'), + (0x1D5AC, 'M', u'm'), + (0x1D5AD, 'M', u'n'), + (0x1D5AE, 'M', u'o'), + (0x1D5AF, 'M', u'p'), + (0x1D5B0, 'M', u'q'), + (0x1D5B1, 'M', u'r'), + (0x1D5B2, 'M', u's'), + (0x1D5B3, 'M', u't'), + (0x1D5B4, 'M', u'u'), + (0x1D5B5, 'M', u'v'), + (0x1D5B6, 'M', u'w'), + (0x1D5B7, 'M', u'x'), + (0x1D5B8, 'M', u'y'), + (0x1D5B9, 'M', u'z'), + (0x1D5BA, 'M', u'a'), + (0x1D5BB, 'M', u'b'), + (0x1D5BC, 'M', u'c'), + (0x1D5BD, 'M', u'd'), + (0x1D5BE, 'M', u'e'), + (0x1D5BF, 'M', u'f'), + (0x1D5C0, 'M', u'g'), + (0x1D5C1, 'M', u'h'), + (0x1D5C2, 'M', u'i'), + (0x1D5C3, 'M', u'j'), + (0x1D5C4, 'M', u'k'), + (0x1D5C5, 'M', u'l'), + (0x1D5C6, 'M', u'm'), + (0x1D5C7, 'M', u'n'), + (0x1D5C8, 'M', u'o'), + (0x1D5C9, 'M', u'p'), + (0x1D5CA, 'M', u'q'), + (0x1D5CB, 'M', u'r'), + (0x1D5CC, 'M', u's'), + (0x1D5CD, 'M', u't'), + (0x1D5CE, 'M', u'u'), + (0x1D5CF, 'M', u'v'), + (0x1D5D0, 'M', u'w'), + (0x1D5D1, 'M', u'x'), + (0x1D5D2, 'M', u'y'), + (0x1D5D3, 'M', u'z'), + (0x1D5D4, 'M', u'a'), + (0x1D5D5, 'M', u'b'), + (0x1D5D6, 'M', u'c'), + (0x1D5D7, 'M', u'd'), + (0x1D5D8, 'M', u'e'), + (0x1D5D9, 'M', u'f'), + (0x1D5DA, 'M', u'g'), + (0x1D5DB, 'M', u'h'), + (0x1D5DC, 'M', u'i'), + (0x1D5DD, 'M', u'j'), + (0x1D5DE, 'M', u'k'), + (0x1D5DF, 'M', u'l'), + (0x1D5E0, 'M', u'm'), + (0x1D5E1, 'M', u'n'), + (0x1D5E2, 'M', u'o'), + (0x1D5E3, 'M', u'p'), + (0x1D5E4, 'M', u'q'), + (0x1D5E5, 'M', u'r'), + (0x1D5E6, 'M', u's'), + (0x1D5E7, 'M', u't'), + (0x1D5E8, 'M', u'u'), + (0x1D5E9, 'M', u'v'), + (0x1D5EA, 'M', u'w'), + (0x1D5EB, 'M', u'x'), + (0x1D5EC, 'M', u'y'), + (0x1D5ED, 'M', u'z'), + (0x1D5EE, 'M', u'a'), + (0x1D5EF, 'M', u'b'), + (0x1D5F0, 'M', u'c'), + (0x1D5F1, 'M', u'd'), + (0x1D5F2, 'M', u'e'), + (0x1D5F3, 'M', u'f'), + (0x1D5F4, 'M', u'g'), + (0x1D5F5, 'M', u'h'), + (0x1D5F6, 'M', u'i'), + (0x1D5F7, 'M', u'j'), + (0x1D5F8, 'M', u'k'), + (0x1D5F9, 'M', u'l'), + (0x1D5FA, 'M', u'm'), + (0x1D5FB, 'M', u'n'), + (0x1D5FC, 'M', u'o'), + (0x1D5FD, 'M', u'p'), + (0x1D5FE, 'M', u'q'), + (0x1D5FF, 'M', u'r'), + ] + +def _seg_63(): + return [ + (0x1D600, 'M', u's'), + (0x1D601, 'M', u't'), + (0x1D602, 'M', u'u'), + (0x1D603, 'M', u'v'), + (0x1D604, 'M', u'w'), + (0x1D605, 'M', u'x'), + (0x1D606, 'M', u'y'), + (0x1D607, 'M', u'z'), + (0x1D608, 'M', u'a'), + (0x1D609, 'M', u'b'), + (0x1D60A, 'M', u'c'), + (0x1D60B, 'M', u'd'), + (0x1D60C, 'M', u'e'), + (0x1D60D, 'M', u'f'), + (0x1D60E, 'M', u'g'), + (0x1D60F, 'M', u'h'), + (0x1D610, 'M', u'i'), + (0x1D611, 'M', u'j'), + (0x1D612, 'M', u'k'), + (0x1D613, 'M', u'l'), + (0x1D614, 'M', u'm'), + (0x1D615, 'M', u'n'), + (0x1D616, 'M', u'o'), + (0x1D617, 'M', u'p'), + (0x1D618, 'M', u'q'), + (0x1D619, 'M', u'r'), + (0x1D61A, 'M', u's'), + (0x1D61B, 'M', u't'), + (0x1D61C, 'M', u'u'), + (0x1D61D, 'M', u'v'), + (0x1D61E, 'M', u'w'), + (0x1D61F, 'M', u'x'), + (0x1D620, 'M', u'y'), + (0x1D621, 'M', u'z'), + (0x1D622, 'M', u'a'), + (0x1D623, 'M', u'b'), + (0x1D624, 'M', u'c'), + (0x1D625, 'M', u'd'), + (0x1D626, 'M', u'e'), + (0x1D627, 'M', u'f'), + (0x1D628, 'M', u'g'), + (0x1D629, 'M', u'h'), + (0x1D62A, 'M', u'i'), + (0x1D62B, 'M', u'j'), + (0x1D62C, 'M', u'k'), + (0x1D62D, 'M', u'l'), + (0x1D62E, 'M', u'm'), + (0x1D62F, 'M', u'n'), + (0x1D630, 'M', u'o'), + (0x1D631, 'M', u'p'), + (0x1D632, 'M', u'q'), + (0x1D633, 'M', u'r'), + (0x1D634, 'M', u's'), + (0x1D635, 'M', u't'), + (0x1D636, 'M', u'u'), + (0x1D637, 'M', u'v'), + (0x1D638, 'M', u'w'), + (0x1D639, 'M', u'x'), + (0x1D63A, 'M', u'y'), + (0x1D63B, 'M', u'z'), + (0x1D63C, 'M', u'a'), + (0x1D63D, 'M', u'b'), + (0x1D63E, 'M', u'c'), + (0x1D63F, 'M', u'd'), + (0x1D640, 'M', u'e'), + (0x1D641, 'M', u'f'), + (0x1D642, 'M', u'g'), + (0x1D643, 'M', u'h'), + (0x1D644, 'M', u'i'), + (0x1D645, 'M', u'j'), + (0x1D646, 'M', u'k'), + (0x1D647, 'M', u'l'), + (0x1D648, 'M', u'm'), + (0x1D649, 'M', u'n'), + (0x1D64A, 'M', u'o'), + (0x1D64B, 'M', u'p'), + (0x1D64C, 'M', u'q'), + (0x1D64D, 'M', u'r'), + (0x1D64E, 'M', u's'), + (0x1D64F, 'M', u't'), + (0x1D650, 'M', u'u'), + (0x1D651, 'M', u'v'), + (0x1D652, 'M', u'w'), + (0x1D653, 'M', u'x'), + (0x1D654, 'M', u'y'), + (0x1D655, 'M', u'z'), + (0x1D656, 'M', u'a'), + (0x1D657, 'M', u'b'), + (0x1D658, 'M', u'c'), + (0x1D659, 'M', u'd'), + (0x1D65A, 'M', u'e'), + (0x1D65B, 'M', u'f'), + (0x1D65C, 'M', u'g'), + (0x1D65D, 'M', u'h'), + (0x1D65E, 'M', u'i'), + (0x1D65F, 'M', u'j'), + (0x1D660, 'M', u'k'), + (0x1D661, 'M', u'l'), + (0x1D662, 'M', u'm'), + (0x1D663, 'M', u'n'), + ] + +def _seg_64(): + return [ + (0x1D664, 'M', u'o'), + (0x1D665, 'M', u'p'), + (0x1D666, 'M', u'q'), + (0x1D667, 'M', u'r'), + (0x1D668, 'M', u's'), + (0x1D669, 'M', u't'), + (0x1D66A, 'M', u'u'), + (0x1D66B, 'M', u'v'), + (0x1D66C, 'M', u'w'), + (0x1D66D, 'M', u'x'), + (0x1D66E, 'M', u'y'), + (0x1D66F, 'M', u'z'), + (0x1D670, 'M', u'a'), + (0x1D671, 'M', u'b'), + (0x1D672, 'M', u'c'), + (0x1D673, 'M', u'd'), + (0x1D674, 'M', u'e'), + (0x1D675, 'M', u'f'), + (0x1D676, 'M', u'g'), + (0x1D677, 'M', u'h'), + (0x1D678, 'M', u'i'), + (0x1D679, 'M', u'j'), + (0x1D67A, 'M', u'k'), + (0x1D67B, 'M', u'l'), + (0x1D67C, 'M', u'm'), + (0x1D67D, 'M', u'n'), + (0x1D67E, 'M', u'o'), + (0x1D67F, 'M', u'p'), + (0x1D680, 'M', u'q'), + (0x1D681, 'M', u'r'), + (0x1D682, 'M', u's'), + (0x1D683, 'M', u't'), + (0x1D684, 'M', u'u'), + (0x1D685, 'M', u'v'), + (0x1D686, 'M', u'w'), + (0x1D687, 'M', u'x'), + (0x1D688, 'M', u'y'), + (0x1D689, 'M', u'z'), + (0x1D68A, 'M', u'a'), + (0x1D68B, 'M', u'b'), + (0x1D68C, 'M', u'c'), + (0x1D68D, 'M', u'd'), + (0x1D68E, 'M', u'e'), + (0x1D68F, 'M', u'f'), + (0x1D690, 'M', u'g'), + (0x1D691, 'M', u'h'), + (0x1D692, 'M', u'i'), + (0x1D693, 'M', u'j'), + (0x1D694, 'M', u'k'), + (0x1D695, 'M', u'l'), + (0x1D696, 'M', u'm'), + (0x1D697, 'M', u'n'), + (0x1D698, 'M', u'o'), + (0x1D699, 'M', u'p'), + (0x1D69A, 'M', u'q'), + (0x1D69B, 'M', u'r'), + (0x1D69C, 'M', u's'), + (0x1D69D, 'M', u't'), + (0x1D69E, 'M', u'u'), + (0x1D69F, 'M', u'v'), + (0x1D6A0, 'M', u'w'), + (0x1D6A1, 'M', u'x'), + (0x1D6A2, 'M', u'y'), + (0x1D6A3, 'M', u'z'), + (0x1D6A4, 'M', u'ı'), + (0x1D6A5, 'M', u'ȷ'), + (0x1D6A6, 'X'), + (0x1D6A8, 'M', u'α'), + (0x1D6A9, 'M', u'β'), + (0x1D6AA, 'M', u'γ'), + (0x1D6AB, 'M', u'δ'), + (0x1D6AC, 'M', u'ε'), + (0x1D6AD, 'M', u'ζ'), + (0x1D6AE, 'M', u'η'), + (0x1D6AF, 'M', u'θ'), + (0x1D6B0, 'M', u'ι'), + (0x1D6B1, 'M', u'κ'), + (0x1D6B2, 'M', u'λ'), + (0x1D6B3, 'M', u'μ'), + (0x1D6B4, 'M', u'ν'), + (0x1D6B5, 'M', u'ξ'), + (0x1D6B6, 'M', u'ο'), + (0x1D6B7, 'M', u'π'), + (0x1D6B8, 'M', u'ρ'), + (0x1D6B9, 'M', u'θ'), + (0x1D6BA, 'M', u'σ'), + (0x1D6BB, 'M', u'τ'), + (0x1D6BC, 'M', u'υ'), + (0x1D6BD, 'M', u'φ'), + (0x1D6BE, 'M', u'χ'), + (0x1D6BF, 'M', u'ψ'), + (0x1D6C0, 'M', u'ω'), + (0x1D6C1, 'M', u'∇'), + (0x1D6C2, 'M', u'α'), + (0x1D6C3, 'M', u'β'), + (0x1D6C4, 'M', u'γ'), + (0x1D6C5, 'M', u'δ'), + (0x1D6C6, 'M', u'ε'), + (0x1D6C7, 'M', u'ζ'), + (0x1D6C8, 'M', u'η'), + ] + +def _seg_65(): + return [ + (0x1D6C9, 'M', u'θ'), + (0x1D6CA, 'M', u'ι'), + (0x1D6CB, 'M', u'κ'), + (0x1D6CC, 'M', u'λ'), + (0x1D6CD, 'M', u'μ'), + (0x1D6CE, 'M', u'ν'), + (0x1D6CF, 'M', u'ξ'), + (0x1D6D0, 'M', u'ο'), + (0x1D6D1, 'M', u'π'), + (0x1D6D2, 'M', u'ρ'), + (0x1D6D3, 'M', u'σ'), + (0x1D6D5, 'M', u'τ'), + (0x1D6D6, 'M', u'υ'), + (0x1D6D7, 'M', u'φ'), + (0x1D6D8, 'M', u'χ'), + (0x1D6D9, 'M', u'ψ'), + (0x1D6DA, 'M', u'ω'), + (0x1D6DB, 'M', u'∂'), + (0x1D6DC, 'M', u'ε'), + (0x1D6DD, 'M', u'θ'), + (0x1D6DE, 'M', u'κ'), + (0x1D6DF, 'M', u'φ'), + (0x1D6E0, 'M', u'ρ'), + (0x1D6E1, 'M', u'π'), + (0x1D6E2, 'M', u'α'), + (0x1D6E3, 'M', u'β'), + (0x1D6E4, 'M', u'γ'), + (0x1D6E5, 'M', u'δ'), + (0x1D6E6, 'M', u'ε'), + (0x1D6E7, 'M', u'ζ'), + (0x1D6E8, 'M', u'η'), + (0x1D6E9, 'M', u'θ'), + (0x1D6EA, 'M', u'ι'), + (0x1D6EB, 'M', u'κ'), + (0x1D6EC, 'M', u'λ'), + (0x1D6ED, 'M', u'μ'), + (0x1D6EE, 'M', u'ν'), + (0x1D6EF, 'M', u'ξ'), + (0x1D6F0, 'M', u'ο'), + (0x1D6F1, 'M', u'π'), + (0x1D6F2, 'M', u'ρ'), + (0x1D6F3, 'M', u'θ'), + (0x1D6F4, 'M', u'σ'), + (0x1D6F5, 'M', u'τ'), + (0x1D6F6, 'M', u'υ'), + (0x1D6F7, 'M', u'φ'), + (0x1D6F8, 'M', u'χ'), + (0x1D6F9, 'M', u'ψ'), + (0x1D6FA, 'M', u'ω'), + (0x1D6FB, 'M', u'∇'), + (0x1D6FC, 'M', u'α'), + (0x1D6FD, 'M', u'β'), + (0x1D6FE, 'M', u'γ'), + (0x1D6FF, 'M', u'δ'), + (0x1D700, 'M', u'ε'), + (0x1D701, 'M', u'ζ'), + (0x1D702, 'M', u'η'), + (0x1D703, 'M', u'θ'), + (0x1D704, 'M', u'ι'), + (0x1D705, 'M', u'κ'), + (0x1D706, 'M', u'λ'), + (0x1D707, 'M', u'μ'), + (0x1D708, 'M', u'ν'), + (0x1D709, 'M', u'ξ'), + (0x1D70A, 'M', u'ο'), + (0x1D70B, 'M', u'π'), + (0x1D70C, 'M', u'ρ'), + (0x1D70D, 'M', u'σ'), + (0x1D70F, 'M', u'τ'), + (0x1D710, 'M', u'υ'), + (0x1D711, 'M', u'φ'), + (0x1D712, 'M', u'χ'), + (0x1D713, 'M', u'ψ'), + (0x1D714, 'M', u'ω'), + (0x1D715, 'M', u'∂'), + (0x1D716, 'M', u'ε'), + (0x1D717, 'M', u'θ'), + (0x1D718, 'M', u'κ'), + (0x1D719, 'M', u'φ'), + (0x1D71A, 'M', u'ρ'), + (0x1D71B, 'M', u'π'), + (0x1D71C, 'M', u'α'), + (0x1D71D, 'M', u'β'), + (0x1D71E, 'M', u'γ'), + (0x1D71F, 'M', u'δ'), + (0x1D720, 'M', u'ε'), + (0x1D721, 'M', u'ζ'), + (0x1D722, 'M', u'η'), + (0x1D723, 'M', u'θ'), + (0x1D724, 'M', u'ι'), + (0x1D725, 'M', u'κ'), + (0x1D726, 'M', u'λ'), + (0x1D727, 'M', u'μ'), + (0x1D728, 'M', u'ν'), + (0x1D729, 'M', u'ξ'), + (0x1D72A, 'M', u'ο'), + (0x1D72B, 'M', u'π'), + (0x1D72C, 'M', u'ρ'), + (0x1D72D, 'M', u'θ'), + (0x1D72E, 'M', u'σ'), + ] + +def _seg_66(): + return [ + (0x1D72F, 'M', u'τ'), + (0x1D730, 'M', u'υ'), + (0x1D731, 'M', u'φ'), + (0x1D732, 'M', u'χ'), + (0x1D733, 'M', u'ψ'), + (0x1D734, 'M', u'ω'), + (0x1D735, 'M', u'∇'), + (0x1D736, 'M', u'α'), + (0x1D737, 'M', u'β'), + (0x1D738, 'M', u'γ'), + (0x1D739, 'M', u'δ'), + (0x1D73A, 'M', u'ε'), + (0x1D73B, 'M', u'ζ'), + (0x1D73C, 'M', u'η'), + (0x1D73D, 'M', u'θ'), + (0x1D73E, 'M', u'ι'), + (0x1D73F, 'M', u'κ'), + (0x1D740, 'M', u'λ'), + (0x1D741, 'M', u'μ'), + (0x1D742, 'M', u'ν'), + (0x1D743, 'M', u'ξ'), + (0x1D744, 'M', u'ο'), + (0x1D745, 'M', u'π'), + (0x1D746, 'M', u'ρ'), + (0x1D747, 'M', u'σ'), + (0x1D749, 'M', u'τ'), + (0x1D74A, 'M', u'υ'), + (0x1D74B, 'M', u'φ'), + (0x1D74C, 'M', u'χ'), + (0x1D74D, 'M', u'ψ'), + (0x1D74E, 'M', u'ω'), + (0x1D74F, 'M', u'∂'), + (0x1D750, 'M', u'ε'), + (0x1D751, 'M', u'θ'), + (0x1D752, 'M', u'κ'), + (0x1D753, 'M', u'φ'), + (0x1D754, 'M', u'ρ'), + (0x1D755, 'M', u'π'), + (0x1D756, 'M', u'α'), + (0x1D757, 'M', u'β'), + (0x1D758, 'M', u'γ'), + (0x1D759, 'M', u'δ'), + (0x1D75A, 'M', u'ε'), + (0x1D75B, 'M', u'ζ'), + (0x1D75C, 'M', u'η'), + (0x1D75D, 'M', u'θ'), + (0x1D75E, 'M', u'ι'), + (0x1D75F, 'M', u'κ'), + (0x1D760, 'M', u'λ'), + (0x1D761, 'M', u'μ'), + (0x1D762, 'M', u'ν'), + (0x1D763, 'M', u'ξ'), + (0x1D764, 'M', u'ο'), + (0x1D765, 'M', u'π'), + (0x1D766, 'M', u'ρ'), + (0x1D767, 'M', u'θ'), + (0x1D768, 'M', u'σ'), + (0x1D769, 'M', u'τ'), + (0x1D76A, 'M', u'υ'), + (0x1D76B, 'M', u'φ'), + (0x1D76C, 'M', u'χ'), + (0x1D76D, 'M', u'ψ'), + (0x1D76E, 'M', u'ω'), + (0x1D76F, 'M', u'∇'), + (0x1D770, 'M', u'α'), + (0x1D771, 'M', u'β'), + (0x1D772, 'M', u'γ'), + (0x1D773, 'M', u'δ'), + (0x1D774, 'M', u'ε'), + (0x1D775, 'M', u'ζ'), + (0x1D776, 'M', u'η'), + (0x1D777, 'M', u'θ'), + (0x1D778, 'M', u'ι'), + (0x1D779, 'M', u'κ'), + (0x1D77A, 'M', u'λ'), + (0x1D77B, 'M', u'μ'), + (0x1D77C, 'M', u'ν'), + (0x1D77D, 'M', u'ξ'), + (0x1D77E, 'M', u'ο'), + (0x1D77F, 'M', u'π'), + (0x1D780, 'M', u'ρ'), + (0x1D781, 'M', u'σ'), + (0x1D783, 'M', u'τ'), + (0x1D784, 'M', u'υ'), + (0x1D785, 'M', u'φ'), + (0x1D786, 'M', u'χ'), + (0x1D787, 'M', u'ψ'), + (0x1D788, 'M', u'ω'), + (0x1D789, 'M', u'∂'), + (0x1D78A, 'M', u'ε'), + (0x1D78B, 'M', u'θ'), + (0x1D78C, 'M', u'κ'), + (0x1D78D, 'M', u'φ'), + (0x1D78E, 'M', u'ρ'), + (0x1D78F, 'M', u'π'), + (0x1D790, 'M', u'α'), + (0x1D791, 'M', u'β'), + (0x1D792, 'M', u'γ'), + (0x1D793, 'M', u'δ'), + (0x1D794, 'M', u'ε'), + ] + +def _seg_67(): + return [ + (0x1D795, 'M', u'ζ'), + (0x1D796, 'M', u'η'), + (0x1D797, 'M', u'θ'), + (0x1D798, 'M', u'ι'), + (0x1D799, 'M', u'κ'), + (0x1D79A, 'M', u'λ'), + (0x1D79B, 'M', u'μ'), + (0x1D79C, 'M', u'ν'), + (0x1D79D, 'M', u'ξ'), + (0x1D79E, 'M', u'ο'), + (0x1D79F, 'M', u'π'), + (0x1D7A0, 'M', u'ρ'), + (0x1D7A1, 'M', u'θ'), + (0x1D7A2, 'M', u'σ'), + (0x1D7A3, 'M', u'τ'), + (0x1D7A4, 'M', u'υ'), + (0x1D7A5, 'M', u'φ'), + (0x1D7A6, 'M', u'χ'), + (0x1D7A7, 'M', u'ψ'), + (0x1D7A8, 'M', u'ω'), + (0x1D7A9, 'M', u'∇'), + (0x1D7AA, 'M', u'α'), + (0x1D7AB, 'M', u'β'), + (0x1D7AC, 'M', u'γ'), + (0x1D7AD, 'M', u'δ'), + (0x1D7AE, 'M', u'ε'), + (0x1D7AF, 'M', u'ζ'), + (0x1D7B0, 'M', u'η'), + (0x1D7B1, 'M', u'θ'), + (0x1D7B2, 'M', u'ι'), + (0x1D7B3, 'M', u'κ'), + (0x1D7B4, 'M', u'λ'), + (0x1D7B5, 'M', u'μ'), + (0x1D7B6, 'M', u'ν'), + (0x1D7B7, 'M', u'ξ'), + (0x1D7B8, 'M', u'ο'), + (0x1D7B9, 'M', u'π'), + (0x1D7BA, 'M', u'ρ'), + (0x1D7BB, 'M', u'σ'), + (0x1D7BD, 'M', u'τ'), + (0x1D7BE, 'M', u'υ'), + (0x1D7BF, 'M', u'φ'), + (0x1D7C0, 'M', u'χ'), + (0x1D7C1, 'M', u'ψ'), + (0x1D7C2, 'M', u'ω'), + (0x1D7C3, 'M', u'∂'), + (0x1D7C4, 'M', u'ε'), + (0x1D7C5, 'M', u'θ'), + (0x1D7C6, 'M', u'κ'), + (0x1D7C7, 'M', u'φ'), + (0x1D7C8, 'M', u'ρ'), + (0x1D7C9, 'M', u'π'), + (0x1D7CA, 'M', u'ϝ'), + (0x1D7CC, 'X'), + (0x1D7CE, 'M', u'0'), + (0x1D7CF, 'M', u'1'), + (0x1D7D0, 'M', u'2'), + (0x1D7D1, 'M', u'3'), + (0x1D7D2, 'M', u'4'), + (0x1D7D3, 'M', u'5'), + (0x1D7D4, 'M', u'6'), + (0x1D7D5, 'M', u'7'), + (0x1D7D6, 'M', u'8'), + (0x1D7D7, 'M', u'9'), + (0x1D7D8, 'M', u'0'), + (0x1D7D9, 'M', u'1'), + (0x1D7DA, 'M', u'2'), + (0x1D7DB, 'M', u'3'), + (0x1D7DC, 'M', u'4'), + (0x1D7DD, 'M', u'5'), + (0x1D7DE, 'M', u'6'), + (0x1D7DF, 'M', u'7'), + (0x1D7E0, 'M', u'8'), + (0x1D7E1, 'M', u'9'), + (0x1D7E2, 'M', u'0'), + (0x1D7E3, 'M', u'1'), + (0x1D7E4, 'M', u'2'), + (0x1D7E5, 'M', u'3'), + (0x1D7E6, 'M', u'4'), + (0x1D7E7, 'M', u'5'), + (0x1D7E8, 'M', u'6'), + (0x1D7E9, 'M', u'7'), + (0x1D7EA, 'M', u'8'), + (0x1D7EB, 'M', u'9'), + (0x1D7EC, 'M', u'0'), + (0x1D7ED, 'M', u'1'), + (0x1D7EE, 'M', u'2'), + (0x1D7EF, 'M', u'3'), + (0x1D7F0, 'M', u'4'), + (0x1D7F1, 'M', u'5'), + (0x1D7F2, 'M', u'6'), + (0x1D7F3, 'M', u'7'), + (0x1D7F4, 'M', u'8'), + (0x1D7F5, 'M', u'9'), + (0x1D7F6, 'M', u'0'), + (0x1D7F7, 'M', u'1'), + (0x1D7F8, 'M', u'2'), + (0x1D7F9, 'M', u'3'), + (0x1D7FA, 'M', u'4'), + (0x1D7FB, 'M', u'5'), + ] + +def _seg_68(): + return [ + (0x1D7FC, 'M', u'6'), + (0x1D7FD, 'M', u'7'), + (0x1D7FE, 'M', u'8'), + (0x1D7FF, 'M', u'9'), + (0x1D800, 'V'), + (0x1DA8C, 'X'), + (0x1DA9B, 'V'), + (0x1DAA0, 'X'), + (0x1DAA1, 'V'), + (0x1DAB0, 'X'), + (0x1E000, 'V'), + (0x1E007, 'X'), + (0x1E008, 'V'), + (0x1E019, 'X'), + (0x1E01B, 'V'), + (0x1E022, 'X'), + (0x1E023, 'V'), + (0x1E025, 'X'), + (0x1E026, 'V'), + (0x1E02B, 'X'), + (0x1E800, 'V'), + (0x1E8C5, 'X'), + (0x1E8C7, 'V'), + (0x1E8D7, 'X'), + (0x1E900, 'M', u'𞤢'), + (0x1E901, 'M', u'𞤣'), + (0x1E902, 'M', u'𞤤'), + (0x1E903, 'M', u'𞤥'), + (0x1E904, 'M', u'𞤦'), + (0x1E905, 'M', u'𞤧'), + (0x1E906, 'M', u'𞤨'), + (0x1E907, 'M', u'𞤩'), + (0x1E908, 'M', u'𞤪'), + (0x1E909, 'M', u'𞤫'), + (0x1E90A, 'M', u'𞤬'), + (0x1E90B, 'M', u'𞤭'), + (0x1E90C, 'M', u'𞤮'), + (0x1E90D, 'M', u'𞤯'), + (0x1E90E, 'M', u'𞤰'), + (0x1E90F, 'M', u'𞤱'), + (0x1E910, 'M', u'𞤲'), + (0x1E911, 'M', u'𞤳'), + (0x1E912, 'M', u'𞤴'), + (0x1E913, 'M', u'𞤵'), + (0x1E914, 'M', u'𞤶'), + (0x1E915, 'M', u'𞤷'), + (0x1E916, 'M', u'𞤸'), + (0x1E917, 'M', u'𞤹'), + (0x1E918, 'M', u'𞤺'), + (0x1E919, 'M', u'𞤻'), + (0x1E91A, 'M', u'𞤼'), + (0x1E91B, 'M', u'𞤽'), + (0x1E91C, 'M', u'𞤾'), + (0x1E91D, 'M', u'𞤿'), + (0x1E91E, 'M', u'𞥀'), + (0x1E91F, 'M', u'𞥁'), + (0x1E920, 'M', u'𞥂'), + (0x1E921, 'M', u'𞥃'), + (0x1E922, 'V'), + (0x1E94B, 'X'), + (0x1E950, 'V'), + (0x1E95A, 'X'), + (0x1E95E, 'V'), + (0x1E960, 'X'), + (0x1EE00, 'M', u'ا'), + (0x1EE01, 'M', u'ب'), + (0x1EE02, 'M', u'ج'), + (0x1EE03, 'M', u'د'), + (0x1EE04, 'X'), + (0x1EE05, 'M', u'و'), + (0x1EE06, 'M', u'ز'), + (0x1EE07, 'M', u'ح'), + (0x1EE08, 'M', u'ط'), + (0x1EE09, 'M', u'ي'), + (0x1EE0A, 'M', u'ك'), + (0x1EE0B, 'M', u'ل'), + (0x1EE0C, 'M', u'م'), + (0x1EE0D, 'M', u'ن'), + (0x1EE0E, 'M', u'س'), + (0x1EE0F, 'M', u'ع'), + (0x1EE10, 'M', u'ف'), + (0x1EE11, 'M', u'ص'), + (0x1EE12, 'M', u'ق'), + (0x1EE13, 'M', u'ر'), + (0x1EE14, 'M', u'ش'), + (0x1EE15, 'M', u'ت'), + (0x1EE16, 'M', u'ث'), + (0x1EE17, 'M', u'خ'), + (0x1EE18, 'M', u'ذ'), + (0x1EE19, 'M', u'ض'), + (0x1EE1A, 'M', u'ظ'), + (0x1EE1B, 'M', u'غ'), + (0x1EE1C, 'M', u'ٮ'), + (0x1EE1D, 'M', u'ں'), + (0x1EE1E, 'M', u'ڡ'), + (0x1EE1F, 'M', u'ٯ'), + (0x1EE20, 'X'), + (0x1EE21, 'M', u'ب'), + (0x1EE22, 'M', u'ج'), + (0x1EE23, 'X'), + ] + +def _seg_69(): + return [ + (0x1EE24, 'M', u'ه'), + (0x1EE25, 'X'), + (0x1EE27, 'M', u'ح'), + (0x1EE28, 'X'), + (0x1EE29, 'M', u'ي'), + (0x1EE2A, 'M', u'ك'), + (0x1EE2B, 'M', u'ل'), + (0x1EE2C, 'M', u'م'), + (0x1EE2D, 'M', u'ن'), + (0x1EE2E, 'M', u'س'), + (0x1EE2F, 'M', u'ع'), + (0x1EE30, 'M', u'ف'), + (0x1EE31, 'M', u'ص'), + (0x1EE32, 'M', u'ق'), + (0x1EE33, 'X'), + (0x1EE34, 'M', u'ش'), + (0x1EE35, 'M', u'ت'), + (0x1EE36, 'M', u'ث'), + (0x1EE37, 'M', u'خ'), + (0x1EE38, 'X'), + (0x1EE39, 'M', u'ض'), + (0x1EE3A, 'X'), + (0x1EE3B, 'M', u'غ'), + (0x1EE3C, 'X'), + (0x1EE42, 'M', u'ج'), + (0x1EE43, 'X'), + (0x1EE47, 'M', u'ح'), + (0x1EE48, 'X'), + (0x1EE49, 'M', u'ي'), + (0x1EE4A, 'X'), + (0x1EE4B, 'M', u'ل'), + (0x1EE4C, 'X'), + (0x1EE4D, 'M', u'ن'), + (0x1EE4E, 'M', u'س'), + (0x1EE4F, 'M', u'ع'), + (0x1EE50, 'X'), + (0x1EE51, 'M', u'ص'), + (0x1EE52, 'M', u'ق'), + (0x1EE53, 'X'), + (0x1EE54, 'M', u'ش'), + (0x1EE55, 'X'), + (0x1EE57, 'M', u'خ'), + (0x1EE58, 'X'), + (0x1EE59, 'M', u'ض'), + (0x1EE5A, 'X'), + (0x1EE5B, 'M', u'غ'), + (0x1EE5C, 'X'), + (0x1EE5D, 'M', u'ں'), + (0x1EE5E, 'X'), + (0x1EE5F, 'M', u'ٯ'), + (0x1EE60, 'X'), + (0x1EE61, 'M', u'ب'), + (0x1EE62, 'M', u'ج'), + (0x1EE63, 'X'), + (0x1EE64, 'M', u'ه'), + (0x1EE65, 'X'), + (0x1EE67, 'M', u'ح'), + (0x1EE68, 'M', u'ط'), + (0x1EE69, 'M', u'ي'), + (0x1EE6A, 'M', u'ك'), + (0x1EE6B, 'X'), + (0x1EE6C, 'M', u'م'), + (0x1EE6D, 'M', u'ن'), + (0x1EE6E, 'M', u'س'), + (0x1EE6F, 'M', u'ع'), + (0x1EE70, 'M', u'ف'), + (0x1EE71, 'M', u'ص'), + (0x1EE72, 'M', u'ق'), + (0x1EE73, 'X'), + (0x1EE74, 'M', u'ش'), + (0x1EE75, 'M', u'ت'), + (0x1EE76, 'M', u'ث'), + (0x1EE77, 'M', u'خ'), + (0x1EE78, 'X'), + (0x1EE79, 'M', u'ض'), + (0x1EE7A, 'M', u'ظ'), + (0x1EE7B, 'M', u'غ'), + (0x1EE7C, 'M', u'ٮ'), + (0x1EE7D, 'X'), + (0x1EE7E, 'M', u'ڡ'), + (0x1EE7F, 'X'), + (0x1EE80, 'M', u'ا'), + (0x1EE81, 'M', u'ب'), + (0x1EE82, 'M', u'ج'), + (0x1EE83, 'M', u'د'), + (0x1EE84, 'M', u'ه'), + (0x1EE85, 'M', u'و'), + (0x1EE86, 'M', u'ز'), + (0x1EE87, 'M', u'ح'), + (0x1EE88, 'M', u'ط'), + (0x1EE89, 'M', u'ي'), + (0x1EE8A, 'X'), + (0x1EE8B, 'M', u'ل'), + (0x1EE8C, 'M', u'م'), + (0x1EE8D, 'M', u'ن'), + (0x1EE8E, 'M', u'س'), + (0x1EE8F, 'M', u'ع'), + (0x1EE90, 'M', u'ف'), + (0x1EE91, 'M', u'ص'), + (0x1EE92, 'M', u'ق'), + ] + +def _seg_70(): + return [ + (0x1EE93, 'M', u'ر'), + (0x1EE94, 'M', u'ش'), + (0x1EE95, 'M', u'ت'), + (0x1EE96, 'M', u'ث'), + (0x1EE97, 'M', u'خ'), + (0x1EE98, 'M', u'ذ'), + (0x1EE99, 'M', u'ض'), + (0x1EE9A, 'M', u'ظ'), + (0x1EE9B, 'M', u'غ'), + (0x1EE9C, 'X'), + (0x1EEA1, 'M', u'ب'), + (0x1EEA2, 'M', u'ج'), + (0x1EEA3, 'M', u'د'), + (0x1EEA4, 'X'), + (0x1EEA5, 'M', u'و'), + (0x1EEA6, 'M', u'ز'), + (0x1EEA7, 'M', u'ح'), + (0x1EEA8, 'M', u'ط'), + (0x1EEA9, 'M', u'ي'), + (0x1EEAA, 'X'), + (0x1EEAB, 'M', u'ل'), + (0x1EEAC, 'M', u'م'), + (0x1EEAD, 'M', u'ن'), + (0x1EEAE, 'M', u'س'), + (0x1EEAF, 'M', u'ع'), + (0x1EEB0, 'M', u'ف'), + (0x1EEB1, 'M', u'ص'), + (0x1EEB2, 'M', u'ق'), + (0x1EEB3, 'M', u'ر'), + (0x1EEB4, 'M', u'ش'), + (0x1EEB5, 'M', u'ت'), + (0x1EEB6, 'M', u'ث'), + (0x1EEB7, 'M', u'خ'), + (0x1EEB8, 'M', u'ذ'), + (0x1EEB9, 'M', u'ض'), + (0x1EEBA, 'M', u'ظ'), + (0x1EEBB, 'M', u'غ'), + (0x1EEBC, 'X'), + (0x1EEF0, 'V'), + (0x1EEF2, 'X'), + (0x1F000, 'V'), + (0x1F02C, 'X'), + (0x1F030, 'V'), + (0x1F094, 'X'), + (0x1F0A0, 'V'), + (0x1F0AF, 'X'), + (0x1F0B1, 'V'), + (0x1F0C0, 'X'), + (0x1F0C1, 'V'), + (0x1F0D0, 'X'), + (0x1F0D1, 'V'), + (0x1F0F6, 'X'), + (0x1F101, '3', u'0,'), + (0x1F102, '3', u'1,'), + (0x1F103, '3', u'2,'), + (0x1F104, '3', u'3,'), + (0x1F105, '3', u'4,'), + (0x1F106, '3', u'5,'), + (0x1F107, '3', u'6,'), + (0x1F108, '3', u'7,'), + (0x1F109, '3', u'8,'), + (0x1F10A, '3', u'9,'), + (0x1F10B, 'V'), + (0x1F10D, 'X'), + (0x1F110, '3', u'(a)'), + (0x1F111, '3', u'(b)'), + (0x1F112, '3', u'(c)'), + (0x1F113, '3', u'(d)'), + (0x1F114, '3', u'(e)'), + (0x1F115, '3', u'(f)'), + (0x1F116, '3', u'(g)'), + (0x1F117, '3', u'(h)'), + (0x1F118, '3', u'(i)'), + (0x1F119, '3', u'(j)'), + (0x1F11A, '3', u'(k)'), + (0x1F11B, '3', u'(l)'), + (0x1F11C, '3', u'(m)'), + (0x1F11D, '3', u'(n)'), + (0x1F11E, '3', u'(o)'), + (0x1F11F, '3', u'(p)'), + (0x1F120, '3', u'(q)'), + (0x1F121, '3', u'(r)'), + (0x1F122, '3', u'(s)'), + (0x1F123, '3', u'(t)'), + (0x1F124, '3', u'(u)'), + (0x1F125, '3', u'(v)'), + (0x1F126, '3', u'(w)'), + (0x1F127, '3', u'(x)'), + (0x1F128, '3', u'(y)'), + (0x1F129, '3', u'(z)'), + (0x1F12A, 'M', u'〔s〕'), + (0x1F12B, 'M', u'c'), + (0x1F12C, 'M', u'r'), + (0x1F12D, 'M', u'cd'), + (0x1F12E, 'M', u'wz'), + (0x1F12F, 'X'), + (0x1F130, 'M', u'a'), + (0x1F131, 'M', u'b'), + (0x1F132, 'M', u'c'), + (0x1F133, 'M', u'd'), + ] + +def _seg_71(): + return [ + (0x1F134, 'M', u'e'), + (0x1F135, 'M', u'f'), + (0x1F136, 'M', u'g'), + (0x1F137, 'M', u'h'), + (0x1F138, 'M', u'i'), + (0x1F139, 'M', u'j'), + (0x1F13A, 'M', u'k'), + (0x1F13B, 'M', u'l'), + (0x1F13C, 'M', u'm'), + (0x1F13D, 'M', u'n'), + (0x1F13E, 'M', u'o'), + (0x1F13F, 'M', u'p'), + (0x1F140, 'M', u'q'), + (0x1F141, 'M', u'r'), + (0x1F142, 'M', u's'), + (0x1F143, 'M', u't'), + (0x1F144, 'M', u'u'), + (0x1F145, 'M', u'v'), + (0x1F146, 'M', u'w'), + (0x1F147, 'M', u'x'), + (0x1F148, 'M', u'y'), + (0x1F149, 'M', u'z'), + (0x1F14A, 'M', u'hv'), + (0x1F14B, 'M', u'mv'), + (0x1F14C, 'M', u'sd'), + (0x1F14D, 'M', u'ss'), + (0x1F14E, 'M', u'ppv'), + (0x1F14F, 'M', u'wc'), + (0x1F150, 'V'), + (0x1F16A, 'M', u'mc'), + (0x1F16B, 'M', u'md'), + (0x1F16C, 'X'), + (0x1F170, 'V'), + (0x1F190, 'M', u'dj'), + (0x1F191, 'V'), + (0x1F1AD, 'X'), + (0x1F1E6, 'V'), + (0x1F200, 'M', u'ほか'), + (0x1F201, 'M', u'ココ'), + (0x1F202, 'M', u'サ'), + (0x1F203, 'X'), + (0x1F210, 'M', u'手'), + (0x1F211, 'M', u'字'), + (0x1F212, 'M', u'双'), + (0x1F213, 'M', u'デ'), + (0x1F214, 'M', u'二'), + (0x1F215, 'M', u'多'), + (0x1F216, 'M', u'解'), + (0x1F217, 'M', u'天'), + (0x1F218, 'M', u'交'), + (0x1F219, 'M', u'映'), + (0x1F21A, 'M', u'無'), + (0x1F21B, 'M', u'料'), + (0x1F21C, 'M', u'前'), + (0x1F21D, 'M', u'後'), + (0x1F21E, 'M', u'再'), + (0x1F21F, 'M', u'新'), + (0x1F220, 'M', u'初'), + (0x1F221, 'M', u'終'), + (0x1F222, 'M', u'生'), + (0x1F223, 'M', u'販'), + (0x1F224, 'M', u'声'), + (0x1F225, 'M', u'吹'), + (0x1F226, 'M', u'演'), + (0x1F227, 'M', u'投'), + (0x1F228, 'M', u'捕'), + (0x1F229, 'M', u'一'), + (0x1F22A, 'M', u'三'), + (0x1F22B, 'M', u'遊'), + (0x1F22C, 'M', u'左'), + (0x1F22D, 'M', u'中'), + (0x1F22E, 'M', u'右'), + (0x1F22F, 'M', u'指'), + (0x1F230, 'M', u'走'), + (0x1F231, 'M', u'打'), + (0x1F232, 'M', u'禁'), + (0x1F233, 'M', u'空'), + (0x1F234, 'M', u'合'), + (0x1F235, 'M', u'満'), + (0x1F236, 'M', u'有'), + (0x1F237, 'M', u'月'), + (0x1F238, 'M', u'申'), + (0x1F239, 'M', u'割'), + (0x1F23A, 'M', u'営'), + (0x1F23B, 'M', u'配'), + (0x1F23C, 'X'), + (0x1F240, 'M', u'〔本〕'), + (0x1F241, 'M', u'〔三〕'), + (0x1F242, 'M', u'〔二〕'), + (0x1F243, 'M', u'〔安〕'), + (0x1F244, 'M', u'〔点〕'), + (0x1F245, 'M', u'〔打〕'), + (0x1F246, 'M', u'〔盗〕'), + (0x1F247, 'M', u'〔勝〕'), + (0x1F248, 'M', u'〔敗〕'), + (0x1F249, 'X'), + (0x1F250, 'M', u'得'), + (0x1F251, 'M', u'可'), + (0x1F252, 'X'), + (0x1F260, 'V'), + ] + +def _seg_72(): + return [ + (0x1F266, 'X'), + (0x1F300, 'V'), + (0x1F6D5, 'X'), + (0x1F6E0, 'V'), + (0x1F6ED, 'X'), + (0x1F6F0, 'V'), + (0x1F6F9, 'X'), + (0x1F700, 'V'), + (0x1F774, 'X'), + (0x1F780, 'V'), + (0x1F7D5, 'X'), + (0x1F800, 'V'), + (0x1F80C, 'X'), + (0x1F810, 'V'), + (0x1F848, 'X'), + (0x1F850, 'V'), + (0x1F85A, 'X'), + (0x1F860, 'V'), + (0x1F888, 'X'), + (0x1F890, 'V'), + (0x1F8AE, 'X'), + (0x1F900, 'V'), + (0x1F90C, 'X'), + (0x1F910, 'V'), + (0x1F93F, 'X'), + (0x1F940, 'V'), + (0x1F94D, 'X'), + (0x1F950, 'V'), + (0x1F96C, 'X'), + (0x1F980, 'V'), + (0x1F998, 'X'), + (0x1F9C0, 'V'), + (0x1F9C1, 'X'), + (0x1F9D0, 'V'), + (0x1F9E7, 'X'), + (0x20000, 'V'), + (0x2A6D7, 'X'), + (0x2A700, 'V'), + (0x2B735, 'X'), + (0x2B740, 'V'), + (0x2B81E, 'X'), + (0x2B820, 'V'), + (0x2CEA2, 'X'), + (0x2CEB0, 'V'), + (0x2EBE1, 'X'), + (0x2F800, 'M', u'丽'), + (0x2F801, 'M', u'丸'), + (0x2F802, 'M', u'乁'), + (0x2F803, 'M', u'𠄢'), + (0x2F804, 'M', u'你'), + (0x2F805, 'M', u'侮'), + (0x2F806, 'M', u'侻'), + (0x2F807, 'M', u'倂'), + (0x2F808, 'M', u'偺'), + (0x2F809, 'M', u'備'), + (0x2F80A, 'M', u'僧'), + (0x2F80B, 'M', u'像'), + (0x2F80C, 'M', u'㒞'), + (0x2F80D, 'M', u'𠘺'), + (0x2F80E, 'M', u'免'), + (0x2F80F, 'M', u'兔'), + (0x2F810, 'M', u'兤'), + (0x2F811, 'M', u'具'), + (0x2F812, 'M', u'𠔜'), + (0x2F813, 'M', u'㒹'), + (0x2F814, 'M', u'內'), + (0x2F815, 'M', u'再'), + (0x2F816, 'M', u'𠕋'), + (0x2F817, 'M', u'冗'), + (0x2F818, 'M', u'冤'), + (0x2F819, 'M', u'仌'), + (0x2F81A, 'M', u'冬'), + (0x2F81B, 'M', u'况'), + (0x2F81C, 'M', u'𩇟'), + (0x2F81D, 'M', u'凵'), + (0x2F81E, 'M', u'刃'), + (0x2F81F, 'M', u'㓟'), + (0x2F820, 'M', u'刻'), + (0x2F821, 'M', u'剆'), + (0x2F822, 'M', u'割'), + (0x2F823, 'M', u'剷'), + (0x2F824, 'M', u'㔕'), + (0x2F825, 'M', u'勇'), + (0x2F826, 'M', u'勉'), + (0x2F827, 'M', u'勤'), + (0x2F828, 'M', u'勺'), + (0x2F829, 'M', u'包'), + (0x2F82A, 'M', u'匆'), + (0x2F82B, 'M', u'北'), + (0x2F82C, 'M', u'卉'), + (0x2F82D, 'M', u'卑'), + (0x2F82E, 'M', u'博'), + (0x2F82F, 'M', u'即'), + (0x2F830, 'M', u'卽'), + (0x2F831, 'M', u'卿'), + (0x2F834, 'M', u'𠨬'), + (0x2F835, 'M', u'灰'), + (0x2F836, 'M', u'及'), + (0x2F837, 'M', u'叟'), + (0x2F838, 'M', u'𠭣'), + ] + +def _seg_73(): + return [ + (0x2F839, 'M', u'叫'), + (0x2F83A, 'M', u'叱'), + (0x2F83B, 'M', u'吆'), + (0x2F83C, 'M', u'咞'), + (0x2F83D, 'M', u'吸'), + (0x2F83E, 'M', u'呈'), + (0x2F83F, 'M', u'周'), + (0x2F840, 'M', u'咢'), + (0x2F841, 'M', u'哶'), + (0x2F842, 'M', u'唐'), + (0x2F843, 'M', u'啓'), + (0x2F844, 'M', u'啣'), + (0x2F845, 'M', u'善'), + (0x2F847, 'M', u'喙'), + (0x2F848, 'M', u'喫'), + (0x2F849, 'M', u'喳'), + (0x2F84A, 'M', u'嗂'), + (0x2F84B, 'M', u'圖'), + (0x2F84C, 'M', u'嘆'), + (0x2F84D, 'M', u'圗'), + (0x2F84E, 'M', u'噑'), + (0x2F84F, 'M', u'噴'), + (0x2F850, 'M', u'切'), + (0x2F851, 'M', u'壮'), + (0x2F852, 'M', u'城'), + (0x2F853, 'M', u'埴'), + (0x2F854, 'M', u'堍'), + (0x2F855, 'M', u'型'), + (0x2F856, 'M', u'堲'), + (0x2F857, 'M', u'報'), + (0x2F858, 'M', u'墬'), + (0x2F859, 'M', u'𡓤'), + (0x2F85A, 'M', u'売'), + (0x2F85B, 'M', u'壷'), + (0x2F85C, 'M', u'夆'), + (0x2F85D, 'M', u'多'), + (0x2F85E, 'M', u'夢'), + (0x2F85F, 'M', u'奢'), + (0x2F860, 'M', u'𡚨'), + (0x2F861, 'M', u'𡛪'), + (0x2F862, 'M', u'姬'), + (0x2F863, 'M', u'娛'), + (0x2F864, 'M', u'娧'), + (0x2F865, 'M', u'姘'), + (0x2F866, 'M', u'婦'), + (0x2F867, 'M', u'㛮'), + (0x2F868, 'X'), + (0x2F869, 'M', u'嬈'), + (0x2F86A, 'M', u'嬾'), + (0x2F86C, 'M', u'𡧈'), + (0x2F86D, 'M', u'寃'), + (0x2F86E, 'M', u'寘'), + (0x2F86F, 'M', u'寧'), + (0x2F870, 'M', u'寳'), + (0x2F871, 'M', u'𡬘'), + (0x2F872, 'M', u'寿'), + (0x2F873, 'M', u'将'), + (0x2F874, 'X'), + (0x2F875, 'M', u'尢'), + (0x2F876, 'M', u'㞁'), + (0x2F877, 'M', u'屠'), + (0x2F878, 'M', u'屮'), + (0x2F879, 'M', u'峀'), + (0x2F87A, 'M', u'岍'), + (0x2F87B, 'M', u'𡷤'), + (0x2F87C, 'M', u'嵃'), + (0x2F87D, 'M', u'𡷦'), + (0x2F87E, 'M', u'嵮'), + (0x2F87F, 'M', u'嵫'), + (0x2F880, 'M', u'嵼'), + (0x2F881, 'M', u'巡'), + (0x2F882, 'M', u'巢'), + (0x2F883, 'M', u'㠯'), + (0x2F884, 'M', u'巽'), + (0x2F885, 'M', u'帨'), + (0x2F886, 'M', u'帽'), + (0x2F887, 'M', u'幩'), + (0x2F888, 'M', u'㡢'), + (0x2F889, 'M', u'𢆃'), + (0x2F88A, 'M', u'㡼'), + (0x2F88B, 'M', u'庰'), + (0x2F88C, 'M', u'庳'), + (0x2F88D, 'M', u'庶'), + (0x2F88E, 'M', u'廊'), + (0x2F88F, 'M', u'𪎒'), + (0x2F890, 'M', u'廾'), + (0x2F891, 'M', u'𢌱'), + (0x2F893, 'M', u'舁'), + (0x2F894, 'M', u'弢'), + (0x2F896, 'M', u'㣇'), + (0x2F897, 'M', u'𣊸'), + (0x2F898, 'M', u'𦇚'), + (0x2F899, 'M', u'形'), + (0x2F89A, 'M', u'彫'), + (0x2F89B, 'M', u'㣣'), + (0x2F89C, 'M', u'徚'), + (0x2F89D, 'M', u'忍'), + (0x2F89E, 'M', u'志'), + (0x2F89F, 'M', u'忹'), + (0x2F8A0, 'M', u'悁'), + ] + +def _seg_74(): + return [ + (0x2F8A1, 'M', u'㤺'), + (0x2F8A2, 'M', u'㤜'), + (0x2F8A3, 'M', u'悔'), + (0x2F8A4, 'M', u'𢛔'), + (0x2F8A5, 'M', u'惇'), + (0x2F8A6, 'M', u'慈'), + (0x2F8A7, 'M', u'慌'), + (0x2F8A8, 'M', u'慎'), + (0x2F8A9, 'M', u'慌'), + (0x2F8AA, 'M', u'慺'), + (0x2F8AB, 'M', u'憎'), + (0x2F8AC, 'M', u'憲'), + (0x2F8AD, 'M', u'憤'), + (0x2F8AE, 'M', u'憯'), + (0x2F8AF, 'M', u'懞'), + (0x2F8B0, 'M', u'懲'), + (0x2F8B1, 'M', u'懶'), + (0x2F8B2, 'M', u'成'), + (0x2F8B3, 'M', u'戛'), + (0x2F8B4, 'M', u'扝'), + (0x2F8B5, 'M', u'抱'), + (0x2F8B6, 'M', u'拔'), + (0x2F8B7, 'M', u'捐'), + (0x2F8B8, 'M', u'𢬌'), + (0x2F8B9, 'M', u'挽'), + (0x2F8BA, 'M', u'拼'), + (0x2F8BB, 'M', u'捨'), + (0x2F8BC, 'M', u'掃'), + (0x2F8BD, 'M', u'揤'), + (0x2F8BE, 'M', u'𢯱'), + (0x2F8BF, 'M', u'搢'), + (0x2F8C0, 'M', u'揅'), + (0x2F8C1, 'M', u'掩'), + (0x2F8C2, 'M', u'㨮'), + (0x2F8C3, 'M', u'摩'), + (0x2F8C4, 'M', u'摾'), + (0x2F8C5, 'M', u'撝'), + (0x2F8C6, 'M', u'摷'), + (0x2F8C7, 'M', u'㩬'), + (0x2F8C8, 'M', u'敏'), + (0x2F8C9, 'M', u'敬'), + (0x2F8CA, 'M', u'𣀊'), + (0x2F8CB, 'M', u'旣'), + (0x2F8CC, 'M', u'書'), + (0x2F8CD, 'M', u'晉'), + (0x2F8CE, 'M', u'㬙'), + (0x2F8CF, 'M', u'暑'), + (0x2F8D0, 'M', u'㬈'), + (0x2F8D1, 'M', u'㫤'), + (0x2F8D2, 'M', u'冒'), + (0x2F8D3, 'M', u'冕'), + (0x2F8D4, 'M', u'最'), + (0x2F8D5, 'M', u'暜'), + (0x2F8D6, 'M', u'肭'), + (0x2F8D7, 'M', u'䏙'), + (0x2F8D8, 'M', u'朗'), + (0x2F8D9, 'M', u'望'), + (0x2F8DA, 'M', u'朡'), + (0x2F8DB, 'M', u'杞'), + (0x2F8DC, 'M', u'杓'), + (0x2F8DD, 'M', u'𣏃'), + (0x2F8DE, 'M', u'㭉'), + (0x2F8DF, 'M', u'柺'), + (0x2F8E0, 'M', u'枅'), + (0x2F8E1, 'M', u'桒'), + (0x2F8E2, 'M', u'梅'), + (0x2F8E3, 'M', u'𣑭'), + (0x2F8E4, 'M', u'梎'), + (0x2F8E5, 'M', u'栟'), + (0x2F8E6, 'M', u'椔'), + (0x2F8E7, 'M', u'㮝'), + (0x2F8E8, 'M', u'楂'), + (0x2F8E9, 'M', u'榣'), + (0x2F8EA, 'M', u'槪'), + (0x2F8EB, 'M', u'檨'), + (0x2F8EC, 'M', u'𣚣'), + (0x2F8ED, 'M', u'櫛'), + (0x2F8EE, 'M', u'㰘'), + (0x2F8EF, 'M', u'次'), + (0x2F8F0, 'M', u'𣢧'), + (0x2F8F1, 'M', u'歔'), + (0x2F8F2, 'M', u'㱎'), + (0x2F8F3, 'M', u'歲'), + (0x2F8F4, 'M', u'殟'), + (0x2F8F5, 'M', u'殺'), + (0x2F8F6, 'M', u'殻'), + (0x2F8F7, 'M', u'𣪍'), + (0x2F8F8, 'M', u'𡴋'), + (0x2F8F9, 'M', u'𣫺'), + (0x2F8FA, 'M', u'汎'), + (0x2F8FB, 'M', u'𣲼'), + (0x2F8FC, 'M', u'沿'), + (0x2F8FD, 'M', u'泍'), + (0x2F8FE, 'M', u'汧'), + (0x2F8FF, 'M', u'洖'), + (0x2F900, 'M', u'派'), + (0x2F901, 'M', u'海'), + (0x2F902, 'M', u'流'), + (0x2F903, 'M', u'浩'), + (0x2F904, 'M', u'浸'), + ] + +def _seg_75(): + return [ + (0x2F905, 'M', u'涅'), + (0x2F906, 'M', u'𣴞'), + (0x2F907, 'M', u'洴'), + (0x2F908, 'M', u'港'), + (0x2F909, 'M', u'湮'), + (0x2F90A, 'M', u'㴳'), + (0x2F90B, 'M', u'滋'), + (0x2F90C, 'M', u'滇'), + (0x2F90D, 'M', u'𣻑'), + (0x2F90E, 'M', u'淹'), + (0x2F90F, 'M', u'潮'), + (0x2F910, 'M', u'𣽞'), + (0x2F911, 'M', u'𣾎'), + (0x2F912, 'M', u'濆'), + (0x2F913, 'M', u'瀹'), + (0x2F914, 'M', u'瀞'), + (0x2F915, 'M', u'瀛'), + (0x2F916, 'M', u'㶖'), + (0x2F917, 'M', u'灊'), + (0x2F918, 'M', u'災'), + (0x2F919, 'M', u'灷'), + (0x2F91A, 'M', u'炭'), + (0x2F91B, 'M', u'𠔥'), + (0x2F91C, 'M', u'煅'), + (0x2F91D, 'M', u'𤉣'), + (0x2F91E, 'M', u'熜'), + (0x2F91F, 'X'), + (0x2F920, 'M', u'爨'), + (0x2F921, 'M', u'爵'), + (0x2F922, 'M', u'牐'), + (0x2F923, 'M', u'𤘈'), + (0x2F924, 'M', u'犀'), + (0x2F925, 'M', u'犕'), + (0x2F926, 'M', u'𤜵'), + (0x2F927, 'M', u'𤠔'), + (0x2F928, 'M', u'獺'), + (0x2F929, 'M', u'王'), + (0x2F92A, 'M', u'㺬'), + (0x2F92B, 'M', u'玥'), + (0x2F92C, 'M', u'㺸'), + (0x2F92E, 'M', u'瑇'), + (0x2F92F, 'M', u'瑜'), + (0x2F930, 'M', u'瑱'), + (0x2F931, 'M', u'璅'), + (0x2F932, 'M', u'瓊'), + (0x2F933, 'M', u'㼛'), + (0x2F934, 'M', u'甤'), + (0x2F935, 'M', u'𤰶'), + (0x2F936, 'M', u'甾'), + (0x2F937, 'M', u'𤲒'), + (0x2F938, 'M', u'異'), + (0x2F939, 'M', u'𢆟'), + (0x2F93A, 'M', u'瘐'), + (0x2F93B, 'M', u'𤾡'), + (0x2F93C, 'M', u'𤾸'), + (0x2F93D, 'M', u'𥁄'), + (0x2F93E, 'M', u'㿼'), + (0x2F93F, 'M', u'䀈'), + (0x2F940, 'M', u'直'), + (0x2F941, 'M', u'𥃳'), + (0x2F942, 'M', u'𥃲'), + (0x2F943, 'M', u'𥄙'), + (0x2F944, 'M', u'𥄳'), + (0x2F945, 'M', u'眞'), + (0x2F946, 'M', u'真'), + (0x2F948, 'M', u'睊'), + (0x2F949, 'M', u'䀹'), + (0x2F94A, 'M', u'瞋'), + (0x2F94B, 'M', u'䁆'), + (0x2F94C, 'M', u'䂖'), + (0x2F94D, 'M', u'𥐝'), + (0x2F94E, 'M', u'硎'), + (0x2F94F, 'M', u'碌'), + (0x2F950, 'M', u'磌'), + (0x2F951, 'M', u'䃣'), + (0x2F952, 'M', u'𥘦'), + (0x2F953, 'M', u'祖'), + (0x2F954, 'M', u'𥚚'), + (0x2F955, 'M', u'𥛅'), + (0x2F956, 'M', u'福'), + (0x2F957, 'M', u'秫'), + (0x2F958, 'M', u'䄯'), + (0x2F959, 'M', u'穀'), + (0x2F95A, 'M', u'穊'), + (0x2F95B, 'M', u'穏'), + (0x2F95C, 'M', u'𥥼'), + (0x2F95D, 'M', u'𥪧'), + (0x2F95F, 'X'), + (0x2F960, 'M', u'䈂'), + (0x2F961, 'M', u'𥮫'), + (0x2F962, 'M', u'篆'), + (0x2F963, 'M', u'築'), + (0x2F964, 'M', u'䈧'), + (0x2F965, 'M', u'𥲀'), + (0x2F966, 'M', u'糒'), + (0x2F967, 'M', u'䊠'), + (0x2F968, 'M', u'糨'), + (0x2F969, 'M', u'糣'), + (0x2F96A, 'M', u'紀'), + (0x2F96B, 'M', u'𥾆'), + ] + +def _seg_76(): + return [ + (0x2F96C, 'M', u'絣'), + (0x2F96D, 'M', u'䌁'), + (0x2F96E, 'M', u'緇'), + (0x2F96F, 'M', u'縂'), + (0x2F970, 'M', u'繅'), + (0x2F971, 'M', u'䌴'), + (0x2F972, 'M', u'𦈨'), + (0x2F973, 'M', u'𦉇'), + (0x2F974, 'M', u'䍙'), + (0x2F975, 'M', u'𦋙'), + (0x2F976, 'M', u'罺'), + (0x2F977, 'M', u'𦌾'), + (0x2F978, 'M', u'羕'), + (0x2F979, 'M', u'翺'), + (0x2F97A, 'M', u'者'), + (0x2F97B, 'M', u'𦓚'), + (0x2F97C, 'M', u'𦔣'), + (0x2F97D, 'M', u'聠'), + (0x2F97E, 'M', u'𦖨'), + (0x2F97F, 'M', u'聰'), + (0x2F980, 'M', u'𣍟'), + (0x2F981, 'M', u'䏕'), + (0x2F982, 'M', u'育'), + (0x2F983, 'M', u'脃'), + (0x2F984, 'M', u'䐋'), + (0x2F985, 'M', u'脾'), + (0x2F986, 'M', u'媵'), + (0x2F987, 'M', u'𦞧'), + (0x2F988, 'M', u'𦞵'), + (0x2F989, 'M', u'𣎓'), + (0x2F98A, 'M', u'𣎜'), + (0x2F98B, 'M', u'舁'), + (0x2F98C, 'M', u'舄'), + (0x2F98D, 'M', u'辞'), + (0x2F98E, 'M', u'䑫'), + (0x2F98F, 'M', u'芑'), + (0x2F990, 'M', u'芋'), + (0x2F991, 'M', u'芝'), + (0x2F992, 'M', u'劳'), + (0x2F993, 'M', u'花'), + (0x2F994, 'M', u'芳'), + (0x2F995, 'M', u'芽'), + (0x2F996, 'M', u'苦'), + (0x2F997, 'M', u'𦬼'), + (0x2F998, 'M', u'若'), + (0x2F999, 'M', u'茝'), + (0x2F99A, 'M', u'荣'), + (0x2F99B, 'M', u'莭'), + (0x2F99C, 'M', u'茣'), + (0x2F99D, 'M', u'莽'), + (0x2F99E, 'M', u'菧'), + (0x2F99F, 'M', u'著'), + (0x2F9A0, 'M', u'荓'), + (0x2F9A1, 'M', u'菊'), + (0x2F9A2, 'M', u'菌'), + (0x2F9A3, 'M', u'菜'), + (0x2F9A4, 'M', u'𦰶'), + (0x2F9A5, 'M', u'𦵫'), + (0x2F9A6, 'M', u'𦳕'), + (0x2F9A7, 'M', u'䔫'), + (0x2F9A8, 'M', u'蓱'), + (0x2F9A9, 'M', u'蓳'), + (0x2F9AA, 'M', u'蔖'), + (0x2F9AB, 'M', u'𧏊'), + (0x2F9AC, 'M', u'蕤'), + (0x2F9AD, 'M', u'𦼬'), + (0x2F9AE, 'M', u'䕝'), + (0x2F9AF, 'M', u'䕡'), + (0x2F9B0, 'M', u'𦾱'), + (0x2F9B1, 'M', u'𧃒'), + (0x2F9B2, 'M', u'䕫'), + (0x2F9B3, 'M', u'虐'), + (0x2F9B4, 'M', u'虜'), + (0x2F9B5, 'M', u'虧'), + (0x2F9B6, 'M', u'虩'), + (0x2F9B7, 'M', u'蚩'), + (0x2F9B8, 'M', u'蚈'), + (0x2F9B9, 'M', u'蜎'), + (0x2F9BA, 'M', u'蛢'), + (0x2F9BB, 'M', u'蝹'), + (0x2F9BC, 'M', u'蜨'), + (0x2F9BD, 'M', u'蝫'), + (0x2F9BE, 'M', u'螆'), + (0x2F9BF, 'X'), + (0x2F9C0, 'M', u'蟡'), + (0x2F9C1, 'M', u'蠁'), + (0x2F9C2, 'M', u'䗹'), + (0x2F9C3, 'M', u'衠'), + (0x2F9C4, 'M', u'衣'), + (0x2F9C5, 'M', u'𧙧'), + (0x2F9C6, 'M', u'裗'), + (0x2F9C7, 'M', u'裞'), + (0x2F9C8, 'M', u'䘵'), + (0x2F9C9, 'M', u'裺'), + (0x2F9CA, 'M', u'㒻'), + (0x2F9CB, 'M', u'𧢮'), + (0x2F9CC, 'M', u'𧥦'), + (0x2F9CD, 'M', u'䚾'), + (0x2F9CE, 'M', u'䛇'), + (0x2F9CF, 'M', u'誠'), + ] + +def _seg_77(): + return [ + (0x2F9D0, 'M', u'諭'), + (0x2F9D1, 'M', u'變'), + (0x2F9D2, 'M', u'豕'), + (0x2F9D3, 'M', u'𧲨'), + (0x2F9D4, 'M', u'貫'), + (0x2F9D5, 'M', u'賁'), + (0x2F9D6, 'M', u'贛'), + (0x2F9D7, 'M', u'起'), + (0x2F9D8, 'M', u'𧼯'), + (0x2F9D9, 'M', u'𠠄'), + (0x2F9DA, 'M', u'跋'), + (0x2F9DB, 'M', u'趼'), + (0x2F9DC, 'M', u'跰'), + (0x2F9DD, 'M', u'𠣞'), + (0x2F9DE, 'M', u'軔'), + (0x2F9DF, 'M', u'輸'), + (0x2F9E0, 'M', u'𨗒'), + (0x2F9E1, 'M', u'𨗭'), + (0x2F9E2, 'M', u'邔'), + (0x2F9E3, 'M', u'郱'), + (0x2F9E4, 'M', u'鄑'), + (0x2F9E5, 'M', u'𨜮'), + (0x2F9E6, 'M', u'鄛'), + (0x2F9E7, 'M', u'鈸'), + (0x2F9E8, 'M', u'鋗'), + (0x2F9E9, 'M', u'鋘'), + (0x2F9EA, 'M', u'鉼'), + (0x2F9EB, 'M', u'鏹'), + (0x2F9EC, 'M', u'鐕'), + (0x2F9ED, 'M', u'𨯺'), + (0x2F9EE, 'M', u'開'), + (0x2F9EF, 'M', u'䦕'), + (0x2F9F0, 'M', u'閷'), + (0x2F9F1, 'M', u'𨵷'), + (0x2F9F2, 'M', u'䧦'), + (0x2F9F3, 'M', u'雃'), + (0x2F9F4, 'M', u'嶲'), + (0x2F9F5, 'M', u'霣'), + (0x2F9F6, 'M', u'𩅅'), + (0x2F9F7, 'M', u'𩈚'), + (0x2F9F8, 'M', u'䩮'), + (0x2F9F9, 'M', u'䩶'), + (0x2F9FA, 'M', u'韠'), + (0x2F9FB, 'M', u'𩐊'), + (0x2F9FC, 'M', u'䪲'), + (0x2F9FD, 'M', u'𩒖'), + (0x2F9FE, 'M', u'頋'), + (0x2FA00, 'M', u'頩'), + (0x2FA01, 'M', u'𩖶'), + (0x2FA02, 'M', u'飢'), + (0x2FA03, 'M', u'䬳'), + (0x2FA04, 'M', u'餩'), + (0x2FA05, 'M', u'馧'), + (0x2FA06, 'M', u'駂'), + (0x2FA07, 'M', u'駾'), + (0x2FA08, 'M', u'䯎'), + (0x2FA09, 'M', u'𩬰'), + (0x2FA0A, 'M', u'鬒'), + (0x2FA0B, 'M', u'鱀'), + (0x2FA0C, 'M', u'鳽'), + (0x2FA0D, 'M', u'䳎'), + (0x2FA0E, 'M', u'䳭'), + (0x2FA0F, 'M', u'鵧'), + (0x2FA10, 'M', u'𪃎'), + (0x2FA11, 'M', u'䳸'), + (0x2FA12, 'M', u'𪄅'), + (0x2FA13, 'M', u'𪈎'), + (0x2FA14, 'M', u'𪊑'), + (0x2FA15, 'M', u'麻'), + (0x2FA16, 'M', u'䵖'), + (0x2FA17, 'M', u'黹'), + (0x2FA18, 'M', u'黾'), + (0x2FA19, 'M', u'鼅'), + (0x2FA1A, 'M', u'鼏'), + (0x2FA1B, 'M', u'鼖'), + (0x2FA1C, 'M', u'鼻'), + (0x2FA1D, 'M', u'𪘀'), + (0x2FA1E, 'X'), + (0xE0100, 'I'), + (0xE01F0, 'X'), + ] + +uts46data = tuple( + _seg_0() + + _seg_1() + + _seg_2() + + _seg_3() + + _seg_4() + + _seg_5() + + _seg_6() + + _seg_7() + + _seg_8() + + _seg_9() + + _seg_10() + + _seg_11() + + _seg_12() + + _seg_13() + + _seg_14() + + _seg_15() + + _seg_16() + + _seg_17() + + _seg_18() + + _seg_19() + + _seg_20() + + _seg_21() + + _seg_22() + + _seg_23() + + _seg_24() + + _seg_25() + + _seg_26() + + _seg_27() + + _seg_28() + + _seg_29() + + _seg_30() + + _seg_31() + + _seg_32() + + _seg_33() + + _seg_34() + + _seg_35() + + _seg_36() + + _seg_37() + + _seg_38() + + _seg_39() + + _seg_40() + + _seg_41() + + _seg_42() + + _seg_43() + + _seg_44() + + _seg_45() + + _seg_46() + + _seg_47() + + _seg_48() + + _seg_49() + + _seg_50() + + _seg_51() + + _seg_52() + + _seg_53() + + _seg_54() + + _seg_55() + + _seg_56() + + _seg_57() + + _seg_58() + + _seg_59() + + _seg_60() + + _seg_61() + + _seg_62() + + _seg_63() + + _seg_64() + + _seg_65() + + _seg_66() + + _seg_67() + + _seg_68() + + _seg_69() + + _seg_70() + + _seg_71() + + _seg_72() + + _seg_73() + + _seg_74() + + _seg_75() + + _seg_76() + + _seg_77() +) diff --git a/venv/Lib/site-packages/pip/_vendor/ipaddress.py b/venv/Lib/site-packages/pip/_vendor/ipaddress.py new file mode 100644 index 0000000..f2d0766 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/ipaddress.py @@ -0,0 +1,2419 @@ +# Copyright 2007 Google Inc. +# Licensed to PSF under a Contributor Agreement. + +"""A fast, lightweight IPv4/IPv6 manipulation library in Python. + +This library is used to create/poke/manipulate IPv4 and IPv6 addresses +and networks. + +""" + +from __future__ import unicode_literals + + +import itertools +import struct + +__version__ = '1.0.22' + +# Compatibility functions +_compat_int_types = (int,) +try: + _compat_int_types = (int, long) +except NameError: + pass +try: + _compat_str = unicode +except NameError: + _compat_str = str + assert bytes != str +if b'\0'[0] == 0: # Python 3 semantics + def _compat_bytes_to_byte_vals(byt): + return byt +else: + def _compat_bytes_to_byte_vals(byt): + return [struct.unpack(b'!B', b)[0] for b in byt] +try: + _compat_int_from_byte_vals = int.from_bytes +except AttributeError: + def _compat_int_from_byte_vals(bytvals, endianess): + assert endianess == 'big' + res = 0 + for bv in bytvals: + assert isinstance(bv, _compat_int_types) + res = (res << 8) + bv + return res + + +def _compat_to_bytes(intval, length, endianess): + assert isinstance(intval, _compat_int_types) + assert endianess == 'big' + if length == 4: + if intval < 0 or intval >= 2 ** 32: + raise struct.error("integer out of range for 'I' format code") + return struct.pack(b'!I', intval) + elif length == 16: + if intval < 0 or intval >= 2 ** 128: + raise struct.error("integer out of range for 'QQ' format code") + return struct.pack(b'!QQ', intval >> 64, intval & 0xffffffffffffffff) + else: + raise NotImplementedError() + + +if hasattr(int, 'bit_length'): + # Not int.bit_length , since that won't work in 2.7 where long exists + def _compat_bit_length(i): + return i.bit_length() +else: + def _compat_bit_length(i): + for res in itertools.count(): + if i >> res == 0: + return res + + +def _compat_range(start, end, step=1): + assert step > 0 + i = start + while i < end: + yield i + i += step + + +class _TotalOrderingMixin(object): + __slots__ = () + + # Helper that derives the other comparison operations from + # __lt__ and __eq__ + # We avoid functools.total_ordering because it doesn't handle + # NotImplemented correctly yet (http://bugs.python.org/issue10042) + def __eq__(self, other): + raise NotImplementedError + + def __ne__(self, other): + equal = self.__eq__(other) + if equal is NotImplemented: + return NotImplemented + return not equal + + def __lt__(self, other): + raise NotImplementedError + + def __le__(self, other): + less = self.__lt__(other) + if less is NotImplemented or not less: + return self.__eq__(other) + return less + + def __gt__(self, other): + less = self.__lt__(other) + if less is NotImplemented: + return NotImplemented + equal = self.__eq__(other) + if equal is NotImplemented: + return NotImplemented + return not (less or equal) + + def __ge__(self, other): + less = self.__lt__(other) + if less is NotImplemented: + return NotImplemented + return not less + + +IPV4LENGTH = 32 +IPV6LENGTH = 128 + + +class AddressValueError(ValueError): + """A Value Error related to the address.""" + + +class NetmaskValueError(ValueError): + """A Value Error related to the netmask.""" + + +def ip_address(address): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP address. Either IPv4 or + IPv6 addresses may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + + Returns: + An IPv4Address or IPv6Address object. + + Raises: + ValueError: if the *address* passed isn't either a v4 or a v6 + address + + """ + try: + return IPv4Address(address) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Address(address) + except (AddressValueError, NetmaskValueError): + pass + + if isinstance(address, bytes): + raise AddressValueError( + '%r does not appear to be an IPv4 or IPv6 address. ' + 'Did you pass in a bytes (str in Python 2) instead of' + ' a unicode object?' % address) + + raise ValueError('%r does not appear to be an IPv4 or IPv6 address' % + address) + + +def ip_network(address, strict=True): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP network. Either IPv4 or + IPv6 networks may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + + Returns: + An IPv4Network or IPv6Network object. + + Raises: + ValueError: if the string passed isn't either a v4 or a v6 + address. Or if the network has host bits set. + + """ + try: + return IPv4Network(address, strict) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Network(address, strict) + except (AddressValueError, NetmaskValueError): + pass + + if isinstance(address, bytes): + raise AddressValueError( + '%r does not appear to be an IPv4 or IPv6 network. ' + 'Did you pass in a bytes (str in Python 2) instead of' + ' a unicode object?' % address) + + raise ValueError('%r does not appear to be an IPv4 or IPv6 network' % + address) + + +def ip_interface(address): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP address. Either IPv4 or + IPv6 addresses may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + + Returns: + An IPv4Interface or IPv6Interface object. + + Raises: + ValueError: if the string passed isn't either a v4 or a v6 + address. + + Notes: + The IPv?Interface classes describe an Address on a particular + Network, so they're basically a combination of both the Address + and Network classes. + + """ + try: + return IPv4Interface(address) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Interface(address) + except (AddressValueError, NetmaskValueError): + pass + + raise ValueError('%r does not appear to be an IPv4 or IPv6 interface' % + address) + + +def v4_int_to_packed(address): + """Represent an address as 4 packed bytes in network (big-endian) order. + + Args: + address: An integer representation of an IPv4 IP address. + + Returns: + The integer address packed as 4 bytes in network (big-endian) order. + + Raises: + ValueError: If the integer is negative or too large to be an + IPv4 IP address. + + """ + try: + return _compat_to_bytes(address, 4, 'big') + except (struct.error, OverflowError): + raise ValueError("Address negative or too large for IPv4") + + +def v6_int_to_packed(address): + """Represent an address as 16 packed bytes in network (big-endian) order. + + Args: + address: An integer representation of an IPv6 IP address. + + Returns: + The integer address packed as 16 bytes in network (big-endian) order. + + """ + try: + return _compat_to_bytes(address, 16, 'big') + except (struct.error, OverflowError): + raise ValueError("Address negative or too large for IPv6") + + +def _split_optional_netmask(address): + """Helper to split the netmask and raise AddressValueError if needed""" + addr = _compat_str(address).split('/') + if len(addr) > 2: + raise AddressValueError("Only one '/' permitted in %r" % address) + return addr + + +def _find_address_range(addresses): + """Find a sequence of sorted deduplicated IPv#Address. + + Args: + addresses: a list of IPv#Address objects. + + Yields: + A tuple containing the first and last IP addresses in the sequence. + + """ + it = iter(addresses) + first = last = next(it) + for ip in it: + if ip._ip != last._ip + 1: + yield first, last + first = ip + last = ip + yield first, last + + +def _count_righthand_zero_bits(number, bits): + """Count the number of zero bits on the right hand side. + + Args: + number: an integer. + bits: maximum number of bits to count. + + Returns: + The number of zero bits on the right hand side of the number. + + """ + if number == 0: + return bits + return min(bits, _compat_bit_length(~number & (number - 1))) + + +def summarize_address_range(first, last): + """Summarize a network range given the first and last IP addresses. + + Example: + >>> list(summarize_address_range(IPv4Address('192.0.2.0'), + ... IPv4Address('192.0.2.130'))) + ... #doctest: +NORMALIZE_WHITESPACE + [IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/31'), + IPv4Network('192.0.2.130/32')] + + Args: + first: the first IPv4Address or IPv6Address in the range. + last: the last IPv4Address or IPv6Address in the range. + + Returns: + An iterator of the summarized IPv(4|6) network objects. + + Raise: + TypeError: + If the first and last objects are not IP addresses. + If the first and last objects are not the same version. + ValueError: + If the last object is not greater than the first. + If the version of the first address is not 4 or 6. + + """ + if (not (isinstance(first, _BaseAddress) and + isinstance(last, _BaseAddress))): + raise TypeError('first and last must be IP addresses, not networks') + if first.version != last.version: + raise TypeError("%s and %s are not of the same version" % ( + first, last)) + if first > last: + raise ValueError('last IP address must be greater than first') + + if first.version == 4: + ip = IPv4Network + elif first.version == 6: + ip = IPv6Network + else: + raise ValueError('unknown IP version') + + ip_bits = first._max_prefixlen + first_int = first._ip + last_int = last._ip + while first_int <= last_int: + nbits = min(_count_righthand_zero_bits(first_int, ip_bits), + _compat_bit_length(last_int - first_int + 1) - 1) + net = ip((first_int, ip_bits - nbits)) + yield net + first_int += 1 << nbits + if first_int - 1 == ip._ALL_ONES: + break + + +def _collapse_addresses_internal(addresses): + """Loops through the addresses, collapsing concurrent netblocks. + + Example: + + ip1 = IPv4Network('192.0.2.0/26') + ip2 = IPv4Network('192.0.2.64/26') + ip3 = IPv4Network('192.0.2.128/26') + ip4 = IPv4Network('192.0.2.192/26') + + _collapse_addresses_internal([ip1, ip2, ip3, ip4]) -> + [IPv4Network('192.0.2.0/24')] + + This shouldn't be called directly; it is called via + collapse_addresses([]). + + Args: + addresses: A list of IPv4Network's or IPv6Network's + + Returns: + A list of IPv4Network's or IPv6Network's depending on what we were + passed. + + """ + # First merge + to_merge = list(addresses) + subnets = {} + while to_merge: + net = to_merge.pop() + supernet = net.supernet() + existing = subnets.get(supernet) + if existing is None: + subnets[supernet] = net + elif existing != net: + # Merge consecutive subnets + del subnets[supernet] + to_merge.append(supernet) + # Then iterate over resulting networks, skipping subsumed subnets + last = None + for net in sorted(subnets.values()): + if last is not None: + # Since they are sorted, + # last.network_address <= net.network_address is a given. + if last.broadcast_address >= net.broadcast_address: + continue + yield net + last = net + + +def collapse_addresses(addresses): + """Collapse a list of IP objects. + + Example: + collapse_addresses([IPv4Network('192.0.2.0/25'), + IPv4Network('192.0.2.128/25')]) -> + [IPv4Network('192.0.2.0/24')] + + Args: + addresses: An iterator of IPv4Network or IPv6Network objects. + + Returns: + An iterator of the collapsed IPv(4|6)Network objects. + + Raises: + TypeError: If passed a list of mixed version objects. + + """ + addrs = [] + ips = [] + nets = [] + + # split IP addresses and networks + for ip in addresses: + if isinstance(ip, _BaseAddress): + if ips and ips[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + ip, ips[-1])) + ips.append(ip) + elif ip._prefixlen == ip._max_prefixlen: + if ips and ips[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + ip, ips[-1])) + try: + ips.append(ip.ip) + except AttributeError: + ips.append(ip.network_address) + else: + if nets and nets[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + ip, nets[-1])) + nets.append(ip) + + # sort and dedup + ips = sorted(set(ips)) + + # find consecutive address ranges in the sorted sequence and summarize them + if ips: + for first, last in _find_address_range(ips): + addrs.extend(summarize_address_range(first, last)) + + return _collapse_addresses_internal(addrs + nets) + + +def get_mixed_type_key(obj): + """Return a key suitable for sorting between networks and addresses. + + Address and Network objects are not sortable by default; they're + fundamentally different so the expression + + IPv4Address('192.0.2.0') <= IPv4Network('192.0.2.0/24') + + doesn't make any sense. There are some times however, where you may wish + to have ipaddress sort these for you anyway. If you need to do this, you + can use this function as the key= argument to sorted(). + + Args: + obj: either a Network or Address object. + Returns: + appropriate key. + + """ + if isinstance(obj, _BaseNetwork): + return obj._get_networks_key() + elif isinstance(obj, _BaseAddress): + return obj._get_address_key() + return NotImplemented + + +class _IPAddressBase(_TotalOrderingMixin): + + """The mother class.""" + + __slots__ = () + + @property + def exploded(self): + """Return the longhand version of the IP address as a string.""" + return self._explode_shorthand_ip_string() + + @property + def compressed(self): + """Return the shorthand version of the IP address as a string.""" + return _compat_str(self) + + @property + def reverse_pointer(self): + """The name of the reverse DNS pointer for the IP address, e.g.: + >>> ipaddress.ip_address("127.0.0.1").reverse_pointer + '1.0.0.127.in-addr.arpa' + >>> ipaddress.ip_address("2001:db8::1").reverse_pointer + '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa' + + """ + return self._reverse_pointer() + + @property + def version(self): + msg = '%200s has no version specified' % (type(self),) + raise NotImplementedError(msg) + + def _check_int_address(self, address): + if address < 0: + msg = "%d (< 0) is not permitted as an IPv%d address" + raise AddressValueError(msg % (address, self._version)) + if address > self._ALL_ONES: + msg = "%d (>= 2**%d) is not permitted as an IPv%d address" + raise AddressValueError(msg % (address, self._max_prefixlen, + self._version)) + + def _check_packed_address(self, address, expected_len): + address_len = len(address) + if address_len != expected_len: + msg = ( + '%r (len %d != %d) is not permitted as an IPv%d address. ' + 'Did you pass in a bytes (str in Python 2) instead of' + ' a unicode object?') + raise AddressValueError(msg % (address, address_len, + expected_len, self._version)) + + @classmethod + def _ip_int_from_prefix(cls, prefixlen): + """Turn the prefix length into a bitwise netmask + + Args: + prefixlen: An integer, the prefix length. + + Returns: + An integer. + + """ + return cls._ALL_ONES ^ (cls._ALL_ONES >> prefixlen) + + @classmethod + def _prefix_from_ip_int(cls, ip_int): + """Return prefix length from the bitwise netmask. + + Args: + ip_int: An integer, the netmask in expanded bitwise format + + Returns: + An integer, the prefix length. + + Raises: + ValueError: If the input intermingles zeroes & ones + """ + trailing_zeroes = _count_righthand_zero_bits(ip_int, + cls._max_prefixlen) + prefixlen = cls._max_prefixlen - trailing_zeroes + leading_ones = ip_int >> trailing_zeroes + all_ones = (1 << prefixlen) - 1 + if leading_ones != all_ones: + byteslen = cls._max_prefixlen // 8 + details = _compat_to_bytes(ip_int, byteslen, 'big') + msg = 'Netmask pattern %r mixes zeroes & ones' + raise ValueError(msg % details) + return prefixlen + + @classmethod + def _report_invalid_netmask(cls, netmask_str): + msg = '%r is not a valid netmask' % netmask_str + raise NetmaskValueError(msg) + + @classmethod + def _prefix_from_prefix_string(cls, prefixlen_str): + """Return prefix length from a numeric string + + Args: + prefixlen_str: The string to be converted + + Returns: + An integer, the prefix length. + + Raises: + NetmaskValueError: If the input is not a valid netmask + """ + # int allows a leading +/- as well as surrounding whitespace, + # so we ensure that isn't the case + if not _BaseV4._DECIMAL_DIGITS.issuperset(prefixlen_str): + cls._report_invalid_netmask(prefixlen_str) + try: + prefixlen = int(prefixlen_str) + except ValueError: + cls._report_invalid_netmask(prefixlen_str) + if not (0 <= prefixlen <= cls._max_prefixlen): + cls._report_invalid_netmask(prefixlen_str) + return prefixlen + + @classmethod + def _prefix_from_ip_string(cls, ip_str): + """Turn a netmask/hostmask string into a prefix length + + Args: + ip_str: The netmask/hostmask to be converted + + Returns: + An integer, the prefix length. + + Raises: + NetmaskValueError: If the input is not a valid netmask/hostmask + """ + # Parse the netmask/hostmask like an IP address. + try: + ip_int = cls._ip_int_from_string(ip_str) + except AddressValueError: + cls._report_invalid_netmask(ip_str) + + # Try matching a netmask (this would be /1*0*/ as a bitwise regexp). + # Note that the two ambiguous cases (all-ones and all-zeroes) are + # treated as netmasks. + try: + return cls._prefix_from_ip_int(ip_int) + except ValueError: + pass + + # Invert the bits, and try matching a /0+1+/ hostmask instead. + ip_int ^= cls._ALL_ONES + try: + return cls._prefix_from_ip_int(ip_int) + except ValueError: + cls._report_invalid_netmask(ip_str) + + def __reduce__(self): + return self.__class__, (_compat_str(self),) + + +class _BaseAddress(_IPAddressBase): + + """A generic IP object. + + This IP class contains the version independent methods which are + used by single IP addresses. + """ + + __slots__ = () + + def __int__(self): + return self._ip + + def __eq__(self, other): + try: + return (self._ip == other._ip and + self._version == other._version) + except AttributeError: + return NotImplemented + + def __lt__(self, other): + if not isinstance(other, _IPAddressBase): + return NotImplemented + if not isinstance(other, _BaseAddress): + raise TypeError('%s and %s are not of the same type' % ( + self, other)) + if self._version != other._version: + raise TypeError('%s and %s are not of the same version' % ( + self, other)) + if self._ip != other._ip: + return self._ip < other._ip + return False + + # Shorthand for Integer addition and subtraction. This is not + # meant to ever support addition/subtraction of addresses. + def __add__(self, other): + if not isinstance(other, _compat_int_types): + return NotImplemented + return self.__class__(int(self) + other) + + def __sub__(self, other): + if not isinstance(other, _compat_int_types): + return NotImplemented + return self.__class__(int(self) - other) + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, _compat_str(self)) + + def __str__(self): + return _compat_str(self._string_from_ip_int(self._ip)) + + def __hash__(self): + return hash(hex(int(self._ip))) + + def _get_address_key(self): + return (self._version, self) + + def __reduce__(self): + return self.__class__, (self._ip,) + + +class _BaseNetwork(_IPAddressBase): + + """A generic IP network object. + + This IP class contains the version independent methods which are + used by networks. + + """ + def __init__(self, address): + self._cache = {} + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, _compat_str(self)) + + def __str__(self): + return '%s/%d' % (self.network_address, self.prefixlen) + + def hosts(self): + """Generate Iterator over usable hosts in a network. + + This is like __iter__ except it doesn't return the network + or broadcast addresses. + + """ + network = int(self.network_address) + broadcast = int(self.broadcast_address) + for x in _compat_range(network + 1, broadcast): + yield self._address_class(x) + + def __iter__(self): + network = int(self.network_address) + broadcast = int(self.broadcast_address) + for x in _compat_range(network, broadcast + 1): + yield self._address_class(x) + + def __getitem__(self, n): + network = int(self.network_address) + broadcast = int(self.broadcast_address) + if n >= 0: + if network + n > broadcast: + raise IndexError('address out of range') + return self._address_class(network + n) + else: + n += 1 + if broadcast + n < network: + raise IndexError('address out of range') + return self._address_class(broadcast + n) + + def __lt__(self, other): + if not isinstance(other, _IPAddressBase): + return NotImplemented + if not isinstance(other, _BaseNetwork): + raise TypeError('%s and %s are not of the same type' % ( + self, other)) + if self._version != other._version: + raise TypeError('%s and %s are not of the same version' % ( + self, other)) + if self.network_address != other.network_address: + return self.network_address < other.network_address + if self.netmask != other.netmask: + return self.netmask < other.netmask + return False + + def __eq__(self, other): + try: + return (self._version == other._version and + self.network_address == other.network_address and + int(self.netmask) == int(other.netmask)) + except AttributeError: + return NotImplemented + + def __hash__(self): + return hash(int(self.network_address) ^ int(self.netmask)) + + def __contains__(self, other): + # always false if one is v4 and the other is v6. + if self._version != other._version: + return False + # dealing with another network. + if isinstance(other, _BaseNetwork): + return False + # dealing with another address + else: + # address + return (int(self.network_address) <= int(other._ip) <= + int(self.broadcast_address)) + + def overlaps(self, other): + """Tell if self is partly contained in other.""" + return self.network_address in other or ( + self.broadcast_address in other or ( + other.network_address in self or ( + other.broadcast_address in self))) + + @property + def broadcast_address(self): + x = self._cache.get('broadcast_address') + if x is None: + x = self._address_class(int(self.network_address) | + int(self.hostmask)) + self._cache['broadcast_address'] = x + return x + + @property + def hostmask(self): + x = self._cache.get('hostmask') + if x is None: + x = self._address_class(int(self.netmask) ^ self._ALL_ONES) + self._cache['hostmask'] = x + return x + + @property + def with_prefixlen(self): + return '%s/%d' % (self.network_address, self._prefixlen) + + @property + def with_netmask(self): + return '%s/%s' % (self.network_address, self.netmask) + + @property + def with_hostmask(self): + return '%s/%s' % (self.network_address, self.hostmask) + + @property + def num_addresses(self): + """Number of hosts in the current subnet.""" + return int(self.broadcast_address) - int(self.network_address) + 1 + + @property + def _address_class(self): + # Returning bare address objects (rather than interfaces) allows for + # more consistent behaviour across the network address, broadcast + # address and individual host addresses. + msg = '%200s has no associated address class' % (type(self),) + raise NotImplementedError(msg) + + @property + def prefixlen(self): + return self._prefixlen + + def address_exclude(self, other): + """Remove an address from a larger block. + + For example: + + addr1 = ip_network('192.0.2.0/28') + addr2 = ip_network('192.0.2.1/32') + list(addr1.address_exclude(addr2)) = + [IPv4Network('192.0.2.0/32'), IPv4Network('192.0.2.2/31'), + IPv4Network('192.0.2.4/30'), IPv4Network('192.0.2.8/29')] + + or IPv6: + + addr1 = ip_network('2001:db8::1/32') + addr2 = ip_network('2001:db8::1/128') + list(addr1.address_exclude(addr2)) = + [ip_network('2001:db8::1/128'), + ip_network('2001:db8::2/127'), + ip_network('2001:db8::4/126'), + ip_network('2001:db8::8/125'), + ... + ip_network('2001:db8:8000::/33')] + + Args: + other: An IPv4Network or IPv6Network object of the same type. + + Returns: + An iterator of the IPv(4|6)Network objects which is self + minus other. + + Raises: + TypeError: If self and other are of differing address + versions, or if other is not a network object. + ValueError: If other is not completely contained by self. + + """ + if not self._version == other._version: + raise TypeError("%s and %s are not of the same version" % ( + self, other)) + + if not isinstance(other, _BaseNetwork): + raise TypeError("%s is not a network object" % other) + + if not other.subnet_of(self): + raise ValueError('%s not contained in %s' % (other, self)) + if other == self: + return + + # Make sure we're comparing the network of other. + other = other.__class__('%s/%s' % (other.network_address, + other.prefixlen)) + + s1, s2 = self.subnets() + while s1 != other and s2 != other: + if other.subnet_of(s1): + yield s2 + s1, s2 = s1.subnets() + elif other.subnet_of(s2): + yield s1 + s1, s2 = s2.subnets() + else: + # If we got here, there's a bug somewhere. + raise AssertionError('Error performing exclusion: ' + 's1: %s s2: %s other: %s' % + (s1, s2, other)) + if s1 == other: + yield s2 + elif s2 == other: + yield s1 + else: + # If we got here, there's a bug somewhere. + raise AssertionError('Error performing exclusion: ' + 's1: %s s2: %s other: %s' % + (s1, s2, other)) + + def compare_networks(self, other): + """Compare two IP objects. + + This is only concerned about the comparison of the integer + representation of the network addresses. This means that the + host bits aren't considered at all in this method. If you want + to compare host bits, you can easily enough do a + 'HostA._ip < HostB._ip' + + Args: + other: An IP object. + + Returns: + If the IP versions of self and other are the same, returns: + + -1 if self < other: + eg: IPv4Network('192.0.2.0/25') < IPv4Network('192.0.2.128/25') + IPv6Network('2001:db8::1000/124') < + IPv6Network('2001:db8::2000/124') + 0 if self == other + eg: IPv4Network('192.0.2.0/24') == IPv4Network('192.0.2.0/24') + IPv6Network('2001:db8::1000/124') == + IPv6Network('2001:db8::1000/124') + 1 if self > other + eg: IPv4Network('192.0.2.128/25') > IPv4Network('192.0.2.0/25') + IPv6Network('2001:db8::2000/124') > + IPv6Network('2001:db8::1000/124') + + Raises: + TypeError if the IP versions are different. + + """ + # does this need to raise a ValueError? + if self._version != other._version: + raise TypeError('%s and %s are not of the same type' % ( + self, other)) + # self._version == other._version below here: + if self.network_address < other.network_address: + return -1 + if self.network_address > other.network_address: + return 1 + # self.network_address == other.network_address below here: + if self.netmask < other.netmask: + return -1 + if self.netmask > other.netmask: + return 1 + return 0 + + def _get_networks_key(self): + """Network-only key function. + + Returns an object that identifies this address' network and + netmask. This function is a suitable "key" argument for sorted() + and list.sort(). + + """ + return (self._version, self.network_address, self.netmask) + + def subnets(self, prefixlen_diff=1, new_prefix=None): + """The subnets which join to make the current subnet. + + In the case that self contains only one IP + (self._prefixlen == 32 for IPv4 or self._prefixlen == 128 + for IPv6), yield an iterator with just ourself. + + Args: + prefixlen_diff: An integer, the amount the prefix length + should be increased by. This should not be set if + new_prefix is also set. + new_prefix: The desired new prefix length. This must be a + larger number (smaller prefix) than the existing prefix. + This should not be set if prefixlen_diff is also set. + + Returns: + An iterator of IPv(4|6) objects. + + Raises: + ValueError: The prefixlen_diff is too small or too large. + OR + prefixlen_diff and new_prefix are both set or new_prefix + is a smaller number than the current prefix (smaller + number means a larger network) + + """ + if self._prefixlen == self._max_prefixlen: + yield self + return + + if new_prefix is not None: + if new_prefix < self._prefixlen: + raise ValueError('new prefix must be longer') + if prefixlen_diff != 1: + raise ValueError('cannot set prefixlen_diff and new_prefix') + prefixlen_diff = new_prefix - self._prefixlen + + if prefixlen_diff < 0: + raise ValueError('prefix length diff must be > 0') + new_prefixlen = self._prefixlen + prefixlen_diff + + if new_prefixlen > self._max_prefixlen: + raise ValueError( + 'prefix length diff %d is invalid for netblock %s' % ( + new_prefixlen, self)) + + start = int(self.network_address) + end = int(self.broadcast_address) + 1 + step = (int(self.hostmask) + 1) >> prefixlen_diff + for new_addr in _compat_range(start, end, step): + current = self.__class__((new_addr, new_prefixlen)) + yield current + + def supernet(self, prefixlen_diff=1, new_prefix=None): + """The supernet containing the current network. + + Args: + prefixlen_diff: An integer, the amount the prefix length of + the network should be decreased by. For example, given a + /24 network and a prefixlen_diff of 3, a supernet with a + /21 netmask is returned. + + Returns: + An IPv4 network object. + + Raises: + ValueError: If self.prefixlen - prefixlen_diff < 0. I.e., you have + a negative prefix length. + OR + If prefixlen_diff and new_prefix are both set or new_prefix is a + larger number than the current prefix (larger number means a + smaller network) + + """ + if self._prefixlen == 0: + return self + + if new_prefix is not None: + if new_prefix > self._prefixlen: + raise ValueError('new prefix must be shorter') + if prefixlen_diff != 1: + raise ValueError('cannot set prefixlen_diff and new_prefix') + prefixlen_diff = self._prefixlen - new_prefix + + new_prefixlen = self.prefixlen - prefixlen_diff + if new_prefixlen < 0: + raise ValueError( + 'current prefixlen is %d, cannot have a prefixlen_diff of %d' % + (self.prefixlen, prefixlen_diff)) + return self.__class__(( + int(self.network_address) & (int(self.netmask) << prefixlen_diff), + new_prefixlen)) + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is a multicast address. + See RFC 2373 2.7 for details. + + """ + return (self.network_address.is_multicast and + self.broadcast_address.is_multicast) + + @staticmethod + def _is_subnet_of(a, b): + try: + # Always false if one is v4 and the other is v6. + if a._version != b._version: + raise TypeError("%s and %s are not of the same version" (a, b)) + return (b.network_address <= a.network_address and + b.broadcast_address >= a.broadcast_address) + except AttributeError: + raise TypeError("Unable to test subnet containment " + "between %s and %s" % (a, b)) + + def subnet_of(self, other): + """Return True if this network is a subnet of other.""" + return self._is_subnet_of(self, other) + + def supernet_of(self, other): + """Return True if this network is a supernet of other.""" + return self._is_subnet_of(other, self) + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within one of the + reserved IPv6 Network ranges. + + """ + return (self.network_address.is_reserved and + self.broadcast_address.is_reserved) + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is reserved per RFC 4291. + + """ + return (self.network_address.is_link_local and + self.broadcast_address.is_link_local) + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per + iana-ipv4-special-registry or iana-ipv6-special-registry. + + """ + return (self.network_address.is_private and + self.broadcast_address.is_private) + + @property + def is_global(self): + """Test if this address is allocated for public networks. + + Returns: + A boolean, True if the address is not reserved per + iana-ipv4-special-registry or iana-ipv6-special-registry. + + """ + return not self.is_private + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 2373 2.5.2. + + """ + return (self.network_address.is_unspecified and + self.broadcast_address.is_unspecified) + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback address as defined in + RFC 2373 2.5.3. + + """ + return (self.network_address.is_loopback and + self.broadcast_address.is_loopback) + + +class _BaseV4(object): + + """Base IPv4 object. + + The following methods are used by IPv4 objects in both single IP + addresses and networks. + + """ + + __slots__ = () + _version = 4 + # Equivalent to 255.255.255.255 or 32 bits of 1's. + _ALL_ONES = (2 ** IPV4LENGTH) - 1 + _DECIMAL_DIGITS = frozenset('0123456789') + + # the valid octets for host and netmasks. only useful for IPv4. + _valid_mask_octets = frozenset([255, 254, 252, 248, 240, 224, 192, 128, 0]) + + _max_prefixlen = IPV4LENGTH + # There are only a handful of valid v4 netmasks, so we cache them all + # when constructed (see _make_netmask()). + _netmask_cache = {} + + def _explode_shorthand_ip_string(self): + return _compat_str(self) + + @classmethod + def _make_netmask(cls, arg): + """Make a (netmask, prefix_len) tuple from the given argument. + + Argument can be: + - an integer (the prefix length) + - a string representing the prefix length (e.g. "24") + - a string representing the prefix netmask (e.g. "255.255.255.0") + """ + if arg not in cls._netmask_cache: + if isinstance(arg, _compat_int_types): + prefixlen = arg + else: + try: + # Check for a netmask in prefix length form + prefixlen = cls._prefix_from_prefix_string(arg) + except NetmaskValueError: + # Check for a netmask or hostmask in dotted-quad form. + # This may raise NetmaskValueError. + prefixlen = cls._prefix_from_ip_string(arg) + netmask = IPv4Address(cls._ip_int_from_prefix(prefixlen)) + cls._netmask_cache[arg] = netmask, prefixlen + return cls._netmask_cache[arg] + + @classmethod + def _ip_int_from_string(cls, ip_str): + """Turn the given IP string into an integer for comparison. + + Args: + ip_str: A string, the IP ip_str. + + Returns: + The IP ip_str as an integer. + + Raises: + AddressValueError: if ip_str isn't a valid IPv4 Address. + + """ + if not ip_str: + raise AddressValueError('Address cannot be empty') + + octets = ip_str.split('.') + if len(octets) != 4: + raise AddressValueError("Expected 4 octets in %r" % ip_str) + + try: + return _compat_int_from_byte_vals( + map(cls._parse_octet, octets), 'big') + except ValueError as exc: + raise AddressValueError("%s in %r" % (exc, ip_str)) + + @classmethod + def _parse_octet(cls, octet_str): + """Convert a decimal octet into an integer. + + Args: + octet_str: A string, the number to parse. + + Returns: + The octet as an integer. + + Raises: + ValueError: if the octet isn't strictly a decimal from [0..255]. + + """ + if not octet_str: + raise ValueError("Empty octet not permitted") + # Whitelist the characters, since int() allows a lot of bizarre stuff. + if not cls._DECIMAL_DIGITS.issuperset(octet_str): + msg = "Only decimal digits permitted in %r" + raise ValueError(msg % octet_str) + # We do the length check second, since the invalid character error + # is likely to be more informative for the user + if len(octet_str) > 3: + msg = "At most 3 characters permitted in %r" + raise ValueError(msg % octet_str) + # Convert to integer (we know digits are legal) + octet_int = int(octet_str, 10) + # Any octets that look like they *might* be written in octal, + # and which don't look exactly the same in both octal and + # decimal are rejected as ambiguous + if octet_int > 7 and octet_str[0] == '0': + msg = "Ambiguous (octal/decimal) value in %r not permitted" + raise ValueError(msg % octet_str) + if octet_int > 255: + raise ValueError("Octet %d (> 255) not permitted" % octet_int) + return octet_int + + @classmethod + def _string_from_ip_int(cls, ip_int): + """Turns a 32-bit integer into dotted decimal notation. + + Args: + ip_int: An integer, the IP address. + + Returns: + The IP address as a string in dotted decimal notation. + + """ + return '.'.join(_compat_str(struct.unpack(b'!B', b)[0] + if isinstance(b, bytes) + else b) + for b in _compat_to_bytes(ip_int, 4, 'big')) + + def _is_hostmask(self, ip_str): + """Test if the IP string is a hostmask (rather than a netmask). + + Args: + ip_str: A string, the potential hostmask. + + Returns: + A boolean, True if the IP string is a hostmask. + + """ + bits = ip_str.split('.') + try: + parts = [x for x in map(int, bits) if x in self._valid_mask_octets] + except ValueError: + return False + if len(parts) != len(bits): + return False + if parts[0] < parts[-1]: + return True + return False + + def _reverse_pointer(self): + """Return the reverse DNS pointer name for the IPv4 address. + + This implements the method described in RFC1035 3.5. + + """ + reverse_octets = _compat_str(self).split('.')[::-1] + return '.'.join(reverse_octets) + '.in-addr.arpa' + + @property + def max_prefixlen(self): + return self._max_prefixlen + + @property + def version(self): + return self._version + + +class IPv4Address(_BaseV4, _BaseAddress): + + """Represent and manipulate single IPv4 Addresses.""" + + __slots__ = ('_ip', '__weakref__') + + def __init__(self, address): + + """ + Args: + address: A string or integer representing the IP + + Additionally, an integer can be passed, so + IPv4Address('192.0.2.1') == IPv4Address(3221225985). + or, more generally + IPv4Address(int(IPv4Address('192.0.2.1'))) == + IPv4Address('192.0.2.1') + + Raises: + AddressValueError: If ipaddress isn't a valid IPv4 address. + + """ + # Efficient constructor from integer. + if isinstance(address, _compat_int_types): + self._check_int_address(address) + self._ip = address + return + + # Constructing from a packed address + if isinstance(address, bytes): + self._check_packed_address(address, 4) + bvs = _compat_bytes_to_byte_vals(address) + self._ip = _compat_int_from_byte_vals(bvs, 'big') + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP string. + addr_str = _compat_str(address) + if '/' in addr_str: + raise AddressValueError("Unexpected '/' in %r" % address) + self._ip = self._ip_int_from_string(addr_str) + + @property + def packed(self): + """The binary representation of this address.""" + return v4_int_to_packed(self._ip) + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within the + reserved IPv4 Network range. + + """ + return self in self._constants._reserved_network + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per + iana-ipv4-special-registry. + + """ + return any(self in net for net in self._constants._private_networks) + + @property + def is_global(self): + return ( + self not in self._constants._public_network and + not self.is_private) + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is multicast. + See RFC 3171 for details. + + """ + return self in self._constants._multicast_network + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 5735 3. + + """ + return self == self._constants._unspecified_address + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback per RFC 3330. + + """ + return self in self._constants._loopback_network + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is link-local per RFC 3927. + + """ + return self in self._constants._linklocal_network + + +class IPv4Interface(IPv4Address): + + def __init__(self, address): + if isinstance(address, (bytes, _compat_int_types)): + IPv4Address.__init__(self, address) + self.network = IPv4Network(self._ip) + self._prefixlen = self._max_prefixlen + return + + if isinstance(address, tuple): + IPv4Address.__init__(self, address[0]) + if len(address) > 1: + self._prefixlen = int(address[1]) + else: + self._prefixlen = self._max_prefixlen + + self.network = IPv4Network(address, strict=False) + self.netmask = self.network.netmask + self.hostmask = self.network.hostmask + return + + addr = _split_optional_netmask(address) + IPv4Address.__init__(self, addr[0]) + + self.network = IPv4Network(address, strict=False) + self._prefixlen = self.network._prefixlen + + self.netmask = self.network.netmask + self.hostmask = self.network.hostmask + + def __str__(self): + return '%s/%d' % (self._string_from_ip_int(self._ip), + self.network.prefixlen) + + def __eq__(self, other): + address_equal = IPv4Address.__eq__(self, other) + if not address_equal or address_equal is NotImplemented: + return address_equal + try: + return self.network == other.network + except AttributeError: + # An interface with an associated network is NOT the + # same as an unassociated address. That's why the hash + # takes the extra info into account. + return False + + def __lt__(self, other): + address_less = IPv4Address.__lt__(self, other) + if address_less is NotImplemented: + return NotImplemented + try: + return (self.network < other.network or + self.network == other.network and address_less) + except AttributeError: + # We *do* allow addresses and interfaces to be sorted. The + # unassociated address is considered less than all interfaces. + return False + + def __hash__(self): + return self._ip ^ self._prefixlen ^ int(self.network.network_address) + + __reduce__ = _IPAddressBase.__reduce__ + + @property + def ip(self): + return IPv4Address(self._ip) + + @property + def with_prefixlen(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self._prefixlen) + + @property + def with_netmask(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self.netmask) + + @property + def with_hostmask(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self.hostmask) + + +class IPv4Network(_BaseV4, _BaseNetwork): + + """This class represents and manipulates 32-bit IPv4 network + addresses.. + + Attributes: [examples for IPv4Network('192.0.2.0/27')] + .network_address: IPv4Address('192.0.2.0') + .hostmask: IPv4Address('0.0.0.31') + .broadcast_address: IPv4Address('192.0.2.32') + .netmask: IPv4Address('255.255.255.224') + .prefixlen: 27 + + """ + # Class to use when creating address objects + _address_class = IPv4Address + + def __init__(self, address, strict=True): + + """Instantiate a new IPv4 network object. + + Args: + address: A string or integer representing the IP [& network]. + '192.0.2.0/24' + '192.0.2.0/255.255.255.0' + '192.0.0.2/0.0.0.255' + are all functionally the same in IPv4. Similarly, + '192.0.2.1' + '192.0.2.1/255.255.255.255' + '192.0.2.1/32' + are also functionally equivalent. That is to say, failing to + provide a subnetmask will create an object with a mask of /32. + + If the mask (portion after the / in the argument) is given in + dotted quad form, it is treated as a netmask if it starts with a + non-zero field (e.g. /255.0.0.0 == /8) and as a hostmask if it + starts with a zero field (e.g. 0.255.255.255 == /8), with the + single exception of an all-zero mask which is treated as a + netmask == /0. If no mask is given, a default of /32 is used. + + Additionally, an integer can be passed, so + IPv4Network('192.0.2.1') == IPv4Network(3221225985) + or, more generally + IPv4Interface(int(IPv4Interface('192.0.2.1'))) == + IPv4Interface('192.0.2.1') + + Raises: + AddressValueError: If ipaddress isn't a valid IPv4 address. + NetmaskValueError: If the netmask isn't valid for + an IPv4 address. + ValueError: If strict is True and a network address is not + supplied. + + """ + _BaseNetwork.__init__(self, address) + + # Constructing from a packed address or integer + if isinstance(address, (_compat_int_types, bytes)): + self.network_address = IPv4Address(address) + self.netmask, self._prefixlen = self._make_netmask( + self._max_prefixlen) + # fixme: address/network test here. + return + + if isinstance(address, tuple): + if len(address) > 1: + arg = address[1] + else: + # We weren't given an address[1] + arg = self._max_prefixlen + self.network_address = IPv4Address(address[0]) + self.netmask, self._prefixlen = self._make_netmask(arg) + packed = int(self.network_address) + if packed & int(self.netmask) != packed: + if strict: + raise ValueError('%s has host bits set' % self) + else: + self.network_address = IPv4Address(packed & + int(self.netmask)) + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP prefix string. + addr = _split_optional_netmask(address) + self.network_address = IPv4Address(self._ip_int_from_string(addr[0])) + + if len(addr) == 2: + arg = addr[1] + else: + arg = self._max_prefixlen + self.netmask, self._prefixlen = self._make_netmask(arg) + + if strict: + if (IPv4Address(int(self.network_address) & int(self.netmask)) != + self.network_address): + raise ValueError('%s has host bits set' % self) + self.network_address = IPv4Address(int(self.network_address) & + int(self.netmask)) + + if self._prefixlen == (self._max_prefixlen - 1): + self.hosts = self.__iter__ + + @property + def is_global(self): + """Test if this address is allocated for public networks. + + Returns: + A boolean, True if the address is not reserved per + iana-ipv4-special-registry. + + """ + return (not (self.network_address in IPv4Network('100.64.0.0/10') and + self.broadcast_address in IPv4Network('100.64.0.0/10')) and + not self.is_private) + + +class _IPv4Constants(object): + + _linklocal_network = IPv4Network('169.254.0.0/16') + + _loopback_network = IPv4Network('127.0.0.0/8') + + _multicast_network = IPv4Network('224.0.0.0/4') + + _public_network = IPv4Network('100.64.0.0/10') + + _private_networks = [ + IPv4Network('0.0.0.0/8'), + IPv4Network('10.0.0.0/8'), + IPv4Network('127.0.0.0/8'), + IPv4Network('169.254.0.0/16'), + IPv4Network('172.16.0.0/12'), + IPv4Network('192.0.0.0/29'), + IPv4Network('192.0.0.170/31'), + IPv4Network('192.0.2.0/24'), + IPv4Network('192.168.0.0/16'), + IPv4Network('198.18.0.0/15'), + IPv4Network('198.51.100.0/24'), + IPv4Network('203.0.113.0/24'), + IPv4Network('240.0.0.0/4'), + IPv4Network('255.255.255.255/32'), + ] + + _reserved_network = IPv4Network('240.0.0.0/4') + + _unspecified_address = IPv4Address('0.0.0.0') + + +IPv4Address._constants = _IPv4Constants + + +class _BaseV6(object): + + """Base IPv6 object. + + The following methods are used by IPv6 objects in both single IP + addresses and networks. + + """ + + __slots__ = () + _version = 6 + _ALL_ONES = (2 ** IPV6LENGTH) - 1 + _HEXTET_COUNT = 8 + _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef') + _max_prefixlen = IPV6LENGTH + + # There are only a bunch of valid v6 netmasks, so we cache them all + # when constructed (see _make_netmask()). + _netmask_cache = {} + + @classmethod + def _make_netmask(cls, arg): + """Make a (netmask, prefix_len) tuple from the given argument. + + Argument can be: + - an integer (the prefix length) + - a string representing the prefix length (e.g. "24") + - a string representing the prefix netmask (e.g. "255.255.255.0") + """ + if arg not in cls._netmask_cache: + if isinstance(arg, _compat_int_types): + prefixlen = arg + else: + prefixlen = cls._prefix_from_prefix_string(arg) + netmask = IPv6Address(cls._ip_int_from_prefix(prefixlen)) + cls._netmask_cache[arg] = netmask, prefixlen + return cls._netmask_cache[arg] + + @classmethod + def _ip_int_from_string(cls, ip_str): + """Turn an IPv6 ip_str into an integer. + + Args: + ip_str: A string, the IPv6 ip_str. + + Returns: + An int, the IPv6 address + + Raises: + AddressValueError: if ip_str isn't a valid IPv6 Address. + + """ + if not ip_str: + raise AddressValueError('Address cannot be empty') + + parts = ip_str.split(':') + + # An IPv6 address needs at least 2 colons (3 parts). + _min_parts = 3 + if len(parts) < _min_parts: + msg = "At least %d parts expected in %r" % (_min_parts, ip_str) + raise AddressValueError(msg) + + # If the address has an IPv4-style suffix, convert it to hexadecimal. + if '.' in parts[-1]: + try: + ipv4_int = IPv4Address(parts.pop())._ip + except AddressValueError as exc: + raise AddressValueError("%s in %r" % (exc, ip_str)) + parts.append('%x' % ((ipv4_int >> 16) & 0xFFFF)) + parts.append('%x' % (ipv4_int & 0xFFFF)) + + # An IPv6 address can't have more than 8 colons (9 parts). + # The extra colon comes from using the "::" notation for a single + # leading or trailing zero part. + _max_parts = cls._HEXTET_COUNT + 1 + if len(parts) > _max_parts: + msg = "At most %d colons permitted in %r" % ( + _max_parts - 1, ip_str) + raise AddressValueError(msg) + + # Disregarding the endpoints, find '::' with nothing in between. + # This indicates that a run of zeroes has been skipped. + skip_index = None + for i in _compat_range(1, len(parts) - 1): + if not parts[i]: + if skip_index is not None: + # Can't have more than one '::' + msg = "At most one '::' permitted in %r" % ip_str + raise AddressValueError(msg) + skip_index = i + + # parts_hi is the number of parts to copy from above/before the '::' + # parts_lo is the number of parts to copy from below/after the '::' + if skip_index is not None: + # If we found a '::', then check if it also covers the endpoints. + parts_hi = skip_index + parts_lo = len(parts) - skip_index - 1 + if not parts[0]: + parts_hi -= 1 + if parts_hi: + msg = "Leading ':' only permitted as part of '::' in %r" + raise AddressValueError(msg % ip_str) # ^: requires ^:: + if not parts[-1]: + parts_lo -= 1 + if parts_lo: + msg = "Trailing ':' only permitted as part of '::' in %r" + raise AddressValueError(msg % ip_str) # :$ requires ::$ + parts_skipped = cls._HEXTET_COUNT - (parts_hi + parts_lo) + if parts_skipped < 1: + msg = "Expected at most %d other parts with '::' in %r" + raise AddressValueError(msg % (cls._HEXTET_COUNT - 1, ip_str)) + else: + # Otherwise, allocate the entire address to parts_hi. The + # endpoints could still be empty, but _parse_hextet() will check + # for that. + if len(parts) != cls._HEXTET_COUNT: + msg = "Exactly %d parts expected without '::' in %r" + raise AddressValueError(msg % (cls._HEXTET_COUNT, ip_str)) + if not parts[0]: + msg = "Leading ':' only permitted as part of '::' in %r" + raise AddressValueError(msg % ip_str) # ^: requires ^:: + if not parts[-1]: + msg = "Trailing ':' only permitted as part of '::' in %r" + raise AddressValueError(msg % ip_str) # :$ requires ::$ + parts_hi = len(parts) + parts_lo = 0 + parts_skipped = 0 + + try: + # Now, parse the hextets into a 128-bit integer. + ip_int = 0 + for i in range(parts_hi): + ip_int <<= 16 + ip_int |= cls._parse_hextet(parts[i]) + ip_int <<= 16 * parts_skipped + for i in range(-parts_lo, 0): + ip_int <<= 16 + ip_int |= cls._parse_hextet(parts[i]) + return ip_int + except ValueError as exc: + raise AddressValueError("%s in %r" % (exc, ip_str)) + + @classmethod + def _parse_hextet(cls, hextet_str): + """Convert an IPv6 hextet string into an integer. + + Args: + hextet_str: A string, the number to parse. + + Returns: + The hextet as an integer. + + Raises: + ValueError: if the input isn't strictly a hex number from + [0..FFFF]. + + """ + # Whitelist the characters, since int() allows a lot of bizarre stuff. + if not cls._HEX_DIGITS.issuperset(hextet_str): + raise ValueError("Only hex digits permitted in %r" % hextet_str) + # We do the length check second, since the invalid character error + # is likely to be more informative for the user + if len(hextet_str) > 4: + msg = "At most 4 characters permitted in %r" + raise ValueError(msg % hextet_str) + # Length check means we can skip checking the integer value + return int(hextet_str, 16) + + @classmethod + def _compress_hextets(cls, hextets): + """Compresses a list of hextets. + + Compresses a list of strings, replacing the longest continuous + sequence of "0" in the list with "" and adding empty strings at + the beginning or at the end of the string such that subsequently + calling ":".join(hextets) will produce the compressed version of + the IPv6 address. + + Args: + hextets: A list of strings, the hextets to compress. + + Returns: + A list of strings. + + """ + best_doublecolon_start = -1 + best_doublecolon_len = 0 + doublecolon_start = -1 + doublecolon_len = 0 + for index, hextet in enumerate(hextets): + if hextet == '0': + doublecolon_len += 1 + if doublecolon_start == -1: + # Start of a sequence of zeros. + doublecolon_start = index + if doublecolon_len > best_doublecolon_len: + # This is the longest sequence of zeros so far. + best_doublecolon_len = doublecolon_len + best_doublecolon_start = doublecolon_start + else: + doublecolon_len = 0 + doublecolon_start = -1 + + if best_doublecolon_len > 1: + best_doublecolon_end = (best_doublecolon_start + + best_doublecolon_len) + # For zeros at the end of the address. + if best_doublecolon_end == len(hextets): + hextets += [''] + hextets[best_doublecolon_start:best_doublecolon_end] = [''] + # For zeros at the beginning of the address. + if best_doublecolon_start == 0: + hextets = [''] + hextets + + return hextets + + @classmethod + def _string_from_ip_int(cls, ip_int=None): + """Turns a 128-bit integer into hexadecimal notation. + + Args: + ip_int: An integer, the IP address. + + Returns: + A string, the hexadecimal representation of the address. + + Raises: + ValueError: The address is bigger than 128 bits of all ones. + + """ + if ip_int is None: + ip_int = int(cls._ip) + + if ip_int > cls._ALL_ONES: + raise ValueError('IPv6 address is too large') + + hex_str = '%032x' % ip_int + hextets = ['%x' % int(hex_str[x:x + 4], 16) for x in range(0, 32, 4)] + + hextets = cls._compress_hextets(hextets) + return ':'.join(hextets) + + def _explode_shorthand_ip_string(self): + """Expand a shortened IPv6 address. + + Args: + ip_str: A string, the IPv6 address. + + Returns: + A string, the expanded IPv6 address. + + """ + if isinstance(self, IPv6Network): + ip_str = _compat_str(self.network_address) + elif isinstance(self, IPv6Interface): + ip_str = _compat_str(self.ip) + else: + ip_str = _compat_str(self) + + ip_int = self._ip_int_from_string(ip_str) + hex_str = '%032x' % ip_int + parts = [hex_str[x:x + 4] for x in range(0, 32, 4)] + if isinstance(self, (_BaseNetwork, IPv6Interface)): + return '%s/%d' % (':'.join(parts), self._prefixlen) + return ':'.join(parts) + + def _reverse_pointer(self): + """Return the reverse DNS pointer name for the IPv6 address. + + This implements the method described in RFC3596 2.5. + + """ + reverse_chars = self.exploded[::-1].replace(':', '') + return '.'.join(reverse_chars) + '.ip6.arpa' + + @property + def max_prefixlen(self): + return self._max_prefixlen + + @property + def version(self): + return self._version + + +class IPv6Address(_BaseV6, _BaseAddress): + + """Represent and manipulate single IPv6 Addresses.""" + + __slots__ = ('_ip', '__weakref__') + + def __init__(self, address): + """Instantiate a new IPv6 address object. + + Args: + address: A string or integer representing the IP + + Additionally, an integer can be passed, so + IPv6Address('2001:db8::') == + IPv6Address(42540766411282592856903984951653826560) + or, more generally + IPv6Address(int(IPv6Address('2001:db8::'))) == + IPv6Address('2001:db8::') + + Raises: + AddressValueError: If address isn't a valid IPv6 address. + + """ + # Efficient constructor from integer. + if isinstance(address, _compat_int_types): + self._check_int_address(address) + self._ip = address + return + + # Constructing from a packed address + if isinstance(address, bytes): + self._check_packed_address(address, 16) + bvs = _compat_bytes_to_byte_vals(address) + self._ip = _compat_int_from_byte_vals(bvs, 'big') + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP string. + addr_str = _compat_str(address) + if '/' in addr_str: + raise AddressValueError("Unexpected '/' in %r" % address) + self._ip = self._ip_int_from_string(addr_str) + + @property + def packed(self): + """The binary representation of this address.""" + return v6_int_to_packed(self._ip) + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is a multicast address. + See RFC 2373 2.7 for details. + + """ + return self in self._constants._multicast_network + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within one of the + reserved IPv6 Network ranges. + + """ + return any(self in x for x in self._constants._reserved_networks) + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is reserved per RFC 4291. + + """ + return self in self._constants._linklocal_network + + @property + def is_site_local(self): + """Test if the address is reserved for site-local. + + Note that the site-local address space has been deprecated by RFC 3879. + Use is_private to test if this address is in the space of unique local + addresses as defined by RFC 4193. + + Returns: + A boolean, True if the address is reserved per RFC 3513 2.5.6. + + """ + return self in self._constants._sitelocal_network + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per + iana-ipv6-special-registry. + + """ + return any(self in net for net in self._constants._private_networks) + + @property + def is_global(self): + """Test if this address is allocated for public networks. + + Returns: + A boolean, true if the address is not reserved per + iana-ipv6-special-registry. + + """ + return not self.is_private + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 2373 2.5.2. + + """ + return self._ip == 0 + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback address as defined in + RFC 2373 2.5.3. + + """ + return self._ip == 1 + + @property + def ipv4_mapped(self): + """Return the IPv4 mapped address. + + Returns: + If the IPv6 address is a v4 mapped address, return the + IPv4 mapped address. Return None otherwise. + + """ + if (self._ip >> 32) != 0xFFFF: + return None + return IPv4Address(self._ip & 0xFFFFFFFF) + + @property + def teredo(self): + """Tuple of embedded teredo IPs. + + Returns: + Tuple of the (server, client) IPs or None if the address + doesn't appear to be a teredo address (doesn't start with + 2001::/32) + + """ + if (self._ip >> 96) != 0x20010000: + return None + return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF), + IPv4Address(~self._ip & 0xFFFFFFFF)) + + @property + def sixtofour(self): + """Return the IPv4 6to4 embedded address. + + Returns: + The IPv4 6to4-embedded address if present or None if the + address doesn't appear to contain a 6to4 embedded address. + + """ + if (self._ip >> 112) != 0x2002: + return None + return IPv4Address((self._ip >> 80) & 0xFFFFFFFF) + + +class IPv6Interface(IPv6Address): + + def __init__(self, address): + if isinstance(address, (bytes, _compat_int_types)): + IPv6Address.__init__(self, address) + self.network = IPv6Network(self._ip) + self._prefixlen = self._max_prefixlen + return + if isinstance(address, tuple): + IPv6Address.__init__(self, address[0]) + if len(address) > 1: + self._prefixlen = int(address[1]) + else: + self._prefixlen = self._max_prefixlen + self.network = IPv6Network(address, strict=False) + self.netmask = self.network.netmask + self.hostmask = self.network.hostmask + return + + addr = _split_optional_netmask(address) + IPv6Address.__init__(self, addr[0]) + self.network = IPv6Network(address, strict=False) + self.netmask = self.network.netmask + self._prefixlen = self.network._prefixlen + self.hostmask = self.network.hostmask + + def __str__(self): + return '%s/%d' % (self._string_from_ip_int(self._ip), + self.network.prefixlen) + + def __eq__(self, other): + address_equal = IPv6Address.__eq__(self, other) + if not address_equal or address_equal is NotImplemented: + return address_equal + try: + return self.network == other.network + except AttributeError: + # An interface with an associated network is NOT the + # same as an unassociated address. That's why the hash + # takes the extra info into account. + return False + + def __lt__(self, other): + address_less = IPv6Address.__lt__(self, other) + if address_less is NotImplemented: + return NotImplemented + try: + return (self.network < other.network or + self.network == other.network and address_less) + except AttributeError: + # We *do* allow addresses and interfaces to be sorted. The + # unassociated address is considered less than all interfaces. + return False + + def __hash__(self): + return self._ip ^ self._prefixlen ^ int(self.network.network_address) + + __reduce__ = _IPAddressBase.__reduce__ + + @property + def ip(self): + return IPv6Address(self._ip) + + @property + def with_prefixlen(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self._prefixlen) + + @property + def with_netmask(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self.netmask) + + @property + def with_hostmask(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self.hostmask) + + @property + def is_unspecified(self): + return self._ip == 0 and self.network.is_unspecified + + @property + def is_loopback(self): + return self._ip == 1 and self.network.is_loopback + + +class IPv6Network(_BaseV6, _BaseNetwork): + + """This class represents and manipulates 128-bit IPv6 networks. + + Attributes: [examples for IPv6('2001:db8::1000/124')] + .network_address: IPv6Address('2001:db8::1000') + .hostmask: IPv6Address('::f') + .broadcast_address: IPv6Address('2001:db8::100f') + .netmask: IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0') + .prefixlen: 124 + + """ + + # Class to use when creating address objects + _address_class = IPv6Address + + def __init__(self, address, strict=True): + """Instantiate a new IPv6 Network object. + + Args: + address: A string or integer representing the IPv6 network or the + IP and prefix/netmask. + '2001:db8::/128' + '2001:db8:0000:0000:0000:0000:0000:0000/128' + '2001:db8::' + are all functionally the same in IPv6. That is to say, + failing to provide a subnetmask will create an object with + a mask of /128. + + Additionally, an integer can be passed, so + IPv6Network('2001:db8::') == + IPv6Network(42540766411282592856903984951653826560) + or, more generally + IPv6Network(int(IPv6Network('2001:db8::'))) == + IPv6Network('2001:db8::') + + strict: A boolean. If true, ensure that we have been passed + A true network address, eg, 2001:db8::1000/124 and not an + IP address on a network, eg, 2001:db8::1/124. + + Raises: + AddressValueError: If address isn't a valid IPv6 address. + NetmaskValueError: If the netmask isn't valid for + an IPv6 address. + ValueError: If strict was True and a network address was not + supplied. + + """ + _BaseNetwork.__init__(self, address) + + # Efficient constructor from integer or packed address + if isinstance(address, (bytes, _compat_int_types)): + self.network_address = IPv6Address(address) + self.netmask, self._prefixlen = self._make_netmask( + self._max_prefixlen) + return + + if isinstance(address, tuple): + if len(address) > 1: + arg = address[1] + else: + arg = self._max_prefixlen + self.netmask, self._prefixlen = self._make_netmask(arg) + self.network_address = IPv6Address(address[0]) + packed = int(self.network_address) + if packed & int(self.netmask) != packed: + if strict: + raise ValueError('%s has host bits set' % self) + else: + self.network_address = IPv6Address(packed & + int(self.netmask)) + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP prefix string. + addr = _split_optional_netmask(address) + + self.network_address = IPv6Address(self._ip_int_from_string(addr[0])) + + if len(addr) == 2: + arg = addr[1] + else: + arg = self._max_prefixlen + self.netmask, self._prefixlen = self._make_netmask(arg) + + if strict: + if (IPv6Address(int(self.network_address) & int(self.netmask)) != + self.network_address): + raise ValueError('%s has host bits set' % self) + self.network_address = IPv6Address(int(self.network_address) & + int(self.netmask)) + + if self._prefixlen == (self._max_prefixlen - 1): + self.hosts = self.__iter__ + + def hosts(self): + """Generate Iterator over usable hosts in a network. + + This is like __iter__ except it doesn't return the + Subnet-Router anycast address. + + """ + network = int(self.network_address) + broadcast = int(self.broadcast_address) + for x in _compat_range(network + 1, broadcast + 1): + yield self._address_class(x) + + @property + def is_site_local(self): + """Test if the address is reserved for site-local. + + Note that the site-local address space has been deprecated by RFC 3879. + Use is_private to test if this address is in the space of unique local + addresses as defined by RFC 4193. + + Returns: + A boolean, True if the address is reserved per RFC 3513 2.5.6. + + """ + return (self.network_address.is_site_local and + self.broadcast_address.is_site_local) + + +class _IPv6Constants(object): + + _linklocal_network = IPv6Network('fe80::/10') + + _multicast_network = IPv6Network('ff00::/8') + + _private_networks = [ + IPv6Network('::1/128'), + IPv6Network('::/128'), + IPv6Network('::ffff:0:0/96'), + IPv6Network('100::/64'), + IPv6Network('2001::/23'), + IPv6Network('2001:2::/48'), + IPv6Network('2001:db8::/32'), + IPv6Network('2001:10::/28'), + IPv6Network('fc00::/7'), + IPv6Network('fe80::/10'), + ] + + _reserved_networks = [ + IPv6Network('::/8'), IPv6Network('100::/8'), + IPv6Network('200::/7'), IPv6Network('400::/6'), + IPv6Network('800::/5'), IPv6Network('1000::/4'), + IPv6Network('4000::/3'), IPv6Network('6000::/3'), + IPv6Network('8000::/3'), IPv6Network('A000::/3'), + IPv6Network('C000::/3'), IPv6Network('E000::/4'), + IPv6Network('F000::/5'), IPv6Network('F800::/6'), + IPv6Network('FE00::/9'), + ] + + _sitelocal_network = IPv6Network('fec0::/10') + + +IPv6Address._constants = _IPv6Constants diff --git a/venv/Lib/site-packages/pip/_vendor/lockfile/__init__.py b/venv/Lib/site-packages/pip/_vendor/lockfile/__init__.py new file mode 100644 index 0000000..a6f44a5 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/lockfile/__init__.py @@ -0,0 +1,347 @@ +# -*- coding: utf-8 -*- + +""" +lockfile.py - Platform-independent advisory file locks. + +Requires Python 2.5 unless you apply 2.4.diff +Locking is done on a per-thread basis instead of a per-process basis. + +Usage: + +>>> lock = LockFile('somefile') +>>> try: +... lock.acquire() +... except AlreadyLocked: +... print 'somefile', 'is locked already.' +... except LockFailed: +... print 'somefile', 'can\\'t be locked.' +... else: +... print 'got lock' +got lock +>>> print lock.is_locked() +True +>>> lock.release() + +>>> lock = LockFile('somefile') +>>> print lock.is_locked() +False +>>> with lock: +... print lock.is_locked() +True +>>> print lock.is_locked() +False + +>>> lock = LockFile('somefile') +>>> # It is okay to lock twice from the same thread... +>>> with lock: +... lock.acquire() +... +>>> # Though no counter is kept, so you can't unlock multiple times... +>>> print lock.is_locked() +False + +Exceptions: + + Error - base class for other exceptions + LockError - base class for all locking exceptions + AlreadyLocked - Another thread or process already holds the lock + LockFailed - Lock failed for some other reason + UnlockError - base class for all unlocking exceptions + AlreadyUnlocked - File was not locked. + NotMyLock - File was locked but not by the current thread/process +""" + +from __future__ import absolute_import + +import functools +import os +import socket +import threading +import warnings + +# Work with PEP8 and non-PEP8 versions of threading module. +if not hasattr(threading, "current_thread"): + threading.current_thread = threading.currentThread +if not hasattr(threading.Thread, "get_name"): + threading.Thread.get_name = threading.Thread.getName + +__all__ = ['Error', 'LockError', 'LockTimeout', 'AlreadyLocked', + 'LockFailed', 'UnlockError', 'NotLocked', 'NotMyLock', + 'LinkFileLock', 'MkdirFileLock', 'SQLiteFileLock', + 'LockBase', 'locked'] + + +class Error(Exception): + """ + Base class for other exceptions. + + >>> try: + ... raise Error + ... except Exception: + ... pass + """ + pass + + +class LockError(Error): + """ + Base class for error arising from attempts to acquire the lock. + + >>> try: + ... raise LockError + ... except Error: + ... pass + """ + pass + + +class LockTimeout(LockError): + """Raised when lock creation fails within a user-defined period of time. + + >>> try: + ... raise LockTimeout + ... except LockError: + ... pass + """ + pass + + +class AlreadyLocked(LockError): + """Some other thread/process is locking the file. + + >>> try: + ... raise AlreadyLocked + ... except LockError: + ... pass + """ + pass + + +class LockFailed(LockError): + """Lock file creation failed for some other reason. + + >>> try: + ... raise LockFailed + ... except LockError: + ... pass + """ + pass + + +class UnlockError(Error): + """ + Base class for errors arising from attempts to release the lock. + + >>> try: + ... raise UnlockError + ... except Error: + ... pass + """ + pass + + +class NotLocked(UnlockError): + """Raised when an attempt is made to unlock an unlocked file. + + >>> try: + ... raise NotLocked + ... except UnlockError: + ... pass + """ + pass + + +class NotMyLock(UnlockError): + """Raised when an attempt is made to unlock a file someone else locked. + + >>> try: + ... raise NotMyLock + ... except UnlockError: + ... pass + """ + pass + + +class _SharedBase(object): + def __init__(self, path): + self.path = path + + def acquire(self, timeout=None): + """ + Acquire the lock. + + * If timeout is omitted (or None), wait forever trying to lock the + file. + + * If timeout > 0, try to acquire the lock for that many seconds. If + the lock period expires and the file is still locked, raise + LockTimeout. + + * If timeout <= 0, raise AlreadyLocked immediately if the file is + already locked. + """ + raise NotImplemented("implement in subclass") + + def release(self): + """ + Release the lock. + + If the file is not locked, raise NotLocked. + """ + raise NotImplemented("implement in subclass") + + def __enter__(self): + """ + Context manager support. + """ + self.acquire() + return self + + def __exit__(self, *_exc): + """ + Context manager support. + """ + self.release() + + def __repr__(self): + return "<%s: %r>" % (self.__class__.__name__, self.path) + + +class LockBase(_SharedBase): + """Base class for platform-specific lock classes.""" + def __init__(self, path, threaded=True, timeout=None): + """ + >>> lock = LockBase('somefile') + >>> lock = LockBase('somefile', threaded=False) + """ + super(LockBase, self).__init__(path) + self.lock_file = os.path.abspath(path) + ".lock" + self.hostname = socket.gethostname() + self.pid = os.getpid() + if threaded: + t = threading.current_thread() + # Thread objects in Python 2.4 and earlier do not have ident + # attrs. Worm around that. + ident = getattr(t, "ident", hash(t)) + self.tname = "-%x" % (ident & 0xffffffff) + else: + self.tname = "" + dirname = os.path.dirname(self.lock_file) + + # unique name is mostly about the current process, but must + # also contain the path -- otherwise, two adjacent locked + # files conflict (one file gets locked, creating lock-file and + # unique file, the other one gets locked, creating lock-file + # and overwriting the already existing lock-file, then one + # gets unlocked, deleting both lock-file and unique file, + # finally the last lock errors out upon releasing. + self.unique_name = os.path.join(dirname, + "%s%s.%s%s" % (self.hostname, + self.tname, + self.pid, + hash(self.path))) + self.timeout = timeout + + def is_locked(self): + """ + Tell whether or not the file is locked. + """ + raise NotImplemented("implement in subclass") + + def i_am_locking(self): + """ + Return True if this object is locking the file. + """ + raise NotImplemented("implement in subclass") + + def break_lock(self): + """ + Remove a lock. Useful if a locking thread failed to unlock. + """ + raise NotImplemented("implement in subclass") + + def __repr__(self): + return "<%s: %r -- %r>" % (self.__class__.__name__, self.unique_name, + self.path) + + +def _fl_helper(cls, mod, *args, **kwds): + warnings.warn("Import from %s module instead of lockfile package" % mod, + DeprecationWarning, stacklevel=2) + # This is a bit funky, but it's only for awhile. The way the unit tests + # are constructed this function winds up as an unbound method, so it + # actually takes three args, not two. We want to toss out self. + if not isinstance(args[0], str): + # We are testing, avoid the first arg + args = args[1:] + if len(args) == 1 and not kwds: + kwds["threaded"] = True + return cls(*args, **kwds) + + +def LinkFileLock(*args, **kwds): + """Factory function provided for backwards compatibility. + + Do not use in new code. Instead, import LinkLockFile from the + lockfile.linklockfile module. + """ + from . import linklockfile + return _fl_helper(linklockfile.LinkLockFile, "lockfile.linklockfile", + *args, **kwds) + + +def MkdirFileLock(*args, **kwds): + """Factory function provided for backwards compatibility. + + Do not use in new code. Instead, import MkdirLockFile from the + lockfile.mkdirlockfile module. + """ + from . import mkdirlockfile + return _fl_helper(mkdirlockfile.MkdirLockFile, "lockfile.mkdirlockfile", + *args, **kwds) + + +def SQLiteFileLock(*args, **kwds): + """Factory function provided for backwards compatibility. + + Do not use in new code. Instead, import SQLiteLockFile from the + lockfile.mkdirlockfile module. + """ + from . import sqlitelockfile + return _fl_helper(sqlitelockfile.SQLiteLockFile, "lockfile.sqlitelockfile", + *args, **kwds) + + +def locked(path, timeout=None): + """Decorator which enables locks for decorated function. + + Arguments: + - path: path for lockfile. + - timeout (optional): Timeout for acquiring lock. + + Usage: + @locked('/var/run/myname', timeout=0) + def myname(...): + ... + """ + def decor(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + lock = FileLock(path, timeout=timeout) + lock.acquire() + try: + return func(*args, **kwargs) + finally: + lock.release() + return wrapper + return decor + + +if hasattr(os, "link"): + from . import linklockfile as _llf + LockFile = _llf.LinkLockFile +else: + from . import mkdirlockfile as _mlf + LockFile = _mlf.MkdirLockFile + +FileLock = LockFile diff --git a/venv/Lib/site-packages/pip/_vendor/lockfile/linklockfile.py b/venv/Lib/site-packages/pip/_vendor/lockfile/linklockfile.py new file mode 100644 index 0000000..2ca9be0 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/lockfile/linklockfile.py @@ -0,0 +1,73 @@ +from __future__ import absolute_import + +import time +import os + +from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout, + AlreadyLocked) + + +class LinkLockFile(LockBase): + """Lock access to a file using atomic property of link(2). + + >>> lock = LinkLockFile('somefile') + >>> lock = LinkLockFile('somefile', threaded=False) + """ + + def acquire(self, timeout=None): + try: + open(self.unique_name, "wb").close() + except IOError: + raise LockFailed("failed to create %s" % self.unique_name) + + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + while True: + # Try and create a hard link to it. + try: + os.link(self.unique_name, self.lock_file) + except OSError: + # Link creation failed. Maybe we've double-locked? + nlinks = os.stat(self.unique_name).st_nlink + if nlinks == 2: + # The original link plus the one I created == 2. We're + # good to go. + return + else: + # Otherwise the lock creation failed. + if timeout is not None and time.time() > end_time: + os.unlink(self.unique_name) + if timeout > 0: + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + raise AlreadyLocked("%s is already locked" % + self.path) + time.sleep(timeout is not None and timeout / 10 or 0.1) + else: + # Link creation succeeded. We're good to go. + return + + def release(self): + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + elif not os.path.exists(self.unique_name): + raise NotMyLock("%s is locked, but not by me" % self.path) + os.unlink(self.unique_name) + os.unlink(self.lock_file) + + def is_locked(self): + return os.path.exists(self.lock_file) + + def i_am_locking(self): + return (self.is_locked() and + os.path.exists(self.unique_name) and + os.stat(self.unique_name).st_nlink == 2) + + def break_lock(self): + if os.path.exists(self.lock_file): + os.unlink(self.lock_file) diff --git a/venv/Lib/site-packages/pip/_vendor/lockfile/mkdirlockfile.py b/venv/Lib/site-packages/pip/_vendor/lockfile/mkdirlockfile.py new file mode 100644 index 0000000..05a8c96 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/lockfile/mkdirlockfile.py @@ -0,0 +1,84 @@ +from __future__ import absolute_import, division + +import time +import os +import sys +import errno + +from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout, + AlreadyLocked) + + +class MkdirLockFile(LockBase): + """Lock file by creating a directory.""" + def __init__(self, path, threaded=True, timeout=None): + """ + >>> lock = MkdirLockFile('somefile') + >>> lock = MkdirLockFile('somefile', threaded=False) + """ + LockBase.__init__(self, path, threaded, timeout) + # Lock file itself is a directory. Place the unique file name into + # it. + self.unique_name = os.path.join(self.lock_file, + "%s.%s%s" % (self.hostname, + self.tname, + self.pid)) + + def acquire(self, timeout=None): + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + if timeout is None: + wait = 0.1 + else: + wait = max(0, timeout / 10) + + while True: + try: + os.mkdir(self.lock_file) + except OSError: + err = sys.exc_info()[1] + if err.errno == errno.EEXIST: + # Already locked. + if os.path.exists(self.unique_name): + # Already locked by me. + return + if timeout is not None and time.time() > end_time: + if timeout > 0: + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + # Someone else has the lock. + raise AlreadyLocked("%s is already locked" % + self.path) + time.sleep(wait) + else: + # Couldn't create the lock for some other reason + raise LockFailed("failed to create %s" % self.lock_file) + else: + open(self.unique_name, "wb").close() + return + + def release(self): + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + elif not os.path.exists(self.unique_name): + raise NotMyLock("%s is locked, but not by me" % self.path) + os.unlink(self.unique_name) + os.rmdir(self.lock_file) + + def is_locked(self): + return os.path.exists(self.lock_file) + + def i_am_locking(self): + return (self.is_locked() and + os.path.exists(self.unique_name)) + + def break_lock(self): + if os.path.exists(self.lock_file): + for name in os.listdir(self.lock_file): + os.unlink(os.path.join(self.lock_file, name)) + os.rmdir(self.lock_file) diff --git a/venv/Lib/site-packages/pip/_vendor/lockfile/pidlockfile.py b/venv/Lib/site-packages/pip/_vendor/lockfile/pidlockfile.py new file mode 100644 index 0000000..069e85b --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/lockfile/pidlockfile.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- + +# pidlockfile.py +# +# Copyright © 2008–2009 Ben Finney <ben+python@benfinney.id.au> +# +# This is free software: you may copy, modify, and/or distribute this work +# under the terms of the Python Software Foundation License, version 2 or +# later as published by the Python Software Foundation. +# No warranty expressed or implied. See the file LICENSE.PSF-2 for details. + +""" Lockfile behaviour implemented via Unix PID files. + """ + +from __future__ import absolute_import + +import errno +import os +import time + +from . import (LockBase, AlreadyLocked, LockFailed, NotLocked, NotMyLock, + LockTimeout) + + +class PIDLockFile(LockBase): + """ Lockfile implemented as a Unix PID file. + + The lock file is a normal file named by the attribute `path`. + A lock's PID file contains a single line of text, containing + the process ID (PID) of the process that acquired the lock. + + >>> lock = PIDLockFile('somefile') + >>> lock = PIDLockFile('somefile') + """ + + def __init__(self, path, threaded=False, timeout=None): + # pid lockfiles don't support threaded operation, so always force + # False as the threaded arg. + LockBase.__init__(self, path, False, timeout) + self.unique_name = self.path + + def read_pid(self): + """ Get the PID from the lock file. + """ + return read_pid_from_pidfile(self.path) + + def is_locked(self): + """ Test if the lock is currently held. + + The lock is held if the PID file for this lock exists. + + """ + return os.path.exists(self.path) + + def i_am_locking(self): + """ Test if the lock is held by the current process. + + Returns ``True`` if the current process ID matches the + number stored in the PID file. + """ + return self.is_locked() and os.getpid() == self.read_pid() + + def acquire(self, timeout=None): + """ Acquire the lock. + + Creates the PID file for this lock, or raises an error if + the lock could not be acquired. + """ + + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + while True: + try: + write_pid_to_pidfile(self.path) + except OSError as exc: + if exc.errno == errno.EEXIST: + # The lock creation failed. Maybe sleep a bit. + if time.time() > end_time: + if timeout is not None and timeout > 0: + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + raise AlreadyLocked("%s is already locked" % + self.path) + time.sleep(timeout is not None and timeout / 10 or 0.1) + else: + raise LockFailed("failed to create %s" % self.path) + else: + return + + def release(self): + """ Release the lock. + + Removes the PID file to release the lock, or raises an + error if the current process does not hold the lock. + + """ + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + if not self.i_am_locking(): + raise NotMyLock("%s is locked, but not by me" % self.path) + remove_existing_pidfile(self.path) + + def break_lock(self): + """ Break an existing lock. + + Removes the PID file if it already exists, otherwise does + nothing. + + """ + remove_existing_pidfile(self.path) + + +def read_pid_from_pidfile(pidfile_path): + """ Read the PID recorded in the named PID file. + + Read and return the numeric PID recorded as text in the named + PID file. If the PID file cannot be read, or if the content is + not a valid PID, return ``None``. + + """ + pid = None + try: + pidfile = open(pidfile_path, 'r') + except IOError: + pass + else: + # According to the FHS 2.3 section on PID files in /var/run: + # + # The file must consist of the process identifier in + # ASCII-encoded decimal, followed by a newline character. + # + # Programs that read PID files should be somewhat flexible + # in what they accept; i.e., they should ignore extra + # whitespace, leading zeroes, absence of the trailing + # newline, or additional lines in the PID file. + + line = pidfile.readline().strip() + try: + pid = int(line) + except ValueError: + pass + pidfile.close() + + return pid + + +def write_pid_to_pidfile(pidfile_path): + """ Write the PID in the named PID file. + + Get the numeric process ID (“PID”) of the current process + and write it to the named file as a line of text. + + """ + open_flags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY) + open_mode = 0o644 + pidfile_fd = os.open(pidfile_path, open_flags, open_mode) + pidfile = os.fdopen(pidfile_fd, 'w') + + # According to the FHS 2.3 section on PID files in /var/run: + # + # The file must consist of the process identifier in + # ASCII-encoded decimal, followed by a newline character. For + # example, if crond was process number 25, /var/run/crond.pid + # would contain three characters: two, five, and newline. + + pid = os.getpid() + pidfile.write("%s\n" % pid) + pidfile.close() + + +def remove_existing_pidfile(pidfile_path): + """ Remove the named PID file if it exists. + + Removing a PID file that doesn't already exist puts us in the + desired state, so we ignore the condition if the file does not + exist. + + """ + try: + os.remove(pidfile_path) + except OSError as exc: + if exc.errno == errno.ENOENT: + pass + else: + raise diff --git a/venv/Lib/site-packages/pip/_vendor/lockfile/sqlitelockfile.py b/venv/Lib/site-packages/pip/_vendor/lockfile/sqlitelockfile.py new file mode 100644 index 0000000..f997e24 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/lockfile/sqlitelockfile.py @@ -0,0 +1,156 @@ +from __future__ import absolute_import, division + +import time +import os + +try: + unicode +except NameError: + unicode = str + +from . import LockBase, NotLocked, NotMyLock, LockTimeout, AlreadyLocked + + +class SQLiteLockFile(LockBase): + "Demonstrate SQL-based locking." + + testdb = None + + def __init__(self, path, threaded=True, timeout=None): + """ + >>> lock = SQLiteLockFile('somefile') + >>> lock = SQLiteLockFile('somefile', threaded=False) + """ + LockBase.__init__(self, path, threaded, timeout) + self.lock_file = unicode(self.lock_file) + self.unique_name = unicode(self.unique_name) + + if SQLiteLockFile.testdb is None: + import tempfile + _fd, testdb = tempfile.mkstemp() + os.close(_fd) + os.unlink(testdb) + del _fd, tempfile + SQLiteLockFile.testdb = testdb + + import sqlite3 + self.connection = sqlite3.connect(SQLiteLockFile.testdb) + + c = self.connection.cursor() + try: + c.execute("create table locks" + "(" + " lock_file varchar(32)," + " unique_name varchar(32)" + ")") + except sqlite3.OperationalError: + pass + else: + self.connection.commit() + import atexit + atexit.register(os.unlink, SQLiteLockFile.testdb) + + def acquire(self, timeout=None): + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + if timeout is None: + wait = 0.1 + elif timeout <= 0: + wait = 0 + else: + wait = timeout / 10 + + cursor = self.connection.cursor() + + while True: + if not self.is_locked(): + # Not locked. Try to lock it. + cursor.execute("insert into locks" + " (lock_file, unique_name)" + " values" + " (?, ?)", + (self.lock_file, self.unique_name)) + self.connection.commit() + + # Check to see if we are the only lock holder. + cursor.execute("select * from locks" + " where unique_name = ?", + (self.unique_name,)) + rows = cursor.fetchall() + if len(rows) > 1: + # Nope. Someone else got there. Remove our lock. + cursor.execute("delete from locks" + " where unique_name = ?", + (self.unique_name,)) + self.connection.commit() + else: + # Yup. We're done, so go home. + return + else: + # Check to see if we are the only lock holder. + cursor.execute("select * from locks" + " where unique_name = ?", + (self.unique_name,)) + rows = cursor.fetchall() + if len(rows) == 1: + # We're the locker, so go home. + return + + # Maybe we should wait a bit longer. + if timeout is not None and time.time() > end_time: + if timeout > 0: + # No more waiting. + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + # Someone else has the lock and we are impatient.. + raise AlreadyLocked("%s is already locked" % self.path) + + # Well, okay. We'll give it a bit longer. + time.sleep(wait) + + def release(self): + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + if not self.i_am_locking(): + raise NotMyLock("%s is locked, but not by me (by %s)" % + (self.unique_name, self._who_is_locking())) + cursor = self.connection.cursor() + cursor.execute("delete from locks" + " where unique_name = ?", + (self.unique_name,)) + self.connection.commit() + + def _who_is_locking(self): + cursor = self.connection.cursor() + cursor.execute("select unique_name from locks" + " where lock_file = ?", + (self.lock_file,)) + return cursor.fetchone()[0] + + def is_locked(self): + cursor = self.connection.cursor() + cursor.execute("select * from locks" + " where lock_file = ?", + (self.lock_file,)) + rows = cursor.fetchall() + return not not rows + + def i_am_locking(self): + cursor = self.connection.cursor() + cursor.execute("select * from locks" + " where lock_file = ?" + " and unique_name = ?", + (self.lock_file, self.unique_name)) + return not not cursor.fetchall() + + def break_lock(self): + cursor = self.connection.cursor() + cursor.execute("delete from locks" + " where lock_file = ?", + (self.lock_file,)) + self.connection.commit() diff --git a/venv/Lib/site-packages/pip/_vendor/lockfile/symlinklockfile.py b/venv/Lib/site-packages/pip/_vendor/lockfile/symlinklockfile.py new file mode 100644 index 0000000..23b41f5 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/lockfile/symlinklockfile.py @@ -0,0 +1,70 @@ +from __future__ import absolute_import + +import os +import time + +from . import (LockBase, NotLocked, NotMyLock, LockTimeout, + AlreadyLocked) + + +class SymlinkLockFile(LockBase): + """Lock access to a file using symlink(2).""" + + def __init__(self, path, threaded=True, timeout=None): + # super(SymlinkLockFile).__init(...) + LockBase.__init__(self, path, threaded, timeout) + # split it back! + self.unique_name = os.path.split(self.unique_name)[1] + + def acquire(self, timeout=None): + # Hopefully unnecessary for symlink. + # try: + # open(self.unique_name, "wb").close() + # except IOError: + # raise LockFailed("failed to create %s" % self.unique_name) + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + while True: + # Try and create a symbolic link to it. + try: + os.symlink(self.unique_name, self.lock_file) + except OSError: + # Link creation failed. Maybe we've double-locked? + if self.i_am_locking(): + # Linked to out unique name. Proceed. + return + else: + # Otherwise the lock creation failed. + if timeout is not None and time.time() > end_time: + if timeout > 0: + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + raise AlreadyLocked("%s is already locked" % + self.path) + time.sleep(timeout / 10 if timeout is not None else 0.1) + else: + # Link creation succeeded. We're good to go. + return + + def release(self): + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + elif not self.i_am_locking(): + raise NotMyLock("%s is locked, but not by me" % self.path) + os.unlink(self.lock_file) + + def is_locked(self): + return os.path.islink(self.lock_file) + + def i_am_locking(self): + return (os.path.islink(self.lock_file) + and os.readlink(self.lock_file) == self.unique_name) + + def break_lock(self): + if os.path.islink(self.lock_file): # exists && link + os.unlink(self.lock_file) diff --git a/venv/Lib/site-packages/pip/_vendor/msgpack/__init__.py b/venv/Lib/site-packages/pip/_vendor/msgpack/__init__.py new file mode 100644 index 0000000..2afca5a --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/msgpack/__init__.py @@ -0,0 +1,66 @@ +# coding: utf-8 +from pip._vendor.msgpack._version import version +from pip._vendor.msgpack.exceptions import * + +from collections import namedtuple + + +class ExtType(namedtuple('ExtType', 'code data')): + """ExtType represents ext type in msgpack.""" + def __new__(cls, code, data): + if not isinstance(code, int): + raise TypeError("code must be int") + if not isinstance(data, bytes): + raise TypeError("data must be bytes") + if not 0 <= code <= 127: + raise ValueError("code must be 0~127") + return super(ExtType, cls).__new__(cls, code, data) + + +import os +if os.environ.get('MSGPACK_PUREPYTHON'): + from pip._vendor.msgpack.fallback import Packer, unpackb, Unpacker +else: + try: + from pip._vendor.msgpack._packer import Packer + from pip._vendor.msgpack._unpacker import unpackb, Unpacker + except ImportError: + from pip._vendor.msgpack.fallback import Packer, unpackb, Unpacker + + +def pack(o, stream, **kwargs): + """ + Pack object `o` and write it to `stream` + + See :class:`Packer` for options. + """ + packer = Packer(**kwargs) + stream.write(packer.pack(o)) + + +def packb(o, **kwargs): + """ + Pack object `o` and return packed bytes + + See :class:`Packer` for options. + """ + return Packer(**kwargs).pack(o) + + +def unpack(stream, **kwargs): + """ + Unpack an object from `stream`. + + Raises `ExtraData` when `stream` contains extra bytes. + See :class:`Unpacker` for options. + """ + data = stream.read() + return unpackb(data, **kwargs) + + +# alias for compatibility to simplejson/marshal/pickle. +load = unpack +loads = unpackb + +dump = pack +dumps = packb diff --git a/venv/Lib/site-packages/pip/_vendor/msgpack/_version.py b/venv/Lib/site-packages/pip/_vendor/msgpack/_version.py new file mode 100644 index 0000000..d28f0de --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/msgpack/_version.py @@ -0,0 +1 @@ +version = (0, 5, 6) diff --git a/venv/Lib/site-packages/pip/_vendor/msgpack/exceptions.py b/venv/Lib/site-packages/pip/_vendor/msgpack/exceptions.py new file mode 100644 index 0000000..9766881 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/msgpack/exceptions.py @@ -0,0 +1,41 @@ +class UnpackException(Exception): + """Deprecated. Use Exception instead to catch all exception during unpacking.""" + + +class BufferFull(UnpackException): + pass + + +class OutOfData(UnpackException): + pass + + +class UnpackValueError(UnpackException, ValueError): + """Deprecated. Use ValueError instead.""" + + +class ExtraData(UnpackValueError): + def __init__(self, unpacked, extra): + self.unpacked = unpacked + self.extra = extra + + def __str__(self): + return "unpack(b) received extra data." + + +class PackException(Exception): + """Deprecated. Use Exception instead to catch all exception during packing.""" + + +class PackValueError(PackException, ValueError): + """PackValueError is raised when type of input data is supported but it's value is unsupported. + + Deprecated. Use ValueError instead. + """ + + +class PackOverflowError(PackValueError, OverflowError): + """PackOverflowError is raised when integer value is out of range of msgpack support [-2**31, 2**32). + + Deprecated. Use ValueError instead. + """ diff --git a/venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py b/venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py new file mode 100644 index 0000000..9418421 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py @@ -0,0 +1,977 @@ +"""Fallback pure Python implementation of msgpack""" + +import sys +import struct +import warnings + +if sys.version_info[0] == 3: + PY3 = True + int_types = int + Unicode = str + xrange = range + def dict_iteritems(d): + return d.items() +else: + PY3 = False + int_types = (int, long) + Unicode = unicode + def dict_iteritems(d): + return d.iteritems() + + +if hasattr(sys, 'pypy_version_info'): + # cStringIO is slow on PyPy, StringIO is faster. However: PyPy's own + # StringBuilder is fastest. + from __pypy__ import newlist_hint + try: + from __pypy__.builders import BytesBuilder as StringBuilder + except ImportError: + from __pypy__.builders import StringBuilder + USING_STRINGBUILDER = True + class StringIO(object): + def __init__(self, s=b''): + if s: + self.builder = StringBuilder(len(s)) + self.builder.append(s) + else: + self.builder = StringBuilder() + def write(self, s): + if isinstance(s, memoryview): + s = s.tobytes() + elif isinstance(s, bytearray): + s = bytes(s) + self.builder.append(s) + def getvalue(self): + return self.builder.build() +else: + USING_STRINGBUILDER = False + from io import BytesIO as StringIO + newlist_hint = lambda size: [] + + +from pip._vendor.msgpack.exceptions import ( + BufferFull, + OutOfData, + UnpackValueError, + PackValueError, + PackOverflowError, + ExtraData) + +from pip._vendor.msgpack import ExtType + + +EX_SKIP = 0 +EX_CONSTRUCT = 1 +EX_READ_ARRAY_HEADER = 2 +EX_READ_MAP_HEADER = 3 + +TYPE_IMMEDIATE = 0 +TYPE_ARRAY = 1 +TYPE_MAP = 2 +TYPE_RAW = 3 +TYPE_BIN = 4 +TYPE_EXT = 5 + +DEFAULT_RECURSE_LIMIT = 511 + + +def _check_type_strict(obj, t, type=type, tuple=tuple): + if type(t) is tuple: + return type(obj) in t + else: + return type(obj) is t + + +def _get_data_from_buffer(obj): + try: + view = memoryview(obj) + except TypeError: + # try to use legacy buffer protocol if 2.7, otherwise re-raise + if not PY3: + view = memoryview(buffer(obj)) + warnings.warn("using old buffer interface to unpack %s; " + "this leads to unpacking errors if slicing is used and " + "will be removed in a future version" % type(obj), + RuntimeWarning) + else: + raise + if view.itemsize != 1: + raise ValueError("cannot unpack from multi-byte object") + return view + + +def unpack(stream, **kwargs): + warnings.warn( + "Direct calling implementation's unpack() is deprecated, Use msgpack.unpack() or unpackb() instead.", + PendingDeprecationWarning) + data = stream.read() + return unpackb(data, **kwargs) + + +def unpackb(packed, **kwargs): + """ + Unpack an object from `packed`. + + Raises `ExtraData` when `packed` contains extra bytes. + See :class:`Unpacker` for options. + """ + unpacker = Unpacker(None, **kwargs) + unpacker.feed(packed) + try: + ret = unpacker._unpack() + except OutOfData: + raise UnpackValueError("Data is not enough.") + if unpacker._got_extradata(): + raise ExtraData(ret, unpacker._get_extradata()) + return ret + + +class Unpacker(object): + """Streaming unpacker. + + arguments: + + :param file_like: + File-like object having `.read(n)` method. + If specified, unpacker reads serialized data from it and :meth:`feed()` is not usable. + + :param int read_size: + Used as `file_like.read(read_size)`. (default: `min(16*1024, max_buffer_size)`) + + :param bool use_list: + If true, unpack msgpack array to Python list. + Otherwise, unpack to Python tuple. (default: True) + + :param bool raw: + If true, unpack msgpack raw to Python bytes (default). + Otherwise, unpack to Python str (or unicode on Python 2) by decoding + with UTF-8 encoding (recommended). + Currently, the default is true, but it will be changed to false in + near future. So you must specify it explicitly for keeping backward + compatibility. + + *encoding* option which is deprecated overrides this option. + + :param callable object_hook: + When specified, it should be callable. + Unpacker calls it with a dict argument after unpacking msgpack map. + (See also simplejson) + + :param callable object_pairs_hook: + When specified, it should be callable. + Unpacker calls it with a list of key-value pairs after unpacking msgpack map. + (See also simplejson) + + :param str encoding: + Encoding used for decoding msgpack raw. + If it is None (default), msgpack raw is deserialized to Python bytes. + + :param str unicode_errors: + (deprecated) Used for decoding msgpack raw with *encoding*. + (default: `'strict'`) + + :param int max_buffer_size: + Limits size of data waiting unpacked. 0 means system's INT_MAX (default). + Raises `BufferFull` exception when it is insufficient. + You should set this parameter when unpacking data from untrusted source. + + :param int max_str_len: + Limits max length of str. (default: 2**31-1) + + :param int max_bin_len: + Limits max length of bin. (default: 2**31-1) + + :param int max_array_len: + Limits max length of array. (default: 2**31-1) + + :param int max_map_len: + Limits max length of map. (default: 2**31-1) + + + example of streaming deserialize from file-like object:: + + unpacker = Unpacker(file_like, raw=False) + for o in unpacker: + process(o) + + example of streaming deserialize from socket:: + + unpacker = Unpacker(raw=False) + while True: + buf = sock.recv(1024**2) + if not buf: + break + unpacker.feed(buf) + for o in unpacker: + process(o) + """ + + def __init__(self, file_like=None, read_size=0, use_list=True, raw=True, + object_hook=None, object_pairs_hook=None, list_hook=None, + encoding=None, unicode_errors=None, max_buffer_size=0, + ext_hook=ExtType, + max_str_len=2147483647, # 2**32-1 + max_bin_len=2147483647, + max_array_len=2147483647, + max_map_len=2147483647, + max_ext_len=2147483647): + + if encoding is not None: + warnings.warn( + "encoding is deprecated, Use raw=False instead.", + PendingDeprecationWarning) + + if unicode_errors is None: + unicode_errors = 'strict' + + if file_like is None: + self._feeding = True + else: + if not callable(file_like.read): + raise TypeError("`file_like.read` must be callable") + self.file_like = file_like + self._feeding = False + + #: array of bytes fed. + self._buffer = bytearray() + # Some very old pythons don't support `struct.unpack_from()` with a + # `bytearray`. So we wrap it in a `buffer()` there. + if sys.version_info < (2, 7, 6): + self._buffer_view = buffer(self._buffer) + else: + self._buffer_view = self._buffer + #: Which position we currently reads + self._buff_i = 0 + + # When Unpacker is used as an iterable, between the calls to next(), + # the buffer is not "consumed" completely, for efficiency sake. + # Instead, it is done sloppily. To make sure we raise BufferFull at + # the correct moments, we have to keep track of how sloppy we were. + # Furthermore, when the buffer is incomplete (that is: in the case + # we raise an OutOfData) we need to rollback the buffer to the correct + # state, which _buf_checkpoint records. + self._buf_checkpoint = 0 + + self._max_buffer_size = max_buffer_size or 2**31-1 + if read_size > self._max_buffer_size: + raise ValueError("read_size must be smaller than max_buffer_size") + self._read_size = read_size or min(self._max_buffer_size, 16*1024) + self._raw = bool(raw) + self._encoding = encoding + self._unicode_errors = unicode_errors + self._use_list = use_list + self._list_hook = list_hook + self._object_hook = object_hook + self._object_pairs_hook = object_pairs_hook + self._ext_hook = ext_hook + self._max_str_len = max_str_len + self._max_bin_len = max_bin_len + self._max_array_len = max_array_len + self._max_map_len = max_map_len + self._max_ext_len = max_ext_len + self._stream_offset = 0 + + if list_hook is not None and not callable(list_hook): + raise TypeError('`list_hook` is not callable') + if object_hook is not None and not callable(object_hook): + raise TypeError('`object_hook` is not callable') + if object_pairs_hook is not None and not callable(object_pairs_hook): + raise TypeError('`object_pairs_hook` is not callable') + if object_hook is not None and object_pairs_hook is not None: + raise TypeError("object_pairs_hook and object_hook are mutually " + "exclusive") + if not callable(ext_hook): + raise TypeError("`ext_hook` is not callable") + + def feed(self, next_bytes): + assert self._feeding + view = _get_data_from_buffer(next_bytes) + if (len(self._buffer) - self._buff_i + len(view) > self._max_buffer_size): + raise BufferFull + + # Strip buffer before checkpoint before reading file. + if self._buf_checkpoint > 0: + del self._buffer[:self._buf_checkpoint] + self._buff_i -= self._buf_checkpoint + self._buf_checkpoint = 0 + + self._buffer += view + + def _consume(self): + """ Gets rid of the used parts of the buffer. """ + self._stream_offset += self._buff_i - self._buf_checkpoint + self._buf_checkpoint = self._buff_i + + def _got_extradata(self): + return self._buff_i < len(self._buffer) + + def _get_extradata(self): + return self._buffer[self._buff_i:] + + def read_bytes(self, n): + return self._read(n) + + def _read(self, n): + # (int) -> bytearray + self._reserve(n) + i = self._buff_i + self._buff_i = i+n + return self._buffer[i:i+n] + + def _reserve(self, n): + remain_bytes = len(self._buffer) - self._buff_i - n + + # Fast path: buffer has n bytes already + if remain_bytes >= 0: + return + + if self._feeding: + self._buff_i = self._buf_checkpoint + raise OutOfData + + # Strip buffer before checkpoint before reading file. + if self._buf_checkpoint > 0: + del self._buffer[:self._buf_checkpoint] + self._buff_i -= self._buf_checkpoint + self._buf_checkpoint = 0 + + # Read from file + remain_bytes = -remain_bytes + while remain_bytes > 0: + to_read_bytes = max(self._read_size, remain_bytes) + read_data = self.file_like.read(to_read_bytes) + if not read_data: + break + assert isinstance(read_data, bytes) + self._buffer += read_data + remain_bytes -= len(read_data) + + if len(self._buffer) < n + self._buff_i: + self._buff_i = 0 # rollback + raise OutOfData + + def _read_header(self, execute=EX_CONSTRUCT): + typ = TYPE_IMMEDIATE + n = 0 + obj = None + self._reserve(1) + b = self._buffer[self._buff_i] + self._buff_i += 1 + if b & 0b10000000 == 0: + obj = b + elif b & 0b11100000 == 0b11100000: + obj = -1 - (b ^ 0xff) + elif b & 0b11100000 == 0b10100000: + n = b & 0b00011111 + typ = TYPE_RAW + if n > self._max_str_len: + raise UnpackValueError("%s exceeds max_str_len(%s)", n, self._max_str_len) + obj = self._read(n) + elif b & 0b11110000 == 0b10010000: + n = b & 0b00001111 + typ = TYPE_ARRAY + if n > self._max_array_len: + raise UnpackValueError("%s exceeds max_array_len(%s)", n, self._max_array_len) + elif b & 0b11110000 == 0b10000000: + n = b & 0b00001111 + typ = TYPE_MAP + if n > self._max_map_len: + raise UnpackValueError("%s exceeds max_map_len(%s)", n, self._max_map_len) + elif b == 0xc0: + obj = None + elif b == 0xc2: + obj = False + elif b == 0xc3: + obj = True + elif b == 0xc4: + typ = TYPE_BIN + self._reserve(1) + n = self._buffer[self._buff_i] + self._buff_i += 1 + if n > self._max_bin_len: + raise UnpackValueError("%s exceeds max_bin_len(%s)" % (n, self._max_bin_len)) + obj = self._read(n) + elif b == 0xc5: + typ = TYPE_BIN + self._reserve(2) + n = struct.unpack_from(">H", self._buffer_view, self._buff_i)[0] + self._buff_i += 2 + if n > self._max_bin_len: + raise UnpackValueError("%s exceeds max_bin_len(%s)" % (n, self._max_bin_len)) + obj = self._read(n) + elif b == 0xc6: + typ = TYPE_BIN + self._reserve(4) + n = struct.unpack_from(">I", self._buffer_view, self._buff_i)[0] + self._buff_i += 4 + if n > self._max_bin_len: + raise UnpackValueError("%s exceeds max_bin_len(%s)" % (n, self._max_bin_len)) + obj = self._read(n) + elif b == 0xc7: # ext 8 + typ = TYPE_EXT + self._reserve(2) + L, n = struct.unpack_from('Bb', self._buffer_view, self._buff_i) + self._buff_i += 2 + if L > self._max_ext_len: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (L, self._max_ext_len)) + obj = self._read(L) + elif b == 0xc8: # ext 16 + typ = TYPE_EXT + self._reserve(3) + L, n = struct.unpack_from('>Hb', self._buffer_view, self._buff_i) + self._buff_i += 3 + if L > self._max_ext_len: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (L, self._max_ext_len)) + obj = self._read(L) + elif b == 0xc9: # ext 32 + typ = TYPE_EXT + self._reserve(5) + L, n = struct.unpack_from('>Ib', self._buffer_view, self._buff_i) + self._buff_i += 5 + if L > self._max_ext_len: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (L, self._max_ext_len)) + obj = self._read(L) + elif b == 0xca: + self._reserve(4) + obj = struct.unpack_from(">f", self._buffer_view, self._buff_i)[0] + self._buff_i += 4 + elif b == 0xcb: + self._reserve(8) + obj = struct.unpack_from(">d", self._buffer_view, self._buff_i)[0] + self._buff_i += 8 + elif b == 0xcc: + self._reserve(1) + obj = self._buffer[self._buff_i] + self._buff_i += 1 + elif b == 0xcd: + self._reserve(2) + obj = struct.unpack_from(">H", self._buffer_view, self._buff_i)[0] + self._buff_i += 2 + elif b == 0xce: + self._reserve(4) + obj = struct.unpack_from(">I", self._buffer_view, self._buff_i)[0] + self._buff_i += 4 + elif b == 0xcf: + self._reserve(8) + obj = struct.unpack_from(">Q", self._buffer_view, self._buff_i)[0] + self._buff_i += 8 + elif b == 0xd0: + self._reserve(1) + obj = struct.unpack_from("b", self._buffer_view, self._buff_i)[0] + self._buff_i += 1 + elif b == 0xd1: + self._reserve(2) + obj = struct.unpack_from(">h", self._buffer_view, self._buff_i)[0] + self._buff_i += 2 + elif b == 0xd2: + self._reserve(4) + obj = struct.unpack_from(">i", self._buffer_view, self._buff_i)[0] + self._buff_i += 4 + elif b == 0xd3: + self._reserve(8) + obj = struct.unpack_from(">q", self._buffer_view, self._buff_i)[0] + self._buff_i += 8 + elif b == 0xd4: # fixext 1 + typ = TYPE_EXT + if self._max_ext_len < 1: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (1, self._max_ext_len)) + self._reserve(2) + n, obj = struct.unpack_from("b1s", self._buffer_view, self._buff_i) + self._buff_i += 2 + elif b == 0xd5: # fixext 2 + typ = TYPE_EXT + if self._max_ext_len < 2: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (2, self._max_ext_len)) + self._reserve(3) + n, obj = struct.unpack_from("b2s", self._buffer_view, self._buff_i) + self._buff_i += 3 + elif b == 0xd6: # fixext 4 + typ = TYPE_EXT + if self._max_ext_len < 4: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (4, self._max_ext_len)) + self._reserve(5) + n, obj = struct.unpack_from("b4s", self._buffer_view, self._buff_i) + self._buff_i += 5 + elif b == 0xd7: # fixext 8 + typ = TYPE_EXT + if self._max_ext_len < 8: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (8, self._max_ext_len)) + self._reserve(9) + n, obj = struct.unpack_from("b8s", self._buffer_view, self._buff_i) + self._buff_i += 9 + elif b == 0xd8: # fixext 16 + typ = TYPE_EXT + if self._max_ext_len < 16: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (16, self._max_ext_len)) + self._reserve(17) + n, obj = struct.unpack_from("b16s", self._buffer_view, self._buff_i) + self._buff_i += 17 + elif b == 0xd9: + typ = TYPE_RAW + self._reserve(1) + n = self._buffer[self._buff_i] + self._buff_i += 1 + if n > self._max_str_len: + raise UnpackValueError("%s exceeds max_str_len(%s)", n, self._max_str_len) + obj = self._read(n) + elif b == 0xda: + typ = TYPE_RAW + self._reserve(2) + n, = struct.unpack_from(">H", self._buffer_view, self._buff_i) + self._buff_i += 2 + if n > self._max_str_len: + raise UnpackValueError("%s exceeds max_str_len(%s)", n, self._max_str_len) + obj = self._read(n) + elif b == 0xdb: + typ = TYPE_RAW + self._reserve(4) + n, = struct.unpack_from(">I", self._buffer_view, self._buff_i) + self._buff_i += 4 + if n > self._max_str_len: + raise UnpackValueError("%s exceeds max_str_len(%s)", n, self._max_str_len) + obj = self._read(n) + elif b == 0xdc: + typ = TYPE_ARRAY + self._reserve(2) + n, = struct.unpack_from(">H", self._buffer_view, self._buff_i) + self._buff_i += 2 + if n > self._max_array_len: + raise UnpackValueError("%s exceeds max_array_len(%s)", n, self._max_array_len) + elif b == 0xdd: + typ = TYPE_ARRAY + self._reserve(4) + n, = struct.unpack_from(">I", self._buffer_view, self._buff_i) + self._buff_i += 4 + if n > self._max_array_len: + raise UnpackValueError("%s exceeds max_array_len(%s)", n, self._max_array_len) + elif b == 0xde: + self._reserve(2) + n, = struct.unpack_from(">H", self._buffer_view, self._buff_i) + self._buff_i += 2 + if n > self._max_map_len: + raise UnpackValueError("%s exceeds max_map_len(%s)", n, self._max_map_len) + typ = TYPE_MAP + elif b == 0xdf: + self._reserve(4) + n, = struct.unpack_from(">I", self._buffer_view, self._buff_i) + self._buff_i += 4 + if n > self._max_map_len: + raise UnpackValueError("%s exceeds max_map_len(%s)", n, self._max_map_len) + typ = TYPE_MAP + else: + raise UnpackValueError("Unknown header: 0x%x" % b) + return typ, n, obj + + def _unpack(self, execute=EX_CONSTRUCT): + typ, n, obj = self._read_header(execute) + + if execute == EX_READ_ARRAY_HEADER: + if typ != TYPE_ARRAY: + raise UnpackValueError("Expected array") + return n + if execute == EX_READ_MAP_HEADER: + if typ != TYPE_MAP: + raise UnpackValueError("Expected map") + return n + # TODO should we eliminate the recursion? + if typ == TYPE_ARRAY: + if execute == EX_SKIP: + for i in xrange(n): + # TODO check whether we need to call `list_hook` + self._unpack(EX_SKIP) + return + ret = newlist_hint(n) + for i in xrange(n): + ret.append(self._unpack(EX_CONSTRUCT)) + if self._list_hook is not None: + ret = self._list_hook(ret) + # TODO is the interaction between `list_hook` and `use_list` ok? + return ret if self._use_list else tuple(ret) + if typ == TYPE_MAP: + if execute == EX_SKIP: + for i in xrange(n): + # TODO check whether we need to call hooks + self._unpack(EX_SKIP) + self._unpack(EX_SKIP) + return + if self._object_pairs_hook is not None: + ret = self._object_pairs_hook( + (self._unpack(EX_CONSTRUCT), + self._unpack(EX_CONSTRUCT)) + for _ in xrange(n)) + else: + ret = {} + for _ in xrange(n): + key = self._unpack(EX_CONSTRUCT) + ret[key] = self._unpack(EX_CONSTRUCT) + if self._object_hook is not None: + ret = self._object_hook(ret) + return ret + if execute == EX_SKIP: + return + if typ == TYPE_RAW: + if self._encoding is not None: + obj = obj.decode(self._encoding, self._unicode_errors) + elif self._raw: + obj = bytes(obj) + else: + obj = obj.decode('utf_8') + return obj + if typ == TYPE_EXT: + return self._ext_hook(n, bytes(obj)) + if typ == TYPE_BIN: + return bytes(obj) + assert typ == TYPE_IMMEDIATE + return obj + + def __iter__(self): + return self + + def __next__(self): + try: + ret = self._unpack(EX_CONSTRUCT) + self._consume() + return ret + except OutOfData: + self._consume() + raise StopIteration + + next = __next__ + + def skip(self, write_bytes=None): + self._unpack(EX_SKIP) + if write_bytes is not None: + warnings.warn("`write_bytes` option is deprecated. Use `.tell()` instead.", DeprecationWarning) + write_bytes(self._buffer[self._buf_checkpoint:self._buff_i]) + self._consume() + + def unpack(self, write_bytes=None): + ret = self._unpack(EX_CONSTRUCT) + if write_bytes is not None: + warnings.warn("`write_bytes` option is deprecated. Use `.tell()` instead.", DeprecationWarning) + write_bytes(self._buffer[self._buf_checkpoint:self._buff_i]) + self._consume() + return ret + + def read_array_header(self, write_bytes=None): + ret = self._unpack(EX_READ_ARRAY_HEADER) + if write_bytes is not None: + warnings.warn("`write_bytes` option is deprecated. Use `.tell()` instead.", DeprecationWarning) + write_bytes(self._buffer[self._buf_checkpoint:self._buff_i]) + self._consume() + return ret + + def read_map_header(self, write_bytes=None): + ret = self._unpack(EX_READ_MAP_HEADER) + if write_bytes is not None: + warnings.warn("`write_bytes` option is deprecated. Use `.tell()` instead.", DeprecationWarning) + write_bytes(self._buffer[self._buf_checkpoint:self._buff_i]) + self._consume() + return ret + + def tell(self): + return self._stream_offset + + +class Packer(object): + """ + MessagePack Packer + + usage: + + packer = Packer() + astream.write(packer.pack(a)) + astream.write(packer.pack(b)) + + Packer's constructor has some keyword arguments: + + :param callable default: + Convert user type to builtin type that Packer supports. + See also simplejson's document. + + :param bool use_single_float: + Use single precision float type for float. (default: False) + + :param bool autoreset: + Reset buffer after each pack and return its content as `bytes`. (default: True). + If set this to false, use `bytes()` to get content and `.reset()` to clear buffer. + + :param bool use_bin_type: + Use bin type introduced in msgpack spec 2.0 for bytes. + It also enables str8 type for unicode. + + :param bool strict_types: + If set to true, types will be checked to be exact. Derived classes + from serializeable types will not be serialized and will be + treated as unsupported type and forwarded to default. + Additionally tuples will not be serialized as lists. + This is useful when trying to implement accurate serialization + for python types. + + :param str encoding: + (deprecated) Convert unicode to bytes with this encoding. (default: 'utf-8') + + :param str unicode_errors: + Error handler for encoding unicode. (default: 'strict') + """ + def __init__(self, default=None, encoding=None, unicode_errors=None, + use_single_float=False, autoreset=True, use_bin_type=False, + strict_types=False): + if encoding is None: + encoding = 'utf_8' + else: + warnings.warn( + "encoding is deprecated, Use raw=False instead.", + PendingDeprecationWarning) + + if unicode_errors is None: + unicode_errors = 'strict' + + self._strict_types = strict_types + self._use_float = use_single_float + self._autoreset = autoreset + self._use_bin_type = use_bin_type + self._encoding = encoding + self._unicode_errors = unicode_errors + self._buffer = StringIO() + if default is not None: + if not callable(default): + raise TypeError("default must be callable") + self._default = default + + def _pack(self, obj, nest_limit=DEFAULT_RECURSE_LIMIT, + check=isinstance, check_type_strict=_check_type_strict): + default_used = False + if self._strict_types: + check = check_type_strict + list_types = list + else: + list_types = (list, tuple) + while True: + if nest_limit < 0: + raise PackValueError("recursion limit exceeded") + if obj is None: + return self._buffer.write(b"\xc0") + if check(obj, bool): + if obj: + return self._buffer.write(b"\xc3") + return self._buffer.write(b"\xc2") + if check(obj, int_types): + if 0 <= obj < 0x80: + return self._buffer.write(struct.pack("B", obj)) + if -0x20 <= obj < 0: + return self._buffer.write(struct.pack("b", obj)) + if 0x80 <= obj <= 0xff: + return self._buffer.write(struct.pack("BB", 0xcc, obj)) + if -0x80 <= obj < 0: + return self._buffer.write(struct.pack(">Bb", 0xd0, obj)) + if 0xff < obj <= 0xffff: + return self._buffer.write(struct.pack(">BH", 0xcd, obj)) + if -0x8000 <= obj < -0x80: + return self._buffer.write(struct.pack(">Bh", 0xd1, obj)) + if 0xffff < obj <= 0xffffffff: + return self._buffer.write(struct.pack(">BI", 0xce, obj)) + if -0x80000000 <= obj < -0x8000: + return self._buffer.write(struct.pack(">Bi", 0xd2, obj)) + if 0xffffffff < obj <= 0xffffffffffffffff: + return self._buffer.write(struct.pack(">BQ", 0xcf, obj)) + if -0x8000000000000000 <= obj < -0x80000000: + return self._buffer.write(struct.pack(">Bq", 0xd3, obj)) + if not default_used and self._default is not None: + obj = self._default(obj) + default_used = True + continue + raise PackOverflowError("Integer value out of range") + if check(obj, (bytes, bytearray)): + n = len(obj) + if n >= 2**32: + raise PackValueError("%s is too large" % type(obj).__name__) + self._pack_bin_header(n) + return self._buffer.write(obj) + if check(obj, Unicode): + if self._encoding is None: + raise TypeError( + "Can't encode unicode string: " + "no encoding is specified") + obj = obj.encode(self._encoding, self._unicode_errors) + n = len(obj) + if n >= 2**32: + raise PackValueError("String is too large") + self._pack_raw_header(n) + return self._buffer.write(obj) + if check(obj, memoryview): + n = len(obj) * obj.itemsize + if n >= 2**32: + raise PackValueError("Memoryview is too large") + self._pack_bin_header(n) + return self._buffer.write(obj) + if check(obj, float): + if self._use_float: + return self._buffer.write(struct.pack(">Bf", 0xca, obj)) + return self._buffer.write(struct.pack(">Bd", 0xcb, obj)) + if check(obj, ExtType): + code = obj.code + data = obj.data + assert isinstance(code, int) + assert isinstance(data, bytes) + L = len(data) + if L == 1: + self._buffer.write(b'\xd4') + elif L == 2: + self._buffer.write(b'\xd5') + elif L == 4: + self._buffer.write(b'\xd6') + elif L == 8: + self._buffer.write(b'\xd7') + elif L == 16: + self._buffer.write(b'\xd8') + elif L <= 0xff: + self._buffer.write(struct.pack(">BB", 0xc7, L)) + elif L <= 0xffff: + self._buffer.write(struct.pack(">BH", 0xc8, L)) + else: + self._buffer.write(struct.pack(">BI", 0xc9, L)) + self._buffer.write(struct.pack("b", code)) + self._buffer.write(data) + return + if check(obj, list_types): + n = len(obj) + self._pack_array_header(n) + for i in xrange(n): + self._pack(obj[i], nest_limit - 1) + return + if check(obj, dict): + return self._pack_map_pairs(len(obj), dict_iteritems(obj), + nest_limit - 1) + if not default_used and self._default is not None: + obj = self._default(obj) + default_used = 1 + continue + raise TypeError("Cannot serialize %r" % (obj, )) + + def pack(self, obj): + try: + self._pack(obj) + except: + self._buffer = StringIO() # force reset + raise + ret = self._buffer.getvalue() + if self._autoreset: + self._buffer = StringIO() + elif USING_STRINGBUILDER: + self._buffer = StringIO(ret) + return ret + + def pack_map_pairs(self, pairs): + self._pack_map_pairs(len(pairs), pairs) + ret = self._buffer.getvalue() + if self._autoreset: + self._buffer = StringIO() + elif USING_STRINGBUILDER: + self._buffer = StringIO(ret) + return ret + + def pack_array_header(self, n): + if n >= 2**32: + raise PackValueError + self._pack_array_header(n) + ret = self._buffer.getvalue() + if self._autoreset: + self._buffer = StringIO() + elif USING_STRINGBUILDER: + self._buffer = StringIO(ret) + return ret + + def pack_map_header(self, n): + if n >= 2**32: + raise PackValueError + self._pack_map_header(n) + ret = self._buffer.getvalue() + if self._autoreset: + self._buffer = StringIO() + elif USING_STRINGBUILDER: + self._buffer = StringIO(ret) + return ret + + def pack_ext_type(self, typecode, data): + if not isinstance(typecode, int): + raise TypeError("typecode must have int type.") + if not 0 <= typecode <= 127: + raise ValueError("typecode should be 0-127") + if not isinstance(data, bytes): + raise TypeError("data must have bytes type") + L = len(data) + if L > 0xffffffff: + raise PackValueError("Too large data") + if L == 1: + self._buffer.write(b'\xd4') + elif L == 2: + self._buffer.write(b'\xd5') + elif L == 4: + self._buffer.write(b'\xd6') + elif L == 8: + self._buffer.write(b'\xd7') + elif L == 16: + self._buffer.write(b'\xd8') + elif L <= 0xff: + self._buffer.write(b'\xc7' + struct.pack('B', L)) + elif L <= 0xffff: + self._buffer.write(b'\xc8' + struct.pack('>H', L)) + else: + self._buffer.write(b'\xc9' + struct.pack('>I', L)) + self._buffer.write(struct.pack('B', typecode)) + self._buffer.write(data) + + def _pack_array_header(self, n): + if n <= 0x0f: + return self._buffer.write(struct.pack('B', 0x90 + n)) + if n <= 0xffff: + return self._buffer.write(struct.pack(">BH", 0xdc, n)) + if n <= 0xffffffff: + return self._buffer.write(struct.pack(">BI", 0xdd, n)) + raise PackValueError("Array is too large") + + def _pack_map_header(self, n): + if n <= 0x0f: + return self._buffer.write(struct.pack('B', 0x80 + n)) + if n <= 0xffff: + return self._buffer.write(struct.pack(">BH", 0xde, n)) + if n <= 0xffffffff: + return self._buffer.write(struct.pack(">BI", 0xdf, n)) + raise PackValueError("Dict is too large") + + def _pack_map_pairs(self, n, pairs, nest_limit=DEFAULT_RECURSE_LIMIT): + self._pack_map_header(n) + for (k, v) in pairs: + self._pack(k, nest_limit - 1) + self._pack(v, nest_limit - 1) + + def _pack_raw_header(self, n): + if n <= 0x1f: + self._buffer.write(struct.pack('B', 0xa0 + n)) + elif self._use_bin_type and n <= 0xff: + self._buffer.write(struct.pack('>BB', 0xd9, n)) + elif n <= 0xffff: + self._buffer.write(struct.pack(">BH", 0xda, n)) + elif n <= 0xffffffff: + self._buffer.write(struct.pack(">BI", 0xdb, n)) + else: + raise PackValueError('Raw is too large') + + def _pack_bin_header(self, n): + if not self._use_bin_type: + return self._pack_raw_header(n) + elif n <= 0xff: + return self._buffer.write(struct.pack('>BB', 0xc4, n)) + elif n <= 0xffff: + return self._buffer.write(struct.pack(">BH", 0xc5, n)) + elif n <= 0xffffffff: + return self._buffer.write(struct.pack(">BI", 0xc6, n)) + else: + raise PackValueError('Bin is too large') + + def bytes(self): + return self._buffer.getvalue() + + def reset(self): + self._buffer = StringIO() diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/__about__.py b/venv/Lib/site-packages/pip/_vendor/packaging/__about__.py new file mode 100644 index 0000000..21fc6ce --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/packaging/__about__.py @@ -0,0 +1,21 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +__all__ = [ + "__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__", "__license__", "__copyright__", +] + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "18.0" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD or Apache License, Version 2.0" +__copyright__ = "Copyright 2014-2018 %s" % __author__ diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/__init__.py b/venv/Lib/site-packages/pip/_vendor/packaging/__init__.py new file mode 100644 index 0000000..5ee6220 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/packaging/__init__.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +from .__about__ import ( + __author__, __copyright__, __email__, __license__, __summary__, __title__, + __uri__, __version__ +) + +__all__ = [ + "__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__", "__license__", "__copyright__", +] diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/_compat.py b/venv/Lib/site-packages/pip/_vendor/packaging/_compat.py new file mode 100644 index 0000000..210bb80 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/packaging/_compat.py @@ -0,0 +1,30 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import sys + + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + +# flake8: noqa + +if PY3: + string_types = str, +else: + string_types = basestring, + + +def with_metaclass(meta, *bases): + """ + Create a base class with a metaclass. + """ + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/_structures.py b/venv/Lib/site-packages/pip/_vendor/packaging/_structures.py new file mode 100644 index 0000000..e9fc4a0 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/packaging/_structures.py @@ -0,0 +1,70 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + + +class Infinity(object): + + def __repr__(self): + return "Infinity" + + def __hash__(self): + return hash(repr(self)) + + def __lt__(self, other): + return False + + def __le__(self, other): + return False + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __ne__(self, other): + return not isinstance(other, self.__class__) + + def __gt__(self, other): + return True + + def __ge__(self, other): + return True + + def __neg__(self): + return NegativeInfinity + + +Infinity = Infinity() + + +class NegativeInfinity(object): + + def __repr__(self): + return "-Infinity" + + def __hash__(self): + return hash(repr(self)) + + def __lt__(self, other): + return True + + def __le__(self, other): + return True + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __ne__(self, other): + return not isinstance(other, self.__class__) + + def __gt__(self, other): + return False + + def __ge__(self, other): + return False + + def __neg__(self): + return Infinity + + +NegativeInfinity = NegativeInfinity() diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/markers.py b/venv/Lib/site-packages/pip/_vendor/packaging/markers.py new file mode 100644 index 0000000..e5834ce --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/packaging/markers.py @@ -0,0 +1,301 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import operator +import os +import platform +import sys + +from pip._vendor.pyparsing import ParseException, ParseResults, stringStart, stringEnd +from pip._vendor.pyparsing import ZeroOrMore, Group, Forward, QuotedString +from pip._vendor.pyparsing import Literal as L # noqa + +from ._compat import string_types +from .specifiers import Specifier, InvalidSpecifier + + +__all__ = [ + "InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName", + "Marker", "default_environment", +] + + +class InvalidMarker(ValueError): + """ + An invalid marker was found, users should refer to PEP 508. + """ + + +class UndefinedComparison(ValueError): + """ + An invalid operation was attempted on a value that doesn't support it. + """ + + +class UndefinedEnvironmentName(ValueError): + """ + A name was attempted to be used that does not exist inside of the + environment. + """ + + +class Node(object): + + def __init__(self, value): + self.value = value + + def __str__(self): + return str(self.value) + + def __repr__(self): + return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) + + def serialize(self): + raise NotImplementedError + + +class Variable(Node): + + def serialize(self): + return str(self) + + +class Value(Node): + + def serialize(self): + return '"{0}"'.format(self) + + +class Op(Node): + + def serialize(self): + return str(self) + + +VARIABLE = ( + L("implementation_version") | + L("platform_python_implementation") | + L("implementation_name") | + L("python_full_version") | + L("platform_release") | + L("platform_version") | + L("platform_machine") | + L("platform_system") | + L("python_version") | + L("sys_platform") | + L("os_name") | + L("os.name") | # PEP-345 + L("sys.platform") | # PEP-345 + L("platform.version") | # PEP-345 + L("platform.machine") | # PEP-345 + L("platform.python_implementation") | # PEP-345 + L("python_implementation") | # undocumented setuptools legacy + L("extra") +) +ALIASES = { + 'os.name': 'os_name', + 'sys.platform': 'sys_platform', + 'platform.version': 'platform_version', + 'platform.machine': 'platform_machine', + 'platform.python_implementation': 'platform_python_implementation', + 'python_implementation': 'platform_python_implementation' +} +VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) + +VERSION_CMP = ( + L("===") | + L("==") | + L(">=") | + L("<=") | + L("!=") | + L("~=") | + L(">") | + L("<") +) + +MARKER_OP = VERSION_CMP | L("not in") | L("in") +MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) + +MARKER_VALUE = QuotedString("'") | QuotedString('"') +MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) + +BOOLOP = L("and") | L("or") + +MARKER_VAR = VARIABLE | MARKER_VALUE + +MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) +MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) + +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() + +MARKER_EXPR = Forward() +MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) +MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) + +MARKER = stringStart + MARKER_EXPR + stringEnd + + +def _coerce_parse_result(results): + if isinstance(results, ParseResults): + return [_coerce_parse_result(i) for i in results] + else: + return results + + +def _format_marker(marker, first=True): + assert isinstance(marker, (list, tuple, string_types)) + + # Sometimes we have a structure like [[...]] which is a single item list + # where the single item is itself it's own list. In that case we want skip + # the rest of this function so that we don't get extraneous () on the + # outside. + if (isinstance(marker, list) and len(marker) == 1 and + isinstance(marker[0], (list, tuple))): + return _format_marker(marker[0]) + + if isinstance(marker, list): + inner = (_format_marker(m, first=False) for m in marker) + if first: + return " ".join(inner) + else: + return "(" + " ".join(inner) + ")" + elif isinstance(marker, tuple): + return " ".join([m.serialize() for m in marker]) + else: + return marker + + +_operators = { + "in": lambda lhs, rhs: lhs in rhs, + "not in": lambda lhs, rhs: lhs not in rhs, + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.ge, + ">": operator.gt, +} + + +def _eval_op(lhs, op, rhs): + try: + spec = Specifier("".join([op.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs) + + oper = _operators.get(op.serialize()) + if oper is None: + raise UndefinedComparison( + "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) + ) + + return oper(lhs, rhs) + + +_undefined = object() + + +def _get_env(environment, name): + value = environment.get(name, _undefined) + + if value is _undefined: + raise UndefinedEnvironmentName( + "{0!r} does not exist in evaluation environment.".format(name) + ) + + return value + + +def _evaluate_markers(markers, environment): + groups = [[]] + + for marker in markers: + assert isinstance(marker, (list, tuple, string_types)) + + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + lhs_value = _get_env(environment, lhs.value) + rhs_value = rhs.value + else: + lhs_value = lhs.value + rhs_value = _get_env(environment, rhs.value) + + groups[-1].append(_eval_op(lhs_value, op, rhs_value)) + else: + assert marker in ["and", "or"] + if marker == "or": + groups.append([]) + + return any(all(item) for item in groups) + + +def format_full_version(info): + version = '{0.major}.{0.minor}.{0.micro}'.format(info) + kind = info.releaselevel + if kind != 'final': + version += kind[0] + str(info.serial) + return version + + +def default_environment(): + if hasattr(sys, 'implementation'): + iver = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + else: + iver = '0' + implementation_name = '' + + return { + "implementation_name": implementation_name, + "implementation_version": iver, + "os_name": os.name, + "platform_machine": platform.machine(), + "platform_release": platform.release(), + "platform_system": platform.system(), + "platform_version": platform.version(), + "python_full_version": platform.python_version(), + "platform_python_implementation": platform.python_implementation(), + "python_version": platform.python_version()[:3], + "sys_platform": sys.platform, + } + + +class Marker(object): + + def __init__(self, marker): + try: + self._markers = _coerce_parse_result(MARKER.parseString(marker)) + except ParseException as e: + err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( + marker, marker[e.loc:e.loc + 8]) + raise InvalidMarker(err_str) + + def __str__(self): + return _format_marker(self._markers) + + def __repr__(self): + return "<Marker({0!r})>".format(str(self)) + + def evaluate(self, environment=None): + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + if environment is not None: + current_environment.update(environment) + + return _evaluate_markers(self._markers, current_environment) diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/requirements.py b/venv/Lib/site-packages/pip/_vendor/packaging/requirements.py new file mode 100644 index 0000000..d40bd8c --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/packaging/requirements.py @@ -0,0 +1,130 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import string +import re + +from pip._vendor.pyparsing import stringStart, stringEnd, originalTextFor, ParseException +from pip._vendor.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine +from pip._vendor.pyparsing import Literal as L # noqa +from pip._vendor.six.moves.urllib import parse as urlparse + +from .markers import MARKER_EXPR, Marker +from .specifiers import LegacySpecifier, Specifier, SpecifierSet + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +ALPHANUM = Word(string.ascii_letters + string.digits) + +LBRACKET = L("[").suppress() +RBRACKET = L("]").suppress() +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() +COMMA = L(",").suppress() +SEMICOLON = L(";").suppress() +AT = L("@").suppress() + +PUNCTUATION = Word("-_.") +IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) +IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) + +NAME = IDENTIFIER("name") +EXTRA = IDENTIFIER + +URI = Regex(r'[^ ]+')("url") +URL = (AT + URI) + +EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) +EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") + +VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) +VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) + +VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY +VERSION_MANY = Combine(VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), + joinString=",", adjacent=False)("_raw_spec") +_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) +_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '') + +VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") +VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) + +MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") +MARKER_EXPR.setParseAction( + lambda s, l, t: Marker(s[t._original_start:t._original_end]) +) +MARKER_SEPARATOR = SEMICOLON +MARKER = MARKER_SEPARATOR + MARKER_EXPR + +VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) +URL_AND_MARKER = URL + Optional(MARKER) + +NAMED_REQUIREMENT = \ + NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) + +REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd +# pyparsing isn't thread safe during initialization, so we do it eagerly, see +# issue #104 +REQUIREMENT.parseString("x[]") + + +class Requirement(object): + """Parse a requirement. + + Parse a given requirement string into its parts, such as name, specifier, + URL, and extras. Raises InvalidRequirement on a badly-formed requirement + string. + """ + + # TODO: Can we test whether something is contained within a requirement? + # If so how do we do that? Do we need to test against the _name_ of + # the thing as well as the version? What about the markers? + # TODO: Can we normalize the name and extra name? + + def __init__(self, requirement_string): + try: + req = REQUIREMENT.parseString(requirement_string) + except ParseException as e: + raise InvalidRequirement("Parse error at \"{0!r}\": {1}".format( + requirement_string[e.loc:e.loc + 8], e.msg + )) + + self.name = req.name + if req.url: + parsed_url = urlparse.urlparse(req.url) + if not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc): + raise InvalidRequirement("Invalid URL: {0}".format(req.url)) + self.url = req.url + else: + self.url = None + self.extras = set(req.extras.asList() if req.extras else []) + self.specifier = SpecifierSet(req.specifier) + self.marker = req.marker if req.marker else None + + def __str__(self): + parts = [self.name] + + if self.extras: + parts.append("[{0}]".format(",".join(sorted(self.extras)))) + + if self.specifier: + parts.append(str(self.specifier)) + + if self.url: + parts.append("@ {0}".format(self.url)) + + if self.marker: + parts.append("; {0}".format(self.marker)) + + return "".join(parts) + + def __repr__(self): + return "<Requirement({0!r})>".format(str(self)) diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py b/venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..4c79899 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py @@ -0,0 +1,774 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import abc +import functools +import itertools +import re + +from ._compat import string_types, with_metaclass +from .version import Version, LegacyVersion, parse + + +class InvalidSpecifier(ValueError): + """ + An invalid specifier was found, users should refer to PEP 440. + """ + + +class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): + + @abc.abstractmethod + def __str__(self): + """ + Returns the str representation of this Specifier like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self): + """ + Returns a hash value for this Specifier like object. + """ + + @abc.abstractmethod + def __eq__(self, other): + """ + Returns a boolean representing whether or not the two Specifier like + objects are equal. + """ + + @abc.abstractmethod + def __ne__(self, other): + """ + Returns a boolean representing whether or not the two Specifier like + objects are not equal. + """ + + @abc.abstractproperty + def prereleases(self): + """ + Returns whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @prereleases.setter + def prereleases(self, value): + """ + Sets whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @abc.abstractmethod + def contains(self, item, prereleases=None): + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter(self, iterable, prereleases=None): + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class _IndividualSpecifier(BaseSpecifier): + + _operators = {} + + def __init__(self, spec="", prereleases=None): + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) + + self._spec = ( + match.group("operator").strip(), + match.group("version").strip(), + ) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + def __repr__(self): + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "<{0}({1!r}{2})>".format( + self.__class__.__name__, + str(self), + pre, + ) + + def __str__(self): + return "{0}{1}".format(*self._spec) + + def __hash__(self): + return hash(self._spec) + + def __eq__(self, other): + if isinstance(other, string_types): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec == other._spec + + def __ne__(self, other): + if isinstance(other, string_types): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec != other._spec + + def _get_operator(self, op): + return getattr(self, "_compare_{0}".format(self._operators[op])) + + def _coerce_version(self, version): + if not isinstance(version, (LegacyVersion, Version)): + version = parse(version) + return version + + @property + def operator(self): + return self._spec[0] + + @property + def version(self): + return self._spec[1] + + @property + def prereleases(self): + return self._prereleases + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version or LegacyVersion, this allows us to have + # a shortcut for ``"2.0" in Specifier(">=2") + item = self._coerce_version(item) + + # Determine if we should be supporting prereleases in this specifier + # or not, if we do not support prereleases than we can short circuit + # logic if this version is a prereleases. + if item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + return self._get_operator(self.operator)(item, self.version) + + def filter(self, iterable, prereleases=None): + yielded = False + found_prereleases = [] + + kw = {"prereleases": prereleases if prereleases is not None else True} + + # Attempt to iterate over all the values in the iterable and if any of + # them match, yield them. + for version in iterable: + parsed_version = self._coerce_version(version) + + if self.contains(parsed_version, **kw): + # If our version is a prerelease, and we were not set to allow + # prereleases, then we'll store it for later incase nothing + # else matches this specifier. + if (parsed_version.is_prerelease and not + (prereleases or self.prereleases)): + found_prereleases.append(version) + # Either this is not a prerelease, or we should have been + # accepting prereleases from the beginning. + else: + yielded = True + yield version + + # Now that we've iterated over everything, determine if we've yielded + # any values, and if we have not and we have any prereleases stored up + # then we will go ahead and yield the prereleases. + if not yielded and found_prereleases: + for version in found_prereleases: + yield version + + +class LegacySpecifier(_IndividualSpecifier): + + _regex_str = ( + r""" + (?P<operator>(==|!=|<=|>=|<|>)) + \s* + (?P<version> + [^,;\s)]* # Since this is a "legacy" specifier, and the version + # string can be just about anything, we match everything + # except for whitespace, a semi-colon for marker support, + # a closing paren since versions can be enclosed in + # them, and a comma since it's a version separator. + ) + """ + ) + + _regex = re.compile( + r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + + _operators = { + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + } + + def _coerce_version(self, version): + if not isinstance(version, LegacyVersion): + version = LegacyVersion(str(version)) + return version + + def _compare_equal(self, prospective, spec): + return prospective == self._coerce_version(spec) + + def _compare_not_equal(self, prospective, spec): + return prospective != self._coerce_version(spec) + + def _compare_less_than_equal(self, prospective, spec): + return prospective <= self._coerce_version(spec) + + def _compare_greater_than_equal(self, prospective, spec): + return prospective >= self._coerce_version(spec) + + def _compare_less_than(self, prospective, spec): + return prospective < self._coerce_version(spec) + + def _compare_greater_than(self, prospective, spec): + return prospective > self._coerce_version(spec) + + +def _require_version_compare(fn): + @functools.wraps(fn) + def wrapped(self, prospective, spec): + if not isinstance(prospective, Version): + return False + return fn(self, prospective, spec) + return wrapped + + +class Specifier(_IndividualSpecifier): + + _regex_str = ( + r""" + (?P<operator>(~=|==|!=|<=|>=|<|>|===)) + (?P<version> + (?: + # The identity operators allow for an escape hatch that will + # do an exact string match of the version you wish to install. + # This will not be parsed by PEP 440 and we cannot determine + # any semantic meaning from it. This operator is discouraged + # but included entirely as an escape hatch. + (?<====) # Only match for the identity operator + \s* + [^\s]* # We just match everything, except for whitespace + # since we are only testing for strict identity. + ) + | + (?: + # The (non)equality operators allow for wild card and local + # versions to be specified so we have to define these two + # operators separately to enable that. + (?<===|!=) # Only match for equals and not equals + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + + # You cannot use a wild card and a dev or local version + # together so group them with a | and make them optional. + (?: + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + | + \.\* # Wild card syntax of .* + )? + ) + | + (?: + # The compatible operator requires at least two digits in the + # release segment. + (?<=~=) # Only match for the compatible operator + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + | + (?: + # All other operators only allow a sub set of what the + # (non)equality operators do. Specifically they do not allow + # local versions to be specified nor do they allow the prefix + # matching wild cards. + (?<!==|!=|~=) # We have special cases for these + # operators so we want to make sure they + # don't match here. + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + ) + """ + ) + + _regex = re.compile( + r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + + _operators = { + "~=": "compatible", + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + @_require_version_compare + def _compare_compatible(self, prospective, spec): + # Compatible releases have an equivalent combination of >= and ==. That + # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to + # implement this in terms of the other specifiers instead of + # implementing it ourselves. The only thing we need to do is construct + # the other specifiers. + + # We want everything but the last item in the version, but we want to + # ignore post and dev releases and we want to treat the pre-release as + # it's own separate segment. + prefix = ".".join( + list( + itertools.takewhile( + lambda x: (not x.startswith("post") and not + x.startswith("dev")), + _version_split(spec), + ) + )[:-1] + ) + + # Add the prefix notation to the end of our string + prefix += ".*" + + return (self._get_operator(">=")(prospective, spec) and + self._get_operator("==")(prospective, prefix)) + + @_require_version_compare + def _compare_equal(self, prospective, spec): + # We need special logic to handle prefix matching + if spec.endswith(".*"): + # In the case of prefix matching we want to ignore local segment. + prospective = Version(prospective.public) + # Split the spec out by dots, and pretend that there is an implicit + # dot in between a release segment and a pre-release segment. + spec = _version_split(spec[:-2]) # Remove the trailing .* + + # Split the prospective version out by dots, and pretend that there + # is an implicit dot in between a release segment and a pre-release + # segment. + prospective = _version_split(str(prospective)) + + # Shorten the prospective version to be the same length as the spec + # so that we can determine if the specifier is a prefix of the + # prospective version or not. + prospective = prospective[:len(spec)] + + # Pad out our two sides with zeros so that they both equal the same + # length. + spec, prospective = _pad_version(spec, prospective) + else: + # Convert our spec string into a Version + spec = Version(spec) + + # If the specifier does not have a local segment, then we want to + # act as if the prospective version also does not have a local + # segment. + if not spec.local: + prospective = Version(prospective.public) + + return prospective == spec + + @_require_version_compare + def _compare_not_equal(self, prospective, spec): + return not self._compare_equal(prospective, spec) + + @_require_version_compare + def _compare_less_than_equal(self, prospective, spec): + return prospective <= Version(spec) + + @_require_version_compare + def _compare_greater_than_equal(self, prospective, spec): + return prospective >= Version(spec) + + @_require_version_compare + def _compare_less_than(self, prospective, spec): + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec) + + # Check to see if the prospective version is less than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective < spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a pre-release version, that we do not accept pre-release + # versions for the version mentioned in the specifier (e.g. <3.1 should + # not match 3.1.dev0, but should match 3.0.dev0). + if not spec.is_prerelease and prospective.is_prerelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # less than the spec version *and* it's not a pre-release of the same + # version in the spec. + return True + + @_require_version_compare + def _compare_greater_than(self, prospective, spec): + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec) + + # Check to see if the prospective version is greater than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective > spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a post-release version, that we do not accept + # post-release versions for the version mentioned in the specifier + # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). + if not spec.is_postrelease and prospective.is_postrelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # Ensure that we do not allow a local version of the version mentioned + # in the specifier, which is technically greater than, to match. + if prospective.local is not None: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # greater than the spec version *and* it's not a pre-release of the + # same version in the spec. + return True + + def _compare_arbitrary(self, prospective, spec): + return str(prospective).lower() == str(spec).lower() + + @property + def prereleases(self): + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Look at all of our specifiers and determine if they are inclusive + # operators, and if they are if they are including an explicit + # prerelease. + operator, version = self._spec + if operator in ["==", ">=", "<=", "~=", "==="]: + # The == specifier can include a trailing .*, if it does we + # want to remove before parsing. + if operator == "==" and version.endswith(".*"): + version = version[:-2] + + # Parse the version, and if it is a pre-release than this + # specifier allows pre-releases. + if parse(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version): + result = [] + for item in version.split("."): + match = _prefix_regex.search(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _pad_version(left, right): + left_split, right_split = [], [] + + # Get the release segment of our versions + left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) + right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) + + # Get the rest of our versions + left_split.append(left[len(left_split[0]):]) + right_split.append(right[len(right_split[0]):]) + + # Insert our padding + left_split.insert( + 1, + ["0"] * max(0, len(right_split[0]) - len(left_split[0])), + ) + right_split.insert( + 1, + ["0"] * max(0, len(left_split[0]) - len(right_split[0])), + ) + + return ( + list(itertools.chain(*left_split)), + list(itertools.chain(*right_split)), + ) + + +class SpecifierSet(BaseSpecifier): + + def __init__(self, specifiers="", prereleases=None): + # Split on , to break each indidivual specifier into it's own item, and + # strip each item to remove leading/trailing whitespace. + specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + # Parsed each individual specifier, attempting first to make it a + # Specifier and falling back to a LegacySpecifier. + parsed = set() + for specifier in specifiers: + try: + parsed.add(Specifier(specifier)) + except InvalidSpecifier: + parsed.add(LegacySpecifier(specifier)) + + # Turn our parsed specifiers into a frozen set and save them for later. + self._specs = frozenset(parsed) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + def __repr__(self): + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "<SpecifierSet({0!r}{1})>".format(str(self), pre) + + def __str__(self): + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self): + return hash(self._specs) + + def __and__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = frozenset(self._specs | other._specs) + + if self._prereleases is None and other._prereleases is not None: + specifier._prereleases = other._prereleases + elif self._prereleases is not None and other._prereleases is None: + specifier._prereleases = self._prereleases + elif self._prereleases == other._prereleases: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease " + "overrides." + ) + + return specifier + + def __eq__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __ne__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs != other._specs + + def __len__(self): + return len(self._specs) + + def __iter__(self): + return iter(self._specs) + + @property + def prereleases(self): + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Ensure that our item is a Version or LegacyVersion instance. + if not isinstance(item, (LegacyVersion, Version)): + item = parse(item) + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # We can determine if we're going to allow pre-releases by looking to + # see if any of the underlying items supports them. If none of them do + # and this item is a pre-release then we do not allow it and we can + # short circuit that here. + # Note: This means that 1.0.dev1 would not be contained in something + # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 + if not prereleases and item.is_prerelease: + return False + + # We simply dispatch to the underlying specs here to make sure that the + # given version is contained within all of them. + # Note: This use of all() here means that an empty set of specifiers + # will always return True, this is an explicit design decision. + return all( + s.contains(item, prereleases=prereleases) + for s in self._specs + ) + + def filter(self, iterable, prereleases=None): + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # If we have any specifiers, then we want to wrap our iterable in the + # filter method for each one, this will act as a logical AND amongst + # each specifier. + if self._specs: + for spec in self._specs: + iterable = spec.filter(iterable, prereleases=bool(prereleases)) + return iterable + # If we do not have any specifiers, then we need to have a rough filter + # which will filter out any pre-releases, unless there are no final + # releases, and which will filter out LegacyVersion in general. + else: + filtered = [] + found_prereleases = [] + + for item in iterable: + # Ensure that we some kind of Version class for this item. + if not isinstance(item, (LegacyVersion, Version)): + parsed_version = parse(item) + else: + parsed_version = item + + # Filter out any item which is parsed as a LegacyVersion + if isinstance(parsed_version, LegacyVersion): + continue + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return found_prereleases + + return filtered diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/utils.py b/venv/Lib/site-packages/pip/_vendor/packaging/utils.py new file mode 100644 index 0000000..4b94a82 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/packaging/utils.py @@ -0,0 +1,63 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import re + +from .version import InvalidVersion, Version + + +_canonicalize_regex = re.compile(r"[-_.]+") + + +def canonicalize_name(name): + # This is taken from PEP 503. + return _canonicalize_regex.sub("-", name).lower() + + +def canonicalize_version(version): + """ + This is very similar to Version.__str__, but has one subtle differences + with the way it handles the release segment. + """ + + try: + version = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + + parts = [] + + # Epoch + if version.epoch != 0: + parts.append("{0}!".format(version.epoch)) + + # Release segment + # NB: This strips trailing '.0's to normalize + parts.append( + re.sub( + r'(\.0)+$', + '', + ".".join(str(x) for x in version.release) + ) + ) + + # Pre-release + if version.pre is not None: + parts.append("".join(str(x) for x in version.pre)) + + # Post-release + if version.post is not None: + parts.append(".post{0}".format(version.post)) + + # Development release + if version.dev is not None: + parts.append(".dev{0}".format(version.dev)) + + # Local version segment + if version.local is not None: + parts.append("+{0}".format(version.local)) + + return "".join(parts) diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/version.py b/venv/Lib/site-packages/pip/_vendor/packaging/version.py new file mode 100644 index 0000000..6ed5cbb --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/packaging/version.py @@ -0,0 +1,441 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import collections +import itertools +import re + +from ._structures import Infinity + + +__all__ = [ + "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN" +] + + +_Version = collections.namedtuple( + "_Version", + ["epoch", "release", "dev", "pre", "post", "local"], +) + + +def parse(version): + """ + Parse the given version string and return either a :class:`Version` object + or a :class:`LegacyVersion` object depending on if the given version is + a valid PEP 440 version or a legacy version. + """ + try: + return Version(version) + except InvalidVersion: + return LegacyVersion(version) + + +class InvalidVersion(ValueError): + """ + An invalid version was found, users should refer to PEP 440. + """ + + +class _BaseVersion(object): + + def __hash__(self): + return hash(self._key) + + def __lt__(self, other): + return self._compare(other, lambda s, o: s < o) + + def __le__(self, other): + return self._compare(other, lambda s, o: s <= o) + + def __eq__(self, other): + return self._compare(other, lambda s, o: s == o) + + def __ge__(self, other): + return self._compare(other, lambda s, o: s >= o) + + def __gt__(self, other): + return self._compare(other, lambda s, o: s > o) + + def __ne__(self, other): + return self._compare(other, lambda s, o: s != o) + + def _compare(self, other, method): + if not isinstance(other, _BaseVersion): + return NotImplemented + + return method(self._key, other._key) + + +class LegacyVersion(_BaseVersion): + + def __init__(self, version): + self._version = str(version) + self._key = _legacy_cmpkey(self._version) + + def __str__(self): + return self._version + + def __repr__(self): + return "<LegacyVersion({0})>".format(repr(str(self))) + + @property + def public(self): + return self._version + + @property + def base_version(self): + return self._version + + @property + def epoch(self): + return -1 + + @property + def release(self): + return None + + @property + def pre(self): + return None + + @property + def post(self): + return None + + @property + def dev(self): + return None + + @property + def local(self): + return None + + @property + def is_prerelease(self): + return False + + @property + def is_postrelease(self): + return False + + @property + def is_devrelease(self): + return False + + +_legacy_version_component_re = re.compile( + r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE, +) + +_legacy_version_replacement_map = { + "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@", +} + + +def _parse_version_parts(s): + for part in _legacy_version_component_re.split(s): + part = _legacy_version_replacement_map.get(part, part) + + if not part or part == ".": + continue + + if part[:1] in "0123456789": + # pad for numeric comparison + yield part.zfill(8) + else: + yield "*" + part + + # ensure that alpha/beta/candidate are before final + yield "*final" + + +def _legacy_cmpkey(version): + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch + # greater than or equal to 0. This will effectively put the LegacyVersion, + # which uses the defacto standard originally implemented by setuptools, + # as before all PEP 440 versions. + epoch = -1 + + # This scheme is taken from pkg_resources.parse_version setuptools prior to + # it's adoption of the packaging library. + parts = [] + for part in _parse_version_parts(version.lower()): + if part.startswith("*"): + # remove "-" before a prerelease tag + if part < "*final": + while parts and parts[-1] == "*final-": + parts.pop() + + # remove trailing zeros from each series of numeric parts + while parts and parts[-1] == "00000000": + parts.pop() + + parts.append(part) + parts = tuple(parts) + + return epoch, parts + + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse +VERSION_PATTERN = r""" + v? + (?: + (?:(?P<epoch>[0-9]+)!)? # epoch + (?P<release>[0-9]+(?:\.[0-9]+)*) # release segment + (?P<pre> # pre-release + [-_\.]? + (?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview)) + [-_\.]? + (?P<pre_n>[0-9]+)? + )? + (?P<post> # post release + (?:-(?P<post_n1>[0-9]+)) + | + (?: + [-_\.]? + (?P<post_l>post|rev|r) + [-_\.]? + (?P<post_n2>[0-9]+)? + ) + )? + (?P<dev> # dev release + [-_\.]? + (?P<dev_l>dev) + [-_\.]? + (?P<dev_n>[0-9]+)? + )? + ) + (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version +""" + + +class Version(_BaseVersion): + + _regex = re.compile( + r"^\s*" + VERSION_PATTERN + r"\s*$", + re.VERBOSE | re.IGNORECASE, + ) + + def __init__(self, version): + # Validate the version and parse it into pieces + match = self._regex.search(version) + if not match: + raise InvalidVersion("Invalid version: '{0}'".format(version)) + + # Store the parsed out pieces of the version + self._version = _Version( + epoch=int(match.group("epoch")) if match.group("epoch") else 0, + release=tuple(int(i) for i in match.group("release").split(".")), + pre=_parse_letter_version( + match.group("pre_l"), + match.group("pre_n"), + ), + post=_parse_letter_version( + match.group("post_l"), + match.group("post_n1") or match.group("post_n2"), + ), + dev=_parse_letter_version( + match.group("dev_l"), + match.group("dev_n"), + ), + local=_parse_local_version(match.group("local")), + ) + + # Generate a key which will be used for sorting + self._key = _cmpkey( + self._version.epoch, + self._version.release, + self._version.pre, + self._version.post, + self._version.dev, + self._version.local, + ) + + def __repr__(self): + return "<Version({0})>".format(repr(str(self))) + + def __str__(self): + parts = [] + + # Epoch + if self.epoch != 0: + parts.append("{0}!".format(self.epoch)) + + # Release segment + parts.append(".".join(str(x) for x in self.release)) + + # Pre-release + if self.pre is not None: + parts.append("".join(str(x) for x in self.pre)) + + # Post-release + if self.post is not None: + parts.append(".post{0}".format(self.post)) + + # Development release + if self.dev is not None: + parts.append(".dev{0}".format(self.dev)) + + # Local version segment + if self.local is not None: + parts.append("+{0}".format(self.local)) + + return "".join(parts) + + @property + def epoch(self): + return self._version.epoch + + @property + def release(self): + return self._version.release + + @property + def pre(self): + return self._version.pre + + @property + def post(self): + return self._version.post[1] if self._version.post else None + + @property + def dev(self): + return self._version.dev[1] if self._version.dev else None + + @property + def local(self): + if self._version.local: + return ".".join(str(x) for x in self._version.local) + else: + return None + + @property + def public(self): + return str(self).split("+", 1)[0] + + @property + def base_version(self): + parts = [] + + # Epoch + if self.epoch != 0: + parts.append("{0}!".format(self.epoch)) + + # Release segment + parts.append(".".join(str(x) for x in self.release)) + + return "".join(parts) + + @property + def is_prerelease(self): + return self.dev is not None or self.pre is not None + + @property + def is_postrelease(self): + return self.post is not None + + @property + def is_devrelease(self): + return self.dev is not None + + +def _parse_letter_version(letter, number): + if letter: + # We consider there to be an implicit 0 in a pre-release if there is + # not a numeral associated with it. + if number is None: + number = 0 + + # We normalize any letters to their lower case form + letter = letter.lower() + + # We consider some words to be alternate spellings of other words and + # in those cases we want to normalize the spellings to our preferred + # spelling. + if letter == "alpha": + letter = "a" + elif letter == "beta": + letter = "b" + elif letter in ["c", "pre", "preview"]: + letter = "rc" + elif letter in ["rev", "r"]: + letter = "post" + + return letter, int(number) + if not letter and number: + # We assume if we are given a number, but we are not given a letter + # then this is using the implicit post release syntax (e.g. 1.0-1) + letter = "post" + + return letter, int(number) + + +_local_version_separators = re.compile(r"[\._-]") + + +def _parse_local_version(local): + """ + Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). + """ + if local is not None: + return tuple( + part.lower() if not part.isdigit() else int(part) + for part in _local_version_separators.split(local) + ) + + +def _cmpkey(epoch, release, pre, post, dev, local): + # When we compare a release version, we want to compare it with all of the + # trailing zeros removed. So we'll use a reverse the list, drop all the now + # leading zeros until we come to something non zero, then take the rest + # re-reverse it back into the correct order and make it a tuple and use + # that for our sorting key. + release = tuple( + reversed(list( + itertools.dropwhile( + lambda x: x == 0, + reversed(release), + ) + )) + ) + + # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0. + # We'll do this by abusing the pre segment, but we _only_ want to do this + # if there is not a pre or a post segment. If we have one of those then + # the normal sorting rules will handle this case correctly. + if pre is None and post is None and dev is not None: + pre = -Infinity + # Versions without a pre-release (except as noted above) should sort after + # those with one. + elif pre is None: + pre = Infinity + + # Versions without a post segment should sort before those with one. + if post is None: + post = -Infinity + + # Versions without a development segment should sort after those with one. + if dev is None: + dev = Infinity + + if local is None: + # Versions without a local segment should sort before those with one. + local = -Infinity + else: + # Versions with a local segment need that segment parsed to implement + # the sorting rules in PEP440. + # - Alpha numeric segments sort before numeric segments + # - Alpha numeric segments sort lexicographically + # - Numeric segments sort numerically + # - Shorter versions sort before longer versions when the prefixes + # match exactly + local = tuple( + (i, "") if isinstance(i, int) else (-Infinity, i) + for i in local + ) + + return epoch, release, pre, post, dev, local diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/__init__.py b/venv/Lib/site-packages/pip/_vendor/pep517/__init__.py new file mode 100644 index 0000000..8beedea --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pep517/__init__.py @@ -0,0 +1,4 @@ +"""Wrappers to build Python packages using PEP 517 hooks +""" + +__version__ = '0.2' diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/_in_process.py b/venv/Lib/site-packages/pip/_vendor/pep517/_in_process.py new file mode 100644 index 0000000..baa14d3 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pep517/_in_process.py @@ -0,0 +1,182 @@ +"""This is invoked in a subprocess to call the build backend hooks. + +It expects: +- Command line args: hook_name, control_dir +- Environment variable: PEP517_BUILD_BACKEND=entry.point:spec +- control_dir/input.json: + - {"kwargs": {...}} + +Results: +- control_dir/output.json + - {"return_val": ...} +""" +from glob import glob +from importlib import import_module +import os +from os.path import join as pjoin +import re +import shutil +import sys + +# This is run as a script, not a module, so it can't do a relative import +import compat + +def _build_backend(): + """Find and load the build backend""" + ep = os.environ['PEP517_BUILD_BACKEND'] + mod_path, _, obj_path = ep.partition(':') + obj = import_module(mod_path) + if obj_path: + for path_part in obj_path.split('.'): + obj = getattr(obj, path_part) + return obj + +def get_requires_for_build_wheel(config_settings): + """Invoke the optional get_requires_for_build_wheel hook + + Returns [] if the hook is not defined. + """ + backend = _build_backend() + try: + hook = backend.get_requires_for_build_wheel + except AttributeError: + return [] + else: + return hook(config_settings) + +def prepare_metadata_for_build_wheel(metadata_directory, config_settings): + """Invoke optional prepare_metadata_for_build_wheel + + Implements a fallback by building a wheel if the hook isn't defined. + """ + backend = _build_backend() + try: + hook = backend.prepare_metadata_for_build_wheel + except AttributeError: + return _get_wheel_metadata_from_wheel(backend, metadata_directory, + config_settings) + else: + return hook(metadata_directory, config_settings) + +WHEEL_BUILT_MARKER = 'PEP517_ALREADY_BUILT_WHEEL' + +def _dist_info_files(whl_zip): + """Identify the .dist-info folder inside a wheel ZipFile.""" + res = [] + for path in whl_zip.namelist(): + m = re.match(r'[^/\\]+-[^/\\]+\.dist-info/', path) + if m: + res.append(path) + if res: + return res + raise Exception("No .dist-info folder found in wheel") + +def _get_wheel_metadata_from_wheel(backend, metadata_directory, config_settings): + """Build a wheel and extract the metadata from it. + + Fallback for when the build backend does not define the 'get_wheel_metadata' + hook. + """ + from zipfile import ZipFile + whl_basename = backend.build_wheel(metadata_directory, config_settings) + with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), 'wb'): + pass # Touch marker file + + whl_file = os.path.join(metadata_directory, whl_basename) + with ZipFile(whl_file) as zipf: + dist_info = _dist_info_files(zipf) + zipf.extractall(path=metadata_directory, members=dist_info) + return dist_info[0].split('/')[0] + +def _find_already_built_wheel(metadata_directory): + """Check for a wheel already built during the get_wheel_metadata hook. + """ + if not metadata_directory: + return None + metadata_parent = os.path.dirname(metadata_directory) + if not os.path.isfile(pjoin(metadata_parent, WHEEL_BUILT_MARKER)): + return None + + whl_files = glob(os.path.join(metadata_parent, '*.whl')) + if not whl_files: + print('Found wheel built marker, but no .whl files') + return None + if len(whl_files) > 1: + print('Found multiple .whl files; unspecified behaviour. ' + 'Will call build_wheel.') + return None + + # Exactly one .whl file + return whl_files[0] + +def build_wheel(wheel_directory, config_settings, metadata_directory=None): + """Invoke the mandatory build_wheel hook. + + If a wheel was already built in the prepare_metadata_for_build_wheel fallback, this + will copy it rather than rebuilding the wheel. + """ + prebuilt_whl = _find_already_built_wheel(metadata_directory) + if prebuilt_whl: + shutil.copy2(prebuilt_whl, wheel_directory) + return os.path.basename(prebuilt_whl) + + return _build_backend().build_wheel(wheel_directory, config_settings, + metadata_directory) + + +def get_requires_for_build_sdist(config_settings): + """Invoke the optional get_requires_for_build_wheel hook + + Returns [] if the hook is not defined. + """ + backend = _build_backend() + try: + hook = backend.get_requires_for_build_sdist + except AttributeError: + return [] + else: + return hook(config_settings) + +class _DummyException(Exception): + """Nothing should ever raise this exception""" + +class GotUnsupportedOperation(Exception): + """For internal use when backend raises UnsupportedOperation""" + +def build_sdist(sdist_directory, config_settings): + """Invoke the mandatory build_sdist hook.""" + backend = _build_backend() + try: + return backend.build_sdist(sdist_directory, config_settings) + except getattr(backend, 'UnsupportedOperation', _DummyException): + raise GotUnsupportedOperation + +HOOK_NAMES = { + 'get_requires_for_build_wheel', + 'prepare_metadata_for_build_wheel', + 'build_wheel', + 'get_requires_for_build_sdist', + 'build_sdist', +} + +def main(): + if len(sys.argv) < 3: + sys.exit("Needs args: hook_name, control_dir") + hook_name = sys.argv[1] + control_dir = sys.argv[2] + if hook_name not in HOOK_NAMES: + sys.exit("Unknown hook: %s" % hook_name) + hook = globals()[hook_name] + + hook_input = compat.read_json(pjoin(control_dir, 'input.json')) + + json_out = {'unsupported': False, 'return_val': None} + try: + json_out['return_val'] = hook(**hook_input['kwargs']) + except GotUnsupportedOperation: + json_out['unsupported'] = True + + compat.write_json(json_out, pjoin(control_dir, 'output.json'), indent=2) + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/check.py b/venv/Lib/site-packages/pip/_vendor/pep517/check.py new file mode 100644 index 0000000..c65d51c --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pep517/check.py @@ -0,0 +1,194 @@ +"""Check a project and backend by attempting to build using PEP 517 hooks. +""" +import argparse +import logging +import os +from os.path import isfile, join as pjoin +from pip._vendor.pytoml import TomlError, load as toml_load +import shutil +from subprocess import CalledProcessError +import sys +import tarfile +from tempfile import mkdtemp +import zipfile + +from .colorlog import enable_colourful_output +from .envbuild import BuildEnvironment +from .wrappers import Pep517HookCaller + +log = logging.getLogger(__name__) + +def check_build_sdist(hooks): + with BuildEnvironment() as env: + try: + env.pip_install(hooks.build_sys_requires) + log.info('Installed static build dependencies') + except CalledProcessError: + log.error('Failed to install static build dependencies') + return False + + try: + reqs = hooks.get_requires_for_build_sdist({}) + log.info('Got build requires: %s', reqs) + except: + log.error('Failure in get_requires_for_build_sdist', exc_info=True) + return False + + try: + env.pip_install(reqs) + log.info('Installed dynamic build dependencies') + except CalledProcessError: + log.error('Failed to install dynamic build dependencies') + return False + + td = mkdtemp() + log.info('Trying to build sdist in %s', td) + try: + try: + filename = hooks.build_sdist(td, {}) + log.info('build_sdist returned %r', filename) + except: + log.info('Failure in build_sdist', exc_info=True) + return False + + if not filename.endswith('.tar.gz'): + log.error("Filename %s doesn't have .tar.gz extension", filename) + return False + + path = pjoin(td, filename) + if isfile(path): + log.info("Output file %s exists", path) + else: + log.error("Output file %s does not exist", path) + return False + + if tarfile.is_tarfile(path): + log.info("Output file is a tar file") + else: + log.error("Output file is not a tar file") + return False + + finally: + shutil.rmtree(td) + + return True + +def check_build_wheel(hooks): + with BuildEnvironment() as env: + try: + env.pip_install(hooks.build_sys_requires) + log.info('Installed static build dependencies') + except CalledProcessError: + log.error('Failed to install static build dependencies') + return False + + try: + reqs = hooks.get_requires_for_build_wheel({}) + log.info('Got build requires: %s', reqs) + except: + log.error('Failure in get_requires_for_build_sdist', exc_info=True) + return False + + try: + env.pip_install(reqs) + log.info('Installed dynamic build dependencies') + except CalledProcessError: + log.error('Failed to install dynamic build dependencies') + return False + + td = mkdtemp() + log.info('Trying to build wheel in %s', td) + try: + try: + filename = hooks.build_wheel(td, {}) + log.info('build_wheel returned %r', filename) + except: + log.info('Failure in build_wheel', exc_info=True) + return False + + if not filename.endswith('.whl'): + log.error("Filename %s doesn't have .whl extension", filename) + return False + + path = pjoin(td, filename) + if isfile(path): + log.info("Output file %s exists", path) + else: + log.error("Output file %s does not exist", path) + return False + + if zipfile.is_zipfile(path): + log.info("Output file is a zip file") + else: + log.error("Output file is not a zip file") + return False + + finally: + shutil.rmtree(td) + + return True + + +def check(source_dir): + pyproject = pjoin(source_dir, 'pyproject.toml') + if isfile(pyproject): + log.info('Found pyproject.toml') + else: + log.error('Missing pyproject.toml') + return False + + try: + with open(pyproject) as f: + pyproject_data = toml_load(f) + # Ensure the mandatory data can be loaded + buildsys = pyproject_data['build-system'] + requires = buildsys['requires'] + backend = buildsys['build-backend'] + log.info('Loaded pyproject.toml') + except (TomlError, KeyError): + log.error("Invalid pyproject.toml", exc_info=True) + return False + + hooks = Pep517HookCaller(source_dir, backend) + + sdist_ok = check_build_sdist(hooks) + wheel_ok = check_build_wheel(hooks) + + if not sdist_ok: + log.warning('Sdist checks failed; scroll up to see') + if not wheel_ok: + log.warning('Wheel checks failed') + + return sdist_ok + + +def main(argv=None): + ap = argparse.ArgumentParser() + ap.add_argument('source_dir', + help="A directory containing pyproject.toml") + args = ap.parse_args(argv) + + enable_colourful_output() + + ok = check(args.source_dir) + + if ok: + print(ansi('Checks passed', 'green')) + else: + print(ansi('Checks failed', 'red')) + sys.exit(1) + +ansi_codes = { + 'reset': '\x1b[0m', + 'bold': '\x1b[1m', + 'red': '\x1b[31m', + 'green': '\x1b[32m', +} +def ansi(s, attr): + if os.name != 'nt' and sys.stdout.isatty(): + return ansi_codes[attr] + str(s) + ansi_codes['reset'] + else: + return str(s) + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/colorlog.py b/venv/Lib/site-packages/pip/_vendor/pep517/colorlog.py new file mode 100644 index 0000000..26cf748 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pep517/colorlog.py @@ -0,0 +1,110 @@ +"""Nicer log formatting with colours. + +Code copied from Tornado, Apache licensed. +""" +# Copyright 2012 Facebook +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging +import sys + +try: + import curses +except ImportError: + curses = None + +def _stderr_supports_color(): + color = False + if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty(): + try: + curses.setupterm() + if curses.tigetnum("colors") > 0: + color = True + except Exception: + pass + return color + +class LogFormatter(logging.Formatter): + """Log formatter with colour support + """ + DEFAULT_COLORS = { + logging.INFO: 2, # Green + logging.WARNING: 3, # Yellow + logging.ERROR: 1, # Red + logging.CRITICAL: 1, + } + + def __init__(self, color=True, datefmt=None): + r""" + :arg bool color: Enables color support. + :arg string fmt: Log message format. + It will be applied to the attributes dict of log records. The + text between ``%(color)s`` and ``%(end_color)s`` will be colored + depending on the level if color support is on. + :arg dict colors: color mappings from logging level to terminal color + code + :arg string datefmt: Datetime format. + Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``. + .. versionchanged:: 3.2 + Added ``fmt`` and ``datefmt`` arguments. + """ + logging.Formatter.__init__(self, datefmt=datefmt) + self._colors = {} + if color and _stderr_supports_color(): + # The curses module has some str/bytes confusion in + # python3. Until version 3.2.3, most methods return + # bytes, but only accept strings. In addition, we want to + # output these strings with the logging module, which + # works with unicode strings. The explicit calls to + # unicode() below are harmless in python2 but will do the + # right conversion in python 3. + fg_color = (curses.tigetstr("setaf") or + curses.tigetstr("setf") or "") + if (3, 0) < sys.version_info < (3, 2, 3): + fg_color = str(fg_color, "ascii") + + for levelno, code in self.DEFAULT_COLORS.items(): + self._colors[levelno] = str(curses.tparm(fg_color, code), "ascii") + self._normal = str(curses.tigetstr("sgr0"), "ascii") + + scr = curses.initscr() + self.termwidth = scr.getmaxyx()[1] + curses.endwin() + else: + self._normal = '' + # Default width is usually 80, but too wide is worse than too narrow + self.termwidth = 70 + + def formatMessage(self, record): + l = len(record.message) + right_text = '{initial}-{name}'.format(initial=record.levelname[0], + name=record.name) + if l + len(right_text) < self.termwidth: + space = ' ' * (self.termwidth - (l + len(right_text))) + else: + space = ' ' + + if record.levelno in self._colors: + start_color = self._colors[record.levelno] + end_color = self._normal + else: + start_color = end_color = '' + + return record.message + space + start_color + right_text + end_color + +def enable_colourful_output(level=logging.INFO): + handler = logging.StreamHandler() + handler.setFormatter(LogFormatter()) + logging.root.addHandler(handler) + logging.root.setLevel(level) diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/compat.py b/venv/Lib/site-packages/pip/_vendor/pep517/compat.py new file mode 100644 index 0000000..01c66fc --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pep517/compat.py @@ -0,0 +1,23 @@ +"""Handle reading and writing JSON in UTF-8, on Python 3 and 2.""" +import json +import sys + +if sys.version_info[0] >= 3: + # Python 3 + def write_json(obj, path, **kwargs): + with open(path, 'w', encoding='utf-8') as f: + json.dump(obj, f, **kwargs) + + def read_json(path): + with open(path, 'r', encoding='utf-8') as f: + return json.load(f) + +else: + # Python 2 + def write_json(obj, path, **kwargs): + with open(path, 'wb') as f: + json.dump(obj, f, encoding='utf-8', **kwargs) + + def read_json(path): + with open(path, 'rb') as f: + return json.load(f) diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py b/venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py new file mode 100644 index 0000000..c264f46 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py @@ -0,0 +1,150 @@ +"""Build wheels/sdists by installing build deps to a temporary environment. +""" + +import os +import logging +from pip._vendor import pytoml +import shutil +from subprocess import check_call +import sys +from sysconfig import get_paths +from tempfile import mkdtemp + +from .wrappers import Pep517HookCaller + +log = logging.getLogger(__name__) + +def _load_pyproject(source_dir): + with open(os.path.join(source_dir, 'pyproject.toml')) as f: + pyproject_data = pytoml.load(f) + buildsys = pyproject_data['build-system'] + return buildsys['requires'], buildsys['build-backend'] + + +class BuildEnvironment(object): + """Context manager to install build deps in a simple temporary environment + + Based on code I wrote for pip, which is MIT licensed. + """ + # Copyright (c) 2008-2016 The pip developers (see AUTHORS.txt file) + # + # 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. + + path = None + + def __init__(self, cleanup=True): + self._cleanup = cleanup + + def __enter__(self): + self.path = mkdtemp(prefix='pep517-build-env-') + log.info('Temporary build environment: %s', self.path) + + self.save_path = os.environ.get('PATH', None) + self.save_pythonpath = os.environ.get('PYTHONPATH', None) + + install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix' + install_dirs = get_paths(install_scheme, vars={ + 'base': self.path, + 'platbase': self.path, + }) + + scripts = install_dirs['scripts'] + if self.save_path: + os.environ['PATH'] = scripts + os.pathsep + self.save_path + else: + os.environ['PATH'] = scripts + os.pathsep + os.defpath + + if install_dirs['purelib'] == install_dirs['platlib']: + lib_dirs = install_dirs['purelib'] + else: + lib_dirs = install_dirs['purelib'] + os.pathsep + \ + install_dirs['platlib'] + if self.save_pythonpath: + os.environ['PYTHONPATH'] = lib_dirs + os.pathsep + \ + self.save_pythonpath + else: + os.environ['PYTHONPATH'] = lib_dirs + + return self + + def pip_install(self, reqs): + """Install dependencies into this env by calling pip in a subprocess""" + if not reqs: + return + log.info('Calling pip to install %s', reqs) + check_call([sys.executable, '-m', 'pip', 'install', '--ignore-installed', + '--prefix', self.path] + list(reqs)) + + def __exit__(self, exc_type, exc_val, exc_tb): + if self._cleanup and (self.path is not None) and os.path.isdir(self.path): + shutil.rmtree(self.path) + + if self.save_path is None: + os.environ.pop('PATH', None) + else: + os.environ['PATH'] = self.save_path + + if self.save_pythonpath is None: + os.environ.pop('PYTHONPATH', None) + else: + os.environ['PYTHONPATH'] = self.save_pythonpath + +def build_wheel(source_dir, wheel_dir, config_settings=None): + """Build a wheel from a source directory using PEP 517 hooks. + + :param str source_dir: Source directory containing pyproject.toml + :param str wheel_dir: Target directory to create wheel in + :param dict config_settings: Options to pass to build backend + + This is a blocking function which will run pip in a subprocess to install + build requirements. + """ + if config_settings is None: + config_settings = {} + requires, backend = _load_pyproject(source_dir) + hooks = Pep517HookCaller(source_dir, backend) + + with BuildEnvironment() as env: + env.pip_install(requires) + reqs = hooks.get_requires_for_build_wheel(config_settings) + env.pip_install(reqs) + return hooks.build_wheel(wheel_dir, config_settings) + + +def build_sdist(source_dir, sdist_dir, config_settings=None): + """Build an sdist from a source directory using PEP 517 hooks. + + :param str source_dir: Source directory containing pyproject.toml + :param str sdist_dir: Target directory to place sdist in + :param dict config_settings: Options to pass to build backend + + This is a blocking function which will run pip in a subprocess to install + build requirements. + """ + if config_settings is None: + config_settings = {} + requires, backend = _load_pyproject(source_dir) + hooks = Pep517HookCaller(source_dir, backend) + + with BuildEnvironment() as env: + env.pip_install(requires) + reqs = hooks.get_requires_for_build_sdist(config_settings) + env.pip_install(reqs) + return hooks.build_sdist(sdist_dir, config_settings) diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/wrappers.py b/venv/Lib/site-packages/pip/_vendor/pep517/wrappers.py new file mode 100644 index 0000000..28260f3 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pep517/wrappers.py @@ -0,0 +1,134 @@ +from contextlib import contextmanager +import os +from os.path import dirname, abspath, join as pjoin +import shutil +from subprocess import check_call +import sys +from tempfile import mkdtemp + +from . import compat + +_in_proc_script = pjoin(dirname(abspath(__file__)), '_in_process.py') + +@contextmanager +def tempdir(): + td = mkdtemp() + try: + yield td + finally: + shutil.rmtree(td) + +class UnsupportedOperation(Exception): + """May be raised by build_sdist if the backend indicates that it can't.""" + +class Pep517HookCaller(object): + """A wrapper around a source directory to be built with a PEP 517 backend. + + source_dir : The path to the source directory, containing pyproject.toml. + backend : The build backend spec, as per PEP 517, from pyproject.toml. + """ + def __init__(self, source_dir, build_backend): + self.source_dir = abspath(source_dir) + self.build_backend = build_backend + + def get_requires_for_build_wheel(self, config_settings=None): + """Identify packages required for building a wheel + + Returns a list of dependency specifications, e.g.: + ["wheel >= 0.25", "setuptools"] + + This does not include requirements specified in pyproject.toml. + It returns the result of calling the equivalently named hook in a + subprocess. + """ + return self._call_hook('get_requires_for_build_wheel', { + 'config_settings': config_settings + }) + + def prepare_metadata_for_build_wheel(self, metadata_directory, config_settings=None): + """Prepare a *.dist-info folder with metadata for this project. + + Returns the name of the newly created folder. + + If the build backend defines a hook with this name, it will be called + in a subprocess. If not, the backend will be asked to build a wheel, + and the dist-info extracted from that. + """ + return self._call_hook('prepare_metadata_for_build_wheel', { + 'metadata_directory': abspath(metadata_directory), + 'config_settings': config_settings, + }) + + def build_wheel(self, wheel_directory, config_settings=None, metadata_directory=None): + """Build a wheel from this project. + + Returns the name of the newly created file. + + In general, this will call the 'build_wheel' hook in the backend. + However, if that was previously called by + 'prepare_metadata_for_build_wheel', and the same metadata_directory is + used, the previously built wheel will be copied to wheel_directory. + """ + if metadata_directory is not None: + metadata_directory = abspath(metadata_directory) + return self._call_hook('build_wheel', { + 'wheel_directory': abspath(wheel_directory), + 'config_settings': config_settings, + 'metadata_directory': metadata_directory, + }) + + def get_requires_for_build_sdist(self, config_settings=None): + """Identify packages required for building a wheel + + Returns a list of dependency specifications, e.g.: + ["setuptools >= 26"] + + This does not include requirements specified in pyproject.toml. + It returns the result of calling the equivalently named hook in a + subprocess. + """ + return self._call_hook('get_requires_for_build_sdist', { + 'config_settings': config_settings + }) + + def build_sdist(self, sdist_directory, config_settings=None): + """Build an sdist from this project. + + Returns the name of the newly created file. + + This calls the 'build_sdist' backend hook in a subprocess. + """ + return self._call_hook('build_sdist', { + 'sdist_directory': abspath(sdist_directory), + 'config_settings': config_settings, + }) + + + def _call_hook(self, hook_name, kwargs): + env = os.environ.copy() + + # On Python 2, pytoml returns Unicode values (which is correct) but the + # environment passed to check_call needs to contain string values. We + # convert here by encoding using ASCII (the backend can only contain + # letters, digits and _, . and : characters, and will be used as a + # Python identifier, so non-ASCII content is wrong on Python 2 in + # any case). + if sys.version_info[0] == 2: + build_backend = self.build_backend.encode('ASCII') + else: + build_backend = self.build_backend + + env['PEP517_BUILD_BACKEND'] = build_backend + with tempdir() as td: + compat.write_json({'kwargs': kwargs}, pjoin(td, 'input.json'), + indent=2) + + # Run the hook in a subprocess + check_call([sys.executable, _in_proc_script, hook_name, td], + cwd=self.source_dir, env=env) + + data = compat.read_json(pjoin(td, 'output.json')) + if data.get('unsupported'): + raise UnsupportedOperation + return data['return_val'] + diff --git a/venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py b/venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py new file mode 100644 index 0000000..0b432f6 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py @@ -0,0 +1,3149 @@ +# coding: utf-8 +""" +Package resource API +-------------------- + +A resource is a logical file contained within a package, or a logical +subdirectory thereof. The package resource API expects resource names +to have their path parts separated with ``/``, *not* whatever the local +path separator is. Do not use os.path operations to manipulate resource +names being passed into the API. + +The package resource API is designed to work with normal filesystem packages, +.egg files, and unpacked .egg files. It can also work in a limited way with +.zip files and with custom PEP 302 loaders that support the ``get_data()`` +method. +""" + +from __future__ import absolute_import + +import sys +import os +import io +import time +import re +import types +import zipfile +import zipimport +import warnings +import stat +import functools +import pkgutil +import operator +import platform +import collections +import plistlib +import email.parser +import errno +import tempfile +import textwrap +import itertools +import inspect +from pkgutil import get_importer + +try: + import _imp +except ImportError: + # Python 3.2 compatibility + import imp as _imp + +try: + FileExistsError +except NameError: + FileExistsError = OSError + +from pip._vendor import six +from pip._vendor.six.moves import urllib, map, filter + +# capture these to bypass sandboxing +from os import utime +try: + from os import mkdir, rename, unlink + WRITE_SUPPORT = True +except ImportError: + # no write support, probably under GAE + WRITE_SUPPORT = False + +from os import open as os_open +from os.path import isdir, split + +try: + import importlib.machinery as importlib_machinery + # access attribute to force import under delayed import mechanisms. + importlib_machinery.__name__ +except ImportError: + importlib_machinery = None + +from . import py31compat +from pip._vendor import appdirs +from pip._vendor import packaging +__import__('pip._vendor.packaging.version') +__import__('pip._vendor.packaging.specifiers') +__import__('pip._vendor.packaging.requirements') +__import__('pip._vendor.packaging.markers') + + +__metaclass__ = type + + +if (3, 0) < sys.version_info < (3, 4): + raise RuntimeError("Python 3.4 or later is required") + +if six.PY2: + # Those builtin exceptions are only defined in Python 3 + PermissionError = None + NotADirectoryError = None + +# declare some globals that will be defined later to +# satisfy the linters. +require = None +working_set = None +add_activation_listener = None +resources_stream = None +cleanup_resources = None +resource_dir = None +resource_stream = None +set_extraction_path = None +resource_isdir = None +resource_string = None +iter_entry_points = None +resource_listdir = None +resource_filename = None +resource_exists = None +_distribution_finders = None +_namespace_handlers = None +_namespace_packages = None + + +class PEP440Warning(RuntimeWarning): + """ + Used when there is an issue with a version or specifier not complying with + PEP 440. + """ + + +def parse_version(v): + try: + return packaging.version.Version(v) + except packaging.version.InvalidVersion: + return packaging.version.LegacyVersion(v) + + +_state_vars = {} + + +def _declare_state(vartype, **kw): + globals().update(kw) + _state_vars.update(dict.fromkeys(kw, vartype)) + + +def __getstate__(): + state = {} + g = globals() + for k, v in _state_vars.items(): + state[k] = g['_sget_' + v](g[k]) + return state + + +def __setstate__(state): + g = globals() + for k, v in state.items(): + g['_sset_' + _state_vars[k]](k, g[k], v) + return state + + +def _sget_dict(val): + return val.copy() + + +def _sset_dict(key, ob, state): + ob.clear() + ob.update(state) + + +def _sget_object(val): + return val.__getstate__() + + +def _sset_object(key, ob, state): + ob.__setstate__(state) + + +_sget_none = _sset_none = lambda *args: None + + +def get_supported_platform(): + """Return this platform's maximum compatible version. + + distutils.util.get_platform() normally reports the minimum version + of Mac OS X that would be required to *use* extensions produced by + distutils. But what we want when checking compatibility is to know the + version of Mac OS X that we are *running*. To allow usage of packages that + explicitly require a newer version of Mac OS X, we must also know the + current version of the OS. + + If this condition occurs for any other platform with a version in its + platform strings, this function should be extended accordingly. + """ + plat = get_build_platform() + m = macosVersionString.match(plat) + if m is not None and sys.platform == "darwin": + try: + plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3)) + except ValueError: + # not Mac OS X + pass + return plat + + +__all__ = [ + # Basic resource access and distribution/entry point discovery + 'require', 'run_script', 'get_provider', 'get_distribution', + 'load_entry_point', 'get_entry_map', 'get_entry_info', + 'iter_entry_points', + 'resource_string', 'resource_stream', 'resource_filename', + 'resource_listdir', 'resource_exists', 'resource_isdir', + + # Environmental control + 'declare_namespace', 'working_set', 'add_activation_listener', + 'find_distributions', 'set_extraction_path', 'cleanup_resources', + 'get_default_cache', + + # Primary implementation classes + 'Environment', 'WorkingSet', 'ResourceManager', + 'Distribution', 'Requirement', 'EntryPoint', + + # Exceptions + 'ResolutionError', 'VersionConflict', 'DistributionNotFound', + 'UnknownExtra', 'ExtractionError', + + # Warnings + 'PEP440Warning', + + # Parsing functions and string utilities + 'parse_requirements', 'parse_version', 'safe_name', 'safe_version', + 'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections', + 'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker', + + # filesystem utilities + 'ensure_directory', 'normalize_path', + + # Distribution "precedence" constants + 'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST', + + # "Provider" interfaces, implementations, and registration/lookup APIs + 'IMetadataProvider', 'IResourceProvider', 'FileMetadata', + 'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider', + 'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider', + 'register_finder', 'register_namespace_handler', 'register_loader_type', + 'fixup_namespace_packages', 'get_importer', + + # Deprecated/backward compatibility only + 'run_main', 'AvailableDistributions', +] + + +class ResolutionError(Exception): + """Abstract base for dependency resolution errors""" + + def __repr__(self): + return self.__class__.__name__ + repr(self.args) + + +class VersionConflict(ResolutionError): + """ + An already-installed version conflicts with the requested version. + + Should be initialized with the installed Distribution and the requested + Requirement. + """ + + _template = "{self.dist} is installed but {self.req} is required" + + @property + def dist(self): + return self.args[0] + + @property + def req(self): + return self.args[1] + + def report(self): + return self._template.format(**locals()) + + def with_context(self, required_by): + """ + If required_by is non-empty, return a version of self that is a + ContextualVersionConflict. + """ + if not required_by: + return self + args = self.args + (required_by,) + return ContextualVersionConflict(*args) + + +class ContextualVersionConflict(VersionConflict): + """ + A VersionConflict that accepts a third parameter, the set of the + requirements that required the installed Distribution. + """ + + _template = VersionConflict._template + ' by {self.required_by}' + + @property + def required_by(self): + return self.args[2] + + +class DistributionNotFound(ResolutionError): + """A requested distribution was not found""" + + _template = ("The '{self.req}' distribution was not found " + "and is required by {self.requirers_str}") + + @property + def req(self): + return self.args[0] + + @property + def requirers(self): + return self.args[1] + + @property + def requirers_str(self): + if not self.requirers: + return 'the application' + return ', '.join(self.requirers) + + def report(self): + return self._template.format(**locals()) + + def __str__(self): + return self.report() + + +class UnknownExtra(ResolutionError): + """Distribution doesn't have an "extra feature" of the given name""" + + +_provider_factories = {} + +PY_MAJOR = sys.version[:3] +EGG_DIST = 3 +BINARY_DIST = 2 +SOURCE_DIST = 1 +CHECKOUT_DIST = 0 +DEVELOP_DIST = -1 + + +def register_loader_type(loader_type, provider_factory): + """Register `provider_factory` to make providers for `loader_type` + + `loader_type` is the type or class of a PEP 302 ``module.__loader__``, + and `provider_factory` is a function that, passed a *module* object, + returns an ``IResourceProvider`` for that module. + """ + _provider_factories[loader_type] = provider_factory + + +def get_provider(moduleOrReq): + """Return an IResourceProvider for the named module or requirement""" + if isinstance(moduleOrReq, Requirement): + return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0] + try: + module = sys.modules[moduleOrReq] + except KeyError: + __import__(moduleOrReq) + module = sys.modules[moduleOrReq] + loader = getattr(module, '__loader__', None) + return _find_adapter(_provider_factories, loader)(module) + + +def _macosx_vers(_cache=[]): + if not _cache: + version = platform.mac_ver()[0] + # fallback for MacPorts + if version == '': + plist = '/System/Library/CoreServices/SystemVersion.plist' + if os.path.exists(plist): + if hasattr(plistlib, 'readPlist'): + plist_content = plistlib.readPlist(plist) + if 'ProductVersion' in plist_content: + version = plist_content['ProductVersion'] + + _cache.append(version.split('.')) + return _cache[0] + + +def _macosx_arch(machine): + return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) + + +def get_build_platform(): + """Return this platform's string for platform-specific distributions + + XXX Currently this is the same as ``distutils.util.get_platform()``, but it + needs some hacks for Linux and Mac OS X. + """ + from sysconfig import get_platform + + plat = get_platform() + if sys.platform == "darwin" and not plat.startswith('macosx-'): + try: + version = _macosx_vers() + machine = os.uname()[4].replace(" ", "_") + return "macosx-%d.%d-%s" % ( + int(version[0]), int(version[1]), + _macosx_arch(machine), + ) + except ValueError: + # if someone is running a non-Mac darwin system, this will fall + # through to the default implementation + pass + return plat + + +macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)") +darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)") +# XXX backward compat +get_platform = get_build_platform + + +def compatible_platforms(provided, required): + """Can code for the `provided` platform run on the `required` platform? + + Returns true if either platform is ``None``, or the platforms are equal. + + XXX Needs compatibility checks for Linux and other unixy OSes. + """ + if provided is None or required is None or provided == required: + # easy case + return True + + # Mac OS X special cases + reqMac = macosVersionString.match(required) + if reqMac: + provMac = macosVersionString.match(provided) + + # is this a Mac package? + if not provMac: + # this is backwards compatibility for packages built before + # setuptools 0.6. All packages built after this point will + # use the new macosx designation. + provDarwin = darwinVersionString.match(provided) + if provDarwin: + dversion = int(provDarwin.group(1)) + macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2)) + if dversion == 7 and macosversion >= "10.3" or \ + dversion == 8 and macosversion >= "10.4": + return True + # egg isn't macosx or legacy darwin + return False + + # are they the same major version and machine type? + if provMac.group(1) != reqMac.group(1) or \ + provMac.group(3) != reqMac.group(3): + return False + + # is the required OS major update >= the provided one? + if int(provMac.group(2)) > int(reqMac.group(2)): + return False + + return True + + # XXX Linux and other platforms' special cases should go here + return False + + +def run_script(dist_spec, script_name): + """Locate distribution `dist_spec` and run its `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + require(dist_spec)[0].run_script(script_name, ns) + + +# backward compatibility +run_main = run_script + + +def get_distribution(dist): + """Return a current distribution object for a Requirement or string""" + if isinstance(dist, six.string_types): + dist = Requirement.parse(dist) + if isinstance(dist, Requirement): + dist = get_provider(dist) + if not isinstance(dist, Distribution): + raise TypeError("Expected string, Requirement, or Distribution", dist) + return dist + + +def load_entry_point(dist, group, name): + """Return `name` entry point of `group` for `dist` or raise ImportError""" + return get_distribution(dist).load_entry_point(group, name) + + +def get_entry_map(dist, group=None): + """Return the entry point map for `group`, or the full entry map""" + return get_distribution(dist).get_entry_map(group) + + +def get_entry_info(dist, group, name): + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return get_distribution(dist).get_entry_info(group, name) + + +class IMetadataProvider: + def has_metadata(name): + """Does the package's distribution contain the named metadata?""" + + def get_metadata(name): + """The named metadata resource as a string""" + + def get_metadata_lines(name): + """Yield named metadata resource as list of non-blank non-comment lines + + Leading and trailing whitespace is stripped from each line, and lines + with ``#`` as the first non-blank character are omitted.""" + + def metadata_isdir(name): + """Is the named metadata a directory? (like ``os.path.isdir()``)""" + + def metadata_listdir(name): + """List of metadata names in the directory (like ``os.listdir()``)""" + + def run_script(script_name, namespace): + """Execute the named script in the supplied namespace dictionary""" + + +class IResourceProvider(IMetadataProvider): + """An object that provides access to package resources""" + + def get_resource_filename(manager, resource_name): + """Return a true filesystem path for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_stream(manager, resource_name): + """Return a readable file-like object for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_string(manager, resource_name): + """Return a string containing the contents of `resource_name` + + `manager` must be an ``IResourceManager``""" + + def has_resource(resource_name): + """Does the package contain the named resource?""" + + def resource_isdir(resource_name): + """Is the named resource a directory? (like ``os.path.isdir()``)""" + + def resource_listdir(resource_name): + """List of resource names in the directory (like ``os.listdir()``)""" + + +class WorkingSet: + """A collection of active distributions on sys.path (or a similar list)""" + + def __init__(self, entries=None): + """Create working set from list of path entries (default=sys.path)""" + self.entries = [] + self.entry_keys = {} + self.by_key = {} + self.callbacks = [] + + if entries is None: + entries = sys.path + + for entry in entries: + self.add_entry(entry) + + @classmethod + def _build_master(cls): + """ + Prepare the master working set. + """ + ws = cls() + try: + from __main__ import __requires__ + except ImportError: + # The main program does not list any requirements + return ws + + # ensure the requirements are met + try: + ws.require(__requires__) + except VersionConflict: + return cls._build_from_requirements(__requires__) + + return ws + + @classmethod + def _build_from_requirements(cls, req_spec): + """ + Build a working set from a requirement spec. Rewrites sys.path. + """ + # try it without defaults already on sys.path + # by starting with an empty path + ws = cls([]) + reqs = parse_requirements(req_spec) + dists = ws.resolve(reqs, Environment()) + for dist in dists: + ws.add(dist) + + # add any missing entries from sys.path + for entry in sys.path: + if entry not in ws.entries: + ws.add_entry(entry) + + # then copy back to sys.path + sys.path[:] = ws.entries + return ws + + def add_entry(self, entry): + """Add a path item to ``.entries``, finding any distributions on it + + ``find_distributions(entry, True)`` is used to find distributions + corresponding to the path entry, and they are added. `entry` is + always appended to ``.entries``, even if it is already present. + (This is because ``sys.path`` can contain the same value more than + once, and the ``.entries`` of the ``sys.path`` WorkingSet should always + equal ``sys.path``.) + """ + self.entry_keys.setdefault(entry, []) + self.entries.append(entry) + for dist in find_distributions(entry, True): + self.add(dist, entry, False) + + def __contains__(self, dist): + """True if `dist` is the active distribution for its project""" + return self.by_key.get(dist.key) == dist + + def find(self, req): + """Find a distribution matching requirement `req` + + If there is an active distribution for the requested project, this + returns it as long as it meets the version requirement specified by + `req`. But, if there is an active distribution for the project and it + does *not* meet the `req` requirement, ``VersionConflict`` is raised. + If there is no active distribution for the requested project, ``None`` + is returned. + """ + dist = self.by_key.get(req.key) + if dist is not None and dist not in req: + # XXX add more info + raise VersionConflict(dist, req) + return dist + + def iter_entry_points(self, group, name=None): + """Yield entry point objects from `group` matching `name` + + If `name` is None, yields all entry points in `group` from all + distributions in the working set, otherwise only ones matching + both `group` and `name` are yielded (in distribution order). + """ + return ( + entry + for dist in self + for entry in dist.get_entry_map(group).values() + if name is None or name == entry.name + ) + + def run_script(self, requires, script_name): + """Locate distribution for `requires` and run `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + self.require(requires)[0].run_script(script_name, ns) + + def __iter__(self): + """Yield distributions for non-duplicate projects in the working set + + The yield order is the order in which the items' path entries were + added to the working set. + """ + seen = {} + for item in self.entries: + if item not in self.entry_keys: + # workaround a cache issue + continue + + for key in self.entry_keys[item]: + if key not in seen: + seen[key] = 1 + yield self.by_key[key] + + def add(self, dist, entry=None, insert=True, replace=False): + """Add `dist` to working set, associated with `entry` + + If `entry` is unspecified, it defaults to the ``.location`` of `dist`. + On exit from this routine, `entry` is added to the end of the working + set's ``.entries`` (if it wasn't already present). + + `dist` is only added to the working set if it's for a project that + doesn't already have a distribution in the set, unless `replace=True`. + If it's added, any callbacks registered with the ``subscribe()`` method + will be called. + """ + if insert: + dist.insert_on(self.entries, entry, replace=replace) + + if entry is None: + entry = dist.location + keys = self.entry_keys.setdefault(entry, []) + keys2 = self.entry_keys.setdefault(dist.location, []) + if not replace and dist.key in self.by_key: + # ignore hidden distros + return + + self.by_key[dist.key] = dist + if dist.key not in keys: + keys.append(dist.key) + if dist.key not in keys2: + keys2.append(dist.key) + self._added_new(dist) + + def resolve(self, requirements, env=None, installer=None, + replace_conflicting=False, extras=None): + """List all distributions needed to (recursively) meet `requirements` + + `requirements` must be a sequence of ``Requirement`` objects. `env`, + if supplied, should be an ``Environment`` instance. If + not supplied, it defaults to all distributions available within any + entry or distribution in the working set. `installer`, if supplied, + will be invoked with each requirement that cannot be met by an + already-installed distribution; it should return a ``Distribution`` or + ``None``. + + Unless `replace_conflicting=True`, raises a VersionConflict exception + if + any requirements are found on the path that have the correct name but + the wrong version. Otherwise, if an `installer` is supplied it will be + invoked to obtain the correct version of the requirement and activate + it. + + `extras` is a list of the extras to be used with these requirements. + This is important because extra requirements may look like `my_req; + extra = "my_extra"`, which would otherwise be interpreted as a purely + optional requirement. Instead, we want to be able to assert that these + requirements are truly required. + """ + + # set up the stack + requirements = list(requirements)[::-1] + # set of processed requirements + processed = {} + # key -> dist + best = {} + to_activate = [] + + req_extras = _ReqExtras() + + # Mapping of requirement to set of distributions that required it; + # useful for reporting info about conflicts. + required_by = collections.defaultdict(set) + + while requirements: + # process dependencies breadth-first + req = requirements.pop(0) + if req in processed: + # Ignore cyclic or redundant dependencies + continue + + if not req_extras.markers_pass(req, extras): + continue + + dist = best.get(req.key) + if dist is None: + # Find the best distribution and add it to the map + dist = self.by_key.get(req.key) + if dist is None or (dist not in req and replace_conflicting): + ws = self + if env is None: + if dist is None: + env = Environment(self.entries) + else: + # Use an empty environment and workingset to avoid + # any further conflicts with the conflicting + # distribution + env = Environment([]) + ws = WorkingSet([]) + dist = best[req.key] = env.best_match( + req, ws, installer, + replace_conflicting=replace_conflicting + ) + if dist is None: + requirers = required_by.get(req, None) + raise DistributionNotFound(req, requirers) + to_activate.append(dist) + if dist not in req: + # Oops, the "best" so far conflicts with a dependency + dependent_req = required_by[req] + raise VersionConflict(dist, req).with_context(dependent_req) + + # push the new requirements onto the stack + new_requirements = dist.requires(req.extras)[::-1] + requirements.extend(new_requirements) + + # Register the new requirements needed by req + for new_requirement in new_requirements: + required_by[new_requirement].add(req.project_name) + req_extras[new_requirement] = req.extras + + processed[req] = True + + # return list of distros to activate + return to_activate + + def find_plugins( + self, plugin_env, full_env=None, installer=None, fallback=True): + """Find all activatable distributions in `plugin_env` + + Example usage:: + + distributions, errors = working_set.find_plugins( + Environment(plugin_dirlist) + ) + # add plugins+libs to sys.path + map(working_set.add, distributions) + # display errors + print('Could not load', errors) + + The `plugin_env` should be an ``Environment`` instance that contains + only distributions that are in the project's "plugin directory" or + directories. The `full_env`, if supplied, should be an ``Environment`` + contains all currently-available distributions. If `full_env` is not + supplied, one is created automatically from the ``WorkingSet`` this + method is called on, which will typically mean that every directory on + ``sys.path`` will be scanned for distributions. + + `installer` is a standard installer callback as used by the + ``resolve()`` method. The `fallback` flag indicates whether we should + attempt to resolve older versions of a plugin if the newest version + cannot be resolved. + + This method returns a 2-tuple: (`distributions`, `error_info`), where + `distributions` is a list of the distributions found in `plugin_env` + that were loadable, along with any other distributions that are needed + to resolve their dependencies. `error_info` is a dictionary mapping + unloadable plugin distributions to an exception instance describing the + error that occurred. Usually this will be a ``DistributionNotFound`` or + ``VersionConflict`` instance. + """ + + plugin_projects = list(plugin_env) + # scan project names in alphabetic order + plugin_projects.sort() + + error_info = {} + distributions = {} + + if full_env is None: + env = Environment(self.entries) + env += plugin_env + else: + env = full_env + plugin_env + + shadow_set = self.__class__([]) + # put all our entries in shadow_set + list(map(shadow_set.add, self)) + + for project_name in plugin_projects: + + for dist in plugin_env[project_name]: + + req = [dist.as_requirement()] + + try: + resolvees = shadow_set.resolve(req, env, installer) + + except ResolutionError as v: + # save error info + error_info[dist] = v + if fallback: + # try the next older version of project + continue + else: + # give up on this project, keep going + break + + else: + list(map(shadow_set.add, resolvees)) + distributions.update(dict.fromkeys(resolvees)) + + # success, no need to try any more versions of this project + break + + distributions = list(distributions) + distributions.sort() + + return distributions, error_info + + def require(self, *requirements): + """Ensure that distributions matching `requirements` are activated + + `requirements` must be a string or a (possibly-nested) sequence + thereof, specifying the distributions and versions required. The + return value is a sequence of the distributions that needed to be + activated to fulfill the requirements; all relevant distributions are + included, even if they were already activated in this working set. + """ + needed = self.resolve(parse_requirements(requirements)) + + for dist in needed: + self.add(dist) + + return needed + + def subscribe(self, callback, existing=True): + """Invoke `callback` for all distributions + + If `existing=True` (default), + call on all existing ones, as well. + """ + if callback in self.callbacks: + return + self.callbacks.append(callback) + if not existing: + return + for dist in self: + callback(dist) + + def _added_new(self, dist): + for callback in self.callbacks: + callback(dist) + + def __getstate__(self): + return ( + self.entries[:], self.entry_keys.copy(), self.by_key.copy(), + self.callbacks[:] + ) + + def __setstate__(self, e_k_b_c): + entries, keys, by_key, callbacks = e_k_b_c + self.entries = entries[:] + self.entry_keys = keys.copy() + self.by_key = by_key.copy() + self.callbacks = callbacks[:] + + +class _ReqExtras(dict): + """ + Map each requirement to the extras that demanded it. + """ + + def markers_pass(self, req, extras=None): + """ + Evaluate markers for req against each extra that + demanded it. + + Return False if the req has a marker and fails + evaluation. Otherwise, return True. + """ + extra_evals = ( + req.marker.evaluate({'extra': extra}) + for extra in self.get(req, ()) + (extras or (None,)) + ) + return not req.marker or any(extra_evals) + + +class Environment: + """Searchable snapshot of distributions on a search path""" + + def __init__( + self, search_path=None, platform=get_supported_platform(), + python=PY_MAJOR): + """Snapshot distributions available on a search path + + Any distributions found on `search_path` are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. + + `platform` is an optional string specifying the name of the platform + that platform-specific distributions must be compatible with. If + unspecified, it defaults to the current platform. `python` is an + optional string naming the desired version of Python (e.g. ``'3.6'``); + it defaults to the current version. + + You may explicitly set `platform` (and/or `python`) to ``None`` if you + wish to map *all* distributions, not just those compatible with the + running platform or Python version. + """ + self._distmap = {} + self.platform = platform + self.python = python + self.scan(search_path) + + def can_add(self, dist): + """Is distribution `dist` acceptable for this environment? + + The distribution must match the platform and python version + requirements specified when this environment was created, or False + is returned. + """ + py_compat = ( + self.python is None + or dist.py_version is None + or dist.py_version == self.python + ) + return py_compat and compatible_platforms(dist.platform, self.platform) + + def remove(self, dist): + """Remove `dist` from the environment""" + self._distmap[dist.key].remove(dist) + + def scan(self, search_path=None): + """Scan `search_path` for distributions usable in this environment + + Any distributions found are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. Only distributions conforming to + the platform/python version defined at initialization are added. + """ + if search_path is None: + search_path = sys.path + + for item in search_path: + for dist in find_distributions(item): + self.add(dist) + + def __getitem__(self, project_name): + """Return a newest-to-oldest list of distributions for `project_name` + + Uses case-insensitive `project_name` comparison, assuming all the + project's distributions use their project's name converted to all + lowercase as their key. + + """ + distribution_key = project_name.lower() + return self._distmap.get(distribution_key, []) + + def add(self, dist): + """Add `dist` if we ``can_add()`` it and it has not already been added + """ + if self.can_add(dist) and dist.has_version(): + dists = self._distmap.setdefault(dist.key, []) + if dist not in dists: + dists.append(dist) + dists.sort(key=operator.attrgetter('hashcmp'), reverse=True) + + def best_match( + self, req, working_set, installer=None, replace_conflicting=False): + """Find distribution best matching `req` and usable on `working_set` + + This calls the ``find(req)`` method of the `working_set` to see if a + suitable distribution is already active. (This may raise + ``VersionConflict`` if an unsuitable version of the project is already + active in the specified `working_set`.) If a suitable distribution + isn't active, this method returns the newest distribution in the + environment that meets the ``Requirement`` in `req`. If no suitable + distribution is found, and `installer` is supplied, then the result of + calling the environment's ``obtain(req, installer)`` method will be + returned. + """ + try: + dist = working_set.find(req) + except VersionConflict: + if not replace_conflicting: + raise + dist = None + if dist is not None: + return dist + for dist in self[req.key]: + if dist in req: + return dist + # try to download/install + return self.obtain(req, installer) + + def obtain(self, requirement, installer=None): + """Obtain a distribution matching `requirement` (e.g. via download) + + Obtain a distro that matches requirement (e.g. via download). In the + base ``Environment`` class, this routine just returns + ``installer(requirement)``, unless `installer` is None, in which case + None is returned instead. This method is a hook that allows subclasses + to attempt other ways of obtaining a distribution before falling back + to the `installer` argument.""" + if installer is not None: + return installer(requirement) + + def __iter__(self): + """Yield the unique project names of the available distributions""" + for key in self._distmap.keys(): + if self[key]: + yield key + + def __iadd__(self, other): + """In-place addition of a distribution or environment""" + if isinstance(other, Distribution): + self.add(other) + elif isinstance(other, Environment): + for project in other: + for dist in other[project]: + self.add(dist) + else: + raise TypeError("Can't add %r to environment" % (other,)) + return self + + def __add__(self, other): + """Add an environment or distribution to an environment""" + new = self.__class__([], platform=None, python=None) + for env in self, other: + new += env + return new + + +# XXX backward compatibility +AvailableDistributions = Environment + + +class ExtractionError(RuntimeError): + """An error occurred extracting a resource + + The following attributes are available from instances of this exception: + + manager + The resource manager that raised this exception + + cache_path + The base directory for resource extraction + + original_error + The exception instance that caused extraction to fail + """ + + +class ResourceManager: + """Manage resource extraction and packages""" + extraction_path = None + + def __init__(self): + self.cached_files = {} + + def resource_exists(self, package_or_requirement, resource_name): + """Does the named resource exist?""" + return get_provider(package_or_requirement).has_resource(resource_name) + + def resource_isdir(self, package_or_requirement, resource_name): + """Is the named resource an existing directory?""" + return get_provider(package_or_requirement).resource_isdir( + resource_name + ) + + def resource_filename(self, package_or_requirement, resource_name): + """Return a true filesystem path for specified resource""" + return get_provider(package_or_requirement).get_resource_filename( + self, resource_name + ) + + def resource_stream(self, package_or_requirement, resource_name): + """Return a readable file-like object for specified resource""" + return get_provider(package_or_requirement).get_resource_stream( + self, resource_name + ) + + def resource_string(self, package_or_requirement, resource_name): + """Return specified resource as a string""" + return get_provider(package_or_requirement).get_resource_string( + self, resource_name + ) + + def resource_listdir(self, package_or_requirement, resource_name): + """List the contents of the named resource directory""" + return get_provider(package_or_requirement).resource_listdir( + resource_name + ) + + def extraction_error(self): + """Give an error message for problems extracting file(s)""" + + old_exc = sys.exc_info()[1] + cache_path = self.extraction_path or get_default_cache() + + tmpl = textwrap.dedent(""" + Can't extract file(s) to egg cache + + The following error occurred while trying to extract file(s) + to the Python egg cache: + + {old_exc} + + The Python egg cache directory is currently set to: + + {cache_path} + + Perhaps your account does not have write access to this directory? + You can change the cache directory by setting the PYTHON_EGG_CACHE + environment variable to point to an accessible directory. + """).lstrip() + err = ExtractionError(tmpl.format(**locals())) + err.manager = self + err.cache_path = cache_path + err.original_error = old_exc + raise err + + def get_cache_path(self, archive_name, names=()): + """Return absolute location in cache for `archive_name` and `names` + + The parent directory of the resulting path will be created if it does + not already exist. `archive_name` should be the base filename of the + enclosing egg (which may not be the name of the enclosing zipfile!), + including its ".egg" extension. `names`, if provided, should be a + sequence of path name parts "under" the egg's extraction location. + + This method should only be called by resource providers that need to + obtain an extraction location, and only for names they intend to + extract, as it tracks the generated names for possible cleanup later. + """ + extract_path = self.extraction_path or get_default_cache() + target_path = os.path.join(extract_path, archive_name + '-tmp', *names) + try: + _bypass_ensure_directory(target_path) + except Exception: + self.extraction_error() + + self._warn_unsafe_extraction_path(extract_path) + + self.cached_files[target_path] = 1 + return target_path + + @staticmethod + def _warn_unsafe_extraction_path(path): + """ + If the default extraction path is overridden and set to an insecure + location, such as /tmp, it opens up an opportunity for an attacker to + replace an extracted file with an unauthorized payload. Warn the user + if a known insecure location is used. + + See Distribute #375 for more details. + """ + if os.name == 'nt' and not path.startswith(os.environ['windir']): + # On Windows, permissions are generally restrictive by default + # and temp directories are not writable by other users, so + # bypass the warning. + return + mode = os.stat(path).st_mode + if mode & stat.S_IWOTH or mode & stat.S_IWGRP: + msg = ( + "%s is writable by group/others and vulnerable to attack " + "when " + "used with get_resource_filename. Consider a more secure " + "location (set with .set_extraction_path or the " + "PYTHON_EGG_CACHE environment variable)." % path + ) + warnings.warn(msg, UserWarning) + + def postprocess(self, tempname, filename): + """Perform any platform-specific postprocessing of `tempname` + + This is where Mac header rewrites should be done; other platforms don't + have anything special they should do. + + Resource providers should call this method ONLY after successfully + extracting a compressed resource. They must NOT call it on resources + that are already in the filesystem. + + `tempname` is the current (temporary) name of the file, and `filename` + is the name it will be renamed to by the caller after this routine + returns. + """ + + if os.name == 'posix': + # Make the resource executable + mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777 + os.chmod(tempname, mode) + + def set_extraction_path(self, path): + """Set the base path where resources will be extracted to, if needed. + + If you do not call this routine before any extractions take place, the + path defaults to the return value of ``get_default_cache()``. (Which + is based on the ``PYTHON_EGG_CACHE`` environment variable, with various + platform-specific fallbacks. See that routine's documentation for more + details.) + + Resources are extracted to subdirectories of this path based upon + information given by the ``IResourceProvider``. You may set this to a + temporary directory, but then you must call ``cleanup_resources()`` to + delete the extracted files when done. There is no guarantee that + ``cleanup_resources()`` will be able to remove all extracted files. + + (Note: you may not change the extraction path for a given resource + manager once resources have been extracted, unless you first call + ``cleanup_resources()``.) + """ + if self.cached_files: + raise ValueError( + "Can't change extraction path, files already extracted" + ) + + self.extraction_path = path + + def cleanup_resources(self, force=False): + """ + Delete all extracted resource files and directories, returning a list + of the file and directory names that could not be successfully removed. + This function does not have any concurrency protection, so it should + generally only be called when the extraction path is a temporary + directory exclusive to a single process. This method is not + automatically called; you must call it explicitly or register it as an + ``atexit`` function if you wish to ensure cleanup of a temporary + directory used for extractions. + """ + # XXX + + +def get_default_cache(): + """ + Return the ``PYTHON_EGG_CACHE`` environment variable + or a platform-relevant user cache dir for an app + named "Python-Eggs". + """ + return ( + os.environ.get('PYTHON_EGG_CACHE') + or appdirs.user_cache_dir(appname='Python-Eggs') + ) + + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """ + Convert an arbitrary string to a standard version string + """ + try: + # normalize the version + return str(packaging.version.Version(version)) + except packaging.version.InvalidVersion: + version = version.replace(' ', '.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def safe_extra(extra): + """Convert an arbitrary string to a standard 'extra' name + + Any runs of non-alphanumeric characters are replaced with a single '_', + and the result is always lowercased. + """ + return re.sub('[^A-Za-z0-9.-]+', '_', extra).lower() + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-', '_') + + +def invalid_marker(text): + """ + Validate text as a PEP 508 environment marker; return an exception + if invalid or False otherwise. + """ + try: + evaluate_marker(text) + except SyntaxError as e: + e.filename = None + e.lineno = None + return e + return False + + +def evaluate_marker(text, extra=None): + """ + Evaluate a PEP 508 environment marker. + Return a boolean indicating the marker result in this environment. + Raise SyntaxError if marker is invalid. + + This implementation uses the 'pyparsing' module. + """ + try: + marker = packaging.markers.Marker(text) + return marker.evaluate() + except packaging.markers.InvalidMarker as e: + raise SyntaxError(e) + + +class NullProvider: + """Try to implement resources and metadata for arbitrary PEP 302 loaders""" + + egg_name = None + egg_info = None + loader = None + + def __init__(self, module): + self.loader = getattr(module, '__loader__', None) + self.module_path = os.path.dirname(getattr(module, '__file__', '')) + + def get_resource_filename(self, manager, resource_name): + return self._fn(self.module_path, resource_name) + + def get_resource_stream(self, manager, resource_name): + return io.BytesIO(self.get_resource_string(manager, resource_name)) + + def get_resource_string(self, manager, resource_name): + return self._get(self._fn(self.module_path, resource_name)) + + def has_resource(self, resource_name): + return self._has(self._fn(self.module_path, resource_name)) + + def has_metadata(self, name): + return self.egg_info and self._has(self._fn(self.egg_info, name)) + + def get_metadata(self, name): + if not self.egg_info: + return "" + value = self._get(self._fn(self.egg_info, name)) + return value.decode('utf-8') if six.PY3 else value + + def get_metadata_lines(self, name): + return yield_lines(self.get_metadata(name)) + + def resource_isdir(self, resource_name): + return self._isdir(self._fn(self.module_path, resource_name)) + + def metadata_isdir(self, name): + return self.egg_info and self._isdir(self._fn(self.egg_info, name)) + + def resource_listdir(self, resource_name): + return self._listdir(self._fn(self.module_path, resource_name)) + + def metadata_listdir(self, name): + if self.egg_info: + return self._listdir(self._fn(self.egg_info, name)) + return [] + + def run_script(self, script_name, namespace): + script = 'scripts/' + script_name + if not self.has_metadata(script): + raise ResolutionError( + "Script {script!r} not found in metadata at {self.egg_info!r}" + .format(**locals()), + ) + script_text = self.get_metadata(script).replace('\r\n', '\n') + script_text = script_text.replace('\r', '\n') + script_filename = self._fn(self.egg_info, script) + namespace['__file__'] = script_filename + if os.path.exists(script_filename): + source = open(script_filename).read() + code = compile(source, script_filename, 'exec') + exec(code, namespace, namespace) + else: + from linecache import cache + cache[script_filename] = ( + len(script_text), 0, script_text.split('\n'), script_filename + ) + script_code = compile(script_text, script_filename, 'exec') + exec(script_code, namespace, namespace) + + def _has(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _isdir(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _listdir(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _fn(self, base, resource_name): + if resource_name: + return os.path.join(base, *resource_name.split('/')) + return base + + def _get(self, path): + if hasattr(self.loader, 'get_data'): + return self.loader.get_data(path) + raise NotImplementedError( + "Can't perform this operation for loaders without 'get_data()'" + ) + + +register_loader_type(object, NullProvider) + + +class EggProvider(NullProvider): + """Provider based on a virtual filesystem""" + + def __init__(self, module): + NullProvider.__init__(self, module) + self._setup_prefix() + + def _setup_prefix(self): + # we assume here that our metadata may be nested inside a "basket" + # of multiple eggs; that's why we use module_path instead of .archive + path = self.module_path + old = None + while path != old: + if _is_egg_path(path): + self.egg_name = os.path.basename(path) + self.egg_info = os.path.join(path, 'EGG-INFO') + self.egg_root = path + break + old = path + path, base = os.path.split(path) + + +class DefaultProvider(EggProvider): + """Provides access to package resources in the filesystem""" + + def _has(self, path): + return os.path.exists(path) + + def _isdir(self, path): + return os.path.isdir(path) + + def _listdir(self, path): + return os.listdir(path) + + def get_resource_stream(self, manager, resource_name): + return open(self._fn(self.module_path, resource_name), 'rb') + + def _get(self, path): + with open(path, 'rb') as stream: + return stream.read() + + @classmethod + def _register(cls): + loader_names = 'SourceFileLoader', 'SourcelessFileLoader', + for name in loader_names: + loader_cls = getattr(importlib_machinery, name, type(None)) + register_loader_type(loader_cls, cls) + + +DefaultProvider._register() + + +class EmptyProvider(NullProvider): + """Provider that returns nothing for all requests""" + + module_path = None + + _isdir = _has = lambda self, path: False + + def _get(self, path): + return '' + + def _listdir(self, path): + return [] + + def __init__(self): + pass + + +empty_provider = EmptyProvider() + + +class ZipManifests(dict): + """ + zip manifest builder + """ + + @classmethod + def build(cls, path): + """ + Build a dictionary similar to the zipimport directory + caches, except instead of tuples, store ZipInfo objects. + + Use a platform-specific path separator (os.sep) for the path keys + for compatibility with pypy on Windows. + """ + with zipfile.ZipFile(path) as zfile: + items = ( + ( + name.replace('/', os.sep), + zfile.getinfo(name), + ) + for name in zfile.namelist() + ) + return dict(items) + + load = build + + +class MemoizedZipManifests(ZipManifests): + """ + Memoized zipfile manifests. + """ + manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime') + + def load(self, path): + """ + Load a manifest at path or return a suitable manifest already loaded. + """ + path = os.path.normpath(path) + mtime = os.stat(path).st_mtime + + if path not in self or self[path].mtime != mtime: + manifest = self.build(path) + self[path] = self.manifest_mod(manifest, mtime) + + return self[path].manifest + + +class ZipProvider(EggProvider): + """Resource support for zips and eggs""" + + eagers = None + _zip_manifests = MemoizedZipManifests() + + def __init__(self, module): + EggProvider.__init__(self, module) + self.zip_pre = self.loader.archive + os.sep + + def _zipinfo_name(self, fspath): + # Convert a virtual filename (full path to file) into a zipfile subpath + # usable with the zipimport directory cache for our target archive + fspath = fspath.rstrip(os.sep) + if fspath == self.loader.archive: + return '' + if fspath.startswith(self.zip_pre): + return fspath[len(self.zip_pre):] + raise AssertionError( + "%s is not a subpath of %s" % (fspath, self.zip_pre) + ) + + def _parts(self, zip_path): + # Convert a zipfile subpath into an egg-relative path part list. + # pseudo-fs path + fspath = self.zip_pre + zip_path + if fspath.startswith(self.egg_root + os.sep): + return fspath[len(self.egg_root) + 1:].split(os.sep) + raise AssertionError( + "%s is not a subpath of %s" % (fspath, self.egg_root) + ) + + @property + def zipinfo(self): + return self._zip_manifests.load(self.loader.archive) + + def get_resource_filename(self, manager, resource_name): + if not self.egg_name: + raise NotImplementedError( + "resource_filename() only supported for .egg, not .zip" + ) + # no need to lock for extraction, since we use temp names + zip_path = self._resource_to_zip(resource_name) + eagers = self._get_eager_resources() + if '/'.join(self._parts(zip_path)) in eagers: + for name in eagers: + self._extract_resource(manager, self._eager_to_zip(name)) + return self._extract_resource(manager, zip_path) + + @staticmethod + def _get_date_and_size(zip_stat): + size = zip_stat.file_size + # ymdhms+wday, yday, dst + date_time = zip_stat.date_time + (0, 0, -1) + # 1980 offset already done + timestamp = time.mktime(date_time) + return timestamp, size + + def _extract_resource(self, manager, zip_path): + + if zip_path in self._index(): + for name in self._index()[zip_path]: + last = self._extract_resource( + manager, os.path.join(zip_path, name) + ) + # return the extracted directory name + return os.path.dirname(last) + + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + + if not WRITE_SUPPORT: + raise IOError('"os.rename" and "os.unlink" are not supported ' + 'on this platform') + try: + + real_path = manager.get_cache_path( + self.egg_name, self._parts(zip_path) + ) + + if self._is_current(real_path, zip_path): + return real_path + + outf, tmpnam = _mkstemp( + ".$extract", + dir=os.path.dirname(real_path), + ) + os.write(outf, self.loader.get_data(zip_path)) + os.close(outf) + utime(tmpnam, (timestamp, timestamp)) + manager.postprocess(tmpnam, real_path) + + try: + rename(tmpnam, real_path) + + except os.error: + if os.path.isfile(real_path): + if self._is_current(real_path, zip_path): + # the file became current since it was checked above, + # so proceed. + return real_path + # Windows, del old file and retry + elif os.name == 'nt': + unlink(real_path) + rename(tmpnam, real_path) + return real_path + raise + + except os.error: + # report a user-friendly error + manager.extraction_error() + + return real_path + + def _is_current(self, file_path, zip_path): + """ + Return True if the file_path is current for this zip_path + """ + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + if not os.path.isfile(file_path): + return False + stat = os.stat(file_path) + if stat.st_size != size or stat.st_mtime != timestamp: + return False + # check that the contents match + zip_contents = self.loader.get_data(zip_path) + with open(file_path, 'rb') as f: + file_contents = f.read() + return zip_contents == file_contents + + def _get_eager_resources(self): + if self.eagers is None: + eagers = [] + for name in ('native_libs.txt', 'eager_resources.txt'): + if self.has_metadata(name): + eagers.extend(self.get_metadata_lines(name)) + self.eagers = eagers + return self.eagers + + def _index(self): + try: + return self._dirindex + except AttributeError: + ind = {} + for path in self.zipinfo: + parts = path.split(os.sep) + while parts: + parent = os.sep.join(parts[:-1]) + if parent in ind: + ind[parent].append(parts[-1]) + break + else: + ind[parent] = [parts.pop()] + self._dirindex = ind + return ind + + def _has(self, fspath): + zip_path = self._zipinfo_name(fspath) + return zip_path in self.zipinfo or zip_path in self._index() + + def _isdir(self, fspath): + return self._zipinfo_name(fspath) in self._index() + + def _listdir(self, fspath): + return list(self._index().get(self._zipinfo_name(fspath), ())) + + def _eager_to_zip(self, resource_name): + return self._zipinfo_name(self._fn(self.egg_root, resource_name)) + + def _resource_to_zip(self, resource_name): + return self._zipinfo_name(self._fn(self.module_path, resource_name)) + + +register_loader_type(zipimport.zipimporter, ZipProvider) + + +class FileMetadata(EmptyProvider): + """Metadata handler for standalone PKG-INFO files + + Usage:: + + metadata = FileMetadata("/path/to/PKG-INFO") + + This provider rejects all data and metadata requests except for PKG-INFO, + which is treated as existing, and will be the contents of the file at + the provided location. + """ + + def __init__(self, path): + self.path = path + + def has_metadata(self, name): + return name == 'PKG-INFO' and os.path.isfile(self.path) + + def get_metadata(self, name): + if name != 'PKG-INFO': + raise KeyError("No metadata except PKG-INFO is available") + + with io.open(self.path, encoding='utf-8', errors="replace") as f: + metadata = f.read() + self._warn_on_replacement(metadata) + return metadata + + def _warn_on_replacement(self, metadata): + # Python 2.7 compat for: replacement_char = '�' + replacement_char = b'\xef\xbf\xbd'.decode('utf-8') + if replacement_char in metadata: + tmpl = "{self.path} could not be properly decoded in UTF-8" + msg = tmpl.format(**locals()) + warnings.warn(msg) + + def get_metadata_lines(self, name): + return yield_lines(self.get_metadata(name)) + + +class PathMetadata(DefaultProvider): + """Metadata provider for egg directories + + Usage:: + + # Development eggs: + + egg_info = "/path/to/PackageName.egg-info" + base_dir = os.path.dirname(egg_info) + metadata = PathMetadata(base_dir, egg_info) + dist_name = os.path.splitext(os.path.basename(egg_info))[0] + dist = Distribution(basedir, project_name=dist_name, metadata=metadata) + + # Unpacked egg directories: + + egg_path = "/path/to/PackageName-ver-pyver-etc.egg" + metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO')) + dist = Distribution.from_filename(egg_path, metadata=metadata) + """ + + def __init__(self, path, egg_info): + self.module_path = path + self.egg_info = egg_info + + +class EggMetadata(ZipProvider): + """Metadata provider for .egg files""" + + def __init__(self, importer): + """Create a metadata provider from a zipimporter""" + + self.zip_pre = importer.archive + os.sep + self.loader = importer + if importer.prefix: + self.module_path = os.path.join(importer.archive, importer.prefix) + else: + self.module_path = importer.archive + self._setup_prefix() + + +_declare_state('dict', _distribution_finders={}) + + +def register_finder(importer_type, distribution_finder): + """Register `distribution_finder` to find distributions in sys.path items + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `distribution_finder` is a callable that, passed a path + item and the importer instance, yields ``Distribution`` instances found on + that path item. See ``pkg_resources.find_on_path`` for an example.""" + _distribution_finders[importer_type] = distribution_finder + + +def find_distributions(path_item, only=False): + """Yield distributions accessible via `path_item`""" + importer = get_importer(path_item) + finder = _find_adapter(_distribution_finders, importer) + return finder(importer, path_item, only) + + +def find_eggs_in_zip(importer, path_item, only=False): + """ + Find eggs in zip files; possibly multiple nested eggs. + """ + if importer.archive.endswith('.whl'): + # wheels are not supported with this finder + # they don't have PKG-INFO metadata, and won't ever contain eggs + return + metadata = EggMetadata(importer) + if metadata.has_metadata('PKG-INFO'): + yield Distribution.from_filename(path_item, metadata=metadata) + if only: + # don't yield nested distros + return + for subitem in metadata.resource_listdir('/'): + if _is_egg_path(subitem): + subpath = os.path.join(path_item, subitem) + dists = find_eggs_in_zip(zipimport.zipimporter(subpath), subpath) + for dist in dists: + yield dist + elif subitem.lower().endswith('.dist-info'): + subpath = os.path.join(path_item, subitem) + submeta = EggMetadata(zipimport.zipimporter(subpath)) + submeta.egg_info = subpath + yield Distribution.from_location(path_item, subitem, submeta) + + +register_finder(zipimport.zipimporter, find_eggs_in_zip) + + +def find_nothing(importer, path_item, only=False): + return () + + +register_finder(object, find_nothing) + + +def _by_version_descending(names): + """ + Given a list of filenames, return them in descending order + by version number. + + >>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg' + >>> _by_version_descending(names) + ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'foo', 'bar'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.post1.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg'] + """ + def _by_version(name): + """ + Parse each component of the filename + """ + name, ext = os.path.splitext(name) + parts = itertools.chain(name.split('-'), [ext]) + return [packaging.version.parse(part) for part in parts] + + return sorted(names, key=_by_version, reverse=True) + + +def find_on_path(importer, path_item, only=False): + """Yield distributions accessible on a sys.path directory""" + path_item = _normalize_cached(path_item) + + if _is_unpacked_egg(path_item): + yield Distribution.from_filename( + path_item, metadata=PathMetadata( + path_item, os.path.join(path_item, 'EGG-INFO') + ) + ) + return + + entries = safe_listdir(path_item) + + # for performance, before sorting by version, + # screen entries for only those that will yield + # distributions + filtered = ( + entry + for entry in entries + if dist_factory(path_item, entry, only) + ) + + # scan for .egg and .egg-info in directory + path_item_entries = _by_version_descending(filtered) + for entry in path_item_entries: + fullpath = os.path.join(path_item, entry) + factory = dist_factory(path_item, entry, only) + for dist in factory(fullpath): + yield dist + + +def dist_factory(path_item, entry, only): + """ + Return a dist_factory for a path_item and entry + """ + lower = entry.lower() + is_meta = any(map(lower.endswith, ('.egg-info', '.dist-info'))) + return ( + distributions_from_metadata + if is_meta else + find_distributions + if not only and _is_egg_path(entry) else + resolve_egg_link + if not only and lower.endswith('.egg-link') else + NoDists() + ) + + +class NoDists: + """ + >>> bool(NoDists()) + False + + >>> list(NoDists()('anything')) + [] + """ + def __bool__(self): + return False + if six.PY2: + __nonzero__ = __bool__ + + def __call__(self, fullpath): + return iter(()) + + +def safe_listdir(path): + """ + Attempt to list contents of path, but suppress some exceptions. + """ + try: + return os.listdir(path) + except (PermissionError, NotADirectoryError): + pass + except OSError as e: + # Ignore the directory if does not exist, not a directory or + # permission denied + ignorable = ( + e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT) + # Python 2 on Windows needs to be handled this way :( + or getattr(e, "winerror", None) == 267 + ) + if not ignorable: + raise + return () + + +def distributions_from_metadata(path): + root = os.path.dirname(path) + if os.path.isdir(path): + if len(os.listdir(path)) == 0: + # empty metadata dir; skip + return + metadata = PathMetadata(root, path) + else: + metadata = FileMetadata(path) + entry = os.path.basename(path) + yield Distribution.from_location( + root, entry, metadata, precedence=DEVELOP_DIST, + ) + + +def non_empty_lines(path): + """ + Yield non-empty lines from file at path + """ + with open(path) as f: + for line in f: + line = line.strip() + if line: + yield line + + +def resolve_egg_link(path): + """ + Given a path to an .egg-link, resolve distributions + present in the referenced path. + """ + referenced_paths = non_empty_lines(path) + resolved_paths = ( + os.path.join(os.path.dirname(path), ref) + for ref in referenced_paths + ) + dist_groups = map(find_distributions, resolved_paths) + return next(dist_groups, ()) + + +register_finder(pkgutil.ImpImporter, find_on_path) + +if hasattr(importlib_machinery, 'FileFinder'): + register_finder(importlib_machinery.FileFinder, find_on_path) + +_declare_state('dict', _namespace_handlers={}) +_declare_state('dict', _namespace_packages={}) + + +def register_namespace_handler(importer_type, namespace_handler): + """Register `namespace_handler` to declare namespace packages + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `namespace_handler` is a callable like this:: + + def namespace_handler(importer, path_entry, moduleName, module): + # return a path_entry to use for child packages + + Namespace handlers are only called if the importer object has already + agreed that it can handle the relevant path item, and they should only + return a subpath if the module __path__ does not already contain an + equivalent subpath. For an example namespace handler, see + ``pkg_resources.file_ns_handler``. + """ + _namespace_handlers[importer_type] = namespace_handler + + +def _handle_ns(packageName, path_item): + """Ensure that named package includes a subpath of path_item (if needed)""" + + importer = get_importer(path_item) + if importer is None: + return None + + # capture warnings due to #1111 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + loader = importer.find_module(packageName) + + if loader is None: + return None + module = sys.modules.get(packageName) + if module is None: + module = sys.modules[packageName] = types.ModuleType(packageName) + module.__path__ = [] + _set_parent_ns(packageName) + elif not hasattr(module, '__path__'): + raise TypeError("Not a package:", packageName) + handler = _find_adapter(_namespace_handlers, importer) + subpath = handler(importer, path_item, packageName, module) + if subpath is not None: + path = module.__path__ + path.append(subpath) + loader.load_module(packageName) + _rebuild_mod_path(path, packageName, module) + return subpath + + +def _rebuild_mod_path(orig_path, package_name, module): + """ + Rebuild module.__path__ ensuring that all entries are ordered + corresponding to their sys.path order + """ + sys_path = [_normalize_cached(p) for p in sys.path] + + def safe_sys_path_index(entry): + """ + Workaround for #520 and #513. + """ + try: + return sys_path.index(entry) + except ValueError: + return float('inf') + + def position_in_sys_path(path): + """ + Return the ordinal of the path based on its position in sys.path + """ + path_parts = path.split(os.sep) + module_parts = package_name.count('.') + 1 + parts = path_parts[:-module_parts] + return safe_sys_path_index(_normalize_cached(os.sep.join(parts))) + + new_path = sorted(orig_path, key=position_in_sys_path) + new_path = [_normalize_cached(p) for p in new_path] + + if isinstance(module.__path__, list): + module.__path__[:] = new_path + else: + module.__path__ = new_path + + +def declare_namespace(packageName): + """Declare that package 'packageName' is a namespace package""" + + _imp.acquire_lock() + try: + if packageName in _namespace_packages: + return + + path = sys.path + parent, _, _ = packageName.rpartition('.') + + if parent: + declare_namespace(parent) + if parent not in _namespace_packages: + __import__(parent) + try: + path = sys.modules[parent].__path__ + except AttributeError: + raise TypeError("Not a package:", parent) + + # Track what packages are namespaces, so when new path items are added, + # they can be updated + _namespace_packages.setdefault(parent or None, []).append(packageName) + _namespace_packages.setdefault(packageName, []) + + for path_item in path: + # Ensure all the parent's path items are reflected in the child, + # if they apply + _handle_ns(packageName, path_item) + + finally: + _imp.release_lock() + + +def fixup_namespace_packages(path_item, parent=None): + """Ensure that previously-declared namespace packages include path_item""" + _imp.acquire_lock() + try: + for package in _namespace_packages.get(parent, ()): + subpath = _handle_ns(package, path_item) + if subpath: + fixup_namespace_packages(subpath, package) + finally: + _imp.release_lock() + + +def file_ns_handler(importer, path_item, packageName, module): + """Compute an ns-package subpath for a filesystem or zipfile importer""" + + subpath = os.path.join(path_item, packageName.split('.')[-1]) + normalized = _normalize_cached(subpath) + for item in module.__path__: + if _normalize_cached(item) == normalized: + break + else: + # Only return the path if it's not already there + return subpath + + +register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) +register_namespace_handler(zipimport.zipimporter, file_ns_handler) + +if hasattr(importlib_machinery, 'FileFinder'): + register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler) + + +def null_ns_handler(importer, path_item, packageName, module): + return None + + +register_namespace_handler(object, null_ns_handler) + + +def normalize_path(filename): + """Normalize a file/dir name for comparison purposes""" + return os.path.normcase(os.path.realpath(filename)) + + +def _normalize_cached(filename, _cache={}): + try: + return _cache[filename] + except KeyError: + _cache[filename] = result = normalize_path(filename) + return result + + +def _is_egg_path(path): + """ + Determine if given path appears to be an egg. + """ + return path.lower().endswith('.egg') + + +def _is_unpacked_egg(path): + """ + Determine if given path appears to be an unpacked egg. + """ + return ( + _is_egg_path(path) and + os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO')) + ) + + +def _set_parent_ns(packageName): + parts = packageName.split('.') + name = parts.pop() + if parts: + parent = '.'.join(parts) + setattr(sys.modules[parent], name, sys.modules[packageName]) + + +def yield_lines(strs): + """Yield non-empty/non-comment lines of a string or sequence""" + if isinstance(strs, six.string_types): + for s in strs.splitlines(): + s = s.strip() + # skip blank lines/comments + if s and not s.startswith('#'): + yield s + else: + for ss in strs: + for s in yield_lines(ss): + yield s + + +MODULE = re.compile(r"\w+(\.\w+)*$").match +EGG_NAME = re.compile( + r""" + (?P<name>[^-]+) ( + -(?P<ver>[^-]+) ( + -py(?P<pyver>[^-]+) ( + -(?P<plat>.+) + )? + )? + )? + """, + re.VERBOSE | re.IGNORECASE, +).match + + +class EntryPoint: + """Object representing an advertised importable object""" + + def __init__(self, name, module_name, attrs=(), extras=(), dist=None): + if not MODULE(module_name): + raise ValueError("Invalid module name", module_name) + self.name = name + self.module_name = module_name + self.attrs = tuple(attrs) + self.extras = tuple(extras) + self.dist = dist + + def __str__(self): + s = "%s = %s" % (self.name, self.module_name) + if self.attrs: + s += ':' + '.'.join(self.attrs) + if self.extras: + s += ' [%s]' % ','.join(self.extras) + return s + + def __repr__(self): + return "EntryPoint.parse(%r)" % str(self) + + def load(self, require=True, *args, **kwargs): + """ + Require packages for this EntryPoint, then resolve it. + """ + if not require or args or kwargs: + warnings.warn( + "Parameters to load are deprecated. Call .resolve and " + ".require separately.", + DeprecationWarning, + stacklevel=2, + ) + if require: + self.require(*args, **kwargs) + return self.resolve() + + def resolve(self): + """ + Resolve the entry point from its module and attrs. + """ + module = __import__(self.module_name, fromlist=['__name__'], level=0) + try: + return functools.reduce(getattr, self.attrs, module) + except AttributeError as exc: + raise ImportError(str(exc)) + + def require(self, env=None, installer=None): + if self.extras and not self.dist: + raise UnknownExtra("Can't require() without a distribution", self) + + # Get the requirements for this entry point with all its extras and + # then resolve them. We have to pass `extras` along when resolving so + # that the working set knows what extras we want. Otherwise, for + # dist-info distributions, the working set will assume that the + # requirements for that extra are purely optional and skip over them. + reqs = self.dist.requires(self.extras) + items = working_set.resolve(reqs, env, installer, extras=self.extras) + list(map(working_set.add, items)) + + pattern = re.compile( + r'\s*' + r'(?P<name>.+?)\s*' + r'=\s*' + r'(?P<module>[\w.]+)\s*' + r'(:\s*(?P<attr>[\w.]+))?\s*' + r'(?P<extras>\[.*\])?\s*$' + ) + + @classmethod + def parse(cls, src, dist=None): + """Parse a single entry point from string `src` + + Entry point syntax follows the form:: + + name = some.module:some.attr [extra1, extra2] + + The entry name and module name are required, but the ``:attrs`` and + ``[extras]`` parts are optional + """ + m = cls.pattern.match(src) + if not m: + msg = "EntryPoint must be in 'name=module:attrs [extras]' format" + raise ValueError(msg, src) + res = m.groupdict() + extras = cls._parse_extras(res['extras']) + attrs = res['attr'].split('.') if res['attr'] else () + return cls(res['name'], res['module'], attrs, extras, dist) + + @classmethod + def _parse_extras(cls, extras_spec): + if not extras_spec: + return () + req = Requirement.parse('x' + extras_spec) + if req.specs: + raise ValueError() + return req.extras + + @classmethod + def parse_group(cls, group, lines, dist=None): + """Parse an entry point group""" + if not MODULE(group): + raise ValueError("Invalid group name", group) + this = {} + for line in yield_lines(lines): + ep = cls.parse(line, dist) + if ep.name in this: + raise ValueError("Duplicate entry point", group, ep.name) + this[ep.name] = ep + return this + + @classmethod + def parse_map(cls, data, dist=None): + """Parse a map of entry point groups""" + if isinstance(data, dict): + data = data.items() + else: + data = split_sections(data) + maps = {} + for group, lines in data: + if group is None: + if not lines: + continue + raise ValueError("Entry points must be listed in groups") + group = group.strip() + if group in maps: + raise ValueError("Duplicate group name", group) + maps[group] = cls.parse_group(group, lines, dist) + return maps + + +def _remove_md5_fragment(location): + if not location: + return '' + parsed = urllib.parse.urlparse(location) + if parsed[-1].startswith('md5='): + return urllib.parse.urlunparse(parsed[:-1] + ('',)) + return location + + +def _version_from_file(lines): + """ + Given an iterable of lines from a Metadata file, return + the value of the Version field, if present, or None otherwise. + """ + def is_version_line(line): + return line.lower().startswith('version:') + version_lines = filter(is_version_line, lines) + line = next(iter(version_lines), '') + _, _, value = line.partition(':') + return safe_version(value.strip()) or None + + +class Distribution: + """Wrap an actual or potential sys.path entry w/metadata""" + PKG_INFO = 'PKG-INFO' + + def __init__( + self, location=None, metadata=None, project_name=None, + version=None, py_version=PY_MAJOR, platform=None, + precedence=EGG_DIST): + self.project_name = safe_name(project_name or 'Unknown') + if version is not None: + self._version = safe_version(version) + self.py_version = py_version + self.platform = platform + self.location = location + self.precedence = precedence + self._provider = metadata or empty_provider + + @classmethod + def from_location(cls, location, basename, metadata=None, **kw): + project_name, version, py_version, platform = [None] * 4 + basename, ext = os.path.splitext(basename) + if ext.lower() in _distributionImpl: + cls = _distributionImpl[ext.lower()] + + match = EGG_NAME(basename) + if match: + project_name, version, py_version, platform = match.group( + 'name', 'ver', 'pyver', 'plat' + ) + return cls( + location, metadata, project_name=project_name, version=version, + py_version=py_version, platform=platform, **kw + )._reload_version() + + def _reload_version(self): + return self + + @property + def hashcmp(self): + return ( + self.parsed_version, + self.precedence, + self.key, + _remove_md5_fragment(self.location), + self.py_version or '', + self.platform or '', + ) + + def __hash__(self): + return hash(self.hashcmp) + + def __lt__(self, other): + return self.hashcmp < other.hashcmp + + def __le__(self, other): + return self.hashcmp <= other.hashcmp + + def __gt__(self, other): + return self.hashcmp > other.hashcmp + + def __ge__(self, other): + return self.hashcmp >= other.hashcmp + + def __eq__(self, other): + if not isinstance(other, self.__class__): + # It's not a Distribution, so they are not equal + return False + return self.hashcmp == other.hashcmp + + def __ne__(self, other): + return not self == other + + # These properties have to be lazy so that we don't have to load any + # metadata until/unless it's actually needed. (i.e., some distributions + # may not know their name or version without loading PKG-INFO) + + @property + def key(self): + try: + return self._key + except AttributeError: + self._key = key = self.project_name.lower() + return key + + @property + def parsed_version(self): + if not hasattr(self, "_parsed_version"): + self._parsed_version = parse_version(self.version) + + return self._parsed_version + + def _warn_legacy_version(self): + LV = packaging.version.LegacyVersion + is_legacy = isinstance(self._parsed_version, LV) + if not is_legacy: + return + + # While an empty version is technically a legacy version and + # is not a valid PEP 440 version, it's also unlikely to + # actually come from someone and instead it is more likely that + # it comes from setuptools attempting to parse a filename and + # including it in the list. So for that we'll gate this warning + # on if the version is anything at all or not. + if not self.version: + return + + tmpl = textwrap.dedent(""" + '{project_name} ({version})' is being parsed as a legacy, + non PEP 440, + version. You may find odd behavior and sort order. + In particular it will be sorted as less than 0.0. It + is recommended to migrate to PEP 440 compatible + versions. + """).strip().replace('\n', ' ') + + warnings.warn(tmpl.format(**vars(self)), PEP440Warning) + + @property + def version(self): + try: + return self._version + except AttributeError: + version = _version_from_file(self._get_metadata(self.PKG_INFO)) + if version is None: + tmpl = "Missing 'Version:' header and/or %s file" + raise ValueError(tmpl % self.PKG_INFO, self) + return version + + @property + def _dep_map(self): + """ + A map of extra to its list of (direct) requirements + for this distribution, including the null extra. + """ + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._filter_extras(self._build_dep_map()) + return self.__dep_map + + @staticmethod + def _filter_extras(dm): + """ + Given a mapping of extras to dependencies, strip off + environment markers and filter out any dependencies + not matching the markers. + """ + for extra in list(filter(None, dm)): + new_extra = extra + reqs = dm.pop(extra) + new_extra, _, marker = extra.partition(':') + fails_marker = marker and ( + invalid_marker(marker) + or not evaluate_marker(marker) + ) + if fails_marker: + reqs = [] + new_extra = safe_extra(new_extra) or None + + dm.setdefault(new_extra, []).extend(reqs) + return dm + + def _build_dep_map(self): + dm = {} + for name in 'requires.txt', 'depends.txt': + for extra, reqs in split_sections(self._get_metadata(name)): + dm.setdefault(extra, []).extend(parse_requirements(reqs)) + return dm + + def requires(self, extras=()): + """List of Requirements needed for this distro if `extras` are used""" + dm = self._dep_map + deps = [] + deps.extend(dm.get(None, ())) + for ext in extras: + try: + deps.extend(dm[safe_extra(ext)]) + except KeyError: + raise UnknownExtra( + "%s has no such extra feature %r" % (self, ext) + ) + return deps + + def _get_metadata(self, name): + if self.has_metadata(name): + for line in self.get_metadata_lines(name): + yield line + + def activate(self, path=None, replace=False): + """Ensure distribution is importable on `path` (default=sys.path)""" + if path is None: + path = sys.path + self.insert_on(path, replace=replace) + if path is sys.path: + fixup_namespace_packages(self.location) + for pkg in self._get_metadata('namespace_packages.txt'): + if pkg in sys.modules: + declare_namespace(pkg) + + def egg_name(self): + """Return what this distribution's standard .egg filename should be""" + filename = "%s-%s-py%s" % ( + to_filename(self.project_name), to_filename(self.version), + self.py_version or PY_MAJOR + ) + + if self.platform: + filename += '-' + self.platform + return filename + + def __repr__(self): + if self.location: + return "%s (%s)" % (self, self.location) + else: + return str(self) + + def __str__(self): + try: + version = getattr(self, 'version', None) + except ValueError: + version = None + version = version or "[unknown version]" + return "%s %s" % (self.project_name, version) + + def __getattr__(self, attr): + """Delegate all unrecognized public attributes to .metadata provider""" + if attr.startswith('_'): + raise AttributeError(attr) + return getattr(self._provider, attr) + + def __dir__(self): + return list( + set(super(Distribution, self).__dir__()) + | set( + attr for attr in self._provider.__dir__() + if not attr.startswith('_') + ) + ) + + if not hasattr(object, '__dir__'): + # python 2.7 not supported + del __dir__ + + @classmethod + def from_filename(cls, filename, metadata=None, **kw): + return cls.from_location( + _normalize_cached(filename), os.path.basename(filename), metadata, + **kw + ) + + def as_requirement(self): + """Return a ``Requirement`` that matches this distribution exactly""" + if isinstance(self.parsed_version, packaging.version.Version): + spec = "%s==%s" % (self.project_name, self.parsed_version) + else: + spec = "%s===%s" % (self.project_name, self.parsed_version) + + return Requirement.parse(spec) + + def load_entry_point(self, group, name): + """Return the `name` entry point of `group` or raise ImportError""" + ep = self.get_entry_info(group, name) + if ep is None: + raise ImportError("Entry point %r not found" % ((group, name),)) + return ep.load() + + def get_entry_map(self, group=None): + """Return the entry point map for `group`, or the full entry map""" + try: + ep_map = self._ep_map + except AttributeError: + ep_map = self._ep_map = EntryPoint.parse_map( + self._get_metadata('entry_points.txt'), self + ) + if group is not None: + return ep_map.get(group, {}) + return ep_map + + def get_entry_info(self, group, name): + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return self.get_entry_map(group).get(name) + + def insert_on(self, path, loc=None, replace=False): + """Ensure self.location is on path + + If replace=False (default): + - If location is already in path anywhere, do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent. + - Else: add to the end of path. + If replace=True: + - If location is already on path anywhere (not eggs) + or higher priority than its parent (eggs) + do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent, + removing any lower-priority entries. + - Else: add it to the front of path. + """ + + loc = loc or self.location + if not loc: + return + + nloc = _normalize_cached(loc) + bdir = os.path.dirname(nloc) + npath = [(p and _normalize_cached(p) or p) for p in path] + + for p, item in enumerate(npath): + if item == nloc: + if replace: + break + else: + # don't modify path (even removing duplicates) if + # found and not replace + return + elif item == bdir and self.precedence == EGG_DIST: + # if it's an .egg, give it precedence over its directory + # UNLESS it's already been added to sys.path and replace=False + if (not replace) and nloc in npath[p:]: + return + if path is sys.path: + self.check_version_conflict() + path.insert(p, loc) + npath.insert(p, nloc) + break + else: + if path is sys.path: + self.check_version_conflict() + if replace: + path.insert(0, loc) + else: + path.append(loc) + return + + # p is the spot where we found or inserted loc; now remove duplicates + while True: + try: + np = npath.index(nloc, p + 1) + except ValueError: + break + else: + del npath[np], path[np] + # ha! + p = np + + return + + def check_version_conflict(self): + if self.key == 'setuptools': + # ignore the inevitable setuptools self-conflicts :( + return + + nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt')) + loc = normalize_path(self.location) + for modname in self._get_metadata('top_level.txt'): + if (modname not in sys.modules or modname in nsp + or modname in _namespace_packages): + continue + if modname in ('pkg_resources', 'setuptools', 'site'): + continue + fn = getattr(sys.modules[modname], '__file__', None) + if fn and (normalize_path(fn).startswith(loc) or + fn.startswith(self.location)): + continue + issue_warning( + "Module %s was already imported from %s, but %s is being added" + " to sys.path" % (modname, fn, self.location), + ) + + def has_version(self): + try: + self.version + except ValueError: + issue_warning("Unbuilt egg for " + repr(self)) + return False + return True + + def clone(self, **kw): + """Copy this distribution, substituting in any changed keyword args""" + names = 'project_name version py_version platform location precedence' + for attr in names.split(): + kw.setdefault(attr, getattr(self, attr, None)) + kw.setdefault('metadata', self._provider) + return self.__class__(**kw) + + @property + def extras(self): + return [dep for dep in self._dep_map if dep] + + +class EggInfoDistribution(Distribution): + def _reload_version(self): + """ + Packages installed by distutils (e.g. numpy or scipy), + which uses an old safe_version, and so + their version numbers can get mangled when + converted to filenames (e.g., 1.11.0.dev0+2329eae to + 1.11.0.dev0_2329eae). These distributions will not be + parsed properly + downstream by Distribution and safe_version, so + take an extra step and try to get the version number from + the metadata file itself instead of the filename. + """ + md_version = _version_from_file(self._get_metadata(self.PKG_INFO)) + if md_version: + self._version = md_version + return self + + +class DistInfoDistribution(Distribution): + """ + Wrap an actual or potential sys.path entry + w/metadata, .dist-info style. + """ + PKG_INFO = 'METADATA' + EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])") + + @property + def _parsed_pkg_info(self): + """Parse and cache metadata""" + try: + return self._pkg_info + except AttributeError: + metadata = self.get_metadata(self.PKG_INFO) + self._pkg_info = email.parser.Parser().parsestr(metadata) + return self._pkg_info + + @property + def _dep_map(self): + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._compute_dependencies() + return self.__dep_map + + def _compute_dependencies(self): + """Recompute this distribution's dependencies.""" + dm = self.__dep_map = {None: []} + + reqs = [] + # Including any condition expressions + for req in self._parsed_pkg_info.get_all('Requires-Dist') or []: + reqs.extend(parse_requirements(req)) + + def reqs_for_extra(extra): + for req in reqs: + if not req.marker or req.marker.evaluate({'extra': extra}): + yield req + + common = frozenset(reqs_for_extra(None)) + dm[None].extend(common) + + for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []: + s_extra = safe_extra(extra.strip()) + dm[s_extra] = list(frozenset(reqs_for_extra(extra)) - common) + + return dm + + +_distributionImpl = { + '.egg': Distribution, + '.egg-info': EggInfoDistribution, + '.dist-info': DistInfoDistribution, +} + + +def issue_warning(*args, **kw): + level = 1 + g = globals() + try: + # find the first stack frame that is *not* code in + # the pkg_resources module, to use for the warning + while sys._getframe(level).f_globals is g: + level += 1 + except ValueError: + pass + warnings.warn(stacklevel=level + 1, *args, **kw) + + +class RequirementParseError(ValueError): + def __str__(self): + return ' '.join(self.args) + + +def parse_requirements(strs): + """Yield ``Requirement`` objects for each specification in `strs` + + `strs` must be a string, or a (possibly-nested) iterable thereof. + """ + # create a steppable iterator, so we can handle \-continuations + lines = iter(yield_lines(strs)) + + for line in lines: + # Drop comments -- a hash without a space may be in a URL. + if ' #' in line: + line = line[:line.find(' #')] + # If there is a line continuation, drop it, and append the next line. + if line.endswith('\\'): + line = line[:-2].strip() + try: + line += next(lines) + except StopIteration: + return + yield Requirement(line) + + +class Requirement(packaging.requirements.Requirement): + def __init__(self, requirement_string): + """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" + try: + super(Requirement, self).__init__(requirement_string) + except packaging.requirements.InvalidRequirement as e: + raise RequirementParseError(str(e)) + self.unsafe_name = self.name + project_name = safe_name(self.name) + self.project_name, self.key = project_name, project_name.lower() + self.specs = [ + (spec.operator, spec.version) for spec in self.specifier] + self.extras = tuple(map(safe_extra, self.extras)) + self.hashCmp = ( + self.key, + self.specifier, + frozenset(self.extras), + str(self.marker) if self.marker else None, + ) + self.__hash = hash(self.hashCmp) + + def __eq__(self, other): + return ( + isinstance(other, Requirement) and + self.hashCmp == other.hashCmp + ) + + def __ne__(self, other): + return not self == other + + def __contains__(self, item): + if isinstance(item, Distribution): + if item.key != self.key: + return False + + item = item.version + + # Allow prereleases always in order to match the previous behavior of + # this method. In the future this should be smarter and follow PEP 440 + # more accurately. + return self.specifier.contains(item, prereleases=True) + + def __hash__(self): + return self.__hash + + def __repr__(self): + return "Requirement.parse(%r)" % str(self) + + @staticmethod + def parse(s): + req, = parse_requirements(s) + return req + + +def _always_object(classes): + """ + Ensure object appears in the mro even + for old-style classes. + """ + if object not in classes: + return classes + (object,) + return classes + + +def _find_adapter(registry, ob): + """Return an adapter factory for `ob` from `registry`""" + types = _always_object(inspect.getmro(getattr(ob, '__class__', type(ob)))) + for t in types: + if t in registry: + return registry[t] + + +def ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + py31compat.makedirs(dirname, exist_ok=True) + + +def _bypass_ensure_directory(path): + """Sandbox-bypassing version of ensure_directory()""" + if not WRITE_SUPPORT: + raise IOError('"os.mkdir" not supported on this platform.') + dirname, filename = split(path) + if dirname and filename and not isdir(dirname): + _bypass_ensure_directory(dirname) + try: + mkdir(dirname, 0o755) + except FileExistsError: + pass + + +def split_sections(s): + """Split a string or iterable thereof into (section, content) pairs + + Each ``section`` is a stripped version of the section header ("[section]") + and each ``content`` is a list of stripped lines excluding blank lines and + comment-only lines. If there are any such lines before the first section + header, they're returned in a first ``section`` of ``None``. + """ + section = None + content = [] + for line in yield_lines(s): + if line.startswith("["): + if line.endswith("]"): + if section or content: + yield section, content + section = line[1:-1].strip() + content = [] + else: + raise ValueError("Invalid section heading", line) + else: + content.append(line) + + # wrap up last segment + yield section, content + + +def _mkstemp(*args, **kw): + old_open = os.open + try: + # temporarily bypass sandboxing + os.open = os_open + return tempfile.mkstemp(*args, **kw) + finally: + # and then put it back + os.open = old_open + + +# Silence the PEP440Warning by default, so that end users don't get hit by it +# randomly just because they use pkg_resources. We want to append the rule +# because we want earlier uses of filterwarnings to take precedence over this +# one. +warnings.filterwarnings("ignore", category=PEP440Warning, append=True) + + +# from jaraco.functools 1.3 +def _call_aside(f, *args, **kwargs): + f(*args, **kwargs) + return f + + +@_call_aside +def _initialize(g=globals()): + "Set up global resource manager (deliberately not state-saved)" + manager = ResourceManager() + g['_manager'] = manager + g.update( + (name, getattr(manager, name)) + for name in dir(manager) + if not name.startswith('_') + ) + + +@_call_aside +def _initialize_master_working_set(): + """ + Prepare the master working set and make the ``require()`` + API available. + + This function has explicit effects on the global state + of pkg_resources. It is intended to be invoked once at + the initialization of this module. + + Invocation by other packages is unsupported and done + at their own risk. + """ + working_set = WorkingSet._build_master() + _declare_state('object', working_set=working_set) + + require = working_set.require + iter_entry_points = working_set.iter_entry_points + add_activation_listener = working_set.subscribe + run_script = working_set.run_script + # backward compatibility + run_main = run_script + # Activate all distributions already on sys.path with replace=False and + # ensure that all distributions added to the working set in the future + # (e.g. by calling ``require()``) will get activated as well, + # with higher priority (replace=True). + tuple( + dist.activate(replace=False) + for dist in working_set + ) + add_activation_listener( + lambda dist: dist.activate(replace=True), + existing=False, + ) + working_set.entries = [] + # match order + list(map(working_set.add_entry, sys.path)) + globals().update(locals()) diff --git a/venv/Lib/site-packages/pip/_vendor/pkg_resources/py31compat.py b/venv/Lib/site-packages/pip/_vendor/pkg_resources/py31compat.py new file mode 100644 index 0000000..a2d3007 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pkg_resources/py31compat.py @@ -0,0 +1,23 @@ +import os +import errno +import sys + +from pip._vendor import six + + +def _makedirs_31(path, exist_ok=False): + try: + os.makedirs(path) + except OSError as exc: + if not exist_ok or exc.errno != errno.EEXIST: + raise + + +# rely on compatibility behavior until mode considerations +# and exists_ok considerations are disentangled. +# See https://github.com/pypa/setuptools/pull/1083#issuecomment-315168663 +needs_makedirs = ( + six.PY2 or + (3, 4) <= sys.version_info < (3, 4, 1) +) +makedirs = _makedirs_31 if needs_makedirs else os.makedirs diff --git a/venv/Lib/site-packages/pip/_vendor/progress/__init__.py b/venv/Lib/site-packages/pip/_vendor/progress/__init__.py new file mode 100644 index 0000000..a41f65d --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/progress/__init__.py @@ -0,0 +1,127 @@ +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from __future__ import division + +from collections import deque +from datetime import timedelta +from math import ceil +from sys import stderr +from time import time + + +__version__ = '1.4' + + +class Infinite(object): + file = stderr + sma_window = 10 # Simple Moving Average window + + def __init__(self, *args, **kwargs): + self.index = 0 + self.start_ts = time() + self.avg = 0 + self._ts = self.start_ts + self._xput = deque(maxlen=self.sma_window) + for key, val in kwargs.items(): + setattr(self, key, val) + + def __getitem__(self, key): + if key.startswith('_'): + return None + return getattr(self, key, None) + + @property + def elapsed(self): + return int(time() - self.start_ts) + + @property + def elapsed_td(self): + return timedelta(seconds=self.elapsed) + + def update_avg(self, n, dt): + if n > 0: + self._xput.append(dt / n) + self.avg = sum(self._xput) / len(self._xput) + + def update(self): + pass + + def start(self): + pass + + def finish(self): + pass + + def next(self, n=1): + now = time() + dt = now - self._ts + self.update_avg(n, dt) + self._ts = now + self.index = self.index + n + self.update() + + def iter(self, it): + try: + for x in it: + yield x + self.next() + finally: + self.finish() + + +class Progress(Infinite): + def __init__(self, *args, **kwargs): + super(Progress, self).__init__(*args, **kwargs) + self.max = kwargs.get('max', 100) + + @property + def eta(self): + return int(ceil(self.avg * self.remaining)) + + @property + def eta_td(self): + return timedelta(seconds=self.eta) + + @property + def percent(self): + return self.progress * 100 + + @property + def progress(self): + return min(1, self.index / self.max) + + @property + def remaining(self): + return max(self.max - self.index, 0) + + def start(self): + self.update() + + def goto(self, index): + incr = index - self.index + self.next(incr) + + def iter(self, it): + try: + self.max = len(it) + except TypeError: + pass + + try: + for x in it: + yield x + self.next() + finally: + self.finish() diff --git a/venv/Lib/site-packages/pip/_vendor/progress/bar.py b/venv/Lib/site-packages/pip/_vendor/progress/bar.py new file mode 100644 index 0000000..025e61c --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/progress/bar.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from __future__ import unicode_literals + +import sys + +from . import Progress +from .helpers import WritelnMixin + + +class Bar(WritelnMixin, Progress): + width = 32 + message = '' + suffix = '%(index)d/%(max)d' + bar_prefix = ' |' + bar_suffix = '| ' + empty_fill = ' ' + fill = '#' + hide_cursor = True + + def update(self): + filled_length = int(self.width * self.progress) + empty_length = self.width - filled_length + + message = self.message % self + bar = self.fill * filled_length + empty = self.empty_fill * empty_length + suffix = self.suffix % self + line = ''.join([message, self.bar_prefix, bar, empty, self.bar_suffix, + suffix]) + self.writeln(line) + + +class ChargingBar(Bar): + suffix = '%(percent)d%%' + bar_prefix = ' ' + bar_suffix = ' ' + empty_fill = '∙' + fill = '█' + + +class FillingSquaresBar(ChargingBar): + empty_fill = '▢' + fill = '▣' + + +class FillingCirclesBar(ChargingBar): + empty_fill = '◯' + fill = '◉' + + +class IncrementalBar(Bar): + if sys.platform.startswith('win'): + phases = (u' ', u'▌', u'█') + else: + phases = (' ', '▏', '▎', '▍', '▌', '▋', '▊', '▉', '█') + + def update(self): + nphases = len(self.phases) + filled_len = self.width * self.progress + nfull = int(filled_len) # Number of full chars + phase = int((filled_len - nfull) * nphases) # Phase of last char + nempty = self.width - nfull # Number of empty chars + + message = self.message % self + bar = self.phases[-1] * nfull + current = self.phases[phase] if phase > 0 else '' + empty = self.empty_fill * max(0, nempty - len(current)) + suffix = self.suffix % self + line = ''.join([message, self.bar_prefix, bar, current, empty, + self.bar_suffix, suffix]) + self.writeln(line) + + +class PixelBar(IncrementalBar): + phases = ('⡀', '⡄', '⡆', '⡇', '⣇', '⣧', '⣷', '⣿') + + +class ShadyBar(IncrementalBar): + phases = (' ', '░', '▒', '▓', '█') diff --git a/venv/Lib/site-packages/pip/_vendor/progress/counter.py b/venv/Lib/site-packages/pip/_vendor/progress/counter.py new file mode 100644 index 0000000..6b45a1e --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/progress/counter.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from __future__ import unicode_literals +from . import Infinite, Progress +from .helpers import WriteMixin + + +class Counter(WriteMixin, Infinite): + message = '' + hide_cursor = True + + def update(self): + self.write(str(self.index)) + + +class Countdown(WriteMixin, Progress): + hide_cursor = True + + def update(self): + self.write(str(self.remaining)) + + +class Stack(WriteMixin, Progress): + phases = (' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█') + hide_cursor = True + + def update(self): + nphases = len(self.phases) + i = min(nphases - 1, int(self.progress * nphases)) + self.write(self.phases[i]) + + +class Pie(Stack): + phases = ('○', '◔', '◑', '◕', '●') diff --git a/venv/Lib/site-packages/pip/_vendor/progress/helpers.py b/venv/Lib/site-packages/pip/_vendor/progress/helpers.py new file mode 100644 index 0000000..0cde44e --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/progress/helpers.py @@ -0,0 +1,91 @@ +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from __future__ import print_function + + +HIDE_CURSOR = '\x1b[?25l' +SHOW_CURSOR = '\x1b[?25h' + + +class WriteMixin(object): + hide_cursor = False + + def __init__(self, message=None, **kwargs): + super(WriteMixin, self).__init__(**kwargs) + self._width = 0 + if message: + self.message = message + + if self.file and self.file.isatty(): + if self.hide_cursor: + print(HIDE_CURSOR, end='', file=self.file) + print(self.message, end='', file=self.file) + self.file.flush() + + def write(self, s): + if self.file and self.file.isatty(): + b = '\b' * self._width + c = s.ljust(self._width) + print(b + c, end='', file=self.file) + self._width = max(self._width, len(s)) + self.file.flush() + + def finish(self): + if self.file and self.file.isatty() and self.hide_cursor: + print(SHOW_CURSOR, end='', file=self.file) + + +class WritelnMixin(object): + hide_cursor = False + + def __init__(self, message=None, **kwargs): + super(WritelnMixin, self).__init__(**kwargs) + if message: + self.message = message + + if self.file and self.file.isatty() and self.hide_cursor: + print(HIDE_CURSOR, end='', file=self.file) + + def clearln(self): + if self.file and self.file.isatty(): + print('\r\x1b[K', end='', file=self.file) + + def writeln(self, line): + if self.file and self.file.isatty(): + self.clearln() + print(line, end='', file=self.file) + self.file.flush() + + def finish(self): + if self.file and self.file.isatty(): + print(file=self.file) + if self.hide_cursor: + print(SHOW_CURSOR, end='', file=self.file) + + +from signal import signal, SIGINT +from sys import exit + + +class SigIntMixin(object): + """Registers a signal handler that calls finish on SIGINT""" + + def __init__(self, *args, **kwargs): + super(SigIntMixin, self).__init__(*args, **kwargs) + signal(SIGINT, self._sigint_handler) + + def _sigint_handler(self, signum, frame): + self.finish() + exit(0) diff --git a/venv/Lib/site-packages/pip/_vendor/progress/spinner.py b/venv/Lib/site-packages/pip/_vendor/progress/spinner.py new file mode 100644 index 0000000..464c7b2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/progress/spinner.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from __future__ import unicode_literals +from . import Infinite +from .helpers import WriteMixin + + +class Spinner(WriteMixin, Infinite): + message = '' + phases = ('-', '\\', '|', '/') + hide_cursor = True + + def update(self): + i = self.index % len(self.phases) + self.write(self.phases[i]) + + +class PieSpinner(Spinner): + phases = ['◷', '◶', '◵', '◴'] + + +class MoonSpinner(Spinner): + phases = ['◑', '◒', '◐', '◓'] + + +class LineSpinner(Spinner): + phases = ['⎺', '⎻', '⎼', '⎽', '⎼', '⎻'] + +class PixelSpinner(Spinner): + phases = ['⣾','⣷', '⣯', '⣟', '⡿', '⢿', '⣻', '⣽'] diff --git a/venv/Lib/site-packages/pip/_vendor/pyparsing.py b/venv/Lib/site-packages/pip/_vendor/pyparsing.py new file mode 100644 index 0000000..865152d --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pyparsing.py @@ -0,0 +1,5742 @@ +# module pyparsing.py +# +# Copyright (c) 2003-2018 Paul T. McGuire +# +# 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. +# + +__doc__ = \ +""" +pyparsing module - Classes and methods to define and execute parsing grammars +============================================================================= + +The pyparsing module is an alternative approach to creating and executing simple grammars, +vs. the traditional lex/yacc approach, or the use of regular expressions. With pyparsing, you +don't need to learn a new syntax for defining grammars or matching expressions - the parsing module +provides a library of classes that you use to construct the grammar directly in Python. + +Here is a program to parse "Hello, World!" (or any greeting of the form +C{"<salutation>, <addressee>!"}), built up using L{Word}, L{Literal}, and L{And} elements +(L{'+'<ParserElement.__add__>} operator gives L{And} expressions, strings are auto-converted to +L{Literal} expressions):: + + from pip._vendor.pyparsing import Word, alphas + + # define grammar of a greeting + greet = Word(alphas) + "," + Word(alphas) + "!" + + hello = "Hello, World!" + print (hello, "->", greet.parseString(hello)) + +The program outputs the following:: + + Hello, World! -> ['Hello', ',', 'World', '!'] + +The Python representation of the grammar is quite readable, owing to the self-explanatory +class names, and the use of '+', '|' and '^' operators. + +The L{ParseResults} object returned from L{ParserElement.parseString<ParserElement.parseString>} can be accessed as a nested list, a dictionary, or an +object with named attributes. + +The pyparsing module handles some of the problems that are typically vexing when writing text parsers: + - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello , World !", etc.) + - quoted strings + - embedded comments + + +Getting Started - +----------------- +Visit the classes L{ParserElement} and L{ParseResults} to see the base classes that most other pyparsing +classes inherit from. Use the docstrings for examples of how to: + - construct literal match expressions from L{Literal} and L{CaselessLiteral} classes + - construct character word-group expressions using the L{Word} class + - see how to create repetitive expressions using L{ZeroOrMore} and L{OneOrMore} classes + - use L{'+'<And>}, L{'|'<MatchFirst>}, L{'^'<Or>}, and L{'&'<Each>} operators to combine simple expressions into more complex ones + - associate names with your parsed results using L{ParserElement.setResultsName} + - find some helpful expression short-cuts like L{delimitedList} and L{oneOf} + - find more useful common expressions in the L{pyparsing_common} namespace class +""" + +__version__ = "2.2.1" +__versionTime__ = "18 Sep 2018 00:49 UTC" +__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>" + +import string +from weakref import ref as wkref +import copy +import sys +import warnings +import re +import sre_constants +import collections +import pprint +import traceback +import types +from datetime import datetime + +try: + from _thread import RLock +except ImportError: + from threading import RLock + +try: + # Python 3 + from collections.abc import Iterable + from collections.abc import MutableMapping +except ImportError: + # Python 2.7 + from collections import Iterable + from collections import MutableMapping + +try: + from collections import OrderedDict as _OrderedDict +except ImportError: + try: + from ordereddict import OrderedDict as _OrderedDict + except ImportError: + _OrderedDict = None + +#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) + +__all__ = [ +'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', +'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', +'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', +'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', +'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', +'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', +'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', +'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', +'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', +'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums', +'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno', +'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral', +'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables', +'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', +'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', +'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', +'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass', +'CloseMatch', 'tokenMap', 'pyparsing_common', +] + +system_version = tuple(sys.version_info)[:3] +PY_3 = system_version[0] == 3 +if PY_3: + _MAX_INT = sys.maxsize + basestring = str + unichr = chr + _ustr = str + + # build list of single arg builtins, that can be used as parse actions + singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max] + +else: + _MAX_INT = sys.maxint + range = xrange + + def _ustr(obj): + """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries + str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It + then < returns the unicode object | encodes it with the default encoding | ... >. + """ + if isinstance(obj,unicode): + return obj + + try: + # If this works, then _ustr(obj) has the same behaviour as str(obj), so + # it won't break any existing code. + return str(obj) + + except UnicodeEncodeError: + # Else encode it + ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace') + xmlcharref = Regex(r'&#\d+;') + xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:]) + return xmlcharref.transformString(ret) + + # build list of single arg builtins, tolerant of Python version, that can be used as parse actions + singleArgBuiltins = [] + import __builtin__ + for fname in "sum len sorted reversed list tuple set any all min max".split(): + try: + singleArgBuiltins.append(getattr(__builtin__,fname)) + except AttributeError: + continue + +_generatorType = type((y for y in range(1))) + +def _xml_escape(data): + """Escape &, <, >, ", ', etc. in a string of data.""" + + # ampersand must be replaced first + from_symbols = '&><"\'' + to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split()) + for from_,to_ in zip(from_symbols, to_symbols): + data = data.replace(from_, to_) + return data + +class _Constants(object): + pass + +alphas = string.ascii_uppercase + string.ascii_lowercase +nums = "0123456789" +hexnums = nums + "ABCDEFabcdef" +alphanums = alphas + nums +_bslash = chr(92) +printables = "".join(c for c in string.printable if c not in string.whitespace) + +class ParseBaseException(Exception): + """base exception class for all parsing runtime exceptions""" + # Performance tuning: we construct a *lot* of these, so keep this + # constructor as small and fast as possible + def __init__( self, pstr, loc=0, msg=None, elem=None ): + self.loc = loc + if msg is None: + self.msg = pstr + self.pstr = "" + else: + self.msg = msg + self.pstr = pstr + self.parserElement = elem + self.args = (pstr, loc, msg) + + @classmethod + def _from_exception(cls, pe): + """ + internal factory method to simplify creating one type of ParseException + from another - avoids having __init__ signature conflicts among subclasses + """ + return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) + + def __getattr__( self, aname ): + """supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + """ + if( aname == "lineno" ): + return lineno( self.loc, self.pstr ) + elif( aname in ("col", "column") ): + return col( self.loc, self.pstr ) + elif( aname == "line" ): + return line( self.loc, self.pstr ) + else: + raise AttributeError(aname) + + def __str__( self ): + return "%s (at char %d), (line:%d, col:%d)" % \ + ( self.msg, self.loc, self.lineno, self.column ) + def __repr__( self ): + return _ustr(self) + def markInputline( self, markerString = ">!<" ): + """Extracts the exception line from the input string, and marks + the location of the exception with a special symbol. + """ + line_str = self.line + line_column = self.column - 1 + if markerString: + line_str = "".join((line_str[:line_column], + markerString, line_str[line_column:])) + return line_str.strip() + def __dir__(self): + return "lineno col line".split() + dir(type(self)) + +class ParseException(ParseBaseException): + """ + Exception thrown when parse expressions don't match class; + supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + + Example:: + try: + Word(nums).setName("integer").parseString("ABC") + except ParseException as pe: + print(pe) + print("column: {}".format(pe.col)) + + prints:: + Expected integer (at char 0), (line:1, col:1) + column: 1 + """ + pass + +class ParseFatalException(ParseBaseException): + """user-throwable exception thrown when inconsistent parse content + is found; stops all parsing immediately""" + pass + +class ParseSyntaxException(ParseFatalException): + """just like L{ParseFatalException}, but thrown internally when an + L{ErrorStop<And._ErrorStop>} ('-' operator) indicates that parsing is to stop + immediately because an unbacktrackable syntax error has been found""" + pass + +#~ class ReparseException(ParseBaseException): + #~ """Experimental class - parse actions can raise this exception to cause + #~ pyparsing to reparse the input string: + #~ - with a modified input string, and/or + #~ - with a modified start location + #~ Set the values of the ReparseException in the constructor, and raise the + #~ exception in a parse action to cause pyparsing to use the new string/location. + #~ Setting the values as None causes no change to be made. + #~ """ + #~ def __init_( self, newstring, restartLoc ): + #~ self.newParseText = newstring + #~ self.reparseLoc = restartLoc + +class RecursiveGrammarException(Exception): + """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive""" + def __init__( self, parseElementList ): + self.parseElementTrace = parseElementList + + def __str__( self ): + return "RecursiveGrammarException: %s" % self.parseElementTrace + +class _ParseResultsWithOffset(object): + def __init__(self,p1,p2): + self.tup = (p1,p2) + def __getitem__(self,i): + return self.tup[i] + def __repr__(self): + return repr(self.tup[0]) + def setOffset(self,i): + self.tup = (self.tup[0],i) + +class ParseResults(object): + """ + Structured parse results, to provide multiple means of access to the parsed data: + - as a list (C{len(results)}) + - by list index (C{results[0], results[1]}, etc.) + - by attribute (C{results.<resultsName>} - see L{ParserElement.setResultsName}) + + Example:: + integer = Word(nums) + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + # equivalent form: + # date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + # parseString returns a ParseResults object + result = date_str.parseString("1999/12/31") + + def test(s, fn=repr): + print("%s -> %s" % (s, fn(eval(s)))) + test("list(result)") + test("result[0]") + test("result['month']") + test("result.day") + test("'month' in result") + test("'minutes' in result") + test("result.dump()", str) + prints:: + list(result) -> ['1999', '/', '12', '/', '31'] + result[0] -> '1999' + result['month'] -> '12' + result.day -> '31' + 'month' in result -> True + 'minutes' in result -> False + result.dump() -> ['1999', '/', '12', '/', '31'] + - day: 31 + - month: 12 + - year: 1999 + """ + def __new__(cls, toklist=None, name=None, asList=True, modal=True ): + if isinstance(toklist, cls): + return toklist + retobj = object.__new__(cls) + retobj.__doinit = True + return retobj + + # Performance tuning: we construct a *lot* of these, so keep this + # constructor as small and fast as possible + def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ): + if self.__doinit: + self.__doinit = False + self.__name = None + self.__parent = None + self.__accumNames = {} + self.__asList = asList + self.__modal = modal + if toklist is None: + toklist = [] + if isinstance(toklist, list): + self.__toklist = toklist[:] + elif isinstance(toklist, _generatorType): + self.__toklist = list(toklist) + else: + self.__toklist = [toklist] + self.__tokdict = dict() + + if name is not None and name: + if not modal: + self.__accumNames[name] = 0 + if isinstance(name,int): + name = _ustr(name) # will always return a str, but use _ustr for consistency + self.__name = name + if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])): + if isinstance(toklist,basestring): + toklist = [ toklist ] + if asList: + if isinstance(toklist,ParseResults): + self[name] = _ParseResultsWithOffset(toklist.copy(),0) + else: + self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0) + self[name].__name = name + else: + try: + self[name] = toklist[0] + except (KeyError,TypeError,IndexError): + self[name] = toklist + + def __getitem__( self, i ): + if isinstance( i, (int,slice) ): + return self.__toklist[i] + else: + if i not in self.__accumNames: + return self.__tokdict[i][-1][0] + else: + return ParseResults([ v[0] for v in self.__tokdict[i] ]) + + def __setitem__( self, k, v, isinstance=isinstance ): + if isinstance(v,_ParseResultsWithOffset): + self.__tokdict[k] = self.__tokdict.get(k,list()) + [v] + sub = v[0] + elif isinstance(k,(int,slice)): + self.__toklist[k] = v + sub = v + else: + self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)] + sub = v + if isinstance(sub,ParseResults): + sub.__parent = wkref(self) + + def __delitem__( self, i ): + if isinstance(i,(int,slice)): + mylen = len( self.__toklist ) + del self.__toklist[i] + + # convert int to slice + if isinstance(i, int): + if i < 0: + i += mylen + i = slice(i, i+1) + # get removed indices + removed = list(range(*i.indices(mylen))) + removed.reverse() + # fixup indices in token dictionary + for name,occurrences in self.__tokdict.items(): + for j in removed: + for k, (value, position) in enumerate(occurrences): + occurrences[k] = _ParseResultsWithOffset(value, position - (position > j)) + else: + del self.__tokdict[i] + + def __contains__( self, k ): + return k in self.__tokdict + + def __len__( self ): return len( self.__toklist ) + def __bool__(self): return ( not not self.__toklist ) + __nonzero__ = __bool__ + def __iter__( self ): return iter( self.__toklist ) + def __reversed__( self ): return iter( self.__toklist[::-1] ) + def _iterkeys( self ): + if hasattr(self.__tokdict, "iterkeys"): + return self.__tokdict.iterkeys() + else: + return iter(self.__tokdict) + + def _itervalues( self ): + return (self[k] for k in self._iterkeys()) + + def _iteritems( self ): + return ((k, self[k]) for k in self._iterkeys()) + + if PY_3: + keys = _iterkeys + """Returns an iterator of all named result keys (Python 3.x only).""" + + values = _itervalues + """Returns an iterator of all named result values (Python 3.x only).""" + + items = _iteritems + """Returns an iterator of all named result key-value tuples (Python 3.x only).""" + + else: + iterkeys = _iterkeys + """Returns an iterator of all named result keys (Python 2.x only).""" + + itervalues = _itervalues + """Returns an iterator of all named result values (Python 2.x only).""" + + iteritems = _iteritems + """Returns an iterator of all named result key-value tuples (Python 2.x only).""" + + def keys( self ): + """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x).""" + return list(self.iterkeys()) + + def values( self ): + """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x).""" + return list(self.itervalues()) + + def items( self ): + """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x).""" + return list(self.iteritems()) + + def haskeys( self ): + """Since keys() returns an iterator, this method is helpful in bypassing + code that looks for the existence of any defined results names.""" + return bool(self.__tokdict) + + def pop( self, *args, **kwargs): + """ + Removes and returns item at specified index (default=C{last}). + Supports both C{list} and C{dict} semantics for C{pop()}. If passed no + argument or an integer argument, it will use C{list} semantics + and pop tokens from the list of parsed tokens. If passed a + non-integer argument (most likely a string), it will use C{dict} + semantics and pop the corresponding value from any defined + results names. A second default return value argument is + supported, just as in C{dict.pop()}. + + Example:: + def remove_first(tokens): + tokens.pop(0) + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321'] + + label = Word(alphas) + patt = label("LABEL") + OneOrMore(Word(nums)) + print(patt.parseString("AAB 123 321").dump()) + + # Use pop() in a parse action to remove named result (note that corresponding value is not + # removed from list form of results) + def remove_LABEL(tokens): + tokens.pop("LABEL") + return tokens + patt.addParseAction(remove_LABEL) + print(patt.parseString("AAB 123 321").dump()) + prints:: + ['AAB', '123', '321'] + - LABEL: AAB + + ['AAB', '123', '321'] + """ + if not args: + args = [-1] + for k,v in kwargs.items(): + if k == 'default': + args = (args[0], v) + else: + raise TypeError("pop() got an unexpected keyword argument '%s'" % k) + if (isinstance(args[0], int) or + len(args) == 1 or + args[0] in self): + index = args[0] + ret = self[index] + del self[index] + return ret + else: + defaultvalue = args[1] + return defaultvalue + + def get(self, key, defaultValue=None): + """ + Returns named result matching the given key, or if there is no + such name, then returns the given C{defaultValue} or C{None} if no + C{defaultValue} is specified. + + Similar to C{dict.get()}. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString("1999/12/31") + print(result.get("year")) # -> '1999' + print(result.get("hour", "not specified")) # -> 'not specified' + print(result.get("hour")) # -> None + """ + if key in self: + return self[key] + else: + return defaultValue + + def insert( self, index, insStr ): + """ + Inserts new element at location index in the list of parsed tokens. + + Similar to C{list.insert()}. + + Example:: + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to insert the parse location in the front of the parsed results + def insert_locn(locn, tokens): + tokens.insert(0, locn) + print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321'] + """ + self.__toklist.insert(index, insStr) + # fixup indices in token dictionary + for name,occurrences in self.__tokdict.items(): + for k, (value, position) in enumerate(occurrences): + occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) + + def append( self, item ): + """ + Add single element to end of ParseResults list of elements. + + Example:: + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to compute the sum of the parsed integers, and add it to the end + def append_sum(tokens): + tokens.append(sum(map(int, tokens))) + print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444] + """ + self.__toklist.append(item) + + def extend( self, itemseq ): + """ + Add sequence of elements to end of ParseResults list of elements. + + Example:: + patt = OneOrMore(Word(alphas)) + + # use a parse action to append the reverse of the matched strings, to make a palindrome + def make_palindrome(tokens): + tokens.extend(reversed([t[::-1] for t in tokens])) + return ''.join(tokens) + print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' + """ + if isinstance(itemseq, ParseResults): + self += itemseq + else: + self.__toklist.extend(itemseq) + + def clear( self ): + """ + Clear all elements and results names. + """ + del self.__toklist[:] + self.__tokdict.clear() + + def __getattr__( self, name ): + try: + return self[name] + except KeyError: + return "" + + if name in self.__tokdict: + if name not in self.__accumNames: + return self.__tokdict[name][-1][0] + else: + return ParseResults([ v[0] for v in self.__tokdict[name] ]) + else: + return "" + + def __add__( self, other ): + ret = self.copy() + ret += other + return ret + + def __iadd__( self, other ): + if other.__tokdict: + offset = len(self.__toklist) + addoffset = lambda a: offset if a<0 else a+offset + otheritems = other.__tokdict.items() + otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) ) + for (k,vlist) in otheritems for v in vlist] + for k,v in otherdictitems: + self[k] = v + if isinstance(v[0],ParseResults): + v[0].__parent = wkref(self) + + self.__toklist += other.__toklist + self.__accumNames.update( other.__accumNames ) + return self + + def __radd__(self, other): + if isinstance(other,int) and other == 0: + # useful for merging many ParseResults using sum() builtin + return self.copy() + else: + # this may raise a TypeError - so be it + return other + self + + def __repr__( self ): + return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) ) + + def __str__( self ): + return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']' + + def _asStringList( self, sep='' ): + out = [] + for item in self.__toklist: + if out and sep: + out.append(sep) + if isinstance( item, ParseResults ): + out += item._asStringList() + else: + out.append( _ustr(item) ) + return out + + def asList( self ): + """ + Returns the parse results as a nested list of matching tokens, all converted to strings. + + Example:: + patt = OneOrMore(Word(alphas)) + result = patt.parseString("sldkj lsdkj sldkj") + # even though the result prints in string-like form, it is actually a pyparsing ParseResults + print(type(result), result) # -> <class 'pyparsing.ParseResults'> ['sldkj', 'lsdkj', 'sldkj'] + + # Use asList() to create an actual list + result_list = result.asList() + print(type(result_list), result_list) # -> <class 'list'> ['sldkj', 'lsdkj', 'sldkj'] + """ + return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist] + + def asDict( self ): + """ + Returns the named parse results as a nested dictionary. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(type(result), repr(result)) # -> <class 'pyparsing.ParseResults'> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) + + result_dict = result.asDict() + print(type(result_dict), repr(result_dict)) # -> <class 'dict'> {'day': '1999', 'year': '12', 'month': '31'} + + # even though a ParseResults supports dict-like access, sometime you just need to have a dict + import json + print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable + print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"} + """ + if PY_3: + item_fn = self.items + else: + item_fn = self.iteritems + + def toItem(obj): + if isinstance(obj, ParseResults): + if obj.haskeys(): + return obj.asDict() + else: + return [toItem(v) for v in obj] + else: + return obj + + return dict((k,toItem(v)) for k,v in item_fn()) + + def copy( self ): + """ + Returns a new copy of a C{ParseResults} object. + """ + ret = ParseResults( self.__toklist ) + ret.__tokdict = self.__tokdict.copy() + ret.__parent = self.__parent + ret.__accumNames.update( self.__accumNames ) + ret.__name = self.__name + return ret + + def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ): + """ + (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names. + """ + nl = "\n" + out = [] + namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items() + for v in vlist) + nextLevelIndent = indent + " " + + # collapse out indents if formatting is not desired + if not formatted: + indent = "" + nextLevelIndent = "" + nl = "" + + selfTag = None + if doctag is not None: + selfTag = doctag + else: + if self.__name: + selfTag = self.__name + + if not selfTag: + if namedItemsOnly: + return "" + else: + selfTag = "ITEM" + + out += [ nl, indent, "<", selfTag, ">" ] + + for i,res in enumerate(self.__toklist): + if isinstance(res,ParseResults): + if i in namedItems: + out += [ res.asXML(namedItems[i], + namedItemsOnly and doctag is None, + nextLevelIndent, + formatted)] + else: + out += [ res.asXML(None, + namedItemsOnly and doctag is None, + nextLevelIndent, + formatted)] + else: + # individual token, see if there is a name for it + resTag = None + if i in namedItems: + resTag = namedItems[i] + if not resTag: + if namedItemsOnly: + continue + else: + resTag = "ITEM" + xmlBodyText = _xml_escape(_ustr(res)) + out += [ nl, nextLevelIndent, "<", resTag, ">", + xmlBodyText, + "</", resTag, ">" ] + + out += [ nl, indent, "</", selfTag, ">" ] + return "".join(out) + + def __lookup(self,sub): + for k,vlist in self.__tokdict.items(): + for v,loc in vlist: + if sub is v: + return k + return None + + def getName(self): + r""" + Returns the results name for this token expression. Useful when several + different expressions might match at a particular location. + + Example:: + integer = Word(nums) + ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") + house_number_expr = Suppress('#') + Word(nums, alphanums) + user_data = (Group(house_number_expr)("house_number") + | Group(ssn_expr)("ssn") + | Group(integer)("age")) + user_info = OneOrMore(user_data) + + result = user_info.parseString("22 111-22-3333 #221B") + for item in result: + print(item.getName(), ':', item[0]) + prints:: + age : 22 + ssn : 111-22-3333 + house_number : 221B + """ + if self.__name: + return self.__name + elif self.__parent: + par = self.__parent() + if par: + return par.__lookup(self) + else: + return None + elif (len(self) == 1 and + len(self.__tokdict) == 1 and + next(iter(self.__tokdict.values()))[0][1] in (0,-1)): + return next(iter(self.__tokdict.keys())) + else: + return None + + def dump(self, indent='', depth=0, full=True): + """ + Diagnostic method for listing out the contents of a C{ParseResults}. + Accepts an optional C{indent} argument so that this string can be embedded + in a nested display of other data. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(result.dump()) + prints:: + ['12', '/', '31', '/', '1999'] + - day: 1999 + - month: 31 + - year: 12 + """ + out = [] + NL = '\n' + out.append( indent+_ustr(self.asList()) ) + if full: + if self.haskeys(): + items = sorted((str(k), v) for k,v in self.items()) + for k,v in items: + if out: + out.append(NL) + out.append( "%s%s- %s: " % (indent,(' '*depth), k) ) + if isinstance(v,ParseResults): + if v: + out.append( v.dump(indent,depth+1) ) + else: + out.append(_ustr(v)) + else: + out.append(repr(v)) + elif any(isinstance(vv,ParseResults) for vv in self): + v = self + for i,vv in enumerate(v): + if isinstance(vv,ParseResults): + out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),vv.dump(indent,depth+1) )) + else: + out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),_ustr(vv))) + + return "".join(out) + + def pprint(self, *args, **kwargs): + """ + Pretty-printer for parsed results as a list, using the C{pprint} module. + Accepts additional positional or keyword args as defined for the + C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint}) + + Example:: + ident = Word(alphas, alphanums) + num = Word(nums) + func = Forward() + term = ident | num | Group('(' + func + ')') + func <<= ident + Group(Optional(delimitedList(term))) + result = func.parseString("fna a,b,(fnb c,d,200),100") + result.pprint(width=40) + prints:: + ['fna', + ['a', + 'b', + ['(', 'fnb', ['c', 'd', '200'], ')'], + '100']] + """ + pprint.pprint(self.asList(), *args, **kwargs) + + # add support for pickle protocol + def __getstate__(self): + return ( self.__toklist, + ( self.__tokdict.copy(), + self.__parent is not None and self.__parent() or None, + self.__accumNames, + self.__name ) ) + + def __setstate__(self,state): + self.__toklist = state[0] + (self.__tokdict, + par, + inAccumNames, + self.__name) = state[1] + self.__accumNames = {} + self.__accumNames.update(inAccumNames) + if par is not None: + self.__parent = wkref(par) + else: + self.__parent = None + + def __getnewargs__(self): + return self.__toklist, self.__name, self.__asList, self.__modal + + def __dir__(self): + return (dir(type(self)) + list(self.keys())) + +MutableMapping.register(ParseResults) + +def col (loc,strg): + """Returns current column within a string, counting newlines as line separators. + The first column is number 1. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information + on parsing strings containing C{<TAB>}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + s = strg + return 1 if 0<loc<len(s) and s[loc-1] == '\n' else loc - s.rfind("\n", 0, loc) + +def lineno(loc,strg): + """Returns current line number within a string, counting newlines as line separators. + The first line is number 1. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information + on parsing strings containing C{<TAB>}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + return strg.count("\n",0,loc) + 1 + +def line( loc, strg ): + """Returns the line of text containing loc within a string, counting newlines as line separators. + """ + lastCR = strg.rfind("\n", 0, loc) + nextCR = strg.find("\n", loc) + if nextCR >= 0: + return strg[lastCR+1:nextCR] + else: + return strg[lastCR+1:] + +def _defaultStartDebugAction( instring, loc, expr ): + print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))) + +def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ): + print ("Matched " + _ustr(expr) + " -> " + str(toks.asList())) + +def _defaultExceptionDebugAction( instring, loc, expr, exc ): + print ("Exception raised:" + _ustr(exc)) + +def nullDebugAction(*args): + """'Do-nothing' debug action, to suppress debugging output during parsing.""" + pass + +# Only works on Python 3.x - nonlocal is toxic to Python 2 installs +#~ 'decorator to trim function calls to match the arity of the target' +#~ def _trim_arity(func, maxargs=3): + #~ if func in singleArgBuiltins: + #~ return lambda s,l,t: func(t) + #~ limit = 0 + #~ foundArity = False + #~ def wrapper(*args): + #~ nonlocal limit,foundArity + #~ while 1: + #~ try: + #~ ret = func(*args[limit:]) + #~ foundArity = True + #~ return ret + #~ except TypeError: + #~ if limit == maxargs or foundArity: + #~ raise + #~ limit += 1 + #~ continue + #~ return wrapper + +# this version is Python 2.x-3.x cross-compatible +'decorator to trim function calls to match the arity of the target' +def _trim_arity(func, maxargs=2): + if func in singleArgBuiltins: + return lambda s,l,t: func(t) + limit = [0] + foundArity = [False] + + # traceback return data structure changed in Py3.5 - normalize back to plain tuples + if system_version[:2] >= (3,5): + def extract_stack(limit=0): + # special handling for Python 3.5.0 - extra deep call stack by 1 + offset = -3 if system_version == (3,5,0) else -2 + frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset] + return [frame_summary[:2]] + def extract_tb(tb, limit=0): + frames = traceback.extract_tb(tb, limit=limit) + frame_summary = frames[-1] + return [frame_summary[:2]] + else: + extract_stack = traceback.extract_stack + extract_tb = traceback.extract_tb + + # synthesize what would be returned by traceback.extract_stack at the call to + # user's parse action 'func', so that we don't incur call penalty at parse time + + LINE_DIFF = 6 + # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND + # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!! + this_line = extract_stack(limit=2)[-1] + pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF) + + def wrapper(*args): + while 1: + try: + ret = func(*args[limit[0]:]) + foundArity[0] = True + return ret + except TypeError: + # re-raise TypeErrors if they did not come from our arity testing + if foundArity[0]: + raise + else: + try: + tb = sys.exc_info()[-1] + if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth: + raise + finally: + del tb + + if limit[0] <= maxargs: + limit[0] += 1 + continue + raise + + # copy func name to wrapper for sensible debug output + func_name = "<parse action>" + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + wrapper.__name__ = func_name + + return wrapper + +class ParserElement(object): + """Abstract base level parser element class.""" + DEFAULT_WHITE_CHARS = " \n\t\r" + verbose_stacktrace = False + + @staticmethod + def setDefaultWhitespaceChars( chars ): + r""" + Overrides the default whitespace chars + + Example:: + # default whitespace chars are space, <TAB> and newline + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def', 'ghi', 'jkl'] + + # change to just treat newline as significant + ParserElement.setDefaultWhitespaceChars(" \t") + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def'] + """ + ParserElement.DEFAULT_WHITE_CHARS = chars + + @staticmethod + def inlineLiteralsUsing(cls): + """ + Set class to be used for inclusion of string literals into a parser. + + Example:: + # default literal class used is Literal + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + + # change to Suppress + ParserElement.inlineLiteralsUsing(Suppress) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '12', '31'] + """ + ParserElement._literalStringClass = cls + + def __init__( self, savelist=False ): + self.parseAction = list() + self.failAction = None + #~ self.name = "<unknown>" # don't define self.name, let subclasses try/except upcall + self.strRepr = None + self.resultsName = None + self.saveAsList = savelist + self.skipWhitespace = True + self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS + self.copyDefaultWhiteChars = True + self.mayReturnEmpty = False # used when checking for left-recursion + self.keepTabs = False + self.ignoreExprs = list() + self.debug = False + self.streamlined = False + self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index + self.errmsg = "" + self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all) + self.debugActions = ( None, None, None ) #custom debug actions + self.re = None + self.callPreparse = True # used to avoid redundant calls to preParse + self.callDuringTry = False + + def copy( self ): + """ + Make a copy of this C{ParserElement}. Useful for defining different parse actions + for the same parsing pattern, using copies of the original parse element. + + Example:: + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K") + integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + + print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M")) + prints:: + [5120, 100, 655360, 268435456] + Equivalent form of C{expr.copy()} is just C{expr()}:: + integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + """ + cpy = copy.copy( self ) + cpy.parseAction = self.parseAction[:] + cpy.ignoreExprs = self.ignoreExprs[:] + if self.copyDefaultWhiteChars: + cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS + return cpy + + def setName( self, name ): + """ + Define name for this expression, makes debugging and exception messages clearer. + + Example:: + Word(nums).parseString("ABC") # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1) + Word(nums).setName("integer").parseString("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) + """ + self.name = name + self.errmsg = "Expected " + self.name + if hasattr(self,"exception"): + self.exception.msg = self.errmsg + return self + + def setResultsName( self, name, listAllMatches=False ): + """ + Define name for referencing matching tokens as a nested attribute + of the returned parse results. + NOTE: this returns a *copy* of the original C{ParserElement} object; + this is so that the client can define a basic element, such as an + integer, and reference it in multiple places with different names. + + You can also set results names using the abbreviated syntax, + C{expr("name")} in place of C{expr.setResultsName("name")} - + see L{I{__call__}<__call__>}. + + Example:: + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + + # equivalent form: + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + """ + newself = self.copy() + if name.endswith("*"): + name = name[:-1] + listAllMatches=True + newself.resultsName = name + newself.modalResults = not listAllMatches + return newself + + def setBreak(self,breakFlag = True): + """Method to invoke the Python pdb debugger when this element is + about to be parsed. Set C{breakFlag} to True to enable, False to + disable. + """ + if breakFlag: + _parseMethod = self._parse + def breaker(instring, loc, doActions=True, callPreParse=True): + import pdb + pdb.set_trace() + return _parseMethod( instring, loc, doActions, callPreParse ) + breaker._originalParseMethod = _parseMethod + self._parse = breaker + else: + if hasattr(self._parse,"_originalParseMethod"): + self._parse = self._parse._originalParseMethod + return self + + def setParseAction( self, *fns, **kwargs ): + """ + Define one or more actions to perform when successfully matching parse element definition. + Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)}, + C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where: + - s = the original string being parsed (see note below) + - loc = the location of the matching substring + - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object + If the functions in fns modify the tokens, they can return them as the return + value from fn, and the modified list of tokens will replace the original. + Otherwise, fn does not need to return any value. + + Optional keyword arguments: + - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{parseString}<parseString>} for more information + on parsing strings containing C{<TAB>}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + + Example:: + integer = Word(nums) + date_str = integer + '/' + integer + '/' + integer + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + # use parse action to convert to ints at parse time + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + date_str = integer + '/' + integer + '/' + integer + + # note that integer fields are now ints, not strings + date_str.parseString("1999/12/31") # -> [1999, '/', 12, '/', 31] + """ + self.parseAction = list(map(_trim_arity, list(fns))) + self.callDuringTry = kwargs.get("callDuringTry", False) + return self + + def addParseAction( self, *fns, **kwargs ): + """ + Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}. + + See examples in L{I{copy}<copy>}. + """ + self.parseAction += list(map(_trim_arity, list(fns))) + self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) + return self + + def addCondition(self, *fns, **kwargs): + """Add a boolean predicate function to expression's list of parse actions. See + L{I{setParseAction}<setParseAction>} for function call signatures. Unlike C{setParseAction}, + functions passed to C{addCondition} need to return boolean success/fail of the condition. + + Optional keyword arguments: + - message = define a custom message to be used in the raised exception + - fatal = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException + + Example:: + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + year_int = integer.copy() + year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later") + date_str = year_int + '/' + integer + '/' + integer + + result = date_str.parseString("1999/12/31") # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1) + """ + msg = kwargs.get("message", "failed user-defined condition") + exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException + for fn in fns: + def pa(s,l,t): + if not bool(_trim_arity(fn)(s,l,t)): + raise exc_type(s,l,msg) + self.parseAction.append(pa) + self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) + return self + + def setFailAction( self, fn ): + """Define action to perform if parsing fails at this expression. + Fail acton fn is a callable function that takes the arguments + C{fn(s,loc,expr,err)} where: + - s = string being parsed + - loc = location where expression match was attempted and failed + - expr = the parse expression that failed + - err = the exception thrown + The function returns no value. It may throw C{L{ParseFatalException}} + if it is desired to stop parsing immediately.""" + self.failAction = fn + return self + + def _skipIgnorables( self, instring, loc ): + exprsFound = True + while exprsFound: + exprsFound = False + for e in self.ignoreExprs: + try: + while 1: + loc,dummy = e._parse( instring, loc ) + exprsFound = True + except ParseException: + pass + return loc + + def preParse( self, instring, loc ): + if self.ignoreExprs: + loc = self._skipIgnorables( instring, loc ) + + if self.skipWhitespace: + wt = self.whiteChars + instrlen = len(instring) + while loc < instrlen and instring[loc] in wt: + loc += 1 + + return loc + + def parseImpl( self, instring, loc, doActions=True ): + return loc, [] + + def postParse( self, instring, loc, tokenlist ): + return tokenlist + + #~ @profile + def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ): + debugging = ( self.debug ) #and doActions ) + + if debugging or self.failAction: + #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) + if (self.debugActions[0] ): + self.debugActions[0]( instring, loc, self ) + if callPreParse and self.callPreparse: + preloc = self.preParse( instring, loc ) + else: + preloc = loc + tokensStart = preloc + try: + try: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + except IndexError: + raise ParseException( instring, len(instring), self.errmsg, self ) + except ParseBaseException as err: + #~ print ("Exception raised:", err) + if self.debugActions[2]: + self.debugActions[2]( instring, tokensStart, self, err ) + if self.failAction: + self.failAction( instring, tokensStart, self, err ) + raise + else: + if callPreParse and self.callPreparse: + preloc = self.preParse( instring, loc ) + else: + preloc = loc + tokensStart = preloc + if self.mayIndexError or preloc >= len(instring): + try: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + except IndexError: + raise ParseException( instring, len(instring), self.errmsg, self ) + else: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + + tokens = self.postParse( instring, loc, tokens ) + + retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults ) + if self.parseAction and (doActions or self.callDuringTry): + if debugging: + try: + for fn in self.parseAction: + tokens = fn( instring, tokensStart, retTokens ) + if tokens is not None: + retTokens = ParseResults( tokens, + self.resultsName, + asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), + modal=self.modalResults ) + except ParseBaseException as err: + #~ print "Exception raised in user parse action:", err + if (self.debugActions[2] ): + self.debugActions[2]( instring, tokensStart, self, err ) + raise + else: + for fn in self.parseAction: + tokens = fn( instring, tokensStart, retTokens ) + if tokens is not None: + retTokens = ParseResults( tokens, + self.resultsName, + asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), + modal=self.modalResults ) + if debugging: + #~ print ("Matched",self,"->",retTokens.asList()) + if (self.debugActions[1] ): + self.debugActions[1]( instring, tokensStart, loc, self, retTokens ) + + return loc, retTokens + + def tryParse( self, instring, loc ): + try: + return self._parse( instring, loc, doActions=False )[0] + except ParseFatalException: + raise ParseException( instring, loc, self.errmsg, self) + + def canParseNext(self, instring, loc): + try: + self.tryParse(instring, loc) + except (ParseException, IndexError): + return False + else: + return True + + class _UnboundedCache(object): + def __init__(self): + cache = {} + self.not_in_cache = not_in_cache = object() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + + def clear(self): + cache.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + if _OrderedDict is not None: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = _OrderedDict() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + while len(cache) > size: + try: + cache.popitem(False) + except KeyError: + pass + + def clear(self): + cache.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + else: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = {} + key_fifo = collections.deque([], size) + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + while len(key_fifo) > size: + cache.pop(key_fifo.popleft(), None) + key_fifo.append(key) + + def clear(self): + cache.clear() + key_fifo.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + # argument cache for optimizing repeated calls when backtracking through recursive expressions + packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail + packrat_cache_lock = RLock() + packrat_cache_stats = [0, 0] + + # this method gets repeatedly called during backtracking with the same arguments - + # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression + def _parseCache( self, instring, loc, doActions=True, callPreParse=True ): + HIT, MISS = 0, 1 + lookup = (self, instring, loc, callPreParse, doActions) + with ParserElement.packrat_cache_lock: + cache = ParserElement.packrat_cache + value = cache.get(lookup) + if value is cache.not_in_cache: + ParserElement.packrat_cache_stats[MISS] += 1 + try: + value = self._parseNoCache(instring, loc, doActions, callPreParse) + except ParseBaseException as pe: + # cache a copy of the exception, without the traceback + cache.set(lookup, pe.__class__(*pe.args)) + raise + else: + cache.set(lookup, (value[0], value[1].copy())) + return value + else: + ParserElement.packrat_cache_stats[HIT] += 1 + if isinstance(value, Exception): + raise value + return (value[0], value[1].copy()) + + _parse = _parseNoCache + + @staticmethod + def resetCache(): + ParserElement.packrat_cache.clear() + ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats) + + _packratEnabled = False + @staticmethod + def enablePackrat(cache_size_limit=128): + """Enables "packrat" parsing, which adds memoizing to the parsing logic. + Repeated parse attempts at the same string location (which happens + often in many complex grammars) can immediately return a cached value, + instead of re-executing parsing/validating code. Memoizing is done of + both valid results and parsing exceptions. + + Parameters: + - cache_size_limit - (default=C{128}) - if an integer value is provided + will limit the size of the packrat cache; if None is passed, then + the cache size will be unbounded; if 0 is passed, the cache will + be effectively disabled. + + This speedup may break existing programs that use parse actions that + have side-effects. For this reason, packrat parsing is disabled when + you first import pyparsing. To activate the packrat feature, your + program must call the class method C{ParserElement.enablePackrat()}. If + your program uses C{psyco} to "compile as you go", you must call + C{enablePackrat} before calling C{psyco.full()}. If you do not do this, + Python will crash. For best results, call C{enablePackrat()} immediately + after importing pyparsing. + + Example:: + from pip._vendor import pyparsing + pyparsing.ParserElement.enablePackrat() + """ + if not ParserElement._packratEnabled: + ParserElement._packratEnabled = True + if cache_size_limit is None: + ParserElement.packrat_cache = ParserElement._UnboundedCache() + else: + ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit) + ParserElement._parse = ParserElement._parseCache + + def parseString( self, instring, parseAll=False ): + """ + Execute the parse expression with the given string. + This is the main interface to the client code, once the complete + expression has been built. + + If you want the grammar to require that the entire input string be + successfully parsed, then set C{parseAll} to True (equivalent to ending + the grammar with C{L{StringEnd()}}). + + Note: C{parseString} implicitly calls C{expandtabs()} on the input string, + in order to report proper column numbers in parse actions. + If the input string contains tabs and + the grammar uses parse actions that use the C{loc} argument to index into the + string being parsed, you can ensure you have a consistent view of the input + string by: + - calling C{parseWithTabs} on your grammar before calling C{parseString} + (see L{I{parseWithTabs}<parseWithTabs>}) + - define your parse action using the full C{(s,loc,toks)} signature, and + reference the input string using the parse action's C{s} argument + - explictly expand the tabs in your input string before calling + C{parseString} + + Example:: + Word('a').parseString('aaaaabaaa') # -> ['aaaaa'] + Word('a').parseString('aaaaabaaa', parseAll=True) # -> Exception: Expected end of text + """ + ParserElement.resetCache() + if not self.streamlined: + self.streamline() + #~ self.saveAsList = True + for e in self.ignoreExprs: + e.streamline() + if not self.keepTabs: + instring = instring.expandtabs() + try: + loc, tokens = self._parse( instring, 0 ) + if parseAll: + loc = self.preParse( instring, loc ) + se = Empty() + StringEnd() + se._parse( instring, loc ) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + else: + return tokens + + def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ): + """ + Scan the input string for expression matches. Each match will return the + matching tokens, start location, and end location. May be called with optional + C{maxMatches} argument, to clip scanning after 'n' matches are found. If + C{overlap} is specified, then overlapping matches will be reported. + + Note that the start and end locations are reported relative to the string + being parsed. See L{I{parseString}<parseString>} for more information on parsing + strings with embedded tabs. + + Example:: + source = "sldjf123lsdjjkf345sldkjf879lkjsfd987" + print(source) + for tokens,start,end in Word(alphas).scanString(source): + print(' '*start + '^'*(end-start)) + print(' '*start + tokens[0]) + + prints:: + + sldjf123lsdjjkf345sldkjf879lkjsfd987 + ^^^^^ + sldjf + ^^^^^^^ + lsdjjkf + ^^^^^^ + sldkjf + ^^^^^^ + lkjsfd + """ + if not self.streamlined: + self.streamline() + for e in self.ignoreExprs: + e.streamline() + + if not self.keepTabs: + instring = _ustr(instring).expandtabs() + instrlen = len(instring) + loc = 0 + preparseFn = self.preParse + parseFn = self._parse + ParserElement.resetCache() + matches = 0 + try: + while loc <= instrlen and matches < maxMatches: + try: + preloc = preparseFn( instring, loc ) + nextLoc,tokens = parseFn( instring, preloc, callPreParse=False ) + except ParseException: + loc = preloc+1 + else: + if nextLoc > loc: + matches += 1 + yield tokens, preloc, nextLoc + if overlap: + nextloc = preparseFn( instring, loc ) + if nextloc > loc: + loc = nextLoc + else: + loc += 1 + else: + loc = nextLoc + else: + loc = preloc+1 + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def transformString( self, instring ): + """ + Extension to C{L{scanString}}, to modify matching text with modified tokens that may + be returned from a parse action. To use C{transformString}, define a grammar and + attach a parse action to it that modifies the returned token list. + Invoking C{transformString()} on a target string will then scan for matches, + and replace the matched text patterns according to the logic in the parse + action. C{transformString()} returns the resulting transformed string. + + Example:: + wd = Word(alphas) + wd.setParseAction(lambda toks: toks[0].title()) + + print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york.")) + Prints:: + Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York. + """ + out = [] + lastE = 0 + # force preservation of <TAB>s, to minimize unwanted transformation of string, and to + # keep string locs straight between transformString and scanString + self.keepTabs = True + try: + for t,s,e in self.scanString( instring ): + out.append( instring[lastE:s] ) + if t: + if isinstance(t,ParseResults): + out += t.asList() + elif isinstance(t,list): + out += t + else: + out.append(t) + lastE = e + out.append(instring[lastE:]) + out = [o for o in out if o] + return "".join(map(_ustr,_flatten(out))) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def searchString( self, instring, maxMatches=_MAX_INT ): + """ + Another extension to C{L{scanString}}, simplifying the access to the tokens found + to match the given parse expression. May be called with optional + C{maxMatches} argument, to clip searching after 'n' matches are found. + + Example:: + # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters + cap_word = Word(alphas.upper(), alphas.lower()) + + print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")) + + # the sum() builtin can be used to merge results into a single ParseResults object + print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))) + prints:: + [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']] + ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity'] + """ + try: + return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ]) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False): + """ + Generator method to split a string using the given expression as a separator. + May be called with optional C{maxsplit} argument, to limit the number of splits; + and the optional C{includeSeparators} argument (default=C{False}), if the separating + matching text should be included in the split results. + + Example:: + punc = oneOf(list(".,;:/-!?")) + print(list(punc.split("This, this?, this sentence, is badly punctuated!"))) + prints:: + ['This', ' this', '', ' this sentence', ' is badly punctuated', ''] + """ + splits = 0 + last = 0 + for t,s,e in self.scanString(instring, maxMatches=maxsplit): + yield instring[last:s] + if includeSeparators: + yield t[0] + last = e + yield instring[last:] + + def __add__(self, other ): + """ + Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement + converts them to L{Literal}s by default. + + Example:: + greet = Word(alphas) + "," + Word(alphas) + "!" + hello = "Hello, World!" + print (hello, "->", greet.parseString(hello)) + Prints:: + Hello, World! -> ['Hello', ',', 'World', '!'] + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return And( [ self, other ] ) + + def __radd__(self, other ): + """ + Implementation of + operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other + self + + def __sub__(self, other): + """ + Implementation of - operator, returns C{L{And}} with error stop + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return self + And._ErrorStop() + other + + def __rsub__(self, other ): + """ + Implementation of - operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other - self + + def __mul__(self,other): + """ + Implementation of * operator, allows use of C{expr * 3} in place of + C{expr + expr + expr}. Expressions may also me multiplied by a 2-integer + tuple, similar to C{{min,max}} multipliers in regular expressions. Tuples + may also include C{None} as in: + - C{expr*(n,None)} or C{expr*(n,)} is equivalent + to C{expr*n + L{ZeroOrMore}(expr)} + (read as "at least n instances of C{expr}") + - C{expr*(None,n)} is equivalent to C{expr*(0,n)} + (read as "0 to n instances of C{expr}") + - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)} + - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)} + + Note that C{expr*(None,n)} does not raise an exception if + more than n exprs exist in the input stream; that is, + C{expr*(None,n)} does not enforce a maximum number of expr + occurrences. If this behavior is desired, then write + C{expr*(None,n) + ~expr} + """ + if isinstance(other,int): + minElements, optElements = other,0 + elif isinstance(other,tuple): + other = (other + (None, None))[:2] + if other[0] is None: + other = (0, other[1]) + if isinstance(other[0],int) and other[1] is None: + if other[0] == 0: + return ZeroOrMore(self) + if other[0] == 1: + return OneOrMore(self) + else: + return self*other[0] + ZeroOrMore(self) + elif isinstance(other[0],int) and isinstance(other[1],int): + minElements, optElements = other + optElements -= minElements + else: + raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1])) + else: + raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other)) + + if minElements < 0: + raise ValueError("cannot multiply ParserElement by negative value") + if optElements < 0: + raise ValueError("second tuple value must be greater or equal to first tuple value") + if minElements == optElements == 0: + raise ValueError("cannot multiply ParserElement by 0 or (0,0)") + + if (optElements): + def makeOptionalList(n): + if n>1: + return Optional(self + makeOptionalList(n-1)) + else: + return Optional(self) + if minElements: + if minElements == 1: + ret = self + makeOptionalList(optElements) + else: + ret = And([self]*minElements) + makeOptionalList(optElements) + else: + ret = makeOptionalList(optElements) + else: + if minElements == 1: + ret = self + else: + ret = And([self]*minElements) + return ret + + def __rmul__(self, other): + return self.__mul__(other) + + def __or__(self, other ): + """ + Implementation of | operator - returns C{L{MatchFirst}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return MatchFirst( [ self, other ] ) + + def __ror__(self, other ): + """ + Implementation of | operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other | self + + def __xor__(self, other ): + """ + Implementation of ^ operator - returns C{L{Or}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return Or( [ self, other ] ) + + def __rxor__(self, other ): + """ + Implementation of ^ operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other ^ self + + def __and__(self, other ): + """ + Implementation of & operator - returns C{L{Each}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return Each( [ self, other ] ) + + def __rand__(self, other ): + """ + Implementation of & operator when left operand is not a C{L{ParserElement}} + """ + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other & self + + def __invert__( self ): + """ + Implementation of ~ operator - returns C{L{NotAny}} + """ + return NotAny( self ) + + def __call__(self, name=None): + """ + Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}. + + If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be + passed as C{True}. + + If C{name} is omitted, same as calling C{L{copy}}. + + Example:: + # these are equivalent + userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") + userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") + """ + if name is not None: + return self.setResultsName(name) + else: + return self.copy() + + def suppress( self ): + """ + Suppresses the output of this C{ParserElement}; useful to keep punctuation from + cluttering up returned output. + """ + return Suppress( self ) + + def leaveWhitespace( self ): + """ + Disables the skipping of whitespace before matching the characters in the + C{ParserElement}'s defined pattern. This is normally only used internally by + the pyparsing module, but may be needed in some whitespace-sensitive grammars. + """ + self.skipWhitespace = False + return self + + def setWhitespaceChars( self, chars ): + """ + Overrides the default whitespace chars + """ + self.skipWhitespace = True + self.whiteChars = chars + self.copyDefaultWhiteChars = False + return self + + def parseWithTabs( self ): + """ + Overrides default behavior to expand C{<TAB>}s to spaces before parsing the input string. + Must be called before C{parseString} when the input grammar contains elements that + match C{<TAB>} characters. + """ + self.keepTabs = True + return self + + def ignore( self, other ): + """ + Define expression to be ignored (e.g., comments) while doing pattern + matching; may be called repeatedly, to define multiple comment or other + ignorable patterns. + + Example:: + patt = OneOrMore(Word(alphas)) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj'] + + patt.ignore(cStyleComment) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd'] + """ + if isinstance(other, basestring): + other = Suppress(other) + + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + self.ignoreExprs.append(other) + else: + self.ignoreExprs.append( Suppress( other.copy() ) ) + return self + + def setDebugActions( self, startAction, successAction, exceptionAction ): + """ + Enable display of debugging messages while doing pattern matching. + """ + self.debugActions = (startAction or _defaultStartDebugAction, + successAction or _defaultSuccessDebugAction, + exceptionAction or _defaultExceptionDebugAction) + self.debug = True + return self + + def setDebug( self, flag=True ): + """ + Enable display of debugging messages while doing pattern matching. + Set C{flag} to True to enable, False to disable. + + Example:: + wd = Word(alphas).setName("alphaword") + integer = Word(nums).setName("numword") + term = wd | integer + + # turn on debugging for wd + wd.setDebug() + + OneOrMore(term).parseString("abc 123 xyz 890") + + prints:: + Match alphaword at loc 0(1,1) + Matched alphaword -> ['abc'] + Match alphaword at loc 3(1,4) + Exception raised:Expected alphaword (at char 4), (line:1, col:5) + Match alphaword at loc 7(1,8) + Matched alphaword -> ['xyz'] + Match alphaword at loc 11(1,12) + Exception raised:Expected alphaword (at char 12), (line:1, col:13) + Match alphaword at loc 15(1,16) + Exception raised:Expected alphaword (at char 15), (line:1, col:16) + + The output shown is that produced by the default debug actions - custom debug actions can be + specified using L{setDebugActions}. Prior to attempting + to match the C{wd} expression, the debugging message C{"Match <exprname> at loc <n>(<line>,<col>)"} + is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"} + message is shown. Also note the use of L{setName} to assign a human-readable name to the expression, + which makes debugging and exception messages easier to understand - for instance, the default + name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}. + """ + if flag: + self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction ) + else: + self.debug = False + return self + + def __str__( self ): + return self.name + + def __repr__( self ): + return _ustr(self) + + def streamline( self ): + self.streamlined = True + self.strRepr = None + return self + + def checkRecursion( self, parseElementList ): + pass + + def validate( self, validateTrace=[] ): + """ + Check defined expressions for valid structure, check for infinite recursive definitions. + """ + self.checkRecursion( [] ) + + def parseFile( self, file_or_filename, parseAll=False ): + """ + Execute the parse expression on the given file or filename. + If a filename is specified (instead of a file object), + the entire file is opened, read, and closed before parsing. + """ + try: + file_contents = file_or_filename.read() + except AttributeError: + with open(file_or_filename, "r") as f: + file_contents = f.read() + try: + return self.parseString(file_contents, parseAll) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def __eq__(self,other): + if isinstance(other, ParserElement): + return self is other or vars(self) == vars(other) + elif isinstance(other, basestring): + return self.matches(other) + else: + return super(ParserElement,self)==other + + def __ne__(self,other): + return not (self == other) + + def __hash__(self): + return hash(id(self)) + + def __req__(self,other): + return self == other + + def __rne__(self,other): + return not (self == other) + + def matches(self, testString, parseAll=True): + """ + Method for quick testing of a parser against a test string. Good for simple + inline microtests of sub expressions while building up larger parser. + + Parameters: + - testString - to test against this expression for a match + - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests + + Example:: + expr = Word(nums) + assert expr.matches("100") + """ + try: + self.parseString(_ustr(testString), parseAll=parseAll) + return True + except ParseBaseException: + return False + + def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False): + """ + Execute the parse expression on a series of test strings, showing each + test, the parsed results or where the parse failed. Quick and easy way to + run a parse expression against a list of sample strings. + + Parameters: + - tests - a list of separate test strings, or a multiline string of test strings + - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests + - comment - (default=C{'#'}) - expression for indicating embedded comments in the test + string; pass None to disable comment filtering + - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline; + if False, only dump nested list + - printResults - (default=C{True}) prints test output to stdout + - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing + + Returns: a (success, results) tuple, where success indicates that all tests succeeded + (or failed if C{failureTests} is True), and the results contain a list of lines of each + test's output + + Example:: + number_expr = pyparsing_common.number.copy() + + result = number_expr.runTests(''' + # unsigned integer + 100 + # negative integer + -100 + # float with scientific notation + 6.02e23 + # integer with scientific notation + 1e-12 + ''') + print("Success" if result[0] else "Failed!") + + result = number_expr.runTests(''' + # stray character + 100Z + # missing leading digit before '.' + -.100 + # too many '.' + 3.14.159 + ''', failureTests=True) + print("Success" if result[0] else "Failed!") + prints:: + # unsigned integer + 100 + [100] + + # negative integer + -100 + [-100] + + # float with scientific notation + 6.02e23 + [6.02e+23] + + # integer with scientific notation + 1e-12 + [1e-12] + + Success + + # stray character + 100Z + ^ + FAIL: Expected end of text (at char 3), (line:1, col:4) + + # missing leading digit before '.' + -.100 + ^ + FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1) + + # too many '.' + 3.14.159 + ^ + FAIL: Expected end of text (at char 4), (line:1, col:5) + + Success + + Each test string must be on a single line. If you want to test a string that spans multiple + lines, create a test like this:: + + expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines") + + (Note that this is a raw string literal, you must include the leading 'r'.) + """ + if isinstance(tests, basestring): + tests = list(map(str.strip, tests.rstrip().splitlines())) + if isinstance(comment, basestring): + comment = Literal(comment) + allResults = [] + comments = [] + success = True + for t in tests: + if comment is not None and comment.matches(t, False) or comments and not t: + comments.append(t) + continue + if not t: + continue + out = ['\n'.join(comments), t] + comments = [] + try: + t = t.replace(r'\n','\n') + result = self.parseString(t, parseAll=parseAll) + out.append(result.dump(full=fullDump)) + success = success and not failureTests + except ParseBaseException as pe: + fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else "" + if '\n' in t: + out.append(line(pe.loc, t)) + out.append(' '*(col(pe.loc,t)-1) + '^' + fatal) + else: + out.append(' '*pe.loc + '^' + fatal) + out.append("FAIL: " + str(pe)) + success = success and failureTests + result = pe + except Exception as exc: + out.append("FAIL-EXCEPTION: " + str(exc)) + success = success and failureTests + result = exc + + if printResults: + if fullDump: + out.append('') + print('\n'.join(out)) + + allResults.append((t, result)) + + return success, allResults + + +class Token(ParserElement): + """ + Abstract C{ParserElement} subclass, for defining atomic matching patterns. + """ + def __init__( self ): + super(Token,self).__init__( savelist=False ) + + +class Empty(Token): + """ + An empty token, will always match. + """ + def __init__( self ): + super(Empty,self).__init__() + self.name = "Empty" + self.mayReturnEmpty = True + self.mayIndexError = False + + +class NoMatch(Token): + """ + A token that will never match. + """ + def __init__( self ): + super(NoMatch,self).__init__() + self.name = "NoMatch" + self.mayReturnEmpty = True + self.mayIndexError = False + self.errmsg = "Unmatchable token" + + def parseImpl( self, instring, loc, doActions=True ): + raise ParseException(instring, loc, self.errmsg, self) + + +class Literal(Token): + """ + Token to exactly match a specified string. + + Example:: + Literal('blah').parseString('blah') # -> ['blah'] + Literal('blah').parseString('blahfooblah') # -> ['blah'] + Literal('blah').parseString('bla') # -> Exception: Expected "blah" + + For case-insensitive matching, use L{CaselessLiteral}. + + For keyword matching (force word break before and after the matched string), + use L{Keyword} or L{CaselessKeyword}. + """ + def __init__( self, matchString ): + super(Literal,self).__init__() + self.match = matchString + self.matchLen = len(matchString) + try: + self.firstMatchChar = matchString[0] + except IndexError: + warnings.warn("null string passed to Literal; use Empty() instead", + SyntaxWarning, stacklevel=2) + self.__class__ = Empty + self.name = '"%s"' % _ustr(self.match) + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = False + self.mayIndexError = False + + # Performance tuning: this routine gets called a *lot* + # if this is a single character match string and the first character matches, + # short-circuit as quickly as possible, and avoid calling startswith + #~ @profile + def parseImpl( self, instring, loc, doActions=True ): + if (instring[loc] == self.firstMatchChar and + (self.matchLen==1 or instring.startswith(self.match,loc)) ): + return loc+self.matchLen, self.match + raise ParseException(instring, loc, self.errmsg, self) +_L = Literal +ParserElement._literalStringClass = Literal + +class Keyword(Token): + """ + Token to exactly match a specified string as a keyword, that is, it must be + immediately followed by a non-keyword character. Compare with C{L{Literal}}: + - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}. + - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'} + Accepts two optional constructor arguments in addition to the keyword string: + - C{identChars} is a string of characters that would be valid identifier characters, + defaulting to all alphanumerics + "_" and "$" + - C{caseless} allows case-insensitive matching, default is C{False}. + + Example:: + Keyword("start").parseString("start") # -> ['start'] + Keyword("start").parseString("starting") # -> Exception + + For case-insensitive matching, use L{CaselessKeyword}. + """ + DEFAULT_KEYWORD_CHARS = alphanums+"_$" + + def __init__( self, matchString, identChars=None, caseless=False ): + super(Keyword,self).__init__() + if identChars is None: + identChars = Keyword.DEFAULT_KEYWORD_CHARS + self.match = matchString + self.matchLen = len(matchString) + try: + self.firstMatchChar = matchString[0] + except IndexError: + warnings.warn("null string passed to Keyword; use Empty() instead", + SyntaxWarning, stacklevel=2) + self.name = '"%s"' % self.match + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = False + self.mayIndexError = False + self.caseless = caseless + if caseless: + self.caselessmatch = matchString.upper() + identChars = identChars.upper() + self.identChars = set(identChars) + + def parseImpl( self, instring, loc, doActions=True ): + if self.caseless: + if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and + (loc == 0 or instring[loc-1].upper() not in self.identChars) ): + return loc+self.matchLen, self.match + else: + if (instring[loc] == self.firstMatchChar and + (self.matchLen==1 or instring.startswith(self.match,loc)) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and + (loc == 0 or instring[loc-1] not in self.identChars) ): + return loc+self.matchLen, self.match + raise ParseException(instring, loc, self.errmsg, self) + + def copy(self): + c = super(Keyword,self).copy() + c.identChars = Keyword.DEFAULT_KEYWORD_CHARS + return c + + @staticmethod + def setDefaultKeywordChars( chars ): + """Overrides the default Keyword chars + """ + Keyword.DEFAULT_KEYWORD_CHARS = chars + +class CaselessLiteral(Literal): + """ + Token to match a specified string, ignoring case of letters. + Note: the matched results will always be in the case of the given + match string, NOT the case of the input text. + + Example:: + OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD'] + + (Contrast with example for L{CaselessKeyword}.) + """ + def __init__( self, matchString ): + super(CaselessLiteral,self).__init__( matchString.upper() ) + # Preserve the defining literal. + self.returnString = matchString + self.name = "'%s'" % self.returnString + self.errmsg = "Expected " + self.name + + def parseImpl( self, instring, loc, doActions=True ): + if instring[ loc:loc+self.matchLen ].upper() == self.match: + return loc+self.matchLen, self.returnString + raise ParseException(instring, loc, self.errmsg, self) + +class CaselessKeyword(Keyword): + """ + Caseless version of L{Keyword}. + + Example:: + OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD'] + + (Contrast with example for L{CaselessLiteral}.) + """ + def __init__( self, matchString, identChars=None ): + super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) + + def parseImpl( self, instring, loc, doActions=True ): + if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ): + return loc+self.matchLen, self.match + raise ParseException(instring, loc, self.errmsg, self) + +class CloseMatch(Token): + """ + A variation on L{Literal} which matches "close" matches, that is, + strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters: + - C{match_string} - string to be matched + - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match + + The results from a successful parse will contain the matched text from the input string and the following named results: + - C{mismatches} - a list of the positions within the match_string where mismatches were found + - C{original} - the original match_string used to compare against the input string + + If C{mismatches} is an empty list, then the match was an exact match. + + Example:: + patt = CloseMatch("ATCATCGAATGGA") + patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']}) + patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1) + + # exact match + patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']}) + + # close match allowing up to 2 mismatches + patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2) + patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']}) + """ + def __init__(self, match_string, maxMismatches=1): + super(CloseMatch,self).__init__() + self.name = match_string + self.match_string = match_string + self.maxMismatches = maxMismatches + self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches) + self.mayIndexError = False + self.mayReturnEmpty = False + + def parseImpl( self, instring, loc, doActions=True ): + start = loc + instrlen = len(instring) + maxloc = start + len(self.match_string) + + if maxloc <= instrlen: + match_string = self.match_string + match_stringloc = 0 + mismatches = [] + maxMismatches = self.maxMismatches + + for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)): + src,mat = s_m + if src != mat: + mismatches.append(match_stringloc) + if len(mismatches) > maxMismatches: + break + else: + loc = match_stringloc + 1 + results = ParseResults([instring[start:loc]]) + results['original'] = self.match_string + results['mismatches'] = mismatches + return loc, results + + raise ParseException(instring, loc, self.errmsg, self) + + +class Word(Token): + """ + Token for matching words composed of allowed character sets. + Defined with string containing all allowed initial characters, + an optional string containing allowed body characters (if omitted, + defaults to the initial character set), and an optional minimum, + maximum, and/or exact length. The default value for C{min} is 1 (a + minimum value < 1 is not valid); the default values for C{max} and C{exact} + are 0, meaning no maximum or exact length restriction. An optional + C{excludeChars} parameter can list characters that might be found in + the input C{bodyChars} string; useful to define a word of all printables + except for one or two characters, for instance. + + L{srange} is useful for defining custom character set strings for defining + C{Word} expressions, using range notation from regular expression character sets. + + A common mistake is to use C{Word} to match a specific literal string, as in + C{Word("Address")}. Remember that C{Word} uses the string argument to define + I{sets} of matchable characters. This expression would match "Add", "AAA", + "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'. + To match an exact literal string, use L{Literal} or L{Keyword}. + + pyparsing includes helper strings for building Words: + - L{alphas} + - L{nums} + - L{alphanums} + - L{hexnums} + - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.) + - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.) + - L{printables} (any non-whitespace character) + + Example:: + # a word composed of digits + integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9")) + + # a word with a leading capital, and zero or more lowercase + capital_word = Word(alphas.upper(), alphas.lower()) + + # hostnames are alphanumeric, with leading alpha, and '-' + hostname = Word(alphas, alphanums+'-') + + # roman numeral (not a strict parser, accepts invalid mix of characters) + roman = Word("IVXLCDM") + + # any string of non-whitespace characters, except for ',' + csv_value = Word(printables, excludeChars=",") + """ + def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ): + super(Word,self).__init__() + if excludeChars: + initChars = ''.join(c for c in initChars if c not in excludeChars) + if bodyChars: + bodyChars = ''.join(c for c in bodyChars if c not in excludeChars) + self.initCharsOrig = initChars + self.initChars = set(initChars) + if bodyChars : + self.bodyCharsOrig = bodyChars + self.bodyChars = set(bodyChars) + else: + self.bodyCharsOrig = initChars + self.bodyChars = set(initChars) + + self.maxSpecified = max > 0 + + if min < 1: + raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted") + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayIndexError = False + self.asKeyword = asKeyword + + if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0): + if self.bodyCharsOrig == self.initCharsOrig: + self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig) + elif len(self.initCharsOrig) == 1: + self.reString = "%s[%s]*" % \ + (re.escape(self.initCharsOrig), + _escapeRegexRangeChars(self.bodyCharsOrig),) + else: + self.reString = "[%s][%s]*" % \ + (_escapeRegexRangeChars(self.initCharsOrig), + _escapeRegexRangeChars(self.bodyCharsOrig),) + if self.asKeyword: + self.reString = r"\b"+self.reString+r"\b" + try: + self.re = re.compile( self.reString ) + except Exception: + self.re = None + + def parseImpl( self, instring, loc, doActions=True ): + if self.re: + result = self.re.match(instring,loc) + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + return loc, result.group() + + if not(instring[ loc ] in self.initChars): + raise ParseException(instring, loc, self.errmsg, self) + + start = loc + loc += 1 + instrlen = len(instring) + bodychars = self.bodyChars + maxloc = start + self.maxLen + maxloc = min( maxloc, instrlen ) + while loc < maxloc and instring[loc] in bodychars: + loc += 1 + + throwException = False + if loc - start < self.minLen: + throwException = True + if self.maxSpecified and loc < instrlen and instring[loc] in bodychars: + throwException = True + if self.asKeyword: + if (start>0 and instring[start-1] in bodychars) or (loc<instrlen and instring[loc] in bodychars): + throwException = True + + if throwException: + raise ParseException(instring, loc, self.errmsg, self) + + return loc, instring[start:loc] + + def __str__( self ): + try: + return super(Word,self).__str__() + except Exception: + pass + + + if self.strRepr is None: + + def charsAsStr(s): + if len(s)>4: + return s[:4]+"..." + else: + return s + + if ( self.initCharsOrig != self.bodyCharsOrig ): + self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) ) + else: + self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig) + + return self.strRepr + + +class Regex(Token): + r""" + Token for matching strings that match a given regular expression. + Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module. + If the given regex contains named groups (defined using C{(?P<name>...)}), these will be preserved as + named parse results. + + Example:: + realnum = Regex(r"[+-]?\d+\.\d*") + date = Regex(r'(?P<year>\d{4})-(?P<month>\d\d?)-(?P<day>\d\d?)') + # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression + roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})") + """ + compiledREtype = type(re.compile("[A-Z]")) + def __init__( self, pattern, flags=0): + """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags.""" + super(Regex,self).__init__() + + if isinstance(pattern, basestring): + if not pattern: + warnings.warn("null string passed to Regex; use Empty() instead", + SyntaxWarning, stacklevel=2) + + self.pattern = pattern + self.flags = flags + + try: + self.re = re.compile(self.pattern, self.flags) + self.reString = self.pattern + except sre_constants.error: + warnings.warn("invalid pattern (%s) passed to Regex" % pattern, + SyntaxWarning, stacklevel=2) + raise + + elif isinstance(pattern, Regex.compiledREtype): + self.re = pattern + self.pattern = \ + self.reString = str(pattern) + self.flags = flags + + else: + raise ValueError("Regex may only be constructed with a string or a compiled RE object") + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayIndexError = False + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + result = self.re.match(instring,loc) + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + d = result.groupdict() + ret = ParseResults(result.group()) + if d: + for k in d: + ret[k] = d[k] + return loc,ret + + def __str__( self ): + try: + return super(Regex,self).__str__() + except Exception: + pass + + if self.strRepr is None: + self.strRepr = "Re:(%s)" % repr(self.pattern) + + return self.strRepr + + +class QuotedString(Token): + r""" + Token for matching strings that are delimited by quoting characters. + + Defined with the following parameters: + - quoteChar - string of one or more characters defining the quote delimiting string + - escChar - character to escape quotes, typically backslash (default=C{None}) + - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None}) + - multiline - boolean indicating whether quotes can span multiple lines (default=C{False}) + - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True}) + - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar) + - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True}) + + Example:: + qs = QuotedString('"') + print(qs.searchString('lsjdf "This is the quote" sldjf')) + complex_qs = QuotedString('{{', endQuoteChar='}}') + print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf')) + sql_qs = QuotedString('"', escQuote='""') + print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf')) + prints:: + [['This is the quote']] + [['This is the "quote"']] + [['This is the quote with "embedded" quotes']] + """ + def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True): + super(QuotedString,self).__init__() + + # remove white space from quote chars - wont work anyway + quoteChar = quoteChar.strip() + if not quoteChar: + warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + if endQuoteChar is None: + endQuoteChar = quoteChar + else: + endQuoteChar = endQuoteChar.strip() + if not endQuoteChar: + warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + self.quoteChar = quoteChar + self.quoteCharLen = len(quoteChar) + self.firstQuoteChar = quoteChar[0] + self.endQuoteChar = endQuoteChar + self.endQuoteCharLen = len(endQuoteChar) + self.escChar = escChar + self.escQuote = escQuote + self.unquoteResults = unquoteResults + self.convertWhitespaceEscapes = convertWhitespaceEscapes + + if multiline: + self.flags = re.MULTILINE | re.DOTALL + self.pattern = r'%s(?:[^%s%s]' % \ + ( re.escape(self.quoteChar), + _escapeRegexRangeChars(self.endQuoteChar[0]), + (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) + else: + self.flags = 0 + self.pattern = r'%s(?:[^%s\n\r%s]' % \ + ( re.escape(self.quoteChar), + _escapeRegexRangeChars(self.endQuoteChar[0]), + (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) + if len(self.endQuoteChar) > 1: + self.pattern += ( + '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]), + _escapeRegexRangeChars(self.endQuoteChar[i])) + for i in range(len(self.endQuoteChar)-1,0,-1)) + ')' + ) + if escQuote: + self.pattern += (r'|(?:%s)' % re.escape(escQuote)) + if escChar: + self.pattern += (r'|(?:%s.)' % re.escape(escChar)) + self.escCharReplacePattern = re.escape(self.escChar)+"(.)" + self.pattern += (r')*%s' % re.escape(self.endQuoteChar)) + + try: + self.re = re.compile(self.pattern, self.flags) + self.reString = self.pattern + except sre_constants.error: + warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern, + SyntaxWarning, stacklevel=2) + raise + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayIndexError = False + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + ret = result.group() + + if self.unquoteResults: + + # strip off quotes + ret = ret[self.quoteCharLen:-self.endQuoteCharLen] + + if isinstance(ret,basestring): + # replace escaped whitespace + if '\\' in ret and self.convertWhitespaceEscapes: + ws_map = { + r'\t' : '\t', + r'\n' : '\n', + r'\f' : '\f', + r'\r' : '\r', + } + for wslit,wschar in ws_map.items(): + ret = ret.replace(wslit, wschar) + + # replace escaped characters + if self.escChar: + ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret) + + # replace escaped quotes + if self.escQuote: + ret = ret.replace(self.escQuote, self.endQuoteChar) + + return loc, ret + + def __str__( self ): + try: + return super(QuotedString,self).__str__() + except Exception: + pass + + if self.strRepr is None: + self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar) + + return self.strRepr + + +class CharsNotIn(Token): + """ + Token for matching words composed of characters I{not} in a given set (will + include whitespace in matched characters if not listed in the provided exclusion set - see example). + Defined with string containing all disallowed characters, and an optional + minimum, maximum, and/or exact length. The default value for C{min} is 1 (a + minimum value < 1 is not valid); the default values for C{max} and C{exact} + are 0, meaning no maximum or exact length restriction. + + Example:: + # define a comma-separated-value as anything that is not a ',' + csv_value = CharsNotIn(',') + print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213")) + prints:: + ['dkls', 'lsdkjf', 's12 34', '@!#', '213'] + """ + def __init__( self, notChars, min=1, max=0, exact=0 ): + super(CharsNotIn,self).__init__() + self.skipWhitespace = False + self.notChars = notChars + + if min < 1: + raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted") + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = ( self.minLen == 0 ) + self.mayIndexError = False + + def parseImpl( self, instring, loc, doActions=True ): + if instring[loc] in self.notChars: + raise ParseException(instring, loc, self.errmsg, self) + + start = loc + loc += 1 + notchars = self.notChars + maxlen = min( start+self.maxLen, len(instring) ) + while loc < maxlen and \ + (instring[loc] not in notchars): + loc += 1 + + if loc - start < self.minLen: + raise ParseException(instring, loc, self.errmsg, self) + + return loc, instring[start:loc] + + def __str__( self ): + try: + return super(CharsNotIn, self).__str__() + except Exception: + pass + + if self.strRepr is None: + if len(self.notChars) > 4: + self.strRepr = "!W:(%s...)" % self.notChars[:4] + else: + self.strRepr = "!W:(%s)" % self.notChars + + return self.strRepr + +class White(Token): + """ + Special matching class for matching whitespace. Normally, whitespace is ignored + by pyparsing grammars. This class is included when some whitespace structures + are significant. Define with a string containing the whitespace characters to be + matched; default is C{" \\t\\r\\n"}. Also takes optional C{min}, C{max}, and C{exact} arguments, + as defined for the C{L{Word}} class. + """ + whiteStrs = { + " " : "<SPC>", + "\t": "<TAB>", + "\n": "<LF>", + "\r": "<CR>", + "\f": "<FF>", + } + def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0): + super(White,self).__init__() + self.matchWhite = ws + self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) ) + #~ self.leaveWhitespace() + self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite)) + self.mayReturnEmpty = True + self.errmsg = "Expected " + self.name + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + def parseImpl( self, instring, loc, doActions=True ): + if not(instring[ loc ] in self.matchWhite): + raise ParseException(instring, loc, self.errmsg, self) + start = loc + loc += 1 + maxloc = start + self.maxLen + maxloc = min( maxloc, len(instring) ) + while loc < maxloc and instring[loc] in self.matchWhite: + loc += 1 + + if loc - start < self.minLen: + raise ParseException(instring, loc, self.errmsg, self) + + return loc, instring[start:loc] + + +class _PositionToken(Token): + def __init__( self ): + super(_PositionToken,self).__init__() + self.name=self.__class__.__name__ + self.mayReturnEmpty = True + self.mayIndexError = False + +class GoToColumn(_PositionToken): + """ + Token to advance to a specific column of input text; useful for tabular report scraping. + """ + def __init__( self, colno ): + super(GoToColumn,self).__init__() + self.col = colno + + def preParse( self, instring, loc ): + if col(loc,instring) != self.col: + instrlen = len(instring) + if self.ignoreExprs: + loc = self._skipIgnorables( instring, loc ) + while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col : + loc += 1 + return loc + + def parseImpl( self, instring, loc, doActions=True ): + thiscol = col( loc, instring ) + if thiscol > self.col: + raise ParseException( instring, loc, "Text not in expected column", self ) + newloc = loc + self.col - thiscol + ret = instring[ loc: newloc ] + return newloc, ret + + +class LineStart(_PositionToken): + """ + Matches if current position is at the beginning of a line within the parse string + + Example:: + + test = '''\ + AAA this line + AAA and this line + AAA but not this one + B AAA and definitely not this one + ''' + + for t in (LineStart() + 'AAA' + restOfLine).searchString(test): + print(t) + + Prints:: + ['AAA', ' this line'] + ['AAA', ' and this line'] + + """ + def __init__( self ): + super(LineStart,self).__init__() + self.errmsg = "Expected start of line" + + def parseImpl( self, instring, loc, doActions=True ): + if col(loc, instring) == 1: + return loc, [] + raise ParseException(instring, loc, self.errmsg, self) + +class LineEnd(_PositionToken): + """ + Matches if current position is at the end of a line within the parse string + """ + def __init__( self ): + super(LineEnd,self).__init__() + self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) + self.errmsg = "Expected end of line" + + def parseImpl( self, instring, loc, doActions=True ): + if loc<len(instring): + if instring[loc] == "\n": + return loc+1, "\n" + else: + raise ParseException(instring, loc, self.errmsg, self) + elif loc == len(instring): + return loc+1, [] + else: + raise ParseException(instring, loc, self.errmsg, self) + +class StringStart(_PositionToken): + """ + Matches if current position is at the beginning of the parse string + """ + def __init__( self ): + super(StringStart,self).__init__() + self.errmsg = "Expected start of text" + + def parseImpl( self, instring, loc, doActions=True ): + if loc != 0: + # see if entire string up to here is just whitespace and ignoreables + if loc != self.preParse( instring, 0 ): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + +class StringEnd(_PositionToken): + """ + Matches if current position is at the end of the parse string + """ + def __init__( self ): + super(StringEnd,self).__init__() + self.errmsg = "Expected end of text" + + def parseImpl( self, instring, loc, doActions=True ): + if loc < len(instring): + raise ParseException(instring, loc, self.errmsg, self) + elif loc == len(instring): + return loc+1, [] + elif loc > len(instring): + return loc, [] + else: + raise ParseException(instring, loc, self.errmsg, self) + +class WordStart(_PositionToken): + """ + Matches if the current position is at the beginning of a Word, and + is not preceded by any character in a given set of C{wordChars} + (default=C{printables}). To emulate the C{\b} behavior of regular expressions, + use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of + the string being parsed, or at the beginning of a line. + """ + def __init__(self, wordChars = printables): + super(WordStart,self).__init__() + self.wordChars = set(wordChars) + self.errmsg = "Not at the start of a word" + + def parseImpl(self, instring, loc, doActions=True ): + if loc != 0: + if (instring[loc-1] in self.wordChars or + instring[loc] not in self.wordChars): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + +class WordEnd(_PositionToken): + """ + Matches if the current position is at the end of a Word, and + is not followed by any character in a given set of C{wordChars} + (default=C{printables}). To emulate the C{\b} behavior of regular expressions, + use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of + the string being parsed, or at the end of a line. + """ + def __init__(self, wordChars = printables): + super(WordEnd,self).__init__() + self.wordChars = set(wordChars) + self.skipWhitespace = False + self.errmsg = "Not at the end of a word" + + def parseImpl(self, instring, loc, doActions=True ): + instrlen = len(instring) + if instrlen>0 and loc<instrlen: + if (instring[loc] in self.wordChars or + instring[loc-1] not in self.wordChars): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + + +class ParseExpression(ParserElement): + """ + Abstract subclass of ParserElement, for combining and post-processing parsed tokens. + """ + def __init__( self, exprs, savelist = False ): + super(ParseExpression,self).__init__(savelist) + if isinstance( exprs, _generatorType ): + exprs = list(exprs) + + if isinstance( exprs, basestring ): + self.exprs = [ ParserElement._literalStringClass( exprs ) ] + elif isinstance( exprs, Iterable ): + exprs = list(exprs) + # if sequence of strings provided, wrap with Literal + if all(isinstance(expr, basestring) for expr in exprs): + exprs = map(ParserElement._literalStringClass, exprs) + self.exprs = list(exprs) + else: + try: + self.exprs = list( exprs ) + except TypeError: + self.exprs = [ exprs ] + self.callPreparse = False + + def __getitem__( self, i ): + return self.exprs[i] + + def append( self, other ): + self.exprs.append( other ) + self.strRepr = None + return self + + def leaveWhitespace( self ): + """Extends C{leaveWhitespace} defined in base class, and also invokes C{leaveWhitespace} on + all contained expressions.""" + self.skipWhitespace = False + self.exprs = [ e.copy() for e in self.exprs ] + for e in self.exprs: + e.leaveWhitespace() + return self + + def ignore( self, other ): + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + super( ParseExpression, self).ignore( other ) + for e in self.exprs: + e.ignore( self.ignoreExprs[-1] ) + else: + super( ParseExpression, self).ignore( other ) + for e in self.exprs: + e.ignore( self.ignoreExprs[-1] ) + return self + + def __str__( self ): + try: + return super(ParseExpression,self).__str__() + except Exception: + pass + + if self.strRepr is None: + self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.exprs) ) + return self.strRepr + + def streamline( self ): + super(ParseExpression,self).streamline() + + for e in self.exprs: + e.streamline() + + # collapse nested And's of the form And( And( And( a,b), c), d) to And( a,b,c,d ) + # but only if there are no parse actions or resultsNames on the nested And's + # (likewise for Or's and MatchFirst's) + if ( len(self.exprs) == 2 ): + other = self.exprs[0] + if ( isinstance( other, self.__class__ ) and + not(other.parseAction) and + other.resultsName is None and + not other.debug ): + self.exprs = other.exprs[:] + [ self.exprs[1] ] + self.strRepr = None + self.mayReturnEmpty |= other.mayReturnEmpty + self.mayIndexError |= other.mayIndexError + + other = self.exprs[-1] + if ( isinstance( other, self.__class__ ) and + not(other.parseAction) and + other.resultsName is None and + not other.debug ): + self.exprs = self.exprs[:-1] + other.exprs[:] + self.strRepr = None + self.mayReturnEmpty |= other.mayReturnEmpty + self.mayIndexError |= other.mayIndexError + + self.errmsg = "Expected " + _ustr(self) + + return self + + def setResultsName( self, name, listAllMatches=False ): + ret = super(ParseExpression,self).setResultsName(name,listAllMatches) + return ret + + def validate( self, validateTrace=[] ): + tmp = validateTrace[:]+[self] + for e in self.exprs: + e.validate(tmp) + self.checkRecursion( [] ) + + def copy(self): + ret = super(ParseExpression,self).copy() + ret.exprs = [e.copy() for e in self.exprs] + return ret + +class And(ParseExpression): + """ + Requires all given C{ParseExpression}s to be found in the given order. + Expressions may be separated by whitespace. + May be constructed using the C{'+'} operator. + May also be constructed using the C{'-'} operator, which will suppress backtracking. + + Example:: + integer = Word(nums) + name_expr = OneOrMore(Word(alphas)) + + expr = And([integer("id"),name_expr("name"),integer("age")]) + # more easily written as: + expr = integer("id") + name_expr("name") + integer("age") + """ + + class _ErrorStop(Empty): + def __init__(self, *args, **kwargs): + super(And._ErrorStop,self).__init__(*args, **kwargs) + self.name = '-' + self.leaveWhitespace() + + def __init__( self, exprs, savelist = True ): + super(And,self).__init__(exprs, savelist) + self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) + self.setWhitespaceChars( self.exprs[0].whiteChars ) + self.skipWhitespace = self.exprs[0].skipWhitespace + self.callPreparse = True + + def parseImpl( self, instring, loc, doActions=True ): + # pass False as last arg to _parse for first element, since we already + # pre-parsed the string as part of our And pre-parsing + loc, resultlist = self.exprs[0]._parse( instring, loc, doActions, callPreParse=False ) + errorStop = False + for e in self.exprs[1:]: + if isinstance(e, And._ErrorStop): + errorStop = True + continue + if errorStop: + try: + loc, exprtokens = e._parse( instring, loc, doActions ) + except ParseSyntaxException: + raise + except ParseBaseException as pe: + pe.__traceback__ = None + raise ParseSyntaxException._from_exception(pe) + except IndexError: + raise ParseSyntaxException(instring, len(instring), self.errmsg, self) + else: + loc, exprtokens = e._parse( instring, loc, doActions ) + if exprtokens or exprtokens.haskeys(): + resultlist += exprtokens + return loc, resultlist + + def __iadd__(self, other ): + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + return self.append( other ) #And( [ self, other ] ) + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + if not e.mayReturnEmpty: + break + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + +class Or(ParseExpression): + """ + Requires that at least one C{ParseExpression} is found. + If two expressions match, the expression that matches the longest string will be used. + May be constructed using the C{'^'} operator. + + Example:: + # construct Or using '^' operator + + number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) + prints:: + [['123'], ['3.1416'], ['789']] + """ + def __init__( self, exprs, savelist = False ): + super(Or,self).__init__(exprs, savelist) + if self.exprs: + self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) + else: + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + maxExcLoc = -1 + maxException = None + matches = [] + for e in self.exprs: + try: + loc2 = e.tryParse( instring, loc ) + except ParseException as err: + err.__traceback__ = None + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + except IndexError: + if len(instring) > maxExcLoc: + maxException = ParseException(instring,len(instring),e.errmsg,self) + maxExcLoc = len(instring) + else: + # save match among all matches, to retry longest to shortest + matches.append((loc2, e)) + + if matches: + matches.sort(key=lambda x: -x[0]) + for _,e in matches: + try: + return e._parse( instring, loc, doActions ) + except ParseException as err: + err.__traceback__ = None + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + + if maxException is not None: + maxException.msg = self.errmsg + raise maxException + else: + raise ParseException(instring, loc, "no defined alternatives to match", self) + + + def __ixor__(self, other ): + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + return self.append( other ) #Or( [ self, other ] ) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class MatchFirst(ParseExpression): + """ + Requires that at least one C{ParseExpression} is found. + If two expressions match, the first one listed is the one that will match. + May be constructed using the C{'|'} operator. + + Example:: + # construct MatchFirst using '|' operator + + # watch the order of expressions to match + number = Word(nums) | Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']] + + # put more selective expression first + number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums) + print(number.searchString("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']] + """ + def __init__( self, exprs, savelist = False ): + super(MatchFirst,self).__init__(exprs, savelist) + if self.exprs: + self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) + else: + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + maxExcLoc = -1 + maxException = None + for e in self.exprs: + try: + ret = e._parse( instring, loc, doActions ) + return ret + except ParseException as err: + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + except IndexError: + if len(instring) > maxExcLoc: + maxException = ParseException(instring,len(instring),e.errmsg,self) + maxExcLoc = len(instring) + + # only got here if no expression matched, raise exception for match that made it the furthest + else: + if maxException is not None: + maxException.msg = self.errmsg + raise maxException + else: + raise ParseException(instring, loc, "no defined alternatives to match", self) + + def __ior__(self, other ): + if isinstance( other, basestring ): + other = ParserElement._literalStringClass( other ) + return self.append( other ) #MatchFirst( [ self, other ] ) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class Each(ParseExpression): + """ + Requires all given C{ParseExpression}s to be found, but in any order. + Expressions may be separated by whitespace. + May be constructed using the C{'&'} operator. + + Example:: + color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN") + shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON") + integer = Word(nums) + shape_attr = "shape:" + shape_type("shape") + posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn") + color_attr = "color:" + color("color") + size_attr = "size:" + integer("size") + + # use Each (using operator '&') to accept attributes in any order + # (shape and posn are required, color and size are optional) + shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr) + + shape_spec.runTests(''' + shape: SQUARE color: BLACK posn: 100, 120 + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + color:GREEN size:20 shape:TRIANGLE posn:20,40 + ''' + ) + prints:: + shape: SQUARE color: BLACK posn: 100, 120 + ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']] + - color: BLACK + - posn: ['100', ',', '120'] + - x: 100 + - y: 120 + - shape: SQUARE + + + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']] + - color: BLUE + - posn: ['50', ',', '80'] + - x: 50 + - y: 80 + - shape: CIRCLE + - size: 50 + + + color: GREEN size: 20 shape: TRIANGLE posn: 20,40 + ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']] + - color: GREEN + - posn: ['20', ',', '40'] + - x: 20 + - y: 40 + - shape: TRIANGLE + - size: 20 + """ + def __init__( self, exprs, savelist = True ): + super(Each,self).__init__(exprs, savelist) + self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) + self.skipWhitespace = True + self.initExprGroups = True + + def parseImpl( self, instring, loc, doActions=True ): + if self.initExprGroups: + self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional)) + opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ] + opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)] + self.optionals = opt1 + opt2 + self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ] + self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ] + self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ] + self.required += self.multirequired + self.initExprGroups = False + tmpLoc = loc + tmpReqd = self.required[:] + tmpOpt = self.optionals[:] + matchOrder = [] + + keepMatching = True + while keepMatching: + tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired + failed = [] + for e in tmpExprs: + try: + tmpLoc = e.tryParse( instring, tmpLoc ) + except ParseException: + failed.append(e) + else: + matchOrder.append(self.opt1map.get(id(e),e)) + if e in tmpReqd: + tmpReqd.remove(e) + elif e in tmpOpt: + tmpOpt.remove(e) + if len(failed) == len(tmpExprs): + keepMatching = False + + if tmpReqd: + missing = ", ".join(_ustr(e) for e in tmpReqd) + raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing ) + + # add any unmatched Optionals, in case they have default values defined + matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt] + + resultlist = [] + for e in matchOrder: + loc,results = e._parse(instring,loc,doActions) + resultlist.append(results) + + finalResults = sum(resultlist, ParseResults([])) + return loc, finalResults + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class ParseElementEnhance(ParserElement): + """ + Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens. + """ + def __init__( self, expr, savelist=False ): + super(ParseElementEnhance,self).__init__(savelist) + if isinstance( expr, basestring ): + if issubclass(ParserElement._literalStringClass, Token): + expr = ParserElement._literalStringClass(expr) + else: + expr = ParserElement._literalStringClass(Literal(expr)) + self.expr = expr + self.strRepr = None + if expr is not None: + self.mayIndexError = expr.mayIndexError + self.mayReturnEmpty = expr.mayReturnEmpty + self.setWhitespaceChars( expr.whiteChars ) + self.skipWhitespace = expr.skipWhitespace + self.saveAsList = expr.saveAsList + self.callPreparse = expr.callPreparse + self.ignoreExprs.extend(expr.ignoreExprs) + + def parseImpl( self, instring, loc, doActions=True ): + if self.expr is not None: + return self.expr._parse( instring, loc, doActions, callPreParse=False ) + else: + raise ParseException("",loc,self.errmsg,self) + + def leaveWhitespace( self ): + self.skipWhitespace = False + self.expr = self.expr.copy() + if self.expr is not None: + self.expr.leaveWhitespace() + return self + + def ignore( self, other ): + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + super( ParseElementEnhance, self).ignore( other ) + if self.expr is not None: + self.expr.ignore( self.ignoreExprs[-1] ) + else: + super( ParseElementEnhance, self).ignore( other ) + if self.expr is not None: + self.expr.ignore( self.ignoreExprs[-1] ) + return self + + def streamline( self ): + super(ParseElementEnhance,self).streamline() + if self.expr is not None: + self.expr.streamline() + return self + + def checkRecursion( self, parseElementList ): + if self in parseElementList: + raise RecursiveGrammarException( parseElementList+[self] ) + subRecCheckList = parseElementList[:] + [ self ] + if self.expr is not None: + self.expr.checkRecursion( subRecCheckList ) + + def validate( self, validateTrace=[] ): + tmp = validateTrace[:]+[self] + if self.expr is not None: + self.expr.validate(tmp) + self.checkRecursion( [] ) + + def __str__( self ): + try: + return super(ParseElementEnhance,self).__str__() + except Exception: + pass + + if self.strRepr is None and self.expr is not None: + self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) ) + return self.strRepr + + +class FollowedBy(ParseElementEnhance): + """ + Lookahead matching of the given parse expression. C{FollowedBy} + does I{not} advance the parsing position within the input string, it only + verifies that the specified parse expression matches at the current + position. C{FollowedBy} always returns a null token list. + + Example:: + # use FollowedBy to match a label only if it is followed by a ':' + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint() + prints:: + [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']] + """ + def __init__( self, expr ): + super(FollowedBy,self).__init__(expr) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + self.expr.tryParse( instring, loc ) + return loc, [] + + +class NotAny(ParseElementEnhance): + """ + Lookahead to disallow matching with the given parse expression. C{NotAny} + does I{not} advance the parsing position within the input string, it only + verifies that the specified parse expression does I{not} match at the current + position. Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny} + always returns a null token list. May be constructed using the '~' operator. + + Example:: + + """ + def __init__( self, expr ): + super(NotAny,self).__init__(expr) + #~ self.leaveWhitespace() + self.skipWhitespace = False # do NOT use self.leaveWhitespace(), don't want to propagate to exprs + self.mayReturnEmpty = True + self.errmsg = "Found unwanted token, "+_ustr(self.expr) + + def parseImpl( self, instring, loc, doActions=True ): + if self.expr.canParseNext(instring, loc): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "~{" + _ustr(self.expr) + "}" + + return self.strRepr + +class _MultipleMatch(ParseElementEnhance): + def __init__( self, expr, stopOn=None): + super(_MultipleMatch, self).__init__(expr) + self.saveAsList = True + ender = stopOn + if isinstance(ender, basestring): + ender = ParserElement._literalStringClass(ender) + self.not_ender = ~ender if ender is not None else None + + def parseImpl( self, instring, loc, doActions=True ): + self_expr_parse = self.expr._parse + self_skip_ignorables = self._skipIgnorables + check_ender = self.not_ender is not None + if check_ender: + try_not_ender = self.not_ender.tryParse + + # must be at least one (but first see if we are the stopOn sentinel; + # if so, fail) + if check_ender: + try_not_ender(instring, loc) + loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False ) + try: + hasIgnoreExprs = (not not self.ignoreExprs) + while 1: + if check_ender: + try_not_ender(instring, loc) + if hasIgnoreExprs: + preloc = self_skip_ignorables( instring, loc ) + else: + preloc = loc + loc, tmptokens = self_expr_parse( instring, preloc, doActions ) + if tmptokens or tmptokens.haskeys(): + tokens += tmptokens + except (ParseException,IndexError): + pass + + return loc, tokens + +class OneOrMore(_MultipleMatch): + """ + Repetition of one or more of the given expression. + + Parameters: + - expr - expression that must match one or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: BLACK" + OneOrMore(attr_expr).parseString(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] + + # use stopOn attribute for OneOrMore to avoid reading label string as part of the data + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] + + # could also be written as + (attr_expr * (1,)).parseString(text).pprint() + """ + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + _ustr(self.expr) + "}..." + + return self.strRepr + +class ZeroOrMore(_MultipleMatch): + """ + Optional repetition of zero or more of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example: similar to L{OneOrMore} + """ + def __init__( self, expr, stopOn=None): + super(ZeroOrMore,self).__init__(expr, stopOn=stopOn) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + try: + return super(ZeroOrMore, self).parseImpl(instring, loc, doActions) + except (ParseException,IndexError): + return loc, [] + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "[" + _ustr(self.expr) + "]..." + + return self.strRepr + +class _NullToken(object): + def __bool__(self): + return False + __nonzero__ = __bool__ + def __str__(self): + return "" + +_optionalNotMatched = _NullToken() +class Optional(ParseElementEnhance): + """ + Optional matching of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - default (optional) - value to be returned if the optional expression is not found. + + Example:: + # US postal code can be a 5-digit zip, plus optional 4-digit qualifier + zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4))) + zip.runTests(''' + # traditional ZIP code + 12345 + + # ZIP+4 form + 12101-0001 + + # invalid ZIP + 98765- + ''') + prints:: + # traditional ZIP code + 12345 + ['12345'] + + # ZIP+4 form + 12101-0001 + ['12101-0001'] + + # invalid ZIP + 98765- + ^ + FAIL: Expected end of text (at char 5), (line:1, col:6) + """ + def __init__( self, expr, default=_optionalNotMatched ): + super(Optional,self).__init__( expr, savelist=False ) + self.saveAsList = self.expr.saveAsList + self.defaultValue = default + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + try: + loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) + except (ParseException,IndexError): + if self.defaultValue is not _optionalNotMatched: + if self.expr.resultsName: + tokens = ParseResults([ self.defaultValue ]) + tokens[self.expr.resultsName] = self.defaultValue + else: + tokens = [ self.defaultValue ] + else: + tokens = [] + return loc, tokens + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "[" + _ustr(self.expr) + "]" + + return self.strRepr + +class SkipTo(ParseElementEnhance): + """ + Token for skipping over all undefined text until the matched expression is found. + + Parameters: + - expr - target expression marking the end of the data to be skipped + - include - (default=C{False}) if True, the target expression is also parsed + (the skipped text and target expression are returned as a 2-element list). + - ignore - (default=C{None}) used to define grammars (typically quoted strings and + comments) that might contain false matches to the target expression + - failOn - (default=C{None}) define expressions that are not allowed to be + included in the skipped test; if found before the target expression is found, + the SkipTo is not a match + + Example:: + report = ''' + Outstanding Issues Report - 1 Jan 2000 + + # | Severity | Description | Days Open + -----+----------+-------------------------------------------+----------- + 101 | Critical | Intermittent system crash | 6 + 94 | Cosmetic | Spelling error on Login ('log|n') | 14 + 79 | Minor | System slow when running too many reports | 47 + ''' + integer = Word(nums) + SEP = Suppress('|') + # use SkipTo to simply match everything up until the next SEP + # - ignore quoted strings, so that a '|' character inside a quoted string does not match + # - parse action will call token.strip() for each matched token, i.e., the description body + string_data = SkipTo(SEP, ignore=quotedString) + string_data.setParseAction(tokenMap(str.strip)) + ticket_expr = (integer("issue_num") + SEP + + string_data("sev") + SEP + + string_data("desc") + SEP + + integer("days_open")) + + for tkt in ticket_expr.searchString(report): + print tkt.dump() + prints:: + ['101', 'Critical', 'Intermittent system crash', '6'] + - days_open: 6 + - desc: Intermittent system crash + - issue_num: 101 + - sev: Critical + ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14'] + - days_open: 14 + - desc: Spelling error on Login ('log|n') + - issue_num: 94 + - sev: Cosmetic + ['79', 'Minor', 'System slow when running too many reports', '47'] + - days_open: 47 + - desc: System slow when running too many reports + - issue_num: 79 + - sev: Minor + """ + def __init__( self, other, include=False, ignore=None, failOn=None ): + super( SkipTo, self ).__init__( other ) + self.ignoreExpr = ignore + self.mayReturnEmpty = True + self.mayIndexError = False + self.includeMatch = include + self.asList = False + if isinstance(failOn, basestring): + self.failOn = ParserElement._literalStringClass(failOn) + else: + self.failOn = failOn + self.errmsg = "No match found for "+_ustr(self.expr) + + def parseImpl( self, instring, loc, doActions=True ): + startloc = loc + instrlen = len(instring) + expr = self.expr + expr_parse = self.expr._parse + self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None + self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None + + tmploc = loc + while tmploc <= instrlen: + if self_failOn_canParseNext is not None: + # break if failOn expression matches + if self_failOn_canParseNext(instring, tmploc): + break + + if self_ignoreExpr_tryParse is not None: + # advance past ignore expressions + while 1: + try: + tmploc = self_ignoreExpr_tryParse(instring, tmploc) + except ParseBaseException: + break + + try: + expr_parse(instring, tmploc, doActions=False, callPreParse=False) + except (ParseException, IndexError): + # no match, advance loc in string + tmploc += 1 + else: + # matched skipto expr, done + break + + else: + # ran off the end of the input string without matching skipto expr, fail + raise ParseException(instring, loc, self.errmsg, self) + + # build up return values + loc = tmploc + skiptext = instring[startloc:loc] + skipresult = ParseResults(skiptext) + + if self.includeMatch: + loc, mat = expr_parse(instring,loc,doActions,callPreParse=False) + skipresult += mat + + return loc, skipresult + +class Forward(ParseElementEnhance): + """ + Forward declaration of an expression to be defined later - + used for recursive grammars, such as algebraic infix notation. + When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator. + + Note: take care when assigning to C{Forward} not to overlook precedence of operators. + Specifically, '|' has a lower precedence than '<<', so that:: + fwdExpr << a | b | c + will actually be evaluated as:: + (fwdExpr << a) | b | c + thereby leaving b and c out as parseable alternatives. It is recommended that you + explicitly group the values inserted into the C{Forward}:: + fwdExpr << (a | b | c) + Converting to use the '<<=' operator instead will avoid this problem. + + See L{ParseResults.pprint} for an example of a recursive parser created using + C{Forward}. + """ + def __init__( self, other=None ): + super(Forward,self).__init__( other, savelist=False ) + + def __lshift__( self, other ): + if isinstance( other, basestring ): + other = ParserElement._literalStringClass(other) + self.expr = other + self.strRepr = None + self.mayIndexError = self.expr.mayIndexError + self.mayReturnEmpty = self.expr.mayReturnEmpty + self.setWhitespaceChars( self.expr.whiteChars ) + self.skipWhitespace = self.expr.skipWhitespace + self.saveAsList = self.expr.saveAsList + self.ignoreExprs.extend(self.expr.ignoreExprs) + return self + + def __ilshift__(self, other): + return self << other + + def leaveWhitespace( self ): + self.skipWhitespace = False + return self + + def streamline( self ): + if not self.streamlined: + self.streamlined = True + if self.expr is not None: + self.expr.streamline() + return self + + def validate( self, validateTrace=[] ): + if self not in validateTrace: + tmp = validateTrace[:]+[self] + if self.expr is not None: + self.expr.validate(tmp) + self.checkRecursion([]) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + return self.__class__.__name__ + ": ..." + + # stubbed out for now - creates awful memory and perf issues + self._revertClass = self.__class__ + self.__class__ = _ForwardNoRecurse + try: + if self.expr is not None: + retString = _ustr(self.expr) + else: + retString = "None" + finally: + self.__class__ = self._revertClass + return self.__class__.__name__ + ": " + retString + + def copy(self): + if self.expr is not None: + return super(Forward,self).copy() + else: + ret = Forward() + ret <<= self + return ret + +class _ForwardNoRecurse(Forward): + def __str__( self ): + return "..." + +class TokenConverter(ParseElementEnhance): + """ + Abstract subclass of C{ParseExpression}, for converting parsed results. + """ + def __init__( self, expr, savelist=False ): + super(TokenConverter,self).__init__( expr )#, savelist ) + self.saveAsList = False + +class Combine(TokenConverter): + """ + Converter to concatenate all matching tokens to a single string. + By default, the matching patterns must also be contiguous in the input string; + this can be disabled by specifying C{'adjacent=False'} in the constructor. + + Example:: + real = Word(nums) + '.' + Word(nums) + print(real.parseString('3.1416')) # -> ['3', '.', '1416'] + # will also erroneously match the following + print(real.parseString('3. 1416')) # -> ['3', '.', '1416'] + + real = Combine(Word(nums) + '.' + Word(nums)) + print(real.parseString('3.1416')) # -> ['3.1416'] + # no match when there are internal spaces + print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...) + """ + def __init__( self, expr, joinString="", adjacent=True ): + super(Combine,self).__init__( expr ) + # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself + if adjacent: + self.leaveWhitespace() + self.adjacent = adjacent + self.skipWhitespace = True + self.joinString = joinString + self.callPreparse = True + + def ignore( self, other ): + if self.adjacent: + ParserElement.ignore(self, other) + else: + super( Combine, self).ignore( other ) + return self + + def postParse( self, instring, loc, tokenlist ): + retToks = tokenlist.copy() + del retToks[:] + retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults) + + if self.resultsName and retToks.haskeys(): + return [ retToks ] + else: + return retToks + +class Group(TokenConverter): + """ + Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions. + + Example:: + ident = Word(alphas) + num = Word(nums) + term = ident | num + func = ident + Optional(delimitedList(term)) + print(func.parseString("fn a,b,100")) # -> ['fn', 'a', 'b', '100'] + + func = ident + Group(Optional(delimitedList(term))) + print(func.parseString("fn a,b,100")) # -> ['fn', ['a', 'b', '100']] + """ + def __init__( self, expr ): + super(Group,self).__init__( expr ) + self.saveAsList = True + + def postParse( self, instring, loc, tokenlist ): + return [ tokenlist ] + +class Dict(TokenConverter): + """ + Converter to return a repetitive expression as a list, but also as a dictionary. + Each element can also be referenced using the first token in the expression as its key. + Useful for tabular report scraping when the first column can be used as a item key. + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + # print attributes as plain groups + print(OneOrMore(attr_expr).parseString(text).dump()) + + # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names + result = Dict(OneOrMore(Group(attr_expr))).parseString(text) + print(result.dump()) + + # access named fields as dict entries, or output as dict + print(result['shape']) + print(result.asDict()) + prints:: + ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap'] + + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'} + See more examples at L{ParseResults} of accessing fields by results name. + """ + def __init__( self, expr ): + super(Dict,self).__init__( expr ) + self.saveAsList = True + + def postParse( self, instring, loc, tokenlist ): + for i,tok in enumerate(tokenlist): + if len(tok) == 0: + continue + ikey = tok[0] + if isinstance(ikey,int): + ikey = _ustr(tok[0]).strip() + if len(tok)==1: + tokenlist[ikey] = _ParseResultsWithOffset("",i) + elif len(tok)==2 and not isinstance(tok[1],ParseResults): + tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i) + else: + dictvalue = tok.copy() #ParseResults(i) + del dictvalue[0] + if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()): + tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i) + else: + tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i) + + if self.resultsName: + return [ tokenlist ] + else: + return tokenlist + + +class Suppress(TokenConverter): + """ + Converter for ignoring the results of a parsed expression. + + Example:: + source = "a, b, c,d" + wd = Word(alphas) + wd_list1 = wd + ZeroOrMore(',' + wd) + print(wd_list1.parseString(source)) + + # often, delimiters that are useful during parsing are just in the + # way afterward - use Suppress to keep them out of the parsed output + wd_list2 = wd + ZeroOrMore(Suppress(',') + wd) + print(wd_list2.parseString(source)) + prints:: + ['a', ',', 'b', ',', 'c', ',', 'd'] + ['a', 'b', 'c', 'd'] + (See also L{delimitedList}.) + """ + def postParse( self, instring, loc, tokenlist ): + return [] + + def suppress( self ): + return self + + +class OnlyOnce(object): + """ + Wrapper for parse actions, to ensure they are only called once. + """ + def __init__(self, methodCall): + self.callable = _trim_arity(methodCall) + self.called = False + def __call__(self,s,l,t): + if not self.called: + results = self.callable(s,l,t) + self.called = True + return results + raise ParseException(s,l,"") + def reset(self): + self.called = False + +def traceParseAction(f): + """ + Decorator for debugging parse actions. + + When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".} + When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised. + + Example:: + wd = Word(alphas) + + @traceParseAction + def remove_duplicate_chars(tokens): + return ''.join(sorted(set(''.join(tokens)))) + + wds = OneOrMore(wd).setParseAction(remove_duplicate_chars) + print(wds.parseString("slkdjs sld sldd sdlf sdljf")) + prints:: + >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {})) + <<leaving remove_duplicate_chars (ret: 'dfjkls') + ['dfjkls'] + """ + f = _trim_arity(f) + def z(*paArgs): + thisFunc = f.__name__ + s,l,t = paArgs[-3:] + if len(paArgs)>3: + thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc + sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) ) + try: + ret = f(*paArgs) + except Exception as exc: + sys.stderr.write( "<<leaving %s (exception: %s)\n" % (thisFunc,exc) ) + raise + sys.stderr.write( "<<leaving %s (ret: %r)\n" % (thisFunc,ret) ) + return ret + try: + z.__name__ = f.__name__ + except AttributeError: + pass + return z + +# +# global helpers +# +def delimitedList( expr, delim=",", combine=False ): + """ + Helper to define a delimited list of expressions - the delimiter defaults to ','. + By default, the list elements and delimiters can have intervening whitespace, and + comments, but this can be overridden by passing C{combine=True} in the constructor. + If C{combine} is set to C{True}, the matching tokens are returned as a single token + string, with the delimiters included; otherwise, the matching tokens are returned + as a list of tokens, with the delimiters suppressed. + + Example:: + delimitedList(Word(alphas)).parseString("aa,bb,cc") # -> ['aa', 'bb', 'cc'] + delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] + """ + dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..." + if combine: + return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName) + else: + return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName) + +def countedArray( expr, intExpr=None ): + """ + Helper to define a counted list of expressions. + This helper defines a pattern of the form:: + integer expr expr expr... + where the leading integer tells how many expr expressions follow. + The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed. + + If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value. + + Example:: + countedArray(Word(alphas)).parseString('2 ab cd ef') # -> ['ab', 'cd'] + + # in this parser, the leading integer value is given in binary, + # '10' indicating that 2 values are in the array + binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2)) + countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef') # -> ['ab', 'cd'] + """ + arrayExpr = Forward() + def countFieldParseAction(s,l,t): + n = t[0] + arrayExpr << (n and Group(And([expr]*n)) or Group(empty)) + return [] + if intExpr is None: + intExpr = Word(nums).setParseAction(lambda t:int(t[0])) + else: + intExpr = intExpr.copy() + intExpr.setName("arrayLen") + intExpr.addParseAction(countFieldParseAction, callDuringTry=True) + return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...') + +def _flatten(L): + ret = [] + for i in L: + if isinstance(i,list): + ret.extend(_flatten(i)) + else: + ret.append(i) + return ret + +def matchPreviousLiteral(expr): + """ + Helper to define an expression that is indirectly defined from + the tokens matched in a previous expression, that is, it looks + for a 'repeat' of a previous expression. For example:: + first = Word(nums) + second = matchPreviousLiteral(first) + matchExpr = first + ":" + second + will match C{"1:1"}, but not C{"1:2"}. Because this matches a + previous literal, will also match the leading C{"1:1"} in C{"1:10"}. + If this is not desired, use C{matchPreviousExpr}. + Do I{not} use with packrat parsing enabled. + """ + rep = Forward() + def copyTokenToRepeater(s,l,t): + if t: + if len(t) == 1: + rep << t[0] + else: + # flatten t tokens + tflat = _flatten(t.asList()) + rep << And(Literal(tt) for tt in tflat) + else: + rep << Empty() + expr.addParseAction(copyTokenToRepeater, callDuringTry=True) + rep.setName('(prev) ' + _ustr(expr)) + return rep + +def matchPreviousExpr(expr): + """ + Helper to define an expression that is indirectly defined from + the tokens matched in a previous expression, that is, it looks + for a 'repeat' of a previous expression. For example:: + first = Word(nums) + second = matchPreviousExpr(first) + matchExpr = first + ":" + second + will match C{"1:1"}, but not C{"1:2"}. Because this matches by + expressions, will I{not} match the leading C{"1:1"} in C{"1:10"}; + the expressions are evaluated first, and then compared, so + C{"1"} is compared with C{"10"}. + Do I{not} use with packrat parsing enabled. + """ + rep = Forward() + e2 = expr.copy() + rep <<= e2 + def copyTokenToRepeater(s,l,t): + matchTokens = _flatten(t.asList()) + def mustMatchTheseTokens(s,l,t): + theseTokens = _flatten(t.asList()) + if theseTokens != matchTokens: + raise ParseException("",0,"") + rep.setParseAction( mustMatchTheseTokens, callDuringTry=True ) + expr.addParseAction(copyTokenToRepeater, callDuringTry=True) + rep.setName('(prev) ' + _ustr(expr)) + return rep + +def _escapeRegexRangeChars(s): + #~ escape these chars: ^-] + for c in r"\^-]": + s = s.replace(c,_bslash+c) + s = s.replace("\n",r"\n") + s = s.replace("\t",r"\t") + return _ustr(s) + +def oneOf( strs, caseless=False, useRegex=True ): + """ + Helper to quickly define a set of alternative Literals, and makes sure to do + longest-first testing when there is a conflict, regardless of the input order, + but returns a C{L{MatchFirst}} for best performance. + + Parameters: + - strs - a string of space-delimited literals, or a collection of string literals + - caseless - (default=C{False}) - treat all literals as caseless + - useRegex - (default=C{True}) - as an optimization, will generate a Regex + object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or + if creating a C{Regex} raises an exception) + + Example:: + comp_oper = oneOf("< = > <= >= !=") + var = Word(alphas) + number = Word(nums) + term = var | number + comparison_expr = term + comp_oper + term + print(comparison_expr.searchString("B = 12 AA=23 B<=AA AA>12")) + prints:: + [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] + """ + if caseless: + isequal = ( lambda a,b: a.upper() == b.upper() ) + masks = ( lambda a,b: b.upper().startswith(a.upper()) ) + parseElementClass = CaselessLiteral + else: + isequal = ( lambda a,b: a == b ) + masks = ( lambda a,b: b.startswith(a) ) + parseElementClass = Literal + + symbols = [] + if isinstance(strs,basestring): + symbols = strs.split() + elif isinstance(strs, Iterable): + symbols = list(strs) + else: + warnings.warn("Invalid argument to oneOf, expected string or iterable", + SyntaxWarning, stacklevel=2) + if not symbols: + return NoMatch() + + i = 0 + while i < len(symbols)-1: + cur = symbols[i] + for j,other in enumerate(symbols[i+1:]): + if ( isequal(other, cur) ): + del symbols[i+j+1] + break + elif ( masks(cur, other) ): + del symbols[i+j+1] + symbols.insert(i,other) + cur = other + break + else: + i += 1 + + if not caseless and useRegex: + #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] )) + try: + if len(symbols)==len("".join(symbols)): + return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols)) + else: + return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols)) + except Exception: + warnings.warn("Exception creating Regex for oneOf, building MatchFirst", + SyntaxWarning, stacklevel=2) + + + # last resort, just use MatchFirst + return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols)) + +def dictOf( key, value ): + """ + Helper to easily and clearly define a dictionary by specifying the respective patterns + for the key and value. Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens + in the proper order. The key pattern can include delimiting markers or punctuation, + as long as they are suppressed, thereby leaving the significant key text. The value + pattern can include named results, so that the C{Dict} results can include named token + fields. + + Example:: + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + print(OneOrMore(attr_expr).parseString(text).dump()) + + attr_label = label + attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join) + + # similar to Dict, but simpler call format + result = dictOf(attr_label, attr_value).parseString(text) + print(result.dump()) + print(result['shape']) + print(result.shape) # object attribute access works too + print(result.asDict()) + prints:: + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + SQUARE + {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} + """ + return Dict( ZeroOrMore( Group ( key + value ) ) ) + +def originalTextFor(expr, asString=True): + """ + Helper to return the original, untokenized text for a given expression. Useful to + restore the parsed fields of an HTML start tag into the raw tag text itself, or to + revert separate tokens with intervening whitespace back to the original matching + input text. By default, returns astring containing the original parsed text. + + If the optional C{asString} argument is passed as C{False}, then the return value is a + C{L{ParseResults}} containing any results names that were originally matched, and a + single token containing the original matched text from the input string. So if + the expression passed to C{L{originalTextFor}} contains expressions with defined + results names, you must set C{asString} to C{False} if you want to preserve those + results name values. + + Example:: + src = "this is test <b> bold <i>text</i> </b> normal text " + for tag in ("b","i"): + opener,closer = makeHTMLTags(tag) + patt = originalTextFor(opener + SkipTo(closer) + closer) + print(patt.searchString(src)[0]) + prints:: + ['<b> bold <i>text</i> </b>'] + ['<i>text</i>'] + """ + locMarker = Empty().setParseAction(lambda s,loc,t: loc) + endlocMarker = locMarker.copy() + endlocMarker.callPreparse = False + matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") + if asString: + extractText = lambda s,l,t: s[t._original_start:t._original_end] + else: + def extractText(s,l,t): + t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]] + matchExpr.setParseAction(extractText) + matchExpr.ignoreExprs = expr.ignoreExprs + return matchExpr + +def ungroup(expr): + """ + Helper to undo pyparsing's default grouping of And expressions, even + if all but one are non-empty. + """ + return TokenConverter(expr).setParseAction(lambda t:t[0]) + +def locatedExpr(expr): + """ + Helper to decorate a returned token with its starting and ending locations in the input string. + This helper adds the following results names: + - locn_start = location where matched expression begins + - locn_end = location where matched expression ends + - value = the actual parsed results + + Be careful if the input text contains C{<TAB>} characters, you may want to call + C{L{ParserElement.parseWithTabs}} + + Example:: + wd = Word(alphas) + for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): + print(match) + prints:: + [[0, 'ljsdf', 5]] + [[8, 'lksdjjf', 15]] + [[18, 'lkkjj', 23]] + """ + locator = Empty().setParseAction(lambda s,l,t: l) + return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end")) + + +# convenience constants for positional expressions +empty = Empty().setName("empty") +lineStart = LineStart().setName("lineStart") +lineEnd = LineEnd().setName("lineEnd") +stringStart = StringStart().setName("stringStart") +stringEnd = StringEnd().setName("stringEnd") + +_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1]) +_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16))) +_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8))) +_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | CharsNotIn(r'\]', exact=1) +_charRange = Group(_singleChar + Suppress("-") + _singleChar) +_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]" + +def srange(s): + r""" + Helper to easily define string ranges for use in Word construction. Borrows + syntax from regexp '[]' string range definitions:: + srange("[0-9]") -> "0123456789" + srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" + srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" + The input string must be enclosed in []'s, and the returned string is the expanded + character set joined into a single string. + The values enclosed in the []'s may be: + - a single character + - an escaped character with a leading backslash (such as C{\-} or C{\]}) + - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) + (C{\0x##} is also supported for backwards compatibility) + - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character) + - a range of any of the above, separated by a dash (C{'a-z'}, etc.) + - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.) + """ + _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1)) + try: + return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body) + except Exception: + return "" + +def matchOnlyAtCol(n): + """ + Helper method for defining parse actions that require matching at a specific + column in the input text. + """ + def verifyCol(strg,locn,toks): + if col(locn,strg) != n: + raise ParseException(strg,locn,"matched token not at column %d" % n) + return verifyCol + +def replaceWith(replStr): + """ + Helper method for common parse actions that simply return a literal value. Especially + useful when used with C{L{transformString<ParserElement.transformString>}()}. + + Example:: + num = Word(nums).setParseAction(lambda toks: int(toks[0])) + na = oneOf("N/A NA").setParseAction(replaceWith(math.nan)) + term = na | num + + OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234] + """ + return lambda s,l,t: [replStr] + +def removeQuotes(s,l,t): + """ + Helper parse action for removing quotation marks from parsed quoted strings. + + Example:: + # by default, quotation marks are included in parsed results + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"] + + # use removeQuotes to strip quotation marks from parsed results + quotedString.setParseAction(removeQuotes) + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"] + """ + return t[0][1:-1] + +def tokenMap(func, *args): + """ + Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional + args are passed, they are forwarded to the given function as additional arguments after + the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the + parsed data to an integer using base 16. + + Example (compare the last to example in L{ParserElement.transformString}:: + hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16)) + hex_ints.runTests(''' + 00 11 22 aa FF 0a 0d 1a + ''') + + upperword = Word(alphas).setParseAction(tokenMap(str.upper)) + OneOrMore(upperword).runTests(''' + my kingdom for a horse + ''') + + wd = Word(alphas).setParseAction(tokenMap(str.title)) + OneOrMore(wd).setParseAction(' '.join).runTests(''' + now is the winter of our discontent made glorious summer by this sun of york + ''') + prints:: + 00 11 22 aa FF 0a 0d 1a + [0, 17, 34, 170, 255, 10, 13, 26] + + my kingdom for a horse + ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE'] + + now is the winter of our discontent made glorious summer by this sun of york + ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York'] + """ + def pa(s,l,t): + return [func(tokn, *args) for tokn in t] + + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + pa.__name__ = func_name + + return pa + +upcaseTokens = tokenMap(lambda t: _ustr(t).upper()) +"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}""" + +downcaseTokens = tokenMap(lambda t: _ustr(t).lower()) +"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}""" + +def _makeTags(tagStr, xml): + """Internal helper to construct opening and closing tag expressions, given a tag name""" + if isinstance(tagStr,basestring): + resname = tagStr + tagStr = Keyword(tagStr, caseless=not xml) + else: + resname = tagStr.name + + tagAttrName = Word(alphas,alphanums+"_-:") + if (xml): + tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes ) + openTag = Suppress("<") + tagStr("tag") + \ + Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \ + Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") + else: + printablesLessRAbrack = "".join(c for c in printables if c not in ">") + tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack) + openTag = Suppress("<") + tagStr("tag") + \ + Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \ + Optional( Suppress("=") + tagAttrValue ) ))) + \ + Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") + closeTag = Combine(_L("</") + tagStr + ">") + + openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname) + closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("</%s>" % resname) + openTag.tag = resname + closeTag.tag = resname + return openTag, closeTag + +def makeHTMLTags(tagStr): + """ + Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches + tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values. + + Example:: + text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>' + # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple + a,a_end = makeHTMLTags("A") + link_expr = a + SkipTo(a_end)("link_text") + a_end + + for link in link_expr.searchString(text): + # attributes in the <A> tag (like "href" shown here) are also accessible as named results + print(link.link_text, '->', link.href) + prints:: + pyparsing -> http://pyparsing.wikispaces.com + """ + return _makeTags( tagStr, False ) + +def makeXMLTags(tagStr): + """ + Helper to construct opening and closing tag expressions for XML, given a tag name. Matches + tags only in the given upper/lower case. + + Example: similar to L{makeHTMLTags} + """ + return _makeTags( tagStr, True ) + +def withAttribute(*args,**attrDict): + """ + Helper to create a validating parse action to be used with start tags created + with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag + with a required attribute value, to avoid false matches on common tags such as + C{<TD>} or C{<DIV>}. + + Call C{withAttribute} with a series of attribute names and values. Specify the list + of filter attributes names and values as: + - keyword arguments, as in C{(align="right")}, or + - as an explicit dict with C{**} operator, when an attribute name is also a Python + reserved word, as in C{**{"class":"Customer", "align":"right"}} + - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) + For attribute names with a namespace prefix, you must use the second form. Attribute + names are matched insensitive to upper/lower case. + + If just testing for C{class} (with or without a namespace), use C{L{withClass}}. + + To verify that the attribute exists, but without specifying a value, pass + C{withAttribute.ANY_VALUE} as the value. + + Example:: + html = ''' + <div> + Some text + <div type="grid">1 4 0 1 0</div> + <div type="graph">1,3 2,3 1,1</div> + <div>this has no type</div> + </div> + + ''' + div,div_end = makeHTMLTags("div") + + # only match div tag having a type attribute with value "grid" + div_grid = div().setParseAction(withAttribute(type="grid")) + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + # construct a match with any div tag having a type attribute, regardless of the value + div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + if args: + attrs = args[:] + else: + attrs = attrDict.items() + attrs = [(k,v) for k,v in attrs] + def pa(s,l,tokens): + for attrName,attrValue in attrs: + if attrName not in tokens: + raise ParseException(s,l,"no matching attribute " + attrName) + if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: + raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % + (attrName, tokens[attrName], attrValue)) + return pa +withAttribute.ANY_VALUE = object() + +def withClass(classname, namespace=''): + """ + Simplified version of C{L{withAttribute}} when matching on a div class - made + difficult because C{class} is a reserved word in Python. + + Example:: + html = ''' + <div> + Some text + <div class="grid">1 4 0 1 0</div> + <div class="graph">1,3 2,3 1,1</div> + <div>this <div> has no class</div> + </div> + + ''' + div,div_end = makeHTMLTags("div") + div_grid = div().setParseAction(withClass("grid")) + + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + classattr = "%s:class" % namespace if namespace else "class" + return withAttribute(**{classattr : classname}) + +opAssoc = _Constants() +opAssoc.LEFT = object() +opAssoc.RIGHT = object() + +def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): + """ + Helper method for constructing grammars of expressions made up of + operators working in a precedence hierarchy. Operators may be unary or + binary, left- or right-associative. Parse actions can also be attached + to operator expressions. The generated parser will also recognize the use + of parentheses to override operator precedences (see example below). + + Note: if you define a deep operator list, you may see performance issues + when using infixNotation. See L{ParserElement.enablePackrat} for a + mechanism to potentially improve your parser performance. + + Parameters: + - baseExpr - expression representing the most basic element for the nested + - opList - list of tuples, one for each operator precedence level in the + expression grammar; each tuple is of the form + (opExpr, numTerms, rightLeftAssoc, parseAction), where: + - opExpr is the pyparsing expression for the operator; + may also be a string, which will be converted to a Literal; + if numTerms is 3, opExpr is a tuple of two expressions, for the + two operators separating the 3 terms + - numTerms is the number of terms for this operator (must + be 1, 2, or 3) + - rightLeftAssoc is the indicator whether the operator is + right or left associative, using the pyparsing-defined + constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. + - parseAction is the parse action to be associated with + expressions matching this operator expression (the + parse action tuple member may be omitted); if the parse action + is passed a tuple or list of functions, this is equivalent to + calling C{setParseAction(*fn)} (L{ParserElement.setParseAction}) + - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) + - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) + + Example:: + # simple example of four-function arithmetic with ints and variable names + integer = pyparsing_common.signed_integer + varname = pyparsing_common.identifier + + arith_expr = infixNotation(integer | varname, + [ + ('-', 1, opAssoc.RIGHT), + (oneOf('* /'), 2, opAssoc.LEFT), + (oneOf('+ -'), 2, opAssoc.LEFT), + ]) + + arith_expr.runTests(''' + 5+3*6 + (5+3)*6 + -2--11 + ''', fullDump=False) + prints:: + 5+3*6 + [[5, '+', [3, '*', 6]]] + + (5+3)*6 + [[[5, '+', 3], '*', 6]] + + -2--11 + [[['-', 2], '-', ['-', 11]]] + """ + ret = Forward() + lastExpr = baseExpr | ( lpar + ret + rpar ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr + if arity == 3: + if opExpr is None or len(opExpr) != 2: + raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") + opExpr1, opExpr2 = opExpr + thisExpr = Forward().setName(termName) + if rightLeftAssoc == opAssoc.LEFT: + if arity == 1: + matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) + else: + matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ + Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + elif rightLeftAssoc == opAssoc.RIGHT: + if arity == 1: + # try to avoid LR with this extra test + if not isinstance(opExpr, Optional): + opExpr = Optional(opExpr) + matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) + else: + matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ + Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + else: + raise ValueError("operator must indicate right or left associativity") + if pa: + if isinstance(pa, (tuple, list)): + matchExpr.setParseAction(*pa) + else: + matchExpr.setParseAction(pa) + thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) + lastExpr = thisExpr + ret <<= lastExpr + return ret + +operatorPrecedence = infixNotation +"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" + +dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") +sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") +quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| + Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") + +def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): + """ + Helper method for defining nested lists enclosed in opening and closing + delimiters ("(" and ")" are the default). + + Parameters: + - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression + - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression + - content - expression for items within the nested lists (default=C{None}) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString}) + + If an expression is not provided for the content argument, the nested + expression will capture all whitespace-delimited content between delimiters + as a list of separate values. + + Use the C{ignoreExpr} argument to define expressions that may contain + opening or closing characters that should not be treated as opening + or closing characters for nesting, such as quotedString or a comment + expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. + The default is L{quotedString}, but if no expressions are to be ignored, + then pass C{None} for this argument. + + Example:: + data_type = oneOf("void int short long char float double") + decl_data_type = Combine(data_type + Optional(Word('*'))) + ident = Word(alphas+'_', alphanums+'_') + number = pyparsing_common.number + arg = Group(decl_data_type + ident) + LPAR,RPAR = map(Suppress, "()") + + code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) + + c_function = (decl_data_type("type") + + ident("name") + + LPAR + Optional(delimitedList(arg), [])("args") + RPAR + + code_body("body")) + c_function.ignore(cStyleComment) + + source_code = ''' + int is_odd(int x) { + return (x%2); + } + + int dec_to_hex(char hchar) { + if (hchar >= '0' && hchar <= '9') { + return (ord(hchar)-ord('0')); + } else { + return (10+ord(hchar)-ord('A')); + } + } + ''' + for func in c_function.searchString(source_code): + print("%(name)s (%(type)s) args: %(args)s" % func) + + prints:: + is_odd (int) args: [['int', 'x']] + dec_to_hex (int) args: [['char', 'hchar']] + """ + if opener == closer: + raise ValueError("opening and closing strings cannot be the same") + if content is None: + if isinstance(opener,basestring) and isinstance(closer,basestring): + if len(opener) == 1 and len(closer)==1: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS + ).setParseAction(lambda t:t[0].strip())) + else: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + ~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + raise ValueError("opening and closing arguments must be strings if no content expression is given") + ret = Forward() + if ignoreExpr is not None: + ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) + else: + ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) + ret.setName('nested %s%s expression' % (opener,closer)) + return ret + +def indentedBlock(blockStatementExpr, indentStack, indent=True): + """ + Helper method for defining space-delimited indentation blocks, such as + those used to define block statements in Python source code. + + Parameters: + - blockStatementExpr - expression defining syntax of statement that + is repeated within the indented block + - indentStack - list created by caller to manage indentation stack + (multiple statementWithIndentedBlock expressions within a single grammar + should share a common indentStack) + - indent - boolean indicating whether block must be indented beyond the + the current level; set to False for block of left-most statements + (default=C{True}) + + A valid block must contain at least one C{blockStatement}. + + Example:: + data = ''' + def A(z): + A1 + B = 100 + G = A2 + A2 + A3 + B + def BB(a,b,c): + BB1 + def BBA(): + bba1 + bba2 + bba3 + C + D + def spam(x,y): + def eggs(z): + pass + ''' + + + indentStack = [1] + stmt = Forward() + + identifier = Word(alphas, alphanums) + funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") + func_body = indentedBlock(stmt, indentStack) + funcDef = Group( funcDecl + func_body ) + + rvalue = Forward() + funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") + rvalue << (funcCall | identifier | Word(nums)) + assignment = Group(identifier + "=" + rvalue) + stmt << ( funcDef | assignment | identifier ) + + module_body = OneOrMore(stmt) + + parseTree = module_body.parseString(data) + parseTree.pprint() + prints:: + [['def', + 'A', + ['(', 'z', ')'], + ':', + [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], + 'B', + ['def', + 'BB', + ['(', 'a', 'b', 'c', ')'], + ':', + [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], + 'C', + 'D', + ['def', + 'spam', + ['(', 'x', 'y', ')'], + ':', + [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] + """ + def checkPeerIndent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if curCol != indentStack[-1]: + if curCol > indentStack[-1]: + raise ParseFatalException(s,l,"illegal nesting") + raise ParseException(s,l,"not a peer entry") + + def checkSubIndent(s,l,t): + curCol = col(l,s) + if curCol > indentStack[-1]: + indentStack.append( curCol ) + else: + raise ParseException(s,l,"not a subentry") + + def checkUnindent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): + raise ParseException(s,l,"not an unindent") + indentStack.pop() + + NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) + INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') + PEER = Empty().setParseAction(checkPeerIndent).setName('') + UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') + if indent: + smExpr = Group( Optional(NL) + + #~ FollowedBy(blockStatementExpr) + + INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) + else: + smExpr = Group( Optional(NL) + + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) + blockStatementExpr.ignore(_bslash + LineEnd()) + return smExpr.setName('indented block') + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) +_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) +commonHTMLEntity = Regex('&(?P<entity>' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") +def replaceHTMLEntity(t): + """Helper parser action to replace common HTML entities with their special characters""" + return _htmlEntityMap.get(t.entity) + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") +"Comment of the form C{/* ... */}" + +htmlComment = Regex(r"<!--[\s\S]*?-->").setName("HTML comment") +"Comment of the form C{<!-- ... -->}" + +restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") +dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") +"Comment of the form C{// ... (to end of line)}" + +cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") +"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" + +javaStyleComment = cppStyleComment +"Same as C{L{cppStyleComment}}" + +pythonStyleComment = Regex(r"#.*").setName("Python style comment") +"Comment of the form C{# ... (to end of line)}" + +_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + + Optional( Word(" \t") + + ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") +commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") +"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. + This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" + +# some other useful expressions - using lower-case class name since we are really using this as a namespace +class pyparsing_common: + """ + Here are some common low-level expressions that may be useful in jump-starting parser development: + - numeric forms (L{integers<integer>}, L{reals<real>}, L{scientific notation<sci_real>}) + - common L{programming identifiers<identifier>} + - network addresses (L{MAC<mac_address>}, L{IPv4<ipv4_address>}, L{IPv6<ipv6_address>}) + - ISO8601 L{dates<iso8601_date>} and L{datetime<iso8601_datetime>} + - L{UUID<uuid>} + - L{comma-separated list<comma_separated_list>} + Parse actions: + - C{L{convertToInteger}} + - C{L{convertToFloat}} + - C{L{convertToDate}} + - C{L{convertToDatetime}} + - C{L{stripHTMLTags}} + - C{L{upcaseTokens}} + - C{L{downcaseTokens}} + + Example:: + pyparsing_common.number.runTests(''' + # any int or real number, returned as the appropriate type + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.fnumber.runTests(''' + # any int or real number, returned as float + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.hex_integer.runTests(''' + # hex numbers + 100 + FF + ''') + + pyparsing_common.fraction.runTests(''' + # fractions + 1/2 + -3/4 + ''') + + pyparsing_common.mixed_integer.runTests(''' + # mixed fractions + 1 + 1/2 + -3/4 + 1-3/4 + ''') + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(''' + # uuid + 12345678-1234-5678-1234-567812345678 + ''') + prints:: + # any int or real number, returned as the appropriate type + 100 + [100] + + -100 + [-100] + + +100 + [100] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # any int or real number, returned as float + 100 + [100.0] + + -100 + [-100.0] + + +100 + [100.0] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # hex numbers + 100 + [256] + + FF + [255] + + # fractions + 1/2 + [0.5] + + -3/4 + [-0.75] + + # mixed fractions + 1 + [1] + + 1/2 + [0.5] + + -3/4 + [-0.75] + + 1-3/4 + [1.75] + + # uuid + 12345678-1234-5678-1234-567812345678 + [UUID('12345678-1234-5678-1234-567812345678')] + """ + + convertToInteger = tokenMap(int) + """ + Parse action for converting parsed integers to Python int + """ + + convertToFloat = tokenMap(float) + """ + Parse action for converting parsed numbers to Python float + """ + + integer = Word(nums).setName("integer").setParseAction(convertToInteger) + """expression that parses an unsigned integer, returns an int""" + + hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) + """expression that parses a hexadecimal integer, returns an int""" + + signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) + """expression that parses an integer with optional leading sign, returns an int""" + + fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") + """fractional expression of an integer divided by an integer, returns a float""" + fraction.addParseAction(lambda t: t[0]/t[-1]) + + mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") + """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" + mixed_integer.addParseAction(sum) + + real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) + """expression that parses a floating point number and returns a float""" + + sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) + """expression that parses a floating point number with optional scientific notation and returns a float""" + + # streamlining this expression makes the docs nicer-looking + number = (sci_real | real | signed_integer).streamline() + """any numeric expression, returns the corresponding Python type""" + + fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) + """any int or real number, returned as float""" + + identifier = Word(alphas+'_', alphanums+'_').setName("identifier") + """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" + + ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") + "IPv4 address (C{0.0.0.0 - 255.255.255.255})" + + _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") + _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") + _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") + _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) + _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") + ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") + "IPv6 address (long, short, or mixed form)" + + mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") + "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" + + @staticmethod + def convertToDate(fmt="%Y-%m-%d"): + """ + Helper to create a parse action for converting parsed date string to Python datetime.date + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) + + Example:: + date_expr = pyparsing_common.iso8601_date.copy() + date_expr.setParseAction(pyparsing_common.convertToDate()) + print(date_expr.parseString("1999-12-31")) + prints:: + [datetime.date(1999, 12, 31)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt).date() + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + @staticmethod + def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): + """ + Helper to create a parse action for converting parsed datetime string to Python datetime.datetime + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) + + Example:: + dt_expr = pyparsing_common.iso8601_datetime.copy() + dt_expr.setParseAction(pyparsing_common.convertToDatetime()) + print(dt_expr.parseString("1999-12-31T23:59:59.999")) + prints:: + [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt) + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + iso8601_date = Regex(r'(?P<year>\d{4})(?:-(?P<month>\d\d)(?:-(?P<day>\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (C{yyyy-mm-dd})" + + iso8601_datetime = Regex(r'(?P<year>\d{4})-(?P<month>\d\d)-(?P<day>\d\d)[T ](?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d(\.\d*)?)?)?(?P<tz>Z|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") + "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" + + uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") + "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" + + _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() + @staticmethod + def stripHTMLTags(s, l, tokens): + """ + Parse action to remove HTML tags from web page HTML source + + Example:: + # strip HTML links from normal text + text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>' + td,td_end = makeHTMLTags("TD") + table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end + + print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' + """ + return pyparsing_common._html_stripper.transformString(tokens[0]) + + _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') + + Optional( White(" \t") ) ) ).streamline().setName("commaItem") + comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") + """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" + + upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) + """Parse action to convert tokens to upper case.""" + + downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) + """Parse action to convert tokens to lower case.""" + + +if __name__ == "__main__": + + selectToken = CaselessLiteral("select") + fromToken = CaselessLiteral("from") + + ident = Word(alphas, alphanums + "_$") + + columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + columnNameList = Group(delimitedList(columnName)).setName("columns") + columnSpec = ('*' | columnNameList) + + tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + tableNameList = Group(delimitedList(tableName)).setName("tables") + + simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") + + # demo runTests method, including embedded comments in test string + simpleSQL.runTests(""" + # '*' as column list and dotted table name + select * from SYS.XYZZY + + # caseless match on "SELECT", and casts back to "select" + SELECT * from XYZZY, ABC + + # list of column names, and mixed case SELECT keyword + Select AA,BB,CC from Sys.dual + + # multiple tables + Select A, B, C from Sys.dual, Table2 + + # invalid SELECT keyword - should fail + Xelect A, B, C from Sys.dual + + # incomplete command - should fail + Select + + # invalid column name - should fail + Select ^^^ frox Sys.dual + + """) + + pyparsing_common.number.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + # any int or real number, returned as float + pyparsing_common.fnumber.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + pyparsing_common.hex_integer.runTests(""" + 100 + FF + """) + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(""" + 12345678-1234-5678-1234-567812345678 + """) diff --git a/venv/Lib/site-packages/pip/_vendor/pytoml/__init__.py b/venv/Lib/site-packages/pip/_vendor/pytoml/__init__.py new file mode 100644 index 0000000..8dc7315 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pytoml/__init__.py @@ -0,0 +1,3 @@ +from .core import TomlError +from .parser import load, loads +from .writer import dump, dumps diff --git a/venv/Lib/site-packages/pip/_vendor/pytoml/core.py b/venv/Lib/site-packages/pip/_vendor/pytoml/core.py new file mode 100644 index 0000000..c182734 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pytoml/core.py @@ -0,0 +1,13 @@ +class TomlError(RuntimeError): + def __init__(self, message, line, col, filename): + RuntimeError.__init__(self, message, line, col, filename) + self.message = message + self.line = line + self.col = col + self.filename = filename + + def __str__(self): + return '{}({}, {}): {}'.format(self.filename, self.line, self.col, self.message) + + def __repr__(self): + return 'TomlError({!r}, {!r}, {!r}, {!r})'.format(self.message, self.line, self.col, self.filename) diff --git a/venv/Lib/site-packages/pip/_vendor/pytoml/parser.py b/venv/Lib/site-packages/pip/_vendor/pytoml/parser.py new file mode 100644 index 0000000..9f94e92 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pytoml/parser.py @@ -0,0 +1,374 @@ +import string, re, sys, datetime +from .core import TomlError + +if sys.version_info[0] == 2: + _chr = unichr +else: + _chr = chr + +def load(fin, translate=lambda t, x, v: v, object_pairs_hook=dict): + return loads(fin.read(), translate=translate, object_pairs_hook=object_pairs_hook, filename=getattr(fin, 'name', repr(fin))) + +def loads(s, filename='<string>', translate=lambda t, x, v: v, object_pairs_hook=dict): + if isinstance(s, bytes): + s = s.decode('utf-8') + + s = s.replace('\r\n', '\n') + + root = object_pairs_hook() + tables = object_pairs_hook() + scope = root + + src = _Source(s, filename=filename) + ast = _p_toml(src, object_pairs_hook=object_pairs_hook) + + def error(msg): + raise TomlError(msg, pos[0], pos[1], filename) + + def process_value(v, object_pairs_hook): + kind, text, value, pos = v + if kind == 'str' and value.startswith('\n'): + value = value[1:] + if kind == 'array': + if value and any(k != value[0][0] for k, t, v, p in value[1:]): + error('array-type-mismatch') + value = [process_value(item, object_pairs_hook=object_pairs_hook) for item in value] + elif kind == 'table': + value = object_pairs_hook([(k, process_value(value[k], object_pairs_hook=object_pairs_hook)) for k in value]) + return translate(kind, text, value) + + for kind, value, pos in ast: + if kind == 'kv': + k, v = value + if k in scope: + error('duplicate_keys. Key "{0}" was used more than once.'.format(k)) + scope[k] = process_value(v, object_pairs_hook=object_pairs_hook) + else: + is_table_array = (kind == 'table_array') + cur = tables + for name in value[:-1]: + if isinstance(cur.get(name), list): + d, cur = cur[name][-1] + else: + d, cur = cur.setdefault(name, (None, object_pairs_hook())) + + scope = object_pairs_hook() + name = value[-1] + if name not in cur: + if is_table_array: + cur[name] = [(scope, object_pairs_hook())] + else: + cur[name] = (scope, object_pairs_hook()) + elif isinstance(cur[name], list): + if not is_table_array: + error('table_type_mismatch') + cur[name].append((scope, object_pairs_hook())) + else: + if is_table_array: + error('table_type_mismatch') + old_scope, next_table = cur[name] + if old_scope is not None: + error('duplicate_tables') + cur[name] = (scope, next_table) + + def merge_tables(scope, tables): + if scope is None: + scope = object_pairs_hook() + for k in tables: + if k in scope: + error('key_table_conflict') + v = tables[k] + if isinstance(v, list): + scope[k] = [merge_tables(sc, tbl) for sc, tbl in v] + else: + scope[k] = merge_tables(v[0], v[1]) + return scope + + return merge_tables(root, tables) + +class _Source: + def __init__(self, s, filename=None): + self.s = s + self._pos = (1, 1) + self._last = None + self._filename = filename + self.backtrack_stack = [] + + def last(self): + return self._last + + def pos(self): + return self._pos + + def fail(self): + return self._expect(None) + + def consume_dot(self): + if self.s: + self._last = self.s[0] + self.s = self[1:] + self._advance(self._last) + return self._last + return None + + def expect_dot(self): + return self._expect(self.consume_dot()) + + def consume_eof(self): + if not self.s: + self._last = '' + return True + return False + + def expect_eof(self): + return self._expect(self.consume_eof()) + + def consume(self, s): + if self.s.startswith(s): + self.s = self.s[len(s):] + self._last = s + self._advance(s) + return True + return False + + def expect(self, s): + return self._expect(self.consume(s)) + + def consume_re(self, re): + m = re.match(self.s) + if m: + self.s = self.s[len(m.group(0)):] + self._last = m + self._advance(m.group(0)) + return m + return None + + def expect_re(self, re): + return self._expect(self.consume_re(re)) + + def __enter__(self): + self.backtrack_stack.append((self.s, self._pos)) + + def __exit__(self, type, value, traceback): + if type is None: + self.backtrack_stack.pop() + else: + self.s, self._pos = self.backtrack_stack.pop() + return type == TomlError + + def commit(self): + self.backtrack_stack[-1] = (self.s, self._pos) + + def _expect(self, r): + if not r: + raise TomlError('msg', self._pos[0], self._pos[1], self._filename) + return r + + def _advance(self, s): + suffix_pos = s.rfind('\n') + if suffix_pos == -1: + self._pos = (self._pos[0], self._pos[1] + len(s)) + else: + self._pos = (self._pos[0] + s.count('\n'), len(s) - suffix_pos) + +_ews_re = re.compile(r'(?:[ \t]|#[^\n]*\n|#[^\n]*\Z|\n)*') +def _p_ews(s): + s.expect_re(_ews_re) + +_ws_re = re.compile(r'[ \t]*') +def _p_ws(s): + s.expect_re(_ws_re) + +_escapes = { 'b': '\b', 'n': '\n', 'r': '\r', 't': '\t', '"': '"', '\'': '\'', + '\\': '\\', '/': '/', 'f': '\f' } + +_basicstr_re = re.compile(r'[^"\\\000-\037]*') +_short_uni_re = re.compile(r'u([0-9a-fA-F]{4})') +_long_uni_re = re.compile(r'U([0-9a-fA-F]{8})') +_escapes_re = re.compile('[bnrt"\'\\\\/f]') +_newline_esc_re = re.compile('\n[ \t\n]*') +def _p_basicstr_content(s, content=_basicstr_re): + res = [] + while True: + res.append(s.expect_re(content).group(0)) + if not s.consume('\\'): + break + if s.consume_re(_newline_esc_re): + pass + elif s.consume_re(_short_uni_re) or s.consume_re(_long_uni_re): + res.append(_chr(int(s.last().group(1), 16))) + else: + s.expect_re(_escapes_re) + res.append(_escapes[s.last().group(0)]) + return ''.join(res) + +_key_re = re.compile(r'[0-9a-zA-Z-_]+') +def _p_key(s): + with s: + s.expect('"') + r = _p_basicstr_content(s, _basicstr_re) + s.expect('"') + return r + if s.consume('\''): + if s.consume('\'\''): + r = s.expect_re(_litstr_ml_re).group(0) + s.expect('\'\'\'') + else: + r = s.expect_re(_litstr_re).group(0) + s.expect('\'') + return r + return s.expect_re(_key_re).group(0) + +_float_re = re.compile(r'[+-]?(?:0|[1-9](?:_?\d)*)(?:\.\d(?:_?\d)*)?(?:[eE][+-]?(?:\d(?:_?\d)*))?') +_datetime_re = re.compile(r'(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:Z|([+-]\d{2}):(\d{2}))') + +_basicstr_ml_re = re.compile(r'(?:(?:|"|"")[^"\\\000-\011\013-\037])*') +_litstr_re = re.compile(r"[^'\000\010\012-\037]*") +_litstr_ml_re = re.compile(r"(?:(?:|'|'')(?:[^'\000-\010\013-\037]))*") +def _p_value(s, object_pairs_hook): + pos = s.pos() + + if s.consume('true'): + return 'bool', s.last(), True, pos + if s.consume('false'): + return 'bool', s.last(), False, pos + + if s.consume('"'): + if s.consume('""'): + r = _p_basicstr_content(s, _basicstr_ml_re) + s.expect('"""') + else: + r = _p_basicstr_content(s, _basicstr_re) + s.expect('"') + return 'str', r, r, pos + + if s.consume('\''): + if s.consume('\'\''): + r = s.expect_re(_litstr_ml_re).group(0) + s.expect('\'\'\'') + else: + r = s.expect_re(_litstr_re).group(0) + s.expect('\'') + return 'str', r, r, pos + + if s.consume_re(_datetime_re): + m = s.last() + s0 = m.group(0) + r = map(int, m.groups()[:6]) + if m.group(7): + micro = float(m.group(7)) + else: + micro = 0 + + if m.group(8): + g = int(m.group(8), 10) * 60 + int(m.group(9), 10) + tz = _TimeZone(datetime.timedelta(0, g * 60)) + else: + tz = _TimeZone(datetime.timedelta(0, 0)) + + y, m, d, H, M, S = r + dt = datetime.datetime(y, m, d, H, M, S, int(micro * 1000000), tz) + return 'datetime', s0, dt, pos + + if s.consume_re(_float_re): + m = s.last().group(0) + r = m.replace('_','') + if '.' in m or 'e' in m or 'E' in m: + return 'float', m, float(r), pos + else: + return 'int', m, int(r, 10), pos + + if s.consume('['): + items = [] + with s: + while True: + _p_ews(s) + items.append(_p_value(s, object_pairs_hook=object_pairs_hook)) + s.commit() + _p_ews(s) + s.expect(',') + s.commit() + _p_ews(s) + s.expect(']') + return 'array', None, items, pos + + if s.consume('{'): + _p_ws(s) + items = object_pairs_hook() + if not s.consume('}'): + k = _p_key(s) + _p_ws(s) + s.expect('=') + _p_ws(s) + items[k] = _p_value(s, object_pairs_hook=object_pairs_hook) + _p_ws(s) + while s.consume(','): + _p_ws(s) + k = _p_key(s) + _p_ws(s) + s.expect('=') + _p_ws(s) + items[k] = _p_value(s, object_pairs_hook=object_pairs_hook) + _p_ws(s) + s.expect('}') + return 'table', None, items, pos + + s.fail() + +def _p_stmt(s, object_pairs_hook): + pos = s.pos() + if s.consume( '['): + is_array = s.consume('[') + _p_ws(s) + keys = [_p_key(s)] + _p_ws(s) + while s.consume('.'): + _p_ws(s) + keys.append(_p_key(s)) + _p_ws(s) + s.expect(']') + if is_array: + s.expect(']') + return 'table_array' if is_array else 'table', keys, pos + + key = _p_key(s) + _p_ws(s) + s.expect('=') + _p_ws(s) + value = _p_value(s, object_pairs_hook=object_pairs_hook) + return 'kv', (key, value), pos + +_stmtsep_re = re.compile(r'(?:[ \t]*(?:#[^\n]*)?\n)+[ \t]*') +def _p_toml(s, object_pairs_hook): + stmts = [] + _p_ews(s) + with s: + stmts.append(_p_stmt(s, object_pairs_hook=object_pairs_hook)) + while True: + s.commit() + s.expect_re(_stmtsep_re) + stmts.append(_p_stmt(s, object_pairs_hook=object_pairs_hook)) + _p_ews(s) + s.expect_eof() + return stmts + +class _TimeZone(datetime.tzinfo): + def __init__(self, offset): + self._offset = offset + + def utcoffset(self, dt): + return self._offset + + def dst(self, dt): + return None + + def tzname(self, dt): + m = self._offset.total_seconds() // 60 + if m < 0: + res = '-' + m = -m + else: + res = '+' + h = m // 60 + m = m - h * 60 + return '{}{:.02}{:.02}'.format(res, h, m) diff --git a/venv/Lib/site-packages/pip/_vendor/pytoml/writer.py b/venv/Lib/site-packages/pip/_vendor/pytoml/writer.py new file mode 100644 index 0000000..6eaf5d7 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/pytoml/writer.py @@ -0,0 +1,127 @@ +from __future__ import unicode_literals +import io, datetime, math, sys + +if sys.version_info[0] == 3: + long = int + unicode = str + + +def dumps(obj, sort_keys=False): + fout = io.StringIO() + dump(obj, fout, sort_keys=sort_keys) + return fout.getvalue() + + +_escapes = {'\n': 'n', '\r': 'r', '\\': '\\', '\t': 't', '\b': 'b', '\f': 'f', '"': '"'} + + +def _escape_string(s): + res = [] + start = 0 + + def flush(): + if start != i: + res.append(s[start:i]) + return i + 1 + + i = 0 + while i < len(s): + c = s[i] + if c in '"\\\n\r\t\b\f': + start = flush() + res.append('\\' + _escapes[c]) + elif ord(c) < 0x20: + start = flush() + res.append('\\u%04x' % ord(c)) + i += 1 + + flush() + return '"' + ''.join(res) + '"' + + +def _escape_id(s): + if any(not c.isalnum() and c not in '-_' for c in s): + return _escape_string(s) + return s + + +def _format_list(v): + return '[{0}]'.format(', '.join(_format_value(obj) for obj in v)) + +# Formula from: +# https://docs.python.org/2/library/datetime.html#datetime.timedelta.total_seconds +# Once support for py26 is dropped, this can be replaced by td.total_seconds() +def _total_seconds(td): + return ((td.microseconds + + (td.seconds + td.days * 24 * 3600) * 10**6) / 10.0**6) + +def _format_value(v): + if isinstance(v, bool): + return 'true' if v else 'false' + if isinstance(v, int) or isinstance(v, long): + return unicode(v) + if isinstance(v, float): + if math.isnan(v) or math.isinf(v): + raise ValueError("{0} is not a valid TOML value".format(v)) + else: + return repr(v) + elif isinstance(v, unicode) or isinstance(v, bytes): + return _escape_string(v) + elif isinstance(v, datetime.datetime): + offs = v.utcoffset() + offs = _total_seconds(offs) // 60 if offs is not None else 0 + + if offs == 0: + suffix = 'Z' + else: + if offs > 0: + suffix = '+' + else: + suffix = '-' + offs = -offs + suffix = '{0}{1:.02}{2:.02}'.format(suffix, offs // 60, offs % 60) + + if v.microsecond: + return v.strftime('%Y-%m-%dT%H:%M:%S.%f') + suffix + else: + return v.strftime('%Y-%m-%dT%H:%M:%S') + suffix + elif isinstance(v, list): + return _format_list(v) + else: + raise RuntimeError(v) + + +def dump(obj, fout, sort_keys=False): + tables = [((), obj, False)] + + while tables: + name, table, is_array = tables.pop() + if name: + section_name = '.'.join(_escape_id(c) for c in name) + if is_array: + fout.write('[[{0}]]\n'.format(section_name)) + else: + fout.write('[{0}]\n'.format(section_name)) + + table_keys = sorted(table.keys()) if sort_keys else table.keys() + new_tables = [] + has_kv = False + for k in table_keys: + v = table[k] + if isinstance(v, dict): + new_tables.append((name + (k,), v, False)) + elif isinstance(v, list) and v and all(isinstance(o, dict) for o in v): + new_tables.extend((name + (k,), d, True) for d in v) + elif v is None: + # based on mojombo's comment: https://github.com/toml-lang/toml/issues/146#issuecomment-25019344 + fout.write( + '#{} = null # To use: uncomment and replace null with value\n'.format(_escape_id(k))) + has_kv = True + else: + fout.write('{0} = {1}\n'.format(_escape_id(k), _format_value(v))) + has_kv = True + + tables.extend(reversed(new_tables)) + + if (name or has_kv) and tables: + fout.write('\n') diff --git a/venv/Lib/site-packages/pip/_vendor/requests/__init__.py b/venv/Lib/site-packages/pip/_vendor/requests/__init__.py new file mode 100644 index 0000000..3f3f4f2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/__init__.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- + +# __ +# /__) _ _ _ _ _/ _ +# / ( (- (/ (/ (- _) / _) +# / + +""" +Requests HTTP Library +~~~~~~~~~~~~~~~~~~~~~ + +Requests is an HTTP library, written in Python, for human beings. Basic GET +usage: + + >>> import requests + >>> r = requests.get('https://www.python.org') + >>> r.status_code + 200 + >>> 'Python is a programming language' in r.content + True + +... or POST: + + >>> payload = dict(key1='value1', key2='value2') + >>> r = requests.post('http://httpbin.org/post', data=payload) + >>> print(r.text) + { + ... + "form": { + "key2": "value2", + "key1": "value1" + }, + ... + } + +The other HTTP methods are supported - see `requests.api`. Full documentation +is at <http://python-requests.org>. + +:copyright: (c) 2017 by Kenneth Reitz. +:license: Apache 2.0, see LICENSE for more details. +""" + +from pip._vendor import urllib3 +from pip._vendor import chardet +import warnings +from .exceptions import RequestsDependencyWarning + + +def check_compatibility(urllib3_version, chardet_version): + urllib3_version = urllib3_version.split('.') + assert urllib3_version != ['dev'] # Verify urllib3 isn't installed from git. + + # Sometimes, urllib3 only reports its version as 16.1. + if len(urllib3_version) == 2: + urllib3_version.append('0') + + # Check urllib3 for compatibility. + major, minor, patch = urllib3_version # noqa: F811 + major, minor, patch = int(major), int(minor), int(patch) + # urllib3 >= 1.21.1, <= 1.23 + assert major == 1 + assert minor >= 21 + assert minor <= 23 + + # Check chardet for compatibility. + major, minor, patch = chardet_version.split('.')[:3] + major, minor, patch = int(major), int(minor), int(patch) + # chardet >= 3.0.2, < 3.1.0 + assert major == 3 + assert minor < 1 + assert patch >= 2 + + +def _check_cryptography(cryptography_version): + # cryptography < 1.3.4 + try: + cryptography_version = list(map(int, cryptography_version.split('.'))) + except ValueError: + return + + if cryptography_version < [1, 3, 4]: + warning = 'Old version of cryptography ({0}) may cause slowdown.'.format(cryptography_version) + warnings.warn(warning, RequestsDependencyWarning) + +# Check imported dependencies for compatibility. +try: + check_compatibility(urllib3.__version__, chardet.__version__) +except (AssertionError, ValueError): + warnings.warn("urllib3 ({0}) or chardet ({1}) doesn't match a supported " + "version!".format(urllib3.__version__, chardet.__version__), + RequestsDependencyWarning) + +# Attempt to enable urllib3's SNI support, if possible +from pip._internal.utils.compat import WINDOWS +if not WINDOWS: + try: + from pip._vendor.urllib3.contrib import pyopenssl + pyopenssl.inject_into_urllib3() + + # Check cryptography version + from cryptography import __version__ as cryptography_version + _check_cryptography(cryptography_version) + except ImportError: + pass + +# urllib3's DependencyWarnings should be silenced. +from pip._vendor.urllib3.exceptions import DependencyWarning +warnings.simplefilter('ignore', DependencyWarning) + +from .__version__ import __title__, __description__, __url__, __version__ +from .__version__ import __build__, __author__, __author_email__, __license__ +from .__version__ import __copyright__, __cake__ + +from . import utils +from . import packages +from .models import Request, Response, PreparedRequest +from .api import request, get, head, post, patch, put, delete, options +from .sessions import session, Session +from .status_codes import codes +from .exceptions import ( + RequestException, Timeout, URLRequired, + TooManyRedirects, HTTPError, ConnectionError, + FileModeWarning, ConnectTimeout, ReadTimeout +) + +# Set default logging handler to avoid "No handler found" warnings. +import logging +try: # Python 2.7+ + from logging import NullHandler +except ImportError: + class NullHandler(logging.Handler): + def emit(self, record): + pass + +logging.getLogger(__name__).addHandler(NullHandler()) + +# FileModeWarnings go off per the default. +warnings.simplefilter('default', FileModeWarning, append=True) diff --git a/venv/Lib/site-packages/pip/_vendor/requests/__version__.py b/venv/Lib/site-packages/pip/_vendor/requests/__version__.py new file mode 100644 index 0000000..ef61ec0 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/__version__.py @@ -0,0 +1,14 @@ +# .-. .-. .-. . . .-. .-. .-. .-. +# |( |- |.| | | |- `-. | `-. +# ' ' `-' `-`.`-' `-' `-' ' `-' + +__title__ = 'requests' +__description__ = 'Python HTTP for Humans.' +__url__ = 'http://python-requests.org' +__version__ = '2.19.1' +__build__ = 0x021901 +__author__ = 'Kenneth Reitz' +__author_email__ = 'me@kennethreitz.org' +__license__ = 'Apache 2.0' +__copyright__ = 'Copyright 2018 Kenneth Reitz' +__cake__ = u'\u2728 \U0001f370 \u2728' diff --git a/venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py b/venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py new file mode 100644 index 0000000..759d9a5 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +""" +requests._internal_utils +~~~~~~~~~~~~~~ + +Provides utility functions that are consumed internally by Requests +which depend on extremely few external helpers (such as compat) +""" + +from .compat import is_py2, builtin_str, str + + +def to_native_string(string, encoding='ascii'): + """Given a string object, regardless of type, returns a representation of + that string in the native string type, encoding and decoding where + necessary. This assumes ASCII unless told otherwise. + """ + if isinstance(string, builtin_str): + out = string + else: + if is_py2: + out = string.encode(encoding) + else: + out = string.decode(encoding) + + return out + + +def unicode_is_ascii(u_string): + """Determine if unicode string only contains ASCII characters. + + :param str u_string: unicode string to check. Must be unicode + and not Python 2 `str`. + :rtype: bool + """ + assert isinstance(u_string, str) + try: + u_string.encode('ascii') + return True + except UnicodeEncodeError: + return False diff --git a/venv/Lib/site-packages/pip/_vendor/requests/adapters.py b/venv/Lib/site-packages/pip/_vendor/requests/adapters.py new file mode 100644 index 0000000..f6f3f99 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/adapters.py @@ -0,0 +1,530 @@ +# -*- coding: utf-8 -*- + +""" +requests.adapters +~~~~~~~~~~~~~~~~~ + +This module contains the transport adapters that Requests uses to define +and maintain connections. +""" + +import os.path +import socket + +from pip._vendor.urllib3.poolmanager import PoolManager, proxy_from_url +from pip._vendor.urllib3.response import HTTPResponse +from pip._vendor.urllib3.util import parse_url +from pip._vendor.urllib3.util import Timeout as TimeoutSauce +from pip._vendor.urllib3.util.retry import Retry +from pip._vendor.urllib3.exceptions import ClosedPoolError +from pip._vendor.urllib3.exceptions import ConnectTimeoutError +from pip._vendor.urllib3.exceptions import HTTPError as _HTTPError +from pip._vendor.urllib3.exceptions import MaxRetryError +from pip._vendor.urllib3.exceptions import NewConnectionError +from pip._vendor.urllib3.exceptions import ProxyError as _ProxyError +from pip._vendor.urllib3.exceptions import ProtocolError +from pip._vendor.urllib3.exceptions import ReadTimeoutError +from pip._vendor.urllib3.exceptions import SSLError as _SSLError +from pip._vendor.urllib3.exceptions import ResponseError + +from .models import Response +from .compat import urlparse, basestring +from .utils import (DEFAULT_CA_BUNDLE_PATH, extract_zipped_paths, + get_encoding_from_headers, prepend_scheme_if_needed, + get_auth_from_url, urldefragauth, select_proxy) +from .structures import CaseInsensitiveDict +from .cookies import extract_cookies_to_jar +from .exceptions import (ConnectionError, ConnectTimeout, ReadTimeout, SSLError, + ProxyError, RetryError, InvalidSchema, InvalidProxyURL) +from .auth import _basic_auth_str + +try: + from pip._vendor.urllib3.contrib.socks import SOCKSProxyManager +except ImportError: + def SOCKSProxyManager(*args, **kwargs): + raise InvalidSchema("Missing dependencies for SOCKS support.") + +DEFAULT_POOLBLOCK = False +DEFAULT_POOLSIZE = 10 +DEFAULT_RETRIES = 0 +DEFAULT_POOL_TIMEOUT = None + + +class BaseAdapter(object): + """The Base Transport Adapter""" + + def __init__(self): + super(BaseAdapter, self).__init__() + + def send(self, request, stream=False, timeout=None, verify=True, + cert=None, proxies=None): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest <PreparedRequest>` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) <timeouts>` tuple. + :type timeout: float or tuple + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + """ + raise NotImplementedError + + def close(self): + """Cleans up adapter specific items.""" + raise NotImplementedError + + +class HTTPAdapter(BaseAdapter): + """The built-in HTTP Adapter for urllib3. + + Provides a general-case interface for Requests sessions to contact HTTP and + HTTPS urls by implementing the Transport Adapter interface. This class will + usually be created by the :class:`Session <Session>` class under the + covers. + + :param pool_connections: The number of urllib3 connection pools to cache. + :param pool_maxsize: The maximum number of connections to save in the pool. + :param max_retries: The maximum number of retries each connection + should attempt. Note, this applies only to failed DNS lookups, socket + connections and connection timeouts, never to requests where data has + made it to the server. By default, Requests does not retry failed + connections. If you need granular control over the conditions under + which we retry a request, import urllib3's ``Retry`` class and pass + that instead. + :param pool_block: Whether the connection pool should block for connections. + + Usage:: + + >>> import requests + >>> s = requests.Session() + >>> a = requests.adapters.HTTPAdapter(max_retries=3) + >>> s.mount('http://', a) + """ + __attrs__ = ['max_retries', 'config', '_pool_connections', '_pool_maxsize', + '_pool_block'] + + def __init__(self, pool_connections=DEFAULT_POOLSIZE, + pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES, + pool_block=DEFAULT_POOLBLOCK): + if max_retries == DEFAULT_RETRIES: + self.max_retries = Retry(0, read=False) + else: + self.max_retries = Retry.from_int(max_retries) + self.config = {} + self.proxy_manager = {} + + super(HTTPAdapter, self).__init__() + + self._pool_connections = pool_connections + self._pool_maxsize = pool_maxsize + self._pool_block = pool_block + + self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block) + + def __getstate__(self): + return dict((attr, getattr(self, attr, None)) for attr in + self.__attrs__) + + def __setstate__(self, state): + # Can't handle by adding 'proxy_manager' to self.__attrs__ because + # self.poolmanager uses a lambda function, which isn't pickleable. + self.proxy_manager = {} + self.config = {} + + for attr, value in state.items(): + setattr(self, attr, value) + + self.init_poolmanager(self._pool_connections, self._pool_maxsize, + block=self._pool_block) + + def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs): + """Initializes a urllib3 PoolManager. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param connections: The number of urllib3 connection pools to cache. + :param maxsize: The maximum number of connections to save in the pool. + :param block: Block when no free connections are available. + :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager. + """ + # save these values for pickling + self._pool_connections = connections + self._pool_maxsize = maxsize + self._pool_block = block + + self.poolmanager = PoolManager(num_pools=connections, maxsize=maxsize, + block=block, strict=True, **pool_kwargs) + + def proxy_manager_for(self, proxy, **proxy_kwargs): + """Return urllib3 ProxyManager for the given proxy. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param proxy: The proxy to return a urllib3 ProxyManager for. + :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager. + :returns: ProxyManager + :rtype: urllib3.ProxyManager + """ + if proxy in self.proxy_manager: + manager = self.proxy_manager[proxy] + elif proxy.lower().startswith('socks'): + username, password = get_auth_from_url(proxy) + manager = self.proxy_manager[proxy] = SOCKSProxyManager( + proxy, + username=username, + password=password, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs + ) + else: + proxy_headers = self.proxy_headers(proxy) + manager = self.proxy_manager[proxy] = proxy_from_url( + proxy, + proxy_headers=proxy_headers, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs) + + return manager + + def cert_verify(self, conn, url, verify, cert): + """Verify a SSL certificate. This method should not be called from user + code, and is only exposed for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param conn: The urllib3 connection object associated with the cert. + :param url: The requested URL. + :param verify: Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use + :param cert: The SSL certificate to verify. + """ + if url.lower().startswith('https') and verify: + + cert_loc = None + + # Allow self-specified cert location. + if verify is not True: + cert_loc = verify + + if not cert_loc: + cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) + + if not cert_loc or not os.path.exists(cert_loc): + raise IOError("Could not find a suitable TLS CA certificate bundle, " + "invalid path: {0}".format(cert_loc)) + + conn.cert_reqs = 'CERT_REQUIRED' + + if not os.path.isdir(cert_loc): + conn.ca_certs = cert_loc + else: + conn.ca_cert_dir = cert_loc + else: + conn.cert_reqs = 'CERT_NONE' + conn.ca_certs = None + conn.ca_cert_dir = None + + if cert: + if not isinstance(cert, basestring): + conn.cert_file = cert[0] + conn.key_file = cert[1] + else: + conn.cert_file = cert + conn.key_file = None + if conn.cert_file and not os.path.exists(conn.cert_file): + raise IOError("Could not find the TLS certificate file, " + "invalid path: {0}".format(conn.cert_file)) + if conn.key_file and not os.path.exists(conn.key_file): + raise IOError("Could not find the TLS key file, " + "invalid path: {0}".format(conn.key_file)) + + def build_response(self, req, resp): + """Builds a :class:`Response <requests.Response>` object from a urllib3 + response. This should not be called from user code, and is only exposed + for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>` + + :param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response. + :param resp: The urllib3 response object. + :rtype: requests.Response + """ + response = Response() + + # Fallback to None if there's no status_code, for whatever reason. + response.status_code = getattr(resp, 'status', None) + + # Make headers case-insensitive. + response.headers = CaseInsensitiveDict(getattr(resp, 'headers', {})) + + # Set encoding. + response.encoding = get_encoding_from_headers(response.headers) + response.raw = resp + response.reason = response.raw.reason + + if isinstance(req.url, bytes): + response.url = req.url.decode('utf-8') + else: + response.url = req.url + + # Add new cookies from the server. + extract_cookies_to_jar(response.cookies, req, resp) + + # Give the Response some context. + response.request = req + response.connection = self + + return response + + def get_connection(self, url, proxies=None): + """Returns a urllib3 connection for the given URL. This should not be + called from user code, and is only exposed for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param url: The URL to connect to. + :param proxies: (optional) A Requests-style dictionary of proxies used on this request. + :rtype: urllib3.ConnectionPool + """ + proxy = select_proxy(url, proxies) + + if proxy: + proxy = prepend_scheme_if_needed(proxy, 'http') + proxy_url = parse_url(proxy) + if not proxy_url.host: + raise InvalidProxyURL("Please check proxy URL. It is malformed" + " and could be missing the host.") + proxy_manager = self.proxy_manager_for(proxy) + conn = proxy_manager.connection_from_url(url) + else: + # Only scheme should be lower case + parsed = urlparse(url) + url = parsed.geturl() + conn = self.poolmanager.connection_from_url(url) + + return conn + + def close(self): + """Disposes of any internal state. + + Currently, this closes the PoolManager and any active ProxyManager, + which closes any pooled connections. + """ + self.poolmanager.clear() + for proxy in self.proxy_manager.values(): + proxy.clear() + + def request_url(self, request, proxies): + """Obtain the url to use when making the final request. + + If the message is being sent through a HTTP proxy, the full URL has to + be used. Otherwise, we should only use the path portion of the URL. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param request: The :class:`PreparedRequest <PreparedRequest>` being sent. + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs. + :rtype: str + """ + proxy = select_proxy(request.url, proxies) + scheme = urlparse(request.url).scheme + + is_proxied_http_request = (proxy and scheme != 'https') + using_socks_proxy = False + if proxy: + proxy_scheme = urlparse(proxy).scheme.lower() + using_socks_proxy = proxy_scheme.startswith('socks') + + url = request.path_url + if is_proxied_http_request and not using_socks_proxy: + url = urldefragauth(request.url) + + return url + + def add_headers(self, request, **kwargs): + """Add any headers needed by the connection. As of v2.0 this does + nothing by default, but is left for overriding by users that subclass + the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param request: The :class:`PreparedRequest <PreparedRequest>` to add headers to. + :param kwargs: The keyword arguments from the call to send(). + """ + pass + + def proxy_headers(self, proxy): + """Returns a dictionary of the headers to add to any request sent + through a proxy. This works with urllib3 magic to ensure that they are + correctly sent to the proxy, rather than in a tunnelled request if + CONNECT is being used. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param proxies: The url of the proxy being used for this request. + :rtype: dict + """ + headers = {} + username, password = get_auth_from_url(proxy) + + if username: + headers['Proxy-Authorization'] = _basic_auth_str(username, + password) + + return headers + + def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest <PreparedRequest>` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) <timeouts>` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + conn = self.get_connection(request.url, proxies) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers(request, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies) + + chunked = not (request.body is None or 'Content-Length' in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError as e: + # this may raise a string formatting error. + err = ("Invalid timeout {0}. Pass a (connect, read) " + "timeout tuple, or a single float to set " + "both timeouts to the same value".format(timeout)) + raise ValueError(err) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + if not chunked: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout + ) + + # Send the request. + else: + if hasattr(conn, 'proxy_pool'): + conn = conn.proxy_pool + + low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT) + + try: + low_conn.putrequest(request.method, + url, + skip_accept_encoding=True) + + for header, value in request.headers.items(): + low_conn.putheader(header, value) + + low_conn.endheaders() + + for i in request.body: + low_conn.send(hex(len(i))[2:].encode('utf-8')) + low_conn.send(b'\r\n') + low_conn.send(i) + low_conn.send(b'\r\n') + low_conn.send(b'0\r\n\r\n') + + # Receive the response from the server + try: + # For Python 2.7+ versions, use buffering of HTTP + # responses + r = low_conn.getresponse(buffering=True) + except TypeError: + # For compatibility with Python 2.6 versions and back + r = low_conn.getresponse() + + resp = HTTPResponse.from_httplib( + r, + pool=conn, + connection=low_conn, + preload_content=False, + decode_content=False + ) + except: + # If we hit any problems here, clean up the connection. + # Then, reraise so that we can handle the actual exception. + low_conn.close() + raise + + except (ProtocolError, socket.error) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + raise ConnectionError(e, request=request) + + except ClosedPoolError as e: + raise ConnectionError(e, request=request) + + except _ProxyError as e: + raise ProxyError(e) + + except (_SSLError, _HTTPError) as e: + if isinstance(e, _SSLError): + # This branch is for urllib3 versions earlier than v1.22 + raise SSLError(e, request=request) + elif isinstance(e, ReadTimeoutError): + raise ReadTimeout(e, request=request) + else: + raise + + return self.build_response(request, resp) diff --git a/venv/Lib/site-packages/pip/_vendor/requests/api.py b/venv/Lib/site-packages/pip/_vendor/requests/api.py new file mode 100644 index 0000000..a2cc84d --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/api.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- + +""" +requests.api +~~~~~~~~~~~~ + +This module implements the Requests API. + +:copyright: (c) 2012 by Kenneth Reitz. +:license: Apache2, see LICENSE for more details. +""" + +from . import sessions + + +def request(method, url, **kwargs): + """Constructs and sends a :class:`Request <Request>`. + + :param method: method for the new :class:`Request` object. + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. + :param data: (optional) Dictionary or list of tuples ``[(key, value)]`` (will be form-encoded), bytes, or file-like object to send in the body of the :class:`Request`. + :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. + :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. + :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. + ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` + or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string + defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers + to add for the file. + :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. + :param timeout: (optional) How many seconds to wait for the server to send data + before giving up, as a float, or a :ref:`(connect timeout, read + timeout) <timeouts>` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use. Defaults to ``True``. + :param stream: (optional) if ``False``, the response content will be immediately downloaded. + :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. + :return: :class:`Response <Response>` object + :rtype: requests.Response + + Usage:: + + >>> import requests + >>> req = requests.request('GET', 'http://httpbin.org/get') + <Response [200]> + """ + + # By using the 'with' statement we are sure the session is closed, thus we + # avoid leaving sockets open which can trigger a ResourceWarning in some + # cases, and look like a memory leak in others. + with sessions.Session() as session: + return session.request(method=method, url=url, **kwargs) + + +def get(url, params=None, **kwargs): + r"""Sends a GET request. + + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return request('get', url, params=params, **kwargs) + + +def options(url, **kwargs): + r"""Sends an OPTIONS request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return request('options', url, **kwargs) + + +def head(url, **kwargs): + r"""Sends a HEAD request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', False) + return request('head', url, **kwargs) + + +def post(url, data=None, json=None, **kwargs): + r"""Sends a POST request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary (will be form-encoded), bytes, or file-like object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + return request('post', url, data=data, json=json, **kwargs) + + +def put(url, data=None, **kwargs): + r"""Sends a PUT request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary (will be form-encoded), bytes, or file-like object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + return request('put', url, data=data, **kwargs) + + +def patch(url, data=None, **kwargs): + r"""Sends a PATCH request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary (will be form-encoded), bytes, or file-like object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + return request('patch', url, data=data, **kwargs) + + +def delete(url, **kwargs): + r"""Sends a DELETE request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + return request('delete', url, **kwargs) diff --git a/venv/Lib/site-packages/pip/_vendor/requests/auth.py b/venv/Lib/site-packages/pip/_vendor/requests/auth.py new file mode 100644 index 0000000..4ae4594 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/auth.py @@ -0,0 +1,305 @@ +# -*- coding: utf-8 -*- + +""" +requests.auth +~~~~~~~~~~~~~ + +This module contains the authentication handlers for Requests. +""" + +import os +import re +import time +import hashlib +import threading +import warnings + +from base64 import b64encode + +from .compat import urlparse, str, basestring +from .cookies import extract_cookies_to_jar +from ._internal_utils import to_native_string +from .utils import parse_dict_header + +CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded' +CONTENT_TYPE_MULTI_PART = 'multipart/form-data' + + +def _basic_auth_str(username, password): + """Returns a Basic Auth string.""" + + # "I want us to put a big-ol' comment on top of it that + # says that this behaviour is dumb but we need to preserve + # it because people are relying on it." + # - Lukasa + # + # These are here solely to maintain backwards compatibility + # for things like ints. This will be removed in 3.0.0. + if not isinstance(username, basestring): + warnings.warn( + "Non-string usernames will no longer be supported in Requests " + "3.0.0. Please convert the object you've passed in ({0!r}) to " + "a string or bytes object in the near future to avoid " + "problems.".format(username), + category=DeprecationWarning, + ) + username = str(username) + + if not isinstance(password, basestring): + warnings.warn( + "Non-string passwords will no longer be supported in Requests " + "3.0.0. Please convert the object you've passed in ({0!r}) to " + "a string or bytes object in the near future to avoid " + "problems.".format(password), + category=DeprecationWarning, + ) + password = str(password) + # -- End Removal -- + + if isinstance(username, str): + username = username.encode('latin1') + + if isinstance(password, str): + password = password.encode('latin1') + + authstr = 'Basic ' + to_native_string( + b64encode(b':'.join((username, password))).strip() + ) + + return authstr + + +class AuthBase(object): + """Base class that all auth implementations derive from""" + + def __call__(self, r): + raise NotImplementedError('Auth hooks must be callable.') + + +class HTTPBasicAuth(AuthBase): + """Attaches HTTP Basic Authentication to the given Request object.""" + + def __init__(self, username, password): + self.username = username + self.password = password + + def __eq__(self, other): + return all([ + self.username == getattr(other, 'username', None), + self.password == getattr(other, 'password', None) + ]) + + def __ne__(self, other): + return not self == other + + def __call__(self, r): + r.headers['Authorization'] = _basic_auth_str(self.username, self.password) + return r + + +class HTTPProxyAuth(HTTPBasicAuth): + """Attaches HTTP Proxy Authentication to a given Request object.""" + + def __call__(self, r): + r.headers['Proxy-Authorization'] = _basic_auth_str(self.username, self.password) + return r + + +class HTTPDigestAuth(AuthBase): + """Attaches HTTP Digest Authentication to the given Request object.""" + + def __init__(self, username, password): + self.username = username + self.password = password + # Keep state in per-thread local storage + self._thread_local = threading.local() + + def init_per_thread_state(self): + # Ensure state is initialized just once per-thread + if not hasattr(self._thread_local, 'init'): + self._thread_local.init = True + self._thread_local.last_nonce = '' + self._thread_local.nonce_count = 0 + self._thread_local.chal = {} + self._thread_local.pos = None + self._thread_local.num_401_calls = None + + def build_digest_header(self, method, url): + """ + :rtype: str + """ + + realm = self._thread_local.chal['realm'] + nonce = self._thread_local.chal['nonce'] + qop = self._thread_local.chal.get('qop') + algorithm = self._thread_local.chal.get('algorithm') + opaque = self._thread_local.chal.get('opaque') + hash_utf8 = None + + if algorithm is None: + _algorithm = 'MD5' + else: + _algorithm = algorithm.upper() + # lambdas assume digest modules are imported at the top level + if _algorithm == 'MD5' or _algorithm == 'MD5-SESS': + def md5_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.md5(x).hexdigest() + hash_utf8 = md5_utf8 + elif _algorithm == 'SHA': + def sha_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.sha1(x).hexdigest() + hash_utf8 = sha_utf8 + elif _algorithm == 'SHA-256': + def sha256_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.sha256(x).hexdigest() + hash_utf8 = sha256_utf8 + elif _algorithm == 'SHA-512': + def sha512_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.sha512(x).hexdigest() + hash_utf8 = sha512_utf8 + + KD = lambda s, d: hash_utf8("%s:%s" % (s, d)) + + if hash_utf8 is None: + return None + + # XXX not implemented yet + entdig = None + p_parsed = urlparse(url) + #: path is request-uri defined in RFC 2616 which should not be empty + path = p_parsed.path or "/" + if p_parsed.query: + path += '?' + p_parsed.query + + A1 = '%s:%s:%s' % (self.username, realm, self.password) + A2 = '%s:%s' % (method, path) + + HA1 = hash_utf8(A1) + HA2 = hash_utf8(A2) + + if nonce == self._thread_local.last_nonce: + self._thread_local.nonce_count += 1 + else: + self._thread_local.nonce_count = 1 + ncvalue = '%08x' % self._thread_local.nonce_count + s = str(self._thread_local.nonce_count).encode('utf-8') + s += nonce.encode('utf-8') + s += time.ctime().encode('utf-8') + s += os.urandom(8) + + cnonce = (hashlib.sha1(s).hexdigest()[:16]) + if _algorithm == 'MD5-SESS': + HA1 = hash_utf8('%s:%s:%s' % (HA1, nonce, cnonce)) + + if not qop: + respdig = KD(HA1, "%s:%s" % (nonce, HA2)) + elif qop == 'auth' or 'auth' in qop.split(','): + noncebit = "%s:%s:%s:%s:%s" % ( + nonce, ncvalue, cnonce, 'auth', HA2 + ) + respdig = KD(HA1, noncebit) + else: + # XXX handle auth-int. + return None + + self._thread_local.last_nonce = nonce + + # XXX should the partial digests be encoded too? + base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \ + 'response="%s"' % (self.username, realm, nonce, path, respdig) + if opaque: + base += ', opaque="%s"' % opaque + if algorithm: + base += ', algorithm="%s"' % algorithm + if entdig: + base += ', digest="%s"' % entdig + if qop: + base += ', qop="auth", nc=%s, cnonce="%s"' % (ncvalue, cnonce) + + return 'Digest %s' % (base) + + def handle_redirect(self, r, **kwargs): + """Reset num_401_calls counter on redirects.""" + if r.is_redirect: + self._thread_local.num_401_calls = 1 + + def handle_401(self, r, **kwargs): + """ + Takes the given response and tries digest-auth, if needed. + + :rtype: requests.Response + """ + + # If response is not 4xx, do not auth + # See https://github.com/requests/requests/issues/3772 + if not 400 <= r.status_code < 500: + self._thread_local.num_401_calls = 1 + return r + + if self._thread_local.pos is not None: + # Rewind the file position indicator of the body to where + # it was to resend the request. + r.request.body.seek(self._thread_local.pos) + s_auth = r.headers.get('www-authenticate', '') + + if 'digest' in s_auth.lower() and self._thread_local.num_401_calls < 2: + + self._thread_local.num_401_calls += 1 + pat = re.compile(r'digest ', flags=re.IGNORECASE) + self._thread_local.chal = parse_dict_header(pat.sub('', s_auth, count=1)) + + # Consume content and release the original connection + # to allow our new request to reuse the same one. + r.content + r.close() + prep = r.request.copy() + extract_cookies_to_jar(prep._cookies, r.request, r.raw) + prep.prepare_cookies(prep._cookies) + + prep.headers['Authorization'] = self.build_digest_header( + prep.method, prep.url) + _r = r.connection.send(prep, **kwargs) + _r.history.append(r) + _r.request = prep + + return _r + + self._thread_local.num_401_calls = 1 + return r + + def __call__(self, r): + # Initialize per-thread state, if needed + self.init_per_thread_state() + # If we have a saved nonce, skip the 401 + if self._thread_local.last_nonce: + r.headers['Authorization'] = self.build_digest_header(r.method, r.url) + try: + self._thread_local.pos = r.body.tell() + except AttributeError: + # In the case of HTTPDigestAuth being reused and the body of + # the previous request was a file-like object, pos has the + # file position of the previous body. Ensure it's set to + # None. + self._thread_local.pos = None + r.register_hook('response', self.handle_401) + r.register_hook('response', self.handle_redirect) + self._thread_local.num_401_calls = 1 + + return r + + def __eq__(self, other): + return all([ + self.username == getattr(other, 'username', None), + self.password == getattr(other, 'password', None) + ]) + + def __ne__(self, other): + return not self == other diff --git a/venv/Lib/site-packages/pip/_vendor/requests/certs.py b/venv/Lib/site-packages/pip/_vendor/requests/certs.py new file mode 100644 index 0000000..06a594e --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/certs.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +requests.certs +~~~~~~~~~~~~~~ + +This module returns the preferred default CA certificate bundle. There is +only one — the one from the certifi package. + +If you are packaging Requests, e.g., for a Linux distribution or a managed +environment, you can change the definition of where() to return a separately +packaged CA bundle. +""" +from pip._vendor.certifi import where + +if __name__ == '__main__': + print(where()) diff --git a/venv/Lib/site-packages/pip/_vendor/requests/compat.py b/venv/Lib/site-packages/pip/_vendor/requests/compat.py new file mode 100644 index 0000000..ec5d305 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/compat.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +""" +requests.compat +~~~~~~~~~~~~~~~ + +This module handles import compatibility issues between Python 2 and +Python 3. +""" + +from pip._vendor import chardet + +import sys + +# ------- +# Pythons +# ------- + +# Syntax sugar. +_ver = sys.version_info + +#: Python 2.x? +is_py2 = (_ver[0] == 2) + +#: Python 3.x? +is_py3 = (_ver[0] == 3) + +# Note: We've patched out simplejson support in pip because it prevents +# upgrading simplejson on Windows. +# try: +# import simplejson as json +# except (ImportError, SyntaxError): +# # simplejson does not support Python 3.2, it throws a SyntaxError +# # because of u'...' Unicode literals. +import json + +# --------- +# Specifics +# --------- + +if is_py2: + from urllib import ( + quote, unquote, quote_plus, unquote_plus, urlencode, getproxies, + proxy_bypass, proxy_bypass_environment, getproxies_environment) + from urlparse import urlparse, urlunparse, urljoin, urlsplit, urldefrag + from urllib2 import parse_http_list + import cookielib + from Cookie import Morsel + from StringIO import StringIO + from collections import Callable, Mapping, MutableMapping + + from pip._vendor.urllib3.packages.ordered_dict import OrderedDict + + builtin_str = str + bytes = str + str = unicode + basestring = basestring + numeric_types = (int, long, float) + integer_types = (int, long) + +elif is_py3: + from urllib.parse import urlparse, urlunparse, urljoin, urlsplit, urlencode, quote, unquote, quote_plus, unquote_plus, urldefrag + from urllib.request import parse_http_list, getproxies, proxy_bypass, proxy_bypass_environment, getproxies_environment + from http import cookiejar as cookielib + from http.cookies import Morsel + from io import StringIO + from collections import OrderedDict + from collections.abc import Callable, Mapping, MutableMapping + + builtin_str = str + str = str + bytes = bytes + basestring = (str, bytes) + numeric_types = (int, float) + integer_types = (int,) diff --git a/venv/Lib/site-packages/pip/_vendor/requests/cookies.py b/venv/Lib/site-packages/pip/_vendor/requests/cookies.py new file mode 100644 index 0000000..50883a8 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/cookies.py @@ -0,0 +1,546 @@ +# -*- coding: utf-8 -*- + +""" +requests.cookies +~~~~~~~~~~~~~~~~ + +Compatibility code to be able to use `cookielib.CookieJar` with requests. + +requests.utils imports from here, so be careful with imports. +""" + +import copy +import time +import calendar + +from ._internal_utils import to_native_string +from .compat import cookielib, urlparse, urlunparse, Morsel, MutableMapping + +try: + import threading +except ImportError: + import dummy_threading as threading + + +class MockRequest(object): + """Wraps a `requests.Request` to mimic a `urllib2.Request`. + + The code in `cookielib.CookieJar` expects this interface in order to correctly + manage cookie policies, i.e., determine whether a cookie can be set, given the + domains of the request and the cookie. + + The original request object is read-only. The client is responsible for collecting + the new headers via `get_new_headers()` and interpreting them appropriately. You + probably want `get_cookie_header`, defined below. + """ + + def __init__(self, request): + self._r = request + self._new_headers = {} + self.type = urlparse(self._r.url).scheme + + def get_type(self): + return self.type + + def get_host(self): + return urlparse(self._r.url).netloc + + def get_origin_req_host(self): + return self.get_host() + + def get_full_url(self): + # Only return the response's URL if the user hadn't set the Host + # header + if not self._r.headers.get('Host'): + return self._r.url + # If they did set it, retrieve it and reconstruct the expected domain + host = to_native_string(self._r.headers['Host'], encoding='utf-8') + parsed = urlparse(self._r.url) + # Reconstruct the URL as we expect it + return urlunparse([ + parsed.scheme, host, parsed.path, parsed.params, parsed.query, + parsed.fragment + ]) + + def is_unverifiable(self): + return True + + def has_header(self, name): + return name in self._r.headers or name in self._new_headers + + def get_header(self, name, default=None): + return self._r.headers.get(name, self._new_headers.get(name, default)) + + def add_header(self, key, val): + """cookielib has no legitimate use for this method; add it back if you find one.""" + raise NotImplementedError("Cookie headers should be added with add_unredirected_header()") + + def add_unredirected_header(self, name, value): + self._new_headers[name] = value + + def get_new_headers(self): + return self._new_headers + + @property + def unverifiable(self): + return self.is_unverifiable() + + @property + def origin_req_host(self): + return self.get_origin_req_host() + + @property + def host(self): + return self.get_host() + + +class MockResponse(object): + """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`. + + ...what? Basically, expose the parsed HTTP headers from the server response + the way `cookielib` expects to see them. + """ + + def __init__(self, headers): + """Make a MockResponse for `cookielib` to read. + + :param headers: a httplib.HTTPMessage or analogous carrying the headers + """ + self._headers = headers + + def info(self): + return self._headers + + def getheaders(self, name): + self._headers.getheaders(name) + + +def extract_cookies_to_jar(jar, request, response): + """Extract the cookies from the response into a CookieJar. + + :param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar) + :param request: our own requests.Request object + :param response: urllib3.HTTPResponse object + """ + if not (hasattr(response, '_original_response') and + response._original_response): + return + # the _original_response field is the wrapped httplib.HTTPResponse object, + req = MockRequest(request) + # pull out the HTTPMessage with the headers and put it in the mock: + res = MockResponse(response._original_response.msg) + jar.extract_cookies(res, req) + + +def get_cookie_header(jar, request): + """ + Produce an appropriate Cookie header string to be sent with `request`, or None. + + :rtype: str + """ + r = MockRequest(request) + jar.add_cookie_header(r) + return r.get_new_headers().get('Cookie') + + +def remove_cookie_by_name(cookiejar, name, domain=None, path=None): + """Unsets a cookie by name, by default over all domains and paths. + + Wraps CookieJar.clear(), is O(n). + """ + clearables = [] + for cookie in cookiejar: + if cookie.name != name: + continue + if domain is not None and domain != cookie.domain: + continue + if path is not None and path != cookie.path: + continue + clearables.append((cookie.domain, cookie.path, cookie.name)) + + for domain, path, name in clearables: + cookiejar.clear(domain, path, name) + + +class CookieConflictError(RuntimeError): + """There are two cookies that meet the criteria specified in the cookie jar. + Use .get and .set and include domain and path args in order to be more specific. + """ + + +class RequestsCookieJar(cookielib.CookieJar, MutableMapping): + """Compatibility class; is a cookielib.CookieJar, but exposes a dict + interface. + + This is the CookieJar we create by default for requests and sessions that + don't specify one, since some clients may expect response.cookies and + session.cookies to support dict operations. + + Requests does not use the dict interface internally; it's just for + compatibility with external client code. All requests code should work + out of the box with externally provided instances of ``CookieJar``, e.g. + ``LWPCookieJar`` and ``FileCookieJar``. + + Unlike a regular CookieJar, this class is pickleable. + + .. warning:: dictionary operations that are normally O(1) may be O(n). + """ + + def get(self, name, default=None, domain=None, path=None): + """Dict-like get() that also supports optional domain and path args in + order to resolve naming collisions from using one cookie jar over + multiple domains. + + .. warning:: operation is O(n), not O(1). + """ + try: + return self._find_no_duplicates(name, domain, path) + except KeyError: + return default + + def set(self, name, value, **kwargs): + """Dict-like set() that also supports optional domain and path args in + order to resolve naming collisions from using one cookie jar over + multiple domains. + """ + # support client code that unsets cookies by assignment of a None value: + if value is None: + remove_cookie_by_name(self, name, domain=kwargs.get('domain'), path=kwargs.get('path')) + return + + if isinstance(value, Morsel): + c = morsel_to_cookie(value) + else: + c = create_cookie(name, value, **kwargs) + self.set_cookie(c) + return c + + def iterkeys(self): + """Dict-like iterkeys() that returns an iterator of names of cookies + from the jar. + + .. seealso:: itervalues() and iteritems(). + """ + for cookie in iter(self): + yield cookie.name + + def keys(self): + """Dict-like keys() that returns a list of names of cookies from the + jar. + + .. seealso:: values() and items(). + """ + return list(self.iterkeys()) + + def itervalues(self): + """Dict-like itervalues() that returns an iterator of values of cookies + from the jar. + + .. seealso:: iterkeys() and iteritems(). + """ + for cookie in iter(self): + yield cookie.value + + def values(self): + """Dict-like values() that returns a list of values of cookies from the + jar. + + .. seealso:: keys() and items(). + """ + return list(self.itervalues()) + + def iteritems(self): + """Dict-like iteritems() that returns an iterator of name-value tuples + from the jar. + + .. seealso:: iterkeys() and itervalues(). + """ + for cookie in iter(self): + yield cookie.name, cookie.value + + def items(self): + """Dict-like items() that returns a list of name-value tuples from the + jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a + vanilla python dict of key value pairs. + + .. seealso:: keys() and values(). + """ + return list(self.iteritems()) + + def list_domains(self): + """Utility method to list all the domains in the jar.""" + domains = [] + for cookie in iter(self): + if cookie.domain not in domains: + domains.append(cookie.domain) + return domains + + def list_paths(self): + """Utility method to list all the paths in the jar.""" + paths = [] + for cookie in iter(self): + if cookie.path not in paths: + paths.append(cookie.path) + return paths + + def multiple_domains(self): + """Returns True if there are multiple domains in the jar. + Returns False otherwise. + + :rtype: bool + """ + domains = [] + for cookie in iter(self): + if cookie.domain is not None and cookie.domain in domains: + return True + domains.append(cookie.domain) + return False # there is only one domain in jar + + def get_dict(self, domain=None, path=None): + """Takes as an argument an optional domain and path and returns a plain + old Python dict of name-value pairs of cookies that meet the + requirements. + + :rtype: dict + """ + dictionary = {} + for cookie in iter(self): + if ( + (domain is None or cookie.domain == domain) and + (path is None or cookie.path == path) + ): + dictionary[cookie.name] = cookie.value + return dictionary + + def __contains__(self, name): + try: + return super(RequestsCookieJar, self).__contains__(name) + except CookieConflictError: + return True + + def __getitem__(self, name): + """Dict-like __getitem__() for compatibility with client code. Throws + exception if there are more than one cookie with name. In that case, + use the more explicit get() method instead. + + .. warning:: operation is O(n), not O(1). + """ + return self._find_no_duplicates(name) + + def __setitem__(self, name, value): + """Dict-like __setitem__ for compatibility with client code. Throws + exception if there is already a cookie of that name in the jar. In that + case, use the more explicit set() method instead. + """ + self.set(name, value) + + def __delitem__(self, name): + """Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s + ``remove_cookie_by_name()``. + """ + remove_cookie_by_name(self, name) + + def set_cookie(self, cookie, *args, **kwargs): + if hasattr(cookie.value, 'startswith') and cookie.value.startswith('"') and cookie.value.endswith('"'): + cookie.value = cookie.value.replace('\\"', '') + return super(RequestsCookieJar, self).set_cookie(cookie, *args, **kwargs) + + def update(self, other): + """Updates this jar with cookies from another CookieJar or dict-like""" + if isinstance(other, cookielib.CookieJar): + for cookie in other: + self.set_cookie(copy.copy(cookie)) + else: + super(RequestsCookieJar, self).update(other) + + def _find(self, name, domain=None, path=None): + """Requests uses this method internally to get cookie values. + + If there are conflicting cookies, _find arbitrarily chooses one. + See _find_no_duplicates if you want an exception thrown if there are + conflicting cookies. + + :param name: a string containing name of cookie + :param domain: (optional) string containing domain of cookie + :param path: (optional) string containing path of cookie + :return: cookie.value + """ + for cookie in iter(self): + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + return cookie.value + + raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) + + def _find_no_duplicates(self, name, domain=None, path=None): + """Both ``__get_item__`` and ``get`` call this function: it's never + used elsewhere in Requests. + + :param name: a string containing name of cookie + :param domain: (optional) string containing domain of cookie + :param path: (optional) string containing path of cookie + :raises KeyError: if cookie is not found + :raises CookieConflictError: if there are multiple cookies + that match name and optionally domain and path + :return: cookie.value + """ + toReturn = None + for cookie in iter(self): + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + if toReturn is not None: # if there are multiple cookies that meet passed in criteria + raise CookieConflictError('There are multiple cookies with name, %r' % (name)) + toReturn = cookie.value # we will eventually return this as long as no cookie conflict + + if toReturn: + return toReturn + raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) + + def __getstate__(self): + """Unlike a normal CookieJar, this class is pickleable.""" + state = self.__dict__.copy() + # remove the unpickleable RLock object + state.pop('_cookies_lock') + return state + + def __setstate__(self, state): + """Unlike a normal CookieJar, this class is pickleable.""" + self.__dict__.update(state) + if '_cookies_lock' not in self.__dict__: + self._cookies_lock = threading.RLock() + + def copy(self): + """Return a copy of this RequestsCookieJar.""" + new_cj = RequestsCookieJar() + new_cj.set_policy(self.get_policy()) + new_cj.update(self) + return new_cj + + def get_policy(self): + """Return the CookiePolicy instance used.""" + return self._policy + + +def _copy_cookie_jar(jar): + if jar is None: + return None + + if hasattr(jar, 'copy'): + # We're dealing with an instance of RequestsCookieJar + return jar.copy() + # We're dealing with a generic CookieJar instance + new_jar = copy.copy(jar) + new_jar.clear() + for cookie in jar: + new_jar.set_cookie(copy.copy(cookie)) + return new_jar + + +def create_cookie(name, value, **kwargs): + """Make a cookie from underspecified parameters. + + By default, the pair of `name` and `value` will be set for the domain '' + and sent on every request (this is sometimes called a "supercookie"). + """ + result = dict( + version=0, + name=name, + value=value, + port=None, + domain='', + path='/', + secure=False, + expires=None, + discard=True, + comment=None, + comment_url=None, + rest={'HttpOnly': None}, + rfc2109=False,) + + badargs = set(kwargs) - set(result) + if badargs: + err = 'create_cookie() got unexpected keyword arguments: %s' + raise TypeError(err % list(badargs)) + + result.update(kwargs) + result['port_specified'] = bool(result['port']) + result['domain_specified'] = bool(result['domain']) + result['domain_initial_dot'] = result['domain'].startswith('.') + result['path_specified'] = bool(result['path']) + + return cookielib.Cookie(**result) + + +def morsel_to_cookie(morsel): + """Convert a Morsel object into a Cookie containing the one k/v pair.""" + + expires = None + if morsel['max-age']: + try: + expires = int(time.time() + int(morsel['max-age'])) + except ValueError: + raise TypeError('max-age: %s must be integer' % morsel['max-age']) + elif morsel['expires']: + time_template = '%a, %d-%b-%Y %H:%M:%S GMT' + expires = calendar.timegm( + time.strptime(morsel['expires'], time_template) + ) + return create_cookie( + comment=morsel['comment'], + comment_url=bool(morsel['comment']), + discard=False, + domain=morsel['domain'], + expires=expires, + name=morsel.key, + path=morsel['path'], + port=None, + rest={'HttpOnly': morsel['httponly']}, + rfc2109=False, + secure=bool(morsel['secure']), + value=morsel.value, + version=morsel['version'] or 0, + ) + + +def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True): + """Returns a CookieJar from a key/value dictionary. + + :param cookie_dict: Dict of key/values to insert into CookieJar. + :param cookiejar: (optional) A cookiejar to add the cookies to. + :param overwrite: (optional) If False, will not replace cookies + already in the jar with new ones. + """ + if cookiejar is None: + cookiejar = RequestsCookieJar() + + if cookie_dict is not None: + names_from_jar = [cookie.name for cookie in cookiejar] + for name in cookie_dict: + if overwrite or (name not in names_from_jar): + cookiejar.set_cookie(create_cookie(name, cookie_dict[name])) + + return cookiejar + + +def merge_cookies(cookiejar, cookies): + """Add cookies to cookiejar and returns a merged CookieJar. + + :param cookiejar: CookieJar object to add the cookies to. + :param cookies: Dictionary or CookieJar object to be added. + """ + if not isinstance(cookiejar, cookielib.CookieJar): + raise ValueError('You can only merge into CookieJar') + + if isinstance(cookies, dict): + cookiejar = cookiejar_from_dict( + cookies, cookiejar=cookiejar, overwrite=False) + elif isinstance(cookies, cookielib.CookieJar): + try: + cookiejar.update(cookies) + except AttributeError: + for cookie_in_jar in cookies: + cookiejar.set_cookie(cookie_in_jar) + + return cookiejar diff --git a/venv/Lib/site-packages/pip/_vendor/requests/exceptions.py b/venv/Lib/site-packages/pip/_vendor/requests/exceptions.py new file mode 100644 index 0000000..a91e1fd --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/exceptions.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- + +""" +requests.exceptions +~~~~~~~~~~~~~~~~~~~ + +This module contains the set of Requests' exceptions. +""" +from pip._vendor.urllib3.exceptions import HTTPError as BaseHTTPError + + +class RequestException(IOError): + """There was an ambiguous exception that occurred while handling your + request. + """ + + def __init__(self, *args, **kwargs): + """Initialize RequestException with `request` and `response` objects.""" + response = kwargs.pop('response', None) + self.response = response + self.request = kwargs.pop('request', None) + if (response is not None and not self.request and + hasattr(response, 'request')): + self.request = self.response.request + super(RequestException, self).__init__(*args, **kwargs) + + +class HTTPError(RequestException): + """An HTTP error occurred.""" + + +class ConnectionError(RequestException): + """A Connection error occurred.""" + + +class ProxyError(ConnectionError): + """A proxy error occurred.""" + + +class SSLError(ConnectionError): + """An SSL error occurred.""" + + +class Timeout(RequestException): + """The request timed out. + + Catching this error will catch both + :exc:`~requests.exceptions.ConnectTimeout` and + :exc:`~requests.exceptions.ReadTimeout` errors. + """ + + +class ConnectTimeout(ConnectionError, Timeout): + """The request timed out while trying to connect to the remote server. + + Requests that produced this error are safe to retry. + """ + + +class ReadTimeout(Timeout): + """The server did not send any data in the allotted amount of time.""" + + +class URLRequired(RequestException): + """A valid URL is required to make a request.""" + + +class TooManyRedirects(RequestException): + """Too many redirects.""" + + +class MissingSchema(RequestException, ValueError): + """The URL schema (e.g. http or https) is missing.""" + + +class InvalidSchema(RequestException, ValueError): + """See defaults.py for valid schemas.""" + + +class InvalidURL(RequestException, ValueError): + """The URL provided was somehow invalid.""" + + +class InvalidHeader(RequestException, ValueError): + """The header value provided was somehow invalid.""" + + +class InvalidProxyURL(InvalidURL): + """The proxy URL provided is invalid.""" + + +class ChunkedEncodingError(RequestException): + """The server declared chunked encoding but sent an invalid chunk.""" + + +class ContentDecodingError(RequestException, BaseHTTPError): + """Failed to decode response content""" + + +class StreamConsumedError(RequestException, TypeError): + """The content for this response was already consumed""" + + +class RetryError(RequestException): + """Custom retries logic failed""" + + +class UnrewindableBodyError(RequestException): + """Requests encountered an error when trying to rewind a body""" + +# Warnings + + +class RequestsWarning(Warning): + """Base warning for Requests.""" + pass + + +class FileModeWarning(RequestsWarning, DeprecationWarning): + """A file was opened in text mode, but Requests determined its binary length.""" + pass + + +class RequestsDependencyWarning(RequestsWarning): + """An imported dependency doesn't match the expected version range.""" + pass diff --git a/venv/Lib/site-packages/pip/_vendor/requests/help.py b/venv/Lib/site-packages/pip/_vendor/requests/help.py new file mode 100644 index 0000000..df1b4eb --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/help.py @@ -0,0 +1,120 @@ +"""Module containing bug report helper(s).""" +from __future__ import print_function + +import json +import platform +import sys +import ssl + +from pip._vendor import idna +from pip._vendor import urllib3 +from pip._vendor import chardet + +from . import __version__ as requests_version + +try: + from pip._vendor.urllib3.contrib import pyopenssl +except ImportError: + pyopenssl = None + OpenSSL = None + cryptography = None +else: + import OpenSSL + import cryptography + + +def _implementation(): + """Return a dict with the Python implementation and version. + + Provide both the name and the version of the Python implementation + currently running. For example, on CPython 2.7.5 it will return + {'name': 'CPython', 'version': '2.7.5'}. + + This function works best on CPython and PyPy: in particular, it probably + doesn't work for Jython or IronPython. Future investigation should be done + to work out the correct shape of the code for those platforms. + """ + implementation = platform.python_implementation() + + if implementation == 'CPython': + implementation_version = platform.python_version() + elif implementation == 'PyPy': + implementation_version = '%s.%s.%s' % (sys.pypy_version_info.major, + sys.pypy_version_info.minor, + sys.pypy_version_info.micro) + if sys.pypy_version_info.releaselevel != 'final': + implementation_version = ''.join([ + implementation_version, sys.pypy_version_info.releaselevel + ]) + elif implementation == 'Jython': + implementation_version = platform.python_version() # Complete Guess + elif implementation == 'IronPython': + implementation_version = platform.python_version() # Complete Guess + else: + implementation_version = 'Unknown' + + return {'name': implementation, 'version': implementation_version} + + +def info(): + """Generate information for a bug report.""" + try: + platform_info = { + 'system': platform.system(), + 'release': platform.release(), + } + except IOError: + platform_info = { + 'system': 'Unknown', + 'release': 'Unknown', + } + + implementation_info = _implementation() + urllib3_info = {'version': urllib3.__version__} + chardet_info = {'version': chardet.__version__} + + pyopenssl_info = { + 'version': None, + 'openssl_version': '', + } + if OpenSSL: + pyopenssl_info = { + 'version': OpenSSL.__version__, + 'openssl_version': '%x' % OpenSSL.SSL.OPENSSL_VERSION_NUMBER, + } + cryptography_info = { + 'version': getattr(cryptography, '__version__', ''), + } + idna_info = { + 'version': getattr(idna, '__version__', ''), + } + + # OPENSSL_VERSION_NUMBER doesn't exist in the Python 2.6 ssl module. + system_ssl = getattr(ssl, 'OPENSSL_VERSION_NUMBER', None) + system_ssl_info = { + 'version': '%x' % system_ssl if system_ssl is not None else '' + } + + return { + 'platform': platform_info, + 'implementation': implementation_info, + 'system_ssl': system_ssl_info, + 'using_pyopenssl': pyopenssl is not None, + 'pyOpenSSL': pyopenssl_info, + 'urllib3': urllib3_info, + 'chardet': chardet_info, + 'cryptography': cryptography_info, + 'idna': idna_info, + 'requests': { + 'version': requests_version, + }, + } + + +def main(): + """Pretty-print the bug information as JSON.""" + print(json.dumps(info(), sort_keys=True, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/pip/_vendor/requests/hooks.py b/venv/Lib/site-packages/pip/_vendor/requests/hooks.py new file mode 100644 index 0000000..32b32de --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/hooks.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +""" +requests.hooks +~~~~~~~~~~~~~~ + +This module provides the capabilities for the Requests hooks system. + +Available hooks: + +``response``: + The response generated from a Request. +""" +HOOKS = ['response'] + + +def default_hooks(): + return dict((event, []) for event in HOOKS) + +# TODO: response is the only one + + +def dispatch_hook(key, hooks, hook_data, **kwargs): + """Dispatches a hook dictionary on a given piece of data.""" + hooks = hooks or dict() + hooks = hooks.get(key) + if hooks: + if hasattr(hooks, '__call__'): + hooks = [hooks] + for hook in hooks: + _hook_data = hook(hook_data, **kwargs) + if _hook_data is not None: + hook_data = _hook_data + return hook_data diff --git a/venv/Lib/site-packages/pip/_vendor/requests/models.py b/venv/Lib/site-packages/pip/_vendor/requests/models.py new file mode 100644 index 0000000..4230535 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/models.py @@ -0,0 +1,952 @@ +# -*- coding: utf-8 -*- + +""" +requests.models +~~~~~~~~~~~~~~~ + +This module contains the primary objects that power Requests. +""" + +import datetime +import sys + +# Import encoding now, to avoid implicit import later. +# Implicit import within threads may cause LookupError when standard library is in a ZIP, +# such as in Embedded Python. See https://github.com/requests/requests/issues/3578. +import encodings.idna + +from pip._vendor.urllib3.fields import RequestField +from pip._vendor.urllib3.filepost import encode_multipart_formdata +from pip._vendor.urllib3.util import parse_url +from pip._vendor.urllib3.exceptions import ( + DecodeError, ReadTimeoutError, ProtocolError, LocationParseError) + +from io import UnsupportedOperation +from .hooks import default_hooks +from .structures import CaseInsensitiveDict + +from .auth import HTTPBasicAuth +from .cookies import cookiejar_from_dict, get_cookie_header, _copy_cookie_jar +from .exceptions import ( + HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError, + ContentDecodingError, ConnectionError, StreamConsumedError) +from ._internal_utils import to_native_string, unicode_is_ascii +from .utils import ( + guess_filename, get_auth_from_url, requote_uri, + stream_decode_response_unicode, to_key_val_list, parse_header_links, + iter_slices, guess_json_utf, super_len, check_header_validity) +from .compat import ( + Callable, Mapping, + cookielib, urlunparse, urlsplit, urlencode, str, bytes, + is_py2, chardet, builtin_str, basestring) +from .compat import json as complexjson +from .status_codes import codes + +#: The set of HTTP status codes that indicate an automatically +#: processable redirect. +REDIRECT_STATI = ( + codes.moved, # 301 + codes.found, # 302 + codes.other, # 303 + codes.temporary_redirect, # 307 + codes.permanent_redirect, # 308 +) + +DEFAULT_REDIRECT_LIMIT = 30 +CONTENT_CHUNK_SIZE = 10 * 1024 +ITER_CHUNK_SIZE = 512 + + +class RequestEncodingMixin(object): + @property + def path_url(self): + """Build the path URL to use.""" + + url = [] + + p = urlsplit(self.url) + + path = p.path + if not path: + path = '/' + + url.append(path) + + query = p.query + if query: + url.append('?') + url.append(query) + + return ''.join(url) + + @staticmethod + def _encode_params(data): + """Encode parameters in a piece of data. + + Will successfully encode parameters when passed as a dict or a list of + 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary + if parameters are supplied as a dict. + """ + + if isinstance(data, (str, bytes)): + return data + elif hasattr(data, 'read'): + return data + elif hasattr(data, '__iter__'): + result = [] + for k, vs in to_key_val_list(data): + if isinstance(vs, basestring) or not hasattr(vs, '__iter__'): + vs = [vs] + for v in vs: + if v is not None: + result.append( + (k.encode('utf-8') if isinstance(k, str) else k, + v.encode('utf-8') if isinstance(v, str) else v)) + return urlencode(result, doseq=True) + else: + return data + + @staticmethod + def _encode_files(files, data): + """Build the body for a multipart/form-data request. + + Will successfully encode files when passed as a dict or a list of + tuples. Order is retained if data is a list of tuples but arbitrary + if parameters are supplied as a dict. + The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype) + or 4-tuples (filename, fileobj, contentype, custom_headers). + """ + if (not files): + raise ValueError("Files must be provided.") + elif isinstance(data, basestring): + raise ValueError("Data must not be a string.") + + new_fields = [] + fields = to_key_val_list(data or {}) + files = to_key_val_list(files or {}) + + for field, val in fields: + if isinstance(val, basestring) or not hasattr(val, '__iter__'): + val = [val] + for v in val: + if v is not None: + # Don't call str() on bytestrings: in Py3 it all goes wrong. + if not isinstance(v, bytes): + v = str(v) + + new_fields.append( + (field.decode('utf-8') if isinstance(field, bytes) else field, + v.encode('utf-8') if isinstance(v, str) else v)) + + for (k, v) in files: + # support for explicit filename + ft = None + fh = None + if isinstance(v, (tuple, list)): + if len(v) == 2: + fn, fp = v + elif len(v) == 3: + fn, fp, ft = v + else: + fn, fp, ft, fh = v + else: + fn = guess_filename(v) or k + fp = v + + if isinstance(fp, (str, bytes, bytearray)): + fdata = fp + elif hasattr(fp, 'read'): + fdata = fp.read() + elif fp is None: + continue + else: + fdata = fp + + rf = RequestField(name=k, data=fdata, filename=fn, headers=fh) + rf.make_multipart(content_type=ft) + new_fields.append(rf) + + body, content_type = encode_multipart_formdata(new_fields) + + return body, content_type + + +class RequestHooksMixin(object): + def register_hook(self, event, hook): + """Properly register a hook.""" + + if event not in self.hooks: + raise ValueError('Unsupported event specified, with event name "%s"' % (event)) + + if isinstance(hook, Callable): + self.hooks[event].append(hook) + elif hasattr(hook, '__iter__'): + self.hooks[event].extend(h for h in hook if isinstance(h, Callable)) + + def deregister_hook(self, event, hook): + """Deregister a previously registered hook. + Returns True if the hook existed, False if not. + """ + + try: + self.hooks[event].remove(hook) + return True + except ValueError: + return False + + +class Request(RequestHooksMixin): + """A user-created :class:`Request <Request>` object. + + Used to prepare a :class:`PreparedRequest <PreparedRequest>`, which is sent to the server. + + :param method: HTTP method to use. + :param url: URL to send. + :param headers: dictionary of headers to send. + :param files: dictionary of {filename: fileobject} files to multipart upload. + :param data: the body to attach to the request. If a dictionary is provided, form-encoding will take place. + :param json: json for the body to attach to the request (if files or data is not specified). + :param params: dictionary of URL parameters to append to the URL. + :param auth: Auth handler or (user, pass) tuple. + :param cookies: dictionary or CookieJar of cookies to attach to this request. + :param hooks: dictionary of callback hooks, for internal usage. + + Usage:: + + >>> import requests + >>> req = requests.Request('GET', 'http://httpbin.org/get') + >>> req.prepare() + <PreparedRequest [GET]> + """ + + def __init__(self, + method=None, url=None, headers=None, files=None, data=None, + params=None, auth=None, cookies=None, hooks=None, json=None): + + # Default empty dicts for dict params. + data = [] if data is None else data + files = [] if files is None else files + headers = {} if headers is None else headers + params = {} if params is None else params + hooks = {} if hooks is None else hooks + + self.hooks = default_hooks() + for (k, v) in list(hooks.items()): + self.register_hook(event=k, hook=v) + + self.method = method + self.url = url + self.headers = headers + self.files = files + self.data = data + self.json = json + self.params = params + self.auth = auth + self.cookies = cookies + + def __repr__(self): + return '<Request [%s]>' % (self.method) + + def prepare(self): + """Constructs a :class:`PreparedRequest <PreparedRequest>` for transmission and returns it.""" + p = PreparedRequest() + p.prepare( + method=self.method, + url=self.url, + headers=self.headers, + files=self.files, + data=self.data, + json=self.json, + params=self.params, + auth=self.auth, + cookies=self.cookies, + hooks=self.hooks, + ) + return p + + +class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): + """The fully mutable :class:`PreparedRequest <PreparedRequest>` object, + containing the exact bytes that will be sent to the server. + + Generated from either a :class:`Request <Request>` object or manually. + + Usage:: + + >>> import requests + >>> req = requests.Request('GET', 'http://httpbin.org/get') + >>> r = req.prepare() + <PreparedRequest [GET]> + + >>> s = requests.Session() + >>> s.send(r) + <Response [200]> + """ + + def __init__(self): + #: HTTP verb to send to the server. + self.method = None + #: HTTP URL to send the request to. + self.url = None + #: dictionary of HTTP headers. + self.headers = None + # The `CookieJar` used to create the Cookie header will be stored here + # after prepare_cookies is called + self._cookies = None + #: request body to send to the server. + self.body = None + #: dictionary of callback hooks, for internal usage. + self.hooks = default_hooks() + #: integer denoting starting position of a readable file-like body. + self._body_position = None + + def prepare(self, + method=None, url=None, headers=None, files=None, data=None, + params=None, auth=None, cookies=None, hooks=None, json=None): + """Prepares the entire request with the given parameters.""" + + self.prepare_method(method) + self.prepare_url(url, params) + self.prepare_headers(headers) + self.prepare_cookies(cookies) + self.prepare_body(data, files, json) + self.prepare_auth(auth, url) + + # Note that prepare_auth must be last to enable authentication schemes + # such as OAuth to work on a fully prepared request. + + # This MUST go after prepare_auth. Authenticators could add a hook + self.prepare_hooks(hooks) + + def __repr__(self): + return '<PreparedRequest [%s]>' % (self.method) + + def copy(self): + p = PreparedRequest() + p.method = self.method + p.url = self.url + p.headers = self.headers.copy() if self.headers is not None else None + p._cookies = _copy_cookie_jar(self._cookies) + p.body = self.body + p.hooks = self.hooks + p._body_position = self._body_position + return p + + def prepare_method(self, method): + """Prepares the given HTTP method.""" + self.method = method + if self.method is not None: + self.method = to_native_string(self.method.upper()) + + @staticmethod + def _get_idna_encoded_host(host): + from pip._vendor import idna + + try: + host = idna.encode(host, uts46=True).decode('utf-8') + except idna.IDNAError: + raise UnicodeError + return host + + def prepare_url(self, url, params): + """Prepares the given HTTP URL.""" + #: Accept objects that have string representations. + #: We're unable to blindly call unicode/str functions + #: as this will include the bytestring indicator (b'') + #: on python 3.x. + #: https://github.com/requests/requests/pull/2238 + if isinstance(url, bytes): + url = url.decode('utf8') + else: + url = unicode(url) if is_py2 else str(url) + + # Remove leading whitespaces from url + url = url.lstrip() + + # Don't do any URL preparation for non-HTTP schemes like `mailto`, + # `data` etc to work around exceptions from `url_parse`, which + # handles RFC 3986 only. + if ':' in url and not url.lower().startswith('http'): + self.url = url + return + + # Support for unicode domain names and paths. + try: + scheme, auth, host, port, path, query, fragment = parse_url(url) + except LocationParseError as e: + raise InvalidURL(*e.args) + + if not scheme: + error = ("Invalid URL {0!r}: No schema supplied. Perhaps you meant http://{0}?") + error = error.format(to_native_string(url, 'utf8')) + + raise MissingSchema(error) + + if not host: + raise InvalidURL("Invalid URL %r: No host supplied" % url) + + # In general, we want to try IDNA encoding the hostname if the string contains + # non-ASCII characters. This allows users to automatically get the correct IDNA + # behaviour. For strings containing only ASCII characters, we need to also verify + # it doesn't start with a wildcard (*), before allowing the unencoded hostname. + if not unicode_is_ascii(host): + try: + host = self._get_idna_encoded_host(host) + except UnicodeError: + raise InvalidURL('URL has an invalid label.') + elif host.startswith(u'*'): + raise InvalidURL('URL has an invalid label.') + + # Carefully reconstruct the network location + netloc = auth or '' + if netloc: + netloc += '@' + netloc += host + if port: + netloc += ':' + str(port) + + # Bare domains aren't valid URLs. + if not path: + path = '/' + + if is_py2: + if isinstance(scheme, str): + scheme = scheme.encode('utf-8') + if isinstance(netloc, str): + netloc = netloc.encode('utf-8') + if isinstance(path, str): + path = path.encode('utf-8') + if isinstance(query, str): + query = query.encode('utf-8') + if isinstance(fragment, str): + fragment = fragment.encode('utf-8') + + if isinstance(params, (str, bytes)): + params = to_native_string(params) + + enc_params = self._encode_params(params) + if enc_params: + if query: + query = '%s&%s' % (query, enc_params) + else: + query = enc_params + + url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment])) + self.url = url + + def prepare_headers(self, headers): + """Prepares the given HTTP headers.""" + + self.headers = CaseInsensitiveDict() + if headers: + for header in headers.items(): + # Raise exception on invalid header value. + check_header_validity(header) + name, value = header + self.headers[to_native_string(name)] = value + + def prepare_body(self, data, files, json=None): + """Prepares the given HTTP body data.""" + + # Check if file, fo, generator, iterator. + # If not, run through normal process. + + # Nottin' on you. + body = None + content_type = None + + if not data and json is not None: + # urllib3 requires a bytes-like body. Python 2's json.dumps + # provides this natively, but Python 3 gives a Unicode string. + content_type = 'application/json' + body = complexjson.dumps(json) + if not isinstance(body, bytes): + body = body.encode('utf-8') + + is_stream = all([ + hasattr(data, '__iter__'), + not isinstance(data, (basestring, list, tuple, Mapping)) + ]) + + try: + length = super_len(data) + except (TypeError, AttributeError, UnsupportedOperation): + length = None + + if is_stream: + body = data + + if getattr(body, 'tell', None) is not None: + # Record the current file position before reading. + # This will allow us to rewind a file in the event + # of a redirect. + try: + self._body_position = body.tell() + except (IOError, OSError): + # This differentiates from None, allowing us to catch + # a failed `tell()` later when trying to rewind the body + self._body_position = object() + + if files: + raise NotImplementedError('Streamed bodies and files are mutually exclusive.') + + if length: + self.headers['Content-Length'] = builtin_str(length) + else: + self.headers['Transfer-Encoding'] = 'chunked' + else: + # Multi-part file uploads. + if files: + (body, content_type) = self._encode_files(files, data) + else: + if data: + body = self._encode_params(data) + if isinstance(data, basestring) or hasattr(data, 'read'): + content_type = None + else: + content_type = 'application/x-www-form-urlencoded' + + self.prepare_content_length(body) + + # Add content-type if it wasn't explicitly provided. + if content_type and ('content-type' not in self.headers): + self.headers['Content-Type'] = content_type + + self.body = body + + def prepare_content_length(self, body): + """Prepare Content-Length header based on request method and body""" + if body is not None: + length = super_len(body) + if length: + # If length exists, set it. Otherwise, we fallback + # to Transfer-Encoding: chunked. + self.headers['Content-Length'] = builtin_str(length) + elif self.method not in ('GET', 'HEAD') and self.headers.get('Content-Length') is None: + # Set Content-Length to 0 for methods that can have a body + # but don't provide one. (i.e. not GET or HEAD) + self.headers['Content-Length'] = '0' + + def prepare_auth(self, auth, url=''): + """Prepares the given HTTP auth data.""" + + # If no Auth is explicitly provided, extract it from the URL first. + if auth is None: + url_auth = get_auth_from_url(self.url) + auth = url_auth if any(url_auth) else None + + if auth: + if isinstance(auth, tuple) and len(auth) == 2: + # special-case basic HTTP auth + auth = HTTPBasicAuth(*auth) + + # Allow auth to make its changes. + r = auth(self) + + # Update self to reflect the auth changes. + self.__dict__.update(r.__dict__) + + # Recompute Content-Length + self.prepare_content_length(self.body) + + def prepare_cookies(self, cookies): + """Prepares the given HTTP cookie data. + + This function eventually generates a ``Cookie`` header from the + given cookies using cookielib. Due to cookielib's design, the header + will not be regenerated if it already exists, meaning this function + can only be called once for the life of the + :class:`PreparedRequest <PreparedRequest>` object. Any subsequent calls + to ``prepare_cookies`` will have no actual effect, unless the "Cookie" + header is removed beforehand. + """ + if isinstance(cookies, cookielib.CookieJar): + self._cookies = cookies + else: + self._cookies = cookiejar_from_dict(cookies) + + cookie_header = get_cookie_header(self._cookies, self) + if cookie_header is not None: + self.headers['Cookie'] = cookie_header + + def prepare_hooks(self, hooks): + """Prepares the given hooks.""" + # hooks can be passed as None to the prepare method and to this + # method. To prevent iterating over None, simply use an empty list + # if hooks is False-y + hooks = hooks or [] + for event in hooks: + self.register_hook(event, hooks[event]) + + +class Response(object): + """The :class:`Response <Response>` object, which contains a + server's response to an HTTP request. + """ + + __attrs__ = [ + '_content', 'status_code', 'headers', 'url', 'history', + 'encoding', 'reason', 'cookies', 'elapsed', 'request' + ] + + def __init__(self): + self._content = False + self._content_consumed = False + self._next = None + + #: Integer Code of responded HTTP Status, e.g. 404 or 200. + self.status_code = None + + #: Case-insensitive Dictionary of Response Headers. + #: For example, ``headers['content-encoding']`` will return the + #: value of a ``'Content-Encoding'`` response header. + self.headers = CaseInsensitiveDict() + + #: File-like object representation of response (for advanced usage). + #: Use of ``raw`` requires that ``stream=True`` be set on the request. + # This requirement does not apply for use internally to Requests. + self.raw = None + + #: Final URL location of Response. + self.url = None + + #: Encoding to decode with when accessing r.text. + self.encoding = None + + #: A list of :class:`Response <Response>` objects from + #: the history of the Request. Any redirect responses will end + #: up here. The list is sorted from the oldest to the most recent request. + self.history = [] + + #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK". + self.reason = None + + #: A CookieJar of Cookies the server sent back. + self.cookies = cookiejar_from_dict({}) + + #: The amount of time elapsed between sending the request + #: and the arrival of the response (as a timedelta). + #: This property specifically measures the time taken between sending + #: the first byte of the request and finishing parsing the headers. It + #: is therefore unaffected by consuming the response content or the + #: value of the ``stream`` keyword argument. + self.elapsed = datetime.timedelta(0) + + #: The :class:`PreparedRequest <PreparedRequest>` object to which this + #: is a response. + self.request = None + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def __getstate__(self): + # Consume everything; accessing the content attribute makes + # sure the content has been fully read. + if not self._content_consumed: + self.content + + return dict( + (attr, getattr(self, attr, None)) + for attr in self.__attrs__ + ) + + def __setstate__(self, state): + for name, value in state.items(): + setattr(self, name, value) + + # pickled objects do not have .raw + setattr(self, '_content_consumed', True) + setattr(self, 'raw', None) + + def __repr__(self): + return '<Response [%s]>' % (self.status_code) + + def __bool__(self): + """Returns True if :attr:`status_code` is less than 400. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code, is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + return self.ok + + def __nonzero__(self): + """Returns True if :attr:`status_code` is less than 400. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code, is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + return self.ok + + def __iter__(self): + """Allows you to use a response as an iterator.""" + return self.iter_content(128) + + @property + def ok(self): + """Returns True if :attr:`status_code` is less than 400, False if not. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + try: + self.raise_for_status() + except HTTPError: + return False + return True + + @property + def is_redirect(self): + """True if this Response is a well-formed HTTP redirect that could have + been processed automatically (by :meth:`Session.resolve_redirects`). + """ + return ('location' in self.headers and self.status_code in REDIRECT_STATI) + + @property + def is_permanent_redirect(self): + """True if this Response one of the permanent versions of redirect.""" + return ('location' in self.headers and self.status_code in (codes.moved_permanently, codes.permanent_redirect)) + + @property + def next(self): + """Returns a PreparedRequest for the next request in a redirect chain, if there is one.""" + return self._next + + @property + def apparent_encoding(self): + """The apparent encoding, provided by the chardet library.""" + return chardet.detect(self.content)['encoding'] + + def iter_content(self, chunk_size=1, decode_unicode=False): + """Iterates over the response data. When stream=True is set on the + request, this avoids reading the content at once into memory for + large responses. The chunk size is the number of bytes it should + read into memory. This is not necessarily the length of each item + returned as decoding can take place. + + chunk_size must be of type int or None. A value of None will + function differently depending on the value of `stream`. + stream=True will read data as it arrives in whatever size the + chunks are received. If stream=False, data is returned as + a single chunk. + + If decode_unicode is True, content will be decoded using the best + available encoding based on the response. + """ + + def generate(): + # Special case for urllib3. + if hasattr(self.raw, 'stream'): + try: + for chunk in self.raw.stream(chunk_size, decode_content=True): + yield chunk + except ProtocolError as e: + raise ChunkedEncodingError(e) + except DecodeError as e: + raise ContentDecodingError(e) + except ReadTimeoutError as e: + raise ConnectionError(e) + else: + # Standard file-like object. + while True: + chunk = self.raw.read(chunk_size) + if not chunk: + break + yield chunk + + self._content_consumed = True + + if self._content_consumed and isinstance(self._content, bool): + raise StreamConsumedError() + elif chunk_size is not None and not isinstance(chunk_size, int): + raise TypeError("chunk_size must be an int, it is instead a %s." % type(chunk_size)) + # simulate reading small chunks of the content + reused_chunks = iter_slices(self._content, chunk_size) + + stream_chunks = generate() + + chunks = reused_chunks if self._content_consumed else stream_chunks + + if decode_unicode: + chunks = stream_decode_response_unicode(chunks, self) + + return chunks + + def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None, delimiter=None): + """Iterates over the response data, one line at a time. When + stream=True is set on the request, this avoids reading the + content at once into memory for large responses. + + .. note:: This method is not reentrant safe. + """ + + pending = None + + for chunk in self.iter_content(chunk_size=chunk_size, decode_unicode=decode_unicode): + + if pending is not None: + chunk = pending + chunk + + if delimiter: + lines = chunk.split(delimiter) + else: + lines = chunk.splitlines() + + if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]: + pending = lines.pop() + else: + pending = None + + for line in lines: + yield line + + if pending is not None: + yield pending + + @property + def content(self): + """Content of the response, in bytes.""" + + if self._content is False: + # Read the contents. + if self._content_consumed: + raise RuntimeError( + 'The content for this response was already consumed') + + if self.status_code == 0 or self.raw is None: + self._content = None + else: + self._content = b''.join(self.iter_content(CONTENT_CHUNK_SIZE)) or b'' + + self._content_consumed = True + # don't need to release the connection; that's been handled by urllib3 + # since we exhausted the data. + return self._content + + @property + def text(self): + """Content of the response, in unicode. + + If Response.encoding is None, encoding will be guessed using + ``chardet``. + + The encoding of the response content is determined based solely on HTTP + headers, following RFC 2616 to the letter. If you can take advantage of + non-HTTP knowledge to make a better guess at the encoding, you should + set ``r.encoding`` appropriately before accessing this property. + """ + + # Try charset from content-type + content = None + encoding = self.encoding + + if not self.content: + return str('') + + # Fallback to auto-detected encoding. + if self.encoding is None: + encoding = self.apparent_encoding + + # Decode unicode from given encoding. + try: + content = str(self.content, encoding, errors='replace') + except (LookupError, TypeError): + # A LookupError is raised if the encoding was not found which could + # indicate a misspelling or similar mistake. + # + # A TypeError can be raised if encoding is None + # + # So we try blindly encoding. + content = str(self.content, errors='replace') + + return content + + def json(self, **kwargs): + r"""Returns the json-encoded content of a response, if any. + + :param \*\*kwargs: Optional arguments that ``json.loads`` takes. + :raises ValueError: If the response body does not contain valid json. + """ + + if not self.encoding and self.content and len(self.content) > 3: + # No encoding set. JSON RFC 4627 section 3 states we should expect + # UTF-8, -16 or -32. Detect which one to use; If the detection or + # decoding fails, fall back to `self.text` (using chardet to make + # a best guess). + encoding = guess_json_utf(self.content) + if encoding is not None: + try: + return complexjson.loads( + self.content.decode(encoding), **kwargs + ) + except UnicodeDecodeError: + # Wrong UTF codec detected; usually because it's not UTF-8 + # but some other 8-bit codec. This is an RFC violation, + # and the server didn't bother to tell us what codec *was* + # used. + pass + return complexjson.loads(self.text, **kwargs) + + @property + def links(self): + """Returns the parsed header links of the response, if any.""" + + header = self.headers.get('link') + + # l = MultiDict() + l = {} + + if header: + links = parse_header_links(header) + + for link in links: + key = link.get('rel') or link.get('url') + l[key] = link + + return l + + def raise_for_status(self): + """Raises stored :class:`HTTPError`, if one occurred.""" + + http_error_msg = '' + if isinstance(self.reason, bytes): + # We attempt to decode utf-8 first because some servers + # choose to localize their reason strings. If the string + # isn't utf-8, we fall back to iso-8859-1 for all other + # encodings. (See PR #3538) + try: + reason = self.reason.decode('utf-8') + except UnicodeDecodeError: + reason = self.reason.decode('iso-8859-1') + else: + reason = self.reason + + if 400 <= self.status_code < 500: + http_error_msg = u'%s Client Error: %s for url: %s' % (self.status_code, reason, self.url) + + elif 500 <= self.status_code < 600: + http_error_msg = u'%s Server Error: %s for url: %s' % (self.status_code, reason, self.url) + + if http_error_msg: + raise HTTPError(http_error_msg, response=self) + + def close(self): + """Releases the connection back to the pool. Once this method has been + called the underlying ``raw`` object must not be accessed again. + + *Note: Should not normally need to be called explicitly.* + """ + if not self._content_consumed: + self.raw.close() + + release_conn = getattr(self.raw, 'release_conn', None) + if release_conn is not None: + release_conn() diff --git a/venv/Lib/site-packages/pip/_vendor/requests/packages.py b/venv/Lib/site-packages/pip/_vendor/requests/packages.py new file mode 100644 index 0000000..9582fa7 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/packages.py @@ -0,0 +1,16 @@ +import sys + +# This code exists for backwards compatibility reasons. +# I don't like it either. Just look the other way. :) + +for package in ('urllib3', 'idna', 'chardet'): + vendored_package = "pip._vendor." + package + locals()[package] = __import__(vendored_package) + # This traversal is apparently necessary such that the identities are + # preserved (requests.packages.urllib3.* is urllib3.*) + for mod in list(sys.modules): + if mod == vendored_package or mod.startswith(vendored_package + '.'): + unprefixed_mod = mod[len("pip._vendor."):] + sys.modules['pip._vendor.requests.packages.' + unprefixed_mod] = sys.modules[mod] + +# Kinda cool, though, right? diff --git a/venv/Lib/site-packages/pip/_vendor/requests/sessions.py b/venv/Lib/site-packages/pip/_vendor/requests/sessions.py new file mode 100644 index 0000000..ba13526 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/sessions.py @@ -0,0 +1,741 @@ +# -*- coding: utf-8 -*- + +""" +requests.session +~~~~~~~~~~~~~~~~ + +This module provides a Session object to manage and persist settings across +requests (cookies, auth, proxies). +""" +import os +import sys +import time +from datetime import timedelta + +from .auth import _basic_auth_str +from .compat import cookielib, is_py3, OrderedDict, urljoin, urlparse, Mapping +from .cookies import ( + cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies) +from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT +from .hooks import default_hooks, dispatch_hook +from ._internal_utils import to_native_string +from .utils import to_key_val_list, default_headers +from .exceptions import ( + TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError) + +from .structures import CaseInsensitiveDict +from .adapters import HTTPAdapter + +from .utils import ( + requote_uri, get_environ_proxies, get_netrc_auth, should_bypass_proxies, + get_auth_from_url, rewind_body +) + +from .status_codes import codes + +# formerly defined here, reexposed here for backward compatibility +from .models import REDIRECT_STATI + +# Preferred clock, based on which one is more accurate on a given system. +if sys.platform == 'win32': + try: # Python 3.4+ + preferred_clock = time.perf_counter + except AttributeError: # Earlier than Python 3. + preferred_clock = time.clock +else: + preferred_clock = time.time + + +def merge_setting(request_setting, session_setting, dict_class=OrderedDict): + """Determines appropriate setting for a given request, taking into account + the explicit setting on that request, and the setting in the session. If a + setting is a dictionary, they will be merged together using `dict_class` + """ + + if session_setting is None: + return request_setting + + if request_setting is None: + return session_setting + + # Bypass if not a dictionary (e.g. verify) + if not ( + isinstance(session_setting, Mapping) and + isinstance(request_setting, Mapping) + ): + return request_setting + + merged_setting = dict_class(to_key_val_list(session_setting)) + merged_setting.update(to_key_val_list(request_setting)) + + # Remove keys that are set to None. Extract keys first to avoid altering + # the dictionary during iteration. + none_keys = [k for (k, v) in merged_setting.items() if v is None] + for key in none_keys: + del merged_setting[key] + + return merged_setting + + +def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict): + """Properly merges both requests and session hooks. + + This is necessary because when request_hooks == {'response': []}, the + merge breaks Session hooks entirely. + """ + if session_hooks is None or session_hooks.get('response') == []: + return request_hooks + + if request_hooks is None or request_hooks.get('response') == []: + return session_hooks + + return merge_setting(request_hooks, session_hooks, dict_class) + + +class SessionRedirectMixin(object): + + def get_redirect_target(self, resp): + """Receives a Response. Returns a redirect URI or ``None``""" + # Due to the nature of how requests processes redirects this method will + # be called at least once upon the original response and at least twice + # on each subsequent redirect response (if any). + # If a custom mixin is used to handle this logic, it may be advantageous + # to cache the redirect location onto the response object as a private + # attribute. + if resp.is_redirect: + location = resp.headers['location'] + # Currently the underlying http module on py3 decode headers + # in latin1, but empirical evidence suggests that latin1 is very + # rarely used with non-ASCII characters in HTTP headers. + # It is more likely to get UTF8 header rather than latin1. + # This causes incorrect handling of UTF8 encoded location headers. + # To solve this, we re-encode the location in latin1. + if is_py3: + location = location.encode('latin1') + return to_native_string(location, 'utf8') + return None + + def resolve_redirects(self, resp, req, stream=False, timeout=None, + verify=True, cert=None, proxies=None, yield_requests=False, **adapter_kwargs): + """Receives a Response. Returns a generator of Responses or Requests.""" + + hist = [] # keep track of history + + url = self.get_redirect_target(resp) + previous_fragment = urlparse(req.url).fragment + while url: + prepared_request = req.copy() + + # Update history and keep track of redirects. + # resp.history must ignore the original request in this loop + hist.append(resp) + resp.history = hist[1:] + + try: + resp.content # Consume socket so it can be released + except (ChunkedEncodingError, ContentDecodingError, RuntimeError): + resp.raw.read(decode_content=False) + + if len(resp.history) >= self.max_redirects: + raise TooManyRedirects('Exceeded %s redirects.' % self.max_redirects, response=resp) + + # Release the connection back into the pool. + resp.close() + + # Handle redirection without scheme (see: RFC 1808 Section 4) + if url.startswith('//'): + parsed_rurl = urlparse(resp.url) + url = '%s:%s' % (to_native_string(parsed_rurl.scheme), url) + + # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2) + parsed = urlparse(url) + if parsed.fragment == '' and previous_fragment: + parsed = parsed._replace(fragment=previous_fragment) + elif parsed.fragment: + previous_fragment = parsed.fragment + url = parsed.geturl() + + # Facilitate relative 'location' headers, as allowed by RFC 7231. + # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') + # Compliant with RFC3986, we percent encode the url. + if not parsed.netloc: + url = urljoin(resp.url, requote_uri(url)) + else: + url = requote_uri(url) + + prepared_request.url = to_native_string(url) + + self.rebuild_method(prepared_request, resp) + + # https://github.com/requests/requests/issues/1084 + if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect): + # https://github.com/requests/requests/issues/3490 + purged_headers = ('Content-Length', 'Content-Type', 'Transfer-Encoding') + for header in purged_headers: + prepared_request.headers.pop(header, None) + prepared_request.body = None + + headers = prepared_request.headers + try: + del headers['Cookie'] + except KeyError: + pass + + # Extract any cookies sent on the response to the cookiejar + # in the new request. Because we've mutated our copied prepared + # request, use the old one that we haven't yet touched. + extract_cookies_to_jar(prepared_request._cookies, req, resp.raw) + merge_cookies(prepared_request._cookies, self.cookies) + prepared_request.prepare_cookies(prepared_request._cookies) + + # Rebuild auth and proxy information. + proxies = self.rebuild_proxies(prepared_request, proxies) + self.rebuild_auth(prepared_request, resp) + + # A failed tell() sets `_body_position` to `object()`. This non-None + # value ensures `rewindable` will be True, allowing us to raise an + # UnrewindableBodyError, instead of hanging the connection. + rewindable = ( + prepared_request._body_position is not None and + ('Content-Length' in headers or 'Transfer-Encoding' in headers) + ) + + # Attempt to rewind consumed file-like object. + if rewindable: + rewind_body(prepared_request) + + # Override the original request. + req = prepared_request + + if yield_requests: + yield req + else: + + resp = self.send( + req, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + allow_redirects=False, + **adapter_kwargs + ) + + extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) + + # extract redirect url, if any, for the next loop + url = self.get_redirect_target(resp) + yield resp + + def rebuild_auth(self, prepared_request, response): + """When being redirected we may want to strip authentication from the + request to avoid leaking credentials. This method intelligently removes + and reapplies authentication where possible to avoid credential loss. + """ + headers = prepared_request.headers + url = prepared_request.url + + if 'Authorization' in headers: + # If we get redirected to a new host, we should strip out any + # authentication headers. + original_parsed = urlparse(response.request.url) + redirect_parsed = urlparse(url) + + if (original_parsed.hostname != redirect_parsed.hostname): + del headers['Authorization'] + + # .netrc might have more auth for us on our new host. + new_auth = get_netrc_auth(url) if self.trust_env else None + if new_auth is not None: + prepared_request.prepare_auth(new_auth) + + return + + def rebuild_proxies(self, prepared_request, proxies): + """This method re-evaluates the proxy configuration by considering the + environment variables. If we are redirected to a URL covered by + NO_PROXY, we strip the proxy configuration. Otherwise, we set missing + proxy keys for this URL (in case they were stripped by a previous + redirect). + + This method also replaces the Proxy-Authorization header where + necessary. + + :rtype: dict + """ + proxies = proxies if proxies is not None else {} + headers = prepared_request.headers + url = prepared_request.url + scheme = urlparse(url).scheme + new_proxies = proxies.copy() + no_proxy = proxies.get('no_proxy') + + bypass_proxy = should_bypass_proxies(url, no_proxy=no_proxy) + if self.trust_env and not bypass_proxy: + environ_proxies = get_environ_proxies(url, no_proxy=no_proxy) + + proxy = environ_proxies.get(scheme, environ_proxies.get('all')) + + if proxy: + new_proxies.setdefault(scheme, proxy) + + if 'Proxy-Authorization' in headers: + del headers['Proxy-Authorization'] + + try: + username, password = get_auth_from_url(new_proxies[scheme]) + except KeyError: + username, password = None, None + + if username and password: + headers['Proxy-Authorization'] = _basic_auth_str(username, password) + + return new_proxies + + def rebuild_method(self, prepared_request, response): + """When being redirected we may want to change the method of the request + based on certain specs or browser behavior. + """ + method = prepared_request.method + + # http://tools.ietf.org/html/rfc7231#section-6.4.4 + if response.status_code == codes.see_other and method != 'HEAD': + method = 'GET' + + # Do what the browsers do, despite standards... + # First, turn 302s into GETs. + if response.status_code == codes.found and method != 'HEAD': + method = 'GET' + + # Second, if a POST is responded to with a 301, turn it into a GET. + # This bizarre behaviour is explained in Issue 1704. + if response.status_code == codes.moved and method == 'POST': + method = 'GET' + + prepared_request.method = method + + +class Session(SessionRedirectMixin): + """A Requests session. + + Provides cookie persistence, connection-pooling, and configuration. + + Basic Usage:: + + >>> import requests + >>> s = requests.Session() + >>> s.get('http://httpbin.org/get') + <Response [200]> + + Or as a context manager:: + + >>> with requests.Session() as s: + >>> s.get('http://httpbin.org/get') + <Response [200]> + """ + + __attrs__ = [ + 'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify', + 'cert', 'prefetch', 'adapters', 'stream', 'trust_env', + 'max_redirects', + ] + + def __init__(self): + + #: A case-insensitive dictionary of headers to be sent on each + #: :class:`Request <Request>` sent from this + #: :class:`Session <Session>`. + self.headers = default_headers() + + #: Default Authentication tuple or object to attach to + #: :class:`Request <Request>`. + self.auth = None + + #: Dictionary mapping protocol or protocol and host to the URL of the proxy + #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to + #: be used on each :class:`Request <Request>`. + self.proxies = {} + + #: Event-handling hooks. + self.hooks = default_hooks() + + #: Dictionary of querystring data to attach to each + #: :class:`Request <Request>`. The dictionary values may be lists for + #: representing multivalued query parameters. + self.params = {} + + #: Stream response content default. + self.stream = False + + #: SSL Verification default. + self.verify = True + + #: SSL client certificate default, if String, path to ssl client + #: cert file (.pem). If Tuple, ('cert', 'key') pair. + self.cert = None + + #: Maximum number of redirects allowed. If the request exceeds this + #: limit, a :class:`TooManyRedirects` exception is raised. + #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is + #: 30. + self.max_redirects = DEFAULT_REDIRECT_LIMIT + + #: Trust environment settings for proxy configuration, default + #: authentication and similar. + self.trust_env = True + + #: A CookieJar containing all currently outstanding cookies set on this + #: session. By default it is a + #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but + #: may be any other ``cookielib.CookieJar`` compatible object. + self.cookies = cookiejar_from_dict({}) + + # Default connection adapters. + self.adapters = OrderedDict() + self.mount('https://', HTTPAdapter()) + self.mount('http://', HTTPAdapter()) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def prepare_request(self, request): + """Constructs a :class:`PreparedRequest <PreparedRequest>` for + transmission and returns it. The :class:`PreparedRequest` has settings + merged from the :class:`Request <Request>` instance and those of the + :class:`Session`. + + :param request: :class:`Request` instance to prepare with this + session's settings. + :rtype: requests.PreparedRequest + """ + cookies = request.cookies or {} + + # Bootstrap CookieJar. + if not isinstance(cookies, cookielib.CookieJar): + cookies = cookiejar_from_dict(cookies) + + # Merge with session cookies + merged_cookies = merge_cookies( + merge_cookies(RequestsCookieJar(), self.cookies), cookies) + + # Set environment's basic authentication if not explicitly set. + auth = request.auth + if self.trust_env and not auth and not self.auth: + auth = get_netrc_auth(request.url) + + p = PreparedRequest() + p.prepare( + method=request.method.upper(), + url=request.url, + files=request.files, + data=request.data, + json=request.json, + headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict), + params=merge_setting(request.params, self.params), + auth=merge_setting(auth, self.auth), + cookies=merged_cookies, + hooks=merge_hooks(request.hooks, self.hooks), + ) + return p + + def request(self, method, url, + params=None, data=None, headers=None, cookies=None, files=None, + auth=None, timeout=None, allow_redirects=True, proxies=None, + hooks=None, stream=None, verify=None, cert=None, json=None): + """Constructs a :class:`Request <Request>`, prepares it and sends it. + Returns :class:`Response <Response>` object. + + :param method: method for the new :class:`Request` object. + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary or bytes to be sent in the query + string for the :class:`Request`. + :param data: (optional) Dictionary, bytes, or file-like object to send + in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the + :class:`Request`. + :param headers: (optional) Dictionary of HTTP Headers to send with the + :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with the + :class:`Request`. + :param files: (optional) Dictionary of ``'filename': file-like-objects`` + for multipart encoding upload. + :param auth: (optional) Auth tuple or callable to enable + Basic/Digest/Custom HTTP Auth. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) <timeouts>` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Set to True by default. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol or protocol and + hostname to the URL of the proxy. + :param stream: (optional) whether to immediately download the response + content. Defaults to ``False``. + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use. Defaults to ``True``. + :param cert: (optional) if String, path to ssl client cert file (.pem). + If Tuple, ('cert', 'key') pair. + :rtype: requests.Response + """ + # Create the Request. + req = Request( + method=method.upper(), + url=url, + headers=headers, + files=files, + data=data or {}, + json=json, + params=params or {}, + auth=auth, + cookies=cookies, + hooks=hooks, + ) + prep = self.prepare_request(req) + + proxies = proxies or {} + + settings = self.merge_environment_settings( + prep.url, proxies, stream, verify, cert + ) + + # Send the request. + send_kwargs = { + 'timeout': timeout, + 'allow_redirects': allow_redirects, + } + send_kwargs.update(settings) + resp = self.send(prep, **send_kwargs) + + return resp + + def get(self, url, **kwargs): + r"""Sends a GET request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return self.request('GET', url, **kwargs) + + def options(self, url, **kwargs): + r"""Sends a OPTIONS request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return self.request('OPTIONS', url, **kwargs) + + def head(self, url, **kwargs): + r"""Sends a HEAD request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', False) + return self.request('HEAD', url, **kwargs) + + def post(self, url, data=None, json=None, **kwargs): + r"""Sends a POST request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('POST', url, data=data, json=json, **kwargs) + + def put(self, url, data=None, **kwargs): + r"""Sends a PUT request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('PUT', url, data=data, **kwargs) + + def patch(self, url, data=None, **kwargs): + r"""Sends a PATCH request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('PATCH', url, data=data, **kwargs) + + def delete(self, url, **kwargs): + r"""Sends a DELETE request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('DELETE', url, **kwargs) + + def send(self, request, **kwargs): + """Send a given PreparedRequest. + + :rtype: requests.Response + """ + # Set defaults that the hooks can utilize to ensure they always have + # the correct parameters to reproduce the previous request. + kwargs.setdefault('stream', self.stream) + kwargs.setdefault('verify', self.verify) + kwargs.setdefault('cert', self.cert) + kwargs.setdefault('proxies', self.proxies) + + # It's possible that users might accidentally send a Request object. + # Guard against that specific failure case. + if isinstance(request, Request): + raise ValueError('You can only send PreparedRequests.') + + # Set up variables needed for resolve_redirects and dispatching of hooks + allow_redirects = kwargs.pop('allow_redirects', True) + stream = kwargs.get('stream') + hooks = request.hooks + + # Get the appropriate adapter to use + adapter = self.get_adapter(url=request.url) + + # Start time (approximately) of the request + start = preferred_clock() + + # Send the request + r = adapter.send(request, **kwargs) + + # Total elapsed time of the request (approximately) + elapsed = preferred_clock() - start + r.elapsed = timedelta(seconds=elapsed) + + # Response manipulation hooks + r = dispatch_hook('response', hooks, r, **kwargs) + + # Persist cookies + if r.history: + + # If the hooks create history then we want those cookies too + for resp in r.history: + extract_cookies_to_jar(self.cookies, resp.request, resp.raw) + + extract_cookies_to_jar(self.cookies, request, r.raw) + + # Redirect resolving generator. + gen = self.resolve_redirects(r, request, **kwargs) + + # Resolve redirects if allowed. + history = [resp for resp in gen] if allow_redirects else [] + + # Shuffle things around if there's history. + if history: + # Insert the first (original) request at the start + history.insert(0, r) + # Get the last request made + r = history.pop() + r.history = history + + # If redirects aren't being followed, store the response on the Request for Response.next(). + if not allow_redirects: + try: + r._next = next(self.resolve_redirects(r, request, yield_requests=True, **kwargs)) + except StopIteration: + pass + + if not stream: + r.content + + return r + + def merge_environment_settings(self, url, proxies, stream, verify, cert): + """ + Check the environment and merge it with some settings. + + :rtype: dict + """ + # Gather clues from the surrounding environment. + if self.trust_env: + # Set environment's proxies. + no_proxy = proxies.get('no_proxy') if proxies is not None else None + env_proxies = get_environ_proxies(url, no_proxy=no_proxy) + for (k, v) in env_proxies.items(): + proxies.setdefault(k, v) + + # Look for requests environment configuration and be compatible + # with cURL. + if verify is True or verify is None: + verify = (os.environ.get('REQUESTS_CA_BUNDLE') or + os.environ.get('CURL_CA_BUNDLE')) + + # Merge all the kwargs. + proxies = merge_setting(proxies, self.proxies) + stream = merge_setting(stream, self.stream) + verify = merge_setting(verify, self.verify) + cert = merge_setting(cert, self.cert) + + return {'verify': verify, 'proxies': proxies, 'stream': stream, + 'cert': cert} + + def get_adapter(self, url): + """ + Returns the appropriate connection adapter for the given URL. + + :rtype: requests.adapters.BaseAdapter + """ + for (prefix, adapter) in self.adapters.items(): + + if url.lower().startswith(prefix.lower()): + return adapter + + # Nothing matches :-/ + raise InvalidSchema("No connection adapters were found for '%s'" % url) + + def close(self): + """Closes all adapters and as such the session""" + for v in self.adapters.values(): + v.close() + + def mount(self, prefix, adapter): + """Registers a connection adapter to a prefix. + + Adapters are sorted in descending order by prefix length. + """ + self.adapters[prefix] = adapter + keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] + + for key in keys_to_move: + self.adapters[key] = self.adapters.pop(key) + + def __getstate__(self): + state = dict((attr, getattr(self, attr, None)) for attr in self.__attrs__) + return state + + def __setstate__(self, state): + for attr, value in state.items(): + setattr(self, attr, value) + + +def session(): + """ + Returns a :class:`Session` for context-management. + + :rtype: Session + """ + + return Session() diff --git a/venv/Lib/site-packages/pip/_vendor/requests/status_codes.py b/venv/Lib/site-packages/pip/_vendor/requests/status_codes.py new file mode 100644 index 0000000..ff462c6 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/status_codes.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- + +""" +The ``codes`` object defines a mapping from common names for HTTP statuses +to their numerical codes, accessible either as attributes or as dictionary +items. + +>>> requests.codes['temporary_redirect'] +307 +>>> requests.codes.teapot +418 +>>> requests.codes['\o/'] +200 + +Some codes have multiple names, and both upper- and lower-case versions of +the names are allowed. For example, ``codes.ok``, ``codes.OK``, and +``codes.okay`` all correspond to the HTTP status code 200. +""" + +from .structures import LookupDict + +_codes = { + + # Informational. + 100: ('continue',), + 101: ('switching_protocols',), + 102: ('processing',), + 103: ('checkpoint',), + 122: ('uri_too_long', 'request_uri_too_long'), + 200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '✓'), + 201: ('created',), + 202: ('accepted',), + 203: ('non_authoritative_info', 'non_authoritative_information'), + 204: ('no_content',), + 205: ('reset_content', 'reset'), + 206: ('partial_content', 'partial'), + 207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'), + 208: ('already_reported',), + 226: ('im_used',), + + # Redirection. + 300: ('multiple_choices',), + 301: ('moved_permanently', 'moved', '\\o-'), + 302: ('found',), + 303: ('see_other', 'other'), + 304: ('not_modified',), + 305: ('use_proxy',), + 306: ('switch_proxy',), + 307: ('temporary_redirect', 'temporary_moved', 'temporary'), + 308: ('permanent_redirect', + 'resume_incomplete', 'resume',), # These 2 to be removed in 3.0 + + # Client Error. + 400: ('bad_request', 'bad'), + 401: ('unauthorized',), + 402: ('payment_required', 'payment'), + 403: ('forbidden',), + 404: ('not_found', '-o-'), + 405: ('method_not_allowed', 'not_allowed'), + 406: ('not_acceptable',), + 407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'), + 408: ('request_timeout', 'timeout'), + 409: ('conflict',), + 410: ('gone',), + 411: ('length_required',), + 412: ('precondition_failed', 'precondition'), + 413: ('request_entity_too_large',), + 414: ('request_uri_too_large',), + 415: ('unsupported_media_type', 'unsupported_media', 'media_type'), + 416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'), + 417: ('expectation_failed',), + 418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'), + 421: ('misdirected_request',), + 422: ('unprocessable_entity', 'unprocessable'), + 423: ('locked',), + 424: ('failed_dependency', 'dependency'), + 425: ('unordered_collection', 'unordered'), + 426: ('upgrade_required', 'upgrade'), + 428: ('precondition_required', 'precondition'), + 429: ('too_many_requests', 'too_many'), + 431: ('header_fields_too_large', 'fields_too_large'), + 444: ('no_response', 'none'), + 449: ('retry_with', 'retry'), + 450: ('blocked_by_windows_parental_controls', 'parental_controls'), + 451: ('unavailable_for_legal_reasons', 'legal_reasons'), + 499: ('client_closed_request',), + + # Server Error. + 500: ('internal_server_error', 'server_error', '/o\\', '✗'), + 501: ('not_implemented',), + 502: ('bad_gateway',), + 503: ('service_unavailable', 'unavailable'), + 504: ('gateway_timeout',), + 505: ('http_version_not_supported', 'http_version'), + 506: ('variant_also_negotiates',), + 507: ('insufficient_storage',), + 509: ('bandwidth_limit_exceeded', 'bandwidth'), + 510: ('not_extended',), + 511: ('network_authentication_required', 'network_auth', 'network_authentication'), +} + +codes = LookupDict(name='status_codes') + +def _init(): + for code, titles in _codes.items(): + for title in titles: + setattr(codes, title, code) + if not title.startswith(('\\', '/')): + setattr(codes, title.upper(), code) + + def doc(code): + names = ', '.join('``%s``' % n for n in _codes[code]) + return '* %d: %s' % (code, names) + + global __doc__ + __doc__ = (__doc__ + '\n' + + '\n'.join(doc(code) for code in sorted(_codes)) + if __doc__ is not None else None) + +_init() diff --git a/venv/Lib/site-packages/pip/_vendor/requests/structures.py b/venv/Lib/site-packages/pip/_vendor/requests/structures.py new file mode 100644 index 0000000..da930e2 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/structures.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- + +""" +requests.structures +~~~~~~~~~~~~~~~~~~~ + +Data structures that power Requests. +""" + +from .compat import OrderedDict, Mapping, MutableMapping + + +class CaseInsensitiveDict(MutableMapping): + """A case-insensitive ``dict``-like object. + + Implements all methods and operations of + ``MutableMapping`` as well as dict's ``copy``. Also + provides ``lower_items``. + + All keys are expected to be strings. The structure remembers the + case of the last key to be set, and ``iter(instance)``, + ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()`` + will contain case-sensitive keys. However, querying and contains + testing is case insensitive:: + + cid = CaseInsensitiveDict() + cid['Accept'] = 'application/json' + cid['aCCEPT'] == 'application/json' # True + list(cid) == ['Accept'] # True + + For example, ``headers['content-encoding']`` will return the + value of a ``'Content-Encoding'`` response header, regardless + of how the header name was originally stored. + + If the constructor, ``.update``, or equality comparison + operations are given keys that have equal ``.lower()``s, the + behavior is undefined. + """ + + def __init__(self, data=None, **kwargs): + self._store = OrderedDict() + if data is None: + data = {} + self.update(data, **kwargs) + + def __setitem__(self, key, value): + # Use the lowercased key for lookups, but store the actual + # key alongside the value. + self._store[key.lower()] = (key, value) + + def __getitem__(self, key): + return self._store[key.lower()][1] + + def __delitem__(self, key): + del self._store[key.lower()] + + def __iter__(self): + return (casedkey for casedkey, mappedvalue in self._store.values()) + + def __len__(self): + return len(self._store) + + def lower_items(self): + """Like iteritems(), but with all lowercase keys.""" + return ( + (lowerkey, keyval[1]) + for (lowerkey, keyval) + in self._store.items() + ) + + def __eq__(self, other): + if isinstance(other, Mapping): + other = CaseInsensitiveDict(other) + else: + return NotImplemented + # Compare insensitively + return dict(self.lower_items()) == dict(other.lower_items()) + + # Copy is required + def copy(self): + return CaseInsensitiveDict(self._store.values()) + + def __repr__(self): + return str(dict(self.items())) + + +class LookupDict(dict): + """Dictionary lookup object.""" + + def __init__(self, name=None): + self.name = name + super(LookupDict, self).__init__() + + def __repr__(self): + return '<lookup \'%s\'>' % (self.name) + + def __getitem__(self, key): + # We allow fall-through here, so values default to None + + return self.__dict__.get(key, None) + + def get(self, key, default=None): + return self.__dict__.get(key, default) diff --git a/venv/Lib/site-packages/pip/_vendor/requests/utils.py b/venv/Lib/site-packages/pip/_vendor/requests/utils.py new file mode 100644 index 0000000..431f6be --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/requests/utils.py @@ -0,0 +1,976 @@ +# -*- coding: utf-8 -*- + +""" +requests.utils +~~~~~~~~~~~~~~ + +This module provides utility functions that are used within Requests +that are also useful for external consumption. +""" + +import codecs +import contextlib +import io +import os +import re +import socket +import struct +import sys +import tempfile +import warnings +import zipfile + +from .__version__ import __version__ +from . import certs +# to_native_string is unused here, but imported here for backwards compatibility +from ._internal_utils import to_native_string +from .compat import parse_http_list as _parse_list_header +from .compat import ( + quote, urlparse, bytes, str, OrderedDict, unquote, getproxies, + proxy_bypass, urlunparse, basestring, integer_types, is_py3, + proxy_bypass_environment, getproxies_environment, Mapping) +from .cookies import cookiejar_from_dict +from .structures import CaseInsensitiveDict +from .exceptions import ( + InvalidURL, InvalidHeader, FileModeWarning, UnrewindableBodyError) + +NETRC_FILES = ('.netrc', '_netrc') + +DEFAULT_CA_BUNDLE_PATH = certs.where() + + +if sys.platform == 'win32': + # provide a proxy_bypass version on Windows without DNS lookups + + def proxy_bypass_registry(host): + try: + if is_py3: + import winreg + else: + import _winreg as winreg + except ImportError: + return False + + try: + internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER, + r'Software\Microsoft\Windows\CurrentVersion\Internet Settings') + # ProxyEnable could be REG_SZ or REG_DWORD, normalizing it + proxyEnable = int(winreg.QueryValueEx(internetSettings, + 'ProxyEnable')[0]) + # ProxyOverride is almost always a string + proxyOverride = winreg.QueryValueEx(internetSettings, + 'ProxyOverride')[0] + except OSError: + return False + if not proxyEnable or not proxyOverride: + return False + + # make a check value list from the registry entry: replace the + # '<local>' string by the localhost entry and the corresponding + # canonical entry. + proxyOverride = proxyOverride.split(';') + # now check if we match one of the registry values. + for test in proxyOverride: + if test == '<local>': + if '.' not in host: + return True + test = test.replace(".", r"\.") # mask dots + test = test.replace("*", r".*") # change glob sequence + test = test.replace("?", r".") # change glob char + if re.match(test, host, re.I): + return True + return False + + def proxy_bypass(host): # noqa + """Return True, if the host should be bypassed. + + Checks proxy settings gathered from the environment, if specified, + or the registry. + """ + if getproxies_environment(): + return proxy_bypass_environment(host) + else: + return proxy_bypass_registry(host) + + +def dict_to_sequence(d): + """Returns an internal sequence dictionary update.""" + + if hasattr(d, 'items'): + d = d.items() + + return d + + +def super_len(o): + total_length = None + current_position = 0 + + if hasattr(o, '__len__'): + total_length = len(o) + + elif hasattr(o, 'len'): + total_length = o.len + + elif hasattr(o, 'fileno'): + try: + fileno = o.fileno() + except io.UnsupportedOperation: + pass + else: + total_length = os.fstat(fileno).st_size + + # Having used fstat to determine the file length, we need to + # confirm that this file was opened up in binary mode. + if 'b' not in o.mode: + warnings.warn(( + "Requests has determined the content-length for this " + "request using the binary size of the file: however, the " + "file has been opened in text mode (i.e. without the 'b' " + "flag in the mode). This may lead to an incorrect " + "content-length. In Requests 3.0, support will be removed " + "for files in text mode."), + FileModeWarning + ) + + if hasattr(o, 'tell'): + try: + current_position = o.tell() + except (OSError, IOError): + # This can happen in some weird situations, such as when the file + # is actually a special file descriptor like stdin. In this + # instance, we don't know what the length is, so set it to zero and + # let requests chunk it instead. + if total_length is not None: + current_position = total_length + else: + if hasattr(o, 'seek') and total_length is None: + # StringIO and BytesIO have seek but no useable fileno + try: + # seek to end of file + o.seek(0, 2) + total_length = o.tell() + + # seek back to current position to support + # partially read file-like objects + o.seek(current_position or 0) + except (OSError, IOError): + total_length = 0 + + if total_length is None: + total_length = 0 + + return max(0, total_length - current_position) + + +def get_netrc_auth(url, raise_errors=False): + """Returns the Requests tuple auth for a given url from netrc.""" + + try: + from netrc import netrc, NetrcParseError + + netrc_path = None + + for f in NETRC_FILES: + try: + loc = os.path.expanduser('~/{0}'.format(f)) + except KeyError: + # os.path.expanduser can fail when $HOME is undefined and + # getpwuid fails. See http://bugs.python.org/issue20164 & + # https://github.com/requests/requests/issues/1846 + return + + if os.path.exists(loc): + netrc_path = loc + break + + # Abort early if there isn't one. + if netrc_path is None: + return + + ri = urlparse(url) + + # Strip port numbers from netloc. This weird `if...encode`` dance is + # used for Python 3.2, which doesn't support unicode literals. + splitstr = b':' + if isinstance(url, str): + splitstr = splitstr.decode('ascii') + host = ri.netloc.split(splitstr)[0] + + try: + _netrc = netrc(netrc_path).authenticators(host) + if _netrc: + # Return with login / password + login_i = (0 if _netrc[0] else 1) + return (_netrc[login_i], _netrc[2]) + except (NetrcParseError, IOError): + # If there was a parsing error or a permissions issue reading the file, + # we'll just skip netrc auth unless explicitly asked to raise errors. + if raise_errors: + raise + + # AppEngine hackiness. + except (ImportError, AttributeError): + pass + + +def guess_filename(obj): + """Tries to guess the filename of the given object.""" + name = getattr(obj, 'name', None) + if (name and isinstance(name, basestring) and name[0] != '<' and + name[-1] != '>'): + return os.path.basename(name) + + +def extract_zipped_paths(path): + """Replace nonexistent paths that look like they refer to a member of a zip + archive with the location of an extracted copy of the target, or else + just return the provided path unchanged. + """ + if os.path.exists(path): + # this is already a valid path, no need to do anything further + return path + + # find the first valid part of the provided path and treat that as a zip archive + # assume the rest of the path is the name of a member in the archive + archive, member = os.path.split(path) + while archive and not os.path.exists(archive): + archive, prefix = os.path.split(archive) + member = '/'.join([prefix, member]) + + if not zipfile.is_zipfile(archive): + return path + + zip_file = zipfile.ZipFile(archive) + if member not in zip_file.namelist(): + return path + + # we have a valid zip archive and a valid member of that archive + tmp = tempfile.gettempdir() + extracted_path = os.path.join(tmp, *member.split('/')) + if not os.path.exists(extracted_path): + extracted_path = zip_file.extract(member, path=tmp) + + return extracted_path + + +def from_key_val_list(value): + """Take an object and test to see if it can be represented as a + dictionary. Unless it can not be represented as such, return an + OrderedDict, e.g., + + :: + + >>> from_key_val_list([('key', 'val')]) + OrderedDict([('key', 'val')]) + >>> from_key_val_list('string') + ValueError: need more than 1 value to unpack + >>> from_key_val_list({'key': 'val'}) + OrderedDict([('key', 'val')]) + + :rtype: OrderedDict + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError('cannot encode objects that are not 2-tuples') + + return OrderedDict(value) + + +def to_key_val_list(value): + """Take an object and test to see if it can be represented as a + dictionary. If it can be, return a list of tuples, e.g., + + :: + + >>> to_key_val_list([('key', 'val')]) + [('key', 'val')] + >>> to_key_val_list({'key': 'val'}) + [('key', 'val')] + >>> to_key_val_list('string') + ValueError: cannot encode objects that are not 2-tuples. + + :rtype: list + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError('cannot encode objects that are not 2-tuples') + + if isinstance(value, Mapping): + value = value.items() + + return list(value) + + +# From mitsuhiko/werkzeug (used with permission). +def parse_list_header(value): + """Parse lists as described by RFC 2068 Section 2. + + In particular, parse comma-separated lists where the elements of + the list may include quoted-strings. A quoted-string could + contain a comma. A non-quoted string could have quotes in the + middle. Quotes are removed automatically after parsing. + + It basically works like :func:`parse_set_header` just that items + may appear multiple times and case sensitivity is preserved. + + The return value is a standard :class:`list`: + + >>> parse_list_header('token, "quoted value"') + ['token', 'quoted value'] + + To create a header from the :class:`list` again, use the + :func:`dump_header` function. + + :param value: a string with a list header. + :return: :class:`list` + :rtype: list + """ + result = [] + for item in _parse_list_header(value): + if item[:1] == item[-1:] == '"': + item = unquote_header_value(item[1:-1]) + result.append(item) + return result + + +# From mitsuhiko/werkzeug (used with permission). +def parse_dict_header(value): + """Parse lists of key, value pairs as described by RFC 2068 Section 2 and + convert them into a python dict: + + >>> d = parse_dict_header('foo="is a fish", bar="as well"') + >>> type(d) is dict + True + >>> sorted(d.items()) + [('bar', 'as well'), ('foo', 'is a fish')] + + If there is no value for a key it will be `None`: + + >>> parse_dict_header('key_without_value') + {'key_without_value': None} + + To create a header from the :class:`dict` again, use the + :func:`dump_header` function. + + :param value: a string with a dict header. + :return: :class:`dict` + :rtype: dict + """ + result = {} + for item in _parse_list_header(value): + if '=' not in item: + result[item] = None + continue + name, value = item.split('=', 1) + if value[:1] == value[-1:] == '"': + value = unquote_header_value(value[1:-1]) + result[name] = value + return result + + +# From mitsuhiko/werkzeug (used with permission). +def unquote_header_value(value, is_filename=False): + r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). + This does not use the real unquoting but what browsers are actually + using for quoting. + + :param value: the header value to unquote. + :rtype: str + """ + if value and value[0] == value[-1] == '"': + # this is not the real unquoting, but fixing this so that the + # RFC is met will result in bugs with internet explorer and + # probably some other browsers as well. IE for example is + # uploading files with "C:\foo\bar.txt" as filename + value = value[1:-1] + + # if this is a filename and the starting characters look like + # a UNC path, then just return the value without quotes. Using the + # replace sequence below on a UNC path has the effect of turning + # the leading double slash into a single slash and then + # _fix_ie_filename() doesn't work correctly. See #458. + if not is_filename or value[:2] != '\\\\': + return value.replace('\\\\', '\\').replace('\\"', '"') + return value + + +def dict_from_cookiejar(cj): + """Returns a key/value dictionary from a CookieJar. + + :param cj: CookieJar object to extract cookies from. + :rtype: dict + """ + + cookie_dict = {} + + for cookie in cj: + cookie_dict[cookie.name] = cookie.value + + return cookie_dict + + +def add_dict_to_cookiejar(cj, cookie_dict): + """Returns a CookieJar from a key/value dictionary. + + :param cj: CookieJar to insert cookies into. + :param cookie_dict: Dict of key/values to insert into CookieJar. + :rtype: CookieJar + """ + + return cookiejar_from_dict(cookie_dict, cj) + + +def get_encodings_from_content(content): + """Returns encodings from given content string. + + :param content: bytestring to extract encodings from. + """ + warnings.warn(( + 'In requests 3.0, get_encodings_from_content will be removed. For ' + 'more information, please see the discussion on issue #2266. (This' + ' warning should only appear once.)'), + DeprecationWarning) + + charset_re = re.compile(r'<meta.*?charset=["\']*(.+?)["\'>]', flags=re.I) + pragma_re = re.compile(r'<meta.*?content=["\']*;?charset=(.+?)["\'>]', flags=re.I) + xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]') + + return (charset_re.findall(content) + + pragma_re.findall(content) + + xml_re.findall(content)) + + +def _parse_content_type_header(header): + """Returns content type and parameters from given header + + :param header: string + :return: tuple containing content type and dictionary of + parameters + """ + + tokens = header.split(';') + content_type, params = tokens[0].strip(), tokens[1:] + params_dict = {} + items_to_strip = "\"' " + + for param in params: + param = param.strip() + if param: + key, value = param, True + index_of_equals = param.find("=") + if index_of_equals != -1: + key = param[:index_of_equals].strip(items_to_strip) + value = param[index_of_equals + 1:].strip(items_to_strip) + params_dict[key] = value + return content_type, params_dict + + +def get_encoding_from_headers(headers): + """Returns encodings from given HTTP Header Dict. + + :param headers: dictionary to extract encoding from. + :rtype: str + """ + + content_type = headers.get('content-type') + + if not content_type: + return None + + content_type, params = _parse_content_type_header(content_type) + + if 'charset' in params: + return params['charset'].strip("'\"") + + if 'text' in content_type: + return 'ISO-8859-1' + + +def stream_decode_response_unicode(iterator, r): + """Stream decodes a iterator.""" + + if r.encoding is None: + for item in iterator: + yield item + return + + decoder = codecs.getincrementaldecoder(r.encoding)(errors='replace') + for chunk in iterator: + rv = decoder.decode(chunk) + if rv: + yield rv + rv = decoder.decode(b'', final=True) + if rv: + yield rv + + +def iter_slices(string, slice_length): + """Iterate over slices of a string.""" + pos = 0 + if slice_length is None or slice_length <= 0: + slice_length = len(string) + while pos < len(string): + yield string[pos:pos + slice_length] + pos += slice_length + + +def get_unicode_from_response(r): + """Returns the requested content back in unicode. + + :param r: Response object to get unicode content from. + + Tried: + + 1. charset from content-type + 2. fall back and replace all unicode characters + + :rtype: str + """ + warnings.warn(( + 'In requests 3.0, get_unicode_from_response will be removed. For ' + 'more information, please see the discussion on issue #2266. (This' + ' warning should only appear once.)'), + DeprecationWarning) + + tried_encodings = [] + + # Try charset from content-type + encoding = get_encoding_from_headers(r.headers) + + if encoding: + try: + return str(r.content, encoding) + except UnicodeError: + tried_encodings.append(encoding) + + # Fall back: + try: + return str(r.content, encoding, errors='replace') + except TypeError: + return r.content + + +# The unreserved URI characters (RFC 3986) +UNRESERVED_SET = frozenset( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789-._~") + + +def unquote_unreserved(uri): + """Un-escape any percent-escape sequences in a URI that are unreserved + characters. This leaves all reserved, illegal and non-ASCII bytes encoded. + + :rtype: str + """ + parts = uri.split('%') + for i in range(1, len(parts)): + h = parts[i][0:2] + if len(h) == 2 and h.isalnum(): + try: + c = chr(int(h, 16)) + except ValueError: + raise InvalidURL("Invalid percent-escape sequence: '%s'" % h) + + if c in UNRESERVED_SET: + parts[i] = c + parts[i][2:] + else: + parts[i] = '%' + parts[i] + else: + parts[i] = '%' + parts[i] + return ''.join(parts) + + +def requote_uri(uri): + """Re-quote the given URI. + + This function passes the given URI through an unquote/quote cycle to + ensure that it is fully and consistently quoted. + + :rtype: str + """ + safe_with_percent = "!#$%&'()*+,/:;=?@[]~" + safe_without_percent = "!#$&'()*+,/:;=?@[]~" + try: + # Unquote only the unreserved characters + # Then quote only illegal characters (do not quote reserved, + # unreserved, or '%') + return quote(unquote_unreserved(uri), safe=safe_with_percent) + except InvalidURL: + # We couldn't unquote the given URI, so let's try quoting it, but + # there may be unquoted '%'s in the URI. We need to make sure they're + # properly quoted so they do not cause issues elsewhere. + return quote(uri, safe=safe_without_percent) + + +def address_in_network(ip, net): + """This function allows you to check if an IP belongs to a network subnet + + Example: returns True if ip = 192.168.1.1 and net = 192.168.1.0/24 + returns False if ip = 192.168.1.1 and net = 192.168.100.0/24 + + :rtype: bool + """ + ipaddr = struct.unpack('=L', socket.inet_aton(ip))[0] + netaddr, bits = net.split('/') + netmask = struct.unpack('=L', socket.inet_aton(dotted_netmask(int(bits))))[0] + network = struct.unpack('=L', socket.inet_aton(netaddr))[0] & netmask + return (ipaddr & netmask) == (network & netmask) + + +def dotted_netmask(mask): + """Converts mask from /xx format to xxx.xxx.xxx.xxx + + Example: if mask is 24 function returns 255.255.255.0 + + :rtype: str + """ + bits = 0xffffffff ^ (1 << 32 - mask) - 1 + return socket.inet_ntoa(struct.pack('>I', bits)) + + +def is_ipv4_address(string_ip): + """ + :rtype: bool + """ + try: + socket.inet_aton(string_ip) + except socket.error: + return False + return True + + +def is_valid_cidr(string_network): + """ + Very simple check of the cidr format in no_proxy variable. + + :rtype: bool + """ + if string_network.count('/') == 1: + try: + mask = int(string_network.split('/')[1]) + except ValueError: + return False + + if mask < 1 or mask > 32: + return False + + try: + socket.inet_aton(string_network.split('/')[0]) + except socket.error: + return False + else: + return False + return True + + +@contextlib.contextmanager +def set_environ(env_name, value): + """Set the environment variable 'env_name' to 'value' + + Save previous value, yield, and then restore the previous value stored in + the environment variable 'env_name'. + + If 'value' is None, do nothing""" + value_changed = value is not None + if value_changed: + old_value = os.environ.get(env_name) + os.environ[env_name] = value + try: + yield + finally: + if value_changed: + if old_value is None: + del os.environ[env_name] + else: + os.environ[env_name] = old_value + + +def should_bypass_proxies(url, no_proxy): + """ + Returns whether we should bypass proxies or not. + + :rtype: bool + """ + # Prioritize lowercase environment variables over uppercase + # to keep a consistent behaviour with other http projects (curl, wget). + get_proxy = lambda k: os.environ.get(k) or os.environ.get(k.upper()) + + # First check whether no_proxy is defined. If it is, check that the URL + # we're getting isn't in the no_proxy list. + no_proxy_arg = no_proxy + if no_proxy is None: + no_proxy = get_proxy('no_proxy') + parsed = urlparse(url) + + if no_proxy: + # We need to check whether we match here. We need to see if we match + # the end of the hostname, both with and without the port. + no_proxy = ( + host for host in no_proxy.replace(' ', '').split(',') if host + ) + + if is_ipv4_address(parsed.hostname): + for proxy_ip in no_proxy: + if is_valid_cidr(proxy_ip): + if address_in_network(parsed.hostname, proxy_ip): + return True + elif parsed.hostname == proxy_ip: + # If no_proxy ip was defined in plain IP notation instead of cidr notation & + # matches the IP of the index + return True + else: + host_with_port = parsed.hostname + if parsed.port: + host_with_port += ':{0}'.format(parsed.port) + + for host in no_proxy: + if parsed.hostname.endswith(host) or host_with_port.endswith(host): + # The URL does match something in no_proxy, so we don't want + # to apply the proxies on this URL. + return True + + # If the system proxy settings indicate that this URL should be bypassed, + # don't proxy. + # The proxy_bypass function is incredibly buggy on OS X in early versions + # of Python 2.6, so allow this call to fail. Only catch the specific + # exceptions we've seen, though: this call failing in other ways can reveal + # legitimate problems. + with set_environ('no_proxy', no_proxy_arg): + try: + bypass = proxy_bypass(parsed.hostname) + except (TypeError, socket.gaierror): + bypass = False + + if bypass: + return True + + return False + + +def get_environ_proxies(url, no_proxy=None): + """ + Return a dict of environment proxies. + + :rtype: dict + """ + if should_bypass_proxies(url, no_proxy=no_proxy): + return {} + else: + return getproxies() + + +def select_proxy(url, proxies): + """Select a proxy for the url, if applicable. + + :param url: The url being for the request + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs + """ + proxies = proxies or {} + urlparts = urlparse(url) + if urlparts.hostname is None: + return proxies.get(urlparts.scheme, proxies.get('all')) + + proxy_keys = [ + urlparts.scheme + '://' + urlparts.hostname, + urlparts.scheme, + 'all://' + urlparts.hostname, + 'all', + ] + proxy = None + for proxy_key in proxy_keys: + if proxy_key in proxies: + proxy = proxies[proxy_key] + break + + return proxy + + +def default_user_agent(name="python-requests"): + """ + Return a string representing the default user agent. + + :rtype: str + """ + return '%s/%s' % (name, __version__) + + +def default_headers(): + """ + :rtype: requests.structures.CaseInsensitiveDict + """ + return CaseInsensitiveDict({ + 'User-Agent': default_user_agent(), + 'Accept-Encoding': ', '.join(('gzip', 'deflate')), + 'Accept': '*/*', + 'Connection': 'keep-alive', + }) + + +def parse_header_links(value): + """Return a list of parsed link headers proxies. + + i.e. Link: <http:/.../front.jpeg>; rel=front; type="image/jpeg",<http://.../back.jpeg>; rel=back;type="image/jpeg" + + :rtype: list + """ + + links = [] + + replace_chars = ' \'"' + + value = value.strip(replace_chars) + if not value: + return links + + for val in re.split(', *<', value): + try: + url, params = val.split(';', 1) + except ValueError: + url, params = val, '' + + link = {'url': url.strip('<> \'"')} + + for param in params.split(';'): + try: + key, value = param.split('=') + except ValueError: + break + + link[key.strip(replace_chars)] = value.strip(replace_chars) + + links.append(link) + + return links + + +# Null bytes; no need to recreate these on each call to guess_json_utf +_null = '\x00'.encode('ascii') # encoding to ASCII for Python 3 +_null2 = _null * 2 +_null3 = _null * 3 + + +def guess_json_utf(data): + """ + :rtype: str + """ + # JSON always starts with two ASCII characters, so detection is as + # easy as counting the nulls and from their location and count + # determine the encoding. Also detect a BOM, if present. + sample = data[:4] + if sample in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE): + return 'utf-32' # BOM included + if sample[:3] == codecs.BOM_UTF8: + return 'utf-8-sig' # BOM included, MS style (discouraged) + if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE): + return 'utf-16' # BOM included + nullcount = sample.count(_null) + if nullcount == 0: + return 'utf-8' + if nullcount == 2: + if sample[::2] == _null2: # 1st and 3rd are null + return 'utf-16-be' + if sample[1::2] == _null2: # 2nd and 4th are null + return 'utf-16-le' + # Did not detect 2 valid UTF-16 ascii-range characters + if nullcount == 3: + if sample[:3] == _null3: + return 'utf-32-be' + if sample[1:] == _null3: + return 'utf-32-le' + # Did not detect a valid UTF-32 ascii-range character + return None + + +def prepend_scheme_if_needed(url, new_scheme): + """Given a URL that may or may not have a scheme, prepend the given scheme. + Does not replace a present scheme with the one provided as an argument. + + :rtype: str + """ + scheme, netloc, path, params, query, fragment = urlparse(url, new_scheme) + + # urlparse is a finicky beast, and sometimes decides that there isn't a + # netloc present. Assume that it's being over-cautious, and switch netloc + # and path if urlparse decided there was no netloc. + if not netloc: + netloc, path = path, netloc + + return urlunparse((scheme, netloc, path, params, query, fragment)) + + +def get_auth_from_url(url): + """Given a url with authentication components, extract them into a tuple of + username,password. + + :rtype: (str,str) + """ + parsed = urlparse(url) + + try: + auth = (unquote(parsed.username), unquote(parsed.password)) + except (AttributeError, TypeError): + auth = ('', '') + + return auth + + +# Moved outside of function to avoid recompile every call +_CLEAN_HEADER_REGEX_BYTE = re.compile(b'^\\S[^\\r\\n]*$|^$') +_CLEAN_HEADER_REGEX_STR = re.compile(r'^\S[^\r\n]*$|^$') + + +def check_header_validity(header): + """Verifies that header value is a string which doesn't contain + leading whitespace or return characters. This prevents unintended + header injection. + + :param header: tuple, in the format (name, value). + """ + name, value = header + + if isinstance(value, bytes): + pat = _CLEAN_HEADER_REGEX_BYTE + else: + pat = _CLEAN_HEADER_REGEX_STR + try: + if not pat.match(value): + raise InvalidHeader("Invalid return character or leading space in header: %s" % name) + except TypeError: + raise InvalidHeader("Value for header {%s: %s} must be of type str or " + "bytes, not %s" % (name, value, type(value))) + + +def urldefragauth(url): + """ + Given a url remove the fragment and the authentication part. + + :rtype: str + """ + scheme, netloc, path, params, query, fragment = urlparse(url) + + # see func:`prepend_scheme_if_needed` + if not netloc: + netloc, path = path, netloc + + netloc = netloc.rsplit('@', 1)[-1] + + return urlunparse((scheme, netloc, path, params, query, '')) + + +def rewind_body(prepared_request): + """Move file pointer back to its recorded starting position + so it can be read again on redirect. + """ + body_seek = getattr(prepared_request.body, 'seek', None) + if body_seek is not None and isinstance(prepared_request._body_position, integer_types): + try: + body_seek(prepared_request._body_position) + except (IOError, OSError): + raise UnrewindableBodyError("An error occurred when rewinding request " + "body for redirect.") + else: + raise UnrewindableBodyError("Unable to rewind request body for redirect.") diff --git a/venv/Lib/site-packages/pip/_vendor/retrying.py b/venv/Lib/site-packages/pip/_vendor/retrying.py new file mode 100644 index 0000000..6d1e627 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/retrying.py @@ -0,0 +1,267 @@ +## Copyright 2013-2014 Ray Holder +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +import random +from pip._vendor import six +import sys +import time +import traceback + + +# sys.maxint / 2, since Python 3.2 doesn't have a sys.maxint... +MAX_WAIT = 1073741823 + + +def retry(*dargs, **dkw): + """ + Decorator function that instantiates the Retrying object + @param *dargs: positional arguments passed to Retrying object + @param **dkw: keyword arguments passed to the Retrying object + """ + # support both @retry and @retry() as valid syntax + if len(dargs) == 1 and callable(dargs[0]): + def wrap_simple(f): + + @six.wraps(f) + def wrapped_f(*args, **kw): + return Retrying().call(f, *args, **kw) + + return wrapped_f + + return wrap_simple(dargs[0]) + + else: + def wrap(f): + + @six.wraps(f) + def wrapped_f(*args, **kw): + return Retrying(*dargs, **dkw).call(f, *args, **kw) + + return wrapped_f + + return wrap + + +class Retrying(object): + + def __init__(self, + stop=None, wait=None, + stop_max_attempt_number=None, + stop_max_delay=None, + wait_fixed=None, + wait_random_min=None, wait_random_max=None, + wait_incrementing_start=None, wait_incrementing_increment=None, + wait_exponential_multiplier=None, wait_exponential_max=None, + retry_on_exception=None, + retry_on_result=None, + wrap_exception=False, + stop_func=None, + wait_func=None, + wait_jitter_max=None): + + self._stop_max_attempt_number = 5 if stop_max_attempt_number is None else stop_max_attempt_number + self._stop_max_delay = 100 if stop_max_delay is None else stop_max_delay + self._wait_fixed = 1000 if wait_fixed is None else wait_fixed + self._wait_random_min = 0 if wait_random_min is None else wait_random_min + self._wait_random_max = 1000 if wait_random_max is None else wait_random_max + self._wait_incrementing_start = 0 if wait_incrementing_start is None else wait_incrementing_start + self._wait_incrementing_increment = 100 if wait_incrementing_increment is None else wait_incrementing_increment + self._wait_exponential_multiplier = 1 if wait_exponential_multiplier is None else wait_exponential_multiplier + self._wait_exponential_max = MAX_WAIT if wait_exponential_max is None else wait_exponential_max + self._wait_jitter_max = 0 if wait_jitter_max is None else wait_jitter_max + + # TODO add chaining of stop behaviors + # stop behavior + stop_funcs = [] + if stop_max_attempt_number is not None: + stop_funcs.append(self.stop_after_attempt) + + if stop_max_delay is not None: + stop_funcs.append(self.stop_after_delay) + + if stop_func is not None: + self.stop = stop_func + + elif stop is None: + self.stop = lambda attempts, delay: any(f(attempts, delay) for f in stop_funcs) + + else: + self.stop = getattr(self, stop) + + # TODO add chaining of wait behaviors + # wait behavior + wait_funcs = [lambda *args, **kwargs: 0] + if wait_fixed is not None: + wait_funcs.append(self.fixed_sleep) + + if wait_random_min is not None or wait_random_max is not None: + wait_funcs.append(self.random_sleep) + + if wait_incrementing_start is not None or wait_incrementing_increment is not None: + wait_funcs.append(self.incrementing_sleep) + + if wait_exponential_multiplier is not None or wait_exponential_max is not None: + wait_funcs.append(self.exponential_sleep) + + if wait_func is not None: + self.wait = wait_func + + elif wait is None: + self.wait = lambda attempts, delay: max(f(attempts, delay) for f in wait_funcs) + + else: + self.wait = getattr(self, wait) + + # retry on exception filter + if retry_on_exception is None: + self._retry_on_exception = self.always_reject + else: + self._retry_on_exception = retry_on_exception + + # TODO simplify retrying by Exception types + # retry on result filter + if retry_on_result is None: + self._retry_on_result = self.never_reject + else: + self._retry_on_result = retry_on_result + + self._wrap_exception = wrap_exception + + def stop_after_attempt(self, previous_attempt_number, delay_since_first_attempt_ms): + """Stop after the previous attempt >= stop_max_attempt_number.""" + return previous_attempt_number >= self._stop_max_attempt_number + + def stop_after_delay(self, previous_attempt_number, delay_since_first_attempt_ms): + """Stop after the time from the first attempt >= stop_max_delay.""" + return delay_since_first_attempt_ms >= self._stop_max_delay + + def no_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """Don't sleep at all before retrying.""" + return 0 + + def fixed_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """Sleep a fixed amount of time between each retry.""" + return self._wait_fixed + + def random_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """Sleep a random amount of time between wait_random_min and wait_random_max""" + return random.randint(self._wait_random_min, self._wait_random_max) + + def incrementing_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """ + Sleep an incremental amount of time after each attempt, starting at + wait_incrementing_start and incrementing by wait_incrementing_increment + """ + result = self._wait_incrementing_start + (self._wait_incrementing_increment * (previous_attempt_number - 1)) + if result < 0: + result = 0 + return result + + def exponential_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + exp = 2 ** previous_attempt_number + result = self._wait_exponential_multiplier * exp + if result > self._wait_exponential_max: + result = self._wait_exponential_max + if result < 0: + result = 0 + return result + + def never_reject(self, result): + return False + + def always_reject(self, result): + return True + + def should_reject(self, attempt): + reject = False + if attempt.has_exception: + reject |= self._retry_on_exception(attempt.value[1]) + else: + reject |= self._retry_on_result(attempt.value) + + return reject + + def call(self, fn, *args, **kwargs): + start_time = int(round(time.time() * 1000)) + attempt_number = 1 + while True: + try: + attempt = Attempt(fn(*args, **kwargs), attempt_number, False) + except: + tb = sys.exc_info() + attempt = Attempt(tb, attempt_number, True) + + if not self.should_reject(attempt): + return attempt.get(self._wrap_exception) + + delay_since_first_attempt_ms = int(round(time.time() * 1000)) - start_time + if self.stop(attempt_number, delay_since_first_attempt_ms): + if not self._wrap_exception and attempt.has_exception: + # get() on an attempt with an exception should cause it to be raised, but raise just in case + raise attempt.get() + else: + raise RetryError(attempt) + else: + sleep = self.wait(attempt_number, delay_since_first_attempt_ms) + if self._wait_jitter_max: + jitter = random.random() * self._wait_jitter_max + sleep = sleep + max(0, jitter) + time.sleep(sleep / 1000.0) + + attempt_number += 1 + + +class Attempt(object): + """ + An Attempt encapsulates a call to a target function that may end as a + normal return value from the function or an Exception depending on what + occurred during the execution. + """ + + def __init__(self, value, attempt_number, has_exception): + self.value = value + self.attempt_number = attempt_number + self.has_exception = has_exception + + def get(self, wrap_exception=False): + """ + Return the return value of this Attempt instance or raise an Exception. + If wrap_exception is true, this Attempt is wrapped inside of a + RetryError before being raised. + """ + if self.has_exception: + if wrap_exception: + raise RetryError(self) + else: + six.reraise(self.value[0], self.value[1], self.value[2]) + else: + return self.value + + def __repr__(self): + if self.has_exception: + return "Attempts: {0}, Error:\n{1}".format(self.attempt_number, "".join(traceback.format_tb(self.value[2]))) + else: + return "Attempts: {0}, Value: {1}".format(self.attempt_number, self.value) + + +class RetryError(Exception): + """ + A RetryError encapsulates the last Attempt instance right before giving up. + """ + + def __init__(self, last_attempt): + self.last_attempt = last_attempt + + def __str__(self): + return "RetryError[{0}]".format(self.last_attempt) diff --git a/venv/Lib/site-packages/pip/_vendor/six.py b/venv/Lib/site-packages/pip/_vendor/six.py new file mode 100644 index 0000000..6bf4fd3 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/six.py @@ -0,0 +1,891 @@ +# Copyright (c) 2010-2017 Benjamin Peterson +# +# 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. + +"""Utilities for writing code that runs on Python 2 and 3""" + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson <benjamin@python.org>" +__version__ = "1.11.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("getoutput", "commands", "subprocess"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("splitvalue", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), + MovedAttribute("parse_http_list", "urllib2", "urllib.request"), + MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + try: + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None + tb = None + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + try: + raise tp, value, tb + finally: + tb = None +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + try: + if from_value is None: + raise value + raise value from from_value + finally: + value = None +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + try: + raise value from from_value + finally: + value = None +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/__init__.py b/venv/Lib/site-packages/pip/_vendor/urllib3/__init__.py new file mode 100644 index 0000000..4bd533b --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/__init__.py @@ -0,0 +1,97 @@ +""" +urllib3 - Thread-safe connection pooling and re-using. +""" + +from __future__ import absolute_import +import warnings + +from .connectionpool import ( + HTTPConnectionPool, + HTTPSConnectionPool, + connection_from_url +) + +from . import exceptions +from .filepost import encode_multipart_formdata +from .poolmanager import PoolManager, ProxyManager, proxy_from_url +from .response import HTTPResponse +from .util.request import make_headers +from .util.url import get_host +from .util.timeout import Timeout +from .util.retry import Retry + + +# Set default logging handler to avoid "No handler found" warnings. +import logging +try: # Python 2.7+ + from logging import NullHandler +except ImportError: + class NullHandler(logging.Handler): + def emit(self, record): + pass + +__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)' +__license__ = 'MIT' +__version__ = '1.23' + +__all__ = ( + 'HTTPConnectionPool', + 'HTTPSConnectionPool', + 'PoolManager', + 'ProxyManager', + 'HTTPResponse', + 'Retry', + 'Timeout', + 'add_stderr_logger', + 'connection_from_url', + 'disable_warnings', + 'encode_multipart_formdata', + 'get_host', + 'make_headers', + 'proxy_from_url', +) + +logging.getLogger(__name__).addHandler(NullHandler()) + + +def add_stderr_logger(level=logging.DEBUG): + """ + Helper for quickly adding a StreamHandler to the logger. Useful for + debugging. + + Returns the handler after adding it. + """ + # This method needs to be in this __init__.py to get the __name__ correct + # even if urllib3 is vendored within another package. + logger = logging.getLogger(__name__) + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s')) + logger.addHandler(handler) + logger.setLevel(level) + logger.debug('Added a stderr logging handler to logger: %s', __name__) + return handler + + +# ... Clean up. +del NullHandler + + +# All warning filters *must* be appended unless you're really certain that they +# shouldn't be: otherwise, it's very hard for users to use most Python +# mechanisms to silence them. +# SecurityWarning's always go off by default. +warnings.simplefilter('always', exceptions.SecurityWarning, append=True) +# SubjectAltNameWarning's should go off once per host +warnings.simplefilter('default', exceptions.SubjectAltNameWarning, append=True) +# InsecurePlatformWarning's don't vary between requests, so we keep it default. +warnings.simplefilter('default', exceptions.InsecurePlatformWarning, + append=True) +# SNIMissingWarnings should go off only once. +warnings.simplefilter('default', exceptions.SNIMissingWarning, append=True) + + +def disable_warnings(category=exceptions.HTTPWarning): + """ + Helper for quickly disabling all urllib3 warnings. + """ + warnings.simplefilter('ignore', category) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/_collections.py b/venv/Lib/site-packages/pip/_vendor/urllib3/_collections.py new file mode 100644 index 0000000..6e36b84 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/_collections.py @@ -0,0 +1,332 @@ +from __future__ import absolute_import +try: + from collections.abc import Mapping, MutableMapping +except ImportError: + from collections import Mapping, MutableMapping +try: + from threading import RLock +except ImportError: # Platform-specific: No threads available + class RLock: + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_value, traceback): + pass + + +try: # Python 2.7+ + from collections import OrderedDict +except ImportError: + from .packages.ordered_dict import OrderedDict +from .exceptions import InvalidHeader +from .packages.six import iterkeys, itervalues, PY3 + + +__all__ = ['RecentlyUsedContainer', 'HTTPHeaderDict'] + + +_Null = object() + + +class RecentlyUsedContainer(MutableMapping): + """ + Provides a thread-safe dict-like container which maintains up to + ``maxsize`` keys while throwing away the least-recently-used keys beyond + ``maxsize``. + + :param maxsize: + Maximum number of recent elements to retain. + + :param dispose_func: + Every time an item is evicted from the container, + ``dispose_func(value)`` is called. Callback which will get called + """ + + ContainerCls = OrderedDict + + def __init__(self, maxsize=10, dispose_func=None): + self._maxsize = maxsize + self.dispose_func = dispose_func + + self._container = self.ContainerCls() + self.lock = RLock() + + def __getitem__(self, key): + # Re-insert the item, moving it to the end of the eviction line. + with self.lock: + item = self._container.pop(key) + self._container[key] = item + return item + + def __setitem__(self, key, value): + evicted_value = _Null + with self.lock: + # Possibly evict the existing value of 'key' + evicted_value = self._container.get(key, _Null) + self._container[key] = value + + # If we didn't evict an existing value, we might have to evict the + # least recently used item from the beginning of the container. + if len(self._container) > self._maxsize: + _key, evicted_value = self._container.popitem(last=False) + + if self.dispose_func and evicted_value is not _Null: + self.dispose_func(evicted_value) + + def __delitem__(self, key): + with self.lock: + value = self._container.pop(key) + + if self.dispose_func: + self.dispose_func(value) + + def __len__(self): + with self.lock: + return len(self._container) + + def __iter__(self): + raise NotImplementedError('Iteration over this class is unlikely to be threadsafe.') + + def clear(self): + with self.lock: + # Copy pointers to all values, then wipe the mapping + values = list(itervalues(self._container)) + self._container.clear() + + if self.dispose_func: + for value in values: + self.dispose_func(value) + + def keys(self): + with self.lock: + return list(iterkeys(self._container)) + + +class HTTPHeaderDict(MutableMapping): + """ + :param headers: + An iterable of field-value pairs. Must not contain multiple field names + when compared case-insensitively. + + :param kwargs: + Additional field-value pairs to pass in to ``dict.update``. + + A ``dict`` like container for storing HTTP Headers. + + Field names are stored and compared case-insensitively in compliance with + RFC 7230. Iteration provides the first case-sensitive key seen for each + case-insensitive pair. + + Using ``__setitem__`` syntax overwrites fields that compare equal + case-insensitively in order to maintain ``dict``'s api. For fields that + compare equal, instead create a new ``HTTPHeaderDict`` and use ``.add`` + in a loop. + + If multiple fields that are equal case-insensitively are passed to the + constructor or ``.update``, the behavior is undefined and some will be + lost. + + >>> headers = HTTPHeaderDict() + >>> headers.add('Set-Cookie', 'foo=bar') + >>> headers.add('set-cookie', 'baz=quxx') + >>> headers['content-length'] = '7' + >>> headers['SET-cookie'] + 'foo=bar, baz=quxx' + >>> headers['Content-Length'] + '7' + """ + + def __init__(self, headers=None, **kwargs): + super(HTTPHeaderDict, self).__init__() + self._container = OrderedDict() + if headers is not None: + if isinstance(headers, HTTPHeaderDict): + self._copy_from(headers) + else: + self.extend(headers) + if kwargs: + self.extend(kwargs) + + def __setitem__(self, key, val): + self._container[key.lower()] = [key, val] + return self._container[key.lower()] + + def __getitem__(self, key): + val = self._container[key.lower()] + return ', '.join(val[1:]) + + def __delitem__(self, key): + del self._container[key.lower()] + + def __contains__(self, key): + return key.lower() in self._container + + def __eq__(self, other): + if not isinstance(other, Mapping) and not hasattr(other, 'keys'): + return False + if not isinstance(other, type(self)): + other = type(self)(other) + return (dict((k.lower(), v) for k, v in self.itermerged()) == + dict((k.lower(), v) for k, v in other.itermerged())) + + def __ne__(self, other): + return not self.__eq__(other) + + if not PY3: # Python 2 + iterkeys = MutableMapping.iterkeys + itervalues = MutableMapping.itervalues + + __marker = object() + + def __len__(self): + return len(self._container) + + def __iter__(self): + # Only provide the originally cased names + for vals in self._container.values(): + yield vals[0] + + def pop(self, key, default=__marker): + '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + ''' + # Using the MutableMapping function directly fails due to the private marker. + # Using ordinary dict.pop would expose the internal structures. + # So let's reinvent the wheel. + try: + value = self[key] + except KeyError: + if default is self.__marker: + raise + return default + else: + del self[key] + return value + + def discard(self, key): + try: + del self[key] + except KeyError: + pass + + def add(self, key, val): + """Adds a (name, value) pair, doesn't overwrite the value if it already + exists. + + >>> headers = HTTPHeaderDict(foo='bar') + >>> headers.add('Foo', 'baz') + >>> headers['foo'] + 'bar, baz' + """ + key_lower = key.lower() + new_vals = [key, val] + # Keep the common case aka no item present as fast as possible + vals = self._container.setdefault(key_lower, new_vals) + if new_vals is not vals: + vals.append(val) + + def extend(self, *args, **kwargs): + """Generic import function for any type of header-like object. + Adapted version of MutableMapping.update in order to insert items + with self.add instead of self.__setitem__ + """ + if len(args) > 1: + raise TypeError("extend() takes at most 1 positional " + "arguments ({0} given)".format(len(args))) + other = args[0] if len(args) >= 1 else () + + if isinstance(other, HTTPHeaderDict): + for key, val in other.iteritems(): + self.add(key, val) + elif isinstance(other, Mapping): + for key in other: + self.add(key, other[key]) + elif hasattr(other, "keys"): + for key in other.keys(): + self.add(key, other[key]) + else: + for key, value in other: + self.add(key, value) + + for key, value in kwargs.items(): + self.add(key, value) + + def getlist(self, key, default=__marker): + """Returns a list of all the values for the named field. Returns an + empty list if the key doesn't exist.""" + try: + vals = self._container[key.lower()] + except KeyError: + if default is self.__marker: + return [] + return default + else: + return vals[1:] + + # Backwards compatibility for httplib + getheaders = getlist + getallmatchingheaders = getlist + iget = getlist + + # Backwards compatibility for http.cookiejar + get_all = getlist + + def __repr__(self): + return "%s(%s)" % (type(self).__name__, dict(self.itermerged())) + + def _copy_from(self, other): + for key in other: + val = other.getlist(key) + if isinstance(val, list): + # Don't need to convert tuples + val = list(val) + self._container[key.lower()] = [key] + val + + def copy(self): + clone = type(self)() + clone._copy_from(self) + return clone + + def iteritems(self): + """Iterate over all header lines, including duplicate ones.""" + for key in self: + vals = self._container[key.lower()] + for val in vals[1:]: + yield vals[0], val + + def itermerged(self): + """Iterate over all headers, merging duplicate ones together.""" + for key in self: + val = self._container[key.lower()] + yield val[0], ', '.join(val[1:]) + + def items(self): + return list(self.iteritems()) + + @classmethod + def from_httplib(cls, message): # Python 2 + """Read headers from a Python 2 httplib message object.""" + # python2.7 does not expose a proper API for exporting multiheaders + # efficiently. This function re-reads raw lines from the message + # object and extracts the multiheaders properly. + obs_fold_continued_leaders = (' ', '\t') + headers = [] + + for line in message.headers: + if line.startswith(obs_fold_continued_leaders): + if not headers: + # We received a header line that starts with OWS as described + # in RFC-7230 S3.2.4. This indicates a multiline header, but + # there exists no previous header to which we can attach it. + raise InvalidHeader( + 'Header continuation with no previous header: %s' % line + ) + else: + key, value = headers[-1] + headers[-1] = (key, value + ' ' + line.strip()) + continue + + key, value = line.split(':', 1) + headers.append((key, value.strip())) + + return cls(headers) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/connection.py b/venv/Lib/site-packages/pip/_vendor/urllib3/connection.py new file mode 100644 index 0000000..a03b573 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/connection.py @@ -0,0 +1,403 @@ +from __future__ import absolute_import +import datetime +import logging +import os +import sys +import socket +from socket import error as SocketError, timeout as SocketTimeout +import warnings +from .packages import six +from .packages.six.moves.http_client import HTTPConnection as _HTTPConnection +from .packages.six.moves.http_client import HTTPException # noqa: F401 + +try: # Compiled with SSL? + import ssl + BaseSSLError = ssl.SSLError +except (ImportError, AttributeError): # Platform-specific: No SSL. + ssl = None + + class BaseSSLError(BaseException): + pass + + +try: # Python 3: + # Not a no-op, we're adding this to the namespace so it can be imported. + ConnectionError = ConnectionError +except NameError: # Python 2: + class ConnectionError(Exception): + pass + + +from .exceptions import ( + NewConnectionError, + ConnectTimeoutError, + SubjectAltNameWarning, + SystemTimeWarning, +) +from .packages.ssl_match_hostname import match_hostname, CertificateError + +from .util.ssl_ import ( + resolve_cert_reqs, + resolve_ssl_version, + assert_fingerprint, + create_urllib3_context, + ssl_wrap_socket +) + + +from .util import connection + +from ._collections import HTTPHeaderDict + +log = logging.getLogger(__name__) + +port_by_scheme = { + 'http': 80, + 'https': 443, +} + +# When updating RECENT_DATE, move it to within two years of the current date, +# and not less than 6 months ago. +# Example: if Today is 2018-01-01, then RECENT_DATE should be any date on or +# after 2016-01-01 (today - 2 years) AND before 2017-07-01 (today - 6 months) +RECENT_DATE = datetime.date(2017, 6, 30) + + +class DummyConnection(object): + """Used to detect a failed ConnectionCls import.""" + pass + + +class HTTPConnection(_HTTPConnection, object): + """ + Based on httplib.HTTPConnection but provides an extra constructor + backwards-compatibility layer between older and newer Pythons. + + Additional keyword parameters are used to configure attributes of the connection. + Accepted parameters include: + + - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool` + - ``source_address``: Set the source address for the current connection. + + .. note:: This is ignored for Python 2.6. It is only applied for 2.7 and 3.x + + - ``socket_options``: Set specific options on the underlying socket. If not specified, then + defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling + Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. + + For example, if you wish to enable TCP Keep Alive in addition to the defaults, + you might pass:: + + HTTPConnection.default_socket_options + [ + (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), + ] + + Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). + """ + + default_port = port_by_scheme['http'] + + #: Disable Nagle's algorithm by default. + #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]`` + default_socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)] + + #: Whether this connection verifies the host's certificate. + is_verified = False + + def __init__(self, *args, **kw): + if six.PY3: # Python 3 + kw.pop('strict', None) + + # Pre-set source_address in case we have an older Python like 2.6. + self.source_address = kw.get('source_address') + + if sys.version_info < (2, 7): # Python 2.6 + # _HTTPConnection on Python 2.6 will balk at this keyword arg, but + # not newer versions. We can still use it when creating a + # connection though, so we pop it *after* we have saved it as + # self.source_address. + kw.pop('source_address', None) + + #: The socket options provided by the user. If no options are + #: provided, we use the default options. + self.socket_options = kw.pop('socket_options', self.default_socket_options) + + # Superclass also sets self.source_address in Python 2.7+. + _HTTPConnection.__init__(self, *args, **kw) + + @property + def host(self): + """ + Getter method to remove any trailing dots that indicate the hostname is an FQDN. + + In general, SSL certificates don't include the trailing dot indicating a + fully-qualified domain name, and thus, they don't validate properly when + checked against a domain name that includes the dot. In addition, some + servers may not expect to receive the trailing dot when provided. + + However, the hostname with trailing dot is critical to DNS resolution; doing a + lookup with the trailing dot will properly only resolve the appropriate FQDN, + whereas a lookup without a trailing dot will search the system's search domain + list. Thus, it's important to keep the original host around for use only in + those cases where it's appropriate (i.e., when doing DNS lookup to establish the + actual TCP connection across which we're going to send HTTP requests). + """ + return self._dns_host.rstrip('.') + + @host.setter + def host(self, value): + """ + Setter for the `host` property. + + We assume that only urllib3 uses the _dns_host attribute; httplib itself + only uses `host`, and it seems reasonable that other libraries follow suit. + """ + self._dns_host = value + + def _new_conn(self): + """ Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + extra_kw = {} + if self.source_address: + extra_kw['source_address'] = self.source_address + + if self.socket_options: + extra_kw['socket_options'] = self.socket_options + + try: + conn = connection.create_connection( + (self._dns_host, self.port), self.timeout, **extra_kw) + + except SocketTimeout as e: + raise ConnectTimeoutError( + self, "Connection to %s timed out. (connect timeout=%s)" % + (self.host, self.timeout)) + + except SocketError as e: + raise NewConnectionError( + self, "Failed to establish a new connection: %s" % e) + + return conn + + def _prepare_conn(self, conn): + self.sock = conn + # the _tunnel_host attribute was added in python 2.6.3 (via + # http://hg.python.org/cpython/rev/0f57b30a152f) so pythons 2.6(0-2) do + # not have them. + if getattr(self, '_tunnel_host', None): + # TODO: Fix tunnel so it doesn't depend on self.sock state. + self._tunnel() + # Mark this connection as not reusable + self.auto_open = 0 + + def connect(self): + conn = self._new_conn() + self._prepare_conn(conn) + + def request_chunked(self, method, url, body=None, headers=None): + """ + Alternative to the common request method, which sends the + body with chunked encoding and not as one block + """ + headers = HTTPHeaderDict(headers if headers is not None else {}) + skip_accept_encoding = 'accept-encoding' in headers + skip_host = 'host' in headers + self.putrequest( + method, + url, + skip_accept_encoding=skip_accept_encoding, + skip_host=skip_host + ) + for header, value in headers.items(): + self.putheader(header, value) + if 'transfer-encoding' not in headers: + self.putheader('Transfer-Encoding', 'chunked') + self.endheaders() + + if body is not None: + stringish_types = six.string_types + (six.binary_type,) + if isinstance(body, stringish_types): + body = (body,) + for chunk in body: + if not chunk: + continue + if not isinstance(chunk, six.binary_type): + chunk = chunk.encode('utf8') + len_str = hex(len(chunk))[2:] + self.send(len_str.encode('utf-8')) + self.send(b'\r\n') + self.send(chunk) + self.send(b'\r\n') + + # After the if clause, to always have a closed body + self.send(b'0\r\n\r\n') + + +class HTTPSConnection(HTTPConnection): + default_port = port_by_scheme['https'] + + ssl_version = None + + def __init__(self, host, port=None, key_file=None, cert_file=None, + strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + ssl_context=None, **kw): + + HTTPConnection.__init__(self, host, port, strict=strict, + timeout=timeout, **kw) + + self.key_file = key_file + self.cert_file = cert_file + self.ssl_context = ssl_context + + # Required property for Google AppEngine 1.9.0 which otherwise causes + # HTTPS requests to go out as HTTP. (See Issue #356) + self._protocol = 'https' + + def connect(self): + conn = self._new_conn() + self._prepare_conn(conn) + + if self.ssl_context is None: + self.ssl_context = create_urllib3_context( + ssl_version=resolve_ssl_version(None), + cert_reqs=resolve_cert_reqs(None), + ) + + self.sock = ssl_wrap_socket( + sock=conn, + keyfile=self.key_file, + certfile=self.cert_file, + ssl_context=self.ssl_context, + ) + + +class VerifiedHTTPSConnection(HTTPSConnection): + """ + Based on httplib.HTTPSConnection but wraps the socket with + SSL certification. + """ + cert_reqs = None + ca_certs = None + ca_cert_dir = None + ssl_version = None + assert_fingerprint = None + + def set_cert(self, key_file=None, cert_file=None, + cert_reqs=None, ca_certs=None, + assert_hostname=None, assert_fingerprint=None, + ca_cert_dir=None): + """ + This method should only be called once, before the connection is used. + """ + # If cert_reqs is not provided, we can try to guess. If the user gave + # us a cert database, we assume they want to use it: otherwise, if + # they gave us an SSL Context object we should use whatever is set for + # it. + if cert_reqs is None: + if ca_certs or ca_cert_dir: + cert_reqs = 'CERT_REQUIRED' + elif self.ssl_context is not None: + cert_reqs = self.ssl_context.verify_mode + + self.key_file = key_file + self.cert_file = cert_file + self.cert_reqs = cert_reqs + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + self.ca_certs = ca_certs and os.path.expanduser(ca_certs) + self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + + def connect(self): + # Add certificate verification + conn = self._new_conn() + + hostname = self.host + if getattr(self, '_tunnel_host', None): + # _tunnel_host was added in Python 2.6.3 + # (See: http://hg.python.org/cpython/rev/0f57b30a152f) + + self.sock = conn + # Calls self._set_hostport(), so self.host is + # self._tunnel_host below. + self._tunnel() + # Mark this connection as not reusable + self.auto_open = 0 + + # Override the host with the one we're requesting data from. + hostname = self._tunnel_host + + is_time_off = datetime.date.today() < RECENT_DATE + if is_time_off: + warnings.warn(( + 'System time is way off (before {0}). This will probably ' + 'lead to SSL verification errors').format(RECENT_DATE), + SystemTimeWarning + ) + + # Wrap socket using verification with the root certs in + # trusted_root_certs + if self.ssl_context is None: + self.ssl_context = create_urllib3_context( + ssl_version=resolve_ssl_version(self.ssl_version), + cert_reqs=resolve_cert_reqs(self.cert_reqs), + ) + + context = self.ssl_context + context.verify_mode = resolve_cert_reqs(self.cert_reqs) + self.sock = ssl_wrap_socket( + sock=conn, + keyfile=self.key_file, + certfile=self.cert_file, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + server_hostname=hostname, + ssl_context=context) + + if self.assert_fingerprint: + assert_fingerprint(self.sock.getpeercert(binary_form=True), + self.assert_fingerprint) + elif context.verify_mode != ssl.CERT_NONE \ + and not getattr(context, 'check_hostname', False) \ + and self.assert_hostname is not False: + # While urllib3 attempts to always turn off hostname matching from + # the TLS library, this cannot always be done. So we check whether + # the TLS Library still thinks it's matching hostnames. + cert = self.sock.getpeercert() + if not cert.get('subjectAltName', ()): + warnings.warn(( + 'Certificate for {0} has no `subjectAltName`, falling back to check for a ' + '`commonName` for now. This feature is being removed by major browsers and ' + 'deprecated by RFC 2818. (See https://github.com/shazow/urllib3/issues/497 ' + 'for details.)'.format(hostname)), + SubjectAltNameWarning + ) + _match_hostname(cert, self.assert_hostname or hostname) + + self.is_verified = ( + context.verify_mode == ssl.CERT_REQUIRED or + self.assert_fingerprint is not None + ) + + +def _match_hostname(cert, asserted_hostname): + try: + match_hostname(cert, asserted_hostname) + except CertificateError as e: + log.error( + 'Certificate did not match expected hostname: %s. ' + 'Certificate: %s', asserted_hostname, cert + ) + # Add cert to exception and reraise so client code can inspect + # the cert when catching the exception, if they want to + e._peer_cert = cert + raise + + +if ssl: + # Make a copy for testing. + UnverifiedHTTPSConnection = HTTPSConnection + HTTPSConnection = VerifiedHTTPSConnection +else: + HTTPSConnection = DummyConnection diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py b/venv/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py new file mode 100644 index 0000000..8fcb0bc --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py @@ -0,0 +1,906 @@ +from __future__ import absolute_import +import errno +import logging +import sys +import warnings + +from socket import error as SocketError, timeout as SocketTimeout +import socket + + +from .exceptions import ( + ClosedPoolError, + ProtocolError, + EmptyPoolError, + HeaderParsingError, + HostChangedError, + LocationValueError, + MaxRetryError, + ProxyError, + ReadTimeoutError, + SSLError, + TimeoutError, + InsecureRequestWarning, + NewConnectionError, +) +from .packages.ssl_match_hostname import CertificateError +from .packages import six +from .packages.six.moves import queue +from .connection import ( + port_by_scheme, + DummyConnection, + HTTPConnection, HTTPSConnection, VerifiedHTTPSConnection, + HTTPException, BaseSSLError, +) +from .request import RequestMethods +from .response import HTTPResponse + +from .util.connection import is_connection_dropped +from .util.request import set_file_position +from .util.response import assert_header_parsing +from .util.retry import Retry +from .util.timeout import Timeout +from .util.url import get_host, Url, NORMALIZABLE_SCHEMES +from .util.queue import LifoQueue + + +xrange = six.moves.xrange + +log = logging.getLogger(__name__) + +_Default = object() + + +# Pool objects +class ConnectionPool(object): + """ + Base class for all connection pools, such as + :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`. + """ + + scheme = None + QueueCls = LifoQueue + + def __init__(self, host, port=None): + if not host: + raise LocationValueError("No host specified.") + + self.host = _ipv6_host(host, self.scheme) + self._proxy_host = host.lower() + self.port = port + + def __str__(self): + return '%s(host=%r, port=%r)' % (type(self).__name__, + self.host, self.port) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + # Return False to re-raise any potential exceptions + return False + + def close(self): + """ + Close all pooled connections and disable the pool. + """ + pass + + +# This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252 +_blocking_errnos = set([errno.EAGAIN, errno.EWOULDBLOCK]) + + +class HTTPConnectionPool(ConnectionPool, RequestMethods): + """ + Thread-safe connection pool for one host. + + :param host: + Host used for this HTTP Connection (e.g. "localhost"), passed into + :class:`httplib.HTTPConnection`. + + :param port: + Port used for this HTTP Connection (None is equivalent to 80), passed + into :class:`httplib.HTTPConnection`. + + :param strict: + Causes BadStatusLine to be raised if the status line can't be parsed + as a valid HTTP/1.0 or 1.1 status line, passed into + :class:`httplib.HTTPConnection`. + + .. note:: + Only works in Python 2. This parameter is ignored in Python 3. + + :param timeout: + Socket timeout in seconds for each individual connection. This can + be a float or integer, which sets the timeout for the HTTP request, + or an instance of :class:`urllib3.util.Timeout` which gives you more + fine-grained control over request timeouts. After the constructor has + been parsed, this is always a `urllib3.util.Timeout` object. + + :param maxsize: + Number of connections to save that can be reused. More than 1 is useful + in multithreaded situations. If ``block`` is set to False, more + connections will be created but they will not be saved once they've + been used. + + :param block: + If set to True, no more than ``maxsize`` connections will be used at + a time. When no free connections are available, the call will block + until a connection has been released. This is a useful side effect for + particular multithreaded situations where one does not want to use more + than maxsize connections per host to prevent flooding. + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + + :param retries: + Retry configuration to use by default with requests in this pool. + + :param _proxy: + Parsed proxy URL, should not be used directly, instead, see + :class:`urllib3.connectionpool.ProxyManager`" + + :param _proxy_headers: + A dictionary with proxy headers, should not be used directly, + instead, see :class:`urllib3.connectionpool.ProxyManager`" + + :param \\**conn_kw: + Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`, + :class:`urllib3.connection.HTTPSConnection` instances. + """ + + scheme = 'http' + ConnectionCls = HTTPConnection + ResponseCls = HTTPResponse + + def __init__(self, host, port=None, strict=False, + timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, block=False, + headers=None, retries=None, + _proxy=None, _proxy_headers=None, + **conn_kw): + ConnectionPool.__init__(self, host, port) + RequestMethods.__init__(self, headers) + + self.strict = strict + + if not isinstance(timeout, Timeout): + timeout = Timeout.from_float(timeout) + + if retries is None: + retries = Retry.DEFAULT + + self.timeout = timeout + self.retries = retries + + self.pool = self.QueueCls(maxsize) + self.block = block + + self.proxy = _proxy + self.proxy_headers = _proxy_headers or {} + + # Fill the queue up so that doing get() on it will block properly + for _ in xrange(maxsize): + self.pool.put(None) + + # These are mostly for testing and debugging purposes. + self.num_connections = 0 + self.num_requests = 0 + self.conn_kw = conn_kw + + if self.proxy: + # Enable Nagle's algorithm for proxies, to avoid packet fragmentation. + # We cannot know if the user has added default socket options, so we cannot replace the + # list. + self.conn_kw.setdefault('socket_options', []) + + def _new_conn(self): + """ + Return a fresh :class:`HTTPConnection`. + """ + self.num_connections += 1 + log.debug("Starting new HTTP connection (%d): %s:%s", + self.num_connections, self.host, self.port or "80") + + conn = self.ConnectionCls(host=self.host, port=self.port, + timeout=self.timeout.connect_timeout, + strict=self.strict, **self.conn_kw) + return conn + + def _get_conn(self, timeout=None): + """ + Get a connection. Will return a pooled connection if one is available. + + If no connections are available and :prop:`.block` is ``False``, then a + fresh connection is returned. + + :param timeout: + Seconds to wait before giving up and raising + :class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and + :prop:`.block` is ``True``. + """ + conn = None + try: + conn = self.pool.get(block=self.block, timeout=timeout) + + except AttributeError: # self.pool is None + raise ClosedPoolError(self, "Pool is closed.") + + except queue.Empty: + if self.block: + raise EmptyPoolError(self, + "Pool reached maximum size and no more " + "connections are allowed.") + pass # Oh well, we'll create a new connection then + + # If this is a persistent connection, check if it got disconnected + if conn and is_connection_dropped(conn): + log.debug("Resetting dropped connection: %s", self.host) + conn.close() + if getattr(conn, 'auto_open', 1) == 0: + # This is a proxied connection that has been mutated by + # httplib._tunnel() and cannot be reused (since it would + # attempt to bypass the proxy) + conn = None + + return conn or self._new_conn() + + def _put_conn(self, conn): + """ + Put a connection back into the pool. + + :param conn: + Connection object for the current host and port as returned by + :meth:`._new_conn` or :meth:`._get_conn`. + + If the pool is already full, the connection is closed and discarded + because we exceeded maxsize. If connections are discarded frequently, + then maxsize should be increased. + + If the pool is closed, then the connection will be closed and discarded. + """ + try: + self.pool.put(conn, block=False) + return # Everything is dandy, done. + except AttributeError: + # self.pool is None. + pass + except queue.Full: + # This should never happen if self.block == True + log.warning( + "Connection pool is full, discarding connection: %s", + self.host) + + # Connection never got put back into the pool, close it. + if conn: + conn.close() + + def _validate_conn(self, conn): + """ + Called right before a request is made, after the socket is created. + """ + pass + + def _prepare_proxy(self, conn): + # Nothing to do for HTTP connections. + pass + + def _get_timeout(self, timeout): + """ Helper that always returns a :class:`urllib3.util.Timeout` """ + if timeout is _Default: + return self.timeout.clone() + + if isinstance(timeout, Timeout): + return timeout.clone() + else: + # User passed us an int/float. This is for backwards compatibility, + # can be removed later + return Timeout.from_float(timeout) + + def _raise_timeout(self, err, url, timeout_value): + """Is the error actually a timeout? Will raise a ReadTimeout or pass""" + + if isinstance(err, SocketTimeout): + raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + + # See the above comment about EAGAIN in Python 3. In Python 2 we have + # to specifically catch it and throw the timeout error + if hasattr(err, 'errno') and err.errno in _blocking_errnos: + raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + + # Catch possible read timeouts thrown as SSL errors. If not the + # case, rethrow the original. We need to do this because of: + # http://bugs.python.org/issue10272 + if 'timed out' in str(err) or 'did not complete (read)' in str(err): # Python 2.6 + raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + + def _make_request(self, conn, method, url, timeout=_Default, chunked=False, + **httplib_request_kw): + """ + Perform a request on a given urllib connection object taken from our + pool. + + :param conn: + a connection from one of our connection pools + + :param timeout: + Socket timeout in seconds for the request. This can be a + float or integer, which will set the same timeout value for + the socket connect and the socket read, or an instance of + :class:`urllib3.util.Timeout`, which gives you more fine-grained + control over your timeouts. + """ + self.num_requests += 1 + + timeout_obj = self._get_timeout(timeout) + timeout_obj.start_connect() + conn.timeout = timeout_obj.connect_timeout + + # Trigger any extra validation we need to do. + try: + self._validate_conn(conn) + except (SocketTimeout, BaseSSLError) as e: + # Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout. + self._raise_timeout(err=e, url=url, timeout_value=conn.timeout) + raise + + # conn.request() calls httplib.*.request, not the method in + # urllib3.request. It also calls makefile (recv) on the socket. + if chunked: + conn.request_chunked(method, url, **httplib_request_kw) + else: + conn.request(method, url, **httplib_request_kw) + + # Reset the timeout for the recv() on the socket + read_timeout = timeout_obj.read_timeout + + # App Engine doesn't have a sock attr + if getattr(conn, 'sock', None): + # In Python 3 socket.py will catch EAGAIN and return None when you + # try and read into the file pointer created by http.client, which + # instead raises a BadStatusLine exception. Instead of catching + # the exception and assuming all BadStatusLine exceptions are read + # timeouts, check for a zero timeout before making the request. + if read_timeout == 0: + raise ReadTimeoutError( + self, url, "Read timed out. (read timeout=%s)" % read_timeout) + if read_timeout is Timeout.DEFAULT_TIMEOUT: + conn.sock.settimeout(socket.getdefaulttimeout()) + else: # None or a value + conn.sock.settimeout(read_timeout) + + # Receive the response from the server + try: + try: # Python 2.7, use buffering of HTTP responses + httplib_response = conn.getresponse(buffering=True) + except TypeError: # Python 2.6 and older, Python 3 + try: + httplib_response = conn.getresponse() + except Exception as e: + # Remove the TypeError from the exception chain in Python 3; + # otherwise it looks like a programming error was the cause. + six.raise_from(e, None) + except (SocketTimeout, BaseSSLError, SocketError) as e: + self._raise_timeout(err=e, url=url, timeout_value=read_timeout) + raise + + # AppEngine doesn't have a version attr. + http_version = getattr(conn, '_http_vsn_str', 'HTTP/?') + log.debug("%s://%s:%s \"%s %s %s\" %s %s", self.scheme, self.host, self.port, + method, url, http_version, httplib_response.status, + httplib_response.length) + + try: + assert_header_parsing(httplib_response.msg) + except (HeaderParsingError, TypeError) as hpe: # Platform-specific: Python 3 + log.warning( + 'Failed to parse headers (url=%s): %s', + self._absolute_url(url), hpe, exc_info=True) + + return httplib_response + + def _absolute_url(self, path): + return Url(scheme=self.scheme, host=self.host, port=self.port, path=path).url + + def close(self): + """ + Close all pooled connections and disable the pool. + """ + if self.pool is None: + return + # Disable access to the pool + old_pool, self.pool = self.pool, None + + try: + while True: + conn = old_pool.get(block=False) + if conn: + conn.close() + + except queue.Empty: + pass # Done. + + def is_same_host(self, url): + """ + Check if the given ``url`` is a member of the same host as this + connection pool. + """ + if url.startswith('/'): + return True + + # TODO: Add optional support for socket.gethostbyname checking. + scheme, host, port = get_host(url) + + host = _ipv6_host(host, self.scheme) + + # Use explicit default port for comparison when none is given + if self.port and not port: + port = port_by_scheme.get(scheme) + elif not self.port and port == port_by_scheme.get(scheme): + port = None + + return (scheme, host, port) == (self.scheme, self.host, self.port) + + def urlopen(self, method, url, body=None, headers=None, retries=None, + redirect=True, assert_same_host=True, timeout=_Default, + pool_timeout=None, release_conn=None, chunked=False, + body_pos=None, **response_kw): + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method provided + by :class:`.RequestMethods`, such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param body: + Data to send in the request body (useful for creating + POST requests, see HTTPConnectionPool.post_url for + more convenience). + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + Pass ``None`` to retry until you receive a response. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When False, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of + ``response_kw.get('preload_content', True)``. + + :param chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + + :param \\**response_kw: + Additional parameters are passed to + :meth:`urllib3.response.HTTPResponse.from_httplib` + """ + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = response_kw.get('preload_content', True) + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] <https://github.com/shazow/urllib3/issues/651> + release_this_conn = release_conn + + # Merge the proxy headers. Only do this in HTTP. We have to copy the + # headers dict so we can safely change it without those changes being + # reflected in anyone else's copy. + if self.scheme == 'http': + headers = headers.copy() + headers.update(self.proxy_headers) + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout + + is_new_proxy_conn = self.proxy is not None and not getattr(conn, 'sock', None) + if is_new_proxy_conn: + self._prepare_proxy(conn) + + # Make the request on the httplib connection object. + httplib_response = self._make_request(conn, method, url, + timeout=timeout_obj, + body=body, headers=headers, + chunked=chunked) + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Pass method to Response for length checking + response_kw['request_method'] = method + + # Import httplib's response into our own wrapper object + response = self.ResponseCls.from_httplib(httplib_response, + pool=self, + connection=response_conn, + retries=retries, + **response_kw) + + # Everything went great! + clean_exit = True + + except queue.Empty: + # Timed out by queue. + raise EmptyPoolError(self, "No pool connections are available.") + + except (TimeoutError, HTTPException, SocketError, ProtocolError, + BaseSSLError, SSLError, CertificateError) as e: + # Discard the connection for these exceptions. It will be + # replaced during the next _get_conn() call. + clean_exit = False + if isinstance(e, (BaseSSLError, CertificateError)): + e = SSLError(e) + elif isinstance(e, (SocketError, NewConnectionError)) and self.proxy: + e = ProxyError('Cannot connect to proxy.', e) + elif isinstance(e, (SocketError, HTTPException)): + e = ProtocolError('Connection aborted.', e) + + retries = retries.increment(method, url, error=e, _pool=self, + _stacktrace=sys.exc_info()[2]) + retries.sleep() + + # Keep track of the error for the retry warning. + err = e + + finally: + if not clean_exit: + # We hit some kind of exception, handled or otherwise. We need + # to throw the connection away unless explicitly told not to. + # Close the connection, set the variable to None, and make sure + # we put the None back in the pool to avoid leaking it. + conn = conn and conn.close() + release_this_conn = True + + if release_this_conn: + # Put the connection back to be reused. If the connection is + # expired then it will be None, which will get replaced with a + # fresh connection during _get_conn. + self._put_conn(conn) + + if not conn: + # Try again + log.warning("Retrying (%r) after connection " + "broken by '%r': %s", retries, err, url) + return self.urlopen(method, url, body, headers, retries, + redirect, assert_same_host, + timeout=timeout, pool_timeout=pool_timeout, + release_conn=release_conn, body_pos=body_pos, + **response_kw) + + def drain_and_release_conn(response): + try: + # discard any remaining response body, the connection will be + # released back to the pool once the entire response is read + response.read() + except (TimeoutError, HTTPException, SocketError, ProtocolError, + BaseSSLError, SSLError) as e: + pass + + # Handle redirect? + redirect_location = redirect and response.get_redirect_location() + if redirect_location: + if response.status == 303: + method = 'GET' + + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_redirect: + # Drain and release the connection for this response, since + # we're not returning it to be released manually. + drain_and_release_conn(response) + raise + return response + + # drain and return the connection to the pool before recursing + drain_and_release_conn(response) + + retries.sleep_for_retry(response) + log.debug("Redirecting %s -> %s", url, redirect_location) + return self.urlopen( + method, redirect_location, body, headers, + retries=retries, redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, pool_timeout=pool_timeout, + release_conn=release_conn, body_pos=body_pos, + **response_kw) + + # Check if we should retry the HTTP response. + has_retry_after = bool(response.getheader('Retry-After')) + if retries.is_retry(method, response.status, has_retry_after): + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_status: + # Drain and release the connection for this response, since + # we're not returning it to be released manually. + drain_and_release_conn(response) + raise + return response + + # drain and return the connection to the pool before recursing + drain_and_release_conn(response) + + retries.sleep(response) + log.debug("Retry: %s", url) + return self.urlopen( + method, url, body, headers, + retries=retries, redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, pool_timeout=pool_timeout, + release_conn=release_conn, + body_pos=body_pos, **response_kw) + + return response + + +class HTTPSConnectionPool(HTTPConnectionPool): + """ + Same as :class:`.HTTPConnectionPool`, but HTTPS. + + When Python is compiled with the :mod:`ssl` module, then + :class:`.VerifiedHTTPSConnection` is used, which *can* verify certificates, + instead of :class:`.HTTPSConnection`. + + :class:`.VerifiedHTTPSConnection` uses one of ``assert_fingerprint``, + ``assert_hostname`` and ``host`` in this order to verify connections. + If ``assert_hostname`` is False, no verification is done. + + The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``, + ``ca_cert_dir``, and ``ssl_version`` are only used if :mod:`ssl` is + available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade + the connection socket into an SSL socket. + """ + + scheme = 'https' + ConnectionCls = HTTPSConnection + + def __init__(self, host, port=None, + strict=False, timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, + block=False, headers=None, retries=None, + _proxy=None, _proxy_headers=None, + key_file=None, cert_file=None, cert_reqs=None, + ca_certs=None, ssl_version=None, + assert_hostname=None, assert_fingerprint=None, + ca_cert_dir=None, **conn_kw): + + HTTPConnectionPool.__init__(self, host, port, strict, timeout, maxsize, + block, headers, retries, _proxy, _proxy_headers, + **conn_kw) + + if ca_certs and cert_reqs is None: + cert_reqs = 'CERT_REQUIRED' + + self.key_file = key_file + self.cert_file = cert_file + self.cert_reqs = cert_reqs + self.ca_certs = ca_certs + self.ca_cert_dir = ca_cert_dir + self.ssl_version = ssl_version + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + + def _prepare_conn(self, conn): + """ + Prepare the ``connection`` for :meth:`urllib3.util.ssl_wrap_socket` + and establish the tunnel if proxy is used. + """ + + if isinstance(conn, VerifiedHTTPSConnection): + conn.set_cert(key_file=self.key_file, + cert_file=self.cert_file, + cert_reqs=self.cert_reqs, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + assert_hostname=self.assert_hostname, + assert_fingerprint=self.assert_fingerprint) + conn.ssl_version = self.ssl_version + return conn + + def _prepare_proxy(self, conn): + """ + Establish tunnel connection early, because otherwise httplib + would improperly set Host: header to proxy's IP:port. + """ + # Python 2.7+ + try: + set_tunnel = conn.set_tunnel + except AttributeError: # Platform-specific: Python 2.6 + set_tunnel = conn._set_tunnel + + if sys.version_info <= (2, 6, 4) and not self.proxy_headers: # Python 2.6.4 and older + set_tunnel(self._proxy_host, self.port) + else: + set_tunnel(self._proxy_host, self.port, self.proxy_headers) + + conn.connect() + + def _new_conn(self): + """ + Return a fresh :class:`httplib.HTTPSConnection`. + """ + self.num_connections += 1 + log.debug("Starting new HTTPS connection (%d): %s:%s", + self.num_connections, self.host, self.port or "443") + + if not self.ConnectionCls or self.ConnectionCls is DummyConnection: + raise SSLError("Can't connect to HTTPS URL because the SSL " + "module is not available.") + + actual_host = self.host + actual_port = self.port + if self.proxy is not None: + actual_host = self.proxy.host + actual_port = self.proxy.port + + conn = self.ConnectionCls(host=actual_host, port=actual_port, + timeout=self.timeout.connect_timeout, + strict=self.strict, **self.conn_kw) + + return self._prepare_conn(conn) + + def _validate_conn(self, conn): + """ + Called right before a request is made, after the socket is created. + """ + super(HTTPSConnectionPool, self)._validate_conn(conn) + + # Force connect early to allow us to validate the connection. + if not getattr(conn, 'sock', None): # AppEngine might not have `.sock` + conn.connect() + + if not conn.is_verified: + warnings.warn(( + 'Unverified HTTPS request is being made. ' + 'Adding certificate verification is strongly advised. See: ' + 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html' + '#ssl-warnings'), + InsecureRequestWarning) + + +def connection_from_url(url, **kw): + """ + Given a url, return an :class:`.ConnectionPool` instance of its host. + + This is a shortcut for not having to parse out the scheme, host, and port + of the url before creating an :class:`.ConnectionPool` instance. + + :param url: + Absolute URL string that must include the scheme. Port is optional. + + :param \\**kw: + Passes additional parameters to the constructor of the appropriate + :class:`.ConnectionPool`. Useful for specifying things like + timeout, maxsize, headers, etc. + + Example:: + + >>> conn = connection_from_url('http://google.com/') + >>> r = conn.request('GET', '/') + """ + scheme, host, port = get_host(url) + port = port or port_by_scheme.get(scheme, 80) + if scheme == 'https': + return HTTPSConnectionPool(host, port=port, **kw) + else: + return HTTPConnectionPool(host, port=port, **kw) + + +def _ipv6_host(host, scheme): + """ + Process IPv6 address literals + """ + + # httplib doesn't like it when we include brackets in IPv6 addresses + # Specifically, if we include brackets but also pass the port then + # httplib crazily doubles up the square brackets on the Host header. + # Instead, we need to make sure we never pass ``None`` as the port. + # However, for backward compatibility reasons we can't actually + # *assert* that. See http://bugs.python.org/issue28539 + # + # Also if an IPv6 address literal has a zone identifier, the + # percent sign might be URIencoded, convert it back into ASCII + if host.startswith('[') and host.endswith(']'): + host = host.replace('%25', '%').strip('[]') + if scheme in NORMALIZABLE_SCHEMES: + host = host.lower() + return host diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__init__.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py new file mode 100644 index 0000000..bcf41c0 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py @@ -0,0 +1,593 @@ +""" +This module uses ctypes to bind a whole bunch of functions and constants from +SecureTransport. The goal here is to provide the low-level API to +SecureTransport. These are essentially the C-level functions and constants, and +they're pretty gross to work with. + +This code is a bastardised version of the code found in Will Bond's oscrypto +library. An enormous debt is owed to him for blazing this trail for us. For +that reason, this code should be considered to be covered both by urllib3's +license and by oscrypto's: + + Copyright (c) 2015-2016 Will Bond <will@wbond.net> + + 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. +""" +from __future__ import absolute_import + +import platform +from ctypes.util import find_library +from ctypes import ( + c_void_p, c_int32, c_char_p, c_size_t, c_byte, c_uint32, c_ulong, c_long, + c_bool +) +from ctypes import CDLL, POINTER, CFUNCTYPE + + +security_path = find_library('Security') +if not security_path: + raise ImportError('The library Security could not be found') + + +core_foundation_path = find_library('CoreFoundation') +if not core_foundation_path: + raise ImportError('The library CoreFoundation could not be found') + + +version = platform.mac_ver()[0] +version_info = tuple(map(int, version.split('.'))) +if version_info < (10, 8): + raise OSError( + 'Only OS X 10.8 and newer are supported, not %s.%s' % ( + version_info[0], version_info[1] + ) + ) + +Security = CDLL(security_path, use_errno=True) +CoreFoundation = CDLL(core_foundation_path, use_errno=True) + +Boolean = c_bool +CFIndex = c_long +CFStringEncoding = c_uint32 +CFData = c_void_p +CFString = c_void_p +CFArray = c_void_p +CFMutableArray = c_void_p +CFDictionary = c_void_p +CFError = c_void_p +CFType = c_void_p +CFTypeID = c_ulong + +CFTypeRef = POINTER(CFType) +CFAllocatorRef = c_void_p + +OSStatus = c_int32 + +CFDataRef = POINTER(CFData) +CFStringRef = POINTER(CFString) +CFArrayRef = POINTER(CFArray) +CFMutableArrayRef = POINTER(CFMutableArray) +CFDictionaryRef = POINTER(CFDictionary) +CFArrayCallBacks = c_void_p +CFDictionaryKeyCallBacks = c_void_p +CFDictionaryValueCallBacks = c_void_p + +SecCertificateRef = POINTER(c_void_p) +SecExternalFormat = c_uint32 +SecExternalItemType = c_uint32 +SecIdentityRef = POINTER(c_void_p) +SecItemImportExportFlags = c_uint32 +SecItemImportExportKeyParameters = c_void_p +SecKeychainRef = POINTER(c_void_p) +SSLProtocol = c_uint32 +SSLCipherSuite = c_uint32 +SSLContextRef = POINTER(c_void_p) +SecTrustRef = POINTER(c_void_p) +SSLConnectionRef = c_uint32 +SecTrustResultType = c_uint32 +SecTrustOptionFlags = c_uint32 +SSLProtocolSide = c_uint32 +SSLConnectionType = c_uint32 +SSLSessionOption = c_uint32 + + +try: + Security.SecItemImport.argtypes = [ + CFDataRef, + CFStringRef, + POINTER(SecExternalFormat), + POINTER(SecExternalItemType), + SecItemImportExportFlags, + POINTER(SecItemImportExportKeyParameters), + SecKeychainRef, + POINTER(CFArrayRef), + ] + Security.SecItemImport.restype = OSStatus + + Security.SecCertificateGetTypeID.argtypes = [] + Security.SecCertificateGetTypeID.restype = CFTypeID + + Security.SecIdentityGetTypeID.argtypes = [] + Security.SecIdentityGetTypeID.restype = CFTypeID + + Security.SecKeyGetTypeID.argtypes = [] + Security.SecKeyGetTypeID.restype = CFTypeID + + Security.SecCertificateCreateWithData.argtypes = [ + CFAllocatorRef, + CFDataRef + ] + Security.SecCertificateCreateWithData.restype = SecCertificateRef + + Security.SecCertificateCopyData.argtypes = [ + SecCertificateRef + ] + Security.SecCertificateCopyData.restype = CFDataRef + + Security.SecCopyErrorMessageString.argtypes = [ + OSStatus, + c_void_p + ] + Security.SecCopyErrorMessageString.restype = CFStringRef + + Security.SecIdentityCreateWithCertificate.argtypes = [ + CFTypeRef, + SecCertificateRef, + POINTER(SecIdentityRef) + ] + Security.SecIdentityCreateWithCertificate.restype = OSStatus + + Security.SecKeychainCreate.argtypes = [ + c_char_p, + c_uint32, + c_void_p, + Boolean, + c_void_p, + POINTER(SecKeychainRef) + ] + Security.SecKeychainCreate.restype = OSStatus + + Security.SecKeychainDelete.argtypes = [ + SecKeychainRef + ] + Security.SecKeychainDelete.restype = OSStatus + + Security.SecPKCS12Import.argtypes = [ + CFDataRef, + CFDictionaryRef, + POINTER(CFArrayRef) + ] + Security.SecPKCS12Import.restype = OSStatus + + SSLReadFunc = CFUNCTYPE(OSStatus, SSLConnectionRef, c_void_p, POINTER(c_size_t)) + SSLWriteFunc = CFUNCTYPE(OSStatus, SSLConnectionRef, POINTER(c_byte), POINTER(c_size_t)) + + Security.SSLSetIOFuncs.argtypes = [ + SSLContextRef, + SSLReadFunc, + SSLWriteFunc + ] + Security.SSLSetIOFuncs.restype = OSStatus + + Security.SSLSetPeerID.argtypes = [ + SSLContextRef, + c_char_p, + c_size_t + ] + Security.SSLSetPeerID.restype = OSStatus + + Security.SSLSetCertificate.argtypes = [ + SSLContextRef, + CFArrayRef + ] + Security.SSLSetCertificate.restype = OSStatus + + Security.SSLSetCertificateAuthorities.argtypes = [ + SSLContextRef, + CFTypeRef, + Boolean + ] + Security.SSLSetCertificateAuthorities.restype = OSStatus + + Security.SSLSetConnection.argtypes = [ + SSLContextRef, + SSLConnectionRef + ] + Security.SSLSetConnection.restype = OSStatus + + Security.SSLSetPeerDomainName.argtypes = [ + SSLContextRef, + c_char_p, + c_size_t + ] + Security.SSLSetPeerDomainName.restype = OSStatus + + Security.SSLHandshake.argtypes = [ + SSLContextRef + ] + Security.SSLHandshake.restype = OSStatus + + Security.SSLRead.argtypes = [ + SSLContextRef, + c_char_p, + c_size_t, + POINTER(c_size_t) + ] + Security.SSLRead.restype = OSStatus + + Security.SSLWrite.argtypes = [ + SSLContextRef, + c_char_p, + c_size_t, + POINTER(c_size_t) + ] + Security.SSLWrite.restype = OSStatus + + Security.SSLClose.argtypes = [ + SSLContextRef + ] + Security.SSLClose.restype = OSStatus + + Security.SSLGetNumberSupportedCiphers.argtypes = [ + SSLContextRef, + POINTER(c_size_t) + ] + Security.SSLGetNumberSupportedCiphers.restype = OSStatus + + Security.SSLGetSupportedCiphers.argtypes = [ + SSLContextRef, + POINTER(SSLCipherSuite), + POINTER(c_size_t) + ] + Security.SSLGetSupportedCiphers.restype = OSStatus + + Security.SSLSetEnabledCiphers.argtypes = [ + SSLContextRef, + POINTER(SSLCipherSuite), + c_size_t + ] + Security.SSLSetEnabledCiphers.restype = OSStatus + + Security.SSLGetNumberEnabledCiphers.argtype = [ + SSLContextRef, + POINTER(c_size_t) + ] + Security.SSLGetNumberEnabledCiphers.restype = OSStatus + + Security.SSLGetEnabledCiphers.argtypes = [ + SSLContextRef, + POINTER(SSLCipherSuite), + POINTER(c_size_t) + ] + Security.SSLGetEnabledCiphers.restype = OSStatus + + Security.SSLGetNegotiatedCipher.argtypes = [ + SSLContextRef, + POINTER(SSLCipherSuite) + ] + Security.SSLGetNegotiatedCipher.restype = OSStatus + + Security.SSLGetNegotiatedProtocolVersion.argtypes = [ + SSLContextRef, + POINTER(SSLProtocol) + ] + Security.SSLGetNegotiatedProtocolVersion.restype = OSStatus + + Security.SSLCopyPeerTrust.argtypes = [ + SSLContextRef, + POINTER(SecTrustRef) + ] + Security.SSLCopyPeerTrust.restype = OSStatus + + Security.SecTrustSetAnchorCertificates.argtypes = [ + SecTrustRef, + CFArrayRef + ] + Security.SecTrustSetAnchorCertificates.restype = OSStatus + + Security.SecTrustSetAnchorCertificatesOnly.argstypes = [ + SecTrustRef, + Boolean + ] + Security.SecTrustSetAnchorCertificatesOnly.restype = OSStatus + + Security.SecTrustEvaluate.argtypes = [ + SecTrustRef, + POINTER(SecTrustResultType) + ] + Security.SecTrustEvaluate.restype = OSStatus + + Security.SecTrustGetCertificateCount.argtypes = [ + SecTrustRef + ] + Security.SecTrustGetCertificateCount.restype = CFIndex + + Security.SecTrustGetCertificateAtIndex.argtypes = [ + SecTrustRef, + CFIndex + ] + Security.SecTrustGetCertificateAtIndex.restype = SecCertificateRef + + Security.SSLCreateContext.argtypes = [ + CFAllocatorRef, + SSLProtocolSide, + SSLConnectionType + ] + Security.SSLCreateContext.restype = SSLContextRef + + Security.SSLSetSessionOption.argtypes = [ + SSLContextRef, + SSLSessionOption, + Boolean + ] + Security.SSLSetSessionOption.restype = OSStatus + + Security.SSLSetProtocolVersionMin.argtypes = [ + SSLContextRef, + SSLProtocol + ] + Security.SSLSetProtocolVersionMin.restype = OSStatus + + Security.SSLSetProtocolVersionMax.argtypes = [ + SSLContextRef, + SSLProtocol + ] + Security.SSLSetProtocolVersionMax.restype = OSStatus + + Security.SecCopyErrorMessageString.argtypes = [ + OSStatus, + c_void_p + ] + Security.SecCopyErrorMessageString.restype = CFStringRef + + Security.SSLReadFunc = SSLReadFunc + Security.SSLWriteFunc = SSLWriteFunc + Security.SSLContextRef = SSLContextRef + Security.SSLProtocol = SSLProtocol + Security.SSLCipherSuite = SSLCipherSuite + Security.SecIdentityRef = SecIdentityRef + Security.SecKeychainRef = SecKeychainRef + Security.SecTrustRef = SecTrustRef + Security.SecTrustResultType = SecTrustResultType + Security.SecExternalFormat = SecExternalFormat + Security.OSStatus = OSStatus + + Security.kSecImportExportPassphrase = CFStringRef.in_dll( + Security, 'kSecImportExportPassphrase' + ) + Security.kSecImportItemIdentity = CFStringRef.in_dll( + Security, 'kSecImportItemIdentity' + ) + + # CoreFoundation time! + CoreFoundation.CFRetain.argtypes = [ + CFTypeRef + ] + CoreFoundation.CFRetain.restype = CFTypeRef + + CoreFoundation.CFRelease.argtypes = [ + CFTypeRef + ] + CoreFoundation.CFRelease.restype = None + + CoreFoundation.CFGetTypeID.argtypes = [ + CFTypeRef + ] + CoreFoundation.CFGetTypeID.restype = CFTypeID + + CoreFoundation.CFStringCreateWithCString.argtypes = [ + CFAllocatorRef, + c_char_p, + CFStringEncoding + ] + CoreFoundation.CFStringCreateWithCString.restype = CFStringRef + + CoreFoundation.CFStringGetCStringPtr.argtypes = [ + CFStringRef, + CFStringEncoding + ] + CoreFoundation.CFStringGetCStringPtr.restype = c_char_p + + CoreFoundation.CFStringGetCString.argtypes = [ + CFStringRef, + c_char_p, + CFIndex, + CFStringEncoding + ] + CoreFoundation.CFStringGetCString.restype = c_bool + + CoreFoundation.CFDataCreate.argtypes = [ + CFAllocatorRef, + c_char_p, + CFIndex + ] + CoreFoundation.CFDataCreate.restype = CFDataRef + + CoreFoundation.CFDataGetLength.argtypes = [ + CFDataRef + ] + CoreFoundation.CFDataGetLength.restype = CFIndex + + CoreFoundation.CFDataGetBytePtr.argtypes = [ + CFDataRef + ] + CoreFoundation.CFDataGetBytePtr.restype = c_void_p + + CoreFoundation.CFDictionaryCreate.argtypes = [ + CFAllocatorRef, + POINTER(CFTypeRef), + POINTER(CFTypeRef), + CFIndex, + CFDictionaryKeyCallBacks, + CFDictionaryValueCallBacks + ] + CoreFoundation.CFDictionaryCreate.restype = CFDictionaryRef + + CoreFoundation.CFDictionaryGetValue.argtypes = [ + CFDictionaryRef, + CFTypeRef + ] + CoreFoundation.CFDictionaryGetValue.restype = CFTypeRef + + CoreFoundation.CFArrayCreate.argtypes = [ + CFAllocatorRef, + POINTER(CFTypeRef), + CFIndex, + CFArrayCallBacks, + ] + CoreFoundation.CFArrayCreate.restype = CFArrayRef + + CoreFoundation.CFArrayCreateMutable.argtypes = [ + CFAllocatorRef, + CFIndex, + CFArrayCallBacks + ] + CoreFoundation.CFArrayCreateMutable.restype = CFMutableArrayRef + + CoreFoundation.CFArrayAppendValue.argtypes = [ + CFMutableArrayRef, + c_void_p + ] + CoreFoundation.CFArrayAppendValue.restype = None + + CoreFoundation.CFArrayGetCount.argtypes = [ + CFArrayRef + ] + CoreFoundation.CFArrayGetCount.restype = CFIndex + + CoreFoundation.CFArrayGetValueAtIndex.argtypes = [ + CFArrayRef, + CFIndex + ] + CoreFoundation.CFArrayGetValueAtIndex.restype = c_void_p + + CoreFoundation.kCFAllocatorDefault = CFAllocatorRef.in_dll( + CoreFoundation, 'kCFAllocatorDefault' + ) + CoreFoundation.kCFTypeArrayCallBacks = c_void_p.in_dll(CoreFoundation, 'kCFTypeArrayCallBacks') + CoreFoundation.kCFTypeDictionaryKeyCallBacks = c_void_p.in_dll( + CoreFoundation, 'kCFTypeDictionaryKeyCallBacks' + ) + CoreFoundation.kCFTypeDictionaryValueCallBacks = c_void_p.in_dll( + CoreFoundation, 'kCFTypeDictionaryValueCallBacks' + ) + + CoreFoundation.CFTypeRef = CFTypeRef + CoreFoundation.CFArrayRef = CFArrayRef + CoreFoundation.CFStringRef = CFStringRef + CoreFoundation.CFDictionaryRef = CFDictionaryRef + +except (AttributeError): + raise ImportError('Error initializing ctypes') + + +class CFConst(object): + """ + A class object that acts as essentially a namespace for CoreFoundation + constants. + """ + kCFStringEncodingUTF8 = CFStringEncoding(0x08000100) + + +class SecurityConst(object): + """ + A class object that acts as essentially a namespace for Security constants. + """ + kSSLSessionOptionBreakOnServerAuth = 0 + + kSSLProtocol2 = 1 + kSSLProtocol3 = 2 + kTLSProtocol1 = 4 + kTLSProtocol11 = 7 + kTLSProtocol12 = 8 + + kSSLClientSide = 1 + kSSLStreamType = 0 + + kSecFormatPEMSequence = 10 + + kSecTrustResultInvalid = 0 + kSecTrustResultProceed = 1 + # This gap is present on purpose: this was kSecTrustResultConfirm, which + # is deprecated. + kSecTrustResultDeny = 3 + kSecTrustResultUnspecified = 4 + kSecTrustResultRecoverableTrustFailure = 5 + kSecTrustResultFatalTrustFailure = 6 + kSecTrustResultOtherError = 7 + + errSSLProtocol = -9800 + errSSLWouldBlock = -9803 + errSSLClosedGraceful = -9805 + errSSLClosedNoNotify = -9816 + errSSLClosedAbort = -9806 + + errSSLXCertChainInvalid = -9807 + errSSLCrypto = -9809 + errSSLInternal = -9810 + errSSLCertExpired = -9814 + errSSLCertNotYetValid = -9815 + errSSLUnknownRootCert = -9812 + errSSLNoRootCert = -9813 + errSSLHostNameMismatch = -9843 + errSSLPeerHandshakeFail = -9824 + errSSLPeerUserCancelled = -9839 + errSSLWeakPeerEphemeralDHKey = -9850 + errSSLServerAuthCompleted = -9841 + errSSLRecordOverflow = -9847 + + errSecVerifyFailed = -67808 + errSecNoTrustSettings = -25263 + errSecItemNotFound = -25300 + errSecInvalidTrustSettings = -25262 + + # Cipher suites. We only pick the ones our default cipher string allows. + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030 + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F + TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3 + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F + TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2 + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024 + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028 + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014 + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B + TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A + TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039 + TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013 + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067 + TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040 + TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033 + TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032 + TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D + TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C + TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D + TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C + TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035 + TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F + TLS_AES_128_GCM_SHA256 = 0x1301 + TLS_AES_256_GCM_SHA384 = 0x1302 + TLS_CHACHA20_POLY1305_SHA256 = 0x1303 diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py new file mode 100644 index 0000000..b13cd9e --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py @@ -0,0 +1,346 @@ +""" +Low-level helpers for the SecureTransport bindings. + +These are Python functions that are not directly related to the high-level APIs +but are necessary to get them to work. They include a whole bunch of low-level +CoreFoundation messing about and memory management. The concerns in this module +are almost entirely about trying to avoid memory leaks and providing +appropriate and useful assistance to the higher-level code. +""" +import base64 +import ctypes +import itertools +import re +import os +import ssl +import tempfile + +from .bindings import Security, CoreFoundation, CFConst + + +# This regular expression is used to grab PEM data out of a PEM bundle. +_PEM_CERTS_RE = re.compile( + b"-----BEGIN CERTIFICATE-----\n(.*?)\n-----END CERTIFICATE-----", re.DOTALL +) + + +def _cf_data_from_bytes(bytestring): + """ + Given a bytestring, create a CFData object from it. This CFData object must + be CFReleased by the caller. + """ + return CoreFoundation.CFDataCreate( + CoreFoundation.kCFAllocatorDefault, bytestring, len(bytestring) + ) + + +def _cf_dictionary_from_tuples(tuples): + """ + Given a list of Python tuples, create an associated CFDictionary. + """ + dictionary_size = len(tuples) + + # We need to get the dictionary keys and values out in the same order. + keys = (t[0] for t in tuples) + values = (t[1] for t in tuples) + cf_keys = (CoreFoundation.CFTypeRef * dictionary_size)(*keys) + cf_values = (CoreFoundation.CFTypeRef * dictionary_size)(*values) + + return CoreFoundation.CFDictionaryCreate( + CoreFoundation.kCFAllocatorDefault, + cf_keys, + cf_values, + dictionary_size, + CoreFoundation.kCFTypeDictionaryKeyCallBacks, + CoreFoundation.kCFTypeDictionaryValueCallBacks, + ) + + +def _cf_string_to_unicode(value): + """ + Creates a Unicode string from a CFString object. Used entirely for error + reporting. + + Yes, it annoys me quite a lot that this function is this complex. + """ + value_as_void_p = ctypes.cast(value, ctypes.POINTER(ctypes.c_void_p)) + + string = CoreFoundation.CFStringGetCStringPtr( + value_as_void_p, + CFConst.kCFStringEncodingUTF8 + ) + if string is None: + buffer = ctypes.create_string_buffer(1024) + result = CoreFoundation.CFStringGetCString( + value_as_void_p, + buffer, + 1024, + CFConst.kCFStringEncodingUTF8 + ) + if not result: + raise OSError('Error copying C string from CFStringRef') + string = buffer.value + if string is not None: + string = string.decode('utf-8') + return string + + +def _assert_no_error(error, exception_class=None): + """ + Checks the return code and throws an exception if there is an error to + report + """ + if error == 0: + return + + cf_error_string = Security.SecCopyErrorMessageString(error, None) + output = _cf_string_to_unicode(cf_error_string) + CoreFoundation.CFRelease(cf_error_string) + + if output is None or output == u'': + output = u'OSStatus %s' % error + + if exception_class is None: + exception_class = ssl.SSLError + + raise exception_class(output) + + +def _cert_array_from_pem(pem_bundle): + """ + Given a bundle of certs in PEM format, turns them into a CFArray of certs + that can be used to validate a cert chain. + """ + # Normalize the PEM bundle's line endings. + pem_bundle = pem_bundle.replace(b"\r\n", b"\n") + + der_certs = [ + base64.b64decode(match.group(1)) + for match in _PEM_CERTS_RE.finditer(pem_bundle) + ] + if not der_certs: + raise ssl.SSLError("No root certificates specified") + + cert_array = CoreFoundation.CFArrayCreateMutable( + CoreFoundation.kCFAllocatorDefault, + 0, + ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks) + ) + if not cert_array: + raise ssl.SSLError("Unable to allocate memory!") + + try: + for der_bytes in der_certs: + certdata = _cf_data_from_bytes(der_bytes) + if not certdata: + raise ssl.SSLError("Unable to allocate memory!") + cert = Security.SecCertificateCreateWithData( + CoreFoundation.kCFAllocatorDefault, certdata + ) + CoreFoundation.CFRelease(certdata) + if not cert: + raise ssl.SSLError("Unable to build cert object!") + + CoreFoundation.CFArrayAppendValue(cert_array, cert) + CoreFoundation.CFRelease(cert) + except Exception: + # We need to free the array before the exception bubbles further. + # We only want to do that if an error occurs: otherwise, the caller + # should free. + CoreFoundation.CFRelease(cert_array) + + return cert_array + + +def _is_cert(item): + """ + Returns True if a given CFTypeRef is a certificate. + """ + expected = Security.SecCertificateGetTypeID() + return CoreFoundation.CFGetTypeID(item) == expected + + +def _is_identity(item): + """ + Returns True if a given CFTypeRef is an identity. + """ + expected = Security.SecIdentityGetTypeID() + return CoreFoundation.CFGetTypeID(item) == expected + + +def _temporary_keychain(): + """ + This function creates a temporary Mac keychain that we can use to work with + credentials. This keychain uses a one-time password and a temporary file to + store the data. We expect to have one keychain per socket. The returned + SecKeychainRef must be freed by the caller, including calling + SecKeychainDelete. + + Returns a tuple of the SecKeychainRef and the path to the temporary + directory that contains it. + """ + # Unfortunately, SecKeychainCreate requires a path to a keychain. This + # means we cannot use mkstemp to use a generic temporary file. Instead, + # we're going to create a temporary directory and a filename to use there. + # This filename will be 8 random bytes expanded into base64. We also need + # some random bytes to password-protect the keychain we're creating, so we + # ask for 40 random bytes. + random_bytes = os.urandom(40) + filename = base64.b16encode(random_bytes[:8]).decode('utf-8') + password = base64.b16encode(random_bytes[8:]) # Must be valid UTF-8 + tempdirectory = tempfile.mkdtemp() + + keychain_path = os.path.join(tempdirectory, filename).encode('utf-8') + + # We now want to create the keychain itself. + keychain = Security.SecKeychainRef() + status = Security.SecKeychainCreate( + keychain_path, + len(password), + password, + False, + None, + ctypes.byref(keychain) + ) + _assert_no_error(status) + + # Having created the keychain, we want to pass it off to the caller. + return keychain, tempdirectory + + +def _load_items_from_file(keychain, path): + """ + Given a single file, loads all the trust objects from it into arrays and + the keychain. + Returns a tuple of lists: the first list is a list of identities, the + second a list of certs. + """ + certificates = [] + identities = [] + result_array = None + + with open(path, 'rb') as f: + raw_filedata = f.read() + + try: + filedata = CoreFoundation.CFDataCreate( + CoreFoundation.kCFAllocatorDefault, + raw_filedata, + len(raw_filedata) + ) + result_array = CoreFoundation.CFArrayRef() + result = Security.SecItemImport( + filedata, # cert data + None, # Filename, leaving it out for now + None, # What the type of the file is, we don't care + None, # what's in the file, we don't care + 0, # import flags + None, # key params, can include passphrase in the future + keychain, # The keychain to insert into + ctypes.byref(result_array) # Results + ) + _assert_no_error(result) + + # A CFArray is not very useful to us as an intermediary + # representation, so we are going to extract the objects we want + # and then free the array. We don't need to keep hold of keys: the + # keychain already has them! + result_count = CoreFoundation.CFArrayGetCount(result_array) + for index in range(result_count): + item = CoreFoundation.CFArrayGetValueAtIndex( + result_array, index + ) + item = ctypes.cast(item, CoreFoundation.CFTypeRef) + + if _is_cert(item): + CoreFoundation.CFRetain(item) + certificates.append(item) + elif _is_identity(item): + CoreFoundation.CFRetain(item) + identities.append(item) + finally: + if result_array: + CoreFoundation.CFRelease(result_array) + + CoreFoundation.CFRelease(filedata) + + return (identities, certificates) + + +def _load_client_cert_chain(keychain, *paths): + """ + Load certificates and maybe keys from a number of files. Has the end goal + of returning a CFArray containing one SecIdentityRef, and then zero or more + SecCertificateRef objects, suitable for use as a client certificate trust + chain. + """ + # Ok, the strategy. + # + # This relies on knowing that macOS will not give you a SecIdentityRef + # unless you have imported a key into a keychain. This is a somewhat + # artificial limitation of macOS (for example, it doesn't necessarily + # affect iOS), but there is nothing inside Security.framework that lets you + # get a SecIdentityRef without having a key in a keychain. + # + # So the policy here is we take all the files and iterate them in order. + # Each one will use SecItemImport to have one or more objects loaded from + # it. We will also point at a keychain that macOS can use to work with the + # private key. + # + # Once we have all the objects, we'll check what we actually have. If we + # already have a SecIdentityRef in hand, fab: we'll use that. Otherwise, + # we'll take the first certificate (which we assume to be our leaf) and + # ask the keychain to give us a SecIdentityRef with that cert's associated + # key. + # + # We'll then return a CFArray containing the trust chain: one + # SecIdentityRef and then zero-or-more SecCertificateRef objects. The + # responsibility for freeing this CFArray will be with the caller. This + # CFArray must remain alive for the entire connection, so in practice it + # will be stored with a single SSLSocket, along with the reference to the + # keychain. + certificates = [] + identities = [] + + # Filter out bad paths. + paths = (path for path in paths if path) + + try: + for file_path in paths: + new_identities, new_certs = _load_items_from_file( + keychain, file_path + ) + identities.extend(new_identities) + certificates.extend(new_certs) + + # Ok, we have everything. The question is: do we have an identity? If + # not, we want to grab one from the first cert we have. + if not identities: + new_identity = Security.SecIdentityRef() + status = Security.SecIdentityCreateWithCertificate( + keychain, + certificates[0], + ctypes.byref(new_identity) + ) + _assert_no_error(status) + identities.append(new_identity) + + # We now want to release the original certificate, as we no longer + # need it. + CoreFoundation.CFRelease(certificates.pop(0)) + + # We now need to build a new CFArray that holds the trust chain. + trust_chain = CoreFoundation.CFArrayCreateMutable( + CoreFoundation.kCFAllocatorDefault, + 0, + ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks), + ) + for item in itertools.chain(identities, certificates): + # ArrayAppendValue does a CFRetain on the item. That's fine, + # because the finally block will release our other refs to them. + CoreFoundation.CFArrayAppendValue(trust_chain, item) + + return trust_chain + finally: + for obj in itertools.chain(identities, certificates): + CoreFoundation.CFRelease(obj) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py new file mode 100644 index 0000000..59f2a61 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py @@ -0,0 +1,305 @@ +""" +This module provides a pool manager that uses Google App Engine's +`URLFetch Service <https://cloud.google.com/appengine/docs/python/urlfetch>`_. + +Example usage:: + + from pip._vendor.urllib3 import PoolManager + from pip._vendor.urllib3.contrib.appengine import AppEngineManager, is_appengine_sandbox + + if is_appengine_sandbox(): + # AppEngineManager uses AppEngine's URLFetch API behind the scenes + http = AppEngineManager() + else: + # PoolManager uses a socket-level API behind the scenes + http = PoolManager() + + r = http.request('GET', 'https://google.com/') + +There are `limitations <https://cloud.google.com/appengine/docs/python/\ +urlfetch/#Python_Quotas_and_limits>`_ to the URLFetch service and it may not be +the best choice for your application. There are three options for using +urllib3 on Google App Engine: + +1. You can use :class:`AppEngineManager` with URLFetch. URLFetch is + cost-effective in many circumstances as long as your usage is within the + limitations. +2. You can use a normal :class:`~urllib3.PoolManager` by enabling sockets. + Sockets also have `limitations and restrictions + <https://cloud.google.com/appengine/docs/python/sockets/\ + #limitations-and-restrictions>`_ and have a lower free quota than URLFetch. + To use sockets, be sure to specify the following in your ``app.yaml``:: + + env_variables: + GAE_USE_SOCKETS_HTTPLIB : 'true' + +3. If you are using `App Engine Flexible +<https://cloud.google.com/appengine/docs/flexible/>`_, you can use the standard +:class:`PoolManager` without any configuration or special environment variables. +""" + +from __future__ import absolute_import +import logging +import os +import warnings +from ..packages.six.moves.urllib.parse import urljoin + +from ..exceptions import ( + HTTPError, + HTTPWarning, + MaxRetryError, + ProtocolError, + TimeoutError, + SSLError +) + +from ..packages.six import BytesIO +from ..request import RequestMethods +from ..response import HTTPResponse +from ..util.timeout import Timeout +from ..util.retry import Retry + +try: + from google.appengine.api import urlfetch +except ImportError: + urlfetch = None + + +log = logging.getLogger(__name__) + + +class AppEnginePlatformWarning(HTTPWarning): + pass + + +class AppEnginePlatformError(HTTPError): + pass + + +class AppEngineManager(RequestMethods): + """ + Connection manager for Google App Engine sandbox applications. + + This manager uses the URLFetch service directly instead of using the + emulated httplib, and is subject to URLFetch limitations as described in + the App Engine documentation `here + <https://cloud.google.com/appengine/docs/python/urlfetch>`_. + + Notably it will raise an :class:`AppEnginePlatformError` if: + * URLFetch is not available. + * If you attempt to use this on App Engine Flexible, as full socket + support is available. + * If a request size is more than 10 megabytes. + * If a response size is more than 32 megabtyes. + * If you use an unsupported request method such as OPTIONS. + + Beyond those cases, it will raise normal urllib3 errors. + """ + + def __init__(self, headers=None, retries=None, validate_certificate=True, + urlfetch_retries=True): + if not urlfetch: + raise AppEnginePlatformError( + "URLFetch is not available in this environment.") + + if is_prod_appengine_mvms(): + raise AppEnginePlatformError( + "Use normal urllib3.PoolManager instead of AppEngineManager" + "on Managed VMs, as using URLFetch is not necessary in " + "this environment.") + + warnings.warn( + "urllib3 is using URLFetch on Google App Engine sandbox instead " + "of sockets. To use sockets directly instead of URLFetch see " + "https://urllib3.readthedocs.io/en/latest/reference/urllib3.contrib.html.", + AppEnginePlatformWarning) + + RequestMethods.__init__(self, headers) + self.validate_certificate = validate_certificate + self.urlfetch_retries = urlfetch_retries + + self.retries = retries or Retry.DEFAULT + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + # Return False to re-raise any potential exceptions + return False + + def urlopen(self, method, url, body=None, headers=None, + retries=None, redirect=True, timeout=Timeout.DEFAULT_TIMEOUT, + **response_kw): + + retries = self._get_retries(retries, redirect) + + try: + follow_redirects = ( + redirect and + retries.redirect != 0 and + retries.total) + response = urlfetch.fetch( + url, + payload=body, + method=method, + headers=headers or {}, + allow_truncated=False, + follow_redirects=self.urlfetch_retries and follow_redirects, + deadline=self._get_absolute_timeout(timeout), + validate_certificate=self.validate_certificate, + ) + except urlfetch.DeadlineExceededError as e: + raise TimeoutError(self, e) + + except urlfetch.InvalidURLError as e: + if 'too large' in str(e): + raise AppEnginePlatformError( + "URLFetch request too large, URLFetch only " + "supports requests up to 10mb in size.", e) + raise ProtocolError(e) + + except urlfetch.DownloadError as e: + if 'Too many redirects' in str(e): + raise MaxRetryError(self, url, reason=e) + raise ProtocolError(e) + + except urlfetch.ResponseTooLargeError as e: + raise AppEnginePlatformError( + "URLFetch response too large, URLFetch only supports" + "responses up to 32mb in size.", e) + + except urlfetch.SSLCertificateError as e: + raise SSLError(e) + + except urlfetch.InvalidMethodError as e: + raise AppEnginePlatformError( + "URLFetch does not support method: %s" % method, e) + + http_response = self._urlfetch_response_to_http_response( + response, retries=retries, **response_kw) + + # Handle redirect? + redirect_location = redirect and http_response.get_redirect_location() + if redirect_location: + # Check for redirect response + if (self.urlfetch_retries and retries.raise_on_redirect): + raise MaxRetryError(self, url, "too many redirects") + else: + if http_response.status == 303: + method = 'GET' + + try: + retries = retries.increment(method, url, response=http_response, _pool=self) + except MaxRetryError: + if retries.raise_on_redirect: + raise MaxRetryError(self, url, "too many redirects") + return http_response + + retries.sleep_for_retry(http_response) + log.debug("Redirecting %s -> %s", url, redirect_location) + redirect_url = urljoin(url, redirect_location) + return self.urlopen( + method, redirect_url, body, headers, + retries=retries, redirect=redirect, + timeout=timeout, **response_kw) + + # Check if we should retry the HTTP response. + has_retry_after = bool(http_response.getheader('Retry-After')) + if retries.is_retry(method, http_response.status, has_retry_after): + retries = retries.increment( + method, url, response=http_response, _pool=self) + log.debug("Retry: %s", url) + retries.sleep(http_response) + return self.urlopen( + method, url, + body=body, headers=headers, + retries=retries, redirect=redirect, + timeout=timeout, **response_kw) + + return http_response + + def _urlfetch_response_to_http_response(self, urlfetch_resp, **response_kw): + + if is_prod_appengine(): + # Production GAE handles deflate encoding automatically, but does + # not remove the encoding header. + content_encoding = urlfetch_resp.headers.get('content-encoding') + + if content_encoding == 'deflate': + del urlfetch_resp.headers['content-encoding'] + + transfer_encoding = urlfetch_resp.headers.get('transfer-encoding') + # We have a full response's content, + # so let's make sure we don't report ourselves as chunked data. + if transfer_encoding == 'chunked': + encodings = transfer_encoding.split(",") + encodings.remove('chunked') + urlfetch_resp.headers['transfer-encoding'] = ','.join(encodings) + + original_response = HTTPResponse( + # In order for decoding to work, we must present the content as + # a file-like object. + body=BytesIO(urlfetch_resp.content), + msg=urlfetch_resp.header_msg, + headers=urlfetch_resp.headers, + status=urlfetch_resp.status_code, + **response_kw + ) + + return HTTPResponse( + body=BytesIO(urlfetch_resp.content), + headers=urlfetch_resp.headers, + status=urlfetch_resp.status_code, + original_response=original_response, + **response_kw + ) + + def _get_absolute_timeout(self, timeout): + if timeout is Timeout.DEFAULT_TIMEOUT: + return None # Defer to URLFetch's default. + if isinstance(timeout, Timeout): + if timeout._read is not None or timeout._connect is not None: + warnings.warn( + "URLFetch does not support granular timeout settings, " + "reverting to total or default URLFetch timeout.", + AppEnginePlatformWarning) + return timeout.total + return timeout + + def _get_retries(self, retries, redirect): + if not isinstance(retries, Retry): + retries = Retry.from_int( + retries, redirect=redirect, default=self.retries) + + if retries.connect or retries.read or retries.redirect: + warnings.warn( + "URLFetch only supports total retries and does not " + "recognize connect, read, or redirect retry parameters.", + AppEnginePlatformWarning) + + return retries + + +def is_appengine(): + return (is_local_appengine() or + is_prod_appengine() or + is_prod_appengine_mvms()) + + +def is_appengine_sandbox(): + return is_appengine() and not is_prod_appengine_mvms() + + +def is_local_appengine(): + return ('APPENGINE_RUNTIME' in os.environ and + 'Development/' in os.environ['SERVER_SOFTWARE']) + + +def is_prod_appengine(): + return ('APPENGINE_RUNTIME' in os.environ and + 'Google App Engine/' in os.environ['SERVER_SOFTWARE'] and + not is_prod_appengine_mvms()) + + +def is_prod_appengine_mvms(): + return os.environ.get('GAE_VM', False) == 'true' diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py new file mode 100644 index 0000000..642e99e --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py @@ -0,0 +1,112 @@ +""" +NTLM authenticating pool, contributed by erikcederstran + +Issue #10, see: http://code.google.com/p/urllib3/issues/detail?id=10 +""" +from __future__ import absolute_import + +from logging import getLogger +from ntlm import ntlm + +from .. import HTTPSConnectionPool +from ..packages.six.moves.http_client import HTTPSConnection + + +log = getLogger(__name__) + + +class NTLMConnectionPool(HTTPSConnectionPool): + """ + Implements an NTLM authentication version of an urllib3 connection pool + """ + + scheme = 'https' + + def __init__(self, user, pw, authurl, *args, **kwargs): + """ + authurl is a random URL on the server that is protected by NTLM. + user is the Windows user, probably in the DOMAIN\\username format. + pw is the password for the user. + """ + super(NTLMConnectionPool, self).__init__(*args, **kwargs) + self.authurl = authurl + self.rawuser = user + user_parts = user.split('\\', 1) + self.domain = user_parts[0].upper() + self.user = user_parts[1] + self.pw = pw + + def _new_conn(self): + # Performs the NTLM handshake that secures the connection. The socket + # must be kept open while requests are performed. + self.num_connections += 1 + log.debug('Starting NTLM HTTPS connection no. %d: https://%s%s', + self.num_connections, self.host, self.authurl) + + headers = {} + headers['Connection'] = 'Keep-Alive' + req_header = 'Authorization' + resp_header = 'www-authenticate' + + conn = HTTPSConnection(host=self.host, port=self.port) + + # Send negotiation message + headers[req_header] = ( + 'NTLM %s' % ntlm.create_NTLM_NEGOTIATE_MESSAGE(self.rawuser)) + log.debug('Request headers: %s', headers) + conn.request('GET', self.authurl, None, headers) + res = conn.getresponse() + reshdr = dict(res.getheaders()) + log.debug('Response status: %s %s', res.status, res.reason) + log.debug('Response headers: %s', reshdr) + log.debug('Response data: %s [...]', res.read(100)) + + # Remove the reference to the socket, so that it can not be closed by + # the response object (we want to keep the socket open) + res.fp = None + + # Server should respond with a challenge message + auth_header_values = reshdr[resp_header].split(', ') + auth_header_value = None + for s in auth_header_values: + if s[:5] == 'NTLM ': + auth_header_value = s[5:] + if auth_header_value is None: + raise Exception('Unexpected %s response header: %s' % + (resp_header, reshdr[resp_header])) + + # Send authentication message + ServerChallenge, NegotiateFlags = \ + ntlm.parse_NTLM_CHALLENGE_MESSAGE(auth_header_value) + auth_msg = ntlm.create_NTLM_AUTHENTICATE_MESSAGE(ServerChallenge, + self.user, + self.domain, + self.pw, + NegotiateFlags) + headers[req_header] = 'NTLM %s' % auth_msg + log.debug('Request headers: %s', headers) + conn.request('GET', self.authurl, None, headers) + res = conn.getresponse() + log.debug('Response status: %s %s', res.status, res.reason) + log.debug('Response headers: %s', dict(res.getheaders())) + log.debug('Response data: %s [...]', res.read()[:100]) + if res.status != 200: + if res.status == 401: + raise Exception('Server rejected request: wrong ' + 'username or password') + raise Exception('Wrong server response: %s %s' % + (res.status, res.reason)) + + res.fp = None + log.debug('Connection established') + return conn + + def urlopen(self, method, url, body=None, headers=None, retries=3, + redirect=True, assert_same_host=True): + if headers is None: + headers = {} + headers['Connection'] = 'Keep-Alive' + return super(NTLMConnectionPool, self).urlopen(method, url, body, + headers, retries, + redirect, + assert_same_host) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py new file mode 100644 index 0000000..6dd3a01 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py @@ -0,0 +1,457 @@ +""" +SSL with SNI_-support for Python 2. Follow these instructions if you would +like to verify SSL certificates in Python 2. Note, the default libraries do +*not* do certificate checking; you need to do additional work to validate +certificates yourself. + +This needs the following packages installed: + +* pyOpenSSL (tested with 16.0.0) +* cryptography (minimum 1.3.4, from pyopenssl) +* idna (minimum 2.0, from cryptography) + +However, pyopenssl depends on cryptography, which depends on idna, so while we +use all three directly here we end up having relatively few packages required. + +You can install them with the following command: + + pip install pyopenssl cryptography idna + +To activate certificate checking, call +:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code +before you begin making HTTP requests. This can be done in a ``sitecustomize`` +module, or at any other time before your application begins using ``urllib3``, +like this:: + + try: + import urllib3.contrib.pyopenssl + urllib3.contrib.pyopenssl.inject_into_urllib3() + except ImportError: + pass + +Now you can use :mod:`urllib3` as you normally would, and it will support SNI +when the required modules are installed. + +Activating this module also has the positive side effect of disabling SSL/TLS +compression in Python 2 (see `CRIME attack`_). + +If you want to configure the default list of supported cipher suites, you can +set the ``urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST`` variable. + +.. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication +.. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit) +""" +from __future__ import absolute_import + +import OpenSSL.SSL +from cryptography import x509 +from cryptography.hazmat.backends.openssl import backend as openssl_backend +from cryptography.hazmat.backends.openssl.x509 import _Certificate +try: + from cryptography.x509 import UnsupportedExtension +except ImportError: + # UnsupportedExtension is gone in cryptography >= 2.1.0 + class UnsupportedExtension(Exception): + pass + +from socket import timeout, error as SocketError +from io import BytesIO + +try: # Platform-specific: Python 2 + from socket import _fileobject +except ImportError: # Platform-specific: Python 3 + _fileobject = None + from ..packages.backports.makefile import backport_makefile + +import logging +import ssl +from ..packages import six +import sys + +from .. import util + +__all__ = ['inject_into_urllib3', 'extract_from_urllib3'] + +# SNI always works. +HAS_SNI = True + +# Map from urllib3 to PyOpenSSL compatible parameter-values. +_openssl_versions = { + ssl.PROTOCOL_SSLv23: OpenSSL.SSL.SSLv23_METHOD, + ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, +} + +if hasattr(ssl, 'PROTOCOL_TLSv1_1') and hasattr(OpenSSL.SSL, 'TLSv1_1_METHOD'): + _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD + +if hasattr(ssl, 'PROTOCOL_TLSv1_2') and hasattr(OpenSSL.SSL, 'TLSv1_2_METHOD'): + _openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD + +try: + _openssl_versions.update({ssl.PROTOCOL_SSLv3: OpenSSL.SSL.SSLv3_METHOD}) +except AttributeError: + pass + +_stdlib_to_openssl_verify = { + ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE, + ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER, + ssl.CERT_REQUIRED: + OpenSSL.SSL.VERIFY_PEER + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, +} +_openssl_to_stdlib_verify = dict( + (v, k) for k, v in _stdlib_to_openssl_verify.items() +) + +# OpenSSL will only write 16K at a time +SSL_WRITE_BLOCKSIZE = 16384 + +orig_util_HAS_SNI = util.HAS_SNI +orig_util_SSLContext = util.ssl_.SSLContext + + +log = logging.getLogger(__name__) + + +def inject_into_urllib3(): + 'Monkey-patch urllib3 with PyOpenSSL-backed SSL-support.' + + _validate_dependencies_met() + + util.ssl_.SSLContext = PyOpenSSLContext + util.HAS_SNI = HAS_SNI + util.ssl_.HAS_SNI = HAS_SNI + util.IS_PYOPENSSL = True + util.ssl_.IS_PYOPENSSL = True + + +def extract_from_urllib3(): + 'Undo monkey-patching by :func:`inject_into_urllib3`.' + + util.ssl_.SSLContext = orig_util_SSLContext + util.HAS_SNI = orig_util_HAS_SNI + util.ssl_.HAS_SNI = orig_util_HAS_SNI + util.IS_PYOPENSSL = False + util.ssl_.IS_PYOPENSSL = False + + +def _validate_dependencies_met(): + """ + Verifies that PyOpenSSL's package-level dependencies have been met. + Throws `ImportError` if they are not met. + """ + # Method added in `cryptography==1.1`; not available in older versions + from cryptography.x509.extensions import Extensions + if getattr(Extensions, "get_extension_for_class", None) is None: + raise ImportError("'cryptography' module missing required functionality. " + "Try upgrading to v1.3.4 or newer.") + + # pyOpenSSL 0.14 and above use cryptography for OpenSSL bindings. The _x509 + # attribute is only present on those versions. + from OpenSSL.crypto import X509 + x509 = X509() + if getattr(x509, "_x509", None) is None: + raise ImportError("'pyOpenSSL' module missing required functionality. " + "Try upgrading to v0.14 or newer.") + + +def _dnsname_to_stdlib(name): + """ + Converts a dNSName SubjectAlternativeName field to the form used by the + standard library on the given Python version. + + Cryptography produces a dNSName as a unicode string that was idna-decoded + from ASCII bytes. We need to idna-encode that string to get it back, and + then on Python 3 we also need to convert to unicode via UTF-8 (the stdlib + uses PyUnicode_FromStringAndSize on it, which decodes via UTF-8). + """ + def idna_encode(name): + """ + Borrowed wholesale from the Python Cryptography Project. It turns out + that we can't just safely call `idna.encode`: it can explode for + wildcard names. This avoids that problem. + """ + from pip._vendor import idna + + for prefix in [u'*.', u'.']: + if name.startswith(prefix): + name = name[len(prefix):] + return prefix.encode('ascii') + idna.encode(name) + return idna.encode(name) + + name = idna_encode(name) + if sys.version_info >= (3, 0): + name = name.decode('utf-8') + return name + + +def get_subj_alt_name(peer_cert): + """ + Given an PyOpenSSL certificate, provides all the subject alternative names. + """ + # Pass the cert to cryptography, which has much better APIs for this. + if hasattr(peer_cert, "to_cryptography"): + cert = peer_cert.to_cryptography() + else: + # This is technically using private APIs, but should work across all + # relevant versions before PyOpenSSL got a proper API for this. + cert = _Certificate(openssl_backend, peer_cert._x509) + + # We want to find the SAN extension. Ask Cryptography to locate it (it's + # faster than looping in Python) + try: + ext = cert.extensions.get_extension_for_class( + x509.SubjectAlternativeName + ).value + except x509.ExtensionNotFound: + # No such extension, return the empty list. + return [] + except (x509.DuplicateExtension, UnsupportedExtension, + x509.UnsupportedGeneralNameType, UnicodeError) as e: + # A problem has been found with the quality of the certificate. Assume + # no SAN field is present. + log.warning( + "A problem was encountered with the certificate that prevented " + "urllib3 from finding the SubjectAlternativeName field. This can " + "affect certificate validation. The error was %s", + e, + ) + return [] + + # We want to return dNSName and iPAddress fields. We need to cast the IPs + # back to strings because the match_hostname function wants them as + # strings. + # Sadly the DNS names need to be idna encoded and then, on Python 3, UTF-8 + # decoded. This is pretty frustrating, but that's what the standard library + # does with certificates, and so we need to attempt to do the same. + names = [ + ('DNS', _dnsname_to_stdlib(name)) + for name in ext.get_values_for_type(x509.DNSName) + ] + names.extend( + ('IP Address', str(name)) + for name in ext.get_values_for_type(x509.IPAddress) + ) + + return names + + +class WrappedSocket(object): + '''API-compatibility wrapper for Python OpenSSL's Connection-class. + + Note: _makefile_refs, _drop() and _reuse() are needed for the garbage + collector of pypy. + ''' + + def __init__(self, connection, socket, suppress_ragged_eofs=True): + self.connection = connection + self.socket = socket + self.suppress_ragged_eofs = suppress_ragged_eofs + self._makefile_refs = 0 + self._closed = False + + def fileno(self): + return self.socket.fileno() + + # Copy-pasted from Python 3.5 source code + def _decref_socketios(self): + if self._makefile_refs > 0: + self._makefile_refs -= 1 + if self._closed: + self.close() + + def recv(self, *args, **kwargs): + try: + data = self.connection.recv(*args, **kwargs) + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): + return b'' + else: + raise SocketError(str(e)) + except OpenSSL.SSL.ZeroReturnError as e: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return b'' + else: + raise + except OpenSSL.SSL.WantReadError: + if not util.wait_for_read(self.socket, self.socket.gettimeout()): + raise timeout('The read operation timed out') + else: + return self.recv(*args, **kwargs) + else: + return data + + def recv_into(self, *args, **kwargs): + try: + return self.connection.recv_into(*args, **kwargs) + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): + return 0 + else: + raise SocketError(str(e)) + except OpenSSL.SSL.ZeroReturnError as e: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return 0 + else: + raise + except OpenSSL.SSL.WantReadError: + if not util.wait_for_read(self.socket, self.socket.gettimeout()): + raise timeout('The read operation timed out') + else: + return self.recv_into(*args, **kwargs) + + def settimeout(self, timeout): + return self.socket.settimeout(timeout) + + def _send_until_done(self, data): + while True: + try: + return self.connection.send(data) + except OpenSSL.SSL.WantWriteError: + if not util.wait_for_write(self.socket, self.socket.gettimeout()): + raise timeout() + continue + except OpenSSL.SSL.SysCallError as e: + raise SocketError(str(e)) + + def sendall(self, data): + total_sent = 0 + while total_sent < len(data): + sent = self._send_until_done(data[total_sent:total_sent + SSL_WRITE_BLOCKSIZE]) + total_sent += sent + + def shutdown(self): + # FIXME rethrow compatible exceptions should we ever use this + self.connection.shutdown() + + def close(self): + if self._makefile_refs < 1: + try: + self._closed = True + return self.connection.close() + except OpenSSL.SSL.Error: + return + else: + self._makefile_refs -= 1 + + def getpeercert(self, binary_form=False): + x509 = self.connection.get_peer_certificate() + + if not x509: + return x509 + + if binary_form: + return OpenSSL.crypto.dump_certificate( + OpenSSL.crypto.FILETYPE_ASN1, + x509) + + return { + 'subject': ( + (('commonName', x509.get_subject().CN),), + ), + 'subjectAltName': get_subj_alt_name(x509) + } + + def _reuse(self): + self._makefile_refs += 1 + + def _drop(self): + if self._makefile_refs < 1: + self.close() + else: + self._makefile_refs -= 1 + + +if _fileobject: # Platform-specific: Python 2 + def makefile(self, mode, bufsize=-1): + self._makefile_refs += 1 + return _fileobject(self, mode, bufsize, close=True) +else: # Platform-specific: Python 3 + makefile = backport_makefile + +WrappedSocket.makefile = makefile + + +class PyOpenSSLContext(object): + """ + I am a wrapper class for the PyOpenSSL ``Context`` object. I am responsible + for translating the interface of the standard library ``SSLContext`` object + to calls into PyOpenSSL. + """ + def __init__(self, protocol): + self.protocol = _openssl_versions[protocol] + self._ctx = OpenSSL.SSL.Context(self.protocol) + self._options = 0 + self.check_hostname = False + + @property + def options(self): + return self._options + + @options.setter + def options(self, value): + self._options = value + self._ctx.set_options(value) + + @property + def verify_mode(self): + return _openssl_to_stdlib_verify[self._ctx.get_verify_mode()] + + @verify_mode.setter + def verify_mode(self, value): + self._ctx.set_verify( + _stdlib_to_openssl_verify[value], + _verify_callback + ) + + def set_default_verify_paths(self): + self._ctx.set_default_verify_paths() + + def set_ciphers(self, ciphers): + if isinstance(ciphers, six.text_type): + ciphers = ciphers.encode('utf-8') + self._ctx.set_cipher_list(ciphers) + + def load_verify_locations(self, cafile=None, capath=None, cadata=None): + if cafile is not None: + cafile = cafile.encode('utf-8') + if capath is not None: + capath = capath.encode('utf-8') + self._ctx.load_verify_locations(cafile, capath) + if cadata is not None: + self._ctx.load_verify_locations(BytesIO(cadata)) + + def load_cert_chain(self, certfile, keyfile=None, password=None): + self._ctx.use_certificate_chain_file(certfile) + if password is not None: + self._ctx.set_passwd_cb(lambda max_length, prompt_twice, userdata: password) + self._ctx.use_privatekey_file(keyfile or certfile) + + def wrap_socket(self, sock, server_side=False, + do_handshake_on_connect=True, suppress_ragged_eofs=True, + server_hostname=None): + cnx = OpenSSL.SSL.Connection(self._ctx, sock) + + if isinstance(server_hostname, six.text_type): # Platform-specific: Python 3 + server_hostname = server_hostname.encode('utf-8') + + if server_hostname is not None: + cnx.set_tlsext_host_name(server_hostname) + + cnx.set_connect_state() + + while True: + try: + cnx.do_handshake() + except OpenSSL.SSL.WantReadError: + if not util.wait_for_read(sock, sock.gettimeout()): + raise timeout('select timed out') + continue + except OpenSSL.SSL.Error as e: + raise ssl.SSLError('bad handshake: %r' % e) + break + + return WrappedSocket(cnx, sock) + + +def _verify_callback(cnx, x509, err_no, err_depth, return_code): + return err_no == 0 diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/securetransport.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/securetransport.py new file mode 100644 index 0000000..77cb59e --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/securetransport.py @@ -0,0 +1,804 @@ +""" +SecureTranport support for urllib3 via ctypes. + +This makes platform-native TLS available to urllib3 users on macOS without the +use of a compiler. This is an important feature because the Python Package +Index is moving to become a TLSv1.2-or-higher server, and the default OpenSSL +that ships with macOS is not capable of doing TLSv1.2. The only way to resolve +this is to give macOS users an alternative solution to the problem, and that +solution is to use SecureTransport. + +We use ctypes here because this solution must not require a compiler. That's +because pip is not allowed to require a compiler either. + +This is not intended to be a seriously long-term solution to this problem. +The hope is that PEP 543 will eventually solve this issue for us, at which +point we can retire this contrib module. But in the short term, we need to +solve the impending tire fire that is Python on Mac without this kind of +contrib module. So...here we are. + +To use this module, simply import and inject it:: + + import urllib3.contrib.securetransport + urllib3.contrib.securetransport.inject_into_urllib3() + +Happy TLSing! +""" +from __future__ import absolute_import + +import contextlib +import ctypes +import errno +import os.path +import shutil +import socket +import ssl +import threading +import weakref + +from .. import util +from ._securetransport.bindings import ( + Security, SecurityConst, CoreFoundation +) +from ._securetransport.low_level import ( + _assert_no_error, _cert_array_from_pem, _temporary_keychain, + _load_client_cert_chain +) + +try: # Platform-specific: Python 2 + from socket import _fileobject +except ImportError: # Platform-specific: Python 3 + _fileobject = None + from ..packages.backports.makefile import backport_makefile + +__all__ = ['inject_into_urllib3', 'extract_from_urllib3'] + +# SNI always works +HAS_SNI = True + +orig_util_HAS_SNI = util.HAS_SNI +orig_util_SSLContext = util.ssl_.SSLContext + +# This dictionary is used by the read callback to obtain a handle to the +# calling wrapped socket. This is a pretty silly approach, but for now it'll +# do. I feel like I should be able to smuggle a handle to the wrapped socket +# directly in the SSLConnectionRef, but for now this approach will work I +# guess. +# +# We need to lock around this structure for inserts, but we don't do it for +# reads/writes in the callbacks. The reasoning here goes as follows: +# +# 1. It is not possible to call into the callbacks before the dictionary is +# populated, so once in the callback the id must be in the dictionary. +# 2. The callbacks don't mutate the dictionary, they only read from it, and +# so cannot conflict with any of the insertions. +# +# This is good: if we had to lock in the callbacks we'd drastically slow down +# the performance of this code. +_connection_refs = weakref.WeakValueDictionary() +_connection_ref_lock = threading.Lock() + +# Limit writes to 16kB. This is OpenSSL's limit, but we'll cargo-cult it over +# for no better reason than we need *a* limit, and this one is right there. +SSL_WRITE_BLOCKSIZE = 16384 + +# This is our equivalent of util.ssl_.DEFAULT_CIPHERS, but expanded out to +# individual cipher suites. We need to do this because this is how +# SecureTransport wants them. +CIPHER_SUITES = [ + SecurityConst.TLS_AES_256_GCM_SHA384, + SecurityConst.TLS_CHACHA20_POLY1305_SHA256, + SecurityConst.TLS_AES_128_GCM_SHA256, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + SecurityConst.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, + SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_RSA_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_RSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA256, + SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA, +] + +# Basically this is simple: for PROTOCOL_SSLv23 we turn it into a low of +# TLSv1 and a high of TLSv1.2. For everything else, we pin to that version. +_protocol_to_min_max = { + ssl.PROTOCOL_SSLv23: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12), +} + +if hasattr(ssl, "PROTOCOL_SSLv2"): + _protocol_to_min_max[ssl.PROTOCOL_SSLv2] = ( + SecurityConst.kSSLProtocol2, SecurityConst.kSSLProtocol2 + ) +if hasattr(ssl, "PROTOCOL_SSLv3"): + _protocol_to_min_max[ssl.PROTOCOL_SSLv3] = ( + SecurityConst.kSSLProtocol3, SecurityConst.kSSLProtocol3 + ) +if hasattr(ssl, "PROTOCOL_TLSv1"): + _protocol_to_min_max[ssl.PROTOCOL_TLSv1] = ( + SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol1 + ) +if hasattr(ssl, "PROTOCOL_TLSv1_1"): + _protocol_to_min_max[ssl.PROTOCOL_TLSv1_1] = ( + SecurityConst.kTLSProtocol11, SecurityConst.kTLSProtocol11 + ) +if hasattr(ssl, "PROTOCOL_TLSv1_2"): + _protocol_to_min_max[ssl.PROTOCOL_TLSv1_2] = ( + SecurityConst.kTLSProtocol12, SecurityConst.kTLSProtocol12 + ) +if hasattr(ssl, "PROTOCOL_TLS"): + _protocol_to_min_max[ssl.PROTOCOL_TLS] = _protocol_to_min_max[ssl.PROTOCOL_SSLv23] + + +def inject_into_urllib3(): + """ + Monkey-patch urllib3 with SecureTransport-backed SSL-support. + """ + util.ssl_.SSLContext = SecureTransportContext + util.HAS_SNI = HAS_SNI + util.ssl_.HAS_SNI = HAS_SNI + util.IS_SECURETRANSPORT = True + util.ssl_.IS_SECURETRANSPORT = True + + +def extract_from_urllib3(): + """ + Undo monkey-patching by :func:`inject_into_urllib3`. + """ + util.ssl_.SSLContext = orig_util_SSLContext + util.HAS_SNI = orig_util_HAS_SNI + util.ssl_.HAS_SNI = orig_util_HAS_SNI + util.IS_SECURETRANSPORT = False + util.ssl_.IS_SECURETRANSPORT = False + + +def _read_callback(connection_id, data_buffer, data_length_pointer): + """ + SecureTransport read callback. This is called by ST to request that data + be returned from the socket. + """ + wrapped_socket = None + try: + wrapped_socket = _connection_refs.get(connection_id) + if wrapped_socket is None: + return SecurityConst.errSSLInternal + base_socket = wrapped_socket.socket + + requested_length = data_length_pointer[0] + + timeout = wrapped_socket.gettimeout() + error = None + read_count = 0 + + try: + while read_count < requested_length: + if timeout is None or timeout >= 0: + if not util.wait_for_read(base_socket, timeout): + raise socket.error(errno.EAGAIN, 'timed out') + + remaining = requested_length - read_count + buffer = (ctypes.c_char * remaining).from_address( + data_buffer + read_count + ) + chunk_size = base_socket.recv_into(buffer, remaining) + read_count += chunk_size + if not chunk_size: + if not read_count: + return SecurityConst.errSSLClosedGraceful + break + except (socket.error) as e: + error = e.errno + + if error is not None and error != errno.EAGAIN: + data_length_pointer[0] = read_count + if error == errno.ECONNRESET or error == errno.EPIPE: + return SecurityConst.errSSLClosedAbort + raise + + data_length_pointer[0] = read_count + + if read_count != requested_length: + return SecurityConst.errSSLWouldBlock + + return 0 + except Exception as e: + if wrapped_socket is not None: + wrapped_socket._exception = e + return SecurityConst.errSSLInternal + + +def _write_callback(connection_id, data_buffer, data_length_pointer): + """ + SecureTransport write callback. This is called by ST to request that data + actually be sent on the network. + """ + wrapped_socket = None + try: + wrapped_socket = _connection_refs.get(connection_id) + if wrapped_socket is None: + return SecurityConst.errSSLInternal + base_socket = wrapped_socket.socket + + bytes_to_write = data_length_pointer[0] + data = ctypes.string_at(data_buffer, bytes_to_write) + + timeout = wrapped_socket.gettimeout() + error = None + sent = 0 + + try: + while sent < bytes_to_write: + if timeout is None or timeout >= 0: + if not util.wait_for_write(base_socket, timeout): + raise socket.error(errno.EAGAIN, 'timed out') + chunk_sent = base_socket.send(data) + sent += chunk_sent + + # This has some needless copying here, but I'm not sure there's + # much value in optimising this data path. + data = data[chunk_sent:] + except (socket.error) as e: + error = e.errno + + if error is not None and error != errno.EAGAIN: + data_length_pointer[0] = sent + if error == errno.ECONNRESET or error == errno.EPIPE: + return SecurityConst.errSSLClosedAbort + raise + + data_length_pointer[0] = sent + + if sent != bytes_to_write: + return SecurityConst.errSSLWouldBlock + + return 0 + except Exception as e: + if wrapped_socket is not None: + wrapped_socket._exception = e + return SecurityConst.errSSLInternal + + +# We need to keep these two objects references alive: if they get GC'd while +# in use then SecureTransport could attempt to call a function that is in freed +# memory. That would be...uh...bad. Yeah, that's the word. Bad. +_read_callback_pointer = Security.SSLReadFunc(_read_callback) +_write_callback_pointer = Security.SSLWriteFunc(_write_callback) + + +class WrappedSocket(object): + """ + API-compatibility wrapper for Python's OpenSSL wrapped socket object. + + Note: _makefile_refs, _drop(), and _reuse() are needed for the garbage + collector of PyPy. + """ + def __init__(self, socket): + self.socket = socket + self.context = None + self._makefile_refs = 0 + self._closed = False + self._exception = None + self._keychain = None + self._keychain_dir = None + self._client_cert_chain = None + + # We save off the previously-configured timeout and then set it to + # zero. This is done because we use select and friends to handle the + # timeouts, but if we leave the timeout set on the lower socket then + # Python will "kindly" call select on that socket again for us. Avoid + # that by forcing the timeout to zero. + self._timeout = self.socket.gettimeout() + self.socket.settimeout(0) + + @contextlib.contextmanager + def _raise_on_error(self): + """ + A context manager that can be used to wrap calls that do I/O from + SecureTransport. If any of the I/O callbacks hit an exception, this + context manager will correctly propagate the exception after the fact. + This avoids silently swallowing those exceptions. + + It also correctly forces the socket closed. + """ + self._exception = None + + # We explicitly don't catch around this yield because in the unlikely + # event that an exception was hit in the block we don't want to swallow + # it. + yield + if self._exception is not None: + exception, self._exception = self._exception, None + self.close() + raise exception + + def _set_ciphers(self): + """ + Sets up the allowed ciphers. By default this matches the set in + util.ssl_.DEFAULT_CIPHERS, at least as supported by macOS. This is done + custom and doesn't allow changing at this time, mostly because parsing + OpenSSL cipher strings is going to be a freaking nightmare. + """ + ciphers = (Security.SSLCipherSuite * len(CIPHER_SUITES))(*CIPHER_SUITES) + result = Security.SSLSetEnabledCiphers( + self.context, ciphers, len(CIPHER_SUITES) + ) + _assert_no_error(result) + + def _custom_validate(self, verify, trust_bundle): + """ + Called when we have set custom validation. We do this in two cases: + first, when cert validation is entirely disabled; and second, when + using a custom trust DB. + """ + # If we disabled cert validation, just say: cool. + if not verify: + return + + # We want data in memory, so load it up. + if os.path.isfile(trust_bundle): + with open(trust_bundle, 'rb') as f: + trust_bundle = f.read() + + cert_array = None + trust = Security.SecTrustRef() + + try: + # Get a CFArray that contains the certs we want. + cert_array = _cert_array_from_pem(trust_bundle) + + # Ok, now the hard part. We want to get the SecTrustRef that ST has + # created for this connection, shove our CAs into it, tell ST to + # ignore everything else it knows, and then ask if it can build a + # chain. This is a buuuunch of code. + result = Security.SSLCopyPeerTrust( + self.context, ctypes.byref(trust) + ) + _assert_no_error(result) + if not trust: + raise ssl.SSLError("Failed to copy trust reference") + + result = Security.SecTrustSetAnchorCertificates(trust, cert_array) + _assert_no_error(result) + + result = Security.SecTrustSetAnchorCertificatesOnly(trust, True) + _assert_no_error(result) + + trust_result = Security.SecTrustResultType() + result = Security.SecTrustEvaluate( + trust, ctypes.byref(trust_result) + ) + _assert_no_error(result) + finally: + if trust: + CoreFoundation.CFRelease(trust) + + if cert_array is not None: + CoreFoundation.CFRelease(cert_array) + + # Ok, now we can look at what the result was. + successes = ( + SecurityConst.kSecTrustResultUnspecified, + SecurityConst.kSecTrustResultProceed + ) + if trust_result.value not in successes: + raise ssl.SSLError( + "certificate verify failed, error code: %d" % + trust_result.value + ) + + def handshake(self, + server_hostname, + verify, + trust_bundle, + min_version, + max_version, + client_cert, + client_key, + client_key_passphrase): + """ + Actually performs the TLS handshake. This is run automatically by + wrapped socket, and shouldn't be needed in user code. + """ + # First, we do the initial bits of connection setup. We need to create + # a context, set its I/O funcs, and set the connection reference. + self.context = Security.SSLCreateContext( + None, SecurityConst.kSSLClientSide, SecurityConst.kSSLStreamType + ) + result = Security.SSLSetIOFuncs( + self.context, _read_callback_pointer, _write_callback_pointer + ) + _assert_no_error(result) + + # Here we need to compute the handle to use. We do this by taking the + # id of self modulo 2**31 - 1. If this is already in the dictionary, we + # just keep incrementing by one until we find a free space. + with _connection_ref_lock: + handle = id(self) % 2147483647 + while handle in _connection_refs: + handle = (handle + 1) % 2147483647 + _connection_refs[handle] = self + + result = Security.SSLSetConnection(self.context, handle) + _assert_no_error(result) + + # If we have a server hostname, we should set that too. + if server_hostname: + if not isinstance(server_hostname, bytes): + server_hostname = server_hostname.encode('utf-8') + + result = Security.SSLSetPeerDomainName( + self.context, server_hostname, len(server_hostname) + ) + _assert_no_error(result) + + # Setup the ciphers. + self._set_ciphers() + + # Set the minimum and maximum TLS versions. + result = Security.SSLSetProtocolVersionMin(self.context, min_version) + _assert_no_error(result) + result = Security.SSLSetProtocolVersionMax(self.context, max_version) + _assert_no_error(result) + + # If there's a trust DB, we need to use it. We do that by telling + # SecureTransport to break on server auth. We also do that if we don't + # want to validate the certs at all: we just won't actually do any + # authing in that case. + if not verify or trust_bundle is not None: + result = Security.SSLSetSessionOption( + self.context, + SecurityConst.kSSLSessionOptionBreakOnServerAuth, + True + ) + _assert_no_error(result) + + # If there's a client cert, we need to use it. + if client_cert: + self._keychain, self._keychain_dir = _temporary_keychain() + self._client_cert_chain = _load_client_cert_chain( + self._keychain, client_cert, client_key + ) + result = Security.SSLSetCertificate( + self.context, self._client_cert_chain + ) + _assert_no_error(result) + + while True: + with self._raise_on_error(): + result = Security.SSLHandshake(self.context) + + if result == SecurityConst.errSSLWouldBlock: + raise socket.timeout("handshake timed out") + elif result == SecurityConst.errSSLServerAuthCompleted: + self._custom_validate(verify, trust_bundle) + continue + else: + _assert_no_error(result) + break + + def fileno(self): + return self.socket.fileno() + + # Copy-pasted from Python 3.5 source code + def _decref_socketios(self): + if self._makefile_refs > 0: + self._makefile_refs -= 1 + if self._closed: + self.close() + + def recv(self, bufsiz): + buffer = ctypes.create_string_buffer(bufsiz) + bytes_read = self.recv_into(buffer, bufsiz) + data = buffer[:bytes_read] + return data + + def recv_into(self, buffer, nbytes=None): + # Read short on EOF. + if self._closed: + return 0 + + if nbytes is None: + nbytes = len(buffer) + + buffer = (ctypes.c_char * nbytes).from_buffer(buffer) + processed_bytes = ctypes.c_size_t(0) + + with self._raise_on_error(): + result = Security.SSLRead( + self.context, buffer, nbytes, ctypes.byref(processed_bytes) + ) + + # There are some result codes that we want to treat as "not always + # errors". Specifically, those are errSSLWouldBlock, + # errSSLClosedGraceful, and errSSLClosedNoNotify. + if (result == SecurityConst.errSSLWouldBlock): + # If we didn't process any bytes, then this was just a time out. + # However, we can get errSSLWouldBlock in situations when we *did* + # read some data, and in those cases we should just read "short" + # and return. + if processed_bytes.value == 0: + # Timed out, no data read. + raise socket.timeout("recv timed out") + elif result in (SecurityConst.errSSLClosedGraceful, SecurityConst.errSSLClosedNoNotify): + # The remote peer has closed this connection. We should do so as + # well. Note that we don't actually return here because in + # principle this could actually be fired along with return data. + # It's unlikely though. + self.close() + else: + _assert_no_error(result) + + # Ok, we read and probably succeeded. We should return whatever data + # was actually read. + return processed_bytes.value + + def settimeout(self, timeout): + self._timeout = timeout + + def gettimeout(self): + return self._timeout + + def send(self, data): + processed_bytes = ctypes.c_size_t(0) + + with self._raise_on_error(): + result = Security.SSLWrite( + self.context, data, len(data), ctypes.byref(processed_bytes) + ) + + if result == SecurityConst.errSSLWouldBlock and processed_bytes.value == 0: + # Timed out + raise socket.timeout("send timed out") + else: + _assert_no_error(result) + + # We sent, and probably succeeded. Tell them how much we sent. + return processed_bytes.value + + def sendall(self, data): + total_sent = 0 + while total_sent < len(data): + sent = self.send(data[total_sent:total_sent + SSL_WRITE_BLOCKSIZE]) + total_sent += sent + + def shutdown(self): + with self._raise_on_error(): + Security.SSLClose(self.context) + + def close(self): + # TODO: should I do clean shutdown here? Do I have to? + if self._makefile_refs < 1: + self._closed = True + if self.context: + CoreFoundation.CFRelease(self.context) + self.context = None + if self._client_cert_chain: + CoreFoundation.CFRelease(self._client_cert_chain) + self._client_cert_chain = None + if self._keychain: + Security.SecKeychainDelete(self._keychain) + CoreFoundation.CFRelease(self._keychain) + shutil.rmtree(self._keychain_dir) + self._keychain = self._keychain_dir = None + return self.socket.close() + else: + self._makefile_refs -= 1 + + def getpeercert(self, binary_form=False): + # Urgh, annoying. + # + # Here's how we do this: + # + # 1. Call SSLCopyPeerTrust to get hold of the trust object for this + # connection. + # 2. Call SecTrustGetCertificateAtIndex for index 0 to get the leaf. + # 3. To get the CN, call SecCertificateCopyCommonName and process that + # string so that it's of the appropriate type. + # 4. To get the SAN, we need to do something a bit more complex: + # a. Call SecCertificateCopyValues to get the data, requesting + # kSecOIDSubjectAltName. + # b. Mess about with this dictionary to try to get the SANs out. + # + # This is gross. Really gross. It's going to be a few hundred LoC extra + # just to repeat something that SecureTransport can *already do*. So my + # operating assumption at this time is that what we want to do is + # instead to just flag to urllib3 that it shouldn't do its own hostname + # validation when using SecureTransport. + if not binary_form: + raise ValueError( + "SecureTransport only supports dumping binary certs" + ) + trust = Security.SecTrustRef() + certdata = None + der_bytes = None + + try: + # Grab the trust store. + result = Security.SSLCopyPeerTrust( + self.context, ctypes.byref(trust) + ) + _assert_no_error(result) + if not trust: + # Probably we haven't done the handshake yet. No biggie. + return None + + cert_count = Security.SecTrustGetCertificateCount(trust) + if not cert_count: + # Also a case that might happen if we haven't handshaked. + # Handshook? Handshaken? + return None + + leaf = Security.SecTrustGetCertificateAtIndex(trust, 0) + assert leaf + + # Ok, now we want the DER bytes. + certdata = Security.SecCertificateCopyData(leaf) + assert certdata + + data_length = CoreFoundation.CFDataGetLength(certdata) + data_buffer = CoreFoundation.CFDataGetBytePtr(certdata) + der_bytes = ctypes.string_at(data_buffer, data_length) + finally: + if certdata: + CoreFoundation.CFRelease(certdata) + if trust: + CoreFoundation.CFRelease(trust) + + return der_bytes + + def _reuse(self): + self._makefile_refs += 1 + + def _drop(self): + if self._makefile_refs < 1: + self.close() + else: + self._makefile_refs -= 1 + + +if _fileobject: # Platform-specific: Python 2 + def makefile(self, mode, bufsize=-1): + self._makefile_refs += 1 + return _fileobject(self, mode, bufsize, close=True) +else: # Platform-specific: Python 3 + def makefile(self, mode="r", buffering=None, *args, **kwargs): + # We disable buffering with SecureTransport because it conflicts with + # the buffering that ST does internally (see issue #1153 for more). + buffering = 0 + return backport_makefile(self, mode, buffering, *args, **kwargs) + +WrappedSocket.makefile = makefile + + +class SecureTransportContext(object): + """ + I am a wrapper class for the SecureTransport library, to translate the + interface of the standard library ``SSLContext`` object to calls into + SecureTransport. + """ + def __init__(self, protocol): + self._min_version, self._max_version = _protocol_to_min_max[protocol] + self._options = 0 + self._verify = False + self._trust_bundle = None + self._client_cert = None + self._client_key = None + self._client_key_passphrase = None + + @property + def check_hostname(self): + """ + SecureTransport cannot have its hostname checking disabled. For more, + see the comment on getpeercert() in this file. + """ + return True + + @check_hostname.setter + def check_hostname(self, value): + """ + SecureTransport cannot have its hostname checking disabled. For more, + see the comment on getpeercert() in this file. + """ + pass + + @property + def options(self): + # TODO: Well, crap. + # + # So this is the bit of the code that is the most likely to cause us + # trouble. Essentially we need to enumerate all of the SSL options that + # users might want to use and try to see if we can sensibly translate + # them, or whether we should just ignore them. + return self._options + + @options.setter + def options(self, value): + # TODO: Update in line with above. + self._options = value + + @property + def verify_mode(self): + return ssl.CERT_REQUIRED if self._verify else ssl.CERT_NONE + + @verify_mode.setter + def verify_mode(self, value): + self._verify = True if value == ssl.CERT_REQUIRED else False + + def set_default_verify_paths(self): + # So, this has to do something a bit weird. Specifically, what it does + # is nothing. + # + # This means that, if we had previously had load_verify_locations + # called, this does not undo that. We need to do that because it turns + # out that the rest of the urllib3 code will attempt to load the + # default verify paths if it hasn't been told about any paths, even if + # the context itself was sometime earlier. We resolve that by just + # ignoring it. + pass + + def load_default_certs(self): + return self.set_default_verify_paths() + + def set_ciphers(self, ciphers): + # For now, we just require the default cipher string. + if ciphers != util.ssl_.DEFAULT_CIPHERS: + raise ValueError( + "SecureTransport doesn't support custom cipher strings" + ) + + def load_verify_locations(self, cafile=None, capath=None, cadata=None): + # OK, we only really support cadata and cafile. + if capath is not None: + raise ValueError( + "SecureTransport does not support cert directories" + ) + + self._trust_bundle = cafile or cadata + + def load_cert_chain(self, certfile, keyfile=None, password=None): + self._client_cert = certfile + self._client_key = keyfile + self._client_cert_passphrase = password + + def wrap_socket(self, sock, server_side=False, + do_handshake_on_connect=True, suppress_ragged_eofs=True, + server_hostname=None): + # So, what do we do here? Firstly, we assert some properties. This is a + # stripped down shim, so there is some functionality we don't support. + # See PEP 543 for the real deal. + assert not server_side + assert do_handshake_on_connect + assert suppress_ragged_eofs + + # Ok, we're good to go. Now we want to create the wrapped socket object + # and store it in the appropriate place. + wrapped_socket = WrappedSocket(sock) + + # Now we can handshake + wrapped_socket.handshake( + server_hostname, self._verify, self._trust_bundle, + self._min_version, self._max_version, self._client_cert, + self._client_key, self._client_key_passphrase + ) + return wrapped_socket diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py new file mode 100644 index 0000000..811e312 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py @@ -0,0 +1,192 @@ +# -*- coding: utf-8 -*- +""" +This module contains provisional support for SOCKS proxies from within +urllib3. This module supports SOCKS4 (specifically the SOCKS4A variant) and +SOCKS5. To enable its functionality, either install PySocks or install this +module with the ``socks`` extra. + +The SOCKS implementation supports the full range of urllib3 features. It also +supports the following SOCKS features: + +- SOCKS4 +- SOCKS4a +- SOCKS5 +- Usernames and passwords for the SOCKS proxy + +Known Limitations: + +- Currently PySocks does not support contacting remote websites via literal + IPv6 addresses. Any such connection attempt will fail. You must use a domain + name. +- Currently PySocks does not support IPv6 connections to the SOCKS proxy. Any + such connection attempt will fail. +""" +from __future__ import absolute_import + +try: + import socks +except ImportError: + import warnings + from ..exceptions import DependencyWarning + + warnings.warn(( + 'SOCKS support in urllib3 requires the installation of optional ' + 'dependencies: specifically, PySocks. For more information, see ' + 'https://urllib3.readthedocs.io/en/latest/contrib.html#socks-proxies' + ), + DependencyWarning + ) + raise + +from socket import error as SocketError, timeout as SocketTimeout + +from ..connection import ( + HTTPConnection, HTTPSConnection +) +from ..connectionpool import ( + HTTPConnectionPool, HTTPSConnectionPool +) +from ..exceptions import ConnectTimeoutError, NewConnectionError +from ..poolmanager import PoolManager +from ..util.url import parse_url + +try: + import ssl +except ImportError: + ssl = None + + +class SOCKSConnection(HTTPConnection): + """ + A plain-text HTTP connection that connects via a SOCKS proxy. + """ + def __init__(self, *args, **kwargs): + self._socks_options = kwargs.pop('_socks_options') + super(SOCKSConnection, self).__init__(*args, **kwargs) + + def _new_conn(self): + """ + Establish a new connection via the SOCKS proxy. + """ + extra_kw = {} + if self.source_address: + extra_kw['source_address'] = self.source_address + + if self.socket_options: + extra_kw['socket_options'] = self.socket_options + + try: + conn = socks.create_connection( + (self.host, self.port), + proxy_type=self._socks_options['socks_version'], + proxy_addr=self._socks_options['proxy_host'], + proxy_port=self._socks_options['proxy_port'], + proxy_username=self._socks_options['username'], + proxy_password=self._socks_options['password'], + proxy_rdns=self._socks_options['rdns'], + timeout=self.timeout, + **extra_kw + ) + + except SocketTimeout as e: + raise ConnectTimeoutError( + self, "Connection to %s timed out. (connect timeout=%s)" % + (self.host, self.timeout)) + + except socks.ProxyError as e: + # This is fragile as hell, but it seems to be the only way to raise + # useful errors here. + if e.socket_err: + error = e.socket_err + if isinstance(error, SocketTimeout): + raise ConnectTimeoutError( + self, + "Connection to %s timed out. (connect timeout=%s)" % + (self.host, self.timeout) + ) + else: + raise NewConnectionError( + self, + "Failed to establish a new connection: %s" % error + ) + else: + raise NewConnectionError( + self, + "Failed to establish a new connection: %s" % e + ) + + except SocketError as e: # Defensive: PySocks should catch all these. + raise NewConnectionError( + self, "Failed to establish a new connection: %s" % e) + + return conn + + +# We don't need to duplicate the Verified/Unverified distinction from +# urllib3/connection.py here because the HTTPSConnection will already have been +# correctly set to either the Verified or Unverified form by that module. This +# means the SOCKSHTTPSConnection will automatically be the correct type. +class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): + pass + + +class SOCKSHTTPConnectionPool(HTTPConnectionPool): + ConnectionCls = SOCKSConnection + + +class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): + ConnectionCls = SOCKSHTTPSConnection + + +class SOCKSProxyManager(PoolManager): + """ + A version of the urllib3 ProxyManager that routes connections via the + defined SOCKS proxy. + """ + pool_classes_by_scheme = { + 'http': SOCKSHTTPConnectionPool, + 'https': SOCKSHTTPSConnectionPool, + } + + def __init__(self, proxy_url, username=None, password=None, + num_pools=10, headers=None, **connection_pool_kw): + parsed = parse_url(proxy_url) + + if username is None and password is None and parsed.auth is not None: + split = parsed.auth.split(':') + if len(split) == 2: + username, password = split + if parsed.scheme == 'socks5': + socks_version = socks.PROXY_TYPE_SOCKS5 + rdns = False + elif parsed.scheme == 'socks5h': + socks_version = socks.PROXY_TYPE_SOCKS5 + rdns = True + elif parsed.scheme == 'socks4': + socks_version = socks.PROXY_TYPE_SOCKS4 + rdns = False + elif parsed.scheme == 'socks4a': + socks_version = socks.PROXY_TYPE_SOCKS4 + rdns = True + else: + raise ValueError( + "Unable to determine SOCKS version from %s" % proxy_url + ) + + self.proxy_url = proxy_url + + socks_options = { + 'socks_version': socks_version, + 'proxy_host': parsed.host, + 'proxy_port': parsed.port, + 'username': username, + 'password': password, + 'rdns': rdns + } + connection_pool_kw['_socks_options'] = socks_options + + super(SOCKSProxyManager, self).__init__( + num_pools, headers, **connection_pool_kw + ) + + self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/exceptions.py b/venv/Lib/site-packages/pip/_vendor/urllib3/exceptions.py new file mode 100644 index 0000000..7bbaa98 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/exceptions.py @@ -0,0 +1,246 @@ +from __future__ import absolute_import +from .packages.six.moves.http_client import ( + IncompleteRead as httplib_IncompleteRead +) +# Base Exceptions + + +class HTTPError(Exception): + "Base exception used by this module." + pass + + +class HTTPWarning(Warning): + "Base warning used by this module." + pass + + +class PoolError(HTTPError): + "Base exception for errors caused within a pool." + def __init__(self, pool, message): + self.pool = pool + HTTPError.__init__(self, "%s: %s" % (pool, message)) + + def __reduce__(self): + # For pickling purposes. + return self.__class__, (None, None) + + +class RequestError(PoolError): + "Base exception for PoolErrors that have associated URLs." + def __init__(self, pool, url, message): + self.url = url + PoolError.__init__(self, pool, message) + + def __reduce__(self): + # For pickling purposes. + return self.__class__, (None, self.url, None) + + +class SSLError(HTTPError): + "Raised when SSL certificate fails in an HTTPS connection." + pass + + +class ProxyError(HTTPError): + "Raised when the connection to a proxy fails." + pass + + +class DecodeError(HTTPError): + "Raised when automatic decoding based on Content-Type fails." + pass + + +class ProtocolError(HTTPError): + "Raised when something unexpected happens mid-request/response." + pass + + +#: Renamed to ProtocolError but aliased for backwards compatibility. +ConnectionError = ProtocolError + + +# Leaf Exceptions + +class MaxRetryError(RequestError): + """Raised when the maximum number of retries is exceeded. + + :param pool: The connection pool + :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool` + :param string url: The requested Url + :param exceptions.Exception reason: The underlying error + + """ + + def __init__(self, pool, url, reason=None): + self.reason = reason + + message = "Max retries exceeded with url: %s (Caused by %r)" % ( + url, reason) + + RequestError.__init__(self, pool, url, message) + + +class HostChangedError(RequestError): + "Raised when an existing pool gets a request for a foreign host." + + def __init__(self, pool, url, retries=3): + message = "Tried to open a foreign host with url: %s" % url + RequestError.__init__(self, pool, url, message) + self.retries = retries + + +class TimeoutStateError(HTTPError): + """ Raised when passing an invalid state to a timeout """ + pass + + +class TimeoutError(HTTPError): + """ Raised when a socket timeout error occurs. + + Catching this error will catch both :exc:`ReadTimeoutErrors + <ReadTimeoutError>` and :exc:`ConnectTimeoutErrors <ConnectTimeoutError>`. + """ + pass + + +class ReadTimeoutError(TimeoutError, RequestError): + "Raised when a socket timeout occurs while receiving data from a server" + pass + + +# This timeout error does not have a URL attached and needs to inherit from the +# base HTTPError +class ConnectTimeoutError(TimeoutError): + "Raised when a socket timeout occurs while connecting to a server" + pass + + +class NewConnectionError(ConnectTimeoutError, PoolError): + "Raised when we fail to establish a new connection. Usually ECONNREFUSED." + pass + + +class EmptyPoolError(PoolError): + "Raised when a pool runs out of connections and no more are allowed." + pass + + +class ClosedPoolError(PoolError): + "Raised when a request enters a pool after the pool has been closed." + pass + + +class LocationValueError(ValueError, HTTPError): + "Raised when there is something wrong with a given URL input." + pass + + +class LocationParseError(LocationValueError): + "Raised when get_host or similar fails to parse the URL input." + + def __init__(self, location): + message = "Failed to parse: %s" % location + HTTPError.__init__(self, message) + + self.location = location + + +class ResponseError(HTTPError): + "Used as a container for an error reason supplied in a MaxRetryError." + GENERIC_ERROR = 'too many error responses' + SPECIFIC_ERROR = 'too many {status_code} error responses' + + +class SecurityWarning(HTTPWarning): + "Warned when performing security reducing actions" + pass + + +class SubjectAltNameWarning(SecurityWarning): + "Warned when connecting to a host with a certificate missing a SAN." + pass + + +class InsecureRequestWarning(SecurityWarning): + "Warned when making an unverified HTTPS request." + pass + + +class SystemTimeWarning(SecurityWarning): + "Warned when system time is suspected to be wrong" + pass + + +class InsecurePlatformWarning(SecurityWarning): + "Warned when certain SSL configuration is not available on a platform." + pass + + +class SNIMissingWarning(HTTPWarning): + "Warned when making a HTTPS request without SNI available." + pass + + +class DependencyWarning(HTTPWarning): + """ + Warned when an attempt is made to import a module with missing optional + dependencies. + """ + pass + + +class ResponseNotChunked(ProtocolError, ValueError): + "Response needs to be chunked in order to read it as chunks." + pass + + +class BodyNotHttplibCompatible(HTTPError): + """ + Body should be httplib.HTTPResponse like (have an fp attribute which + returns raw chunks) for read_chunked(). + """ + pass + + +class IncompleteRead(HTTPError, httplib_IncompleteRead): + """ + Response length doesn't match expected Content-Length + + Subclass of http_client.IncompleteRead to allow int value + for `partial` to avoid creating large objects on streamed + reads. + """ + def __init__(self, partial, expected): + super(IncompleteRead, self).__init__(partial, expected) + + def __repr__(self): + return ('IncompleteRead(%i bytes read, ' + '%i more expected)' % (self.partial, self.expected)) + + +class InvalidHeader(HTTPError): + "The header provided was somehow invalid." + pass + + +class ProxySchemeUnknown(AssertionError, ValueError): + "ProxyManager does not support the supplied scheme" + # TODO(t-8ch): Stop inheriting from AssertionError in v2.0. + + def __init__(self, scheme): + message = "Not supported proxy scheme %s" % scheme + super(ProxySchemeUnknown, self).__init__(message) + + +class HeaderParsingError(HTTPError): + "Raised by assert_header_parsing, but we convert it to a log.warning statement." + def __init__(self, defects, unparsed_data): + message = '%s, unparsed data: %r' % (defects or 'Unknown', unparsed_data) + super(HeaderParsingError, self).__init__(message) + + +class UnrewindableBodyError(HTTPError): + "urllib3 encountered an error when trying to rewind a body" + pass diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/fields.py b/venv/Lib/site-packages/pip/_vendor/urllib3/fields.py new file mode 100644 index 0000000..37fe64a --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/fields.py @@ -0,0 +1,178 @@ +from __future__ import absolute_import +import email.utils +import mimetypes + +from .packages import six + + +def guess_content_type(filename, default='application/octet-stream'): + """ + Guess the "Content-Type" of a file. + + :param filename: + The filename to guess the "Content-Type" of using :mod:`mimetypes`. + :param default: + If no "Content-Type" can be guessed, default to `default`. + """ + if filename: + return mimetypes.guess_type(filename)[0] or default + return default + + +def format_header_param(name, value): + """ + Helper function to format and quote a single header parameter. + + Particularly useful for header parameters which might contain + non-ASCII values, like file names. This follows RFC 2231, as + suggested by RFC 2388 Section 4.4. + + :param name: + The name of the parameter, a string expected to be ASCII only. + :param value: + The value of the parameter, provided as a unicode string. + """ + if not any(ch in value for ch in '"\\\r\n'): + result = '%s="%s"' % (name, value) + try: + result.encode('ascii') + except (UnicodeEncodeError, UnicodeDecodeError): + pass + else: + return result + if not six.PY3 and isinstance(value, six.text_type): # Python 2: + value = value.encode('utf-8') + value = email.utils.encode_rfc2231(value, 'utf-8') + value = '%s*=%s' % (name, value) + return value + + +class RequestField(object): + """ + A data container for request body parameters. + + :param name: + The name of this request field. + :param data: + The data/value body. + :param filename: + An optional filename of the request field. + :param headers: + An optional dict-like object of headers to initially use for the field. + """ + def __init__(self, name, data, filename=None, headers=None): + self._name = name + self._filename = filename + self.data = data + self.headers = {} + if headers: + self.headers = dict(headers) + + @classmethod + def from_tuples(cls, fieldname, value): + """ + A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters. + + Supports constructing :class:`~urllib3.fields.RequestField` from + parameter of key/value strings AND key/filetuple. A filetuple is a + (filename, data, MIME type) tuple where the MIME type is optional. + For example:: + + 'foo': 'bar', + 'fakefile': ('foofile.txt', 'contents of foofile'), + 'realfile': ('barfile.txt', open('realfile').read()), + 'typedfile': ('bazfile.bin', open('bazfile').read(), 'image/jpeg'), + 'nonamefile': 'contents of nonamefile field', + + Field names and filenames must be unicode. + """ + if isinstance(value, tuple): + if len(value) == 3: + filename, data, content_type = value + else: + filename, data = value + content_type = guess_content_type(filename) + else: + filename = None + content_type = None + data = value + + request_param = cls(fieldname, data, filename=filename) + request_param.make_multipart(content_type=content_type) + + return request_param + + def _render_part(self, name, value): + """ + Overridable helper function to format a single header parameter. + + :param name: + The name of the parameter, a string expected to be ASCII only. + :param value: + The value of the parameter, provided as a unicode string. + """ + return format_header_param(name, value) + + def _render_parts(self, header_parts): + """ + Helper function to format and quote a single header. + + Useful for single headers that are composed of multiple items. E.g., + 'Content-Disposition' fields. + + :param header_parts: + A sequence of (k, v) tuples or a :class:`dict` of (k, v) to format + as `k1="v1"; k2="v2"; ...`. + """ + parts = [] + iterable = header_parts + if isinstance(header_parts, dict): + iterable = header_parts.items() + + for name, value in iterable: + if value is not None: + parts.append(self._render_part(name, value)) + + return '; '.join(parts) + + def render_headers(self): + """ + Renders the headers for this request field. + """ + lines = [] + + sort_keys = ['Content-Disposition', 'Content-Type', 'Content-Location'] + for sort_key in sort_keys: + if self.headers.get(sort_key, False): + lines.append('%s: %s' % (sort_key, self.headers[sort_key])) + + for header_name, header_value in self.headers.items(): + if header_name not in sort_keys: + if header_value: + lines.append('%s: %s' % (header_name, header_value)) + + lines.append('\r\n') + return '\r\n'.join(lines) + + def make_multipart(self, content_disposition=None, content_type=None, + content_location=None): + """ + Makes this request field into a multipart request field. + + This method overrides "Content-Disposition", "Content-Type" and + "Content-Location" headers to the request parameter. + + :param content_type: + The 'Content-Type' of the request body. + :param content_location: + The 'Content-Location' of the request body. + + """ + self.headers['Content-Disposition'] = content_disposition or 'form-data' + self.headers['Content-Disposition'] += '; '.join([ + '', self._render_parts( + (('name', self._name), ('filename', self._filename)) + ) + ]) + self.headers['Content-Type'] = content_type + self.headers['Content-Location'] = content_location diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/filepost.py b/venv/Lib/site-packages/pip/_vendor/urllib3/filepost.py new file mode 100644 index 0000000..78f1e19 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/filepost.py @@ -0,0 +1,98 @@ +from __future__ import absolute_import +import binascii +import codecs +import os + +from io import BytesIO + +from .packages import six +from .packages.six import b +from .fields import RequestField + +writer = codecs.lookup('utf-8')[3] + + +def choose_boundary(): + """ + Our embarrassingly-simple replacement for mimetools.choose_boundary. + """ + boundary = binascii.hexlify(os.urandom(16)) + if six.PY3: + boundary = boundary.decode('ascii') + return boundary + + +def iter_field_objects(fields): + """ + Iterate over fields. + + Supports list of (k, v) tuples and dicts, and lists of + :class:`~urllib3.fields.RequestField`. + + """ + if isinstance(fields, dict): + i = six.iteritems(fields) + else: + i = iter(fields) + + for field in i: + if isinstance(field, RequestField): + yield field + else: + yield RequestField.from_tuples(*field) + + +def iter_fields(fields): + """ + .. deprecated:: 1.6 + + Iterate over fields. + + The addition of :class:`~urllib3.fields.RequestField` makes this function + obsolete. Instead, use :func:`iter_field_objects`, which returns + :class:`~urllib3.fields.RequestField` objects. + + Supports list of (k, v) tuples and dicts. + """ + if isinstance(fields, dict): + return ((k, v) for k, v in six.iteritems(fields)) + + return ((k, v) for k, v in fields) + + +def encode_multipart_formdata(fields, boundary=None): + """ + Encode a dictionary of ``fields`` using the multipart/form-data MIME format. + + :param fields: + Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`). + + :param boundary: + If not specified, then a random boundary will be generated using + :func:`urllib3.filepost.choose_boundary`. + """ + body = BytesIO() + if boundary is None: + boundary = choose_boundary() + + for field in iter_field_objects(fields): + body.write(b('--%s\r\n' % (boundary))) + + writer(body).write(field.render_headers()) + data = field.data + + if isinstance(data, int): + data = str(data) # Backwards compatibility + + if isinstance(data, six.text_type): + writer(body).write(data) + else: + body.write(data) + + body.write(b'\r\n') + + body.write(b('--%s--\r\n' % (boundary))) + + content_type = str('multipart/form-data; boundary=%s' % boundary) + + return body.getvalue(), content_type diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/__init__.py b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/__init__.py new file mode 100644 index 0000000..170e974 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/__init__.py @@ -0,0 +1,5 @@ +from __future__ import absolute_import + +from . import ssl_match_hostname + +__all__ = ('ssl_match_hostname', ) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py new file mode 100644 index 0000000..75b80dc --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" +backports.makefile +~~~~~~~~~~~~~~~~~~ + +Backports the Python 3 ``socket.makefile`` method for use with anything that +wants to create a "fake" socket object. +""" +import io + +from socket import SocketIO + + +def backport_makefile(self, mode="r", buffering=None, encoding=None, + errors=None, newline=None): + """ + Backport of ``socket.makefile`` from Python 3.5. + """ + if not set(mode) <= set(["r", "w", "b"]): + raise ValueError( + "invalid mode %r (only r, w, b allowed)" % (mode,) + ) + writing = "w" in mode + reading = "r" in mode or not writing + assert reading or writing + binary = "b" in mode + rawmode = "" + if reading: + rawmode += "r" + if writing: + rawmode += "w" + raw = SocketIO(self, rawmode) + self._makefile_refs += 1 + if buffering is None: + buffering = -1 + if buffering < 0: + buffering = io.DEFAULT_BUFFER_SIZE + if buffering == 0: + if not binary: + raise ValueError("unbuffered streams must be binary") + return raw + if reading and writing: + buffer = io.BufferedRWPair(raw, raw, buffering) + elif reading: + buffer = io.BufferedReader(raw, buffering) + else: + assert writing + buffer = io.BufferedWriter(raw, buffering) + if binary: + return buffer + text = io.TextIOWrapper(buffer, encoding, errors, newline) + text.mode = mode + return text diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ordered_dict.py b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ordered_dict.py new file mode 100644 index 0000000..4479363 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ordered_dict.py @@ -0,0 +1,259 @@ +# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. +# Passes Python2.7's test suite and incorporates all the latest updates. +# Copyright 2009 Raymond Hettinger, released under the MIT License. +# http://code.activestate.com/recipes/576693/ +try: + from thread import get_ident as _get_ident +except ImportError: + from dummy_thread import get_ident as _get_ident + +try: + from _abcoll import KeysView, ValuesView, ItemsView +except ImportError: + pass + + +class OrderedDict(dict): + 'Dictionary that remembers insertion order' + # An inherited dict maps keys to values. + # The inherited dict provides __getitem__, __len__, __contains__, and get. + # The remaining methods are order-aware. + # Big-O running times for all methods are the same as for regular dictionaries. + + # The internal self.__map dictionary maps keys to links in a doubly linked list. + # The circular doubly linked list starts and ends with a sentinel element. + # The sentinel element never gets deleted (this simplifies the algorithm). + # Each link is stored as a list of length three: [PREV, NEXT, KEY]. + + def __init__(self, *args, **kwds): + '''Initialize an ordered dictionary. Signature is the same as for + regular dictionaries, but keyword arguments are not recommended + because their insertion order is arbitrary. + + ''' + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__root + except AttributeError: + self.__root = root = [] # sentinel node + root[:] = [root, root, None] + self.__map = {} + self.__update(*args, **kwds) + + def __setitem__(self, key, value, dict_setitem=dict.__setitem__): + 'od.__setitem__(i, y) <==> od[i]=y' + # Setting a new item creates a new link which goes at the end of the linked + # list, and the inherited dictionary is updated with the new key/value pair. + if key not in self: + root = self.__root + last = root[0] + last[1] = root[0] = self.__map[key] = [last, root, key] + dict_setitem(self, key, value) + + def __delitem__(self, key, dict_delitem=dict.__delitem__): + 'od.__delitem__(y) <==> del od[y]' + # Deleting an existing item uses self.__map to find the link which is + # then removed by updating the links in the predecessor and successor nodes. + dict_delitem(self, key) + link_prev, link_next, key = self.__map.pop(key) + link_prev[1] = link_next + link_next[0] = link_prev + + def __iter__(self): + 'od.__iter__() <==> iter(od)' + root = self.__root + curr = root[1] + while curr is not root: + yield curr[2] + curr = curr[1] + + def __reversed__(self): + 'od.__reversed__() <==> reversed(od)' + root = self.__root + curr = root[0] + while curr is not root: + yield curr[2] + curr = curr[0] + + def clear(self): + 'od.clear() -> None. Remove all items from od.' + try: + for node in self.__map.itervalues(): + del node[:] + root = self.__root + root[:] = [root, root, None] + self.__map.clear() + except AttributeError: + pass + dict.clear(self) + + def popitem(self, last=True): + '''od.popitem() -> (k, v), return and remove a (key, value) pair. + Pairs are returned in LIFO order if last is true or FIFO order if false. + + ''' + if not self: + raise KeyError('dictionary is empty') + root = self.__root + if last: + link = root[0] + link_prev = link[0] + link_prev[1] = root + root[0] = link_prev + else: + link = root[1] + link_next = link[1] + root[1] = link_next + link_next[0] = root + key = link[2] + del self.__map[key] + value = dict.pop(self, key) + return key, value + + # -- the following methods do not depend on the internal structure -- + + def keys(self): + 'od.keys() -> list of keys in od' + return list(self) + + def values(self): + 'od.values() -> list of values in od' + return [self[key] for key in self] + + def items(self): + 'od.items() -> list of (key, value) pairs in od' + return [(key, self[key]) for key in self] + + def iterkeys(self): + 'od.iterkeys() -> an iterator over the keys in od' + return iter(self) + + def itervalues(self): + 'od.itervalues -> an iterator over the values in od' + for k in self: + yield self[k] + + def iteritems(self): + 'od.iteritems -> an iterator over the (key, value) items in od' + for k in self: + yield (k, self[k]) + + def update(*args, **kwds): + '''od.update(E, **F) -> None. Update od from dict/iterable E and F. + + If E is a dict instance, does: for k in E: od[k] = E[k] + If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] + Or if E is an iterable of items, does: for k, v in E: od[k] = v + In either case, this is followed by: for k, v in F.items(): od[k] = v + + ''' + if len(args) > 2: + raise TypeError('update() takes at most 2 positional ' + 'arguments (%d given)' % (len(args),)) + elif not args: + raise TypeError('update() takes at least 1 argument (0 given)') + self = args[0] + # Make progressively weaker assumptions about "other" + other = () + if len(args) == 2: + other = args[1] + if isinstance(other, dict): + for key in other: + self[key] = other[key] + elif hasattr(other, 'keys'): + for key in other.keys(): + self[key] = other[key] + else: + for key, value in other: + self[key] = value + for key, value in kwds.items(): + self[key] = value + + __update = update # let subclasses override update without breaking __init__ + + __marker = object() + + def pop(self, key, default=__marker): + '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + + ''' + if key in self: + result = self[key] + del self[key] + return result + if default is self.__marker: + raise KeyError(key) + return default + + def setdefault(self, key, default=None): + 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + if key in self: + return self[key] + self[key] = default + return default + + def __repr__(self, _repr_running={}): + 'od.__repr__() <==> repr(od)' + call_key = id(self), _get_ident() + if call_key in _repr_running: + return '...' + _repr_running[call_key] = 1 + try: + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + finally: + del _repr_running[call_key] + + def __reduce__(self): + 'Return state information for pickling' + items = [[k, self[k]] for k in self] + inst_dict = vars(self).copy() + for k in vars(OrderedDict()): + inst_dict.pop(k, None) + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def copy(self): + 'od.copy() -> a shallow copy of od' + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S + and values equal to v (which defaults to None). + + ''' + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + while comparison to a regular mapping is order-insensitive. + + ''' + if isinstance(other, OrderedDict): + return len(self)==len(other) and self.items() == other.items() + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other + + # -- the following methods are only used in Python 2.7 -- + + def viewkeys(self): + "od.viewkeys() -> a set-like object providing a view on od's keys" + return KeysView(self) + + def viewvalues(self): + "od.viewvalues() -> an object providing a view on od's values" + return ValuesView(self) + + def viewitems(self): + "od.viewitems() -> a set-like object providing a view on od's items" + return ItemsView(self) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/six.py b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/six.py new file mode 100644 index 0000000..190c023 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/six.py @@ -0,0 +1,868 @@ +"""Utilities for writing code that runs on Python 2 and 3""" + +# Copyright (c) 2010-2015 Benjamin Peterson +# +# 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. + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson <benjamin@python.org>" +__version__ = "1.10.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py new file mode 100644 index 0000000..d6594eb --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py @@ -0,0 +1,19 @@ +import sys + +try: + # Our match_hostname function is the same as 3.5's, so we only want to + # import the match_hostname function if it's at least that good. + if sys.version_info < (3, 5): + raise ImportError("Fallback to vendored code") + + from ssl import CertificateError, match_hostname +except ImportError: + try: + # Backport of the function from a pypi module + from backports.ssl_match_hostname import CertificateError, match_hostname + except ImportError: + # Our vendored copy + from ._implementation import CertificateError, match_hostname + +# Not needed, but documenting what we provide. +__all__ = ('CertificateError', 'match_hostname') diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py new file mode 100644 index 0000000..92c9bc7 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py @@ -0,0 +1,157 @@ +"""The match_hostname() function from Python 3.3.3, essential when using SSL.""" + +# Note: This file is under the PSF license as the code comes from the python +# stdlib. http://docs.python.org/3/license.html + +import re +import sys + +# ipaddress has been backported to 2.6+ in pypi. If it is installed on the +# system, use it to handle IPAddress ServerAltnames (this was added in +# python-3.5) otherwise only do DNS matching. This allows +# backports.ssl_match_hostname to continue to be used all the way back to +# python-2.4. +try: + from pip._vendor import ipaddress +except ImportError: + ipaddress = None + +__version__ = '3.5.0.1' + + +class CertificateError(ValueError): + pass + + +def _dnsname_match(dn, hostname, max_wildcards=1): + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + # Ported from python3-syntax: + # leftmost, *remainder = dn.split(r'.') + parts = dn.split(r'.') + leftmost = parts[0] + remainder = parts[1:] + + wildcards = leftmost.count('*') + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn)) + + # speed up common case w/o wildcards + if not wildcards: + return dn.lower() == hostname.lower() + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == '*': + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append('[^.]+') + elif leftmost.startswith('xn--') or hostname.startswith('xn--'): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + return pat.match(hostname) + + +def _to_unicode(obj): + if isinstance(obj, str) and sys.version_info < (3,): + obj = unicode(obj, encoding='ascii', errors='strict') + return obj + +def _ipaddress_match(ipname, host_ip): + """Exact matching of IP addresses. + + RFC 6125 explicitly doesn't define an algorithm for this + (section 1.7.2 - "Out of Scope"). + """ + # OpenSSL may add a trailing newline to a subjectAltName's IP address + # Divergence from upstream: ipaddress can't handle byte str + ip = ipaddress.ip_address(_to_unicode(ipname).rstrip()) + return ip == host_ip + + +def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError("empty or no certificate, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED") + try: + # Divergence from upstream: ipaddress can't handle byte str + host_ip = ipaddress.ip_address(_to_unicode(hostname)) + except ValueError: + # Not an IP address (common case) + host_ip = None + except UnicodeError: + # Divergence from upstream: Have to deal with ipaddress not taking + # byte strings. addresses should be all ascii, so we consider it not + # an ipaddress in this case + host_ip = None + except AttributeError: + # Divergence from upstream: Make ipaddress library optional + if ipaddress is None: + host_ip = None + else: + raise + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if host_ip is None and _dnsname_match(value, hostname): + return + dnsnames.append(value) + elif key == 'IP Address': + if host_ip is not None and _ipaddress_match(value, host_ip): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get('subject', ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == 'commonName': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError("hostname %r " + "doesn't match either of %s" + % (hostname, ', '.join(map(repr, dnsnames)))) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r " + "doesn't match %r" + % (hostname, dnsnames[0])) + else: + raise CertificateError("no appropriate commonName or " + "subjectAltName fields were found") diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py b/venv/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py new file mode 100644 index 0000000..506a3c9 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py @@ -0,0 +1,449 @@ +from __future__ import absolute_import +import collections +import functools +import logging + +from ._collections import RecentlyUsedContainer +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool +from .connectionpool import port_by_scheme +from .exceptions import LocationValueError, MaxRetryError, ProxySchemeUnknown +from .packages.six.moves.urllib.parse import urljoin +from .request import RequestMethods +from .util.url import parse_url +from .util.retry import Retry + + +__all__ = ['PoolManager', 'ProxyManager', 'proxy_from_url'] + + +log = logging.getLogger(__name__) + +SSL_KEYWORDS = ('key_file', 'cert_file', 'cert_reqs', 'ca_certs', + 'ssl_version', 'ca_cert_dir', 'ssl_context') + +# All known keyword arguments that could be provided to the pool manager, its +# pools, or the underlying connections. This is used to construct a pool key. +_key_fields = ( + 'key_scheme', # str + 'key_host', # str + 'key_port', # int + 'key_timeout', # int or float or Timeout + 'key_retries', # int or Retry + 'key_strict', # bool + 'key_block', # bool + 'key_source_address', # str + 'key_key_file', # str + 'key_cert_file', # str + 'key_cert_reqs', # str + 'key_ca_certs', # str + 'key_ssl_version', # str + 'key_ca_cert_dir', # str + 'key_ssl_context', # instance of ssl.SSLContext or urllib3.util.ssl_.SSLContext + 'key_maxsize', # int + 'key_headers', # dict + 'key__proxy', # parsed proxy url + 'key__proxy_headers', # dict + 'key_socket_options', # list of (level (int), optname (int), value (int or str)) tuples + 'key__socks_options', # dict + 'key_assert_hostname', # bool or string + 'key_assert_fingerprint', # str +) + +#: The namedtuple class used to construct keys for the connection pool. +#: All custom key schemes should include the fields in this key at a minimum. +PoolKey = collections.namedtuple('PoolKey', _key_fields) + + +def _default_key_normalizer(key_class, request_context): + """ + Create a pool key out of a request context dictionary. + + According to RFC 3986, both the scheme and host are case-insensitive. + Therefore, this function normalizes both before constructing the pool + key for an HTTPS request. If you wish to change this behaviour, provide + alternate callables to ``key_fn_by_scheme``. + + :param key_class: + The class to use when constructing the key. This should be a namedtuple + with the ``scheme`` and ``host`` keys at a minimum. + :type key_class: namedtuple + :param request_context: + A dictionary-like object that contain the context for a request. + :type request_context: dict + + :return: A namedtuple that can be used as a connection pool key. + :rtype: PoolKey + """ + # Since we mutate the dictionary, make a copy first + context = request_context.copy() + context['scheme'] = context['scheme'].lower() + context['host'] = context['host'].lower() + + # These are both dictionaries and need to be transformed into frozensets + for key in ('headers', '_proxy_headers', '_socks_options'): + if key in context and context[key] is not None: + context[key] = frozenset(context[key].items()) + + # The socket_options key may be a list and needs to be transformed into a + # tuple. + socket_opts = context.get('socket_options') + if socket_opts is not None: + context['socket_options'] = tuple(socket_opts) + + # Map the kwargs to the names in the namedtuple - this is necessary since + # namedtuples can't have fields starting with '_'. + for key in list(context.keys()): + context['key_' + key] = context.pop(key) + + # Default to ``None`` for keys missing from the context + for field in key_class._fields: + if field not in context: + context[field] = None + + return key_class(**context) + + +#: A dictionary that maps a scheme to a callable that creates a pool key. +#: This can be used to alter the way pool keys are constructed, if desired. +#: Each PoolManager makes a copy of this dictionary so they can be configured +#: globally here, or individually on the instance. +key_fn_by_scheme = { + 'http': functools.partial(_default_key_normalizer, PoolKey), + 'https': functools.partial(_default_key_normalizer, PoolKey), +} + +pool_classes_by_scheme = { + 'http': HTTPConnectionPool, + 'https': HTTPSConnectionPool, +} + + +class PoolManager(RequestMethods): + """ + Allows for arbitrary requests while transparently keeping track of + necessary connection pools for you. + + :param num_pools: + Number of connection pools to cache before discarding the least + recently used pool. + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + + :param \\**connection_pool_kw: + Additional parameters are used to create fresh + :class:`urllib3.connectionpool.ConnectionPool` instances. + + Example:: + + >>> manager = PoolManager(num_pools=2) + >>> r = manager.request('GET', 'http://google.com/') + >>> r = manager.request('GET', 'http://google.com/mail') + >>> r = manager.request('GET', 'http://yahoo.com/') + >>> len(manager.pools) + 2 + + """ + + proxy = None + + def __init__(self, num_pools=10, headers=None, **connection_pool_kw): + RequestMethods.__init__(self, headers) + self.connection_pool_kw = connection_pool_kw + self.pools = RecentlyUsedContainer(num_pools, + dispose_func=lambda p: p.close()) + + # Locally set the pool classes and keys so other PoolManagers can + # override them. + self.pool_classes_by_scheme = pool_classes_by_scheme + self.key_fn_by_scheme = key_fn_by_scheme.copy() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.clear() + # Return False to re-raise any potential exceptions + return False + + def _new_pool(self, scheme, host, port, request_context=None): + """ + Create a new :class:`ConnectionPool` based on host, port, scheme, and + any additional pool keyword arguments. + + If ``request_context`` is provided, it is provided as keyword arguments + to the pool class used. This method is used to actually create the + connection pools handed out by :meth:`connection_from_url` and + companion methods. It is intended to be overridden for customization. + """ + pool_cls = self.pool_classes_by_scheme[scheme] + if request_context is None: + request_context = self.connection_pool_kw.copy() + + # Although the context has everything necessary to create the pool, + # this function has historically only used the scheme, host, and port + # in the positional args. When an API change is acceptable these can + # be removed. + for key in ('scheme', 'host', 'port'): + request_context.pop(key, None) + + if scheme == 'http': + for kw in SSL_KEYWORDS: + request_context.pop(kw, None) + + return pool_cls(host, port, **request_context) + + def clear(self): + """ + Empty our store of pools and direct them all to close. + + This will not affect in-flight connections, but they will not be + re-used after completion. + """ + self.pools.clear() + + def connection_from_host(self, host, port=None, scheme='http', pool_kwargs=None): + """ + Get a :class:`ConnectionPool` based on the host, port, and scheme. + + If ``port`` isn't given, it will be derived from the ``scheme`` using + ``urllib3.connectionpool.port_by_scheme``. If ``pool_kwargs`` is + provided, it is merged with the instance's ``connection_pool_kw`` + variable and used to create the new connection pool, if one is + needed. + """ + + if not host: + raise LocationValueError("No host specified.") + + request_context = self._merge_pool_kwargs(pool_kwargs) + request_context['scheme'] = scheme or 'http' + if not port: + port = port_by_scheme.get(request_context['scheme'].lower(), 80) + request_context['port'] = port + request_context['host'] = host + + return self.connection_from_context(request_context) + + def connection_from_context(self, request_context): + """ + Get a :class:`ConnectionPool` based on the request context. + + ``request_context`` must at least contain the ``scheme`` key and its + value must be a key in ``key_fn_by_scheme`` instance variable. + """ + scheme = request_context['scheme'].lower() + pool_key_constructor = self.key_fn_by_scheme[scheme] + pool_key = pool_key_constructor(request_context) + + return self.connection_from_pool_key(pool_key, request_context=request_context) + + def connection_from_pool_key(self, pool_key, request_context=None): + """ + Get a :class:`ConnectionPool` based on the provided pool key. + + ``pool_key`` should be a namedtuple that only contains immutable + objects. At a minimum it must have the ``scheme``, ``host``, and + ``port`` fields. + """ + with self.pools.lock: + # If the scheme, host, or port doesn't match existing open + # connections, open a new ConnectionPool. + pool = self.pools.get(pool_key) + if pool: + return pool + + # Make a fresh ConnectionPool of the desired type + scheme = request_context['scheme'] + host = request_context['host'] + port = request_context['port'] + pool = self._new_pool(scheme, host, port, request_context=request_context) + self.pools[pool_key] = pool + + return pool + + def connection_from_url(self, url, pool_kwargs=None): + """ + Similar to :func:`urllib3.connectionpool.connection_from_url`. + + If ``pool_kwargs`` is not provided and a new pool needs to be + constructed, ``self.connection_pool_kw`` is used to initialize + the :class:`urllib3.connectionpool.ConnectionPool`. If ``pool_kwargs`` + is provided, it is used instead. Note that if a new pool does not + need to be created for the request, the provided ``pool_kwargs`` are + not used. + """ + u = parse_url(url) + return self.connection_from_host(u.host, port=u.port, scheme=u.scheme, + pool_kwargs=pool_kwargs) + + def _merge_pool_kwargs(self, override): + """ + Merge a dictionary of override values for self.connection_pool_kw. + + This does not modify self.connection_pool_kw and returns a new dict. + Any keys in the override dictionary with a value of ``None`` are + removed from the merged dictionary. + """ + base_pool_kwargs = self.connection_pool_kw.copy() + if override: + for key, value in override.items(): + if value is None: + try: + del base_pool_kwargs[key] + except KeyError: + pass + else: + base_pool_kwargs[key] = value + return base_pool_kwargs + + def urlopen(self, method, url, redirect=True, **kw): + """ + Same as :meth:`urllib3.connectionpool.HTTPConnectionPool.urlopen` + with custom cross-host redirect logic and only sends the request-uri + portion of the ``url``. + + The given ``url`` parameter must be absolute, such that an appropriate + :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it. + """ + u = parse_url(url) + conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme) + + kw['assert_same_host'] = False + kw['redirect'] = False + + if 'headers' not in kw: + kw['headers'] = self.headers.copy() + + if self.proxy is not None and u.scheme == "http": + response = conn.urlopen(method, url, **kw) + else: + response = conn.urlopen(method, u.request_uri, **kw) + + redirect_location = redirect and response.get_redirect_location() + if not redirect_location: + return response + + # Support relative URLs for redirecting. + redirect_location = urljoin(url, redirect_location) + + # RFC 7231, Section 6.4.4 + if response.status == 303: + method = 'GET' + + retries = kw.get('retries') + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect) + + # Strip headers marked as unsafe to forward to the redirected location. + # Check remove_headers_on_redirect to avoid a potential network call within + # conn.is_same_host() which may use socket.gethostbyname() in the future. + if (retries.remove_headers_on_redirect + and not conn.is_same_host(redirect_location)): + for header in retries.remove_headers_on_redirect: + kw['headers'].pop(header, None) + + try: + retries = retries.increment(method, url, response=response, _pool=conn) + except MaxRetryError: + if retries.raise_on_redirect: + raise + return response + + kw['retries'] = retries + kw['redirect'] = redirect + + log.info("Redirecting %s -> %s", url, redirect_location) + return self.urlopen(method, redirect_location, **kw) + + +class ProxyManager(PoolManager): + """ + Behaves just like :class:`PoolManager`, but sends all requests through + the defined proxy, using the CONNECT method for HTTPS URLs. + + :param proxy_url: + The URL of the proxy to be used. + + :param proxy_headers: + A dictionary containing headers that will be sent to the proxy. In case + of HTTP they are being sent with each request, while in the + HTTPS/CONNECT case they are sent only once. Could be used for proxy + authentication. + + Example: + >>> proxy = urllib3.ProxyManager('http://localhost:3128/') + >>> r1 = proxy.request('GET', 'http://google.com/') + >>> r2 = proxy.request('GET', 'http://httpbin.org/') + >>> len(proxy.pools) + 1 + >>> r3 = proxy.request('GET', 'https://httpbin.org/') + >>> r4 = proxy.request('GET', 'https://twitter.com/') + >>> len(proxy.pools) + 3 + + """ + + def __init__(self, proxy_url, num_pools=10, headers=None, + proxy_headers=None, **connection_pool_kw): + + if isinstance(proxy_url, HTTPConnectionPool): + proxy_url = '%s://%s:%i' % (proxy_url.scheme, proxy_url.host, + proxy_url.port) + proxy = parse_url(proxy_url) + if not proxy.port: + port = port_by_scheme.get(proxy.scheme, 80) + proxy = proxy._replace(port=port) + + if proxy.scheme not in ("http", "https"): + raise ProxySchemeUnknown(proxy.scheme) + + self.proxy = proxy + self.proxy_headers = proxy_headers or {} + + connection_pool_kw['_proxy'] = self.proxy + connection_pool_kw['_proxy_headers'] = self.proxy_headers + + super(ProxyManager, self).__init__( + num_pools, headers, **connection_pool_kw) + + def connection_from_host(self, host, port=None, scheme='http', pool_kwargs=None): + if scheme == "https": + return super(ProxyManager, self).connection_from_host( + host, port, scheme, pool_kwargs=pool_kwargs) + + return super(ProxyManager, self).connection_from_host( + self.proxy.host, self.proxy.port, self.proxy.scheme, pool_kwargs=pool_kwargs) + + def _set_proxy_headers(self, url, headers=None): + """ + Sets headers needed by proxies: specifically, the Accept and Host + headers. Only sets headers not provided by the user. + """ + headers_ = {'Accept': '*/*'} + + netloc = parse_url(url).netloc + if netloc: + headers_['Host'] = netloc + + if headers: + headers_.update(headers) + return headers_ + + def urlopen(self, method, url, redirect=True, **kw): + "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute." + u = parse_url(url) + + if u.scheme == "http": + # For proxied HTTPS requests, httplib sets the necessary headers + # on the CONNECT to the proxy. For HTTP, we'll definitely + # need to set 'Host' at the very least. + headers = kw.get('headers', self.headers) + kw['headers'] = self._set_proxy_headers(url, headers) + + return super(ProxyManager, self).urlopen(method, url, redirect=redirect, **kw) + + +def proxy_from_url(url, **kw): + return ProxyManager(proxy_url=url, **kw) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/request.py b/venv/Lib/site-packages/pip/_vendor/urllib3/request.py new file mode 100644 index 0000000..1be3334 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/request.py @@ -0,0 +1,150 @@ +from __future__ import absolute_import + +from .filepost import encode_multipart_formdata +from .packages.six.moves.urllib.parse import urlencode + + +__all__ = ['RequestMethods'] + + +class RequestMethods(object): + """ + Convenience mixin for classes who implement a :meth:`urlopen` method, such + as :class:`~urllib3.connectionpool.HTTPConnectionPool` and + :class:`~urllib3.poolmanager.PoolManager`. + + Provides behavior for making common types of HTTP request methods and + decides which type of request field encoding to use. + + Specifically, + + :meth:`.request_encode_url` is for sending requests whose fields are + encoded in the URL (such as GET, HEAD, DELETE). + + :meth:`.request_encode_body` is for sending requests whose fields are + encoded in the *body* of the request using multipart or www-form-urlencoded + (such as for POST, PUT, PATCH). + + :meth:`.request` is for making any kind of request, it will look up the + appropriate encoding format and use one of the above two methods to make + the request. + + Initializer parameters: + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + """ + + _encode_url_methods = set(['DELETE', 'GET', 'HEAD', 'OPTIONS']) + + def __init__(self, headers=None): + self.headers = headers or {} + + def urlopen(self, method, url, body=None, headers=None, + encode_multipart=True, multipart_boundary=None, + **kw): # Abstract + raise NotImplementedError("Classes extending RequestMethods must implement " + "their own ``urlopen`` method.") + + def request(self, method, url, fields=None, headers=None, **urlopen_kw): + """ + Make a request using :meth:`urlopen` with the appropriate encoding of + ``fields`` based on the ``method`` used. + + This is a convenience method that requires the least amount of manual + effort. It can be used in most situations, while still having the + option to drop down to more specific methods when necessary, such as + :meth:`request_encode_url`, :meth:`request_encode_body`, + or even the lowest level :meth:`urlopen`. + """ + method = method.upper() + + urlopen_kw['request_url'] = url + + if method in self._encode_url_methods: + return self.request_encode_url(method, url, fields=fields, + headers=headers, + **urlopen_kw) + else: + return self.request_encode_body(method, url, fields=fields, + headers=headers, + **urlopen_kw) + + def request_encode_url(self, method, url, fields=None, headers=None, + **urlopen_kw): + """ + Make a request using :meth:`urlopen` with the ``fields`` encoded in + the url. This is useful for request methods like GET, HEAD, DELETE, etc. + """ + if headers is None: + headers = self.headers + + extra_kw = {'headers': headers} + extra_kw.update(urlopen_kw) + + if fields: + url += '?' + urlencode(fields) + + return self.urlopen(method, url, **extra_kw) + + def request_encode_body(self, method, url, fields=None, headers=None, + encode_multipart=True, multipart_boundary=None, + **urlopen_kw): + """ + Make a request using :meth:`urlopen` with the ``fields`` encoded in + the body. This is useful for request methods like POST, PUT, PATCH, etc. + + When ``encode_multipart=True`` (default), then + :meth:`urllib3.filepost.encode_multipart_formdata` is used to encode + the payload with the appropriate content type. Otherwise + :meth:`urllib.urlencode` is used with the + 'application/x-www-form-urlencoded' content type. + + Multipart encoding must be used when posting files, and it's reasonably + safe to use it in other times too. However, it may break request + signing, such as with OAuth. + + Supports an optional ``fields`` parameter of key/value strings AND + key/filetuple. A filetuple is a (filename, data, MIME type) tuple where + the MIME type is optional. For example:: + + fields = { + 'foo': 'bar', + 'fakefile': ('foofile.txt', 'contents of foofile'), + 'realfile': ('barfile.txt', open('realfile').read()), + 'typedfile': ('bazfile.bin', open('bazfile').read(), + 'image/jpeg'), + 'nonamefile': 'contents of nonamefile field', + } + + When uploading a file, providing a filename (the first parameter of the + tuple) is optional but recommended to best mimic behavior of browsers. + + Note that if ``headers`` are supplied, the 'Content-Type' header will + be overwritten because it depends on the dynamic random boundary string + which is used to compose the body of the request. The random boundary + string can be explicitly set with the ``multipart_boundary`` parameter. + """ + if headers is None: + headers = self.headers + + extra_kw = {'headers': {}} + + if fields: + if 'body' in urlopen_kw: + raise TypeError( + "request got values for both 'fields' and 'body', can only specify one.") + + if encode_multipart: + body, content_type = encode_multipart_formdata(fields, boundary=multipart_boundary) + else: + body, content_type = urlencode(fields), 'application/x-www-form-urlencoded' + + extra_kw['body'] = body + extra_kw['headers'] = {'Content-Type': content_type} + + extra_kw['headers'].update(headers) + extra_kw.update(urlopen_kw) + + return self.urlopen(method, url, **extra_kw) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/response.py b/venv/Lib/site-packages/pip/_vendor/urllib3/response.py new file mode 100644 index 0000000..9873cb9 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/response.py @@ -0,0 +1,676 @@ +from __future__ import absolute_import +from contextlib import contextmanager +import zlib +import io +import logging +from socket import timeout as SocketTimeout +from socket import error as SocketError + +from ._collections import HTTPHeaderDict +from .exceptions import ( + BodyNotHttplibCompatible, ProtocolError, DecodeError, ReadTimeoutError, + ResponseNotChunked, IncompleteRead, InvalidHeader +) +from .packages.six import string_types as basestring, binary_type, PY3 +from .packages.six.moves import http_client as httplib +from .connection import HTTPException, BaseSSLError +from .util.response import is_fp_closed, is_response_to_head + +log = logging.getLogger(__name__) + + +class DeflateDecoder(object): + + def __init__(self): + self._first_try = True + self._data = binary_type() + self._obj = zlib.decompressobj() + + def __getattr__(self, name): + return getattr(self._obj, name) + + def decompress(self, data): + if not data: + return data + + if not self._first_try: + return self._obj.decompress(data) + + self._data += data + try: + decompressed = self._obj.decompress(data) + if decompressed: + self._first_try = False + self._data = None + return decompressed + except zlib.error: + self._first_try = False + self._obj = zlib.decompressobj(-zlib.MAX_WBITS) + try: + return self.decompress(self._data) + finally: + self._data = None + + +class GzipDecoderState(object): + + FIRST_MEMBER = 0 + OTHER_MEMBERS = 1 + SWALLOW_DATA = 2 + + +class GzipDecoder(object): + + def __init__(self): + self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) + self._state = GzipDecoderState.FIRST_MEMBER + + def __getattr__(self, name): + return getattr(self._obj, name) + + def decompress(self, data): + ret = binary_type() + if self._state == GzipDecoderState.SWALLOW_DATA or not data: + return ret + while True: + try: + ret += self._obj.decompress(data) + except zlib.error: + previous_state = self._state + # Ignore data after the first error + self._state = GzipDecoderState.SWALLOW_DATA + if previous_state == GzipDecoderState.OTHER_MEMBERS: + # Allow trailing garbage acceptable in other gzip clients + return ret + raise + data = self._obj.unused_data + if not data: + return ret + self._state = GzipDecoderState.OTHER_MEMBERS + self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) + + +def _get_decoder(mode): + if mode == 'gzip': + return GzipDecoder() + + return DeflateDecoder() + + +class HTTPResponse(io.IOBase): + """ + HTTP Response container. + + Backwards-compatible to httplib's HTTPResponse but the response ``body`` is + loaded and decoded on-demand when the ``data`` property is accessed. This + class is also compatible with the Python standard library's :mod:`io` + module, and can hence be treated as a readable object in the context of that + framework. + + Extra parameters for behaviour not present in httplib.HTTPResponse: + + :param preload_content: + If True, the response's body will be preloaded during construction. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param original_response: + When this HTTPResponse wrapper is generated from an httplib.HTTPResponse + object, it's convenient to include the original for debug purposes. It's + otherwise unused. + + :param retries: + The retries contains the last :class:`~urllib3.util.retry.Retry` that + was used during the request. + + :param enforce_content_length: + Enforce content length checking. Body returned by server must match + value of Content-Length header, if present. Otherwise, raise error. + """ + + CONTENT_DECODERS = ['gzip', 'deflate'] + REDIRECT_STATUSES = [301, 302, 303, 307, 308] + + def __init__(self, body='', headers=None, status=0, version=0, reason=None, + strict=0, preload_content=True, decode_content=True, + original_response=None, pool=None, connection=None, msg=None, + retries=None, enforce_content_length=False, + request_method=None, request_url=None): + + if isinstance(headers, HTTPHeaderDict): + self.headers = headers + else: + self.headers = HTTPHeaderDict(headers) + self.status = status + self.version = version + self.reason = reason + self.strict = strict + self.decode_content = decode_content + self.retries = retries + self.enforce_content_length = enforce_content_length + + self._decoder = None + self._body = None + self._fp = None + self._original_response = original_response + self._fp_bytes_read = 0 + self.msg = msg + self._request_url = request_url + + if body and isinstance(body, (basestring, binary_type)): + self._body = body + + self._pool = pool + self._connection = connection + + if hasattr(body, 'read'): + self._fp = body + + # Are we using the chunked-style of transfer encoding? + self.chunked = False + self.chunk_left = None + tr_enc = self.headers.get('transfer-encoding', '').lower() + # Don't incur the penalty of creating a list and then discarding it + encodings = (enc.strip() for enc in tr_enc.split(",")) + if "chunked" in encodings: + self.chunked = True + + # Determine length of response + self.length_remaining = self._init_length(request_method) + + # If requested, preload the body. + if preload_content and not self._body: + self._body = self.read(decode_content=decode_content) + + def get_redirect_location(self): + """ + Should we redirect and where to? + + :returns: Truthy redirect location string if we got a redirect status + code and valid location. ``None`` if redirect status and no + location. ``False`` if not a redirect status code. + """ + if self.status in self.REDIRECT_STATUSES: + return self.headers.get('location') + + return False + + def release_conn(self): + if not self._pool or not self._connection: + return + + self._pool._put_conn(self._connection) + self._connection = None + + @property + def data(self): + # For backwords-compat with earlier urllib3 0.4 and earlier. + if self._body: + return self._body + + if self._fp: + return self.read(cache_content=True) + + @property + def connection(self): + return self._connection + + def isclosed(self): + return is_fp_closed(self._fp) + + def tell(self): + """ + Obtain the number of bytes pulled over the wire so far. May differ from + the amount of content returned by :meth:``HTTPResponse.read`` if bytes + are encoded on the wire (e.g, compressed). + """ + return self._fp_bytes_read + + def _init_length(self, request_method): + """ + Set initial length value for Response content if available. + """ + length = self.headers.get('content-length') + + if length is not None: + if self.chunked: + # This Response will fail with an IncompleteRead if it can't be + # received as chunked. This method falls back to attempt reading + # the response before raising an exception. + log.warning("Received response with both Content-Length and " + "Transfer-Encoding set. This is expressly forbidden " + "by RFC 7230 sec 3.3.2. Ignoring Content-Length and " + "attempting to process response as Transfer-Encoding: " + "chunked.") + return None + + try: + # RFC 7230 section 3.3.2 specifies multiple content lengths can + # be sent in a single Content-Length header + # (e.g. Content-Length: 42, 42). This line ensures the values + # are all valid ints and that as long as the `set` length is 1, + # all values are the same. Otherwise, the header is invalid. + lengths = set([int(val) for val in length.split(',')]) + if len(lengths) > 1: + raise InvalidHeader("Content-Length contained multiple " + "unmatching values (%s)" % length) + length = lengths.pop() + except ValueError: + length = None + else: + if length < 0: + length = None + + # Convert status to int for comparison + # In some cases, httplib returns a status of "_UNKNOWN" + try: + status = int(self.status) + except ValueError: + status = 0 + + # Check for responses that shouldn't include a body + if status in (204, 304) or 100 <= status < 200 or request_method == 'HEAD': + length = 0 + + return length + + def _init_decoder(self): + """ + Set-up the _decoder attribute if necessary. + """ + # Note: content-encoding value should be case-insensitive, per RFC 7230 + # Section 3.2 + content_encoding = self.headers.get('content-encoding', '').lower() + if self._decoder is None and content_encoding in self.CONTENT_DECODERS: + self._decoder = _get_decoder(content_encoding) + + def _decode(self, data, decode_content, flush_decoder): + """ + Decode the data passed in and potentially flush the decoder. + """ + try: + if decode_content and self._decoder: + data = self._decoder.decompress(data) + except (IOError, zlib.error) as e: + content_encoding = self.headers.get('content-encoding', '').lower() + raise DecodeError( + "Received response with content-encoding: %s, but " + "failed to decode it." % content_encoding, e) + + if flush_decoder and decode_content: + data += self._flush_decoder() + + return data + + def _flush_decoder(self): + """ + Flushes the decoder. Should only be called if the decoder is actually + being used. + """ + if self._decoder: + buf = self._decoder.decompress(b'') + return buf + self._decoder.flush() + + return b'' + + @contextmanager + def _error_catcher(self): + """ + Catch low-level python exceptions, instead re-raising urllib3 + variants, so that low-level exceptions are not leaked in the + high-level api. + + On exit, release the connection back to the pool. + """ + clean_exit = False + + try: + try: + yield + + except SocketTimeout: + # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but + # there is yet no clean way to get at it from this context. + raise ReadTimeoutError(self._pool, None, 'Read timed out.') + + except BaseSSLError as e: + # FIXME: Is there a better way to differentiate between SSLErrors? + if 'read operation timed out' not in str(e): # Defensive: + # This shouldn't happen but just in case we're missing an edge + # case, let's avoid swallowing SSL errors. + raise + + raise ReadTimeoutError(self._pool, None, 'Read timed out.') + + except (HTTPException, SocketError) as e: + # This includes IncompleteRead. + raise ProtocolError('Connection broken: %r' % e, e) + + # If no exception is thrown, we should avoid cleaning up + # unnecessarily. + clean_exit = True + finally: + # If we didn't terminate cleanly, we need to throw away our + # connection. + if not clean_exit: + # The response may not be closed but we're not going to use it + # anymore so close it now to ensure that the connection is + # released back to the pool. + if self._original_response: + self._original_response.close() + + # Closing the response may not actually be sufficient to close + # everything, so if we have a hold of the connection close that + # too. + if self._connection: + self._connection.close() + + # If we hold the original response but it's closed now, we should + # return the connection back to the pool. + if self._original_response and self._original_response.isclosed(): + self.release_conn() + + def read(self, amt=None, decode_content=None, cache_content=False): + """ + Similar to :meth:`httplib.HTTPResponse.read`, but with two additional + parameters: ``decode_content`` and ``cache_content``. + + :param amt: + How much of the content to read. If specified, caching is skipped + because it doesn't make sense to cache partial content as the full + response. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param cache_content: + If True, will save the returned data such that the same result is + returned despite of the state of the underlying file object. This + is useful if you want the ``.data`` property to continue working + after having ``.read()`` the file object. (Overridden if ``amt`` is + set.) + """ + self._init_decoder() + if decode_content is None: + decode_content = self.decode_content + + if self._fp is None: + return + + flush_decoder = False + data = None + + with self._error_catcher(): + if amt is None: + # cStringIO doesn't like amt=None + data = self._fp.read() + flush_decoder = True + else: + cache_content = False + data = self._fp.read(amt) + if amt != 0 and not data: # Platform-specific: Buggy versions of Python. + # Close the connection when no data is returned + # + # This is redundant to what httplib/http.client _should_ + # already do. However, versions of python released before + # December 15, 2012 (http://bugs.python.org/issue16298) do + # not properly close the connection in all cases. There is + # no harm in redundantly calling close. + self._fp.close() + flush_decoder = True + if self.enforce_content_length and self.length_remaining not in (0, None): + # This is an edge case that httplib failed to cover due + # to concerns of backward compatibility. We're + # addressing it here to make sure IncompleteRead is + # raised during streaming, so all calls with incorrect + # Content-Length are caught. + raise IncompleteRead(self._fp_bytes_read, self.length_remaining) + + if data: + self._fp_bytes_read += len(data) + if self.length_remaining is not None: + self.length_remaining -= len(data) + + data = self._decode(data, decode_content, flush_decoder) + + if cache_content: + self._body = data + + return data + + def stream(self, amt=2**16, decode_content=None): + """ + A generator wrapper for the read() method. A call will block until + ``amt`` bytes have been read from the connection or until the + connection is closed. + + :param amt: + How much of the content to read. The generator will return up to + much data per iteration, but may return less. This is particularly + likely when using compressed data. However, the empty string will + never be returned. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + if self.chunked and self.supports_chunked_reads(): + for line in self.read_chunked(amt, decode_content=decode_content): + yield line + else: + while not is_fp_closed(self._fp): + data = self.read(amt=amt, decode_content=decode_content) + + if data: + yield data + + @classmethod + def from_httplib(ResponseCls, r, **response_kw): + """ + Given an :class:`httplib.HTTPResponse` instance ``r``, return a + corresponding :class:`urllib3.response.HTTPResponse` object. + + Remaining parameters are passed to the HTTPResponse constructor, along + with ``original_response=r``. + """ + headers = r.msg + + if not isinstance(headers, HTTPHeaderDict): + if PY3: # Python 3 + headers = HTTPHeaderDict(headers.items()) + else: # Python 2 + headers = HTTPHeaderDict.from_httplib(headers) + + # HTTPResponse objects in Python 3 don't have a .strict attribute + strict = getattr(r, 'strict', 0) + resp = ResponseCls(body=r, + headers=headers, + status=r.status, + version=r.version, + reason=r.reason, + strict=strict, + original_response=r, + **response_kw) + return resp + + # Backwards-compatibility methods for httplib.HTTPResponse + def getheaders(self): + return self.headers + + def getheader(self, name, default=None): + return self.headers.get(name, default) + + # Backwards compatibility for http.cookiejar + def info(self): + return self.headers + + # Overrides from io.IOBase + def close(self): + if not self.closed: + self._fp.close() + + if self._connection: + self._connection.close() + + @property + def closed(self): + if self._fp is None: + return True + elif hasattr(self._fp, 'isclosed'): + return self._fp.isclosed() + elif hasattr(self._fp, 'closed'): + return self._fp.closed + else: + return True + + def fileno(self): + if self._fp is None: + raise IOError("HTTPResponse has no file to get a fileno from") + elif hasattr(self._fp, "fileno"): + return self._fp.fileno() + else: + raise IOError("The file-like object this HTTPResponse is wrapped " + "around has no file descriptor") + + def flush(self): + if self._fp is not None and hasattr(self._fp, 'flush'): + return self._fp.flush() + + def readable(self): + # This method is required for `io` module compatibility. + return True + + def readinto(self, b): + # This method is required for `io` module compatibility. + temp = self.read(len(b)) + if len(temp) == 0: + return 0 + else: + b[:len(temp)] = temp + return len(temp) + + def supports_chunked_reads(self): + """ + Checks if the underlying file-like object looks like a + httplib.HTTPResponse object. We do this by testing for the fp + attribute. If it is present we assume it returns raw chunks as + processed by read_chunked(). + """ + return hasattr(self._fp, 'fp') + + def _update_chunk_length(self): + # First, we'll figure out length of a chunk and then + # we'll try to read it from socket. + if self.chunk_left is not None: + return + line = self._fp.fp.readline() + line = line.split(b';', 1)[0] + try: + self.chunk_left = int(line, 16) + except ValueError: + # Invalid chunked protocol response, abort. + self.close() + raise httplib.IncompleteRead(line) + + def _handle_chunk(self, amt): + returned_chunk = None + if amt is None: + chunk = self._fp._safe_read(self.chunk_left) + returned_chunk = chunk + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + elif amt < self.chunk_left: + value = self._fp._safe_read(amt) + self.chunk_left = self.chunk_left - amt + returned_chunk = value + elif amt == self.chunk_left: + value = self._fp._safe_read(amt) + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + returned_chunk = value + else: # amt > self.chunk_left + returned_chunk = self._fp._safe_read(self.chunk_left) + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + return returned_chunk + + def read_chunked(self, amt=None, decode_content=None): + """ + Similar to :meth:`HTTPResponse.read`, but with an additional + parameter: ``decode_content``. + + :param amt: + How much of the content to read. If specified, caching is skipped + because it doesn't make sense to cache partial content as the full + response. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + self._init_decoder() + # FIXME: Rewrite this method and make it a class with a better structured logic. + if not self.chunked: + raise ResponseNotChunked( + "Response is not chunked. " + "Header 'transfer-encoding: chunked' is missing.") + if not self.supports_chunked_reads(): + raise BodyNotHttplibCompatible( + "Body should be httplib.HTTPResponse like. " + "It should have have an fp attribute which returns raw chunks.") + + with self._error_catcher(): + # Don't bother reading the body of a HEAD request. + if self._original_response and is_response_to_head(self._original_response): + self._original_response.close() + return + + # If a response is already read and closed + # then return immediately. + if self._fp.fp is None: + return + + while True: + self._update_chunk_length() + if self.chunk_left == 0: + break + chunk = self._handle_chunk(amt) + decoded = self._decode(chunk, decode_content=decode_content, + flush_decoder=False) + if decoded: + yield decoded + + if decode_content: + # On CPython and PyPy, we should never need to flush the + # decoder. However, on Jython we *might* need to, so + # lets defensively do it anyway. + decoded = self._flush_decoder() + if decoded: # Platform-specific: Jython. + yield decoded + + # Chunk content ends with \r\n: discard it. + while True: + line = self._fp.fp.readline() + if not line: + # Some sites may not end with '\r\n'. + break + if line == b'\r\n': + break + + # We read everything; close the "file". + if self._original_response: + self._original_response.close() + + def geturl(self): + """ + Returns the URL that was the source of this response. + If the request that generated this response redirected, this method + will return the final redirect location. + """ + if self.retries is not None and len(self.retries.history): + return self.retries.history[-1].redirect_location + else: + return self._request_url diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/__init__.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/__init__.py new file mode 100644 index 0000000..2f2770b --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/__init__.py @@ -0,0 +1,54 @@ +from __future__ import absolute_import +# For backwards compatibility, provide imports that used to be here. +from .connection import is_connection_dropped +from .request import make_headers +from .response import is_fp_closed +from .ssl_ import ( + SSLContext, + HAS_SNI, + IS_PYOPENSSL, + IS_SECURETRANSPORT, + assert_fingerprint, + resolve_cert_reqs, + resolve_ssl_version, + ssl_wrap_socket, +) +from .timeout import ( + current_time, + Timeout, +) + +from .retry import Retry +from .url import ( + get_host, + parse_url, + split_first, + Url, +) +from .wait import ( + wait_for_read, + wait_for_write +) + +__all__ = ( + 'HAS_SNI', + 'IS_PYOPENSSL', + 'IS_SECURETRANSPORT', + 'SSLContext', + 'Retry', + 'Timeout', + 'Url', + 'assert_fingerprint', + 'current_time', + 'is_connection_dropped', + 'is_fp_closed', + 'get_host', + 'parse_url', + 'make_headers', + 'resolve_cert_reqs', + 'resolve_ssl_version', + 'split_first', + 'ssl_wrap_socket', + 'wait_for_read', + 'wait_for_write' +) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/connection.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/connection.py new file mode 100644 index 0000000..5cf488f --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/connection.py @@ -0,0 +1,126 @@ +from __future__ import absolute_import +import socket +from .wait import NoWayToWaitForSocketError, wait_for_read + + +def is_connection_dropped(conn): # Platform-specific + """ + Returns True if the connection is dropped and should be closed. + + :param conn: + :class:`httplib.HTTPConnection` object. + + Note: For platforms like AppEngine, this will always return ``False`` to + let the platform handle connection recycling transparently for us. + """ + sock = getattr(conn, 'sock', False) + if sock is False: # Platform-specific: AppEngine + return False + if sock is None: # Connection already closed (such as by httplib). + return True + try: + # Returns True if readable, which here means it's been dropped + return wait_for_read(sock, timeout=0.0) + except NoWayToWaitForSocketError: # Platform-specific: AppEngine + return False + + +# This function is copied from socket.py in the Python 2.7 standard +# library test suite. Added to its signature is only `socket_options`. +# One additional modification is that we avoid binding to IPv6 servers +# discovered in DNS if the system doesn't have IPv6 functionality. +def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + source_address=None, socket_options=None): + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith('['): + host = host.strip('[]') + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + sock.connect(sa) + return sock + + except socket.error as e: + err = e + if sock is not None: + sock.close() + sock = None + + if err is not None: + raise err + + raise socket.error("getaddrinfo returns an empty list") + + +def _set_socket_options(sock, options): + if options is None: + return + + for opt in options: + sock.setsockopt(*opt) + + +def allowed_gai_family(): + """This function is designed to work in the context of + getaddrinfo, where family=socket.AF_UNSPEC is the default and + will perform a DNS search for both IPv6 and IPv4 records.""" + + family = socket.AF_INET + if HAS_IPV6: + family = socket.AF_UNSPEC + return family + + +def _has_ipv6(host): + """ Returns True if the system can bind an IPv6 address. """ + sock = None + has_ipv6 = False + + if socket.has_ipv6: + # has_ipv6 returns true if cPython was compiled with IPv6 support. + # It does not tell us if the system has IPv6 support enabled. To + # determine that we must bind to an IPv6 address. + # https://github.com/shazow/urllib3/pull/611 + # https://bugs.python.org/issue658327 + try: + sock = socket.socket(socket.AF_INET6) + sock.bind((host, 0)) + has_ipv6 = True + except Exception: + pass + + if sock: + sock.close() + return has_ipv6 + + +HAS_IPV6 = _has_ipv6('::1') diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/queue.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/queue.py new file mode 100644 index 0000000..d3d379a --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/queue.py @@ -0,0 +1,21 @@ +import collections +from ..packages import six +from ..packages.six.moves import queue + +if six.PY2: + # Queue is imported for side effects on MS Windows. See issue #229. + import Queue as _unused_module_Queue # noqa: F401 + + +class LifoQueue(queue.Queue): + def _init(self, _): + self.queue = collections.deque() + + def _qsize(self, len=len): + return len(self.queue) + + def _put(self, item): + self.queue.append(item) + + def _get(self): + return self.queue.pop() diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/request.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/request.py new file mode 100644 index 0000000..3ddfcd5 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/request.py @@ -0,0 +1,118 @@ +from __future__ import absolute_import +from base64 import b64encode + +from ..packages.six import b, integer_types +from ..exceptions import UnrewindableBodyError + +ACCEPT_ENCODING = 'gzip,deflate' +_FAILEDTELL = object() + + +def make_headers(keep_alive=None, accept_encoding=None, user_agent=None, + basic_auth=None, proxy_basic_auth=None, disable_cache=None): + """ + Shortcuts for generating request headers. + + :param keep_alive: + If ``True``, adds 'connection: keep-alive' header. + + :param accept_encoding: + Can be a boolean, list, or string. + ``True`` translates to 'gzip,deflate'. + List will get joined by comma. + String will be used as provided. + + :param user_agent: + String representing the user-agent you want, such as + "python-urllib3/0.6" + + :param basic_auth: + Colon-separated username:password string for 'authorization: basic ...' + auth header. + + :param proxy_basic_auth: + Colon-separated username:password string for 'proxy-authorization: basic ...' + auth header. + + :param disable_cache: + If ``True``, adds 'cache-control: no-cache' header. + + Example:: + + >>> make_headers(keep_alive=True, user_agent="Batman/1.0") + {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} + >>> make_headers(accept_encoding=True) + {'accept-encoding': 'gzip,deflate'} + """ + headers = {} + if accept_encoding: + if isinstance(accept_encoding, str): + pass + elif isinstance(accept_encoding, list): + accept_encoding = ','.join(accept_encoding) + else: + accept_encoding = ACCEPT_ENCODING + headers['accept-encoding'] = accept_encoding + + if user_agent: + headers['user-agent'] = user_agent + + if keep_alive: + headers['connection'] = 'keep-alive' + + if basic_auth: + headers['authorization'] = 'Basic ' + \ + b64encode(b(basic_auth)).decode('utf-8') + + if proxy_basic_auth: + headers['proxy-authorization'] = 'Basic ' + \ + b64encode(b(proxy_basic_auth)).decode('utf-8') + + if disable_cache: + headers['cache-control'] = 'no-cache' + + return headers + + +def set_file_position(body, pos): + """ + If a position is provided, move file to that point. + Otherwise, we'll attempt to record a position for future use. + """ + if pos is not None: + rewind_body(body, pos) + elif getattr(body, 'tell', None) is not None: + try: + pos = body.tell() + except (IOError, OSError): + # This differentiates from None, allowing us to catch + # a failed `tell()` later when trying to rewind the body. + pos = _FAILEDTELL + + return pos + + +def rewind_body(body, body_pos): + """ + Attempt to rewind body to a certain position. + Primarily used for request redirects and retries. + + :param body: + File-like object that supports seek. + + :param int pos: + Position to seek to in file. + """ + body_seek = getattr(body, 'seek', None) + if body_seek is not None and isinstance(body_pos, integer_types): + try: + body_seek(body_pos) + except (IOError, OSError): + raise UnrewindableBodyError("An error occurred when rewinding request " + "body for redirect/retry.") + elif body_pos is _FAILEDTELL: + raise UnrewindableBodyError("Unable to record file position for rewinding " + "request body during a redirect/retry.") + else: + raise ValueError("body_pos must be of type integer, " + "instead it was %s." % type(body_pos)) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/response.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/response.py new file mode 100644 index 0000000..67cf730 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/response.py @@ -0,0 +1,81 @@ +from __future__ import absolute_import +from ..packages.six.moves import http_client as httplib + +from ..exceptions import HeaderParsingError + + +def is_fp_closed(obj): + """ + Checks whether a given file-like object is closed. + + :param obj: + The file-like object to check. + """ + + try: + # Check `isclosed()` first, in case Python3 doesn't set `closed`. + # GH Issue #928 + return obj.isclosed() + except AttributeError: + pass + + try: + # Check via the official file-like-object way. + return obj.closed + except AttributeError: + pass + + try: + # Check if the object is a container for another file-like object that + # gets released on exhaustion (e.g. HTTPResponse). + return obj.fp is None + except AttributeError: + pass + + raise ValueError("Unable to determine whether fp is closed.") + + +def assert_header_parsing(headers): + """ + Asserts whether all headers have been successfully parsed. + Extracts encountered errors from the result of parsing headers. + + Only works on Python 3. + + :param headers: Headers to verify. + :type headers: `httplib.HTTPMessage`. + + :raises urllib3.exceptions.HeaderParsingError: + If parsing errors are found. + """ + + # This will fail silently if we pass in the wrong kind of parameter. + # To make debugging easier add an explicit check. + if not isinstance(headers, httplib.HTTPMessage): + raise TypeError('expected httplib.Message, got {0}.'.format( + type(headers))) + + defects = getattr(headers, 'defects', None) + get_payload = getattr(headers, 'get_payload', None) + + unparsed_data = None + if get_payload: # Platform-specific: Python 3. + unparsed_data = get_payload() + + if defects or unparsed_data: + raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) + + +def is_response_to_head(response): + """ + Checks whether the request of a response has been a HEAD-request. + Handles the quirks of AppEngine. + + :param conn: + :type conn: :class:`httplib.HTTPResponse` + """ + # FIXME: Can we do this somehow without accessing private httplib _method? + method = response._method + if isinstance(method, int): # Platform-specific: Appengine + return method == 3 + return method.upper() == 'HEAD' diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/retry.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/retry.py new file mode 100644 index 0000000..7ad3dc6 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/retry.py @@ -0,0 +1,411 @@ +from __future__ import absolute_import +import time +import logging +from collections import namedtuple +from itertools import takewhile +import email +import re + +from ..exceptions import ( + ConnectTimeoutError, + MaxRetryError, + ProtocolError, + ReadTimeoutError, + ResponseError, + InvalidHeader, +) +from ..packages import six + + +log = logging.getLogger(__name__) + + +# Data structure for representing the metadata of requests that result in a retry. +RequestHistory = namedtuple('RequestHistory', ["method", "url", "error", + "status", "redirect_location"]) + + +class Retry(object): + """ Retry configuration. + + Each retry attempt will create a new Retry object with updated values, so + they can be safely reused. + + Retries can be defined as a default for a pool:: + + retries = Retry(connect=5, read=2, redirect=5) + http = PoolManager(retries=retries) + response = http.request('GET', 'http://example.com/') + + Or per-request (which overrides the default for the pool):: + + response = http.request('GET', 'http://example.com/', retries=Retry(10)) + + Retries can be disabled by passing ``False``:: + + response = http.request('GET', 'http://example.com/', retries=False) + + Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless + retries are disabled, in which case the causing exception will be raised. + + :param int total: + Total number of retries to allow. Takes precedence over other counts. + + Set to ``None`` to remove this constraint and fall back on other + counts. It's a good idea to set this to some sensibly-high value to + account for unexpected edge cases and avoid infinite retry loops. + + Set to ``0`` to fail on the first retry. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param int connect: + How many connection-related errors to retry on. + + These are errors raised before the request is sent to the remote server, + which we assume has not triggered the server to process the request. + + Set to ``0`` to fail on the first retry of this type. + + :param int read: + How many times to retry on read errors. + + These errors are raised after the request was sent to the server, so the + request may have side-effects. + + Set to ``0`` to fail on the first retry of this type. + + :param int redirect: + How many redirects to perform. Limit this to avoid infinite redirect + loops. + + A redirect is a HTTP response with a status code 301, 302, 303, 307 or + 308. + + Set to ``0`` to fail on the first retry of this type. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param int status: + How many times to retry on bad status codes. + + These are retries made on responses, where status code matches + ``status_forcelist``. + + Set to ``0`` to fail on the first retry of this type. + + :param iterable method_whitelist: + Set of uppercased HTTP method verbs that we should retry on. + + By default, we only retry on methods which are considered to be + idempotent (multiple requests with the same parameters end with the + same state). See :attr:`Retry.DEFAULT_METHOD_WHITELIST`. + + Set to a ``False`` value to retry on any verb. + + :param iterable status_forcelist: + A set of integer HTTP status codes that we should force a retry on. + A retry is initiated if the request method is in ``method_whitelist`` + and the response status code is in ``status_forcelist``. + + By default, this is disabled with ``None``. + + :param float backoff_factor: + A backoff factor to apply between attempts after the second try + (most errors are resolved immediately by a second try without a + delay). urllib3 will sleep for:: + + {backoff factor} * (2 ^ ({number of total retries} - 1)) + + seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep + for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer + than :attr:`Retry.BACKOFF_MAX`. + + By default, backoff is disabled (set to 0). + + :param bool raise_on_redirect: Whether, if the number of redirects is + exhausted, to raise a MaxRetryError, or to return a response with a + response code in the 3xx range. + + :param bool raise_on_status: Similar meaning to ``raise_on_redirect``: + whether we should raise an exception, or return a response, + if status falls in ``status_forcelist`` range and retries have + been exhausted. + + :param tuple history: The history of the request encountered during + each call to :meth:`~Retry.increment`. The list is in the order + the requests occurred. Each list item is of class :class:`RequestHistory`. + + :param bool respect_retry_after_header: + Whether to respect Retry-After header on status codes defined as + :attr:`Retry.RETRY_AFTER_STATUS_CODES` or not. + + :param iterable remove_headers_on_redirect: + Sequence of headers to remove from the request when a response + indicating a redirect is returned before firing off the redirected + request. + """ + + DEFAULT_METHOD_WHITELIST = frozenset([ + 'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE']) + + RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) + + DEFAULT_REDIRECT_HEADERS_BLACKLIST = frozenset(['Authorization']) + + #: Maximum backoff time. + BACKOFF_MAX = 120 + + def __init__(self, total=10, connect=None, read=None, redirect=None, status=None, + method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None, + backoff_factor=0, raise_on_redirect=True, raise_on_status=True, + history=None, respect_retry_after_header=True, + remove_headers_on_redirect=DEFAULT_REDIRECT_HEADERS_BLACKLIST): + + self.total = total + self.connect = connect + self.read = read + self.status = status + + if redirect is False or total is False: + redirect = 0 + raise_on_redirect = False + + self.redirect = redirect + self.status_forcelist = status_forcelist or set() + self.method_whitelist = method_whitelist + self.backoff_factor = backoff_factor + self.raise_on_redirect = raise_on_redirect + self.raise_on_status = raise_on_status + self.history = history or tuple() + self.respect_retry_after_header = respect_retry_after_header + self.remove_headers_on_redirect = remove_headers_on_redirect + + def new(self, **kw): + params = dict( + total=self.total, + connect=self.connect, read=self.read, redirect=self.redirect, status=self.status, + method_whitelist=self.method_whitelist, + status_forcelist=self.status_forcelist, + backoff_factor=self.backoff_factor, + raise_on_redirect=self.raise_on_redirect, + raise_on_status=self.raise_on_status, + history=self.history, + remove_headers_on_redirect=self.remove_headers_on_redirect + ) + params.update(kw) + return type(self)(**params) + + @classmethod + def from_int(cls, retries, redirect=True, default=None): + """ Backwards-compatibility for the old retries format.""" + if retries is None: + retries = default if default is not None else cls.DEFAULT + + if isinstance(retries, Retry): + return retries + + redirect = bool(redirect) and None + new_retries = cls(retries, redirect=redirect) + log.debug("Converted retries value: %r -> %r", retries, new_retries) + return new_retries + + def get_backoff_time(self): + """ Formula for computing the current backoff + + :rtype: float + """ + # We want to consider only the last consecutive errors sequence (Ignore redirects). + consecutive_errors_len = len(list(takewhile(lambda x: x.redirect_location is None, + reversed(self.history)))) + if consecutive_errors_len <= 1: + return 0 + + backoff_value = self.backoff_factor * (2 ** (consecutive_errors_len - 1)) + return min(self.BACKOFF_MAX, backoff_value) + + def parse_retry_after(self, retry_after): + # Whitespace: https://tools.ietf.org/html/rfc7230#section-3.2.4 + if re.match(r"^\s*[0-9]+\s*$", retry_after): + seconds = int(retry_after) + else: + retry_date_tuple = email.utils.parsedate(retry_after) + if retry_date_tuple is None: + raise InvalidHeader("Invalid Retry-After header: %s" % retry_after) + retry_date = time.mktime(retry_date_tuple) + seconds = retry_date - time.time() + + if seconds < 0: + seconds = 0 + + return seconds + + def get_retry_after(self, response): + """ Get the value of Retry-After in seconds. """ + + retry_after = response.getheader("Retry-After") + + if retry_after is None: + return None + + return self.parse_retry_after(retry_after) + + def sleep_for_retry(self, response=None): + retry_after = self.get_retry_after(response) + if retry_after: + time.sleep(retry_after) + return True + + return False + + def _sleep_backoff(self): + backoff = self.get_backoff_time() + if backoff <= 0: + return + time.sleep(backoff) + + def sleep(self, response=None): + """ Sleep between retry attempts. + + This method will respect a server's ``Retry-After`` response header + and sleep the duration of the time requested. If that is not present, it + will use an exponential backoff. By default, the backoff factor is 0 and + this method will return immediately. + """ + + if response: + slept = self.sleep_for_retry(response) + if slept: + return + + self._sleep_backoff() + + def _is_connection_error(self, err): + """ Errors when we're fairly sure that the server did not receive the + request, so it should be safe to retry. + """ + return isinstance(err, ConnectTimeoutError) + + def _is_read_error(self, err): + """ Errors that occur after the request has been started, so we should + assume that the server began processing it. + """ + return isinstance(err, (ReadTimeoutError, ProtocolError)) + + def _is_method_retryable(self, method): + """ Checks if a given HTTP method should be retried upon, depending if + it is included on the method whitelist. + """ + if self.method_whitelist and method.upper() not in self.method_whitelist: + return False + + return True + + def is_retry(self, method, status_code, has_retry_after=False): + """ Is this method/status code retryable? (Based on whitelists and control + variables such as the number of total retries to allow, whether to + respect the Retry-After header, whether this header is present, and + whether the returned status code is on the list of status codes to + be retried upon on the presence of the aforementioned header) + """ + if not self._is_method_retryable(method): + return False + + if self.status_forcelist and status_code in self.status_forcelist: + return True + + return (self.total and self.respect_retry_after_header and + has_retry_after and (status_code in self.RETRY_AFTER_STATUS_CODES)) + + def is_exhausted(self): + """ Are we out of retries? """ + retry_counts = (self.total, self.connect, self.read, self.redirect, self.status) + retry_counts = list(filter(None, retry_counts)) + if not retry_counts: + return False + + return min(retry_counts) < 0 + + def increment(self, method=None, url=None, response=None, error=None, + _pool=None, _stacktrace=None): + """ Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.HTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise six.reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + cause = 'unknown' + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise six.reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or not self._is_method_retryable(method): + raise six.reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = 'too many redirects' + redirect_location = response.get_redirect_location() + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and a the given method is in the whitelist + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format( + status_code=response.status) + status = response.status + + history = self.history + (RequestHistory(method, url, error, status, redirect_location),) + + new_retry = self.new( + total=total, + connect=connect, read=read, redirect=redirect, status=status_count, + history=history) + + if new_retry.is_exhausted(): + raise MaxRetryError(_pool, url, error or ResponseError(cause)) + + log.debug("Incremented Retry for (url='%s'): %r", url, new_retry) + + return new_retry + + def __repr__(self): + return ('{cls.__name__}(total={self.total}, connect={self.connect}, ' + 'read={self.read}, redirect={self.redirect}, status={self.status})').format( + cls=type(self), self=self) + + +# For backwards compatibility (equivalent to pre-v1.9): +Retry.DEFAULT = Retry(3) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py new file mode 100644 index 0000000..3254280 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py @@ -0,0 +1,396 @@ +from __future__ import absolute_import +import errno +import warnings +import hmac +import socket + +from binascii import hexlify, unhexlify +from hashlib import md5, sha1, sha256 + +from ..exceptions import SSLError, InsecurePlatformWarning, SNIMissingWarning +from ..packages import six + + +SSLContext = None +HAS_SNI = False +IS_PYOPENSSL = False +IS_SECURETRANSPORT = False + +# Maps the length of a digest to a possible hash function producing this digest +HASHFUNC_MAP = { + 32: md5, + 40: sha1, + 64: sha256, +} + + +def _const_compare_digest_backport(a, b): + """ + Compare two digests of equal length in constant time. + + The digests must be of type str/bytes. + Returns True if the digests match, and False otherwise. + """ + result = abs(len(a) - len(b)) + for l, r in zip(bytearray(a), bytearray(b)): + result |= l ^ r + return result == 0 + + +_const_compare_digest = getattr(hmac, 'compare_digest', + _const_compare_digest_backport) + + +try: # Test for SSL features + import ssl + from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23 + from ssl import HAS_SNI # Has SNI? +except ImportError: + pass + + +try: + from ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION +except ImportError: + OP_NO_SSLv2, OP_NO_SSLv3 = 0x1000000, 0x2000000 + OP_NO_COMPRESSION = 0x20000 + + +# Python 2.7 and earlier didn't have inet_pton on non-Linux +# so we fallback on inet_aton in those cases. This means that +# we can only detect IPv4 addresses in this case. +if hasattr(socket, 'inet_pton'): + inet_pton = socket.inet_pton +else: + # Maybe we can use ipaddress if the user has urllib3[secure]? + try: + from pip._vendor import ipaddress + + def inet_pton(_, host): + if isinstance(host, six.binary_type): + host = host.decode('ascii') + return ipaddress.ip_address(host) + + except ImportError: # Platform-specific: Non-Linux + def inet_pton(_, host): + return socket.inet_aton(host) + + +# A secure default. +# Sources for more information on TLS ciphers: +# +# - https://wiki.mozilla.org/Security/Server_Side_TLS +# - https://www.ssllabs.com/projects/best-practices/index.html +# - https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ +# +# The general intent is: +# - Prefer TLS 1.3 cipher suites +# - prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE), +# - prefer ECDHE over DHE for better performance, +# - prefer any AES-GCM and ChaCha20 over any AES-CBC for better performance and +# security, +# - prefer AES-GCM over ChaCha20 because hardware-accelerated AES is common, +# - disable NULL authentication, MD5 MACs and DSS for security reasons. +DEFAULT_CIPHERS = ':'.join([ + 'TLS13-AES-256-GCM-SHA384', + 'TLS13-CHACHA20-POLY1305-SHA256', + 'TLS13-AES-128-GCM-SHA256', + 'ECDH+AESGCM', + 'ECDH+CHACHA20', + 'DH+AESGCM', + 'DH+CHACHA20', + 'ECDH+AES256', + 'DH+AES256', + 'ECDH+AES128', + 'DH+AES', + 'RSA+AESGCM', + 'RSA+AES', + '!aNULL', + '!eNULL', + '!MD5', +]) + +try: + from ssl import SSLContext # Modern SSL? +except ImportError: + import sys + + class SSLContext(object): # Platform-specific: Python 2 & 3.1 + supports_set_ciphers = ((2, 7) <= sys.version_info < (3,) or + (3, 2) <= sys.version_info) + + def __init__(self, protocol_version): + self.protocol = protocol_version + # Use default values from a real SSLContext + self.check_hostname = False + self.verify_mode = ssl.CERT_NONE + self.ca_certs = None + self.options = 0 + self.certfile = None + self.keyfile = None + self.ciphers = None + + def load_cert_chain(self, certfile, keyfile): + self.certfile = certfile + self.keyfile = keyfile + + def load_verify_locations(self, cafile=None, capath=None): + self.ca_certs = cafile + + if capath is not None: + raise SSLError("CA directories not supported in older Pythons") + + def set_ciphers(self, cipher_suite): + if not self.supports_set_ciphers: + raise TypeError( + 'Your version of Python does not support setting ' + 'a custom cipher suite. Please upgrade to Python ' + '2.7, 3.2, or later if you need this functionality.' + ) + self.ciphers = cipher_suite + + def wrap_socket(self, socket, server_hostname=None, server_side=False): + warnings.warn( + 'A true SSLContext object is not available. This prevents ' + 'urllib3 from configuring SSL appropriately and may cause ' + 'certain SSL connections to fail. You can upgrade to a newer ' + 'version of Python to solve this. For more information, see ' + 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html' + '#ssl-warnings', + InsecurePlatformWarning + ) + kwargs = { + 'keyfile': self.keyfile, + 'certfile': self.certfile, + 'ca_certs': self.ca_certs, + 'cert_reqs': self.verify_mode, + 'ssl_version': self.protocol, + 'server_side': server_side, + } + if self.supports_set_ciphers: # Platform-specific: Python 2.7+ + return wrap_socket(socket, ciphers=self.ciphers, **kwargs) + else: # Platform-specific: Python 2.6 + return wrap_socket(socket, **kwargs) + + +def assert_fingerprint(cert, fingerprint): + """ + Checks if given fingerprint matches the supplied certificate. + + :param cert: + Certificate as bytes object. + :param fingerprint: + Fingerprint as string of hexdigits, can be interspersed by colons. + """ + + fingerprint = fingerprint.replace(':', '').lower() + digest_length = len(fingerprint) + hashfunc = HASHFUNC_MAP.get(digest_length) + if not hashfunc: + raise SSLError( + 'Fingerprint of invalid length: {0}'.format(fingerprint)) + + # We need encode() here for py32; works on py2 and p33. + fingerprint_bytes = unhexlify(fingerprint.encode()) + + cert_digest = hashfunc(cert).digest() + + if not _const_compare_digest(cert_digest, fingerprint_bytes): + raise SSLError('Fingerprints did not match. Expected "{0}", got "{1}".' + .format(fingerprint, hexlify(cert_digest))) + + +def resolve_cert_reqs(candidate): + """ + Resolves the argument to a numeric constant, which can be passed to + the wrap_socket function/method from the ssl module. + Defaults to :data:`ssl.CERT_NONE`. + If given a string it is assumed to be the name of the constant in the + :mod:`ssl` module or its abbreviation. + (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. + If it's neither `None` nor a string we assume it is already the numeric + constant which can directly be passed to wrap_socket. + """ + if candidate is None: + return CERT_NONE + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, 'CERT_' + candidate) + return res + + return candidate + + +def resolve_ssl_version(candidate): + """ + like resolve_cert_reqs + """ + if candidate is None: + return PROTOCOL_SSLv23 + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, 'PROTOCOL_' + candidate) + return res + + return candidate + + +def create_urllib3_context(ssl_version=None, cert_reqs=None, + options=None, ciphers=None): + """All arguments have the same meaning as ``ssl_wrap_socket``. + + By default, this function does a lot of the same work that + ``ssl.create_default_context`` does on Python 3.4+. It: + + - Disables SSLv2, SSLv3, and compression + - Sets a restricted set of server ciphers + + If you wish to enable SSLv3, you can do:: + + from pip._vendor.urllib3.util import ssl_ + context = ssl_.create_urllib3_context() + context.options &= ~ssl_.OP_NO_SSLv3 + + You can do the same to enable compression (substituting ``COMPRESSION`` + for ``SSLv3`` in the last line above). + + :param ssl_version: + The desired protocol version to use. This will default to + PROTOCOL_SSLv23 which will negotiate the highest protocol that both + the server and your installation of OpenSSL support. + :param cert_reqs: + Whether to require the certificate verification. This defaults to + ``ssl.CERT_REQUIRED``. + :param options: + Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``, + ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``. + :param ciphers: + Which cipher suites to allow the server to select. + :returns: + Constructed SSLContext object with specified options + :rtype: SSLContext + """ + context = SSLContext(ssl_version or ssl.PROTOCOL_SSLv23) + + # Setting the default here, as we may have no ssl module on import + cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs + + if options is None: + options = 0 + # SSLv2 is easily broken and is considered harmful and dangerous + options |= OP_NO_SSLv2 + # SSLv3 has several problems and is now dangerous + options |= OP_NO_SSLv3 + # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ + # (issue #309) + options |= OP_NO_COMPRESSION + + context.options |= options + + if getattr(context, 'supports_set_ciphers', True): # Platform-specific: Python 2.6 + context.set_ciphers(ciphers or DEFAULT_CIPHERS) + + context.verify_mode = cert_reqs + if getattr(context, 'check_hostname', None) is not None: # Platform-specific: Python 3.2 + # We do our own verification, including fingerprints and alternative + # hostnames. So disable it here + context.check_hostname = False + return context + + +def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, + ca_certs=None, server_hostname=None, + ssl_version=None, ciphers=None, ssl_context=None, + ca_cert_dir=None): + """ + All arguments except for server_hostname, ssl_context, and ca_cert_dir have + the same meaning as they do when using :func:`ssl.wrap_socket`. + + :param server_hostname: + When SNI is supported, the expected hostname of the certificate + :param ssl_context: + A pre-made :class:`SSLContext` object. If none is provided, one will + be created using :func:`create_urllib3_context`. + :param ciphers: + A string of ciphers we wish the client to support. This is not + supported on Python 2.6 as the ssl module does not support it. + :param ca_cert_dir: + A directory containing CA certificates in multiple separate files, as + supported by OpenSSL's -CApath flag or the capath argument to + SSLContext.load_verify_locations(). + """ + context = ssl_context + if context is None: + # Note: This branch of code and all the variables in it are no longer + # used by urllib3 itself. We should consider deprecating and removing + # this code. + context = create_urllib3_context(ssl_version, cert_reqs, + ciphers=ciphers) + + if ca_certs or ca_cert_dir: + try: + context.load_verify_locations(ca_certs, ca_cert_dir) + except IOError as e: # Platform-specific: Python 2.6, 2.7, 3.2 + raise SSLError(e) + # Py33 raises FileNotFoundError which subclasses OSError + # These are not equivalent unless we check the errno attribute + except OSError as e: # Platform-specific: Python 3.3 and beyond + if e.errno == errno.ENOENT: + raise SSLError(e) + raise + elif getattr(context, 'load_default_certs', None) is not None: + # try to load OS default certs; works well on Windows (require Python3.4+) + context.load_default_certs() + + if certfile: + context.load_cert_chain(certfile, keyfile) + + # If we detect server_hostname is an IP address then the SNI + # extension should not be used according to RFC3546 Section 3.1 + # We shouldn't warn the user if SNI isn't available but we would + # not be using SNI anyways due to IP address for server_hostname. + if ((server_hostname is not None and not is_ipaddress(server_hostname)) + or IS_SECURETRANSPORT): + if HAS_SNI and server_hostname is not None: + return context.wrap_socket(sock, server_hostname=server_hostname) + + warnings.warn( + 'An HTTPS request has been made, but the SNI (Server Name ' + 'Indication) extension to TLS is not available on this platform. ' + 'This may cause the server to present an incorrect TLS ' + 'certificate, which can cause validation failures. You can upgrade to ' + 'a newer version of Python to solve this. For more information, see ' + 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html' + '#ssl-warnings', + SNIMissingWarning + ) + + return context.wrap_socket(sock) + + +def is_ipaddress(hostname): + """Detects whether the hostname given is an IP address. + + :param str hostname: Hostname to examine. + :return: True if the hostname is an IP address, False otherwise. + """ + if six.PY3 and isinstance(hostname, six.binary_type): + # IDN A-label bytes are ASCII compatible. + hostname = hostname.decode('ascii') + + families = [socket.AF_INET] + if hasattr(socket, 'AF_INET6'): + families.append(socket.AF_INET6) + + for af in families: + try: + inet_pton(af, hostname) + except (socket.error, ValueError, OSError): + pass + else: + return True + return False diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/timeout.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/timeout.py new file mode 100644 index 0000000..cec817e --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/timeout.py @@ -0,0 +1,242 @@ +from __future__ import absolute_import +# The default socket timeout, used by httplib to indicate that no timeout was +# specified by the user +from socket import _GLOBAL_DEFAULT_TIMEOUT +import time + +from ..exceptions import TimeoutStateError + +# A sentinel value to indicate that no timeout was specified by the user in +# urllib3 +_Default = object() + + +# Use time.monotonic if available. +current_time = getattr(time, "monotonic", time.time) + + +class Timeout(object): + """ Timeout configuration. + + Timeouts can be defined as a default for a pool:: + + timeout = Timeout(connect=2.0, read=7.0) + http = PoolManager(timeout=timeout) + response = http.request('GET', 'http://example.com/') + + Or per-request (which overrides the default for the pool):: + + response = http.request('GET', 'http://example.com/', timeout=Timeout(10)) + + Timeouts can be disabled by setting all the parameters to ``None``:: + + no_timeout = Timeout(connect=None, read=None) + response = http.request('GET', 'http://example.com/, timeout=no_timeout) + + + :param total: + This combines the connect and read timeouts into one; the read timeout + will be set to the time leftover from the connect attempt. In the + event that both a connect timeout and a total are specified, or a read + timeout and a total are specified, the shorter timeout will be applied. + + Defaults to None. + + :type total: integer, float, or None + + :param connect: + The maximum amount of time to wait for a connection attempt to a server + to succeed. Omitting the parameter will default the connect timeout to + the system default, probably `the global default timeout in socket.py + <http://hg.python.org/cpython/file/603b4d593758/Lib/socket.py#l535>`_. + None will set an infinite timeout for connection attempts. + + :type connect: integer, float, or None + + :param read: + The maximum amount of time to wait between consecutive + read operations for a response from the server. Omitting + the parameter will default the read timeout to the system + default, probably `the global default timeout in socket.py + <http://hg.python.org/cpython/file/603b4d593758/Lib/socket.py#l535>`_. + None will set an infinite timeout. + + :type read: integer, float, or None + + .. note:: + + Many factors can affect the total amount of time for urllib3 to return + an HTTP response. + + For example, Python's DNS resolver does not obey the timeout specified + on the socket. Other factors that can affect total request time include + high CPU load, high swap, the program running at a low priority level, + or other behaviors. + + In addition, the read and total timeouts only measure the time between + read operations on the socket connecting the client and the server, + not the total amount of time for the request to return a complete + response. For most requests, the timeout is raised because the server + has not sent the first byte in the specified time. This is not always + the case; if a server streams one byte every fifteen seconds, a timeout + of 20 seconds will not trigger, even though the request will take + several minutes to complete. + + If your goal is to cut off any request after a set amount of wall clock + time, consider having a second "watcher" thread to cut off a slow + request. + """ + + #: A sentinel object representing the default timeout value + DEFAULT_TIMEOUT = _GLOBAL_DEFAULT_TIMEOUT + + def __init__(self, total=None, connect=_Default, read=_Default): + self._connect = self._validate_timeout(connect, 'connect') + self._read = self._validate_timeout(read, 'read') + self.total = self._validate_timeout(total, 'total') + self._start_connect = None + + def __str__(self): + return '%s(connect=%r, read=%r, total=%r)' % ( + type(self).__name__, self._connect, self._read, self.total) + + @classmethod + def _validate_timeout(cls, value, name): + """ Check that a timeout attribute is valid. + + :param value: The timeout value to validate + :param name: The name of the timeout attribute to validate. This is + used to specify in error messages. + :return: The validated and casted version of the given value. + :raises ValueError: If it is a numeric value less than or equal to + zero, or the type is not an integer, float, or None. + """ + if value is _Default: + return cls.DEFAULT_TIMEOUT + + if value is None or value is cls.DEFAULT_TIMEOUT: + return value + + if isinstance(value, bool): + raise ValueError("Timeout cannot be a boolean value. It must " + "be an int, float or None.") + try: + float(value) + except (TypeError, ValueError): + raise ValueError("Timeout value %s was %s, but it must be an " + "int, float or None." % (name, value)) + + try: + if value <= 0: + raise ValueError("Attempted to set %s timeout to %s, but the " + "timeout cannot be set to a value less " + "than or equal to 0." % (name, value)) + except TypeError: # Python 3 + raise ValueError("Timeout value %s was %s, but it must be an " + "int, float or None." % (name, value)) + + return value + + @classmethod + def from_float(cls, timeout): + """ Create a new Timeout from a legacy timeout value. + + The timeout value used by httplib.py sets the same timeout on the + connect(), and recv() socket requests. This creates a :class:`Timeout` + object that sets the individual timeouts to the ``timeout`` value + passed to this function. + + :param timeout: The legacy timeout value. + :type timeout: integer, float, sentinel default object, or None + :return: Timeout object + :rtype: :class:`Timeout` + """ + return Timeout(read=timeout, connect=timeout) + + def clone(self): + """ Create a copy of the timeout object + + Timeout properties are stored per-pool but each request needs a fresh + Timeout object to ensure each one has its own start/stop configured. + + :return: a copy of the timeout object + :rtype: :class:`Timeout` + """ + # We can't use copy.deepcopy because that will also create a new object + # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to + # detect the user default. + return Timeout(connect=self._connect, read=self._read, + total=self.total) + + def start_connect(self): + """ Start the timeout clock, used during a connect() attempt + + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to start a timer that has been started already. + """ + if self._start_connect is not None: + raise TimeoutStateError("Timeout timer has already been started.") + self._start_connect = current_time() + return self._start_connect + + def get_connect_duration(self): + """ Gets the time elapsed since the call to :meth:`start_connect`. + + :return: Elapsed time. + :rtype: float + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to get duration for a timer that hasn't been started. + """ + if self._start_connect is None: + raise TimeoutStateError("Can't get connect duration for timer " + "that has not started.") + return current_time() - self._start_connect + + @property + def connect_timeout(self): + """ Get the value to use when setting a connection timeout. + + This will be a positive float or integer, the value None + (never timeout), or the default system timeout. + + :return: Connect timeout. + :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None + """ + if self.total is None: + return self._connect + + if self._connect is None or self._connect is self.DEFAULT_TIMEOUT: + return self.total + + return min(self._connect, self.total) + + @property + def read_timeout(self): + """ Get the value for the read timeout. + + This assumes some time has elapsed in the connection timeout and + computes the read timeout appropriately. + + If self.total is set, the read timeout is dependent on the amount of + time taken by the connect timeout. If the connection time has not been + established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be + raised. + + :return: Value to use for the read timeout. + :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None + :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` + has not yet been called on this object. + """ + if (self.total is not None and + self.total is not self.DEFAULT_TIMEOUT and + self._read is not None and + self._read is not self.DEFAULT_TIMEOUT): + # In case the connect timeout has not yet been established. + if self._start_connect is None: + return self._read + return max(0, min(self.total - self.get_connect_duration(), + self._read)) + elif self.total is not None and self.total is not self.DEFAULT_TIMEOUT: + return max(0, self.total - self.get_connect_duration()) + else: + return self._read diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/url.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/url.py new file mode 100644 index 0000000..6b6f996 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/url.py @@ -0,0 +1,230 @@ +from __future__ import absolute_import +from collections import namedtuple + +from ..exceptions import LocationParseError + + +url_attrs = ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'] + +# We only want to normalize urls with an HTTP(S) scheme. +# urllib3 infers URLs without a scheme (None) to be http. +NORMALIZABLE_SCHEMES = ('http', 'https', None) + + +class Url(namedtuple('Url', url_attrs)): + """ + Datastructure for representing an HTTP URL. Used as a return value for + :func:`parse_url`. Both the scheme and host are normalized as they are + both case-insensitive according to RFC 3986. + """ + __slots__ = () + + def __new__(cls, scheme=None, auth=None, host=None, port=None, path=None, + query=None, fragment=None): + if path and not path.startswith('/'): + path = '/' + path + if scheme: + scheme = scheme.lower() + if host and scheme in NORMALIZABLE_SCHEMES: + host = host.lower() + return super(Url, cls).__new__(cls, scheme, auth, host, port, path, + query, fragment) + + @property + def hostname(self): + """For backwards-compatibility with urlparse. We're nice like that.""" + return self.host + + @property + def request_uri(self): + """Absolute path including the query string.""" + uri = self.path or '/' + + if self.query is not None: + uri += '?' + self.query + + return uri + + @property + def netloc(self): + """Network location including host and port""" + if self.port: + return '%s:%d' % (self.host, self.port) + return self.host + + @property + def url(self): + """ + Convert self into a url + + This function should more or less round-trip with :func:`.parse_url`. The + returned url may not be exactly the same as the url inputted to + :func:`.parse_url`, but it should be equivalent by the RFC (e.g., urls + with a blank port will have : removed). + + Example: :: + + >>> U = parse_url('http://google.com/mail/') + >>> U.url + 'http://google.com/mail/' + >>> Url('http', 'username:password', 'host.com', 80, + ... '/path', 'query', 'fragment').url + 'http://username:password@host.com:80/path?query#fragment' + """ + scheme, auth, host, port, path, query, fragment = self + url = '' + + # We use "is not None" we want things to happen with empty strings (or 0 port) + if scheme is not None: + url += scheme + '://' + if auth is not None: + url += auth + '@' + if host is not None: + url += host + if port is not None: + url += ':' + str(port) + if path is not None: + url += path + if query is not None: + url += '?' + query + if fragment is not None: + url += '#' + fragment + + return url + + def __str__(self): + return self.url + + +def split_first(s, delims): + """ + Given a string and an iterable of delimiters, split on the first found + delimiter. Return two split parts and the matched delimiter. + + If not found, then the first part is the full input string. + + Example:: + + >>> split_first('foo/bar?baz', '?/=') + ('foo', 'bar?baz', '/') + >>> split_first('foo/bar?baz', '123') + ('foo/bar?baz', '', None) + + Scales linearly with number of delims. Not ideal for large number of delims. + """ + min_idx = None + min_delim = None + for d in delims: + idx = s.find(d) + if idx < 0: + continue + + if min_idx is None or idx < min_idx: + min_idx = idx + min_delim = d + + if min_idx is None or min_idx < 0: + return s, '', None + + return s[:min_idx], s[min_idx + 1:], min_delim + + +def parse_url(url): + """ + Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is + performed to parse incomplete urls. Fields not provided will be None. + + Partly backwards-compatible with :mod:`urlparse`. + + Example:: + + >>> parse_url('http://google.com/mail/') + Url(scheme='http', host='google.com', port=None, path='/mail/', ...) + >>> parse_url('google.com:80') + Url(scheme=None, host='google.com', port=80, path=None, ...) + >>> parse_url('/foo?bar') + Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) + """ + + # While this code has overlap with stdlib's urlparse, it is much + # simplified for our needs and less annoying. + # Additionally, this implementations does silly things to be optimal + # on CPython. + + if not url: + # Empty + return Url() + + scheme = None + auth = None + host = None + port = None + path = None + fragment = None + query = None + + # Scheme + if '://' in url: + scheme, url = url.split('://', 1) + + # Find the earliest Authority Terminator + # (http://tools.ietf.org/html/rfc3986#section-3.2) + url, path_, delim = split_first(url, ['/', '?', '#']) + + if delim: + # Reassemble the path + path = delim + path_ + + # Auth + if '@' in url: + # Last '@' denotes end of auth part + auth, url = url.rsplit('@', 1) + + # IPv6 + if url and url[0] == '[': + host, url = url.split(']', 1) + host += ']' + + # Port + if ':' in url: + _host, port = url.split(':', 1) + + if not host: + host = _host + + if port: + # If given, ports must be integers. No whitespace, no plus or + # minus prefixes, no non-integer digits such as ^2 (superscript). + if not port.isdigit(): + raise LocationParseError(url) + try: + port = int(port) + except ValueError: + raise LocationParseError(url) + else: + # Blank ports are cool, too. (rfc3986#section-3.2.3) + port = None + + elif not host and url: + host = url + + if not path: + return Url(scheme, auth, host, port, path, query, fragment) + + # Fragment + if '#' in path: + path, fragment = path.split('#', 1) + + # Query + if '?' in path: + path, query = path.split('?', 1) + + return Url(scheme, auth, host, port, path, query, fragment) + + +def get_host(url): + """ + Deprecated. Use :func:`parse_url` instead. + """ + p = parse_url(url) + return p.scheme or 'http', p.hostname, p.port diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/wait.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/wait.py new file mode 100644 index 0000000..fa686ef --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/wait.py @@ -0,0 +1,153 @@ +import errno +from functools import partial +import select +import sys +try: + from time import monotonic +except ImportError: + from time import time as monotonic + +__all__ = ["NoWayToWaitForSocketError", "wait_for_read", "wait_for_write"] + + +class NoWayToWaitForSocketError(Exception): + pass + + +# How should we wait on sockets? +# +# There are two types of APIs you can use for waiting on sockets: the fancy +# modern stateful APIs like epoll/kqueue, and the older stateless APIs like +# select/poll. The stateful APIs are more efficient when you have a lots of +# sockets to keep track of, because you can set them up once and then use them +# lots of times. But we only ever want to wait on a single socket at a time +# and don't want to keep track of state, so the stateless APIs are actually +# more efficient. So we want to use select() or poll(). +# +# Now, how do we choose between select() and poll()? On traditional Unixes, +# select() has a strange calling convention that makes it slow, or fail +# altogether, for high-numbered file descriptors. The point of poll() is to fix +# that, so on Unixes, we prefer poll(). +# +# On Windows, there is no poll() (or at least Python doesn't provide a wrapper +# for it), but that's OK, because on Windows, select() doesn't have this +# strange calling convention; plain select() works fine. +# +# So: on Windows we use select(), and everywhere else we use poll(). We also +# fall back to select() in case poll() is somehow broken or missing. + +if sys.version_info >= (3, 5): + # Modern Python, that retries syscalls by default + def _retry_on_intr(fn, timeout): + return fn(timeout) +else: + # Old and broken Pythons. + def _retry_on_intr(fn, timeout): + if timeout is not None and timeout <= 0: + return fn(timeout) + + if timeout is None: + deadline = float("inf") + else: + deadline = monotonic() + timeout + + while True: + try: + return fn(timeout) + # OSError for 3 <= pyver < 3.5, select.error for pyver <= 2.7 + except (OSError, select.error) as e: + # 'e.args[0]' incantation works for both OSError and select.error + if e.args[0] != errno.EINTR: + raise + else: + timeout = deadline - monotonic() + if timeout < 0: + timeout = 0 + if timeout == float("inf"): + timeout = None + continue + + +def select_wait_for_socket(sock, read=False, write=False, timeout=None): + if not read and not write: + raise RuntimeError("must specify at least one of read=True, write=True") + rcheck = [] + wcheck = [] + if read: + rcheck.append(sock) + if write: + wcheck.append(sock) + # When doing a non-blocking connect, most systems signal success by + # marking the socket writable. Windows, though, signals success by marked + # it as "exceptional". We paper over the difference by checking the write + # sockets for both conditions. (The stdlib selectors module does the same + # thing.) + fn = partial(select.select, rcheck, wcheck, wcheck) + rready, wready, xready = _retry_on_intr(fn, timeout) + return bool(rready or wready or xready) + + +def poll_wait_for_socket(sock, read=False, write=False, timeout=None): + if not read and not write: + raise RuntimeError("must specify at least one of read=True, write=True") + mask = 0 + if read: + mask |= select.POLLIN + if write: + mask |= select.POLLOUT + poll_obj = select.poll() + poll_obj.register(sock, mask) + + # For some reason, poll() takes timeout in milliseconds + def do_poll(t): + if t is not None: + t *= 1000 + return poll_obj.poll(t) + + return bool(_retry_on_intr(do_poll, timeout)) + + +def null_wait_for_socket(*args, **kwargs): + raise NoWayToWaitForSocketError("no select-equivalent available") + + +def _have_working_poll(): + # Apparently some systems have a select.poll that fails as soon as you try + # to use it, either due to strange configuration or broken monkeypatching + # from libraries like eventlet/greenlet. + try: + poll_obj = select.poll() + poll_obj.poll(0) + except (AttributeError, OSError): + return False + else: + return True + + +def wait_for_socket(*args, **kwargs): + # We delay choosing which implementation to use until the first time we're + # called. We could do it at import time, but then we might make the wrong + # decision if someone goes wild with monkeypatching select.poll after + # we're imported. + global wait_for_socket + if _have_working_poll(): + wait_for_socket = poll_wait_for_socket + elif hasattr(select, "select"): + wait_for_socket = select_wait_for_socket + else: # Platform-specific: Appengine. + wait_for_socket = null_wait_for_socket + return wait_for_socket(*args, **kwargs) + + +def wait_for_read(sock, timeout=None): + """ Waits for reading to be available on a given socket. + Returns True if the socket is readable, or False if the timeout expired. + """ + return wait_for_socket(sock, read=True, timeout=timeout) + + +def wait_for_write(sock, timeout=None): + """ Waits for writing to be available on a given socket. + Returns True if the socket is readable, or False if the timeout expired. + """ + return wait_for_socket(sock, write=True, timeout=timeout) diff --git a/venv/Lib/site-packages/pip/_vendor/webencodings/__init__.py b/venv/Lib/site-packages/pip/_vendor/webencodings/__init__.py new file mode 100644 index 0000000..d21d697 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/webencodings/__init__.py @@ -0,0 +1,342 @@ +# coding: utf-8 +""" + + webencodings + ~~~~~~~~~~~~ + + This is a Python implementation of the `WHATWG Encoding standard + <http://encoding.spec.whatwg.org/>`. See README for details. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +from __future__ import unicode_literals + +import codecs + +from .labels import LABELS + + +VERSION = '0.5.1' + + +# Some names in Encoding are not valid Python aliases. Remap these. +PYTHON_NAMES = { + 'iso-8859-8-i': 'iso-8859-8', + 'x-mac-cyrillic': 'mac-cyrillic', + 'macintosh': 'mac-roman', + 'windows-874': 'cp874'} + +CACHE = {} + + +def ascii_lower(string): + r"""Transform (only) ASCII letters to lower case: A-Z is mapped to a-z. + + :param string: An Unicode string. + :returns: A new Unicode string. + + This is used for `ASCII case-insensitive + <http://encoding.spec.whatwg.org/#ascii-case-insensitive>`_ + matching of encoding labels. + The same matching is also used, among other things, + for `CSS keywords <http://dev.w3.org/csswg/css-values/#keywords>`_. + + This is different from the :meth:`~py:str.lower` method of Unicode strings + which also affect non-ASCII characters, + sometimes mapping them into the ASCII range: + + >>> keyword = u'Bac\N{KELVIN SIGN}ground' + >>> assert keyword.lower() == u'background' + >>> assert ascii_lower(keyword) != keyword.lower() + >>> assert ascii_lower(keyword) == u'bac\N{KELVIN SIGN}ground' + + """ + # This turns out to be faster than unicode.translate() + return string.encode('utf8').lower().decode('utf8') + + +def lookup(label): + """ + Look for an encoding by its label. + This is the spec’s `get an encoding + <http://encoding.spec.whatwg.org/#concept-encoding-get>`_ algorithm. + Supported labels are listed there. + + :param label: A string. + :returns: + An :class:`Encoding` object, or :obj:`None` for an unknown label. + + """ + # Only strip ASCII whitespace: U+0009, U+000A, U+000C, U+000D, and U+0020. + label = ascii_lower(label.strip('\t\n\f\r ')) + name = LABELS.get(label) + if name is None: + return None + encoding = CACHE.get(name) + if encoding is None: + if name == 'x-user-defined': + from .x_user_defined import codec_info + else: + python_name = PYTHON_NAMES.get(name, name) + # Any python_name value that gets to here should be valid. + codec_info = codecs.lookup(python_name) + encoding = Encoding(name, codec_info) + CACHE[name] = encoding + return encoding + + +def _get_encoding(encoding_or_label): + """ + Accept either an encoding object or label. + + :param encoding: An :class:`Encoding` object or a label string. + :returns: An :class:`Encoding` object. + :raises: :exc:`~exceptions.LookupError` for an unknown label. + + """ + if hasattr(encoding_or_label, 'codec_info'): + return encoding_or_label + + encoding = lookup(encoding_or_label) + if encoding is None: + raise LookupError('Unknown encoding label: %r' % encoding_or_label) + return encoding + + +class Encoding(object): + """Reresents a character encoding such as UTF-8, + that can be used for decoding or encoding. + + .. attribute:: name + + Canonical name of the encoding + + .. attribute:: codec_info + + The actual implementation of the encoding, + a stdlib :class:`~codecs.CodecInfo` object. + See :func:`codecs.register`. + + """ + def __init__(self, name, codec_info): + self.name = name + self.codec_info = codec_info + + def __repr__(self): + return '<Encoding %s>' % self.name + + +#: The UTF-8 encoding. Should be used for new content and formats. +UTF8 = lookup('utf-8') + +_UTF16LE = lookup('utf-16le') +_UTF16BE = lookup('utf-16be') + + +def decode(input, fallback_encoding, errors='replace'): + """ + Decode a single string. + + :param input: A byte string + :param fallback_encoding: + An :class:`Encoding` object or a label string. + The encoding to use if :obj:`input` does note have a BOM. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + :return: + A ``(output, encoding)`` tuple of an Unicode string + and an :obj:`Encoding`. + + """ + # Fail early if `encoding` is an invalid label. + fallback_encoding = _get_encoding(fallback_encoding) + bom_encoding, input = _detect_bom(input) + encoding = bom_encoding or fallback_encoding + return encoding.codec_info.decode(input, errors)[0], encoding + + +def _detect_bom(input): + """Return (bom_encoding, input), with any BOM removed from the input.""" + if input.startswith(b'\xFF\xFE'): + return _UTF16LE, input[2:] + if input.startswith(b'\xFE\xFF'): + return _UTF16BE, input[2:] + if input.startswith(b'\xEF\xBB\xBF'): + return UTF8, input[3:] + return None, input + + +def encode(input, encoding=UTF8, errors='strict'): + """ + Encode a single string. + + :param input: An Unicode string. + :param encoding: An :class:`Encoding` object or a label string. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + :return: A byte string. + + """ + return _get_encoding(encoding).codec_info.encode(input, errors)[0] + + +def iter_decode(input, fallback_encoding, errors='replace'): + """ + "Pull"-based decoder. + + :param input: + An iterable of byte strings. + + The input is first consumed just enough to determine the encoding + based on the precense of a BOM, + then consumed on demand when the return value is. + :param fallback_encoding: + An :class:`Encoding` object or a label string. + The encoding to use if :obj:`input` does note have a BOM. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + :returns: + An ``(output, encoding)`` tuple. + :obj:`output` is an iterable of Unicode strings, + :obj:`encoding` is the :obj:`Encoding` that is being used. + + """ + + decoder = IncrementalDecoder(fallback_encoding, errors) + generator = _iter_decode_generator(input, decoder) + encoding = next(generator) + return generator, encoding + + +def _iter_decode_generator(input, decoder): + """Return a generator that first yields the :obj:`Encoding`, + then yields output chukns as Unicode strings. + + """ + decode = decoder.decode + input = iter(input) + for chunck in input: + output = decode(chunck) + if output: + assert decoder.encoding is not None + yield decoder.encoding + yield output + break + else: + # Input exhausted without determining the encoding + output = decode(b'', final=True) + assert decoder.encoding is not None + yield decoder.encoding + if output: + yield output + return + + for chunck in input: + output = decode(chunck) + if output: + yield output + output = decode(b'', final=True) + if output: + yield output + + +def iter_encode(input, encoding=UTF8, errors='strict'): + """ + “Pull”-based encoder. + + :param input: An iterable of Unicode strings. + :param encoding: An :class:`Encoding` object or a label string. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + :returns: An iterable of byte strings. + + """ + # Fail early if `encoding` is an invalid label. + encode = IncrementalEncoder(encoding, errors).encode + return _iter_encode_generator(input, encode) + + +def _iter_encode_generator(input, encode): + for chunck in input: + output = encode(chunck) + if output: + yield output + output = encode('', final=True) + if output: + yield output + + +class IncrementalDecoder(object): + """ + “Push”-based decoder. + + :param fallback_encoding: + An :class:`Encoding` object or a label string. + The encoding to use if :obj:`input` does note have a BOM. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + + """ + def __init__(self, fallback_encoding, errors='replace'): + # Fail early if `encoding` is an invalid label. + self._fallback_encoding = _get_encoding(fallback_encoding) + self._errors = errors + self._buffer = b'' + self._decoder = None + #: The actual :class:`Encoding` that is being used, + #: or :obj:`None` if that is not determined yet. + #: (Ie. if there is not enough input yet to determine + #: if there is a BOM.) + self.encoding = None # Not known yet. + + def decode(self, input, final=False): + """Decode one chunk of the input. + + :param input: A byte string. + :param final: + Indicate that no more input is available. + Must be :obj:`True` if this is the last call. + :returns: An Unicode string. + + """ + decoder = self._decoder + if decoder is not None: + return decoder(input, final) + + input = self._buffer + input + encoding, input = _detect_bom(input) + if encoding is None: + if len(input) < 3 and not final: # Not enough data yet. + self._buffer = input + return '' + else: # No BOM + encoding = self._fallback_encoding + decoder = encoding.codec_info.incrementaldecoder(self._errors).decode + self._decoder = decoder + self.encoding = encoding + return decoder(input, final) + + +class IncrementalEncoder(object): + """ + “Push”-based encoder. + + :param encoding: An :class:`Encoding` object or a label string. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + + .. method:: encode(input, final=False) + + :param input: An Unicode string. + :param final: + Indicate that no more input is available. + Must be :obj:`True` if this is the last call. + :returns: A byte string. + + """ + def __init__(self, encoding=UTF8, errors='strict'): + encoding = _get_encoding(encoding) + self.encode = encoding.codec_info.incrementalencoder(errors).encode diff --git a/venv/Lib/site-packages/pip/_vendor/webencodings/labels.py b/venv/Lib/site-packages/pip/_vendor/webencodings/labels.py new file mode 100644 index 0000000..29cbf91 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/webencodings/labels.py @@ -0,0 +1,231 @@ +""" + + webencodings.labels + ~~~~~~~~~~~~~~~~~~~ + + Map encoding labels to their name. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +# XXX Do not edit! +# This file is automatically generated by mklabels.py + +LABELS = { + 'unicode-1-1-utf-8': 'utf-8', + 'utf-8': 'utf-8', + 'utf8': 'utf-8', + '866': 'ibm866', + 'cp866': 'ibm866', + 'csibm866': 'ibm866', + 'ibm866': 'ibm866', + 'csisolatin2': 'iso-8859-2', + 'iso-8859-2': 'iso-8859-2', + 'iso-ir-101': 'iso-8859-2', + 'iso8859-2': 'iso-8859-2', + 'iso88592': 'iso-8859-2', + 'iso_8859-2': 'iso-8859-2', + 'iso_8859-2:1987': 'iso-8859-2', + 'l2': 'iso-8859-2', + 'latin2': 'iso-8859-2', + 'csisolatin3': 'iso-8859-3', + 'iso-8859-3': 'iso-8859-3', + 'iso-ir-109': 'iso-8859-3', + 'iso8859-3': 'iso-8859-3', + 'iso88593': 'iso-8859-3', + 'iso_8859-3': 'iso-8859-3', + 'iso_8859-3:1988': 'iso-8859-3', + 'l3': 'iso-8859-3', + 'latin3': 'iso-8859-3', + 'csisolatin4': 'iso-8859-4', + 'iso-8859-4': 'iso-8859-4', + 'iso-ir-110': 'iso-8859-4', + 'iso8859-4': 'iso-8859-4', + 'iso88594': 'iso-8859-4', + 'iso_8859-4': 'iso-8859-4', + 'iso_8859-4:1988': 'iso-8859-4', + 'l4': 'iso-8859-4', + 'latin4': 'iso-8859-4', + 'csisolatincyrillic': 'iso-8859-5', + 'cyrillic': 'iso-8859-5', + 'iso-8859-5': 'iso-8859-5', + 'iso-ir-144': 'iso-8859-5', + 'iso8859-5': 'iso-8859-5', + 'iso88595': 'iso-8859-5', + 'iso_8859-5': 'iso-8859-5', + 'iso_8859-5:1988': 'iso-8859-5', + 'arabic': 'iso-8859-6', + 'asmo-708': 'iso-8859-6', + 'csiso88596e': 'iso-8859-6', + 'csiso88596i': 'iso-8859-6', + 'csisolatinarabic': 'iso-8859-6', + 'ecma-114': 'iso-8859-6', + 'iso-8859-6': 'iso-8859-6', + 'iso-8859-6-e': 'iso-8859-6', + 'iso-8859-6-i': 'iso-8859-6', + 'iso-ir-127': 'iso-8859-6', + 'iso8859-6': 'iso-8859-6', + 'iso88596': 'iso-8859-6', + 'iso_8859-6': 'iso-8859-6', + 'iso_8859-6:1987': 'iso-8859-6', + 'csisolatingreek': 'iso-8859-7', + 'ecma-118': 'iso-8859-7', + 'elot_928': 'iso-8859-7', + 'greek': 'iso-8859-7', + 'greek8': 'iso-8859-7', + 'iso-8859-7': 'iso-8859-7', + 'iso-ir-126': 'iso-8859-7', + 'iso8859-7': 'iso-8859-7', + 'iso88597': 'iso-8859-7', + 'iso_8859-7': 'iso-8859-7', + 'iso_8859-7:1987': 'iso-8859-7', + 'sun_eu_greek': 'iso-8859-7', + 'csiso88598e': 'iso-8859-8', + 'csisolatinhebrew': 'iso-8859-8', + 'hebrew': 'iso-8859-8', + 'iso-8859-8': 'iso-8859-8', + 'iso-8859-8-e': 'iso-8859-8', + 'iso-ir-138': 'iso-8859-8', + 'iso8859-8': 'iso-8859-8', + 'iso88598': 'iso-8859-8', + 'iso_8859-8': 'iso-8859-8', + 'iso_8859-8:1988': 'iso-8859-8', + 'visual': 'iso-8859-8', + 'csiso88598i': 'iso-8859-8-i', + 'iso-8859-8-i': 'iso-8859-8-i', + 'logical': 'iso-8859-8-i', + 'csisolatin6': 'iso-8859-10', + 'iso-8859-10': 'iso-8859-10', + 'iso-ir-157': 'iso-8859-10', + 'iso8859-10': 'iso-8859-10', + 'iso885910': 'iso-8859-10', + 'l6': 'iso-8859-10', + 'latin6': 'iso-8859-10', + 'iso-8859-13': 'iso-8859-13', + 'iso8859-13': 'iso-8859-13', + 'iso885913': 'iso-8859-13', + 'iso-8859-14': 'iso-8859-14', + 'iso8859-14': 'iso-8859-14', + 'iso885914': 'iso-8859-14', + 'csisolatin9': 'iso-8859-15', + 'iso-8859-15': 'iso-8859-15', + 'iso8859-15': 'iso-8859-15', + 'iso885915': 'iso-8859-15', + 'iso_8859-15': 'iso-8859-15', + 'l9': 'iso-8859-15', + 'iso-8859-16': 'iso-8859-16', + 'cskoi8r': 'koi8-r', + 'koi': 'koi8-r', + 'koi8': 'koi8-r', + 'koi8-r': 'koi8-r', + 'koi8_r': 'koi8-r', + 'koi8-u': 'koi8-u', + 'csmacintosh': 'macintosh', + 'mac': 'macintosh', + 'macintosh': 'macintosh', + 'x-mac-roman': 'macintosh', + 'dos-874': 'windows-874', + 'iso-8859-11': 'windows-874', + 'iso8859-11': 'windows-874', + 'iso885911': 'windows-874', + 'tis-620': 'windows-874', + 'windows-874': 'windows-874', + 'cp1250': 'windows-1250', + 'windows-1250': 'windows-1250', + 'x-cp1250': 'windows-1250', + 'cp1251': 'windows-1251', + 'windows-1251': 'windows-1251', + 'x-cp1251': 'windows-1251', + 'ansi_x3.4-1968': 'windows-1252', + 'ascii': 'windows-1252', + 'cp1252': 'windows-1252', + 'cp819': 'windows-1252', + 'csisolatin1': 'windows-1252', + 'ibm819': 'windows-1252', + 'iso-8859-1': 'windows-1252', + 'iso-ir-100': 'windows-1252', + 'iso8859-1': 'windows-1252', + 'iso88591': 'windows-1252', + 'iso_8859-1': 'windows-1252', + 'iso_8859-1:1987': 'windows-1252', + 'l1': 'windows-1252', + 'latin1': 'windows-1252', + 'us-ascii': 'windows-1252', + 'windows-1252': 'windows-1252', + 'x-cp1252': 'windows-1252', + 'cp1253': 'windows-1253', + 'windows-1253': 'windows-1253', + 'x-cp1253': 'windows-1253', + 'cp1254': 'windows-1254', + 'csisolatin5': 'windows-1254', + 'iso-8859-9': 'windows-1254', + 'iso-ir-148': 'windows-1254', + 'iso8859-9': 'windows-1254', + 'iso88599': 'windows-1254', + 'iso_8859-9': 'windows-1254', + 'iso_8859-9:1989': 'windows-1254', + 'l5': 'windows-1254', + 'latin5': 'windows-1254', + 'windows-1254': 'windows-1254', + 'x-cp1254': 'windows-1254', + 'cp1255': 'windows-1255', + 'windows-1255': 'windows-1255', + 'x-cp1255': 'windows-1255', + 'cp1256': 'windows-1256', + 'windows-1256': 'windows-1256', + 'x-cp1256': 'windows-1256', + 'cp1257': 'windows-1257', + 'windows-1257': 'windows-1257', + 'x-cp1257': 'windows-1257', + 'cp1258': 'windows-1258', + 'windows-1258': 'windows-1258', + 'x-cp1258': 'windows-1258', + 'x-mac-cyrillic': 'x-mac-cyrillic', + 'x-mac-ukrainian': 'x-mac-cyrillic', + 'chinese': 'gbk', + 'csgb2312': 'gbk', + 'csiso58gb231280': 'gbk', + 'gb2312': 'gbk', + 'gb_2312': 'gbk', + 'gb_2312-80': 'gbk', + 'gbk': 'gbk', + 'iso-ir-58': 'gbk', + 'x-gbk': 'gbk', + 'gb18030': 'gb18030', + 'hz-gb-2312': 'hz-gb-2312', + 'big5': 'big5', + 'big5-hkscs': 'big5', + 'cn-big5': 'big5', + 'csbig5': 'big5', + 'x-x-big5': 'big5', + 'cseucpkdfmtjapanese': 'euc-jp', + 'euc-jp': 'euc-jp', + 'x-euc-jp': 'euc-jp', + 'csiso2022jp': 'iso-2022-jp', + 'iso-2022-jp': 'iso-2022-jp', + 'csshiftjis': 'shift_jis', + 'ms_kanji': 'shift_jis', + 'shift-jis': 'shift_jis', + 'shift_jis': 'shift_jis', + 'sjis': 'shift_jis', + 'windows-31j': 'shift_jis', + 'x-sjis': 'shift_jis', + 'cseuckr': 'euc-kr', + 'csksc56011987': 'euc-kr', + 'euc-kr': 'euc-kr', + 'iso-ir-149': 'euc-kr', + 'korean': 'euc-kr', + 'ks_c_5601-1987': 'euc-kr', + 'ks_c_5601-1989': 'euc-kr', + 'ksc5601': 'euc-kr', + 'ksc_5601': 'euc-kr', + 'windows-949': 'euc-kr', + 'csiso2022kr': 'iso-2022-kr', + 'iso-2022-kr': 'iso-2022-kr', + 'utf-16be': 'utf-16be', + 'utf-16': 'utf-16le', + 'utf-16le': 'utf-16le', + 'x-user-defined': 'x-user-defined', +} diff --git a/venv/Lib/site-packages/pip/_vendor/webencodings/mklabels.py b/venv/Lib/site-packages/pip/_vendor/webencodings/mklabels.py new file mode 100644 index 0000000..295dc92 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/webencodings/mklabels.py @@ -0,0 +1,59 @@ +""" + + webencodings.mklabels + ~~~~~~~~~~~~~~~~~~~~~ + + Regenarate the webencodings.labels module. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +import json +try: + from urllib import urlopen +except ImportError: + from urllib.request import urlopen + + +def assert_lower(string): + assert string == string.lower() + return string + + +def generate(url): + parts = ['''\ +""" + + webencodings.labels + ~~~~~~~~~~~~~~~~~~~ + + Map encoding labels to their name. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +# XXX Do not edit! +# This file is automatically generated by mklabels.py + +LABELS = { +'''] + labels = [ + (repr(assert_lower(label)).lstrip('u'), + repr(encoding['name']).lstrip('u')) + for category in json.loads(urlopen(url).read().decode('ascii')) + for encoding in category['encodings'] + for label in encoding['labels']] + max_len = max(len(label) for label, name in labels) + parts.extend( + ' %s:%s %s,\n' % (label, ' ' * (max_len - len(label)), name) + for label, name in labels) + parts.append('}') + return ''.join(parts) + + +if __name__ == '__main__': + print(generate('http://encoding.spec.whatwg.org/encodings.json')) diff --git a/venv/Lib/site-packages/pip/_vendor/webencodings/tests.py b/venv/Lib/site-packages/pip/_vendor/webencodings/tests.py new file mode 100644 index 0000000..e12c10d --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/webencodings/tests.py @@ -0,0 +1,153 @@ +# coding: utf-8 +""" + + webencodings.tests + ~~~~~~~~~~~~~~~~~~ + + A basic test suite for Encoding. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +from __future__ import unicode_literals + +from . import (lookup, LABELS, decode, encode, iter_decode, iter_encode, + IncrementalDecoder, IncrementalEncoder, UTF8) + + +def assert_raises(exception, function, *args, **kwargs): + try: + function(*args, **kwargs) + except exception: + return + else: # pragma: no cover + raise AssertionError('Did not raise %s.' % exception) + + +def test_labels(): + assert lookup('utf-8').name == 'utf-8' + assert lookup('Utf-8').name == 'utf-8' + assert lookup('UTF-8').name == 'utf-8' + assert lookup('utf8').name == 'utf-8' + assert lookup('utf8').name == 'utf-8' + assert lookup('utf8 ').name == 'utf-8' + assert lookup(' \r\nutf8\t').name == 'utf-8' + assert lookup('u8') is None # Python label. + assert lookup('utf-8 ') is None # Non-ASCII white space. + + assert lookup('US-ASCII').name == 'windows-1252' + assert lookup('iso-8859-1').name == 'windows-1252' + assert lookup('latin1').name == 'windows-1252' + assert lookup('LATIN1').name == 'windows-1252' + assert lookup('latin-1') is None + assert lookup('LATİN1') is None # ASCII-only case insensitivity. + + +def test_all_labels(): + for label in LABELS: + assert decode(b'', label) == ('', lookup(label)) + assert encode('', label) == b'' + for repeat in [0, 1, 12]: + output, _ = iter_decode([b''] * repeat, label) + assert list(output) == [] + assert list(iter_encode([''] * repeat, label)) == [] + decoder = IncrementalDecoder(label) + assert decoder.decode(b'') == '' + assert decoder.decode(b'', final=True) == '' + encoder = IncrementalEncoder(label) + assert encoder.encode('') == b'' + assert encoder.encode('', final=True) == b'' + # All encoding names are valid labels too: + for name in set(LABELS.values()): + assert lookup(name).name == name + + +def test_invalid_label(): + assert_raises(LookupError, decode, b'\xEF\xBB\xBF\xc3\xa9', 'invalid') + assert_raises(LookupError, encode, 'é', 'invalid') + assert_raises(LookupError, iter_decode, [], 'invalid') + assert_raises(LookupError, iter_encode, [], 'invalid') + assert_raises(LookupError, IncrementalDecoder, 'invalid') + assert_raises(LookupError, IncrementalEncoder, 'invalid') + + +def test_decode(): + assert decode(b'\x80', 'latin1') == ('€', lookup('latin1')) + assert decode(b'\x80', lookup('latin1')) == ('€', lookup('latin1')) + assert decode(b'\xc3\xa9', 'utf8') == ('é', lookup('utf8')) + assert decode(b'\xc3\xa9', UTF8) == ('é', lookup('utf8')) + assert decode(b'\xc3\xa9', 'ascii') == ('é', lookup('ascii')) + assert decode(b'\xEF\xBB\xBF\xc3\xa9', 'ascii') == ('é', lookup('utf8')) # UTF-8 with BOM + + assert decode(b'\xFE\xFF\x00\xe9', 'ascii') == ('é', lookup('utf-16be')) # UTF-16-BE with BOM + assert decode(b'\xFF\xFE\xe9\x00', 'ascii') == ('é', lookup('utf-16le')) # UTF-16-LE with BOM + assert decode(b'\xFE\xFF\xe9\x00', 'ascii') == ('\ue900', lookup('utf-16be')) + assert decode(b'\xFF\xFE\x00\xe9', 'ascii') == ('\ue900', lookup('utf-16le')) + + assert decode(b'\x00\xe9', 'UTF-16BE') == ('é', lookup('utf-16be')) + assert decode(b'\xe9\x00', 'UTF-16LE') == ('é', lookup('utf-16le')) + assert decode(b'\xe9\x00', 'UTF-16') == ('é', lookup('utf-16le')) + + assert decode(b'\xe9\x00', 'UTF-16BE') == ('\ue900', lookup('utf-16be')) + assert decode(b'\x00\xe9', 'UTF-16LE') == ('\ue900', lookup('utf-16le')) + assert decode(b'\x00\xe9', 'UTF-16') == ('\ue900', lookup('utf-16le')) + + +def test_encode(): + assert encode('é', 'latin1') == b'\xe9' + assert encode('é', 'utf8') == b'\xc3\xa9' + assert encode('é', 'utf8') == b'\xc3\xa9' + assert encode('é', 'utf-16') == b'\xe9\x00' + assert encode('é', 'utf-16le') == b'\xe9\x00' + assert encode('é', 'utf-16be') == b'\x00\xe9' + + +def test_iter_decode(): + def iter_decode_to_string(input, fallback_encoding): + output, _encoding = iter_decode(input, fallback_encoding) + return ''.join(output) + assert iter_decode_to_string([], 'latin1') == '' + assert iter_decode_to_string([b''], 'latin1') == '' + assert iter_decode_to_string([b'\xe9'], 'latin1') == 'é' + assert iter_decode_to_string([b'hello'], 'latin1') == 'hello' + assert iter_decode_to_string([b'he', b'llo'], 'latin1') == 'hello' + assert iter_decode_to_string([b'hell', b'o'], 'latin1') == 'hello' + assert iter_decode_to_string([b'\xc3\xa9'], 'latin1') == 'é' + assert iter_decode_to_string([b'\xEF\xBB\xBF\xc3\xa9'], 'latin1') == 'é' + assert iter_decode_to_string([ + b'\xEF\xBB\xBF', b'\xc3', b'\xa9'], 'latin1') == 'é' + assert iter_decode_to_string([ + b'\xEF\xBB\xBF', b'a', b'\xc3'], 'latin1') == 'a\uFFFD' + assert iter_decode_to_string([ + b'', b'\xEF', b'', b'', b'\xBB\xBF\xc3', b'\xa9'], 'latin1') == 'é' + assert iter_decode_to_string([b'\xEF\xBB\xBF'], 'latin1') == '' + assert iter_decode_to_string([b'\xEF\xBB'], 'latin1') == 'ï»' + assert iter_decode_to_string([b'\xFE\xFF\x00\xe9'], 'latin1') == 'é' + assert iter_decode_to_string([b'\xFF\xFE\xe9\x00'], 'latin1') == 'é' + assert iter_decode_to_string([ + b'', b'\xFF', b'', b'', b'\xFE\xe9', b'\x00'], 'latin1') == 'é' + assert iter_decode_to_string([ + b'', b'h\xe9', b'llo'], 'x-user-defined') == 'h\uF7E9llo' + + +def test_iter_encode(): + assert b''.join(iter_encode([], 'latin1')) == b'' + assert b''.join(iter_encode([''], 'latin1')) == b'' + assert b''.join(iter_encode(['é'], 'latin1')) == b'\xe9' + assert b''.join(iter_encode(['', 'é', '', ''], 'latin1')) == b'\xe9' + assert b''.join(iter_encode(['', 'é', '', ''], 'utf-16')) == b'\xe9\x00' + assert b''.join(iter_encode(['', 'é', '', ''], 'utf-16le')) == b'\xe9\x00' + assert b''.join(iter_encode(['', 'é', '', ''], 'utf-16be')) == b'\x00\xe9' + assert b''.join(iter_encode([ + '', 'h\uF7E9', '', 'llo'], 'x-user-defined')) == b'h\xe9llo' + + +def test_x_user_defined(): + encoded = b'2,\x0c\x0b\x1aO\xd9#\xcb\x0f\xc9\xbbt\xcf\xa8\xca' + decoded = '2,\x0c\x0b\x1aO\uf7d9#\uf7cb\x0f\uf7c9\uf7bbt\uf7cf\uf7a8\uf7ca' + encoded = b'aa' + decoded = 'aa' + assert decode(encoded, 'x-user-defined') == (decoded, lookup('x-user-defined')) + assert encode(decoded, 'x-user-defined') == encoded diff --git a/venv/Lib/site-packages/pip/_vendor/webencodings/x_user_defined.py b/venv/Lib/site-packages/pip/_vendor/webencodings/x_user_defined.py new file mode 100644 index 0000000..d16e326 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/webencodings/x_user_defined.py @@ -0,0 +1,325 @@ +# coding: utf-8 +""" + + webencodings.x_user_defined + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + An implementation of the x-user-defined encoding. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +from __future__ import unicode_literals + +import codecs + + +### Codec APIs + +class Codec(codecs.Codec): + + def encode(self, input, errors='strict'): + return codecs.charmap_encode(input, errors, encoding_table) + + def decode(self, input, errors='strict'): + return codecs.charmap_decode(input, errors, decoding_table) + + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input, self.errors, encoding_table)[0] + + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input, self.errors, decoding_table)[0] + + +class StreamWriter(Codec, codecs.StreamWriter): + pass + + +class StreamReader(Codec, codecs.StreamReader): + pass + + +### encodings module API + +codec_info = codecs.CodecInfo( + name='x-user-defined', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, +) + + +### Decoding Table + +# Python 3: +# for c in range(256): print(' %r' % chr(c if c < 128 else c + 0xF700)) +decoding_table = ( + '\x00' + '\x01' + '\x02' + '\x03' + '\x04' + '\x05' + '\x06' + '\x07' + '\x08' + '\t' + '\n' + '\x0b' + '\x0c' + '\r' + '\x0e' + '\x0f' + '\x10' + '\x11' + '\x12' + '\x13' + '\x14' + '\x15' + '\x16' + '\x17' + '\x18' + '\x19' + '\x1a' + '\x1b' + '\x1c' + '\x1d' + '\x1e' + '\x1f' + ' ' + '!' + '"' + '#' + '$' + '%' + '&' + "'" + '(' + ')' + '*' + '+' + ',' + '-' + '.' + '/' + '0' + '1' + '2' + '3' + '4' + '5' + '6' + '7' + '8' + '9' + ':' + ';' + '<' + '=' + '>' + '?' + '@' + 'A' + 'B' + 'C' + 'D' + 'E' + 'F' + 'G' + 'H' + 'I' + 'J' + 'K' + 'L' + 'M' + 'N' + 'O' + 'P' + 'Q' + 'R' + 'S' + 'T' + 'U' + 'V' + 'W' + 'X' + 'Y' + 'Z' + '[' + '\\' + ']' + '^' + '_' + '`' + 'a' + 'b' + 'c' + 'd' + 'e' + 'f' + 'g' + 'h' + 'i' + 'j' + 'k' + 'l' + 'm' + 'n' + 'o' + 'p' + 'q' + 'r' + 's' + 't' + 'u' + 'v' + 'w' + 'x' + 'y' + 'z' + '{' + '|' + '}' + '~' + '\x7f' + '\uf780' + '\uf781' + '\uf782' + '\uf783' + '\uf784' + '\uf785' + '\uf786' + '\uf787' + '\uf788' + '\uf789' + '\uf78a' + '\uf78b' + '\uf78c' + '\uf78d' + '\uf78e' + '\uf78f' + '\uf790' + '\uf791' + '\uf792' + '\uf793' + '\uf794' + '\uf795' + '\uf796' + '\uf797' + '\uf798' + '\uf799' + '\uf79a' + '\uf79b' + '\uf79c' + '\uf79d' + '\uf79e' + '\uf79f' + '\uf7a0' + '\uf7a1' + '\uf7a2' + '\uf7a3' + '\uf7a4' + '\uf7a5' + '\uf7a6' + '\uf7a7' + '\uf7a8' + '\uf7a9' + '\uf7aa' + '\uf7ab' + '\uf7ac' + '\uf7ad' + '\uf7ae' + '\uf7af' + '\uf7b0' + '\uf7b1' + '\uf7b2' + '\uf7b3' + '\uf7b4' + '\uf7b5' + '\uf7b6' + '\uf7b7' + '\uf7b8' + '\uf7b9' + '\uf7ba' + '\uf7bb' + '\uf7bc' + '\uf7bd' + '\uf7be' + '\uf7bf' + '\uf7c0' + '\uf7c1' + '\uf7c2' + '\uf7c3' + '\uf7c4' + '\uf7c5' + '\uf7c6' + '\uf7c7' + '\uf7c8' + '\uf7c9' + '\uf7ca' + '\uf7cb' + '\uf7cc' + '\uf7cd' + '\uf7ce' + '\uf7cf' + '\uf7d0' + '\uf7d1' + '\uf7d2' + '\uf7d3' + '\uf7d4' + '\uf7d5' + '\uf7d6' + '\uf7d7' + '\uf7d8' + '\uf7d9' + '\uf7da' + '\uf7db' + '\uf7dc' + '\uf7dd' + '\uf7de' + '\uf7df' + '\uf7e0' + '\uf7e1' + '\uf7e2' + '\uf7e3' + '\uf7e4' + '\uf7e5' + '\uf7e6' + '\uf7e7' + '\uf7e8' + '\uf7e9' + '\uf7ea' + '\uf7eb' + '\uf7ec' + '\uf7ed' + '\uf7ee' + '\uf7ef' + '\uf7f0' + '\uf7f1' + '\uf7f2' + '\uf7f3' + '\uf7f4' + '\uf7f5' + '\uf7f6' + '\uf7f7' + '\uf7f8' + '\uf7f9' + '\uf7fa' + '\uf7fb' + '\uf7fc' + '\uf7fd' + '\uf7fe' + '\uf7ff' +) + +### Encoding table +encoding_table = codecs.charmap_build(decoding_table) diff --git a/venv/Lib/site-packages/setuptools-28.8.0-py3.6.egg b/venv/Lib/site-packages/setuptools-28.8.0-py3.6.egg new file mode 100644 index 0000000000000000000000000000000000000000..9aea9806a272a5706c0e7cc95e73717be7171643 GIT binary patch literal 465038 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%0AaCsRM0n%G4C>o~+y6iY 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%0AO&)VlzAPpN%N5C)cJU_$M$`Q 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%0D*uozkf=-|IVK%V;d(2cU?PMa~t4(;v<;UqTPi% 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools.pth b/venv/Lib/site-packages/setuptools.pth new file mode 100644 index 0000000..5b5808a --- /dev/null +++ b/venv/Lib/site-packages/setuptools.pth @@ -0,0 +1 @@ +./setuptools-28.8.0-py3.6.egg diff --git a/venv/Lib/site-packages/sqlalchemy/__init__.py b/venv/Lib/site-packages/sqlalchemy/__init__.py new file mode 100644 index 0000000..21143f5 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/__init__.py @@ -0,0 +1,148 @@ +# sqlalchemy/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + + +from .sql import ( + alias, + all_, + and_, + any_, + asc, + between, + bindparam, + case, + cast, + collate, + column, + delete, + desc, + distinct, + except_, + except_all, + exists, + extract, + false, + func, + funcfilter, + insert, + intersect, + intersect_all, + join, + lateral, + literal, + literal_column, + modifier, + not_, + null, + nullsfirst, + nullslast, + or_, + outerjoin, + outparam, + over, + select, + subquery, + table, + tablesample, + text, + true, + tuple_, + type_coerce, + union, + union_all, + update, + within_group, + ) + +from .types import ( + ARRAY, + BIGINT, + BINARY, + BLOB, + BOOLEAN, + BigInteger, + Binary, + Boolean, + CHAR, + CLOB, + DATE, + DATETIME, + DECIMAL, + Date, + DateTime, + Enum, + FLOAT, + Float, + INT, + INTEGER, + Integer, + Interval, + JSON, + LargeBinary, + NCHAR, + NVARCHAR, + NUMERIC, + Numeric, + PickleType, + REAL, + SMALLINT, + SmallInteger, + String, + TEXT, + TIME, + TIMESTAMP, + Text, + Time, + TypeDecorator, + Unicode, + UnicodeText, + VARBINARY, + VARCHAR, + ) + + +from .schema import ( + CheckConstraint, + Column, + ColumnDefault, + Constraint, + DefaultClause, + FetchedValue, + ForeignKey, + ForeignKeyConstraint, + Index, + MetaData, + PassiveDefault, + PrimaryKeyConstraint, + Sequence, + Table, + ThreadLocalMetaData, + UniqueConstraint, + DDL, + BLANK_SCHEMA +) + + +from .inspection import inspect +from .engine import create_engine, engine_from_config + +__version__ = '1.2.15' + + +def __go(lcls): + global __all__ + + from . import events + from . import util as _sa_util + + import inspect as _inspect + + __all__ = sorted(name for name, obj in lcls.items() + if not (name.startswith('_') or _inspect.ismodule(obj))) + + _sa_util.dependencies.resolve_all("sqlalchemy") +__go(locals()) diff --git a/venv/Lib/site-packages/sqlalchemy/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95d8f1b43d04364a1bb45a4d173139b10d2ea901 GIT binary patch literal 2915 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/__pycache__/events.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/__pycache__/events.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91c62f613de6f1e3ca582b542633029854363501 GIT binary patch literal 54321 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/__pycache__/exc.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/__pycache__/exc.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f341b6fe63724cb199f0f83979530694198de930 GIT binary patch literal 17214 zcmcgzU2GdycIFI66h%w2V#j~^amLP%Eyt#vI6sc!WGzch;>}v?wd~EV)&Vu*jHHoC z4t-~6Sp*%R@D}@&0*ghr=z9^MK%WXM(7v>2QS_mGXn{VoU<<TG(E{6t_Mva-L;HQ_ z&J2eXWiNM&Qs8LL%$+;uoO|v)-#O><;?z{>;m@vDRxjmp|B}mm#&CWG-*C&!<=os# z&NW=KX4K6U)5v|CbMvnC!<=h*`N#Pc3-p*<0A27b&|{#--6H6sHwL-@dcrM%E(tvj zdY?N9dQ#{j=qYy^^t8|up!d5opl77N66j~#1E3EGy$|$3_gT=-di&7-B<Mr#VbF)= z{uJmV?orT3g`Ng|%zX~@bKW%C?+5+7`vT|}<o*ok7v1Baj|=?_=$Bj@wCz2E_6I<p za8H6hDfB_mFT1aRe#LthSFDvoxbmv|8m_!1Ee?Y|<(>w8+B=LMj)0zZ&wxH7^ij~S zyJtb4mG;L#pL5Hg%R)Z~`n-Dq^aY`x2mOZoCg?Z47tr5}?!}ekxb~L&Hm<!bT)zbR z9rs<(?|LtxjSc#e`yS}`JR9^0&~xtlpx+nzB<RcT2cSRjPU8N{pg(lK0s0$4zXJM- zI}dtZ=vP5sbr(P{2>lx9MfV!$YeJs_{gL}I=#Pay4f+%JI_T@(Ec!nK`kU^jpg;9q z2YnXwx7-_`Z+PcGm)(oYx!I-mA76V|^_r0%G(x)-dak|gN1OKYcfK`Wt8RMropPyk zd(*SmoY1qqq^Vu4Ibmq~p*@FdbF1ZE`)f)CYPD?JnSQr7{pzOeD6ga(pYL!&+p(1w zwrY_btlNuM=Wkx`^^Tj$vz=Pa4qI#TCND&@Qv5Dy9lxej`niE=Gi!7U)u7&VqAIBL zlaJ5BMVxrK6$3NNbd8m~Ypz&7%B>qRuZk;UdOocb^gLP_*YjtksOQbf#E)`TZlwf$ zO?CGVh}wSkW3S;UziNAwTx+JhR3%&dSoo5U{rH9#aOjv@oZY#{=A(Qk@8<4JsOLKQ zPOf4^W`zHb%`K~Abj;r{46Forr@7R%s)6gxj&<|RpsCnncPwl*@j~|8EmkUi!;dPJ zS*vS>UTwXbchpALytgfFp`3|IMf6yy{BG{+&ll$I-U&Sw-rYGBp4z#)=!N&9pn3Od z5ZpU=!)Z7hUfpX%=azTE$g7{LJNT#WKJXe3?$-RZyP+R>=bBFS9@>R>!}~Qy8_Hc+ zLb<u4#=+82e8VCRIb+f&8U>?$bU<z8gy&RIR2UY&+ycJgb2xNzTe-008p}D?<oUXB z80rv}<d?d+_B*Gqf{P6mv>L7*H0)>-_NWKJiRSFhC~D4~Kc6sIu6yUb^RI@pvqr2H zMF!PmdYHl)G(}Zc!blwl8Ou;Gth{NokL{8nY1=0R$3%oE<0PUO#$&@Z?wRU%WNu-E zx3IP!%RPo*3C;6ZFhxE59N!#9Ghg!>T?>O5DypVC5qS@zO0?7TAjL5Di<P<;h8USs zXdV{WO@7*#F^A|sZkV#p7o@D81VNoY&OI{7usZG<Fv{GP`8c<39_CfB4AX3ny&Brk zYF<5yM?|xVrao&5f8Fu;HG_H{uTaO8%6YVnwOlj{=1jhQWS5q8%al<nh+ok$m7o~~ zJH`^!au`?k(9XU}rPc6d&?@htSxA|4M*I2QuT0y`?&JJwICZa5>6R*$df>KdJfEyo z?zfy;d<XKlLA6rR8sHodHE6AnC3OUc?o>Pxt6r^!#;Ngv;eBjG8<a_X5GUa@4n@N< zEpxI^oG2EHCAn9{<@7^MT){V-!J&VRgGTb*sd=^0;(V4Bv;C7>dIjFz^cuF)u>HmZ zr{-h+VC4!L>ova``Hc;Gz16@8CKYT?p5|JQC8t^QJr^#)#^-i#zHM*`#DcwpP88Pl zX3%g$I||q*hPkEtiu}4~hmjM(lW?sJ<BpbQEyWc>9piyhUp>!5svUhMx_J>dKEPLo zt~jPh_vwQs%ui_JkkyB*_okcwVUcS;*7Gsw$Lb#a_S%+Ljh2Gw+C#6}ik!8Yr&V=- zKvjV>q#~Mde(++Duk6V~-sxE^KTc#<oyMuiUh_mj@jY>Ou&`L#a@l@>#TUwTT-S5w z=Il4hZ)lBH#U8;o|5FftL#igqMVNhX4RBu_tb>nx&Y}GRc;`^+*O7l)vpQ$LsxUFR z3dm|EOLH<Y3lWCaixR^VU5kckyEph=`k9<jGTJBhQc=2y&lZLbpiF}o&`WP9llRH1 zxWrz=S6)>tSXd!J^$q9K%=?+8V7XP@ydkqvk6W{E+-!FTQ`E20I?vSt2P(Jq9JWul zLQG&7tz)|m2A&Yd^_?2bVO9nZ(8R%DtH`gmYL2ow*Ez%uD16PcYe5yZ?3PEdUmF50 zQ)CWt(Nv^I68OMU@KK;Vf1~lKw-aiK?_?!z1opaaVBbRv@|$56r_r{DxgTu@Hn0L@ zrNx9IuiH|b{Eod9_>GaoIsq}hhi@Vl>5;@bHeX-!H(Eg}+$G&+R=PSw<5X+F<9k^_ z?K;5UCfV;7SOnWcX#1BJ_#_1UJR>maK7sSNaD@$cC%X;-V=|gER;(Cxj=3gQqGI>R zg0IBR7QLp|aJ@!#M~2t=4>OmGb+ERBRi_!$f(=@<9R?~2ZM~M;)%bDSO`qpt<$4hY zIkRee1lQCeEuiW(9OVbniD)*(P(iiYQsJClna<eP;S20#fD3ws^;#?3v;!behhE~W z?XOFBdfn0Zz21uS>`K^)WGHMxHP1mj5Ja1vI*Y;d;5IOz@vQP2z^34rF3E*~^7Mi4 z*w>equid(ht8q7r*S>Y__BHKK=-=?x8f@ZyE`@h)F3#iLwvQMB6Sd)aoc36+n?V?z z!|EMWeN5eiA!Rp}ch2?F8)96T{ot+7#<V+g{04b)YAp{gjINN*(6d)p*?pze#A@QL zu0l)B21E#}%3s3+a1V3JTc7J|C6dEq2DJwXJAz}YJJp(}JCAig=R}c0sCC1OoJA~G zWe&dQR`mQ`&5i~~c1{34+V|$uYERxFcNfG}L$(NgiK`3kbPU0_r>H=VT+$844}so1 zPyx(R6o3e_;?xi?d~jQ}QXSwq&Q=WQ8V3Dr{Cwznu|m?PRue=}73hkY!~aZ<EZ`e{ z6Niq4kg2F%>i|-_45cJB(lHm|6CcAfz6)&HusYTwBQmxSHvqLFy!$e5An-zvg^&wJ z^noc?0D84g1H87)vvvwEG1;`=<r|6II=RoM(HnYtY<w5U^S&uh6Kr-Z<u%o;*)_`- zx<=S@a{}bPp14e4^suQuY_T);Y~|V)_n7CYH!WYLXQ_oBmec3qHtCKL0$N;~1qO;c zN8C$G3A<ySi+Bu<Si*usXsl*=ID<nsUk^7lGVNii)Y*q<5%PKtq1tiNGG-XC*-zs# zRTy0~he?Kr-kx~e<xv6#{Lj0_vD~_~&UMaQQbekNd;)k=9@PO}9Uj#Hb}P?qRROy@ zXw^d|C--JDc>ph5dJ;T2iw-m6*26(B<Hj1_M%jupmQk8eyjI0G{X7kxOfDcR;U|dl z&8#QGGUZ9eKrgh3F9J{mYUV?-w2p!;&Vj98!&f_DLy_*|HXrl-i(=#F?=*<{Wu4K2 z2u~VxxC6qZ-hfVrYkB&pO|OOJVZ%ofAr=lRM@hSu-FWyy`BM2}jEEDK`9Ddegfy~# zMBFhMm1a@#>#^n)Krk0sz;Z>es%-ca`U@tW7)k5sm4mDpuxpT<*U@WHkA$aICgZ1) z;9HEhXDmdzU*fAnZh&T_3toAG_R_ijB-4BXawez0&i7F#f}DA!Z?K<cn)|N<VnqJB z?<qZ?@AnNTpaIf<j>fzg(KT-eL7j|a-FawNHyz~wRw%pX*ZpV|Gi4I&C*+;$VbM^e zhjY%9LLYQO+Rn{O*mhvi58;g)fHnXi8YlfWM4ktNPi|z<4^f9>D`y+Y#&gUz_8yLX zP9~g%xGGLmLS(cI5d*u3l_76KA_iErlj#DuBB;R?UY7_^{2O3L+(&Hvb9FD;gg?HC zrq?hLqOc4NSMLQaI9!hx0Oz1zzywZ~O+X3hR(b=XuObNw3A{+{ye}<!+1Lc<RGrqw zCLdRDt&Fyjq7|`&ZHynI0_lw1lVO>V$oe(`nOw~+61p*N_##Q1AqC9L-gTWQxaHjk z>e0yrjVF}1;V75DoBj6G3cQ2#VKa)=`_a<u*svh6yFX4r05es{)zHQG4Oqf&;j8U^ z!ZH-EjBGUrV=3@183wMu&OgI!vZg;EhG;6>3|ci>fMiSXxK2+;2o^IUCkc)DG;z&E zCedhlx5=(TXBcn4;R4P_7_MaaWt=jWjHGKz)?OQ70TP}tL>MG&dXQNatRbG;vFo0L zgg)*{Pn_sL_gPV3pFL^ZvrNgeuQU+e#Dz5miU6lb6=VYlh6rgTAhA7!WNwW-p%=A{ zEWdOYGGg9#Fk5tl`6OL{hgvPz)|NyIjtxi>Ny>{YOXShUO#5Z)Rx`G--LB+~@|)U` z+}^|d)SIxQp(b|`H-0BIxha?&uM9W2J#uf8w=oAXzxDchC2M56R*!hTqBVyVnHkB$ zWt)u@kiP|9e@5=(MV9nPyeCs7JK@_8dWlkuD%PX$9)=w7-bE-GaDn4^_(>kj4R)`s zd!`B%tor{TLHITV{lir7F-3YL!4EA3*BfC|JC^we4uBXEByC3~HB*I|^xquwq;sp8 zr4rF={Af8oT}5z->{Qir#dMLLfh9AK&&U#UFl|4I4}Awx{YfgNsYs6`<-uEsIa_cl z3$*~bOl_gnd8Ad6pk4-8>E#}Sysm<}oi#^<Ovn!eh~OcH%zBYehmM&1PIM}EKUH~! zj?_mo;&&m{pJ${bJ(84hc5or6wdxuqwBMNrI7LwAqpAg$Ta{Zx3cEyIUA+}--&DcF zoz>Nm*uDfd|1xFUP^3p<duA!P#ef@YeVmJ4jMoleiPhCiL=OiNr0xU5BXK$(Bjk+} zDsgsUv^*utBY%~Vfpnh?Tvx8JfsI2yNJEZ22cU-h7XD+W0sz~1_i#og_co`Lwz==@ zDVe0T=#xY!`n%Otc@U|UR64P_BoR9#X2^rP1+vLVK9Z0WaXdCvNH!pi<nxLG>WI1| z85Jq{AbXyM)7`O%`QMDMNF(UK;|qwyS?+AVG+fdY^7jSKef%+SX4uSd8UGQ}^o7iM z9WpaeR`265JX%(aX#W9US^81kI<Y4|NsRo<1;}%F$KUWh5+GZaQM44V{YqwXB+G1e z-rgF?PV|#cGZYyzaLoVdK<MC-xr?l!NZKf$Bz~-(xyVR#_vv*+8;#X78g&_6cgL!G zS<b)Wb{sQKO4el$@rN4^`4;0&%H>((!Fhf?-)BH^&LuS<^&vWVf))Kcxzlz%W3*51 z&D*bLNUZ1m6JM1Y(wO44K0|t1IEyutg`mvJ@~)f%ngCaGDU>yz&I*e?=?8x3vsi`| z)X9MOk(T-^VDmr8dJMBjkHq$oI}I#vNf{=?{dYC@!ZeULV4+T8Fs{@OFfmRDLEwdJ zP8c+vj)O-58S@b4zf$4G6zP$KJ9MY9#@!jZvYN!{?Rn<71U~8r5pr<T5utCU5|@c2 zF+y=ImDmzujv~cXaQ)w@6ow)_k`z;mz-yl=r+LGF=r^2880vYu&hvO1lx%N08<$~p ziCh~M^4bbzg>|VHTuVbHiLiART%xDgPjBNDP3QuU(07q7N92ewi<_%hGEYnRt5R%% zTl6G*=<C4BLlTAtK}CWwx=p}N<P5lTD%Kvcxzj6{#Kv;1yiuNu%RH=P2++$QTJT`| zbtVDrK1FC}%O^x0`^L^fD~y7AD(C6+>}>y&vvcCL`6xfErtj`-888~P*xSg^o#tT? z0U)YPff{%ktkvuf?B$51%U(C^7@AL%P+3EaT|p_=sh{Q!sk6F#b2hE__OryNQPPqU zkNv?1_Nh2jKDE0Un<^%YhA7}<{Md<;vzoI}>~;Zm`ah}N9#o{4`V2RZD_7Wp%XmKo z1N<@py-aLr+ZUG`$K!HiG2Y}h5pQxUN%?WIJKb+)&I7EthzGQlNsgKjEUuX9>Bv$b ztP6EPOlaBIK`V{zDhc)byIr+u<~r8^%hEd!<4l=Y#o!B&6+m)o-E*QAfUKQV!P2+s zBox~uw`7#VRi+P6cxy4OqBkJQ{uORf(cxE`xRUpB#xN8^LbNJ#MlK{XFYaN}K^YI5 zX2P(A;+#Bp8_y*(w<k~>1;NueNd$>`oxUhxHBkOWhS9jorQy{t;<^OEqNZh}H7!$v zla9s8)&`JcWlVsI$?Z^d-$*jkRy4ewNLAM@XjOGB^3g4zQjBP~uY|<i8R{b*KIVa) z4FkYs*h*N!&72gE3WPF8`Uk#;V6cA5hyVFqz*lxJ0kIIEqs$iXd_;-ORG;8>x4@;b z;l`U+)a$r5z(==)?KBlF!rNp=Uc*g$$rwt!QSP<s!{KR-kvmYx$RLygZ4H#L#xi)k zUr|rMRF}wGim3*<>lM0E?#LNOPt^1UPFVM><?6;Gs0mzQN8X0<h&v?nY6+W5ks;@~ zu7==j3|Eb<0?%AaHl4D~Sf|%w9J~BbF;y;~?pUJ!!dARa;JFFbRy*bq91+S*#05MQ zKU<MsYft<Dv!7dyKL0r_pgr>gu`1N<v3r1*zc%`ds}z12-;n8jn(OD2kFXz>+nRoC z-nZ}yqiwM)D<g~{e--VpXBU}oDY&Fnm#<5gYY5|6qxtZ9GUSC^`^><Q$6F0EW!3bt z+0x2%#@FFgeTui~ZF@4N*GX~)tW2T^*;LwzgnD=1Y2KuPBDXDpA|LLKYby*__UZX0 z&#j=Aq&JohNFf^*FOz+uctnt93Dbu)*lGM>`yZ-F7S7s-hl|BskNbvY)>Xev%IsI) z#4991l-FihNwumL(c~!kVOF!a58Ds({40$Y^N8?xw~BB2;c#BTH)M)&&<lBE@IU;q z0w#54d-bIF9s?V!Iowm-Qg2VUGl34{STD^047i&#N!?3Utz+d%((lA-nG|(e%!vyz zaRCUp)Kj$`f+8&S+V>>8z|wwE(^*s~TdH%3Zy*-Z1w_fmXI(|ZMIl5wieJ(52P`zg z7a#|p_zEzS?pVS$_j9@hylF4Rda6HX-qR7BD7SB7w~0hBWKxP+NVu)8$})$@mmT62 z^tFL7l{n;VKY)t8_aT!9P4trY3|ckQz@tdcVow5IwG_k~XOdb4gp!EL-e>_|!M8H? zkE(|2uS*UKxKWqf1~o_UE1YTkBo3GLf_}A0jN>$R;_|V^Bk<v_&&uBb*vQH@h#9^Z z0Mg|!@eI2##IB~lAg0jL^cBPsyU7}gM)N~3#aR(HF-MG`?DDE|V`6VE2<&J0Bx>t} z{0X$dk0pppB`DCSe;7i$Jpyz|=SU1)c4=r3%Aghs%cBhvn;{lKUODo$JqdF|jUt6$ z@wfuRt~pKrqopb<?EKKj@Lb&C*Eqq(oP30oo;oaN=3{dbNl7`+<LWCN6pZ8FS^S$^ zg%eck9Yd{008yR%R$*&gjzt_NxHEqpu93gM0IbSymE8Q7*eT2>;TqAm`YE3M>F#HH zPd&z>7VU#m9e?<V^gOwk`~32wVyC!Od^FK1s6Xu#wx+hGI}@D=IAk0<<M#^ct7v~` zyfbku7tMgfuk`t&+|AtmuRep9j5!*ELymcxwU*i?ILb17C3Z3BeKj%Sd!}1LdIOuU z9VCOhQ&(X^*Alqhok&k5s?gw&3%dq1$9Q&>2gww1f$xrQ)KFQhg<WiPudAR^S$RoD zrjkz9N`%QD>Rl@ptUKL2idf_#`4Nx0iH>GV-F&qc4)~^(Y26vO;8rlhyN7$xKYrVi z6kT1oR=q!W#Uei|1weXFRl3%Y{93n|h`&x}#w2`hyo3ZT+^Jc_uOdwRP2z9HoQcm1 zMk!CXi{E?TTQm;yw<~bR?Ss8_g=HO4Y6{TMdvA+cHWP=D5?F@+33P<7j#1-)bN)Vc zGj#Wpc<Uci1P+8`m=9&x$sk6i&-ft{VY*wUNCPK3lQiGQ#-qY#xklN_MaI37nloUa zMy`Fp<=(dO$HtKy?|&a#w_M!$V*Go#hWWktCo1>_PJYYU0&2vu&?)qc9g#@>%L0%n zqmOZkJ7AV&@j?iB5Ed$Ij)z~<^ZlHz|1njOTF#uEHRB{^DR%RCRuL%*=+O9OAIYA9 zirR`AdkxX31T0;P)wZrNG<E-*R4i>hXVMyGjN=%4TE+o6mdw7fPkyvCYhpVCzY{}& zm&}-bk``f2G;q~jvdsWaOVPuzJ=n$Baf93l>^cP4Wtw>m_o<?lGi2`c!7f_e6?_Fn z55lf^lg#vWZWLH|k}A@1jf|YcA@|=duUP8A`rCUI$|=-Pyy)P@S7e+m_;VG-D<d;I z#Z9eT0K|0KFAnh9cNeU_^|1NQ$gHk`vES=sl~+b)b%M-jHjfQqQ|^Gp_cq_#9GOKj z^ZR`)^2*38&U`zm<2p6XsXf5yU;{AhUA%v3WQJX^_Xm9p^UBB!i>+=bKhm4t+rw<N zcN@%Z+-q))%&Z62{-}>xUKyF$DVaIg3O3wU`|g6(H@7a0KCRZl*q`;W$}1zYI)e)K z2GX8&;K1RAFxUVLufKI+bcQ#;-e2@F%qt@^jGBG4oa{Q)9G@64-CZrfa<qMEV`OvK z1cQIw$1<-BXPL`#|2m7%HQ#-9c^<#l!1g2w2S0OE1Bz~+xy}?JcW9v$kC}*{R^vyR zI$4U}Lr8Pn#zw@EY8gIw_*?R8;sjeKNsq+s^UL!$kw*iI|9?zxfyuwmFim<SrYDx? z6TKdY^@{K^-Y+zg{_f$`|CnKg^z3>4YeS}P<X5&Da^y+!*M<70b2iUT@NklcmwDj+ zYQ1Op0?)W3QTB)_W<q4+k?hM++_us!;^$go&9WUt*N^Aej+IVzg@<__uHw)w==`yu z^Ob{l8i3QN3}wpU8WG*(0}c;#V~R>uTznN5HO1vZaSAFLb@#yT5f}UE9^2C*6o;xi zeM|4mipM}vX~Td(w|H6qu<pZYv<R1Qzz<jRGiPRsGt)C;^7qn=g>!3WYUUXni@bKg Z5_-ST*34lmKlAp?_h$-yf8)4o{V(S%Dh2=m literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/__pycache__/inspection.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/__pycache__/inspection.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b9586c4dcbcd3568c5cb28dd9e4eb2bdfd7c5741 GIT binary patch literal 2953 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 Co_`(y literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/__pycache__/interfaces.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/__pycache__/interfaces.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae8efe7ca36699dd84e02d3a33b4b3f06c0aa842 GIT binary patch literal 11174 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/__pycache__/log.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/__pycache__/log.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5fd034d783df67f6e08d30798dc43af33de1bc6 GIT binary patch literal 6591 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/__pycache__/pool.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/__pycache__/pool.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7707e6e58d23dea69a21eb6155fc8e002efbb10a GIT binary patch literal 47794 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 P!uP#1$7dd%dGh}P5sh8+ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/__pycache__/processors.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/__pycache__/processors.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db0186abb7c5b8b1823a1880a50c89f3f7408227 GIT binary patch literal 4124 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 S+TLC7p+z@bAFh_yrk?>`7v8D> literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/__pycache__/schema.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/__pycache__/schema.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96aac131ab7218ff70441fdb1ac29b26f312908c GIT binary patch literal 1463 zcmZ9MSyS6c5P*FjvU~vM4q#4Wz=ps+ZKYBHJIPXkRH~ft(B&#+uqL4LwB%@H7VkgW zAFzLBUUy&iFXSoHl0&?!Dt+pi>1%pgb5<-GkAGcS`E)GycWmlOX!(_X<v$vB3}SZN zPKY=rAQ2}yDUz6i6s94K8OUH3vY3M$<{^&;7+8P;nqXoPidcdYmZ6LlsNf9D;4IAI z9L!-AsyGkxxBv^d2#dG`OSlZnq5qUv!Btod<+ON)Yp{mvu#OwBfi<Y%CT!vsY~eO+ z;|}cLF6`nS>|q`1xDWf-fCe7G0Up939>Ebdp@}VM;V~TJ37p__c#fxVif3@9V`RFq zM)r?aZ4ZYmaBdyp1ml{^kjo+K^V%2BugR~1iT*vuaZ8IWNg}H;*RJ_ouz=ew!}vF@ zi`9s$VmDm9aikM?eq%wI7gy)+-&x)EZ?CV<Rk?l7`-8UU%D`ui8>md%6C-q0G5U7+ z7d8@sGNx3DlgYMVBgs`|GJMNj?lT=knQwTYquMu2jJPu1cs_UT+;@Desz2o^hv`e# z=8r0O%>&k90aK+9OiJg0|A=1w;5*3tv34<KW<*=N3^=M>mwz2`x6f7jBfAw`&3(N0 zIkT_4J`+!VE1z6Pt4;YRS$90J0y41%I>D7_+nR#+1IKfxbm#xScI`YmuC8&a@3{|+ zMV0Jyu2i9|;|4s+BH!`7VI-EK`KLThT6!8ytfE4MR*`5<tinT7Mraesd5Z=&4*y+E zl%n)h@!YnjvM!O@)Hth?ykPp=3@yAC9JxOAvK7T4-Eh&g^eXkBBe+xr>G_i?NwqpP zuyoT|diRs=k*eI~LAYOS8+xvg%m2=3l^>OibY`LysqZFc?;F3UtYz6=-?AV_O@6CW z_UR@7<7dzNNj>4G2>Q~9`58i%kR#*?2BAPO2}MGQP$pCeGlW^f9HB~>CoB*a2}>GL zWc@2T?w!9(6In{dwuQe!I;(_dgf+rCVS`X3Y!bEz+k_p$E@6*QC+rg%gag7M;fT;A zv<Sz96OBJ({wc|XZ=c)0_CD$Q$lkav>*HRB%R%4`dl#NJXkIgy-G#^8?2e`WS(?c7 z$L~FG_n{}8+n&A}cvIgl1Fa`}dQX$vr5D{St>IYZUZ7`>1b<28lvLeR!c2c&F_Yf` Dd-lWz literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/__pycache__/types.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/__pycache__/types.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99bca9e48e009809245737c770334b3f07fd6db0 GIT binary patch literal 1683 zcmcJPNps>x6vx2^1OmM8E8dN5@PcQbN(F4<kiysnI2FI5ijsLfjLLcvX~|PVzEO@j z<jeFmr+kH+(hrX(ld2q&Ls0S4>$j=<-%=OSY4hW+mG&>OQ0TYN7r#Sof35#MghL?= zK?uV*gu@uYD8|shI401<B&INpBRGm<IF1uIiBmX@GdPQLIFAdsh)cMPE4YdoT*GzT zz)jr3ZQQ|K%;FyI;{oO{kB8ku2;&hRJ3}-K!xVuCMIlNth>-yX#UUOJ>G+A0AQMbV zLXuLDqBNvw1V(5SMrjPjXdK3A0w!n@CTR+$Xd0$z24-j$W@!%QXddQi0TyTx7HJ8V zXc?Ai1y*PkRw)A+T7xxOhjrS34cdfF+JY_GhHct`9omIm%0ia*V2}1;pAO)Fa*(4u z<mnI&=?ISK7>?-#PN)C{I)zhu2G8g@Jf|1%f?mQ)dIhiOHN2)b@J6qv&<JHue=3}L z)UyKn&UWmepK&b`e$VQN%sWqJ{2v|5>AV-z&ucRu^n1e3o2D|G+A6zU+Z8I!_POYI z(h5AOMsIu}&&4|(LXN26O0B6PXWtg<%J{xm9~jZv;1;hvIU>#S4?3$UJ_aiKP4%kS zR7UOQvRtp6sYtzCtSaNYd{(*CMxt4{EH|3P%WD-sFE-0O)cvbhB^4<#W2H*1Sie;X z-Taejl&-F-<zkIv7d)SML$|83WoVQy%5@dtW_k+TF4hR7?RIt04MbN+m1>aXI8P(9 zWOtuzqxL{T+8sS@$Fl-GCo2$5n}lwtE!~jqWYv;gQL<f2_Vpwl??%b<9AUZ2xN+@{ zheD;!Jh!8(T<gx!MW5UQRm^L<bMJ_Oq;Y<@{9rjMQg$CmB`Pk8kAs?Mv0g9Us>pYZ ztJ?4S+X-c3%Cykx1?@oZhGdr<($Je>N!xM*l?=SL?fQY`c7z<}k`tNdv>C=EV}dcw zSYd2wD5L$j47ts&C}W(l%a~`3F-92cj5WpqBgx1xa*POLj<LkpXYc_aH#Jn^(h557 zdEL@rXPlk!+JoafYEiqXR-3oiWtrmWDr1k4<~*~(Z?83|o@MVcWAQQ6tXFCmgVF?h z7Z{@qld-|rVPqLo41G+6YT37X?QQfr?KT_wy=b=~_4IwTt)4B9Ir+2H@`Q&42Fv@H ztMhq7{C^>}Jmu2=S7!N)pYd5>`J9I@82=!ve93{o>nUHc@4pE7b4b4B8pa>(v)3&? zIs8_C$KUC<&V_#;c)gbXMDBB!mTPqd2{*_!`hFlNN0z?H)(7E!XgT&>%eMoO)5q4m s&huM;9b2t|#`#`f#ov;L503bXH`nJ^J2DgtkAzc$%hWW(=1?a7Cr9n9^8f$< literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/connectors/__init__.py b/venv/Lib/site-packages/sqlalchemy/connectors/__init__.py new file mode 100644 index 0000000..ffed966 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/connectors/__init__.py @@ -0,0 +1,10 @@ +# connectors/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + + +class Connector(object): + pass diff --git a/venv/Lib/site-packages/sqlalchemy/connectors/mxodbc.py b/venv/Lib/site-packages/sqlalchemy/connectors/mxodbc.py new file mode 100644 index 0000000..809fca4 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/connectors/mxodbc.py @@ -0,0 +1,150 @@ +# connectors/mxodbc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +Provide a SQLALchemy connector for the eGenix mxODBC commercial +Python adapter for ODBC. This is not a free product, but eGenix +provides SQLAlchemy with a license for use in continuous integration +testing. + +This has been tested for use with mxODBC 3.1.2 on SQL Server 2005 +and 2008, using the SQL Server Native driver. However, it is +possible for this to be used on other database platforms. + +For more info on mxODBC, see http://www.egenix.com/ + +""" + +import sys +import re +import warnings + +from . import Connector + + +class MxODBCConnector(Connector): + driver = 'mxodbc' + + supports_sane_multi_rowcount = False + supports_unicode_statements = True + supports_unicode_binds = True + + supports_native_decimal = True + + @classmethod + def dbapi(cls): + # this classmethod will normally be replaced by an instance + # attribute of the same name, so this is normally only called once. + cls._load_mx_exceptions() + platform = sys.platform + if platform == 'win32': + from mx.ODBC import Windows as module + # this can be the string "linux2", and possibly others + elif 'linux' in platform: + from mx.ODBC import unixODBC as module + elif platform == 'darwin': + from mx.ODBC import iODBC as module + else: + raise ImportError("Unrecognized platform for mxODBC import") + return module + + @classmethod + def _load_mx_exceptions(cls): + """ Import mxODBC exception classes into the module namespace, + as if they had been imported normally. This is done here + to avoid requiring all SQLAlchemy users to install mxODBC. + """ + global InterfaceError, ProgrammingError + from mx.ODBC import InterfaceError + from mx.ODBC import ProgrammingError + + def on_connect(self): + def connect(conn): + conn.stringformat = self.dbapi.MIXED_STRINGFORMAT + conn.datetimeformat = self.dbapi.PYDATETIME_DATETIMEFORMAT + conn.decimalformat = self.dbapi.DECIMAL_DECIMALFORMAT + conn.errorhandler = self._error_handler() + return connect + + def _error_handler(self): + """ Return a handler that adjusts mxODBC's raised Warnings to + emit Python standard warnings. + """ + from mx.ODBC.Error import Warning as MxOdbcWarning + + def error_handler(connection, cursor, errorclass, errorvalue): + if issubclass(errorclass, MxOdbcWarning): + errorclass.__bases__ = (Warning,) + warnings.warn(message=str(errorvalue), + category=errorclass, + stacklevel=2) + else: + raise errorclass(errorvalue) + return error_handler + + def create_connect_args(self, url): + """ Return a tuple of *args,**kwargs for creating a connection. + + The mxODBC 3.x connection constructor looks like this: + + connect(dsn, user='', password='', + clear_auto_commit=1, errorhandler=None) + + This method translates the values in the provided uri + into args and kwargs needed to instantiate an mxODBC Connection. + + The arg 'errorhandler' is not used by SQLAlchemy and will + not be populated. + + """ + opts = url.translate_connect_args(username='user') + opts.update(url.query) + args = opts.pop('host') + opts.pop('port', None) + opts.pop('database', None) + return (args,), opts + + def is_disconnect(self, e, connection, cursor): + # TODO: eGenix recommends checking connection.closed here + # Does that detect dropped connections ? + if isinstance(e, self.dbapi.ProgrammingError): + return "connection already closed" in str(e) + elif isinstance(e, self.dbapi.Error): + return '[08S01]' in str(e) + else: + return False + + def _get_server_version_info(self, connection): + # eGenix suggests using conn.dbms_version instead + # of what we're doing here + dbapi_con = connection.connection + version = [] + r = re.compile(r'[.\-]') + # 18 == pyodbc.SQL_DBMS_VER + for n in r.split(dbapi_con.getinfo(18)[1]): + try: + version.append(int(n)) + except ValueError: + version.append(n) + return tuple(version) + + def _get_direct(self, context): + if context: + native_odbc_execute = context.execution_options.\ + get('native_odbc_execute', 'auto') + # default to direct=True in all cases, is more generally + # compatible especially with SQL Server + return False if native_odbc_execute is True else True + else: + return True + + def do_executemany(self, cursor, statement, parameters, context=None): + cursor.executemany( + statement, parameters, direct=self._get_direct(context)) + + def do_execute(self, cursor, statement, parameters, context=None): + cursor.execute(statement, parameters, direct=self._get_direct(context)) diff --git a/venv/Lib/site-packages/sqlalchemy/connectors/pyodbc.py b/venv/Lib/site-packages/sqlalchemy/connectors/pyodbc.py new file mode 100644 index 0000000..41ba89d --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/connectors/pyodbc.py @@ -0,0 +1,164 @@ +# connectors/pyodbc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from . import Connector +from .. import util + + +import re + + +class PyODBCConnector(Connector): + driver = 'pyodbc' + + supports_sane_rowcount_returning = False + supports_sane_multi_rowcount = False + + supports_unicode_statements = True + supports_unicode_binds = True + + supports_native_decimal = True + default_paramstyle = 'named' + + # for non-DSN connections, this *may* be used to + # hold the desired driver name + pyodbc_driver_name = None + + def __init__(self, supports_unicode_binds=None, **kw): + super(PyODBCConnector, self).__init__(**kw) + if supports_unicode_binds is not None: + self.supports_unicode_binds = supports_unicode_binds + + @classmethod + def dbapi(cls): + return __import__('pyodbc') + + def create_connect_args(self, url): + opts = url.translate_connect_args(username='user') + opts.update(url.query) + + keys = opts + + query = url.query + + connect_args = {} + for param in ('ansi', 'unicode_results', 'autocommit'): + if param in keys: + connect_args[param] = util.asbool(keys.pop(param)) + + if 'odbc_connect' in keys: + connectors = [util.unquote_plus(keys.pop('odbc_connect'))] + else: + def check_quote(token): + if ";" in str(token): + token = "'%s'" % token + return token + + keys = dict( + (k, check_quote(v)) for k, v in keys.items() + ) + + dsn_connection = 'dsn' in keys or \ + ('host' in keys and 'database' not in keys) + if dsn_connection: + connectors = ['dsn=%s' % (keys.pop('host', '') or + keys.pop('dsn', ''))] + else: + port = '' + if 'port' in keys and 'port' not in query: + port = ',%d' % int(keys.pop('port')) + + connectors = [] + driver = keys.pop('driver', self.pyodbc_driver_name) + if driver is None: + util.warn( + "No driver name specified; " + "this is expected by PyODBC when using " + "DSN-less connections") + else: + connectors.append("DRIVER={%s}" % driver) + + connectors.extend( + [ + 'Server=%s%s' % (keys.pop('host', ''), port), + 'Database=%s' % keys.pop('database', '') + ]) + + user = keys.pop("user", None) + if user: + connectors.append("UID=%s" % user) + connectors.append("PWD=%s" % keys.pop('password', '')) + else: + connectors.append("Trusted_Connection=Yes") + + # if set to 'Yes', the ODBC layer will try to automagically + # convert textual data from your database encoding to your + # client encoding. This should obviously be set to 'No' if + # you query a cp1253 encoded database from a latin1 client... + if 'odbc_autotranslate' in keys: + connectors.append("AutoTranslate=%s" % + keys.pop("odbc_autotranslate")) + + connectors.extend(['%s=%s' % (k, v) for k, v in keys.items()]) + + return [[";".join(connectors)], connect_args] + + def is_disconnect(self, e, connection, cursor): + if isinstance(e, self.dbapi.ProgrammingError): + return "The cursor's connection has been closed." in str(e) or \ + 'Attempt to use a closed connection.' in str(e) + else: + return False + + # def initialize(self, connection): + # super(PyODBCConnector, self).initialize(connection) + + def _dbapi_version(self): + if not self.dbapi: + return () + return self._parse_dbapi_version(self.dbapi.version) + + def _parse_dbapi_version(self, vers): + m = re.match( + r'(?:py.*-)?([\d\.]+)(?:-(\w+))?', + vers + ) + if not m: + return () + vers = tuple([int(x) for x in m.group(1).split(".")]) + if m.group(2): + vers += (m.group(2),) + return vers + + def _get_server_version_info(self, connection, allow_chars=True): + # NOTE: this function is not reliable, particularly when + # freetds is in use. Implement database-specific server version + # queries. + dbapi_con = connection.connection + version = [] + r = re.compile(r'[.\-]') + for n in r.split(dbapi_con.getinfo(self.dbapi.SQL_DBMS_VER)): + try: + version.append(int(n)) + except ValueError: + if allow_chars: + version.append(n) + return tuple(version) + + def set_isolation_level(self, connection, level): + # adjust for ConnectionFairy being present + # allows attribute set e.g. "connection.autocommit = True" + # to work properly + if hasattr(connection, 'connection'): + connection = connection.connection + + if level == 'AUTOCOMMIT': + connection.autocommit = True + else: + connection.autocommit = False + super(PyODBCConnector, self).set_isolation_level(connection, + level) diff --git a/venv/Lib/site-packages/sqlalchemy/connectors/zxJDBC.py b/venv/Lib/site-packages/sqlalchemy/connectors/zxJDBC.py new file mode 100644 index 0000000..71decd9 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/connectors/zxJDBC.py @@ -0,0 +1,60 @@ +# connectors/zxJDBC.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +import sys +from . import Connector + + +class ZxJDBCConnector(Connector): + driver = 'zxjdbc' + + supports_sane_rowcount = False + supports_sane_multi_rowcount = False + + supports_unicode_binds = True + supports_unicode_statements = sys.version > '2.5.0+' + description_encoding = None + default_paramstyle = 'qmark' + + jdbc_db_name = None + jdbc_driver_name = None + + @classmethod + def dbapi(cls): + from com.ziclix.python.sql import zxJDBC + return zxJDBC + + def _driver_kwargs(self): + """Return kw arg dict to be sent to connect().""" + return {} + + def _create_jdbc_url(self, url): + """Create a JDBC url from a :class:`~sqlalchemy.engine.url.URL`""" + return 'jdbc:%s://%s%s/%s' % (self.jdbc_db_name, url.host, + url.port is not None + and ':%s' % url.port or '', + url.database) + + def create_connect_args(self, url): + opts = self._driver_kwargs() + opts.update(url.query) + return [ + [self._create_jdbc_url(url), + url.username, url.password, + self.jdbc_driver_name], + opts] + + def is_disconnect(self, e, connection, cursor): + if not isinstance(e, self.dbapi.ProgrammingError): + return False + e = str(e) + return 'connection is closed' in e or 'cursor is closed' in e + + def _get_server_version_info(self, connection): + # use connection.connection.dbversion, and parse appropriately + # to get a tuple + raise NotImplementedError() diff --git a/venv/Lib/site-packages/sqlalchemy/cprocessors.cp36-win_amd64.pyd b/venv/Lib/site-packages/sqlalchemy/cprocessors.cp36-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..163986c9ae04720d5565c4f907f411d997156f12 GIT binary patch literal 17408 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 UKjc0nAKG{bYX9*3FY&;C0cy~_8~^|S literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/cresultproxy.cp36-win_amd64.pyd b/venv/Lib/site-packages/sqlalchemy/cresultproxy.cp36-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..581d51e3c405b4b04fbf37e4567eb8e9541b87ab GIT binary patch literal 19456 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 a@uJ7eA8&d5^yBP_yeIx!+x!2s&i^O;UnYY9 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/cutils.cp36-win_amd64.pyd b/venv/Lib/site-packages/sqlalchemy/cutils.cp36-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..fb4e4c45f65b0d383b9e7b51175531a647da59be GIT binary patch literal 12800 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/databases/__init__.py b/venv/Lib/site-packages/sqlalchemy/databases/__init__.py new file mode 100644 index 0000000..2cb2527 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/databases/__init__.py @@ -0,0 +1,30 @@ +# databases/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Include imports from the sqlalchemy.dialects package for backwards +compatibility with pre 0.6 versions. + +""" +from ..dialects.sqlite import base as sqlite +from ..dialects.postgresql import base as postgresql +postgres = postgresql +from ..dialects.mysql import base as mysql +from ..dialects.oracle import base as oracle +from ..dialects.firebird import base as firebird +from ..dialects.mssql import base as mssql +from ..dialects.sybase import base as sybase + + +__all__ = ( + 'firebird', + 'mssql', + 'mysql', + 'postgresql', + 'sqlite', + 'oracle', + 'sybase', +) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/__init__.py b/venv/Lib/site-packages/sqlalchemy/dialects/__init__.py new file mode 100644 index 0000000..963babc --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/__init__.py @@ -0,0 +1,56 @@ +# dialects/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +__all__ = ( + 'firebird', + 'mssql', + 'mysql', + 'oracle', + 'postgresql', + 'sqlite', + 'sybase', +) + +from .. import util + +_translates = {'postgres': 'postgresql'} + +def _auto_fn(name): + """default dialect importer. + + plugs into the :class:`.PluginLoader` + as a first-hit system. + + """ + if "." in name: + dialect, driver = name.split(".") + else: + dialect = name + driver = "base" + + if dialect in _translates: + translated = _translates[dialect] + util.warn_deprecated( + "The '%s' dialect name has been " + "renamed to '%s'" % (dialect, translated) + ) + dialect = translated + try: + module = __import__('sqlalchemy.dialects.%s' % (dialect, )).dialects + except ImportError: + return None + + module = getattr(module, dialect) + if hasattr(module, driver): + module = getattr(module, driver) + return lambda: module.dialect + else: + return None + +registry = util.PluginLoader("sqlalchemy.dialects", auto_fn=_auto_fn) + +plugins = util.PluginLoader("sqlalchemy.plugins") \ No newline at end of file diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/dialects/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b582ca449bcc81d2dd8a186bda6ea56bdcaa680 GIT binary patch literal 1198 zcmYjR&2Hm15a!Ubq$p0ZKS9tIJ?OFxe6SDOTMz_`ZFhkM+MqyMv@ir{X_Iyo>&G3^ zX=TVKFR+`#zCe$?^#S@ay!O;r=&3`+$%ev=hQlF;^UVyu-rI|Q|NBk)vdh>%?7;{~ zenF&9iOEMJCIcDfQ1B54CU>Nl!Kfo6x%-ZdJjJiNvZp+`_mPkMcDBEPQ6LX4SpTsN zrx~hohSGKmt$)qwSG%8Ip_t^#MvF?D8LC!CEHldbx*iLy{^jJp57t|nEFXn;h7%yQ z8y?XvQHMzXNlZe+=01Mbuq%=`knlCEc^Nir4U(^U0|sdQwSfjcXX}pPR~^}Dpy^Ec z5!(Q;rn83n(0I~oU<yum)xBXqv+~em#-B%60&}12Ua()72|lm}HvEQ}kR-n0_(!WF z>4v>$Q;@z4-h~&fz1!0dS4yg>Smq{{naI_|#96VZ&?p>6QA}fzFK0T=N>jz=QpG2e zTxfmrcK8G7v+}!2NQG}(OQB;C6TI3CE;AGBS{qetPq8qxo&#|;{E<#Mex{G_9hRa{ z@g?mZD^<p*++4=w&snXGC<!8;T&kiTZm+0^&-D3(0KOkC1y6~zPwWH1O`*X#<to^2 z`O78FFDkjr74DJIfAeonlAp9foz%zrxK6%RdTy#k^0KPtgYQKtW~xx78C<ks3<^Qf zU~;X>>m<*{iKfCBEW~6^b~^cV$K>vgNt$M5X3})HsBL(fi()Lr*>iVEBDVS>6-!g4 z({gy4R}+!zGZJo%`a8C(7gTz7H#I0qoeQHh?$9=S@SDIgm1==%LRx7f^3!H4O>KDR z-0r?<b*~UBw82anVGId`vJ?8A^&HCBb4U6TvulMmy7whpi28v0w2lYPD7?Gw5zX{h z#8?o*BkqC6LkPhG4n3N2ID~!hc?9n3ar}}jS5H3ybe}|*oh<~bClA(L!DXdB?+578 zimvO>^%S}WLwXz-x-_GE7W$zL(p2O*MbBD<@QGV<UTyDTjoJ{^EYk*S8*E*)Q9fX| cfu7!0$(e&&yJ*`EyX`aZIz1lpFy80?1M$R0^Z)<= literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/firebird/__init__.py b/venv/Lib/site-packages/sqlalchemy/dialects/firebird/__init__.py new file mode 100644 index 0000000..c83db45 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/firebird/__init__.py @@ -0,0 +1,20 @@ +# firebird/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from . import base, kinterbasdb, fdb # noqa + +from sqlalchemy.dialects.firebird.base import \ + SMALLINT, BIGINT, FLOAT, DATE, TIME, \ + TEXT, NUMERIC, TIMESTAMP, VARCHAR, CHAR, BLOB + +base.dialect = dialect = fdb.dialect + +__all__ = ( + 'SMALLINT', 'BIGINT', 'FLOAT', 'FLOAT', 'DATE', 'TIME', + 'TEXT', 'NUMERIC', 'FLOAT', 'TIMESTAMP', 'VARCHAR', 'CHAR', 'BLOB', + 'dialect' +) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/firebird/base.py b/venv/Lib/site-packages/sqlalchemy/dialects/firebird/base.py new file mode 100644 index 0000000..7b470c1 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/firebird/base.py @@ -0,0 +1,741 @@ +# firebird/base.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +r""" + +.. dialect:: firebird + :name: Firebird + +Firebird Dialects +----------------- + +Firebird offers two distinct dialects_ (not to be confused with a +SQLAlchemy ``Dialect``): + +dialect 1 + This is the old syntax and behaviour, inherited from Interbase pre-6.0. + +dialect 3 + This is the newer and supported syntax, introduced in Interbase 6.0. + +The SQLAlchemy Firebird dialect detects these versions and +adjusts its representation of SQL accordingly. However, +support for dialect 1 is not well tested and probably has +incompatibilities. + +Locking Behavior +---------------- + +Firebird locks tables aggressively. For this reason, a DROP TABLE may +hang until other transactions are released. SQLAlchemy does its best +to release transactions as quickly as possible. The most common cause +of hanging transactions is a non-fully consumed result set, i.e.:: + + result = engine.execute("select * from table") + row = result.fetchone() + return + +Where above, the ``ResultProxy`` has not been fully consumed. The +connection will be returned to the pool and the transactional state +rolled back once the Python garbage collector reclaims the objects +which hold onto the connection, which often occurs asynchronously. +The above use case can be alleviated by calling ``first()`` on the +``ResultProxy`` which will fetch the first row and immediately close +all remaining cursor/connection resources. + +RETURNING support +----------------- + +Firebird 2.0 supports returning a result set from inserts, and 2.1 +extends that to deletes and updates. This is generically exposed by +the SQLAlchemy ``returning()`` method, such as:: + + # INSERT..RETURNING + result = table.insert().returning(table.c.col1, table.c.col2).\ + values(name='foo') + print result.fetchall() + + # UPDATE..RETURNING + raises = empl.update().returning(empl.c.id, empl.c.salary).\ + where(empl.c.sales>100).\ + values(dict(salary=empl.c.salary * 1.1)) + print raises.fetchall() + + +.. _dialects: http://mc-computing.com/Databases/Firebird/SQL_Dialect.html + +""" + +import datetime + +from sqlalchemy import schema as sa_schema +from sqlalchemy import exc, types as sqltypes, sql, util +from sqlalchemy.sql import expression +from sqlalchemy.engine import base, default, reflection +from sqlalchemy.sql import compiler +from sqlalchemy.sql.elements import quoted_name + +from sqlalchemy.types import (BIGINT, BLOB, DATE, FLOAT, INTEGER, NUMERIC, + SMALLINT, TEXT, TIME, TIMESTAMP, Integer) + + +RESERVED_WORDS = set([ + "active", "add", "admin", "after", "all", "alter", "and", "any", "as", + "asc", "ascending", "at", "auto", "avg", "before", "begin", "between", + "bigint", "bit_length", "blob", "both", "by", "case", "cast", "char", + "character", "character_length", "char_length", "check", "close", + "collate", "column", "commit", "committed", "computed", "conditional", + "connect", "constraint", "containing", "count", "create", "cross", + "cstring", "current", "current_connection", "current_date", + "current_role", "current_time", "current_timestamp", + "current_transaction", "current_user", "cursor", "database", "date", + "day", "dec", "decimal", "declare", "default", "delete", "desc", + "descending", "disconnect", "distinct", "do", "domain", "double", + "drop", "else", "end", "entry_point", "escape", "exception", + "execute", "exists", "exit", "external", "extract", "fetch", "file", + "filter", "float", "for", "foreign", "from", "full", "function", + "gdscode", "generator", "gen_id", "global", "grant", "group", + "having", "hour", "if", "in", "inactive", "index", "inner", + "input_type", "insensitive", "insert", "int", "integer", "into", "is", + "isolation", "join", "key", "leading", "left", "length", "level", + "like", "long", "lower", "manual", "max", "maximum_segment", "merge", + "min", "minute", "module_name", "month", "names", "national", + "natural", "nchar", "no", "not", "null", "numeric", "octet_length", + "of", "on", "only", "open", "option", "or", "order", "outer", + "output_type", "overflow", "page", "pages", "page_size", "parameter", + "password", "plan", "position", "post_event", "precision", "primary", + "privileges", "procedure", "protected", "rdb$db_key", "read", "real", + "record_version", "recreate", "recursive", "references", "release", + "reserv", "reserving", "retain", "returning_values", "returns", + "revoke", "right", "rollback", "rows", "row_count", "savepoint", + "schema", "second", "segment", "select", "sensitive", "set", "shadow", + "shared", "singular", "size", "smallint", "snapshot", "some", "sort", + "sqlcode", "stability", "start", "starting", "starts", "statistics", + "sub_type", "sum", "suspend", "table", "then", "time", "timestamp", + "to", "trailing", "transaction", "trigger", "trim", "uncommitted", + "union", "unique", "update", "upper", "user", "using", "value", + "values", "varchar", "variable", "varying", "view", "wait", "when", + "where", "while", "with", "work", "write", "year", +]) + + +class _StringType(sqltypes.String): + """Base for Firebird string types.""" + + def __init__(self, charset=None, **kw): + self.charset = charset + super(_StringType, self).__init__(**kw) + + +class VARCHAR(_StringType, sqltypes.VARCHAR): + """Firebird VARCHAR type""" + __visit_name__ = 'VARCHAR' + + def __init__(self, length=None, **kwargs): + super(VARCHAR, self).__init__(length=length, **kwargs) + + +class CHAR(_StringType, sqltypes.CHAR): + """Firebird CHAR type""" + __visit_name__ = 'CHAR' + + def __init__(self, length=None, **kwargs): + super(CHAR, self).__init__(length=length, **kwargs) + + +class _FBDateTime(sqltypes.DateTime): + def bind_processor(self, dialect): + def process(value): + if type(value) == datetime.date: + return datetime.datetime(value.year, value.month, value.day) + else: + return value + return process + +colspecs = { + sqltypes.DateTime: _FBDateTime +} + +ischema_names = { + 'SHORT': SMALLINT, + 'LONG': INTEGER, + 'QUAD': FLOAT, + 'FLOAT': FLOAT, + 'DATE': DATE, + 'TIME': TIME, + 'TEXT': TEXT, + 'INT64': BIGINT, + 'DOUBLE': FLOAT, + 'TIMESTAMP': TIMESTAMP, + 'VARYING': VARCHAR, + 'CSTRING': CHAR, + 'BLOB': BLOB, +} + + +# TODO: date conversion types (should be implemented as _FBDateTime, +# _FBDate, etc. as bind/result functionality is required) + +class FBTypeCompiler(compiler.GenericTypeCompiler): + def visit_boolean(self, type_, **kw): + return self.visit_SMALLINT(type_, **kw) + + def visit_datetime(self, type_, **kw): + return self.visit_TIMESTAMP(type_, **kw) + + def visit_TEXT(self, type_, **kw): + return "BLOB SUB_TYPE 1" + + def visit_BLOB(self, type_, **kw): + return "BLOB SUB_TYPE 0" + + def _extend_string(self, type_, basic): + charset = getattr(type_, 'charset', None) + if charset is None: + return basic + else: + return '%s CHARACTER SET %s' % (basic, charset) + + def visit_CHAR(self, type_, **kw): + basic = super(FBTypeCompiler, self).visit_CHAR(type_, **kw) + return self._extend_string(type_, basic) + + def visit_VARCHAR(self, type_, **kw): + if not type_.length: + raise exc.CompileError( + "VARCHAR requires a length on dialect %s" % + self.dialect.name) + basic = super(FBTypeCompiler, self).visit_VARCHAR(type_, **kw) + return self._extend_string(type_, basic) + + +class FBCompiler(sql.compiler.SQLCompiler): + """Firebird specific idiosyncrasies""" + + ansi_bind_rules = True + + # def visit_contains_op_binary(self, binary, operator, **kw): + # cant use CONTAINING b.c. it's case insensitive. + + # def visit_notcontains_op_binary(self, binary, operator, **kw): + # cant use NOT CONTAINING b.c. it's case insensitive. + + def visit_now_func(self, fn, **kw): + return "CURRENT_TIMESTAMP" + + def visit_startswith_op_binary(self, binary, operator, **kw): + return '%s STARTING WITH %s' % ( + binary.left._compiler_dispatch(self, **kw), + binary.right._compiler_dispatch(self, **kw)) + + def visit_notstartswith_op_binary(self, binary, operator, **kw): + return '%s NOT STARTING WITH %s' % ( + binary.left._compiler_dispatch(self, **kw), + binary.right._compiler_dispatch(self, **kw)) + + def visit_mod_binary(self, binary, operator, **kw): + return "mod(%s, %s)" % ( + self.process(binary.left, **kw), + self.process(binary.right, **kw)) + + def visit_alias(self, alias, asfrom=False, **kwargs): + if self.dialect._version_two: + return super(FBCompiler, self).\ + visit_alias(alias, asfrom=asfrom, **kwargs) + else: + # Override to not use the AS keyword which FB 1.5 does not like + if asfrom: + alias_name = isinstance(alias.name, + expression._truncated_label) and \ + self._truncated_identifier("alias", + alias.name) or alias.name + + return self.process( + alias.original, asfrom=asfrom, **kwargs) + \ + " " + \ + self.preparer.format_alias(alias, alias_name) + else: + return self.process(alias.original, **kwargs) + + def visit_substring_func(self, func, **kw): + s = self.process(func.clauses.clauses[0]) + start = self.process(func.clauses.clauses[1]) + if len(func.clauses.clauses) > 2: + length = self.process(func.clauses.clauses[2]) + return "SUBSTRING(%s FROM %s FOR %s)" % (s, start, length) + else: + return "SUBSTRING(%s FROM %s)" % (s, start) + + def visit_length_func(self, function, **kw): + if self.dialect._version_two: + return "char_length" + self.function_argspec(function) + else: + return "strlen" + self.function_argspec(function) + + visit_char_length_func = visit_length_func + + def function_argspec(self, func, **kw): + # TODO: this probably will need to be + # narrowed to a fixed list, some no-arg functions + # may require parens - see similar example in the oracle + # dialect + if func.clauses is not None and len(func.clauses): + return self.process(func.clause_expr, **kw) + else: + return "" + + def default_from(self): + return " FROM rdb$database" + + def visit_sequence(self, seq, **kw): + return "gen_id(%s, 1)" % self.preparer.format_sequence(seq) + + def get_select_precolumns(self, select, **kw): + """Called when building a ``SELECT`` statement, position is just + before column list Firebird puts the limit and offset right + after the ``SELECT``... + """ + + result = "" + if select._limit_clause is not None: + result += "FIRST %s " % self.process(select._limit_clause, **kw) + if select._offset_clause is not None: + result += "SKIP %s " % self.process(select._offset_clause, **kw) + if select._distinct: + result += "DISTINCT " + return result + + def limit_clause(self, select, **kw): + """Already taken care of in the `get_select_precolumns` method.""" + + return "" + + def returning_clause(self, stmt, returning_cols): + columns = [ + self._label_select_column(None, c, True, False, {}) + for c in expression._select_iterables(returning_cols) + ] + + return 'RETURNING ' + ', '.join(columns) + + +class FBDDLCompiler(sql.compiler.DDLCompiler): + """Firebird syntactic idiosyncrasies""" + + def visit_create_sequence(self, create): + """Generate a ``CREATE GENERATOR`` statement for the sequence.""" + + # no syntax for these + # http://www.firebirdsql.org/manual/generatorguide-sqlsyntax.html + if create.element.start is not None: + raise NotImplemented( + "Firebird SEQUENCE doesn't support START WITH") + if create.element.increment is not None: + raise NotImplemented( + "Firebird SEQUENCE doesn't support INCREMENT BY") + + if self.dialect._version_two: + return "CREATE SEQUENCE %s" % \ + self.preparer.format_sequence(create.element) + else: + return "CREATE GENERATOR %s" % \ + self.preparer.format_sequence(create.element) + + def visit_drop_sequence(self, drop): + """Generate a ``DROP GENERATOR`` statement for the sequence.""" + + if self.dialect._version_two: + return "DROP SEQUENCE %s" % \ + self.preparer.format_sequence(drop.element) + else: + return "DROP GENERATOR %s" % \ + self.preparer.format_sequence(drop.element) + + +class FBIdentifierPreparer(sql.compiler.IdentifierPreparer): + """Install Firebird specific reserved words.""" + + reserved_words = RESERVED_WORDS + illegal_initial_characters = compiler.ILLEGAL_INITIAL_CHARACTERS.union( + ['_']) + + def __init__(self, dialect): + super(FBIdentifierPreparer, self).__init__(dialect, omit_schema=True) + + +class FBExecutionContext(default.DefaultExecutionContext): + def fire_sequence(self, seq, type_): + """Get the next value from the sequence using ``gen_id()``.""" + + return self._execute_scalar( + "SELECT gen_id(%s, 1) FROM rdb$database" % + self.dialect.identifier_preparer.format_sequence(seq), + type_ + ) + + +class FBDialect(default.DefaultDialect): + """Firebird dialect""" + + name = 'firebird' + + max_identifier_length = 31 + + supports_sequences = True + sequences_optional = False + supports_default_values = True + postfetch_lastrowid = False + + supports_native_boolean = False + + requires_name_normalize = True + supports_empty_insert = False + + statement_compiler = FBCompiler + ddl_compiler = FBDDLCompiler + preparer = FBIdentifierPreparer + type_compiler = FBTypeCompiler + execution_ctx_cls = FBExecutionContext + + colspecs = colspecs + ischema_names = ischema_names + + construct_arguments = [] + + # defaults to dialect ver. 3, + # will be autodetected off upon + # first connect + _version_two = True + + def initialize(self, connection): + super(FBDialect, self).initialize(connection) + self._version_two = ('firebird' in self.server_version_info and + self.server_version_info >= (2, ) + ) or \ + ('interbase' in self.server_version_info and + self.server_version_info >= (6, ) + ) + + if not self._version_two: + # TODO: whatever other pre < 2.0 stuff goes here + self.ischema_names = ischema_names.copy() + self.ischema_names['TIMESTAMP'] = sqltypes.DATE + self.colspecs = { + sqltypes.DateTime: sqltypes.DATE + } + + self.implicit_returning = self._version_two and \ + self.__dict__.get('implicit_returning', True) + + def normalize_name(self, name): + # Remove trailing spaces: FB uses a CHAR() type, + # that is padded with spaces + name = name and name.rstrip() + if name is None: + return None + elif name.upper() == name and \ + not self.identifier_preparer._requires_quotes(name.lower()): + return name.lower() + elif name.lower() == name: + return quoted_name(name, quote=True) + else: + return name + + def denormalize_name(self, name): + if name is None: + return None + elif name.lower() == name and \ + not self.identifier_preparer._requires_quotes(name.lower()): + return name.upper() + else: + return name + + def has_table(self, connection, table_name, schema=None): + """Return ``True`` if the given table exists, ignoring + the `schema`.""" + + tblqry = """ + SELECT 1 AS has_table FROM rdb$database + WHERE EXISTS (SELECT rdb$relation_name + FROM rdb$relations + WHERE rdb$relation_name=?) + """ + c = connection.execute(tblqry, [self.denormalize_name(table_name)]) + return c.first() is not None + + def has_sequence(self, connection, sequence_name, schema=None): + """Return ``True`` if the given sequence (generator) exists.""" + + genqry = """ + SELECT 1 AS has_sequence FROM rdb$database + WHERE EXISTS (SELECT rdb$generator_name + FROM rdb$generators + WHERE rdb$generator_name=?) + """ + c = connection.execute(genqry, [self.denormalize_name(sequence_name)]) + return c.first() is not None + + @reflection.cache + def get_table_names(self, connection, schema=None, **kw): + # there are two queries commonly mentioned for this. + # this one, using view_blr, is at the Firebird FAQ among other places: + # http://www.firebirdfaq.org/faq174/ + s = """ + select rdb$relation_name + from rdb$relations + where rdb$view_blr is null + and (rdb$system_flag is null or rdb$system_flag = 0); + """ + + # the other query is this one. It's not clear if there's really + # any difference between these two. This link: + # http://www.alberton.info/firebird_sql_meta_info.html#.Ur3vXfZGni8 + # states them as interchangeable. Some discussion at [ticket:2898] + # SELECT DISTINCT rdb$relation_name + # FROM rdb$relation_fields + # WHERE rdb$system_flag=0 AND rdb$view_context IS NULL + + return [self.normalize_name(row[0]) for row in connection.execute(s)] + + @reflection.cache + def get_view_names(self, connection, schema=None, **kw): + # see http://www.firebirdfaq.org/faq174/ + s = """ + select rdb$relation_name + from rdb$relations + where rdb$view_blr is not null + and (rdb$system_flag is null or rdb$system_flag = 0); + """ + return [self.normalize_name(row[0]) for row in connection.execute(s)] + + @reflection.cache + def get_view_definition(self, connection, view_name, schema=None, **kw): + qry = """ + SELECT rdb$view_source AS view_source + FROM rdb$relations + WHERE rdb$relation_name=? + """ + rp = connection.execute(qry, [self.denormalize_name(view_name)]) + row = rp.first() + if row: + return row['view_source'] + else: + return None + + @reflection.cache + def get_pk_constraint(self, connection, table_name, schema=None, **kw): + # Query to extract the PK/FK constrained fields of the given table + keyqry = """ + SELECT se.rdb$field_name AS fname + FROM rdb$relation_constraints rc + JOIN rdb$index_segments se ON rc.rdb$index_name=se.rdb$index_name + WHERE rc.rdb$constraint_type=? AND rc.rdb$relation_name=? + """ + tablename = self.denormalize_name(table_name) + # get primary key fields + c = connection.execute(keyqry, ["PRIMARY KEY", tablename]) + pkfields = [self.normalize_name(r['fname']) for r in c.fetchall()] + return {'constrained_columns': pkfields, 'name': None} + + @reflection.cache + def get_column_sequence(self, connection, + table_name, column_name, + schema=None, **kw): + tablename = self.denormalize_name(table_name) + colname = self.denormalize_name(column_name) + # Heuristic-query to determine the generator associated to a PK field + genqry = """ + SELECT trigdep.rdb$depended_on_name AS fgenerator + FROM rdb$dependencies tabdep + JOIN rdb$dependencies trigdep + ON tabdep.rdb$dependent_name=trigdep.rdb$dependent_name + AND trigdep.rdb$depended_on_type=14 + AND trigdep.rdb$dependent_type=2 + JOIN rdb$triggers trig ON + trig.rdb$trigger_name=tabdep.rdb$dependent_name + WHERE tabdep.rdb$depended_on_name=? + AND tabdep.rdb$depended_on_type=0 + AND trig.rdb$trigger_type=1 + AND tabdep.rdb$field_name=? + AND (SELECT count(*) + FROM rdb$dependencies trigdep2 + WHERE trigdep2.rdb$dependent_name = trigdep.rdb$dependent_name) = 2 + """ + genr = connection.execute(genqry, [tablename, colname]).first() + if genr is not None: + return dict(name=self.normalize_name(genr['fgenerator'])) + + @reflection.cache + def get_columns(self, connection, table_name, schema=None, **kw): + # Query to extract the details of all the fields of the given table + tblqry = """ + SELECT r.rdb$field_name AS fname, + r.rdb$null_flag AS null_flag, + t.rdb$type_name AS ftype, + f.rdb$field_sub_type AS stype, + f.rdb$field_length/ + COALESCE(cs.rdb$bytes_per_character,1) AS flen, + f.rdb$field_precision AS fprec, + f.rdb$field_scale AS fscale, + COALESCE(r.rdb$default_source, + f.rdb$default_source) AS fdefault + FROM rdb$relation_fields r + JOIN rdb$fields f ON r.rdb$field_source=f.rdb$field_name + JOIN rdb$types t + ON t.rdb$type=f.rdb$field_type AND + t.rdb$field_name='RDB$FIELD_TYPE' + LEFT JOIN rdb$character_sets cs ON + f.rdb$character_set_id=cs.rdb$character_set_id + WHERE f.rdb$system_flag=0 AND r.rdb$relation_name=? + ORDER BY r.rdb$field_position + """ + # get the PK, used to determine the eventual associated sequence + pk_constraint = self.get_pk_constraint(connection, table_name) + pkey_cols = pk_constraint['constrained_columns'] + + tablename = self.denormalize_name(table_name) + # get all of the fields for this table + c = connection.execute(tblqry, [tablename]) + cols = [] + while True: + row = c.fetchone() + if row is None: + break + name = self.normalize_name(row['fname']) + orig_colname = row['fname'] + + # get the data type + colspec = row['ftype'].rstrip() + coltype = self.ischema_names.get(colspec) + if coltype is None: + util.warn("Did not recognize type '%s' of column '%s'" % + (colspec, name)) + coltype = sqltypes.NULLTYPE + elif issubclass(coltype, Integer) and row['fprec'] != 0: + coltype = NUMERIC( + precision=row['fprec'], + scale=row['fscale'] * -1) + elif colspec in ('VARYING', 'CSTRING'): + coltype = coltype(row['flen']) + elif colspec == 'TEXT': + coltype = TEXT(row['flen']) + elif colspec == 'BLOB': + if row['stype'] == 1: + coltype = TEXT() + else: + coltype = BLOB() + else: + coltype = coltype() + + # does it have a default value? + defvalue = None + if row['fdefault'] is not None: + # the value comes down as "DEFAULT 'value'": there may be + # more than one whitespace around the "DEFAULT" keyword + # and it may also be lower case + # (see also http://tracker.firebirdsql.org/browse/CORE-356) + defexpr = row['fdefault'].lstrip() + assert defexpr[:8].rstrip().upper() == \ + 'DEFAULT', "Unrecognized default value: %s" % \ + defexpr + defvalue = defexpr[8:].strip() + if defvalue == 'NULL': + # Redundant + defvalue = None + col_d = { + 'name': name, + 'type': coltype, + 'nullable': not bool(row['null_flag']), + 'default': defvalue, + 'autoincrement': 'auto', + } + + if orig_colname.lower() == orig_colname: + col_d['quote'] = True + + # if the PK is a single field, try to see if its linked to + # a sequence thru a trigger + if len(pkey_cols) == 1 and name == pkey_cols[0]: + seq_d = self.get_column_sequence(connection, tablename, name) + if seq_d is not None: + col_d['sequence'] = seq_d + + cols.append(col_d) + return cols + + @reflection.cache + def get_foreign_keys(self, connection, table_name, schema=None, **kw): + # Query to extract the details of each UK/FK of the given table + fkqry = """ + SELECT rc.rdb$constraint_name AS cname, + cse.rdb$field_name AS fname, + ix2.rdb$relation_name AS targetrname, + se.rdb$field_name AS targetfname + FROM rdb$relation_constraints rc + JOIN rdb$indices ix1 ON ix1.rdb$index_name=rc.rdb$index_name + JOIN rdb$indices ix2 ON ix2.rdb$index_name=ix1.rdb$foreign_key + JOIN rdb$index_segments cse ON + cse.rdb$index_name=ix1.rdb$index_name + JOIN rdb$index_segments se + ON se.rdb$index_name=ix2.rdb$index_name + AND se.rdb$field_position=cse.rdb$field_position + WHERE rc.rdb$constraint_type=? AND rc.rdb$relation_name=? + ORDER BY se.rdb$index_name, se.rdb$field_position + """ + tablename = self.denormalize_name(table_name) + + c = connection.execute(fkqry, ["FOREIGN KEY", tablename]) + fks = util.defaultdict(lambda: { + 'name': None, + 'constrained_columns': [], + 'referred_schema': None, + 'referred_table': None, + 'referred_columns': [] + }) + + for row in c: + cname = self.normalize_name(row['cname']) + fk = fks[cname] + if not fk['name']: + fk['name'] = cname + fk['referred_table'] = self.normalize_name(row['targetrname']) + fk['constrained_columns'].append( + self.normalize_name(row['fname'])) + fk['referred_columns'].append( + self.normalize_name(row['targetfname'])) + return list(fks.values()) + + @reflection.cache + def get_indexes(self, connection, table_name, schema=None, **kw): + qry = """ + SELECT ix.rdb$index_name AS index_name, + ix.rdb$unique_flag AS unique_flag, + ic.rdb$field_name AS field_name + FROM rdb$indices ix + JOIN rdb$index_segments ic + ON ix.rdb$index_name=ic.rdb$index_name + LEFT OUTER JOIN rdb$relation_constraints + ON rdb$relation_constraints.rdb$index_name = + ic.rdb$index_name + WHERE ix.rdb$relation_name=? AND ix.rdb$foreign_key IS NULL + AND rdb$relation_constraints.rdb$constraint_type IS NULL + ORDER BY index_name, ic.rdb$field_position + """ + c = connection.execute(qry, [self.denormalize_name(table_name)]) + + indexes = util.defaultdict(dict) + for row in c: + indexrec = indexes[row['index_name']] + if 'name' not in indexrec: + indexrec['name'] = self.normalize_name(row['index_name']) + indexrec['column_names'] = [] + indexrec['unique'] = bool(row['unique_flag']) + + indexrec['column_names'].append( + self.normalize_name(row['field_name'])) + + return list(indexes.values()) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/firebird/fdb.py b/venv/Lib/site-packages/sqlalchemy/dialects/firebird/fdb.py new file mode 100644 index 0000000..e8da6e1 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/firebird/fdb.py @@ -0,0 +1,118 @@ +# firebird/fdb.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: firebird+fdb + :name: fdb + :dbapi: pyodbc + :connectstring: firebird+fdb://user:password@host:port/path/to/db\ +[?key=value&key=value...] + :url: http://pypi.python.org/pypi/fdb/ + + fdb is a kinterbasdb compatible DBAPI for Firebird. + + .. versionadded:: 0.8 - Support for the fdb Firebird driver. + + .. versionchanged:: 0.9 - The fdb dialect is now the default dialect + under the ``firebird://`` URL space, as ``fdb`` is now the official + Python driver for Firebird. + +Arguments +---------- + +The ``fdb`` dialect is based on the +:mod:`sqlalchemy.dialects.firebird.kinterbasdb` dialect, however does not +accept every argument that Kinterbasdb does. + +* ``enable_rowcount`` - True by default, setting this to False disables + the usage of "cursor.rowcount" with the + Kinterbasdb dialect, which SQLAlchemy ordinarily calls upon automatically + after any UPDATE or DELETE statement. When disabled, SQLAlchemy's + ResultProxy will return -1 for result.rowcount. The rationale here is + that Kinterbasdb requires a second round trip to the database when + .rowcount is called - since SQLA's resultproxy automatically closes + the cursor after a non-result-returning statement, rowcount must be + called, if at all, before the result object is returned. Additionally, + cursor.rowcount may not return correct results with older versions + of Firebird, and setting this flag to False will also cause the + SQLAlchemy ORM to ignore its usage. The behavior can also be controlled on a + per-execution basis using the ``enable_rowcount`` option with + :meth:`.Connection.execution_options`:: + + conn = engine.connect().execution_options(enable_rowcount=True) + r = conn.execute(stmt) + print r.rowcount + +* ``retaining`` - False by default. Setting this to True will pass the + ``retaining=True`` keyword argument to the ``.commit()`` and ``.rollback()`` + methods of the DBAPI connection, which can improve performance in some + situations, but apparently with significant caveats. + Please read the fdb and/or kinterbasdb DBAPI documentation in order to + understand the implications of this flag. + + .. versionadded:: 0.8.2 - ``retaining`` keyword argument specifying + transaction retaining behavior - in 0.8 it defaults to ``True`` + for backwards compatibility. + + .. versionchanged:: 0.9.0 - the ``retaining`` flag defaults to ``False``. + In 0.8 it defaulted to ``True``. + + .. seealso:: + + http://pythonhosted.org/fdb/usage-guide.html#retaining-transactions + - information on the "retaining" flag. + +""" + +from .kinterbasdb import FBDialect_kinterbasdb +from ... import util + + +class FBDialect_fdb(FBDialect_kinterbasdb): + + def __init__(self, enable_rowcount=True, + retaining=False, **kwargs): + super(FBDialect_fdb, self).__init__( + enable_rowcount=enable_rowcount, + retaining=retaining, **kwargs) + + @classmethod + def dbapi(cls): + return __import__('fdb') + + def create_connect_args(self, url): + opts = url.translate_connect_args(username='user') + if opts.get('port'): + opts['host'] = "%s/%s" % (opts['host'], opts['port']) + del opts['port'] + opts.update(url.query) + + util.coerce_kw_type(opts, 'type_conv', int) + + return ([], opts) + + def _get_server_version_info(self, connection): + """Get the version of the Firebird server used by a connection. + + Returns a tuple of (`major`, `minor`, `build`), three integers + representing the version of the attached server. + """ + + # This is the simpler approach (the other uses the services api), + # that for backward compatibility reasons returns a string like + # LI-V6.3.3.12981 Firebird 2.0 + # where the first version is a fake one resembling the old + # Interbase signature. + + isc_info_firebird_version = 103 + fbconn = connection.connection + + version = fbconn.db_info(isc_info_firebird_version) + + return self._parse_version_info(version) + +dialect = FBDialect_fdb diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/firebird/kinterbasdb.py b/venv/Lib/site-packages/sqlalchemy/dialects/firebird/kinterbasdb.py new file mode 100644 index 0000000..dc88fc8 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/firebird/kinterbasdb.py @@ -0,0 +1,184 @@ +# firebird/kinterbasdb.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: firebird+kinterbasdb + :name: kinterbasdb + :dbapi: kinterbasdb + :connectstring: firebird+kinterbasdb://user:password@host:port/path/to/db\ +[?key=value&key=value...] + :url: http://firebirdsql.org/index.php?op=devel&sub=python + +Arguments +---------- + +The Kinterbasdb backend accepts the ``enable_rowcount`` and ``retaining`` +arguments accepted by the :mod:`sqlalchemy.dialects.firebird.fdb` dialect. +In addition, it also accepts the following: + +* ``type_conv`` - select the kind of mapping done on the types: by default + SQLAlchemy uses 200 with Unicode, datetime and decimal support. See + the linked documents below for further information. + +* ``concurrency_level`` - set the backend policy with regards to threading + issues: by default SQLAlchemy uses policy 1. See the linked documents + below for further information. + +.. seealso:: + + http://sourceforge.net/projects/kinterbasdb + + http://kinterbasdb.sourceforge.net/dist_docs/usage.html#adv_param_conv_dynamic_type_translation + + http://kinterbasdb.sourceforge.net/dist_docs/usage.html#special_issue_concurrency + +""" + +from .base import FBDialect, FBExecutionContext +from ... import util, types as sqltypes +from re import match +import decimal + + +class _kinterbasdb_numeric(object): + def bind_processor(self, dialect): + def process(value): + if isinstance(value, decimal.Decimal): + return str(value) + else: + return value + return process + + +class _FBNumeric_kinterbasdb(_kinterbasdb_numeric, sqltypes.Numeric): + pass + + +class _FBFloat_kinterbasdb(_kinterbasdb_numeric, sqltypes.Float): + pass + + +class FBExecutionContext_kinterbasdb(FBExecutionContext): + @property + def rowcount(self): + if self.execution_options.get('enable_rowcount', + self.dialect.enable_rowcount): + return self.cursor.rowcount + else: + return -1 + + +class FBDialect_kinterbasdb(FBDialect): + driver = 'kinterbasdb' + supports_sane_rowcount = False + supports_sane_multi_rowcount = False + execution_ctx_cls = FBExecutionContext_kinterbasdb + + supports_native_decimal = True + + colspecs = util.update_copy( + FBDialect.colspecs, + { + sqltypes.Numeric: _FBNumeric_kinterbasdb, + sqltypes.Float: _FBFloat_kinterbasdb, + } + + ) + + def __init__(self, type_conv=200, concurrency_level=1, + enable_rowcount=True, + retaining=False, **kwargs): + super(FBDialect_kinterbasdb, self).__init__(**kwargs) + self.enable_rowcount = enable_rowcount + self.type_conv = type_conv + self.concurrency_level = concurrency_level + self.retaining = retaining + if enable_rowcount: + self.supports_sane_rowcount = True + + @classmethod + def dbapi(cls): + return __import__('kinterbasdb') + + def do_execute(self, cursor, statement, parameters, context=None): + # kinterbase does not accept a None, but wants an empty list + # when there are no arguments. + cursor.execute(statement, parameters or []) + + def do_rollback(self, dbapi_connection): + dbapi_connection.rollback(self.retaining) + + def do_commit(self, dbapi_connection): + dbapi_connection.commit(self.retaining) + + def create_connect_args(self, url): + opts = url.translate_connect_args(username='user') + if opts.get('port'): + opts['host'] = "%s/%s" % (opts['host'], opts['port']) + del opts['port'] + opts.update(url.query) + + util.coerce_kw_type(opts, 'type_conv', int) + + type_conv = opts.pop('type_conv', self.type_conv) + concurrency_level = opts.pop('concurrency_level', + self.concurrency_level) + + if self.dbapi is not None: + initialized = getattr(self.dbapi, 'initialized', None) + if initialized is None: + # CVS rev 1.96 changed the name of the attribute: + # http://kinterbasdb.cvs.sourceforge.net/viewvc/kinterbasdb/ + # Kinterbasdb-3.0/__init__.py?r1=1.95&r2=1.96 + initialized = getattr(self.dbapi, '_initialized', False) + if not initialized: + self.dbapi.init(type_conv=type_conv, + concurrency_level=concurrency_level) + return ([], opts) + + def _get_server_version_info(self, connection): + """Get the version of the Firebird server used by a connection. + + Returns a tuple of (`major`, `minor`, `build`), three integers + representing the version of the attached server. + """ + + # This is the simpler approach (the other uses the services api), + # that for backward compatibility reasons returns a string like + # LI-V6.3.3.12981 Firebird 2.0 + # where the first version is a fake one resembling the old + # Interbase signature. + + fbconn = connection.connection + version = fbconn.server_version + + return self._parse_version_info(version) + + def _parse_version_info(self, version): + m = match( + r'\w+-V(\d+)\.(\d+)\.(\d+)\.(\d+)( \w+ (\d+)\.(\d+))?', version) + if not m: + raise AssertionError( + "Could not determine version from string '%s'" % version) + + if m.group(5) != None: + return tuple([int(x) for x in m.group(6, 7, 4)] + ['firebird']) + else: + return tuple([int(x) for x in m.group(1, 2, 3)] + ['interbase']) + + def is_disconnect(self, e, connection, cursor): + if isinstance(e, (self.dbapi.OperationalError, + self.dbapi.ProgrammingError)): + msg = str(e) + return ('Unable to complete network request to host' in msg or + 'Invalid connection state' in msg or + 'Invalid cursor state' in msg or + 'connection shutdown' in msg) + else: + return False + +dialect = FBDialect_kinterbasdb diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mssql/__init__.py b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/__init__.py new file mode 100644 index 0000000..9c861e8 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/__init__.py @@ -0,0 +1,26 @@ +# mssql/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from . import base, pyodbc, adodbapi, pymssql, zxjdbc, mxodbc # noqa + +from .base import \ + INTEGER, BIGINT, SMALLINT, TINYINT, VARCHAR, NVARCHAR, CHAR, \ + NCHAR, TEXT, NTEXT, DECIMAL, NUMERIC, FLOAT, DATETIME,\ + DATETIME2, DATETIMEOFFSET, DATE, TIME, SMALLDATETIME, \ + BINARY, VARBINARY, BIT, REAL, IMAGE, TIMESTAMP, ROWVERSION, \ + MONEY, SMALLMONEY, UNIQUEIDENTIFIER, SQL_VARIANT, XML + +base.dialect = dialect = pyodbc.dialect + + +__all__ = ( + 'INTEGER', 'BIGINT', 'SMALLINT', 'TINYINT', 'VARCHAR', 'NVARCHAR', 'CHAR', + 'NCHAR', 'TEXT', 'NTEXT', 'DECIMAL', 'NUMERIC', 'FLOAT', 'DATETIME', + 'DATETIME2', 'DATETIMEOFFSET', 'DATE', 'TIME', 'SMALLDATETIME', + 'BINARY', 'VARBINARY', 'BIT', 'REAL', 'IMAGE', 'TIMESTAMP', 'ROWVERSION', + 'MONEY', 'SMALLMONEY', 'UNIQUEIDENTIFIER', 'SQL_VARIANT', 'XML', 'dialect' +) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mssql/adodbapi.py b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/adodbapi.py new file mode 100644 index 0000000..e5bb9ba --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/adodbapi.py @@ -0,0 +1,87 @@ +# mssql/adodbapi.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: mssql+adodbapi + :name: adodbapi + :dbapi: adodbapi + :connectstring: mssql+adodbapi://<username>:<password>@<dsnname> + :url: http://adodbapi.sourceforge.net/ + +.. note:: + + The adodbapi dialect is not implemented SQLAlchemy versions 0.6 and + above at this time. + +""" +import datetime +from sqlalchemy import types as sqltypes, util +from sqlalchemy.dialects.mssql.base import MSDateTime, MSDialect +import sys + + +class MSDateTime_adodbapi(MSDateTime): + def result_processor(self, dialect, coltype): + def process(value): + # adodbapi will return datetimes with empty time + # values as datetime.date() objects. + # Promote them back to full datetime.datetime() + if type(value) is datetime.date: + return datetime.datetime(value.year, value.month, value.day) + return value + return process + + +class MSDialect_adodbapi(MSDialect): + supports_sane_rowcount = True + supports_sane_multi_rowcount = True + supports_unicode = sys.maxunicode == 65535 + supports_unicode_statements = True + driver = 'adodbapi' + + @classmethod + def import_dbapi(cls): + import adodbapi as module + return module + + colspecs = util.update_copy( + MSDialect.colspecs, + { + sqltypes.DateTime: MSDateTime_adodbapi + } + ) + + def create_connect_args(self, url): + def check_quote(token): + if ";" in str(token): + token = "'%s'" % token + return token + + keys = dict( + (k, check_quote(v)) for k, v in url.query.items() + ) + + connectors = ["Provider=SQLOLEDB"] + if 'port' in keys: + connectors.append("Data Source=%s, %s" % + (keys.get("host"), keys.get("port"))) + else: + connectors.append("Data Source=%s" % keys.get("host")) + connectors.append("Initial Catalog=%s" % keys.get("database")) + user = keys.get("user") + if user: + connectors.append("User Id=%s" % user) + connectors.append("Password=%s" % keys.get("password", "")) + else: + connectors.append("Integrated Security=SSPI") + return [[";".join(connectors)], {}] + + def is_disconnect(self, e, connection, cursor): + return isinstance(e, self.dbapi.adodbapi.DatabaseError) and \ + "'connection failure'" in str(e) + +dialect = MSDialect_adodbapi diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mssql/base.py b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/base.py new file mode 100644 index 0000000..c10b75c --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/base.py @@ -0,0 +1,2295 @@ +# mssql/base.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: mssql + :name: Microsoft SQL Server + + +Auto Increment Behavior +----------------------- + +SQL Server provides so-called "auto incrementing" behavior using the +``IDENTITY`` construct, which can be placed on an integer primary key. +SQLAlchemy considers ``IDENTITY`` within its default "autoincrement" behavior, +described at :paramref:`.Column.autoincrement`; this means +that by default, the first integer primary key column in a :class:`.Table` +will be considered to be the identity column and will generate DDL as such:: + + from sqlalchemy import Table, MetaData, Column, Integer + + m = MetaData() + t = Table('t', m, + Column('id', Integer, primary_key=True), + Column('x', Integer)) + m.create_all(engine) + +The above example will generate DDL as: + +.. sourcecode:: sql + + CREATE TABLE t ( + id INTEGER NOT NULL IDENTITY(1,1), + x INTEGER NULL, + PRIMARY KEY (id) + ) + +For the case where this default generation of ``IDENTITY`` is not desired, +specify ``autoincrement=False`` on all integer primary key columns:: + + m = MetaData() + t = Table('t', m, + Column('id', Integer, primary_key=True, autoincrement=False), + Column('x', Integer)) + m.create_all(engine) + +.. note:: + + An INSERT statement which refers to an explicit value for such + a column is prohibited by SQL Server, however SQLAlchemy will detect this + and modify the ``IDENTITY_INSERT`` flag accordingly at statement execution + time. As this is not a high performing process, care should be taken to + set the ``autoincrement`` flag appropriately for columns that will not + actually require IDENTITY behavior. + +Controlling "Start" and "Increment" +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Specific control over the parameters of the ``IDENTITY`` value is supported +using the :class:`.schema.Sequence` object. While this object normally +represents an explicit "sequence" for supporting backends, on SQL Server it is +re-purposed to specify behavior regarding the identity column, including +support of the "start" and "increment" values:: + + from sqlalchemy import Table, Integer, Sequence, Column + + Table('test', metadata, + Column('id', Integer, + Sequence('blah', start=100, increment=10), + primary_key=True), + Column('name', String(20)) + ).create(some_engine) + +would yield: + +.. sourcecode:: sql + + CREATE TABLE test ( + id INTEGER NOT NULL IDENTITY(100,10) PRIMARY KEY, + name VARCHAR(20) NULL, + ) + +Note that the ``start`` and ``increment`` values for sequences are +optional and will default to 1,1. + +INSERT behavior +^^^^^^^^^^^^^^^^ + +Handling of the ``IDENTITY`` column at INSERT time involves two key +techniques. The most common is being able to fetch the "last inserted value" +for a given ``IDENTITY`` column, a process which SQLAlchemy performs +implicitly in many cases, most importantly within the ORM. + +The process for fetching this value has several variants: + +* In the vast majority of cases, RETURNING is used in conjunction with INSERT + statements on SQL Server in order to get newly generated primary key values: + + .. sourcecode:: sql + + INSERT INTO t (x) OUTPUT inserted.id VALUES (?) + +* When RETURNING is not available or has been disabled via + ``implicit_returning=False``, either the ``scope_identity()`` function or + the ``@@identity`` variable is used; behavior varies by backend: + + * when using PyODBC, the phrase ``; select scope_identity()`` will be + appended to the end of the INSERT statement; a second result set will be + fetched in order to receive the value. Given a table as:: + + t = Table('t', m, Column('id', Integer, primary_key=True), + Column('x', Integer), + implicit_returning=False) + + an INSERT will look like: + + .. sourcecode:: sql + + INSERT INTO t (x) VALUES (?); select scope_identity() + + * Other dialects such as pymssql will call upon + ``SELECT scope_identity() AS lastrowid`` subsequent to an INSERT + statement. If the flag ``use_scope_identity=False`` is passed to + :func:`.create_engine`, the statement ``SELECT @@identity AS lastrowid`` + is used instead. + +A table that contains an ``IDENTITY`` column will prohibit an INSERT statement +that refers to the identity column explicitly. The SQLAlchemy dialect will +detect when an INSERT construct, created using a core :func:`.insert` +construct (not a plain string SQL), refers to the identity column, and +in this case will emit ``SET IDENTITY_INSERT ON`` prior to the insert +statement proceeding, and ``SET IDENTITY_INSERT OFF`` subsequent to the +execution. Given this example:: + + m = MetaData() + t = Table('t', m, Column('id', Integer, primary_key=True), + Column('x', Integer)) + m.create_all(engine) + + engine.execute(t.insert(), {'id': 1, 'x':1}, {'id':2, 'x':2}) + +The above column will be created with IDENTITY, however the INSERT statement +we emit is specifying explicit values. In the echo output we can see +how SQLAlchemy handles this: + +.. sourcecode:: sql + + CREATE TABLE t ( + id INTEGER NOT NULL IDENTITY(1,1), + x INTEGER NULL, + PRIMARY KEY (id) + ) + + COMMIT + SET IDENTITY_INSERT t ON + INSERT INTO t (id, x) VALUES (?, ?) + ((1, 1), (2, 2)) + SET IDENTITY_INSERT t OFF + COMMIT + + + +This +is an auxiliary use case suitable for testing and bulk insert scenarios. + +MAX on VARCHAR / NVARCHAR +------------------------- + +SQL Server supports the special string "MAX" within the +:class:`.sqltypes.VARCHAR` and :class:`.sqltypes.NVARCHAR` datatypes, +to indicate "maximum length possible". The dialect currently handles this as +a length of "None" in the base type, rather than supplying a +dialect-specific version of these types, so that a base type +specified such as ``VARCHAR(None)`` can assume "unlengthed" behavior on +more than one backend without using dialect-specific types. + +To build a SQL Server VARCHAR or NVARCHAR with MAX length, use None:: + + my_table = Table( + 'my_table', metadata, + Column('my_data', VARCHAR(None)), + Column('my_n_data', NVARCHAR(None)) + ) + + +Collation Support +----------------- + +Character collations are supported by the base string types, +specified by the string argument "collation":: + + from sqlalchemy import VARCHAR + Column('login', VARCHAR(32, collation='Latin1_General_CI_AS')) + +When such a column is associated with a :class:`.Table`, the +CREATE TABLE statement for this column will yield:: + + login VARCHAR(32) COLLATE Latin1_General_CI_AS NULL + +.. versionadded:: 0.8 Character collations are now part of the base string + types. + +LIMIT/OFFSET Support +-------------------- + +MSSQL has no support for the LIMIT or OFFSET keywords. LIMIT is +supported directly through the ``TOP`` Transact SQL keyword:: + + select.limit + +will yield:: + + SELECT TOP n + +If using SQL Server 2005 or above, LIMIT with OFFSET +support is available through the ``ROW_NUMBER OVER`` construct. +For versions below 2005, LIMIT with OFFSET usage will fail. + +.. _mssql_isolation_level: + +Transaction Isolation Level +--------------------------- + +All SQL Server dialects support setting of transaction isolation level +both via a dialect-specific parameter +:paramref:`.create_engine.isolation_level` +accepted by :func:`.create_engine`, +as well as the :paramref:`.Connection.execution_options.isolation_level` +argument as passed to +:meth:`.Connection.execution_options`. This feature works by issuing the +command ``SET TRANSACTION ISOLATION LEVEL <level>`` for +each new connection. + +To set isolation level using :func:`.create_engine`:: + + engine = create_engine( + "mssql+pyodbc://scott:tiger@ms_2008", + isolation_level="REPEATABLE READ" + ) + +To set using per-connection execution options:: + + connection = engine.connect() + connection = connection.execution_options( + isolation_level="READ COMMITTED" + ) + +Valid values for ``isolation_level`` include: + +* ``AUTOCOMMIT`` - pyodbc / pymssql-specific +* ``READ COMMITTED`` +* ``READ UNCOMMITTED`` +* ``REPEATABLE READ`` +* ``SERIALIZABLE`` +* ``SNAPSHOT`` - specific to SQL Server + +.. versionadded:: 1.1 support for isolation level setting on Microsoft + SQL Server. + +.. versionadded:: 1.2 added AUTOCOMMIT isolation level setting + +Nullability +----------- +MSSQL has support for three levels of column nullability. The default +nullability allows nulls and is explicit in the CREATE TABLE +construct:: + + name VARCHAR(20) NULL + +If ``nullable=None`` is specified then no specification is made. In +other words the database's configured default is used. This will +render:: + + name VARCHAR(20) + +If ``nullable`` is ``True`` or ``False`` then the column will be +``NULL`` or ``NOT NULL`` respectively. + +Date / Time Handling +-------------------- +DATE and TIME are supported. Bind parameters are converted +to datetime.datetime() objects as required by most MSSQL drivers, +and results are processed from strings if needed. +The DATE and TIME types are not available for MSSQL 2005 and +previous - if a server version below 2008 is detected, DDL +for these types will be issued as DATETIME. + +.. _mssql_large_type_deprecation: + +Large Text/Binary Type Deprecation +---------------------------------- + +Per `SQL Server 2012/2014 Documentation <http://technet.microsoft.com/en-us/library/ms187993.aspx>`_, +the ``NTEXT``, ``TEXT`` and ``IMAGE`` datatypes are to be removed from SQL Server +in a future release. SQLAlchemy normally relates these types to the +:class:`.UnicodeText`, :class:`.Text` and :class:`.LargeBinary` datatypes. + +In order to accommodate this change, a new flag ``deprecate_large_types`` +is added to the dialect, which will be automatically set based on detection +of the server version in use, if not otherwise set by the user. The +behavior of this flag is as follows: + +* When this flag is ``True``, the :class:`.UnicodeText`, :class:`.Text` and + :class:`.LargeBinary` datatypes, when used to render DDL, will render the + types ``NVARCHAR(max)``, ``VARCHAR(max)``, and ``VARBINARY(max)``, + respectively. This is a new behavior as of the addition of this flag. + +* When this flag is ``False``, the :class:`.UnicodeText`, :class:`.Text` and + :class:`.LargeBinary` datatypes, when used to render DDL, will render the + types ``NTEXT``, ``TEXT``, and ``IMAGE``, + respectively. This is the long-standing behavior of these types. + +* The flag begins with the value ``None``, before a database connection is + established. If the dialect is used to render DDL without the flag being + set, it is interpreted the same as ``False``. + +* On first connection, the dialect detects if SQL Server version 2012 or greater + is in use; if the flag is still at ``None``, it sets it to ``True`` or + ``False`` based on whether 2012 or greater is detected. + +* The flag can be set to either ``True`` or ``False`` when the dialect + is created, typically via :func:`.create_engine`:: + + eng = create_engine("mssql+pymssql://user:pass@host/db", + deprecate_large_types=True) + +* Complete control over whether the "old" or "new" types are rendered is + available in all SQLAlchemy versions by using the UPPERCASE type objects + instead: :class:`.NVARCHAR`, :class:`.VARCHAR`, :class:`.types.VARBINARY`, + :class:`.TEXT`, :class:`.mssql.NTEXT`, :class:`.mssql.IMAGE` will always remain + fixed and always output exactly that type. + +.. versionadded:: 1.0.0 + +.. _multipart_schema_names: + +Multipart Schema Names +---------------------- + +SQL Server schemas sometimes require multiple parts to their "schema" +qualifier, that is, including the database name and owner name as separate +tokens, such as ``mydatabase.dbo.some_table``. These multipart names can be set +at once using the :paramref:`.Table.schema` argument of :class:`.Table`:: + + Table( + "some_table", metadata, + Column("q", String(50)), + schema="mydatabase.dbo" + ) + +When performing operations such as table or component reflection, a schema +argument that contains a dot will be split into separate +"database" and "owner" components in order to correctly query the SQL +Server information schema tables, as these two values are stored separately. +Additionally, when rendering the schema name for DDL or SQL, the two +components will be quoted separately for case sensitive names and other +special characters. Given an argument as below:: + + Table( + "some_table", metadata, + Column("q", String(50)), + schema="MyDataBase.dbo" + ) + +The above schema would be rendered as ``[MyDataBase].dbo``, and also in +reflection, would be reflected using "dbo" as the owner and "MyDataBase" +as the database name. + +To control how the schema name is broken into database / owner, +specify brackets (which in SQL Server are quoting characters) in the name. +Below, the "owner" will be considered as ``MyDataBase.dbo`` and the +"database" will be None:: + + Table( + "some_table", metadata, + Column("q", String(50)), + schema="[MyDataBase.dbo]" + ) + +To individually specify both database and owner name with special characters +or embedded dots, use two sets of brackets:: + + Table( + "some_table", metadata, + Column("q", String(50)), + schema="[MyDataBase.Period].[MyOwner.Dot]" + ) + + +.. versionchanged:: 1.2 the SQL Server dialect now treats brackets as + identifier delimeters splitting the schema into separate database + and owner tokens, to allow dots within either name itself. + +.. _legacy_schema_rendering: + +Legacy Schema Mode +------------------ + +Very old versions of the MSSQL dialect introduced the behavior such that a +schema-qualified table would be auto-aliased when used in a +SELECT statement; given a table:: + + account_table = Table( + 'account', metadata, + Column('id', Integer, primary_key=True), + Column('info', String(100)), + schema="customer_schema" + ) + +this legacy mode of rendering would assume that "customer_schema.account" +would not be accepted by all parts of the SQL statement, as illustrated +below:: + + >>> eng = create_engine("mssql+pymssql://mydsn", legacy_schema_aliasing=True) + >>> print(account_table.select().compile(eng)) + SELECT account_1.id, account_1.info + FROM customer_schema.account AS account_1 + +This mode of behavior is now off by default, as it appears to have served +no purpose; however in the case that legacy applications rely upon it, +it is available using the ``legacy_schema_aliasing`` argument to +:func:`.create_engine` as illustrated above. + +.. versionchanged:: 1.1 the ``legacy_schema_aliasing`` flag introduced + in version 1.0.5 to allow disabling of legacy mode for schemas now + defaults to False. + + +.. _mssql_indexes: + +Clustered Index Support +----------------------- + +The MSSQL dialect supports clustered indexes (and primary keys) via the +``mssql_clustered`` option. This option is available to :class:`.Index`, +:class:`.UniqueConstraint`. and :class:`.PrimaryKeyConstraint`. + +To generate a clustered index:: + + Index("my_index", table.c.x, mssql_clustered=True) + +which renders the index as ``CREATE CLUSTERED INDEX my_index ON table (x)``. + +To generate a clustered primary key use:: + + Table('my_table', metadata, + Column('x', ...), + Column('y', ...), + PrimaryKeyConstraint("x", "y", mssql_clustered=True)) + +which will render the table, for example, as:: + + CREATE TABLE my_table (x INTEGER NOT NULL, y INTEGER NOT NULL, + PRIMARY KEY CLUSTERED (x, y)) + +Similarly, we can generate a clustered unique constraint using:: + + Table('my_table', metadata, + Column('x', ...), + Column('y', ...), + PrimaryKeyConstraint("x"), + UniqueConstraint("y", mssql_clustered=True), + ) + +To explicitly request a non-clustered primary key (for example, when +a separate clustered index is desired), use:: + + Table('my_table', metadata, + Column('x', ...), + Column('y', ...), + PrimaryKeyConstraint("x", "y", mssql_clustered=False)) + +which will render the table, for example, as:: + + CREATE TABLE my_table (x INTEGER NOT NULL, y INTEGER NOT NULL, + PRIMARY KEY NONCLUSTERED (x, y)) + +.. versionchanged:: 1.1 the ``mssql_clustered`` option now defaults + to None, rather than False. ``mssql_clustered=False`` now explicitly + renders the NONCLUSTERED clause, whereas None omits the CLUSTERED + clause entirely, allowing SQL Server defaults to take effect. + + +MSSQL-Specific Index Options +----------------------------- + +In addition to clustering, the MSSQL dialect supports other special options +for :class:`.Index`. + +INCLUDE +^^^^^^^ + +The ``mssql_include`` option renders INCLUDE(colname) for the given string +names:: + + Index("my_index", table.c.x, mssql_include=['y']) + +would render the index as ``CREATE INDEX my_index ON table (x) INCLUDE (y)`` + +.. versionadded:: 0.8 + +Index ordering +^^^^^^^^^^^^^^ + +Index ordering is available via functional expressions, such as:: + + Index("my_index", table.c.x.desc()) + +would render the index as ``CREATE INDEX my_index ON table (x DESC)`` + +.. versionadded:: 0.8 + +.. seealso:: + + :ref:`schema_indexes_functional` + +Compatibility Levels +-------------------- +MSSQL supports the notion of setting compatibility levels at the +database level. This allows, for instance, to run a database that +is compatible with SQL2000 while running on a SQL2005 database +server. ``server_version_info`` will always return the database +server version information (in this case SQL2005) and not the +compatibility level information. Because of this, if running under +a backwards compatibility mode SQAlchemy may attempt to use T-SQL +statements that are unable to be parsed by the database server. + +Triggers +-------- + +SQLAlchemy by default uses OUTPUT INSERTED to get at newly +generated primary key values via IDENTITY columns or other +server side defaults. MS-SQL does not +allow the usage of OUTPUT INSERTED on tables that have triggers. +To disable the usage of OUTPUT INSERTED on a per-table basis, +specify ``implicit_returning=False`` for each :class:`.Table` +which has triggers:: + + Table('mytable', metadata, + Column('id', Integer, primary_key=True), + # ..., + implicit_returning=False + ) + +Declarative form:: + + class MyClass(Base): + # ... + __table_args__ = {'implicit_returning':False} + + +This option can also be specified engine-wide using the +``implicit_returning=False`` argument on :func:`.create_engine`. + +.. _mssql_rowcount_versioning: + +Rowcount Support / ORM Versioning +--------------------------------- + +The SQL Server drivers may have limited ability to return the number +of rows updated from an UPDATE or DELETE statement. + +As of this writing, the PyODBC driver is not able to return a rowcount when +OUTPUT INSERTED is used. This impacts the SQLAlchemy ORM's versioning feature +in many cases where server-side value generators are in use in that while the +versioning operations can succeed, the ORM cannot always check that an UPDATE +or DELETE statement matched the number of rows expected, which is how it +verifies that the version identifier matched. When this condition occurs, a +warning will be emitted but the operation will proceed. + +The use of OUTPUT INSERTED can be disabled by setting the +:paramref:`.Table.implicit_returning` flag to ``False`` on a particular +:class:`.Table`, which in declarative looks like:: + + class MyTable(Base): + __tablename__ = 'mytable' + id = Column(Integer, primary_key=True) + stuff = Column(String(10)) + timestamp = Column(TIMESTAMP(), default=text('DEFAULT')) + __mapper_args__ = { + 'version_id_col': timestamp, + 'version_id_generator': False, + } + __table_args__ = { + 'implicit_returning': False + } + +Enabling Snapshot Isolation +--------------------------- + +SQL Server has a default transaction +isolation mode that locks entire tables, and causes even mildly concurrent +applications to have long held locks and frequent deadlocks. +Enabling snapshot isolation for the database as a whole is recommended +for modern levels of concurrency support. This is accomplished via the +following ALTER DATABASE commands executed at the SQL prompt:: + + ALTER DATABASE MyDatabase SET ALLOW_SNAPSHOT_ISOLATION ON + + ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON + +Background on SQL Server snapshot isolation is available at +http://msdn.microsoft.com/en-us/library/ms175095.aspx. + + +""" +import codecs +import datetime +import operator +import re + +from ... import sql, schema as sa_schema, exc, util +from ...sql import compiler, expression, util as sql_util, quoted_name +from ... import engine +from ...engine import reflection, default +from ... import types as sqltypes +from ...types import INTEGER, BIGINT, SMALLINT, DECIMAL, NUMERIC, \ + FLOAT, DATETIME, DATE, BINARY, \ + TEXT, VARCHAR, NVARCHAR, CHAR, NCHAR + + +from ...util import update_wrapper +from . import information_schema as ischema + +# http://sqlserverbuilds.blogspot.com/ +MS_2016_VERSION = (13,) +MS_2014_VERSION = (12,) +MS_2012_VERSION = (11,) +MS_2008_VERSION = (10,) +MS_2005_VERSION = (9,) +MS_2000_VERSION = (8,) + +RESERVED_WORDS = set( + ['add', 'all', 'alter', 'and', 'any', 'as', 'asc', 'authorization', + 'backup', 'begin', 'between', 'break', 'browse', 'bulk', 'by', 'cascade', + 'case', 'check', 'checkpoint', 'close', 'clustered', 'coalesce', + 'collate', 'column', 'commit', 'compute', 'constraint', 'contains', + 'containstable', 'continue', 'convert', 'create', 'cross', 'current', + 'current_date', 'current_time', 'current_timestamp', 'current_user', + 'cursor', 'database', 'dbcc', 'deallocate', 'declare', 'default', + 'delete', 'deny', 'desc', 'disk', 'distinct', 'distributed', 'double', + 'drop', 'dump', 'else', 'end', 'errlvl', 'escape', 'except', 'exec', + 'execute', 'exists', 'exit', 'external', 'fetch', 'file', 'fillfactor', + 'for', 'foreign', 'freetext', 'freetexttable', 'from', 'full', + 'function', 'goto', 'grant', 'group', 'having', 'holdlock', 'identity', + 'identity_insert', 'identitycol', 'if', 'in', 'index', 'inner', 'insert', + 'intersect', 'into', 'is', 'join', 'key', 'kill', 'left', 'like', + 'lineno', 'load', 'merge', 'national', 'nocheck', 'nonclustered', 'not', + 'null', 'nullif', 'of', 'off', 'offsets', 'on', 'open', 'opendatasource', + 'openquery', 'openrowset', 'openxml', 'option', 'or', 'order', 'outer', + 'over', 'percent', 'pivot', 'plan', 'precision', 'primary', 'print', + 'proc', 'procedure', 'public', 'raiserror', 'read', 'readtext', + 'reconfigure', 'references', 'replication', 'restore', 'restrict', + 'return', 'revert', 'revoke', 'right', 'rollback', 'rowcount', + 'rowguidcol', 'rule', 'save', 'schema', 'securityaudit', 'select', + 'session_user', 'set', 'setuser', 'shutdown', 'some', 'statistics', + 'system_user', 'table', 'tablesample', 'textsize', 'then', 'to', 'top', + 'tran', 'transaction', 'trigger', 'truncate', 'tsequal', 'union', + 'unique', 'unpivot', 'update', 'updatetext', 'use', 'user', 'values', + 'varying', 'view', 'waitfor', 'when', 'where', 'while', 'with', + 'writetext', + ]) + + +class REAL(sqltypes.REAL): + __visit_name__ = 'REAL' + + def __init__(self, **kw): + # REAL is a synonym for FLOAT(24) on SQL server + kw['precision'] = 24 + super(REAL, self).__init__(**kw) + + +class TINYINT(sqltypes.Integer): + __visit_name__ = 'TINYINT' + + +# MSSQL DATE/TIME types have varied behavior, sometimes returning +# strings. MSDate/TIME check for everything, and always +# filter bind parameters into datetime objects (required by pyodbc, +# not sure about other dialects). + +class _MSDate(sqltypes.Date): + + def bind_processor(self, dialect): + def process(value): + if type(value) == datetime.date: + return datetime.datetime(value.year, value.month, value.day) + else: + return value + return process + + _reg = re.compile(r"(\d+)-(\d+)-(\d+)") + + def result_processor(self, dialect, coltype): + def process(value): + if isinstance(value, datetime.datetime): + return value.date() + elif isinstance(value, util.string_types): + m = self._reg.match(value) + if not m: + raise ValueError( + "could not parse %r as a date value" % (value, )) + return datetime.date(*[ + int(x or 0) + for x in m.groups() + ]) + else: + return value + return process + + +class TIME(sqltypes.TIME): + + def __init__(self, precision=None, **kwargs): + self.precision = precision + super(TIME, self).__init__() + + __zero_date = datetime.date(1900, 1, 1) + + def bind_processor(self, dialect): + def process(value): + if isinstance(value, datetime.datetime): + value = datetime.datetime.combine( + self.__zero_date, value.time()) + elif isinstance(value, datetime.time): + value = datetime.datetime.combine(self.__zero_date, value) + return value + return process + + _reg = re.compile(r"(\d+):(\d+):(\d+)(?:\.(\d{0,6}))?") + + def result_processor(self, dialect, coltype): + def process(value): + if isinstance(value, datetime.datetime): + return value.time() + elif isinstance(value, util.string_types): + m = self._reg.match(value) + if not m: + raise ValueError( + "could not parse %r as a time value" % (value, )) + return datetime.time(*[ + int(x or 0) + for x in m.groups()]) + else: + return value + return process +_MSTime = TIME + + +class _DateTimeBase(object): + + def bind_processor(self, dialect): + def process(value): + if type(value) == datetime.date: + return datetime.datetime(value.year, value.month, value.day) + else: + return value + return process + + +class _MSDateTime(_DateTimeBase, sqltypes.DateTime): + pass + + +class SMALLDATETIME(_DateTimeBase, sqltypes.DateTime): + __visit_name__ = 'SMALLDATETIME' + + +class DATETIME2(_DateTimeBase, sqltypes.DateTime): + __visit_name__ = 'DATETIME2' + + def __init__(self, precision=None, **kw): + super(DATETIME2, self).__init__(**kw) + self.precision = precision + + +# TODO: is this not an Interval ? +class DATETIMEOFFSET(sqltypes.TypeEngine): + __visit_name__ = 'DATETIMEOFFSET' + + def __init__(self, precision=None, **kwargs): + self.precision = precision + + +class _StringType(object): + + """Base for MSSQL string types.""" + + def __init__(self, collation=None): + super(_StringType, self).__init__(collation=collation) + + +class TIMESTAMP(sqltypes._Binary): + """Implement the SQL Server TIMESTAMP type. + + Note this is **completely different** than the SQL Standard + TIMESTAMP type, which is not supported by SQL Server. It + is a read-only datatype that does not support INSERT of values. + + .. versionadded:: 1.2 + + .. seealso:: + + :class:`.mssql.ROWVERSION` + + """ + + __visit_name__ = 'TIMESTAMP' + + # expected by _Binary to be present + length = None + + def __init__(self, convert_int=False): + """Construct a TIMESTAMP or ROWVERSION type. + + :param convert_int: if True, binary integer values will + be converted to integers on read. + + .. versionadded:: 1.2 + + """ + self.convert_int = convert_int + + def result_processor(self, dialect, coltype): + super_ = super(TIMESTAMP, self).result_processor(dialect, coltype) + if self.convert_int: + def process(value): + value = super_(value) + if value is not None: + # https://stackoverflow.com/a/30403242/34549 + value = int(codecs.encode(value, 'hex'), 16) + return value + return process + else: + return super_ + + +class ROWVERSION(TIMESTAMP): + """Implement the SQL Server ROWVERSION type. + + The ROWVERSION datatype is a SQL Server synonym for the TIMESTAMP + datatype, however current SQL Server documentation suggests using + ROWVERSION for new datatypes going forward. + + The ROWVERSION datatype does **not** reflect (e.g. introspect) from the + database as itself; the returned datatype will be + :class:`.mssql.TIMESTAMP`. + + This is a read-only datatype that does not support INSERT of values. + + .. versionadded:: 1.2 + + .. seealso:: + + :class:`.mssql.TIMESTAMP` + + """ + + __visit_name__ = 'ROWVERSION' + + +class NTEXT(sqltypes.UnicodeText): + + """MSSQL NTEXT type, for variable-length unicode text up to 2^30 + characters.""" + + __visit_name__ = 'NTEXT' + + +class VARBINARY(sqltypes.VARBINARY, sqltypes.LargeBinary): + """The MSSQL VARBINARY type. + + This type is present to support "deprecate_large_types" mode where + either ``VARBINARY(max)`` or IMAGE is rendered. Otherwise, this type + object is redundant vs. :class:`.types.VARBINARY`. + + .. versionadded:: 1.0.0 + + .. seealso:: + + :ref:`mssql_large_type_deprecation` + + + + """ + __visit_name__ = 'VARBINARY' + + +class IMAGE(sqltypes.LargeBinary): + __visit_name__ = 'IMAGE' + + +class XML(sqltypes.Text): + """MSSQL XML type. + + This is a placeholder type for reflection purposes that does not include + any Python-side datatype support. It also does not currently support + additional arguments, such as "CONTENT", "DOCUMENT", + "xml_schema_collection". + + .. versionadded:: 1.1.11 + + """ + __visit_name__ = 'XML' + + +class BIT(sqltypes.TypeEngine): + __visit_name__ = 'BIT' + + +class MONEY(sqltypes.TypeEngine): + __visit_name__ = 'MONEY' + + +class SMALLMONEY(sqltypes.TypeEngine): + __visit_name__ = 'SMALLMONEY' + + +class UNIQUEIDENTIFIER(sqltypes.TypeEngine): + __visit_name__ = "UNIQUEIDENTIFIER" + + +class SQL_VARIANT(sqltypes.TypeEngine): + __visit_name__ = 'SQL_VARIANT' + +# old names. +MSDateTime = _MSDateTime +MSDate = _MSDate +MSReal = REAL +MSTinyInteger = TINYINT +MSTime = TIME +MSSmallDateTime = SMALLDATETIME +MSDateTime2 = DATETIME2 +MSDateTimeOffset = DATETIMEOFFSET +MSText = TEXT +MSNText = NTEXT +MSString = VARCHAR +MSNVarchar = NVARCHAR +MSChar = CHAR +MSNChar = NCHAR +MSBinary = BINARY +MSVarBinary = VARBINARY +MSImage = IMAGE +MSBit = BIT +MSMoney = MONEY +MSSmallMoney = SMALLMONEY +MSUniqueIdentifier = UNIQUEIDENTIFIER +MSVariant = SQL_VARIANT + +ischema_names = { + 'int': INTEGER, + 'bigint': BIGINT, + 'smallint': SMALLINT, + 'tinyint': TINYINT, + 'varchar': VARCHAR, + 'nvarchar': NVARCHAR, + 'char': CHAR, + 'nchar': NCHAR, + 'text': TEXT, + 'ntext': NTEXT, + 'decimal': DECIMAL, + 'numeric': NUMERIC, + 'float': FLOAT, + 'datetime': DATETIME, + 'datetime2': DATETIME2, + 'datetimeoffset': DATETIMEOFFSET, + 'date': DATE, + 'time': TIME, + 'smalldatetime': SMALLDATETIME, + 'binary': BINARY, + 'varbinary': VARBINARY, + 'bit': BIT, + 'real': REAL, + 'image': IMAGE, + 'xml': XML, + 'timestamp': TIMESTAMP, + 'money': MONEY, + 'smallmoney': SMALLMONEY, + 'uniqueidentifier': UNIQUEIDENTIFIER, + 'sql_variant': SQL_VARIANT, +} + + +class MSTypeCompiler(compiler.GenericTypeCompiler): + def _extend(self, spec, type_, length=None): + """Extend a string-type declaration with standard SQL + COLLATE annotations. + + """ + + if getattr(type_, 'collation', None): + collation = 'COLLATE %s' % type_.collation + else: + collation = None + + if not length: + length = type_.length + + if length: + spec = spec + "(%s)" % length + + return ' '.join([c for c in (spec, collation) + if c is not None]) + + def visit_FLOAT(self, type_, **kw): + precision = getattr(type_, 'precision', None) + if precision is None: + return "FLOAT" + else: + return "FLOAT(%(precision)s)" % {'precision': precision} + + def visit_TINYINT(self, type_, **kw): + return "TINYINT" + + def visit_DATETIMEOFFSET(self, type_, **kw): + if type_.precision is not None: + return "DATETIMEOFFSET(%s)" % type_.precision + else: + return "DATETIMEOFFSET" + + def visit_TIME(self, type_, **kw): + precision = getattr(type_, 'precision', None) + if precision is not None: + return "TIME(%s)" % precision + else: + return "TIME" + + def visit_TIMESTAMP(self, type_, **kw): + return "TIMESTAMP" + + def visit_ROWVERSION(self, type_, **kw): + return "ROWVERSION" + + def visit_DATETIME2(self, type_, **kw): + precision = getattr(type_, 'precision', None) + if precision is not None: + return "DATETIME2(%s)" % precision + else: + return "DATETIME2" + + def visit_SMALLDATETIME(self, type_, **kw): + return "SMALLDATETIME" + + def visit_unicode(self, type_, **kw): + return self.visit_NVARCHAR(type_, **kw) + + def visit_text(self, type_, **kw): + if self.dialect.deprecate_large_types: + return self.visit_VARCHAR(type_, **kw) + else: + return self.visit_TEXT(type_, **kw) + + def visit_unicode_text(self, type_, **kw): + if self.dialect.deprecate_large_types: + return self.visit_NVARCHAR(type_, **kw) + else: + return self.visit_NTEXT(type_, **kw) + + def visit_NTEXT(self, type_, **kw): + return self._extend("NTEXT", type_) + + def visit_TEXT(self, type_, **kw): + return self._extend("TEXT", type_) + + def visit_VARCHAR(self, type_, **kw): + return self._extend("VARCHAR", type_, length=type_.length or 'max') + + def visit_CHAR(self, type_, **kw): + return self._extend("CHAR", type_) + + def visit_NCHAR(self, type_, **kw): + return self._extend("NCHAR", type_) + + def visit_NVARCHAR(self, type_, **kw): + return self._extend("NVARCHAR", type_, length=type_.length or 'max') + + def visit_date(self, type_, **kw): + if self.dialect.server_version_info < MS_2008_VERSION: + return self.visit_DATETIME(type_, **kw) + else: + return self.visit_DATE(type_, **kw) + + def visit_time(self, type_, **kw): + if self.dialect.server_version_info < MS_2008_VERSION: + return self.visit_DATETIME(type_, **kw) + else: + return self.visit_TIME(type_, **kw) + + def visit_large_binary(self, type_, **kw): + if self.dialect.deprecate_large_types: + return self.visit_VARBINARY(type_, **kw) + else: + return self.visit_IMAGE(type_, **kw) + + def visit_IMAGE(self, type_, **kw): + return "IMAGE" + + def visit_XML(self, type_, **kw): + return "XML" + + def visit_VARBINARY(self, type_, **kw): + return self._extend( + "VARBINARY", + type_, + length=type_.length or 'max') + + def visit_boolean(self, type_, **kw): + return self.visit_BIT(type_) + + def visit_BIT(self, type_, **kw): + return "BIT" + + def visit_MONEY(self, type_, **kw): + return "MONEY" + + def visit_SMALLMONEY(self, type_, **kw): + return 'SMALLMONEY' + + def visit_UNIQUEIDENTIFIER(self, type_, **kw): + return "UNIQUEIDENTIFIER" + + def visit_SQL_VARIANT(self, type_, **kw): + return 'SQL_VARIANT' + + +class MSExecutionContext(default.DefaultExecutionContext): + _enable_identity_insert = False + _select_lastrowid = False + _result_proxy = None + _lastrowid = None + + def _opt_encode(self, statement): + if not self.dialect.supports_unicode_statements: + return self.dialect._encoder(statement)[0] + else: + return statement + + def pre_exec(self): + """Activate IDENTITY_INSERT if needed.""" + + if self.isinsert: + tbl = self.compiled.statement.table + seq_column = tbl._autoincrement_column + insert_has_sequence = seq_column is not None + + if insert_has_sequence: + self._enable_identity_insert = \ + seq_column.key in self.compiled_parameters[0] or \ + ( + self.compiled.statement.parameters and ( + ( + self.compiled.statement._has_multi_parameters + and + seq_column.key in + self.compiled.statement.parameters[0] + ) or ( + not + self.compiled.statement._has_multi_parameters + and + seq_column.key in + self.compiled.statement.parameters + ) + ) + ) + else: + self._enable_identity_insert = False + + self._select_lastrowid = not self.compiled.inline and \ + insert_has_sequence and \ + not self.compiled.returning and \ + not self._enable_identity_insert and \ + not self.executemany + + if self._enable_identity_insert: + self.root_connection._cursor_execute( + self.cursor, + self._opt_encode( + "SET IDENTITY_INSERT %s ON" % + self.dialect.identifier_preparer.format_table(tbl)), + (), + self) + + def post_exec(self): + """Disable IDENTITY_INSERT if enabled.""" + + conn = self.root_connection + if self._select_lastrowid: + if self.dialect.use_scope_identity: + conn._cursor_execute( + self.cursor, + "SELECT scope_identity() AS lastrowid", (), self) + else: + conn._cursor_execute(self.cursor, + "SELECT @@identity AS lastrowid", + (), + self) + # fetchall() ensures the cursor is consumed without closing it + row = self.cursor.fetchall()[0] + self._lastrowid = int(row[0]) + + if (self.isinsert or self.isupdate or self.isdelete) and \ + self.compiled.returning: + self._result_proxy = engine.FullyBufferedResultProxy(self) + + if self._enable_identity_insert: + conn._cursor_execute( + self.cursor, + self._opt_encode( + "SET IDENTITY_INSERT %s OFF" % + self.dialect.identifier_preparer. format_table( + self.compiled.statement.table)), + (), + self) + + def get_lastrowid(self): + return self._lastrowid + + def handle_dbapi_exception(self, e): + if self._enable_identity_insert: + try: + self.cursor.execute( + self._opt_encode( + "SET IDENTITY_INSERT %s OFF" % + self.dialect.identifier_preparer. format_table( + self.compiled.statement.table))) + except Exception: + pass + + def get_result_proxy(self): + if self._result_proxy: + return self._result_proxy + else: + return engine.ResultProxy(self) + + +class MSSQLCompiler(compiler.SQLCompiler): + returning_precedes_values = True + + extract_map = util.update_copy( + compiler.SQLCompiler.extract_map, + { + 'doy': 'dayofyear', + 'dow': 'weekday', + 'milliseconds': 'millisecond', + 'microseconds': 'microsecond' + }) + + def __init__(self, *args, **kwargs): + self.tablealiases = {} + super(MSSQLCompiler, self).__init__(*args, **kwargs) + + def _with_legacy_schema_aliasing(fn): + def decorate(self, *arg, **kw): + if self.dialect.legacy_schema_aliasing: + return fn(self, *arg, **kw) + else: + super_ = getattr(super(MSSQLCompiler, self), fn.__name__) + return super_(*arg, **kw) + return decorate + + def visit_now_func(self, fn, **kw): + return "CURRENT_TIMESTAMP" + + def visit_current_date_func(self, fn, **kw): + return "GETDATE()" + + def visit_length_func(self, fn, **kw): + return "LEN%s" % self.function_argspec(fn, **kw) + + def visit_char_length_func(self, fn, **kw): + return "LEN%s" % self.function_argspec(fn, **kw) + + def visit_concat_op_binary(self, binary, operator, **kw): + return "%s + %s" % \ + (self.process(binary.left, **kw), + self.process(binary.right, **kw)) + + def visit_true(self, expr, **kw): + return '1' + + def visit_false(self, expr, **kw): + return '0' + + def visit_match_op_binary(self, binary, operator, **kw): + return "CONTAINS (%s, %s)" % ( + self.process(binary.left, **kw), + self.process(binary.right, **kw)) + + def get_select_precolumns(self, select, **kw): + """ MS-SQL puts TOP, it's version of LIMIT here """ + + s = "" + if select._distinct: + s += "DISTINCT " + + if select._simple_int_limit and not select._offset: + # ODBC drivers and possibly others + # don't support bind params in the SELECT clause on SQL Server. + # so have to use literal here. + s += "TOP %d " % select._limit + + if s: + return s + else: + return compiler.SQLCompiler.get_select_precolumns( + self, select, **kw) + + def get_from_hint_text(self, table, text): + return text + + def get_crud_hint_text(self, table, text): + return text + + def limit_clause(self, select, **kw): + # Limit in mssql is after the select keyword + return "" + + def visit_select(self, select, **kwargs): + """Look for ``LIMIT`` and OFFSET in a select statement, and if + so tries to wrap it in a subquery with ``row_number()`` criterion. + + """ + if ( + ( + not select._simple_int_limit and + select._limit_clause is not None + ) or ( + select._offset_clause is not None and + not select._simple_int_offset or select._offset + ) + ) and not getattr(select, '_mssql_visit', None): + + # to use ROW_NUMBER(), an ORDER BY is required. + if not select._order_by_clause.clauses: + raise exc.CompileError('MSSQL requires an order_by when ' + 'using an OFFSET or a non-simple ' + 'LIMIT clause') + + _order_by_clauses = [ + sql_util.unwrap_label_reference(elem) + for elem in select._order_by_clause.clauses + ] + + limit_clause = select._limit_clause + offset_clause = select._offset_clause + kwargs['select_wraps_for'] = select + select = select._generate() + select._mssql_visit = True + select = select.column( + sql.func.ROW_NUMBER().over(order_by=_order_by_clauses) + .label("mssql_rn")).order_by(None).alias() + + mssql_rn = sql.column('mssql_rn') + limitselect = sql.select([c for c in select.c if + c.key != 'mssql_rn']) + if offset_clause is not None: + limitselect.append_whereclause(mssql_rn > offset_clause) + if limit_clause is not None: + limitselect.append_whereclause( + mssql_rn <= (limit_clause + offset_clause)) + else: + limitselect.append_whereclause( + mssql_rn <= (limit_clause)) + return self.process(limitselect, **kwargs) + else: + return compiler.SQLCompiler.visit_select(self, select, **kwargs) + + @_with_legacy_schema_aliasing + def visit_table(self, table, mssql_aliased=False, iscrud=False, **kwargs): + if mssql_aliased is table or iscrud: + return super(MSSQLCompiler, self).visit_table(table, **kwargs) + + # alias schema-qualified tables + alias = self._schema_aliased_table(table) + if alias is not None: + return self.process(alias, mssql_aliased=table, **kwargs) + else: + return super(MSSQLCompiler, self).visit_table(table, **kwargs) + + @_with_legacy_schema_aliasing + def visit_alias(self, alias, **kw): + # translate for schema-qualified table aliases + kw['mssql_aliased'] = alias.original + return super(MSSQLCompiler, self).visit_alias(alias, **kw) + + @_with_legacy_schema_aliasing + def visit_column(self, column, add_to_result_map=None, **kw): + if column.table is not None and \ + (not self.isupdate and not self.isdelete) or \ + self.is_subquery(): + # translate for schema-qualified table aliases + t = self._schema_aliased_table(column.table) + if t is not None: + converted = expression._corresponding_column_or_error( + t, column) + if add_to_result_map is not None: + add_to_result_map( + column.name, + column.name, + (column, column.name, column.key), + column.type + ) + + return super(MSSQLCompiler, self).\ + visit_column(converted, **kw) + + return super(MSSQLCompiler, self).visit_column( + column, add_to_result_map=add_to_result_map, **kw) + + def _schema_aliased_table(self, table): + if getattr(table, 'schema', None) is not None: + if table not in self.tablealiases: + self.tablealiases[table] = table.alias() + return self.tablealiases[table] + else: + return None + + def visit_extract(self, extract, **kw): + field = self.extract_map.get(extract.field, extract.field) + return 'DATEPART(%s, %s)' % \ + (field, self.process(extract.expr, **kw)) + + def visit_savepoint(self, savepoint_stmt): + return "SAVE TRANSACTION %s" % \ + self.preparer.format_savepoint(savepoint_stmt) + + def visit_rollback_to_savepoint(self, savepoint_stmt): + return ("ROLLBACK TRANSACTION %s" + % self.preparer.format_savepoint(savepoint_stmt)) + + def visit_binary(self, binary, **kwargs): + """Move bind parameters to the right-hand side of an operator, where + possible. + + """ + if ( + isinstance(binary.left, expression.BindParameter) + and binary.operator == operator.eq + and not isinstance(binary.right, expression.BindParameter) + ): + return self.process( + expression.BinaryExpression(binary.right, + binary.left, + binary.operator), + **kwargs) + return super(MSSQLCompiler, self).visit_binary(binary, **kwargs) + + def returning_clause(self, stmt, returning_cols): + + if self.isinsert or self.isupdate: + target = stmt.table.alias("inserted") + else: + target = stmt.table.alias("deleted") + + adapter = sql_util.ClauseAdapter(target) + + columns = [ + self._label_select_column(None, adapter.traverse(c), + True, False, {}) + for c in expression._select_iterables(returning_cols) + ] + + return 'OUTPUT ' + ', '.join(columns) + + def get_cte_preamble(self, recursive): + # SQL Server finds it too inconvenient to accept + # an entirely optional, SQL standard specified, + # "RECURSIVE" word with their "WITH", + # so here we go + return "WITH" + + def label_select_column(self, select, column, asfrom): + if isinstance(column, expression.Function): + return column.label(None) + else: + return super(MSSQLCompiler, self).\ + label_select_column(select, column, asfrom) + + def for_update_clause(self, select): + # "FOR UPDATE" is only allowed on "DECLARE CURSOR" which + # SQLAlchemy doesn't use + return '' + + def order_by_clause(self, select, **kw): + order_by = self.process(select._order_by_clause, **kw) + + # MSSQL only allows ORDER BY in subqueries if there is a LIMIT + if order_by and (not self.is_subquery() or select._limit): + return " ORDER BY " + order_by + else: + return "" + + def update_from_clause(self, update_stmt, + from_table, extra_froms, + from_hints, + **kw): + """Render the UPDATE..FROM clause specific to MSSQL. + + In MSSQL, if the UPDATE statement involves an alias of the table to + be updated, then the table itself must be added to the FROM list as + well. Otherwise, it is optional. Here, we add it regardless. + + """ + return "FROM " + ', '.join( + t._compiler_dispatch(self, asfrom=True, + fromhints=from_hints, **kw) + for t in [from_table] + extra_froms) + + def delete_table_clause(self, delete_stmt, from_table, + extra_froms): + """If we have extra froms make sure we render any alias as hint.""" + ashint = False + if extra_froms: + ashint = True + return from_table._compiler_dispatch( + self, asfrom=True, iscrud=True, ashint=ashint + ) + + def delete_extra_from_clause(self, delete_stmt, from_table, + extra_froms, from_hints, **kw): + """Render the DELETE .. FROM clause specific to MSSQL. + + Yes, it has the FROM keyword twice. + + """ + return "FROM " + ', '.join( + t._compiler_dispatch(self, asfrom=True, + fromhints=from_hints, **kw) + for t in [from_table] + extra_froms) + + +class MSSQLStrictCompiler(MSSQLCompiler): + + """A subclass of MSSQLCompiler which disables the usage of bind + parameters where not allowed natively by MS-SQL. + + A dialect may use this compiler on a platform where native + binds are used. + + """ + ansi_bind_rules = True + + def visit_in_op_binary(self, binary, operator, **kw): + kw['literal_binds'] = True + return "%s IN %s" % ( + self.process(binary.left, **kw), + self.process(binary.right, **kw) + ) + + def visit_notin_op_binary(self, binary, operator, **kw): + kw['literal_binds'] = True + return "%s NOT IN %s" % ( + self.process(binary.left, **kw), + self.process(binary.right, **kw) + ) + + def render_literal_value(self, value, type_): + """ + For date and datetime values, convert to a string + format acceptable to MSSQL. That seems to be the + so-called ODBC canonical date format which looks + like this: + + yyyy-mm-dd hh:mi:ss.mmm(24h) + + For other data types, call the base class implementation. + """ + # datetime and date are both subclasses of datetime.date + if issubclass(type(value), datetime.date): + # SQL Server wants single quotes around the date string. + return "'" + str(value) + "'" + else: + return super(MSSQLStrictCompiler, self).\ + render_literal_value(value, type_) + + +class MSDDLCompiler(compiler.DDLCompiler): + + def get_column_specification(self, column, **kwargs): + colspec = ( + self.preparer.format_column(column) + " " + + self.dialect.type_compiler.process( + column.type, type_expression=column) + ) + + if column.nullable is not None: + if not column.nullable or column.primary_key or \ + isinstance(column.default, sa_schema.Sequence): + colspec += " NOT NULL" + else: + colspec += " NULL" + + if column.table is None: + raise exc.CompileError( + "mssql requires Table-bound columns " + "in order to generate DDL") + + # install an IDENTITY Sequence if we either a sequence or an implicit + # IDENTITY column + if isinstance(column.default, sa_schema.Sequence): + if column.default.start == 0: + start = 0 + else: + start = column.default.start or 1 + + colspec += " IDENTITY(%s,%s)" % (start, + column.default.increment or 1) + elif column is column.table._autoincrement_column: + colspec += " IDENTITY(1,1)" + else: + default = self.get_column_default_string(column) + if default is not None: + colspec += " DEFAULT " + default + + return colspec + + def visit_create_index(self, create, include_schema=False): + index = create.element + self._verify_index_table(index) + preparer = self.preparer + text = "CREATE " + if index.unique: + text += "UNIQUE " + + # handle clustering option + clustered = index.dialect_options['mssql']['clustered'] + if clustered is not None: + if clustered: + text += "CLUSTERED " + else: + text += "NONCLUSTERED " + + text += "INDEX %s ON %s (%s)" \ + % ( + self._prepared_index_name(index, + include_schema=include_schema), + preparer.format_table(index.table), + ', '.join( + self.sql_compiler.process(expr, + include_table=False, + literal_binds=True) for + expr in index.expressions) + ) + + # handle other included columns + if index.dialect_options['mssql']['include']: + inclusions = [index.table.c[col] + if isinstance(col, util.string_types) else col + for col in + index.dialect_options['mssql']['include'] + ] + + text += " INCLUDE (%s)" \ + % ', '.join([preparer.quote(c.name) + for c in inclusions]) + + return text + + def visit_drop_index(self, drop): + return "\nDROP INDEX %s ON %s" % ( + self._prepared_index_name(drop.element, include_schema=False), + self.preparer.format_table(drop.element.table) + ) + + def visit_primary_key_constraint(self, constraint): + if len(constraint) == 0: + return '' + text = "" + if constraint.name is not None: + text += "CONSTRAINT %s " % \ + self.preparer.format_constraint(constraint) + text += "PRIMARY KEY " + + clustered = constraint.dialect_options['mssql']['clustered'] + if clustered is not None: + if clustered: + text += "CLUSTERED " + else: + text += "NONCLUSTERED " + + text += "(%s)" % ', '.join(self.preparer.quote(c.name) + for c in constraint) + text += self.define_constraint_deferrability(constraint) + return text + + def visit_unique_constraint(self, constraint): + if len(constraint) == 0: + return '' + text = "" + if constraint.name is not None: + text += "CONSTRAINT %s " % \ + self.preparer.format_constraint(constraint) + text += "UNIQUE " + + clustered = constraint.dialect_options['mssql']['clustered'] + if clustered is not None: + if clustered: + text += "CLUSTERED " + else: + text += "NONCLUSTERED " + + text += "(%s)" % ', '.join(self.preparer.quote(c.name) + for c in constraint) + text += self.define_constraint_deferrability(constraint) + return text + + +class MSIdentifierPreparer(compiler.IdentifierPreparer): + reserved_words = RESERVED_WORDS + + def __init__(self, dialect): + super(MSIdentifierPreparer, self).__init__( + dialect, initial_quote='[', + final_quote=']', quote_case_sensitive_collations=False) + + def _escape_identifier(self, value): + return value + + def quote_schema(self, schema, force=None): + """Prepare a quoted table and schema name.""" + + dbname, owner = _schema_elements(schema) + if dbname: + result = "%s.%s" % ( + self.quote(dbname, force), self.quote(owner, force)) + elif owner: + result = self.quote(owner, force) + else: + result = "" + return result + + +def _db_plus_owner_listing(fn): + def wrap(dialect, connection, schema=None, **kw): + dbname, owner = _owner_plus_db(dialect, schema) + return _switch_db(dbname, connection, fn, dialect, connection, + dbname, owner, schema, **kw) + return update_wrapper(wrap, fn) + + +def _db_plus_owner(fn): + def wrap(dialect, connection, tablename, schema=None, **kw): + dbname, owner = _owner_plus_db(dialect, schema) + return _switch_db(dbname, connection, fn, dialect, connection, + tablename, dbname, owner, schema, **kw) + return update_wrapper(wrap, fn) + + +def _switch_db(dbname, connection, fn, *arg, **kw): + if dbname: + current_db = connection.scalar("select db_name()") + connection.execute("use %s" % dbname) + try: + return fn(*arg, **kw) + finally: + if dbname: + connection.execute("use %s" % current_db) + + +def _owner_plus_db(dialect, schema): + if not schema: + return None, dialect.default_schema_name + elif "." in schema: + return _schema_elements(schema) + else: + return None, schema + + +def _schema_elements(schema): + if isinstance(schema, quoted_name) and schema.quote: + return None, schema + + push = [] + symbol = "" + bracket = False + for token in re.split(r"(\[|\]|\.)", schema): + if not token: + continue + if token == '[': + bracket = True + elif token == ']': + bracket = False + elif not bracket and token == ".": + push.append(symbol) + symbol = "" + else: + symbol += token + if symbol: + push.append(symbol) + if len(push) > 1: + return push[0], "".join(push[1:]) + elif len(push): + return None, push[0] + else: + return None, None + + +class MSDialect(default.DefaultDialect): + name = 'mssql' + supports_default_values = True + supports_empty_insert = False + execution_ctx_cls = MSExecutionContext + use_scope_identity = True + max_identifier_length = 128 + schema_name = "dbo" + + colspecs = { + sqltypes.DateTime: _MSDateTime, + sqltypes.Date: _MSDate, + sqltypes.Time: TIME, + } + + engine_config_types = default.DefaultDialect.engine_config_types.union([ + ('legacy_schema_aliasing', util.asbool), + ]) + + ischema_names = ischema_names + + supports_native_boolean = False + non_native_boolean_check_constraint = False + supports_unicode_binds = True + postfetch_lastrowid = True + + server_version_info = () + + statement_compiler = MSSQLCompiler + ddl_compiler = MSDDLCompiler + type_compiler = MSTypeCompiler + preparer = MSIdentifierPreparer + + construct_arguments = [ + (sa_schema.PrimaryKeyConstraint, { + "clustered": None + }), + (sa_schema.UniqueConstraint, { + "clustered": None + }), + (sa_schema.Index, { + "clustered": None, + "include": None + }) + ] + + def __init__(self, + query_timeout=None, + use_scope_identity=True, + max_identifier_length=None, + schema_name="dbo", + isolation_level=None, + deprecate_large_types=None, + legacy_schema_aliasing=False, **opts): + self.query_timeout = int(query_timeout or 0) + self.schema_name = schema_name + + self.use_scope_identity = use_scope_identity + self.max_identifier_length = int(max_identifier_length or 0) or \ + self.max_identifier_length + self.deprecate_large_types = deprecate_large_types + self.legacy_schema_aliasing = legacy_schema_aliasing + + super(MSDialect, self).__init__(**opts) + + self.isolation_level = isolation_level + + def do_savepoint(self, connection, name): + # give the DBAPI a push + connection.execute("IF @@TRANCOUNT = 0 BEGIN TRANSACTION") + super(MSDialect, self).do_savepoint(connection, name) + + def do_release_savepoint(self, connection, name): + # SQL Server does not support RELEASE SAVEPOINT + pass + + _isolation_lookup = set(['SERIALIZABLE', 'READ UNCOMMITTED', + 'READ COMMITTED', 'REPEATABLE READ', + 'SNAPSHOT']) + + def set_isolation_level(self, connection, level): + level = level.replace('_', ' ') + if level not in self._isolation_lookup: + raise exc.ArgumentError( + "Invalid value '%s' for isolation_level. " + "Valid isolation levels for %s are %s" % + (level, self.name, ", ".join(self._isolation_lookup)) + ) + cursor = connection.cursor() + cursor.execute( + "SET TRANSACTION ISOLATION LEVEL %s" % level) + cursor.close() + + def get_isolation_level(self, connection): + if self.server_version_info < MS_2005_VERSION: + raise NotImplementedError( + "Can't fetch isolation level prior to SQL Server 2005") + + last_error = None + + views = ("sys.dm_exec_sessions", "sys.dm_pdw_nodes_exec_sessions") + for view in views: + cursor = connection.cursor() + try: + cursor.execute(""" + SELECT CASE transaction_isolation_level + WHEN 0 THEN NULL + WHEN 1 THEN 'READ UNCOMMITTED' + WHEN 2 THEN 'READ COMMITTED' + WHEN 3 THEN 'REPEATABLE READ' + WHEN 4 THEN 'SERIALIZABLE' + WHEN 5 THEN 'SNAPSHOT' END AS TRANSACTION_ISOLATION_LEVEL + FROM %s + where session_id = @@SPID + """ % view) + val = cursor.fetchone()[0] + except self.dbapi.Error as err: + # Python3 scoping rules + last_error = err + continue + else: + return val.upper() + finally: + cursor.close() + else: + util.warn( + "Could not fetch transaction isolation level, " + "tried views: %s; final error was: %s" % (views, last_error)) + + raise NotImplementedError( + "Can't fetch isolation level on this particular " + "SQL Server version" + ) + + def initialize(self, connection): + super(MSDialect, self).initialize(connection) + self._setup_version_attributes() + + def on_connect(self): + if self.isolation_level is not None: + def connect(conn): + self.set_isolation_level(conn, self.isolation_level) + return connect + else: + return None + + def _setup_version_attributes(self): + if self.server_version_info[0] not in list(range(8, 17)): + util.warn( + "Unrecognized server version info '%s'. Some SQL Server " + "features may not function properly." % + ".".join(str(x) for x in self.server_version_info)) + if self.server_version_info >= MS_2005_VERSION and \ + 'implicit_returning' not in self.__dict__: + self.implicit_returning = True + if self.server_version_info >= MS_2008_VERSION: + self.supports_multivalues_insert = True + if self.deprecate_large_types is None: + self.deprecate_large_types = \ + self.server_version_info >= MS_2012_VERSION + + def _get_default_schema_name(self, connection): + if self.server_version_info < MS_2005_VERSION: + return self.schema_name + else: + query = sql.text("SELECT schema_name()") + default_schema_name = connection.scalar(query) + if default_schema_name is not None: + return util.text_type(default_schema_name) + else: + return self.schema_name + + @_db_plus_owner + def has_table(self, connection, tablename, dbname, owner, schema): + columns = ischema.columns + + whereclause = columns.c.table_name == tablename + + if owner: + whereclause = sql.and_(whereclause, + columns.c.table_schema == owner) + s = sql.select([columns], whereclause) + c = connection.execute(s) + return c.first() is not None + + @reflection.cache + def get_schema_names(self, connection, **kw): + s = sql.select([ischema.schemata.c.schema_name], + order_by=[ischema.schemata.c.schema_name] + ) + schema_names = [r[0] for r in connection.execute(s)] + return schema_names + + @reflection.cache + @_db_plus_owner_listing + def get_table_names(self, connection, dbname, owner, schema, **kw): + tables = ischema.tables + s = sql.select([tables.c.table_name], + sql.and_( + tables.c.table_schema == owner, + tables.c.table_type == 'BASE TABLE' + ), + order_by=[tables.c.table_name] + ) + table_names = [r[0] for r in connection.execute(s)] + return table_names + + @reflection.cache + @_db_plus_owner_listing + def get_view_names(self, connection, dbname, owner, schema, **kw): + tables = ischema.tables + s = sql.select([tables.c.table_name], + sql.and_( + tables.c.table_schema == owner, + tables.c.table_type == 'VIEW' + ), + order_by=[tables.c.table_name] + ) + view_names = [r[0] for r in connection.execute(s)] + return view_names + + @reflection.cache + @_db_plus_owner + def get_indexes(self, connection, tablename, dbname, owner, schema, **kw): + # using system catalogs, don't support index reflection + # below MS 2005 + if self.server_version_info < MS_2005_VERSION: + return [] + + rp = connection.execute( + sql.text("select ind.index_id, ind.is_unique, ind.name " + "from sys.indexes as ind join sys.tables as tab on " + "ind.object_id=tab.object_id " + "join sys.schemas as sch on sch.schema_id=tab.schema_id " + "where tab.name = :tabname " + "and sch.name=:schname " + "and ind.is_primary_key=0 and ind.type != 0", + bindparams=[ + sql.bindparam('tabname', tablename, + sqltypes.String(convert_unicode=True)), + sql.bindparam('schname', owner, + sqltypes.String(convert_unicode=True)) + ], + typemap={ + 'name': sqltypes.Unicode() + } + ) + ) + indexes = {} + for row in rp: + indexes[row['index_id']] = { + 'name': row['name'], + 'unique': row['is_unique'] == 1, + 'column_names': [] + } + rp = connection.execute( + sql.text( + "select ind_col.index_id, ind_col.object_id, col.name " + "from sys.columns as col " + "join sys.tables as tab on tab.object_id=col.object_id " + "join sys.index_columns as ind_col on " + "(ind_col.column_id=col.column_id and " + "ind_col.object_id=tab.object_id) " + "join sys.schemas as sch on sch.schema_id=tab.schema_id " + "where tab.name=:tabname " + "and sch.name=:schname", + bindparams=[ + sql.bindparam('tabname', tablename, + sqltypes.String(convert_unicode=True)), + sql.bindparam('schname', owner, + sqltypes.String(convert_unicode=True)) + ], + typemap={'name': sqltypes.Unicode()} + ), + ) + for row in rp: + if row['index_id'] in indexes: + indexes[row['index_id']]['column_names'].append(row['name']) + + return list(indexes.values()) + + @reflection.cache + @_db_plus_owner + def get_view_definition(self, connection, viewname, + dbname, owner, schema, **kw): + rp = connection.execute( + sql.text( + "select definition from sys.sql_modules as mod, " + "sys.views as views, " + "sys.schemas as sch" + " where " + "mod.object_id=views.object_id and " + "views.schema_id=sch.schema_id and " + "views.name=:viewname and sch.name=:schname", + bindparams=[ + sql.bindparam('viewname', viewname, + sqltypes.String(convert_unicode=True)), + sql.bindparam('schname', owner, + sqltypes.String(convert_unicode=True)) + ] + ) + ) + + if rp: + view_def = rp.scalar() + return view_def + + @reflection.cache + @_db_plus_owner + def get_columns(self, connection, tablename, dbname, owner, schema, **kw): + # Get base columns + columns = ischema.columns + if owner: + whereclause = sql.and_(columns.c.table_name == tablename, + columns.c.table_schema == owner) + else: + whereclause = columns.c.table_name == tablename + s = sql.select([columns], whereclause, + order_by=[columns.c.ordinal_position]) + + c = connection.execute(s) + cols = [] + while True: + row = c.fetchone() + if row is None: + break + (name, type, nullable, charlen, + numericprec, numericscale, default, collation) = ( + row[columns.c.column_name], + row[columns.c.data_type], + row[columns.c.is_nullable] == 'YES', + row[columns.c.character_maximum_length], + row[columns.c.numeric_precision], + row[columns.c.numeric_scale], + row[columns.c.column_default], + row[columns.c.collation_name] + ) + coltype = self.ischema_names.get(type, None) + + kwargs = {} + if coltype in (MSString, MSChar, MSNVarchar, MSNChar, MSText, + MSNText, MSBinary, MSVarBinary, + sqltypes.LargeBinary): + if charlen == -1: + charlen = None + kwargs['length'] = charlen + if collation: + kwargs['collation'] = collation + + if coltype is None: + util.warn( + "Did not recognize type '%s' of column '%s'" % + (type, name)) + coltype = sqltypes.NULLTYPE + else: + if issubclass(coltype, sqltypes.Numeric) and \ + coltype is not MSReal: + kwargs['scale'] = numericscale + kwargs['precision'] = numericprec + + coltype = coltype(**kwargs) + cdict = { + 'name': name, + 'type': coltype, + 'nullable': nullable, + 'default': default, + 'autoincrement': False, + } + cols.append(cdict) + # autoincrement and identity + colmap = {} + for col in cols: + colmap[col['name']] = col + # We also run an sp_columns to check for identity columns: + cursor = connection.execute("sp_columns @table_name = '%s', " + "@table_owner = '%s'" + % (tablename, owner)) + ic = None + while True: + row = cursor.fetchone() + if row is None: + break + (col_name, type_name) = row[3], row[5] + if type_name.endswith("identity") and col_name in colmap: + ic = col_name + colmap[col_name]['autoincrement'] = True + colmap[col_name]['sequence'] = dict( + name='%s_identity' % col_name) + break + cursor.close() + + if ic is not None and self.server_version_info >= MS_2005_VERSION: + table_fullname = "%s.%s" % (owner, tablename) + cursor = connection.execute( + "select ident_seed('%s'), ident_incr('%s')" + % (table_fullname, table_fullname) + ) + + row = cursor.first() + if row is not None and row[0] is not None: + colmap[ic]['sequence'].update({ + 'start': int(row[0]), + 'increment': int(row[1]) + }) + return cols + + @reflection.cache + @_db_plus_owner + def get_pk_constraint(self, connection, tablename, + dbname, owner, schema, **kw): + pkeys = [] + TC = ischema.constraints + C = ischema.key_constraints.alias('C') + + # Primary key constraints + s = sql.select([C.c.column_name, + TC.c.constraint_type, + C.c.constraint_name], + sql.and_(TC.c.constraint_name == C.c.constraint_name, + TC.c.table_schema == C.c.table_schema, + C.c.table_name == tablename, + C.c.table_schema == owner) + ) + c = connection.execute(s) + constraint_name = None + for row in c: + if 'PRIMARY' in row[TC.c.constraint_type.name]: + pkeys.append(row[0]) + if constraint_name is None: + constraint_name = row[C.c.constraint_name.name] + return {'constrained_columns': pkeys, 'name': constraint_name} + + @reflection.cache + @_db_plus_owner + def get_foreign_keys(self, connection, tablename, + dbname, owner, schema, **kw): + RR = ischema.ref_constraints + C = ischema.key_constraints.alias('C') + R = ischema.key_constraints.alias('R') + + # Foreign key constraints + s = sql.select([C.c.column_name, + R.c.table_schema, R.c.table_name, R.c.column_name, + RR.c.constraint_name, RR.c.match_option, + RR.c.update_rule, + RR.c.delete_rule], + sql.and_(C.c.table_name == tablename, + C.c.table_schema == owner, + RR.c.constraint_schema == C.c.table_schema, + C.c.constraint_name == RR.c.constraint_name, + R.c.constraint_name == + RR.c.unique_constraint_name, + R.c.constraint_schema == + RR.c.unique_constraint_schema, + C.c.ordinal_position == R.c.ordinal_position + ), + order_by=[RR.c.constraint_name, R.c.ordinal_position] + ) + + # group rows by constraint ID, to handle multi-column FKs + fkeys = [] + fknm, scols, rcols = (None, [], []) + + def fkey_rec(): + return { + 'name': None, + 'constrained_columns': [], + 'referred_schema': None, + 'referred_table': None, + 'referred_columns': [] + } + + fkeys = util.defaultdict(fkey_rec) + + for r in connection.execute(s).fetchall(): + scol, rschema, rtbl, rcol, rfknm, fkmatch, fkuprule, fkdelrule = r + + rec = fkeys[rfknm] + rec['name'] = rfknm + if not rec['referred_table']: + rec['referred_table'] = rtbl + if schema is not None or owner != rschema: + if dbname: + rschema = dbname + "." + rschema + rec['referred_schema'] = rschema + + local_cols, remote_cols = \ + rec['constrained_columns'],\ + rec['referred_columns'] + + local_cols.append(scol) + remote_cols.append(rcol) + + return list(fkeys.values()) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mssql/information_schema.py b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/information_schema.py new file mode 100644 index 0000000..3682fae --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/information_schema.py @@ -0,0 +1,139 @@ +# mssql/information_schema.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +# TODO: should be using the sys. catalog with SQL Server, not information +# schema + +from ... import Table, MetaData, Column +from ...types import String, Unicode, UnicodeText, Integer, TypeDecorator +from ... import cast +from ... import util +from ...sql import expression +from ...ext.compiler import compiles + +ischema = MetaData() + + +class CoerceUnicode(TypeDecorator): + impl = Unicode + + def process_bind_param(self, value, dialect): + if util.py2k and isinstance(value, util.binary_type): + value = value.decode(dialect.encoding) + return value + + def bind_expression(self, bindvalue): + return _cast_on_2005(bindvalue) + + +class _cast_on_2005(expression.ColumnElement): + def __init__(self, bindvalue): + self.bindvalue = bindvalue + + +@compiles(_cast_on_2005) +def _compile(element, compiler, **kw): + from . import base + if compiler.dialect.server_version_info is None or \ + compiler.dialect.server_version_info < base.MS_2005_VERSION: + return compiler.process(element.bindvalue, **kw) + else: + return compiler.process(cast(element.bindvalue, Unicode), **kw) + +schemata = Table("SCHEMATA", ischema, + Column("CATALOG_NAME", CoerceUnicode, key="catalog_name"), + Column("SCHEMA_NAME", CoerceUnicode, key="schema_name"), + Column("SCHEMA_OWNER", CoerceUnicode, key="schema_owner"), + schema="INFORMATION_SCHEMA") + +tables = Table("TABLES", ischema, + Column("TABLE_CATALOG", CoerceUnicode, key="table_catalog"), + Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"), + Column("TABLE_NAME", CoerceUnicode, key="table_name"), + Column( + "TABLE_TYPE", String(convert_unicode=True), + key="table_type"), + schema="INFORMATION_SCHEMA") + +columns = Table("COLUMNS", ischema, + Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"), + Column("TABLE_NAME", CoerceUnicode, key="table_name"), + Column("COLUMN_NAME", CoerceUnicode, key="column_name"), + Column("IS_NULLABLE", Integer, key="is_nullable"), + Column("DATA_TYPE", String, key="data_type"), + Column("ORDINAL_POSITION", Integer, key="ordinal_position"), + Column("CHARACTER_MAXIMUM_LENGTH", Integer, + key="character_maximum_length"), + Column("NUMERIC_PRECISION", Integer, key="numeric_precision"), + Column("NUMERIC_SCALE", Integer, key="numeric_scale"), + Column("COLUMN_DEFAULT", Integer, key="column_default"), + Column("COLLATION_NAME", String, key="collation_name"), + schema="INFORMATION_SCHEMA") + +constraints = Table("TABLE_CONSTRAINTS", ischema, + Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"), + Column("TABLE_NAME", CoerceUnicode, key="table_name"), + Column("CONSTRAINT_NAME", CoerceUnicode, + key="constraint_name"), + Column("CONSTRAINT_TYPE", String( + convert_unicode=True), key="constraint_type"), + schema="INFORMATION_SCHEMA") + +column_constraints = Table("CONSTRAINT_COLUMN_USAGE", ischema, + Column("TABLE_SCHEMA", CoerceUnicode, + key="table_schema"), + Column("TABLE_NAME", CoerceUnicode, + key="table_name"), + Column("COLUMN_NAME", CoerceUnicode, + key="column_name"), + Column("CONSTRAINT_NAME", CoerceUnicode, + key="constraint_name"), + schema="INFORMATION_SCHEMA") + +key_constraints = Table("KEY_COLUMN_USAGE", ischema, + Column("TABLE_SCHEMA", CoerceUnicode, + key="table_schema"), + Column("TABLE_NAME", CoerceUnicode, + key="table_name"), + Column("COLUMN_NAME", CoerceUnicode, + key="column_name"), + Column("CONSTRAINT_NAME", CoerceUnicode, + key="constraint_name"), + Column("CONSTRAINT_SCHEMA", CoerceUnicode, + key="constraint_schema"), + Column("ORDINAL_POSITION", Integer, + key="ordinal_position"), + schema="INFORMATION_SCHEMA") + +ref_constraints = Table("REFERENTIAL_CONSTRAINTS", ischema, + Column("CONSTRAINT_CATALOG", CoerceUnicode, + key="constraint_catalog"), + Column("CONSTRAINT_SCHEMA", CoerceUnicode, + key="constraint_schema"), + Column("CONSTRAINT_NAME", CoerceUnicode, + key="constraint_name"), + # TODO: is CATLOG misspelled ? + Column("UNIQUE_CONSTRAINT_CATLOG", CoerceUnicode, + key="unique_constraint_catalog"), + + Column("UNIQUE_CONSTRAINT_SCHEMA", CoerceUnicode, + key="unique_constraint_schema"), + Column("UNIQUE_CONSTRAINT_NAME", CoerceUnicode, + key="unique_constraint_name"), + Column("MATCH_OPTION", String, key="match_option"), + Column("UPDATE_RULE", String, key="update_rule"), + Column("DELETE_RULE", String, key="delete_rule"), + schema="INFORMATION_SCHEMA") + +views = Table("VIEWS", ischema, + Column("TABLE_CATALOG", CoerceUnicode, key="table_catalog"), + Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"), + Column("TABLE_NAME", CoerceUnicode, key="table_name"), + Column("VIEW_DEFINITION", CoerceUnicode, key="view_definition"), + Column("CHECK_OPTION", String, key="check_option"), + Column("IS_UPDATABLE", String, key="is_updatable"), + schema="INFORMATION_SCHEMA") diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mssql/mxodbc.py b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/mxodbc.py new file mode 100644 index 0000000..8983a3b --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/mxodbc.py @@ -0,0 +1,139 @@ +# mssql/mxodbc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: mssql+mxodbc + :name: mxODBC + :dbapi: mxodbc + :connectstring: mssql+mxodbc://<username>:<password>@<dsnname> + :url: http://www.egenix.com/ + +Execution Modes +--------------- + +mxODBC features two styles of statement execution, using the +``cursor.execute()`` and ``cursor.executedirect()`` methods (the second being +an extension to the DBAPI specification). The former makes use of a particular +API call specific to the SQL Server Native Client ODBC driver known +SQLDescribeParam, while the latter does not. + +mxODBC apparently only makes repeated use of a single prepared statement +when SQLDescribeParam is used. The advantage to prepared statement reuse is +one of performance. The disadvantage is that SQLDescribeParam has a limited +set of scenarios in which bind parameters are understood, including that they +cannot be placed within the argument lists of function calls, anywhere outside +the FROM, or even within subqueries within the FROM clause - making the usage +of bind parameters within SELECT statements impossible for all but the most +simplistic statements. + +For this reason, the mxODBC dialect uses the "native" mode by default only for +INSERT, UPDATE, and DELETE statements, and uses the escaped string mode for +all other statements. + +This behavior can be controlled via +:meth:`~sqlalchemy.sql.expression.Executable.execution_options` using the +``native_odbc_execute`` flag with a value of ``True`` or ``False``, where a +value of ``True`` will unconditionally use native bind parameters and a value +of ``False`` will unconditionally use string-escaped parameters. + +""" + + +from ... import types as sqltypes +from ...connectors.mxodbc import MxODBCConnector +from .pyodbc import MSExecutionContext_pyodbc, _MSNumeric_pyodbc +from .base import (MSDialect, + MSSQLStrictCompiler, + VARBINARY, + _MSDateTime, _MSDate, _MSTime) + + +class _MSNumeric_mxodbc(_MSNumeric_pyodbc): + """Include pyodbc's numeric processor. + """ + + +class _MSDate_mxodbc(_MSDate): + def bind_processor(self, dialect): + def process(value): + if value is not None: + return "%s-%s-%s" % (value.year, value.month, value.day) + else: + return None + return process + + +class _MSTime_mxodbc(_MSTime): + def bind_processor(self, dialect): + def process(value): + if value is not None: + return "%s:%s:%s" % (value.hour, value.minute, value.second) + else: + return None + return process + + +class _VARBINARY_mxodbc(VARBINARY): + + """ + mxODBC Support for VARBINARY column types. + + This handles the special case for null VARBINARY values, + which maps None values to the mx.ODBC.Manager.BinaryNull symbol. + """ + + def bind_processor(self, dialect): + if dialect.dbapi is None: + return None + + DBAPIBinary = dialect.dbapi.Binary + + def process(value): + if value is not None: + return DBAPIBinary(value) + else: + # should pull from mx.ODBC.Manager.BinaryNull + return dialect.dbapi.BinaryNull + return process + + +class MSExecutionContext_mxodbc(MSExecutionContext_pyodbc): + """ + The pyodbc execution context is useful for enabling + SELECT SCOPE_IDENTITY in cases where OUTPUT clause + does not work (tables with insert triggers). + """ + # todo - investigate whether the pyodbc execution context + # is really only being used in cases where OUTPUT + # won't work. + + +class MSDialect_mxodbc(MxODBCConnector, MSDialect): + + # this is only needed if "native ODBC" mode is used, + # which is now disabled by default. + # statement_compiler = MSSQLStrictCompiler + + execution_ctx_cls = MSExecutionContext_mxodbc + + # flag used by _MSNumeric_mxodbc + _need_decimal_fix = True + + colspecs = { + sqltypes.Numeric: _MSNumeric_mxodbc, + sqltypes.DateTime: _MSDateTime, + sqltypes.Date: _MSDate_mxodbc, + sqltypes.Time: _MSTime_mxodbc, + VARBINARY: _VARBINARY_mxodbc, + sqltypes.LargeBinary: _VARBINARY_mxodbc, + } + + def __init__(self, description_encoding=None, **params): + super(MSDialect_mxodbc, self).__init__(**params) + self.description_encoding = description_encoding + +dialect = MSDialect_mxodbc diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mssql/pymssql.py b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/pymssql.py new file mode 100644 index 0000000..8589c8b --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/pymssql.py @@ -0,0 +1,116 @@ +# mssql/pymssql.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: mssql+pymssql + :name: pymssql + :dbapi: pymssql + :connectstring: mssql+pymssql://<username>:<password>@<freetds_name>/?\ +charset=utf8 + :url: http://pymssql.org/ + +pymssql is a Python module that provides a Python DBAPI interface around +`FreeTDS <http://www.freetds.org/>`_. Compatible builds are available for +Linux, MacOSX and Windows platforms. + +Modern versions of this driver work very well with SQL Server and +FreeTDS from Linux and is highly recommended. + +""" +from .base import MSDialect, MSIdentifierPreparer +from ... import types as sqltypes, util, processors +import re + + +class _MSNumeric_pymssql(sqltypes.Numeric): + def result_processor(self, dialect, type_): + if not self.asdecimal: + return processors.to_float + else: + return sqltypes.Numeric.result_processor(self, dialect, type_) + + +class MSIdentifierPreparer_pymssql(MSIdentifierPreparer): + + def __init__(self, dialect): + super(MSIdentifierPreparer_pymssql, self).__init__(dialect) + # pymssql has the very unusual behavior that it uses pyformat + # yet does not require that percent signs be doubled + self._double_percents = False + + +class MSDialect_pymssql(MSDialect): + supports_native_decimal = True + driver = 'pymssql' + + preparer = MSIdentifierPreparer_pymssql + + colspecs = util.update_copy( + MSDialect.colspecs, + { + sqltypes.Numeric: _MSNumeric_pymssql, + sqltypes.Float: sqltypes.Float, + } + ) + + @classmethod + def dbapi(cls): + module = __import__('pymssql') + # pymmsql < 2.1.1 doesn't have a Binary method. we use string + client_ver = tuple(int(x) for x in module.__version__.split(".")) + if client_ver < (2, 1, 1): + # TODO: monkeypatching here is less than ideal + module.Binary = lambda x: x if hasattr(x, 'decode') else str(x) + + if client_ver < (1, ): + util.warn("The pymssql dialect expects at least " + "the 1.0 series of the pymssql DBAPI.") + return module + + def _get_server_version_info(self, connection): + vers = connection.scalar("select @@version") + m = re.match( + r"Microsoft .*? - (\d+).(\d+).(\d+).(\d+)", vers) + if m: + return tuple(int(x) for x in m.group(1, 2, 3, 4)) + else: + return None + + def create_connect_args(self, url): + opts = url.translate_connect_args(username='user') + opts.update(url.query) + port = opts.pop('port', None) + if port and 'host' in opts: + opts['host'] = "%s:%s" % (opts['host'], port) + return [[], opts] + + def is_disconnect(self, e, connection, cursor): + for msg in ( + "Adaptive Server connection timed out", + "Net-Lib error during Connection reset by peer", + "message 20003", # connection timeout + "Error 10054", + "Not connected to any MS SQL server", + "Connection is closed", + "message 20006", # Write to the server failed + "message 20017", # Unexpected EOF from the server + ): + if msg in str(e): + return True + else: + return False + + def set_isolation_level(self, connection, level): + if level == 'AUTOCOMMIT': + connection.autocommit(True) + else: + connection.autocommit(False) + super(MSDialect_pymssql, self).set_isolation_level(connection, + level) + + +dialect = MSDialect_pymssql diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mssql/pyodbc.py b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/pyodbc.py new file mode 100644 index 0000000..36bcc49 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/pyodbc.py @@ -0,0 +1,310 @@ +# mssql/pyodbc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +r""" +.. dialect:: mssql+pyodbc + :name: PyODBC + :dbapi: pyodbc + :connectstring: mssql+pyodbc://<username>:<password>@<dsnname> + :url: http://pypi.python.org/pypi/pyodbc/ + +Connecting to PyODBC +-------------------- + +The URL here is to be translated to PyODBC connection strings, as +detailed in `ConnectionStrings <https://code.google.com/p/pyodbc/wiki/ConnectionStrings>`_. + +DSN Connections +^^^^^^^^^^^^^^^ + +A DSN-based connection is **preferred** overall when using ODBC. A +basic DSN-based connection looks like:: + + engine = create_engine("mssql+pyodbc://scott:tiger@some_dsn") + +Which above, will pass the following connection string to PyODBC:: + + dsn=mydsn;UID=user;PWD=pass + +If the username and password are omitted, the DSN form will also add +the ``Trusted_Connection=yes`` directive to the ODBC string. + +Hostname Connections +^^^^^^^^^^^^^^^^^^^^ + +Hostname-based connections are **not preferred**, however are supported. +The ODBC driver name must be explicitly specified:: + + engine = create_engine("mssql+pyodbc://scott:tiger@myhost:port/databasename?driver=SQL+Server+Native+Client+10.0") + +.. versionchanged:: 1.0.0 Hostname-based PyODBC connections now require the + SQL Server driver name specified explicitly. SQLAlchemy cannot + choose an optimal default here as it varies based on platform + and installed drivers. + +Other keywords interpreted by the Pyodbc dialect to be passed to +``pyodbc.connect()`` in both the DSN and hostname cases include: +``odbc_autotranslate``, ``ansi``, ``unicode_results``, ``autocommit``. + +Pass through exact Pyodbc string +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A PyODBC connection string can also be sent exactly as specified in +`ConnectionStrings <https://code.google.com/p/pyodbc/wiki/ConnectionStrings>`_ +into the driver using the parameter ``odbc_connect``. The delimeters must be URL escaped, however, +as illustrated below using ``urllib.quote_plus``:: + + import urllib + params = urllib.quote_plus("DRIVER={SQL Server Native Client 10.0};SERVER=dagger;DATABASE=test;UID=user;PWD=password") + + engine = create_engine("mssql+pyodbc:///?odbc_connect=%s" % params) + + +Driver / Unicode Support +------------------------- + +PyODBC works best with Microsoft ODBC drivers, particularly in the area +of Unicode support on both Python 2 and Python 3. + +Using the FreeTDS ODBC drivers on Linux or OSX with PyODBC is **not** +recommended; there have been historically many Unicode-related issues +in this area, including before Microsoft offered ODBC drivers for Linux +and OSX. Now that Microsoft offers drivers for all platforms, for +PyODBC support these are recommended. FreeTDS remains relevant for +non-ODBC drivers such as pymssql where it works very well. + + +Rowcount Support +---------------- + +Pyodbc only has partial support for rowcount. See the notes at +:ref:`mssql_rowcount_versioning` for important notes when using ORM +versioning. + +""" + +from .base import MSExecutionContext, MSDialect, BINARY, VARBINARY +from ...connectors.pyodbc import PyODBCConnector +from ... import types as sqltypes, util, exc +import decimal +import re + + +class _ms_numeric_pyodbc(object): + + """Turns Decimals with adjusted() < 0 or > 7 into strings. + + The routines here are needed for older pyodbc versions + as well as current mxODBC versions. + + """ + + def bind_processor(self, dialect): + + super_process = super(_ms_numeric_pyodbc, self).\ + bind_processor(dialect) + + if not dialect._need_decimal_fix: + return super_process + + def process(value): + if self.asdecimal and \ + isinstance(value, decimal.Decimal): + adjusted = value.adjusted() + if adjusted < 0: + return self._small_dec_to_string(value) + elif adjusted > 7: + return self._large_dec_to_string(value) + + if super_process: + return super_process(value) + else: + return value + return process + + # these routines needed for older versions of pyodbc. + # as of 2.1.8 this logic is integrated. + + def _small_dec_to_string(self, value): + return "%s0.%s%s" % ( + (value < 0 and '-' or ''), + '0' * (abs(value.adjusted()) - 1), + "".join([str(nint) for nint in value.as_tuple()[1]])) + + def _large_dec_to_string(self, value): + _int = value.as_tuple()[1] + if 'E' in str(value): + result = "%s%s%s" % ( + (value < 0 and '-' or ''), + "".join([str(s) for s in _int]), + "0" * (value.adjusted() - (len(_int) - 1))) + else: + if (len(_int) - 1) > value.adjusted(): + result = "%s%s.%s" % ( + (value < 0 and '-' or ''), + "".join( + [str(s) for s in _int][0:value.adjusted() + 1]), + "".join( + [str(s) for s in _int][value.adjusted() + 1:])) + else: + result = "%s%s" % ( + (value < 0 and '-' or ''), + "".join( + [str(s) for s in _int][0:value.adjusted() + 1])) + return result + + +class _MSNumeric_pyodbc(_ms_numeric_pyodbc, sqltypes.Numeric): + pass + + +class _MSFloat_pyodbc(_ms_numeric_pyodbc, sqltypes.Float): + pass + + +class _ms_binary_pyodbc(object): + """Wraps binary values in dialect-specific Binary wrapper. + If the value is null, return a pyodbc-specific BinaryNull + object to prevent pyODBC [and FreeTDS] from defaulting binary + NULL types to SQLWCHAR and causing implicit conversion errors. + """ + + def bind_processor(self, dialect): + if dialect.dbapi is None: + return None + + DBAPIBinary = dialect.dbapi.Binary + + def process(value): + if value is not None: + return DBAPIBinary(value) + else: + # pyodbc-specific + return dialect.dbapi.BinaryNull + return process + + +class _VARBINARY_pyodbc(_ms_binary_pyodbc, VARBINARY): + pass + + +class _BINARY_pyodbc(_ms_binary_pyodbc, BINARY): + pass + + +class MSExecutionContext_pyodbc(MSExecutionContext): + _embedded_scope_identity = False + + def pre_exec(self): + """where appropriate, issue "select scope_identity()" in the same + statement. + + Background on why "scope_identity()" is preferable to "@@identity": + http://msdn.microsoft.com/en-us/library/ms190315.aspx + + Background on why we attempt to embed "scope_identity()" into the same + statement as the INSERT: + http://code.google.com/p/pyodbc/wiki/FAQs#How_do_I_retrieve_autogenerated/identity_values? + + """ + + super(MSExecutionContext_pyodbc, self).pre_exec() + + # don't embed the scope_identity select into an + # "INSERT .. DEFAULT VALUES" + if self._select_lastrowid and \ + self.dialect.use_scope_identity and \ + len(self.parameters[0]): + self._embedded_scope_identity = True + + self.statement += "; select scope_identity()" + + def post_exec(self): + if self._embedded_scope_identity: + # Fetch the last inserted id from the manipulated statement + # We may have to skip over a number of result sets with + # no data (due to triggers, etc.) + while True: + try: + # fetchall() ensures the cursor is consumed + # without closing it (FreeTDS particularly) + row = self.cursor.fetchall()[0] + break + except self.dialect.dbapi.Error as e: + # no way around this - nextset() consumes the previous set + # so we need to just keep flipping + self.cursor.nextset() + + self._lastrowid = int(row[0]) + else: + super(MSExecutionContext_pyodbc, self).post_exec() + + +class MSDialect_pyodbc(PyODBCConnector, MSDialect): + + execution_ctx_cls = MSExecutionContext_pyodbc + + colspecs = util.update_copy( + MSDialect.colspecs, + { + sqltypes.Numeric: _MSNumeric_pyodbc, + sqltypes.Float: _MSFloat_pyodbc, + BINARY: _BINARY_pyodbc, + + # SQL Server dialect has a VARBINARY that is just to support + # "deprecate_large_types" w/ VARBINARY(max), but also we must + # handle the usual SQL standard VARBINARY + VARBINARY: _VARBINARY_pyodbc, + sqltypes.VARBINARY: _VARBINARY_pyodbc, + sqltypes.LargeBinary: _VARBINARY_pyodbc, + } + ) + + def __init__(self, description_encoding=None, **params): + if 'description_encoding' in params: + self.description_encoding = params.pop('description_encoding') + super(MSDialect_pyodbc, self).__init__(**params) + self.use_scope_identity = self.use_scope_identity and \ + self.dbapi and \ + hasattr(self.dbapi.Cursor, 'nextset') + self._need_decimal_fix = self.dbapi and \ + self._dbapi_version() < (2, 1, 8) + + def _get_server_version_info(self, connection): + try: + # "Version of the instance of SQL Server, in the form + # of 'major.minor.build.revision'" + raw = connection.scalar( + "SELECT CAST(SERVERPROPERTY('ProductVersion') AS VARCHAR)") + except exc.DBAPIError: + # SQL Server docs indicate this function isn't present prior to + # 2008. Before we had the VARCHAR cast above, pyodbc would also + # fail on this query. + return super(MSDialect_pyodbc, self).\ + _get_server_version_info(connection, allow_chars=False) + else: + version = [] + r = re.compile(r'[.\-]') + for n in r.split(raw): + try: + version.append(int(n)) + except ValueError: + pass + return tuple(version) + + def is_disconnect(self, e, connection, cursor): + if isinstance(e, self.dbapi.Error): + for code in ( + '08S01', '01002', '08003', '08007', + '08S02', '08001', 'HYT00', 'HY010', + '10054'): + if code in str(e): + return True + return super(MSDialect_pyodbc, self).is_disconnect( + e, connection, cursor) + +dialect = MSDialect_pyodbc diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mssql/zxjdbc.py b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/zxjdbc.py new file mode 100644 index 0000000..3fb93b2 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mssql/zxjdbc.py @@ -0,0 +1,69 @@ +# mssql/zxjdbc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: mssql+zxjdbc + :name: zxJDBC for Jython + :dbapi: zxjdbc + :connectstring: mssql+zxjdbc://user:pass@host:port/dbname\ +[?key=value&key=value...] + :driverurl: http://jtds.sourceforge.net/ + + .. note:: Jython is not supported by current versions of SQLAlchemy. The + zxjdbc dialect should be considered as experimental. + +""" +from ...connectors.zxJDBC import ZxJDBCConnector +from .base import MSDialect, MSExecutionContext +from ... import engine + + +class MSExecutionContext_zxjdbc(MSExecutionContext): + + _embedded_scope_identity = False + + def pre_exec(self): + super(MSExecutionContext_zxjdbc, self).pre_exec() + # scope_identity after the fact returns null in jTDS so we must + # embed it + if self._select_lastrowid and self.dialect.use_scope_identity: + self._embedded_scope_identity = True + self.statement += "; SELECT scope_identity()" + + def post_exec(self): + if self._embedded_scope_identity: + while True: + try: + row = self.cursor.fetchall()[0] + break + except self.dialect.dbapi.Error: + self.cursor.nextset() + self._lastrowid = int(row[0]) + + if (self.isinsert or self.isupdate or self.isdelete) and \ + self.compiled.returning: + self._result_proxy = engine.FullyBufferedResultProxy(self) + + if self._enable_identity_insert: + table = self.dialect.identifier_preparer.format_table( + self.compiled.statement.table) + self.cursor.execute("SET IDENTITY_INSERT %s OFF" % table) + + +class MSDialect_zxjdbc(ZxJDBCConnector, MSDialect): + jdbc_db_name = 'jtds:sqlserver' + jdbc_driver_name = 'net.sourceforge.jtds.jdbc.Driver' + + execution_ctx_cls = MSExecutionContext_zxjdbc + + def _get_server_version_info(self, connection): + return tuple( + int(x) + for x in connection.connection.dbversion.split('.') + ) + +dialect = MSDialect_zxjdbc diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/__init__.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/__init__.py new file mode 100644 index 0000000..de4e1fa --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/__init__.py @@ -0,0 +1,34 @@ +# mysql/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from . import base, mysqldb, oursql, \ + pyodbc, zxjdbc, mysqlconnector, pymysql, \ + gaerdbms, cymysql + +from .base import \ + BIGINT, BINARY, BIT, BLOB, BOOLEAN, CHAR, DATE, DATETIME, \ + DECIMAL, DOUBLE, ENUM, DECIMAL,\ + FLOAT, INTEGER, INTEGER, JSON, LONGBLOB, LONGTEXT, MEDIUMBLOB, \ + MEDIUMINT, MEDIUMTEXT, NCHAR, \ + NVARCHAR, NUMERIC, SET, SMALLINT, REAL, TEXT, TIME, TIMESTAMP, \ + TINYBLOB, TINYINT, TINYTEXT,\ + VARBINARY, VARCHAR, YEAR + +from .dml import insert, Insert + +# default dialect +base.dialect = dialect = mysqldb.dialect + + +__all__ = ( + 'BIGINT', 'BINARY', 'BIT', 'BLOB', 'BOOLEAN', 'CHAR', 'DATE', 'DATETIME', + 'DECIMAL', 'DOUBLE', 'ENUM', 'DECIMAL', 'FLOAT', 'INTEGER', 'INTEGER', + 'JSON', 'LONGBLOB', 'LONGTEXT', 'MEDIUMBLOB', 'MEDIUMINT', 'MEDIUMTEXT', + 'NCHAR', 'NVARCHAR', 'NUMERIC', 'SET', 'SMALLINT', 'REAL', 'TEXT', 'TIME', + 'TIMESTAMP', 'TINYBLOB', 'TINYINT', 'TINYTEXT', 'VARBINARY', 'VARCHAR', + 'YEAR', 'dialect' +) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/base.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/base.py new file mode 100644 index 0000000..1b25e14 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/base.py @@ -0,0 +1,2438 @@ +# mysql/base.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +r""" + +.. dialect:: mysql + :name: MySQL + +Supported Versions and Features +------------------------------- + +SQLAlchemy supports MySQL starting with version 4.1 through modern releases. +However, no heroic measures are taken to work around major missing +SQL features - if your server version does not support sub-selects, for +example, they won't work in SQLAlchemy either. + +See the official MySQL documentation for detailed information about features +supported in any given server release. + +.. _mysql_connection_timeouts: + +Connection Timeouts and Disconnects +----------------------------------- + +MySQL features an automatic connection close behavior, for connections that +have been idle for a fixed period of time, defaulting to eight hours. +To circumvent having this issue, use +the :paramref:`.create_engine.pool_recycle` option which ensures that +a connection will be discarded and replaced with a new one if it has been +present in the pool for a fixed number of seconds:: + + engine = create_engine('mysql+mysqldb://...', pool_recycle=3600) + +For more comprehensive disconnect detection of pooled connections, including +accommodation of server restarts and network issues, a pre-ping approach may +be employed. See :ref:`pool_disconnects` for current approaches. + +.. seealso:: + + :ref:`pool_disconnects` - Background on several techniques for dealing + with timed out connections as well as database restarts. + +.. _mysql_storage_engines: + +CREATE TABLE arguments including Storage Engines +------------------------------------------------ + +MySQL's CREATE TABLE syntax includes a wide array of special options, +including ``ENGINE``, ``CHARSET``, ``MAX_ROWS``, ``ROW_FORMAT``, +``INSERT_METHOD``, and many more. +To accommodate the rendering of these arguments, specify the form +``mysql_argument_name="value"``. For example, to specify a table with +``ENGINE`` of ``InnoDB``, ``CHARSET`` of ``utf8mb4``, and ``KEY_BLOCK_SIZE`` +of ``1024``:: + + Table('mytable', metadata, + Column('data', String(32)), + mysql_engine='InnoDB', + mysql_charset='utf8mb4', + mysql_key_block_size="1024" + ) + +The MySQL dialect will normally transfer any keyword specified as +``mysql_keyword_name`` to be rendered as ``KEYWORD_NAME`` in the +``CREATE TABLE`` statement. A handful of these names will render with a space +instead of an underscore; to support this, the MySQL dialect has awareness of +these particular names, which include ``DATA DIRECTORY`` +(e.g. ``mysql_data_directory``), ``CHARACTER SET`` (e.g. +``mysql_character_set``) and ``INDEX DIRECTORY`` (e.g. +``mysql_index_directory``). + +The most common argument is ``mysql_engine``, which refers to the storage +engine for the table. Historically, MySQL server installations would default +to ``MyISAM`` for this value, although newer versions may be defaulting +to ``InnoDB``. The ``InnoDB`` engine is typically preferred for its support +of transactions and foreign keys. + +A :class:`.Table` that is created in a MySQL database with a storage engine +of ``MyISAM`` will be essentially non-transactional, meaning any +INSERT/UPDATE/DELETE statement referring to this table will be invoked as +autocommit. It also will have no support for foreign key constraints; while +the ``CREATE TABLE`` statement accepts foreign key options, when using the +``MyISAM`` storage engine these arguments are discarded. Reflecting such a +table will also produce no foreign key constraint information. + +For fully atomic transactions as well as support for foreign key +constraints, all participating ``CREATE TABLE`` statements must specify a +transactional engine, which in the vast majority of cases is ``InnoDB``. + +.. seealso:: + + `The InnoDB Storage Engine + <http://dev.mysql.com/doc/refman/5.0/en/innodb-storage-engine.html>`_ - + on the MySQL website. + +Case Sensitivity and Table Reflection +------------------------------------- + +MySQL has inconsistent support for case-sensitive identifier +names, basing support on specific details of the underlying +operating system. However, it has been observed that no matter +what case sensitivity behavior is present, the names of tables in +foreign key declarations are *always* received from the database +as all-lower case, making it impossible to accurately reflect a +schema where inter-related tables use mixed-case identifier names. + +Therefore it is strongly advised that table names be declared as +all lower case both within SQLAlchemy as well as on the MySQL +database itself, especially if database reflection features are +to be used. + +.. _mysql_isolation_level: + +Transaction Isolation Level +--------------------------- + +All MySQL dialects support setting of transaction isolation level +both via a dialect-specific parameter :paramref:`.create_engine.isolation_level` +accepted by :func:`.create_engine`, +as well as the :paramref:`.Connection.execution_options.isolation_level` +argument as passed to :meth:`.Connection.execution_options`. +This feature works by issuing the command +``SET SESSION TRANSACTION ISOLATION LEVEL <level>`` for +each new connection. For the special AUTOCOMMIT isolation level, DBAPI-specific +techniques are used. + +To set isolation level using :func:`.create_engine`:: + + engine = create_engine( + "mysql://scott:tiger@localhost/test", + isolation_level="READ UNCOMMITTED" + ) + +To set using per-connection execution options:: + + connection = engine.connect() + connection = connection.execution_options( + isolation_level="READ COMMITTED" + ) + +Valid values for ``isolation_level`` include: + +* ``READ COMMITTED`` +* ``READ UNCOMMITTED`` +* ``REPEATABLE READ`` +* ``SERIALIZABLE`` +* ``AUTOCOMMIT`` + +The special ``AUTOCOMMIT`` value makes use of the various "autocommit" +attributes provided by specific DBAPIs, and is currently supported by +MySQLdb, MySQL-Client, MySQL-Connector Python, and PyMySQL. Using it, +the MySQL connection will return true for the value of +``SELECT @@autocommit;``. + +.. versionadded:: 1.1 - added support for the AUTOCOMMIT isolation level. + +AUTO_INCREMENT Behavior +----------------------- + +When creating tables, SQLAlchemy will automatically set ``AUTO_INCREMENT`` on +the first :class:`.Integer` primary key column which is not marked as a +foreign key:: + + >>> t = Table('mytable', metadata, + ... Column('mytable_id', Integer, primary_key=True) + ... ) + >>> t.create() + CREATE TABLE mytable ( + id INTEGER NOT NULL AUTO_INCREMENT, + PRIMARY KEY (id) + ) + +You can disable this behavior by passing ``False`` to the +:paramref:`~.Column.autoincrement` argument of :class:`.Column`. This flag +can also be used to enable auto-increment on a secondary column in a +multi-column key for some storage engines:: + + Table('mytable', metadata, + Column('gid', Integer, primary_key=True, autoincrement=False), + Column('id', Integer, primary_key=True) + ) + +.. _mysql_ss_cursors: + +Server Side Cursors +------------------- + +Server-side cursor support is available for the MySQLdb and PyMySQL dialects. +From a MySQL point of view this means that the ``MySQLdb.cursors.SSCursor`` or +``pymysql.cursors.SSCursor`` class is used when building up the cursor which +will receive results. The most typical way of invoking this feature is via the +:paramref:`.Connection.execution_options.stream_results` connection execution +option. Server side cursors can also be enabled for all SELECT statements +unconditionally by passing ``server_side_cursors=True`` to +:func:`.create_engine`. + +.. versionadded:: 1.1.4 - added server-side cursor support. + +.. _mysql_unicode: + +Unicode +------- + +Charset Selection +~~~~~~~~~~~~~~~~~ + +Most MySQL DBAPIs offer the option to set the client character set for +a connection. This is typically delivered using the ``charset`` parameter +in the URL, such as:: + + e = create_engine( + "mysql+pymysql://scott:tiger@localhost/test?charset=utf8mb4") + +This charset is the **client character set** for the connection. Some +MySQL DBAPIs will default this to a value such as ``latin1``, and some +will make use of the ``default-character-set`` setting in the ``my.cnf`` +file as well. Documentation for the DBAPI in use should be consulted +for specific behavior. + +The encoding used for Unicode has traditionally been ``'utf8'``. However, +for MySQL versions 5.5.3 on forward, a new MySQL-specific encoding +``'utf8mb4'`` has been introduced, and as of MySQL 8.0 a warning is emitted +by the server if plain ``utf8`` is specified within any server-side +directives, replaced with ``utf8mb3``. The rationale for this new encoding +is due to the fact that MySQL's legacy utf-8 encoding only supports +codepoints up to three bytes instead of four. Therefore, +when communicating with a MySQL database +that includes codepoints more than three bytes in size, +this new charset is preferred, if supported by both the database as well +as the client DBAPI, as in:: + + e = create_engine( + "mysql+pymysql://scott:tiger@localhost/test?charset=utf8mb4") + +All modern DBAPIs should support the ``utf8mb4`` charset. + +In order to use ``utf8mb4`` encoding for a schema that was created with legacy +``utf8``, changes to the MySQL schema and/or server configuration may be +required. + +.. seealso:: + + `The utf8mb4 Character Set \ + <http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html>`_ - \ + in the MySQL documentation + +.. _mysql_binary_introducer: + +Dealing with Binary Data Warnings and Unicode +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +MySQL versions 5.6, 5.7 and later (not MariaDB at the time of this writing) now +emit a warning when attempting to pass binary data to the database, while a +character set encoding is also in place, when the binary data itself is not +valid for that encoding:: + + default.py:509: Warning: (1300, "Invalid utf8mb4 character string: 'F9876A'") + cursor.execute(statement, parameters) + +This warning is due to the fact that the MySQL client library is attempting to +interpret the binary string as a unicode object even if a datatype such as +:class:`.LargeBinary` is in use. To resolve this, the SQL statement requires +a binary "character set introducer" be present before any non-NULL value +that renders like this:: + + INSERT INTO table (data) VALUES (_binary %s) + +These character set introducers are provided by the DBAPI driver, assuming +the use of mysqlclient or PyMySQL (both of which are recommended). Add the +query string parameter ``binary_prefix=true`` to the URL to repair this warning:: + + # mysqlclient + engine = create_engine("mysql+mysqldb://scott:tiger@localhost/test?charset=utf8mb4&binary_prefix=true") + + # PyMySQL + engine = create_engine("mysql+pymysql://scott:tiger@localhost/test?charset=utf8mb4&binary_prefix=true") + +The ``binary_prefix`` flag may or may not be supported by other MySQL drivers. + +SQLAlchemy itself cannot render this ``_binary`` prefix reliably, as it does not +work with the NULL value, which is valid to be sent as a bound parameter. +As the MySQL driver renders parameters directly into the SQL string, it's the +most efficient place for this additional keyword to be passed. + +.. seealso:: + + `Character set introducers <https://dev.mysql.com/doc/refman/5.7/en/charset-introducer.html>`_ - on the MySQL website + + +Ansi Quoting Style +------------------ + +MySQL features two varieties of identifier "quoting style", one using +backticks and the other using quotes, e.g. ```some_identifier``` vs. +``"some_identifier"``. All MySQL dialects detect which version +is in use by checking the value of ``sql_mode`` when a connection is first +established with a particular :class:`.Engine`. This quoting style comes +into play when rendering table and column names as well as when reflecting +existing database structures. The detection is entirely automatic and +no special configuration is needed to use either quoting style. + +.. versionchanged:: 0.6 detection of ANSI quoting style is entirely automatic, + there's no longer any end-user ``create_engine()`` options in this regard. + +MySQL SQL Extensions +-------------------- + +Many of the MySQL SQL extensions are handled through SQLAlchemy's generic +function and operator support:: + + table.select(table.c.password==func.md5('plaintext')) + table.select(table.c.username.op('regexp')('^[a-d]')) + +And of course any valid MySQL statement can be executed as a string as well. + +Some limited direct support for MySQL extensions to SQL is currently +available. + +* INSERT..ON DUPLICATE KEY UPDATE: See :ref:`mysql_insert_on_duplicate_key_update` + +* SELECT pragma:: + + select(..., prefixes=['HIGH_PRIORITY', 'SQL_SMALL_RESULT']) + +* UPDATE with LIMIT:: + + update(..., mysql_limit=10) + +.. _mysql_insert_on_duplicate_key_update: + +INSERT...ON DUPLICATE KEY UPDATE (Upsert) +------------------------------------------ + +MySQL allows "upserts" (update or insert) +of rows into a table via the ``ON DUPLICATE KEY UPDATE`` clause of the +``INSERT`` statement. A candidate row will only be inserted if that row does +not match an existing primary or unique key in the table; otherwise, an UPDATE +will be performed. The statement allows for separate specification of the +values to INSERT versus the values for UPDATE. + +SQLAlchemy provides ``ON DUPLICATE KEY UPDATE`` support via the MySQL-specific +:func:`.mysql.dml.insert()` function, which provides +the generative method :meth:`~.mysql.dml.Insert.on_duplicate_key_update`:: + + from sqlalchemy.dialects.mysql import insert + + insert_stmt = insert(my_table).values( + id='some_existing_id', + data='inserted value') + + on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update( + data=insert_stmt.inserted.data, + status='U' + ) + + conn.execute(on_duplicate_key_stmt) + +Unlike Postgresql's "ON CONFLICT" phrase, the "ON DUPLICATE KEY UPDATE" +phrase will always match on any primary key or unique key, and will always +perform an UPDATE if there's a match; there are no options for it to raise +an error or to skip performing an UPDATE. + +``ON DUPLICATE KEY UPDATE`` is used to perform an update of the already +existing row, using any combination of new values as well as values +from the proposed insertion. These values are specified using +keyword arguments passed to the +:meth:`~.mysql.dml.Insert.on_duplicate_key_update` +given column key values (usually the name of the column, unless it +specifies :paramref:`.Column.key`) as keys and literal or SQL expressions +as values:: + + on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update( + data="some data" + updated_at=func.current_timestamp() + ) + +.. warning:: + + The :meth:`.Insert.on_duplicate_key_update` method does **not** take into + account Python-side default UPDATE values or generation functions, e.g. + e.g. those specified using :paramref:`.Column.onupdate`. + These values will not be exercised for an ON DUPLICATE KEY style of UPDATE, + unless they are manually specified explicitly in the parameters. + +In order to refer to the proposed insertion row, the special alias +:attr:`~.mysql.dml.Insert.inserted` is available as an attribute on +the :class:`.mysql.dml.Insert` object; this object is a +:class:`.ColumnCollection` which contains all columns of the target +table:: + + from sqlalchemy.dialects.mysql import insert + + stmt = insert(my_table).values( + id='some_id', + data='inserted value', + author='jlh') + do_update_stmt = stmt.on_duplicate_key_update( + data="updated value", + author=stmt.inserted.author + ) + conn.execute(do_update_stmt) + +When rendered, the "inserted" namespace will produce the expression +``VALUES(<columnname>)``. + +.. versionadded:: 1.2 Added support for MySQL ON DUPLICATE KEY UPDATE clause + + + +rowcount Support +---------------- + +SQLAlchemy standardizes the DBAPI ``cursor.rowcount`` attribute to be the +usual definition of "number of rows matched by an UPDATE or DELETE" statement. +This is in contradiction to the default setting on most MySQL DBAPI drivers, +which is "number of rows actually modified/deleted". For this reason, the +SQLAlchemy MySQL dialects always add the ``constants.CLIENT.FOUND_ROWS`` +flag, or whatever is equivalent for the target dialect, upon connection. +This setting is currently hardcoded. + +.. seealso:: + + :attr:`.ResultProxy.rowcount` + + +CAST Support +------------ + +MySQL documents the CAST operator as available in version 4.0.2. When using +the SQLAlchemy :func:`.cast` function, SQLAlchemy +will not render the CAST token on MySQL before this version, based on server +version detection, instead rendering the internal expression directly. + +CAST may still not be desirable on an early MySQL version post-4.0.2, as it +didn't add all datatype support until 4.1.1. If your application falls into +this narrow area, the behavior of CAST can be controlled using the +:ref:`sqlalchemy.ext.compiler_toplevel` system, as per the recipe below:: + + from sqlalchemy.sql.expression import Cast + from sqlalchemy.ext.compiler import compiles + + @compiles(Cast, 'mysql') + def _check_mysql_version(element, compiler, **kw): + if compiler.dialect.server_version_info < (4, 1, 0): + return compiler.process(element.clause, **kw) + else: + return compiler.visit_cast(element, **kw) + +The above function, which only needs to be declared once +within an application, overrides the compilation of the +:func:`.cast` construct to check for version 4.1.0 before +fully rendering CAST; else the internal element of the +construct is rendered directly. + + +.. _mysql_indexes: + +MySQL Specific Index Options +---------------------------- + +MySQL-specific extensions to the :class:`.Index` construct are available. + +Index Length +~~~~~~~~~~~~~ + +MySQL provides an option to create index entries with a certain length, where +"length" refers to the number of characters or bytes in each value which will +become part of the index. SQLAlchemy provides this feature via the +``mysql_length`` parameter:: + + Index('my_index', my_table.c.data, mysql_length=10) + + Index('a_b_idx', my_table.c.a, my_table.c.b, mysql_length={'a': 4, + 'b': 9}) + +Prefix lengths are given in characters for nonbinary string types and in bytes +for binary string types. The value passed to the keyword argument *must* be +either an integer (and, thus, specify the same prefix length value for all +columns of the index) or a dict in which keys are column names and values are +prefix length values for corresponding columns. MySQL only allows a length for +a column of an index if it is for a CHAR, VARCHAR, TEXT, BINARY, VARBINARY and +BLOB. + +.. versionadded:: 0.8.2 ``mysql_length`` may now be specified as a dictionary + for use with composite indexes. + +Index Prefixes +~~~~~~~~~~~~~~ + +MySQL storage engines permit you to specify an index prefix when creating +an index. SQLAlchemy provides this feature via the +``mysql_prefix`` parameter on :class:`.Index`:: + + Index('my_index', my_table.c.data, mysql_prefix='FULLTEXT') + +The value passed to the keyword argument will be simply passed through to the +underlying CREATE INDEX, so it *must* be a valid index prefix for your MySQL +storage engine. + +.. versionadded:: 1.1.5 + +.. seealso:: + + `CREATE INDEX <http://dev.mysql.com/doc/refman/5.0/en/create-index.html>`_ - \ + MySQL documentation + +Index Types +~~~~~~~~~~~~~ + +Some MySQL storage engines permit you to specify an index type when creating +an index or primary key constraint. SQLAlchemy provides this feature via the +``mysql_using`` parameter on :class:`.Index`:: + + Index('my_index', my_table.c.data, mysql_using='hash') + +As well as the ``mysql_using`` parameter on :class:`.PrimaryKeyConstraint`:: + + PrimaryKeyConstraint("data", mysql_using='hash') + +The value passed to the keyword argument will be simply passed through to the +underlying CREATE INDEX or PRIMARY KEY clause, so it *must* be a valid index +type for your MySQL storage engine. + +More information can be found at: + +http://dev.mysql.com/doc/refman/5.0/en/create-index.html + +http://dev.mysql.com/doc/refman/5.0/en/create-table.html + +.. _mysql_foreign_keys: + +MySQL Foreign Keys +------------------ + +MySQL's behavior regarding foreign keys has some important caveats. + +Foreign Key Arguments to Avoid +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +MySQL does not support the foreign key arguments "DEFERRABLE", "INITIALLY", +or "MATCH". Using the ``deferrable`` or ``initially`` keyword argument with +:class:`.ForeignKeyConstraint` or :class:`.ForeignKey` will have the effect of +these keywords being rendered in a DDL expression, which will then raise an +error on MySQL. In order to use these keywords on a foreign key while having +them ignored on a MySQL backend, use a custom compile rule:: + + from sqlalchemy.ext.compiler import compiles + from sqlalchemy.schema import ForeignKeyConstraint + + @compiles(ForeignKeyConstraint, "mysql") + def process(element, compiler, **kw): + element.deferrable = element.initially = None + return compiler.visit_foreign_key_constraint(element, **kw) + +.. versionchanged:: 0.9.0 - the MySQL backend no longer silently ignores + the ``deferrable`` or ``initially`` keyword arguments of + :class:`.ForeignKeyConstraint` and :class:`.ForeignKey`. + +The "MATCH" keyword is in fact more insidious, and is explicitly disallowed +by SQLAlchemy in conjunction with the MySQL backend. This argument is +silently ignored by MySQL, but in addition has the effect of ON UPDATE and ON +DELETE options also being ignored by the backend. Therefore MATCH should +never be used with the MySQL backend; as is the case with DEFERRABLE and +INITIALLY, custom compilation rules can be used to correct a MySQL +ForeignKeyConstraint at DDL definition time. + +.. versionadded:: 0.9.0 - the MySQL backend will raise a + :class:`.CompileError` when the ``match`` keyword is used with + :class:`.ForeignKeyConstraint` or :class:`.ForeignKey`. + +Reflection of Foreign Key Constraints +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Not all MySQL storage engines support foreign keys. When using the +very common ``MyISAM`` MySQL storage engine, the information loaded by table +reflection will not include foreign keys. For these tables, you may supply a +:class:`~sqlalchemy.ForeignKeyConstraint` at reflection time:: + + Table('mytable', metadata, + ForeignKeyConstraint(['other_id'], ['othertable.other_id']), + autoload=True + ) + +.. seealso:: + + :ref:`mysql_storage_engines` + +.. _mysql_unique_constraints: + +MySQL Unique Constraints and Reflection +--------------------------------------- + +SQLAlchemy supports both the :class:`.Index` construct with the +flag ``unique=True``, indicating a UNIQUE index, as well as the +:class:`.UniqueConstraint` construct, representing a UNIQUE constraint. +Both objects/syntaxes are supported by MySQL when emitting DDL to create +these constraints. However, MySQL does not have a unique constraint +construct that is separate from a unique index; that is, the "UNIQUE" +constraint on MySQL is equivalent to creating a "UNIQUE INDEX". + +When reflecting these constructs, the :meth:`.Inspector.get_indexes` +and the :meth:`.Inspector.get_unique_constraints` methods will **both** +return an entry for a UNIQUE index in MySQL. However, when performing +full table reflection using ``Table(..., autoload=True)``, +the :class:`.UniqueConstraint` construct is +**not** part of the fully reflected :class:`.Table` construct under any +circumstances; this construct is always represented by a :class:`.Index` +with the ``unique=True`` setting present in the :attr:`.Table.indexes` +collection. + + +.. _mysql_timestamp_null: + +TIMESTAMP Columns and NULL +-------------------------- + +MySQL historically enforces that a column which specifies the +TIMESTAMP datatype implicitly includes a default value of +CURRENT_TIMESTAMP, even though this is not stated, and additionally +sets the column as NOT NULL, the opposite behavior vs. that of all +other datatypes:: + + mysql> CREATE TABLE ts_test ( + -> a INTEGER, + -> b INTEGER NOT NULL, + -> c TIMESTAMP, + -> d TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + -> e TIMESTAMP NULL); + Query OK, 0 rows affected (0.03 sec) + + mysql> SHOW CREATE TABLE ts_test; + +---------+----------------------------------------------------- + | Table | Create Table + +---------+----------------------------------------------------- + | ts_test | CREATE TABLE `ts_test` ( + `a` int(11) DEFAULT NULL, + `b` int(11) NOT NULL, + `c` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `d` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `e` timestamp NULL DEFAULT NULL + ) ENGINE=MyISAM DEFAULT CHARSET=latin1 + +Above, we see that an INTEGER column defaults to NULL, unless it is specified +with NOT NULL. But when the column is of type TIMESTAMP, an implicit +default of CURRENT_TIMESTAMP is generated which also coerces the column +to be a NOT NULL, even though we did not specify it as such. + +This behavior of MySQL can be changed on the MySQL side using the +`explicit_defaults_for_timestamp +<http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html +#sysvar_explicit_defaults_for_timestamp>`_ configuration flag introduced in +MySQL 5.6. With this server setting enabled, TIMESTAMP columns behave like +any other datatype on the MySQL side with regards to defaults and nullability. + +However, to accommodate the vast majority of MySQL databases that do not +specify this new flag, SQLAlchemy emits the "NULL" specifier explicitly with +any TIMESTAMP column that does not specify ``nullable=False``. In order +to accommodate newer databases that specify ``explicit_defaults_for_timestamp``, +SQLAlchemy also emits NOT NULL for TIMESTAMP columns that do specify +``nullable=False``. The following example illustrates:: + + from sqlalchemy import MetaData, Integer, Table, Column, text + from sqlalchemy.dialects.mysql import TIMESTAMP + + m = MetaData() + t = Table('ts_test', m, + Column('a', Integer), + Column('b', Integer, nullable=False), + Column('c', TIMESTAMP), + Column('d', TIMESTAMP, nullable=False) + ) + + + from sqlalchemy import create_engine + e = create_engine("mysql://scott:tiger@localhost/test", echo=True) + m.create_all(e) + +output:: + + CREATE TABLE ts_test ( + a INTEGER, + b INTEGER NOT NULL, + c TIMESTAMP NULL, + d TIMESTAMP NOT NULL + ) + +.. versionchanged:: 1.0.0 - SQLAlchemy now renders NULL or NOT NULL in all + cases for TIMESTAMP columns, to accommodate + ``explicit_defaults_for_timestamp``. Prior to this version, it will + not render "NOT NULL" for a TIMESTAMP column that is ``nullable=False``. + +""" + +from collections import defaultdict +import re +import sys +import json + +from ... import schema as sa_schema +from ... import exc, log, sql, util +from ...sql import compiler, elements +from array import array as _array + +from ...engine import reflection +from ...engine import default +from ... import types as sqltypes +from ...util import topological +from ...types import DATE, BOOLEAN, \ + BLOB, BINARY, VARBINARY + +from . import reflection as _reflection +from .types import BIGINT, BIT, CHAR, DECIMAL, DATETIME, \ + DOUBLE, FLOAT, INTEGER, LONGBLOB, LONGTEXT, MEDIUMBLOB, MEDIUMINT, \ + MEDIUMTEXT, NCHAR, NUMERIC, NVARCHAR, REAL, SMALLINT, TEXT, TIME, \ + TIMESTAMP, TINYBLOB, TINYINT, TINYTEXT, VARCHAR, YEAR +from .types import _StringType, _IntegerType, _NumericType, \ + _FloatType, _MatchType +from .enumerated import ENUM, SET +from .json import JSON, JSONIndexType, JSONPathType + + +RESERVED_WORDS = set( + ['accessible', 'add', 'all', 'alter', 'analyze', 'and', 'as', 'asc', + 'asensitive', 'before', 'between', 'bigint', 'binary', 'blob', 'both', + 'by', 'call', 'cascade', 'case', 'change', 'char', 'character', 'check', + 'collate', 'column', 'condition', 'constraint', 'continue', 'convert', + 'create', 'cross', 'current_date', 'current_time', 'current_timestamp', + 'current_user', 'cursor', 'database', 'databases', 'day_hour', + 'day_microsecond', 'day_minute', 'day_second', 'dec', 'decimal', + 'declare', 'default', 'delayed', 'delete', 'desc', 'describe', + 'deterministic', 'distinct', 'distinctrow', 'div', 'double', 'drop', + 'dual', 'each', 'else', 'elseif', 'enclosed', 'escaped', 'exists', + 'exit', 'explain', 'false', 'fetch', 'float', 'float4', 'float8', + 'for', 'force', 'foreign', 'from', 'fulltext', 'grant', 'group', + 'having', 'high_priority', 'hour_microsecond', 'hour_minute', + 'hour_second', 'if', 'ignore', 'in', 'index', 'infile', 'inner', 'inout', + 'insensitive', 'insert', 'int', 'int1', 'int2', 'int3', 'int4', 'int8', + 'integer', 'interval', 'into', 'is', 'iterate', 'join', 'key', 'keys', + 'kill', 'leading', 'leave', 'left', 'like', 'limit', 'linear', 'lines', + 'load', 'localtime', 'localtimestamp', 'lock', 'long', 'longblob', + 'longtext', 'loop', 'low_priority', 'master_ssl_verify_server_cert', + 'match', 'mediumblob', 'mediumint', 'mediumtext', 'middleint', + 'minute_microsecond', 'minute_second', 'mod', 'modifies', 'natural', + 'not', 'no_write_to_binlog', 'null', 'numeric', 'on', 'optimize', + 'option', 'optionally', 'or', 'order', 'out', 'outer', 'outfile', + 'precision', 'primary', 'procedure', 'purge', 'range', 'read', 'reads', + 'read_only', 'read_write', 'real', 'references', 'regexp', 'release', + 'rename', 'repeat', 'replace', 'require', 'restrict', 'return', + 'revoke', 'right', 'rlike', 'schema', 'schemas', 'second_microsecond', + 'select', 'sensitive', 'separator', 'set', 'show', 'smallint', 'spatial', + 'specific', 'sql', 'sqlexception', 'sqlstate', 'sqlwarning', + 'sql_big_result', 'sql_calc_found_rows', 'sql_small_result', 'ssl', + 'starting', 'straight_join', 'table', 'terminated', 'then', 'tinyblob', + 'tinyint', 'tinytext', 'to', 'trailing', 'trigger', 'true', 'undo', + 'union', 'unique', 'unlock', 'unsigned', 'update', 'usage', 'use', + 'using', 'utc_date', 'utc_time', 'utc_timestamp', 'values', 'varbinary', + 'varchar', 'varcharacter', 'varying', 'when', 'where', 'while', 'with', + + 'write', 'x509', 'xor', 'year_month', 'zerofill', # 5.0 + + 'columns', 'fields', 'privileges', 'soname', 'tables', # 4.1 + + 'accessible', 'linear', 'master_ssl_verify_server_cert', 'range', + 'read_only', 'read_write', # 5.1 + + 'general', 'ignore_server_ids', 'master_heartbeat_period', 'maxvalue', + 'resignal', 'signal', 'slow', # 5.5 + + 'get', 'io_after_gtids', 'io_before_gtids', 'master_bind', 'one_shot', + 'partition', 'sql_after_gtids', 'sql_before_gtids', # 5.6 + + 'generated', 'optimizer_costs', 'stored', 'virtual', # 5.7 + + 'admin', 'cume_dist', 'empty', 'except', 'first_value', 'grouping', + 'function', 'groups', 'json_table', 'last_value', 'nth_value', + 'ntile', 'of', 'over', 'percent_rank', 'persist', 'persist_only', + 'rank', 'recursive', 'role', 'row', 'rows', 'row_number', 'system', + 'window', # 8.0 + ]) + +AUTOCOMMIT_RE = re.compile( + r'\s*(?:UPDATE|INSERT|CREATE|DELETE|DROP|ALTER|LOAD +DATA|REPLACE)', + re.I | re.UNICODE) +SET_RE = re.compile( + r'\s*SET\s+(?:(?:GLOBAL|SESSION)\s+)?\w', + re.I | re.UNICODE) + + +# old names +MSTime = TIME +MSSet = SET +MSEnum = ENUM +MSLongBlob = LONGBLOB +MSMediumBlob = MEDIUMBLOB +MSTinyBlob = TINYBLOB +MSBlob = BLOB +MSBinary = BINARY +MSVarBinary = VARBINARY +MSNChar = NCHAR +MSNVarChar = NVARCHAR +MSChar = CHAR +MSString = VARCHAR +MSLongText = LONGTEXT +MSMediumText = MEDIUMTEXT +MSTinyText = TINYTEXT +MSText = TEXT +MSYear = YEAR +MSTimeStamp = TIMESTAMP +MSBit = BIT +MSSmallInteger = SMALLINT +MSTinyInteger = TINYINT +MSMediumInteger = MEDIUMINT +MSBigInteger = BIGINT +MSNumeric = NUMERIC +MSDecimal = DECIMAL +MSDouble = DOUBLE +MSReal = REAL +MSFloat = FLOAT +MSInteger = INTEGER + +colspecs = { + _IntegerType: _IntegerType, + _NumericType: _NumericType, + _FloatType: _FloatType, + sqltypes.Numeric: NUMERIC, + sqltypes.Float: FLOAT, + sqltypes.Time: TIME, + sqltypes.Enum: ENUM, + sqltypes.MatchType: _MatchType, + sqltypes.JSON: JSON, + sqltypes.JSON.JSONIndexType: JSONIndexType, + sqltypes.JSON.JSONPathType: JSONPathType + +} + +# Everything 3.23 through 5.1 excepting OpenGIS types. +ischema_names = { + 'bigint': BIGINT, + 'binary': BINARY, + 'bit': BIT, + 'blob': BLOB, + 'boolean': BOOLEAN, + 'char': CHAR, + 'date': DATE, + 'datetime': DATETIME, + 'decimal': DECIMAL, + 'double': DOUBLE, + 'enum': ENUM, + 'fixed': DECIMAL, + 'float': FLOAT, + 'int': INTEGER, + 'integer': INTEGER, + 'json': JSON, + 'longblob': LONGBLOB, + 'longtext': LONGTEXT, + 'mediumblob': MEDIUMBLOB, + 'mediumint': MEDIUMINT, + 'mediumtext': MEDIUMTEXT, + 'nchar': NCHAR, + 'nvarchar': NVARCHAR, + 'numeric': NUMERIC, + 'set': SET, + 'smallint': SMALLINT, + 'text': TEXT, + 'time': TIME, + 'timestamp': TIMESTAMP, + 'tinyblob': TINYBLOB, + 'tinyint': TINYINT, + 'tinytext': TINYTEXT, + 'varbinary': VARBINARY, + 'varchar': VARCHAR, + 'year': YEAR, +} + + +class MySQLExecutionContext(default.DefaultExecutionContext): + + def should_autocommit_text(self, statement): + return AUTOCOMMIT_RE.match(statement) + + def create_server_side_cursor(self): + if self.dialect.supports_server_side_cursors: + return self._dbapi_connection.cursor(self.dialect._sscursor) + else: + raise NotImplementedError() + + +class MySQLCompiler(compiler.SQLCompiler): + + render_table_with_column_in_update_from = True + """Overridden from base SQLCompiler value""" + + extract_map = compiler.SQLCompiler.extract_map.copy() + extract_map.update({'milliseconds': 'millisecond'}) + + def visit_random_func(self, fn, **kw): + return "rand%s" % self.function_argspec(fn) + + def visit_sysdate_func(self, fn, **kw): + return "SYSDATE()" + + def visit_json_getitem_op_binary(self, binary, operator, **kw): + return "JSON_EXTRACT(%s, %s)" % ( + self.process(binary.left, **kw), + self.process(binary.right, **kw)) + + def visit_json_path_getitem_op_binary(self, binary, operator, **kw): + return "JSON_EXTRACT(%s, %s)" % ( + self.process(binary.left, **kw), + self.process(binary.right, **kw)) + + def visit_on_duplicate_key_update(self, on_duplicate, **kw): + cols = self.statement.table.c + + clauses = [] + # traverse in table column order + for column in cols: + val = on_duplicate.update.get(column.key) + if val is None: + continue + elif elements._is_literal(val): + val = elements.BindParameter(None, val, type_=column.type) + value_text = self.process(val.self_group(), use_schema=False) + elif isinstance(val, elements.BindParameter) and val.type._isnull: + val = val._clone() + val.type = column.type + value_text = self.process(val.self_group(), use_schema=False) + elif isinstance(val, elements.ColumnClause) \ + and val.table is on_duplicate.inserted_alias: + value_text = 'VALUES(' + self.preparer.quote(column.name) + ')' + else: + value_text = self.process(val.self_group(), use_schema=False) + name_text = self.preparer.quote(column.name) + clauses.append("%s = %s" % (name_text, value_text)) + + non_matching = set(on_duplicate.update) - set(cols.keys()) + if non_matching: + util.warn( + 'Additional column names not matching ' + "any column keys in table '%s': %s" % ( + self.statement.table.name, + (', '.join("'%s'" % c for c in non_matching)) + ) + ) + + return 'ON DUPLICATE KEY UPDATE ' + ', '.join(clauses) + + def visit_concat_op_binary(self, binary, operator, **kw): + return "concat(%s, %s)" % (self.process(binary.left, **kw), + self.process(binary.right, **kw)) + + def visit_match_op_binary(self, binary, operator, **kw): + return "MATCH (%s) AGAINST (%s IN BOOLEAN MODE)" % \ + (self.process(binary.left, **kw), self.process(binary.right, **kw)) + + def get_from_hint_text(self, table, text): + return text + + def visit_typeclause(self, typeclause, type_=None, **kw): + if type_ is None: + type_ = typeclause.type.dialect_impl(self.dialect) + if isinstance(type_, sqltypes.TypeDecorator): + return self.visit_typeclause(typeclause, type_.impl, **kw) + elif isinstance(type_, sqltypes.Integer): + if getattr(type_, 'unsigned', False): + return 'UNSIGNED INTEGER' + else: + return 'SIGNED INTEGER' + elif isinstance(type_, sqltypes.TIMESTAMP): + return 'DATETIME' + elif isinstance(type_, (sqltypes.DECIMAL, sqltypes.DateTime, + sqltypes.Date, sqltypes.Time)): + return self.dialect.type_compiler.process(type_) + elif isinstance(type_, sqltypes.String) \ + and not isinstance(type_, (ENUM, SET)): + adapted = CHAR._adapt_string_for_cast(type_) + return self.dialect.type_compiler.process(adapted) + elif isinstance(type_, sqltypes._Binary): + return 'BINARY' + elif isinstance(type_, sqltypes.JSON): + return "JSON" + elif isinstance(type_, sqltypes.NUMERIC): + return self.dialect.type_compiler.process( + type_).replace('NUMERIC', 'DECIMAL') + else: + return None + + def visit_cast(self, cast, **kw): + # No cast until 4, no decimals until 5. + if not self.dialect._supports_cast: + util.warn( + "Current MySQL version does not support " + "CAST; the CAST will be skipped.") + return self.process(cast.clause.self_group(), **kw) + + type_ = self.process(cast.typeclause) + if type_ is None: + util.warn( + "Datatype %s does not support CAST on MySQL; " + "the CAST will be skipped." % + self.dialect.type_compiler.process(cast.typeclause.type)) + return self.process(cast.clause.self_group(), **kw) + + return 'CAST(%s AS %s)' % (self.process(cast.clause, **kw), type_) + + def render_literal_value(self, value, type_): + value = super(MySQLCompiler, self).render_literal_value(value, type_) + if self.dialect._backslash_escapes: + value = value.replace('\\', '\\\\') + return value + + # override native_boolean=False behavior here, as + # MySQL still supports native boolean + def visit_true(self, element, **kw): + return "true" + + def visit_false(self, element, **kw): + return "false" + + def get_select_precolumns(self, select, **kw): + """Add special MySQL keywords in place of DISTINCT. + + .. note:: + + this usage is deprecated. :meth:`.Select.prefix_with` + should be used for special keywords at the start + of a SELECT. + + """ + if isinstance(select._distinct, util.string_types): + return select._distinct.upper() + " " + elif select._distinct: + return "DISTINCT " + else: + return "" + + def visit_join(self, join, asfrom=False, **kwargs): + if join.full: + join_type = " FULL OUTER JOIN " + elif join.isouter: + join_type = " LEFT OUTER JOIN " + else: + join_type = " INNER JOIN " + + return ''.join( + (self.process(join.left, asfrom=True, **kwargs), + join_type, + self.process(join.right, asfrom=True, **kwargs), + " ON ", + self.process(join.onclause, **kwargs))) + + def for_update_clause(self, select, **kw): + if select._for_update_arg.read: + return " LOCK IN SHARE MODE" + else: + return " FOR UPDATE" + + def limit_clause(self, select, **kw): + # MySQL supports: + # LIMIT <limit> + # LIMIT <offset>, <limit> + # and in server versions > 3.3: + # LIMIT <limit> OFFSET <offset> + # The latter is more readable for offsets but we're stuck with the + # former until we can refine dialects by server revision. + + limit_clause, offset_clause = select._limit_clause, \ + select._offset_clause + + if limit_clause is None and offset_clause is None: + return '' + elif offset_clause is not None: + # As suggested by the MySQL docs, need to apply an + # artificial limit if one wasn't provided + # http://dev.mysql.com/doc/refman/5.0/en/select.html + if limit_clause is None: + # hardwire the upper limit. Currently + # needed by OurSQL with Python 3 + # (https://bugs.launchpad.net/oursql/+bug/686232), + # but also is consistent with the usage of the upper + # bound as part of MySQL's "syntax" for OFFSET with + # no LIMIT + return ' \n LIMIT %s, %s' % ( + self.process(offset_clause, **kw), + "18446744073709551615") + else: + return ' \n LIMIT %s, %s' % ( + self.process(offset_clause, **kw), + self.process(limit_clause, **kw)) + else: + # No offset provided, so just use the limit + return ' \n LIMIT %s' % (self.process(limit_clause, **kw),) + + def update_limit_clause(self, update_stmt): + limit = update_stmt.kwargs.get('%s_limit' % self.dialect.name, None) + if limit: + return "LIMIT %s" % limit + else: + return None + + def update_tables_clause(self, update_stmt, from_table, + extra_froms, **kw): + return ', '.join(t._compiler_dispatch(self, asfrom=True, **kw) + for t in [from_table] + list(extra_froms)) + + def update_from_clause(self, update_stmt, from_table, + extra_froms, from_hints, **kw): + return None + + def delete_table_clause(self, delete_stmt, from_table, + extra_froms): + """If we have extra froms make sure we render any alias as hint.""" + ashint = False + if extra_froms: + ashint = True + return from_table._compiler_dispatch( + self, asfrom=True, iscrud=True, ashint=ashint + ) + + def delete_extra_from_clause(self, delete_stmt, from_table, + extra_froms, from_hints, **kw): + """Render the DELETE .. USING clause specific to MySQL.""" + return "USING " + ', '.join( + t._compiler_dispatch(self, asfrom=True, + fromhints=from_hints, **kw) + for t in [from_table] + extra_froms) + + +class MySQLDDLCompiler(compiler.DDLCompiler): + def get_column_specification(self, column, **kw): + """Builds column DDL.""" + + colspec = [ + self.preparer.format_column(column), + self.dialect.type_compiler.process( + column.type, type_expression=column) + ] + + is_timestamp = isinstance(column.type, sqltypes.TIMESTAMP) + + if not column.nullable: + colspec.append('NOT NULL') + + # see: http://docs.sqlalchemy.org/en/latest/dialects/ + # mysql.html#mysql_timestamp_null + elif column.nullable and is_timestamp: + colspec.append('NULL') + + default = self.get_column_default_string(column) + if default is not None: + colspec.append('DEFAULT ' + default) + + comment = column.comment + if comment is not None: + literal = self.sql_compiler.render_literal_value( + comment, sqltypes.String()) + colspec.append('COMMENT ' + literal) + + if column.table is not None \ + and column is column.table._autoincrement_column and \ + column.server_default is None: + colspec.append('AUTO_INCREMENT') + + return ' '.join(colspec) + + def post_create_table(self, table): + """Build table-level CREATE options like ENGINE and COLLATE.""" + + table_opts = [] + + opts = dict( + ( + k[len(self.dialect.name) + 1:].upper(), + v + ) + for k, v in table.kwargs.items() + if k.startswith('%s_' % self.dialect.name) + ) + + if table.comment is not None: + opts['COMMENT'] = table.comment + + partition_options = [ + 'PARTITION_BY', 'PARTITIONS', 'SUBPARTITIONS', + 'SUBPARTITION_BY' + ] + + nonpart_options = set(opts).difference(partition_options) + part_options = set(opts).intersection(partition_options) + + for opt in topological.sort([ + ('DEFAULT_CHARSET', 'COLLATE'), + ('DEFAULT_CHARACTER_SET', 'COLLATE'), + ], nonpart_options): + arg = opts[opt] + if opt in _reflection._options_of_type_string: + + arg = self.sql_compiler.render_literal_value( + arg, sqltypes.String()) + + if opt in ('DATA_DIRECTORY', 'INDEX_DIRECTORY', + 'DEFAULT_CHARACTER_SET', 'CHARACTER_SET', + 'DEFAULT_CHARSET', + 'DEFAULT_COLLATE'): + opt = opt.replace('_', ' ') + + joiner = '=' + if opt in ('TABLESPACE', 'DEFAULT CHARACTER SET', + 'CHARACTER SET', 'COLLATE'): + joiner = ' ' + + table_opts.append(joiner.join((opt, arg))) + + for opt in topological.sort([ + ('PARTITION_BY', 'PARTITIONS'), + ('PARTITION_BY', 'SUBPARTITION_BY'), + ('PARTITION_BY', 'SUBPARTITIONS'), + ('PARTITIONS', 'SUBPARTITIONS'), + ('PARTITIONS', 'SUBPARTITION_BY'), + ('SUBPARTITION_BY', 'SUBPARTITIONS') + ], part_options): + arg = opts[opt] + if opt in _reflection._options_of_type_string: + arg = self.sql_compiler.render_literal_value( + arg, sqltypes.String()) + + opt = opt.replace('_', ' ') + joiner = ' ' + + table_opts.append(joiner.join((opt, arg))) + + return ' '.join(table_opts) + + def visit_create_index(self, create, **kw): + index = create.element + self._verify_index_table(index) + preparer = self.preparer + table = preparer.format_table(index.table) + columns = [self.sql_compiler.process(expr, include_table=False, + literal_binds=True) + for expr in index.expressions] + + name = self._prepared_index_name(index) + + text = "CREATE " + if index.unique: + text += "UNIQUE " + + index_prefix = index.kwargs.get('mysql_prefix', None) + if index_prefix: + text += index_prefix + ' ' + + text += "INDEX %s ON %s " % (name, table) + + length = index.dialect_options['mysql']['length'] + if length is not None: + + if isinstance(length, dict): + # length value can be a (column_name --> integer value) + # mapping specifying the prefix length for each column of the + # index + columns = ', '.join( + '%s(%d)' % (expr, length[col.name]) if col.name in length + else + ( + '%s(%d)' % (expr, length[expr]) if expr in length + else '%s' % expr + ) + for col, expr in zip(index.expressions, columns) + ) + else: + # or can be an integer value specifying the same + # prefix length for all columns of the index + columns = ', '.join( + '%s(%d)' % (col, length) + for col in columns + ) + else: + columns = ', '.join(columns) + text += '(%s)' % columns + + using = index.dialect_options['mysql']['using'] + if using is not None: + text += " USING %s" % (preparer.quote(using)) + + return text + + def visit_primary_key_constraint(self, constraint): + text = super(MySQLDDLCompiler, self).\ + visit_primary_key_constraint(constraint) + using = constraint.dialect_options['mysql']['using'] + if using: + text += " USING %s" % (self.preparer.quote(using)) + return text + + def visit_drop_index(self, drop): + index = drop.element + + return "\nDROP INDEX %s ON %s" % ( + self._prepared_index_name(index, + include_schema=False), + self.preparer.format_table(index.table)) + + def visit_drop_constraint(self, drop): + constraint = drop.element + if isinstance(constraint, sa_schema.ForeignKeyConstraint): + qual = "FOREIGN KEY " + const = self.preparer.format_constraint(constraint) + elif isinstance(constraint, sa_schema.PrimaryKeyConstraint): + qual = "PRIMARY KEY " + const = "" + elif isinstance(constraint, sa_schema.UniqueConstraint): + qual = "INDEX " + const = self.preparer.format_constraint(constraint) + else: + qual = "" + const = self.preparer.format_constraint(constraint) + return "ALTER TABLE %s DROP %s%s" % \ + (self.preparer.format_table(constraint.table), + qual, const) + + def define_constraint_match(self, constraint): + if constraint.match is not None: + raise exc.CompileError( + "MySQL ignores the 'MATCH' keyword while at the same time " + "causes ON UPDATE/ON DELETE clauses to be ignored.") + return "" + + def visit_set_table_comment(self, create): + return "ALTER TABLE %s COMMENT %s" % ( + self.preparer.format_table(create.element), + self.sql_compiler.render_literal_value( + create.element.comment, sqltypes.String()) + ) + + def visit_set_column_comment(self, create): + return "ALTER TABLE %s CHANGE %s %s" % ( + self.preparer.format_table(create.element.table), + self.preparer.format_column(create.element), + self.get_column_specification(create.element) + ) + + +class MySQLTypeCompiler(compiler.GenericTypeCompiler): + def _extend_numeric(self, type_, spec): + "Extend a numeric-type declaration with MySQL specific extensions." + + if not self._mysql_type(type_): + return spec + + if type_.unsigned: + spec += ' UNSIGNED' + if type_.zerofill: + spec += ' ZEROFILL' + return spec + + def _extend_string(self, type_, defaults, spec): + """Extend a string-type declaration with standard SQL CHARACTER SET / + COLLATE annotations and MySQL specific extensions. + + """ + + def attr(name): + return getattr(type_, name, defaults.get(name)) + + if attr('charset'): + charset = 'CHARACTER SET %s' % attr('charset') + elif attr('ascii'): + charset = 'ASCII' + elif attr('unicode'): + charset = 'UNICODE' + else: + charset = None + + if attr('collation'): + collation = 'COLLATE %s' % type_.collation + elif attr('binary'): + collation = 'BINARY' + else: + collation = None + + if attr('national'): + # NATIONAL (aka NCHAR/NVARCHAR) trumps charsets. + return ' '.join([c for c in ('NATIONAL', spec, collation) + if c is not None]) + return ' '.join([c for c in (spec, charset, collation) + if c is not None]) + + def _mysql_type(self, type_): + return isinstance(type_, (_StringType, _NumericType)) + + def visit_NUMERIC(self, type_, **kw): + if type_.precision is None: + return self._extend_numeric(type_, "NUMERIC") + elif type_.scale is None: + return self._extend_numeric(type_, + "NUMERIC(%(precision)s)" % + {'precision': type_.precision}) + else: + return self._extend_numeric(type_, + "NUMERIC(%(precision)s, %(scale)s)" % + {'precision': type_.precision, + 'scale': type_.scale}) + + def visit_DECIMAL(self, type_, **kw): + if type_.precision is None: + return self._extend_numeric(type_, "DECIMAL") + elif type_.scale is None: + return self._extend_numeric(type_, + "DECIMAL(%(precision)s)" % + {'precision': type_.precision}) + else: + return self._extend_numeric(type_, + "DECIMAL(%(precision)s, %(scale)s)" % + {'precision': type_.precision, + 'scale': type_.scale}) + + def visit_DOUBLE(self, type_, **kw): + if type_.precision is not None and type_.scale is not None: + return self._extend_numeric(type_, + "DOUBLE(%(precision)s, %(scale)s)" % + {'precision': type_.precision, + 'scale': type_.scale}) + else: + return self._extend_numeric(type_, 'DOUBLE') + + def visit_REAL(self, type_, **kw): + if type_.precision is not None and type_.scale is not None: + return self._extend_numeric(type_, + "REAL(%(precision)s, %(scale)s)" % + {'precision': type_.precision, + 'scale': type_.scale}) + else: + return self._extend_numeric(type_, 'REAL') + + def visit_FLOAT(self, type_, **kw): + if self._mysql_type(type_) and \ + type_.scale is not None and \ + type_.precision is not None: + return self._extend_numeric( + type_, "FLOAT(%s, %s)" % (type_.precision, type_.scale)) + elif type_.precision is not None: + return self._extend_numeric(type_, + "FLOAT(%s)" % (type_.precision,)) + else: + return self._extend_numeric(type_, "FLOAT") + + def visit_INTEGER(self, type_, **kw): + if self._mysql_type(type_) and type_.display_width is not None: + return self._extend_numeric( + type_, "INTEGER(%(display_width)s)" % + {'display_width': type_.display_width}) + else: + return self._extend_numeric(type_, "INTEGER") + + def visit_BIGINT(self, type_, **kw): + if self._mysql_type(type_) and type_.display_width is not None: + return self._extend_numeric( + type_, "BIGINT(%(display_width)s)" % + {'display_width': type_.display_width}) + else: + return self._extend_numeric(type_, "BIGINT") + + def visit_MEDIUMINT(self, type_, **kw): + if self._mysql_type(type_) and type_.display_width is not None: + return self._extend_numeric( + type_, "MEDIUMINT(%(display_width)s)" % + {'display_width': type_.display_width}) + else: + return self._extend_numeric(type_, "MEDIUMINT") + + def visit_TINYINT(self, type_, **kw): + if self._mysql_type(type_) and type_.display_width is not None: + return self._extend_numeric(type_, + "TINYINT(%s)" % type_.display_width) + else: + return self._extend_numeric(type_, "TINYINT") + + def visit_SMALLINT(self, type_, **kw): + if self._mysql_type(type_) and type_.display_width is not None: + return self._extend_numeric(type_, + "SMALLINT(%(display_width)s)" % + {'display_width': type_.display_width} + ) + else: + return self._extend_numeric(type_, "SMALLINT") + + def visit_BIT(self, type_, **kw): + if type_.length is not None: + return "BIT(%s)" % type_.length + else: + return "BIT" + + def visit_DATETIME(self, type_, **kw): + if getattr(type_, 'fsp', None): + return "DATETIME(%d)" % type_.fsp + else: + return "DATETIME" + + def visit_DATE(self, type_, **kw): + return "DATE" + + def visit_TIME(self, type_, **kw): + if getattr(type_, 'fsp', None): + return "TIME(%d)" % type_.fsp + else: + return "TIME" + + def visit_TIMESTAMP(self, type_, **kw): + if getattr(type_, 'fsp', None): + return "TIMESTAMP(%d)" % type_.fsp + else: + return "TIMESTAMP" + + def visit_YEAR(self, type_, **kw): + if type_.display_width is None: + return "YEAR" + else: + return "YEAR(%s)" % type_.display_width + + def visit_TEXT(self, type_, **kw): + if type_.length: + return self._extend_string(type_, {}, "TEXT(%d)" % type_.length) + else: + return self._extend_string(type_, {}, "TEXT") + + def visit_TINYTEXT(self, type_, **kw): + return self._extend_string(type_, {}, "TINYTEXT") + + def visit_MEDIUMTEXT(self, type_, **kw): + return self._extend_string(type_, {}, "MEDIUMTEXT") + + def visit_LONGTEXT(self, type_, **kw): + return self._extend_string(type_, {}, "LONGTEXT") + + def visit_VARCHAR(self, type_, **kw): + if type_.length: + return self._extend_string( + type_, {}, "VARCHAR(%d)" % type_.length) + else: + raise exc.CompileError( + "VARCHAR requires a length on dialect %s" % + self.dialect.name) + + def visit_CHAR(self, type_, **kw): + if type_.length: + return self._extend_string(type_, {}, "CHAR(%(length)s)" % + {'length': type_.length}) + else: + return self._extend_string(type_, {}, "CHAR") + + def visit_NVARCHAR(self, type_, **kw): + # We'll actually generate the equiv. "NATIONAL VARCHAR" instead + # of "NVARCHAR". + if type_.length: + return self._extend_string( + type_, {'national': True}, + "VARCHAR(%(length)s)" % {'length': type_.length}) + else: + raise exc.CompileError( + "NVARCHAR requires a length on dialect %s" % + self.dialect.name) + + def visit_NCHAR(self, type_, **kw): + # We'll actually generate the equiv. + # "NATIONAL CHAR" instead of "NCHAR". + if type_.length: + return self._extend_string( + type_, {'national': True}, + "CHAR(%(length)s)" % {'length': type_.length}) + else: + return self._extend_string(type_, {'national': True}, "CHAR") + + def visit_VARBINARY(self, type_, **kw): + return "VARBINARY(%d)" % type_.length + + def visit_JSON(self, type_, **kw): + return "JSON" + + def visit_large_binary(self, type_, **kw): + return self.visit_BLOB(type_) + + def visit_enum(self, type_, **kw): + if not type_.native_enum: + return super(MySQLTypeCompiler, self).visit_enum(type_) + else: + return self._visit_enumerated_values("ENUM", type_, type_.enums) + + def visit_BLOB(self, type_, **kw): + if type_.length: + return "BLOB(%d)" % type_.length + else: + return "BLOB" + + def visit_TINYBLOB(self, type_, **kw): + return "TINYBLOB" + + def visit_MEDIUMBLOB(self, type_, **kw): + return "MEDIUMBLOB" + + def visit_LONGBLOB(self, type_, **kw): + return "LONGBLOB" + + def _visit_enumerated_values(self, name, type_, enumerated_values): + quoted_enums = [] + for e in enumerated_values: + quoted_enums.append("'%s'" % e.replace("'", "''")) + return self._extend_string(type_, {}, "%s(%s)" % ( + name, ",".join(quoted_enums)) + ) + + def visit_ENUM(self, type_, **kw): + return self._visit_enumerated_values("ENUM", type_, + type_._enumerated_values) + + def visit_SET(self, type_, **kw): + return self._visit_enumerated_values("SET", type_, + type_._enumerated_values) + + def visit_BOOLEAN(self, type, **kw): + return "BOOL" + + +class MySQLIdentifierPreparer(compiler.IdentifierPreparer): + + reserved_words = RESERVED_WORDS + + def __init__(self, dialect, server_ansiquotes=False, **kw): + if not server_ansiquotes: + quote = "`" + else: + quote = '"' + + super(MySQLIdentifierPreparer, self).__init__( + dialect, + initial_quote=quote, + escape_quote=quote) + + def _quote_free_identifiers(self, *ids): + """Unilaterally identifier-quote any number of strings.""" + + return tuple([self.quote_identifier(i) for i in ids if i is not None]) + + +@log.class_logger +class MySQLDialect(default.DefaultDialect): + """Details of the MySQL dialect. + Not used directly in application code. + """ + + name = 'mysql' + supports_alter = True + + # MySQL has no true "boolean" type; we + # allow for the "true" and "false" keywords, however + supports_native_boolean = False + + # identifiers are 64, however aliases can be 255... + max_identifier_length = 255 + max_index_name_length = 64 + + supports_native_enum = True + + supports_sane_rowcount = True + supports_sane_multi_rowcount = False + supports_multivalues_insert = True + + supports_comments = True + inline_comments = True + default_paramstyle = 'format' + colspecs = colspecs + + cte_follows_insert = True + + statement_compiler = MySQLCompiler + ddl_compiler = MySQLDDLCompiler + type_compiler = MySQLTypeCompiler + ischema_names = ischema_names + preparer = MySQLIdentifierPreparer + + # default SQL compilation settings - + # these are modified upon initialize(), + # i.e. first connect + _backslash_escapes = True + _server_ansiquotes = False + + construct_arguments = [ + (sa_schema.Table, { + "*": None + }), + (sql.Update, { + "limit": None + }), + (sa_schema.PrimaryKeyConstraint, { + "using": None + }), + (sa_schema.Index, { + "using": None, + "length": None, + "prefix": None, + }) + ] + + def __init__(self, isolation_level=None, json_serializer=None, + json_deserializer=None, **kwargs): + kwargs.pop('use_ansiquotes', None) # legacy + default.DefaultDialect.__init__(self, **kwargs) + self.isolation_level = isolation_level + self._json_serializer = json_serializer + self._json_deserializer = json_deserializer + + def on_connect(self): + if self.isolation_level is not None: + def connect(conn): + self.set_isolation_level(conn, self.isolation_level) + return connect + else: + return None + + _isolation_lookup = set(['SERIALIZABLE', 'READ UNCOMMITTED', + 'READ COMMITTED', 'REPEATABLE READ']) + + def set_isolation_level(self, connection, level): + level = level.replace('_', ' ') + + # adjust for ConnectionFairy being present + # allows attribute set e.g. "connection.autocommit = True" + # to work properly + if hasattr(connection, 'connection'): + connection = connection.connection + + self._set_isolation_level(connection, level) + + def _set_isolation_level(self, connection, level): + if level not in self._isolation_lookup: + raise exc.ArgumentError( + "Invalid value '%s' for isolation_level. " + "Valid isolation levels for %s are %s" % + (level, self.name, ", ".join(self._isolation_lookup)) + ) + cursor = connection.cursor() + cursor.execute("SET SESSION TRANSACTION ISOLATION LEVEL %s" % level) + cursor.execute("COMMIT") + cursor.close() + + def get_isolation_level(self, connection): + cursor = connection.cursor() + if self._is_mysql and self.server_version_info >= (5, 7, 20): + cursor.execute('SELECT @@transaction_isolation') + else: + cursor.execute('SELECT @@tx_isolation') + val = cursor.fetchone()[0] + cursor.close() + if util.py3k and isinstance(val, bytes): + val = val.decode() + return val.upper().replace("-", " ") + + def _get_server_version_info(self, connection): + # get database server version info explicitly over the wire + # to avoid proxy servers like MaxScale getting in the + # way with their own values, see #4205 + dbapi_con = connection.connection + cursor = dbapi_con.cursor() + cursor.execute("SELECT VERSION()") + val = cursor.fetchone()[0] + cursor.close() + if util.py3k and isinstance(val, bytes): + val = val.decode() + + version = [] + r = re.compile(r'[.\-]') + for n in r.split(val): + try: + version.append(int(n)) + except ValueError: + version.append(n) + return tuple(version) + + def do_commit(self, dbapi_connection): + """Execute a COMMIT.""" + + # COMMIT/ROLLBACK were introduced in 3.23.15. + # Yes, we have at least one user who has to talk to these old + # versions! + # + # Ignore commit/rollback if support isn't present, otherwise even + # basic operations via autocommit fail. + try: + dbapi_connection.commit() + except Exception: + if self.server_version_info < (3, 23, 15): + args = sys.exc_info()[1].args + if args and args[0] == 1064: + return + raise + + def do_rollback(self, dbapi_connection): + """Execute a ROLLBACK.""" + + try: + dbapi_connection.rollback() + except Exception: + if self.server_version_info < (3, 23, 15): + args = sys.exc_info()[1].args + if args and args[0] == 1064: + return + raise + + def do_begin_twophase(self, connection, xid): + connection.execute(sql.text("XA BEGIN :xid"), xid=xid) + + def do_prepare_twophase(self, connection, xid): + connection.execute(sql.text("XA END :xid"), xid=xid) + connection.execute(sql.text("XA PREPARE :xid"), xid=xid) + + def do_rollback_twophase(self, connection, xid, is_prepared=True, + recover=False): + if not is_prepared: + connection.execute(sql.text("XA END :xid"), xid=xid) + connection.execute(sql.text("XA ROLLBACK :xid"), xid=xid) + + def do_commit_twophase(self, connection, xid, is_prepared=True, + recover=False): + if not is_prepared: + self.do_prepare_twophase(connection, xid) + connection.execute(sql.text("XA COMMIT :xid"), xid=xid) + + def do_recover_twophase(self, connection): + resultset = connection.execute("XA RECOVER") + return [row['data'][0:row['gtrid_length']] for row in resultset] + + def is_disconnect(self, e, connection, cursor): + if isinstance(e, (self.dbapi.OperationalError, + self.dbapi.ProgrammingError)): + return self._extract_error_code(e) in \ + (2006, 2013, 2014, 2045, 2055) + elif isinstance( + e, (self.dbapi.InterfaceError, self.dbapi.InternalError)): + # if underlying connection is closed, + # this is the error you get + return "(0, '')" in str(e) + else: + return False + + def _compat_fetchall(self, rp, charset=None): + """Proxy result rows to smooth over MySQL-Python driver + inconsistencies.""" + + return [_DecodingRowProxy(row, charset) for row in rp.fetchall()] + + def _compat_fetchone(self, rp, charset=None): + """Proxy a result row to smooth over MySQL-Python driver + inconsistencies.""" + + row = rp.fetchone() + if row: + return _DecodingRowProxy(row, charset) + else: + return None + + def _compat_first(self, rp, charset=None): + """Proxy a result row to smooth over MySQL-Python driver + inconsistencies.""" + + row = rp.first() + if row: + return _DecodingRowProxy(row, charset) + else: + return None + + def _extract_error_code(self, exception): + raise NotImplementedError() + + def _get_default_schema_name(self, connection): + return connection.execute('SELECT DATABASE()').scalar() + + def has_table(self, connection, table_name, schema=None): + # SHOW TABLE STATUS LIKE and SHOW TABLES LIKE do not function properly + # on macosx (and maybe win?) with multibyte table names. + # + # TODO: if this is not a problem on win, make the strategy swappable + # based on platform. DESCRIBE is slower. + + # [ticket:726] + # full_name = self.identifier_preparer.format_table(table, + # use_schema=True) + + full_name = '.'.join(self.identifier_preparer._quote_free_identifiers( + schema, table_name)) + + st = "DESCRIBE %s" % full_name + rs = None + try: + try: + rs = connection.execution_options( + skip_user_error_events=True).execute(st) + have = rs.fetchone() is not None + rs.close() + return have + except exc.DBAPIError as e: + if self._extract_error_code(e.orig) == 1146: + return False + raise + finally: + if rs: + rs.close() + + def initialize(self, connection): + self._connection_charset = self._detect_charset(connection) + self._detect_sql_mode(connection) + self._detect_ansiquotes(connection) + self._detect_casing(connection) + if self._server_ansiquotes: + # if ansiquotes == True, build a new IdentifierPreparer + # with the new setting + self.identifier_preparer = self.preparer( + self, server_ansiquotes=self._server_ansiquotes) + + default.DefaultDialect.initialize(self, connection) + + self._needs_correct_for_88718 = ( + not self._is_mariadb and self.server_version_info >= (8, ) + ) + + self._warn_for_known_db_issues() + + def _warn_for_known_db_issues(self): + if self._is_mariadb: + mdb_version = self._mariadb_normalized_version_info + if mdb_version > (10, 2) and mdb_version < (10, 2, 9): + util.warn( + "MariaDB %r before 10.2.9 has known issues regarding " + "CHECK constraints, which impact handling of NULL values " + "with SQLAlchemy's boolean datatype (MDEV-13596). An " + "additional issue prevents proper migrations of columns " + "with CHECK constraints (MDEV-11114). Please upgrade to " + "MariaDB 10.2.9 or greater, or use the MariaDB 10.1 " + "series, to avoid these issues." % (mdb_version, )) + + @property + def _is_mariadb(self): + return 'MariaDB' in self.server_version_info + + @property + def _is_mysql(self): + return 'MariaDB' not in self.server_version_info + + @property + def _is_mariadb_102(self): + return self._is_mariadb and \ + self._mariadb_normalized_version_info > (10, 2) + + @property + def _mariadb_normalized_version_info(self): + # MariaDB's wire-protocol prepends the server_version with + # the string "5.5"; now that we use @@version we no longer see this. + + if self._is_mariadb: + idx = self.server_version_info.index('MariaDB') + return self.server_version_info[idx - 3: idx] + else: + return self.server_version_info + + @property + def _supports_cast(self): + return self.server_version_info is None or \ + self.server_version_info >= (4, 0, 2) + + @reflection.cache + def get_schema_names(self, connection, **kw): + rp = connection.execute("SHOW schemas") + return [r[0] for r in rp] + + @reflection.cache + def get_table_names(self, connection, schema=None, **kw): + """Return a Unicode SHOW TABLES from a given schema.""" + if schema is not None: + current_schema = schema + else: + current_schema = self.default_schema_name + + charset = self._connection_charset + if self.server_version_info < (5, 0, 2): + rp = connection.execute( + "SHOW TABLES FROM %s" % + self.identifier_preparer.quote_identifier(current_schema)) + return [row[0] for + row in self._compat_fetchall(rp, charset=charset)] + else: + rp = connection.execute( + "SHOW FULL TABLES FROM %s" % + self.identifier_preparer.quote_identifier(current_schema)) + + return [row[0] + for row in self._compat_fetchall(rp, charset=charset) + if row[1] == 'BASE TABLE'] + + @reflection.cache + def get_view_names(self, connection, schema=None, **kw): + if self.server_version_info < (5, 0, 2): + raise NotImplementedError + if schema is None: + schema = self.default_schema_name + if self.server_version_info < (5, 0, 2): + return self.get_table_names(connection, schema) + charset = self._connection_charset + rp = connection.execute( + "SHOW FULL TABLES FROM %s" % + self.identifier_preparer.quote_identifier(schema)) + return [row[0] + for row in self._compat_fetchall(rp, charset=charset) + if row[1] in ('VIEW', 'SYSTEM VIEW')] + + @reflection.cache + def get_table_options(self, connection, table_name, schema=None, **kw): + + parsed_state = self._parsed_state_or_create( + connection, table_name, schema, **kw) + return parsed_state.table_options + + @reflection.cache + def get_columns(self, connection, table_name, schema=None, **kw): + parsed_state = self._parsed_state_or_create( + connection, table_name, schema, **kw) + return parsed_state.columns + + @reflection.cache + def get_pk_constraint(self, connection, table_name, schema=None, **kw): + parsed_state = self._parsed_state_or_create( + connection, table_name, schema, **kw) + for key in parsed_state.keys: + if key['type'] == 'PRIMARY': + # There can be only one. + cols = [s[0] for s in key['columns']] + return {'constrained_columns': cols, 'name': None} + return {'constrained_columns': [], 'name': None} + + @reflection.cache + def get_foreign_keys(self, connection, table_name, schema=None, **kw): + + parsed_state = self._parsed_state_or_create( + connection, table_name, schema, **kw) + default_schema = None + + fkeys = [] + + for spec in parsed_state.fk_constraints: + ref_name = spec['table'][-1] + ref_schema = len(spec['table']) > 1 and \ + spec['table'][-2] or schema + + if not ref_schema: + if default_schema is None: + default_schema = \ + connection.dialect.default_schema_name + if schema == default_schema: + ref_schema = schema + + loc_names = spec['local'] + ref_names = spec['foreign'] + + con_kw = {} + for opt in ('onupdate', 'ondelete'): + if spec.get(opt, False): + con_kw[opt] = spec[opt] + + fkey_d = { + 'name': spec['name'], + 'constrained_columns': loc_names, + 'referred_schema': ref_schema, + 'referred_table': ref_name, + 'referred_columns': ref_names, + 'options': con_kw + } + fkeys.append(fkey_d) + + if self._needs_correct_for_88718: + self._correct_for_mysql_bug_88718(fkeys, connection) + + return fkeys + + def _correct_for_mysql_bug_88718(self, fkeys, connection): + # Foreign key is always in lower case (MySQL 8.0) + # https://bugs.mysql.com/bug.php?id=88718 + # issue #4344 for SQLAlchemy + + # for lower_case_table_names=2, information_schema.columns + # preserves the original table/schema casing, but SHOW CREATE + # TABLE does not. this problem is not in lower_case_table_names=1, + # but use case-insensitive matching for these two modes in any case. + if self._casing in (1, 2): + lower = str.lower + else: + # if on case sensitive, there can be two tables referenced + # with the same name different casing, so we need to use + # case-sensitive matching. + def lower(s): + return s + + default_schema_name = connection.dialect.default_schema_name + col_tuples = [ + ( + lower(rec['referred_schema'] or default_schema_name), + lower(rec['referred_table']), + col_name + ) + for rec in fkeys + for col_name in rec['referred_columns'] + ] + + if col_tuples: + + correct_for_wrong_fk_case = connection.execute( + sql.text(""" + select table_schema, table_name, column_name + from information_schema.columns + where (table_schema, table_name, lower(column_name)) in + :table_data; + """).bindparams( + sql.bindparam("table_data", expanding=True) + ), table_data=col_tuples + ) + + # in casing=0, table name and schema name come back in their + # exact case. + # in casing=1, table name and schema name come back in lower + # case. + # in casing=2, table name and schema name come back from the + # information_schema.columns view in the case + # that was used in CREATE DATABASE and CREATE TABLE, but + # SHOW CREATE TABLE converts them to *lower case*, therefore + # not matching. So for this case, case-insensitive lookup + # is necessary + d = defaultdict(dict) + for schema, tname, cname in correct_for_wrong_fk_case: + d[(lower(schema), lower(tname))][cname.lower()] = cname + + for fkey in fkeys: + fkey['referred_columns'] = [ + d[ + ( + lower( + fkey['referred_schema'] or + default_schema_name), + lower(fkey['referred_table']) + ) + ][col.lower()] + for col in fkey['referred_columns'] + ] + + @reflection.cache + def get_check_constraints( + self, connection, table_name, schema=None, **kw): + + parsed_state = self._parsed_state_or_create( + connection, table_name, schema, **kw) + + return [ + {"name": spec['name'], "sqltext": spec['sqltext']} + for spec in parsed_state.ck_constraints + ] + + @reflection.cache + def get_table_comment(self, connection, table_name, schema=None, **kw): + parsed_state = self._parsed_state_or_create( + connection, table_name, schema, **kw) + return {"text": parsed_state.table_options.get('mysql_comment', None)} + + @reflection.cache + def get_indexes(self, connection, table_name, schema=None, **kw): + + parsed_state = self._parsed_state_or_create( + connection, table_name, schema, **kw) + + indexes = [] + for spec in parsed_state.keys: + unique = False + flavor = spec['type'] + if flavor == 'PRIMARY': + continue + if flavor == 'UNIQUE': + unique = True + elif flavor in (None, 'FULLTEXT', 'SPATIAL'): + pass + else: + self.logger.info( + "Converting unknown KEY type %s to a plain KEY", flavor) + pass + index_d = {} + index_d['name'] = spec['name'] + index_d['column_names'] = [s[0] for s in spec['columns']] + index_d['unique'] = unique + if flavor: + index_d['type'] = flavor + indexes.append(index_d) + return indexes + + @reflection.cache + def get_unique_constraints(self, connection, table_name, + schema=None, **kw): + parsed_state = self._parsed_state_or_create( + connection, table_name, schema, **kw) + + return [ + { + 'name': key['name'], + 'column_names': [col[0] for col in key['columns']], + 'duplicates_index': key['name'], + } + for key in parsed_state.keys + if key['type'] == 'UNIQUE' + ] + + @reflection.cache + def get_view_definition(self, connection, view_name, schema=None, **kw): + + charset = self._connection_charset + full_name = '.'.join(self.identifier_preparer._quote_free_identifiers( + schema, view_name)) + sql = self._show_create_table(connection, None, charset, + full_name=full_name) + return sql + + def _parsed_state_or_create(self, connection, table_name, + schema=None, **kw): + return self._setup_parser( + connection, + table_name, + schema, + info_cache=kw.get('info_cache', None) + ) + + @util.memoized_property + def _tabledef_parser(self): + """return the MySQLTableDefinitionParser, generate if needed. + + The deferred creation ensures that the dialect has + retrieved server version information first. + + """ + if (self.server_version_info < (4, 1) and self._server_ansiquotes): + # ANSI_QUOTES doesn't affect SHOW CREATE TABLE on < 4.1 + preparer = self.preparer(self, server_ansiquotes=False) + else: + preparer = self.identifier_preparer + return _reflection.MySQLTableDefinitionParser(self, preparer) + + @reflection.cache + def _setup_parser(self, connection, table_name, schema=None, **kw): + charset = self._connection_charset + parser = self._tabledef_parser + full_name = '.'.join(self.identifier_preparer._quote_free_identifiers( + schema, table_name)) + sql = self._show_create_table(connection, None, charset, + full_name=full_name) + if re.match(r'^CREATE (?:ALGORITHM)?.* VIEW', sql): + # Adapt views to something table-like. + columns = self._describe_table(connection, None, charset, + full_name=full_name) + sql = parser._describe_to_create(table_name, columns) + return parser.parse(sql, charset) + + def _detect_charset(self, connection): + raise NotImplementedError() + + def _detect_casing(self, connection): + """Sniff out identifier case sensitivity. + + Cached per-connection. This value can not change without a server + restart. + + """ + # http://dev.mysql.com/doc/refman/5.0/en/name-case-sensitivity.html + + charset = self._connection_charset + row = self._compat_first(connection.execute( + "SHOW VARIABLES LIKE 'lower_case_table_names'"), + charset=charset) + if not row: + cs = 0 + else: + # 4.0.15 returns OFF or ON according to [ticket:489] + # 3.23 doesn't, 4.0.27 doesn't.. + if row[1] == 'OFF': + cs = 0 + elif row[1] == 'ON': + cs = 1 + else: + cs = int(row[1]) + self._casing = cs + return cs + + def _detect_collations(self, connection): + """Pull the active COLLATIONS list from the server. + + Cached per-connection. + """ + + collations = {} + if self.server_version_info < (4, 1, 0): + pass + else: + charset = self._connection_charset + rs = connection.execute('SHOW COLLATION') + for row in self._compat_fetchall(rs, charset): + collations[row[0]] = row[1] + return collations + + def _detect_sql_mode(self, connection): + row = self._compat_first( + connection.execute("SHOW VARIABLES LIKE 'sql_mode'"), + charset=self._connection_charset) + + if not row: + util.warn( + "Could not retrieve SQL_MODE; please ensure the " + "MySQL user has permissions to SHOW VARIABLES") + self._sql_mode = '' + else: + self._sql_mode = row[1] or '' + + def _detect_ansiquotes(self, connection): + """Detect and adjust for the ANSI_QUOTES sql mode.""" + + mode = self._sql_mode + if not mode: + mode = '' + elif mode.isdigit(): + mode_no = int(mode) + mode = (mode_no | 4 == mode_no) and 'ANSI_QUOTES' or '' + + self._server_ansiquotes = 'ANSI_QUOTES' in mode + + # as of MySQL 5.0.1 + self._backslash_escapes = 'NO_BACKSLASH_ESCAPES' not in mode + + def _show_create_table(self, connection, table, charset=None, + full_name=None): + """Run SHOW CREATE TABLE for a ``Table``.""" + + if full_name is None: + full_name = self.identifier_preparer.format_table(table) + st = "SHOW CREATE TABLE %s" % full_name + + rp = None + try: + rp = connection.execution_options( + skip_user_error_events=True).execute(st) + except exc.DBAPIError as e: + if self._extract_error_code(e.orig) == 1146: + raise exc.NoSuchTableError(full_name) + else: + raise + row = self._compat_first(rp, charset=charset) + if not row: + raise exc.NoSuchTableError(full_name) + return row[1].strip() + + return sql + + def _describe_table(self, connection, table, charset=None, + full_name=None): + """Run DESCRIBE for a ``Table`` and return processed rows.""" + + if full_name is None: + full_name = self.identifier_preparer.format_table(table) + st = "DESCRIBE %s" % full_name + + rp, rows = None, None + try: + try: + rp = connection.execution_options( + skip_user_error_events=True).execute(st) + except exc.DBAPIError as e: + code = self._extract_error_code(e.orig) + if code == 1146: + raise exc.NoSuchTableError(full_name) + elif code == 1356: + raise exc.UnreflectableTableError( + "Table or view named %s could not be " + "reflected: %s" % (full_name, e) + ) + else: + raise + rows = self._compat_fetchall(rp, charset=charset) + finally: + if rp: + rp.close() + return rows + + +class _DecodingRowProxy(object): + """Return unicode-decoded values based on type inspection. + + Smooth over data type issues (esp. with alpha driver versions) and + normalize strings as Unicode regardless of user-configured driver + encoding settings. + + """ + + # Some MySQL-python versions can return some columns as + # sets.Set(['value']) (seriously) but thankfully that doesn't + # seem to come up in DDL queries. + + _encoding_compat = { + 'koi8r': 'koi8_r', + 'koi8u': 'koi8_u', + 'utf16': 'utf-16-be', # MySQL's uft16 is always bigendian + 'utf8mb4': 'utf8', # real utf8 + 'eucjpms': 'ujis', + } + + def __init__(self, rowproxy, charset): + self.rowproxy = rowproxy + self.charset = self._encoding_compat.get(charset, charset) + + def __getitem__(self, index): + item = self.rowproxy[index] + if isinstance(item, _array): + item = item.tostring() + + if self.charset and isinstance(item, util.binary_type): + return item.decode(self.charset) + else: + return item + + def __getattr__(self, attr): + item = getattr(self.rowproxy, attr) + if isinstance(item, _array): + item = item.tostring() + if self.charset and isinstance(item, util.binary_type): + return item.decode(self.charset) + else: + return item diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/cymysql.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/cymysql.py new file mode 100644 index 0000000..d142905 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/cymysql.py @@ -0,0 +1,76 @@ +# mysql/cymysql.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" + +.. dialect:: mysql+cymysql + :name: CyMySQL + :dbapi: cymysql + :connectstring: mysql+cymysql://<username>:<password>@<host>/<dbname>\ +[?<options>] + :url: https://github.com/nakagami/CyMySQL + +""" +import re + +from .mysqldb import MySQLDialect_mysqldb +from .base import (BIT, MySQLDialect) +from ... import util + + +class _cymysqlBIT(BIT): + def result_processor(self, dialect, coltype): + """Convert a MySQL's 64 bit, variable length binary string to a long. + """ + + def process(value): + if value is not None: + v = 0 + for i in util.iterbytes(value): + v = v << 8 | i + return v + return value + return process + + +class MySQLDialect_cymysql(MySQLDialect_mysqldb): + driver = 'cymysql' + + description_encoding = None + supports_sane_rowcount = True + supports_sane_multi_rowcount = False + supports_unicode_statements = True + + colspecs = util.update_copy( + MySQLDialect.colspecs, + { + BIT: _cymysqlBIT, + } + ) + + @classmethod + def dbapi(cls): + return __import__('cymysql') + + def _detect_charset(self, connection): + return connection.connection.charset + + def _extract_error_code(self, exception): + return exception.errno + + def is_disconnect(self, e, connection, cursor): + if isinstance(e, self.dbapi.OperationalError): + return self._extract_error_code(e) in \ + (2006, 2013, 2014, 2045, 2055) + elif isinstance(e, self.dbapi.InterfaceError): + # if underlying connection is closed, + # this is the error you get + return True + else: + return False + +dialect = MySQLDialect_cymysql diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/dml.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/dml.py new file mode 100644 index 0000000..217dc7a --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/dml.py @@ -0,0 +1,80 @@ +from ...sql.elements import ClauseElement +from ...sql.dml import Insert as StandardInsert +from ...sql.expression import alias +from ...util.langhelpers import public_factory +from ...sql.base import _generative +from ... import util + +__all__ = ('Insert', 'insert') + + +class Insert(StandardInsert): + """MySQL-specific implementation of INSERT. + + Adds methods for MySQL-specific syntaxes such as ON DUPLICATE KEY UPDATE. + + .. versionadded:: 1.2 + + """ + + @property + def inserted(self): + """Provide the "inserted" namespace for an ON DUPLICATE KEY UPDATE statement + + MySQL's ON DUPLICATE KEY UPDATE clause allows reference to the row + that would be inserted, via a special function called ``VALUES()``. + This attribute provides all columns in this row to be referenaceable + such that they will render within a ``VALUES()`` function inside the + ON DUPLICATE KEY UPDATE clause. The attribute is named ``.inserted`` + so as not to conflict with the existing :meth:`.Insert.values` method. + + .. seealso:: + + :ref:`mysql_insert_on_duplicate_key_update` - example of how + to use :attr:`.Insert.inserted` + + """ + return self.inserted_alias.columns + + @util.memoized_property + def inserted_alias(self): + return alias(self.table, name='inserted') + + @_generative + def on_duplicate_key_update(self, **kw): + r""" + Specifies the ON DUPLICATE KEY UPDATE clause. + + :param \**kw: Column keys linked to UPDATE values. The + values may be any SQL expression or supported literal Python + values. + + .. warning:: This dictionary does **not** take into account + Python-specified default UPDATE values or generation functions, + e.g. those specified using :paramref:`.Column.onupdate`. + These values will not be exercised for an ON DUPLICATE KEY UPDATE + style of UPDATE, unless values are manually specified here. + + .. versionadded:: 1.2 + + .. seealso:: + + :ref:`mysql_insert_on_duplicate_key_update` + + """ + inserted_alias = getattr(self, 'inserted_alias', None) + self._post_values_clause = OnDuplicateClause(inserted_alias, kw) + return self + + +insert = public_factory(Insert, '.dialects.mysql.insert') + + +class OnDuplicateClause(ClauseElement): + __visit_name__ = 'on_duplicate_key_update' + + def __init__(self, inserted_alias, update): + self.inserted_alias = inserted_alias + if not update or not isinstance(update, dict): + raise ValueError('update parameter must be a non-empty dictionary') + self.update = update diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/enumerated.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/enumerated.py new file mode 100644 index 0000000..f63d64e --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/enumerated.py @@ -0,0 +1,311 @@ +# mysql/enumerated.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +import re + +from .types import _StringType +from ... import exc, sql, util +from ...sql import sqltypes + + +class _EnumeratedValues(_StringType): + def _init_values(self, values, kw): + self.quoting = kw.pop('quoting', 'auto') + + if self.quoting == 'auto' and len(values): + # What quoting character are we using? + q = None + for e in values: + if len(e) == 0: + self.quoting = 'unquoted' + break + elif q is None: + q = e[0] + + if len(e) == 1 or e[0] != q or e[-1] != q: + self.quoting = 'unquoted' + break + else: + self.quoting = 'quoted' + + if self.quoting == 'quoted': + util.warn_deprecated( + 'Manually quoting %s value literals is deprecated. Supply ' + 'unquoted values and use the quoting= option in cases of ' + 'ambiguity.' % self.__class__.__name__) + + values = self._strip_values(values) + + self._enumerated_values = values + length = max([len(v) for v in values] + [0]) + return values, length + + @classmethod + def _strip_values(cls, values): + strip_values = [] + for a in values: + if a[0:1] == '"' or a[0:1] == "'": + # strip enclosing quotes and unquote interior + a = a[1:-1].replace(a[0] * 2, a[0]) + strip_values.append(a) + return strip_values + + +class ENUM(sqltypes.NativeForEmulated, sqltypes.Enum, _EnumeratedValues): + """MySQL ENUM type.""" + + __visit_name__ = 'ENUM' + + native_enum = True + + def __init__(self, *enums, **kw): + """Construct an ENUM. + + E.g.:: + + Column('myenum', ENUM("foo", "bar", "baz")) + + :param enums: The range of valid values for this ENUM. Values will be + quoted when generating the schema according to the quoting flag (see + below). This object may also be a PEP-435-compliant enumerated + type. + + .. versionadded: 1.1 added support for PEP-435-compliant enumerated + types. + + :param strict: This flag has no effect. + + .. versionchanged:: The MySQL ENUM type as well as the base Enum + type now validates all Python data values. + + :param charset: Optional, a column-level character set for this string + value. Takes precedence to 'ascii' or 'unicode' short-hand. + + :param collation: Optional, a column-level collation for this string + value. Takes precedence to 'binary' short-hand. + + :param ascii: Defaults to False: short-hand for the ``latin1`` + character set, generates ASCII in schema. + + :param unicode: Defaults to False: short-hand for the ``ucs2`` + character set, generates UNICODE in schema. + + :param binary: Defaults to False: short-hand, pick the binary + collation type that matches the column's character set. Generates + BINARY in schema. This does not affect the type of data stored, + only the collation of character data. + + :param quoting: Defaults to 'auto': automatically determine enum value + quoting. If all enum values are surrounded by the same quoting + character, then use 'quoted' mode. Otherwise, use 'unquoted' mode. + + 'quoted': values in enums are already quoted, they will be used + directly when generating the schema - this usage is deprecated. + + 'unquoted': values in enums are not quoted, they will be escaped and + surrounded by single quotes when generating the schema. + + Previous versions of this type always required manually quoted + values to be supplied; future versions will always quote the string + literals for you. This is a transitional option. + + """ + + kw.pop('strict', None) + self._enum_init(enums, kw) + _StringType.__init__(self, length=self.length, **kw) + + @classmethod + def adapt_emulated_to_native(cls, impl, **kw): + """Produce a MySQL native :class:`.mysql.ENUM` from plain + :class:`.Enum`. + + """ + kw.setdefault("validate_strings", impl.validate_strings) + kw.setdefault("values_callable", impl.values_callable) + return cls(**kw) + + def _setup_for_values(self, values, objects, kw): + values, length = self._init_values(values, kw) + return super(ENUM, self)._setup_for_values(values, objects, kw) + + def _object_value_for_elem(self, elem): + # mysql sends back a blank string for any value that + # was persisted that was not in the enums; that is, it does no + # validation on the incoming data, it "truncates" it to be + # the blank string. Return it straight. + if elem == "": + return elem + else: + return super(ENUM, self)._object_value_for_elem(elem) + + def __repr__(self): + return util.generic_repr( + self, to_inspect=[ENUM, _StringType, sqltypes.Enum]) + + +class SET(_EnumeratedValues): + """MySQL SET type.""" + + __visit_name__ = 'SET' + + def __init__(self, *values, **kw): + """Construct a SET. + + E.g.:: + + Column('myset', SET("foo", "bar", "baz")) + + + The list of potential values is required in the case that this + set will be used to generate DDL for a table, or if the + :paramref:`.SET.retrieve_as_bitwise` flag is set to True. + + :param values: The range of valid values for this SET. + + :param convert_unicode: Same flag as that of + :paramref:`.String.convert_unicode`. + + :param collation: same as that of :paramref:`.String.collation` + + :param charset: same as that of :paramref:`.VARCHAR.charset`. + + :param ascii: same as that of :paramref:`.VARCHAR.ascii`. + + :param unicode: same as that of :paramref:`.VARCHAR.unicode`. + + :param binary: same as that of :paramref:`.VARCHAR.binary`. + + :param quoting: Defaults to 'auto': automatically determine set value + quoting. If all values are surrounded by the same quoting + character, then use 'quoted' mode. Otherwise, use 'unquoted' mode. + + 'quoted': values in enums are already quoted, they will be used + directly when generating the schema - this usage is deprecated. + + 'unquoted': values in enums are not quoted, they will be escaped and + surrounded by single quotes when generating the schema. + + Previous versions of this type always required manually quoted + values to be supplied; future versions will always quote the string + literals for you. This is a transitional option. + + .. versionadded:: 0.9.0 + + :param retrieve_as_bitwise: if True, the data for the set type will be + persisted and selected using an integer value, where a set is coerced + into a bitwise mask for persistence. MySQL allows this mode which + has the advantage of being able to store values unambiguously, + such as the blank string ``''``. The datatype will appear + as the expression ``col + 0`` in a SELECT statement, so that the + value is coerced into an integer value in result sets. + This flag is required if one wishes + to persist a set that can store the blank string ``''`` as a value. + + .. warning:: + + When using :paramref:`.mysql.SET.retrieve_as_bitwise`, it is + essential that the list of set values is expressed in the + **exact same order** as exists on the MySQL database. + + .. versionadded:: 1.0.0 + + + """ + self.retrieve_as_bitwise = kw.pop('retrieve_as_bitwise', False) + values, length = self._init_values(values, kw) + self.values = tuple(values) + if not self.retrieve_as_bitwise and '' in values: + raise exc.ArgumentError( + "Can't use the blank value '' in a SET without " + "setting retrieve_as_bitwise=True") + if self.retrieve_as_bitwise: + self._bitmap = dict( + (value, 2 ** idx) + for idx, value in enumerate(self.values) + ) + self._bitmap.update( + (2 ** idx, value) + for idx, value in enumerate(self.values) + ) + kw.setdefault('length', length) + super(SET, self).__init__(**kw) + + def column_expression(self, colexpr): + if self.retrieve_as_bitwise: + return sql.type_coerce( + sql.type_coerce(colexpr, sqltypes.Integer) + 0, + self + ) + else: + return colexpr + + def result_processor(self, dialect, coltype): + if self.retrieve_as_bitwise: + def process(value): + if value is not None: + value = int(value) + + return set( + util.map_bits(self._bitmap.__getitem__, value) + ) + else: + return None + else: + super_convert = super(SET, self).result_processor(dialect, coltype) + + def process(value): + if isinstance(value, util.string_types): + # MySQLdb returns a string, let's parse + if super_convert: + value = super_convert(value) + return set(re.findall(r'[^,]+', value)) + else: + # mysql-connector-python does a naive + # split(",") which throws in an empty string + if value is not None: + value.discard('') + return value + return process + + def bind_processor(self, dialect): + super_convert = super(SET, self).bind_processor(dialect) + if self.retrieve_as_bitwise: + def process(value): + if value is None: + return None + elif isinstance(value, util.int_types + util.string_types): + if super_convert: + return super_convert(value) + else: + return value + else: + int_value = 0 + for v in value: + int_value |= self._bitmap[v] + return int_value + else: + + def process(value): + # accept strings and int (actually bitflag) values directly + if value is not None and not isinstance( + value, util.int_types + util.string_types): + value = ",".join(value) + + if super_convert: + return super_convert(value) + else: + return value + return process + + def adapt(self, impltype, **kw): + kw['retrieve_as_bitwise'] = self.retrieve_as_bitwise + return util.constructor_copy( + self, impltype, + *self.values, + **kw + ) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/gaerdbms.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/gaerdbms.py new file mode 100644 index 0000000..806e4c8 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/gaerdbms.py @@ -0,0 +1,102 @@ +# mysql/gaerdbms.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +""" +.. dialect:: mysql+gaerdbms + :name: Google Cloud SQL + :dbapi: rdbms + :connectstring: mysql+gaerdbms:///<dbname>?instance=<instancename> + :url: https://developers.google.com/appengine/docs/python/cloud-sql/\ +developers-guide + + This dialect is based primarily on the :mod:`.mysql.mysqldb` dialect with + minimal changes. + + .. versionadded:: 0.7.8 + + .. deprecated:: 1.0 This dialect is **no longer necessary** for + Google Cloud SQL; the MySQLdb dialect can be used directly. + Cloud SQL now recommends creating connections via the + mysql dialect using the URL format + + ``mysql+mysqldb://root@/<dbname>?unix_socket=/cloudsql/<projectid>:<instancename>`` + + +Pooling +------- + +Google App Engine connections appear to be randomly recycled, +so the dialect does not pool connections. The :class:`.NullPool` +implementation is installed within the :class:`.Engine` by +default. + +""" + +import os + +from .mysqldb import MySQLDialect_mysqldb +from ...pool import NullPool +import re +from sqlalchemy.util import warn_deprecated + + +def _is_dev_environment(): + return os.environ.get('SERVER_SOFTWARE', '').startswith('Development/') + + +class MySQLDialect_gaerdbms(MySQLDialect_mysqldb): + + @classmethod + def dbapi(cls): + + warn_deprecated( + "Google Cloud SQL now recommends creating connections via the " + "MySQLdb dialect directly, using the URL format " + "mysql+mysqldb://root@/<dbname>?unix_socket=/cloudsql/" + "<projectid>:<instancename>" + ) + + # from django: + # http://code.google.com/p/googleappengine/source/ + # browse/trunk/python/google/storage/speckle/ + # python/django/backend/base.py#118 + # see also [ticket:2649] + # see also http://stackoverflow.com/q/14224679/34549 + from google.appengine.api import apiproxy_stub_map + + if _is_dev_environment(): + from google.appengine.api import rdbms_mysqldb + return rdbms_mysqldb + elif apiproxy_stub_map.apiproxy.GetStub('rdbms'): + from google.storage.speckle.python.api import rdbms_apiproxy + return rdbms_apiproxy + else: + from google.storage.speckle.python.api import rdbms_googleapi + return rdbms_googleapi + + @classmethod + def get_pool_class(cls, url): + # Cloud SQL connections die at any moment + return NullPool + + def create_connect_args(self, url): + opts = url.translate_connect_args() + if not _is_dev_environment(): + # 'dsn' and 'instance' are because we are skipping + # the traditional google.api.rdbms wrapper + opts['dsn'] = '' + opts['instance'] = url.query['instance'] + return [], opts + + def _extract_error_code(self, exception): + match = re.compile(r"^(\d+)L?:|^\((\d+)L?,").match(str(exception)) + # The rdbms api will wrap then re-raise some types of errors + # making this regex return no matches. + code = match.group(1) or match.group(2) if match else None + if code: + return int(code) + +dialect = MySQLDialect_gaerdbms diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/json.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/json.py new file mode 100644 index 0000000..84dcefc --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/json.py @@ -0,0 +1,79 @@ +# mysql/json.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from __future__ import absolute_import + +import json + +from ...sql import elements +from ... import types as sqltypes +from ... import util + + +class JSON(sqltypes.JSON): + """MySQL JSON type. + + MySQL supports JSON as of version 5.7. Note that MariaDB does **not** + support JSON at the time of this writing. + + The :class:`.mysql.JSON` type supports persistence of JSON values + as well as the core index operations provided by :class:`.types.JSON` + datatype, by adapting the operations to render the ``JSON_EXTRACT`` + function at the database level. + + .. versionadded:: 1.1 + + """ + + pass + + +class _FormatTypeMixin(object): + def _format_value(self, value): + raise NotImplementedError() + + def bind_processor(self, dialect): + super_proc = self.string_bind_processor(dialect) + + def process(value): + value = self._format_value(value) + if super_proc: + value = super_proc(value) + return value + + return process + + def literal_processor(self, dialect): + super_proc = self.string_literal_processor(dialect) + + def process(value): + value = self._format_value(value) + if super_proc: + value = super_proc(value) + return value + + return process + + +class JSONIndexType(_FormatTypeMixin, sqltypes.JSON.JSONIndexType): + + def _format_value(self, value): + if isinstance(value, int): + value = "$[%s]" % value + else: + value = '$."%s"' % value + return value + + +class JSONPathType(_FormatTypeMixin, sqltypes.JSON.JSONPathType): + def _format_value(self, value): + return "$%s" % ( + "".join([ + "[%s]" % elem if isinstance(elem, int) + else '."%s"' % elem for elem in value + ]) + ) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/mysqlconnector.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/mysqlconnector.py new file mode 100644 index 0000000..1ead8aa --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/mysqlconnector.py @@ -0,0 +1,251 @@ +# mysql/mysqlconnector.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: mysql+mysqlconnector + :name: MySQL Connector/Python + :dbapi: myconnpy + :connectstring: mysql+mysqlconnector://<user>:<password>@\ +<host>[:<port>]/<dbname> + :url: http://dev.mysql.com/downloads/connector/python/ + + +Current Issues +-------------- + +The mysqlconnector driver has many issues that have gone unresolved +for many years and it recommended that mysqlclient or pymysql be used +if possible; as of June 27, 2018: + +* the values in cursor.description are randomly sent as either bytes + or text with no discernible pattern, so the dialect must test these + individually and attempt to decode + +* has been observed to leak interpreter memory (likely at the C code level) + under scenarios that do not leak memory when using mysqlclient + +* Under Python 2, the driver does not support SQL statements that contain + non-ascii characters within the SQL text, making it impossible to support + schema objects with non-ascii names; an ascii encoding error is raised. + +* additional random bytes-returned issues occur when running under MySQL 8.0 + only + +* The driver does not accept the "utf8mb4" or "utf8mb3" charset parameters, + only "utf8", even though MySQL itself has deprecated this symbol + +* The driver produces deadlocks when trying to make use of SELECT..FOR UPDATE, + the reason is unknown. + +This list should be updated as these issues are resolved either in the +upstream mysql-connector-python driver or if appropriate usage patterns +are contributed to SQLAlchemy. + +""" + +from .base import (MySQLDialect, MySQLExecutionContext, + MySQLCompiler, MySQLIdentifierPreparer, + BIT) + +from ... import util +import re +from ... import processors + + +class MySQLExecutionContext_mysqlconnector(MySQLExecutionContext): + + def get_lastrowid(self): + return self.cursor.lastrowid + + +class MySQLCompiler_mysqlconnector(MySQLCompiler): + def visit_mod_binary(self, binary, operator, **kw): + if self.dialect._mysqlconnector_double_percents: + return self.process(binary.left, **kw) + " %% " + \ + self.process(binary.right, **kw) + else: + return self.process(binary.left, **kw) + " % " + \ + self.process(binary.right, **kw) + + def post_process_text(self, text): + if self.dialect._mysqlconnector_double_percents: + return text.replace('%', '%%') + else: + return text + + def escape_literal_column(self, text): + if self.dialect._mysqlconnector_double_percents: + return text.replace('%', '%%') + else: + return text + + +class MySQLIdentifierPreparer_mysqlconnector(MySQLIdentifierPreparer): + @property + def _double_percents(self): + return self.dialect._mysqlconnector_double_percents + + @_double_percents.setter + def _double_percents(self, value): + pass + + def _escape_identifier(self, value): + value = value.replace(self.escape_quote, self.escape_to_quote) + if self.dialect._mysqlconnector_double_percents: + return value.replace("%", "%%") + else: + return value + + +class _myconnpyBIT(BIT): + def result_processor(self, dialect, coltype): + """MySQL-connector already converts mysql bits, so.""" + + return None + + +class MySQLDialect_mysqlconnector(MySQLDialect): + driver = 'mysqlconnector' + + supports_unicode_binds = True + + supports_sane_rowcount = True + supports_sane_multi_rowcount = True + + supports_native_decimal = True + + default_paramstyle = 'format' + execution_ctx_cls = MySQLExecutionContext_mysqlconnector + statement_compiler = MySQLCompiler_mysqlconnector + + preparer = MySQLIdentifierPreparer_mysqlconnector + + colspecs = util.update_copy( + MySQLDialect.colspecs, + { + BIT: _myconnpyBIT, + } + ) + + def __init__(self, *arg, **kw): + super(MySQLDialect_mysqlconnector, self).__init__(*arg, **kw) + + # hack description encoding since mysqlconnector randomly + # returns bytes or not + self._description_decoder = \ + processors.to_conditional_unicode_processor_factory( + self.description_encoding + ) + + def _check_unicode_description(self, connection): + # hack description encoding since mysqlconnector randomly + # returns bytes or not + return False + + @property + def description_encoding(self): + # total guess + return "latin-1" + + @util.memoized_property + def supports_unicode_statements(self): + return util.py3k or self._mysqlconnector_version_info > (2, 0) + + @classmethod + def dbapi(cls): + from mysql import connector + return connector + + def create_connect_args(self, url): + opts = url.translate_connect_args(username='user') + + opts.update(url.query) + + util.coerce_kw_type(opts, 'allow_local_infile', bool) + util.coerce_kw_type(opts, 'autocommit', bool) + util.coerce_kw_type(opts, 'buffered', bool) + util.coerce_kw_type(opts, 'compress', bool) + util.coerce_kw_type(opts, 'connection_timeout', int) + util.coerce_kw_type(opts, 'connect_timeout', int) + util.coerce_kw_type(opts, 'consume_results', bool) + util.coerce_kw_type(opts, 'force_ipv6', bool) + util.coerce_kw_type(opts, 'get_warnings', bool) + util.coerce_kw_type(opts, 'pool_reset_session', bool) + util.coerce_kw_type(opts, 'pool_size', int) + util.coerce_kw_type(opts, 'raise_on_warnings', bool) + util.coerce_kw_type(opts, 'raw', bool) + util.coerce_kw_type(opts, 'ssl_verify_cert', bool) + util.coerce_kw_type(opts, 'use_pure', bool) + util.coerce_kw_type(opts, 'use_unicode', bool) + + # unfortunately, MySQL/connector python refuses to release a + # cursor without reading fully, so non-buffered isn't an option + opts.setdefault('buffered', True) + + # FOUND_ROWS must be set in ClientFlag to enable + # supports_sane_rowcount. + if self.dbapi is not None: + try: + from mysql.connector.constants import ClientFlag + client_flags = opts.get( + 'client_flags', ClientFlag.get_default()) + client_flags |= ClientFlag.FOUND_ROWS + opts['client_flags'] = client_flags + except Exception: + pass + return [[], opts] + + @util.memoized_property + def _mysqlconnector_version_info(self): + if self.dbapi and hasattr(self.dbapi, '__version__'): + m = re.match(r'(\d+)\.(\d+)(?:\.(\d+))?', + self.dbapi.__version__) + if m: + return tuple( + int(x) + for x in m.group(1, 2, 3) + if x is not None) + + @util.memoized_property + def _mysqlconnector_double_percents(self): + return not util.py3k and self._mysqlconnector_version_info < (2, 0) + + def _detect_charset(self, connection): + return connection.connection.charset + + def _extract_error_code(self, exception): + return exception.errno + + def is_disconnect(self, e, connection, cursor): + errnos = (2006, 2013, 2014, 2045, 2055, 2048) + exceptions = (self.dbapi.OperationalError, self.dbapi.InterfaceError) + if isinstance(e, exceptions): + return e.errno in errnos or \ + "MySQL Connection not available." in str(e) + else: + return False + + def _compat_fetchall(self, rp, charset=None): + return rp.fetchall() + + def _compat_fetchone(self, rp, charset=None): + return rp.fetchone() + + _isolation_lookup = set(['SERIALIZABLE', 'READ UNCOMMITTED', + 'READ COMMITTED', 'REPEATABLE READ', + 'AUTOCOMMIT']) + + def _set_isolation_level(self, connection, level): + if level == 'AUTOCOMMIT': + connection.autocommit = True + else: + connection.autocommit = False + super(MySQLDialect_mysqlconnector, self)._set_isolation_level( + connection, level) + + +dialect = MySQLDialect_mysqlconnector diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/mysqldb.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/mysqldb.py new file mode 100644 index 0000000..d397b5c --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/mysqldb.py @@ -0,0 +1,210 @@ +# mysql/mysqldb.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" + +.. dialect:: mysql+mysqldb + :name: MySQL-Python + :dbapi: mysqldb + :connectstring: mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname> + :url: http://sourceforge.net/projects/mysql-python + +.. _mysqldb_unicode: + +Unicode +------- + +Please see :ref:`mysql_unicode` for current recommendations on unicode +handling. + +Py3K Support +------------ + +Currently, MySQLdb only runs on Python 2 and development has been stopped. +`mysqlclient`_ is fork of MySQLdb and provides Python 3 support as well +as some bugfixes. + +.. _mysqlclient: https://github.com/PyMySQL/mysqlclient-python + +Using MySQLdb with Google Cloud SQL +----------------------------------- + +Google Cloud SQL now recommends use of the MySQLdb dialect. Connect +using a URL like the following:: + + mysql+mysqldb://root@/<dbname>?unix_socket=/cloudsql/<projectid>:<instancename> + +Server Side Cursors +------------------- + +The mysqldb dialect supports server-side cursors. See :ref:`mysql_ss_cursors`. + +""" + +from .base import (MySQLDialect, MySQLExecutionContext, + MySQLCompiler, MySQLIdentifierPreparer) +from .base import TEXT +from ... import sql +from ... import util +import re + + +class MySQLExecutionContext_mysqldb(MySQLExecutionContext): + + @property + def rowcount(self): + if hasattr(self, '_rowcount'): + return self._rowcount + else: + return self.cursor.rowcount + + +class MySQLCompiler_mysqldb(MySQLCompiler): + pass + + +class MySQLIdentifierPreparer_mysqldb(MySQLIdentifierPreparer): + pass + + +class MySQLDialect_mysqldb(MySQLDialect): + driver = 'mysqldb' + supports_unicode_statements = True + supports_sane_rowcount = True + supports_sane_multi_rowcount = True + + supports_native_decimal = True + + default_paramstyle = 'format' + execution_ctx_cls = MySQLExecutionContext_mysqldb + statement_compiler = MySQLCompiler_mysqldb + preparer = MySQLIdentifierPreparer_mysqldb + + def __init__(self, server_side_cursors=False, **kwargs): + super(MySQLDialect_mysqldb, self).__init__(**kwargs) + self.server_side_cursors = server_side_cursors + + @util.langhelpers.memoized_property + def supports_server_side_cursors(self): + try: + cursors = __import__('MySQLdb.cursors').cursors + self._sscursor = cursors.SSCursor + return True + except (ImportError, AttributeError): + return False + + @classmethod + def dbapi(cls): + return __import__('MySQLdb') + + def do_executemany(self, cursor, statement, parameters, context=None): + rowcount = cursor.executemany(statement, parameters) + if context is not None: + context._rowcount = rowcount + + def _check_unicode_returns(self, connection): + # work around issue fixed in + # https://github.com/farcepest/MySQLdb1/commit/cd44524fef63bd3fcb71947392326e9742d520e8 + # specific issue w/ the utf8mb4_bin collation and unicode returns + + has_utf8mb4_bin = self.server_version_info > (5, ) and \ + connection.scalar( + "show collation where %s = 'utf8mb4' and %s = 'utf8mb4_bin'" + % ( + self.identifier_preparer.quote("Charset"), + self.identifier_preparer.quote("Collation") + )) + if has_utf8mb4_bin: + additional_tests = [ + sql.collate(sql.cast( + sql.literal_column( + "'test collated returns'"), + TEXT(charset='utf8mb4')), "utf8mb4_bin") + ] + else: + additional_tests = [] + return super(MySQLDialect_mysqldb, self)._check_unicode_returns( + connection, additional_tests) + + def create_connect_args(self, url): + opts = url.translate_connect_args(database='db', username='user', + password='passwd') + opts.update(url.query) + + util.coerce_kw_type(opts, 'compress', bool) + util.coerce_kw_type(opts, 'connect_timeout', int) + util.coerce_kw_type(opts, 'read_timeout', int) + util.coerce_kw_type(opts, 'write_timeout', int) + util.coerce_kw_type(opts, 'client_flag', int) + util.coerce_kw_type(opts, 'local_infile', int) + # Note: using either of the below will cause all strings to be + # returned as Unicode, both in raw SQL operations and with column + # types like String and MSString. + util.coerce_kw_type(opts, 'use_unicode', bool) + util.coerce_kw_type(opts, 'charset', str) + + # Rich values 'cursorclass' and 'conv' are not supported via + # query string. + + ssl = {} + keys = ['ssl_ca', 'ssl_key', 'ssl_cert', 'ssl_capath', 'ssl_cipher'] + for key in keys: + if key in opts: + ssl[key[4:]] = opts[key] + util.coerce_kw_type(ssl, key[4:], str) + del opts[key] + if ssl: + opts['ssl'] = ssl + + # FOUND_ROWS must be set in CLIENT_FLAGS to enable + # supports_sane_rowcount. + client_flag = opts.get('client_flag', 0) + if self.dbapi is not None: + try: + CLIENT_FLAGS = __import__( + self.dbapi.__name__ + '.constants.CLIENT' + ).constants.CLIENT + client_flag |= CLIENT_FLAGS.FOUND_ROWS + except (AttributeError, ImportError): + self.supports_sane_rowcount = False + opts['client_flag'] = client_flag + return [[], opts] + + def _extract_error_code(self, exception): + return exception.args[0] + + def _detect_charset(self, connection): + """Sniff out the character set in use for connection results.""" + + try: + # note: the SQL here would be + # "SHOW VARIABLES LIKE 'character_set%%'" + cset_name = connection.connection.character_set_name + except AttributeError: + util.warn( + "No 'character_set_name' can be detected with " + "this MySQL-Python version; " + "please upgrade to a recent version of MySQL-Python. " + "Assuming latin1.") + return 'latin1' + else: + return cset_name() + + _isolation_lookup = set(['SERIALIZABLE', 'READ UNCOMMITTED', + 'READ COMMITTED', 'REPEATABLE READ', + 'AUTOCOMMIT']) + + def _set_isolation_level(self, connection, level): + if level == 'AUTOCOMMIT': + connection.autocommit(True) + else: + connection.autocommit(False) + super(MySQLDialect_mysqldb, self)._set_isolation_level(connection, + level) + + +dialect = MySQLDialect_mysqldb diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/oursql.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/oursql.py new file mode 100644 index 0000000..67dbb7c --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/oursql.py @@ -0,0 +1,243 @@ +# mysql/oursql.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" + +.. dialect:: mysql+oursql + :name: OurSQL + :dbapi: oursql + :connectstring: mysql+oursql://<user>:<password>@<host>[:<port>]/<dbname> + :url: http://packages.python.org/oursql/ + +Unicode +------- + +Please see :ref:`mysql_unicode` for current recommendations on unicode +handling. + + +""" + +import re + +from .base import (BIT, MySQLDialect, MySQLExecutionContext) +from ... import types as sqltypes, util + + +class _oursqlBIT(BIT): + def result_processor(self, dialect, coltype): + """oursql already converts mysql bits, so.""" + + return None + + +class MySQLExecutionContext_oursql(MySQLExecutionContext): + + @property + def plain_query(self): + return self.execution_options.get('_oursql_plain_query', False) + + +class MySQLDialect_oursql(MySQLDialect): + driver = 'oursql' + + if util.py2k: + supports_unicode_binds = True + supports_unicode_statements = True + + supports_native_decimal = True + + supports_sane_rowcount = True + supports_sane_multi_rowcount = True + execution_ctx_cls = MySQLExecutionContext_oursql + + colspecs = util.update_copy( + MySQLDialect.colspecs, + { + sqltypes.Time: sqltypes.Time, + BIT: _oursqlBIT, + } + ) + + @classmethod + def dbapi(cls): + return __import__('oursql') + + def do_execute(self, cursor, statement, parameters, context=None): + """Provide an implementation of + *cursor.execute(statement, parameters)*.""" + + if context and context.plain_query: + cursor.execute(statement, plain_query=True) + else: + cursor.execute(statement, parameters) + + def do_begin(self, connection): + connection.cursor().execute('BEGIN', plain_query=True) + + def _xa_query(self, connection, query, xid): + if util.py2k: + arg = connection.connection._escape_string(xid) + else: + charset = self._connection_charset + arg = connection.connection._escape_string( + xid.encode(charset)).decode(charset) + arg = "'%s'" % arg + connection.execution_options( + _oursql_plain_query=True).execute(query % arg) + + # Because mysql is bad, these methods have to be + # reimplemented to use _PlainQuery. Basically, some queries + # refuse to return any data if they're run through + # the parameterized query API, or refuse to be parameterized + # in the first place. + def do_begin_twophase(self, connection, xid): + self._xa_query(connection, 'XA BEGIN %s', xid) + + def do_prepare_twophase(self, connection, xid): + self._xa_query(connection, 'XA END %s', xid) + self._xa_query(connection, 'XA PREPARE %s', xid) + + def do_rollback_twophase(self, connection, xid, is_prepared=True, + recover=False): + if not is_prepared: + self._xa_query(connection, 'XA END %s', xid) + self._xa_query(connection, 'XA ROLLBACK %s', xid) + + def do_commit_twophase(self, connection, xid, is_prepared=True, + recover=False): + if not is_prepared: + self.do_prepare_twophase(connection, xid) + self._xa_query(connection, 'XA COMMIT %s', xid) + + # Q: why didn't we need all these "plain_query" overrides earlier ? + # am i on a newer/older version of OurSQL ? + def has_table(self, connection, table_name, schema=None): + return MySQLDialect.has_table( + self, + connection.connect().execution_options(_oursql_plain_query=True), + table_name, + schema + ) + + def get_table_options(self, connection, table_name, schema=None, **kw): + return MySQLDialect.get_table_options( + self, + connection.connect().execution_options(_oursql_plain_query=True), + table_name, + schema=schema, + **kw + ) + + def get_columns(self, connection, table_name, schema=None, **kw): + return MySQLDialect.get_columns( + self, + connection.connect().execution_options(_oursql_plain_query=True), + table_name, + schema=schema, + **kw + ) + + def get_view_names(self, connection, schema=None, **kw): + return MySQLDialect.get_view_names( + self, + connection.connect().execution_options(_oursql_plain_query=True), + schema=schema, + **kw + ) + + def get_table_names(self, connection, schema=None, **kw): + return MySQLDialect.get_table_names( + self, + connection.connect().execution_options(_oursql_plain_query=True), + schema + ) + + def get_schema_names(self, connection, **kw): + return MySQLDialect.get_schema_names( + self, + connection.connect().execution_options(_oursql_plain_query=True), + **kw + ) + + def initialize(self, connection): + return MySQLDialect.initialize( + self, + connection.execution_options(_oursql_plain_query=True) + ) + + def _show_create_table(self, connection, table, charset=None, + full_name=None): + return MySQLDialect._show_create_table( + self, + connection.contextual_connect(close_with_result=True). + execution_options(_oursql_plain_query=True), + table, charset, full_name + ) + + def is_disconnect(self, e, connection, cursor): + if isinstance(e, self.dbapi.ProgrammingError): + return e.errno is None and 'cursor' not in e.args[1] \ + and e.args[1].endswith('closed') + else: + return e.errno in (2006, 2013, 2014, 2045, 2055) + + def create_connect_args(self, url): + opts = url.translate_connect_args(database='db', username='user', + password='passwd') + opts.update(url.query) + + util.coerce_kw_type(opts, 'port', int) + util.coerce_kw_type(opts, 'compress', bool) + util.coerce_kw_type(opts, 'autoping', bool) + util.coerce_kw_type(opts, 'raise_on_warnings', bool) + + util.coerce_kw_type(opts, 'default_charset', bool) + if opts.pop('default_charset', False): + opts['charset'] = None + else: + util.coerce_kw_type(opts, 'charset', str) + opts['use_unicode'] = opts.get('use_unicode', True) + util.coerce_kw_type(opts, 'use_unicode', bool) + + # FOUND_ROWS must be set in CLIENT_FLAGS to enable + # supports_sane_rowcount. + opts.setdefault('found_rows', True) + + ssl = {} + for key in ['ssl_ca', 'ssl_key', 'ssl_cert', + 'ssl_capath', 'ssl_cipher']: + if key in opts: + ssl[key[4:]] = opts[key] + util.coerce_kw_type(ssl, key[4:], str) + del opts[key] + if ssl: + opts['ssl'] = ssl + + return [[], opts] + + def _extract_error_code(self, exception): + return exception.errno + + def _detect_charset(self, connection): + """Sniff out the character set in use for connection results.""" + + return connection.connection.charset + + def _compat_fetchall(self, rp, charset=None): + """oursql isn't super-broken like MySQLdb, yaaay.""" + return rp.fetchall() + + def _compat_fetchone(self, rp, charset=None): + """oursql isn't super-broken like MySQLdb, yaaay.""" + return rp.fetchone() + + def _compat_first(self, rp, charset=None): + return rp.first() + + +dialect = MySQLDialect_oursql diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/pymysql.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/pymysql.py new file mode 100644 index 0000000..4f1c792 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/pymysql.py @@ -0,0 +1,70 @@ +# mysql/pymysql.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" + +.. dialect:: mysql+pymysql + :name: PyMySQL + :dbapi: pymysql + :connectstring: mysql+pymysql://<username>:<password>@<host>/<dbname>\ +[?<options>] + :url: https://pymysql.readthedocs.io/ + +Unicode +------- + +Please see :ref:`mysql_unicode` for current recommendations on unicode +handling. + +MySQL-Python Compatibility +-------------------------- + +The pymysql DBAPI is a pure Python port of the MySQL-python (MySQLdb) driver, +and targets 100% compatibility. Most behavioral notes for MySQL-python apply +to the pymysql driver as well. + +""" + +from .mysqldb import MySQLDialect_mysqldb +from ...util import langhelpers, py3k + + +class MySQLDialect_pymysql(MySQLDialect_mysqldb): + driver = 'pymysql' + + description_encoding = None + + # generally, these two values should be both True + # or both False. PyMySQL unicode tests pass all the way back + # to 0.4 either way. See [ticket:3337] + supports_unicode_statements = True + supports_unicode_binds = True + + def __init__(self, server_side_cursors=False, **kwargs): + super(MySQLDialect_pymysql, self).__init__(**kwargs) + self.server_side_cursors = server_side_cursors + + @langhelpers.memoized_property + def supports_server_side_cursors(self): + try: + cursors = __import__('pymysql.cursors').cursors + self._sscursor = cursors.SSCursor + return True + except (ImportError, AttributeError): + return False + + @classmethod + def dbapi(cls): + return __import__('pymysql') + + if py3k: + def _extract_error_code(self, exception): + if isinstance(exception.args[0], Exception): + exception = exception.args[0] + return exception.args[0] + +dialect = MySQLDialect_pymysql diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/pyodbc.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/pyodbc.py new file mode 100644 index 0000000..7187546 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/pyodbc.py @@ -0,0 +1,79 @@ +# mysql/pyodbc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" + + +.. dialect:: mysql+pyodbc + :name: PyODBC + :dbapi: pyodbc + :connectstring: mysql+pyodbc://<username>:<password>@<dsnname> + :url: http://pypi.python.org/pypi/pyodbc/ + + .. note:: The PyODBC for MySQL dialect is not well supported, and + is subject to unresolved character encoding issues + which exist within the current ODBC drivers available. + (see http://code.google.com/p/pyodbc/issues/detail?id=25). + Other dialects for MySQL are recommended. + +""" + +from .base import MySQLDialect, MySQLExecutionContext +from ...connectors.pyodbc import PyODBCConnector +from ... import util +import re + + +class MySQLExecutionContext_pyodbc(MySQLExecutionContext): + + def get_lastrowid(self): + cursor = self.create_cursor() + cursor.execute("SELECT LAST_INSERT_ID()") + lastrowid = cursor.fetchone()[0] + cursor.close() + return lastrowid + + +class MySQLDialect_pyodbc(PyODBCConnector, MySQLDialect): + supports_unicode_statements = False + execution_ctx_cls = MySQLExecutionContext_pyodbc + + pyodbc_driver_name = "MySQL" + + def __init__(self, **kw): + # deal with http://code.google.com/p/pyodbc/issues/detail?id=25 + kw.setdefault('convert_unicode', True) + super(MySQLDialect_pyodbc, self).__init__(**kw) + + def _detect_charset(self, connection): + """Sniff out the character set in use for connection results.""" + + # Prefer 'character_set_results' for the current connection over the + # value in the driver. SET NAMES or individual variable SETs will + # change the charset without updating the driver's view of the world. + # + # If it's decided that issuing that sort of SQL leaves you SOL, then + # this can prefer the driver value. + rs = connection.execute("SHOW VARIABLES LIKE 'character_set%%'") + opts = {row[0]: row[1] for row in self._compat_fetchall(rs)} + for key in ('character_set_connection', 'character_set'): + if opts.get(key, None): + return opts[key] + + util.warn("Could not detect the connection character set. " + "Assuming latin1.") + return 'latin1' + + def _extract_error_code(self, exception): + m = re.compile(r"\((\d+)\)").search(str(exception.args)) + c = m.group(1) + if c: + return int(c) + else: + return None + +dialect = MySQLDialect_pyodbc diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/reflection.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/reflection.py new file mode 100644 index 0000000..3e7affe --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/reflection.py @@ -0,0 +1,479 @@ +# mysql/reflection.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +import re +from ... import log, util +from ... import types as sqltypes +from .enumerated import _EnumeratedValues, SET +from .types import DATETIME, TIME, TIMESTAMP + + +class ReflectedState(object): + """Stores raw information about a SHOW CREATE TABLE statement.""" + + def __init__(self): + self.columns = [] + self.table_options = {} + self.table_name = None + self.keys = [] + self.fk_constraints = [] + self.ck_constraints = [] + + +@log.class_logger +class MySQLTableDefinitionParser(object): + """Parses the results of a SHOW CREATE TABLE statement.""" + + def __init__(self, dialect, preparer): + self.dialect = dialect + self.preparer = preparer + self._prep_regexes() + + def parse(self, show_create, charset): + state = ReflectedState() + state.charset = charset + for line in re.split(r'\r?\n', show_create): + if line.startswith(' ' + self.preparer.initial_quote): + self._parse_column(line, state) + # a regular table options line + elif line.startswith(') '): + self._parse_table_options(line, state) + # an ANSI-mode table options line + elif line == ')': + pass + elif line.startswith('CREATE '): + self._parse_table_name(line, state) + # Not present in real reflection, but may be if + # loading from a file. + elif not line: + pass + else: + type_, spec = self._parse_constraints(line) + if type_ is None: + util.warn("Unknown schema content: %r" % line) + elif type_ == 'key': + state.keys.append(spec) + elif type_ == 'fk_constraint': + state.fk_constraints.append(spec) + elif type_ == 'ck_constraint': + state.ck_constraints.append(spec) + else: + pass + return state + + def _parse_constraints(self, line): + """Parse a KEY or CONSTRAINT line. + + :param line: A line of SHOW CREATE TABLE output + """ + + # KEY + m = self._re_key.match(line) + if m: + spec = m.groupdict() + # convert columns into name, length pairs + # NOTE: we may want to consider SHOW INDEX as the + # format of indexes in MySQL becomes more complex + spec['columns'] = self._parse_keyexprs(spec['columns']) + return 'key', spec + + # FOREIGN KEY CONSTRAINT + m = self._re_fk_constraint.match(line) + if m: + spec = m.groupdict() + spec['table'] = \ + self.preparer.unformat_identifiers(spec['table']) + spec['local'] = [c[0] + for c in self._parse_keyexprs(spec['local'])] + spec['foreign'] = [c[0] + for c in self._parse_keyexprs(spec['foreign'])] + return 'fk_constraint', spec + + # CHECK constraint + m = self._re_ck_constraint.match(line) + if m: + spec = m.groupdict() + return 'ck_constraint', spec + + # PARTITION and SUBPARTITION + m = self._re_partition.match(line) + if m: + # Punt! + return 'partition', line + + # No match. + return (None, line) + + def _parse_table_name(self, line, state): + """Extract the table name. + + :param line: The first line of SHOW CREATE TABLE + """ + + regex, cleanup = self._pr_name + m = regex.match(line) + if m: + state.table_name = cleanup(m.group('name')) + + def _parse_table_options(self, line, state): + """Build a dictionary of all reflected table-level options. + + :param line: The final line of SHOW CREATE TABLE output. + """ + + options = {} + + if not line or line == ')': + pass + + else: + rest_of_line = line[:] + for regex, cleanup in self._pr_options: + m = regex.search(rest_of_line) + if not m: + continue + directive, value = m.group('directive'), m.group('val') + if cleanup: + value = cleanup(value) + options[directive.lower()] = value + rest_of_line = regex.sub('', rest_of_line) + + for nope in ('auto_increment', 'data directory', 'index directory'): + options.pop(nope, None) + + for opt, val in options.items(): + state.table_options['%s_%s' % (self.dialect.name, opt)] = val + + def _parse_column(self, line, state): + """Extract column details. + + Falls back to a 'minimal support' variant if full parse fails. + + :param line: Any column-bearing line from SHOW CREATE TABLE + """ + + spec = None + m = self._re_column.match(line) + if m: + spec = m.groupdict() + spec['full'] = True + else: + m = self._re_column_loose.match(line) + if m: + spec = m.groupdict() + spec['full'] = False + if not spec: + util.warn("Unknown column definition %r" % line) + return + if not spec['full']: + util.warn("Incomplete reflection of column definition %r" % line) + + name, type_, args = spec['name'], spec['coltype'], spec['arg'] + + try: + col_type = self.dialect.ischema_names[type_] + except KeyError: + util.warn("Did not recognize type '%s' of column '%s'" % + (type_, name)) + col_type = sqltypes.NullType + + # Column type positional arguments eg. varchar(32) + if args is None or args == '': + type_args = [] + elif args[0] == "'" and args[-1] == "'": + type_args = self._re_csv_str.findall(args) + else: + type_args = [int(v) for v in self._re_csv_int.findall(args)] + + # Column type keyword options + type_kw = {} + + if issubclass(col_type, (DATETIME, TIME, TIMESTAMP)): + if type_args: + type_kw['fsp'] = type_args.pop(0) + + for kw in ('unsigned', 'zerofill'): + if spec.get(kw, False): + type_kw[kw] = True + for kw in ('charset', 'collate'): + if spec.get(kw, False): + type_kw[kw] = spec[kw] + if issubclass(col_type, _EnumeratedValues): + type_args = _EnumeratedValues._strip_values(type_args) + + if issubclass(col_type, SET) and '' in type_args: + type_kw['retrieve_as_bitwise'] = True + + type_instance = col_type(*type_args, **type_kw) + + col_kw = {} + + # NOT NULL + col_kw['nullable'] = True + # this can be "NULL" in the case of TIMESTAMP + if spec.get('notnull', False) == 'NOT NULL': + col_kw['nullable'] = False + + # AUTO_INCREMENT + if spec.get('autoincr', False): + col_kw['autoincrement'] = True + elif issubclass(col_type, sqltypes.Integer): + col_kw['autoincrement'] = False + + # DEFAULT + default = spec.get('default', None) + + if default == 'NULL': + # eliminates the need to deal with this later. + default = None + + comment = spec.get('comment', None) + + if comment is not None: + comment = comment.replace("\\\\", "\\").replace("''", "'") + + col_d = dict(name=name, type=type_instance, default=default, + comment=comment) + col_d.update(col_kw) + state.columns.append(col_d) + + def _describe_to_create(self, table_name, columns): + """Re-format DESCRIBE output as a SHOW CREATE TABLE string. + + DESCRIBE is a much simpler reflection and is sufficient for + reflecting views for runtime use. This method formats DDL + for columns only- keys are omitted. + + :param columns: A sequence of DESCRIBE or SHOW COLUMNS 6-tuples. + SHOW FULL COLUMNS FROM rows must be rearranged for use with + this function. + """ + + buffer = [] + for row in columns: + (name, col_type, nullable, default, extra) = \ + [row[i] for i in (0, 1, 2, 4, 5)] + + line = [' '] + line.append(self.preparer.quote_identifier(name)) + line.append(col_type) + if not nullable: + line.append('NOT NULL') + if default: + if 'auto_increment' in default: + pass + elif (col_type.startswith('timestamp') and + default.startswith('C')): + line.append('DEFAULT') + line.append(default) + elif default == 'NULL': + line.append('DEFAULT') + line.append(default) + else: + line.append('DEFAULT') + line.append("'%s'" % default.replace("'", "''")) + if extra: + line.append(extra) + + buffer.append(' '.join(line)) + + return ''.join([('CREATE TABLE %s (\n' % + self.preparer.quote_identifier(table_name)), + ',\n'.join(buffer), + '\n) ']) + + def _parse_keyexprs(self, identifiers): + """Unpack '"col"(2),"col" ASC'-ish strings into components.""" + + return self._re_keyexprs.findall(identifiers) + + def _prep_regexes(self): + """Pre-compile regular expressions.""" + + self._re_columns = [] + self._pr_options = [] + + _final = self.preparer.final_quote + + quotes = dict(zip(('iq', 'fq', 'esc_fq'), + [re.escape(s) for s in + (self.preparer.initial_quote, + _final, + self.preparer._escape_identifier(_final))])) + + self._pr_name = _pr_compile( + r'^CREATE (?:\w+ +)?TABLE +' + r'%(iq)s(?P<name>(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s +\($' % quotes, + self.preparer._unescape_identifier) + + # `col`,`col2`(32),`col3`(15) DESC + # + self._re_keyexprs = _re_compile( + r'(?:' + r'(?:%(iq)s((?:%(esc_fq)s|[^%(fq)s])+)%(fq)s)' + r'(?:\((\d+)\))?(?: +(ASC|DESC))?(?=\,|$))+' % quotes) + + # 'foo' or 'foo','bar' or 'fo,o','ba''a''r' + self._re_csv_str = _re_compile(r'\x27(?:\x27\x27|[^\x27])*\x27') + + # 123 or 123,456 + self._re_csv_int = _re_compile(r'\d+') + + # `colname` <type> [type opts] + # (NOT NULL | NULL) + # DEFAULT ('value' | CURRENT_TIMESTAMP...) + # COMMENT 'comment' + # COLUMN_FORMAT (FIXED|DYNAMIC|DEFAULT) + # STORAGE (DISK|MEMORY) + self._re_column = _re_compile( + r" " + r"%(iq)s(?P<name>(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s +" + r"(?P<coltype>\w+)" + r"(?:\((?P<arg>(?:\d+|\d+,\d+|" + r"(?:'(?:''|[^'])*',?)+))\))?" + r"(?: +(?P<unsigned>UNSIGNED))?" + r"(?: +(?P<zerofill>ZEROFILL))?" + r"(?: +CHARACTER SET +(?P<charset>[\w_]+))?" + r"(?: +COLLATE +(?P<collate>[\w_]+))?" + r"(?: +(?P<notnull>(?:NOT )?NULL))?" + r"(?: +DEFAULT +(?P<default>" + r"(?:NULL|'(?:''|[^'])*'|[\w\(\)]+" + r"(?: +ON UPDATE [\w\(\)]+)?)" + r"))?" + r"(?: +(?P<autoincr>AUTO_INCREMENT))?" + r"(?: +COMMENT +'(?P<comment>(?:''|[^'])*)')?" + r"(?: +COLUMN_FORMAT +(?P<colfmt>\w+))?" + r"(?: +STORAGE +(?P<storage>\w+))?" + r"(?: +(?P<extra>.*))?" + r",?$" + % quotes + ) + + # Fallback, try to parse as little as possible + self._re_column_loose = _re_compile( + r' ' + r'%(iq)s(?P<name>(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s +' + r'(?P<coltype>\w+)' + r'(?:\((?P<arg>(?:\d+|\d+,\d+|\x27(?:\x27\x27|[^\x27])+\x27))\))?' + r'.*?(?P<notnull>(?:NOT )NULL)?' + % quotes + ) + + # (PRIMARY|UNIQUE|FULLTEXT|SPATIAL) INDEX `name` (USING (BTREE|HASH))? + # (`col` (ASC|DESC)?, `col` (ASC|DESC)?) + # KEY_BLOCK_SIZE size | WITH PARSER name + self._re_key = _re_compile( + r' ' + r'(?:(?P<type>\S+) )?KEY' + r'(?: +%(iq)s(?P<name>(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s)?' + r'(?: +USING +(?P<using_pre>\S+))?' + r' +\((?P<columns>.+?)\)' + r'(?: +USING +(?P<using_post>\S+))?' + r'(?: +KEY_BLOCK_SIZE *[ =]? *(?P<keyblock>\S+))?' + r'(?: +WITH PARSER +(?P<parser>\S+))?' + r'(?: +COMMENT +(?P<comment>(\x27\x27|\x27([^\x27])*?\x27)+))?' + r',?$' + % quotes + ) + + # CONSTRAINT `name` FOREIGN KEY (`local_col`) + # REFERENCES `remote` (`remote_col`) + # MATCH FULL | MATCH PARTIAL | MATCH SIMPLE + # ON DELETE CASCADE ON UPDATE RESTRICT + # + # unique constraints come back as KEYs + kw = quotes.copy() + kw['on'] = 'RESTRICT|CASCADE|SET NULL|NOACTION' + self._re_fk_constraint = _re_compile( + r' ' + r'CONSTRAINT +' + r'%(iq)s(?P<name>(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s +' + r'FOREIGN KEY +' + r'\((?P<local>[^\)]+?)\) REFERENCES +' + r'(?P<table>%(iq)s[^%(fq)s]+%(fq)s' + r'(?:\.%(iq)s[^%(fq)s]+%(fq)s)?) +' + r'\((?P<foreign>[^\)]+?)\)' + r'(?: +(?P<match>MATCH \w+))?' + r'(?: +ON DELETE (?P<ondelete>%(on)s))?' + r'(?: +ON UPDATE (?P<onupdate>%(on)s))?' + % kw + ) + + # CONSTRAINT `CONSTRAINT_1` CHECK (`x` > 5)' + # testing on MariaDB 10.2 shows that the CHECK constraint + # is returned on a line by itself, so to match without worrying + # about parenthesis in the expresion we go to the end of the line + self._re_ck_constraint = _re_compile( + r' ' + r'CONSTRAINT +' + r'%(iq)s(?P<name>(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s +' + r'CHECK +' + r'\((?P<sqltext>.+)\),?' + % kw + ) + + # PARTITION + # + # punt! + self._re_partition = _re_compile(r'(?:.*)(?:SUB)?PARTITION(?:.*)') + + # Table-level options (COLLATE, ENGINE, etc.) + # Do the string options first, since they have quoted + # strings we need to get rid of. + for option in _options_of_type_string: + self._add_option_string(option) + + for option in ('ENGINE', 'TYPE', 'AUTO_INCREMENT', + 'AVG_ROW_LENGTH', 'CHARACTER SET', + 'DEFAULT CHARSET', 'CHECKSUM', + 'COLLATE', 'DELAY_KEY_WRITE', 'INSERT_METHOD', + 'MAX_ROWS', 'MIN_ROWS', 'PACK_KEYS', 'ROW_FORMAT', + 'KEY_BLOCK_SIZE'): + self._add_option_word(option) + + self._add_option_regex('UNION', r'\([^\)]+\)') + self._add_option_regex('TABLESPACE', r'.*? STORAGE DISK') + self._add_option_regex( + 'RAID_TYPE', + r'\w+\s+RAID_CHUNKS\s*\=\s*\w+RAID_CHUNKSIZE\s*=\s*\w+') + + _optional_equals = r'(?:\s*(?:=\s*)|\s+)' + + def _add_option_string(self, directive): + regex = (r'(?P<directive>%s)%s' + r"'(?P<val>(?:[^']|'')*?)'(?!')" % + (re.escape(directive), self._optional_equals)) + self._pr_options.append(_pr_compile( + regex, lambda v: v.replace("\\\\", "\\").replace("''", "'") + )) + + def _add_option_word(self, directive): + regex = (r'(?P<directive>%s)%s' + r'(?P<val>\w+)' % + (re.escape(directive), self._optional_equals)) + self._pr_options.append(_pr_compile(regex)) + + def _add_option_regex(self, directive, regex): + regex = (r'(?P<directive>%s)%s' + r'(?P<val>%s)' % + (re.escape(directive), self._optional_equals, regex)) + self._pr_options.append(_pr_compile(regex)) + +_options_of_type_string = ('COMMENT', 'DATA DIRECTORY', 'INDEX DIRECTORY', + 'PASSWORD', 'CONNECTION') + + +def _pr_compile(regex, cleanup=None): + """Prepare a 2-tuple of compiled regex and callable.""" + + return (_re_compile(regex), cleanup) + + +def _re_compile(regex): + """Compile a string to regex, I and UNICODE.""" + + return re.compile(regex, re.I | re.UNICODE) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/types.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/types.py new file mode 100644 index 0000000..cb09a08 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/types.py @@ -0,0 +1,766 @@ +# mysql/types.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +import datetime +from ... import exc, util +from ... import types as sqltypes + + +class _NumericType(object): + """Base for MySQL numeric types. + + This is the base both for NUMERIC as well as INTEGER, hence + it's a mixin. + + """ + + def __init__(self, unsigned=False, zerofill=False, **kw): + self.unsigned = unsigned + self.zerofill = zerofill + super(_NumericType, self).__init__(**kw) + + def __repr__(self): + return util.generic_repr(self, + to_inspect=[_NumericType, sqltypes.Numeric]) + + +class _FloatType(_NumericType, sqltypes.Float): + def __init__(self, precision=None, scale=None, asdecimal=True, **kw): + if isinstance(self, (REAL, DOUBLE)) and \ + ( + (precision is None and scale is not None) or + (precision is not None and scale is None) + ): + raise exc.ArgumentError( + "You must specify both precision and scale or omit " + "both altogether.") + super(_FloatType, self).__init__( + precision=precision, asdecimal=asdecimal, **kw) + self.scale = scale + + def __repr__(self): + return util.generic_repr(self, to_inspect=[_FloatType, + _NumericType, + sqltypes.Float]) + + +class _IntegerType(_NumericType, sqltypes.Integer): + def __init__(self, display_width=None, **kw): + self.display_width = display_width + super(_IntegerType, self).__init__(**kw) + + def __repr__(self): + return util.generic_repr(self, to_inspect=[_IntegerType, + _NumericType, + sqltypes.Integer]) + + +class _StringType(sqltypes.String): + """Base for MySQL string types.""" + + def __init__(self, charset=None, collation=None, + ascii=False, binary=False, unicode=False, + national=False, **kw): + self.charset = charset + + # allow collate= or collation= + kw.setdefault('collation', kw.pop('collate', collation)) + + self.ascii = ascii + self.unicode = unicode + self.binary = binary + self.national = national + super(_StringType, self).__init__(**kw) + + def __repr__(self): + return util.generic_repr(self, + to_inspect=[_StringType, sqltypes.String]) + + +class _MatchType(sqltypes.Float, sqltypes.MatchType): + def __init__(self, **kw): + # TODO: float arguments? + sqltypes.Float.__init__(self) + sqltypes.MatchType.__init__(self) + + + +class NUMERIC(_NumericType, sqltypes.NUMERIC): + """MySQL NUMERIC type.""" + + __visit_name__ = 'NUMERIC' + + def __init__(self, precision=None, scale=None, asdecimal=True, **kw): + """Construct a NUMERIC. + + :param precision: Total digits in this number. If scale and precision + are both None, values are stored to limits allowed by the server. + + :param scale: The number of digits after the decimal point. + + :param unsigned: a boolean, optional. + + :param zerofill: Optional. If true, values will be stored as strings + left-padded with zeros. Note that this does not effect the values + returned by the underlying database API, which continue to be + numeric. + + """ + super(NUMERIC, self).__init__(precision=precision, + scale=scale, asdecimal=asdecimal, **kw) + + +class DECIMAL(_NumericType, sqltypes.DECIMAL): + """MySQL DECIMAL type.""" + + __visit_name__ = 'DECIMAL' + + def __init__(self, precision=None, scale=None, asdecimal=True, **kw): + """Construct a DECIMAL. + + :param precision: Total digits in this number. If scale and precision + are both None, values are stored to limits allowed by the server. + + :param scale: The number of digits after the decimal point. + + :param unsigned: a boolean, optional. + + :param zerofill: Optional. If true, values will be stored as strings + left-padded with zeros. Note that this does not effect the values + returned by the underlying database API, which continue to be + numeric. + + """ + super(DECIMAL, self).__init__(precision=precision, scale=scale, + asdecimal=asdecimal, **kw) + + +class DOUBLE(_FloatType): + """MySQL DOUBLE type.""" + + __visit_name__ = 'DOUBLE' + + def __init__(self, precision=None, scale=None, asdecimal=True, **kw): + """Construct a DOUBLE. + + .. note:: + + The :class:`.DOUBLE` type by default converts from float + to Decimal, using a truncation that defaults to 10 digits. + Specify either ``scale=n`` or ``decimal_return_scale=n`` in order + to change this scale, or ``asdecimal=False`` to return values + directly as Python floating points. + + :param precision: Total digits in this number. If scale and precision + are both None, values are stored to limits allowed by the server. + + :param scale: The number of digits after the decimal point. + + :param unsigned: a boolean, optional. + + :param zerofill: Optional. If true, values will be stored as strings + left-padded with zeros. Note that this does not effect the values + returned by the underlying database API, which continue to be + numeric. + + """ + super(DOUBLE, self).__init__(precision=precision, scale=scale, + asdecimal=asdecimal, **kw) + + +class REAL(_FloatType, sqltypes.REAL): + """MySQL REAL type.""" + + __visit_name__ = 'REAL' + + def __init__(self, precision=None, scale=None, asdecimal=True, **kw): + """Construct a REAL. + + .. note:: + + The :class:`.REAL` type by default converts from float + to Decimal, using a truncation that defaults to 10 digits. + Specify either ``scale=n`` or ``decimal_return_scale=n`` in order + to change this scale, or ``asdecimal=False`` to return values + directly as Python floating points. + + :param precision: Total digits in this number. If scale and precision + are both None, values are stored to limits allowed by the server. + + :param scale: The number of digits after the decimal point. + + :param unsigned: a boolean, optional. + + :param zerofill: Optional. If true, values will be stored as strings + left-padded with zeros. Note that this does not effect the values + returned by the underlying database API, which continue to be + numeric. + + """ + super(REAL, self).__init__(precision=precision, scale=scale, + asdecimal=asdecimal, **kw) + + +class FLOAT(_FloatType, sqltypes.FLOAT): + """MySQL FLOAT type.""" + + __visit_name__ = 'FLOAT' + + def __init__(self, precision=None, scale=None, asdecimal=False, **kw): + """Construct a FLOAT. + + :param precision: Total digits in this number. If scale and precision + are both None, values are stored to limits allowed by the server. + + :param scale: The number of digits after the decimal point. + + :param unsigned: a boolean, optional. + + :param zerofill: Optional. If true, values will be stored as strings + left-padded with zeros. Note that this does not effect the values + returned by the underlying database API, which continue to be + numeric. + + """ + super(FLOAT, self).__init__(precision=precision, scale=scale, + asdecimal=asdecimal, **kw) + + def bind_processor(self, dialect): + return None + + +class INTEGER(_IntegerType, sqltypes.INTEGER): + """MySQL INTEGER type.""" + + __visit_name__ = 'INTEGER' + + def __init__(self, display_width=None, **kw): + """Construct an INTEGER. + + :param display_width: Optional, maximum display width for this number. + + :param unsigned: a boolean, optional. + + :param zerofill: Optional. If true, values will be stored as strings + left-padded with zeros. Note that this does not effect the values + returned by the underlying database API, which continue to be + numeric. + + """ + super(INTEGER, self).__init__(display_width=display_width, **kw) + + +class BIGINT(_IntegerType, sqltypes.BIGINT): + """MySQL BIGINTEGER type.""" + + __visit_name__ = 'BIGINT' + + def __init__(self, display_width=None, **kw): + """Construct a BIGINTEGER. + + :param display_width: Optional, maximum display width for this number. + + :param unsigned: a boolean, optional. + + :param zerofill: Optional. If true, values will be stored as strings + left-padded with zeros. Note that this does not effect the values + returned by the underlying database API, which continue to be + numeric. + + """ + super(BIGINT, self).__init__(display_width=display_width, **kw) + + +class MEDIUMINT(_IntegerType): + """MySQL MEDIUMINTEGER type.""" + + __visit_name__ = 'MEDIUMINT' + + def __init__(self, display_width=None, **kw): + """Construct a MEDIUMINTEGER + + :param display_width: Optional, maximum display width for this number. + + :param unsigned: a boolean, optional. + + :param zerofill: Optional. If true, values will be stored as strings + left-padded with zeros. Note that this does not effect the values + returned by the underlying database API, which continue to be + numeric. + + """ + super(MEDIUMINT, self).__init__(display_width=display_width, **kw) + + +class TINYINT(_IntegerType): + """MySQL TINYINT type.""" + + __visit_name__ = 'TINYINT' + + def __init__(self, display_width=None, **kw): + """Construct a TINYINT. + + :param display_width: Optional, maximum display width for this number. + + :param unsigned: a boolean, optional. + + :param zerofill: Optional. If true, values will be stored as strings + left-padded with zeros. Note that this does not effect the values + returned by the underlying database API, which continue to be + numeric. + + """ + super(TINYINT, self).__init__(display_width=display_width, **kw) + + +class SMALLINT(_IntegerType, sqltypes.SMALLINT): + """MySQL SMALLINTEGER type.""" + + __visit_name__ = 'SMALLINT' + + def __init__(self, display_width=None, **kw): + """Construct a SMALLINTEGER. + + :param display_width: Optional, maximum display width for this number. + + :param unsigned: a boolean, optional. + + :param zerofill: Optional. If true, values will be stored as strings + left-padded with zeros. Note that this does not effect the values + returned by the underlying database API, which continue to be + numeric. + + """ + super(SMALLINT, self).__init__(display_width=display_width, **kw) + + +class BIT(sqltypes.TypeEngine): + """MySQL BIT type. + + This type is for MySQL 5.0.3 or greater for MyISAM, and 5.0.5 or greater + for MyISAM, MEMORY, InnoDB and BDB. For older versions, use a + MSTinyInteger() type. + + """ + + __visit_name__ = 'BIT' + + def __init__(self, length=None): + """Construct a BIT. + + :param length: Optional, number of bits. + + """ + self.length = length + + def result_processor(self, dialect, coltype): + """Convert a MySQL's 64 bit, variable length binary string to a long. + + TODO: this is MySQL-db, pyodbc specific. OurSQL and mysqlconnector + already do this, so this logic should be moved to those dialects. + + """ + + def process(value): + if value is not None: + v = 0 + for i in value: + if not isinstance(i, int): + i = ord(i) # convert byte to int on Python 2 + v = v << 8 | i + return v + return value + return process + + +class TIME(sqltypes.TIME): + """MySQL TIME type. """ + + __visit_name__ = 'TIME' + + def __init__(self, timezone=False, fsp=None): + """Construct a MySQL TIME type. + + :param timezone: not used by the MySQL dialect. + :param fsp: fractional seconds precision value. + MySQL 5.6 supports storage of fractional seconds; + this parameter will be used when emitting DDL + for the TIME type. + + .. note:: + + DBAPI driver support for fractional seconds may + be limited; current support includes + MySQL Connector/Python. + + .. versionadded:: 0.8 The MySQL-specific TIME + type as well as fractional seconds support. + + """ + super(TIME, self).__init__(timezone=timezone) + self.fsp = fsp + + def result_processor(self, dialect, coltype): + time = datetime.time + + def process(value): + # convert from a timedelta value + if value is not None: + microseconds = value.microseconds + seconds = value.seconds + minutes = seconds // 60 + return time(minutes // 60, + minutes % 60, + seconds - minutes * 60, + microsecond=microseconds) + else: + return None + return process + + +class TIMESTAMP(sqltypes.TIMESTAMP): + """MySQL TIMESTAMP type. + + """ + + __visit_name__ = 'TIMESTAMP' + + def __init__(self, timezone=False, fsp=None): + """Construct a MySQL TIMESTAMP type. + + :param timezone: not used by the MySQL dialect. + :param fsp: fractional seconds precision value. + MySQL 5.6.4 supports storage of fractional seconds; + this parameter will be used when emitting DDL + for the TIMESTAMP type. + + .. note:: + + DBAPI driver support for fractional seconds may + be limited; current support includes + MySQL Connector/Python. + + .. versionadded:: 0.8.5 Added MySQL-specific :class:`.mysql.TIMESTAMP` + with fractional seconds support. + + """ + super(TIMESTAMP, self).__init__(timezone=timezone) + self.fsp = fsp + + +class DATETIME(sqltypes.DATETIME): + """MySQL DATETIME type. + + """ + + __visit_name__ = 'DATETIME' + + def __init__(self, timezone=False, fsp=None): + """Construct a MySQL DATETIME type. + + :param timezone: not used by the MySQL dialect. + :param fsp: fractional seconds precision value. + MySQL 5.6.4 supports storage of fractional seconds; + this parameter will be used when emitting DDL + for the DATETIME type. + + .. note:: + + DBAPI driver support for fractional seconds may + be limited; current support includes + MySQL Connector/Python. + + .. versionadded:: 0.8.5 Added MySQL-specific :class:`.mysql.DATETIME` + with fractional seconds support. + + """ + super(DATETIME, self).__init__(timezone=timezone) + self.fsp = fsp + + +class YEAR(sqltypes.TypeEngine): + """MySQL YEAR type, for single byte storage of years 1901-2155.""" + + __visit_name__ = 'YEAR' + + def __init__(self, display_width=None): + self.display_width = display_width + + +class TEXT(_StringType, sqltypes.TEXT): + """MySQL TEXT type, for text up to 2^16 characters.""" + + __visit_name__ = 'TEXT' + + def __init__(self, length=None, **kw): + """Construct a TEXT. + + :param length: Optional, if provided the server may optimize storage + by substituting the smallest TEXT type sufficient to store + ``length`` characters. + + :param charset: Optional, a column-level character set for this string + value. Takes precedence to 'ascii' or 'unicode' short-hand. + + :param collation: Optional, a column-level collation for this string + value. Takes precedence to 'binary' short-hand. + + :param ascii: Defaults to False: short-hand for the ``latin1`` + character set, generates ASCII in schema. + + :param unicode: Defaults to False: short-hand for the ``ucs2`` + character set, generates UNICODE in schema. + + :param national: Optional. If true, use the server's configured + national character set. + + :param binary: Defaults to False: short-hand, pick the binary + collation type that matches the column's character set. Generates + BINARY in schema. This does not affect the type of data stored, + only the collation of character data. + + """ + super(TEXT, self).__init__(length=length, **kw) + + +class TINYTEXT(_StringType): + """MySQL TINYTEXT type, for text up to 2^8 characters.""" + + __visit_name__ = 'TINYTEXT' + + def __init__(self, **kwargs): + """Construct a TINYTEXT. + + :param charset: Optional, a column-level character set for this string + value. Takes precedence to 'ascii' or 'unicode' short-hand. + + :param collation: Optional, a column-level collation for this string + value. Takes precedence to 'binary' short-hand. + + :param ascii: Defaults to False: short-hand for the ``latin1`` + character set, generates ASCII in schema. + + :param unicode: Defaults to False: short-hand for the ``ucs2`` + character set, generates UNICODE in schema. + + :param national: Optional. If true, use the server's configured + national character set. + + :param binary: Defaults to False: short-hand, pick the binary + collation type that matches the column's character set. Generates + BINARY in schema. This does not affect the type of data stored, + only the collation of character data. + + """ + super(TINYTEXT, self).__init__(**kwargs) + + +class MEDIUMTEXT(_StringType): + """MySQL MEDIUMTEXT type, for text up to 2^24 characters.""" + + __visit_name__ = 'MEDIUMTEXT' + + def __init__(self, **kwargs): + """Construct a MEDIUMTEXT. + + :param charset: Optional, a column-level character set for this string + value. Takes precedence to 'ascii' or 'unicode' short-hand. + + :param collation: Optional, a column-level collation for this string + value. Takes precedence to 'binary' short-hand. + + :param ascii: Defaults to False: short-hand for the ``latin1`` + character set, generates ASCII in schema. + + :param unicode: Defaults to False: short-hand for the ``ucs2`` + character set, generates UNICODE in schema. + + :param national: Optional. If true, use the server's configured + national character set. + + :param binary: Defaults to False: short-hand, pick the binary + collation type that matches the column's character set. Generates + BINARY in schema. This does not affect the type of data stored, + only the collation of character data. + + """ + super(MEDIUMTEXT, self).__init__(**kwargs) + + +class LONGTEXT(_StringType): + """MySQL LONGTEXT type, for text up to 2^32 characters.""" + + __visit_name__ = 'LONGTEXT' + + def __init__(self, **kwargs): + """Construct a LONGTEXT. + + :param charset: Optional, a column-level character set for this string + value. Takes precedence to 'ascii' or 'unicode' short-hand. + + :param collation: Optional, a column-level collation for this string + value. Takes precedence to 'binary' short-hand. + + :param ascii: Defaults to False: short-hand for the ``latin1`` + character set, generates ASCII in schema. + + :param unicode: Defaults to False: short-hand for the ``ucs2`` + character set, generates UNICODE in schema. + + :param national: Optional. If true, use the server's configured + national character set. + + :param binary: Defaults to False: short-hand, pick the binary + collation type that matches the column's character set. Generates + BINARY in schema. This does not affect the type of data stored, + only the collation of character data. + + """ + super(LONGTEXT, self).__init__(**kwargs) + + +class VARCHAR(_StringType, sqltypes.VARCHAR): + """MySQL VARCHAR type, for variable-length character data.""" + + __visit_name__ = 'VARCHAR' + + def __init__(self, length=None, **kwargs): + """Construct a VARCHAR. + + :param charset: Optional, a column-level character set for this string + value. Takes precedence to 'ascii' or 'unicode' short-hand. + + :param collation: Optional, a column-level collation for this string + value. Takes precedence to 'binary' short-hand. + + :param ascii: Defaults to False: short-hand for the ``latin1`` + character set, generates ASCII in schema. + + :param unicode: Defaults to False: short-hand for the ``ucs2`` + character set, generates UNICODE in schema. + + :param national: Optional. If true, use the server's configured + national character set. + + :param binary: Defaults to False: short-hand, pick the binary + collation type that matches the column's character set. Generates + BINARY in schema. This does not affect the type of data stored, + only the collation of character data. + + """ + super(VARCHAR, self).__init__(length=length, **kwargs) + + +class CHAR(_StringType, sqltypes.CHAR): + """MySQL CHAR type, for fixed-length character data.""" + + __visit_name__ = 'CHAR' + + def __init__(self, length=None, **kwargs): + """Construct a CHAR. + + :param length: Maximum data length, in characters. + + :param binary: Optional, use the default binary collation for the + national character set. This does not affect the type of data + stored, use a BINARY type for binary data. + + :param collation: Optional, request a particular collation. Must be + compatible with the national character set. + + """ + super(CHAR, self).__init__(length=length, **kwargs) + + @classmethod + def _adapt_string_for_cast(self, type_): + # copy the given string type into a CHAR + # for the purposes of rendering a CAST expression + type_ = sqltypes.to_instance(type_) + if isinstance(type_, sqltypes.CHAR): + return type_ + elif isinstance(type_, _StringType): + return CHAR( + length=type_.length, + charset=type_.charset, + collation=type_.collation, + ascii=type_.ascii, + binary=type_.binary, + unicode=type_.unicode, + national=False # not supported in CAST + ) + else: + return CHAR(length=type_.length) + + +class NVARCHAR(_StringType, sqltypes.NVARCHAR): + """MySQL NVARCHAR type. + + For variable-length character data in the server's configured national + character set. + """ + + __visit_name__ = 'NVARCHAR' + + def __init__(self, length=None, **kwargs): + """Construct an NVARCHAR. + + :param length: Maximum data length, in characters. + + :param binary: Optional, use the default binary collation for the + national character set. This does not affect the type of data + stored, use a BINARY type for binary data. + + :param collation: Optional, request a particular collation. Must be + compatible with the national character set. + + """ + kwargs['national'] = True + super(NVARCHAR, self).__init__(length=length, **kwargs) + + +class NCHAR(_StringType, sqltypes.NCHAR): + """MySQL NCHAR type. + + For fixed-length character data in the server's configured national + character set. + """ + + __visit_name__ = 'NCHAR' + + def __init__(self, length=None, **kwargs): + """Construct an NCHAR. + + :param length: Maximum data length, in characters. + + :param binary: Optional, use the default binary collation for the + national character set. This does not affect the type of data + stored, use a BINARY type for binary data. + + :param collation: Optional, request a particular collation. Must be + compatible with the national character set. + + """ + kwargs['national'] = True + super(NCHAR, self).__init__(length=length, **kwargs) + + +class TINYBLOB(sqltypes._Binary): + """MySQL TINYBLOB type, for binary data up to 2^8 bytes.""" + + __visit_name__ = 'TINYBLOB' + + +class MEDIUMBLOB(sqltypes._Binary): + """MySQL MEDIUMBLOB type, for binary data up to 2^24 bytes.""" + + __visit_name__ = 'MEDIUMBLOB' + + +class LONGBLOB(sqltypes._Binary): + """MySQL LONGBLOB type, for binary data up to 2^32 bytes.""" + + __visit_name__ = 'LONGBLOB' diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/mysql/zxjdbc.py b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/zxjdbc.py new file mode 100644 index 0000000..4aee2db --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/mysql/zxjdbc.py @@ -0,0 +1,117 @@ +# mysql/zxjdbc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" + +.. dialect:: mysql+zxjdbc + :name: zxjdbc for Jython + :dbapi: zxjdbc + :connectstring: mysql+zxjdbc://<user>:<password>@<hostname>[:<port>]/\ +<database> + :driverurl: http://dev.mysql.com/downloads/connector/j/ + + .. note:: Jython is not supported by current versions of SQLAlchemy. The + zxjdbc dialect should be considered as experimental. + +Character Sets +-------------- + +SQLAlchemy zxjdbc dialects pass unicode straight through to the +zxjdbc/JDBC layer. To allow multiple character sets to be sent from the +MySQL Connector/J JDBC driver, by default SQLAlchemy sets its +``characterEncoding`` connection property to ``UTF-8``. It may be +overridden via a ``create_engine`` URL parameter. + +""" +import re + +from ... import types as sqltypes, util +from ...connectors.zxJDBC import ZxJDBCConnector +from .base import BIT, MySQLDialect, MySQLExecutionContext + + +class _ZxJDBCBit(BIT): + def result_processor(self, dialect, coltype): + """Converts boolean or byte arrays from MySQL Connector/J to longs.""" + def process(value): + if value is None: + return value + if isinstance(value, bool): + return int(value) + v = 0 + for i in value: + v = v << 8 | (i & 0xff) + value = v + return value + return process + + +class MySQLExecutionContext_zxjdbc(MySQLExecutionContext): + def get_lastrowid(self): + cursor = self.create_cursor() + cursor.execute("SELECT LAST_INSERT_ID()") + lastrowid = cursor.fetchone()[0] + cursor.close() + return lastrowid + + +class MySQLDialect_zxjdbc(ZxJDBCConnector, MySQLDialect): + jdbc_db_name = 'mysql' + jdbc_driver_name = 'com.mysql.jdbc.Driver' + + execution_ctx_cls = MySQLExecutionContext_zxjdbc + + colspecs = util.update_copy( + MySQLDialect.colspecs, + { + sqltypes.Time: sqltypes.Time, + BIT: _ZxJDBCBit + } + ) + + def _detect_charset(self, connection): + """Sniff out the character set in use for connection results.""" + # Prefer 'character_set_results' for the current connection over the + # value in the driver. SET NAMES or individual variable SETs will + # change the charset without updating the driver's view of the world. + # + # If it's decided that issuing that sort of SQL leaves you SOL, then + # this can prefer the driver value. + rs = connection.execute("SHOW VARIABLES LIKE 'character_set%%'") + opts = {row[0]: row[1] for row in self._compat_fetchall(rs)} + for key in ('character_set_connection', 'character_set'): + if opts.get(key, None): + return opts[key] + + util.warn("Could not detect the connection character set. " + "Assuming latin1.") + return 'latin1' + + def _driver_kwargs(self): + """return kw arg dict to be sent to connect().""" + return dict(characterEncoding='UTF-8', yearIsDateType='false') + + def _extract_error_code(self, exception): + # e.g.: DBAPIError: (Error) Table 'test.u2' doesn't exist + # [SQLCode: 1146], [SQLState: 42S02] 'DESCRIBE `u2`' () + m = re.compile(r"\[SQLCode\: (\d+)\]").search(str(exception.args)) + c = m.group(1) + if c: + return int(c) + + def _get_server_version_info(self, connection): + dbapi_con = connection.connection + version = [] + r = re.compile(r'[.\-]') + for n in r.split(dbapi_con.dbversion): + try: + version.append(int(n)) + except ValueError: + version.append(n) + return tuple(version) + +dialect = MySQLDialect_zxjdbc diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/oracle/__init__.py b/venv/Lib/site-packages/sqlalchemy/dialects/oracle/__init__.py new file mode 100644 index 0000000..e3d9fed --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/oracle/__init__.py @@ -0,0 +1,24 @@ +# oracle/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from . import base, cx_oracle, zxjdbc # noqa + +from .base import \ + VARCHAR, NVARCHAR, CHAR, DATE, NUMBER,\ + BLOB, BFILE, BINARY_FLOAT, BINARY_DOUBLE, CLOB, NCLOB, TIMESTAMP, RAW,\ + FLOAT, DOUBLE_PRECISION, LONG, INTERVAL,\ + VARCHAR2, NVARCHAR2, ROWID + +base.dialect = dialect = cx_oracle.dialect + +__all__ = ( + 'VARCHAR', 'NVARCHAR', 'CHAR', 'DATE', 'NUMBER', + 'BLOB', 'BFILE', 'CLOB', 'NCLOB', 'TIMESTAMP', 'RAW', + 'FLOAT', 'DOUBLE_PRECISION', 'BINARY_DOUBLE', 'BINARY_FLOAT', + 'LONG', 'dialect', 'INTERVAL', + 'VARCHAR2', 'NVARCHAR2', 'ROWID' +) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/oracle/base.py b/venv/Lib/site-packages/sqlalchemy/dialects/oracle/base.py new file mode 100644 index 0000000..356c2a2 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/oracle/base.py @@ -0,0 +1,1779 @@ +# oracle/base.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: oracle + :name: Oracle + + Oracle version 8 through current (11g at the time of this writing) are + supported. + +Connect Arguments +----------------- + +The dialect supports several :func:`~sqlalchemy.create_engine()` arguments +which affect the behavior of the dialect regardless of driver in use. + +* ``use_ansi`` - Use ANSI JOIN constructs (see the section on Oracle 8). + Defaults to ``True``. If ``False``, Oracle-8 compatible constructs are used + for joins. + +* ``optimize_limits`` - defaults to ``False``. see the section on + LIMIT/OFFSET. + +* ``use_binds_for_limits`` - defaults to ``True``. see the section on + LIMIT/OFFSET. + +Auto Increment Behavior +----------------------- + +SQLAlchemy Table objects which include integer primary keys are usually +assumed to have "autoincrementing" behavior, meaning they can generate their +own primary key values upon INSERT. Since Oracle has no "autoincrement" +feature, SQLAlchemy relies upon sequences to produce these values. With the +Oracle dialect, *a sequence must always be explicitly specified to enable +autoincrement*. This is divergent with the majority of documentation +examples which assume the usage of an autoincrement-capable database. To +specify sequences, use the sqlalchemy.schema.Sequence object which is passed +to a Column construct:: + + t = Table('mytable', metadata, + Column('id', Integer, Sequence('id_seq'), primary_key=True), + Column(...), ... + ) + +This step is also required when using table reflection, i.e. autoload=True:: + + t = Table('mytable', metadata, + Column('id', Integer, Sequence('id_seq'), primary_key=True), + autoload=True + ) + +Identifier Casing +----------------- + +In Oracle, the data dictionary represents all case insensitive identifier +names using UPPERCASE text. SQLAlchemy on the other hand considers an +all-lower case identifier name to be case insensitive. The Oracle dialect +converts all case insensitive identifiers to and from those two formats during +schema level communication, such as reflection of tables and indexes. Using +an UPPERCASE name on the SQLAlchemy side indicates a case sensitive +identifier, and SQLAlchemy will quote the name - this will cause mismatches +against data dictionary data received from Oracle, so unless identifier names +have been truly created as case sensitive (i.e. using quoted names), all +lowercase names should be used on the SQLAlchemy side. + + +LIMIT/OFFSET Support +-------------------- + +Oracle has no support for the LIMIT or OFFSET keywords. SQLAlchemy uses +a wrapped subquery approach in conjunction with ROWNUM. The exact methodology +is taken from +http://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56asktom-086197.html . + +There are two options which affect its behavior: + +* the "FIRST ROWS()" optimization keyword is not used by default. To enable + the usage of this optimization directive, specify ``optimize_limits=True`` + to :func:`.create_engine`. +* the values passed for the limit/offset are sent as bound parameters. Some + users have observed that Oracle produces a poor query plan when the values + are sent as binds and not rendered literally. To render the limit/offset + values literally within the SQL statement, specify + ``use_binds_for_limits=False`` to :func:`.create_engine`. + +Some users have reported better performance when the entirely different +approach of a window query is used, i.e. ROW_NUMBER() OVER (ORDER BY), to +provide LIMIT/OFFSET (note that the majority of users don't observe this). +To suit this case the method used for LIMIT/OFFSET can be replaced entirely. +See the recipe at +http://www.sqlalchemy.org/trac/wiki/UsageRecipes/WindowFunctionsByDefault +which installs a select compiler that overrides the generation of limit/offset +with a window function. + +.. _oracle_returning: + +RETURNING Support +----------------- + +The Oracle database supports a limited form of RETURNING, in order to retrieve +result sets of matched rows from INSERT, UPDATE and DELETE statements. +Oracle's RETURNING..INTO syntax only supports one row being returned, as it +relies upon OUT parameters in order to function. In addition, supported +DBAPIs have further limitations (see :ref:`cx_oracle_returning`). + +SQLAlchemy's "implicit returning" feature, which employs RETURNING within an +INSERT and sometimes an UPDATE statement in order to fetch newly generated +primary key values and other SQL defaults and expressions, is normally enabled +on the Oracle backend. By default, "implicit returning" typically only +fetches the value of a single ``nextval(some_seq)`` expression embedded into +an INSERT in order to increment a sequence within an INSERT statement and get +the value back at the same time. To disable this feature across the board, +specify ``implicit_returning=False`` to :func:`.create_engine`:: + + engine = create_engine("oracle://scott:tiger@dsn", + implicit_returning=False) + +Implicit returning can also be disabled on a table-by-table basis as a table +option:: + + # Core Table + my_table = Table("my_table", metadata, ..., implicit_returning=False) + + + # declarative + class MyClass(Base): + __tablename__ = 'my_table' + __table_args__ = {"implicit_returning": False} + +.. seealso:: + + :ref:`cx_oracle_returning` - additional cx_oracle-specific restrictions on + implicit returning. + +ON UPDATE CASCADE +----------------- + +Oracle doesn't have native ON UPDATE CASCADE functionality. A trigger based +solution is available at +http://asktom.oracle.com/tkyte/update_cascade/index.html . + +When using the SQLAlchemy ORM, the ORM has limited ability to manually issue +cascading updates - specify ForeignKey objects using the +"deferrable=True, initially='deferred'" keyword arguments, +and specify "passive_updates=False" on each relationship(). + +Oracle 8 Compatibility +---------------------- + +When Oracle 8 is detected, the dialect internally configures itself to the +following behaviors: + +* the use_ansi flag is set to False. This has the effect of converting all + JOIN phrases into the WHERE clause, and in the case of LEFT OUTER JOIN + makes use of Oracle's (+) operator. + +* the NVARCHAR2 and NCLOB datatypes are no longer generated as DDL when + the :class:`~sqlalchemy.types.Unicode` is used - VARCHAR2 and CLOB are + issued instead. This because these types don't seem to work correctly on + Oracle 8 even though they are available. The + :class:`~sqlalchemy.types.NVARCHAR` and + :class:`~sqlalchemy.dialects.oracle.NCLOB` types will always generate + NVARCHAR2 and NCLOB. + +* the "native unicode" mode is disabled when using cx_oracle, i.e. SQLAlchemy + encodes all Python unicode objects to "string" before passing in as bind + parameters. + +Synonym/DBLINK Reflection +------------------------- + +When using reflection with Table objects, the dialect can optionally search +for tables indicated by synonyms, either in local or remote schemas or +accessed over DBLINK, by passing the flag ``oracle_resolve_synonyms=True`` as +a keyword argument to the :class:`.Table` construct:: + + some_table = Table('some_table', autoload=True, + autoload_with=some_engine, + oracle_resolve_synonyms=True) + +When this flag is set, the given name (such as ``some_table`` above) will +be searched not just in the ``ALL_TABLES`` view, but also within the +``ALL_SYNONYMS`` view to see if this name is actually a synonym to another +name. If the synonym is located and refers to a DBLINK, the oracle dialect +knows how to locate the table's information using DBLINK syntax(e.g. +``@dblink``). + +``oracle_resolve_synonyms`` is accepted wherever reflection arguments are +accepted, including methods such as :meth:`.MetaData.reflect` and +:meth:`.Inspector.get_columns`. + +If synonyms are not in use, this flag should be left disabled. + +.. _oracle_constraint_reflection: + +Constraint Reflection +--------------------- + +The Oracle dialect can return information about foreign key, unique, and +CHECK constraints, as well as indexes on tables. + +Raw information regarding these constraints can be acquired using +:meth:`.Inspector.get_foreign_keys`, :meth:`.Inspector.get_unique_constraints`, +:meth:`.Inspector.get_check_constraints`, and :meth:`.Inspector.get_indexes`. + +.. versionchanged:: 1.2 The Oracle dialect can now reflect UNIQUE and + CHECK constraints. + +When using reflection at the :class:`.Table` level, the :class:`.Table` +will also include these constraints. + +Note the following caveats: + +* When using the :meth:`.Inspector.get_check_constraints` method, Oracle + builds a special "IS NOT NULL" constraint for columns that specify + "NOT NULL". This constraint is **not** returned by default; to include + the "IS NOT NULL" constraints, pass the flag ``include_all=True``:: + + from sqlalchemy import create_engine, inspect + + engine = create_engine("oracle+cx_oracle://s:t@dsn") + inspector = inspect(engine) + all_check_constraints = inspector.get_check_constraints( + "some_table", include_all=True) + +* in most cases, when reflecting a :class:`.Table`, a UNIQUE constraint will + **not** be available as a :class:`.UniqueConstraint` object, as Oracle + mirrors unique constraints with a UNIQUE index in most cases (the exception + seems to be when two or more unique constraints represent the same columns); + the :class:`.Table` will instead represent these using :class:`.Index` + with the ``unique=True`` flag set. + +* Oracle creates an implicit index for the primary key of a table; this index + is **excluded** from all index results. + +* the list of columns reflected for an index will not include column names + that start with SYS_NC. + +Table names with SYSTEM/SYSAUX tablespaces +------------------------------------------- + +The :meth:`.Inspector.get_table_names` and +:meth:`.Inspector.get_temp_table_names` +methods each return a list of table names for the current engine. These methods +are also part of the reflection which occurs within an operation such as +:meth:`.MetaData.reflect`. By default, these operations exclude the ``SYSTEM`` +and ``SYSAUX`` tablespaces from the operation. In order to change this, the +default list of tablespaces excluded can be changed at the engine level using +the ``exclude_tablespaces`` parameter:: + + # exclude SYSAUX and SOME_TABLESPACE, but not SYSTEM + e = create_engine( + "oracle://scott:tiger@xe", + exclude_tablespaces=["SYSAUX", "SOME_TABLESPACE"]) + +.. versionadded:: 1.1 + +DateTime Compatibility +---------------------- + +Oracle has no datatype known as ``DATETIME``, it instead has only ``DATE``, +which can actually store a date and time value. For this reason, the Oracle +dialect provides a type :class:`.oracle.DATE` which is a subclass of +:class:`.DateTime`. This type has no special behavior, and is only +present as a "marker" for this type; additionally, when a database column +is reflected and the type is reported as ``DATE``, the time-supporting +:class:`.oracle.DATE` type is used. + +.. versionchanged:: 0.9.4 Added :class:`.oracle.DATE` to subclass + :class:`.DateTime`. This is a change as previous versions + would reflect a ``DATE`` column as :class:`.types.DATE`, which subclasses + :class:`.Date`. The only significance here is for schemes that are + examining the type of column for use in special Python translations or + for migrating schemas to other database backends. + +.. _oracle_table_options: + +Oracle Table Options +------------------------- + +The CREATE TABLE phrase supports the following options with Oracle +in conjunction with the :class:`.Table` construct: + + +* ``ON COMMIT``:: + + Table( + "some_table", metadata, ..., + prefixes=['GLOBAL TEMPORARY'], oracle_on_commit='PRESERVE ROWS') + +.. versionadded:: 1.0.0 + +* ``COMPRESS``:: + + Table('mytable', metadata, Column('data', String(32)), + oracle_compress=True) + + Table('mytable', metadata, Column('data', String(32)), + oracle_compress=6) + + The ``oracle_compress`` parameter accepts either an integer compression + level, or ``True`` to use the default compression level. + +.. versionadded:: 1.0.0 + +.. _oracle_index_options: + +Oracle Specific Index Options +----------------------------- + +Bitmap Indexes +~~~~~~~~~~~~~~ + +You can specify the ``oracle_bitmap`` parameter to create a bitmap index +instead of a B-tree index:: + + Index('my_index', my_table.c.data, oracle_bitmap=True) + +Bitmap indexes cannot be unique and cannot be compressed. SQLAlchemy will not +check for such limitations, only the database will. + +.. versionadded:: 1.0.0 + +Index compression +~~~~~~~~~~~~~~~~~ + +Oracle has a more efficient storage mode for indexes containing lots of +repeated values. Use the ``oracle_compress`` parameter to turn on key c +ompression:: + + Index('my_index', my_table.c.data, oracle_compress=True) + + Index('my_index', my_table.c.data1, my_table.c.data2, unique=True, + oracle_compress=1) + +The ``oracle_compress`` parameter accepts either an integer specifying the +number of prefix columns to compress, or ``True`` to use the default (all +columns for non-unique indexes, all but the last column for unique indexes). + +.. versionadded:: 1.0.0 + +""" + +import re + +from sqlalchemy import util, sql +from sqlalchemy.engine import default, reflection +from sqlalchemy.sql import compiler, visitors, expression, util as sql_util +from sqlalchemy.sql import operators as sql_operators +from sqlalchemy.sql.elements import quoted_name +from sqlalchemy import types as sqltypes, schema as sa_schema +from sqlalchemy.types import VARCHAR, NVARCHAR, CHAR, \ + BLOB, CLOB, TIMESTAMP, FLOAT, INTEGER +from itertools import groupby + +RESERVED_WORDS = \ + set('SHARE RAW DROP BETWEEN FROM DESC OPTION PRIOR LONG THEN ' + 'DEFAULT ALTER IS INTO MINUS INTEGER NUMBER GRANT IDENTIFIED ' + 'ALL TO ORDER ON FLOAT DATE HAVING CLUSTER NOWAIT RESOURCE ' + 'ANY TABLE INDEX FOR UPDATE WHERE CHECK SMALLINT WITH DELETE ' + 'BY ASC REVOKE LIKE SIZE RENAME NOCOMPRESS NULL GROUP VALUES ' + 'AS IN VIEW EXCLUSIVE COMPRESS SYNONYM SELECT INSERT EXISTS ' + 'NOT TRIGGER ELSE CREATE INTERSECT PCTFREE DISTINCT USER ' + 'CONNECT SET MODE OF UNIQUE VARCHAR2 VARCHAR LOCK OR CHAR ' + 'DECIMAL UNION PUBLIC AND START UID COMMENT CURRENT LEVEL'.split()) + +NO_ARG_FNS = set('UID CURRENT_DATE SYSDATE USER ' + 'CURRENT_TIME CURRENT_TIMESTAMP'.split()) + + +class RAW(sqltypes._Binary): + __visit_name__ = 'RAW' +OracleRaw = RAW + + +class NCLOB(sqltypes.Text): + __visit_name__ = 'NCLOB' + + +class VARCHAR2(VARCHAR): + __visit_name__ = 'VARCHAR2' + +NVARCHAR2 = NVARCHAR + + +class NUMBER(sqltypes.Numeric, sqltypes.Integer): + __visit_name__ = 'NUMBER' + + def __init__(self, precision=None, scale=None, asdecimal=None): + if asdecimal is None: + asdecimal = bool(scale and scale > 0) + + super(NUMBER, self).__init__( + precision=precision, scale=scale, asdecimal=asdecimal) + + def adapt(self, impltype): + ret = super(NUMBER, self).adapt(impltype) + # leave a hint for the DBAPI handler + ret._is_oracle_number = True + return ret + + @property + def _type_affinity(self): + if bool(self.scale and self.scale > 0): + return sqltypes.Numeric + else: + return sqltypes.Integer + + +class DOUBLE_PRECISION(sqltypes.Float): + __visit_name__ = 'DOUBLE_PRECISION' + + +class BINARY_DOUBLE(sqltypes.Float): + __visit_name__ = 'BINARY_DOUBLE' + + +class BINARY_FLOAT(sqltypes.Float): + __visit_name__ = 'BINARY_FLOAT' + + +class BFILE(sqltypes.LargeBinary): + __visit_name__ = 'BFILE' + + +class LONG(sqltypes.Text): + __visit_name__ = 'LONG' + + +class DATE(sqltypes.DateTime): + """Provide the oracle DATE type. + + This type has no special Python behavior, except that it subclasses + :class:`.types.DateTime`; this is to suit the fact that the Oracle + ``DATE`` type supports a time value. + + .. versionadded:: 0.9.4 + + """ + __visit_name__ = 'DATE' + + def _compare_type_affinity(self, other): + return other._type_affinity in (sqltypes.DateTime, sqltypes.Date) + + +class INTERVAL(sqltypes.TypeEngine): + __visit_name__ = 'INTERVAL' + + def __init__(self, + day_precision=None, + second_precision=None): + """Construct an INTERVAL. + + Note that only DAY TO SECOND intervals are currently supported. + This is due to a lack of support for YEAR TO MONTH intervals + within available DBAPIs (cx_oracle and zxjdbc). + + :param day_precision: the day precision value. this is the number of + digits to store for the day field. Defaults to "2" + :param second_precision: the second precision value. this is the + number of digits to store for the fractional seconds field. + Defaults to "6". + + """ + self.day_precision = day_precision + self.second_precision = second_precision + + @classmethod + def _adapt_from_generic_interval(cls, interval): + return INTERVAL(day_precision=interval.day_precision, + second_precision=interval.second_precision) + + @property + def _type_affinity(self): + return sqltypes.Interval + + +class ROWID(sqltypes.TypeEngine): + """Oracle ROWID type. + + When used in a cast() or similar, generates ROWID. + + """ + __visit_name__ = 'ROWID' + + +class _OracleBoolean(sqltypes.Boolean): + def get_dbapi_type(self, dbapi): + return dbapi.NUMBER + +colspecs = { + sqltypes.Boolean: _OracleBoolean, + sqltypes.Interval: INTERVAL, + sqltypes.DateTime: DATE +} + +ischema_names = { + 'VARCHAR2': VARCHAR, + 'NVARCHAR2': NVARCHAR, + 'CHAR': CHAR, + 'DATE': DATE, + 'NUMBER': NUMBER, + 'BLOB': BLOB, + 'BFILE': BFILE, + 'CLOB': CLOB, + 'NCLOB': NCLOB, + 'TIMESTAMP': TIMESTAMP, + 'TIMESTAMP WITH TIME ZONE': TIMESTAMP, + 'INTERVAL DAY TO SECOND': INTERVAL, + 'RAW': RAW, + 'FLOAT': FLOAT, + 'DOUBLE PRECISION': DOUBLE_PRECISION, + 'LONG': LONG, + 'BINARY_DOUBLE': BINARY_DOUBLE, + 'BINARY_FLOAT': BINARY_FLOAT +} + + +class OracleTypeCompiler(compiler.GenericTypeCompiler): + # Note: + # Oracle DATE == DATETIME + # Oracle does not allow milliseconds in DATE + # Oracle does not support TIME columns + + def visit_datetime(self, type_, **kw): + return self.visit_DATE(type_, **kw) + + def visit_float(self, type_, **kw): + return self.visit_FLOAT(type_, **kw) + + def visit_unicode(self, type_, **kw): + if self.dialect._supports_nchar: + return self.visit_NVARCHAR2(type_, **kw) + else: + return self.visit_VARCHAR2(type_, **kw) + + def visit_INTERVAL(self, type_, **kw): + return "INTERVAL DAY%s TO SECOND%s" % ( + type_.day_precision is not None and + "(%d)" % type_.day_precision or + "", + type_.second_precision is not None and + "(%d)" % type_.second_precision or + "", + ) + + def visit_LONG(self, type_, **kw): + return "LONG" + + def visit_TIMESTAMP(self, type_, **kw): + if type_.timezone: + return "TIMESTAMP WITH TIME ZONE" + else: + return "TIMESTAMP" + + def visit_DOUBLE_PRECISION(self, type_, **kw): + return self._generate_numeric(type_, "DOUBLE PRECISION", **kw) + + def visit_BINARY_DOUBLE(self, type_, **kw): + return self._generate_numeric(type_, "BINARY_DOUBLE", **kw) + + def visit_BINARY_FLOAT(self, type_, **kw): + return self._generate_numeric(type_, "BINARY_FLOAT", **kw) + + def visit_FLOAT(self, type_, **kw): + # don't support conversion between decimal/binary + # precision yet + kw['no_precision'] = True + return self._generate_numeric(type_, "FLOAT", **kw) + + def visit_NUMBER(self, type_, **kw): + return self._generate_numeric(type_, "NUMBER", **kw) + + def _generate_numeric( + self, type_, name, precision=None, + scale=None, no_precision=False, **kw): + if precision is None: + precision = type_.precision + + if scale is None: + scale = getattr(type_, 'scale', None) + + if no_precision or precision is None: + return name + elif scale is None: + n = "%(name)s(%(precision)s)" + return n % {'name': name, 'precision': precision} + else: + n = "%(name)s(%(precision)s, %(scale)s)" + return n % {'name': name, 'precision': precision, 'scale': scale} + + def visit_string(self, type_, **kw): + return self.visit_VARCHAR2(type_, **kw) + + def visit_VARCHAR2(self, type_, **kw): + return self._visit_varchar(type_, '', '2') + + def visit_NVARCHAR2(self, type_, **kw): + return self._visit_varchar(type_, 'N', '2') + visit_NVARCHAR = visit_NVARCHAR2 + + def visit_VARCHAR(self, type_, **kw): + return self._visit_varchar(type_, '', '') + + def _visit_varchar(self, type_, n, num): + if not type_.length: + return "%(n)sVARCHAR%(two)s" % {'two': num, 'n': n} + elif not n and self.dialect._supports_char_length: + varchar = "VARCHAR%(two)s(%(length)s CHAR)" + return varchar % {'length': type_.length, 'two': num} + else: + varchar = "%(n)sVARCHAR%(two)s(%(length)s)" + return varchar % {'length': type_.length, 'two': num, 'n': n} + + def visit_text(self, type_, **kw): + return self.visit_CLOB(type_, **kw) + + def visit_unicode_text(self, type_, **kw): + if self.dialect._supports_nchar: + return self.visit_NCLOB(type_, **kw) + else: + return self.visit_CLOB(type_, **kw) + + def visit_large_binary(self, type_, **kw): + return self.visit_BLOB(type_, **kw) + + def visit_big_integer(self, type_, **kw): + return self.visit_NUMBER(type_, precision=19, **kw) + + def visit_boolean(self, type_, **kw): + return self.visit_SMALLINT(type_, **kw) + + def visit_RAW(self, type_, **kw): + if type_.length: + return "RAW(%(length)s)" % {'length': type_.length} + else: + return "RAW" + + def visit_ROWID(self, type_, **kw): + return "ROWID" + + +class OracleCompiler(compiler.SQLCompiler): + """Oracle compiler modifies the lexical structure of Select + statements to work under non-ANSI configured Oracle databases, if + the use_ansi flag is False. + """ + + compound_keywords = util.update_copy( + compiler.SQLCompiler.compound_keywords, + { + expression.CompoundSelect.EXCEPT: 'MINUS' + } + ) + + def __init__(self, *args, **kwargs): + self.__wheres = {} + self._quoted_bind_names = {} + super(OracleCompiler, self).__init__(*args, **kwargs) + + def visit_mod_binary(self, binary, operator, **kw): + return "mod(%s, %s)" % (self.process(binary.left, **kw), + self.process(binary.right, **kw)) + + def visit_now_func(self, fn, **kw): + return "CURRENT_TIMESTAMP" + + def visit_char_length_func(self, fn, **kw): + return "LENGTH" + self.function_argspec(fn, **kw) + + def visit_match_op_binary(self, binary, operator, **kw): + return "CONTAINS (%s, %s)" % (self.process(binary.left), + self.process(binary.right)) + + def visit_true(self, expr, **kw): + return '1' + + def visit_false(self, expr, **kw): + return '0' + + def get_cte_preamble(self, recursive): + return "WITH" + + def get_select_hint_text(self, byfroms): + return " ".join( + "/*+ %s */" % text for table, text in byfroms.items() + ) + + def function_argspec(self, fn, **kw): + if len(fn.clauses) > 0 or fn.name.upper() not in NO_ARG_FNS: + return compiler.SQLCompiler.function_argspec(self, fn, **kw) + else: + return "" + + def default_from(self): + """Called when a ``SELECT`` statement has no froms, + and no ``FROM`` clause is to be appended. + + The Oracle compiler tacks a "FROM DUAL" to the statement. + """ + + return " FROM DUAL" + + def visit_join(self, join, **kwargs): + if self.dialect.use_ansi: + return compiler.SQLCompiler.visit_join(self, join, **kwargs) + else: + kwargs['asfrom'] = True + if isinstance(join.right, expression.FromGrouping): + right = join.right.element + else: + right = join.right + return self.process(join.left, **kwargs) + \ + ", " + self.process(right, **kwargs) + + def _get_nonansi_join_whereclause(self, froms): + clauses = [] + + def visit_join(join): + if join.isouter: + # https://docs.oracle.com/database/121/SQLRF/queries006.htm#SQLRF52354 + # "apply the outer join operator (+) to all columns of B in + # the join condition in the WHERE clause" - that is, + # unconditionally regardless of operator or the other side + def visit_binary(binary): + if isinstance(binary.left, expression.ColumnClause) \ + and join.right.is_derived_from(binary.left.table): + binary.left = _OuterJoinColumn(binary.left) + elif isinstance(binary.right, expression.ColumnClause) \ + and join.right.is_derived_from(binary.right.table): + binary.right = _OuterJoinColumn(binary.right) + clauses.append(visitors.cloned_traverse( + join.onclause, {}, {'binary': visit_binary})) + else: + clauses.append(join.onclause) + + for j in join.left, join.right: + if isinstance(j, expression.Join): + visit_join(j) + elif isinstance(j, expression.FromGrouping): + visit_join(j.element) + + for f in froms: + if isinstance(f, expression.Join): + visit_join(f) + + if not clauses: + return None + else: + return sql.and_(*clauses) + + def visit_outer_join_column(self, vc, **kw): + return self.process(vc.column, **kw) + "(+)" + + def visit_sequence(self, seq, **kw): + return (self.dialect.identifier_preparer.format_sequence(seq) + + ".nextval") + + def get_render_as_alias_suffix(self, alias_name_text): + """Oracle doesn't like ``FROM table AS alias``""" + + return " " + alias_name_text + + def returning_clause(self, stmt, returning_cols): + columns = [] + binds = [] + + for i, column in enumerate( + expression._select_iterables(returning_cols)): + if column.type._has_column_expression: + col_expr = column.type.column_expression(column) + else: + col_expr = column + + outparam = sql.outparam("ret_%d" % i, type_=column.type) + self.binds[outparam.key] = outparam + binds.append( + self.bindparam_string(self._truncate_bindparam(outparam))) + columns.append( + self.process(col_expr, within_columns_clause=False)) + + self._add_to_result_map( + getattr(col_expr, 'name', col_expr.anon_label), + getattr(col_expr, 'name', col_expr.anon_label), + (column, getattr(column, 'name', None), + getattr(column, 'key', None)), + column.type + ) + + return 'RETURNING ' + ', '.join(columns) + " INTO " + ", ".join(binds) + + def _TODO_visit_compound_select(self, select): + """Need to determine how to get ``LIMIT``/``OFFSET`` into a + ``UNION`` for Oracle. + """ + pass + + def visit_select(self, select, **kwargs): + """Look for ``LIMIT`` and OFFSET in a select statement, and if + so tries to wrap it in a subquery with ``rownum`` criterion. + """ + + if not getattr(select, '_oracle_visit', None): + if not self.dialect.use_ansi: + froms = self._display_froms_for_select( + select, kwargs.get('asfrom', False)) + whereclause = self._get_nonansi_join_whereclause(froms) + if whereclause is not None: + select = select.where(whereclause) + select._oracle_visit = True + + limit_clause = select._limit_clause + offset_clause = select._offset_clause + if limit_clause is not None or offset_clause is not None: + # See http://www.oracle.com/technology/oramag/oracle/06-sep/\ + # o56asktom.html + # + # Generalized form of an Oracle pagination query: + # select ... from ( + # select /*+ FIRST_ROWS(N) */ ...., rownum as ora_rn from + # ( select distinct ... where ... order by ... + # ) where ROWNUM <= :limit+:offset + # ) where ora_rn > :offset + # Outer select and "ROWNUM as ora_rn" can be dropped if + # limit=0 + + kwargs['select_wraps_for'] = select + select = select._generate() + select._oracle_visit = True + + # Wrap the middle select and add the hint + limitselect = sql.select([c for c in select.c]) + if limit_clause is not None and \ + self.dialect.optimize_limits and \ + select._simple_int_limit: + limitselect = limitselect.prefix_with( + "/*+ FIRST_ROWS(%d) */" % + select._limit) + + limitselect._oracle_visit = True + limitselect._is_wrapper = True + + # add expressions to accommodate FOR UPDATE OF + for_update = select._for_update_arg + if for_update is not None and for_update.of: + for_update = for_update._clone() + for_update._copy_internals() + + for elem in for_update.of: + select.append_column(elem) + + adapter = sql_util.ClauseAdapter(select) + for_update.of = [ + adapter.traverse(elem) + for elem in for_update.of] + + # If needed, add the limiting clause + if limit_clause is not None: + if not self.dialect.use_binds_for_limits: + # use simple int limits, will raise an exception + # if the limit isn't specified this way + max_row = select._limit + + if offset_clause is not None: + max_row += select._offset + max_row = sql.literal_column("%d" % max_row) + else: + max_row = limit_clause + if offset_clause is not None: + max_row = max_row + offset_clause + limitselect.append_whereclause( + sql.literal_column("ROWNUM") <= max_row) + + # If needed, add the ora_rn, and wrap again with offset. + if offset_clause is None: + limitselect._for_update_arg = for_update + select = limitselect + else: + limitselect = limitselect.column( + sql.literal_column("ROWNUM").label("ora_rn")) + limitselect._oracle_visit = True + limitselect._is_wrapper = True + + offsetselect = sql.select( + [c for c in limitselect.c if c.key != 'ora_rn']) + offsetselect._oracle_visit = True + offsetselect._is_wrapper = True + + if for_update is not None and for_update.of: + for elem in for_update.of: + if limitselect.corresponding_column(elem) is None: + limitselect.append_column(elem) + + if not self.dialect.use_binds_for_limits: + offset_clause = sql.literal_column( + "%d" % select._offset) + offsetselect.append_whereclause( + sql.literal_column("ora_rn") > offset_clause) + + offsetselect._for_update_arg = for_update + select = offsetselect + + return compiler.SQLCompiler.visit_select(self, select, **kwargs) + + def limit_clause(self, select, **kw): + return "" + + def for_update_clause(self, select, **kw): + if self.is_subquery(): + return "" + + tmp = ' FOR UPDATE' + + if select._for_update_arg.of: + tmp += ' OF ' + ', '.join( + self.process(elem, **kw) for elem in + select._for_update_arg.of + ) + + if select._for_update_arg.nowait: + tmp += " NOWAIT" + if select._for_update_arg.skip_locked: + tmp += " SKIP LOCKED" + + return tmp + + +class OracleDDLCompiler(compiler.DDLCompiler): + + def define_constraint_cascades(self, constraint): + text = "" + if constraint.ondelete is not None: + text += " ON DELETE %s" % constraint.ondelete + + # oracle has no ON UPDATE CASCADE - + # its only available via triggers + # http://asktom.oracle.com/tkyte/update_cascade/index.html + if constraint.onupdate is not None: + util.warn( + "Oracle does not contain native UPDATE CASCADE " + "functionality - onupdates will not be rendered for foreign " + "keys. Consider using deferrable=True, initially='deferred' " + "or triggers.") + + return text + + def visit_create_index(self, create): + index = create.element + self._verify_index_table(index) + preparer = self.preparer + text = "CREATE " + if index.unique: + text += "UNIQUE " + if index.dialect_options['oracle']['bitmap']: + text += "BITMAP " + text += "INDEX %s ON %s (%s)" % ( + self._prepared_index_name(index, include_schema=True), + preparer.format_table(index.table, use_schema=True), + ', '.join( + self.sql_compiler.process( + expr, + include_table=False, literal_binds=True) + for expr in index.expressions) + ) + if index.dialect_options['oracle']['compress'] is not False: + if index.dialect_options['oracle']['compress'] is True: + text += " COMPRESS" + else: + text += " COMPRESS %d" % ( + index.dialect_options['oracle']['compress'] + ) + return text + + def post_create_table(self, table): + table_opts = [] + opts = table.dialect_options['oracle'] + + if opts['on_commit']: + on_commit_options = opts['on_commit'].replace("_", " ").upper() + table_opts.append('\n ON COMMIT %s' % on_commit_options) + + if opts['compress']: + if opts['compress'] is True: + table_opts.append("\n COMPRESS") + else: + table_opts.append("\n COMPRESS FOR %s" % ( + opts['compress'] + )) + + return ''.join(table_opts) + + +class OracleIdentifierPreparer(compiler.IdentifierPreparer): + + reserved_words = {x.lower() for x in RESERVED_WORDS} + illegal_initial_characters = {str(dig) for dig in range(0, 10)} \ + .union(["_", "$"]) + + def _bindparam_requires_quotes(self, value): + """Return True if the given identifier requires quoting.""" + lc_value = value.lower() + return (lc_value in self.reserved_words + or value[0] in self.illegal_initial_characters + or not self.legal_characters.match(util.text_type(value)) + ) + + def format_savepoint(self, savepoint): + name = savepoint.ident.lstrip('_') + return super( + OracleIdentifierPreparer, self).format_savepoint(savepoint, name) + + +class OracleExecutionContext(default.DefaultExecutionContext): + def fire_sequence(self, seq, type_): + return self._execute_scalar( + "SELECT " + + self.dialect.identifier_preparer.format_sequence(seq) + + ".nextval FROM DUAL", type_) + + +class OracleDialect(default.DefaultDialect): + name = 'oracle' + supports_alter = True + supports_unicode_statements = False + supports_unicode_binds = False + max_identifier_length = 30 + + supports_simple_order_by_label = False + cte_follows_insert = True + + supports_sequences = True + sequences_optional = False + postfetch_lastrowid = False + + default_paramstyle = 'named' + colspecs = colspecs + ischema_names = ischema_names + requires_name_normalize = True + + supports_comments = True + supports_default_values = False + supports_empty_insert = False + + statement_compiler = OracleCompiler + ddl_compiler = OracleDDLCompiler + type_compiler = OracleTypeCompiler + preparer = OracleIdentifierPreparer + execution_ctx_cls = OracleExecutionContext + + reflection_options = ('oracle_resolve_synonyms', ) + + construct_arguments = [ + (sa_schema.Table, { + "resolve_synonyms": False, + "on_commit": None, + "compress": False + }), + (sa_schema.Index, { + "bitmap": False, + "compress": False + }) + ] + + def __init__(self, + use_ansi=True, + optimize_limits=False, + use_binds_for_limits=True, + exclude_tablespaces=('SYSTEM', 'SYSAUX', ), + **kwargs): + default.DefaultDialect.__init__(self, **kwargs) + self.use_ansi = use_ansi + self.optimize_limits = optimize_limits + self.use_binds_for_limits = use_binds_for_limits + self.exclude_tablespaces = exclude_tablespaces + + def initialize(self, connection): + super(OracleDialect, self).initialize(connection) + self.implicit_returning = self.__dict__.get( + 'implicit_returning', + self.server_version_info > (10, ) + ) + + if self._is_oracle_8: + self.colspecs = self.colspecs.copy() + self.colspecs.pop(sqltypes.Interval) + self.use_ansi = False + + @property + def _is_oracle_8(self): + return self.server_version_info and \ + self.server_version_info < (9, ) + + @property + def _supports_table_compression(self): + return self.server_version_info and \ + self.server_version_info >= (10, 1, ) + + @property + def _supports_table_compress_for(self): + return self.server_version_info and \ + self.server_version_info >= (11, ) + + @property + def _supports_char_length(self): + return not self._is_oracle_8 + + @property + def _supports_nchar(self): + return not self._is_oracle_8 + + def do_release_savepoint(self, connection, name): + # Oracle does not support RELEASE SAVEPOINT + pass + + def has_table(self, connection, table_name, schema=None): + if not schema: + schema = self.default_schema_name + cursor = connection.execute( + sql.text("SELECT table_name FROM all_tables " + "WHERE table_name = :name AND owner = :schema_name"), + name=self.denormalize_name(table_name), + schema_name=self.denormalize_name(schema)) + return cursor.first() is not None + + def has_sequence(self, connection, sequence_name, schema=None): + if not schema: + schema = self.default_schema_name + cursor = connection.execute( + sql.text("SELECT sequence_name FROM all_sequences " + "WHERE sequence_name = :name AND " + "sequence_owner = :schema_name"), + name=self.denormalize_name(sequence_name), + schema_name=self.denormalize_name(schema)) + return cursor.first() is not None + + def normalize_name(self, name): + if name is None: + return None + if util.py2k: + if isinstance(name, str): + name = name.decode(self.encoding) + if name.upper() == name and not \ + self.identifier_preparer._requires_quotes(name.lower()): + return name.lower() + elif name.lower() == name: + return quoted_name(name, quote=True) + else: + return name + + def denormalize_name(self, name): + if name is None: + return None + elif name.lower() == name and not \ + self.identifier_preparer._requires_quotes(name.lower()): + name = name.upper() + if util.py2k: + if not self.supports_unicode_binds: + name = name.encode(self.encoding) + else: + name = unicode(name) + return name + + def _get_default_schema_name(self, connection): + return self.normalize_name( + connection.execute('SELECT USER FROM DUAL').scalar()) + + def _resolve_synonym(self, connection, desired_owner=None, + desired_synonym=None, desired_table=None): + """search for a local synonym matching the given desired owner/name. + + if desired_owner is None, attempts to locate a distinct owner. + + returns the actual name, owner, dblink name, and synonym name if + found. + """ + + q = "SELECT owner, table_owner, table_name, db_link, "\ + "synonym_name FROM all_synonyms WHERE " + clauses = [] + params = {} + if desired_synonym: + clauses.append("synonym_name = :synonym_name") + params['synonym_name'] = desired_synonym + if desired_owner: + clauses.append("owner = :desired_owner") + params['desired_owner'] = desired_owner + if desired_table: + clauses.append("table_name = :tname") + params['tname'] = desired_table + + q += " AND ".join(clauses) + + result = connection.execute(sql.text(q), **params) + if desired_owner: + row = result.first() + if row: + return (row['table_name'], row['table_owner'], + row['db_link'], row['synonym_name']) + else: + return None, None, None, None + else: + rows = result.fetchall() + if len(rows) > 1: + raise AssertionError( + "There are multiple tables visible to the schema, you " + "must specify owner") + elif len(rows) == 1: + row = rows[0] + return (row['table_name'], row['table_owner'], + row['db_link'], row['synonym_name']) + else: + return None, None, None, None + + @reflection.cache + def _prepare_reflection_args(self, connection, table_name, schema=None, + resolve_synonyms=False, dblink='', **kw): + + if resolve_synonyms: + actual_name, owner, dblink, synonym = self._resolve_synonym( + connection, + desired_owner=self.denormalize_name(schema), + desired_synonym=self.denormalize_name(table_name) + ) + else: + actual_name, owner, dblink, synonym = None, None, None, None + if not actual_name: + actual_name = self.denormalize_name(table_name) + + if dblink: + # using user_db_links here since all_db_links appears + # to have more restricted permissions. + # http://docs.oracle.com/cd/B28359_01/server.111/b28310/ds_admin005.htm + # will need to hear from more users if we are doing + # the right thing here. See [ticket:2619] + owner = connection.scalar( + sql.text("SELECT username FROM user_db_links " + "WHERE db_link=:link"), link=dblink) + dblink = "@" + dblink + elif not owner: + owner = self.denormalize_name(schema or self.default_schema_name) + + return (actual_name, owner, dblink or '', synonym) + + @reflection.cache + def get_schema_names(self, connection, **kw): + s = "SELECT username FROM all_users ORDER BY username" + cursor = connection.execute(s,) + return [self.normalize_name(row[0]) for row in cursor] + + @reflection.cache + def get_table_names(self, connection, schema=None, **kw): + schema = self.denormalize_name(schema or self.default_schema_name) + + # note that table_names() isn't loading DBLINKed or synonym'ed tables + if schema is None: + schema = self.default_schema_name + + sql_str = "SELECT table_name FROM all_tables WHERE " + if self.exclude_tablespaces: + sql_str += ( + "nvl(tablespace_name, 'no tablespace') " + "NOT IN (%s) AND " % ( + ', '.join(["'%s'" % ts for ts in self.exclude_tablespaces]) + ) + ) + sql_str += ( + "OWNER = :owner " + "AND IOT_NAME IS NULL " + "AND DURATION IS NULL") + + cursor = connection.execute(sql.text(sql_str), owner=schema) + return [self.normalize_name(row[0]) for row in cursor] + + @reflection.cache + def get_temp_table_names(self, connection, **kw): + schema = self.denormalize_name(self.default_schema_name) + + sql_str = "SELECT table_name FROM all_tables WHERE " + if self.exclude_tablespaces: + sql_str += ( + "nvl(tablespace_name, 'no tablespace') " + "NOT IN (%s) AND " % ( + ', '.join(["'%s'" % ts for ts in self.exclude_tablespaces]) + ) + ) + sql_str += ( + "OWNER = :owner " + "AND IOT_NAME IS NULL " + "AND DURATION IS NOT NULL") + + cursor = connection.execute(sql.text(sql_str), owner=schema) + return [self.normalize_name(row[0]) for row in cursor] + + @reflection.cache + def get_view_names(self, connection, schema=None, **kw): + schema = self.denormalize_name(schema or self.default_schema_name) + s = sql.text("SELECT view_name FROM all_views WHERE owner = :owner") + cursor = connection.execute(s, owner=self.denormalize_name(schema)) + return [self.normalize_name(row[0]) for row in cursor] + + @reflection.cache + def get_table_options(self, connection, table_name, schema=None, **kw): + options = {} + + resolve_synonyms = kw.get('oracle_resolve_synonyms', False) + dblink = kw.get('dblink', '') + info_cache = kw.get('info_cache') + + (table_name, schema, dblink, synonym) = \ + self._prepare_reflection_args(connection, table_name, schema, + resolve_synonyms, dblink, + info_cache=info_cache) + + params = {"table_name": table_name} + + columns = ["table_name"] + if self._supports_table_compression: + columns.append("compression") + if self._supports_table_compress_for: + columns.append("compress_for") + + text = "SELECT %(columns)s "\ + "FROM ALL_TABLES%(dblink)s "\ + "WHERE table_name = :table_name" + + if schema is not None: + params['owner'] = schema + text += " AND owner = :owner " + text = text % {'dblink': dblink, 'columns': ", ".join(columns)} + + result = connection.execute(sql.text(text), **params) + + enabled = dict(DISABLED=False, ENABLED=True) + + row = result.first() + if row: + if "compression" in row and enabled.get(row.compression, False): + if "compress_for" in row: + options['oracle_compress'] = row.compress_for + else: + options['oracle_compress'] = True + + return options + + @reflection.cache + def get_columns(self, connection, table_name, schema=None, **kw): + """ + + kw arguments can be: + + oracle_resolve_synonyms + + dblink + + """ + + resolve_synonyms = kw.get('oracle_resolve_synonyms', False) + dblink = kw.get('dblink', '') + info_cache = kw.get('info_cache') + + (table_name, schema, dblink, synonym) = \ + self._prepare_reflection_args(connection, table_name, schema, + resolve_synonyms, dblink, + info_cache=info_cache) + columns = [] + if self._supports_char_length: + char_length_col = 'char_length' + else: + char_length_col = 'data_length' + + params = {"table_name": table_name} + text = """ + SELECT col.column_name, col.data_type, col.%(char_length_col)s, + col.data_precision, col.data_scale, col.nullable, + col.data_default, com.comments\ + FROM all_tab_columns%(dblink)s col + LEFT JOIN all_col_comments%(dblink)s com + ON col.table_name = com.table_name + AND col.column_name = com.column_name + AND col.owner = com.owner + WHERE col.table_name = :table_name + """ + if schema is not None: + params['owner'] = schema + text += " AND col.owner = :owner " + text += " ORDER BY col.column_id" + text = text % {'dblink': dblink, 'char_length_col': char_length_col} + + c = connection.execute(sql.text(text), **params) + + for row in c: + colname = self.normalize_name(row[0]) + orig_colname = row[0] + coltype = row[1] + length = row[2] + precision = row[3] + scale = row[4] + nullable = row[5] == 'Y' + default = row[6] + comment = row[7] + + if coltype == 'NUMBER': + if precision is None and scale == 0: + coltype = INTEGER() + else: + coltype = NUMBER(precision, scale) + elif coltype == 'FLOAT': + # TODO: support "precision" here as "binary_precision" + coltype = FLOAT() + elif coltype in ('VARCHAR2', 'NVARCHAR2', 'CHAR'): + coltype = self.ischema_names.get(coltype)(length) + elif 'WITH TIME ZONE' in coltype: + coltype = TIMESTAMP(timezone=True) + else: + coltype = re.sub(r'\(\d+\)', '', coltype) + try: + coltype = self.ischema_names[coltype] + except KeyError: + util.warn("Did not recognize type '%s' of column '%s'" % + (coltype, colname)) + coltype = sqltypes.NULLTYPE + + cdict = { + 'name': colname, + 'type': coltype, + 'nullable': nullable, + 'default': default, + 'autoincrement': 'auto', + 'comment': comment, + } + if orig_colname.lower() == orig_colname: + cdict['quote'] = True + + columns.append(cdict) + return columns + + @reflection.cache + def get_table_comment(self, connection, table_name, schema=None, + resolve_synonyms=False, dblink='', **kw): + + info_cache = kw.get('info_cache') + (table_name, schema, dblink, synonym) = \ + self._prepare_reflection_args(connection, table_name, schema, + resolve_synonyms, dblink, + info_cache=info_cache) + + COMMENT_SQL = """ + SELECT comments + FROM user_tab_comments + WHERE table_name = :table_name + """ + + c = connection.execute(sql.text(COMMENT_SQL), table_name=table_name) + return {"text": c.scalar()} + + @reflection.cache + def get_indexes(self, connection, table_name, schema=None, + resolve_synonyms=False, dblink='', **kw): + + info_cache = kw.get('info_cache') + (table_name, schema, dblink, synonym) = \ + self._prepare_reflection_args(connection, table_name, schema, + resolve_synonyms, dblink, + info_cache=info_cache) + indexes = [] + + params = {'table_name': table_name} + text = \ + "SELECT a.index_name, a.column_name, "\ + "\nb.index_type, b.uniqueness, b.compression, b.prefix_length "\ + "\nFROM ALL_IND_COLUMNS%(dblink)s a, "\ + "\nALL_INDEXES%(dblink)s b "\ + "\nWHERE "\ + "\na.index_name = b.index_name "\ + "\nAND a.table_owner = b.table_owner "\ + "\nAND a.table_name = b.table_name "\ + "\nAND a.table_name = :table_name " + + if schema is not None: + params['schema'] = schema + text += "AND a.table_owner = :schema " + + text += "ORDER BY a.index_name, a.column_position" + + text = text % {'dblink': dblink} + + q = sql.text(text) + rp = connection.execute(q, **params) + indexes = [] + last_index_name = None + pk_constraint = self.get_pk_constraint( + connection, table_name, schema, resolve_synonyms=resolve_synonyms, + dblink=dblink, info_cache=kw.get('info_cache')) + pkeys = pk_constraint['constrained_columns'] + uniqueness = dict(NONUNIQUE=False, UNIQUE=True) + enabled = dict(DISABLED=False, ENABLED=True) + + oracle_sys_col = re.compile(r'SYS_NC\d+\$', re.IGNORECASE) + + index = None + for rset in rp: + if rset.index_name != last_index_name: + index = dict(name=self.normalize_name(rset.index_name), + column_names=[], dialect_options={}) + indexes.append(index) + index['unique'] = uniqueness.get(rset.uniqueness, False) + + if rset.index_type in ('BITMAP', 'FUNCTION-BASED BITMAP'): + index['dialect_options']['oracle_bitmap'] = True + if enabled.get(rset.compression, False): + index['dialect_options']['oracle_compress'] = rset.prefix_length + + # filter out Oracle SYS_NC names. could also do an outer join + # to the all_tab_columns table and check for real col names there. + if not oracle_sys_col.match(rset.column_name): + index['column_names'].append( + self.normalize_name(rset.column_name)) + last_index_name = rset.index_name + + def upper_name_set(names): + return {i.upper() for i in names} + + pk_names = upper_name_set(pkeys) + if pk_names: + def is_pk_index(index): + # don't include the primary key index + return upper_name_set(index['column_names']) == pk_names + indexes = [idx for idx in indexes if not is_pk_index(idx)] + + return indexes + + @reflection.cache + def _get_constraint_data(self, connection, table_name, schema=None, + dblink='', **kw): + + params = {'table_name': table_name} + + text = ( + "SELECT" + "\nac.constraint_name," # 0 + "\nac.constraint_type," # 1 + "\nloc.column_name AS local_column," # 2 + "\nrem.table_name AS remote_table," # 3 + "\nrem.column_name AS remote_column," # 4 + "\nrem.owner AS remote_owner," # 5 + "\nloc.position as loc_pos," # 6 + "\nrem.position as rem_pos," # 7 + "\nac.search_condition," # 8 + "\nac.delete_rule" # 9 + "\nFROM all_constraints%(dblink)s ac," + "\nall_cons_columns%(dblink)s loc," + "\nall_cons_columns%(dblink)s rem" + "\nWHERE ac.table_name = :table_name" + "\nAND ac.constraint_type IN ('R','P', 'U', 'C')" + ) + + if schema is not None: + params['owner'] = schema + text += "\nAND ac.owner = :owner" + + text += ( + "\nAND ac.owner = loc.owner" + "\nAND ac.constraint_name = loc.constraint_name" + "\nAND ac.r_owner = rem.owner(+)" + "\nAND ac.r_constraint_name = rem.constraint_name(+)" + "\nAND (rem.position IS NULL or loc.position=rem.position)" + "\nORDER BY ac.constraint_name, loc.position" + ) + + text = text % {'dblink': dblink} + rp = connection.execute(sql.text(text), **params) + constraint_data = rp.fetchall() + return constraint_data + + @reflection.cache + def get_pk_constraint(self, connection, table_name, schema=None, **kw): + resolve_synonyms = kw.get('oracle_resolve_synonyms', False) + dblink = kw.get('dblink', '') + info_cache = kw.get('info_cache') + + (table_name, schema, dblink, synonym) = \ + self._prepare_reflection_args(connection, table_name, schema, + resolve_synonyms, dblink, + info_cache=info_cache) + pkeys = [] + constraint_name = None + constraint_data = self._get_constraint_data( + connection, table_name, schema, dblink, + info_cache=kw.get('info_cache')) + + for row in constraint_data: + (cons_name, cons_type, local_column, remote_table, remote_column, remote_owner) = \ + row[0:2] + tuple([self.normalize_name(x) for x in row[2:6]]) + if cons_type == 'P': + if constraint_name is None: + constraint_name = self.normalize_name(cons_name) + pkeys.append(local_column) + return {'constrained_columns': pkeys, 'name': constraint_name} + + @reflection.cache + def get_foreign_keys(self, connection, table_name, schema=None, **kw): + """ + + kw arguments can be: + + oracle_resolve_synonyms + + dblink + + """ + requested_schema = schema # to check later on + resolve_synonyms = kw.get('oracle_resolve_synonyms', False) + dblink = kw.get('dblink', '') + info_cache = kw.get('info_cache') + + (table_name, schema, dblink, synonym) = \ + self._prepare_reflection_args(connection, table_name, schema, + resolve_synonyms, dblink, + info_cache=info_cache) + + constraint_data = self._get_constraint_data( + connection, table_name, schema, dblink, + info_cache=kw.get('info_cache')) + + def fkey_rec(): + return { + 'name': None, + 'constrained_columns': [], + 'referred_schema': None, + 'referred_table': None, + 'referred_columns': [], + 'options': {}, + } + + fkeys = util.defaultdict(fkey_rec) + + for row in constraint_data: + (cons_name, cons_type, local_column, remote_table, remote_column, remote_owner) = \ + row[0:2] + tuple([self.normalize_name(x) for x in row[2:6]]) + + cons_name = self.normalize_name(cons_name) + + if cons_type == 'R': + if remote_table is None: + # ticket 363 + util.warn( + ("Got 'None' querying 'table_name' from " + "all_cons_columns%(dblink)s - does the user have " + "proper rights to the table?") % {'dblink': dblink}) + continue + + rec = fkeys[cons_name] + rec['name'] = cons_name + local_cols, remote_cols = rec[ + 'constrained_columns'], rec['referred_columns'] + + if not rec['referred_table']: + if resolve_synonyms: + ref_remote_name, ref_remote_owner, ref_dblink, ref_synonym = \ + self._resolve_synonym( + connection, + desired_owner=self.denormalize_name( + remote_owner), + desired_table=self.denormalize_name( + remote_table) + ) + if ref_synonym: + remote_table = self.normalize_name(ref_synonym) + remote_owner = self.normalize_name( + ref_remote_owner) + + rec['referred_table'] = remote_table + + if requested_schema is not None or \ + self.denormalize_name(remote_owner) != schema: + rec['referred_schema'] = remote_owner + + if row[9] != 'NO ACTION': + rec['options']['ondelete'] = row[9] + + local_cols.append(local_column) + remote_cols.append(remote_column) + + return list(fkeys.values()) + + @reflection.cache + def get_unique_constraints(self, connection, table_name, schema=None, **kw): + resolve_synonyms = kw.get('oracle_resolve_synonyms', False) + dblink = kw.get('dblink', '') + info_cache = kw.get('info_cache') + + (table_name, schema, dblink, synonym) = \ + self._prepare_reflection_args(connection, table_name, schema, + resolve_synonyms, dblink, + info_cache=info_cache) + + constraint_data = self._get_constraint_data( + connection, table_name, schema, dblink, + info_cache=kw.get('info_cache')) + + unique_keys = filter(lambda x: x[1] == 'U', constraint_data) + uniques_group = groupby(unique_keys, lambda x: x[0]) + + index_names = set([ix['name'] for ix in self.get_indexes(connection, table_name, schema=schema)]) + return [ + { + 'name': name, + 'column_names': cols, + 'duplicates_index': name if name in index_names else None + } + for name, cols in + [ + [ + self.normalize_name(i[0]), + [self.normalize_name(x[2]) for x in i[1]] + ] for i in uniques_group + ] + ] + + @reflection.cache + def get_view_definition(self, connection, view_name, schema=None, + resolve_synonyms=False, dblink='', **kw): + info_cache = kw.get('info_cache') + (view_name, schema, dblink, synonym) = \ + self._prepare_reflection_args(connection, view_name, schema, + resolve_synonyms, dblink, + info_cache=info_cache) + + params = {'view_name': view_name} + text = "SELECT text FROM all_views WHERE view_name=:view_name" + + if schema is not None: + text += " AND owner = :schema" + params['schema'] = schema + + rp = connection.execute(sql.text(text), **params).scalar() + if rp: + if util.py2k: + rp = rp.decode(self.encoding) + return rp + else: + return None + + @reflection.cache + def get_check_constraints(self, connection, table_name, schema=None, + include_all=False, **kw): + resolve_synonyms = kw.get('oracle_resolve_synonyms', False) + dblink = kw.get('dblink', '') + info_cache = kw.get('info_cache') + + (table_name, schema, dblink, synonym) = \ + self._prepare_reflection_args(connection, table_name, schema, + resolve_synonyms, dblink, + info_cache=info_cache) + + constraint_data = self._get_constraint_data( + connection, table_name, schema, dblink, + info_cache=kw.get('info_cache')) + + check_constraints = filter(lambda x: x[1] == 'C', constraint_data) + + return [ + { + 'name': self.normalize_name(cons[0]), + 'sqltext': cons[8], + } + for cons in check_constraints if include_all or + not re.match(r'..+?. IS NOT NULL$', cons[8])] + + +class _OuterJoinColumn(sql.ClauseElement): + __visit_name__ = 'outer_join_column' + + def __init__(self, column): + self.column = column diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py b/venv/Lib/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py new file mode 100644 index 0000000..d77f37f --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py @@ -0,0 +1,1040 @@ +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" + +.. dialect:: oracle+cx_oracle + :name: cx-Oracle + :dbapi: cx_oracle + :connectstring: oracle+cx_oracle://user:pass@host:port/dbname\ +[?key=value&key=value...] + :url: http://cx-oracle.sourceforge.net/ + +Additional Connect Arguments +---------------------------- + +When connecting with the ``dbname`` URL token present, the ``hostname``, +``port``, and ``dbname`` tokens are converted to a TNS name using the +``cx_Oracle.makedsn()`` function. The URL below:: + + e = create_engine("oracle+cx_oracle://user:pass@hostname/dbname") + +Will be used to create the DSN as follows:: + + >>> import cx_Oracle + >>> cx_Oracle.makedsn("hostname", 1521, sid="dbname") + '(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=hostname)(PORT=1521))(CONNECT_DATA=(SID=dbname)))' + +The ``service_name`` parameter, also consumed by ``cx_Oracle.makedsn()``, may +be specified in the URL query string, e.g. ``?service_name=my_service``. + +If ``dbname`` is not present, then the value of ``hostname`` in the +URL is used directly as the DSN passed to ``cx_Oracle.connect()``. + +Additional connection arguments may be sent to the ``cx_Oracle.connect()`` +function using the :paramref:`.create_engine.connect_args` dictionary. +Any cx_Oracle parameter value and/or constant may be passed, such as:: + + import cx_Oracle + e = create_engine( + "oracle+cx_oracle://user:pass@dsn", + connect_args={ + "mode": cx_Oracle.SYSDBA, + "events": True + } + ) + +There are also options that are consumed by the SQLAlchemy cx_oracle dialect +itself. These options are always passed directly to :func:`.create_engine`, +such as:: + + e = create_engine("oracle+cx_oracle://user:pass@dsn", coerce_to_unicode=False) + +The parameters accepted by the cx_oracle dialect are as follows: + +* ``arraysize`` - set the cx_oracle.arraysize value on cursors, defaulted + to 50. This setting is significant with cx_Oracle as the contents of LOB + objects are only readable within a "live" row (e.g. within a batch of + 50 rows). + +* ``auto_convert_lobs`` - defaults to True; See :ref:`cx_oracle_lob`. + +* ``coerce_to_unicode`` - see :ref:`cx_oracle_unicode` for detail. + +* ``coerce_to_decimal`` - see :ref:`cx_oracle_numeric` for detail. + +* ``threaded`` - this parameter is passed as the value of "threaded" to + ``cx_Oracle.connect()`` and defaults to True, which is the opposite of + cx_Oracle's default. This parameter is deprecated and will default to + ``False`` in version 1.3 of SQLAlchemy. + +.. _cx_oracle_unicode: + +Unicode +------- + +The cx_Oracle DBAPI as of version 5 fully supports unicode, and has the +ability to return string results as Python unicode objects natively. + +When used in Python 3, cx_Oracle returns all strings as Python unicode objects +(that is, plain ``str`` in Python 3). In Python 2, it will return as Python +unicode those column values that are of type ``NVARCHAR`` or ``NCLOB``. For +column values that are of type ``VARCHAR`` or other non-unicode string types, +it will return values as Python strings (e.g. bytestrings). + +The cx_Oracle SQLAlchemy dialect presents several different options for the use +case of receiving ``VARCHAR`` column values as Python unicode objects under +Python 2: + +* When using Core expression objects as well as the ORM, SQLAlchemy's + unicode-decoding services are available, which are established by + using either the :class:`.Unicode` datatype or by using the + :class:`.String` datatype with :paramref:`.String.convert_unicode` set + to True. + +* When using raw SQL strings, typing behavior can be added for unicode + conversion using the :func:`.text` construct:: + + from sqlalchemy import text, Unicode + result = conn.execute( + text("select username from user").columns(username=Unicode)) + +* Otherwise, when using raw SQL strings sent directly to an ``.execute()`` + method without any Core typing behavior added, the flag + ``coerce_to_unicode=True`` flag can be passed to :func:`.create_engine` + which will add an unconditional unicode processor to cx_Oracle for all + string values:: + + engine = create_engine("oracle+cx_oracle://dsn", coerce_to_unicode=True) + + The above approach will add significant latency to result-set fetches + of plain string values. + +Sending String Values as Unicode or Non-Unicode +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As of SQLAlchemy 1.2.2, the cx_Oracle dialect unconditionally calls +``setinputsizes()`` for bound values that are passed as Python unicode objects. +In Python 3, all string values are Unicode; for cx_Oracle, this corresponds to +``cx_Oracle.NCHAR`` being passed to ``setinputsizes()`` for that parameter. +In some edge cases, such as passing format specifiers for +the ``trunc()`` function, Oracle does not accept these as NCHAR:: + + from sqlalchemy import func + + conn.execute( + func.trunc(func.sysdate(), 'dd') + ) + +In these cases, an error as follows may be raised:: + + ORA-01899: bad precision specifier + +When this error is encountered, it may be necessary to pass the string value +with an explicit non-unicode type:: + + from sqlalchemy import func + from sqlalchemy import literal + from sqlalchemy import String + + conn.execute( + func.trunc(func.sysdate(), literal('dd', String)) + ) + +For full control over this ``setinputsizes()`` behavior, see the section +:ref:`cx_oracle_setinputsizes` + +.. _cx_oracle_setinputsizes: + +Fine grained control over cx_Oracle data binding and performance with setinputsizes +----------------------------------------------------------------------------------- + +The cx_Oracle DBAPI has a deep and fundamental reliance upon the usage of the +DBAPI ``setinputsizes()`` call. The purpose of this call is to establish the +datatypes that are bound to a SQL statement for Python values being passed as +parameters. While virtually no other DBAPI assigns any use to the +``setinputsizes()`` call, the cx_Oracle DBAPI relies upon it heavily in its +interactions with the Oracle client interface, and in some scenarios it is not +possible for SQLAlchemy to know exactly how data should be bound, as some +settings can cause profoundly different performance characteristics, while +altering the type coercion behavior at the same time. + +Users of the cx_Oracle dialect are **strongly encouraged** to read through +cx_Oracle's list of built-in datatype symbols at http://cx-oracle.readthedocs.io/en/latest/module.html#types. +Note that in some cases, signficant performance degradation can occur when using +these types vs. not, in particular when specifying ``cx_Oracle.CLOB``. + +On the SQLAlchemy side, the :meth:`.DialectEvents.do_setinputsizes` event +can be used both for runtime visibliity (e.g. logging) of the setinputsizes +step as well as to fully control how ``setinputsizes()`` is used on a per-statement +basis. + +.. versionadded:: 1.2.9 Added :meth:`.DialectEvents.setinputsizes` + + +Example 1 - logging all setinputsizes calls +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example illustrates how to log the intermediary values from +a SQLAlchemy perspective before they are converted to the raw ``setinputsizes()`` +parameter dictionary. The keys of the dictionary are :class:`.BindParameter` +objects which have a ``.key`` and a ``.type`` attribute:: + + from sqlalchemy import create_engine, event + + engine = create_engine("oracle+cx_oracle://scott:tiger@host/xe") + + @event.listens_for(engine, "do_setinputsizes") + def _log_setinputsizes(inputsizes, cursor, statement, parameters, context): + for bindparam, dbapitype in inputsizes.items(): + log.info( + "Bound parameter name: %s SQLAlchemy type: %r " + "DBAPI object: %s", + bindparam.key, bindparam.type, dbapitype) + +Example 2 - remove all bindings to CLOB +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``CLOB`` datatype in cx_Oracle incurs a significant performance overhead, +however is set by default for the ``Text`` type within the SQLAlchemy 1.2 +series. This setting can be modified as follows:: + + from sqlalchemy import create_engine, event + from cx_Oracle import CLOB + + engine = create_engine("oracle+cx_oracle://scott:tiger@host/xe") + + @event.listens_for(engine, "do_setinputsizes") + def _remove_clob(inputsizes, cursor, statement, parameters, context): + for bindparam, dbapitype in list(inputsizes.items()): + if dbapitype is CLOB: + del inputsizes[bindparam] + + +.. _cx_oracle_returning: + +RETURNING Support +----------------- + +The cx_Oracle dialect implements RETURNING using OUT parameters. +The dialect supports RETURNING fully, however cx_Oracle 6 is recommended +for complete support. + +.. _cx_oracle_lob: + +LOB Objects +----------- + +cx_oracle returns oracle LOBs using the cx_oracle.LOB object. SQLAlchemy +converts these to strings so that the interface of the Binary type is +consistent with that of other backends, which takes place within a cx_Oracle +outputtypehandler. + +cx_Oracle prior to version 6 would require that LOB objects be read before +a new batch of rows would be read, as determined by the ``cursor.arraysize``. +As of the 6 series, this limitation has been lifted. Nevertheless, because +SQLAlchemy pre-reads these LOBs up front, this issue is avoided in any case. + +To disable the auto "read()" feature of the dialect, the flag +``auto_convert_lobs=False`` may be passed to :func:`.create_engine`. Under +the cx_Oracle 5 series, having this flag turned off means there is the chance +of reading from a stale LOB object if not read as it is fetched. With +cx_Oracle 6, this issue is resolved. + +.. versionchanged:: 1.2 the LOB handling system has been greatly simplified + internally to make use of outputtypehandlers, and no longer makes use + of alternate "buffered" result set objects. + +Two Phase Transactions Not Supported +------------------------------------- + +Two phase transactions are **not supported** under cx_Oracle due to poor +driver support. As of cx_Oracle 6.0b1, the interface for +two phase transactions has been changed to be more of a direct pass-through +to the underlying OCI layer with less automation. The additional logic +to support this system is not implemented in SQLAlchemy. + +.. _cx_oracle_numeric: + +Precision Numerics +------------------ + +SQLAlchemy's numeric types can handle receiving and returning values as Python +``Decimal`` objects or float objects. When a :class:`.Numeric` object, or a +subclass such as :class:`.Float`, :class:`.oracle.DOUBLE_PRECISION` etc. is in +use, the :paramref:`.Numeric.asdecimal` flag determines if values should be +coerced to ``Decimal`` upon return, or returned as float objects. To make +matters more complicated under Oracle, Oracle's ``NUMBER`` type can also +represent integer values if the "scale" is zero, so the Oracle-specific +:class:`.oracle.NUMBER` type takes this into account as well. + +The cx_Oracle dialect makes extensive use of connection- and cursor-level +"outputtypehandler" callables in order to coerce numeric values as requested. +These callables are specific to the specific flavor of :class:`.Numeric` in +use, as well as if no SQLAlchemy typing objects are present. There are +observed scenarios where Oracle may sends incomplete or ambiguous information +about the numeric types being returned, such as a query where the numeric types +are buried under multiple levels of subquery. The type handlers do their best +to make the right decision in all cases, deferring to the underlying cx_Oracle +DBAPI for all those cases where the driver can make the best decision. + +When no typing objects are present, as when executing plain SQL strings, a +default "outputtypehandler" is present which will generally return numeric +values which specify precision and scale as Python ``Decimal`` objects. To +disable this coercion to decimal for performance reasons, pass the flag +``coerce_to_decimal=False`` to :func:`.create_engine`:: + + engine = create_engine("oracle+cx_oracle://dsn", coerce_to_decimal=False) + +The ``coerce_to_decimal`` flag only impacts the results of plain string +SQL staements that are not otherwise associated with a :class:`.Numeric` +SQLAlchemy type (or a subclass of such). + +.. versionchanged:: 1.2 The numeric handling system for cx_Oracle has been + reworked to take advantage of newer cx_Oracle features as well + as better integration of outputtypehandlers. + +""" + +from __future__ import absolute_import + +from .base import OracleCompiler, OracleDialect, OracleExecutionContext +from . import base as oracle +from ...engine import result as _result +from sqlalchemy import types as sqltypes, util, exc, processors +import random +import collections +import decimal +import re +import time + + +class _OracleInteger(sqltypes.Integer): + def get_dbapi_type(self, dbapi): + # see https://github.com/oracle/python-cx_Oracle/issues/208#issuecomment-409715955 + return int + + def _cx_oracle_var(self, dialect, cursor): + cx_Oracle = dialect.dbapi + return cursor.var( + cx_Oracle.STRING, + 255, + arraysize=cursor.arraysize, + outconverter=int + ) + + def _cx_oracle_outputtypehandler(self, dialect): + def handler(cursor, name, + default_type, size, precision, scale): + return self._cx_oracle_var(dialect, cursor) + return handler + + +class _OracleNumeric(sqltypes.Numeric): + is_number = False + + def bind_processor(self, dialect): + if self.scale == 0: + return None + elif self.asdecimal: + processor = processors.to_decimal_processor_factory( + decimal.Decimal, self._effective_decimal_return_scale) + + def process(value): + if isinstance(value, (int, float)): + return processor(value) + elif value is not None and value.is_infinite(): + return float(value) + else: + return value + return process + else: + return processors.to_float + + def result_processor(self, dialect, coltype): + return None + + def _cx_oracle_outputtypehandler(self, dialect): + cx_Oracle = dialect.dbapi + + is_cx_oracle_6 = dialect._is_cx_oracle_6 + has_native_int = dialect._has_native_int + + def handler(cursor, name, default_type, size, precision, scale): + outconverter = None + if precision: + if self.asdecimal: + if default_type == cx_Oracle.NATIVE_FLOAT: + # receiving float and doing Decimal after the fact + # allows for float("inf") to be handled + type_ = default_type + outconverter = decimal.Decimal + elif is_cx_oracle_6: + type_ = decimal.Decimal + else: + type_ = cx_Oracle.STRING + outconverter = dialect._to_decimal + else: + if self.is_number and scale == 0: + if has_native_int: + type_ = cx_Oracle.NATIVE_INT + else: + type_ = cx_Oracle.NUMBER + outconverter = int + else: + type_ = cx_Oracle.NATIVE_FLOAT + else: + if self.asdecimal: + if default_type == cx_Oracle.NATIVE_FLOAT: + type_ = default_type + outconverter = decimal.Decimal + elif is_cx_oracle_6: + type_ = decimal.Decimal + else: + type_ = cx_Oracle.STRING + outconverter = dialect._to_decimal + else: + if self.is_number and scale == 0: + if has_native_int: + type_ = cx_Oracle.NATIVE_INT + else: + type_ = cx_Oracle.NUMBER + outconverter = int + else: + type_ = cx_Oracle.NATIVE_FLOAT + + return cursor.var( + type_, 255, + arraysize=cursor.arraysize, + outconverter=outconverter + ) + + return handler + + +class _OracleBinaryFloat(_OracleNumeric): + def get_dbapi_type(self, dbapi): + return dbapi.NATIVE_FLOAT + + +class _OracleBINARY_FLOAT(_OracleBinaryFloat, oracle.BINARY_FLOAT): + pass + + +class _OracleBINARY_DOUBLE(_OracleBinaryFloat, oracle.BINARY_DOUBLE): + pass + + +class _OracleNUMBER(_OracleNumeric): + is_number = True + + +class _OracleDate(sqltypes.Date): + def bind_processor(self, dialect): + return None + + def result_processor(self, dialect, coltype): + def process(value): + if value is not None: + return value.date() + else: + return value + return process + + +class _OracleChar(sqltypes.CHAR): + def get_dbapi_type(self, dbapi): + return dbapi.FIXED_CHAR + + +class _OracleNVarChar(sqltypes.NVARCHAR): + def get_dbapi_type(self, dbapi): + return dbapi.NCHAR + + +class _OracleText(sqltypes.Text): + def get_dbapi_type(self, dbapi): + return dbapi.CLOB + + +class _OracleLong(oracle.LONG): + def get_dbapi_type(self, dbapi): + return dbapi.LONG_STRING + + +class _OracleString(sqltypes.String): + pass + + +class _OracleEnum(sqltypes.Enum): + def bind_processor(self, dialect): + enum_proc = sqltypes.Enum.bind_processor(self, dialect) + + def process(value): + raw_str = enum_proc(value) + return raw_str + return process + + +class _OracleUnicodeText(sqltypes.UnicodeText): + def get_dbapi_type(self, dbapi): + return dbapi.NCLOB + + +class _OracleBinary(sqltypes.LargeBinary): + def get_dbapi_type(self, dbapi): + return dbapi.BLOB + + def bind_processor(self, dialect): + return None + + def result_processor(self, dialect, coltype): + if not dialect.auto_convert_lobs: + return None + else: + return super(_OracleBinary, self).result_processor( + dialect, coltype) + + +class _OracleInterval(oracle.INTERVAL): + def get_dbapi_type(self, dbapi): + return dbapi.INTERVAL + + +class _OracleRaw(oracle.RAW): + pass + + +class _OracleRowid(oracle.ROWID): + def get_dbapi_type(self, dbapi): + return dbapi.ROWID + + +class OracleCompiler_cx_oracle(OracleCompiler): + _oracle_cx_sql_compiler = True + + def bindparam_string(self, name, **kw): + quote = getattr(name, 'quote', None) + if quote is True or quote is not False and \ + self.preparer._bindparam_requires_quotes(name): + if kw.get('expanding', False): + raise exc.CompileError( + "Can't use expanding feature with parameter name " + "%r on Oracle; it requires quoting which is not supported " + "in this context." % name) + quoted_name = '"%s"' % name + self._quoted_bind_names[name] = quoted_name + return OracleCompiler.bindparam_string(self, quoted_name, **kw) + else: + return OracleCompiler.bindparam_string(self, name, **kw) + + +class OracleExecutionContext_cx_oracle(OracleExecutionContext): + out_parameters = None + + def _setup_quoted_bind_names(self): + quoted_bind_names = self.compiled._quoted_bind_names + if quoted_bind_names: + for param in self.parameters: + for fromname, toname in quoted_bind_names.items(): + param[toname] = param[fromname] + del param[fromname] + + def _handle_out_parameters(self): + # if a single execute, check for outparams + if len(self.compiled_parameters) == 1: + quoted_bind_names = self.compiled._quoted_bind_names + for bindparam in self.compiled.binds.values(): + if bindparam.isoutparam: + name = self.compiled.bind_names[bindparam] + type_impl = bindparam.type.dialect_impl(self.dialect) + if hasattr(type_impl, '_cx_oracle_var'): + self.out_parameters[name] = type_impl._cx_oracle_var( + self.dialect, self.cursor) + else: + dbtype = type_impl.get_dbapi_type(self.dialect.dbapi) + if dbtype is None: + raise exc.InvalidRequestError( + "Cannot create out parameter for parameter " + "%r - its type %r is not supported by" + " cx_oracle" % + (bindparam.key, bindparam.type) + ) + self.out_parameters[name] = self.cursor.var(dbtype) + self.parameters[0][quoted_bind_names.get(name, name)] = \ + self.out_parameters[name] + + def _generate_cursor_outputtype_handler(self): + output_handlers = {} + + for (keyname, name, objects, type_) in self.compiled._result_columns: + handler = type_._cached_custom_processor( + self.dialect, + 'cx_oracle_outputtypehandler', + self._get_cx_oracle_type_handler) + + if handler: + denormalized_name = self.dialect.denormalize_name(keyname) + output_handlers[denormalized_name] = handler + + if output_handlers: + default_handler = self._dbapi_connection.outputtypehandler + + def output_type_handler(cursor, name, default_type, + size, precision, scale): + if name in output_handlers: + return output_handlers[name]( + cursor, name, + default_type, size, precision, scale) + else: + return default_handler( + cursor, name, default_type, size, precision, scale + ) + self.cursor.outputtypehandler = output_type_handler + + def _get_cx_oracle_type_handler(self, impl): + if hasattr(impl, "_cx_oracle_outputtypehandler"): + return impl._cx_oracle_outputtypehandler(self.dialect) + else: + return None + + def pre_exec(self): + if not getattr(self.compiled, "_oracle_cx_sql_compiler", False): + return + + self.out_parameters = {} + + if self.compiled._quoted_bind_names: + self._setup_quoted_bind_names() + + self.set_input_sizes( + self.compiled._quoted_bind_names, + include_types=self.dialect._include_setinputsizes + ) + + self._handle_out_parameters() + + self._generate_cursor_outputtype_handler() + + def create_cursor(self): + c = self._dbapi_connection.cursor() + if self.dialect.arraysize: + c.arraysize = self.dialect.arraysize + + return c + + def get_result_proxy(self): + if self.out_parameters and self.compiled.returning: + returning_params = [ + self.dialect._returningval( + self.out_parameters["ret_%d" % i] + ) + for i in range(len(self.out_parameters)) + ] + return ReturningResultProxy(self, returning_params) + + result = _result.ResultProxy(self) + + if self.out_parameters: + if self.compiled_parameters is not None and \ + len(self.compiled_parameters) == 1: + result.out_parameters = out_parameters = {} + + for bind, name in self.compiled.bind_names.items(): + if name in self.out_parameters: + type = bind.type + impl_type = type.dialect_impl(self.dialect) + dbapi_type = impl_type.get_dbapi_type( + self.dialect.dbapi) + result_processor = impl_type.\ + result_processor(self.dialect, + dbapi_type) + if result_processor is not None: + out_parameters[name] = \ + result_processor( + self.dialect._paramval( + self.out_parameters[name] + )) + else: + out_parameters[name] = self.dialect._paramval( + self.out_parameters[name]) + else: + result.out_parameters = dict( + (k, self._dialect._paramval(v)) + for k, v in self.out_parameters.items() + ) + + return result + + +class ReturningResultProxy(_result.FullyBufferedResultProxy): + """Result proxy which stuffs the _returning clause + outparams + into the fetch.""" + + def __init__(self, context, returning_params): + self._returning_params = returning_params + super(ReturningResultProxy, self).__init__(context) + + def _cursor_description(self): + returning = self.context.compiled.returning + return [ + (getattr(col, 'name', col.anon_label), None) + for col in returning + ] + + def _buffer_rows(self): + return collections.deque( + [tuple(self._returning_params)] + ) + + +class OracleDialect_cx_oracle(OracleDialect): + execution_ctx_cls = OracleExecutionContext_cx_oracle + statement_compiler = OracleCompiler_cx_oracle + + supports_sane_rowcount = True + supports_sane_multi_rowcount = True + + supports_unicode_statements = True + supports_unicode_binds = True + + driver = "cx_oracle" + + colspecs = { + sqltypes.Numeric: _OracleNumeric, + sqltypes.Float: _OracleNumeric, + oracle.BINARY_FLOAT: _OracleBINARY_FLOAT, + oracle.BINARY_DOUBLE: _OracleBINARY_DOUBLE, + sqltypes.Integer: _OracleInteger, + oracle.NUMBER: _OracleNUMBER, + + sqltypes.Date: _OracleDate, + sqltypes.LargeBinary: _OracleBinary, + sqltypes.Boolean: oracle._OracleBoolean, + sqltypes.Interval: _OracleInterval, + oracle.INTERVAL: _OracleInterval, + sqltypes.Text: _OracleText, + sqltypes.String: _OracleString, + sqltypes.UnicodeText: _OracleUnicodeText, + sqltypes.CHAR: _OracleChar, + sqltypes.Enum: _OracleEnum, + + oracle.LONG: _OracleLong, + oracle.RAW: _OracleRaw, + sqltypes.Unicode: _OracleNVarChar, + sqltypes.NVARCHAR: _OracleNVarChar, + oracle.ROWID: _OracleRowid, + } + + execute_sequence_format = list + + def __init__(self, + auto_convert_lobs=True, + threaded=True, + coerce_to_unicode=False, + coerce_to_decimal=True, + arraysize=50, + **kwargs): + + self._pop_deprecated_kwargs(kwargs) + + OracleDialect.__init__(self, **kwargs) + self.threaded = threaded + self.arraysize = arraysize + self.auto_convert_lobs = auto_convert_lobs + self.coerce_to_unicode = coerce_to_unicode + self.coerce_to_decimal = coerce_to_decimal + + cx_Oracle = self.dbapi + + if cx_Oracle is None: + self._include_setinputsizes = {} + self.cx_oracle_ver = (0, 0, 0) + else: + self.cx_oracle_ver = self._parse_cx_oracle_ver(cx_Oracle.version) + if self.cx_oracle_ver < (5, 2) and self.cx_oracle_ver > (0, 0, 0): + raise exc.InvalidRequestError( + "cx_Oracle version 5.2 and above are supported") + + self._has_native_int = hasattr(cx_Oracle, "NATIVE_INT") + + self._include_setinputsizes = { + cx_Oracle.NCLOB, cx_Oracle.CLOB, cx_Oracle.LOB, + cx_Oracle.NCHAR, cx_Oracle.FIXED_NCHAR, + cx_Oracle.BLOB, cx_Oracle.FIXED_CHAR, cx_Oracle.TIMESTAMP, + _OracleInteger, _OracleBINARY_FLOAT, _OracleBINARY_DOUBLE + } + + self._paramval = lambda value: value.getvalue() + + # https://github.com/oracle/python-cx_Oracle/issues/176#issuecomment-386821291 + # https://github.com/oracle/python-cx_Oracle/issues/224 + self._values_are_lists = self.cx_oracle_ver >= (6, 3) + if self._values_are_lists: + cx_Oracle.__future__.dml_ret_array_val = True + + def _returningval(value): + try: + return value.values[0][0] + except IndexError: + return None + + self._returningval = _returningval + else: + self._returningval = self._paramval + + self._is_cx_oracle_6 = self.cx_oracle_ver >= (6, ) + + def _pop_deprecated_kwargs(self, kwargs): + auto_setinputsizes = kwargs.pop('auto_setinputsizes', None) + exclude_setinputsizes = kwargs.pop('exclude_setinputsizes', None) + if auto_setinputsizes or exclude_setinputsizes: + util.warn_deprecated( + "auto_setinputsizes and exclude_setinputsizes are deprecated. " + "Modern cx_Oracle only requires that LOB types are part " + "of this behavior, and these parameters no longer have any " + "effect.") + allow_twophase = kwargs.pop('allow_twophase', None) + if allow_twophase is not None: + util.warn.deprecated( + "allow_twophase is deprecated. The cx_Oracle dialect no " + "longer supports two-phase transaction mode." + ) + + def _parse_cx_oracle_ver(self, version): + m = re.match(r'(\d+)\.(\d+)(?:\.(\d+))?', version) + if m: + return tuple( + int(x) + for x in m.group(1, 2, 3) + if x is not None) + else: + return (0, 0, 0) + + @classmethod + def dbapi(cls): + import cx_Oracle + return cx_Oracle + + def initialize(self, connection): + super(OracleDialect_cx_oracle, self).initialize(connection) + if self._is_oracle_8: + self.supports_unicode_binds = False + + self._detect_decimal_char(connection) + + def _detect_decimal_char(self, connection): + # we have the option to change this setting upon connect, + # or just look at what it is upon connect and convert. + # to minimize the chance of interference with changes to + # NLS_TERRITORY or formatting behavior of the DB, we opt + # to just look at it + + self._decimal_char = connection.scalar( + "select value from nls_session_parameters " + "where parameter = 'NLS_NUMERIC_CHARACTERS'")[0] + if self._decimal_char != '.': + _detect_decimal = self._detect_decimal + _to_decimal = self._to_decimal + + self._detect_decimal = lambda value: _detect_decimal( + value.replace(self._decimal_char, ".")) + self._to_decimal = lambda value: _to_decimal( + value.replace(self._decimal_char, ".")) + + def _detect_decimal(self, value): + if "." in value: + return self._to_decimal(value) + else: + return int(value) + + _to_decimal = decimal.Decimal + + def _generate_connection_outputtype_handler(self): + """establish the default outputtypehandler established at the + connection level. + + """ + + dialect = self + cx_Oracle = dialect.dbapi + + number_handler = _OracleNUMBER(asdecimal=True).\ + _cx_oracle_outputtypehandler(dialect) + float_handler = _OracleNUMBER(asdecimal=False).\ + _cx_oracle_outputtypehandler(dialect) + + def output_type_handler(cursor, name, default_type, + size, precision, scale): + if default_type == cx_Oracle.NUMBER: + if not dialect.coerce_to_decimal: + return None + elif precision == 0 and scale in (0, -127): + # ambiguous type, this occurs when selecting + # numbers from deep subqueries + return cursor.var( + cx_Oracle.STRING, + 255, + outconverter=dialect._detect_decimal, + arraysize=cursor.arraysize) + elif precision and scale > 0: + return number_handler( + cursor, name, default_type, size, precision, scale + ) + else: + return float_handler( + cursor, name, default_type, size, precision, scale + ) + + # allow all strings to come back natively as Unicode + elif dialect.coerce_to_unicode and \ + default_type in (cx_Oracle.STRING, cx_Oracle.FIXED_CHAR): + return cursor.var( + util.text_type, size, cursor.arraysize + ) + elif dialect.auto_convert_lobs and default_type in ( + cx_Oracle.CLOB, cx_Oracle.NCLOB, cx_Oracle.BLOB + ): + return cursor.var( + default_type, size, cursor.arraysize, + outconverter=lambda value: value.read() + ) + return output_type_handler + + def on_connect(self): + + output_type_handler = self._generate_connection_outputtype_handler() + + def on_connect(conn): + conn.outputtypehandler = output_type_handler + + return on_connect + + def create_connect_args(self, url): + dialect_opts = dict(url.query) + + for opt in ('use_ansi', 'auto_setinputsizes', 'auto_convert_lobs', + 'threaded', 'allow_twophase'): + if opt in dialect_opts: + util.coerce_kw_type(dialect_opts, opt, bool) + setattr(self, opt, dialect_opts[opt]) + + database = url.database + service_name = dialect_opts.get('service_name', None) + if database or service_name: + # if we have a database, then we have a remote host + port = url.port + if port: + port = int(port) + else: + port = 1521 + + if database and service_name: + raise exc.InvalidRequestError( + '"service_name" option shouldn\'t ' + 'be used with a "database" part of the url') + if database: + makedsn_kwargs = {'sid': database} + if service_name: + makedsn_kwargs = {'service_name': service_name} + + dsn = self.dbapi.makedsn(url.host, port, **makedsn_kwargs) + else: + # we have a local tnsname + dsn = url.host + + opts = dict( + threaded=self.threaded, + ) + + if dsn is not None: + opts['dsn'] = dsn + if url.password is not None: + opts['password'] = url.password + if url.username is not None: + opts['user'] = url.username + + if 'mode' in url.query: + opts['mode'] = url.query['mode'] + if isinstance(opts['mode'], util.string_types): + mode = opts['mode'].upper() + if mode == 'SYSDBA': + opts['mode'] = self.dbapi.SYSDBA + elif mode == 'SYSOPER': + opts['mode'] = self.dbapi.SYSOPER + else: + util.coerce_kw_type(opts, 'mode', int) + return ([], opts) + + def _get_server_version_info(self, connection): + return tuple( + int(x) + for x in connection.connection.version.split('.') + ) + + def is_disconnect(self, e, connection, cursor): + error, = e.args + if isinstance( + e, + (self.dbapi.InterfaceError, self.dbapi.DatabaseError) + ) and "not connected" in str(e): + return True + + if hasattr(error, 'code'): + # ORA-00028: your session has been killed + # ORA-03114: not connected to ORACLE + # ORA-03113: end-of-file on communication channel + # ORA-03135: connection lost contact + # ORA-01033: ORACLE initialization or shutdown in progress + # ORA-02396: exceeded maximum idle time, please connect again + # TODO: Others ? + return error.code in (28, 3114, 3113, 3135, 1033, 2396) + else: + return False + + def create_xid(self): + """create a two-phase transaction ID. + + this id will be passed to do_begin_twophase(), do_rollback_twophase(), + do_commit_twophase(). its format is unspecified. + + .. deprecated:: two-phase transaction support is no longer functional + in SQLAlchemy's cx_Oracle dialect as of cx_Oracle 6.0b1 + + """ + + id = random.randint(0, 2 ** 128) + return (0x1234, "%032x" % id, "%032x" % 9) + + def do_executemany(self, cursor, statement, parameters, context=None): + if isinstance(parameters, tuple): + parameters = list(parameters) + cursor.executemany(statement, parameters) + + def do_begin_twophase(self, connection, xid): + connection.connection.begin(*xid) + + def do_prepare_twophase(self, connection, xid): + result = connection.connection.prepare() + connection.info['cx_oracle_prepared'] = result + + def do_rollback_twophase(self, connection, xid, is_prepared=True, + recover=False): + self.do_rollback(connection.connection) + + def do_commit_twophase(self, connection, xid, is_prepared=True, + recover=False): + if not is_prepared: + self.do_commit(connection.connection) + else: + oci_prepared = connection.info['cx_oracle_prepared'] + if oci_prepared: + self.do_commit(connection.connection) + + def do_recover_twophase(self, connection): + connection.info.pop('cx_oracle_prepared', None) + +dialect = OracleDialect_cx_oracle diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/oracle/zxjdbc.py b/venv/Lib/site-packages/sqlalchemy/dialects/oracle/zxjdbc.py new file mode 100644 index 0000000..aa25625 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/oracle/zxjdbc.py @@ -0,0 +1,235 @@ +# oracle/zxjdbc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: oracle+zxjdbc + :name: zxJDBC for Jython + :dbapi: zxjdbc + :connectstring: oracle+zxjdbc://user:pass@host/dbname + :driverurl: http://www.oracle.com/technetwork/database/features/jdbc/index-091264.html + + .. note:: Jython is not supported by current versions of SQLAlchemy. The + zxjdbc dialect should be considered as experimental. + +""" +import decimal +import re + +from sqlalchemy import sql, types as sqltypes, util +from sqlalchemy.connectors.zxJDBC import ZxJDBCConnector +from sqlalchemy.dialects.oracle.base import (OracleCompiler, + OracleDialect, + OracleExecutionContext) +from sqlalchemy.engine import result as _result +from sqlalchemy.sql import expression +import collections + +SQLException = zxJDBC = None + + +class _ZxJDBCDate(sqltypes.Date): + + def result_processor(self, dialect, coltype): + def process(value): + if value is None: + return None + else: + return value.date() + return process + + +class _ZxJDBCNumeric(sqltypes.Numeric): + + def result_processor(self, dialect, coltype): + # XXX: does the dialect return Decimal or not??? + # if it does (in all cases), we could use a None processor as well as + # the to_float generic processor + if self.asdecimal: + def process(value): + if isinstance(value, decimal.Decimal): + return value + else: + return decimal.Decimal(str(value)) + else: + def process(value): + if isinstance(value, decimal.Decimal): + return float(value) + else: + return value + return process + + +class OracleCompiler_zxjdbc(OracleCompiler): + + def returning_clause(self, stmt, returning_cols): + self.returning_cols = list( + expression._select_iterables(returning_cols)) + + # within_columns_clause=False so that labels (foo AS bar) don't render + columns = [self.process(c, within_columns_clause=False) + for c in self.returning_cols] + + if not hasattr(self, 'returning_parameters'): + self.returning_parameters = [] + + binds = [] + for i, col in enumerate(self.returning_cols): + dbtype = col.type.dialect_impl( + self.dialect).get_dbapi_type(self.dialect.dbapi) + self.returning_parameters.append((i + 1, dbtype)) + + bindparam = sql.bindparam( + "ret_%d" % i, value=ReturningParam(dbtype)) + self.binds[bindparam.key] = bindparam + binds.append( + self.bindparam_string(self._truncate_bindparam(bindparam))) + + return 'RETURNING ' + ', '.join(columns) + " INTO " + ", ".join(binds) + + +class OracleExecutionContext_zxjdbc(OracleExecutionContext): + + def pre_exec(self): + if hasattr(self.compiled, 'returning_parameters'): + # prepare a zxJDBC statement so we can grab its underlying + # OraclePreparedStatement's getReturnResultSet later + self.statement = self.cursor.prepare(self.statement) + + def get_result_proxy(self): + if hasattr(self.compiled, 'returning_parameters'): + rrs = None + try: + try: + rrs = self.statement.__statement__.getReturnResultSet() + next(rrs) + except SQLException as sqle: + msg = '%s [SQLCode: %d]' % ( + sqle.getMessage(), sqle.getErrorCode()) + if sqle.getSQLState() is not None: + msg += ' [SQLState: %s]' % sqle.getSQLState() + raise zxJDBC.Error(msg) + else: + row = tuple( + self.cursor.datahandler.getPyObject( + rrs, index, dbtype) + for index, dbtype in + self.compiled.returning_parameters) + return ReturningResultProxy(self, row) + finally: + if rrs is not None: + try: + rrs.close() + except SQLException: + pass + self.statement.close() + + return _result.ResultProxy(self) + + def create_cursor(self): + cursor = self._dbapi_connection.cursor() + cursor.datahandler = self.dialect.DataHandler(cursor.datahandler) + return cursor + + +class ReturningResultProxy(_result.FullyBufferedResultProxy): + + """ResultProxy backed by the RETURNING ResultSet results.""" + + def __init__(self, context, returning_row): + self._returning_row = returning_row + super(ReturningResultProxy, self).__init__(context) + + def _cursor_description(self): + ret = [] + for c in self.context.compiled.returning_cols: + if hasattr(c, 'name'): + ret.append((c.name, c.type)) + else: + ret.append((c.anon_label, c.type)) + return ret + + def _buffer_rows(self): + return collections.deque([self._returning_row]) + + +class ReturningParam(object): + + """A bindparam value representing a RETURNING parameter. + + Specially handled by OracleReturningDataHandler. + """ + + def __init__(self, type): + self.type = type + + def __eq__(self, other): + if isinstance(other, ReturningParam): + return self.type == other.type + return NotImplemented + + def __ne__(self, other): + if isinstance(other, ReturningParam): + return self.type != other.type + return NotImplemented + + def __repr__(self): + kls = self.__class__ + return '<%s.%s object at 0x%x type=%s>' % ( + kls.__module__, kls.__name__, id(self), self.type) + + +class OracleDialect_zxjdbc(ZxJDBCConnector, OracleDialect): + jdbc_db_name = 'oracle' + jdbc_driver_name = 'oracle.jdbc.OracleDriver' + + statement_compiler = OracleCompiler_zxjdbc + execution_ctx_cls = OracleExecutionContext_zxjdbc + + colspecs = util.update_copy( + OracleDialect.colspecs, + { + sqltypes.Date: _ZxJDBCDate, + sqltypes.Numeric: _ZxJDBCNumeric + } + ) + + def __init__(self, *args, **kwargs): + super(OracleDialect_zxjdbc, self).__init__(*args, **kwargs) + global SQLException, zxJDBC + from java.sql import SQLException + from com.ziclix.python.sql import zxJDBC + from com.ziclix.python.sql.handler import OracleDataHandler + + class OracleReturningDataHandler(OracleDataHandler): + """zxJDBC DataHandler that specially handles ReturningParam.""" + + def setJDBCObject(self, statement, index, object, dbtype=None): + if type(object) is ReturningParam: + statement.registerReturnParameter(index, object.type) + elif dbtype is None: + OracleDataHandler.setJDBCObject( + self, statement, index, object) + else: + OracleDataHandler.setJDBCObject( + self, statement, index, object, dbtype) + self.DataHandler = OracleReturningDataHandler + + def initialize(self, connection): + super(OracleDialect_zxjdbc, self).initialize(connection) + self.implicit_returning = \ + connection.connection.driverversion >= '10.2' + + def _create_jdbc_url(self, url): + return 'jdbc:oracle:thin:@%s:%s:%s' % ( + url.host, url.port or 1521, url.database) + + def _get_server_version_info(self, connection): + version = re.search( + r'Release ([\d\.]+)', connection.connection.dbversion).group(1) + return tuple(int(x) for x in version.split('.')) + +dialect = OracleDialect_zxjdbc diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__init__.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__init__.py new file mode 100644 index 0000000..d2f8057 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__init__.py @@ -0,0 +1,37 @@ +# postgresql/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from . import base, psycopg2, pg8000, pypostgresql, pygresql, \ + zxjdbc, psycopg2cffi # noqa + +from .base import \ + INTEGER, BIGINT, SMALLINT, VARCHAR, CHAR, TEXT, NUMERIC, FLOAT, REAL, \ + INET, CIDR, UUID, BIT, MACADDR, MONEY, OID, REGCLASS, DOUBLE_PRECISION, \ + TIMESTAMP, TIME, DATE, BYTEA, BOOLEAN, INTERVAL, ENUM, TSVECTOR, \ + DropEnumType, CreateEnumType +from .hstore import HSTORE, hstore +from .json import JSON, JSONB, json +from .array import array, ARRAY, Any, All +from .ext import aggregate_order_by, ExcludeConstraint, array_agg +from .dml import insert, Insert + +from .ranges import INT4RANGE, INT8RANGE, NUMRANGE, DATERANGE, TSRANGE, \ + TSTZRANGE + +base.dialect = dialect = psycopg2.dialect + + +__all__ = ( + 'INTEGER', 'BIGINT', 'SMALLINT', 'VARCHAR', 'CHAR', 'TEXT', 'NUMERIC', + 'FLOAT', 'REAL', 'INET', 'CIDR', 'UUID', 'BIT', 'MACADDR', 'MONEY', 'OID', + 'REGCLASS', 'DOUBLE_PRECISION', 'TIMESTAMP', 'TIME', 'DATE', 'BYTEA', + 'BOOLEAN', 'INTERVAL', 'ARRAY', 'ENUM', 'dialect', 'array', 'HSTORE', + 'hstore', 'INT4RANGE', 'INT8RANGE', 'NUMRANGE', 'DATERANGE', + 'TSRANGE', 'TSTZRANGE', 'json', 'JSON', 'JSONB', 'Any', 'All', + 'DropEnumType', 'CreateEnumType', 'ExcludeConstraint', + 'aggregate_order_by', 'array_agg', 'insert', 'Insert' +) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/array.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/array.py new file mode 100644 index 0000000..b267404 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/array.py @@ -0,0 +1,303 @@ +# postgresql/array.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from .base import ischema_names, colspecs +from ...sql import expression, operators +from ...sql.base import SchemaEventTarget +from ... import types as sqltypes + +try: + from uuid import UUID as _python_UUID +except ImportError: + _python_UUID = None + + +def Any(other, arrexpr, operator=operators.eq): + """A synonym for the :meth:`.ARRAY.Comparator.any` method. + + This method is legacy and is here for backwards-compatibility. + + .. seealso:: + + :func:`.expression.any_` + + """ + + return arrexpr.any(other, operator) + + +def All(other, arrexpr, operator=operators.eq): + """A synonym for the :meth:`.ARRAY.Comparator.all` method. + + This method is legacy and is here for backwards-compatibility. + + .. seealso:: + + :func:`.expression.all_` + + """ + + return arrexpr.all(other, operator) + + +class array(expression.Tuple): + + """A PostgreSQL ARRAY literal. + + This is used to produce ARRAY literals in SQL expressions, e.g.:: + + from sqlalchemy.dialects.postgresql import array + from sqlalchemy.dialects import postgresql + from sqlalchemy import select, func + + stmt = select([ + array([1,2]) + array([3,4,5]) + ]) + + print stmt.compile(dialect=postgresql.dialect()) + + Produces the SQL:: + + SELECT ARRAY[%(param_1)s, %(param_2)s] || + ARRAY[%(param_3)s, %(param_4)s, %(param_5)s]) AS anon_1 + + An instance of :class:`.array` will always have the datatype + :class:`.ARRAY`. The "inner" type of the array is inferred from + the values present, unless the ``type_`` keyword argument is passed:: + + array(['foo', 'bar'], type_=CHAR) + + .. versionadded:: 0.8 Added the :class:`~.postgresql.array` literal type. + + See also: + + :class:`.postgresql.ARRAY` + + """ + __visit_name__ = 'array' + + def __init__(self, clauses, **kw): + super(array, self).__init__(*clauses, **kw) + self.type = ARRAY(self.type) + + def _bind_param(self, operator, obj, _assume_scalar=False, type_=None): + if _assume_scalar or operator is operators.getitem: + # if getitem->slice were called, Indexable produces + # a Slice object from that + assert isinstance(obj, int) + return expression.BindParameter( + None, obj, _compared_to_operator=operator, + type_=type_, + _compared_to_type=self.type, unique=True) + + else: + return array([ + self._bind_param(operator, o, _assume_scalar=True, type_=type_) + for o in obj]) + + def self_group(self, against=None): + if (against in ( + operators.any_op, operators.all_op, operators.getitem)): + return expression.Grouping(self) + else: + return self + + +CONTAINS = operators.custom_op("@>", precedence=5) + +CONTAINED_BY = operators.custom_op("<@", precedence=5) + +OVERLAP = operators.custom_op("&&", precedence=5) + + +class ARRAY(sqltypes.ARRAY): + + """PostgreSQL ARRAY type. + + .. versionchanged:: 1.1 The :class:`.postgresql.ARRAY` type is now + a subclass of the core :class:`.types.ARRAY` type. + + The :class:`.postgresql.ARRAY` type is constructed in the same way + as the core :class:`.types.ARRAY` type; a member type is required, and a + number of dimensions is recommended if the type is to be used for more + than one dimension:: + + from sqlalchemy.dialects import postgresql + + mytable = Table("mytable", metadata, + Column("data", postgresql.ARRAY(Integer, dimensions=2)) + ) + + The :class:`.postgresql.ARRAY` type provides all operations defined on the + core :class:`.types.ARRAY` type, including support for "dimensions", indexed + access, and simple matching such as :meth:`.types.ARRAY.Comparator.any` + and :meth:`.types.ARRAY.Comparator.all`. :class:`.postgresql.ARRAY` class also + provides PostgreSQL-specific methods for containment operations, including + :meth:`.postgresql.ARRAY.Comparator.contains` + :meth:`.postgresql.ARRAY.Comparator.contained_by`, + and :meth:`.postgresql.ARRAY.Comparator.overlap`, e.g.:: + + mytable.c.data.contains([1, 2]) + + The :class:`.postgresql.ARRAY` type may not be supported on all + PostgreSQL DBAPIs; it is currently known to work on psycopg2 only. + + Additionally, the :class:`.postgresql.ARRAY` type does not work directly in + conjunction with the :class:`.ENUM` type. For a workaround, see the + special type at :ref:`postgresql_array_of_enum`. + + .. seealso:: + + :class:`.types.ARRAY` - base array type + + :class:`.postgresql.array` - produces a literal array value. + + """ + + class Comparator(sqltypes.ARRAY.Comparator): + + """Define comparison operations for :class:`.ARRAY`. + + Note that these operations are in addition to those provided + by the base :class:`.types.ARRAY.Comparator` class, including + :meth:`.types.ARRAY.Comparator.any` and + :meth:`.types.ARRAY.Comparator.all`. + + """ + + def contains(self, other, **kwargs): + """Boolean expression. Test if elements are a superset of the + elements of the argument array expression. + """ + return self.operate(CONTAINS, other, result_type=sqltypes.Boolean) + + def contained_by(self, other): + """Boolean expression. Test if elements are a proper subset of the + elements of the argument array expression. + """ + return self.operate( + CONTAINED_BY, other, result_type=sqltypes.Boolean) + + def overlap(self, other): + """Boolean expression. Test if array has elements in common with + an argument array expression. + """ + return self.operate(OVERLAP, other, result_type=sqltypes.Boolean) + + comparator_factory = Comparator + + def __init__(self, item_type, as_tuple=False, dimensions=None, + zero_indexes=False): + """Construct an ARRAY. + + E.g.:: + + Column('myarray', ARRAY(Integer)) + + Arguments are: + + :param item_type: The data type of items of this array. Note that + dimensionality is irrelevant here, so multi-dimensional arrays like + ``INTEGER[][]``, are constructed as ``ARRAY(Integer)``, not as + ``ARRAY(ARRAY(Integer))`` or such. + + :param as_tuple=False: Specify whether return results + should be converted to tuples from lists. DBAPIs such + as psycopg2 return lists by default. When tuples are + returned, the results are hashable. + + :param dimensions: if non-None, the ARRAY will assume a fixed + number of dimensions. This will cause the DDL emitted for this + ARRAY to include the exact number of bracket clauses ``[]``, + and will also optimize the performance of the type overall. + Note that PG arrays are always implicitly "non-dimensioned", + meaning they can store any number of dimensions no matter how + they were declared. + + :param zero_indexes=False: when True, index values will be converted + between Python zero-based and PostgreSQL one-based indexes, e.g. + a value of one will be added to all index values before passing + to the database. + + .. versionadded:: 0.9.5 + + + """ + if isinstance(item_type, ARRAY): + raise ValueError("Do not nest ARRAY types; ARRAY(basetype) " + "handles multi-dimensional arrays of basetype") + if isinstance(item_type, type): + item_type = item_type() + self.item_type = item_type + self.as_tuple = as_tuple + self.dimensions = dimensions + self.zero_indexes = zero_indexes + + @property + def hashable(self): + return self.as_tuple + + @property + def python_type(self): + return list + + def compare_values(self, x, y): + return x == y + + def _proc_array(self, arr, itemproc, dim, collection): + if dim is None: + arr = list(arr) + if dim == 1 or dim is None and ( + # this has to be (list, tuple), or at least + # not hasattr('__iter__'), since Py3K strings + # etc. have __iter__ + not arr or not isinstance(arr[0], (list, tuple))): + if itemproc: + return collection(itemproc(x) for x in arr) + else: + return collection(arr) + else: + return collection( + self._proc_array( + x, itemproc, + dim - 1 if dim is not None else None, + collection) + for x in arr + ) + + def bind_processor(self, dialect): + item_proc = self.item_type.dialect_impl(dialect).\ + bind_processor(dialect) + + def process(value): + if value is None: + return value + else: + return self._proc_array( + value, + item_proc, + self.dimensions, + list) + return process + + def result_processor(self, dialect, coltype): + item_proc = self.item_type.dialect_impl(dialect).\ + result_processor(dialect, coltype) + + def process(value): + if value is None: + return value + else: + return self._proc_array( + value, + item_proc, + self.dimensions, + tuple if self.as_tuple else list) + return process + +colspecs[sqltypes.ARRAY] = ARRAY +ischema_names['_array'] = ARRAY diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/base.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/base.py new file mode 100644 index 0000000..01e0e33 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/base.py @@ -0,0 +1,3171 @@ +# postgresql/base.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +r""" +.. dialect:: postgresql + :name: PostgreSQL + +.. _postgresql_sequences: + +Sequences/SERIAL/IDENTITY +------------------------- + +PostgreSQL supports sequences, and SQLAlchemy uses these as the default means +of creating new primary key values for integer-based primary key columns. When +creating tables, SQLAlchemy will issue the ``SERIAL`` datatype for +integer-based primary key columns, which generates a sequence and server side +default corresponding to the column. + +To specify a specific named sequence to be used for primary key generation, +use the :func:`~sqlalchemy.schema.Sequence` construct:: + + Table('sometable', metadata, + Column('id', Integer, Sequence('some_id_seq'), primary_key=True) + ) + +When SQLAlchemy issues a single INSERT statement, to fulfill the contract of +having the "last insert identifier" available, a RETURNING clause is added to +the INSERT statement which specifies the primary key columns should be +returned after the statement completes. The RETURNING functionality only takes +place if PostgreSQL 8.2 or later is in use. As a fallback approach, the +sequence, whether specified explicitly or implicitly via ``SERIAL``, is +executed independently beforehand, the returned value to be used in the +subsequent insert. Note that when an +:func:`~sqlalchemy.sql.expression.insert()` construct is executed using +"executemany" semantics, the "last inserted identifier" functionality does not +apply; no RETURNING clause is emitted nor is the sequence pre-executed in this +case. + +To force the usage of RETURNING by default off, specify the flag +``implicit_returning=False`` to :func:`.create_engine`. + +Postgresql 10 IDENTITY columns +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Postgresql 10 has a new IDENTITY feature that supersedes the use of SERIAL. +Built-in support for rendering of IDENTITY is not available yet, however the +following compilation hook may be used to replace occurrences of SERIAL with +IDENTITY:: + + from sqlalchemy.schema import CreateColumn + from sqlalchemy.ext.compiler import compiles + + + @compiles(CreateColumn, 'postgresql') + def use_identity(element, compiler, **kw): + text = compiler.visit_create_column(element, **kw) + text = text.replace("SERIAL", "INT GENERATED BY DEFAULT AS IDENTITY") + return text + +Using the above, a table such as:: + + t = Table( + 't', m, + Column('id', Integer, primary_key=True), + Column('data', String) + ) + +Will generate on the backing database as:: + + CREATE TABLE t ( + id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, + data VARCHAR, + PRIMARY KEY (id) + ) + +.. _postgresql_isolation_level: + +Transaction Isolation Level +--------------------------- + +All PostgreSQL dialects support setting of transaction isolation level +both via a dialect-specific parameter +:paramref:`.create_engine.isolation_level` accepted by :func:`.create_engine`, +as well as the :paramref:`.Connection.execution_options.isolation_level` +argument as passed to :meth:`.Connection.execution_options`. +When using a non-psycopg2 dialect, this feature works by issuing the command +``SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL <level>`` for +each new connection. For the special AUTOCOMMIT isolation level, +DBAPI-specific techniques are used. + +To set isolation level using :func:`.create_engine`:: + + engine = create_engine( + "postgresql+pg8000://scott:tiger@localhost/test", + isolation_level="READ UNCOMMITTED" + ) + +To set using per-connection execution options:: + + connection = engine.connect() + connection = connection.execution_options( + isolation_level="READ COMMITTED" + ) + +Valid values for ``isolation_level`` include: + +* ``READ COMMITTED`` +* ``READ UNCOMMITTED`` +* ``REPEATABLE READ`` +* ``SERIALIZABLE`` +* ``AUTOCOMMIT`` - on psycopg2 / pg8000 only + +.. seealso:: + + :ref:`psycopg2_isolation_level` + + :ref:`pg8000_isolation_level` + +.. _postgresql_schema_reflection: + +Remote-Schema Table Introspection and PostgreSQL search_path +------------------------------------------------------------ + +**TL;DR;**: keep the ``search_path`` variable set to its default of ``public``, +name schemas **other** than ``public`` explicitly within ``Table`` defintitions. + +The PostgreSQL dialect can reflect tables from any schema. The +:paramref:`.Table.schema` argument, or alternatively the +:paramref:`.MetaData.reflect.schema` argument determines which schema will +be searched for the table or tables. The reflected :class:`.Table` objects +will in all cases retain this ``.schema`` attribute as was specified. +However, with regards to tables which these :class:`.Table` objects refer to +via foreign key constraint, a decision must be made as to how the ``.schema`` +is represented in those remote tables, in the case where that remote +schema name is also a member of the current +`PostgreSQL search path +<http://www.postgresql.org/docs/current/static/ddl-schemas.html#DDL-SCHEMAS-PATH>`_. + +By default, the PostgreSQL dialect mimics the behavior encouraged by +PostgreSQL's own ``pg_get_constraintdef()`` builtin procedure. This function +returns a sample definition for a particular foreign key constraint, +omitting the referenced schema name from that definition when the name is +also in the PostgreSQL schema search path. The interaction below +illustrates this behavior:: + + test=> CREATE TABLE test_schema.referred(id INTEGER PRIMARY KEY); + CREATE TABLE + test=> CREATE TABLE referring( + test(> id INTEGER PRIMARY KEY, + test(> referred_id INTEGER REFERENCES test_schema.referred(id)); + CREATE TABLE + test=> SET search_path TO public, test_schema; + test=> SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM + test-> pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n + test-> ON n.oid = c.relnamespace + test-> JOIN pg_catalog.pg_constraint r ON c.oid = r.conrelid + test-> WHERE c.relname='referring' AND r.contype = 'f' + test-> ; + pg_get_constraintdef + --------------------------------------------------- + FOREIGN KEY (referred_id) REFERENCES referred(id) + (1 row) + +Above, we created a table ``referred`` as a member of the remote schema +``test_schema``, however when we added ``test_schema`` to the +PG ``search_path`` and then asked ``pg_get_constraintdef()`` for the +``FOREIGN KEY`` syntax, ``test_schema`` was not included in the output of +the function. + +On the other hand, if we set the search path back to the typical default +of ``public``:: + + test=> SET search_path TO public; + SET + +The same query against ``pg_get_constraintdef()`` now returns the fully +schema-qualified name for us:: + + test=> SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM + test-> pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n + test-> ON n.oid = c.relnamespace + test-> JOIN pg_catalog.pg_constraint r ON c.oid = r.conrelid + test-> WHERE c.relname='referring' AND r.contype = 'f'; + pg_get_constraintdef + --------------------------------------------------------------- + FOREIGN KEY (referred_id) REFERENCES test_schema.referred(id) + (1 row) + +SQLAlchemy will by default use the return value of ``pg_get_constraintdef()`` +in order to determine the remote schema name. That is, if our ``search_path`` +were set to include ``test_schema``, and we invoked a table +reflection process as follows:: + + >>> from sqlalchemy import Table, MetaData, create_engine + >>> engine = create_engine("postgresql://scott:tiger@localhost/test") + >>> with engine.connect() as conn: + ... conn.execute("SET search_path TO test_schema, public") + ... meta = MetaData() + ... referring = Table('referring', meta, + ... autoload=True, autoload_with=conn) + ... + <sqlalchemy.engine.result.ResultProxy object at 0x101612ed0> + +The above process would deliver to the :attr:`.MetaData.tables` collection +``referred`` table named **without** the schema:: + + >>> meta.tables['referred'].schema is None + True + +To alter the behavior of reflection such that the referred schema is +maintained regardless of the ``search_path`` setting, use the +``postgresql_ignore_search_path`` option, which can be specified as a +dialect-specific argument to both :class:`.Table` as well as +:meth:`.MetaData.reflect`:: + + >>> with engine.connect() as conn: + ... conn.execute("SET search_path TO test_schema, public") + ... meta = MetaData() + ... referring = Table('referring', meta, autoload=True, + ... autoload_with=conn, + ... postgresql_ignore_search_path=True) + ... + <sqlalchemy.engine.result.ResultProxy object at 0x1016126d0> + +We will now have ``test_schema.referred`` stored as schema-qualified:: + + >>> meta.tables['test_schema.referred'].schema + 'test_schema' + +.. sidebar:: Best Practices for PostgreSQL Schema reflection + + The description of PostgreSQL schema reflection behavior is complex, and + is the product of many years of dealing with widely varied use cases and + user preferences. But in fact, there's no need to understand any of it if + you just stick to the simplest use pattern: leave the ``search_path`` set + to its default of ``public`` only, never refer to the name ``public`` as + an explicit schema name otherwise, and refer to all other schema names + explicitly when building up a :class:`.Table` object. The options + described here are only for those users who can't, or prefer not to, stay + within these guidelines. + +Note that **in all cases**, the "default" schema is always reflected as +``None``. The "default" schema on PostgreSQL is that which is returned by the +PostgreSQL ``current_schema()`` function. On a typical PostgreSQL +installation, this is the name ``public``. So a table that refers to another +which is in the ``public`` (i.e. default) schema will always have the +``.schema`` attribute set to ``None``. + +.. versionadded:: 0.9.2 Added the ``postgresql_ignore_search_path`` + dialect-level option accepted by :class:`.Table` and + :meth:`.MetaData.reflect`. + + +.. seealso:: + + `The Schema Search Path + <http://www.postgresql.org/docs/9.0/static/ddl-schemas.html#DDL-SCHEMAS-PATH>`_ + - on the PostgreSQL website. + +INSERT/UPDATE...RETURNING +------------------------- + +The dialect supports PG 8.2's ``INSERT..RETURNING``, ``UPDATE..RETURNING`` and +``DELETE..RETURNING`` syntaxes. ``INSERT..RETURNING`` is used by default +for single-row INSERT statements in order to fetch newly generated +primary key identifiers. To specify an explicit ``RETURNING`` clause, +use the :meth:`._UpdateBase.returning` method on a per-statement basis:: + + # INSERT..RETURNING + result = table.insert().returning(table.c.col1, table.c.col2).\ + values(name='foo') + print result.fetchall() + + # UPDATE..RETURNING + result = table.update().returning(table.c.col1, table.c.col2).\ + where(table.c.name=='foo').values(name='bar') + print result.fetchall() + + # DELETE..RETURNING + result = table.delete().returning(table.c.col1, table.c.col2).\ + where(table.c.name=='foo') + print result.fetchall() + +.. _postgresql_insert_on_conflict: + +INSERT...ON CONFLICT (Upsert) +------------------------------ + +Starting with version 9.5, PostgreSQL allows "upserts" (update or insert) +of rows into a table via the ``ON CONFLICT`` clause of the ``INSERT`` statement. +A candidate row will only be inserted if that row does not violate +any unique constraints. In the case of a unique constraint violation, +a secondary action can occur which can be either "DO UPDATE", indicating +that the data in the target row should be updated, or "DO NOTHING", +which indicates to silently skip this row. + +Conflicts are determined using existing unique constraints and indexes. These +constraints may be identified either using their name as stated in DDL, +or they may be *inferred* by stating the columns and conditions that comprise +the indexes. + +SQLAlchemy provides ``ON CONFLICT`` support via the PostgreSQL-specific +:func:`.postgresql.dml.insert()` function, which provides +the generative methods :meth:`~.postgresql.dml.Insert.on_conflict_do_update` +and :meth:`~.postgresql.dml.Insert.on_conflict_do_nothing`:: + + from sqlalchemy.dialects.postgresql import insert + + insert_stmt = insert(my_table).values( + id='some_existing_id', + data='inserted value') + + do_nothing_stmt = insert_stmt.on_conflict_do_nothing( + index_elements=['id'] + ) + + conn.execute(do_nothing_stmt) + + do_update_stmt = insert_stmt.on_conflict_do_update( + constraint='pk_my_table', + set_=dict(data='updated value') + ) + + conn.execute(do_update_stmt) + +Both methods supply the "target" of the conflict using either the +named constraint or by column inference: + +* The :paramref:`.Insert.on_conflict_do_update.index_elements` argument + specifies a sequence containing string column names, :class:`.Column` objects, + and/or SQL expression elements, which would identify a unique index:: + + do_update_stmt = insert_stmt.on_conflict_do_update( + index_elements=['id'], + set_=dict(data='updated value') + ) + + do_update_stmt = insert_stmt.on_conflict_do_update( + index_elements=[my_table.c.id], + set_=dict(data='updated value') + ) + +* When using :paramref:`.Insert.on_conflict_do_update.index_elements` to + infer an index, a partial index can be inferred by also specifying the + use the :paramref:`.Insert.on_conflict_do_update.index_where` parameter:: + + from sqlalchemy.dialects.postgresql import insert + + stmt = insert(my_table).values(user_email='a@b.com', data='inserted data') + stmt = stmt.on_conflict_do_update( + index_elements=[my_table.c.user_email], + index_where=my_table.c.user_email.like('%@gmail.com'), + set_=dict(data=stmt.excluded.data) + ) + conn.execute(stmt) + +* The :paramref:`.Insert.on_conflict_do_update.constraint` argument is + used to specify an index directly rather than inferring it. This can be + the name of a UNIQUE constraint, a PRIMARY KEY constraint, or an INDEX:: + + do_update_stmt = insert_stmt.on_conflict_do_update( + constraint='my_table_idx_1', + set_=dict(data='updated value') + ) + + do_update_stmt = insert_stmt.on_conflict_do_update( + constraint='my_table_pk', + set_=dict(data='updated value') + ) + +* The :paramref:`.Insert.on_conflict_do_update.constraint` argument may + also refer to a SQLAlchemy construct representing a constraint, + e.g. :class:`.UniqueConstraint`, :class:`.PrimaryKeyConstraint`, + :class:`.Index`, or :class:`.ExcludeConstraint`. In this use, + if the constraint has a name, it is used directly. Otherwise, if the + constraint is unnamed, then inference will be used, where the expressions + and optional WHERE clause of the constraint will be spelled out in the + construct. This use is especially convenient + to refer to the named or unnamed primary key of a :class:`.Table` using the + :attr:`.Table.primary_key` attribute:: + + do_update_stmt = insert_stmt.on_conflict_do_update( + constraint=my_table.primary_key, + set_=dict(data='updated value') + ) + +``ON CONFLICT...DO UPDATE`` is used to perform an update of the already +existing row, using any combination of new values as well as values +from the proposed insertion. These values are specified using the +:paramref:`.Insert.on_conflict_do_update.set_` parameter. This +parameter accepts a dictionary which consists of direct values +for UPDATE:: + + from sqlalchemy.dialects.postgresql import insert + + stmt = insert(my_table).values(id='some_id', data='inserted value') + do_update_stmt = stmt.on_conflict_do_update( + index_elements=['id'], + set_=dict(data='updated value') + ) + conn.execute(do_update_stmt) + +.. warning:: + + The :meth:`.Insert.on_conflict_do_update` method does **not** take into + account Python-side default UPDATE values or generation functions, e.g. + e.g. those specified using :paramref:`.Column.onupdate`. + These values will not be exercised for an ON CONFLICT style of UPDATE, + unless they are manually specified in the + :paramref:`.Insert.on_conflict_do_update.set_` dictionary. + +In order to refer to the proposed insertion row, the special alias +:attr:`~.postgresql.dml.Insert.excluded` is available as an attribute on +the :class:`.postgresql.dml.Insert` object; this object is a +:class:`.ColumnCollection` which alias contains all columns of the target +table:: + + from sqlalchemy.dialects.postgresql import insert + + stmt = insert(my_table).values( + id='some_id', + data='inserted value', + author='jlh') + do_update_stmt = stmt.on_conflict_do_update( + index_elements=['id'], + set_=dict(data='updated value', author=stmt.excluded.author) + ) + conn.execute(do_update_stmt) + +The :meth:`.Insert.on_conflict_do_update` method also accepts +a WHERE clause using the :paramref:`.Insert.on_conflict_do_update.where` +parameter, which will limit those rows which receive an UPDATE:: + + from sqlalchemy.dialects.postgresql import insert + + stmt = insert(my_table).values( + id='some_id', + data='inserted value', + author='jlh') + on_update_stmt = stmt.on_conflict_do_update( + index_elements=['id'], + set_=dict(data='updated value', author=stmt.excluded.author) + where=(my_table.c.status == 2) + ) + conn.execute(on_update_stmt) + +``ON CONFLICT`` may also be used to skip inserting a row entirely +if any conflict with a unique or exclusion constraint occurs; below +this is illustrated using the +:meth:`~.postgresql.dml.Insert.on_conflict_do_nothing` method:: + + from sqlalchemy.dialects.postgresql import insert + + stmt = insert(my_table).values(id='some_id', data='inserted value') + stmt = stmt.on_conflict_do_nothing(index_elements=['id']) + conn.execute(stmt) + +If ``DO NOTHING`` is used without specifying any columns or constraint, +it has the effect of skipping the INSERT for any unique or exclusion +constraint violation which occurs:: + + from sqlalchemy.dialects.postgresql import insert + + stmt = insert(my_table).values(id='some_id', data='inserted value') + stmt = stmt.on_conflict_do_nothing() + conn.execute(stmt) + +.. versionadded:: 1.1 Added support for PostgreSQL ON CONFLICT clauses + +.. seealso:: + + `INSERT .. ON CONFLICT + <http://www.postgresql.org/docs/current/static/sql-insert.html#SQL-ON-CONFLICT>`_ + - in the PostgreSQL documentation. + +.. _postgresql_match: + +Full Text Search +---------------- + +SQLAlchemy makes available the PostgreSQL ``@@`` operator via the +:meth:`.ColumnElement.match` method on any textual column expression. +On a PostgreSQL dialect, an expression like the following:: + + select([sometable.c.text.match("search string")]) + +will emit to the database:: + + SELECT text @@ to_tsquery('search string') FROM table + +The PostgreSQL text search functions such as ``to_tsquery()`` +and ``to_tsvector()`` are available +explicitly using the standard :data:`.func` construct. For example:: + + select([ + func.to_tsvector('fat cats ate rats').match('cat & rat') + ]) + +Emits the equivalent of:: + + SELECT to_tsvector('fat cats ate rats') @@ to_tsquery('cat & rat') + +The :class:`.postgresql.TSVECTOR` type can provide for explicit CAST:: + + from sqlalchemy.dialects.postgresql import TSVECTOR + from sqlalchemy import select, cast + select([cast("some text", TSVECTOR)]) + +produces a statement equivalent to:: + + SELECT CAST('some text' AS TSVECTOR) AS anon_1 + +Full Text Searches in PostgreSQL are influenced by a combination of: the +PostgresSQL setting of ``default_text_search_config``, the ``regconfig`` used +to build the GIN/GiST indexes, and the ``regconfig`` optionally passed in +during a query. + +When performing a Full Text Search against a column that has a GIN or +GiST index that is already pre-computed (which is common on full text +searches) one may need to explicitly pass in a particular PostgresSQL +``regconfig`` value to ensure the query-planner utilizes the index and does +not re-compute the column on demand. + +In order to provide for this explicit query planning, or to use different +search strategies, the ``match`` method accepts a ``postgresql_regconfig`` +keyword argument:: + + select([mytable.c.id]).where( + mytable.c.title.match('somestring', postgresql_regconfig='english') + ) + +Emits the equivalent of:: + + SELECT mytable.id FROM mytable + WHERE mytable.title @@ to_tsquery('english', 'somestring') + +One can also specifically pass in a `'regconfig'` value to the +``to_tsvector()`` command as the initial argument:: + + select([mytable.c.id]).where( + func.to_tsvector('english', mytable.c.title )\ + .match('somestring', postgresql_regconfig='english') + ) + +produces a statement equivalent to:: + + SELECT mytable.id FROM mytable + WHERE to_tsvector('english', mytable.title) @@ + to_tsquery('english', 'somestring') + +It is recommended that you use the ``EXPLAIN ANALYZE...`` tool from +PostgresSQL to ensure that you are generating queries with SQLAlchemy that +take full advantage of any indexes you may have created for full text search. + +FROM ONLY ... +------------------------ + +The dialect supports PostgreSQL's ONLY keyword for targeting only a particular +table in an inheritance hierarchy. This can be used to produce the +``SELECT ... FROM ONLY``, ``UPDATE ONLY ...``, and ``DELETE FROM ONLY ...`` +syntaxes. It uses SQLAlchemy's hints mechanism:: + + # SELECT ... FROM ONLY ... + result = table.select().with_hint(table, 'ONLY', 'postgresql') + print result.fetchall() + + # UPDATE ONLY ... + table.update(values=dict(foo='bar')).with_hint('ONLY', + dialect_name='postgresql') + + # DELETE FROM ONLY ... + table.delete().with_hint('ONLY', dialect_name='postgresql') + + +.. _postgresql_indexes: + +PostgreSQL-Specific Index Options +--------------------------------- + +Several extensions to the :class:`.Index` construct are available, specific +to the PostgreSQL dialect. + +.. _postgresql_partial_indexes: + +Partial Indexes +^^^^^^^^^^^^^^^ + +Partial indexes add criterion to the index definition so that the index is +applied to a subset of rows. These can be specified on :class:`.Index` +using the ``postgresql_where`` keyword argument:: + + Index('my_index', my_table.c.id, postgresql_where=my_table.c.value > 10) + +Operator Classes +^^^^^^^^^^^^^^^^ + +PostgreSQL allows the specification of an *operator class* for each column of +an index (see +http://www.postgresql.org/docs/8.3/interactive/indexes-opclass.html). +The :class:`.Index` construct allows these to be specified via the +``postgresql_ops`` keyword argument:: + + Index( + 'my_index', my_table.c.id, my_table.c.data, + postgresql_ops={ + 'data': 'text_pattern_ops', + 'id': 'int4_ops' + }) + +Note that the keys in the ``postgresql_ops`` dictionary are the "key" name of +the :class:`.Column`, i.e. the name used to access it from the ``.c`` +collection of :class:`.Table`, which can be configured to be different than +the actual name of the column as expressed in the database. + +If ``postgresql_ops`` is to be used against a complex SQL expression such +as a function call, then to apply to the column it must be given a label +that is identified in the dictionary by name, e.g.:: + + Index( + 'my_index', my_table.c.id, + func.lower(my_table.c.data).label('data_lower'), + postgresql_ops={ + 'data_lower': 'text_pattern_ops', + 'id': 'int4_ops' + }) + + +Index Types +^^^^^^^^^^^ + +PostgreSQL provides several index types: B-Tree, Hash, GiST, and GIN, as well +as the ability for users to create their own (see +http://www.postgresql.org/docs/8.3/static/indexes-types.html). These can be +specified on :class:`.Index` using the ``postgresql_using`` keyword argument:: + + Index('my_index', my_table.c.data, postgresql_using='gin') + +The value passed to the keyword argument will be simply passed through to the +underlying CREATE INDEX command, so it *must* be a valid index type for your +version of PostgreSQL. + +.. _postgresql_index_storage: + +Index Storage Parameters +^^^^^^^^^^^^^^^^^^^^^^^^ + +PostgreSQL allows storage parameters to be set on indexes. The storage +parameters available depend on the index method used by the index. Storage +parameters can be specified on :class:`.Index` using the ``postgresql_with`` +keyword argument:: + + Index('my_index', my_table.c.data, postgresql_with={"fillfactor": 50}) + +.. versionadded:: 1.0.6 + +PostgreSQL allows to define the tablespace in which to create the index. +The tablespace can be specified on :class:`.Index` using the +``postgresql_tablespace`` keyword argument:: + + Index('my_index', my_table.c.data, postgresql_tablespace='my_tablespace') + +.. versionadded:: 1.1 + +Note that the same option is available on :class:`.Table` as well. + +.. _postgresql_index_concurrently: + +Indexes with CONCURRENTLY +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The PostgreSQL index option CONCURRENTLY is supported by passing the +flag ``postgresql_concurrently`` to the :class:`.Index` construct:: + + tbl = Table('testtbl', m, Column('data', Integer)) + + idx1 = Index('test_idx1', tbl.c.data, postgresql_concurrently=True) + +The above index construct will render DDL for CREATE INDEX, assuming +PostgreSQL 8.2 or higher is detected or for a connection-less dialect, as:: + + CREATE INDEX CONCURRENTLY test_idx1 ON testtbl (data) + +For DROP INDEX, assuming PostgreSQL 9.2 or higher is detected or for +a connection-less dialect, it will emit:: + + DROP INDEX CONCURRENTLY test_idx1 + +.. versionadded:: 1.1 support for CONCURRENTLY on DROP INDEX. The + CONCURRENTLY keyword is now only emitted if a high enough version + of PostgreSQL is detected on the connection (or for a connection-less + dialect). + +When using CONCURRENTLY, the Postgresql database requires that the statement +be invoked outside of a transaction block. The Python DBAPI enforces that +even for a single statement, a transaction is present, so to use this +construct, the DBAPI's "autocommit" mode must be used:: + + metadata = MetaData() + table = Table( + "foo", metadata, + Column("id", String)) + index = Index( + "foo_idx", table.c.id, postgresql_concurrently=True) + + with engine.connect() as conn: + with conn.execution_options(isolation_level='AUTOCOMMIT'): + table.create(conn) + +.. seealso:: + + :ref:`postgresql_isolation_level` + +.. _postgresql_index_reflection: + +PostgreSQL Index Reflection +--------------------------- + +The PostgreSQL database creates a UNIQUE INDEX implicitly whenever the +UNIQUE CONSTRAINT construct is used. When inspecting a table using +:class:`.Inspector`, the :meth:`.Inspector.get_indexes` +and the :meth:`.Inspector.get_unique_constraints` will report on these +two constructs distinctly; in the case of the index, the key +``duplicates_constraint`` will be present in the index entry if it is +detected as mirroring a constraint. When performing reflection using +``Table(..., autoload=True)``, the UNIQUE INDEX is **not** returned +in :attr:`.Table.indexes` when it is detected as mirroring a +:class:`.UniqueConstraint` in the :attr:`.Table.constraints` collection. + +.. versionchanged:: 1.0.0 - :class:`.Table` reflection now includes + :class:`.UniqueConstraint` objects present in the :attr:`.Table.constraints` + collection; the PostgreSQL backend will no longer include a "mirrored" + :class:`.Index` construct in :attr:`.Table.indexes` if it is detected + as corresponding to a unique constraint. + +Special Reflection Options +-------------------------- + +The :class:`.Inspector` used for the PostgreSQL backend is an instance +of :class:`.PGInspector`, which offers additional methods:: + + from sqlalchemy import create_engine, inspect + + engine = create_engine("postgresql+psycopg2://localhost/test") + insp = inspect(engine) # will be a PGInspector + + print(insp.get_enums()) + +.. autoclass:: PGInspector + :members: + +.. _postgresql_table_options: + +PostgreSQL Table Options +------------------------ + +Several options for CREATE TABLE are supported directly by the PostgreSQL +dialect in conjunction with the :class:`.Table` construct: + +* ``TABLESPACE``:: + + Table("some_table", metadata, ..., postgresql_tablespace='some_tablespace') + + The above option is also available on the :class:`.Index` construct. + +* ``ON COMMIT``:: + + Table("some_table", metadata, ..., postgresql_on_commit='PRESERVE ROWS') + +* ``WITH OIDS``:: + + Table("some_table", metadata, ..., postgresql_with_oids=True) + +* ``WITHOUT OIDS``:: + + Table("some_table", metadata, ..., postgresql_with_oids=False) + +* ``INHERITS``:: + + Table("some_table", metadata, ..., postgresql_inherits="some_supertable") + + Table("some_table", metadata, ..., postgresql_inherits=("t1", "t2", ...)) + + .. versionadded:: 1.0.0 + +* ``PARTITION BY``:: + + Table("some_table", metadata, ..., + postgresql_partition_by='LIST (part_column)') + + .. versionadded:: 1.2.6 + +.. seealso:: + + `PostgreSQL CREATE TABLE options + <http://www.postgresql.org/docs/current/static/sql-createtable.html>`_ + +ARRAY Types +----------- + +The PostgreSQL dialect supports arrays, both as multidimensional column types +as well as array literals: + +* :class:`.postgresql.ARRAY` - ARRAY datatype + +* :class:`.postgresql.array` - array literal + +* :func:`.postgresql.array_agg` - ARRAY_AGG SQL function + +* :class:`.postgresql.aggregate_order_by` - helper for PG's ORDER BY aggregate + function syntax. + +JSON Types +---------- + +The PostgreSQL dialect supports both JSON and JSONB datatypes, including +psycopg2's native support and support for all of PostgreSQL's special +operators: + +* :class:`.postgresql.JSON` + +* :class:`.postgresql.JSONB` + +HSTORE Type +----------- + +The PostgreSQL HSTORE type as well as hstore literals are supported: + +* :class:`.postgresql.HSTORE` - HSTORE datatype + +* :class:`.postgresql.hstore` - hstore literal + +ENUM Types +---------- + +PostgreSQL has an independently creatable TYPE structure which is used +to implement an enumerated type. This approach introduces significant +complexity on the SQLAlchemy side in terms of when this type should be +CREATED and DROPPED. The type object is also an independently reflectable +entity. The following sections should be consulted: + +* :class:`.postgresql.ENUM` - DDL and typing support for ENUM. + +* :meth:`.PGInspector.get_enums` - retrieve a listing of current ENUM types + +* :meth:`.postgresql.ENUM.create` , :meth:`.postgresql.ENUM.drop` - individual + CREATE and DROP commands for ENUM. + +.. _postgresql_array_of_enum: + +Using ENUM with ARRAY +^^^^^^^^^^^^^^^^^^^^^ + +The combination of ENUM and ARRAY is not directly supported by backend +DBAPIs at this time. In order to send and receive an ARRAY of ENUM, +use the following workaround type:: + + class ArrayOfEnum(ARRAY): + + def bind_expression(self, bindvalue): + return sa.cast(bindvalue, self) + + def result_processor(self, dialect, coltype): + super_rp = super(ArrayOfEnum, self).result_processor( + dialect, coltype) + + def handle_raw_string(value): + inner = re.match(r"^{(.*)}$", value).group(1) + return inner.split(",") if inner else [] + + def process(value): + if value is None: + return None + return super_rp(handle_raw_string(value)) + return process + +E.g.:: + + Table( + 'mydata', metadata, + Column('id', Integer, primary_key=True), + Column('data', ArrayOfEnum(ENUM('a', 'b, 'c', name='myenum'))) + + ) + +This type is not included as a built-in type as it would be incompatible +with a DBAPI that suddenly decides to support ARRAY of ENUM directly in +a new version. + +.. _postgresql_array_of_json: + +Using JSON/JSONB with ARRAY +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Similar to using ENUM, for an ARRAY of JSON/JSONB we need to render the +appropriate CAST, however current psycopg2 drivers seem to handle the result +for ARRAY of JSON automatically, so the type is simpler:: + + + class CastingArray(ARRAY): + def bind_expression(self, bindvalue): + return sa.cast(bindvalue, self) + +E.g.:: + + Table( + 'mydata', metadata, + Column('id', Integer, primary_key=True), + Column('data', CastingArray(JSONB)) + ) + + +""" +from collections import defaultdict +import re +import datetime as dt + + +from sqlalchemy.sql import elements +from ... import sql, schema, exc, util +from ...engine import default, reflection +from ...sql import compiler, expression +from ...sql import sqltypes + +try: + from uuid import UUID as _python_UUID +except ImportError: + _python_UUID = None + +from sqlalchemy.types import INTEGER, BIGINT, SMALLINT, VARCHAR, \ + CHAR, TEXT, FLOAT, NUMERIC, \ + DATE, BOOLEAN, REAL + +AUTOCOMMIT_REGEXP = re.compile( + r'\s*(?:UPDATE|INSERT|CREATE|DELETE|DROP|ALTER|GRANT|REVOKE|' + 'IMPORT FOREIGN SCHEMA|REFRESH MATERIALIZED VIEW|TRUNCATE)', + re.I | re.UNICODE) + +RESERVED_WORDS = set( + ["all", "analyse", "analyze", "and", "any", "array", "as", "asc", + "asymmetric", "both", "case", "cast", "check", "collate", "column", + "constraint", "create", "current_catalog", "current_date", + "current_role", "current_time", "current_timestamp", "current_user", + "default", "deferrable", "desc", "distinct", "do", "else", "end", + "except", "false", "fetch", "for", "foreign", "from", "grant", "group", + "having", "in", "initially", "intersect", "into", "leading", "limit", + "localtime", "localtimestamp", "new", "not", "null", "of", "off", + "offset", "old", "on", "only", "or", "order", "placing", "primary", + "references", "returning", "select", "session_user", "some", "symmetric", + "table", "then", "to", "trailing", "true", "union", "unique", "user", + "using", "variadic", "when", "where", "window", "with", "authorization", + "between", "binary", "cross", "current_schema", "freeze", "full", + "ilike", "inner", "is", "isnull", "join", "left", "like", "natural", + "notnull", "outer", "over", "overlaps", "right", "similar", "verbose" + ]) + +_DECIMAL_TYPES = (1231, 1700) +_FLOAT_TYPES = (700, 701, 1021, 1022) +_INT_TYPES = (20, 21, 23, 26, 1005, 1007, 1016) + +class BYTEA(sqltypes.LargeBinary): + __visit_name__ = 'BYTEA' + + +class DOUBLE_PRECISION(sqltypes.Float): + __visit_name__ = 'DOUBLE_PRECISION' + + +class INET(sqltypes.TypeEngine): + __visit_name__ = "INET" +PGInet = INET + + +class CIDR(sqltypes.TypeEngine): + __visit_name__ = "CIDR" +PGCidr = CIDR + + +class MACADDR(sqltypes.TypeEngine): + __visit_name__ = "MACADDR" +PGMacAddr = MACADDR + + +class MONEY(sqltypes.TypeEngine): + + """Provide the PostgreSQL MONEY type. + + .. versionadded:: 1.2 + + """ + __visit_name__ = "MONEY" + + +class OID(sqltypes.TypeEngine): + + """Provide the PostgreSQL OID type. + + .. versionadded:: 0.9.5 + + """ + __visit_name__ = "OID" + + +class REGCLASS(sqltypes.TypeEngine): + + """Provide the PostgreSQL REGCLASS type. + + .. versionadded:: 1.2.7 + + """ + __visit_name__ = "REGCLASS" + + +class TIMESTAMP(sqltypes.TIMESTAMP): + + def __init__(self, timezone=False, precision=None): + super(TIMESTAMP, self).__init__(timezone=timezone) + self.precision = precision + + +class TIME(sqltypes.TIME): + + def __init__(self, timezone=False, precision=None): + super(TIME, self).__init__(timezone=timezone) + self.precision = precision + + +class INTERVAL(sqltypes.NativeForEmulated, sqltypes._AbstractInterval): + + """PostgreSQL INTERVAL type. + + The INTERVAL type may not be supported on all DBAPIs. + It is known to work on psycopg2 and not pg8000 or zxjdbc. + + """ + __visit_name__ = 'INTERVAL' + native = True + + def __init__(self, precision=None, fields=None): + """Construct an INTERVAL. + + :param precision: optional integer precision value + :param fields: string fields specifier. allows storage of fields + to be limited, such as ``"YEAR"``, ``"MONTH"``, ``"DAY TO HOUR"``, + etc. + + .. versionadded:: 1.2 + + """ + self.precision = precision + self.fields = fields + + @classmethod + def adapt_emulated_to_native(cls, interval, **kw): + return INTERVAL(precision=interval.second_precision) + + @property + def _type_affinity(self): + return sqltypes.Interval + + @property + def python_type(self): + return dt.timedelta + +PGInterval = INTERVAL + + +class BIT(sqltypes.TypeEngine): + __visit_name__ = 'BIT' + + def __init__(self, length=None, varying=False): + if not varying: + # BIT without VARYING defaults to length 1 + self.length = length or 1 + else: + # but BIT VARYING can be unlimited-length, so no default + self.length = length + self.varying = varying + +PGBit = BIT + + +class UUID(sqltypes.TypeEngine): + + """PostgreSQL UUID type. + + Represents the UUID column type, interpreting + data either as natively returned by the DBAPI + or as Python uuid objects. + + The UUID type may not be supported on all DBAPIs. + It is known to work on psycopg2 and not pg8000. + + """ + __visit_name__ = 'UUID' + + def __init__(self, as_uuid=False): + """Construct a UUID type. + + + :param as_uuid=False: if True, values will be interpreted + as Python uuid objects, converting to/from string via the + DBAPI. + + """ + if as_uuid and _python_UUID is None: + raise NotImplementedError( + "This version of Python does not support " + "the native UUID type." + ) + self.as_uuid = as_uuid + + def bind_processor(self, dialect): + if self.as_uuid: + def process(value): + if value is not None: + value = util.text_type(value) + return value + return process + else: + return None + + def result_processor(self, dialect, coltype): + if self.as_uuid: + def process(value): + if value is not None: + value = _python_UUID(value) + return value + return process + else: + return None + +PGUuid = UUID + + +class TSVECTOR(sqltypes.TypeEngine): + + """The :class:`.postgresql.TSVECTOR` type implements the PostgreSQL + text search type TSVECTOR. + + It can be used to do full text queries on natural language + documents. + + .. versionadded:: 0.9.0 + + .. seealso:: + + :ref:`postgresql_match` + + """ + __visit_name__ = 'TSVECTOR' + + +class ENUM(sqltypes.NativeForEmulated, sqltypes.Enum): + + """PostgreSQL ENUM type. + + This is a subclass of :class:`.types.Enum` which includes + support for PG's ``CREATE TYPE`` and ``DROP TYPE``. + + When the builtin type :class:`.types.Enum` is used and the + :paramref:`.Enum.native_enum` flag is left at its default of + True, the PostgreSQL backend will use a :class:`.postgresql.ENUM` + type as the implementation, so the special create/drop rules + will be used. + + The create/drop behavior of ENUM is necessarily intricate, due to the + awkward relationship the ENUM type has in relationship to the + parent table, in that it may be "owned" by just a single table, or + may be shared among many tables. + + When using :class:`.types.Enum` or :class:`.postgresql.ENUM` + in an "inline" fashion, the ``CREATE TYPE`` and ``DROP TYPE`` is emitted + corresponding to when the :meth:`.Table.create` and :meth:`.Table.drop` + methods are called:: + + table = Table('sometable', metadata, + Column('some_enum', ENUM('a', 'b', 'c', name='myenum')) + ) + + table.create(engine) # will emit CREATE ENUM and CREATE TABLE + table.drop(engine) # will emit DROP TABLE and DROP ENUM + + To use a common enumerated type between multiple tables, the best + practice is to declare the :class:`.types.Enum` or + :class:`.postgresql.ENUM` independently, and associate it with the + :class:`.MetaData` object itself:: + + my_enum = ENUM('a', 'b', 'c', name='myenum', metadata=metadata) + + t1 = Table('sometable_one', metadata, + Column('some_enum', myenum) + ) + + t2 = Table('sometable_two', metadata, + Column('some_enum', myenum) + ) + + When this pattern is used, care must still be taken at the level + of individual table creates. Emitting CREATE TABLE without also + specifying ``checkfirst=True`` will still cause issues:: + + t1.create(engine) # will fail: no such type 'myenum' + + If we specify ``checkfirst=True``, the individual table-level create + operation will check for the ``ENUM`` and create if not exists:: + + # will check if enum exists, and emit CREATE TYPE if not + t1.create(engine, checkfirst=True) + + When using a metadata-level ENUM type, the type will always be created + and dropped if either the metadata-wide create/drop is called:: + + metadata.create_all(engine) # will emit CREATE TYPE + metadata.drop_all(engine) # will emit DROP TYPE + + The type can also be created and dropped directly:: + + my_enum.create(engine) + my_enum.drop(engine) + + .. versionchanged:: 1.0.0 The PostgreSQL :class:`.postgresql.ENUM` type + now behaves more strictly with regards to CREATE/DROP. A metadata-level + ENUM type will only be created and dropped at the metadata level, + not the table level, with the exception of + ``table.create(checkfirst=True)``. + The ``table.drop()`` call will now emit a DROP TYPE for a table-level + enumerated type. + + """ + + native_enum = True + + def __init__(self, *enums, **kw): + """Construct an :class:`~.postgresql.ENUM`. + + Arguments are the same as that of + :class:`.types.Enum`, but also including + the following parameters. + + :param create_type: Defaults to True. + Indicates that ``CREATE TYPE`` should be + emitted, after optionally checking for the + presence of the type, when the parent + table is being created; and additionally + that ``DROP TYPE`` is called when the table + is dropped. When ``False``, no check + will be performed and no ``CREATE TYPE`` + or ``DROP TYPE`` is emitted, unless + :meth:`~.postgresql.ENUM.create` + or :meth:`~.postgresql.ENUM.drop` + are called directly. + Setting to ``False`` is helpful + when invoking a creation scheme to a SQL file + without access to the actual database - + the :meth:`~.postgresql.ENUM.create` and + :meth:`~.postgresql.ENUM.drop` methods can + be used to emit SQL to a target bind. + + .. versionadded:: 0.7.4 + + """ + self.create_type = kw.pop("create_type", True) + super(ENUM, self).__init__(*enums, **kw) + + @classmethod + def adapt_emulated_to_native(cls, impl, **kw): + """Produce a Postgresql native :class:`.postgresql.ENUM` from plain + :class:`.Enum`. + + """ + kw.setdefault("validate_strings", impl.validate_strings) + kw.setdefault('name', impl.name) + kw.setdefault('schema', impl.schema) + kw.setdefault('inherit_schema', impl.inherit_schema) + kw.setdefault('metadata', impl.metadata) + kw.setdefault('_create_events', False) + kw.setdefault('values_callable', impl.values_callable) + return cls(**kw) + + def create(self, bind=None, checkfirst=True): + """Emit ``CREATE TYPE`` for this + :class:`~.postgresql.ENUM`. + + If the underlying dialect does not support + PostgreSQL CREATE TYPE, no action is taken. + + :param bind: a connectable :class:`.Engine`, + :class:`.Connection`, or similar object to emit + SQL. + :param checkfirst: if ``True``, a query against + the PG catalog will be first performed to see + if the type does not exist already before + creating. + + """ + if not bind.dialect.supports_native_enum: + return + + if not checkfirst or \ + not bind.dialect.has_type( + bind, self.name, schema=self.schema): + bind.execute(CreateEnumType(self)) + + def drop(self, bind=None, checkfirst=True): + """Emit ``DROP TYPE`` for this + :class:`~.postgresql.ENUM`. + + If the underlying dialect does not support + PostgreSQL DROP TYPE, no action is taken. + + :param bind: a connectable :class:`.Engine`, + :class:`.Connection`, or similar object to emit + SQL. + :param checkfirst: if ``True``, a query against + the PG catalog will be first performed to see + if the type actually exists before dropping. + + """ + if not bind.dialect.supports_native_enum: + return + + if not checkfirst or \ + bind.dialect.has_type(bind, self.name, schema=self.schema): + bind.execute(DropEnumType(self)) + + def _check_for_name_in_memos(self, checkfirst, kw): + """Look in the 'ddl runner' for 'memos', then + note our name in that collection. + + This to ensure a particular named enum is operated + upon only once within any kind of create/drop + sequence without relying upon "checkfirst". + + """ + if not self.create_type: + return True + if '_ddl_runner' in kw: + ddl_runner = kw['_ddl_runner'] + if '_pg_enums' in ddl_runner.memo: + pg_enums = ddl_runner.memo['_pg_enums'] + else: + pg_enums = ddl_runner.memo['_pg_enums'] = set() + present = self.name in pg_enums + pg_enums.add(self.name) + return present + else: + return False + + def _on_table_create(self, target, bind, checkfirst=False, **kw): + if checkfirst or ( + not self.metadata and + not kw.get('_is_metadata_operation', False)) and \ + not self._check_for_name_in_memos(checkfirst, kw): + self.create(bind=bind, checkfirst=checkfirst) + + def _on_table_drop(self, target, bind, checkfirst=False, **kw): + if not self.metadata and \ + not kw.get('_is_metadata_operation', False) and \ + not self._check_for_name_in_memos(checkfirst, kw): + self.drop(bind=bind, checkfirst=checkfirst) + + def _on_metadata_create(self, target, bind, checkfirst=False, **kw): + if not self._check_for_name_in_memos(checkfirst, kw): + self.create(bind=bind, checkfirst=checkfirst) + + def _on_metadata_drop(self, target, bind, checkfirst=False, **kw): + if not self._check_for_name_in_memos(checkfirst, kw): + self.drop(bind=bind, checkfirst=checkfirst) + +colspecs = { + sqltypes.Interval: INTERVAL, + sqltypes.Enum: ENUM, +} + +ischema_names = { + 'integer': INTEGER, + 'bigint': BIGINT, + 'smallint': SMALLINT, + 'character varying': VARCHAR, + 'character': CHAR, + '"char"': sqltypes.String, + 'name': sqltypes.String, + 'text': TEXT, + 'numeric': NUMERIC, + 'float': FLOAT, + 'real': REAL, + 'inet': INET, + 'cidr': CIDR, + 'uuid': UUID, + 'bit': BIT, + 'bit varying': BIT, + 'macaddr': MACADDR, + 'money': MONEY, + 'oid': OID, + 'regclass': REGCLASS, + 'double precision': DOUBLE_PRECISION, + 'timestamp': TIMESTAMP, + 'timestamp with time zone': TIMESTAMP, + 'timestamp without time zone': TIMESTAMP, + 'time with time zone': TIME, + 'time without time zone': TIME, + 'date': DATE, + 'time': TIME, + 'bytea': BYTEA, + 'boolean': BOOLEAN, + 'interval': INTERVAL, + 'tsvector': TSVECTOR +} + + +class PGCompiler(compiler.SQLCompiler): + + def visit_array(self, element, **kw): + return "ARRAY[%s]" % self.visit_clauselist(element, **kw) + + def visit_slice(self, element, **kw): + return "%s:%s" % ( + self.process(element.start, **kw), + self.process(element.stop, **kw), + ) + + def visit_json_getitem_op_binary(self, binary, operator, **kw): + kw['eager_grouping'] = True + return self._generate_generic_binary( + binary, " -> ", **kw + ) + + def visit_json_path_getitem_op_binary(self, binary, operator, **kw): + kw['eager_grouping'] = True + return self._generate_generic_binary( + binary, " #> ", **kw + ) + + def visit_getitem_binary(self, binary, operator, **kw): + return "%s[%s]" % ( + self.process(binary.left, **kw), + self.process(binary.right, **kw) + ) + + def visit_aggregate_order_by(self, element, **kw): + return "%s ORDER BY %s" % ( + self.process(element.target, **kw), + self.process(element.order_by, **kw) + ) + + def visit_match_op_binary(self, binary, operator, **kw): + if "postgresql_regconfig" in binary.modifiers: + regconfig = self.render_literal_value( + binary.modifiers['postgresql_regconfig'], + sqltypes.STRINGTYPE) + if regconfig: + return "%s @@ to_tsquery(%s, %s)" % ( + self.process(binary.left, **kw), + regconfig, + self.process(binary.right, **kw) + ) + return "%s @@ to_tsquery(%s)" % ( + self.process(binary.left, **kw), + self.process(binary.right, **kw) + ) + + def visit_ilike_op_binary(self, binary, operator, **kw): + escape = binary.modifiers.get("escape", None) + + return '%s ILIKE %s' % \ + (self.process(binary.left, **kw), + self.process(binary.right, **kw)) \ + + ( + ' ESCAPE ' + + self.render_literal_value(escape, sqltypes.STRINGTYPE) + if escape else '' + ) + + def visit_notilike_op_binary(self, binary, operator, **kw): + escape = binary.modifiers.get("escape", None) + return '%s NOT ILIKE %s' % \ + (self.process(binary.left, **kw), + self.process(binary.right, **kw)) \ + + ( + ' ESCAPE ' + + self.render_literal_value(escape, sqltypes.STRINGTYPE) + if escape else '' + ) + + def render_literal_value(self, value, type_): + value = super(PGCompiler, self).render_literal_value(value, type_) + + if self.dialect._backslash_escapes: + value = value.replace('\\', '\\\\') + return value + + def visit_sequence(self, seq, **kw): + return "nextval('%s')" % self.preparer.format_sequence(seq) + + def limit_clause(self, select, **kw): + text = "" + if select._limit_clause is not None: + text += " \n LIMIT " + self.process(select._limit_clause, **kw) + if select._offset_clause is not None: + if select._limit_clause is None: + text += " \n LIMIT ALL" + text += " OFFSET " + self.process(select._offset_clause, **kw) + return text + + def format_from_hint_text(self, sqltext, table, hint, iscrud): + if hint.upper() != 'ONLY': + raise exc.CompileError("Unrecognized hint: %r" % hint) + return "ONLY " + sqltext + + def get_select_precolumns(self, select, **kw): + if select._distinct is not False: + if select._distinct is True: + return "DISTINCT " + elif isinstance(select._distinct, (list, tuple)): + return "DISTINCT ON (" + ', '.join( + [self.process(col, **kw) for col in select._distinct] + ) + ") " + else: + return "DISTINCT ON (" + \ + self.process(select._distinct, **kw) + ") " + else: + return "" + + def for_update_clause(self, select, **kw): + + if select._for_update_arg.read: + if select._for_update_arg.key_share: + tmp = " FOR KEY SHARE" + else: + tmp = " FOR SHARE" + elif select._for_update_arg.key_share: + tmp = " FOR NO KEY UPDATE" + else: + tmp = " FOR UPDATE" + + if select._for_update_arg.of: + tables = util.OrderedSet( + c.table if isinstance(c, expression.ColumnClause) + else c for c in select._for_update_arg.of) + tmp += " OF " + ", ".join( + self.process(table, ashint=True, use_schema=False, **kw) + for table in tables + ) + + if select._for_update_arg.nowait: + tmp += " NOWAIT" + if select._for_update_arg.skip_locked: + tmp += " SKIP LOCKED" + + return tmp + + def returning_clause(self, stmt, returning_cols): + + columns = [ + self._label_select_column(None, c, True, False, {}) + for c in expression._select_iterables(returning_cols) + ] + + return 'RETURNING ' + ', '.join(columns) + + def visit_substring_func(self, func, **kw): + s = self.process(func.clauses.clauses[0], **kw) + start = self.process(func.clauses.clauses[1], **kw) + if len(func.clauses.clauses) > 2: + length = self.process(func.clauses.clauses[2], **kw) + return "SUBSTRING(%s FROM %s FOR %s)" % (s, start, length) + else: + return "SUBSTRING(%s FROM %s)" % (s, start) + + def _on_conflict_target(self, clause, **kw): + + if clause.constraint_target is not None: + target_text = 'ON CONSTRAINT %s' % clause.constraint_target + elif clause.inferred_target_elements is not None: + target_text = '(%s)' % ', '.join( + (self.preparer.quote(c) + if isinstance(c, util.string_types) + else + self.process(c, include_table=False, use_schema=False)) + for c in clause.inferred_target_elements + ) + if clause.inferred_target_whereclause is not None: + target_text += ' WHERE %s' % \ + self.process( + clause.inferred_target_whereclause, + include_table=False, + use_schema=False + ) + else: + target_text = '' + + return target_text + + def visit_on_conflict_do_nothing(self, on_conflict, **kw): + + target_text = self._on_conflict_target(on_conflict, **kw) + + if target_text: + return "ON CONFLICT %s DO NOTHING" % target_text + else: + return "ON CONFLICT DO NOTHING" + + def visit_on_conflict_do_update(self, on_conflict, **kw): + + clause = on_conflict + + target_text = self._on_conflict_target(on_conflict, **kw) + + action_set_ops = [] + + set_parameters = dict(clause.update_values_to_set) + # create a list of column assignment clauses as tuples + + insert_statement = self.stack[-1]['selectable'] + cols = insert_statement.table.c + for c in cols: + col_key = c.key + if col_key in set_parameters: + value = set_parameters.pop(col_key) + if elements._is_literal(value): + value = elements.BindParameter( + None, value, type_=c.type + ) + + else: + if isinstance(value, elements.BindParameter) and \ + value.type._isnull: + value = value._clone() + value.type = c.type + value_text = self.process(value.self_group(), use_schema=False) + + key_text = ( + self.preparer.quote(col_key) + ) + action_set_ops.append('%s = %s' % (key_text, value_text)) + + # check for names that don't match columns + if set_parameters: + util.warn( + "Additional column names not matching " + "any column keys in table '%s': %s" % ( + self.statement.table.name, + (", ".join("'%s'" % c for c in set_parameters)) + ) + ) + for k, v in set_parameters.items(): + key_text = ( + self.preparer.quote(k) + if isinstance(k, util.string_types) + else self.process(k, use_schema=False) + ) + value_text = self.process( + elements._literal_as_binds(v), + use_schema=False + ) + action_set_ops.append('%s = %s' % (key_text, value_text)) + + action_text = ', '.join(action_set_ops) + if clause.update_whereclause is not None: + action_text += ' WHERE %s' % \ + self.process( + clause.update_whereclause, + include_table=True, + use_schema=False + ) + + return 'ON CONFLICT %s DO UPDATE SET %s' % (target_text, action_text) + + def update_from_clause(self, update_stmt, + from_table, extra_froms, + from_hints, + **kw): + return "FROM " + ', '.join( + t._compiler_dispatch(self, asfrom=True, + fromhints=from_hints, **kw) + for t in extra_froms) + + def delete_extra_from_clause(self, delete_stmt, from_table, + extra_froms, from_hints, **kw): + """Render the DELETE .. USING clause specific to PostgresSQL.""" + return "USING " + ', '.join( + t._compiler_dispatch(self, asfrom=True, + fromhints=from_hints, **kw) + for t in extra_froms) + + +class PGDDLCompiler(compiler.DDLCompiler): + + def get_column_specification(self, column, **kwargs): + + colspec = self.preparer.format_column(column) + impl_type = column.type.dialect_impl(self.dialect) + if isinstance(impl_type, sqltypes.TypeDecorator): + impl_type = impl_type.impl + + if column.primary_key and \ + column is column.table._autoincrement_column and \ + ( + self.dialect.supports_smallserial or + not isinstance(impl_type, sqltypes.SmallInteger) + ) and ( + column.default is None or + ( + isinstance(column.default, schema.Sequence) and + column.default.optional + )): + if isinstance(impl_type, sqltypes.BigInteger): + colspec += " BIGSERIAL" + elif isinstance(impl_type, sqltypes.SmallInteger): + colspec += " SMALLSERIAL" + else: + colspec += " SERIAL" + else: + colspec += " " + self.dialect.type_compiler.process( + column.type, type_expression=column) + default = self.get_column_default_string(column) + if default is not None: + colspec += " DEFAULT " + default + + if not column.nullable: + colspec += " NOT NULL" + return colspec + + def visit_create_enum_type(self, create): + type_ = create.element + + return "CREATE TYPE %s AS ENUM (%s)" % ( + self.preparer.format_type(type_), + ", ".join( + self.sql_compiler.process(sql.literal(e), literal_binds=True) + for e in type_.enums) + ) + + def visit_drop_enum_type(self, drop): + type_ = drop.element + + return "DROP TYPE %s" % ( + self.preparer.format_type(type_) + ) + + def visit_create_index(self, create): + preparer = self.preparer + index = create.element + self._verify_index_table(index) + text = "CREATE " + if index.unique: + text += "UNIQUE " + text += "INDEX " + + if self.dialect._supports_create_index_concurrently: + concurrently = index.dialect_options['postgresql']['concurrently'] + if concurrently: + text += "CONCURRENTLY " + + text += "%s ON %s " % ( + self._prepared_index_name(index, + include_schema=False), + preparer.format_table(index.table) + ) + + using = index.dialect_options['postgresql']['using'] + if using: + text += "USING %s " % preparer.quote(using) + + ops = index.dialect_options["postgresql"]["ops"] + text += "(%s)" \ + % ( + ', '.join([ + self.sql_compiler.process( + expr.self_group() + if not isinstance(expr, expression.ColumnClause) + else expr, + include_table=False, literal_binds=True) + + ( + (' ' + ops[expr.key]) + if hasattr(expr, 'key') + and expr.key in ops else '' + ) + for expr in index.expressions + ]) + ) + + withclause = index.dialect_options['postgresql']['with'] + + if withclause: + text += " WITH (%s)" % (', '.join( + ['%s = %s' % storage_parameter + for storage_parameter in withclause.items()])) + + tablespace_name = index.dialect_options['postgresql']['tablespace'] + + if tablespace_name: + text += " TABLESPACE %s" % preparer.quote(tablespace_name) + + whereclause = index.dialect_options["postgresql"]["where"] + + if whereclause is not None: + where_compiled = self.sql_compiler.process( + whereclause, include_table=False, + literal_binds=True) + text += " WHERE " + where_compiled + return text + + def visit_drop_index(self, drop): + index = drop.element + + text = "\nDROP INDEX " + + if self.dialect._supports_drop_index_concurrently: + concurrently = index.dialect_options['postgresql']['concurrently'] + if concurrently: + text += "CONCURRENTLY " + + text += self._prepared_index_name(index, include_schema=True) + return text + + def visit_exclude_constraint(self, constraint, **kw): + text = "" + if constraint.name is not None: + text += "CONSTRAINT %s " % \ + self.preparer.format_constraint(constraint) + elements = [] + for expr, name, op in constraint._render_exprs: + kw['include_table'] = False + elements.append( + "%s WITH %s" % (self.sql_compiler.process(expr, **kw), op) + ) + text += "EXCLUDE USING %s (%s)" % (constraint.using, + ', '.join(elements)) + if constraint.where is not None: + text += ' WHERE (%s)' % self.sql_compiler.process( + constraint.where, + literal_binds=True) + text += self.define_constraint_deferrability(constraint) + return text + + def post_create_table(self, table): + table_opts = [] + pg_opts = table.dialect_options['postgresql'] + + inherits = pg_opts.get('inherits') + if inherits is not None: + if not isinstance(inherits, (list, tuple)): + inherits = (inherits, ) + table_opts.append( + '\n INHERITS ( ' + + ', '.join(self.preparer.quote(name) for name in inherits) + + ' )') + + if pg_opts['partition_by']: + table_opts.append('\n PARTITION BY %s' % pg_opts['partition_by']) + + if pg_opts['with_oids'] is True: + table_opts.append('\n WITH OIDS') + elif pg_opts['with_oids'] is False: + table_opts.append('\n WITHOUT OIDS') + + if pg_opts['on_commit']: + on_commit_options = pg_opts['on_commit'].replace("_", " ").upper() + table_opts.append('\n ON COMMIT %s' % on_commit_options) + + if pg_opts['tablespace']: + tablespace_name = pg_opts['tablespace'] + table_opts.append( + '\n TABLESPACE %s' % self.preparer.quote(tablespace_name) + ) + + return ''.join(table_opts) + + +class PGTypeCompiler(compiler.GenericTypeCompiler): + def visit_TSVECTOR(self, type, **kw): + return "TSVECTOR" + + def visit_INET(self, type_, **kw): + return "INET" + + def visit_CIDR(self, type_, **kw): + return "CIDR" + + def visit_MACADDR(self, type_, **kw): + return "MACADDR" + + def visit_MONEY(self, type_, **kw): + return "MONEY" + + def visit_OID(self, type_, **kw): + return "OID" + + def visit_REGCLASS(self, type_, **kw): + return "REGCLASS" + + def visit_FLOAT(self, type_, **kw): + if not type_.precision: + return "FLOAT" + else: + return "FLOAT(%(precision)s)" % {'precision': type_.precision} + + def visit_DOUBLE_PRECISION(self, type_, **kw): + return "DOUBLE PRECISION" + + def visit_BIGINT(self, type_, **kw): + return "BIGINT" + + def visit_HSTORE(self, type_, **kw): + return "HSTORE" + + def visit_JSON(self, type_, **kw): + return "JSON" + + def visit_JSONB(self, type_, **kw): + return "JSONB" + + def visit_INT4RANGE(self, type_, **kw): + return "INT4RANGE" + + def visit_INT8RANGE(self, type_, **kw): + return "INT8RANGE" + + def visit_NUMRANGE(self, type_, **kw): + return "NUMRANGE" + + def visit_DATERANGE(self, type_, **kw): + return "DATERANGE" + + def visit_TSRANGE(self, type_, **kw): + return "TSRANGE" + + def visit_TSTZRANGE(self, type_, **kw): + return "TSTZRANGE" + + def visit_datetime(self, type_, **kw): + return self.visit_TIMESTAMP(type_, **kw) + + def visit_enum(self, type_, **kw): + if not type_.native_enum or not self.dialect.supports_native_enum: + return super(PGTypeCompiler, self).visit_enum(type_, **kw) + else: + return self.visit_ENUM(type_, **kw) + + def visit_ENUM(self, type_, **kw): + return self.dialect.identifier_preparer.format_type(type_) + + def visit_TIMESTAMP(self, type_, **kw): + return "TIMESTAMP%s %s" % ( + "(%d)" % type_.precision + if getattr(type_, 'precision', None) is not None else "", + (type_.timezone and "WITH" or "WITHOUT") + " TIME ZONE" + ) + + def visit_TIME(self, type_, **kw): + return "TIME%s %s" % ( + "(%d)" % type_.precision + if getattr(type_, 'precision', None) is not None else "", + (type_.timezone and "WITH" or "WITHOUT") + " TIME ZONE" + ) + + def visit_INTERVAL(self, type_, **kw): + text = "INTERVAL" + if type_.fields is not None: + text += " " + type_.fields + if type_.precision is not None: + text += " (%d)" % type_.precision + return text + + def visit_BIT(self, type_, **kw): + if type_.varying: + compiled = "BIT VARYING" + if type_.length is not None: + compiled += "(%d)" % type_.length + else: + compiled = "BIT(%d)" % type_.length + return compiled + + def visit_UUID(self, type_, **kw): + return "UUID" + + def visit_large_binary(self, type_, **kw): + return self.visit_BYTEA(type_, **kw) + + def visit_BYTEA(self, type_, **kw): + return "BYTEA" + + def visit_ARRAY(self, type_, **kw): + + # TODO: pass **kw? + inner = self.process(type_.item_type) + return re.sub( + r'((?: COLLATE.*)?)$', + (r'%s\1' % ( + "[]" * + (type_.dimensions if type_.dimensions is not None else 1) + )), + inner, + count=1 + ) + + +class PGIdentifierPreparer(compiler.IdentifierPreparer): + + reserved_words = RESERVED_WORDS + + def _unquote_identifier(self, value): + if value[0] == self.initial_quote: + value = value[1:-1].\ + replace(self.escape_to_quote, self.escape_quote) + return value + + def format_type(self, type_, use_schema=True): + if not type_.name: + raise exc.CompileError("PostgreSQL ENUM type requires a name.") + + name = self.quote(type_.name) + effective_schema = self.schema_for_object(type_) + + if not self.omit_schema and use_schema and \ + effective_schema is not None: + name = self.quote_schema(effective_schema) + "." + name + return name + + +class PGInspector(reflection.Inspector): + + def __init__(self, conn): + reflection.Inspector.__init__(self, conn) + + def get_table_oid(self, table_name, schema=None): + """Return the OID for the given table name.""" + + return self.dialect.get_table_oid(self.bind, table_name, schema, + info_cache=self.info_cache) + + def get_enums(self, schema=None): + """Return a list of ENUM objects. + + Each member is a dictionary containing these fields: + + * name - name of the enum + * schema - the schema name for the enum. + * visible - boolean, whether or not this enum is visible + in the default search path. + * labels - a list of string labels that apply to the enum. + + :param schema: schema name. If None, the default schema + (typically 'public') is used. May also be set to '*' to + indicate load enums for all schemas. + + .. versionadded:: 1.0.0 + + """ + schema = schema or self.default_schema_name + return self.dialect._load_enums(self.bind, schema) + + def get_foreign_table_names(self, schema=None): + """Return a list of FOREIGN TABLE names. + + Behavior is similar to that of :meth:`.Inspector.get_table_names`, + except that the list is limited to those tables tha report a + ``relkind`` value of ``f``. + + .. versionadded:: 1.0.0 + + """ + schema = schema or self.default_schema_name + return self.dialect._get_foreign_table_names(self.bind, schema) + + def get_view_names(self, schema=None, include=('plain', 'materialized')): + """Return all view names in `schema`. + + :param schema: Optional, retrieve names from a non-default schema. + For special quoting, use :class:`.quoted_name`. + + :param include: specify which types of views to return. Passed + as a string value (for a single type) or a tuple (for any number + of types). Defaults to ``('plain', 'materialized')``. + + .. versionadded:: 1.1 + + """ + + return self.dialect.get_view_names(self.bind, schema, + info_cache=self.info_cache, + include=include) + + +class CreateEnumType(schema._CreateDropBase): + __visit_name__ = "create_enum_type" + + +class DropEnumType(schema._CreateDropBase): + __visit_name__ = "drop_enum_type" + + +class PGExecutionContext(default.DefaultExecutionContext): + + def fire_sequence(self, seq, type_): + return self._execute_scalar(( + "select nextval('%s')" % + self.dialect.identifier_preparer.format_sequence(seq)), type_) + + def get_insert_default(self, column): + if column.primary_key and \ + column is column.table._autoincrement_column: + if column.server_default and column.server_default.has_argument: + + # pre-execute passive defaults on primary key columns + return self._execute_scalar("select %s" % + column.server_default.arg, + column.type) + + elif (column.default is None or + (column.default.is_sequence and + column.default.optional)): + + # execute the sequence associated with a SERIAL primary + # key column. for non-primary-key SERIAL, the ID just + # generates server side. + + try: + seq_name = column._postgresql_seq_name + except AttributeError: + tab = column.table.name + col = column.name + tab = tab[0:29 + max(0, (29 - len(col)))] + col = col[0:29 + max(0, (29 - len(tab)))] + name = "%s_%s_seq" % (tab, col) + column._postgresql_seq_name = seq_name = name + + if column.table is not None: + effective_schema = self.connection.schema_for_object( + column.table) + else: + effective_schema = None + + if effective_schema is not None: + exc = "select nextval('\"%s\".\"%s\"')" % \ + (effective_schema, seq_name) + else: + exc = "select nextval('\"%s\"')" % \ + (seq_name, ) + + return self._execute_scalar(exc, column.type) + + return super(PGExecutionContext, self).get_insert_default(column) + + def should_autocommit_text(self, statement): + return AUTOCOMMIT_REGEXP.match(statement) + + +class PGDialect(default.DefaultDialect): + name = 'postgresql' + supports_alter = True + max_identifier_length = 63 + supports_sane_rowcount = True + + supports_native_enum = True + supports_native_boolean = True + supports_smallserial = True + + supports_sequences = True + sequences_optional = True + preexecute_autoincrement_sequences = True + postfetch_lastrowid = False + + supports_comments = True + supports_default_values = True + supports_empty_insert = False + supports_multivalues_insert = True + default_paramstyle = 'pyformat' + ischema_names = ischema_names + colspecs = colspecs + + statement_compiler = PGCompiler + ddl_compiler = PGDDLCompiler + type_compiler = PGTypeCompiler + preparer = PGIdentifierPreparer + execution_ctx_cls = PGExecutionContext + inspector = PGInspector + isolation_level = None + + construct_arguments = [ + (schema.Index, { + "using": False, + "where": None, + "ops": {}, + "concurrently": False, + "with": {}, + "tablespace": None + }), + (schema.Table, { + "ignore_search_path": False, + "tablespace": None, + "partition_by": None, + "with_oids": None, + "on_commit": None, + "inherits": None + }), + ] + + reflection_options = ('postgresql_ignore_search_path', ) + + _backslash_escapes = True + _supports_create_index_concurrently = True + _supports_drop_index_concurrently = True + + def __init__(self, isolation_level=None, json_serializer=None, + json_deserializer=None, **kwargs): + default.DefaultDialect.__init__(self, **kwargs) + self.isolation_level = isolation_level + self._json_deserializer = json_deserializer + self._json_serializer = json_serializer + + def initialize(self, connection): + super(PGDialect, self).initialize(connection) + self.implicit_returning = self.server_version_info > (8, 2) and \ + self.__dict__.get('implicit_returning', True) + self.supports_native_enum = self.server_version_info >= (8, 3) + if not self.supports_native_enum: + self.colspecs = self.colspecs.copy() + # pop base Enum type + self.colspecs.pop(sqltypes.Enum, None) + # psycopg2, others may have placed ENUM here as well + self.colspecs.pop(ENUM, None) + + # http://www.postgresql.org/docs/9.3/static/release-9-2.html#AEN116689 + self.supports_smallserial = self.server_version_info >= (9, 2) + + self._backslash_escapes = self.server_version_info < (8, 2) or \ + connection.scalar( + "show standard_conforming_strings" + ) == 'off' + + self._supports_create_index_concurrently = \ + self.server_version_info >= (8, 2) + self._supports_drop_index_concurrently = \ + self.server_version_info >= (9, 2) + + def on_connect(self): + if self.isolation_level is not None: + def connect(conn): + self.set_isolation_level(conn, self.isolation_level) + return connect + else: + return None + + _isolation_lookup = set(['SERIALIZABLE', 'READ UNCOMMITTED', + 'READ COMMITTED', 'REPEATABLE READ']) + + def set_isolation_level(self, connection, level): + level = level.replace('_', ' ') + if level not in self._isolation_lookup: + raise exc.ArgumentError( + "Invalid value '%s' for isolation_level. " + "Valid isolation levels for %s are %s" % + (level, self.name, ", ".join(self._isolation_lookup)) + ) + cursor = connection.cursor() + cursor.execute( + "SET SESSION CHARACTERISTICS AS TRANSACTION " + "ISOLATION LEVEL %s" % level) + cursor.execute("COMMIT") + cursor.close() + + def get_isolation_level(self, connection): + cursor = connection.cursor() + cursor.execute('show transaction isolation level') + val = cursor.fetchone()[0] + cursor.close() + return val.upper() + + def do_begin_twophase(self, connection, xid): + self.do_begin(connection.connection) + + def do_prepare_twophase(self, connection, xid): + connection.execute("PREPARE TRANSACTION '%s'" % xid) + + def do_rollback_twophase(self, connection, xid, + is_prepared=True, recover=False): + if is_prepared: + if recover: + # FIXME: ugly hack to get out of transaction + # context when committing recoverable transactions + # Must find out a way how to make the dbapi not + # open a transaction. + connection.execute("ROLLBACK") + connection.execute("ROLLBACK PREPARED '%s'" % xid) + connection.execute("BEGIN") + self.do_rollback(connection.connection) + else: + self.do_rollback(connection.connection) + + def do_commit_twophase(self, connection, xid, + is_prepared=True, recover=False): + if is_prepared: + if recover: + connection.execute("ROLLBACK") + connection.execute("COMMIT PREPARED '%s'" % xid) + connection.execute("BEGIN") + self.do_rollback(connection.connection) + else: + self.do_commit(connection.connection) + + def do_recover_twophase(self, connection): + resultset = connection.execute( + sql.text("SELECT gid FROM pg_prepared_xacts")) + return [row[0] for row in resultset] + + def _get_default_schema_name(self, connection): + return connection.scalar("select current_schema()") + + def has_schema(self, connection, schema): + query = ("select nspname from pg_namespace " + "where lower(nspname)=:schema") + cursor = connection.execute( + sql.text( + query, + bindparams=[ + sql.bindparam( + 'schema', util.text_type(schema.lower()), + type_=sqltypes.Unicode)] + ) + ) + + return bool(cursor.first()) + + def has_table(self, connection, table_name, schema=None): + # seems like case gets folded in pg_class... + if schema is None: + cursor = connection.execute( + sql.text( + "select relname from pg_class c join pg_namespace n on " + "n.oid=c.relnamespace where " + "pg_catalog.pg_table_is_visible(c.oid) " + "and relname=:name", + bindparams=[ + sql.bindparam('name', util.text_type(table_name), + type_=sqltypes.Unicode)] + ) + ) + else: + cursor = connection.execute( + sql.text( + "select relname from pg_class c join pg_namespace n on " + "n.oid=c.relnamespace where n.nspname=:schema and " + "relname=:name", + bindparams=[ + sql.bindparam('name', + util.text_type(table_name), + type_=sqltypes.Unicode), + sql.bindparam('schema', + util.text_type(schema), + type_=sqltypes.Unicode)] + ) + ) + return bool(cursor.first()) + + def has_sequence(self, connection, sequence_name, schema=None): + if schema is None: + cursor = connection.execute( + sql.text( + "SELECT relname FROM pg_class c join pg_namespace n on " + "n.oid=c.relnamespace where relkind='S' and " + "n.nspname=current_schema() " + "and relname=:name", + bindparams=[ + sql.bindparam('name', util.text_type(sequence_name), + type_=sqltypes.Unicode) + ] + ) + ) + else: + cursor = connection.execute( + sql.text( + "SELECT relname FROM pg_class c join pg_namespace n on " + "n.oid=c.relnamespace where relkind='S' and " + "n.nspname=:schema and relname=:name", + bindparams=[ + sql.bindparam('name', util.text_type(sequence_name), + type_=sqltypes.Unicode), + sql.bindparam('schema', + util.text_type(schema), + type_=sqltypes.Unicode) + ] + ) + ) + + return bool(cursor.first()) + + def has_type(self, connection, type_name, schema=None): + if schema is not None: + query = """ + SELECT EXISTS ( + SELECT * FROM pg_catalog.pg_type t, pg_catalog.pg_namespace n + WHERE t.typnamespace = n.oid + AND t.typname = :typname + AND n.nspname = :nspname + ) + """ + query = sql.text(query) + else: + query = """ + SELECT EXISTS ( + SELECT * FROM pg_catalog.pg_type t + WHERE t.typname = :typname + AND pg_type_is_visible(t.oid) + ) + """ + query = sql.text(query) + query = query.bindparams( + sql.bindparam('typname', + util.text_type(type_name), type_=sqltypes.Unicode), + ) + if schema is not None: + query = query.bindparams( + sql.bindparam('nspname', + util.text_type(schema), type_=sqltypes.Unicode), + ) + cursor = connection.execute(query) + return bool(cursor.scalar()) + + def _get_server_version_info(self, connection): + v = connection.execute("select version()").scalar() + m = re.match( + r'.*(?:PostgreSQL|EnterpriseDB) ' + r'(\d+)\.?(\d+)?(?:\.(\d+))?(?:\.\d+)?(?:devel|beta)?', + v) + if not m: + raise AssertionError( + "Could not determine version from string '%s'" % v) + return tuple([int(x) for x in m.group(1, 2, 3) if x is not None]) + + @reflection.cache + def get_table_oid(self, connection, table_name, schema=None, **kw): + """Fetch the oid for schema.table_name. + + Several reflection methods require the table oid. The idea for using + this method is that it can be fetched one time and cached for + subsequent calls. + + """ + table_oid = None + if schema is not None: + schema_where_clause = "n.nspname = :schema" + else: + schema_where_clause = "pg_catalog.pg_table_is_visible(c.oid)" + query = """ + SELECT c.oid + FROM pg_catalog.pg_class c + LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace + WHERE (%s) + AND c.relname = :table_name AND c.relkind in ('r', 'v', 'm', 'f') + """ % schema_where_clause + # Since we're binding to unicode, table_name and schema_name must be + # unicode. + table_name = util.text_type(table_name) + if schema is not None: + schema = util.text_type(schema) + s = sql.text(query).bindparams(table_name=sqltypes.Unicode) + s = s.columns(oid=sqltypes.Integer) + if schema: + s = s.bindparams(sql.bindparam('schema', type_=sqltypes.Unicode)) + c = connection.execute(s, table_name=table_name, schema=schema) + table_oid = c.scalar() + if table_oid is None: + raise exc.NoSuchTableError(table_name) + return table_oid + + @reflection.cache + def get_schema_names(self, connection, **kw): + result = connection.execute( + sql.text("SELECT nspname FROM pg_namespace " + "WHERE nspname NOT LIKE 'pg_%' " + "ORDER BY nspname" + ).columns(nspname=sqltypes.Unicode)) + return [name for name, in result] + + @reflection.cache + def get_table_names(self, connection, schema=None, **kw): + result = connection.execute( + sql.text("SELECT c.relname FROM pg_class c " + "JOIN pg_namespace n ON n.oid = c.relnamespace " + "WHERE n.nspname = :schema AND c.relkind = 'r'" + ).columns(relname=sqltypes.Unicode), + schema=schema if schema is not None else self.default_schema_name) + return [name for name, in result] + + @reflection.cache + def _get_foreign_table_names(self, connection, schema=None, **kw): + result = connection.execute( + sql.text("SELECT c.relname FROM pg_class c " + "JOIN pg_namespace n ON n.oid = c.relnamespace " + "WHERE n.nspname = :schema AND c.relkind = 'f'" + ).columns(relname=sqltypes.Unicode), + schema=schema if schema is not None else self.default_schema_name) + return [name for name, in result] + + @reflection.cache + def get_view_names( + self, connection, schema=None, + include=('plain', 'materialized'), **kw): + + include_kind = {'plain': 'v', 'materialized': 'm'} + try: + kinds = [include_kind[i] for i in util.to_list(include)] + except KeyError: + raise ValueError( + "include %r unknown, needs to be a sequence containing " + "one or both of 'plain' and 'materialized'" % (include,)) + if not kinds: + raise ValueError( + "empty include, needs to be a sequence containing " + "one or both of 'plain' and 'materialized'") + + result = connection.execute( + sql.text("SELECT c.relname FROM pg_class c " + "JOIN pg_namespace n ON n.oid = c.relnamespace " + "WHERE n.nspname = :schema AND c.relkind IN (%s)" % + (", ".join("'%s'" % elem for elem in kinds)) + ).columns(relname=sqltypes.Unicode), + schema=schema if schema is not None else self.default_schema_name) + return [name for name, in result] + + @reflection.cache + def get_view_definition(self, connection, view_name, schema=None, **kw): + view_def = connection.scalar( + sql.text("SELECT pg_get_viewdef(c.oid) view_def FROM pg_class c " + "JOIN pg_namespace n ON n.oid = c.relnamespace " + "WHERE n.nspname = :schema AND c.relname = :view_name " + "AND c.relkind IN ('v', 'm')" + ).columns(view_def=sqltypes.Unicode), + schema=schema if schema is not None else self.default_schema_name, + view_name=view_name) + return view_def + + @reflection.cache + def get_columns(self, connection, table_name, schema=None, **kw): + + table_oid = self.get_table_oid(connection, table_name, schema, + info_cache=kw.get('info_cache')) + SQL_COLS = """ + SELECT a.attname, + pg_catalog.format_type(a.atttypid, a.atttypmod), + (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid) + FROM pg_catalog.pg_attrdef d + WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum + AND a.atthasdef) + AS DEFAULT, + a.attnotnull, a.attnum, a.attrelid as table_oid, + pgd.description as comment + FROM pg_catalog.pg_attribute a + LEFT JOIN pg_catalog.pg_description pgd ON ( + pgd.objoid = a.attrelid AND pgd.objsubid = a.attnum) + WHERE a.attrelid = :table_oid + AND a.attnum > 0 AND NOT a.attisdropped + ORDER BY a.attnum + """ + s = sql.text(SQL_COLS, + bindparams=[ + sql.bindparam('table_oid', type_=sqltypes.Integer)], + typemap={ + 'attname': sqltypes.Unicode, + 'default': sqltypes.Unicode} + ) + c = connection.execute(s, table_oid=table_oid) + rows = c.fetchall() + domains = self._load_domains(connection) + enums = dict( + ( + "%s.%s" % (rec['schema'], rec['name']) + if not rec['visible'] else rec['name'], rec) for rec in + self._load_enums(connection, schema='*') + ) + + # format columns + columns = [] + for name, format_type, default, notnull, attnum, table_oid, \ + comment in rows: + column_info = self._get_column_info( + name, format_type, default, notnull, domains, enums, + schema, comment) + columns.append(column_info) + return columns + + def _get_column_info(self, name, format_type, default, + notnull, domains, enums, schema, comment): + def _handle_array_type(attype): + return ( + attype.replace('[]', ''), # strip '[]' from integer[], etc. + attype.endswith('[]'), + ) + + # strip (*) from character varying(5), timestamp(5) + # with time zone, geometry(POLYGON), etc. + attype = re.sub(r'\(.*\)', '', format_type) + + # strip quotes from case sensitive enum names + attype = re.sub(r'^"|"$', '', attype) + + # strip '[]' from integer[], etc. and check if an array + attype, is_array = _handle_array_type(attype) + + nullable = not notnull + + charlen = re.search(r'\(([\d,]+)\)', format_type) + if charlen: + charlen = charlen.group(1) + args = re.search(r'\((.*)\)', format_type) + if args and args.group(1): + args = tuple(re.split(r'\s*,\s*', args.group(1))) + else: + args = () + kwargs = {} + + if attype == 'numeric': + if charlen: + prec, scale = charlen.split(',') + args = (int(prec), int(scale)) + else: + args = () + elif attype == 'double precision': + args = (53, ) + elif attype == 'integer': + args = () + elif attype in ('timestamp with time zone', + 'time with time zone'): + kwargs['timezone'] = True + if charlen: + kwargs['precision'] = int(charlen) + args = () + elif attype in ('timestamp without time zone', + 'time without time zone', 'time'): + kwargs['timezone'] = False + if charlen: + kwargs['precision'] = int(charlen) + args = () + elif attype == 'bit varying': + kwargs['varying'] = True + if charlen: + args = (int(charlen),) + else: + args = () + elif attype.startswith('interval'): + field_match = re.match(r'interval (.+)', attype, re.I) + if charlen: + kwargs['precision'] = int(charlen) + if field_match: + kwargs['fields'] = field_match.group(1) + attype = "interval" + args = () + elif charlen: + args = (int(charlen),) + + while True: + if attype in self.ischema_names: + coltype = self.ischema_names[attype] + break + elif attype in enums: + enum = enums[attype] + coltype = ENUM + kwargs['name'] = enum['name'] + if not enum['visible']: + kwargs['schema'] = enum['schema'] + args = tuple(enum['labels']) + break + elif attype in domains: + domain = domains[attype] + attype = domain['attype'] + attype, is_array = _handle_array_type(attype) + # A table can't override whether the domain is nullable. + nullable = domain['nullable'] + if domain['default'] and not default: + # It can, however, override the default + # value, but can't set it to null. + default = domain['default'] + continue + else: + coltype = None + break + + if coltype: + coltype = coltype(*args, **kwargs) + if is_array: + coltype = self.ischema_names['_array'](coltype) + else: + util.warn("Did not recognize type '%s' of column '%s'" % + (attype, name)) + coltype = sqltypes.NULLTYPE + # adjust the default value + autoincrement = False + if default is not None: + match = re.search(r"""(nextval\(')([^']+)('.*$)""", default) + if match is not None: + if issubclass(coltype._type_affinity, sqltypes.Integer): + autoincrement = True + # the default is related to a Sequence + sch = schema + if '.' not in match.group(2) and sch is not None: + # unconditionally quote the schema name. this could + # later be enhanced to obey quoting rules / + # "quote schema" + default = match.group(1) + \ + ('"%s"' % sch) + '.' + \ + match.group(2) + match.group(3) + + column_info = dict(name=name, type=coltype, nullable=nullable, + default=default, autoincrement=autoincrement, + comment=comment) + return column_info + + @reflection.cache + def get_pk_constraint(self, connection, table_name, schema=None, **kw): + table_oid = self.get_table_oid(connection, table_name, schema, + info_cache=kw.get('info_cache')) + + if self.server_version_info < (8, 4): + PK_SQL = """ + SELECT a.attname + FROM + pg_class t + join pg_index ix on t.oid = ix.indrelid + join pg_attribute a + on t.oid=a.attrelid AND %s + WHERE + t.oid = :table_oid and ix.indisprimary = 't' + ORDER BY a.attnum + """ % self._pg_index_any("a.attnum", "ix.indkey") + + else: + # unnest() and generate_subscripts() both introduced in + # version 8.4 + PK_SQL = """ + SELECT a.attname + FROM pg_attribute a JOIN ( + SELECT unnest(ix.indkey) attnum, + generate_subscripts(ix.indkey, 1) ord + FROM pg_index ix + WHERE ix.indrelid = :table_oid AND ix.indisprimary + ) k ON a.attnum=k.attnum + WHERE a.attrelid = :table_oid + ORDER BY k.ord + """ + t = sql.text(PK_SQL, typemap={'attname': sqltypes.Unicode}) + c = connection.execute(t, table_oid=table_oid) + cols = [r[0] for r in c.fetchall()] + + PK_CONS_SQL = """ + SELECT conname + FROM pg_catalog.pg_constraint r + WHERE r.conrelid = :table_oid AND r.contype = 'p' + ORDER BY 1 + """ + t = sql.text(PK_CONS_SQL, typemap={'conname': sqltypes.Unicode}) + c = connection.execute(t, table_oid=table_oid) + name = c.scalar() + + return {'constrained_columns': cols, 'name': name} + + @reflection.cache + def get_foreign_keys(self, connection, table_name, schema=None, + postgresql_ignore_search_path=False, **kw): + preparer = self.identifier_preparer + table_oid = self.get_table_oid(connection, table_name, schema, + info_cache=kw.get('info_cache')) + + FK_SQL = """ + SELECT r.conname, + pg_catalog.pg_get_constraintdef(r.oid, true) as condef, + n.nspname as conschema + FROM pg_catalog.pg_constraint r, + pg_namespace n, + pg_class c + + WHERE r.conrelid = :table AND + r.contype = 'f' AND + c.oid = confrelid AND + n.oid = c.relnamespace + ORDER BY 1 + """ + # http://www.postgresql.org/docs/9.0/static/sql-createtable.html + FK_REGEX = re.compile( + r'FOREIGN KEY \((.*?)\) REFERENCES (?:(.*?)\.)?(.*?)\((.*?)\)' + r'[\s]?(MATCH (FULL|PARTIAL|SIMPLE)+)?' + r'[\s]?(ON UPDATE ' + r'(CASCADE|RESTRICT|NO ACTION|SET NULL|SET DEFAULT)+)?' + r'[\s]?(ON DELETE ' + r'(CASCADE|RESTRICT|NO ACTION|SET NULL|SET DEFAULT)+)?' + r'[\s]?(DEFERRABLE|NOT DEFERRABLE)?' + r'[\s]?(INITIALLY (DEFERRED|IMMEDIATE)+)?' + ) + + t = sql.text(FK_SQL, typemap={ + 'conname': sqltypes.Unicode, + 'condef': sqltypes.Unicode}) + c = connection.execute(t, table=table_oid) + fkeys = [] + for conname, condef, conschema in c.fetchall(): + m = re.search(FK_REGEX, condef).groups() + + constrained_columns, referred_schema, \ + referred_table, referred_columns, \ + _, match, _, onupdate, _, ondelete, \ + deferrable, _, initially = m + + if deferrable is not None: + deferrable = True if deferrable == 'DEFERRABLE' else False + constrained_columns = [preparer._unquote_identifier(x) + for x in re.split( + r'\s*,\s*', constrained_columns)] + + if postgresql_ignore_search_path: + # when ignoring search path, we use the actual schema + # provided it isn't the "default" schema + if conschema != self.default_schema_name: + referred_schema = conschema + else: + referred_schema = schema + elif referred_schema: + # referred_schema is the schema that we regexp'ed from + # pg_get_constraintdef(). If the schema is in the search + # path, pg_get_constraintdef() will give us None. + referred_schema = \ + preparer._unquote_identifier(referred_schema) + elif schema is not None and schema == conschema: + # If the actual schema matches the schema of the table + # we're reflecting, then we will use that. + referred_schema = schema + + referred_table = preparer._unquote_identifier(referred_table) + referred_columns = [preparer._unquote_identifier(x) + for x in + re.split(r'\s*,\s', referred_columns)] + fkey_d = { + 'name': conname, + 'constrained_columns': constrained_columns, + 'referred_schema': referred_schema, + 'referred_table': referred_table, + 'referred_columns': referred_columns, + 'options': { + 'onupdate': onupdate, + 'ondelete': ondelete, + 'deferrable': deferrable, + 'initially': initially, + 'match': match + } + } + fkeys.append(fkey_d) + return fkeys + + def _pg_index_any(self, col, compare_to): + if self.server_version_info < (8, 1): + # http://www.postgresql.org/message-id/10279.1124395722@sss.pgh.pa.us + # "In CVS tip you could replace this with "attnum = ANY (indkey)". + # Unfortunately, most array support doesn't work on int2vector in + # pre-8.1 releases, so I think you're kinda stuck with the above + # for now. + # regards, tom lane" + return "(%s)" % " OR ".join( + "%s[%d] = %s" % (compare_to, ind, col) + for ind in range(0, 10) + ) + else: + return "%s = ANY(%s)" % (col, compare_to) + + @reflection.cache + def get_indexes(self, connection, table_name, schema, **kw): + table_oid = self.get_table_oid(connection, table_name, schema, + info_cache=kw.get('info_cache')) + + # cast indkey as varchar since it's an int2vector, + # returned as a list by some drivers such as pypostgresql + + if self.server_version_info < (8, 5): + IDX_SQL = """ + SELECT + i.relname as relname, + ix.indisunique, ix.indexprs, ix.indpred, + a.attname, a.attnum, NULL, ix.indkey%s, + %s, am.amname + FROM + pg_class t + join pg_index ix on t.oid = ix.indrelid + join pg_class i on i.oid = ix.indexrelid + left outer join + pg_attribute a + on t.oid = a.attrelid and %s + left outer join + pg_am am + on i.relam = am.oid + WHERE + t.relkind IN ('r', 'v', 'f', 'm') + and t.oid = :table_oid + and ix.indisprimary = 'f' + ORDER BY + t.relname, + i.relname + """ % ( + # version 8.3 here was based on observing the + # cast does not work in PG 8.2.4, does work in 8.3.0. + # nothing in PG changelogs regarding this. + "::varchar" if self.server_version_info >= (8, 3) else "", + "i.reloptions" if self.server_version_info >= (8, 2) + else "NULL", + self._pg_index_any("a.attnum", "ix.indkey") + ) + else: + IDX_SQL = """ + SELECT + i.relname as relname, + ix.indisunique, ix.indexprs, ix.indpred, + a.attname, a.attnum, c.conrelid, ix.indkey::varchar, + i.reloptions, am.amname + FROM + pg_class t + join pg_index ix on t.oid = ix.indrelid + join pg_class i on i.oid = ix.indexrelid + left outer join + pg_attribute a + on t.oid = a.attrelid and a.attnum = ANY(ix.indkey) + left outer join + pg_constraint c + on (ix.indrelid = c.conrelid and + ix.indexrelid = c.conindid and + c.contype in ('p', 'u', 'x')) + left outer join + pg_am am + on i.relam = am.oid + WHERE + t.relkind IN ('r', 'v', 'f', 'm') + and t.oid = :table_oid + and ix.indisprimary = 'f' + ORDER BY + t.relname, + i.relname + """ + + t = sql.text(IDX_SQL, typemap={ + 'relname': sqltypes.Unicode, + 'attname': sqltypes.Unicode}) + c = connection.execute(t, table_oid=table_oid) + + indexes = defaultdict(lambda: defaultdict(dict)) + + sv_idx_name = None + for row in c.fetchall(): + (idx_name, unique, expr, prd, col, + col_num, conrelid, idx_key, options, amname) = row + + if expr: + if idx_name != sv_idx_name: + util.warn( + "Skipped unsupported reflection of " + "expression-based index %s" + % idx_name) + sv_idx_name = idx_name + continue + + if prd and not idx_name == sv_idx_name: + util.warn( + "Predicate of partial index %s ignored during reflection" + % idx_name) + sv_idx_name = idx_name + + has_idx = idx_name in indexes + index = indexes[idx_name] + if col is not None: + index['cols'][col_num] = col + if not has_idx: + index['key'] = [int(k.strip()) for k in idx_key.split()] + index['unique'] = unique + if conrelid is not None: + index['duplicates_constraint'] = idx_name + if options: + index['options'] = dict( + [option.split("=") for option in options]) + + # it *might* be nice to include that this is 'btree' in the + # reflection info. But we don't want an Index object + # to have a ``postgresql_using`` in it that is just the + # default, so for the moment leaving this out. + if amname and amname != 'btree': + index['amname'] = amname + + result = [] + for name, idx in indexes.items(): + entry = { + 'name': name, + 'unique': idx['unique'], + 'column_names': [idx['cols'][i] for i in idx['key']] + } + if 'duplicates_constraint' in idx: + entry['duplicates_constraint'] = idx['duplicates_constraint'] + if 'options' in idx: + entry.setdefault( + 'dialect_options', {})["postgresql_with"] = idx['options'] + if 'amname' in idx: + entry.setdefault( + 'dialect_options', {})["postgresql_using"] = idx['amname'] + result.append(entry) + return result + + @reflection.cache + def get_unique_constraints(self, connection, table_name, + schema=None, **kw): + table_oid = self.get_table_oid(connection, table_name, schema, + info_cache=kw.get('info_cache')) + + UNIQUE_SQL = """ + SELECT + cons.conname as name, + cons.conkey as key, + a.attnum as col_num, + a.attname as col_name + FROM + pg_catalog.pg_constraint cons + join pg_attribute a + on cons.conrelid = a.attrelid AND + a.attnum = ANY(cons.conkey) + WHERE + cons.conrelid = :table_oid AND + cons.contype = 'u' + """ + + t = sql.text(UNIQUE_SQL, typemap={'col_name': sqltypes.Unicode}) + c = connection.execute(t, table_oid=table_oid) + + uniques = defaultdict(lambda: defaultdict(dict)) + for row in c.fetchall(): + uc = uniques[row.name] + uc["key"] = row.key + uc["cols"][row.col_num] = row.col_name + + return [ + {'name': name, + 'column_names': [uc["cols"][i] for i in uc["key"]]} + for name, uc in uniques.items() + ] + + @reflection.cache + def get_table_comment(self, connection, table_name, schema=None, **kw): + table_oid = self.get_table_oid(connection, table_name, schema, + info_cache=kw.get('info_cache')) + + COMMENT_SQL = """ + SELECT + pgd.description as table_comment + FROM + pg_catalog.pg_description pgd + WHERE + pgd.objsubid = 0 AND + pgd.objoid = :table_oid + """ + + c = connection.execute(sql.text(COMMENT_SQL), table_oid=table_oid) + return {"text": c.scalar()} + + @reflection.cache + def get_check_constraints( + self, connection, table_name, schema=None, **kw): + table_oid = self.get_table_oid(connection, table_name, schema, + info_cache=kw.get('info_cache')) + + CHECK_SQL = """ + SELECT + cons.conname as name, + cons.consrc as src + FROM + pg_catalog.pg_constraint cons + WHERE + cons.conrelid = :table_oid AND + cons.contype = 'c' + """ + + c = connection.execute(sql.text(CHECK_SQL), table_oid=table_oid) + + return [ + {'name': name, + 'sqltext': src[1:-1]} + for name, src in c.fetchall() + ] + + def _load_enums(self, connection, schema=None): + schema = schema or self.default_schema_name + if not self.supports_native_enum: + return {} + + # Load data types for enums: + SQL_ENUMS = """ + SELECT t.typname as "name", + -- no enum defaults in 8.4 at least + -- t.typdefault as "default", + pg_catalog.pg_type_is_visible(t.oid) as "visible", + n.nspname as "schema", + e.enumlabel as "label" + FROM pg_catalog.pg_type t + LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace + LEFT JOIN pg_catalog.pg_enum e ON t.oid = e.enumtypid + WHERE t.typtype = 'e' + """ + + if schema != '*': + SQL_ENUMS += "AND n.nspname = :schema " + + # e.oid gives us label order within an enum + SQL_ENUMS += 'ORDER BY "schema", "name", e.oid' + + s = sql.text(SQL_ENUMS, typemap={ + 'attname': sqltypes.Unicode, + 'label': sqltypes.Unicode}) + + if schema != '*': + s = s.bindparams(schema=schema) + + c = connection.execute(s) + + enums = [] + enum_by_name = {} + for enum in c.fetchall(): + key = (enum['schema'], enum['name']) + if key in enum_by_name: + enum_by_name[key]['labels'].append(enum['label']) + else: + enum_by_name[key] = enum_rec = { + 'name': enum['name'], + 'schema': enum['schema'], + 'visible': enum['visible'], + 'labels': [enum['label']], + } + enums.append(enum_rec) + return enums + + def _load_domains(self, connection): + # Load data types for domains: + SQL_DOMAINS = """ + SELECT t.typname as "name", + pg_catalog.format_type(t.typbasetype, t.typtypmod) as "attype", + not t.typnotnull as "nullable", + t.typdefault as "default", + pg_catalog.pg_type_is_visible(t.oid) as "visible", + n.nspname as "schema" + FROM pg_catalog.pg_type t + LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace + WHERE t.typtype = 'd' + """ + + s = sql.text(SQL_DOMAINS, typemap={'attname': sqltypes.Unicode}) + c = connection.execute(s) + + domains = {} + for domain in c.fetchall(): + # strip (30) from character varying(30) + attype = re.search(r'([^\(]+)', domain['attype']).group(1) + if domain['visible']: + # 'visible' just means whether or not the domain is in a + # schema that's on the search path -- or not overridden by + # a schema with higher precedence. If it's not visible, + # it will be prefixed with the schema-name when it's used. + name = domain['name'] + else: + name = "%s.%s" % (domain['schema'], domain['name']) + + domains[name] = { + 'attype': attype, + 'nullable': domain['nullable'], + 'default': domain['default'] + } + + return domains diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/dml.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/dml.py new file mode 100644 index 0000000..555a900 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/dml.py @@ -0,0 +1,214 @@ +# postgresql/on_conflict.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from ...sql.elements import ClauseElement, _literal_as_binds +from ...sql.dml import Insert as StandardInsert +from ...sql.expression import alias +from ...sql import schema +from ...util.langhelpers import public_factory +from ...sql.base import _generative +from ... import util +from . import ext + +__all__ = ('Insert', 'insert') + + +class Insert(StandardInsert): + """PostgreSQL-specific implementation of INSERT. + + Adds methods for PG-specific syntaxes such as ON CONFLICT. + + .. versionadded:: 1.1 + + """ + + @util.memoized_property + def excluded(self): + """Provide the ``excluded`` namespace for an ON CONFLICT statement + + PG's ON CONFLICT clause allows reference to the row that would + be inserted, known as ``excluded``. This attribute provides + all columns in this row to be referenceable. + + .. seealso:: + + :ref:`postgresql_insert_on_conflict` - example of how + to use :attr:`.Insert.excluded` + + """ + return alias(self.table, name='excluded').columns + + @_generative + def on_conflict_do_update( + self, + constraint=None, index_elements=None, + index_where=None, set_=None, where=None): + """ + Specifies a DO UPDATE SET action for ON CONFLICT clause. + + Either the ``constraint`` or ``index_elements`` argument is + required, but only one of these can be specified. + + :param constraint: + The name of a unique or exclusion constraint on the table, + or the constraint object itself if it has a .name attribute. + + :param index_elements: + A sequence consisting of string column names, :class:`.Column` + objects, or other column expression objects that will be used + to infer a target index. + + :param index_where: + Additional WHERE criterion that can be used to infer a + conditional target index. + + :param set_: + Required argument. A dictionary or other mapping object + with column names as keys and expressions or literals as values, + specifying the ``SET`` actions to take. + If the target :class:`.Column` specifies a ".key" attribute distinct + from the column name, that key should be used. + + .. warning:: This dictionary does **not** take into account + Python-specified default UPDATE values or generation functions, + e.g. those specified using :paramref:`.Column.onupdate`. + These values will not be exercised for an ON CONFLICT style of + UPDATE, unless they are manually specified in the + :paramref:`.Insert.on_conflict_do_update.set_` dictionary. + + :param where: + Optional argument. If present, can be a literal SQL + string or an acceptable expression for a ``WHERE`` clause + that restricts the rows affected by ``DO UPDATE SET``. Rows + not meeting the ``WHERE`` condition will not be updated + (effectively a ``DO NOTHING`` for those rows). + + .. versionadded:: 1.1 + + + .. seealso:: + + :ref:`postgresql_insert_on_conflict` + + """ + self._post_values_clause = OnConflictDoUpdate( + constraint, index_elements, index_where, set_, where) + return self + + @_generative + def on_conflict_do_nothing( + self, + constraint=None, index_elements=None, index_where=None): + """ + Specifies a DO NOTHING action for ON CONFLICT clause. + + The ``constraint`` and ``index_elements`` arguments + are optional, but only one of these can be specified. + + :param constraint: + The name of a unique or exclusion constraint on the table, + or the constraint object itself if it has a .name attribute. + + :param index_elements: + A sequence consisting of string column names, :class:`.Column` + objects, or other column expression objects that will be used + to infer a target index. + + :param index_where: + Additional WHERE criterion that can be used to infer a + conditional target index. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`postgresql_insert_on_conflict` + + """ + self._post_values_clause = OnConflictDoNothing( + constraint, index_elements, index_where) + return self + +insert = public_factory(Insert, '.dialects.postgresql.insert') + + +class OnConflictClause(ClauseElement): + def __init__( + self, + constraint=None, + index_elements=None, + index_where=None): + + if constraint is not None: + if not isinstance(constraint, util.string_types) and \ + isinstance(constraint, ( + schema.Index, schema.Constraint, + ext.ExcludeConstraint)): + constraint = getattr(constraint, 'name') or constraint + + if constraint is not None: + if index_elements is not None: + raise ValueError( + "'constraint' and 'index_elements' are mutually exclusive") + + if isinstance(constraint, util.string_types): + self.constraint_target = constraint + self.inferred_target_elements = None + self.inferred_target_whereclause = None + elif isinstance(constraint, schema.Index): + index_elements = constraint.expressions + index_where = \ + constraint.dialect_options['postgresql'].get("where") + elif isinstance(constraint, ext.ExcludeConstraint): + index_elements = constraint.columns + index_where = constraint.where + else: + index_elements = constraint.columns + index_where = \ + constraint.dialect_options['postgresql'].get("where") + + if index_elements is not None: + self.constraint_target = None + self.inferred_target_elements = index_elements + self.inferred_target_whereclause = index_where + elif constraint is None: + self.constraint_target = self.inferred_target_elements = \ + self.inferred_target_whereclause = None + + +class OnConflictDoNothing(OnConflictClause): + __visit_name__ = 'on_conflict_do_nothing' + + +class OnConflictDoUpdate(OnConflictClause): + __visit_name__ = 'on_conflict_do_update' + + def __init__( + self, + constraint=None, + index_elements=None, + index_where=None, + set_=None, + where=None): + super(OnConflictDoUpdate, self).__init__( + constraint=constraint, + index_elements=index_elements, + index_where=index_where) + + if self.inferred_target_elements is None and \ + self.constraint_target is None: + raise ValueError( + "Either constraint or index_elements, " + "but not both, must be specified unless DO NOTHING") + + if (not isinstance(set_, dict) or not set_): + raise ValueError("set parameter must be a non-empty dictionary") + self.update_values_to_set = [ + (key, value) + for key, value in set_.items() + ] + self.update_whereclause = where diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/ext.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/ext.py new file mode 100644 index 0000000..a588eaf --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/ext.py @@ -0,0 +1,230 @@ +# postgresql/ext.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from ...sql import expression +from ...sql import elements +from ...sql import functions +from ...sql.schema import ColumnCollectionConstraint +from .array import ARRAY + + +class aggregate_order_by(expression.ColumnElement): + """Represent a PostgreSQL aggregate order by expression. + + E.g.:: + + from sqlalchemy.dialects.postgresql import aggregate_order_by + expr = func.array_agg(aggregate_order_by(table.c.a, table.c.b.desc())) + stmt = select([expr]) + + would represent the expression:: + + SELECT array_agg(a ORDER BY b DESC) FROM table; + + Similarly:: + + expr = func.string_agg( + table.c.a, + aggregate_order_by(literal_column("','"), table.c.a) + ) + stmt = select([expr]) + + Would represent:: + + SELECT string_agg(a, ',' ORDER BY a) FROM table; + + .. versionadded:: 1.1 + + .. versionchanged:: 1.2.13 - the ORDER BY argument may be multiple terms + + .. seealso:: + + :class:`.array_agg` + + """ + + __visit_name__ = 'aggregate_order_by' + + def __init__(self, target, *order_by): + self.target = elements._literal_as_binds(target) + + _lob = len(order_by) + if _lob == 0: + raise TypeError("at least one ORDER BY element is required") + elif _lob == 1: + self.order_by = elements._literal_as_binds(order_by[0]) + else: + self.order_by = elements.ClauseList( + *order_by, + _literal_as_text=elements._literal_as_binds) + + def self_group(self, against=None): + return self + + def get_children(self, **kwargs): + return self.target, self.order_by + + def _copy_internals(self, clone=elements._clone, **kw): + self.target = clone(self.target, **kw) + self.order_by = clone(self.order_by, **kw) + + @property + def _from_objects(self): + return self.target._from_objects + self.order_by._from_objects + + +class ExcludeConstraint(ColumnCollectionConstraint): + """A table-level EXCLUDE constraint. + + Defines an EXCLUDE constraint as described in the `postgres + documentation`__. + + __ http://www.postgresql.org/docs/9.0/\ +static/sql-createtable.html#SQL-CREATETABLE-EXCLUDE + """ + + __visit_name__ = 'exclude_constraint' + + where = None + + def __init__(self, *elements, **kw): + r""" + Create an :class:`.ExcludeConstraint` object. + + E.g.:: + + const = ExcludeConstraint( + (Column('period'), '&&'), + (Column('group'), '='), + where=(Column('group') != 'some group') + ) + + The constraint is normally embedded into the :class:`.Table` construct + directly, or added later using :meth:`.append_constraint`:: + + some_table = Table( + 'some_table', metadata, + Column('id', Integer, primary_key=True), + Column('period', TSRANGE()), + Column('group', String) + ) + + some_table.append_constraint( + ExcludeConstraint( + (some_table.c.period, '&&'), + (some_table.c.group, '='), + where=some_table.c.group != 'some group', + name='some_table_excl_const' + ) + ) + + :param \*elements: + A sequence of two tuples of the form ``(column, operator)`` where + "column" is a SQL expression element or a raw SQL string, most + typically a :class:`.Column` object, + and "operator" is a string containing the operator to use. + + .. note:: + + A plain string passed for the value of "column" is interpreted + as an arbitrary SQL expression; when passing a plain string, + any necessary quoting and escaping syntaxes must be applied + manually. In order to specify a column name when a + :class:`.Column` object is not available, while ensuring that + any necessary quoting rules take effect, an ad-hoc + :class:`.Column` or :func:`.sql.expression.column` object may + be used. + + :param name: + Optional, the in-database name of this constraint. + + :param deferrable: + Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when + issuing DDL for this constraint. + + :param initially: + Optional string. If set, emit INITIALLY <value> when issuing DDL + for this constraint. + + :param using: + Optional string. If set, emit USING <index_method> when issuing DDL + for this constraint. Defaults to 'gist'. + + :param where: + Optional SQL expression construct or literal SQL string. + If set, emit WHERE <predicate> when issuing DDL + for this constraint. + + .. note:: + + A plain string passed here is interpreted as an arbitrary SQL + expression; when passing a plain string, any necessary quoting + and escaping syntaxes must be applied manually. + + """ + columns = [] + render_exprs = [] + self.operators = {} + + expressions, operators = zip(*elements) + + for (expr, column, strname, add_element), operator in zip( + self._extract_col_expression_collection(expressions), + operators + ): + if add_element is not None: + columns.append(add_element) + + name = column.name if column is not None else strname + + if name is not None: + # backwards compat + self.operators[name] = operator + + expr = expression._literal_as_text(expr) + + render_exprs.append( + (expr, name, operator) + ) + + self._render_exprs = render_exprs + ColumnCollectionConstraint.__init__( + self, + *columns, + name=kw.get('name'), + deferrable=kw.get('deferrable'), + initially=kw.get('initially') + ) + self.using = kw.get('using', 'gist') + where = kw.get('where') + if where is not None: + self.where = expression._literal_as_text(where) + + def copy(self, **kw): + elements = [(col, self.operators[col]) + for col in self.columns.keys()] + c = self.__class__(*elements, + name=self.name, + deferrable=self.deferrable, + initially=self.initially, + where=self.where, + using=self.using) + c.dispatch._update(self.dispatch) + return c + + +def array_agg(*arg, **kw): + """PostgreSQL-specific form of :class:`.array_agg`, ensures + return type is :class:`.postgresql.ARRAY` and not + the plain :class:`.types.ARRAY`, unless an explicit ``type_`` + is passed. + + .. versionadded:: 1.1 + + """ + kw['_default_array_type'] = ARRAY + return functions.func.array_agg(*arg, **kw) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/hstore.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/hstore.py new file mode 100644 index 0000000..b6c9e71 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/hstore.py @@ -0,0 +1,420 @@ +# postgresql/hstore.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +import re + +from .base import ischema_names +from .array import ARRAY +from ... import types as sqltypes +from ...sql import functions as sqlfunc +from ...sql import operators +from ... import util + +__all__ = ('HSTORE', 'hstore') + +idx_precedence = operators._PRECEDENCE[operators.json_getitem_op] + +GETITEM = operators.custom_op( + "->", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + +HAS_KEY = operators.custom_op( + "?", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + +HAS_ALL = operators.custom_op( + "?&", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + +HAS_ANY = operators.custom_op( + "?|", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + +CONTAINS = operators.custom_op( + "@>", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + +CONTAINED_BY = operators.custom_op( + "<@", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + + +class HSTORE(sqltypes.Indexable, sqltypes.Concatenable, sqltypes.TypeEngine): + """Represent the PostgreSQL HSTORE type. + + The :class:`.HSTORE` type stores dictionaries containing strings, e.g.:: + + data_table = Table('data_table', metadata, + Column('id', Integer, primary_key=True), + Column('data', HSTORE) + ) + + with engine.connect() as conn: + conn.execute( + data_table.insert(), + data = {"key1": "value1", "key2": "value2"} + ) + + :class:`.HSTORE` provides for a wide range of operations, including: + + * Index operations:: + + data_table.c.data['some key'] == 'some value' + + * Containment operations:: + + data_table.c.data.has_key('some key') + + data_table.c.data.has_all(['one', 'two', 'three']) + + * Concatenation:: + + data_table.c.data + {"k1": "v1"} + + For a full list of special methods see + :class:`.HSTORE.comparator_factory`. + + For usage with the SQLAlchemy ORM, it may be desirable to combine + the usage of :class:`.HSTORE` with :class:`.MutableDict` dictionary + now part of the :mod:`sqlalchemy.ext.mutable` + extension. This extension will allow "in-place" changes to the + dictionary, e.g. addition of new keys or replacement/removal of existing + keys to/from the current dictionary, to produce events which will be + detected by the unit of work:: + + from sqlalchemy.ext.mutable import MutableDict + + class MyClass(Base): + __tablename__ = 'data_table' + + id = Column(Integer, primary_key=True) + data = Column(MutableDict.as_mutable(HSTORE)) + + my_object = session.query(MyClass).one() + + # in-place mutation, requires Mutable extension + # in order for the ORM to detect + my_object.data['some_key'] = 'some value' + + session.commit() + + When the :mod:`sqlalchemy.ext.mutable` extension is not used, the ORM + will not be alerted to any changes to the contents of an existing + dictionary, unless that dictionary value is re-assigned to the + HSTORE-attribute itself, thus generating a change event. + + .. versionadded:: 0.8 + + .. seealso:: + + :class:`.hstore` - render the PostgreSQL ``hstore()`` function. + + + """ + + __visit_name__ = 'HSTORE' + hashable = False + text_type = sqltypes.Text() + + def __init__(self, text_type=None): + """Construct a new :class:`.HSTORE`. + + :param text_type: the type that should be used for indexed values. + Defaults to :class:`.types.Text`. + + .. versionadded:: 1.1.0 + + """ + if text_type is not None: + self.text_type = text_type + + class Comparator( + sqltypes.Indexable.Comparator, sqltypes.Concatenable.Comparator): + """Define comparison operations for :class:`.HSTORE`.""" + + def has_key(self, other): + """Boolean expression. Test for presence of a key. Note that the + key may be a SQLA expression. + """ + return self.operate(HAS_KEY, other, result_type=sqltypes.Boolean) + + def has_all(self, other): + """Boolean expression. Test for presence of all keys in jsonb + """ + return self.operate(HAS_ALL, other, result_type=sqltypes.Boolean) + + def has_any(self, other): + """Boolean expression. Test for presence of any key in jsonb + """ + return self.operate(HAS_ANY, other, result_type=sqltypes.Boolean) + + def contains(self, other, **kwargs): + """Boolean expression. Test if keys (or array) are a superset + of/contained the keys of the argument jsonb expression. + """ + return self.operate(CONTAINS, other, result_type=sqltypes.Boolean) + + def contained_by(self, other): + """Boolean expression. Test if keys are a proper subset of the + keys of the argument jsonb expression. + """ + return self.operate( + CONTAINED_BY, other, result_type=sqltypes.Boolean) + + def _setup_getitem(self, index): + return GETITEM, index, self.type.text_type + + def defined(self, key): + """Boolean expression. Test for presence of a non-NULL value for + the key. Note that the key may be a SQLA expression. + """ + return _HStoreDefinedFunction(self.expr, key) + + def delete(self, key): + """HStore expression. Returns the contents of this hstore with the + given key deleted. Note that the key may be a SQLA expression. + """ + if isinstance(key, dict): + key = _serialize_hstore(key) + return _HStoreDeleteFunction(self.expr, key) + + def slice(self, array): + """HStore expression. Returns a subset of an hstore defined by + array of keys. + """ + return _HStoreSliceFunction(self.expr, array) + + def keys(self): + """Text array expression. Returns array of keys.""" + return _HStoreKeysFunction(self.expr) + + def vals(self): + """Text array expression. Returns array of values.""" + return _HStoreValsFunction(self.expr) + + def array(self): + """Text array expression. Returns array of alternating keys and + values. + """ + return _HStoreArrayFunction(self.expr) + + def matrix(self): + """Text array expression. Returns array of [key, value] pairs.""" + return _HStoreMatrixFunction(self.expr) + + comparator_factory = Comparator + + def bind_processor(self, dialect): + if util.py2k: + encoding = dialect.encoding + + def process(value): + if isinstance(value, dict): + return _serialize_hstore(value).encode(encoding) + else: + return value + else: + def process(value): + if isinstance(value, dict): + return _serialize_hstore(value) + else: + return value + return process + + def result_processor(self, dialect, coltype): + if util.py2k: + encoding = dialect.encoding + + def process(value): + if value is not None: + return _parse_hstore(value.decode(encoding)) + else: + return value + else: + def process(value): + if value is not None: + return _parse_hstore(value) + else: + return value + return process + + +ischema_names['hstore'] = HSTORE + + +class hstore(sqlfunc.GenericFunction): + """Construct an hstore value within a SQL expression using the + PostgreSQL ``hstore()`` function. + + The :class:`.hstore` function accepts one or two arguments as described + in the PostgreSQL documentation. + + E.g.:: + + from sqlalchemy.dialects.postgresql import array, hstore + + select([hstore('key1', 'value1')]) + + select([ + hstore( + array(['key1', 'key2', 'key3']), + array(['value1', 'value2', 'value3']) + ) + ]) + + .. versionadded:: 0.8 + + .. seealso:: + + :class:`.HSTORE` - the PostgreSQL ``HSTORE`` datatype. + + """ + type = HSTORE + name = 'hstore' + + +class _HStoreDefinedFunction(sqlfunc.GenericFunction): + type = sqltypes.Boolean + name = 'defined' + + +class _HStoreDeleteFunction(sqlfunc.GenericFunction): + type = HSTORE + name = 'delete' + + +class _HStoreSliceFunction(sqlfunc.GenericFunction): + type = HSTORE + name = 'slice' + + +class _HStoreKeysFunction(sqlfunc.GenericFunction): + type = ARRAY(sqltypes.Text) + name = 'akeys' + + +class _HStoreValsFunction(sqlfunc.GenericFunction): + type = ARRAY(sqltypes.Text) + name = 'avals' + + +class _HStoreArrayFunction(sqlfunc.GenericFunction): + type = ARRAY(sqltypes.Text) + name = 'hstore_to_array' + + +class _HStoreMatrixFunction(sqlfunc.GenericFunction): + type = ARRAY(sqltypes.Text) + name = 'hstore_to_matrix' + + +# +# parsing. note that none of this is used with the psycopg2 backend, +# which provides its own native extensions. +# + +# My best guess at the parsing rules of hstore literals, since no formal +# grammar is given. This is mostly reverse engineered from PG's input parser +# behavior. +HSTORE_PAIR_RE = re.compile(r""" +( + "(?P<key> (\\ . | [^"])* )" # Quoted key +) +[ ]* => [ ]* # Pair operator, optional adjoining whitespace +( + (?P<value_null> NULL ) # NULL value + | "(?P<value> (\\ . | [^"])* )" # Quoted value +) +""", re.VERBOSE) + +HSTORE_DELIMITER_RE = re.compile(r""" +[ ]* , [ ]* +""", re.VERBOSE) + + +def _parse_error(hstore_str, pos): + """format an unmarshalling error.""" + + ctx = 20 + hslen = len(hstore_str) + + parsed_tail = hstore_str[max(pos - ctx - 1, 0):min(pos, hslen)] + residual = hstore_str[min(pos, hslen):min(pos + ctx + 1, hslen)] + + if len(parsed_tail) > ctx: + parsed_tail = '[...]' + parsed_tail[1:] + if len(residual) > ctx: + residual = residual[:-1] + '[...]' + + return "After %r, could not parse residual at position %d: %r" % ( + parsed_tail, pos, residual) + + +def _parse_hstore(hstore_str): + """Parse an hstore from its literal string representation. + + Attempts to approximate PG's hstore input parsing rules as closely as + possible. Although currently this is not strictly necessary, since the + current implementation of hstore's output syntax is stricter than what it + accepts as input, the documentation makes no guarantees that will always + be the case. + + + + """ + result = {} + pos = 0 + pair_match = HSTORE_PAIR_RE.match(hstore_str) + + while pair_match is not None: + key = pair_match.group('key').replace(r'\"', '"').replace( + "\\\\", "\\") + if pair_match.group('value_null'): + value = None + else: + value = pair_match.group('value').replace( + r'\"', '"').replace("\\\\", "\\") + result[key] = value + + pos += pair_match.end() + + delim_match = HSTORE_DELIMITER_RE.match(hstore_str[pos:]) + if delim_match is not None: + pos += delim_match.end() + + pair_match = HSTORE_PAIR_RE.match(hstore_str[pos:]) + + if pos != len(hstore_str): + raise ValueError(_parse_error(hstore_str, pos)) + + return result + + +def _serialize_hstore(val): + """Serialize a dictionary into an hstore literal. Keys and values must + both be strings (except None for values). + + """ + def esc(s, position): + if position == 'value' and s is None: + return 'NULL' + elif isinstance(s, util.string_types): + return '"%s"' % s.replace("\\", "\\\\").replace('"', r'\"') + else: + raise ValueError("%r in %s position is not a string." % + (s, position)) + + return ', '.join('%s=>%s' % (esc(k, 'key'), esc(v, 'value')) + for k, v in val.items()) + + diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/json.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/json.py new file mode 100644 index 0000000..1a1367f --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/json.py @@ -0,0 +1,300 @@ +# postgresql/json.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +from __future__ import absolute_import + +import json + +from .base import ischema_names, colspecs +from ... import types as sqltypes +from ...sql import operators +from ...sql import elements +from ... import util + +__all__ = ('JSON', 'JSONB') + +idx_precedence = operators._PRECEDENCE[operators.json_getitem_op] + +ASTEXT = operators.custom_op( + "->>", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + +JSONPATH_ASTEXT = operators.custom_op( + "#>>", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + + +HAS_KEY = operators.custom_op( + "?", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + +HAS_ALL = operators.custom_op( + "?&", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + +HAS_ANY = operators.custom_op( + "?|", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + +CONTAINS = operators.custom_op( + "@>", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + +CONTAINED_BY = operators.custom_op( + "<@", precedence=idx_precedence, natural_self_precedent=True, + eager_grouping=True +) + + +class JSONPathType(sqltypes.JSON.JSONPathType): + def bind_processor(self, dialect): + super_proc = self.string_bind_processor(dialect) + + def process(value): + assert isinstance(value, util.collections_abc.Sequence) + tokens = [util.text_type(elem)for elem in value] + value = "{%s}" % (", ".join(tokens)) + if super_proc: + value = super_proc(value) + return value + + return process + + def literal_processor(self, dialect): + super_proc = self.string_literal_processor(dialect) + + def process(value): + assert isinstance(value, util.collections_abc.Sequence) + tokens = [util.text_type(elem)for elem in value] + value = "{%s}" % (", ".join(tokens)) + if super_proc: + value = super_proc(value) + return value + + return process + +colspecs[sqltypes.JSON.JSONPathType] = JSONPathType + + +class JSON(sqltypes.JSON): + """Represent the PostgreSQL JSON type. + + This type is a specialization of the Core-level :class:`.types.JSON` + type. Be sure to read the documentation for :class:`.types.JSON` for + important tips regarding treatment of NULL values and ORM use. + + .. versionchanged:: 1.1 :class:`.postgresql.JSON` is now a PostgreSQL- + specific specialization of the new :class:`.types.JSON` type. + + The operators provided by the PostgreSQL version of :class:`.JSON` + include: + + * Index operations (the ``->`` operator):: + + data_table.c.data['some key'] + + data_table.c.data[5] + + + * Index operations returning text (the ``->>`` operator):: + + data_table.c.data['some key'].astext == 'some value' + + * Index operations with CAST + (equivalent to ``CAST(col ->> ['some key'] AS <type>)``):: + + data_table.c.data['some key'].astext.cast(Integer) == 5 + + * Path index operations (the ``#>`` operator):: + + data_table.c.data[('key_1', 'key_2', 5, ..., 'key_n')] + + * Path index operations returning text (the ``#>>`` operator):: + + data_table.c.data[('key_1', 'key_2', 5, ..., 'key_n')].astext == \ +'some value' + + .. versionchanged:: 1.1 The :meth:`.ColumnElement.cast` operator on + JSON objects now requires that the :attr:`.JSON.Comparator.astext` + modifier be called explicitly, if the cast works only from a textual + string. + + Index operations return an expression object whose type defaults to + :class:`.JSON` by default, so that further JSON-oriented instructions + may be called upon the result type. + + Custom serializers and deserializers are specified at the dialect level, + that is using :func:`.create_engine`. The reason for this is that when + using psycopg2, the DBAPI only allows serializers at the per-cursor + or per-connection level. E.g.:: + + engine = create_engine("postgresql://scott:tiger@localhost/test", + json_serializer=my_serialize_fn, + json_deserializer=my_deserialize_fn + ) + + When using the psycopg2 dialect, the json_deserializer is registered + against the database using ``psycopg2.extras.register_default_json``. + + .. seealso:: + + :class:`.types.JSON` - Core level JSON type + + :class:`.JSONB` + + """ + + astext_type = sqltypes.Text() + + def __init__(self, none_as_null=False, astext_type=None): + """Construct a :class:`.JSON` type. + + :param none_as_null: if True, persist the value ``None`` as a + SQL NULL value, not the JSON encoding of ``null``. Note that + when this flag is False, the :func:`.null` construct can still + be used to persist a NULL value:: + + from sqlalchemy import null + conn.execute(table.insert(), data=null()) + + .. versionchanged:: 0.9.8 - Added ``none_as_null``, and :func:`.null` + is now supported in order to persist a NULL value. + + .. seealso:: + + :attr:`.JSON.NULL` + + :param astext_type: the type to use for the + :attr:`.JSON.Comparator.astext` + accessor on indexed attributes. Defaults to :class:`.types.Text`. + + .. versionadded:: 1.1 + + """ + super(JSON, self).__init__(none_as_null=none_as_null) + if astext_type is not None: + self.astext_type = astext_type + + class Comparator(sqltypes.JSON.Comparator): + """Define comparison operations for :class:`.JSON`.""" + + @property + def astext(self): + """On an indexed expression, use the "astext" (e.g. "->>") + conversion when rendered in SQL. + + E.g.:: + + select([data_table.c.data['some key'].astext]) + + .. seealso:: + + :meth:`.ColumnElement.cast` + + """ + + if isinstance(self.expr.right.type, sqltypes.JSON.JSONPathType): + return self.expr.left.operate( + JSONPATH_ASTEXT, + self.expr.right, result_type=self.type.astext_type) + else: + return self.expr.left.operate( + ASTEXT, self.expr.right, result_type=self.type.astext_type) + + comparator_factory = Comparator + + +colspecs[sqltypes.JSON] = JSON +ischema_names['json'] = JSON + + +class JSONB(JSON): + """Represent the PostgreSQL JSONB type. + + The :class:`.JSONB` type stores arbitrary JSONB format data, e.g.:: + + data_table = Table('data_table', metadata, + Column('id', Integer, primary_key=True), + Column('data', JSONB) + ) + + with engine.connect() as conn: + conn.execute( + data_table.insert(), + data = {"key1": "value1", "key2": "value2"} + ) + + The :class:`.JSONB` type includes all operations provided by + :class:`.JSON`, including the same behaviors for indexing operations. + It also adds additional operators specific to JSONB, including + :meth:`.JSONB.Comparator.has_key`, :meth:`.JSONB.Comparator.has_all`, + :meth:`.JSONB.Comparator.has_any`, :meth:`.JSONB.Comparator.contains`, + and :meth:`.JSONB.Comparator.contained_by`. + + Like the :class:`.JSON` type, the :class:`.JSONB` type does not detect + in-place changes when used with the ORM, unless the + :mod:`sqlalchemy.ext.mutable` extension is used. + + Custom serializers and deserializers + are shared with the :class:`.JSON` class, using the ``json_serializer`` + and ``json_deserializer`` keyword arguments. These must be specified + at the dialect level using :func:`.create_engine`. When using + psycopg2, the serializers are associated with the jsonb type using + ``psycopg2.extras.register_default_jsonb`` on a per-connection basis, + in the same way that ``psycopg2.extras.register_default_json`` is used + to register these handlers with the json type. + + .. versionadded:: 0.9.7 + + .. seealso:: + + :class:`.JSON` + + """ + + __visit_name__ = 'JSONB' + + class Comparator(JSON.Comparator): + """Define comparison operations for :class:`.JSON`.""" + + def has_key(self, other): + """Boolean expression. Test for presence of a key. Note that the + key may be a SQLA expression. + """ + return self.operate(HAS_KEY, other, result_type=sqltypes.Boolean) + + def has_all(self, other): + """Boolean expression. Test for presence of all keys in jsonb + """ + return self.operate(HAS_ALL, other, result_type=sqltypes.Boolean) + + def has_any(self, other): + """Boolean expression. Test for presence of any key in jsonb + """ + return self.operate(HAS_ANY, other, result_type=sqltypes.Boolean) + + def contains(self, other, **kwargs): + """Boolean expression. Test if keys (or array) are a superset + of/contained the keys of the argument jsonb expression. + """ + return self.operate(CONTAINS, other, result_type=sqltypes.Boolean) + + def contained_by(self, other): + """Boolean expression. Test if keys are a proper subset of the + keys of the argument jsonb expression. + """ + return self.operate( + CONTAINED_BY, other, result_type=sqltypes.Boolean) + + comparator_factory = Comparator + +ischema_names['jsonb'] = JSONB diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pg8000.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pg8000.py new file mode 100644 index 0000000..80929b8 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pg8000.py @@ -0,0 +1,295 @@ +# postgresql/pg8000.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors <see AUTHORS +# file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: postgresql+pg8000 + :name: pg8000 + :dbapi: pg8000 + :connectstring: \ +postgresql+pg8000://user:password@host:port/dbname[?key=value&key=value...] + :url: https://pythonhosted.org/pg8000/ + + +.. _pg8000_unicode: + +Unicode +------- + +pg8000 will encode / decode string values between it and the server using the +PostgreSQL ``client_encoding`` parameter; by default this is the value in +the ``postgresql.conf`` file, which often defaults to ``SQL_ASCII``. +Typically, this can be changed to ``utf-8``, as a more useful default:: + + #client_encoding = sql_ascii # actually, defaults to database + # encoding + client_encoding = utf8 + +The ``client_encoding`` can be overridden for a session by executing the SQL: + +SET CLIENT_ENCODING TO 'utf8'; + +SQLAlchemy will execute this SQL on all new connections based on the value +passed to :func:`.create_engine` using the ``client_encoding`` parameter:: + + engine = create_engine( + "postgresql+pg8000://user:pass@host/dbname", client_encoding='utf8') + + +.. _pg8000_isolation_level: + +pg8000 Transaction Isolation Level +------------------------------------- + +The pg8000 dialect offers the same isolation level settings as that +of the :ref:`psycopg2 <psycopg2_isolation_level>` dialect: + +* ``READ COMMITTED`` +* ``READ UNCOMMITTED`` +* ``REPEATABLE READ`` +* ``SERIALIZABLE`` +* ``AUTOCOMMIT`` + +.. versionadded:: 0.9.5 support for AUTOCOMMIT isolation level when using + pg8000. + +.. seealso:: + + :ref:`postgresql_isolation_level` + + :ref:`psycopg2_isolation_level` + + +""" +from ... import util, exc +import decimal +from ... import processors +from ... import types as sqltypes +from .base import ( + PGDialect, PGCompiler, PGIdentifierPreparer, PGExecutionContext, + _DECIMAL_TYPES, _FLOAT_TYPES, _INT_TYPES, UUID) +import re +from sqlalchemy.dialects.postgresql.json import JSON +from ...sql.elements import quoted_name + +try: + from uuid import UUID as _python_UUID +except ImportError: + _python_UUID = None + + +class _PGNumeric(sqltypes.Numeric): + def result_processor(self, dialect, coltype): + if self.asdecimal: + if coltype in _FLOAT_TYPES: + return processors.to_decimal_processor_factory( + decimal.Decimal, self._effective_decimal_return_scale) + elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES: + # pg8000 returns Decimal natively for 1700 + return None + else: + raise exc.InvalidRequestError( + "Unknown PG numeric type: %d" % coltype) + else: + if coltype in _FLOAT_TYPES: + # pg8000 returns float natively for 701 + return None + elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES: + return processors.to_float + else: + raise exc.InvalidRequestError( + "Unknown PG numeric type: %d" % coltype) + + +class _PGNumericNoBind(_PGNumeric): + def bind_processor(self, dialect): + return None + + +class _PGJSON(JSON): + + def result_processor(self, dialect, coltype): + if dialect._dbapi_version > (1, 10, 1): + return None # Has native JSON + else: + return super(_PGJSON, self).result_processor(dialect, coltype) + + +class _PGUUID(UUID): + def bind_processor(self, dialect): + if not self.as_uuid: + def process(value): + if value is not None: + value = _python_UUID(value) + return value + return process + + def result_processor(self, dialect, coltype): + if not self.as_uuid: + def process(value): + if value is not None: + value = str(value) + return value + return process + + +class PGExecutionContext_pg8000(PGExecutionContext): + pass + + +class PGCompiler_pg8000(PGCompiler): + def visit_mod_binary(self, binary, operator, **kw): + return self.process(binary.left, **kw) + " %% " + \ + self.process(binary.right, **kw) + + def post_process_text(self, text): + if '%%' in text: + util.warn("The SQLAlchemy postgresql dialect " + "now automatically escapes '%' in text() " + "expressions to '%%'.") + return text.replace('%', '%%') + + +class PGIdentifierPreparer_pg8000(PGIdentifierPreparer): + def _escape_identifier(self, value): + value = value.replace(self.escape_quote, self.escape_to_quote) + return value.replace('%', '%%') + + +class PGDialect_pg8000(PGDialect): + driver = 'pg8000' + + supports_unicode_statements = True + + supports_unicode_binds = True + + default_paramstyle = 'format' + supports_sane_multi_rowcount = True + execution_ctx_cls = PGExecutionContext_pg8000 + statement_compiler = PGCompiler_pg8000 + preparer = PGIdentifierPreparer_pg8000 + description_encoding = 'use_encoding' + + colspecs = util.update_copy( + PGDialect.colspecs, + { + sqltypes.Numeric: _PGNumericNoBind, + sqltypes.Float: _PGNumeric, + JSON: _PGJSON, + sqltypes.JSON: _PGJSON, + UUID: _PGUUID + } + ) + + def __init__(self, client_encoding=None, **kwargs): + PGDialect.__init__(self, **kwargs) + self.client_encoding = client_encoding + + def initialize(self, connection): + self.supports_sane_multi_rowcount = self._dbapi_version >= (1, 9, 14) + super(PGDialect_pg8000, self).initialize(connection) + + @util.memoized_property + def _dbapi_version(self): + if self.dbapi and hasattr(self.dbapi, '__version__'): + return tuple( + [ + int(x) for x in re.findall( + r'(\d+)(?:[-\.]?|$)', self.dbapi.__version__)]) + else: + return (99, 99, 99) + + @classmethod + def dbapi(cls): + return __import__('pg8000') + + def create_connect_args(self, url): + opts = url.translate_connect_args(username='user') + if 'port' in opts: + opts['port'] = int(opts['port']) + opts.update(url.query) + return ([], opts) + + def is_disconnect(self, e, connection, cursor): + return "connection is closed" in str(e) + + def set_isolation_level(self, connection, level): + level = level.replace('_', ' ') + + # adjust for ConnectionFairy possibly being present + if hasattr(connection, 'connection'): + connection = connection.connection + + if level == 'AUTOCOMMIT': + connection.autocommit = True + elif level in self._isolation_lookup: + connection.autocommit = False + cursor = connection.cursor() + cursor.execute( + "SET SESSION CHARACTERISTICS AS TRANSACTION " + "ISOLATION LEVEL %s" % level) + cursor.execute("COMMIT") + cursor.close() + else: + raise exc.ArgumentError( + "Invalid value '%s' for isolation_level. " + "Valid isolation levels for %s are %s or AUTOCOMMIT" % + (level, self.name, ", ".join(self._isolation_lookup)) + ) + + def set_client_encoding(self, connection, client_encoding): + # adjust for ConnectionFairy possibly being present + if hasattr(connection, 'connection'): + connection = connection.connection + + cursor = connection.cursor() + cursor.execute("SET CLIENT_ENCODING TO '" + client_encoding + "'") + cursor.execute("COMMIT") + cursor.close() + + def do_begin_twophase(self, connection, xid): + connection.connection.tpc_begin((0, xid, '')) + + def do_prepare_twophase(self, connection, xid): + connection.connection.tpc_prepare() + + def do_rollback_twophase( + self, connection, xid, is_prepared=True, recover=False): + connection.connection.tpc_rollback((0, xid, '')) + + def do_commit_twophase( + self, connection, xid, is_prepared=True, recover=False): + connection.connection.tpc_commit((0, xid, '')) + + def do_recover_twophase(self, connection): + return [row[1] for row in connection.connection.tpc_recover()] + + def on_connect(self): + fns = [] + + def on_connect(conn): + conn.py_types[quoted_name] = conn.py_types[util.text_type] + fns.append(on_connect) + + if self.client_encoding is not None: + def on_connect(conn): + self.set_client_encoding(conn, self.client_encoding) + fns.append(on_connect) + + if self.isolation_level is not None: + def on_connect(conn): + self.set_isolation_level(conn, self.isolation_level) + fns.append(on_connect) + + if len(fns) > 0: + def on_connect(conn): + for fn in fns: + fn(conn) + return on_connect + else: + return None + +dialect = PGDialect_pg8000 diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py new file mode 100644 index 0000000..2a949c4 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py @@ -0,0 +1,740 @@ +# postgresql/psycopg2.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +r""" +.. dialect:: postgresql+psycopg2 + :name: psycopg2 + :dbapi: psycopg2 + :connectstring: postgresql+psycopg2://user:password@host:port/dbname[?key=value&key=value...] + :url: http://pypi.python.org/pypi/psycopg2/ + +psycopg2 Connect Arguments +----------------------------------- + +psycopg2-specific keyword arguments which are accepted by +:func:`.create_engine()` are: + +* ``server_side_cursors``: Enable the usage of "server side cursors" for SQL + statements which support this feature. What this essentially means from a + psycopg2 point of view is that the cursor is created using a name, e.g. + ``connection.cursor('some name')``, which has the effect that result rows + are not immediately pre-fetched and buffered after statement execution, but + are instead left on the server and only retrieved as needed. SQLAlchemy's + :class:`~sqlalchemy.engine.ResultProxy` uses special row-buffering + behavior when this feature is enabled, such that groups of 100 rows at a + time are fetched over the wire to reduce conversational overhead. + Note that the :paramref:`.Connection.execution_options.stream_results` + execution option is a more targeted + way of enabling this mode on a per-execution basis. + +* ``use_native_unicode``: Enable the usage of Psycopg2 "native unicode" mode + per connection. True by default. + + .. seealso:: + + :ref:`psycopg2_disable_native_unicode` + +* ``isolation_level``: This option, available for all PostgreSQL dialects, + includes the ``AUTOCOMMIT`` isolation level when using the psycopg2 + dialect. + + .. seealso:: + + :ref:`psycopg2_isolation_level` + +* ``client_encoding``: sets the client encoding in a libpq-agnostic way, + using psycopg2's ``set_client_encoding()`` method. + + .. seealso:: + + :ref:`psycopg2_unicode` + +* ``use_batch_mode``: This flag allows ``psycopg2.extras.execute_batch`` + for ``cursor.executemany()`` calls performed by the :class:`.Engine`. + It is currently experimental but + may well become True by default as it is critical for executemany + performance. + + .. seealso:: + + :ref:`psycopg2_batch_mode` + +Unix Domain Connections +------------------------ + +psycopg2 supports connecting via Unix domain connections. When the ``host`` +portion of the URL is omitted, SQLAlchemy passes ``None`` to psycopg2, +which specifies Unix-domain communication rather than TCP/IP communication:: + + create_engine("postgresql+psycopg2://user:password@/dbname") + +By default, the socket file used is to connect to a Unix-domain socket +in ``/tmp``, or whatever socket directory was specified when PostgreSQL +was built. This value can be overridden by passing a pathname to psycopg2, +using ``host`` as an additional keyword argument:: + + create_engine("postgresql+psycopg2://user:password@/dbname?\ +host=/var/lib/postgresql") + +.. seealso:: + + `PQconnectdbParams <http://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-PQCONNECTDBPARAMS>`_ + +.. _psycopg2_execution_options: + +Per-Statement/Connection Execution Options +------------------------------------------- + +The following DBAPI-specific options are respected when used with +:meth:`.Connection.execution_options`, :meth:`.Executable.execution_options`, +:meth:`.Query.execution_options`, in addition to those not specific to DBAPIs: + +* ``isolation_level`` - Set the transaction isolation level for the lifespan of a + :class:`.Connection` (can only be set on a connection, not a statement + or query). See :ref:`psycopg2_isolation_level`. + +* ``stream_results`` - Enable or disable usage of psycopg2 server side cursors - + this feature makes use of "named" cursors in combination with special + result handling methods so that result rows are not fully buffered. + If ``None`` or not set, the ``server_side_cursors`` option of the + :class:`.Engine` is used. + +* ``max_row_buffer`` - when using ``stream_results``, an integer value that + specifies the maximum number of rows to buffer at a time. This is + interpreted by the :class:`.BufferedRowResultProxy`, and if omitted the + buffer will grow to ultimately store 1000 rows at a time. + + .. versionadded:: 1.0.6 + +.. _psycopg2_batch_mode: + +Psycopg2 Batch Mode (Fast Execution) +------------------------------------ + +Modern versions of psycopg2 include a feature known as +`Fast Execution Helpers <http://initd.org/psycopg/docs/extras.html#fast-execution-helpers>`_, +which have been shown in benchmarking to improve psycopg2's executemany() +performance with INSERTS by multiple orders of magnitude. SQLAlchemy +allows this extension to be used for all ``executemany()`` style calls +invoked by an :class:`.Engine` when used with multiple parameter sets, +by adding the ``use_batch_mode`` flag to :func:`.create_engine`:: + + engine = create_engine( + "postgresql+psycopg2://scott:tiger@host/dbname", + use_batch_mode=True) + +Batch mode is considered to be **experimental** at this time, however may +be enabled by default in a future release. + + +.. versionadded:: 1.2.0 + + + +.. _psycopg2_unicode: + +Unicode with Psycopg2 +---------------------- + +By default, the psycopg2 driver uses the ``psycopg2.extensions.UNICODE`` +extension, such that the DBAPI receives and returns all strings as Python +Unicode objects directly - SQLAlchemy passes these values through without +change. Psycopg2 here will encode/decode string values based on the +current "client encoding" setting; by default this is the value in +the ``postgresql.conf`` file, which often defaults to ``SQL_ASCII``. +Typically, this can be changed to ``utf8``, as a more useful default:: + + # postgresql.conf file + + # client_encoding = sql_ascii # actually, defaults to database + # encoding + client_encoding = utf8 + +A second way to affect the client encoding is to set it within Psycopg2 +locally. SQLAlchemy will call psycopg2's +:meth:`psycopg2:connection.set_client_encoding` method +on all new connections based on the value passed to +:func:`.create_engine` using the ``client_encoding`` parameter:: + + # set_client_encoding() setting; + # works for *all* PostgreSQL versions + engine = create_engine("postgresql://user:pass@host/dbname", + client_encoding='utf8') + +This overrides the encoding specified in the PostgreSQL client configuration. +When using the parameter in this way, the psycopg2 driver emits +``SET client_encoding TO 'utf8'`` on the connection explicitly, and works +in all PostgreSQL versions. + +Note that the ``client_encoding`` setting as passed to :func:`.create_engine` +is **not the same** as the more recently added ``client_encoding`` parameter +now supported by libpq directly. This is enabled when ``client_encoding`` +is passed directly to ``psycopg2.connect()``, and from SQLAlchemy is passed +using the :paramref:`.create_engine.connect_args` parameter:: + + # libpq direct parameter setting; + # only works for PostgreSQL **9.1 and above** + engine = create_engine("postgresql://user:pass@host/dbname", + connect_args={'client_encoding': 'utf8'}) + + # using the query string is equivalent + engine = create_engine("postgresql://user:pass@host/dbname?client_encoding=utf8") + +The above parameter was only added to libpq as of version 9.1 of PostgreSQL, +so using the previous method is better for cross-version support. + +.. _psycopg2_disable_native_unicode: + +Disabling Native Unicode +^^^^^^^^^^^^^^^^^^^^^^^^ + +SQLAlchemy can also be instructed to skip the usage of the psycopg2 +``UNICODE`` extension and to instead utilize its own unicode encode/decode +services, which are normally reserved only for those DBAPIs that don't +fully support unicode directly. Passing ``use_native_unicode=False`` to +:func:`.create_engine` will disable usage of ``psycopg2.extensions.UNICODE``. +SQLAlchemy will instead encode data itself into Python bytestrings on the way +in and coerce from bytes on the way back, +using the value of the :func:`.create_engine` ``encoding`` parameter, which +defaults to ``utf-8``. +SQLAlchemy's own unicode encode/decode functionality is steadily becoming +obsolete as most DBAPIs now support unicode fully. + +Bound Parameter Styles +---------------------- + +The default parameter style for the psycopg2 dialect is "pyformat", where +SQL is rendered using ``%(paramname)s`` style. This format has the limitation +that it does not accommodate the unusual case of parameter names that +actually contain percent or parenthesis symbols; as SQLAlchemy in many cases +generates bound parameter names based on the name of a column, the presence +of these characters in a column name can lead to problems. + +There are two solutions to the issue of a :class:`.schema.Column` that contains +one of these characters in its name. One is to specify the +:paramref:`.schema.Column.key` for columns that have such names:: + + measurement = Table('measurement', metadata, + Column('Size (meters)', Integer, key='size_meters') + ) + +Above, an INSERT statement such as ``measurement.insert()`` will use +``size_meters`` as the parameter name, and a SQL expression such as +``measurement.c.size_meters > 10`` will derive the bound parameter name +from the ``size_meters`` key as well. + +.. versionchanged:: 1.0.0 - SQL expressions will use :attr:`.Column.key` + as the source of naming when anonymous bound parameters are created + in SQL expressions; previously, this behavior only applied to + :meth:`.Table.insert` and :meth:`.Table.update` parameter names. + +The other solution is to use a positional format; psycopg2 allows use of the +"format" paramstyle, which can be passed to +:paramref:`.create_engine.paramstyle`:: + + engine = create_engine( + 'postgresql://scott:tiger@localhost:5432/test', paramstyle='format') + +With the above engine, instead of a statement like:: + + INSERT INTO measurement ("Size (meters)") VALUES (%(Size (meters))s) + {'Size (meters)': 1} + +we instead see:: + + INSERT INTO measurement ("Size (meters)") VALUES (%s) + (1, ) + +Where above, the dictionary style is converted into a tuple with positional +style. + + +Transactions +------------ + +The psycopg2 dialect fully supports SAVEPOINT and two-phase commit operations. + +.. _psycopg2_isolation_level: + +Psycopg2 Transaction Isolation Level +------------------------------------- + +As discussed in :ref:`postgresql_isolation_level`, +all PostgreSQL dialects support setting of transaction isolation level +both via the ``isolation_level`` parameter passed to :func:`.create_engine`, +as well as the ``isolation_level`` argument used by +:meth:`.Connection.execution_options`. When using the psycopg2 dialect, these +options make use of psycopg2's ``set_isolation_level()`` connection method, +rather than emitting a PostgreSQL directive; this is because psycopg2's +API-level setting is always emitted at the start of each transaction in any +case. + +The psycopg2 dialect supports these constants for isolation level: + +* ``READ COMMITTED`` +* ``READ UNCOMMITTED`` +* ``REPEATABLE READ`` +* ``SERIALIZABLE`` +* ``AUTOCOMMIT`` + +.. versionadded:: 0.8.2 support for AUTOCOMMIT isolation level when using + psycopg2. + +.. seealso:: + + :ref:`postgresql_isolation_level` + + :ref:`pg8000_isolation_level` + + +NOTICE logging +--------------- + +The psycopg2 dialect will log PostgreSQL NOTICE messages via the +``sqlalchemy.dialects.postgresql`` logger:: + + import logging + logging.getLogger('sqlalchemy.dialects.postgresql').setLevel(logging.INFO) + +.. _psycopg2_hstore:: + +HSTORE type +------------ + +The ``psycopg2`` DBAPI includes an extension to natively handle marshalling of +the HSTORE type. The SQLAlchemy psycopg2 dialect will enable this extension +by default when psycopg2 version 2.4 or greater is used, and +it is detected that the target database has the HSTORE type set up for use. +In other words, when the dialect makes the first +connection, a sequence like the following is performed: + +1. Request the available HSTORE oids using + ``psycopg2.extras.HstoreAdapter.get_oids()``. + If this function returns a list of HSTORE identifiers, we then determine + that the ``HSTORE`` extension is present. + This function is **skipped** if the version of psycopg2 installed is + less than version 2.4. + +2. If the ``use_native_hstore`` flag is at its default of ``True``, and + we've detected that ``HSTORE`` oids are available, the + ``psycopg2.extensions.register_hstore()`` extension is invoked for all + connections. + +The ``register_hstore()`` extension has the effect of **all Python +dictionaries being accepted as parameters regardless of the type of target +column in SQL**. The dictionaries are converted by this extension into a +textual HSTORE expression. If this behavior is not desired, disable the +use of the hstore extension by setting ``use_native_hstore`` to ``False`` as +follows:: + + engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test", + use_native_hstore=False) + +The ``HSTORE`` type is **still supported** when the +``psycopg2.extensions.register_hstore()`` extension is not used. It merely +means that the coercion between Python dictionaries and the HSTORE +string format, on both the parameter side and the result side, will take +place within SQLAlchemy's own marshalling logic, and not that of ``psycopg2`` +which may be more performant. + +""" +from __future__ import absolute_import + +import re +import logging + +from ... import util, exc +import decimal +from ... import processors +from ...engine import result as _result +from ...sql import expression +from ... import types as sqltypes +from .base import PGDialect, PGCompiler, \ + PGIdentifierPreparer, PGExecutionContext, \ + ENUM, _DECIMAL_TYPES, _FLOAT_TYPES,\ + _INT_TYPES, UUID +from .hstore import HSTORE +from .json import JSON, JSONB + +try: + from uuid import UUID as _python_UUID +except ImportError: + _python_UUID = None + + +logger = logging.getLogger('sqlalchemy.dialects.postgresql') + + +class _PGNumeric(sqltypes.Numeric): + def bind_processor(self, dialect): + return None + + def result_processor(self, dialect, coltype): + if self.asdecimal: + if coltype in _FLOAT_TYPES: + return processors.to_decimal_processor_factory( + decimal.Decimal, + self._effective_decimal_return_scale) + elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES: + # pg8000 returns Decimal natively for 1700 + return None + else: + raise exc.InvalidRequestError( + "Unknown PG numeric type: %d" % coltype) + else: + if coltype in _FLOAT_TYPES: + # pg8000 returns float natively for 701 + return None + elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES: + return processors.to_float + else: + raise exc.InvalidRequestError( + "Unknown PG numeric type: %d" % coltype) + + +class _PGEnum(ENUM): + def result_processor(self, dialect, coltype): + if util.py2k and self.convert_unicode is True: + # we can't easily use PG's extensions here because + # the OID is on the fly, and we need to give it a python + # function anyway - not really worth it. + self.convert_unicode = "force_nocheck" + return super(_PGEnum, self).result_processor(dialect, coltype) + + +class _PGHStore(HSTORE): + def bind_processor(self, dialect): + if dialect._has_native_hstore: + return None + else: + return super(_PGHStore, self).bind_processor(dialect) + + def result_processor(self, dialect, coltype): + if dialect._has_native_hstore: + return None + else: + return super(_PGHStore, self).result_processor(dialect, coltype) + + +class _PGJSON(JSON): + + def result_processor(self, dialect, coltype): + if dialect._has_native_json: + return None + else: + return super(_PGJSON, self).result_processor(dialect, coltype) + + +class _PGJSONB(JSONB): + + def result_processor(self, dialect, coltype): + if dialect._has_native_jsonb: + return None + else: + return super(_PGJSONB, self).result_processor(dialect, coltype) + + +class _PGUUID(UUID): + def bind_processor(self, dialect): + if not self.as_uuid and dialect.use_native_uuid: + nonetype = type(None) + + def process(value): + if value is not None: + value = _python_UUID(value) + return value + return process + + def result_processor(self, dialect, coltype): + if not self.as_uuid and dialect.use_native_uuid: + def process(value): + if value is not None: + value = str(value) + return value + return process + + +_server_side_id = util.counter() + + +class PGExecutionContext_psycopg2(PGExecutionContext): + def create_server_side_cursor(self): + # use server-side cursors: + # http://lists.initd.org/pipermail/psycopg/2007-January/005251.html + ident = "c_%s_%s" % (hex(id(self))[2:], + hex(_server_side_id())[2:]) + return self._dbapi_connection.cursor(ident) + + def get_result_proxy(self): + # TODO: ouch + if logger.isEnabledFor(logging.INFO): + self._log_notices(self.cursor) + + if self._is_server_side: + return _result.BufferedRowResultProxy(self) + else: + return _result.ResultProxy(self) + + def _log_notices(self, cursor): + for notice in cursor.connection.notices: + # NOTICE messages have a + # newline character at the end + logger.info(notice.rstrip()) + + cursor.connection.notices[:] = [] + + +class PGCompiler_psycopg2(PGCompiler): + pass + + +class PGIdentifierPreparer_psycopg2(PGIdentifierPreparer): + pass + + +class PGDialect_psycopg2(PGDialect): + driver = 'psycopg2' + if util.py2k: + supports_unicode_statements = False + + supports_server_side_cursors = True + + default_paramstyle = 'pyformat' + # set to true based on psycopg2 version + supports_sane_multi_rowcount = False + execution_ctx_cls = PGExecutionContext_psycopg2 + statement_compiler = PGCompiler_psycopg2 + preparer = PGIdentifierPreparer_psycopg2 + psycopg2_version = (0, 0) + + FEATURE_VERSION_MAP = dict( + native_json=(2, 5), + native_jsonb=(2, 5, 4), + sane_multi_rowcount=(2, 0, 9), + array_oid=(2, 4, 3), + hstore_adapter=(2, 4) + ) + + _has_native_hstore = False + _has_native_json = False + _has_native_jsonb = False + + engine_config_types = PGDialect.engine_config_types.union([ + ('use_native_unicode', util.asbool), + ]) + + colspecs = util.update_copy( + PGDialect.colspecs, + { + sqltypes.Numeric: _PGNumeric, + ENUM: _PGEnum, # needs force_unicode + sqltypes.Enum: _PGEnum, # needs force_unicode + HSTORE: _PGHStore, + JSON: _PGJSON, + sqltypes.JSON: _PGJSON, + JSONB: _PGJSONB, + UUID: _PGUUID + } + ) + + def __init__(self, server_side_cursors=False, use_native_unicode=True, + client_encoding=None, + use_native_hstore=True, use_native_uuid=True, + use_batch_mode=False, + **kwargs): + PGDialect.__init__(self, **kwargs) + self.server_side_cursors = server_side_cursors + self.use_native_unicode = use_native_unicode + self.use_native_hstore = use_native_hstore + self.use_native_uuid = use_native_uuid + self.supports_unicode_binds = use_native_unicode + self.client_encoding = client_encoding + self.psycopg2_batch_mode = use_batch_mode + if self.dbapi and hasattr(self.dbapi, '__version__'): + m = re.match(r'(\d+)\.(\d+)(?:\.(\d+))?', + self.dbapi.__version__) + if m: + self.psycopg2_version = tuple( + int(x) + for x in m.group(1, 2, 3) + if x is not None) + + def initialize(self, connection): + super(PGDialect_psycopg2, self).initialize(connection) + self._has_native_hstore = self.use_native_hstore and \ + self._hstore_oids(connection.connection) \ + is not None + self._has_native_json = \ + self.psycopg2_version >= self.FEATURE_VERSION_MAP['native_json'] + self._has_native_jsonb = \ + self.psycopg2_version >= self.FEATURE_VERSION_MAP['native_jsonb'] + + # http://initd.org/psycopg/docs/news.html#what-s-new-in-psycopg-2-0-9 + self.supports_sane_multi_rowcount = \ + self.psycopg2_version >= \ + self.FEATURE_VERSION_MAP['sane_multi_rowcount'] and \ + not self.psycopg2_batch_mode + + @classmethod + def dbapi(cls): + import psycopg2 + return psycopg2 + + @classmethod + def _psycopg2_extensions(cls): + from psycopg2 import extensions + return extensions + + @classmethod + def _psycopg2_extras(cls): + from psycopg2 import extras + return extras + + @util.memoized_property + def _isolation_lookup(self): + extensions = self._psycopg2_extensions() + return { + 'AUTOCOMMIT': extensions.ISOLATION_LEVEL_AUTOCOMMIT, + 'READ COMMITTED': extensions.ISOLATION_LEVEL_READ_COMMITTED, + 'READ UNCOMMITTED': extensions.ISOLATION_LEVEL_READ_UNCOMMITTED, + 'REPEATABLE READ': extensions.ISOLATION_LEVEL_REPEATABLE_READ, + 'SERIALIZABLE': extensions.ISOLATION_LEVEL_SERIALIZABLE + } + + def set_isolation_level(self, connection, level): + try: + level = self._isolation_lookup[level.replace('_', ' ')] + except KeyError: + raise exc.ArgumentError( + "Invalid value '%s' for isolation_level. " + "Valid isolation levels for %s are %s" % + (level, self.name, ", ".join(self._isolation_lookup)) + ) + + connection.set_isolation_level(level) + + def on_connect(self): + extras = self._psycopg2_extras() + extensions = self._psycopg2_extensions() + + fns = [] + if self.client_encoding is not None: + def on_connect(conn): + conn.set_client_encoding(self.client_encoding) + fns.append(on_connect) + + if self.isolation_level is not None: + def on_connect(conn): + self.set_isolation_level(conn, self.isolation_level) + fns.append(on_connect) + + if self.dbapi and self.use_native_uuid: + def on_connect(conn): + extras.register_uuid(None, conn) + fns.append(on_connect) + + if self.dbapi and self.use_native_unicode: + def on_connect(conn): + extensions.register_type(extensions.UNICODE, conn) + extensions.register_type(extensions.UNICODEARRAY, conn) + fns.append(on_connect) + + if self.dbapi and self.use_native_hstore: + def on_connect(conn): + hstore_oids = self._hstore_oids(conn) + if hstore_oids is not None: + oid, array_oid = hstore_oids + kw = {'oid': oid} + if util.py2k: + kw['unicode'] = True + if self.psycopg2_version >= \ + self.FEATURE_VERSION_MAP['array_oid']: + kw['array_oid'] = array_oid + extras.register_hstore(conn, **kw) + fns.append(on_connect) + + if self.dbapi and self._json_deserializer: + def on_connect(conn): + if self._has_native_json: + extras.register_default_json( + conn, loads=self._json_deserializer) + if self._has_native_jsonb: + extras.register_default_jsonb( + conn, loads=self._json_deserializer) + fns.append(on_connect) + + if fns: + def on_connect(conn): + for fn in fns: + fn(conn) + return on_connect + else: + return None + + def do_executemany(self, cursor, statement, parameters, context=None): + if self.psycopg2_batch_mode: + extras = self._psycopg2_extras() + extras.execute_batch(cursor, statement, parameters) + else: + cursor.executemany(statement, parameters) + + @util.memoized_instancemethod + def _hstore_oids(self, conn): + if self.psycopg2_version >= self.FEATURE_VERSION_MAP['hstore_adapter']: + extras = self._psycopg2_extras() + oids = extras.HstoreAdapter.get_oids(conn) + if oids is not None and oids[0]: + return oids[0:2] + return None + + def create_connect_args(self, url): + opts = url.translate_connect_args(username='user') + if 'port' in opts: + opts['port'] = int(opts['port']) + opts.update(url.query) + return ([], opts) + + def is_disconnect(self, e, connection, cursor): + if isinstance(e, self.dbapi.Error): + # check the "closed" flag. this might not be + # present on old psycopg2 versions. Also, + # this flag doesn't actually help in a lot of disconnect + # situations, so don't rely on it. + if getattr(connection, 'closed', False): + return True + + # checks based on strings. in the case that .closed + # didn't cut it, fall back onto these. + str_e = str(e).partition("\n")[0] + for msg in [ + # these error messages from libpq: interfaces/libpq/fe-misc.c + # and interfaces/libpq/fe-secure.c. + 'terminating connection', + 'closed the connection', + 'connection not open', + 'could not receive data from server', + 'could not send data to server', + # psycopg2 client errors, psycopg2/conenction.h, + # psycopg2/cursor.h + 'connection already closed', + 'cursor already closed', + # not sure where this path is originally from, it may + # be obsolete. It really says "losed", not "closed". + 'losed the connection unexpectedly', + # these can occur in newer SSL + 'connection has been closed unexpectedly', + 'SSL SYSCALL error: Bad file descriptor', + 'SSL SYSCALL error: EOF detected', + 'SSL error: decryption failed or bad record mac', + 'SSL SYSCALL error: Operation timed out', + ]: + idx = str_e.find(msg) + if idx >= 0 and '"' not in str_e[:idx]: + return True + return False + +dialect = PGDialect_psycopg2 diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg2cffi.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg2cffi.py new file mode 100644 index 0000000..a1141a9 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg2cffi.py @@ -0,0 +1,61 @@ +# testing/engines.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +""" +.. dialect:: postgresql+psycopg2cffi + :name: psycopg2cffi + :dbapi: psycopg2cffi + :connectstring: \ +postgresql+psycopg2cffi://user:password@host:port/dbname\ +[?key=value&key=value...] + :url: http://pypi.python.org/pypi/psycopg2cffi/ + +``psycopg2cffi`` is an adaptation of ``psycopg2``, using CFFI for the C +layer. This makes it suitable for use in e.g. PyPy. Documentation +is as per ``psycopg2``. + +.. versionadded:: 1.0.0 + +.. seealso:: + + :mod:`sqlalchemy.dialects.postgresql.psycopg2` + +""" +from .psycopg2 import PGDialect_psycopg2 + + +class PGDialect_psycopg2cffi(PGDialect_psycopg2): + driver = 'psycopg2cffi' + supports_unicode_statements = True + + # psycopg2cffi's first release is 2.5.0, but reports + # __version__ as 2.4.4. Subsequent releases seem to have + # fixed this. + + FEATURE_VERSION_MAP = dict( + native_json=(2, 4, 4), + native_jsonb=(2, 7, 1), + sane_multi_rowcount=(2, 4, 4), + array_oid=(2, 4, 4), + hstore_adapter=(2, 4, 4) + ) + + @classmethod + def dbapi(cls): + return __import__('psycopg2cffi') + + @classmethod + def _psycopg2_extensions(cls): + root = __import__('psycopg2cffi', fromlist=['extensions']) + return root.extensions + + @classmethod + def _psycopg2_extras(cls): + root = __import__('psycopg2cffi', fromlist=['extras']) + return root.extras + + +dialect = PGDialect_psycopg2cffi diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pygresql.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pygresql.py new file mode 100644 index 0000000..304afca --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pygresql.py @@ -0,0 +1,243 @@ +# postgresql/pygresql.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: postgresql+pygresql + :name: pygresql + :dbapi: pgdb + :connectstring: postgresql+pygresql://user:password@host:port/dbname\ +[?key=value&key=value...] + :url: http://www.pygresql.org/ +""" + +import decimal +import re + +from ... import exc, processors, util +from ...types import Numeric, JSON as Json +from ...sql.elements import Null +from .base import PGDialect, PGCompiler, PGIdentifierPreparer, \ + _DECIMAL_TYPES, _FLOAT_TYPES, _INT_TYPES, UUID +from .hstore import HSTORE +from .json import JSON, JSONB + + +class _PGNumeric(Numeric): + + def bind_processor(self, dialect): + return None + + def result_processor(self, dialect, coltype): + if not isinstance(coltype, int): + coltype = coltype.oid + if self.asdecimal: + if coltype in _FLOAT_TYPES: + return processors.to_decimal_processor_factory( + decimal.Decimal, + self._effective_decimal_return_scale) + elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES: + # PyGreSQL returns Decimal natively for 1700 (numeric) + return None + else: + raise exc.InvalidRequestError( + "Unknown PG numeric type: %d" % coltype) + else: + if coltype in _FLOAT_TYPES: + # PyGreSQL returns float natively for 701 (float8) + return None + elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES: + return processors.to_float + else: + raise exc.InvalidRequestError( + "Unknown PG numeric type: %d" % coltype) + + +class _PGHStore(HSTORE): + + def bind_processor(self, dialect): + if not dialect.has_native_hstore: + return super(_PGHStore, self).bind_processor(dialect) + hstore = dialect.dbapi.Hstore + def process(value): + if isinstance(value, dict): + return hstore(value) + return value + return process + + def result_processor(self, dialect, coltype): + if not dialect.has_native_hstore: + return super(_PGHStore, self).result_processor(dialect, coltype) + + +class _PGJSON(JSON): + + def bind_processor(self, dialect): + if not dialect.has_native_json: + return super(_PGJSON, self).bind_processor(dialect) + json = dialect.dbapi.Json + + def process(value): + if value is self.NULL: + value = None + elif isinstance(value, Null) or ( + value is None and self.none_as_null): + return None + if value is None or isinstance(value, (dict, list)): + return json(value) + return value + + return process + + def result_processor(self, dialect, coltype): + if not dialect.has_native_json: + return super(_PGJSON, self).result_processor(dialect, coltype) + + +class _PGJSONB(JSONB): + + def bind_processor(self, dialect): + if not dialect.has_native_json: + return super(_PGJSONB, self).bind_processor(dialect) + json = dialect.dbapi.Json + + def process(value): + if value is self.NULL: + value = None + elif isinstance(value, Null) or ( + value is None and self.none_as_null): + return None + if value is None or isinstance(value, (dict, list)): + return json(value) + return value + + return process + + def result_processor(self, dialect, coltype): + if not dialect.has_native_json: + return super(_PGJSONB, self).result_processor(dialect, coltype) + + +class _PGUUID(UUID): + + def bind_processor(self, dialect): + if not dialect.has_native_uuid: + return super(_PGUUID, self).bind_processor(dialect) + uuid = dialect.dbapi.Uuid + + def process(value): + if value is None: + return None + if isinstance(value, (str, bytes)): + if len(value) == 16: + return uuid(bytes=value) + return uuid(value) + if isinstance(value, int): + return uuid(int=value) + return value + + return process + + def result_processor(self, dialect, coltype): + if not dialect.has_native_uuid: + return super(_PGUUID, self).result_processor(dialect, coltype) + if not self.as_uuid: + def process(value): + if value is not None: + return str(value) + return process + + +class _PGCompiler(PGCompiler): + + def visit_mod_binary(self, binary, operator, **kw): + return self.process(binary.left, **kw) + " %% " + \ + self.process(binary.right, **kw) + + def post_process_text(self, text): + return text.replace('%', '%%') + + +class _PGIdentifierPreparer(PGIdentifierPreparer): + + def _escape_identifier(self, value): + value = value.replace(self.escape_quote, self.escape_to_quote) + return value.replace('%', '%%') + + +class PGDialect_pygresql(PGDialect): + + driver = 'pygresql' + + statement_compiler = _PGCompiler + preparer = _PGIdentifierPreparer + + @classmethod + def dbapi(cls): + import pgdb + return pgdb + + colspecs = util.update_copy( + PGDialect.colspecs, + { + Numeric: _PGNumeric, + HSTORE: _PGHStore, + Json: _PGJSON, + JSON: _PGJSON, + JSONB: _PGJSONB, + UUID: _PGUUID, + } + ) + + def __init__(self, **kwargs): + super(PGDialect_pygresql, self).__init__(**kwargs) + try: + version = self.dbapi.version + m = re.match(r'(\d+)\.(\d+)', version) + version = (int(m.group(1)), int(m.group(2))) + except (AttributeError, ValueError, TypeError): + version = (0, 0) + self.dbapi_version = version + if version < (5, 0): + has_native_hstore = has_native_json = has_native_uuid = False + if version != (0, 0): + util.warn("PyGreSQL is only fully supported by SQLAlchemy" + " since version 5.0.") + else: + self.supports_unicode_statements = True + self.supports_unicode_binds = True + has_native_hstore = has_native_json = has_native_uuid = True + self.has_native_hstore = has_native_hstore + self.has_native_json = has_native_json + self.has_native_uuid = has_native_uuid + + def create_connect_args(self, url): + opts = url.translate_connect_args(username='user') + if 'port' in opts: + opts['host'] = '%s:%s' % ( + opts.get('host', '').rsplit(':', 1)[0], opts.pop('port')) + opts.update(url.query) + return [], opts + + def is_disconnect(self, e, connection, cursor): + if isinstance(e, self.dbapi.Error): + if not connection: + return False + try: + connection = connection.connection + except AttributeError: + pass + else: + if not connection: + return False + try: + return connection.closed + except AttributeError: # PyGreSQL < 5.0 + return connection._cnx is None + return False + + +dialect = PGDialect_pygresql diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pypostgresql.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pypostgresql.py new file mode 100644 index 0000000..b633323 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pypostgresql.py @@ -0,0 +1,97 @@ +# postgresql/pypostgresql.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: postgresql+pypostgresql + :name: py-postgresql + :dbapi: pypostgresql + :connectstring: postgresql+pypostgresql://user:password@host:port/dbname\ +[?key=value&key=value...] + :url: http://python.projects.pgfoundry.org/ + + +""" +from ... import util +from ... import types as sqltypes +from .base import PGDialect, PGExecutionContext +from ... import processors + + +class PGNumeric(sqltypes.Numeric): + def bind_processor(self, dialect): + return processors.to_str + + def result_processor(self, dialect, coltype): + if self.asdecimal: + return None + else: + return processors.to_float + + +class PGExecutionContext_pypostgresql(PGExecutionContext): + pass + + +class PGDialect_pypostgresql(PGDialect): + driver = 'pypostgresql' + + supports_unicode_statements = True + supports_unicode_binds = True + description_encoding = None + default_paramstyle = 'pyformat' + + # requires trunk version to support sane rowcounts + # TODO: use dbapi version information to set this flag appropriately + supports_sane_rowcount = True + supports_sane_multi_rowcount = False + + execution_ctx_cls = PGExecutionContext_pypostgresql + colspecs = util.update_copy( + PGDialect.colspecs, + { + sqltypes.Numeric: PGNumeric, + + # prevents PGNumeric from being used + sqltypes.Float: sqltypes.Float, + } + ) + + @classmethod + def dbapi(cls): + from postgresql.driver import dbapi20 + return dbapi20 + + _DBAPI_ERROR_NAMES = [ + "Error", + "InterfaceError", "DatabaseError", "DataError", + "OperationalError", "IntegrityError", "InternalError", + "ProgrammingError", "NotSupportedError" + ] + + @util.memoized_property + def dbapi_exception_translation_map(self): + if self.dbapi is None: + return {} + + return dict( + (getattr(self.dbapi, name).__name__, name) + for name in self._DBAPI_ERROR_NAMES + ) + + def create_connect_args(self, url): + opts = url.translate_connect_args(username='user') + if 'port' in opts: + opts['port'] = int(opts['port']) + else: + opts['port'] = 5432 + opts.update(url.query) + return ([], opts) + + def is_disconnect(self, e, connection, cursor): + return "connection is closed" in str(e) + +dialect = PGDialect_pypostgresql diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/ranges.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/ranges.py new file mode 100644 index 0000000..eb2d86b --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/ranges.py @@ -0,0 +1,172 @@ +# Copyright (C) 2013-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from .base import ischema_names +from ... import types as sqltypes + +__all__ = ('INT4RANGE', 'INT8RANGE', 'NUMRANGE') + + +class RangeOperators(object): + """ + This mixin provides functionality for the Range Operators + listed in Table 9-44 of the `postgres documentation`__ for Range + Functions and Operators. It is used by all the range types + provided in the ``postgres`` dialect and can likely be used for + any range types you create yourself. + + __ http://www.postgresql.org/docs/devel/static/functions-range.html + + No extra support is provided for the Range Functions listed in + Table 9-45 of the postgres documentation. For these, the normal + :func:`~sqlalchemy.sql.expression.func` object should be used. + + .. versionadded:: 0.8.2 Support for PostgreSQL RANGE operations. + + """ + + class comparator_factory(sqltypes.Concatenable.Comparator): + """Define comparison operations for range types.""" + + def __ne__(self, other): + "Boolean expression. Returns true if two ranges are not equal" + if other is None: + return super( + RangeOperators.comparator_factory, self).__ne__(other) + else: + return self.expr.op('<>')(other) + + def contains(self, other, **kw): + """Boolean expression. Returns true if the right hand operand, + which can be an element or a range, is contained within the + column. + """ + return self.expr.op('@>')(other) + + def contained_by(self, other): + """Boolean expression. Returns true if the column is contained + within the right hand operand. + """ + return self.expr.op('<@')(other) + + def overlaps(self, other): + """Boolean expression. Returns true if the column overlaps + (has points in common with) the right hand operand. + """ + return self.expr.op('&&')(other) + + def strictly_left_of(self, other): + """Boolean expression. Returns true if the column is strictly + left of the right hand operand. + """ + return self.expr.op('<<')(other) + + __lshift__ = strictly_left_of + + def strictly_right_of(self, other): + """Boolean expression. Returns true if the column is strictly + right of the right hand operand. + """ + return self.expr.op('>>')(other) + + __rshift__ = strictly_right_of + + def not_extend_right_of(self, other): + """Boolean expression. Returns true if the range in the column + does not extend right of the range in the operand. + """ + return self.expr.op('&<')(other) + + def not_extend_left_of(self, other): + """Boolean expression. Returns true if the range in the column + does not extend left of the range in the operand. + """ + return self.expr.op('&>')(other) + + def adjacent_to(self, other): + """Boolean expression. Returns true if the range in the column + is adjacent to the range in the operand. + """ + return self.expr.op('-|-')(other) + + def __add__(self, other): + """Range expression. Returns the union of the two ranges. + Will raise an exception if the resulting range is not + contigous. + """ + return self.expr.op('+')(other) + + +class INT4RANGE(RangeOperators, sqltypes.TypeEngine): + """Represent the PostgreSQL INT4RANGE type. + + .. versionadded:: 0.8.2 + + """ + + __visit_name__ = 'INT4RANGE' + +ischema_names['int4range'] = INT4RANGE + + +class INT8RANGE(RangeOperators, sqltypes.TypeEngine): + """Represent the PostgreSQL INT8RANGE type. + + .. versionadded:: 0.8.2 + + """ + + __visit_name__ = 'INT8RANGE' + +ischema_names['int8range'] = INT8RANGE + + +class NUMRANGE(RangeOperators, sqltypes.TypeEngine): + """Represent the PostgreSQL NUMRANGE type. + + .. versionadded:: 0.8.2 + + """ + + __visit_name__ = 'NUMRANGE' + +ischema_names['numrange'] = NUMRANGE + + +class DATERANGE(RangeOperators, sqltypes.TypeEngine): + """Represent the PostgreSQL DATERANGE type. + + .. versionadded:: 0.8.2 + + """ + + __visit_name__ = 'DATERANGE' + +ischema_names['daterange'] = DATERANGE + + +class TSRANGE(RangeOperators, sqltypes.TypeEngine): + """Represent the PostgreSQL TSRANGE type. + + .. versionadded:: 0.8.2 + + """ + + __visit_name__ = 'TSRANGE' + +ischema_names['tsrange'] = TSRANGE + + +class TSTZRANGE(RangeOperators, sqltypes.TypeEngine): + """Represent the PostgreSQL TSTZRANGE type. + + .. versionadded:: 0.8.2 + + """ + + __visit_name__ = 'TSTZRANGE' + +ischema_names['tstzrange'] = TSTZRANGE diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/zxjdbc.py b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/zxjdbc.py new file mode 100644 index 0000000..ef6e8f1 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/postgresql/zxjdbc.py @@ -0,0 +1,46 @@ +# postgresql/zxjdbc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: postgresql+zxjdbc + :name: zxJDBC for Jython + :dbapi: zxjdbc + :connectstring: postgresql+zxjdbc://scott:tiger@localhost/db + :driverurl: http://jdbc.postgresql.org/ + + +""" +from ...connectors.zxJDBC import ZxJDBCConnector +from .base import PGDialect, PGExecutionContext + + +class PGExecutionContext_zxjdbc(PGExecutionContext): + + def create_cursor(self): + cursor = self._dbapi_connection.cursor() + cursor.datahandler = self.dialect.DataHandler(cursor.datahandler) + return cursor + + +class PGDialect_zxjdbc(ZxJDBCConnector, PGDialect): + jdbc_db_name = 'postgresql' + jdbc_driver_name = 'org.postgresql.Driver' + + execution_ctx_cls = PGExecutionContext_zxjdbc + + supports_native_decimal = True + + def __init__(self, *args, **kwargs): + super(PGDialect_zxjdbc, self).__init__(*args, **kwargs) + from com.ziclix.python.sql.handler import PostgresqlDataHandler + self.DataHandler = PostgresqlDataHandler + + def _get_server_version_info(self, connection): + parts = connection.connection.dbversion.split('.') + return tuple(int(x) for x in parts) + +dialect = PGDialect_zxjdbc diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__init__.py b/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__init__.py new file mode 100644 index 0000000..cb5337a --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__init__.py @@ -0,0 +1,21 @@ +# sqlite/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from . import base, pysqlite, pysqlcipher # noqa + +from sqlalchemy.dialects.sqlite.base import ( + BLOB, BOOLEAN, CHAR, DATE, DATETIME, DECIMAL, FLOAT, INTEGER, REAL, + NUMERIC, SMALLINT, TEXT, TIME, TIMESTAMP, VARCHAR +) + +# default dialect +base.dialect = dialect = pysqlite.dialect + + +__all__ = ('BLOB', 'BOOLEAN', 'CHAR', 'DATE', 'DATETIME', 'DECIMAL', + 'FLOAT', 'INTEGER', 'NUMERIC', 'SMALLINT', 'TEXT', 'TIME', + 'TIMESTAMP', 'VARCHAR', 'REAL', 'dialect') diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03c83e00273172ea45aa7bb9dbd26b414fe12652 GIT binary patch literal 775 zcmb`F%Z}496o#EkoAiE5N9<URvS>H#&<K&X0V?%^+6+QRk)ljvF`+qah{K3H5)Z(8 zu=bYKyaFqHxCnNzz*4?g_HX~^;zylM^TVfQ^jT-@3;SdA)%>aYM5d6iM9UazprH;r z8enKF(Gw#xu?97)Lme%!&;}bD(7+}%u>~z`LmNBL!7g;M2R-aV9|thNAq;T@BaLzW ztz$B-MGiR07{-aSXYR$lrYv1Unm4iF(#|XKE=x-;Ti<7gG{5D=y^=7uC(YSvJCoLI zyIuL-Mw;^%-cFheFYu*J43=wOS_^-^Tze~7d$!tofwY#Jz<=&5;m%j9*5-Qc@0N3E z@0Dn!Hl-Q(uZeWJLEH!4`lYm9c{_3_>dL*a2sWWXXcAh4Hlai45vY{KfG{MC2o9mE zAgv^gGkz#HZvQ`CTa4A+?uBH9YAh=Zf~f!xRWal2aLcip{&W`89|fLLz5h7|3jQzo zQ4G?GqBzT<=p!p0&=JDdV*WV1R%RlsF2!XPF1R?B=RBO9&&SC+KE-c1@>4n4SEA%N z!C3u@@Q$DE!YsWBg?bK?JU$$iPK3WR3V&rpYf2MFQF=<tD4OP#w6E}-ywCU(YMG#J MIr^gBHkih=Z_dH9xc~qF literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/base.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/base.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9deb3116c51664825dec6066ffb7dd749cafb0c GIT binary patch literal 50831 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%0DgTJDmka!uE)40fEU^&M%u_HJ+2<nNhp@k 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 b3=~~x(2W+M=S*Vk-LX4kXUCo&JO2LymekI) literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c39d40ffd5c1dc5983c8795e8e137ae304655af3 GIT binary patch literal 4965 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 L+FRyRyV(5?NP`8{ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d02d211f354d4ade93459d82b31b6a085cf20b9a GIT binary patch literal 15362 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/base.py b/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/base.py new file mode 100644 index 0000000..6e0d430 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/base.py @@ -0,0 +1,1585 @@ +# sqlite/base.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +r""" +.. dialect:: sqlite + :name: SQLite + +.. _sqlite_datetime: + +Date and Time Types +------------------- + +SQLite does not have built-in DATE, TIME, or DATETIME types, and pysqlite does +not provide out of the box functionality for translating values between Python +`datetime` objects and a SQLite-supported format. SQLAlchemy's own +:class:`~sqlalchemy.types.DateTime` and related types provide date formatting +and parsing functionality when SQlite is used. The implementation classes are +:class:`~.sqlite.DATETIME`, :class:`~.sqlite.DATE` and :class:`~.sqlite.TIME`. +These types represent dates and times as ISO formatted strings, which also +nicely support ordering. There's no reliance on typical "libc" internals for +these functions so historical dates are fully supported. + +Ensuring Text affinity +^^^^^^^^^^^^^^^^^^^^^^ + +The DDL rendered for these types is the standard ``DATE``, ``TIME`` +and ``DATETIME`` indicators. However, custom storage formats can also be +applied to these types. When the +storage format is detected as containing no alpha characters, the DDL for +these types is rendered as ``DATE_CHAR``, ``TIME_CHAR``, and ``DATETIME_CHAR``, +so that the column continues to have textual affinity. + +.. seealso:: + + `Type Affinity <http://www.sqlite.org/datatype3.html#affinity>`_ - in the SQLite documentation + +.. _sqlite_autoincrement: + +SQLite Auto Incrementing Behavior +---------------------------------- + +Background on SQLite's autoincrement is at: http://sqlite.org/autoinc.html + +Key concepts: + +* SQLite has an implicit "auto increment" feature that takes place for any + non-composite primary-key column that is specifically created using + "INTEGER PRIMARY KEY" for the type + primary key. + +* SQLite also has an explicit "AUTOINCREMENT" keyword, that is **not** + equivalent to the implicit autoincrement feature; this keyword is not + recommended for general use. SQLAlchemy does not render this keyword + unless a special SQLite-specific directive is used (see below). However, + it still requires that the column's type is named "INTEGER". + +Using the AUTOINCREMENT Keyword +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To specifically render the AUTOINCREMENT keyword on the primary key column +when rendering DDL, add the flag ``sqlite_autoincrement=True`` to the Table +construct:: + + Table('sometable', metadata, + Column('id', Integer, primary_key=True), + sqlite_autoincrement=True) + +Allowing autoincrement behavior SQLAlchemy types other than Integer/INTEGER +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +SQLite's typing model is based on naming conventions. Among +other things, this means that any type name which contains the +substring ``"INT"`` will be determined to be of "integer affinity". A +type named ``"BIGINT"``, ``"SPECIAL_INT"`` or even ``"XYZINTQPR"``, will be considered by +SQLite to be of "integer" affinity. However, **the SQLite +autoincrement feature, whether implicitly or explicitly enabled, +requires that the name of the column's type +is exactly the string "INTEGER"**. Therefore, if an +application uses a type like :class:`.BigInteger` for a primary key, on +SQLite this type will need to be rendered as the name ``"INTEGER"`` when +emitting the initial ``CREATE TABLE`` statement in order for the autoincrement +behavior to be available. + +One approach to achieve this is to use :class:`.Integer` on SQLite +only using :meth:`.TypeEngine.with_variant`:: + + table = Table( + "my_table", metadata, + Column("id", BigInteger().with_variant(Integer, "sqlite"), primary_key=True) + ) + +Another is to use a subclass of :class:`.BigInteger` that overrides its DDL name +to be ``INTEGER`` when compiled against SQLite:: + + from sqlalchemy import BigInteger + from sqlalchemy.ext.compiler import compiles + + class SLBigInteger(BigInteger): + pass + + @compiles(SLBigInteger, 'sqlite') + def bi_c(element, compiler, **kw): + return "INTEGER" + + @compiles(SLBigInteger) + def bi_c(element, compiler, **kw): + return compiler.visit_BIGINT(element, **kw) + + + table = Table( + "my_table", metadata, + Column("id", SLBigInteger(), primary_key=True) + ) + +.. seealso:: + + :meth:`.TypeEngine.with_variant` + + :ref:`sqlalchemy.ext.compiler_toplevel` + + `Datatypes In SQLite Version 3 <http://sqlite.org/datatype3.html>`_ + +.. _sqlite_concurrency: + +Database Locking Behavior / Concurrency +--------------------------------------- + +SQLite is not designed for a high level of write concurrency. The database +itself, being a file, is locked completely during write operations within +transactions, meaning exactly one "connection" (in reality a file handle) +has exclusive access to the database during this period - all other +"connections" will be blocked during this time. + +The Python DBAPI specification also calls for a connection model that is +always in a transaction; there is no ``connection.begin()`` method, +only ``connection.commit()`` and ``connection.rollback()``, upon which a +new transaction is to be begun immediately. This may seem to imply +that the SQLite driver would in theory allow only a single filehandle on a +particular database file at any time; however, there are several +factors both within SQlite itself as well as within the pysqlite driver +which loosen this restriction significantly. + +However, no matter what locking modes are used, SQLite will still always +lock the database file once a transaction is started and DML (e.g. INSERT, +UPDATE, DELETE) has at least been emitted, and this will block +other transactions at least at the point that they also attempt to emit DML. +By default, the length of time on this block is very short before it times out +with an error. + +This behavior becomes more critical when used in conjunction with the +SQLAlchemy ORM. SQLAlchemy's :class:`.Session` object by default runs +within a transaction, and with its autoflush model, may emit DML preceding +any SELECT statement. This may lead to a SQLite database that locks +more quickly than is expected. The locking mode of SQLite and the pysqlite +driver can be manipulated to some degree, however it should be noted that +achieving a high degree of write-concurrency with SQLite is a losing battle. + +For more information on SQLite's lack of write concurrency by design, please +see +`Situations Where Another RDBMS May Work Better - High Concurrency +<http://www.sqlite.org/whentouse.html>`_ near the bottom of the page. + +The following subsections introduce areas that are impacted by SQLite's +file-based architecture and additionally will usually require workarounds to +work when using the pysqlite driver. + +.. _sqlite_isolation_level: + +Transaction Isolation Level +---------------------------- + +SQLite supports "transaction isolation" in a non-standard way, along two +axes. One is that of the `PRAGMA read_uncommitted <http://www.sqlite.org/pragma.html#pragma_read_uncommitted>`_ +instruction. This setting can essentially switch SQLite between its +default mode of ``SERIALIZABLE`` isolation, and a "dirty read" isolation +mode normally referred to as ``READ UNCOMMITTED``. + +SQLAlchemy ties into this PRAGMA statement using the +:paramref:`.create_engine.isolation_level` parameter of :func:`.create_engine`. +Valid values for this parameter when used with SQLite are ``"SERIALIZABLE"`` +and ``"READ UNCOMMITTED"`` corresponding to a value of 0 and 1, respectively. +SQLite defaults to ``SERIALIZABLE``, however its behavior is impacted by +the pysqlite driver's default behavior. + +The other axis along which SQLite's transactional locking is impacted is +via the nature of the ``BEGIN`` statement used. The three varieties +are "deferred", "immediate", and "exclusive", as described at +`BEGIN TRANSACTION <http://sqlite.org/lang_transaction.html>`_. A straight +``BEGIN`` statement uses the "deferred" mode, where the the database file is +not locked until the first read or write operation, and read access remains +open to other transactions until the first write operation. But again, +it is critical to note that the pysqlite driver interferes with this behavior +by *not even emitting BEGIN* until the first write operation. + +.. warning:: + + SQLite's transactional scope is impacted by unresolved + issues in the pysqlite driver, which defers BEGIN statements to a greater + degree than is often feasible. See the section :ref:`pysqlite_serializable` + for techniques to work around this behavior. + +SAVEPOINT Support +---------------------------- + +SQLite supports SAVEPOINTs, which only function once a transaction is +begun. SQLAlchemy's SAVEPOINT support is available using the +:meth:`.Connection.begin_nested` method at the Core level, and +:meth:`.Session.begin_nested` at the ORM level. However, SAVEPOINTs +won't work at all with pysqlite unless workarounds are taken. + +.. warning:: + + SQLite's SAVEPOINT feature is impacted by unresolved + issues in the pysqlite driver, which defers BEGIN statements to a greater + degree than is often feasible. See the section :ref:`pysqlite_serializable` + for techniques to work around this behavior. + +Transactional DDL +---------------------------- + +The SQLite database supports transactional :term:`DDL` as well. +In this case, the pysqlite driver is not only failing to start transactions, +it also is ending any existing transction when DDL is detected, so again, +workarounds are required. + +.. warning:: + + SQLite's transactional DDL is impacted by unresolved issues + in the pysqlite driver, which fails to emit BEGIN and additionally + forces a COMMIT to cancel any transaction when DDL is encountered. + See the section :ref:`pysqlite_serializable` + for techniques to work around this behavior. + +.. _sqlite_foreign_keys: + +Foreign Key Support +------------------- + +SQLite supports FOREIGN KEY syntax when emitting CREATE statements for tables, +however by default these constraints have no effect on the operation of the +table. + +Constraint checking on SQLite has three prerequisites: + +* At least version 3.6.19 of SQLite must be in use +* The SQLite library must be compiled *without* the SQLITE_OMIT_FOREIGN_KEY + or SQLITE_OMIT_TRIGGER symbols enabled. +* The ``PRAGMA foreign_keys = ON`` statement must be emitted on all + connections before use. + +SQLAlchemy allows for the ``PRAGMA`` statement to be emitted automatically for +new connections through the usage of events:: + + from sqlalchemy.engine import Engine + from sqlalchemy import event + + @event.listens_for(Engine, "connect") + def set_sqlite_pragma(dbapi_connection, connection_record): + cursor = dbapi_connection.cursor() + cursor.execute("PRAGMA foreign_keys=ON") + cursor.close() + +.. warning:: + + When SQLite foreign keys are enabled, it is **not possible** + to emit CREATE or DROP statements for tables that contain + mutually-dependent foreign key constraints; + to emit the DDL for these tables requires that ALTER TABLE be used to + create or drop these constraints separately, for which SQLite has + no support. + +.. seealso:: + + `SQLite Foreign Key Support <http://www.sqlite.org/foreignkeys.html>`_ + - on the SQLite web site. + + :ref:`event_toplevel` - SQLAlchemy event API. + + :ref:`use_alter` - more information on SQLAlchemy's facilities for handling + mutually-dependent foreign key constraints. + +.. _sqlite_type_reflection: + +Type Reflection +--------------- + +SQLite types are unlike those of most other database backends, in that +the string name of the type usually does not correspond to a "type" in a +one-to-one fashion. Instead, SQLite links per-column typing behavior +to one of five so-called "type affinities" based on a string matching +pattern for the type. + +SQLAlchemy's reflection process, when inspecting types, uses a simple +lookup table to link the keywords returned to provided SQLAlchemy types. +This lookup table is present within the SQLite dialect as it is for all +other dialects. However, the SQLite dialect has a different "fallback" +routine for when a particular type name is not located in the lookup map; +it instead implements the SQLite "type affinity" scheme located at +http://www.sqlite.org/datatype3.html section 2.1. + +The provided typemap will make direct associations from an exact string +name match for the following types: + +:class:`~.types.BIGINT`, :class:`~.types.BLOB`, +:class:`~.types.BOOLEAN`, :class:`~.types.BOOLEAN`, +:class:`~.types.CHAR`, :class:`~.types.DATE`, +:class:`~.types.DATETIME`, :class:`~.types.FLOAT`, +:class:`~.types.DECIMAL`, :class:`~.types.FLOAT`, +:class:`~.types.INTEGER`, :class:`~.types.INTEGER`, +:class:`~.types.NUMERIC`, :class:`~.types.REAL`, +:class:`~.types.SMALLINT`, :class:`~.types.TEXT`, +:class:`~.types.TIME`, :class:`~.types.TIMESTAMP`, +:class:`~.types.VARCHAR`, :class:`~.types.NVARCHAR`, +:class:`~.types.NCHAR` + +When a type name does not match one of the above types, the "type affinity" +lookup is used instead: + +* :class:`~.types.INTEGER` is returned if the type name includes the + string ``INT`` +* :class:`~.types.TEXT` is returned if the type name includes the + string ``CHAR``, ``CLOB`` or ``TEXT`` +* :class:`~.types.NullType` is returned if the type name includes the + string ``BLOB`` +* :class:`~.types.REAL` is returned if the type name includes the string + ``REAL``, ``FLOA`` or ``DOUB``. +* Otherwise, the :class:`~.types.NUMERIC` type is used. + +.. versionadded:: 0.9.3 Support for SQLite type affinity rules when reflecting + columns. + + +.. _sqlite_partial_index: + +Partial Indexes +--------------- + +A partial index, e.g. one which uses a WHERE clause, can be specified +with the DDL system using the argument ``sqlite_where``:: + + tbl = Table('testtbl', m, Column('data', Integer)) + idx = Index('test_idx1', tbl.c.data, + sqlite_where=and_(tbl.c.data > 5, tbl.c.data < 10)) + +The index will be rendered at create time as:: + + CREATE INDEX test_idx1 ON testtbl (data) + WHERE data > 5 AND data < 10 + +.. versionadded:: 0.9.9 + +.. _sqlite_dotted_column_names: + +Dotted Column Names +------------------- + +Using table or column names that explicitly have periods in them is +**not recommended**. While this is generally a bad idea for relational +databases in general, as the dot is a syntactically significant character, +the SQLite driver up until version **3.10.0** of SQLite has a bug which +requires that SQLAlchemy filter out these dots in result sets. + +.. versionchanged:: 1.1 + + The following SQLite issue has been resolved as of version 3.10.0 + of SQLite. SQLAlchemy as of **1.1** automatically disables its internal + workarounds based on detection of this version. + +The bug, entirely outside of SQLAlchemy, can be illustrated thusly:: + + import sqlite3 + + assert sqlite3.sqlite_version_info < (3, 10, 0), "bug is fixed in this version" + + conn = sqlite3.connect(":memory:") + cursor = conn.cursor() + + cursor.execute("create table x (a integer, b integer)") + cursor.execute("insert into x (a, b) values (1, 1)") + cursor.execute("insert into x (a, b) values (2, 2)") + + cursor.execute("select x.a, x.b from x") + assert [c[0] for c in cursor.description] == ['a', 'b'] + + cursor.execute(''' + select x.a, x.b from x where a=1 + union + select x.a, x.b from x where a=2 + ''') + assert [c[0] for c in cursor.description] == ['a', 'b'], \ + [c[0] for c in cursor.description] + +The second assertion fails:: + + Traceback (most recent call last): + File "test.py", line 19, in <module> + [c[0] for c in cursor.description] + AssertionError: ['x.a', 'x.b'] + +Where above, the driver incorrectly reports the names of the columns +including the name of the table, which is entirely inconsistent vs. +when the UNION is not present. + +SQLAlchemy relies upon column names being predictable in how they match +to the original statement, so the SQLAlchemy dialect has no choice but +to filter these out:: + + + from sqlalchemy import create_engine + + eng = create_engine("sqlite://") + conn = eng.connect() + + conn.execute("create table x (a integer, b integer)") + conn.execute("insert into x (a, b) values (1, 1)") + conn.execute("insert into x (a, b) values (2, 2)") + + result = conn.execute("select x.a, x.b from x") + assert result.keys() == ["a", "b"] + + result = conn.execute(''' + select x.a, x.b from x where a=1 + union + select x.a, x.b from x where a=2 + ''') + assert result.keys() == ["a", "b"] + +Note that above, even though SQLAlchemy filters out the dots, *both +names are still addressable*:: + + >>> row = result.first() + >>> row["a"] + 1 + >>> row["x.a"] + 1 + >>> row["b"] + 1 + >>> row["x.b"] + 1 + +Therefore, the workaround applied by SQLAlchemy only impacts +:meth:`.ResultProxy.keys` and :meth:`.RowProxy.keys()` in the public API. +In the very specific case where +an application is forced to use column names that contain dots, and the +functionality of :meth:`.ResultProxy.keys` and :meth:`.RowProxy.keys()` +is required to return these dotted names unmodified, the ``sqlite_raw_colnames`` +execution option may be provided, either on a per-:class:`.Connection` basis:: + + result = conn.execution_options(sqlite_raw_colnames=True).execute(''' + select x.a, x.b from x where a=1 + union + select x.a, x.b from x where a=2 + ''') + assert result.keys() == ["x.a", "x.b"] + +or on a per-:class:`.Engine` basis:: + + engine = create_engine("sqlite://", execution_options={"sqlite_raw_colnames": True}) + +When using the per-:class:`.Engine` execution option, note that +**Core and ORM queries that use UNION may not function properly**. + +""" + +import datetime +import re + +from ... import processors +from ... import sql, exc +from ... import types as sqltypes, schema as sa_schema +from ... import util +from ...engine import default, reflection +from ...sql import compiler + +from ...types import (BLOB, BOOLEAN, CHAR, DECIMAL, FLOAT, + INTEGER, REAL, NUMERIC, SMALLINT, TEXT, + TIMESTAMP, VARCHAR) + + +class _DateTimeMixin(object): + _reg = None + _storage_format = None + + def __init__(self, storage_format=None, regexp=None, **kw): + super(_DateTimeMixin, self).__init__(**kw) + if regexp is not None: + self._reg = re.compile(regexp) + if storage_format is not None: + self._storage_format = storage_format + + @property + def format_is_text_affinity(self): + """return True if the storage format will automatically imply + a TEXT affinity. + + If the storage format contains no non-numeric characters, + it will imply a NUMERIC storage format on SQLite; in this case, + the type will generate its DDL as DATE_CHAR, DATETIME_CHAR, + TIME_CHAR. + + .. versionadded:: 1.0.0 + + """ + spec = self._storage_format % { + "year": 0, "month": 0, "day": 0, "hour": 0, + "minute": 0, "second": 0, "microsecond": 0 + } + return bool(re.search(r'[^0-9]', spec)) + + def adapt(self, cls, **kw): + if issubclass(cls, _DateTimeMixin): + if self._storage_format: + kw["storage_format"] = self._storage_format + if self._reg: + kw["regexp"] = self._reg + return super(_DateTimeMixin, self).adapt(cls, **kw) + + def literal_processor(self, dialect): + bp = self.bind_processor(dialect) + + def process(value): + return "'%s'" % bp(value) + return process + + +class DATETIME(_DateTimeMixin, sqltypes.DateTime): + r"""Represent a Python datetime object in SQLite using a string. + + The default string storage format is:: + + "%(year)04d-%(month)02d-%(day)02d %(hour)02d:%(min)02d:%(second)02d.%(microsecond)06d" + + e.g.:: + + 2011-03-15 12:05:57.10558 + + The storage format can be customized to some degree using the + ``storage_format`` and ``regexp`` parameters, such as:: + + import re + from sqlalchemy.dialects.sqlite import DATETIME + + dt = DATETIME(storage_format="%(year)04d/%(month)02d/%(day)02d " + "%(hour)02d:%(min)02d:%(second)02d", + regexp=r"(\d+)/(\d+)/(\d+) (\d+)-(\d+)-(\d+)" + ) + + :param storage_format: format string which will be applied to the dict + with keys year, month, day, hour, minute, second, and microsecond. + + :param regexp: regular expression which will be applied to incoming result + rows. If the regexp contains named groups, the resulting match dict is + applied to the Python datetime() constructor as keyword arguments. + Otherwise, if positional groups are used, the datetime() constructor + is called with positional arguments via + ``*map(int, match_obj.groups(0))``. + + """ + + _storage_format = ( + "%(year)04d-%(month)02d-%(day)02d " + "%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d" + ) + + def __init__(self, *args, **kwargs): + truncate_microseconds = kwargs.pop('truncate_microseconds', False) + super(DATETIME, self).__init__(*args, **kwargs) + if truncate_microseconds: + assert 'storage_format' not in kwargs, "You can specify only "\ + "one of truncate_microseconds or storage_format." + assert 'regexp' not in kwargs, "You can specify only one of "\ + "truncate_microseconds or regexp." + self._storage_format = ( + "%(year)04d-%(month)02d-%(day)02d " + "%(hour)02d:%(minute)02d:%(second)02d" + ) + + def bind_processor(self, dialect): + datetime_datetime = datetime.datetime + datetime_date = datetime.date + format = self._storage_format + + def process(value): + if value is None: + return None + elif isinstance(value, datetime_datetime): + return format % { + 'year': value.year, + 'month': value.month, + 'day': value.day, + 'hour': value.hour, + 'minute': value.minute, + 'second': value.second, + 'microsecond': value.microsecond, + } + elif isinstance(value, datetime_date): + return format % { + 'year': value.year, + 'month': value.month, + 'day': value.day, + 'hour': 0, + 'minute': 0, + 'second': 0, + 'microsecond': 0, + } + else: + raise TypeError("SQLite DateTime type only accepts Python " + "datetime and date objects as input.") + return process + + def result_processor(self, dialect, coltype): + if self._reg: + return processors.str_to_datetime_processor_factory( + self._reg, datetime.datetime) + else: + return processors.str_to_datetime + + +class DATE(_DateTimeMixin, sqltypes.Date): + r"""Represent a Python date object in SQLite using a string. + + The default string storage format is:: + + "%(year)04d-%(month)02d-%(day)02d" + + e.g.:: + + 2011-03-15 + + The storage format can be customized to some degree using the + ``storage_format`` and ``regexp`` parameters, such as:: + + import re + from sqlalchemy.dialects.sqlite import DATE + + d = DATE( + storage_format="%(month)02d/%(day)02d/%(year)04d", + regexp=re.compile("(?P<month>\d+)/(?P<day>\d+)/(?P<year>\d+)") + ) + + :param storage_format: format string which will be applied to the + dict with keys year, month, and day. + + :param regexp: regular expression which will be applied to + incoming result rows. If the regexp contains named groups, the + resulting match dict is applied to the Python date() constructor + as keyword arguments. Otherwise, if positional groups are used, the + date() constructor is called with positional arguments via + ``*map(int, match_obj.groups(0))``. + """ + + _storage_format = "%(year)04d-%(month)02d-%(day)02d" + + def bind_processor(self, dialect): + datetime_date = datetime.date + format = self._storage_format + + def process(value): + if value is None: + return None + elif isinstance(value, datetime_date): + return format % { + 'year': value.year, + 'month': value.month, + 'day': value.day, + } + else: + raise TypeError("SQLite Date type only accepts Python " + "date objects as input.") + return process + + def result_processor(self, dialect, coltype): + if self._reg: + return processors.str_to_datetime_processor_factory( + self._reg, datetime.date) + else: + return processors.str_to_date + + +class TIME(_DateTimeMixin, sqltypes.Time): + r"""Represent a Python time object in SQLite using a string. + + The default string storage format is:: + + "%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d" + + e.g.:: + + 12:05:57.10558 + + The storage format can be customized to some degree using the + ``storage_format`` and ``regexp`` parameters, such as:: + + import re + from sqlalchemy.dialects.sqlite import TIME + + t = TIME(storage_format="%(hour)02d-%(minute)02d-" + "%(second)02d-%(microsecond)06d", + regexp=re.compile("(\d+)-(\d+)-(\d+)-(?:-(\d+))?") + ) + + :param storage_format: format string which will be applied to the dict + with keys hour, minute, second, and microsecond. + + :param regexp: regular expression which will be applied to incoming result + rows. If the regexp contains named groups, the resulting match dict is + applied to the Python time() constructor as keyword arguments. Otherwise, + if positional groups are used, the time() constructor is called with + positional arguments via ``*map(int, match_obj.groups(0))``. + """ + + _storage_format = "%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d" + + def __init__(self, *args, **kwargs): + truncate_microseconds = kwargs.pop('truncate_microseconds', False) + super(TIME, self).__init__(*args, **kwargs) + if truncate_microseconds: + assert 'storage_format' not in kwargs, "You can specify only "\ + "one of truncate_microseconds or storage_format." + assert 'regexp' not in kwargs, "You can specify only one of "\ + "truncate_microseconds or regexp." + self._storage_format = "%(hour)02d:%(minute)02d:%(second)02d" + + def bind_processor(self, dialect): + datetime_time = datetime.time + format = self._storage_format + + def process(value): + if value is None: + return None + elif isinstance(value, datetime_time): + return format % { + 'hour': value.hour, + 'minute': value.minute, + 'second': value.second, + 'microsecond': value.microsecond, + } + else: + raise TypeError("SQLite Time type only accepts Python " + "time objects as input.") + return process + + def result_processor(self, dialect, coltype): + if self._reg: + return processors.str_to_datetime_processor_factory( + self._reg, datetime.time) + else: + return processors.str_to_time + +colspecs = { + sqltypes.Date: DATE, + sqltypes.DateTime: DATETIME, + sqltypes.Time: TIME, +} + +ischema_names = { + 'BIGINT': sqltypes.BIGINT, + 'BLOB': sqltypes.BLOB, + 'BOOL': sqltypes.BOOLEAN, + 'BOOLEAN': sqltypes.BOOLEAN, + 'CHAR': sqltypes.CHAR, + 'DATE': sqltypes.DATE, + 'DATE_CHAR': sqltypes.DATE, + 'DATETIME': sqltypes.DATETIME, + 'DATETIME_CHAR': sqltypes.DATETIME, + 'DOUBLE': sqltypes.FLOAT, + 'DECIMAL': sqltypes.DECIMAL, + 'FLOAT': sqltypes.FLOAT, + 'INT': sqltypes.INTEGER, + 'INTEGER': sqltypes.INTEGER, + 'NUMERIC': sqltypes.NUMERIC, + 'REAL': sqltypes.REAL, + 'SMALLINT': sqltypes.SMALLINT, + 'TEXT': sqltypes.TEXT, + 'TIME': sqltypes.TIME, + 'TIME_CHAR': sqltypes.TIME, + 'TIMESTAMP': sqltypes.TIMESTAMP, + 'VARCHAR': sqltypes.VARCHAR, + 'NVARCHAR': sqltypes.NVARCHAR, + 'NCHAR': sqltypes.NCHAR, +} + + +class SQLiteCompiler(compiler.SQLCompiler): + extract_map = util.update_copy( + compiler.SQLCompiler.extract_map, + { + 'month': '%m', + 'day': '%d', + 'year': '%Y', + 'second': '%S', + 'hour': '%H', + 'doy': '%j', + 'minute': '%M', + 'epoch': '%s', + 'dow': '%w', + 'week': '%W', + }) + + def visit_now_func(self, fn, **kw): + return "CURRENT_TIMESTAMP" + + def visit_localtimestamp_func(self, func, **kw): + return 'DATETIME(CURRENT_TIMESTAMP, "localtime")' + + def visit_true(self, expr, **kw): + return '1' + + def visit_false(self, expr, **kw): + return '0' + + def visit_char_length_func(self, fn, **kw): + return "length%s" % self.function_argspec(fn) + + def visit_cast(self, cast, **kwargs): + if self.dialect.supports_cast: + return super(SQLiteCompiler, self).visit_cast(cast, **kwargs) + else: + return self.process(cast.clause, **kwargs) + + def visit_extract(self, extract, **kw): + try: + return "CAST(STRFTIME('%s', %s) AS INTEGER)" % ( + self.extract_map[extract.field], + self.process(extract.expr, **kw) + ) + except KeyError: + raise exc.CompileError( + "%s is not a valid extract argument." % extract.field) + + def limit_clause(self, select, **kw): + text = "" + if select._limit_clause is not None: + text += "\n LIMIT " + self.process(select._limit_clause, **kw) + if select._offset_clause is not None: + if select._limit_clause is None: + text += "\n LIMIT " + self.process(sql.literal(-1)) + text += " OFFSET " + self.process(select._offset_clause, **kw) + else: + text += " OFFSET " + self.process(sql.literal(0), **kw) + return text + + def for_update_clause(self, select, **kw): + # sqlite has no "FOR UPDATE" AFAICT + return '' + + def visit_is_distinct_from_binary(self, binary, operator, **kw): + return "%s IS NOT %s" % (self.process(binary.left), + self.process(binary.right)) + + def visit_isnot_distinct_from_binary(self, binary, operator, **kw): + return "%s IS %s" % (self.process(binary.left), + self.process(binary.right)) + + +class SQLiteDDLCompiler(compiler.DDLCompiler): + + def get_column_specification(self, column, **kwargs): + coltype = self.dialect.type_compiler.process( + column.type, type_expression=column) + colspec = self.preparer.format_column(column) + " " + coltype + default = self.get_column_default_string(column) + if default is not None: + colspec += " DEFAULT " + default + + if not column.nullable: + colspec += " NOT NULL" + + if column.primary_key: + if ( + column.autoincrement is True and + len(column.table.primary_key.columns) != 1 + ): + raise exc.CompileError( + "SQLite does not support autoincrement for " + "composite primary keys") + + if (column.table.dialect_options['sqlite']['autoincrement'] and + len(column.table.primary_key.columns) == 1 and + issubclass(column.type._type_affinity, sqltypes.Integer) and + not column.foreign_keys): + colspec += " PRIMARY KEY AUTOINCREMENT" + + return colspec + + def visit_primary_key_constraint(self, constraint): + # for columns with sqlite_autoincrement=True, + # the PRIMARY KEY constraint can only be inline + # with the column itself. + if len(constraint.columns) == 1: + c = list(constraint)[0] + if (c.primary_key and + c.table.dialect_options['sqlite']['autoincrement'] and + issubclass(c.type._type_affinity, sqltypes.Integer) and + not c.foreign_keys): + return None + + return super(SQLiteDDLCompiler, self).visit_primary_key_constraint( + constraint) + + def visit_foreign_key_constraint(self, constraint): + + local_table = constraint.elements[0].parent.table + remote_table = constraint.elements[0].column.table + + if local_table.schema != remote_table.schema: + return None + else: + return super( + SQLiteDDLCompiler, + self).visit_foreign_key_constraint(constraint) + + def define_constraint_remote_table(self, constraint, table, preparer): + """Format the remote table clause of a CREATE CONSTRAINT clause.""" + + return preparer.format_table(table, use_schema=False) + + def visit_create_index(self, create, include_schema=False, + include_table_schema=True): + index = create.element + self._verify_index_table(index) + preparer = self.preparer + text = "CREATE " + if index.unique: + text += "UNIQUE " + text += "INDEX %s ON %s (%s)" \ + % ( + self._prepared_index_name(index, + include_schema=True), + preparer.format_table(index.table, + use_schema=False), + ', '.join( + self.sql_compiler.process( + expr, include_table=False, literal_binds=True) for + expr in index.expressions) + ) + + whereclause = index.dialect_options["sqlite"]["where"] + if whereclause is not None: + where_compiled = self.sql_compiler.process( + whereclause, include_table=False, + literal_binds=True) + text += " WHERE " + where_compiled + + return text + + +class SQLiteTypeCompiler(compiler.GenericTypeCompiler): + def visit_large_binary(self, type_, **kw): + return self.visit_BLOB(type_) + + def visit_DATETIME(self, type_, **kw): + if not isinstance(type_, _DateTimeMixin) or \ + type_.format_is_text_affinity: + return super(SQLiteTypeCompiler, self).visit_DATETIME(type_) + else: + return "DATETIME_CHAR" + + def visit_DATE(self, type_, **kw): + if not isinstance(type_, _DateTimeMixin) or \ + type_.format_is_text_affinity: + return super(SQLiteTypeCompiler, self).visit_DATE(type_) + else: + return "DATE_CHAR" + + def visit_TIME(self, type_, **kw): + if not isinstance(type_, _DateTimeMixin) or \ + type_.format_is_text_affinity: + return super(SQLiteTypeCompiler, self).visit_TIME(type_) + else: + return "TIME_CHAR" + + +class SQLiteIdentifierPreparer(compiler.IdentifierPreparer): + reserved_words = set([ + 'add', 'after', 'all', 'alter', 'analyze', 'and', 'as', 'asc', + 'attach', 'autoincrement', 'before', 'begin', 'between', 'by', + 'cascade', 'case', 'cast', 'check', 'collate', 'column', 'commit', + 'conflict', 'constraint', 'create', 'cross', 'current_date', + 'current_time', 'current_timestamp', 'database', 'default', + 'deferrable', 'deferred', 'delete', 'desc', 'detach', 'distinct', + 'drop', 'each', 'else', 'end', 'escape', 'except', 'exclusive', + 'explain', 'false', 'fail', 'for', 'foreign', 'from', 'full', 'glob', + 'group', 'having', 'if', 'ignore', 'immediate', 'in', 'index', + 'indexed', 'initially', 'inner', 'insert', 'instead', 'intersect', + 'into', 'is', 'isnull', 'join', 'key', 'left', 'like', 'limit', + 'match', 'natural', 'not', 'notnull', 'null', 'of', 'offset', 'on', + 'or', 'order', 'outer', 'plan', 'pragma', 'primary', 'query', + 'raise', 'references', 'reindex', 'rename', 'replace', 'restrict', + 'right', 'rollback', 'row', 'select', 'set', 'table', 'temp', + 'temporary', 'then', 'to', 'transaction', 'trigger', 'true', 'union', + 'unique', 'update', 'using', 'vacuum', 'values', 'view', 'virtual', + 'when', 'where', + ]) + + def format_index(self, index, use_schema=True, name=None): + """Prepare a quoted index and schema name.""" + + if name is None: + name = index.name + result = self.quote(name, index.quote) + if (not self.omit_schema and + use_schema and + getattr(index.table, "schema", None)): + result = self.quote_schema( + index.table.schema, index.table.quote_schema) + "." + result + return result + + +class SQLiteExecutionContext(default.DefaultExecutionContext): + @util.memoized_property + def _preserve_raw_colnames(self): + return not self.dialect._broken_dotted_colnames or \ + self.execution_options.get("sqlite_raw_colnames", False) + + def _translate_colname(self, colname): + # TODO: detect SQLite version 3.10.0 or greater; + # see [ticket:3633] + + # adjust for dotted column names. SQLite + # in the case of UNION may store col names as + # "tablename.colname", or if using an attached database, + # "database.tablename.colname", in cursor.description + if not self._preserve_raw_colnames and "." in colname: + return colname.split(".")[-1], colname + else: + return colname, None + + +class SQLiteDialect(default.DefaultDialect): + name = 'sqlite' + supports_alter = False + supports_unicode_statements = True + supports_unicode_binds = True + supports_default_values = True + supports_empty_insert = False + supports_cast = True + supports_multivalues_insert = True + + default_paramstyle = 'qmark' + execution_ctx_cls = SQLiteExecutionContext + statement_compiler = SQLiteCompiler + ddl_compiler = SQLiteDDLCompiler + type_compiler = SQLiteTypeCompiler + preparer = SQLiteIdentifierPreparer + ischema_names = ischema_names + colspecs = colspecs + isolation_level = None + + supports_cast = True + supports_default_values = True + + construct_arguments = [ + (sa_schema.Table, { + "autoincrement": False + }), + (sa_schema.Index, { + "where": None, + }), + ] + + _broken_fk_pragma_quotes = False + _broken_dotted_colnames = False + + def __init__(self, isolation_level=None, native_datetime=False, **kwargs): + default.DefaultDialect.__init__(self, **kwargs) + self.isolation_level = isolation_level + + # this flag used by pysqlite dialect, and perhaps others in the + # future, to indicate the driver is handling date/timestamp + # conversions (and perhaps datetime/time as well on some hypothetical + # driver ?) + self.native_datetime = native_datetime + + if self.dbapi is not None: + self.supports_right_nested_joins = ( + self.dbapi.sqlite_version_info >= (3, 7, 16)) + self._broken_dotted_colnames = ( + self.dbapi.sqlite_version_info < (3, 10, 0) + ) + self.supports_default_values = ( + self.dbapi.sqlite_version_info >= (3, 3, 8)) + self.supports_cast = ( + self.dbapi.sqlite_version_info >= (3, 2, 3)) + self.supports_multivalues_insert = ( + # http://www.sqlite.org/releaselog/3_7_11.html + self.dbapi.sqlite_version_info >= (3, 7, 11)) + # see http://www.sqlalchemy.org/trac/ticket/2568 + # as well as http://www.sqlite.org/src/info/600482d161 + self._broken_fk_pragma_quotes = ( + self.dbapi.sqlite_version_info < (3, 6, 14)) + + _isolation_lookup = { + 'READ UNCOMMITTED': 1, + 'SERIALIZABLE': 0, + } + + def set_isolation_level(self, connection, level): + try: + isolation_level = self._isolation_lookup[level.replace('_', ' ')] + except KeyError: + raise exc.ArgumentError( + "Invalid value '%s' for isolation_level. " + "Valid isolation levels for %s are %s" % + (level, self.name, ", ".join(self._isolation_lookup)) + ) + cursor = connection.cursor() + cursor.execute("PRAGMA read_uncommitted = %d" % isolation_level) + cursor.close() + + def get_isolation_level(self, connection): + cursor = connection.cursor() + cursor.execute('PRAGMA read_uncommitted') + res = cursor.fetchone() + if res: + value = res[0] + else: + # http://www.sqlite.org/changes.html#version_3_3_3 + # "Optional READ UNCOMMITTED isolation (instead of the + # default isolation level of SERIALIZABLE) and + # table level locking when database connections + # share a common cache."" + # pre-SQLite 3.3.0 default to 0 + value = 0 + cursor.close() + if value == 0: + return "SERIALIZABLE" + elif value == 1: + return "READ UNCOMMITTED" + else: + assert False, "Unknown isolation level %s" % value + + def on_connect(self): + if self.isolation_level is not None: + def connect(conn): + self.set_isolation_level(conn, self.isolation_level) + return connect + else: + return None + + @reflection.cache + def get_schema_names(self, connection, **kw): + s = "PRAGMA database_list" + dl = connection.execute(s) + + return [db[1] for db in dl if db[1] != "temp"] + + @reflection.cache + def get_table_names(self, connection, schema=None, **kw): + if schema is not None: + qschema = self.identifier_preparer.quote_identifier(schema) + master = '%s.sqlite_master' % qschema + else: + master = "sqlite_master" + s = ("SELECT name FROM %s " + "WHERE type='table' ORDER BY name") % (master,) + rs = connection.execute(s) + return [row[0] for row in rs] + + @reflection.cache + def get_temp_table_names(self, connection, **kw): + s = "SELECT name FROM sqlite_temp_master "\ + "WHERE type='table' ORDER BY name " + rs = connection.execute(s) + + return [row[0] for row in rs] + + @reflection.cache + def get_temp_view_names(self, connection, **kw): + s = "SELECT name FROM sqlite_temp_master "\ + "WHERE type='view' ORDER BY name " + rs = connection.execute(s) + + return [row[0] for row in rs] + + def has_table(self, connection, table_name, schema=None): + info = self._get_table_pragma( + connection, "table_info", table_name, schema=schema) + return bool(info) + + @reflection.cache + def get_view_names(self, connection, schema=None, **kw): + if schema is not None: + qschema = self.identifier_preparer.quote_identifier(schema) + master = '%s.sqlite_master' % qschema + else: + master = "sqlite_master" + s = ("SELECT name FROM %s " + "WHERE type='view' ORDER BY name") % (master,) + rs = connection.execute(s) + + return [row[0] for row in rs] + + @reflection.cache + def get_view_definition(self, connection, view_name, schema=None, **kw): + if schema is not None: + qschema = self.identifier_preparer.quote_identifier(schema) + master = '%s.sqlite_master' % qschema + s = ("SELECT sql FROM %s WHERE name = '%s'" + "AND type='view'") % (master, view_name) + rs = connection.execute(s) + else: + try: + s = ("SELECT sql FROM " + " (SELECT * FROM sqlite_master UNION ALL " + " SELECT * FROM sqlite_temp_master) " + "WHERE name = '%s' " + "AND type='view'") % view_name + rs = connection.execute(s) + except exc.DBAPIError: + s = ("SELECT sql FROM sqlite_master WHERE name = '%s' " + "AND type='view'") % view_name + rs = connection.execute(s) + + result = rs.fetchall() + if result: + return result[0].sql + + @reflection.cache + def get_columns(self, connection, table_name, schema=None, **kw): + info = self._get_table_pragma( + connection, "table_info", table_name, schema=schema) + + columns = [] + for row in info: + (name, type_, nullable, default, primary_key) = ( + row[1], row[2].upper(), not row[3], row[4], row[5]) + + columns.append(self._get_column_info(name, type_, nullable, + default, primary_key)) + return columns + + def _get_column_info(self, name, type_, nullable, default, primary_key): + coltype = self._resolve_type_affinity(type_) + + if default is not None: + default = util.text_type(default) + + return { + 'name': name, + 'type': coltype, + 'nullable': nullable, + 'default': default, + 'autoincrement': 'auto', + 'primary_key': primary_key, + } + + def _resolve_type_affinity(self, type_): + """Return a data type from a reflected column, using affinity tules. + + SQLite's goal for universal compatibility introduces some complexity + during reflection, as a column's defined type might not actually be a + type that SQLite understands - or indeed, my not be defined *at all*. + Internally, SQLite handles this with a 'data type affinity' for each + column definition, mapping to one of 'TEXT', 'NUMERIC', 'INTEGER', + 'REAL', or 'NONE' (raw bits). The algorithm that determines this is + listed in http://www.sqlite.org/datatype3.html section 2.1. + + This method allows SQLAlchemy to support that algorithm, while still + providing access to smarter reflection utilities by regcognizing + column definitions that SQLite only supports through affinity (like + DATE and DOUBLE). + + """ + match = re.match(r'([\w ]+)(\(.*?\))?', type_) + if match: + coltype = match.group(1) + args = match.group(2) + else: + coltype = '' + args = '' + + if coltype in self.ischema_names: + coltype = self.ischema_names[coltype] + elif 'INT' in coltype: + coltype = sqltypes.INTEGER + elif 'CHAR' in coltype or 'CLOB' in coltype or 'TEXT' in coltype: + coltype = sqltypes.TEXT + elif 'BLOB' in coltype or not coltype: + coltype = sqltypes.NullType + elif 'REAL' in coltype or 'FLOA' in coltype or 'DOUB' in coltype: + coltype = sqltypes.REAL + else: + coltype = sqltypes.NUMERIC + + if args is not None: + args = re.findall(r'(\d+)', args) + try: + coltype = coltype(*[int(a) for a in args]) + except TypeError: + util.warn( + "Could not instantiate type %s with " + "reflected arguments %s; using no arguments." % + (coltype, args)) + coltype = coltype() + else: + coltype = coltype() + + return coltype + + @reflection.cache + def get_pk_constraint(self, connection, table_name, schema=None, **kw): + constraint_name = None + table_data = self._get_table_sql(connection, table_name, schema=schema) + if table_data: + PK_PATTERN = r'CONSTRAINT (\w+) PRIMARY KEY' + result = re.search(PK_PATTERN, table_data, re.I) + constraint_name = result.group(1) if result else None + + cols = self.get_columns(connection, table_name, schema, **kw) + pkeys = [] + for col in cols: + if col['primary_key']: + pkeys.append(col['name']) + + return {'constrained_columns': pkeys, 'name': constraint_name} + + @reflection.cache + def get_foreign_keys(self, connection, table_name, schema=None, **kw): + # sqlite makes this *extremely difficult*. + # First, use the pragma to get the actual FKs. + pragma_fks = self._get_table_pragma( + connection, "foreign_key_list", + table_name, schema=schema + ) + + fks = {} + + for row in pragma_fks: + (numerical_id, rtbl, lcol, rcol) = ( + row[0], row[2], row[3], row[4]) + + if rcol is None: + rcol = lcol + + if self._broken_fk_pragma_quotes: + rtbl = re.sub(r'^[\"\[`\']|[\"\]`\']$', '', rtbl) + + if numerical_id in fks: + fk = fks[numerical_id] + else: + fk = fks[numerical_id] = { + 'name': None, + 'constrained_columns': [], + 'referred_schema': schema, + 'referred_table': rtbl, + 'referred_columns': [], + 'options': {} + } + fks[numerical_id] = fk + + fk['constrained_columns'].append(lcol) + fk['referred_columns'].append(rcol) + + def fk_sig(constrained_columns, referred_table, referred_columns): + return tuple(constrained_columns) + (referred_table,) + \ + tuple(referred_columns) + + # then, parse the actual SQL and attempt to find DDL that matches + # the names as well. SQLite saves the DDL in whatever format + # it was typed in as, so need to be liberal here. + + keys_by_signature = dict( + ( + fk_sig( + fk['constrained_columns'], + fk['referred_table'], fk['referred_columns']), + fk + ) for fk in fks.values() + ) + + table_data = self._get_table_sql(connection, table_name, schema=schema) + if table_data is None: + # system tables, etc. + return [] + + def parse_fks(): + FK_PATTERN = ( + r'(?:CONSTRAINT (\w+) +)?' + r'FOREIGN KEY *\( *(.+?) *\) +' + r'REFERENCES +(?:(?:"(.+?)")|([a-z0-9_]+)) *\((.+?)\) *' + r'((?:ON (?:DELETE|UPDATE) ' + r'(?:SET NULL|SET DEFAULT|CASCADE|RESTRICT|NO ACTION) *)*)' + ) + for match in re.finditer(FK_PATTERN, table_data, re.I): + ( + constraint_name, constrained_columns, + referred_quoted_name, referred_name, + referred_columns, onupdatedelete) = \ + match.group(1, 2, 3, 4, 5, 6) + constrained_columns = list( + self._find_cols_in_sig(constrained_columns)) + if not referred_columns: + referred_columns = constrained_columns + else: + referred_columns = list( + self._find_cols_in_sig(referred_columns)) + referred_name = referred_quoted_name or referred_name + options = {} + + for token in re.split(r" *\bON\b *", onupdatedelete.upper()): + if token.startswith("DELETE"): + options['ondelete'] = token[6:].strip() + elif token.startswith("UPDATE"): + options["onupdate"] = token[6:].strip() + yield ( + constraint_name, constrained_columns, + referred_name, referred_columns, options) + fkeys = [] + + for ( + constraint_name, constrained_columns, + referred_name, referred_columns, options) in parse_fks(): + sig = fk_sig( + constrained_columns, referred_name, referred_columns) + if sig not in keys_by_signature: + util.warn( + "WARNING: SQL-parsed foreign key constraint " + "'%s' could not be located in PRAGMA " + "foreign_keys for table %s" % ( + sig, + table_name + )) + continue + key = keys_by_signature.pop(sig) + key['name'] = constraint_name + key['options'] = options + fkeys.append(key) + # assume the remainders are the unnamed, inline constraints, just + # use them as is as it's extremely difficult to parse inline + # constraints + fkeys.extend(keys_by_signature.values()) + return fkeys + + def _find_cols_in_sig(self, sig): + for match in re.finditer(r'(?:"(.+?)")|([a-z0-9_]+)', sig, re.I): + yield match.group(1) or match.group(2) + + @reflection.cache + def get_unique_constraints(self, connection, table_name, + schema=None, **kw): + + auto_index_by_sig = {} + for idx in self.get_indexes( + connection, table_name, schema=schema, + include_auto_indexes=True, **kw): + if not idx['name'].startswith("sqlite_autoindex"): + continue + sig = tuple(idx['column_names']) + auto_index_by_sig[sig] = idx + + table_data = self._get_table_sql( + connection, table_name, schema=schema, **kw) + if not table_data: + return [] + + unique_constraints = [] + + def parse_uqs(): + UNIQUE_PATTERN = r'(?:CONSTRAINT "?(.+?)"? +)?UNIQUE *\((.+?)\)' + INLINE_UNIQUE_PATTERN = ( + r'(?:(".+?")|([a-z0-9]+)) ' + r'+[a-z0-9_ ]+? +UNIQUE') + + for match in re.finditer(UNIQUE_PATTERN, table_data, re.I): + name, cols = match.group(1, 2) + yield name, list(self._find_cols_in_sig(cols)) + + # we need to match inlines as well, as we seek to differentiate + # a UNIQUE constraint from a UNIQUE INDEX, even though these + # are kind of the same thing :) + for match in re.finditer(INLINE_UNIQUE_PATTERN, table_data, re.I): + cols = list( + self._find_cols_in_sig(match.group(1) or match.group(2))) + yield None, cols + + for name, cols in parse_uqs(): + sig = tuple(cols) + if sig in auto_index_by_sig: + auto_index_by_sig.pop(sig) + parsed_constraint = { + 'name': name, + 'column_names': cols + } + unique_constraints.append(parsed_constraint) + # NOTE: auto_index_by_sig might not be empty here, + # the PRIMARY KEY may have an entry. + return unique_constraints + + @reflection.cache + def get_check_constraints(self, connection, table_name, + schema=None, **kw): + table_data = self._get_table_sql( + connection, table_name, schema=schema, **kw) + if not table_data: + return [] + + CHECK_PATTERN = ( + r'(?:CONSTRAINT (\w+) +)?' + r'CHECK *\( *(.+) *\),? *' + ) + check_constraints = [] + # NOTE: we aren't using re.S here because we actually are + # taking advantage of each CHECK constraint being all on one + # line in the table definition in order to delineate. This + # necessarily makes assumptions as to how the CREATE TABLE + # was emitted. + for match in re.finditer(CHECK_PATTERN, table_data, re.I): + check_constraints.append({ + 'sqltext': match.group(2), + 'name': match.group(1) + }) + + return check_constraints + + @reflection.cache + def get_indexes(self, connection, table_name, schema=None, **kw): + pragma_indexes = self._get_table_pragma( + connection, "index_list", table_name, schema=schema) + indexes = [] + + include_auto_indexes = kw.pop('include_auto_indexes', False) + for row in pragma_indexes: + # ignore implicit primary key index. + # http://www.mail-archive.com/sqlite-users@sqlite.org/msg30517.html + if (not include_auto_indexes and + row[1].startswith('sqlite_autoindex')): + continue + + indexes.append(dict(name=row[1], column_names=[], unique=row[2])) + + # loop thru unique indexes to get the column names. + for idx in indexes: + pragma_index = self._get_table_pragma( + connection, "index_info", idx['name']) + + for row in pragma_index: + idx['column_names'].append(row[2]) + return indexes + + @reflection.cache + def _get_table_sql(self, connection, table_name, schema=None, **kw): + if schema: + schema_expr = "%s." % ( + self.identifier_preparer.quote_identifier(schema)) + else: + schema_expr = "" + try: + s = ("SELECT sql FROM " + " (SELECT * FROM %(schema)ssqlite_master UNION ALL " + " SELECT * FROM %(schema)ssqlite_temp_master) " + "WHERE name = '%(table)s' " + "AND type = 'table'" % { + "schema": schema_expr, + "table": table_name}) + rs = connection.execute(s) + except exc.DBAPIError: + s = ("SELECT sql FROM %(schema)ssqlite_master " + "WHERE name = '%(table)s' " + "AND type = 'table'" % { + "schema": schema_expr, + "table": table_name}) + rs = connection.execute(s) + return rs.scalar() + + def _get_table_pragma(self, connection, pragma, table_name, schema=None): + quote = self.identifier_preparer.quote_identifier + if schema is not None: + statement = "PRAGMA %s." % quote(schema) + else: + statement = "PRAGMA " + qtable = quote(table_name) + statement = "%s%s(%s)" % (statement, pragma, qtable) + cursor = connection.execute(statement) + if not cursor._soft_closed: + # work around SQLite issue whereby cursor.description + # is blank when PRAGMA returns no rows: + # http://www.sqlite.org/cvstrac/tktview?tn=1884 + result = cursor.fetchall() + else: + result = [] + return result diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/pysqlcipher.py b/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/pysqlcipher.py new file mode 100644 index 0000000..09f2b80 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/pysqlcipher.py @@ -0,0 +1,130 @@ +# sqlite/pysqlcipher.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: sqlite+pysqlcipher + :name: pysqlcipher + :dbapi: pysqlcipher + :connectstring: sqlite+pysqlcipher://:passphrase/file_path[?kdf_iter=<iter>] + :url: https://pypi.python.org/pypi/pysqlcipher + + ``pysqlcipher`` is a fork of the standard ``pysqlite`` driver to make + use of the `SQLCipher <https://www.zetetic.net/sqlcipher>`_ backend. + + ``pysqlcipher3`` is a fork of ``pysqlcipher`` for Python 3. This dialect + will attempt to import it if ``pysqlcipher`` is non-present. + + .. versionadded:: 1.1.4 - added fallback import for pysqlcipher3 + + .. versionadded:: 0.9.9 - added pysqlcipher dialect + +Driver +------ + +The driver here is the `pysqlcipher <https://pypi.python.org/pypi/pysqlcipher>`_ +driver, which makes use of the SQLCipher engine. This system essentially +introduces new PRAGMA commands to SQLite which allows the setting of a +passphrase and other encryption parameters, allowing the database +file to be encrypted. + +`pysqlcipher3` is a fork of `pysqlcipher` with support for Python 3, +the driver is the same. + +Connect Strings +--------------- + +The format of the connect string is in every way the same as that +of the :mod:`~sqlalchemy.dialects.sqlite.pysqlite` driver, except that the +"password" field is now accepted, which should contain a passphrase:: + + e = create_engine('sqlite+pysqlcipher://:testing@/foo.db') + +For an absolute file path, two leading slashes should be used for the +database name:: + + e = create_engine('sqlite+pysqlcipher://:testing@//path/to/foo.db') + +A selection of additional encryption-related pragmas supported by SQLCipher +as documented at https://www.zetetic.net/sqlcipher/sqlcipher-api/ can be passed +in the query string, and will result in that PRAGMA being called for each +new connection. Currently, ``cipher``, ``kdf_iter`` +``cipher_page_size`` and ``cipher_use_hmac`` are supported:: + + e = create_engine('sqlite+pysqlcipher://:testing@/foo.db?cipher=aes-256-cfb&kdf_iter=64000') + + +Pooling Behavior +---------------- + +The driver makes a change to the default pool behavior of pysqlite +as described in :ref:`pysqlite_threading_pooling`. The pysqlcipher driver +has been observed to be significantly slower on connection than the +pysqlite driver, most likely due to the encryption overhead, so the +dialect here defaults to using the :class:`.SingletonThreadPool` +implementation, +instead of the :class:`.NullPool` pool used by pysqlite. As always, the pool +implementation is entirely configurable using the +:paramref:`.create_engine.poolclass` parameter; the :class:`.StaticPool` may +be more feasible for single-threaded use, or :class:`.NullPool` may be used +to prevent unencrypted connections from being held open for long periods of +time, at the expense of slower startup time for new connections. + + +""" +from __future__ import absolute_import +from .pysqlite import SQLiteDialect_pysqlite +from ...engine import url as _url +from ... import pool + + +class SQLiteDialect_pysqlcipher(SQLiteDialect_pysqlite): + driver = 'pysqlcipher' + + pragmas = ('kdf_iter', 'cipher', 'cipher_page_size', 'cipher_use_hmac') + + @classmethod + def dbapi(cls): + try: + from pysqlcipher import dbapi2 as sqlcipher + except ImportError as e: + try: + from pysqlcipher3 import dbapi2 as sqlcipher + except ImportError: + raise e + return sqlcipher + + @classmethod + def get_pool_class(cls, url): + return pool.SingletonThreadPool + + def connect(self, *cargs, **cparams): + passphrase = cparams.pop('passphrase', '') + + pragmas = dict( + (key, cparams.pop(key, None)) for key in + self.pragmas + ) + + conn = super(SQLiteDialect_pysqlcipher, self).\ + connect(*cargs, **cparams) + conn.execute('pragma key="%s"' % passphrase) + for prag, value in pragmas.items(): + if value is not None: + conn.execute('pragma %s="%s"' % (prag, value)) + + return conn + + def create_connect_args(self, url): + super_url = _url.URL( + url.drivername, username=url.username, + host=url.host, database=url.database, query=url.query) + c_args, opts = super(SQLiteDialect_pysqlcipher, self).\ + create_connect_args(super_url) + opts['passphrase'] = url.password + return c_args, opts + +dialect = SQLiteDialect_pysqlcipher diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py b/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py new file mode 100644 index 0000000..8809962 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py @@ -0,0 +1,377 @@ +# sqlite/pysqlite.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +r""" +.. dialect:: sqlite+pysqlite + :name: pysqlite + :dbapi: sqlite3 + :connectstring: sqlite+pysqlite:///file_path + :url: http://docs.python.org/library/sqlite3.html + + Note that ``pysqlite`` is the same driver as the ``sqlite3`` + module included with the Python distribution. + +Driver +------ + +When using Python 2.5 and above, the built in ``sqlite3`` driver is +already installed and no additional installation is needed. Otherwise, +the ``pysqlite2`` driver needs to be present. This is the same driver as +``sqlite3``, just with a different name. + +The ``pysqlite2`` driver will be loaded first, and if not found, ``sqlite3`` +is loaded. This allows an explicitly installed pysqlite driver to take +precedence over the built in one. As with all dialects, a specific +DBAPI module may be provided to :func:`~sqlalchemy.create_engine()` to control +this explicitly:: + + from sqlite3 import dbapi2 as sqlite + e = create_engine('sqlite+pysqlite:///file.db', module=sqlite) + + +Connect Strings +--------------- + +The file specification for the SQLite database is taken as the "database" +portion of the URL. Note that the format of a SQLAlchemy url is:: + + driver://user:pass@host/database + +This means that the actual filename to be used starts with the characters to +the **right** of the third slash. So connecting to a relative filepath +looks like:: + + # relative path + e = create_engine('sqlite:///path/to/database.db') + +An absolute path, which is denoted by starting with a slash, means you +need **four** slashes:: + + # absolute path + e = create_engine('sqlite:////path/to/database.db') + +To use a Windows path, regular drive specifications and backslashes can be +used. Double backslashes are probably needed:: + + # absolute path on Windows + e = create_engine('sqlite:///C:\\path\\to\\database.db') + +The sqlite ``:memory:`` identifier is the default if no filepath is +present. Specify ``sqlite://`` and nothing else:: + + # in-memory database + e = create_engine('sqlite://') + +Compatibility with sqlite3 "native" date and datetime types +----------------------------------------------------------- + +The pysqlite driver includes the sqlite3.PARSE_DECLTYPES and +sqlite3.PARSE_COLNAMES options, which have the effect of any column +or expression explicitly cast as "date" or "timestamp" will be converted +to a Python date or datetime object. The date and datetime types provided +with the pysqlite dialect are not currently compatible with these options, +since they render the ISO date/datetime including microseconds, which +pysqlite's driver does not. Additionally, SQLAlchemy does not at +this time automatically render the "cast" syntax required for the +freestanding functions "current_timestamp" and "current_date" to return +datetime/date types natively. Unfortunately, pysqlite +does not provide the standard DBAPI types in ``cursor.description``, +leaving SQLAlchemy with no way to detect these types on the fly +without expensive per-row type checks. + +Keeping in mind that pysqlite's parsing option is not recommended, +nor should be necessary, for use with SQLAlchemy, usage of PARSE_DECLTYPES +can be forced if one configures "native_datetime=True" on create_engine():: + + engine = create_engine('sqlite://', + connect_args={'detect_types': + sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES}, + native_datetime=True + ) + +With this flag enabled, the DATE and TIMESTAMP types (but note - not the +DATETIME or TIME types...confused yet ?) will not perform any bind parameter +or result processing. Execution of "func.current_date()" will return a string. +"func.current_timestamp()" is registered as returning a DATETIME type in +SQLAlchemy, so this function still receives SQLAlchemy-level result +processing. + +.. _pysqlite_threading_pooling: + +Threading/Pooling Behavior +--------------------------- + +Pysqlite's default behavior is to prohibit the usage of a single connection +in more than one thread. This is originally intended to work with older +versions of SQLite that did not support multithreaded operation under +various circumstances. In particular, older SQLite versions +did not allow a ``:memory:`` database to be used in multiple threads +under any circumstances. + +Pysqlite does include a now-undocumented flag known as +``check_same_thread`` which will disable this check, however note that +pysqlite connections are still not safe to use in concurrently in multiple +threads. In particular, any statement execution calls would need to be +externally mutexed, as Pysqlite does not provide for thread-safe propagation +of error messages among other things. So while even ``:memory:`` databases +can be shared among threads in modern SQLite, Pysqlite doesn't provide enough +thread-safety to make this usage worth it. + +SQLAlchemy sets up pooling to work with Pysqlite's default behavior: + +* When a ``:memory:`` SQLite database is specified, the dialect by default + will use :class:`.SingletonThreadPool`. This pool maintains a single + connection per thread, so that all access to the engine within the current + thread use the same ``:memory:`` database - other threads would access a + different ``:memory:`` database. +* When a file-based database is specified, the dialect will use + :class:`.NullPool` as the source of connections. This pool closes and + discards connections which are returned to the pool immediately. SQLite + file-based connections have extremely low overhead, so pooling is not + necessary. The scheme also prevents a connection from being used again in + a different thread and works best with SQLite's coarse-grained file locking. + + .. versionchanged:: 0.7 + Default selection of :class:`.NullPool` for SQLite file-based databases. + Previous versions select :class:`.SingletonThreadPool` by + default for all SQLite databases. + + +Using a Memory Database in Multiple Threads +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To use a ``:memory:`` database in a multithreaded scenario, the same +connection object must be shared among threads, since the database exists +only within the scope of that connection. The +:class:`.StaticPool` implementation will maintain a single connection +globally, and the ``check_same_thread`` flag can be passed to Pysqlite +as ``False``:: + + from sqlalchemy.pool import StaticPool + engine = create_engine('sqlite://', + connect_args={'check_same_thread':False}, + poolclass=StaticPool) + +Note that using a ``:memory:`` database in multiple threads requires a recent +version of SQLite. + +Using Temporary Tables with SQLite +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Due to the way SQLite deals with temporary tables, if you wish to use a +temporary table in a file-based SQLite database across multiple checkouts +from the connection pool, such as when using an ORM :class:`.Session` where +the temporary table should continue to remain after :meth:`.Session.commit` or +:meth:`.Session.rollback` is called, a pool which maintains a single +connection must be used. Use :class:`.SingletonThreadPool` if the scope is +only needed within the current thread, or :class:`.StaticPool` is scope is +needed within multiple threads for this case:: + + # maintain the same connection per thread + from sqlalchemy.pool import SingletonThreadPool + engine = create_engine('sqlite:///mydb.db', + poolclass=SingletonThreadPool) + + + # maintain the same connection across all threads + from sqlalchemy.pool import StaticPool + engine = create_engine('sqlite:///mydb.db', + poolclass=StaticPool) + +Note that :class:`.SingletonThreadPool` should be configured for the number +of threads that are to be used; beyond that number, connections will be +closed out in a non deterministic way. + +Unicode +------- + +The pysqlite driver only returns Python ``unicode`` objects in result sets, +never plain strings, and accommodates ``unicode`` objects within bound +parameter values in all cases. Regardless of the SQLAlchemy string type in +use, string-based result values will by Python ``unicode`` in Python 2. +The :class:`.Unicode` type should still be used to indicate those columns that +require unicode, however, so that non-``unicode`` values passed inadvertently +will emit a warning. Pysqlite will emit an error if a non-``unicode`` string +is passed containing non-ASCII characters. + +.. _pysqlite_serializable: + +Serializable isolation / Savepoints / Transactional DDL +------------------------------------------------------- + +In the section :ref:`sqlite_concurrency`, we refer to the pysqlite +driver's assortment of issues that prevent several features of SQLite +from working correctly. The pysqlite DBAPI driver has several +long-standing bugs which impact the correctness of its transactional +behavior. In its default mode of operation, SQLite features such as +SERIALIZABLE isolation, transactional DDL, and SAVEPOINT support are +non-functional, and in order to use these features, workarounds must +be taken. + +The issue is essentially that the driver attempts to second-guess the user's +intent, failing to start transactions and sometimes ending them prematurely, in +an effort to minimize the SQLite databases's file locking behavior, even +though SQLite itself uses "shared" locks for read-only activities. + +SQLAlchemy chooses to not alter this behavior by default, as it is the +long-expected behavior of the pysqlite driver; if and when the pysqlite +driver attempts to repair these issues, that will be more of a driver towards +defaults for SQLAlchemy. + +The good news is that with a few events, we can implement transactional +support fully, by disabling pysqlite's feature entirely and emitting BEGIN +ourselves. This is achieved using two event listeners:: + + from sqlalchemy import create_engine, event + + engine = create_engine("sqlite:///myfile.db") + + @event.listens_for(engine, "connect") + def do_connect(dbapi_connection, connection_record): + # disable pysqlite's emitting of the BEGIN statement entirely. + # also stops it from emitting COMMIT before any DDL. + dbapi_connection.isolation_level = None + + @event.listens_for(engine, "begin") + def do_begin(conn): + # emit our own BEGIN + conn.execute("BEGIN") + +Above, we intercept a new pysqlite connection and disable any transactional +integration. Then, at the point at which SQLAlchemy knows that transaction +scope is to begin, we emit ``"BEGIN"`` ourselves. + +When we take control of ``"BEGIN"``, we can also control directly SQLite's +locking modes, introduced at `BEGIN TRANSACTION <http://sqlite.org/lang_transaction.html>`_, +by adding the desired locking mode to our ``"BEGIN"``:: + + @event.listens_for(engine, "begin") + def do_begin(conn): + conn.execute("BEGIN EXCLUSIVE") + +.. seealso:: + + `BEGIN TRANSACTION <http://sqlite.org/lang_transaction.html>`_ - on the SQLite site + + `sqlite3 SELECT does not BEGIN a transaction <http://bugs.python.org/issue9924>`_ - on the Python bug tracker + + `sqlite3 module breaks transactions and potentially corrupts data <http://bugs.python.org/issue10740>`_ - on the Python bug tracker + + +""" + +from sqlalchemy.dialects.sqlite.base import SQLiteDialect, DATETIME, DATE +from sqlalchemy import exc, pool +from sqlalchemy import types as sqltypes +from sqlalchemy import util + +import os + + +class _SQLite_pysqliteTimeStamp(DATETIME): + def bind_processor(self, dialect): + if dialect.native_datetime: + return None + else: + return DATETIME.bind_processor(self, dialect) + + def result_processor(self, dialect, coltype): + if dialect.native_datetime: + return None + else: + return DATETIME.result_processor(self, dialect, coltype) + + +class _SQLite_pysqliteDate(DATE): + def bind_processor(self, dialect): + if dialect.native_datetime: + return None + else: + return DATE.bind_processor(self, dialect) + + def result_processor(self, dialect, coltype): + if dialect.native_datetime: + return None + else: + return DATE.result_processor(self, dialect, coltype) + + +class SQLiteDialect_pysqlite(SQLiteDialect): + default_paramstyle = 'qmark' + + colspecs = util.update_copy( + SQLiteDialect.colspecs, + { + sqltypes.Date: _SQLite_pysqliteDate, + sqltypes.TIMESTAMP: _SQLite_pysqliteTimeStamp, + } + ) + + if not util.py2k: + description_encoding = None + + driver = 'pysqlite' + + def __init__(self, **kwargs): + SQLiteDialect.__init__(self, **kwargs) + + if self.dbapi is not None: + sqlite_ver = self.dbapi.version_info + if sqlite_ver < (2, 1, 3): + util.warn( + ("The installed version of pysqlite2 (%s) is out-dated " + "and will cause errors in some cases. Version 2.1.3 " + "or greater is recommended.") % + '.'.join([str(subver) for subver in sqlite_ver])) + + @classmethod + def dbapi(cls): + try: + from pysqlite2 import dbapi2 as sqlite + except ImportError: + try: + from sqlite3 import dbapi2 as sqlite # try 2.5+ stdlib name. + except ImportError as e: + raise e + return sqlite + + @classmethod + def get_pool_class(cls, url): + if url.database and url.database != ':memory:': + return pool.NullPool + else: + return pool.SingletonThreadPool + + def _get_server_version_info(self, connection): + return self.dbapi.sqlite_version_info + + def create_connect_args(self, url): + if url.username or url.password or url.host or url.port: + raise exc.ArgumentError( + "Invalid SQLite URL: %s\n" + "Valid SQLite URL forms are:\n" + " sqlite:///:memory: (or, sqlite://)\n" + " sqlite:///relative/path/to/file.db\n" + " sqlite:////absolute/path/to/file.db" % (url,)) + filename = url.database or ':memory:' + if filename != ':memory:': + filename = os.path.abspath(filename) + + opts = url.query.copy() + util.coerce_kw_type(opts, 'timeout', float) + util.coerce_kw_type(opts, 'isolation_level', str) + util.coerce_kw_type(opts, 'detect_types', int) + util.coerce_kw_type(opts, 'check_same_thread', bool) + util.coerce_kw_type(opts, 'cached_statements', int) + + return ([filename], opts) + + def is_disconnect(self, e, connection, cursor): + return isinstance(e, self.dbapi.ProgrammingError) and \ + "Cannot operate on a closed database." in str(e) + +dialect = SQLiteDialect_pysqlite diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sybase/__init__.py b/venv/Lib/site-packages/sqlalchemy/dialects/sybase/__init__.py new file mode 100644 index 0000000..be43497 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/sybase/__init__.py @@ -0,0 +1,27 @@ +# sybase/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from . import base, pysybase, pyodbc # noqa + +from .base import CHAR, VARCHAR, TIME, NCHAR, NVARCHAR,\ + TEXT, DATE, DATETIME, FLOAT, NUMERIC,\ + BIGINT, INT, INTEGER, SMALLINT, BINARY,\ + VARBINARY, UNITEXT, UNICHAR, UNIVARCHAR,\ + IMAGE, BIT, MONEY, SMALLMONEY, TINYINT + +# default dialect +base.dialect = dialect = pyodbc.dialect + + +__all__ = ( + 'CHAR', 'VARCHAR', 'TIME', 'NCHAR', 'NVARCHAR', + 'TEXT', 'DATE', 'DATETIME', 'FLOAT', 'NUMERIC', + 'BIGINT', 'INT', 'INTEGER', 'SMALLINT', 'BINARY', + 'VARBINARY', 'UNITEXT', 'UNICHAR', 'UNIVARCHAR', + 'IMAGE', 'BIT', 'MONEY', 'SMALLMONEY', 'TINYINT', + 'dialect' +) diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sybase/base.py b/venv/Lib/site-packages/sqlalchemy/dialects/sybase/base.py new file mode 100644 index 0000000..7dd9735 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/sybase/base.py @@ -0,0 +1,839 @@ +# sybase/base.py +# Copyright (C) 2010-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# get_select_precolumns(), limit_clause() implementation +# copyright (C) 2007 Fisch Asset Management +# AG http://www.fam.ch, with coding by Alexander Houben +# alexander.houben@thor-solutions.ch +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" + +.. dialect:: sybase + :name: Sybase + +.. note:: + + The Sybase dialect functions on current SQLAlchemy versions + but is not regularly tested, and may have many issues and + caveats not currently handled. + +""" +import operator +import re + +from sqlalchemy.sql import compiler, expression, text, bindparam +from sqlalchemy.engine import default, base, reflection +from sqlalchemy import types as sqltypes +from sqlalchemy.sql import operators as sql_operators +from sqlalchemy import schema as sa_schema +from sqlalchemy import util, sql, exc + +from sqlalchemy.types import CHAR, VARCHAR, TIME, NCHAR, NVARCHAR,\ + TEXT, DATE, DATETIME, FLOAT, NUMERIC,\ + BIGINT, INT, INTEGER, SMALLINT, BINARY,\ + VARBINARY, DECIMAL, TIMESTAMP, Unicode,\ + UnicodeText, REAL + +RESERVED_WORDS = set([ + "add", "all", "alter", "and", + "any", "as", "asc", "backup", + "begin", "between", "bigint", "binary", + "bit", "bottom", "break", "by", + "call", "capability", "cascade", "case", + "cast", "char", "char_convert", "character", + "check", "checkpoint", "close", "comment", + "commit", "connect", "constraint", "contains", + "continue", "convert", "create", "cross", + "cube", "current", "current_timestamp", "current_user", + "cursor", "date", "dbspace", "deallocate", + "dec", "decimal", "declare", "default", + "delete", "deleting", "desc", "distinct", + "do", "double", "drop", "dynamic", + "else", "elseif", "encrypted", "end", + "endif", "escape", "except", "exception", + "exec", "execute", "existing", "exists", + "externlogin", "fetch", "first", "float", + "for", "force", "foreign", "forward", + "from", "full", "goto", "grant", + "group", "having", "holdlock", "identified", + "if", "in", "index", "index_lparen", + "inner", "inout", "insensitive", "insert", + "inserting", "install", "instead", "int", + "integer", "integrated", "intersect", "into", + "iq", "is", "isolation", "join", + "key", "lateral", "left", "like", + "lock", "login", "long", "match", + "membership", "message", "mode", "modify", + "natural", "new", "no", "noholdlock", + "not", "notify", "null", "numeric", + "of", "off", "on", "open", + "option", "options", "or", "order", + "others", "out", "outer", "over", + "passthrough", "precision", "prepare", "primary", + "print", "privileges", "proc", "procedure", + "publication", "raiserror", "readtext", "real", + "reference", "references", "release", "remote", + "remove", "rename", "reorganize", "resource", + "restore", "restrict", "return", "revoke", + "right", "rollback", "rollup", "save", + "savepoint", "scroll", "select", "sensitive", + "session", "set", "setuser", "share", + "smallint", "some", "sqlcode", "sqlstate", + "start", "stop", "subtrans", "subtransaction", + "synchronize", "syntax_error", "table", "temporary", + "then", "time", "timestamp", "tinyint", + "to", "top", "tran", "trigger", + "truncate", "tsequal", "unbounded", "union", + "unique", "unknown", "unsigned", "update", + "updating", "user", "using", "validate", + "values", "varbinary", "varchar", "variable", + "varying", "view", "wait", "waitfor", + "when", "where", "while", "window", + "with", "with_cube", "with_lparen", "with_rollup", + "within", "work", "writetext", +]) + + +class _SybaseUnitypeMixin(object): + """these types appear to return a buffer object.""" + + def result_processor(self, dialect, coltype): + def process(value): + if value is not None: + return str(value) # decode("ucs-2") + else: + return None + return process + + +class UNICHAR(_SybaseUnitypeMixin, sqltypes.Unicode): + __visit_name__ = 'UNICHAR' + + +class UNIVARCHAR(_SybaseUnitypeMixin, sqltypes.Unicode): + __visit_name__ = 'UNIVARCHAR' + + +class UNITEXT(_SybaseUnitypeMixin, sqltypes.UnicodeText): + __visit_name__ = 'UNITEXT' + + +class TINYINT(sqltypes.Integer): + __visit_name__ = 'TINYINT' + + +class BIT(sqltypes.TypeEngine): + __visit_name__ = 'BIT' + + +class MONEY(sqltypes.TypeEngine): + __visit_name__ = "MONEY" + + +class SMALLMONEY(sqltypes.TypeEngine): + __visit_name__ = "SMALLMONEY" + + +class UNIQUEIDENTIFIER(sqltypes.TypeEngine): + __visit_name__ = "UNIQUEIDENTIFIER" + + +class IMAGE(sqltypes.LargeBinary): + __visit_name__ = 'IMAGE' + + +class SybaseTypeCompiler(compiler.GenericTypeCompiler): + def visit_large_binary(self, type_, **kw): + return self.visit_IMAGE(type_) + + def visit_boolean(self, type_, **kw): + return self.visit_BIT(type_) + + def visit_unicode(self, type_, **kw): + return self.visit_NVARCHAR(type_) + + def visit_UNICHAR(self, type_, **kw): + return "UNICHAR(%d)" % type_.length + + def visit_UNIVARCHAR(self, type_, **kw): + return "UNIVARCHAR(%d)" % type_.length + + def visit_UNITEXT(self, type_, **kw): + return "UNITEXT" + + def visit_TINYINT(self, type_, **kw): + return "TINYINT" + + def visit_IMAGE(self, type_, **kw): + return "IMAGE" + + def visit_BIT(self, type_, **kw): + return "BIT" + + def visit_MONEY(self, type_, **kw): + return "MONEY" + + def visit_SMALLMONEY(self, type_, **kw): + return "SMALLMONEY" + + def visit_UNIQUEIDENTIFIER(self, type_, **kw): + return "UNIQUEIDENTIFIER" + +ischema_names = { + 'bigint': BIGINT, + 'int': INTEGER, + 'integer': INTEGER, + 'smallint': SMALLINT, + 'tinyint': TINYINT, + 'unsigned bigint': BIGINT, # TODO: unsigned flags + 'unsigned int': INTEGER, # TODO: unsigned flags + 'unsigned smallint': SMALLINT, # TODO: unsigned flags + 'numeric': NUMERIC, + 'decimal': DECIMAL, + 'dec': DECIMAL, + 'float': FLOAT, + 'double': NUMERIC, # TODO + 'double precision': NUMERIC, # TODO + 'real': REAL, + 'smallmoney': SMALLMONEY, + 'money': MONEY, + 'smalldatetime': DATETIME, + 'datetime': DATETIME, + 'date': DATE, + 'time': TIME, + 'char': CHAR, + 'character': CHAR, + 'varchar': VARCHAR, + 'character varying': VARCHAR, + 'char varying': VARCHAR, + 'unichar': UNICHAR, + 'unicode character': UNIVARCHAR, + 'nchar': NCHAR, + 'national char': NCHAR, + 'national character': NCHAR, + 'nvarchar': NVARCHAR, + 'nchar varying': NVARCHAR, + 'national char varying': NVARCHAR, + 'national character varying': NVARCHAR, + 'text': TEXT, + 'unitext': UNITEXT, + 'binary': BINARY, + 'varbinary': VARBINARY, + 'image': IMAGE, + 'bit': BIT, + + # not in documentation for ASE 15.7 + 'long varchar': TEXT, # TODO + 'timestamp': TIMESTAMP, + 'uniqueidentifier': UNIQUEIDENTIFIER, + +} + + +class SybaseInspector(reflection.Inspector): + + def __init__(self, conn): + reflection.Inspector.__init__(self, conn) + + def get_table_id(self, table_name, schema=None): + """Return the table id from `table_name` and `schema`.""" + + return self.dialect.get_table_id(self.bind, table_name, schema, + info_cache=self.info_cache) + + +class SybaseExecutionContext(default.DefaultExecutionContext): + _enable_identity_insert = False + + def set_ddl_autocommit(self, connection, value): + """Must be implemented by subclasses to accommodate DDL executions. + + "connection" is the raw unwrapped DBAPI connection. "value" + is True or False. when True, the connection should be configured + such that a DDL can take place subsequently. when False, + a DDL has taken place and the connection should be resumed + into non-autocommit mode. + + """ + raise NotImplementedError() + + def pre_exec(self): + if self.isinsert: + tbl = self.compiled.statement.table + seq_column = tbl._autoincrement_column + insert_has_sequence = seq_column is not None + + if insert_has_sequence: + self._enable_identity_insert = \ + seq_column.key in self.compiled_parameters[0] + else: + self._enable_identity_insert = False + + if self._enable_identity_insert: + self.cursor.execute( + "SET IDENTITY_INSERT %s ON" % + self.dialect.identifier_preparer.format_table(tbl)) + + if self.isddl: + # TODO: to enhance this, we can detect "ddl in tran" on the + # database settings. this error message should be improved to + # include a note about that. + if not self.should_autocommit: + raise exc.InvalidRequestError( + "The Sybase dialect only supports " + "DDL in 'autocommit' mode at this time.") + + self.root_connection.engine.logger.info( + "AUTOCOMMIT (Assuming no Sybase 'ddl in tran')") + + self.set_ddl_autocommit( + self.root_connection.connection.connection, + True) + + def post_exec(self): + if self.isddl: + self.set_ddl_autocommit(self.root_connection, False) + + if self._enable_identity_insert: + self.cursor.execute( + "SET IDENTITY_INSERT %s OFF" % + self.dialect.identifier_preparer. + format_table(self.compiled.statement.table) + ) + + def get_lastrowid(self): + cursor = self.create_cursor() + cursor.execute("SELECT @@identity AS lastrowid") + lastrowid = cursor.fetchone()[0] + cursor.close() + return lastrowid + + +class SybaseSQLCompiler(compiler.SQLCompiler): + ansi_bind_rules = True + + extract_map = util.update_copy( + compiler.SQLCompiler.extract_map, + { + 'doy': 'dayofyear', + 'dow': 'weekday', + 'milliseconds': 'millisecond' + }) + + def get_select_precolumns(self, select, **kw): + s = select._distinct and "DISTINCT " or "" + # TODO: don't think Sybase supports + # bind params for FIRST / TOP + limit = select._limit + if limit: + # if select._limit == 1: + # s += "FIRST " + # else: + # s += "TOP %s " % (select._limit,) + s += "TOP %s " % (limit,) + offset = select._offset + if offset: + raise NotImplementedError("Sybase ASE does not support OFFSET") + return s + + def get_from_hint_text(self, table, text): + return text + + def limit_clause(self, select, **kw): + # Limit in sybase is after the select keyword + return "" + + def visit_extract(self, extract, **kw): + field = self.extract_map.get(extract.field, extract.field) + return 'DATEPART("%s", %s)' % ( + field, self.process(extract.expr, **kw)) + + def visit_now_func(self, fn, **kw): + return "GETDATE()" + + def for_update_clause(self, select): + # "FOR UPDATE" is only allowed on "DECLARE CURSOR" + # which SQLAlchemy doesn't use + return '' + + def order_by_clause(self, select, **kw): + kw['literal_binds'] = True + order_by = self.process(select._order_by_clause, **kw) + + # SybaseSQL only allows ORDER BY in subqueries if there is a LIMIT + if order_by and (not self.is_subquery() or select._limit): + return " ORDER BY " + order_by + else: + return "" + + def delete_table_clause(self, delete_stmt, from_table, + extra_froms): + """If we have extra froms make sure we render any alias as hint.""" + ashint = False + if extra_froms: + ashint = True + return from_table._compiler_dispatch( + self, asfrom=True, iscrud=True, ashint=ashint + ) + + def delete_extra_from_clause(self, delete_stmt, from_table, + extra_froms, from_hints, **kw): + """Render the DELETE .. FROM clause specific to Sybase.""" + return "FROM " + ', '.join( + t._compiler_dispatch(self, asfrom=True, + fromhints=from_hints, **kw) + for t in [from_table] + extra_froms) + + +class SybaseDDLCompiler(compiler.DDLCompiler): + def get_column_specification(self, column, **kwargs): + colspec = self.preparer.format_column(column) + " " + \ + self.dialect.type_compiler.process( + column.type, type_expression=column) + + if column.table is None: + raise exc.CompileError( + "The Sybase dialect requires Table-bound " + "columns in order to generate DDL") + seq_col = column.table._autoincrement_column + + # install a IDENTITY Sequence if we have an implicit IDENTITY column + if seq_col is column: + sequence = isinstance(column.default, sa_schema.Sequence) \ + and column.default + if sequence: + start, increment = sequence.start or 1, \ + sequence.increment or 1 + else: + start, increment = 1, 1 + if (start, increment) == (1, 1): + colspec += " IDENTITY" + else: + # TODO: need correct syntax for this + colspec += " IDENTITY(%s,%s)" % (start, increment) + else: + default = self.get_column_default_string(column) + if default is not None: + colspec += " DEFAULT " + default + + if column.nullable is not None: + if not column.nullable or column.primary_key: + colspec += " NOT NULL" + else: + colspec += " NULL" + + return colspec + + def visit_drop_index(self, drop): + index = drop.element + return "\nDROP INDEX %s.%s" % ( + self.preparer.quote_identifier(index.table.name), + self._prepared_index_name(drop.element, + include_schema=False) + ) + + +class SybaseIdentifierPreparer(compiler.IdentifierPreparer): + reserved_words = RESERVED_WORDS + + +class SybaseDialect(default.DefaultDialect): + name = 'sybase' + supports_unicode_statements = False + supports_sane_rowcount = False + supports_sane_multi_rowcount = False + + supports_native_boolean = False + supports_unicode_binds = False + postfetch_lastrowid = True + + colspecs = {} + ischema_names = ischema_names + + type_compiler = SybaseTypeCompiler + statement_compiler = SybaseSQLCompiler + ddl_compiler = SybaseDDLCompiler + preparer = SybaseIdentifierPreparer + inspector = SybaseInspector + + construct_arguments = [] + + def _get_default_schema_name(self, connection): + return connection.scalar( + text("SELECT user_name() as user_name", + typemap={'user_name': Unicode}) + ) + + def initialize(self, connection): + super(SybaseDialect, self).initialize(connection) + if self.server_version_info is not None and\ + self.server_version_info < (15, ): + self.max_identifier_length = 30 + else: + self.max_identifier_length = 255 + + def get_table_id(self, connection, table_name, schema=None, **kw): + """Fetch the id for schema.table_name. + + Several reflection methods require the table id. The idea for using + this method is that it can be fetched one time and cached for + subsequent calls. + + """ + + table_id = None + if schema is None: + schema = self.default_schema_name + + TABLEID_SQL = text(""" + SELECT o.id AS id + FROM sysobjects o JOIN sysusers u ON o.uid=u.uid + WHERE u.name = :schema_name + AND o.name = :table_name + AND o.type in ('U', 'V') + """) + + if util.py2k: + if isinstance(schema, unicode): + schema = schema.encode("ascii") + if isinstance(table_name, unicode): + table_name = table_name.encode("ascii") + result = connection.execute(TABLEID_SQL, + schema_name=schema, + table_name=table_name) + table_id = result.scalar() + if table_id is None: + raise exc.NoSuchTableError(table_name) + return table_id + + @reflection.cache + def get_columns(self, connection, table_name, schema=None, **kw): + table_id = self.get_table_id(connection, table_name, schema, + info_cache=kw.get("info_cache")) + + COLUMN_SQL = text(""" + SELECT col.name AS name, + t.name AS type, + (col.status & 8) AS nullable, + (col.status & 128) AS autoincrement, + com.text AS 'default', + col.prec AS precision, + col.scale AS scale, + col.length AS length + FROM systypes t, syscolumns col LEFT OUTER JOIN syscomments com ON + col.cdefault = com.id + WHERE col.usertype = t.usertype + AND col.id = :table_id + ORDER BY col.colid + """) + + results = connection.execute(COLUMN_SQL, table_id=table_id) + + columns = [] + for (name, type_, nullable, autoincrement, default, precision, scale, + length) in results: + col_info = self._get_column_info(name, type_, bool(nullable), + bool(autoincrement), + default, precision, scale, + length) + columns.append(col_info) + + return columns + + def _get_column_info(self, name, type_, nullable, autoincrement, default, + precision, scale, length): + + coltype = self.ischema_names.get(type_, None) + + kwargs = {} + + if coltype in (NUMERIC, DECIMAL): + args = (precision, scale) + elif coltype == FLOAT: + args = (precision,) + elif coltype in (CHAR, VARCHAR, UNICHAR, UNIVARCHAR, NCHAR, NVARCHAR): + args = (length,) + else: + args = () + + if coltype: + coltype = coltype(*args, **kwargs) + # is this necessary + # if is_array: + # coltype = ARRAY(coltype) + else: + util.warn("Did not recognize type '%s' of column '%s'" % + (type_, name)) + coltype = sqltypes.NULLTYPE + + if default: + default = default.replace("DEFAULT", "").strip() + default = re.sub("^'(.*)'$", lambda m: m.group(1), default) + else: + default = None + + column_info = dict(name=name, type=coltype, nullable=nullable, + default=default, autoincrement=autoincrement) + return column_info + + @reflection.cache + def get_foreign_keys(self, connection, table_name, schema=None, **kw): + + table_id = self.get_table_id(connection, table_name, schema, + info_cache=kw.get("info_cache")) + + table_cache = {} + column_cache = {} + foreign_keys = [] + + table_cache[table_id] = {"name": table_name, "schema": schema} + + COLUMN_SQL = text(""" + SELECT c.colid AS id, c.name AS name + FROM syscolumns c + WHERE c.id = :table_id + """) + + results = connection.execute(COLUMN_SQL, table_id=table_id) + columns = {} + for col in results: + columns[col["id"]] = col["name"] + column_cache[table_id] = columns + + REFCONSTRAINT_SQL = text(""" + SELECT o.name AS name, r.reftabid AS reftable_id, + r.keycnt AS 'count', + r.fokey1 AS fokey1, r.fokey2 AS fokey2, r.fokey3 AS fokey3, + r.fokey4 AS fokey4, r.fokey5 AS fokey5, r.fokey6 AS fokey6, + r.fokey7 AS fokey7, r.fokey1 AS fokey8, r.fokey9 AS fokey9, + r.fokey10 AS fokey10, r.fokey11 AS fokey11, r.fokey12 AS fokey12, + r.fokey13 AS fokey13, r.fokey14 AS fokey14, r.fokey15 AS fokey15, + r.fokey16 AS fokey16, + r.refkey1 AS refkey1, r.refkey2 AS refkey2, r.refkey3 AS refkey3, + r.refkey4 AS refkey4, r.refkey5 AS refkey5, r.refkey6 AS refkey6, + r.refkey7 AS refkey7, r.refkey1 AS refkey8, r.refkey9 AS refkey9, + r.refkey10 AS refkey10, r.refkey11 AS refkey11, + r.refkey12 AS refkey12, r.refkey13 AS refkey13, + r.refkey14 AS refkey14, r.refkey15 AS refkey15, + r.refkey16 AS refkey16 + FROM sysreferences r JOIN sysobjects o on r.tableid = o.id + WHERE r.tableid = :table_id + """) + referential_constraints = connection.execute( + REFCONSTRAINT_SQL, table_id=table_id).fetchall() + + REFTABLE_SQL = text(""" + SELECT o.name AS name, u.name AS 'schema' + FROM sysobjects o JOIN sysusers u ON o.uid = u.uid + WHERE o.id = :table_id + """) + + for r in referential_constraints: + reftable_id = r["reftable_id"] + + if reftable_id not in table_cache: + c = connection.execute(REFTABLE_SQL, table_id=reftable_id) + reftable = c.fetchone() + c.close() + table_info = {"name": reftable["name"], "schema": None} + if (schema is not None or + reftable["schema"] != self.default_schema_name): + table_info["schema"] = reftable["schema"] + + table_cache[reftable_id] = table_info + results = connection.execute(COLUMN_SQL, table_id=reftable_id) + reftable_columns = {} + for col in results: + reftable_columns[col["id"]] = col["name"] + column_cache[reftable_id] = reftable_columns + + reftable = table_cache[reftable_id] + reftable_columns = column_cache[reftable_id] + + constrained_columns = [] + referred_columns = [] + for i in range(1, r["count"] + 1): + constrained_columns.append(columns[r["fokey%i" % i]]) + referred_columns.append(reftable_columns[r["refkey%i" % i]]) + + fk_info = { + "constrained_columns": constrained_columns, + "referred_schema": reftable["schema"], + "referred_table": reftable["name"], + "referred_columns": referred_columns, + "name": r["name"] + } + + foreign_keys.append(fk_info) + + return foreign_keys + + @reflection.cache + def get_indexes(self, connection, table_name, schema=None, **kw): + table_id = self.get_table_id(connection, table_name, schema, + info_cache=kw.get("info_cache")) + + INDEX_SQL = text(""" + SELECT object_name(i.id) AS table_name, + i.keycnt AS 'count', + i.name AS name, + (i.status & 0x2) AS 'unique', + index_col(object_name(i.id), i.indid, 1) AS col_1, + index_col(object_name(i.id), i.indid, 2) AS col_2, + index_col(object_name(i.id), i.indid, 3) AS col_3, + index_col(object_name(i.id), i.indid, 4) AS col_4, + index_col(object_name(i.id), i.indid, 5) AS col_5, + index_col(object_name(i.id), i.indid, 6) AS col_6, + index_col(object_name(i.id), i.indid, 7) AS col_7, + index_col(object_name(i.id), i.indid, 8) AS col_8, + index_col(object_name(i.id), i.indid, 9) AS col_9, + index_col(object_name(i.id), i.indid, 10) AS col_10, + index_col(object_name(i.id), i.indid, 11) AS col_11, + index_col(object_name(i.id), i.indid, 12) AS col_12, + index_col(object_name(i.id), i.indid, 13) AS col_13, + index_col(object_name(i.id), i.indid, 14) AS col_14, + index_col(object_name(i.id), i.indid, 15) AS col_15, + index_col(object_name(i.id), i.indid, 16) AS col_16 + FROM sysindexes i, sysobjects o + WHERE o.id = i.id + AND o.id = :table_id + AND (i.status & 2048) = 0 + AND i.indid BETWEEN 1 AND 254 + """) + + results = connection.execute(INDEX_SQL, table_id=table_id) + indexes = [] + for r in results: + column_names = [] + for i in range(1, r["count"]): + column_names.append(r["col_%i" % (i,)]) + index_info = {"name": r["name"], + "unique": bool(r["unique"]), + "column_names": column_names} + indexes.append(index_info) + + return indexes + + @reflection.cache + def get_pk_constraint(self, connection, table_name, schema=None, **kw): + table_id = self.get_table_id(connection, table_name, schema, + info_cache=kw.get("info_cache")) + + PK_SQL = text(""" + SELECT object_name(i.id) AS table_name, + i.keycnt AS 'count', + i.name AS name, + index_col(object_name(i.id), i.indid, 1) AS pk_1, + index_col(object_name(i.id), i.indid, 2) AS pk_2, + index_col(object_name(i.id), i.indid, 3) AS pk_3, + index_col(object_name(i.id), i.indid, 4) AS pk_4, + index_col(object_name(i.id), i.indid, 5) AS pk_5, + index_col(object_name(i.id), i.indid, 6) AS pk_6, + index_col(object_name(i.id), i.indid, 7) AS pk_7, + index_col(object_name(i.id), i.indid, 8) AS pk_8, + index_col(object_name(i.id), i.indid, 9) AS pk_9, + index_col(object_name(i.id), i.indid, 10) AS pk_10, + index_col(object_name(i.id), i.indid, 11) AS pk_11, + index_col(object_name(i.id), i.indid, 12) AS pk_12, + index_col(object_name(i.id), i.indid, 13) AS pk_13, + index_col(object_name(i.id), i.indid, 14) AS pk_14, + index_col(object_name(i.id), i.indid, 15) AS pk_15, + index_col(object_name(i.id), i.indid, 16) AS pk_16 + FROM sysindexes i, sysobjects o + WHERE o.id = i.id + AND o.id = :table_id + AND (i.status & 2048) = 2048 + AND i.indid BETWEEN 1 AND 254 + """) + + results = connection.execute(PK_SQL, table_id=table_id) + pks = results.fetchone() + results.close() + + constrained_columns = [] + if pks: + for i in range(1, pks["count"] + 1): + constrained_columns.append(pks["pk_%i" % (i,)]) + return {"constrained_columns": constrained_columns, + "name": pks["name"]} + else: + return {"constrained_columns": [], "name": None} + + @reflection.cache + def get_schema_names(self, connection, **kw): + + SCHEMA_SQL = text("SELECT u.name AS name FROM sysusers u") + + schemas = connection.execute(SCHEMA_SQL) + + return [s["name"] for s in schemas] + + @reflection.cache + def get_table_names(self, connection, schema=None, **kw): + if schema is None: + schema = self.default_schema_name + + TABLE_SQL = text(""" + SELECT o.name AS name + FROM sysobjects o JOIN sysusers u ON o.uid = u.uid + WHERE u.name = :schema_name + AND o.type = 'U' + """) + + if util.py2k: + if isinstance(schema, unicode): + schema = schema.encode("ascii") + + tables = connection.execute(TABLE_SQL, schema_name=schema) + + return [t["name"] for t in tables] + + @reflection.cache + def get_view_definition(self, connection, view_name, schema=None, **kw): + if schema is None: + schema = self.default_schema_name + + VIEW_DEF_SQL = text(""" + SELECT c.text + FROM syscomments c JOIN sysobjects o ON c.id = o.id + WHERE o.name = :view_name + AND o.type = 'V' + """) + + if util.py2k: + if isinstance(view_name, unicode): + view_name = view_name.encode("ascii") + + view = connection.execute(VIEW_DEF_SQL, view_name=view_name) + + return view.scalar() + + @reflection.cache + def get_view_names(self, connection, schema=None, **kw): + if schema is None: + schema = self.default_schema_name + + VIEW_SQL = text(""" + SELECT o.name AS name + FROM sysobjects o JOIN sysusers u ON o.uid = u.uid + WHERE u.name = :schema_name + AND o.type = 'V' + """) + + if util.py2k: + if isinstance(schema, unicode): + schema = schema.encode("ascii") + views = connection.execute(VIEW_SQL, schema_name=schema) + + return [v["name"] for v in views] + + def has_table(self, connection, table_name, schema=None): + try: + self.get_table_id(connection, table_name, schema) + except exc.NoSuchTableError: + return False + else: + return True diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sybase/mxodbc.py b/venv/Lib/site-packages/sqlalchemy/dialects/sybase/mxodbc.py new file mode 100644 index 0000000..ddb6b7e --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/sybase/mxodbc.py @@ -0,0 +1,33 @@ +# sybase/mxodbc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +""" + +.. dialect:: sybase+mxodbc + :name: mxODBC + :dbapi: mxodbc + :connectstring: sybase+mxodbc://<username>:<password>@<dsnname> + :url: http://www.egenix.com/ + +.. note:: + + This dialect is a stub only and is likely non functional at this time. + + +""" +from sqlalchemy.dialects.sybase.base import SybaseDialect +from sqlalchemy.dialects.sybase.base import SybaseExecutionContext +from sqlalchemy.connectors.mxodbc import MxODBCConnector + + +class SybaseExecutionContext_mxodbc(SybaseExecutionContext): + pass + + +class SybaseDialect_mxodbc(MxODBCConnector, SybaseDialect): + execution_ctx_cls = SybaseExecutionContext_mxodbc + +dialect = SybaseDialect_mxodbc diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sybase/pyodbc.py b/venv/Lib/site-packages/sqlalchemy/dialects/sybase/pyodbc.py new file mode 100644 index 0000000..af6469d --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/sybase/pyodbc.py @@ -0,0 +1,86 @@ +# sybase/pyodbc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: sybase+pyodbc + :name: PyODBC + :dbapi: pyodbc + :connectstring: sybase+pyodbc://<username>:<password>@<dsnname>\ +[/<database>] + :url: http://pypi.python.org/pypi/pyodbc/ + + +Unicode Support +--------------- + +The pyodbc driver currently supports usage of these Sybase types with +Unicode or multibyte strings:: + + CHAR + NCHAR + NVARCHAR + TEXT + VARCHAR + +Currently *not* supported are:: + + UNICHAR + UNITEXT + UNIVARCHAR + +""" + +from sqlalchemy.dialects.sybase.base import SybaseDialect,\ + SybaseExecutionContext +from sqlalchemy.connectors.pyodbc import PyODBCConnector +from sqlalchemy import types as sqltypes, processors +import decimal + + +class _SybNumeric_pyodbc(sqltypes.Numeric): + """Turns Decimals with adjusted() < -6 into floats. + + It's not yet known how to get decimals with many + significant digits or very large adjusted() into Sybase + via pyodbc. + + """ + + def bind_processor(self, dialect): + super_process = super(_SybNumeric_pyodbc, self).\ + bind_processor(dialect) + + def process(value): + if self.asdecimal and \ + isinstance(value, decimal.Decimal): + + if value.adjusted() < -6: + return processors.to_float(value) + + if super_process: + return super_process(value) + else: + return value + return process + + +class SybaseExecutionContext_pyodbc(SybaseExecutionContext): + def set_ddl_autocommit(self, connection, value): + if value: + connection.autocommit = True + else: + connection.autocommit = False + + +class SybaseDialect_pyodbc(PyODBCConnector, SybaseDialect): + execution_ctx_cls = SybaseExecutionContext_pyodbc + + colspecs = { + sqltypes.Numeric: _SybNumeric_pyodbc, + } + +dialect = SybaseDialect_pyodbc diff --git a/venv/Lib/site-packages/sqlalchemy/dialects/sybase/pysybase.py b/venv/Lib/site-packages/sqlalchemy/dialects/sybase/pysybase.py new file mode 100644 index 0000000..2168d55 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/dialects/sybase/pysybase.py @@ -0,0 +1,102 @@ +# sybase/pysybase.py +# Copyright (C) 2010-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +.. dialect:: sybase+pysybase + :name: Python-Sybase + :dbapi: Sybase + :connectstring: sybase+pysybase://<username>:<password>@<dsn>/\ +[database name] + :url: http://python-sybase.sourceforge.net/ + +Unicode Support +--------------- + +The python-sybase driver does not appear to support non-ASCII strings of any +kind at this time. + +""" + +from sqlalchemy import types as sqltypes, processors +from sqlalchemy.dialects.sybase.base import SybaseDialect, \ + SybaseExecutionContext, SybaseSQLCompiler + + +class _SybNumeric(sqltypes.Numeric): + def result_processor(self, dialect, type_): + if not self.asdecimal: + return processors.to_float + else: + return sqltypes.Numeric.result_processor(self, dialect, type_) + + +class SybaseExecutionContext_pysybase(SybaseExecutionContext): + + def set_ddl_autocommit(self, dbapi_connection, value): + if value: + # call commit() on the Sybase connection directly, + # to avoid any side effects of calling a Connection + # transactional method inside of pre_exec() + dbapi_connection.commit() + + def pre_exec(self): + SybaseExecutionContext.pre_exec(self) + + for param in self.parameters: + for key in list(param): + param["@" + key] = param[key] + del param[key] + + +class SybaseSQLCompiler_pysybase(SybaseSQLCompiler): + def bindparam_string(self, name, **kw): + return "@" + name + + +class SybaseDialect_pysybase(SybaseDialect): + driver = 'pysybase' + execution_ctx_cls = SybaseExecutionContext_pysybase + statement_compiler = SybaseSQLCompiler_pysybase + + colspecs = { + sqltypes.Numeric: _SybNumeric, + sqltypes.Float: sqltypes.Float + } + + @classmethod + def dbapi(cls): + import Sybase + return Sybase + + def create_connect_args(self, url): + opts = url.translate_connect_args(username='user', password='passwd') + + return ([opts.pop('host')], opts) + + def do_executemany(self, cursor, statement, parameters, context=None): + # calling python-sybase executemany yields: + # TypeError: string too long for buffer + for param in parameters: + cursor.execute(statement, param) + + def _get_server_version_info(self, connection): + vers = connection.scalar("select @@version_number") + # i.e. 15500, 15000, 12500 == (15, 5, 0, 0), (15, 0, 0, 0), + # (12, 5, 0, 0) + return (vers / 1000, vers % 1000 / 100, vers % 100 / 10, vers % 10) + + def is_disconnect(self, e, connection, cursor): + if isinstance(e, (self.dbapi.OperationalError, + self.dbapi.ProgrammingError)): + msg = str(e) + return ('Unable to complete network request to host' in msg or + 'Invalid connection state' in msg or + 'Invalid cursor state' in msg) + else: + return False + +dialect = SybaseDialect_pysybase diff --git a/venv/Lib/site-packages/sqlalchemy/engine/__init__.py b/venv/Lib/site-packages/sqlalchemy/engine/__init__.py new file mode 100644 index 0000000..b0d765b --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/engine/__init__.py @@ -0,0 +1,472 @@ +# engine/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""SQL connections, SQL execution and high-level DB-API interface. + +The engine package defines the basic components used to interface +DB-API modules with higher-level statement construction, +connection-management, execution and result contexts. The primary +"entry point" class into this package is the Engine and its public +constructor ``create_engine()``. + +This package includes: + +base.py + Defines interface classes and some implementation classes which + comprise the basic components used to interface between a DB-API, + constructed and plain-text statements, connections, transactions, + and results. + +default.py + Contains default implementations of some of the components defined + in base.py. All current database dialects use the classes in + default.py as base classes for their own database-specific + implementations. + +strategies.py + The mechanics of constructing ``Engine`` objects are represented + here. Defines the ``EngineStrategy`` class which represents how + to go from arguments specified to the ``create_engine()`` + function, to a fully constructed ``Engine``, including + initialization of connection pooling, dialects, and specific + subclasses of ``Engine``. + +threadlocal.py + The ``TLEngine`` class is defined here, which is a subclass of + the generic ``Engine`` and tracks ``Connection`` and + ``Transaction`` objects against the identity of the current + thread. This allows certain programming patterns based around + the concept of a "thread-local connection" to be possible. + The ``TLEngine`` is created by using the "threadlocal" engine + strategy in conjunction with the ``create_engine()`` function. + +url.py + Defines the ``URL`` class which represents the individual + components of a string URL passed to ``create_engine()``. Also + defines a basic module-loading strategy for the dialect specifier + within a URL. +""" + +from .interfaces import ( + Connectable, + CreateEnginePlugin, + Dialect, + ExecutionContext, + ExceptionContext, + + # backwards compat + Compiled, + TypeCompiler +) + +from .base import ( + Connection, + Engine, + NestedTransaction, + RootTransaction, + Transaction, + TwoPhaseTransaction, +) + +from .result import ( + BaseRowProxy, + BufferedColumnResultProxy, + BufferedColumnRow, + BufferedRowResultProxy, + FullyBufferedResultProxy, + ResultProxy, + RowProxy, +) + +from .util import ( + connection_memoize +) + + +from . import util, strategies + +# backwards compat +from ..sql import ddl + +default_strategy = 'plain' + + +def create_engine(*args, **kwargs): + """Create a new :class:`.Engine` instance. + + The standard calling form is to send the URL as the + first positional argument, usually a string + that indicates database dialect and connection arguments:: + + + engine = create_engine("postgresql://scott:tiger@localhost/test") + + Additional keyword arguments may then follow it which + establish various options on the resulting :class:`.Engine` + and its underlying :class:`.Dialect` and :class:`.Pool` + constructs:: + + engine = create_engine("mysql://scott:tiger@hostname/dbname", + encoding='latin1', echo=True) + + The string form of the URL is + ``dialect[+driver]://user:password@host/dbname[?key=value..]``, where + ``dialect`` is a database name such as ``mysql``, ``oracle``, + ``postgresql``, etc., and ``driver`` the name of a DBAPI, such as + ``psycopg2``, ``pyodbc``, ``cx_oracle``, etc. Alternatively, + the URL can be an instance of :class:`~sqlalchemy.engine.url.URL`. + + ``**kwargs`` takes a wide variety of options which are routed + towards their appropriate components. Arguments may be specific to + the :class:`.Engine`, the underlying :class:`.Dialect`, as well as the + :class:`.Pool`. Specific dialects also accept keyword arguments that + are unique to that dialect. Here, we describe the parameters + that are common to most :func:`.create_engine()` usage. + + Once established, the newly resulting :class:`.Engine` will + request a connection from the underlying :class:`.Pool` once + :meth:`.Engine.connect` is called, or a method which depends on it + such as :meth:`.Engine.execute` is invoked. The :class:`.Pool` in turn + will establish the first actual DBAPI connection when this request + is received. The :func:`.create_engine` call itself does **not** + establish any actual DBAPI connections directly. + + .. seealso:: + + :doc:`/core/engines` + + :doc:`/dialects/index` + + :ref:`connections_toplevel` + + :param case_sensitive=True: if False, result column names + will match in a case-insensitive fashion, that is, + ``row['SomeColumn']``. + + .. versionchanged:: 0.8 + By default, result row names match case-sensitively. + In version 0.7 and prior, all matches were case-insensitive. + + :param connect_args: a dictionary of options which will be + passed directly to the DBAPI's ``connect()`` method as + additional keyword arguments. See the example + at :ref:`custom_dbapi_args`. + + :param convert_unicode=False: if set to True, sets + the default behavior of ``convert_unicode`` on the + :class:`.String` type to ``True``, regardless + of a setting of ``False`` on an individual + :class:`.String` type, thus causing all :class:`.String` + -based columns + to accommodate Python ``unicode`` objects. This flag + is useful as an engine-wide setting when using a + DBAPI that does not natively support Python + ``unicode`` objects and raises an error when + one is received (such as pyodbc with FreeTDS). + + See :class:`.String` for further details on + what this flag indicates. + + :param creator: a callable which returns a DBAPI connection. + This creation function will be passed to the underlying + connection pool and will be used to create all new database + connections. Usage of this function causes connection + parameters specified in the URL argument to be bypassed. + + :param echo=False: if True, the Engine will log all statements + as well as a repr() of their parameter lists to the engines + logger, which defaults to sys.stdout. The ``echo`` attribute of + ``Engine`` can be modified at any time to turn logging on and + off. If set to the string ``"debug"``, result rows will be + printed to the standard output as well. This flag ultimately + controls a Python logger; see :ref:`dbengine_logging` for + information on how to configure logging directly. + + :param echo_pool=False: if True, the connection pool will log + all checkouts/checkins to the logging stream, which defaults to + sys.stdout. This flag ultimately controls a Python logger; see + :ref:`dbengine_logging` for information on how to configure logging + directly. + + :param empty_in_strategy: The SQL compilation strategy to use when + rendering an IN or NOT IN expression for :meth:`.ColumnOperators.in_` + where the right-hand side + is an empty set. This is a string value that may be one of + ``static``, ``dynamic``, or ``dynamic_warn``. The ``static`` + strategy is the default, and an IN comparison to an empty set + will generate a simple false expression "1 != 1". The ``dynamic`` + strategy behaves like that of SQLAlchemy 1.1 and earlier, emitting + a false expression of the form "expr != expr", which has the effect + of evaluting to NULL in the case of a null expression. + ``dynamic_warn`` is the same as ``dynamic``, however also emits a + warning when an empty set is encountered; this because the "dynamic" + comparison is typically poorly performing on most databases. + + .. versionadded:: 1.2 Added the ``empty_in_strategy`` setting and + additionally defaulted the behavior for empty-set IN comparisons + to a static boolean expression. + + :param encoding: Defaults to ``utf-8``. This is the string + encoding used by SQLAlchemy for string encode/decode + operations which occur within SQLAlchemy, **outside of + the DBAPI.** Most modern DBAPIs feature some degree of + direct support for Python ``unicode`` objects, + what you see in Python 2 as a string of the form + ``u'some string'``. For those scenarios where the + DBAPI is detected as not supporting a Python ``unicode`` + object, this encoding is used to determine the + source/destination encoding. It is **not used** + for those cases where the DBAPI handles unicode + directly. + + To properly configure a system to accommodate Python + ``unicode`` objects, the DBAPI should be + configured to handle unicode to the greatest + degree as is appropriate - see + the notes on unicode pertaining to the specific + target database in use at :ref:`dialect_toplevel`. + + Areas where string encoding may need to be accommodated + outside of the DBAPI include zero or more of: + + * the values passed to bound parameters, corresponding to + the :class:`.Unicode` type or the :class:`.String` type + when ``convert_unicode`` is ``True``; + * the values returned in result set columns corresponding + to the :class:`.Unicode` type or the :class:`.String` + type when ``convert_unicode`` is ``True``; + * the string SQL statement passed to the DBAPI's + ``cursor.execute()`` method; + * the string names of the keys in the bound parameter + dictionary passed to the DBAPI's ``cursor.execute()`` + as well as ``cursor.setinputsizes()`` methods; + * the string column names retrieved from the DBAPI's + ``cursor.description`` attribute. + + When using Python 3, the DBAPI is required to support + *all* of the above values as Python ``unicode`` objects, + which in Python 3 are just known as ``str``. In Python 2, + the DBAPI does not specify unicode behavior at all, + so SQLAlchemy must make decisions for each of the above + values on a per-DBAPI basis - implementations are + completely inconsistent in their behavior. + + :param execution_options: Dictionary execution options which will + be applied to all connections. See + :meth:`~sqlalchemy.engine.Connection.execution_options` + + :param implicit_returning=True: When ``True``, a RETURNING- + compatible construct, if available, will be used to + fetch newly generated primary key values when a single row + INSERT statement is emitted with no existing returning() + clause. This applies to those backends which support RETURNING + or a compatible construct, including PostgreSQL, Firebird, Oracle, + Microsoft SQL Server. Set this to ``False`` to disable + the automatic usage of RETURNING. + + :param isolation_level: this string parameter is interpreted by various + dialects in order to affect the transaction isolation level of the + database connection. The parameter essentially accepts some subset of + these string arguments: ``"SERIALIZABLE"``, ``"REPEATABLE_READ"``, + ``"READ_COMMITTED"``, ``"READ_UNCOMMITTED"`` and ``"AUTOCOMMIT"``. + Behavior here varies per backend, and + individual dialects should be consulted directly. + + Note that the isolation level can also be set on a per-:class:`.Connection` + basis as well, using the + :paramref:`.Connection.execution_options.isolation_level` + feature. + + .. seealso:: + + :attr:`.Connection.default_isolation_level` - view default level + + :paramref:`.Connection.execution_options.isolation_level` + - set per :class:`.Connection` isolation level + + :ref:`SQLite Transaction Isolation <sqlite_isolation_level>` + + :ref:`PostgreSQL Transaction Isolation <postgresql_isolation_level>` + + :ref:`MySQL Transaction Isolation <mysql_isolation_level>` + + :ref:`session_transaction_isolation` - for the ORM + + :param label_length=None: optional integer value which limits + the size of dynamically generated column labels to that many + characters. If less than 6, labels are generated as + "_(counter)". If ``None``, the value of + ``dialect.max_identifier_length`` is used instead. + + :param listeners: A list of one or more + :class:`~sqlalchemy.interfaces.PoolListener` objects which will + receive connection pool events. + + :param logging_name: String identifier which will be used within + the "name" field of logging records generated within the + "sqlalchemy.engine" logger. Defaults to a hexstring of the + object's id. + + :param max_overflow=10: the number of connections to allow in + connection pool "overflow", that is connections that can be + opened above and beyond the pool_size setting, which defaults + to five. this is only used with :class:`~sqlalchemy.pool.QueuePool`. + + :param module=None: reference to a Python module object (the module + itself, not its string name). Specifies an alternate DBAPI module to + be used by the engine's dialect. Each sub-dialect references a + specific DBAPI which will be imported before first connect. This + parameter causes the import to be bypassed, and the given module to + be used instead. Can be used for testing of DBAPIs as well as to + inject "mock" DBAPI implementations into the :class:`.Engine`. + + :param paramstyle=None: The `paramstyle <http://legacy.python.org/dev/peps/pep-0249/#paramstyle>`_ + to use when rendering bound parameters. This style defaults to the + one recommended by the DBAPI itself, which is retrieved from the + ``.paramstyle`` attribute of the DBAPI. However, most DBAPIs accept + more than one paramstyle, and in particular it may be desirable + to change a "named" paramstyle into a "positional" one, or vice versa. + When this attribute is passed, it should be one of the values + ``"qmark"``, ``"numeric"``, ``"named"``, ``"format"`` or + ``"pyformat"``, and should correspond to a parameter style known + to be supported by the DBAPI in use. + + :param pool=None: an already-constructed instance of + :class:`~sqlalchemy.pool.Pool`, such as a + :class:`~sqlalchemy.pool.QueuePool` instance. If non-None, this + pool will be used directly as the underlying connection pool + for the engine, bypassing whatever connection parameters are + present in the URL argument. For information on constructing + connection pools manually, see :ref:`pooling_toplevel`. + + :param poolclass=None: a :class:`~sqlalchemy.pool.Pool` + subclass, which will be used to create a connection pool + instance using the connection parameters given in the URL. Note + this differs from ``pool`` in that you don't actually + instantiate the pool in this case, you just indicate what type + of pool to be used. + + :param pool_logging_name: String identifier which will be used within + the "name" field of logging records generated within the + "sqlalchemy.pool" logger. Defaults to a hexstring of the object's + id. + + :param pool_pre_ping: boolean, if True will enable the connection pool + "pre-ping" feature that tests connections for liveness upon + each checkout. + + .. versionadded:: 1.2 + + .. seealso:: + + :ref:`pool_disconnects_pessimistic` + + :param pool_size=5: the number of connections to keep open + inside the connection pool. This used with + :class:`~sqlalchemy.pool.QueuePool` as + well as :class:`~sqlalchemy.pool.SingletonThreadPool`. With + :class:`~sqlalchemy.pool.QueuePool`, a ``pool_size`` setting + of 0 indicates no limit; to disable pooling, set ``poolclass`` to + :class:`~sqlalchemy.pool.NullPool` instead. + + :param pool_recycle=-1: this setting causes the pool to recycle + connections after the given number of seconds has passed. It + defaults to -1, or no timeout. For example, setting to 3600 + means connections will be recycled after one hour. Note that + MySQL in particular will disconnect automatically if no + activity is detected on a connection for eight hours (although + this is configurable with the MySQLDB connection itself and the + server configuration as well). + + .. seealso:: + + :ref:`pool_setting_recycle` + + :param pool_reset_on_return='rollback': set the + :paramref:`.Pool.reset_on_return` parameter of the underlying + :class:`.Pool` object, which can be set to the values + ``"rollback"``, ``"commit"``, or ``None``. + + .. seealso:: + + :paramref:`.Pool.reset_on_return` + + :param pool_timeout=30: number of seconds to wait before giving + up on getting a connection from the pool. This is only used + with :class:`~sqlalchemy.pool.QueuePool`. + + :param plugins: string list of plugin names to load. See + :class:`.CreateEnginePlugin` for background. + + .. versionadded:: 1.2.3 + + :param strategy='plain': selects alternate engine implementations. + Currently available are: + + * the ``threadlocal`` strategy, which is described in + :ref:`threadlocal_strategy`; + * the ``mock`` strategy, which dispatches all statement + execution to a function passed as the argument ``executor``. + See `example in the FAQ + <http://docs.sqlalchemy.org/en/latest/faq/metadata_schema.html#how-can-i-get-the-create-table-drop-table-output-as-a-string>`_. + + :param executor=None: a function taking arguments + ``(sql, *multiparams, **params)``, to which the ``mock`` strategy will + dispatch all statement execution. Used only by ``strategy='mock'``. + + """ + + strategy = kwargs.pop('strategy', default_strategy) + strategy = strategies.strategies[strategy] + return strategy.create(*args, **kwargs) + + +def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs): + """Create a new Engine instance using a configuration dictionary. + + The dictionary is typically produced from a config file. + + The keys of interest to ``engine_from_config()`` should be prefixed, e.g. + ``sqlalchemy.url``, ``sqlalchemy.echo``, etc. The 'prefix' argument + indicates the prefix to be searched for. Each matching key (after the + prefix is stripped) is treated as though it were the corresponding keyword + argument to a :func:`.create_engine` call. + + The only required key is (assuming the default prefix) ``sqlalchemy.url``, + which provides the :ref:`database URL <database_urls>`. + + A select set of keyword arguments will be "coerced" to their + expected type based on string values. The set of arguments + is extensible per-dialect using the ``engine_config_types`` accessor. + + :param configuration: A dictionary (typically produced from a config file, + but this is not a requirement). Items whose keys start with the value + of 'prefix' will have that prefix stripped, and will then be passed to + :ref:`create_engine`. + + :param prefix: Prefix to match and then strip from keys + in 'configuration'. + + :param kwargs: Each keyword argument to ``engine_from_config()`` itself + overrides the corresponding item taken from the 'configuration' + dictionary. Keyword arguments should *not* be prefixed. + + """ + + options = dict((key[len(prefix):], configuration[key]) + for key in configuration + if key.startswith(prefix)) + options['_coerce_config'] = True + options.update(kwargs) + url = options.pop('url') + return create_engine(url, **options) + + +__all__ = ( + 'create_engine', + 'engine_from_config', +) diff --git a/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b928ae6f2019285c1131c454ba84d71a6298c973 GIT binary patch literal 20526 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/base.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/base.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46cfdf9f943d5cf9078c919708a171b68f28f3f6 GIT binary patch literal 67856 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/default.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/default.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..35288b79a8290436db63b1db7a48cdfce764e1e9 GIT binary patch literal 36124 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/interfaces.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/interfaces.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b749bc6fcdbb13af24c268dc6702938e4c510fb4 GIT binary patch literal 41112 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/reflection.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/reflection.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ee66b5810433c176e088489a5362612694170ff GIT binary patch literal 25622 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 E0X8gj#Q*>R literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/result.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/result.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c093ea1db8e7579af74596fca9528e5db7946d4 GIT binary patch literal 44084 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 Br~Lo` literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/strategies.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/strategies.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..707f659d0d3eccf314e9e6c4b6c7fcaba6be4e1b GIT binary patch literal 8468 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/threadlocal.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/threadlocal.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..739ca6670a4eed431adc5f3008f0c0d25f3c9e5a GIT binary patch literal 4593 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/url.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/url.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..238b3683af2f892d535d28f2814dc779ccff3890 GIT binary patch literal 8467 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/util.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/engine/__pycache__/util.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..13f9557c260d9bb46da6d68866f62d751d4bd7b3 GIT binary patch literal 1761 zcmZWp&2Aev5GJ`RX}$i5;{*i)^oIyiI5KQqdJBvoXi^kLQTLz}ZB;cgwM$8Bak(3k zva`k-D4+oC1M~@UYJpz+6n%re_T*RSsWU5A5?TtJ6*<FihBNc=lkM&B%^xR&hZ}_a zMb^e9{C@`1z624Im@1Oe9;GB^vG<nrSnS6Q_<F@|+>BewPaC}kCEpPJ&?mAv4b3Sf z_>-+UIS-^aCv!Oc9ntJt5(iz<-nd}^-KLd4bBT&5EO6ka57UEb-vi;uIsJ)@XiQ`F zHki|ET2eUfUH$n&Mwt;VGlx8tX_j0`F8FAsBbQ{FCmL2!rnRisokWi^Zk@1`P8f0+ zUujivk>@Ij#864@vgKjRFDFSf;Z95?*BKXPJWHi^mS=7v4R;d(=rzik2hLwkWE~2> zOqAk73H~x8V~pkOY|Rp5`F<*kAI{D?3lzLA4!k<%{vJ&G3`EJ!$(&rXB7}eRXYzn_ z!N@Vl@RLP`OH$I3&6$t3U~x>Yz0&Iv$TQfs>1o@mTDQ(CA6-|$7qa-wn9Q{Oij8#T zTd77>xQejyE`);Gco5`o<Zt=>N&jmrjqMlvcE9Mqkap^_y#HyIO^?15T8t&8=crp) zC)1-;z{m72q`v5@WZ1WflSjFTreJ6L_O%i!n#i>1OFd4s>|+CT@}k;^7ZsK99$tS7 z1YrSXG^9K9KE3*Q72x2u;+<oaMMBx9o#oj!GuT8+G`J-*A?%oK6qwyMU2LF-YVZH3 zXk)Pyq_wwU?!Y&~1rF>}gntgx?trNKl3YWd&N1EIX@qJVSW^V)!nD7GD2csaQnORi zF|_a)6#5qjyK`vnDKkHoz^(^SYX|jo%y}cIsu`7Ri`bs?ONQFmgS}VFl|Kiqc~!D5 zR3A}a|61!BV0E$BL5Uju7&Ntq8spt^wZM2+LOdlw%eV;_X^9WuuAe7R#kKw)nJk6& zmmHF=5<TXl%%uDvohg^(!idx!@`Hohe|2jk)r@pAgM3tpbvzsKVWMMRTS@2?poy79 zU?So4%+3HBnE9{q5+zP*CJU2Dd&^-2Khd`VS&7j5MqKW5`9?<2R`3oim@J}5=D^=P zgJy(np;qqg;nGFocq$5H?$SMXvs_7FzNrIR_k4rRa08Q6!C(MQZU%#@0hdYg$_M7y z({`isl+;z2*hItQqa#`Qn1rfP!&a?{u);Y59Nk<?V%1!vu;*V%pegp_Lew35XAlDf zJTZfX)kmPVk1&qVr#@}6Cf%Yz&6!=#_W~NSrWdjux=Vd_^(Zd}BS`TO2)Uxqe;|ST z%>}c7EAKDe*MZbAY_6evi2iH3&|u%-%G?EUd-AlsQIVRGHIr(6@9ydq=wP9gt-Vy6 yCuyD;S3gcwYpk*%kh^Nm@>oE6S9w?k5*(*lJX7*%fKgy<TV8N4XfwF&4*duG^V9nP literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/engine/base.py b/venv/Lib/site-packages/sqlalchemy/engine/base.py new file mode 100644 index 0000000..4a057ee --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/engine/base.py @@ -0,0 +1,2234 @@ +# engine/base.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +from __future__ import with_statement + +"""Defines :class:`.Connection` and :class:`.Engine`. + +""" + + +import sys +from .. import exc, util, log, interfaces +from ..sql import util as sql_util +from ..sql import schema +from .interfaces import Connectable, ExceptionContext +from .util import _distill_params +import contextlib + + +class Connection(Connectable): + """Provides high-level functionality for a wrapped DB-API connection. + + Provides execution support for string-based SQL statements as well as + :class:`.ClauseElement`, :class:`.Compiled` and :class:`.DefaultGenerator` + objects. Provides a :meth:`begin` method to return :class:`.Transaction` + objects. + + The Connection object is **not** thread-safe. While a Connection can be + shared among threads using properly synchronized access, it is still + possible that the underlying DBAPI connection may not support shared + access between threads. Check the DBAPI documentation for details. + + The Connection object represents a single dbapi connection checked out + from the connection pool. In this state, the connection pool has no affect + upon the connection, including its expiration or timeout state. For the + connection pool to properly manage connections, connections should be + returned to the connection pool (i.e. ``connection.close()``) whenever the + connection is not in use. + + .. index:: + single: thread safety; Connection + + """ + + schema_for_object = schema._schema_getter(None) + """Return the ".schema" attribute for an object. + + Used for :class:`.Table`, :class:`.Sequence` and similar objects, + and takes into account + the :paramref:`.Connection.execution_options.schema_translate_map` + parameter. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`schema_translating` + + """ + + def __init__(self, engine, connection=None, close_with_result=False, + _branch_from=None, _execution_options=None, + _dispatch=None, + _has_events=None): + """Construct a new Connection. + + The constructor here is not public and is only called only by an + :class:`.Engine`. See :meth:`.Engine.connect` and + :meth:`.Engine.contextual_connect` methods. + + """ + self.engine = engine + self.dialect = engine.dialect + self.__branch_from = _branch_from + self.__branch = _branch_from is not None + + if _branch_from: + self.__connection = connection + self._execution_options = _execution_options + self._echo = _branch_from._echo + self.should_close_with_result = False + self.dispatch = _dispatch + self._has_events = _branch_from._has_events + self.schema_for_object = _branch_from.schema_for_object + else: + self.__connection = connection \ + if connection is not None else engine.raw_connection() + self.__transaction = None + self.__savepoint_seq = 0 + self.should_close_with_result = close_with_result + self.__invalid = False + self.__can_reconnect = True + self._echo = self.engine._should_log_info() + + if _has_events is None: + # if _has_events is sent explicitly as False, + # then don't join the dispatch of the engine; we don't + # want to handle any of the engine's events in that case. + self.dispatch = self.dispatch._join(engine.dispatch) + self._has_events = _has_events or ( + _has_events is None and engine._has_events) + + assert not _execution_options + self._execution_options = engine._execution_options + + if self._has_events or self.engine._has_events: + self.dispatch.engine_connect(self, self.__branch) + + def _branch(self): + """Return a new Connection which references this Connection's + engine and connection; but does not have close_with_result enabled, + and also whose close() method does nothing. + + The Core uses this very sparingly, only in the case of + custom SQL default functions that are to be INSERTed as the + primary key of a row where we need to get the value back, so we have + to invoke it distinctly - this is a very uncommon case. + + Userland code accesses _branch() when the connect() or + contextual_connect() methods are called. The branched connection + acts as much as possible like the parent, except that it stays + connected when a close() event occurs. + + """ + if self.__branch_from: + return self.__branch_from._branch() + else: + return self.engine._connection_cls( + self.engine, + self.__connection, + _branch_from=self, + _execution_options=self._execution_options, + _has_events=self._has_events, + _dispatch=self.dispatch) + + @property + def _root(self): + """return the 'root' connection. + + Returns 'self' if this connection is not a branch, else + returns the root connection from which we ultimately branched. + + """ + + if self.__branch_from: + return self.__branch_from + else: + return self + + def _clone(self): + """Create a shallow copy of this Connection. + + """ + c = self.__class__.__new__(self.__class__) + c.__dict__ = self.__dict__.copy() + return c + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + def execution_options(self, **opt): + r""" Set non-SQL options for the connection which take effect + during execution. + + The method returns a copy of this :class:`.Connection` which references + the same underlying DBAPI connection, but also defines the given + execution options which will take effect for a call to + :meth:`execute`. As the new :class:`.Connection` references the same + underlying resource, it's usually a good idea to ensure that the copies + will be discarded immediately, which is implicit if used as in:: + + result = connection.execution_options(stream_results=True).\ + execute(stmt) + + Note that any key/value can be passed to + :meth:`.Connection.execution_options`, and it will be stored in the + ``_execution_options`` dictionary of the :class:`.Connection`. It + is suitable for usage by end-user schemes to communicate with + event listeners, for example. + + The keywords that are currently recognized by SQLAlchemy itself + include all those listed under :meth:`.Executable.execution_options`, + as well as others that are specific to :class:`.Connection`. + + :param autocommit: Available on: Connection, statement. + When True, a COMMIT will be invoked after execution + when executed in 'autocommit' mode, i.e. when an explicit + transaction is not begun on the connection. Note that DBAPI + connections by default are always in a transaction - SQLAlchemy uses + rules applied to different kinds of statements to determine if + COMMIT will be invoked in order to provide its "autocommit" feature. + Typically, all INSERT/UPDATE/DELETE statements as well as + CREATE/DROP statements have autocommit behavior enabled; SELECT + constructs do not. Use this option when invoking a SELECT or other + specific SQL construct where COMMIT is desired (typically when + calling stored procedures and such), and an explicit + transaction is not in progress. + + :param compiled_cache: Available on: Connection. + A dictionary where :class:`.Compiled` objects + will be cached when the :class:`.Connection` compiles a clause + expression into a :class:`.Compiled` object. + It is the user's responsibility to + manage the size of this dictionary, which will have keys + corresponding to the dialect, clause element, the column + names within the VALUES or SET clause of an INSERT or UPDATE, + as well as the "batch" mode for an INSERT or UPDATE statement. + The format of this dictionary is not guaranteed to stay the + same in future releases. + + Note that the ORM makes use of its own "compiled" caches for + some operations, including flush operations. The caching + used by the ORM internally supersedes a cache dictionary + specified here. + + :param isolation_level: Available on: :class:`.Connection`. + Set the transaction isolation level for + the lifespan of this :class:`.Connection` object (*not* the + underlying DBAPI connection, for which the level is reset + to its original setting upon termination of this + :class:`.Connection` object). + + Valid values include + those string values accepted by the + :paramref:`.create_engine.isolation_level` + parameter passed to :func:`.create_engine`. These levels are + semi-database specific; see individual dialect documentation for + valid levels. + + Note that this option necessarily affects the underlying + DBAPI connection for the lifespan of the originating + :class:`.Connection`, and is not per-execution. This + setting is not removed until the underlying DBAPI connection + is returned to the connection pool, i.e. + the :meth:`.Connection.close` method is called. + + .. warning:: The ``isolation_level`` execution option should + **not** be used when a transaction is already established, that + is, the :meth:`.Connection.begin` method or similar has been + called. A database cannot change the isolation level on a + transaction in progress, and different DBAPIs and/or + SQLAlchemy dialects may implicitly roll back or commit + the transaction, or not affect the connection at all. + + .. versionchanged:: 0.9.9 A warning is emitted when the + ``isolation_level`` execution option is used after a + transaction has been started with :meth:`.Connection.begin` + or similar. + + .. note:: The ``isolation_level`` execution option is implicitly + reset if the :class:`.Connection` is invalidated, e.g. via + the :meth:`.Connection.invalidate` method, or if a + disconnection error occurs. The new connection produced after + the invalidation will not have the isolation level re-applied + to it automatically. + + .. seealso:: + + :paramref:`.create_engine.isolation_level` + - set per :class:`.Engine` isolation level + + :meth:`.Connection.get_isolation_level` - view current level + + :ref:`SQLite Transaction Isolation <sqlite_isolation_level>` + + :ref:`PostgreSQL Transaction Isolation <postgresql_isolation_level>` + + :ref:`MySQL Transaction Isolation <mysql_isolation_level>` + + :ref:`SQL Server Transaction Isolation <mssql_isolation_level>` + + :ref:`session_transaction_isolation` - for the ORM + + :param no_parameters: When ``True``, if the final parameter + list or dictionary is totally empty, will invoke the + statement on the cursor as ``cursor.execute(statement)``, + not passing the parameter collection at all. + Some DBAPIs such as psycopg2 and mysql-python consider + percent signs as significant only when parameters are + present; this option allows code to generate SQL + containing percent signs (and possibly other characters) + that is neutral regarding whether it's executed by the DBAPI + or piped into a script that's later invoked by + command line tools. + + .. versionadded:: 0.7.6 + + :param stream_results: Available on: Connection, statement. + Indicate to the dialect that results should be + "streamed" and not pre-buffered, if possible. This is a limitation + of many DBAPIs. The flag is currently understood only by the + psycopg2, mysqldb and pymysql dialects. + + :param schema_translate_map: Available on: Connection, Engine. + A dictionary mapping schema names to schema names, that will be + applied to the :paramref:`.Table.schema` element of each + :class:`.Table` encountered when SQL or DDL expression elements + are compiled into strings; the resulting schema name will be + converted based on presence in the map of the original name. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`schema_translating` + + """ + c = self._clone() + c._execution_options = c._execution_options.union(opt) + if self._has_events or self.engine._has_events: + self.dispatch.set_connection_execution_options(c, opt) + self.dialect.set_connection_execution_options(c, opt) + return c + + @property + def closed(self): + """Return True if this connection is closed.""" + + return '_Connection__connection' not in self.__dict__ \ + and not self.__can_reconnect + + @property + def invalidated(self): + """Return True if this connection was invalidated.""" + + return self._root.__invalid + + @property + def connection(self): + """The underlying DB-API connection managed by this Connection. + + .. seealso:: + + + :ref:`dbapi_connections` + + """ + + try: + return self.__connection + except AttributeError: + # escape "except AttributeError" before revalidating + # to prevent misleading stacktraces in Py3K + pass + try: + return self._revalidate_connection() + except BaseException as e: + self._handle_dbapi_exception(e, None, None, None, None) + + def get_isolation_level(self): + """Return the current isolation level assigned to this + :class:`.Connection`. + + This will typically be the default isolation level as determined + by the dialect, unless if the + :paramref:`.Connection.execution_options.isolation_level` + feature has been used to alter the isolation level on a + per-:class:`.Connection` basis. + + This attribute will typically perform a live SQL operation in order + to procure the current isolation level, so the value returned is the + actual level on the underlying DBAPI connection regardless of how + this state was set. Compare to the + :attr:`.Connection.default_isolation_level` accessor + which returns the dialect-level setting without performing a SQL + query. + + .. versionadded:: 0.9.9 + + .. seealso:: + + :attr:`.Connection.default_isolation_level` - view default level + + :paramref:`.create_engine.isolation_level` + - set per :class:`.Engine` isolation level + + :paramref:`.Connection.execution_options.isolation_level` + - set per :class:`.Connection` isolation level + + """ + try: + return self.dialect.get_isolation_level(self.connection) + except BaseException as e: + self._handle_dbapi_exception(e, None, None, None, None) + + @property + def default_isolation_level(self): + """The default isolation level assigned to this :class:`.Connection`. + + This is the isolation level setting that the :class:`.Connection` + has when first procured via the :meth:`.Engine.connect` method. + This level stays in place until the + :paramref:`.Connection.execution_options.isolation_level` is used + to change the setting on a per-:class:`.Connection` basis. + + Unlike :meth:`.Connection.get_isolation_level`, this attribute is set + ahead of time from the first connection procured by the dialect, + so SQL query is not invoked when this accessor is called. + + .. versionadded:: 0.9.9 + + .. seealso:: + + :meth:`.Connection.get_isolation_level` - view current level + + :paramref:`.create_engine.isolation_level` + - set per :class:`.Engine` isolation level + + :paramref:`.Connection.execution_options.isolation_level` + - set per :class:`.Connection` isolation level + + """ + return self.dialect.default_isolation_level + + def _revalidate_connection(self): + if self.__branch_from: + return self.__branch_from._revalidate_connection() + if self.__can_reconnect and self.__invalid: + if self.__transaction is not None: + raise exc.InvalidRequestError( + "Can't reconnect until invalid " + "transaction is rolled back") + self.__connection = self.engine.raw_connection(_connection=self) + self.__invalid = False + return self.__connection + raise exc.ResourceClosedError("This Connection is closed") + + @property + def _connection_is_valid(self): + # use getattr() for is_valid to support exceptions raised in + # dialect initializer, where the connection is not wrapped in + # _ConnectionFairy + + return getattr(self.__connection, 'is_valid', False) + + @property + def _still_open_and_connection_is_valid(self): + return \ + not self.closed and \ + not self.invalidated and \ + getattr(self.__connection, 'is_valid', False) + + @property + def info(self): + """Info dictionary associated with the underlying DBAPI connection + referred to by this :class:`.Connection`, allowing user-defined + data to be associated with the connection. + + The data here will follow along with the DBAPI connection including + after it is returned to the connection pool and used again + in subsequent instances of :class:`.Connection`. + + """ + + return self.connection.info + + def connect(self): + """Returns a branched version of this :class:`.Connection`. + + The :meth:`.Connection.close` method on the returned + :class:`.Connection` can be called and this + :class:`.Connection` will remain open. + + This method provides usage symmetry with + :meth:`.Engine.connect`, including for usage + with context managers. + + """ + + return self._branch() + + def contextual_connect(self, **kwargs): + """Returns a branched version of this :class:`.Connection`. + + The :meth:`.Connection.close` method on the returned + :class:`.Connection` can be called and this + :class:`.Connection` will remain open. + + This method provides usage symmetry with + :meth:`.Engine.contextual_connect`, including for usage + with context managers. + + """ + + return self._branch() + + def invalidate(self, exception=None): + """Invalidate the underlying DBAPI connection associated with + this :class:`.Connection`. + + The underlying DBAPI connection is literally closed (if + possible), and is discarded. Its source connection pool will + typically lazily create a new connection to replace it. + + Upon the next use (where "use" typically means using the + :meth:`.Connection.execute` method or similar), + this :class:`.Connection` will attempt to + procure a new DBAPI connection using the services of the + :class:`.Pool` as a source of connectivity (e.g. a "reconnection"). + + If a transaction was in progress (e.g. the + :meth:`.Connection.begin` method has been called) when + :meth:`.Connection.invalidate` method is called, at the DBAPI + level all state associated with this transaction is lost, as + the DBAPI connection is closed. The :class:`.Connection` + will not allow a reconnection to proceed until the + :class:`.Transaction` object is ended, by calling the + :meth:`.Transaction.rollback` method; until that point, any attempt at + continuing to use the :class:`.Connection` will raise an + :class:`~sqlalchemy.exc.InvalidRequestError`. + This is to prevent applications from accidentally + continuing an ongoing transactional operations despite the + fact that the transaction has been lost due to an + invalidation. + + The :meth:`.Connection.invalidate` method, just like auto-invalidation, + will at the connection pool level invoke the + :meth:`.PoolEvents.invalidate` event. + + .. seealso:: + + :ref:`pool_connection_invalidation` + + """ + + if self.invalidated: + return + + if self.closed: + raise exc.ResourceClosedError("This Connection is closed") + + if self._root._connection_is_valid: + self._root.__connection.invalidate(exception) + del self._root.__connection + self._root.__invalid = True + + def detach(self): + """Detach the underlying DB-API connection from its connection pool. + + E.g.:: + + with engine.connect() as conn: + conn.detach() + conn.execute("SET search_path TO schema1, schema2") + + # work with connection + + # connection is fully closed (since we used "with:", can + # also call .close()) + + This :class:`.Connection` instance will remain usable. When closed + (or exited from a context manager context as above), + the DB-API connection will be literally closed and not + returned to its originating pool. + + This method can be used to insulate the rest of an application + from a modified state on a connection (such as a transaction + isolation level or similar). + + """ + + self.__connection.detach() + + def begin(self): + """Begin a transaction and return a transaction handle. + + The returned object is an instance of :class:`.Transaction`. + This object represents the "scope" of the transaction, + which completes when either the :meth:`.Transaction.rollback` + or :meth:`.Transaction.commit` method is called. + + Nested calls to :meth:`.begin` on the same :class:`.Connection` + will return new :class:`.Transaction` objects that represent + an emulated transaction within the scope of the enclosing + transaction, that is:: + + trans = conn.begin() # outermost transaction + trans2 = conn.begin() # "nested" + trans2.commit() # does nothing + trans.commit() # actually commits + + Calls to :meth:`.Transaction.commit` only have an effect + when invoked via the outermost :class:`.Transaction` object, though the + :meth:`.Transaction.rollback` method of any of the + :class:`.Transaction` objects will roll back the + transaction. + + See also: + + :meth:`.Connection.begin_nested` - use a SAVEPOINT + + :meth:`.Connection.begin_twophase` - use a two phase /XID transaction + + :meth:`.Engine.begin` - context manager available from + :class:`.Engine`. + + """ + if self.__branch_from: + return self.__branch_from.begin() + + if self.__transaction is None: + self.__transaction = RootTransaction(self) + return self.__transaction + else: + return Transaction(self, self.__transaction) + + def begin_nested(self): + """Begin a nested transaction and return a transaction handle. + + The returned object is an instance of :class:`.NestedTransaction`. + + Nested transactions require SAVEPOINT support in the + underlying database. Any transaction in the hierarchy may + ``commit`` and ``rollback``, however the outermost transaction + still controls the overall ``commit`` or ``rollback`` of the + transaction of a whole. + + See also :meth:`.Connection.begin`, + :meth:`.Connection.begin_twophase`. + """ + if self.__branch_from: + return self.__branch_from.begin_nested() + + if self.__transaction is None: + self.__transaction = RootTransaction(self) + else: + self.__transaction = NestedTransaction(self, self.__transaction) + return self.__transaction + + def begin_twophase(self, xid=None): + """Begin a two-phase or XA transaction and return a transaction + handle. + + The returned object is an instance of :class:`.TwoPhaseTransaction`, + which in addition to the methods provided by + :class:`.Transaction`, also provides a + :meth:`~.TwoPhaseTransaction.prepare` method. + + :param xid: the two phase transaction id. If not supplied, a + random id will be generated. + + See also :meth:`.Connection.begin`, + :meth:`.Connection.begin_twophase`. + + """ + + if self.__branch_from: + return self.__branch_from.begin_twophase(xid=xid) + + if self.__transaction is not None: + raise exc.InvalidRequestError( + "Cannot start a two phase transaction when a transaction " + "is already in progress.") + if xid is None: + xid = self.engine.dialect.create_xid() + self.__transaction = TwoPhaseTransaction(self, xid) + return self.__transaction + + def recover_twophase(self): + return self.engine.dialect.do_recover_twophase(self) + + def rollback_prepared(self, xid, recover=False): + self.engine.dialect.do_rollback_twophase(self, xid, recover=recover) + + def commit_prepared(self, xid, recover=False): + self.engine.dialect.do_commit_twophase(self, xid, recover=recover) + + def in_transaction(self): + """Return True if a transaction is in progress.""" + return self._root.__transaction is not None + + def _begin_impl(self, transaction): + assert not self.__branch_from + + if self._echo: + self.engine.logger.info("BEGIN (implicit)") + + if self._has_events or self.engine._has_events: + self.dispatch.begin(self) + + try: + self.engine.dialect.do_begin(self.connection) + if self.connection._reset_agent is None: + self.connection._reset_agent = transaction + except BaseException as e: + self._handle_dbapi_exception(e, None, None, None, None) + + def _rollback_impl(self): + assert not self.__branch_from + + if self._has_events or self.engine._has_events: + self.dispatch.rollback(self) + + if self._still_open_and_connection_is_valid: + if self._echo: + self.engine.logger.info("ROLLBACK") + try: + self.engine.dialect.do_rollback(self.connection) + except BaseException as e: + self._handle_dbapi_exception(e, None, None, None, None) + finally: + if not self.__invalid and \ + self.connection._reset_agent is self.__transaction: + self.connection._reset_agent = None + self.__transaction = None + else: + self.__transaction = None + + def _commit_impl(self, autocommit=False): + assert not self.__branch_from + + if self._has_events or self.engine._has_events: + self.dispatch.commit(self) + + if self._echo: + self.engine.logger.info("COMMIT") + try: + self.engine.dialect.do_commit(self.connection) + except BaseException as e: + self._handle_dbapi_exception(e, None, None, None, None) + finally: + if not self.__invalid and \ + self.connection._reset_agent is self.__transaction: + self.connection._reset_agent = None + self.__transaction = None + + def _savepoint_impl(self, name=None): + assert not self.__branch_from + + if self._has_events or self.engine._has_events: + self.dispatch.savepoint(self, name) + + if name is None: + self.__savepoint_seq += 1 + name = 'sa_savepoint_%s' % self.__savepoint_seq + if self._still_open_and_connection_is_valid: + self.engine.dialect.do_savepoint(self, name) + return name + + def _rollback_to_savepoint_impl(self, name, context): + assert not self.__branch_from + + if self._has_events or self.engine._has_events: + self.dispatch.rollback_savepoint(self, name, context) + + if self._still_open_and_connection_is_valid: + self.engine.dialect.do_rollback_to_savepoint(self, name) + self.__transaction = context + + def _release_savepoint_impl(self, name, context): + assert not self.__branch_from + + if self._has_events or self.engine._has_events: + self.dispatch.release_savepoint(self, name, context) + + if self._still_open_and_connection_is_valid: + self.engine.dialect.do_release_savepoint(self, name) + self.__transaction = context + + def _begin_twophase_impl(self, transaction): + assert not self.__branch_from + + if self._echo: + self.engine.logger.info("BEGIN TWOPHASE (implicit)") + if self._has_events or self.engine._has_events: + self.dispatch.begin_twophase(self, transaction.xid) + + if self._still_open_and_connection_is_valid: + self.engine.dialect.do_begin_twophase(self, transaction.xid) + + if self.connection._reset_agent is None: + self.connection._reset_agent = transaction + + def _prepare_twophase_impl(self, xid): + assert not self.__branch_from + + if self._has_events or self.engine._has_events: + self.dispatch.prepare_twophase(self, xid) + + if self._still_open_and_connection_is_valid: + assert isinstance(self.__transaction, TwoPhaseTransaction) + self.engine.dialect.do_prepare_twophase(self, xid) + + def _rollback_twophase_impl(self, xid, is_prepared): + assert not self.__branch_from + + if self._has_events or self.engine._has_events: + self.dispatch.rollback_twophase(self, xid, is_prepared) + + if self._still_open_and_connection_is_valid: + assert isinstance(self.__transaction, TwoPhaseTransaction) + try: + self.engine.dialect.do_rollback_twophase( + self, xid, is_prepared) + finally: + if self.connection._reset_agent is self.__transaction: + self.connection._reset_agent = None + self.__transaction = None + else: + self.__transaction = None + + def _commit_twophase_impl(self, xid, is_prepared): + assert not self.__branch_from + + if self._has_events or self.engine._has_events: + self.dispatch.commit_twophase(self, xid, is_prepared) + + if self._still_open_and_connection_is_valid: + assert isinstance(self.__transaction, TwoPhaseTransaction) + try: + self.engine.dialect.do_commit_twophase(self, xid, is_prepared) + finally: + if self.connection._reset_agent is self.__transaction: + self.connection._reset_agent = None + self.__transaction = None + else: + self.__transaction = None + + def _autorollback(self): + if not self._root.in_transaction(): + self._root._rollback_impl() + + def close(self): + """Close this :class:`.Connection`. + + This results in a release of the underlying database + resources, that is, the DBAPI connection referenced + internally. The DBAPI connection is typically restored + back to the connection-holding :class:`.Pool` referenced + by the :class:`.Engine` that produced this + :class:`.Connection`. Any transactional state present on + the DBAPI connection is also unconditionally released via + the DBAPI connection's ``rollback()`` method, regardless + of any :class:`.Transaction` object that may be + outstanding with regards to this :class:`.Connection`. + + After :meth:`~.Connection.close` is called, the + :class:`.Connection` is permanently in a closed state, + and will allow no further operations. + + """ + if self.__branch_from: + try: + del self.__connection + except AttributeError: + pass + finally: + self.__can_reconnect = False + return + try: + conn = self.__connection + except AttributeError: + pass + else: + + conn.close() + if conn._reset_agent is self.__transaction: + conn._reset_agent = None + + # the close() process can end up invalidating us, + # as the pool will call our transaction as the "reset_agent" + # for rollback(), which can then cause an invalidation + if not self.__invalid: + del self.__connection + self.__can_reconnect = False + self.__transaction = None + + def scalar(self, object, *multiparams, **params): + """Executes and returns the first column of the first row. + + The underlying result/cursor is closed after execution. + """ + + return self.execute(object, *multiparams, **params).scalar() + + def execute(self, object, *multiparams, **params): + r"""Executes a SQL statement construct and returns a + :class:`.ResultProxy`. + + :param object: The statement to be executed. May be + one of: + + * a plain string + * any :class:`.ClauseElement` construct that is also + a subclass of :class:`.Executable`, such as a + :func:`~.expression.select` construct + * a :class:`.FunctionElement`, such as that generated + by :data:`.func`, will be automatically wrapped in + a SELECT statement, which is then executed. + * a :class:`.DDLElement` object + * a :class:`.DefaultGenerator` object + * a :class:`.Compiled` object + + :param \*multiparams/\**params: represent bound parameter + values to be used in the execution. Typically, + the format is either a collection of one or more + dictionaries passed to \*multiparams:: + + conn.execute( + table.insert(), + {"id":1, "value":"v1"}, + {"id":2, "value":"v2"} + ) + + ...or individual key/values interpreted by \**params:: + + conn.execute( + table.insert(), id=1, value="v1" + ) + + In the case that a plain SQL string is passed, and the underlying + DBAPI accepts positional bind parameters, a collection of tuples + or individual values in \*multiparams may be passed:: + + conn.execute( + "INSERT INTO table (id, value) VALUES (?, ?)", + (1, "v1"), (2, "v2") + ) + + conn.execute( + "INSERT INTO table (id, value) VALUES (?, ?)", + 1, "v1" + ) + + Note above, the usage of a question mark "?" or other + symbol is contingent upon the "paramstyle" accepted by the DBAPI + in use, which may be any of "qmark", "named", "pyformat", "format", + "numeric". See `pep-249 <http://www.python.org/dev/peps/pep-0249/>`_ + for details on paramstyle. + + To execute a textual SQL statement which uses bound parameters in a + DBAPI-agnostic way, use the :func:`~.expression.text` construct. + + """ + if isinstance(object, util.string_types[0]): + return self._execute_text(object, multiparams, params) + try: + meth = object._execute_on_connection + except AttributeError: + raise exc.ObjectNotExecutableError(object) + else: + return meth(self, multiparams, params) + + def _execute_function(self, func, multiparams, params): + """Execute a sql.FunctionElement object.""" + + return self._execute_clauseelement(func.select(), + multiparams, params) + + def _execute_default(self, default, multiparams, params): + """Execute a schema.ColumnDefault object.""" + + if self._has_events or self.engine._has_events: + for fn in self.dispatch.before_execute: + default, multiparams, params = \ + fn(self, default, multiparams, params) + + try: + try: + conn = self.__connection + except AttributeError: + # escape "except AttributeError" before revalidating + # to prevent misleading stacktraces in Py3K + conn = None + if conn is None: + conn = self._revalidate_connection() + + dialect = self.dialect + ctx = dialect.execution_ctx_cls._init_default( + dialect, self, conn) + except BaseException as e: + self._handle_dbapi_exception(e, None, None, None, None) + + ret = ctx._exec_default(None, default, None) + if self.should_close_with_result: + self.close() + + if self._has_events or self.engine._has_events: + self.dispatch.after_execute(self, + default, multiparams, params, ret) + + return ret + + def _execute_ddl(self, ddl, multiparams, params): + """Execute a schema.DDL object.""" + + if self._has_events or self.engine._has_events: + for fn in self.dispatch.before_execute: + ddl, multiparams, params = \ + fn(self, ddl, multiparams, params) + + dialect = self.dialect + + compiled = ddl.compile( + dialect=dialect, + schema_translate_map=self.schema_for_object + if not self.schema_for_object.is_default else None) + ret = self._execute_context( + dialect, + dialect.execution_ctx_cls._init_ddl, + compiled, + None, + compiled + ) + if self._has_events or self.engine._has_events: + self.dispatch.after_execute(self, + ddl, multiparams, params, ret) + return ret + + def _execute_clauseelement(self, elem, multiparams, params): + """Execute a sql.ClauseElement object.""" + + if self._has_events or self.engine._has_events: + for fn in self.dispatch.before_execute: + elem, multiparams, params = \ + fn(self, elem, multiparams, params) + + distilled_params = _distill_params(multiparams, params) + if distilled_params: + # ensure we don't retain a link to the view object for keys() + # which links to the values, which we don't want to cache + keys = list(distilled_params[0].keys()) + else: + keys = [] + + dialect = self.dialect + if 'compiled_cache' in self._execution_options: + key = ( + dialect, elem, tuple(sorted(keys)), + self.schema_for_object.hash_key, + len(distilled_params) > 1 + ) + compiled_sql = self._execution_options['compiled_cache'].get(key) + if compiled_sql is None: + compiled_sql = elem.compile( + dialect=dialect, column_keys=keys, + inline=len(distilled_params) > 1, + schema_translate_map=self.schema_for_object + if not self.schema_for_object.is_default else None + ) + self._execution_options['compiled_cache'][key] = compiled_sql + else: + compiled_sql = elem.compile( + dialect=dialect, column_keys=keys, + inline=len(distilled_params) > 1, + schema_translate_map=self.schema_for_object + if not self.schema_for_object.is_default else None) + + ret = self._execute_context( + dialect, + dialect.execution_ctx_cls._init_compiled, + compiled_sql, + distilled_params, + compiled_sql, distilled_params + ) + if self._has_events or self.engine._has_events: + self.dispatch.after_execute(self, + elem, multiparams, params, ret) + return ret + + def _execute_compiled(self, compiled, multiparams, params): + """Execute a sql.Compiled object.""" + + if self._has_events or self.engine._has_events: + for fn in self.dispatch.before_execute: + compiled, multiparams, params = \ + fn(self, compiled, multiparams, params) + + dialect = self.dialect + parameters = _distill_params(multiparams, params) + ret = self._execute_context( + dialect, + dialect.execution_ctx_cls._init_compiled, + compiled, + parameters, + compiled, parameters + ) + if self._has_events or self.engine._has_events: + self.dispatch.after_execute(self, + compiled, multiparams, params, ret) + return ret + + def _execute_text(self, statement, multiparams, params): + """Execute a string SQL statement.""" + + if self._has_events or self.engine._has_events: + for fn in self.dispatch.before_execute: + statement, multiparams, params = \ + fn(self, statement, multiparams, params) + + dialect = self.dialect + parameters = _distill_params(multiparams, params) + ret = self._execute_context( + dialect, + dialect.execution_ctx_cls._init_statement, + statement, + parameters, + statement, parameters + ) + if self._has_events or self.engine._has_events: + self.dispatch.after_execute(self, + statement, multiparams, params, ret) + return ret + + def _execute_context(self, dialect, constructor, + statement, parameters, + *args): + """Create an :class:`.ExecutionContext` and execute, returning + a :class:`.ResultProxy`.""" + + try: + try: + conn = self.__connection + except AttributeError: + # escape "except AttributeError" before revalidating + # to prevent misleading stacktraces in Py3K + conn = None + if conn is None: + conn = self._revalidate_connection() + + context = constructor(dialect, self, conn, *args) + except BaseException as e: + self._handle_dbapi_exception( + e, + util.text_type(statement), parameters, + None, None) + + if context.compiled: + context.pre_exec() + + cursor, statement, parameters = context.cursor, \ + context.statement, \ + context.parameters + + if not context.executemany: + parameters = parameters[0] + + if self._has_events or self.engine._has_events: + for fn in self.dispatch.before_cursor_execute: + statement, parameters = \ + fn(self, cursor, statement, parameters, + context, context.executemany) + + if self._echo: + self.engine.logger.info(statement) + self.engine.logger.info( + "%r", + sql_util._repr_params(parameters, batches=10) + ) + + evt_handled = False + try: + if context.executemany: + if self.dialect._has_events: + for fn in self.dialect.dispatch.do_executemany: + if fn(cursor, statement, parameters, context): + evt_handled = True + break + if not evt_handled: + self.dialect.do_executemany( + cursor, + statement, + parameters, + context) + elif not parameters and context.no_parameters: + if self.dialect._has_events: + for fn in self.dialect.dispatch.do_execute_no_params: + if fn(cursor, statement, context): + evt_handled = True + break + if not evt_handled: + self.dialect.do_execute_no_params( + cursor, + statement, + context) + else: + if self.dialect._has_events: + for fn in self.dialect.dispatch.do_execute: + if fn(cursor, statement, parameters, context): + evt_handled = True + break + if not evt_handled: + self.dialect.do_execute( + cursor, + statement, + parameters, + context) + except BaseException as e: + self._handle_dbapi_exception( + e, + statement, + parameters, + cursor, + context) + + if self._has_events or self.engine._has_events: + self.dispatch.after_cursor_execute(self, cursor, + statement, + parameters, + context, + context.executemany) + + if context.compiled: + context.post_exec() + + if context.is_crud or context.is_text: + result = context._setup_crud_result_proxy() + else: + result = context.get_result_proxy() + if result._metadata is None: + result._soft_close() + + if context.should_autocommit and self._root.__transaction is None: + self._root._commit_impl(autocommit=True) + + # for "connectionless" execution, we have to close this + # Connection after the statement is complete. + if self.should_close_with_result: + # ResultProxy already exhausted rows / has no rows. + # close us now + if result._soft_closed: + self.close() + else: + # ResultProxy will close this Connection when no more + # rows to fetch. + result._autoclose_connection = True + return result + + def _cursor_execute(self, cursor, statement, parameters, context=None): + """Execute a statement + params on the given cursor. + + Adds appropriate logging and exception handling. + + This method is used by DefaultDialect for special-case + executions, such as for sequences and column defaults. + The path of statement execution in the majority of cases + terminates at _execute_context(). + + """ + if self._has_events or self.engine._has_events: + for fn in self.dispatch.before_cursor_execute: + statement, parameters = \ + fn(self, cursor, statement, parameters, + context, + False) + + if self._echo: + self.engine.logger.info(statement) + self.engine.logger.info("%r", parameters) + try: + for fn in () if not self.dialect._has_events \ + else self.dialect.dispatch.do_execute: + if fn(cursor, statement, parameters, context): + break + else: + self.dialect.do_execute( + cursor, + statement, + parameters, + context) + except BaseException as e: + self._handle_dbapi_exception( + e, + statement, + parameters, + cursor, + context) + + if self._has_events or self.engine._has_events: + self.dispatch.after_cursor_execute(self, cursor, + statement, + parameters, + context, + False) + + def _safe_close_cursor(self, cursor): + """Close the given cursor, catching exceptions + and turning into log warnings. + + """ + try: + cursor.close() + except Exception: + # log the error through the connection pool's logger. + self.engine.pool.logger.error( + "Error closing cursor", exc_info=True) + + _reentrant_error = False + _is_disconnect = False + + def _handle_dbapi_exception(self, + e, + statement, + parameters, + cursor, + context): + exc_info = sys.exc_info() + + if context and context.exception is None: + context.exception = e + + is_exit_exception = not isinstance(e, Exception) + + if not self._is_disconnect: + self._is_disconnect = ( + isinstance(e, self.dialect.dbapi.Error) and + not self.closed and + self.dialect.is_disconnect( + e, + self.__connection if not self.invalidated else None, + cursor) + ) or ( + is_exit_exception and not self.closed + ) + + if context: + context.is_disconnect = self._is_disconnect + + invalidate_pool_on_disconnect = not is_exit_exception + + if self._reentrant_error: + util.raise_from_cause( + exc.DBAPIError.instance(statement, + parameters, + e, + self.dialect.dbapi.Error, + dialect=self.dialect), + exc_info + ) + self._reentrant_error = True + try: + # non-DBAPI error - if we already got a context, + # or there's no string statement, don't wrap it + should_wrap = isinstance(e, self.dialect.dbapi.Error) or \ + (statement is not None + and context is None and not is_exit_exception) + + if should_wrap: + sqlalchemy_exception = exc.DBAPIError.instance( + statement, + parameters, + e, + self.dialect.dbapi.Error, + connection_invalidated=self._is_disconnect, + dialect=self.dialect) + else: + sqlalchemy_exception = None + + newraise = None + + if (self._has_events or self.engine._has_events) and \ + not self._execution_options.get( + 'skip_user_error_events', False): + # legacy dbapi_error event + if should_wrap and context: + self.dispatch.dbapi_error(self, + cursor, + statement, + parameters, + context, + e) + + # new handle_error event + ctx = ExceptionContextImpl( + e, sqlalchemy_exception, self.engine, + self, cursor, statement, + parameters, context, self._is_disconnect, + invalidate_pool_on_disconnect) + + for fn in self.dispatch.handle_error: + try: + # handler returns an exception; + # call next handler in a chain + per_fn = fn(ctx) + if per_fn is not None: + ctx.chained_exception = newraise = per_fn + except Exception as _raised: + # handler raises an exception - stop processing + newraise = _raised + break + + if self._is_disconnect != ctx.is_disconnect: + self._is_disconnect = ctx.is_disconnect + if sqlalchemy_exception: + sqlalchemy_exception.connection_invalidated = \ + ctx.is_disconnect + + # set up potentially user-defined value for + # invalidate pool. + invalidate_pool_on_disconnect = \ + ctx.invalidate_pool_on_disconnect + + if should_wrap and context: + context.handle_dbapi_exception(e) + + if not self._is_disconnect: + if cursor: + self._safe_close_cursor(cursor) + with util.safe_reraise(warn_only=True): + self._autorollback() + + if newraise: + util.raise_from_cause(newraise, exc_info) + elif should_wrap: + util.raise_from_cause( + sqlalchemy_exception, + exc_info + ) + else: + util.reraise(*exc_info) + + finally: + del self._reentrant_error + if self._is_disconnect: + del self._is_disconnect + if not self.invalidated: + dbapi_conn_wrapper = self.__connection + if invalidate_pool_on_disconnect: + self.engine.pool._invalidate(dbapi_conn_wrapper, e) + self.invalidate(e) + if self.should_close_with_result: + self.close() + + @classmethod + def _handle_dbapi_exception_noconnection(cls, e, dialect, engine): + exc_info = sys.exc_info() + + is_disconnect = dialect.is_disconnect(e, None, None) + + should_wrap = isinstance(e, dialect.dbapi.Error) + + if should_wrap: + sqlalchemy_exception = exc.DBAPIError.instance( + None, + None, + e, + dialect.dbapi.Error, + connection_invalidated=is_disconnect) + else: + sqlalchemy_exception = None + + newraise = None + + if engine._has_events: + ctx = ExceptionContextImpl( + e, sqlalchemy_exception, engine, None, None, None, + None, None, is_disconnect, True) + for fn in engine.dispatch.handle_error: + try: + # handler returns an exception; + # call next handler in a chain + per_fn = fn(ctx) + if per_fn is not None: + ctx.chained_exception = newraise = per_fn + except Exception as _raised: + # handler raises an exception - stop processing + newraise = _raised + break + + if sqlalchemy_exception and \ + is_disconnect != ctx.is_disconnect: + sqlalchemy_exception.connection_invalidated = \ + is_disconnect = ctx.is_disconnect + + if newraise: + util.raise_from_cause(newraise, exc_info) + elif should_wrap: + util.raise_from_cause( + sqlalchemy_exception, + exc_info + ) + else: + util.reraise(*exc_info) + + def transaction(self, callable_, *args, **kwargs): + r"""Execute the given function within a transaction boundary. + + The function is passed this :class:`.Connection` + as the first argument, followed by the given \*args and \**kwargs, + e.g.:: + + def do_something(conn, x, y): + conn.execute("some statement", {'x':x, 'y':y}) + + conn.transaction(do_something, 5, 10) + + The operations inside the function are all invoked within the + context of a single :class:`.Transaction`. + Upon success, the transaction is committed. If an + exception is raised, the transaction is rolled back + before propagating the exception. + + .. note:: + + The :meth:`.transaction` method is superseded by + the usage of the Python ``with:`` statement, which can + be used with :meth:`.Connection.begin`:: + + with conn.begin(): + conn.execute("some statement", {'x':5, 'y':10}) + + As well as with :meth:`.Engine.begin`:: + + with engine.begin() as conn: + conn.execute("some statement", {'x':5, 'y':10}) + + See also: + + :meth:`.Engine.begin` - engine-level transactional + context + + :meth:`.Engine.transaction` - engine-level version of + :meth:`.Connection.transaction` + + """ + + trans = self.begin() + try: + ret = self.run_callable(callable_, *args, **kwargs) + trans.commit() + return ret + except: + with util.safe_reraise(): + trans.rollback() + + def run_callable(self, callable_, *args, **kwargs): + r"""Given a callable object or function, execute it, passing + a :class:`.Connection` as the first argument. + + The given \*args and \**kwargs are passed subsequent + to the :class:`.Connection` argument. + + This function, along with :meth:`.Engine.run_callable`, + allows a function to be run with a :class:`.Connection` + or :class:`.Engine` object without the need to know + which one is being dealt with. + + """ + return callable_(self, *args, **kwargs) + + def _run_visitor(self, visitorcallable, element, **kwargs): + visitorcallable(self.dialect, self, + **kwargs).traverse_single(element) + + +class ExceptionContextImpl(ExceptionContext): + """Implement the :class:`.ExceptionContext` interface.""" + + def __init__(self, exception, sqlalchemy_exception, + engine, connection, cursor, statement, parameters, + context, is_disconnect, invalidate_pool_on_disconnect): + self.engine = engine + self.connection = connection + self.sqlalchemy_exception = sqlalchemy_exception + self.original_exception = exception + self.execution_context = context + self.statement = statement + self.parameters = parameters + self.is_disconnect = is_disconnect + self.invalidate_pool_on_disconnect = invalidate_pool_on_disconnect + + +class Transaction(object): + """Represent a database transaction in progress. + + The :class:`.Transaction` object is procured by + calling the :meth:`~.Connection.begin` method of + :class:`.Connection`:: + + from sqlalchemy import create_engine + engine = create_engine("postgresql://scott:tiger@localhost/test") + connection = engine.connect() + trans = connection.begin() + connection.execute("insert into x (a, b) values (1, 2)") + trans.commit() + + The object provides :meth:`.rollback` and :meth:`.commit` + methods in order to control transaction boundaries. It + also implements a context manager interface so that + the Python ``with`` statement can be used with the + :meth:`.Connection.begin` method:: + + with connection.begin(): + connection.execute("insert into x (a, b) values (1, 2)") + + The Transaction object is **not** threadsafe. + + See also: :meth:`.Connection.begin`, :meth:`.Connection.begin_twophase`, + :meth:`.Connection.begin_nested`. + + .. index:: + single: thread safety; Transaction + """ + + def __init__(self, connection, parent): + self.connection = connection + self._actual_parent = parent + self.is_active = True + + @property + def _parent(self): + return self._actual_parent or self + + def close(self): + """Close this :class:`.Transaction`. + + If this transaction is the base transaction in a begin/commit + nesting, the transaction will rollback(). Otherwise, the + method returns. + + This is used to cancel a Transaction without affecting the scope of + an enclosing transaction. + + """ + if not self._parent.is_active: + return + if self._parent is self: + self.rollback() + + def rollback(self): + """Roll back this :class:`.Transaction`. + + """ + if not self._parent.is_active: + return + self._do_rollback() + self.is_active = False + + def _do_rollback(self): + self._parent.rollback() + + def commit(self): + """Commit this :class:`.Transaction`.""" + + if not self._parent.is_active: + raise exc.InvalidRequestError("This transaction is inactive") + self._do_commit() + self.is_active = False + + def _do_commit(self): + pass + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + if type is None and self.is_active: + try: + self.commit() + except: + with util.safe_reraise(): + self.rollback() + else: + self.rollback() + + +class RootTransaction(Transaction): + def __init__(self, connection): + super(RootTransaction, self).__init__(connection, None) + self.connection._begin_impl(self) + + def _do_rollback(self): + if self.is_active: + self.connection._rollback_impl() + + def _do_commit(self): + if self.is_active: + self.connection._commit_impl() + + +class NestedTransaction(Transaction): + """Represent a 'nested', or SAVEPOINT transaction. + + A new :class:`.NestedTransaction` object may be procured + using the :meth:`.Connection.begin_nested` method. + + The interface is the same as that of :class:`.Transaction`. + + """ + + def __init__(self, connection, parent): + super(NestedTransaction, self).__init__(connection, parent) + self._savepoint = self.connection._savepoint_impl() + + def _do_rollback(self): + if self.is_active: + self.connection._rollback_to_savepoint_impl( + self._savepoint, self._parent) + + def _do_commit(self): + if self.is_active: + self.connection._release_savepoint_impl( + self._savepoint, self._parent) + + +class TwoPhaseTransaction(Transaction): + """Represent a two-phase transaction. + + A new :class:`.TwoPhaseTransaction` object may be procured + using the :meth:`.Connection.begin_twophase` method. + + The interface is the same as that of :class:`.Transaction` + with the addition of the :meth:`prepare` method. + + """ + + def __init__(self, connection, xid): + super(TwoPhaseTransaction, self).__init__(connection, None) + self._is_prepared = False + self.xid = xid + self.connection._begin_twophase_impl(self) + + def prepare(self): + """Prepare this :class:`.TwoPhaseTransaction`. + + After a PREPARE, the transaction can be committed. + + """ + if not self._parent.is_active: + raise exc.InvalidRequestError("This transaction is inactive") + self.connection._prepare_twophase_impl(self.xid) + self._is_prepared = True + + def _do_rollback(self): + self.connection._rollback_twophase_impl(self.xid, self._is_prepared) + + def _do_commit(self): + self.connection._commit_twophase_impl(self.xid, self._is_prepared) + + +class Engine(Connectable, log.Identified): + """ + Connects a :class:`~sqlalchemy.pool.Pool` and + :class:`~sqlalchemy.engine.interfaces.Dialect` together to provide a + source of database connectivity and behavior. + + An :class:`.Engine` object is instantiated publicly using the + :func:`~sqlalchemy.create_engine` function. + + See also: + + :doc:`/core/engines` + + :ref:`connections_toplevel` + + """ + + _execution_options = util.immutabledict() + _has_events = False + _connection_cls = Connection + + schema_for_object = schema._schema_getter(None) + """Return the ".schema" attribute for an object. + + Used for :class:`.Table`, :class:`.Sequence` and similar objects, + and takes into account + the :paramref:`.Connection.execution_options.schema_translate_map` + parameter. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`schema_translating` + + """ + + def __init__(self, pool, dialect, url, + logging_name=None, echo=None, proxy=None, + execution_options=None + ): + self.pool = pool + self.url = url + self.dialect = dialect + if logging_name: + self.logging_name = logging_name + self.echo = echo + self.engine = self + log.instance_logger(self, echoflag=echo) + if proxy: + interfaces.ConnectionProxy._adapt_listener(self, proxy) + if execution_options: + self.update_execution_options(**execution_options) + + def update_execution_options(self, **opt): + r"""Update the default execution_options dictionary + of this :class:`.Engine`. + + The given keys/values in \**opt are added to the + default execution options that will be used for + all connections. The initial contents of this dictionary + can be sent via the ``execution_options`` parameter + to :func:`.create_engine`. + + .. seealso:: + + :meth:`.Connection.execution_options` + + :meth:`.Engine.execution_options` + + """ + self._execution_options = \ + self._execution_options.union(opt) + self.dispatch.set_engine_execution_options(self, opt) + self.dialect.set_engine_execution_options(self, opt) + + def execution_options(self, **opt): + """Return a new :class:`.Engine` that will provide + :class:`.Connection` objects with the given execution options. + + The returned :class:`.Engine` remains related to the original + :class:`.Engine` in that it shares the same connection pool and + other state: + + * The :class:`.Pool` used by the new :class:`.Engine` is the + same instance. The :meth:`.Engine.dispose` method will replace + the connection pool instance for the parent engine as well + as this one. + * Event listeners are "cascaded" - meaning, the new :class:`.Engine` + inherits the events of the parent, and new events can be associated + with the new :class:`.Engine` individually. + * The logging configuration and logging_name is copied from the parent + :class:`.Engine`. + + The intent of the :meth:`.Engine.execution_options` method is + to implement "sharding" schemes where multiple :class:`.Engine` + objects refer to the same connection pool, but are differentiated + by options that would be consumed by a custom event:: + + primary_engine = create_engine("mysql://") + shard1 = primary_engine.execution_options(shard_id="shard1") + shard2 = primary_engine.execution_options(shard_id="shard2") + + Above, the ``shard1`` engine serves as a factory for + :class:`.Connection` objects that will contain the execution option + ``shard_id=shard1``, and ``shard2`` will produce :class:`.Connection` + objects that contain the execution option ``shard_id=shard2``. + + An event handler can consume the above execution option to perform + a schema switch or other operation, given a connection. Below + we emit a MySQL ``use`` statement to switch databases, at the same + time keeping track of which database we've established using the + :attr:`.Connection.info` dictionary, which gives us a persistent + storage space that follows the DBAPI connection:: + + from sqlalchemy import event + from sqlalchemy.engine import Engine + + shards = {"default": "base", shard_1: "db1", "shard_2": "db2"} + + @event.listens_for(Engine, "before_cursor_execute") + def _switch_shard(conn, cursor, stmt, + params, context, executemany): + shard_id = conn._execution_options.get('shard_id', "default") + current_shard = conn.info.get("current_shard", None) + + if current_shard != shard_id: + cursor.execute("use %s" % shards[shard_id]) + conn.info["current_shard"] = shard_id + + .. versionadded:: 0.8 + + .. seealso:: + + :meth:`.Connection.execution_options` - update execution options + on a :class:`.Connection` object. + + :meth:`.Engine.update_execution_options` - update the execution + options for a given :class:`.Engine` in place. + + """ + return OptionEngine(self, opt) + + @property + def name(self): + """String name of the :class:`~sqlalchemy.engine.interfaces.Dialect` + in use by this :class:`Engine`.""" + + return self.dialect.name + + @property + def driver(self): + """Driver name of the :class:`~sqlalchemy.engine.interfaces.Dialect` + in use by this :class:`Engine`.""" + + return self.dialect.driver + + echo = log.echo_property() + + def __repr__(self): + return 'Engine(%r)' % self.url + + def dispose(self): + """Dispose of the connection pool used by this :class:`.Engine`. + + This has the effect of fully closing all **currently checked in** + database connections. Connections that are still checked out + will **not** be closed, however they will no longer be associated + with this :class:`.Engine`, so when they are closed individually, + eventually the :class:`.Pool` which they are associated with will + be garbage collected and they will be closed out fully, if + not already closed on checkin. + + A new connection pool is created immediately after the old one has + been disposed. This new pool, like all SQLAlchemy connection pools, + does not make any actual connections to the database until one is + first requested, so as long as the :class:`.Engine` isn't used again, + no new connections will be made. + + .. seealso:: + + :ref:`engine_disposal` + + """ + self.pool.dispose() + self.pool = self.pool.recreate() + self.dispatch.engine_disposed(self) + + def _execute_default(self, default): + with self.contextual_connect() as conn: + return conn._execute_default(default, (), {}) + + @contextlib.contextmanager + def _optional_conn_ctx_manager(self, connection=None): + if connection is None: + with self.contextual_connect() as conn: + yield conn + else: + yield connection + + def _run_visitor(self, visitorcallable, element, + connection=None, **kwargs): + with self._optional_conn_ctx_manager(connection) as conn: + conn._run_visitor(visitorcallable, element, **kwargs) + + class _trans_ctx(object): + def __init__(self, conn, transaction, close_with_result): + self.conn = conn + self.transaction = transaction + self.close_with_result = close_with_result + + def __enter__(self): + return self.conn + + def __exit__(self, type, value, traceback): + if type is not None: + self.transaction.rollback() + else: + self.transaction.commit() + if not self.close_with_result: + self.conn.close() + + def begin(self, close_with_result=False): + """Return a context manager delivering a :class:`.Connection` + with a :class:`.Transaction` established. + + E.g.:: + + with engine.begin() as conn: + conn.execute("insert into table (x, y, z) values (1, 2, 3)") + conn.execute("my_special_procedure(5)") + + Upon successful operation, the :class:`.Transaction` + is committed. If an error is raised, the :class:`.Transaction` + is rolled back. + + The ``close_with_result`` flag is normally ``False``, and indicates + that the :class:`.Connection` will be closed when the operation + is complete. When set to ``True``, it indicates the + :class:`.Connection` is in "single use" mode, where the + :class:`.ResultProxy` returned by the first call to + :meth:`.Connection.execute` will close the :class:`.Connection` when + that :class:`.ResultProxy` has exhausted all result rows. + + .. versionadded:: 0.7.6 + + See also: + + :meth:`.Engine.connect` - procure a :class:`.Connection` from + an :class:`.Engine`. + + :meth:`.Connection.begin` - start a :class:`.Transaction` + for a particular :class:`.Connection`. + + """ + conn = self.contextual_connect(close_with_result=close_with_result) + try: + trans = conn.begin() + except: + with util.safe_reraise(): + conn.close() + return Engine._trans_ctx(conn, trans, close_with_result) + + def transaction(self, callable_, *args, **kwargs): + r"""Execute the given function within a transaction boundary. + + The function is passed a :class:`.Connection` newly procured + from :meth:`.Engine.contextual_connect` as the first argument, + followed by the given \*args and \**kwargs. + + e.g.:: + + def do_something(conn, x, y): + conn.execute("some statement", {'x':x, 'y':y}) + + engine.transaction(do_something, 5, 10) + + The operations inside the function are all invoked within the + context of a single :class:`.Transaction`. + Upon success, the transaction is committed. If an + exception is raised, the transaction is rolled back + before propagating the exception. + + .. note:: + + The :meth:`.transaction` method is superseded by + the usage of the Python ``with:`` statement, which can + be used with :meth:`.Engine.begin`:: + + with engine.begin() as conn: + conn.execute("some statement", {'x':5, 'y':10}) + + See also: + + :meth:`.Engine.begin` - engine-level transactional + context + + :meth:`.Connection.transaction` - connection-level version of + :meth:`.Engine.transaction` + + """ + + with self.contextual_connect() as conn: + return conn.transaction(callable_, *args, **kwargs) + + def run_callable(self, callable_, *args, **kwargs): + r"""Given a callable object or function, execute it, passing + a :class:`.Connection` as the first argument. + + The given \*args and \**kwargs are passed subsequent + to the :class:`.Connection` argument. + + This function, along with :meth:`.Connection.run_callable`, + allows a function to be run with a :class:`.Connection` + or :class:`.Engine` object without the need to know + which one is being dealt with. + + """ + with self.contextual_connect() as conn: + return conn.run_callable(callable_, *args, **kwargs) + + def execute(self, statement, *multiparams, **params): + """Executes the given construct and returns a :class:`.ResultProxy`. + + The arguments are the same as those used by + :meth:`.Connection.execute`. + + Here, a :class:`.Connection` is acquired using the + :meth:`~.Engine.contextual_connect` method, and the statement executed + with that connection. The returned :class:`.ResultProxy` is flagged + such that when the :class:`.ResultProxy` is exhausted and its + underlying cursor is closed, the :class:`.Connection` created here + will also be closed, which allows its associated DBAPI connection + resource to be returned to the connection pool. + + """ + + connection = self.contextual_connect(close_with_result=True) + return connection.execute(statement, *multiparams, **params) + + def scalar(self, statement, *multiparams, **params): + return self.execute(statement, *multiparams, **params).scalar() + + def _execute_clauseelement(self, elem, multiparams=None, params=None): + connection = self.contextual_connect(close_with_result=True) + return connection._execute_clauseelement(elem, multiparams, params) + + def _execute_compiled(self, compiled, multiparams, params): + connection = self.contextual_connect(close_with_result=True) + return connection._execute_compiled(compiled, multiparams, params) + + def connect(self, **kwargs): + """Return a new :class:`.Connection` object. + + The :class:`.Connection` object is a facade that uses a DBAPI + connection internally in order to communicate with the database. This + connection is procured from the connection-holding :class:`.Pool` + referenced by this :class:`.Engine`. When the + :meth:`~.Connection.close` method of the :class:`.Connection` object + is called, the underlying DBAPI connection is then returned to the + connection pool, where it may be used again in a subsequent call to + :meth:`~.Engine.connect`. + + """ + + return self._connection_cls(self, **kwargs) + + def contextual_connect(self, close_with_result=False, **kwargs): + """Return a :class:`.Connection` object which may be part of some + ongoing context. + + By default, this method does the same thing as :meth:`.Engine.connect`. + Subclasses of :class:`.Engine` may override this method + to provide contextual behavior. + + :param close_with_result: When True, the first :class:`.ResultProxy` + created by the :class:`.Connection` will call the + :meth:`.Connection.close` method of that connection as soon as any + pending result rows are exhausted. This is used to supply the + "connectionless execution" behavior provided by the + :meth:`.Engine.execute` method. + + """ + + return self._connection_cls( + self, + self._wrap_pool_connect(self.pool.connect, None), + close_with_result=close_with_result, + **kwargs) + + def table_names(self, schema=None, connection=None): + """Return a list of all table names available in the database. + + :param schema: Optional, retrieve names from a non-default schema. + + :param connection: Optional, use a specified connection. Default is + the ``contextual_connect`` for this ``Engine``. + """ + + with self._optional_conn_ctx_manager(connection) as conn: + if not schema: + schema = self.dialect.default_schema_name + return self.dialect.get_table_names(conn, schema) + + def has_table(self, table_name, schema=None): + """Return True if the given backend has a table of the given name. + + .. seealso:: + + :ref:`metadata_reflection_inspector` - detailed schema inspection + using the :class:`.Inspector` interface. + + :class:`.quoted_name` - used to pass quoting information along + with a schema identifier. + + """ + return self.run_callable(self.dialect.has_table, table_name, schema) + + def _wrap_pool_connect(self, fn, connection): + dialect = self.dialect + try: + return fn() + except dialect.dbapi.Error as e: + if connection is None: + Connection._handle_dbapi_exception_noconnection( + e, dialect, self) + else: + util.reraise(*sys.exc_info()) + + def raw_connection(self, _connection=None): + """Return a "raw" DBAPI connection from the connection pool. + + The returned object is a proxied version of the DBAPI + connection object used by the underlying driver in use. + The object will have all the same behavior as the real DBAPI + connection, except that its ``close()`` method will result in the + connection being returned to the pool, rather than being closed + for real. + + This method provides direct DBAPI connection access for + special situations when the API provided by :class:`.Connection` + is not needed. When a :class:`.Connection` object is already + present, the DBAPI connection is available using + the :attr:`.Connection.connection` accessor. + + .. seealso:: + + :ref:`dbapi_connections` + + """ + return self._wrap_pool_connect( + self.pool.unique_connection, _connection) + + +class OptionEngine(Engine): + _sa_propagate_class_events = False + + def __init__(self, proxied, execution_options): + self._proxied = proxied + self.url = proxied.url + self.dialect = proxied.dialect + self.logging_name = proxied.logging_name + self.echo = proxied.echo + log.instance_logger(self, echoflag=self.echo) + + # note: this will propagate events that are assigned to the parent + # engine after this OptionEngine is created. Since we share + # the events of the parent we also disallow class-level events + # to apply to the OptionEngine class directly. + # + # the other way this can work would be to transfer existing + # events only, using: + # self.dispatch._update(proxied.dispatch) + # + # that might be more appropriate however it would be a behavioral + # change for logic that assigns events to the parent engine and + # would like it to take effect for the already-created sub-engine. + self.dispatch = self.dispatch._join(proxied.dispatch) + + self._execution_options = proxied._execution_options + self.update_execution_options(**execution_options) + + def _get_pool(self): + return self._proxied.pool + + def _set_pool(self, pool): + self._proxied.pool = pool + + pool = property(_get_pool, _set_pool) + + def _get_has_events(self): + return self._proxied._has_events or \ + self.__dict__.get('_has_events', False) + + def _set_has_events(self, value): + self.__dict__['_has_events'] = value + + _has_events = property(_get_has_events, _set_has_events) diff --git a/venv/Lib/site-packages/sqlalchemy/engine/default.py b/venv/Lib/site-packages/sqlalchemy/engine/default.py new file mode 100644 index 0000000..915812a --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/engine/default.py @@ -0,0 +1,1364 @@ +# engine/default.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Default implementations of per-dialect sqlalchemy.engine classes. + +These are semi-private implementation classes which are only of importance +to database dialect authors; dialects will usually use the classes here +as the base class for their own corresponding classes. + +""" + +import re +import random +from . import reflection, interfaces, result +from ..sql import compiler, expression, schema +from .. import types as sqltypes +from .. import exc, util, pool, processors +import codecs +import weakref +from .. import event + +AUTOCOMMIT_REGEXP = re.compile( + r'\s*(?:UPDATE|INSERT|CREATE|DELETE|DROP|ALTER)', + re.I | re.UNICODE) + +# When we're handed literal SQL, ensure it's a SELECT query +SERVER_SIDE_CURSOR_RE = re.compile( + r'\s*SELECT', + re.I | re.UNICODE) + + +class DefaultDialect(interfaces.Dialect): + """Default implementation of Dialect""" + + statement_compiler = compiler.SQLCompiler + ddl_compiler = compiler.DDLCompiler + type_compiler = compiler.GenericTypeCompiler + preparer = compiler.IdentifierPreparer + supports_alter = True + supports_comments = False + inline_comments = False + + # the first value we'd get for an autoincrement + # column. + default_sequence_base = 1 + + # most DBAPIs happy with this for execute(). + # not cx_oracle. + execute_sequence_format = tuple + + supports_views = True + supports_sequences = False + sequences_optional = False + preexecute_autoincrement_sequences = False + postfetch_lastrowid = True + implicit_returning = False + + supports_right_nested_joins = True + cte_follows_insert = False + + supports_native_enum = False + supports_native_boolean = False + non_native_boolean_check_constraint = True + + supports_simple_order_by_label = True + + engine_config_types = util.immutabledict([ + ('convert_unicode', util.bool_or_str('force')), + ('pool_timeout', util.asint), + ('echo', util.bool_or_str('debug')), + ('echo_pool', util.bool_or_str('debug')), + ('pool_recycle', util.asint), + ('pool_size', util.asint), + ('max_overflow', util.asint), + ('pool_threadlocal', util.asbool), + ]) + + # if the NUMERIC type + # returns decimal.Decimal. + # *not* the FLOAT type however. + supports_native_decimal = False + + if util.py3k: + supports_unicode_statements = True + supports_unicode_binds = True + returns_unicode_strings = True + description_encoding = None + else: + supports_unicode_statements = False + supports_unicode_binds = False + returns_unicode_strings = False + description_encoding = 'use_encoding' + + name = 'default' + + # length at which to truncate + # any identifier. + max_identifier_length = 9999 + + # length at which to truncate + # the name of an index. + # Usually None to indicate + # 'use max_identifier_length'. + # thanks to MySQL, sigh + max_index_name_length = None + + supports_sane_rowcount = True + supports_sane_multi_rowcount = True + colspecs = {} + default_paramstyle = 'named' + supports_default_values = False + supports_empty_insert = True + supports_multivalues_insert = False + + supports_server_side_cursors = False + + server_version_info = None + + construct_arguments = None + """Optional set of argument specifiers for various SQLAlchemy + constructs, typically schema items. + + To implement, establish as a series of tuples, as in:: + + construct_arguments = [ + (schema.Index, { + "using": False, + "where": None, + "ops": None + }) + ] + + If the above construct is established on the PostgreSQL dialect, + the :class:`.Index` construct will now accept the keyword arguments + ``postgresql_using``, ``postgresql_where``, nad ``postgresql_ops``. + Any other argument specified to the constructor of :class:`.Index` + which is prefixed with ``postgresql_`` will raise :class:`.ArgumentError`. + + A dialect which does not include a ``construct_arguments`` member will + not participate in the argument validation system. For such a dialect, + any argument name is accepted by all participating constructs, within + the namespace of arguments prefixed with that dialect name. The rationale + here is so that third-party dialects that haven't yet implemented this + feature continue to function in the old way. + + .. versionadded:: 0.9.2 + + .. seealso:: + + :class:`.DialectKWArgs` - implementing base class which consumes + :attr:`.DefaultDialect.construct_arguments` + + + """ + + # indicates symbol names are + # UPPERCASEd if they are case insensitive + # within the database. + # if this is True, the methods normalize_name() + # and denormalize_name() must be provided. + requires_name_normalize = False + + reflection_options = () + + dbapi_exception_translation_map = util.immutabledict() + """mapping used in the extremely unusual case that a DBAPI's + published exceptions don't actually have the __name__ that they + are linked towards. + + .. versionadded:: 1.0.5 + + """ + + def __init__(self, convert_unicode=False, + encoding='utf-8', paramstyle=None, dbapi=None, + implicit_returning=None, + supports_right_nested_joins=None, + case_sensitive=True, + supports_native_boolean=None, + empty_in_strategy='static', + label_length=None, **kwargs): + + if not getattr(self, 'ported_sqla_06', True): + util.warn( + "The %s dialect is not yet ported to the 0.6 format" % + self.name) + + self.convert_unicode = convert_unicode + self.encoding = encoding + self.positional = False + self._ischema = None + self.dbapi = dbapi + if paramstyle is not None: + self.paramstyle = paramstyle + elif self.dbapi is not None: + self.paramstyle = self.dbapi.paramstyle + else: + self.paramstyle = self.default_paramstyle + if implicit_returning is not None: + self.implicit_returning = implicit_returning + self.positional = self.paramstyle in ('qmark', 'format', 'numeric') + self.identifier_preparer = self.preparer(self) + self.type_compiler = self.type_compiler(self) + if supports_right_nested_joins is not None: + self.supports_right_nested_joins = supports_right_nested_joins + if supports_native_boolean is not None: + self.supports_native_boolean = supports_native_boolean + self.case_sensitive = case_sensitive + + self.empty_in_strategy = empty_in_strategy + if empty_in_strategy == 'static': + self._use_static_in = True + elif empty_in_strategy in ('dynamic', 'dynamic_warn'): + self._use_static_in = False + self._warn_on_empty_in = empty_in_strategy == 'dynamic_warn' + else: + raise exc.ArgumentError( + "empty_in_strategy may be 'static', " + "'dynamic', or 'dynamic_warn'") + + if label_length and label_length > self.max_identifier_length: + raise exc.ArgumentError( + "Label length of %d is greater than this dialect's" + " maximum identifier length of %d" % + (label_length, self.max_identifier_length)) + self.label_length = label_length + + if self.description_encoding == 'use_encoding': + self._description_decoder = \ + processors.to_unicode_processor_factory( + encoding + ) + elif self.description_encoding is not None: + self._description_decoder = \ + processors.to_unicode_processor_factory( + self.description_encoding + ) + self._encoder = codecs.getencoder(self.encoding) + self._decoder = processors.to_unicode_processor_factory(self.encoding) + + @util.memoized_property + def _type_memos(self): + return weakref.WeakKeyDictionary() + + @property + def dialect_description(self): + return self.name + "+" + self.driver + + @property + def supports_sane_rowcount_returning(self): + return self.supports_sane_rowcount + + @classmethod + def get_pool_class(cls, url): + return getattr(cls, 'poolclass', pool.QueuePool) + + def initialize(self, connection): + try: + self.server_version_info = \ + self._get_server_version_info(connection) + except NotImplementedError: + self.server_version_info = None + try: + self.default_schema_name = \ + self._get_default_schema_name(connection) + except NotImplementedError: + self.default_schema_name = None + + try: + self.default_isolation_level = \ + self.get_isolation_level(connection.connection) + except NotImplementedError: + self.default_isolation_level = None + + self.returns_unicode_strings = self._check_unicode_returns(connection) + + if self.description_encoding is not None and \ + self._check_unicode_description(connection): + self._description_decoder = self.description_encoding = None + + self.do_rollback(connection.connection) + + def on_connect(self): + """return a callable which sets up a newly created DBAPI connection. + + This is used to set dialect-wide per-connection options such as + isolation modes, unicode modes, etc. + + If a callable is returned, it will be assembled into a pool listener + that receives the direct DBAPI connection, with all wrappers removed. + + If None is returned, no listener will be generated. + + """ + return None + + def _check_unicode_returns(self, connection, additional_tests=None): + if util.py2k and not self.supports_unicode_statements: + cast_to = util.binary_type + else: + cast_to = util.text_type + + if self.positional: + parameters = self.execute_sequence_format() + else: + parameters = {} + + def check_unicode(test): + statement = cast_to( + expression.select([test]).compile(dialect=self)) + try: + cursor = connection.connection.cursor() + connection._cursor_execute(cursor, statement, parameters) + row = cursor.fetchone() + cursor.close() + except exc.DBAPIError as de: + # note that _cursor_execute() will have closed the cursor + # if an exception is thrown. + util.warn("Exception attempting to " + "detect unicode returns: %r" % de) + return False + else: + return isinstance(row[0], util.text_type) + + tests = [ + # detect plain VARCHAR + expression.cast( + expression.literal_column("'test plain returns'"), + sqltypes.VARCHAR(60) + ), + # detect if there's an NVARCHAR type with different behavior + # available + expression.cast( + expression.literal_column("'test unicode returns'"), + sqltypes.Unicode(60) + ), + ] + + if additional_tests: + tests += additional_tests + + results = {check_unicode(test) for test in tests} + + if results.issuperset([True, False]): + return "conditional" + else: + return results == {True} + + def _check_unicode_description(self, connection): + # all DBAPIs on Py2K return cursor.description as encoded, + # until pypy2.1beta2 with sqlite, so let's just check it - + # it's likely others will start doing this too in Py2k. + + if util.py2k and not self.supports_unicode_statements: + cast_to = util.binary_type + else: + cast_to = util.text_type + + cursor = connection.connection.cursor() + try: + cursor.execute( + cast_to( + expression.select([ + expression.literal_column("'x'").label("some_label") + ]).compile(dialect=self) + ) + ) + return isinstance(cursor.description[0][0], util.text_type) + finally: + cursor.close() + + def type_descriptor(self, typeobj): + """Provide a database-specific :class:`.TypeEngine` object, given + the generic object which comes from the types module. + + This method looks for a dictionary called + ``colspecs`` as a class or instance-level variable, + and passes on to :func:`.types.adapt_type`. + + """ + return sqltypes.adapt_type(typeobj, self.colspecs) + + def reflecttable( + self, connection, table, include_columns, exclude_columns, **opts): + insp = reflection.Inspector.from_engine(connection) + return insp.reflecttable( + table, include_columns, exclude_columns, **opts) + + def get_pk_constraint(self, conn, table_name, schema=None, **kw): + """Compatibility method, adapts the result of get_primary_keys() + for those dialects which don't implement get_pk_constraint(). + + """ + return { + 'constrained_columns': + self.get_primary_keys(conn, table_name, + schema=schema, **kw) + } + + def validate_identifier(self, ident): + if len(ident) > self.max_identifier_length: + raise exc.IdentifierError( + "Identifier '%s' exceeds maximum length of %d characters" % + (ident, self.max_identifier_length) + ) + + def connect(self, *cargs, **cparams): + return self.dbapi.connect(*cargs, **cparams) + + def create_connect_args(self, url): + opts = url.translate_connect_args() + opts.update(url.query) + return [[], opts] + + def set_engine_execution_options(self, engine, opts): + if 'isolation_level' in opts: + isolation_level = opts['isolation_level'] + + @event.listens_for(engine, "engine_connect") + def set_isolation(connection, branch): + if not branch: + self._set_connection_isolation(connection, isolation_level) + + if 'schema_translate_map' in opts: + getter = schema._schema_getter(opts['schema_translate_map']) + engine.schema_for_object = getter + + @event.listens_for(engine, "engine_connect") + def set_schema_translate_map(connection, branch): + connection.schema_for_object = getter + + def set_connection_execution_options(self, connection, opts): + if 'isolation_level' in opts: + self._set_connection_isolation(connection, opts['isolation_level']) + + if 'schema_translate_map' in opts: + getter = schema._schema_getter(opts['schema_translate_map']) + connection.schema_for_object = getter + + def _set_connection_isolation(self, connection, level): + if connection.in_transaction(): + util.warn( + "Connection is already established with a Transaction; " + "setting isolation_level may implicitly rollback or commit " + "the existing transaction, or have no effect until " + "next transaction") + self.set_isolation_level(connection.connection, level) + connection.connection._connection_record.\ + finalize_callback.append(self.reset_isolation_level) + + def do_begin(self, dbapi_connection): + pass + + def do_rollback(self, dbapi_connection): + dbapi_connection.rollback() + + def do_commit(self, dbapi_connection): + dbapi_connection.commit() + + def do_close(self, dbapi_connection): + dbapi_connection.close() + + @util.memoized_property + def _dialect_specific_select_one(self): + return str(expression.select([1]).compile(dialect=self)) + + def do_ping(self, dbapi_connection): + cursor = None + try: + cursor = dbapi_connection.cursor() + try: + cursor.execute(self._dialect_specific_select_one) + finally: + cursor.close() + except self.dbapi.Error as err: + if self.is_disconnect(err, dbapi_connection, cursor): + return False + else: + raise + else: + return True + + def create_xid(self): + """Create a random two-phase transaction ID. + + This id will be passed to do_begin_twophase(), do_rollback_twophase(), + do_commit_twophase(). Its format is unspecified. + """ + + return "_sa_%032x" % random.randint(0, 2 ** 128) + + def do_savepoint(self, connection, name): + connection.execute(expression.SavepointClause(name)) + + def do_rollback_to_savepoint(self, connection, name): + connection.execute(expression.RollbackToSavepointClause(name)) + + def do_release_savepoint(self, connection, name): + connection.execute(expression.ReleaseSavepointClause(name)) + + def do_executemany(self, cursor, statement, parameters, context=None): + cursor.executemany(statement, parameters) + + def do_execute(self, cursor, statement, parameters, context=None): + cursor.execute(statement, parameters) + + def do_execute_no_params(self, cursor, statement, context=None): + cursor.execute(statement) + + def is_disconnect(self, e, connection, cursor): + return False + + def reset_isolation_level(self, dbapi_conn): + # default_isolation_level is read from the first connection + # after the initial set of 'isolation_level', if any, so is + # the configured default of this dialect. + self.set_isolation_level(dbapi_conn, self.default_isolation_level) + + +class StrCompileDialect(DefaultDialect): + + statement_compiler = compiler.StrSQLCompiler + ddl_compiler = compiler.DDLCompiler + type_compiler = compiler.StrSQLTypeCompiler + preparer = compiler.IdentifierPreparer + + supports_sequences = True + sequences_optional = True + preexecute_autoincrement_sequences = False + implicit_returning = False + + supports_native_boolean = True + + supports_simple_order_by_label = True + + +class DefaultExecutionContext(interfaces.ExecutionContext): + isinsert = False + isupdate = False + isdelete = False + is_crud = False + is_text = False + isddl = False + executemany = False + compiled = None + statement = None + result_column_struct = None + returned_defaults = None + _is_implicit_returning = False + _is_explicit_returning = False + + # a hook for SQLite's translation of + # result column names + _translate_colname = None + + _expanded_parameters = util.immutabledict() + + @classmethod + def _init_ddl(cls, dialect, connection, dbapi_connection, compiled_ddl): + """Initialize execution context for a DDLElement construct.""" + + self = cls.__new__(cls) + self.root_connection = connection + self._dbapi_connection = dbapi_connection + self.dialect = connection.dialect + + self.compiled = compiled = compiled_ddl + self.isddl = True + + self.execution_options = compiled.execution_options + if connection._execution_options: + self.execution_options = dict(self.execution_options) + self.execution_options.update(connection._execution_options) + + if not dialect.supports_unicode_statements: + self.unicode_statement = util.text_type(compiled) + self.statement = dialect._encoder(self.unicode_statement)[0] + else: + self.statement = self.unicode_statement = util.text_type(compiled) + + self.cursor = self.create_cursor() + self.compiled_parameters = [] + + if dialect.positional: + self.parameters = [dialect.execute_sequence_format()] + else: + self.parameters = [{}] + + return self + + @classmethod + def _init_compiled(cls, dialect, connection, dbapi_connection, + compiled, parameters): + """Initialize execution context for a Compiled construct.""" + + self = cls.__new__(cls) + self.root_connection = connection + self._dbapi_connection = dbapi_connection + self.dialect = connection.dialect + + self.compiled = compiled + + # this should be caught in the engine before + # we get here + assert compiled.can_execute + + self.execution_options = compiled.execution_options.union( + connection._execution_options) + + self.result_column_struct = ( + compiled._result_columns, compiled._ordered_columns, + compiled._textual_ordered_columns) + + self.unicode_statement = util.text_type(compiled) + if not dialect.supports_unicode_statements: + self.statement = self.unicode_statement.encode( + self.dialect.encoding) + else: + self.statement = self.unicode_statement + + self.isinsert = compiled.isinsert + self.isupdate = compiled.isupdate + self.isdelete = compiled.isdelete + self.is_text = compiled.isplaintext + + if not parameters: + self.compiled_parameters = [compiled.construct_params()] + else: + self.compiled_parameters = \ + [compiled.construct_params(m, _group_number=grp) for + grp, m in enumerate(parameters)] + + self.executemany = len(parameters) > 1 + + self.cursor = self.create_cursor() + + if self.isinsert or self.isupdate or self.isdelete: + self.is_crud = True + self._is_explicit_returning = bool(compiled.statement._returning) + self._is_implicit_returning = bool( + compiled.returning and not compiled.statement._returning) + + if self.compiled.insert_prefetch or self.compiled.update_prefetch: + if self.executemany: + self._process_executemany_defaults() + else: + self._process_executesingle_defaults() + + processors = compiled._bind_processors + + if compiled.contains_expanding_parameters: + positiontup = self._expand_in_parameters(compiled, processors) + elif compiled.positional: + positiontup = self.compiled.positiontup + + # Convert the dictionary of bind parameter values + # into a dict or list to be sent to the DBAPI's + # execute() or executemany() method. + parameters = [] + if compiled.positional: + for compiled_params in self.compiled_parameters: + param = [] + for key in positiontup: + if key in processors: + param.append(processors[key](compiled_params[key])) + else: + param.append(compiled_params[key]) + parameters.append(dialect.execute_sequence_format(param)) + else: + encode = not dialect.supports_unicode_statements + for compiled_params in self.compiled_parameters: + + if encode: + param = dict( + ( + dialect._encoder(key)[0], + processors[key](compiled_params[key]) + if key in processors + else compiled_params[key] + ) + for key in compiled_params + ) + else: + param = dict( + ( + key, + processors[key](compiled_params[key]) + if key in processors + else compiled_params[key] + ) + for key in compiled_params + ) + + parameters.append(param) + + self.parameters = dialect.execute_sequence_format(parameters) + + return self + + def _expand_in_parameters(self, compiled, processors): + """handle special 'expanding' parameters, IN tuples that are rendered + on a per-parameter basis for an otherwise fixed SQL statement string. + + """ + if self.executemany: + raise exc.InvalidRequestError( + "'expanding' parameters can't be used with " + "executemany()") + + if self.compiled.positional and self.compiled._numeric_binds: + # I'm not familiar with any DBAPI that uses 'numeric' + raise NotImplementedError( + "'expanding' bind parameters not supported with " + "'numeric' paramstyle at this time.") + + self._expanded_parameters = {} + + compiled_params = self.compiled_parameters[0] + if compiled.positional: + positiontup = [] + else: + positiontup = None + + replacement_expressions = {} + for name in ( + self.compiled.positiontup if compiled.positional + else self.compiled.binds + ): + parameter = self.compiled.binds[name] + if parameter.expanding: + values = compiled_params.pop(name) + if not values: + raise exc.InvalidRequestError( + "'expanding' parameters can't be used with an " + "empty list" + ) + elif isinstance(values[0], (tuple, list)): + to_update = [ + ("%s_%s_%s" % (name, i, j), value) + for i, tuple_element in enumerate(values, 1) + for j, value in enumerate(tuple_element, 1) + ] + replacement_expressions[name] = ", ".join( + "(%s)" % ", ".join( + self.compiled.bindtemplate % { + "name": + to_update[i * len(tuple_element) + j][0] + } + for j, value in enumerate(tuple_element) + ) + for i, tuple_element in enumerate(values) + + ) + else: + to_update = [ + ("%s_%s" % (name, i), value) + for i, value in enumerate(values, 1) + ] + replacement_expressions[name] = ", ".join( + self.compiled.bindtemplate % { + "name": key} + for key, value in to_update + ) + compiled_params.update(to_update) + processors.update( + (key, processors[name]) + for key, value in to_update if name in processors + ) + if compiled.positional: + positiontup.extend(name for name, value in to_update) + self._expanded_parameters[name] = [ + expand_key for expand_key, value in to_update] + elif compiled.positional: + positiontup.append(name) + + def process_expanding(m): + return replacement_expressions.pop(m.group(1)) + + self.statement = re.sub( + r"\[EXPANDING_(\S+)\]", + process_expanding, + self.statement + ) + return positiontup + + @classmethod + def _init_statement(cls, dialect, connection, dbapi_connection, + statement, parameters): + """Initialize execution context for a string SQL statement.""" + + self = cls.__new__(cls) + self.root_connection = connection + self._dbapi_connection = dbapi_connection + self.dialect = connection.dialect + self.is_text = True + + # plain text statement + self.execution_options = connection._execution_options + + if not parameters: + if self.dialect.positional: + self.parameters = [dialect.execute_sequence_format()] + else: + self.parameters = [{}] + elif isinstance(parameters[0], dialect.execute_sequence_format): + self.parameters = parameters + elif isinstance(parameters[0], dict): + if dialect.supports_unicode_statements: + self.parameters = parameters + else: + self.parameters = [ + {dialect._encoder(k)[0]: d[k] for k in d} + for d in parameters + ] or [{}] + else: + self.parameters = [dialect.execute_sequence_format(p) + for p in parameters] + + self.executemany = len(parameters) > 1 + + if not dialect.supports_unicode_statements and \ + isinstance(statement, util.text_type): + self.unicode_statement = statement + self.statement = dialect._encoder(statement)[0] + else: + self.statement = self.unicode_statement = statement + + self.cursor = self.create_cursor() + return self + + @classmethod + def _init_default(cls, dialect, connection, dbapi_connection): + """Initialize execution context for a ColumnDefault construct.""" + + self = cls.__new__(cls) + self.root_connection = connection + self._dbapi_connection = dbapi_connection + self.dialect = connection.dialect + self.execution_options = connection._execution_options + self.cursor = self.create_cursor() + return self + + @util.memoized_property + def engine(self): + return self.root_connection.engine + + @util.memoized_property + def postfetch_cols(self): + return self.compiled.postfetch + + @util.memoized_property + def prefetch_cols(self): + if self.isinsert: + return self.compiled.insert_prefetch + elif self.isupdate: + return self.compiled.update_prefetch + else: + return () + + @util.memoized_property + def returning_cols(self): + self.compiled.returning + + @util.memoized_property + def no_parameters(self): + return self.execution_options.get("no_parameters", False) + + @util.memoized_property + def should_autocommit(self): + autocommit = self.execution_options.get('autocommit', + not self.compiled and + self.statement and + expression.PARSE_AUTOCOMMIT + or False) + + if autocommit is expression.PARSE_AUTOCOMMIT: + return self.should_autocommit_text(self.unicode_statement) + else: + return autocommit + + def _execute_scalar(self, stmt, type_): + """Execute a string statement on the current cursor, returning a + scalar result. + + Used to fire off sequences, default phrases, and "select lastrowid" + types of statements individually or in the context of a parent INSERT + or UPDATE statement. + + """ + + conn = self.root_connection + if isinstance(stmt, util.text_type) and \ + not self.dialect.supports_unicode_statements: + stmt = self.dialect._encoder(stmt)[0] + + if self.dialect.positional: + default_params = self.dialect.execute_sequence_format() + else: + default_params = {} + + conn._cursor_execute(self.cursor, stmt, default_params, context=self) + r = self.cursor.fetchone()[0] + if type_ is not None: + # apply type post processors to the result + proc = type_._cached_result_processor( + self.dialect, + self.cursor.description[0][1] + ) + if proc: + return proc(r) + return r + + @property + def connection(self): + return self.root_connection._branch() + + def should_autocommit_text(self, statement): + return AUTOCOMMIT_REGEXP.match(statement) + + def _use_server_side_cursor(self): + if not self.dialect.supports_server_side_cursors: + return False + + if self.dialect.server_side_cursors: + use_server_side = \ + self.execution_options.get('stream_results', True) and ( + (self.compiled and isinstance(self.compiled.statement, + expression.Selectable) + or + ( + (not self.compiled or + isinstance(self.compiled.statement, + expression.TextClause)) + and self.statement and SERVER_SIDE_CURSOR_RE.match( + self.statement)) + ) + ) + else: + use_server_side = \ + self.execution_options.get('stream_results', False) + + return use_server_side + + def create_cursor(self): + if self._use_server_side_cursor(): + self._is_server_side = True + return self.create_server_side_cursor() + else: + self._is_server_side = False + return self._dbapi_connection.cursor() + + def create_server_side_cursor(self): + raise NotImplementedError() + + def pre_exec(self): + pass + + def post_exec(self): + pass + + def get_result_processor(self, type_, colname, coltype): + """Return a 'result processor' for a given type as present in + cursor.description. + + This has a default implementation that dialects can override + for context-sensitive result type handling. + + """ + return type_._cached_result_processor(self.dialect, coltype) + + def get_lastrowid(self): + """return self.cursor.lastrowid, or equivalent, after an INSERT. + + This may involve calling special cursor functions, + issuing a new SELECT on the cursor (or a new one), + or returning a stored value that was + calculated within post_exec(). + + This function will only be called for dialects + which support "implicit" primary key generation, + keep preexecute_autoincrement_sequences set to False, + and when no explicit id value was bound to the + statement. + + The function is called once, directly after + post_exec() and before the transaction is committed + or ResultProxy is generated. If the post_exec() + method assigns a value to `self._lastrowid`, the + value is used in place of calling get_lastrowid(). + + Note that this method is *not* equivalent to the + ``lastrowid`` method on ``ResultProxy``, which is a + direct proxy to the DBAPI ``lastrowid`` accessor + in all cases. + + """ + return self.cursor.lastrowid + + def handle_dbapi_exception(self, e): + pass + + def get_result_proxy(self): + if self._is_server_side: + return result.BufferedRowResultProxy(self) + else: + return result.ResultProxy(self) + + @property + def rowcount(self): + return self.cursor.rowcount + + def supports_sane_rowcount(self): + return self.dialect.supports_sane_rowcount + + def supports_sane_multi_rowcount(self): + return self.dialect.supports_sane_multi_rowcount + + def _setup_crud_result_proxy(self): + if self.isinsert and \ + not self.executemany: + if not self._is_implicit_returning and \ + not self.compiled.inline and \ + self.dialect.postfetch_lastrowid: + + self._setup_ins_pk_from_lastrowid() + + elif not self._is_implicit_returning: + self._setup_ins_pk_from_empty() + + result = self.get_result_proxy() + + if self.isinsert: + if self._is_implicit_returning: + row = result.fetchone() + self.returned_defaults = row + self._setup_ins_pk_from_implicit_returning(row) + result._soft_close() + result._metadata = None + elif not self._is_explicit_returning: + result._soft_close() + result._metadata = None + elif self.isupdate and self._is_implicit_returning: + row = result.fetchone() + self.returned_defaults = row + result._soft_close() + result._metadata = None + + elif result._metadata is None: + # no results, get rowcount + # (which requires open cursor on some drivers + # such as kintersbasdb, mxodbc) + result.rowcount + result._soft_close() + return result + + def _setup_ins_pk_from_lastrowid(self): + key_getter = self.compiled._key_getters_for_crud_column[2] + table = self.compiled.statement.table + compiled_params = self.compiled_parameters[0] + + lastrowid = self.get_lastrowid() + if lastrowid is not None: + autoinc_col = table._autoincrement_column + if autoinc_col is not None: + # apply type post processors to the lastrowid + proc = autoinc_col.type._cached_result_processor( + self.dialect, None) + if proc is not None: + lastrowid = proc(lastrowid) + self.inserted_primary_key = [ + lastrowid if c is autoinc_col else + compiled_params.get(key_getter(c), None) + for c in table.primary_key + ] + else: + # don't have a usable lastrowid, so + # do the same as _setup_ins_pk_from_empty + self.inserted_primary_key = [ + compiled_params.get(key_getter(c), None) + for c in table.primary_key + ] + + def _setup_ins_pk_from_empty(self): + key_getter = self.compiled._key_getters_for_crud_column[2] + table = self.compiled.statement.table + compiled_params = self.compiled_parameters[0] + self.inserted_primary_key = [ + compiled_params.get(key_getter(c), None) + for c in table.primary_key + ] + + def _setup_ins_pk_from_implicit_returning(self, row): + if row is None: + self.inserted_primary_key = None + return + + key_getter = self.compiled._key_getters_for_crud_column[2] + table = self.compiled.statement.table + compiled_params = self.compiled_parameters[0] + self.inserted_primary_key = [ + row[col] if value is None else value + for col, value in [ + (col, compiled_params.get(key_getter(col), None)) + for col in table.primary_key + ] + ] + + def lastrow_has_defaults(self): + return (self.isinsert or self.isupdate) and \ + bool(self.compiled.postfetch) + + def set_input_sizes( + self, translate=None, include_types=None, exclude_types=None): + """Given a cursor and ClauseParameters, call the appropriate + style of ``setinputsizes()`` on the cursor, using DB-API types + from the bind parameter's ``TypeEngine`` objects. + + This method only called by those dialects which require it, + currently cx_oracle. + + """ + + if not hasattr(self.compiled, 'bind_names'): + return + + inputsizes = {} + for bindparam in self.compiled.bind_names: + + dialect_impl = bindparam.type._unwrapped_dialect_impl(self.dialect) + dialect_impl_cls = type(dialect_impl) + dbtype = dialect_impl.get_dbapi_type(self.dialect.dbapi) + if dbtype is not None and ( + not exclude_types or dbtype not in exclude_types and + dialect_impl_cls not in exclude_types + ) and ( + not include_types or dbtype in include_types or + dialect_impl_cls in include_types + ): + inputsizes[bindparam] = dbtype + else: + inputsizes[bindparam] = None + + if self.dialect._has_events: + self.dialect.dispatch.do_setinputsizes( + inputsizes, self.cursor, self.statement, self.parameters, self + ) + + if self.dialect.positional: + positional_inputsizes = [] + for key in self.compiled.positiontup: + bindparam = self.compiled.binds[key] + dbtype = inputsizes.get(bindparam, None) + if dbtype is not None: + if key in self._expanded_parameters: + positional_inputsizes.extend( + [dbtype] * len(self._expanded_parameters[key])) + else: + positional_inputsizes.append(dbtype) + try: + self.cursor.setinputsizes(*positional_inputsizes) + except BaseException as e: + self.root_connection._handle_dbapi_exception( + e, None, None, None, self) + else: + keyword_inputsizes = {} + for bindparam, key in self.compiled.bind_names.items(): + dbtype = inputsizes.get(bindparam, None) + if dbtype is not None: + if translate: + # TODO: this part won't work w/ the + # expanded_parameters feature, e.g. for cx_oracle + # quoted bound names + key = translate.get(key, key) + if not self.dialect.supports_unicode_binds: + key = self.dialect._encoder(key)[0] + if key in self._expanded_parameters: + keyword_inputsizes.update( + (expand_key, dbtype) for expand_key + in self._expanded_parameters[key] + ) + else: + keyword_inputsizes[key] = dbtype + try: + self.cursor.setinputsizes(**keyword_inputsizes) + except BaseException as e: + self.root_connection._handle_dbapi_exception( + e, None, None, None, self) + + def _exec_default(self, column, default, type_): + if default.is_sequence: + return self.fire_sequence(default, type_) + elif default.is_callable: + self.current_column = column + return default.arg(self) + elif default.is_clause_element: + # TODO: expensive branching here should be + # pulled into _exec_scalar() + conn = self.connection + if not default._arg_is_typed: + default_arg = expression.type_coerce(default.arg, type_) + else: + default_arg = default.arg + c = expression.select([default_arg]).compile(bind=conn) + return conn._execute_compiled(c, (), {}).scalar() + else: + return default.arg + + current_parameters = None + """A dictionary of parameters applied to the current row. + + This attribute is only available in the context of a user-defined default + generation function, e.g. as described at :ref:`context_default_functions`. + It consists of a dictionary which includes entries for each column/value + pair that is to be part of the INSERT or UPDATE statement. The keys of the + dictionary will be the key value of each :class:`.Column`, which is usually + synonymous with the name. + + Note that the :attr:`.DefaultExecutionContext.current_parameters` attribute + does not accommodate for the "multi-values" feature of the + :meth:`.Insert.values` method. The + :meth:`.DefaultExecutionContext.get_current_parameters` method should be + preferred. + + .. seealso:: + + :meth:`.DefaultExecutionContext.get_current_parameters` + + :ref:`context_default_functions` + + """ + + def get_current_parameters(self, isolate_multiinsert_groups=True): + """Return a dictionary of parameters applied to the current row. + + This method can only be used in the context of a user-defined default + generation function, e.g. as described at + :ref:`context_default_functions`. When invoked, a dictionary is + returned which includes entries for each column/value pair that is part + of the INSERT or UPDATE statement. The keys of the dictionary will be + the key value of each :class:`.Column`, which is usually synonymous + with the name. + + :param isolate_multiinsert_groups=True: indicates that multi-valued + INSERT contructs created using :meth:`.Insert.values` should be + handled by returning only the subset of parameters that are local + to the current column default invocation. When ``False``, the + raw parameters of the statement are returned including the + naming convention used in the case of multi-valued INSERT. + + .. versionadded:: 1.2 added + :meth:`.DefaultExecutionContext.get_current_parameters` + which provides more functionality over the existing + :attr:`.DefaultExecutionContext.current_parameters` + attribute. + + .. seealso:: + + :attr:`.DefaultExecutionContext.current_parameters` + + :ref:`context_default_functions` + + """ + try: + parameters = self.current_parameters + column = self.current_column + except AttributeError: + raise exc.InvalidRequestError( + "get_current_parameters() can only be invoked in the " + "context of a Python side column default function") + if isolate_multiinsert_groups and \ + self.isinsert and \ + self.compiled.statement._has_multi_parameters: + if column._is_multiparam_column: + index = column.index + 1 + d = {column.original.key: parameters[column.key]} + else: + d = {column.key: parameters[column.key]} + index = 0 + keys = self.compiled.statement.parameters[0].keys() + d.update( + (key, parameters["%s_m%d" % (key, index)]) + for key in keys + ) + return d + else: + return parameters + + def get_insert_default(self, column): + if column.default is None: + return None + else: + return self._exec_default(column, column.default, column.type) + + def get_update_default(self, column): + if column.onupdate is None: + return None + else: + return self._exec_default(column, column.onupdate, column.type) + + def _process_executemany_defaults(self): + key_getter = self.compiled._key_getters_for_crud_column[2] + + scalar_defaults = {} + + insert_prefetch = self.compiled.insert_prefetch + update_prefetch = self.compiled.update_prefetch + + # pre-determine scalar Python-side defaults + # to avoid many calls of get_insert_default()/ + # get_update_default() + for c in insert_prefetch: + if c.default and c.default.is_scalar: + scalar_defaults[c] = c.default.arg + for c in update_prefetch: + if c.onupdate and c.onupdate.is_scalar: + scalar_defaults[c] = c.onupdate.arg + + for param in self.compiled_parameters: + self.current_parameters = param + for c in insert_prefetch: + if c in scalar_defaults: + val = scalar_defaults[c] + else: + val = self.get_insert_default(c) + if val is not None: + param[key_getter(c)] = val + for c in update_prefetch: + if c in scalar_defaults: + val = scalar_defaults[c] + else: + val = self.get_update_default(c) + if val is not None: + param[key_getter(c)] = val + + del self.current_parameters + + def _process_executesingle_defaults(self): + key_getter = self.compiled._key_getters_for_crud_column[2] + self.current_parameters = compiled_parameters = \ + self.compiled_parameters[0] + + for c in self.compiled.insert_prefetch: + if c.default and \ + not c.default.is_sequence and c.default.is_scalar: + val = c.default.arg + else: + val = self.get_insert_default(c) + + if val is not None: + compiled_parameters[key_getter(c)] = val + + for c in self.compiled.update_prefetch: + val = self.get_update_default(c) + + if val is not None: + compiled_parameters[key_getter(c)] = val + del self.current_parameters + + +DefaultDialect.execution_ctx_cls = DefaultExecutionContext diff --git a/venv/Lib/site-packages/sqlalchemy/engine/interfaces.py b/venv/Lib/site-packages/sqlalchemy/engine/interfaces.py new file mode 100644 index 0000000..9c3b24e --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/engine/interfaces.py @@ -0,0 +1,1305 @@ +# engine/interfaces.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Define core interfaces used by the engine system.""" + +from .. import util + +# backwards compat +from ..sql.compiler import Compiled, TypeCompiler + + +class Dialect(object): + """Define the behavior of a specific database and DB-API combination. + + Any aspect of metadata definition, SQL query generation, + execution, result-set handling, or anything else which varies + between databases is defined under the general category of the + Dialect. The Dialect acts as a factory for other + database-specific object implementations including + ExecutionContext, Compiled, DefaultGenerator, and TypeEngine. + + All Dialects implement the following attributes: + + name + identifying name for the dialect from a DBAPI-neutral point of view + (i.e. 'sqlite') + + driver + identifying name for the dialect's DBAPI + + positional + True if the paramstyle for this Dialect is positional. + + paramstyle + the paramstyle to be used (some DB-APIs support multiple + paramstyles). + + convert_unicode + True if Unicode conversion should be applied to all ``str`` + types. + + encoding + type of encoding to use for unicode, usually defaults to + 'utf-8'. + + statement_compiler + a :class:`.Compiled` class used to compile SQL statements + + ddl_compiler + a :class:`.Compiled` class used to compile DDL statements + + server_version_info + a tuple containing a version number for the DB backend in use. + This value is only available for supporting dialects, and is + typically populated during the initial connection to the database. + + default_schema_name + the name of the default schema. This value is only available for + supporting dialects, and is typically populated during the + initial connection to the database. + + execution_ctx_cls + a :class:`.ExecutionContext` class used to handle statement execution + + execute_sequence_format + either the 'tuple' or 'list' type, depending on what cursor.execute() + accepts for the second argument (they vary). + + preparer + a :class:`~sqlalchemy.sql.compiler.IdentifierPreparer` class used to + quote identifiers. + + supports_alter + ``True`` if the database supports ``ALTER TABLE``. + + max_identifier_length + The maximum length of identifier names. + + supports_unicode_statements + Indicate whether the DB-API can receive SQL statements as Python + unicode strings + + supports_unicode_binds + Indicate whether the DB-API can receive string bind parameters + as Python unicode strings + + supports_sane_rowcount + Indicate whether the dialect properly implements rowcount for + ``UPDATE`` and ``DELETE`` statements. + + supports_sane_multi_rowcount + Indicate whether the dialect properly implements rowcount for + ``UPDATE`` and ``DELETE`` statements when executed via + executemany. + + preexecute_autoincrement_sequences + True if 'implicit' primary key functions must be executed separately + in order to get their value. This is currently oriented towards + PostgreSQL. + + implicit_returning + use RETURNING or equivalent during INSERT execution in order to load + newly generated primary keys and other column defaults in one execution, + which are then available via inserted_primary_key. + If an insert statement has returning() specified explicitly, + the "implicit" functionality is not used and inserted_primary_key + will not be available. + + colspecs + A dictionary of TypeEngine classes from sqlalchemy.types mapped + to subclasses that are specific to the dialect class. This + dictionary is class-level only and is not accessed from the + dialect instance itself. + + supports_default_values + Indicates if the construct ``INSERT INTO tablename DEFAULT + VALUES`` is supported + + supports_sequences + Indicates if the dialect supports CREATE SEQUENCE or similar. + + sequences_optional + If True, indicates if the "optional" flag on the Sequence() construct + should signal to not generate a CREATE SEQUENCE. Applies only to + dialects that support sequences. Currently used only to allow PostgreSQL + SERIAL to be used on a column that specifies Sequence() for usage on + other backends. + + supports_native_enum + Indicates if the dialect supports a native ENUM construct. + This will prevent types.Enum from generating a CHECK + constraint when that type is used. + + supports_native_boolean + Indicates if the dialect supports a native boolean construct. + This will prevent types.Boolean from generating a CHECK + constraint when that type is used. + + dbapi_exception_translation_map + A dictionary of names that will contain as values the names of + pep-249 exceptions ("IntegrityError", "OperationalError", etc) + keyed to alternate class names, to support the case where a + DBAPI has exception classes that aren't named as they are + referred to (e.g. IntegrityError = MyException). In the vast + majority of cases this dictionary is empty. + + .. versionadded:: 1.0.5 + + """ + + _has_events = False + + def create_connect_args(self, url): + """Build DB-API compatible connection arguments. + + Given a :class:`~sqlalchemy.engine.url.URL` object, returns a tuple + consisting of a `*args`/`**kwargs` suitable to send directly + to the dbapi's connect function. + + """ + + raise NotImplementedError() + + @classmethod + def type_descriptor(cls, typeobj): + """Transform a generic type to a dialect-specific type. + + Dialect classes will usually use the + :func:`.types.adapt_type` function in the types module to + accomplish this. + + The returned result is cached *per dialect class* so can + contain no dialect-instance state. + + """ + + raise NotImplementedError() + + def initialize(self, connection): + """Called during strategized creation of the dialect with a + connection. + + Allows dialects to configure options based on server version info or + other properties. + + The connection passed here is a SQLAlchemy Connection object, + with full capabilities. + + The initialize() method of the base dialect should be called via + super(). + + """ + + pass + + def reflecttable( + self, connection, table, include_columns, exclude_columns): + """Load table description from the database. + + Given a :class:`.Connection` and a + :class:`~sqlalchemy.schema.Table` object, reflect its columns and + properties from the database. + + The implementation of this method is provided by + :meth:`.DefaultDialect.reflecttable`, which makes use of + :class:`.Inspector` to retrieve column information. + + Dialects should **not** seek to implement this method, and should + instead implement individual schema inspection operations such as + :meth:`.Dialect.get_columns`, :meth:`.Dialect.get_pk_constraint`, + etc. + + """ + + raise NotImplementedError() + + def get_columns(self, connection, table_name, schema=None, **kw): + """Return information about columns in `table_name`. + + Given a :class:`.Connection`, a string + `table_name`, and an optional string `schema`, return column + information as a list of dictionaries with these keys: + + name + the column's name + + type + [sqlalchemy.types#TypeEngine] + + nullable + boolean + + default + the column's default value + + autoincrement + boolean + + sequence + a dictionary of the form + {'name' : str, 'start' :int, 'increment': int, 'minvalue': int, + 'maxvalue': int, 'nominvalue': bool, 'nomaxvalue': bool, + 'cycle': bool, 'cache': int, 'order': bool} + + Additional column attributes may be present. + """ + + raise NotImplementedError() + + def get_primary_keys(self, connection, table_name, schema=None, **kw): + """Return information about primary keys in `table_name`. + + + Deprecated. This method is only called by the default + implementation of :meth:`.Dialect.get_pk_constraint`. Dialects should + instead implement the :meth:`.Dialect.get_pk_constraint` method + directly. + + """ + + raise NotImplementedError() + + def get_pk_constraint(self, connection, table_name, schema=None, **kw): + """Return information about the primary key constraint on + table_name`. + + Given a :class:`.Connection`, a string + `table_name`, and an optional string `schema`, return primary + key information as a dictionary with these keys: + + constrained_columns + a list of column names that make up the primary key + + name + optional name of the primary key constraint. + + """ + raise NotImplementedError() + + def get_foreign_keys(self, connection, table_name, schema=None, **kw): + """Return information about foreign_keys in `table_name`. + + Given a :class:`.Connection`, a string + `table_name`, and an optional string `schema`, return foreign + key information as a list of dicts with these keys: + + name + the constraint's name + + constrained_columns + a list of column names that make up the foreign key + + referred_schema + the name of the referred schema + + referred_table + the name of the referred table + + referred_columns + a list of column names in the referred table that correspond to + constrained_columns + """ + + raise NotImplementedError() + + def get_table_names(self, connection, schema=None, **kw): + """Return a list of table names for `schema`.""" + + raise NotImplementedError() + + def get_temp_table_names(self, connection, schema=None, **kw): + """Return a list of temporary table names on the given connection, + if supported by the underlying backend. + + """ + + raise NotImplementedError() + + def get_view_names(self, connection, schema=None, **kw): + """Return a list of all view names available in the database. + + schema: + Optional, retrieve names from a non-default schema. + """ + + raise NotImplementedError() + + def get_temp_view_names(self, connection, schema=None, **kw): + """Return a list of temporary view names on the given connection, + if supported by the underlying backend. + + """ + + raise NotImplementedError() + + def get_view_definition(self, connection, view_name, schema=None, **kw): + """Return view definition. + + Given a :class:`.Connection`, a string + `view_name`, and an optional string `schema`, return the view + definition. + """ + + raise NotImplementedError() + + def get_indexes(self, connection, table_name, schema=None, **kw): + """Return information about indexes in `table_name`. + + Given a :class:`.Connection`, a string + `table_name` and an optional string `schema`, return index + information as a list of dictionaries with these keys: + + name + the index's name + + column_names + list of column names in order + + unique + boolean + """ + + raise NotImplementedError() + + def get_unique_constraints( + self, connection, table_name, schema=None, **kw): + r"""Return information about unique constraints in `table_name`. + + Given a string `table_name` and an optional string `schema`, return + unique constraint information as a list of dicts with these keys: + + name + the unique constraint's name + + column_names + list of column names in order + + \**kw + other options passed to the dialect's get_unique_constraints() + method. + + .. versionadded:: 0.9.0 + + """ + + raise NotImplementedError() + + def get_check_constraints( + self, connection, table_name, schema=None, **kw): + r"""Return information about check constraints in `table_name`. + + Given a string `table_name` and an optional string `schema`, return + check constraint information as a list of dicts with these keys: + + name + the check constraint's name + + sqltext + the check constraint's SQL expression + + \**kw + other options passed to the dialect's get_check_constraints() + method. + + .. versionadded:: 1.1.0 + + """ + + raise NotImplementedError() + + def get_table_comment( + self, connection, table_name, schema=None, **kw): + r"""Return the "comment" for the table identified by `table_name`. + + Given a string `table_name` and an optional string `schema`, return + table comment information as a dictionary with this key: + + text + text of the comment + + Raises ``NotImplementedError`` for dialects that don't support + comments. + + .. versionadded:: 1.2 + + """ + + raise NotImplementedError() + + def normalize_name(self, name): + """convert the given name to lowercase if it is detected as + case insensitive. + + this method is only used if the dialect defines + requires_name_normalize=True. + + """ + raise NotImplementedError() + + def denormalize_name(self, name): + """convert the given name to a case insensitive identifier + for the backend if it is an all-lowercase name. + + this method is only used if the dialect defines + requires_name_normalize=True. + + """ + raise NotImplementedError() + + def has_table(self, connection, table_name, schema=None): + """Check the existence of a particular table in the database. + + Given a :class:`.Connection` object and a string + `table_name`, return True if the given table (possibly within + the specified `schema`) exists in the database, False + otherwise. + """ + + raise NotImplementedError() + + def has_sequence(self, connection, sequence_name, schema=None): + """Check the existence of a particular sequence in the database. + + Given a :class:`.Connection` object and a string + `sequence_name`, return True if the given sequence exists in + the database, False otherwise. + """ + + raise NotImplementedError() + + def _get_server_version_info(self, connection): + """Retrieve the server version info from the given connection. + + This is used by the default implementation to populate the + "server_version_info" attribute and is called exactly + once upon first connect. + + """ + + raise NotImplementedError() + + def _get_default_schema_name(self, connection): + """Return the string name of the currently selected schema from + the given connection. + + This is used by the default implementation to populate the + "default_schema_name" attribute and is called exactly + once upon first connect. + + """ + + raise NotImplementedError() + + def do_begin(self, dbapi_connection): + """Provide an implementation of ``connection.begin()``, given a + DB-API connection. + + The DBAPI has no dedicated "begin" method and it is expected + that transactions are implicit. This hook is provided for those + DBAPIs that might need additional help in this area. + + Note that :meth:`.Dialect.do_begin` is not called unless a + :class:`.Transaction` object is in use. The + :meth:`.Dialect.do_autocommit` + hook is provided for DBAPIs that need some extra commands emitted + after a commit in order to enter the next transaction, when the + SQLAlchemy :class:`.Connection` is used in its default "autocommit" + mode. + + :param dbapi_connection: a DBAPI connection, typically + proxied within a :class:`.ConnectionFairy`. + + """ + + raise NotImplementedError() + + def do_rollback(self, dbapi_connection): + """Provide an implementation of ``connection.rollback()``, given + a DB-API connection. + + :param dbapi_connection: a DBAPI connection, typically + proxied within a :class:`.ConnectionFairy`. + + """ + + raise NotImplementedError() + + def do_commit(self, dbapi_connection): + """Provide an implementation of ``connection.commit()``, given a + DB-API connection. + + :param dbapi_connection: a DBAPI connection, typically + proxied within a :class:`.ConnectionFairy`. + + """ + + raise NotImplementedError() + + def do_close(self, dbapi_connection): + """Provide an implementation of ``connection.close()``, given a DBAPI + connection. + + This hook is called by the :class:`.Pool` when a connection has been + detached from the pool, or is being returned beyond the normal + capacity of the pool. + + .. versionadded:: 0.8 + + """ + + raise NotImplementedError() + + def create_xid(self): + """Create a two-phase transaction ID. + + This id will be passed to do_begin_twophase(), + do_rollback_twophase(), do_commit_twophase(). Its format is + unspecified. + """ + + raise NotImplementedError() + + def do_savepoint(self, connection, name): + """Create a savepoint with the given name. + + :param connection: a :class:`.Connection`. + :param name: savepoint name. + + """ + + raise NotImplementedError() + + def do_rollback_to_savepoint(self, connection, name): + """Rollback a connection to the named savepoint. + + :param connection: a :class:`.Connection`. + :param name: savepoint name. + + """ + + raise NotImplementedError() + + def do_release_savepoint(self, connection, name): + """Release the named savepoint on a connection. + + :param connection: a :class:`.Connection`. + :param name: savepoint name. + """ + + raise NotImplementedError() + + def do_begin_twophase(self, connection, xid): + """Begin a two phase transaction on the given connection. + + :param connection: a :class:`.Connection`. + :param xid: xid + + """ + + raise NotImplementedError() + + def do_prepare_twophase(self, connection, xid): + """Prepare a two phase transaction on the given connection. + + :param connection: a :class:`.Connection`. + :param xid: xid + + """ + + raise NotImplementedError() + + def do_rollback_twophase(self, connection, xid, is_prepared=True, + recover=False): + """Rollback a two phase transaction on the given connection. + + :param connection: a :class:`.Connection`. + :param xid: xid + :param is_prepared: whether or not + :meth:`.TwoPhaseTransaction.prepare` was called. + :param recover: if the recover flag was passed. + + """ + + raise NotImplementedError() + + def do_commit_twophase(self, connection, xid, is_prepared=True, + recover=False): + """Commit a two phase transaction on the given connection. + + + :param connection: a :class:`.Connection`. + :param xid: xid + :param is_prepared: whether or not + :meth:`.TwoPhaseTransaction.prepare` was called. + :param recover: if the recover flag was passed. + + """ + + raise NotImplementedError() + + def do_recover_twophase(self, connection): + """Recover list of uncommitted prepared two phase transaction + identifiers on the given connection. + + :param connection: a :class:`.Connection`. + + """ + + raise NotImplementedError() + + def do_executemany(self, cursor, statement, parameters, context=None): + """Provide an implementation of ``cursor.executemany(statement, + parameters)``.""" + + raise NotImplementedError() + + def do_execute(self, cursor, statement, parameters, context=None): + """Provide an implementation of ``cursor.execute(statement, + parameters)``.""" + + raise NotImplementedError() + + def do_execute_no_params(self, cursor, statement, parameters, + context=None): + """Provide an implementation of ``cursor.execute(statement)``. + + The parameter collection should not be sent. + + """ + + raise NotImplementedError() + + def is_disconnect(self, e, connection, cursor): + """Return True if the given DB-API error indicates an invalid + connection""" + + raise NotImplementedError() + + def connect(self): + """return a callable which sets up a newly created DBAPI connection. + + The callable accepts a single argument "conn" which is the + DBAPI connection itself. It has no return value. + + This is used to set dialect-wide per-connection options such as + isolation modes, unicode modes, etc. + + If a callable is returned, it will be assembled into a pool listener + that receives the direct DBAPI connection, with all wrappers removed. + + If None is returned, no listener will be generated. + + """ + return None + + def reset_isolation_level(self, dbapi_conn): + """Given a DBAPI connection, revert its isolation to the default. + + Note that this is a dialect-level method which is used as part + of the implementation of the :class:`.Connection` and + :class:`.Engine` + isolation level facilities; these APIs should be preferred for + most typical use cases. + + .. seealso:: + + :meth:`.Connection.get_isolation_level` - view current level + + :attr:`.Connection.default_isolation_level` - view default level + + :paramref:`.Connection.execution_options.isolation_level` - + set per :class:`.Connection` isolation level + + :paramref:`.create_engine.isolation_level` - + set per :class:`.Engine` isolation level + + """ + + raise NotImplementedError() + + def set_isolation_level(self, dbapi_conn, level): + """Given a DBAPI connection, set its isolation level. + + Note that this is a dialect-level method which is used as part + of the implementation of the :class:`.Connection` and + :class:`.Engine` + isolation level facilities; these APIs should be preferred for + most typical use cases. + + .. seealso:: + + :meth:`.Connection.get_isolation_level` - view current level + + :attr:`.Connection.default_isolation_level` - view default level + + :paramref:`.Connection.execution_options.isolation_level` - + set per :class:`.Connection` isolation level + + :paramref:`.create_engine.isolation_level` - + set per :class:`.Engine` isolation level + + """ + + raise NotImplementedError() + + def get_isolation_level(self, dbapi_conn): + """Given a DBAPI connection, return its isolation level. + + When working with a :class:`.Connection` object, the corresponding + DBAPI connection may be procured using the + :attr:`.Connection.connection` accessor. + + Note that this is a dialect-level method which is used as part + of the implementation of the :class:`.Connection` and + :class:`.Engine` isolation level facilities; + these APIs should be preferred for most typical use cases. + + + .. seealso:: + + :meth:`.Connection.get_isolation_level` - view current level + + :attr:`.Connection.default_isolation_level` - view default level + + :paramref:`.Connection.execution_options.isolation_level` - + set per :class:`.Connection` isolation level + + :paramref:`.create_engine.isolation_level` - + set per :class:`.Engine` isolation level + + + """ + + raise NotImplementedError() + + @classmethod + def get_dialect_cls(cls, url): + """Given a URL, return the :class:`.Dialect` that will be used. + + This is a hook that allows an external plugin to provide functionality + around an existing dialect, by allowing the plugin to be loaded + from the url based on an entrypoint, and then the plugin returns + the actual dialect to be used. + + By default this just returns the cls. + + .. versionadded:: 1.0.3 + + """ + return cls + + @classmethod + def engine_created(cls, engine): + """A convenience hook called before returning the final :class:`.Engine`. + + If the dialect returned a different class from the + :meth:`.get_dialect_cls` + method, then the hook is called on both classes, first on + the dialect class returned by the :meth:`.get_dialect_cls` method and + then on the class on which the method was called. + + The hook should be used by dialects and/or wrappers to apply special + events to the engine or its components. In particular, it allows + a dialect-wrapping class to apply dialect-level events. + + .. versionadded:: 1.0.3 + + """ + pass + + +class CreateEnginePlugin(object): + """A set of hooks intended to augment the construction of an + :class:`.Engine` object based on entrypoint names in a URL. + + The purpose of :class:`.CreateEnginePlugin` is to allow third-party + systems to apply engine, pool and dialect level event listeners without + the need for the target application to be modified; instead, the plugin + names can be added to the database URL. Target applications for + :class:`.CreateEnginePlugin` include: + + * connection and SQL performance tools, e.g. which use events to track + number of checkouts and/or time spent with statements + + * connectivity plugins such as proxies + + Plugins are registered using entry points in a similar way as that + of dialects:: + + entry_points={ + 'sqlalchemy.plugins': [ + 'myplugin = myapp.plugins:MyPlugin' + ] + + A plugin that uses the above names would be invoked from a database + URL as in:: + + from sqlalchemy import create_engine + + engine = create_engine( + "mysql+pymysql://scott:tiger@localhost/test?plugin=myplugin") + + Alternatively, the :paramref:`.create_engine.plugins" argument may be + passed as a list to :func:`.create_engine`:: + + engine = create_engine( + "mysql+pymysql://scott:tiger@localhost/test", + plugins=["myplugin"]) + + .. versionadded:: 1.2.3 plugin names can also be specified + to :func:`.create_engine` as a list + + The ``plugin`` argument supports multiple instances, so that a URL + may specify multiple plugins; they are loaded in the order stated + in the URL:: + + engine = create_engine( + "mysql+pymysql://scott:tiger@localhost/" + "test?plugin=plugin_one&plugin=plugin_twp&plugin=plugin_three") + + A plugin can receive additional arguments from the URL string as + well as from the keyword arguments passed to :func:`.create_engine`. + The :class:`.URL` object and the keyword dictionary are passed to the + constructor so that these arguments can be extracted from the url's + :attr:`.URL.query` collection as well as from the dictionary:: + + class MyPlugin(CreateEnginePlugin): + def __init__(self, url, kwargs): + self.my_argument_one = url.query.pop('my_argument_one') + self.my_argument_two = url.query.pop('my_argument_two') + self.my_argument_three = kwargs.pop('my_argument_three', None) + + Arguments like those illustrated above would be consumed from the + following:: + + from sqlalchemy import create_engine + + engine = create_engine( + "mysql+pymysql://scott:tiger@localhost/" + "test?plugin=myplugin&my_argument_one=foo&my_argument_two=bar", + my_argument_three='bat') + + The URL and dictionary are used for subsequent setup of the engine + as they are, so the plugin can modify their arguments in-place. + Arguments that are only understood by the plugin should be popped + or otherwise removed so that they aren't interpreted as erroneous + arguments afterwards. + + When the engine creation process completes and produces the + :class:`.Engine` object, it is again passed to the plugin via the + :meth:`.CreateEnginePlugin.engine_created` hook. In this hook, additional + changes can be made to the engine, most typically involving setup of + events (e.g. those defined in :ref:`core_event_toplevel`). + + .. versionadded:: 1.1 + + """ + def __init__(self, url, kwargs): + """Contruct a new :class:`.CreateEnginePlugin`. + + The plugin object is instantiated individually for each call + to :func:`.create_engine`. A single :class:`.Engine` will be + passed to the :meth:`.CreateEnginePlugin.engine_created` method + corresponding to this URL. + + :param url: the :class:`.URL` object. The plugin should inspect + what it needs here as well as remove its custom arguments from the + :attr:`.URL.query` collection. The URL can be modified in-place + in any other way as well. + :param kwargs: The keyword arguments passed to :func`.create_engine`. + The plugin can read and modify this dictionary in-place, to affect + the ultimate arguments used to create the engine. It should + remove its custom arguments from the dictionary as well. + + """ + self.url = url + + def handle_dialect_kwargs(self, dialect_cls, dialect_args): + """parse and modify dialect kwargs""" + + def handle_pool_kwargs(self, pool_cls, pool_args): + """parse and modify pool kwargs""" + + def engine_created(self, engine): + """Receive the :class:`.Engine` object when it is fully constructed. + + The plugin may make additional changes to the engine, such as + registering engine or connection pool events. + + """ + + +class ExecutionContext(object): + """A messenger object for a Dialect that corresponds to a single + execution. + + ExecutionContext should have these data members: + + connection + Connection object which can be freely used by default value + generators to execute SQL. This Connection should reference the + same underlying connection/transactional resources of + root_connection. + + root_connection + Connection object which is the source of this ExecutionContext. This + Connection may have close_with_result=True set, in which case it can + only be used once. + + dialect + dialect which created this ExecutionContext. + + cursor + DB-API cursor procured from the connection, + + compiled + if passed to constructor, sqlalchemy.engine.base.Compiled object + being executed, + + statement + string version of the statement to be executed. Is either + passed to the constructor, or must be created from the + sql.Compiled object by the time pre_exec() has completed. + + parameters + bind parameters passed to the execute() method. For compiled + statements, this is a dictionary or list of dictionaries. For + textual statements, it should be in a format suitable for the + dialect's paramstyle (i.e. dict or list of dicts for non + positional, list or list of lists/tuples for positional). + + isinsert + True if the statement is an INSERT. + + isupdate + True if the statement is an UPDATE. + + should_autocommit + True if the statement is a "committable" statement. + + prefetch_cols + a list of Column objects for which a client-side default + was fired off. Applies to inserts and updates. + + postfetch_cols + a list of Column objects for which a server-side default or + inline SQL expression value was fired off. Applies to inserts + and updates. + """ + + exception = None + """A DBAPI-level exception that was caught when this ExecutionContext + attempted to execute a statement. + + This attribute is meaningful only within the + :meth:`.ConnectionEvents.dbapi_error` event. + + .. versionadded:: 0.9.7 + + .. seealso:: + + :attr:`.ExecutionContext.is_disconnect` + + :meth:`.ConnectionEvents.dbapi_error` + + """ + + is_disconnect = None + """Boolean flag set to True or False when a DBAPI-level exception + is caught when this ExecutionContext attempted to execute a statement. + + This attribute is meaningful only within the + :meth:`.ConnectionEvents.dbapi_error` event. + + .. versionadded:: 0.9.7 + + .. seealso:: + + :attr:`.ExecutionContext.exception` + + :meth:`.ConnectionEvents.dbapi_error` + + """ + + def create_cursor(self): + """Return a new cursor generated from this ExecutionContext's + connection. + + Some dialects may wish to change the behavior of + connection.cursor(), such as postgresql which may return a PG + "server side" cursor. + """ + + raise NotImplementedError() + + def pre_exec(self): + """Called before an execution of a compiled statement. + + If a compiled statement was passed to this ExecutionContext, + the `statement` and `parameters` datamembers must be + initialized after this statement is complete. + """ + + raise NotImplementedError() + + def post_exec(self): + """Called after the execution of a compiled statement. + + If a compiled statement was passed to this ExecutionContext, + the `last_insert_ids`, `last_inserted_params`, etc. + datamembers should be available after this method completes. + """ + + raise NotImplementedError() + + def result(self): + """Return a result object corresponding to this ExecutionContext. + + Returns a ResultProxy. + """ + + raise NotImplementedError() + + def handle_dbapi_exception(self, e): + """Receive a DBAPI exception which occurred upon execute, result + fetch, etc.""" + + raise NotImplementedError() + + def should_autocommit_text(self, statement): + """Parse the given textual statement and return True if it refers to + a "committable" statement""" + + raise NotImplementedError() + + def lastrow_has_defaults(self): + """Return True if the last INSERT or UPDATE row contained + inlined or database-side defaults. + """ + + raise NotImplementedError() + + def get_rowcount(self): + """Return the DBAPI ``cursor.rowcount`` value, or in some + cases an interpreted value. + + See :attr:`.ResultProxy.rowcount` for details on this. + + """ + + raise NotImplementedError() + + +class Connectable(object): + """Interface for an object which supports execution of SQL constructs. + + The two implementations of :class:`.Connectable` are + :class:`.Connection` and :class:`.Engine`. + + Connectable must also implement the 'dialect' member which references a + :class:`.Dialect` instance. + + """ + + def connect(self, **kwargs): + """Return a :class:`.Connection` object. + + Depending on context, this may be ``self`` if this object + is already an instance of :class:`.Connection`, or a newly + procured :class:`.Connection` if this object is an instance + of :class:`.Engine`. + + """ + + def contextual_connect(self): + """Return a :class:`.Connection` object which may be part of an ongoing + context. + + Depending on context, this may be ``self`` if this object + is already an instance of :class:`.Connection`, or a newly + procured :class:`.Connection` if this object is an instance + of :class:`.Engine`. + + """ + + raise NotImplementedError() + + @util.deprecated("0.7", + "Use the create() method on the given schema " + "object directly, i.e. :meth:`.Table.create`, " + ":meth:`.Index.create`, :meth:`.MetaData.create_all`") + def create(self, entity, **kwargs): + """Emit CREATE statements for the given schema entity. + """ + + raise NotImplementedError() + + @util.deprecated("0.7", + "Use the drop() method on the given schema " + "object directly, i.e. :meth:`.Table.drop`, " + ":meth:`.Index.drop`, :meth:`.MetaData.drop_all`") + def drop(self, entity, **kwargs): + """Emit DROP statements for the given schema entity. + """ + + raise NotImplementedError() + + def execute(self, object, *multiparams, **params): + """Executes the given construct and returns a :class:`.ResultProxy`.""" + raise NotImplementedError() + + def scalar(self, object, *multiparams, **params): + """Executes and returns the first column of the first row. + + The underlying cursor is closed after execution. + """ + raise NotImplementedError() + + def _run_visitor(self, visitorcallable, element, + **kwargs): + raise NotImplementedError() + + def _execute_clauseelement(self, elem, multiparams=None, params=None): + raise NotImplementedError() + + +class ExceptionContext(object): + """Encapsulate information about an error condition in progress. + + This object exists solely to be passed to the + :meth:`.ConnectionEvents.handle_error` event, supporting an interface that + can be extended without backwards-incompatibility. + + .. versionadded:: 0.9.7 + + """ + + connection = None + """The :class:`.Connection` in use during the exception. + + This member is present, except in the case of a failure when + first connecting. + + .. seealso:: + + :attr:`.ExceptionContext.engine` + + + """ + + engine = None + """The :class:`.Engine` in use during the exception. + + This member should always be present, even in the case of a failure + when first connecting. + + .. versionadded:: 1.0.0 + + """ + + cursor = None + """The DBAPI cursor object. + + May be None. + + """ + + statement = None + """String SQL statement that was emitted directly to the DBAPI. + + May be None. + + """ + + parameters = None + """Parameter collection that was emitted directly to the DBAPI. + + May be None. + + """ + + original_exception = None + """The exception object which was caught. + + This member is always present. + + """ + + sqlalchemy_exception = None + """The :class:`sqlalchemy.exc.StatementError` which wraps the original, + and will be raised if exception handling is not circumvented by the event. + + May be None, as not all exception types are wrapped by SQLAlchemy. + For DBAPI-level exceptions that subclass the dbapi's Error class, this + field will always be present. + + """ + + chained_exception = None + """The exception that was returned by the previous handler in the + exception chain, if any. + + If present, this exception will be the one ultimately raised by + SQLAlchemy unless a subsequent handler replaces it. + + May be None. + + """ + + execution_context = None + """The :class:`.ExecutionContext` corresponding to the execution + operation in progress. + + This is present for statement execution operations, but not for + operations such as transaction begin/end. It also is not present when + the exception was raised before the :class:`.ExecutionContext` + could be constructed. + + Note that the :attr:`.ExceptionContext.statement` and + :attr:`.ExceptionContext.parameters` members may represent a + different value than that of the :class:`.ExecutionContext`, + potentially in the case where a + :meth:`.ConnectionEvents.before_cursor_execute` event or similar + modified the statement/parameters to be sent. + + May be None. + + """ + + is_disconnect = None + """Represent whether the exception as occurred represents a "disconnect" + condition. + + This flag will always be True or False within the scope of the + :meth:`.ConnectionEvents.handle_error` handler. + + SQLAlchemy will defer to this flag in order to determine whether or not + the connection should be invalidated subsequently. That is, by + assigning to this flag, a "disconnect" event which then results in + a connection and pool invalidation can be invoked or prevented by + changing this flag. + + """ + + invalidate_pool_on_disconnect = True + """Represent whether all connections in the pool should be invalidated + when a "disconnect" condition is in effect. + + Setting this flag to False within the scope of the + :meth:`.ConnectionEvents.handle_error` event will have the effect such + that the full collection of connections in the pool will not be + invalidated during a disconnect; only the current connection that is the + subject of the error will actually be invalidated. + + The purpose of this flag is for custom disconnect-handling schemes where + the invalidation of other connections in the pool is to be performed + based on other conditions, or even on a per-connection basis. + + .. versionadded:: 1.0.3 + + """ diff --git a/venv/Lib/site-packages/sqlalchemy/engine/reflection.py b/venv/Lib/site-packages/sqlalchemy/engine/reflection.py new file mode 100644 index 0000000..a572a34 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/engine/reflection.py @@ -0,0 +1,877 @@ +# engine/reflection.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Provides an abstraction for obtaining database schema information. + +Usage Notes: + +Here are some general conventions when accessing the low level inspector +methods such as get_table_names, get_columns, etc. + +1. Inspector methods return lists of dicts in most cases for the following + reasons: + + * They're both standard types that can be serialized. + * Using a dict instead of a tuple allows easy expansion of attributes. + * Using a list for the outer structure maintains order and is easy to work + with (e.g. list comprehension [d['name'] for d in cols]). + +2. Records that contain a name, such as the column name in a column record + use the key 'name'. So for most return values, each record will have a + 'name' attribute.. +""" + +from .. import exc, sql +from ..sql import schema as sa_schema +from .. import util +from ..sql.type_api import TypeEngine +from ..util import deprecated +from ..util import topological +from .. import inspection +from .base import Connectable + + +@util.decorator +def cache(fn, self, con, *args, **kw): + info_cache = kw.get('info_cache', None) + if info_cache is None: + return fn(self, con, *args, **kw) + key = ( + fn.__name__, + tuple(a for a in args if isinstance(a, util.string_types)), + tuple((k, v) for k, v in kw.items() if + isinstance(v, + util.string_types + util.int_types + (float, ) + ) + ) + ) + ret = info_cache.get(key) + if ret is None: + ret = fn(self, con, *args, **kw) + info_cache[key] = ret + return ret + + +class Inspector(object): + """Performs database schema inspection. + + The Inspector acts as a proxy to the reflection methods of the + :class:`~sqlalchemy.engine.interfaces.Dialect`, providing a + consistent interface as well as caching support for previously + fetched metadata. + + A :class:`.Inspector` object is usually created via the + :func:`.inspect` function:: + + from sqlalchemy import inspect, create_engine + engine = create_engine('...') + insp = inspect(engine) + + The inspection method above is equivalent to using the + :meth:`.Inspector.from_engine` method, i.e.:: + + engine = create_engine('...') + insp = Inspector.from_engine(engine) + + Where above, the :class:`~sqlalchemy.engine.interfaces.Dialect` may opt + to return an :class:`.Inspector` subclass that provides additional + methods specific to the dialect's target database. + + """ + + def __init__(self, bind): + """Initialize a new :class:`.Inspector`. + + :param bind: a :class:`~sqlalchemy.engine.Connectable`, + which is typically an instance of + :class:`~sqlalchemy.engine.Engine` or + :class:`~sqlalchemy.engine.Connection`. + + For a dialect-specific instance of :class:`.Inspector`, see + :meth:`.Inspector.from_engine` + + """ + # this might not be a connection, it could be an engine. + self.bind = bind + + # set the engine + if hasattr(bind, 'engine'): + self.engine = bind.engine + else: + self.engine = bind + + if self.engine is bind: + # if engine, ensure initialized + bind.connect().close() + + self.dialect = self.engine.dialect + self.info_cache = {} + + @classmethod + def from_engine(cls, bind): + """Construct a new dialect-specific Inspector object from the given + engine or connection. + + :param bind: a :class:`~sqlalchemy.engine.Connectable`, + which is typically an instance of + :class:`~sqlalchemy.engine.Engine` or + :class:`~sqlalchemy.engine.Connection`. + + This method differs from direct a direct constructor call of + :class:`.Inspector` in that the + :class:`~sqlalchemy.engine.interfaces.Dialect` is given a chance to + provide a dialect-specific :class:`.Inspector` instance, which may + provide additional methods. + + See the example at :class:`.Inspector`. + + """ + if hasattr(bind.dialect, 'inspector'): + return bind.dialect.inspector(bind) + return Inspector(bind) + + @inspection._inspects(Connectable) + def _insp(bind): + return Inspector.from_engine(bind) + + @property + def default_schema_name(self): + """Return the default schema name presented by the dialect + for the current engine's database user. + + E.g. this is typically ``public`` for PostgreSQL and ``dbo`` + for SQL Server. + + """ + return self.dialect.default_schema_name + + def get_schema_names(self): + """Return all schema names. + """ + + if hasattr(self.dialect, 'get_schema_names'): + return self.dialect.get_schema_names(self.bind, + info_cache=self.info_cache) + return [] + + def get_table_names(self, schema=None, order_by=None): + """Return all table names in referred to within a particular schema. + + The names are expected to be real tables only, not views. + Views are instead returned using the :meth:`.Inspector.get_view_names` + method. + + + :param schema: Schema name. If ``schema`` is left at ``None``, the + database's default schema is + used, else the named schema is searched. If the database does not + support named schemas, behavior is undefined if ``schema`` is not + passed as ``None``. For special quoting, use :class:`.quoted_name`. + + :param order_by: Optional, may be the string "foreign_key" to sort + the result on foreign key dependencies. Does not automatically + resolve cycles, and will raise :class:`.CircularDependencyError` + if cycles exist. + + .. deprecated:: 1.0.0 - see + :meth:`.Inspector.get_sorted_table_and_fkc_names` for a version + of this which resolves foreign key cycles between tables + automatically. + + .. versionchanged:: 0.8 the "foreign_key" sorting sorts tables + in order of dependee to dependent; that is, in creation + order, rather than in drop order. This is to maintain + consistency with similar features such as + :attr:`.MetaData.sorted_tables` and :func:`.util.sort_tables`. + + .. seealso:: + + :meth:`.Inspector.get_sorted_table_and_fkc_names` + + :attr:`.MetaData.sorted_tables` + + """ + + if hasattr(self.dialect, 'get_table_names'): + tnames = self.dialect.get_table_names( + self.bind, schema, info_cache=self.info_cache) + else: + tnames = self.engine.table_names(schema) + if order_by == 'foreign_key': + tuples = [] + for tname in tnames: + for fkey in self.get_foreign_keys(tname, schema): + if tname != fkey['referred_table']: + tuples.append((fkey['referred_table'], tname)) + tnames = list(topological.sort(tuples, tnames)) + return tnames + + def get_sorted_table_and_fkc_names(self, schema=None): + """Return dependency-sorted table and foreign key constraint names in + referred to within a particular schema. + + This will yield 2-tuples of + ``(tablename, [(tname, fkname), (tname, fkname), ...])`` + consisting of table names in CREATE order grouped with the foreign key + constraint names that are not detected as belonging to a cycle. + The final element + will be ``(None, [(tname, fkname), (tname, fkname), ..])`` + which will consist of remaining + foreign key constraint names that would require a separate CREATE + step after-the-fact, based on dependencies between tables. + + .. versionadded:: 1.0.- + + .. seealso:: + + :meth:`.Inspector.get_table_names` + + :func:`.sort_tables_and_constraints` - similar method which works + with an already-given :class:`.MetaData`. + + """ + if hasattr(self.dialect, 'get_table_names'): + tnames = self.dialect.get_table_names( + self.bind, schema, info_cache=self.info_cache) + else: + tnames = self.engine.table_names(schema) + + tuples = set() + remaining_fkcs = set() + + fknames_for_table = {} + for tname in tnames: + fkeys = self.get_foreign_keys(tname, schema) + fknames_for_table[tname] = set( + [fk['name'] for fk in fkeys] + ) + for fkey in fkeys: + if tname != fkey['referred_table']: + tuples.add((fkey['referred_table'], tname)) + try: + candidate_sort = list(topological.sort(tuples, tnames)) + except exc.CircularDependencyError as err: + for edge in err.edges: + tuples.remove(edge) + remaining_fkcs.update( + (edge[1], fkc) + for fkc in fknames_for_table[edge[1]] + ) + + candidate_sort = list(topological.sort(tuples, tnames)) + return [ + (tname, fknames_for_table[tname].difference(remaining_fkcs)) + for tname in candidate_sort + ] + [(None, list(remaining_fkcs))] + + def get_temp_table_names(self): + """return a list of temporary table names for the current bind. + + This method is unsupported by most dialects; currently + only SQLite implements it. + + .. versionadded:: 1.0.0 + + """ + return self.dialect.get_temp_table_names( + self.bind, info_cache=self.info_cache) + + def get_temp_view_names(self): + """return a list of temporary view names for the current bind. + + This method is unsupported by most dialects; currently + only SQLite implements it. + + .. versionadded:: 1.0.0 + + """ + return self.dialect.get_temp_view_names( + self.bind, info_cache=self.info_cache) + + def get_table_options(self, table_name, schema=None, **kw): + """Return a dictionary of options specified when the table of the + given name was created. + + This currently includes some options that apply to MySQL tables. + + :param table_name: string name of the table. For special quoting, + use :class:`.quoted_name`. + + :param schema: string schema name; if omitted, uses the default schema + of the database connection. For special quoting, + use :class:`.quoted_name`. + + """ + if hasattr(self.dialect, 'get_table_options'): + return self.dialect.get_table_options( + self.bind, table_name, schema, + info_cache=self.info_cache, **kw) + return {} + + def get_view_names(self, schema=None): + """Return all view names in `schema`. + + :param schema: Optional, retrieve names from a non-default schema. + For special quoting, use :class:`.quoted_name`. + + """ + + return self.dialect.get_view_names(self.bind, schema, + info_cache=self.info_cache) + + def get_view_definition(self, view_name, schema=None): + """Return definition for `view_name`. + + :param schema: Optional, retrieve names from a non-default schema. + For special quoting, use :class:`.quoted_name`. + + """ + + return self.dialect.get_view_definition( + self.bind, view_name, schema, info_cache=self.info_cache) + + def get_columns(self, table_name, schema=None, **kw): + """Return information about columns in `table_name`. + + Given a string `table_name` and an optional string `schema`, return + column information as a list of dicts with these keys: + + * ``name`` - the column's name + + * ``type`` - the type of this column; an instance of + :class:`~sqlalchemy.types.TypeEngine` + + * ``nullable`` - boolean flag if the column is NULL or NOT NULL + + * ``default`` - the column's server default value - this is returned + as a string SQL expression. + + * ``attrs`` - dict containing optional column attributes + + :param table_name: string name of the table. For special quoting, + use :class:`.quoted_name`. + + :param schema: string schema name; if omitted, uses the default schema + of the database connection. For special quoting, + use :class:`.quoted_name`. + + :return: list of dictionaries, each representing the definition of + a database column. + + """ + + col_defs = self.dialect.get_columns(self.bind, table_name, schema, + info_cache=self.info_cache, + **kw) + for col_def in col_defs: + # make this easy and only return instances for coltype + coltype = col_def['type'] + if not isinstance(coltype, TypeEngine): + col_def['type'] = coltype() + return col_defs + + @deprecated('0.7', 'Call to deprecated method get_primary_keys.' + ' Use get_pk_constraint instead.') + def get_primary_keys(self, table_name, schema=None, **kw): + """Return information about primary keys in `table_name`. + + Given a string `table_name`, and an optional string `schema`, return + primary key information as a list of column names. + """ + + return self.dialect.get_pk_constraint(self.bind, table_name, schema, + info_cache=self.info_cache, + **kw)['constrained_columns'] + + def get_pk_constraint(self, table_name, schema=None, **kw): + """Return information about primary key constraint on `table_name`. + + Given a string `table_name`, and an optional string `schema`, return + primary key information as a dictionary with these keys: + + constrained_columns + a list of column names that make up the primary key + + name + optional name of the primary key constraint. + + :param table_name: string name of the table. For special quoting, + use :class:`.quoted_name`. + + :param schema: string schema name; if omitted, uses the default schema + of the database connection. For special quoting, + use :class:`.quoted_name`. + + """ + return self.dialect.get_pk_constraint(self.bind, table_name, schema, + info_cache=self.info_cache, + **kw) + + def get_foreign_keys(self, table_name, schema=None, **kw): + """Return information about foreign_keys in `table_name`. + + Given a string `table_name`, and an optional string `schema`, return + foreign key information as a list of dicts with these keys: + + constrained_columns + a list of column names that make up the foreign key + + referred_schema + the name of the referred schema + + referred_table + the name of the referred table + + referred_columns + a list of column names in the referred table that correspond to + constrained_columns + + name + optional name of the foreign key constraint. + + :param table_name: string name of the table. For special quoting, + use :class:`.quoted_name`. + + :param schema: string schema name; if omitted, uses the default schema + of the database connection. For special quoting, + use :class:`.quoted_name`. + + """ + + return self.dialect.get_foreign_keys(self.bind, table_name, schema, + info_cache=self.info_cache, + **kw) + + def get_indexes(self, table_name, schema=None, **kw): + """Return information about indexes in `table_name`. + + Given a string `table_name` and an optional string `schema`, return + index information as a list of dicts with these keys: + + name + the index's name + + column_names + list of column names in order + + unique + boolean + + dialect_options + dict of dialect-specific index options. May not be present + for all dialects. + + .. versionadded:: 1.0.0 + + :param table_name: string name of the table. For special quoting, + use :class:`.quoted_name`. + + :param schema: string schema name; if omitted, uses the default schema + of the database connection. For special quoting, + use :class:`.quoted_name`. + + """ + + return self.dialect.get_indexes(self.bind, table_name, + schema, + info_cache=self.info_cache, **kw) + + def get_unique_constraints(self, table_name, schema=None, **kw): + """Return information about unique constraints in `table_name`. + + Given a string `table_name` and an optional string `schema`, return + unique constraint information as a list of dicts with these keys: + + name + the unique constraint's name + + column_names + list of column names in order + + :param table_name: string name of the table. For special quoting, + use :class:`.quoted_name`. + + :param schema: string schema name; if omitted, uses the default schema + of the database connection. For special quoting, + use :class:`.quoted_name`. + + .. versionadded:: 0.8.4 + + """ + + return self.dialect.get_unique_constraints( + self.bind, table_name, schema, info_cache=self.info_cache, **kw) + + def get_table_comment(self, table_name, schema=None, **kw): + """Return information about the table comment for ``table_name``. + + Given a string ``table_name`` and an optional string ``schema``, + return table comment information as a dictionary with these keys: + + text + text of the comment. + + Raises ``NotImplementedError`` for a dialect that does not support + comments. + + .. versionadded:: 1.2 + + """ + + return self.dialect.get_table_comment( + self.bind, table_name, schema, info_cache=self.info_cache, + **kw) + + def get_check_constraints(self, table_name, schema=None, **kw): + """Return information about check constraints in `table_name`. + + Given a string `table_name` and an optional string `schema`, return + check constraint information as a list of dicts with these keys: + + name + the check constraint's name + + sqltext + the check constraint's SQL expression + + :param table_name: string name of the table. For special quoting, + use :class:`.quoted_name`. + + :param schema: string schema name; if omitted, uses the default schema + of the database connection. For special quoting, + use :class:`.quoted_name`. + + .. versionadded:: 1.1.0 + + """ + + return self.dialect.get_check_constraints( + self.bind, table_name, schema, info_cache=self.info_cache, **kw) + + def reflecttable(self, table, include_columns, exclude_columns=(), + _extend_on=None): + """Given a Table object, load its internal constructs based on + introspection. + + This is the underlying method used by most dialects to produce + table reflection. Direct usage is like:: + + from sqlalchemy import create_engine, MetaData, Table + from sqlalchemy.engine.reflection import Inspector + + engine = create_engine('...') + meta = MetaData() + user_table = Table('user', meta) + insp = Inspector.from_engine(engine) + insp.reflecttable(user_table, None) + + :param table: a :class:`~sqlalchemy.schema.Table` instance. + :param include_columns: a list of string column names to include + in the reflection process. If ``None``, all columns are reflected. + + """ + + if _extend_on is not None: + if table in _extend_on: + return + else: + _extend_on.add(table) + + dialect = self.bind.dialect + + schema = self.bind.schema_for_object(table) + + table_name = table.name + + # get table-level arguments that are specifically + # intended for reflection, e.g. oracle_resolve_synonyms. + # these are unconditionally passed to related Table + # objects + reflection_options = dict( + (k, table.dialect_kwargs.get(k)) + for k in dialect.reflection_options + if k in table.dialect_kwargs + ) + + # reflect table options, like mysql_engine + tbl_opts = self.get_table_options( + table_name, schema, **table.dialect_kwargs) + if tbl_opts: + # add additional kwargs to the Table if the dialect + # returned them + table._validate_dialect_kwargs(tbl_opts) + + if util.py2k: + if isinstance(schema, str): + schema = schema.decode(dialect.encoding) + if isinstance(table_name, str): + table_name = table_name.decode(dialect.encoding) + + found_table = False + cols_by_orig_name = {} + + for col_d in self.get_columns( + table_name, schema, **table.dialect_kwargs): + found_table = True + + self._reflect_column( + table, col_d, include_columns, + exclude_columns, cols_by_orig_name) + + if not found_table: + raise exc.NoSuchTableError(table.name) + + self._reflect_pk( + table_name, schema, table, cols_by_orig_name, exclude_columns) + + self._reflect_fk( + table_name, schema, table, cols_by_orig_name, + exclude_columns, _extend_on, reflection_options) + + self._reflect_indexes( + table_name, schema, table, cols_by_orig_name, + include_columns, exclude_columns, reflection_options) + + self._reflect_unique_constraints( + table_name, schema, table, cols_by_orig_name, + include_columns, exclude_columns, reflection_options) + + self._reflect_check_constraints( + table_name, schema, table, cols_by_orig_name, + include_columns, exclude_columns, reflection_options) + + self._reflect_table_comment( + table_name, schema, table, reflection_options + ) + + def _reflect_column( + self, table, col_d, include_columns, + exclude_columns, cols_by_orig_name): + + orig_name = col_d['name'] + + table.dispatch.column_reflect(self, table, col_d) + + # fetch name again as column_reflect is allowed to + # change it + name = col_d['name'] + if (include_columns and name not in include_columns) \ + or (exclude_columns and name in exclude_columns): + return + + coltype = col_d['type'] + + col_kw = dict( + (k, col_d[k]) + for k in ['nullable', 'autoincrement', 'quote', 'info', 'key', 'comment'] + if k in col_d + ) + + colargs = [] + if col_d.get('default') is not None: + default = col_d['default'] + if isinstance(default, sql.elements.TextClause): + default = sa_schema.DefaultClause(default, _reflected=True) + elif not isinstance(default, sa_schema.FetchedValue): + default = sa_schema.DefaultClause( + sql.text(col_d['default']), _reflected=True) + + colargs.append(default) + + if 'sequence' in col_d: + self._reflect_col_sequence(col_d, colargs) + + cols_by_orig_name[orig_name] = col = \ + sa_schema.Column(name, coltype, *colargs, **col_kw) + + if col.key in table.primary_key: + col.primary_key = True + table.append_column(col) + + def _reflect_col_sequence(self, col_d, colargs): + if 'sequence' in col_d: + # TODO: mssql and sybase are using this. + seq = col_d['sequence'] + sequence = sa_schema.Sequence(seq['name'], 1, 1) + if 'start' in seq: + sequence.start = seq['start'] + if 'increment' in seq: + sequence.increment = seq['increment'] + colargs.append(sequence) + + def _reflect_pk( + self, table_name, schema, table, + cols_by_orig_name, exclude_columns): + pk_cons = self.get_pk_constraint( + table_name, schema, **table.dialect_kwargs) + if pk_cons: + pk_cols = [ + cols_by_orig_name[pk] + for pk in pk_cons['constrained_columns'] + if pk in cols_by_orig_name and pk not in exclude_columns + ] + + # update pk constraint name + table.primary_key.name = pk_cons.get('name') + + # tell the PKConstraint to re-initialize + # its column collection + table.primary_key._reload(pk_cols) + + def _reflect_fk( + self, table_name, schema, table, cols_by_orig_name, + exclude_columns, _extend_on, reflection_options): + fkeys = self.get_foreign_keys( + table_name, schema, **table.dialect_kwargs) + for fkey_d in fkeys: + conname = fkey_d['name'] + # look for columns by orig name in cols_by_orig_name, + # but support columns that are in-Python only as fallback + constrained_columns = [ + cols_by_orig_name[c].key + if c in cols_by_orig_name else c + for c in fkey_d['constrained_columns'] + ] + if exclude_columns and set(constrained_columns).intersection( + exclude_columns): + continue + referred_schema = fkey_d['referred_schema'] + referred_table = fkey_d['referred_table'] + referred_columns = fkey_d['referred_columns'] + refspec = [] + if referred_schema is not None: + sa_schema.Table(referred_table, table.metadata, + autoload=True, schema=referred_schema, + autoload_with=self.bind, + _extend_on=_extend_on, + **reflection_options + ) + for column in referred_columns: + refspec.append(".".join( + [referred_schema, referred_table, column])) + else: + sa_schema.Table(referred_table, table.metadata, autoload=True, + autoload_with=self.bind, + schema=sa_schema.BLANK_SCHEMA, + _extend_on=_extend_on, + **reflection_options + ) + for column in referred_columns: + refspec.append(".".join([referred_table, column])) + if 'options' in fkey_d: + options = fkey_d['options'] + else: + options = {} + table.append_constraint( + sa_schema.ForeignKeyConstraint(constrained_columns, refspec, + conname, link_to_name=True, + **options)) + + def _reflect_indexes( + self, table_name, schema, table, cols_by_orig_name, + include_columns, exclude_columns, reflection_options): + # Indexes + indexes = self.get_indexes(table_name, schema) + for index_d in indexes: + name = index_d['name'] + columns = index_d['column_names'] + unique = index_d['unique'] + flavor = index_d.get('type', 'index') + dialect_options = index_d.get('dialect_options', {}) + + duplicates = index_d.get('duplicates_constraint') + if include_columns and \ + not set(columns).issubset(include_columns): + util.warn( + "Omitting %s key for (%s), key covers omitted columns." % + (flavor, ', '.join(columns))) + continue + if duplicates: + continue + # look for columns by orig name in cols_by_orig_name, + # but support columns that are in-Python only as fallback + idx_cols = [] + for c in columns: + try: + idx_col = cols_by_orig_name[c] \ + if c in cols_by_orig_name else table.c[c] + except KeyError: + util.warn( + "%s key '%s' was not located in " + "columns for table '%s'" % ( + flavor, c, table_name + )) + else: + idx_cols.append(idx_col) + + sa_schema.Index( + name, *idx_cols, + _table=table, + **dict(list(dialect_options.items()) + [('unique', unique)]) + ) + + def _reflect_unique_constraints( + self, table_name, schema, table, cols_by_orig_name, + include_columns, exclude_columns, reflection_options): + + # Unique Constraints + try: + constraints = self.get_unique_constraints(table_name, schema) + except NotImplementedError: + # optional dialect feature + return + + for const_d in constraints: + conname = const_d['name'] + columns = const_d['column_names'] + duplicates = const_d.get('duplicates_index') + if include_columns and \ + not set(columns).issubset(include_columns): + util.warn( + "Omitting unique constraint key for (%s), " + "key covers omitted columns." % + ', '.join(columns)) + continue + if duplicates: + continue + # look for columns by orig name in cols_by_orig_name, + # but support columns that are in-Python only as fallback + constrained_cols = [] + for c in columns: + try: + constrained_col = cols_by_orig_name[c] \ + if c in cols_by_orig_name else table.c[c] + except KeyError: + util.warn( + "unique constraint key '%s' was not located in " + "columns for table '%s'" % (c, table_name)) + else: + constrained_cols.append(constrained_col) + table.append_constraint( + sa_schema.UniqueConstraint(*constrained_cols, name=conname)) + + def _reflect_check_constraints( + self, table_name, schema, table, cols_by_orig_name, + include_columns, exclude_columns, reflection_options): + try: + constraints = self.get_check_constraints(table_name, schema) + except NotImplementedError: + # optional dialect feature + return + + for const_d in constraints: + table.append_constraint( + sa_schema.CheckConstraint(**const_d)) + + def _reflect_table_comment( + self, table_name, schema, table, reflection_options): + try: + comment_dict = self.get_table_comment(table_name, schema) + except NotImplementedError: + return + else: + table.comment = comment_dict.get('text', None) diff --git a/venv/Lib/site-packages/sqlalchemy/engine/result.py b/venv/Lib/site-packages/sqlalchemy/engine/result.py new file mode 100644 index 0000000..d4c8623 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/engine/result.py @@ -0,0 +1,1448 @@ +# engine/result.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Define result set constructs including :class:`.ResultProxy` +and :class:`.RowProxy.""" + + +from .. import exc, util +from ..sql import expression, sqltypes, util as sql_util +import collections +import operator + +# This reconstructor is necessary so that pickles with the C extension or +# without use the same Binary format. +try: + # We need a different reconstructor on the C extension so that we can + # add extra checks that fields have correctly been initialized by + # __setstate__. + from sqlalchemy.cresultproxy import safe_rowproxy_reconstructor + + # The extra function embedding is needed so that the + # reconstructor function has the same signature whether or not + # the extension is present. + def rowproxy_reconstructor(cls, state): + return safe_rowproxy_reconstructor(cls, state) +except ImportError: + def rowproxy_reconstructor(cls, state): + obj = cls.__new__(cls) + obj.__setstate__(state) + return obj + +try: + from sqlalchemy.cresultproxy import BaseRowProxy + _baserowproxy_usecext = True +except ImportError: + _baserowproxy_usecext = False + + class BaseRowProxy(object): + __slots__ = ('_parent', '_row', '_processors', '_keymap') + + def __init__(self, parent, row, processors, keymap): + """RowProxy objects are constructed by ResultProxy objects.""" + + self._parent = parent + self._row = row + self._processors = processors + self._keymap = keymap + + def __reduce__(self): + return (rowproxy_reconstructor, + (self.__class__, self.__getstate__())) + + def values(self): + """Return the values represented by this RowProxy as a list.""" + return list(self) + + def __iter__(self): + for processor, value in zip(self._processors, self._row): + if processor is None: + yield value + else: + yield processor(value) + + def __len__(self): + return len(self._row) + + def __getitem__(self, key): + try: + processor, obj, index = self._keymap[key] + except KeyError: + processor, obj, index = self._parent._key_fallback(key) + except TypeError: + if isinstance(key, slice): + l = [] + for processor, value in zip(self._processors[key], + self._row[key]): + if processor is None: + l.append(value) + else: + l.append(processor(value)) + return tuple(l) + else: + raise + if index is None: + raise exc.InvalidRequestError( + "Ambiguous column name '%s' in " + "result set column descriptions" % obj) + if processor is not None: + return processor(self._row[index]) + else: + return self._row[index] + + def __getattr__(self, name): + try: + return self[name] + except KeyError as e: + raise AttributeError(e.args[0]) + + +class RowProxy(BaseRowProxy): + """Proxy values from a single cursor row. + + Mostly follows "ordered dictionary" behavior, mapping result + values to the string-based column name, the integer position of + the result in the row, as well as Column instances which can be + mapped to the original Columns that produced this result set (for + results that correspond to constructed SQL expressions). + """ + __slots__ = () + + def __contains__(self, key): + return self._parent._has_key(key) + + def __getstate__(self): + return { + '_parent': self._parent, + '_row': tuple(self) + } + + def __setstate__(self, state): + self._parent = parent = state['_parent'] + self._row = state['_row'] + self._processors = parent._processors + self._keymap = parent._keymap + + __hash__ = None + + def _op(self, other, op): + return op(tuple(self), tuple(other)) \ + if isinstance(other, RowProxy) \ + else op(tuple(self), other) + + def __lt__(self, other): + return self._op(other, operator.lt) + + def __le__(self, other): + return self._op(other, operator.le) + + def __ge__(self, other): + return self._op(other, operator.ge) + + def __gt__(self, other): + return self._op(other, operator.gt) + + def __eq__(self, other): + return self._op(other, operator.eq) + + def __ne__(self, other): + return self._op(other, operator.ne) + + def __repr__(self): + return repr(sql_util._repr_row(self)) + + def has_key(self, key): + """Return True if this RowProxy contains the given key.""" + + return self._parent._has_key(key) + + def items(self): + """Return a list of tuples, each tuple containing a key/value pair.""" + # TODO: no coverage here + return [(key, self[key]) for key in self.keys()] + + def keys(self): + """Return the list of keys as strings represented by this RowProxy.""" + + return self._parent.keys + + def iterkeys(self): + return iter(self._parent.keys) + + def itervalues(self): + return iter(self) + +try: + # Register RowProxy with Sequence, + # so sequence protocol is implemented + util.collections_abc.Sequence.register(RowProxy) +except ImportError: + pass + + +class ResultMetaData(object): + """Handle cursor.description, applying additional info from an execution + context.""" + + __slots__ = ( + '_keymap', 'case_sensitive', 'matched_on_name', + '_processors', 'keys', '_orig_processors') + + def __init__(self, parent, cursor_description): + context = parent.context + dialect = context.dialect + self.case_sensitive = dialect.case_sensitive + self.matched_on_name = False + self._orig_processors = None + + if context.result_column_struct: + result_columns, cols_are_ordered, textual_ordered = \ + context.result_column_struct + num_ctx_cols = len(result_columns) + else: + result_columns = cols_are_ordered = \ + num_ctx_cols = textual_ordered = False + + # merge cursor.description with the column info + # present in the compiled structure, if any + raw = self._merge_cursor_description( + context, cursor_description, result_columns, + num_ctx_cols, cols_are_ordered, textual_ordered) + + self._keymap = {} + if not _baserowproxy_usecext: + # keymap indexes by integer index: this is only used + # in the pure Python BaseRowProxy.__getitem__ + # implementation to avoid an expensive + # isinstance(key, util.int_types) in the most common + # case path + + len_raw = len(raw) + + self._keymap.update([ + (elem[0], (elem[3], elem[4], elem[0])) + for elem in raw + ] + [ + (elem[0] - len_raw, (elem[3], elem[4], elem[0])) + for elem in raw + ]) + + # processors in key order for certain per-row + # views like __iter__ and slices + self._processors = [elem[3] for elem in raw] + + # keymap by primary string... + by_key = dict([ + (elem[2], (elem[3], elem[4], elem[0])) + for elem in raw + ]) + + # for compiled SQL constructs, copy additional lookup keys into + # the key lookup map, such as Column objects, labels, + # column keys and other names + if num_ctx_cols: + + # if by-primary-string dictionary smaller (or bigger?!) than + # number of columns, assume we have dupes, rewrite + # dupe records with "None" for index which results in + # ambiguous column exception when accessed. + if len(by_key) != num_ctx_cols: + seen = set() + for rec in raw: + key = rec[1] + if key in seen: + # this is an "ambiguous" element, replacing + # the full record in the map + key = key.lower() if not self.case_sensitive else key + by_key[key] = (None, key, None) + seen.add(key) + + # copy secondary elements from compiled columns + # into self._keymap, write in the potentially "ambiguous" + # element + self._keymap.update([ + (obj_elem, by_key[elem[2]]) + for elem in raw if elem[4] + for obj_elem in elem[4] + ]) + + # if we did a pure positional match, then reset the + # original "expression element" back to the "unambiguous" + # entry. This is a new behavior in 1.1 which impacts + # TextAsFrom but also straight compiled SQL constructs. + if not self.matched_on_name: + self._keymap.update([ + (elem[4][0], (elem[3], elem[4], elem[0])) + for elem in raw if elem[4] + ]) + else: + # no dupes - copy secondary elements from compiled + # columns into self._keymap + self._keymap.update([ + (obj_elem, (elem[3], elem[4], elem[0])) + for elem in raw if elem[4] + for obj_elem in elem[4] + ]) + + # update keymap with primary string names taking + # precedence + self._keymap.update(by_key) + + # update keymap with "translated" names (sqlite-only thing) + if not num_ctx_cols and context._translate_colname: + self._keymap.update([ + (elem[5], self._keymap[elem[2]]) + for elem in raw if elem[5] + ]) + + def _merge_cursor_description( + self, context, cursor_description, result_columns, + num_ctx_cols, cols_are_ordered, textual_ordered): + """Merge a cursor.description with compiled result column information. + + There are at least four separate strategies used here, selected + depending on the type of SQL construct used to start with. + + The most common case is that of the compiled SQL expression construct, + which generated the column names present in the raw SQL string and + which has the identical number of columns as were reported by + cursor.description. In this case, we assume a 1-1 positional mapping + between the entries in cursor.description and the compiled object. + This is also the most performant case as we disregard extracting / + decoding the column names present in cursor.description since we + already have the desired name we generated in the compiled SQL + construct. + + The next common case is that of the completely raw string SQL, + such as passed to connection.execute(). In this case we have no + compiled construct to work with, so we extract and decode the + names from cursor.description and index those as the primary + result row target keys. + + The remaining fairly common case is that of the textual SQL + that includes at least partial column information; this is when + we use a :class:`.TextAsFrom` construct. This contruct may have + unordered or ordered column information. In the ordered case, we + merge the cursor.description and the compiled construct's information + positionally, and warn if there are additional description names + present, however we still decode the names in cursor.description + as we don't have a guarantee that the names in the columns match + on these. In the unordered case, we match names in cursor.description + to that of the compiled construct based on name matching. + In both of these cases, the cursor.description names and the column + expression objects and names are indexed as result row target keys. + + The final case is much less common, where we have a compiled + non-textual SQL expression construct, but the number of columns + in cursor.description doesn't match what's in the compiled + construct. We make the guess here that there might be textual + column expressions in the compiled construct that themselves include + a comma in them causing them to split. We do the same name-matching + as with textual non-ordered columns. + + The name-matched system of merging is the same as that used by + SQLAlchemy for all cases up through te 0.9 series. Positional + matching for compiled SQL expressions was introduced in 1.0 as a + major performance feature, and positional matching for textual + :class:`.TextAsFrom` objects in 1.1. As name matching is no longer + a common case, it was acceptable to factor it into smaller generator- + oriented methods that are easier to understand, but incur slightly + more performance overhead. + + """ + + case_sensitive = context.dialect.case_sensitive + + if num_ctx_cols and \ + cols_are_ordered and \ + not textual_ordered and \ + num_ctx_cols == len(cursor_description): + self.keys = [elem[0] for elem in result_columns] + # pure positional 1-1 case; doesn't need to read + # the names from cursor.description + return [ + ( + idx, + key, + name.lower() if not case_sensitive else name, + context.get_result_processor( + type_, key, cursor_description[idx][1] + ), + obj, + None + ) for idx, (key, name, obj, type_) + in enumerate(result_columns) + ] + else: + # name-based or text-positional cases, where we need + # to read cursor.description names + if textual_ordered: + # textual positional case + raw_iterator = self._merge_textual_cols_by_position( + context, cursor_description, result_columns) + elif num_ctx_cols: + # compiled SQL with a mismatch of description cols + # vs. compiled cols, or textual w/ unordered columns + raw_iterator = self._merge_cols_by_name( + context, cursor_description, result_columns) + else: + # no compiled SQL, just a raw string + raw_iterator = self._merge_cols_by_none( + context, cursor_description) + + return [ + ( + idx, colname, colname, + context.get_result_processor( + mapped_type, colname, coltype), + obj, untranslated) + + for idx, colname, mapped_type, coltype, obj, untranslated + in raw_iterator + ] + + def _colnames_from_description(self, context, cursor_description): + """Extract column names and data types from a cursor.description. + + Applies unicode decoding, column translation, "normalization", + and case sensitivity rules to the names based on the dialect. + + """ + + dialect = context.dialect + case_sensitive = dialect.case_sensitive + translate_colname = context._translate_colname + description_decoder = dialect._description_decoder \ + if dialect.description_encoding else None + normalize_name = dialect.normalize_name \ + if dialect.requires_name_normalize else None + untranslated = None + + self.keys = [] + + for idx, rec in enumerate(cursor_description): + colname = rec[0] + coltype = rec[1] + + if description_decoder: + colname = description_decoder(colname) + + if translate_colname: + colname, untranslated = translate_colname(colname) + + if normalize_name: + colname = normalize_name(colname) + + self.keys.append(colname) + if not case_sensitive: + colname = colname.lower() + + yield idx, colname, untranslated, coltype + + def _merge_textual_cols_by_position( + self, context, cursor_description, result_columns): + dialect = context.dialect + num_ctx_cols = len(result_columns) if result_columns else None + + if num_ctx_cols > len(cursor_description): + util.warn( + "Number of columns in textual SQL (%d) is " + "smaller than number of columns requested (%d)" % ( + num_ctx_cols, len(cursor_description) + )) + + seen = set() + for idx, colname, untranslated, coltype in \ + self._colnames_from_description(context, cursor_description): + if idx < num_ctx_cols: + ctx_rec = result_columns[idx] + obj = ctx_rec[2] + mapped_type = ctx_rec[3] + if obj[0] in seen: + raise exc.InvalidRequestError( + "Duplicate column expression requested " + "in textual SQL: %r" % obj[0]) + seen.add(obj[0]) + else: + mapped_type = sqltypes.NULLTYPE + obj = None + + yield idx, colname, mapped_type, coltype, obj, untranslated + + def _merge_cols_by_name(self, context, cursor_description, result_columns): + dialect = context.dialect + case_sensitive = dialect.case_sensitive + result_map = self._create_result_map(result_columns, case_sensitive) + + self.matched_on_name = True + for idx, colname, untranslated, coltype in \ + self._colnames_from_description(context, cursor_description): + try: + ctx_rec = result_map[colname] + except KeyError: + mapped_type = sqltypes.NULLTYPE + obj = None + else: + obj = ctx_rec[1] + mapped_type = ctx_rec[2] + yield idx, colname, mapped_type, coltype, obj, untranslated + + def _merge_cols_by_none(self, context, cursor_description): + dialect = context.dialect + for idx, colname, untranslated, coltype in \ + self._colnames_from_description(context, cursor_description): + yield idx, colname, sqltypes.NULLTYPE, coltype, None, untranslated + + @classmethod + def _create_result_map(cls, result_columns, case_sensitive=True): + d = {} + for elem in result_columns: + key, rec = elem[0], elem[1:] + if not case_sensitive: + key = key.lower() + if key in d: + # conflicting keyname, just double up the list + # of objects. this will cause an "ambiguous name" + # error if an attempt is made by the result set to + # access. + e_name, e_obj, e_type = d[key] + d[key] = e_name, e_obj + rec[1], e_type + else: + d[key] = rec + return d + + def _key_fallback(self, key, raiseerr=True): + map = self._keymap + result = None + if isinstance(key, util.string_types): + result = map.get(key if self.case_sensitive else key.lower()) + # fallback for targeting a ColumnElement to a textual expression + # this is a rare use case which only occurs when matching text() + # or colummn('name') constructs to ColumnElements, or after a + # pickle/unpickle roundtrip + elif isinstance(key, expression.ColumnElement): + if key._label and ( + key._label + if self.case_sensitive + else key._label.lower()) in map: + result = map[key._label + if self.case_sensitive + else key._label.lower()] + elif hasattr(key, 'name') and ( + key.name + if self.case_sensitive + else key.name.lower()) in map: + # match is only on name. + result = map[key.name + if self.case_sensitive + else key.name.lower()] + # search extra hard to make sure this + # isn't a column/label name overlap. + # this check isn't currently available if the row + # was unpickled. + if result is not None and \ + result[1] is not None: + for obj in result[1]: + if key._compare_name_for_result(obj): + break + else: + result = None + if result is None: + if raiseerr: + raise exc.NoSuchColumnError( + "Could not locate column in row for column '%s'" % + expression._string_or_unprintable(key)) + else: + return None + else: + map[key] = result + return result + + def _has_key(self, key): + if key in self._keymap: + return True + else: + return self._key_fallback(key, False) is not None + + def _getter(self, key, raiseerr=True): + if key in self._keymap: + processor, obj, index = self._keymap[key] + else: + ret = self._key_fallback(key, raiseerr) + if ret is None: + return None + processor, obj, index = ret + + if index is None: + raise exc.InvalidRequestError( + "Ambiguous column name '%s' in " + "result set column descriptions" % obj) + + return operator.itemgetter(index) + + def __getstate__(self): + return { + '_pickled_keymap': dict( + (key, index) + for key, (processor, obj, index) in self._keymap.items() + if isinstance(key, util.string_types + util.int_types) + ), + 'keys': self.keys, + "case_sensitive": self.case_sensitive, + "matched_on_name": self.matched_on_name + } + + def __setstate__(self, state): + # the row has been processed at pickling time so we don't need any + # processor anymore + self._processors = [None for _ in range(len(state['keys']))] + self._keymap = keymap = {} + for key, index in state['_pickled_keymap'].items(): + # not preserving "obj" here, unfortunately our + # proxy comparison fails with the unpickle + keymap[key] = (None, None, index) + self.keys = state['keys'] + self.case_sensitive = state['case_sensitive'] + self.matched_on_name = state['matched_on_name'] + + +class ResultProxy(object): + """Wraps a DB-API cursor object to provide easier access to row columns. + + Individual columns may be accessed by their integer position, + case-insensitive column name, or by ``schema.Column`` + object. e.g.:: + + row = fetchone() + + col1 = row[0] # access via integer position + + col2 = row['col2'] # access via name + + col3 = row[mytable.c.mycol] # access via Column object. + + ``ResultProxy`` also handles post-processing of result column + data using ``TypeEngine`` objects, which are referenced from + the originating SQL statement that produced this result set. + + """ + + _process_row = RowProxy + out_parameters = None + _autoclose_connection = False + _metadata = None + _soft_closed = False + closed = False + + def __init__(self, context): + self.context = context + self.dialect = context.dialect + self.cursor = self._saved_cursor = context.cursor + self.connection = context.root_connection + self._echo = self.connection._echo and \ + context.engine._should_log_debug() + self._init_metadata() + + def _getter(self, key, raiseerr=True): + try: + getter = self._metadata._getter + except AttributeError: + return self._non_result(None) + else: + return getter(key, raiseerr) + + def _has_key(self, key): + try: + has_key = self._metadata._has_key + except AttributeError: + return self._non_result(None) + else: + return has_key(key) + + def _init_metadata(self): + cursor_description = self._cursor_description() + if cursor_description is not None: + if self.context.compiled and \ + 'compiled_cache' in self.context.execution_options: + if self.context.compiled._cached_metadata: + self._metadata = self.context.compiled._cached_metadata + else: + self._metadata = self.context.compiled._cached_metadata = \ + ResultMetaData(self, cursor_description) + else: + self._metadata = ResultMetaData(self, cursor_description) + if self._echo: + self.context.engine.logger.debug( + "Col %r", tuple(x[0] for x in cursor_description)) + + def keys(self): + """Return the current set of string keys for rows.""" + if self._metadata: + return self._metadata.keys + else: + return [] + + @util.memoized_property + def rowcount(self): + """Return the 'rowcount' for this result. + + The 'rowcount' reports the number of rows *matched* + by the WHERE criterion of an UPDATE or DELETE statement. + + .. note:: + + Notes regarding :attr:`.ResultProxy.rowcount`: + + + * This attribute returns the number of rows *matched*, + which is not necessarily the same as the number of rows + that were actually *modified* - an UPDATE statement, for example, + may have no net change on a given row if the SET values + given are the same as those present in the row already. + Such a row would be matched but not modified. + On backends that feature both styles, such as MySQL, + rowcount is configured by default to return the match + count in all cases. + + * :attr:`.ResultProxy.rowcount` is *only* useful in conjunction + with an UPDATE or DELETE statement. Contrary to what the Python + DBAPI says, it does *not* return the + number of rows available from the results of a SELECT statement + as DBAPIs cannot support this functionality when rows are + unbuffered. + + * :attr:`.ResultProxy.rowcount` may not be fully implemented by + all dialects. In particular, most DBAPIs do not support an + aggregate rowcount result from an executemany call. + The :meth:`.ResultProxy.supports_sane_rowcount` and + :meth:`.ResultProxy.supports_sane_multi_rowcount` methods + will report from the dialect if each usage is known to be + supported. + + * Statements that use RETURNING may not return a correct + rowcount. + + """ + try: + return self.context.rowcount + except BaseException as e: + self.connection._handle_dbapi_exception( + e, None, None, self.cursor, self.context) + + @property + def lastrowid(self): + """return the 'lastrowid' accessor on the DBAPI cursor. + + This is a DBAPI specific method and is only functional + for those backends which support it, for statements + where it is appropriate. It's behavior is not + consistent across backends. + + Usage of this method is normally unnecessary when + using insert() expression constructs; the + :attr:`~ResultProxy.inserted_primary_key` attribute provides a + tuple of primary key values for a newly inserted row, + regardless of database backend. + + """ + try: + return self._saved_cursor.lastrowid + except BaseException as e: + self.connection._handle_dbapi_exception( + e, None, None, + self._saved_cursor, self.context) + + @property + def returns_rows(self): + """True if this :class:`.ResultProxy` returns rows. + + I.e. if it is legal to call the methods + :meth:`~.ResultProxy.fetchone`, + :meth:`~.ResultProxy.fetchmany` + :meth:`~.ResultProxy.fetchall`. + + """ + return self._metadata is not None + + @property + def is_insert(self): + """True if this :class:`.ResultProxy` is the result + of a executing an expression language compiled + :func:`.expression.insert` construct. + + When True, this implies that the + :attr:`inserted_primary_key` attribute is accessible, + assuming the statement did not include + a user defined "returning" construct. + + """ + return self.context.isinsert + + def _cursor_description(self): + """May be overridden by subclasses.""" + + return self._saved_cursor.description + + def _soft_close(self): + """Soft close this :class:`.ResultProxy`. + + This releases all DBAPI cursor resources, but leaves the + ResultProxy "open" from a semantic perspective, meaning the + fetchXXX() methods will continue to return empty results. + + This method is called automatically when: + + * all result rows are exhausted using the fetchXXX() methods. + * cursor.description is None. + + This method is **not public**, but is documented in order to clarify + the "autoclose" process used. + + .. versionadded:: 1.0.0 + + .. seealso:: + + :meth:`.ResultProxy.close` + + + """ + if self._soft_closed: + return + self._soft_closed = True + cursor = self.cursor + self.connection._safe_close_cursor(cursor) + if self._autoclose_connection: + self.connection.close() + self.cursor = None + + def close(self): + """Close this ResultProxy. + + This closes out the underlying DBAPI cursor corresonding + to the statement execution, if one is still present. Note that the + DBAPI cursor is automatically released when the :class:`.ResultProxy` + exhausts all available rows. :meth:`.ResultProxy.close` is generally + an optional method except in the case when discarding a + :class:`.ResultProxy` that still has additional rows pending for fetch. + + In the case of a result that is the product of + :ref:`connectionless execution <dbengine_implicit>`, + the underlying :class:`.Connection` object is also closed, which + :term:`releases` DBAPI connection resources. + + After this method is called, it is no longer valid to call upon + the fetch methods, which will raise a :class:`.ResourceClosedError` + on subsequent use. + + .. versionchanged:: 1.0.0 - the :meth:`.ResultProxy.close` method + has been separated out from the process that releases the underlying + DBAPI cursor resource. The "auto close" feature of the + :class:`.Connection` now performs a so-called "soft close", which + releases the underlying DBAPI cursor, but allows the + :class:`.ResultProxy` to still behave as an open-but-exhausted + result set; the actual :meth:`.ResultProxy.close` method is never + called. It is still safe to discard a :class:`.ResultProxy` + that has been fully exhausted without calling this method. + + .. seealso:: + + :ref:`connections_toplevel` + + :meth:`.ResultProxy._soft_close` + + """ + + if not self.closed: + self._soft_close() + self.closed = True + + def __iter__(self): + """Implement iteration protocol.""" + + while True: + row = self.fetchone() + if row is None: + return + else: + yield row + + def __next__(self): + """Implement the next() protocol. + + .. versionadded:: 1.2 + + """ + row = self.fetchone() + if row is None: + raise StopIteration() + else: + return row + + next = __next__ + + @util.memoized_property + def inserted_primary_key(self): + """Return the primary key for the row just inserted. + + The return value is a list of scalar values + corresponding to the list of primary key columns + in the target table. + + This only applies to single row :func:`.insert` + constructs which did not explicitly specify + :meth:`.Insert.returning`. + + Note that primary key columns which specify a + server_default clause, + or otherwise do not qualify as "autoincrement" + columns (see the notes at :class:`.Column`), and were + generated using the database-side default, will + appear in this list as ``None`` unless the backend + supports "returning" and the insert statement executed + with the "implicit returning" enabled. + + Raises :class:`~sqlalchemy.exc.InvalidRequestError` if the executed + statement is not a compiled expression construct + or is not an insert() construct. + + """ + + if not self.context.compiled: + raise exc.InvalidRequestError( + "Statement is not a compiled " + "expression construct.") + elif not self.context.isinsert: + raise exc.InvalidRequestError( + "Statement is not an insert() " + "expression construct.") + elif self.context._is_explicit_returning: + raise exc.InvalidRequestError( + "Can't call inserted_primary_key " + "when returning() " + "is used.") + + return self.context.inserted_primary_key + + def last_updated_params(self): + """Return the collection of updated parameters from this + execution. + + Raises :class:`~sqlalchemy.exc.InvalidRequestError` if the executed + statement is not a compiled expression construct + or is not an update() construct. + + """ + if not self.context.compiled: + raise exc.InvalidRequestError( + "Statement is not a compiled " + "expression construct.") + elif not self.context.isupdate: + raise exc.InvalidRequestError( + "Statement is not an update() " + "expression construct.") + elif self.context.executemany: + return self.context.compiled_parameters + else: + return self.context.compiled_parameters[0] + + def last_inserted_params(self): + """Return the collection of inserted parameters from this + execution. + + Raises :class:`~sqlalchemy.exc.InvalidRequestError` if the executed + statement is not a compiled expression construct + or is not an insert() construct. + + """ + if not self.context.compiled: + raise exc.InvalidRequestError( + "Statement is not a compiled " + "expression construct.") + elif not self.context.isinsert: + raise exc.InvalidRequestError( + "Statement is not an insert() " + "expression construct.") + elif self.context.executemany: + return self.context.compiled_parameters + else: + return self.context.compiled_parameters[0] + + @property + def returned_defaults(self): + """Return the values of default columns that were fetched using + the :meth:`.ValuesBase.return_defaults` feature. + + The value is an instance of :class:`.RowProxy`, or ``None`` + if :meth:`.ValuesBase.return_defaults` was not used or if the + backend does not support RETURNING. + + .. versionadded:: 0.9.0 + + .. seealso:: + + :meth:`.ValuesBase.return_defaults` + + """ + return self.context.returned_defaults + + def lastrow_has_defaults(self): + """Return ``lastrow_has_defaults()`` from the underlying + :class:`.ExecutionContext`. + + See :class:`.ExecutionContext` for details. + + """ + + return self.context.lastrow_has_defaults() + + def postfetch_cols(self): + """Return ``postfetch_cols()`` from the underlying + :class:`.ExecutionContext`. + + See :class:`.ExecutionContext` for details. + + Raises :class:`~sqlalchemy.exc.InvalidRequestError` if the executed + statement is not a compiled expression construct + or is not an insert() or update() construct. + + """ + + if not self.context.compiled: + raise exc.InvalidRequestError( + "Statement is not a compiled " + "expression construct.") + elif not self.context.isinsert and not self.context.isupdate: + raise exc.InvalidRequestError( + "Statement is not an insert() or update() " + "expression construct.") + return self.context.postfetch_cols + + def prefetch_cols(self): + """Return ``prefetch_cols()`` from the underlying + :class:`.ExecutionContext`. + + See :class:`.ExecutionContext` for details. + + Raises :class:`~sqlalchemy.exc.InvalidRequestError` if the executed + statement is not a compiled expression construct + or is not an insert() or update() construct. + + """ + + if not self.context.compiled: + raise exc.InvalidRequestError( + "Statement is not a compiled " + "expression construct.") + elif not self.context.isinsert and not self.context.isupdate: + raise exc.InvalidRequestError( + "Statement is not an insert() or update() " + "expression construct.") + return self.context.prefetch_cols + + def supports_sane_rowcount(self): + """Return ``supports_sane_rowcount`` from the dialect. + + See :attr:`.ResultProxy.rowcount` for background. + + """ + + return self.dialect.supports_sane_rowcount + + def supports_sane_multi_rowcount(self): + """Return ``supports_sane_multi_rowcount`` from the dialect. + + See :attr:`.ResultProxy.rowcount` for background. + + """ + + return self.dialect.supports_sane_multi_rowcount + + def _fetchone_impl(self): + try: + return self.cursor.fetchone() + except AttributeError: + return self._non_result(None) + + def _fetchmany_impl(self, size=None): + try: + if size is None: + return self.cursor.fetchmany() + else: + return self.cursor.fetchmany(size) + except AttributeError: + return self._non_result([]) + + def _fetchall_impl(self): + try: + return self.cursor.fetchall() + except AttributeError: + return self._non_result([]) + + def _non_result(self, default): + if self._metadata is None: + raise exc.ResourceClosedError( + "This result object does not return rows. " + "It has been closed automatically.", + ) + elif self.closed: + raise exc.ResourceClosedError("This result object is closed.") + else: + return default + + def process_rows(self, rows): + process_row = self._process_row + metadata = self._metadata + keymap = metadata._keymap + processors = metadata._processors + if self._echo: + log = self.context.engine.logger.debug + l = [] + for row in rows: + log("Row %r", sql_util._repr_row(row)) + l.append(process_row(metadata, row, processors, keymap)) + return l + else: + return [process_row(metadata, row, processors, keymap) + for row in rows] + + def fetchall(self): + """Fetch all rows, just like DB-API ``cursor.fetchall()``. + + After all rows have been exhausted, the underlying DBAPI + cursor resource is released, and the object may be safely + discarded. + + Subsequent calls to :meth:`.ResultProxy.fetchall` will return + an empty list. After the :meth:`.ResultProxy.close` method is + called, the method will raise :class:`.ResourceClosedError`. + + .. versionchanged:: 1.0.0 - Added "soft close" behavior which + allows the result to be used in an "exhausted" state prior to + calling the :meth:`.ResultProxy.close` method. + + """ + + try: + l = self.process_rows(self._fetchall_impl()) + self._soft_close() + return l + except BaseException as e: + self.connection._handle_dbapi_exception( + e, None, None, + self.cursor, self.context) + + def fetchmany(self, size=None): + """Fetch many rows, just like DB-API + ``cursor.fetchmany(size=cursor.arraysize)``. + + After all rows have been exhausted, the underlying DBAPI + cursor resource is released, and the object may be safely + discarded. + + Calls to :meth:`.ResultProxy.fetchmany` after all rows have been + exhausted will return + an empty list. After the :meth:`.ResultProxy.close` method is + called, the method will raise :class:`.ResourceClosedError`. + + .. versionchanged:: 1.0.0 - Added "soft close" behavior which + allows the result to be used in an "exhausted" state prior to + calling the :meth:`.ResultProxy.close` method. + + """ + + try: + l = self.process_rows(self._fetchmany_impl(size)) + if len(l) == 0: + self._soft_close() + return l + except BaseException as e: + self.connection._handle_dbapi_exception( + e, None, None, + self.cursor, self.context) + + def fetchone(self): + """Fetch one row, just like DB-API ``cursor.fetchone()``. + + After all rows have been exhausted, the underlying DBAPI + cursor resource is released, and the object may be safely + discarded. + + Calls to :meth:`.ResultProxy.fetchone` after all rows have + been exhausted will return ``None``. + After the :meth:`.ResultProxy.close` method is + called, the method will raise :class:`.ResourceClosedError`. + + .. versionchanged:: 1.0.0 - Added "soft close" behavior which + allows the result to be used in an "exhausted" state prior to + calling the :meth:`.ResultProxy.close` method. + + """ + try: + row = self._fetchone_impl() + if row is not None: + return self.process_rows([row])[0] + else: + self._soft_close() + return None + except BaseException as e: + self.connection._handle_dbapi_exception( + e, None, None, + self.cursor, self.context) + + def first(self): + """Fetch the first row and then close the result set unconditionally. + + Returns None if no row is present. + + After calling this method, the object is fully closed, + e.g. the :meth:`.ResultProxy.close` method will have been called. + + """ + if self._metadata is None: + return self._non_result(None) + + try: + row = self._fetchone_impl() + except BaseException as e: + self.connection._handle_dbapi_exception( + e, None, None, + self.cursor, self.context) + + try: + if row is not None: + return self.process_rows([row])[0] + else: + return None + finally: + self.close() + + def scalar(self): + """Fetch the first column of the first row, and close the result set. + + Returns None if no row is present. + + After calling this method, the object is fully closed, + e.g. the :meth:`.ResultProxy.close` method will have been called. + + """ + row = self.first() + if row is not None: + return row[0] + else: + return None + + +class BufferedRowResultProxy(ResultProxy): + """A ResultProxy with row buffering behavior. + + ``ResultProxy`` that buffers the contents of a selection of rows + before ``fetchone()`` is called. This is to allow the results of + ``cursor.description`` to be available immediately, when + interfacing with a DB-API that requires rows to be consumed before + this information is available (currently psycopg2, when used with + server-side cursors). + + The pre-fetching behavior fetches only one row initially, and then + grows its buffer size by a fixed amount with each successive need + for additional rows up to a size of 1000. + + The size argument is configurable using the ``max_row_buffer`` + execution option:: + + with psycopg2_engine.connect() as conn: + + result = conn.execution_options( + stream_results=True, max_row_buffer=50 + ).execute("select * from table") + + .. versionadded:: 1.0.6 Added the ``max_row_buffer`` option. + + .. seealso:: + + :ref:`psycopg2_execution_options` + """ + + def _init_metadata(self): + self._max_row_buffer = self.context.execution_options.get( + 'max_row_buffer', None) + self.__buffer_rows() + super(BufferedRowResultProxy, self)._init_metadata() + + # this is a "growth chart" for the buffering of rows. + # each successive __buffer_rows call will use the next + # value in the list for the buffer size until the max + # is reached + size_growth = { + 1: 5, + 5: 10, + 10: 20, + 20: 50, + 50: 100, + 100: 250, + 250: 500, + 500: 1000 + } + + def __buffer_rows(self): + if self.cursor is None: + return + size = getattr(self, '_bufsize', 1) + self.__rowbuffer = collections.deque(self.cursor.fetchmany(size)) + self._bufsize = self.size_growth.get(size, size) + if self._max_row_buffer is not None: + self._bufsize = min(self._max_row_buffer, self._bufsize) + + def _soft_close(self, **kw): + self.__rowbuffer.clear() + super(BufferedRowResultProxy, self)._soft_close(**kw) + + def _fetchone_impl(self): + if self.cursor is None: + return self._non_result(None) + if not self.__rowbuffer: + self.__buffer_rows() + if not self.__rowbuffer: + return None + return self.__rowbuffer.popleft() + + def _fetchmany_impl(self, size=None): + if size is None: + return self._fetchall_impl() + result = [] + for x in range(0, size): + row = self._fetchone_impl() + if row is None: + break + result.append(row) + return result + + def _fetchall_impl(self): + if self.cursor is None: + return self._non_result([]) + self.__rowbuffer.extend(self.cursor.fetchall()) + ret = self.__rowbuffer + self.__rowbuffer = collections.deque() + return ret + + +class FullyBufferedResultProxy(ResultProxy): + """A result proxy that buffers rows fully upon creation. + + Used for operations where a result is to be delivered + after the database conversation can not be continued, + such as MSSQL INSERT...OUTPUT after an autocommit. + + """ + + def _init_metadata(self): + super(FullyBufferedResultProxy, self)._init_metadata() + self.__rowbuffer = self._buffer_rows() + + def _buffer_rows(self): + return collections.deque(self.cursor.fetchall()) + + def _soft_close(self, **kw): + self.__rowbuffer.clear() + super(FullyBufferedResultProxy, self)._soft_close(**kw) + + def _fetchone_impl(self): + if self.__rowbuffer: + return self.__rowbuffer.popleft() + else: + return self._non_result(None) + + def _fetchmany_impl(self, size=None): + if size is None: + return self._fetchall_impl() + result = [] + for x in range(0, size): + row = self._fetchone_impl() + if row is None: + break + result.append(row) + return result + + def _fetchall_impl(self): + if not self.cursor: + return self._non_result([]) + ret = self.__rowbuffer + self.__rowbuffer = collections.deque() + return ret + + +class BufferedColumnRow(RowProxy): + def __init__(self, parent, row, processors, keymap): + # preprocess row + row = list(row) + # this is a tad faster than using enumerate + index = 0 + for processor in parent._orig_processors: + if processor is not None: + row[index] = processor(row[index]) + index += 1 + row = tuple(row) + super(BufferedColumnRow, self).__init__(parent, row, + processors, keymap) + + +class BufferedColumnResultProxy(ResultProxy): + """A ResultProxy with column buffering behavior. + + ``ResultProxy`` that loads all columns into memory each time + fetchone() is called. If fetchmany() or fetchall() are called, + the full grid of results is fetched. This is to operate with + databases where result rows contain "live" results that fall out + of scope unless explicitly fetched. + + .. versionchanged:: 1.2 This :class:`.ResultProxy` is not used by + any SQLAlchemy-included dialects. + + """ + + _process_row = BufferedColumnRow + + def _init_metadata(self): + super(BufferedColumnResultProxy, self)._init_metadata() + + metadata = self._metadata + + # don't double-replace the processors, in the case + # of a cached ResultMetaData + if metadata._orig_processors is None: + # orig_processors will be used to preprocess each row when + # they are constructed. + metadata._orig_processors = metadata._processors + # replace the all type processors by None processors. + metadata._processors = [None for _ in range(len(metadata.keys))] + keymap = {} + for k, (func, obj, index) in metadata._keymap.items(): + keymap[k] = (None, obj, index) + metadata._keymap = keymap + + def fetchall(self): + # can't call cursor.fetchall(), since rows must be + # fully processed before requesting more from the DBAPI. + l = [] + while True: + row = self.fetchone() + if row is None: + break + l.append(row) + return l + + def fetchmany(self, size=None): + # can't call cursor.fetchmany(), since rows must be + # fully processed before requesting more from the DBAPI. + if size is None: + return self.fetchall() + l = [] + for i in range(size): + row = self.fetchone() + if row is None: + break + l.append(row) + return l diff --git a/venv/Lib/site-packages/sqlalchemy/engine/strategies.py b/venv/Lib/site-packages/sqlalchemy/engine/strategies.py new file mode 100644 index 0000000..4b6ee77 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/engine/strategies.py @@ -0,0 +1,285 @@ +# engine/strategies.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Strategies for creating new instances of Engine types. + +These are semi-private implementation classes which provide the +underlying behavior for the "strategy" keyword argument available on +:func:`~sqlalchemy.engine.create_engine`. Current available options are +``plain``, ``threadlocal``, and ``mock``. + +New strategies can be added via new ``EngineStrategy`` classes. +""" + +from operator import attrgetter + +from sqlalchemy.engine import base, threadlocal, url +from sqlalchemy import util, event +from sqlalchemy import pool as poollib +from sqlalchemy.sql import schema + +strategies = {} + + +class EngineStrategy(object): + """An adaptor that processes input arguments and produces an Engine. + + Provides a ``create`` method that receives input arguments and + produces an instance of base.Engine or a subclass. + + """ + + def __init__(self): + strategies[self.name] = self + + def create(self, *args, **kwargs): + """Given arguments, returns a new Engine instance.""" + + raise NotImplementedError() + + +class DefaultEngineStrategy(EngineStrategy): + """Base class for built-in strategies.""" + + def create(self, name_or_url, **kwargs): + # create url.URL object + u = url.make_url(name_or_url) + + plugins = u._instantiate_plugins(kwargs) + + u.query.pop('plugin', None) + kwargs.pop('plugins', None) + + entrypoint = u._get_entrypoint() + dialect_cls = entrypoint.get_dialect_cls(u) + + if kwargs.pop('_coerce_config', False): + def pop_kwarg(key, default=None): + value = kwargs.pop(key, default) + if key in dialect_cls.engine_config_types: + value = dialect_cls.engine_config_types[key](value) + return value + else: + pop_kwarg = kwargs.pop + + dialect_args = {} + # consume dialect arguments from kwargs + for k in util.get_cls_kwargs(dialect_cls): + if k in kwargs: + dialect_args[k] = pop_kwarg(k) + + dbapi = kwargs.pop('module', None) + if dbapi is None: + dbapi_args = {} + for k in util.get_func_kwargs(dialect_cls.dbapi): + if k in kwargs: + dbapi_args[k] = pop_kwarg(k) + dbapi = dialect_cls.dbapi(**dbapi_args) + + dialect_args['dbapi'] = dbapi + + for plugin in plugins: + plugin.handle_dialect_kwargs(dialect_cls, dialect_args) + + # create dialect + dialect = dialect_cls(**dialect_args) + + # assemble connection arguments + (cargs, cparams) = dialect.create_connect_args(u) + cparams.update(pop_kwarg('connect_args', {})) + cargs = list(cargs) # allow mutability + + # look for existing pool or create + pool = pop_kwarg('pool', None) + if pool is None: + def connect(connection_record=None): + if dialect._has_events: + for fn in dialect.dispatch.do_connect: + connection = fn( + dialect, connection_record, cargs, cparams) + if connection is not None: + return connection + return dialect.connect(*cargs, **cparams) + + creator = pop_kwarg('creator', connect) + + poolclass = pop_kwarg('poolclass', None) + if poolclass is None: + poolclass = dialect_cls.get_pool_class(u) + pool_args = { + 'dialect': dialect + } + + # consume pool arguments from kwargs, translating a few of + # the arguments + translate = {'logging_name': 'pool_logging_name', + 'echo': 'echo_pool', + 'timeout': 'pool_timeout', + 'recycle': 'pool_recycle', + 'events': 'pool_events', + 'use_threadlocal': 'pool_threadlocal', + 'reset_on_return': 'pool_reset_on_return', + 'pre_ping': 'pool_pre_ping'} + for k in util.get_cls_kwargs(poolclass): + tk = translate.get(k, k) + if tk in kwargs: + pool_args[k] = pop_kwarg(tk) + + for plugin in plugins: + plugin.handle_pool_kwargs(poolclass, pool_args) + + pool = poolclass(creator, **pool_args) + else: + if isinstance(pool, poollib._DBProxy): + pool = pool.get_pool(*cargs, **cparams) + else: + pool = pool + + pool._dialect = dialect + + # create engine. + engineclass = self.engine_cls + engine_args = {} + for k in util.get_cls_kwargs(engineclass): + if k in kwargs: + engine_args[k] = pop_kwarg(k) + + _initialize = kwargs.pop('_initialize', True) + + # all kwargs should be consumed + if kwargs: + raise TypeError( + "Invalid argument(s) %s sent to create_engine(), " + "using configuration %s/%s/%s. Please check that the " + "keyword arguments are appropriate for this combination " + "of components." % (','.join("'%s'" % k for k in kwargs), + dialect.__class__.__name__, + pool.__class__.__name__, + engineclass.__name__)) + + engine = engineclass(pool, dialect, u, **engine_args) + + if _initialize: + do_on_connect = dialect.on_connect() + if do_on_connect: + def on_connect(dbapi_connection, connection_record): + conn = getattr( + dbapi_connection, '_sqla_unwrap', dbapi_connection) + if conn is None: + return + do_on_connect(conn) + + event.listen(pool, 'first_connect', on_connect) + event.listen(pool, 'connect', on_connect) + + def first_connect(dbapi_connection, connection_record): + c = base.Connection(engine, connection=dbapi_connection, + _has_events=False) + c._execution_options = util.immutabledict() + dialect.initialize(c) + event.listen(pool, 'first_connect', first_connect, once=True) + + dialect_cls.engine_created(engine) + if entrypoint is not dialect_cls: + entrypoint.engine_created(engine) + + for plugin in plugins: + plugin.engine_created(engine) + + return engine + + +class PlainEngineStrategy(DefaultEngineStrategy): + """Strategy for configuring a regular Engine.""" + + name = 'plain' + engine_cls = base.Engine + +PlainEngineStrategy() + + +class ThreadLocalEngineStrategy(DefaultEngineStrategy): + """Strategy for configuring an Engine with threadlocal behavior.""" + + name = 'threadlocal' + engine_cls = threadlocal.TLEngine + +ThreadLocalEngineStrategy() + + +class MockEngineStrategy(EngineStrategy): + """Strategy for configuring an Engine-like object with mocked execution. + + Produces a single mock Connectable object which dispatches + statement execution to a passed-in function. + + """ + + name = 'mock' + + def create(self, name_or_url, executor, **kwargs): + # create url.URL object + u = url.make_url(name_or_url) + + dialect_cls = u.get_dialect() + + dialect_args = {} + # consume dialect arguments from kwargs + for k in util.get_cls_kwargs(dialect_cls): + if k in kwargs: + dialect_args[k] = kwargs.pop(k) + + # create dialect + dialect = dialect_cls(**dialect_args) + + return MockEngineStrategy.MockConnection(dialect, executor) + + class MockConnection(base.Connectable): + def __init__(self, dialect, execute): + self._dialect = dialect + self.execute = execute + + engine = property(lambda s: s) + dialect = property(attrgetter('_dialect')) + name = property(lambda s: s._dialect.name) + + schema_for_object = schema._schema_getter(None) + + def contextual_connect(self, **kwargs): + return self + + def execution_options(self, **kw): + return self + + def compiler(self, statement, parameters, **kwargs): + return self._dialect.compiler( + statement, parameters, engine=self, **kwargs) + + def create(self, entity, **kwargs): + kwargs['checkfirst'] = False + from sqlalchemy.engine import ddl + + ddl.SchemaGenerator( + self.dialect, self, **kwargs).traverse_single(entity) + + def drop(self, entity, **kwargs): + kwargs['checkfirst'] = False + from sqlalchemy.engine import ddl + ddl.SchemaDropper( + self.dialect, self, **kwargs).traverse_single(entity) + + def _run_visitor(self, visitorcallable, element, + connection=None, + **kwargs): + kwargs['checkfirst'] = False + visitorcallable(self.dialect, self, + **kwargs).traverse_single(element) + + def execute(self, object, *multiparams, **params): + raise NotImplementedError() + +MockEngineStrategy() diff --git a/venv/Lib/site-packages/sqlalchemy/engine/threadlocal.py b/venv/Lib/site-packages/sqlalchemy/engine/threadlocal.py new file mode 100644 index 0000000..0ec1f96 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/engine/threadlocal.py @@ -0,0 +1,138 @@ +# engine/threadlocal.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Provides a thread-local transactional wrapper around the root Engine class. + +The ``threadlocal`` module is invoked when using the +``strategy="threadlocal"`` flag with :func:`~sqlalchemy.engine.create_engine`. +This module is semi-private and is invoked automatically when the threadlocal +engine strategy is used. +""" + +from .. import util +from . import base +import weakref + + +class TLConnection(base.Connection): + + def __init__(self, *arg, **kw): + super(TLConnection, self).__init__(*arg, **kw) + self.__opencount = 0 + + def _increment_connect(self): + self.__opencount += 1 + return self + + def close(self): + if self.__opencount == 1: + base.Connection.close(self) + self.__opencount -= 1 + + def _force_close(self): + self.__opencount = 0 + base.Connection.close(self) + + +class TLEngine(base.Engine): + """An Engine that includes support for thread-local managed + transactions. + + """ + _tl_connection_cls = TLConnection + + def __init__(self, *args, **kwargs): + super(TLEngine, self).__init__(*args, **kwargs) + self._connections = util.threading.local() + + def contextual_connect(self, **kw): + if not hasattr(self._connections, 'conn'): + connection = None + else: + connection = self._connections.conn() + + if connection is None or connection.closed: + # guards against pool-level reapers, if desired. + # or not connection.connection.is_valid: + connection = self._tl_connection_cls( + self, + self._wrap_pool_connect( + self.pool.connect, connection), + **kw) + self._connections.conn = weakref.ref(connection) + + return connection._increment_connect() + + def begin_twophase(self, xid=None): + if not hasattr(self._connections, 'trans'): + self._connections.trans = [] + self._connections.trans.append( + self.contextual_connect().begin_twophase(xid=xid)) + return self + + def begin_nested(self): + if not hasattr(self._connections, 'trans'): + self._connections.trans = [] + self._connections.trans.append( + self.contextual_connect().begin_nested()) + return self + + def begin(self): + if not hasattr(self._connections, 'trans'): + self._connections.trans = [] + self._connections.trans.append(self.contextual_connect().begin()) + return self + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + if type is None: + self.commit() + else: + self.rollback() + + def prepare(self): + if not hasattr(self._connections, 'trans') or \ + not self._connections.trans: + return + self._connections.trans[-1].prepare() + + def commit(self): + if not hasattr(self._connections, 'trans') or \ + not self._connections.trans: + return + trans = self._connections.trans.pop(-1) + trans.commit() + + def rollback(self): + if not hasattr(self._connections, 'trans') or \ + not self._connections.trans: + return + trans = self._connections.trans.pop(-1) + trans.rollback() + + def dispose(self): + self._connections = util.threading.local() + super(TLEngine, self).dispose() + + @property + def closed(self): + return not hasattr(self._connections, 'conn') or \ + self._connections.conn() is None or \ + self._connections.conn().closed + + def close(self): + if not self.closed: + self.contextual_connect().close() + connection = self._connections.conn() + connection._force_close() + del self._connections.conn + self._connections.trans = [] + + def __repr__(self): + return 'TLEngine(%r)' % self.url diff --git a/venv/Lib/site-packages/sqlalchemy/engine/url.py b/venv/Lib/site-packages/sqlalchemy/engine/url.py new file mode 100644 index 0000000..1662efe --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/engine/url.py @@ -0,0 +1,288 @@ +# engine/url.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Provides the :class:`~sqlalchemy.engine.url.URL` class which encapsulates +information about a database connection specification. + +The URL object is created automatically when +:func:`~sqlalchemy.engine.create_engine` is called with a string +argument; alternatively, the URL is a public-facing construct which can +be used directly and is also accepted directly by ``create_engine()``. +""" + +import re +from .. import exc, util +from . import Dialect +from ..dialects import registry, plugins + + +class URL(object): + """ + Represent the components of a URL used to connect to a database. + + This object is suitable to be passed directly to a + :func:`~sqlalchemy.create_engine` call. The fields of the URL are parsed + from a string by the :func:`.make_url` function. the string + format of the URL is an RFC-1738-style string. + + All initialization parameters are available as public attributes. + + :param drivername: the name of the database backend. + This name will correspond to a module in sqlalchemy/databases + or a third party plug-in. + + :param username: The user name. + + :param password: database password. + + :param host: The name of the host. + + :param port: The port number. + + :param database: The database name. + + :param query: A dictionary of options to be passed to the + dialect and/or the DBAPI upon connect. + + """ + + def __init__(self, drivername, username=None, password=None, + host=None, port=None, database=None, query=None): + self.drivername = drivername + self.username = username + self.password_original = password + self.host = host + if port is not None: + self.port = int(port) + else: + self.port = None + self.database = database + self.query = query or {} + + def __to_string__(self, hide_password=True): + s = self.drivername + "://" + if self.username is not None: + s += _rfc_1738_quote(self.username) + if self.password is not None: + s += ':' + ('***' if hide_password + else _rfc_1738_quote(self.password)) + s += "@" + if self.host is not None: + if ':' in self.host: + s += "[%s]" % self.host + else: + s += self.host + if self.port is not None: + s += ':' + str(self.port) + if self.database is not None: + s += '/' + self.database + if self.query: + keys = list(self.query) + keys.sort() + s += '?' + "&".join( + "%s=%s" % ( + k, + element + ) for k in keys for element in util.to_list(self.query[k]) + ) + return s + + def __str__(self): + return self.__to_string__(hide_password=False) + + def __repr__(self): + return self.__to_string__() + + def __hash__(self): + return hash(str(self)) + + def __eq__(self, other): + return \ + isinstance(other, URL) and \ + self.drivername == other.drivername and \ + self.username == other.username and \ + self.password == other.password and \ + self.host == other.host and \ + self.database == other.database and \ + self.query == other.query + + @property + def password(self): + if self.password_original is None: + return None + else: + return util.text_type(self.password_original) + + @password.setter + def password(self, password): + self.password_original = password + + def get_backend_name(self): + if '+' not in self.drivername: + return self.drivername + else: + return self.drivername.split('+')[0] + + def get_driver_name(self): + if '+' not in self.drivername: + return self.get_dialect().driver + else: + return self.drivername.split('+')[1] + + def _instantiate_plugins(self, kwargs): + plugin_names = util.to_list(self.query.get('plugin', ())) + plugin_names += kwargs.get('plugins', []) + + return [ + plugins.load(plugin_name)(self, kwargs) + for plugin_name in plugin_names + ] + + def _get_entrypoint(self): + """Return the "entry point" dialect class. + + This is normally the dialect itself except in the case when the + returned class implements the get_dialect_cls() method. + + """ + if '+' not in self.drivername: + name = self.drivername + else: + name = self.drivername.replace('+', '.') + cls = registry.load(name) + # check for legacy dialects that + # would return a module with 'dialect' as the + # actual class + if hasattr(cls, 'dialect') and \ + isinstance(cls.dialect, type) and \ + issubclass(cls.dialect, Dialect): + return cls.dialect + else: + return cls + + def get_dialect(self): + """Return the SQLAlchemy database dialect class corresponding + to this URL's driver name. + """ + entrypoint = self._get_entrypoint() + dialect_cls = entrypoint.get_dialect_cls(self) + return dialect_cls + + def translate_connect_args(self, names=[], **kw): + r"""Translate url attributes into a dictionary of connection arguments. + + Returns attributes of this url (`host`, `database`, `username`, + `password`, `port`) as a plain dictionary. The attribute names are + used as the keys by default. Unset or false attributes are omitted + from the final dictionary. + + :param \**kw: Optional, alternate key names for url attributes. + + :param names: Deprecated. Same purpose as the keyword-based alternate + names, but correlates the name to the original positionally. + """ + + translated = {} + attribute_names = ['host', 'database', 'username', 'password', 'port'] + for sname in attribute_names: + if names: + name = names.pop(0) + elif sname in kw: + name = kw[sname] + else: + name = sname + if name is not None and getattr(self, sname, False): + translated[name] = getattr(self, sname) + return translated + + +def make_url(name_or_url): + """Given a string or unicode instance, produce a new URL instance. + + The given string is parsed according to the RFC 1738 spec. If an + existing URL object is passed, just returns the object. + """ + + if isinstance(name_or_url, util.string_types): + return _parse_rfc1738_args(name_or_url) + else: + return name_or_url + + +def _parse_rfc1738_args(name): + pattern = re.compile(r''' + (?P<name>[\w\+]+):// + (?: + (?P<username>[^:/]*) + (?::(?P<password>.*))? + @)? + (?: + (?: + \[(?P<ipv6host>[^/]+)\] | + (?P<ipv4host>[^/:]+) + )? + (?::(?P<port>[^/]*))? + )? + (?:/(?P<database>.*))? + ''', re.X) + + m = pattern.match(name) + if m is not None: + components = m.groupdict() + if components['database'] is not None: + tokens = components['database'].split('?', 2) + components['database'] = tokens[0] + + if len(tokens) > 1: + query = {} + + for key, value in util.parse_qsl(tokens[1]): + if util.py2k: + key = key.encode('ascii') + if key in query: + query[key] = util.to_list(query[key]) + query[key].append(value) + else: + query[key] = value + else: + query = None + else: + query = None + components['query'] = query + + if components['username'] is not None: + components['username'] = _rfc_1738_unquote(components['username']) + + if components['password'] is not None: + components['password'] = _rfc_1738_unquote(components['password']) + + ipv4host = components.pop('ipv4host') + ipv6host = components.pop('ipv6host') + components['host'] = ipv4host or ipv6host + name = components.pop('name') + return URL(name, **components) + else: + raise exc.ArgumentError( + "Could not parse rfc1738 URL from string '%s'" % name) + + +def _rfc_1738_quote(text): + return re.sub(r'[:@/]', lambda m: "%%%X" % ord(m.group(0)), text) + + +def _rfc_1738_unquote(text): + return util.unquote(text) + + +def _parse_keyvalue_args(name): + m = re.match(r'(\w+)://(.*)', name) + if m is not None: + (name, args) = m.group(1, 2) + opts = dict(util.parse_qsl(args)) + return URL(name, *opts) + else: + return None diff --git a/venv/Lib/site-packages/sqlalchemy/engine/util.py b/venv/Lib/site-packages/sqlalchemy/engine/util.py new file mode 100644 index 0000000..17bc9a3 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/engine/util.py @@ -0,0 +1,74 @@ +# engine/util.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from .. import util + + +def connection_memoize(key): + """Decorator, memoize a function in a connection.info stash. + + Only applicable to functions which take no arguments other than a + connection. The memo will be stored in ``connection.info[key]``. + """ + + @util.decorator + def decorated(fn, self, connection): + connection = connection.connect() + try: + return connection.info[key] + except KeyError: + connection.info[key] = val = fn(self, connection) + return val + + return decorated + + +def py_fallback(): + def _distill_params(multiparams, params): + """Given arguments from the calling form *multiparams, **params, + return a list of bind parameter structures, usually a list of + dictionaries. + + In the case of 'raw' execution which accepts positional parameters, + it may be a list of tuples or lists. + + """ + + if not multiparams: + if params: + return [params] + else: + return [] + elif len(multiparams) == 1: + zero = multiparams[0] + if isinstance(zero, (list, tuple)): + if not zero or hasattr(zero[0], '__iter__') and \ + not hasattr(zero[0], 'strip'): + # execute(stmt, [{}, {}, {}, ...]) + # execute(stmt, [(), (), (), ...]) + return zero + else: + # execute(stmt, ("value", "value")) + return [zero] + elif hasattr(zero, 'keys'): + # execute(stmt, {"key":"value"}) + return [zero] + else: + # execute(stmt, "value") + return [[zero]] + else: + if hasattr(multiparams[0], '__iter__') and \ + not hasattr(multiparams[0], 'strip'): + return multiparams + else: + return [multiparams] + + return locals() +try: + from sqlalchemy.cutils import _distill_params +except ImportError: + globals().update(py_fallback()) diff --git a/venv/Lib/site-packages/sqlalchemy/event/__init__.py b/venv/Lib/site-packages/sqlalchemy/event/__init__.py new file mode 100644 index 0000000..a7e27e9 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/event/__init__.py @@ -0,0 +1,11 @@ +# event/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from .api import CANCEL, NO_RETVAL, listen, listens_for, remove, contains +from .base import Events, dispatcher +from .attr import RefCollection +from .legacy import _legacy_signature diff --git a/venv/Lib/site-packages/sqlalchemy/event/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/event/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0993d175595b4a99b99c8ad55b0d5a53315d54ed GIT binary patch literal 462 zcmYk1y-ve06oum?HEBx_ya5~PP!=|X5KuZGl_F7Kuq;__YKm1m4z^p7=V9Ujcp0xu zyaE%~Wx<ladyaqaIVsED+vg2?M@jOP{2M9g3z%*KSR@Oi5{(GbDM@ukGM$rLcSuKL zf>GR0)s8Mmp}VBJ^_lAFKIw0Du6A`vO3<BIGQi*P7nFcUm($UB61vknc0Yc2x|{^8 zr1PQ<{lDtiv#|kNq0L5wqB6DTvUY<s;PFP(-i6*mx`zAeMc4rf<-K?w8Kp$!rKw@z zfGM%$Rm+@Q*4(eHm=5+x#v5s4K$}J65uFGeu>%PCoI7EQ=(}6by|=aplHk85Z6C}> zV)x>ih@Y3yDSd=nj<!ecsHNA!t-NXIl`*T~EwA}fXlQykYayc^Y7WIx$g!bH&Z(1L u3>#jp;GCoGRdEHebW25yN*R;2^o$)hZ79yPS*(>fj}5xm^vyXY5}9e|$z literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/event/__pycache__/api.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/event/__pycache__/api.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f84acae040f095fbb6a0bca5c8d9fef1fa9afb82 GIT binary patch literal 6168 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/event/__pycache__/attr.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/event/__pycache__/attr.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..abfa24d855f2ded5e3de8dafaf77d590631f30f1 GIT binary patch literal 14730 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/event/__pycache__/base.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/event/__pycache__/base.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df413db6bcf6fb208f53b8f210fd457de33ff6f7 GIT binary patch literal 9153 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 Rji51S|In^o`$^;K{{j+6hr0j( literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/event/__pycache__/legacy.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/event/__pycache__/legacy.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a8e8e8869e3c0223354c104cebb647f28ec21dd GIT binary patch literal 4842 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/event/__pycache__/registry.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/event/__pycache__/registry.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c19fa8f373506c8c2b33dbec9a1ca47903796e22 GIT binary patch literal 5568 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 b*NPO{hPNmS&^60zcq?ubcUgD0-L-!MNLB5k literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/event/api.py b/venv/Lib/site-packages/sqlalchemy/event/api.py new file mode 100644 index 0000000..acfacc2 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/event/api.py @@ -0,0 +1,188 @@ +# event/api.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Public API functions for the event system. + +""" +from __future__ import absolute_import + +from .. import util, exc +from .base import _registrars +from .registry import _EventKey + +CANCEL = util.symbol('CANCEL') +NO_RETVAL = util.symbol('NO_RETVAL') + + +def _event_key(target, identifier, fn): + for evt_cls in _registrars[identifier]: + tgt = evt_cls._accept_with(target) + if tgt is not None: + return _EventKey(target, identifier, fn, tgt) + else: + raise exc.InvalidRequestError("No such event '%s' for target '%s'" % + (identifier, target)) + + +def listen(target, identifier, fn, *args, **kw): + """Register a listener function for the given target. + + e.g.:: + + from sqlalchemy import event + from sqlalchemy.schema import UniqueConstraint + + def unique_constraint_name(const, table): + const.name = "uq_%s_%s" % ( + table.name, + list(const.columns)[0].name + ) + event.listen( + UniqueConstraint, + "after_parent_attach", + unique_constraint_name) + + + A given function can also be invoked for only the first invocation + of the event using the ``once`` argument:: + + def on_config(): + do_config() + + event.listen(Mapper, "before_configure", on_config, once=True) + + .. versionadded:: 0.9.4 Added ``once=True`` to :func:`.event.listen` + and :func:`.event.listens_for`. + + .. note:: + + The :func:`.listen` function cannot be called at the same time + that the target event is being run. This has implications + for thread safety, and also means an event cannot be added + from inside the listener function for itself. The list of + events to be run are present inside of a mutable collection + that can't be changed during iteration. + + Event registration and removal is not intended to be a "high + velocity" operation; it is a configurational operation. For + systems that need to quickly associate and deassociate with + events at high scale, use a mutable structure that is handled + from inside of a single listener. + + .. versionchanged:: 1.0.0 - a ``collections.deque()`` object is now + used as the container for the list of events, which explicitly + disallows collection mutation while the collection is being + iterated. + + .. seealso:: + + :func:`.listens_for` + + :func:`.remove` + + """ + + _event_key(target, identifier, fn).listen(*args, **kw) + + +def listens_for(target, identifier, *args, **kw): + """Decorate a function as a listener for the given target + identifier. + + e.g.:: + + from sqlalchemy import event + from sqlalchemy.schema import UniqueConstraint + + @event.listens_for(UniqueConstraint, "after_parent_attach") + def unique_constraint_name(const, table): + const.name = "uq_%s_%s" % ( + table.name, + list(const.columns)[0].name + ) + + A given function can also be invoked for only the first invocation + of the event using the ``once`` argument:: + + @event.listens_for(Mapper, "before_configure", once=True) + def on_config(): + do_config() + + + .. versionadded:: 0.9.4 Added ``once=True`` to :func:`.event.listen` + and :func:`.event.listens_for`. + + .. seealso:: + + :func:`.listen` - general description of event listening + + """ + def decorate(fn): + listen(target, identifier, fn, *args, **kw) + return fn + return decorate + + +def remove(target, identifier, fn): + """Remove an event listener. + + The arguments here should match exactly those which were sent to + :func:`.listen`; all the event registration which proceeded as a result + of this call will be reverted by calling :func:`.remove` with the same + arguments. + + e.g.:: + + # if a function was registered like this... + @event.listens_for(SomeMappedClass, "before_insert", propagate=True) + def my_listener_function(*arg): + pass + + # ... it's removed like this + event.remove(SomeMappedClass, "before_insert", my_listener_function) + + Above, the listener function associated with ``SomeMappedClass`` was also + propagated to subclasses of ``SomeMappedClass``; the :func:`.remove` + function will revert all of these operations. + + .. versionadded:: 0.9.0 + + .. note:: + + The :func:`.remove` function cannot be called at the same time + that the target event is being run. This has implications + for thread safety, and also means an event cannot be removed + from inside the listener function for itself. The list of + events to be run are present inside of a mutable collection + that can't be changed during iteration. + + Event registration and removal is not intended to be a "high + velocity" operation; it is a configurational operation. For + systems that need to quickly associate and deassociate with + events at high scale, use a mutable structure that is handled + from inside of a single listener. + + .. versionchanged:: 1.0.0 - a ``collections.deque()`` object is now + used as the container for the list of events, which explicitly + disallows collection mutation while the collection is being + iterated. + + .. seealso:: + + :func:`.listen` + + """ + _event_key(target, identifier, fn).remove() + + +def contains(target, identifier, fn): + """Return True if the given target/ident/fn is set up to listen. + + .. versionadded:: 0.9.0 + + """ + + return _event_key(target, identifier, fn).contains() diff --git a/venv/Lib/site-packages/sqlalchemy/event/attr.py b/venv/Lib/site-packages/sqlalchemy/event/attr.py new file mode 100644 index 0000000..c33ec82 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/event/attr.py @@ -0,0 +1,401 @@ +# event/attr.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Attribute implementation for _Dispatch classes. + +The various listener targets for a particular event class are represented +as attributes, which refer to collections of listeners to be fired off. +These collections can exist at the class level as well as at the instance +level. An event is fired off using code like this:: + + some_object.dispatch.first_connect(arg1, arg2) + +Above, ``some_object.dispatch`` would be an instance of ``_Dispatch`` and +``first_connect`` is typically an instance of ``_ListenerCollection`` +if event listeners are present, or ``_EmptyListener`` if none are present. + +The attribute mechanics here spend effort trying to ensure listener functions +are available with a minimum of function call overhead, that unnecessary +objects aren't created (i.e. many empty per-instance listener collections), +as well as that everything is garbage collectable when owning references are +lost. Other features such as "propagation" of listener functions across +many ``_Dispatch`` instances, "joining" of multiple ``_Dispatch`` instances, +as well as support for subclass propagation (e.g. events assigned to +``Pool`` vs. ``QueuePool``) are all implemented here. + +""" + +from __future__ import absolute_import, with_statement +from .. import exc +from .. import util +from ..util import threading +from . import registry +from . import legacy +from itertools import chain +import weakref +import collections + + +class RefCollection(util.MemoizedSlots): + __slots__ = 'ref', + + def _memoized_attr_ref(self): + return weakref.ref(self, registry._collection_gced) + + +class _empty_collection(object): + def append(self, element): + pass + + def extend(self, other): + pass + + def remove(self, element): + pass + + def __iter__(self): + return iter([]) + + def clear(self): + pass + + +class _ClsLevelDispatch(RefCollection): + """Class-level events on :class:`._Dispatch` classes.""" + + __slots__ = ('name', 'arg_names', 'has_kw', + 'legacy_signatures', '_clslevel', '__weakref__') + + def __init__(self, parent_dispatch_cls, fn): + self.name = fn.__name__ + argspec = util.inspect_getargspec(fn) + self.arg_names = argspec.args[1:] + self.has_kw = bool(argspec.keywords) + self.legacy_signatures = list(reversed( + sorted( + getattr(fn, '_legacy_signatures', []), + key=lambda s: s[0] + ) + )) + fn.__doc__ = legacy._augment_fn_docs(self, parent_dispatch_cls, fn) + + self._clslevel = weakref.WeakKeyDictionary() + + def _adjust_fn_spec(self, fn, named): + if named: + fn = self._wrap_fn_for_kw(fn) + if self.legacy_signatures: + try: + argspec = util.get_callable_argspec(fn, no_self=True) + except TypeError: + pass + else: + fn = legacy._wrap_fn_for_legacy(self, fn, argspec) + return fn + + def _wrap_fn_for_kw(self, fn): + def wrap_kw(*args, **kw): + argdict = dict(zip(self.arg_names, args)) + argdict.update(kw) + return fn(**argdict) + return wrap_kw + + def insert(self, event_key, propagate): + target = event_key.dispatch_target + assert isinstance(target, type), \ + "Class-level Event targets must be classes." + if not getattr(target, '_sa_propagate_class_events', True): + raise exc.InvalidRequestError( + "Can't assign an event directly to the %s class" % target) + stack = [target] + while stack: + cls = stack.pop(0) + stack.extend(cls.__subclasses__()) + if cls is not target and cls not in self._clslevel: + self.update_subclass(cls) + else: + if cls not in self._clslevel: + self._assign_cls_collection(cls) + self._clslevel[cls].appendleft(event_key._listen_fn) + registry._stored_in_collection(event_key, self) + + def append(self, event_key, propagate): + target = event_key.dispatch_target + assert isinstance(target, type), \ + "Class-level Event targets must be classes." + if not getattr(target, '_sa_propagate_class_events', True): + raise exc.InvalidRequestError( + "Can't assign an event directly to the %s class" % target) + stack = [target] + while stack: + cls = stack.pop(0) + stack.extend(cls.__subclasses__()) + if cls is not target and cls not in self._clslevel: + self.update_subclass(cls) + else: + if cls not in self._clslevel: + self._assign_cls_collection(cls) + self._clslevel[cls].append(event_key._listen_fn) + registry._stored_in_collection(event_key, self) + + def _assign_cls_collection(self, target): + if getattr(target, '_sa_propagate_class_events', True): + self._clslevel[target] = collections.deque() + else: + self._clslevel[target] = _empty_collection() + + def update_subclass(self, target): + if target not in self._clslevel: + self._assign_cls_collection(target) + clslevel = self._clslevel[target] + for cls in target.__mro__[1:]: + if cls in self._clslevel: + clslevel.extend([ + fn for fn + in self._clslevel[cls] + if fn not in clslevel + ]) + + def remove(self, event_key): + target = event_key.dispatch_target + stack = [target] + while stack: + cls = stack.pop(0) + stack.extend(cls.__subclasses__()) + if cls in self._clslevel: + self._clslevel[cls].remove(event_key._listen_fn) + registry._removed_from_collection(event_key, self) + + def clear(self): + """Clear all class level listeners""" + + to_clear = set() + for dispatcher in self._clslevel.values(): + to_clear.update(dispatcher) + dispatcher.clear() + registry._clear(self, to_clear) + + def for_modify(self, obj): + """Return an event collection which can be modified. + + For _ClsLevelDispatch at the class level of + a dispatcher, this returns self. + + """ + return self + + +class _InstanceLevelDispatch(RefCollection): + __slots__ = () + + def _adjust_fn_spec(self, fn, named): + return self.parent._adjust_fn_spec(fn, named) + + +class _EmptyListener(_InstanceLevelDispatch): + """Serves as a proxy interface to the events + served by a _ClsLevelDispatch, when there are no + instance-level events present. + + Is replaced by _ListenerCollection when instance-level + events are added. + + """ + + propagate = frozenset() + listeners = () + + __slots__ = 'parent', 'parent_listeners', 'name' + + def __init__(self, parent, target_cls): + if target_cls not in parent._clslevel: + parent.update_subclass(target_cls) + self.parent = parent # _ClsLevelDispatch + self.parent_listeners = parent._clslevel[target_cls] + self.name = parent.name + + def for_modify(self, obj): + """Return an event collection which can be modified. + + For _EmptyListener at the instance level of + a dispatcher, this generates a new + _ListenerCollection, applies it to the instance, + and returns it. + + """ + result = _ListenerCollection(self.parent, obj._instance_cls) + if getattr(obj, self.name) is self: + setattr(obj, self.name, result) + else: + assert isinstance(getattr(obj, self.name), _JoinedListener) + return result + + def _needs_modify(self, *args, **kw): + raise NotImplementedError("need to call for_modify()") + + exec_once = insert = append = remove = clear = _needs_modify + + def __call__(self, *args, **kw): + """Execute this event.""" + + for fn in self.parent_listeners: + fn(*args, **kw) + + def __len__(self): + return len(self.parent_listeners) + + def __iter__(self): + return iter(self.parent_listeners) + + def __bool__(self): + return bool(self.parent_listeners) + + __nonzero__ = __bool__ + + +class _CompoundListener(_InstanceLevelDispatch): + __slots__ = '_exec_once_mutex', '_exec_once' + + def _memoized_attr__exec_once_mutex(self): + return threading.Lock() + + def exec_once(self, *args, **kw): + """Execute this event, but only if it has not been + executed already for this collection.""" + + if not self._exec_once: + with self._exec_once_mutex: + if not self._exec_once: + try: + self(*args, **kw) + finally: + self._exec_once = True + + def __call__(self, *args, **kw): + """Execute this event.""" + + for fn in self.parent_listeners: + fn(*args, **kw) + for fn in self.listeners: + fn(*args, **kw) + + def __len__(self): + return len(self.parent_listeners) + len(self.listeners) + + def __iter__(self): + return chain(self.parent_listeners, self.listeners) + + def __bool__(self): + return bool(self.listeners or self.parent_listeners) + + __nonzero__ = __bool__ + + +class _ListenerCollection(_CompoundListener): + """Instance-level attributes on instances of :class:`._Dispatch`. + + Represents a collection of listeners. + + As of 0.7.9, _ListenerCollection is only first + created via the _EmptyListener.for_modify() method. + + """ + + __slots__ = ( + 'parent_listeners', 'parent', 'name', 'listeners', + 'propagate', '__weakref__') + + def __init__(self, parent, target_cls): + if target_cls not in parent._clslevel: + parent.update_subclass(target_cls) + self._exec_once = False + self.parent_listeners = parent._clslevel[target_cls] + self.parent = parent + self.name = parent.name + self.listeners = collections.deque() + self.propagate = set() + + def for_modify(self, obj): + """Return an event collection which can be modified. + + For _ListenerCollection at the instance level of + a dispatcher, this returns self. + + """ + return self + + def _update(self, other, only_propagate=True): + """Populate from the listeners in another :class:`_Dispatch` + object.""" + + existing_listeners = self.listeners + existing_listener_set = set(existing_listeners) + self.propagate.update(other.propagate) + other_listeners = [l for l + in other.listeners + if l not in existing_listener_set + and not only_propagate or l in self.propagate + ] + + existing_listeners.extend(other_listeners) + + to_associate = other.propagate.union(other_listeners) + registry._stored_in_collection_multi(self, other, to_associate) + + def insert(self, event_key, propagate): + if event_key.prepend_to_list(self, self.listeners): + if propagate: + self.propagate.add(event_key._listen_fn) + + def append(self, event_key, propagate): + if event_key.append_to_list(self, self.listeners): + if propagate: + self.propagate.add(event_key._listen_fn) + + def remove(self, event_key): + self.listeners.remove(event_key._listen_fn) + self.propagate.discard(event_key._listen_fn) + registry._removed_from_collection(event_key, self) + + def clear(self): + registry._clear(self, self.listeners) + self.propagate.clear() + self.listeners.clear() + + +class _JoinedListener(_CompoundListener): + __slots__ = 'parent', 'name', 'local', 'parent_listeners' + + def __init__(self, parent, name, local): + self._exec_once = False + self.parent = parent + self.name = name + self.local = local + self.parent_listeners = self.local + + @property + def listeners(self): + return getattr(self.parent, self.name) + + def _adjust_fn_spec(self, fn, named): + return self.local._adjust_fn_spec(fn, named) + + def for_modify(self, obj): + self.local = self.parent_listeners = self.local.for_modify(obj) + return self + + def insert(self, event_key, propagate): + self.local.insert(event_key, propagate) + + def append(self, event_key, propagate): + self.local.append(event_key, propagate) + + def remove(self, event_key): + self.local.remove(event_key) + + def clear(self): + raise NotImplementedError() diff --git a/venv/Lib/site-packages/sqlalchemy/event/base.py b/venv/Lib/site-packages/sqlalchemy/event/base.py new file mode 100644 index 0000000..47bcc44 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/event/base.py @@ -0,0 +1,289 @@ +# event/base.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Base implementation classes. + +The public-facing ``Events`` serves as the base class for an event interface; +its public attributes represent different kinds of events. These attributes +are mirrored onto a ``_Dispatch`` class, which serves as a container for +collections of listener functions. These collections are represented both +at the class level of a particular ``_Dispatch`` class as well as within +instances of ``_Dispatch``. + +""" +from __future__ import absolute_import + +import weakref + +from .. import util +from .attr import _JoinedListener, \ + _EmptyListener, _ClsLevelDispatch + +_registrars = util.defaultdict(list) + + +def _is_event_name(name): + return not name.startswith('_') and name != 'dispatch' + + +class _UnpickleDispatch(object): + """Serializable callable that re-generates an instance of + :class:`_Dispatch` given a particular :class:`.Events` subclass. + + """ + + def __call__(self, _instance_cls): + for cls in _instance_cls.__mro__: + if 'dispatch' in cls.__dict__: + return cls.__dict__['dispatch'].\ + dispatch_cls._for_class(_instance_cls) + else: + raise AttributeError("No class with a 'dispatch' member present.") + + +class _Dispatch(object): + """Mirror the event listening definitions of an Events class with + listener collections. + + Classes which define a "dispatch" member will return a + non-instantiated :class:`._Dispatch` subclass when the member + is accessed at the class level. When the "dispatch" member is + accessed at the instance level of its owner, an instance + of the :class:`._Dispatch` class is returned. + + A :class:`._Dispatch` class is generated for each :class:`.Events` + class defined, by the :func:`._create_dispatcher_class` function. + The original :class:`.Events` classes remain untouched. + This decouples the construction of :class:`.Events` subclasses from + the implementation used by the event internals, and allows + inspecting tools like Sphinx to work in an unsurprising + way against the public API. + + """ + + # in one ORM edge case, an attribute is added to _Dispatch, + # so __dict__ is used in just that case and potentially others. + __slots__ = '_parent', '_instance_cls', '__dict__', '_empty_listeners' + + _empty_listener_reg = weakref.WeakKeyDictionary() + + def __init__(self, parent, instance_cls=None): + self._parent = parent + self._instance_cls = instance_cls + if instance_cls: + try: + self._empty_listeners = self._empty_listener_reg[instance_cls] + except KeyError: + self._empty_listeners = \ + self._empty_listener_reg[instance_cls] = dict( + (ls.name, _EmptyListener(ls, instance_cls)) + for ls in parent._event_descriptors + ) + else: + self._empty_listeners = {} + + def __getattr__(self, name): + # assign EmptyListeners as attributes on demand + # to reduce startup time for new dispatch objects + try: + ls = self._empty_listeners[name] + except KeyError: + raise AttributeError(name) + else: + setattr(self, ls.name, ls) + return ls + + @property + def _event_descriptors(self): + for k in self._event_names: + yield getattr(self, k) + + def _for_class(self, instance_cls): + return self.__class__(self, instance_cls) + + def _for_instance(self, instance): + instance_cls = instance.__class__ + return self._for_class(instance_cls) + + @property + def _listen(self): + return self._events._listen + + def _join(self, other): + """Create a 'join' of this :class:`._Dispatch` and another. + + This new dispatcher will dispatch events to both + :class:`._Dispatch` objects. + + """ + if '_joined_dispatch_cls' not in self.__class__.__dict__: + cls = type( + "Joined%s" % self.__class__.__name__, + (_JoinedDispatcher, ), {'__slots__': self._event_names} + ) + + self.__class__._joined_dispatch_cls = cls + return self._joined_dispatch_cls(self, other) + + def __reduce__(self): + return _UnpickleDispatch(), (self._instance_cls, ) + + def _update(self, other, only_propagate=True): + """Populate from the listeners in another :class:`_Dispatch` + object.""" + for ls in other._event_descriptors: + if isinstance(ls, _EmptyListener): + continue + getattr(self, ls.name).\ + for_modify(self)._update(ls, only_propagate=only_propagate) + + def _clear(self): + for ls in self._event_descriptors: + ls.for_modify(self).clear() + + +class _EventMeta(type): + """Intercept new Event subclasses and create + associated _Dispatch classes.""" + + def __init__(cls, classname, bases, dict_): + _create_dispatcher_class(cls, classname, bases, dict_) + return type.__init__(cls, classname, bases, dict_) + + +def _create_dispatcher_class(cls, classname, bases, dict_): + """Create a :class:`._Dispatch` class corresponding to an + :class:`.Events` class.""" + + # there's all kinds of ways to do this, + # i.e. make a Dispatch class that shares the '_listen' method + # of the Event class, this is the straight monkeypatch. + if hasattr(cls, 'dispatch'): + dispatch_base = cls.dispatch.__class__ + else: + dispatch_base = _Dispatch + + event_names = [k for k in dict_ if _is_event_name(k)] + dispatch_cls = type("%sDispatch" % classname, + (dispatch_base, ), {'__slots__': event_names}) + + dispatch_cls._event_names = event_names + + dispatch_inst = cls._set_dispatch(cls, dispatch_cls) + for k in dispatch_cls._event_names: + setattr(dispatch_inst, k, _ClsLevelDispatch(cls, dict_[k])) + _registrars[k].append(cls) + + for super_ in dispatch_cls.__bases__: + if issubclass(super_, _Dispatch) and super_ is not _Dispatch: + for ls in super_._events.dispatch._event_descriptors: + setattr(dispatch_inst, ls.name, ls) + dispatch_cls._event_names.append(ls.name) + + if getattr(cls, '_dispatch_target', None): + cls._dispatch_target.dispatch = dispatcher(cls) + + +def _remove_dispatcher(cls): + for k in cls.dispatch._event_names: + _registrars[k].remove(cls) + if not _registrars[k]: + del _registrars[k] + + +class Events(util.with_metaclass(_EventMeta, object)): + """Define event listening functions for a particular target type.""" + + @staticmethod + def _set_dispatch(cls, dispatch_cls): + # this allows an Events subclass to define additional utility + # methods made available to the target via + # "self.dispatch._events.<utilitymethod>" + # @staticemethod to allow easy "super" calls while in a metaclass + # constructor. + cls.dispatch = dispatch_cls(None) + dispatch_cls._events = cls + return cls.dispatch + + @classmethod + def _accept_with(cls, target): + # Mapper, ClassManager, Session override this to + # also accept classes, scoped_sessions, sessionmakers, etc. + if hasattr(target, 'dispatch') and ( + + isinstance(target.dispatch, cls.dispatch.__class__) or + + + ( + isinstance(target.dispatch, type) and + isinstance(target.dispatch, cls.dispatch.__class__) + ) or + + ( + isinstance(target.dispatch, _JoinedDispatcher) and + isinstance(target.dispatch.parent, cls.dispatch.__class__) + ) + + + ): + return target + else: + return None + + @classmethod + def _listen(cls, event_key, propagate=False, insert=False, named=False): + event_key.base_listen(propagate=propagate, insert=insert, named=named) + + @classmethod + def _remove(cls, event_key): + event_key.remove() + + @classmethod + def _clear(cls): + cls.dispatch._clear() + + +class _JoinedDispatcher(object): + """Represent a connection between two _Dispatch objects.""" + + __slots__ = 'local', 'parent', '_instance_cls' + + def __init__(self, local, parent): + self.local = local + self.parent = parent + self._instance_cls = self.local._instance_cls + + def __getattr__(self, name): + # assign _JoinedListeners as attributes on demand + # to reduce startup time for new dispatch objects + ls = getattr(self.local, name) + jl = _JoinedListener(self.parent, ls.name, ls) + setattr(self, ls.name, jl) + return jl + + @property + def _listen(self): + return self.parent._listen + + +class dispatcher(object): + """Descriptor used by target classes to + deliver the _Dispatch class at the class level + and produce new _Dispatch instances for target + instances. + + """ + + def __init__(self, events): + self.dispatch_cls = events.dispatch + self.events = events + + def __get__(self, obj, cls): + if obj is None: + return self.dispatch_cls + obj.__dict__['dispatch'] = disp = self.dispatch_cls._for_instance(obj) + return disp diff --git a/venv/Lib/site-packages/sqlalchemy/event/legacy.py b/venv/Lib/site-packages/sqlalchemy/event/legacy.py new file mode 100644 index 0000000..1883070 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/event/legacy.py @@ -0,0 +1,169 @@ +# event/legacy.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Routines to handle adaption of legacy call signatures, +generation of deprecation notes and docstrings. + +""" + +from .. import util + + +def _legacy_signature(since, argnames, converter=None): + def leg(fn): + if not hasattr(fn, '_legacy_signatures'): + fn._legacy_signatures = [] + fn._legacy_signatures.append((since, argnames, converter)) + return fn + return leg + + +def _wrap_fn_for_legacy(dispatch_collection, fn, argspec): + for since, argnames, conv in dispatch_collection.legacy_signatures: + if argnames[-1] == "**kw": + has_kw = True + argnames = argnames[0:-1] + else: + has_kw = False + + if len(argnames) == len(argspec.args) \ + and has_kw is bool(argspec.keywords): + + if conv: + assert not has_kw + + def wrap_leg(*args): + return fn(*conv(*args)) + else: + def wrap_leg(*args, **kw): + argdict = dict(zip(dispatch_collection.arg_names, args)) + args = [argdict[name] for name in argnames] + if has_kw: + return fn(*args, **kw) + else: + return fn(*args) + return wrap_leg + else: + return fn + + +def _indent(text, indent): + return "\n".join( + indent + line + for line in text.split("\n") + ) + + +def _standard_listen_example(dispatch_collection, sample_target, fn): + example_kw_arg = _indent( + "\n".join( + "%(arg)s = kw['%(arg)s']" % {"arg": arg} + for arg in dispatch_collection.arg_names[0:2] + ), + " ") + if dispatch_collection.legacy_signatures: + current_since = max(since for since, args, conv + in dispatch_collection.legacy_signatures) + else: + current_since = None + text = ( + "from sqlalchemy import event\n\n" + "# standard decorator style%(current_since)s\n" + "@event.listens_for(%(sample_target)s, '%(event_name)s')\n" + "def receive_%(event_name)s(" + "%(named_event_arguments)s%(has_kw_arguments)s):\n" + " \"listen for the '%(event_name)s' event\"\n" + "\n # ... (event handling logic) ...\n" + ) + + if len(dispatch_collection.arg_names) > 3: + text += ( + + "\n# named argument style (new in 0.9)\n" + "@event.listens_for(" + "%(sample_target)s, '%(event_name)s', named=True)\n" + "def receive_%(event_name)s(**kw):\n" + " \"listen for the '%(event_name)s' event\"\n" + "%(example_kw_arg)s\n" + "\n # ... (event handling logic) ...\n" + ) + + text %= { + "current_since": " (arguments as of %s)" % + current_since if current_since else "", + "event_name": fn.__name__, + "has_kw_arguments": ", **kw" if dispatch_collection.has_kw else "", + "named_event_arguments": ", ".join(dispatch_collection.arg_names), + "example_kw_arg": example_kw_arg, + "sample_target": sample_target + } + return text + + +def _legacy_listen_examples(dispatch_collection, sample_target, fn): + text = "" + for since, args, conv in dispatch_collection.legacy_signatures: + text += ( + "\n# legacy calling style (pre-%(since)s)\n" + "@event.listens_for(%(sample_target)s, '%(event_name)s')\n" + "def receive_%(event_name)s(" + "%(named_event_arguments)s%(has_kw_arguments)s):\n" + " \"listen for the '%(event_name)s' event\"\n" + "\n # ... (event handling logic) ...\n" % { + "since": since, + "event_name": fn.__name__, + "has_kw_arguments": " **kw" + if dispatch_collection.has_kw else "", + "named_event_arguments": ", ".join(args), + "sample_target": sample_target + } + ) + return text + + +def _version_signature_changes(dispatch_collection): + since, args, conv = dispatch_collection.legacy_signatures[0] + return ( + "\n.. versionchanged:: %(since)s\n" + " The ``%(event_name)s`` event now accepts the \n" + " arguments ``%(named_event_arguments)s%(has_kw_arguments)s``.\n" + " Listener functions which accept the previous argument \n" + " signature(s) listed above will be automatically \n" + " adapted to the new signature." % { + "since": since, + "event_name": dispatch_collection.name, + "named_event_arguments": ", ".join(dispatch_collection.arg_names), + "has_kw_arguments": ", **kw" if dispatch_collection.has_kw else "" + } + ) + + +def _augment_fn_docs(dispatch_collection, parent_dispatch_cls, fn): + header = ".. container:: event_signatures\n\n"\ + " Example argument forms::\n"\ + "\n" + + sample_target = getattr(parent_dispatch_cls, "_target_class_doc", "obj") + text = ( + header + + _indent( + _standard_listen_example( + dispatch_collection, sample_target, fn), + " " * 8) + ) + if dispatch_collection.legacy_signatures: + text += _indent( + _legacy_listen_examples( + dispatch_collection, sample_target, fn), + " " * 8) + + text += _version_signature_changes(dispatch_collection) + + return util.inject_docstring_text(fn.__doc__, + text, + 1 + ) diff --git a/venv/Lib/site-packages/sqlalchemy/event/registry.py b/venv/Lib/site-packages/sqlalchemy/event/registry.py new file mode 100644 index 0000000..7582304 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/event/registry.py @@ -0,0 +1,262 @@ +# event/registry.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Provides managed registration services on behalf of :func:`.listen` +arguments. + +By "managed registration", we mean that event listening functions and +other objects can be added to various collections in such a way that their +membership in all those collections can be revoked at once, based on +an equivalent :class:`._EventKey`. + +""" + +from __future__ import absolute_import + +import weakref +import collections +import types +from .. import exc, util + + +_key_to_collection = collections.defaultdict(dict) +""" +Given an original listen() argument, can locate all +listener collections and the listener fn contained + +(target, identifier, fn) -> { + ref(listenercollection) -> ref(listener_fn) + ref(listenercollection) -> ref(listener_fn) + ref(listenercollection) -> ref(listener_fn) + } +""" + +_collection_to_key = collections.defaultdict(dict) +""" +Given a _ListenerCollection or _ClsLevelListener, can locate +all the original listen() arguments and the listener fn contained + +ref(listenercollection) -> { + ref(listener_fn) -> (target, identifier, fn), + ref(listener_fn) -> (target, identifier, fn), + ref(listener_fn) -> (target, identifier, fn), + } +""" + + +def _collection_gced(ref): + # defaultdict, so can't get a KeyError + if not _collection_to_key or ref not in _collection_to_key: + return + listener_to_key = _collection_to_key.pop(ref) + for key in listener_to_key.values(): + if key in _key_to_collection: + # defaultdict, so can't get a KeyError + dispatch_reg = _key_to_collection[key] + dispatch_reg.pop(ref) + if not dispatch_reg: + _key_to_collection.pop(key) + + +def _stored_in_collection(event_key, owner): + key = event_key._key + + dispatch_reg = _key_to_collection[key] + + owner_ref = owner.ref + listen_ref = weakref.ref(event_key._listen_fn) + + if owner_ref in dispatch_reg: + return False + + dispatch_reg[owner_ref] = listen_ref + + listener_to_key = _collection_to_key[owner_ref] + listener_to_key[listen_ref] = key + + return True + + +def _removed_from_collection(event_key, owner): + key = event_key._key + + dispatch_reg = _key_to_collection[key] + + listen_ref = weakref.ref(event_key._listen_fn) + + owner_ref = owner.ref + dispatch_reg.pop(owner_ref, None) + if not dispatch_reg: + del _key_to_collection[key] + + if owner_ref in _collection_to_key: + listener_to_key = _collection_to_key[owner_ref] + listener_to_key.pop(listen_ref) + + +def _stored_in_collection_multi(newowner, oldowner, elements): + if not elements: + return + + oldowner = oldowner.ref + newowner = newowner.ref + + old_listener_to_key = _collection_to_key[oldowner] + new_listener_to_key = _collection_to_key[newowner] + + for listen_fn in elements: + listen_ref = weakref.ref(listen_fn) + key = old_listener_to_key[listen_ref] + dispatch_reg = _key_to_collection[key] + if newowner in dispatch_reg: + assert dispatch_reg[newowner] == listen_ref + else: + dispatch_reg[newowner] = listen_ref + + new_listener_to_key[listen_ref] = key + + +def _clear(owner, elements): + if not elements: + return + + owner = owner.ref + listener_to_key = _collection_to_key[owner] + for listen_fn in elements: + listen_ref = weakref.ref(listen_fn) + key = listener_to_key[listen_ref] + dispatch_reg = _key_to_collection[key] + dispatch_reg.pop(owner, None) + + if not dispatch_reg: + del _key_to_collection[key] + + +class _EventKey(object): + """Represent :func:`.listen` arguments. + """ + + __slots__ = ( + 'target', 'identifier', 'fn', 'fn_key', 'fn_wrap', 'dispatch_target' + ) + + def __init__(self, target, identifier, + fn, dispatch_target, _fn_wrap=None): + self.target = target + self.identifier = identifier + self.fn = fn + if isinstance(fn, types.MethodType): + self.fn_key = id(fn.__func__), id(fn.__self__) + else: + self.fn_key = id(fn) + self.fn_wrap = _fn_wrap + self.dispatch_target = dispatch_target + + @property + def _key(self): + return (id(self.target), self.identifier, self.fn_key) + + def with_wrapper(self, fn_wrap): + if fn_wrap is self._listen_fn: + return self + else: + return _EventKey( + self.target, + self.identifier, + self.fn, + self.dispatch_target, + _fn_wrap=fn_wrap + ) + + def with_dispatch_target(self, dispatch_target): + if dispatch_target is self.dispatch_target: + return self + else: + return _EventKey( + self.target, + self.identifier, + self.fn, + dispatch_target, + _fn_wrap=self.fn_wrap + ) + + def listen(self, *args, **kw): + once = kw.pop("once", False) + named = kw.pop("named", False) + + target, identifier, fn = \ + self.dispatch_target, self.identifier, self._listen_fn + + dispatch_collection = getattr(target.dispatch, identifier) + + adjusted_fn = dispatch_collection._adjust_fn_spec(fn, named) + + self = self.with_wrapper(adjusted_fn) + + if once: + self.with_wrapper( + util.only_once(self._listen_fn)).listen(*args, **kw) + else: + self.dispatch_target.dispatch._listen(self, *args, **kw) + + def remove(self): + key = self._key + + if key not in _key_to_collection: + raise exc.InvalidRequestError( + "No listeners found for event %s / %r / %s " % + (self.target, self.identifier, self.fn) + ) + dispatch_reg = _key_to_collection.pop(key) + + for collection_ref, listener_ref in dispatch_reg.items(): + collection = collection_ref() + listener_fn = listener_ref() + if collection is not None and listener_fn is not None: + collection.remove(self.with_wrapper(listener_fn)) + + def contains(self): + """Return True if this event key is registered to listen. + """ + return self._key in _key_to_collection + + def base_listen(self, propagate=False, insert=False, + named=False): + + target, identifier, fn = \ + self.dispatch_target, self.identifier, self._listen_fn + + dispatch_collection = getattr(target.dispatch, identifier) + + if insert: + dispatch_collection.\ + for_modify(target.dispatch).insert(self, propagate) + else: + dispatch_collection.\ + for_modify(target.dispatch).append(self, propagate) + + @property + def _listen_fn(self): + return self.fn_wrap or self.fn + + def append_to_list(self, owner, list_): + if _stored_in_collection(self, owner): + list_.append(self._listen_fn) + return True + else: + return False + + def remove_from_list(self, owner, list_): + _removed_from_collection(self, owner) + list_.remove(self._listen_fn) + + def prepend_to_list(self, owner, list_): + if _stored_in_collection(self, owner): + list_.appendleft(self._listen_fn) + return True + else: + return False diff --git a/venv/Lib/site-packages/sqlalchemy/events.py b/venv/Lib/site-packages/sqlalchemy/events.py new file mode 100644 index 0000000..3e97ea8 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/events.py @@ -0,0 +1,1274 @@ +# sqlalchemy/events.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Core event interfaces.""" + +from . import event, exc +from .pool import Pool +from .engine import Connectable, Engine, Dialect +from .sql.base import SchemaEventTarget + + +class DDLEvents(event.Events): + """ + Define event listeners for schema objects, + that is, :class:`.SchemaItem` and other :class:`.SchemaEventTarget` + subclasses, including :class:`.MetaData`, :class:`.Table`, + :class:`.Column`. + + :class:`.MetaData` and :class:`.Table` support events + specifically regarding when CREATE and DROP + DDL is emitted to the database. + + Attachment events are also provided to customize + behavior whenever a child schema element is associated + with a parent, such as, when a :class:`.Column` is associated + with its :class:`.Table`, when a :class:`.ForeignKeyConstraint` + is associated with a :class:`.Table`, etc. + + Example using the ``after_create`` event:: + + from sqlalchemy import event + from sqlalchemy import Table, Column, Metadata, Integer + + m = MetaData() + some_table = Table('some_table', m, Column('data', Integer)) + + def after_create(target, connection, **kw): + connection.execute("ALTER TABLE %s SET name=foo_%s" % + (target.name, target.name)) + + event.listen(some_table, "after_create", after_create) + + DDL events integrate closely with the + :class:`.DDL` class and the :class:`.DDLElement` hierarchy + of DDL clause constructs, which are themselves appropriate + as listener callables:: + + from sqlalchemy import DDL + event.listen( + some_table, + "after_create", + DDL("ALTER TABLE %(table)s SET name=foo_%(table)s") + ) + + The methods here define the name of an event as well + as the names of members that are passed to listener + functions. + + For all :class:`.DDLEvent` events, the ``propagate=True`` keyword argument + will ensure that a given event handler is propagated to copies of the + object, which are made when using the :meth:`.Table.tometadata` method:: + + from sqlalchemy import DDL + event.listen( + some_table, + "after_create", + DDL("ALTER TABLE %(table)s SET name=foo_%(table)s"), + propagate=True + ) + + new_table = some_table.tometadata(new_metadata) + + The above :class:`.DDL` object will also be associated with the + :class:`.Table` object represented by ``new_table``. + + + See also: + + :ref:`event_toplevel` + + :class:`.DDLElement` + + :class:`.DDL` + + :ref:`schema_ddl_sequences` + + """ + + _target_class_doc = "SomeSchemaClassOrObject" + _dispatch_target = SchemaEventTarget + + def before_create(self, target, connection, **kw): + r"""Called before CREATE statements are emitted. + + :param target: the :class:`.MetaData` or :class:`.Table` + object which is the target of the event. + :param connection: the :class:`.Connection` where the + CREATE statement or statements will be emitted. + :param \**kw: additional keyword arguments relevant + to the event. The contents of this dictionary + may vary across releases, and include the + list of tables being generated for a metadata-level + event, the checkfirst flag, and other + elements used by internal events. + + :func:`.event.listen` also accepts the ``propagate=True`` + modifier for this event; when True, the listener function will + be established for any copies made of the target object, + i.e. those copies that are generated when + :meth:`.Table.tometadata` is used. + + """ + + def after_create(self, target, connection, **kw): + r"""Called after CREATE statements are emitted. + + :param target: the :class:`.MetaData` or :class:`.Table` + object which is the target of the event. + :param connection: the :class:`.Connection` where the + CREATE statement or statements have been emitted. + :param \**kw: additional keyword arguments relevant + to the event. The contents of this dictionary + may vary across releases, and include the + list of tables being generated for a metadata-level + event, the checkfirst flag, and other + elements used by internal events. + + :func:`.event.listen` also accepts the ``propagate=True`` + modifier for this event; when True, the listener function will + be established for any copies made of the target object, + i.e. those copies that are generated when + :meth:`.Table.tometadata` is used. + + """ + + def before_drop(self, target, connection, **kw): + r"""Called before DROP statements are emitted. + + :param target: the :class:`.MetaData` or :class:`.Table` + object which is the target of the event. + :param connection: the :class:`.Connection` where the + DROP statement or statements will be emitted. + :param \**kw: additional keyword arguments relevant + to the event. The contents of this dictionary + may vary across releases, and include the + list of tables being generated for a metadata-level + event, the checkfirst flag, and other + elements used by internal events. + + :func:`.event.listen` also accepts the ``propagate=True`` + modifier for this event; when True, the listener function will + be established for any copies made of the target object, + i.e. those copies that are generated when + :meth:`.Table.tometadata` is used. + + """ + + def after_drop(self, target, connection, **kw): + r"""Called after DROP statements are emitted. + + :param target: the :class:`.MetaData` or :class:`.Table` + object which is the target of the event. + :param connection: the :class:`.Connection` where the + DROP statement or statements have been emitted. + :param \**kw: additional keyword arguments relevant + to the event. The contents of this dictionary + may vary across releases, and include the + list of tables being generated for a metadata-level + event, the checkfirst flag, and other + elements used by internal events. + + :func:`.event.listen` also accepts the ``propagate=True`` + modifier for this event; when True, the listener function will + be established for any copies made of the target object, + i.e. those copies that are generated when + :meth:`.Table.tometadata` is used. + + """ + + def before_parent_attach(self, target, parent): + """Called before a :class:`.SchemaItem` is associated with + a parent :class:`.SchemaItem`. + + :param target: the target object + :param parent: the parent to which the target is being attached. + + :func:`.event.listen` also accepts the ``propagate=True`` + modifier for this event; when True, the listener function will + be established for any copies made of the target object, + i.e. those copies that are generated when + :meth:`.Table.tometadata` is used. + + """ + + def after_parent_attach(self, target, parent): + """Called after a :class:`.SchemaItem` is associated with + a parent :class:`.SchemaItem`. + + :param target: the target object + :param parent: the parent to which the target is being attached. + + :func:`.event.listen` also accepts the ``propagate=True`` + modifier for this event; when True, the listener function will + be established for any copies made of the target object, + i.e. those copies that are generated when + :meth:`.Table.tometadata` is used. + + """ + + def column_reflect(self, inspector, table, column_info): + """Called for each unit of 'column info' retrieved when + a :class:`.Table` is being reflected. + + The dictionary of column information as returned by the + dialect is passed, and can be modified. The dictionary + is that returned in each element of the list returned + by :meth:`.reflection.Inspector.get_columns`: + + * ``name`` - the column's name + + * ``type`` - the type of this column, which should be an instance + of :class:`~sqlalchemy.types.TypeEngine` + + * ``nullable`` - boolean flag if the column is NULL or NOT NULL + + * ``default`` - the column's server default value. This is + normally specified as a plain string SQL expression, however the + event can pass a :class:`.FetchedValue`, :class:`.DefaultClause`, + or :func:`.sql.expression.text` object as well. + + .. versionchanged:: 1.1.6 + + The :meth:`.DDLEvents.column_reflect` event allows a non + string :class:`.FetchedValue`, + :func:`.sql.expression.text`, or derived object to be + specified as the value of ``default`` in the column + dictionary. + + * ``attrs`` - dict containing optional column attributes + + The event is called before any action is taken against + this dictionary, and the contents can be modified. + The :class:`.Column` specific arguments ``info``, ``key``, + and ``quote`` can also be added to the dictionary and + will be passed to the constructor of :class:`.Column`. + + Note that this event is only meaningful if either + associated with the :class:`.Table` class across the + board, e.g.:: + + from sqlalchemy.schema import Table + from sqlalchemy import event + + def listen_for_reflect(inspector, table, column_info): + "receive a column_reflect event" + # ... + + event.listen( + Table, + 'column_reflect', + listen_for_reflect) + + ...or with a specific :class:`.Table` instance using + the ``listeners`` argument:: + + def listen_for_reflect(inspector, table, column_info): + "receive a column_reflect event" + # ... + + t = Table( + 'sometable', + autoload=True, + listeners=[ + ('column_reflect', listen_for_reflect) + ]) + + This because the reflection process initiated by ``autoload=True`` + completes within the scope of the constructor for :class:`.Table`. + + :func:`.event.listen` also accepts the ``propagate=True`` + modifier for this event; when True, the listener function will + be established for any copies made of the target object, + i.e. those copies that are generated when + :meth:`.Table.tometadata` is used. + + """ + + +class PoolEvents(event.Events): + """Available events for :class:`.Pool`. + + The methods here define the name of an event as well + as the names of members that are passed to listener + functions. + + e.g.:: + + from sqlalchemy import event + + def my_on_checkout(dbapi_conn, connection_rec, connection_proxy): + "handle an on checkout event" + + event.listen(Pool, 'checkout', my_on_checkout) + + In addition to accepting the :class:`.Pool` class and + :class:`.Pool` instances, :class:`.PoolEvents` also accepts + :class:`.Engine` objects and the :class:`.Engine` class as + targets, which will be resolved to the ``.pool`` attribute of the + given engine or the :class:`.Pool` class:: + + engine = create_engine("postgresql://scott:tiger@localhost/test") + + # will associate with engine.pool + event.listen(engine, 'checkout', my_on_checkout) + + """ + + _target_class_doc = "SomeEngineOrPool" + _dispatch_target = Pool + + @classmethod + def _accept_with(cls, target): + if isinstance(target, type): + if issubclass(target, Engine): + return Pool + elif issubclass(target, Pool): + return target + elif isinstance(target, Engine): + return target.pool + else: + return target + + def connect(self, dbapi_connection, connection_record): + """Called at the moment a particular DBAPI connection is first + created for a given :class:`.Pool`. + + This event allows one to capture the point directly after which + the DBAPI module-level ``.connect()`` method has been used in order + to produce a new DBAPI connection. + + :param dbapi_connection: a DBAPI connection. + + :param connection_record: the :class:`._ConnectionRecord` managing the + DBAPI connection. + + """ + + def first_connect(self, dbapi_connection, connection_record): + """Called exactly once for the first time a DBAPI connection is + checked out from a particular :class:`.Pool`. + + The rationale for :meth:`.PoolEvents.first_connect` is to determine + information about a particular series of database connections based + on the settings used for all connections. Since a particular + :class:`.Pool` refers to a single "creator" function (which in terms + of a :class:`.Engine` refers to the URL and connection options used), + it is typically valid to make observations about a single connection + that can be safely assumed to be valid about all subsequent + connections, such as the database version, the server and client + encoding settings, collation settings, and many others. + + :param dbapi_connection: a DBAPI connection. + + :param connection_record: the :class:`._ConnectionRecord` managing the + DBAPI connection. + + """ + + def checkout(self, dbapi_connection, connection_record, connection_proxy): + """Called when a connection is retrieved from the Pool. + + :param dbapi_connection: a DBAPI connection. + + :param connection_record: the :class:`._ConnectionRecord` managing the + DBAPI connection. + + :param connection_proxy: the :class:`._ConnectionFairy` object which + will proxy the public interface of the DBAPI connection for the + lifespan of the checkout. + + If you raise a :class:`~sqlalchemy.exc.DisconnectionError`, the current + connection will be disposed and a fresh connection retrieved. + Processing of all checkout listeners will abort and restart + using the new connection. + + .. seealso:: :meth:`.ConnectionEvents.engine_connect` - a similar event + which occurs upon creation of a new :class:`.Connection`. + + """ + + def checkin(self, dbapi_connection, connection_record): + """Called when a connection returns to the pool. + + Note that the connection may be closed, and may be None if the + connection has been invalidated. ``checkin`` will not be called + for detached connections. (They do not return to the pool.) + + :param dbapi_connection: a DBAPI connection. + + :param connection_record: the :class:`._ConnectionRecord` managing the + DBAPI connection. + + """ + + def reset(self, dbapi_connection, connection_record): + """Called before the "reset" action occurs for a pooled connection. + + This event represents + when the ``rollback()`` method is called on the DBAPI connection + before it is returned to the pool. The behavior of "reset" can + be controlled, including disabled, using the ``reset_on_return`` + pool argument. + + + The :meth:`.PoolEvents.reset` event is usually followed by the + :meth:`.PoolEvents.checkin` event is called, except in those + cases where the connection is discarded immediately after reset. + + :param dbapi_connection: a DBAPI connection. + + :param connection_record: the :class:`._ConnectionRecord` managing the + DBAPI connection. + + .. versionadded:: 0.8 + + .. seealso:: + + :meth:`.ConnectionEvents.rollback` + + :meth:`.ConnectionEvents.commit` + + """ + + def invalidate(self, dbapi_connection, connection_record, exception): + """Called when a DBAPI connection is to be "invalidated". + + This event is called any time the :meth:`._ConnectionRecord.invalidate` + method is invoked, either from API usage or via "auto-invalidation", + without the ``soft`` flag. + + The event occurs before a final attempt to call ``.close()`` on the + connection occurs. + + :param dbapi_connection: a DBAPI connection. + + :param connection_record: the :class:`._ConnectionRecord` managing the + DBAPI connection. + + :param exception: the exception object corresponding to the reason + for this invalidation, if any. May be ``None``. + + .. versionadded:: 0.9.2 Added support for connection invalidation + listening. + + .. seealso:: + + :ref:`pool_connection_invalidation` + + """ + + def soft_invalidate(self, dbapi_connection, connection_record, exception): + """Called when a DBAPI connection is to be "soft invalidated". + + This event is called any time the :meth:`._ConnectionRecord.invalidate` + method is invoked with the ``soft`` flag. + + Soft invalidation refers to when the connection record that tracks + this connection will force a reconnect after the current connection + is checked in. It does not actively close the dbapi_connection + at the point at which it is called. + + .. versionadded:: 1.0.3 + + """ + + def close(self, dbapi_connection, connection_record): + """Called when a DBAPI connection is closed. + + The event is emitted before the close occurs. + + The close of a connection can fail; typically this is because + the connection is already closed. If the close operation fails, + the connection is discarded. + + The :meth:`.close` event corresponds to a connection that's still + associated with the pool. To intercept close events for detached + connections use :meth:`.close_detached`. + + .. versionadded:: 1.1 + + """ + + def detach(self, dbapi_connection, connection_record): + """Called when a DBAPI connection is "detached" from a pool. + + This event is emitted after the detach occurs. The connection + is no longer associated with the given connection record. + + .. versionadded:: 1.1 + + """ + + def close_detached(self, dbapi_connection): + """Called when a detached DBAPI connection is closed. + + The event is emitted before the close occurs. + + The close of a connection can fail; typically this is because + the connection is already closed. If the close operation fails, + the connection is discarded. + + .. versionadded:: 1.1 + + """ + + +class ConnectionEvents(event.Events): + """Available events for :class:`.Connectable`, which includes + :class:`.Connection` and :class:`.Engine`. + + The methods here define the name of an event as well as the names of + members that are passed to listener functions. + + An event listener can be associated with any :class:`.Connectable` + class or instance, such as an :class:`.Engine`, e.g.:: + + from sqlalchemy import event, create_engine + + def before_cursor_execute(conn, cursor, statement, parameters, context, + executemany): + log.info("Received statement: %s", statement) + + engine = create_engine('postgresql://scott:tiger@localhost/test') + event.listen(engine, "before_cursor_execute", before_cursor_execute) + + or with a specific :class:`.Connection`:: + + with engine.begin() as conn: + @event.listens_for(conn, 'before_cursor_execute') + def before_cursor_execute(conn, cursor, statement, parameters, + context, executemany): + log.info("Received statement: %s", statement) + + When the methods are called with a `statement` parameter, such as in + :meth:`.after_cursor_execute`, :meth:`.before_cursor_execute` and + :meth:`.dbapi_error`, the statement is the exact SQL string that was + prepared for transmission to the DBAPI ``cursor`` in the connection's + :class:`.Dialect`. + + The :meth:`.before_execute` and :meth:`.before_cursor_execute` + events can also be established with the ``retval=True`` flag, which + allows modification of the statement and parameters to be sent + to the database. The :meth:`.before_cursor_execute` event is + particularly useful here to add ad-hoc string transformations, such + as comments, to all executions:: + + from sqlalchemy.engine import Engine + from sqlalchemy import event + + @event.listens_for(Engine, "before_cursor_execute", retval=True) + def comment_sql_calls(conn, cursor, statement, parameters, + context, executemany): + statement = statement + " -- some comment" + return statement, parameters + + .. note:: :class:`.ConnectionEvents` can be established on any + combination of :class:`.Engine`, :class:`.Connection`, as well + as instances of each of those classes. Events across all + four scopes will fire off for a given instance of + :class:`.Connection`. However, for performance reasons, the + :class:`.Connection` object determines at instantiation time + whether or not its parent :class:`.Engine` has event listeners + established. Event listeners added to the :class:`.Engine` + class or to an instance of :class:`.Engine` *after* the instantiation + of a dependent :class:`.Connection` instance will usually + *not* be available on that :class:`.Connection` instance. The newly + added listeners will instead take effect for :class:`.Connection` + instances created subsequent to those event listeners being + established on the parent :class:`.Engine` class or instance. + + :param retval=False: Applies to the :meth:`.before_execute` and + :meth:`.before_cursor_execute` events only. When True, the + user-defined event function must have a return value, which + is a tuple of parameters that replace the given statement + and parameters. See those methods for a description of + specific return arguments. + + .. versionchanged:: 0.8 :class:`.ConnectionEvents` can now be associated + with any :class:`.Connectable` including :class:`.Connection`, + in addition to the existing support for :class:`.Engine`. + + """ + + _target_class_doc = "SomeEngine" + _dispatch_target = Connectable + + @classmethod + def _listen(cls, event_key, retval=False): + target, identifier, fn = \ + event_key.dispatch_target, event_key.identifier, \ + event_key._listen_fn + + target._has_events = True + + if not retval: + if identifier == 'before_execute': + orig_fn = fn + + def wrap_before_execute(conn, clauseelement, + multiparams, params): + orig_fn(conn, clauseelement, multiparams, params) + return clauseelement, multiparams, params + fn = wrap_before_execute + elif identifier == 'before_cursor_execute': + orig_fn = fn + + def wrap_before_cursor_execute(conn, cursor, statement, + parameters, context, + executemany): + orig_fn(conn, cursor, statement, + parameters, context, executemany) + return statement, parameters + fn = wrap_before_cursor_execute + elif retval and \ + identifier not in ('before_execute', + 'before_cursor_execute', 'handle_error'): + raise exc.ArgumentError( + "Only the 'before_execute', " + "'before_cursor_execute' and 'handle_error' engine " + "event listeners accept the 'retval=True' " + "argument.") + event_key.with_wrapper(fn).base_listen() + + def before_execute(self, conn, clauseelement, multiparams, params): + """Intercept high level execute() events, receiving uncompiled + SQL constructs and other objects prior to rendering into SQL. + + This event is good for debugging SQL compilation issues as well + as early manipulation of the parameters being sent to the database, + as the parameter lists will be in a consistent format here. + + This event can be optionally established with the ``retval=True`` + flag. The ``clauseelement``, ``multiparams``, and ``params`` + arguments should be returned as a three-tuple in this case:: + + @event.listens_for(Engine, "before_execute", retval=True) + def before_execute(conn, clauseelement, multiparams, params): + # do something with clauseelement, multiparams, params + return clauseelement, multiparams, params + + :param conn: :class:`.Connection` object + :param clauseelement: SQL expression construct, :class:`.Compiled` + instance, or string statement passed to :meth:`.Connection.execute`. + :param multiparams: Multiple parameter sets, a list of dictionaries. + :param params: Single parameter set, a single dictionary. + + See also: + + :meth:`.before_cursor_execute` + + """ + + def after_execute(self, conn, clauseelement, multiparams, params, result): + """Intercept high level execute() events after execute. + + + :param conn: :class:`.Connection` object + :param clauseelement: SQL expression construct, :class:`.Compiled` + instance, or string statement passed to :meth:`.Connection.execute`. + :param multiparams: Multiple parameter sets, a list of dictionaries. + :param params: Single parameter set, a single dictionary. + :param result: :class:`.ResultProxy` generated by the execution. + + """ + + def before_cursor_execute(self, conn, cursor, statement, + parameters, context, executemany): + """Intercept low-level cursor execute() events before execution, + receiving the string SQL statement and DBAPI-specific parameter list to + be invoked against a cursor. + + This event is a good choice for logging as well as late modifications + to the SQL string. It's less ideal for parameter modifications except + for those which are specific to a target backend. + + This event can be optionally established with the ``retval=True`` + flag. The ``statement`` and ``parameters`` arguments should be + returned as a two-tuple in this case:: + + @event.listens_for(Engine, "before_cursor_execute", retval=True) + def before_cursor_execute(conn, cursor, statement, + parameters, context, executemany): + # do something with statement, parameters + return statement, parameters + + See the example at :class:`.ConnectionEvents`. + + :param conn: :class:`.Connection` object + :param cursor: DBAPI cursor object + :param statement: string SQL statement, as to be passed to the DBAPI + :param parameters: Dictionary, tuple, or list of parameters being + passed to the ``execute()`` or ``executemany()`` method of the + DBAPI ``cursor``. In some cases may be ``None``. + :param context: :class:`.ExecutionContext` object in use. May + be ``None``. + :param executemany: boolean, if ``True``, this is an ``executemany()`` + call, if ``False``, this is an ``execute()`` call. + + See also: + + :meth:`.before_execute` + + :meth:`.after_cursor_execute` + + """ + + def after_cursor_execute(self, conn, cursor, statement, + parameters, context, executemany): + """Intercept low-level cursor execute() events after execution. + + :param conn: :class:`.Connection` object + :param cursor: DBAPI cursor object. Will have results pending + if the statement was a SELECT, but these should not be consumed + as they will be needed by the :class:`.ResultProxy`. + :param statement: string SQL statement, as passed to the DBAPI + :param parameters: Dictionary, tuple, or list of parameters being + passed to the ``execute()`` or ``executemany()`` method of the + DBAPI ``cursor``. In some cases may be ``None``. + :param context: :class:`.ExecutionContext` object in use. May + be ``None``. + :param executemany: boolean, if ``True``, this is an ``executemany()`` + call, if ``False``, this is an ``execute()`` call. + + """ + + def dbapi_error(self, conn, cursor, statement, parameters, + context, exception): + """Intercept a raw DBAPI error. + + This event is called with the DBAPI exception instance + received from the DBAPI itself, *before* SQLAlchemy wraps the + exception with it's own exception wrappers, and before any + other operations are performed on the DBAPI cursor; the + existing transaction remains in effect as well as any state + on the cursor. + + The use case here is to inject low-level exception handling + into an :class:`.Engine`, typically for logging and + debugging purposes. + + .. warning:: + + Code should **not** modify + any state or throw any exceptions here as this will + interfere with SQLAlchemy's cleanup and error handling + routines. For exception modification, please refer to the + new :meth:`.ConnectionEvents.handle_error` event. + + Subsequent to this hook, SQLAlchemy may attempt any + number of operations on the connection/cursor, including + closing the cursor, rolling back of the transaction in the + case of connectionless execution, and disposing of the entire + connection pool if a "disconnect" was detected. The + exception is then wrapped in a SQLAlchemy DBAPI exception + wrapper and re-thrown. + + :param conn: :class:`.Connection` object + :param cursor: DBAPI cursor object + :param statement: string SQL statement, as passed to the DBAPI + :param parameters: Dictionary, tuple, or list of parameters being + passed to the ``execute()`` or ``executemany()`` method of the + DBAPI ``cursor``. In some cases may be ``None``. + :param context: :class:`.ExecutionContext` object in use. May + be ``None``. + :param exception: The **unwrapped** exception emitted directly from the + DBAPI. The class here is specific to the DBAPI module in use. + + .. deprecated:: 0.9.7 - replaced by + :meth:`.ConnectionEvents.handle_error` + + """ + + def handle_error(self, exception_context): + r"""Intercept all exceptions processed by the :class:`.Connection`. + + This includes all exceptions emitted by the DBAPI as well as + within SQLAlchemy's statement invocation process, including + encoding errors and other statement validation errors. Other areas + in which the event is invoked include transaction begin and end, + result row fetching, cursor creation. + + Note that :meth:`.handle_error` may support new kinds of exceptions + and new calling scenarios at *any time*. Code which uses this + event must expect new calling patterns to be present in minor + releases. + + To support the wide variety of members that correspond to an exception, + as well as to allow extensibility of the event without backwards + incompatibility, the sole argument received is an instance of + :class:`.ExceptionContext`. This object contains data members + representing detail about the exception. + + Use cases supported by this hook include: + + * read-only, low-level exception handling for logging and + debugging purposes + * exception re-writing + * Establishing or disabling whether a connection or the owning + connection pool is invalidated or expired in response to a + specific exception. + + The hook is called while the cursor from the failed operation + (if any) is still open and accessible. Special cleanup operations + can be called on this cursor; SQLAlchemy will attempt to close + this cursor subsequent to this hook being invoked. If the connection + is in "autocommit" mode, the transaction also remains open within + the scope of this hook; the rollback of the per-statement transaction + also occurs after the hook is called. + + For the common case of detecting a "disconnect" situation which + is not currently handled by the SQLAlchemy dialect, the + :attr:`.ExceptionContext.is_disconnect` flag can be set to True which + will cause the exception to be considered as a disconnect situation, + which typically results in the connection pool being invalidated:: + + @event.listens_for(Engine, "handle_error") + def handle_exception(context): + if isinstance(context.original_exception, pyodbc.Error): + for code in ( + '08S01', '01002', '08003', + '08007', '08S02', '08001', 'HYT00', 'HY010'): + + if code in str(context.original_exception): + context.is_disconnect = True + + A handler function has two options for replacing + the SQLAlchemy-constructed exception into one that is user + defined. It can either raise this new exception directly, in + which case all further event listeners are bypassed and the + exception will be raised, after appropriate cleanup as taken + place:: + + @event.listens_for(Engine, "handle_error") + def handle_exception(context): + if isinstance(context.original_exception, + psycopg2.OperationalError) and \ + "failed" in str(context.original_exception): + raise MySpecialException("failed operation") + + .. warning:: Because the :meth:`.ConnectionEvents.handle_error` + event specifically provides for exceptions to be re-thrown as + the ultimate exception raised by the failed statement, + **stack traces will be misleading** if the user-defined event + handler itself fails and throws an unexpected exception; + the stack trace may not illustrate the actual code line that + failed! It is advised to code carefully here and use + logging and/or inline debugging if unexpected exceptions are + occurring. + + Alternatively, a "chained" style of event handling can be + used, by configuring the handler with the ``retval=True`` + modifier and returning the new exception instance from the + function. In this case, event handling will continue onto the + next handler. The "chained" exception is available using + :attr:`.ExceptionContext.chained_exception`:: + + @event.listens_for(Engine, "handle_error", retval=True) + def handle_exception(context): + if context.chained_exception is not None and \ + "special" in context.chained_exception.message: + return MySpecialException("failed", + cause=context.chained_exception) + + Handlers that return ``None`` may be used within the chain; when + a handler returns ``None``, the previous exception instance, + if any, is maintained as the current exception that is passed onto the + next handler. + + When a custom exception is raised or returned, SQLAlchemy raises + this new exception as-is, it is not wrapped by any SQLAlchemy + object. If the exception is not a subclass of + :class:`sqlalchemy.exc.StatementError`, + certain features may not be available; currently this includes + the ORM's feature of adding a detail hint about "autoflush" to + exceptions raised within the autoflush process. + + :param context: an :class:`.ExceptionContext` object. See this + class for details on all available members. + + .. versionadded:: 0.9.7 Added the + :meth:`.ConnectionEvents.handle_error` hook. + + .. versionchanged:: 1.1 The :meth:`.handle_error` event will now + receive all exceptions that inherit from ``BaseException``, including + ``SystemExit`` and ``KeyboardInterrupt``. The setting for + :attr:`.ExceptionContext.is_disconnect` is ``True`` in this case + and the default for :attr:`.ExceptionContext.invalidate_pool_on_disconnect` + is ``False``. + + .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is now + invoked when an :class:`.Engine` fails during the initial + call to :meth:`.Engine.connect`, as well as when a + :class:`.Connection` object encounters an error during a + reconnect operation. + + .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is + not fired off when a dialect makes use of the + ``skip_user_error_events`` execution option. This is used + by dialects which intend to catch SQLAlchemy-specific exceptions + within specific operations, such as when the MySQL dialect detects + a table not present within the ``has_table()`` dialect method. + Prior to 1.0.0, code which implements :meth:`.handle_error` needs + to ensure that exceptions thrown in these scenarios are re-raised + without modification. + + """ + + def engine_connect(self, conn, branch): + """Intercept the creation of a new :class:`.Connection`. + + This event is called typically as the direct result of calling + the :meth:`.Engine.connect` method. + + It differs from the :meth:`.PoolEvents.connect` method, which + refers to the actual connection to a database at the DBAPI level; + a DBAPI connection may be pooled and reused for many operations. + In contrast, this event refers only to the production of a higher level + :class:`.Connection` wrapper around such a DBAPI connection. + + It also differs from the :meth:`.PoolEvents.checkout` event + in that it is specific to the :class:`.Connection` object, not the + DBAPI connection that :meth:`.PoolEvents.checkout` deals with, although + this DBAPI connection is available here via the + :attr:`.Connection.connection` attribute. But note there can in fact + be multiple :meth:`.PoolEvents.checkout` events within the lifespan + of a single :class:`.Connection` object, if that :class:`.Connection` + is invalidated and re-established. There can also be multiple + :class:`.Connection` objects generated for the same already-checked-out + DBAPI connection, in the case that a "branch" of a :class:`.Connection` + is produced. + + :param conn: :class:`.Connection` object. + :param branch: if True, this is a "branch" of an existing + :class:`.Connection`. A branch is generated within the course + of a statement execution to invoke supplemental statements, most + typically to pre-execute a SELECT of a default value for the purposes + of an INSERT statement. + + .. versionadded:: 0.9.0 + + .. seealso:: + + :ref:`pool_disconnects_pessimistic` - illustrates how to use + :meth:`.ConnectionEvents.engine_connect` + to transparently ensure pooled connections are connected to the + database. + + :meth:`.PoolEvents.checkout` the lower-level pool checkout event + for an individual DBAPI connection + + :meth:`.ConnectionEvents.set_connection_execution_options` - a copy + of a :class:`.Connection` is also made when the + :meth:`.Connection.execution_options` method is called. + + """ + + def set_connection_execution_options(self, conn, opts): + """Intercept when the :meth:`.Connection.execution_options` + method is called. + + This method is called after the new :class:`.Connection` has been + produced, with the newly updated execution options collection, but + before the :class:`.Dialect` has acted upon any of those new options. + + Note that this method is not called when a new :class:`.Connection` + is produced which is inheriting execution options from its parent + :class:`.Engine`; to intercept this condition, use the + :meth:`.ConnectionEvents.engine_connect` event. + + :param conn: The newly copied :class:`.Connection` object + + :param opts: dictionary of options that were passed to the + :meth:`.Connection.execution_options` method. + + .. versionadded:: 0.9.0 + + .. seealso:: + + :meth:`.ConnectionEvents.set_engine_execution_options` - event + which is called when :meth:`.Engine.execution_options` is called. + + + """ + + def set_engine_execution_options(self, engine, opts): + """Intercept when the :meth:`.Engine.execution_options` + method is called. + + The :meth:`.Engine.execution_options` method produces a shallow + copy of the :class:`.Engine` which stores the new options. That new + :class:`.Engine` is passed here. A particular application of this + method is to add a :meth:`.ConnectionEvents.engine_connect` event + handler to the given :class:`.Engine` which will perform some per- + :class:`.Connection` task specific to these execution options. + + :param conn: The newly copied :class:`.Engine` object + + :param opts: dictionary of options that were passed to the + :meth:`.Connection.execution_options` method. + + .. versionadded:: 0.9.0 + + .. seealso:: + + :meth:`.ConnectionEvents.set_connection_execution_options` - event + which is called when :meth:`.Connection.execution_options` is + called. + + """ + + def engine_disposed(self, engine): + """Intercept when the :meth:`.Engine.dispose` method is called. + + The :meth:`.Engine.dispose` method instructs the engine to + "dispose" of it's connection pool (e.g. :class:`.Pool`), and + replaces it with a new one. Disposing of the old pool has the + effect that existing checked-in connections are closed. The new + pool does not establish any new connections until it is first used. + + This event can be used to indicate that resources related to the + :class:`.Engine` should also be cleaned up, keeping in mind that the + :class:`.Engine` can still be used for new requests in which case + it re-acquires connection resources. + + .. versionadded:: 1.0.5 + + """ + def begin(self, conn): + """Intercept begin() events. + + :param conn: :class:`.Connection` object + + """ + + def rollback(self, conn): + """Intercept rollback() events, as initiated by a + :class:`.Transaction`. + + Note that the :class:`.Pool` also "auto-rolls back" + a DBAPI connection upon checkin, if the ``reset_on_return`` + flag is set to its default value of ``'rollback'``. + To intercept this + rollback, use the :meth:`.PoolEvents.reset` hook. + + :param conn: :class:`.Connection` object + + .. seealso:: + + :meth:`.PoolEvents.reset` + + """ + + def commit(self, conn): + """Intercept commit() events, as initiated by a + :class:`.Transaction`. + + Note that the :class:`.Pool` may also "auto-commit" + a DBAPI connection upon checkin, if the ``reset_on_return`` + flag is set to the value ``'commit'``. To intercept this + commit, use the :meth:`.PoolEvents.reset` hook. + + :param conn: :class:`.Connection` object + """ + + def savepoint(self, conn, name): + """Intercept savepoint() events. + + :param conn: :class:`.Connection` object + :param name: specified name used for the savepoint. + + """ + + def rollback_savepoint(self, conn, name, context): + """Intercept rollback_savepoint() events. + + :param conn: :class:`.Connection` object + :param name: specified name used for the savepoint. + :param context: :class:`.ExecutionContext` in use. May be ``None``. + + """ + + def release_savepoint(self, conn, name, context): + """Intercept release_savepoint() events. + + :param conn: :class:`.Connection` object + :param name: specified name used for the savepoint. + :param context: :class:`.ExecutionContext` in use. May be ``None``. + + """ + + def begin_twophase(self, conn, xid): + """Intercept begin_twophase() events. + + :param conn: :class:`.Connection` object + :param xid: two-phase XID identifier + + """ + + def prepare_twophase(self, conn, xid): + """Intercept prepare_twophase() events. + + :param conn: :class:`.Connection` object + :param xid: two-phase XID identifier + """ + + def rollback_twophase(self, conn, xid, is_prepared): + """Intercept rollback_twophase() events. + + :param conn: :class:`.Connection` object + :param xid: two-phase XID identifier + :param is_prepared: boolean, indicates if + :meth:`.TwoPhaseTransaction.prepare` was called. + + """ + + def commit_twophase(self, conn, xid, is_prepared): + """Intercept commit_twophase() events. + + :param conn: :class:`.Connection` object + :param xid: two-phase XID identifier + :param is_prepared: boolean, indicates if + :meth:`.TwoPhaseTransaction.prepare` was called. + + """ + + +class DialectEvents(event.Events): + """event interface for execution-replacement functions. + + These events allow direct instrumentation and replacement + of key dialect functions which interact with the DBAPI. + + .. note:: + + :class:`.DialectEvents` hooks should be considered **semi-public** + and experimental. + These hooks are not for general use and are only for those situations + where intricate re-statement of DBAPI mechanics must be injected onto + an existing dialect. For general-use statement-interception events, + please use the :class:`.ConnectionEvents` interface. + + .. seealso:: + + :meth:`.ConnectionEvents.before_cursor_execute` + + :meth:`.ConnectionEvents.before_execute` + + :meth:`.ConnectionEvents.after_cursor_execute` + + :meth:`.ConnectionEvents.after_execute` + + + .. versionadded:: 0.9.4 + + """ + + _target_class_doc = "SomeEngine" + _dispatch_target = Dialect + + @classmethod + def _listen(cls, event_key, retval=False): + target, identifier, fn = \ + event_key.dispatch_target, event_key.identifier, event_key.fn + + target._has_events = True + event_key.base_listen() + + @classmethod + def _accept_with(cls, target): + if isinstance(target, type): + if issubclass(target, Engine): + return Dialect + elif issubclass(target, Dialect): + return target + elif isinstance(target, Engine): + return target.dialect + else: + return target + + def do_connect(self, dialect, conn_rec, cargs, cparams): + """Receive connection arguments before a connection is made. + + Return a DBAPI connection to halt further events from invoking; + the returned connection will be used. + + Alternatively, the event can manipulate the cargs and/or cparams + collections; cargs will always be a Python list that can be mutated + in-place and cparams a Python dictionary. Return None to + allow control to pass to the next event handler and ultimately + to allow the dialect to connect normally, given the updated + arguments. + + .. versionadded:: 1.0.3 + + """ + + def do_executemany(self, cursor, statement, parameters, context): + """Receive a cursor to have executemany() called. + + Return the value True to halt further events from invoking, + and to indicate that the cursor execution has already taken + place within the event handler. + + """ + + def do_execute_no_params(self, cursor, statement, context): + """Receive a cursor to have execute() with no parameters called. + + Return the value True to halt further events from invoking, + and to indicate that the cursor execution has already taken + place within the event handler. + + """ + + def do_execute(self, cursor, statement, parameters, context): + """Receive a cursor to have execute() called. + + Return the value True to halt further events from invoking, + and to indicate that the cursor execution has already taken + place within the event handler. + + """ + + def do_setinputsizes(self, + inputsizes, cursor, statement, parameters, context): + """Receive the setinputsizes dictionary for possible modification. + + This event is emitted in the case where the dialect makes use of the + DBAPI ``cursor.setinputsizes()`` method which passes information about + parameter binding for a particular statement. The given + ``inputsizes`` dictionary will contain :class:`.BindParameter` objects + as keys, linked to DBAPI-specific type objects as values; for + parameters that are not bound, they are added to the dictionary with + ``None`` as the value, which means the parameter will not be included + in the ultimate setinputsizes call. The event may be used to inspect + and/or log the datatypes that are being bound, as well as to modify the + dictionary in place. Parameters can be added, modified, or removed + from this dictionary. Callers will typically want to inspect the + :attr:`.BindParameter.type` attribute of the given bind objects in + order to make decisions about the DBAPI object. + + After the event, the ``inputsizes`` dictionary is converted into + an appropriate datastructure to be passed to ``cursor.setinputsizes``; + either a list for a positional bound parameter execution style, + or a dictionary of string parameter keys to DBAPI type objects for + a named bound parameter execution style. + + Most dialects **do not use** this method at all; the only built-in + dialect which uses this hook is the cx_Oracle dialect. The hook here + is made available so as to allow customization of how datatypes are set + up with the cx_Oracle DBAPI. + + .. versionadded:: 1.2.9 + + .. seealso:: + + :ref:`cx_oracle_setinputsizes` + + """ + pass diff --git a/venv/Lib/site-packages/sqlalchemy/exc.py b/venv/Lib/site-packages/sqlalchemy/exc.py new file mode 100644 index 0000000..40dcb7c --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/exc.py @@ -0,0 +1,480 @@ +# sqlalchemy/exc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Exceptions used with SQLAlchemy. + +The base exception class is :exc:`.SQLAlchemyError`. Exceptions which are +raised as a result of DBAPI exceptions are all subclasses of +:exc:`.DBAPIError`. + +""" + +from .util import compat + + +class SQLAlchemyError(Exception): + """Generic error class.""" + + code = None + + def __init__(self, *arg, **kw): + code = kw.pop('code', None) + if code is not None: + self.code = code + super(SQLAlchemyError, self).__init__(*arg, **kw) + + def _code_str(self): + if not self.code: + return "" + else: + return ( + "(Background on this error at: " + "http://sqlalche.me/e/%s)" % (self.code, ) + ) + + def _message(self): + # get string representation just like Exception.__str__(self), + # but also support if the string has non-ascii chars + if len(self.args) == 1: + return compat.text_type(self.args[0]) + else: + return compat.text_type(self.args) + + def __str__(self): + message = self._message() + + if self.code: + message = ( + "%s %s" % (message, self._code_str()) + ) + + return message + + def __unicode__(self): + return self.__str__() + + +class ArgumentError(SQLAlchemyError): + """Raised when an invalid or conflicting function argument is supplied. + + This error generally corresponds to construction time state errors. + + """ + + +class ObjectNotExecutableError(ArgumentError): + """Raised when an object is passed to .execute() that can't be + executed as SQL. + + .. versionadded:: 1.1 + + """ + + def __init__(self, target): + super(ObjectNotExecutableError, self).__init__( + "Not an executable object: %r" % target + ) + + +class NoSuchModuleError(ArgumentError): + """Raised when a dynamically-loaded module (usually a database dialect) + of a particular name cannot be located.""" + + +class NoForeignKeysError(ArgumentError): + """Raised when no foreign keys can be located between two selectables + during a join.""" + + +class AmbiguousForeignKeysError(ArgumentError): + """Raised when more than one foreign key matching can be located + between two selectables during a join.""" + + +class CircularDependencyError(SQLAlchemyError): + """Raised by topological sorts when a circular dependency is detected. + + There are two scenarios where this error occurs: + + * In a Session flush operation, if two objects are mutually dependent + on each other, they can not be inserted or deleted via INSERT or + DELETE statements alone; an UPDATE will be needed to post-associate + or pre-deassociate one of the foreign key constrained values. + The ``post_update`` flag described at :ref:`post_update` can resolve + this cycle. + * In a :attr:`.MetaData.sorted_tables` operation, two :class:`.ForeignKey` + or :class:`.ForeignKeyConstraint` objects mutually refer to each + other. Apply the ``use_alter=True`` flag to one or both, + see :ref:`use_alter`. + + """ + def __init__(self, message, cycles, edges, msg=None, code=None): + if msg is None: + message += " (%s)" % ", ".join(repr(s) for s in cycles) + else: + message = msg + SQLAlchemyError.__init__(self, message, code=code) + self.cycles = cycles + self.edges = edges + + def __reduce__(self): + return self.__class__, (None, self.cycles, + self.edges, self.args[0]) + + +class CompileError(SQLAlchemyError): + """Raised when an error occurs during SQL compilation""" + + +class UnsupportedCompilationError(CompileError): + """Raised when an operation is not supported by the given compiler. + + + .. versionadded:: 0.8.3 + + """ + + def __init__(self, compiler, element_type): + super(UnsupportedCompilationError, self).__init__( + "Compiler %r can't render element of type %s" % + (compiler, element_type)) + + +class IdentifierError(SQLAlchemyError): + """Raised when a schema name is beyond the max character limit""" + + +class DisconnectionError(SQLAlchemyError): + """A disconnect is detected on a raw DB-API connection. + + This error is raised and consumed internally by a connection pool. It can + be raised by the :meth:`.PoolEvents.checkout` event so that the host pool + forces a retry; the exception will be caught three times in a row before + the pool gives up and raises :class:`~sqlalchemy.exc.InvalidRequestError` + regarding the connection attempt. + + """ + invalidate_pool = False + + +class InvalidatePoolError(DisconnectionError): + """Raised when the connection pool should invalidate all stale connections. + + A subclass of :class:`.DisconnectionError` that indicates that the + disconnect situation encountered on the connection probably means the + entire pool should be invalidated, as the database has been restarted. + + This exception will be handled otherwise the same way as + :class:`.DisconnectionError`, allowing three attempts to reconnect + before giving up. + + .. versionadded:: 1.2 + + """ + invalidate_pool = True + + +class TimeoutError(SQLAlchemyError): + """Raised when a connection pool times out on getting a connection.""" + + +class InvalidRequestError(SQLAlchemyError): + """SQLAlchemy was asked to do something it can't do. + + This error generally corresponds to runtime state errors. + + """ + + +class NoInspectionAvailable(InvalidRequestError): + """A subject passed to :func:`sqlalchemy.inspection.inspect` produced + no context for inspection.""" + + +class ResourceClosedError(InvalidRequestError): + """An operation was requested from a connection, cursor, or other + object that's in a closed state.""" + + +class NoSuchColumnError(KeyError, InvalidRequestError): + """A nonexistent column is requested from a ``RowProxy``.""" + + +class NoReferenceError(InvalidRequestError): + """Raised by ``ForeignKey`` to indicate a reference cannot be resolved.""" + + +class NoReferencedTableError(NoReferenceError): + """Raised by ``ForeignKey`` when the referred ``Table`` cannot be + located. + + """ + def __init__(self, message, tname): + NoReferenceError.__init__(self, message) + self.table_name = tname + + def __reduce__(self): + return self.__class__, (self.args[0], self.table_name) + + +class NoReferencedColumnError(NoReferenceError): + """Raised by ``ForeignKey`` when the referred ``Column`` cannot be + located. + + """ + def __init__(self, message, tname, cname): + NoReferenceError.__init__(self, message) + self.table_name = tname + self.column_name = cname + + def __reduce__(self): + return self.__class__, (self.args[0], self.table_name, + self.column_name) + + +class NoSuchTableError(InvalidRequestError): + """Table does not exist or is not visible to a connection.""" + + +class UnreflectableTableError(InvalidRequestError): + """Table exists but can't be reflectted for some reason. + + .. versionadded:: 1.2 + + """ + + +class UnboundExecutionError(InvalidRequestError): + """SQL was attempted without a database connection to execute it on.""" + + +class DontWrapMixin(object): + """A mixin class which, when applied to a user-defined Exception class, + will not be wrapped inside of :exc:`.StatementError` if the error is + emitted within the process of executing a statement. + + E.g.:: + + from sqlalchemy.exc import DontWrapMixin + + class MyCustomException(Exception, DontWrapMixin): + pass + + class MySpecialType(TypeDecorator): + impl = String + + def process_bind_param(self, value, dialect): + if value == 'invalid': + raise MyCustomException("invalid!") + + """ + +# Moved to orm.exc; compatibility definition installed by orm import until 0.6 +UnmappedColumnError = None + + +class StatementError(SQLAlchemyError): + """An error occurred during execution of a SQL statement. + + :class:`StatementError` wraps the exception raised + during execution, and features :attr:`.statement` + and :attr:`.params` attributes which supply context regarding + the specifics of the statement which had an issue. + + The wrapped exception object is available in + the :attr:`.orig` attribute. + + """ + + statement = None + """The string SQL statement being invoked when this exception occurred.""" + + params = None + """The parameter list being used when this exception occurred.""" + + orig = None + """The DBAPI exception object.""" + + def __init__(self, message, statement, params, orig, code=None): + SQLAlchemyError.__init__(self, message, code=code) + self.statement = statement + self.params = params + self.orig = orig + self.detail = [] + + def add_detail(self, msg): + self.detail.append(msg) + + def __reduce__(self): + return self.__class__, (self.args[0], self.statement, + self.params, self.orig) + + def __str__(self): + from sqlalchemy.sql import util + + details = [self._message()] + if self.statement: + details.append("[SQL: %r]" % self.statement) + if self.params: + params_repr = util._repr_params(self.params, 10) + details.append("[parameters: %r]" % params_repr) + code_str = self._code_str() + if code_str: + details.append(code_str) + return ' '.join([ + "(%s)" % det for det in self.detail + ] + details) + + +class DBAPIError(StatementError): + """Raised when the execution of a database operation fails. + + Wraps exceptions raised by the DB-API underlying the + database operation. Driver-specific implementations of the standard + DB-API exception types are wrapped by matching sub-types of SQLAlchemy's + :class:`DBAPIError` when possible. DB-API's ``Error`` type maps to + :class:`DBAPIError` in SQLAlchemy, otherwise the names are identical. Note + that there is no guarantee that different DB-API implementations will + raise the same exception type for any given error condition. + + :class:`DBAPIError` features :attr:`~.StatementError.statement` + and :attr:`~.StatementError.params` attributes which supply context + regarding the specifics of the statement which had an issue, for the + typical case when the error was raised within the context of + emitting a SQL statement. + + The wrapped exception object is available in the + :attr:`~.StatementError.orig` attribute. Its type and properties are + DB-API implementation specific. + + """ + + code = 'dbapi' + + @classmethod + def instance(cls, statement, params, + orig, dbapi_base_err, + connection_invalidated=False, + dialect=None): + # Don't ever wrap these, just return them directly as if + # DBAPIError didn't exist. + if (isinstance(orig, BaseException) and + not isinstance(orig, Exception)) or \ + isinstance(orig, DontWrapMixin): + return orig + + if orig is not None: + # not a DBAPI error, statement is present. + # raise a StatementError + if isinstance(orig, SQLAlchemyError) and statement: + return StatementError( + "(%s.%s) %s" % + (orig.__class__.__module__, orig.__class__.__name__, + orig.args[0]), + statement, params, orig, code=orig.code + ) + elif not isinstance(orig, dbapi_base_err) and statement: + return StatementError( + "(%s.%s) %s" % + (orig.__class__.__module__, orig.__class__.__name__, + orig), + statement, params, orig + ) + + glob = globals() + for super_ in orig.__class__.__mro__: + name = super_.__name__ + if dialect: + name = dialect.dbapi_exception_translation_map.get( + name, name) + if name in glob and issubclass(glob[name], DBAPIError): + cls = glob[name] + break + + return cls(statement, params, orig, connection_invalidated, + code=cls.code) + + def __reduce__(self): + return self.__class__, (self.statement, self.params, + self.orig, self.connection_invalidated) + + def __init__(self, statement, params, orig, connection_invalidated=False, + code=None): + try: + text = str(orig) + except Exception as e: + text = 'Error in str() of DB-API-generated exception: ' + str(e) + StatementError.__init__( + self, + '(%s.%s) %s' % ( + orig.__class__.__module__, orig.__class__.__name__, text, ), + statement, + params, + orig, code=code + ) + self.connection_invalidated = connection_invalidated + + +class InterfaceError(DBAPIError): + """Wraps a DB-API InterfaceError.""" + + code = "rvf5" + + +class DatabaseError(DBAPIError): + """Wraps a DB-API DatabaseError.""" + + code = "4xp6" + + +class DataError(DatabaseError): + """Wraps a DB-API DataError.""" + + code = "9h9h" + + +class OperationalError(DatabaseError): + """Wraps a DB-API OperationalError.""" + + code = "e3q8" + + +class IntegrityError(DatabaseError): + """Wraps a DB-API IntegrityError.""" + + code = "gkpj" + + +class InternalError(DatabaseError): + """Wraps a DB-API InternalError.""" + + code = "2j85" + + +class ProgrammingError(DatabaseError): + """Wraps a DB-API ProgrammingError.""" + + code = "f405" + + +class NotSupportedError(DatabaseError): + """Wraps a DB-API NotSupportedError.""" + + code = "tw8g" + +# Warnings + +class SADeprecationWarning(DeprecationWarning): + """Issued once per usage of a deprecated API.""" + + +class SAPendingDeprecationWarning(PendingDeprecationWarning): + """Issued once per usage of a deprecated API.""" + + +class SAWarning(RuntimeWarning): + """Issued at runtime.""" diff --git a/venv/Lib/site-packages/sqlalchemy/ext/__init__.py b/venv/Lib/site-packages/sqlalchemy/ext/__init__.py new file mode 100644 index 0000000..9558b2a --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/__init__.py @@ -0,0 +1,11 @@ +# ext/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from .. import util as _sa_util + +_sa_util.dependencies.resolve_all("sqlalchemy.ext") + diff --git a/venv/Lib/site-packages/sqlalchemy/ext/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/ext/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4eb45e2f43744d5c8783d8c7f624a85ae3b04e92 GIT binary patch literal 281 zcmYk0Jx&8L5Jqih5i3YI0e!T^rb&koLV<<?nurEjvYhn*&iZHBaaJ)cr{Mrx#w`_B zpu$T7BhB|s@uYdn+3aZlaU)igkWcb&SiUdw;%N>=6g9-s2Sy37?POah_C_gDlQpS| zuaT{2Dt1Ae*Asj((lpOt;{x{Kt~yAhABj{7lwxGkR6z$$fon8iI_?3z89+#5-bsI$ z+28nnygK9e2tD#R$9d$}fNk&{UwPlwx6;Wcu;4<yju^n!R_5yY0B+z$Z#e1zbtjuP gbIAX+c-G=VXs3e^i!P?}+<LV$a8c&hIJ!>R4<}MnhX4Qo literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/ext/__pycache__/baked.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/ext/__pycache__/baked.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93a1ae15e01293fd49488bef62e927b53977635c GIT binary patch literal 18706 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 Y$zEQ>wzRweZECMjr`jtORAV^*7etJ*UjP6A literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/ext/associationproxy.py b/venv/Lib/site-packages/sqlalchemy/ext/associationproxy.py new file mode 100644 index 0000000..e97b8c3 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/associationproxy.py @@ -0,0 +1,1145 @@ +# ext/associationproxy.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Contain the ``AssociationProxy`` class. + +The ``AssociationProxy`` is a Python property object which provides +transparent proxied access to the endpoint of an association object. + +See the example ``examples/association/proxied_association.py``. + +""" +import operator +import weakref +from .. import exc, orm, util +from ..orm import collections, interfaces +from ..sql import or_ +from .. import inspect + + +def association_proxy(target_collection, attr, **kw): + r"""Return a Python property implementing a view of a target + attribute which references an attribute on members of the + target. + + The returned value is an instance of :class:`.AssociationProxy`. + + Implements a Python property representing a relationship as a collection + of simpler values, or a scalar value. The proxied property will mimic + the collection type of the target (list, dict or set), or, in the case of + a one to one relationship, a simple scalar value. + + :param target_collection: Name of the attribute we'll proxy to. + This attribute is typically mapped by + :func:`~sqlalchemy.orm.relationship` to link to a target collection, but + can also be a many-to-one or non-scalar relationship. + + :param attr: Attribute on the associated instance or instances we'll + proxy for. + + For example, given a target collection of [obj1, obj2], a list created + by this proxy property would look like [getattr(obj1, *attr*), + getattr(obj2, *attr*)] + + If the relationship is one-to-one or otherwise uselist=False, then + simply: getattr(obj, *attr*) + + :param creator: optional. + + When new items are added to this proxied collection, new instances of + the class collected by the target collection will be created. For list + and set collections, the target class constructor will be called with + the 'value' for the new instance. For dict types, two arguments are + passed: key and value. + + If you want to construct instances differently, supply a *creator* + function that takes arguments as above and returns instances. + + For scalar relationships, creator() will be called if the target is None. + If the target is present, set operations are proxied to setattr() on the + associated object. + + If you have an associated object with multiple attributes, you may set + up multiple association proxies mapping to different attributes. See + the unit tests for examples, and for examples of how creator() functions + can be used to construct the scalar relationship on-demand in this + situation. + + :param \*\*kw: Passes along any other keyword arguments to + :class:`.AssociationProxy`. + + """ + return AssociationProxy(target_collection, attr, **kw) + + +ASSOCIATION_PROXY = util.symbol('ASSOCIATION_PROXY') +"""Symbol indicating an :class:`InspectionAttr` that's + of type :class:`.AssociationProxy`. + + Is assigned to the :attr:`.InspectionAttr.extension_type` + attibute. + +""" + + +class AssociationProxy(interfaces.InspectionAttrInfo): + """A descriptor that presents a read/write view of an object attribute.""" + + is_attribute = False + extension_type = ASSOCIATION_PROXY + + def __init__(self, target_collection, attr, creator=None, + getset_factory=None, proxy_factory=None, + proxy_bulk_set=None, info=None): + """Construct a new :class:`.AssociationProxy`. + + The :func:`.association_proxy` function is provided as the usual + entrypoint here, though :class:`.AssociationProxy` can be instantiated + and/or subclassed directly. + + :param target_collection: Name of the collection we'll proxy to, + usually created with :func:`.relationship`. + + :param attr: Attribute on the collected instances we'll proxy + for. For example, given a target collection of [obj1, obj2], a + list created by this proxy property would look like + [getattr(obj1, attr), getattr(obj2, attr)] + + :param creator: Optional. When new items are added to this proxied + collection, new instances of the class collected by the target + collection will be created. For list and set collections, the + target class constructor will be called with the 'value' for the + new instance. For dict types, two arguments are passed: + key and value. + + If you want to construct instances differently, supply a 'creator' + function that takes arguments as above and returns instances. + + :param getset_factory: Optional. Proxied attribute access is + automatically handled by routines that get and set values based on + the `attr` argument for this proxy. + + If you would like to customize this behavior, you may supply a + `getset_factory` callable that produces a tuple of `getter` and + `setter` functions. The factory is called with two arguments, the + abstract type of the underlying collection and this proxy instance. + + :param proxy_factory: Optional. The type of collection to emulate is + determined by sniffing the target collection. If your collection + type can't be determined by duck typing or you'd like to use a + different collection implementation, you may supply a factory + function to produce those collections. Only applicable to + non-scalar relationships. + + :param proxy_bulk_set: Optional, use with proxy_factory. See + the _set() method for details. + + :param info: optional, will be assigned to + :attr:`.AssociationProxy.info` if present. + + .. versionadded:: 1.0.9 + + """ + self.target_collection = target_collection + self.value_attr = attr + self.creator = creator + self.getset_factory = getset_factory + self.proxy_factory = proxy_factory + self.proxy_bulk_set = proxy_bulk_set + + self.owning_class = None + self.key = '_%s_%s_%s' % ( + type(self).__name__, target_collection, id(self)) + self.collection_class = None + if info: + self.info = info + + @property + def remote_attr(self): + """The 'remote' :class:`.MapperProperty` referenced by this + :class:`.AssociationProxy`. + + .. versionadded:: 0.7.3 + + See also: + + :attr:`.AssociationProxy.attr` + + :attr:`.AssociationProxy.local_attr` + + """ + return getattr(self.target_class, self.value_attr) + + @property + def local_attr(self): + """The 'local' :class:`.MapperProperty` referenced by this + :class:`.AssociationProxy`. + + .. versionadded:: 0.7.3 + + See also: + + :attr:`.AssociationProxy.attr` + + :attr:`.AssociationProxy.remote_attr` + + """ + return getattr(self.owning_class, self.target_collection) + + @property + def attr(self): + """Return a tuple of ``(local_attr, remote_attr)``. + + This attribute is convenient when specifying a join + using :meth:`.Query.join` across two relationships:: + + sess.query(Parent).join(*Parent.proxied.attr) + + .. versionadded:: 0.7.3 + + See also: + + :attr:`.AssociationProxy.local_attr` + + :attr:`.AssociationProxy.remote_attr` + + """ + return (self.local_attr, self.remote_attr) + + def _get_property(self): + owning_class = self.owning_class + if owning_class is None: + raise exc.InvalidRequestError( + "This association proxy has no mapped owning class; " + "can't locate a mapped property") + return (orm.class_mapper(owning_class). + get_property(self.target_collection)) + + @util.memoized_property + def target_class(self): + """The intermediary class handled by this :class:`.AssociationProxy`. + + Intercepted append/set/assignment events will result + in the generation of new instances of this class. + + """ + return self._get_property().mapper.class_ + + @util.memoized_property + def scalar(self): + """Return ``True`` if this :class:`.AssociationProxy` proxies a scalar + relationship on the local side.""" + + scalar = not self._get_property().uselist + if scalar: + self._initialize_scalar_accessors() + return scalar + + @util.memoized_property + def _value_is_scalar(self): + return not self._get_property().\ + mapper.get_property(self.value_attr).uselist + + @util.memoized_property + def _target_is_object(self): + return getattr(self.target_class, self.value_attr).impl.uses_objects + + def _calc_owner(self, obj, class_): + if obj is not None and class_ is None: + target_cls = type(obj) + elif class_ is not None: + target_cls = class_ + else: + return + + # we might be getting invoked for a subclass + # that is not mapped yet, in some declarative situations. + # save until we are mapped + try: + insp = inspect(target_cls) + except exc.NoInspectionAvailable: + # can't find a mapper, don't set owner. if we are a not-yet-mapped + # subclass, we can also scan through __mro__ to find a mapped + # class, but instead just wait for us to be called again against a + # mapped class normally. + return + + # note we can get our real .key here too + owner = insp.mapper.class_manager._locate_owning_manager(self) + if owner is not None: + self.owning_class = owner.class_ + else: + # the proxy is attached to a class that is not mapped + # (like a mixin), we are mapped, so, it's us. + self.owning_class = target_cls + + def __get__(self, obj, class_): + if self.owning_class is None: + self._calc_owner(obj, class_) + + if obj is None: + return self + + if self.scalar: + target = getattr(obj, self.target_collection) + return self._scalar_get(target) + else: + try: + # If the owning instance is reborn (orm session resurrect, + # etc.), refresh the proxy cache. + creator_id, proxy = getattr(obj, self.key) + if id(obj) == creator_id: + return proxy + except AttributeError: + pass + proxy = self._new(_lazy_collection(obj, self.target_collection)) + setattr(obj, self.key, (id(obj), proxy)) + return proxy + + def __set__(self, obj, values): + if self.owning_class is None: + self._calc_owner(obj, None) + + if self.scalar: + creator = self.creator and self.creator or self.target_class + target = getattr(obj, self.target_collection) + if target is None: + setattr(obj, self.target_collection, creator(values)) + else: + self._scalar_set(target, values) + else: + proxy = self.__get__(obj, None) + if proxy is not values: + proxy.clear() + self._set(proxy, values) + + def __delete__(self, obj): + if self.owning_class is None: + self._calc_owner(obj, None) + + delattr(obj, self.key) + + def _initialize_scalar_accessors(self): + if self.getset_factory: + get, set = self.getset_factory(None, self) + else: + get, set = self._default_getset(None) + self._scalar_get, self._scalar_set = get, set + + def _default_getset(self, collection_class): + attr = self.value_attr + _getter = operator.attrgetter(attr) + + def getter(target): + return _getter(target) if target is not None else None + + if collection_class is dict: + def setter(o, k, v): + setattr(o, attr, v) + else: + def setter(o, v): + setattr(o, attr, v) + return getter, setter + + def _new(self, lazy_collection): + creator = self.creator and self.creator or self.target_class + self.collection_class = util.duck_type_collection(lazy_collection()) + + if self.proxy_factory: + return self.proxy_factory( + lazy_collection, creator, self.value_attr, self) + + if self.getset_factory: + getter, setter = self.getset_factory(self.collection_class, self) + else: + getter, setter = self._default_getset(self.collection_class) + + if self.collection_class is list: + return _AssociationList( + lazy_collection, creator, getter, setter, self) + elif self.collection_class is dict: + return _AssociationDict( + lazy_collection, creator, getter, setter, self) + elif self.collection_class is set: + return _AssociationSet( + lazy_collection, creator, getter, setter, self) + else: + raise exc.ArgumentError( + 'could not guess which interface to use for ' + 'collection_class "%s" backing "%s"; specify a ' + 'proxy_factory and proxy_bulk_set manually' % + (self.collection_class.__name__, self.target_collection)) + + def _inflate(self, proxy): + creator = self.creator and self.creator or self.target_class + + if self.getset_factory: + getter, setter = self.getset_factory(self.collection_class, self) + else: + getter, setter = self._default_getset(self.collection_class) + + proxy.creator = creator + proxy.getter = getter + proxy.setter = setter + + def _set(self, proxy, values): + if self.proxy_bulk_set: + self.proxy_bulk_set(proxy, values) + elif self.collection_class is list: + proxy.extend(values) + elif self.collection_class is dict: + proxy.update(values) + elif self.collection_class is set: + proxy.update(values) + else: + raise exc.ArgumentError( + 'no proxy_bulk_set supplied for custom ' + 'collection_class implementation') + + @property + def _comparator(self): + return self._get_property().comparator + + @util.memoized_property + def _unwrap_target_assoc_proxy(self): + attr = getattr(self.target_class, self.value_attr) + if isinstance(attr, AssociationProxy): + return attr + return None + + def _criterion_exists(self, criterion=None, **kwargs): + is_has = kwargs.pop('is_has', None) + + target_assoc = self._unwrap_target_assoc_proxy + if target_assoc is not None: + inner = target_assoc._criterion_exists( + criterion=criterion, **kwargs) + return self._comparator._criterion_exists(inner) + + if self._target_is_object: + prop = getattr(self.target_class, self.value_attr) + value_expr = prop._criterion_exists(criterion, **kwargs) + else: + if kwargs: + raise exc.ArgumentError( + "Can't apply keyword arguments to column-targeted " + "association proxy; use ==" + ) + elif is_has and criterion is not None: + raise exc.ArgumentError( + "Non-empty has() not allowed for " + "column-targeted association proxy; use ==" + ) + + value_expr = criterion + + return self._comparator._criterion_exists(value_expr) + + def any(self, criterion=None, **kwargs): + """Produce a proxied 'any' expression using EXISTS. + + This expression will be a composed product + using the :meth:`.RelationshipProperty.Comparator.any` + and/or :meth:`.RelationshipProperty.Comparator.has` + operators of the underlying proxied attributes. + + """ + if self._unwrap_target_assoc_proxy is None and ( + self.scalar and ( + not self._target_is_object or self._value_is_scalar) + ): + raise exc.InvalidRequestError( + "'any()' not implemented for scalar " + "attributes. Use has()." + ) + return self._criterion_exists( + criterion=criterion, is_has=False, **kwargs) + + def has(self, criterion=None, **kwargs): + """Produce a proxied 'has' expression using EXISTS. + + This expression will be a composed product + using the :meth:`.RelationshipProperty.Comparator.any` + and/or :meth:`.RelationshipProperty.Comparator.has` + operators of the underlying proxied attributes. + + """ + if self._unwrap_target_assoc_proxy is None and ( + not self.scalar or ( + self._target_is_object and not self._value_is_scalar) + ): + raise exc.InvalidRequestError( + "'has()' not implemented for collections. " + "Use any().") + return self._criterion_exists( + criterion=criterion, is_has=True, **kwargs) + + def contains(self, obj): + """Produce a proxied 'contains' expression using EXISTS. + + This expression will be a composed product + using the :meth:`.RelationshipProperty.Comparator.any` + , :meth:`.RelationshipProperty.Comparator.has`, + and/or :meth:`.RelationshipProperty.Comparator.contains` + operators of the underlying proxied attributes. + """ + + target_assoc = self._unwrap_target_assoc_proxy + if target_assoc is not None: + return self._comparator._criterion_exists( + target_assoc.contains(obj) + if not target_assoc.scalar else target_assoc == obj + ) + elif self._target_is_object and self.scalar and \ + not self._value_is_scalar: + return self._comparator.has( + getattr(self.target_class, self.value_attr).contains(obj) + ) + elif self._target_is_object and self.scalar and \ + self._value_is_scalar: + raise exc.InvalidRequestError( + "contains() doesn't apply to a scalar endpoint; use ==") + else: + + return self._comparator._criterion_exists(**{self.value_attr: obj}) + + def __eq__(self, obj): + # note the has() here will fail for collections; eq_() + # is only allowed with a scalar. + if obj is None: + return or_( + self._comparator.has(**{self.value_attr: obj}), + self._comparator == None + ) + else: + return self._comparator.has(**{self.value_attr: obj}) + + def __ne__(self, obj): + # note the has() here will fail for collections; eq_() + # is only allowed with a scalar. + return self._comparator.has( + getattr(self.target_class, self.value_attr) != obj) + + def __repr__(self): + return "AssociationProxy(%r, %r)" % ( + self.target_collection, self.value_attr) + + +class _lazy_collection(object): + def __init__(self, obj, target): + self.ref = weakref.ref(obj) + self.target = target + + def __call__(self): + obj = self.ref() + if obj is None: + raise exc.InvalidRequestError( + "stale association proxy, parent object has gone out of " + "scope") + return getattr(obj, self.target) + + def __getstate__(self): + return {'obj': self.ref(), 'target': self.target} + + def __setstate__(self, state): + self.ref = weakref.ref(state['obj']) + self.target = state['target'] + + +class _AssociationCollection(object): + def __init__(self, lazy_collection, creator, getter, setter, parent): + """Constructs an _AssociationCollection. + + This will always be a subclass of either _AssociationList, + _AssociationSet, or _AssociationDict. + + lazy_collection + A callable returning a list-based collection of entities (usually an + object attribute managed by a SQLAlchemy relationship()) + + creator + A function that creates new target entities. Given one parameter: + value. This assertion is assumed:: + + obj = creator(somevalue) + assert getter(obj) == somevalue + + getter + A function. Given an associated object, return the 'value'. + + setter + A function. Given an associated object and a value, store that + value on the object. + + """ + self.lazy_collection = lazy_collection + self.creator = creator + self.getter = getter + self.setter = setter + self.parent = parent + + col = property(lambda self: self.lazy_collection()) + + def __len__(self): + return len(self.col) + + def __bool__(self): + return bool(self.col) + + __nonzero__ = __bool__ + + def __getstate__(self): + return {'parent': self.parent, 'lazy_collection': self.lazy_collection} + + def __setstate__(self, state): + self.parent = state['parent'] + self.lazy_collection = state['lazy_collection'] + self.parent._inflate(self) + + +class _AssociationList(_AssociationCollection): + """Generic, converting, list-to-list proxy.""" + + def _create(self, value): + return self.creator(value) + + def _get(self, object): + return self.getter(object) + + def _set(self, object, value): + return self.setter(object, value) + + def __getitem__(self, index): + if not isinstance(index, slice): + return self._get(self.col[index]) + else: + return [self._get(member) for member in self.col[index]] + + def __setitem__(self, index, value): + if not isinstance(index, slice): + self._set(self.col[index], value) + else: + if index.stop is None: + stop = len(self) + elif index.stop < 0: + stop = len(self) + index.stop + else: + stop = index.stop + step = index.step or 1 + + start = index.start or 0 + rng = list(range(index.start or 0, stop, step)) + if step == 1: + for i in rng: + del self[start] + i = start + for item in value: + self.insert(i, item) + i += 1 + else: + if len(value) != len(rng): + raise ValueError( + "attempt to assign sequence of size %s to " + "extended slice of size %s" % (len(value), + len(rng))) + for i, item in zip(rng, value): + self._set(self.col[i], item) + + def __delitem__(self, index): + del self.col[index] + + def __contains__(self, value): + for member in self.col: + # testlib.pragma exempt:__eq__ + if self._get(member) == value: + return True + return False + + def __getslice__(self, start, end): + return [self._get(member) for member in self.col[start:end]] + + def __setslice__(self, start, end, values): + members = [self._create(v) for v in values] + self.col[start:end] = members + + def __delslice__(self, start, end): + del self.col[start:end] + + def __iter__(self): + """Iterate over proxied values. + + For the actual domain objects, iterate over .col instead or + just use the underlying collection directly from its property + on the parent. + """ + + for member in self.col: + yield self._get(member) + return + + def append(self, value): + col = self.col + item = self._create(value) + col.append(item) + + def count(self, value): + return sum([1 for _ in + util.itertools_filter(lambda v: v == value, iter(self))]) + + def extend(self, values): + for v in values: + self.append(v) + + def insert(self, index, value): + self.col[index:index] = [self._create(value)] + + def pop(self, index=-1): + return self.getter(self.col.pop(index)) + + def remove(self, value): + for i, val in enumerate(self): + if val == value: + del self.col[i] + return + raise ValueError("value not in list") + + def reverse(self): + """Not supported, use reversed(mylist)""" + + raise NotImplementedError + + def sort(self): + """Not supported, use sorted(mylist)""" + + raise NotImplementedError + + def clear(self): + del self.col[0:len(self.col)] + + def __eq__(self, other): + return list(self) == other + + def __ne__(self, other): + return list(self) != other + + def __lt__(self, other): + return list(self) < other + + def __le__(self, other): + return list(self) <= other + + def __gt__(self, other): + return list(self) > other + + def __ge__(self, other): + return list(self) >= other + + def __cmp__(self, other): + return util.cmp(list(self), other) + + def __add__(self, iterable): + try: + other = list(iterable) + except TypeError: + return NotImplemented + return list(self) + other + + def __radd__(self, iterable): + try: + other = list(iterable) + except TypeError: + return NotImplemented + return other + list(self) + + def __mul__(self, n): + if not isinstance(n, int): + return NotImplemented + return list(self) * n + __rmul__ = __mul__ + + def __iadd__(self, iterable): + self.extend(iterable) + return self + + def __imul__(self, n): + # unlike a regular list *=, proxied __imul__ will generate unique + # backing objects for each copy. *= on proxied lists is a bit of + # a stretch anyhow, and this interpretation of the __imul__ contract + # is more plausibly useful than copying the backing objects. + if not isinstance(n, int): + return NotImplemented + if n == 0: + self.clear() + elif n > 1: + self.extend(list(self) * (n - 1)) + return self + + def index(self, item, *args): + return list(self).index(item, *args) + + def copy(self): + return list(self) + + def __repr__(self): + return repr(list(self)) + + def __hash__(self): + raise TypeError("%s objects are unhashable" % type(self).__name__) + + for func_name, func in list(locals().items()): + if (util.callable(func) and func.__name__ == func_name and + not func.__doc__ and hasattr(list, func_name)): + func.__doc__ = getattr(list, func_name).__doc__ + del func_name, func + + +_NotProvided = util.symbol('_NotProvided') + + +class _AssociationDict(_AssociationCollection): + """Generic, converting, dict-to-dict proxy.""" + + def _create(self, key, value): + return self.creator(key, value) + + def _get(self, object): + return self.getter(object) + + def _set(self, object, key, value): + return self.setter(object, key, value) + + def __getitem__(self, key): + return self._get(self.col[key]) + + def __setitem__(self, key, value): + if key in self.col: + self._set(self.col[key], key, value) + else: + self.col[key] = self._create(key, value) + + def __delitem__(self, key): + del self.col[key] + + def __contains__(self, key): + # testlib.pragma exempt:__hash__ + return key in self.col + + def has_key(self, key): + # testlib.pragma exempt:__hash__ + return key in self.col + + def __iter__(self): + return iter(self.col.keys()) + + def clear(self): + self.col.clear() + + def __eq__(self, other): + return dict(self) == other + + def __ne__(self, other): + return dict(self) != other + + def __lt__(self, other): + return dict(self) < other + + def __le__(self, other): + return dict(self) <= other + + def __gt__(self, other): + return dict(self) > other + + def __ge__(self, other): + return dict(self) >= other + + def __cmp__(self, other): + return util.cmp(dict(self), other) + + def __repr__(self): + return repr(dict(self.items())) + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + def setdefault(self, key, default=None): + if key not in self.col: + self.col[key] = self._create(key, default) + return default + else: + return self[key] + + def keys(self): + return self.col.keys() + + if util.py2k: + def iteritems(self): + return ((key, self._get(self.col[key])) for key in self.col) + + def itervalues(self): + return (self._get(self.col[key]) for key in self.col) + + def iterkeys(self): + return self.col.iterkeys() + + def values(self): + return [self._get(member) for member in self.col.values()] + + def items(self): + return [(k, self._get(self.col[k])) for k in self] + else: + def items(self): + return ((key, self._get(self.col[key])) for key in self.col) + + def values(self): + return (self._get(self.col[key]) for key in self.col) + + def pop(self, key, default=_NotProvided): + if default is _NotProvided: + member = self.col.pop(key) + else: + member = self.col.pop(key, default) + return self._get(member) + + def popitem(self): + item = self.col.popitem() + return (item[0], self._get(item[1])) + + def update(self, *a, **kw): + if len(a) > 1: + raise TypeError('update expected at most 1 arguments, got %i' % + len(a)) + elif len(a) == 1: + seq_or_map = a[0] + # discern dict from sequence - took the advice from + # http://www.voidspace.org.uk/python/articles/duck_typing.shtml + # still not perfect :( + if hasattr(seq_or_map, 'keys'): + for item in seq_or_map: + self[item] = seq_or_map[item] + else: + try: + for k, v in seq_or_map: + self[k] = v + except ValueError: + raise ValueError( + "dictionary update sequence " + "requires 2-element tuples") + + for key, value in kw: + self[key] = value + + def copy(self): + return dict(self.items()) + + def __hash__(self): + raise TypeError("%s objects are unhashable" % type(self).__name__) + + for func_name, func in list(locals().items()): + if (util.callable(func) and func.__name__ == func_name and + not func.__doc__ and hasattr(dict, func_name)): + func.__doc__ = getattr(dict, func_name).__doc__ + del func_name, func + + +class _AssociationSet(_AssociationCollection): + """Generic, converting, set-to-set proxy.""" + + def _create(self, value): + return self.creator(value) + + def _get(self, object): + return self.getter(object) + + def __len__(self): + return len(self.col) + + def __bool__(self): + if self.col: + return True + else: + return False + + __nonzero__ = __bool__ + + def __contains__(self, value): + for member in self.col: + # testlib.pragma exempt:__eq__ + if self._get(member) == value: + return True + return False + + def __iter__(self): + """Iterate over proxied values. + + For the actual domain objects, iterate over .col instead or just use + the underlying collection directly from its property on the parent. + + """ + for member in self.col: + yield self._get(member) + return + + def add(self, value): + if value not in self: + self.col.add(self._create(value)) + + # for discard and remove, choosing a more expensive check strategy rather + # than call self.creator() + def discard(self, value): + for member in self.col: + if self._get(member) == value: + self.col.discard(member) + break + + def remove(self, value): + for member in self.col: + if self._get(member) == value: + self.col.discard(member) + return + raise KeyError(value) + + def pop(self): + if not self.col: + raise KeyError('pop from an empty set') + member = self.col.pop() + return self._get(member) + + def update(self, other): + for value in other: + self.add(value) + + def __ior__(self, other): + if not collections._set_binops_check_strict(self, other): + return NotImplemented + for value in other: + self.add(value) + return self + + def _set(self): + return set(iter(self)) + + def union(self, other): + return set(self).union(other) + + __or__ = union + + def difference(self, other): + return set(self).difference(other) + + __sub__ = difference + + def difference_update(self, other): + for value in other: + self.discard(value) + + def __isub__(self, other): + if not collections._set_binops_check_strict(self, other): + return NotImplemented + for value in other: + self.discard(value) + return self + + def intersection(self, other): + return set(self).intersection(other) + + __and__ = intersection + + def intersection_update(self, other): + want, have = self.intersection(other), set(self) + + remove, add = have - want, want - have + + for value in remove: + self.remove(value) + for value in add: + self.add(value) + + def __iand__(self, other): + if not collections._set_binops_check_strict(self, other): + return NotImplemented + want, have = self.intersection(other), set(self) + + remove, add = have - want, want - have + + for value in remove: + self.remove(value) + for value in add: + self.add(value) + return self + + def symmetric_difference(self, other): + return set(self).symmetric_difference(other) + + __xor__ = symmetric_difference + + def symmetric_difference_update(self, other): + want, have = self.symmetric_difference(other), set(self) + + remove, add = have - want, want - have + + for value in remove: + self.remove(value) + for value in add: + self.add(value) + + def __ixor__(self, other): + if not collections._set_binops_check_strict(self, other): + return NotImplemented + want, have = self.symmetric_difference(other), set(self) + + remove, add = have - want, want - have + + for value in remove: + self.remove(value) + for value in add: + self.add(value) + return self + + def issubset(self, other): + return set(self).issubset(other) + + def issuperset(self, other): + return set(self).issuperset(other) + + def clear(self): + self.col.clear() + + def copy(self): + return set(self) + + def __eq__(self, other): + return set(self) == other + + def __ne__(self, other): + return set(self) != other + + def __lt__(self, other): + return set(self) < other + + def __le__(self, other): + return set(self) <= other + + def __gt__(self, other): + return set(self) > other + + def __ge__(self, other): + return set(self) >= other + + def __repr__(self): + return repr(set(self)) + + def __hash__(self): + raise TypeError("%s objects are unhashable" % type(self).__name__) + + for func_name, func in list(locals().items()): + if (util.callable(func) and func.__name__ == func_name and + not func.__doc__ and hasattr(set, func_name)): + func.__doc__ = getattr(set, func_name).__doc__ + del func_name, func diff --git a/venv/Lib/site-packages/sqlalchemy/ext/automap.py b/venv/Lib/site-packages/sqlalchemy/ext/automap.py new file mode 100644 index 0000000..cafb3d6 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/automap.py @@ -0,0 +1,1055 @@ +# ext/automap.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +r"""Define an extension to the :mod:`sqlalchemy.ext.declarative` system +which automatically generates mapped classes and relationships from a database +schema, typically though not necessarily one which is reflected. + +.. versionadded:: 0.9.1 Added :mod:`sqlalchemy.ext.automap`. + +It is hoped that the :class:`.AutomapBase` system provides a quick +and modernized solution to the problem that the very famous +`SQLSoup <https://sqlsoup.readthedocs.io/en/latest/>`_ +also tries to solve, that of generating a quick and rudimentary object +model from an existing database on the fly. By addressing the issue strictly +at the mapper configuration level, and integrating fully with existing +Declarative class techniques, :class:`.AutomapBase` seeks to provide +a well-integrated approach to the issue of expediently auto-generating ad-hoc +mappings. + + +Basic Use +========= + +The simplest usage is to reflect an existing database into a new model. +We create a new :class:`.AutomapBase` class in a similar manner as to how +we create a declarative base class, using :func:`.automap_base`. +We then call :meth:`.AutomapBase.prepare` on the resulting base class, +asking it to reflect the schema and produce mappings:: + + from sqlalchemy.ext.automap import automap_base + from sqlalchemy.orm import Session + from sqlalchemy import create_engine + + Base = automap_base() + + # engine, suppose it has two tables 'user' and 'address' set up + engine = create_engine("sqlite:///mydatabase.db") + + # reflect the tables + Base.prepare(engine, reflect=True) + + # mapped classes are now created with names by default + # matching that of the table name. + User = Base.classes.user + Address = Base.classes.address + + session = Session(engine) + + # rudimentary relationships are produced + session.add(Address(email_address="foo@bar.com", user=User(name="foo"))) + session.commit() + + # collection-based relationships are by default named + # "<classname>_collection" + print (u1.address_collection) + +Above, calling :meth:`.AutomapBase.prepare` while passing along the +:paramref:`.AutomapBase.prepare.reflect` parameter indicates that the +:meth:`.MetaData.reflect` method will be called on this declarative base +classes' :class:`.MetaData` collection; then, each **viable** +:class:`.Table` within the :class:`.MetaData` will get a new mapped class +generated automatically. The :class:`.ForeignKeyConstraint` objects which +link the various tables together will be used to produce new, bidirectional +:func:`.relationship` objects between classes. The classes and relationships +follow along a default naming scheme that we can customize. At this point, +our basic mapping consisting of related ``User`` and ``Address`` classes is +ready to use in the traditional way. + +.. note:: By **viable**, we mean that for a table to be mapped, it must + specify a primary key. Additionally, if the table is detected as being + a pure association table between two other tables, it will not be directly + mapped and will instead be configured as a many-to-many table between + the mappings for the two referring tables. + +Generating Mappings from an Existing MetaData +============================================= + +We can pass a pre-declared :class:`.MetaData` object to :func:`.automap_base`. +This object can be constructed in any way, including programmatically, from +a serialized file, or from itself being reflected using +:meth:`.MetaData.reflect`. Below we illustrate a combination of reflection and +explicit table declaration:: + + from sqlalchemy import create_engine, MetaData, Table, Column, ForeignKey + from sqlalchemy.ext.automap import automap_base + engine = create_engine("sqlite:///mydatabase.db") + + # produce our own MetaData object + metadata = MetaData() + + # we can reflect it ourselves from a database, using options + # such as 'only' to limit what tables we look at... + metadata.reflect(engine, only=['user', 'address']) + + # ... or just define our own Table objects with it (or combine both) + Table('user_order', metadata, + Column('id', Integer, primary_key=True), + Column('user_id', ForeignKey('user.id')) + ) + + # we can then produce a set of mappings from this MetaData. + Base = automap_base(metadata=metadata) + + # calling prepare() just sets up mapped classes and relationships. + Base.prepare() + + # mapped classes are ready + User, Address, Order = Base.classes.user, Base.classes.address,\ + Base.classes.user_order + +Specifying Classes Explicitly +============================= + +The :mod:`.sqlalchemy.ext.automap` extension allows classes to be defined +explicitly, in a way similar to that of the :class:`.DeferredReflection` class. +Classes that extend from :class:`.AutomapBase` act like regular declarative +classes, but are not immediately mapped after their construction, and are +instead mapped when we call :meth:`.AutomapBase.prepare`. The +:meth:`.AutomapBase.prepare` method will make use of the classes we've +established based on the table name we use. If our schema contains tables +``user`` and ``address``, we can define one or both of the classes to be used:: + + from sqlalchemy.ext.automap import automap_base + from sqlalchemy import create_engine + + # automap base + Base = automap_base() + + # pre-declare User for the 'user' table + class User(Base): + __tablename__ = 'user' + + # override schema elements like Columns + user_name = Column('name', String) + + # override relationships too, if desired. + # we must use the same name that automap would use for the + # relationship, and also must refer to the class name that automap will + # generate for "address" + address_collection = relationship("address", collection_class=set) + + # reflect + engine = create_engine("sqlite:///mydatabase.db") + Base.prepare(engine, reflect=True) + + # we still have Address generated from the tablename "address", + # but User is the same as Base.classes.User now + + Address = Base.classes.address + + u1 = session.query(User).first() + print (u1.address_collection) + + # the backref is still there: + a1 = session.query(Address).first() + print (a1.user) + +Above, one of the more intricate details is that we illustrated overriding +one of the :func:`.relationship` objects that automap would have created. +To do this, we needed to make sure the names match up with what automap +would normally generate, in that the relationship name would be +``User.address_collection`` and the name of the class referred to, from +automap's perspective, is called ``address``, even though we are referring to +it as ``Address`` within our usage of this class. + +Overriding Naming Schemes +========================= + +:mod:`.sqlalchemy.ext.automap` is tasked with producing mapped classes and +relationship names based on a schema, which means it has decision points in how +these names are determined. These three decision points are provided using +functions which can be passed to the :meth:`.AutomapBase.prepare` method, and +are known as :func:`.classname_for_table`, +:func:`.name_for_scalar_relationship`, +and :func:`.name_for_collection_relationship`. Any or all of these +functions are provided as in the example below, where we use a "camel case" +scheme for class names and a "pluralizer" for collection names using the +`Inflect <https://pypi.python.org/pypi/inflect>`_ package:: + + import re + import inflect + + def camelize_classname(base, tablename, table): + "Produce a 'camelized' class name, e.g. " + "'words_and_underscores' -> 'WordsAndUnderscores'" + + return str(tablename[0].upper() + \ + re.sub(r'_([a-z])', lambda m: m.group(1).upper(), tablename[1:])) + + _pluralizer = inflect.engine() + def pluralize_collection(base, local_cls, referred_cls, constraint): + "Produce an 'uncamelized', 'pluralized' class name, e.g. " + "'SomeTerm' -> 'some_terms'" + + referred_name = referred_cls.__name__ + uncamelized = re.sub(r'[A-Z]', + lambda m: "_%s" % m.group(0).lower(), + referred_name)[1:] + pluralized = _pluralizer.plural(uncamelized) + return pluralized + + from sqlalchemy.ext.automap import automap_base + + Base = automap_base() + + engine = create_engine("sqlite:///mydatabase.db") + + Base.prepare(engine, reflect=True, + classname_for_table=camelize_classname, + name_for_collection_relationship=pluralize_collection + ) + +From the above mapping, we would now have classes ``User`` and ``Address``, +where the collection from ``User`` to ``Address`` is called +``User.addresses``:: + + User, Address = Base.classes.User, Base.classes.Address + + u1 = User(addresses=[Address(email="foo@bar.com")]) + +Relationship Detection +====================== + +The vast majority of what automap accomplishes is the generation of +:func:`.relationship` structures based on foreign keys. The mechanism +by which this works for many-to-one and one-to-many relationships is as +follows: + +1. A given :class:`.Table`, known to be mapped to a particular class, + is examined for :class:`.ForeignKeyConstraint` objects. + +2. From each :class:`.ForeignKeyConstraint`, the remote :class:`.Table` + object present is matched up to the class to which it is to be mapped, + if any, else it is skipped. + +3. As the :class:`.ForeignKeyConstraint` we are examining corresponds to a + reference from the immediate mapped class, the relationship will be set up + as a many-to-one referring to the referred class; a corresponding + one-to-many backref will be created on the referred class referring + to this class. + +4. If any of the columns that are part of the :class:`.ForeignKeyConstraint` + are not nullable (e.g. ``nullable=False``), a + :paramref:`~.relationship.cascade` keyword argument + of ``all, delete-orphan`` will be added to the keyword arguments to + be passed to the relationship or backref. If the + :class:`.ForeignKeyConstraint` reports that + :paramref:`.ForeignKeyConstraint.ondelete` + is set to ``CASCADE`` for a not null or ``SET NULL`` for a nullable + set of columns, the option :paramref:`~.relationship.passive_deletes` + flag is set to ``True`` in the set of relationship keyword arguments. + Note that not all backends support reflection of ON DELETE. + + .. versionadded:: 1.0.0 - automap will detect non-nullable foreign key + constraints when producing a one-to-many relationship and establish + a default cascade of ``all, delete-orphan`` if so; additionally, + if the constraint specifies :paramref:`.ForeignKeyConstraint.ondelete` + of ``CASCADE`` for non-nullable or ``SET NULL`` for nullable columns, + the ``passive_deletes=True`` option is also added. + +5. The names of the relationships are determined using the + :paramref:`.AutomapBase.prepare.name_for_scalar_relationship` and + :paramref:`.AutomapBase.prepare.name_for_collection_relationship` + callable functions. It is important to note that the default relationship + naming derives the name from the **the actual class name**. If you've + given a particular class an explicit name by declaring it, or specified an + alternate class naming scheme, that's the name from which the relationship + name will be derived. + +6. The classes are inspected for an existing mapped property matching these + names. If one is detected on one side, but none on the other side, + :class:`.AutomapBase` attempts to create a relationship on the missing side, + then uses the :paramref:`.relationship.back_populates` parameter in order to + point the new relationship to the other side. + +7. In the usual case where no relationship is on either side, + :meth:`.AutomapBase.prepare` produces a :func:`.relationship` on the + "many-to-one" side and matches it to the other using the + :paramref:`.relationship.backref` parameter. + +8. Production of the :func:`.relationship` and optionally the :func:`.backref` + is handed off to the :paramref:`.AutomapBase.prepare.generate_relationship` + function, which can be supplied by the end-user in order to augment + the arguments passed to :func:`.relationship` or :func:`.backref` or to + make use of custom implementations of these functions. + +Custom Relationship Arguments +----------------------------- + +The :paramref:`.AutomapBase.prepare.generate_relationship` hook can be used +to add parameters to relationships. For most cases, we can make use of the +existing :func:`.automap.generate_relationship` function to return +the object, after augmenting the given keyword dictionary with our own +arguments. + +Below is an illustration of how to send +:paramref:`.relationship.cascade` and +:paramref:`.relationship.passive_deletes` +options along to all one-to-many relationships:: + + from sqlalchemy.ext.automap import generate_relationship + + def _gen_relationship(base, direction, return_fn, + attrname, local_cls, referred_cls, **kw): + if direction is interfaces.ONETOMANY: + kw['cascade'] = 'all, delete-orphan' + kw['passive_deletes'] = True + # make use of the built-in function to actually return + # the result. + return generate_relationship(base, direction, return_fn, + attrname, local_cls, referred_cls, **kw) + + from sqlalchemy.ext.automap import automap_base + from sqlalchemy import create_engine + + # automap base + Base = automap_base() + + engine = create_engine("sqlite:///mydatabase.db") + Base.prepare(engine, reflect=True, + generate_relationship=_gen_relationship) + +Many-to-Many relationships +-------------------------- + +:mod:`.sqlalchemy.ext.automap` will generate many-to-many relationships, e.g. +those which contain a ``secondary`` argument. The process for producing these +is as follows: + +1. A given :class:`.Table` is examined for :class:`.ForeignKeyConstraint` + objects, before any mapped class has been assigned to it. + +2. If the table contains two and exactly two :class:`.ForeignKeyConstraint` + objects, and all columns within this table are members of these two + :class:`.ForeignKeyConstraint` objects, the table is assumed to be a + "secondary" table, and will **not be mapped directly**. + +3. The two (or one, for self-referential) external tables to which the + :class:`.Table` refers to are matched to the classes to which they will be + mapped, if any. + +4. If mapped classes for both sides are located, a many-to-many bi-directional + :func:`.relationship` / :func:`.backref` pair is created between the two + classes. + +5. The override logic for many-to-many works the same as that of one-to-many/ + many-to-one; the :func:`.generate_relationship` function is called upon + to generate the strucures and existing attributes will be maintained. + +Relationships with Inheritance +------------------------------ + +:mod:`.sqlalchemy.ext.automap` will not generate any relationships between +two classes that are in an inheritance relationship. That is, with two +classes given as follows:: + + class Employee(Base): + __tablename__ = 'employee' + id = Column(Integer, primary_key=True) + type = Column(String(50)) + __mapper_args__ = { + 'polymorphic_identity':'employee', 'polymorphic_on': type + } + + class Engineer(Employee): + __tablename__ = 'engineer' + id = Column(Integer, ForeignKey('employee.id'), primary_key=True) + __mapper_args__ = { + 'polymorphic_identity':'engineer', + } + +The foreign key from ``Engineer`` to ``Employee`` is used not for a +relationship, but to establish joined inheritance between the two classes. + +Note that this means automap will not generate *any* relationships +for foreign keys that link from a subclass to a superclass. If a mapping +has actual relationships from subclass to superclass as well, those +need to be explicit. Below, as we have two separate foreign keys +from ``Engineer`` to ``Employee``, we need to set up both the relationship +we want as well as the ``inherit_condition``, as these are not things +SQLAlchemy can guess:: + + class Employee(Base): + __tablename__ = 'employee' + id = Column(Integer, primary_key=True) + type = Column(String(50)) + + __mapper_args__ = { + 'polymorphic_identity':'employee', 'polymorphic_on':type + } + + class Engineer(Employee): + __tablename__ = 'engineer' + id = Column(Integer, ForeignKey('employee.id'), primary_key=True) + favorite_employee_id = Column(Integer, ForeignKey('employee.id')) + + favorite_employee = relationship(Employee, + foreign_keys=favorite_employee_id) + + __mapper_args__ = { + 'polymorphic_identity':'engineer', + 'inherit_condition': id == Employee.id + } + +Handling Simple Naming Conflicts +-------------------------------- + +In the case of naming conflicts during mapping, override any of +:func:`.classname_for_table`, :func:`.name_for_scalar_relationship`, +and :func:`.name_for_collection_relationship` as needed. For example, if +automap is attempting to name a many-to-one relationship the same as an +existing column, an alternate convention can be conditionally selected. Given +a schema: + +.. sourcecode:: sql + + CREATE TABLE table_a ( + id INTEGER PRIMARY KEY + ); + + CREATE TABLE table_b ( + id INTEGER PRIMARY KEY, + table_a INTEGER, + FOREIGN KEY(table_a) REFERENCES table_a(id) + ); + +The above schema will first automap the ``table_a`` table as a class named +``table_a``; it will then automap a relationship onto the class for ``table_b`` +with the same name as this related class, e.g. ``table_a``. This +relationship name conflicts with the mapping column ``table_b.table_a``, +and will emit an error on mapping. + +We can resolve this conflict by using an underscore as follows:: + + def name_for_scalar_relationship(base, local_cls, referred_cls, constraint): + name = referred_cls.__name__.lower() + local_table = local_cls.__table__ + if name in local_table.columns: + newname = name + "_" + warnings.warn( + "Already detected name %s present. using %s" % + (name, newname)) + return newname + return name + + + Base.prepare(engine, reflect=True, + name_for_scalar_relationship=name_for_scalar_relationship) + +Alternatively, we can change the name on the column side. The columns +that are mapped can be modified using the technique described at +:ref:`mapper_column_distinct_names`, by assigning the column explicitly +to a new name:: + + Base = automap_base() + + class TableB(Base): + __tablename__ = 'table_b' + _table_a = Column('table_a', ForeignKey('table_a.id')) + + Base.prepare(engine, reflect=True) + + +Using Automap with Explicit Declarations +======================================== + +As noted previously, automap has no dependency on reflection, and can make +use of any collection of :class:`.Table` objects within a :class:`.MetaData` +collection. From this, it follows that automap can also be used +generate missing relationships given an otherwise complete model that fully +defines table metadata:: + + from sqlalchemy.ext.automap import automap_base + from sqlalchemy import Column, Integer, String, ForeignKey + + Base = automap_base() + + class User(Base): + __tablename__ = 'user' + + id = Column(Integer, primary_key=True) + name = Column(String) + + class Address(Base): + __tablename__ = 'address' + + id = Column(Integer, primary_key=True) + email = Column(String) + user_id = Column(ForeignKey('user.id')) + + # produce relationships + Base.prepare() + + # mapping is complete, with "address_collection" and + # "user" relationships + a1 = Address(email='u1') + a2 = Address(email='u2') + u1 = User(address_collection=[a1, a2]) + assert a1.user is u1 + +Above, given mostly complete ``User`` and ``Address`` mappings, the +:class:`.ForeignKey` which we defined on ``Address.user_id`` allowed a +bidirectional relationship pair ``Address.user`` and +``User.address_collection`` to be generated on the mapped classes. + +Note that when subclassing :class:`.AutomapBase`, +the :meth:`.AutomapBase.prepare` method is required; if not called, the classes +we've declared are in an un-mapped state. + + +""" +from .declarative import declarative_base as _declarative_base +from .declarative.base import _DeferredMapperConfig +from ..sql import and_ +from ..schema import ForeignKeyConstraint +from ..orm import relationship, backref, interfaces +from ..orm.mapper import _CONFIGURE_MUTEX +from .. import util + + +def classname_for_table(base, tablename, table): + """Return the class name that should be used, given the name + of a table. + + The default implementation is:: + + return str(tablename) + + Alternate implementations can be specified using the + :paramref:`.AutomapBase.prepare.classname_for_table` + parameter. + + :param base: the :class:`.AutomapBase` class doing the prepare. + + :param tablename: string name of the :class:`.Table`. + + :param table: the :class:`.Table` object itself. + + :return: a string class name. + + .. note:: + + In Python 2, the string used for the class name **must** be a + non-Unicode object, e.g. a ``str()`` object. The ``.name`` attribute + of :class:`.Table` is typically a Python unicode subclass, so the + ``str()`` function should be applied to this name, after accounting for + any non-ASCII characters. + + """ + return str(tablename) + + +def name_for_scalar_relationship(base, local_cls, referred_cls, constraint): + """Return the attribute name that should be used to refer from one + class to another, for a scalar object reference. + + The default implementation is:: + + return referred_cls.__name__.lower() + + Alternate implementations can be specified using the + :paramref:`.AutomapBase.prepare.name_for_scalar_relationship` + parameter. + + :param base: the :class:`.AutomapBase` class doing the prepare. + + :param local_cls: the class to be mapped on the local side. + + :param referred_cls: the class to be mapped on the referring side. + + :param constraint: the :class:`.ForeignKeyConstraint` that is being + inspected to produce this relationship. + + """ + return referred_cls.__name__.lower() + + +def name_for_collection_relationship( + base, local_cls, referred_cls, constraint): + """Return the attribute name that should be used to refer from one + class to another, for a collection reference. + + The default implementation is:: + + return referred_cls.__name__.lower() + "_collection" + + Alternate implementations + can be specified using the + :paramref:`.AutomapBase.prepare.name_for_collection_relationship` + parameter. + + :param base: the :class:`.AutomapBase` class doing the prepare. + + :param local_cls: the class to be mapped on the local side. + + :param referred_cls: the class to be mapped on the referring side. + + :param constraint: the :class:`.ForeignKeyConstraint` that is being + inspected to produce this relationship. + + """ + return referred_cls.__name__.lower() + "_collection" + + +def generate_relationship( + base, direction, return_fn, attrname, local_cls, referred_cls, **kw): + r"""Generate a :func:`.relationship` or :func:`.backref` on behalf of two + mapped classes. + + An alternate implementation of this function can be specified using the + :paramref:`.AutomapBase.prepare.generate_relationship` parameter. + + The default implementation of this function is as follows:: + + if return_fn is backref: + return return_fn(attrname, **kw) + elif return_fn is relationship: + return return_fn(referred_cls, **kw) + else: + raise TypeError("Unknown relationship function: %s" % return_fn) + + :param base: the :class:`.AutomapBase` class doing the prepare. + + :param direction: indicate the "direction" of the relationship; this will + be one of :data:`.ONETOMANY`, :data:`.MANYTOONE`, :data:`.MANYTOMANY`. + + :param return_fn: the function that is used by default to create the + relationship. This will be either :func:`.relationship` or + :func:`.backref`. The :func:`.backref` function's result will be used to + produce a new :func:`.relationship` in a second step, so it is critical + that user-defined implementations correctly differentiate between the two + functions, if a custom relationship function is being used. + + :param attrname: the attribute name to which this relationship is being + assigned. If the value of :paramref:`.generate_relationship.return_fn` is + the :func:`.backref` function, then this name is the name that is being + assigned to the backref. + + :param local_cls: the "local" class to which this relationship or backref + will be locally present. + + :param referred_cls: the "referred" class to which the relationship or + backref refers to. + + :param \**kw: all additional keyword arguments are passed along to the + function. + + :return: a :func:`.relationship` or :func:`.backref` construct, as dictated + by the :paramref:`.generate_relationship.return_fn` parameter. + + """ + if return_fn is backref: + return return_fn(attrname, **kw) + elif return_fn is relationship: + return return_fn(referred_cls, **kw) + else: + raise TypeError("Unknown relationship function: %s" % return_fn) + + +class AutomapBase(object): + """Base class for an "automap" schema. + + The :class:`.AutomapBase` class can be compared to the "declarative base" + class that is produced by the :func:`.declarative.declarative_base` + function. In practice, the :class:`.AutomapBase` class is always used + as a mixin along with an actual declarative base. + + A new subclassable :class:`.AutomapBase` is typically instantated + using the :func:`.automap_base` function. + + .. seealso:: + + :ref:`automap_toplevel` + + """ + __abstract__ = True + + classes = None + """An instance of :class:`.util.Properties` containing classes. + + This object behaves much like the ``.c`` collection on a table. Classes + are present under the name they were given, e.g.:: + + Base = automap_base() + Base.prepare(engine=some_engine, reflect=True) + + User, Address = Base.classes.User, Base.classes.Address + + """ + + @classmethod + def prepare( + cls, + engine=None, + reflect=False, + schema=None, + classname_for_table=classname_for_table, + collection_class=list, + name_for_scalar_relationship=name_for_scalar_relationship, + name_for_collection_relationship=name_for_collection_relationship, + generate_relationship=generate_relationship): + """Extract mapped classes and relationships from the :class:`.MetaData` and + perform mappings. + + :param engine: an :class:`.Engine` or :class:`.Connection` with which + to perform schema reflection, if specified. + If the :paramref:`.AutomapBase.prepare.reflect` argument is False, + this object is not used. + + :param reflect: if True, the :meth:`.MetaData.reflect` method is called + on the :class:`.MetaData` associated with this :class:`.AutomapBase`. + The :class:`.Engine` passed via + :paramref:`.AutomapBase.prepare.engine` will be used to perform the + reflection if present; else, the :class:`.MetaData` should already be + bound to some engine else the operation will fail. + + :param classname_for_table: callable function which will be used to + produce new class names, given a table name. Defaults to + :func:`.classname_for_table`. + + :param name_for_scalar_relationship: callable function which will be + used to produce relationship names for scalar relationships. Defaults + to :func:`.name_for_scalar_relationship`. + + :param name_for_collection_relationship: callable function which will + be used to produce relationship names for collection-oriented + relationships. Defaults to :func:`.name_for_collection_relationship`. + + :param generate_relationship: callable function which will be used to + actually generate :func:`.relationship` and :func:`.backref` + constructs. Defaults to :func:`.generate_relationship`. + + :param collection_class: the Python collection class that will be used + when a new :func:`.relationship` object is created that represents a + collection. Defaults to ``list``. + + :param schema: When present in conjunction with the + :paramref:`.AutomapBase.prepare.reflect` flag, is passed to + :meth:`.MetaData.reflect` to indicate the primary schema where tables + should be reflected from. When omitted, the default schema in use + by the database connection is used. + + .. versionadded:: 1.1 + + """ + if reflect: + cls.metadata.reflect( + engine, + schema=schema, + extend_existing=True, + autoload_replace=False + ) + + _CONFIGURE_MUTEX.acquire() + try: + table_to_map_config = dict( + (m.local_table, m) + for m in _DeferredMapperConfig. + classes_for_base(cls, sort=False) + ) + + many_to_many = [] + + for table in cls.metadata.tables.values(): + lcl_m2m, rem_m2m, m2m_const = _is_many_to_many(cls, table) + if lcl_m2m is not None: + many_to_many.append((lcl_m2m, rem_m2m, m2m_const, table)) + elif not table.primary_key: + continue + elif table not in table_to_map_config: + mapped_cls = type( + classname_for_table(cls, table.name, table), + (cls, ), + {"__table__": table} + ) + map_config = _DeferredMapperConfig.config_for_cls( + mapped_cls) + cls.classes[map_config.cls.__name__] = mapped_cls + table_to_map_config[table] = map_config + + for map_config in table_to_map_config.values(): + _relationships_for_fks(cls, + map_config, + table_to_map_config, + collection_class, + name_for_scalar_relationship, + name_for_collection_relationship, + generate_relationship) + + for lcl_m2m, rem_m2m, m2m_const, table in many_to_many: + _m2m_relationship(cls, lcl_m2m, rem_m2m, m2m_const, table, + table_to_map_config, + collection_class, + name_for_scalar_relationship, + name_for_collection_relationship, + generate_relationship) + + for map_config in _DeferredMapperConfig.classes_for_base(cls): + map_config.map() + finally: + _CONFIGURE_MUTEX.release() + + _sa_decl_prepare = True + """Indicate that the mapping of classes should be deferred. + + The presence of this attribute name indicates to declarative + that the call to mapper() should not occur immediately; instead, + information about the table and attributes to be mapped are gathered + into an internal structure called _DeferredMapperConfig. These + objects can be collected later using classes_for_base(), additional + mapping decisions can be made, and then the map() method will actually + apply the mapping. + + The only real reason this deferral of the whole + thing is needed is to support primary key columns that aren't reflected + yet when the class is declared; everything else can theoretically be + added to the mapper later. However, the _DeferredMapperConfig is a + nice interface in any case which exists at that not usually exposed point + at which declarative has the class and the Table but hasn't called + mapper() yet. + + """ + + +def automap_base(declarative_base=None, **kw): + r"""Produce a declarative automap base. + + This function produces a new base class that is a product of the + :class:`.AutomapBase` class as well a declarative base produced by + :func:`.declarative.declarative_base`. + + All parameters other than ``declarative_base`` are keyword arguments + that are passed directly to the :func:`.declarative.declarative_base` + function. + + :param declarative_base: an existing class produced by + :func:`.declarative.declarative_base`. When this is passed, the function + no longer invokes :func:`.declarative.declarative_base` itself, and all + other keyword arguments are ignored. + + :param \**kw: keyword arguments are passed along to + :func:`.declarative.declarative_base`. + + """ + if declarative_base is None: + Base = _declarative_base(**kw) + else: + Base = declarative_base + + return type( + Base.__name__, + (AutomapBase, Base,), + {"__abstract__": True, "classes": util.Properties({})} + ) + + +def _is_many_to_many(automap_base, table): + fk_constraints = [const for const in table.constraints + if isinstance(const, ForeignKeyConstraint)] + if len(fk_constraints) != 2: + return None, None, None + + cols = sum( + [[fk.parent for fk in fk_constraint.elements] + for fk_constraint in fk_constraints], []) + + if set(cols) != set(table.c): + return None, None, None + + return ( + fk_constraints[0].elements[0].column.table, + fk_constraints[1].elements[0].column.table, + fk_constraints + ) + + +def _relationships_for_fks(automap_base, map_config, table_to_map_config, + collection_class, + name_for_scalar_relationship, + name_for_collection_relationship, + generate_relationship): + local_table = map_config.local_table + local_cls = map_config.cls # derived from a weakref, may be None + + if local_table is None or local_cls is None: + return + for constraint in local_table.constraints: + if isinstance(constraint, ForeignKeyConstraint): + fks = constraint.elements + referred_table = fks[0].column.table + referred_cfg = table_to_map_config.get(referred_table, None) + if referred_cfg is None: + continue + referred_cls = referred_cfg.cls + + if local_cls is not referred_cls and issubclass( + local_cls, referred_cls): + continue + + relationship_name = name_for_scalar_relationship( + automap_base, + local_cls, + referred_cls, constraint) + backref_name = name_for_collection_relationship( + automap_base, + referred_cls, + local_cls, + constraint + ) + + o2m_kws = {} + nullable = False not in {fk.parent.nullable for fk in fks} + if not nullable: + o2m_kws['cascade'] = "all, delete-orphan" + + if constraint.ondelete and \ + constraint.ondelete.lower() == "cascade": + o2m_kws['passive_deletes'] = True + else: + if constraint.ondelete and \ + constraint.ondelete.lower() == "set null": + o2m_kws['passive_deletes'] = True + + create_backref = backref_name not in referred_cfg.properties + + if relationship_name not in map_config.properties: + if create_backref: + backref_obj = generate_relationship( + automap_base, + interfaces.ONETOMANY, backref, + backref_name, referred_cls, local_cls, + collection_class=collection_class, + **o2m_kws) + else: + backref_obj = None + rel = generate_relationship(automap_base, + interfaces.MANYTOONE, + relationship, + relationship_name, + local_cls, referred_cls, + foreign_keys=[ + fk.parent + for fk in constraint.elements], + backref=backref_obj, + remote_side=[ + fk.column + for fk in constraint.elements] + ) + if rel is not None: + map_config.properties[relationship_name] = rel + if not create_backref: + referred_cfg.properties[ + backref_name].back_populates = relationship_name + elif create_backref: + rel = generate_relationship(automap_base, + interfaces.ONETOMANY, + relationship, + backref_name, + referred_cls, local_cls, + foreign_keys=[ + fk.parent + for fk in constraint.elements], + back_populates=relationship_name, + collection_class=collection_class, + **o2m_kws) + if rel is not None: + referred_cfg.properties[backref_name] = rel + map_config.properties[ + relationship_name].back_populates = backref_name + + +def _m2m_relationship(automap_base, lcl_m2m, rem_m2m, m2m_const, table, + table_to_map_config, + collection_class, + name_for_scalar_relationship, + name_for_collection_relationship, + generate_relationship): + + map_config = table_to_map_config.get(lcl_m2m, None) + referred_cfg = table_to_map_config.get(rem_m2m, None) + if map_config is None or referred_cfg is None: + return + + local_cls = map_config.cls + referred_cls = referred_cfg.cls + + relationship_name = name_for_collection_relationship( + automap_base, + local_cls, + referred_cls, m2m_const[0]) + backref_name = name_for_collection_relationship( + automap_base, + referred_cls, + local_cls, + m2m_const[1] + ) + + create_backref = backref_name not in referred_cfg.properties + + if relationship_name not in map_config.properties: + if create_backref: + backref_obj = generate_relationship( + automap_base, + interfaces.MANYTOMANY, + backref, + backref_name, + referred_cls, local_cls, + collection_class=collection_class + ) + else: + backref_obj = None + rel = generate_relationship(automap_base, + interfaces.MANYTOMANY, + relationship, + relationship_name, + local_cls, referred_cls, + secondary=table, + primaryjoin=and_( + fk.column == fk.parent + for fk in m2m_const[0].elements), + secondaryjoin=and_( + fk.column == fk.parent + for fk in m2m_const[1].elements), + backref=backref_obj, + collection_class=collection_class + ) + if rel is not None: + map_config.properties[relationship_name] = rel + + if not create_backref: + referred_cfg.properties[ + backref_name].back_populates = relationship_name + elif create_backref: + rel = generate_relationship(automap_base, + interfaces.MANYTOMANY, + relationship, + backref_name, + referred_cls, local_cls, + secondary=table, + primaryjoin=and_( + fk.column == fk.parent + for fk in m2m_const[1].elements), + secondaryjoin=and_( + fk.column == fk.parent + for fk in m2m_const[0].elements), + back_populates=relationship_name, + collection_class=collection_class) + if rel is not None: + referred_cfg.properties[backref_name] = rel + map_config.properties[ + relationship_name].back_populates = backref_name diff --git a/venv/Lib/site-packages/sqlalchemy/ext/baked.py b/venv/Lib/site-packages/sqlalchemy/ext/baked.py new file mode 100644 index 0000000..72fe929 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/baked.py @@ -0,0 +1,585 @@ +# sqlalchemy/ext/baked.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +"""Baked query extension. + +Provides a creational pattern for the :class:`.query.Query` object which +allows the fully constructed object, Core select statement, and string +compiled result to be fully cached. + + +""" + +from ..orm.query import Query +from ..orm import strategy_options +from ..sql import util as sql_util, func, literal_column +from ..orm import exc as orm_exc +from .. import exc as sa_exc +from .. import util + +import copy +import logging + +log = logging.getLogger(__name__) + + +class Bakery(object): + """Callable which returns a :class:`.BakedQuery`. + + This object is returned by the class method + :meth:`.BakedQuery.bakery`. It exists as an object + so that the "cache" can be easily inspected. + + .. versionadded:: 1.2 + + + """ + __slots__ = 'cls', 'cache' + + def __init__(self, cls_, cache): + self.cls = cls_ + self.cache = cache + + def __call__(self, initial_fn, *args): + return self.cls(self.cache, initial_fn, args) + + +class BakedQuery(object): + """A builder object for :class:`.query.Query` objects.""" + + __slots__ = 'steps', '_bakery', '_cache_key', '_spoiled' + + def __init__(self, bakery, initial_fn, args=()): + self._cache_key = () + self._update_cache_key(initial_fn, args) + self.steps = [initial_fn] + self._spoiled = False + self._bakery = bakery + + @classmethod + def bakery(cls, size=200, _size_alert=None): + """Construct a new bakery. + + :return: an instance of :class:`.Bakery` + + """ + + return Bakery(cls, util.LRUCache(size, size_alert=_size_alert)) + + def _clone(self): + b1 = BakedQuery.__new__(BakedQuery) + b1._cache_key = self._cache_key + b1.steps = list(self.steps) + b1._bakery = self._bakery + b1._spoiled = self._spoiled + return b1 + + def _update_cache_key(self, fn, args=()): + self._cache_key += (fn.__code__,) + args + + def __iadd__(self, other): + if isinstance(other, tuple): + self.add_criteria(*other) + else: + self.add_criteria(other) + return self + + def __add__(self, other): + if isinstance(other, tuple): + return self.with_criteria(*other) + else: + return self.with_criteria(other) + + def add_criteria(self, fn, *args): + """Add a criteria function to this :class:`.BakedQuery`. + + This is equivalent to using the ``+=`` operator to + modify a :class:`.BakedQuery` in-place. + + """ + self._update_cache_key(fn, args) + self.steps.append(fn) + return self + + def with_criteria(self, fn, *args): + """Add a criteria function to a :class:`.BakedQuery` cloned from this one. + + This is equivalent to using the ``+`` operator to + produce a new :class:`.BakedQuery` with modifications. + + """ + return self._clone().add_criteria(fn, *args) + + def for_session(self, session): + """Return a :class:`.Result` object for this :class:`.BakedQuery`. + + This is equivalent to calling the :class:`.BakedQuery` as a + Python callable, e.g. ``result = my_baked_query(session)``. + + """ + return Result(self, session) + + def __call__(self, session): + return self.for_session(session) + + def spoil(self, full=False): + """Cancel any query caching that will occur on this BakedQuery object. + + The BakedQuery can continue to be used normally, however additional + creational functions will not be cached; they will be called + on every invocation. + + This is to support the case where a particular step in constructing + a baked query disqualifies the query from being cacheable, such + as a variant that relies upon some uncacheable value. + + :param full: if False, only functions added to this + :class:`.BakedQuery` object subsequent to the spoil step will be + non-cached; the state of the :class:`.BakedQuery` up until + this point will be pulled from the cache. If True, then the + entire :class:`.Query` object is built from scratch each + time, with all creational functions being called on each + invocation. + + """ + if not full and not self._spoiled: + _spoil_point = self._clone() + _spoil_point._cache_key += ('_query_only', ) + self.steps = [_spoil_point._retrieve_baked_query] + self._spoiled = True + return self + + def _effective_key(self, session): + """Return the key that actually goes into the cache dictionary for + this :class:`.BakedQuery`, taking into account the given + :class:`.Session`. + + This basically means we also will include the session's query_class, + as the actual :class:`.Query` object is part of what's cached + and needs to match the type of :class:`.Query` that a later + session will want to use. + + """ + return self._cache_key + (session._query_cls, ) + + def _with_lazyload_options(self, options, effective_path, cache_path=None): + """Cloning version of _add_lazyload_options. + """ + q = self._clone() + q._add_lazyload_options(options, effective_path, cache_path=cache_path) + return q + + def _add_lazyload_options(self, options, effective_path, cache_path=None): + """Used by per-state lazy loaders to add options to the + "lazy load" query from a parent query. + + Creates a cache key based on given load path and query options; + if a repeatable cache key cannot be generated, the query is + "spoiled" so that it won't use caching. + + """ + + key = () + + if not cache_path: + cache_path = effective_path + + if cache_path.path[0].is_aliased_class: + # paths that are against an AliasedClass are unsafe to cache + # with since the AliasedClass is an ad-hoc object. + self.spoil() + else: + for opt in options: + cache_key = opt._generate_cache_key(cache_path) + if cache_key is False: + self.spoil() + elif cache_key is not None: + key += cache_key + + self.add_criteria( + lambda q: q._with_current_path(effective_path). + _conditional_options(*options), + cache_path.path, key + ) + + def _retrieve_baked_query(self, session): + query = self._bakery.get(self._effective_key(session), None) + if query is None: + query = self._as_query(session) + self._bakery[self._effective_key(session)] = query.with_session(None) + return query.with_session(session) + + def _bake(self, session): + query = self._as_query(session) + + context = query._compile_context() + self._bake_subquery_loaders(session, context) + context.session = None + context.query = query = context.query.with_session(None) + query._execution_options = query._execution_options.union( + {"compiled_cache": self._bakery} + ) + # we'll be holding onto the query for some of its state, + # so delete some compilation-use-only attributes that can take up + # space + for attr in ( + '_correlate', '_from_obj', '_mapper_adapter_map', + '_joinpath', '_joinpoint'): + query.__dict__.pop(attr, None) + self._bakery[self._effective_key(session)] = context + return context + + def _as_query(self, session): + query = self.steps[0](session) + + for step in self.steps[1:]: + query = step(query) + return query + + def _bake_subquery_loaders(self, session, context): + """convert subquery eager loaders in the cache into baked queries. + + For subquery eager loading to work, all we need here is that the + Query point to the correct session when it is run. However, since + we are "baking" anyway, we may as well also turn the query into + a "baked" query so that we save on performance too. + + """ + context.attributes['baked_queries'] = baked_queries = [] + for k, v in list(context.attributes.items()): + if isinstance(v, Query): + if 'subquery' in k: + bk = BakedQuery(self._bakery, lambda *args: v) + bk._cache_key = self._cache_key + k + bk._bake(session) + baked_queries.append((k, bk._cache_key, v)) + del context.attributes[k] + + def _unbake_subquery_loaders( + self, session, context, params, post_criteria): + """Retrieve subquery eager loaders stored by _bake_subquery_loaders + and turn them back into Result objects that will iterate just + like a Query object. + + """ + for k, cache_key, query in context.attributes["baked_queries"]: + bk = BakedQuery(self._bakery, + lambda sess, q=query: q.with_session(sess)) + bk._cache_key = cache_key + q = bk.for_session(session) + for fn in post_criteria: + q = q.with_post_criteria(fn) + context.attributes[k] = q.params(**params) + + +class Result(object): + """Invokes a :class:`.BakedQuery` against a :class:`.Session`. + + The :class:`.Result` object is where the actual :class:`.query.Query` + object gets created, or retrieved from the cache, + against a target :class:`.Session`, and is then invoked for results. + + """ + __slots__ = 'bq', 'session', '_params', '_post_criteria' + + def __init__(self, bq, session): + self.bq = bq + self.session = session + self._params = {} + self._post_criteria = [] + + def params(self, *args, **kw): + """Specify parameters to be replaced into the string SQL statement.""" + + if len(args) == 1: + kw.update(args[0]) + elif len(args) > 0: + raise sa_exc.ArgumentError( + "params() takes zero or one positional argument, " + "which is a dictionary.") + self._params.update(kw) + return self + + def _using_post_criteria(self, fns): + if fns: + self._post_criteria.extend(fns) + return self + + def with_post_criteria(self, fn): + """Add a criteria function that will be applied post-cache. + + This adds a function that will be run against the + :class:`.Query` object after it is retrieved from the + cache. Functions here can be used to alter the query in ways + that **do not affect the SQL output**, such as execution options + and shard identifiers (when using a shard-enabled query object) + + .. warning:: :meth:`.Result.with_post_criteria` functions are applied + to the :class:`.Query` object **after** the query's SQL statement + object has been retrieved from the cache. Any operations here + which intend to modify the SQL should ensure that + :meth:`.BakedQuery.spoil` was called first. + + .. versionadded:: 1.2 + + + """ + return self._using_post_criteria([fn]) + + def _as_query(self): + q = self.bq._as_query(self.session).params(self._params) + for fn in self._post_criteria: + q = fn(q) + return q + + def __str__(self): + return str(self._as_query()) + + def __iter__(self): + bq = self.bq + if not self.session.enable_baked_queries or bq._spoiled: + return iter(self._as_query()) + + baked_context = bq._bakery.get(bq._effective_key(self.session), None) + if baked_context is None: + baked_context = bq._bake(self.session) + + context = copy.copy(baked_context) + context.session = self.session + context.attributes = context.attributes.copy() + + bq._unbake_subquery_loaders( + self.session, context, self._params, self._post_criteria) + + context.statement.use_labels = True + if context.autoflush and not context.populate_existing: + self.session._autoflush() + q = context.query.params(self._params).with_session(self.session) + for fn in self._post_criteria: + q = fn(q) + + return q._execute_and_instances(context) + + def count(self): + """return the 'count'. + + Equivalent to :meth:`.Query.count`. + + Note this uses a subquery to ensure an accurate count regardless + of the structure of the original statement. + + .. versionadded:: 1.1.6 + + """ + + col = func.count(literal_column('*')) + bq = self.bq.with_criteria(lambda q: q.from_self(col)) + return bq.for_session(self.session).params(self._params).scalar() + + def scalar(self): + """Return the first element of the first result or None + if no rows present. If multiple rows are returned, + raises MultipleResultsFound. + + Equivalent to :meth:`.Query.scalar`. + + .. versionadded:: 1.1.6 + + """ + try: + ret = self.one() + if not isinstance(ret, tuple): + return ret + return ret[0] + except orm_exc.NoResultFound: + return None + + def first(self): + """Return the first row. + + Equivalent to :meth:`.Query.first`. + + """ + bq = self.bq.with_criteria(lambda q: q.slice(0, 1)) + ret = list( + bq.for_session(self.session).params(self._params). + _using_post_criteria(self._post_criteria)) + if len(ret) > 0: + return ret[0] + else: + return None + + def one(self): + """Return exactly one result or raise an exception. + + Equivalent to :meth:`.Query.one`. + + """ + try: + ret = self.one_or_none() + except orm_exc.MultipleResultsFound: + raise orm_exc.MultipleResultsFound( + "Multiple rows were found for one()") + else: + if ret is None: + raise orm_exc.NoResultFound("No row was found for one()") + return ret + + def one_or_none(self): + """Return one or zero results, or raise an exception for multiple + rows. + + Equivalent to :meth:`.Query.one_or_none`. + + .. versionadded:: 1.0.9 + + """ + ret = list(self) + + l = len(ret) + if l == 1: + return ret[0] + elif l == 0: + return None + else: + raise orm_exc.MultipleResultsFound( + "Multiple rows were found for one_or_none()") + + def all(self): + """Return all rows. + + Equivalent to :meth:`.Query.all`. + + """ + return list(self) + + def get(self, ident): + """Retrieve an object based on identity. + + Equivalent to :meth:`.Query.get`. + + """ + + query = self.bq.steps[0](self.session) + return query._get_impl(ident, self._load_on_pk_identity) + + def _load_on_pk_identity(self, query, primary_key_identity): + """Load the given primary key identity from the database.""" + + mapper = query._mapper_zero() + + _get_clause, _get_params = mapper._get_clause + + def setup(query): + _lcl_get_clause = _get_clause + q = query._clone() + q._get_condition() + q._order_by = None + + # None present in ident - turn those comparisons + # into "IS NULL" + if None in primary_key_identity: + nones = set([ + _get_params[col].key for col, value in + zip(mapper.primary_key, primary_key_identity) + if value is None + ]) + _lcl_get_clause = sql_util.adapt_criterion_to_null( + _lcl_get_clause, nones) + + _lcl_get_clause = q._adapt_clause(_lcl_get_clause, True, False) + q._criterion = _lcl_get_clause + for fn in self._post_criteria: + q = fn(q) + return q + + # cache the query against a key that includes + # which positions in the primary key are NULL + # (remember, we can map to an OUTER JOIN) + bq = self.bq + + # add the clause we got from mapper._get_clause to the cache + # key so that if a race causes multiple calls to _get_clause, + # we've cached on ours + bq = bq._clone() + bq._cache_key += (_get_clause, ) + + bq = bq.with_criteria( + setup, tuple(elem is None for elem in primary_key_identity)) + + params = dict([ + (_get_params[primary_key].key, id_val) + for id_val, primary_key + in zip(primary_key_identity, mapper.primary_key) + ]) + + result = list(bq.for_session(self.session).params(**params)) + l = len(result) + if l > 1: + raise orm_exc.MultipleResultsFound() + elif l: + return result[0] + else: + return None + + +@util.deprecated( + "1.2", "Baked lazy loading is now the default implementation.") +def bake_lazy_loaders(): + """Enable the use of baked queries for all lazyloaders systemwide. + + The "baked" implementation of lazy loading is now the sole implementation + for the base lazy loader; this method has no effect except for a warning. + + """ + pass + + +@util.deprecated( + "1.2", "Baked lazy loading is now the default implementation.") +def unbake_lazy_loaders(): + """Disable the use of baked queries for all lazyloaders systemwide. + + This method now raises NotImplmentedError() as the "baked" implementation + is the only lazy load implementation. The + :paramref:`.relationship.bake_queries` flag may be used to disable + the caching of queries on a per-relationship basis. + + """ + raise NotImplementedError( + "Baked lazy loading is now the default implementation") + + +@strategy_options.loader_option() +def baked_lazyload(loadopt, attr): + """Indicate that the given attribute should be loaded using "lazy" + loading with a "baked" query used in the load. + + """ + return loadopt.set_relationship_strategy(attr, {"lazy": "baked_select"}) + + +@baked_lazyload._add_unbound_fn +@util.deprecated( + "1.2", "Baked lazy loading is now the default " + "implementation for lazy loading.") +def baked_lazyload(*keys): + return strategy_options._UnboundLoad._from_keys( + strategy_options._UnboundLoad.baked_lazyload, keys, False, {}) + + +@baked_lazyload._add_unbound_all_fn +@util.deprecated( + "1.2", "Baked lazy loading is now the default " + "implementation for lazy loading.") +def baked_lazyload_all(*keys): + return strategy_options._UnboundLoad._from_keys( + strategy_options._UnboundLoad.baked_lazyload, keys, True, {}) + +baked_lazyload = baked_lazyload._unbound_fn +baked_lazyload_all = baked_lazyload_all._unbound_all_fn + +bakery = BakedQuery.bakery diff --git a/venv/Lib/site-packages/sqlalchemy/ext/compiler.py b/venv/Lib/site-packages/sqlalchemy/ext/compiler.py new file mode 100644 index 0000000..b56943b --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/compiler.py @@ -0,0 +1,474 @@ +# ext/compiler.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +r"""Provides an API for creation of custom ClauseElements and compilers. + +Synopsis +======== + +Usage involves the creation of one or more +:class:`~sqlalchemy.sql.expression.ClauseElement` subclasses and one or +more callables defining its compilation:: + + from sqlalchemy.ext.compiler import compiles + from sqlalchemy.sql.expression import ColumnClause + + class MyColumn(ColumnClause): + pass + + @compiles(MyColumn) + def compile_mycolumn(element, compiler, **kw): + return "[%s]" % element.name + +Above, ``MyColumn`` extends :class:`~sqlalchemy.sql.expression.ColumnClause`, +the base expression element for named column objects. The ``compiles`` +decorator registers itself with the ``MyColumn`` class so that it is invoked +when the object is compiled to a string:: + + from sqlalchemy import select + + s = select([MyColumn('x'), MyColumn('y')]) + print str(s) + +Produces:: + + SELECT [x], [y] + +Dialect-specific compilation rules +================================== + +Compilers can also be made dialect-specific. The appropriate compiler will be +invoked for the dialect in use:: + + from sqlalchemy.schema import DDLElement + + class AlterColumn(DDLElement): + + def __init__(self, column, cmd): + self.column = column + self.cmd = cmd + + @compiles(AlterColumn) + def visit_alter_column(element, compiler, **kw): + return "ALTER COLUMN %s ..." % element.column.name + + @compiles(AlterColumn, 'postgresql') + def visit_alter_column(element, compiler, **kw): + return "ALTER TABLE %s ALTER COLUMN %s ..." % (element.table.name, + element.column.name) + +The second ``visit_alter_table`` will be invoked when any ``postgresql`` +dialect is used. + +Compiling sub-elements of a custom expression construct +======================================================= + +The ``compiler`` argument is the +:class:`~sqlalchemy.engine.interfaces.Compiled` object in use. This object +can be inspected for any information about the in-progress compilation, +including ``compiler.dialect``, ``compiler.statement`` etc. The +:class:`~sqlalchemy.sql.compiler.SQLCompiler` and +:class:`~sqlalchemy.sql.compiler.DDLCompiler` both include a ``process()`` +method which can be used for compilation of embedded attributes:: + + from sqlalchemy.sql.expression import Executable, ClauseElement + + class InsertFromSelect(Executable, ClauseElement): + def __init__(self, table, select): + self.table = table + self.select = select + + @compiles(InsertFromSelect) + def visit_insert_from_select(element, compiler, **kw): + return "INSERT INTO %s (%s)" % ( + compiler.process(element.table, asfrom=True), + compiler.process(element.select) + ) + + insert = InsertFromSelect(t1, select([t1]).where(t1.c.x>5)) + print insert + +Produces:: + + "INSERT INTO mytable (SELECT mytable.x, mytable.y, mytable.z + FROM mytable WHERE mytable.x > :x_1)" + +.. note:: + + The above ``InsertFromSelect`` construct is only an example, this actual + functionality is already available using the + :meth:`.Insert.from_select` method. + +.. note:: + + The above ``InsertFromSelect`` construct probably wants to have "autocommit" + enabled. See :ref:`enabling_compiled_autocommit` for this step. + +Cross Compiling between SQL and DDL compilers +--------------------------------------------- + +SQL and DDL constructs are each compiled using different base compilers - +``SQLCompiler`` and ``DDLCompiler``. A common need is to access the +compilation rules of SQL expressions from within a DDL expression. The +``DDLCompiler`` includes an accessor ``sql_compiler`` for this reason, such as +below where we generate a CHECK constraint that embeds a SQL expression:: + + @compiles(MyConstraint) + def compile_my_constraint(constraint, ddlcompiler, **kw): + return "CONSTRAINT %s CHECK (%s)" % ( + constraint.name, + ddlcompiler.sql_compiler.process( + constraint.expression, literal_binds=True) + ) + +Above, we add an additional flag to the process step as called by +:meth:`.SQLCompiler.process`, which is the ``literal_binds`` flag. This +indicates that any SQL expression which refers to a :class:`.BindParameter` +object or other "literal" object such as those which refer to strings or +integers should be rendered **in-place**, rather than being referred to as +a bound parameter; when emitting DDL, bound parameters are typically not +supported. + + +.. _enabling_compiled_autocommit: + +Enabling Autocommit on a Construct +================================== + +Recall from the section :ref:`autocommit` that the :class:`.Engine`, when +asked to execute a construct in the absence of a user-defined transaction, +detects if the given construct represents DML or DDL, that is, a data +modification or data definition statement, which requires (or may require, +in the case of DDL) that the transaction generated by the DBAPI be committed +(recall that DBAPI always has a transaction going on regardless of what +SQLAlchemy does). Checking for this is actually accomplished by checking for +the "autocommit" execution option on the construct. When building a +construct like an INSERT derivation, a new DDL type, or perhaps a stored +procedure that alters data, the "autocommit" option needs to be set in order +for the statement to function with "connectionless" execution +(as described in :ref:`dbengine_implicit`). + +Currently a quick way to do this is to subclass :class:`.Executable`, then +add the "autocommit" flag to the ``_execution_options`` dictionary (note this +is a "frozen" dictionary which supplies a generative ``union()`` method):: + + from sqlalchemy.sql.expression import Executable, ClauseElement + + class MyInsertThing(Executable, ClauseElement): + _execution_options = \ + Executable._execution_options.union({'autocommit': True}) + +More succinctly, if the construct is truly similar to an INSERT, UPDATE, or +DELETE, :class:`.UpdateBase` can be used, which already is a subclass +of :class:`.Executable`, :class:`.ClauseElement` and includes the +``autocommit`` flag:: + + from sqlalchemy.sql.expression import UpdateBase + + class MyInsertThing(UpdateBase): + def __init__(self, ...): + ... + + + + +DDL elements that subclass :class:`.DDLElement` already have the +"autocommit" flag turned on. + + + + +Changing the default compilation of existing constructs +======================================================= + +The compiler extension applies just as well to the existing constructs. When +overriding the compilation of a built in SQL construct, the @compiles +decorator is invoked upon the appropriate class (be sure to use the class, +i.e. ``Insert`` or ``Select``, instead of the creation function such +as ``insert()`` or ``select()``). + +Within the new compilation function, to get at the "original" compilation +routine, use the appropriate visit_XXX method - this +because compiler.process() will call upon the overriding routine and cause +an endless loop. Such as, to add "prefix" to all insert statements:: + + from sqlalchemy.sql.expression import Insert + + @compiles(Insert) + def prefix_inserts(insert, compiler, **kw): + return compiler.visit_insert(insert.prefix_with("some prefix"), **kw) + +The above compiler will prefix all INSERT statements with "some prefix" when +compiled. + +.. _type_compilation_extension: + +Changing Compilation of Types +============================= + +``compiler`` works for types, too, such as below where we implement the +MS-SQL specific 'max' keyword for ``String``/``VARCHAR``:: + + @compiles(String, 'mssql') + @compiles(VARCHAR, 'mssql') + def compile_varchar(element, compiler, **kw): + if element.length == 'max': + return "VARCHAR('max')" + else: + return compiler.visit_VARCHAR(element, **kw) + + foo = Table('foo', metadata, + Column('data', VARCHAR('max')) + ) + +Subclassing Guidelines +====================== + +A big part of using the compiler extension is subclassing SQLAlchemy +expression constructs. To make this easier, the expression and +schema packages feature a set of "bases" intended for common tasks. +A synopsis is as follows: + +* :class:`~sqlalchemy.sql.expression.ClauseElement` - This is the root + expression class. Any SQL expression can be derived from this base, and is + probably the best choice for longer constructs such as specialized INSERT + statements. + +* :class:`~sqlalchemy.sql.expression.ColumnElement` - The root of all + "column-like" elements. Anything that you'd place in the "columns" clause of + a SELECT statement (as well as order by and group by) can derive from this - + the object will automatically have Python "comparison" behavior. + + :class:`~sqlalchemy.sql.expression.ColumnElement` classes want to have a + ``type`` member which is expression's return type. This can be established + at the instance level in the constructor, or at the class level if its + generally constant:: + + class timestamp(ColumnElement): + type = TIMESTAMP() + +* :class:`~sqlalchemy.sql.functions.FunctionElement` - This is a hybrid of a + ``ColumnElement`` and a "from clause" like object, and represents a SQL + function or stored procedure type of call. Since most databases support + statements along the line of "SELECT FROM <some function>" + ``FunctionElement`` adds in the ability to be used in the FROM clause of a + ``select()`` construct:: + + from sqlalchemy.sql.expression import FunctionElement + + class coalesce(FunctionElement): + name = 'coalesce' + + @compiles(coalesce) + def compile(element, compiler, **kw): + return "coalesce(%s)" % compiler.process(element.clauses) + + @compiles(coalesce, 'oracle') + def compile(element, compiler, **kw): + if len(element.clauses) > 2: + raise TypeError("coalesce only supports two arguments on Oracle") + return "nvl(%s)" % compiler.process(element.clauses) + +* :class:`~sqlalchemy.schema.DDLElement` - The root of all DDL expressions, + like CREATE TABLE, ALTER TABLE, etc. Compilation of ``DDLElement`` + subclasses is issued by a ``DDLCompiler`` instead of a ``SQLCompiler``. + ``DDLElement`` also features ``Table`` and ``MetaData`` event hooks via the + ``execute_at()`` method, allowing the construct to be invoked during CREATE + TABLE and DROP TABLE sequences. + +* :class:`~sqlalchemy.sql.expression.Executable` - This is a mixin which + should be used with any expression class that represents a "standalone" + SQL statement that can be passed directly to an ``execute()`` method. It + is already implicit within ``DDLElement`` and ``FunctionElement``. + +Further Examples +================ + +"UTC timestamp" function +------------------------- + +A function that works like "CURRENT_TIMESTAMP" except applies the +appropriate conversions so that the time is in UTC time. Timestamps are best +stored in relational databases as UTC, without time zones. UTC so that your +database doesn't think time has gone backwards in the hour when daylight +savings ends, without timezones because timezones are like character +encodings - they're best applied only at the endpoints of an application +(i.e. convert to UTC upon user input, re-apply desired timezone upon display). + +For PostgreSQL and Microsoft SQL Server:: + + from sqlalchemy.sql import expression + from sqlalchemy.ext.compiler import compiles + from sqlalchemy.types import DateTime + + class utcnow(expression.FunctionElement): + type = DateTime() + + @compiles(utcnow, 'postgresql') + def pg_utcnow(element, compiler, **kw): + return "TIMEZONE('utc', CURRENT_TIMESTAMP)" + + @compiles(utcnow, 'mssql') + def ms_utcnow(element, compiler, **kw): + return "GETUTCDATE()" + +Example usage:: + + from sqlalchemy import ( + Table, Column, Integer, String, DateTime, MetaData + ) + metadata = MetaData() + event = Table("event", metadata, + Column("id", Integer, primary_key=True), + Column("description", String(50), nullable=False), + Column("timestamp", DateTime, server_default=utcnow()) + ) + +"GREATEST" function +------------------- + +The "GREATEST" function is given any number of arguments and returns the one +that is of the highest value - its equivalent to Python's ``max`` +function. A SQL standard version versus a CASE based version which only +accommodates two arguments:: + + from sqlalchemy.sql import expression + from sqlalchemy.ext.compiler import compiles + from sqlalchemy.types import Numeric + + class greatest(expression.FunctionElement): + type = Numeric() + name = 'greatest' + + @compiles(greatest) + def default_greatest(element, compiler, **kw): + return compiler.visit_function(element) + + @compiles(greatest, 'sqlite') + @compiles(greatest, 'mssql') + @compiles(greatest, 'oracle') + def case_greatest(element, compiler, **kw): + arg1, arg2 = list(element.clauses) + return "CASE WHEN %s > %s THEN %s ELSE %s END" % ( + compiler.process(arg1), + compiler.process(arg2), + compiler.process(arg1), + compiler.process(arg2), + ) + +Example usage:: + + Session.query(Account).\ + filter( + greatest( + Account.checking_balance, + Account.savings_balance) > 10000 + ) + +"false" expression +------------------ + +Render a "false" constant expression, rendering as "0" on platforms that +don't have a "false" constant:: + + from sqlalchemy.sql import expression + from sqlalchemy.ext.compiler import compiles + + class sql_false(expression.ColumnElement): + pass + + @compiles(sql_false) + def default_false(element, compiler, **kw): + return "false" + + @compiles(sql_false, 'mssql') + @compiles(sql_false, 'mysql') + @compiles(sql_false, 'oracle') + def int_false(element, compiler, **kw): + return "0" + +Example usage:: + + from sqlalchemy import select, union_all + + exp = union_all( + select([users.c.name, sql_false().label("enrolled")]), + select([customers.c.name, customers.c.enrolled]) + ) + +""" +from .. import exc +from ..sql import visitors + + +def compiles(class_, *specs): + """Register a function as a compiler for a + given :class:`.ClauseElement` type.""" + + def decorate(fn): + # get an existing @compiles handler + existing = class_.__dict__.get('_compiler_dispatcher', None) + + # get the original handler. All ClauseElement classes have one + # of these, but some TypeEngine classes will not. + existing_dispatch = getattr(class_, '_compiler_dispatch', None) + + if not existing: + existing = _dispatcher() + + if existing_dispatch: + def _wrap_existing_dispatch(element, compiler, **kw): + try: + return existing_dispatch(element, compiler, **kw) + except exc.UnsupportedCompilationError: + raise exc.CompileError( + "%s construct has no default " + "compilation handler." % type(element)) + existing.specs['default'] = _wrap_existing_dispatch + + # TODO: why is the lambda needed ? + setattr(class_, '_compiler_dispatch', + lambda *arg, **kw: existing(*arg, **kw)) + setattr(class_, '_compiler_dispatcher', existing) + + if specs: + for s in specs: + existing.specs[s] = fn + + else: + existing.specs['default'] = fn + return fn + return decorate + + +def deregister(class_): + """Remove all custom compilers associated with a given + :class:`.ClauseElement` type.""" + + if hasattr(class_, '_compiler_dispatcher'): + # regenerate default _compiler_dispatch + visitors._generate_dispatch(class_) + # remove custom directive + del class_._compiler_dispatcher + + +class _dispatcher(object): + def __init__(self): + self.specs = {} + + def __call__(self, element, compiler, **kw): + # TODO: yes, this could also switch off of DBAPI in use. + fn = self.specs.get(compiler.dialect.name, None) + if not fn: + try: + fn = self.specs['default'] + except KeyError: + raise exc.CompileError( + "%s construct has no default " + "compilation handler." % type(element)) + + return fn(element, compiler, **kw) diff --git a/venv/Lib/site-packages/sqlalchemy/ext/declarative/__init__.py b/venv/Lib/site-packages/sqlalchemy/ext/declarative/__init__.py new file mode 100644 index 0000000..cb81f51 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/declarative/__init__.py @@ -0,0 +1,18 @@ +# ext/declarative/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from .api import declarative_base, synonym_for, comparable_using, \ + instrument_declarative, ConcreteBase, AbstractConcreteBase, \ + DeclarativeMeta, DeferredReflection, has_inherited_table,\ + declared_attr, as_declarative + + +__all__ = ['declarative_base', 'synonym_for', 'has_inherited_table', + 'comparable_using', 'instrument_declarative', 'declared_attr', + 'as_declarative', + 'ConcreteBase', 'AbstractConcreteBase', 'DeclarativeMeta', + 'DeferredReflection'] diff --git a/venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7b706799436f60a70d81a929d6d30640a9cba6a GIT binary patch literal 611 zcmZvYyKdVs6oz%NC0VkPAT9a|fd_YKr=lp@#7l>CNYI5KK+xi`O_&#zq>bp2`T%(u zu9>oD>J$<I8Wkvk^YM1he{lW}ZCl^ne<x2_k~}1%)r9lc5cVd3B`g(*Oi?N`l*t_B za)Ksuil(wag)C7iD^$rDn#mf~vO!Inu#Dww!ltZXC9Bws)vRIjZG+~ly-gM;k7*EI zH2#c1C#a#0AAzi?1>e{~X*Ec)(Z-*3TK3_4Er1*>S6ly%E9=Zbf^uY3>*rTmbp{+< zMv=GQ)**{_?w{Aw)oZhBaMYi#U<1YgyMc{>&T*~$n;o@;s~s5bfDspa`}U<(xKrnh zKM5(L<|f#em^`A6m_$q?qK{@CQACsxRm3dd$D;K)?fIzuwemmtFXDeCeMtxvf{@>d zIgOeTf6Ub<{L_N5c(||^173mMJKf_;t@q1os^}J?cgx$sI*?0A!)EXi)Da85#x@MS y>}j_Tc^2DWf{Jbja=>us@MvUAf=ApD^1dH@^;v3m5b!0AW}^ikiy|%3EW|SppRIHN literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/api.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/api.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..41a8b7e1ddfff94401feb9b0a6c126231a534d0b GIT binary patch literal 25388 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ˆ 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/base.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/base.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53b8d802d35948cc1ca97ed309401d477700e773 GIT binary patch literal 17923 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/clsregistry.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/clsregistry.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dabcef91bd5058cfcedd5b4b0421c81816a8ec10 GIT binary patch literal 10149 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/ext/declarative/api.py b/venv/Lib/site-packages/sqlalchemy/ext/declarative/api.py new file mode 100644 index 0000000..b08d3ce --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/declarative/api.py @@ -0,0 +1,731 @@ +# ext/declarative/api.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +"""Public API functions and helpers for declarative.""" + + +from ...schema import Table, MetaData, Column +from ...orm import synonym as _orm_synonym, \ + comparable_property,\ + interfaces, properties, attributes +from ...orm.util import polymorphic_union +from ...orm.base import _mapper_or_none +from ...util import OrderedDict, hybridmethod, hybridproperty +from ... import util +from ... import exc +import weakref +import re + +from .base import _as_declarative, \ + _declarative_constructor,\ + _DeferredMapperConfig, _add_attribute +from .clsregistry import _class_resolver + + +def instrument_declarative(cls, registry, metadata): + """Given a class, configure the class declaratively, + using the given registry, which can be any dictionary, and + MetaData object. + + """ + if '_decl_class_registry' in cls.__dict__: + raise exc.InvalidRequestError( + "Class %r already has been " + "instrumented declaratively" % cls) + cls._decl_class_registry = registry + cls.metadata = metadata + _as_declarative(cls, cls.__name__, cls.__dict__) + + +def has_inherited_table(cls): + """Given a class, return True if any of the classes it inherits from has a + mapped table, otherwise return False. + + This is used in declarative mixins to build attributes that behave + differently for the base class vs. a subclass in an inheritance + hierarchy. + + .. seealso:: + + :ref:`decl_mixin_inheritance` + + """ + for class_ in cls.__mro__[1:]: + if getattr(class_, '__table__', None) is not None: + return True + return False + + +class DeclarativeMeta(type): + def __init__(cls, classname, bases, dict_): + if '_decl_class_registry' not in cls.__dict__: + _as_declarative(cls, classname, cls.__dict__) + type.__init__(cls, classname, bases, dict_) + + def __setattr__(cls, key, value): + _add_attribute(cls, key, value) + + +def synonym_for(name, map_column=False): + """Decorator that produces an :func:`.orm.synonym` attribute in conjunction + with a Python descriptor. + + The function being decorated is passed to :func:`.orm.synonym` as the + :paramref:`.orm.synonym.descriptor` parameter:: + + class MyClass(Base): + __tablename__ = 'my_table' + + id = Column(Integer, primary_key=True) + _job_status = Column("job_status", String(50)) + + @synonym_for("job_status") + @property + def job_status(self): + return "Status: %s" % self._job_status + + The :ref:`hybrid properties <mapper_hybrids>` feature of SQLAlchemy + is typically preferred instead of synonyms, which is a more legacy + feature. + + .. seealso:: + + :ref:`synonyms` - Overview of synonyms + + :func:`.orm.synonym` - the mapper-level function + + :ref:`mapper_hybrids` - The Hybrid Attribute extension provides an + updated approach to augmenting attribute behavior more flexibly than + can be achieved with synonyms. + + """ + def decorate(fn): + return _orm_synonym(name, map_column=map_column, descriptor=fn) + return decorate + + +def comparable_using(comparator_factory): + """Decorator, allow a Python @property to be used in query criteria. + + This is a decorator front end to + :func:`~sqlalchemy.orm.comparable_property` that passes + through the comparator_factory and the function being decorated:: + + @comparable_using(MyComparatorType) + @property + def prop(self): + return 'special sauce' + + The regular ``comparable_property()`` is also usable directly in a + declarative setting and may be convenient for read/write properties:: + + prop = comparable_property(MyComparatorType) + + """ + def decorate(fn): + return comparable_property(comparator_factory, fn) + return decorate + + +class declared_attr(interfaces._MappedAttribute, property): + """Mark a class-level method as representing the definition of + a mapped property or special declarative member name. + + @declared_attr turns the attribute into a scalar-like + property that can be invoked from the uninstantiated class. + Declarative treats attributes specifically marked with + @declared_attr as returning a construct that is specific + to mapping or declarative table configuration. The name + of the attribute is that of what the non-dynamic version + of the attribute would be. + + @declared_attr is more often than not applicable to mixins, + to define relationships that are to be applied to different + implementors of the class:: + + class ProvidesUser(object): + "A mixin that adds a 'user' relationship to classes." + + @declared_attr + def user(self): + return relationship("User") + + It also can be applied to mapped classes, such as to provide + a "polymorphic" scheme for inheritance:: + + class Employee(Base): + id = Column(Integer, primary_key=True) + type = Column(String(50), nullable=False) + + @declared_attr + def __tablename__(cls): + return cls.__name__.lower() + + @declared_attr + def __mapper_args__(cls): + if cls.__name__ == 'Employee': + return { + "polymorphic_on":cls.type, + "polymorphic_identity":"Employee" + } + else: + return {"polymorphic_identity":cls.__name__} + + .. versionchanged:: 0.8 :class:`.declared_attr` can be used with + non-ORM or extension attributes, such as user-defined attributes + or :func:`.association_proxy` objects, which will be assigned + to the class at class construction time. + + + """ + + def __init__(self, fget, cascading=False): + super(declared_attr, self).__init__(fget) + self.__doc__ = fget.__doc__ + self._cascading = cascading + + def __get__(desc, self, cls): + reg = cls.__dict__.get('_sa_declared_attr_reg', None) + if reg is None: + if not re.match(r'^__.+__$', desc.fget.__name__) and \ + attributes.manager_of_class(cls) is None: + util.warn( + "Unmanaged access of declarative attribute %s from " + "non-mapped class %s" % + (desc.fget.__name__, cls.__name__)) + return desc.fget(cls) + elif desc in reg: + return reg[desc] + else: + reg[desc] = obj = desc.fget(cls) + return obj + + @hybridmethod + def _stateful(cls, **kw): + return _stateful_declared_attr(**kw) + + @hybridproperty + def cascading(cls): + """Mark a :class:`.declared_attr` as cascading. + + This is a special-use modifier which indicates that a column + or MapperProperty-based declared attribute should be configured + distinctly per mapped subclass, within a mapped-inheritance scenario. + + .. warning:: + + The :attr:`.declared_attr.cascading` modifier has several + limitations: + + * The flag **only** applies to the use of :class:`.declared_attr` + on declarative mixin classes and ``__abstract__`` classes; it + currently has no effect when used on a mapped class directly. + + * The flag **only** applies to normally-named attributes, e.g. + not any special underscore attributes such as ``__tablename__``. + On these attributes it has **no** effect. + + * The flag currently **does not allow further overrides** down + the class hierarchy; if a subclass tries to override the + attribute, a warning is emitted and the overridden attribute + is skipped. This is a limitation that it is hoped will be + resolved at some point. + + Below, both MyClass as well as MySubClass will have a distinct + ``id`` Column object established:: + + class HasIdMixin(object): + @declared_attr.cascading + def id(cls): + if has_inherited_table(cls): + return Column(ForeignKey('myclass.id'), primary_key=True) + else: + return Column(Integer, primary_key=True) + + class MyClass(HasIdMixin, Base): + __tablename__ = 'myclass' + # ... + + class MySubClass(MyClass): + "" + # ... + + The behavior of the above configuration is that ``MySubClass`` + will refer to both its own ``id`` column as well as that of + ``MyClass`` underneath the attribute named ``some_id``. + + .. seealso:: + + :ref:`declarative_inheritance` + + :ref:`mixin_inheritance_columns` + + + """ + return cls._stateful(cascading=True) + + +class _stateful_declared_attr(declared_attr): + def __init__(self, **kw): + self.kw = kw + + def _stateful(self, **kw): + new_kw = self.kw.copy() + new_kw.update(kw) + return _stateful_declared_attr(**new_kw) + + def __call__(self, fn): + return declared_attr(fn, **self.kw) + + +def declarative_base(bind=None, metadata=None, mapper=None, cls=object, + name='Base', constructor=_declarative_constructor, + class_registry=None, + metaclass=DeclarativeMeta): + r"""Construct a base class for declarative class definitions. + + The new base class will be given a metaclass that produces + appropriate :class:`~sqlalchemy.schema.Table` objects and makes + the appropriate :func:`~sqlalchemy.orm.mapper` calls based on the + information provided declaratively in the class and any subclasses + of the class. + + :param bind: An optional + :class:`~sqlalchemy.engine.Connectable`, will be assigned + the ``bind`` attribute on the :class:`~sqlalchemy.schema.MetaData` + instance. + + :param metadata: + An optional :class:`~sqlalchemy.schema.MetaData` instance. All + :class:`~sqlalchemy.schema.Table` objects implicitly declared by + subclasses of the base will share this MetaData. A MetaData instance + will be created if none is provided. The + :class:`~sqlalchemy.schema.MetaData` instance will be available via the + `metadata` attribute of the generated declarative base class. + + :param mapper: + An optional callable, defaults to :func:`~sqlalchemy.orm.mapper`. Will + be used to map subclasses to their Tables. + + :param cls: + Defaults to :class:`object`. A type to use as the base for the generated + declarative base class. May be a class or tuple of classes. + + :param name: + Defaults to ``Base``. The display name for the generated + class. Customizing this is not required, but can improve clarity in + tracebacks and debugging. + + :param constructor: + Defaults to + :func:`~sqlalchemy.ext.declarative.base._declarative_constructor`, an + __init__ implementation that assigns \**kwargs for declared + fields and relationships to an instance. If ``None`` is supplied, + no __init__ will be provided and construction will fall back to + cls.__init__ by way of the normal Python semantics. + + :param class_registry: optional dictionary that will serve as the + registry of class names-> mapped classes when string names + are used to identify classes inside of :func:`.relationship` + and others. Allows two or more declarative base classes + to share the same registry of class names for simplified + inter-base relationships. + + :param metaclass: + Defaults to :class:`.DeclarativeMeta`. A metaclass or __metaclass__ + compatible callable to use as the meta type of the generated + declarative base class. + + .. versionchanged:: 1.1 if :paramref:`.declarative_base.cls` is a single class (rather + than a tuple), the constructed base class will inherit its docstring. + + .. seealso:: + + :func:`.as_declarative` + + """ + lcl_metadata = metadata or MetaData() + if bind: + lcl_metadata.bind = bind + + if class_registry is None: + class_registry = weakref.WeakValueDictionary() + + bases = not isinstance(cls, tuple) and (cls,) or cls + class_dict = dict(_decl_class_registry=class_registry, + metadata=lcl_metadata) + + if isinstance(cls, type): + class_dict['__doc__'] = cls.__doc__ + + if constructor: + class_dict['__init__'] = constructor + if mapper: + class_dict['__mapper_cls__'] = mapper + + return metaclass(name, bases, class_dict) + + +def as_declarative(**kw): + """ + Class decorator for :func:`.declarative_base`. + + Provides a syntactical shortcut to the ``cls`` argument + sent to :func:`.declarative_base`, allowing the base class + to be converted in-place to a "declarative" base:: + + from sqlalchemy.ext.declarative import as_declarative + + @as_declarative() + class Base(object): + @declared_attr + def __tablename__(cls): + return cls.__name__.lower() + id = Column(Integer, primary_key=True) + + class MyMappedClass(Base): + # ... + + All keyword arguments passed to :func:`.as_declarative` are passed + along to :func:`.declarative_base`. + + .. versionadded:: 0.8.3 + + .. seealso:: + + :func:`.declarative_base` + + """ + def decorate(cls): + kw['cls'] = cls + kw['name'] = cls.__name__ + return declarative_base(**kw) + + return decorate + + +class ConcreteBase(object): + """A helper class for 'concrete' declarative mappings. + + :class:`.ConcreteBase` will use the :func:`.polymorphic_union` + function automatically, against all tables mapped as a subclass + to this class. The function is called via the + ``__declare_last__()`` function, which is essentially + a hook for the :meth:`.after_configured` event. + + :class:`.ConcreteBase` produces a mapped + table for the class itself. Compare to :class:`.AbstractConcreteBase`, + which does not. + + Example:: + + from sqlalchemy.ext.declarative import ConcreteBase + + class Employee(ConcreteBase, Base): + __tablename__ = 'employee' + employee_id = Column(Integer, primary_key=True) + name = Column(String(50)) + __mapper_args__ = { + 'polymorphic_identity':'employee', + 'concrete':True} + + class Manager(Employee): + __tablename__ = 'manager' + employee_id = Column(Integer, primary_key=True) + name = Column(String(50)) + manager_data = Column(String(40)) + __mapper_args__ = { + 'polymorphic_identity':'manager', + 'concrete':True} + + .. seealso:: + + :class:`.AbstractConcreteBase` + + :ref:`concrete_inheritance` + + :ref:`inheritance_concrete_helpers` + + + """ + + @classmethod + def _create_polymorphic_union(cls, mappers): + return polymorphic_union(OrderedDict( + (mp.polymorphic_identity, mp.local_table) + for mp in mappers + ), 'type', 'pjoin') + + @classmethod + def __declare_first__(cls): + m = cls.__mapper__ + if m.with_polymorphic: + return + + mappers = list(m.self_and_descendants) + pjoin = cls._create_polymorphic_union(mappers) + m._set_with_polymorphic(("*", pjoin)) + m._set_polymorphic_on(pjoin.c.type) + + +class AbstractConcreteBase(ConcreteBase): + """A helper class for 'concrete' declarative mappings. + + :class:`.AbstractConcreteBase` will use the :func:`.polymorphic_union` + function automatically, against all tables mapped as a subclass + to this class. The function is called via the + ``__declare_last__()`` function, which is essentially + a hook for the :meth:`.after_configured` event. + + :class:`.AbstractConcreteBase` does produce a mapped class + for the base class, however it is not persisted to any table; it + is instead mapped directly to the "polymorphic" selectable directly + and is only used for selecting. Compare to :class:`.ConcreteBase`, + which does create a persisted table for the base class. + + Example:: + + from sqlalchemy.ext.declarative import AbstractConcreteBase + + class Employee(AbstractConcreteBase, Base): + pass + + class Manager(Employee): + __tablename__ = 'manager' + employee_id = Column(Integer, primary_key=True) + name = Column(String(50)) + manager_data = Column(String(40)) + + __mapper_args__ = { + 'polymorphic_identity':'manager', + 'concrete':True} + + The abstract base class is handled by declarative in a special way; + at class configuration time, it behaves like a declarative mixin + or an ``__abstract__`` base class. Once classes are configured + and mappings are produced, it then gets mapped itself, but + after all of its decscendants. This is a very unique system of mapping + not found in any other SQLAlchemy system. + + Using this approach, we can specify columns and properties + that will take place on mapped subclasses, in the way that + we normally do as in :ref:`declarative_mixins`:: + + class Company(Base): + __tablename__ = 'company' + id = Column(Integer, primary_key=True) + + class Employee(AbstractConcreteBase, Base): + employee_id = Column(Integer, primary_key=True) + + @declared_attr + def company_id(cls): + return Column(ForeignKey('company.id')) + + @declared_attr + def company(cls): + return relationship("Company") + + class Manager(Employee): + __tablename__ = 'manager' + + name = Column(String(50)) + manager_data = Column(String(40)) + + __mapper_args__ = { + 'polymorphic_identity':'manager', + 'concrete':True} + + When we make use of our mappings however, both ``Manager`` and + ``Employee`` will have an independently usable ``.company`` attribute:: + + session.query(Employee).filter(Employee.company.has(id=5)) + + .. versionchanged:: 1.0.0 - The mechanics of :class:`.AbstractConcreteBase` + have been reworked to support relationships established directly + on the abstract base, without any special configurational steps. + + .. seealso:: + + :class:`.ConcreteBase` + + :ref:`concrete_inheritance` + + :ref:`inheritance_concrete_helpers` + + """ + + __no_table__ = True + + @classmethod + def __declare_first__(cls): + cls._sa_decl_prepare_nocascade() + + @classmethod + def _sa_decl_prepare_nocascade(cls): + if getattr(cls, '__mapper__', None): + return + + to_map = _DeferredMapperConfig.config_for_cls(cls) + + # can't rely on 'self_and_descendants' here + # since technically an immediate subclass + # might not be mapped, but a subclass + # may be. + mappers = [] + stack = list(cls.__subclasses__()) + while stack: + klass = stack.pop() + stack.extend(klass.__subclasses__()) + mn = _mapper_or_none(klass) + if mn is not None: + mappers.append(mn) + pjoin = cls._create_polymorphic_union(mappers) + + # For columns that were declared on the class, these + # are normally ignored with the "__no_table__" mapping, + # unless they have a different attribute key vs. col name + # and are in the properties argument. + # In that case, ensure we update the properties entry + # to the correct column from the pjoin target table. + declared_cols = set(to_map.declared_columns) + for k, v in list(to_map.properties.items()): + if v in declared_cols: + to_map.properties[k] = pjoin.c[v.key] + + to_map.local_table = pjoin + + m_args = to_map.mapper_args_fn or dict + + def mapper_args(): + args = m_args() + args['polymorphic_on'] = pjoin.c.type + return args + to_map.mapper_args_fn = mapper_args + + m = to_map.map() + + for scls in cls.__subclasses__(): + sm = _mapper_or_none(scls) + if sm and sm.concrete and cls in scls.__bases__: + sm._set_concrete_base(m) + + +class DeferredReflection(object): + """A helper class for construction of mappings based on + a deferred reflection step. + + Normally, declarative can be used with reflection by + setting a :class:`.Table` object using autoload=True + as the ``__table__`` attribute on a declarative class. + The caveat is that the :class:`.Table` must be fully + reflected, or at the very least have a primary key column, + at the point at which a normal declarative mapping is + constructed, meaning the :class:`.Engine` must be available + at class declaration time. + + The :class:`.DeferredReflection` mixin moves the construction + of mappers to be at a later point, after a specific + method is called which first reflects all :class:`.Table` + objects created so far. Classes can define it as such:: + + from sqlalchemy.ext.declarative import declarative_base + from sqlalchemy.ext.declarative import DeferredReflection + Base = declarative_base() + + class MyClass(DeferredReflection, Base): + __tablename__ = 'mytable' + + Above, ``MyClass`` is not yet mapped. After a series of + classes have been defined in the above fashion, all tables + can be reflected and mappings created using + :meth:`.prepare`:: + + engine = create_engine("someengine://...") + DeferredReflection.prepare(engine) + + The :class:`.DeferredReflection` mixin can be applied to individual + classes, used as the base for the declarative base itself, + or used in a custom abstract class. Using an abstract base + allows that only a subset of classes to be prepared for a + particular prepare step, which is necessary for applications + that use more than one engine. For example, if an application + has two engines, you might use two bases, and prepare each + separately, e.g.:: + + class ReflectedOne(DeferredReflection, Base): + __abstract__ = True + + class ReflectedTwo(DeferredReflection, Base): + __abstract__ = True + + class MyClass(ReflectedOne): + __tablename__ = 'mytable' + + class MyOtherClass(ReflectedOne): + __tablename__ = 'myothertable' + + class YetAnotherClass(ReflectedTwo): + __tablename__ = 'yetanothertable' + + # ... etc. + + Above, the class hierarchies for ``ReflectedOne`` and + ``ReflectedTwo`` can be configured separately:: + + ReflectedOne.prepare(engine_one) + ReflectedTwo.prepare(engine_two) + + .. versionadded:: 0.8 + + """ + @classmethod + def prepare(cls, engine): + """Reflect all :class:`.Table` objects for all current + :class:`.DeferredReflection` subclasses""" + + to_map = _DeferredMapperConfig.classes_for_base(cls) + for thingy in to_map: + cls._sa_decl_prepare(thingy.local_table, engine) + thingy.map() + mapper = thingy.cls.__mapper__ + metadata = mapper.class_.metadata + for rel in mapper._props.values(): + if isinstance(rel, properties.RelationshipProperty) and \ + rel.secondary is not None: + if isinstance(rel.secondary, Table): + cls._reflect_table(rel.secondary, engine) + elif isinstance(rel.secondary, _class_resolver): + rel.secondary._resolvers += ( + cls._sa_deferred_table_resolver(engine, metadata), + ) + + @classmethod + def _sa_deferred_table_resolver(cls, engine, metadata): + def _resolve(key): + t1 = Table(key, metadata) + cls._reflect_table(t1, engine) + return t1 + return _resolve + + @classmethod + def _sa_decl_prepare(cls, local_table, engine): + # autoload Table, which is already + # present in the metadata. This + # will fill in db-loaded columns + # into the existing Table object. + if local_table is not None: + cls._reflect_table(local_table, engine) + + @classmethod + def _reflect_table(cls, table, engine): + Table(table.name, + table.metadata, + extend_existing=True, + autoload_replace=False, + autoload=True, + autoload_with=engine, + schema=table.schema) diff --git a/venv/Lib/site-packages/sqlalchemy/ext/declarative/base.py b/venv/Lib/site-packages/sqlalchemy/ext/declarative/base.py new file mode 100644 index 0000000..b580a79 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/declarative/base.py @@ -0,0 +1,770 @@ +# ext/declarative/base.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +"""Internal implementation for declarative.""" + +from ...schema import Table, Column +from ...orm import mapper, class_mapper, synonym +from ...orm.interfaces import MapperProperty +from ...orm.properties import ColumnProperty, CompositeProperty +from ...orm.attributes import QueryableAttribute +from ...orm.base import _is_mapped_class, InspectionAttr +from ... import util, exc +from ...util import topological +from ...sql import expression +from ... import event +from . import clsregistry +import collections +import weakref +from sqlalchemy.orm import instrumentation + +declared_attr = declarative_props = None + + +def _declared_mapping_info(cls): + # deferred mapping + if _DeferredMapperConfig.has_cls(cls): + return _DeferredMapperConfig.config_for_cls(cls) + # regular mapping + elif _is_mapped_class(cls): + return class_mapper(cls, configure=False) + else: + return None + + +def _resolve_for_abstract_or_classical(cls): + if cls is object: + return None + + if _get_immediate_cls_attr(cls, '__abstract__', strict=True): + for sup in cls.__bases__: + sup = _resolve_for_abstract_or_classical(sup) + if sup is not None: + return sup + else: + return None + else: + classical = _dive_for_classically_mapped_class(cls) + if classical is not None: + return classical + else: + return cls + + +def _dive_for_classically_mapped_class(cls): + # support issue #4321 + + # if we are within a base hierarchy, don't + # search at all for classical mappings + if hasattr(cls, '_decl_class_registry'): + return None + + manager = instrumentation.manager_of_class(cls) + if manager is not None: + return cls + else: + for sup in cls.__bases__: + mapper = _dive_for_classically_mapped_class(sup) + if mapper is not None: + return sup + else: + return None + + +def _get_immediate_cls_attr(cls, attrname, strict=False): + """return an attribute of the class that is either present directly + on the class, e.g. not on a superclass, or is from a superclass but + this superclass is a non-mapped mixin, that is, not a descendant of + the declarative base and is also not classically mapped. + + This is used to detect attributes that indicate something about + a mapped class independently from any mapped classes that it may + inherit from. + + """ + if not issubclass(cls, object): + return None + + for base in cls.__mro__: + _is_declarative_inherits = hasattr(base, '_decl_class_registry') + _is_classicial_inherits = not _is_declarative_inherits and \ + _dive_for_classically_mapped_class(base) is not None + + if attrname in base.__dict__ and ( + base is cls or + ((base in cls.__bases__ if strict else True) + and not _is_declarative_inherits + and not _is_classicial_inherits) + ): + return getattr(base, attrname) + else: + return None + + +def _as_declarative(cls, classname, dict_): + global declared_attr, declarative_props + if declared_attr is None: + from .api import declared_attr + declarative_props = (declared_attr, util.classproperty) + + if _get_immediate_cls_attr(cls, '__abstract__', strict=True): + return + + _MapperConfig.setup_mapping(cls, classname, dict_) + + +def _check_declared_props_nocascade(obj, name, cls): + + if isinstance(obj, declarative_props): + if getattr(obj, '_cascading', False): + util.warn( + "@declared_attr.cascading is not supported on the %s " + "attribute on class %s. This attribute invokes for " + "subclasses in any case." % (name, cls)) + return True + else: + return False + + +class _MapperConfig(object): + + @classmethod + def setup_mapping(cls, cls_, classname, dict_): + defer_map = _get_immediate_cls_attr( + cls_, '_sa_decl_prepare_nocascade', strict=True) or \ + hasattr(cls_, '_sa_decl_prepare') + + if defer_map: + cfg_cls = _DeferredMapperConfig + else: + cfg_cls = _MapperConfig + cfg_cls(cls_, classname, dict_) + + def __init__(self, cls_, classname, dict_): + + self.cls = cls_ + + # dict_ will be a dictproxy, which we can't write to, and we need to! + self.dict_ = dict(dict_) + self.classname = classname + self.mapped_table = None + self.properties = util.OrderedDict() + self.declared_columns = set() + self.column_copies = {} + self._setup_declared_events() + + # temporary registry. While early 1.0 versions + # set up the ClassManager here, by API contract + # we can't do that until there's a mapper. + self.cls._sa_declared_attr_reg = {} + + self._scan_attributes() + + clsregistry.add_class(self.classname, self.cls) + + self._extract_mappable_attributes() + + self._extract_declared_columns() + + self._setup_table() + + self._setup_inheritance() + + self._early_mapping() + + def _early_mapping(self): + self.map() + + def _setup_declared_events(self): + if _get_immediate_cls_attr(self.cls, '__declare_last__'): + @event.listens_for(mapper, "after_configured") + def after_configured(): + self.cls.__declare_last__() + + if _get_immediate_cls_attr(self.cls, '__declare_first__'): + @event.listens_for(mapper, "before_configured") + def before_configured(): + self.cls.__declare_first__() + + def _scan_attributes(self): + cls = self.cls + dict_ = self.dict_ + column_copies = self.column_copies + mapper_args_fn = None + table_args = inherited_table_args = None + tablename = None + + for base in cls.__mro__: + class_mapped = base is not cls and \ + _declared_mapping_info(base) is not None and \ + not _get_immediate_cls_attr( + base, '_sa_decl_prepare_nocascade', strict=True) + + if not class_mapped and base is not cls: + self._produce_column_copies(base) + + for name, obj in vars(base).items(): + if name == '__mapper_args__': + check_decl = \ + _check_declared_props_nocascade(obj, name, cls) + if not mapper_args_fn and ( + not class_mapped or + check_decl + ): + # don't even invoke __mapper_args__ until + # after we've determined everything about the + # mapped table. + # make a copy of it so a class-level dictionary + # is not overwritten when we update column-based + # arguments. + mapper_args_fn = lambda: dict(cls.__mapper_args__) # noqa + elif name == '__tablename__': + check_decl = \ + _check_declared_props_nocascade(obj, name, cls) + if not tablename and ( + not class_mapped or + check_decl + ): + tablename = cls.__tablename__ + elif name == '__table_args__': + check_decl = \ + _check_declared_props_nocascade(obj, name, cls) + if not table_args and ( + not class_mapped or + check_decl + ): + table_args = cls.__table_args__ + if not isinstance( + table_args, (tuple, dict, type(None))): + raise exc.ArgumentError( + "__table_args__ value must be a tuple, " + "dict, or None") + if base is not cls: + inherited_table_args = True + elif class_mapped: + if isinstance(obj, declarative_props): + util.warn("Regular (i.e. not __special__) " + "attribute '%s.%s' uses @declared_attr, " + "but owning class %s is mapped - " + "not applying to subclass %s." + % (base.__name__, name, base, cls)) + continue + elif base is not cls: + # we're a mixin, abstract base, or something that is + # acting like that for now. + if isinstance(obj, Column): + # already copied columns to the mapped class. + continue + elif isinstance(obj, MapperProperty): + raise exc.InvalidRequestError( + "Mapper properties (i.e. deferred," + "column_property(), relationship(), etc.) must " + "be declared as @declared_attr callables " + "on declarative mixin classes.") + elif isinstance(obj, declarative_props): + oldclassprop = isinstance(obj, util.classproperty) + if not oldclassprop and obj._cascading: + if name in dict_: + # unfortunately, while we can use the user- + # defined attribute here to allow a clean + # override, if there's another + # subclass below then it still tries to use + # this. not sure if there is enough information + # here to add this as a feature later on. + util.warn( + "Attribute '%s' on class %s cannot be " + "processed due to " + "@declared_attr.cascading; " + "skipping" % (name, cls)) + dict_[name] = column_copies[obj] = \ + ret = obj.__get__(obj, cls) + setattr(cls, name, ret) + else: + if oldclassprop: + util.warn_deprecated( + "Use of sqlalchemy.util.classproperty on " + "declarative classes is deprecated.") + # access attribute using normal class access + ret = getattr(cls, name) + + # correct for proxies created from hybrid_property + # or similar. note there is no known case that + # produces nested proxies, so we are only + # looking one level deep right now. + if isinstance(ret, InspectionAttr) and \ + ret._is_internal_proxy and not isinstance( + ret.original_property, MapperProperty): + ret = ret.descriptor + + dict_[name] = column_copies[obj] = ret + if isinstance(ret, (Column, MapperProperty)) and \ + ret.doc is None: + ret.doc = obj.__doc__ + # here, the attribute is some other kind of property that + # we assume is not part of the declarative mapping. + # however, check for some more common mistakes + else: + self._warn_for_decl_attributes(base, name, obj) + + if inherited_table_args and not tablename: + table_args = None + + self.table_args = table_args + self.tablename = tablename + self.mapper_args_fn = mapper_args_fn + + def _warn_for_decl_attributes(self, cls, key, c): + if isinstance(c, expression.ColumnClause): + util.warn( + "Attribute '%s' on class %s appears to be a non-schema " + "'sqlalchemy.sql.column()' " + "object; this won't be part of the declarative mapping" % + (key, cls)) + + def _produce_column_copies(self, base): + cls = self.cls + dict_ = self.dict_ + column_copies = self.column_copies + # copy mixin columns to the mapped class + for name, obj in vars(base).items(): + if isinstance(obj, Column): + if getattr(cls, name) is not obj: + # if column has been overridden + # (like by the InstrumentedAttribute of the + # superclass), skip + continue + elif obj.foreign_keys: + raise exc.InvalidRequestError( + "Columns with foreign keys to other columns " + "must be declared as @declared_attr callables " + "on declarative mixin classes. ") + elif name not in dict_ and not ( + '__table__' in dict_ and + (obj.name or name) in dict_['__table__'].c + ): + column_copies[obj] = copy_ = obj.copy() + copy_._creation_order = obj._creation_order + setattr(cls, name, copy_) + dict_[name] = copy_ + + def _extract_mappable_attributes(self): + cls = self.cls + dict_ = self.dict_ + + our_stuff = self.properties + + late_mapped = _get_immediate_cls_attr( + cls, '_sa_decl_prepare_nocascade', strict=True) + + for k in list(dict_): + + if k in ('__table__', '__tablename__', '__mapper_args__'): + continue + + value = dict_[k] + if isinstance(value, declarative_props): + if isinstance(value, declared_attr) and value._cascading: + util.warn( + "Use of @declared_attr.cascading only applies to " + "Declarative 'mixin' and 'abstract' classes. " + "Currently, this flag is ignored on mapped class " + "%s" % self.cls) + + value = getattr(cls, k) + + elif isinstance(value, QueryableAttribute) and \ + value.class_ is not cls and \ + value.key != k: + # detect a QueryableAttribute that's already mapped being + # assigned elsewhere in userland, turn into a synonym() + value = synonym(value.key) + setattr(cls, k, value) + + if (isinstance(value, tuple) and len(value) == 1 and + isinstance(value[0], (Column, MapperProperty))): + util.warn("Ignoring declarative-like tuple value of attribute " + "'%s': possibly a copy-and-paste error with a comma " + "accidentally placed at the end of the line?" % k) + continue + elif not isinstance(value, (Column, MapperProperty)): + # using @declared_attr for some object that + # isn't Column/MapperProperty; remove from the dict_ + # and place the evaluated value onto the class. + if not k.startswith('__'): + dict_.pop(k) + self._warn_for_decl_attributes(cls, k, value) + if not late_mapped: + setattr(cls, k, value) + continue + # we expect to see the name 'metadata' in some valid cases; + # however at this point we see it's assigned to something trying + # to be mapped, so raise for that. + elif k == 'metadata': + raise exc.InvalidRequestError( + "Attribute name 'metadata' is reserved " + "for the MetaData instance when using a " + "declarative base class." + ) + prop = clsregistry._deferred_relationship(cls, value) + our_stuff[k] = prop + + def _extract_declared_columns(self): + our_stuff = self.properties + + # set up attributes in the order they were created + our_stuff.sort(key=lambda key: our_stuff[key]._creation_order) + + # extract columns from the class dict + declared_columns = self.declared_columns + name_to_prop_key = collections.defaultdict(set) + for key, c in list(our_stuff.items()): + if isinstance(c, (ColumnProperty, CompositeProperty)): + for col in c.columns: + if isinstance(col, Column) and \ + col.table is None: + _undefer_column_name(key, col) + if not isinstance(c, CompositeProperty): + name_to_prop_key[col.name].add(key) + declared_columns.add(col) + elif isinstance(c, Column): + _undefer_column_name(key, c) + name_to_prop_key[c.name].add(key) + declared_columns.add(c) + # if the column is the same name as the key, + # remove it from the explicit properties dict. + # the normal rules for assigning column-based properties + # will take over, including precedence of columns + # in multi-column ColumnProperties. + if key == c.key: + del our_stuff[key] + + for name, keys in name_to_prop_key.items(): + if len(keys) > 1: + util.warn( + "On class %r, Column object %r named " + "directly multiple times, " + "only one will be used: %s. " + "Consider using orm.synonym instead" % + (self.classname, name, (", ".join(sorted(keys)))) + ) + + def _setup_table(self): + cls = self.cls + tablename = self.tablename + table_args = self.table_args + dict_ = self.dict_ + declared_columns = self.declared_columns + + declared_columns = self.declared_columns = sorted( + declared_columns, key=lambda c: c._creation_order) + table = None + + if hasattr(cls, '__table_cls__'): + table_cls = util.unbound_method_to_callable(cls.__table_cls__) + else: + table_cls = Table + + if '__table__' not in dict_: + if tablename is not None: + + args, table_kw = (), {} + if table_args: + if isinstance(table_args, dict): + table_kw = table_args + elif isinstance(table_args, tuple): + if isinstance(table_args[-1], dict): + args, table_kw = table_args[0:-1], table_args[-1] + else: + args = table_args + + autoload = dict_.get('__autoload__') + if autoload: + table_kw['autoload'] = True + + cls.__table__ = table = table_cls( + tablename, cls.metadata, + *(tuple(declared_columns) + tuple(args)), + **table_kw) + else: + table = cls.__table__ + if declared_columns: + for c in declared_columns: + if not table.c.contains_column(c): + raise exc.ArgumentError( + "Can't add additional column %r when " + "specifying __table__" % c.key + ) + self.local_table = table + + def _setup_inheritance(self): + table = self.local_table + cls = self.cls + table_args = self.table_args + declared_columns = self.declared_columns + + # since we search for classical mappings now, search for + # multiple mapped bases as well and raise an error. + inherits = [] + for c in cls.__bases__: + c = _resolve_for_abstract_or_classical(c) + if c is None: + continue + if _declared_mapping_info(c) is not None and \ + not _get_immediate_cls_attr( + c, '_sa_decl_prepare_nocascade', strict=True): + inherits.append(c) + + if inherits: + if len(inherits) > 1: + raise exc.InvalidRequestError( + "Class %s has multiple mapped bases: %r" % (cls, inherits)) + self.inherits = inherits[0] + else: + self.inherits = None + + if table is None and self.inherits is None and \ + not _get_immediate_cls_attr(cls, '__no_table__'): + + raise exc.InvalidRequestError( + "Class %r does not have a __table__ or __tablename__ " + "specified and does not inherit from an existing " + "table-mapped class." % cls + ) + elif self.inherits: + inherited_mapper = _declared_mapping_info(self.inherits) + inherited_table = inherited_mapper.local_table + inherited_mapped_table = inherited_mapper.mapped_table + + if table is None: + # single table inheritance. + # ensure no table args + if table_args: + raise exc.ArgumentError( + "Can't place __table_args__ on an inherited class " + "with no table." + ) + # add any columns declared here to the inherited table. + for c in declared_columns: + if c.name in inherited_table.c: + if inherited_table.c[c.name] is c: + continue + raise exc.ArgumentError( + "Column '%s' on class %s conflicts with " + "existing column '%s'" % + (c, cls, inherited_table.c[c.name]) + ) + if c.primary_key: + raise exc.ArgumentError( + "Can't place primary key columns on an inherited " + "class with no table." + ) + inherited_table.append_column(c) + if inherited_mapped_table is not None and \ + inherited_mapped_table is not inherited_table: + inherited_mapped_table._refresh_for_new_column(c) + + def _prepare_mapper_arguments(self): + properties = self.properties + if self.mapper_args_fn: + mapper_args = self.mapper_args_fn() + else: + mapper_args = {} + + # make sure that column copies are used rather + # than the original columns from any mixins + for k in ('version_id_col', 'polymorphic_on',): + if k in mapper_args: + v = mapper_args[k] + mapper_args[k] = self.column_copies.get(v, v) + + assert 'inherits' not in mapper_args, \ + "Can't specify 'inherits' explicitly with declarative mappings" + + if self.inherits: + mapper_args['inherits'] = self.inherits + + if self.inherits and not mapper_args.get('concrete', False): + # single or joined inheritance + # exclude any cols on the inherited table which are + # not mapped on the parent class, to avoid + # mapping columns specific to sibling/nephew classes + inherited_mapper = _declared_mapping_info(self.inherits) + inherited_table = inherited_mapper.local_table + + if 'exclude_properties' not in mapper_args: + mapper_args['exclude_properties'] = exclude_properties = \ + set( + [c.key for c in inherited_table.c + if c not in inherited_mapper._columntoproperty] + ).union( + inherited_mapper.exclude_properties or () + ) + exclude_properties.difference_update( + [c.key for c in self.declared_columns]) + + # look through columns in the current mapper that + # are keyed to a propname different than the colname + # (if names were the same, we'd have popped it out above, + # in which case the mapper makes this combination). + # See if the superclass has a similar column property. + # If so, join them together. + for k, col in list(properties.items()): + if not isinstance(col, expression.ColumnElement): + continue + if k in inherited_mapper._props: + p = inherited_mapper._props[k] + if isinstance(p, ColumnProperty): + # note here we place the subclass column + # first. See [ticket:1892] for background. + properties[k] = [col] + p.columns + result_mapper_args = mapper_args.copy() + result_mapper_args['properties'] = properties + self.mapper_args = result_mapper_args + + def map(self): + self._prepare_mapper_arguments() + if hasattr(self.cls, '__mapper_cls__'): + mapper_cls = util.unbound_method_to_callable( + self.cls.__mapper_cls__) + else: + mapper_cls = mapper + + self.cls.__mapper__ = mp_ = mapper_cls( + self.cls, + self.local_table, + **self.mapper_args + ) + del self.cls._sa_declared_attr_reg + return mp_ + + +class _DeferredMapperConfig(_MapperConfig): + _configs = util.OrderedDict() + + def _early_mapping(self): + pass + + @property + def cls(self): + return self._cls() + + @cls.setter + def cls(self, class_): + self._cls = weakref.ref(class_, self._remove_config_cls) + self._configs[self._cls] = self + + @classmethod + def _remove_config_cls(cls, ref): + cls._configs.pop(ref, None) + + @classmethod + def has_cls(cls, class_): + # 2.6 fails on weakref if class_ is an old style class + return isinstance(class_, type) and \ + weakref.ref(class_) in cls._configs + + @classmethod + def config_for_cls(cls, class_): + return cls._configs[weakref.ref(class_)] + + @classmethod + def classes_for_base(cls, base_cls, sort=True): + classes_for_base = [ + m for m, cls_ in + [(m, m.cls) for m in cls._configs.values()] + if cls_ is not None and issubclass(cls_, base_cls) + ] + + if not sort: + return classes_for_base + + all_m_by_cls = dict( + (m.cls, m) + for m in classes_for_base + ) + + tuples = [] + for m_cls in all_m_by_cls: + tuples.extend( + (all_m_by_cls[base_cls], all_m_by_cls[m_cls]) + for base_cls in m_cls.__bases__ + if base_cls in all_m_by_cls + ) + return list( + topological.sort( + tuples, + classes_for_base + ) + ) + + def map(self): + self._configs.pop(self._cls, None) + return super(_DeferredMapperConfig, self).map() + + +def _add_attribute(cls, key, value): + """add an attribute to an existing declarative class. + + This runs through the logic to determine MapperProperty, + adds it to the Mapper, adds a column to the mapped Table, etc. + + """ + + if '__mapper__' in cls.__dict__: + if isinstance(value, Column): + _undefer_column_name(key, value) + cls.__table__.append_column(value) + cls.__mapper__.add_property(key, value) + elif isinstance(value, ColumnProperty): + for col in value.columns: + if isinstance(col, Column) and col.table is None: + _undefer_column_name(key, col) + cls.__table__.append_column(col) + cls.__mapper__.add_property(key, value) + elif isinstance(value, MapperProperty): + cls.__mapper__.add_property( + key, + clsregistry._deferred_relationship(cls, value) + ) + elif isinstance(value, QueryableAttribute) and value.key != key: + # detect a QueryableAttribute that's already mapped being + # assigned elsewhere in userland, turn into a synonym() + value = synonym(value.key) + cls.__mapper__.add_property( + key, + clsregistry._deferred_relationship(cls, value) + ) + else: + type.__setattr__(cls, key, value) + else: + type.__setattr__(cls, key, value) + + +def _declarative_constructor(self, **kwargs): + """A simple constructor that allows initialization from kwargs. + + Sets attributes on the constructed instance using the names and + values in ``kwargs``. + + Only keys that are present as + attributes of the instance's class are allowed. These could be, + for example, any mapped columns or relationships. + """ + cls_ = type(self) + for k in kwargs: + if not hasattr(cls_, k): + raise TypeError( + "%r is an invalid keyword argument for %s" % + (k, cls_.__name__)) + setattr(self, k, kwargs[k]) +_declarative_constructor.__name__ = '__init__' + + +def _undefer_column_name(key, column): + if column.key is None: + column.key = key + if column.name is None: + column.name = key diff --git a/venv/Lib/site-packages/sqlalchemy/ext/declarative/clsregistry.py b/venv/Lib/site-packages/sqlalchemy/ext/declarative/clsregistry.py new file mode 100644 index 0000000..e941b9e --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/declarative/clsregistry.py @@ -0,0 +1,328 @@ +# ext/declarative/clsregistry.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +"""Routines to handle the string class registry used by declarative. + +This system allows specification of classes and expressions used in +:func:`.relationship` using strings. + +""" +from ...orm.properties import ColumnProperty, RelationshipProperty, \ + SynonymProperty +from ...schema import _get_table_key +from ...orm import class_mapper, interfaces +from ... import util +from ... import inspection +from ... import exc +import weakref + +# strong references to registries which we place in +# the _decl_class_registry, which is usually weak referencing. +# the internal registries here link to classes with weakrefs and remove +# themselves when all references to contained classes are removed. +_registries = set() + + +def add_class(classname, cls): + """Add a class to the _decl_class_registry associated with the + given declarative class. + + """ + if classname in cls._decl_class_registry: + # class already exists. + existing = cls._decl_class_registry[classname] + if not isinstance(existing, _MultipleClassMarker): + existing = \ + cls._decl_class_registry[classname] = \ + _MultipleClassMarker([cls, existing]) + else: + cls._decl_class_registry[classname] = cls + + try: + root_module = cls._decl_class_registry['_sa_module_registry'] + except KeyError: + cls._decl_class_registry['_sa_module_registry'] = \ + root_module = _ModuleMarker('_sa_module_registry', None) + + tokens = cls.__module__.split(".") + + # build up a tree like this: + # modulename: myapp.snacks.nuts + # + # myapp->snack->nuts->(classes) + # snack->nuts->(classes) + # nuts->(classes) + # + # this allows partial token paths to be used. + while tokens: + token = tokens.pop(0) + module = root_module.get_module(token) + for token in tokens: + module = module.get_module(token) + module.add_class(classname, cls) + + +class _MultipleClassMarker(object): + """refers to multiple classes of the same name + within _decl_class_registry. + + """ + + __slots__ = 'on_remove', 'contents', '__weakref__' + + def __init__(self, classes, on_remove=None): + self.on_remove = on_remove + self.contents = set([ + weakref.ref(item, self._remove_item) for item in classes]) + _registries.add(self) + + def __iter__(self): + return (ref() for ref in self.contents) + + def attempt_get(self, path, key): + if len(self.contents) > 1: + raise exc.InvalidRequestError( + "Multiple classes found for path \"%s\" " + "in the registry of this declarative " + "base. Please use a fully module-qualified path." % + (".".join(path + [key])) + ) + else: + ref = list(self.contents)[0] + cls = ref() + if cls is None: + raise NameError(key) + return cls + + def _remove_item(self, ref): + self.contents.remove(ref) + if not self.contents: + _registries.discard(self) + if self.on_remove: + self.on_remove() + + def add_item(self, item): + # protect against class registration race condition against + # asynchronous garbage collection calling _remove_item, + # [ticket:3208] + modules = set([ + cls.__module__ for cls in + [ref() for ref in self.contents] if cls is not None]) + if item.__module__ in modules: + util.warn( + "This declarative base already contains a class with the " + "same class name and module name as %s.%s, and will " + "be replaced in the string-lookup table." % ( + item.__module__, + item.__name__ + ) + ) + self.contents.add(weakref.ref(item, self._remove_item)) + + +class _ModuleMarker(object): + """"refers to a module name within + _decl_class_registry. + + """ + + __slots__ = 'parent', 'name', 'contents', 'mod_ns', 'path', '__weakref__' + + def __init__(self, name, parent): + self.parent = parent + self.name = name + self.contents = {} + self.mod_ns = _ModNS(self) + if self.parent: + self.path = self.parent.path + [self.name] + else: + self.path = [] + _registries.add(self) + + def __contains__(self, name): + return name in self.contents + + def __getitem__(self, name): + return self.contents[name] + + def _remove_item(self, name): + self.contents.pop(name, None) + if not self.contents and self.parent is not None: + self.parent._remove_item(self.name) + _registries.discard(self) + + def resolve_attr(self, key): + return getattr(self.mod_ns, key) + + def get_module(self, name): + if name not in self.contents: + marker = _ModuleMarker(name, self) + self.contents[name] = marker + else: + marker = self.contents[name] + return marker + + def add_class(self, name, cls): + if name in self.contents: + existing = self.contents[name] + existing.add_item(cls) + else: + existing = self.contents[name] = \ + _MultipleClassMarker([cls], + on_remove=lambda: self._remove_item(name)) + + +class _ModNS(object): + __slots__ = '__parent', + + def __init__(self, parent): + self.__parent = parent + + def __getattr__(self, key): + try: + value = self.__parent.contents[key] + except KeyError: + pass + else: + if value is not None: + if isinstance(value, _ModuleMarker): + return value.mod_ns + else: + assert isinstance(value, _MultipleClassMarker) + return value.attempt_get(self.__parent.path, key) + raise AttributeError("Module %r has no mapped classes " + "registered under the name %r" % ( + self.__parent.name, key)) + + +class _GetColumns(object): + __slots__ = 'cls', + + def __init__(self, cls): + self.cls = cls + + def __getattr__(self, key): + mp = class_mapper(self.cls, configure=False) + if mp: + if key not in mp.all_orm_descriptors: + raise exc.InvalidRequestError( + "Class %r does not have a mapped column named %r" + % (self.cls, key)) + + desc = mp.all_orm_descriptors[key] + if desc.extension_type is interfaces.NOT_EXTENSION: + prop = desc.property + if isinstance(prop, SynonymProperty): + key = prop.name + elif not isinstance(prop, ColumnProperty): + raise exc.InvalidRequestError( + "Property %r is not an instance of" + " ColumnProperty (i.e. does not correspond" + " directly to a Column)." % key) + return getattr(self.cls, key) + +inspection._inspects(_GetColumns)( + lambda target: inspection.inspect(target.cls)) + + +class _GetTable(object): + __slots__ = 'key', 'metadata' + + def __init__(self, key, metadata): + self.key = key + self.metadata = metadata + + def __getattr__(self, key): + return self.metadata.tables[ + _get_table_key(key, self.key) + ] + + +def _determine_container(key, value): + if isinstance(value, _MultipleClassMarker): + value = value.attempt_get([], key) + return _GetColumns(value) + + +class _class_resolver(object): + def __init__(self, cls, prop, fallback, arg): + self.cls = cls + self.prop = prop + self.arg = self._declarative_arg = arg + self.fallback = fallback + self._dict = util.PopulateDict(self._access_cls) + self._resolvers = () + + def _access_cls(self, key): + cls = self.cls + if key in cls._decl_class_registry: + return _determine_container(key, cls._decl_class_registry[key]) + elif key in cls.metadata.tables: + return cls.metadata.tables[key] + elif key in cls.metadata._schemas: + return _GetTable(key, cls.metadata) + elif '_sa_module_registry' in cls._decl_class_registry and \ + key in cls._decl_class_registry['_sa_module_registry']: + registry = cls._decl_class_registry['_sa_module_registry'] + return registry.resolve_attr(key) + elif self._resolvers: + for resolv in self._resolvers: + value = resolv(key) + if value is not None: + return value + + return self.fallback[key] + + def __call__(self): + try: + x = eval(self.arg, globals(), self._dict) + + if isinstance(x, _GetColumns): + return x.cls + else: + return x + except NameError as n: + raise exc.InvalidRequestError( + "When initializing mapper %s, expression %r failed to " + "locate a name (%r). If this is a class name, consider " + "adding this relationship() to the %r class after " + "both dependent classes have been defined." % + (self.prop.parent, self.arg, n.args[0], self.cls) + ) + + +def _resolver(cls, prop): + import sqlalchemy + from sqlalchemy.orm import foreign, remote + + fallback = sqlalchemy.__dict__.copy() + fallback.update({'foreign': foreign, 'remote': remote}) + + def resolve_arg(arg): + return _class_resolver(cls, prop, fallback, arg) + return resolve_arg + + +def _deferred_relationship(cls, prop): + + if isinstance(prop, RelationshipProperty): + resolve_arg = _resolver(cls, prop) + + for attr in ('argument', 'order_by', 'primaryjoin', 'secondaryjoin', + 'secondary', '_user_defined_foreign_keys', 'remote_side'): + v = getattr(prop, attr) + if isinstance(v, util.string_types): + setattr(prop, attr, resolve_arg(v)) + + if prop.backref and isinstance(prop.backref, tuple): + key, kwargs = prop.backref + for attr in ('primaryjoin', 'secondaryjoin', 'secondary', + 'foreign_keys', 'remote_side', 'order_by'): + if attr in kwargs and isinstance(kwargs[attr], + util.string_types): + kwargs[attr] = resolve_arg(kwargs[attr]) + + return prop diff --git a/venv/Lib/site-packages/sqlalchemy/ext/horizontal_shard.py b/venv/Lib/site-packages/sqlalchemy/ext/horizontal_shard.py new file mode 100644 index 0000000..6ef4c56 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/horizontal_shard.py @@ -0,0 +1,196 @@ +# ext/horizontal_shard.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Horizontal sharding support. + +Defines a rudimental 'horizontal sharding' system which allows a Session to +distribute queries and persistence operations across multiple databases. + +For a usage example, see the :ref:`examples_sharding` example included in +the source distribution. + +""" + +from .. import inspect +from .. import util +from ..orm.session import Session +from ..orm.query import Query + +__all__ = ['ShardedSession', 'ShardedQuery'] + + +class ShardedQuery(Query): + def __init__(self, *args, **kwargs): + super(ShardedQuery, self).__init__(*args, **kwargs) + self.id_chooser = self.session.id_chooser + self.query_chooser = self.session.query_chooser + self._shard_id = None + + def set_shard(self, shard_id): + """return a new query, limited to a single shard ID. + + all subsequent operations with the returned query will + be against the single shard regardless of other state. + """ + + q = self._clone() + q._shard_id = shard_id + return q + + def _execute_and_instances(self, context): + def iter_for_shard(shard_id): + context.attributes['shard_id'] = context.identity_token = shard_id + result = self._connection_from_session( + mapper=self._mapper_zero(), + shard_id=shard_id).execute( + context.statement, + self._params) + return self.instances(result, context) + + if context.identity_token is not None: + return iter_for_shard(context.identity_token) + elif self._shard_id is not None: + return iter_for_shard(self._shard_id) + else: + partial = [] + for shard_id in self.query_chooser(self): + partial.extend(iter_for_shard(shard_id)) + + # if some kind of in memory 'sorting' + # were done, this is where it would happen + return iter(partial) + + def _identity_lookup( + self, mapper, primary_key_identity, identity_token=None, + lazy_loaded_from=None, **kw): + """override the default Query._identity_lookup method so that we + search for a given non-token primary key identity across all + possible identity tokens (e.g. shard ids). + + """ + + if identity_token is not None: + return super(ShardedQuery, self)._identity_lookup( + mapper, primary_key_identity, + identity_token=identity_token, **kw + ) + else: + q = self.session.query(mapper) + if lazy_loaded_from: + q = q._set_lazyload_from(lazy_loaded_from) + for shard_id in self.id_chooser(q, primary_key_identity): + obj = super(ShardedQuery, self)._identity_lookup( + mapper, primary_key_identity, identity_token=shard_id, **kw + ) + if obj is not None: + return obj + + return None + + def _get_impl( + self, primary_key_identity, db_load_fn, identity_token=None): + """Override the default Query._get_impl() method so that we emit + a query to the DB for each possible identity token, if we don't + have one already. + + """ + def _db_load_fn(query, primary_key_identity): + # load from the database. The original db_load_fn will + # use the given Query object to load from the DB, so our + # shard_id is what will indicate the DB that we query from. + if self._shard_id is not None: + return db_load_fn(self, primary_key_identity) + else: + ident = util.to_list(primary_key_identity) + # build a ShardedQuery for each shard identifier and + # try to load from the DB + for shard_id in self.id_chooser(self, ident): + q = self.set_shard(shard_id) + o = db_load_fn(q, ident) + if o is not None: + return o + else: + return None + + if identity_token is None and self._shard_id is not None: + identity_token = self._shard_id + + return super(ShardedQuery, self)._get_impl( + primary_key_identity, _db_load_fn, identity_token=identity_token) + + +class ShardedSession(Session): + def __init__(self, shard_chooser, id_chooser, query_chooser, shards=None, + query_cls=ShardedQuery, **kwargs): + """Construct a ShardedSession. + + :param shard_chooser: A callable which, passed a Mapper, a mapped + instance, and possibly a SQL clause, returns a shard ID. This id + may be based off of the attributes present within the object, or on + some round-robin scheme. If the scheme is based on a selection, it + should set whatever state on the instance to mark it in the future as + participating in that shard. + + :param id_chooser: A callable, passed a query and a tuple of identity + values, which should return a list of shard ids where the ID might + reside. The databases will be queried in the order of this listing. + + :param query_chooser: For a given Query, returns the list of shard_ids + where the query should be issued. Results from all shards returned + will be combined together into a single listing. + + :param shards: A dictionary of string shard names + to :class:`~sqlalchemy.engine.Engine` objects. + + """ + super(ShardedSession, self).__init__(query_cls=query_cls, **kwargs) + self.shard_chooser = shard_chooser + self.id_chooser = id_chooser + self.query_chooser = query_chooser + self.__binds = {} + self.connection_callable = self.connection + if shards is not None: + for k in shards: + self.bind_shard(k, shards[k]) + + def _choose_shard_and_assign(self, mapper, instance, **kw): + if instance is not None: + state = inspect(instance) + if state.key: + token = state.key[2] + assert token is not None + return token + elif state.identity_token: + return state.identity_token + + shard_id = self.shard_chooser(mapper, instance, **kw) + if instance is not None: + state.identity_token = shard_id + return shard_id + + def connection(self, mapper=None, instance=None, shard_id=None, **kwargs): + if shard_id is None: + shard_id = self._choose_shard_and_assign(mapper, instance) + + if self.transaction is not None: + return self.transaction.connection(mapper, shard_id=shard_id) + else: + return self.get_bind( + mapper, + shard_id=shard_id, + instance=instance + ).contextual_connect(**kwargs) + + def get_bind(self, mapper, shard_id=None, + instance=None, clause=None, **kw): + if shard_id is None: + shard_id = self._choose_shard_and_assign( + mapper, instance, clause=clause) + return self.__binds[shard_id] + + def bind_shard(self, shard_id, bind): + self.__binds[shard_id] = bind diff --git a/venv/Lib/site-packages/sqlalchemy/ext/hybrid.py b/venv/Lib/site-packages/sqlalchemy/ext/hybrid.py new file mode 100644 index 0000000..95eecb9 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/hybrid.py @@ -0,0 +1,1149 @@ +# ext/hybrid.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +r"""Define attributes on ORM-mapped classes that have "hybrid" behavior. + +"hybrid" means the attribute has distinct behaviors defined at the +class level and at the instance level. + +The :mod:`~sqlalchemy.ext.hybrid` extension provides a special form of +method decorator, is around 50 lines of code and has almost no +dependencies on the rest of SQLAlchemy. It can, in theory, work with +any descriptor-based expression system. + +Consider a mapping ``Interval``, representing integer ``start`` and ``end`` +values. We can define higher level functions on mapped classes that produce +SQL expressions at the class level, and Python expression evaluation at the +instance level. Below, each function decorated with :class:`.hybrid_method` or +:class:`.hybrid_property` may receive ``self`` as an instance of the class, or +as the class itself:: + + from sqlalchemy import Column, Integer + from sqlalchemy.ext.declarative import declarative_base + from sqlalchemy.orm import Session, aliased + from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method + + Base = declarative_base() + + class Interval(Base): + __tablename__ = 'interval' + + id = Column(Integer, primary_key=True) + start = Column(Integer, nullable=False) + end = Column(Integer, nullable=False) + + def __init__(self, start, end): + self.start = start + self.end = end + + @hybrid_property + def length(self): + return self.end - self.start + + @hybrid_method + def contains(self, point): + return (self.start <= point) & (point <= self.end) + + @hybrid_method + def intersects(self, other): + return self.contains(other.start) | self.contains(other.end) + +Above, the ``length`` property returns the difference between the +``end`` and ``start`` attributes. With an instance of ``Interval``, +this subtraction occurs in Python, using normal Python descriptor +mechanics:: + + >>> i1 = Interval(5, 10) + >>> i1.length + 5 + +When dealing with the ``Interval`` class itself, the :class:`.hybrid_property` +descriptor evaluates the function body given the ``Interval`` class as +the argument, which when evaluated with SQLAlchemy expression mechanics +returns a new SQL expression:: + + >>> print Interval.length + interval."end" - interval.start + + >>> print Session().query(Interval).filter(Interval.length > 10) + SELECT interval.id AS interval_id, interval.start AS interval_start, + interval."end" AS interval_end + FROM interval + WHERE interval."end" - interval.start > :param_1 + +ORM methods such as :meth:`~.Query.filter_by` generally use ``getattr()`` to +locate attributes, so can also be used with hybrid attributes:: + + >>> print Session().query(Interval).filter_by(length=5) + SELECT interval.id AS interval_id, interval.start AS interval_start, + interval."end" AS interval_end + FROM interval + WHERE interval."end" - interval.start = :param_1 + +The ``Interval`` class example also illustrates two methods, +``contains()`` and ``intersects()``, decorated with +:class:`.hybrid_method`. This decorator applies the same idea to +methods that :class:`.hybrid_property` applies to attributes. The +methods return boolean values, and take advantage of the Python ``|`` +and ``&`` bitwise operators to produce equivalent instance-level and +SQL expression-level boolean behavior:: + + >>> i1.contains(6) + True + >>> i1.contains(15) + False + >>> i1.intersects(Interval(7, 18)) + True + >>> i1.intersects(Interval(25, 29)) + False + + >>> print Session().query(Interval).filter(Interval.contains(15)) + SELECT interval.id AS interval_id, interval.start AS interval_start, + interval."end" AS interval_end + FROM interval + WHERE interval.start <= :start_1 AND interval."end" > :end_1 + + >>> ia = aliased(Interval) + >>> print Session().query(Interval, ia).filter(Interval.intersects(ia)) + SELECT interval.id AS interval_id, interval.start AS interval_start, + interval."end" AS interval_end, interval_1.id AS interval_1_id, + interval_1.start AS interval_1_start, interval_1."end" AS interval_1_end + FROM interval, interval AS interval_1 + WHERE interval.start <= interval_1.start + AND interval."end" > interval_1.start + OR interval.start <= interval_1."end" + AND interval."end" > interval_1."end" + +.. _hybrid_distinct_expression: + +Defining Expression Behavior Distinct from Attribute Behavior +-------------------------------------------------------------- + +Our usage of the ``&`` and ``|`` bitwise operators above was +fortunate, considering our functions operated on two boolean values to +return a new one. In many cases, the construction of an in-Python +function and a SQLAlchemy SQL expression have enough differences that +two separate Python expressions should be defined. The +:mod:`~sqlalchemy.ext.hybrid` decorators define the +:meth:`.hybrid_property.expression` modifier for this purpose. As an +example we'll define the radius of the interval, which requires the +usage of the absolute value function:: + + from sqlalchemy import func + + class Interval(object): + # ... + + @hybrid_property + def radius(self): + return abs(self.length) / 2 + + @radius.expression + def radius(cls): + return func.abs(cls.length) / 2 + +Above the Python function ``abs()`` is used for instance-level +operations, the SQL function ``ABS()`` is used via the :data:`.func` +object for class-level expressions:: + + >>> i1.radius + 2 + + >>> print Session().query(Interval).filter(Interval.radius > 5) + SELECT interval.id AS interval_id, interval.start AS interval_start, + interval."end" AS interval_end + FROM interval + WHERE abs(interval."end" - interval.start) / :abs_1 > :param_1 + +.. note:: When defining an expression for a hybrid property or method, the + expression method **must** retain the name of the original hybrid, else + the new hybrid with the additional state will be attached to the class + with the non-matching name. To use the example above:: + + class Interval(object): + # ... + + @hybrid_property + def radius(self): + return abs(self.length) / 2 + + # WRONG - the non-matching name will cause this function to be + # ignored + @radius.expression + def radius_expression(cls): + return func.abs(cls.length) / 2 + + This is also true for other mutator methods, such as + :meth:`.hybrid_property.update_expression`. This is the same behavior + as that of the ``@property`` construct that is part of standard Python. + +Defining Setters +---------------- + +Hybrid properties can also define setter methods. If we wanted +``length`` above, when set, to modify the endpoint value:: + + class Interval(object): + # ... + + @hybrid_property + def length(self): + return self.end - self.start + + @length.setter + def length(self, value): + self.end = self.start + value + +The ``length(self, value)`` method is now called upon set:: + + >>> i1 = Interval(5, 10) + >>> i1.length + 5 + >>> i1.length = 12 + >>> i1.end + 17 + +.. _hybrid_bulk_update: + +Allowing Bulk ORM Update +------------------------ + +A hybrid can define a custom "UPDATE" handler for when using the +:meth:`.Query.update` method, allowing the hybrid to be used in the +SET clause of the update. + +Normally, when using a hybrid with :meth:`.Query.update`, the SQL +expression is used as the column that's the target of the SET. If our +``Interval`` class had a hybrid ``start_point`` that linked to +``Interval.start``, this could be substituted directly:: + + session.query(Interval).update({Interval.start_point: 10}) + +However, when using a composite hybrid like ``Interval.length``, this +hybrid represents more than one column. We can set up a handler that will +accommodate a value passed to :meth:`.Query.update` which can affect +this, using the :meth:`.hybrid_property.update_expression` decorator. +A handler that works similarly to our setter would be:: + + class Interval(object): + # ... + + @hybrid_property + def length(self): + return self.end - self.start + + @length.setter + def length(self, value): + self.end = self.start + value + + @length.update_expression + def length(cls, value): + return [ + (cls.end, cls.start + value) + ] + +Above, if we use ``Interval.length`` in an UPDATE expression as:: + + session.query(Interval).update( + {Interval.length: 25}, synchronize_session='fetch') + +We'll get an UPDATE statement along the lines of:: + + UPDATE interval SET end=start + :value + +In some cases, the default "evaluate" strategy can't perform the SET +expression in Python; while the addition operator we're using above +is supported, for more complex SET expressions it will usually be necessary +to use either the "fetch" or False synchronization strategy as illustrated +above. + +.. versionadded:: 1.2 added support for bulk updates to hybrid properties. + +Working with Relationships +-------------------------- + +There's no essential difference when creating hybrids that work with +related objects as opposed to column-based data. The need for distinct +expressions tends to be greater. The two variants we'll illustrate +are the "join-dependent" hybrid, and the "correlated subquery" hybrid. + +Join-Dependent Relationship Hybrid +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Consider the following declarative +mapping which relates a ``User`` to a ``SavingsAccount``:: + + from sqlalchemy import Column, Integer, ForeignKey, Numeric, String + from sqlalchemy.orm import relationship + from sqlalchemy.ext.declarative import declarative_base + from sqlalchemy.ext.hybrid import hybrid_property + + Base = declarative_base() + + class SavingsAccount(Base): + __tablename__ = 'account' + id = Column(Integer, primary_key=True) + user_id = Column(Integer, ForeignKey('user.id'), nullable=False) + balance = Column(Numeric(15, 5)) + + class User(Base): + __tablename__ = 'user' + id = Column(Integer, primary_key=True) + name = Column(String(100), nullable=False) + + accounts = relationship("SavingsAccount", backref="owner") + + @hybrid_property + def balance(self): + if self.accounts: + return self.accounts[0].balance + else: + return None + + @balance.setter + def balance(self, value): + if not self.accounts: + account = Account(owner=self) + else: + account = self.accounts[0] + account.balance = value + + @balance.expression + def balance(cls): + return SavingsAccount.balance + +The above hybrid property ``balance`` works with the first +``SavingsAccount`` entry in the list of accounts for this user. The +in-Python getter/setter methods can treat ``accounts`` as a Python +list available on ``self``. + +However, at the expression level, it's expected that the ``User`` class will +be used in an appropriate context such that an appropriate join to +``SavingsAccount`` will be present:: + + >>> print Session().query(User, User.balance).\ + ... join(User.accounts).filter(User.balance > 5000) + SELECT "user".id AS user_id, "user".name AS user_name, + account.balance AS account_balance + FROM "user" JOIN account ON "user".id = account.user_id + WHERE account.balance > :balance_1 + +Note however, that while the instance level accessors need to worry +about whether ``self.accounts`` is even present, this issue expresses +itself differently at the SQL expression level, where we basically +would use an outer join:: + + >>> from sqlalchemy import or_ + >>> print (Session().query(User, User.balance).outerjoin(User.accounts). + ... filter(or_(User.balance < 5000, User.balance == None))) + SELECT "user".id AS user_id, "user".name AS user_name, + account.balance AS account_balance + FROM "user" LEFT OUTER JOIN account ON "user".id = account.user_id + WHERE account.balance < :balance_1 OR account.balance IS NULL + +Correlated Subquery Relationship Hybrid +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We can, of course, forego being dependent on the enclosing query's usage +of joins in favor of the correlated subquery, which can portably be packed +into a single column expression. A correlated subquery is more portable, but +often performs more poorly at the SQL level. Using the same technique +illustrated at :ref:`mapper_column_property_sql_expressions`, +we can adjust our ``SavingsAccount`` example to aggregate the balances for +*all* accounts, and use a correlated subquery for the column expression:: + + from sqlalchemy import Column, Integer, ForeignKey, Numeric, String + from sqlalchemy.orm import relationship + from sqlalchemy.ext.declarative import declarative_base + from sqlalchemy.ext.hybrid import hybrid_property + from sqlalchemy import select, func + + Base = declarative_base() + + class SavingsAccount(Base): + __tablename__ = 'account' + id = Column(Integer, primary_key=True) + user_id = Column(Integer, ForeignKey('user.id'), nullable=False) + balance = Column(Numeric(15, 5)) + + class User(Base): + __tablename__ = 'user' + id = Column(Integer, primary_key=True) + name = Column(String(100), nullable=False) + + accounts = relationship("SavingsAccount", backref="owner") + + @hybrid_property + def balance(self): + return sum(acc.balance for acc in self.accounts) + + @balance.expression + def balance(cls): + return select([func.sum(SavingsAccount.balance)]).\ + where(SavingsAccount.user_id==cls.id).\ + label('total_balance') + +The above recipe will give us the ``balance`` column which renders +a correlated SELECT:: + + >>> print s.query(User).filter(User.balance > 400) + SELECT "user".id AS user_id, "user".name AS user_name + FROM "user" + WHERE (SELECT sum(account.balance) AS sum_1 + FROM account + WHERE account.user_id = "user".id) > :param_1 + +.. _hybrid_custom_comparators: + +Building Custom Comparators +--------------------------- + +The hybrid property also includes a helper that allows construction of +custom comparators. A comparator object allows one to customize the +behavior of each SQLAlchemy expression operator individually. They +are useful when creating custom types that have some highly +idiosyncratic behavior on the SQL side. + +.. note:: The :meth:`.hybrid_property.comparator` decorator introduced + in this section **replaces** the use of the + :meth:`.hybrid_property.expression` decorator. They cannot be used together. + +The example class below allows case-insensitive comparisons on the attribute +named ``word_insensitive``:: + + from sqlalchemy.ext.hybrid import Comparator, hybrid_property + from sqlalchemy import func, Column, Integer, String + from sqlalchemy.orm import Session + from sqlalchemy.ext.declarative import declarative_base + + Base = declarative_base() + + class CaseInsensitiveComparator(Comparator): + def __eq__(self, other): + return func.lower(self.__clause_element__()) == func.lower(other) + + class SearchWord(Base): + __tablename__ = 'searchword' + id = Column(Integer, primary_key=True) + word = Column(String(255), nullable=False) + + @hybrid_property + def word_insensitive(self): + return self.word.lower() + + @word_insensitive.comparator + def word_insensitive(cls): + return CaseInsensitiveComparator(cls.word) + +Above, SQL expressions against ``word_insensitive`` will apply the ``LOWER()`` +SQL function to both sides:: + + >>> print Session().query(SearchWord).filter_by(word_insensitive="Trucks") + SELECT searchword.id AS searchword_id, searchword.word AS searchword_word + FROM searchword + WHERE lower(searchword.word) = lower(:lower_1) + +The ``CaseInsensitiveComparator`` above implements part of the +:class:`.ColumnOperators` interface. A "coercion" operation like +lowercasing can be applied to all comparison operations (i.e. ``eq``, +``lt``, ``gt``, etc.) using :meth:`.Operators.operate`:: + + class CaseInsensitiveComparator(Comparator): + def operate(self, op, other): + return op(func.lower(self.__clause_element__()), func.lower(other)) + +.. _hybrid_reuse_subclass: + +Reusing Hybrid Properties across Subclasses +------------------------------------------- + +A hybrid can be referred to from a superclass, to allow modifying +methods like :meth:`.hybrid_property.getter`, :meth:`.hybrid_property.setter` +to be used to redefine those methods on a subclass. This is similar to +how the standard Python ``@property`` object works:: + + class FirstNameOnly(Base): + # ... + + first_name = Column(String) + + @hybrid_property + def name(self): + return self.first_name + + @name.setter + def name(self, value): + self.first_name = value + + class FirstNameLastName(FirstNameOnly): + # ... + + last_name = Column(String) + + @FirstNameOnly.name.getter + def name(self): + return self.first_name + ' ' + self.last_name + + @name.setter + def name(self, value): + self.first_name, self.last_name = value.split(' ', 1) + +Above, the ``FirstNameLastName`` class refers to the hybrid from +``FirstNameOnly.name`` to repurpose its getter and setter for the subclass. + +When overriding :meth:`.hybrid_property.expression` and +:meth:`.hybrid_property.comparator` alone as the first reference +to the superclass, these names conflict +with the same-named accessors on the class-level :class:`.QueryableAttribute` +object returned at the class level. To override these methods when +referring directly to the parent class descriptor, add +the special qualifier :attr:`.hybrid_property.overrides`, which will +de-reference the instrumented attribute back to the hybrid object:: + + class FirstNameLastName(FirstNameOnly): + # ... + + last_name = Column(String) + + @FirstNameOnly.overrides.expression + def name(cls): + return func.concat(cls.first_name, ' ', cls.last_name) + +.. versionadded:: 1.2 Added :meth:`.hybrid_property.getter` as well as the + ability to redefine accessors per-subclass. + + +Hybrid Value Objects +-------------------- + +Note in our previous example, if we were to compare the +``word_insensitive`` attribute of a ``SearchWord`` instance to a plain +Python string, the plain Python string would not be coerced to lower +case - the ``CaseInsensitiveComparator`` we built, being returned by +``@word_insensitive.comparator``, only applies to the SQL side. + +A more comprehensive form of the custom comparator is to construct a +*Hybrid Value Object*. This technique applies the target value or +expression to a value object which is then returned by the accessor in +all cases. The value object allows control of all operations upon +the value as well as how compared values are treated, both on the SQL +expression side as well as the Python value side. Replacing the +previous ``CaseInsensitiveComparator`` class with a new +``CaseInsensitiveWord`` class:: + + class CaseInsensitiveWord(Comparator): + "Hybrid value representing a lower case representation of a word." + + def __init__(self, word): + if isinstance(word, basestring): + self.word = word.lower() + elif isinstance(word, CaseInsensitiveWord): + self.word = word.word + else: + self.word = func.lower(word) + + def operate(self, op, other): + if not isinstance(other, CaseInsensitiveWord): + other = CaseInsensitiveWord(other) + return op(self.word, other.word) + + def __clause_element__(self): + return self.word + + def __str__(self): + return self.word + + key = 'word' + "Label to apply to Query tuple results" + +Above, the ``CaseInsensitiveWord`` object represents ``self.word``, +which may be a SQL function, or may be a Python native. By +overriding ``operate()`` and ``__clause_element__()`` to work in terms +of ``self.word``, all comparison operations will work against the +"converted" form of ``word``, whether it be SQL side or Python side. +Our ``SearchWord`` class can now deliver the ``CaseInsensitiveWord`` +object unconditionally from a single hybrid call:: + + class SearchWord(Base): + __tablename__ = 'searchword' + id = Column(Integer, primary_key=True) + word = Column(String(255), nullable=False) + + @hybrid_property + def word_insensitive(self): + return CaseInsensitiveWord(self.word) + +The ``word_insensitive`` attribute now has case-insensitive comparison +behavior universally, including SQL expression vs. Python expression +(note the Python value is converted to lower case on the Python side +here):: + + >>> print Session().query(SearchWord).filter_by(word_insensitive="Trucks") + SELECT searchword.id AS searchword_id, searchword.word AS searchword_word + FROM searchword + WHERE lower(searchword.word) = :lower_1 + +SQL expression versus SQL expression:: + + >>> sw1 = aliased(SearchWord) + >>> sw2 = aliased(SearchWord) + >>> print Session().query( + ... sw1.word_insensitive, + ... sw2.word_insensitive).\ + ... filter( + ... sw1.word_insensitive > sw2.word_insensitive + ... ) + SELECT lower(searchword_1.word) AS lower_1, + lower(searchword_2.word) AS lower_2 + FROM searchword AS searchword_1, searchword AS searchword_2 + WHERE lower(searchword_1.word) > lower(searchword_2.word) + +Python only expression:: + + >>> ws1 = SearchWord(word="SomeWord") + >>> ws1.word_insensitive == "sOmEwOrD" + True + >>> ws1.word_insensitive == "XOmEwOrX" + False + >>> print ws1.word_insensitive + someword + +The Hybrid Value pattern is very useful for any kind of value that may +have multiple representations, such as timestamps, time deltas, units +of measurement, currencies and encrypted passwords. + +.. seealso:: + + `Hybrids and Value Agnostic Types + <http://techspot.zzzeek.org/2011/10/21/hybrids-and-value-agnostic-types/>`_ + - on the techspot.zzzeek.org blog + + `Value Agnostic Types, Part II + <http://techspot.zzzeek.org/2011/10/29/value-agnostic-types-part-ii/>`_ - + on the techspot.zzzeek.org blog + +.. _hybrid_transformers: + +Building Transformers +---------------------- + +A *transformer* is an object which can receive a :class:`.Query` +object and return a new one. The :class:`.Query` object includes a +method :meth:`.with_transformation` that returns a new :class:`.Query` +transformed by the given function. + +We can combine this with the :class:`.Comparator` class to produce one type +of recipe which can both set up the FROM clause of a query as well as assign +filtering criterion. + +Consider a mapped class ``Node``, which assembles using adjacency list +into a hierarchical tree pattern:: + + from sqlalchemy import Column, Integer, ForeignKey + from sqlalchemy.orm import relationship + from sqlalchemy.ext.declarative import declarative_base + Base = declarative_base() + + class Node(Base): + __tablename__ = 'node' + id = Column(Integer, primary_key=True) + parent_id = Column(Integer, ForeignKey('node.id')) + parent = relationship("Node", remote_side=id) + +Suppose we wanted to add an accessor ``grandparent``. This would +return the ``parent`` of ``Node.parent``. When we have an instance of +``Node``, this is simple:: + + from sqlalchemy.ext.hybrid import hybrid_property + + class Node(Base): + # ... + + @hybrid_property + def grandparent(self): + return self.parent.parent + +For the expression, things are not so clear. We'd need to construct +a :class:`.Query` where we :meth:`~.Query.join` twice along +``Node.parent`` to get to the ``grandparent``. We can instead return +a transforming callable that we'll combine with the +:class:`.Comparator` class to receive any :class:`.Query` object, and +return a new one that's joined to the ``Node.parent`` attribute and +filtered based on the given criterion:: + + from sqlalchemy.ext.hybrid import Comparator + + class GrandparentTransformer(Comparator): + def operate(self, op, other): + def transform(q): + cls = self.__clause_element__() + parent_alias = aliased(cls) + return q.join(parent_alias, cls.parent).\ + filter(op(parent_alias.parent, other)) + return transform + + Base = declarative_base() + + class Node(Base): + __tablename__ = 'node' + id =Column(Integer, primary_key=True) + parent_id = Column(Integer, ForeignKey('node.id')) + parent = relationship("Node", remote_side=id) + + @hybrid_property + def grandparent(self): + return self.parent.parent + + @grandparent.comparator + def grandparent(cls): + return GrandparentTransformer(cls) + +The ``GrandparentTransformer`` overrides the core +:meth:`.Operators.operate` method at the base of the +:class:`.Comparator` hierarchy to return a query-transforming +callable, which then runs the given comparison operation in a +particular context. Such as, in the example above, the ``operate`` +method is called, given the :attr:`.Operators.eq` callable as well as +the right side of the comparison ``Node(id=5)``. A function +``transform`` is then returned which will transform a :class:`.Query` +first to join to ``Node.parent``, then to compare ``parent_alias`` +using :attr:`.Operators.eq` against the left and right sides, passing +into :class:`.Query.filter`: + +.. sourcecode:: pycon+sql + + >>> from sqlalchemy.orm import Session + >>> session = Session() + {sql}>>> session.query(Node).\ + ... with_transformation(Node.grandparent==Node(id=5)).\ + ... all() + SELECT node.id AS node_id, node.parent_id AS node_parent_id + FROM node JOIN node AS node_1 ON node_1.id = node.parent_id + WHERE :param_1 = node_1.parent_id + {stop} + +We can modify the pattern to be more verbose but flexible by separating +the "join" step from the "filter" step. The tricky part here is ensuring +that successive instances of ``GrandparentTransformer`` use the same +:class:`.AliasedClass` object against ``Node``. Below we use a simple +memoizing approach that associates a ``GrandparentTransformer`` +with each class:: + + class Node(Base): + + # ... + + @grandparent.comparator + def grandparent(cls): + # memoize a GrandparentTransformer + # per class + if '_gp' not in cls.__dict__: + cls._gp = GrandparentTransformer(cls) + return cls._gp + + class GrandparentTransformer(Comparator): + + def __init__(self, cls): + self.parent_alias = aliased(cls) + + @property + def join(self): + def go(q): + return q.join(self.parent_alias, Node.parent) + return go + + def operate(self, op, other): + return op(self.parent_alias.parent, other) + +.. sourcecode:: pycon+sql + + {sql}>>> session.query(Node).\ + ... with_transformation(Node.grandparent.join).\ + ... filter(Node.grandparent==Node(id=5)) + SELECT node.id AS node_id, node.parent_id AS node_parent_id + FROM node JOIN node AS node_1 ON node_1.id = node.parent_id + WHERE :param_1 = node_1.parent_id + {stop} + +The "transformer" pattern is an experimental pattern that starts +to make usage of some functional programming paradigms. +While it's only recommended for advanced and/or patient developers, +there's probably a whole lot of amazing things it can be used for. + +""" +from .. import util +from ..orm import attributes, interfaces + +HYBRID_METHOD = util.symbol('HYBRID_METHOD') +"""Symbol indicating an :class:`InspectionAttr` that's + of type :class:`.hybrid_method`. + + Is assigned to the :attr:`.InspectionAttr.extension_type` + attibute. + + .. seealso:: + + :attr:`.Mapper.all_orm_attributes` + +""" + +HYBRID_PROPERTY = util.symbol('HYBRID_PROPERTY') +"""Symbol indicating an :class:`InspectionAttr` that's + of type :class:`.hybrid_method`. + + Is assigned to the :attr:`.InspectionAttr.extension_type` + attibute. + + .. seealso:: + + :attr:`.Mapper.all_orm_attributes` + +""" + + +class hybrid_method(interfaces.InspectionAttrInfo): + """A decorator which allows definition of a Python object method with both + instance-level and class-level behavior. + + """ + + is_attribute = True + extension_type = HYBRID_METHOD + + def __init__(self, func, expr=None): + """Create a new :class:`.hybrid_method`. + + Usage is typically via decorator:: + + from sqlalchemy.ext.hybrid import hybrid_method + + class SomeClass(object): + @hybrid_method + def value(self, x, y): + return self._value + x + y + + @value.expression + def value(self, x, y): + return func.some_function(self._value, x, y) + + """ + self.func = func + self.expression(expr or func) + + def __get__(self, instance, owner): + if instance is None: + return self.expr.__get__(owner, owner.__class__) + else: + return self.func.__get__(instance, owner) + + def expression(self, expr): + """Provide a modifying decorator that defines a + SQL-expression producing method.""" + + self.expr = expr + if not self.expr.__doc__: + self.expr.__doc__ = self.func.__doc__ + return self + + +class hybrid_property(interfaces.InspectionAttrInfo): + """A decorator which allows definition of a Python descriptor with both + instance-level and class-level behavior. + + """ + + is_attribute = True + extension_type = HYBRID_PROPERTY + + def __init__( + self, fget, fset=None, fdel=None, + expr=None, custom_comparator=None, update_expr=None): + """Create a new :class:`.hybrid_property`. + + Usage is typically via decorator:: + + from sqlalchemy.ext.hybrid import hybrid_property + + class SomeClass(object): + @hybrid_property + def value(self): + return self._value + + @value.setter + def value(self, value): + self._value = value + + """ + self.fget = fget + self.fset = fset + self.fdel = fdel + self.expr = expr + self.custom_comparator = custom_comparator + self.update_expr = update_expr + util.update_wrapper(self, fget) + + def __get__(self, instance, owner): + if instance is None: + return self._expr_comparator(owner) + else: + return self.fget(instance) + + def __set__(self, instance, value): + if self.fset is None: + raise AttributeError("can't set attribute") + self.fset(instance, value) + + def __delete__(self, instance): + if self.fdel is None: + raise AttributeError("can't delete attribute") + self.fdel(instance) + + def _copy(self, **kw): + defaults = { + key: value + for key, value in self.__dict__.items() + if not key.startswith("_")} + defaults.update(**kw) + return type(self)(**defaults) + + @property + def overrides(self): + """Prefix for a method that is overriding an existing attribute. + + The :attr:`.hybrid_property.overrides` accessor just returns + this hybrid object, which when called at the class level from + a parent class, will de-reference the "instrumented attribute" + normally returned at this level, and allow modifying decorators + like :meth:`.hybrid_property.expression` and + :meth:`.hybrid_property.comparator` + to be used without conflicting with the same-named attributes + normally present on the :class:`.QueryableAttribute`:: + + class SuperClass(object): + # ... + + @hybrid_property + def foobar(self): + return self._foobar + + class SubClass(SuperClass): + # ... + + @SuperClass.foobar.overrides.expression + def foobar(cls): + return func.subfoobar(self._foobar) + + .. versionadded:: 1.2 + + .. seealso:: + + :ref:`hybrid_reuse_subclass` + + """ + return self + + def getter(self, fget): + """Provide a modifying decorator that defines a getter method. + + .. versionadded:: 1.2 + + """ + + return self._copy(fget=fget) + + def setter(self, fset): + """Provide a modifying decorator that defines a setter method.""" + + return self._copy(fset=fset) + + def deleter(self, fdel): + """Provide a modifying decorator that defines a deletion method.""" + + return self._copy(fdel=fdel) + + def expression(self, expr): + """Provide a modifying decorator that defines a SQL-expression + producing method. + + When a hybrid is invoked at the class level, the SQL expression given + here is wrapped inside of a specialized :class:`.QueryableAttribute`, + which is the same kind of object used by the ORM to represent other + mapped attributes. The reason for this is so that other class-level + attributes such as docstrings and a reference to the hybrid itself may + be maintained within the structure that's returned, without any + modifications to the original SQL expression passed in. + + .. note:: + + when referring to a hybrid property from an owning class (e.g. + ``SomeClass.some_hybrid``), an instance of + :class:`.QueryableAttribute` is returned, representing the + expression or comparator object as well as this hybrid object. + However, that object itself has accessors called ``expression`` and + ``comparator``; so when attempting to override these decorators on a + subclass, it may be necessary to qualify it using the + :attr:`.hybrid_property.overrides` modifier first. See that + modifier for details. + + .. seealso:: + + :ref:`hybrid_distinct_expression` + + """ + + return self._copy(expr=expr) + + def comparator(self, comparator): + """Provide a modifying decorator that defines a custom + comparator producing method. + + The return value of the decorated method should be an instance of + :class:`~.hybrid.Comparator`. + + .. note:: The :meth:`.hybrid_property.comparator` decorator + **replaces** the use of the :meth:`.hybrid_property.expression` + decorator. They cannot be used together. + + When a hybrid is invoked at the class level, the + :class:`~.hybrid.Comparator` object given here is wrapped inside of a + specialized :class:`.QueryableAttribute`, which is the same kind of + object used by the ORM to represent other mapped attributes. The + reason for this is so that other class-level attributes such as + docstrings and a reference to the hybrid itself may be maintained + within the structure that's returned, without any modifications to the + original comparator object passed in. + + .. note:: + + when referring to a hybrid property from an owning class (e.g. + ``SomeClass.some_hybrid``), an instance of + :class:`.QueryableAttribute` is returned, representing the + expression or comparator object as this hybrid object. However, + that object itself has accessors called ``expression`` and + ``comparator``; so when attempting to override these decorators on a + subclass, it may be necessary to qualify it using the + :attr:`.hybrid_property.overrides` modifier first. See that + modifier for details. + + """ + return self._copy(custom_comparator=comparator) + + def update_expression(self, meth): + """Provide a modifying decorator that defines an UPDATE tuple + producing method. + + The method accepts a single value, which is the value to be + rendered into the SET clause of an UPDATE statement. The method + should then process this value into individual column expressions + that fit into the ultimate SET clause, and return them as a + sequence of 2-tuples. Each tuple + contains a column expression as the key and a value to be rendered. + + E.g.:: + + class Person(Base): + # ... + + first_name = Column(String) + last_name = Column(String) + + @hybrid_property + def fullname(self): + return first_name + " " + last_name + + @fullname.update_expression + def fullname(cls, value): + fname, lname = value.split(" ", 1) + return [ + (cls.first_name, fname), + (cls.last_name, lname) + ] + + .. versionadded:: 1.2 + + """ + return self._copy(update_expr=meth) + + @util.memoized_property + def _expr_comparator(self): + if self.custom_comparator is not None: + return self._get_comparator(self.custom_comparator) + elif self.expr is not None: + return self._get_expr(self.expr) + else: + return self._get_expr(self.fget) + + def _get_expr(self, expr): + + def _expr(cls): + return ExprComparator(cls, expr(cls), self) + util.update_wrapper(_expr, expr) + + return self._get_comparator(_expr) + + def _get_comparator(self, comparator): + + proxy_attr = attributes.create_proxied_attribute(self) + + def expr_comparator(owner): + return proxy_attr( + owner, self.__name__, self, comparator(owner), + doc=comparator.__doc__ or self.__doc__) + return expr_comparator + + +class Comparator(interfaces.PropComparator): + """A helper class that allows easy construction of custom + :class:`~.orm.interfaces.PropComparator` + classes for usage with hybrids.""" + + property = None + + def __init__(self, expression): + self.expression = expression + + def __clause_element__(self): + expr = self.expression + if hasattr(expr, '__clause_element__'): + expr = expr.__clause_element__() + return expr + + def adapt_to_entity(self, adapt_to_entity): + # interesting.... + return self + + +class ExprComparator(Comparator): + def __init__(self, cls, expression, hybrid): + self.cls = cls + self.expression = expression + self.hybrid = hybrid + + def __getattr__(self, key): + return getattr(self.expression, key) + + @property + def info(self): + return self.hybrid.info + + def _bulk_update_tuples(self, value): + if isinstance(self.expression, attributes.QueryableAttribute): + return self.expression._bulk_update_tuples(value) + elif self.hybrid.update_expr is not None: + return self.hybrid.update_expr(self.cls, value) + else: + return [(self.expression, value)] + + @property + def property(self): + return self.expression.property + + def operate(self, op, *other, **kwargs): + return op(self.expression, *other, **kwargs) + + def reverse_operate(self, op, other, **kwargs): + return op(other, self.expression, **kwargs) diff --git a/venv/Lib/site-packages/sqlalchemy/ext/indexable.py b/venv/Lib/site-packages/sqlalchemy/ext/indexable.py new file mode 100644 index 0000000..0bc2b65 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/indexable.py @@ -0,0 +1,349 @@ +# ext/index.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Define attributes on ORM-mapped classes that have "index" attributes for +columns with :class:`~.types.Indexable` types. + +"index" means the attribute is associated with an element of an +:class:`~.types.Indexable` column with the predefined index to access it. +The :class:`~.types.Indexable` types include types such as +:class:`~.types.ARRAY`, :class:`~.types.JSON` and +:class:`~.postgresql.HSTORE`. + + + +The :mod:`~sqlalchemy.ext.indexable` extension provides +:class:`~.schema.Column`-like interface for any element of an +:class:`~.types.Indexable` typed column. In simple cases, it can be +treated as a :class:`~.schema.Column` - mapped attribute. + + +.. versionadded:: 1.1 + +Synopsis +======== + +Given ``Person`` as a model with a primary key and JSON data field. +While this field may have any number of elements encoded within it, +we would like to refer to the element called ``name`` individually +as a dedicated attribute which behaves like a standalone column:: + + from sqlalchemy import Column, JSON, Integer + from sqlalchemy.ext.declarative import declarative_base + from sqlalchemy.ext.indexable import index_property + + Base = declarative_base() + + class Person(Base): + __tablename__ = 'person' + + id = Column(Integer, primary_key=True) + data = Column(JSON) + + name = index_property('data', 'name') + + +Above, the ``name`` attribute now behaves like a mapped column. We +can compose a new ``Person`` and set the value of ``name``:: + + >>> person = Person(name='Alchemist') + +The value is now accessible:: + + >>> person.name + 'Alchemist' + +Behind the scenes, the JSON field was initialized to a new blank dictionary +and the field was set:: + + >>> person.data + {"name": "Alchemist'} + +The field is mutable in place:: + + >>> person.name = 'Renamed' + >>> person.name + 'Renamed' + >>> person.data + {'name': 'Renamed'} + +When using :class:`.index_property`, the change that we make to the indexable +structure is also automatically tracked as history; we no longer need +to use :class:`~.mutable.MutableDict` in order to track this change +for the unit of work. + +Deletions work normally as well:: + + >>> del person.name + >>> person.data + {} + +Above, deletion of ``person.name`` deletes the value from the dictionary, +but not the dictionary itself. + +A missing key will produce ``AttributeError``:: + + >>> person = Person() + >>> person.name + ... + AttributeError: 'name' + +Unless you set a default value:: + + >>> class Person(Base): + >>> __tablename__ = 'person' + >>> + >>> id = Column(Integer, primary_key=True) + >>> data = Column(JSON) + >>> + >>> name = index_property('data', 'name', default=None) # See default + + >>> person = Person() + >>> print(person.name) + None + + +The attributes are also accessible at the class level. +Below, we illustrate ``Person.name`` used to generate +an indexed SQL criteria:: + + >>> from sqlalchemy.orm import Session + >>> session = Session() + >>> query = session.query(Person).filter(Person.name == 'Alchemist') + +The above query is equivalent to:: + + >>> query = session.query(Person).filter(Person.data['name'] == 'Alchemist') + +Multiple :class:`.index_property` objects can be chained to produce +multiple levels of indexing:: + + from sqlalchemy import Column, JSON, Integer + from sqlalchemy.ext.declarative import declarative_base + from sqlalchemy.ext.indexable import index_property + + Base = declarative_base() + + class Person(Base): + __tablename__ = 'person' + + id = Column(Integer, primary_key=True) + data = Column(JSON) + + birthday = index_property('data', 'birthday') + year = index_property('birthday', 'year') + month = index_property('birthday', 'month') + day = index_property('birthday', 'day') + +Above, a query such as:: + + q = session.query(Person).filter(Person.year == '1980') + +On a PostgreSQL backend, the above query will render as:: + + SELECT person.id, person.data + FROM person + WHERE person.data -> %(data_1)s -> %(param_1)s = %(param_2)s + +Default Values +============== + +:class:`.index_property` includes special behaviors for when the indexed +data structure does not exist, and a set operation is called: + +* For an :class:`.index_property` that is given an integer index value, + the default data structure will be a Python list of ``None`` values, + at least as long as the index value; the value is then set at its + place in the list. This means for an index value of zero, the list + will be initialized to ``[None]`` before setting the given value, + and for an index value of five, the list will be initialized to + ``[None, None, None, None, None]`` before setting the fifth element + to the given value. Note that an existing list is **not** extended + in place to receive a value. + +* for an :class:`.index_property` that is given any other kind of index + value (e.g. strings usually), a Python dictionary is used as the + default data structure. + +* The default data structure can be set to any Python callable using the + :paramref:`.index_property.datatype` parameter, overriding the previous + rules. + + + + + + +Subclassing +=========== + +:class:`.index_property` can be subclassed, in particular for the common +use case of providing coercion of values or SQL expressions as they are +accessed. Below is a common recipe for use with a PostgreSQL JSON type, +where we want to also include automatic casting plus ``astext()``:: + + class pg_json_property(index_property): + def __init__(self, attr_name, index, cast_type): + super(pg_json_property, self).__init__(attr_name, index) + self.cast_type = cast_type + + def expr(self, model): + expr = super(pg_json_property, self).expr(model) + return expr.astext.cast(self.cast_type) + +The above subclass can be used with the PostgreSQL-specific +version of :class:`.postgresql.JSON`:: + + from sqlalchemy import Column, Integer + from sqlalchemy.ext.declarative import declarative_base + from sqlalchemy.dialects.postgresql import JSON + + Base = declarative_base() + + class Person(Base): + __tablename__ = 'person' + + id = Column(Integer, primary_key=True) + data = Column(JSON) + + age = pg_json_property('data', 'age', Integer) + +The ``age`` attribute at the instance level works as before; however +when rendering SQL, PostgreSQL's ``->>`` operator will be used +for indexed access, instead of the usual index opearator of ``->``:: + + >>> query = session.query(Person).filter(Person.age < 20) + +The above query will render:: + + SELECT person.id, person.data + FROM person + WHERE CAST(person.data ->> %(data_1)s AS INTEGER) < %(param_1)s + +""" +from __future__ import absolute_import + +from sqlalchemy import inspect +from ..orm.attributes import flag_modified +from ..ext.hybrid import hybrid_property + + +__all__ = ['index_property'] + + +class index_property(hybrid_property): # noqa + """A property generator. The generated property describes an object + attribute that corresponds to an :class:`~.types.Indexable` + column. + + .. versionadded:: 1.1 + + .. seealso:: + + :mod:`sqlalchemy.ext.indexable` + + """ + + _NO_DEFAULT_ARGUMENT = object() + + def __init__( + self, attr_name, index, default=_NO_DEFAULT_ARGUMENT, + datatype=None, mutable=True, onebased=True): + """Create a new :class:`.index_property`. + + :param attr_name: + An attribute name of an `Indexable` typed column, or other + attribute that returns an indexable structure. + :param index: + The index to be used for getting and setting this value. This + should be the Python-side index value for integers. + :param default: + A value which will be returned instead of `AttributeError` + when there is not a value at given index. + :param datatype: default datatype to use when the field is empty. + By default, this is derived from the type of index used; a + Python list for an integer index, or a Python dictionary for + any other style of index. For a list, the list will be + initialized to a list of None values that is at least + ``index`` elements long. + :param mutable: if False, writes and deletes to the attribute will + be disallowed. + :param onebased: assume the SQL representation of this value is + one-based; that is, the first index in SQL is 1, not zero. + """ + + if mutable: + super(index_property, self).__init__( + self.fget, self.fset, self.fdel, self.expr + ) + else: + super(index_property, self).__init__( + self.fget, None, None, self.expr + ) + self.attr_name = attr_name + self.index = index + self.default = default + is_numeric = isinstance(index, int) + onebased = is_numeric and onebased + + if datatype is not None: + self.datatype = datatype + else: + if is_numeric: + self.datatype = lambda: [None for x in range(index + 1)] + else: + self.datatype = dict + self.onebased = onebased + + def _fget_default(self): + if self.default == self._NO_DEFAULT_ARGUMENT: + raise AttributeError(self.attr_name) + else: + return self.default + + def fget(self, instance): + attr_name = self.attr_name + column_value = getattr(instance, attr_name) + if column_value is None: + return self._fget_default() + try: + value = column_value[self.index] + except (KeyError, IndexError): + return self._fget_default() + else: + return value + + def fset(self, instance, value): + attr_name = self.attr_name + column_value = getattr(instance, attr_name, None) + if column_value is None: + column_value = self.datatype() + setattr(instance, attr_name, column_value) + column_value[self.index] = value + setattr(instance, attr_name, column_value) + if attr_name in inspect(instance).mapper.attrs: + flag_modified(instance, attr_name) + + def fdel(self, instance): + attr_name = self.attr_name + column_value = getattr(instance, attr_name) + if column_value is None: + raise AttributeError(self.attr_name) + try: + del column_value[self.index] + except KeyError: + raise AttributeError(self.attr_name) + else: + setattr(instance, attr_name, column_value) + flag_modified(instance, attr_name) + + def expr(self, model): + column = getattr(model, self.attr_name) + index = self.index + if self.onebased: + index += 1 + return column[index] diff --git a/venv/Lib/site-packages/sqlalchemy/ext/instrumentation.py b/venv/Lib/site-packages/sqlalchemy/ext/instrumentation.py new file mode 100644 index 0000000..30a0ab7 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/instrumentation.py @@ -0,0 +1,414 @@ +"""Extensible class instrumentation. + +The :mod:`sqlalchemy.ext.instrumentation` package provides for alternate +systems of class instrumentation within the ORM. Class instrumentation +refers to how the ORM places attributes on the class which maintain +data and track changes to that data, as well as event hooks installed +on the class. + +.. note:: + The extension package is provided for the benefit of integration + with other object management packages, which already perform + their own instrumentation. It is not intended for general use. + +For examples of how the instrumentation extension is used, +see the example :ref:`examples_instrumentation`. + +.. versionchanged:: 0.8 + The :mod:`sqlalchemy.orm.instrumentation` was split out so + that all functionality having to do with non-standard + instrumentation was moved out to :mod:`sqlalchemy.ext.instrumentation`. + When imported, the module installs itself within + :mod:`sqlalchemy.orm.instrumentation` so that it + takes effect, including recognition of + ``__sa_instrumentation_manager__`` on mapped classes, as + well :data:`.instrumentation_finders` + being used to determine class instrumentation resolution. + +""" +from ..orm import instrumentation as orm_instrumentation +from ..orm.instrumentation import ( + ClassManager, InstrumentationFactory, _default_state_getter, + _default_dict_getter, _default_manager_getter +) +from ..orm import attributes, collections, base as orm_base +from .. import util +from ..orm import exc as orm_exc +import weakref + +INSTRUMENTATION_MANAGER = '__sa_instrumentation_manager__' +"""Attribute, elects custom instrumentation when present on a mapped class. + +Allows a class to specify a slightly or wildly different technique for +tracking changes made to mapped attributes and collections. + +Only one instrumentation implementation is allowed in a given object +inheritance hierarchy. + +The value of this attribute must be a callable and will be passed a class +object. The callable must return one of: + + - An instance of an InstrumentationManager or subclass + - An object implementing all or some of InstrumentationManager (TODO) + - A dictionary of callables, implementing all or some of the above (TODO) + - An instance of a ClassManager or subclass + +This attribute is consulted by SQLAlchemy instrumentation +resolution, once the :mod:`sqlalchemy.ext.instrumentation` module +has been imported. If custom finders are installed in the global +instrumentation_finders list, they may or may not choose to honor this +attribute. + +""" + + +def find_native_user_instrumentation_hook(cls): + """Find user-specified instrumentation management for a class.""" + return getattr(cls, INSTRUMENTATION_MANAGER, None) + +instrumentation_finders = [find_native_user_instrumentation_hook] +"""An extensible sequence of callables which return instrumentation +implementations + +When a class is registered, each callable will be passed a class object. +If None is returned, the +next finder in the sequence is consulted. Otherwise the return must be an +instrumentation factory that follows the same guidelines as +sqlalchemy.ext.instrumentation.INSTRUMENTATION_MANAGER. + +By default, the only finder is find_native_user_instrumentation_hook, which +searches for INSTRUMENTATION_MANAGER. If all finders return None, standard +ClassManager instrumentation is used. + +""" + + +class ExtendedInstrumentationRegistry(InstrumentationFactory): + """Extends :class:`.InstrumentationFactory` with additional + bookkeeping, to accommodate multiple types of + class managers. + + """ + _manager_finders = weakref.WeakKeyDictionary() + _state_finders = weakref.WeakKeyDictionary() + _dict_finders = weakref.WeakKeyDictionary() + _extended = False + + def _locate_extended_factory(self, class_): + for finder in instrumentation_finders: + factory = finder(class_) + if factory is not None: + manager = self._extended_class_manager(class_, factory) + return manager, factory + else: + return None, None + + def _check_conflicts(self, class_, factory): + existing_factories = self._collect_management_factories_for(class_).\ + difference([factory]) + if existing_factories: + raise TypeError( + "multiple instrumentation implementations specified " + "in %s inheritance hierarchy: %r" % ( + class_.__name__, list(existing_factories))) + + def _extended_class_manager(self, class_, factory): + manager = factory(class_) + if not isinstance(manager, ClassManager): + manager = _ClassInstrumentationAdapter(class_, manager) + + if factory != ClassManager and not self._extended: + # somebody invoked a custom ClassManager. + # reinstall global "getter" functions with the more + # expensive ones. + self._extended = True + _install_instrumented_lookups() + + self._manager_finders[class_] = manager.manager_getter() + self._state_finders[class_] = manager.state_getter() + self._dict_finders[class_] = manager.dict_getter() + return manager + + def _collect_management_factories_for(self, cls): + """Return a collection of factories in play or specified for a + hierarchy. + + Traverses the entire inheritance graph of a cls and returns a + collection of instrumentation factories for those classes. Factories + are extracted from active ClassManagers, if available, otherwise + instrumentation_finders is consulted. + + """ + hierarchy = util.class_hierarchy(cls) + factories = set() + for member in hierarchy: + manager = self.manager_of_class(member) + if manager is not None: + factories.add(manager.factory) + else: + for finder in instrumentation_finders: + factory = finder(member) + if factory is not None: + break + else: + factory = None + factories.add(factory) + factories.discard(None) + return factories + + def unregister(self, class_): + if class_ in self._manager_finders: + del self._manager_finders[class_] + del self._state_finders[class_] + del self._dict_finders[class_] + super(ExtendedInstrumentationRegistry, self).unregister(class_) + + def manager_of_class(self, cls): + if cls is None: + return None + try: + finder = self._manager_finders.get(cls, _default_manager_getter) + except TypeError: + # due to weakref lookup on invalid object + return None + else: + return finder(cls) + + def state_of(self, instance): + if instance is None: + raise AttributeError("None has no persistent state.") + return self._state_finders.get( + instance.__class__, _default_state_getter)(instance) + + def dict_of(self, instance): + if instance is None: + raise AttributeError("None has no persistent state.") + return self._dict_finders.get( + instance.__class__, _default_dict_getter)(instance) + + +orm_instrumentation._instrumentation_factory = \ + _instrumentation_factory = ExtendedInstrumentationRegistry() +orm_instrumentation.instrumentation_finders = instrumentation_finders + + +class InstrumentationManager(object): + """User-defined class instrumentation extension. + + :class:`.InstrumentationManager` can be subclassed in order + to change + how class instrumentation proceeds. This class exists for + the purposes of integration with other object management + frameworks which would like to entirely modify the + instrumentation methodology of the ORM, and is not intended + for regular usage. For interception of class instrumentation + events, see :class:`.InstrumentationEvents`. + + The API for this class should be considered as semi-stable, + and may change slightly with new releases. + + .. versionchanged:: 0.8 + :class:`.InstrumentationManager` was moved from + :mod:`sqlalchemy.orm.instrumentation` to + :mod:`sqlalchemy.ext.instrumentation`. + + """ + + # r4361 added a mandatory (cls) constructor to this interface. + # given that, perhaps class_ should be dropped from all of these + # signatures. + + def __init__(self, class_): + pass + + def manage(self, class_, manager): + setattr(class_, '_default_class_manager', manager) + + def dispose(self, class_, manager): + delattr(class_, '_default_class_manager') + + def manager_getter(self, class_): + def get(cls): + return cls._default_class_manager + return get + + def instrument_attribute(self, class_, key, inst): + pass + + def post_configure_attribute(self, class_, key, inst): + pass + + def install_descriptor(self, class_, key, inst): + setattr(class_, key, inst) + + def uninstall_descriptor(self, class_, key): + delattr(class_, key) + + def install_member(self, class_, key, implementation): + setattr(class_, key, implementation) + + def uninstall_member(self, class_, key): + delattr(class_, key) + + def instrument_collection_class(self, class_, key, collection_class): + return collections.prepare_instrumentation(collection_class) + + def get_instance_dict(self, class_, instance): + return instance.__dict__ + + def initialize_instance_dict(self, class_, instance): + pass + + def install_state(self, class_, instance, state): + setattr(instance, '_default_state', state) + + def remove_state(self, class_, instance): + delattr(instance, '_default_state') + + def state_getter(self, class_): + return lambda instance: getattr(instance, '_default_state') + + def dict_getter(self, class_): + return lambda inst: self.get_instance_dict(class_, inst) + + +class _ClassInstrumentationAdapter(ClassManager): + """Adapts a user-defined InstrumentationManager to a ClassManager.""" + + def __init__(self, class_, override): + self._adapted = override + self._get_state = self._adapted.state_getter(class_) + self._get_dict = self._adapted.dict_getter(class_) + + ClassManager.__init__(self, class_) + + def manage(self): + self._adapted.manage(self.class_, self) + + def dispose(self): + self._adapted.dispose(self.class_) + + def manager_getter(self): + return self._adapted.manager_getter(self.class_) + + def instrument_attribute(self, key, inst, propagated=False): + ClassManager.instrument_attribute(self, key, inst, propagated) + if not propagated: + self._adapted.instrument_attribute(self.class_, key, inst) + + def post_configure_attribute(self, key): + super(_ClassInstrumentationAdapter, self).post_configure_attribute(key) + self._adapted.post_configure_attribute(self.class_, key, self[key]) + + def install_descriptor(self, key, inst): + self._adapted.install_descriptor(self.class_, key, inst) + + def uninstall_descriptor(self, key): + self._adapted.uninstall_descriptor(self.class_, key) + + def install_member(self, key, implementation): + self._adapted.install_member(self.class_, key, implementation) + + def uninstall_member(self, key): + self._adapted.uninstall_member(self.class_, key) + + def instrument_collection_class(self, key, collection_class): + return self._adapted.instrument_collection_class( + self.class_, key, collection_class) + + def initialize_collection(self, key, state, factory): + delegate = getattr(self._adapted, 'initialize_collection', None) + if delegate: + return delegate(key, state, factory) + else: + return ClassManager.initialize_collection(self, key, + state, factory) + + def new_instance(self, state=None): + instance = self.class_.__new__(self.class_) + self.setup_instance(instance, state) + return instance + + def _new_state_if_none(self, instance): + """Install a default InstanceState if none is present. + + A private convenience method used by the __init__ decorator. + """ + if self.has_state(instance): + return False + else: + return self.setup_instance(instance) + + def setup_instance(self, instance, state=None): + self._adapted.initialize_instance_dict(self.class_, instance) + + if state is None: + state = self._state_constructor(instance, self) + + # the given instance is assumed to have no state + self._adapted.install_state(self.class_, instance, state) + return state + + def teardown_instance(self, instance): + self._adapted.remove_state(self.class_, instance) + + def has_state(self, instance): + try: + self._get_state(instance) + except orm_exc.NO_STATE: + return False + else: + return True + + def state_getter(self): + return self._get_state + + def dict_getter(self): + return self._get_dict + + +def _install_instrumented_lookups(): + """Replace global class/object management functions + with ExtendedInstrumentationRegistry implementations, which + allow multiple types of class managers to be present, + at the cost of performance. + + This function is called only by ExtendedInstrumentationRegistry + and unit tests specific to this behavior. + + The _reinstall_default_lookups() function can be called + after this one to re-establish the default functions. + + """ + _install_lookups( + dict( + instance_state=_instrumentation_factory.state_of, + instance_dict=_instrumentation_factory.dict_of, + manager_of_class=_instrumentation_factory.manager_of_class + ) + ) + + +def _reinstall_default_lookups(): + """Restore simplified lookups.""" + _install_lookups( + dict( + instance_state=_default_state_getter, + instance_dict=_default_dict_getter, + manager_of_class=_default_manager_getter + ) + ) + _instrumentation_factory._extended = False + + +def _install_lookups(lookups): + global instance_state, instance_dict, manager_of_class + instance_state = lookups['instance_state'] + instance_dict = lookups['instance_dict'] + manager_of_class = lookups['manager_of_class'] + orm_base.instance_state = attributes.instance_state = \ + orm_instrumentation.instance_state = instance_state + orm_base.instance_dict = attributes.instance_dict = \ + orm_instrumentation.instance_dict = instance_dict + orm_base.manager_of_class = attributes.manager_of_class = \ + orm_instrumentation.manager_of_class = manager_of_class diff --git a/venv/Lib/site-packages/sqlalchemy/ext/mutable.py b/venv/Lib/site-packages/sqlalchemy/ext/mutable.py new file mode 100644 index 0000000..014cef3 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/mutable.py @@ -0,0 +1,950 @@ +# ext/mutable.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +r"""Provide support for tracking of in-place changes to scalar values, +which are propagated into ORM change events on owning parent objects. + +.. versionadded:: 0.7 :mod:`sqlalchemy.ext.mutable` replaces SQLAlchemy's + legacy approach to in-place mutations of scalar values; see + :ref:`07_migration_mutation_extension`. + +.. _mutable_scalars: + +Establishing Mutability on Scalar Column Values +=============================================== + +A typical example of a "mutable" structure is a Python dictionary. +Following the example introduced in :ref:`types_toplevel`, we +begin with a custom type that marshals Python dictionaries into +JSON strings before being persisted:: + + from sqlalchemy.types import TypeDecorator, VARCHAR + import json + + class JSONEncodedDict(TypeDecorator): + "Represents an immutable structure as a json-encoded string." + + impl = VARCHAR + + def process_bind_param(self, value, dialect): + if value is not None: + value = json.dumps(value) + return value + + def process_result_value(self, value, dialect): + if value is not None: + value = json.loads(value) + return value + +The usage of ``json`` is only for the purposes of example. The +:mod:`sqlalchemy.ext.mutable` extension can be used +with any type whose target Python type may be mutable, including +:class:`.PickleType`, :class:`.postgresql.ARRAY`, etc. + +When using the :mod:`sqlalchemy.ext.mutable` extension, the value itself +tracks all parents which reference it. Below, we illustrate a simple +version of the :class:`.MutableDict` dictionary object, which applies +the :class:`.Mutable` mixin to a plain Python dictionary:: + + from sqlalchemy.ext.mutable import Mutable + + class MutableDict(Mutable, dict): + @classmethod + def coerce(cls, key, value): + "Convert plain dictionaries to MutableDict." + + if not isinstance(value, MutableDict): + if isinstance(value, dict): + return MutableDict(value) + + # this call will raise ValueError + return Mutable.coerce(key, value) + else: + return value + + def __setitem__(self, key, value): + "Detect dictionary set events and emit change events." + + dict.__setitem__(self, key, value) + self.changed() + + def __delitem__(self, key): + "Detect dictionary del events and emit change events." + + dict.__delitem__(self, key) + self.changed() + +The above dictionary class takes the approach of subclassing the Python +built-in ``dict`` to produce a dict +subclass which routes all mutation events through ``__setitem__``. There are +variants on this approach, such as subclassing ``UserDict.UserDict`` or +``collections.MutableMapping``; the part that's important to this example is +that the :meth:`.Mutable.changed` method is called whenever an in-place +change to the datastructure takes place. + +We also redefine the :meth:`.Mutable.coerce` method which will be used to +convert any values that are not instances of ``MutableDict``, such +as the plain dictionaries returned by the ``json`` module, into the +appropriate type. Defining this method is optional; we could just as well +created our ``JSONEncodedDict`` such that it always returns an instance +of ``MutableDict``, and additionally ensured that all calling code +uses ``MutableDict`` explicitly. When :meth:`.Mutable.coerce` is not +overridden, any values applied to a parent object which are not instances +of the mutable type will raise a ``ValueError``. + +Our new ``MutableDict`` type offers a class method +:meth:`~.Mutable.as_mutable` which we can use within column metadata +to associate with types. This method grabs the given type object or +class and associates a listener that will detect all future mappings +of this type, applying event listening instrumentation to the mapped +attribute. Such as, with classical table metadata:: + + from sqlalchemy import Table, Column, Integer + + my_data = Table('my_data', metadata, + Column('id', Integer, primary_key=True), + Column('data', MutableDict.as_mutable(JSONEncodedDict)) + ) + +Above, :meth:`~.Mutable.as_mutable` returns an instance of ``JSONEncodedDict`` +(if the type object was not an instance already), which will intercept any +attributes which are mapped against this type. Below we establish a simple +mapping against the ``my_data`` table:: + + from sqlalchemy import mapper + + class MyDataClass(object): + pass + + # associates mutation listeners with MyDataClass.data + mapper(MyDataClass, my_data) + +The ``MyDataClass.data`` member will now be notified of in place changes +to its value. + +There's no difference in usage when using declarative:: + + from sqlalchemy.ext.declarative import declarative_base + + Base = declarative_base() + + class MyDataClass(Base): + __tablename__ = 'my_data' + id = Column(Integer, primary_key=True) + data = Column(MutableDict.as_mutable(JSONEncodedDict)) + +Any in-place changes to the ``MyDataClass.data`` member +will flag the attribute as "dirty" on the parent object:: + + >>> from sqlalchemy.orm import Session + + >>> sess = Session() + >>> m1 = MyDataClass(data={'value1':'foo'}) + >>> sess.add(m1) + >>> sess.commit() + + >>> m1.data['value1'] = 'bar' + >>> assert m1 in sess.dirty + True + +The ``MutableDict`` can be associated with all future instances +of ``JSONEncodedDict`` in one step, using +:meth:`~.Mutable.associate_with`. This is similar to +:meth:`~.Mutable.as_mutable` except it will intercept all occurrences +of ``MutableDict`` in all mappings unconditionally, without +the need to declare it individually:: + + MutableDict.associate_with(JSONEncodedDict) + + class MyDataClass(Base): + __tablename__ = 'my_data' + id = Column(Integer, primary_key=True) + data = Column(JSONEncodedDict) + + +Supporting Pickling +-------------------- + +The key to the :mod:`sqlalchemy.ext.mutable` extension relies upon the +placement of a ``weakref.WeakKeyDictionary`` upon the value object, which +stores a mapping of parent mapped objects keyed to the attribute name under +which they are associated with this value. ``WeakKeyDictionary`` objects are +not picklable, due to the fact that they contain weakrefs and function +callbacks. In our case, this is a good thing, since if this dictionary were +picklable, it could lead to an excessively large pickle size for our value +objects that are pickled by themselves outside of the context of the parent. +The developer responsibility here is only to provide a ``__getstate__`` method +that excludes the :meth:`~MutableBase._parents` collection from the pickle +stream:: + + class MyMutableType(Mutable): + def __getstate__(self): + d = self.__dict__.copy() + d.pop('_parents', None) + return d + +With our dictionary example, we need to return the contents of the dict itself +(and also restore them on __setstate__):: + + class MutableDict(Mutable, dict): + # .... + + def __getstate__(self): + return dict(self) + + def __setstate__(self, state): + self.update(state) + +In the case that our mutable value object is pickled as it is attached to one +or more parent objects that are also part of the pickle, the :class:`.Mutable` +mixin will re-establish the :attr:`.Mutable._parents` collection on each value +object as the owning parents themselves are unpickled. + +Receiving Events +---------------- + +The :meth:`.AttributeEvents.modified` event handler may be used to receive +an event when a mutable scalar emits a change event. This event handler +is called when the :func:`.attributes.flag_modified` function is called +from within the mutable extension:: + + from sqlalchemy.ext.declarative import declarative_base + from sqlalchemy import event + + Base = declarative_base() + + class MyDataClass(Base): + __tablename__ = 'my_data' + id = Column(Integer, primary_key=True) + data = Column(MutableDict.as_mutable(JSONEncodedDict)) + + @event.listens_for(MyDataClass.data, "modified") + def modified_json(instance): + print("json value modified:", instance.data) + +.. _mutable_composites: + +Establishing Mutability on Composites +===================================== + +Composites are a special ORM feature which allow a single scalar attribute to +be assigned an object value which represents information "composed" from one +or more columns from the underlying mapped table. The usual example is that of +a geometric "point", and is introduced in :ref:`mapper_composite`. + +.. versionchanged:: 0.7 + The internals of :func:`.orm.composite` have been + greatly simplified and in-place mutation detection is no longer enabled by + default; instead, the user-defined value must detect changes on its own and + propagate them to all owning parents. The :mod:`sqlalchemy.ext.mutable` + extension provides the helper class :class:`.MutableComposite`, which is a + slight variant on the :class:`.Mutable` class. + +As is the case with :class:`.Mutable`, the user-defined composite class +subclasses :class:`.MutableComposite` as a mixin, and detects and delivers +change events to its parents via the :meth:`.MutableComposite.changed` method. +In the case of a composite class, the detection is usually via the usage of +Python descriptors (i.e. ``@property``), or alternatively via the special +Python method ``__setattr__()``. Below we expand upon the ``Point`` class +introduced in :ref:`mapper_composite` to subclass :class:`.MutableComposite` +and to also route attribute set events via ``__setattr__`` to the +:meth:`.MutableComposite.changed` method:: + + from sqlalchemy.ext.mutable import MutableComposite + + class Point(MutableComposite): + def __init__(self, x, y): + self.x = x + self.y = y + + def __setattr__(self, key, value): + "Intercept set events" + + # set the attribute + object.__setattr__(self, key, value) + + # alert all parents to the change + self.changed() + + def __composite_values__(self): + return self.x, self.y + + def __eq__(self, other): + return isinstance(other, Point) and \ + other.x == self.x and \ + other.y == self.y + + def __ne__(self, other): + return not self.__eq__(other) + +The :class:`.MutableComposite` class uses a Python metaclass to automatically +establish listeners for any usage of :func:`.orm.composite` that specifies our +``Point`` type. Below, when ``Point`` is mapped to the ``Vertex`` class, +listeners are established which will route change events from ``Point`` +objects to each of the ``Vertex.start`` and ``Vertex.end`` attributes:: + + from sqlalchemy.orm import composite, mapper + from sqlalchemy import Table, Column + + vertices = Table('vertices', metadata, + Column('id', Integer, primary_key=True), + Column('x1', Integer), + Column('y1', Integer), + Column('x2', Integer), + Column('y2', Integer), + ) + + class Vertex(object): + pass + + mapper(Vertex, vertices, properties={ + 'start': composite(Point, vertices.c.x1, vertices.c.y1), + 'end': composite(Point, vertices.c.x2, vertices.c.y2) + }) + +Any in-place changes to the ``Vertex.start`` or ``Vertex.end`` members +will flag the attribute as "dirty" on the parent object:: + + >>> from sqlalchemy.orm import Session + + >>> sess = Session() + >>> v1 = Vertex(start=Point(3, 4), end=Point(12, 15)) + >>> sess.add(v1) + >>> sess.commit() + + >>> v1.end.x = 8 + >>> assert v1 in sess.dirty + True + +Coercing Mutable Composites +--------------------------- + +The :meth:`.MutableBase.coerce` method is also supported on composite types. +In the case of :class:`.MutableComposite`, the :meth:`.MutableBase.coerce` +method is only called for attribute set operations, not load operations. +Overriding the :meth:`.MutableBase.coerce` method is essentially equivalent +to using a :func:`.validates` validation routine for all attributes which +make use of the custom composite type:: + + class Point(MutableComposite): + # other Point methods + # ... + + def coerce(cls, key, value): + if isinstance(value, tuple): + value = Point(*value) + elif not isinstance(value, Point): + raise ValueError("tuple or Point expected") + return value + +.. versionadded:: 0.7.10,0.8.0b2 + Support for the :meth:`.MutableBase.coerce` method in conjunction with + objects of type :class:`.MutableComposite`. + +Supporting Pickling +-------------------- + +As is the case with :class:`.Mutable`, the :class:`.MutableComposite` helper +class uses a ``weakref.WeakKeyDictionary`` available via the +:meth:`MutableBase._parents` attribute which isn't picklable. If we need to +pickle instances of ``Point`` or its owning class ``Vertex``, we at least need +to define a ``__getstate__`` that doesn't include the ``_parents`` dictionary. +Below we define both a ``__getstate__`` and a ``__setstate__`` that package up +the minimal form of our ``Point`` class:: + + class Point(MutableComposite): + # ... + + def __getstate__(self): + return self.x, self.y + + def __setstate__(self, state): + self.x, self.y = state + +As with :class:`.Mutable`, the :class:`.MutableComposite` augments the +pickling process of the parent's object-relational state so that the +:meth:`MutableBase._parents` collection is restored to all ``Point`` objects. + +""" +from ..orm.attributes import flag_modified +from .. import event, types +from ..orm import mapper, object_mapper, Mapper +from ..util import memoized_property +from ..sql.base import SchemaEventTarget +import weakref + + +class MutableBase(object): + """Common base class to :class:`.Mutable` + and :class:`.MutableComposite`. + + """ + + @memoized_property + def _parents(self): + """Dictionary of parent object->attribute name on the parent. + + This attribute is a so-called "memoized" property. It initializes + itself with a new ``weakref.WeakKeyDictionary`` the first time + it is accessed, returning the same object upon subsequent access. + + """ + + return weakref.WeakKeyDictionary() + + @classmethod + def coerce(cls, key, value): + """Given a value, coerce it into the target type. + + Can be overridden by custom subclasses to coerce incoming + data into a particular type. + + By default, raises ``ValueError``. + + This method is called in different scenarios depending on if + the parent class is of type :class:`.Mutable` or of type + :class:`.MutableComposite`. In the case of the former, it is called + for both attribute-set operations as well as during ORM loading + operations. For the latter, it is only called during attribute-set + operations; the mechanics of the :func:`.composite` construct + handle coercion during load operations. + + + :param key: string name of the ORM-mapped attribute being set. + :param value: the incoming value. + :return: the method should return the coerced value, or raise + ``ValueError`` if the coercion cannot be completed. + + """ + if value is None: + return None + msg = "Attribute '%s' does not accept objects of type %s" + raise ValueError(msg % (key, type(value))) + + @classmethod + def _get_listen_keys(cls, attribute): + """Given a descriptor attribute, return a ``set()`` of the attribute + keys which indicate a change in the state of this attribute. + + This is normally just ``set([attribute.key])``, but can be overridden + to provide for additional keys. E.g. a :class:`.MutableComposite` + augments this set with the attribute keys associated with the columns + that comprise the composite value. + + This collection is consulted in the case of intercepting the + :meth:`.InstanceEvents.refresh` and + :meth:`.InstanceEvents.refresh_flush` events, which pass along a list + of attribute names that have been refreshed; the list is compared + against this set to determine if action needs to be taken. + + .. versionadded:: 1.0.5 + + """ + return {attribute.key} + + @classmethod + def _listen_on_attribute(cls, attribute, coerce, parent_cls): + """Establish this type as a mutation listener for the given + mapped descriptor. + + """ + key = attribute.key + if parent_cls is not attribute.class_: + return + + # rely on "propagate" here + parent_cls = attribute.class_ + + listen_keys = cls._get_listen_keys(attribute) + + def load(state, *args): + """Listen for objects loaded or refreshed. + + Wrap the target data member's value with + ``Mutable``. + + """ + val = state.dict.get(key, None) + if val is not None: + if coerce: + val = cls.coerce(key, val) + state.dict[key] = val + val._parents[state.obj()] = key + + def load_attrs(state, ctx, attrs): + if not attrs or listen_keys.intersection(attrs): + load(state) + + def set(target, value, oldvalue, initiator): + """Listen for set/replace events on the target + data member. + + Establish a weak reference to the parent object + on the incoming value, remove it for the one + outgoing. + + """ + if value is oldvalue: + return value + + if not isinstance(value, cls): + value = cls.coerce(key, value) + if value is not None: + value._parents[target.obj()] = key + if isinstance(oldvalue, cls): + oldvalue._parents.pop(target.obj(), None) + return value + + def pickle(state, state_dict): + val = state.dict.get(key, None) + if val is not None: + if 'ext.mutable.values' not in state_dict: + state_dict['ext.mutable.values'] = [] + state_dict['ext.mutable.values'].append(val) + + def unpickle(state, state_dict): + if 'ext.mutable.values' in state_dict: + for val in state_dict['ext.mutable.values']: + val._parents[state.obj()] = key + + event.listen(parent_cls, 'load', load, + raw=True, propagate=True) + event.listen(parent_cls, 'refresh', load_attrs, + raw=True, propagate=True) + event.listen(parent_cls, 'refresh_flush', load_attrs, + raw=True, propagate=True) + event.listen(attribute, 'set', set, + raw=True, retval=True, propagate=True) + event.listen(parent_cls, 'pickle', pickle, + raw=True, propagate=True) + event.listen(parent_cls, 'unpickle', unpickle, + raw=True, propagate=True) + + +class Mutable(MutableBase): + """Mixin that defines transparent propagation of change + events to a parent object. + + See the example in :ref:`mutable_scalars` for usage information. + + """ + + def changed(self): + """Subclasses should call this method whenever change events occur.""" + + for parent, key in self._parents.items(): + flag_modified(parent, key) + + @classmethod + def associate_with_attribute(cls, attribute): + """Establish this type as a mutation listener for the given + mapped descriptor. + + """ + cls._listen_on_attribute(attribute, True, attribute.class_) + + @classmethod + def associate_with(cls, sqltype): + """Associate this wrapper with all future mapped columns + of the given type. + + This is a convenience method that calls + ``associate_with_attribute`` automatically. + + .. warning:: + + The listeners established by this method are *global* + to all mappers, and are *not* garbage collected. Only use + :meth:`.associate_with` for types that are permanent to an + application, not with ad-hoc types else this will cause unbounded + growth in memory usage. + + """ + + def listen_for_type(mapper, class_): + if mapper.non_primary: + return + for prop in mapper.column_attrs: + if isinstance(prop.columns[0].type, sqltype): + cls.associate_with_attribute(getattr(class_, prop.key)) + + event.listen(mapper, 'mapper_configured', listen_for_type) + + @classmethod + def as_mutable(cls, sqltype): + """Associate a SQL type with this mutable Python type. + + This establishes listeners that will detect ORM mappings against + the given type, adding mutation event trackers to those mappings. + + The type is returned, unconditionally as an instance, so that + :meth:`.as_mutable` can be used inline:: + + Table('mytable', metadata, + Column('id', Integer, primary_key=True), + Column('data', MyMutableType.as_mutable(PickleType)) + ) + + Note that the returned type is always an instance, even if a class + is given, and that only columns which are declared specifically with + that type instance receive additional instrumentation. + + To associate a particular mutable type with all occurrences of a + particular type, use the :meth:`.Mutable.associate_with` classmethod + of the particular :class:`.Mutable` subclass to establish a global + association. + + .. warning:: + + The listeners established by this method are *global* + to all mappers, and are *not* garbage collected. Only use + :meth:`.as_mutable` for types that are permanent to an application, + not with ad-hoc types else this will cause unbounded growth + in memory usage. + + """ + sqltype = types.to_instance(sqltype) + + # a SchemaType will be copied when the Column is copied, + # and we'll lose our ability to link that type back to the original. + # so track our original type w/ columns + if isinstance(sqltype, SchemaEventTarget): + @event.listens_for(sqltype, "before_parent_attach") + def _add_column_memo(sqltyp, parent): + parent.info['_ext_mutable_orig_type'] = sqltyp + schema_event_check = True + else: + schema_event_check = False + + def listen_for_type(mapper, class_): + if mapper.non_primary: + return + for prop in mapper.column_attrs: + if ( + schema_event_check and + hasattr(prop.expression, 'info') and + prop.expression.info.get('_ext_mutable_orig_type') + is sqltype + ) or ( + prop.columns[0].type is sqltype + ): + cls.associate_with_attribute(getattr(class_, prop.key)) + + event.listen(mapper, 'mapper_configured', listen_for_type) + + return sqltype + + +class MutableComposite(MutableBase): + """Mixin that defines transparent propagation of change + events on a SQLAlchemy "composite" object to its + owning parent or parents. + + See the example in :ref:`mutable_composites` for usage information. + + """ + + @classmethod + def _get_listen_keys(cls, attribute): + return {attribute.key}.union(attribute.property._attribute_keys) + + def changed(self): + """Subclasses should call this method whenever change events occur.""" + + for parent, key in self._parents.items(): + + prop = object_mapper(parent).get_property(key) + for value, attr_name in zip( + self.__composite_values__(), + prop._attribute_keys): + setattr(parent, attr_name, value) + + +def _setup_composite_listener(): + def _listen_for_type(mapper, class_): + for prop in mapper.iterate_properties: + if (hasattr(prop, 'composite_class') and + isinstance(prop.composite_class, type) and + issubclass(prop.composite_class, MutableComposite)): + prop.composite_class._listen_on_attribute( + getattr(class_, prop.key), False, class_) + if not event.contains(Mapper, "mapper_configured", _listen_for_type): + event.listen(Mapper, 'mapper_configured', _listen_for_type) +_setup_composite_listener() + + +class MutableDict(Mutable, dict): + """A dictionary type that implements :class:`.Mutable`. + + The :class:`.MutableDict` object implements a dictionary that will + emit change events to the underlying mapping when the contents of + the dictionary are altered, including when values are added or removed. + + Note that :class:`.MutableDict` does **not** apply mutable tracking to the + *values themselves* inside the dictionary. Therefore it is not a sufficient + solution for the use case of tracking deep changes to a *recursive* + dictionary structure, such as a JSON structure. To support this use case, + build a subclass of :class:`.MutableDict` that provides appropriate + coersion to the values placed in the dictionary so that they too are + "mutable", and emit events up to their parent structure. + + .. versionadded:: 0.8 + + .. seealso:: + + :class:`.MutableList` + + :class:`.MutableSet` + + """ + + def __setitem__(self, key, value): + """Detect dictionary set events and emit change events.""" + dict.__setitem__(self, key, value) + self.changed() + + def setdefault(self, key, value): + result = dict.setdefault(self, key, value) + self.changed() + return result + + def __delitem__(self, key): + """Detect dictionary del events and emit change events.""" + dict.__delitem__(self, key) + self.changed() + + def update(self, *a, **kw): + dict.update(self, *a, **kw) + self.changed() + + def pop(self, *arg): + result = dict.pop(self, *arg) + self.changed() + return result + + def popitem(self): + result = dict.popitem(self) + self.changed() + return result + + def clear(self): + dict.clear(self) + self.changed() + + @classmethod + def coerce(cls, key, value): + """Convert plain dictionary to instance of this class.""" + if not isinstance(value, cls): + if isinstance(value, dict): + return cls(value) + return Mutable.coerce(key, value) + else: + return value + + def __getstate__(self): + return dict(self) + + def __setstate__(self, state): + self.update(state) + + +class MutableList(Mutable, list): + """A list type that implements :class:`.Mutable`. + + The :class:`.MutableList` object implements a list that will + emit change events to the underlying mapping when the contents of + the list are altered, including when values are added or removed. + + Note that :class:`.MutableList` does **not** apply mutable tracking to the + *values themselves* inside the list. Therefore it is not a sufficient + solution for the use case of tracking deep changes to a *recursive* + mutable structure, such as a JSON structure. To support this use case, + build a subclass of :class:`.MutableList` that provides appropriate + coersion to the values placed in the dictionary so that they too are + "mutable", and emit events up to their parent structure. + + .. versionadded:: 1.1 + + .. seealso:: + + :class:`.MutableDict` + + :class:`.MutableSet` + + """ + + def __setitem__(self, index, value): + """Detect list set events and emit change events.""" + list.__setitem__(self, index, value) + self.changed() + + def __setslice__(self, start, end, value): + """Detect list set events and emit change events.""" + list.__setslice__(self, start, end, value) + self.changed() + + def __delitem__(self, index): + """Detect list del events and emit change events.""" + list.__delitem__(self, index) + self.changed() + + def __delslice__(self, start, end): + """Detect list del events and emit change events.""" + list.__delslice__(self, start, end) + self.changed() + + def pop(self, *arg): + result = list.pop(self, *arg) + self.changed() + return result + + def append(self, x): + list.append(self, x) + self.changed() + + def extend(self, x): + list.extend(self, x) + self.changed() + + def __iadd__(self, x): + self.extend(x) + return self + + def insert(self, i, x): + list.insert(self, i, x) + self.changed() + + def remove(self, i): + list.remove(self, i) + self.changed() + + def clear(self): + list.clear(self) + self.changed() + + def sort(self): + list.sort(self) + self.changed() + + def reverse(self): + list.reverse(self) + self.changed() + + @classmethod + def coerce(cls, index, value): + """Convert plain list to instance of this class.""" + if not isinstance(value, cls): + if isinstance(value, list): + return cls(value) + return Mutable.coerce(index, value) + else: + return value + + def __getstate__(self): + return list(self) + + def __setstate__(self, state): + self[:] = state + + +class MutableSet(Mutable, set): + """A set type that implements :class:`.Mutable`. + + The :class:`.MutableSet` object implements a set that will + emit change events to the underlying mapping when the contents of + the set are altered, including when values are added or removed. + + Note that :class:`.MutableSet` does **not** apply mutable tracking to the + *values themselves* inside the set. Therefore it is not a sufficient + solution for the use case of tracking deep changes to a *recursive* + mutable structure. To support this use case, + build a subclass of :class:`.MutableSet` that provides appropriate + coersion to the values placed in the dictionary so that they too are + "mutable", and emit events up to their parent structure. + + .. versionadded:: 1.1 + + .. seealso:: + + :class:`.MutableDict` + + :class:`.MutableList` + + + """ + + def update(self, *arg): + set.update(self, *arg) + self.changed() + + def intersection_update(self, *arg): + set.intersection_update(self, *arg) + self.changed() + + def difference_update(self, *arg): + set.difference_update(self, *arg) + self.changed() + + def symmetric_difference_update(self, *arg): + set.symmetric_difference_update(self, *arg) + self.changed() + + def __ior__(self, other): + self.update(other) + return self + + def __iand__(self, other): + self.intersection_update(other) + return self + + def __ixor__(self, other): + self.symmetric_difference_update(other) + return self + + def __isub__(self, other): + self.difference_update(other) + return self + + def add(self, elem): + set.add(self, elem) + self.changed() + + def remove(self, elem): + set.remove(self, elem) + self.changed() + + def discard(self, elem): + set.discard(self, elem) + self.changed() + + def pop(self, *arg): + result = set.pop(self, *arg) + self.changed() + return result + + def clear(self): + set.clear(self) + self.changed() + + @classmethod + def coerce(cls, index, value): + """Convert plain set to instance of this class.""" + if not isinstance(value, cls): + if isinstance(value, set): + return cls(value) + return Mutable.coerce(index, value) + else: + return value + + def __getstate__(self): + return set(self) + + def __setstate__(self, state): + self.update(state) + + def __reduce_ex__(self, proto): + return (self.__class__, (list(self), )) diff --git a/venv/Lib/site-packages/sqlalchemy/ext/orderinglist.py b/venv/Lib/site-packages/sqlalchemy/ext/orderinglist.py new file mode 100644 index 0000000..316742a --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/orderinglist.py @@ -0,0 +1,380 @@ +# ext/orderinglist.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""A custom list that manages index/position information for contained +elements. + +:author: Jason Kirtland + +``orderinglist`` is a helper for mutable ordered relationships. It will +intercept list operations performed on a :func:`.relationship`-managed +collection and +automatically synchronize changes in list position onto a target scalar +attribute. + +Example: A ``slide`` table, where each row refers to zero or more entries +in a related ``bullet`` table. The bullets within a slide are +displayed in order based on the value of the ``position`` column in the +``bullet`` table. As entries are reordered in memory, the value of the +``position`` attribute should be updated to reflect the new sort order:: + + + Base = declarative_base() + + class Slide(Base): + __tablename__ = 'slide' + + id = Column(Integer, primary_key=True) + name = Column(String) + + bullets = relationship("Bullet", order_by="Bullet.position") + + class Bullet(Base): + __tablename__ = 'bullet' + id = Column(Integer, primary_key=True) + slide_id = Column(Integer, ForeignKey('slide.id')) + position = Column(Integer) + text = Column(String) + +The standard relationship mapping will produce a list-like attribute on each +``Slide`` containing all related ``Bullet`` objects, +but coping with changes in ordering is not handled automatically. +When appending a ``Bullet`` into ``Slide.bullets``, the ``Bullet.position`` +attribute will remain unset until manually assigned. When the ``Bullet`` +is inserted into the middle of the list, the following ``Bullet`` objects +will also need to be renumbered. + +The :class:`.OrderingList` object automates this task, managing the +``position`` attribute on all ``Bullet`` objects in the collection. It is +constructed using the :func:`.ordering_list` factory:: + + from sqlalchemy.ext.orderinglist import ordering_list + + Base = declarative_base() + + class Slide(Base): + __tablename__ = 'slide' + + id = Column(Integer, primary_key=True) + name = Column(String) + + bullets = relationship("Bullet", order_by="Bullet.position", + collection_class=ordering_list('position')) + + class Bullet(Base): + __tablename__ = 'bullet' + id = Column(Integer, primary_key=True) + slide_id = Column(Integer, ForeignKey('slide.id')) + position = Column(Integer) + text = Column(String) + +With the above mapping the ``Bullet.position`` attribute is managed:: + + s = Slide() + s.bullets.append(Bullet()) + s.bullets.append(Bullet()) + s.bullets[1].position + >>> 1 + s.bullets.insert(1, Bullet()) + s.bullets[2].position + >>> 2 + +The :class:`.OrderingList` construct only works with **changes** to a +collection, and not the initial load from the database, and requires that the +list be sorted when loaded. Therefore, be sure to specify ``order_by`` on the +:func:`.relationship` against the target ordering attribute, so that the +ordering is correct when first loaded. + +.. warning:: + + :class:`.OrderingList` only provides limited functionality when a primary + key column or unique column is the target of the sort. Operations + that are unsupported or are problematic include: + + * two entries must trade values. This is not supported directly in the + case of a primary key or unique constraint because it means at least + one row would need to be temporarily removed first, or changed to + a third, neutral value while the switch occurs. + + * an entry must be deleted in order to make room for a new entry. + SQLAlchemy's unit of work performs all INSERTs before DELETEs within a + single flush. In the case of a primary key, it will trade + an INSERT/DELETE of the same primary key for an UPDATE statement in order + to lessen the impact of this limitation, however this does not take place + for a UNIQUE column. + A future feature will allow the "DELETE before INSERT" behavior to be + possible, allevating this limitation, though this feature will require + explicit configuration at the mapper level for sets of columns that + are to be handled in this way. + +:func:`.ordering_list` takes the name of the related object's ordering +attribute as an argument. By default, the zero-based integer index of the +object's position in the :func:`.ordering_list` is synchronized with the +ordering attribute: index 0 will get position 0, index 1 position 1, etc. To +start numbering at 1 or some other integer, provide ``count_from=1``. + + +""" +from ..orm.collections import collection, collection_adapter +from .. import util + +__all__ = ['ordering_list'] + + +def ordering_list(attr, count_from=None, **kw): + """Prepares an :class:`OrderingList` factory for use in mapper definitions. + + Returns an object suitable for use as an argument to a Mapper + relationship's ``collection_class`` option. e.g.:: + + from sqlalchemy.ext.orderinglist import ordering_list + + class Slide(Base): + __tablename__ = 'slide' + + id = Column(Integer, primary_key=True) + name = Column(String) + + bullets = relationship("Bullet", order_by="Bullet.position", + collection_class=ordering_list('position')) + + :param attr: + Name of the mapped attribute to use for storage and retrieval of + ordering information + + :param count_from: + Set up an integer-based ordering, starting at ``count_from``. For + example, ``ordering_list('pos', count_from=1)`` would create a 1-based + list in SQL, storing the value in the 'pos' column. Ignored if + ``ordering_func`` is supplied. + + Additional arguments are passed to the :class:`.OrderingList` constructor. + + """ + + kw = _unsugar_count_from(count_from=count_from, **kw) + return lambda: OrderingList(attr, **kw) + + +# Ordering utility functions + + +def count_from_0(index, collection): + """Numbering function: consecutive integers starting at 0.""" + + return index + + +def count_from_1(index, collection): + """Numbering function: consecutive integers starting at 1.""" + + return index + 1 + + +def count_from_n_factory(start): + """Numbering function: consecutive integers starting at arbitrary start.""" + + def f(index, collection): + return index + start + try: + f.__name__ = 'count_from_%i' % start + except TypeError: + pass + return f + + +def _unsugar_count_from(**kw): + """Builds counting functions from keyword arguments. + + Keyword argument filter, prepares a simple ``ordering_func`` from a + ``count_from`` argument, otherwise passes ``ordering_func`` on unchanged. + """ + + count_from = kw.pop('count_from', None) + if kw.get('ordering_func', None) is None and count_from is not None: + if count_from == 0: + kw['ordering_func'] = count_from_0 + elif count_from == 1: + kw['ordering_func'] = count_from_1 + else: + kw['ordering_func'] = count_from_n_factory(count_from) + return kw + + +class OrderingList(list): + """A custom list that manages position information for its children. + + The :class:`.OrderingList` object is normally set up using the + :func:`.ordering_list` factory function, used in conjunction with + the :func:`.relationship` function. + + """ + + def __init__(self, ordering_attr=None, ordering_func=None, + reorder_on_append=False): + """A custom list that manages position information for its children. + + ``OrderingList`` is a ``collection_class`` list implementation that + syncs position in a Python list with a position attribute on the + mapped objects. + + This implementation relies on the list starting in the proper order, + so be **sure** to put an ``order_by`` on your relationship. + + :param ordering_attr: + Name of the attribute that stores the object's order in the + relationship. + + :param ordering_func: Optional. A function that maps the position in + the Python list to a value to store in the + ``ordering_attr``. Values returned are usually (but need not be!) + integers. + + An ``ordering_func`` is called with two positional parameters: the + index of the element in the list, and the list itself. + + If omitted, Python list indexes are used for the attribute values. + Two basic pre-built numbering functions are provided in this module: + ``count_from_0`` and ``count_from_1``. For more exotic examples + like stepped numbering, alphabetical and Fibonacci numbering, see + the unit tests. + + :param reorder_on_append: + Default False. When appending an object with an existing (non-None) + ordering value, that value will be left untouched unless + ``reorder_on_append`` is true. This is an optimization to avoid a + variety of dangerous unexpected database writes. + + SQLAlchemy will add instances to the list via append() when your + object loads. If for some reason the result set from the database + skips a step in the ordering (say, row '1' is missing but you get + '2', '3', and '4'), reorder_on_append=True would immediately + renumber the items to '1', '2', '3'. If you have multiple sessions + making changes, any of whom happen to load this collection even in + passing, all of the sessions would try to "clean up" the numbering + in their commits, possibly causing all but one to fail with a + concurrent modification error. + + Recommend leaving this with the default of False, and just call + ``reorder()`` if you're doing ``append()`` operations with + previously ordered instances or when doing some housekeeping after + manual sql operations. + + """ + self.ordering_attr = ordering_attr + if ordering_func is None: + ordering_func = count_from_0 + self.ordering_func = ordering_func + self.reorder_on_append = reorder_on_append + + # More complex serialization schemes (multi column, e.g.) are possible by + # subclassing and reimplementing these two methods. + def _get_order_value(self, entity): + return getattr(entity, self.ordering_attr) + + def _set_order_value(self, entity, value): + setattr(entity, self.ordering_attr, value) + + def reorder(self): + """Synchronize ordering for the entire collection. + + Sweeps through the list and ensures that each object has accurate + ordering information set. + + """ + for index, entity in enumerate(self): + self._order_entity(index, entity, True) + + # As of 0.5, _reorder is no longer semi-private + _reorder = reorder + + def _order_entity(self, index, entity, reorder=True): + have = self._get_order_value(entity) + + # Don't disturb existing ordering if reorder is False + if have is not None and not reorder: + return + + should_be = self.ordering_func(index, self) + if have != should_be: + self._set_order_value(entity, should_be) + + def append(self, entity): + super(OrderingList, self).append(entity) + self._order_entity(len(self) - 1, entity, self.reorder_on_append) + + def _raw_append(self, entity): + """Append without any ordering behavior.""" + + super(OrderingList, self).append(entity) + _raw_append = collection.adds(1)(_raw_append) + + def insert(self, index, entity): + super(OrderingList, self).insert(index, entity) + self._reorder() + + def remove(self, entity): + super(OrderingList, self).remove(entity) + + adapter = collection_adapter(self) + if adapter and adapter._referenced_by_owner: + self._reorder() + + def pop(self, index=-1): + entity = super(OrderingList, self).pop(index) + self._reorder() + return entity + + def __setitem__(self, index, entity): + if isinstance(index, slice): + step = index.step or 1 + start = index.start or 0 + if start < 0: + start += len(self) + stop = index.stop or len(self) + if stop < 0: + stop += len(self) + + for i in range(start, stop, step): + self.__setitem__(i, entity[i]) + else: + self._order_entity(index, entity, True) + super(OrderingList, self).__setitem__(index, entity) + + def __delitem__(self, index): + super(OrderingList, self).__delitem__(index) + self._reorder() + + def __setslice__(self, start, end, values): + super(OrderingList, self).__setslice__(start, end, values) + self._reorder() + + def __delslice__(self, start, end): + super(OrderingList, self).__delslice__(start, end) + self._reorder() + + def __reduce__(self): + return _reconstitute, (self.__class__, self.__dict__, list(self)) + + for func_name, func in list(locals().items()): + if (util.callable(func) and func.__name__ == func_name and + not func.__doc__ and hasattr(list, func_name)): + func.__doc__ = getattr(list, func_name).__doc__ + del func_name, func + + +def _reconstitute(cls, dict_, items): + """ Reconstitute an :class:`.OrderingList`. + + This is the adjoint to :meth:`.OrderingList.__reduce__`. It is used for + unpickling :class:`.OrderingList` objects. + + """ + obj = cls.__new__(cls) + obj.__dict__.update(dict_) + list.extend(obj, items) + return obj diff --git a/venv/Lib/site-packages/sqlalchemy/ext/serializer.py b/venv/Lib/site-packages/sqlalchemy/ext/serializer.py new file mode 100644 index 0000000..2fded51 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/ext/serializer.py @@ -0,0 +1,159 @@ +# ext/serializer.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Serializer/Deserializer objects for usage with SQLAlchemy query structures, +allowing "contextual" deserialization. + +Any SQLAlchemy query structure, either based on sqlalchemy.sql.* +or sqlalchemy.orm.* can be used. The mappers, Tables, Columns, Session +etc. which are referenced by the structure are not persisted in serialized +form, but are instead re-associated with the query structure +when it is deserialized. + +Usage is nearly the same as that of the standard Python pickle module:: + + from sqlalchemy.ext.serializer import loads, dumps + metadata = MetaData(bind=some_engine) + Session = scoped_session(sessionmaker()) + + # ... define mappers + + query = Session.query(MyClass). + filter(MyClass.somedata=='foo').order_by(MyClass.sortkey) + + # pickle the query + serialized = dumps(query) + + # unpickle. Pass in metadata + scoped_session + query2 = loads(serialized, metadata, Session) + + print query2.all() + +Similar restrictions as when using raw pickle apply; mapped classes must be +themselves be pickleable, meaning they are importable from a module-level +namespace. + +The serializer module is only appropriate for query structures. It is not +needed for: + +* instances of user-defined classes. These contain no references to engines, + sessions or expression constructs in the typical case and can be serialized + directly. + +* Table metadata that is to be loaded entirely from the serialized structure + (i.e. is not already declared in the application). Regular + pickle.loads()/dumps() can be used to fully dump any ``MetaData`` object, + typically one which was reflected from an existing database at some previous + point in time. The serializer module is specifically for the opposite case, + where the Table metadata is already present in memory. + +""" + +from ..orm import class_mapper +from ..orm.session import Session +from ..orm.mapper import Mapper +from ..orm.interfaces import MapperProperty +from ..orm.attributes import QueryableAttribute +from .. import Table, Column +from ..engine import Engine +from ..util import pickle, byte_buffer, b64encode, b64decode, text_type +import re + + +__all__ = ['Serializer', 'Deserializer', 'dumps', 'loads'] + + +def Serializer(*args, **kw): + pickler = pickle.Pickler(*args, **kw) + + def persistent_id(obj): + # print "serializing:", repr(obj) + if isinstance(obj, QueryableAttribute): + cls = obj.impl.class_ + key = obj.impl.key + id = "attribute:" + key + ":" + b64encode(pickle.dumps(cls)) + elif isinstance(obj, Mapper) and not obj.non_primary: + id = "mapper:" + b64encode(pickle.dumps(obj.class_)) + elif isinstance(obj, MapperProperty) and not obj.parent.non_primary: + id = "mapperprop:" + b64encode(pickle.dumps(obj.parent.class_)) + \ + ":" + obj.key + elif isinstance(obj, Table): + id = "table:" + text_type(obj.key) + elif isinstance(obj, Column) and isinstance(obj.table, Table): + id = "column:" + \ + text_type(obj.table.key) + ":" + text_type(obj.key) + elif isinstance(obj, Session): + id = "session:" + elif isinstance(obj, Engine): + id = "engine:" + else: + return None + return id + + pickler.persistent_id = persistent_id + return pickler + +our_ids = re.compile( + r'(mapperprop|mapper|table|column|session|attribute|engine):(.*)') + + +def Deserializer(file, metadata=None, scoped_session=None, engine=None): + unpickler = pickle.Unpickler(file) + + def get_engine(): + if engine: + return engine + elif scoped_session and scoped_session().bind: + return scoped_session().bind + elif metadata and metadata.bind: + return metadata.bind + else: + return None + + def persistent_load(id): + m = our_ids.match(text_type(id)) + if not m: + return None + else: + type_, args = m.group(1, 2) + if type_ == 'attribute': + key, clsarg = args.split(":") + cls = pickle.loads(b64decode(clsarg)) + return getattr(cls, key) + elif type_ == "mapper": + cls = pickle.loads(b64decode(args)) + return class_mapper(cls) + elif type_ == "mapperprop": + mapper, keyname = args.split(':') + cls = pickle.loads(b64decode(mapper)) + return class_mapper(cls).attrs[keyname] + elif type_ == "table": + return metadata.tables[args] + elif type_ == "column": + table, colname = args.split(':') + return metadata.tables[table].c[colname] + elif type_ == "session": + return scoped_session() + elif type_ == "engine": + return get_engine() + else: + raise Exception("Unknown token: %s" % type_) + unpickler.persistent_load = persistent_load + return unpickler + + +def dumps(obj, protocol=pickle.HIGHEST_PROTOCOL): + buf = byte_buffer() + pickler = Serializer(buf, protocol) + pickler.dump(obj) + return buf.getvalue() + + +def loads(data, metadata=None, scoped_session=None, engine=None): + buf = byte_buffer(data) + unpickler = Deserializer(buf, metadata, scoped_session, engine) + return unpickler.load() diff --git a/venv/Lib/site-packages/sqlalchemy/inspection.py b/venv/Lib/site-packages/sqlalchemy/inspection.py new file mode 100644 index 0000000..3a03e25 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/inspection.py @@ -0,0 +1,93 @@ +# sqlalchemy/inspect.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""The inspection module provides the :func:`.inspect` function, +which delivers runtime information about a wide variety +of SQLAlchemy objects, both within the Core as well as the +ORM. + +The :func:`.inspect` function is the entry point to SQLAlchemy's +public API for viewing the configuration and construction +of in-memory objects. Depending on the type of object +passed to :func:`.inspect`, the return value will either be +a related object which provides a known interface, or in many +cases it will return the object itself. + +The rationale for :func:`.inspect` is twofold. One is that +it replaces the need to be aware of a large variety of "information +getting" functions in SQLAlchemy, such as :meth:`.Inspector.from_engine`, +:func:`.orm.attributes.instance_state`, :func:`.orm.class_mapper`, +and others. The other is that the return value of :func:`.inspect` +is guaranteed to obey a documented API, thus allowing third party +tools which build on top of SQLAlchemy configurations to be constructed +in a forwards-compatible way. + +.. versionadded:: 0.8 The :func:`.inspect` system is introduced + as of version 0.8. + +""" + +from . import util, exc +_registrars = util.defaultdict(list) + + +def inspect(subject, raiseerr=True): + """Produce an inspection object for the given target. + + The returned value in some cases may be the + same object as the one given, such as if a + :class:`.Mapper` object is passed. In other + cases, it will be an instance of the registered + inspection type for the given object, such as + if an :class:`.engine.Engine` is passed, an + :class:`.Inspector` object is returned. + + :param subject: the subject to be inspected. + :param raiseerr: When ``True``, if the given subject + does not + correspond to a known SQLAlchemy inspected type, + :class:`sqlalchemy.exc.NoInspectionAvailable` + is raised. If ``False``, ``None`` is returned. + + """ + type_ = type(subject) + for cls in type_.__mro__: + if cls in _registrars: + reg = _registrars[cls] + if reg is True: + return subject + ret = reg(subject) + if ret is not None: + break + else: + reg = ret = None + + if raiseerr and ( + reg is None or ret is None + ): + raise exc.NoInspectionAvailable( + "No inspection system is " + "available for object of type %s" % + type_) + return ret + + +def _inspects(*types): + def decorate(fn_or_cls): + for type_ in types: + if type_ in _registrars: + raise AssertionError( + "Type %s is already " + "registered" % type_) + _registrars[type_] = fn_or_cls + return fn_or_cls + return decorate + + +def _self_inspects(cls): + _inspects(cls)(True) + return cls diff --git a/venv/Lib/site-packages/sqlalchemy/interfaces.py b/venv/Lib/site-packages/sqlalchemy/interfaces.py new file mode 100644 index 0000000..30698ea --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/interfaces.py @@ -0,0 +1,312 @@ +# sqlalchemy/interfaces.py +# Copyright (C) 2007-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# Copyright (C) 2007 Jason Kirtland jek@discorporate.us +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Deprecated core event interfaces. + +This module is **deprecated** and is superseded by the +event system. + +""" + +from . import event, util + + +class PoolListener(object): + """Hooks into the lifecycle of connections in a :class:`.Pool`. + + .. note:: + + :class:`.PoolListener` is deprecated. Please + refer to :class:`.PoolEvents`. + + Usage:: + + class MyListener(PoolListener): + def connect(self, dbapi_con, con_record): + '''perform connect operations''' + # etc. + + # create a new pool with a listener + p = QueuePool(..., listeners=[MyListener()]) + + # add a listener after the fact + p.add_listener(MyListener()) + + # usage with create_engine() + e = create_engine("url://", listeners=[MyListener()]) + + All of the standard connection :class:`~sqlalchemy.pool.Pool` types can + accept event listeners for key connection lifecycle events: + creation, pool check-out and check-in. There are no events fired + when a connection closes. + + For any given DB-API connection, there will be one ``connect`` + event, `n` number of ``checkout`` events, and either `n` or `n - 1` + ``checkin`` events. (If a ``Connection`` is detached from its + pool via the ``detach()`` method, it won't be checked back in.) + + These are low-level events for low-level objects: raw Python + DB-API connections, without the conveniences of the SQLAlchemy + ``Connection`` wrapper, ``Dialect`` services or ``ClauseElement`` + execution. If you execute SQL through the connection, explicitly + closing all cursors and other resources is recommended. + + Events also receive a ``_ConnectionRecord``, a long-lived internal + ``Pool`` object that basically represents a "slot" in the + connection pool. ``_ConnectionRecord`` objects have one public + attribute of note: ``info``, a dictionary whose contents are + scoped to the lifetime of the DB-API connection managed by the + record. You can use this shared storage area however you like. + + There is no need to subclass ``PoolListener`` to handle events. + Any class that implements one or more of these methods can be used + as a pool listener. The ``Pool`` will inspect the methods + provided by a listener object and add the listener to one or more + internal event queues based on its capabilities. In terms of + efficiency and function call overhead, you're much better off only + providing implementations for the hooks you'll be using. + + """ + + @classmethod + def _adapt_listener(cls, self, listener): + """Adapt a :class:`.PoolListener` to individual + :class:`event.Dispatch` events. + + """ + + listener = util.as_interface(listener, + methods=('connect', 'first_connect', + 'checkout', 'checkin')) + if hasattr(listener, 'connect'): + event.listen(self, 'connect', listener.connect) + if hasattr(listener, 'first_connect'): + event.listen(self, 'first_connect', listener.first_connect) + if hasattr(listener, 'checkout'): + event.listen(self, 'checkout', listener.checkout) + if hasattr(listener, 'checkin'): + event.listen(self, 'checkin', listener.checkin) + + def connect(self, dbapi_con, con_record): + """Called once for each new DB-API connection or Pool's ``creator()``. + + dbapi_con + A newly connected raw DB-API connection (not a SQLAlchemy + ``Connection`` wrapper). + + con_record + The ``_ConnectionRecord`` that persistently manages the connection + + """ + + def first_connect(self, dbapi_con, con_record): + """Called exactly once for the first DB-API connection. + + dbapi_con + A newly connected raw DB-API connection (not a SQLAlchemy + ``Connection`` wrapper). + + con_record + The ``_ConnectionRecord`` that persistently manages the connection + + """ + + def checkout(self, dbapi_con, con_record, con_proxy): + """Called when a connection is retrieved from the Pool. + + dbapi_con + A raw DB-API connection + + con_record + The ``_ConnectionRecord`` that persistently manages the connection + + con_proxy + The ``_ConnectionFairy`` which manages the connection for the span of + the current checkout. + + If you raise an ``exc.DisconnectionError``, the current + connection will be disposed and a fresh connection retrieved. + Processing of all checkout listeners will abort and restart + using the new connection. + """ + + def checkin(self, dbapi_con, con_record): + """Called when a connection returns to the pool. + + Note that the connection may be closed, and may be None if the + connection has been invalidated. ``checkin`` will not be called + for detached connections. (They do not return to the pool.) + + dbapi_con + A raw DB-API connection + + con_record + The ``_ConnectionRecord`` that persistently manages the connection + + """ + + +class ConnectionProxy(object): + """Allows interception of statement execution by Connections. + + .. note:: + + :class:`.ConnectionProxy` is deprecated. Please + refer to :class:`.ConnectionEvents`. + + Either or both of the ``execute()`` and ``cursor_execute()`` + may be implemented to intercept compiled statement and + cursor level executions, e.g.:: + + class MyProxy(ConnectionProxy): + def execute(self, conn, execute, clauseelement, + *multiparams, **params): + print "compiled statement:", clauseelement + return execute(clauseelement, *multiparams, **params) + + def cursor_execute(self, execute, cursor, statement, + parameters, context, executemany): + print "raw statement:", statement + return execute(cursor, statement, parameters, context) + + The ``execute`` argument is a function that will fulfill the default + execution behavior for the operation. The signature illustrated + in the example should be used. + + The proxy is installed into an :class:`~sqlalchemy.engine.Engine` via + the ``proxy`` argument:: + + e = create_engine('someurl://', proxy=MyProxy()) + + """ + + @classmethod + def _adapt_listener(cls, self, listener): + + def adapt_execute(conn, clauseelement, multiparams, params): + + def execute_wrapper(clauseelement, *multiparams, **params): + return clauseelement, multiparams, params + + return listener.execute(conn, execute_wrapper, + clauseelement, *multiparams, + **params) + + event.listen(self, 'before_execute', adapt_execute) + + def adapt_cursor_execute(conn, cursor, statement, + parameters, context, executemany): + + def execute_wrapper( + cursor, + statement, + parameters, + context, + ): + return statement, parameters + + return listener.cursor_execute( + execute_wrapper, + cursor, + statement, + parameters, + context, + executemany, + ) + + event.listen(self, 'before_cursor_execute', adapt_cursor_execute) + + def do_nothing_callback(*arg, **kw): + pass + + def adapt_listener(fn): + + def go(conn, *arg, **kw): + fn(conn, do_nothing_callback, *arg, **kw) + + return util.update_wrapper(go, fn) + + event.listen(self, 'begin', adapt_listener(listener.begin)) + event.listen(self, 'rollback', + adapt_listener(listener.rollback)) + event.listen(self, 'commit', adapt_listener(listener.commit)) + event.listen(self, 'savepoint', + adapt_listener(listener.savepoint)) + event.listen(self, 'rollback_savepoint', + adapt_listener(listener.rollback_savepoint)) + event.listen(self, 'release_savepoint', + adapt_listener(listener.release_savepoint)) + event.listen(self, 'begin_twophase', + adapt_listener(listener.begin_twophase)) + event.listen(self, 'prepare_twophase', + adapt_listener(listener.prepare_twophase)) + event.listen(self, 'rollback_twophase', + adapt_listener(listener.rollback_twophase)) + event.listen(self, 'commit_twophase', + adapt_listener(listener.commit_twophase)) + + def execute(self, conn, execute, clauseelement, *multiparams, **params): + """Intercept high level execute() events.""" + + return execute(clauseelement, *multiparams, **params) + + def cursor_execute(self, execute, cursor, statement, parameters, + context, executemany): + """Intercept low-level cursor execute() events.""" + + return execute(cursor, statement, parameters, context) + + def begin(self, conn, begin): + """Intercept begin() events.""" + + return begin() + + def rollback(self, conn, rollback): + """Intercept rollback() events.""" + + return rollback() + + def commit(self, conn, commit): + """Intercept commit() events.""" + + return commit() + + def savepoint(self, conn, savepoint, name=None): + """Intercept savepoint() events.""" + + return savepoint(name=name) + + def rollback_savepoint(self, conn, rollback_savepoint, name, context): + """Intercept rollback_savepoint() events.""" + + return rollback_savepoint(name, context) + + def release_savepoint(self, conn, release_savepoint, name, context): + """Intercept release_savepoint() events.""" + + return release_savepoint(name, context) + + def begin_twophase(self, conn, begin_twophase, xid): + """Intercept begin_twophase() events.""" + + return begin_twophase(xid) + + def prepare_twophase(self, conn, prepare_twophase, xid): + """Intercept prepare_twophase() events.""" + + return prepare_twophase(xid) + + def rollback_twophase(self, conn, rollback_twophase, xid, is_prepared): + """Intercept rollback_twophase() events.""" + + return rollback_twophase(xid, is_prepared) + + def commit_twophase(self, conn, commit_twophase, xid, is_prepared): + """Intercept commit_twophase() events.""" + + return commit_twophase(xid, is_prepared) diff --git a/venv/Lib/site-packages/sqlalchemy/log.py b/venv/Lib/site-packages/sqlalchemy/log.py new file mode 100644 index 0000000..a79b21e --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/log.py @@ -0,0 +1,217 @@ +# sqlalchemy/log.py +# Copyright (C) 2006-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# Includes alterations by Vinay Sajip vinay_sajip@yahoo.co.uk +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Logging control and utilities. + +Control of logging for SA can be performed from the regular python logging +module. The regular dotted module namespace is used, starting at +'sqlalchemy'. For class-level logging, the class name is appended. + +The "echo" keyword parameter, available on SQLA :class:`.Engine` +and :class:`.Pool` objects, corresponds to a logger specific to that +instance only. + +""" + +import logging +import sys + +# set initial level to WARN. This so that +# log statements don't occur in the absence of explicit +# logging being enabled for 'sqlalchemy'. +rootlogger = logging.getLogger('sqlalchemy') +if rootlogger.level == logging.NOTSET: + rootlogger.setLevel(logging.WARN) + + +def _add_default_handler(logger): + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(logging.Formatter( + '%(asctime)s %(levelname)s %(name)s %(message)s')) + logger.addHandler(handler) + + +_logged_classes = set() + + +def class_logger(cls): + logger = logging.getLogger(cls.__module__ + "." + cls.__name__) + cls._should_log_debug = lambda self: logger.isEnabledFor(logging.DEBUG) + cls._should_log_info = lambda self: logger.isEnabledFor(logging.INFO) + cls.logger = logger + _logged_classes.add(cls) + return cls + + +class Identified(object): + logging_name = None + + def _should_log_debug(self): + return self.logger.isEnabledFor(logging.DEBUG) + + def _should_log_info(self): + return self.logger.isEnabledFor(logging.INFO) + + +class InstanceLogger(object): + """A logger adapter (wrapper) for :class:`.Identified` subclasses. + + This allows multiple instances (e.g. Engine or Pool instances) + to share a logger, but have its verbosity controlled on a + per-instance basis. + + The basic functionality is to return a logging level + which is based on an instance's echo setting. + + Default implementation is: + + 'debug' -> logging.DEBUG + True -> logging.INFO + False -> Effective level of underlying logger + (logging.WARNING by default) + None -> same as False + """ + + # Map echo settings to logger levels + _echo_map = { + None: logging.NOTSET, + False: logging.NOTSET, + True: logging.INFO, + 'debug': logging.DEBUG, + } + + def __init__(self, echo, name): + self.echo = echo + self.logger = logging.getLogger(name) + + # if echo flag is enabled and no handlers, + # add a handler to the list + if self._echo_map[echo] <= logging.INFO \ + and not self.logger.handlers: + _add_default_handler(self.logger) + + # + # Boilerplate convenience methods + # + def debug(self, msg, *args, **kwargs): + """Delegate a debug call to the underlying logger.""" + + self.log(logging.DEBUG, msg, *args, **kwargs) + + def info(self, msg, *args, **kwargs): + """Delegate an info call to the underlying logger.""" + + self.log(logging.INFO, msg, *args, **kwargs) + + def warning(self, msg, *args, **kwargs): + """Delegate a warning call to the underlying logger.""" + + self.log(logging.WARNING, msg, *args, **kwargs) + + warn = warning + + def error(self, msg, *args, **kwargs): + """ + Delegate an error call to the underlying logger. + """ + self.log(logging.ERROR, msg, *args, **kwargs) + + def exception(self, msg, *args, **kwargs): + """Delegate an exception call to the underlying logger.""" + + kwargs["exc_info"] = 1 + self.log(logging.ERROR, msg, *args, **kwargs) + + def critical(self, msg, *args, **kwargs): + """Delegate a critical call to the underlying logger.""" + + self.log(logging.CRITICAL, msg, *args, **kwargs) + + def log(self, level, msg, *args, **kwargs): + """Delegate a log call to the underlying logger. + + The level here is determined by the echo + flag as well as that of the underlying logger, and + logger._log() is called directly. + + """ + + # inline the logic from isEnabledFor(), + # getEffectiveLevel(), to avoid overhead. + + if self.logger.manager.disable >= level: + return + + selected_level = self._echo_map[self.echo] + if selected_level == logging.NOTSET: + selected_level = self.logger.getEffectiveLevel() + + if level >= selected_level: + self.logger._log(level, msg, args, **kwargs) + + def isEnabledFor(self, level): + """Is this logger enabled for level 'level'?""" + + if self.logger.manager.disable >= level: + return False + return level >= self.getEffectiveLevel() + + def getEffectiveLevel(self): + """What's the effective level for this logger?""" + + level = self._echo_map[self.echo] + if level == logging.NOTSET: + level = self.logger.getEffectiveLevel() + return level + + +def instance_logger(instance, echoflag=None): + """create a logger for an instance that implements :class:`.Identified`.""" + + if instance.logging_name: + name = "%s.%s.%s" % (instance.__class__.__module__, + instance.__class__.__name__, + instance.logging_name) + else: + name = "%s.%s" % (instance.__class__.__module__, + instance.__class__.__name__) + + instance._echo = echoflag + + if echoflag in (False, None): + # if no echo setting or False, return a Logger directly, + # avoiding overhead of filtering + logger = logging.getLogger(name) + else: + # if a specified echo flag, return an EchoLogger, + # which checks the flag, overrides normal log + # levels by calling logger._log() + logger = InstanceLogger(echoflag, name) + + instance.logger = logger + + +class echo_property(object): + __doc__ = """\ + When ``True``, enable log output for this element. + + This has the effect of setting the Python logging level for the namespace + of this element's class and object reference. A value of boolean ``True`` + indicates that the loglevel ``logging.INFO`` will be set for the logger, + whereas the string value ``debug`` will set the loglevel to + ``logging.DEBUG``. + """ + + def __get__(self, instance, owner): + if instance is None: + return self + else: + return instance._echo + + def __set__(self, instance, value): + instance_logger(instance, echoflag=value) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__init__.py b/venv/Lib/site-packages/sqlalchemy/orm/__init__.py new file mode 100644 index 0000000..f279c9b --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/__init__.py @@ -0,0 +1,297 @@ +# orm/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" +Functional constructs for ORM configuration. + +See the SQLAlchemy object relational tutorial and mapper configuration +documentation for an overview of how this module is used. + +""" + +from . import exc +from .mapper import ( + Mapper, + _mapper_registry, + class_mapper, + configure_mappers, + reconstructor, + validates +) +from .interfaces import ( + EXT_CONTINUE, + EXT_STOP, + PropComparator, +) +from .deprecated_interfaces import ( + MapperExtension, + SessionExtension, + AttributeExtension, +) +from .util import ( + aliased, + join, + object_mapper, + outerjoin, + polymorphic_union, + was_deleted, + with_parent, + with_polymorphic, +) +from .properties import ColumnProperty +from .relationships import RelationshipProperty +from .descriptor_props import ( + ComparableProperty, + CompositeProperty, + SynonymProperty, +) +from .relationships import ( + foreign, + remote, +) +from .session import ( + Session, + object_session, + sessionmaker, + make_transient, + make_transient_to_detached +) +from .scoping import ( + scoped_session +) +from . import mapper as mapperlib +from .query import AliasOption, Query, Bundle +from ..util.langhelpers import public_factory +from .. import util as _sa_util +from . import strategies as _strategies +from .. import sql as _sql + + +def create_session(bind=None, **kwargs): + r"""Create a new :class:`.Session` + with no automation enabled by default. + + This function is used primarily for testing. The usual + route to :class:`.Session` creation is via its constructor + or the :func:`.sessionmaker` function. + + :param bind: optional, a single Connectable to use for all + database access in the created + :class:`~sqlalchemy.orm.session.Session`. + + :param \*\*kwargs: optional, passed through to the + :class:`.Session` constructor. + + :returns: an :class:`~sqlalchemy.orm.session.Session` instance + + The defaults of create_session() are the opposite of that of + :func:`sessionmaker`; ``autoflush`` and ``expire_on_commit`` are + False, ``autocommit`` is True. In this sense the session acts + more like the "classic" SQLAlchemy 0.3 session with these. + + Usage:: + + >>> from sqlalchemy.orm import create_session + >>> session = create_session() + + It is recommended to use :func:`sessionmaker` instead of + create_session(). + + """ + kwargs.setdefault('autoflush', False) + kwargs.setdefault('autocommit', True) + kwargs.setdefault('expire_on_commit', False) + return Session(bind=bind, **kwargs) + +relationship = public_factory(RelationshipProperty, ".orm.relationship") + + +def relation(*arg, **kw): + """A synonym for :func:`relationship`.""" + + return relationship(*arg, **kw) + + +def dynamic_loader(argument, **kw): + """Construct a dynamically-loading mapper property. + + This is essentially the same as + using the ``lazy='dynamic'`` argument with :func:`relationship`:: + + dynamic_loader(SomeClass) + + # is the same as + + relationship(SomeClass, lazy="dynamic") + + See the section :ref:`dynamic_relationship` for more details + on dynamic loading. + + """ + kw['lazy'] = 'dynamic' + return relationship(argument, **kw) + + +column_property = public_factory(ColumnProperty, ".orm.column_property") +composite = public_factory(CompositeProperty, ".orm.composite") + + +def backref(name, **kwargs): + """Create a back reference with explicit keyword arguments, which are the + same arguments one can send to :func:`relationship`. + + Used with the ``backref`` keyword argument to :func:`relationship` in + place of a string argument, e.g.:: + + 'items':relationship( + SomeItem, backref=backref('parent', lazy='subquery')) + + .. seealso:: + + :ref:`relationships_backref` + + """ + + return (name, kwargs) + + +def deferred(*columns, **kw): + r"""Indicate a column-based mapped attribute that by default will + not load unless accessed. + + :param \*columns: columns to be mapped. This is typically a single + :class:`.Column` object, however a collection is supported in order + to support multiple columns mapped under the same attribute. + + :param \**kw: additional keyword arguments passed to + :class:`.ColumnProperty`. + + .. seealso:: + + :ref:`deferred` + + """ + return ColumnProperty(deferred=True, *columns, **kw) + + +def query_expression(): + """Indicate an attribute that populates from a query-time SQL expression. + + .. versionadded:: 1.2 + + .. seealso:: + + :ref:`mapper_query_expression` + + """ + prop = ColumnProperty(_sql.null()) + prop.strategy_key = (("query_expression", True),) + return prop + +mapper = public_factory(Mapper, ".orm.mapper") + +synonym = public_factory(SynonymProperty, ".orm.synonym") + +comparable_property = public_factory(ComparableProperty, + ".orm.comparable_property") + + +@_sa_util.deprecated("0.7", message=":func:`.compile_mappers` " + "is renamed to :func:`.configure_mappers`") +def compile_mappers(): + """Initialize the inter-mapper relationships of all mappers that have + been defined. + + """ + configure_mappers() + + +def clear_mappers(): + """Remove all mappers from all classes. + + This function removes all instrumentation from classes and disposes + of their associated mappers. Once called, the classes are unmapped + and can be later re-mapped with new mappers. + + :func:`.clear_mappers` is *not* for normal use, as there is literally no + valid usage for it outside of very specific testing scenarios. Normally, + mappers are permanent structural components of user-defined classes, and + are never discarded independently of their class. If a mapped class + itself is garbage collected, its mapper is automatically disposed of as + well. As such, :func:`.clear_mappers` is only for usage in test suites + that re-use the same classes with different mappings, which is itself an + extremely rare use case - the only such use case is in fact SQLAlchemy's + own test suite, and possibly the test suites of other ORM extension + libraries which intend to test various combinations of mapper construction + upon a fixed set of classes. + + """ + mapperlib._CONFIGURE_MUTEX.acquire() + try: + while _mapper_registry: + try: + # can't even reliably call list(weakdict) in jython + mapper, b = _mapper_registry.popitem() + mapper.dispose() + except KeyError: + pass + finally: + mapperlib._CONFIGURE_MUTEX.release() + +from . import strategy_options + +joinedload = strategy_options.joinedload._unbound_fn +joinedload_all = strategy_options.joinedload._unbound_all_fn +contains_eager = strategy_options.contains_eager._unbound_fn +defer = strategy_options.defer._unbound_fn +undefer = strategy_options.undefer._unbound_fn +undefer_group = strategy_options.undefer_group._unbound_fn +with_expression = strategy_options.with_expression._unbound_fn +load_only = strategy_options.load_only._unbound_fn +lazyload = strategy_options.lazyload._unbound_fn +lazyload_all = strategy_options.lazyload_all._unbound_all_fn +subqueryload = strategy_options.subqueryload._unbound_fn +subqueryload_all = strategy_options.subqueryload_all._unbound_all_fn +selectinload = strategy_options.selectinload._unbound_fn +selectinload_all = strategy_options.selectinload_all._unbound_all_fn +immediateload = strategy_options.immediateload._unbound_fn +noload = strategy_options.noload._unbound_fn +raiseload = strategy_options.raiseload._unbound_fn +defaultload = strategy_options.defaultload._unbound_fn +selectin_polymorphic = strategy_options.selectin_polymorphic._unbound_fn + +from .strategy_options import Load + + +def eagerload(*args, **kwargs): + """A synonym for :func:`joinedload()`.""" + return joinedload(*args, **kwargs) + + +def eagerload_all(*args, **kwargs): + """A synonym for :func:`joinedload_all()`""" + return joinedload_all(*args, **kwargs) + + +contains_alias = public_factory(AliasOption, ".orm.contains_alias") + + +def __go(lcls): + global __all__ + from .. import util as sa_util + from . import dynamic + from . import events + from . import loading + import inspect as _inspect + + __all__ = sorted(name for name, obj in lcls.items() + if not (name.startswith('_') or _inspect.ismodule(obj))) + + _sa_util.dependencies.resolve_all("sqlalchemy.orm") + _sa_util.dependencies.resolve_all("sqlalchemy.ext") + +__go(locals()) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6235f84bf5c4f333da473dad8c46cb5907d925cd GIT binary patch literal 8465 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 Fe*ryj;l%&| literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/attributes.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/attributes.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..83991c6824212568aae1cbf4d04b7f5257d81a02 GIT binary patch literal 49310 zcmeIb3y@sdc^=m9?&<02dE&WPuot^nV0MAt<t{}oK`ez?V88{59e}{>l5DTk)6@43 z(1U&q&g}s(od&5amfR&vOOv)E%d$erq)5FOSr%=*%8DgfrXP_K$+4sOCQ&3Swya1_ zr7GpBL`f<6zW<zi?(Lp#ESA~0QmG8kw{PFadHm-;|NB4Z*{P}goxgLT`j`JS7W>0k z<S&c!Gx&HH6S0^RtHhkRlW4{(aVP1dnu%7jl5C|asaCp@j>nv|lfku2CDY1QvaMWY zLat?-`PO7*vQ?-QT2qxNxt7DVVx`!cu1vRPDl@Iw%4}<{GS`}~%(oUQ3#~nsJ*~Z! zy{&zfeXaeK{jCF)1FeIVgRO@u548?e4z(VxJlr~5Iox_r<vp!Zr6grepv*@qkF<_d zj>Kbbdh^kb#GJe{`FhNmbTgZe;d2z9$M2=@#Vf~fui#AK-jsXnUb1rBDLT`y$100% za`Opi22VUGXR|mvA!l<qJ1J-LI4jH90?wX7Nqd~VC~2?zG)j6ee(!Vk<M)2~{S1B| za1P@4LH8Nle-^(Vat`75A@^DQejk26>>S4L!*c(-ocA~-wB-F#!y~BS1MYL~2i@Ym zIF4u!j%drZB<`HTGaqt}IFCAyIY)6NQ8|s$9(RtQv?K0mlz!Sh?IidQ=g+$bH{#AQ z=lJXK%7^8A(Rl*r-|d`tPB<rVcE%|?PoeH5=OfP3&U<m?1?NTQ8RuD?ops*ld>8JW zbKdWK0N=~bbIu3xecn0cd<fqkaV|Kgo#*l7i_V9g@5Yr2&KYM3-yd~eaL(fUW6n8e z8Q&M3#MRj1M|;0@&fRFVU9Z$=dtJ5Na@*Znx6x^rHae<QZ`M4oRO@zCV|}}ehiYx7 z)V=OD6dv!os#fQt`P+@|b$P$muDf12pI^P+@Jg+Yv)yz{INSENYt80Psom+8oQ87i zUHrJ!@EYs5-|gVOt4g)4t!ASxb$cgDs4nlAmeoqln!4tewrZ+d>TIw9rAt>{I>t7e zM%-$2cTjKsk2z~r{ut9FwwUx&+ucUfPrJ9!DL;=6ZlP*4_>bfIJ}%+?jT##5Pu4rl zroQd@N%u}2?-vOZ_Nj`muIo?P-%g|6^=DeOcI}$0s-2ChKx%Q`pS`?v_3DLJma8k5 zs#lh;zI<`jFD$LDUa5X!>1y@r@~WSgU&}9DUj3Ax!$YquU3_`jpIBLbW%-IcFoBC_ zUtC)G$g*EJyL9p5(hC=tuU0R8%+Fl?_(gutURl1lw7Pt*dg+CaE}vcHrR;^33vx2M za%rV{dHKrK3s+Z{SLD*(i<g$pRhK@pbYbP{YW3`;mtMNCinsmAprx12pZE6%KdvmV zzI<h+8a8K7a0ks|Ef<z9UZ^blGm*>Zmsiie7#4vC<qG;Q?ezBr&t6)&_^C+i`~n_b zdU^HI`HL@KeQ`12PY9R+Y~>d$sPU`LQ4Ri5I5~rl_uDwQF%ZF6CE>&?NheWBIZrw% zCk^yZgUDo^jFWvmhAEnJCot)9&IIn{H94w~9A!26!4n1fCFBRcrre@4<IKvt)6SeT zj|ns5EI51cJ?rds_ThWZ+3y^{_q=n^c?jPN&LQVveD84%JMY2wUZ><dg71Bp)*ub2 zdB1bic^p>`$n=j@4oaPz{%m8>Nn%2t^yfal?W&y`rp8h*?`mHI!oE=RTr(j#HBSL^ zTc_3>*Q=|>R=1<pN}cshAh3t|QS0idQFphx`4ax@6fVhR@Jg8A>+bbhbAuDARyrm4 zdupxx5~iW6E~^fX-JLagS(jTbl~ytFFy%3s$G*VnUaMn5$>f&TZ#8N%WlymYsA6~j z%3A~V*~W54v-Rb2$#dOW)9ai%rLXX+ZSw_7xm?G>Y;DAY!=01Dr6|)h{XiMSs@hSl zY6x5}THMgX=oN0WnAMd!SvCJA@FAw2#mD<z9QyHotQzYlaE$kpI41fj9FzStj;VeI z$8<N-kKc>m&GxgZa_4wIyO})~`<}yhb9gIvBcq<{$2N1D6Mg(XfU|q?c<knLpMXFB zyos+Y=KYL}V%1OHaCiJvqqWua^Yu;(bh8Eo@u#Xd11ZFRkV`*Dy4BsmKo`(HKT}-? zhIxL@X?R<;ZvDESt!{5QpnHoGe#&|qO!ryk?sC=ZYGwZcG|SyQB+HMG%BvDBZ8 zsrTaHKf8YR)T=Lp*n6+;9P^Ityn4>{Zge|aufEXf+&KA?P*XCXlUH}VuG>1<s^Lq$ z3i5sH)n;S;Rj<)?Pj1!fH$Zj0SG}9fTC;xLZSA~@!FttVjaS}c=c<i%qg$<h0QGu% zafl@|ar-ZkOQe#8crM<1XaqV2@7BpD_{VR4WHfU)!AQh5;~MS>frdZtZEtOLRM)F+ zb+)#f!pIim>I9ya&g;W7_~|cHM?Cvc-0{e`Vu{|dQH>h$PCd!z@yBnAZu{{=H?W%^ zLUofo_91=f2B&m`C$G8PDi}3TV@Kujj+(?_F)2;<a{&SNvs(}X8n@h`Ua0ru=@ooD zRve2L;=RKonjNWRG&uQoU!$<pFa~DDY85yL`bNrFtzKcp23n7dsCMw_(KQbaWbNlO zvDg*2yRF(nu8uX@8=Yg2S43o}sU4AsD0^(T9alAX8trQ(u;db2MWh9>A`Rdl=_IM^ ztp*77bytZHRP#_kv)Q@rMQGR)r3+e~vDhmFv;<*fyVb@705x_>0eL)8YLwlwttLVO z&&u<bY@aCIzTT)qCW92FiwMN7$jcDCY9)^osCKdmwh$rey10O&oB%vX)&q7FDdTpd z*(|9LXaSACD#c#FBrX$-Yf&%)n>zId=!!E?5JX4<x870i{hJd3^f>TY)-VJYij@(d zbUTq2)Y?0x4qBi}HyV&~!4_q3?`$bDGw@4&6U?;@xDC~?b|O*|yJ3M<G9cPO!GJjL z1wa~PM;cI(Vj`Fqf-IDuMd+5|0^U8;*=Uncz;=rW%NK_F)##GAmz)mddrIiQTOcgt zh|cYHX>ConOEWl)-eKR4Yc%t&cea}jP+pH92>uC7k|y8=NKv>ysYQ8*RY!VQ?+E9v zvrexRJkx2ri}xR6<pP?JbAZ6ZgBB^>4$&Z-*D#CP_2#xiaM)+AxSC>n*Be{mG>Vk= z|LCBav4q%W8#L7D0f4Nc<050dEjmT#oz|QpT^d&x#ttLnG^i?3Z;JwqqGxSX8AHg2 zmesn@(8gl!$U-zVDjV`cx)YsAb<8~An2f#xS;Y8ndl<!NbBLR7BV&eh^8}g~d3kN^ zrJb|Xc*|a=<ytJa(y5sVk)eQK3cg6{P;(sDId!V^bosgR(;+7qExB?U1w>$&QiktF zpBlslLn>_37ZX|*OOe)&=AY^m80IUMX)gBmjbNI5eSlSx!Jg$yln}d+cpz3J?upz} z%R(SB>=NSwX~8JhJQbxG14Y_NY10rMEh<LfCw2lMkQi4NRs`Asq%d6qP<9AOLpnqq zD4={x%?5~$14Up9)7x>kT%a#1-QEJ-CK9!4;Onjhn4StWD4=yEwy?WnSmq$B6K=el zUva)hx&fIf4FLIB0|j*qzebOPpV5{6hlHxgFZteq5ksM$8W;>KfKpb|^4&cn`kV2e ztI`euf?G8*Nwqqr6?xb4wYO|b61|5<v?P4;owY_iKNbjb>&v#@c<<<ldUyBb!Rqf_ zZ#HhYrQNR|Ulg-asScV3>Op*MY{23K3TF{xeJ4aJY15i;8H#QC1*pD(WLFNR-Ja@A zs_a9s9}Ri+-RSmTv0YE~j*sg4sPgK9EqIgP0~1r@0%d&4_q}6gR(ft1+Eb~!y#-Dz z!fOrkPxD6U<;&+_KeElAukxzV^{V=Dbd8FOpT1RVqHk2*Lj6UZ!OtJUM<Z~u_xPwj z>@IYm2UL`Ww3Kne9uz=jy7AY9#Ps8vP?0yGAa6oF-h`n>G?=&(e=ChXCRQ-xtO}}5 zqJ+hqI*u>Dxa2|JAm>_E3OetE;DP5=tA0k$^I-*cy5`ijy47x{s_&bwt7Q~7+;Lv} z5qyLmQsv3vUnZW4_l}GJ!BD{i-KXv$-KRR;B>;5CW4hz$HdQusz0Xc(bf@Erz*@|z zBlsGnjmv#aearwR>rHRC7cAt*@$rf{#Nr9Gpa1gg$H5W(FgpD}`z->p474+Lk3BFb zxB?gw&;4|>w(d3qWJ<MbtzEeE5j^*^_-I^;_x6uyfRtjZr^1-T=MvwV!-1xi&DaJs z+^;11$uAnUJT5$OF{KeX(b@9TBEkEa8@FLj9)^marO69g$+uW6yB8Y+4gHu^y@Td_ zjPK3hfabu;g9eRjjJklQ6!m?@mxhMNpH@)D6o|NK(9h$Uph2V1Z0Paxwq*noTPJw7 zfD^Wgqts6Tr{IpbF#)_vt}Mp=d}piMXu%fHsGrAhac=n&5Xv<h3Ty&tvCpa%+#PDP z;$Zv|K0!kdjTlB-o*hbpnWBZX@I{>L6wF+R--CN173(H$<kbst7+>NzcWBCp-HUIg z_`L~iBZ=t8H&T0X^|kn?V(r8ywLL7c(mTImB#NWTm`0)As8aYVBAbc;e$*2^1OkYa z{qXc++Rws>!An}oDlP?zuqH7n+R$ilB6@@4>Y1m>%pl?PC#zLcRJHnd2`!>8XJ8>- z#4(Y`VHPGv8&V>#4lqKFvcQjJIWiSAE!jxyrFZ~Hq>p;#qaH@_Q9Pnnc_7&t&7olS zqXoWN{l|DD7|u~-!8|t57%QU;+E#l1CSq8zQrn<VyB5_Ij6&21#dG`CZp2!HC}ugh zsYv)1#*@Yu5inr~ZFSo4J3-P6Bvh!l8}>z8!LTM16*NbxiP7pY`eGK%1vZ<}Kvtgs zaDUSRcygo}(iV|g!QW#1{Nh70cz(WG)y_3=wL%rrN3E%Gk7MU_>b%_WsuqjYv#3cC zQT*AK+v+rWu2T(4SKq~(2Y8@GPkoRFY76Q#4{WIVFb|aHgscny*Zh&JM3IE6i#)u< z14)5;nFs0{5keu8;Tc{d|9THT9^cO`q!OtFr+9w0m@1@lsp6Bx0=|>EOfH?v<tA{3 zfB9r?DwmXZ>v+xnk%*kZ$D;r+=n!&}aJQtyB^0Z`FHUz*CFA5UEhhXuP#28HGvYVs z{g|kLrS%TXe1a+3DGG_v!qcb<(D}R)%-fB+AkheqOv~+F?>O{`QklEz!uAuWEF;j^ z$Y_DrL2OPNaHD$N(gxH~C@?HZiW;EtY4lqtz+s$d8b4_Gbz=8YaLyZL!I*!OkPmm6 zeHG2+YKey-(nYEOdvWy-aWAy^K0FeJW%KL+owuCm;K)|qOYCZ4#(^3G5jM0<8`3I` z9BLotgnqnQ{UbJ!SOE@R8rQ(!VGGHCj|(Kvv5oL7-pHu}jnm*t;5K)K>!>nd4Bifp zOqrgXO?YFj67EUxB`voh2;i!PcSh{$dYX~!YVMi@BaogM2}K|e5#@H<ReqYbzv_q~ zpf$bvKcWyLf#%{V@Qj&w9=hAOp2|BbkQ#;Xf08Zu6b}_1syx(qaCr!U8cx>l!3`fD zk2F1(MTMA}idQY^C$M2X7qQGEvG_>r4G3fmQ+Yk-B)YMC$-D7x{7v$1$Q}Ct&1#|x zc=gRyC;4W~NxcOu?NfY9_x|=-1(b*Nr(N1TQ%X<ikbz*PfI?s2Syv6mOf<~{XnbtD zw@1BCjx81{*b18NHB?0NW8g`Z85U@H67pb}_VLBQgnJGCkapx1ZIrL0=k_mY&b(J~ zGC-E*7`6j{Fg(@tQ(G@}#?5Z##7hl0RVIv|I<I}yl}QsEQxM3~JDsacNf5_;?^s=+ zMVfx60oraURrz$Y0}a>vaQQNB?exBKSzBc2Bz_xWWMd=1mm?-WRslmf%qpEN?K{4_ z10oj%28PFyIaIrK*18}=>kZiPdIMG&5IHkwq>w9h;u!z<O{}_#k8p_*{?n}ZPw7QX zi5E33UeruL=dz)O<Rg|O7+rt1qZ-#xchj;!$Yl{X=}dJ<ag&(>1(O<aSf<+GPcnO9 zg?<u@S6$=Ja1sf#BlFEbFw*K)%K9LoIQE`?z<~or)$YtyJLf2B@goo-xHRR*?13*> zOCiF%?<0ULuvJ1R6A#`JAb`XM3^c?J6?+^S3e{ec$9rfR+}$9M>I8p_UXRErbCq#O zXmVN$C^;Hr=;Kq@QIu-%K(%{JHwgdz>;~0{SfT*=nE=kmd(S*5Xv3i&;8&z)0^5LJ z!6-rLS~`Mn(5`zlMo81iG!&VJ7W)M?*#1BnU=N1zg4Z5l)5+grFeGR-y!U|zZTJwH zT24mF!;iFHWP%7ML(7d080Aq3jm_i%M%mMnKphu`+s%iLv)zP{v^&vz>OtEqS`2J5 zcwhebZQJ@TX)q#pmf1qV?f5%sooGG-c)&)@<KuBWck<oXo0J8#b{dCvx~{cTqq)-I z9^QM^Xs+OdU2r(7xx$7%N^>>(D<?#<w{p&~CYUk}|EX33{u7X}2n!;kda9#@d6Aa# zFGTI~yQL0uqJA8ZP*-^1^i#w$5iSh2`xBP`VAjf92&D-s2JhwAFiIhoN?@u|^&L`x zpM6L8#Ok1UC#fYBM~XSmqAq`~3Z+Qcnj!vocC>m!1XN_xAvxFLkSH*s%CG~wI^(B> ztNx$3=h42BOQ%xMVkT0FLaI2C%R+xisy53d5CEMQ@#Rl#s7?#*&|VUN^9Y}?K-6UL z5R_F!@kk(!W$R+H1~*4Z0#zwG+A>mKfB!hx{X5*%bTYrNJOf&p0MSu5akY}Uvi#De zSC;+E(&fv`E9WZNE6bNJE}dPj6kd4w;>Qrlr+?*Mx^(Ws`3uYENEhrM!FL89?>QU> zB%LB<947;%EA3>R9F;FO>p-&6QF+d!Q^2(eX9^fU?WdO+s9E~~l(z&#zX1u0%%(>9 zjA5s$vwaPo=B~&t_0BeIWLg5LU#~UbpGP2DV40CwphMlP@}+J;%A+hp2NMKr#zgTY zN<tn=t4nG8B`=EL$CxuV;#LC!gcMpU!8O>X<p8s*-Bk$bpr<KlD3!KaU3J9uJ*HjS zu7@WB#c(w$iwO{d&<pyX=s{z<*|N1YiUA0oK)fFeChD35>`KFKUl)nACe0E1iSB>N z+eRb^-akcc6u!}=07z1kS7v}n+c6Og;~s+UXP#gP@VKWGj0?A1+G9w4?%%EYwxah9 z78#k6a20Zt;ecU3*Ae6eKW{jUu*|g`2n7fYW&;2gxVgjyY(_y|i;e;YC<L?~Iz2Ak z+nrKtn^BC-9Rwz@^8(=jEWxOZa_)XGaPFSERYMFkBJ55LcGCj)0`9GaVL0rR>9IyH zX%V_^M$>>yitTLaLcN{VdIwQC#A=M&rOQ=4JK^GtT+=f*2tXOQA(IwQ(}EQi5}nZ! z3?XyD_-J*rg6SZp?odS6J{MZAgRnoiO)yOdBoFf3v!n(hH&Mt7q8=%EF#a(SNa)6i zBj)ffUNNFarO3ug#Ceq<*7kB?A#xy<6O+~!`A7!8Vj}eQih|KrRvuvEBvR5WQd_F? zT`=lS2|^tOy2Oo~oKu}o-XH^pSeHV~IN}R5omL*+3jq)ac4EaJY#JgdtM1KebvtZb z9{jCW2wKOR0|F2p)NRY*L<?xT1=<pSA7icD>aik%9g%|*x)QLCf!^Cep_m+qfZAV3 zW#K5Uirp^g)@5E8?3UdBw5=-Mo7PoXtG;#0h2xnY#f<5b^O_w5N>;UD@)j!-JENFK z#C{mo9S%81;0hbSPUH^KY6$yax{#8wspF#kV<h7le7tYtFu;<Y7KTLS`@LXFI<z}u zLc6mjv>V~#Zr05qlOvD33x;{ep?>Qy?}9r8!v;gW5wh)0<DB8%afFBKaPL{Uk8BWJ zo0D^7h2VT%&L4M<;e0{Pk2{Mv-{Vff9FW*ZI!`!H;`d&;r!ztJnM{!V5*q%jKQ%yI zdhZKl86iXxgczo75N~%|F1_;kmDTzbfWkj|t5W!g>*{B5GD69Y1}RT$JqC9R0+sud zIA;1;Ck_MInJ&y0_&w3a6@K>%xIUp?>BDqZ&9`xHlh5b71sc&9ynMHK=SX*Iv)IpU z-MR*`7-7yccc=T)o74Sbf10Mio4rrO`Z@JOj;LGJnSLH6%y#GcvF`jm5|IUmhN5Kc zvzY&9Z5$bR`v8n0wuM*4@><mdj_e6MF$hG|arL1S0n;b)8~{n`NE3S?FIWN<zVm9J zxJIj>Ta7_fr47VX@zijHYc9-uVs@%YJOUM7nv%iNC_tFVlt>!_G89C1g3~$M2(uTq zcLbqxwe4p2NXfW8BuWdhoi!xRfci(usoN}<P*_5nAUZS{uC>unur}-9GS@-i*M@`y znmC?FIkhG%P7ZT!1e7>*kqvZ1`wQ*05(`*W+lU7Q#N5^vWH-b>@=4wiNz4QbF#<5? zHy^Gcf;1W+iuW2_Bn{Qj<7hByqy_FBgtjvnD^h>0#0W6}7d)^D|0V~}V#spvg7FU? z_ZH3g&}9@U1u07|EPfEhx=(4t@l#rAdP++?Pr2QC*`!t=PvRT{pCDhlFh~aA(c+Lx z2^XU)A?%c5+6XM6Bopa|R@&TZfHILHY9Ss;l81Q|SOA&EjJ*!YL}U}aovnfB7D4&O zwYFdz2Vxu~nb&p84T8fX4KRYU;2okBX<9ZB<gksJFwk1_c5TOtMg<?~G#!JR(V$cU zgJ^(p)iSkv$E_n}3v-PrU%j1hYDgfLopdD#*6nMEgtjfyc&DWid$Ck{*(9Dx$ROkt zGhYTBkBC*}yc7Jx#HSY~QUnDz%i_?<U{r|6Pzn$_wy{G*J_5@<T7D#)R?)FvlE80> zoy~3o;W!}*QcT3~A)ph1)Gel6pzj)GBaINRK(u6g57AjjsH?(tKNJZGxSoghf|=)Z z+Q%@b9dr@Rloxi6grMDp2e#}|$=MdlCvN|;smhLmcYKARx`s$-^dm~|CKe<|f@=V8 zWYD14ICNwp3JW%CnHr(LS4fBH0mtBik;ASs6R$k<8t{jXHtp$^_~mvqwA<a-K;poM zbQ?6Pu}Iq!{aza_P()(P$StCS#xgnqBlho7$sjz#1{Jy!Oz@S}#XX^Q*q^ZMKs^D@ z>Q6@KqoT0uFO2HHpVLYULYETmO=MwskR_q2_<Kl{w;(iWs$Ok&YRE$HA2y`FI`Xz; z_Gp7b76DEqfc~^GZ}SJa5HAc&cfUA@R3csZlTbeb?H{k$VG{*lac0;!zJnf0$5oGg zov^gopBbiaesQpVfsT5e1^fp*e1->#D1M3~#^lWIuxv{E&Sxna9>+=SyFwc~&A|w& zNMzt9%fWM1NG!x>5-FU+`o4E5&@(s<t=<rhq8jXmZkaUd)2K$b@pC+58wW)CBrcrA zM<P7lg{Ta%Y5rbB`rb3#c5S)D6Y|Gzexwb9YDMq(3*bM9fr9s1#scnTOi*fC<-w7F zHbC4}_iz|(fA{BUn;b%*Kd{YA_8u8(=I*EOqo+tPOBKfJKpP5MV?4o2Y+k4<i2Z9` z9Myw8`Pa5d@m)=#TYK<ntItw6wj;^P9qj|guy&Ef$Cx83kekUa#N^GiBw5+Gs@nUY zbw%P{);pahOh<-LLJp~w)+}dPD{VAu;Cn{!0Y|Weg``UR8#Nscx!x-7tc8(VrVNKZ zb0m9!+1+L)DBm|!B4ElQD%c9b^bIhLc4v31OyE6Ti6Yb3H<u`Xg5+DKi1WZMW1BH7 z>EW4LD55>lQCr~VPcX?qi-If@&A!dh9iehgi5Alm6V;IT7l;|_VK_GgdDy|k8pH$^ zMEK}JVJzAPgKh%S6hfa8c4FWk@Fd$8fj`;?bcpb%rARb721i>ZbOZ6MX&#JL5ivdh zhAmm*kHnH_(`$AQeWcFb`&S9nftjOa#V&1*8;xrS1(DurNm>g(Q0*2oLT%Lxz*MH~ z(Z1%Xb^b~;9HCx8w1Fu2E4-6zdDn)fkf{29!YM<_5t~WHi;1~-Z{I+R$`*I)oF@Fs z;*-YbEI!_saKLoo#L&|v#p!~;6j)dgN|El<8whs^93kn`vGy8VxHv~p#cTQ+w8Tt5 z!yrXm$siy$v6;S+l=IBJn3II&`DtpNPO3XWy%I;bRX7bdk~$oaS~K)VQ-kQx>E5qh zbs-EPyhl$)8Fi>!;Uq(jX$xS6aneKffTR&bCL>fOJW8cX6eEbPMCWkUqG%2GAmq?w zKU-u7k;tY;U@XP#iIUdVDCY!vyM;Hhu)<mY9izue?@L!`prwE<q@;8dQ<qcAG}&IP zH9Hi-ExsQ+>K)U;^MsRm8Y)glITA8?4T2zxY(m@wv4FUVIp_gPHF-eC_?|>EQI!%E zh{#!9%!e4O{vt3&v)C{4>@ghtB$Ce&nu}y?EXm*(ud<GFwQfy9FI5STAh1Oo^TcRt zmXx08I40tupKLhQ$|Q1ox+VvD2;=<%roBbzI0(0Q1n<Q%M12NE7jVo&hA6;ol8PtN zlrRb~K=lsVK@u4vT8y0+oFAA?dS2vlf`FLVYnHFY{rQznm#>Qr$91H012fgZON#E5 zp>|8i-lJ?TKB%O3V6Zt61?_EHLwic1_6;=DFfAxyMq@TP1mIc61+p+HuWv3|oF*-$ z{-PHO;IbTg+${pZvJe=*<$e3+lYulH!S73q(u8{5Y*1b6m{Lz_>xeBfu+mff7IMjX zNMa5D2N4`?b?>10JP#-`d?Ve^u%i`aaVnw0aK^LJNQy2iAQ+|LR@=2b;O*$2hR@jB z(14;UaP-X*CYB^xKOd}@F|g#uVi`>C_+m&05D}{WDhB<55XGMs$qNe`nB1txEz->N z9^Q>K7|!5HD-F@oZpk}EElNXJr$8bKV8S~DVvnc@#Ka-h90Wd%=<Nwr?5FQ0Zx&(B zWBPfb_n`~o^JpRnH9~a-Y|Iz5G)G)m7*Se{uv!fBP|7qhQw*z2(s5!`AvH}PWI`&S zWVS5fs-M_s`$>3~hNcY{gQ4Svjd+fLkm!*lCOZuER1|vq2SE}(Zl@8M9nH+b?DCj3 zghM|82DYH%|4me2OhaWeek1EW17;NOrw%Z#J*WP(pX?^j#Jmr8Q-~h~<iOxEFq_7i z^#SQJaW~z5POfKh{T8lg`7YU_y}uu|=F}H>wAa881^THQIqxen3OV)FK4MjHo=3g^ z)ecvbKuHo|KZzKIe`?|sCZVa=So)iP`pH;(FW#BJDE=vGfcX~hrMZj%T0V6q_NAEj zXI(f;aIeUF5>cX~7yb{>5Y>Q24TdOVrx{JiW;Lw{)#nS8hXb8im?{!^S}==>MO36r z{Yn%`#2IMknwdU)Ibt}lW!gMLu`;1tIug+!`#v~6?K2=4y2Zf@py@ORu<eW|G~Eeo z8Wv;2zVx0y#|&i(X<AL!k-ZkRk%F*MYp`n|Z)UX!$$STMghO;?aauFKX{cwd1`MZ; zrao#G_@?H0pb6if(~5c|-p|7#AFLAGl6D~{G(&h=m*rehMZTQ-n7gAB80OJI2EduB zq<?BJAN=b$MER?pS;B|Noi&3M9eGd(6PfcYC)O!E7R!KlV)YG1dN5#>-?)OQmH~H7 zC1$`;a}tbDOfU&z2ET;sCVR7k(-1VQPAbGdesd1W{9`Z)u#gT6MCM}7q?syaB8rhN z78mA;ukXc!V`#;Mp5)LnMsZp{qb<HSE!~Xw76x0V9~fvL<!5PN7(|BT8%}n}zf9~3 zo0yPVF<{r-`i&|qruS{F`YK8u-A-+i?y{W}JY;2#p_b}L23k51wDb^80#Jr6brNqO z;a4>ABrIJ?UG<OQSw9I3{?6K}zJd4e3^a6Tz)|!-NGQncSSd;~LldSc`i<oS5ooc= zQtuE;5X|V?!A%UK9_EN8{bYic5x#&Q8eSmr3<)QR2P%S4gB^Ae=Db0saNL1>)d!(( z&)_cEBoGx)rH&$=tlkOmsbKLDc#Fiu?HFnUX+}t(B{mkN(?TlL53^e{JP3_g#~C7v ztY93Oxa5mNefQ@n91Kx_?;|WUUo2p%LhOYRZP<7+;ynu`B3}|8!G8%ugkV6(oW?W- zjO6?pyzmJ6rf3lFJuj3tntr8e?Vx^U%mhNdkLgAqgAalEtdF5O<iO~QyXB%0T~~ht zuY5ndDs&~8M=<?xCr6OS#oYOo6}VDYR~9F~LvtbXc@B*7k87v+ZL~`#(0o77-r_-g z<e%r+B_4i&hXXvkpNEHd_(2{>wuJ<%ukq~bJaDQBj;kN%8M__90wLcLS8)e7UW)@B zPvZpCI?2Qw1hrk}F~vljG*WRA`MtR#g=AqaH(4m-J5@-}Bnt(_x9j-WKOzACJl+{_ z#B;T_BwX+bB;+J3S$uNN-pYhTw=-5O2j{y)ieaU;0^ID3730cmSTNE_!d=d_+PG?( ztkYJptF*bZ+L@0C`nfmY-LA|7wf6b*SH&4RK(KqySrr_DXW%XslskDH1elV*NOiot zdfqQq@p2PO2WqDi_^u}Z_NS(?CO`%Nl|O=mAF^TZeK>SuFeJwAB@l&z8S%w9(i{<k z(oNk<q(D{RX@{R(u4KjQoeONC6Mh=&K|#j&Nfe?nhT#!@9%gzIt*3s9w|<&eC*&$4 z{M0w`+r-)UNd%Dji5s^;z0dO55DREe{}DSY;&b@dJ2V1>)@E*%1kQR(8!Pz18NV4< z6jY^;-6WKQWV;A^4k-XqW+3P??k2XGQD^XtTyJ6GT;fa;La^i+o`gW01f`I7p4XfR zF*^CsLai~Lj8>CGp@zhLe->_S7vsg?H0@^+Fht!L;(gjhF<^dZI+1B2Gh_`#{up6H zF)Efs`c0w;%fK9@n%==tFw!1lOBV?p3n50GvhSie9PUaqlJp<f4j^qtMD6667*mb9 z$(5KuD&QXRx01qR(h%xD+C`GMeLuxF1y@4hCu_*#i_nU(<d?rk;!r)A7h%ST7W~%H zEG=VBy@;hGFJ1Xm^@WQ|D<2!pQ~a#a?uS7|y7adQDvkw+NPK~A7?4<sfxY*P0v0(6 zte<RQm<>YVg!GwMD)}^mF2hKb8$v1uo(v;ZuAjOGeH09cIuJrk1gSJ*N}aZ7HSsoR zMKRK!GiasBfMAgN7H%v~3^U#(TpcNc{B7PClAT%huUMb~U>d)e8Vn5EHwtJJPK1Bq zRm#K`R#cYnAZUkE5E}aaKzF17x^)=W6ZcZk&hth)$Ar=fw&oNhAiKqXcE(43tBd8$ zwmsdgzlLkW{rDwZ`zP#&7?IKTM0{_&_xPyx?*?R@GlYNaik&UT@#D@B+nv2sb1_>W z@NXTG&VWL&GXa$}o%uRm(}ZuNm0)}qehx4GHoHKo79;qN-lzE?c^Pm8;(d*{b2!m$ zcj8#U5ps{2p^EGQkSBk~T#~iiJc$SgWfmE32t}^q!GFefv1~;B?H|=DCb^+d%LOz9 z7DkF3--lxS0$ev<)fn*<xf7R<3|7VUv(64epc-{0q6s;KTH_&<K!~}D!>$N8Ev~Ss zV2*l$+`0?1QY5c2U_3%s$;ti-J4FqI1VZP-;0PVDkj_mNre+EVsZR;dtK%X2N0xd9 zAJ4&Iq?D0_l#!@pH)4=7xI!$V&fPqu3Z?`jxKAVuy*})e%N1f7kf$Y%BIJ!Co}YH6 zof&-3IC)4Ev;F}M^GmYm>Y!Zl*GI?|_m?QFO%VPCv6{m+#x;)-uO#J*L4>jBbdm+` zfzW2U#7tx%Kw>d9Axe~{!w_im^!e4%teTX9$Vuj77_zJsp)*{C5tH>ML)M$p3|EGl zQiZUWT`W|MB5`YY{NHe>g=j&yfOt_F3(xZZs~D*IJ7~ONo_cV`GEemjcyu(&RKJK@ z!xWS3^4}6HBiW@PJtGjV9ATPuAyp`de83XUpT!RuU8<scMTimwwS5?3Mf^-mJ&n5} zo{3okVnsW34fer)3OUdd>QyMRN%NiJH}ag}&g-W(64&6$Yh!NlTjS4W26cQB92RTZ ziW*Ee9!vRVAEK=JW-0jH*Wc;QEK1Cv#LNbqco6fLS;v)0#Lj><ELM<z`4~E(tzYXr zqX|rFyLq082K+Ug?dDj|@xB23hj4}v8<PM!4F4a*-T#g7XH67WXbhgiO0g7L$cHoV z{^&)2Xe0*GE{;c9j!DFdK1P_K4o5ISLMC4`5^^{7Ladv90hR$sc)4~cJ7>DtrN|xQ zAHCJjb#nmdEdW%1V*z2N26rc%WH(R1b|)qF7Dn8_JBr$-jHDe1MOq9hKn$W0H`ROZ zRq>UMP{OGeabfhU5{(u5B+P)V^q$dZN)IEQ-jPt-V5t?xxmt@T9gIlbA`y~k6HQuZ zG*Iv1VLuP#3tBuC>u3HI`UJ#;sUG325)XnT<U#L(6gCgO;ysSCVrlRr@FCy=u!4R- z{^J$cD<$`!kOVe7JRTb;p^X%2UP0+Z09C@w<18uR7v1D=l5_1R%BLdoAX^?1TvX&a zjVED^hsg5;<TuVYO0=RT8NuQ_=0Xn@Xj6eq69KUmh_t-=9Y{sb5Hu{1Qqf$Z7~h-d zJvqMnyA-g|;_BxDV3Z-Cl2qW^fmYK6JHC;{FEAj4+K}`FprLS^-puS0ZZG0`R)kwD zk!{6&=tUqd1ovwO+^?B3HMrrpGS;Z%%K25GkU%e_G@Pz_Jqm%N2s}T<Yv00QH{lVv z|Cf3Ht33QQ9)?6YqUwhTOrkjRSYZ1WfI?b^Udi8ciQb8~1#LtLS)3g!<Eg*PCQ!6f zzrurva%((0zyldj#EvB06aLOtMp`a?5TWf?apPZ*WU~3Wj1}4>H=|G}JT!ASH=+1c z9UuEAd>_BO-@{?7z(%uB0s`AfqzIP9Fhr4T0)`<il4-GxVwo)24{>hRnZx<CGw&>j zs3v=YV99>j3j|B{%N`(DvS0T8z>@v4=Ld2yXkg-wAI!ht`6Jl9BVO4j=fUFrd6=FO zFg>MY8UJ{=jK3pG_*Wis@We&}TM(SUyNAU1^f2h?Q~tiQR*D`F6l*^X!G8&^=Rn4` zLSjf6B^D}_`huubak67ApLL|fVaZw}+==U4XUZ|QFDO>B6~i*<QWL2%@S&5M2SVkD zrj}?76YqLWtv3*Jq_89`bOm+Ntf6QE^_hu>L`+7<XrcAE2|J@9jUmnwPp~M4+jWJ+ zF|6SmtYa%HBa0_4(f}^Z=PEe)N=I%r+}lTx8zIrkhz>I~2y{%qF?)>ge@3+;{-mat zg@q5Xh7hp2$i?*FA(RBM<vVC3V%cr-pV-&2vNt+a!jkv3p^O?UJNv162dm4^g|Mw+ z`w$u{#L%YAN@hXDLHl5_ihPY5vfe)Yb>2!{h$pb0#D&v=zGUxba2PZRQn}sW{s7tj zgcJYaWIz8`#Z1_TDG+9#_mO;IhX5wv(5a+%5r}n-xez-FKnM`iVP4KB<-8!tH{>2j zzQH@wT{<OTi^WPo&=|Y3n@F^A;DOe<62i46&9w}!NhU9@6@<%7&}JZH@(nazy@>-B zsPbxvHbw?B!i&XK9|Fv(A0T=$P!33$=s0a@pwh5|QTz+^_qh7UxQ?AzGFlrFqBQ`0 zSpxxP&_Sx|r&xI(H3S+m*AzC}y9`W0YrqUwO5v;i9CtzJU=h9tZ8#t|qT%X4L;E$U z{Y{<?H8X=%vQgXjMH>3_gEv&ayg0A^6Bhj&JUokof4DlP3~1rNUiA<7<hOb7_&ANj zI<Zva@ZaPeDs=ud?nJXi)j#Adj+?(=u{i=E^^bV}<2<DJ(4>wr_o@gGTAb3Bj7ND_ zgtQ%=k&XHVnkoW6eTYl`6q{@FU<9@Fiiy9)SN{tf0s}(GkO%>X@T9FMmgI7Jl+g4M z5#S=NA$CjtF|MnaSV;9sV<2YHV<1YI5IBpEB>g*aEOLZN3y&6}5YGV(M0Kgtro%s? zIA0Jnr($;v0?7B|?&9$;CvL(a7$OC>z)6&!_TxL@B}Fd#XZh2FB-~nCAO0-+#xgUp z-ZSG{O~{2P9lj1HSq?8NVxvj+#7-dyc^s;;f+SX!@p@1lV#^NSzdEaVQqJW^Kvc#F z1um^>h(bfRC=&+{F?K{&f#Tg!ChN4;14RC!F0FO%X)#>a?Sy`Z8ulCP5`hp44AGB{ z>)0s0nr{-jQA*7Fc^ss735cgf(U@r^5#1V&ofPEv1WfeOe<T@a)K7rO1y3+S3gRr> z!&&tc6fItdzD)$Wn*rVBueAI!y7A|s`1P~*;-8Pd5r>G9#uK?d1mAo5H@s-U?|pjp zt*l(Z9y0gh`+$B}S<2P`6rEVXvPU4(hJp^@EBhz-%84rYdWiTc`pJI17TVT91P8=7 z5*mk@E*;m)nI+Wk;w3*`AHpd0FL3#X0(62a`9t@EdPH*`g~so4Yt?^;wvB4<e;l^g zR>^@UfadY>N;n9B_P?6A8@~(v{mmGLQ#>>QaKeJYDK&6BKaPdk{X$@gV9J7iOr&ap z>g%kU^<=Pm)H|p=^EQ<$irnhI#{nbu`#cYcC+q$3@37vt>vXhU?6#&?e;Cp4pBz~~ zDW1hj>KX#{8LTP?ETlR(0PI0rl+K2OAS6|=@tdql(^A9)5!W^I5>paMwV0Ru6j$R5 z*8jT9%MK6E@$id0{5lSP(RPV85zn6v$5t*#13+^BfN%Uh4>Ht0h_f%U9VMKQUok!^ zFLoxxN0DD)uOe6*GDqSI$=<OCoHL_s4WC2}_jjKdUl5ny29@fPe$fRB*-)(-g?Saf zV1|m?Cv;>d!h&+zg7Y=hqh*P2@obidK{LeY5UjCUvhm;=54#w;e}*s<53G2W783B1 zQBz(V2O%**L`$&zig;!D)dGHKK@|O`ks(+)GlBjkM9-HF?KX6OmEDxNWG58N#PmEv zK^gEq|8jtLV5%qY+uq?f$B!#{%)4bn>O$?d{T4$=ZfN}UIDefz7Sq2S=XW@;;wbtR zf<x4y(3W)nVT`)+tv)d@jAuBEWV^zcsZU@~CFY3BNE@Vv8yHs+YXOIFlFATJU6`|R za&X8EU9cVWixdj#R`^i=5;Y;I5vg6OO53ImFxZVAVs9Bl54DDOzs=qka6*GKf+2y% zh(8`HYZ+e%UXnlJEzi&5BaSoaG$<n2kpu@whvEpif~1KMt7k9Rz~`ZeG9<xb3nm+I z9rh4`RsLRCeozNYM>|iNxl8==<5hoxs&tjA^w5-$;H2MSpT!20WGHCw$hb)miMuA9 z<{yWdAL;SaIN@xSkUJXBnOu}a-*reyitFe@gVts}*i<)Y*oLf<QBUDbe@|HcaIdt2 zMD;}d6C9%B>1PA2bJUprF`oXf**_66VZfWinD)l&pCg+vU>>!!iS{@>z8F<TxF+e( z7g>4**Ny)Mk~Dn!ganb_I|V8ZrCKr->7jw46P98e<FR%DiB+KFcq4E|Tc1nsL^vj; z+X#O)xo##UTxhUflnK_IGAREa5mX|n|0|vmGjt-_9!U{3X$MuTyd;a1NNAcrYY1{= z;zZJUG@lhCst{M<xT9eHHB|Nogt=x2j4zpif|o(O$@rP1s~!#MH56zdO|lWfnsNJ= z1-3d!39uIam=ai@?!ty*U`_8A4Aq3Mg>~*-f%SxjH3^Rf_|Ngo0$lx1{7KsE&l*Dc ze-pec>rX=P&OH!#b=4NUobAMpv-rqDaXa*CVe$f%BLUl=h_w%<#Nv;**(6dEp3$iZ z155iuydx@LPO@d{@AL4ZIQaPsu<6|qm(cwfk>Jq~DgFlD|99-8)(jJ={qY6t4jE2@ zL9<abFqt9)yC+?H1$uu!nTp-b-JQ@K6k43WJBeLAf4QIE)LPWjJVW$R9PYg@bdf<U z=U6ajGj(?g3+GJ1`O!5tzEvl_Rq5yG=D1tr6ZoDckLF@IT;*R9D`5kq#ZJA6CnxTq z{`NEdg5-Pr9Lk?mKZ@%GUcdRv-6=eqlHW;rGS@BYwsfcEo7TXYyR$gb5*Y8#^k<FZ zWS#@oU2xK%J#+oJ?jFpCKgV->c^!;&4l!7B{fQg7FU6JE2SJ_pkq7nXoQ%wsxy}7| z=dpkg8SR17vF<_CFw>vM0zb1(R#t$XTO(@BZa#!E=lb)kSqy@?6+gu#Cwfm^zy@X5 z*0d`dNQa!NL_5+c`W%r0Lbu~l?<6+^!Xo)lbYbPt`=I7|CQePyVA3kSNnsWbDUxs4 zFJVf?Y7lr%%w6ig#8qwG;vkEA#h+M3ZGvll7O8_Y*{lDBcR4Zr)Yi_kH~fsQM$`(i zvnZM-VQm*nnl^sPMmfe<9PxV!DV0cGkv;xfmMn9e2&(>b92OVeCeDmF4MUD_D=a0! z{v)BLGDXcqgM&YdNNS04zdzwy%!gzuj2NsdMP_X_m^doG{j}txS2DL@TSt;fo+ap> zVs(aFU5WXq6yY_9mOC`z$jttn6Ye5TVpy7?2!|?M?ofZI_GmXd&31(plSOE_%fsC| zLuCk#_!*rhg2a+kl8*U2T(fYN;W&QM(g~YjZTLgYq2R<BOhr=6H3>|tgjsbUxuOVo zO8y_mw~xFt^s@oE#&w{~pTIS3-Tk*b8|<M7O~KO<K%9p3;^89#-+cvx5ieV%h4OM5 zA0ax#T}&DDK!{BWp%_VZsGkCf5#}Sg)tqoi2!hOqmU9z(PeDPao_z)1+KCK?CzBP| z!N5dlps2B#!{)@9&566YJA3=woVW-gm)gwtC&=P%c0Uor^_;yvX|8`iJg~BFQ}6k! zj3|hD{snDhJ#jc^^fCwZR57?<SA_rSVnNT(hmF_}tfwP_H&zBU!M&V-danEg&am$5 zu<+q&J&yCkpOmfpI2~HZp3>sAV}33~#1mV*zYyW7_9`Ng1sg*9fXFQ(t#C#mA75`u z=clDYKdVb|l+V@<qK<^;pCO(c#?4p~SyHqK0O<=r_M+IZbrw|-ZufCK3g`~B8%XQ2 z`Xf=tqE8t=821BOF0rDd<*;2MIfaUm$d`{%$7fUmc?@LeRPXaP!;fyYNE@`*Zdb?$ zpBlZ4#aUUro+0>5uiIS`R;6Hmx!s^t@_NJIw^9%$BvTc$avj~#f)VHJF7-rlIJEU< z2<jAJ{{o@T$ra1mK;_3HZ3;qGB-?vX6@<v~4_NfKdH87_zR81-`hSG8#WYez5Xp4- zf!*`}Dc`@%!?Qg6HV+4Q7;@DAD_;B8JcuZ`#xr3Z<Ypt2Kp^j%BoL9?{~n*S&;U0i zS<wcn-@w@lsHMj`b9n^41pkm4!u5`ehjYn7wou9ykQ_3Ro5Z@u6NNnfO@LZIkIf<u ziEqm4=^2!h#Rp}^yf5PLrYLZqPx1TnNilBIr;__T%-C-v-blUy2dHeBkh`0dD=7qL za#`Wr-P9Win8GDp1T-(9)+tK88FxUH5X6~xCYc1-UNBb+Kwo>+T$_S$Z?8E;p!Rg{ z{Y%I`-w|&$S4a}yvA2ViEY)c<ZMnwv)=pxTGpwf)ZW~*+J!H?=%X#+jF&w%vEC9|t z584~RT{aVMf~IaJf{m+0yun6IUwomVH)__428K}p5JxteutH)Xmo9QdvDIklGLyyO zST~+<2t9PM2k2Qka@0G5mG&Cfu8Er+i!WfEtsS$p@j&dAJZYAxD4B(K<@GWI?7xrJ zs}c`g9**JwckE>SdIQW|lD0)~LZGl*;UYr7o+h6abfS$Div;P_sVIF^kpW+YSapZ< ztbn`R{wjf85r|g3vZ-H|suI|T*8dTOSZ+vRi8v(?pP>j2Wf<fPDa)|@z^x5&`ZWly z!8g~Zd=2Cb${Mn3a7-Xu4;Yl%1QsGTJ1yKUQ@}1MRFTa$xF6QIB>$t*oRu;&;C|wG z-<-S`do!^K*E$q)V4ZfCPz**r;LE57N7O^bAZP2z!}3&old(>}$up=0MV@1kd649O zUc~PFW&!F$9{Dl9hvRe`{kaQV?oZyB=={(4F5H-4J-9c6Kme*8XbV%cgO&iR!#t4L z;mFBJ08Z>7dL39{SweY!iZ_IYeS>FT=0WJ%A)X0B2yy!we!{;?*5{`M5h^(de{v@1 zQJJ7DDCi=0s<fy}ZIwMb-xUHT*7N3~gR-EQDqsu)q>_iHcwtUIY`C&KiRI2EVKyp7 z>oS17MN&CVKz~v%TB6qzmX?R;dBjELb2Y%cXjTk4s8~8aOKd%lg7iKO690#cW_Xd4 zFg8H^q<Bg{GglVlnq#<wZ{DY<%G?dC8X`|{(e>W*L2cSYjwu@A`P;X~5*n%k7VSw& z1?hnHBq-W50<xpgj@Ces96@ic&VX$=-sJiW7kSoCW9c273))GJKM*)A(*Rnh`d*x8 z1?{q!SQ$L|1$=V?d;xdFssW^f)`=;WABl96;Vw=QRFi=aj1r`?$fQw?SO&wANL14t zNs76G8H!U}5hc_t4|6zx@!R3nxEYS{;%=DMtpzto+>?R+c@FgBs7G&Xv3npH_8A$- zbMI&%$5c9cJU@p<Xd(hqg)@+l;jx6D8PPxRBX6caqRw%|%y$L^3}Xg@g%uoNFW|2f z)|S9%4U8j*q9J5yq}kCB6Wh$oIASe`5gGJ_k>ePW451c6H)JqFy73@`Suyk5$k#Hz zEAs(?h-6!W#c)<fSj_!L*c2!u{5u@sBW8qyDNM~#Pt@r6_P*oMjaDe~7c@veg9LA= zJ6MpUc3US{eI3ofI^&w|%49&}Vtz(b1;~@aNZD0C3xm2?4!CKL+QY+M9*E=+aRv45 z(`uf#q(EW7v%9c#lTaw(-Pj~ujD@MW_s%?2*sm92D1cyLV-7j0hfq`{kELi#BA~=z zC@y#cBY%*lj^rTOtIesi$l#B1&>4KZEgS};JQ<2mNx3N`0PA!dvkev^KADEYG#yB9 z_?|G^U**mASBUx4DZfbYWiqg=;EnSl&KI0LuS3Q{{O4Y9ynWKr+Mg#FWBKe!S;L6S z0*U&?mEnREd#(dwny=ICJ)MqYvv??GQGL-ba9O<u)@hY>Mmu$J`|4$z^;&U3B&9?% zT7k^yE2}8R#g>6CmLjGA37HK_M5z|;m}Sy4?G6yKxDwagLKNdR)^i>#R+fv#-WThe z5DRJcLV>^(Y3?bmdxVVxpigseb}SEw-&}<HRAhN0S^vws*{n4&&sx}wp|#afC`rE) zM)or&v-Es${dj9fSGibb55010C+w+7-_-9eV<Bg~2Q?OHWw@%OG67P?8in=tZf<jF zUel&0N^5H}LThWKlN#!lQgie~PuF9}zH(S>&86e~374za6)0g*YHogA0&2Dp6uM}} zfK}Mhzi%}XUTW5p#oBvjNd~<ffvI7rau185aaoyh^%^{Tz*^T`tdMN%1a<+P(G_EP z2rR}nvfeu8s{HXAvq9U8C_BK#9lIfGFHnd61zf?`34DdRpeTXd-IgonRjUWnn9eK% zuIm|tstA(PH%C{r9}k0n!AG}+sM5`Z9ATC!Kt_Q_@;dAYT=s%1G;*m(S?9v;A7^I+ zEUa9c(o8`E11x9_VB>PI)K_#`i?|sv6JS4w6x%7ZOFax=s6#l6>2X@tojT0!X4u^f z43Y8PRM2I)JG#F#*`S}XtsG-i(Ce9Nv3)#CY#rp8^!i!T>slmC+Y+bJlM(9lWI&tB zkZ?$LrPb2@F-=cmp|C|ZoXjMaLSo8f&}`fp-ROmpjh3b^K4??90*7tjn6^!}J8iR3 zr}MOJBND87^FbRUPrT!{J~^hXcDe(!W(u;k9RK@4a)NCUYV`hrw$4V|D)&dXbOum0 zs881p^h|;1GY^jP2H!dM?_pyFzQo;e@Wni-nwL(q%`#V$A#@}62ilBhHC=dG+N`5m ziM|XU<PH@GM???g7fgflE6HD6X#kcjTNo=O;$6i@Bn%xMH!6N`Ozhy;XuPvQhY&sZ zSTF?Wub%04tIIIj_1{OM&WDZq_fg%2usR5i%ta07-2G5F7r<$W1r+ff4KF6Z4BsI& z4<59}71BSB=vjQcAH)F~BO@+g%EDqEjBjOjXilAm$pLH5ag)al`6tU);H;m#7T=J? zEZU!>tATn3@(5_4sqoqGK5`V`Gw*V}yXHoWnWg=dWR~iZKc`DJYtWhZ^e<yVV+`64 zqlwxG{20y<<r)!QXL;|_Jp4u8o8`T$t4piP)vKR+>4i%duR`cP&)XtPi|YA{{Q2uV z2+t6$(yEq<5x^@1=|P;tMAzpkp>$6bvy9+l23G;w^9J}DwJ%uVVKl7iy%+=Qa~js( zaeQ<659EQ2h4~uceLq-xZJ0}$P14;BbMFhLh97tv*dwZD81(9Kk}MKI9Ro;^t79Zy z?-0nph8G)yKt_Cr_5fxV1jzh71Y|9thS}K`$TXP{si~iMzm3B#APc&ACDZi*ga(Sj zRU$ECsV-Kpk=4DfK$4O`4-SH$y`B=A1X^%M)@0FP-a<L=QunbT*>^MD98fuqFmHr) zGt9dmiv)V7No(b|4)oT)xyf=CWtc#4seuAUPWvrK6C}<GA<FMGsCz77{&Gl^dQT1@ zdT8+o4P0+WpWprV{YPQ@EJ!%`q-tU^rgcI<vM?0JZQ|QQc^HhCA>7eu=2219U$HgD z-a(Cw-TqE%{E_ddMn-KvP>p}UPKRWVk>WoZ)`(2mL8?1E^*_$n|92k#B@W1x9ZO{Y zkPrP2Jg_zhX;Sm=^8#%d>c7F^9&09=kl=FW)6^<aUof|dIhHB=j26JZ-o7yyxq@vg zR$1gHdH56$6&?<QLV;^kA^i1R)xm-#C0tRD@Nk5OM|pURhod;a8-j(UJ-r2q@B{U0 zEQSpY@g@R0GVM5fK+Pz(54)H#Tb6#a4E}MUlZOfitxYh%)3bP3_Ev{GgPaq27>Ff; zNQouG5bp>(A4m(Xm*qORD3=nun;?nOjwePzQdFz`E*fp+<lUMUOz~XTTQ86w_-r*T z6}5{hmt|a~a7&j{uu;@nG)0`Ei(kJ1TM4r0^!{4Z{OQ{d3?yc=$6<XIJILt$k&NzU z%0)$|u*SG-NE24iO}Pf2GpoR!Vtyw#`NSg93|*uR$L{GK^v#wN=#!#<x@OTu6GUXT zx+<A8Q|#{)E;t>$hqTyW{|3E9K(J4j$$+)#r+QDv;I&{^#$dtcOXtt81?^^oxu|v< zp*Nm~oZHgXk6#R447Y5cWgGrL1B2DxO5hh1eVQFTW*~w^61mGgx}mpm!0t#9+lf~W z8Zofy^!eWTpg_HQ!hK5|S0wrcmQPIfr?K$}HNc=5I^CR3ZGV>TbdlwQ6~J^xQBu1D zINwLQ?dh2Q2IrGVwKKBa3xqC21SP~#J}{No2$@O+oIs<2VV!|%)XQiCVOb#YBKVE1 z@JURdUk`|1EUx(byp=8n#AO--r~x9-4@qw{;)|CNE*2Qx5vt63{nYBEbC*ynn<QQK z^HkHc%PNqSzRa7_q!peqepxqDh_W1nKAuElzRkw59f+#}BowdN$R|oa{Co@_??oIa z{XpNtDuf94;5qz@BG&UfDd%Z=uK_6?shN5U{!w@}ab*KORIXas$Le#?0@dRvPo3mp z69)iPzLK*1q(86RYs}9^V7TkmRbvZ@)BZkt(auKk(t$@j%5tR3e~V{>Wg{(zg$(xI zpW9v9Z&}o!KMwzB^e@I0ccHEm5d5P98P92;{8OBmLBlF>>WO|9lvV?Xu3N@gV=F|_ zee#H*4)5mSex>_31$S*)eUkaucsAdkL<V>Ei^!O6e-!22Aw?Eo;yPB`&Nt%yEVH_C z|A~G|eTkXc!F}`I9E8ZBYlsq++Nu!#)URUu3;BgiaUJcRgJT-#hM|$`7@ow6`se5w zRxA}n`v%VaheCoJ2~pFlb!mS61Rm4L#lOn4pXA{v4|ELp`;jEgC?m5gNhoZ?9~=(f zK|Ct!Pgj^1pgBV&&tL&vlb<yX3MB(AvH95`v7T+#`6g2B;DD>AQ1tI|gxOBLP78Nv zC?Z;lA%P1#$3HqY3Ru5|*JdCt$a^*#Do_Z7FqBb_8T|n58TS-_D+MnaGV!aJybPIx zkd7!CL3}vZ6Crv+FBbrR4;xO2!e59$*<$$sf)C=hp4}UKf2h0M*8b1fUDklv&G`+_ zRUxi;$IfvYVfc{_&EZ6Mh)O$ALX>6DyM#2<-@A=169T>*8Nx(l2nXBYFKoAW&$Mg? z4P&*SiO)rwlfor_q&ZZS4SYd7HnE2Q12I5av?(cGtD%TUn#zEQ)Iv`zzbafYIaD;@ z9FTP*0+x6kY%lP9kOcw*^}_3VTkn#NxQVPp5h*IzVO8%-N8X41UChddh!8>sKDS|u z$U~40-14<@)WZ@M2UDW7O#CPaISB6@_c|>zFHV$>8Gi+qL<;!5NMpPV>A?7?PT6}9 zNFr9`pc1pM_h4w`ah75BkT}oYV1EUAqg3FGa*Ru59ZRL2fNw^xj&D3kpAP**Wvrti zt{>T^*(;BTH4OHw!E_INJ{HLUG9w=n@fhq0z%8|9xm~-ilyndSrh|v@iXJJzodZFN z5#7YBvEJbJ9;Nkd?felx!nVg9qXy+3?Uw?(%>xm2u(sp@8POvg6wKo<^AX`dA`v+J zIXDv}J7|)Kk54Q%$3PIdW~C0y))4t0#}AP@unR6xpCK6}?80x8AS)#DDJ>WD&b=G6 z0KE`dL5QaW1^yA*0GDytBkAB5a8-v!4t4N@n8`<M2b1KWiq}SW@C1s{vzsD=1U#9@ z6XA?l#Y%(-;Zd^id7}0gFy9#$Oy`{m2Zon68YC>KX>jvkDs!!sV5e0*l4pr9*hI6| z-m!DjJ{bz_^t|fQt?(=eRvTFOSy0Q9HF0+@#^Z4-b^TJrfhkl0b%%(v`^_jV0yeUz z9=8+TM!t$_?oie@TUZKpk_~yy9;iZLbgfJ#<yt<3j^0`Vy>=YzWFmVE%fdC{&t>`i z#XWQMwiFlI5f}0a?PN1G)&c5uS^LJJMuy-jL>lUir0X3=6M(?uZPUOa)moB_rl|-N z*iG48M<C|8R1dpu3Ep083<6N<GFdyCD@{O2vhA-#GHPc!$5um}#dWr?UFUYOvNs%E z;@Tt3iW}D&+-KR)<EZa%1-LF#eJIZDZ|G_80ph*PfF+)Vvtno(@bM=&4N^FXP0z(8 zNP9r)=S&|GA?zg3(S;Bl8M1>(z?ea508B5YS13{Ql5aB&PSCYYR2ex9RtNZ83$X>k zpn_3jv&Sa1NCB56!ZOAhxU$<)guoilx)?UFGhZ3x8PhM6+JSe<g=hNaHc-;+a(s3h zld}~u4PfOZP+xl9F~tyE0Nj-=Robp*z^&R1q8*-*Mg)5t7_>wa5F~++(T0b#R2$bS zBg^erWa^;Cj@~62m<#=QNk=gZS_A?XbBc%-@EvjO2Rpn3yP0Z&GQ!|+pS{1$(?VKK z84828GdlNLd!Rs_=?MZR_K~1f920AVS?2!GYiK7oK<sQcxxLyg4IT_9W(oHF-l|<= z`iYJQ2*nW-+8YEEf}aXTU=>;Hjo3B;5rOz41cuNEVnoQlEs(=V5wgy8mpG^C5>kX4 zX75JK+raBSP&FU6;+9^{7LN}1P;-A7_plDEXwO~09PZR>2P%rmL`&H`T<*)9C;0G7 zmjuTr<Re^>NUvadvr@TSLNXV31{YuLsZ*u*mY*p<s|RNsdH7yTX6W8S<l(EhI+jNG zQv+ap-cpMLpcbddGDSE>5u_oQ?wVRqCKzIwS`!bc1u2EtC~6fH^O5^8OG_ol4}(RO zxE*&eR|Qo6AEXu`IUxpEWHwtJJ{ZLq!z$J>LXe|ETo}hC{3+V7Y~2-$0eN7+hsI(6 zb=L>M0Lv>rj-t*%mIpuOR2V5WSw#5%*~Nnm{y0jo_TZzB*phJcuMdp=>FDTFQovn3 z|Igv%expwZJCc4WFCg_-j1CE%w%^S$PJ>Cavb$-j_P?T%mul+9p!m#mk5HT<^E?)C zykk+x6x2)0rr~Y9EwPSx6XQJvGK!x<MJ$5`Yex-;vf+-YMjX{qD26vsNG7K03@|%E zv~aqL8}?GE1vx?oR|Z?h86o}z0{LjN)(psUlnXi~^9th#y{;tW!iWxvS8G7i5Bk?B zwG3R0;&}wxWOwI?EPH`0J`hN5*+%X~?hV>$7PqX2O_M%j5q4qEdh1to*lR$B2S28g z0`5}-$hN)fvO=ciOaYs<z4XfT7a$J;ZCDdBZ5j$*1+AOA1xIZN_kUkAG9)o0Kh>*( zQ>!LMXa!EWQF1Sdjh_n;Kz$BBbvon>&OiW1Q8`f_2L)n(N_r5KbBoB%n8MiX95lq4 zxcY7)JU;SAW@i!pIDQNvyeyr?)F>oMA=t=C3Q|)rPSt)1Tr)swD>V^`1$xKq2#y{! zC{|bi6bXbD6(}g<KNLTN#bQm9aFKjHvLY42_0qX5C~NRV%O1DfCN73*4RKn=o01tF zHKIU@*{JSrv0my_S{%WSxQK<VX<O1Min$2Ry^446ln8!adELcCajR0|8br7fHijjM zhd2oN8v?GDpdr~CzYpf5VCCp`U`__DXAy8JL}Dm0ogi&8<dQQ(n^(;Cs5%=AFw(L( z8uDW2HlCKXSRbI}sHqKwLu;*pkj?Bb&MlyK6t0*uY<%DfGw>(}Gb0TC4Tuf}LQ8mp zfKbLkH@bmRDOI9W(JCA5dUKmbI#W~NMjP~;jg%4f#Cm5_GMEr+M!e+O0?%U_Kz;~k z<F_yaBrpbMD4B0#1&Vw~C*I-|6YX;W(!?2-i9N*9=b2TC#qx20N%tk~!|tO3U$50E zYjG-a97Q{~UjK1|XMc%@r+EllCYEb@Lu8jo*;_Rd!9!EBUV@(iqiPQidwJN$1O3@L zKb1CK?b%~wg?g9=y2A8o?j@c*f&+HZLRP~D78jBTb<dxvzQp7%XDQJ4{Ne?D4|9YU z{o-Y4Kxeu7gDmEtj<77TwBKZb8SP+If0LJghliixfgT<8SseU~o)EJ1sccuKJsQ8t z2Y!PGx-}$v!JmLFrRTQ674_ECj9kK62e8{X5@)TW?|y+V(QPPhKykH+Ye_r|VwD$L zxYz{6ZYFjEQ3pgKR1^$^Sjvz_%!@*ApFzof?liD!yXk&-8oxY+0|Ex%+gL~}Bv!HW z8vPr{5dTc<gEJ51Ug2^H@-I2_^vq{spNTEZEYEDq+2P!0VpCibRqtO7Pgn}qAH@!C ghp@~-HaE##-tr}U3P{}Dm&@hm^AG2W__y!>0_X;nGynhq literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/base.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/base.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..80868e26b668edbfdda6c1400b1012b2e520b1bb GIT binary patch literal 12836 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 Uo|)|H=%?pLa<7kmIePhj0BWD;L;wH) literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/collections.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/collections.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f76937e6d2b715e5d8f0f68bc1c574e754e79348 GIT binary patch literal 53322 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/dependency.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/dependency.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c68671265842fe4d8ae02b29c293a726d5c396c GIT binary patch literal 23288 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/deprecated_interfaces.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/deprecated_interfaces.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0e282c9efa054b04322212d3951d7a330283f849 GIT binary patch literal 18898 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/descriptor_props.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/descriptor_props.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5415c4f993103c05b4dca52b82d5d081c9fe36e4 GIT binary patch literal 28191 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/dynamic.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/dynamic.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24139b31f6a6d914a978d509bd407f1e3b96bdc3 GIT binary patch literal 11435 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/evaluator.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/evaluator.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c671cf1a37dcd80ad24f3640f823f31fb2abdc31 GIT binary patch literal 5791 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/events.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/events.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..60f8ea875ab217c5135ab3f59c944ab3f59ce998 GIT binary patch literal 90870 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/exc.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/exc.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3094b7b1f1d75d7038d6f46cac7a3ac5cafd71b GIT binary patch literal 6477 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/identity.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/identity.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aefe3e41169133ff53c815220d81365f16882af2 GIT binary patch literal 11408 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/instrumentation.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/instrumentation.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6867e05cea80341582ac9a5ab0c710eeb26d4bd GIT binary patch literal 17943 zcmcg!TaX;rS?=zcotd2-jaIAGT6UbIEnD%fBTXD9&PA4G%aRp4iPw&`j$x2#_0H+t z?cLd#)#)B-cNme7HMfMMiWCn}m2j&{DBu;|sDk1diYKU|DJTkxhwuOoJV6x&-}j&H zp6S_@oEKPmPM_{Rm;e0dzn{PTsS_va`+xkpclf$x{j)XkS4RD1{GvZXp{%y0Y~^(A zU8n8rmfEG=a=W}c)1I*{RZ?X<ue2+u+iC{&YP)J%L1nvkXtig*V5y3#zHg~&P(LiS z=Wt(Bv$&rP8n{1!`?{LL{hZu4gC=^N4657nWve~kK84x>Y76bts4b$l*gm5g>csn2 zdr39bNn9UM^Xe3?XVro_jq9Uo@uszM#{XW)vaSakVK0c>n_v0z)oy1i*gbeUa-)MN z4tCxChTG}-QRIfbDAt4Bpcngb*zdLK^|h@qa(DY`&<$MF2GPLpb`RWMKXz59gHDW_ zyI~Zr<9*!6`#`(?-d;EC$OzE|cYP4oRXVxvc744WxO={iIcvNXxNpAw1|SK0aiIN9 z9NrD%1I#-O$=-ph0>2x%_riF~)xl;6NM#C+6ZF(YnOc?+MD>lnb|WkTKw;FKu(#QA zUHA1kpC`hK`%wVsWk6}$+B>NGJ>~jwtV4huM0n!f+X_2dZh#i+-7wm6_X2&9(D}Vi z;IcU%$h;Txj?>^@0L0NAuod3Ll!Dgac$8;b((!xlI@%1PxW5|?1LgL6u0Niujs<RZ z`|Ez!UH2o|87xu|dI{Tg>2`Fd>zjRU1^aPp3ZJ*@W=}7q%iGiayP*Pka7T6nzlT~M zTNieL4y=*DhZ{go7u(b$UZ)0hf$`l4D{HmfyMc~CJ)JGTw;8BQ?z64W)q(vr3_aZo zpanfP&ena;C%N6_dIuSaursJc00Eu!1xWS;BwhE%TP}v!10ndE0SMvUt)PcN{5X3? z(hhg``Z^BO1vey_V1v8eh*O6#=ww6aBm|seykf;}L})M&8Ub#N9qaGz=>QW%*lTvj z&iA_mvJ((XoP-@9*WU@;U}FO$0i6BFhHP0WC#7J&lgxGc-7ZO`--{9u5|+8)cY-LH z5hDGO!?t!(#*kfJvHd{@&+<PoyOCJ`fMElxZ7ZwoD7#$>%G)#T3d(A`7R(0q?YW?K zXdl|`hH}~`g68(g_B?8*D9Io_PO2GI!F54ZRSnnE!J?W~_4h6GI1?<XIn|K*BdDKH zO{t$n{iK?g`lFcPF?C8U$lZt3X|;$^9#^;28MXAj-Ttt8Pd%c}qV^H>sCo?VKB_*X z9>>*HA66g1^$GP+<>GoyMtVY>laZcOPpYTz^eJ^-eGJ$0c>6K+v|5(8Ps`nkdPeS+ z<?iF^g50gBpH?mP3C#G6dRBcBeLt?AQ=h{1g7Va-)$@3BQGG^z7EfC0bL#WBenMSR zFW~yDx~yKr^^@v~dI{I()XVBBuAfq`sB5@>THRLH)vFlcd38g50Z%>ye7&Y#mzh7S zeDy{3CA|HdT326IZ=m*hwW{7kyG!aV^%Y!SP;aZ7xL#Ik>K$BPR2}tI^)6aoQ9q%6 z5>H+VUTVLr+UloJzp5O_c9k@)QF6cG_dwcuctOM+1iQ$-ut%;mQW7*u-ZpZ+8$b@V zRy(96{>|V=vbly|^k*nW_Q>+A&_W6FLFq`nBxRW@5Q?hAa%62=+cVce6#SuOW&bRm zRjym_HSas9RY%UDZCiJ)zst6@?b+=*M?Z9IYyZN?-kw9tbN5SeV^lh{@0UkqW$n!B zp0qtNDnog)cTajlCE6V2B(JKB-t#*Z{U`CM?FBV6vX=k>o?AdnWwk@5;vW}DEq)Wf z=q!p&4DsIleS3tZVJZystInM=I@zngZ>>0q(|R^3?QUvaM`J1eYxmlvTkk-(MYo_f zpFX&CJ&1PV{@$%u`u&}Y2Km&Q7jKF}y}0W`uj*TOgWla+-EjR@6vn~DRQ^P_#v<ev zw&&KAOoYz)uli-EnwX;G$`wLQ@GF*69<EI8cFXg^UKo2`>vFf>fpWZpO4hHieD1F- z|0Af#@|W)0F?Kn&4;@{Ooe{)<L4vGzr3A?ni4cUAOUm)VUXaZ18Y1)h8=jESilgT+ zp02UTaivf2wu#~;{0!)}V<%P5Q$g4BhCfUz%PSCw@m60{o1x3|)bHJ{+wq~z-9b+W zJ|re>i_Sr(3-v@@v>QOs9=P4W-+_4neK9s^j_8;E2K0;D!8p+75XBpT4yd=ya8Lzr zVHixMG3H$u3u5gEP_32INkyRYk}?@7sr7<;g79Qc(Cty(N6BnohnpcZ5vra?X&iZ8 zyJBidWw56(cT!VfwCBg2E#Lvn*Nda}V;%>TDKgCF7zeU;b>H*!Dc}%Y(org=VKNUm zGE2>XR0C4O4roDF*`s{V*S(dQq)gSNPvep7QoAbJq8sT39~Jj3!5D^~K8u&pG78JC zWd9u27Rn8~>CBfJb`Ad)SUPpPQXZbUHX(&_y3A>itVlK#`9g95?SWT?<)FiyP_5Yd z9G)Ko(v}}>dEQUpWhCOtJ~EkUkqa}nDojRkvs07F40Ss)S2ELwM4aGqq1>e6dBGje z`>BZuPfx9m?~h*_F{&`#wzX5igwAS$E!%^Ii1oR6_kd;2hLI=j)KBNrI>V*Hw0V=` zXFiJ6*$v$v!VU@I2h7^=VB&Z?!GVW1>k|_{T9|mURblR2V=SR>ERK=r1N+dTE|C2# z73DCyh#-0l6%j-Wl*3RVHs$cPQxrmF7-ik0KDLOG62ML6PEsZVPEm{qo-kWW#TtI5 zxR&uy9>?bDqvOOU=6D99B$c~<cMwDmz~CA0xbZePJw>+VIQH<|2Ltebja6$BtB$?^ z$l7Q8PAAxlBQJvH@U_?N`wC>5iu6yQ$#IzN;d!Qw?BVmpr4>dJ^gax=`dN(i0K6_1 zp)zCbaPA5UqP1cVpZLJp3wX^%!aPQZL2%$wSSH_q5n$v{NwZ0^@J`RLq7474)_pAk z^mtG;#pE6QjBqLqi?YHne=eFRn@JcV2XCXI>`nW=v;P()#x^Xd_pZYdaxzOOI(KJ& z|Jk@SDs7i(U%^hAVSP07sCB<Gs*LQNnr@Ap?U@nY!%Ax1X}xRRnTM_9sM4ccSJ}6* zs+)8t<1O7EY;L(=`UyP=Ppd=O4fP1`ra#c`RtQgAceW1by~tk_@Sgr9{c4(#n{aN> zdVL#?3RN(M&z)cBWN2v}!~@;ShhV4mehg=Jw;#c=yVnaeJfCcaS6iE{OP9v?e7@1| zyDzd__x18h@tuI|zU4!k$CvU^ve5{N>3UqRYg_PYmrdW5qn-DFjBz(<+)DLx3tWP@ zM&JQ_U;`pstnOvNxZ>XH54tJ`g$P14Bn5F|rK>@pfb;bOpe^cmLSvo7|K2~K%iRIB z0jG#dY=oNw_>**lfxLWRGj(dm-!qPEFh)K-S#mZuR87vRse*s>Klodz7<=GxJTT@( z3F2S0Q&OR>>Zz5gQINCwo{IC?*-2{A0LrYB84xXuk(3}Jr({L4bUdc+qJ2bdVLei@ zn=l{BungzzI%;+N8$MZN>0_~-GZT$k{yO+YTPR@9lOdY1b!gqU_t(Hw&?sP=*T7=0 zz~peY;bMS&pqlWsb-x6S1BN2&@ZAV3bH5x{M&-B)x5EKzl}6<a=c{lwgt5-P3x=gy z_fG<yhQdkuu<j9y0&5@?MeY*nT<;IS1~~>9T7eG+ip{r@3Mio4;9im*mu|OS@0l=B zzjqZhe0yRCB@h?(I^6-oG<k=w3<7;X3z_XDXmy+XcrO4LxaRSdd{Ub7@DB%wY2kf7 z&ep-Y4wVNw#Qw$y7^F8Q=DgL_G0DC~ik$>-b!ApRk6oCcE*G~+U4@8qh}oT#e5LeB zHlh?vn#JX=%owt(A`HT1>d&(!X-r>YF=ehUieW-$N-S{`vHJ})j40u)*&38Y(_RwF zY8+7)G<~M}c4(dOkHE1mVw&^sFXsaKEU5-U*n#6&=XGRL(B7$2=-y1}WBB}-IC2VL z1dn3S$S#H*g|~IMw@1+$#;w(r(iHYq%7PL>LQ+K-)gN?YgO@36oc1<@*mx6y4*Cy& z2+$)MhE}NwG&G%|TV$}KU2^WCF3eRXSyrs*N`G;L+M`fc;I&!u!tmDHBI?~WJwSlZ z1a*ym7%v@CX=A{^F&&K1?=qq}LEK>rVL6!b!c5?bR!**~P`k8OV3?f4#Zbf4(wk<% z=r4u>1N9L>w<^OA71v9EysTZmRBm;?RC>ttj1h})phYQA9*ZGHvM0j+e+BlxCQha6 zyYS))XcZ;tgT>a(PD`8{2$%%rX^AX~zLG{0xG;>4_%tXKiozd2XF^HvmceMK-5jmJ z7c|7!6-4ti9+~5h1UJz>4J(t){Y_{xMV*sD3b}xX9FBAvLY&<jD{ukxqBy>8s`n5) z&WISZ^q|;e>Bi=P?1?)q4!`9?6+%S>z8aeph>>3G^m|6h!KXKx6jYKL7^IezQ*wI* z?z;PicvBPDgZSNpMKK;sc#K%PxEa96kVQ_S-r$hX7D$l1C(icS3Ex_jvxo32g!@6V zWa0>!+V=n}7%oCbl4epuJ*lQEnHE;3ybQ}(_dh`&!<=(;rlI{lG6`nuNQZ(on~NuU zDMmOkN(WmJtjXva?#igoh&Nhhv`y?>XH`Fm9+?rqdU7I*V3<%s6OzOrPVV&?wrP?q zC^Hd#Sr>O!*s;io`lD$27x)=7y#kSA4?kR7)tFjk^unTYCZu=Bh3F-e(CQ|vhb0vk z;TYKF2e8II7`kM^GY!oNmrVaRIj~R`*n*E1fj!c`0B*hqA|Azphj64<0c^*x(;+MD zRcJLXC#i3QI*JWSB_KoFrG4UcLXMEsO-5{w_b~uC+Q94t!!YVYYWzC_ZhXx7BVZqC zT>v|GKo&6u12M)G0@0Lsaw#(X**AQ>15V<(ND3g$1xk~Yz=czJ>d~S1K-HC0VMkKd zhz?5{Z(Loy`h{2D_O7n2y?y+`G==RyawXh(>!|ZY^SS_gj`9VxAHy!>Z6IH{9!8OG zEbA<wXM~H3c*w3C)J%YkkYo<B{t5;?9#ZID#P4um_%CRZLAzKWmb5_uTpHci*n>Ea zQ$N#A9zi5jG<hZqsJmf9K49)<it@D8zvdt~Q~Isg?t>s1{$;ATeRnzA<Q4bg6}Lc= zqK9Z;PN=AJLSU`|`dr=uW+hA-oEU09L`*e+a=>Zumx2RvqTRmEwbIqhST%28H1zt@ z)5=IV0^JZhI@JsL2pPa*O964jGdW%j2Rsz3jmb9umg97GSd4y6w)!B9!dOWba~LEZ z`%n<(CYt<b4uLZ~J4INC>zZ+HbcE&6lyMLW5TULvDx;EKq>0AJqke|6zPB{u`3r2B z%ZABqoWxKv(bYkGj!qAaxy#o6GiZmyLB{RgdmLvlIKN>;0nvsToZp}yL~sdC&}(pj zNX5?T+Dc7R>?I4b6Hp9rOFFP+A)Au3)DnH#coJP4$H<bC!Tui71|S82S4cf0Dw<xd z>=-=PuW{f-wn;^GZoxoriQW;%6Jhx%PU>T0M&Ce3{RI|Niiu&>|Hdz(%(LKjB5IAu zbpzL$LkDy!nsnUUxxGldQxJ9Vi`G!=uM*GO68C)X<@*jIIEXC4Q7Fj)6Hc}efS%R3 z(v=+9x`8vY(sqf84H&QU)JvSqD~R4SCU9If?!bGP!x$&0rmcu5bDCxmHW=XC9f$9N zg$DX0_U<$yd_10mSc;mcSn$1T_*H-qXpy4*ab%`^{S?+Y?NPTeng$Em0$qUu=^ltG zN#$KxUq>5Jg6+k8Cxrye_R1n3X5gQtg-7jY%*C<0x5$+UOE4gKzQ8sQ>XUnUTmt(F z3u#ou!>Q^9NqppJH)p5kgq;?+$s_y4l$ppt6Rad}rWmP&SVDTJsaJ8730A_Eg9Y!b zPHb|@NQ+}DX3R>O#}N7!3$Yo-l(1B=O>(rR<Y4@y+wAv1p4h<KvqT`b(2`(`;R_Rt zsR(hDr-L%bPn%0d(gt}$kV-x#(hhlAYnM^?Kp&n^3L=_#y8l!>Ls<kaILm_ow5~vY z(cK2Jh~hiTS<q#8IX$I=89L$gm;hRyp93cZu{E(=3goZ~f%OL9k$|lbqoyoKW^k_P z>!i$sQ;}$zczGB#)_E?;{G&iG6Mf_~t5kC!OdPt|!$%4ek6Y)IM+D`RM^R=d4U4un z7*}{ahQKw)+263mq({sOA_XEi6~DuG&+AKwvEISi1iiR?|J|Oyix2}?1u0mt&Lq2O zEX@qE#;wyFtcac1vxPIm`pvbgYp;sgCKgdr%T&023$Pk)H9n6aGmlTF1a2G8K27XM z_yMF;c52ghD#4;lF(&tdh{?&w1nh|&kVNLlz_51o+c`WB4iD%6jb0!qVq{KB`s28+ zuu&Cs7iuOWJ#3=@AY=13DH{3{=L&}<(YIA||NpdsHN8!&(3aIaGSQEqNb0c7Q=>Df z!k8cl7?Hv~O<h>oPPlqmTvDiIdS}0c_E(5b?l?6FclNA3Wr}8dYuv?rUkjAKjQ&84 z>?@GvNWg`P@+l}#qfEv$kNqy2rIhyid&g5^Qp-HqX`sK1scsNx?xrQ7=PAc@Ob02* z1eJfhl5&IwxMn#!f_^vNouk>H{RjdY$Z9l}%|r_2Bxc+5V@t?H>ZZU<J~Zak$&<wn zU92xnknnOr!aSr?N0cJ~<hpZ_a}GC9Obm7t`S4gNYhewR4@<Q981y!)E1A`ux|Zjp zT~+=bBwtbk5lOgh(j?Q=sYx{rn6zh^lj$M<!B48b={LRNGvk2Z#EMtBRwFr5+oX7k zdTgfx*XBD$<pyQmAm{Q--f%E$B1%D4wut@7jY=x!#A_PZlQ@GPBRf!QV~Q%njvPK- z+?HbFoCgS64ju{0m`vNLx%7eYPEA{-JwXk{<cn-ya+Qph<N2tpjNg0+wVRSK5?rvQ z4?B>p=PDz5FQ3_&(sbiC`C7>v{0Z|shf2!tGzu7Cg^)ym^_-9(cpfr<<X8eJilk>m zogoxs2Q!6;DKnma6C))j(x?a|8-3?gBMAl8MEuadAbw53w4|hAa(ImMK@6o*jAu~| z{F`$A3az;g{uS2r6du4m;wlgsr+p}^p!}jaBMw(aXpX?Ge+FGLXh*0E(@X=}#Ht2+ zxu3L6P>t*&pi{!O*fvK#i7ZEcsziz2<CN$&fl8IA05yfUQ!%V0g@AAlQa`7IY0u$E z(*kGa5OD@Z+KD8y&31CiyBTP6KJbQ#+GLoLeXSdBoNn_p!s<w3Nqh3FGgJ0OTWs7W zM^a%o>C6d8f+Ri9Xc!O%YTPbCFMS&IvXn?Rl(R&rd2$h+(@i!y3`vgJ&dZ4q7grh{ zzHY1?*GD3|1b?Oi9ufrUA)Ft%&?OO$1t%gdSMe;oix?nHI~=`-lEs#$&q2zjMsMI| zDO{KYN69>toEYT>&ghaV&Ob!^bIZj9VBVUj7;x4KAJT3|(2>+{5FT!Ly*^^X<hvh> ze)Z3z<$(LJfQnUVz{70V6<jN@lR>~!fz~5a<@-}0;#7L2M4U=5eMqQ2h=6B#>?5oB z)&zMJQ>^kPx!R=4%+)6S1?)8V9jD0TZaDDY=I%P}^4go%-$eAM-rtMEUHArJ2izor zniJD9=*8J#9!c3)&cDdo6sOe*h3W8}9MWk~J8n}D^Js5GEn!YWxi4OYyELho%&8If z^WrejeK--D;A|vUMsj?eMdnNjacfP$dq7|k34`nXK5{z0I{|1>8)YpDU=jh>@RMv@ zlP+h>5c)cY_C{&R1eu2Om(L4S=kZZV?4IAh0u;WFj!adgy8?n0xA-YMdoYZ!%(UnI zI$<PUEaV&<$ssgtsB8G&jqJbd_7(k2PFqJ>rhE(Ic}c_b?%-n>1BPU`_B()wR!e*F z`l~mtzVqcZZ!!ihCV6`ydo|%@k#-Y_M}{54(<X~kw60Na!ga|KL<bZ#;%bFltE6Hw zapY)6;|nHBM=Z{>c$$OPvV(oY;1X5%INyGf1r>90Dv#OaZE2wE7g)T^;uRL$HxUqW z*kfF35etmv>zgQ&hDjz!SMm;@zsuq$Suhf(eHMzv1`FovX?l#}t7&=|nijjh!(zbV z9t-l5K45X5#m}<%8jEkRpvcj`z~Yx!{4$DU4s_D*k<PI--{Rvjw@FQsm1pr{1wY9m zK~f5HU7e<bD}J>nYNcAKQO3WSMx{AZd$PVzpRLtu^R;?y2KV^aEH~$x<$AL=TPxS+ zYmHhJeQJ19s#WS0^sdyab>5X~OSMwHTq~Cy%~?D6<$n})FXI=vC?-GLP{`&&%70mv z)eNrqECWKPs=tnA!{2$!e7fUi>d(0N%tRSycTBQ>mb0;jvz$D!LzGi?3U!;GaYgR! z+nF+1Ub%gn*$deeCWVDrUTnjYY0t~rz?skVaH@H7?N8<_;YSjZHA0C(pn9+Ywk2A+ z&B*kW(UgWjC8$eVKB?@5ot<tlDNGYv9UJw1677FLw$Cgl1PLEIVquKal|?$B59Jf< z0%#YN)M91`%l4e~BZZp2z(YO;$DtMY@qzOVyZ0F4d^7q6%F1@Nf$*Eu@C6{l?Gg@d z<H>Ap;g+}Y9S(I#9@%hKhx^z3o_u0AMrM|5$A}G5cwC6Yi##<kr|rn4hf2T~W6XBX z;-uy{UP57_-m_1bc#4NuF;$ZL)&bH`aD=Y=%o%%Pr6F-<BUJ_GB+BGWmJ^xLiJaRQ zSwbPf6y5ABKWR=zYP(&-rrf%AZO}_M&t&=-%U8ljKZp0n?I9(-A+SH-M)JtPM6H=Q z<{%v%!TT98iI3e_({&X-D&@ES6&BxS@f{YV5B)tB$4P~+<Hh$g>Zw&A31)=lI(VM{ z<vjZ=9zC3ARTaEjF&}-ltEvtjo=cX9(TRA$4U=myd^)YjF)OU2f(FmeWTRGYCl#3M zHh>Y|YBp-b7Gj8uoQba$J<fa3tGN8P5OYb7DQfykTw<Lq<-;Au9EB?J%<&|sl?7!p zzpR!tkrIZ4F?_6MNa7S`#`#a0xhLso$g{{&C`9Jr>jiv&+Sw5@ktn6nRzf24#t;p2 ztYmyHdR{0}RNHvNM1sGHY5$yiMH|h6fjM7pA}mF(ya6c=gY)9?h;AL9(~+Z<`^%q* z0^(RcyDANzdlLsO$e2@LIOcNZNi@uOQGF&dWs7&a_(*85ha+-lb=8<tsiP%pnp(+k zVUEA%N;s?K3_tMzxF3Y#T-uh<9pg+uPIuoKzVI-}@s&(0Bu9Sc-bm7~&}4|uVN>u^ zS6je8Za01#)Bf$m8lHa88m8zV$2wu>?;3v5%P8oS30XQkWs`JGN3LniRSsij7<JvQ zB7lB5h6@(sJ{PL6>Sm2-3o)G7Ddt3K!4TZ!v4&8o381=c{5Fcr4V5(u8BXmRhy8o5 znLY|nQ}|C@ohKf!`tiX4&nCW!Ueof4RIN!6`nOpK<l||jCe;6Zy!}(o%}2CfYNu<k zF*Kj&hIfIIX{=GH0jCfR6IpKQ6QLCE?ev%<O~eXwrW6|^RqrN=z~qJm`*eMf%!n*5 z2rVS68TLwZ%(L8)v}cxbWzGk=Solm2LU7W|4qc3|`X5{`jcu!H(x>$Apq{T5Z^wMg zvrZG~B%P#w(G&0F$2bpIA5R#rqpKhu?uCFh#y?S?Aor*U7iTrRThhOa7CB85|G7~? zF_jq=6vh0aQe4T>@ZW!objmc7)MQz5TjXQ((4@tUw`m*}7>DMvPoC2~kZi+^K~KIn z#{L}et8)n`6#c^SptARphaunjV~+fP1OU+|wgto0_>7uKov={G$2-RJ<f#>M0J8kg zHDLBG&;bsk@(}+{%za>AB2hdbn^4d~@pK08H#)#|qN$Z<V~hdHkQ05^2odAtxc zV}9`gYg3MH9a`4kra^~9?<MR044#(tYdFDi@O3E;AMRIBukb@?)T_O-@}_F;q+XMH zEv_6Q1cZqCEF{m&XqIwkG^=KjP+muB`Ru6f(d4Up^-)#-+Ng@!9Or_8_`^|+KRjuS zXcN?jS6)fKyFVtH0|fI+UVL(h&wYzIrLOs|ur>TAc!_z}<@3wjrvne?;VV)4y+a;u zabGmFpCux-js|@&06!x1@IP)qf7y73s<PP7_yY=U(tz_fErbfQWZyh$n1i!(FB>iB zM!}I$vV)x51<96S`oTfRF&4gO*$Y%s4R<|~aB_lWmVXni(nU{WZOAHtUFz-J8{eBe z$P>m0{7uk^@wrbU(+K&tTrN}bWerJr@8G!|y^BUkMN(j+q}=K69VF#oKj?@tiO^-_ zdpX!#y8a<Y{XG;bb0R#F6Gqvjd0OrHytzz+&qilu1?zovkXG;dI!dcC42q=82KomW zvpp|uckbnD#Q#5_ch{_^D5dll(SCvKLSx;kRP0)_%tJ6Zy8NIMY6{6P{0aj(^G+GL z6pYhJuho^u%<?SxI!ca%BzfQ1?o|vYA&=w<*$q$fdzdaZP65ip35+@<WeMKW_toEJ z@pTqdR3d8AJX3h0CRV4viZ{uLOvK4IuA1M18d<@3t;l^5wI)cwwN8i&!GwtF+GP{F oxkBX^okLMBou!I~TR#6a>uc8C`E&Ejm6^JOf2G=qTBX+bUzBji0RR91 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/interfaces.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/interfaces.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..937e6d92681c9038d1c4388e651509f7b4357827 GIT binary patch literal 26213 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/loading.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/loading.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a21ac5176ea68c9274b2d56044a9421256edee8 GIT binary patch literal 17575 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/mapper.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/mapper.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..898a48ad433a4a45c943ca4933b9f5f5d03f36ff GIT binary patch literal 89127 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 V)UV{bSAMp9Z}~r<znm!V{{N#P#&ZAw literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/path_registry.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/path_registry.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95d858f1c762173fc124d9e4aabbf177f859e63d GIT binary patch literal 9996 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 Avj6}9 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/persistence.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/persistence.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..75fdc631d5abb6a591985d92d16d09e28e7da80f GIT binary patch literal 37162 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 WH_@25XJUNf?8N@5V^ig+gZ~f9<NSL7 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/properties.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/properties.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77478821a1fe4dbf92b6d942e622d8e57d970797 GIT binary patch literal 10214 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 G)%<@H)j1~s literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/query.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/query.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed1254165c1e2d9f79f0280dcaa5daf1690be580 GIT binary patch literal 134821 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೬Zsy<D5Ca<w_;YoRS|bws4>{ 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 RnfQe-Pk%K>Z<~JW{{sNBzKQ?< literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/relationships.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/relationships.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..31c4846ab631662a187ee12546c9db43ed532b9f GIT binary patch literal 93046 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/scoping.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/scoping.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f47938e32fdd473b05a3324f63d933d050ae548 GIT binary patch literal 6347 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/session.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/session.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1ea5dd68f7fbd43083a8a680dbf0a4968ed7ccc GIT binary patch literal 107134 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 Lda*U%I`)46bZ9vq literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/state.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/state.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..45014a97d7f5b4433bd4db62c122fe0e191ee25e GIT binary patch literal 25536 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/strategies.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/strategies.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47060f5a80d3adbb8aba606ec729c9713396cdcf GIT binary patch literal 47031 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 Vxij}gce|sHq#n>UzI^q_{|}Xa`(FS6 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/strategy_options.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/strategy_options.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..280d78031e7d442104148afa541b3a285b759222 GIT binary patch literal 39294 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/sync.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/sync.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23b2eeb3aa8db4a3f56adc97bfd96b5308793fb6 GIT binary patch literal 3604 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 Sw2XUZ(`)YHx7}<v_x=svOnuA% literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/unitofwork.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/unitofwork.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fbc3298e9127702bbfb87e7ddc33d05e826cf8ab GIT binary patch literal 19821 zcmc(HTWlQJd1l?Zy4h?tDT<;lmgOE<no%1XS@GD;#8w8H(Z$hthMJff$?0fP)9Nac z-E4MKr-~BUZpKbH$!HM7n@x5N<l@)~a<La)uf6fc>%{AI3}heoAqetN4*`Mz0URul zh4YZWPx-$8R8?Qt(%8-<ZlX@tIaPJ)od5j)_uo$QshOGL_W!u%edkrf_*-M>lf(H% z9HDC(hHumi-}J4vxnb3;4ZCKWtlMpC!>Ktqcl;c#<!ZSNx8~xWytKJ!E7YcNp7#sw zVy);;`Nh|a+VqDEf7&m-X85I`v}f05P@nP3sF$T)Mt#<wLwznN<N7S>2mE=|=jHkw z>IeM=)E9zsFt_K_4xr_bzlfGa)a}|l>WBR!s2`E~LDY}>$51~e^##=5<{wA>xYQ4! z{&xQa>L;YWi26JH3hEW9ANC8&M)k4YKipUgD$Pz5s7}2dR#v*I5}{?Q(~K(JmCF6D zTCapVVH9kfD;9Zg7=&T7+j#=_wztByGu6tb>Ne0=*{p}SSn1wr1&t`IMBPfg(&(b= z?P#msPTGrWfeOx6?yof)YZX4Qai@)kq_0X;57)!Woq8Dfm2Ri9QQzDQR9LBZd=9Fq z_$stS%^*Bm?5fpzr`f~9xVC{gRJzI!6i4j`n?c79I*pyP@^G*nG`6B3#1y`5fd#5o z>}*BNcAN|D1)ZpB#?w)Ev)k^jHXHT!x4|Dq)sBnxC{oQkd@!EoK$;*!rx7UW!i?=; zyYX$@YAiL_!Vh^YkHf|hwovrVmhp!1y0K!C+g>Z|T5@jt4$f^k&-pISoycrieehM^ z@(cKz`sJKyByGjMy{~P$?+n`f64r92cl$~h)$g>M;aWxJRlzAnOcH{cdR;1mNhOQ0 z2J*IpVg;W@J#5td;K^?1$uNlCP4ahq5KXRePW-qyINyUf!c7!?(>K-&s^lBXM&D=| zyLR7J?Y`YI)#E5F-`q1zUa?zFEBC-SYGAf*E8j=^Zm#dN3au&J@3e~H%?C!`YE7?O z@_ebE+cQgsoX_way_lG(wbVP%=(gKnGjL)>53^d1^9gUpv)=u>>UiCbCv*tovL_5C z&CSkgoZqN->Z^f@UCm>$>yfoW#mU9xW(S1CN_+T<g1F!%go&ra`n|y0+Vt5xm$Z6! z>Wy_3tc0<>9_--dWcoV#NFG1r$x~gGJgA$iZk*HIJ55MeJk^BAHS1AV#SZ5YI~-A5 z<i+F(Q!QY4pEtg{cIgARUk<UZw|AZhpV+y5ISALmz_*|4cGsVLQKq&5zJ7991oz2} zI@mzn2CLt@-EQ8w9iq=CH!*0CAiN#kZP(ikh|tdMuG+Xw$?vXE?$2%R#I7dp39j`q z6oykU9m_Qx(>04$*({m`bIvT`yLT#5=rSc$LiuwS+FgiL_{=%oQDY((eF7a%;3A_1 znP}OXW583-fSVjC$u+Ke+BZ9vZ;>yee5;W0(^RXd`nUs}M6SWPt?u=6c)GOi;>>Q% z@LtpCxW3)bd1Z2L)fwd~6>N0x1-&CGXk)Scf!qLH+CdcDsLrY)#;s;plv&KOm}XHz z5$CtUfD$N7rRLavfbBE71Fx$#A$xJrYli8$<8^}jaX#4I-0G|b@$sZR@V)L<6gK_9 z>#leSmseeNn0+2$ag+rYNWG24aTHa}F6!-U-~z_3p3{499`X(Yi-*Ou%2pBIMbov5 zRsoAyHcRfCEvN3P;jkNg3OF)Q_&=!ht-eW6puh?!IDJQb3yVTfz|u?rg`BX!l!`KN zaQis{hX~)r@Mi!Hwtz!~9!h;zz=7v8JO?;f0HP_p#X=9|>}*!f*wdW)mh=sl+0FO! zc=7-k;#=t5Y0dZZU=@z=U|(1&g_=YAIRM~TUC^taE%!Jd5J?7$uI5=KWQq){Lp)nV z5l<~$xq0Oy-tv_j@qw3~U0%L+^NP20-TTOu<(FT$p&sLX?__b3#VHikf~<S309LtI zZ*Kvgx@}+1#@L`%PH)iwQY)u7jsVIKhX8pZZwW66&!RBwMA#wl6!yYE+`%VB)~q7< zM#TT*qs5;ct9PK!8&JNss^2F!Mi{kv9F4J!>q}Kz6NeZPyIOfwb8*oR!iH*Y0va`U zvm1F#y^CjL@RZqw&le^Qg|ZS-OpTmtl@?0JOO*w4!Bpqb(qMD?$?U{0q9rhD&~7!b zE3~&!vjfMc?l$(!TF$5DHfk<yL44;ETd<%l!P-<%Y)wP&(+)IiC2jxJX0+W`EBjA~ zeP`BYwOv=6)An5LKrr7rsBJmCWkFkUwL{u^t1bGE`6uzV!~Q${Q}{mOpY|We_fdZV z7<MKuzkL1H4OQ=iby1F)WkTU3T`%DXKZ2rf_6^TyLO4A$0?J0t9$Rul=dN7MOIg5m z-a93=BJa{Pk*B5wLG7gxLh_Z%d>zp+GWO(Hu`o<?3G4MFS~cqlLJeT5aG_n_xZ~HK z`3xV%Vf3n_vpVN_)Rvw%P%r5-)smJ=5kHO~p7w*4`c^ygn~g|b8auELL)rjhUdFSV zDhRtO@-)f9cy6;BMk6hy%%X(p8M~sl!+4(ejVSeymL*VsMGs<pV(G9WPK=0Pn6Bfx zrCcfJ=3KXcqj&frQ*F>H<%i##n5-l<P(*-*KGTH70Pb|VZS`Zg0xEwHUqe(D`6#T_ zLysE%cW^OeUxu~sz3Dw897ME}_dbUc+P3PMoq2qlec_=!%QS8ool}kx!O~kVstYED zE4H85d#j>#eT?*@hEGPYdpCgsTdGru)E4}Z6>&aRn=n->LgY#_tUwz!6*Vy2fcy0@ z(Z9Y}r4@XzeD|8&t+2gwE^E7SwOSB8$|vI)uidTtUeb{^@}^U;y?*s-oQLIu%Z+&c zx_B>_MaBD9lmh0E#_=IfYdk9G_Kv4jxL>t2fkhFIt@c5T)dRi-bw9iR_tBLGA;&c> za~`%+8J1M<<k+H5cywePvh}C(rzE$Gb+^07@4k5yp6pRtG*<7{n^SkAuGUH1{dB$G zjMm^)p$IEm-TMI56jjctDZCflB~11R8WdsLB&7N>r!Kta^p1{AcW_r`AJVYkA>2k~ zl+IEuq_c!!VeR22UB4iH$1*(o6krGeRYxxn4d-|YmS`IeQs6-Y1<|-zI2$nezKR<| zS~CU}oHb|m4vr0$cV&xV<3=7b%nDA@!Ep(8Em$Ph3fwv_%LT)?F*K+5LCr}OI#+N; zDhcd}?_E#WenoXRL>49y{s!SwNN*3=FD^7gP3rM^EhL;P0+oW?H!#*vtT(%C_1-x) z$4O6QwjS4>N>b+hS2*z@{(Wo3-i3|Fxx%J;Aaiz(8n^A;+^$P&D*{MeG~mwtMc>xv zIHS{R9ffPSTj&>B4*D@$Ib3yd<exM4@N_??6s{Fe=2}yG#;)6O`sfn|z1z1}Z2hJS zM(6p-y~*1KeSJgv`o^RC>boL7*H`NFhb5Ge#RAT)!j#dpgk-=d**Q}^i||9Ey~X9s zAgEDqKzvpikAQxG0t(N?`Q(XeQG<}X{F5(u*FP+_W9&3HHrw$uyH9R|rlQh%7Br3* zU%3A4W$)P!J$r3w`G$At`in1KyK&>n<+ubd1O9^!y2J%wj*P83HDC>~s^UE3GeFi_ z9?-r9PgT8uerq!$Q>m3XZ1@|=&5Fk0I71!c5VJT!I@mwQo!V^772Tpa=g=(V;ZTww zOO82jm29@?nq!u%-l;Lpo7i<GZp4oa`)JI;WiYjXt;>=v-GFGk9~Tm2j;E676mWbD zpGgLQ)8;>L+yYEyZ*goi$t@$Ol!-eHS7CgM5f5SfygC_)Fzjiew;}Wi9$7N1xcHI^ zk|CwobQAr=MN0f;UBPYFM)25Z;f1U+nxhb?NIU4N?{MtIIl6N6!N<lw@u90r4aTVW zA<$)_{8>~u3u^N{GyDL>3F7+xeoow~_eb!)eKX3nTwbU6t=NmOgb@veE*E^xujkb# z@EjsK;$RuAB3fQWuhVp|#1bg>^ALDAhT3I=2$NQb20?_6_ieoKxB6E2Nw|ynPT*6n zYWQ60J%3%8oq`qZYzCFnVWrcJ7ywK><FjmHps=pvL+via@QKsm6P5eTc3W<iB?$bh zH>xw*RHH){=M!Be5x&@A5DCr|OaloP&T2_%v>`_jG9?f^<o3OfT^#lBUQe5rNzf@Z zEA6`P!+8^nObmwS`9OhwPPfzEiDw#&Ikgjo8Bb+1&|VMOeVm79vjy%7P5ue@XxH8@ zIdi6KEr??eH%)Ze9Gtka**oztF43K2+)l>Iq>X+OR#sv%;CDcF-v+!}yN<;00Nt{l zxmHfQ&ne%#0}92{4DBYwQ6J!ZUPTeRI-rJtMn;>O*x;#cjbLA>LoC#Ku_N^{0fClF z1!Q2AAAqr(X>&%aCTP0-Pi1gJ0%YReL4~F^h+ytnyH*6YP0ocJ&|VgqnPk{Lf-AYp z#w%s&L<o$C$=&m}jP1v9C66n|so9{_Zr**Kx@hFS=Wk`OQH*Dn5p#T5LXOY2VNQ|) zQ<ud+W<?H5nI)W=Y(e;Y<eQyDE&2kt0#ZSF|$%A7MNr!3{)F!?h-SqQbpqa9^15 z(8SKtB_c1@atv#R@Oxz}h<@VrnpH=aMtQjVFjs<Bm;s$oGUqIWnQ+hwhW&eHj3XZ^ zL?d2Mwlo|LO^Vbbh4QxRh@TsY`CG5}JDvJQvmq&kL@A$nQT)HNmCG5`UP_5ph04zq z`3<@z$ctP>922RFCakswkOi2M_$w1SNZ=<3vtF_YJfMkgPCcsV0VMHtI5HLHSG27_ zzlXL3EORlzg9<GHtW#a>z;H}*5m9>wk^P1a-m?Q3xQ#7|R}S^Mk7nYOS3oo58(^ji z;se8R2IfVZ7-Z|r57|p%=_~38s4E6IV;d@3t0;yVkvp-Yt<5oDQt!c~^IUR#8@==6 z%bVgOV&Y($30}OVA-MEXcT?tb8MA+gH#?i5t?unKUn`bv(b<o6V>f45M_x(~iF8vF zzG1#@ybk+9_^QyiE<=n-mEOkHW*fSA$Xg|fb78L~se@%^4kQT{t#v~#Ydg4C??kjE zk*~_wLi@TCIB3!W5{$VN2XTZI6suUmSB~tO4`??-03H}CSUd$V{*TsOXIbkDm_k{M z8u1D1-1*o<ph7Ws6PHJqLa!sE1m7)Q;G7z;N}l;Kl9VrZjo#B^Yq=tn&_-wIA%8f& zqG=~sKE^!|<P((V+qDKIS|r0!_t`2zibE4Sekxns9AIPI+?n`D=C=^d$rlYA;Z+nG zuXmm8r#1C}%4c^?AT$JlZZzsgfYMZTxuZtkL0al$KX(%Wjk_mr8F%LgDD7xiUSR0Q zL!1IM2pfQ6EO@AB7if1aQE;Nksr*$PMXdmXyRl9o602YoJma^C)uAbytkqf*rj2T> z?R=2bV0eP&Us-|)y?2eR&m$hsfPv;<s_Vj^L8sgA^-WOXRAf95g}(>J2Q}vD(J~=} zNco~~?V5KNZ$Sr!)=S1+>y`w4a6f!ElzD&&?%T5*)I20R`}VJx>S8oCNPzZj3KU}T z&i4P6XAs55TfVuA&}u)o?uLH=)6)ek73GX(BhQcnkhIQM_%0Zdk{G<(*Z1~qc%T0~ zd}X@>uT=U;R@9ZN$Qhs&eQJ$;H^z#x541=h{gFQ2C4JzU^l@xA?L+#7AuL+|SIoO7 z*b698W(xPUj1GF9t=YaU08;Gjeu#NlK!iZZ%2vC57CCpu9D(>lxlJI-OE71aap5CD z1F7(VLb)HnmX)wgs`Qb8Zz9*LbNEmoW1OJ#xKtBYZ)8iKLdsKM0f7_^D2V5Yal@tF z0|E-uBfSFjL-5wpan!>!EVNOq)2oT4i|bqHjzN~j2xtqgOkG&T+_yK?Ge1rC<p6-w zPmK-M<4q(`8n75r66y0p%4uXi#BtGaOt1(<mUjgp!!ckq<AUD|84+S6PsVK4$5q)j zI@#n%i5;$CnZ5UZA7jlhv^p0%tY|mTm3$3?(8<;W#m?jD6&M#@W2b>F5!$f(Q8v3t z%S))JkFy1FwQ!4O095hgY<-mlBkIVOG*?!z#{y<SHLs!eC)n=l#ZkY&N**cddoTxM zC-9L57c3SSP6Eb)zz6oN!E+=7?RKMJ!L46_L5&O%@`et|Ma#7<y7ulIT6qn5MEuR0 z$CB?8))Bim&XxOKk}V}Q9m#SDN7zO|m5lfg!=0wOy8TfmA#hID1FHFzle^aT@xFxw zvDM1qJ|GS)Z@41}OW!>O$%J%vPHtE5Tz=>|ZKD?w8~x@0)u+@i;jKb3y*{%#DFE+p z>kde-*C@6(k*7b9ETmUoU4<tC#zi6kQmgjRMKz}vf~zSxtJv(QT~<cHUT)qZkr~$o zs25#W$Bdu2ASnne=lh}*$*VKMk!X|wAzTe7BwScz+#S&g5)t4YA*1EnugNt+bppju zd>|^|4CJ%aRgx+7=#WLfDRqS1=;W|JgfqX*2y(g!2<WAqNJ3Qwd|dBT-knD5EA<r` zP4!B+)u3|%*vssoWHZr_)gy7|jR58oY+&?-*e!a`g3>{@|0uafcRp-eC3L|SrQ{Zb zbM(6dA6+HeijaE90^1edaVJ3b0d~(uGXfe9jc92fg=yY~6c(W7B79*J2ctnSU;E|= zX39##geJ(Sdn_^oV4Mkl7FT~G1sunK)4Jd0!e%XOeM$lh4QwO8PL2r(2?U_WtdSwd ztt6p(oeeY^)MXU4V@PZyF0f8*47`gpK*cU<ISDVvxh^QYFP63l;F>U^U*;^t=_`PB z5rnYJMVkS^5{};KF&LfPM>Ti#>eZ#C>M?A?@!-S-o`)?H-bUBoLLlFZOP+TZ+spJ7 zbpb<-3#qfKp5ZjFu%NxCuCe$q3tBtzp)>`AeEYSfYd5Yv`@*%_74<T2xW$69qkI-? zEa=B-zvKXu09XLTNfW<XY+JR%J3hujkb|<XevQRI&gG0OqkaU1FwhyC6wJbtC8tm- zxP^S7P_7p2LXNq!;t{lz%jH4=?N1es$WskGkbS5?FX9kFkLRm(%q%{54T1+u(jI5{ zVsjhuYCg!1XkdKXI6^vZuzax9AR2Lg&<3<^IT|LGc@I#w1s_9<0;GPi*78ZadkVGi zBnrsJF}f&Bg5WV&6L3++<pcVc-a)(oe{f$6BU8>BIHUIv+sKP(q6#S~GM^iWjAixS zJz>V@66bHCq`2y**fBR_DfOE?b(3&x4;6_%B`;T>W&_u8q=5qgk7^q|{C$o_o92vh zAmZWljz4NlOOUqz#E$<ni%+rmEQ?8;^BcJEdwc<DT`(2vBRoM3a1n=CbQw<+Yj(0{ z!AbTk<oqdM__X>c?v+VvBBVicq)rb)&uwjvMlwg7W)9&JU&Z+|2<*|Xl1&A&VF23% zP;-P!tfi|^g(R-LM_@_)4y(V5BJ~Uh*9UW#Ri<$NF{jP2HB#rI6?+E<5%p|eK*rm= zlf;k_eg`Mqhk)1}OMANpbtb`XSPw723%LsAXZ4Y_(sqS-Bj@1(B9=!F5a2GEtnhr_ zg(|dQU1;CqyjU8^IqZxh@QN(;i|AdprD+R9ROgfqjzX3Go?I=0Vm9rQ<f<rEgulOl zJN_PR(+G$Eo!p6lIl|%Qo*n*WR=2VF%}%Z-TiS%$(^Sn2$|tgkY<O7{`?DZ{c1T4H z2<=5fyvhs0_StrbB(A4ca4DWa+Ci)z3Hr%;VV?xR&tr}x(LiIN#!;sriF{LdunR_F zhDp?*rngmUPgTcx4|6yV)sTC*`gJk_(MlKriP|HuH))5Kta+rGXnRVDok|5T3Qee@ z1RNc!H$TKOiIG6@8!+C0C*3ijjHf&=Y<Hv3^F-N*$u?0)DN|ocxwT*!rehaek+cy} zrTkpPF)FHgh^m-pHHTZ#vB3wPCPcLmPY?Nn;tXWGp4!VFVsGrSk8Q_&>#$hDjBvm+ zKEZDW?r92g!||Sp1iG{#fsXV}-|_5zit3bCN6~7HU2WFIc_`uFJ|<5;1e>$$214^( z)7Yu2)ldfpe**O(nuzfJ7qtBi$-&(~l3X{7&H`2i0zB|=A;Hr9WQbwl1k8^Ci~X5+ zqo%g8lem%|3p~$od<37w)BlenAju80_x9oU?la$+W0yIObAb9oJgqn##U6*=B5);> z{Twd*HD5(LgPZ6I(~4~)D{rIWB98EB6d6d*V&(OUJ3h9S$;wMC0UOPV{xs@&zvR#0 zyWp3>5pzr_O01vR0AXQ~8)~x#S%M#abJmw|gzrKj5Di8kP%~-oC3ZipeZZfqVJh|a z^JD$0_o55+85RMHS5c%s4(UIByAWnT9pd;uk~&44XdHJTutf57BKwh?)Z!sipxmd7 zfS45h!3s+HgW!1LX{Ri8_=dWkP9+Bc2KI1!bf^I)1wDB+0(acpi^K@9jXsL(-40XL zB$E@o9GlV}2J~G{i3*3@?IOiNoXIAXI%&duxQE}_lJKbrp^9`9m#KamEhFZ|*bDo( zZsK{(%(>plp;tX5ER$Ji80SDKGDbqBp|b#lgcdecEK;_x&;H=)@XYBDNsnZ2$&a57 zt5t^}sMDaHhR2NUM#4SWiqmvw*4p!cgDH)e_;JP;UEh^K8pVg|yMZHQ_+$Hdj+F8b zm3;vY|N8-6SYipwoq^{?P#eliayAIt9`EO%NReF@pX@kPbZOw`JKE5CAG+7x66Gh& zbjgZK!1mE9>A&HbSx!##^qd4^v3qD?Xk9YK<4)I%WMT?oyT+LDCn!Tn(He&9t^SAw z-!u_)zK9F|pFBu}70}7UXTYE06_POfknt|!7*#q2K#%Nhh0=k#5$MT-52oT7LggR_ zDfA$f8eANH_{~m5I_GdQ3^1weSz5OXd6Ruv1M;$UXj+-UcZ@`p*HBk1jtpc!O7KR4 zosB~xRdhS(bcpo^*L;6MhnZZ)w8=)uqu6`=`4`!Lf=_dh-JU`rQN`n_SA!jQ2n@3e z@LCpQge=H5Ictckbd*s70Q;p|=DdyGE1WaQiF`|SbjAsc4Eg$l2&1EO>asd{JdIy1 z4N~E$GcObu2wTN1Lh+H2$RPG`=>!Ks0W`qN3s&#Y#DOpvoUsFh=Fwa%h%hMRU0=Z) z4yW%DwsjQSCNu1LT*-9h^du`ubf&B0@6eS+tYk?w@tBOWLC)ZZ-z4!R91?CzIF=a> z{Bj{&h^(JCQ_i*Z9I5Q$^0WBW8h(S;?a21f5!E1+7@Hqu_zdSaVB(RC{(dIrut<0r zS_LP}Z3D@)j>b3gk#Gcnf_B2H+E9}<FVH~eelR`ckKyr=^&K1C9<HC|=(OTuI2Qq@ zL!%=c*Inrjzy=-;AIJa&hO?m|d`LwCx!T3Xa_!^5zdFkl>MN&qa}j(h_)hSr@Eo@W zyM0IJQ2MSw^jYkbIEQP9%C<DPpTLG9vo(cZZ9u<cw_v_S->E=CAi8@efpZdKTKyb` zq{A?>AY!Am&=$`Q=C#ZO?O^wWHfeVFAfYzhKKE%hh_tX1{N7XhEr><xWF0536RER9 zi8j~{`!eF}^(hjX3YSKY{yjy}##T9aCiC!DdZ!1&gE4pRd(q=IIukyb$H}lBU$Igh z?urhtf>HlF?$HuTx=hy9qGT>{pj!52f5zhIz*22a8(DfPyouZowAX<B<V+BN!g-o+ zEaF5X0Myo;2rXGG``{kqB98C}L;>=^TZ01XkI~ilB>FFMVwvdAUls=9>ZU&i$c!D% z?#;v={{E2I+e`559VK#~vgY?C`9I0QjY$51!2rKMsmI8^Z>e|E%;~>U>V*!I#h$zs zPwPqjKq9a4bicr>uaTJs>@1v2KL03b`UoL^Y(U{3o`A1$kV67K!k5|ze~W~hGWR^+ zHYVI`9}#XM*+m@T85AQb{-N$LReM}YENs2H1GAd;`eX-~i!*GWY6p0r>qq-|h+`}6 zz}WI%K*vlFB$Q=<5@wV30cCgtq33M*NsU$3^)JTZ-@HNR)_g;B{u}tk`0M8D*j_)p z3yC=<IJ-Jrh#cK2{&xX?p{BP~Amrmbuv87kFBPpL4si)Js9Rl=$vXRk_F~|k!&t<? zeO#x_AMoeh(Bjx%#NTxC%z9wyv*CS4iGs<<2fKsmuGm!gHNAsCkvHV^4B(#@@5e|r z0udAJMVcV*8KGN(MT6|>Bm+2eaM79Acgy&e-&x6Qih4G~BQ`~gC*xHA3%8F;Gza$y z4vu}g1`=wbM6)@@2L}1}WV@=i48Fw9zKmjofBq{Pu%&oQ!5>D1N-PUf0;=R2d?4#r zHbF^%=|101?Vd(T7M-HG$l#{cJMm~DWdxzePfojpBdnkpwiLLp8~ldwHA;8IN$rM1 ziSJMU-xswd-hV;?G4y4)ub{tpO3WYG7M(fDk1#NPk&_}KAc!Gx02-YmlNs0;!@e^6 z#u_}-%lIU&Yd2Z=YfuP0e;rr5d^0&s42@i2R<X5#Bl~cvFX9+QB4$tadB^h0PdCY; z{7*ci7oSGaWKUxOS5i-dKW}FMZh$;OIzB9MwGsQ`VR-YaoKI$792ppD4*?wBnu1Qa z#L)}^&IIq6K!cD91FmEgc*tOtakv?>Ngf6&r|_!OYX*S8TOJ3MVZpStn2{92(us2s z6Csf-uB<DC^w&uTYBY@?z4wh%;)e<$pW*0m5JKF++|>U@lh|+St2`6-9uAEVFVr_# zaJD1UmzjSOt)F7wjJarlSb#@C&4YmIV{b}>5riOX2tpjeNs18ZCL8VoOOYY<C~`v{ z#VdG5{TU0H#Kd2oie&z7x=>p5dlOhTH9)L^H2vU0PpSM5d}k)~NV6-pz9mBcr|Fa* z;aI%oLjMKMClmUGA@g>!g^>yTs~GL~F7UY19v5}|?-u?S7g{7H5*7HLHhtL?@Bj`# z5{<|`#IE?ahJZgxIV*vH9~q|04^{lX%8}uK_-7KwDwj}9E()`t<04{;65-Y{#r@I5 z>_@jWt#lyK9PCKYW&`o?YlBzrM;uWmbQ{&5px9@jXvEoPoCxZEnonie^CiAz6nhqd zJqUci{gK4as6fl)-v@WT&Y=#uJY!PxmSPS;;q!do2<EW0dW0!dzuUmpCGiyuWs{<$ zf$U1s+Cb2h53A=;;GY8_Ru$oA{h|E73iWwj;r=1n`k}XPe2&#GviJ%MI=+fdme@Cf zcS^8|ie${njSCm_eyV4<#Np#89Awl>*u_~gu@mX`+{cZN8&A!Z=FZK{&p$C&o;!`d I%G|Mk0c<8rqyPW_ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/util.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/orm/__pycache__/util.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eca98bf4727848129a07e3af46b7d85083b2bab4 GIT binary patch literal 36108 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 WH`_(Dt6Z8WJ%v3{4iyg<r~fY)8I~ac literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/orm/attributes.py b/venv/Lib/site-packages/sqlalchemy/orm/attributes.py new file mode 100644 index 0000000..0a5dc9f --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/attributes.py @@ -0,0 +1,1698 @@ +# orm/attributes.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Defines instrumentation for class attributes and their interaction +with instances. + +This module is usually not directly visible to user applications, but +defines a large part of the ORM's interactivity. + + +""" + +import operator +from .. import util, event, inspection +from . import interfaces, collections, exc as orm_exc + +from .base import instance_state, instance_dict, manager_of_class + +from .base import PASSIVE_NO_RESULT, ATTR_WAS_SET, ATTR_EMPTY, NO_VALUE,\ + NEVER_SET, NO_CHANGE, CALLABLES_OK, SQL_OK, RELATED_OBJECT_OK,\ + INIT_OK, NON_PERSISTENT_OK, LOAD_AGAINST_COMMITTED, PASSIVE_OFF,\ + PASSIVE_RETURN_NEVER_SET, PASSIVE_NO_INITIALIZE, PASSIVE_NO_FETCH,\ + PASSIVE_NO_FETCH_RELATED, PASSIVE_ONLY_PERSISTENT, NO_AUTOFLUSH +from .base import state_str, instance_str + + +@inspection._self_inspects +class QueryableAttribute(interfaces._MappedAttribute, + interfaces.InspectionAttr, + interfaces.PropComparator): + """Base class for :term:`descriptor` objects that intercept + attribute events on behalf of a :class:`.MapperProperty` + object. The actual :class:`.MapperProperty` is accessible + via the :attr:`.QueryableAttribute.property` + attribute. + + + .. seealso:: + + :class:`.InstrumentedAttribute` + + :class:`.MapperProperty` + + :attr:`.Mapper.all_orm_descriptors` + + :attr:`.Mapper.attrs` + """ + + is_attribute = True + + def __init__(self, class_, key, impl=None, + comparator=None, parententity=None, + of_type=None): + self.class_ = class_ + self.key = key + self.impl = impl + self.comparator = comparator + self._parententity = parententity + self._of_type = of_type + + manager = manager_of_class(class_) + # manager is None in the case of AliasedClass + if manager: + # propagate existing event listeners from + # immediate superclass + for base in manager._bases: + if key in base: + self.dispatch._update(base[key].dispatch) + + @util.memoized_property + def _supports_population(self): + return self.impl.supports_population + + def get_history(self, instance, passive=PASSIVE_OFF): + return self.impl.get_history(instance_state(instance), + instance_dict(instance), passive) + + def __selectable__(self): + # TODO: conditionally attach this method based on clause_element ? + return self + + @util.memoized_property + def info(self): + """Return the 'info' dictionary for the underlying SQL element. + + The behavior here is as follows: + + * If the attribute is a column-mapped property, i.e. + :class:`.ColumnProperty`, which is mapped directly + to a schema-level :class:`.Column` object, this attribute + will return the :attr:`.SchemaItem.info` dictionary associated + with the core-level :class:`.Column` object. + + * If the attribute is a :class:`.ColumnProperty` but is mapped to + any other kind of SQL expression other than a :class:`.Column`, + the attribute will refer to the :attr:`.MapperProperty.info` + dictionary associated directly with the :class:`.ColumnProperty`, + assuming the SQL expression itself does not have its own ``.info`` + attribute (which should be the case, unless a user-defined SQL + construct has defined one). + + * If the attribute refers to any other kind of + :class:`.MapperProperty`, including :class:`.RelationshipProperty`, + the attribute will refer to the :attr:`.MapperProperty.info` + dictionary associated with that :class:`.MapperProperty`. + + * To access the :attr:`.MapperProperty.info` dictionary of the + :class:`.MapperProperty` unconditionally, including for a + :class:`.ColumnProperty` that's associated directly with a + :class:`.schema.Column`, the attribute can be referred to using + :attr:`.QueryableAttribute.property` attribute, as + ``MyClass.someattribute.property.info``. + + .. versionadded:: 0.8.0 + + .. seealso:: + + :attr:`.SchemaItem.info` + + :attr:`.MapperProperty.info` + + """ + return self.comparator.info + + @util.memoized_property + def parent(self): + """Return an inspection instance representing the parent. + + This will be either an instance of :class:`.Mapper` + or :class:`.AliasedInsp`, depending upon the nature + of the parent entity which this attribute is associated + with. + + """ + return inspection.inspect(self._parententity) + + @property + def expression(self): + return self.comparator.__clause_element__() + + def __clause_element__(self): + return self.comparator.__clause_element__() + + def _query_clause_element(self): + """like __clause_element__(), but called specifically + by :class:`.Query` to allow special behavior.""" + + return self.comparator._query_clause_element() + + def _bulk_update_tuples(self, value): + """Return setter tuples for a bulk UPDATE.""" + + return self.comparator._bulk_update_tuples(value) + + def adapt_to_entity(self, adapt_to_entity): + assert not self._of_type + return self.__class__(adapt_to_entity.entity, + self.key, impl=self.impl, + comparator=self.comparator.adapt_to_entity( + adapt_to_entity), + parententity=adapt_to_entity) + + def of_type(self, cls): + return QueryableAttribute( + self.class_, + self.key, + self.impl, + self.comparator.of_type(cls), + self._parententity, + of_type=cls) + + def label(self, name): + return self._query_clause_element().label(name) + + def operate(self, op, *other, **kwargs): + return op(self.comparator, *other, **kwargs) + + def reverse_operate(self, op, other, **kwargs): + return op(other, self.comparator, **kwargs) + + def hasparent(self, state, optimistic=False): + return self.impl.hasparent(state, optimistic=optimistic) is not False + + def __getattr__(self, key): + try: + return getattr(self.comparator, key) + except AttributeError: + raise AttributeError( + 'Neither %r object nor %r object associated with %s ' + 'has an attribute %r' % ( + type(self).__name__, + type(self.comparator).__name__, + self, + key) + ) + + def __str__(self): + return "%s.%s" % (self.class_.__name__, self.key) + + @util.memoized_property + def property(self): + """Return the :class:`.MapperProperty` associated with this + :class:`.QueryableAttribute`. + + + Return values here will commonly be instances of + :class:`.ColumnProperty` or :class:`.RelationshipProperty`. + + + """ + return self.comparator.property + + +class InstrumentedAttribute(QueryableAttribute): + """Class bound instrumented attribute which adds basic + :term:`descriptor` methods. + + See :class:`.QueryableAttribute` for a description of most features. + + + """ + + def __set__(self, instance, value): + self.impl.set(instance_state(instance), + instance_dict(instance), value, None) + + def __delete__(self, instance): + self.impl.delete(instance_state(instance), instance_dict(instance)) + + def __get__(self, instance, owner): + if instance is None: + return self + + dict_ = instance_dict(instance) + if self._supports_population and self.key in dict_: + return dict_[self.key] + else: + return self.impl.get(instance_state(instance), dict_) + + +def create_proxied_attribute(descriptor): + """Create an QueryableAttribute / user descriptor hybrid. + + Returns a new QueryableAttribute type that delegates descriptor + behavior and getattr() to the given descriptor. + """ + + # TODO: can move this to descriptor_props if the need for this + # function is removed from ext/hybrid.py + + class Proxy(QueryableAttribute): + """Presents the :class:`.QueryableAttribute` interface as a + proxy on top of a Python descriptor / :class:`.PropComparator` + combination. + + """ + + def __init__(self, class_, key, descriptor, + comparator, + adapt_to_entity=None, doc=None, + original_property=None): + self.class_ = class_ + self.key = key + self.descriptor = descriptor + self.original_property = original_property + self._comparator = comparator + self._adapt_to_entity = adapt_to_entity + self.__doc__ = doc + + _is_internal_proxy = True + + @property + def property(self): + return self.comparator.property + + @util.memoized_property + def comparator(self): + if util.callable(self._comparator): + self._comparator = self._comparator() + if self._adapt_to_entity: + self._comparator = self._comparator.adapt_to_entity( + self._adapt_to_entity) + return self._comparator + + def adapt_to_entity(self, adapt_to_entity): + return self.__class__(adapt_to_entity.entity, + self.key, + self.descriptor, + self._comparator, + adapt_to_entity) + + def __get__(self, instance, owner): + if instance is None: + return self + else: + return self.descriptor.__get__(instance, owner) + + def __str__(self): + return "%s.%s" % (self.class_.__name__, self.key) + + def __getattr__(self, attribute): + """Delegate __getattr__ to the original descriptor and/or + comparator.""" + + try: + return getattr(descriptor, attribute) + except AttributeError: + try: + return getattr(self.comparator, attribute) + except AttributeError: + raise AttributeError( + 'Neither %r object nor %r object associated with %s ' + 'has an attribute %r' % ( + type(descriptor).__name__, + type(self.comparator).__name__, + self, + attribute) + ) + + Proxy.__name__ = type(descriptor).__name__ + 'Proxy' + + util.monkeypatch_proxied_specials(Proxy, type(descriptor), + name='descriptor', + from_instance=descriptor) + return Proxy + +OP_REMOVE = util.symbol("REMOVE") +OP_APPEND = util.symbol("APPEND") +OP_REPLACE = util.symbol("REPLACE") +OP_BULK_REPLACE = util.symbol("BULK_REPLACE") +OP_MODIFIED = util.symbol("MODIFIED") + + +class Event(object): + """A token propagated throughout the course of a chain of attribute + events. + + Serves as an indicator of the source of the event and also provides + a means of controlling propagation across a chain of attribute + operations. + + The :class:`.Event` object is sent as the ``initiator`` argument + when dealing with events such as :meth:`.AttributeEvents.append`, + :meth:`.AttributeEvents.set`, + and :meth:`.AttributeEvents.remove`. + + The :class:`.Event` object is currently interpreted by the backref + event handlers, and is used to control the propagation of operations + across two mutually-dependent attributes. + + .. versionadded:: 0.9.0 + + :var impl: The :class:`.AttributeImpl` which is the current event + initiator. + + :var op: The symbol :attr:`.OP_APPEND`, :attr:`.OP_REMOVE`, + :attr:`.OP_REPLACE`, or :attr:`.OP_BULK_REPLACE`, indicating the + source operation. + + """ + + __slots__ = 'impl', 'op', 'parent_token' + + def __init__(self, attribute_impl, op): + self.impl = attribute_impl + self.op = op + self.parent_token = self.impl.parent_token + + def __eq__(self, other): + return isinstance(other, Event) and \ + other.impl is self.impl and \ + other.op == self.op + + @property + def key(self): + return self.impl.key + + def hasparent(self, state): + return self.impl.hasparent(state) + + +class AttributeImpl(object): + """internal implementation for instrumented attributes.""" + + def __init__(self, class_, key, + callable_, dispatch, trackparent=False, extension=None, + compare_function=None, active_history=False, + parent_token=None, expire_missing=True, + send_modified_events=True, accepts_scalar_loader=None, + **kwargs): + r"""Construct an AttributeImpl. + + \class_ + associated class + + key + string name of the attribute + + \callable_ + optional function which generates a callable based on a parent + instance, which produces the "default" values for a scalar or + collection attribute when it's first accessed, if not present + already. + + trackparent + if True, attempt to track if an instance has a parent attached + to it via this attribute. + + extension + a single or list of AttributeExtension object(s) which will + receive set/delete/append/remove/etc. events. Deprecated. + The event package is now used. + + compare_function + a function that compares two values which are normally + assignable to this attribute. + + active_history + indicates that get_history() should always return the "old" value, + even if it means executing a lazy callable upon attribute change. + + parent_token + Usually references the MapperProperty, used as a key for + the hasparent() function to identify an "owning" attribute. + Allows multiple AttributeImpls to all match a single + owner attribute. + + expire_missing + if False, don't add an "expiry" callable to this attribute + during state.expire_attributes(None), if no value is present + for this key. + + send_modified_events + if False, the InstanceState._modified_event method will have no + effect; this means the attribute will never show up as changed in a + history entry. + """ + self.class_ = class_ + self.key = key + self.callable_ = callable_ + self.dispatch = dispatch + self.trackparent = trackparent + self.parent_token = parent_token or self + self.send_modified_events = send_modified_events + if compare_function is None: + self.is_equal = operator.eq + else: + self.is_equal = compare_function + + if accepts_scalar_loader is not None: + self.accepts_scalar_loader = accepts_scalar_loader + else: + self.accepts_scalar_loader = self.default_accepts_scalar_loader + + # TODO: pass in the manager here + # instead of doing a lookup + attr = manager_of_class(class_)[key] + + for ext in util.to_list(extension or []): + ext._adapt_listener(attr, ext) + + if active_history: + self.dispatch._active_history = True + + self.expire_missing = expire_missing + self._modified_token = Event(self, OP_MODIFIED) + + __slots__ = ( + 'class_', 'key', 'callable_', 'dispatch', 'trackparent', + 'parent_token', 'send_modified_events', 'is_equal', 'expire_missing', + '_modified_token', 'accepts_scalar_loader' + ) + + def __str__(self): + return "%s.%s" % (self.class_.__name__, self.key) + + def _get_active_history(self): + """Backwards compat for impl.active_history""" + + return self.dispatch._active_history + + def _set_active_history(self, value): + self.dispatch._active_history = value + + active_history = property(_get_active_history, _set_active_history) + + def hasparent(self, state, optimistic=False): + """Return the boolean value of a `hasparent` flag attached to + the given state. + + The `optimistic` flag determines what the default return value + should be if no `hasparent` flag can be located. + + As this function is used to determine if an instance is an + *orphan*, instances that were loaded from storage should be + assumed to not be orphans, until a True/False value for this + flag is set. + + An instance attribute that is loaded by a callable function + will also not have a `hasparent` flag. + + """ + msg = "This AttributeImpl is not configured to track parents." + assert self.trackparent, msg + + return state.parents.get(id(self.parent_token), optimistic) \ + is not False + + def sethasparent(self, state, parent_state, value): + """Set a boolean flag on the given item corresponding to + whether or not it is attached to a parent object via the + attribute represented by this ``InstrumentedAttribute``. + + """ + msg = "This AttributeImpl is not configured to track parents." + assert self.trackparent, msg + + id_ = id(self.parent_token) + if value: + state.parents[id_] = parent_state + else: + if id_ in state.parents: + last_parent = state.parents[id_] + + if last_parent is not False and \ + last_parent.key != parent_state.key: + + if last_parent.obj() is None: + raise orm_exc.StaleDataError( + "Removing state %s from parent " + "state %s along attribute '%s', " + "but the parent record " + "has gone stale, can't be sure this " + "is the most recent parent." % + (state_str(state), + state_str(parent_state), + self.key)) + + return + + state.parents[id_] = False + + def get_history(self, state, dict_, passive=PASSIVE_OFF): + raise NotImplementedError() + + def get_all_pending(self, state, dict_, passive=PASSIVE_NO_INITIALIZE): + """Return a list of tuples of (state, obj) + for all objects in this attribute's current state + + history. + + Only applies to object-based attributes. + + This is an inlining of existing functionality + which roughly corresponds to: + + get_state_history( + state, + key, + passive=PASSIVE_NO_INITIALIZE).sum() + + """ + raise NotImplementedError() + + def initialize(self, state, dict_): + """Initialize the given state's attribute with an empty value.""" + + value = None + for fn in self.dispatch.init_scalar: + ret = fn(state, value, dict_) + if ret is not ATTR_EMPTY: + value = ret + + return value + + def get(self, state, dict_, passive=PASSIVE_OFF): + """Retrieve a value from the given object. + If a callable is assembled on this object's attribute, and + passive is False, the callable will be executed and the + resulting value will be set as the new value for this attribute. + """ + if self.key in dict_: + return dict_[self.key] + else: + # if history present, don't load + key = self.key + if key not in state.committed_state or \ + state.committed_state[key] is NEVER_SET: + if not passive & CALLABLES_OK: + return PASSIVE_NO_RESULT + + if key in state.expired_attributes: + value = state._load_expired(state, passive) + elif key in state.callables: + callable_ = state.callables[key] + value = callable_(state, passive) + elif self.callable_: + value = self.callable_(state, passive) + else: + value = ATTR_EMPTY + + if value is PASSIVE_NO_RESULT or value is NEVER_SET: + return value + elif value is ATTR_WAS_SET: + try: + return dict_[key] + except KeyError: + # TODO: no test coverage here. + raise KeyError( + "Deferred loader for attribute " + "%r failed to populate " + "correctly" % key) + elif value is not ATTR_EMPTY: + return self.set_committed_value(state, dict_, value) + + if not passive & INIT_OK: + return NEVER_SET + else: + # Return a new, empty value + return self.initialize(state, dict_) + + def append(self, state, dict_, value, initiator, passive=PASSIVE_OFF): + self.set(state, dict_, value, initiator, passive=passive) + + def remove(self, state, dict_, value, initiator, passive=PASSIVE_OFF): + self.set(state, dict_, None, initiator, + passive=passive, check_old=value) + + def pop(self, state, dict_, value, initiator, passive=PASSIVE_OFF): + self.set(state, dict_, None, initiator, + passive=passive, check_old=value, pop=True) + + def set(self, state, dict_, value, initiator, + passive=PASSIVE_OFF, check_old=None, pop=False): + raise NotImplementedError() + + def get_committed_value(self, state, dict_, passive=PASSIVE_OFF): + """return the unchanged value of this attribute""" + + if self.key in state.committed_state: + value = state.committed_state[self.key] + if value in (NO_VALUE, NEVER_SET): + return None + else: + return value + else: + return self.get(state, dict_, passive=passive) + + def set_committed_value(self, state, dict_, value): + """set an attribute value on the given instance and 'commit' it.""" + + dict_[self.key] = value + state._commit(dict_, [self.key]) + return value + + +class ScalarAttributeImpl(AttributeImpl): + """represents a scalar value-holding InstrumentedAttribute.""" + + default_accepts_scalar_loader = True + uses_objects = False + supports_population = True + collection = False + dynamic = False + + __slots__ = '_replace_token', '_append_token', '_remove_token' + + def __init__(self, *arg, **kw): + super(ScalarAttributeImpl, self).__init__(*arg, **kw) + self._replace_token = self._append_token = Event(self, OP_REPLACE) + self._remove_token = Event(self, OP_REMOVE) + + def delete(self, state, dict_): + + # TODO: catch key errors, convert to attributeerror? + if self.dispatch._active_history: + old = self.get(state, dict_, PASSIVE_RETURN_NEVER_SET) + else: + old = dict_.get(self.key, NO_VALUE) + + if self.dispatch.remove: + self.fire_remove_event(state, dict_, old, self._remove_token) + state._modified_event(dict_, self, old) + del dict_[self.key] + + def get_history(self, state, dict_, passive=PASSIVE_OFF): + if self.key in dict_: + return History.from_scalar_attribute(self, state, dict_[self.key]) + else: + if passive & INIT_OK: + passive ^= INIT_OK + current = self.get(state, dict_, passive=passive) + if current is PASSIVE_NO_RESULT: + return HISTORY_BLANK + else: + return History.from_scalar_attribute(self, state, current) + + def set(self, state, dict_, value, initiator, + passive=PASSIVE_OFF, check_old=None, pop=False): + if self.dispatch._active_history: + old = self.get(state, dict_, PASSIVE_RETURN_NEVER_SET) + else: + old = dict_.get(self.key, NO_VALUE) + + if self.dispatch.set: + value = self.fire_replace_event(state, dict_, + value, old, initiator) + state._modified_event(dict_, self, old) + dict_[self.key] = value + + def fire_replace_event(self, state, dict_, value, previous, initiator): + for fn in self.dispatch.set: + value = fn( + state, value, previous, initiator or self._replace_token) + return value + + def fire_remove_event(self, state, dict_, value, initiator): + for fn in self.dispatch.remove: + fn(state, value, initiator or self._remove_token) + + @property + def type(self): + self.property.columns[0].type + + +class ScalarObjectAttributeImpl(ScalarAttributeImpl): + """represents a scalar-holding InstrumentedAttribute, + where the target object is also instrumented. + + Adds events to delete/set operations. + + """ + + default_accepts_scalar_loader = False + uses_objects = True + supports_population = True + collection = False + + __slots__ = () + + def delete(self, state, dict_): + old = self.get(state, dict_) + self.fire_remove_event(state, dict_, old, self._remove_token) + del dict_[self.key] + + def get_history(self, state, dict_, passive=PASSIVE_OFF): + if self.key in dict_: + return History.from_object_attribute(self, state, dict_[self.key]) + else: + if passive & INIT_OK: + passive ^= INIT_OK + current = self.get(state, dict_, passive=passive) + if current is PASSIVE_NO_RESULT: + return HISTORY_BLANK + else: + return History.from_object_attribute(self, state, current) + + def get_all_pending(self, state, dict_, passive=PASSIVE_NO_INITIALIZE): + if self.key in dict_: + current = dict_[self.key] + elif passive & CALLABLES_OK: + current = self.get(state, dict_, passive=passive) + else: + return [] + + # can't use __hash__(), can't use __eq__() here + if current is not None and \ + current is not PASSIVE_NO_RESULT and \ + current is not NEVER_SET: + ret = [(instance_state(current), current)] + else: + ret = [(None, None)] + + if self.key in state.committed_state: + original = state.committed_state[self.key] + if original is not None and \ + original is not PASSIVE_NO_RESULT and \ + original is not NEVER_SET and \ + original is not current: + + ret.append((instance_state(original), original)) + return ret + + def set(self, state, dict_, value, initiator, + passive=PASSIVE_OFF, check_old=None, pop=False): + """Set a value on the given InstanceState. + + """ + if self.dispatch._active_history: + old = self.get( + state, dict_, + passive=PASSIVE_ONLY_PERSISTENT | + NO_AUTOFLUSH | LOAD_AGAINST_COMMITTED) + else: + old = self.get( + state, dict_, passive=PASSIVE_NO_FETCH ^ INIT_OK | + LOAD_AGAINST_COMMITTED) + + if check_old is not None and \ + old is not PASSIVE_NO_RESULT and \ + check_old is not old: + if pop: + return + else: + raise ValueError( + "Object %s not associated with %s on attribute '%s'" % ( + instance_str(check_old), + state_str(state), + self.key + )) + + value = self.fire_replace_event(state, dict_, value, old, initiator) + dict_[self.key] = value + + def fire_remove_event(self, state, dict_, value, initiator): + if self.trackparent and value is not None: + self.sethasparent(instance_state(value), state, False) + + for fn in self.dispatch.remove: + fn(state, value, initiator or self._remove_token) + + state._modified_event(dict_, self, value) + + def fire_replace_event(self, state, dict_, value, previous, initiator): + if self.trackparent: + if (previous is not value and + previous not in (None, PASSIVE_NO_RESULT, NEVER_SET)): + self.sethasparent(instance_state(previous), state, False) + + for fn in self.dispatch.set: + value = fn( + state, value, previous, initiator or self._replace_token) + + state._modified_event(dict_, self, previous) + + if self.trackparent: + if value is not None: + self.sethasparent(instance_state(value), state, True) + + return value + + +class CollectionAttributeImpl(AttributeImpl): + """A collection-holding attribute that instruments changes in membership. + + Only handles collections of instrumented objects. + + InstrumentedCollectionAttribute holds an arbitrary, user-specified + container object (defaulting to a list) and brokers access to the + CollectionAdapter, a "view" onto that object that presents consistent bag + semantics to the orm layer independent of the user data implementation. + + """ + default_accepts_scalar_loader = False + uses_objects = True + supports_population = True + collection = True + dynamic = False + + __slots__ = ( + 'copy', 'collection_factory', '_append_token', '_remove_token', + '_bulk_replace_token', '_duck_typed_as' + ) + + def __init__(self, class_, key, callable_, dispatch, + typecallable=None, trackparent=False, extension=None, + copy_function=None, compare_function=None, **kwargs): + super(CollectionAttributeImpl, self).__init__( + class_, + key, + callable_, dispatch, + trackparent=trackparent, + extension=extension, + compare_function=compare_function, + **kwargs) + + if copy_function is None: + copy_function = self.__copy + self.copy = copy_function + self.collection_factory = typecallable + self._append_token = Event(self, OP_APPEND) + self._remove_token = Event(self, OP_REMOVE) + self._bulk_replace_token = Event(self, OP_BULK_REPLACE) + self._duck_typed_as = util.duck_type_collection( + self.collection_factory()) + + if getattr(self.collection_factory, "_sa_linker", None): + + @event.listens_for(self, "init_collection") + def link(target, collection, collection_adapter): + collection._sa_linker(collection_adapter) + + @event.listens_for(self, "dispose_collection") + def unlink(target, collection, collection_adapter): + collection._sa_linker(None) + + def __copy(self, item): + return [y for y in collections.collection_adapter(item)] + + def get_history(self, state, dict_, passive=PASSIVE_OFF): + current = self.get(state, dict_, passive=passive) + if current is PASSIVE_NO_RESULT: + return HISTORY_BLANK + else: + return History.from_collection(self, state, current) + + def get_all_pending(self, state, dict_, passive=PASSIVE_NO_INITIALIZE): + # NOTE: passive is ignored here at the moment + + if self.key not in dict_: + return [] + + current = dict_[self.key] + current = getattr(current, '_sa_adapter') + + if self.key in state.committed_state: + original = state.committed_state[self.key] + if original not in (NO_VALUE, NEVER_SET): + current_states = [((c is not None) and + instance_state(c) or None, c) + for c in current] + original_states = [((c is not None) and + instance_state(c) or None, c) + for c in original] + + current_set = dict(current_states) + original_set = dict(original_states) + + return \ + [(s, o) for s, o in current_states + if s not in original_set] + \ + [(s, o) for s, o in current_states + if s in original_set] + \ + [(s, o) for s, o in original_states + if s not in current_set] + + return [(instance_state(o), o) for o in current] + + def fire_append_event(self, state, dict_, value, initiator): + for fn in self.dispatch.append: + value = fn( + state, value, initiator or self._append_token) + + state._modified_event(dict_, self, NEVER_SET, True) + + if self.trackparent and value is not None: + self.sethasparent(instance_state(value), state, True) + + return value + + def fire_pre_remove_event(self, state, dict_, initiator): + state._modified_event(dict_, self, NEVER_SET, True) + + def fire_remove_event(self, state, dict_, value, initiator): + if self.trackparent and value is not None: + self.sethasparent(instance_state(value), state, False) + + for fn in self.dispatch.remove: + fn(state, value, initiator or self._remove_token) + + state._modified_event(dict_, self, NEVER_SET, True) + + def delete(self, state, dict_): + if self.key not in dict_: + return + + state._modified_event(dict_, self, NEVER_SET, True) + + collection = self.get_collection(state, state.dict) + collection.clear_with_event() + # TODO: catch key errors, convert to attributeerror? + del dict_[self.key] + + def initialize(self, state, dict_): + """Initialize this attribute with an empty collection.""" + + _, user_data = self._initialize_collection(state) + dict_[self.key] = user_data + return user_data + + def _initialize_collection(self, state): + + adapter, collection = state.manager.initialize_collection( + self.key, state, self.collection_factory) + + self.dispatch.init_collection(state, collection, adapter) + + return adapter, collection + + def append(self, state, dict_, value, initiator, passive=PASSIVE_OFF): + collection = self.get_collection(state, dict_, passive=passive) + if collection is PASSIVE_NO_RESULT: + value = self.fire_append_event(state, dict_, value, initiator) + assert self.key not in dict_, \ + "Collection was loaded during event handling." + state._get_pending_mutation(self.key).append(value) + else: + collection.append_with_event(value, initiator) + + def remove(self, state, dict_, value, initiator, passive=PASSIVE_OFF): + collection = self.get_collection(state, state.dict, passive=passive) + if collection is PASSIVE_NO_RESULT: + self.fire_remove_event(state, dict_, value, initiator) + assert self.key not in dict_, \ + "Collection was loaded during event handling." + state._get_pending_mutation(self.key).remove(value) + else: + collection.remove_with_event(value, initiator) + + def pop(self, state, dict_, value, initiator, passive=PASSIVE_OFF): + try: + # TODO: better solution here would be to add + # a "popper" role to collections.py to complement + # "remover". + self.remove(state, dict_, value, initiator, passive=passive) + except (ValueError, KeyError, IndexError): + pass + + def set(self, state, dict_, value, initiator=None, + passive=PASSIVE_OFF, pop=False, _adapt=True): + iterable = orig_iterable = value + + # pulling a new collection first so that an adaptation exception does + # not trigger a lazy load of the old collection. + new_collection, user_data = self._initialize_collection(state) + if _adapt: + if new_collection._converter is not None: + iterable = new_collection._converter(iterable) + else: + setting_type = util.duck_type_collection(iterable) + receiving_type = self._duck_typed_as + + if setting_type is not receiving_type: + given = iterable is None and 'None' or \ + iterable.__class__.__name__ + wanted = self._duck_typed_as.__name__ + raise TypeError( + "Incompatible collection type: %s is not %s-like" % ( + given, wanted)) + + # If the object is an adapted collection, return the (iterable) + # adapter. + if hasattr(iterable, '_sa_iterator'): + iterable = iterable._sa_iterator() + elif setting_type is dict: + if util.py3k: + iterable = iterable.values() + else: + iterable = getattr( + iterable, 'itervalues', iterable.values)() + else: + iterable = iter(iterable) + new_values = list(iterable) + + evt = self._bulk_replace_token + + self.dispatch.bulk_replace(state, new_values, evt) + + old = self.get(state, dict_, passive=PASSIVE_ONLY_PERSISTENT) + if old is PASSIVE_NO_RESULT: + old = self.initialize(state, dict_) + elif old is orig_iterable: + # ignore re-assignment of the current collection, as happens + # implicitly with in-place operators (foo.collection |= other) + return + + # place a copy of "old" in state.committed_state + state._modified_event(dict_, self, old, True) + + old_collection = old._sa_adapter + + dict_[self.key] = user_data + + collections.bulk_replace( + new_values, old_collection, new_collection, + initiator=evt) + + del old._sa_adapter + self.dispatch.dispose_collection(state, old, old_collection) + + def _invalidate_collection(self, collection): + adapter = getattr(collection, '_sa_adapter') + adapter.invalidated = True + + def set_committed_value(self, state, dict_, value): + """Set an attribute value on the given instance and 'commit' it.""" + + collection, user_data = self._initialize_collection(state) + + if value: + collection.append_multiple_without_event(value) + + state.dict[self.key] = user_data + + state._commit(dict_, [self.key]) + + if self.key in state._pending_mutations: + # pending items exist. issue a modified event, + # add/remove new items. + state._modified_event(dict_, self, user_data, True) + + pending = state._pending_mutations.pop(self.key) + added = pending.added_items + removed = pending.deleted_items + for item in added: + collection.append_without_event(item) + for item in removed: + collection.remove_without_event(item) + + return user_data + + def get_collection(self, state, dict_, + user_data=None, passive=PASSIVE_OFF): + """Retrieve the CollectionAdapter associated with the given state. + + Creates a new CollectionAdapter if one does not exist. + + """ + if user_data is None: + user_data = self.get(state, dict_, passive=passive) + if user_data is PASSIVE_NO_RESULT: + return user_data + + return getattr(user_data, '_sa_adapter') + + +def backref_listeners(attribute, key, uselist): + """Apply listeners to synchronize a two-way relationship.""" + + # use easily recognizable names for stack traces. + + # in the sections marked "tokens to test for a recursive loop", + # this is somewhat brittle and very performance-sensitive logic + # that is specific to how we might arrive at each event. a marker + # that can target us directly to arguments being invoked against + # the impl might be simpler, but could interfere with other systems. + + parent_token = attribute.impl.parent_token + parent_impl = attribute.impl + + def _acceptable_key_err(child_state, initiator, child_impl): + raise ValueError( + "Bidirectional attribute conflict detected: " + 'Passing object %s to attribute "%s" ' + 'triggers a modify event on attribute "%s" ' + 'via the backref "%s".' % ( + state_str(child_state), + initiator.parent_token, + child_impl.parent_token, + attribute.impl.parent_token + ) + ) + + def emit_backref_from_scalar_set_event(state, child, oldchild, initiator): + if oldchild is child: + return child + if oldchild is not None and \ + oldchild is not PASSIVE_NO_RESULT and \ + oldchild is not NEVER_SET: + # With lazy=None, there's no guarantee that the full collection is + # present when updating via a backref. + old_state, old_dict = instance_state(oldchild),\ + instance_dict(oldchild) + impl = old_state.manager[key].impl + + # tokens to test for a recursive loop. + if not impl.collection and not impl.dynamic: + check_recursive_token = impl._replace_token + else: + check_recursive_token = impl._remove_token + + if initiator is not check_recursive_token: + impl.pop(old_state, + old_dict, + state.obj(), + parent_impl._append_token, + passive=PASSIVE_NO_FETCH) + + if child is not None: + child_state, child_dict = instance_state(child),\ + instance_dict(child) + child_impl = child_state.manager[key].impl + + if initiator.parent_token is not parent_token and \ + initiator.parent_token is not child_impl.parent_token: + _acceptable_key_err(state, initiator, child_impl) + + # tokens to test for a recursive loop. + check_append_token = child_impl._append_token + check_bulk_replace_token = child_impl._bulk_replace_token \ + if child_impl.collection else None + + if initiator is not check_append_token and \ + initiator is not check_bulk_replace_token: + child_impl.append( + child_state, + child_dict, + state.obj(), + initiator, + passive=PASSIVE_NO_FETCH) + return child + + def emit_backref_from_collection_append_event(state, child, initiator): + if child is None: + return + + child_state, child_dict = instance_state(child), \ + instance_dict(child) + child_impl = child_state.manager[key].impl + + if initiator.parent_token is not parent_token and \ + initiator.parent_token is not child_impl.parent_token: + _acceptable_key_err(state, initiator, child_impl) + + # tokens to test for a recursive loop. + check_append_token = child_impl._append_token + check_bulk_replace_token = child_impl._bulk_replace_token \ + if child_impl.collection else None + + if initiator is not check_append_token and \ + initiator is not check_bulk_replace_token: + child_impl.append( + child_state, + child_dict, + state.obj(), + initiator, + passive=PASSIVE_NO_FETCH) + return child + + def emit_backref_from_collection_remove_event(state, child, initiator): + if child is not None: + child_state, child_dict = instance_state(child),\ + instance_dict(child) + child_impl = child_state.manager[key].impl + + # tokens to test for a recursive loop. + if not child_impl.collection and not child_impl.dynamic: + check_remove_token = child_impl._remove_token + check_replace_token = child_impl._replace_token + else: + check_remove_token = child_impl._remove_token + check_replace_token = child_impl._bulk_replace_token \ + if child_impl.collection else None + + if initiator is not check_remove_token and \ + initiator is not check_replace_token: + child_impl.pop( + child_state, + child_dict, + state.obj(), + initiator, + passive=PASSIVE_NO_FETCH) + + if uselist: + event.listen(attribute, "append", + emit_backref_from_collection_append_event, + retval=True, raw=True) + else: + event.listen(attribute, "set", + emit_backref_from_scalar_set_event, + retval=True, raw=True) + # TODO: need coverage in test/orm/ of remove event + event.listen(attribute, "remove", + emit_backref_from_collection_remove_event, + retval=True, raw=True) + +_NO_HISTORY = util.symbol('NO_HISTORY') +_NO_STATE_SYMBOLS = frozenset([ + id(PASSIVE_NO_RESULT), + id(NO_VALUE), + id(NEVER_SET)]) + +History = util.namedtuple("History", [ + "added", "unchanged", "deleted" +]) + + +class History(History): + """A 3-tuple of added, unchanged and deleted values, + representing the changes which have occurred on an instrumented + attribute. + + The easiest way to get a :class:`.History` object for a particular + attribute on an object is to use the :func:`.inspect` function:: + + from sqlalchemy import inspect + + hist = inspect(myobject).attrs.myattribute.history + + Each tuple member is an iterable sequence: + + * ``added`` - the collection of items added to the attribute (the first + tuple element). + + * ``unchanged`` - the collection of items that have not changed on the + attribute (the second tuple element). + + * ``deleted`` - the collection of items that have been removed from the + attribute (the third tuple element). + + """ + + def __bool__(self): + return self != HISTORY_BLANK + __nonzero__ = __bool__ + + def empty(self): + """Return True if this :class:`.History` has no changes + and no existing, unchanged state. + + """ + + return not bool( + (self.added or self.deleted) + or self.unchanged + ) + + def sum(self): + """Return a collection of added + unchanged + deleted.""" + + return (self.added or []) +\ + (self.unchanged or []) +\ + (self.deleted or []) + + def non_deleted(self): + """Return a collection of added + unchanged.""" + + return (self.added or []) +\ + (self.unchanged or []) + + def non_added(self): + """Return a collection of unchanged + deleted.""" + + return (self.unchanged or []) +\ + (self.deleted or []) + + def has_changes(self): + """Return True if this :class:`.History` has changes.""" + + return bool(self.added or self.deleted) + + def as_state(self): + return History( + [(c is not None) + and instance_state(c) or None + for c in self.added], + [(c is not None) + and instance_state(c) or None + for c in self.unchanged], + [(c is not None) + and instance_state(c) or None + for c in self.deleted], + ) + + @classmethod + def from_scalar_attribute(cls, attribute, state, current): + original = state.committed_state.get(attribute.key, _NO_HISTORY) + + if original is _NO_HISTORY: + if current is NEVER_SET: + return cls((), (), ()) + else: + return cls((), [current], ()) + # don't let ClauseElement expressions here trip things up + elif attribute.is_equal(current, original) is True: + return cls((), [current], ()) + else: + # current convention on native scalars is to not + # include information + # about missing previous value in "deleted", but + # we do include None, which helps in some primary + # key situations + if id(original) in _NO_STATE_SYMBOLS: + deleted = () + else: + deleted = [original] + if current is NEVER_SET: + return cls((), (), deleted) + else: + return cls([current], (), deleted) + + @classmethod + def from_object_attribute(cls, attribute, state, current): + original = state.committed_state.get(attribute.key, _NO_HISTORY) + + if original is _NO_HISTORY: + if current is NO_VALUE or current is NEVER_SET: + return cls((), (), ()) + else: + return cls((), [current], ()) + elif current is original: + return cls((), [current], ()) + else: + # current convention on related objects is to not + # include information + # about missing previous value in "deleted", and + # to also not include None - the dependency.py rules + # ignore the None in any case. + if id(original) in _NO_STATE_SYMBOLS or original is None: + deleted = () + else: + deleted = [original] + if current is NO_VALUE or current is NEVER_SET: + return cls((), (), deleted) + else: + return cls([current], (), deleted) + + @classmethod + def from_collection(cls, attribute, state, current): + original = state.committed_state.get(attribute.key, _NO_HISTORY) + + if current is NO_VALUE or current is NEVER_SET: + return cls((), (), ()) + + current = getattr(current, '_sa_adapter') + if original in (NO_VALUE, NEVER_SET): + return cls(list(current), (), ()) + elif original is _NO_HISTORY: + return cls((), list(current), ()) + else: + + current_states = [((c is not None) and instance_state(c) + or None, c) + for c in current + ] + original_states = [((c is not None) and instance_state(c) + or None, c) + for c in original + ] + + current_set = dict(current_states) + original_set = dict(original_states) + + return cls( + [o for s, o in current_states if s not in original_set], + [o for s, o in current_states if s in original_set], + [o for s, o in original_states if s not in current_set] + ) + +HISTORY_BLANK = History(None, None, None) + + +def get_history(obj, key, passive=PASSIVE_OFF): + """Return a :class:`.History` record for the given object + and attribute key. + + :param obj: an object whose class is instrumented by the + attributes package. + + :param key: string attribute name. + + :param passive: indicates loading behavior for the attribute + if the value is not already present. This is a + bitflag attribute, which defaults to the symbol + :attr:`.PASSIVE_OFF` indicating all necessary SQL + should be emitted. + + """ + if passive is True: + util.warn_deprecated("Passing True for 'passive' is deprecated. " + "Use attributes.PASSIVE_NO_INITIALIZE") + passive = PASSIVE_NO_INITIALIZE + elif passive is False: + util.warn_deprecated("Passing False for 'passive' is " + "deprecated. Use attributes.PASSIVE_OFF") + passive = PASSIVE_OFF + + return get_state_history(instance_state(obj), key, passive) + + +def get_state_history(state, key, passive=PASSIVE_OFF): + return state.get_history(key, passive) + + +def has_parent(cls, obj, key, optimistic=False): + """TODO""" + manager = manager_of_class(cls) + state = instance_state(obj) + return manager.has_parent(state, key, optimistic) + + +def register_attribute(class_, key, **kw): + comparator = kw.pop('comparator', None) + parententity = kw.pop('parententity', None) + doc = kw.pop('doc', None) + desc = register_descriptor(class_, key, + comparator, parententity, doc=doc) + register_attribute_impl(class_, key, **kw) + return desc + + +def register_attribute_impl(class_, key, + uselist=False, callable_=None, + useobject=False, + impl_class=None, backref=None, **kw): + + manager = manager_of_class(class_) + if uselist: + factory = kw.pop('typecallable', None) + typecallable = manager.instrument_collection_class( + key, factory or list) + else: + typecallable = kw.pop('typecallable', None) + + dispatch = manager[key].dispatch + + if impl_class: + impl = impl_class(class_, key, typecallable, dispatch, **kw) + elif uselist: + impl = CollectionAttributeImpl(class_, key, callable_, dispatch, + typecallable=typecallable, **kw) + elif useobject: + impl = ScalarObjectAttributeImpl(class_, key, callable_, + dispatch, **kw) + else: + impl = ScalarAttributeImpl(class_, key, callable_, dispatch, **kw) + + manager[key].impl = impl + + if backref: + backref_listeners(manager[key], backref, uselist) + + manager.post_configure_attribute(key) + return manager[key] + + +def register_descriptor(class_, key, comparator=None, + parententity=None, doc=None): + manager = manager_of_class(class_) + + descriptor = InstrumentedAttribute(class_, key, comparator=comparator, + parententity=parententity) + + descriptor.__doc__ = doc + + manager.instrument_attribute(key, descriptor) + return descriptor + + +def unregister_attribute(class_, key): + manager_of_class(class_).uninstrument_attribute(key) + + +def init_collection(obj, key): + """Initialize a collection attribute and return the collection adapter. + + This function is used to provide direct access to collection internals + for a previously unloaded attribute. e.g.:: + + collection_adapter = init_collection(someobject, 'elements') + for elem in values: + collection_adapter.append_without_event(elem) + + For an easier way to do the above, see + :func:`~sqlalchemy.orm.attributes.set_committed_value`. + + obj is an instrumented object instance. An InstanceState + is accepted directly for backwards compatibility but + this usage is deprecated. + + """ + state = instance_state(obj) + dict_ = state.dict + return init_state_collection(state, dict_, key) + + +def init_state_collection(state, dict_, key): + """Initialize a collection attribute and return the collection adapter.""" + + attr = state.manager[key].impl + user_data = attr.initialize(state, dict_) + return attr.get_collection(state, dict_, user_data) + + +def set_committed_value(instance, key, value): + """Set the value of an attribute with no history events. + + Cancels any previous history present. The value should be + a scalar value for scalar-holding attributes, or + an iterable for any collection-holding attribute. + + This is the same underlying method used when a lazy loader + fires off and loads additional data from the database. + In particular, this method can be used by application code + which has loaded additional attributes or collections through + separate queries, which can then be attached to an instance + as though it were part of its original loaded state. + + """ + state, dict_ = instance_state(instance), instance_dict(instance) + state.manager[key].impl.set_committed_value(state, dict_, value) + + +def set_attribute(instance, key, value, initiator=None): + """Set the value of an attribute, firing history events. + + This function may be used regardless of instrumentation + applied directly to the class, i.e. no descriptors are required. + Custom attribute management schemes will need to make usage + of this method to establish attribute state as understood + by SQLAlchemy. + + :param instance: the object that will be modified + + :param key: string name of the attribute + + :param value: value to assign + + :param initiator: an instance of :class:`.Event` that would have + been propagated from a previous event listener. This argument + is used when the :func:`.set_attribute` function is being used within + an existing event listening function where an :class:`.Event` object + is being supplied; the object may be used to track the origin of the + chain of events. + + .. versionadded:: 1.2.3 + + """ + state, dict_ = instance_state(instance), instance_dict(instance) + state.manager[key].impl.set(state, dict_, value, initiator) + + +def get_attribute(instance, key): + """Get the value of an attribute, firing any callables required. + + This function may be used regardless of instrumentation + applied directly to the class, i.e. no descriptors are required. + Custom attribute management schemes will need to make usage + of this method to make usage of attribute state as understood + by SQLAlchemy. + + """ + state, dict_ = instance_state(instance), instance_dict(instance) + return state.manager[key].impl.get(state, dict_) + + +def del_attribute(instance, key): + """Delete the value of an attribute, firing history events. + + This function may be used regardless of instrumentation + applied directly to the class, i.e. no descriptors are required. + Custom attribute management schemes will need to make usage + of this method to establish attribute state as understood + by SQLAlchemy. + + """ + state, dict_ = instance_state(instance), instance_dict(instance) + state.manager[key].impl.delete(state, dict_) + + +def flag_modified(instance, key): + """Mark an attribute on an instance as 'modified'. + + This sets the 'modified' flag on the instance and + establishes an unconditional change event for the given attribute. + The attribute must have a value present, else an + :class:`.InvalidRequestError` is raised. + + To mark an object "dirty" without referring to any specific attribute + so that it is considered within a flush, use the + :func:`.attributes.flag_dirty` call. + + .. seealso:: + + :func:`.attributes.flag_dirty` + + """ + state, dict_ = instance_state(instance), instance_dict(instance) + impl = state.manager[key].impl + impl.dispatch.modified(state, impl._modified_token) + state._modified_event(dict_, impl, NO_VALUE, is_userland=True) + + +def flag_dirty(instance): + """Mark an instance as 'dirty' without any specific attribute mentioned. + + This is a special operation that will allow the object to travel through + the flush process for interception by events such as + :meth:`.SessionEvents.before_flush`. Note that no SQL will be emitted in + the flush process for an object that has no changes, even if marked dirty + via this method. However, a :meth:`.SessionEvents.before_flush` handler + will be able to see the object in the :attr:`.Session.dirty` collection and + may establish changes on it, which will then be included in the SQL + emitted. + + .. versionadded:: 1.2 + + .. seealso:: + + :func:`.attributes.flag_modified` + + """ + + state, dict_ = instance_state(instance), instance_dict(instance) + state._modified_event(dict_, None, NO_VALUE, is_userland=True) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/base.py b/venv/Lib/site-packages/sqlalchemy/orm/base.py new file mode 100644 index 0000000..e06e1fc --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/base.py @@ -0,0 +1,547 @@ +# orm/base.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Constants and rudimental functions used throughout the ORM. + +""" + +from .. import util, inspection, exc as sa_exc +from ..sql import expression +from . import exc +import operator + +PASSIVE_NO_RESULT = util.symbol( + 'PASSIVE_NO_RESULT', + """Symbol returned by a loader callable or other attribute/history + retrieval operation when a value could not be determined, based + on loader callable flags. + """ +) + +ATTR_WAS_SET = util.symbol( + 'ATTR_WAS_SET', + """Symbol returned by a loader callable to indicate the + retrieved value, or values, were assigned to their attributes + on the target object. + """ +) + +ATTR_EMPTY = util.symbol( + 'ATTR_EMPTY', + """Symbol used internally to indicate an attribute had no callable.""" +) + +NO_VALUE = util.symbol( + 'NO_VALUE', + """Symbol which may be placed as the 'previous' value of an attribute, + indicating no value was loaded for an attribute when it was modified, + and flags indicated we were not to load it. + """ +) + +NEVER_SET = util.symbol( + 'NEVER_SET', + """Symbol which may be placed as the 'previous' value of an attribute + indicating that the attribute had not been assigned to previously. + """ +) + +NO_CHANGE = util.symbol( + "NO_CHANGE", + """No callables or SQL should be emitted on attribute access + and no state should change + """, canonical=0 +) + +CALLABLES_OK = util.symbol( + "CALLABLES_OK", + """Loader callables can be fired off if a value + is not present. + """, canonical=1 +) + +SQL_OK = util.symbol( + "SQL_OK", + """Loader callables can emit SQL at least on scalar value attributes.""", + canonical=2 +) + +RELATED_OBJECT_OK = util.symbol( + "RELATED_OBJECT_OK", + """Callables can use SQL to load related objects as well + as scalar value attributes. + """, canonical=4 +) + +INIT_OK = util.symbol( + "INIT_OK", + """Attributes should be initialized with a blank + value (None or an empty collection) upon get, if no other + value can be obtained. + """, canonical=8 +) + +NON_PERSISTENT_OK = util.symbol( + "NON_PERSISTENT_OK", + """Callables can be emitted if the parent is not persistent.""", + canonical=16 +) + +LOAD_AGAINST_COMMITTED = util.symbol( + "LOAD_AGAINST_COMMITTED", + """Callables should use committed values as primary/foreign keys during a + load. + """, canonical=32 +) + +NO_AUTOFLUSH = util.symbol( + "NO_AUTOFLUSH", + """Loader callables should disable autoflush.""", + canonical=64 +) + +# pre-packaged sets of flags used as inputs +PASSIVE_OFF = util.symbol( + "PASSIVE_OFF", + "Callables can be emitted in all cases.", + canonical=(RELATED_OBJECT_OK | NON_PERSISTENT_OK | + INIT_OK | CALLABLES_OK | SQL_OK) +) +PASSIVE_RETURN_NEVER_SET = util.symbol( + "PASSIVE_RETURN_NEVER_SET", + """PASSIVE_OFF ^ INIT_OK""", + canonical=PASSIVE_OFF ^ INIT_OK +) +PASSIVE_NO_INITIALIZE = util.symbol( + "PASSIVE_NO_INITIALIZE", + "PASSIVE_RETURN_NEVER_SET ^ CALLABLES_OK", + canonical=PASSIVE_RETURN_NEVER_SET ^ CALLABLES_OK +) +PASSIVE_NO_FETCH = util.symbol( + "PASSIVE_NO_FETCH", + "PASSIVE_OFF ^ SQL_OK", + canonical=PASSIVE_OFF ^ SQL_OK +) +PASSIVE_NO_FETCH_RELATED = util.symbol( + "PASSIVE_NO_FETCH_RELATED", + "PASSIVE_OFF ^ RELATED_OBJECT_OK", + canonical=PASSIVE_OFF ^ RELATED_OBJECT_OK +) +PASSIVE_ONLY_PERSISTENT = util.symbol( + "PASSIVE_ONLY_PERSISTENT", + "PASSIVE_OFF ^ NON_PERSISTENT_OK", + canonical=PASSIVE_OFF ^ NON_PERSISTENT_OK +) + +DEFAULT_MANAGER_ATTR = '_sa_class_manager' +DEFAULT_STATE_ATTR = '_sa_instance_state' +_INSTRUMENTOR = ('mapper', 'instrumentor') + +EXT_CONTINUE = util.symbol('EXT_CONTINUE') +EXT_STOP = util.symbol('EXT_STOP') + +ONETOMANY = util.symbol( + 'ONETOMANY', + """Indicates the one-to-many direction for a :func:`.relationship`. + + This symbol is typically used by the internals but may be exposed within + certain API features. + + """) + +MANYTOONE = util.symbol( + 'MANYTOONE', + """Indicates the many-to-one direction for a :func:`.relationship`. + + This symbol is typically used by the internals but may be exposed within + certain API features. + + """) + +MANYTOMANY = util.symbol( + 'MANYTOMANY', + """Indicates the many-to-many direction for a :func:`.relationship`. + + This symbol is typically used by the internals but may be exposed within + certain API features. + + """) + +NOT_EXTENSION = util.symbol( + 'NOT_EXTENSION', + """Symbol indicating an :class:`InspectionAttr` that's + not part of sqlalchemy.ext. + + Is assigned to the :attr:`.InspectionAttr.extension_type` + attibute. + + """) + +_never_set = frozenset([NEVER_SET]) + +_none_set = frozenset([None, NEVER_SET, PASSIVE_NO_RESULT]) + +_SET_DEFERRED_EXPIRED = util.symbol("SET_DEFERRED_EXPIRED") + +_DEFER_FOR_STATE = util.symbol("DEFER_FOR_STATE") + + +def _generative(*assertions): + """Mark a method as generative, e.g. method-chained.""" + + @util.decorator + def generate(fn, *args, **kw): + self = args[0]._clone() + for assertion in assertions: + assertion(self, fn.__name__) + fn(self, *args[1:], **kw) + return self + return generate + + +# these can be replaced by sqlalchemy.ext.instrumentation +# if augmented class instrumentation is enabled. +def manager_of_class(cls): + return cls.__dict__.get(DEFAULT_MANAGER_ATTR, None) + +instance_state = operator.attrgetter(DEFAULT_STATE_ATTR) + +instance_dict = operator.attrgetter('__dict__') + + +def instance_str(instance): + """Return a string describing an instance.""" + + return state_str(instance_state(instance)) + + +def state_str(state): + """Return a string describing an instance via its InstanceState.""" + + if state is None: + return "None" + else: + return '<%s at 0x%x>' % (state.class_.__name__, id(state.obj())) + + +def state_class_str(state): + """Return a string describing an instance's class via its + InstanceState. + """ + + if state is None: + return "None" + else: + return '<%s>' % (state.class_.__name__, ) + + +def attribute_str(instance, attribute): + return instance_str(instance) + "." + attribute + + +def state_attribute_str(state, attribute): + return state_str(state) + "." + attribute + + +def object_mapper(instance): + """Given an object, return the primary Mapper associated with the object + instance. + + Raises :class:`sqlalchemy.orm.exc.UnmappedInstanceError` + if no mapping is configured. + + This function is available via the inspection system as:: + + inspect(instance).mapper + + Using the inspection system will raise + :class:`sqlalchemy.exc.NoInspectionAvailable` if the instance is + not part of a mapping. + + """ + return object_state(instance).mapper + + +def object_state(instance): + """Given an object, return the :class:`.InstanceState` + associated with the object. + + Raises :class:`sqlalchemy.orm.exc.UnmappedInstanceError` + if no mapping is configured. + + Equivalent functionality is available via the :func:`.inspect` + function as:: + + inspect(instance) + + Using the inspection system will raise + :class:`sqlalchemy.exc.NoInspectionAvailable` if the instance is + not part of a mapping. + + """ + state = _inspect_mapped_object(instance) + if state is None: + raise exc.UnmappedInstanceError(instance) + else: + return state + + +@inspection._inspects(object) +def _inspect_mapped_object(instance): + try: + return instance_state(instance) + # TODO: whats the py-2/3 syntax to catch two + # different kinds of exceptions at once ? + except exc.UnmappedClassError: + return None + except exc.NO_STATE: + return None + + +def _class_to_mapper(class_or_mapper): + insp = inspection.inspect(class_or_mapper, False) + if insp is not None: + return insp.mapper + else: + raise exc.UnmappedClassError(class_or_mapper) + + +def _mapper_or_none(entity): + """Return the :class:`.Mapper` for the given class or None if the + class is not mapped. + """ + + insp = inspection.inspect(entity, False) + if insp is not None: + return insp.mapper + else: + return None + + +def _is_mapped_class(entity): + """Return True if the given object is a mapped class, + :class:`.Mapper`, or :class:`.AliasedClass`. + """ + + insp = inspection.inspect(entity, False) + return insp is not None and \ + not insp.is_clause_element and \ + ( + insp.is_mapper or insp.is_aliased_class + ) + + +def _attr_as_key(attr): + if hasattr(attr, 'key'): + return attr.key + else: + return expression._column_as_key(attr) + + +def _orm_columns(entity): + insp = inspection.inspect(entity, False) + if hasattr(insp, 'selectable') and hasattr(insp.selectable, 'c'): + return [c for c in insp.selectable.c] + else: + return [entity] + + +def _is_aliased_class(entity): + insp = inspection.inspect(entity, False) + return insp is not None and \ + getattr(insp, "is_aliased_class", False) + + +def _entity_descriptor(entity, key): + """Return a class attribute given an entity and string name. + + May return :class:`.InstrumentedAttribute` or user-defined + attribute. + + """ + insp = inspection.inspect(entity) + if insp.is_selectable: + description = entity + entity = insp.c + elif insp.is_aliased_class: + entity = insp.entity + description = entity + elif hasattr(insp, "mapper"): + description = entity = insp.mapper.class_ + else: + description = entity + + try: + return getattr(entity, key) + except AttributeError: + raise sa_exc.InvalidRequestError( + "Entity '%s' has no property '%s'" % + (description, key) + ) + +_state_mapper = util.dottedgetter('manager.mapper') + + +@inspection._inspects(type) +def _inspect_mapped_class(class_, configure=False): + try: + class_manager = manager_of_class(class_) + if not class_manager.is_mapped: + return None + mapper = class_manager.mapper + except exc.NO_STATE: + return None + else: + if configure and mapper._new_mappers: + mapper._configure_all() + return mapper + + +def class_mapper(class_, configure=True): + """Given a class, return the primary :class:`.Mapper` associated + with the key. + + Raises :exc:`.UnmappedClassError` if no mapping is configured + on the given class, or :exc:`.ArgumentError` if a non-class + object is passed. + + Equivalent functionality is available via the :func:`.inspect` + function as:: + + inspect(some_mapped_class) + + Using the inspection system will raise + :class:`sqlalchemy.exc.NoInspectionAvailable` if the class is not mapped. + + """ + mapper = _inspect_mapped_class(class_, configure=configure) + if mapper is None: + if not isinstance(class_, type): + raise sa_exc.ArgumentError( + "Class object expected, got '%r'." % (class_, )) + raise exc.UnmappedClassError(class_) + else: + return mapper + + +class InspectionAttr(object): + """A base class applied to all ORM objects that can be returned + by the :func:`.inspect` function. + + The attributes defined here allow the usage of simple boolean + checks to test basic facts about the object returned. + + While the boolean checks here are basically the same as using + the Python isinstance() function, the flags here can be used without + the need to import all of these classes, and also such that + the SQLAlchemy class system can change while leaving the flags + here intact for forwards-compatibility. + + """ + __slots__ = () + + is_selectable = False + """Return True if this object is an instance of :class:`.Selectable`.""" + + is_aliased_class = False + """True if this object is an instance of :class:`.AliasedClass`.""" + + is_instance = False + """True if this object is an instance of :class:`.InstanceState`.""" + + is_mapper = False + """True if this object is an instance of :class:`.Mapper`.""" + + is_property = False + """True if this object is an instance of :class:`.MapperProperty`.""" + + is_attribute = False + """True if this object is a Python :term:`descriptor`. + + This can refer to one of many types. Usually a + :class:`.QueryableAttribute` which handles attributes events on behalf + of a :class:`.MapperProperty`. But can also be an extension type + such as :class:`.AssociationProxy` or :class:`.hybrid_property`. + The :attr:`.InspectionAttr.extension_type` will refer to a constant + identifying the specific subtype. + + .. seealso:: + + :attr:`.Mapper.all_orm_descriptors` + + """ + + _is_internal_proxy = False + """True if this object is an internal proxy object. + + .. versionadded:: 1.2.12 + + """ + + is_clause_element = False + """True if this object is an instance of :class:`.ClauseElement`.""" + + extension_type = NOT_EXTENSION + """The extension type, if any. + Defaults to :data:`.interfaces.NOT_EXTENSION` + + .. versionadded:: 0.8.0 + + .. seealso:: + + :data:`.HYBRID_METHOD` + + :data:`.HYBRID_PROPERTY` + + :data:`.ASSOCIATION_PROXY` + + """ + + +class InspectionAttrInfo(InspectionAttr): + """Adds the ``.info`` attribute to :class:`.InspectionAttr`. + + The rationale for :class:`.InspectionAttr` vs. :class:`.InspectionAttrInfo` + is that the former is compatible as a mixin for classes that specify + ``__slots__``; this is essentially an implementation artifact. + + """ + + @util.memoized_property + def info(self): + """Info dictionary associated with the object, allowing user-defined + data to be associated with this :class:`.InspectionAttr`. + + The dictionary is generated when first accessed. Alternatively, + it can be specified as a constructor argument to the + :func:`.column_property`, :func:`.relationship`, or :func:`.composite` + functions. + + .. versionadded:: 0.8 Added support for .info to all + :class:`.MapperProperty` subclasses. + + .. versionchanged:: 1.0.0 :attr:`.MapperProperty.info` is also + available on extension types via the + :attr:`.InspectionAttrInfo.info` attribute, so that it can apply + to a wider variety of ORM and extension constructs. + + .. seealso:: + + :attr:`.QueryableAttribute.info` + + :attr:`.SchemaItem.info` + + """ + return {} + + +class _MappedAttribute(object): + """Mixin for attributes which should be replaced by mapper-assigned + attributes. + + """ + __slots__ = () diff --git a/venv/Lib/site-packages/sqlalchemy/orm/collections.py b/venv/Lib/site-packages/sqlalchemy/orm/collections.py new file mode 100644 index 0000000..5faff83 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/collections.py @@ -0,0 +1,1551 @@ +# orm/collections.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Support for collections of mapped entities. + +The collections package supplies the machinery used to inform the ORM of +collection membership changes. An instrumentation via decoration approach is +used, allowing arbitrary types (including built-ins) to be used as entity +collections without requiring inheritance from a base class. + +Instrumentation decoration relays membership change events to the +:class:`.CollectionAttributeImpl` that is currently managing the collection. +The decorators observe function call arguments and return values, tracking +entities entering or leaving the collection. Two decorator approaches are +provided. One is a bundle of generic decorators that map function arguments +and return values to events:: + + from sqlalchemy.orm.collections import collection + class MyClass(object): + # ... + + @collection.adds(1) + def store(self, item): + self.data.append(item) + + @collection.removes_return() + def pop(self): + return self.data.pop() + + +The second approach is a bundle of targeted decorators that wrap appropriate +append and remove notifiers around the mutation methods present in the +standard Python ``list``, ``set`` and ``dict`` interfaces. These could be +specified in terms of generic decorator recipes, but are instead hand-tooled +for increased efficiency. The targeted decorators occasionally implement +adapter-like behavior, such as mapping bulk-set methods (``extend``, +``update``, ``__setslice__``, etc.) into the series of atomic mutation events +that the ORM requires. + +The targeted decorators are used internally for automatic instrumentation of +entity collection classes. Every collection class goes through a +transformation process roughly like so: + +1. If the class is a built-in, substitute a trivial sub-class +2. Is this class already instrumented? +3. Add in generic decorators +4. Sniff out the collection interface through duck-typing +5. Add targeted decoration to any undecorated interface method + +This process modifies the class at runtime, decorating methods and adding some +bookkeeping properties. This isn't possible (or desirable) for built-in +classes like ``list``, so trivial sub-classes are substituted to hold +decoration:: + + class InstrumentedList(list): + pass + +Collection classes can be specified in ``relationship(collection_class=)`` as +types or a function that returns an instance. Collection classes are +inspected and instrumented during the mapper compilation phase. The +collection_class callable will be executed once to produce a specimen +instance, and the type of that specimen will be instrumented. Functions that +return built-in types like ``lists`` will be adapted to produce instrumented +instances. + +When extending a known type like ``list``, additional decorations are not +generally not needed. Odds are, the extension method will delegate to a +method that's already instrumented. For example:: + + class QueueIsh(list): + def push(self, item): + self.append(item) + def shift(self): + return self.pop(0) + +There's no need to decorate these methods. ``append`` and ``pop`` are already +instrumented as part of the ``list`` interface. Decorating them would fire +duplicate events, which should be avoided. + +The targeted decoration tries not to rely on other methods in the underlying +collection class, but some are unavoidable. Many depend on 'read' methods +being present to properly instrument a 'write', for example, ``__setitem__`` +needs ``__getitem__``. "Bulk" methods like ``update`` and ``extend`` may also +reimplemented in terms of atomic appends and removes, so the ``extend`` +decoration will actually perform many ``append`` operations and not call the +underlying method at all. + +Tight control over bulk operation and the firing of events is also possible by +implementing the instrumentation internally in your methods. The basic +instrumentation package works under the general assumption that collection +mutation will not raise unusual exceptions. If you want to closely +orchestrate append and remove events with exception management, internal +instrumentation may be the answer. Within your method, +``collection_adapter(self)`` will retrieve an object that you can use for +explicit control over triggering append and remove events. + +The owning object and :class:`.CollectionAttributeImpl` are also reachable +through the adapter, allowing for some very sophisticated behavior. + +""" + +import inspect +import operator +import weakref + +from ..sql import expression +from .. import util, exc as sa_exc +from . import base + +from sqlalchemy.util.compat import inspect_getargspec + +__all__ = ['collection', 'collection_adapter', + 'mapped_collection', 'column_mapped_collection', + 'attribute_mapped_collection'] + +__instrumentation_mutex = util.threading.Lock() + + +class _PlainColumnGetter(object): + """Plain column getter, stores collection of Column objects + directly. + + Serializes to a :class:`._SerializableColumnGetterV2` + which has more expensive __call__() performance + and some rare caveats. + + """ + + def __init__(self, cols): + self.cols = cols + self.composite = len(cols) > 1 + + def __reduce__(self): + return _SerializableColumnGetterV2._reduce_from_cols(self.cols) + + def _cols(self, mapper): + return self.cols + + def __call__(self, value): + state = base.instance_state(value) + m = base._state_mapper(state) + + key = [ + m._get_state_attr_by_column(state, state.dict, col) + for col in self._cols(m) + ] + + if self.composite: + return tuple(key) + else: + return key[0] + + +class _SerializableColumnGetter(object): + """Column-based getter used in version 0.7.6 only. + + Remains here for pickle compatibility with 0.7.6. + + """ + + def __init__(self, colkeys): + self.colkeys = colkeys + self.composite = len(colkeys) > 1 + + def __reduce__(self): + return _SerializableColumnGetter, (self.colkeys,) + + def __call__(self, value): + state = base.instance_state(value) + m = base._state_mapper(state) + key = [m._get_state_attr_by_column( + state, state.dict, + m.mapped_table.columns[k]) + for k in self.colkeys] + if self.composite: + return tuple(key) + else: + return key[0] + + +class _SerializableColumnGetterV2(_PlainColumnGetter): + """Updated serializable getter which deals with + multi-table mapped classes. + + Two extremely unusual cases are not supported. + Mappings which have tables across multiple metadata + objects, or which are mapped to non-Table selectables + linked across inheriting mappers may fail to function + here. + + """ + + def __init__(self, colkeys): + self.colkeys = colkeys + self.composite = len(colkeys) > 1 + + def __reduce__(self): + return self.__class__, (self.colkeys,) + + @classmethod + def _reduce_from_cols(cls, cols): + def _table_key(c): + if not isinstance(c.table, expression.TableClause): + return None + else: + return c.table.key + colkeys = [(c.key, _table_key(c)) for c in cols] + return _SerializableColumnGetterV2, (colkeys,) + + def _cols(self, mapper): + cols = [] + metadata = getattr(mapper.local_table, 'metadata', None) + for (ckey, tkey) in self.colkeys: + if tkey is None or \ + metadata is None or \ + tkey not in metadata: + cols.append(mapper.local_table.c[ckey]) + else: + cols.append(metadata.tables[tkey].c[ckey]) + return cols + + +def column_mapped_collection(mapping_spec): + """A dictionary-based collection type with column-based keying. + + Returns a :class:`.MappedCollection` factory with a keying function + generated from mapping_spec, which may be a Column or a sequence + of Columns. + + The key value must be immutable for the lifetime of the object. You + can not, for example, map on foreign key values if those key values will + change during the session, i.e. from None to a database-assigned integer + after a session flush. + + """ + cols = [expression._only_column_elements(q, "mapping_spec") + for q in util.to_list(mapping_spec) + ] + keyfunc = _PlainColumnGetter(cols) + return lambda: MappedCollection(keyfunc) + + +class _SerializableAttrGetter(object): + def __init__(self, name): + self.name = name + self.getter = operator.attrgetter(name) + + def __call__(self, target): + return self.getter(target) + + def __reduce__(self): + return _SerializableAttrGetter, (self.name, ) + + +def attribute_mapped_collection(attr_name): + """A dictionary-based collection type with attribute-based keying. + + Returns a :class:`.MappedCollection` factory with a keying based on the + 'attr_name' attribute of entities in the collection, where ``attr_name`` + is the string name of the attribute. + + The key value must be immutable for the lifetime of the object. You + can not, for example, map on foreign key values if those key values will + change during the session, i.e. from None to a database-assigned integer + after a session flush. + + """ + getter = _SerializableAttrGetter(attr_name) + return lambda: MappedCollection(getter) + + +def mapped_collection(keyfunc): + """A dictionary-based collection type with arbitrary keying. + + Returns a :class:`.MappedCollection` factory with a keying function + generated from keyfunc, a callable that takes an entity and returns a + key value. + + The key value must be immutable for the lifetime of the object. You + can not, for example, map on foreign key values if those key values will + change during the session, i.e. from None to a database-assigned integer + after a session flush. + + """ + return lambda: MappedCollection(keyfunc) + + +class collection(object): + """Decorators for entity collection classes. + + The decorators fall into two groups: annotations and interception recipes. + + The annotating decorators (appender, remover, iterator, linker, converter, + internally_instrumented) indicate the method's purpose and take no + arguments. They are not written with parens:: + + @collection.appender + def append(self, append): ... + + The recipe decorators all require parens, even those that take no + arguments:: + + @collection.adds('entity') + def insert(self, position, entity): ... + + @collection.removes_return() + def popitem(self): ... + + """ + # Bundled as a class solely for ease of use: packaging, doc strings, + # importability. + + @staticmethod + def appender(fn): + """Tag the method as the collection appender. + + The appender method is called with one positional argument: the value + to append. The method will be automatically decorated with 'adds(1)' + if not already decorated:: + + @collection.appender + def add(self, append): ... + + # or, equivalently + @collection.appender + @collection.adds(1) + def add(self, append): ... + + # for mapping type, an 'append' may kick out a previous value + # that occupies that slot. consider d['a'] = 'foo'- any previous + # value in d['a'] is discarded. + @collection.appender + @collection.replaces(1) + def add(self, entity): + key = some_key_func(entity) + previous = None + if key in self: + previous = self[key] + self[key] = entity + return previous + + If the value to append is not allowed in the collection, you may + raise an exception. Something to remember is that the appender + will be called for each object mapped by a database query. If the + database contains rows that violate your collection semantics, you + will need to get creative to fix the problem, as access via the + collection will not work. + + If the appender method is internally instrumented, you must also + receive the keyword argument '_sa_initiator' and ensure its + promulgation to collection events. + + """ + fn._sa_instrument_role = 'appender' + return fn + + @staticmethod + def remover(fn): + """Tag the method as the collection remover. + + The remover method is called with one positional argument: the value + to remove. The method will be automatically decorated with + :meth:`removes_return` if not already decorated:: + + @collection.remover + def zap(self, entity): ... + + # or, equivalently + @collection.remover + @collection.removes_return() + def zap(self, ): ... + + If the value to remove is not present in the collection, you may + raise an exception or return None to ignore the error. + + If the remove method is internally instrumented, you must also + receive the keyword argument '_sa_initiator' and ensure its + promulgation to collection events. + + """ + fn._sa_instrument_role = 'remover' + return fn + + @staticmethod + def iterator(fn): + """Tag the method as the collection remover. + + The iterator method is called with no arguments. It is expected to + return an iterator over all collection members:: + + @collection.iterator + def __iter__(self): ... + + """ + fn._sa_instrument_role = 'iterator' + return fn + + @staticmethod + def internally_instrumented(fn): + """Tag the method as instrumented. + + This tag will prevent any decoration from being applied to the + method. Use this if you are orchestrating your own calls to + :func:`.collection_adapter` in one of the basic SQLAlchemy + interface methods, or to prevent an automatic ABC method + decoration from wrapping your implementation:: + + # normally an 'extend' method on a list-like class would be + # automatically intercepted and re-implemented in terms of + # SQLAlchemy events and append(). your implementation will + # never be called, unless: + @collection.internally_instrumented + def extend(self, items): ... + + """ + fn._sa_instrumented = True + return fn + + @staticmethod + def linker(fn): + """Tag the method as a "linked to attribute" event handler. + + This optional event handler will be called when the collection class + is linked to or unlinked from the InstrumentedAttribute. It is + invoked immediately after the '_sa_adapter' property is set on + the instance. A single argument is passed: the collection adapter + that has been linked, or None if unlinking. + + .. deprecated:: 1.0.0 - the :meth:`.collection.linker` handler + is superseded by the :meth:`.AttributeEvents.init_collection` + and :meth:`.AttributeEvents.dispose_collection` handlers. + + """ + fn._sa_instrument_role = 'linker' + return fn + + link = linker + """deprecated; synonym for :meth:`.collection.linker`.""" + + @staticmethod + def converter(fn): + """Tag the method as the collection converter. + + This optional method will be called when a collection is being + replaced entirely, as in:: + + myobj.acollection = [newvalue1, newvalue2] + + The converter method will receive the object being assigned and should + return an iterable of values suitable for use by the ``appender`` + method. A converter must not assign values or mutate the collection, + its sole job is to adapt the value the user provides into an iterable + of values for the ORM's use. + + The default converter implementation will use duck-typing to do the + conversion. A dict-like collection will be convert into an iterable + of dictionary values, and other types will simply be iterated:: + + @collection.converter + def convert(self, other): ... + + If the duck-typing of the object does not match the type of this + collection, a TypeError is raised. + + Supply an implementation of this method if you want to expand the + range of possible types that can be assigned in bulk or perform + validation on the values about to be assigned. + + """ + fn._sa_instrument_role = 'converter' + return fn + + @staticmethod + def adds(arg): + """Mark the method as adding an entity to the collection. + + Adds "add to collection" handling to the method. The decorator + argument indicates which method argument holds the SQLAlchemy-relevant + value. Arguments can be specified positionally (i.e. integer) or by + name:: + + @collection.adds(1) + def push(self, item): ... + + @collection.adds('entity') + def do_stuff(self, thing, entity=None): ... + + """ + def decorator(fn): + fn._sa_instrument_before = ('fire_append_event', arg) + return fn + return decorator + + @staticmethod + def replaces(arg): + """Mark the method as replacing an entity in the collection. + + Adds "add to collection" and "remove from collection" handling to + the method. The decorator argument indicates which method argument + holds the SQLAlchemy-relevant value to be added, and return value, if + any will be considered the value to remove. + + Arguments can be specified positionally (i.e. integer) or by name:: + + @collection.replaces(2) + def __setitem__(self, index, item): ... + + """ + def decorator(fn): + fn._sa_instrument_before = ('fire_append_event', arg) + fn._sa_instrument_after = 'fire_remove_event' + return fn + return decorator + + @staticmethod + def removes(arg): + """Mark the method as removing an entity in the collection. + + Adds "remove from collection" handling to the method. The decorator + argument indicates which method argument holds the SQLAlchemy-relevant + value to be removed. Arguments can be specified positionally (i.e. + integer) or by name:: + + @collection.removes(1) + def zap(self, item): ... + + For methods where the value to remove is not known at call-time, use + collection.removes_return. + + """ + def decorator(fn): + fn._sa_instrument_before = ('fire_remove_event', arg) + return fn + return decorator + + @staticmethod + def removes_return(): + """Mark the method as removing an entity in the collection. + + Adds "remove from collection" handling to the method. The return + value of the method, if any, is considered the value to remove. The + method arguments are not inspected:: + + @collection.removes_return() + def pop(self): ... + + For methods where the value to remove is known at call-time, use + collection.remove. + + """ + def decorator(fn): + fn._sa_instrument_after = 'fire_remove_event' + return fn + return decorator + + +collection_adapter = operator.attrgetter('_sa_adapter') +"""Fetch the :class:`.CollectionAdapter` for a collection.""" + + +class CollectionAdapter(object): + """Bridges between the ORM and arbitrary Python collections. + + Proxies base-level collection operations (append, remove, iterate) + to the underlying Python collection, and emits add/remove events for + entities entering or leaving the collection. + + The ORM uses :class:`.CollectionAdapter` exclusively for interaction with + entity collections. + + + """ + + __slots__ = ( + 'attr', '_key', '_data', 'owner_state', '_converter', 'invalidated') + + def __init__(self, attr, owner_state, data): + self.attr = attr + self._key = attr.key + self._data = weakref.ref(data) + self.owner_state = owner_state + data._sa_adapter = self + self._converter = data._sa_converter + self.invalidated = False + + def _warn_invalidated(self): + util.warn("This collection has been invalidated.") + + @property + def data(self): + "The entity collection being adapted." + return self._data() + + @property + def _referenced_by_owner(self): + """return True if the owner state still refers to this collection. + + This will return False within a bulk replace operation, + where this collection is the one being replaced. + + """ + return self.owner_state.dict[self._key] is self._data() + + def bulk_appender(self): + return self._data()._sa_appender + + def append_with_event(self, item, initiator=None): + """Add an entity to the collection, firing mutation events.""" + + self._data()._sa_appender(item, _sa_initiator=initiator) + + def append_without_event(self, item): + """Add or restore an entity to the collection, firing no events.""" + self._data()._sa_appender(item, _sa_initiator=False) + + def append_multiple_without_event(self, items): + """Add or restore an entity to the collection, firing no events.""" + appender = self._data()._sa_appender + for item in items: + appender(item, _sa_initiator=False) + + def bulk_remover(self): + return self._data()._sa_remover + + def remove_with_event(self, item, initiator=None): + """Remove an entity from the collection, firing mutation events.""" + self._data()._sa_remover(item, _sa_initiator=initiator) + + def remove_without_event(self, item): + """Remove an entity from the collection, firing no events.""" + self._data()._sa_remover(item, _sa_initiator=False) + + def clear_with_event(self, initiator=None): + """Empty the collection, firing a mutation event for each entity.""" + + remover = self._data()._sa_remover + for item in list(self): + remover(item, _sa_initiator=initiator) + + def clear_without_event(self): + """Empty the collection, firing no events.""" + + remover = self._data()._sa_remover + for item in list(self): + remover(item, _sa_initiator=False) + + def __iter__(self): + """Iterate over entities in the collection.""" + + return iter(self._data()._sa_iterator()) + + def __len__(self): + """Count entities in the collection.""" + return len(list(self._data()._sa_iterator())) + + def __bool__(self): + return True + + __nonzero__ = __bool__ + + def fire_append_event(self, item, initiator=None): + """Notify that a entity has entered the collection. + + Initiator is a token owned by the InstrumentedAttribute that + initiated the membership mutation, and should be left as None + unless you are passing along an initiator value from a chained + operation. + + """ + if initiator is not False: + if self.invalidated: + self._warn_invalidated() + return self.attr.fire_append_event( + self.owner_state, + self.owner_state.dict, + item, initiator) + else: + return item + + def fire_remove_event(self, item, initiator=None): + """Notify that a entity has been removed from the collection. + + Initiator is the InstrumentedAttribute that initiated the membership + mutation, and should be left as None unless you are passing along + an initiator value from a chained operation. + + """ + if initiator is not False: + if self.invalidated: + self._warn_invalidated() + self.attr.fire_remove_event( + self.owner_state, + self.owner_state.dict, + item, initiator) + + def fire_pre_remove_event(self, initiator=None): + """Notify that an entity is about to be removed from the collection. + + Only called if the entity cannot be removed after calling + fire_remove_event(). + + """ + if self.invalidated: + self._warn_invalidated() + self.attr.fire_pre_remove_event( + self.owner_state, + self.owner_state.dict, + initiator=initiator) + + def __getstate__(self): + return {'key': self._key, + 'owner_state': self.owner_state, + 'owner_cls': self.owner_state.class_, + 'data': self.data, + 'invalidated': self.invalidated} + + def __setstate__(self, d): + self._key = d['key'] + self.owner_state = d['owner_state'] + self._data = weakref.ref(d['data']) + self._converter = d['data']._sa_converter + d['data']._sa_adapter = self + self.invalidated = d['invalidated'] + self.attr = getattr(d['owner_cls'], self._key).impl + + +def bulk_replace(values, existing_adapter, new_adapter, initiator=None): + """Load a new collection, firing events based on prior like membership. + + Appends instances in ``values`` onto the ``new_adapter``. Events will be + fired for any instance not present in the ``existing_adapter``. Any + instances in ``existing_adapter`` not present in ``values`` will have + remove events fired upon them. + + :param values: An iterable of collection member instances + + :param existing_adapter: A :class:`.CollectionAdapter` of + instances to be replaced + + :param new_adapter: An empty :class:`.CollectionAdapter` + to load with ``values`` + + + """ + + assert isinstance(values, list) + + idset = util.IdentitySet + existing_idset = idset(existing_adapter or ()) + constants = existing_idset.intersection(values or ()) + additions = idset(values or ()).difference(constants) + removals = existing_idset.difference(constants) + + appender = new_adapter.bulk_appender() + + for member in values or (): + if member in additions: + appender(member, _sa_initiator=initiator) + elif member in constants: + appender(member, _sa_initiator=False) + + if existing_adapter: + for member in removals: + existing_adapter.fire_remove_event(member, initiator=initiator) + + +def prepare_instrumentation(factory): + """Prepare a callable for future use as a collection class factory. + + Given a collection class factory (either a type or no-arg callable), + return another factory that will produce compatible instances when + called. + + This function is responsible for converting collection_class=list + into the run-time behavior of collection_class=InstrumentedList. + + """ + # Convert a builtin to 'Instrumented*' + if factory in __canned_instrumentation: + factory = __canned_instrumentation[factory] + + # Create a specimen + cls = type(factory()) + + # Did factory callable return a builtin? + if cls in __canned_instrumentation: + # Wrap it so that it returns our 'Instrumented*' + factory = __converting_factory(cls, factory) + cls = factory() + + # Instrument the class if needed. + if __instrumentation_mutex.acquire(): + try: + if getattr(cls, '_sa_instrumented', None) != id(cls): + _instrument_class(cls) + finally: + __instrumentation_mutex.release() + + return factory + + +def __converting_factory(specimen_cls, original_factory): + """Return a wrapper that converts a "canned" collection like + set, dict, list into the Instrumented* version. + + """ + + instrumented_cls = __canned_instrumentation[specimen_cls] + + def wrapper(): + collection = original_factory() + return instrumented_cls(collection) + + # often flawed but better than nothing + wrapper.__name__ = "%sWrapper" % original_factory.__name__ + wrapper.__doc__ = original_factory.__doc__ + + return wrapper + + +def _instrument_class(cls): + """Modify methods in a class and install instrumentation.""" + + # In the normal call flow, a request for any of the 3 basic collection + # types is transformed into one of our trivial subclasses + # (e.g. InstrumentedList). Catch anything else that sneaks in here... + if cls.__module__ == '__builtin__': + raise sa_exc.ArgumentError( + "Can not instrument a built-in type. Use a " + "subclass, even a trivial one.") + + roles, methods = _locate_roles_and_methods(cls) + + _setup_canned_roles(cls, roles, methods) + + _assert_required_roles(cls, roles, methods) + + _set_collection_attributes(cls, roles, methods) + + +def _locate_roles_and_methods(cls): + """search for _sa_instrument_role-decorated methods in + method resolution order, assign to roles. + + """ + + roles = {} + methods = {} + + for supercls in cls.__mro__: + for name, method in vars(supercls).items(): + if not util.callable(method): + continue + + # note role declarations + if hasattr(method, '_sa_instrument_role'): + role = method._sa_instrument_role + assert role in ('appender', 'remover', 'iterator', + 'linker', 'converter') + roles.setdefault(role, name) + + # transfer instrumentation requests from decorated function + # to the combined queue + before, after = None, None + if hasattr(method, '_sa_instrument_before'): + op, argument = method._sa_instrument_before + assert op in ('fire_append_event', 'fire_remove_event') + before = op, argument + if hasattr(method, '_sa_instrument_after'): + op = method._sa_instrument_after + assert op in ('fire_append_event', 'fire_remove_event') + after = op + if before: + methods[name] = before + (after, ) + elif after: + methods[name] = None, None, after + return roles, methods + + +def _setup_canned_roles(cls, roles, methods): + """see if this class has "canned" roles based on a known + collection type (dict, set, list). Apply those roles + as needed to the "roles" dictionary, and also + prepare "decorator" methods + + """ + collection_type = util.duck_type_collection(cls) + if collection_type in __interfaces: + canned_roles, decorators = __interfaces[collection_type] + for role, name in canned_roles.items(): + roles.setdefault(role, name) + + # apply ABC auto-decoration to methods that need it + for method, decorator in decorators.items(): + fn = getattr(cls, method, None) + if (fn and method not in methods and + not hasattr(fn, '_sa_instrumented')): + setattr(cls, method, decorator(fn)) + + +def _assert_required_roles(cls, roles, methods): + """ensure all roles are present, and apply implicit instrumentation if + needed + + """ + if 'appender' not in roles or not hasattr(cls, roles['appender']): + raise sa_exc.ArgumentError( + "Type %s must elect an appender method to be " + "a collection class" % cls.__name__) + elif (roles['appender'] not in methods and + not hasattr(getattr(cls, roles['appender']), '_sa_instrumented')): + methods[roles['appender']] = ('fire_append_event', 1, None) + + if 'remover' not in roles or not hasattr(cls, roles['remover']): + raise sa_exc.ArgumentError( + "Type %s must elect a remover method to be " + "a collection class" % cls.__name__) + elif (roles['remover'] not in methods and + not hasattr(getattr(cls, roles['remover']), '_sa_instrumented')): + methods[roles['remover']] = ('fire_remove_event', 1, None) + + if 'iterator' not in roles or not hasattr(cls, roles['iterator']): + raise sa_exc.ArgumentError( + "Type %s must elect an iterator method to be " + "a collection class" % cls.__name__) + + +def _set_collection_attributes(cls, roles, methods): + """apply ad-hoc instrumentation from decorators, class-level defaults + and implicit role declarations + + """ + for method_name, (before, argument, after) in methods.items(): + setattr(cls, method_name, + _instrument_membership_mutator(getattr(cls, method_name), + before, argument, after)) + # intern the role map + for role, method_name in roles.items(): + setattr(cls, '_sa_%s' % role, getattr(cls, method_name)) + + cls._sa_adapter = None + + if not hasattr(cls, '_sa_converter'): + cls._sa_converter = None + cls._sa_instrumented = id(cls) + + +def _instrument_membership_mutator(method, before, argument, after): + """Route method args and/or return value through the collection + adapter.""" + # This isn't smart enough to handle @adds(1) for 'def fn(self, (a, b))' + if before: + fn_args = list(util.flatten_iterator(inspect_getargspec(method)[0])) + if isinstance(argument, int): + pos_arg = argument + named_arg = len(fn_args) > argument and fn_args[argument] or None + else: + if argument in fn_args: + pos_arg = fn_args.index(argument) + else: + pos_arg = None + named_arg = argument + del fn_args + + def wrapper(*args, **kw): + if before: + if pos_arg is None: + if named_arg not in kw: + raise sa_exc.ArgumentError( + "Missing argument %s" % argument) + value = kw[named_arg] + else: + if len(args) > pos_arg: + value = args[pos_arg] + elif named_arg in kw: + value = kw[named_arg] + else: + raise sa_exc.ArgumentError( + "Missing argument %s" % argument) + + initiator = kw.pop('_sa_initiator', None) + if initiator is False: + executor = None + else: + executor = args[0]._sa_adapter + + if before and executor: + getattr(executor, before)(value, initiator) + + if not after or not executor: + return method(*args, **kw) + else: + res = method(*args, **kw) + if res is not None: + getattr(executor, after)(res, initiator) + return res + + wrapper._sa_instrumented = True + if hasattr(method, "_sa_instrument_role"): + wrapper._sa_instrument_role = method._sa_instrument_role + wrapper.__name__ = method.__name__ + wrapper.__doc__ = method.__doc__ + return wrapper + + +def __set(collection, item, _sa_initiator=None): + """Run set events, may eventually be inlined into decorators.""" + + if _sa_initiator is not False: + executor = collection._sa_adapter + if executor: + item = executor.fire_append_event(item, _sa_initiator) + return item + + +def __del(collection, item, _sa_initiator=None): + """Run del events, may eventually be inlined into decorators.""" + if _sa_initiator is not False: + executor = collection._sa_adapter + if executor: + executor.fire_remove_event(item, _sa_initiator) + + +def __before_delete(collection, _sa_initiator=None): + """Special method to run 'commit existing value' methods""" + executor = collection._sa_adapter + if executor: + executor.fire_pre_remove_event(_sa_initiator) + + +def _list_decorators(): + """Tailored instrumentation wrappers for any list-like class.""" + + def _tidy(fn): + fn._sa_instrumented = True + fn.__doc__ = getattr(list, fn.__name__).__doc__ + + def append(fn): + def append(self, item, _sa_initiator=None): + item = __set(self, item, _sa_initiator) + fn(self, item) + _tidy(append) + return append + + def remove(fn): + def remove(self, value, _sa_initiator=None): + __before_delete(self, _sa_initiator) + # testlib.pragma exempt:__eq__ + fn(self, value) + __del(self, value, _sa_initiator) + _tidy(remove) + return remove + + def insert(fn): + def insert(self, index, value): + value = __set(self, value) + fn(self, index, value) + _tidy(insert) + return insert + + def __setitem__(fn): + def __setitem__(self, index, value): + if not isinstance(index, slice): + existing = self[index] + if existing is not None: + __del(self, existing) + value = __set(self, value) + fn(self, index, value) + else: + # slice assignment requires __delitem__, insert, __len__ + step = index.step or 1 + start = index.start or 0 + if start < 0: + start += len(self) + if index.stop is not None: + stop = index.stop + else: + stop = len(self) + if stop < 0: + stop += len(self) + + if step == 1: + for i in range(start, stop, step): + if len(self) > start: + del self[start] + + for i, item in enumerate(value): + self.insert(i + start, item) + else: + rng = list(range(start, stop, step)) + if len(value) != len(rng): + raise ValueError( + "attempt to assign sequence of size %s to " + "extended slice of size %s" % (len(value), + len(rng))) + for i, item in zip(rng, value): + self.__setitem__(i, item) + _tidy(__setitem__) + return __setitem__ + + def __delitem__(fn): + def __delitem__(self, index): + if not isinstance(index, slice): + item = self[index] + __del(self, item) + fn(self, index) + else: + # slice deletion requires __getslice__ and a slice-groking + # __getitem__ for stepped deletion + # note: not breaking this into atomic dels + for item in self[index]: + __del(self, item) + fn(self, index) + _tidy(__delitem__) + return __delitem__ + + if util.py2k: + def __setslice__(fn): + def __setslice__(self, start, end, values): + for value in self[start:end]: + __del(self, value) + values = [__set(self, value) for value in values] + fn(self, start, end, values) + _tidy(__setslice__) + return __setslice__ + + def __delslice__(fn): + def __delslice__(self, start, end): + for value in self[start:end]: + __del(self, value) + fn(self, start, end) + _tidy(__delslice__) + return __delslice__ + + def extend(fn): + def extend(self, iterable): + for value in iterable: + self.append(value) + _tidy(extend) + return extend + + def __iadd__(fn): + def __iadd__(self, iterable): + # list.__iadd__ takes any iterable and seems to let TypeError + # raise as-is instead of returning NotImplemented + for value in iterable: + self.append(value) + return self + _tidy(__iadd__) + return __iadd__ + + def pop(fn): + def pop(self, index=-1): + __before_delete(self) + item = fn(self, index) + __del(self, item) + return item + _tidy(pop) + return pop + + if not util.py2k: + def clear(fn): + def clear(self, index=-1): + for item in self: + __del(self, item) + fn(self) + _tidy(clear) + return clear + + # __imul__ : not wrapping this. all members of the collection are already + # present, so no need to fire appends... wrapping it with an explicit + # decorator is still possible, so events on *= can be had if they're + # desired. hard to imagine a use case for __imul__, though. + + l = locals().copy() + l.pop('_tidy') + return l + + +def _dict_decorators(): + """Tailored instrumentation wrappers for any dict-like mapping class.""" + + def _tidy(fn): + fn._sa_instrumented = True + fn.__doc__ = getattr(dict, fn.__name__).__doc__ + + Unspecified = util.symbol('Unspecified') + + def __setitem__(fn): + def __setitem__(self, key, value, _sa_initiator=None): + if key in self: + __del(self, self[key], _sa_initiator) + value = __set(self, value, _sa_initiator) + fn(self, key, value) + _tidy(__setitem__) + return __setitem__ + + def __delitem__(fn): + def __delitem__(self, key, _sa_initiator=None): + if key in self: + __del(self, self[key], _sa_initiator) + fn(self, key) + _tidy(__delitem__) + return __delitem__ + + def clear(fn): + def clear(self): + for key in self: + __del(self, self[key]) + fn(self) + _tidy(clear) + return clear + + def pop(fn): + def pop(self, key, default=Unspecified): + if key in self: + __del(self, self[key]) + if default is Unspecified: + return fn(self, key) + else: + return fn(self, key, default) + _tidy(pop) + return pop + + def popitem(fn): + def popitem(self): + __before_delete(self) + item = fn(self) + __del(self, item[1]) + return item + _tidy(popitem) + return popitem + + def setdefault(fn): + def setdefault(self, key, default=None): + if key not in self: + self.__setitem__(key, default) + return default + else: + return self.__getitem__(key) + _tidy(setdefault) + return setdefault + + def update(fn): + def update(self, __other=Unspecified, **kw): + if __other is not Unspecified: + if hasattr(__other, 'keys'): + for key in list(__other): + if (key not in self or + self[key] is not __other[key]): + self[key] = __other[key] + else: + for key, value in __other: + if key not in self or self[key] is not value: + self[key] = value + for key in kw: + if key not in self or self[key] is not kw[key]: + self[key] = kw[key] + _tidy(update) + return update + + l = locals().copy() + l.pop('_tidy') + l.pop('Unspecified') + return l + +_set_binop_bases = (set, frozenset) + + +def _set_binops_check_strict(self, obj): + """Allow only set, frozenset and self.__class__-derived + objects in binops.""" + return isinstance(obj, _set_binop_bases + (self.__class__,)) + + +def _set_binops_check_loose(self, obj): + """Allow anything set-like to participate in set binops.""" + return (isinstance(obj, _set_binop_bases + (self.__class__,)) or + util.duck_type_collection(obj) == set) + + +def _set_decorators(): + """Tailored instrumentation wrappers for any set-like class.""" + + def _tidy(fn): + fn._sa_instrumented = True + fn.__doc__ = getattr(set, fn.__name__).__doc__ + + Unspecified = util.symbol('Unspecified') + + def add(fn): + def add(self, value, _sa_initiator=None): + if value not in self: + value = __set(self, value, _sa_initiator) + # testlib.pragma exempt:__hash__ + fn(self, value) + _tidy(add) + return add + + def discard(fn): + def discard(self, value, _sa_initiator=None): + # testlib.pragma exempt:__hash__ + if value in self: + __del(self, value, _sa_initiator) + # testlib.pragma exempt:__hash__ + fn(self, value) + _tidy(discard) + return discard + + def remove(fn): + def remove(self, value, _sa_initiator=None): + # testlib.pragma exempt:__hash__ + if value in self: + __del(self, value, _sa_initiator) + # testlib.pragma exempt:__hash__ + fn(self, value) + _tidy(remove) + return remove + + def pop(fn): + def pop(self): + __before_delete(self) + item = fn(self) + __del(self, item) + return item + _tidy(pop) + return pop + + def clear(fn): + def clear(self): + for item in list(self): + self.remove(item) + _tidy(clear) + return clear + + def update(fn): + def update(self, value): + for item in value: + self.add(item) + _tidy(update) + return update + + def __ior__(fn): + def __ior__(self, value): + if not _set_binops_check_strict(self, value): + return NotImplemented + for item in value: + self.add(item) + return self + _tidy(__ior__) + return __ior__ + + def difference_update(fn): + def difference_update(self, value): + for item in value: + self.discard(item) + _tidy(difference_update) + return difference_update + + def __isub__(fn): + def __isub__(self, value): + if not _set_binops_check_strict(self, value): + return NotImplemented + for item in value: + self.discard(item) + return self + _tidy(__isub__) + return __isub__ + + def intersection_update(fn): + def intersection_update(self, other): + want, have = self.intersection(other), set(self) + remove, add = have - want, want - have + + for item in remove: + self.remove(item) + for item in add: + self.add(item) + _tidy(intersection_update) + return intersection_update + + def __iand__(fn): + def __iand__(self, other): + if not _set_binops_check_strict(self, other): + return NotImplemented + want, have = self.intersection(other), set(self) + remove, add = have - want, want - have + + for item in remove: + self.remove(item) + for item in add: + self.add(item) + return self + _tidy(__iand__) + return __iand__ + + def symmetric_difference_update(fn): + def symmetric_difference_update(self, other): + want, have = self.symmetric_difference(other), set(self) + remove, add = have - want, want - have + + for item in remove: + self.remove(item) + for item in add: + self.add(item) + _tidy(symmetric_difference_update) + return symmetric_difference_update + + def __ixor__(fn): + def __ixor__(self, other): + if not _set_binops_check_strict(self, other): + return NotImplemented + want, have = self.symmetric_difference(other), set(self) + remove, add = have - want, want - have + + for item in remove: + self.remove(item) + for item in add: + self.add(item) + return self + _tidy(__ixor__) + return __ixor__ + + l = locals().copy() + l.pop('_tidy') + l.pop('Unspecified') + return l + + +class InstrumentedList(list): + """An instrumented version of the built-in list.""" + + +class InstrumentedSet(set): + """An instrumented version of the built-in set.""" + + +class InstrumentedDict(dict): + """An instrumented version of the built-in dict.""" + + +__canned_instrumentation = { + list: InstrumentedList, + set: InstrumentedSet, + dict: InstrumentedDict, +} + +__interfaces = { + list: ( + {'appender': 'append', 'remover': 'remove', + 'iterator': '__iter__'}, _list_decorators() + ), + + set: ({'appender': 'add', + 'remover': 'remove', + 'iterator': '__iter__'}, _set_decorators() + ), + + # decorators are required for dicts and object collections. + dict: ({'iterator': 'values'}, _dict_decorators()) if util.py3k + else ({'iterator': 'itervalues'}, _dict_decorators()), +} + + +class MappedCollection(dict): + """A basic dictionary-based collection class. + + Extends dict with the minimal bag semantics that collection + classes require. ``set`` and ``remove`` are implemented in terms + of a keying function: any callable that takes an object and + returns an object for use as a dictionary key. + + """ + + def __init__(self, keyfunc): + """Create a new collection with keying provided by keyfunc. + + keyfunc may be any callable that takes an object and returns an object + for use as a dictionary key. + + The keyfunc will be called every time the ORM needs to add a member by + value-only (such as when loading instances from the database) or + remove a member. The usual cautions about dictionary keying apply- + ``keyfunc(object)`` should return the same output for the life of the + collection. Keying based on mutable properties can result in + unreachable instances "lost" in the collection. + + """ + self.keyfunc = keyfunc + + @collection.appender + @collection.internally_instrumented + def set(self, value, _sa_initiator=None): + """Add an item by value, consulting the keyfunc for the key.""" + + key = self.keyfunc(value) + self.__setitem__(key, value, _sa_initiator) + + @collection.remover + @collection.internally_instrumented + def remove(self, value, _sa_initiator=None): + """Remove an item by value, consulting the keyfunc for the key.""" + + key = self.keyfunc(value) + # Let self[key] raise if key is not in this collection + # testlib.pragma exempt:__ne__ + if self[key] != value: + raise sa_exc.InvalidRequestError( + "Can not remove '%s': collection holds '%s' for key '%s'. " + "Possible cause: is the MappedCollection key function " + "based on mutable properties or properties that only obtain " + "values after flush?" % + (value, self[key], key)) + self.__delitem__(key, _sa_initiator) + + @collection.converter + def _convert(self, dictlike): + """Validate and convert a dict-like object into values for set()ing. + + This is called behind the scenes when a MappedCollection is replaced + entirely by another collection, as in:: + + myobj.mappedcollection = {'a':obj1, 'b': obj2} # ... + + Raises a TypeError if the key in any (key, value) pair in the dictlike + object does not match the key that this collection's keyfunc would + have assigned for that value. + + """ + for incoming_key, value in util.dictlike_iteritems(dictlike): + new_key = self.keyfunc(value) + if incoming_key != new_key: + raise TypeError( + "Found incompatible key %r for value %r; this " + "collection's " + "keying function requires a key of %r for this value." % ( + incoming_key, value, new_key)) + yield value + +# ensure instrumentation is associated with +# these built-in classes; if a user-defined class +# subclasses these and uses @internally_instrumented, +# the superclass is otherwise not instrumented. +# see [ticket:2406]. +_instrument_class(MappedCollection) +_instrument_class(InstrumentedList) +_instrument_class(InstrumentedSet) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/dependency.py b/venv/Lib/site-packages/sqlalchemy/orm/dependency.py new file mode 100644 index 0000000..799e633 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/dependency.py @@ -0,0 +1,1165 @@ +# orm/dependency.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Relationship dependencies. + +""" + +from .. import sql, util, exc as sa_exc +from . import attributes, exc, sync, unitofwork, \ + util as mapperutil +from .interfaces import ONETOMANY, MANYTOONE, MANYTOMANY + + +class DependencyProcessor(object): + def __init__(self, prop): + self.prop = prop + self.cascade = prop.cascade + self.mapper = prop.mapper + self.parent = prop.parent + self.secondary = prop.secondary + self.direction = prop.direction + self.post_update = prop.post_update + self.passive_deletes = prop.passive_deletes + self.passive_updates = prop.passive_updates + self.enable_typechecks = prop.enable_typechecks + if self.passive_deletes: + self._passive_delete_flag = attributes.PASSIVE_NO_INITIALIZE + else: + self._passive_delete_flag = attributes.PASSIVE_OFF + if self.passive_updates: + self._passive_update_flag = attributes.PASSIVE_NO_INITIALIZE + else: + self._passive_update_flag = attributes.PASSIVE_OFF + + self.key = prop.key + if not self.prop.synchronize_pairs: + raise sa_exc.ArgumentError( + "Can't build a DependencyProcessor for relationship %s. " + "No target attributes to populate between parent and " + "child are present" % + self.prop) + + @classmethod + def from_relationship(cls, prop): + return _direction_to_processor[prop.direction](prop) + + def hasparent(self, state): + """return True if the given object instance has a parent, + according to the ``InstrumentedAttribute`` handled by this + ``DependencyProcessor``. + + """ + return self.parent.class_manager.get_impl(self.key).hasparent(state) + + def per_property_preprocessors(self, uow): + """establish actions and dependencies related to a flush. + + These actions will operate on all relevant states in + the aggregate. + + """ + uow.register_preprocessor(self, True) + + def per_property_flush_actions(self, uow): + after_save = unitofwork.ProcessAll(uow, self, False, True) + before_delete = unitofwork.ProcessAll(uow, self, True, True) + + parent_saves = unitofwork.SaveUpdateAll( + uow, + self.parent.primary_base_mapper + ) + child_saves = unitofwork.SaveUpdateAll( + uow, + self.mapper.primary_base_mapper + ) + + parent_deletes = unitofwork.DeleteAll( + uow, + self.parent.primary_base_mapper + ) + child_deletes = unitofwork.DeleteAll( + uow, + self.mapper.primary_base_mapper + ) + + self.per_property_dependencies(uow, + parent_saves, + child_saves, + parent_deletes, + child_deletes, + after_save, + before_delete + ) + + def per_state_flush_actions(self, uow, states, isdelete): + """establish actions and dependencies related to a flush. + + These actions will operate on all relevant states + individually. This occurs only if there are cycles + in the 'aggregated' version of events. + + """ + + child_base_mapper = self.mapper.primary_base_mapper + child_saves = unitofwork.SaveUpdateAll(uow, child_base_mapper) + child_deletes = unitofwork.DeleteAll(uow, child_base_mapper) + + # locate and disable the aggregate processors + # for this dependency + + if isdelete: + before_delete = unitofwork.ProcessAll(uow, self, True, True) + before_delete.disabled = True + else: + after_save = unitofwork.ProcessAll(uow, self, False, True) + after_save.disabled = True + + # check if the "child" side is part of the cycle + + if child_saves not in uow.cycles: + # based on the current dependencies we use, the saves/ + # deletes should always be in the 'cycles' collection + # together. if this changes, we will have to break up + # this method a bit more. + assert child_deletes not in uow.cycles + + # child side is not part of the cycle, so we will link per-state + # actions to the aggregate "saves", "deletes" actions + child_actions = [ + (child_saves, False), (child_deletes, True) + ] + child_in_cycles = False + else: + child_in_cycles = True + + # check if the "parent" side is part of the cycle + if not isdelete: + parent_saves = unitofwork.SaveUpdateAll( + uow, + self.parent.base_mapper) + parent_deletes = before_delete = None + if parent_saves in uow.cycles: + parent_in_cycles = True + else: + parent_deletes = unitofwork.DeleteAll( + uow, + self.parent.base_mapper) + parent_saves = after_save = None + if parent_deletes in uow.cycles: + parent_in_cycles = True + + # now create actions /dependencies for each state. + + for state in states: + # detect if there's anything changed or loaded + # by a preprocessor on this state/attribute. In the + # case of deletes we may try to load missing items here as well. + sum_ = state.manager[self.key].impl.get_all_pending( + state, state.dict, + self._passive_delete_flag + if isdelete + else attributes.PASSIVE_NO_INITIALIZE) + + if not sum_: + continue + + if isdelete: + before_delete = unitofwork.ProcessState(uow, + self, True, state) + if parent_in_cycles: + parent_deletes = unitofwork.DeleteState(uow, state) + else: + after_save = unitofwork.ProcessState(uow, self, False, state) + if parent_in_cycles: + parent_saves = unitofwork.SaveUpdateState(uow, state) + + if child_in_cycles: + child_actions = [] + for child_state, child in sum_: + if child_state not in uow.states: + child_action = (None, None) + else: + (deleted, listonly) = uow.states[child_state] + if deleted: + child_action = ( + unitofwork.DeleteState(uow, child_state), + True) + else: + child_action = ( + unitofwork.SaveUpdateState(uow, child_state), + False) + child_actions.append(child_action) + + # establish dependencies between our possibly per-state + # parent action and our possibly per-state child action. + for child_action, childisdelete in child_actions: + self.per_state_dependencies(uow, parent_saves, + parent_deletes, + child_action, + after_save, before_delete, + isdelete, childisdelete) + + def presort_deletes(self, uowcommit, states): + return False + + def presort_saves(self, uowcommit, states): + return False + + def process_deletes(self, uowcommit, states): + pass + + def process_saves(self, uowcommit, states): + pass + + def prop_has_changes(self, uowcommit, states, isdelete): + if not isdelete or self.passive_deletes: + passive = attributes.PASSIVE_NO_INITIALIZE + elif self.direction is MANYTOONE: + passive = attributes.PASSIVE_NO_FETCH_RELATED + else: + passive = attributes.PASSIVE_OFF + + for s in states: + # TODO: add a high speed method + # to InstanceState which returns: attribute + # has a non-None value, or had one + history = uowcommit.get_attribute_history( + s, + self.key, + passive) + if history and not history.empty(): + return True + else: + return states and \ + not self.prop._is_self_referential and \ + self.mapper in uowcommit.mappers + + def _verify_canload(self, state): + if self.prop.uselist and state is None: + raise exc.FlushError( + "Can't flush None value found in " + "collection %s" % (self.prop, )) + elif state is not None and \ + not self.mapper._canload( + state, allow_subtypes=not self.enable_typechecks): + if self.mapper._canload(state, allow_subtypes=True): + raise exc.FlushError('Attempting to flush an item of type ' + '%(x)s as a member of collection ' + '"%(y)s". Expected an object of type ' + '%(z)s or a polymorphic subclass of ' + 'this type. If %(x)s is a subclass of ' + '%(z)s, configure mapper "%(zm)s" to ' + 'load this subtype polymorphically, or ' + 'set enable_typechecks=False to allow ' + 'any subtype to be accepted for flush. ' + % { + 'x': state.class_, + 'y': self.prop, + 'z': self.mapper.class_, + 'zm': self.mapper, + }) + else: + raise exc.FlushError( + 'Attempting to flush an item of type ' + '%(x)s as a member of collection ' + '"%(y)s". Expected an object of type ' + '%(z)s or a polymorphic subclass of ' + 'this type.' % { + 'x': state.class_, + 'y': self.prop, + 'z': self.mapper.class_, + }) + + def _synchronize(self, state, child, associationrow, + clearkeys, uowcommit): + raise NotImplementedError() + + def _get_reversed_processed_set(self, uow): + if not self.prop._reverse_property: + return None + + process_key = tuple(sorted( + [self.key] + + [p.key for p in self.prop._reverse_property] + )) + return uow.memo( + ('reverse_key', process_key), + set + ) + + def _post_update(self, state, uowcommit, related, is_m2o_delete=False): + for x in related: + if not is_m2o_delete or x is not None: + uowcommit.register_post_update( + state, + [r for l, r in self.prop.synchronize_pairs] + ) + break + + def _pks_changed(self, uowcommit, state): + raise NotImplementedError() + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, self.prop) + + +class OneToManyDP(DependencyProcessor): + + def per_property_dependencies(self, uow, parent_saves, + child_saves, + parent_deletes, + child_deletes, + after_save, + before_delete, + ): + if self.post_update: + child_post_updates = unitofwork.PostUpdateAll( + uow, + self.mapper.primary_base_mapper, + False) + child_pre_updates = unitofwork.PostUpdateAll( + uow, + self.mapper.primary_base_mapper, + True) + + uow.dependencies.update([ + (child_saves, after_save), + (parent_saves, after_save), + (after_save, child_post_updates), + + (before_delete, child_pre_updates), + (child_pre_updates, parent_deletes), + (child_pre_updates, child_deletes), + + ]) + else: + uow.dependencies.update([ + (parent_saves, after_save), + (after_save, child_saves), + (after_save, child_deletes), + + (child_saves, parent_deletes), + (child_deletes, parent_deletes), + + (before_delete, child_saves), + (before_delete, child_deletes), + ]) + + def per_state_dependencies(self, uow, + save_parent, + delete_parent, + child_action, + after_save, before_delete, + isdelete, childisdelete): + + if self.post_update: + + child_post_updates = unitofwork.PostUpdateAll( + uow, + self.mapper.primary_base_mapper, + False) + child_pre_updates = unitofwork.PostUpdateAll( + uow, + self.mapper.primary_base_mapper, + True) + + # TODO: this whole block is not covered + # by any tests + if not isdelete: + if childisdelete: + uow.dependencies.update([ + (child_action, after_save), + (after_save, child_post_updates), + ]) + else: + uow.dependencies.update([ + (save_parent, after_save), + (child_action, after_save), + (after_save, child_post_updates), + ]) + else: + if childisdelete: + uow.dependencies.update([ + (before_delete, child_pre_updates), + (child_pre_updates, delete_parent), + ]) + else: + uow.dependencies.update([ + (before_delete, child_pre_updates), + (child_pre_updates, delete_parent), + ]) + elif not isdelete: + uow.dependencies.update([ + (save_parent, after_save), + (after_save, child_action), + (save_parent, child_action) + ]) + else: + uow.dependencies.update([ + (before_delete, child_action), + (child_action, delete_parent) + ]) + + def presort_deletes(self, uowcommit, states): + # head object is being deleted, and we manage its list of + # child objects the child objects have to have their + # foreign key to the parent set to NULL + should_null_fks = not self.cascade.delete and \ + not self.passive_deletes == 'all' + + for state in states: + history = uowcommit.get_attribute_history( + state, + self.key, + self._passive_delete_flag) + if history: + for child in history.deleted: + if child is not None and self.hasparent(child) is False: + if self.cascade.delete_orphan: + uowcommit.register_object(child, isdelete=True) + else: + uowcommit.register_object(child) + + if should_null_fks: + for child in history.unchanged: + if child is not None: + uowcommit.register_object( + child, operation="delete", prop=self.prop) + + def presort_saves(self, uowcommit, states): + children_added = uowcommit.memo(('children_added', self), set) + + for state in states: + pks_changed = self._pks_changed(uowcommit, state) + + if not pks_changed or self.passive_updates: + passive = attributes.PASSIVE_NO_INITIALIZE + else: + passive = attributes.PASSIVE_OFF + + history = uowcommit.get_attribute_history( + state, + self.key, + passive) + if history: + for child in history.added: + if child is not None: + uowcommit.register_object(child, cancel_delete=True, + operation="add", + prop=self.prop) + + children_added.update(history.added) + + for child in history.deleted: + if not self.cascade.delete_orphan: + uowcommit.register_object(child, isdelete=False, + operation='delete', + prop=self.prop) + elif self.hasparent(child) is False: + uowcommit.register_object( + child, isdelete=True, + operation="delete", prop=self.prop) + for c, m, st_, dct_ in self.mapper.cascade_iterator( + 'delete', child): + uowcommit.register_object( + st_, + isdelete=True) + + if pks_changed: + if history: + for child in history.unchanged: + if child is not None: + uowcommit.register_object( + child, + False, + self.passive_updates, + operation="pk change", + prop=self.prop) + + def process_deletes(self, uowcommit, states): + # head object is being deleted, and we manage its list of + # child objects the child objects have to have their foreign + # key to the parent set to NULL this phase can be called + # safely for any cascade but is unnecessary if delete cascade + # is on. + + if self.post_update or not self.passive_deletes == 'all': + children_added = uowcommit.memo(('children_added', self), set) + + for state in states: + history = uowcommit.get_attribute_history( + state, + self.key, + self._passive_delete_flag) + if history: + for child in history.deleted: + if child is not None and \ + self.hasparent(child) is False: + self._synchronize( + state, + child, + None, True, + uowcommit, False) + if self.post_update and child: + self._post_update(child, uowcommit, [state]) + + if self.post_update or not self.cascade.delete: + for child in set(history.unchanged).\ + difference(children_added): + if child is not None: + self._synchronize( + state, + child, + None, True, + uowcommit, False) + if self.post_update and child: + self._post_update(child, + uowcommit, + [state]) + + # technically, we can even remove each child from the + # collection here too. but this would be a somewhat + # inconsistent behavior since it wouldn't happen + # if the old parent wasn't deleted but child was moved. + + def process_saves(self, uowcommit, states): + for state in states: + history = uowcommit.get_attribute_history( + state, + self.key, + attributes.PASSIVE_NO_INITIALIZE) + if history: + for child in history.added: + self._synchronize(state, child, None, + False, uowcommit, False) + if child is not None and self.post_update: + self._post_update(child, uowcommit, [state]) + + for child in history.deleted: + if not self.cascade.delete_orphan and \ + not self.hasparent(child): + self._synchronize(state, child, None, True, + uowcommit, False) + + if self._pks_changed(uowcommit, state): + for child in history.unchanged: + self._synchronize(state, child, None, + False, uowcommit, True) + + def _synchronize(self, state, child, + associationrow, clearkeys, uowcommit, + pks_changed): + source = state + dest = child + self._verify_canload(child) + if dest is None or \ + (not self.post_update and uowcommit.is_deleted(dest)): + return + if clearkeys: + sync.clear(dest, self.mapper, self.prop.synchronize_pairs) + else: + sync.populate(source, self.parent, dest, self.mapper, + self.prop.synchronize_pairs, uowcommit, + self.passive_updates and pks_changed) + + def _pks_changed(self, uowcommit, state): + return sync.source_modified( + uowcommit, + state, + self.parent, + self.prop.synchronize_pairs) + + +class ManyToOneDP(DependencyProcessor): + def __init__(self, prop): + DependencyProcessor.__init__(self, prop) + self.mapper._dependency_processors.append(DetectKeySwitch(prop)) + + def per_property_dependencies(self, uow, + parent_saves, + child_saves, + parent_deletes, + child_deletes, + after_save, + before_delete): + + if self.post_update: + parent_post_updates = unitofwork.PostUpdateAll( + uow, + self.parent.primary_base_mapper, + False) + parent_pre_updates = unitofwork.PostUpdateAll( + uow, + self.parent.primary_base_mapper, + True) + + uow.dependencies.update([ + (child_saves, after_save), + (parent_saves, after_save), + (after_save, parent_post_updates), + + (after_save, parent_pre_updates), + (before_delete, parent_pre_updates), + + (parent_pre_updates, child_deletes), + (parent_pre_updates, parent_deletes), + ]) + else: + uow.dependencies.update([ + (child_saves, after_save), + (after_save, parent_saves), + (parent_saves, child_deletes), + (parent_deletes, child_deletes) + ]) + + def per_state_dependencies(self, uow, + save_parent, + delete_parent, + child_action, + after_save, before_delete, + isdelete, childisdelete): + + if self.post_update: + + if not isdelete: + parent_post_updates = unitofwork.PostUpdateAll( + uow, + self.parent.primary_base_mapper, + False) + if childisdelete: + uow.dependencies.update([ + (after_save, parent_post_updates), + (parent_post_updates, child_action) + ]) + else: + uow.dependencies.update([ + (save_parent, after_save), + (child_action, after_save), + + (after_save, parent_post_updates) + ]) + else: + parent_pre_updates = unitofwork.PostUpdateAll( + uow, + self.parent.primary_base_mapper, + True) + + uow.dependencies.update([ + (before_delete, parent_pre_updates), + (parent_pre_updates, delete_parent), + (parent_pre_updates, child_action) + ]) + + elif not isdelete: + if not childisdelete: + uow.dependencies.update([ + (child_action, after_save), + (after_save, save_parent), + ]) + else: + uow.dependencies.update([ + (after_save, save_parent), + ]) + + else: + if childisdelete: + uow.dependencies.update([ + (delete_parent, child_action) + ]) + + def presort_deletes(self, uowcommit, states): + if self.cascade.delete or self.cascade.delete_orphan: + for state in states: + history = uowcommit.get_attribute_history( + state, + self.key, + self._passive_delete_flag) + if history: + if self.cascade.delete_orphan: + todelete = history.sum() + else: + todelete = history.non_deleted() + for child in todelete: + if child is None: + continue + uowcommit.register_object( + child, isdelete=True, + operation="delete", prop=self.prop) + t = self.mapper.cascade_iterator('delete', child) + for c, m, st_, dct_ in t: + uowcommit.register_object( + st_, isdelete=True) + + def presort_saves(self, uowcommit, states): + for state in states: + uowcommit.register_object(state, operation="add", prop=self.prop) + if self.cascade.delete_orphan: + history = uowcommit.get_attribute_history( + state, + self.key, + self._passive_delete_flag) + if history: + for child in history.deleted: + if self.hasparent(child) is False: + uowcommit.register_object( + child, isdelete=True, + operation="delete", prop=self.prop) + + t = self.mapper.cascade_iterator('delete', child) + for c, m, st_, dct_ in t: + uowcommit.register_object(st_, isdelete=True) + + def process_deletes(self, uowcommit, states): + if self.post_update and \ + not self.cascade.delete_orphan and \ + not self.passive_deletes == 'all': + + # post_update means we have to update our + # row to not reference the child object + # before we can DELETE the row + for state in states: + self._synchronize(state, None, None, True, uowcommit) + if state and self.post_update: + history = uowcommit.get_attribute_history( + state, + self.key, + self._passive_delete_flag) + if history: + self._post_update( + state, uowcommit, history.sum(), + is_m2o_delete=True) + + def process_saves(self, uowcommit, states): + for state in states: + history = uowcommit.get_attribute_history( + state, + self.key, + attributes.PASSIVE_NO_INITIALIZE) + if history: + if history.added: + for child in history.added: + self._synchronize(state, child, None, False, + uowcommit, "add") + if self.post_update: + self._post_update(state, uowcommit, history.sum()) + + def _synchronize(self, state, child, associationrow, + clearkeys, uowcommit, operation=None): + if state is None or \ + (not self.post_update and uowcommit.is_deleted(state)): + return + + if operation is not None and \ + child is not None and \ + not uowcommit.session._contains_state(child): + util.warn( + "Object of type %s not in session, %s " + "operation along '%s' won't proceed" % + (mapperutil.state_class_str(child), operation, self.prop)) + return + + if clearkeys or child is None: + sync.clear(state, self.parent, self.prop.synchronize_pairs) + else: + self._verify_canload(child) + sync.populate(child, self.mapper, state, + self.parent, + self.prop.synchronize_pairs, + uowcommit, + False) + + +class DetectKeySwitch(DependencyProcessor): + """For many-to-one relationships with no one-to-many backref, + searches for parents through the unit of work when a primary + key has changed and updates them. + + Theoretically, this approach could be expanded to support transparent + deletion of objects referenced via many-to-one as well, although + the current attribute system doesn't do enough bookkeeping for this + to be efficient. + + """ + + def per_property_preprocessors(self, uow): + if self.prop._reverse_property: + if self.passive_updates: + return + else: + if False in (prop.passive_updates for + prop in self.prop._reverse_property): + return + + uow.register_preprocessor(self, False) + + def per_property_flush_actions(self, uow): + parent_saves = unitofwork.SaveUpdateAll( + uow, + self.parent.base_mapper) + after_save = unitofwork.ProcessAll(uow, self, False, False) + uow.dependencies.update([ + (parent_saves, after_save) + ]) + + def per_state_flush_actions(self, uow, states, isdelete): + pass + + def presort_deletes(self, uowcommit, states): + pass + + def presort_saves(self, uow, states): + if not self.passive_updates: + # for non-passive updates, register in the preprocess stage + # so that mapper save_obj() gets a hold of changes + self._process_key_switches(states, uow) + + def prop_has_changes(self, uow, states, isdelete): + if not isdelete and self.passive_updates: + d = self._key_switchers(uow, states) + return bool(d) + + return False + + def process_deletes(self, uowcommit, states): + assert False + + def process_saves(self, uowcommit, states): + # for passive updates, register objects in the process stage + # so that we avoid ManyToOneDP's registering the object without + # the listonly flag in its own preprocess stage (results in UPDATE) + # statements being emitted + assert self.passive_updates + self._process_key_switches(states, uowcommit) + + def _key_switchers(self, uow, states): + switched, notswitched = uow.memo( + ('pk_switchers', self), + lambda: (set(), set()) + ) + + allstates = switched.union(notswitched) + for s in states: + if s not in allstates: + if self._pks_changed(uow, s): + switched.add(s) + else: + notswitched.add(s) + return switched + + def _process_key_switches(self, deplist, uowcommit): + switchers = self._key_switchers(uowcommit, deplist) + if switchers: + # if primary key values have actually changed somewhere, perform + # a linear search through the UOW in search of a parent. + for state in uowcommit.session.identity_map.all_states(): + if not issubclass(state.class_, self.parent.class_): + continue + dict_ = state.dict + related = state.get_impl(self.key).get( + state, dict_, passive=self._passive_update_flag) + if related is not attributes.PASSIVE_NO_RESULT and \ + related is not None: + related_state = attributes.instance_state(dict_[self.key]) + if related_state in switchers: + uowcommit.register_object(state, + False, + self.passive_updates) + sync.populate( + related_state, + self.mapper, state, + self.parent, self.prop.synchronize_pairs, + uowcommit, self.passive_updates) + + def _pks_changed(self, uowcommit, state): + return bool(state.key) and sync.source_modified( + uowcommit, state, self.mapper, self.prop.synchronize_pairs) + + +class ManyToManyDP(DependencyProcessor): + + def per_property_dependencies(self, uow, parent_saves, + child_saves, + parent_deletes, + child_deletes, + after_save, + before_delete + ): + + uow.dependencies.update([ + (parent_saves, after_save), + (child_saves, after_save), + (after_save, child_deletes), + + # a rowswitch on the parent from deleted to saved + # can make this one occur, as the "save" may remove + # an element from the + # "deleted" list before we have a chance to + # process its child rows + (before_delete, parent_saves), + + (before_delete, parent_deletes), + (before_delete, child_deletes), + (before_delete, child_saves), + ]) + + def per_state_dependencies(self, uow, + save_parent, + delete_parent, + child_action, + after_save, before_delete, + isdelete, childisdelete): + if not isdelete: + if childisdelete: + uow.dependencies.update([ + (save_parent, after_save), + (after_save, child_action), + ]) + else: + uow.dependencies.update([ + (save_parent, after_save), + (child_action, after_save), + ]) + else: + uow.dependencies.update([ + (before_delete, child_action), + (before_delete, delete_parent) + ]) + + def presort_deletes(self, uowcommit, states): + # TODO: no tests fail if this whole + # thing is removed !!!! + if not self.passive_deletes: + # if no passive deletes, load history on + # the collection, so that prop_has_changes() + # returns True + for state in states: + uowcommit.get_attribute_history( + state, + self.key, + self._passive_delete_flag) + + def presort_saves(self, uowcommit, states): + if not self.passive_updates: + # if no passive updates, load history on + # each collection where parent has changed PK, + # so that prop_has_changes() returns True + for state in states: + if self._pks_changed(uowcommit, state): + history = uowcommit.get_attribute_history( + state, + self.key, + attributes.PASSIVE_OFF) + + if not self.cascade.delete_orphan: + return + + # check for child items removed from the collection + # if delete_orphan check is turned on. + for state in states: + history = uowcommit.get_attribute_history( + state, + self.key, + attributes.PASSIVE_NO_INITIALIZE) + if history: + for child in history.deleted: + if self.hasparent(child) is False: + uowcommit.register_object( + child, isdelete=True, + operation="delete", prop=self.prop) + for c, m, st_, dct_ in self.mapper.cascade_iterator( + 'delete', + child): + uowcommit.register_object( + st_, isdelete=True) + + def process_deletes(self, uowcommit, states): + secondary_delete = [] + secondary_insert = [] + secondary_update = [] + + processed = self._get_reversed_processed_set(uowcommit) + tmp = set() + for state in states: + # this history should be cached already, as + # we loaded it in preprocess_deletes + history = uowcommit.get_attribute_history( + state, + self.key, + self._passive_delete_flag) + if history: + for child in history.non_added(): + if child is None or \ + (processed is not None and + (state, child) in processed): + continue + associationrow = {} + if not self._synchronize( + state, + child, + associationrow, + False, uowcommit, "delete"): + continue + secondary_delete.append(associationrow) + + tmp.update((c, state) for c in history.non_added()) + + if processed is not None: + processed.update(tmp) + + self._run_crud(uowcommit, secondary_insert, + secondary_update, secondary_delete) + + def process_saves(self, uowcommit, states): + secondary_delete = [] + secondary_insert = [] + secondary_update = [] + + processed = self._get_reversed_processed_set(uowcommit) + tmp = set() + + for state in states: + need_cascade_pks = not self.passive_updates and \ + self._pks_changed(uowcommit, state) + if need_cascade_pks: + passive = attributes.PASSIVE_OFF + else: + passive = attributes.PASSIVE_NO_INITIALIZE + history = uowcommit.get_attribute_history(state, self.key, + passive) + if history: + for child in history.added: + if (processed is not None and + (state, child) in processed): + continue + associationrow = {} + if not self._synchronize(state, + child, + associationrow, + False, uowcommit, "add"): + continue + secondary_insert.append(associationrow) + for child in history.deleted: + if (processed is not None and + (state, child) in processed): + continue + associationrow = {} + if not self._synchronize(state, + child, + associationrow, + False, uowcommit, "delete"): + continue + secondary_delete.append(associationrow) + + tmp.update((c, state) + for c in history.added + history.deleted) + + if need_cascade_pks: + + for child in history.unchanged: + associationrow = {} + sync.update(state, + self.parent, + associationrow, + "old_", + self.prop.synchronize_pairs) + sync.update(child, + self.mapper, + associationrow, + "old_", + self.prop.secondary_synchronize_pairs) + + secondary_update.append(associationrow) + + if processed is not None: + processed.update(tmp) + + self._run_crud(uowcommit, secondary_insert, + secondary_update, secondary_delete) + + def _run_crud(self, uowcommit, secondary_insert, + secondary_update, secondary_delete): + connection = uowcommit.transaction.connection(self.mapper) + + if secondary_delete: + associationrow = secondary_delete[0] + statement = self.secondary.delete(sql.and_(*[ + c == sql.bindparam(c.key, type_=c.type) + for c in self.secondary.c + if c.key in associationrow + ])) + result = connection.execute(statement, secondary_delete) + + if result.supports_sane_multi_rowcount() and \ + result.rowcount != len(secondary_delete): + raise exc.StaleDataError( + "DELETE statement on table '%s' expected to delete " + "%d row(s); Only %d were matched." % + (self.secondary.description, len(secondary_delete), + result.rowcount) + ) + + if secondary_update: + associationrow = secondary_update[0] + statement = self.secondary.update(sql.and_(*[ + c == sql.bindparam("old_" + c.key, type_=c.type) + for c in self.secondary.c + if c.key in associationrow + ])) + result = connection.execute(statement, secondary_update) + + if result.supports_sane_multi_rowcount() and \ + result.rowcount != len(secondary_update): + raise exc.StaleDataError( + "UPDATE statement on table '%s' expected to update " + "%d row(s); Only %d were matched." % + (self.secondary.description, len(secondary_update), + result.rowcount) + ) + + if secondary_insert: + statement = self.secondary.insert() + connection.execute(statement, secondary_insert) + + def _synchronize(self, state, child, associationrow, + clearkeys, uowcommit, operation): + + # this checks for None if uselist=True + self._verify_canload(child) + + # but if uselist=False we get here. If child is None, + # no association row can be generated, so return. + if child is None: + return False + + if child is not None and not uowcommit.session._contains_state(child): + if not child.deleted: + util.warn( + "Object of type %s not in session, %s " + "operation along '%s' won't proceed" % + (mapperutil.state_class_str(child), operation, self.prop)) + return False + + sync.populate_dict(state, self.parent, associationrow, + self.prop.synchronize_pairs) + sync.populate_dict(child, self.mapper, associationrow, + self.prop.secondary_synchronize_pairs) + + return True + + def _pks_changed(self, uowcommit, state): + return sync.source_modified( + uowcommit, + state, + self.parent, + self.prop.synchronize_pairs) + +_direction_to_processor = { + ONETOMANY: OneToManyDP, + MANYTOONE: ManyToOneDP, + MANYTOMANY: ManyToManyDP, +} diff --git a/venv/Lib/site-packages/sqlalchemy/orm/deprecated_interfaces.py b/venv/Lib/site-packages/sqlalchemy/orm/deprecated_interfaces.py new file mode 100644 index 0000000..426288e --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/deprecated_interfaces.py @@ -0,0 +1,487 @@ +# orm/deprecated_interfaces.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from .. import event, util +from .interfaces import EXT_CONTINUE + + +@util.langhelpers.dependency_for("sqlalchemy.orm.interfaces") +class MapperExtension(object): + """Base implementation for :class:`.Mapper` event hooks. + + .. note:: + + :class:`.MapperExtension` is deprecated. Please + refer to :func:`.event.listen` as well as + :class:`.MapperEvents`. + + New extension classes subclass :class:`.MapperExtension` and are specified + using the ``extension`` mapper() argument, which is a single + :class:`.MapperExtension` or a list of such:: + + from sqlalchemy.orm.interfaces import MapperExtension + + class MyExtension(MapperExtension): + def before_insert(self, mapper, connection, instance): + print "instance %s before insert !" % instance + + m = mapper(User, users_table, extension=MyExtension()) + + A single mapper can maintain a chain of ``MapperExtension`` + objects. When a particular mapping event occurs, the + corresponding method on each ``MapperExtension`` is invoked + serially, and each method has the ability to halt the chain + from proceeding further:: + + m = mapper(User, users_table, extension=[ext1, ext2, ext3]) + + Each ``MapperExtension`` method returns the symbol + EXT_CONTINUE by default. This symbol generally means "move + to the next ``MapperExtension`` for processing". For methods + that return objects like translated rows or new object + instances, EXT_CONTINUE means the result of the method + should be ignored. In some cases it's required for a + default mapper activity to be performed, such as adding a + new instance to a result list. + + The symbol EXT_STOP has significance within a chain + of ``MapperExtension`` objects that the chain will be stopped + when this symbol is returned. Like EXT_CONTINUE, it also + has additional significance in some cases that a default + mapper activity will not be performed. + + """ + + @classmethod + def _adapt_instrument_class(cls, self, listener): + cls._adapt_listener_methods(self, listener, ('instrument_class',)) + + @classmethod + def _adapt_listener(cls, self, listener): + cls._adapt_listener_methods( + self, listener, + ( + 'init_instance', + 'init_failed', + 'reconstruct_instance', + 'before_insert', + 'after_insert', + 'before_update', + 'after_update', + 'before_delete', + 'after_delete' + )) + + @classmethod + def _adapt_listener_methods(cls, self, listener, methods): + + for meth in methods: + me_meth = getattr(MapperExtension, meth) + ls_meth = getattr(listener, meth) + + if not util.methods_equivalent(me_meth, ls_meth): + if meth == 'reconstruct_instance': + def go(ls_meth): + def reconstruct(instance, ctx): + ls_meth(self, instance) + return reconstruct + event.listen(self.class_manager, 'load', + go(ls_meth), raw=False, propagate=True) + elif meth == 'init_instance': + def go(ls_meth): + def init_instance(instance, args, kwargs): + ls_meth(self, self.class_, + self.class_manager.original_init, + instance, args, kwargs) + return init_instance + event.listen(self.class_manager, 'init', + go(ls_meth), raw=False, propagate=True) + elif meth == 'init_failed': + def go(ls_meth): + def init_failed(instance, args, kwargs): + util.warn_exception( + ls_meth, self, self.class_, + self.class_manager.original_init, + instance, args, kwargs) + + return init_failed + event.listen(self.class_manager, 'init_failure', + go(ls_meth), raw=False, propagate=True) + else: + event.listen(self, "%s" % meth, ls_meth, + raw=False, retval=True, propagate=True) + + def instrument_class(self, mapper, class_): + """Receive a class when the mapper is first constructed, and has + applied instrumentation to the mapped class. + + The return value is only significant within the ``MapperExtension`` + chain; the parent mapper's behavior isn't modified by this method. + + """ + return EXT_CONTINUE + + def init_instance(self, mapper, class_, oldinit, instance, args, kwargs): + """Receive an instance when its constructor is called. + + This method is only called during a userland construction of + an object. It is not called when an object is loaded from the + database. + + The return value is only significant within the ``MapperExtension`` + chain; the parent mapper's behavior isn't modified by this method. + + """ + return EXT_CONTINUE + + def init_failed(self, mapper, class_, oldinit, instance, args, kwargs): + """Receive an instance when its constructor has been called, + and raised an exception. + + This method is only called during a userland construction of + an object. It is not called when an object is loaded from the + database. + + The return value is only significant within the ``MapperExtension`` + chain; the parent mapper's behavior isn't modified by this method. + + """ + return EXT_CONTINUE + + def reconstruct_instance(self, mapper, instance): + """Receive an object instance after it has been created via + ``__new__``, and after initial attribute population has + occurred. + + This typically occurs when the instance is created based on + incoming result rows, and is only called once for that + instance's lifetime. + + Note that during a result-row load, this method is called upon + the first row received for this instance. Note that some + attributes and collections may or may not be loaded or even + initialized, depending on what's present in the result rows. + + The return value is only significant within the ``MapperExtension`` + chain; the parent mapper's behavior isn't modified by this method. + + """ + return EXT_CONTINUE + + def before_insert(self, mapper, connection, instance): + """Receive an object instance before that instance is inserted + into its table. + + This is a good place to set up primary key values and such + that aren't handled otherwise. + + Column-based attributes can be modified within this method + which will result in the new value being inserted. However + *no* changes to the overall flush plan can be made, and + manipulation of the ``Session`` will not have the desired effect. + To manipulate the ``Session`` within an extension, use + ``SessionExtension``. + + The return value is only significant within the ``MapperExtension`` + chain; the parent mapper's behavior isn't modified by this method. + + """ + + return EXT_CONTINUE + + def after_insert(self, mapper, connection, instance): + """Receive an object instance after that instance is inserted. + + The return value is only significant within the ``MapperExtension`` + chain; the parent mapper's behavior isn't modified by this method. + + """ + + return EXT_CONTINUE + + def before_update(self, mapper, connection, instance): + """Receive an object instance before that instance is updated. + + Note that this method is called for all instances that are marked as + "dirty", even those which have no net changes to their column-based + attributes. An object is marked as dirty when any of its column-based + attributes have a "set attribute" operation called or when any of its + collections are modified. If, at update time, no column-based + attributes have any net changes, no UPDATE statement will be issued. + This means that an instance being sent to before_update is *not* a + guarantee that an UPDATE statement will be issued (although you can + affect the outcome here). + + To detect if the column-based attributes on the object have net + changes, and will therefore generate an UPDATE statement, use + ``object_session(instance).is_modified(instance, + include_collections=False)``. + + Column-based attributes can be modified within this method + which will result in the new value being updated. However + *no* changes to the overall flush plan can be made, and + manipulation of the ``Session`` will not have the desired effect. + To manipulate the ``Session`` within an extension, use + ``SessionExtension``. + + The return value is only significant within the ``MapperExtension`` + chain; the parent mapper's behavior isn't modified by this method. + + """ + + return EXT_CONTINUE + + def after_update(self, mapper, connection, instance): + """Receive an object instance after that instance is updated. + + The return value is only significant within the ``MapperExtension`` + chain; the parent mapper's behavior isn't modified by this method. + + """ + + return EXT_CONTINUE + + def before_delete(self, mapper, connection, instance): + """Receive an object instance before that instance is deleted. + + Note that *no* changes to the overall flush plan can be made + here; and manipulation of the ``Session`` will not have the + desired effect. To manipulate the ``Session`` within an + extension, use ``SessionExtension``. + + The return value is only significant within the ``MapperExtension`` + chain; the parent mapper's behavior isn't modified by this method. + + """ + + return EXT_CONTINUE + + def after_delete(self, mapper, connection, instance): + """Receive an object instance after that instance is deleted. + + The return value is only significant within the ``MapperExtension`` + chain; the parent mapper's behavior isn't modified by this method. + + """ + + return EXT_CONTINUE + + +@util.langhelpers.dependency_for("sqlalchemy.orm.interfaces") +class SessionExtension(object): + + """Base implementation for :class:`.Session` event hooks. + + .. note:: + + :class:`.SessionExtension` is deprecated. Please + refer to :func:`.event.listen` as well as + :class:`.SessionEvents`. + + Subclasses may be installed into a :class:`.Session` (or + :class:`.sessionmaker`) using the ``extension`` keyword + argument:: + + from sqlalchemy.orm.interfaces import SessionExtension + + class MySessionExtension(SessionExtension): + def before_commit(self, session): + print "before commit!" + + Session = sessionmaker(extension=MySessionExtension()) + + The same :class:`.SessionExtension` instance can be used + with any number of sessions. + + """ + + @classmethod + def _adapt_listener(cls, self, listener): + for meth in [ + 'before_commit', + 'after_commit', + 'after_rollback', + 'before_flush', + 'after_flush', + 'after_flush_postexec', + 'after_begin', + 'after_attach', + 'after_bulk_update', + 'after_bulk_delete', + ]: + me_meth = getattr(SessionExtension, meth) + ls_meth = getattr(listener, meth) + + if not util.methods_equivalent(me_meth, ls_meth): + event.listen(self, meth, getattr(listener, meth)) + + def before_commit(self, session): + """Execute right before commit is called. + + Note that this may not be per-flush if a longer running + transaction is ongoing.""" + + def after_commit(self, session): + """Execute after a commit has occurred. + + Note that this may not be per-flush if a longer running + transaction is ongoing.""" + + def after_rollback(self, session): + """Execute after a rollback has occurred. + + Note that this may not be per-flush if a longer running + transaction is ongoing.""" + + def before_flush(self, session, flush_context, instances): + """Execute before flush process has started. + + `instances` is an optional list of objects which were passed to + the ``flush()`` method. """ + + def after_flush(self, session, flush_context): + """Execute after flush has completed, but before commit has been + called. + + Note that the session's state is still in pre-flush, i.e. 'new', + 'dirty', and 'deleted' lists still show pre-flush state as well + as the history settings on instance attributes.""" + + def after_flush_postexec(self, session, flush_context): + """Execute after flush has completed, and after the post-exec + state occurs. + + This will be when the 'new', 'dirty', and 'deleted' lists are in + their final state. An actual commit() may or may not have + occurred, depending on whether or not the flush started its own + transaction or participated in a larger transaction. """ + + def after_begin(self, session, transaction, connection): + """Execute after a transaction is begun on a connection + + `transaction` is the SessionTransaction. This method is called + after an engine level transaction is begun on a connection. """ + + def after_attach(self, session, instance): + """Execute after an instance is attached to a session. + + This is called after an add, delete or merge. """ + + def after_bulk_update(self, session, query, query_context, result): + """Execute after a bulk update operation to the session. + + This is called after a session.query(...).update() + + `query` is the query object that this update operation was + called on. `query_context` was the query context object. + `result` is the result object returned from the bulk operation. + """ + + def after_bulk_delete(self, session, query, query_context, result): + """Execute after a bulk delete operation to the session. + + This is called after a session.query(...).delete() + + `query` is the query object that this delete operation was + called on. `query_context` was the query context object. + `result` is the result object returned from the bulk operation. + """ + + +@util.langhelpers.dependency_for("sqlalchemy.orm.interfaces") +class AttributeExtension(object): + """Base implementation for :class:`.AttributeImpl` event hooks, events + that fire upon attribute mutations in user code. + + .. note:: + + :class:`.AttributeExtension` is deprecated. Please + refer to :func:`.event.listen` as well as + :class:`.AttributeEvents`. + + :class:`.AttributeExtension` is used to listen for set, + remove, and append events on individual mapped attributes. + It is established on an individual mapped attribute using + the `extension` argument, available on + :func:`.column_property`, :func:`.relationship`, and + others:: + + from sqlalchemy.orm.interfaces import AttributeExtension + from sqlalchemy.orm import mapper, relationship, column_property + + class MyAttrExt(AttributeExtension): + def append(self, state, value, initiator): + print "append event !" + return value + + def set(self, state, value, oldvalue, initiator): + print "set event !" + return value + + mapper(SomeClass, sometable, properties={ + 'foo':column_property(sometable.c.foo, extension=MyAttrExt()), + 'bar':relationship(Bar, extension=MyAttrExt()) + }) + + Note that the :class:`.AttributeExtension` methods + :meth:`~.AttributeExtension.append` and + :meth:`~.AttributeExtension.set` need to return the + ``value`` parameter. The returned value is used as the + effective value, and allows the extension to change what is + ultimately persisted. + + AttributeExtension is assembled within the descriptors associated + with a mapped class. + + """ + + active_history = True + """indicates that the set() method would like to receive the 'old' value, + even if it means firing lazy callables. + + Note that ``active_history`` can also be set directly via + :func:`.column_property` and :func:`.relationship`. + + """ + + @classmethod + def _adapt_listener(cls, self, listener): + event.listen(self, 'append', listener.append, + active_history=listener.active_history, + raw=True, retval=True) + event.listen(self, 'remove', listener.remove, + active_history=listener.active_history, + raw=True, retval=True) + event.listen(self, 'set', listener.set, + active_history=listener.active_history, + raw=True, retval=True) + + def append(self, state, value, initiator): + """Receive a collection append event. + + The returned value will be used as the actual value to be + appended. + + """ + return value + + def remove(self, state, value, initiator): + """Receive a remove event. + + No return value is defined. + + """ + pass + + def set(self, state, value, oldvalue, initiator): + """Receive a set event. + + The returned value will be used as the actual value to be + set. + + """ + return value diff --git a/venv/Lib/site-packages/sqlalchemy/orm/descriptor_props.py b/venv/Lib/site-packages/sqlalchemy/orm/descriptor_props.py new file mode 100644 index 0000000..aaf53b6 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/descriptor_props.py @@ -0,0 +1,772 @@ +# orm/descriptor_props.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Descriptor properties are more "auxiliary" properties +that exist as configurational elements, but don't participate +as actively in the load/persist ORM loop. + +""" + +from .interfaces import MapperProperty, PropComparator +from .util import _none_set +from . import attributes +from .. import util, sql, exc as sa_exc, event, schema +from ..sql import expression +from . import properties +from . import query + + +class DescriptorProperty(MapperProperty): + """:class:`.MapperProperty` which proxies access to a + user-defined descriptor.""" + + doc = None + + def instrument_class(self, mapper): + prop = self + + class _ProxyImpl(object): + accepts_scalar_loader = False + expire_missing = True + collection = False + + def __init__(self, key): + self.key = key + + if hasattr(prop, 'get_history'): + def get_history(self, state, dict_, + passive=attributes.PASSIVE_OFF): + return prop.get_history(state, dict_, passive) + + if self.descriptor is None: + desc = getattr(mapper.class_, self.key, None) + if mapper._is_userland_descriptor(desc): + self.descriptor = desc + + if self.descriptor is None: + def fset(obj, value): + setattr(obj, self.name, value) + + def fdel(obj): + delattr(obj, self.name) + + def fget(obj): + return getattr(obj, self.name) + + self.descriptor = property( + fget=fget, + fset=fset, + fdel=fdel, + ) + + proxy_attr = attributes.create_proxied_attribute( + self.descriptor)( + self.parent.class_, + self.key, + self.descriptor, + lambda: self._comparator_factory(mapper), + doc=self.doc, + original_property=self + ) + proxy_attr.impl = _ProxyImpl(self.key) + mapper.class_manager.instrument_attribute(self.key, proxy_attr) + + +@util.langhelpers.dependency_for("sqlalchemy.orm.properties") +class CompositeProperty(DescriptorProperty): + """Defines a "composite" mapped attribute, representing a collection + of columns as one attribute. + + :class:`.CompositeProperty` is constructed using the :func:`.composite` + function. + + .. seealso:: + + :ref:`mapper_composite` + + """ + + def __init__(self, class_, *attrs, **kwargs): + r"""Return a composite column-based property for use with a Mapper. + + See the mapping documentation section :ref:`mapper_composite` for a + full usage example. + + The :class:`.MapperProperty` returned by :func:`.composite` + is the :class:`.CompositeProperty`. + + :param class\_: + The "composite type" class. + + :param \*cols: + List of Column objects to be mapped. + + :param active_history=False: + When ``True``, indicates that the "previous" value for a + scalar attribute should be loaded when replaced, if not + already loaded. See the same flag on :func:`.column_property`. + + .. versionchanged:: 0.7 + This flag specifically becomes meaningful + - previously it was a placeholder. + + :param group: + A group name for this property when marked as deferred. + + :param deferred: + When True, the column property is "deferred", meaning that it does + not load immediately, and is instead loaded when the attribute is + first accessed on an instance. See also + :func:`~sqlalchemy.orm.deferred`. + + :param comparator_factory: a class which extends + :class:`.CompositeProperty.Comparator` which provides custom SQL + clause generation for comparison operations. + + :param doc: + optional string that will be applied as the doc on the + class-bound descriptor. + + :param info: Optional data dictionary which will be populated into the + :attr:`.MapperProperty.info` attribute of this object. + + .. versionadded:: 0.8 + + :param extension: + an :class:`.AttributeExtension` instance, + or list of extensions, which will be prepended to the list of + attribute listeners for the resulting descriptor placed on the + class. **Deprecated.** Please see :class:`.AttributeEvents`. + + """ + super(CompositeProperty, self).__init__() + + self.attrs = attrs + self.composite_class = class_ + self.active_history = kwargs.get('active_history', False) + self.deferred = kwargs.get('deferred', False) + self.group = kwargs.get('group', None) + self.comparator_factory = kwargs.pop('comparator_factory', + self.__class__.Comparator) + if 'info' in kwargs: + self.info = kwargs.pop('info') + + util.set_creation_order(self) + self._create_descriptor() + + def instrument_class(self, mapper): + super(CompositeProperty, self).instrument_class(mapper) + self._setup_event_handlers() + + def do_init(self): + """Initialization which occurs after the :class:`.CompositeProperty` + has been associated with its parent mapper. + + """ + self._setup_arguments_on_columns() + + def _create_descriptor(self): + """Create the Python descriptor that will serve as + the access point on instances of the mapped class. + + """ + + def fget(instance): + dict_ = attributes.instance_dict(instance) + state = attributes.instance_state(instance) + + if self.key not in dict_: + # key not present. Iterate through related + # attributes, retrieve their values. This + # ensures they all load. + values = [ + getattr(instance, key) + for key in self._attribute_keys + ] + + # current expected behavior here is that the composite is + # created on access if the object is persistent or if + # col attributes have non-None. This would be better + # if the composite were created unconditionally, + # but that would be a behavioral change. + if self.key not in dict_ and ( + state.key is not None or + not _none_set.issuperset(values) + ): + dict_[self.key] = self.composite_class(*values) + state.manager.dispatch.refresh(state, None, [self.key]) + + return dict_.get(self.key, None) + + def fset(instance, value): + dict_ = attributes.instance_dict(instance) + state = attributes.instance_state(instance) + attr = state.manager[self.key] + previous = dict_.get(self.key, attributes.NO_VALUE) + for fn in attr.dispatch.set: + value = fn(state, value, previous, attr.impl) + dict_[self.key] = value + if value is None: + for key in self._attribute_keys: + setattr(instance, key, None) + else: + for key, value in zip( + self._attribute_keys, + value.__composite_values__()): + setattr(instance, key, value) + + def fdel(instance): + state = attributes.instance_state(instance) + dict_ = attributes.instance_dict(instance) + previous = dict_.pop(self.key, attributes.NO_VALUE) + attr = state.manager[self.key] + attr.dispatch.remove(state, previous, attr.impl) + for key in self._attribute_keys: + setattr(instance, key, None) + + self.descriptor = property(fget, fset, fdel) + + @util.memoized_property + def _comparable_elements(self): + return [ + getattr(self.parent.class_, prop.key) + for prop in self.props + ] + + @util.memoized_property + def props(self): + props = [] + for attr in self.attrs: + if isinstance(attr, str): + prop = self.parent.get_property( + attr, _configure_mappers=False) + elif isinstance(attr, schema.Column): + prop = self.parent._columntoproperty[attr] + elif isinstance(attr, attributes.InstrumentedAttribute): + prop = attr.property + else: + raise sa_exc.ArgumentError( + "Composite expects Column objects or mapped " + "attributes/attribute names as arguments, got: %r" + % (attr,)) + props.append(prop) + return props + + @property + def columns(self): + return [a for a in self.attrs if isinstance(a, schema.Column)] + + def _setup_arguments_on_columns(self): + """Propagate configuration arguments made on this composite + to the target columns, for those that apply. + + """ + for prop in self.props: + prop.active_history = self.active_history + if self.deferred: + prop.deferred = self.deferred + prop.strategy_key = ( + ("deferred", True), + ("instrument", True)) + prop.group = self.group + + def _setup_event_handlers(self): + """Establish events that populate/expire the composite attribute.""" + + def load_handler(state, *args): + _load_refresh_handler(state, args, is_refresh=False) + + def refresh_handler(state, *args): + _load_refresh_handler(state, args, is_refresh=True) + + def _load_refresh_handler(state, args, is_refresh): + dict_ = state.dict + + if not is_refresh and self.key in dict_: + return + + # if column elements aren't loaded, skip. + # __get__() will initiate a load for those + # columns + for k in self._attribute_keys: + if k not in dict_: + return + + dict_[self.key] = self.composite_class( + *[state.dict[key] for key in + self._attribute_keys] + ) + + def expire_handler(state, keys): + if keys is None or set(self._attribute_keys).intersection(keys): + state.dict.pop(self.key, None) + + def insert_update_handler(mapper, connection, state): + """After an insert or update, some columns may be expired due + to server side defaults, or re-populated due to client side + defaults. Pop out the composite value here so that it + recreates. + + """ + + state.dict.pop(self.key, None) + + event.listen(self.parent, 'after_insert', + insert_update_handler, raw=True) + event.listen(self.parent, 'after_update', + insert_update_handler, raw=True) + event.listen(self.parent, 'load', + load_handler, raw=True, propagate=True) + event.listen(self.parent, 'refresh', + refresh_handler, raw=True, propagate=True) + event.listen(self.parent, 'expire', + expire_handler, raw=True, propagate=True) + + # TODO: need a deserialize hook here + + @util.memoized_property + def _attribute_keys(self): + return [ + prop.key for prop in self.props + ] + + def get_history(self, state, dict_, passive=attributes.PASSIVE_OFF): + """Provided for userland code that uses attributes.get_history().""" + + added = [] + deleted = [] + + has_history = False + for prop in self.props: + key = prop.key + hist = state.manager[key].impl.get_history(state, dict_) + if hist.has_changes(): + has_history = True + + non_deleted = hist.non_deleted() + if non_deleted: + added.extend(non_deleted) + else: + added.append(None) + if hist.deleted: + deleted.extend(hist.deleted) + else: + deleted.append(None) + + if has_history: + return attributes.History( + [self.composite_class(*added)], + (), + [self.composite_class(*deleted)] + ) + else: + return attributes.History( + (), [self.composite_class(*added)], () + ) + + def _comparator_factory(self, mapper): + return self.comparator_factory(self, mapper) + + class CompositeBundle(query.Bundle): + def __init__(self, property, expr): + self.property = property + super(CompositeProperty.CompositeBundle, self).__init__( + property.key, *expr) + + def create_row_processor(self, query, procs, labels): + def proc(row): + return self.property.composite_class( + *[proc(row) for proc in procs]) + return proc + + class Comparator(PropComparator): + """Produce boolean, comparison, and other operators for + :class:`.CompositeProperty` attributes. + + See the example in :ref:`composite_operations` for an overview + of usage , as well as the documentation for :class:`.PropComparator`. + + See also: + + :class:`.PropComparator` + + :class:`.ColumnOperators` + + :ref:`types_operators` + + :attr:`.TypeEngine.comparator_factory` + + """ + + __hash__ = None + + @property + def clauses(self): + return self.__clause_element__() + + def __clause_element__(self): + return expression.ClauseList( + group=False, *self._comparable_elements) + + def _query_clause_element(self): + return CompositeProperty.CompositeBundle( + self.prop, self.__clause_element__()) + + def _bulk_update_tuples(self, value): + if value is None: + values = [None for key in self.prop._attribute_keys] + elif isinstance(value, self.prop.composite_class): + values = value.__composite_values__() + else: + raise sa_exc.ArgumentError( + "Can't UPDATE composite attribute %s to %r" % + (self.prop, value)) + + return zip( + self._comparable_elements, + values + ) + + @util.memoized_property + def _comparable_elements(self): + if self._adapt_to_entity: + return [ + getattr( + self._adapt_to_entity.entity, + prop.key + ) for prop in self.prop._comparable_elements + ] + else: + return self.prop._comparable_elements + + def __eq__(self, other): + if other is None: + values = [None] * len(self.prop._comparable_elements) + else: + values = other.__composite_values__() + comparisons = [ + a == b + for a, b in zip(self.prop._comparable_elements, values) + ] + if self._adapt_to_entity: + comparisons = [self.adapter(x) for x in comparisons] + return sql.and_(*comparisons) + + def __ne__(self, other): + return sql.not_(self.__eq__(other)) + + def __str__(self): + return str(self.parent.class_.__name__) + "." + self.key + + +@util.langhelpers.dependency_for("sqlalchemy.orm.properties") +class ConcreteInheritedProperty(DescriptorProperty): + """A 'do nothing' :class:`.MapperProperty` that disables + an attribute on a concrete subclass that is only present + on the inherited mapper, not the concrete classes' mapper. + + Cases where this occurs include: + + * When the superclass mapper is mapped against a + "polymorphic union", which includes all attributes from + all subclasses. + * When a relationship() is configured on an inherited mapper, + but not on the subclass mapper. Concrete mappers require + that relationship() is configured explicitly on each + subclass. + + """ + + def _comparator_factory(self, mapper): + comparator_callable = None + + for m in self.parent.iterate_to_root(): + p = m._props[self.key] + if not isinstance(p, ConcreteInheritedProperty): + comparator_callable = p.comparator_factory + break + return comparator_callable + + def __init__(self): + super(ConcreteInheritedProperty, self).__init__() + def warn(): + raise AttributeError("Concrete %s does not implement " + "attribute %r at the instance level. Add " + "this property explicitly to %s." % + (self.parent, self.key, self.parent)) + + class NoninheritedConcreteProp(object): + + def __set__(s, obj, value): + warn() + + def __delete__(s, obj): + warn() + + def __get__(s, obj, owner): + if obj is None: + return self.descriptor + warn() + self.descriptor = NoninheritedConcreteProp() + + +@util.langhelpers.dependency_for("sqlalchemy.orm.properties") +class SynonymProperty(DescriptorProperty): + + def __init__(self, name, map_column=None, + descriptor=None, comparator_factory=None, + doc=None, info=None): + """Denote an attribute name as a synonym to a mapped property, + in that the attribute will mirror the value and expression behavior + of another attribute. + + e.g.:: + + class MyClass(Base): + __tablename__ = 'my_table' + + id = Column(Integer, primary_key=True) + job_status = Column(String(50)) + + status = synonym("job_status") + + + :param name: the name of the existing mapped property. This + can refer to the string name ORM-mapped attribute + configured on the class, including column-bound attributes + and relationships. + + :param descriptor: a Python :term:`descriptor` that will be used + as a getter (and potentially a setter) when this attribute is + accessed at the instance level. + + :param map_column: **For classical mappings and mappings against + an existing Table object only**. if ``True``, the :func:`.synonym` + construct will locate the :class:`.Column` object upon the mapped + table that would normally be associated with the attribute name of + this synonym, and produce a new :class:`.ColumnProperty` that instead + maps this :class:`.Column` to the alternate name given as the "name" + argument of the synonym; in this way, the usual step of redefining + the mapping of the :class:`.Column` to be under a different name is + unnecessary. This is usually intended to be used when a + :class:`.Column` is to be replaced with an attribute that also uses a + descriptor, that is, in conjunction with the + :paramref:`.synonym.descriptor` parameter:: + + my_table = Table( + "my_table", metadata, + Column('id', Integer, primary_key=True), + Column('job_status', String(50)) + ) + + class MyClass(object): + @property + def _job_status_descriptor(self): + return "Status: %s" % self._job_status + + + mapper( + MyClass, my_table, properties={ + "job_status": synonym( + "_job_status", map_column=True, + descriptor=MyClass._job_status_descriptor) + } + ) + + Above, the attribute named ``_job_status`` is automatically + mapped to the ``job_status`` column:: + + >>> j1 = MyClass() + >>> j1._job_status = "employed" + >>> j1.job_status + Status: employed + + When using Declarative, in order to provide a descriptor in + conjunction with a synonym, use the + :func:`sqlalchemy.ext.declarative.synonym_for` helper. However, + note that the :ref:`hybrid properties <mapper_hybrids>` feature + should usually be preferred, particularly when redefining attribute + behavior. + + :param info: Optional data dictionary which will be populated into the + :attr:`.InspectionAttr.info` attribute of this object. + + .. versionadded:: 1.0.0 + + :param comparator_factory: A subclass of :class:`.PropComparator` + that will provide custom comparison behavior at the SQL expression + level. + + .. note:: + + For the use case of providing an attribute which redefines both + Python-level and SQL-expression level behavior of an attribute, + please refer to the Hybrid attribute introduced at + :ref:`mapper_hybrids` for a more effective technique. + + .. seealso:: + + :ref:`synonyms` - Overview of synonyms + + :func:`.synonym_for` - a helper oriented towards Declarative + + :ref:`mapper_hybrids` - The Hybrid Attribute extension provides an + updated approach to augmenting attribute behavior more flexibly + than can be achieved with synonyms. + + """ + super(SynonymProperty, self).__init__() + + self.name = name + self.map_column = map_column + self.descriptor = descriptor + self.comparator_factory = comparator_factory + self.doc = doc or (descriptor and descriptor.__doc__) or None + if info: + self.info = info + + util.set_creation_order(self) + + # TODO: when initialized, check _proxied_property, + # emit a warning if its not a column-based property + + @util.memoized_property + def _proxied_property(self): + attr = getattr(self.parent.class_, self.name) + if not hasattr(attr, 'property') or not \ + isinstance(attr.property, MapperProperty): + raise sa_exc.InvalidRequestError( + """synonym() attribute "%s.%s" only supports """ + """ORM mapped attributes, got %r""" % ( + self.parent.class_.__name__, + self.name, + attr + ) + ) + return attr.property + + def _comparator_factory(self, mapper): + prop = self._proxied_property + + if self.comparator_factory: + comp = self.comparator_factory(prop, mapper) + else: + comp = prop.comparator_factory(prop, mapper) + return comp + + def set_parent(self, parent, init): + if self.map_column: + # implement the 'map_column' option. + if self.key not in parent.mapped_table.c: + raise sa_exc.ArgumentError( + "Can't compile synonym '%s': no column on table " + "'%s' named '%s'" + % (self.name, parent.mapped_table.description, self.key)) + elif parent.mapped_table.c[self.key] in \ + parent._columntoproperty and \ + parent._columntoproperty[ + parent.mapped_table.c[self.key] + ].key == self.name: + raise sa_exc.ArgumentError( + "Can't call map_column=True for synonym %r=%r, " + "a ColumnProperty already exists keyed to the name " + "%r for column %r" % + (self.key, self.name, self.name, self.key) + ) + p = properties.ColumnProperty(parent.mapped_table.c[self.key]) + parent._configure_property( + self.name, p, + init=init, + setparent=True) + p._mapped_by_synonym = self.key + + self.parent = parent + + +@util.langhelpers.dependency_for("sqlalchemy.orm.properties") +class ComparableProperty(DescriptorProperty): + """Instruments a Python property for use in query expressions.""" + + def __init__( + self, comparator_factory, descriptor=None, doc=None, info=None): + """Provides a method of applying a :class:`.PropComparator` + to any Python descriptor attribute. + + .. versionchanged:: 0.7 + :func:`.comparable_property` is superseded by + the :mod:`~sqlalchemy.ext.hybrid` extension. See the example + at :ref:`hybrid_custom_comparators`. + + Allows any Python descriptor to behave like a SQL-enabled + attribute when used at the class level in queries, allowing + redefinition of expression operator behavior. + + In the example below we redefine :meth:`.PropComparator.operate` + to wrap both sides of an expression in ``func.lower()`` to produce + case-insensitive comparison:: + + from sqlalchemy.orm import comparable_property + from sqlalchemy.orm.interfaces import PropComparator + from sqlalchemy.sql import func + from sqlalchemy import Integer, String, Column + from sqlalchemy.ext.declarative import declarative_base + + class CaseInsensitiveComparator(PropComparator): + def __clause_element__(self): + return self.prop + + def operate(self, op, other): + return op( + func.lower(self.__clause_element__()), + func.lower(other) + ) + + Base = declarative_base() + + class SearchWord(Base): + __tablename__ = 'search_word' + id = Column(Integer, primary_key=True) + word = Column(String) + word_insensitive = comparable_property(lambda prop, mapper: + CaseInsensitiveComparator( + mapper.c.word, mapper) + ) + + + A mapping like the above allows the ``word_insensitive`` attribute + to render an expression like:: + + >>> print SearchWord.word_insensitive == "Trucks" + lower(search_word.word) = lower(:lower_1) + + :param comparator_factory: + A PropComparator subclass or factory that defines operator behavior + for this property. + + :param descriptor: + Optional when used in a ``properties={}`` declaration. The Python + descriptor or property to layer comparison behavior on top of. + + The like-named descriptor will be automatically retrieved from the + mapped class if left blank in a ``properties`` declaration. + + :param info: Optional data dictionary which will be populated into the + :attr:`.InspectionAttr.info` attribute of this object. + + .. versionadded:: 1.0.0 + + """ + super(ComparableProperty, self).__init__() + self.descriptor = descriptor + self.comparator_factory = comparator_factory + self.doc = doc or (descriptor and descriptor.__doc__) or None + if info: + self.info = info + util.set_creation_order(self) + + def _comparator_factory(self, mapper): + return self.comparator_factory(self, mapper) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/dynamic.py b/venv/Lib/site-packages/sqlalchemy/orm/dynamic.py new file mode 100644 index 0000000..087e7dc --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/dynamic.py @@ -0,0 +1,379 @@ +# orm/dynamic.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Dynamic collection API. + +Dynamic collections act like Query() objects for read operations and support +basic add/delete mutation. + +""" + +from .. import log, util, exc +from ..sql import operators +from . import ( + attributes, object_session, util as orm_util, strategies, + object_mapper, exc as orm_exc, properties +) +from .query import Query + + +@log.class_logger +@properties.RelationshipProperty.strategy_for(lazy="dynamic") +class DynaLoader(strategies.AbstractRelationshipLoader): + def init_class_attribute(self, mapper): + self.is_class_level = True + if not self.uselist: + raise exc.InvalidRequestError( + "On relationship %s, 'dynamic' loaders cannot be used with " + "many-to-one/one-to-one relationships and/or " + "uselist=False." % self.parent_property) + strategies._register_attribute( + self.parent_property, + mapper, + useobject=True, + impl_class=DynamicAttributeImpl, + target_mapper=self.parent_property.mapper, + order_by=self.parent_property.order_by, + query_class=self.parent_property.query_class, + ) + + +class DynamicAttributeImpl(attributes.AttributeImpl): + uses_objects = True + default_accepts_scalar_loader = False + supports_population = False + collection = False + dynamic = True + + def __init__(self, class_, key, typecallable, + dispatch, + target_mapper, order_by, query_class=None, **kw): + super(DynamicAttributeImpl, self).\ + __init__(class_, key, typecallable, dispatch, **kw) + self.target_mapper = target_mapper + self.order_by = order_by + if not query_class: + self.query_class = AppenderQuery + elif AppenderMixin in query_class.mro(): + self.query_class = query_class + else: + self.query_class = mixin_user_query(query_class) + + def get(self, state, dict_, passive=attributes.PASSIVE_OFF): + if not passive & attributes.SQL_OK: + return self._get_collection_history( + state, attributes.PASSIVE_NO_INITIALIZE).added_items + else: + return self.query_class(self, state) + + def get_collection(self, state, dict_, user_data=None, + passive=attributes.PASSIVE_NO_INITIALIZE): + if not passive & attributes.SQL_OK: + return self._get_collection_history(state, + passive).added_items + else: + history = self._get_collection_history(state, passive) + return history.added_plus_unchanged + + @util.memoized_property + def _append_token(self): + return attributes.Event(self, attributes.OP_APPEND) + + @util.memoized_property + def _remove_token(self): + return attributes.Event(self, attributes.OP_REMOVE) + + def fire_append_event(self, state, dict_, value, initiator, + collection_history=None): + if collection_history is None: + collection_history = self._modified_event(state, dict_) + + collection_history.add_added(value) + + for fn in self.dispatch.append: + value = fn(state, value, initiator or self._append_token) + + if self.trackparent and value is not None: + self.sethasparent(attributes.instance_state(value), state, True) + + def fire_remove_event(self, state, dict_, value, initiator, + collection_history=None): + if collection_history is None: + collection_history = self._modified_event(state, dict_) + + collection_history.add_removed(value) + + if self.trackparent and value is not None: + self.sethasparent(attributes.instance_state(value), state, False) + + for fn in self.dispatch.remove: + fn(state, value, initiator or self._remove_token) + + def _modified_event(self, state, dict_): + + if self.key not in state.committed_state: + state.committed_state[self.key] = CollectionHistory(self, state) + + state._modified_event(dict_, + self, + attributes.NEVER_SET) + + # this is a hack to allow the fixtures.ComparableEntity fixture + # to work + dict_[self.key] = True + return state.committed_state[self.key] + + def set(self, state, dict_, value, initiator=None, + passive=attributes.PASSIVE_OFF, + check_old=None, pop=False, _adapt=True): + if initiator and initiator.parent_token is self.parent_token: + return + + if pop and value is None: + return + + iterable = value + new_values = list(iterable) + if state.has_identity: + old_collection = util.IdentitySet(self.get(state, dict_)) + + collection_history = self._modified_event(state, dict_) + if not state.has_identity: + old_collection = collection_history.added_items + else: + old_collection = old_collection.union( + collection_history.added_items) + + idset = util.IdentitySet + constants = old_collection.intersection(new_values) + additions = idset(new_values).difference(constants) + removals = old_collection.difference(constants) + + for member in new_values: + if member in additions: + self.fire_append_event(state, dict_, member, None, + collection_history=collection_history) + + for member in removals: + self.fire_remove_event(state, dict_, member, None, + collection_history=collection_history) + + def delete(self, *args, **kwargs): + raise NotImplementedError() + + def set_committed_value(self, state, dict_, value): + raise NotImplementedError("Dynamic attributes don't support " + "collection population.") + + def get_history(self, state, dict_, passive=attributes.PASSIVE_OFF): + c = self._get_collection_history(state, passive) + return c.as_history() + + def get_all_pending(self, state, dict_, + passive=attributes.PASSIVE_NO_INITIALIZE): + c = self._get_collection_history( + state, passive) + return [ + (attributes.instance_state(x), x) + for x in + c.all_items + ] + + def _get_collection_history(self, state, passive=attributes.PASSIVE_OFF): + if self.key in state.committed_state: + c = state.committed_state[self.key] + else: + c = CollectionHistory(self, state) + + if state.has_identity and (passive & attributes.INIT_OK): + return CollectionHistory(self, state, apply_to=c) + else: + return c + + def append(self, state, dict_, value, initiator, + passive=attributes.PASSIVE_OFF): + if initiator is not self: + self.fire_append_event(state, dict_, value, initiator) + + def remove(self, state, dict_, value, initiator, + passive=attributes.PASSIVE_OFF): + if initiator is not self: + self.fire_remove_event(state, dict_, value, initiator) + + def pop(self, state, dict_, value, initiator, + passive=attributes.PASSIVE_OFF): + self.remove(state, dict_, value, initiator, passive=passive) + + +class AppenderMixin(object): + query_class = None + + def __init__(self, attr, state): + super(AppenderMixin, self).__init__(attr.target_mapper, None) + self.instance = instance = state.obj() + self.attr = attr + + mapper = object_mapper(instance) + prop = mapper._props[self.attr.key] + + if prop.secondary is not None: + # this is a hack right now. The Query only knows how to + # make subsequent joins() without a given left-hand side + # from self._from_obj[0]. We need to ensure prop.secondary + # is in the FROM. So we purposly put the mapper selectable + # in _from_obj[0] to ensure a user-defined join() later on + # doesn't fail, and secondary is then in _from_obj[1]. + self._from_obj = (prop.mapper.selectable, prop.secondary) + + self._criterion = prop._with_parent( + instance, + alias_secondary=False) + + if self.attr.order_by: + self._order_by = self.attr.order_by + + def session(self): + sess = object_session(self.instance) + if sess is not None and self.autoflush and sess.autoflush \ + and self.instance in sess: + sess.flush() + if not orm_util.has_identity(self.instance): + return None + else: + return sess + session = property(session, lambda s, x: None) + + def __iter__(self): + sess = self.session + if sess is None: + return iter(self.attr._get_collection_history( + attributes.instance_state(self.instance), + attributes.PASSIVE_NO_INITIALIZE).added_items) + else: + return iter(self._clone(sess)) + + def __getitem__(self, index): + sess = self.session + if sess is None: + return self.attr._get_collection_history( + attributes.instance_state(self.instance), + attributes.PASSIVE_NO_INITIALIZE).indexed(index) + else: + return self._clone(sess).__getitem__(index) + + def count(self): + sess = self.session + if sess is None: + return len(self.attr._get_collection_history( + attributes.instance_state(self.instance), + attributes.PASSIVE_NO_INITIALIZE).added_items) + else: + return self._clone(sess).count() + + def _clone(self, sess=None): + # note we're returning an entirely new Query class instance + # here without any assignment capabilities; the class of this + # query is determined by the session. + instance = self.instance + if sess is None: + sess = object_session(instance) + if sess is None: + raise orm_exc.DetachedInstanceError( + "Parent instance %s is not bound to a Session, and no " + "contextual session is established; lazy load operation " + "of attribute '%s' cannot proceed" % ( + orm_util.instance_str(instance), self.attr.key)) + + if self.query_class: + query = self.query_class(self.attr.target_mapper, session=sess) + else: + query = sess.query(self.attr.target_mapper) + + query._criterion = self._criterion + query._from_obj = self._from_obj + query._order_by = self._order_by + + return query + + def extend(self, iterator): + for item in iterator: + self.attr.append( + attributes.instance_state(self.instance), + attributes.instance_dict(self.instance), item, None) + + def append(self, item): + self.attr.append( + attributes.instance_state(self.instance), + attributes.instance_dict(self.instance), item, None) + + def remove(self, item): + self.attr.remove( + attributes.instance_state(self.instance), + attributes.instance_dict(self.instance), item, None) + + +class AppenderQuery(AppenderMixin, Query): + """A dynamic query that supports basic collection storage operations.""" + + +def mixin_user_query(cls): + """Return a new class with AppenderQuery functionality layered over.""" + name = 'Appender' + cls.__name__ + return type(name, (AppenderMixin, cls), {'query_class': cls}) + + +class CollectionHistory(object): + """Overrides AttributeHistory to receive append/remove events directly.""" + + def __init__(self, attr, state, apply_to=None): + if apply_to: + coll = AppenderQuery(attr, state).autoflush(False) + self.unchanged_items = util.OrderedIdentitySet(coll) + self.added_items = apply_to.added_items + self.deleted_items = apply_to.deleted_items + self._reconcile_collection = True + else: + self.deleted_items = util.OrderedIdentitySet() + self.added_items = util.OrderedIdentitySet() + self.unchanged_items = util.OrderedIdentitySet() + self._reconcile_collection = False + + @property + def added_plus_unchanged(self): + return list(self.added_items.union(self.unchanged_items)) + + @property + def all_items(self): + return list(self.added_items.union( + self.unchanged_items).union(self.deleted_items)) + + def as_history(self): + if self._reconcile_collection: + added = self.added_items.difference(self.unchanged_items) + deleted = self.deleted_items.intersection(self.unchanged_items) + unchanged = self.unchanged_items.difference(deleted) + else: + added, unchanged, deleted = self.added_items,\ + self.unchanged_items,\ + self.deleted_items + return attributes.History( + list(added), + list(unchanged), + list(deleted), + ) + + def indexed(self, index): + return list(self.added_items)[index] + + def add_added(self, value): + self.added_items.add(value) + + def add_removed(self, value): + if value in self.added_items: + self.added_items.remove(value) + else: + self.deleted_items.add(value) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/evaluator.py b/venv/Lib/site-packages/sqlalchemy/orm/evaluator.py new file mode 100644 index 0000000..4abf08a --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/evaluator.py @@ -0,0 +1,150 @@ +# orm/evaluator.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +import operator +from ..sql import operators +from .. import inspect +from .. import util + + +class UnevaluatableError(Exception): + pass + +_straight_ops = set(getattr(operators, op) + for op in ('add', 'mul', 'sub', + 'div', + 'mod', 'truediv', + 'lt', 'le', 'ne', 'gt', 'ge', 'eq')) + + +_notimplemented_ops = set(getattr(operators, op) + for op in ('like_op', 'notlike_op', 'ilike_op', + 'notilike_op', 'between_op', 'in_op', + 'notin_op', 'endswith_op', 'concat_op')) + + +class EvaluatorCompiler(object): + def __init__(self, target_cls=None): + self.target_cls = target_cls + + def process(self, clause): + meth = getattr(self, "visit_%s" % clause.__visit_name__, None) + if not meth: + raise UnevaluatableError( + "Cannot evaluate %s" % type(clause).__name__) + return meth(clause) + + def visit_grouping(self, clause): + return self.process(clause.element) + + def visit_null(self, clause): + return lambda obj: None + + def visit_false(self, clause): + return lambda obj: False + + def visit_true(self, clause): + return lambda obj: True + + def visit_column(self, clause): + if 'parentmapper' in clause._annotations: + parentmapper = clause._annotations['parentmapper'] + if self.target_cls and not issubclass( + self.target_cls, parentmapper.class_): + raise UnevaluatableError( + "Can't evaluate criteria against alternate class %s" % + parentmapper.class_ + ) + key = parentmapper._columntoproperty[clause].key + else: + key = clause.key + if self.target_cls and \ + key in inspect(self.target_cls).column_attrs: + util.warn( + "Evaluating non-mapped column expression '%s' onto " + "ORM instances; this is a deprecated use case. Please " + "make use of the actual mapped columns in ORM-evaluated " + "UPDATE / DELETE expressions." % clause) + else: + raise UnevaluatableError( + "Cannot evaluate column: %s" % clause + ) + + get_corresponding_attr = operator.attrgetter(key) + return lambda obj: get_corresponding_attr(obj) + + def visit_clauselist(self, clause): + evaluators = list(map(self.process, clause.clauses)) + if clause.operator is operators.or_: + def evaluate(obj): + has_null = False + for sub_evaluate in evaluators: + value = sub_evaluate(obj) + if value: + return True + has_null = has_null or value is None + if has_null: + return None + return False + elif clause.operator is operators.and_: + def evaluate(obj): + for sub_evaluate in evaluators: + value = sub_evaluate(obj) + if not value: + if value is None: + return None + return False + return True + else: + raise UnevaluatableError( + "Cannot evaluate clauselist with operator %s" % + clause.operator) + + return evaluate + + def visit_binary(self, clause): + eval_left, eval_right = list(map(self.process, + [clause.left, clause.right])) + operator = clause.operator + if operator is operators.is_: + def evaluate(obj): + return eval_left(obj) == eval_right(obj) + elif operator is operators.isnot: + def evaluate(obj): + return eval_left(obj) != eval_right(obj) + elif operator in _straight_ops: + def evaluate(obj): + left_val = eval_left(obj) + right_val = eval_right(obj) + if left_val is None or right_val is None: + return None + return operator(eval_left(obj), eval_right(obj)) + else: + raise UnevaluatableError( + "Cannot evaluate %s with operator %s" % + (type(clause).__name__, clause.operator)) + return evaluate + + def visit_unary(self, clause): + eval_inner = self.process(clause.element) + if clause.operator is operators.inv: + def evaluate(obj): + value = eval_inner(obj) + if value is None: + return None + return not value + return evaluate + raise UnevaluatableError( + "Cannot evaluate %s with operator %s" % + (type(clause).__name__, clause.operator)) + + def visit_bindparam(self, clause): + if clause.callable: + val = clause.callable() + else: + val = clause.value + return lambda obj: val diff --git a/venv/Lib/site-packages/sqlalchemy/orm/events.py b/venv/Lib/site-packages/sqlalchemy/orm/events.py new file mode 100644 index 0000000..f27849e --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/events.py @@ -0,0 +1,2275 @@ +# orm/events.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""ORM event interfaces. + +""" +from .. import event, exc, util +from .base import _mapper_or_none +import inspect +import weakref +from . import interfaces +from . import mapperlib, instrumentation +from .session import Session, sessionmaker +from .scoping import scoped_session +from .attributes import QueryableAttribute +from .query import Query +from sqlalchemy.util.compat import inspect_getargspec + +class InstrumentationEvents(event.Events): + """Events related to class instrumentation events. + + The listeners here support being established against + any new style class, that is any object that is a subclass + of 'type'. Events will then be fired off for events + against that class. If the "propagate=True" flag is passed + to event.listen(), the event will fire off for subclasses + of that class as well. + + The Python ``type`` builtin is also accepted as a target, + which when used has the effect of events being emitted + for all classes. + + Note the "propagate" flag here is defaulted to ``True``, + unlike the other class level events where it defaults + to ``False``. This means that new subclasses will also + be the subject of these events, when a listener + is established on a superclass. + + .. versionchanged:: 0.8 - events here will emit based + on comparing the incoming class to the type of class + passed to :func:`.event.listen`. Previously, the + event would fire for any class unconditionally regardless + of what class was sent for listening, despite + documentation which stated the contrary. + + """ + + _target_class_doc = "SomeBaseClass" + _dispatch_target = instrumentation.InstrumentationFactory + + @classmethod + def _accept_with(cls, target): + if isinstance(target, type): + return _InstrumentationEventsHold(target) + else: + return None + + @classmethod + def _listen(cls, event_key, propagate=True, **kw): + target, identifier, fn = \ + event_key.dispatch_target, event_key.identifier, \ + event_key._listen_fn + + def listen(target_cls, *arg): + listen_cls = target() + if propagate and issubclass(target_cls, listen_cls): + return fn(target_cls, *arg) + elif not propagate and target_cls is listen_cls: + return fn(target_cls, *arg) + + def remove(ref): + key = event.registry._EventKey( + None, identifier, listen, + instrumentation._instrumentation_factory) + getattr(instrumentation._instrumentation_factory.dispatch, + identifier).remove(key) + + target = weakref.ref(target.class_, remove) + + event_key.\ + with_dispatch_target(instrumentation._instrumentation_factory).\ + with_wrapper(listen).base_listen(**kw) + + @classmethod + def _clear(cls): + super(InstrumentationEvents, cls)._clear() + instrumentation._instrumentation_factory.dispatch._clear() + + def class_instrument(self, cls): + """Called after the given class is instrumented. + + To get at the :class:`.ClassManager`, use + :func:`.manager_of_class`. + + """ + + def class_uninstrument(self, cls): + """Called before the given class is uninstrumented. + + To get at the :class:`.ClassManager`, use + :func:`.manager_of_class`. + + """ + + def attribute_instrument(self, cls, key, inst): + """Called when an attribute is instrumented.""" + + +class _InstrumentationEventsHold(object): + """temporary marker object used to transfer from _accept_with() to + _listen() on the InstrumentationEvents class. + + """ + + def __init__(self, class_): + self.class_ = class_ + + dispatch = event.dispatcher(InstrumentationEvents) + + +class InstanceEvents(event.Events): + """Define events specific to object lifecycle. + + e.g.:: + + from sqlalchemy import event + + def my_load_listener(target, context): + print "on load!" + + event.listen(SomeClass, 'load', my_load_listener) + + Available targets include: + + * mapped classes + * unmapped superclasses of mapped or to-be-mapped classes + (using the ``propagate=True`` flag) + * :class:`.Mapper` objects + * the :class:`.Mapper` class itself and the :func:`.mapper` + function indicate listening for all mappers. + + .. versionchanged:: 0.8.0 instance events can be associated with + unmapped superclasses of mapped classes. + + Instance events are closely related to mapper events, but + are more specific to the instance and its instrumentation, + rather than its system of persistence. + + When using :class:`.InstanceEvents`, several modifiers are + available to the :func:`.event.listen` function. + + :param propagate=False: When True, the event listener should + be applied to all inheriting classes as well as the + class which is the target of this listener. + :param raw=False: When True, the "target" argument passed + to applicable event listener functions will be the + instance's :class:`.InstanceState` management + object, rather than the mapped instance itself. + + """ + + _target_class_doc = "SomeClass" + + _dispatch_target = instrumentation.ClassManager + + @classmethod + def _new_classmanager_instance(cls, class_, classmanager): + _InstanceEventsHold.populate(class_, classmanager) + + @classmethod + @util.dependencies("sqlalchemy.orm") + def _accept_with(cls, orm, target): + if isinstance(target, instrumentation.ClassManager): + return target + elif isinstance(target, mapperlib.Mapper): + return target.class_manager + elif target is orm.mapper: + return instrumentation.ClassManager + elif isinstance(target, type): + if issubclass(target, mapperlib.Mapper): + return instrumentation.ClassManager + else: + manager = instrumentation.manager_of_class(target) + if manager: + return manager + else: + return _InstanceEventsHold(target) + return None + + @classmethod + def _listen(cls, event_key, raw=False, propagate=False, **kw): + target, identifier, fn = \ + event_key.dispatch_target, event_key.identifier, \ + event_key._listen_fn + + if not raw: + def wrap(state, *arg, **kw): + return fn(state.obj(), *arg, **kw) + event_key = event_key.with_wrapper(wrap) + + event_key.base_listen(propagate=propagate, **kw) + + if propagate: + for mgr in target.subclass_managers(True): + event_key.with_dispatch_target(mgr).base_listen( + propagate=True) + + @classmethod + def _clear(cls): + super(InstanceEvents, cls)._clear() + _InstanceEventsHold._clear() + + def first_init(self, manager, cls): + """Called when the first instance of a particular mapping is called. + + This event is called when the ``__init__`` method of a class + is called the first time for that particular class. The event + invokes before ``__init__`` actually proceeds as well as before + the :meth:`.InstanceEvents.init` event is invoked. + + """ + + def init(self, target, args, kwargs): + """Receive an instance when its constructor is called. + + This method is only called during a userland construction of + an object, in conjunction with the object's constructor, e.g. + its ``__init__`` method. It is not called when an object is + loaded from the database; see the :meth:`.InstanceEvents.load` + event in order to intercept a database load. + + The event is called before the actual ``__init__`` constructor + of the object is called. The ``kwargs`` dictionary may be + modified in-place in order to affect what is passed to + ``__init__``. + + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :param args: positional arguments passed to the ``__init__`` method. + This is passed as a tuple and is currently immutable. + :param kwargs: keyword arguments passed to the ``__init__`` method. + This structure *can* be altered in place. + + .. seealso:: + + :meth:`.InstanceEvents.init_failure` + + :meth:`.InstanceEvents.load` + + """ + + def init_failure(self, target, args, kwargs): + """Receive an instance when its constructor has been called, + and raised an exception. + + This method is only called during a userland construction of + an object, in conjunction with the object's constructor, e.g. + its ``__init__`` method. It is not called when an object is loaded + from the database. + + The event is invoked after an exception raised by the ``__init__`` + method is caught. After the event + is invoked, the original exception is re-raised outwards, so that + the construction of the object still raises an exception. The + actual exception and stack trace raised should be present in + ``sys.exc_info()``. + + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :param args: positional arguments that were passed to the ``__init__`` + method. + :param kwargs: keyword arguments that were passed to the ``__init__`` + method. + + .. seealso:: + + :meth:`.InstanceEvents.init` + + :meth:`.InstanceEvents.load` + + """ + + def load(self, target, context): + """Receive an object instance after it has been created via + ``__new__``, and after initial attribute population has + occurred. + + This typically occurs when the instance is created based on + incoming result rows, and is only called once for that + instance's lifetime. + + Note that during a result-row load, this method is called upon + the first row received for this instance. Note that some + attributes and collections may or may not be loaded or even + initialized, depending on what's present in the result rows. + + The :meth:`.InstanceEvents.load` event is also available in a + class-method decorator format called :func:`.orm.reconstructor`. + + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :param context: the :class:`.QueryContext` corresponding to the + current :class:`.Query` in progress. This argument may be + ``None`` if the load does not correspond to a :class:`.Query`, + such as during :meth:`.Session.merge`. + + .. seealso:: + + :meth:`.InstanceEvents.init` + + :meth:`.InstanceEvents.refresh` + + :meth:`.SessionEvents.loaded_as_persistent` + + :ref:`mapping_constructors` + + """ + + def refresh(self, target, context, attrs): + """Receive an object instance after one or more attributes have + been refreshed from a query. + + Contrast this to the :meth:`.InstanceEvents.load` method, which + is invoked when the object is first loaded from a query. + + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :param context: the :class:`.QueryContext` corresponding to the + current :class:`.Query` in progress. + :param attrs: sequence of attribute names which + were populated, or None if all column-mapped, non-deferred + attributes were populated. + + .. seealso:: + + :meth:`.InstanceEvents.load` + + """ + + def refresh_flush(self, target, flush_context, attrs): + """Receive an object instance after one or more attributes have + been refreshed within the persistence of the object. + + This event is the same as :meth:`.InstanceEvents.refresh` except + it is invoked within the unit of work flush process, and the values + here typically come from the process of handling an INSERT or + UPDATE, such as via the RETURNING clause or from Python-side default + values. + + .. versionadded:: 1.0.5 + + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :param flush_context: Internal :class:`.UOWTransaction` object + which handles the details of the flush. + :param attrs: sequence of attribute names which + were populated. + + """ + + def expire(self, target, attrs): + """Receive an object instance after its attributes or some subset + have been expired. + + 'keys' is a list of attribute names. If None, the entire + state was expired. + + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :param attrs: sequence of attribute + names which were expired, or None if all attributes were + expired. + + """ + + def pickle(self, target, state_dict): + """Receive an object instance when its associated state is + being pickled. + + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :param state_dict: the dictionary returned by + :class:`.InstanceState.__getstate__`, containing the state + to be pickled. + + """ + + def unpickle(self, target, state_dict): + """Receive an object instance after its associated state has + been unpickled. + + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :param state_dict: the dictionary sent to + :class:`.InstanceState.__setstate__`, containing the state + dictionary which was pickled. + + """ + + +class _EventsHold(event.RefCollection): + """Hold onto listeners against unmapped, uninstrumented classes. + + Establish _listen() for that class' mapper/instrumentation when + those objects are created for that class. + + """ + + def __init__(self, class_): + self.class_ = class_ + + @classmethod + def _clear(cls): + cls.all_holds.clear() + + class HoldEvents(object): + _dispatch_target = None + + @classmethod + def _listen(cls, event_key, raw=False, propagate=False, **kw): + target, identifier, fn = \ + event_key.dispatch_target, event_key.identifier, event_key.fn + + if target.class_ in target.all_holds: + collection = target.all_holds[target.class_] + else: + collection = target.all_holds[target.class_] = {} + + event.registry._stored_in_collection(event_key, target) + collection[event_key._key] = (event_key, raw, propagate) + + if propagate: + stack = list(target.class_.__subclasses__()) + while stack: + subclass = stack.pop(0) + stack.extend(subclass.__subclasses__()) + subject = target.resolve(subclass) + if subject is not None: + # we are already going through __subclasses__() + # so leave generic propagate flag False + event_key.with_dispatch_target(subject).\ + listen(raw=raw, propagate=False, **kw) + + def remove(self, event_key): + target, identifier, fn = \ + event_key.dispatch_target, event_key.identifier, event_key.fn + + if isinstance(target, _EventsHold): + collection = target.all_holds[target.class_] + del collection[event_key._key] + + @classmethod + def populate(cls, class_, subject): + for subclass in class_.__mro__: + if subclass in cls.all_holds: + collection = cls.all_holds[subclass] + for event_key, raw, propagate in collection.values(): + if propagate or subclass is class_: + # since we can't be sure in what order different + # classes in a hierarchy are triggered with + # populate(), we rely upon _EventsHold for all event + # assignment, instead of using the generic propagate + # flag. + event_key.with_dispatch_target(subject).\ + listen(raw=raw, propagate=False) + + +class _InstanceEventsHold(_EventsHold): + all_holds = weakref.WeakKeyDictionary() + + def resolve(self, class_): + return instrumentation.manager_of_class(class_) + + class HoldInstanceEvents(_EventsHold.HoldEvents, InstanceEvents): + pass + + dispatch = event.dispatcher(HoldInstanceEvents) + + +class MapperEvents(event.Events): + """Define events specific to mappings. + + e.g.:: + + from sqlalchemy import event + + def my_before_insert_listener(mapper, connection, target): + # execute a stored procedure upon INSERT, + # apply the value to the row to be inserted + target.calculated_value = connection.scalar( + "select my_special_function(%d)" + % target.special_number) + + # associate the listener function with SomeClass, + # to execute during the "before_insert" hook + event.listen( + SomeClass, 'before_insert', my_before_insert_listener) + + Available targets include: + + * mapped classes + * unmapped superclasses of mapped or to-be-mapped classes + (using the ``propagate=True`` flag) + * :class:`.Mapper` objects + * the :class:`.Mapper` class itself and the :func:`.mapper` + function indicate listening for all mappers. + + .. versionchanged:: 0.8.0 mapper events can be associated with + unmapped superclasses of mapped classes. + + Mapper events provide hooks into critical sections of the + mapper, including those related to object instrumentation, + object loading, and object persistence. In particular, the + persistence methods :meth:`~.MapperEvents.before_insert`, + and :meth:`~.MapperEvents.before_update` are popular + places to augment the state being persisted - however, these + methods operate with several significant restrictions. The + user is encouraged to evaluate the + :meth:`.SessionEvents.before_flush` and + :meth:`.SessionEvents.after_flush` methods as more + flexible and user-friendly hooks in which to apply + additional database state during a flush. + + When using :class:`.MapperEvents`, several modifiers are + available to the :func:`.event.listen` function. + + :param propagate=False: When True, the event listener should + be applied to all inheriting mappers and/or the mappers of + inheriting classes, as well as any + mapper which is the target of this listener. + :param raw=False: When True, the "target" argument passed + to applicable event listener functions will be the + instance's :class:`.InstanceState` management + object, rather than the mapped instance itself. + :param retval=False: when True, the user-defined event function + must have a return value, the purpose of which is either to + control subsequent event propagation, or to otherwise alter + the operation in progress by the mapper. Possible return + values are: + + * ``sqlalchemy.orm.interfaces.EXT_CONTINUE`` - continue event + processing normally. + * ``sqlalchemy.orm.interfaces.EXT_STOP`` - cancel all subsequent + event handlers in the chain. + * other values - the return value specified by specific listeners. + + """ + + _target_class_doc = "SomeClass" + _dispatch_target = mapperlib.Mapper + + @classmethod + def _new_mapper_instance(cls, class_, mapper): + _MapperEventsHold.populate(class_, mapper) + + @classmethod + @util.dependencies("sqlalchemy.orm") + def _accept_with(cls, orm, target): + if target is orm.mapper: + return mapperlib.Mapper + elif isinstance(target, type): + if issubclass(target, mapperlib.Mapper): + return target + else: + mapper = _mapper_or_none(target) + if mapper is not None: + return mapper + else: + return _MapperEventsHold(target) + else: + return target + + @classmethod + def _listen( + cls, event_key, raw=False, retval=False, propagate=False, **kw): + target, identifier, fn = \ + event_key.dispatch_target, event_key.identifier, \ + event_key._listen_fn + + if identifier in ("before_configured", "after_configured") and \ + target is not mapperlib.Mapper: + util.warn( + "'before_configured' and 'after_configured' ORM events " + "only invoke with the mapper() function or Mapper class " + "as the target.") + + if not raw or not retval: + if not raw: + meth = getattr(cls, identifier) + try: + target_index = \ + inspect_getargspec(meth)[0].index('target') - 1 + except ValueError: + target_index = None + + def wrap(*arg, **kw): + if not raw and target_index is not None: + arg = list(arg) + arg[target_index] = arg[target_index].obj() + if not retval: + fn(*arg, **kw) + return interfaces.EXT_CONTINUE + else: + return fn(*arg, **kw) + event_key = event_key.with_wrapper(wrap) + + if propagate: + for mapper in target.self_and_descendants: + event_key.with_dispatch_target(mapper).base_listen( + propagate=True, **kw) + else: + event_key.base_listen(**kw) + + @classmethod + def _clear(cls): + super(MapperEvents, cls)._clear() + _MapperEventsHold._clear() + + def instrument_class(self, mapper, class_): + r"""Receive a class when the mapper is first constructed, + before instrumentation is applied to the mapped class. + + This event is the earliest phase of mapper construction. + Most attributes of the mapper are not yet initialized. + + This listener can either be applied to the :class:`.Mapper` + class overall, or to any un-mapped class which serves as a base + for classes that will be mapped (using the ``propagate=True`` flag):: + + Base = declarative_base() + + @event.listens_for(Base, "instrument_class", propagate=True) + def on_new_class(mapper, cls_): + " ... " + + :param mapper: the :class:`.Mapper` which is the target + of this event. + :param class\_: the mapped class. + + """ + + def mapper_configured(self, mapper, class_): + r"""Called when a specific mapper has completed its own configuration + within the scope of the :func:`.configure_mappers` call. + + The :meth:`.MapperEvents.mapper_configured` event is invoked + for each mapper that is encountered when the + :func:`.orm.configure_mappers` function proceeds through the current + list of not-yet-configured mappers. + :func:`.orm.configure_mappers` is typically invoked + automatically as mappings are first used, as well as each time + new mappers have been made available and new mapper use is + detected. + + When the event is called, the mapper should be in its final + state, but **not including backrefs** that may be invoked from + other mappers; they might still be pending within the + configuration operation. Bidirectional relationships that + are instead configured via the + :paramref:`.orm.relationship.back_populates` argument + *will* be fully available, since this style of relationship does not + rely upon other possibly-not-configured mappers to know that they + exist. + + For an event that is guaranteed to have **all** mappers ready + to go including backrefs that are defined only on other + mappings, use the :meth:`.MapperEvents.after_configured` + event; this event invokes only after all known mappings have been + fully configured. + + The :meth:`.MapperEvents.mapper_configured` event, unlike + :meth:`.MapperEvents.before_configured` or + :meth:`.MapperEvents.after_configured`, + is called for each mapper/class individually, and the mapper is + passed to the event itself. It also is called exactly once for + a particular mapper. The event is therefore useful for + configurational steps that benefit from being invoked just once + on a specific mapper basis, which don't require that "backref" + configurations are necessarily ready yet. + + :param mapper: the :class:`.Mapper` which is the target + of this event. + :param class\_: the mapped class. + + .. seealso:: + + :meth:`.MapperEvents.before_configured` + + :meth:`.MapperEvents.after_configured` + + """ + # TODO: need coverage for this event + + def before_configured(self): + """Called before a series of mappers have been configured. + + The :meth:`.MapperEvents.before_configured` event is invoked + each time the :func:`.orm.configure_mappers` function is + invoked, before the function has done any of its work. + :func:`.orm.configure_mappers` is typically invoked + automatically as mappings are first used, as well as each time + new mappers have been made available and new mapper use is + detected. + + This event can **only** be applied to the :class:`.Mapper` class + or :func:`.mapper` function, and not to individual mappings or + mapped classes. It is only invoked for all mappings as a whole:: + + from sqlalchemy.orm import mapper + + @event.listens_for(mapper, "before_configured") + def go(): + # ... + + Contrast this event to :meth:`.MapperEvents.after_configured`, + which is invoked after the series of mappers has been configured, + as well as :meth:`.MapperEvents.mapper_configured`, which is invoked + on a per-mapper basis as each one is configured to the extent possible. + + Theoretically this event is called once per + application, but is actually called any time new mappers + are to be affected by a :func:`.orm.configure_mappers` + call. If new mappings are constructed after existing ones have + already been used, this event will likely be called again. To ensure + that a particular event is only called once and no further, the + ``once=True`` argument (new in 0.9.4) can be applied:: + + from sqlalchemy.orm import mapper + + @event.listens_for(mapper, "before_configured", once=True) + def go(): + # ... + + + .. versionadded:: 0.9.3 + + + .. seealso:: + + :meth:`.MapperEvents.mapper_configured` + + :meth:`.MapperEvents.after_configured` + + """ + + def after_configured(self): + """Called after a series of mappers have been configured. + + The :meth:`.MapperEvents.after_configured` event is invoked + each time the :func:`.orm.configure_mappers` function is + invoked, after the function has completed its work. + :func:`.orm.configure_mappers` is typically invoked + automatically as mappings are first used, as well as each time + new mappers have been made available and new mapper use is + detected. + + Contrast this event to the :meth:`.MapperEvents.mapper_configured` + event, which is called on a per-mapper basis while the configuration + operation proceeds; unlike that event, when this event is invoked, + all cross-configurations (e.g. backrefs) will also have been made + available for any mappers that were pending. + Also contrast to :meth:`.MapperEvents.before_configured`, + which is invoked before the series of mappers has been configured. + + This event can **only** be applied to the :class:`.Mapper` class + or :func:`.mapper` function, and not to individual mappings or + mapped classes. It is only invoked for all mappings as a whole:: + + from sqlalchemy.orm import mapper + + @event.listens_for(mapper, "after_configured") + def go(): + # ... + + Theoretically this event is called once per + application, but is actually called any time new mappers + have been affected by a :func:`.orm.configure_mappers` + call. If new mappings are constructed after existing ones have + already been used, this event will likely be called again. To ensure + that a particular event is only called once and no further, the + ``once=True`` argument (new in 0.9.4) can be applied:: + + from sqlalchemy.orm import mapper + + @event.listens_for(mapper, "after_configured", once=True) + def go(): + # ... + + .. seealso:: + + :meth:`.MapperEvents.mapper_configured` + + :meth:`.MapperEvents.before_configured` + + """ + + def before_insert(self, mapper, connection, target): + """Receive an object instance before an INSERT statement + is emitted corresponding to that instance. + + This event is used to modify local, non-object related + attributes on the instance before an INSERT occurs, as well + as to emit additional SQL statements on the given + connection. + + The event is often called for a batch of objects of the + same class before their INSERT statements are emitted at + once in a later step. In the extremely rare case that + this is not desirable, the :func:`.mapper` can be + configured with ``batch=False``, which will cause + batches of instances to be broken up into individual + (and more poorly performing) event->persist->event + steps. + + .. warning:: + + Mapper-level flush events only allow **very limited operations**, + on attributes local to the row being operated upon only, + as well as allowing any SQL to be emitted on the given + :class:`.Connection`. **Please read fully** the notes + at :ref:`session_persistence_mapper` for guidelines on using + these methods; generally, the :meth:`.SessionEvents.before_flush` + method should be preferred for general on-flush changes. + + :param mapper: the :class:`.Mapper` which is the target + of this event. + :param connection: the :class:`.Connection` being used to + emit INSERT statements for this instance. This + provides a handle into the current transaction on the + target database specific to this instance. + :param target: the mapped instance being persisted. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :return: No return value is supported by this event. + + .. seealso:: + + :ref:`session_persistence_events` + + """ + + def after_insert(self, mapper, connection, target): + """Receive an object instance after an INSERT statement + is emitted corresponding to that instance. + + This event is used to modify in-Python-only + state on the instance after an INSERT occurs, as well + as to emit additional SQL statements on the given + connection. + + The event is often called for a batch of objects of the + same class after their INSERT statements have been + emitted at once in a previous step. In the extremely + rare case that this is not desirable, the + :func:`.mapper` can be configured with ``batch=False``, + which will cause batches of instances to be broken up + into individual (and more poorly performing) + event->persist->event steps. + + .. warning:: + + Mapper-level flush events only allow **very limited operations**, + on attributes local to the row being operated upon only, + as well as allowing any SQL to be emitted on the given + :class:`.Connection`. **Please read fully** the notes + at :ref:`session_persistence_mapper` for guidelines on using + these methods; generally, the :meth:`.SessionEvents.before_flush` + method should be preferred for general on-flush changes. + + :param mapper: the :class:`.Mapper` which is the target + of this event. + :param connection: the :class:`.Connection` being used to + emit INSERT statements for this instance. This + provides a handle into the current transaction on the + target database specific to this instance. + :param target: the mapped instance being persisted. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :return: No return value is supported by this event. + + .. seealso:: + + :ref:`session_persistence_events` + + """ + + def before_update(self, mapper, connection, target): + """Receive an object instance before an UPDATE statement + is emitted corresponding to that instance. + + This event is used to modify local, non-object related + attributes on the instance before an UPDATE occurs, as well + as to emit additional SQL statements on the given + connection. + + This method is called for all instances that are + marked as "dirty", *even those which have no net changes + to their column-based attributes*. An object is marked + as dirty when any of its column-based attributes have a + "set attribute" operation called or when any of its + collections are modified. If, at update time, no + column-based attributes have any net changes, no UPDATE + statement will be issued. This means that an instance + being sent to :meth:`~.MapperEvents.before_update` is + *not* a guarantee that an UPDATE statement will be + issued, although you can affect the outcome here by + modifying attributes so that a net change in value does + exist. + + To detect if the column-based attributes on the object have net + changes, and will therefore generate an UPDATE statement, use + ``object_session(instance).is_modified(instance, + include_collections=False)``. + + The event is often called for a batch of objects of the + same class before their UPDATE statements are emitted at + once in a later step. In the extremely rare case that + this is not desirable, the :func:`.mapper` can be + configured with ``batch=False``, which will cause + batches of instances to be broken up into individual + (and more poorly performing) event->persist->event + steps. + + .. warning:: + + Mapper-level flush events only allow **very limited operations**, + on attributes local to the row being operated upon only, + as well as allowing any SQL to be emitted on the given + :class:`.Connection`. **Please read fully** the notes + at :ref:`session_persistence_mapper` for guidelines on using + these methods; generally, the :meth:`.SessionEvents.before_flush` + method should be preferred for general on-flush changes. + + :param mapper: the :class:`.Mapper` which is the target + of this event. + :param connection: the :class:`.Connection` being used to + emit UPDATE statements for this instance. This + provides a handle into the current transaction on the + target database specific to this instance. + :param target: the mapped instance being persisted. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :return: No return value is supported by this event. + + .. seealso:: + + :ref:`session_persistence_events` + + """ + + def after_update(self, mapper, connection, target): + """Receive an object instance after an UPDATE statement + is emitted corresponding to that instance. + + This event is used to modify in-Python-only + state on the instance after an UPDATE occurs, as well + as to emit additional SQL statements on the given + connection. + + This method is called for all instances that are + marked as "dirty", *even those which have no net changes + to their column-based attributes*, and for which + no UPDATE statement has proceeded. An object is marked + as dirty when any of its column-based attributes have a + "set attribute" operation called or when any of its + collections are modified. If, at update time, no + column-based attributes have any net changes, no UPDATE + statement will be issued. This means that an instance + being sent to :meth:`~.MapperEvents.after_update` is + *not* a guarantee that an UPDATE statement has been + issued. + + To detect if the column-based attributes on the object have net + changes, and therefore resulted in an UPDATE statement, use + ``object_session(instance).is_modified(instance, + include_collections=False)``. + + The event is often called for a batch of objects of the + same class after their UPDATE statements have been emitted at + once in a previous step. In the extremely rare case that + this is not desirable, the :func:`.mapper` can be + configured with ``batch=False``, which will cause + batches of instances to be broken up into individual + (and more poorly performing) event->persist->event + steps. + + .. warning:: + + Mapper-level flush events only allow **very limited operations**, + on attributes local to the row being operated upon only, + as well as allowing any SQL to be emitted on the given + :class:`.Connection`. **Please read fully** the notes + at :ref:`session_persistence_mapper` for guidelines on using + these methods; generally, the :meth:`.SessionEvents.before_flush` + method should be preferred for general on-flush changes. + + :param mapper: the :class:`.Mapper` which is the target + of this event. + :param connection: the :class:`.Connection` being used to + emit UPDATE statements for this instance. This + provides a handle into the current transaction on the + target database specific to this instance. + :param target: the mapped instance being persisted. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :return: No return value is supported by this event. + + .. seealso:: + + :ref:`session_persistence_events` + + """ + + def before_delete(self, mapper, connection, target): + """Receive an object instance before a DELETE statement + is emitted corresponding to that instance. + + This event is used to emit additional SQL statements on + the given connection as well as to perform application + specific bookkeeping related to a deletion event. + + The event is often called for a batch of objects of the + same class before their DELETE statements are emitted at + once in a later step. + + .. warning:: + + Mapper-level flush events only allow **very limited operations**, + on attributes local to the row being operated upon only, + as well as allowing any SQL to be emitted on the given + :class:`.Connection`. **Please read fully** the notes + at :ref:`session_persistence_mapper` for guidelines on using + these methods; generally, the :meth:`.SessionEvents.before_flush` + method should be preferred for general on-flush changes. + + :param mapper: the :class:`.Mapper` which is the target + of this event. + :param connection: the :class:`.Connection` being used to + emit DELETE statements for this instance. This + provides a handle into the current transaction on the + target database specific to this instance. + :param target: the mapped instance being deleted. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :return: No return value is supported by this event. + + .. seealso:: + + :ref:`session_persistence_events` + + """ + + def after_delete(self, mapper, connection, target): + """Receive an object instance after a DELETE statement + has been emitted corresponding to that instance. + + This event is used to emit additional SQL statements on + the given connection as well as to perform application + specific bookkeeping related to a deletion event. + + The event is often called for a batch of objects of the + same class after their DELETE statements have been emitted at + once in a previous step. + + .. warning:: + + Mapper-level flush events only allow **very limited operations**, + on attributes local to the row being operated upon only, + as well as allowing any SQL to be emitted on the given + :class:`.Connection`. **Please read fully** the notes + at :ref:`session_persistence_mapper` for guidelines on using + these methods; generally, the :meth:`.SessionEvents.before_flush` + method should be preferred for general on-flush changes. + + :param mapper: the :class:`.Mapper` which is the target + of this event. + :param connection: the :class:`.Connection` being used to + emit DELETE statements for this instance. This + provides a handle into the current transaction on the + target database specific to this instance. + :param target: the mapped instance being deleted. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :return: No return value is supported by this event. + + .. seealso:: + + :ref:`session_persistence_events` + + """ + + +class _MapperEventsHold(_EventsHold): + all_holds = weakref.WeakKeyDictionary() + + def resolve(self, class_): + return _mapper_or_none(class_) + + class HoldMapperEvents(_EventsHold.HoldEvents, MapperEvents): + pass + + dispatch = event.dispatcher(HoldMapperEvents) + + +class SessionEvents(event.Events): + """Define events specific to :class:`.Session` lifecycle. + + e.g.:: + + from sqlalchemy import event + from sqlalchemy.orm import sessionmaker + + def my_before_commit(session): + print "before commit!" + + Session = sessionmaker() + + event.listen(Session, "before_commit", my_before_commit) + + The :func:`~.event.listen` function will accept + :class:`.Session` objects as well as the return result + of :class:`~.sessionmaker()` and :class:`~.scoped_session()`. + + Additionally, it accepts the :class:`.Session` class which + will apply listeners to all :class:`.Session` instances + globally. + + """ + + _target_class_doc = "SomeSessionOrFactory" + + _dispatch_target = Session + + @classmethod + def _accept_with(cls, target): + if isinstance(target, scoped_session): + + target = target.session_factory + if not isinstance(target, sessionmaker) and \ + ( + not isinstance(target, type) or + not issubclass(target, Session) + ): + raise exc.ArgumentError( + "Session event listen on a scoped_session " + "requires that its creation callable " + "is associated with the Session class.") + + if isinstance(target, sessionmaker): + return target.class_ + elif isinstance(target, type): + if issubclass(target, scoped_session): + return Session + elif issubclass(target, Session): + return target + elif isinstance(target, Session): + return target + else: + return None + + def after_transaction_create(self, session, transaction): + """Execute when a new :class:`.SessionTransaction` is created. + + This event differs from :meth:`~.SessionEvents.after_begin` + in that it occurs for each :class:`.SessionTransaction` + overall, as opposed to when transactions are begun + on individual database connections. It is also invoked + for nested transactions and subtransactions, and is always + matched by a corresponding + :meth:`~.SessionEvents.after_transaction_end` event + (assuming normal operation of the :class:`.Session`). + + :param session: the target :class:`.Session`. + :param transaction: the target :class:`.SessionTransaction`. + + To detect if this is the outermost + :class:`.SessionTransaction`, as opposed to a "subtransaction" or a + SAVEPOINT, test that the :attr:`.SessionTransaction.parent` attribute + is ``None``:: + + @event.listens_for(session, "after_transaction_create") + def after_transaction_create(session, transaction): + if transaction.parent is None: + # work with top-level transaction + + To detect if the :class:`.SessionTransaction` is a SAVEPOINT, use the + :attr:`.SessionTransaction.nested` attribute:: + + @event.listens_for(session, "after_transaction_create") + def after_transaction_create(session, transaction): + if transaction.nested: + # work with SAVEPOINT transaction + + + .. seealso:: + + :class:`.SessionTransaction` + + :meth:`~.SessionEvents.after_transaction_end` + + """ + + def after_transaction_end(self, session, transaction): + """Execute when the span of a :class:`.SessionTransaction` ends. + + This event differs from :meth:`~.SessionEvents.after_commit` + in that it corresponds to all :class:`.SessionTransaction` + objects in use, including those for nested transactions + and subtransactions, and is always matched by a corresponding + :meth:`~.SessionEvents.after_transaction_create` event. + + :param session: the target :class:`.Session`. + :param transaction: the target :class:`.SessionTransaction`. + + To detect if this is the outermost + :class:`.SessionTransaction`, as opposed to a "subtransaction" or a + SAVEPOINT, test that the :attr:`.SessionTransaction.parent` attribute + is ``None``:: + + @event.listens_for(session, "after_transaction_create") + def after_transaction_end(session, transaction): + if transaction.parent is None: + # work with top-level transaction + + To detect if the :class:`.SessionTransaction` is a SAVEPOINT, use the + :attr:`.SessionTransaction.nested` attribute:: + + @event.listens_for(session, "after_transaction_create") + def after_transaction_end(session, transaction): + if transaction.nested: + # work with SAVEPOINT transaction + + + .. seealso:: + + :class:`.SessionTransaction` + + :meth:`~.SessionEvents.after_transaction_create` + + """ + + def before_commit(self, session): + """Execute before commit is called. + + .. note:: + + The :meth:`~.SessionEvents.before_commit` hook is *not* per-flush, + that is, the :class:`.Session` can emit SQL to the database + many times within the scope of a transaction. + For interception of these events, use the + :meth:`~.SessionEvents.before_flush`, + :meth:`~.SessionEvents.after_flush`, or + :meth:`~.SessionEvents.after_flush_postexec` + events. + + :param session: The target :class:`.Session`. + + .. seealso:: + + :meth:`~.SessionEvents.after_commit` + + :meth:`~.SessionEvents.after_begin` + + :meth:`~.SessionEvents.after_transaction_create` + + :meth:`~.SessionEvents.after_transaction_end` + + """ + + def after_commit(self, session): + """Execute after a commit has occurred. + + .. note:: + + The :meth:`~.SessionEvents.after_commit` hook is *not* per-flush, + that is, the :class:`.Session` can emit SQL to the database + many times within the scope of a transaction. + For interception of these events, use the + :meth:`~.SessionEvents.before_flush`, + :meth:`~.SessionEvents.after_flush`, or + :meth:`~.SessionEvents.after_flush_postexec` + events. + + .. note:: + + The :class:`.Session` is not in an active transaction + when the :meth:`~.SessionEvents.after_commit` event is invoked, + and therefore can not emit SQL. To emit SQL corresponding to + every transaction, use the :meth:`~.SessionEvents.before_commit` + event. + + :param session: The target :class:`.Session`. + + .. seealso:: + + :meth:`~.SessionEvents.before_commit` + + :meth:`~.SessionEvents.after_begin` + + :meth:`~.SessionEvents.after_transaction_create` + + :meth:`~.SessionEvents.after_transaction_end` + + """ + + def after_rollback(self, session): + """Execute after a real DBAPI rollback has occurred. + + Note that this event only fires when the *actual* rollback against + the database occurs - it does *not* fire each time the + :meth:`.Session.rollback` method is called, if the underlying + DBAPI transaction has already been rolled back. In many + cases, the :class:`.Session` will not be in + an "active" state during this event, as the current + transaction is not valid. To acquire a :class:`.Session` + which is active after the outermost rollback has proceeded, + use the :meth:`.SessionEvents.after_soft_rollback` event, checking the + :attr:`.Session.is_active` flag. + + :param session: The target :class:`.Session`. + + """ + + def after_soft_rollback(self, session, previous_transaction): + """Execute after any rollback has occurred, including "soft" + rollbacks that don't actually emit at the DBAPI level. + + This corresponds to both nested and outer rollbacks, i.e. + the innermost rollback that calls the DBAPI's + rollback() method, as well as the enclosing rollback + calls that only pop themselves from the transaction stack. + + The given :class:`.Session` can be used to invoke SQL and + :meth:`.Session.query` operations after an outermost rollback + by first checking the :attr:`.Session.is_active` flag:: + + @event.listens_for(Session, "after_soft_rollback") + def do_something(session, previous_transaction): + if session.is_active: + session.execute("select * from some_table") + + :param session: The target :class:`.Session`. + :param previous_transaction: The :class:`.SessionTransaction` + transactional marker object which was just closed. The current + :class:`.SessionTransaction` for the given :class:`.Session` is + available via the :attr:`.Session.transaction` attribute. + + .. versionadded:: 0.7.3 + + """ + + def before_flush(self, session, flush_context, instances): + """Execute before flush process has started. + + :param session: The target :class:`.Session`. + :param flush_context: Internal :class:`.UOWTransaction` object + which handles the details of the flush. + :param instances: Usually ``None``, this is the collection of + objects which can be passed to the :meth:`.Session.flush` method + (note this usage is deprecated). + + .. seealso:: + + :meth:`~.SessionEvents.after_flush` + + :meth:`~.SessionEvents.after_flush_postexec` + + :ref:`session_persistence_events` + + """ + + def after_flush(self, session, flush_context): + """Execute after flush has completed, but before commit has been + called. + + Note that the session's state is still in pre-flush, i.e. 'new', + 'dirty', and 'deleted' lists still show pre-flush state as well + as the history settings on instance attributes. + + :param session: The target :class:`.Session`. + :param flush_context: Internal :class:`.UOWTransaction` object + which handles the details of the flush. + + .. seealso:: + + :meth:`~.SessionEvents.before_flush` + + :meth:`~.SessionEvents.after_flush_postexec` + + :ref:`session_persistence_events` + + """ + + def after_flush_postexec(self, session, flush_context): + """Execute after flush has completed, and after the post-exec + state occurs. + + This will be when the 'new', 'dirty', and 'deleted' lists are in + their final state. An actual commit() may or may not have + occurred, depending on whether or not the flush started its own + transaction or participated in a larger transaction. + + :param session: The target :class:`.Session`. + :param flush_context: Internal :class:`.UOWTransaction` object + which handles the details of the flush. + + + .. seealso:: + + :meth:`~.SessionEvents.before_flush` + + :meth:`~.SessionEvents.after_flush` + + :ref:`session_persistence_events` + + """ + + def after_begin(self, session, transaction, connection): + """Execute after a transaction is begun on a connection + + :param session: The target :class:`.Session`. + :param transaction: The :class:`.SessionTransaction`. + :param connection: The :class:`~.engine.Connection` object + which will be used for SQL statements. + + .. seealso:: + + :meth:`~.SessionEvents.before_commit` + + :meth:`~.SessionEvents.after_commit` + + :meth:`~.SessionEvents.after_transaction_create` + + :meth:`~.SessionEvents.after_transaction_end` + + """ + + def before_attach(self, session, instance): + """Execute before an instance is attached to a session. + + This is called before an add, delete or merge causes + the object to be part of the session. + + .. versionadded:: 0.8. Note that :meth:`~.SessionEvents.after_attach` + now fires off after the item is part of the session. + :meth:`.before_attach` is provided for those cases where + the item should not yet be part of the session state. + + .. seealso:: + + :meth:`~.SessionEvents.after_attach` + + :ref:`session_lifecycle_events` + + """ + + def after_attach(self, session, instance): + """Execute after an instance is attached to a session. + + This is called after an add, delete or merge. + + .. note:: + + As of 0.8, this event fires off *after* the item + has been fully associated with the session, which is + different than previous releases. For event + handlers that require the object not yet + be part of session state (such as handlers which + may autoflush while the target object is not + yet complete) consider the + new :meth:`.before_attach` event. + + .. seealso:: + + :meth:`~.SessionEvents.before_attach` + + :ref:`session_lifecycle_events` + + """ + + @event._legacy_signature("0.9", + ["session", "query", "query_context", "result"], + lambda update_context: ( + update_context.session, + update_context.query, + update_context.context, + update_context.result)) + def after_bulk_update(self, update_context): + """Execute after a bulk update operation to the session. + + This is called as a result of the :meth:`.Query.update` method. + + :param update_context: an "update context" object which contains + details about the update, including these attributes: + + * ``session`` - the :class:`.Session` involved + * ``query`` -the :class:`.Query` object that this update operation + was called upon. + * ``context`` The :class:`.QueryContext` object, corresponding + to the invocation of an ORM query. + * ``result`` the :class:`.ResultProxy` returned as a result of the + bulk UPDATE operation. + + + """ + + @event._legacy_signature("0.9", + ["session", "query", "query_context", "result"], + lambda delete_context: ( + delete_context.session, + delete_context.query, + delete_context.context, + delete_context.result)) + def after_bulk_delete(self, delete_context): + """Execute after a bulk delete operation to the session. + + This is called as a result of the :meth:`.Query.delete` method. + + :param delete_context: a "delete context" object which contains + details about the update, including these attributes: + + * ``session`` - the :class:`.Session` involved + * ``query`` -the :class:`.Query` object that this update operation + was called upon. + * ``context`` The :class:`.QueryContext` object, corresponding + to the invocation of an ORM query. + * ``result`` the :class:`.ResultProxy` returned as a result of the + bulk DELETE operation. + + + """ + + def transient_to_pending(self, session, instance): + """Intercept the "transient to pending" transition for a specific object. + + This event is a specialization of the + :meth:`.SessionEvents.after_attach` event which is only invoked + for this specific transition. It is invoked typically during the + :meth:`.Session.add` call. + + :param session: target :class:`.Session` + + :param instance: the ORM-mapped instance being operated upon. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`session_lifecycle_events` + + """ + + def pending_to_transient(self, session, instance): + """Intercept the "pending to transient" transition for a specific object. + + This less common transition occurs when an pending object that has + not been flushed is evicted from the session; this can occur + when the :meth:`.Session.rollback` method rolls back the transaction, + or when the :meth:`.Session.expunge` method is used. + + :param session: target :class:`.Session` + + :param instance: the ORM-mapped instance being operated upon. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`session_lifecycle_events` + + """ + + def persistent_to_transient(self, session, instance): + """Intercept the "persistent to transient" transition for a specific object. + + This less common transition occurs when an pending object that has + has been flushed is evicted from the session; this can occur + when the :meth:`.Session.rollback` method rolls back the transaction. + + :param session: target :class:`.Session` + + :param instance: the ORM-mapped instance being operated upon. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`session_lifecycle_events` + + """ + + def pending_to_persistent(self, session, instance): + """Intercept the "pending to persistent"" transition for a specific object. + + This event is invoked within the flush process, and is + similar to scanning the :attr:`.Session.new` collection within + the :meth:`.SessionEvents.after_flush` event. However, in this + case the object has already been moved to the persistent state + when the event is called. + + :param session: target :class:`.Session` + + :param instance: the ORM-mapped instance being operated upon. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`session_lifecycle_events` + + """ + + def detached_to_persistent(self, session, instance): + """Intercept the "detached to persistent" transition for a specific object. + + This event is a specialization of the + :meth:`.SessionEvents.after_attach` event which is only invoked + for this specific transition. It is invoked typically during the + :meth:`.Session.add` call, as well as during the + :meth:`.Session.delete` call if the object was not previously + associated with the + :class:`.Session` (note that an object marked as "deleted" remains + in the "persistent" state until the flush proceeds). + + .. note:: + + If the object becomes persistent as part of a call to + :meth:`.Session.delete`, the object is **not** yet marked as + deleted when this event is called. To detect deleted objects, + check the ``deleted`` flag sent to the + :meth:`.SessionEvents.persistent_to_detached` to event after the + flush proceeds, or check the :attr:`.Session.deleted` collection + within the :meth:`.SessionEvents.before_flush` event if deleted + objects need to be intercepted before the flush. + + :param session: target :class:`.Session` + + :param instance: the ORM-mapped instance being operated upon. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`session_lifecycle_events` + + """ + + def loaded_as_persistent(self, session, instance): + """Intercept the "loaded as persistent" transition for a specific object. + + This event is invoked within the ORM loading process, and is invoked + very similarly to the :meth:`.InstanceEvents.load` event. However, + the event here is linkable to a :class:`.Session` class or instance, + rather than to a mapper or class hierarchy, and integrates + with the other session lifecycle events smoothly. The object + is guaranteed to be present in the session's identity map when + this event is called. + + + :param session: target :class:`.Session` + + :param instance: the ORM-mapped instance being operated upon. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`session_lifecycle_events` + + """ + + def persistent_to_deleted(self, session, instance): + """Intercept the "persistent to deleted" transition for a specific object. + + This event is invoked when a persistent object's identity + is deleted from the database within a flush, however the object + still remains associated with the :class:`.Session` until the + transaction completes. + + If the transaction is rolled back, the object moves again + to the persistent state, and the + :meth:`.SessionEvents.deleted_to_persistent` event is called. + If the transaction is committed, the object becomes detached, + which will emit the :meth:`.SessionEvents.deleted_to_detached` + event. + + Note that while the :meth:`.Session.delete` method is the primary + public interface to mark an object as deleted, many objects + get deleted due to cascade rules, which are not always determined + until flush time. Therefore, there's no way to catch + every object that will be deleted until the flush has proceeded. + the :meth:`.SessionEvents.persistent_to_deleted` event is therefore + invoked at the end of a flush. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`session_lifecycle_events` + + """ + + def deleted_to_persistent(self, session, instance): + """Intercept the "deleted to persistent" transition for a specific object. + + This transition occurs only when an object that's been deleted + successfully in a flush is restored due to a call to + :meth:`.Session.rollback`. The event is not called under + any other circumstances. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`session_lifecycle_events` + + """ + + def deleted_to_detached(self, session, instance): + """Intercept the "deleted to detached" transition for a specific object. + + This event is invoked when a deleted object is evicted + from the session. The typical case when this occurs is when + the transaction for a :class:`.Session` in which the object + was deleted is committed; the object moves from the deleted + state to the detached state. + + It is also invoked for objects that were deleted in a flush + when the :meth:`.Session.expunge_all` or :meth:`.Session.close` + events are called, as well as if the object is individually + expunged from its deleted state via :meth:`.Session.expunge`. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`session_lifecycle_events` + + """ + + def persistent_to_detached(self, session, instance): + """Intercept the "persistent to detached" transition for a specific object. + + This event is invoked when a persistent object is evicted + from the session. There are many conditions that cause this + to happen, including: + + * using a method such as :meth:`.Session.expunge` + or :meth:`.Session.close` + + * Calling the :meth:`.Session.rollback` method, when the object + was part of an INSERT statement for that session's transaction + + + :param session: target :class:`.Session` + + :param instance: the ORM-mapped instance being operated upon. + + :param deleted: boolean. If True, indicates this object moved + to the detached state because it was marked as deleted and flushed. + + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`session_lifecycle_events` + + """ + + +class AttributeEvents(event.Events): + """Define events for object attributes. + + These are typically defined on the class-bound descriptor for the + target class. + + e.g.:: + + from sqlalchemy import event + + def my_append_listener(target, value, initiator): + print "received append event for target: %s" % target + + event.listen(MyClass.collection, 'append', my_append_listener) + + Listeners have the option to return a possibly modified version + of the value, when the ``retval=True`` flag is passed + to :func:`~.event.listen`:: + + def validate_phone(target, value, oldvalue, initiator): + "Strip non-numeric characters from a phone number" + + return re.sub(r'\D', '', value) + + # setup listener on UserContact.phone attribute, instructing + # it to use the return value + listen(UserContact.phone, 'set', validate_phone, retval=True) + + A validation function like the above can also raise an exception + such as :exc:`ValueError` to halt the operation. + + Several modifiers are available to the :func:`~.event.listen` function. + + :param active_history=False: When True, indicates that the + "set" event would like to receive the "old" value being + replaced unconditionally, even if this requires firing off + database loads. Note that ``active_history`` can also be + set directly via :func:`.column_property` and + :func:`.relationship`. + + :param propagate=False: When True, the listener function will + be established not just for the class attribute given, but + for attributes of the same name on all current subclasses + of that class, as well as all future subclasses of that + class, using an additional listener that listens for + instrumentation events. + :param raw=False: When True, the "target" argument to the + event will be the :class:`.InstanceState` management + object, rather than the mapped instance itself. + :param retval=False: when True, the user-defined event + listening must return the "value" argument from the + function. This gives the listening function the opportunity + to change the value that is ultimately used for a "set" + or "append" event. + + """ + + _target_class_doc = "SomeClass.some_attribute" + _dispatch_target = QueryableAttribute + + @staticmethod + def _set_dispatch(cls, dispatch_cls): + dispatch = event.Events._set_dispatch(cls, dispatch_cls) + dispatch_cls._active_history = False + return dispatch + + @classmethod + def _accept_with(cls, target): + # TODO: coverage + if isinstance(target, interfaces.MapperProperty): + return getattr(target.parent.class_, target.key) + else: + return target + + @classmethod + def _listen(cls, event_key, active_history=False, + raw=False, retval=False, + propagate=False): + + target, identifier, fn = \ + event_key.dispatch_target, event_key.identifier, \ + event_key._listen_fn + + if active_history: + target.dispatch._active_history = True + + if not raw or not retval: + def wrap(target, *arg): + if not raw: + target = target.obj() + if not retval: + if arg: + value = arg[0] + else: + value = None + fn(target, *arg) + return value + else: + return fn(target, *arg) + event_key = event_key.with_wrapper(wrap) + + event_key.base_listen(propagate=propagate) + + if propagate: + manager = instrumentation.manager_of_class(target.class_) + + for mgr in manager.subclass_managers(True): + event_key.with_dispatch_target( + mgr[target.key]).base_listen(propagate=True) + + def append(self, target, value, initiator): + """Receive a collection append event. + + The append event is invoked for each element as it is appended + to the collection. This occurs for single-item appends as well + as for a "bulk replace" operation. + + :param target: the object instance receiving the event. + If the listener is registered with ``raw=True``, this will + be the :class:`.InstanceState` object. + :param value: the value being appended. If this listener + is registered with ``retval=True``, the listener + function must return this value, or a new value which + replaces it. + :param initiator: An instance of :class:`.attributes.Event` + representing the initiation of the event. May be modified + from its original value by backref handlers in order to control + chained event propagation, as well as be inspected for information + about the source of the event. + :return: if the event was registered with ``retval=True``, + the given value, or a new effective value, should be returned. + + .. seealso:: + + :meth:`.AttributeEvents.bulk_replace` + + """ + + def bulk_replace(self, target, values, initiator): + """Receive a collection 'bulk replace' event. + + This event is invoked for a sequence of values as they are incoming + to a bulk collection set operation, which can be + modified in place before the values are treated as ORM objects. + This is an "early hook" that runs before the bulk replace routine + attempts to reconcile which objects are already present in the + collection and which are being removed by the net replace operation. + + It is typical that this method be combined with use of the + :meth:`.AttributeEvents.append` event. When using both of these + events, note that a bulk replace operation will invoke + the :meth:`.AttributeEvents.append` event for all new items, + even after :meth:`.AttributeEvents.bulk_replace` has been invoked + for the collection as a whole. In order to determine if an + :meth:`.AttributeEvents.append` event is part of a bulk replace, + use the symbol :attr:`~.attributes.OP_BULK_REPLACE` to test the + incoming initiator:: + + from sqlalchemy.orm.attributes import OP_BULK_REPLACE + + @event.listens_for(SomeObject.collection, "bulk_replace") + def process_collection(target, values, initiator): + values[:] = [_make_value(value) for value in values] + + @event.listens_for(SomeObject.collection, "append", retval=True) + def process_collection(target, value, initiator): + # make sure bulk_replace didn't already do it + if initiator is None or initiator.op is not OP_BULK_REPLACE: + return _make_value(value) + else: + return value + + + + .. versionadded:: 1.2 + + :param target: the object instance receiving the event. + If the listener is registered with ``raw=True``, this will + be the :class:`.InstanceState` object. + :param value: a sequence (e.g. a list) of the values being set. The + handler can modify this list in place. + :param initiator: An instance of :class:`.attributes.Event` + representing the initiation of the event. + + """ + + def remove(self, target, value, initiator): + """Receive a collection remove event. + + :param target: the object instance receiving the event. + If the listener is registered with ``raw=True``, this will + be the :class:`.InstanceState` object. + :param value: the value being removed. + :param initiator: An instance of :class:`.attributes.Event` + representing the initiation of the event. May be modified + from its original value by backref handlers in order to control + chained event propagation. + + .. versionchanged:: 0.9.0 the ``initiator`` argument is now + passed as a :class:`.attributes.Event` object, and may be + modified by backref handlers within a chain of backref-linked + events. + + :return: No return value is defined for this event. + """ + + def set(self, target, value, oldvalue, initiator): + """Receive a scalar set event. + + :param target: the object instance receiving the event. + If the listener is registered with ``raw=True``, this will + be the :class:`.InstanceState` object. + :param value: the value being set. If this listener + is registered with ``retval=True``, the listener + function must return this value, or a new value which + replaces it. + :param oldvalue: the previous value being replaced. This + may also be the symbol ``NEVER_SET`` or ``NO_VALUE``. + If the listener is registered with ``active_history=True``, + the previous value of the attribute will be loaded from + the database if the existing value is currently unloaded + or expired. + :param initiator: An instance of :class:`.attributes.Event` + representing the initiation of the event. May be modified + from its original value by backref handlers in order to control + chained event propagation. + + .. versionchanged:: 0.9.0 the ``initiator`` argument is now + passed as a :class:`.attributes.Event` object, and may be + modified by backref handlers within a chain of backref-linked + events. + + :return: if the event was registered with ``retval=True``, + the given value, or a new effective value, should be returned. + + """ + + def init_scalar(self, target, value, dict_): + """Receive a scalar "init" event. + + This event is invoked when an uninitialized, unpersisted scalar + attribute is accessed, e.g. read:: + + + x = my_object.some_attribute + + The ORM's default behavior when this occurs for an un-initialized + attribute is to return the value ``None``; note this differs from + Python's usual behavior of raising ``AttributeError``. The + event here can be used to customize what value is actually returned, + with the assumption that the event listener would be mirroring + a default generator that is configured on the Core :class:`.Column` + object as well. + + Since a default generator on a :class:`.Column` might also produce + a changing value such as a timestamp, the + :meth:`.AttributeEvents.init_scalar` + event handler can also be used to **set** the newly returned value, so + that a Core-level default generation function effecively fires off + only once, but at the moment the attribute is accessed on the + non-persisted object. Normally, no change to the object's state + is made when an uninitialized attribute is accessed (much older + SQLAlchemy versions did in fact change the object's state). + + If a default generator on a column returned a particular constant, + a handler might be used as follows:: + + SOME_CONSTANT = 3.1415926 + + class MyClass(Base): + # ... + + some_attribute = Column(Numeric, default=SOME_CONSTANT) + + @event.listens_for( + MyClass.some_attribute, "init_scalar", + retval=True, propagate=True) + def _init_some_attribute(target, dict_, value): + dict_['some_attribute'] = SOME_CONSTANT + return SOME_CONSTANT + + Above, we initialize the attribute ``MyClass.some_attribute`` to the + value of ``SOME_CONSTANT``. The above code includes the following + features: + + * By setting the value ``SOME_CONSTANT`` in the given ``dict_``, + we indicate that this value is to be persisted to the database. + This supersedes the use of ``SOME_CONSTANT`` in the default generator + for the :class:`.Column`. The ``active_column_defaults.py`` + example given at :ref:`examples_instrumentation` illustrates using + the same approach for a changing default, e.g. a timestamp + generator. In this particular example, it is not strictly + necessary to do this since ``SOME_CONSTANT`` would be part of the + INSERT statement in either case. + + * By establishing the ``retval=True`` flag, the value we return + from the function will be returned by the attribute getter. + Without this flag, the event is assumed to be a passive observer + and the return value of our function is ignored. + + * The ``propagate=True`` flag is significant if the mapped class + includes inheriting subclasses, which would also make use of this + event listener. Without this flag, an inheriting subclass will + not use our event handler. + + In the above example, the attribute set event + :meth:`.AttributeEvents.set` as well as the related validation feature + provided by :obj:`.orm.validates` is **not** invoked when we apply our + value to the given ``dict_``. To have these events to invoke in + response to our newly generated value, apply the value to the given + object as a normal attribute set operation:: + + SOME_CONSTANT = 3.1415926 + + @event.listens_for( + MyClass.some_attribute, "init_scalar", + retval=True, propagate=True) + def _init_some_attribute(target, dict_, value): + # will also fire off attribute set events + target.some_attribute = SOME_CONSTANT + return SOME_CONSTANT + + When multiple listeners are set up, the generation of the value + is "chained" from one listener to the next by passing the value + returned by the previous listener that specifies ``retval=True`` + as the ``value`` argument of the next listener. + + .. versionadded:: 1.1 + + :param target: the object instance receiving the event. + If the listener is registered with ``raw=True``, this will + be the :class:`.InstanceState` object. + :param value: the value that is to be returned before this event + listener were invoked. This value begins as the value ``None``, + however will be the return value of the previous event handler + function if multiple listeners are present. + :param dict_: the attribute dictionary of this mapped object. + This is normally the ``__dict__`` of the object, but in all cases + represents the destination that the attribute system uses to get + at the actual value of this attribute. Placing the value in this + dictionary has the effect that the value will be used in the + INSERT statement generated by the unit of work. + + + .. seealso:: + + :ref:`examples_instrumentation` - see the + ``active_column_defaults.py`` example. + + """ + + def init_collection(self, target, collection, collection_adapter): + """Receive a 'collection init' event. + + This event is triggered for a collection-based attribute, when + the initial "empty collection" is first generated for a blank + attribute, as well as for when the collection is replaced with + a new one, such as via a set event. + + E.g., given that ``User.addresses`` is a relationship-based + collection, the event is triggered here:: + + u1 = User() + u1.addresses.append(a1) # <- new collection + + and also during replace operations:: + + u1.addresses = [a2, a3] # <- new collection + + :param target: the object instance receiving the event. + If the listener is registered with ``raw=True``, this will + be the :class:`.InstanceState` object. + :param collection: the new collection. This will always be generated + from what was specified as + :paramref:`.RelationshipProperty.collection_class`, and will always + be empty. + :param collection_adpater: the :class:`.CollectionAdapter` that will + mediate internal access to the collection. + + .. versionadded:: 1.0.0 the :meth:`.AttributeEvents.init_collection` + and :meth:`.AttributeEvents.dispose_collection` events supersede + the :class:`.collection.linker` hook. + + """ + + def dispose_collection(self, target, collection, collection_adpater): + """Receive a 'collection dispose' event. + + This event is triggered for a collection-based attribute when + a collection is replaced, that is:: + + u1.addresses.append(a1) + + u1.addresses = [a2, a3] # <- old collection is disposed + + The old collection received will contain its previous contents. + + .. versionchanged:: 1.2 The collection passed to + :meth:`.AttributeEvents.dispose_collection` will now have its + contents before the dispose intact; previously, the collection + would be empty. + + .. versionadded:: 1.0.0 the :meth:`.AttributeEvents.init_collection` + and :meth:`.AttributeEvents.dispose_collection` events supersede + the :class:`.collection.linker` hook. + + """ + + def modified(self, target, initiator): + """Receive a 'modified' event. + + This event is triggered when the :func:`.attributes.flag_modified` + function is used to trigger a modify event on an attribute without + any specific value being set. + + .. versionadded:: 1.2 + + :param target: the object instance receiving the event. + If the listener is registered with ``raw=True``, this will + be the :class:`.InstanceState` object. + + :param initiator: An instance of :class:`.attributes.Event` + representing the initiation of the event. + + """ + + +class QueryEvents(event.Events): + """Represent events within the construction of a :class:`.Query` object. + + The events here are intended to be used with an as-yet-unreleased + inspection system for :class:`.Query`. Some very basic operations + are possible now, however the inspection system is intended to allow + complex query manipulations to be automated. + + .. versionadded:: 1.0.0 + + """ + + _target_class_doc = "SomeQuery" + _dispatch_target = Query + + def before_compile(self, query): + """Receive the :class:`.Query` object before it is composed into a + core :class:`.Select` object. + + This event is intended to allow changes to the query given:: + + @event.listens_for(Query, "before_compile", retval=True) + def no_deleted(query): + for desc in query.column_descriptions: + if desc['type'] is User: + entity = desc['entity'] + query = query.filter(entity.deleted == False) + return query + + The event should normally be listened with the ``retval=True`` + parameter set, so that the modified query may be returned. + + + """ + + @classmethod + def _listen( + cls, event_key, retval=False, **kw): + fn = event_key._listen_fn + + if not retval: + def wrap(*arg, **kw): + if not retval: + query = arg[0] + fn(*arg, **kw) + return query + else: + return fn(*arg, **kw) + event_key = event_key.with_wrapper(wrap) + + event_key.base_listen(**kw) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/exc.py b/venv/Lib/site-packages/sqlalchemy/orm/exc.py new file mode 100644 index 0000000..eb4baa0 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/exc.py @@ -0,0 +1,167 @@ +# orm/exc.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""SQLAlchemy ORM exceptions.""" +from .. import exc as sa_exc, util + +NO_STATE = (AttributeError, KeyError) +"""Exception types that may be raised by instrumentation implementations.""" + + +class StaleDataError(sa_exc.SQLAlchemyError): + """An operation encountered database state that is unaccounted for. + + Conditions which cause this to happen include: + + * A flush may have attempted to update or delete rows + and an unexpected number of rows were matched during + the UPDATE or DELETE statement. Note that when + version_id_col is used, rows in UPDATE or DELETE statements + are also matched against the current known version + identifier. + + * A mapped object with version_id_col was refreshed, + and the version number coming back from the database does + not match that of the object itself. + + * A object is detached from its parent object, however + the object was previously attached to a different parent + identity which was garbage collected, and a decision + cannot be made if the new parent was really the most + recent "parent". + + .. versionadded:: 0.7.4 + + """ + +ConcurrentModificationError = StaleDataError + + +class FlushError(sa_exc.SQLAlchemyError): + """A invalid condition was detected during flush().""" + + +class UnmappedError(sa_exc.InvalidRequestError): + """Base for exceptions that involve expected mappings not present.""" + + +class ObjectDereferencedError(sa_exc.SQLAlchemyError): + """An operation cannot complete due to an object being garbage + collected. + + """ + + +class DetachedInstanceError(sa_exc.SQLAlchemyError): + """An attempt to access unloaded attributes on a + mapped instance that is detached.""" + + code = "bhk3" + + +class UnmappedInstanceError(UnmappedError): + """An mapping operation was requested for an unknown instance.""" + + @util.dependencies("sqlalchemy.orm.base") + def __init__(self, base, obj, msg=None): + if not msg: + try: + base.class_mapper(type(obj)) + name = _safe_cls_name(type(obj)) + msg = ("Class %r is mapped, but this instance lacks " + "instrumentation. This occurs when the instance " + "is created before sqlalchemy.orm.mapper(%s) " + "was called." % (name, name)) + except UnmappedClassError: + msg = _default_unmapped(type(obj)) + if isinstance(obj, type): + msg += ( + '; was a class (%s) supplied where an instance was ' + 'required?' % _safe_cls_name(obj)) + UnmappedError.__init__(self, msg) + + def __reduce__(self): + return self.__class__, (None, self.args[0]) + + +class UnmappedClassError(UnmappedError): + """An mapping operation was requested for an unknown class.""" + + def __init__(self, cls, msg=None): + if not msg: + msg = _default_unmapped(cls) + UnmappedError.__init__(self, msg) + + def __reduce__(self): + return self.__class__, (None, self.args[0]) + + +class ObjectDeletedError(sa_exc.InvalidRequestError): + """A refresh operation failed to retrieve the database + row corresponding to an object's known primary key identity. + + A refresh operation proceeds when an expired attribute is + accessed on an object, or when :meth:`.Query.get` is + used to retrieve an object which is, upon retrieval, detected + as expired. A SELECT is emitted for the target row + based on primary key; if no row is returned, this + exception is raised. + + The true meaning of this exception is simply that + no row exists for the primary key identifier associated + with a persistent object. The row may have been + deleted, or in some cases the primary key updated + to a new value, outside of the ORM's management of the target + object. + + """ + @util.dependencies("sqlalchemy.orm.base") + def __init__(self, base, state, msg=None): + if not msg: + msg = "Instance '%s' has been deleted, or its "\ + "row is otherwise not present." % base.state_str(state) + + sa_exc.InvalidRequestError.__init__(self, msg) + + def __reduce__(self): + return self.__class__, (None, self.args[0]) + + +class UnmappedColumnError(sa_exc.InvalidRequestError): + """Mapping operation was requested on an unknown column.""" + + +class NoResultFound(sa_exc.InvalidRequestError): + """A database result was required but none was found.""" + + +class MultipleResultsFound(sa_exc.InvalidRequestError): + """A single database result was required but more than one were found.""" + + +def _safe_cls_name(cls): + try: + cls_name = '.'.join((cls.__module__, cls.__name__)) + except AttributeError: + cls_name = getattr(cls, '__name__', None) + if cls_name is None: + cls_name = repr(cls) + return cls_name + + +@util.dependencies("sqlalchemy.orm.base") +def _default_unmapped(base, cls): + try: + mappers = base.manager_of_class(cls).mappers + except NO_STATE: + mappers = {} + except TypeError: + mappers = {} + name = _safe_cls_name(cls) + + if not mappers: + return "Class '%s' is not mapped" % name diff --git a/venv/Lib/site-packages/sqlalchemy/orm/identity.py b/venv/Lib/site-packages/sqlalchemy/orm/identity.py new file mode 100644 index 0000000..b03bb0a --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/identity.py @@ -0,0 +1,355 @@ +# orm/identity.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +import weakref +from . import attributes +from .. import util +from .. import exc as sa_exc +from . import util as orm_util + +class IdentityMap(object): + def __init__(self): + self._dict = {} + self._modified = set() + self._wr = weakref.ref(self) + + def keys(self): + return self._dict.keys() + + def replace(self, state): + raise NotImplementedError() + + def add(self, state): + raise NotImplementedError() + + def _add_unpresent(self, state, key): + """optional inlined form of add() which can assume item isn't present + in the map""" + self.add(state) + + def update(self, dict): + raise NotImplementedError("IdentityMap uses add() to insert data") + + def clear(self): + raise NotImplementedError("IdentityMap uses remove() to remove data") + + def _manage_incoming_state(self, state): + state._instance_dict = self._wr + + if state.modified: + self._modified.add(state) + + def _manage_removed_state(self, state): + del state._instance_dict + if state.modified: + self._modified.discard(state) + + def _dirty_states(self): + return self._modified + + def check_modified(self): + """return True if any InstanceStates present have been marked + as 'modified'. + + """ + return bool(self._modified) + + def has_key(self, key): + return key in self + + def popitem(self): + raise NotImplementedError("IdentityMap uses remove() to remove data") + + def pop(self, key, *args): + raise NotImplementedError("IdentityMap uses remove() to remove data") + + def setdefault(self, key, default=None): + raise NotImplementedError("IdentityMap uses add() to insert data") + + def __len__(self): + return len(self._dict) + + def copy(self): + raise NotImplementedError() + + def __setitem__(self, key, value): + raise NotImplementedError("IdentityMap uses add() to insert data") + + def __delitem__(self, key): + raise NotImplementedError("IdentityMap uses remove() to remove data") + + +class WeakInstanceDict(IdentityMap): + + def __getitem__(self, key): + state = self._dict[key] + o = state.obj() + if o is None: + raise KeyError(key) + return o + + def __contains__(self, key): + try: + if key in self._dict: + state = self._dict[key] + o = state.obj() + else: + return False + except KeyError: + return False + else: + return o is not None + + def contains_state(self, state): + if state.key in self._dict: + try: + return self._dict[state.key] is state + except KeyError: + return False + else: + return False + + def replace(self, state): + if state.key in self._dict: + try: + existing = self._dict[state.key] + except KeyError: + # catch gc removed the key after we just checked for it + pass + else: + if existing is not state: + self._manage_removed_state(existing) + else: + return + + self._dict[state.key] = state + self._manage_incoming_state(state) + + def add(self, state): + key = state.key + # inline of self.__contains__ + if key in self._dict: + try: + existing_state = self._dict[key] + except KeyError: + # catch gc removed the key after we just checked for it + pass + else: + if existing_state is not state: + o = existing_state.obj() + if o is not None: + raise sa_exc.InvalidRequestError( + "Can't attach instance " + "%s; another instance with key %s is already " + "present in this session." % ( + orm_util.state_str(state), state.key)) + else: + return False + self._dict[key] = state + self._manage_incoming_state(state) + return True + + def _add_unpresent(self, state, key): + # inlined form of add() called by loading.py + self._dict[key] = state + state._instance_dict = self._wr + + def get(self, key, default=None): + if key not in self._dict: + return default + try: + state = self._dict[key] + except KeyError: + # catch gc removed the key after we just checked for it + return default + else: + o = state.obj() + if o is None: + return default + return o + + def items(self): + values = self.all_states() + result = [] + for state in values: + value = state.obj() + if value is not None: + result.append((state.key, value)) + return result + + def values(self): + values = self.all_states() + result = [] + for state in values: + value = state.obj() + if value is not None: + result.append(value) + + return result + + def __iter__(self): + return iter(self.keys()) + + if util.py2k: + + def iteritems(self): + return iter(self.items()) + + def itervalues(self): + return iter(self.values()) + + def all_states(self): + if util.py2k: + return self._dict.values() + else: + return list(self._dict.values()) + + def _fast_discard(self, state): + # used by InstanceState for state being + # GC'ed, inlines _managed_removed_state + try: + st = self._dict[state.key] + except KeyError: + # catch gc removed the key after we just checked for it + pass + else: + if st is state: + self._dict.pop(state.key, None) + + def discard(self, state): + self.safe_discard(state) + + def safe_discard(self, state): + if state.key in self._dict: + try: + st = self._dict[state.key] + except KeyError: + # catch gc removed the key after we just checked for it + pass + else: + if st is state: + self._dict.pop(state.key, None) + self._manage_removed_state(state) + + def prune(self): + return 0 + + +class StrongInstanceDict(IdentityMap): + """A 'strong-referencing' version of the identity map. + + .. deprecated 1.1:: + The strong + reference identity map is legacy. See the + recipe at :ref:`session_referencing_behavior` for + an event-based approach to maintaining strong identity + references. + + + """ + + if util.py2k: + def itervalues(self): + return self._dict.itervalues() + + def iteritems(self): + return self._dict.iteritems() + + def __iter__(self): + return iter(self.dict_) + + def __getitem__(self, key): + return self._dict[key] + + def __contains__(self, key): + return key in self._dict + + def get(self, key, default=None): + return self._dict.get(key, default) + + def values(self): + return self._dict.values() + + def items(self): + return self._dict.items() + + def all_states(self): + return [attributes.instance_state(o) for o in self.values()] + + def contains_state(self, state): + return ( + state.key in self and + attributes.instance_state(self[state.key]) is state) + + def replace(self, state): + if state.key in self._dict: + existing = self._dict[state.key] + existing = attributes.instance_state(existing) + if existing is not state: + self._manage_removed_state(existing) + else: + return + + self._dict[state.key] = state.obj() + self._manage_incoming_state(state) + + def add(self, state): + if state.key in self: + if attributes.instance_state(self._dict[state.key]) is not state: + raise sa_exc.InvalidRequestError( + "Can't attach instance " + "%s; another instance with key %s is already " + "present in this session." % ( + orm_util.state_str(state), state.key)) + return False + else: + self._dict[state.key] = state.obj() + self._manage_incoming_state(state) + return True + + def _add_unpresent(self, state, key): + # inlined form of add() called by loading.py + self._dict[key] = state.obj() + state._instance_dict = self._wr + + def _fast_discard(self, state): + # used by InstanceState for state being + # GC'ed, inlines _managed_removed_state + try: + obj = self._dict[state.key] + except KeyError: + # catch gc removed the key after we just checked for it + pass + else: + if attributes.instance_state(obj) is state: + self._dict.pop(state.key, None) + + def discard(self, state): + self.safe_discard(state) + + def safe_discard(self, state): + if state.key in self._dict: + obj = self._dict[state.key] + st = attributes.instance_state(obj) + if st is state: + self._dict.pop(state.key, None) + self._manage_removed_state(state) + + def prune(self): + """prune unreferenced, non-dirty states.""" + + ref_count = len(self) + dirty = [s.obj() for s in self.all_states() if s.modified] + + # work around http://bugs.python.org/issue6149 + keepers = weakref.WeakValueDictionary() + keepers.update(self) + + self._dict.clear() + self._dict.update(keepers) + self.modified = bool(dirty) + return ref_count - len(self) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/instrumentation.py b/venv/Lib/site-packages/sqlalchemy/orm/instrumentation.py new file mode 100644 index 0000000..1b839cf --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/instrumentation.py @@ -0,0 +1,573 @@ +# orm/instrumentation.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Defines SQLAlchemy's system of class instrumentation. + +This module is usually not directly visible to user applications, but +defines a large part of the ORM's interactivity. + +instrumentation.py deals with registration of end-user classes +for state tracking. It interacts closely with state.py +and attributes.py which establish per-instance and per-class-attribute +instrumentation, respectively. + +The class instrumentation system can be customized on a per-class +or global basis using the :mod:`sqlalchemy.ext.instrumentation` +module, which provides the means to build and specify +alternate instrumentation forms. + +.. versionchanged: 0.8 + The instrumentation extension system was moved out of the + ORM and into the external :mod:`sqlalchemy.ext.instrumentation` + package. When that package is imported, it installs + itself within sqlalchemy.orm so that its more comprehensive + resolution mechanics take effect. + +""" + + +from . import exc, collections, interfaces, state +from .. import util +from . import base + + +_memoized_key_collection = util.group_expirable_memoized_property() + + +class ClassManager(dict): + """tracks state information at the class level.""" + + MANAGER_ATTR = base.DEFAULT_MANAGER_ATTR + STATE_ATTR = base.DEFAULT_STATE_ATTR + + _state_setter = staticmethod(util.attrsetter(STATE_ATTR)) + + deferred_scalar_loader = None + + original_init = object.__init__ + + factory = None + + def __init__(self, class_): + self.class_ = class_ + self.info = {} + self.new_init = None + self.local_attrs = {} + self.originals = {} + + self._bases = [mgr for mgr in [ + manager_of_class(base) + for base in self.class_.__bases__ + if isinstance(base, type) + ] if mgr is not None] + + for base in self._bases: + self.update(base) + + self.dispatch._events._new_classmanager_instance(class_, self) + # events._InstanceEventsHold.populate(class_, self) + + for basecls in class_.__mro__: + mgr = manager_of_class(basecls) + if mgr is not None: + self.dispatch._update(mgr.dispatch) + self.manage() + self._instrument_init() + + if '__del__' in class_.__dict__: + util.warn("__del__() method on class %s will " + "cause unreachable cycles and memory leaks, " + "as SQLAlchemy instrumentation often creates " + "reference cycles. Please remove this method." % + class_) + + def __hash__(self): + return id(self) + + def __eq__(self, other): + return other is self + + @property + def is_mapped(self): + return 'mapper' in self.__dict__ + + @_memoized_key_collection + def _all_key_set(self): + return frozenset(self) + + @_memoized_key_collection + def _collection_impl_keys(self): + return frozenset([ + attr.key for attr in self.values() if attr.impl.collection]) + + @_memoized_key_collection + def _scalar_loader_impls(self): + return frozenset([ + attr.impl for attr in + self.values() if attr.impl.accepts_scalar_loader]) + + @util.memoized_property + def mapper(self): + # raises unless self.mapper has been assigned + raise exc.UnmappedClassError(self.class_) + + def _locate_owning_manager(self, attribute): + """Scan through all instrumented classes in our hierarchy + searching for the given object as an attribute, and return + the bottommost owner. + + E.g.:: + + foo = foobar() + + class Parent: + attr = foo + + class Child(Parent): + pass + + Child.manager._locate_owning_manager(foo) would + give us Parent. + + Needed by association proxy to correctly figure out the + owning class when the attribute is accessed. + + """ + + stack = [None] + for supercls in self.class_.__mro__: + mgr = manager_of_class(supercls) + if not mgr: + continue + for key in set(supercls.__dict__): + val = supercls.__dict__[key] + if val is attribute: + stack.append(mgr) + continue + return stack[-1] + + def _all_sqla_attributes(self, exclude=None): + """return an iterator of all classbound attributes that are + implement :class:`.InspectionAttr`. + + This includes :class:`.QueryableAttribute` as well as extension + types such as :class:`.hybrid_property` and + :class:`.AssociationProxy`. + + """ + if exclude is None: + exclude = set() + for supercls in self.class_.__mro__: + for key in set(supercls.__dict__).difference(exclude): + exclude.add(key) + val = supercls.__dict__[key] + if isinstance(val, interfaces.InspectionAttr): + yield key, val + + def _get_class_attr_mro(self, key, default=None): + """return an attribute on the class without tripping it.""" + + for supercls in self.class_.__mro__: + if key in supercls.__dict__: + return supercls.__dict__[key] + else: + return default + + def _attr_has_impl(self, key): + """Return True if the given attribute is fully initialized. + + i.e. has an impl. + """ + + return key in self and self[key].impl is not None + + def _subclass_manager(self, cls): + """Create a new ClassManager for a subclass of this ClassManager's + class. + + This is called automatically when attributes are instrumented so that + the attributes can be propagated to subclasses against their own + class-local manager, without the need for mappers etc. to have already + pre-configured managers for the full class hierarchy. Mappers + can post-configure the auto-generated ClassManager when needed. + + """ + manager = manager_of_class(cls) + if manager is None: + manager = _instrumentation_factory.create_manager_for_cls(cls) + return manager + + def _instrument_init(self): + # TODO: self.class_.__init__ is often the already-instrumented + # __init__ from an instrumented superclass. We still need to make + # our own wrapper, but it would + # be nice to wrap the original __init__ and not our existing wrapper + # of such, since this adds method overhead. + self.original_init = self.class_.__init__ + self.new_init = _generate_init(self.class_, self) + self.install_member('__init__', self.new_init) + + def _uninstrument_init(self): + if self.new_init: + self.uninstall_member('__init__') + self.new_init = None + + @util.memoized_property + def _state_constructor(self): + self.dispatch.first_init(self, self.class_) + return state.InstanceState + + def manage(self): + """Mark this instance as the manager for its class.""" + + setattr(self.class_, self.MANAGER_ATTR, self) + + def dispose(self): + """Dissasociate this manager from its class.""" + + delattr(self.class_, self.MANAGER_ATTR) + + @util.hybridmethod + def manager_getter(self): + return _default_manager_getter + + @util.hybridmethod + def state_getter(self): + """Return a (instance) -> InstanceState callable. + + "state getter" callables should raise either KeyError or + AttributeError if no InstanceState could be found for the + instance. + """ + + return _default_state_getter + + @util.hybridmethod + def dict_getter(self): + return _default_dict_getter + + def instrument_attribute(self, key, inst, propagated=False): + if propagated: + if key in self.local_attrs: + return # don't override local attr with inherited attr + else: + self.local_attrs[key] = inst + self.install_descriptor(key, inst) + _memoized_key_collection.expire_instance(self) + self[key] = inst + + for cls in self.class_.__subclasses__(): + manager = self._subclass_manager(cls) + manager.instrument_attribute(key, inst, True) + + def subclass_managers(self, recursive): + for cls in self.class_.__subclasses__(): + mgr = manager_of_class(cls) + if mgr is not None and mgr is not self: + yield mgr + if recursive: + for m in mgr.subclass_managers(True): + yield m + + def post_configure_attribute(self, key): + _instrumentation_factory.dispatch.\ + attribute_instrument(self.class_, key, self[key]) + + def uninstrument_attribute(self, key, propagated=False): + if key not in self: + return + if propagated: + if key in self.local_attrs: + return # don't get rid of local attr + else: + del self.local_attrs[key] + self.uninstall_descriptor(key) + _memoized_key_collection.expire_instance(self) + del self[key] + for cls in self.class_.__subclasses__(): + manager = manager_of_class(cls) + if manager: + manager.uninstrument_attribute(key, True) + + def unregister(self): + """remove all instrumentation established by this ClassManager.""" + + self._uninstrument_init() + + self.mapper = self.dispatch = None + self.info.clear() + + for key in list(self): + if key in self.local_attrs: + self.uninstrument_attribute(key) + + def install_descriptor(self, key, inst): + if key in (self.STATE_ATTR, self.MANAGER_ATTR): + raise KeyError("%r: requested attribute name conflicts with " + "instrumentation attribute of the same name." % + key) + setattr(self.class_, key, inst) + + def uninstall_descriptor(self, key): + delattr(self.class_, key) + + def install_member(self, key, implementation): + if key in (self.STATE_ATTR, self.MANAGER_ATTR): + raise KeyError("%r: requested attribute name conflicts with " + "instrumentation attribute of the same name." % + key) + self.originals.setdefault(key, getattr(self.class_, key, None)) + setattr(self.class_, key, implementation) + + def uninstall_member(self, key): + original = self.originals.pop(key, None) + if original is not None: + setattr(self.class_, key, original) + + def instrument_collection_class(self, key, collection_class): + return collections.prepare_instrumentation(collection_class) + + def initialize_collection(self, key, state, factory): + user_data = factory() + adapter = collections.CollectionAdapter( + self.get_impl(key), state, user_data) + return adapter, user_data + + def is_instrumented(self, key, search=False): + if search: + return key in self + else: + return key in self.local_attrs + + def get_impl(self, key): + return self[key].impl + + @property + def attributes(self): + return iter(self.values()) + + # InstanceState management + + def new_instance(self, state=None): + instance = self.class_.__new__(self.class_) + if state is None: + state = self._state_constructor(instance, self) + self._state_setter(instance, state) + return instance + + def setup_instance(self, instance, state=None): + if state is None: + state = self._state_constructor(instance, self) + self._state_setter(instance, state) + + def teardown_instance(self, instance): + delattr(instance, self.STATE_ATTR) + + def _serialize(self, state, state_dict): + return _SerializeManager(state, state_dict) + + def _new_state_if_none(self, instance): + """Install a default InstanceState if none is present. + + A private convenience method used by the __init__ decorator. + + """ + if hasattr(instance, self.STATE_ATTR): + return False + elif self.class_ is not instance.__class__ and \ + self.is_mapped: + # this will create a new ClassManager for the + # subclass, without a mapper. This is likely a + # user error situation but allow the object + # to be constructed, so that it is usable + # in a non-ORM context at least. + return self._subclass_manager(instance.__class__).\ + _new_state_if_none(instance) + else: + state = self._state_constructor(instance, self) + self._state_setter(instance, state) + return state + + def has_state(self, instance): + return hasattr(instance, self.STATE_ATTR) + + def has_parent(self, state, key, optimistic=False): + """TODO""" + return self.get_impl(key).hasparent(state, optimistic=optimistic) + + def __bool__(self): + """All ClassManagers are non-zero regardless of attribute state.""" + return True + + __nonzero__ = __bool__ + + def __repr__(self): + return '<%s of %r at %x>' % ( + self.__class__.__name__, self.class_, id(self)) + + +class _SerializeManager(object): + """Provide serialization of a :class:`.ClassManager`. + + The :class:`.InstanceState` uses ``__init__()`` on serialize + and ``__call__()`` on deserialize. + + """ + + def __init__(self, state, d): + self.class_ = state.class_ + manager = state.manager + manager.dispatch.pickle(state, d) + + def __call__(self, state, inst, state_dict): + state.manager = manager = manager_of_class(self.class_) + if manager is None: + raise exc.UnmappedInstanceError( + inst, + "Cannot deserialize object of type %r - " + "no mapper() has " + "been configured for this class within the current " + "Python process!" % + self.class_) + elif manager.is_mapped and not manager.mapper.configured: + manager.mapper._configure_all() + + # setup _sa_instance_state ahead of time so that + # unpickle events can access the object normally. + # see [ticket:2362] + if inst is not None: + manager.setup_instance(inst, state) + manager.dispatch.unpickle(state, state_dict) + + +class InstrumentationFactory(object): + """Factory for new ClassManager instances.""" + + def create_manager_for_cls(self, class_): + assert class_ is not None + assert manager_of_class(class_) is None + + # give a more complicated subclass + # a chance to do what it wants here + manager, factory = self._locate_extended_factory(class_) + + if factory is None: + factory = ClassManager + manager = factory(class_) + + self._check_conflicts(class_, factory) + + manager.factory = factory + + self.dispatch.class_instrument(class_) + return manager + + def _locate_extended_factory(self, class_): + """Overridden by a subclass to do an extended lookup.""" + return None, None + + def _check_conflicts(self, class_, factory): + """Overridden by a subclass to test for conflicting factories.""" + return + + def unregister(self, class_): + manager = manager_of_class(class_) + manager.unregister() + manager.dispose() + self.dispatch.class_uninstrument(class_) + if ClassManager.MANAGER_ATTR in class_.__dict__: + delattr(class_, ClassManager.MANAGER_ATTR) + +# this attribute is replaced by sqlalchemy.ext.instrumentation +# when importred. +_instrumentation_factory = InstrumentationFactory() + +# these attributes are replaced by sqlalchemy.ext.instrumentation +# when a non-standard InstrumentationManager class is first +# used to instrument a class. +instance_state = _default_state_getter = base.instance_state + +instance_dict = _default_dict_getter = base.instance_dict + +manager_of_class = _default_manager_getter = base.manager_of_class + + +def register_class(class_): + """Register class instrumentation. + + Returns the existing or newly created class manager. + + """ + + manager = manager_of_class(class_) + if manager is None: + manager = _instrumentation_factory.create_manager_for_cls(class_) + return manager + + +def unregister_class(class_): + """Unregister class instrumentation.""" + + _instrumentation_factory.unregister(class_) + + +def is_instrumented(instance, key): + """Return True if the given attribute on the given instance is + instrumented by the attributes package. + + This function may be used regardless of instrumentation + applied directly to the class, i.e. no descriptors are required. + + """ + return manager_of_class(instance.__class__).\ + is_instrumented(key, search=True) + + +def _generate_init(class_, class_manager): + """Build an __init__ decorator that triggers ClassManager events.""" + + # TODO: we should use the ClassManager's notion of the + # original '__init__' method, once ClassManager is fixed + # to always reference that. + original__init__ = class_.__init__ + assert original__init__ + + # Go through some effort here and don't change the user's __init__ + # calling signature, including the unlikely case that it has + # a return value. + # FIXME: need to juggle local names to avoid constructor argument + # clashes. + func_body = """\ +def __init__(%(apply_pos)s): + new_state = class_manager._new_state_if_none(%(self_arg)s) + if new_state: + return new_state._initialize_instance(%(apply_kw)s) + else: + return original__init__(%(apply_kw)s) +""" + func_vars = util.format_argspec_init(original__init__, grouped=False) + func_text = func_body % func_vars + + if util.py2k: + func = getattr(original__init__, 'im_func', original__init__) + func_defaults = getattr(func, 'func_defaults', None) + else: + func_defaults = getattr(original__init__, '__defaults__', None) + func_kw_defaults = getattr(original__init__, '__kwdefaults__', None) + + env = locals().copy() + exec(func_text, env) + __init__ = env['__init__'] + __init__.__doc__ = original__init__.__doc__ + __init__._sa_original_init = original__init__ + + if func_defaults: + __init__.__defaults__ = func_defaults + if not util.py2k and func_kw_defaults: + __init__.__kwdefaults__ = func_kw_defaults + + return __init__ diff --git a/venv/Lib/site-packages/sqlalchemy/orm/interfaces.py b/venv/Lib/site-packages/sqlalchemy/orm/interfaces.py new file mode 100644 index 0000000..9529137 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/interfaces.py @@ -0,0 +1,710 @@ +# orm/interfaces.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" + +Contains various base classes used throughout the ORM. + +Defines some key base classes prominent within the internals, +as well as the now-deprecated ORM extension classes. + +Other than the deprecated extensions, this module and the +classes within are mostly private, though some attributes +are exposed when inspecting mappings. + +""" + +from __future__ import absolute_import + +from .. import util +from ..sql import operators +from .base import (ONETOMANY, MANYTOONE, MANYTOMANY, + EXT_CONTINUE, EXT_STOP, NOT_EXTENSION) +from .base import InspectionAttr, InspectionAttrInfo, _MappedAttribute +import collections +from .. import inspect +from . import path_registry + +# imported later +MapperExtension = SessionExtension = AttributeExtension = None + +__all__ = ( + 'AttributeExtension', + 'EXT_CONTINUE', + 'EXT_STOP', + 'ONETOMANY', + 'MANYTOMANY', + 'MANYTOONE', + 'NOT_EXTENSION', + 'LoaderStrategy', + 'MapperExtension', + 'MapperOption', + 'MapperProperty', + 'PropComparator', + 'SessionExtension', + 'StrategizedProperty', +) + + +class MapperProperty(_MappedAttribute, InspectionAttr, util.MemoizedSlots): + """Represent a particular class attribute mapped by :class:`.Mapper`. + + The most common occurrences of :class:`.MapperProperty` are the + mapped :class:`.Column`, which is represented in a mapping as + an instance of :class:`.ColumnProperty`, + and a reference to another class produced by :func:`.relationship`, + represented in the mapping as an instance of + :class:`.RelationshipProperty`. + + """ + + __slots__ = ( + '_configure_started', '_configure_finished', 'parent', 'key', + 'info' + ) + + cascade = frozenset() + """The set of 'cascade' attribute names. + + This collection is checked before the 'cascade_iterator' method is called. + + The collection typically only applies to a RelationshipProperty. + + """ + + is_property = True + """Part of the InspectionAttr interface; states this object is a + mapper property. + + """ + + def _memoized_attr_info(self): + """Info dictionary associated with the object, allowing user-defined + data to be associated with this :class:`.InspectionAttr`. + + The dictionary is generated when first accessed. Alternatively, + it can be specified as a constructor argument to the + :func:`.column_property`, :func:`.relationship`, or :func:`.composite` + functions. + + .. versionadded:: 0.8 Added support for .info to all + :class:`.MapperProperty` subclasses. + + .. versionchanged:: 1.0.0 :attr:`.MapperProperty.info` is also + available on extension types via the + :attr:`.InspectionAttrInfo.info` attribute, so that it can apply + to a wider variety of ORM and extension constructs. + + .. seealso:: + + :attr:`.QueryableAttribute.info` + + :attr:`.SchemaItem.info` + + """ + return {} + + def setup(self, context, entity, path, adapter, **kwargs): + """Called by Query for the purposes of constructing a SQL statement. + + Each MapperProperty associated with the target mapper processes the + statement referenced by the query context, adding columns and/or + criterion as appropriate. + + """ + + def create_row_processor(self, context, path, + mapper, result, adapter, populators): + """Produce row processing functions and append to the given + set of populators lists. + + """ + + def cascade_iterator(self, type_, state, visited_instances=None, + halt_on=None): + """Iterate through instances related to the given instance for + a particular 'cascade', starting with this MapperProperty. + + Return an iterator3-tuples (instance, mapper, state). + + Note that the 'cascade' collection on this MapperProperty is + checked first for the given type before cascade_iterator is called. + + This method typically only applies to RelationshipProperty. + + """ + + return iter(()) + + def set_parent(self, parent, init): + """Set the parent mapper that references this MapperProperty. + + This method is overridden by some subclasses to perform extra + setup when the mapper is first known. + + """ + self.parent = parent + + def instrument_class(self, mapper): + """Hook called by the Mapper to the property to initiate + instrumentation of the class attribute managed by this + MapperProperty. + + The MapperProperty here will typically call out to the + attributes module to set up an InstrumentedAttribute. + + This step is the first of two steps to set up an InstrumentedAttribute, + and is called early in the mapper setup process. + + The second step is typically the init_class_attribute step, + called from StrategizedProperty via the post_instrument_class() + hook. This step assigns additional state to the InstrumentedAttribute + (specifically the "impl") which has been determined after the + MapperProperty has determined what kind of persistence + management it needs to do (e.g. scalar, object, collection, etc). + + """ + + def __init__(self): + self._configure_started = False + self._configure_finished = False + + def init(self): + """Called after all mappers are created to assemble + relationships between mappers and perform other post-mapper-creation + initialization steps. + + """ + self._configure_started = True + self.do_init() + self._configure_finished = True + + @property + def class_attribute(self): + """Return the class-bound descriptor corresponding to this + :class:`.MapperProperty`. + + This is basically a ``getattr()`` call:: + + return getattr(self.parent.class_, self.key) + + I.e. if this :class:`.MapperProperty` were named ``addresses``, + and the class to which it is mapped is ``User``, this sequence + is possible:: + + >>> from sqlalchemy import inspect + >>> mapper = inspect(User) + >>> addresses_property = mapper.attrs.addresses + >>> addresses_property.class_attribute is User.addresses + True + >>> User.addresses.property is addresses_property + True + + + """ + + return getattr(self.parent.class_, self.key) + + def do_init(self): + """Perform subclass-specific initialization post-mapper-creation + steps. + + This is a template method called by the ``MapperProperty`` + object's init() method. + + """ + + def post_instrument_class(self, mapper): + """Perform instrumentation adjustments that need to occur + after init() has completed. + + The given Mapper is the Mapper invoking the operation, which + may not be the same Mapper as self.parent in an inheritance + scenario; however, Mapper will always at least be a sub-mapper of + self.parent. + + This method is typically used by StrategizedProperty, which delegates + it to LoaderStrategy.init_class_attribute() to perform final setup + on the class-bound InstrumentedAttribute. + + """ + + def merge(self, session, source_state, source_dict, dest_state, + dest_dict, load, _recursive, _resolve_conflict_map): + """Merge the attribute represented by this ``MapperProperty`` + from source to destination object. + + """ + + def __repr__(self): + return '<%s at 0x%x; %s>' % ( + self.__class__.__name__, + id(self), getattr(self, 'key', 'no key')) + + +class PropComparator(operators.ColumnOperators): + r"""Defines SQL operators for :class:`.MapperProperty` objects. + + SQLAlchemy allows for operators to + be redefined at both the Core and ORM level. :class:`.PropComparator` + is the base class of operator redefinition for ORM-level operations, + including those of :class:`.ColumnProperty`, + :class:`.RelationshipProperty`, and :class:`.CompositeProperty`. + + .. note:: With the advent of Hybrid properties introduced in SQLAlchemy + 0.7, as well as Core-level operator redefinition in + SQLAlchemy 0.8, the use case for user-defined :class:`.PropComparator` + instances is extremely rare. See :ref:`hybrids_toplevel` as well + as :ref:`types_operators`. + + User-defined subclasses of :class:`.PropComparator` may be created. The + built-in Python comparison and math operator methods, such as + :meth:`.operators.ColumnOperators.__eq__`, + :meth:`.operators.ColumnOperators.__lt__`, and + :meth:`.operators.ColumnOperators.__add__`, can be overridden to provide + new operator behavior. The custom :class:`.PropComparator` is passed to + the :class:`.MapperProperty` instance via the ``comparator_factory`` + argument. In each case, + the appropriate subclass of :class:`.PropComparator` should be used:: + + # definition of custom PropComparator subclasses + + from sqlalchemy.orm.properties import \ + ColumnProperty,\ + CompositeProperty,\ + RelationshipProperty + + class MyColumnComparator(ColumnProperty.Comparator): + def __eq__(self, other): + return self.__clause_element__() == other + + class MyRelationshipComparator(RelationshipProperty.Comparator): + def any(self, expression): + "define the 'any' operation" + # ... + + class MyCompositeComparator(CompositeProperty.Comparator): + def __gt__(self, other): + "redefine the 'greater than' operation" + + return sql.and_(*[a>b for a, b in + zip(self.__clause_element__().clauses, + other.__composite_values__())]) + + + # application of custom PropComparator subclasses + + from sqlalchemy.orm import column_property, relationship, composite + from sqlalchemy import Column, String + + class SomeMappedClass(Base): + some_column = column_property(Column("some_column", String), + comparator_factory=MyColumnComparator) + + some_relationship = relationship(SomeOtherClass, + comparator_factory=MyRelationshipComparator) + + some_composite = composite( + Column("a", String), Column("b", String), + comparator_factory=MyCompositeComparator + ) + + Note that for column-level operator redefinition, it's usually + simpler to define the operators at the Core level, using the + :attr:`.TypeEngine.comparator_factory` attribute. See + :ref:`types_operators` for more detail. + + See also: + + :class:`.ColumnProperty.Comparator` + + :class:`.RelationshipProperty.Comparator` + + :class:`.CompositeProperty.Comparator` + + :class:`.ColumnOperators` + + :ref:`types_operators` + + :attr:`.TypeEngine.comparator_factory` + + """ + + __slots__ = 'prop', 'property', '_parententity', '_adapt_to_entity' + + def __init__(self, prop, parentmapper, adapt_to_entity=None): + self.prop = self.property = prop + self._parententity = adapt_to_entity or parentmapper + self._adapt_to_entity = adapt_to_entity + + def __clause_element__(self): + raise NotImplementedError("%r" % self) + + def _query_clause_element(self): + return self.__clause_element__() + + def _bulk_update_tuples(self, value): + return [(self.__clause_element__(), value)] + + def adapt_to_entity(self, adapt_to_entity): + """Return a copy of this PropComparator which will use the given + :class:`.AliasedInsp` to produce corresponding expressions. + """ + return self.__class__(self.prop, self._parententity, adapt_to_entity) + + @property + def _parentmapper(self): + """legacy; this is renamed to _parententity to be + compatible with QueryableAttribute.""" + return inspect(self._parententity).mapper + + @property + def adapter(self): + """Produce a callable that adapts column expressions + to suit an aliased version of this comparator. + + """ + if self._adapt_to_entity is None: + return None + else: + return self._adapt_to_entity._adapt_element + + @property + def info(self): + return self.property.info + + @staticmethod + def any_op(a, b, **kwargs): + return a.any(b, **kwargs) + + @staticmethod + def has_op(a, b, **kwargs): + return a.has(b, **kwargs) + + @staticmethod + def of_type_op(a, class_): + return a.of_type(class_) + + def of_type(self, class_): + r"""Redefine this object in terms of a polymorphic subclass. + + Returns a new PropComparator from which further criterion can be + evaluated. + + e.g.:: + + query.join(Company.employees.of_type(Engineer)).\ + filter(Engineer.name=='foo') + + :param \class_: a class or mapper indicating that criterion will be + against this specific subclass. + + .. seealso:: + + :ref:`inheritance_of_type` + + """ + + return self.operate(PropComparator.of_type_op, class_) + + def any(self, criterion=None, **kwargs): + r"""Return true if this collection contains any member that meets the + given criterion. + + The usual implementation of ``any()`` is + :meth:`.RelationshipProperty.Comparator.any`. + + :param criterion: an optional ClauseElement formulated against the + member class' table or attributes. + + :param \**kwargs: key/value pairs corresponding to member class + attribute names which will be compared via equality to the + corresponding values. + + """ + + return self.operate(PropComparator.any_op, criterion, **kwargs) + + def has(self, criterion=None, **kwargs): + r"""Return true if this element references a member which meets the + given criterion. + + The usual implementation of ``has()`` is + :meth:`.RelationshipProperty.Comparator.has`. + + :param criterion: an optional ClauseElement formulated against the + member class' table or attributes. + + :param \**kwargs: key/value pairs corresponding to member class + attribute names which will be compared via equality to the + corresponding values. + + """ + + return self.operate(PropComparator.has_op, criterion, **kwargs) + + +class StrategizedProperty(MapperProperty): + """A MapperProperty which uses selectable strategies to affect + loading behavior. + + There is a single strategy selected by default. Alternate + strategies can be selected at Query time through the usage of + ``StrategizedOption`` objects via the Query.options() method. + + The mechanics of StrategizedProperty are used for every Query + invocation for every mapped attribute participating in that Query, + to determine first how the attribute will be rendered in SQL + and secondly how the attribute will retrieve a value from a result + row and apply it to a mapped object. The routines here are very + performance-critical. + + """ + + __slots__ = ( + '_strategies', 'strategy', + '_wildcard_token', '_default_path_loader_key' + ) + + strategy_wildcard_key = None + + def _memoized_attr__wildcard_token(self): + return ("%s:%s" % ( + self.strategy_wildcard_key, path_registry._WILDCARD_TOKEN), ) + + def _memoized_attr__default_path_loader_key(self): + return ( + "loader", + ("%s:%s" % ( + self.strategy_wildcard_key, path_registry._DEFAULT_TOKEN), ) + ) + + def _get_context_loader(self, context, path): + load = None + + # use EntityRegistry.__getitem__()->PropRegistry here so + # that the path is stated in terms of our base + search_path = dict.__getitem__(path, self) + + # search among: exact match, "attr.*", "default" strategy + # if any. + for path_key in ( + search_path._loader_key, + search_path._wildcard_path_loader_key, + search_path._default_path_loader_key + ): + if path_key in context.attributes: + load = context.attributes[path_key] + break + + return load + + def _get_strategy(self, key): + try: + return self._strategies[key] + except KeyError: + cls = self._strategy_lookup(*key) + self._strategies[key] = self._strategies[ + cls] = strategy = cls(self, key) + return strategy + + def setup( + self, context, entity, path, adapter, **kwargs): + loader = self._get_context_loader(context, path) + if loader and loader.strategy: + strat = self._get_strategy(loader.strategy) + else: + strat = self.strategy + strat.setup_query(context, entity, path, loader, adapter, **kwargs) + + def create_row_processor( + self, context, path, mapper, + result, adapter, populators): + loader = self._get_context_loader(context, path) + if loader and loader.strategy: + strat = self._get_strategy(loader.strategy) + else: + strat = self.strategy + strat.create_row_processor( + context, path, loader, + mapper, result, adapter, populators) + + def do_init(self): + self._strategies = {} + self.strategy = self._get_strategy(self.strategy_key) + + def post_instrument_class(self, mapper): + if not self.parent.non_primary and \ + not mapper.class_manager._attr_has_impl(self.key): + self.strategy.init_class_attribute(mapper) + + _all_strategies = collections.defaultdict(dict) + + @classmethod + def strategy_for(cls, **kw): + def decorate(dec_cls): + # ensure each subclass of the strategy has its + # own _strategy_keys collection + if '_strategy_keys' not in dec_cls.__dict__: + dec_cls._strategy_keys = [] + key = tuple(sorted(kw.items())) + cls._all_strategies[cls][key] = dec_cls + dec_cls._strategy_keys.append(key) + return dec_cls + return decorate + + @classmethod + def _strategy_lookup(cls, *key): + for prop_cls in cls.__mro__: + if prop_cls in cls._all_strategies: + strategies = cls._all_strategies[prop_cls] + try: + return strategies[key] + except KeyError: + pass + raise Exception("can't locate strategy for %s %s" % (cls, key)) + + +class MapperOption(object): + """Describe a modification to a Query.""" + + propagate_to_loaders = False + """if True, indicate this option should be carried along + to "secondary" Query objects produced during lazy loads + or refresh operations. + + """ + + def process_query(self, query): + """Apply a modification to the given :class:`.Query`.""" + + def process_query_conditionally(self, query): + """same as process_query(), except that this option may not + apply to the given query. + + This is typically used during a lazy load or scalar refresh + operation to propagate options stated in the original Query to the + new Query being used for the load. It occurs for those options that + specify propagate_to_loaders=True. + + """ + + self.process_query(query) + + def _generate_cache_key(self, path): + """Used by the "baked lazy loader" to see if this option can be cached. + + The "baked lazy loader" refers to the :class:`.Query` that is + produced during a lazy load operation for a mapped relationship. + It does not yet apply to the "lazy" load operation for deferred + or expired column attributes, however this may change in the future. + + This loader generates SQL for a query only once and attempts to cache + it; from that point on, if the SQL has been cached it will no longer + run the :meth:`.Query.options` method of the :class:`.Query`. The + :class:`.MapperOption` object that wishes to participate within a lazy + load operation therefore needs to tell the baked loader that it either + needs to forego this caching, or that it needs to include the state of + the :class:`.MapperOption` itself as part of its cache key, otherwise + SQL or other query state that has been affected by the + :class:`.MapperOption` may be cached in place of a query that does not + include these modifications, or the option may not be invoked at all. + + By default, this method returns the value ``False``, which means + the :class:`.BakedQuery` generated by the lazy loader will + not cache the SQL when this :class:`.MapperOption` is present. + This is the safest option and ensures both that the option is + invoked every time, and also that the cache isn't filled up with + an unlimited number of :class:`.Query` objects for an unlimited + number of :class:`.MapperOption` objects. + + .. versionchanged:: 1.2.8 the default return value of + :meth:`.MapperOption._generate_cache_key` is False; previously it + was ``None`` indicating "safe to cache, don't include as part of + the cache key" + + To enable caching of :class:`.Query` objects within lazy loaders, a + given :class:`.MapperOption` that returns a cache key must return a key + that uniquely identifies the complete state of this option, which will + match any other :class:`.MapperOption` that itself retains the + identical state. This includes path options, flags, etc. It should + be a state that is repeatable and part of a limited set of possible + options. + + If the :class:`.MapperOption` does not apply to the given path and + would not affect query results on such a path, it should return None, + indicating the :class:`.Query` is safe to cache for this given + loader path and that this :class:`.MapperOption` need not be + part of the cache key. + + + """ + return False + + +class LoaderStrategy(object): + """Describe the loading behavior of a StrategizedProperty object. + + The ``LoaderStrategy`` interacts with the querying process in three + ways: + + * it controls the configuration of the ``InstrumentedAttribute`` + placed on a class to handle the behavior of the attribute. this + may involve setting up class-level callable functions to fire + off a select operation when the attribute is first accessed + (i.e. a lazy load) + + * it processes the ``QueryContext`` at statement construction time, + where it can modify the SQL statement that is being produced. + For example, simple column attributes will add their represented + column to the list of selected columns, a joined eager loader + may establish join clauses to add to the statement. + + * It produces "row processor" functions at result fetching time. + These "row processor" functions populate a particular attribute + on a particular mapped instance. + + """ + + __slots__ = 'parent_property', 'is_class_level', 'parent', 'key', \ + 'strategy_key', 'strategy_opts' + + def __init__(self, parent, strategy_key): + self.parent_property = parent + self.is_class_level = False + self.parent = self.parent_property.parent + self.key = self.parent_property.key + self.strategy_key = strategy_key + self.strategy_opts = dict(strategy_key) + + def init_class_attribute(self, mapper): + pass + + def setup_query(self, context, entity, path, loadopt, adapter, **kwargs): + """Establish column and other state for a given QueryContext. + + This method fulfills the contract specified by MapperProperty.setup(). + + StrategizedProperty delegates its setup() method + directly to this method. + + """ + + def create_row_processor(self, context, path, loadopt, mapper, + result, adapter, populators): + """Establish row processing functions for a given QueryContext. + + This method fulfills the contract specified by + MapperProperty.create_row_processor(). + + StrategizedProperty delegates its create_row_processor() method + directly to this method. + + """ + + def __str__(self): + return str(self.parent_property) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/loading.py b/venv/Lib/site-packages/sqlalchemy/orm/loading.py new file mode 100644 index 0000000..0a6f802 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/loading.py @@ -0,0 +1,881 @@ +# orm/loading.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""private module containing functions used to convert database +rows into object instances and associated state. + +the functions here are called primarily by Query, Mapper, +as well as some of the attribute loading strategies. + +""" +from __future__ import absolute_import + +from .. import util +from . import attributes, exc as orm_exc +from ..sql import util as sql_util +from . import strategy_options +from . import path_registry +from .. import sql + +from .util import _none_set, state_str +from .base import _SET_DEFERRED_EXPIRED, _DEFER_FOR_STATE +from .. import exc as sa_exc +import collections + +_new_runid = util.counter() + + +def instances(query, cursor, context): + """Return an ORM result as an iterator.""" + + context.runid = _new_runid() + context.post_load_paths = {} + + filtered = query._has_mapper_entities + + single_entity = not query._only_return_tuples and \ + len(query._entities) == 1 and \ + query._entities[0].supports_single_entity + + if filtered: + if single_entity: + filter_fn = id + else: + def filter_fn(row): + return tuple( + id(item) + if ent.use_id_for_hash + else item + for ent, item in zip(query._entities, row) + ) + + try: + (process, labels) = \ + list(zip(*[ + query_entity.row_processor(query, + context, cursor) + for query_entity in query._entities + ])) + + if not single_entity: + keyed_tuple = util.lightweight_named_tuple('result', labels) + + while True: + context.partials = {} + + if query._yield_per: + fetch = cursor.fetchmany(query._yield_per) + if not fetch: + break + else: + fetch = cursor.fetchall() + + if single_entity: + proc = process[0] + rows = [proc(row) for row in fetch] + else: + rows = [keyed_tuple([proc(row) for proc in process]) + for row in fetch] + + for path, post_load in \ + context.post_load_paths.items(): + post_load.invoke(context, path) + + if filtered: + rows = util.unique_list(rows, filter_fn) + + for row in rows: + yield row + + if not query._yield_per: + break + except Exception as err: + cursor.close() + util.raise_from_cause(err) + + +@util.dependencies("sqlalchemy.orm.query") +def merge_result(querylib, query, iterator, load=True): + """Merge a result into this :class:`.Query` object's Session.""" + + session = query.session + if load: + # flush current contents if we expect to load data + session._autoflush() + + autoflush = session.autoflush + try: + session.autoflush = False + single_entity = len(query._entities) == 1 + if single_entity: + if isinstance(query._entities[0], querylib._MapperEntity): + result = [session._merge( + attributes.instance_state(instance), + attributes.instance_dict(instance), + load=load, _recursive={}, _resolve_conflict_map={}) + for instance in iterator] + else: + result = list(iterator) + else: + mapped_entities = [i for i, e in enumerate(query._entities) + if isinstance(e, querylib._MapperEntity)] + result = [] + keys = [ent._label_name for ent in query._entities] + keyed_tuple = util.lightweight_named_tuple('result', keys) + for row in iterator: + newrow = list(row) + for i in mapped_entities: + if newrow[i] is not None: + newrow[i] = session._merge( + attributes.instance_state(newrow[i]), + attributes.instance_dict(newrow[i]), + load=load, _recursive={}, _resolve_conflict_map={}) + result.append(keyed_tuple(newrow)) + + return iter(result) + finally: + session.autoflush = autoflush + + +def get_from_identity(session, key, passive): + """Look up the given key in the given session's identity map, + check the object for expired state if found. + + """ + instance = session.identity_map.get(key) + if instance is not None: + + state = attributes.instance_state(instance) + + # expired - ensure it still exists + if state.expired: + if not passive & attributes.SQL_OK: + # TODO: no coverage here + return attributes.PASSIVE_NO_RESULT + elif not passive & attributes.RELATED_OBJECT_OK: + # this mode is used within a flush and the instance's + # expired state will be checked soon enough, if necessary + return instance + try: + state._load_expired(state, passive) + except orm_exc.ObjectDeletedError: + session._remove_newly_deleted([state]) + return None + return instance + else: + return None + + +def load_on_ident(query, key, + refresh_state=None, with_for_update=None, + only_load_props=None): + """Load the given identity key from the database.""" + + if key is not None: + ident = key[1] + identity_token = key[2] + else: + ident = identity_token = None + + return load_on_pk_identity( + query, ident, refresh_state=refresh_state, + with_for_update=with_for_update, + only_load_props=only_load_props, + identity_token=identity_token + ) + + +def load_on_pk_identity(query, primary_key_identity, + refresh_state=None, with_for_update=None, + only_load_props=None, identity_token=None): + + """Load the given primary key identity from the database.""" + + if refresh_state is None: + q = query._clone() + q._get_condition() + else: + q = query._clone() + + if primary_key_identity is not None: + mapper = query._mapper_zero() + + (_get_clause, _get_params) = mapper._get_clause + + # None present in ident - turn those comparisons + # into "IS NULL" + if None in primary_key_identity: + nones = set([ + _get_params[col].key for col, value in + zip(mapper.primary_key, primary_key_identity) + if value is None + ]) + _get_clause = sql_util.adapt_criterion_to_null( + _get_clause, nones) + + _get_clause = q._adapt_clause(_get_clause, True, False) + q._criterion = _get_clause + + params = dict([ + (_get_params[primary_key].key, id_val) + for id_val, primary_key + in zip(primary_key_identity, mapper.primary_key) + ]) + + q._params = params + + # with_for_update needs to be query.LockmodeArg() + if with_for_update is not None: + version_check = True + q._for_update_arg = with_for_update + elif query._for_update_arg is not None: + version_check = True + q._for_update_arg = query._for_update_arg + else: + version_check = False + + q._get_options( + populate_existing=bool(refresh_state), + version_check=version_check, + only_load_props=only_load_props, + refresh_state=refresh_state, + identity_token=identity_token) + q._order_by = None + + try: + return q.one() + except orm_exc.NoResultFound: + return None + + +def _setup_entity_query( + context, mapper, query_entity, + path, adapter, column_collection, + with_polymorphic=None, only_load_props=None, + polymorphic_discriminator=None, **kw): + + if with_polymorphic: + poly_properties = mapper._iterate_polymorphic_properties( + with_polymorphic) + else: + poly_properties = mapper._polymorphic_properties + + quick_populators = {} + + path.set( + context.attributes, + "memoized_setups", + quick_populators) + + for value in poly_properties: + if only_load_props and \ + value.key not in only_load_props: + continue + value.setup( + context, + query_entity, + path, + adapter, + only_load_props=only_load_props, + column_collection=column_collection, + memoized_populators=quick_populators, + **kw + ) + + if polymorphic_discriminator is not None and \ + polymorphic_discriminator \ + is not mapper.polymorphic_on: + + if adapter: + pd = adapter.columns[polymorphic_discriminator] + else: + pd = polymorphic_discriminator + column_collection.append(pd) + + +def _instance_processor( + mapper, context, result, path, adapter, + only_load_props=None, refresh_state=None, + polymorphic_discriminator=None, + _polymorphic_from=None): + """Produce a mapper level row processor callable + which processes rows into mapped instances.""" + + # note that this method, most of which exists in a closure + # called _instance(), resists being broken out, as + # attempts to do so tend to add significant function + # call overhead. _instance() is the most + # performance-critical section in the whole ORM. + + pk_cols = mapper.primary_key + + if adapter: + pk_cols = [adapter.columns[c] for c in pk_cols] + + identity_class = mapper._identity_class + + populators = collections.defaultdict(list) + + props = mapper._prop_set + if only_load_props is not None: + props = props.intersection( + mapper._props[k] for k in only_load_props) + + quick_populators = path.get( + context.attributes, "memoized_setups", _none_set) + + for prop in props: + if prop in quick_populators: + # this is an inlined path just for column-based attributes. + col = quick_populators[prop] + if col is _DEFER_FOR_STATE: + populators["new"].append( + (prop.key, prop._deferred_column_loader)) + elif col is _SET_DEFERRED_EXPIRED: + # note that in this path, we are no longer + # searching in the result to see if the column might + # be present in some unexpected way. + populators["expire"].append((prop.key, False)) + else: + getter = None + # the "adapter" can be here via different paths, + # e.g. via adapter present at setup_query or adapter + # applied to the query afterwards via eager load subquery. + # If the column here + # were already a product of this adapter, sending it through + # the adapter again can return a totally new expression that + # won't be recognized in the result, and the ColumnAdapter + # currently does not accommodate for this. OTOH, if the + # column were never applied through this adapter, we may get + # None back, in which case we still won't get our "getter". + # so try both against result._getter(). See issue #4048 + if adapter: + adapted_col = adapter.columns[col] + if adapted_col is not None: + getter = result._getter(adapted_col, False) + if not getter: + getter = result._getter(col, False) + if getter: + populators["quick"].append((prop.key, getter)) + else: + # fall back to the ColumnProperty itself, which + # will iterate through all of its columns + # to see if one fits + prop.create_row_processor( + context, path, mapper, result, adapter, populators) + else: + prop.create_row_processor( + context, path, mapper, result, adapter, populators) + + propagate_options = context.propagate_options + load_path = context.query._current_path + path \ + if context.query._current_path.path else path + + session_identity_map = context.session.identity_map + + populate_existing = context.populate_existing or mapper.always_refresh + load_evt = bool(mapper.class_manager.dispatch.load) + refresh_evt = bool(mapper.class_manager.dispatch.refresh) + persistent_evt = bool(context.session.dispatch.loaded_as_persistent) + if persistent_evt: + loaded_as_persistent = context.session.dispatch.loaded_as_persistent + instance_state = attributes.instance_state + instance_dict = attributes.instance_dict + session_id = context.session.hash_key + version_check = context.version_check + runid = context.runid + identity_token = context.identity_token + + if not refresh_state and _polymorphic_from is not None: + key = ('loader', path.path) + if ( + key in context.attributes and + context.attributes[key].strategy == + (('selectinload_polymorphic', True), ) + ): + selectin_load_via = mapper._should_selectin_load( + context.attributes[key].local_opts['entities'], + _polymorphic_from) + else: + selectin_load_via = mapper._should_selectin_load( + None, _polymorphic_from) + + if selectin_load_via and selectin_load_via is not _polymorphic_from: + # only_load_props goes w/ refresh_state only, and in a refresh + # we are a single row query for the exact entity; polymorphic + # loading does not apply + assert only_load_props is None + + callable_ = _load_subclass_via_in(context, path, selectin_load_via) + + PostLoad.callable_for_path( + context, load_path, selectin_load_via.mapper, + selectin_load_via, + callable_, selectin_load_via) + + post_load = PostLoad.for_context(context, load_path, only_load_props) + + if refresh_state: + refresh_identity_key = refresh_state.key + if refresh_identity_key is None: + # super-rare condition; a refresh is being called + # on a non-instance-key instance; this is meant to only + # occur within a flush() + refresh_identity_key = \ + mapper._identity_key_from_state(refresh_state) + else: + refresh_identity_key = None + + if mapper.allow_partial_pks: + is_not_primary_key = _none_set.issuperset + else: + is_not_primary_key = _none_set.intersection + + def _instance(row): + + # determine the state that we'll be populating + if refresh_identity_key: + # fixed state that we're refreshing + state = refresh_state + instance = state.obj() + dict_ = instance_dict(instance) + isnew = state.runid != runid + currentload = True + loaded_instance = False + else: + # look at the row, see if that identity is in the + # session, or we have to create a new one + identitykey = ( + identity_class, + tuple([row[column] for column in pk_cols]), + identity_token + ) + + instance = session_identity_map.get(identitykey) + + if instance is not None: + # existing instance + state = instance_state(instance) + dict_ = instance_dict(instance) + + isnew = state.runid != runid + currentload = not isnew + loaded_instance = False + + if version_check and not currentload: + _validate_version_id(mapper, state, dict_, row, adapter) + + else: + # create a new instance + + # check for non-NULL values in the primary key columns, + # else no entity is returned for the row + if is_not_primary_key(identitykey[1]): + return None + + isnew = True + currentload = True + loaded_instance = True + + instance = mapper.class_manager.new_instance() + + dict_ = instance_dict(instance) + state = instance_state(instance) + state.key = identitykey + state.identity_token = identity_token + + # attach instance to session. + state.session_id = session_id + session_identity_map._add_unpresent(state, identitykey) + + # populate. this looks at whether this state is new + # for this load or was existing, and whether or not this + # row is the first row with this identity. + if currentload or populate_existing: + # full population routines. Objects here are either + # just created, or we are doing a populate_existing + + # be conservative about setting load_path when populate_existing + # is in effect; want to maintain options from the original + # load. see test_expire->test_refresh_maintains_deferred_options + if isnew and (propagate_options or not populate_existing): + state.load_options = propagate_options + state.load_path = load_path + + _populate_full( + context, row, state, dict_, isnew, load_path, + loaded_instance, populate_existing, populators) + + if isnew: + if loaded_instance: + if load_evt: + state.manager.dispatch.load(state, context) + if persistent_evt: + loaded_as_persistent(context.session, state.obj()) + elif refresh_evt: + state.manager.dispatch.refresh( + state, context, only_load_props) + + if populate_existing or state.modified: + if refresh_state and only_load_props: + state._commit(dict_, only_load_props) + else: + state._commit_all(dict_, session_identity_map) + + if post_load: + post_load.add_state(state, True) + + else: + # partial population routines, for objects that were already + # in the Session, but a row matches them; apply eager loaders + # on existing objects, etc. + unloaded = state.unloaded + isnew = state not in context.partials + + if not isnew or unloaded or populators["eager"]: + # state is having a partial set of its attributes + # refreshed. Populate those attributes, + # and add to the "context.partials" collection. + + to_load = _populate_partial( + context, row, state, dict_, isnew, load_path, + unloaded, populators) + + if isnew: + if refresh_evt: + state.manager.dispatch.refresh( + state, context, to_load) + + state._commit(dict_, to_load) + + if post_load and context.invoke_all_eagers: + post_load.add_state(state, False) + + return instance + + if mapper.polymorphic_map and not _polymorphic_from and not refresh_state: + # if we are doing polymorphic, dispatch to a different _instance() + # method specific to the subclass mapper + _instance = _decorate_polymorphic_switch( + _instance, context, mapper, result, path, + polymorphic_discriminator, adapter) + + return _instance + + +def _load_subclass_via_in(context, path, entity): + mapper = entity.mapper + + zero_idx = len(mapper.base_mapper.primary_key) == 1 + + if entity.is_aliased_class: + q, enable_opt, disable_opt = mapper._subclass_load_via_in(entity) + else: + q, enable_opt, disable_opt = mapper._subclass_load_via_in_mapper + + def do_load(context, path, states, load_only, effective_entity): + orig_query = context.query + + q2 = q._with_lazyload_options( + (enable_opt, ) + orig_query._with_options + (disable_opt, ), + path.parent, cache_path=path + ) + + if orig_query._populate_existing: + q2.add_criteria( + lambda q: q.populate_existing() + ) + + q2(context.session).params( + primary_keys=[ + state.key[1][0] if zero_idx else state.key[1] + for state, load_attrs in states + ] + ).all() + + return do_load + + +def _populate_full( + context, row, state, dict_, isnew, load_path, + loaded_instance, populate_existing, populators): + if isnew: + # first time we are seeing a row with this identity. + state.runid = context.runid + + for key, getter in populators["quick"]: + dict_[key] = getter(row) + if populate_existing: + for key, set_callable in populators["expire"]: + dict_.pop(key, None) + if set_callable: + state.expired_attributes.add(key) + else: + for key, set_callable in populators["expire"]: + if set_callable: + state.expired_attributes.add(key) + for key, populator in populators["new"]: + populator(state, dict_, row) + for key, populator in populators["delayed"]: + populator(state, dict_, row) + elif load_path != state.load_path: + # new load path, e.g. object is present in more than one + # column position in a series of rows + state.load_path = load_path + + # if we have data, and the data isn't in the dict, OK, let's put + # it in. + for key, getter in populators["quick"]: + if key not in dict_: + dict_[key] = getter(row) + + # otherwise treat like an "already seen" row + for key, populator in populators["existing"]: + populator(state, dict_, row) + # TODO: allow "existing" populator to know this is + # a new path for the state: + # populator(state, dict_, row, new_path=True) + + else: + # have already seen rows with this identity in this same path. + for key, populator in populators["existing"]: + populator(state, dict_, row) + + # TODO: same path + # populator(state, dict_, row, new_path=False) + + +def _populate_partial( + context, row, state, dict_, isnew, load_path, + unloaded, populators): + + if not isnew: + to_load = context.partials[state] + for key, populator in populators["existing"]: + if key in to_load: + populator(state, dict_, row) + else: + to_load = unloaded + context.partials[state] = to_load + + for key, getter in populators["quick"]: + if key in to_load: + dict_[key] = getter(row) + for key, set_callable in populators["expire"]: + if key in to_load: + dict_.pop(key, None) + if set_callable: + state.expired_attributes.add(key) + for key, populator in populators["new"]: + if key in to_load: + populator(state, dict_, row) + for key, populator in populators["delayed"]: + if key in to_load: + populator(state, dict_, row) + for key, populator in populators["eager"]: + if key not in unloaded: + populator(state, dict_, row) + + return to_load + + +def _validate_version_id(mapper, state, dict_, row, adapter): + + version_id_col = mapper.version_id_col + + if version_id_col is None: + return + + if adapter: + version_id_col = adapter.columns[version_id_col] + + if mapper._get_state_attr_by_column( + state, dict_, mapper.version_id_col) != row[version_id_col]: + raise orm_exc.StaleDataError( + "Instance '%s' has version id '%s' which " + "does not match database-loaded version id '%s'." + % (state_str(state), mapper._get_state_attr_by_column( + state, dict_, mapper.version_id_col), + row[version_id_col])) + + +def _decorate_polymorphic_switch( + instance_fn, context, mapper, result, path, + polymorphic_discriminator, adapter): + if polymorphic_discriminator is not None: + polymorphic_on = polymorphic_discriminator + else: + polymorphic_on = mapper.polymorphic_on + if polymorphic_on is None: + return instance_fn + + if adapter: + polymorphic_on = adapter.columns[polymorphic_on] + + def configure_subclass_mapper(discriminator): + try: + sub_mapper = mapper.polymorphic_map[discriminator] + except KeyError: + raise AssertionError( + "No such polymorphic_identity %r is defined" % + discriminator) + else: + if sub_mapper is mapper: + return None + + return _instance_processor( + sub_mapper, context, result, + path, adapter, _polymorphic_from=mapper) + + polymorphic_instances = util.PopulateDict( + configure_subclass_mapper + ) + + def polymorphic_instance(row): + discriminator = row[polymorphic_on] + if discriminator is not None: + _instance = polymorphic_instances[discriminator] + if _instance: + return _instance(row) + return instance_fn(row) + return polymorphic_instance + + +class PostLoad(object): + """Track loaders and states for "post load" operations. + + """ + __slots__ = 'loaders', 'states', 'load_keys' + + def __init__(self): + self.loaders = {} + self.states = util.OrderedDict() + self.load_keys = None + + def add_state(self, state, overwrite): + # the states for a polymorphic load here are all shared + # within a single PostLoad object among multiple subtypes. + # Filtering of callables on a per-subclass basis needs to be done at + # the invocation level + self.states[state] = overwrite + + def invoke(self, context, path): + if not self.states: + return + path = path_registry.PathRegistry.coerce(path) + for token, limit_to_mapper, loader, arg, kw in self.loaders.values(): + states = [ + (state, overwrite) + for state, overwrite + in self.states.items() + if state.manager.mapper.isa(limit_to_mapper) + ] + if states: + loader(context, path, states, self.load_keys, *arg, **kw) + self.states.clear() + + @classmethod + def for_context(cls, context, path, only_load_props): + pl = context.post_load_paths.get(path.path) + if pl is not None and only_load_props: + pl.load_keys = only_load_props + return pl + + @classmethod + def path_exists(self, context, path, key): + return path.path in context.post_load_paths and \ + key in context.post_load_paths[path.path].loaders + + @classmethod + def callable_for_path( + cls, context, path, limit_to_mapper, token, + loader_callable, *arg, **kw): + if path.path in context.post_load_paths: + pl = context.post_load_paths[path.path] + else: + pl = context.post_load_paths[path.path] = PostLoad() + pl.loaders[token] = (token, limit_to_mapper, loader_callable, arg, kw) + + +def load_scalar_attributes(mapper, state, attribute_names): + """initiate a column-based attribute refresh operation.""" + + # assert mapper is _state_mapper(state) + session = state.session + if not session: + raise orm_exc.DetachedInstanceError( + "Instance %s is not bound to a Session; " + "attribute refresh operation cannot proceed" % + (state_str(state))) + + has_key = bool(state.key) + + result = False + + # in the case of inheritance, particularly concrete and abstract + # concrete inheritance, the class manager might have some keys + # of attributes on the superclass that we didn't actually map. + # These could be mapped as "concrete, dont load" or could be completely + # exluded from the mapping and we know nothing about them. Filter them + # here to prevent them from coming through. + if attribute_names: + attribute_names = attribute_names.intersection(mapper.attrs.keys()) + + if mapper.inherits and not mapper.concrete: + # because we are using Core to produce a select() that we + # pass to the Query, we aren't calling setup() for mapped + # attributes; in 1.0 this means deferred attrs won't get loaded + # by default + statement = mapper._optimized_get_statement(state, attribute_names) + if statement is not None: + result = load_on_ident( + session.query(mapper). + options( + strategy_options.Load(mapper).undefer("*") + ).from_statement(statement), + None, + only_load_props=attribute_names, + refresh_state=state + ) + + if result is False: + if has_key: + identity_key = state.key + else: + # this codepath is rare - only valid when inside a flush, and the + # object is becoming persistent but hasn't yet been assigned + # an identity_key. + # check here to ensure we have the attrs we need. + pk_attrs = [mapper._columntoproperty[col].key + for col in mapper.primary_key] + if state.expired_attributes.intersection(pk_attrs): + raise sa_exc.InvalidRequestError( + "Instance %s cannot be refreshed - it's not " + " persistent and does not " + "contain a full primary key." % state_str(state)) + identity_key = mapper._identity_key_from_state(state) + + if (_none_set.issubset(identity_key) and + not mapper.allow_partial_pks) or \ + _none_set.issuperset(identity_key): + util.warn_limited( + "Instance %s to be refreshed doesn't " + "contain a full primary key - can't be refreshed " + "(and shouldn't be expired, either).", + state_str(state)) + return + + result = load_on_ident( + session.query(mapper), + identity_key, + refresh_state=state, + only_load_props=attribute_names) + + # if instance is pending, a refresh operation + # may not complete (even if PK attributes are assigned) + if has_key and result is None: + raise orm_exc.ObjectDeletedError(state) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/mapper.py b/venv/Lib/site-packages/sqlalchemy/orm/mapper.py new file mode 100644 index 0000000..7866271 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/mapper.py @@ -0,0 +1,3180 @@ +# orm/mapper.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Logic to map Python classes to and from selectables. + +Defines the :class:`~sqlalchemy.orm.mapper.Mapper` class, the central +configurational unit which associates a class with a database table. + +This is a semi-private module; the main configurational API of the ORM is +available in :class:`~sqlalchemy.orm.`. + +""" +from __future__ import absolute_import + +import types +import weakref +from itertools import chain +from collections import deque + +from .. import sql, util, log, exc as sa_exc, event, schema, inspection +from ..sql import expression, visitors, operators, util as sql_util +from . import instrumentation, attributes, exc as orm_exc, loading +from . import properties +from . import util as orm_util +from .interfaces import MapperProperty, InspectionAttr, _MappedAttribute + +from .base import _class_to_mapper, _state_mapper, class_mapper, \ + state_str, _INSTRUMENTOR +from .path_registry import PathRegistry + +import sys + + +_mapper_registry = weakref.WeakKeyDictionary() +_already_compiling = False + +_memoized_configured_property = util.group_expirable_memoized_property() + + +# a constant returned by _get_attr_by_column to indicate +# this mapper is not handling an attribute for a particular +# column +NO_ATTRIBUTE = util.symbol('NO_ATTRIBUTE') + +# lock used to synchronize the "mapper configure" step +_CONFIGURE_MUTEX = util.threading.RLock() + + +@inspection._self_inspects +@log.class_logger +class Mapper(InspectionAttr): + """Define the correlation of class attributes to database table + columns. + + The :class:`.Mapper` object is instantiated using the + :func:`~sqlalchemy.orm.mapper` function. For information + about instantiating new :class:`.Mapper` objects, see + that function's documentation. + + + When :func:`.mapper` is used + explicitly to link a user defined class with table + metadata, this is referred to as *classical mapping*. + Modern SQLAlchemy usage tends to favor the + :mod:`sqlalchemy.ext.declarative` extension for class + configuration, which + makes usage of :func:`.mapper` behind the scenes. + + Given a particular class known to be mapped by the ORM, + the :class:`.Mapper` which maintains it can be acquired + using the :func:`.inspect` function:: + + from sqlalchemy import inspect + + mapper = inspect(MyClass) + + A class which was mapped by the :mod:`sqlalchemy.ext.declarative` + extension will also have its mapper available via the ``__mapper__`` + attribute. + + + """ + + _new_mappers = False + + def __init__(self, + class_, + local_table=None, + properties=None, + primary_key=None, + non_primary=False, + inherits=None, + inherit_condition=None, + inherit_foreign_keys=None, + extension=None, + order_by=False, + always_refresh=False, + version_id_col=None, + version_id_generator=None, + polymorphic_on=None, + _polymorphic_map=None, + polymorphic_identity=None, + concrete=False, + with_polymorphic=None, + polymorphic_load=None, + allow_partial_pks=True, + batch=True, + column_prefix=None, + include_properties=None, + exclude_properties=None, + passive_updates=True, + passive_deletes=False, + confirm_deleted_rows=True, + eager_defaults=False, + legacy_is_orphan=False, + _compiled_cache_size=100, + ): + r"""Return a new :class:`~.Mapper` object. + + This function is typically used behind the scenes + via the Declarative extension. When using Declarative, + many of the usual :func:`.mapper` arguments are handled + by the Declarative extension itself, including ``class_``, + ``local_table``, ``properties``, and ``inherits``. + Other options are passed to :func:`.mapper` using + the ``__mapper_args__`` class variable:: + + class MyClass(Base): + __tablename__ = 'my_table' + id = Column(Integer, primary_key=True) + type = Column(String(50)) + alt = Column("some_alt", Integer) + + __mapper_args__ = { + 'polymorphic_on' : type + } + + + Explicit use of :func:`.mapper` + is often referred to as *classical mapping*. The above + declarative example is equivalent in classical form to:: + + my_table = Table("my_table", metadata, + Column('id', Integer, primary_key=True), + Column('type', String(50)), + Column("some_alt", Integer) + ) + + class MyClass(object): + pass + + mapper(MyClass, my_table, + polymorphic_on=my_table.c.type, + properties={ + 'alt':my_table.c.some_alt + }) + + .. seealso:: + + :ref:`classical_mapping` - discussion of direct usage of + :func:`.mapper` + + :param class\_: The class to be mapped. When using Declarative, + this argument is automatically passed as the declared class + itself. + + :param local_table: The :class:`.Table` or other selectable + to which the class is mapped. May be ``None`` if + this mapper inherits from another mapper using single-table + inheritance. When using Declarative, this argument is + automatically passed by the extension, based on what + is configured via the ``__table__`` argument or via the + :class:`.Table` produced as a result of the ``__tablename__`` + and :class:`.Column` arguments present. + + :param always_refresh: If True, all query operations for this mapped + class will overwrite all data within object instances that already + exist within the session, erasing any in-memory changes with + whatever information was loaded from the database. Usage of this + flag is highly discouraged; as an alternative, see the method + :meth:`.Query.populate_existing`. + + :param allow_partial_pks: Defaults to True. Indicates that a + composite primary key with some NULL values should be considered as + possibly existing within the database. This affects whether a + mapper will assign an incoming row to an existing identity, as well + as if :meth:`.Session.merge` will check the database first for a + particular primary key value. A "partial primary key" can occur if + one has mapped to an OUTER JOIN, for example. + + :param batch: Defaults to ``True``, indicating that save operations + of multiple entities can be batched together for efficiency. + Setting to False indicates + that an instance will be fully saved before saving the next + instance. This is used in the extremely rare case that a + :class:`.MapperEvents` listener requires being called + in between individual row persistence operations. + + :param column_prefix: A string which will be prepended + to the mapped attribute name when :class:`.Column` + objects are automatically assigned as attributes to the + mapped class. Does not affect explicitly specified + column-based properties. + + See the section :ref:`column_prefix` for an example. + + :param concrete: If True, indicates this mapper should use concrete + table inheritance with its parent mapper. + + See the section :ref:`concrete_inheritance` for an example. + + :param confirm_deleted_rows: defaults to True; when a DELETE occurs + of one more rows based on specific primary keys, a warning is + emitted when the number of rows matched does not equal the number + of rows expected. This parameter may be set to False to handle the + case where database ON DELETE CASCADE rules may be deleting some of + those rows automatically. The warning may be changed to an + exception in a future release. + + .. versionadded:: 0.9.4 - added + :paramref:`.mapper.confirm_deleted_rows` as well as conditional + matched row checking on delete. + + :param eager_defaults: if True, the ORM will immediately fetch the + value of server-generated default values after an INSERT or UPDATE, + rather than leaving them as expired to be fetched on next access. + This can be used for event schemes where the server-generated values + are needed immediately before the flush completes. By default, + this scheme will emit an individual ``SELECT`` statement per row + inserted or updated, which note can add significant performance + overhead. However, if the + target database supports :term:`RETURNING`, the default values will + be returned inline with the INSERT or UPDATE statement, which can + greatly enhance performance for an application that needs frequent + access to just-generated server defaults. + + .. seealso:: + + :ref:`orm_server_defaults` + + .. versionchanged:: 0.9.0 The ``eager_defaults`` option can now + make use of :term:`RETURNING` for backends which support it. + + :param exclude_properties: A list or set of string column names to + be excluded from mapping. + + See :ref:`include_exclude_cols` for an example. + + :param extension: A :class:`.MapperExtension` instance or + list of :class:`.MapperExtension` instances which will be applied + to all operations by this :class:`.Mapper`. **Deprecated.** + Please see :class:`.MapperEvents`. + + :param include_properties: An inclusive list or set of string column + names to map. + + See :ref:`include_exclude_cols` for an example. + + :param inherits: A mapped class or the corresponding :class:`.Mapper` + of one indicating a superclass to which this :class:`.Mapper` + should *inherit* from. The mapped class here must be a subclass + of the other mapper's class. When using Declarative, this argument + is passed automatically as a result of the natural class + hierarchy of the declared classes. + + .. seealso:: + + :ref:`inheritance_toplevel` + + :param inherit_condition: For joined table inheritance, a SQL + expression which will + define how the two tables are joined; defaults to a natural join + between the two tables. + + :param inherit_foreign_keys: When ``inherit_condition`` is used and + the columns present are missing a :class:`.ForeignKey` + configuration, this parameter can be used to specify which columns + are "foreign". In most cases can be left as ``None``. + + :param legacy_is_orphan: Boolean, defaults to ``False``. + When ``True``, specifies that "legacy" orphan consideration + is to be applied to objects mapped by this mapper, which means + that a pending (that is, not persistent) object is auto-expunged + from an owning :class:`.Session` only when it is de-associated + from *all* parents that specify a ``delete-orphan`` cascade towards + this mapper. The new default behavior is that the object is + auto-expunged when it is de-associated with *any* of its parents + that specify ``delete-orphan`` cascade. This behavior is more + consistent with that of a persistent object, and allows behavior to + be consistent in more scenarios independently of whether or not an + orphanable object has been flushed yet or not. + + See the change note and example at :ref:`legacy_is_orphan_addition` + for more detail on this change. + + .. versionadded:: 0.8 - the consideration of a pending object as + an "orphan" has been modified to more closely match the + behavior as that of persistent objects, which is that the object + is expunged from the :class:`.Session` as soon as it is + de-associated from any of its orphan-enabled parents. Previously, + the pending object would be expunged only if de-associated + from all of its orphan-enabled parents. The new flag + ``legacy_is_orphan`` is added to :func:`.orm.mapper` which + re-establishes the legacy behavior. + + :param non_primary: Specify that this :class:`.Mapper` is in addition + to the "primary" mapper, that is, the one used for persistence. + The :class:`.Mapper` created here may be used for ad-hoc + mapping of the class to an alternate selectable, for loading + only. + + :paramref:`.Mapper.non_primary` is not an often used option, but + is useful in some specific :func:`.relationship` cases. + + .. seealso:: + + :ref:`relationship_non_primary_mapper` + + :param order_by: A single :class:`.Column` or list of :class:`.Column` + objects for which selection operations should use as the default + ordering for entities. By default mappers have no pre-defined + ordering. + + .. deprecated:: 1.1 The :paramref:`.Mapper.order_by` parameter + is deprecated. Use :meth:`.Query.order_by` to determine the + ordering of a result set. + + :param passive_deletes: Indicates DELETE behavior of foreign key + columns when a joined-table inheritance entity is being deleted. + Defaults to ``False`` for a base mapper; for an inheriting mapper, + defaults to ``False`` unless the value is set to ``True`` + on the superclass mapper. + + When ``True``, it is assumed that ON DELETE CASCADE is configured + on the foreign key relationships that link this mapper's table + to its superclass table, so that when the unit of work attempts + to delete the entity, it need only emit a DELETE statement for the + superclass table, and not this table. + + When ``False``, a DELETE statement is emitted for this mapper's + table individually. If the primary key attributes local to this + table are unloaded, then a SELECT must be emitted in order to + validate these attributes; note that the primary key columns + of a joined-table subclass are not part of the "primary key" of + the object as a whole. + + Note that a value of ``True`` is **always** forced onto the + subclass mappers; that is, it's not possible for a superclass + to specify passive_deletes without this taking effect for + all subclass mappers. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`passive_deletes` - description of similar feature as + used with :func:`.relationship` + + :paramref:`.mapper.passive_updates` - supporting ON UPDATE + CASCADE for joined-table inheritance mappers + + :param passive_updates: Indicates UPDATE behavior of foreign key + columns when a primary key column changes on a joined-table + inheritance mapping. Defaults to ``True``. + + When True, it is assumed that ON UPDATE CASCADE is configured on + the foreign key in the database, and that the database will handle + propagation of an UPDATE from a source column to dependent columns + on joined-table rows. + + When False, it is assumed that the database does not enforce + referential integrity and will not be issuing its own CASCADE + operation for an update. The unit of work process will + emit an UPDATE statement for the dependent columns during a + primary key change. + + .. seealso:: + + :ref:`passive_updates` - description of a similar feature as + used with :func:`.relationship` + + :paramref:`.mapper.passive_deletes` - supporting ON DELETE + CASCADE for joined-table inheritance mappers + + :param polymorphic_load: Specifies "polymorphic loading" behavior + for a subclass in an inheritance hierarchy (joined and single + table inheritance only). Valid values are: + + * "'inline'" - specifies this class should be part of the + "with_polymorphic" mappers, e.g. its columns will be included + in a SELECT query against the base. + + * "'selectin'" - specifies that when instances of this class + are loaded, an additional SELECT will be emitted to retrieve + the columns specific to this subclass. The SELECT uses + IN to fetch multiple subclasses at once. + + .. versionadded:: 1.2 + + .. seealso:: + + :ref:`with_polymorphic_mapper_config` + + :ref:`polymorphic_selectin` + + :param polymorphic_on: Specifies the column, attribute, or + SQL expression used to determine the target class for an + incoming row, when inheriting classes are present. + + This value is commonly a :class:`.Column` object that's + present in the mapped :class:`.Table`:: + + class Employee(Base): + __tablename__ = 'employee' + + id = Column(Integer, primary_key=True) + discriminator = Column(String(50)) + + __mapper_args__ = { + "polymorphic_on":discriminator, + "polymorphic_identity":"employee" + } + + It may also be specified + as a SQL expression, as in this example where we + use the :func:`.case` construct to provide a conditional + approach:: + + class Employee(Base): + __tablename__ = 'employee' + + id = Column(Integer, primary_key=True) + discriminator = Column(String(50)) + + __mapper_args__ = { + "polymorphic_on":case([ + (discriminator == "EN", "engineer"), + (discriminator == "MA", "manager"), + ], else_="employee"), + "polymorphic_identity":"employee" + } + + It may also refer to any attribute + configured with :func:`.column_property`, or to the + string name of one:: + + class Employee(Base): + __tablename__ = 'employee' + + id = Column(Integer, primary_key=True) + discriminator = Column(String(50)) + employee_type = column_property( + case([ + (discriminator == "EN", "engineer"), + (discriminator == "MA", "manager"), + ], else_="employee") + ) + + __mapper_args__ = { + "polymorphic_on":employee_type, + "polymorphic_identity":"employee" + } + + .. versionchanged:: 0.7.4 + ``polymorphic_on`` may be specified as a SQL expression, + or refer to any attribute configured with + :func:`.column_property`, or to the string name of one. + + When setting ``polymorphic_on`` to reference an + attribute or expression that's not present in the + locally mapped :class:`.Table`, yet the value + of the discriminator should be persisted to the database, + the value of the + discriminator is not automatically set on new + instances; this must be handled by the user, + either through manual means or via event listeners. + A typical approach to establishing such a listener + looks like:: + + from sqlalchemy import event + from sqlalchemy.orm import object_mapper + + @event.listens_for(Employee, "init", propagate=True) + def set_identity(instance, *arg, **kw): + mapper = object_mapper(instance) + instance.discriminator = mapper.polymorphic_identity + + Where above, we assign the value of ``polymorphic_identity`` + for the mapped class to the ``discriminator`` attribute, + thus persisting the value to the ``discriminator`` column + in the database. + + .. warning:: + + Currently, **only one discriminator column may be set**, typically + on the base-most class in the hierarchy. "Cascading" polymorphic + columns are not yet supported. + + .. seealso:: + + :ref:`inheritance_toplevel` + + :param polymorphic_identity: Specifies the value which + identifies this particular class as returned by the + column expression referred to by the ``polymorphic_on`` + setting. As rows are received, the value corresponding + to the ``polymorphic_on`` column expression is compared + to this value, indicating which subclass should + be used for the newly reconstructed object. + + :param properties: A dictionary mapping the string names of object + attributes to :class:`.MapperProperty` instances, which define the + persistence behavior of that attribute. Note that :class:`.Column` + objects present in + the mapped :class:`.Table` are automatically placed into + ``ColumnProperty`` instances upon mapping, unless overridden. + When using Declarative, this argument is passed automatically, + based on all those :class:`.MapperProperty` instances declared + in the declared class body. + + :param primary_key: A list of :class:`.Column` objects which define + the primary key to be used against this mapper's selectable unit. + This is normally simply the primary key of the ``local_table``, but + can be overridden here. + + :param version_id_col: A :class:`.Column` + that will be used to keep a running version id of rows + in the table. This is used to detect concurrent updates or + the presence of stale data in a flush. The methodology is to + detect if an UPDATE statement does not match the last known + version id, a + :class:`~sqlalchemy.orm.exc.StaleDataError` exception is + thrown. + By default, the column must be of :class:`.Integer` type, + unless ``version_id_generator`` specifies an alternative version + generator. + + .. seealso:: + + :ref:`mapper_version_counter` - discussion of version counting + and rationale. + + :param version_id_generator: Define how new version ids should + be generated. Defaults to ``None``, which indicates that + a simple integer counting scheme be employed. To provide a custom + versioning scheme, provide a callable function of the form:: + + def generate_version(version): + return next_version + + Alternatively, server-side versioning functions such as triggers, + or programmatic versioning schemes outside of the version id + generator may be used, by specifying the value ``False``. + Please see :ref:`server_side_version_counter` for a discussion + of important points when using this option. + + .. versionadded:: 0.9.0 ``version_id_generator`` supports + server-side version number generation. + + .. seealso:: + + :ref:`custom_version_counter` + + :ref:`server_side_version_counter` + + + :param with_polymorphic: A tuple in the form ``(<classes>, + <selectable>)`` indicating the default style of "polymorphic" + loading, that is, which tables are queried at once. <classes> is + any single or list of mappers and/or classes indicating the + inherited classes that should be loaded at once. The special value + ``'*'`` may be used to indicate all descending classes should be + loaded immediately. The second tuple argument <selectable> + indicates a selectable that will be used to query for multiple + classes. + + .. seealso:: + + :ref:`with_polymorphic` - discussion of polymorphic querying + techniques. + + """ + + self.class_ = util.assert_arg_type(class_, type, 'class_') + + self.class_manager = None + + self._primary_key_argument = util.to_list(primary_key) + self.non_primary = non_primary + + if order_by is not False: + self.order_by = util.to_list(order_by) + util.warn_deprecated( + "Mapper.order_by is deprecated." + "Use Query.order_by() in order to affect the ordering of ORM " + "result sets.") + + else: + self.order_by = order_by + + self.always_refresh = always_refresh + + if isinstance(version_id_col, MapperProperty): + self.version_id_prop = version_id_col + self.version_id_col = None + else: + self.version_id_col = version_id_col + if version_id_generator is False: + self.version_id_generator = False + elif version_id_generator is None: + self.version_id_generator = lambda x: (x or 0) + 1 + else: + self.version_id_generator = version_id_generator + + self.concrete = concrete + self.single = False + self.inherits = inherits + self.local_table = local_table + self.inherit_condition = inherit_condition + self.inherit_foreign_keys = inherit_foreign_keys + self._init_properties = properties or {} + self._delete_orphans = [] + self.batch = batch + self.eager_defaults = eager_defaults + self.column_prefix = column_prefix + self.polymorphic_on = expression._clause_element_as_expr( + polymorphic_on) + self._dependency_processors = [] + self.validators = util.immutabledict() + self.passive_updates = passive_updates + self.passive_deletes = passive_deletes + self.legacy_is_orphan = legacy_is_orphan + self._clause_adapter = None + self._requires_row_aliasing = False + self._inherits_equated_pairs = None + self._memoized_values = {} + self._compiled_cache_size = _compiled_cache_size + self._reconstructor = None + self._deprecated_extensions = util.to_list(extension or []) + self.allow_partial_pks = allow_partial_pks + + if self.inherits and not self.concrete: + self.confirm_deleted_rows = False + else: + self.confirm_deleted_rows = confirm_deleted_rows + + if isinstance(self.local_table, expression.SelectBase): + raise sa_exc.InvalidRequestError( + "When mapping against a select() construct, map against " + "an alias() of the construct instead." + "This because several databases don't allow a " + "SELECT from a subquery that does not have an alias." + ) + + self._set_with_polymorphic(with_polymorphic) + self.polymorphic_load = polymorphic_load + + # our 'polymorphic identity', a string name that when located in a + # result set row indicates this Mapper should be used to construct + # the object instance for that row. + self.polymorphic_identity = polymorphic_identity + + # a dictionary of 'polymorphic identity' names, associating those + # names with Mappers that will be used to construct object instances + # upon a select operation. + if _polymorphic_map is None: + self.polymorphic_map = {} + else: + self.polymorphic_map = _polymorphic_map + + if include_properties is not None: + self.include_properties = util.to_set(include_properties) + else: + self.include_properties = None + if exclude_properties: + self.exclude_properties = util.to_set(exclude_properties) + else: + self.exclude_properties = None + + self.configured = False + + # prevent this mapper from being constructed + # while a configure_mappers() is occurring (and defer a + # configure_mappers() until construction succeeds) + _CONFIGURE_MUTEX.acquire() + try: + self.dispatch._events._new_mapper_instance(class_, self) + self._configure_inheritance() + self._configure_legacy_instrument_class() + self._configure_class_instrumentation() + self._configure_listeners() + self._configure_properties() + self._configure_polymorphic_setter() + self._configure_pks() + Mapper._new_mappers = True + self._log("constructed") + self._expire_memoizations() + finally: + _CONFIGURE_MUTEX.release() + + # major attributes initialized at the classlevel so that + # they can be Sphinx-documented. + + is_mapper = True + """Part of the inspection API.""" + + represents_outer_join = False + + @property + def mapper(self): + """Part of the inspection API. + + Returns self. + + """ + return self + + @property + def entity(self): + r"""Part of the inspection API. + + Returns self.class\_. + + """ + return self.class_ + + local_table = None + """The :class:`.Selectable` which this :class:`.Mapper` manages. + + Typically is an instance of :class:`.Table` or :class:`.Alias`. + May also be ``None``. + + The "local" table is the + selectable that the :class:`.Mapper` is directly responsible for + managing from an attribute access and flush perspective. For + non-inheriting mappers, the local table is the same as the + "mapped" table. For joined-table inheritance mappers, local_table + will be the particular sub-table of the overall "join" which + this :class:`.Mapper` represents. If this mapper is a + single-table inheriting mapper, local_table will be ``None``. + + .. seealso:: + + :attr:`~.Mapper.mapped_table`. + + """ + + mapped_table = None + """The :class:`.Selectable` to which this :class:`.Mapper` is mapped. + + Typically an instance of :class:`.Table`, :class:`.Join`, or + :class:`.Alias`. + + The "mapped" table is the selectable that + the mapper selects from during queries. For non-inheriting + mappers, the mapped table is the same as the "local" table. + For joined-table inheritance mappers, mapped_table references the + full :class:`.Join` representing full rows for this particular + subclass. For single-table inheritance mappers, mapped_table + references the base table. + + .. seealso:: + + :attr:`~.Mapper.local_table`. + + """ + + inherits = None + """References the :class:`.Mapper` which this :class:`.Mapper` + inherits from, if any. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + configured = None + """Represent ``True`` if this :class:`.Mapper` has been configured. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + .. seealso:: + + :func:`.configure_mappers`. + + """ + + concrete = None + """Represent ``True`` if this :class:`.Mapper` is a concrete + inheritance mapper. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + tables = None + """An iterable containing the collection of :class:`.Table` objects + which this :class:`.Mapper` is aware of. + + If the mapper is mapped to a :class:`.Join`, or an :class:`.Alias` + representing a :class:`.Select`, the individual :class:`.Table` + objects that comprise the full construct will be represented here. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + primary_key = None + """An iterable containing the collection of :class:`.Column` objects + which comprise the 'primary key' of the mapped table, from the + perspective of this :class:`.Mapper`. + + This list is against the selectable in :attr:`~.Mapper.mapped_table`. In + the case of inheriting mappers, some columns may be managed by a + superclass mapper. For example, in the case of a :class:`.Join`, the + primary key is determined by all of the primary key columns across all + tables referenced by the :class:`.Join`. + + The list is also not necessarily the same as the primary key column + collection associated with the underlying tables; the :class:`.Mapper` + features a ``primary_key`` argument that can override what the + :class:`.Mapper` considers as primary key columns. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + class_ = None + """The Python class which this :class:`.Mapper` maps. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + class_manager = None + """The :class:`.ClassManager` which maintains event listeners + and class-bound descriptors for this :class:`.Mapper`. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + single = None + """Represent ``True`` if this :class:`.Mapper` is a single table + inheritance mapper. + + :attr:`~.Mapper.local_table` will be ``None`` if this flag is set. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + non_primary = None + """Represent ``True`` if this :class:`.Mapper` is a "non-primary" + mapper, e.g. a mapper that is used only to selet rows but not for + persistence management. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + polymorphic_on = None + """The :class:`.Column` or SQL expression specified as the + ``polymorphic_on`` argument + for this :class:`.Mapper`, within an inheritance scenario. + + This attribute is normally a :class:`.Column` instance but + may also be an expression, such as one derived from + :func:`.cast`. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + polymorphic_map = None + """A mapping of "polymorphic identity" identifiers mapped to + :class:`.Mapper` instances, within an inheritance scenario. + + The identifiers can be of any type which is comparable to the + type of column represented by :attr:`~.Mapper.polymorphic_on`. + + An inheritance chain of mappers will all reference the same + polymorphic map object. The object is used to correlate incoming + result rows to target mappers. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + polymorphic_identity = None + """Represent an identifier which is matched against the + :attr:`~.Mapper.polymorphic_on` column during result row loading. + + Used only with inheritance, this object can be of any type which is + comparable to the type of column represented by + :attr:`~.Mapper.polymorphic_on`. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + base_mapper = None + """The base-most :class:`.Mapper` in an inheritance chain. + + In a non-inheriting scenario, this attribute will always be this + :class:`.Mapper`. In an inheritance scenario, it references + the :class:`.Mapper` which is parent to all other :class:`.Mapper` + objects in the inheritance chain. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + columns = None + """A collection of :class:`.Column` or other scalar expression + objects maintained by this :class:`.Mapper`. + + The collection behaves the same as that of the ``c`` attribute on + any :class:`.Table` object, except that only those columns included in + this mapping are present, and are keyed based on the attribute name + defined in the mapping, not necessarily the ``key`` attribute of the + :class:`.Column` itself. Additionally, scalar expressions mapped + by :func:`.column_property` are also present here. + + This is a *read only* attribute determined during mapper construction. + Behavior is undefined if directly modified. + + """ + + validators = None + """An immutable dictionary of attributes which have been decorated + using the :func:`~.orm.validates` decorator. + + The dictionary contains string attribute names as keys + mapped to the actual validation method. + + """ + + c = None + """A synonym for :attr:`~.Mapper.columns`.""" + + @util.memoized_property + def _path_registry(self): + return PathRegistry.per_mapper(self) + + def _configure_inheritance(self): + """Configure settings related to inherting and/or inherited mappers + being present.""" + + # a set of all mappers which inherit from this one. + self._inheriting_mappers = util.WeakSequence() + + if self.inherits: + if isinstance(self.inherits, type): + self.inherits = class_mapper(self.inherits, configure=False) + if not issubclass(self.class_, self.inherits.class_): + raise sa_exc.ArgumentError( + "Class '%s' does not inherit from '%s'" % + (self.class_.__name__, self.inherits.class_.__name__)) + if self.non_primary != self.inherits.non_primary: + np = not self.non_primary and "primary" or "non-primary" + raise sa_exc.ArgumentError( + "Inheritance of %s mapper for class '%s' is " + "only allowed from a %s mapper" % + (np, self.class_.__name__, np)) + # inherit_condition is optional. + if self.local_table is None: + self.local_table = self.inherits.local_table + self.mapped_table = self.inherits.mapped_table + self.single = True + elif self.local_table is not self.inherits.local_table: + if self.concrete: + self.mapped_table = self.local_table + for mapper in self.iterate_to_root(): + if mapper.polymorphic_on is not None: + mapper._requires_row_aliasing = True + else: + if self.inherit_condition is None: + # figure out inherit condition from our table to the + # immediate table of the inherited mapper, not its + # full table which could pull in other stuff we don't + # want (allows test/inheritance.InheritTest4 to pass) + self.inherit_condition = sql_util.join_condition( + self.inherits.local_table, + self.local_table) + self.mapped_table = sql.join( + self.inherits.mapped_table, + self.local_table, + self.inherit_condition) + + fks = util.to_set(self.inherit_foreign_keys) + self._inherits_equated_pairs = \ + sql_util.criterion_as_pairs( + self.mapped_table.onclause, + consider_as_foreign_keys=fks) + else: + self.mapped_table = self.local_table + + if self.polymorphic_identity is not None and not self.concrete: + self._identity_class = self.inherits._identity_class + else: + self._identity_class = self.class_ + + if self.version_id_col is None: + self.version_id_col = self.inherits.version_id_col + self.version_id_generator = self.inherits.version_id_generator + elif self.inherits.version_id_col is not None and \ + self.version_id_col is not self.inherits.version_id_col: + util.warn( + "Inheriting version_id_col '%s' does not match inherited " + "version_id_col '%s' and will not automatically populate " + "the inherited versioning column. " + "version_id_col should only be specified on " + "the base-most mapper that includes versioning." % + (self.version_id_col.description, + self.inherits.version_id_col.description) + ) + + if self.order_by is False and \ + not self.concrete and \ + self.inherits.order_by is not False: + self.order_by = self.inherits.order_by + + self.polymorphic_map = self.inherits.polymorphic_map + self.batch = self.inherits.batch + self.inherits._inheriting_mappers.append(self) + self.base_mapper = self.inherits.base_mapper + self.passive_updates = self.inherits.passive_updates + self.passive_deletes = self.inherits.passive_deletes or \ + self.passive_deletes + self._all_tables = self.inherits._all_tables + + if self.polymorphic_identity is not None: + if self.polymorphic_identity in self.polymorphic_map: + util.warn( + "Reassigning polymorphic association for identity %r " + "from %r to %r: Check for duplicate use of %r as " + "value for polymorphic_identity." % + (self.polymorphic_identity, + self.polymorphic_map[self.polymorphic_identity], + self, self.polymorphic_identity) + ) + self.polymorphic_map[self.polymorphic_identity] = self + + if self.polymorphic_load and self.concrete: + raise exc.ArgumentError( + "polymorphic_load is not currently supported " + "with concrete table inheritance") + if self.polymorphic_load == 'inline': + self.inherits._add_with_polymorphic_subclass(self) + elif self.polymorphic_load == 'selectin': + pass + elif self.polymorphic_load is not None: + raise sa_exc.ArgumentError( + "unknown argument for polymorphic_load: %r" % + self.polymorphic_load) + + else: + self._all_tables = set() + self.base_mapper = self + self.mapped_table = self.local_table + if self.polymorphic_identity is not None: + self.polymorphic_map[self.polymorphic_identity] = self + self._identity_class = self.class_ + + if self.mapped_table is None: + raise sa_exc.ArgumentError( + "Mapper '%s' does not have a mapped_table specified." + % self) + + def _set_with_polymorphic(self, with_polymorphic): + if with_polymorphic == '*': + self.with_polymorphic = ('*', None) + elif isinstance(with_polymorphic, (tuple, list)): + if isinstance( + with_polymorphic[0], util.string_types + (tuple, list)): + self.with_polymorphic = with_polymorphic + else: + self.with_polymorphic = (with_polymorphic, None) + elif with_polymorphic is not None: + raise sa_exc.ArgumentError("Invalid setting for with_polymorphic") + else: + self.with_polymorphic = None + + if isinstance(self.local_table, expression.SelectBase): + raise sa_exc.InvalidRequestError( + "When mapping against a select() construct, map against " + "an alias() of the construct instead." + "This because several databases don't allow a " + "SELECT from a subquery that does not have an alias." + ) + + if self.with_polymorphic and \ + isinstance(self.with_polymorphic[1], + expression.SelectBase): + self.with_polymorphic = (self.with_polymorphic[0], + self.with_polymorphic[1].alias()) + + if self.configured: + self._expire_memoizations() + + def _add_with_polymorphic_subclass(self, mapper): + subcl = mapper.class_ + if self.with_polymorphic is None: + self._set_with_polymorphic((subcl,)) + elif self.with_polymorphic[0] != '*': + self._set_with_polymorphic( + ( + self.with_polymorphic[0] + (subcl, ), + self.with_polymorphic[1] + ) + ) + + def _set_concrete_base(self, mapper): + """Set the given :class:`.Mapper` as the 'inherits' for this + :class:`.Mapper`, assuming this :class:`.Mapper` is concrete + and does not already have an inherits.""" + + assert self.concrete + assert not self.inherits + assert isinstance(mapper, Mapper) + self.inherits = mapper + self.inherits.polymorphic_map.update(self.polymorphic_map) + self.polymorphic_map = self.inherits.polymorphic_map + for mapper in self.iterate_to_root(): + if mapper.polymorphic_on is not None: + mapper._requires_row_aliasing = True + self.batch = self.inherits.batch + for mp in self.self_and_descendants: + mp.base_mapper = self.inherits.base_mapper + self.inherits._inheriting_mappers.append(self) + self.passive_updates = self.inherits.passive_updates + self._all_tables = self.inherits._all_tables + + for key, prop in mapper._props.items(): + if key not in self._props and \ + not self._should_exclude(key, key, local=False, + column=None): + self._adapt_inherited_property(key, prop, False) + + def _set_polymorphic_on(self, polymorphic_on): + self.polymorphic_on = polymorphic_on + self._configure_polymorphic_setter(True) + + def _configure_legacy_instrument_class(self): + + if self.inherits: + self.dispatch._update(self.inherits.dispatch) + super_extensions = set( + chain(*[m._deprecated_extensions + for m in self.inherits.iterate_to_root()])) + else: + super_extensions = set() + + for ext in self._deprecated_extensions: + if ext not in super_extensions: + ext._adapt_instrument_class(self, ext) + + def _configure_listeners(self): + if self.inherits: + super_extensions = set( + chain(*[m._deprecated_extensions + for m in self.inherits.iterate_to_root()])) + else: + super_extensions = set() + + for ext in self._deprecated_extensions: + if ext not in super_extensions: + ext._adapt_listener(self, ext) + + def _configure_class_instrumentation(self): + """If this mapper is to be a primary mapper (i.e. the + non_primary flag is not set), associate this Mapper with the + given class_ and entity name. + + Subsequent calls to ``class_mapper()`` for the class_/entity + name combination will return this mapper. Also decorate the + `__init__` method on the mapped class to include optional + auto-session attachment logic. + + """ + + manager = attributes.manager_of_class(self.class_) + + if self.non_primary: + if not manager or not manager.is_mapped: + raise sa_exc.InvalidRequestError( + "Class %s has no primary mapper configured. Configure " + "a primary mapper first before setting up a non primary " + "Mapper." % self.class_) + self.class_manager = manager + self._identity_class = manager.mapper._identity_class + _mapper_registry[self] = True + return + + if manager is not None: + assert manager.class_ is self.class_ + if manager.is_mapped: + raise sa_exc.ArgumentError( + "Class '%s' already has a primary mapper defined. " + "Use non_primary=True to " + "create a non primary Mapper. clear_mappers() will " + "remove *all* current mappers from all classes." % + self.class_) + # else: + # a ClassManager may already exist as + # ClassManager.instrument_attribute() creates + # new managers for each subclass if they don't yet exist. + + _mapper_registry[self] = True + + # note: this *must be called before instrumentation.register_class* + # to maintain the documented behavior of instrument_class + self.dispatch.instrument_class(self, self.class_) + + if manager is None: + manager = instrumentation.register_class(self.class_) + + self.class_manager = manager + + manager.mapper = self + manager.deferred_scalar_loader = util.partial( + loading.load_scalar_attributes, self) + + # The remaining members can be added by any mapper, + # e_name None or not. + if manager.info.get(_INSTRUMENTOR, False): + return + + event.listen(manager, 'first_init', _event_on_first_init, raw=True) + event.listen(manager, 'init', _event_on_init, raw=True) + + for key, method in util.iterate_attributes(self.class_): + if key == '__init__' and hasattr(method, '_sa_original_init'): + method = method._sa_original_init + if isinstance(method, types.MethodType): + method = method.im_func + if isinstance(method, types.FunctionType): + if hasattr(method, '__sa_reconstructor__'): + self._reconstructor = method + event.listen(manager, 'load', _event_on_load, raw=True) + elif hasattr(method, '__sa_validators__'): + validation_opts = method.__sa_validation_opts__ + for name in method.__sa_validators__: + if name in self.validators: + raise sa_exc.InvalidRequestError( + "A validation function for mapped " + "attribute %r on mapper %s already exists." % + (name, self)) + self.validators = self.validators.union( + {name: (method, validation_opts)} + ) + + manager.info[_INSTRUMENTOR] = self + + @classmethod + def _configure_all(cls): + """Class-level path to the :func:`.configure_mappers` call. + """ + configure_mappers() + + def dispose(self): + # Disable any attribute-based compilation. + self.configured = True + + if hasattr(self, '_configure_failed'): + del self._configure_failed + + if not self.non_primary and \ + self.class_manager is not None and \ + self.class_manager.is_mapped and \ + self.class_manager.mapper is self: + instrumentation.unregister_class(self.class_) + + def _configure_pks(self): + self.tables = sql_util.find_tables(self.mapped_table) + + self._pks_by_table = {} + self._cols_by_table = {} + + all_cols = util.column_set(chain(*[ + col.proxy_set for col in + self._columntoproperty])) + + pk_cols = util.column_set(c for c in all_cols if c.primary_key) + + # identify primary key columns which are also mapped by this mapper. + tables = set(self.tables + [self.mapped_table]) + self._all_tables.update(tables) + for t in tables: + if t.primary_key and pk_cols.issuperset(t.primary_key): + # ordering is important since it determines the ordering of + # mapper.primary_key (and therefore query.get()) + self._pks_by_table[t] = \ + util.ordered_column_set(t.primary_key).\ + intersection(pk_cols) + self._cols_by_table[t] = \ + util.ordered_column_set(t.c).\ + intersection(all_cols) + + # if explicit PK argument sent, add those columns to the + # primary key mappings + if self._primary_key_argument: + for k in self._primary_key_argument: + if k.table not in self._pks_by_table: + self._pks_by_table[k.table] = util.OrderedSet() + self._pks_by_table[k.table].add(k) + + # otherwise, see that we got a full PK for the mapped table + elif self.mapped_table not in self._pks_by_table or \ + len(self._pks_by_table[self.mapped_table]) == 0: + raise sa_exc.ArgumentError( + "Mapper %s could not assemble any primary " + "key columns for mapped table '%s'" % + (self, self.mapped_table.description)) + elif self.local_table not in self._pks_by_table and \ + isinstance(self.local_table, schema.Table): + util.warn("Could not assemble any primary " + "keys for locally mapped table '%s' - " + "no rows will be persisted in this Table." + % self.local_table.description) + + if self.inherits and \ + not self.concrete and \ + not self._primary_key_argument: + # if inheriting, the "primary key" for this mapper is + # that of the inheriting (unless concrete or explicit) + self.primary_key = self.inherits.primary_key + else: + # determine primary key from argument or mapped_table pks - + # reduce to the minimal set of columns + if self._primary_key_argument: + primary_key = sql_util.reduce_columns( + [self.mapped_table.corresponding_column(c) for c in + self._primary_key_argument], + ignore_nonexistent_tables=True) + else: + primary_key = sql_util.reduce_columns( + self._pks_by_table[self.mapped_table], + ignore_nonexistent_tables=True) + + if len(primary_key) == 0: + raise sa_exc.ArgumentError( + "Mapper %s could not assemble any primary " + "key columns for mapped table '%s'" % + (self, self.mapped_table.description)) + + self.primary_key = tuple(primary_key) + self._log("Identified primary key columns: %s", primary_key) + + # determine cols that aren't expressed within our tables; mark these + # as "read only" properties which are refreshed upon INSERT/UPDATE + self._readonly_props = set( + self._columntoproperty[col] + for col in self._columntoproperty + if self._columntoproperty[col] not in self._identity_key_props and + (not hasattr(col, 'table') or + col.table not in self._cols_by_table)) + + def _configure_properties(self): + # Column and other ClauseElement objects which are mapped + self.columns = self.c = util.OrderedProperties() + + # object attribute names mapped to MapperProperty objects + self._props = util.OrderedDict() + + # table columns mapped to lists of MapperProperty objects + # using a list allows a single column to be defined as + # populating multiple object attributes + self._columntoproperty = _ColumnMapping(self) + + # load custom properties + if self._init_properties: + for key, prop in self._init_properties.items(): + self._configure_property(key, prop, False) + + # pull properties from the inherited mapper if any. + if self.inherits: + for key, prop in self.inherits._props.items(): + if key not in self._props and \ + not self._should_exclude(key, key, local=False, + column=None): + self._adapt_inherited_property(key, prop, False) + + # create properties for each column in the mapped table, + # for those columns which don't already map to a property + for column in self.mapped_table.columns: + if column in self._columntoproperty: + continue + + column_key = (self.column_prefix or '') + column.key + + if self._should_exclude( + column.key, column_key, + local=self.local_table.c.contains_column(column), + column=column + ): + continue + + # adjust the "key" used for this column to that + # of the inheriting mapper + for mapper in self.iterate_to_root(): + if column in mapper._columntoproperty: + column_key = mapper._columntoproperty[column].key + + self._configure_property(column_key, + column, + init=False, + setparent=True) + + def _configure_polymorphic_setter(self, init=False): + """Configure an attribute on the mapper representing the + 'polymorphic_on' column, if applicable, and not + already generated by _configure_properties (which is typical). + + Also create a setter function which will assign this + attribute to the value of the 'polymorphic_identity' + upon instance construction, also if applicable. This + routine will run when an instance is created. + + """ + setter = False + + if self.polymorphic_on is not None: + setter = True + + if isinstance(self.polymorphic_on, util.string_types): + # polymorphic_on specified as a string - link + # it to mapped ColumnProperty + try: + self.polymorphic_on = self._props[self.polymorphic_on] + except KeyError: + raise sa_exc.ArgumentError( + "Can't determine polymorphic_on " + "value '%s' - no attribute is " + "mapped to this name." % self.polymorphic_on) + + if self.polymorphic_on in self._columntoproperty: + # polymorphic_on is a column that is already mapped + # to a ColumnProperty + prop = self._columntoproperty[self.polymorphic_on] + elif isinstance(self.polymorphic_on, MapperProperty): + # polymorphic_on is directly a MapperProperty, + # ensure it's a ColumnProperty + if not isinstance(self.polymorphic_on, + properties.ColumnProperty): + raise sa_exc.ArgumentError( + "Only direct column-mapped " + "property or SQL expression " + "can be passed for polymorphic_on") + prop = self.polymorphic_on + elif not expression._is_column(self.polymorphic_on): + # polymorphic_on is not a Column and not a ColumnProperty; + # not supported right now. + raise sa_exc.ArgumentError( + "Only direct column-mapped " + "property or SQL expression " + "can be passed for polymorphic_on" + ) + else: + # polymorphic_on is a Column or SQL expression and + # doesn't appear to be mapped. this means it can be 1. + # only present in the with_polymorphic selectable or + # 2. a totally standalone SQL expression which we'd + # hope is compatible with this mapper's mapped_table + col = self.mapped_table.corresponding_column( + self.polymorphic_on) + if col is None: + # polymorphic_on doesn't derive from any + # column/expression isn't present in the mapped + # table. we will make a "hidden" ColumnProperty + # for it. Just check that if it's directly a + # schema.Column and we have with_polymorphic, it's + # likely a user error if the schema.Column isn't + # represented somehow in either mapped_table or + # with_polymorphic. Otherwise as of 0.7.4 we + # just go with it and assume the user wants it + # that way (i.e. a CASE statement) + setter = False + instrument = False + col = self.polymorphic_on + if isinstance(col, schema.Column) and ( + self.with_polymorphic is None or + self.with_polymorphic[1]. + corresponding_column(col) is None): + raise sa_exc.InvalidRequestError( + "Could not map polymorphic_on column " + "'%s' to the mapped table - polymorphic " + "loads will not function properly" + % col.description) + else: + # column/expression that polymorphic_on derives from + # is present in our mapped table + # and is probably mapped, but polymorphic_on itself + # is not. This happens when + # the polymorphic_on is only directly present in the + # with_polymorphic selectable, as when use + # polymorphic_union. + # we'll make a separate ColumnProperty for it. + instrument = True + key = getattr(col, 'key', None) + if key: + if self._should_exclude(col.key, col.key, False, col): + raise sa_exc.InvalidRequestError( + "Cannot exclude or override the " + "discriminator column %r" % + col.key) + else: + self.polymorphic_on = col = \ + col.label("_sa_polymorphic_on") + key = col.key + + prop = properties.ColumnProperty(col, _instrument=instrument) + self._configure_property(key, prop, init=init, setparent=True) + + # the actual polymorphic_on should be the first public-facing + # column in the property + self.polymorphic_on = prop.columns[0] + polymorphic_key = prop.key + + else: + # no polymorphic_on was set. + # check inheriting mappers for one. + for mapper in self.iterate_to_root(): + # determine if polymorphic_on of the parent + # should be propagated here. If the col + # is present in our mapped table, or if our mapped + # table is the same as the parent (i.e. single table + # inheritance), we can use it + if mapper.polymorphic_on is not None: + if self.mapped_table is mapper.mapped_table: + self.polymorphic_on = mapper.polymorphic_on + else: + self.polymorphic_on = \ + self.mapped_table.corresponding_column( + mapper.polymorphic_on) + # we can use the parent mapper's _set_polymorphic_identity + # directly; it ensures the polymorphic_identity of the + # instance's mapper is used so is portable to subclasses. + if self.polymorphic_on is not None: + self._set_polymorphic_identity = \ + mapper._set_polymorphic_identity + self._validate_polymorphic_identity = \ + mapper._validate_polymorphic_identity + else: + self._set_polymorphic_identity = None + return + + if setter: + def _set_polymorphic_identity(state): + dict_ = state.dict + state.get_impl(polymorphic_key).set( + state, dict_, + state.manager.mapper.polymorphic_identity, + None) + + def _validate_polymorphic_identity(mapper, state, dict_): + if polymorphic_key in dict_ and \ + dict_[polymorphic_key] not in \ + mapper._acceptable_polymorphic_identities: + util.warn_limited( + "Flushing object %s with " + "incompatible polymorphic identity %r; the " + "object may not refresh and/or load correctly", + (state_str(state), dict_[polymorphic_key]) + ) + + self._set_polymorphic_identity = _set_polymorphic_identity + self._validate_polymorphic_identity = \ + _validate_polymorphic_identity + else: + self._set_polymorphic_identity = None + + _validate_polymorphic_identity = None + + @_memoized_configured_property + def _version_id_prop(self): + if self.version_id_col is not None: + return self._columntoproperty[self.version_id_col] + else: + return None + + @_memoized_configured_property + def _acceptable_polymorphic_identities(self): + identities = set() + + stack = deque([self]) + while stack: + item = stack.popleft() + if item.mapped_table is self.mapped_table: + identities.add(item.polymorphic_identity) + stack.extend(item._inheriting_mappers) + + return identities + + @_memoized_configured_property + def _prop_set(self): + return frozenset(self._props.values()) + + def _adapt_inherited_property(self, key, prop, init): + if not self.concrete: + self._configure_property(key, prop, init=False, setparent=False) + elif key not in self._props: + # determine if the class implements this attribute; if not, + # or if it is implemented by the attribute that is handling the + # given superclass-mapped property, then we need to report that we + # can't use this at the instance level since we are a concrete + # mapper and we don't map this. don't trip user-defined + # descriptors that might have side effects when invoked. + implementing_attribute = self.class_manager._get_class_attr_mro( + key, prop) + if implementing_attribute is prop or (isinstance( + implementing_attribute, + attributes.InstrumentedAttribute) and + implementing_attribute._parententity is prop.parent + ): + self._configure_property( + key, + properties.ConcreteInheritedProperty(), + init=init, setparent=True) + + def _configure_property(self, key, prop, init=True, setparent=True): + self._log("_configure_property(%s, %s)", key, prop.__class__.__name__) + + if not isinstance(prop, MapperProperty): + prop = self._property_from_column(key, prop) + + if isinstance(prop, properties.ColumnProperty): + col = self.mapped_table.corresponding_column(prop.columns[0]) + + # if the column is not present in the mapped table, + # test if a column has been added after the fact to the + # parent table (or their parent, etc.) [ticket:1570] + if col is None and self.inherits: + path = [self] + for m in self.inherits.iterate_to_root(): + col = m.local_table.corresponding_column(prop.columns[0]) + if col is not None: + for m2 in path: + m2.mapped_table._reset_exported() + col = self.mapped_table.corresponding_column( + prop.columns[0]) + break + path.append(m) + + # subquery expression, column not present in the mapped + # selectable. + if col is None: + col = prop.columns[0] + + # column is coming in after _readonly_props was + # initialized; check for 'readonly' + if hasattr(self, '_readonly_props') and \ + (not hasattr(col, 'table') or + col.table not in self._cols_by_table): + self._readonly_props.add(prop) + + else: + # if column is coming in after _cols_by_table was + # initialized, ensure the col is in the right set + if hasattr(self, '_cols_by_table') and \ + col.table in self._cols_by_table and \ + col not in self._cols_by_table[col.table]: + self._cols_by_table[col.table].add(col) + + # if this properties.ColumnProperty represents the "polymorphic + # discriminator" column, mark it. We'll need this when rendering + # columns in SELECT statements. + if not hasattr(prop, '_is_polymorphic_discriminator'): + prop._is_polymorphic_discriminator = \ + (col is self.polymorphic_on or + prop.columns[0] is self.polymorphic_on) + + self.columns[key] = col + for col in prop.columns + prop._orig_columns: + for col in col.proxy_set: + self._columntoproperty[col] = prop + + prop.key = key + + if setparent: + prop.set_parent(self, init) + + if key in self._props and \ + getattr(self._props[key], '_mapped_by_synonym', False): + syn = self._props[key]._mapped_by_synonym + raise sa_exc.ArgumentError( + "Can't call map_column=True for synonym %r=%r, " + "a ColumnProperty already exists keyed to the name " + "%r for column %r" % (syn, key, key, syn) + ) + + if key in self._props and \ + not isinstance(prop, properties.ColumnProperty) and \ + not isinstance( + self._props[key], + ( + properties.ColumnProperty, + properties.ConcreteInheritedProperty) + ): + util.warn("Property %s on %s being replaced with new " + "property %s; the old property will be discarded" % ( + self._props[key], + self, + prop, + )) + oldprop = self._props[key] + self._path_registry.pop(oldprop, None) + + self._props[key] = prop + + if not self.non_primary: + prop.instrument_class(self) + + for mapper in self._inheriting_mappers: + mapper._adapt_inherited_property(key, prop, init) + + if init: + prop.init() + prop.post_instrument_class(self) + + if self.configured: + self._expire_memoizations() + + def _property_from_column(self, key, prop): + """generate/update a :class:`.ColumnProprerty` given a + :class:`.Column` object. """ + + # we were passed a Column or a list of Columns; + # generate a properties.ColumnProperty + columns = util.to_list(prop) + column = columns[0] + if not expression._is_column(column): + raise sa_exc.ArgumentError( + "%s=%r is not an instance of MapperProperty or Column" + % (key, prop)) + + prop = self._props.get(key, None) + + if isinstance(prop, properties.ColumnProperty): + if ( + not self._inherits_equated_pairs or + (prop.columns[0], column) not in self._inherits_equated_pairs + ) and \ + not prop.columns[0].shares_lineage(column) and \ + prop.columns[0] is not self.version_id_col and \ + column is not self.version_id_col: + warn_only = prop.parent is not self + msg = ("Implicitly combining column %s with column " + "%s under attribute '%s'. Please configure one " + "or more attributes for these same-named columns " + "explicitly." % (prop.columns[-1], column, key)) + if warn_only: + util.warn(msg) + else: + raise sa_exc.InvalidRequestError(msg) + + # existing properties.ColumnProperty from an inheriting + # mapper. make a copy and append our column to it + prop = prop.copy() + prop.columns.insert(0, column) + self._log("inserting column to existing list " + "in properties.ColumnProperty %s" % (key)) + return prop + elif prop is None or isinstance(prop, + properties.ConcreteInheritedProperty): + mapped_column = [] + for c in columns: + mc = self.mapped_table.corresponding_column(c) + if mc is None: + mc = self.local_table.corresponding_column(c) + if mc is not None: + # if the column is in the local table but not the + # mapped table, this corresponds to adding a + # column after the fact to the local table. + # [ticket:1523] + self.mapped_table._reset_exported() + mc = self.mapped_table.corresponding_column(c) + if mc is None: + raise sa_exc.ArgumentError( + "When configuring property '%s' on %s, " + "column '%s' is not represented in the mapper's " + "table. Use the `column_property()` function to " + "force this column to be mapped as a read-only " + "attribute." % (key, self, c)) + mapped_column.append(mc) + return properties.ColumnProperty(*mapped_column) + else: + raise sa_exc.ArgumentError( + "WARNING: when configuring property '%s' on %s, " + "column '%s' conflicts with property '%r'. " + "To resolve this, map the column to the class under a " + "different name in the 'properties' dictionary. Or, " + "to remove all awareness of the column entirely " + "(including its availability as a foreign key), " + "use the 'include_properties' or 'exclude_properties' " + "mapper arguments to control specifically which table " + "columns get mapped." % + (key, self, column.key, prop)) + + def _post_configure_properties(self): + """Call the ``init()`` method on all ``MapperProperties`` + attached to this mapper. + + This is a deferred configuration step which is intended + to execute once all mappers have been constructed. + + """ + + self._log("_post_configure_properties() started") + l = [(key, prop) for key, prop in self._props.items()] + for key, prop in l: + self._log("initialize prop %s", key) + + if prop.parent is self and not prop._configure_started: + prop.init() + + if prop._configure_finished: + prop.post_instrument_class(self) + + self._log("_post_configure_properties() complete") + self.configured = True + + def add_properties(self, dict_of_properties): + """Add the given dictionary of properties to this mapper, + using `add_property`. + + """ + for key, value in dict_of_properties.items(): + self.add_property(key, value) + + def add_property(self, key, prop): + """Add an individual MapperProperty to this mapper. + + If the mapper has not been configured yet, just adds the + property to the initial properties dictionary sent to the + constructor. If this Mapper has already been configured, then + the given MapperProperty is configured immediately. + + """ + self._init_properties[key] = prop + self._configure_property(key, prop, init=self.configured) + + def _expire_memoizations(self): + for mapper in self.iterate_to_root(): + _memoized_configured_property.expire_instance(mapper) + + @property + def _log_desc(self): + return "(" + self.class_.__name__ + \ + "|" + \ + (self.local_table is not None and + self.local_table.description or + str(self.local_table)) +\ + (self.non_primary and + "|non-primary" or "") + ")" + + def _log(self, msg, *args): + self.logger.info( + "%s " + msg, *((self._log_desc,) + args) + ) + + def _log_debug(self, msg, *args): + self.logger.debug( + "%s " + msg, *((self._log_desc,) + args) + ) + + def __repr__(self): + return '<Mapper at 0x%x; %s>' % ( + id(self), self.class_.__name__) + + def __str__(self): + return "Mapper|%s|%s%s" % ( + self.class_.__name__, + self.local_table is not None and + self.local_table.description or None, + self.non_primary and "|non-primary" or "" + ) + + def _is_orphan(self, state): + orphan_possible = False + for mapper in self.iterate_to_root(): + for (key, cls) in mapper._delete_orphans: + orphan_possible = True + + has_parent = attributes.manager_of_class(cls).has_parent( + state, key, optimistic=state.has_identity) + + if self.legacy_is_orphan and has_parent: + return False + elif not self.legacy_is_orphan and not has_parent: + return True + + if self.legacy_is_orphan: + return orphan_possible + else: + return False + + def has_property(self, key): + return key in self._props + + def get_property(self, key, _configure_mappers=True): + """return a MapperProperty associated with the given key. + """ + + if _configure_mappers and Mapper._new_mappers: + configure_mappers() + + try: + return self._props[key] + except KeyError: + raise sa_exc.InvalidRequestError( + "Mapper '%s' has no property '%s'" % (self, key)) + + def get_property_by_column(self, column): + """Given a :class:`.Column` object, return the + :class:`.MapperProperty` which maps this column.""" + + return self._columntoproperty[column] + + @property + def iterate_properties(self): + """return an iterator of all MapperProperty objects.""" + if Mapper._new_mappers: + configure_mappers() + return iter(self._props.values()) + + def _mappers_from_spec(self, spec, selectable): + """given a with_polymorphic() argument, return the set of mappers it + represents. + + Trims the list of mappers to just those represented within the given + selectable, if present. This helps some more legacy-ish mappings. + + """ + if spec == '*': + mappers = list(self.self_and_descendants) + elif spec: + mappers = set() + for m in util.to_list(spec): + m = _class_to_mapper(m) + if not m.isa(self): + raise sa_exc.InvalidRequestError( + "%r does not inherit from %r" % + (m, self)) + + if selectable is None: + mappers.update(m.iterate_to_root()) + else: + mappers.add(m) + mappers = [m for m in self.self_and_descendants if m in mappers] + else: + mappers = [] + + if selectable is not None: + tables = set(sql_util.find_tables(selectable, + include_aliases=True)) + mappers = [m for m in mappers if m.local_table in tables] + return mappers + + def _selectable_from_mappers(self, mappers, innerjoin): + """given a list of mappers (assumed to be within this mapper's + inheritance hierarchy), construct an outerjoin amongst those mapper's + mapped tables. + + """ + from_obj = self.mapped_table + for m in mappers: + if m is self: + continue + if m.concrete: + raise sa_exc.InvalidRequestError( + "'with_polymorphic()' requires 'selectable' argument " + "when concrete-inheriting mappers are used.") + elif not m.single: + if innerjoin: + from_obj = from_obj.join(m.local_table, + m.inherit_condition) + else: + from_obj = from_obj.outerjoin(m.local_table, + m.inherit_condition) + + return from_obj + + @_memoized_configured_property + def _single_table_criterion(self): + if self.single and \ + self.inherits and \ + self.polymorphic_on is not None: + return self.polymorphic_on.in_( + m.polymorphic_identity + for m in self.self_and_descendants) + else: + return None + + @_memoized_configured_property + def _with_polymorphic_mappers(self): + if Mapper._new_mappers: + configure_mappers() + if not self.with_polymorphic: + return [] + return self._mappers_from_spec(*self.with_polymorphic) + + @_memoized_configured_property + def _with_polymorphic_selectable(self): + if not self.with_polymorphic: + return self.mapped_table + + spec, selectable = self.with_polymorphic + if selectable is not None: + return selectable + else: + return self._selectable_from_mappers( + self._mappers_from_spec(spec, selectable), + False) + + with_polymorphic_mappers = _with_polymorphic_mappers + """The list of :class:`.Mapper` objects included in the + default "polymorphic" query. + + """ + + @_memoized_configured_property + def _insert_cols_evaluating_none(self): + return dict( + ( + table, + frozenset( + col for col in columns + if col.type.should_evaluate_none + ) + ) + for table, columns in self._cols_by_table.items() + ) + + @_memoized_configured_property + def _insert_cols_as_none(self): + return dict( + ( + table, + frozenset( + col.key for col in columns + if not col.primary_key and + not col.server_default and not col.default + and not col.type.should_evaluate_none) + ) + for table, columns in self._cols_by_table.items() + ) + + @_memoized_configured_property + def _propkey_to_col(self): + return dict( + ( + table, + dict( + (self._columntoproperty[col].key, col) + for col in columns + ) + ) + for table, columns in self._cols_by_table.items() + ) + + @_memoized_configured_property + def _pk_keys_by_table(self): + return dict( + ( + table, + frozenset([col.key for col in pks]) + ) + for table, pks in self._pks_by_table.items() + ) + + @_memoized_configured_property + def _pk_attr_keys_by_table(self): + return dict( + ( + table, + frozenset([self._columntoproperty[col].key for col in pks]) + ) + for table, pks in self._pks_by_table.items() + ) + + @_memoized_configured_property + def _server_default_cols(self): + return dict( + ( + table, + frozenset([ + col.key for col in columns + if col.server_default is not None]) + ) + for table, columns in self._cols_by_table.items() + ) + + @_memoized_configured_property + def _server_default_plus_onupdate_propkeys(self): + result = set() + + for table, columns in self._cols_by_table.items(): + for col in columns: + if ( + ( + col.server_default is not None or + col.server_onupdate is not None + ) and col in self._columntoproperty + ): + result.add(self._columntoproperty[col].key) + + return result + + @_memoized_configured_property + def _server_onupdate_default_cols(self): + return dict( + ( + table, + frozenset([ + col.key for col in columns + if col.server_onupdate is not None]) + ) + for table, columns in self._cols_by_table.items() + ) + + @property + def selectable(self): + """The :func:`.select` construct this :class:`.Mapper` selects from + by default. + + Normally, this is equivalent to :attr:`.mapped_table`, unless + the ``with_polymorphic`` feature is in use, in which case the + full "polymorphic" selectable is returned. + + """ + return self._with_polymorphic_selectable + + def _with_polymorphic_args(self, spec=None, selectable=False, + innerjoin=False): + if self.with_polymorphic: + if not spec: + spec = self.with_polymorphic[0] + if selectable is False: + selectable = self.with_polymorphic[1] + elif selectable is False: + selectable = None + mappers = self._mappers_from_spec(spec, selectable) + if selectable is not None: + return mappers, selectable + else: + return mappers, self._selectable_from_mappers(mappers, + innerjoin) + + @_memoized_configured_property + def _polymorphic_properties(self): + return list(self._iterate_polymorphic_properties( + self._with_polymorphic_mappers)) + + def _iterate_polymorphic_properties(self, mappers=None): + """Return an iterator of MapperProperty objects which will render into + a SELECT.""" + if mappers is None: + mappers = self._with_polymorphic_mappers + + if not mappers: + for c in self.iterate_properties: + yield c + else: + # in the polymorphic case, filter out discriminator columns + # from other mappers, as these are sometimes dependent on that + # mapper's polymorphic selectable (which we don't want rendered) + for c in util.unique_list( + chain(*[ + list(mapper.iterate_properties) for mapper in + [self] + mappers + ]) + ): + if getattr(c, '_is_polymorphic_discriminator', False) and \ + (self.polymorphic_on is None or + c.columns[0] is not self.polymorphic_on): + continue + yield c + + @_memoized_configured_property + def attrs(self): + """A namespace of all :class:`.MapperProperty` objects + associated this mapper. + + This is an object that provides each property based on + its key name. For instance, the mapper for a + ``User`` class which has ``User.name`` attribute would + provide ``mapper.attrs.name``, which would be the + :class:`.ColumnProperty` representing the ``name`` + column. The namespace object can also be iterated, + which would yield each :class:`.MapperProperty`. + + :class:`.Mapper` has several pre-filtered views + of this attribute which limit the types of properties + returned, inclding :attr:`.synonyms`, :attr:`.column_attrs`, + :attr:`.relationships`, and :attr:`.composites`. + + .. warning:: + + The :attr:`.Mapper.attrs` accessor namespace is an + instance of :class:`.OrderedProperties`. This is + a dictionary-like object which includes a small number of + named methods such as :meth:`.OrderedProperties.items` + and :meth:`.OrderedProperties.values`. When + accessing attributes dynamically, favor using the dict-access + scheme, e.g. ``mapper.attrs[somename]`` over + ``getattr(mapper.attrs, somename)`` to avoid name collisions. + + .. seealso:: + + :attr:`.Mapper.all_orm_descriptors` + + """ + if Mapper._new_mappers: + configure_mappers() + return util.ImmutableProperties(self._props) + + @_memoized_configured_property + def all_orm_descriptors(self): + """A namespace of all :class:`.InspectionAttr` attributes associated + with the mapped class. + + These attributes are in all cases Python :term:`descriptors` + associated with the mapped class or its superclasses. + + This namespace includes attributes that are mapped to the class + as well as attributes declared by extension modules. + It includes any Python descriptor type that inherits from + :class:`.InspectionAttr`. This includes + :class:`.QueryableAttribute`, as well as extension types such as + :class:`.hybrid_property`, :class:`.hybrid_method` and + :class:`.AssociationProxy`. + + To distinguish between mapped attributes and extension attributes, + the attribute :attr:`.InspectionAttr.extension_type` will refer + to a constant that distinguishes between different extension types. + + When dealing with a :class:`.QueryableAttribute`, the + :attr:`.QueryableAttribute.property` attribute refers to the + :class:`.MapperProperty` property, which is what you get when + referring to the collection of mapped properties via + :attr:`.Mapper.attrs`. + + .. warning:: + + The :attr:`.Mapper.all_orm_descriptors` accessor namespace is an + instance of :class:`.OrderedProperties`. This is + a dictionary-like object which includes a small number of + named methods such as :meth:`.OrderedProperties.items` + and :meth:`.OrderedProperties.values`. When + accessing attributes dynamically, favor using the dict-access + scheme, e.g. ``mapper.all_orm_descriptors[somename]`` over + ``getattr(mapper.all_orm_descriptors, somename)`` to avoid name + collisions. + + .. versionadded:: 0.8.0 + + .. seealso:: + + :attr:`.Mapper.attrs` + + """ + return util.ImmutableProperties( + dict(self.class_manager._all_sqla_attributes())) + + @_memoized_configured_property + def synonyms(self): + """Return a namespace of all :class:`.SynonymProperty` + properties maintained by this :class:`.Mapper`. + + .. seealso:: + + :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty` + objects. + + """ + return self._filter_properties(properties.SynonymProperty) + + @_memoized_configured_property + def column_attrs(self): + """Return a namespace of all :class:`.ColumnProperty` + properties maintained by this :class:`.Mapper`. + + .. seealso:: + + :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty` + objects. + + """ + return self._filter_properties(properties.ColumnProperty) + + @_memoized_configured_property + def relationships(self): + """A namespace of all :class:`.RelationshipProperty` properties + maintained by this :class:`.Mapper`. + + .. warning:: + + the :attr:`.Mapper.relationships` accessor namespace is an + instance of :class:`.OrderedProperties`. This is + a dictionary-like object which includes a small number of + named methods such as :meth:`.OrderedProperties.items` + and :meth:`.OrderedProperties.values`. When + accessing attributes dynamically, favor using the dict-access + scheme, e.g. ``mapper.relationships[somename]`` over + ``getattr(mapper.relationships, somename)`` to avoid name + collisions. + + .. seealso:: + + :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty` + objects. + + """ + return self._filter_properties(properties.RelationshipProperty) + + @_memoized_configured_property + def composites(self): + """Return a namespace of all :class:`.CompositeProperty` + properties maintained by this :class:`.Mapper`. + + .. seealso:: + + :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty` + objects. + + """ + return self._filter_properties(properties.CompositeProperty) + + def _filter_properties(self, type_): + if Mapper._new_mappers: + configure_mappers() + return util.ImmutableProperties(util.OrderedDict( + (k, v) for k, v in self._props.items() + if isinstance(v, type_) + )) + + @_memoized_configured_property + def _get_clause(self): + """create a "get clause" based on the primary key. this is used + by query.get() and many-to-one lazyloads to load this item + by primary key. + + """ + params = [(primary_key, sql.bindparam(None, type_=primary_key.type)) + for primary_key in self.primary_key] + return sql.and_(*[k == v for (k, v) in params]), \ + util.column_dict(params) + + @_memoized_configured_property + def _equivalent_columns(self): + """Create a map of all *equivalent* columns, based on + the determination of column pairs that are equated to + one another based on inherit condition. This is designed + to work with the queries that util.polymorphic_union + comes up with, which often don't include the columns from + the base table directly (including the subclass table columns + only). + + The resulting structure is a dictionary of columns mapped + to lists of equivalent columns, i.e. + + { + tablea.col1: + {tableb.col1, tablec.col1}, + tablea.col2: + {tabled.col2} + } + + """ + result = util.column_dict() + + def visit_binary(binary): + if binary.operator == operators.eq: + if binary.left in result: + result[binary.left].add(binary.right) + else: + result[binary.left] = util.column_set((binary.right,)) + if binary.right in result: + result[binary.right].add(binary.left) + else: + result[binary.right] = util.column_set((binary.left,)) + for mapper in self.base_mapper.self_and_descendants: + if mapper.inherit_condition is not None: + visitors.traverse( + mapper.inherit_condition, {}, + {'binary': visit_binary}) + + return result + + def _is_userland_descriptor(self, obj): + if isinstance(obj, (_MappedAttribute, + instrumentation.ClassManager, + expression.ColumnElement)): + return False + else: + return True + + def _should_exclude(self, name, assigned_name, local, column): + """determine whether a particular property should be implicitly + present on the class. + + This occurs when properties are propagated from an inherited class, or + are applied from the columns present in the mapped table. + + """ + + # check for class-bound attributes and/or descriptors, + # either local or from an inherited class + if local: + if self.class_.__dict__.get(assigned_name, None) is not None \ + and self._is_userland_descriptor( + self.class_.__dict__[assigned_name]): + return True + else: + attr = self.class_manager._get_class_attr_mro(assigned_name, None) + if attr is not None and self._is_userland_descriptor(attr): + return True + + if self.include_properties is not None and \ + name not in self.include_properties and \ + (column is None or column not in self.include_properties): + self._log("not including property %s" % (name)) + return True + + if self.exclude_properties is not None and \ + ( + name in self.exclude_properties or + (column is not None and column in self.exclude_properties) + ): + self._log("excluding property %s" % (name)) + return True + + return False + + def common_parent(self, other): + """Return true if the given mapper shares a + common inherited parent as this mapper.""" + + return self.base_mapper is other.base_mapper + + def _canload(self, state, allow_subtypes): + s = self.primary_mapper() + if self.polymorphic_on is not None or allow_subtypes: + return _state_mapper(state).isa(s) + else: + return _state_mapper(state) is s + + def isa(self, other): + """Return True if the this mapper inherits from the given mapper.""" + + m = self + while m and m is not other: + m = m.inherits + return bool(m) + + def iterate_to_root(self): + m = self + while m: + yield m + m = m.inherits + + @_memoized_configured_property + def self_and_descendants(self): + """The collection including this mapper and all descendant mappers. + + This includes not just the immediately inheriting mappers but + all their inheriting mappers as well. + + """ + descendants = [] + stack = deque([self]) + while stack: + item = stack.popleft() + descendants.append(item) + stack.extend(item._inheriting_mappers) + return util.WeakSequence(descendants) + + def polymorphic_iterator(self): + """Iterate through the collection including this mapper and + all descendant mappers. + + This includes not just the immediately inheriting mappers but + all their inheriting mappers as well. + + To iterate through an entire hierarchy, use + ``mapper.base_mapper.polymorphic_iterator()``. + + """ + return iter(self.self_and_descendants) + + def primary_mapper(self): + """Return the primary mapper corresponding to this mapper's class key + (class).""" + + return self.class_manager.mapper + + @property + def primary_base_mapper(self): + return self.class_manager.mapper.base_mapper + + def _result_has_identity_key(self, result, adapter=None): + pk_cols = self.primary_key + if adapter: + pk_cols = [adapter.columns[c] for c in pk_cols] + for col in pk_cols: + if not result._has_key(col): + return False + else: + return True + + def identity_key_from_row(self, row, identity_token=None, adapter=None): + """Return an identity-map key for use in storing/retrieving an + item from the identity map. + + :param row: A :class:`.RowProxy` instance. The columns which are + mapped by this :class:`.Mapper` should be locatable in the row, + preferably via the :class:`.Column` object directly (as is the case + when a :func:`.select` construct is executed), or via string names of + the form ``<tablename>_<colname>``. + + """ + pk_cols = self.primary_key + if adapter: + pk_cols = [adapter.columns[c] for c in pk_cols] + + return self._identity_class, \ + tuple(row[column] for column in pk_cols), identity_token + + def identity_key_from_primary_key(self, primary_key, identity_token=None): + """Return an identity-map key for use in storing/retrieving an + item from an identity map. + + :param primary_key: A list of values indicating the identifier. + + """ + return self._identity_class, tuple(primary_key), identity_token + + def identity_key_from_instance(self, instance): + """Return the identity key for the given instance, based on + its primary key attributes. + + If the instance's state is expired, calling this method + will result in a database check to see if the object has been deleted. + If the row no longer exists, + :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. + + This value is typically also found on the instance state under the + attribute name `key`. + + """ + state = attributes.instance_state(instance) + return self._identity_key_from_state(state, attributes.PASSIVE_OFF) + + def _identity_key_from_state( + self, state, passive=attributes.PASSIVE_RETURN_NEVER_SET): + dict_ = state.dict + manager = state.manager + return self._identity_class, tuple([ + manager[prop.key]. + impl.get(state, dict_, passive) + for prop in self._identity_key_props + ]), state.identity_token + + def primary_key_from_instance(self, instance): + """Return the list of primary key values for the given + instance. + + If the instance's state is expired, calling this method + will result in a database check to see if the object has been deleted. + If the row no longer exists, + :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. + + """ + state = attributes.instance_state(instance) + identity_key = self._identity_key_from_state( + state, attributes.PASSIVE_OFF) + return identity_key[1] + + @_memoized_configured_property + def _identity_key_props(self): + return [self._columntoproperty[col] for col in self.primary_key] + + @_memoized_configured_property + def _all_pk_props(self): + collection = set() + for table in self.tables: + collection.update(self._pks_by_table[table]) + return collection + + @_memoized_configured_property + def _should_undefer_in_wildcard(self): + cols = set(self.primary_key) + if self.polymorphic_on is not None: + cols.add(self.polymorphic_on) + return cols + + @_memoized_configured_property + def _primary_key_propkeys(self): + return {prop.key for prop in self._all_pk_props} + + def _get_state_attr_by_column( + self, state, dict_, column, + passive=attributes.PASSIVE_RETURN_NEVER_SET): + prop = self._columntoproperty[column] + return state.manager[prop.key].impl.get(state, dict_, passive=passive) + + def _set_committed_state_attr_by_column(self, state, dict_, column, value): + prop = self._columntoproperty[column] + state.manager[prop.key].impl.set_committed_value(state, dict_, value) + + def _set_state_attr_by_column(self, state, dict_, column, value): + prop = self._columntoproperty[column] + state.manager[prop.key].impl.set(state, dict_, value, None) + + def _get_committed_attr_by_column(self, obj, column): + state = attributes.instance_state(obj) + dict_ = attributes.instance_dict(obj) + return self._get_committed_state_attr_by_column( + state, dict_, column, passive=attributes.PASSIVE_OFF) + + def _get_committed_state_attr_by_column( + self, state, dict_, column, + passive=attributes.PASSIVE_RETURN_NEVER_SET): + + prop = self._columntoproperty[column] + return state.manager[prop.key].impl.\ + get_committed_value(state, dict_, passive=passive) + + def _optimized_get_statement(self, state, attribute_names): + """assemble a WHERE clause which retrieves a given state by primary + key, using a minimized set of tables. + + Applies to a joined-table inheritance mapper where the + requested attribute names are only present on joined tables, + not the base table. The WHERE clause attempts to include + only those tables to minimize joins. + + """ + props = self._props + + tables = set(chain( + *[sql_util.find_tables(c, check_columns=True) + for key in attribute_names + for c in props[key].columns] + )) + + if self.base_mapper.local_table in tables: + return None + + class ColumnsNotAvailable(Exception): + pass + + def visit_binary(binary): + leftcol = binary.left + rightcol = binary.right + if leftcol is None or rightcol is None: + return + + if leftcol.table not in tables: + leftval = self._get_committed_state_attr_by_column( + state, state.dict, + leftcol, + passive=attributes.PASSIVE_NO_INITIALIZE) + if leftval in orm_util._none_set: + raise ColumnsNotAvailable() + binary.left = sql.bindparam(None, leftval, + type_=binary.right.type) + elif rightcol.table not in tables: + rightval = self._get_committed_state_attr_by_column( + state, state.dict, + rightcol, + passive=attributes.PASSIVE_NO_INITIALIZE) + if rightval in orm_util._none_set: + raise ColumnsNotAvailable() + binary.right = sql.bindparam(None, rightval, + type_=binary.right.type) + + allconds = [] + + try: + start = False + for mapper in reversed(list(self.iterate_to_root())): + if mapper.local_table in tables: + start = True + elif not isinstance(mapper.local_table, + expression.TableClause): + return None + if start and not mapper.single: + allconds.append(visitors.cloned_traverse( + mapper.inherit_condition, + {}, + {'binary': visit_binary} + ) + ) + except ColumnsNotAvailable: + return None + + cond = sql.and_(*allconds) + + cols = [] + for key in attribute_names: + cols.extend(props[key].columns) + return sql.select(cols, cond, use_labels=True) + + def _iterate_to_target_viawpoly(self, mapper): + if self.isa(mapper): + prev = self + for m in self.iterate_to_root(): + yield m + + if m is not prev and prev not in \ + m._with_polymorphic_mappers: + break + + prev = m + if m is mapper: + break + + def _should_selectin_load(self, enabled_via_opt, polymorphic_from): + if not enabled_via_opt: + # common case, takes place for all polymorphic loads + mapper = polymorphic_from + for m in self._iterate_to_target_viawpoly(mapper): + if m.polymorphic_load == 'selectin': + return m + else: + # uncommon case, selectin load options were used + enabled_via_opt = set(enabled_via_opt) + enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} + for entity in enabled_via_opt.union([polymorphic_from]): + mapper = entity.mapper + for m in self._iterate_to_target_viawpoly(mapper): + if m.polymorphic_load == 'selectin' or \ + m in enabled_via_opt_mappers: + return enabled_via_opt_mappers.get(m, m) + + return None + + @util.dependencies( + "sqlalchemy.ext.baked", + "sqlalchemy.orm.strategy_options") + def _subclass_load_via_in(self, baked, strategy_options, entity): + """Assemble a BakedQuery that can load the columns local to + this subclass as a SELECT with IN. + + """ + assert self.inherits + + polymorphic_prop = self._columntoproperty[ + self.polymorphic_on] + keep_props = set( + [polymorphic_prop] + self._identity_key_props) + + disable_opt = strategy_options.Load(entity) + enable_opt = strategy_options.Load(entity) + + for prop in self.attrs: + if prop.parent is self or prop in keep_props: + # "enable" options, to turn on the properties that we want to + # load by default (subject to options from the query) + enable_opt.set_generic_strategy( + (prop.key, ), + dict(prop.strategy_key) + ) + else: + # "disable" options, to turn off the properties from the + # superclass that we *don't* want to load, applied after + # the options from the query to override them + disable_opt.set_generic_strategy( + (prop.key, ), + {"do_nothing": True} + ) + + if len(self.primary_key) > 1: + in_expr = sql.tuple_(*self.primary_key) + else: + in_expr = self.primary_key[0] + + if entity.is_aliased_class: + assert entity.mapper is self + q = baked.BakedQuery( + self._compiled_cache, + lambda session: session.query(entity). + select_entity_from(entity.selectable)._adapt_all_clauses(), + (self, ) + ) + q.spoil() + else: + q = baked.BakedQuery( + self._compiled_cache, + lambda session: session.query(self), + (self, ) + ) + + q += lambda q: q.filter( + in_expr.in_( + sql.bindparam('primary_keys', expanding=True) + ) + ).order_by(*self.primary_key) + + return q, enable_opt, disable_opt + + @_memoized_configured_property + def _subclass_load_via_in_mapper(self): + return self._subclass_load_via_in(self) + + def cascade_iterator(self, type_, state, halt_on=None): + """Iterate each element and its mapper in an object graph, + for all relationships that meet the given cascade rule. + + :param type_: + The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, + etc.). + + .. note:: the ``"all"`` cascade is not accepted here. For a generic + object traversal function, see :ref:`faq_walk_objects`. + + :param state: + The lead InstanceState. child items will be processed per + the relationships defined for this object's mapper. + + :return: the method yields individual object instances. + + .. seealso:: + + :ref:`unitofwork_cascades` + + :ref:`faq_walk_objects` - illustrates a generic function to + traverse all objects without relying on cascades. + + """ + visited_states = set() + prp, mpp = object(), object() + + assert state.mapper.isa(self) + + visitables = deque([(deque(state.mapper._props.values()), prp, + state, state.dict)]) + + while visitables: + iterator, item_type, parent_state, parent_dict = visitables[-1] + if not iterator: + visitables.pop() + continue + + if item_type is prp: + prop = iterator.popleft() + if type_ not in prop.cascade: + continue + queue = deque(prop.cascade_iterator( + type_, parent_state, parent_dict, + visited_states, halt_on)) + if queue: + visitables.append((queue, mpp, None, None)) + elif item_type is mpp: + instance, instance_mapper, corresponding_state, \ + corresponding_dict = iterator.popleft() + yield instance, instance_mapper, \ + corresponding_state, corresponding_dict + visitables.append( + ( + deque(instance_mapper._props.values()), + prp, corresponding_state, + corresponding_dict + ) + ) + + @_memoized_configured_property + def _compiled_cache(self): + return util.LRUCache(self._compiled_cache_size) + + @_memoized_configured_property + def _sorted_tables(self): + table_to_mapper = {} + + for mapper in self.base_mapper.self_and_descendants: + for t in mapper.tables: + table_to_mapper.setdefault(t, mapper) + + extra_dependencies = [] + for table, mapper in table_to_mapper.items(): + super_ = mapper.inherits + if super_: + extra_dependencies.extend([ + (super_table, table) + for super_table in super_.tables + ]) + + def skip(fk): + # attempt to skip dependencies that are not + # significant to the inheritance chain + # for two tables that are related by inheritance. + # while that dependency may be important, it's technically + # not what we mean to sort on here. + parent = table_to_mapper.get(fk.parent.table) + dep = table_to_mapper.get(fk.column.table) + if parent is not None and \ + dep is not None and \ + dep is not parent and \ + dep.inherit_condition is not None: + cols = set(sql_util._find_columns(dep.inherit_condition)) + if parent.inherit_condition is not None: + cols = cols.union(sql_util._find_columns( + parent.inherit_condition)) + return fk.parent not in cols and fk.column not in cols + else: + return fk.parent not in cols + return False + + sorted_ = sql_util.sort_tables(table_to_mapper, + skip_fn=skip, + extra_dependencies=extra_dependencies) + + ret = util.OrderedDict() + for t in sorted_: + ret[t] = table_to_mapper[t] + return ret + + def _memo(self, key, callable_): + if key in self._memoized_values: + return self._memoized_values[key] + else: + self._memoized_values[key] = value = callable_() + return value + + @util.memoized_property + def _table_to_equated(self): + """memoized map of tables to collections of columns to be + synchronized upwards to the base mapper.""" + + result = util.defaultdict(list) + + for table in self._sorted_tables: + cols = set(table.c) + for m in self.iterate_to_root(): + if m._inherits_equated_pairs and \ + cols.intersection( + util.reduce(set.union, + [l.proxy_set for l, r in + m._inherits_equated_pairs]) + ): + result[table].append((m, m._inherits_equated_pairs)) + + return result + + +def configure_mappers(): + """Initialize the inter-mapper relationships of all mappers that + have been constructed thus far. + + This function can be called any number of times, but in + most cases is invoked automatically, the first time mappings are used, + as well as whenever mappings are used and additional not-yet-configured + mappers have been constructed. + + Points at which this occur include when a mapped class is instantiated + into an instance, as well as when the :meth:`.Session.query` method + is used. + + The :func:`.configure_mappers` function provides several event hooks + that can be used to augment its functionality. These methods include: + + * :meth:`.MapperEvents.before_configured` - called once before + :func:`.configure_mappers` does any work; this can be used to establish + additional options, properties, or related mappings before the operation + proceeds. + + * :meth:`.MapperEvents.mapper_configured` - called as each indivudal + :class:`.Mapper` is configured within the process; will include all + mapper state except for backrefs set up by other mappers that are still + to be configured. + + * :meth:`.MapperEvents.after_configured` - called once after + :func:`.configure_mappers` is complete; at this stage, all + :class:`.Mapper` objects that are known to SQLAlchemy will be fully + configured. Note that the calling application may still have other + mappings that haven't been produced yet, such as if they are in modules + as yet unimported. + + """ + + if not Mapper._new_mappers: + return + + _CONFIGURE_MUTEX.acquire() + try: + global _already_compiling + if _already_compiling: + return + _already_compiling = True + try: + + # double-check inside mutex + if not Mapper._new_mappers: + return + + Mapper.dispatch._for_class(Mapper).before_configured() + # initialize properties on all mappers + # note that _mapper_registry is unordered, which + # may randomly conceal/reveal issues related to + # the order of mapper compilation + + for mapper in list(_mapper_registry): + if getattr(mapper, '_configure_failed', False): + e = sa_exc.InvalidRequestError( + "One or more mappers failed to initialize - " + "can't proceed with initialization of other " + "mappers. Triggering mapper: '%s'. " + "Original exception was: %s" + % (mapper, mapper._configure_failed)) + e._configure_failed = mapper._configure_failed + raise e + if not mapper.configured: + try: + mapper._post_configure_properties() + mapper._expire_memoizations() + mapper.dispatch.mapper_configured( + mapper, mapper.class_) + except Exception: + exc = sys.exc_info()[1] + if not hasattr(exc, '_configure_failed'): + mapper._configure_failed = exc + raise + + Mapper._new_mappers = False + finally: + _already_compiling = False + finally: + _CONFIGURE_MUTEX.release() + Mapper.dispatch._for_class(Mapper).after_configured() + + +def reconstructor(fn): + """Decorate a method as the 'reconstructor' hook. + + Designates a method as the "reconstructor", an ``__init__``-like + method that will be called by the ORM after the instance has been + loaded from the database or otherwise reconstituted. + + The reconstructor will be invoked with no arguments. Scalar + (non-collection) database-mapped attributes of the instance will + be available for use within the function. Eagerly-loaded + collections are generally not yet available and will usually only + contain the first element. ORM state changes made to objects at + this stage will not be recorded for the next flush() operation, so + the activity within a reconstructor should be conservative. + + .. seealso:: + + :ref:`mapping_constructors` + + :meth:`.InstanceEvents.load` + + """ + fn.__sa_reconstructor__ = True + return fn + + +def validates(*names, **kw): + r"""Decorate a method as a 'validator' for one or more named properties. + + Designates a method as a validator, a method which receives the + name of the attribute as well as a value to be assigned, or in the + case of a collection, the value to be added to the collection. + The function can then raise validation exceptions to halt the + process from continuing (where Python's built-in ``ValueError`` + and ``AssertionError`` exceptions are reasonable choices), or can + modify or replace the value before proceeding. The function should + otherwise return the given value. + + Note that a validator for a collection **cannot** issue a load of that + collection within the validation routine - this usage raises + an assertion to avoid recursion overflows. This is a reentrant + condition which is not supported. + + :param \*names: list of attribute names to be validated. + :param include_removes: if True, "remove" events will be + sent as well - the validation function must accept an additional + argument "is_remove" which will be a boolean. + + .. versionadded:: 0.7.7 + :param include_backrefs: defaults to ``True``; if ``False``, the + validation function will not emit if the originator is an attribute + event related via a backref. This can be used for bi-directional + :func:`.validates` usage where only one validator should emit per + attribute operation. + + .. versionadded:: 0.9.0 + + .. seealso:: + + :ref:`simple_validators` - usage examples for :func:`.validates` + + """ + include_removes = kw.pop('include_removes', False) + include_backrefs = kw.pop('include_backrefs', True) + + def wrap(fn): + fn.__sa_validators__ = names + fn.__sa_validation_opts__ = { + "include_removes": include_removes, + "include_backrefs": include_backrefs + } + return fn + return wrap + + +def _event_on_load(state, ctx): + instrumenting_mapper = state.manager.info[_INSTRUMENTOR] + if instrumenting_mapper._reconstructor: + instrumenting_mapper._reconstructor(state.obj()) + + +def _event_on_first_init(manager, cls): + """Initial mapper compilation trigger. + + instrumentation calls this one when InstanceState + is first generated, and is needed for legacy mutable + attributes to work. + """ + + instrumenting_mapper = manager.info.get(_INSTRUMENTOR) + if instrumenting_mapper: + if Mapper._new_mappers: + configure_mappers() + + +def _event_on_init(state, args, kwargs): + """Run init_instance hooks. + + This also includes mapper compilation, normally not needed + here but helps with some piecemeal configuration + scenarios (such as in the ORM tutorial). + + """ + + instrumenting_mapper = state.manager.info.get(_INSTRUMENTOR) + if instrumenting_mapper: + if Mapper._new_mappers: + configure_mappers() + if instrumenting_mapper._set_polymorphic_identity: + instrumenting_mapper._set_polymorphic_identity(state) + + +class _ColumnMapping(dict): + """Error reporting helper for mapper._columntoproperty.""" + + __slots__ = 'mapper', + + def __init__(self, mapper): + self.mapper = mapper + + def __missing__(self, column): + prop = self.mapper._props.get(column) + if prop: + raise orm_exc.UnmappedColumnError( + "Column '%s.%s' is not available, due to " + "conflicting property '%s':%r" % ( + column.table.name, column.name, column.key, prop)) + raise orm_exc.UnmappedColumnError( + "No column %s is configured on mapper %s..." % + (column, self.mapper)) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/path_registry.py b/venv/Lib/site-packages/sqlalchemy/orm/path_registry.py new file mode 100644 index 0000000..bb4e2ed --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/path_registry.py @@ -0,0 +1,271 @@ +# orm/path_registry.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +"""Path tracking utilities, representing mapper graph traversals. + +""" + +from .. import inspection +from .. import util +from .. import exc +from itertools import chain +from .base import class_mapper +import logging + +log = logging.getLogger(__name__) + + +def _unreduce_path(path): + return PathRegistry.deserialize(path) + + +_WILDCARD_TOKEN = "*" +_DEFAULT_TOKEN = "_sa_default" + + +class PathRegistry(object): + """Represent query load paths and registry functions. + + Basically represents structures like: + + (<User mapper>, "orders", <Order mapper>, "items", <Item mapper>) + + These structures are generated by things like + query options (joinedload(), subqueryload(), etc.) and are + used to compose keys stored in the query._attributes dictionary + for various options. + + They are then re-composed at query compile/result row time as + the query is formed and as rows are fetched, where they again + serve to compose keys to look up options in the context.attributes + dictionary, which is copied from query._attributes. + + The path structure has a limited amount of caching, where each + "root" ultimately pulls from a fixed registry associated with + the first mapper, that also contains elements for each of its + property keys. However paths longer than two elements, which + are the exception rather than the rule, are generated on an + as-needed basis. + + """ + + is_token = False + is_root = False + + def __eq__(self, other): + return other is not None and \ + self.path == other.path + + def set(self, attributes, key, value): + log.debug("set '%s' on path '%s' to '%s'", key, self, value) + attributes[(key, self.path)] = value + + def setdefault(self, attributes, key, value): + log.debug("setdefault '%s' on path '%s' to '%s'", key, self, value) + attributes.setdefault((key, self.path), value) + + def get(self, attributes, key, value=None): + key = (key, self.path) + if key in attributes: + return attributes[key] + else: + return value + + def __len__(self): + return len(self.path) + + @property + def length(self): + return len(self.path) + + def pairs(self): + path = self.path + for i in range(0, len(path), 2): + yield path[i], path[i + 1] + + def contains_mapper(self, mapper): + for path_mapper in [ + self.path[i] for i in range(0, len(self.path), 2) + ]: + if path_mapper.is_mapper and \ + path_mapper.isa(mapper): + return True + else: + return False + + def contains(self, attributes, key): + return (key, self.path) in attributes + + def __reduce__(self): + return _unreduce_path, (self.serialize(), ) + + def serialize(self): + path = self.path + return list(zip( + [m.class_ for m in [path[i] for i in range(0, len(path), 2)]], + [path[i].key for i in range(1, len(path), 2)] + [None] + )) + + @classmethod + def deserialize(cls, path): + if path is None: + return None + + p = tuple(chain(*[(class_mapper(mcls), + class_mapper(mcls).attrs[key] + if key is not None else None) + for mcls, key in path])) + if p and p[-1] is None: + p = p[0:-1] + return cls.coerce(p) + + @classmethod + def per_mapper(cls, mapper): + return EntityRegistry( + cls.root, mapper + ) + + @classmethod + def coerce(cls, raw): + return util.reduce(lambda prev, next: prev[next], raw, cls.root) + + def token(self, token): + if token.endswith(':' + _WILDCARD_TOKEN): + return TokenRegistry(self, token) + elif token.endswith(":" + _DEFAULT_TOKEN): + return TokenRegistry(self.root, token) + else: + raise exc.ArgumentError("invalid token: %s" % token) + + def __add__(self, other): + return util.reduce( + lambda prev, next: prev[next], + other.path, self) + + def __repr__(self): + return "%s(%r)" % (self.__class__.__name__, self.path, ) + + +class RootRegistry(PathRegistry): + """Root registry, defers to mappers so that + paths are maintained per-root-mapper. + + """ + path = () + has_entity = False + is_aliased_class = False + is_root = True + + def __getitem__(self, entity): + return entity._path_registry + +PathRegistry.root = RootRegistry() + + +class TokenRegistry(PathRegistry): + def __init__(self, parent, token): + self.token = token + self.parent = parent + self.path = parent.path + (token,) + + has_entity = False + + is_token = True + + def generate_for_superclasses(self): + if not self.parent.is_aliased_class and not self.parent.is_root: + for ent in self.parent.mapper.iterate_to_root(): + yield TokenRegistry(self.parent.parent[ent], self.token) + else: + yield self + + def __getitem__(self, entity): + raise NotImplementedError() + + +class PropRegistry(PathRegistry): + def __init__(self, parent, prop): + # restate this path in terms of the + # given MapperProperty's parent. + insp = inspection.inspect(parent[-1]) + if not insp.is_aliased_class or insp._use_mapper_path: + parent = parent.parent[prop.parent] + elif insp.is_aliased_class and insp.with_polymorphic_mappers: + if prop.parent is not insp.mapper and \ + prop.parent in insp.with_polymorphic_mappers: + subclass_entity = parent[-1]._entity_for_mapper(prop.parent) + parent = parent.parent[subclass_entity] + + self.prop = prop + self.parent = parent + self.path = parent.path + (prop,) + + self._wildcard_path_loader_key = ( + "loader", + self.parent.path + self.prop._wildcard_token + ) + self._default_path_loader_key = self.prop._default_path_loader_key + self._loader_key = ("loader", self.path) + + def __str__(self): + return " -> ".join( + str(elem) for elem in self.path + ) + + @util.memoized_property + def has_entity(self): + return hasattr(self.prop, "mapper") + + @util.memoized_property + def entity(self): + return self.prop.mapper + + @property + def mapper(self): + return self.entity + + @property + def entity_path(self): + return self[self.entity] + + def __getitem__(self, entity): + if isinstance(entity, (int, slice)): + return self.path[entity] + else: + return EntityRegistry( + self, entity + ) + + +class EntityRegistry(PathRegistry, dict): + is_aliased_class = False + has_entity = True + + def __init__(self, parent, entity): + self.key = entity + self.parent = parent + self.is_aliased_class = entity.is_aliased_class + self.entity = entity + self.path = parent.path + (entity,) + self.entity_path = self + + @property + def mapper(self): + return inspection.inspect(self.entity).mapper + + def __bool__(self): + return True + __nonzero__ = __bool__ + + def __getitem__(self, entity): + if isinstance(entity, (int, slice)): + return self.path[entity] + else: + return dict.__getitem__(self, entity) + + def __missing__(self, key): + self[key] = item = PropRegistry(self, key) + return item diff --git a/venv/Lib/site-packages/sqlalchemy/orm/persistence.py b/venv/Lib/site-packages/sqlalchemy/orm/persistence.py new file mode 100644 index 0000000..2311cb4 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/persistence.py @@ -0,0 +1,1627 @@ +# orm/persistence.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""private module containing functions used to emit INSERT, UPDATE +and DELETE statements on behalf of a :class:`.Mapper` and its descending +mappers. + +The functions here are called only by the unit of work functions +in unitofwork.py. + +""" + +import operator +from itertools import groupby, chain +from .. import sql, util, exc as sa_exc +from . import attributes, sync, exc as orm_exc, evaluator +from .base import state_str, _entity_descriptor +from ..sql import expression +from ..sql.base import _from_objects +from . import loading + + +def _bulk_insert( + mapper, mappings, session_transaction, isstates, return_defaults, + render_nulls): + base_mapper = mapper.base_mapper + + cached_connections = _cached_connection_dict(base_mapper) + + if session_transaction.session.connection_callable: + raise NotImplementedError( + "connection_callable / per-instance sharding " + "not supported in bulk_insert()") + + if isstates: + if return_defaults: + states = [(state, state.dict) for state in mappings] + mappings = [dict_ for (state, dict_) in states] + else: + mappings = [state.dict for state in mappings] + else: + mappings = list(mappings) + + connection = session_transaction.connection(base_mapper) + for table, super_mapper in base_mapper._sorted_tables.items(): + if not mapper.isa(super_mapper): + continue + + records = ( + (None, state_dict, params, mapper, + connection, value_params, has_all_pks, has_all_defaults) + for + state, state_dict, params, mp, + conn, value_params, has_all_pks, + has_all_defaults in _collect_insert_commands(table, ( + (None, mapping, mapper, connection) + for mapping in mappings), + bulk=True, return_defaults=return_defaults, + render_nulls=render_nulls + ) + ) + _emit_insert_statements(base_mapper, None, + cached_connections, + super_mapper, table, records, + bookkeeping=return_defaults) + + if return_defaults and isstates: + identity_cls = mapper._identity_class + identity_props = [p.key for p in mapper._identity_key_props] + for state, dict_ in states: + state.key = ( + identity_cls, + tuple([dict_[key] for key in identity_props]) + ) + + +def _bulk_update(mapper, mappings, session_transaction, + isstates, update_changed_only): + base_mapper = mapper.base_mapper + + cached_connections = _cached_connection_dict(base_mapper) + + search_keys = mapper._primary_key_propkeys + if mapper._version_id_prop: + search_keys = {mapper._version_id_prop.key}.union(search_keys) + + def _changed_dict(mapper, state): + return dict( + (k, v) + for k, v in state.dict.items() if k in state.committed_state or k + in search_keys + + ) + + if isstates: + if update_changed_only: + mappings = [_changed_dict(mapper, state) for state in mappings] + else: + mappings = [state.dict for state in mappings] + else: + mappings = list(mappings) + + if session_transaction.session.connection_callable: + raise NotImplementedError( + "connection_callable / per-instance sharding " + "not supported in bulk_update()") + + connection = session_transaction.connection(base_mapper) + + for table, super_mapper in base_mapper._sorted_tables.items(): + if not mapper.isa(super_mapper): + continue + + records = _collect_update_commands(None, table, ( + (None, mapping, mapper, connection, + (mapping[mapper._version_id_prop.key] + if mapper._version_id_prop else None)) + for mapping in mappings + ), bulk=True) + + _emit_update_statements(base_mapper, None, + cached_connections, + super_mapper, table, records, + bookkeeping=False) + + +def save_obj( + base_mapper, states, uowtransaction, single=False): + """Issue ``INSERT`` and/or ``UPDATE`` statements for a list + of objects. + + This is called within the context of a UOWTransaction during a + flush operation, given a list of states to be flushed. The + base mapper in an inheritance hierarchy handles the inserts/ + updates for all descendant mappers. + + """ + + # if batch=false, call _save_obj separately for each object + if not single and not base_mapper.batch: + for state in _sort_states(states): + save_obj(base_mapper, [state], uowtransaction, single=True) + return + + states_to_update = [] + states_to_insert = [] + cached_connections = _cached_connection_dict(base_mapper) + + for (state, dict_, mapper, connection, + has_identity, + row_switch, update_version_id) in _organize_states_for_save( + base_mapper, states, uowtransaction + ): + if has_identity or row_switch: + states_to_update.append( + (state, dict_, mapper, connection, update_version_id) + ) + else: + states_to_insert.append( + (state, dict_, mapper, connection) + ) + + for table, mapper in base_mapper._sorted_tables.items(): + if table not in mapper._pks_by_table: + continue + insert = _collect_insert_commands(table, states_to_insert) + + update = _collect_update_commands( + uowtransaction, table, states_to_update) + + _emit_update_statements(base_mapper, uowtransaction, + cached_connections, + mapper, table, update) + + _emit_insert_statements(base_mapper, uowtransaction, + cached_connections, + mapper, table, insert) + + _finalize_insert_update_commands( + base_mapper, uowtransaction, + chain( + ( + (state, state_dict, mapper, connection, False) + for state, state_dict, mapper, connection in states_to_insert + ), + ( + (state, state_dict, mapper, connection, True) + for state, state_dict, mapper, connection, + update_version_id in states_to_update + ) + ) + ) + + +def post_update(base_mapper, states, uowtransaction, post_update_cols): + """Issue UPDATE statements on behalf of a relationship() which + specifies post_update. + + """ + cached_connections = _cached_connection_dict(base_mapper) + + states_to_update = list(_organize_states_for_post_update( + base_mapper, + states, uowtransaction)) + + for table, mapper in base_mapper._sorted_tables.items(): + if table not in mapper._pks_by_table: + continue + + update = ( + ( + state, state_dict, sub_mapper, connection, + mapper._get_committed_state_attr_by_column( + state, state_dict, mapper.version_id_col + ) if mapper.version_id_col is not None else None + ) + for + state, state_dict, sub_mapper, connection in states_to_update + if table in sub_mapper._pks_by_table + ) + + update = _collect_post_update_commands( + base_mapper, uowtransaction, + table, update, + post_update_cols + ) + + _emit_post_update_statements(base_mapper, uowtransaction, + cached_connections, + mapper, table, update) + + +def delete_obj(base_mapper, states, uowtransaction): + """Issue ``DELETE`` statements for a list of objects. + + This is called within the context of a UOWTransaction during a + flush operation. + + """ + + cached_connections = _cached_connection_dict(base_mapper) + + states_to_delete = list(_organize_states_for_delete( + base_mapper, + states, + uowtransaction)) + + table_to_mapper = base_mapper._sorted_tables + + for table in reversed(list(table_to_mapper.keys())): + mapper = table_to_mapper[table] + if table not in mapper._pks_by_table: + continue + elif mapper.inherits and mapper.passive_deletes: + continue + + delete = _collect_delete_commands(base_mapper, uowtransaction, + table, states_to_delete) + + _emit_delete_statements(base_mapper, uowtransaction, + cached_connections, mapper, table, delete) + + for state, state_dict, mapper, connection, \ + update_version_id in states_to_delete: + mapper.dispatch.after_delete(mapper, connection, state) + + +def _organize_states_for_save(base_mapper, states, uowtransaction): + """Make an initial pass across a set of states for INSERT or + UPDATE. + + This includes splitting out into distinct lists for + each, calling before_insert/before_update, obtaining + key information for each state including its dictionary, + mapper, the connection to use for the execution per state, + and the identity flag. + + """ + + for state, dict_, mapper, connection in _connections_for_states( + base_mapper, uowtransaction, + states): + + has_identity = bool(state.key) + + instance_key = state.key or mapper._identity_key_from_state(state) + + row_switch = update_version_id = None + + # call before_XXX extensions + if not has_identity: + mapper.dispatch.before_insert(mapper, connection, state) + else: + mapper.dispatch.before_update(mapper, connection, state) + + if mapper._validate_polymorphic_identity: + mapper._validate_polymorphic_identity(mapper, state, dict_) + + # detect if we have a "pending" instance (i.e. has + # no instance_key attached to it), and another instance + # with the same identity key already exists as persistent. + # convert to an UPDATE if so. + if not has_identity and \ + instance_key in uowtransaction.session.identity_map: + instance = \ + uowtransaction.session.identity_map[instance_key] + existing = attributes.instance_state(instance) + + if not uowtransaction.was_already_deleted(existing): + if not uowtransaction.is_deleted(existing): + raise orm_exc.FlushError( + "New instance %s with identity key %s conflicts " + "with persistent instance %s" % + (state_str(state), instance_key, + state_str(existing))) + + base_mapper._log_debug( + "detected row switch for identity %s. " + "will update %s, remove %s from " + "transaction", instance_key, + state_str(state), state_str(existing)) + + # remove the "delete" flag from the existing element + uowtransaction.remove_state_actions(existing) + row_switch = existing + + if (has_identity or row_switch) and mapper.version_id_col is not None: + update_version_id = mapper._get_committed_state_attr_by_column( + row_switch if row_switch else state, + row_switch.dict if row_switch else dict_, + mapper.version_id_col) + + yield (state, dict_, mapper, connection, + has_identity, row_switch, update_version_id) + + +def _organize_states_for_post_update(base_mapper, states, + uowtransaction): + """Make an initial pass across a set of states for UPDATE + corresponding to post_update. + + This includes obtaining key information for each state + including its dictionary, mapper, the connection to use for + the execution per state. + + """ + return _connections_for_states(base_mapper, uowtransaction, states) + + +def _organize_states_for_delete(base_mapper, states, uowtransaction): + """Make an initial pass across a set of states for DELETE. + + This includes calling out before_delete and obtaining + key information for each state including its dictionary, + mapper, the connection to use for the execution per state. + + """ + for state, dict_, mapper, connection in _connections_for_states( + base_mapper, uowtransaction, + states): + + mapper.dispatch.before_delete(mapper, connection, state) + + if mapper.version_id_col is not None: + update_version_id = \ + mapper._get_committed_state_attr_by_column( + state, dict_, + mapper.version_id_col) + else: + update_version_id = None + + yield ( + state, dict_, mapper, connection, update_version_id) + + +def _collect_insert_commands( + table, states_to_insert, + bulk=False, return_defaults=False, render_nulls=False): + """Identify sets of values to use in INSERT statements for a + list of states. + + """ + for state, state_dict, mapper, connection in states_to_insert: + if table not in mapper._pks_by_table: + continue + + params = {} + value_params = {} + + propkey_to_col = mapper._propkey_to_col[table] + + eval_none = mapper._insert_cols_evaluating_none[table] + + for propkey in set(propkey_to_col).intersection(state_dict): + value = state_dict[propkey] + col = propkey_to_col[propkey] + if value is None and col not in eval_none and not render_nulls: + continue + elif not bulk and hasattr(value, '__clause_element__') or \ + isinstance(value, sql.ClauseElement): + value_params[col.key] = value.__clause_element__() \ + if hasattr(value, '__clause_element__') else value + else: + params[col.key] = value + + if not bulk: + # for all the columns that have no default and we don't have + # a value and where "None" is not a special value, add + # explicit None to the INSERT. This is a legacy behavior + # which might be worth removing, as it should not be necessary + # and also produces confusion, given that "missing" and None + # now have distinct meanings + for colkey in mapper._insert_cols_as_none[table].\ + difference(params).difference(value_params): + params[colkey] = None + + if not bulk or return_defaults: + # params are in terms of Column key objects, so + # compare to pk_keys_by_table + has_all_pks = mapper._pk_keys_by_table[table].issubset(params) + + if mapper.base_mapper.eager_defaults: + has_all_defaults = mapper._server_default_cols[table].\ + issubset(params) + else: + has_all_defaults = True + else: + has_all_defaults = has_all_pks = True + + if mapper.version_id_generator is not False \ + and mapper.version_id_col is not None and \ + mapper.version_id_col in mapper._cols_by_table[table]: + params[mapper.version_id_col.key] = \ + mapper.version_id_generator(None) + + yield ( + state, state_dict, params, mapper, + connection, value_params, has_all_pks, + has_all_defaults) + + +def _collect_update_commands( + uowtransaction, table, states_to_update, + bulk=False): + """Identify sets of values to use in UPDATE statements for a + list of states. + + This function works intricately with the history system + to determine exactly what values should be updated + as well as how the row should be matched within an UPDATE + statement. Includes some tricky scenarios where the primary + key of an object might have been changed. + + """ + + for state, state_dict, mapper, connection, \ + update_version_id in states_to_update: + + if table not in mapper._pks_by_table: + continue + + pks = mapper._pks_by_table[table] + + value_params = {} + + propkey_to_col = mapper._propkey_to_col[table] + + if bulk: + # keys here are mapped attrbute keys, so + # look at mapper attribute keys for pk + params = dict( + (propkey_to_col[propkey].key, state_dict[propkey]) + for propkey in + set(propkey_to_col).intersection(state_dict).difference( + mapper._pk_attr_keys_by_table[table]) + ) + has_all_defaults = True + else: + params = {} + for propkey in set(propkey_to_col).intersection( + state.committed_state): + value = state_dict[propkey] + col = propkey_to_col[propkey] + + if hasattr(value, '__clause_element__') or \ + isinstance(value, sql.ClauseElement): + value_params[col] = value.__clause_element__() \ + if hasattr(value, '__clause_element__') else value + # guard against values that generate non-__nonzero__ + # objects for __eq__() + elif state.manager[propkey].impl.is_equal( + value, state.committed_state[propkey]) is not True: + params[col.key] = value + + if mapper.base_mapper.eager_defaults: + has_all_defaults = mapper._server_onupdate_default_cols[table].\ + issubset(params) + else: + has_all_defaults = True + + if update_version_id is not None and \ + mapper.version_id_col in mapper._cols_by_table[table]: + + if not bulk and not (params or value_params): + # HACK: check for history in other tables, in case the + # history is only in a different table than the one + # where the version_id_col is. This logic was lost + # from 0.9 -> 1.0.0 and restored in 1.0.6. + for prop in mapper._columntoproperty.values(): + history = ( + state.manager[prop.key].impl.get_history( + state, state_dict, + attributes.PASSIVE_NO_INITIALIZE)) + if history.added: + break + else: + # no net change, break + continue + + col = mapper.version_id_col + no_params = not params and not value_params + params[col._label] = update_version_id + + if (bulk or col.key not in params) and \ + mapper.version_id_generator is not False: + val = mapper.version_id_generator(update_version_id) + params[col.key] = val + elif mapper.version_id_generator is False and no_params: + # no version id generator, no values set on the table, + # and version id wasn't manually incremented. + # set version id to itself so we get an UPDATE + # statement + params[col.key] = update_version_id + + elif not (params or value_params): + continue + + has_all_pks = True + if bulk: + # keys here are mapped attrbute keys, so + # look at mapper attribute keys for pk + pk_params = dict( + (propkey_to_col[propkey]._label, state_dict.get(propkey)) + for propkey in + set(propkey_to_col). + intersection(mapper._pk_attr_keys_by_table[table]) + ) + else: + pk_params = {} + for col in pks: + propkey = mapper._columntoproperty[col].key + + history = state.manager[propkey].impl.get_history( + state, state_dict, attributes.PASSIVE_OFF) + + if history.added: + if not history.deleted or \ + ("pk_cascaded", state, col) in \ + uowtransaction.attributes: + pk_params[col._label] = history.added[0] + params.pop(col.key, None) + else: + # else, use the old value to locate the row + pk_params[col._label] = history.deleted[0] + if col in value_params: + has_all_pks = False + else: + pk_params[col._label] = history.unchanged[0] + if pk_params[col._label] is None: + raise orm_exc.FlushError( + "Can't update table %s using NULL for primary " + "key value on column %s" % (table, col)) + + if params or value_params: + params.update(pk_params) + yield ( + state, state_dict, params, mapper, + connection, value_params, has_all_defaults, has_all_pks) + + +def _collect_post_update_commands(base_mapper, uowtransaction, table, + states_to_update, post_update_cols): + """Identify sets of values to use in UPDATE statements for a + list of states within a post_update operation. + + """ + + for state, state_dict, mapper, connection, \ + update_version_id in states_to_update: + + # assert table in mapper._pks_by_table + + pks = mapper._pks_by_table[table] + params = {} + hasdata = False + + for col in mapper._cols_by_table[table]: + if col in pks: + params[col._label] = \ + mapper._get_state_attr_by_column( + state, + state_dict, col, passive=attributes.PASSIVE_OFF) + + elif col in post_update_cols or col.onupdate is not None: + prop = mapper._columntoproperty[col] + history = state.manager[prop.key].impl.get_history( + state, state_dict, + attributes.PASSIVE_NO_INITIALIZE) + if history.added: + value = history.added[0] + params[col.key] = value + hasdata = True + if hasdata: + if update_version_id is not None and \ + mapper.version_id_col in mapper._cols_by_table[table]: + + col = mapper.version_id_col + params[col._label] = update_version_id + + if bool(state.key) and col.key not in params and \ + mapper.version_id_generator is not False: + val = mapper.version_id_generator(update_version_id) + params[col.key] = val + yield state, state_dict, mapper, connection, params + + +def _collect_delete_commands(base_mapper, uowtransaction, table, + states_to_delete): + """Identify values to use in DELETE statements for a list of + states to be deleted.""" + + for state, state_dict, mapper, connection, \ + update_version_id in states_to_delete: + + if table not in mapper._pks_by_table: + continue + + params = {} + for col in mapper._pks_by_table[table]: + params[col.key] = \ + value = \ + mapper._get_committed_state_attr_by_column( + state, state_dict, col) + if value is None: + raise orm_exc.FlushError( + "Can't delete from table %s " + "using NULL for primary " + "key value on column %s" % (table, col)) + + if update_version_id is not None and \ + mapper.version_id_col in mapper._cols_by_table[table]: + params[mapper.version_id_col.key] = update_version_id + yield params, connection + + +def _emit_update_statements(base_mapper, uowtransaction, + cached_connections, mapper, table, update, + bookkeeping=True): + """Emit UPDATE statements corresponding to value lists collected + by _collect_update_commands().""" + + needs_version_id = mapper.version_id_col is not None and \ + mapper.version_id_col in mapper._cols_by_table[table] + + def update_stmt(): + clause = sql.and_() + + for col in mapper._pks_by_table[table]: + clause.clauses.append(col == sql.bindparam(col._label, + type_=col.type)) + + if needs_version_id: + clause.clauses.append( + mapper.version_id_col == sql.bindparam( + mapper.version_id_col._label, + type_=mapper.version_id_col.type)) + + stmt = table.update(clause) + return stmt + + cached_stmt = base_mapper._memo(('update', table), update_stmt) + + for (connection, paramkeys, hasvalue, has_all_defaults, has_all_pks), \ + records in groupby( + update, + lambda rec: ( + rec[4], # connection + set(rec[2]), # set of parameter keys + bool(rec[5]), # whether or not we have "value" parameters + rec[6], # has_all_defaults + rec[7] # has all pks + ) + ): + rows = 0 + records = list(records) + + statement = cached_stmt + return_defaults = False + + if not has_all_pks: + statement = statement.return_defaults() + return_defaults = True + elif bookkeeping and not has_all_defaults and \ + mapper.base_mapper.eager_defaults: + statement = statement.return_defaults() + return_defaults = True + elif mapper.version_id_col is not None: + statement = statement.return_defaults(mapper.version_id_col) + return_defaults = True + + assert_singlerow = ( + connection.dialect.supports_sane_rowcount + if not return_defaults + else connection.dialect.supports_sane_rowcount_returning + ) + + assert_multirow = assert_singlerow and \ + connection.dialect.supports_sane_multi_rowcount + allow_multirow = has_all_defaults and not needs_version_id + + if hasvalue: + for state, state_dict, params, mapper, \ + connection, value_params, \ + has_all_defaults, has_all_pks in records: + c = connection.execute( + statement.values(value_params), + params) + if bookkeeping: + _postfetch( + mapper, + uowtransaction, + table, + state, + state_dict, + c, + c.context.compiled_parameters[0], + value_params) + rows += c.rowcount + check_rowcount = assert_singlerow + else: + if not allow_multirow: + check_rowcount = assert_singlerow + for state, state_dict, params, mapper, \ + connection, value_params, has_all_defaults, \ + has_all_pks in records: + c = cached_connections[connection].\ + execute(statement, params) + + # TODO: why with bookkeeping=False? + if bookkeeping: + _postfetch( + mapper, + uowtransaction, + table, + state, + state_dict, + c, + c.context.compiled_parameters[0], + value_params) + rows += c.rowcount + else: + multiparams = [rec[2] for rec in records] + + check_rowcount = assert_multirow or ( + assert_singlerow and + len(multiparams) == 1 + ) + + c = cached_connections[connection].\ + execute(statement, multiparams) + + rows += c.rowcount + + for state, state_dict, params, mapper, \ + connection, value_params, \ + has_all_defaults, has_all_pks in records: + if bookkeeping: + _postfetch( + mapper, + uowtransaction, + table, + state, + state_dict, + c, + c.context.compiled_parameters[0], + value_params) + + if check_rowcount: + if rows != len(records): + raise orm_exc.StaleDataError( + "UPDATE statement on table '%s' expected to " + "update %d row(s); %d were matched." % + (table.description, len(records), rows)) + + elif needs_version_id: + util.warn("Dialect %s does not support updated rowcount " + "- versioning cannot be verified." % + c.dialect.dialect_description) + + +def _emit_insert_statements(base_mapper, uowtransaction, + cached_connections, mapper, table, insert, + bookkeeping=True): + """Emit INSERT statements corresponding to value lists collected + by _collect_insert_commands().""" + + cached_stmt = base_mapper._memo(('insert', table), table.insert) + + for (connection, pkeys, hasvalue, has_all_pks, has_all_defaults), \ + records in groupby( + insert, + lambda rec: ( + rec[4], # connection + set(rec[2]), # parameter keys + bool(rec[5]), # whether we have "value" parameters + rec[6], + rec[7])): + + statement = cached_stmt + + if not bookkeeping or \ + ( + has_all_defaults + or not base_mapper.eager_defaults + or not connection.dialect.implicit_returning + ) and has_all_pks and not hasvalue: + + records = list(records) + multiparams = [rec[2] for rec in records] + + c = cached_connections[connection].\ + execute(statement, multiparams) + + if bookkeeping: + for (state, state_dict, params, mapper_rec, + conn, value_params, has_all_pks, has_all_defaults), \ + last_inserted_params in \ + zip(records, c.context.compiled_parameters): + if state: + _postfetch( + mapper_rec, + uowtransaction, + table, + state, + state_dict, + c, + last_inserted_params, + value_params) + else: + _postfetch_bulk_save(mapper_rec, state_dict, table) + + else: + if not has_all_defaults and base_mapper.eager_defaults: + statement = statement.return_defaults() + elif mapper.version_id_col is not None: + statement = statement.return_defaults(mapper.version_id_col) + + for state, state_dict, params, mapper_rec, \ + connection, value_params, \ + has_all_pks, has_all_defaults in records: + + if value_params: + result = connection.execute( + statement.values(value_params), + params) + else: + result = cached_connections[connection].\ + execute(statement, params) + + primary_key = result.context.inserted_primary_key + + if primary_key is not None: + # set primary key attributes + for pk, col in zip(primary_key, + mapper._pks_by_table[table]): + prop = mapper_rec._columntoproperty[col] + if state_dict.get(prop.key) is None: + state_dict[prop.key] = pk + if bookkeeping: + if state: + _postfetch( + mapper_rec, + uowtransaction, + table, + state, + state_dict, + result, + result.context.compiled_parameters[0], + value_params) + else: + _postfetch_bulk_save(mapper_rec, state_dict, table) + + +def _emit_post_update_statements(base_mapper, uowtransaction, + cached_connections, mapper, table, update): + """Emit UPDATE statements corresponding to value lists collected + by _collect_post_update_commands().""" + + needs_version_id = mapper.version_id_col is not None and \ + mapper.version_id_col in mapper._cols_by_table[table] + + def update_stmt(): + clause = sql.and_() + + for col in mapper._pks_by_table[table]: + clause.clauses.append(col == sql.bindparam(col._label, + type_=col.type)) + + if needs_version_id: + clause.clauses.append( + mapper.version_id_col == sql.bindparam( + mapper.version_id_col._label, + type_=mapper.version_id_col.type)) + + stmt = table.update(clause) + + if mapper.version_id_col is not None: + stmt = stmt.return_defaults(mapper.version_id_col) + + return stmt + + statement = base_mapper._memo(('post_update', table), update_stmt) + + # execute each UPDATE in the order according to the original + # list of states to guarantee row access order, but + # also group them into common (connection, cols) sets + # to support executemany(). + for key, records in groupby( + update, lambda rec: ( + rec[3], # connection + set(rec[4]), # parameter keys + ) + ): + rows = 0 + + records = list(records) + connection = key[0] + + assert_singlerow = ( + connection.dialect.supports_sane_rowcount + if mapper.version_id_col is None + else connection.dialect.supports_sane_rowcount_returning + ) + assert_multirow = assert_singlerow and \ + connection.dialect.supports_sane_multi_rowcount + allow_multirow = not needs_version_id or assert_multirow + + + if not allow_multirow: + check_rowcount = assert_singlerow + for state, state_dict, mapper_rec, \ + connection, params in records: + c = cached_connections[connection].\ + execute(statement, params) + _postfetch_post_update( + mapper_rec, uowtransaction, table, state, state_dict, + c, c.context.compiled_parameters[0]) + rows += c.rowcount + else: + multiparams = [ + params for + state, state_dict, mapper_rec, conn, params in records] + + check_rowcount = assert_multirow or ( + assert_singlerow and + len(multiparams) == 1 + ) + + c = cached_connections[connection].\ + execute(statement, multiparams) + + rows += c.rowcount + for state, state_dict, mapper_rec, \ + connection, params in records: + _postfetch_post_update( + mapper_rec, uowtransaction, table, state, state_dict, + c, c.context.compiled_parameters[0]) + + if check_rowcount: + if rows != len(records): + raise orm_exc.StaleDataError( + "UPDATE statement on table '%s' expected to " + "update %d row(s); %d were matched." % + (table.description, len(records), rows)) + + elif needs_version_id: + util.warn("Dialect %s does not support updated rowcount " + "- versioning cannot be verified." % + c.dialect.dialect_description) + + +def _emit_delete_statements(base_mapper, uowtransaction, cached_connections, + mapper, table, delete): + """Emit DELETE statements corresponding to value lists collected + by _collect_delete_commands().""" + + need_version_id = mapper.version_id_col is not None and \ + mapper.version_id_col in mapper._cols_by_table[table] + + def delete_stmt(): + clause = sql.and_() + for col in mapper._pks_by_table[table]: + clause.clauses.append( + col == sql.bindparam(col.key, type_=col.type)) + + if need_version_id: + clause.clauses.append( + mapper.version_id_col == + sql.bindparam( + mapper.version_id_col.key, + type_=mapper.version_id_col.type + ) + ) + + return table.delete(clause) + + statement = base_mapper._memo(('delete', table), delete_stmt) + for connection, recs in groupby( + delete, + lambda rec: rec[1] # connection + ): + del_objects = [params for params, connection in recs] + + connection = cached_connections[connection] + + expected = len(del_objects) + rows_matched = -1 + only_warn = False + if connection.dialect.supports_sane_multi_rowcount: + c = connection.execute(statement, del_objects) + + if not need_version_id: + only_warn = True + + rows_matched = c.rowcount + + elif need_version_id: + if connection.dialect.supports_sane_rowcount: + rows_matched = 0 + # execute deletes individually so that versioned + # rows can be verified + for params in del_objects: + c = connection.execute(statement, params) + rows_matched += c.rowcount + else: + util.warn( + "Dialect %s does not support deleted rowcount " + "- versioning cannot be verified." % + connection.dialect.dialect_description, + stacklevel=12) + connection.execute(statement, del_objects) + else: + c = connection.execute(statement, del_objects) + + if not need_version_id: + only_warn = True + + rows_matched = c.rowcount + + if base_mapper.confirm_deleted_rows and \ + rows_matched > -1 and expected != rows_matched: + if only_warn: + util.warn( + "DELETE statement on table '%s' expected to " + "delete %d row(s); %d were matched. Please set " + "confirm_deleted_rows=False within the mapper " + "configuration to prevent this warning." % + (table.description, expected, rows_matched) + ) + else: + raise orm_exc.StaleDataError( + "DELETE statement on table '%s' expected to " + "delete %d row(s); %d were matched. Please set " + "confirm_deleted_rows=False within the mapper " + "configuration to prevent this warning." % + (table.description, expected, rows_matched) + ) + + +def _finalize_insert_update_commands(base_mapper, uowtransaction, states): + """finalize state on states that have been inserted or updated, + including calling after_insert/after_update events. + + """ + for state, state_dict, mapper, connection, has_identity in states: + + if mapper._readonly_props: + readonly = state.unmodified_intersection( + [ + p.key for p in mapper._readonly_props + if ( + p.expire_on_flush and + (not p.deferred or p.key in state.dict) + ) or ( + not p.expire_on_flush and + not p.deferred and p.key not in state.dict + ) + ] + ) + if readonly: + state._expire_attributes(state.dict, readonly) + + # if eager_defaults option is enabled, load + # all expired cols. Else if we have a version_id_col, make sure + # it isn't expired. + toload_now = [] + + if base_mapper.eager_defaults: + toload_now.extend( + state._unloaded_non_object.intersection( + mapper._server_default_plus_onupdate_propkeys) + ) + + if mapper.version_id_col is not None and \ + mapper.version_id_generator is False: + if mapper._version_id_prop.key in state.unloaded: + toload_now.extend([mapper._version_id_prop.key]) + + if toload_now: + state.key = base_mapper._identity_key_from_state(state) + loading.load_on_ident( + uowtransaction.session.query(mapper), + state.key, refresh_state=state, + only_load_props=toload_now) + + # call after_XXX extensions + if not has_identity: + mapper.dispatch.after_insert(mapper, connection, state) + else: + mapper.dispatch.after_update(mapper, connection, state) + + if mapper.version_id_generator is False and \ + mapper.version_id_col is not None: + if state_dict[mapper._version_id_prop.key] is None: + raise orm_exc.FlushError( + "Instance does not contain a non-NULL version value") + + +def _postfetch_post_update(mapper, uowtransaction, table, + state, dict_, result, params): + if uowtransaction.is_deleted(state): + return + + prefetch_cols = result.context.compiled.prefetch + postfetch_cols = result.context.compiled.postfetch + + if mapper.version_id_col is not None and \ + mapper.version_id_col in mapper._cols_by_table[table]: + prefetch_cols = list(prefetch_cols) + [mapper.version_id_col] + + refresh_flush = bool(mapper.class_manager.dispatch.refresh_flush) + if refresh_flush: + load_evt_attrs = [] + + for c in prefetch_cols: + if c.key in params and c in mapper._columntoproperty: + dict_[mapper._columntoproperty[c].key] = params[c.key] + if refresh_flush: + load_evt_attrs.append(mapper._columntoproperty[c].key) + + if refresh_flush and load_evt_attrs: + mapper.class_manager.dispatch.refresh_flush( + state, uowtransaction, load_evt_attrs) + + if postfetch_cols: + state._expire_attributes(state.dict, + [mapper._columntoproperty[c].key + for c in postfetch_cols if c in + mapper._columntoproperty] + ) + + +def _postfetch(mapper, uowtransaction, table, + state, dict_, result, params, value_params): + """Expire attributes in need of newly persisted database state, + after an INSERT or UPDATE statement has proceeded for that + state.""" + + prefetch_cols = result.context.compiled.prefetch + postfetch_cols = result.context.compiled.postfetch + returning_cols = result.context.compiled.returning + + if mapper.version_id_col is not None and \ + mapper.version_id_col in mapper._cols_by_table[table]: + prefetch_cols = list(prefetch_cols) + [mapper.version_id_col] + + refresh_flush = bool(mapper.class_manager.dispatch.refresh_flush) + if refresh_flush: + load_evt_attrs = [] + + if returning_cols: + row = result.context.returned_defaults + if row is not None: + for col in returning_cols: + # pk cols returned from insert are handled + # distinctly, don't step on the values here + if col.primary_key and result.context.isinsert: + continue + + # note that columns can be in the "return defaults" that are + # not mapped to this mapper, typically because they are + # "excluded", which can be specified directly or also occurs + # when using declarative w/ single table inheritance + prop = mapper._columntoproperty.get(col) + if prop: + dict_[prop.key] = row[col] + if refresh_flush: + load_evt_attrs.append(prop.key) + + for c in prefetch_cols: + if c.key in params and c in mapper._columntoproperty: + dict_[mapper._columntoproperty[c].key] = params[c.key] + if refresh_flush: + load_evt_attrs.append(mapper._columntoproperty[c].key) + + if refresh_flush and load_evt_attrs: + mapper.class_manager.dispatch.refresh_flush( + state, uowtransaction, load_evt_attrs) + + if postfetch_cols: + state._expire_attributes(state.dict, + [mapper._columntoproperty[c].key + for c in postfetch_cols if c in + mapper._columntoproperty] + ) + + # synchronize newly inserted ids from one table to the next + # TODO: this still goes a little too often. would be nice to + # have definitive list of "columns that changed" here + for m, equated_pairs in mapper._table_to_equated[table]: + sync.populate(state, m, state, m, + equated_pairs, + uowtransaction, + mapper.passive_updates) + + +def _postfetch_bulk_save(mapper, dict_, table): + for m, equated_pairs in mapper._table_to_equated[table]: + sync.bulk_populate_inherit_keys(dict_, m, equated_pairs) + + +def _connections_for_states(base_mapper, uowtransaction, states): + """Return an iterator of (state, state.dict, mapper, connection). + + The states are sorted according to _sort_states, then paired + with the connection they should be using for the given + unit of work transaction. + + """ + # if session has a connection callable, + # organize individual states with the connection + # to use for update + if uowtransaction.session.connection_callable: + connection_callable = \ + uowtransaction.session.connection_callable + else: + connection = uowtransaction.transaction.connection(base_mapper) + connection_callable = None + + for state in _sort_states(states): + if connection_callable: + connection = connection_callable(base_mapper, state.obj()) + + mapper = state.manager.mapper + + yield state, state.dict, mapper, connection + + +def _cached_connection_dict(base_mapper): + # dictionary of connection->connection_with_cache_options. + return util.PopulateDict( + lambda conn: conn.execution_options( + compiled_cache=base_mapper._compiled_cache + )) + + +def _sort_states(states): + pending = set(states) + persistent = set(s for s in pending if s.key is not None) + pending.difference_update(persistent) + return sorted(pending, key=operator.attrgetter("insert_order")) + \ + sorted(persistent, key=lambda q: q.key[1]) + + +class BulkUD(object): + """Handle bulk update and deletes via a :class:`.Query`.""" + + def __init__(self, query): + self.query = query.enable_eagerloads(False) + self.mapper = self.query._bind_mapper() + self._validate_query_state() + + def _validate_query_state(self): + for attr, methname, notset, op in ( + ('_limit', 'limit()', None, operator.is_), + ('_offset', 'offset()', None, operator.is_), + ('_order_by', 'order_by()', False, operator.is_), + ('_group_by', 'group_by()', False, operator.is_), + ('_distinct', 'distinct()', False, operator.is_), + ( + '_from_obj', + 'join(), outerjoin(), select_from(), or from_self()', + (), operator.eq) + ): + if not op(getattr(self.query, attr), notset): + raise sa_exc.InvalidRequestError( + "Can't call Query.update() or Query.delete() " + "when %s has been called" % + (methname, ) + ) + + @property + def session(self): + return self.query.session + + @classmethod + def _factory(cls, lookup, synchronize_session, *arg): + try: + klass = lookup[synchronize_session] + except KeyError: + raise sa_exc.ArgumentError( + "Valid strategies for session synchronization " + "are %s" % (", ".join(sorted(repr(x) + for x in lookup)))) + else: + return klass(*arg) + + def exec_(self): + self._do_pre() + self._do_pre_synchronize() + self._do_exec() + self._do_post_synchronize() + self._do_post() + + def _execute_stmt(self, stmt): + self.result = self.query.session.execute( + stmt, params=self.query._params, + mapper=self.mapper) + self.rowcount = self.result.rowcount + + @util.dependencies("sqlalchemy.orm.query") + def _do_pre(self, querylib): + query = self.query + self.context = querylib.QueryContext(query) + + if isinstance(query._entities[0], querylib._ColumnEntity): + # check for special case of query(table) + tables = set() + for ent in query._entities: + if not isinstance(ent, querylib._ColumnEntity): + tables.clear() + break + else: + tables.update(_from_objects(ent.column)) + + if len(tables) != 1: + raise sa_exc.InvalidRequestError( + "This operation requires only one Table or " + "entity be specified as the target." + ) + else: + self.primary_table = tables.pop() + + else: + self.primary_table = query._only_entity_zero( + "This operation requires only one Table or " + "entity be specified as the target." + ).mapper.local_table + + session = query.session + + if query._autoflush: + session._autoflush() + + def _do_pre_synchronize(self): + pass + + def _do_post_synchronize(self): + pass + + +class BulkEvaluate(BulkUD): + """BulkUD which does the 'evaluate' method of session state resolution.""" + + def _additional_evaluators(self, evaluator_compiler): + pass + + def _do_pre_synchronize(self): + query = self.query + target_cls = query._mapper_zero().class_ + + try: + evaluator_compiler = evaluator.EvaluatorCompiler(target_cls) + if query.whereclause is not None: + eval_condition = evaluator_compiler.process( + query.whereclause) + else: + def eval_condition(obj): + return True + + self._additional_evaluators(evaluator_compiler) + + except evaluator.UnevaluatableError as err: + raise sa_exc.InvalidRequestError( + 'Could not evaluate current criteria in Python: "%s". ' + 'Specify \'fetch\' or False for the ' + 'synchronize_session parameter.' % err) + + # TODO: detect when the where clause is a trivial primary key match + self.matched_objects = [ + obj for (cls, pk, identity_token), obj in + query.session.identity_map.items() + if issubclass(cls, target_cls) and + eval_condition(obj)] + + +class BulkFetch(BulkUD): + """BulkUD which does the 'fetch' method of session state resolution.""" + + def _do_pre_synchronize(self): + query = self.query + session = query.session + context = query._compile_context() + select_stmt = context.statement.with_only_columns( + self.primary_table.primary_key) + self.matched_rows = session.execute( + select_stmt, + mapper=self.mapper, + params=query._params).fetchall() + + +class BulkUpdate(BulkUD): + """BulkUD which handles UPDATEs.""" + + def __init__(self, query, values, update_kwargs): + super(BulkUpdate, self).__init__(query) + self.values = values + self.update_kwargs = update_kwargs + + @classmethod + def factory(cls, query, synchronize_session, values, update_kwargs): + return BulkUD._factory({ + "evaluate": BulkUpdateEvaluate, + "fetch": BulkUpdateFetch, + False: BulkUpdate + }, synchronize_session, query, values, update_kwargs) + + @property + def _resolved_values(self): + values = [] + for k, v in ( + self.values.items() if hasattr(self.values, 'items') + else self.values): + if self.mapper: + if isinstance(k, util.string_types): + desc = _entity_descriptor(self.mapper, k) + values.extend(desc._bulk_update_tuples(v)) + elif isinstance(k, attributes.QueryableAttribute): + values.extend(k._bulk_update_tuples(v)) + else: + values.append((k, v)) + else: + values.append((k, v)) + return values + + @property + def _resolved_values_keys_as_propnames(self): + values = [] + for k, v in self._resolved_values: + if isinstance(k, attributes.QueryableAttribute): + values.append((k.key, v)) + continue + elif hasattr(k, '__clause_element__'): + k = k.__clause_element__() + + if self.mapper and isinstance(k, expression.ColumnElement): + try: + attr = self.mapper._columntoproperty[k] + except orm_exc.UnmappedColumnError: + pass + else: + values.append((attr.key, v)) + else: + raise sa_exc.InvalidRequestError( + "Invalid expression type: %r" % k) + return values + + def _do_exec(self): + values = self._resolved_values + + if not self.update_kwargs.get('preserve_parameter_order', False): + values = dict(values) + + update_stmt = sql.update(self.primary_table, + self.context.whereclause, values, + **self.update_kwargs) + + self._execute_stmt(update_stmt) + + def _do_post(self): + session = self.query.session + session.dispatch.after_bulk_update(self) + + +class BulkDelete(BulkUD): + """BulkUD which handles DELETEs.""" + + def __init__(self, query): + super(BulkDelete, self).__init__(query) + + @classmethod + def factory(cls, query, synchronize_session): + return BulkUD._factory({ + "evaluate": BulkDeleteEvaluate, + "fetch": BulkDeleteFetch, + False: BulkDelete + }, synchronize_session, query) + + def _do_exec(self): + delete_stmt = sql.delete(self.primary_table, + self.context.whereclause) + + self._execute_stmt(delete_stmt) + + def _do_post(self): + session = self.query.session + session.dispatch.after_bulk_delete(self) + + +class BulkUpdateEvaluate(BulkEvaluate, BulkUpdate): + """BulkUD which handles UPDATEs using the "evaluate" + method of session resolution.""" + + def _additional_evaluators(self, evaluator_compiler): + self.value_evaluators = {} + values = self._resolved_values_keys_as_propnames + for key, value in values: + self.value_evaluators[key] = evaluator_compiler.process( + expression._literal_as_binds(value)) + + def _do_post_synchronize(self): + session = self.query.session + states = set() + evaluated_keys = list(self.value_evaluators.keys()) + for obj in self.matched_objects: + state, dict_ = attributes.instance_state(obj),\ + attributes.instance_dict(obj) + + # only evaluate unmodified attributes + to_evaluate = state.unmodified.intersection( + evaluated_keys) + for key in to_evaluate: + dict_[key] = self.value_evaluators[key](obj) + + state.manager.dispatch.refresh( + state, None, to_evaluate) + + state._commit(dict_, list(to_evaluate)) + + # expire attributes with pending changes + # (there was no autoflush, so they are overwritten) + state._expire_attributes(dict_, + set(evaluated_keys). + difference(to_evaluate)) + states.add(state) + session._register_altered(states) + + +class BulkDeleteEvaluate(BulkEvaluate, BulkDelete): + """BulkUD which handles DELETEs using the "evaluate" + method of session resolution.""" + + def _do_post_synchronize(self): + self.query.session._remove_newly_deleted( + [attributes.instance_state(obj) + for obj in self.matched_objects]) + + +class BulkUpdateFetch(BulkFetch, BulkUpdate): + """BulkUD which handles UPDATEs using the "fetch" + method of session resolution.""" + + def _do_post_synchronize(self): + session = self.query.session + target_mapper = self.query._mapper_zero() + + states = set([ + attributes.instance_state(session.identity_map[identity_key]) + for identity_key in [ + target_mapper.identity_key_from_primary_key( + list(primary_key)) + for primary_key in self.matched_rows + ] + if identity_key in session.identity_map + ]) + + values = self._resolved_values_keys_as_propnames + attrib = set(k for k, v in values) + for state in states: + to_expire = attrib.intersection(state.dict) + if to_expire: + session._expire_state(state, to_expire) + session._register_altered(states) + + +class BulkDeleteFetch(BulkFetch, BulkDelete): + """BulkUD which handles DELETEs using the "fetch" + method of session resolution.""" + + def _do_post_synchronize(self): + session = self.query.session + target_mapper = self.query._mapper_zero() + for primary_key in self.matched_rows: + # TODO: inline this and call remove_newly_deleted + # once + identity_key = target_mapper.identity_key_from_primary_key( + list(primary_key)) + if identity_key in session.identity_map: + session._remove_newly_deleted( + [attributes.instance_state( + session.identity_map[identity_key] + )] + ) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/properties.py b/venv/Lib/site-packages/sqlalchemy/orm/properties.py new file mode 100644 index 0000000..360edc6 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/properties.py @@ -0,0 +1,277 @@ +# orm/properties.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""MapperProperty implementations. + +This is a private module which defines the behavior of invidual ORM- +mapped attributes. + +""" +from __future__ import absolute_import + +from .. import util, log +from ..sql import expression +from . import attributes +from .util import _orm_full_deannotate + +from .interfaces import PropComparator, StrategizedProperty + +__all__ = ['ColumnProperty', 'CompositeProperty', 'SynonymProperty', + 'ComparableProperty', 'RelationshipProperty'] + + +@log.class_logger +class ColumnProperty(StrategizedProperty): + """Describes an object attribute that corresponds to a table column. + + Public constructor is the :func:`.orm.column_property` function. + + """ + + strategy_wildcard_key = 'column' + + __slots__ = ( + '_orig_columns', 'columns', 'group', 'deferred', + 'instrument', 'comparator_factory', 'descriptor', 'extension', + 'active_history', 'expire_on_flush', 'info', 'doc', + 'strategy_key', '_creation_order', '_is_polymorphic_discriminator', + '_mapped_by_synonym', '_deferred_column_loader') + + def __init__(self, *columns, **kwargs): + r"""Provide a column-level property for use with a Mapper. + + Column-based properties can normally be applied to the mapper's + ``properties`` dictionary using the :class:`.Column` element directly. + Use this function when the given column is not directly present within + the mapper's selectable; examples include SQL expressions, functions, + and scalar SELECT queries. + + Columns that aren't present in the mapper's selectable won't be + persisted by the mapper and are effectively "read-only" attributes. + + :param \*cols: + list of Column objects to be mapped. + + :param active_history=False: + When ``True``, indicates that the "previous" value for a + scalar attribute should be loaded when replaced, if not + already loaded. Normally, history tracking logic for + simple non-primary-key scalar values only needs to be + aware of the "new" value in order to perform a flush. This + flag is available for applications that make use of + :func:`.attributes.get_history` or :meth:`.Session.is_modified` + which also need to know + the "previous" value of the attribute. + + .. versionadded:: 0.6.6 + + :param comparator_factory: a class which extends + :class:`.ColumnProperty.Comparator` which provides custom SQL + clause generation for comparison operations. + + :param group: + a group name for this property when marked as deferred. + + :param deferred: + when True, the column property is "deferred", meaning that + it does not load immediately, and is instead loaded when the + attribute is first accessed on an instance. See also + :func:`~sqlalchemy.orm.deferred`. + + :param doc: + optional string that will be applied as the doc on the + class-bound descriptor. + + :param expire_on_flush=True: + Disable expiry on flush. A column_property() which refers + to a SQL expression (and not a single table-bound column) + is considered to be a "read only" property; populating it + has no effect on the state of data, and it can only return + database state. For this reason a column_property()'s value + is expired whenever the parent object is involved in a + flush, that is, has any kind of "dirty" state within a flush. + Setting this parameter to ``False`` will have the effect of + leaving any existing value present after the flush proceeds. + Note however that the :class:`.Session` with default expiration + settings still expires + all attributes after a :meth:`.Session.commit` call, however. + + .. versionadded:: 0.7.3 + + :param info: Optional data dictionary which will be populated into the + :attr:`.MapperProperty.info` attribute of this object. + + .. versionadded:: 0.8 + + :param extension: + an + :class:`.AttributeExtension` + instance, or list of extensions, which will be prepended + to the list of attribute listeners for the resulting + descriptor placed on the class. + **Deprecated.** Please see :class:`.AttributeEvents`. + + """ + super(ColumnProperty, self).__init__() + self._orig_columns = [expression._labeled(c) for c in columns] + self.columns = [expression._labeled(_orm_full_deannotate(c)) + for c in columns] + self.group = kwargs.pop('group', None) + self.deferred = kwargs.pop('deferred', False) + self.instrument = kwargs.pop('_instrument', True) + self.comparator_factory = kwargs.pop('comparator_factory', + self.__class__.Comparator) + self.descriptor = kwargs.pop('descriptor', None) + self.extension = kwargs.pop('extension', None) + self.active_history = kwargs.pop('active_history', False) + self.expire_on_flush = kwargs.pop('expire_on_flush', True) + + if 'info' in kwargs: + self.info = kwargs.pop('info') + + if 'doc' in kwargs: + self.doc = kwargs.pop('doc') + else: + for col in reversed(self.columns): + doc = getattr(col, 'doc', None) + if doc is not None: + self.doc = doc + break + else: + self.doc = None + + if kwargs: + raise TypeError( + "%s received unexpected keyword argument(s): %s" % ( + self.__class__.__name__, + ', '.join(sorted(kwargs.keys())))) + + util.set_creation_order(self) + + self.strategy_key = ( + ("deferred", self.deferred), + ("instrument", self.instrument) + ) + + @util.dependencies("sqlalchemy.orm.state", "sqlalchemy.orm.strategies") + def _memoized_attr__deferred_column_loader(self, state, strategies): + return state.InstanceState._instance_level_callable_processor( + self.parent.class_manager, + strategies.LoadDeferredColumns(self.key), self.key) + + @property + def expression(self): + """Return the primary column or expression for this ColumnProperty. + + """ + return self.columns[0] + + def instrument_class(self, mapper): + if not self.instrument: + return + + attributes.register_descriptor( + mapper.class_, + self.key, + comparator=self.comparator_factory(self, mapper), + parententity=mapper, + doc=self.doc + ) + + def do_init(self): + super(ColumnProperty, self).do_init() + if len(self.columns) > 1 and \ + set(self.parent.primary_key).issuperset(self.columns): + util.warn( + ("On mapper %s, primary key column '%s' is being combined " + "with distinct primary key column '%s' in attribute '%s'. " + "Use explicit properties to give each column its own mapped " + "attribute name.") % (self.parent, self.columns[1], + self.columns[0], self.key)) + + def copy(self): + return ColumnProperty( + deferred=self.deferred, + group=self.group, + active_history=self.active_history, + *self.columns) + + def _getcommitted(self, state, dict_, column, + passive=attributes.PASSIVE_OFF): + return state.get_impl(self.key).\ + get_committed_value(state, dict_, passive=passive) + + def merge(self, session, source_state, source_dict, dest_state, + dest_dict, load, _recursive, _resolve_conflict_map): + if not self.instrument: + return + elif self.key in source_dict: + value = source_dict[self.key] + + if not load: + dest_dict[self.key] = value + else: + impl = dest_state.get_impl(self.key) + impl.set(dest_state, dest_dict, value, None) + elif dest_state.has_identity and self.key not in dest_dict: + dest_state._expire_attributes( + dest_dict, [self.key], no_loader=True) + + class Comparator(util.MemoizedSlots, PropComparator): + """Produce boolean, comparison, and other operators for + :class:`.ColumnProperty` attributes. + + See the documentation for :class:`.PropComparator` for a brief + overview. + + See also: + + :class:`.PropComparator` + + :class:`.ColumnOperators` + + :ref:`types_operators` + + :attr:`.TypeEngine.comparator_factory` + + """ + + __slots__ = '__clause_element__', 'info' + + def _memoized_method___clause_element__(self): + if self.adapter: + return self.adapter(self.prop.columns[0]) + else: + # no adapter, so we aren't aliased + # assert self._parententity is self._parentmapper + return self.prop.columns[0]._annotate({ + "parententity": self._parententity, + "parentmapper": self._parententity}) + + def _memoized_attr_info(self): + ce = self.__clause_element__() + try: + return ce.info + except AttributeError: + return self.prop.info + + def _fallback_getattr(self, key): + """proxy attribute access down to the mapped column. + + this allows user-defined comparison methods to be accessed. + """ + return getattr(self.__clause_element__(), key) + + def operate(self, op, *other, **kwargs): + return op(self.__clause_element__(), *other, **kwargs) + + def reverse_operate(self, op, other, **kwargs): + col = self.__clause_element__() + return op(col._bind_param(op, other), col, **kwargs) + + def __str__(self): + return str(self.parent.class_.__name__) + "." + self.key diff --git a/venv/Lib/site-packages/sqlalchemy/orm/query.py b/venv/Lib/site-packages/sqlalchemy/orm/query.py new file mode 100644 index 0000000..d14e34a --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/query.py @@ -0,0 +1,4351 @@ +# orm/query.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""The Query class and support. + +Defines the :class:`.Query` class, the central +construct used by the ORM to construct database queries. + +The :class:`.Query` class should not be confused with the +:class:`.Select` class, which defines database +SELECT operations at the SQL (non-ORM) level. ``Query`` differs from +``Select`` in that it returns ORM-mapped objects and interacts with an +ORM session, whereas the ``Select`` construct interacts directly with the +database to return iterable result sets. + +""" + +from itertools import chain + +from . import ( + attributes, interfaces, object_mapper, persistence, + exc as orm_exc, loading +) +from .base import _entity_descriptor, _is_aliased_class, \ + _is_mapped_class, _orm_columns, _generative, InspectionAttr +from .path_registry import PathRegistry +from .util import ( + AliasedClass, ORMAdapter, join as orm_join, with_parent, aliased, + _entity_corresponds_to +) +from .. import sql, util, log, exc as sa_exc, inspect, inspection +from ..sql.expression import _interpret_as_from +from ..sql import ( + util as sql_util, + expression, visitors +) +from ..sql.base import ColumnCollection +from . import properties + +__all__ = ['Query', 'QueryContext', 'aliased'] + + +_path_registry = PathRegistry.root + + +@inspection._self_inspects +@log.class_logger +class Query(object): + """ORM-level SQL construction object. + + :class:`.Query` is the source of all SELECT statements generated by the + ORM, both those formulated by end-user query operations as well as by + high level internal operations such as related collection loading. It + features a generative interface whereby successive calls return a new + :class:`.Query` object, a copy of the former with additional + criteria and options associated with it. + + :class:`.Query` objects are normally initially generated using the + :meth:`~.Session.query` method of :class:`.Session`, and in + less common cases by instantiating the :class:`.Query` directly and + associating with a :class:`.Session` using the :meth:`.Query.with_session` + method. + + For a full walkthrough of :class:`.Query` usage, see the + :ref:`ormtutorial_toplevel`. + + """ + + _only_return_tuples = False + _enable_eagerloads = True + _enable_assertions = True + _with_labels = False + _criterion = None + _yield_per = None + _order_by = False + _group_by = False + _having = None + _distinct = False + _prefixes = None + _suffixes = None + _offset = None + _limit = None + _for_update_arg = None + _statement = None + _correlate = frozenset() + _populate_existing = False + _invoke_all_eagers = True + _version_check = False + _autoflush = True + _only_load_props = None + _refresh_state = None + _refresh_identity_token = None + _from_obj = () + _join_entities = () + _select_from_entity = None + _mapper_adapter_map = {} + _filter_aliases = None + _from_obj_alias = None + _joinpath = _joinpoint = util.immutabledict() + _execution_options = util.immutabledict() + _params = util.immutabledict() + _attributes = util.immutabledict() + _with_options = () + _with_hints = () + _enable_single_crit = True + _orm_only_adapt = True + _orm_only_from_obj_alias = True + _current_path = _path_registry + _has_mapper_entities = False + + lazy_loaded_from = None + """An :class:`.InstanceState` that is using this :class:`.Query` for a + lazy load operation. + + This can be used for extensions like the horizontal sharding extension + as well as event handlers and custom mapper options to determine + when a query is being used to lazy load a relationship on an object. + + .. versionadded:: 1.2.9 + + """ + + def __init__(self, entities, session=None): + """Construct a :class:`.Query` directly. + + E.g.:: + + q = Query([User, Address], session=some_session) + + The above is equivalent to:: + + q = some_session.query(User, Address) + + :param entities: a sequence of entities and/or SQL expressions. + + :param session: a :class:`.Session` with which the :class:`.Query` + will be associated. Optional; a :class:`.Query` can be associated + with a :class:`.Session` generatively via the + :meth:`.Query.with_session` method as well. + + .. seealso:: + + :meth:`.Session.query` + + :meth:`.Query.with_session` + + """ + self.session = session + self._polymorphic_adapters = {} + self._set_entities(entities) + + def _set_entities(self, entities, entity_wrapper=None): + if entity_wrapper is None: + entity_wrapper = _QueryEntity + self._entities = [] + self._primary_entity = None + self._has_mapper_entities = False + + # 1. don't run util.to_list() or _set_entity_selectables + # if no entities were passed - major performance bottleneck + # from lazy loader implementation when it seeks to use Query + # class for an identity lookup, causes test_orm.py to fail + # with thousands of extra function calls, see issue #4228 + # for why this use had to be added + # 2. can't use classmethod on Query because session.query_cls + # is an arbitrary callable in some user recipes, not + # necessarily a class, so we don't have the class available. + # see issue #4256 + # 3. can't do "if entities is not None" because we usually get here + # from session.query() which takes in *entities. + # 4. can't do "if entities" because users make use of undocumented + # to_list() behavior here and they pass clause expressions that + # can't be evaluated as boolean. See issue #4269. + # 5. the empty tuple is a singleton in cPython, take advantage of this + # so that we can skip for the empty "*entities" case without using + # any Python overloadable operators. + # + if entities is not (): + for ent in util.to_list(entities): + entity_wrapper(self, ent) + + self._set_entity_selectables(self._entities) + + def _set_entity_selectables(self, entities): + self._mapper_adapter_map = d = self._mapper_adapter_map.copy() + + for ent in entities: + for entity in ent.entities: + if entity not in d: + ext_info = inspect(entity) + if not ext_info.is_aliased_class and \ + ext_info.mapper.with_polymorphic: + if ext_info.mapper.mapped_table not in \ + self._polymorphic_adapters: + self._mapper_loads_polymorphically_with( + ext_info.mapper, + sql_util.ColumnAdapter( + ext_info.selectable, + ext_info.mapper._equivalent_columns + ) + ) + aliased_adapter = None + elif ext_info.is_aliased_class: + aliased_adapter = ext_info._adapter + else: + aliased_adapter = None + + d[entity] = ( + ext_info, + aliased_adapter + ) + ent.setup_entity(*d[entity]) + + def _mapper_loads_polymorphically_with(self, mapper, adapter): + for m2 in mapper._with_polymorphic_mappers or [mapper]: + self._polymorphic_adapters[m2] = adapter + for m in m2.iterate_to_root(): + self._polymorphic_adapters[m.local_table] = adapter + + def _set_select_from(self, obj, set_base_alias): + fa = [] + select_from_alias = None + + for from_obj in obj: + info = inspect(from_obj) + if hasattr(info, 'mapper') and \ + (info.is_mapper or info.is_aliased_class): + self._select_from_entity = info + if set_base_alias and not info.is_aliased_class: + raise sa_exc.ArgumentError( + "A selectable (FromClause) instance is " + "expected when the base alias is being set.") + fa.append(info.selectable) + elif not info.is_selectable: + raise sa_exc.ArgumentError( + "argument is not a mapped class, mapper, " + "aliased(), or FromClause instance.") + else: + if isinstance(from_obj, expression.SelectBase): + from_obj = from_obj.alias() + if set_base_alias: + select_from_alias = from_obj + fa.append(from_obj) + + self._from_obj = tuple(fa) + + if set_base_alias and \ + len(self._from_obj) == 1 and \ + isinstance(select_from_alias, expression.Alias): + equivs = self.__all_equivs() + self._from_obj_alias = sql_util.ColumnAdapter( + self._from_obj[0], equivs) + elif set_base_alias and \ + len(self._from_obj) == 1 and \ + hasattr(info, "mapper") and \ + info.is_aliased_class: + self._from_obj_alias = info._adapter + + def _reset_polymorphic_adapter(self, mapper): + for m2 in mapper._with_polymorphic_mappers: + self._polymorphic_adapters.pop(m2, None) + for m in m2.iterate_to_root(): + self._polymorphic_adapters.pop(m.local_table, None) + + def _adapt_polymorphic_element(self, element): + if "parententity" in element._annotations: + search = element._annotations['parententity'] + alias = self._polymorphic_adapters.get(search, None) + if alias: + return alias.adapt_clause(element) + + if isinstance(element, expression.FromClause): + search = element + elif hasattr(element, 'table'): + search = element.table + else: + return None + + alias = self._polymorphic_adapters.get(search, None) + if alias: + return alias.adapt_clause(element) + + def _adapt_col_list(self, cols): + return [ + self._adapt_clause( + expression._literal_as_label_reference(o), + True, True) + for o in cols + ] + + @_generative() + def _set_lazyload_from(self, state): + self.lazy_loaded_from = state + + @_generative() + def _adapt_all_clauses(self): + self._orm_only_adapt = False + + def _adapt_clause(self, clause, as_filter, orm_only): + """Adapt incoming clauses to transformations which + have been applied within this query.""" + + adapters = [] + # do we adapt all expression elements or only those + # tagged as 'ORM' constructs ? + if not self._orm_only_adapt: + orm_only = False + + if as_filter and self._filter_aliases: + for fa in self._filter_aliases._visitor_iterator: + adapters.append( + ( + orm_only, fa.replace + ) + ) + + if self._from_obj_alias: + # for the "from obj" alias, apply extra rule to the + # 'ORM only' check, if this query were generated from a + # subquery of itself, i.e. _from_selectable(), apply adaption + # to all SQL constructs. + adapters.append( + ( + orm_only if self._orm_only_from_obj_alias else False, + self._from_obj_alias.replace + ) + ) + + if self._polymorphic_adapters: + adapters.append( + ( + orm_only, self._adapt_polymorphic_element + ) + ) + + if not adapters: + return clause + + def replace(elem): + for _orm_only, adapter in adapters: + # if 'orm only', look for ORM annotations + # in the element before adapting. + if not _orm_only or \ + '_orm_adapt' in elem._annotations or \ + "parententity" in elem._annotations: + + e = adapter(elem) + if e is not None: + return e + + return visitors.replacement_traverse( + clause, + {}, + replace + ) + + def _query_entity_zero(self): + """Return the first QueryEntity.""" + return self._entities[0] + + def _mapper_zero(self): + """return the Mapper associated with the first QueryEntity.""" + return self._entities[0].mapper + + def _entity_zero(self): + """Return the 'entity' (mapper or AliasedClass) associated + with the first QueryEntity, or alternatively the 'select from' + entity if specified.""" + + return self._select_from_entity \ + if self._select_from_entity is not None \ + else self._query_entity_zero().entity_zero + + @property + def _mapper_entities(self): + for ent in self._entities: + if isinstance(ent, _MapperEntity): + yield ent + + def _joinpoint_zero(self): + return self._joinpoint.get( + '_joinpoint_entity', + self._entity_zero() + ) + + def _bind_mapper(self): + ezero = self._entity_zero() + if ezero is not None: + insp = inspect(ezero) + if not insp.is_clause_element: + return insp.mapper + + return None + + def _only_full_mapper_zero(self, methname): + if self._entities != [self._primary_entity]: + raise sa_exc.InvalidRequestError( + "%s() can only be used against " + "a single mapped class." % methname) + return self._primary_entity.entity_zero + + def _only_entity_zero(self, rationale=None): + if len(self._entities) > 1: + raise sa_exc.InvalidRequestError( + rationale or + "This operation requires a Query " + "against a single mapper." + ) + return self._entity_zero() + + def __all_equivs(self): + equivs = {} + for ent in self._mapper_entities: + equivs.update(ent.mapper._equivalent_columns) + return equivs + + def _get_condition(self): + return self._no_criterion_condition( + "get", order_by=False, distinct=False) + + def _get_existing_condition(self): + self._no_criterion_assertion("get", order_by=False, distinct=False) + + def _no_criterion_assertion(self, meth, order_by=True, distinct=True): + if not self._enable_assertions: + return + if self._criterion is not None or \ + self._statement is not None or self._from_obj or \ + self._limit is not None or self._offset is not None or \ + self._group_by or (order_by and self._order_by) or \ + (distinct and self._distinct): + raise sa_exc.InvalidRequestError( + "Query.%s() being called on a " + "Query with existing criterion. " % meth) + + def _no_criterion_condition(self, meth, order_by=True, distinct=True): + self._no_criterion_assertion(meth, order_by, distinct) + + self._from_obj = () + self._statement = self._criterion = None + self._order_by = self._group_by = self._distinct = False + + def _no_clauseelement_condition(self, meth): + if not self._enable_assertions: + return + if self._order_by: + raise sa_exc.InvalidRequestError( + "Query.%s() being called on a " + "Query with existing criterion. " % meth) + self._no_criterion_condition(meth) + + def _no_statement_condition(self, meth): + if not self._enable_assertions: + return + if self._statement is not None: + raise sa_exc.InvalidRequestError( + ("Query.%s() being called on a Query with an existing full " + "statement - can't apply criterion.") % meth) + + def _no_limit_offset(self, meth): + if not self._enable_assertions: + return + if self._limit is not None or self._offset is not None: + raise sa_exc.InvalidRequestError( + "Query.%s() being called on a Query which already has LIMIT " + "or OFFSET applied. To modify the row-limited results of a " + " Query, call from_self() first. " + "Otherwise, call %s() before limit() or offset() " + "are applied." + % (meth, meth) + ) + + def _get_options(self, populate_existing=None, + version_check=None, + only_load_props=None, + refresh_state=None, + identity_token=None): + if populate_existing: + self._populate_existing = populate_existing + if version_check: + self._version_check = version_check + if refresh_state: + self._refresh_state = refresh_state + if only_load_props: + self._only_load_props = set(only_load_props) + if identity_token: + self._refresh_identity_token = identity_token + return self + + def _clone(self): + cls = self.__class__ + q = cls.__new__(cls) + q.__dict__ = self.__dict__.copy() + return q + + @property + def statement(self): + """The full SELECT statement represented by this Query. + + The statement by default will not have disambiguating labels + applied to the construct unless with_labels(True) is called + first. + + """ + + stmt = self._compile_context(labels=self._with_labels).\ + statement + if self._params: + stmt = stmt.params(self._params) + + # TODO: there's no tests covering effects of + # the annotation not being there + return stmt._annotate({'no_replacement_traverse': True}) + + def subquery(self, name=None, with_labels=False, reduce_columns=False): + """return the full SELECT statement represented by + this :class:`.Query`, embedded within an :class:`.Alias`. + + Eager JOIN generation within the query is disabled. + + :param name: string name to be assigned as the alias; + this is passed through to :meth:`.FromClause.alias`. + If ``None``, a name will be deterministically generated + at compile time. + + :param with_labels: if True, :meth:`.with_labels` will be called + on the :class:`.Query` first to apply table-qualified labels + to all columns. + + :param reduce_columns: if True, :meth:`.Select.reduce_columns` will + be called on the resulting :func:`.select` construct, + to remove same-named columns where one also refers to the other + via foreign key or WHERE clause equivalence. + + .. versionchanged:: 0.8 the ``with_labels`` and ``reduce_columns`` + keyword arguments were added. + + """ + q = self.enable_eagerloads(False) + if with_labels: + q = q.with_labels() + q = q.statement + + if reduce_columns: + q = q.reduce_columns() + return q.alias(name=name) + + def cte(self, name=None, recursive=False): + r"""Return the full SELECT statement represented by this + :class:`.Query` represented as a common table expression (CTE). + + Parameters and usage are the same as those of the + :meth:`.SelectBase.cte` method; see that method for + further details. + + Here is the `PostgreSQL WITH + RECURSIVE example + <http://www.postgresql.org/docs/8.4/static/queries-with.html>`_. + Note that, in this example, the ``included_parts`` cte and the + ``incl_alias`` alias of it are Core selectables, which + means the columns are accessed via the ``.c.`` attribute. The + ``parts_alias`` object is an :func:`.orm.aliased` instance of the + ``Part`` entity, so column-mapped attributes are available + directly:: + + from sqlalchemy.orm import aliased + + class Part(Base): + __tablename__ = 'part' + part = Column(String, primary_key=True) + sub_part = Column(String, primary_key=True) + quantity = Column(Integer) + + included_parts = session.query( + Part.sub_part, + Part.part, + Part.quantity).\ + filter(Part.part=="our part").\ + cte(name="included_parts", recursive=True) + + incl_alias = aliased(included_parts, name="pr") + parts_alias = aliased(Part, name="p") + included_parts = included_parts.union_all( + session.query( + parts_alias.sub_part, + parts_alias.part, + parts_alias.quantity).\ + filter(parts_alias.part==incl_alias.c.sub_part) + ) + + q = session.query( + included_parts.c.sub_part, + func.sum(included_parts.c.quantity). + label('total_quantity') + ).\ + group_by(included_parts.c.sub_part) + + .. seealso:: + + :meth:`.HasCTE.cte` + + """ + return self.enable_eagerloads(False).\ + statement.cte(name=name, recursive=recursive) + + def label(self, name): + """Return the full SELECT statement represented by this + :class:`.Query`, converted + to a scalar subquery with a label of the given name. + + Analogous to :meth:`sqlalchemy.sql.expression.SelectBase.label`. + + .. versionadded:: 0.6.5 + + """ + + return self.enable_eagerloads(False).statement.label(name) + + def as_scalar(self): + """Return the full SELECT statement represented by this + :class:`.Query`, converted to a scalar subquery. + + Analogous to :meth:`sqlalchemy.sql.expression.SelectBase.as_scalar`. + + .. versionadded:: 0.6.5 + + """ + + return self.enable_eagerloads(False).statement.as_scalar() + + @property + def selectable(self): + """Return the :class:`.Select` object emitted by this :class:`.Query`. + + Used for :func:`.inspect` compatibility, this is equivalent to:: + + query.enable_eagerloads(False).with_labels().statement + + """ + return self.__clause_element__() + + def __clause_element__(self): + return self.enable_eagerloads(False).with_labels().statement + + @_generative() + def only_return_tuples(self, value): + """When set to True, the query results will always be a tuple, + specifically for single element queries. The default is False. + + . .. versionadded:: 1.2.5 + + """ + self._only_return_tuples = value + + @_generative() + def enable_eagerloads(self, value): + """Control whether or not eager joins and subqueries are + rendered. + + When set to False, the returned Query will not render + eager joins regardless of :func:`~sqlalchemy.orm.joinedload`, + :func:`~sqlalchemy.orm.subqueryload` options + or mapper-level ``lazy='joined'``/``lazy='subquery'`` + configurations. + + This is used primarily when nesting the Query's + statement into a subquery or other + selectable, or when using :meth:`.Query.yield_per`. + + """ + self._enable_eagerloads = value + + def _no_yield_per(self, message): + raise sa_exc.InvalidRequestError( + "The yield_per Query option is currently not " + "compatible with %s eager loading. Please " + "specify lazyload('*') or query.enable_eagerloads(False) in " + "order to " + "proceed with query.yield_per()." % message) + + @_generative() + def with_labels(self): + """Apply column labels to the return value of Query.statement. + + Indicates that this Query's `statement` accessor should return + a SELECT statement that applies labels to all columns in the + form <tablename>_<columnname>; this is commonly used to + disambiguate columns from multiple tables which have the same + name. + + When the `Query` actually issues SQL to load rows, it always + uses column labeling. + + .. note:: The :meth:`.Query.with_labels` method *only* applies + the output of :attr:`.Query.statement`, and *not* to any of + the result-row invoking systems of :class:`.Query` itself, e.g. + :meth:`.Query.first`, :meth:`.Query.all`, etc. To execute + a query using :meth:`.Query.with_labels`, invoke the + :attr:`.Query.statement` using :meth:`.Session.execute`:: + + result = session.execute(query.with_labels().statement) + + + """ + self._with_labels = True + + @_generative() + def enable_assertions(self, value): + """Control whether assertions are generated. + + When set to False, the returned Query will + not assert its state before certain operations, + including that LIMIT/OFFSET has not been applied + when filter() is called, no criterion exists + when get() is called, and no "from_statement()" + exists when filter()/order_by()/group_by() etc. + is called. This more permissive mode is used by + custom Query subclasses to specify criterion or + other modifiers outside of the usual usage patterns. + + Care should be taken to ensure that the usage + pattern is even possible. A statement applied + by from_statement() will override any criterion + set by filter() or order_by(), for example. + + """ + self._enable_assertions = value + + @property + def whereclause(self): + """A readonly attribute which returns the current WHERE criterion for + this Query. + + This returned value is a SQL expression construct, or ``None`` if no + criterion has been established. + + """ + return self._criterion + + @_generative() + def _with_current_path(self, path): + """indicate that this query applies to objects loaded + within a certain path. + + Used by deferred loaders (see strategies.py) which transfer + query options from an originating query to a newly generated + query intended for the deferred load. + + """ + self._current_path = path + + @_generative(_no_clauseelement_condition) + def with_polymorphic(self, + cls_or_mappers, + selectable=None, + polymorphic_on=None): + """Load columns for inheriting classes. + + :meth:`.Query.with_polymorphic` applies transformations + to the "main" mapped class represented by this :class:`.Query`. + The "main" mapped class here means the :class:`.Query` + object's first argument is a full class, i.e. + ``session.query(SomeClass)``. These transformations allow additional + tables to be present in the FROM clause so that columns for a + joined-inheritance subclass are available in the query, both for the + purposes of load-time efficiency as well as the ability to use + these columns at query time. + + See the documentation section :ref:`with_polymorphic` for + details on how this method is used. + + .. versionchanged:: 0.8 + A new and more flexible function + :func:`.orm.with_polymorphic` supersedes + :meth:`.Query.with_polymorphic`, as it can apply the equivalent + functionality to any set of columns or classes in the + :class:`.Query`, not just the "zero mapper". See that + function for a description of arguments. + + """ + + if not self._primary_entity: + raise sa_exc.InvalidRequestError( + "No primary mapper set up for this Query.") + entity = self._entities[0]._clone() + self._entities = [entity] + self._entities[1:] + entity.set_with_polymorphic(self, + cls_or_mappers, + selectable=selectable, + polymorphic_on=polymorphic_on) + + @_generative() + def yield_per(self, count): + r"""Yield only ``count`` rows at a time. + + The purpose of this method is when fetching very large result sets + (> 10K rows), to batch results in sub-collections and yield them + out partially, so that the Python interpreter doesn't need to declare + very large areas of memory which is both time consuming and leads + to excessive memory use. The performance from fetching hundreds of + thousands of rows can often double when a suitable yield-per setting + (e.g. approximately 1000) is used, even with DBAPIs that buffer + rows (which are most). + + The :meth:`.Query.yield_per` method **is not compatible + subqueryload eager loading or joinedload eager loading when + using collections**. It is potentially compatible with "select in" + eager loading, **provided the databse driver supports multiple, + independent cursors** (pysqlite and psycopg2 are known to work, + MySQL and SQL Server ODBC drivers do not). + + Therefore in some cases, it may be helpful to disable + eager loads, either unconditionally with + :meth:`.Query.enable_eagerloads`:: + + q = sess.query(Object).yield_per(100).enable_eagerloads(False) + + Or more selectively using :func:`.lazyload`; such as with + an asterisk to specify the default loader scheme:: + + q = sess.query(Object).yield_per(100).\ + options(lazyload('*'), joinedload(Object.some_related)) + + .. warning:: + + Use this method with caution; if the same instance is + present in more than one batch of rows, end-user changes + to attributes will be overwritten. + + In particular, it's usually impossible to use this setting + with eagerly loaded collections (i.e. any lazy='joined' or + 'subquery') since those collections will be cleared for a + new load when encountered in a subsequent result batch. + In the case of 'subquery' loading, the full result for all + rows is fetched which generally defeats the purpose of + :meth:`~sqlalchemy.orm.query.Query.yield_per`. + + Also note that while + :meth:`~sqlalchemy.orm.query.Query.yield_per` will set the + ``stream_results`` execution option to True, currently + this is only understood by + :mod:`~sqlalchemy.dialects.postgresql.psycopg2`, + :mod:`~sqlalchemy.dialects.mysql.mysqldb` and + :mod:`~sqlalchemy.dialects.mysql.pymysql` dialects + which will stream results using server side cursors + instead of pre-buffer all rows for this query. Other + DBAPIs **pre-buffer all rows** before making them + available. The memory use of raw database rows is much less + than that of an ORM-mapped object, but should still be taken into + consideration when benchmarking. + + .. seealso:: + + :meth:`.Query.enable_eagerloads` + + """ + self._yield_per = count + self._execution_options = self._execution_options.union( + {"stream_results": True, + "max_row_buffer": count}) + + def get(self, ident): + """Return an instance based on the given primary key identifier, + or ``None`` if not found. + + E.g.:: + + my_user = session.query(User).get(5) + + some_object = session.query(VersionedFoo).get((5, 10)) + + :meth:`~.Query.get` is special in that it provides direct + access to the identity map of the owning :class:`.Session`. + If the given primary key identifier is present + in the local identity map, the object is returned + directly from this collection and no SQL is emitted, + unless the object has been marked fully expired. + If not present, + a SELECT is performed in order to locate the object. + + :meth:`~.Query.get` also will perform a check if + the object is present in the identity map and + marked as expired - a SELECT + is emitted to refresh the object as well as to + ensure that the row is still present. + If not, :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. + + :meth:`~.Query.get` is only used to return a single + mapped instance, not multiple instances or + individual column constructs, and strictly + on a single primary key value. The originating + :class:`.Query` must be constructed in this way, + i.e. against a single mapped entity, + with no additional filtering criterion. Loading + options via :meth:`~.Query.options` may be applied + however, and will be used if the object is not + yet locally present. + + A lazy-loading, many-to-one attribute configured + by :func:`.relationship`, using a simple + foreign-key-to-primary-key criterion, will also use an + operation equivalent to :meth:`~.Query.get` in order to retrieve + the target value from the local identity map + before querying the database. See :doc:`/orm/loading_relationships` + for further details on relationship loading. + + :param ident: A scalar or tuple value representing + the primary key. For a composite primary key, + the order of identifiers corresponds in most cases + to that of the mapped :class:`.Table` object's + primary key columns. For a :func:`.mapper` that + was given the ``primary key`` argument during + construction, the order of identifiers corresponds + to the elements present in this collection. + + :return: The object instance, or ``None``. + + """ + return self._get_impl( + ident, loading.load_on_pk_identity) + + def _identity_lookup(self, mapper, primary_key_identity, + identity_token=None, passive=attributes.PASSIVE_OFF, + lazy_loaded_from=None): + """Locate an object in the identity map. + + Given a primary key identity, constructs an identity key and then + looks in the session's identity map. If present, the object may + be run through unexpiration rules (e.g. load unloaded attributes, + check if was deleted). + + For performance reasons, while the :class:`.Query` must be + instantiated, it may be instantiated with no entities, and the + mapper is passed:: + + obj = session.query()._identity_lookup(inspect(SomeClass), (1, )) + + :param mapper: mapper in use + :param primary_key_identity: the primary key we are searching for, as + a tuple. + :param identity_token: identity token that should be used to create + the identity key. Used as is, however overriding subclasses can + repurpose this in order to interpret the value in a special way, + such as if None then look among multple target tokens. + :param passive: passive load flag passed to + :func:`.loading.get_from_identity`, which impacts the behavior if + the object is found; the object may be validated and/or unexpired + if the flag allows for SQL to be emitted. + :param lazy_loaded_from: an :class:`.InstanceState` that is + specifically asking for this identity as a related identity. Used + for sharding schemes where there is a correspondence between an object + and a related object being lazy-loaded (or otherwise + relationship-loaded). + + .. versionadded:: 1.2.9 + + :return: None if the object is not found in the identity map, *or* + if the object was unexpired and found to have been deleted. + if passive flags disallow SQL and the object is expired, returns + PASSIVE_NO_RESULT. In all other cases the instance is returned. + + .. versionadded:: 1.2.7 + + """ + + key = mapper.identity_key_from_primary_key( + primary_key_identity, identity_token=identity_token) + return loading.get_from_identity( + self.session, key, passive) + + def _get_impl( + self, primary_key_identity, db_load_fn, identity_token=None): + # convert composite types to individual args + if hasattr(primary_key_identity, '__composite_values__'): + primary_key_identity = primary_key_identity.__composite_values__() + + primary_key_identity = util.to_list(primary_key_identity) + + mapper = self._only_full_mapper_zero("get") + + if len(primary_key_identity) != len(mapper.primary_key): + raise sa_exc.InvalidRequestError( + "Incorrect number of values in identifier to formulate " + "primary key for query.get(); primary key columns are %s" % + ','.join("'%s'" % c for c in mapper.primary_key)) + + if not self._populate_existing and \ + not mapper.always_refresh and \ + self._for_update_arg is None: + + instance = self._identity_lookup( + mapper, primary_key_identity, + identity_token=identity_token) + + if instance is not None: + self._get_existing_condition() + # reject calls for id in identity map but class + # mismatch. + if not issubclass(instance.__class__, mapper.class_): + return None + return instance + + return db_load_fn(self, primary_key_identity) + + @_generative() + def correlate(self, *args): + """Return a :class:`.Query` construct which will correlate the given + FROM clauses to that of an enclosing :class:`.Query` or + :func:`~.expression.select`. + + The method here accepts mapped classes, :func:`.aliased` constructs, + and :func:`.mapper` constructs as arguments, which are resolved into + expression constructs, in addition to appropriate expression + constructs. + + The correlation arguments are ultimately passed to + :meth:`.Select.correlate` after coercion to expression constructs. + + The correlation arguments take effect in such cases + as when :meth:`.Query.from_self` is used, or when + a subquery as returned by :meth:`.Query.subquery` is + embedded in another :func:`~.expression.select` construct. + + """ + + for s in args: + if s is None: + self._correlate = self._correlate.union([None]) + else: + self._correlate = self._correlate.union( + sql_util.surface_selectables(_interpret_as_from(s)) + ) + + @_generative() + def autoflush(self, setting): + """Return a Query with a specific 'autoflush' setting. + + Note that a Session with autoflush=False will + not autoflush, even if this flag is set to True at the + Query level. Therefore this flag is usually used only + to disable autoflush for a specific Query. + + """ + self._autoflush = setting + + @_generative() + def populate_existing(self): + """Return a :class:`.Query` that will expire and refresh all instances + as they are loaded, or reused from the current :class:`.Session`. + + :meth:`.populate_existing` does not improve behavior when + the ORM is used normally - the :class:`.Session` object's usual + behavior of maintaining a transaction and expiring all attributes + after rollback or commit handles object state automatically. + This method is not intended for general use. + + """ + self._populate_existing = True + + @_generative() + def _with_invoke_all_eagers(self, value): + """Set the 'invoke all eagers' flag which causes joined- and + subquery loaders to traverse into already-loaded related objects + and collections. + + Default is that of :attr:`.Query._invoke_all_eagers`. + + """ + self._invoke_all_eagers = value + + def with_parent(self, instance, property=None, from_entity=None): + """Add filtering criterion that relates the given instance + to a child object or collection, using its attribute state + as well as an established :func:`.relationship()` + configuration. + + The method uses the :func:`.with_parent` function to generate + the clause, the result of which is passed to :meth:`.Query.filter`. + + Parameters are the same as :func:`.with_parent`, with the exception + that the given property can be None, in which case a search is + performed against this :class:`.Query` object's target mapper. + + :param instance: + An instance which has some :func:`.relationship`. + + :param property: + String property name, or class-bound attribute, which indicates + what relationship from the instance should be used to reconcile the + parent/child relationship. + + :param from_entity: + Entity in which to consider as the left side. This defaults to the + "zero" entity of the :class:`.Query` itself. + + """ + + if from_entity: + entity_zero = inspect(from_entity) + else: + entity_zero = self._entity_zero() + if property is None: + + mapper = object_mapper(instance) + + for prop in mapper.iterate_properties: + if isinstance(prop, properties.RelationshipProperty) and \ + prop.mapper is entity_zero.mapper: + property = prop + break + else: + raise sa_exc.InvalidRequestError( + "Could not locate a property which relates instances " + "of class '%s' to instances of class '%s'" % + ( + entity_zero.mapper.class_.__name__, + instance.__class__.__name__) + ) + + return self.filter(with_parent(instance, property, entity_zero.entity)) + + @_generative() + def add_entity(self, entity, alias=None): + """add a mapped entity to the list of result columns + to be returned.""" + + if alias is not None: + entity = aliased(entity, alias) + + self._entities = list(self._entities) + m = _MapperEntity(self, entity) + self._set_entity_selectables([m]) + + @_generative() + def with_session(self, session): + """Return a :class:`.Query` that will use the given :class:`.Session`. + + While the :class:`.Query` object is normally instantiated using the + :meth:`.Session.query` method, it is legal to build the :class:`.Query` + directly without necessarily using a :class:`.Session`. Such a + :class:`.Query` object, or any :class:`.Query` already associated + with a different :class:`.Session`, can produce a new :class:`.Query` + object associated with a target session using this method:: + + from sqlalchemy.orm import Query + + query = Query([MyClass]).filter(MyClass.id == 5) + + result = query.with_session(my_session).one() + + """ + + self.session = session + + def from_self(self, *entities): + r"""return a Query that selects from this Query's + SELECT statement. + + :meth:`.Query.from_self` essentially turns the SELECT statement + into a SELECT of itself. Given a query such as:: + + q = session.query(User).filter(User.name.like('e%')) + + Given the :meth:`.Query.from_self` version:: + + q = session.query(User).filter(User.name.like('e%')).from_self() + + This query renders as: + + .. sourcecode:: sql + + SELECT anon_1.user_id AS anon_1_user_id, + anon_1.user_name AS anon_1_user_name + FROM (SELECT "user".id AS user_id, "user".name AS user_name + FROM "user" + WHERE "user".name LIKE :name_1) AS anon_1 + + There are lots of cases where :meth:`.Query.from_self` may be useful. + A simple one is where above, we may want to apply a row LIMIT to + the set of user objects we query against, and then apply additional + joins against that row-limited set:: + + q = session.query(User).filter(User.name.like('e%')).\ + limit(5).from_self().\ + join(User.addresses).filter(Address.email.like('q%')) + + The above query joins to the ``Address`` entity but only against the + first five results of the ``User`` query: + + .. sourcecode:: sql + + SELECT anon_1.user_id AS anon_1_user_id, + anon_1.user_name AS anon_1_user_name + FROM (SELECT "user".id AS user_id, "user".name AS user_name + FROM "user" + WHERE "user".name LIKE :name_1 + LIMIT :param_1) AS anon_1 + JOIN address ON anon_1.user_id = address.user_id + WHERE address.email LIKE :email_1 + + **Automatic Aliasing** + + Another key behavior of :meth:`.Query.from_self` is that it applies + **automatic aliasing** to the entities inside the subquery, when + they are referenced on the outside. Above, if we continue to + refer to the ``User`` entity without any additional aliasing applied + to it, those references wil be in terms of the subquery:: + + q = session.query(User).filter(User.name.like('e%')).\ + limit(5).from_self().\ + join(User.addresses).filter(Address.email.like('q%')).\ + order_by(User.name) + + The ORDER BY against ``User.name`` is aliased to be in terms of the + inner subquery: + + .. sourcecode:: sql + + SELECT anon_1.user_id AS anon_1_user_id, + anon_1.user_name AS anon_1_user_name + FROM (SELECT "user".id AS user_id, "user".name AS user_name + FROM "user" + WHERE "user".name LIKE :name_1 + LIMIT :param_1) AS anon_1 + JOIN address ON anon_1.user_id = address.user_id + WHERE address.email LIKE :email_1 ORDER BY anon_1.user_name + + The automatic aliasing feature only works in a **limited** way, + for simple filters and orderings. More ambitious constructions + such as referring to the entity in joins should prefer to use + explicit subquery objects, typically making use of the + :meth:`.Query.subquery` method to produce an explicit subquery object. + Always test the structure of queries by viewing the SQL to ensure + a particular structure does what's expected! + + **Changing the Entities** + + :meth:`.Query.from_self` also includes the ability to modify what + columns are being queried. In our example, we want ``User.id`` + to be queried by the inner query, so that we can join to the + ``Address`` entity on the outside, but we only wanted the outer + query to return the ``Address.email`` column:: + + q = session.query(User).filter(User.name.like('e%')).\ + limit(5).from_self(Address.email).\ + join(User.addresses).filter(Address.email.like('q%')) + + yielding: + + .. sourcecode:: sql + + SELECT address.email AS address_email + FROM (SELECT "user".id AS user_id, "user".name AS user_name + FROM "user" + WHERE "user".name LIKE :name_1 + LIMIT :param_1) AS anon_1 + JOIN address ON anon_1.user_id = address.user_id + WHERE address.email LIKE :email_1 + + **Looking out for Inner / Outer Columns** + + Keep in mind that when referring to columns that originate from + inside the subquery, we need to ensure they are present in the + columns clause of the subquery itself; this is an ordinary aspect of + SQL. For example, if we wanted to load from a joined entity inside + the subquery using :func:`.contains_eager`, we need to add those + columns. Below illustrates a join of ``Address`` to ``User``, + then a subquery, and then we'd like :func:`.contains_eager` to access + the ``User`` columns:: + + q = session.query(Address).join(Address.user).\ + filter(User.name.like('e%')) + + q = q.add_entity(User).from_self().\ + options(contains_eager(Address.user)) + + We use :meth:`.Query.add_entity` above **before** we call + :meth:`.Query.from_self` so that the ``User`` columns are present + in the inner subquery, so that they are available to the + :func:`.contains_eager` modifier we are using on the outside, + producing: + + .. sourcecode:: sql + + SELECT anon_1.address_id AS anon_1_address_id, + anon_1.address_email AS anon_1_address_email, + anon_1.address_user_id AS anon_1_address_user_id, + anon_1.user_id AS anon_1_user_id, + anon_1.user_name AS anon_1_user_name + FROM ( + SELECT address.id AS address_id, + address.email AS address_email, + address.user_id AS address_user_id, + "user".id AS user_id, + "user".name AS user_name + FROM address JOIN "user" ON "user".id = address.user_id + WHERE "user".name LIKE :name_1) AS anon_1 + + If we didn't call ``add_entity(User)``, but still asked + :func:`.contains_eager` to load the ``User`` entity, it would be + forced to add the table on the outside without the correct + join criteria - note the ``anon1, "user"`` phrase at + the end: + + .. sourcecode:: sql + + -- incorrect query + SELECT anon_1.address_id AS anon_1_address_id, + anon_1.address_email AS anon_1_address_email, + anon_1.address_user_id AS anon_1_address_user_id, + "user".id AS user_id, + "user".name AS user_name + FROM ( + SELECT address.id AS address_id, + address.email AS address_email, + address.user_id AS address_user_id + FROM address JOIN "user" ON "user".id = address.user_id + WHERE "user".name LIKE :name_1) AS anon_1, "user" + + :param \*entities: optional list of entities which will replace + those being selected. + + """ + fromclause = self.with_labels().enable_eagerloads(False).\ + statement.correlate(None) + q = self._from_selectable(fromclause) + q._enable_single_crit = False + q._select_from_entity = self._entity_zero() + if entities: + q._set_entities(entities) + return q + + @_generative() + def _set_enable_single_crit(self, val): + self._enable_single_crit = val + + @_generative() + def _from_selectable(self, fromclause): + for attr in ( + '_statement', '_criterion', + '_order_by', '_group_by', + '_limit', '_offset', + '_joinpath', '_joinpoint', + '_distinct', '_having', + '_prefixes', '_suffixes' + ): + self.__dict__.pop(attr, None) + self._set_select_from([fromclause], True) + + # this enables clause adaptation for non-ORM + # expressions. + self._orm_only_from_obj_alias = False + + old_entities = self._entities + self._entities = [] + for e in old_entities: + e.adapt_to_selectable(self, self._from_obj[0]) + + def values(self, *columns): + """Return an iterator yielding result tuples corresponding + to the given list of columns""" + + if not columns: + return iter(()) + q = self._clone() + q._set_entities(columns, entity_wrapper=_ColumnEntity) + if not q._yield_per: + q._yield_per = 10 + return iter(q) + _values = values + + def value(self, column): + """Return a scalar result corresponding to the given + column expression.""" + try: + return next(self.values(column))[0] + except StopIteration: + return None + + @_generative() + def with_entities(self, *entities): + r"""Return a new :class:`.Query` replacing the SELECT list with the + given entities. + + e.g.:: + + # Users, filtered on some arbitrary criterion + # and then ordered by related email address + q = session.query(User).\ + join(User.address).\ + filter(User.name.like('%ed%')).\ + order_by(Address.email) + + # given *only* User.id==5, Address.email, and 'q', what + # would the *next* User in the result be ? + subq = q.with_entities(Address.email).\ + order_by(None).\ + filter(User.id==5).\ + subquery() + q = q.join((subq, subq.c.email < Address.email)).\ + limit(1) + + .. versionadded:: 0.6.5 + + """ + self._set_entities(entities) + + @_generative() + def add_columns(self, *column): + """Add one or more column expressions to the list + of result columns to be returned.""" + + self._entities = list(self._entities) + l = len(self._entities) + for c in column: + _ColumnEntity(self, c) + # _ColumnEntity may add many entities if the + # given arg is a FROM clause + self._set_entity_selectables(self._entities[l:]) + + @util.pending_deprecation("0.7", + ":meth:`.add_column` is superseded " + "by :meth:`.add_columns`", + False) + def add_column(self, column): + """Add a column expression to the list of result columns to be + returned. + + Pending deprecation: :meth:`.add_column` will be superseded by + :meth:`.add_columns`. + + """ + return self.add_columns(column) + + def options(self, *args): + """Return a new Query object, applying the given list of + mapper options. + + Most supplied options regard changing how column- and + relationship-mapped attributes are loaded. See the sections + :ref:`deferred` and :doc:`/orm/loading_relationships` for reference + documentation. + + """ + return self._options(False, *args) + + def _conditional_options(self, *args): + return self._options(True, *args) + + @_generative() + def _options(self, conditional, *args): + # most MapperOptions write to the '_attributes' dictionary, + # so copy that as well + self._attributes = self._attributes.copy() + if '_unbound_load_dedupes' not in self._attributes: + self._attributes['_unbound_load_dedupes'] = set() + opts = tuple(util.flatten_iterator(args)) + self._with_options = self._with_options + opts + if conditional: + for opt in opts: + opt.process_query_conditionally(self) + else: + for opt in opts: + opt.process_query(self) + + def with_transformation(self, fn): + """Return a new :class:`.Query` object transformed by + the given function. + + E.g.:: + + def filter_something(criterion): + def transform(q): + return q.filter(criterion) + return transform + + q = q.with_transformation(filter_something(x==5)) + + This allows ad-hoc recipes to be created for :class:`.Query` + objects. See the example at :ref:`hybrid_transformers`. + + .. versionadded:: 0.7.4 + + """ + return fn(self) + + @_generative() + def with_hint(self, selectable, text, dialect_name='*'): + """Add an indexing or other executional context + hint for the given entity or selectable to + this :class:`.Query`. + + Functionality is passed straight through to + :meth:`~sqlalchemy.sql.expression.Select.with_hint`, + with the addition that ``selectable`` can be a + :class:`.Table`, :class:`.Alias`, or ORM entity / mapped class + /etc. + + .. seealso:: + + :meth:`.Query.with_statement_hint` + + """ + if selectable is not None: + selectable = inspect(selectable).selectable + + self._with_hints += ((selectable, text, dialect_name),) + + def with_statement_hint(self, text, dialect_name='*'): + """add a statement hint to this :class:`.Select`. + + This method is similar to :meth:`.Select.with_hint` except that + it does not require an individual table, and instead applies to the + statement as a whole. + + This feature calls down into :meth:`.Select.with_statement_hint`. + + .. versionadded:: 1.0.0 + + .. seealso:: + + :meth:`.Query.with_hint` + + """ + return self.with_hint(None, text, dialect_name) + + @_generative() + def execution_options(self, **kwargs): + """ Set non-SQL options which take effect during execution. + + The options are the same as those accepted by + :meth:`.Connection.execution_options`. + + Note that the ``stream_results`` execution option is enabled + automatically if the :meth:`~sqlalchemy.orm.query.Query.yield_per()` + method is used. + + """ + self._execution_options = self._execution_options.union(kwargs) + + @_generative() + def with_lockmode(self, mode): + """Return a new :class:`.Query` object with the specified "locking mode", + which essentially refers to the ``FOR UPDATE`` clause. + + .. deprecated:: 0.9.0 superseded by :meth:`.Query.with_for_update`. + + :param mode: a string representing the desired locking mode. + Valid values are: + + * ``None`` - translates to no lockmode + + * ``'update'`` - translates to ``FOR UPDATE`` + (standard SQL, supported by most dialects) + + * ``'update_nowait'`` - translates to ``FOR UPDATE NOWAIT`` + (supported by Oracle, PostgreSQL 8.1 upwards) + + * ``'read'`` - translates to ``LOCK IN SHARE MODE`` (for MySQL), + and ``FOR SHARE`` (for PostgreSQL) + + .. seealso:: + + :meth:`.Query.with_for_update` - improved API for + specifying the ``FOR UPDATE`` clause. + + """ + self._for_update_arg = LockmodeArg.parse_legacy_query(mode) + + @_generative() + def with_for_update(self, read=False, nowait=False, of=None, + skip_locked=False, key_share=False): + """return a new :class:`.Query` with the specified options for the + ``FOR UPDATE`` clause. + + The behavior of this method is identical to that of + :meth:`.SelectBase.with_for_update`. When called with no arguments, + the resulting ``SELECT`` statement will have a ``FOR UPDATE`` clause + appended. When additional arguments are specified, backend-specific + options such as ``FOR UPDATE NOWAIT`` or ``LOCK IN SHARE MODE`` + can take effect. + + E.g.:: + + q = sess.query(User).with_for_update(nowait=True, of=User) + + The above query on a PostgreSQL backend will render like:: + + SELECT users.id AS users_id FROM users FOR UPDATE OF users NOWAIT + + .. versionadded:: 0.9.0 :meth:`.Query.with_for_update` supersedes + the :meth:`.Query.with_lockmode` method. + + .. seealso:: + + :meth:`.GenerativeSelect.with_for_update` - Core level method with + full argument and behavioral description. + + """ + self._for_update_arg = LockmodeArg(read=read, nowait=nowait, of=of, + skip_locked=skip_locked, + key_share=key_share) + + @_generative() + def params(self, *args, **kwargs): + r"""add values for bind parameters which may have been + specified in filter(). + + parameters may be specified using \**kwargs, or optionally a single + dictionary as the first positional argument. The reason for both is + that \**kwargs is convenient, however some parameter dictionaries + contain unicode keys in which case \**kwargs cannot be used. + + """ + if len(args) == 1: + kwargs.update(args[0]) + elif len(args) > 0: + raise sa_exc.ArgumentError( + "params() takes zero or one positional argument, " + "which is a dictionary.") + self._params = self._params.copy() + self._params.update(kwargs) + + @_generative(_no_statement_condition, _no_limit_offset) + def filter(self, *criterion): + r"""apply the given filtering criterion to a copy + of this :class:`.Query`, using SQL expressions. + + e.g.:: + + session.query(MyClass).filter(MyClass.name == 'some name') + + Multiple criteria may be specified as comma separated; the effect + is that they will be joined together using the :func:`.and_` + function:: + + session.query(MyClass).\ + filter(MyClass.name == 'some name', MyClass.id > 5) + + The criterion is any SQL expression object applicable to the + WHERE clause of a select. String expressions are coerced + into SQL expression constructs via the :func:`.text` construct. + + .. seealso:: + + :meth:`.Query.filter_by` - filter on keyword expressions. + + """ + for criterion in list(criterion): + criterion = expression._expression_literal_as_text(criterion) + + criterion = self._adapt_clause(criterion, True, True) + + if self._criterion is not None: + self._criterion = self._criterion & criterion + else: + self._criterion = criterion + + def filter_by(self, **kwargs): + r"""apply the given filtering criterion to a copy + of this :class:`.Query`, using keyword expressions. + + e.g.:: + + session.query(MyClass).filter_by(name = 'some name') + + Multiple criteria may be specified as comma separated; the effect + is that they will be joined together using the :func:`.and_` + function:: + + session.query(MyClass).\ + filter_by(name = 'some name', id = 5) + + The keyword expressions are extracted from the primary + entity of the query, or the last entity that was the + target of a call to :meth:`.Query.join`. + + .. seealso:: + + :meth:`.Query.filter` - filter on SQL expressions. + + """ + + clauses = [_entity_descriptor(self._joinpoint_zero(), key) == value + for key, value in kwargs.items()] + return self.filter(sql.and_(*clauses)) + + @_generative(_no_statement_condition, _no_limit_offset) + def order_by(self, *criterion): + """apply one or more ORDER BY criterion to the query and return + the newly resulting ``Query`` + + All existing ORDER BY settings can be suppressed by + passing ``None`` - this will suppress any ORDER BY configured + on mappers as well. + + Alternatively, passing False will reset ORDER BY and additionally + re-allow default mapper.order_by to take place. Note mapper.order_by + is deprecated. + + """ + + if len(criterion) == 1: + if criterion[0] is False: + if '_order_by' in self.__dict__: + self._order_by = False + return + if criterion[0] is None: + self._order_by = None + return + + criterion = self._adapt_col_list(criterion) + + if self._order_by is False or self._order_by is None: + self._order_by = criterion + else: + self._order_by = self._order_by + criterion + + @_generative(_no_statement_condition, _no_limit_offset) + def group_by(self, *criterion): + """apply one or more GROUP BY criterion to the query and return + the newly resulting :class:`.Query` + + All existing GROUP BY settings can be suppressed by + passing ``None`` - this will suppress any GROUP BY configured + on mappers as well. + + .. versionadded:: 1.1 GROUP BY can be cancelled by passing None, + in the same way as ORDER BY. + + """ + + if len(criterion) == 1: + if criterion[0] is None: + self._group_by = False + return + + criterion = list(chain(*[_orm_columns(c) for c in criterion])) + criterion = self._adapt_col_list(criterion) + + if self._group_by is False: + self._group_by = criterion + else: + self._group_by = self._group_by + criterion + + @_generative(_no_statement_condition, _no_limit_offset) + def having(self, criterion): + r"""apply a HAVING criterion to the query and return the + newly resulting :class:`.Query`. + + :meth:`~.Query.having` is used in conjunction with + :meth:`~.Query.group_by`. + + HAVING criterion makes it possible to use filters on aggregate + functions like COUNT, SUM, AVG, MAX, and MIN, eg.:: + + q = session.query(User.id).\ + join(User.addresses).\ + group_by(User.id).\ + having(func.count(Address.id) > 2) + + """ + + criterion = expression._expression_literal_as_text(criterion) + + if criterion is not None and \ + not isinstance(criterion, sql.ClauseElement): + raise sa_exc.ArgumentError( + "having() argument must be of type " + "sqlalchemy.sql.ClauseElement or string") + + criterion = self._adapt_clause(criterion, True, True) + + if self._having is not None: + self._having = self._having & criterion + else: + self._having = criterion + + def _set_op(self, expr_fn, *q): + return self._from_selectable( + expr_fn(*([self] + list(q))) + )._set_enable_single_crit(False) + + def union(self, *q): + """Produce a UNION of this Query against one or more queries. + + e.g.:: + + q1 = sess.query(SomeClass).filter(SomeClass.foo=='bar') + q2 = sess.query(SomeClass).filter(SomeClass.bar=='foo') + + q3 = q1.union(q2) + + The method accepts multiple Query objects so as to control + the level of nesting. A series of ``union()`` calls such as:: + + x.union(y).union(z).all() + + will nest on each ``union()``, and produces:: + + SELECT * FROM (SELECT * FROM (SELECT * FROM X UNION + SELECT * FROM y) UNION SELECT * FROM Z) + + Whereas:: + + x.union(y, z).all() + + produces:: + + SELECT * FROM (SELECT * FROM X UNION SELECT * FROM y UNION + SELECT * FROM Z) + + Note that many database backends do not allow ORDER BY to + be rendered on a query called within UNION, EXCEPT, etc. + To disable all ORDER BY clauses including those configured + on mappers, issue ``query.order_by(None)`` - the resulting + :class:`.Query` object will not render ORDER BY within + its SELECT statement. + + """ + return self._set_op(expression.union, *q) + + def union_all(self, *q): + """Produce a UNION ALL of this Query against one or more queries. + + Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See + that method for usage examples. + + """ + return self._set_op(expression.union_all, *q) + + def intersect(self, *q): + """Produce an INTERSECT of this Query against one or more queries. + + Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See + that method for usage examples. + + """ + return self._set_op(expression.intersect, *q) + + def intersect_all(self, *q): + """Produce an INTERSECT ALL of this Query against one or more queries. + + Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See + that method for usage examples. + + """ + return self._set_op(expression.intersect_all, *q) + + def except_(self, *q): + """Produce an EXCEPT of this Query against one or more queries. + + Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See + that method for usage examples. + + """ + return self._set_op(expression.except_, *q) + + def except_all(self, *q): + """Produce an EXCEPT ALL of this Query against one or more queries. + + Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See + that method for usage examples. + + """ + return self._set_op(expression.except_all, *q) + + def join(self, *props, **kwargs): + r"""Create a SQL JOIN against this :class:`.Query` object's criterion + and apply generatively, returning the newly resulting :class:`.Query`. + + **Simple Relationship Joins** + + Consider a mapping between two classes ``User`` and ``Address``, + with a relationship ``User.addresses`` representing a collection + of ``Address`` objects associated with each ``User``. The most + common usage of :meth:`~.Query.join` is to create a JOIN along this + relationship, using the ``User.addresses`` attribute as an indicator + for how this should occur:: + + q = session.query(User).join(User.addresses) + + Where above, the call to :meth:`~.Query.join` along ``User.addresses`` + will result in SQL equivalent to:: + + SELECT user.* FROM user JOIN address ON user.id = address.user_id + + In the above example we refer to ``User.addresses`` as passed to + :meth:`~.Query.join` as the *on clause*, that is, it indicates + how the "ON" portion of the JOIN should be constructed. For a + single-entity query such as the one above (i.e. we start by selecting + only from ``User`` and nothing else), the relationship can also be + specified by its string name:: + + q = session.query(User).join("addresses") + + :meth:`~.Query.join` can also accommodate multiple + "on clause" arguments to produce a chain of joins, such as below + where a join across four related entities is constructed:: + + q = session.query(User).join("orders", "items", "keywords") + + The above would be shorthand for three separate calls to + :meth:`~.Query.join`, each using an explicit attribute to indicate + the source entity:: + + q = session.query(User).\ + join(User.orders).\ + join(Order.items).\ + join(Item.keywords) + + **Joins to a Target Entity or Selectable** + + A second form of :meth:`~.Query.join` allows any mapped entity + or core selectable construct as a target. In this usage, + :meth:`~.Query.join` will attempt + to create a JOIN along the natural foreign key relationship between + two entities:: + + q = session.query(User).join(Address) + + The above calling form of :meth:`~.Query.join` will raise an error if + either there are no foreign keys between the two entities, or if + there are multiple foreign key linkages between them. In the + above calling form, :meth:`~.Query.join` is called upon to + create the "on clause" automatically for us. The target can + be any mapped entity or selectable, such as a :class:`.Table`:: + + q = session.query(User).join(addresses_table) + + **Joins to a Target with an ON Clause** + + The third calling form allows both the target entity as well + as the ON clause to be passed explicitly. Suppose for + example we wanted to join to ``Address`` twice, using + an alias the second time. We use :func:`~sqlalchemy.orm.aliased` + to create a distinct alias of ``Address``, and join + to it using the ``target, onclause`` form, so that the + alias can be specified explicitly as the target along with + the relationship to instruct how the ON clause should proceed:: + + a_alias = aliased(Address) + + q = session.query(User).\ + join(User.addresses).\ + join(a_alias, User.addresses).\ + filter(Address.email_address=='ed@foo.com').\ + filter(a_alias.email_address=='ed@bar.com') + + Where above, the generated SQL would be similar to:: + + SELECT user.* FROM user + JOIN address ON user.id = address.user_id + JOIN address AS address_1 ON user.id=address_1.user_id + WHERE address.email_address = :email_address_1 + AND address_1.email_address = :email_address_2 + + The two-argument calling form of :meth:`~.Query.join` + also allows us to construct arbitrary joins with SQL-oriented + "on clause" expressions, not relying upon configured relationships + at all. Any SQL expression can be passed as the ON clause + when using the two-argument form, which should refer to the target + entity in some way as well as an applicable source entity:: + + q = session.query(User).join(Address, User.id==Address.user_id) + + .. versionchanged:: 0.7 + In SQLAlchemy 0.6 and earlier, the two argument form of + :meth:`~.Query.join` requires the usage of a tuple: + ``query(User).join((Address, User.id==Address.user_id))``\ . + This calling form is accepted in 0.7 and further, though + is not necessary unless multiple join conditions are passed to + a single :meth:`~.Query.join` call, which itself is also not + generally necessary as it is now equivalent to multiple + calls (this wasn't always the case). + + **Advanced Join Targeting and Adaption** + + There is a lot of flexibility in what the "target" can be when using + :meth:`~.Query.join`. As noted previously, it also accepts + :class:`.Table` constructs and other selectables such as + :func:`.alias` and :func:`.select` constructs, with either the one + or two-argument forms:: + + addresses_q = select([Address.user_id]).\ + where(Address.email_address.endswith("@bar.com")).\ + alias() + + q = session.query(User).\ + join(addresses_q, addresses_q.c.user_id==User.id) + + :meth:`~.Query.join` also features the ability to *adapt* a + :meth:`~sqlalchemy.orm.relationship` -driven ON clause to the target + selectable. Below we construct a JOIN from ``User`` to a subquery + against ``Address``, allowing the relationship denoted by + ``User.addresses`` to *adapt* itself to the altered target:: + + address_subq = session.query(Address).\ + filter(Address.email_address == 'ed@foo.com').\ + subquery() + + q = session.query(User).join(address_subq, User.addresses) + + Producing SQL similar to:: + + SELECT user.* FROM user + JOIN ( + SELECT address.id AS id, + address.user_id AS user_id, + address.email_address AS email_address + FROM address + WHERE address.email_address = :email_address_1 + ) AS anon_1 ON user.id = anon_1.user_id + + The above form allows one to fall back onto an explicit ON + clause at any time:: + + q = session.query(User).\ + join(address_subq, User.id==address_subq.c.user_id) + + **Controlling what to Join From** + + While :meth:`~.Query.join` exclusively deals with the "right" + side of the JOIN, we can also control the "left" side, in those + cases where it's needed, using :meth:`~.Query.select_from`. + Below we construct a query against ``Address`` but can still + make usage of ``User.addresses`` as our ON clause by instructing + the :class:`.Query` to select first from the ``User`` + entity:: + + q = session.query(Address).select_from(User).\ + join(User.addresses).\ + filter(User.name == 'ed') + + Which will produce SQL similar to:: + + SELECT address.* FROM user + JOIN address ON user.id=address.user_id + WHERE user.name = :name_1 + + **Constructing Aliases Anonymously** + + :meth:`~.Query.join` can construct anonymous aliases + using the ``aliased=True`` flag. This feature is useful + when a query is being joined algorithmically, such as + when querying self-referentially to an arbitrary depth:: + + q = session.query(Node).\ + join("children", "children", aliased=True) + + When ``aliased=True`` is used, the actual "alias" construct + is not explicitly available. To work with it, methods such as + :meth:`.Query.filter` will adapt the incoming entity to + the last join point:: + + q = session.query(Node).\ + join("children", "children", aliased=True).\ + filter(Node.name == 'grandchild 1') + + When using automatic aliasing, the ``from_joinpoint=True`` + argument can allow a multi-node join to be broken into + multiple calls to :meth:`~.Query.join`, so that + each path along the way can be further filtered:: + + q = session.query(Node).\ + join("children", aliased=True).\ + filter(Node.name='child 1').\ + join("children", aliased=True, from_joinpoint=True).\ + filter(Node.name == 'grandchild 1') + + The filtering aliases above can then be reset back to the + original ``Node`` entity using :meth:`~.Query.reset_joinpoint`:: + + q = session.query(Node).\ + join("children", "children", aliased=True).\ + filter(Node.name == 'grandchild 1').\ + reset_joinpoint().\ + filter(Node.name == 'parent 1) + + For an example of ``aliased=True``, see the distribution + example :ref:`examples_xmlpersistence` which illustrates + an XPath-like query system using algorithmic joins. + + :param \*props: A collection of one or more join conditions, + each consisting of a relationship-bound attribute or string + relationship name representing an "on clause", or a single + target entity, or a tuple in the form of ``(target, onclause)``. + A special two-argument calling form of the form ``target, onclause`` + is also accepted. + :param aliased=False: If True, indicate that the JOIN target should be + anonymously aliased. Subsequent calls to :meth:`~.Query.filter` + and similar will adapt the incoming criterion to the target + alias, until :meth:`~.Query.reset_joinpoint` is called. + :param isouter=False: If True, the join used will be a left outer join, + just as if the :meth:`.Query.outerjoin` method were called. This + flag is here to maintain consistency with the same flag as accepted + by :meth:`.FromClause.join` and other Core constructs. + + + .. versionadded:: 1.0.0 + + :param full=False: render FULL OUTER JOIN; implies ``isouter``. + + .. versionadded:: 1.1 + + :param from_joinpoint=False: When using ``aliased=True``, a setting + of True here will cause the join to be from the most recent + joined target, rather than starting back from the original + FROM clauses of the query. + + .. seealso:: + + :ref:`ormtutorial_joins` in the ORM tutorial. + + :ref:`inheritance_toplevel` for details on how + :meth:`~.Query.join` is used for inheritance relationships. + + :func:`.orm.join` - a standalone ORM-level join function, + used internally by :meth:`.Query.join`, which in previous + SQLAlchemy versions was the primary ORM-level joining interface. + + """ + aliased, from_joinpoint, isouter, full = kwargs.pop('aliased', False),\ + kwargs.pop('from_joinpoint', False),\ + kwargs.pop('isouter', False),\ + kwargs.pop('full', False) + if kwargs: + raise TypeError("unknown arguments: %s" % + ', '.join(sorted(kwargs))) + return self._join(props, + outerjoin=isouter, full=full, + create_aliases=aliased, + from_joinpoint=from_joinpoint) + + def outerjoin(self, *props, **kwargs): + """Create a left outer join against this ``Query`` object's criterion + and apply generatively, returning the newly resulting ``Query``. + + Usage is the same as the ``join()`` method. + + """ + aliased, from_joinpoint, full = kwargs.pop('aliased', False), \ + kwargs.pop('from_joinpoint', False), \ + kwargs.pop('full', False) + if kwargs: + raise TypeError("unknown arguments: %s" % + ', '.join(sorted(kwargs))) + return self._join(props, + outerjoin=True, full=full, create_aliases=aliased, + from_joinpoint=from_joinpoint) + + def _update_joinpoint(self, jp): + self._joinpoint = jp + # copy backwards to the root of the _joinpath + # dict, so that no existing dict in the path is mutated + while 'prev' in jp: + f, prev = jp['prev'] + prev = prev.copy() + prev[f] = jp + jp['prev'] = (f, prev) + jp = prev + self._joinpath = jp + + @_generative(_no_statement_condition, _no_limit_offset) + def _join(self, keys, outerjoin, full, create_aliases, from_joinpoint): + """consumes arguments from join() or outerjoin(), places them into a + consistent format with which to form the actual JOIN constructs. + + """ + + if not from_joinpoint: + self._reset_joinpoint() + + if len(keys) == 2 and \ + isinstance(keys[0], (expression.FromClause, + type, AliasedClass)) and \ + isinstance(keys[1], (str, expression.ClauseElement, + interfaces.PropComparator)): + # detect 2-arg form of join and + # convert to a tuple. + keys = (keys,) + + keylist = util.to_list(keys) + for idx, arg1 in enumerate(keylist): + if isinstance(arg1, tuple): + # "tuple" form of join, multiple + # tuples are accepted as well. The simpler + # "2-arg" form is preferred. May deprecate + # the "tuple" usage. + arg1, arg2 = arg1 + else: + arg2 = None + + # determine onclause/right_entity. there + # is a little bit of legacy behavior still at work here + # which means they might be in either order. may possibly + # lock this down to (right_entity, onclause) in 0.6. + if isinstance( + arg1, (interfaces.PropComparator, util.string_types)): + right_entity, onclause = arg2, arg1 + else: + right_entity, onclause = arg1, arg2 + + left_entity = prop = None + + if isinstance(onclause, interfaces.PropComparator): + of_type = getattr(onclause, '_of_type', None) + else: + of_type = None + + if isinstance(onclause, util.string_types): + left_entity = self._joinpoint_zero() + + descriptor = _entity_descriptor(left_entity, onclause) + onclause = descriptor + + # check for q.join(Class.propname, from_joinpoint=True) + # and Class is that of the current joinpoint + elif from_joinpoint and \ + isinstance(onclause, interfaces.PropComparator): + left_entity = onclause._parententity + + info = inspect(self._joinpoint_zero()) + left_mapper, left_selectable, left_is_aliased = \ + getattr(info, 'mapper', None), \ + info.selectable, \ + getattr(info, 'is_aliased_class', None) + + if left_mapper is left_entity: + left_entity = self._joinpoint_zero() + descriptor = _entity_descriptor(left_entity, + onclause.key) + onclause = descriptor + + if isinstance(onclause, interfaces.PropComparator): + if right_entity is None: + if of_type: + right_entity = of_type + else: + right_entity = onclause.property.mapper + + left_entity = onclause._parententity + + alias = self._polymorphic_adapters.get(left_entity, None) + # could be None or could be ColumnAdapter also + if isinstance(alias, ORMAdapter) and \ + alias.mapper.isa(left_entity): + left_entity = alias.aliased_class + onclause = getattr(left_entity, onclause.key) + + prop = onclause.property + if not isinstance(onclause, attributes.QueryableAttribute): + onclause = prop + + if not create_aliases: + # check for this path already present. + # don't render in that case. + edge = (left_entity, right_entity, prop.key) + if edge in self._joinpoint: + # The child's prev reference might be stale -- + # it could point to a parent older than the + # current joinpoint. If this is the case, + # then we need to update it and then fix the + # tree's spine with _update_joinpoint. Copy + # and then mutate the child, which might be + # shared by a different query object. + jp = self._joinpoint[edge].copy() + jp['prev'] = (edge, self._joinpoint) + self._update_joinpoint(jp) + + if idx == len(keylist) - 1: + util.warn( + "Pathed join target %s has already " + "been joined to; skipping" % prop) + continue + + elif onclause is not None and right_entity is None: + # TODO: no coverage here + raise NotImplementedError("query.join(a==b) not supported.") + + self._join_left_to_right( + left_entity, + right_entity, onclause, + outerjoin, full, create_aliases, prop) + + def _join_left_to_right(self, left, right, + onclause, outerjoin, full, create_aliases, prop): + """append a JOIN to the query's from clause.""" + + self._polymorphic_adapters = self._polymorphic_adapters.copy() + + if left is None: + if self._from_obj: + left = self._from_obj[0] + elif self._entities: + left = self._entities[0].entity_zero_or_selectable + + if left is None: + if self._entities: + problem = "Don't know how to join from %s" % self._entities[0] + else: + problem = "No entities to join from" + + raise sa_exc.InvalidRequestError( + "%s; please use " + "select_from() to establish the left " + "entity/selectable of this join" % problem) + + if left is right and \ + not create_aliases: + raise sa_exc.InvalidRequestError( + "Can't construct a join from %s to %s, they " + "are the same entity" % + (left, right)) + + l_info = inspect(left) + r_info = inspect(right) + + overlap = False + if not create_aliases: + right_mapper = getattr(r_info, "mapper", None) + # if the target is a joined inheritance mapping, + # be more liberal about auto-aliasing. + if right_mapper and ( + right_mapper.with_polymorphic or + isinstance(right_mapper.mapped_table, expression.Join) + ): + for from_obj in self._from_obj or [l_info.selectable]: + if sql_util.selectables_overlap( + l_info.selectable, from_obj) and \ + sql_util.selectables_overlap( + from_obj, r_info.selectable): + overlap = True + break + + if (overlap or not create_aliases) and \ + l_info.selectable is r_info.selectable: + raise sa_exc.InvalidRequestError( + "Can't join table/selectable '%s' to itself" % + l_info.selectable) + + right, onclause = self._prepare_right_side( + r_info, right, onclause, + create_aliases, + prop, overlap) + + # if joining on a MapperProperty path, + # track the path to prevent redundant joins + if not create_aliases and prop: + self._update_joinpoint({ + '_joinpoint_entity': right, + 'prev': ((left, right, prop.key), self._joinpoint) + }) + else: + self._joinpoint = {'_joinpoint_entity': right} + + self._join_to_left(l_info, left, right, onclause, outerjoin, full) + + def _prepare_right_side(self, r_info, right, onclause, create_aliases, + prop, overlap): + info = r_info + + right_mapper, right_selectable, right_is_aliased = \ + getattr(info, 'mapper', None), \ + info.selectable, \ + getattr(info, 'is_aliased_class', False) + + if right_mapper: + self._join_entities += (info, ) + + if right_mapper and prop and \ + not right_mapper.common_parent(prop.mapper): + raise sa_exc.InvalidRequestError( + "Join target %s does not correspond to " + "the right side of join condition %s" % (right, onclause) + ) + + if not right_mapper and prop: + right_mapper = prop.mapper + + need_adapter = False + + if r_info.is_clause_element and right_selectable._is_lateral: + # orm_only is disabled to suit the case where we have to + # adapt an explicit correlate(Entity) - the select() loses + # the ORM-ness in this case right now, ideally it would not + right = self._adapt_clause(right, True, False) + + if right_mapper and right is right_selectable: + if not right_selectable.is_derived_from( + right_mapper.mapped_table): + raise sa_exc.InvalidRequestError( + "Selectable '%s' is not derived from '%s'" % + (right_selectable.description, + right_mapper.mapped_table.description)) + + if isinstance(right_selectable, expression.SelectBase): + # TODO: this isn't even covered now! + right_selectable = right_selectable.alias() + need_adapter = True + + right = aliased(right_mapper, right_selectable) + + aliased_entity = right_mapper and \ + not right_is_aliased and \ + ( + right_mapper.with_polymorphic and isinstance( + right_mapper._with_polymorphic_selectable, + expression.Alias) + or + overlap # test for overlap: + # orm/inheritance/relationships.py + # SelfReferentialM2MTest + ) + + if not need_adapter and (create_aliases or aliased_entity): + right = aliased(right, flat=True) + need_adapter = True + + # if an alias() of the right side was generated here, + # apply an adapter to all subsequent filter() calls + # until reset_joinpoint() is called. + if need_adapter: + self._filter_aliases = ORMAdapter( + right, + equivalents=right_mapper and + right_mapper._equivalent_columns or {}, + chain_to=self._filter_aliases) + + # if the onclause is a ClauseElement, adapt it with any + # adapters that are in place right now + if isinstance(onclause, expression.ClauseElement): + onclause = self._adapt_clause(onclause, True, True) + + # if an alias() on the right side was generated, + # which is intended to wrap a the right side in a subquery, + # ensure that columns retrieved from this target in the result + # set are also adapted. + if aliased_entity and not create_aliases: + self._mapper_loads_polymorphically_with( + right_mapper, + ORMAdapter( + right, + equivalents=right_mapper._equivalent_columns + ) + ) + + return right, onclause + + def _join_to_left(self, l_info, left, right, onclause, outerjoin, full): + info = l_info + left_mapper = getattr(info, 'mapper', None) + left_selectable = info.selectable + + if self._from_obj: + replace_clause_index, clause = sql_util.find_join_source( + self._from_obj, + left_selectable) + if clause is not None: + try: + clause = orm_join(clause, + right, + onclause, isouter=outerjoin, full=full) + except sa_exc.ArgumentError as ae: + raise sa_exc.InvalidRequestError( + "Could not find a FROM clause to join from. " + "Tried joining to %s, but got: %s" % (right, ae)) + + self._from_obj = \ + self._from_obj[:replace_clause_index] + \ + (clause, ) + \ + self._from_obj[replace_clause_index + 1:] + return + + if left_mapper: + for ent in self._entities: + if ent.corresponds_to(left): + clause = ent.selectable + break + else: + clause = left + else: + clause = left_selectable + + assert clause is not None + try: + clause = orm_join( + clause, right, onclause, isouter=outerjoin, full=full) + except sa_exc.ArgumentError as ae: + raise sa_exc.InvalidRequestError( + "Could not find a FROM clause to join from. " + "Tried joining to %s, but got: %s" % (right, ae)) + self._from_obj = self._from_obj + (clause,) + + def _reset_joinpoint(self): + self._joinpoint = self._joinpath + self._filter_aliases = None + + @_generative(_no_statement_condition) + def reset_joinpoint(self): + """Return a new :class:`.Query`, where the "join point" has + been reset back to the base FROM entities of the query. + + This method is usually used in conjunction with the + ``aliased=True`` feature of the :meth:`~.Query.join` + method. See the example in :meth:`~.Query.join` for how + this is used. + + """ + self._reset_joinpoint() + + @_generative(_no_clauseelement_condition) + def select_from(self, *from_obj): + r"""Set the FROM clause of this :class:`.Query` explicitly. + + :meth:`.Query.select_from` is often used in conjunction with + :meth:`.Query.join` in order to control which entity is selected + from on the "left" side of the join. + + The entity or selectable object here effectively replaces the + "left edge" of any calls to :meth:`~.Query.join`, when no + joinpoint is otherwise established - usually, the default "join + point" is the leftmost entity in the :class:`~.Query` object's + list of entities to be selected. + + A typical example:: + + q = session.query(Address).select_from(User).\ + join(User.addresses).\ + filter(User.name == 'ed') + + Which produces SQL equivalent to:: + + SELECT address.* FROM user + JOIN address ON user.id=address.user_id + WHERE user.name = :name_1 + + :param \*from_obj: collection of one or more entities to apply + to the FROM clause. Entities can be mapped classes, + :class:`.AliasedClass` objects, :class:`.Mapper` objects + as well as core :class:`.FromClause` elements like subqueries. + + .. versionchanged:: 0.9 + This method no longer applies the given FROM object + to be the selectable from which matching entities + select from; the :meth:`.select_entity_from` method + now accomplishes this. See that method for a description + of this behavior. + + .. seealso:: + + :meth:`~.Query.join` + + :meth:`.Query.select_entity_from` + + """ + + self._set_select_from(from_obj, False) + + @_generative(_no_clauseelement_condition) + def select_entity_from(self, from_obj): + r"""Set the FROM clause of this :class:`.Query` to a + core selectable, applying it as a replacement FROM clause + for corresponding mapped entities. + + The :meth:`.Query.select_entity_from` method supplies an alternative + approach to the use case of applying an :func:`.aliased` construct + explicitly throughout a query. Instead of referring to the + :func:`.aliased` construct explicitly, + :meth:`.Query.select_entity_from` automatically *adapts* all occurences + of the entity to the target selectable. + + Given a case for :func:`.aliased` such as selecting ``User`` + objects from a SELECT statement:: + + select_stmt = select([User]).where(User.id == 7) + user_alias = aliased(User, select_stmt) + + q = session.query(user_alias).\ + filter(user_alias.name == 'ed') + + Above, we apply the ``user_alias`` object explicitly throughout the + query. When it's not feasible for ``user_alias`` to be referenced + explicitly in many places, :meth:`.Query.select_entity_from` may be + used at the start of the query to adapt the existing ``User`` entity:: + + q = session.query(User).\ + select_entity_from(select_stmt).\ + filter(User.name == 'ed') + + Above, the generated SQL will show that the ``User`` entity is + adapted to our statement, even in the case of the WHERE clause: + + .. sourcecode:: sql + + SELECT anon_1.id AS anon_1_id, anon_1.name AS anon_1_name + FROM (SELECT "user".id AS id, "user".name AS name + FROM "user" + WHERE "user".id = :id_1) AS anon_1 + WHERE anon_1.name = :name_1 + + The :meth:`.Query.select_entity_from` method is similar to the + :meth:`.Query.select_from` method, in that it sets the FROM clause + of the query. The difference is that it additionally applies + adaptation to the other parts of the query that refer to the + primary entity. If above we had used :meth:`.Query.select_from` + instead, the SQL generated would have been: + + .. sourcecode:: sql + + -- uses plain select_from(), not select_entity_from() + SELECT "user".id AS user_id, "user".name AS user_name + FROM "user", (SELECT "user".id AS id, "user".name AS name + FROM "user" + WHERE "user".id = :id_1) AS anon_1 + WHERE "user".name = :name_1 + + To supply textual SQL to the :meth:`.Query.select_entity_from` method, + we can make use of the :func:`.text` construct. However, the + :func:`.text` construct needs to be aligned with the columns of our + entity, which is achieved by making use of the + :meth:`.TextClause.columns` method:: + + text_stmt = text("select id, name from user").columns( + User.id, User.name) + q = session.query(User).select_entity_from(text_stmt) + + :meth:`.Query.select_entity_from` itself accepts an :func:`.aliased` + object, so that the special options of :func:`.aliased` such as + :paramref:`.aliased.adapt_on_names` may be used within the + scope of the :meth:`.Query.select_entity_from` method's adaptation + services. Suppose + a view ``user_view`` also returns rows from ``user``. If + we reflect this view into a :class:`.Table`, this view has no + relationship to the :class:`.Table` to which we are mapped, however + we can use name matching to select from it:: + + user_view = Table('user_view', metadata, + autoload_with=engine) + user_view_alias = aliased( + User, user_view, adapt_on_names=True) + q = session.query(User).\ + select_entity_from(user_view_alias).\ + order_by(User.name) + + .. versionchanged:: 1.1.7 The :meth:`.Query.select_entity_from` + method now accepts an :func:`.aliased` object as an alternative + to a :class:`.FromClause` object. + + :param from_obj: a :class:`.FromClause` object that will replace + the FROM clause of this :class:`.Query`. It also may be an instance + of :func:`.aliased`. + + + + .. seealso:: + + :meth:`.Query.select_from` + + """ + + self._set_select_from([from_obj], True) + + def __getitem__(self, item): + if isinstance(item, slice): + start, stop, step = util.decode_slice(item) + + if isinstance(stop, int) and \ + isinstance(start, int) and \ + stop - start <= 0: + return [] + + # perhaps we should execute a count() here so that we + # can still use LIMIT/OFFSET ? + elif (isinstance(start, int) and start < 0) \ + or (isinstance(stop, int) and stop < 0): + return list(self)[item] + + res = self.slice(start, stop) + if step is not None: + return list(res)[None:None:item.step] + else: + return list(res) + else: + if item == -1: + return list(self)[-1] + else: + return list(self[item:item + 1])[0] + + @_generative(_no_statement_condition) + def slice(self, start, stop): + """Computes the "slice" of the :class:`.Query` represented by + the given indices and returns the resulting :class:`.Query`. + + The start and stop indices behave like the argument to Python's + built-in :func:`range` function. This method provides an + alternative to using ``LIMIT``/``OFFSET`` to get a slice of the + query. + + For example, :: + + session.query(User).order_by(User.id).slice(1, 3) + + renders as + + .. sourcecode:: sql + + SELECT users.id AS users_id, + users.name AS users_name + FROM users ORDER BY users.id + LIMIT ? OFFSET ? + (2, 1) + + .. seealso:: + + :meth:`.Query.limit` + + :meth:`.Query.offset` + + """ + if start is not None and stop is not None: + self._offset = (self._offset or 0) + start + self._limit = stop - start + elif start is None and stop is not None: + self._limit = stop + elif start is not None and stop is None: + self._offset = (self._offset or 0) + start + + if self._offset == 0: + self._offset = None + + @_generative(_no_statement_condition) + def limit(self, limit): + """Apply a ``LIMIT`` to the query and return the newly resulting + ``Query``. + + """ + self._limit = limit + + @_generative(_no_statement_condition) + def offset(self, offset): + """Apply an ``OFFSET`` to the query and return the newly resulting + ``Query``. + + """ + self._offset = offset + + @_generative(_no_statement_condition) + def distinct(self, *criterion): + r"""Apply a ``DISTINCT`` to the query and return the newly resulting + ``Query``. + + + .. note:: + + The :meth:`.distinct` call includes logic that will automatically + add columns from the ORDER BY of the query to the columns + clause of the SELECT statement, to satisfy the common need + of the database backend that ORDER BY columns be part of the + SELECT list when DISTINCT is used. These columns *are not* + added to the list of columns actually fetched by the + :class:`.Query`, however, so would not affect results. + The columns are passed through when using the + :attr:`.Query.statement` accessor, however. + + :param \*expr: optional column expressions. When present, + the PostgreSQL dialect will render a ``DISTINCT ON (<expressions>>)`` + construct. + + """ + if not criterion: + self._distinct = True + else: + criterion = self._adapt_col_list(criterion) + if isinstance(self._distinct, list): + self._distinct += criterion + else: + self._distinct = criterion + + @_generative() + def prefix_with(self, *prefixes): + r"""Apply the prefixes to the query and return the newly resulting + ``Query``. + + :param \*prefixes: optional prefixes, typically strings, + not using any commas. In particular is useful for MySQL keywords. + + e.g.:: + + query = sess.query(User.name).\ + prefix_with('HIGH_PRIORITY').\ + prefix_with('SQL_SMALL_RESULT', 'ALL') + + Would render:: + + SELECT HIGH_PRIORITY SQL_SMALL_RESULT ALL users.name AS users_name + FROM users + + .. versionadded:: 0.7.7 + + .. seealso:: + + :meth:`.HasPrefixes.prefix_with` + + """ + if self._prefixes: + self._prefixes += prefixes + else: + self._prefixes = prefixes + + @_generative() + def suffix_with(self, *suffixes): + r"""Apply the suffix to the query and return the newly resulting + ``Query``. + + :param \*suffixes: optional suffixes, typically strings, + not using any commas. + + .. versionadded:: 1.0.0 + + .. seealso:: + + :meth:`.Query.prefix_with` + + :meth:`.HasSuffixes.suffix_with` + + """ + if self._suffixes: + self._suffixes += suffixes + else: + self._suffixes = suffixes + + def all(self): + """Return the results represented by this ``Query`` as a list. + + This results in an execution of the underlying query. + + """ + return list(self) + + @_generative(_no_clauseelement_condition) + def from_statement(self, statement): + """Execute the given SELECT statement and return results. + + This method bypasses all internal statement compilation, and the + statement is executed without modification. + + The statement is typically either a :func:`~.expression.text` + or :func:`~.expression.select` construct, and should return the set + of columns + appropriate to the entity class represented by this :class:`.Query`. + + .. seealso:: + + :ref:`orm_tutorial_literal_sql` - usage examples in the + ORM tutorial + + """ + statement = expression._expression_literal_as_text(statement) + + if not isinstance(statement, + (expression.TextClause, + expression.SelectBase)): + raise sa_exc.ArgumentError( + "from_statement accepts text(), select(), " + "and union() objects only.") + + self._statement = statement + + def first(self): + """Return the first result of this ``Query`` or + None if the result doesn't contain any row. + + first() applies a limit of one within the generated SQL, so that + only one primary entity row is generated on the server side + (note this may consist of multiple result rows if join-loaded + collections are present). + + Calling :meth:`.Query.first` results in an execution of the underlying query. + + .. seealso:: + + :meth:`.Query.one` + + :meth:`.Query.one_or_none` + + """ + if self._statement is not None: + ret = list(self)[0:1] + else: + ret = list(self[0:1]) + if len(ret) > 0: + return ret[0] + else: + return None + + def one_or_none(self): + """Return at most one result or raise an exception. + + Returns ``None`` if the query selects + no rows. Raises ``sqlalchemy.orm.exc.MultipleResultsFound`` + if multiple object identities are returned, or if multiple + rows are returned for a query that returns only scalar values + as opposed to full identity-mapped entities. + + Calling :meth:`.Query.one_or_none` results in an execution of the + underlying query. + + .. versionadded:: 1.0.9 + + Added :meth:`.Query.one_or_none` + + .. seealso:: + + :meth:`.Query.first` + + :meth:`.Query.one` + + """ + ret = list(self) + + l = len(ret) + if l == 1: + return ret[0] + elif l == 0: + return None + else: + raise orm_exc.MultipleResultsFound( + "Multiple rows were found for one_or_none()") + + def one(self): + """Return exactly one result or raise an exception. + + Raises ``sqlalchemy.orm.exc.NoResultFound`` if the query selects + no rows. Raises ``sqlalchemy.orm.exc.MultipleResultsFound`` + if multiple object identities are returned, or if multiple + rows are returned for a query that returns only scalar values + as opposed to full identity-mapped entities. + + Calling :meth:`.one` results in an execution of the underlying query. + + .. seealso:: + + :meth:`.Query.first` + + :meth:`.Query.one_or_none` + + """ + try: + ret = self.one_or_none() + except orm_exc.MultipleResultsFound: + raise orm_exc.MultipleResultsFound( + "Multiple rows were found for one()") + else: + if ret is None: + raise orm_exc.NoResultFound("No row was found for one()") + return ret + + def scalar(self): + """Return the first element of the first result or None + if no rows present. If multiple rows are returned, + raises MultipleResultsFound. + + >>> session.query(Item).scalar() + <Item> + >>> session.query(Item.id).scalar() + 1 + >>> session.query(Item.id).filter(Item.id < 0).scalar() + None + >>> session.query(Item.id, Item.name).scalar() + 1 + >>> session.query(func.count(Parent.id)).scalar() + 20 + + This results in an execution of the underlying query. + + """ + try: + ret = self.one() + if not isinstance(ret, tuple): + return ret + return ret[0] + except orm_exc.NoResultFound: + return None + + def __iter__(self): + context = self._compile_context() + context.statement.use_labels = True + if self._autoflush and not self._populate_existing: + self.session._autoflush() + return self._execute_and_instances(context) + + def __str__(self): + context = self._compile_context() + try: + bind = self._get_bind_args( + context, self.session.get_bind) if self.session else None + except sa_exc.UnboundExecutionError: + bind = None + return str(context.statement.compile(bind)) + + def _connection_from_session(self, **kw): + conn = self.session.connection(**kw) + if self._execution_options: + conn = conn.execution_options(**self._execution_options) + return conn + + def _execute_and_instances(self, querycontext): + conn = self._get_bind_args( + querycontext, + self._connection_from_session, + close_with_result=True) + + result = conn.execute(querycontext.statement, self._params) + return loading.instances(querycontext.query, result, querycontext) + + def _get_bind_args(self, querycontext, fn, **kw): + return fn( + mapper=self._bind_mapper(), + clause=querycontext.statement, + **kw + ) + + @property + def column_descriptions(self): + """Return metadata about the columns which would be + returned by this :class:`.Query`. + + Format is a list of dictionaries:: + + user_alias = aliased(User, name='user2') + q = sess.query(User, User.id, user_alias) + + # this expression: + q.column_descriptions + + # would return: + [ + { + 'name':'User', + 'type':User, + 'aliased':False, + 'expr':User, + 'entity': User + }, + { + 'name':'id', + 'type':Integer(), + 'aliased':False, + 'expr':User.id, + 'entity': User + }, + { + 'name':'user2', + 'type':User, + 'aliased':True, + 'expr':user_alias, + 'entity': user_alias + } + ] + + """ + + return [ + { + 'name': ent._label_name, + 'type': ent.type, + 'aliased': getattr(insp_ent, 'is_aliased_class', False), + 'expr': ent.expr, + 'entity': + getattr(insp_ent, "entity", None) + if ent.entity_zero is not None + and not insp_ent.is_clause_element + else None + } + for ent, insp_ent in [ + ( + _ent, + (inspect(_ent.entity_zero) + if _ent.entity_zero is not None else None) + ) + for _ent in self._entities + ] + ] + + def instances(self, cursor, __context=None): + """Given a ResultProxy cursor as returned by connection.execute(), + return an ORM result as an iterator. + + e.g.:: + + result = engine.execute("select * from users") + for u in session.query(User).instances(result): + print u + """ + context = __context + if context is None: + context = QueryContext(self) + + return loading.instances(self, cursor, context) + + def merge_result(self, iterator, load=True): + """Merge a result into this :class:`.Query` object's Session. + + Given an iterator returned by a :class:`.Query` of the same structure + as this one, return an identical iterator of results, with all mapped + instances merged into the session using :meth:`.Session.merge`. This + is an optimized method which will merge all mapped instances, + preserving the structure of the result rows and unmapped columns with + less method overhead than that of calling :meth:`.Session.merge` + explicitly for each value. + + The structure of the results is determined based on the column list of + this :class:`.Query` - if these do not correspond, unchecked errors + will occur. + + The 'load' argument is the same as that of :meth:`.Session.merge`. + + For an example of how :meth:`~.Query.merge_result` is used, see + the source code for the example :ref:`examples_caching`, where + :meth:`~.Query.merge_result` is used to efficiently restore state + from a cache back into a target :class:`.Session`. + + """ + + return loading.merge_result(self, iterator, load) + + @property + def _select_args(self): + return { + 'limit': self._limit, + 'offset': self._offset, + 'distinct': self._distinct, + 'prefixes': self._prefixes, + 'suffixes': self._suffixes, + 'group_by': self._group_by or None, + 'having': self._having + } + + @property + def _should_nest_selectable(self): + kwargs = self._select_args + return (kwargs.get('limit') is not None or + kwargs.get('offset') is not None or + kwargs.get('distinct', False)) + + def exists(self): + """A convenience method that turns a query into an EXISTS subquery + of the form EXISTS (SELECT 1 FROM ... WHERE ...). + + e.g.:: + + q = session.query(User).filter(User.name == 'fred') + session.query(q.exists()) + + Producing SQL similar to:: + + SELECT EXISTS ( + SELECT 1 FROM users WHERE users.name = :name_1 + ) AS anon_1 + + The EXISTS construct is usually used in the WHERE clause:: + + session.query(User.id).filter(q.exists()).scalar() + + Note that some databases such as SQL Server don't allow an + EXISTS expression to be present in the columns clause of a + SELECT. To select a simple boolean value based on the exists + as a WHERE, use :func:`.literal`:: + + from sqlalchemy import literal + + session.query(literal(True)).filter(q.exists()).scalar() + + .. versionadded:: 0.8.1 + + """ + + # .add_columns() for the case that we are a query().select_from(X), + # so that ".statement" can be produced (#2995) but also without + # omitting the FROM clause from a query(X) (#2818); + # .with_only_columns() after we have a core select() so that + # we get just "SELECT 1" without any entities. + return sql.exists(self.enable_eagerloads(False).add_columns('1'). + with_labels(). + statement.with_only_columns([1])) + + def count(self): + r"""Return a count of rows this Query would return. + + This generates the SQL for this Query as follows:: + + SELECT count(1) AS count_1 FROM ( + SELECT <rest of query follows...> + ) AS anon_1 + + .. versionchanged:: 0.7 + The above scheme is newly refined as of 0.7b3. + + For fine grained control over specific columns + to count, to skip the usage of a subquery or + otherwise control of the FROM clause, + or to use other aggregate functions, + use :attr:`~sqlalchemy.sql.expression.func` + expressions in conjunction + with :meth:`~.Session.query`, i.e.:: + + from sqlalchemy import func + + # count User records, without + # using a subquery. + session.query(func.count(User.id)) + + # return count of user "id" grouped + # by "name" + session.query(func.count(User.id)).\ + group_by(User.name) + + from sqlalchemy import distinct + + # count distinct "name" values + session.query(func.count(distinct(User.name))) + + """ + col = sql.func.count(sql.literal_column('*')) + return self.from_self(col).scalar() + + def delete(self, synchronize_session='evaluate'): + r"""Perform a bulk delete query. + + Deletes rows matched by this query from the database. + + E.g.:: + + sess.query(User).filter(User.age == 25).\ + delete(synchronize_session=False) + + sess.query(User).filter(User.age == 25).\ + delete(synchronize_session='evaluate') + + .. warning:: The :meth:`.Query.delete` method is a "bulk" operation, + which bypasses ORM unit-of-work automation in favor of greater + performance. **Please read all caveats and warnings below.** + + :param synchronize_session: chooses the strategy for the removal of + matched objects from the session. Valid values are: + + ``False`` - don't synchronize the session. This option is the most + efficient and is reliable once the session is expired, which + typically occurs after a commit(), or explicitly using + expire_all(). Before the expiration, objects may still remain in + the session which were in fact deleted which can lead to confusing + results if they are accessed via get() or already loaded + collections. + + ``'fetch'`` - performs a select query before the delete to find + objects that are matched by the delete query and need to be + removed from the session. Matched objects are removed from the + session. + + ``'evaluate'`` - Evaluate the query's criteria in Python straight + on the objects in the session. If evaluation of the criteria isn't + implemented, an error is raised. + + The expression evaluator currently doesn't account for differing + string collations between the database and Python. + + :return: the count of rows matched as returned by the database's + "row count" feature. + + .. warning:: **Additional Caveats for bulk query deletes** + + * This method does **not work for joined + inheritance mappings**, since the **multiple table + deletes are not supported by SQL** as well as that the + **join condition of an inheritance mapper is not + automatically rendered**. Care must be taken in any + multiple-table delete to first accommodate via some other means + how the related table will be deleted, as well as to + explicitly include the joining + condition between those tables, even in mappings where + this is normally automatic. E.g. if a class ``Engineer`` + subclasses ``Employee``, a DELETE against the ``Employee`` + table would look like:: + + session.query(Engineer).\ + filter(Engineer.id == Employee.id).\ + filter(Employee.name == 'dilbert').\ + delete() + + However the above SQL will not delete from the Engineer table, + unless an ON DELETE CASCADE rule is established in the database + to handle it. + + Short story, **do not use this method for joined inheritance + mappings unless you have taken the additional steps to make + this feasible**. + + * The polymorphic identity WHERE criteria is **not** included + for single- or + joined- table updates - this must be added **manually** even + for single table inheritance. + + * The method does **not** offer in-Python cascading of + relationships - it is assumed that ON DELETE CASCADE/SET + NULL/etc. is configured for any foreign key references + which require it, otherwise the database may emit an + integrity violation if foreign key references are being + enforced. + + After the DELETE, dependent objects in the + :class:`.Session` which were impacted by an ON DELETE + may not contain the current state, or may have been + deleted. This issue is resolved once the + :class:`.Session` is expired, which normally occurs upon + :meth:`.Session.commit` or can be forced by using + :meth:`.Session.expire_all`. Accessing an expired + object whose row has been deleted will invoke a SELECT + to locate the row; when the row is not found, an + :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is + raised. + + * The ``'fetch'`` strategy results in an additional + SELECT statement emitted and will significantly reduce + performance. + + * The ``'evaluate'`` strategy performs a scan of + all matching objects within the :class:`.Session`; if the + contents of the :class:`.Session` are expired, such as + via a proceeding :meth:`.Session.commit` call, **this will + result in SELECT queries emitted for every matching object**. + + * The :meth:`.MapperEvents.before_delete` and + :meth:`.MapperEvents.after_delete` + events **are not invoked** from this method. Instead, the + :meth:`.SessionEvents.after_bulk_delete` method is provided to + act upon a mass DELETE of entity rows. + + .. seealso:: + + :meth:`.Query.update` + + :ref:`inserts_and_updates` - Core SQL tutorial + + """ + + delete_op = persistence.BulkDelete.factory( + self, synchronize_session) + delete_op.exec_() + return delete_op.rowcount + + def update(self, values, synchronize_session='evaluate', update_args=None): + r"""Perform a bulk update query. + + Updates rows matched by this query in the database. + + E.g.:: + + sess.query(User).filter(User.age == 25).\ + update({User.age: User.age - 10}, synchronize_session=False) + + sess.query(User).filter(User.age == 25).\ + update({"age": User.age - 10}, synchronize_session='evaluate') + + + .. warning:: The :meth:`.Query.update` method is a "bulk" operation, + which bypasses ORM unit-of-work automation in favor of greater + performance. **Please read all caveats and warnings below.** + + + :param values: a dictionary with attributes names, or alternatively + mapped attributes or SQL expressions, as keys, and literal + values or sql expressions as values. If :ref:`parameter-ordered + mode <updates_order_parameters>` is desired, the values can be + passed as a list of 2-tuples; + this requires that the :paramref:`~sqlalchemy.sql.expression.update.preserve_parameter_order` + flag is passed to the :paramref:`.Query.update.update_args` dictionary + as well. + + .. versionchanged:: 1.0.0 - string names in the values dictionary + are now resolved against the mapped entity; previously, these + strings were passed as literal column names with no mapper-level + translation. + + :param synchronize_session: chooses the strategy to update the + attributes on objects in the session. Valid values are: + + ``False`` - don't synchronize the session. This option is the most + efficient and is reliable once the session is expired, which + typically occurs after a commit(), or explicitly using + expire_all(). Before the expiration, updated objects may still + remain in the session with stale values on their attributes, which + can lead to confusing results. + + ``'fetch'`` - performs a select query before the update to find + objects that are matched by the update query. The updated + attributes are expired on matched objects. + + ``'evaluate'`` - Evaluate the Query's criteria in Python straight + on the objects in the session. If evaluation of the criteria isn't + implemented, an exception is raised. + + The expression evaluator currently doesn't account for differing + string collations between the database and Python. + + :param update_args: Optional dictionary, if present will be passed + to the underlying :func:`.update` construct as the ``**kw`` for + the object. May be used to pass dialect-specific arguments such + as ``mysql_limit``, as well as other special arguments such as + :paramref:`~sqlalchemy.sql.expression.update.preserve_parameter_order`. + + .. versionadded:: 1.0.0 + + :return: the count of rows matched as returned by the database's + "row count" feature. + + .. warning:: **Additional Caveats for bulk query updates** + + * The method does **not** offer in-Python cascading of + relationships - it is assumed that ON UPDATE CASCADE is + configured for any foreign key references which require + it, otherwise the database may emit an integrity + violation if foreign key references are being enforced. + + After the UPDATE, dependent objects in the + :class:`.Session` which were impacted by an ON UPDATE + CASCADE may not contain the current state; this issue is + resolved once the :class:`.Session` is expired, which + normally occurs upon :meth:`.Session.commit` or can be + forced by using :meth:`.Session.expire_all`. + + * The ``'fetch'`` strategy results in an additional + SELECT statement emitted and will significantly reduce + performance. + + * The ``'evaluate'`` strategy performs a scan of + all matching objects within the :class:`.Session`; if the + contents of the :class:`.Session` are expired, such as + via a proceeding :meth:`.Session.commit` call, **this will + result in SELECT queries emitted for every matching object**. + + * The method supports multiple table updates, as detailed + in :ref:`multi_table_updates`, and this behavior does + extend to support updates of joined-inheritance and + other multiple table mappings. However, the **join + condition of an inheritance mapper is not + automatically rendered**. Care must be taken in any + multiple-table update to explicitly include the joining + condition between those tables, even in mappings where + this is normally automatic. E.g. if a class ``Engineer`` + subclasses ``Employee``, an UPDATE of the ``Engineer`` + local table using criteria against the ``Employee`` + local table might look like:: + + session.query(Engineer).\ + filter(Engineer.id == Employee.id).\ + filter(Employee.name == 'dilbert').\ + update({"engineer_type": "programmer"}) + + * The polymorphic identity WHERE criteria is **not** included + for single- or + joined- table updates - this must be added **manually**, even + for single table inheritance. + + * The :meth:`.MapperEvents.before_update` and + :meth:`.MapperEvents.after_update` + events **are not invoked from this method**. Instead, the + :meth:`.SessionEvents.after_bulk_update` method is provided to + act upon a mass UPDATE of entity rows. + + .. seealso:: + + :meth:`.Query.delete` + + :ref:`inserts_and_updates` - Core SQL tutorial + + """ + + update_args = update_args or {} + update_op = persistence.BulkUpdate.factory( + self, synchronize_session, values, update_args) + update_op.exec_() + return update_op.rowcount + + def _compile_context(self, labels=True): + if self.dispatch.before_compile: + for fn in self.dispatch.before_compile: + new_query = fn(self) + if new_query is not None: + self = new_query + + context = QueryContext(self) + + if context.statement is not None: + return context + + context.labels = labels + + context._for_update_arg = self._for_update_arg + + for entity in self._entities: + entity.setup_context(self, context) + + for rec in context.create_eager_joins: + strategy = rec[0] + strategy(*rec[1:]) + + if context.from_clause: + # "load from explicit FROMs" mode, + # i.e. when select_from() or join() is used + context.froms = list(context.from_clause) + # else "load from discrete FROMs" mode, + # i.e. when each _MappedEntity has its own FROM + + if self._enable_single_crit: + self._adjust_for_single_inheritance(context) + + if not context.primary_columns: + if self._only_load_props: + raise sa_exc.InvalidRequestError( + "No column-based properties specified for " + "refresh operation. Use session.expire() " + "to reload collections and related items.") + else: + raise sa_exc.InvalidRequestError( + "Query contains no columns with which to " + "SELECT from.") + + if context.multi_row_eager_loaders and self._should_nest_selectable: + context.statement = self._compound_eager_statement(context) + else: + context.statement = self._simple_statement(context) + + return context + + def _compound_eager_statement(self, context): + # for eager joins present and LIMIT/OFFSET/DISTINCT, + # wrap the query inside a select, + # then append eager joins onto that + + if context.order_by: + order_by_col_expr = \ + sql_util.expand_column_list_from_order_by( + context.primary_columns, + context.order_by + ) + else: + context.order_by = None + order_by_col_expr = [] + + inner = sql.select( + context.primary_columns + order_by_col_expr, + context.whereclause, + from_obj=context.froms, + use_labels=context.labels, + # TODO: this order_by is only needed if + # LIMIT/OFFSET is present in self._select_args, + # else the application on the outside is enough + order_by=context.order_by, + **self._select_args + ) + + for hint in self._with_hints: + inner = inner.with_hint(*hint) + + if self._correlate: + inner = inner.correlate(*self._correlate) + + inner = inner.alias() + + equivs = self.__all_equivs() + + context.adapter = sql_util.ColumnAdapter(inner, equivs) + + statement = sql.select( + [inner] + context.secondary_columns, + use_labels=context.labels) + + statement._for_update_arg = context._for_update_arg + + from_clause = inner + for eager_join in context.eager_joins.values(): + # EagerLoader places a 'stop_on' attribute on the join, + # giving us a marker as to where the "splice point" of + # the join should be + from_clause = sql_util.splice_joins( + from_clause, + eager_join, eager_join.stop_on) + + statement.append_from(from_clause) + + if context.order_by: + statement.append_order_by( + *context.adapter.copy_and_process( + context.order_by + ) + ) + + statement.append_order_by(*context.eager_order_by) + return statement + + def _simple_statement(self, context): + if not context.order_by: + context.order_by = None + + if self._distinct is True and context.order_by: + context.primary_columns += \ + sql_util.expand_column_list_from_order_by( + context.primary_columns, + context.order_by + ) + context.froms += tuple(context.eager_joins.values()) + + statement = sql.select( + context.primary_columns + + context.secondary_columns, + context.whereclause, + from_obj=context.froms, + use_labels=context.labels, + order_by=context.order_by, + **self._select_args + ) + statement._for_update_arg = context._for_update_arg + + for hint in self._with_hints: + statement = statement.with_hint(*hint) + + if self._correlate: + statement = statement.correlate(*self._correlate) + + if context.eager_order_by: + statement.append_order_by(*context.eager_order_by) + return statement + + def _adjust_for_single_inheritance(self, context): + """Apply single-table-inheritance filtering. + + For all distinct single-table-inheritance mappers represented in + the columns clause of this query, as well as the "select from entity", + add criterion to the WHERE + clause of the given QueryContext such that only the appropriate + subtypes are selected from the total results. + + """ + + search = set(self._mapper_adapter_map.values()) + if self._select_from_entity and \ + self._select_from_entity not in self._mapper_adapter_map: + insp = inspect(self._select_from_entity) + if insp.is_aliased_class: + adapter = insp._adapter + else: + adapter = None + search = search.union([(self._select_from_entity, adapter)]) + + for (ext_info, adapter) in search: + if ext_info in self._join_entities: + continue + single_crit = ext_info.mapper._single_table_criterion + if single_crit is not None: + if adapter: + single_crit = adapter.traverse(single_crit) + single_crit = self._adapt_clause(single_crit, False, False) + context.whereclause = sql.and_( + sql.True_._ifnone(context.whereclause), + single_crit) + + +from ..sql.selectable import ForUpdateArg + + +class LockmodeArg(ForUpdateArg): + @classmethod + def parse_legacy_query(self, mode): + if mode in (None, False): + return None + + if mode == "read": + read = True + nowait = False + elif mode == "update": + read = nowait = False + elif mode == "update_nowait": + nowait = True + read = False + else: + raise sa_exc.ArgumentError( + "Unknown with_lockmode argument: %r" % mode) + + return LockmodeArg(read=read, nowait=nowait) + + +class _QueryEntity(object): + """represent an entity column returned within a Query result.""" + + def __new__(cls, *args, **kwargs): + if cls is _QueryEntity: + entity = args[1] + if not isinstance(entity, util.string_types) and \ + _is_mapped_class(entity): + cls = _MapperEntity + elif isinstance(entity, Bundle): + cls = _BundleEntity + else: + cls = _ColumnEntity + return object.__new__(cls) + + def _clone(self): + q = self.__class__.__new__(self.__class__) + q.__dict__ = self.__dict__.copy() + return q + + +class _MapperEntity(_QueryEntity): + """mapper/class/AliasedClass entity""" + + def __init__(self, query, entity): + if not query._primary_entity: + query._primary_entity = self + query._entities.append(self) + query._has_mapper_entities = True + self.entities = [entity] + self.expr = entity + + supports_single_entity = True + + use_id_for_hash = True + + def setup_entity(self, ext_info, aliased_adapter): + self.mapper = ext_info.mapper + self.aliased_adapter = aliased_adapter + self.selectable = ext_info.selectable + self.is_aliased_class = ext_info.is_aliased_class + self._with_polymorphic = ext_info.with_polymorphic_mappers + self._polymorphic_discriminator = \ + ext_info.polymorphic_on + self.entity_zero = ext_info + if ext_info.is_aliased_class: + self._label_name = self.entity_zero.name + else: + self._label_name = self.mapper.class_.__name__ + self.path = self.entity_zero._path_registry + + def set_with_polymorphic(self, query, cls_or_mappers, + selectable, polymorphic_on): + """Receive an update from a call to query.with_polymorphic(). + + Note the newer style of using a free standing with_polymporphic() + construct doesn't make use of this method. + + + """ + if self.is_aliased_class: + # TODO: invalidrequest ? + raise NotImplementedError( + "Can't use with_polymorphic() against " + "an Aliased object" + ) + + if cls_or_mappers is None: + query._reset_polymorphic_adapter(self.mapper) + return + + mappers, from_obj = self.mapper._with_polymorphic_args( + cls_or_mappers, selectable) + self._with_polymorphic = mappers + self._polymorphic_discriminator = polymorphic_on + + self.selectable = from_obj + query._mapper_loads_polymorphically_with( + self.mapper, sql_util.ColumnAdapter( + from_obj, self.mapper._equivalent_columns)) + + @property + def type(self): + return self.mapper.class_ + + @property + def entity_zero_or_selectable(self): + return self.entity_zero + + def corresponds_to(self, entity): + return _entity_corresponds_to(self.entity_zero, entity) + + def adapt_to_selectable(self, query, sel): + query._entities.append(self) + + def _get_entity_clauses(self, query, context): + + adapter = None + + if not self.is_aliased_class: + if query._polymorphic_adapters: + adapter = query._polymorphic_adapters.get(self.mapper, None) + else: + adapter = self.aliased_adapter + + if adapter: + if query._from_obj_alias: + ret = adapter.wrap(query._from_obj_alias) + else: + ret = adapter + else: + ret = query._from_obj_alias + + return ret + + def row_processor(self, query, context, result): + adapter = self._get_entity_clauses(query, context) + + if context.adapter and adapter: + adapter = adapter.wrap(context.adapter) + elif not adapter: + adapter = context.adapter + + # polymorphic mappers which have concrete tables in + # their hierarchy usually + # require row aliasing unconditionally. + if not adapter and self.mapper._requires_row_aliasing: + adapter = sql_util.ColumnAdapter( + self.selectable, + self.mapper._equivalent_columns) + + if query._primary_entity is self: + only_load_props = query._only_load_props + refresh_state = context.refresh_state + else: + only_load_props = refresh_state = None + + _instance = loading._instance_processor( + self.mapper, + context, + result, + self.path, + adapter, + only_load_props=only_load_props, + refresh_state=refresh_state, + polymorphic_discriminator=self._polymorphic_discriminator + ) + + return _instance, self._label_name + + def setup_context(self, query, context): + adapter = self._get_entity_clauses(query, context) + + # if self._adapted_selectable is None: + context.froms += (self.selectable,) + + if context.order_by is False and self.mapper.order_by: + context.order_by = self.mapper.order_by + + # apply adaptation to the mapper's order_by if needed. + if adapter: + context.order_by = adapter.adapt_list( + util.to_list( + context.order_by + ) + ) + + loading._setup_entity_query( + context, self.mapper, self, + self.path, adapter, context.primary_columns, + with_polymorphic=self._with_polymorphic, + only_load_props=query._only_load_props, + polymorphic_discriminator=self._polymorphic_discriminator + ) + + def __str__(self): + return str(self.mapper) + + +@inspection._self_inspects +class Bundle(InspectionAttr): + """A grouping of SQL expressions that are returned by a :class:`.Query` + under one namespace. + + The :class:`.Bundle` essentially allows nesting of the tuple-based + results returned by a column-oriented :class:`.Query` object. It also + is extensible via simple subclassing, where the primary capability + to override is that of how the set of expressions should be returned, + allowing post-processing as well as custom return types, without + involving ORM identity-mapped classes. + + .. versionadded:: 0.9.0 + + .. seealso:: + + :ref:`bundles` + + """ + + single_entity = False + """If True, queries for a single Bundle will be returned as a single + entity, rather than an element within a keyed tuple.""" + + is_clause_element = False + + is_mapper = False + + is_aliased_class = False + + def __init__(self, name, *exprs, **kw): + r"""Construct a new :class:`.Bundle`. + + e.g.:: + + bn = Bundle("mybundle", MyClass.x, MyClass.y) + + for row in session.query(bn).filter( + bn.c.x == 5).filter(bn.c.y == 4): + print(row.mybundle.x, row.mybundle.y) + + :param name: name of the bundle. + :param \*exprs: columns or SQL expressions comprising the bundle. + :param single_entity=False: if True, rows for this :class:`.Bundle` + can be returned as a "single entity" outside of any enclosing tuple + in the same manner as a mapped entity. + + """ + self.name = self._label = name + self.exprs = exprs + self.c = self.columns = ColumnCollection() + self.columns.update((getattr(col, "key", col._label), col) + for col in exprs) + self.single_entity = kw.pop('single_entity', self.single_entity) + + columns = None + """A namespace of SQL expressions referred to by this :class:`.Bundle`. + + e.g.:: + + bn = Bundle("mybundle", MyClass.x, MyClass.y) + + q = sess.query(bn).filter(bn.c.x == 5) + + Nesting of bundles is also supported:: + + b1 = Bundle("b1", + Bundle('b2', MyClass.a, MyClass.b), + Bundle('b3', MyClass.x, MyClass.y) + ) + + q = sess.query(b1).filter( + b1.c.b2.c.a == 5).filter(b1.c.b3.c.y == 9) + + .. seealso:: + + :attr:`.Bundle.c` + + """ + + c = None + """An alias for :attr:`.Bundle.columns`.""" + + def _clone(self): + cloned = self.__class__.__new__(self.__class__) + cloned.__dict__.update(self.__dict__) + return cloned + + def __clause_element__(self): + return expression.ClauseList(group=False, *self.exprs) + + @property + def clauses(self): + return self.__clause_element__().clauses + + def label(self, name): + """Provide a copy of this :class:`.Bundle` passing a new label.""" + + cloned = self._clone() + cloned.name = name + return cloned + + def create_row_processor(self, query, procs, labels): + """Produce the "row processing" function for this :class:`.Bundle`. + + May be overridden by subclasses. + + .. seealso:: + + :ref:`bundles` - includes an example of subclassing. + + """ + keyed_tuple = util.lightweight_named_tuple('result', labels) + + def proc(row): + return keyed_tuple([proc(row) for proc in procs]) + return proc + + +class _BundleEntity(_QueryEntity): + use_id_for_hash = False + + def __init__(self, query, bundle, setup_entities=True): + query._entities.append(self) + self.bundle = self.expr = bundle + self.type = type(bundle) + self._label_name = bundle.name + self._entities = [] + + if setup_entities: + for expr in bundle.exprs: + if isinstance(expr, Bundle): + _BundleEntity(self, expr) + else: + _ColumnEntity(self, expr, namespace=self) + + self.supports_single_entity = self.bundle.single_entity + + @property + def mapper(self): + return self.entity_zero.mapper + + @property + def entities(self): + entities = [] + for ent in self._entities: + entities.extend(ent.entities) + return entities + + @property + def entity_zero(self): + for ent in self._entities: + ezero = ent.entity_zero + if ezero is not None: + return ezero + else: + return None + + def corresponds_to(self, entity): + # TODO: this seems to have no effect for + # _ColumnEntity either + return False + + @property + def entity_zero_or_selectable(self): + for ent in self._entities: + ezero = ent.entity_zero_or_selectable + if ezero is not None: + return ezero + else: + return None + + def adapt_to_selectable(self, query, sel): + c = _BundleEntity(query, self.bundle, setup_entities=False) + # c._label_name = self._label_name + # c.entity_zero = self.entity_zero + # c.entities = self.entities + + for ent in self._entities: + ent.adapt_to_selectable(c, sel) + + def setup_entity(self, ext_info, aliased_adapter): + for ent in self._entities: + ent.setup_entity(ext_info, aliased_adapter) + + def setup_context(self, query, context): + for ent in self._entities: + ent.setup_context(query, context) + + def row_processor(self, query, context, result): + procs, labels = zip( + *[ent.row_processor(query, context, result) + for ent in self._entities] + ) + + proc = self.bundle.create_row_processor(query, procs, labels) + + return proc, self._label_name + + +class _ColumnEntity(_QueryEntity): + """Column/expression based entity.""" + + def __init__(self, query, column, namespace=None): + self.expr = column + self.namespace = namespace + search_entities = True + check_column = False + + if isinstance(column, util.string_types): + column = sql.literal_column(column) + self._label_name = column.name + search_entities = False + check_column = True + _entity = None + elif isinstance(column, ( + attributes.QueryableAttribute, + interfaces.PropComparator + )): + _entity = getattr(column, '_parententity', None) + if _entity is not None: + search_entities = False + self._label_name = column.key + column = column._query_clause_element() + check_column = True + if isinstance(column, Bundle): + _BundleEntity(query, column) + return + + if not isinstance(column, sql.ColumnElement): + if hasattr(column, '_select_iterable'): + # break out an object like Table into + # individual columns + for c in column._select_iterable: + if c is column: + break + _ColumnEntity(query, c, namespace=column) + else: + return + + raise sa_exc.InvalidRequestError( + "SQL expression, column, or mapped entity " + "expected - got '%r'" % (column, ) + ) + elif not check_column: + self._label_name = getattr(column, 'key', None) + search_entities = True + + self.type = type_ = column.type + self.use_id_for_hash = not type_.hashable + + # If the Column is unnamed, give it a + # label() so that mutable column expressions + # can be located in the result even + # if the expression's identity has been changed + # due to adaption. + + if not column._label and not getattr(column, 'is_literal', False): + column = column.label(self._label_name) + + query._entities.append(self) + + self.column = column + self.froms = set() + + # look for ORM entities represented within the + # given expression. Try to count only entities + # for columns whose FROM object is in the actual list + # of FROMs for the overall expression - this helps + # subqueries which were built from ORM constructs from + # leaking out their entities into the main select construct + self.actual_froms = actual_froms = set(column._from_objects) + + if not search_entities: + self.entity_zero = _entity + if _entity: + self.entities = [_entity] + self.mapper = _entity.mapper + else: + self.entities = [] + self.mapper = None + self._from_entities = set(self.entities) + else: + all_elements = [ + elem for elem in sql_util.surface_column_elements( + column, include_scalar_selects=False) + if 'parententity' in elem._annotations + ] + + self.entities = util.unique_list([ + elem._annotations['parententity'] + for elem in all_elements + if 'parententity' in elem._annotations + ]) + + self._from_entities = set([ + elem._annotations['parententity'] + for elem in all_elements + if 'parententity' in elem._annotations + and actual_froms.intersection(elem._from_objects) + ]) + if self.entities: + self.entity_zero = self.entities[0] + self.mapper = self.entity_zero.mapper + elif self.namespace is not None: + self.entity_zero = self.namespace + self.mapper = None + else: + self.entity_zero = None + self.mapper = None + + supports_single_entity = False + + @property + def entity_zero_or_selectable(self): + if self.entity_zero is not None: + return self.entity_zero + elif self.actual_froms: + return list(self.actual_froms)[0] + else: + return None + + def adapt_to_selectable(self, query, sel): + c = _ColumnEntity(query, sel.corresponding_column(self.column)) + c._label_name = self._label_name + c.entity_zero = self.entity_zero + c.entities = self.entities + + def setup_entity(self, ext_info, aliased_adapter): + if 'selectable' not in self.__dict__: + self.selectable = ext_info.selectable + + if self.actual_froms.intersection(ext_info.selectable._from_objects): + self.froms.add(ext_info.selectable) + + def corresponds_to(self, entity): + # TODO: just returning False here, + # no tests fail + if self.entity_zero is None: + return False + elif _is_aliased_class(entity): + # TODO: polymorphic subclasses ? + return entity is self.entity_zero + else: + return not _is_aliased_class(self.entity_zero) and \ + entity.common_parent(self.entity_zero) + + def row_processor(self, query, context, result): + if ('fetch_column', self) in context.attributes: + column = context.attributes[('fetch_column', self)] + else: + column = query._adapt_clause(self.column, False, True) + + if column._annotations: + # annotated columns perform more slowly in compiler and + # result due to the __eq__() method, so use deannotated + column = column._deannotate() + + if context.adapter: + column = context.adapter.columns[column] + + getter = result._getter(column) + return getter, self._label_name + + def setup_context(self, query, context): + column = query._adapt_clause(self.column, False, True) + + if column._annotations: + # annotated columns perform more slowly in compiler and + # result due to the __eq__() method, so use deannotated + column = column._deannotate() + + context.froms += tuple(self.froms) + context.primary_columns.append(column) + + context.attributes[('fetch_column', self)] = column + + def __str__(self): + return str(self.column) + + +class QueryContext(object): + __slots__ = ( + 'multi_row_eager_loaders', 'adapter', 'froms', 'for_update', + 'query', 'session', 'autoflush', 'populate_existing', + 'invoke_all_eagers', 'version_check', 'refresh_state', + 'primary_columns', 'secondary_columns', 'eager_order_by', + 'eager_joins', 'create_eager_joins', 'propagate_options', + 'attributes', 'statement', 'from_clause', 'whereclause', + 'order_by', 'labels', '_for_update_arg', 'runid', 'partials', + 'post_load_paths', 'identity_token' + ) + + def __init__(self, query): + + if query._statement is not None: + if isinstance(query._statement, expression.SelectBase) and \ + not query._statement._textual and \ + not query._statement.use_labels: + self.statement = query._statement.apply_labels() + else: + self.statement = query._statement + else: + self.statement = None + self.from_clause = query._from_obj + self.whereclause = query._criterion + self.order_by = query._order_by + + self.multi_row_eager_loaders = False + self.adapter = None + self.froms = () + self.for_update = None + self.query = query + self.session = query.session + self.autoflush = query._autoflush + self.populate_existing = query._populate_existing + self.invoke_all_eagers = query._invoke_all_eagers + self.version_check = query._version_check + self.refresh_state = query._refresh_state + self.primary_columns = [] + self.secondary_columns = [] + self.eager_order_by = [] + self.eager_joins = {} + self.create_eager_joins = [] + self.propagate_options = set(o for o in query._with_options if + o.propagate_to_loaders) + self.attributes = query._attributes.copy() + if self.refresh_state is not None: + self.identity_token = query._refresh_identity_token + else: + self.identity_token = None + + +class AliasOption(interfaces.MapperOption): + + def __init__(self, alias): + r"""Return a :class:`.MapperOption` that will indicate to the :class:`.Query` + that the main table has been aliased. + + This is a seldom-used option to suit the + very rare case that :func:`.contains_eager` + is being used in conjunction with a user-defined SELECT + statement that aliases the parent table. E.g.:: + + # define an aliased UNION called 'ulist' + ulist = users.select(users.c.user_id==7).\ + union(users.select(users.c.user_id>7)).\ + alias('ulist') + + # add on an eager load of "addresses" + statement = ulist.outerjoin(addresses).\ + select().apply_labels() + + # create query, indicating "ulist" will be an + # alias for the main table, "addresses" + # property should be eager loaded + query = session.query(User).options( + contains_alias(ulist), + contains_eager(User.addresses)) + + # then get results via the statement + results = query.from_statement(statement).all() + + :param alias: is the string name of an alias, or a + :class:`~.sql.expression.Alias` object representing + the alias. + + """ + self.alias = alias + + def process_query(self, query): + if isinstance(self.alias, util.string_types): + alias = query._mapper_zero().mapped_table.alias(self.alias) + else: + alias = self.alias + query._from_obj_alias = sql_util.ColumnAdapter(alias) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/relationships.py b/venv/Lib/site-packages/sqlalchemy/orm/relationships.py new file mode 100644 index 0000000..151eebe --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/relationships.py @@ -0,0 +1,2913 @@ +# orm/relationships.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Heuristics related to join conditions as used in +:func:`.relationship`. + +Provides the :class:`.JoinCondition` object, which encapsulates +SQL annotation and aliasing behavior focused on the `primaryjoin` +and `secondaryjoin` aspects of :func:`.relationship`. + +""" +from __future__ import absolute_import +from .. import sql, util, exc as sa_exc, schema, log + +import weakref +from .util import CascadeOptions, _orm_annotate, _orm_deannotate +from . import dependency +from . import attributes +from ..sql.util import ( + ClauseAdapter, + join_condition, _shallow_annotate, visit_binary_product, + _deep_deannotate, selectables_overlap, adapt_criterion_to_null +) +from ..sql import operators, expression, visitors +from .interfaces import (MANYTOMANY, MANYTOONE, ONETOMANY, + StrategizedProperty, PropComparator) +from ..inspection import inspect +from . import mapper as mapperlib +import collections + + +def remote(expr): + """Annotate a portion of a primaryjoin expression + with a 'remote' annotation. + + See the section :ref:`relationship_custom_foreign` for a + description of use. + + .. versionadded:: 0.8 + + .. seealso:: + + :ref:`relationship_custom_foreign` + + :func:`.foreign` + + """ + return _annotate_columns(expression._clause_element_as_expr(expr), + {"remote": True}) + + +def foreign(expr): + """Annotate a portion of a primaryjoin expression + with a 'foreign' annotation. + + See the section :ref:`relationship_custom_foreign` for a + description of use. + + .. versionadded:: 0.8 + + .. seealso:: + + :ref:`relationship_custom_foreign` + + :func:`.remote` + + """ + + return _annotate_columns(expression._clause_element_as_expr(expr), + {"foreign": True}) + + +@log.class_logger +@util.langhelpers.dependency_for("sqlalchemy.orm.properties") +class RelationshipProperty(StrategizedProperty): + """Describes an object property that holds a single item or list + of items that correspond to a related database table. + + Public constructor is the :func:`.orm.relationship` function. + + See also: + + :ref:`relationship_config_toplevel` + + """ + + strategy_wildcard_key = 'relationship' + + _dependency_processor = None + + def __init__(self, argument, + secondary=None, primaryjoin=None, + secondaryjoin=None, + foreign_keys=None, + uselist=None, + order_by=False, + backref=None, + back_populates=None, + post_update=False, + cascade=False, extension=None, + viewonly=False, lazy="select", + collection_class=None, passive_deletes=False, + passive_updates=True, remote_side=None, + enable_typechecks=True, join_depth=None, + comparator_factory=None, + single_parent=False, innerjoin=False, + distinct_target_key=None, + doc=None, + active_history=False, + cascade_backrefs=True, + load_on_pending=False, + bake_queries=True, + _local_remote_pairs=None, + query_class=None, + info=None): + """Provide a relationship between two mapped classes. + + This corresponds to a parent-child or associative table relationship. + The constructed class is an instance of + :class:`.RelationshipProperty`. + + A typical :func:`.relationship`, used in a classical mapping:: + + mapper(Parent, properties={ + 'children': relationship(Child) + }) + + Some arguments accepted by :func:`.relationship` optionally accept a + callable function, which when called produces the desired value. + The callable is invoked by the parent :class:`.Mapper` at "mapper + initialization" time, which happens only when mappers are first used, + and is assumed to be after all mappings have been constructed. This + can be used to resolve order-of-declaration and other dependency + issues, such as if ``Child`` is declared below ``Parent`` in the same + file:: + + mapper(Parent, properties={ + "children":relationship(lambda: Child, + order_by=lambda: Child.id) + }) + + When using the :ref:`declarative_toplevel` extension, the Declarative + initializer allows string arguments to be passed to + :func:`.relationship`. These string arguments are converted into + callables that evaluate the string as Python code, using the + Declarative class-registry as a namespace. This allows the lookup of + related classes to be automatic via their string name, and removes the + need to import related classes at all into the local module space:: + + from sqlalchemy.ext.declarative import declarative_base + + Base = declarative_base() + + class Parent(Base): + __tablename__ = 'parent' + id = Column(Integer, primary_key=True) + children = relationship("Child", order_by="Child.id") + + .. seealso:: + + :ref:`relationship_config_toplevel` - Full introductory and + reference documentation for :func:`.relationship`. + + :ref:`orm_tutorial_relationship` - ORM tutorial introduction. + + :param argument: + a mapped class, or actual :class:`.Mapper` instance, representing + the target of the relationship. + + :paramref:`~.relationship.argument` may also be passed as a callable + function which is evaluated at mapper initialization time, and may + be passed as a Python-evaluable string when using Declarative. + + .. seealso:: + + :ref:`declarative_configuring_relationships` - further detail + on relationship configuration when using Declarative. + + :param secondary: + for a many-to-many relationship, specifies the intermediary + table, and is typically an instance of :class:`.Table`. + In less common circumstances, the argument may also be specified + as an :class:`.Alias` construct, or even a :class:`.Join` construct. + + :paramref:`~.relationship.secondary` may + also be passed as a callable function which is evaluated at + mapper initialization time. When using Declarative, it may also + be a string argument noting the name of a :class:`.Table` that is + present in the :class:`.MetaData` collection associated with the + parent-mapped :class:`.Table`. + + The :paramref:`~.relationship.secondary` keyword argument is + typically applied in the case where the intermediary :class:`.Table` + is not otherwise expressed in any direct class mapping. If the + "secondary" table is also explicitly mapped elsewhere (e.g. as in + :ref:`association_pattern`), one should consider applying the + :paramref:`~.relationship.viewonly` flag so that this + :func:`.relationship` is not used for persistence operations which + may conflict with those of the association object pattern. + + .. seealso:: + + :ref:`relationships_many_to_many` - Reference example of "many + to many". + + :ref:`orm_tutorial_many_to_many` - ORM tutorial introduction to + many-to-many relationships. + + :ref:`self_referential_many_to_many` - Specifics on using + many-to-many in a self-referential case. + + :ref:`declarative_many_to_many` - Additional options when using + Declarative. + + :ref:`association_pattern` - an alternative to + :paramref:`~.relationship.secondary` when composing association + table relationships, allowing additional attributes to be + specified on the association table. + + :ref:`composite_secondary_join` - a lesser-used pattern which + in some cases can enable complex :func:`.relationship` SQL + conditions to be used. + + .. versionadded:: 0.9.2 :paramref:`~.relationship.secondary` works + more effectively when referring to a :class:`.Join` instance. + + :param active_history=False: + When ``True``, indicates that the "previous" value for a + many-to-one reference should be loaded when replaced, if + not already loaded. Normally, history tracking logic for + simple many-to-ones only needs to be aware of the "new" + value in order to perform a flush. This flag is available + for applications that make use of + :func:`.attributes.get_history` which also need to know + the "previous" value of the attribute. + + :param backref: + indicates the string name of a property to be placed on the related + mapper's class that will handle this relationship in the other + direction. The other property will be created automatically + when the mappers are configured. Can also be passed as a + :func:`.backref` object to control the configuration of the + new relationship. + + .. seealso:: + + :ref:`relationships_backref` - Introductory documentation and + examples. + + :paramref:`~.relationship.back_populates` - alternative form + of backref specification. + + :func:`.backref` - allows control over :func:`.relationship` + configuration when using :paramref:`~.relationship.backref`. + + + :param back_populates: + Takes a string name and has the same meaning as + :paramref:`~.relationship.backref`, except the complementing + property is **not** created automatically, and instead must be + configured explicitly on the other mapper. The complementing + property should also indicate + :paramref:`~.relationship.back_populates` to this relationship to + ensure proper functioning. + + .. seealso:: + + :ref:`relationships_backref` - Introductory documentation and + examples. + + :paramref:`~.relationship.backref` - alternative form + of backref specification. + + :param bake_queries=True: + Use the :class:`.BakedQuery` cache to cache the construction of SQL + used in lazy loads. True by default. Set to False if the + join condition of the relationship has unusual features that + might not respond well to statement caching. + + .. versionchanged:: 1.2 + "Baked" loading is the default implementation for the "select", + a.k.a. "lazy" loading strategy for relationships. + + .. versionadded:: 1.0.0 + + .. seealso:: + + :ref:`baked_toplevel` + + :param cascade: + a comma-separated list of cascade rules which determines how + Session operations should be "cascaded" from parent to child. + This defaults to ``False``, which means the default cascade + should be used - this default cascade is ``"save-update, merge"``. + + The available cascades are ``save-update``, ``merge``, + ``expunge``, ``delete``, ``delete-orphan``, and ``refresh-expire``. + An additional option, ``all`` indicates shorthand for + ``"save-update, merge, refresh-expire, + expunge, delete"``, and is often used as in ``"all, delete-orphan"`` + to indicate that related objects should follow along with the + parent object in all cases, and be deleted when de-associated. + + .. seealso:: + + :ref:`unitofwork_cascades` - Full detail on each of the available + cascade options. + + :ref:`tutorial_delete_cascade` - Tutorial example describing + a delete cascade. + + :param cascade_backrefs=True: + a boolean value indicating if the ``save-update`` cascade should + operate along an assignment event intercepted by a backref. + When set to ``False``, the attribute managed by this relationship + will not cascade an incoming transient object into the session of a + persistent parent, if the event is received via backref. + + .. seealso:: + + :ref:`backref_cascade` - Full discussion and examples on how + the :paramref:`~.relationship.cascade_backrefs` option is used. + + :param collection_class: + a class or callable that returns a new list-holding object. will + be used in place of a plain list for storing elements. + + .. seealso:: + + :ref:`custom_collections` - Introductory documentation and + examples. + + :param comparator_factory: + a class which extends :class:`.RelationshipProperty.Comparator` + which provides custom SQL clause generation for comparison + operations. + + .. seealso:: + + :class:`.PropComparator` - some detail on redefining comparators + at this level. + + :ref:`custom_comparators` - Brief intro to this feature. + + + :param distinct_target_key=None: + Indicate if a "subquery" eager load should apply the DISTINCT + keyword to the innermost SELECT statement. When left as ``None``, + the DISTINCT keyword will be applied in those cases when the target + columns do not comprise the full primary key of the target table. + When set to ``True``, the DISTINCT keyword is applied to the + innermost SELECT unconditionally. + + It may be desirable to set this flag to False when the DISTINCT is + reducing performance of the innermost subquery beyond that of what + duplicate innermost rows may be causing. + + .. versionadded:: 0.8.3 - + :paramref:`~.relationship.distinct_target_key` allows the + subquery eager loader to apply a DISTINCT modifier to the + innermost SELECT. + + .. versionchanged:: 0.9.0 - + :paramref:`~.relationship.distinct_target_key` now defaults to + ``None``, so that the feature enables itself automatically for + those cases where the innermost query targets a non-unique + key. + + .. seealso:: + + :ref:`loading_toplevel` - includes an introduction to subquery + eager loading. + + :param doc: + docstring which will be applied to the resulting descriptor. + + :param extension: + an :class:`.AttributeExtension` instance, or list of extensions, + which will be prepended to the list of attribute listeners for + the resulting descriptor placed on the class. + + .. deprecated:: 0.7 Please see :class:`.AttributeEvents`. + + :param foreign_keys: + + a list of columns which are to be used as "foreign key" + columns, or columns which refer to the value in a remote + column, within the context of this :func:`.relationship` + object's :paramref:`~.relationship.primaryjoin` condition. + That is, if the :paramref:`~.relationship.primaryjoin` + condition of this :func:`.relationship` is ``a.id == + b.a_id``, and the values in ``b.a_id`` are required to be + present in ``a.id``, then the "foreign key" column of this + :func:`.relationship` is ``b.a_id``. + + In normal cases, the :paramref:`~.relationship.foreign_keys` + parameter is **not required.** :func:`.relationship` will + automatically determine which columns in the + :paramref:`~.relationship.primaryjoin` conditition are to be + considered "foreign key" columns based on those + :class:`.Column` objects that specify :class:`.ForeignKey`, + or are otherwise listed as referencing columns in a + :class:`.ForeignKeyConstraint` construct. + :paramref:`~.relationship.foreign_keys` is only needed when: + + 1. There is more than one way to construct a join from the local + table to the remote table, as there are multiple foreign key + references present. Setting ``foreign_keys`` will limit the + :func:`.relationship` to consider just those columns specified + here as "foreign". + + .. versionchanged:: 0.8 + A multiple-foreign key join ambiguity can be resolved by + setting the :paramref:`~.relationship.foreign_keys` + parameter alone, without the need to explicitly set + :paramref:`~.relationship.primaryjoin` as well. + + 2. The :class:`.Table` being mapped does not actually have + :class:`.ForeignKey` or :class:`.ForeignKeyConstraint` + constructs present, often because the table + was reflected from a database that does not support foreign key + reflection (MySQL MyISAM). + + 3. The :paramref:`~.relationship.primaryjoin` argument is used to + construct a non-standard join condition, which makes use of + columns or expressions that do not normally refer to their + "parent" column, such as a join condition expressed by a + complex comparison using a SQL function. + + The :func:`.relationship` construct will raise informative + error messages that suggest the use of the + :paramref:`~.relationship.foreign_keys` parameter when + presented with an ambiguous condition. In typical cases, + if :func:`.relationship` doesn't raise any exceptions, the + :paramref:`~.relationship.foreign_keys` parameter is usually + not needed. + + :paramref:`~.relationship.foreign_keys` may also be passed as a + callable function which is evaluated at mapper initialization time, + and may be passed as a Python-evaluable string when using + Declarative. + + .. seealso:: + + :ref:`relationship_foreign_keys` + + :ref:`relationship_custom_foreign` + + :func:`.foreign` - allows direct annotation of the "foreign" + columns within a :paramref:`~.relationship.primaryjoin` condition. + + .. versionadded:: 0.8 + The :func:`.foreign` annotation can also be applied + directly to the :paramref:`~.relationship.primaryjoin` + expression, which is an alternate, more specific system of + describing which columns in a particular + :paramref:`~.relationship.primaryjoin` should be considered + "foreign". + + :param info: Optional data dictionary which will be populated into the + :attr:`.MapperProperty.info` attribute of this object. + + .. versionadded:: 0.8 + + :param innerjoin=False: + when ``True``, joined eager loads will use an inner join to join + against related tables instead of an outer join. The purpose + of this option is generally one of performance, as inner joins + generally perform better than outer joins. + + This flag can be set to ``True`` when the relationship references an + object via many-to-one using local foreign keys that are not + nullable, or when the reference is one-to-one or a collection that + is guaranteed to have one or at least one entry. + + The option supports the same "nested" and "unnested" options as + that of :paramref:`.joinedload.innerjoin`. See that flag + for details on nested / unnested behaviors. + + .. seealso:: + + :paramref:`.joinedload.innerjoin` - the option as specified by + loader option, including detail on nesting behavior. + + :ref:`what_kind_of_loading` - Discussion of some details of + various loader options. + + + :param join_depth: + when non-``None``, an integer value indicating how many levels + deep "eager" loaders should join on a self-referring or cyclical + relationship. The number counts how many times the same Mapper + shall be present in the loading condition along a particular join + branch. When left at its default of ``None``, eager loaders + will stop chaining when they encounter a the same target mapper + which is already higher up in the chain. This option applies + both to joined- and subquery- eager loaders. + + .. seealso:: + + :ref:`self_referential_eager_loading` - Introductory documentation + and examples. + + :param lazy='select': specifies + how the related items should be loaded. Default value is + ``select``. Values include: + + * ``select`` - items should be loaded lazily when the property is + first accessed, using a separate SELECT statement, or identity map + fetch for simple many-to-one references. + + * ``immediate`` - items should be loaded as the parents are loaded, + using a separate SELECT statement, or identity map fetch for + simple many-to-one references. + + * ``joined`` - items should be loaded "eagerly" in the same query as + that of the parent, using a JOIN or LEFT OUTER JOIN. Whether + the join is "outer" or not is determined by the + :paramref:`~.relationship.innerjoin` parameter. + + * ``subquery`` - items should be loaded "eagerly" as the parents are + loaded, using one additional SQL statement, which issues a JOIN to + a subquery of the original statement, for each collection + requested. + + * ``selectin`` - items should be loaded "eagerly" as the parents + are loaded, using one or more additional SQL statements, which + issues a JOIN to the immediate parent object, specifying primary + key identifiers using an IN clause. + + .. versionadded:: 1.2 + + * ``noload`` - no loading should occur at any time. This is to + support "write-only" attributes, or attributes which are + populated in some manner specific to the application. + + * ``raise`` - lazy loading is disallowed; accessing + the attribute, if its value were not already loaded via eager + loading, will raise an :exc:`~sqlalchemy.exc.InvalidRequestError`. + This strategy can be used when objects are to be detached from + their attached :class:`.Session` after they are loaded. + + .. versionadded:: 1.1 + + * ``raise_on_sql`` - lazy loading that emits SQL is disallowed; + accessing the attribute, if its value were not already loaded via + eager loading, will raise an + :exc:`~sqlalchemy.exc.InvalidRequestError`, **if the lazy load + needs to emit SQL**. If the lazy load can pull the related value + from the identity map or determine that it should be None, the + value is loaded. This strategy can be used when objects will + remain associated with the attached :class:`.Session`, however + additional SELECT statements should be blocked. + + .. versionadded:: 1.1 + + * ``dynamic`` - the attribute will return a pre-configured + :class:`.Query` object for all read + operations, onto which further filtering operations can be + applied before iterating the results. See + the section :ref:`dynamic_relationship` for more details. + + * True - a synonym for 'select' + + * False - a synonym for 'joined' + + * None - a synonym for 'noload' + + .. seealso:: + + :doc:`/orm/loading_relationships` - Full documentation on relationship loader + configuration. + + :ref:`dynamic_relationship` - detail on the ``dynamic`` option. + + :ref:`collections_noload_raiseload` - notes on "noload" and "raise" + + :param load_on_pending=False: + Indicates loading behavior for transient or pending parent objects. + + When set to ``True``, causes the lazy-loader to + issue a query for a parent object that is not persistent, meaning it + has never been flushed. This may take effect for a pending object + when autoflush is disabled, or for a transient object that has been + "attached" to a :class:`.Session` but is not part of its pending + collection. + + The :paramref:`~.relationship.load_on_pending` flag does not improve + behavior when the ORM is used normally - object references should be + constructed at the object level, not at the foreign key level, so + that they are present in an ordinary way before a flush proceeds. + This flag is not not intended for general use. + + .. seealso:: + + :meth:`.Session.enable_relationship_loading` - this method + establishes "load on pending" behavior for the whole object, and + also allows loading on objects that remain transient or + detached. + + :param order_by: + indicates the ordering that should be applied when loading these + items. :paramref:`~.relationship.order_by` is expected to refer to + one of the :class:`.Column` objects to which the target class is + mapped, or the attribute itself bound to the target class which + refers to the column. + + :paramref:`~.relationship.order_by` may also be passed as a callable + function which is evaluated at mapper initialization time, and may + be passed as a Python-evaluable string when using Declarative. + + :param passive_deletes=False: + Indicates loading behavior during delete operations. + + A value of True indicates that unloaded child items should not + be loaded during a delete operation on the parent. Normally, + when a parent item is deleted, all child items are loaded so + that they can either be marked as deleted, or have their + foreign key to the parent set to NULL. Marking this flag as + True usually implies an ON DELETE <CASCADE|SET NULL> rule is in + place which will handle updating/deleting child rows on the + database side. + + Additionally, setting the flag to the string value 'all' will + disable the "nulling out" of the child foreign keys, when the parent + object is deleted and there is no delete or delete-orphan cascade + enabled. This is typically used when a triggering or error raise + scenario is in place on the database side. Note that the foreign + key attributes on in-session child objects will not be changed after + a flush occurs so this is a very special use-case setting. + Additionally, the "nulling out" will still occur if the child + object is de-associated with the parent. + + .. seealso:: + + :ref:`passive_deletes` - Introductory documentation + and examples. + + :param passive_updates=True: + Indicates the persistence behavior to take when a referenced + primary key value changes in place, indicating that the referencing + foreign key columns will also need their value changed. + + When True, it is assumed that ``ON UPDATE CASCADE`` is configured on + the foreign key in the database, and that the database will + handle propagation of an UPDATE from a source column to + dependent rows. When False, the SQLAlchemy :func:`.relationship` + construct will attempt to emit its own UPDATE statements to + modify related targets. However note that SQLAlchemy **cannot** + emit an UPDATE for more than one level of cascade. Also, + setting this flag to False is not compatible in the case where + the database is in fact enforcing referential integrity, unless + those constraints are explicitly "deferred", if the target backend + supports it. + + It is highly advised that an application which is employing + mutable primary keys keeps ``passive_updates`` set to True, + and instead uses the referential integrity features of the database + itself in order to handle the change efficiently and fully. + + .. seealso:: + + :ref:`passive_updates` - Introductory documentation and + examples. + + :paramref:`.mapper.passive_updates` - a similar flag which + takes effect for joined-table inheritance mappings. + + :param post_update: + this indicates that the relationship should be handled by a + second UPDATE statement after an INSERT or before a + DELETE. Currently, it also will issue an UPDATE after the + instance was UPDATEd as well, although this technically should + be improved. This flag is used to handle saving bi-directional + dependencies between two individual rows (i.e. each row + references the other), where it would otherwise be impossible to + INSERT or DELETE both rows fully since one row exists before the + other. Use this flag when a particular mapping arrangement will + incur two rows that are dependent on each other, such as a table + that has a one-to-many relationship to a set of child rows, and + also has a column that references a single child row within that + list (i.e. both tables contain a foreign key to each other). If + a flush operation returns an error that a "cyclical + dependency" was detected, this is a cue that you might want to + use :paramref:`~.relationship.post_update` to "break" the cycle. + + .. seealso:: + + :ref:`post_update` - Introductory documentation and examples. + + :param primaryjoin: + a SQL expression that will be used as the primary + join of this child object against the parent object, or in a + many-to-many relationship the join of the primary object to the + association table. By default, this value is computed based on the + foreign key relationships of the parent and child tables (or + association table). + + :paramref:`~.relationship.primaryjoin` may also be passed as a + callable function which is evaluated at mapper initialization time, + and may be passed as a Python-evaluable string when using + Declarative. + + .. seealso:: + + :ref:`relationship_primaryjoin` + + :param remote_side: + used for self-referential relationships, indicates the column or + list of columns that form the "remote side" of the relationship. + + :paramref:`.relationship.remote_side` may also be passed as a + callable function which is evaluated at mapper initialization time, + and may be passed as a Python-evaluable string when using + Declarative. + + .. versionchanged:: 0.8 + The :func:`.remote` annotation can also be applied + directly to the ``primaryjoin`` expression, which is an + alternate, more specific system of describing which columns in a + particular ``primaryjoin`` should be considered "remote". + + .. seealso:: + + :ref:`self_referential` - in-depth explanation of how + :paramref:`~.relationship.remote_side` + is used to configure self-referential relationships. + + :func:`.remote` - an annotation function that accomplishes the + same purpose as :paramref:`~.relationship.remote_side`, typically + when a custom :paramref:`~.relationship.primaryjoin` condition + is used. + + :param query_class: + a :class:`.Query` subclass that will be used as the base of the + "appender query" returned by a "dynamic" relationship, that + is, a relationship that specifies ``lazy="dynamic"`` or was + otherwise constructed using the :func:`.orm.dynamic_loader` + function. + + .. seealso:: + + :ref:`dynamic_relationship` - Introduction to "dynamic" + relationship loaders. + + :param secondaryjoin: + a SQL expression that will be used as the join of + an association table to the child object. By default, this value is + computed based on the foreign key relationships of the association + and child tables. + + :paramref:`~.relationship.secondaryjoin` may also be passed as a + callable function which is evaluated at mapper initialization time, + and may be passed as a Python-evaluable string when using + Declarative. + + .. seealso:: + + :ref:`relationship_primaryjoin` + + :param single_parent: + when True, installs a validator which will prevent objects + from being associated with more than one parent at a time. + This is used for many-to-one or many-to-many relationships that + should be treated either as one-to-one or one-to-many. Its usage + is optional, except for :func:`.relationship` constructs which + are many-to-one or many-to-many and also + specify the ``delete-orphan`` cascade option. The + :func:`.relationship` construct itself will raise an error + instructing when this option is required. + + .. seealso:: + + :ref:`unitofwork_cascades` - includes detail on when the + :paramref:`~.relationship.single_parent` flag may be appropriate. + + :param uselist: + a boolean that indicates if this property should be loaded as a + list or a scalar. In most cases, this value is determined + automatically by :func:`.relationship` at mapper configuration + time, based on the type and direction + of the relationship - one to many forms a list, many to one + forms a scalar, many to many is a list. If a scalar is desired + where normally a list would be present, such as a bi-directional + one-to-one relationship, set :paramref:`~.relationship.uselist` to + False. + + The :paramref:`~.relationship.uselist` flag is also available on an + existing :func:`.relationship` construct as a read-only attribute, + which can be used to determine if this :func:`.relationship` deals + with collections or scalar attributes:: + + >>> User.addresses.property.uselist + True + + .. seealso:: + + :ref:`relationships_one_to_one` - Introduction to the "one to + one" relationship pattern, which is typically when the + :paramref:`~.relationship.uselist` flag is needed. + + :param viewonly=False: + when set to True, the relationship is used only for loading objects, + and not for any persistence operation. A :func:`.relationship` + which specifies :paramref:`~.relationship.viewonly` can work + with a wider range of SQL operations within the + :paramref:`~.relationship.primaryjoin` condition, including + operations that feature the use of a variety of comparison operators + as well as SQL functions such as :func:`~.sql.expression.cast`. The + :paramref:`~.relationship.viewonly` flag is also of general use when + defining any kind of :func:`~.relationship` that doesn't represent + the full set of related objects, to prevent modifications of the + collection from resulting in persistence operations. + + + """ + super(RelationshipProperty, self).__init__() + + self.uselist = uselist + self.argument = argument + self.secondary = secondary + self.primaryjoin = primaryjoin + self.secondaryjoin = secondaryjoin + self.post_update = post_update + self.direction = None + self.viewonly = viewonly + self.lazy = lazy + self.single_parent = single_parent + self._user_defined_foreign_keys = foreign_keys + self.collection_class = collection_class + self.passive_deletes = passive_deletes + self.cascade_backrefs = cascade_backrefs + self.passive_updates = passive_updates + self.remote_side = remote_side + self.enable_typechecks = enable_typechecks + self.query_class = query_class + self.innerjoin = innerjoin + self.distinct_target_key = distinct_target_key + self.doc = doc + self.active_history = active_history + self.join_depth = join_depth + self.local_remote_pairs = _local_remote_pairs + self.extension = extension + self.bake_queries = bake_queries + self.load_on_pending = load_on_pending + self.comparator_factory = comparator_factory or \ + RelationshipProperty.Comparator + self.comparator = self.comparator_factory(self, None) + util.set_creation_order(self) + + if info is not None: + self.info = info + + self.strategy_key = (("lazy", self.lazy), ) + + self._reverse_property = set() + + self.cascade = cascade if cascade is not False \ + else "save-update, merge" + + self.order_by = order_by + + self.back_populates = back_populates + + if self.back_populates: + if backref: + raise sa_exc.ArgumentError( + "backref and back_populates keyword arguments " + "are mutually exclusive") + self.backref = None + else: + self.backref = backref + + def instrument_class(self, mapper): + attributes.register_descriptor( + mapper.class_, + self.key, + comparator=self.comparator_factory(self, mapper), + parententity=mapper, + doc=self.doc, + ) + + class Comparator(PropComparator): + """Produce boolean, comparison, and other operators for + :class:`.RelationshipProperty` attributes. + + See the documentation for :class:`.PropComparator` for a brief + overview of ORM level operator definition. + + See also: + + :class:`.PropComparator` + + :class:`.ColumnProperty.Comparator` + + :class:`.ColumnOperators` + + :ref:`types_operators` + + :attr:`.TypeEngine.comparator_factory` + + """ + + _of_type = None + + def __init__( + self, prop, parentmapper, adapt_to_entity=None, of_type=None): + """Construction of :class:`.RelationshipProperty.Comparator` + is internal to the ORM's attribute mechanics. + + """ + self.prop = prop + self._parententity = parentmapper + self._adapt_to_entity = adapt_to_entity + if of_type: + self._of_type = of_type + + def adapt_to_entity(self, adapt_to_entity): + return self.__class__(self.property, self._parententity, + adapt_to_entity=adapt_to_entity, + of_type=self._of_type) + + @util.memoized_property + def mapper(self): + """The target :class:`.Mapper` referred to by this + :class:`.RelationshipProperty.Comparator`. + + This is the "target" or "remote" side of the + :func:`.relationship`. + + """ + return self.property.mapper + + @util.memoized_property + def _parententity(self): + return self.property.parent + + def _source_selectable(self): + if self._adapt_to_entity: + return self._adapt_to_entity.selectable + else: + return self.property.parent._with_polymorphic_selectable + + def __clause_element__(self): + adapt_from = self._source_selectable() + if self._of_type: + of_type = inspect(self._of_type).mapper + else: + of_type = None + + pj, sj, source, dest, \ + secondary, target_adapter = self.property._create_joins( + source_selectable=adapt_from, + source_polymorphic=True, + of_type=of_type) + if sj is not None: + return pj & sj + else: + return pj + + def of_type(self, cls): + r"""Redefine this object in terms of a polymorphic subclass. + + See :meth:`.PropComparator.of_type` for an example. + + """ + return RelationshipProperty.Comparator( + self.property, + self._parententity, + adapt_to_entity=self._adapt_to_entity, + of_type=cls) + + def in_(self, other): + """Produce an IN clause - this is not implemented + for :func:`~.orm.relationship`-based attributes at this time. + + """ + raise NotImplementedError('in_() not yet supported for ' + 'relationships. For a simple ' + 'many-to-one, use in_() against ' + 'the set of foreign key values.') + + __hash__ = None + + def __eq__(self, other): + """Implement the ``==`` operator. + + In a many-to-one context, such as:: + + MyClass.some_prop == <some object> + + this will typically produce a + clause such as:: + + mytable.related_id == <some id> + + Where ``<some id>`` is the primary key of the given + object. + + The ``==`` operator provides partial functionality for non- + many-to-one comparisons: + + * Comparisons against collections are not supported. + Use :meth:`~.RelationshipProperty.Comparator.contains`. + * Compared to a scalar one-to-many, will produce a + clause that compares the target columns in the parent to + the given target. + * Compared to a scalar many-to-many, an alias + of the association table will be rendered as + well, forming a natural join that is part of the + main body of the query. This will not work for + queries that go beyond simple AND conjunctions of + comparisons, such as those which use OR. Use + explicit joins, outerjoins, or + :meth:`~.RelationshipProperty.Comparator.has` for + more comprehensive non-many-to-one scalar + membership tests. + * Comparisons against ``None`` given in a one-to-many + or many-to-many context produce a NOT EXISTS clause. + + """ + if isinstance(other, (util.NoneType, expression.Null)): + if self.property.direction in [ONETOMANY, MANYTOMANY]: + return ~self._criterion_exists() + else: + return _orm_annotate(self.property._optimized_compare( + None, adapt_source=self.adapter)) + elif self.property.uselist: + raise sa_exc.InvalidRequestError( + "Can't compare a collection to an object or collection; " + "use contains() to test for membership.") + else: + return _orm_annotate( + self.property._optimized_compare( + other, adapt_source=self.adapter)) + + def _criterion_exists(self, criterion=None, **kwargs): + if getattr(self, '_of_type', None): + info = inspect(self._of_type) + target_mapper, to_selectable, is_aliased_class = \ + info.mapper, info.selectable, info.is_aliased_class + if self.property._is_self_referential and not \ + is_aliased_class: + to_selectable = to_selectable.alias() + + single_crit = target_mapper._single_table_criterion + if single_crit is not None: + if criterion is not None: + criterion = single_crit & criterion + else: + criterion = single_crit + else: + is_aliased_class = False + to_selectable = None + + if self.adapter: + source_selectable = self._source_selectable() + else: + source_selectable = None + + pj, sj, source, dest, secondary, target_adapter = \ + self.property._create_joins( + dest_polymorphic=True, + dest_selectable=to_selectable, + source_selectable=source_selectable) + + for k in kwargs: + crit = getattr(self.property.mapper.class_, k) == kwargs[k] + if criterion is None: + criterion = crit + else: + criterion = criterion & crit + + # annotate the *local* side of the join condition, in the case + # of pj + sj this is the full primaryjoin, in the case of just + # pj its the local side of the primaryjoin. + if sj is not None: + j = _orm_annotate(pj) & sj + else: + j = _orm_annotate(pj, exclude=self.property.remote_side) + + if criterion is not None and target_adapter and not \ + is_aliased_class: + # limit this adapter to annotated only? + criterion = target_adapter.traverse(criterion) + + # only have the "joined left side" of what we + # return be subject to Query adaption. The right + # side of it is used for an exists() subquery and + # should not correlate or otherwise reach out + # to anything in the enclosing query. + if criterion is not None: + criterion = criterion._annotate( + {'no_replacement_traverse': True}) + + crit = j & sql.True_._ifnone(criterion) + + if secondary is not None: + ex = sql.exists([1], crit, from_obj=[dest, secondary]).\ + correlate_except(dest, secondary) + else: + ex = sql.exists([1], crit, from_obj=dest).\ + correlate_except(dest) + return ex + + def any(self, criterion=None, **kwargs): + """Produce an expression that tests a collection against + particular criterion, using EXISTS. + + An expression like:: + + session.query(MyClass).filter( + MyClass.somereference.any(SomeRelated.x==2) + ) + + + Will produce a query like:: + + SELECT * FROM my_table WHERE + EXISTS (SELECT 1 FROM related WHERE related.my_id=my_table.id + AND related.x=2) + + Because :meth:`~.RelationshipProperty.Comparator.any` uses + a correlated subquery, its performance is not nearly as + good when compared against large target tables as that of + using a join. + + :meth:`~.RelationshipProperty.Comparator.any` is particularly + useful for testing for empty collections:: + + session.query(MyClass).filter( + ~MyClass.somereference.any() + ) + + will produce:: + + SELECT * FROM my_table WHERE + NOT EXISTS (SELECT 1 FROM related WHERE + related.my_id=my_table.id) + + :meth:`~.RelationshipProperty.Comparator.any` is only + valid for collections, i.e. a :func:`.relationship` + that has ``uselist=True``. For scalar references, + use :meth:`~.RelationshipProperty.Comparator.has`. + + """ + if not self.property.uselist: + raise sa_exc.InvalidRequestError( + "'any()' not implemented for scalar " + "attributes. Use has()." + ) + + return self._criterion_exists(criterion, **kwargs) + + def has(self, criterion=None, **kwargs): + """Produce an expression that tests a scalar reference against + particular criterion, using EXISTS. + + An expression like:: + + session.query(MyClass).filter( + MyClass.somereference.has(SomeRelated.x==2) + ) + + + Will produce a query like:: + + SELECT * FROM my_table WHERE + EXISTS (SELECT 1 FROM related WHERE + related.id==my_table.related_id AND related.x=2) + + Because :meth:`~.RelationshipProperty.Comparator.has` uses + a correlated subquery, its performance is not nearly as + good when compared against large target tables as that of + using a join. + + :meth:`~.RelationshipProperty.Comparator.has` is only + valid for scalar references, i.e. a :func:`.relationship` + that has ``uselist=False``. For collection references, + use :meth:`~.RelationshipProperty.Comparator.any`. + + """ + if self.property.uselist: + raise sa_exc.InvalidRequestError( + "'has()' not implemented for collections. " + "Use any().") + return self._criterion_exists(criterion, **kwargs) + + def contains(self, other, **kwargs): + """Return a simple expression that tests a collection for + containment of a particular item. + + :meth:`~.RelationshipProperty.Comparator.contains` is + only valid for a collection, i.e. a + :func:`~.orm.relationship` that implements + one-to-many or many-to-many with ``uselist=True``. + + When used in a simple one-to-many context, an + expression like:: + + MyClass.contains(other) + + Produces a clause like:: + + mytable.id == <some id> + + Where ``<some id>`` is the value of the foreign key + attribute on ``other`` which refers to the primary + key of its parent object. From this it follows that + :meth:`~.RelationshipProperty.Comparator.contains` is + very useful when used with simple one-to-many + operations. + + For many-to-many operations, the behavior of + :meth:`~.RelationshipProperty.Comparator.contains` + has more caveats. The association table will be + rendered in the statement, producing an "implicit" + join, that is, includes multiple tables in the FROM + clause which are equated in the WHERE clause:: + + query(MyClass).filter(MyClass.contains(other)) + + Produces a query like:: + + SELECT * FROM my_table, my_association_table AS + my_association_table_1 WHERE + my_table.id = my_association_table_1.parent_id + AND my_association_table_1.child_id = <some id> + + Where ``<some id>`` would be the primary key of + ``other``. From the above, it is clear that + :meth:`~.RelationshipProperty.Comparator.contains` + will **not** work with many-to-many collections when + used in queries that move beyond simple AND + conjunctions, such as multiple + :meth:`~.RelationshipProperty.Comparator.contains` + expressions joined by OR. In such cases subqueries or + explicit "outer joins" will need to be used instead. + See :meth:`~.RelationshipProperty.Comparator.any` for + a less-performant alternative using EXISTS, or refer + to :meth:`.Query.outerjoin` as well as :ref:`ormtutorial_joins` + for more details on constructing outer joins. + + """ + if not self.property.uselist: + raise sa_exc.InvalidRequestError( + "'contains' not implemented for scalar " + "attributes. Use ==") + clause = self.property._optimized_compare( + other, adapt_source=self.adapter) + + if self.property.secondaryjoin is not None: + clause.negation_clause = \ + self.__negated_contains_or_equals(other) + + return clause + + def __negated_contains_or_equals(self, other): + if self.property.direction == MANYTOONE: + state = attributes.instance_state(other) + + def state_bindparam(x, state, col): + dict_ = state.dict + return sql.bindparam( + x, unique=True, + callable_=self.property._get_attr_w_warn_on_none( + col, + self.property.mapper._get_state_attr_by_column, + state, dict_, col, passive=attributes.PASSIVE_OFF + ) + ) + + def adapt(col): + if self.adapter: + return self.adapter(col) + else: + return col + + if self.property._use_get: + return sql.and_(*[ + sql.or_( + adapt(x) != state_bindparam(adapt(x), state, y), + adapt(x) == None) + for (x, y) in self.property.local_remote_pairs]) + + criterion = sql.and_(*[ + x == y for (x, y) in + zip( + self.property.mapper.primary_key, + self.property.mapper.primary_key_from_instance(other) + ) + ]) + + return ~self._criterion_exists(criterion) + + def __ne__(self, other): + """Implement the ``!=`` operator. + + In a many-to-one context, such as:: + + MyClass.some_prop != <some object> + + This will typically produce a clause such as:: + + mytable.related_id != <some id> + + Where ``<some id>`` is the primary key of the + given object. + + The ``!=`` operator provides partial functionality for non- + many-to-one comparisons: + + * Comparisons against collections are not supported. + Use + :meth:`~.RelationshipProperty.Comparator.contains` + in conjunction with :func:`~.expression.not_`. + * Compared to a scalar one-to-many, will produce a + clause that compares the target columns in the parent to + the given target. + * Compared to a scalar many-to-many, an alias + of the association table will be rendered as + well, forming a natural join that is part of the + main body of the query. This will not work for + queries that go beyond simple AND conjunctions of + comparisons, such as those which use OR. Use + explicit joins, outerjoins, or + :meth:`~.RelationshipProperty.Comparator.has` in + conjunction with :func:`~.expression.not_` for + more comprehensive non-many-to-one scalar + membership tests. + * Comparisons against ``None`` given in a one-to-many + or many-to-many context produce an EXISTS clause. + + """ + if isinstance(other, (util.NoneType, expression.Null)): + if self.property.direction == MANYTOONE: + return _orm_annotate(~self.property._optimized_compare( + None, adapt_source=self.adapter)) + + else: + return self._criterion_exists() + elif self.property.uselist: + raise sa_exc.InvalidRequestError( + "Can't compare a collection" + " to an object or collection; use " + "contains() to test for membership.") + else: + return _orm_annotate(self.__negated_contains_or_equals(other)) + + @util.memoized_property + def property(self): + if mapperlib.Mapper._new_mappers: + mapperlib.Mapper._configure_all() + return self.prop + + def _with_parent(self, instance, alias_secondary=True, from_entity=None): + assert instance is not None + adapt_source = None + if from_entity is not None: + insp = inspect(from_entity) + if insp.is_aliased_class: + adapt_source = insp._adapter.adapt_clause + return self._optimized_compare( + instance, value_is_parent=True, adapt_source=adapt_source, + alias_secondary=alias_secondary) + + def _optimized_compare(self, state, value_is_parent=False, + adapt_source=None, + alias_secondary=True): + if state is not None: + state = attributes.instance_state(state) + + reverse_direction = not value_is_parent + + if state is None: + return self._lazy_none_clause( + reverse_direction, + adapt_source=adapt_source) + + if not reverse_direction: + criterion, bind_to_col = \ + self._lazy_strategy._lazywhere, \ + self._lazy_strategy._bind_to_col + else: + criterion, bind_to_col = \ + self._lazy_strategy._rev_lazywhere, \ + self._lazy_strategy._rev_bind_to_col + + if reverse_direction: + mapper = self.mapper + else: + mapper = self.parent + + dict_ = attributes.instance_dict(state.obj()) + + def visit_bindparam(bindparam): + if bindparam._identifying_key in bind_to_col: + bindparam.callable = self._get_attr_w_warn_on_none( + bind_to_col[bindparam._identifying_key], + mapper._get_state_attr_by_column, + state, dict_, + bind_to_col[bindparam._identifying_key], + passive=attributes.PASSIVE_OFF) + + if self.secondary is not None and alias_secondary: + criterion = ClauseAdapter( + self.secondary.alias()).\ + traverse(criterion) + + criterion = visitors.cloned_traverse( + criterion, {}, {'bindparam': visit_bindparam}) + + if adapt_source: + criterion = adapt_source(criterion) + return criterion + + def _get_attr_w_warn_on_none(self, column, fn, *arg, **kw): + def _go(): + value = fn(*arg, **kw) + if value is None: + util.warn( + "Got None for value of column %s; this is unsupported " + "for a relationship comparison and will not " + "currently produce an IS comparison " + "(but may in a future release)" % column) + return value + return _go + + def _lazy_none_clause(self, reverse_direction=False, adapt_source=None): + if not reverse_direction: + criterion, bind_to_col = \ + self._lazy_strategy._lazywhere, \ + self._lazy_strategy._bind_to_col + else: + criterion, bind_to_col = \ + self._lazy_strategy._rev_lazywhere, \ + self._lazy_strategy._rev_bind_to_col + + criterion = adapt_criterion_to_null(criterion, bind_to_col) + + if adapt_source: + criterion = adapt_source(criterion) + return criterion + + def __str__(self): + return str(self.parent.class_.__name__) + "." + self.key + + def merge(self, + session, + source_state, + source_dict, + dest_state, + dest_dict, + load, _recursive, _resolve_conflict_map): + + if load: + for r in self._reverse_property: + if (source_state, r) in _recursive: + return + + if "merge" not in self._cascade: + return + + if self.key not in source_dict: + return + + if self.uselist: + instances = source_state.get_impl(self.key).\ + get(source_state, source_dict) + if hasattr(instances, '_sa_adapter'): + # convert collections to adapters to get a true iterator + instances = instances._sa_adapter + + if load: + # for a full merge, pre-load the destination collection, + # so that individual _merge of each item pulls from identity + # map for those already present. + # also assumes CollectionAttrbiuteImpl behavior of loading + # "old" list in any case + dest_state.get_impl(self.key).get(dest_state, dest_dict) + + dest_list = [] + for current in instances: + current_state = attributes.instance_state(current) + current_dict = attributes.instance_dict(current) + _recursive[(current_state, self)] = True + obj = session._merge( + current_state, current_dict, + load=load, _recursive=_recursive, + _resolve_conflict_map=_resolve_conflict_map) + if obj is not None: + dest_list.append(obj) + + if not load: + coll = attributes.init_state_collection(dest_state, + dest_dict, self.key) + for c in dest_list: + coll.append_without_event(c) + else: + dest_state.get_impl(self.key).set( + dest_state, dest_dict, dest_list, + _adapt=False) + else: + current = source_dict[self.key] + if current is not None: + current_state = attributes.instance_state(current) + current_dict = attributes.instance_dict(current) + _recursive[(current_state, self)] = True + obj = session._merge( + current_state, current_dict, + load=load, _recursive=_recursive, + _resolve_conflict_map=_resolve_conflict_map) + else: + obj = None + + if not load: + dest_dict[self.key] = obj + else: + dest_state.get_impl(self.key).set(dest_state, + dest_dict, obj, None) + + def _value_as_iterable(self, state, dict_, key, + passive=attributes.PASSIVE_OFF): + """Return a list of tuples (state, obj) for the given + key. + + returns an empty list if the value is None/empty/PASSIVE_NO_RESULT + """ + + impl = state.manager[key].impl + x = impl.get(state, dict_, passive=passive) + if x is attributes.PASSIVE_NO_RESULT or x is None: + return [] + elif hasattr(impl, 'get_collection'): + return [ + (attributes.instance_state(o), o) for o in + impl.get_collection(state, dict_, x, passive=passive) + ] + else: + return [(attributes.instance_state(x), x)] + + def cascade_iterator(self, type_, state, dict_, + visited_states, halt_on=None): + # assert type_ in self._cascade + + # only actively lazy load on the 'delete' cascade + if type_ != 'delete' or self.passive_deletes: + passive = attributes.PASSIVE_NO_INITIALIZE + else: + passive = attributes.PASSIVE_OFF + + if type_ == 'save-update': + tuples = state.manager[self.key].impl.\ + get_all_pending(state, dict_) + + else: + tuples = self._value_as_iterable(state, dict_, self.key, + passive=passive) + + skip_pending = type_ == 'refresh-expire' and 'delete-orphan' \ + not in self._cascade + + for instance_state, c in tuples: + if instance_state in visited_states: + continue + + if c is None: + # would like to emit a warning here, but + # would not be consistent with collection.append(None) + # current behavior of silently skipping. + # see [ticket:2229] + continue + + instance_dict = attributes.instance_dict(c) + + if halt_on and halt_on(instance_state): + continue + + if skip_pending and not instance_state.key: + continue + + instance_mapper = instance_state.manager.mapper + + if not instance_mapper.isa(self.mapper.class_manager.mapper): + raise AssertionError("Attribute '%s' on class '%s' " + "doesn't handle objects " + "of type '%s'" % ( + self.key, + self.parent.class_, + c.__class__ + )) + + visited_states.add(instance_state) + + yield c, instance_mapper, instance_state, instance_dict + + def _add_reverse_property(self, key): + other = self.mapper.get_property(key, _configure_mappers=False) + self._reverse_property.add(other) + other._reverse_property.add(self) + + if not other.mapper.common_parent(self.parent): + raise sa_exc.ArgumentError( + 'reverse_property %r on ' + 'relationship %s references relationship %s, which ' + 'does not reference mapper %s' % + (key, self, other, self.parent)) + + if self.direction in (ONETOMANY, MANYTOONE) and self.direction \ + == other.direction: + raise sa_exc.ArgumentError( + '%s and back-reference %s are ' + 'both of the same direction %r. Did you mean to ' + 'set remote_side on the many-to-one side ?' % + (other, self, self.direction)) + + @util.memoized_property + def mapper(self): + """Return the targeted :class:`.Mapper` for this + :class:`.RelationshipProperty`. + + This is a lazy-initializing static attribute. + + """ + if util.callable(self.argument) and \ + not isinstance(self.argument, (type, mapperlib.Mapper)): + argument = self.argument() + else: + argument = self.argument + + if isinstance(argument, type): + mapper_ = mapperlib.class_mapper(argument, + configure=False) + elif isinstance(self.argument, mapperlib.Mapper): + mapper_ = argument + else: + raise sa_exc.ArgumentError( + "relationship '%s' expects " + "a class or a mapper argument (received: %s)" + % (self.key, type(argument))) + return mapper_ + + @util.memoized_property + @util.deprecated("0.7", "Use .target") + def table(self): + """Return the selectable linked to this + :class:`.RelationshipProperty` object's target + :class:`.Mapper`. + """ + return self.target + + def do_init(self): + self._check_conflicts() + self._process_dependent_arguments() + self._setup_join_conditions() + self._check_cascade_settings(self._cascade) + self._post_init() + self._generate_backref() + self._join_condition._warn_for_conflicting_sync_targets() + super(RelationshipProperty, self).do_init() + self._lazy_strategy = self._get_strategy((("lazy", "select"),)) + + def _process_dependent_arguments(self): + """Convert incoming configuration arguments to their + proper form. + + Callables are resolved, ORM annotations removed. + + """ + # accept callables for other attributes which may require + # deferred initialization. This technique is used + # by declarative "string configs" and some recipes. + for attr in ( + 'order_by', 'primaryjoin', 'secondaryjoin', + 'secondary', '_user_defined_foreign_keys', 'remote_side', + ): + attr_value = getattr(self, attr) + if util.callable(attr_value): + setattr(self, attr, attr_value()) + + # remove "annotations" which are present if mapped class + # descriptors are used to create the join expression. + for attr in 'primaryjoin', 'secondaryjoin': + val = getattr(self, attr) + if val is not None: + setattr(self, attr, _orm_deannotate( + expression._only_column_elements(val, attr)) + ) + + # ensure expressions in self.order_by, foreign_keys, + # remote_side are all columns, not strings. + if self.order_by is not False and self.order_by is not None: + self.order_by = [ + expression._only_column_elements(x, "order_by") + for x in + util.to_list(self.order_by)] + + self._user_defined_foreign_keys = \ + util.column_set( + expression._only_column_elements(x, "foreign_keys") + for x in util.to_column_set( + self._user_defined_foreign_keys + )) + + self.remote_side = \ + util.column_set( + expression._only_column_elements(x, "remote_side") + for x in + util.to_column_set(self.remote_side)) + + self.target = self.mapper.mapped_table + + def _setup_join_conditions(self): + self._join_condition = jc = JoinCondition( + parent_selectable=self.parent.mapped_table, + child_selectable=self.mapper.mapped_table, + parent_local_selectable=self.parent.local_table, + child_local_selectable=self.mapper.local_table, + primaryjoin=self.primaryjoin, + secondary=self.secondary, + secondaryjoin=self.secondaryjoin, + parent_equivalents=self.parent._equivalent_columns, + child_equivalents=self.mapper._equivalent_columns, + consider_as_foreign_keys=self._user_defined_foreign_keys, + local_remote_pairs=self.local_remote_pairs, + remote_side=self.remote_side, + self_referential=self._is_self_referential, + prop=self, + support_sync=not self.viewonly, + can_be_synced_fn=self._columns_are_mapped + ) + self.primaryjoin = jc.primaryjoin + self.secondaryjoin = jc.secondaryjoin + self.direction = jc.direction + self.local_remote_pairs = jc.local_remote_pairs + self.remote_side = jc.remote_columns + self.local_columns = jc.local_columns + self.synchronize_pairs = jc.synchronize_pairs + self._calculated_foreign_keys = jc.foreign_key_columns + self.secondary_synchronize_pairs = jc.secondary_synchronize_pairs + + def _check_conflicts(self): + """Test that this relationship is legal, warn about + inheritance conflicts.""" + + if self.parent.non_primary and not mapperlib.class_mapper( + self.parent.class_, + configure=False).has_property(self.key): + raise sa_exc.ArgumentError( + "Attempting to assign a new " + "relationship '%s' to a non-primary mapper on " + "class '%s'. New relationships can only be added " + "to the primary mapper, i.e. the very first mapper " + "created for class '%s' " % + (self.key, self.parent.class_.__name__, + self.parent.class_.__name__)) + + def _get_cascade(self): + """Return the current cascade setting for this + :class:`.RelationshipProperty`. + """ + return self._cascade + + def _set_cascade(self, cascade): + cascade = CascadeOptions(cascade) + if 'mapper' in self.__dict__: + self._check_cascade_settings(cascade) + self._cascade = cascade + + if self._dependency_processor: + self._dependency_processor.cascade = cascade + + cascade = property(_get_cascade, _set_cascade) + + def _check_cascade_settings(self, cascade): + if cascade.delete_orphan and not self.single_parent \ + and (self.direction is MANYTOMANY or self.direction + is MANYTOONE): + raise sa_exc.ArgumentError( + 'On %s, delete-orphan cascade is not supported ' + 'on a many-to-many or many-to-one relationship ' + 'when single_parent is not set. Set ' + 'single_parent=True on the relationship().' + % self) + if self.direction is MANYTOONE and self.passive_deletes: + util.warn("On %s, 'passive_deletes' is normally configured " + "on one-to-many, one-to-one, many-to-many " + "relationships only." + % self) + + if self.passive_deletes == 'all' and \ + ("delete" in cascade or + "delete-orphan" in cascade): + raise sa_exc.ArgumentError( + "On %s, can't set passive_deletes='all' in conjunction " + "with 'delete' or 'delete-orphan' cascade" % self) + + if cascade.delete_orphan: + self.mapper.primary_mapper()._delete_orphans.append( + (self.key, self.parent.class_) + ) + + def _persists_for(self, mapper): + """Return True if this property will persist values on behalf + of the given mapper. + + """ + + return self.key in mapper.relationships and \ + mapper.relationships[self.key] is self + + def _columns_are_mapped(self, *cols): + """Return True if all columns in the given collection are + mapped by the tables referenced by this :class:`.Relationship`. + + """ + for c in cols: + if self.secondary is not None \ + and self.secondary.c.contains_column(c): + continue + if not self.parent.mapped_table.c.contains_column(c) and \ + not self.target.c.contains_column(c): + return False + return True + + def _generate_backref(self): + """Interpret the 'backref' instruction to create a + :func:`.relationship` complementary to this one.""" + + if self.parent.non_primary: + return + if self.backref is not None and not self.back_populates: + if isinstance(self.backref, util.string_types): + backref_key, kwargs = self.backref, {} + else: + backref_key, kwargs = self.backref + mapper = self.mapper.primary_mapper() + + if not mapper.concrete: + check = set(mapper.iterate_to_root()).\ + union(mapper.self_and_descendants) + for m in check: + if m.has_property(backref_key) and not m.concrete: + raise sa_exc.ArgumentError( + "Error creating backref " + "'%s' on relationship '%s': property of that " + "name exists on mapper '%s'" % + (backref_key, self, m)) + + # determine primaryjoin/secondaryjoin for the + # backref. Use the one we had, so that + # a custom join doesn't have to be specified in + # both directions. + if self.secondary is not None: + # for many to many, just switch primaryjoin/ + # secondaryjoin. use the annotated + # pj/sj on the _join_condition. + pj = kwargs.pop( + 'primaryjoin', + self._join_condition.secondaryjoin_minus_local) + sj = kwargs.pop( + 'secondaryjoin', + self._join_condition.primaryjoin_minus_local) + else: + pj = kwargs.pop( + 'primaryjoin', + self._join_condition.primaryjoin_reverse_remote) + sj = kwargs.pop('secondaryjoin', None) + if sj: + raise sa_exc.InvalidRequestError( + "Can't assign 'secondaryjoin' on a backref " + "against a non-secondary relationship." + ) + + foreign_keys = kwargs.pop('foreign_keys', + self._user_defined_foreign_keys) + parent = self.parent.primary_mapper() + kwargs.setdefault('viewonly', self.viewonly) + kwargs.setdefault('post_update', self.post_update) + kwargs.setdefault('passive_updates', self.passive_updates) + self.back_populates = backref_key + relationship = RelationshipProperty( + parent, self.secondary, + pj, sj, + foreign_keys=foreign_keys, + back_populates=self.key, + **kwargs) + mapper._configure_property(backref_key, relationship) + + if self.back_populates: + self._add_reverse_property(self.back_populates) + + def _post_init(self): + if self.uselist is None: + self.uselist = self.direction is not MANYTOONE + if not self.viewonly: + self._dependency_processor = \ + dependency.DependencyProcessor.from_relationship(self) + + @util.memoized_property + def _use_get(self): + """memoize the 'use_get' attribute of this RelationshipLoader's + lazyloader.""" + + strategy = self._lazy_strategy + return strategy.use_get + + @util.memoized_property + def _is_self_referential(self): + return self.mapper.common_parent(self.parent) + + def _create_joins(self, source_polymorphic=False, + source_selectable=None, dest_polymorphic=False, + dest_selectable=None, of_type=None): + if source_selectable is None: + if source_polymorphic and self.parent.with_polymorphic: + source_selectable = self.parent._with_polymorphic_selectable + + aliased = False + if dest_selectable is None: + if dest_polymorphic and self.mapper.with_polymorphic: + dest_selectable = self.mapper._with_polymorphic_selectable + aliased = True + else: + dest_selectable = self.mapper.mapped_table + + if self._is_self_referential and source_selectable is None: + dest_selectable = dest_selectable.alias() + aliased = True + else: + aliased = True + + dest_mapper = of_type or self.mapper + + single_crit = dest_mapper._single_table_criterion + aliased = aliased or (source_selectable is not None) + + primaryjoin, secondaryjoin, secondary, target_adapter, dest_selectable = \ + self._join_condition.join_targets( + source_selectable, dest_selectable, aliased, single_crit + ) + if source_selectable is None: + source_selectable = self.parent.local_table + if dest_selectable is None: + dest_selectable = self.mapper.local_table + return (primaryjoin, secondaryjoin, source_selectable, + dest_selectable, secondary, target_adapter) + + +def _annotate_columns(element, annotations): + def clone(elem): + if isinstance(elem, expression.ColumnClause): + elem = elem._annotate(annotations.copy()) + elem._copy_internals(clone=clone) + return elem + + if element is not None: + element = clone(element) + return element + + +class JoinCondition(object): + def __init__(self, + parent_selectable, + child_selectable, + parent_local_selectable, + child_local_selectable, + primaryjoin=None, + secondary=None, + secondaryjoin=None, + parent_equivalents=None, + child_equivalents=None, + consider_as_foreign_keys=None, + local_remote_pairs=None, + remote_side=None, + self_referential=False, + prop=None, + support_sync=True, + can_be_synced_fn=lambda *c: True + ): + self.parent_selectable = parent_selectable + self.parent_local_selectable = parent_local_selectable + self.child_selectable = child_selectable + self.child_local_selectable = child_local_selectable + self.parent_equivalents = parent_equivalents + self.child_equivalents = child_equivalents + self.primaryjoin = primaryjoin + self.secondaryjoin = secondaryjoin + self.secondary = secondary + self.consider_as_foreign_keys = consider_as_foreign_keys + self._local_remote_pairs = local_remote_pairs + self._remote_side = remote_side + self.prop = prop + self.self_referential = self_referential + self.support_sync = support_sync + self.can_be_synced_fn = can_be_synced_fn + self._determine_joins() + self._sanitize_joins() + self._annotate_fks() + self._annotate_remote() + self._annotate_local() + self._annotate_parentmapper() + self._setup_pairs() + self._check_foreign_cols(self.primaryjoin, True) + if self.secondaryjoin is not None: + self._check_foreign_cols(self.secondaryjoin, False) + self._determine_direction() + self._check_remote_side() + self._log_joins() + + def _log_joins(self): + if self.prop is None: + return + log = self.prop.logger + log.info('%s setup primary join %s', self.prop, + self.primaryjoin) + log.info('%s setup secondary join %s', self.prop, + self.secondaryjoin) + log.info('%s synchronize pairs [%s]', self.prop, + ','.join('(%s => %s)' % (l, r) for (l, r) in + self.synchronize_pairs)) + log.info('%s secondary synchronize pairs [%s]', self.prop, + ','.join('(%s => %s)' % (l, r) for (l, r) in + self.secondary_synchronize_pairs or [])) + log.info('%s local/remote pairs [%s]', self.prop, + ','.join('(%s / %s)' % (l, r) for (l, r) in + self.local_remote_pairs)) + log.info('%s remote columns [%s]', self.prop, + ','.join('%s' % col for col in self.remote_columns) + ) + log.info('%s local columns [%s]', self.prop, + ','.join('%s' % col for col in self.local_columns) + ) + log.info('%s relationship direction %s', self.prop, + self.direction) + + def _sanitize_joins(self): + """remove the parententity annotation from our join conditions which + can leak in here based on some declarative patterns and maybe others. + + We'd want to remove "parentmapper" also, but apparently there's + an exotic use case in _join_fixture_inh_selfref_w_entity + that relies upon it being present, see :ticket:`3364`. + + """ + + self.primaryjoin = _deep_deannotate( + self.primaryjoin, values=("parententity",)) + if self.secondaryjoin is not None: + self.secondaryjoin = _deep_deannotate( + self.secondaryjoin, values=("parententity",)) + + def _determine_joins(self): + """Determine the 'primaryjoin' and 'secondaryjoin' attributes, + if not passed to the constructor already. + + This is based on analysis of the foreign key relationships + between the parent and target mapped selectables. + + """ + if self.secondaryjoin is not None and self.secondary is None: + raise sa_exc.ArgumentError( + "Property %s specified with secondary " + "join condition but " + "no secondary argument" % self.prop) + + # find a join between the given mapper's mapped table and + # the given table. will try the mapper's local table first + # for more specificity, then if not found will try the more + # general mapped table, which in the case of inheritance is + # a join. + try: + consider_as_foreign_keys = self.consider_as_foreign_keys or None + if self.secondary is not None: + if self.secondaryjoin is None: + self.secondaryjoin = \ + join_condition( + self.child_selectable, + self.secondary, + a_subset=self.child_local_selectable, + consider_as_foreign_keys=consider_as_foreign_keys + ) + if self.primaryjoin is None: + self.primaryjoin = \ + join_condition( + self.parent_selectable, + self.secondary, + a_subset=self.parent_local_selectable, + consider_as_foreign_keys=consider_as_foreign_keys + ) + else: + if self.primaryjoin is None: + self.primaryjoin = \ + join_condition( + self.parent_selectable, + self.child_selectable, + a_subset=self.parent_local_selectable, + consider_as_foreign_keys=consider_as_foreign_keys + ) + except sa_exc.NoForeignKeysError: + if self.secondary is not None: + raise sa_exc.NoForeignKeysError( + "Could not determine join " + "condition between parent/child tables on " + "relationship %s - there are no foreign keys " + "linking these tables via secondary table '%s'. " + "Ensure that referencing columns are associated " + "with a ForeignKey or ForeignKeyConstraint, or " + "specify 'primaryjoin' and 'secondaryjoin' " + "expressions." % (self.prop, self.secondary)) + else: + raise sa_exc.NoForeignKeysError( + "Could not determine join " + "condition between parent/child tables on " + "relationship %s - there are no foreign keys " + "linking these tables. " + "Ensure that referencing columns are associated " + "with a ForeignKey or ForeignKeyConstraint, or " + "specify a 'primaryjoin' expression." % self.prop) + except sa_exc.AmbiguousForeignKeysError: + if self.secondary is not None: + raise sa_exc.AmbiguousForeignKeysError( + "Could not determine join " + "condition between parent/child tables on " + "relationship %s - there are multiple foreign key " + "paths linking the tables via secondary table '%s'. " + "Specify the 'foreign_keys' " + "argument, providing a list of those columns which " + "should be counted as containing a foreign key " + "reference from the secondary table to each of the " + "parent and child tables." + % (self.prop, self.secondary)) + else: + raise sa_exc.AmbiguousForeignKeysError( + "Could not determine join " + "condition between parent/child tables on " + "relationship %s - there are multiple foreign key " + "paths linking the tables. Specify the " + "'foreign_keys' argument, providing a list of those " + "columns which should be counted as containing a " + "foreign key reference to the parent table." + % self.prop) + + @property + def primaryjoin_minus_local(self): + return _deep_deannotate(self.primaryjoin, values=("local", "remote")) + + @property + def secondaryjoin_minus_local(self): + return _deep_deannotate(self.secondaryjoin, + values=("local", "remote")) + + @util.memoized_property + def primaryjoin_reverse_remote(self): + """Return the primaryjoin condition suitable for the + "reverse" direction. + + If the primaryjoin was delivered here with pre-existing + "remote" annotations, the local/remote annotations + are reversed. Otherwise, the local/remote annotations + are removed. + + """ + if self._has_remote_annotations: + def replace(element): + if "remote" in element._annotations: + v = element._annotations.copy() + del v['remote'] + v['local'] = True + return element._with_annotations(v) + elif "local" in element._annotations: + v = element._annotations.copy() + del v['local'] + v['remote'] = True + return element._with_annotations(v) + return visitors.replacement_traverse( + self.primaryjoin, {}, replace) + else: + if self._has_foreign_annotations: + # TODO: coverage + return _deep_deannotate(self.primaryjoin, + values=("local", "remote")) + else: + return _deep_deannotate(self.primaryjoin) + + def _has_annotation(self, clause, annotation): + for col in visitors.iterate(clause, {}): + if annotation in col._annotations: + return True + else: + return False + + @util.memoized_property + def _has_foreign_annotations(self): + return self._has_annotation(self.primaryjoin, "foreign") + + @util.memoized_property + def _has_remote_annotations(self): + return self._has_annotation(self.primaryjoin, "remote") + + def _annotate_fks(self): + """Annotate the primaryjoin and secondaryjoin + structures with 'foreign' annotations marking columns + considered as foreign. + + """ + if self._has_foreign_annotations: + return + + if self.consider_as_foreign_keys: + self._annotate_from_fk_list() + else: + self._annotate_present_fks() + + def _annotate_from_fk_list(self): + def check_fk(col): + if col in self.consider_as_foreign_keys: + return col._annotate({"foreign": True}) + self.primaryjoin = visitors.replacement_traverse( + self.primaryjoin, + {}, + check_fk + ) + if self.secondaryjoin is not None: + self.secondaryjoin = visitors.replacement_traverse( + self.secondaryjoin, + {}, + check_fk + ) + + def _annotate_present_fks(self): + if self.secondary is not None: + secondarycols = util.column_set(self.secondary.c) + else: + secondarycols = set() + + def is_foreign(a, b): + if isinstance(a, schema.Column) and \ + isinstance(b, schema.Column): + if a.references(b): + return a + elif b.references(a): + return b + + if secondarycols: + if a in secondarycols and b not in secondarycols: + return a + elif b in secondarycols and a not in secondarycols: + return b + + def visit_binary(binary): + if not isinstance(binary.left, sql.ColumnElement) or \ + not isinstance(binary.right, sql.ColumnElement): + return + + if "foreign" not in binary.left._annotations and \ + "foreign" not in binary.right._annotations: + col = is_foreign(binary.left, binary.right) + if col is not None: + if col.compare(binary.left): + binary.left = binary.left._annotate( + {"foreign": True}) + elif col.compare(binary.right): + binary.right = binary.right._annotate( + {"foreign": True}) + + self.primaryjoin = visitors.cloned_traverse( + self.primaryjoin, + {}, + {"binary": visit_binary} + ) + if self.secondaryjoin is not None: + self.secondaryjoin = visitors.cloned_traverse( + self.secondaryjoin, + {}, + {"binary": visit_binary} + ) + + def _refers_to_parent_table(self): + """Return True if the join condition contains column + comparisons where both columns are in both tables. + + """ + pt = self.parent_selectable + mt = self.child_selectable + result = [False] + + def visit_binary(binary): + c, f = binary.left, binary.right + if ( + isinstance(c, expression.ColumnClause) and + isinstance(f, expression.ColumnClause) and + pt.is_derived_from(c.table) and + pt.is_derived_from(f.table) and + mt.is_derived_from(c.table) and + mt.is_derived_from(f.table) + ): + result[0] = True + visitors.traverse( + self.primaryjoin, + {}, + {"binary": visit_binary} + ) + return result[0] + + def _tables_overlap(self): + """Return True if parent/child tables have some overlap.""" + + return selectables_overlap( + self.parent_selectable, self.child_selectable) + + def _annotate_remote(self): + """Annotate the primaryjoin and secondaryjoin + structures with 'remote' annotations marking columns + considered as part of the 'remote' side. + + """ + if self._has_remote_annotations: + return + + if self.secondary is not None: + self._annotate_remote_secondary() + elif self._local_remote_pairs or self._remote_side: + self._annotate_remote_from_args() + elif self._refers_to_parent_table(): + self._annotate_selfref(lambda col: "foreign" in col._annotations, False) + elif self._tables_overlap(): + self._annotate_remote_with_overlap() + else: + self._annotate_remote_distinct_selectables() + + def _annotate_remote_secondary(self): + """annotate 'remote' in primaryjoin, secondaryjoin + when 'secondary' is present. + + """ + def repl(element): + if self.secondary.c.contains_column(element): + return element._annotate({"remote": True}) + self.primaryjoin = visitors.replacement_traverse( + self.primaryjoin, {}, repl) + self.secondaryjoin = visitors.replacement_traverse( + self.secondaryjoin, {}, repl) + + def _annotate_selfref(self, fn, remote_side_given): + """annotate 'remote' in primaryjoin, secondaryjoin + when the relationship is detected as self-referential. + + """ + def visit_binary(binary): + equated = binary.left.compare(binary.right) + if isinstance(binary.left, expression.ColumnClause) and \ + isinstance(binary.right, expression.ColumnClause): + # assume one to many - FKs are "remote" + if fn(binary.left): + binary.left = binary.left._annotate({"remote": True}) + if fn(binary.right) and not equated: + binary.right = binary.right._annotate( + {"remote": True}) + elif not remote_side_given: + self._warn_non_column_elements() + + self.primaryjoin = visitors.cloned_traverse( + self.primaryjoin, {}, + {"binary": visit_binary}) + + def _annotate_remote_from_args(self): + """annotate 'remote' in primaryjoin, secondaryjoin + when the 'remote_side' or '_local_remote_pairs' + arguments are used. + + """ + if self._local_remote_pairs: + if self._remote_side: + raise sa_exc.ArgumentError( + "remote_side argument is redundant " + "against more detailed _local_remote_side " + "argument.") + + remote_side = [r for (l, r) in self._local_remote_pairs] + else: + remote_side = self._remote_side + + if self._refers_to_parent_table(): + self._annotate_selfref(lambda col: col in remote_side, True) + else: + def repl(element): + if element in remote_side: + return element._annotate({"remote": True}) + self.primaryjoin = visitors.replacement_traverse( + self.primaryjoin, {}, repl) + + def _annotate_remote_with_overlap(self): + """annotate 'remote' in primaryjoin, secondaryjoin + when the parent/child tables have some set of + tables in common, though is not a fully self-referential + relationship. + + """ + def visit_binary(binary): + binary.left, binary.right = proc_left_right(binary.left, + binary.right) + binary.right, binary.left = proc_left_right(binary.right, + binary.left) + + check_entities = self.prop is not None and \ + self.prop.mapper is not self.prop.parent + + def proc_left_right(left, right): + if isinstance(left, expression.ColumnClause) and \ + isinstance(right, expression.ColumnClause): + if self.child_selectable.c.contains_column(right) and \ + self.parent_selectable.c.contains_column(left): + right = right._annotate({"remote": True}) + elif check_entities and \ + right._annotations.get('parentmapper') is self.prop.mapper: + right = right._annotate({"remote": True}) + elif check_entities and \ + left._annotations.get('parentmapper') is self.prop.mapper: + left = left._annotate({"remote": True}) + else: + self._warn_non_column_elements() + + return left, right + + self.primaryjoin = visitors.cloned_traverse( + self.primaryjoin, {}, + {"binary": visit_binary}) + + def _annotate_remote_distinct_selectables(self): + """annotate 'remote' in primaryjoin, secondaryjoin + when the parent/child tables are entirely + separate. + + """ + def repl(element): + if self.child_selectable.c.contains_column(element) and \ + (not self.parent_local_selectable.c. + contains_column(element) or + self.child_local_selectable.c. + contains_column(element)): + return element._annotate({"remote": True}) + self.primaryjoin = visitors.replacement_traverse( + self.primaryjoin, {}, repl) + + def _warn_non_column_elements(self): + util.warn( + "Non-simple column elements in primary " + "join condition for property %s - consider using " + "remote() annotations to mark the remote side." + % self.prop + ) + + def _annotate_local(self): + """Annotate the primaryjoin and secondaryjoin + structures with 'local' annotations. + + This annotates all column elements found + simultaneously in the parent table + and the join condition that don't have a + 'remote' annotation set up from + _annotate_remote() or user-defined. + + """ + if self._has_annotation(self.primaryjoin, "local"): + return + + if self._local_remote_pairs: + local_side = util.column_set([l for (l, r) + in self._local_remote_pairs]) + else: + local_side = util.column_set(self.parent_selectable.c) + + def locals_(elem): + if "remote" not in elem._annotations and \ + elem in local_side: + return elem._annotate({"local": True}) + self.primaryjoin = visitors.replacement_traverse( + self.primaryjoin, {}, locals_ + ) + + def _annotate_parentmapper(self): + if self.prop is None: + return + + def parentmappers_(elem): + if "remote" in elem._annotations: + return elem._annotate({"parentmapper": self.prop.mapper}) + elif "local" in elem._annotations: + return elem._annotate({"parentmapper": self.prop.parent}) + self.primaryjoin = visitors.replacement_traverse( + self.primaryjoin, {}, parentmappers_ + ) + + def _check_remote_side(self): + if not self.local_remote_pairs: + raise sa_exc.ArgumentError( + 'Relationship %s could ' + 'not determine any unambiguous local/remote column ' + 'pairs based on join condition and remote_side ' + 'arguments. ' + 'Consider using the remote() annotation to ' + 'accurately mark those elements of the join ' + 'condition that are on the remote side of ' + 'the relationship.' % (self.prop, )) + + def _check_foreign_cols(self, join_condition, primary): + """Check the foreign key columns collected and emit error + messages.""" + + can_sync = False + + foreign_cols = self._gather_columns_with_annotation( + join_condition, "foreign") + + has_foreign = bool(foreign_cols) + + if primary: + can_sync = bool(self.synchronize_pairs) + else: + can_sync = bool(self.secondary_synchronize_pairs) + + if self.support_sync and can_sync or \ + (not self.support_sync and has_foreign): + return + + # from here below is just determining the best error message + # to report. Check for a join condition using any operator + # (not just ==), perhaps they need to turn on "viewonly=True". + if self.support_sync and has_foreign and not can_sync: + err = "Could not locate any simple equality expressions "\ + "involving locally mapped foreign key columns for "\ + "%s join condition "\ + "'%s' on relationship %s." % ( + primary and 'primary' or 'secondary', + join_condition, + self.prop + ) + err += \ + " Ensure that referencing columns are associated "\ + "with a ForeignKey or ForeignKeyConstraint, or are "\ + "annotated in the join condition with the foreign() "\ + "annotation. To allow comparison operators other than "\ + "'==', the relationship can be marked as viewonly=True." + + raise sa_exc.ArgumentError(err) + else: + err = "Could not locate any relevant foreign key columns "\ + "for %s join condition '%s' on relationship %s." % ( + primary and 'primary' or 'secondary', + join_condition, + self.prop + ) + err += \ + ' Ensure that referencing columns are associated '\ + 'with a ForeignKey or ForeignKeyConstraint, or are '\ + 'annotated in the join condition with the foreign() '\ + 'annotation.' + raise sa_exc.ArgumentError(err) + + def _determine_direction(self): + """Determine if this relationship is one to many, many to one, + many to many. + + """ + if self.secondaryjoin is not None: + self.direction = MANYTOMANY + else: + parentcols = util.column_set(self.parent_selectable.c) + targetcols = util.column_set(self.child_selectable.c) + + # fk collection which suggests ONETOMANY. + onetomany_fk = targetcols.intersection( + self.foreign_key_columns) + + # fk collection which suggests MANYTOONE. + + manytoone_fk = parentcols.intersection( + self.foreign_key_columns) + + if onetomany_fk and manytoone_fk: + # fks on both sides. test for overlap of local/remote + # with foreign key. + # we will gather columns directly from their annotations + # without deannotating, so that we can distinguish on a column + # that refers to itself. + + # 1. columns that are both remote and FK suggest + # onetomany. + onetomany_local = self._gather_columns_with_annotation( + self.primaryjoin, "remote", "foreign") + + # 2. columns that are FK but are not remote (e.g. local) + # suggest manytoone. + manytoone_local = set([c for c in + self._gather_columns_with_annotation( + self.primaryjoin, + "foreign") + if "remote" not in c._annotations]) + + # 3. if both collections are present, remove columns that + # refer to themselves. This is for the case of + # and_(Me.id == Me.remote_id, Me.version == Me.version) + if onetomany_local and manytoone_local: + self_equated = self.remote_columns.intersection( + self.local_columns + ) + onetomany_local = onetomany_local.difference(self_equated) + manytoone_local = manytoone_local.difference(self_equated) + + # at this point, if only one or the other collection is + # present, we know the direction, otherwise it's still + # ambiguous. + + if onetomany_local and not manytoone_local: + self.direction = ONETOMANY + elif manytoone_local and not onetomany_local: + self.direction = MANYTOONE + else: + raise sa_exc.ArgumentError( + "Can't determine relationship" + " direction for relationship '%s' - foreign " + "key columns within the join condition are present " + "in both the parent and the child's mapped tables. " + "Ensure that only those columns referring " + "to a parent column are marked as foreign, " + "either via the foreign() annotation or " + "via the foreign_keys argument." % self.prop) + elif onetomany_fk: + self.direction = ONETOMANY + elif manytoone_fk: + self.direction = MANYTOONE + else: + raise sa_exc.ArgumentError( + "Can't determine relationship " + "direction for relationship '%s' - foreign " + "key columns are present in neither the parent " + "nor the child's mapped tables" % self.prop) + + def _deannotate_pairs(self, collection): + """provide deannotation for the various lists of + pairs, so that using them in hashes doesn't incur + high-overhead __eq__() comparisons against + original columns mapped. + + """ + return [(x._deannotate(), y._deannotate()) + for x, y in collection] + + def _setup_pairs(self): + sync_pairs = [] + lrp = util.OrderedSet([]) + secondary_sync_pairs = [] + + def go(joincond, collection): + def visit_binary(binary, left, right): + if "remote" in right._annotations and \ + "remote" not in left._annotations and \ + self.can_be_synced_fn(left): + lrp.add((left, right)) + elif "remote" in left._annotations and \ + "remote" not in right._annotations and \ + self.can_be_synced_fn(right): + lrp.add((right, left)) + if binary.operator is operators.eq and \ + self.can_be_synced_fn(left, right): + if "foreign" in right._annotations: + collection.append((left, right)) + elif "foreign" in left._annotations: + collection.append((right, left)) + visit_binary_product(visit_binary, joincond) + + for joincond, collection in [ + (self.primaryjoin, sync_pairs), + (self.secondaryjoin, secondary_sync_pairs) + ]: + if joincond is None: + continue + go(joincond, collection) + + self.local_remote_pairs = self._deannotate_pairs(lrp) + self.synchronize_pairs = self._deannotate_pairs(sync_pairs) + self.secondary_synchronize_pairs = \ + self._deannotate_pairs(secondary_sync_pairs) + + _track_overlapping_sync_targets = weakref.WeakKeyDictionary() + + def _warn_for_conflicting_sync_targets(self): + if not self.support_sync: + return + + # we would like to detect if we are synchronizing any column + # pairs in conflict with another relationship that wishes to sync + # an entirely different column to the same target. This is a + # very rare edge case so we will try to minimize the memory/overhead + # impact of this check + for from_, to_ in [ + (from_, to_) for (from_, to_) in self.synchronize_pairs + ] + [ + (from_, to_) for (from_, to_) in self.secondary_synchronize_pairs + ]: + # save ourselves a ton of memory and overhead by only + # considering columns that are subject to a overlapping + # FK constraints at the core level. This condition can arise + # if multiple relationships overlap foreign() directly, but + # we're going to assume it's typically a ForeignKeyConstraint- + # level configuration that benefits from this warning. + if len(to_.foreign_keys) < 2: + continue + + if to_ not in self._track_overlapping_sync_targets: + self._track_overlapping_sync_targets[to_] = \ + weakref.WeakKeyDictionary({self.prop: from_}) + else: + other_props = [] + prop_to_from = self._track_overlapping_sync_targets[to_] + + for pr, fr_ in prop_to_from.items(): + if pr.mapper in mapperlib._mapper_registry and \ + ( + self.prop._persists_for(pr.parent) or + pr._persists_for(self.prop.parent) + ) and \ + fr_ is not from_ and \ + pr not in self.prop._reverse_property: + + other_props.append((pr, fr_)) + + if other_props: + util.warn( + "relationship '%s' will copy column %s to column %s, " + "which conflicts with relationship(s): %s. " + "Consider applying " + "viewonly=True to read-only relationships, or provide " + "a primaryjoin condition marking writable columns " + "with the foreign() annotation." % ( + self.prop, + from_, to_, + ", ".join( + "'%s' (copies %s to %s)" % (pr, fr_, to_) + for (pr, fr_) in other_props) + ) + ) + self._track_overlapping_sync_targets[to_][self.prop] = from_ + + @util.memoized_property + def remote_columns(self): + return self._gather_join_annotations("remote") + + @util.memoized_property + def local_columns(self): + return self._gather_join_annotations("local") + + @util.memoized_property + def foreign_key_columns(self): + return self._gather_join_annotations("foreign") + + def _gather_join_annotations(self, annotation): + s = set( + self._gather_columns_with_annotation( + self.primaryjoin, annotation) + ) + if self.secondaryjoin is not None: + s.update( + self._gather_columns_with_annotation( + self.secondaryjoin, annotation) + ) + return {x._deannotate() for x in s} + + def _gather_columns_with_annotation(self, clause, *annotation): + annotation = set(annotation) + return set([ + col for col in visitors.iterate(clause, {}) + if annotation.issubset(col._annotations) + ]) + + def join_targets(self, source_selectable, + dest_selectable, + aliased, + single_crit=None): + """Given a source and destination selectable, create a + join between them. + + This takes into account aliasing the join clause + to reference the appropriate corresponding columns + in the target objects, as well as the extra child + criterion, equivalent column sets, etc. + + """ + + # place a barrier on the destination such that + # replacement traversals won't ever dig into it. + # its internal structure remains fixed + # regardless of context. + dest_selectable = _shallow_annotate( + dest_selectable, + {'no_replacement_traverse': True}) + + primaryjoin, secondaryjoin, secondary = self.primaryjoin, \ + self.secondaryjoin, self.secondary + + # adjust the join condition for single table inheritance, + # in the case that the join is to a subclass + # this is analogous to the + # "_adjust_for_single_table_inheritance()" method in Query. + + if single_crit is not None: + if secondaryjoin is not None: + secondaryjoin = secondaryjoin & single_crit + else: + primaryjoin = primaryjoin & single_crit + + if aliased: + if secondary is not None: + secondary = secondary.alias(flat=True) + primary_aliasizer = ClauseAdapter(secondary) + secondary_aliasizer = \ + ClauseAdapter(dest_selectable, + equivalents=self.child_equivalents).\ + chain(primary_aliasizer) + if source_selectable is not None: + primary_aliasizer = \ + ClauseAdapter(secondary).\ + chain(ClauseAdapter( + source_selectable, + equivalents=self.parent_equivalents)) + secondaryjoin = \ + secondary_aliasizer.traverse(secondaryjoin) + else: + primary_aliasizer = ClauseAdapter( + dest_selectable, + exclude_fn=_ColInAnnotations("local"), + equivalents=self.child_equivalents) + if source_selectable is not None: + primary_aliasizer.chain( + ClauseAdapter(source_selectable, + exclude_fn=_ColInAnnotations("remote"), + equivalents=self.parent_equivalents)) + secondary_aliasizer = None + + primaryjoin = primary_aliasizer.traverse(primaryjoin) + target_adapter = secondary_aliasizer or primary_aliasizer + target_adapter.exclude_fn = None + else: + target_adapter = None + return primaryjoin, secondaryjoin, secondary, \ + target_adapter, dest_selectable + + def create_lazy_clause(self, reverse_direction=False): + binds = util.column_dict() + equated_columns = util.column_dict() + + has_secondary = self.secondaryjoin is not None + + if has_secondary: + lookup = collections.defaultdict(list) + for l, r in self.local_remote_pairs: + lookup[l].append((l, r)) + equated_columns[r] = l + elif not reverse_direction: + for l, r in self.local_remote_pairs: + equated_columns[r] = l + else: + for l, r in self.local_remote_pairs: + equated_columns[l] = r + + def col_to_bind(col): + + if ( + (not reverse_direction and 'local' in col._annotations) or + reverse_direction and ( + (has_secondary and col in lookup) or + (not has_secondary and 'remote' in col._annotations) + ) + ): + if col not in binds: + binds[col] = sql.bindparam( + None, None, type_=col.type, unique=True) + return binds[col] + return None + + lazywhere = self.primaryjoin + if self.secondaryjoin is None or not reverse_direction: + lazywhere = visitors.replacement_traverse( + lazywhere, {}, col_to_bind) + + if self.secondaryjoin is not None: + secondaryjoin = self.secondaryjoin + if reverse_direction: + secondaryjoin = visitors.replacement_traverse( + secondaryjoin, {}, col_to_bind) + lazywhere = sql.and_(lazywhere, secondaryjoin) + + bind_to_col = {binds[col].key: col for col in binds} + + return lazywhere, bind_to_col, equated_columns + + +class _ColInAnnotations(object): + """Seralizable equivalent to: + + lambda c: "name" in c._annotations + """ + + def __init__(self, name): + self.name = name + + def __call__(self, c): + return self.name in c._annotations diff --git a/venv/Lib/site-packages/sqlalchemy/orm/scoping.py b/venv/Lib/site-packages/sqlalchemy/orm/scoping.py new file mode 100644 index 0000000..2e16872 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/scoping.py @@ -0,0 +1,181 @@ +# orm/scoping.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from .. import exc as sa_exc +from ..util import ScopedRegistry, ThreadLocalRegistry, warn +from . import class_mapper, exc as orm_exc +from .session import Session + + +__all__ = ['scoped_session'] + + +class scoped_session(object): + """Provides scoped management of :class:`.Session` objects. + + See :ref:`unitofwork_contextual` for a tutorial. + + """ + + session_factory = None + """The `session_factory` provided to `__init__` is stored in this + attribute and may be accessed at a later time. This can be useful when + a new non-scoped :class:`.Session` or :class:`.Connection` to the + database is needed.""" + + def __init__(self, session_factory, scopefunc=None): + """Construct a new :class:`.scoped_session`. + + :param session_factory: a factory to create new :class:`.Session` + instances. This is usually, but not necessarily, an instance + of :class:`.sessionmaker`. + :param scopefunc: optional function which defines + the current scope. If not passed, the :class:`.scoped_session` + object assumes "thread-local" scope, and will use + a Python ``threading.local()`` in order to maintain the current + :class:`.Session`. If passed, the function should return + a hashable token; this token will be used as the key in a + dictionary in order to store and retrieve the current + :class:`.Session`. + + """ + self.session_factory = session_factory + + if scopefunc: + self.registry = ScopedRegistry(session_factory, scopefunc) + else: + self.registry = ThreadLocalRegistry(session_factory) + + def __call__(self, **kw): + r"""Return the current :class:`.Session`, creating it + using the :attr:`.scoped_session.session_factory` if not present. + + :param \**kw: Keyword arguments will be passed to the + :attr:`.scoped_session.session_factory` callable, if an existing + :class:`.Session` is not present. If the :class:`.Session` is present + and keyword arguments have been passed, + :exc:`~sqlalchemy.exc.InvalidRequestError` is raised. + + """ + if kw: + if self.registry.has(): + raise sa_exc.InvalidRequestError( + "Scoped session is already present; " + "no new arguments may be specified.") + else: + sess = self.session_factory(**kw) + self.registry.set(sess) + return sess + else: + return self.registry() + + def remove(self): + """Dispose of the current :class:`.Session`, if present. + + This will first call :meth:`.Session.close` method + on the current :class:`.Session`, which releases any existing + transactional/connection resources still being held; transactions + specifically are rolled back. The :class:`.Session` is then + discarded. Upon next usage within the same scope, + the :class:`.scoped_session` will produce a new + :class:`.Session` object. + + """ + + if self.registry.has(): + self.registry().close() + self.registry.clear() + + def configure(self, **kwargs): + """reconfigure the :class:`.sessionmaker` used by this + :class:`.scoped_session`. + + See :meth:`.sessionmaker.configure`. + + """ + + if self.registry.has(): + warn('At least one scoped session is already present. ' + ' configure() can not affect sessions that have ' + 'already been created.') + + self.session_factory.configure(**kwargs) + + def query_property(self, query_cls=None): + """return a class property which produces a :class:`.Query` object + against the class and the current :class:`.Session` when called. + + e.g.:: + + Session = scoped_session(sessionmaker()) + + class MyClass(object): + query = Session.query_property() + + # after mappers are defined + result = MyClass.query.filter(MyClass.name=='foo').all() + + Produces instances of the session's configured query class by + default. To override and use a custom implementation, provide + a ``query_cls`` callable. The callable will be invoked with + the class's mapper as a positional argument and a session + keyword argument. + + There is no limit to the number of query properties placed on + a class. + + """ + class query(object): + def __get__(s, instance, owner): + try: + mapper = class_mapper(owner) + if mapper: + if query_cls: + # custom query class + return query_cls(mapper, session=self.registry()) + else: + # session's configured query class + return self.registry().query(mapper) + except orm_exc.UnmappedClassError: + return None + return query() + +ScopedSession = scoped_session +"""Old name for backwards compatibility.""" + + +def instrument(name): + def do(self, *args, **kwargs): + return getattr(self.registry(), name)(*args, **kwargs) + return do + +for meth in Session.public_methods: + setattr(scoped_session, meth, instrument(meth)) + + +def makeprop(name): + def set(self, attr): + setattr(self.registry(), name, attr) + + def get(self): + return getattr(self.registry(), name) + + return property(get, set) + +for prop in ('bind', 'dirty', 'deleted', 'new', 'identity_map', + 'is_active', 'autoflush', 'no_autoflush', 'info', + 'autocommit'): + setattr(scoped_session, prop, makeprop(prop)) + + +def clslevel(name): + def do(cls, *args, **kwargs): + return getattr(Session, name)(*args, **kwargs) + return classmethod(do) + +for prop in ('close_all', 'object_session', 'identity_key'): + setattr(scoped_session, prop, clslevel(prop)) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/session.py b/venv/Lib/site-packages/sqlalchemy/orm/session.py new file mode 100644 index 0000000..63a7700 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/session.py @@ -0,0 +1,3153 @@ +# orm/session.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +"""Provides the Session class and related utilities.""" + + +import weakref +from .. import util, sql, engine, exc as sa_exc +from ..sql import util as sql_util, expression +from . import ( + SessionExtension, attributes, exc, query, + loading, identity +) +from ..inspection import inspect +from .base import ( + object_mapper, class_mapper, + _class_to_mapper, _state_mapper, object_state, + _none_set, state_str, instance_str +) +import itertools +from . import persistence +from .unitofwork import UOWTransaction +from . import state as statelib +import sys + +__all__ = ['Session', 'SessionTransaction', + 'SessionExtension', 'sessionmaker'] + +_sessions = weakref.WeakValueDictionary() +"""Weak-referencing dictionary of :class:`.Session` objects. +""" + + +def _state_session(state): + """Given an :class:`.InstanceState`, return the :class:`.Session` + associated, if any. + """ + if state.session_id: + try: + return _sessions[state.session_id] + except KeyError: + pass + return None + + +class _SessionClassMethods(object): + """Class-level methods for :class:`.Session`, :class:`.sessionmaker`.""" + + @classmethod + def close_all(cls): + """Close *all* sessions in memory.""" + + for sess in _sessions.values(): + sess.close() + + @classmethod + @util.dependencies("sqlalchemy.orm.util") + def identity_key(cls, orm_util, *args, **kwargs): + """Return an identity key. + + This is an alias of :func:`.util.identity_key`. + + """ + return orm_util.identity_key(*args, **kwargs) + + @classmethod + def object_session(cls, instance): + """Return the :class:`.Session` to which an object belongs. + + This is an alias of :func:`.object_session`. + + """ + + return object_session(instance) + + +ACTIVE = util.symbol('ACTIVE') +PREPARED = util.symbol('PREPARED') +COMMITTED = util.symbol('COMMITTED') +DEACTIVE = util.symbol('DEACTIVE') +CLOSED = util.symbol('CLOSED') + + +class SessionTransaction(object): + """A :class:`.Session`-level transaction. + + :class:`.SessionTransaction` is a mostly behind-the-scenes object + not normally referenced directly by application code. It coordinates + among multiple :class:`.Connection` objects, maintaining a database + transaction for each one individually, committing or rolling them + back all at once. It also provides optional two-phase commit behavior + which can augment this coordination operation. + + The :attr:`.Session.transaction` attribute of :class:`.Session` + refers to the current :class:`.SessionTransaction` object in use, if any. + The :attr:`.SessionTransaction.parent` attribute refers to the parent + :class:`.SessionTransaction` in the stack of :class:`.SessionTransaction` + objects. If this attribute is ``None``, then this is the top of the stack. + If non-``None``, then this :class:`.SessionTransaction` refers either + to a so-called "subtransaction" or a "nested" transaction. A + "subtransaction" is a scoping concept that demarcates an inner portion + of the outermost "real" transaction. A nested transaction, which + is indicated when the :attr:`.SessionTransaction.nested` + attribute is also True, indicates that this :class:`.SessionTransaction` + corresponds to a SAVEPOINT. + + **Life Cycle** + + A :class:`.SessionTransaction` is associated with a :class:`.Session` + in its default mode of ``autocommit=False`` immediately, associated + with no database connections. As the :class:`.Session` is called upon + to emit SQL on behalf of various :class:`.Engine` or :class:`.Connection` + objects, a corresponding :class:`.Connection` and associated + :class:`.Transaction` is added to a collection within the + :class:`.SessionTransaction` object, becoming one of the + connection/transaction pairs maintained by the + :class:`.SessionTransaction`. The start of a :class:`.SessionTransaction` + can be tracked using the :meth:`.SessionEvents.after_transaction_create` + event. + + The lifespan of the :class:`.SessionTransaction` ends when the + :meth:`.Session.commit`, :meth:`.Session.rollback` or + :meth:`.Session.close` methods are called. At this point, the + :class:`.SessionTransaction` removes its association with its parent + :class:`.Session`. A :class:`.Session` that is in ``autocommit=False`` + mode will create a new :class:`.SessionTransaction` to replace it + immediately, whereas a :class:`.Session` that's in ``autocommit=True`` + mode will remain without a :class:`.SessionTransaction` until the + :meth:`.Session.begin` method is called. The end of a + :class:`.SessionTransaction` can be tracked using the + :meth:`.SessionEvents.after_transaction_end` event. + + **Nesting and Subtransactions** + + Another detail of :class:`.SessionTransaction` behavior is that it is + capable of "nesting". This means that the :meth:`.Session.begin` method + can be called while an existing :class:`.SessionTransaction` is already + present, producing a new :class:`.SessionTransaction` that temporarily + replaces the parent :class:`.SessionTransaction`. When a + :class:`.SessionTransaction` is produced as nested, it assigns itself to + the :attr:`.Session.transaction` attribute, and it additionally will assign + the previous :class:`.SessionTransaction` to its :attr:`.Session.parent` + attribute. The behavior is effectively a + stack, where :attr:`.Session.transaction` refers to the current head of + the stack, and the :attr:`.SessionTransaction.parent` attribute allows + traversal up the stack until :attr:`.SessionTransaction.parent` is + ``None``, indicating the top of the stack. + + When the scope of :class:`.SessionTransaction` is ended via + :meth:`.Session.commit` or :meth:`.Session.rollback`, it restores its + parent :class:`.SessionTransaction` back onto the + :attr:`.Session.transaction` attribute. + + The purpose of this stack is to allow nesting of + :meth:`.Session.rollback` or :meth:`.Session.commit` calls in context + with various flavors of :meth:`.Session.begin`. This nesting behavior + applies to when :meth:`.Session.begin_nested` is used to emit a + SAVEPOINT transaction, and is also used to produce a so-called + "subtransaction" which allows a block of code to use a + begin/rollback/commit sequence regardless of whether or not its enclosing + code block has begun a transaction. The :meth:`.flush` method, whether + called explicitly or via autoflush, is the primary consumer of the + "subtransaction" feature, in that it wishes to guarantee that it works + within in a transaction block regardless of whether or not the + :class:`.Session` is in transactional mode when the method is called. + + Note that the flush process that occurs within the "autoflush" feature + as well as when the :meth:`.Session.flush` method is used **always** + creates a :class:`.SessionTransaction` object. This object is normally + a subtransaction, unless the :class:`.Session` is in autocommit mode + and no transaction exists at all, in which case it's the outermost + transaction. Any event-handling logic or other inspection logic + needs to take into account whether a :class:`.SessionTransaction` + is the outermost transaction, a subtransaction, or a "nested" / SAVEPOINT + transaction. + + .. seealso:: + + :meth:`.Session.rollback` + + :meth:`.Session.commit` + + :meth:`.Session.begin` + + :meth:`.Session.begin_nested` + + :attr:`.Session.is_active` + + :meth:`.SessionEvents.after_transaction_create` + + :meth:`.SessionEvents.after_transaction_end` + + :meth:`.SessionEvents.after_commit` + + :meth:`.SessionEvents.after_rollback` + + :meth:`.SessionEvents.after_soft_rollback` + + """ + + _rollback_exception = None + + def __init__(self, session, parent=None, nested=False): + self.session = session + self._connections = {} + self._parent = parent + self.nested = nested + self._state = ACTIVE + if not parent and nested: + raise sa_exc.InvalidRequestError( + "Can't start a SAVEPOINT transaction when no existing " + "transaction is in progress") + + if self.session._enable_transaction_accounting: + self._take_snapshot() + + self.session.dispatch.after_transaction_create(self.session, self) + + @property + def parent(self): + """The parent :class:`.SessionTransaction` of this + :class:`.SessionTransaction`. + + If this attribute is ``None``, indicates this + :class:`.SessionTransaction` is at the top of the stack, and + corresponds to a real "COMMIT"/"ROLLBACK" + block. If non-``None``, then this is either a "subtransaction" + or a "nested" / SAVEPOINT transaction. If the + :attr:`.SessionTransaction.nested` attribute is ``True``, then + this is a SAVEPOINT, and if ``False``, indicates this a subtransaction. + + .. versionadded:: 1.0.16 - use ._parent for previous versions + + """ + return self._parent + + nested = False + """Indicates if this is a nested, or SAVEPOINT, transaction. + + When :attr:`.SessionTransaction.nested` is True, it is expected + that :attr:`.SessionTransaction.parent` will be True as well. + + """ + + @property + def is_active(self): + return self.session is not None and self._state is ACTIVE + + def _assert_active(self, prepared_ok=False, + rollback_ok=False, + deactive_ok=False, + closed_msg="This transaction is closed"): + if self._state is COMMITTED: + raise sa_exc.InvalidRequestError( + "This session is in 'committed' state; no further " + "SQL can be emitted within this transaction." + ) + elif self._state is PREPARED: + if not prepared_ok: + raise sa_exc.InvalidRequestError( + "This session is in 'prepared' state; no further " + "SQL can be emitted within this transaction." + ) + elif self._state is DEACTIVE: + if not deactive_ok and not rollback_ok: + if self._rollback_exception: + raise sa_exc.InvalidRequestError( + "This Session's transaction has been rolled back " + "due to a previous exception during flush." + " To begin a new transaction with this Session, " + "first issue Session.rollback()." + " Original exception was: %s" + % self._rollback_exception + ) + elif not deactive_ok: + raise sa_exc.InvalidRequestError( + "This session is in 'inactive' state, due to the " + "SQL transaction being rolled back; no further " + "SQL can be emitted within this transaction." + ) + elif self._state is CLOSED: + raise sa_exc.ResourceClosedError(closed_msg) + + @property + def _is_transaction_boundary(self): + return self.nested or not self._parent + + def connection(self, bindkey, execution_options=None, **kwargs): + self._assert_active() + bind = self.session.get_bind(bindkey, **kwargs) + return self._connection_for_bind(bind, execution_options) + + def _begin(self, nested=False): + self._assert_active() + return SessionTransaction( + self.session, self, nested=nested) + + def _iterate_self_and_parents(self, upto=None): + + current = self + result = () + while current: + result += (current, ) + if current._parent is upto: + break + elif current._parent is None: + raise sa_exc.InvalidRequestError( + "Transaction %s is not on the active transaction list" % ( + upto)) + else: + current = current._parent + + return result + + def _take_snapshot(self): + if not self._is_transaction_boundary: + self._new = self._parent._new + self._deleted = self._parent._deleted + self._dirty = self._parent._dirty + self._key_switches = self._parent._key_switches + return + + if not self.session._flushing: + self.session.flush() + + self._new = weakref.WeakKeyDictionary() + self._deleted = weakref.WeakKeyDictionary() + self._dirty = weakref.WeakKeyDictionary() + self._key_switches = weakref.WeakKeyDictionary() + + def _restore_snapshot(self, dirty_only=False): + """Restore the restoration state taken before a transaction began. + + Corresponds to a rollback. + + """ + assert self._is_transaction_boundary + + to_expunge = set(self._new).union(self.session._new) + self.session._expunge_states(to_expunge, to_transient=True) + + for s, (oldkey, newkey) in self._key_switches.items(): + # we probably can do this conditionally based on + # if we expunged or not, but safe_discard does that anyway + self.session.identity_map.safe_discard(s) + + # restore the old key + s.key = oldkey + + # now restore the object, but only if we didn't expunge + if s not in to_expunge: + self.session.identity_map.replace(s) + + for s in set(self._deleted).union(self.session._deleted): + self.session._update_impl(s, revert_deletion=True) + + assert not self.session._deleted + + for s in self.session.identity_map.all_states(): + if not dirty_only or s.modified or s in self._dirty: + s._expire(s.dict, self.session.identity_map._modified) + + def _remove_snapshot(self): + """Remove the restoration state taken before a transaction began. + + Corresponds to a commit. + + """ + assert self._is_transaction_boundary + + if not self.nested and self.session.expire_on_commit: + for s in self.session.identity_map.all_states(): + s._expire(s.dict, self.session.identity_map._modified) + + statelib.InstanceState._detach_states( + list(self._deleted), self.session) + self._deleted.clear() + elif self.nested: + self._parent._new.update(self._new) + self._parent._dirty.update(self._dirty) + self._parent._deleted.update(self._deleted) + self._parent._key_switches.update(self._key_switches) + + def _connection_for_bind(self, bind, execution_options): + self._assert_active() + + if bind in self._connections: + if execution_options: + util.warn( + "Connection is already established for the " + "given bind; execution_options ignored") + return self._connections[bind][0] + + if self._parent: + conn = self._parent._connection_for_bind(bind, execution_options) + if not self.nested: + return conn + else: + if isinstance(bind, engine.Connection): + conn = bind + if conn.engine in self._connections: + raise sa_exc.InvalidRequestError( + "Session already has a Connection associated for the " + "given Connection's Engine") + else: + conn = bind.contextual_connect() + + if execution_options: + conn = conn.execution_options(**execution_options) + + if self.session.twophase and self._parent is None: + transaction = conn.begin_twophase() + elif self.nested: + transaction = conn.begin_nested() + else: + transaction = conn.begin() + + self._connections[conn] = self._connections[conn.engine] = \ + (conn, transaction, conn is not bind) + self.session.dispatch.after_begin(self.session, self, conn) + return conn + + def prepare(self): + if self._parent is not None or not self.session.twophase: + raise sa_exc.InvalidRequestError( + "'twophase' mode not enabled, or not root transaction; " + "can't prepare.") + self._prepare_impl() + + def _prepare_impl(self): + self._assert_active() + if self._parent is None or self.nested: + self.session.dispatch.before_commit(self.session) + + stx = self.session.transaction + if stx is not self: + for subtransaction in stx._iterate_self_and_parents(upto=self): + subtransaction.commit() + + if not self.session._flushing: + for _flush_guard in range(100): + if self.session._is_clean(): + break + self.session.flush() + else: + raise exc.FlushError( + "Over 100 subsequent flushes have occurred within " + "session.commit() - is an after_flush() hook " + "creating new objects?") + + if self._parent is None and self.session.twophase: + try: + for t in set(self._connections.values()): + t[1].prepare() + except: + with util.safe_reraise(): + self.rollback() + + self._state = PREPARED + + def commit(self): + self._assert_active(prepared_ok=True) + if self._state is not PREPARED: + self._prepare_impl() + + if self._parent is None or self.nested: + for t in set(self._connections.values()): + t[1].commit() + + self._state = COMMITTED + self.session.dispatch.after_commit(self.session) + + if self.session._enable_transaction_accounting: + self._remove_snapshot() + + self.close() + return self._parent + + def rollback(self, _capture_exception=False): + self._assert_active(prepared_ok=True, rollback_ok=True) + + stx = self.session.transaction + if stx is not self: + for subtransaction in stx._iterate_self_and_parents(upto=self): + subtransaction.close() + + boundary = self + rollback_err = None + if self._state in (ACTIVE, PREPARED): + for transaction in self._iterate_self_and_parents(): + if transaction._parent is None or transaction.nested: + try: + for t in set(transaction._connections.values()): + t[1].rollback() + + transaction._state = DEACTIVE + self.session.dispatch.after_rollback(self.session) + except: + rollback_err = sys.exc_info() + finally: + transaction._state = DEACTIVE + if self.session._enable_transaction_accounting: + transaction._restore_snapshot( + dirty_only=transaction.nested) + boundary = transaction + break + else: + transaction._state = DEACTIVE + + sess = self.session + + if not rollback_err and sess._enable_transaction_accounting and \ + not sess._is_clean(): + + # if items were added, deleted, or mutated + # here, we need to re-restore the snapshot + util.warn( + "Session's state has been changed on " + "a non-active transaction - this state " + "will be discarded.") + boundary._restore_snapshot(dirty_only=boundary.nested) + + self.close() + + if self._parent and _capture_exception: + self._parent._rollback_exception = sys.exc_info()[1] + + if rollback_err: + util.reraise(*rollback_err) + + sess.dispatch.after_soft_rollback(sess, self) + + return self._parent + + + def close(self, invalidate=False): + self.session.transaction = self._parent + if self._parent is None: + for connection, transaction, autoclose in \ + set(self._connections.values()): + if invalidate: + connection.invalidate() + if autoclose: + connection.close() + else: + transaction.close() + + self._state = CLOSED + self.session.dispatch.after_transaction_end(self.session, self) + + if self._parent is None: + if not self.session.autocommit: + self.session.begin() + self.session = None + self._connections = None + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self._assert_active(deactive_ok=True, prepared_ok=True) + if self.session.transaction is None: + return + if type is None: + try: + self.commit() + except: + with util.safe_reraise(): + self.rollback() + else: + self.rollback() + + +class Session(_SessionClassMethods): + """Manages persistence operations for ORM-mapped objects. + + The Session's usage paradigm is described at :doc:`/orm/session`. + + + """ + + public_methods = ( + '__contains__', '__iter__', 'add', 'add_all', 'begin', 'begin_nested', + 'close', 'commit', 'connection', 'delete', 'execute', 'expire', + 'expire_all', 'expunge', 'expunge_all', 'flush', 'get_bind', + 'is_modified', 'bulk_save_objects', 'bulk_insert_mappings', + 'bulk_update_mappings', + 'merge', 'query', 'refresh', 'rollback', + 'scalar') + + def __init__(self, bind=None, autoflush=True, expire_on_commit=True, + _enable_transaction_accounting=True, + autocommit=False, twophase=False, + weak_identity_map=True, binds=None, extension=None, + enable_baked_queries=True, + info=None, + query_cls=query.Query): + r"""Construct a new Session. + + See also the :class:`.sessionmaker` function which is used to + generate a :class:`.Session`-producing callable with a given + set of arguments. + + :param autocommit: + + .. warning:: + + The autocommit flag is **not for general use**, and if it is + used, queries should only be invoked within the span of a + :meth:`.Session.begin` / :meth:`.Session.commit` pair. Executing + queries outside of a demarcated transaction is a legacy mode + of usage, and can in some cases lead to concurrent connection + checkouts. + + Defaults to ``False``. When ``True``, the + :class:`.Session` does not keep a persistent transaction running, + and will acquire connections from the engine on an as-needed basis, + returning them immediately after their use. Flushes will begin and + commit (or possibly rollback) their own transaction if no + transaction is present. When using this mode, the + :meth:`.Session.begin` method is used to explicitly start + transactions. + + .. seealso:: + + :ref:`session_autocommit` + + :param autoflush: When ``True``, all query operations will issue a + :meth:`~.Session.flush` call to this ``Session`` before proceeding. + This is a convenience feature so that :meth:`~.Session.flush` need + not be called repeatedly in order for database queries to retrieve + results. It's typical that ``autoflush`` is used in conjunction + with ``autocommit=False``. In this scenario, explicit calls to + :meth:`~.Session.flush` are rarely needed; you usually only need to + call :meth:`~.Session.commit` (which flushes) to finalize changes. + + :param bind: An optional :class:`.Engine` or :class:`.Connection` to + which this ``Session`` should be bound. When specified, all SQL + operations performed by this session will execute via this + connectable. + + :param binds: A dictionary which may specify any number of + :class:`.Engine` or :class:`.Connection` objects as the source of + connectivity for SQL operations on a per-entity basis. The keys + of the dictionary consist of any series of mapped classes, + arbitrary Python classes that are bases for mapped classes, + :class:`.Table` objects and :class:`.Mapper` objects. The + values of the dictionary are then instances of :class:`.Engine` + or less commonly :class:`.Connection` objects. Operations which + proceed relative to a particular mapped class will consult this + dictionary for the closest matching entity in order to determine + which :class:`.Engine` should be used for a particular SQL + operation. The complete heuristics for resolution are + described at :meth:`.Session.get_bind`. Usage looks like:: + + Session = sessionmaker(binds={ + SomeMappedClass: create_engine('postgresql://engine1'), + SomeDeclarativeBase: create_engine('postgresql://engine2'), + some_mapper: create_engine('postgresql://engine3'), + some_table: create_engine('postgresql://engine4'), + }) + + .. seealso:: + + :ref:`session_partitioning` + + :meth:`.Session.bind_mapper` + + :meth:`.Session.bind_table` + + :meth:`.Session.get_bind` + + + :param \class_: Specify an alternate class other than + ``sqlalchemy.orm.session.Session`` which should be used by the + returned class. This is the only argument that is local to the + :class:`.sessionmaker` function, and is not sent directly to the + constructor for ``Session``. + + :param enable_baked_queries: defaults to ``True``. A flag consumed + by the :mod:`sqlalchemy.ext.baked` extension to determine if + "baked queries" should be cached, as is the normal operation + of this extension. When set to ``False``, all caching is disabled, + including baked queries defined by the calling application as + well as those used internally. Setting this flag to ``False`` + can significantly reduce memory use, however will also degrade + performance for those areas that make use of baked queries + (such as relationship loaders). Additionally, baked query + logic in the calling application or potentially within the ORM + that may be malfunctioning due to cache key collisions or similar + can be flagged by observing if this flag resolves the issue. + + .. versionadded:: 1.2 + + :param _enable_transaction_accounting: Defaults to ``True``. A + legacy-only flag which when ``False`` disables *all* 0.5-style + object accounting on transaction boundaries, including auto-expiry + of instances on rollback and commit, maintenance of the "new" and + "deleted" lists upon rollback, and autoflush of pending changes + upon :meth:`~.Session.begin`, all of which are interdependent. + + :param expire_on_commit: Defaults to ``True``. When ``True``, all + instances will be fully expired after each :meth:`~.commit`, + so that all attribute/object access subsequent to a completed + transaction will load from the most recent database state. + + :param extension: An optional + :class:`~.SessionExtension` instance, or a list + of such instances, which will receive pre- and post- commit and + flush events, as well as a post-rollback event. **Deprecated.** + Please see :class:`.SessionEvents`. + + :param info: optional dictionary of arbitrary data to be associated + with this :class:`.Session`. Is available via the + :attr:`.Session.info` attribute. Note the dictionary is copied at + construction time so that modifications to the per- + :class:`.Session` dictionary will be local to that + :class:`.Session`. + + .. versionadded:: 0.9.0 + + :param query_cls: Class which should be used to create new Query + objects, as returned by the :meth:`~.Session.query` method. + Defaults to :class:`.Query`. + + :param twophase: When ``True``, all transactions will be started as + a "two phase" transaction, i.e. using the "two phase" semantics + of the database in use along with an XID. During a + :meth:`~.commit`, after :meth:`~.flush` has been issued for all + attached databases, the :meth:`~.TwoPhaseTransaction.prepare` + method on each database's :class:`.TwoPhaseTransaction` will be + called. This allows each database to roll back the entire + transaction, before each transaction is committed. + + :param weak_identity_map: Defaults to ``True`` - when set to + ``False``, objects placed in the :class:`.Session` will be + strongly referenced until explicitly removed or the + :class:`.Session` is closed. **Deprecated** - The strong + reference identity map is legacy. See the + recipe at :ref:`session_referencing_behavior` for + an event-based approach to maintaining strong identity + references. + + """ + + if weak_identity_map: + self._identity_cls = identity.WeakInstanceDict + else: + util.warn_deprecated( + "weak_identity_map=False is deprecated. " + "See the documentation on 'Session Referencing Behavior' " + "for an event-based approach to maintaining strong identity " + "references.") + + self._identity_cls = identity.StrongInstanceDict + self.identity_map = self._identity_cls() + + self._new = {} # InstanceState->object, strong refs object + self._deleted = {} # same + self.bind = bind + self.__binds = {} + self._flushing = False + self._warn_on_events = False + self.transaction = None + self.hash_key = _new_sessionid() + self.autoflush = autoflush + self.autocommit = autocommit + self.expire_on_commit = expire_on_commit + self.enable_baked_queries = enable_baked_queries + self._enable_transaction_accounting = _enable_transaction_accounting + self.twophase = twophase + self._query_cls = query_cls + if info: + self.info.update(info) + + if extension: + for ext in util.to_list(extension): + SessionExtension._adapt_listener(self, ext) + + if binds is not None: + for key, bind in binds.items(): + self._add_bind(key, bind) + + if not self.autocommit: + self.begin() + _sessions[self.hash_key] = self + + connection_callable = None + + transaction = None + """The current active or inactive :class:`.SessionTransaction`.""" + + @util.memoized_property + def info(self): + """A user-modifiable dictionary. + + The initial value of this dictionary can be populated using the + ``info`` argument to the :class:`.Session` constructor or + :class:`.sessionmaker` constructor or factory methods. The dictionary + here is always local to this :class:`.Session` and can be modified + independently of all other :class:`.Session` objects. + + .. versionadded:: 0.9.0 + + """ + return {} + + def begin(self, subtransactions=False, nested=False): + """Begin a transaction on this :class:`.Session`. + + .. warning:: + + The :meth:`.Session.begin` method is part of a larger pattern + of use with the :class:`.Session` known as **autocommit mode**. + This is essentially a **legacy mode of use** and is + not necessary for new applications. The :class:`.Session` + normally handles the work of "begin" transparently, which in + turn relies upon the Python DBAPI to transparently "begin" + transactions; there is **no need to explcitly begin transactions** + when using modern :class:`.Session` programming patterns. + In its default mode of ``autocommit=False``, the + :class:`.Session` does all of its work within + the context of a transaction, so as soon as you call + :meth:`.Session.commit`, the next transaction is implicitly + started when the next database operation is invoked. See + :ref:`session_autocommit` for further background. + + The method will raise an error if this :class:`.Session` is already + inside of a transaction, unless + :paramref:`~.Session.begin.subtransactions` or + :paramref:`~.Session.begin.nested` are specified. A "subtransaction" + is essentially a code embedding pattern that does not affect the + transactional state of the database connection unless a rollback is + emitted, in which case the whole transaction is rolled back. For + documentation on subtransactions, please see + :ref:`session_subtransactions`. + + :param subtransactions: if True, indicates that this + :meth:`~.Session.begin` can create a "subtransaction". + + :param nested: if True, begins a SAVEPOINT transaction and is equivalent + to calling :meth:`~.Session.begin_nested`. For documentation on + SAVEPOINT transactions, please see :ref:`session_begin_nested`. + + :return: the :class:`.SessionTransaction` object. Note that + :class:`.SessionTransaction` + acts as a Python context manager, allowing :meth:`.Session.begin` + to be used in a "with" block. See :ref:`session_autocommit` for + an example. + + .. seealso:: + + :ref:`session_autocommit` + + :meth:`.Session.begin_nested` + + + """ + if self.transaction is not None: + if subtransactions or nested: + self.transaction = self.transaction._begin( + nested=nested) + else: + raise sa_exc.InvalidRequestError( + "A transaction is already begun. Use " + "subtransactions=True to allow subtransactions.") + else: + self.transaction = SessionTransaction( + self, nested=nested) + return self.transaction # needed for __enter__/__exit__ hook + + def begin_nested(self): + """Begin a "nested" transaction on this Session, e.g. SAVEPOINT. + + The target database(s) and associated drivers must support SQL + SAVEPOINT for this method to function correctly. + + For documentation on SAVEPOINT + transactions, please see :ref:`session_begin_nested`. + + :return: the :class:`.SessionTransaction` object. Note that + :class:`.SessionTransaction` acts as a context manager, allowing + :meth:`.Session.begin_nested` to be used in a "with" block. + See :ref:`session_begin_nested` for a usage example. + + .. seealso:: + + :ref:`session_begin_nested` + + :ref:`pysqlite_serializable` - special workarounds required + with the SQLite driver in order for SAVEPOINT to work + correctly. + + """ + return self.begin(nested=True) + + def rollback(self): + """Rollback the current transaction in progress. + + If no transaction is in progress, this method is a pass-through. + + This method rolls back the current transaction or nested transaction + regardless of subtransactions being in effect. All subtransactions up + to the first real transaction are closed. Subtransactions occur when + :meth:`.begin` is called multiple times. + + .. seealso:: + + :ref:`session_rollback` + + """ + if self.transaction is None: + pass + else: + self.transaction.rollback() + + def commit(self): + """Flush pending changes and commit the current transaction. + + If no transaction is in progress, this method raises an + :exc:`~sqlalchemy.exc.InvalidRequestError`. + + By default, the :class:`.Session` also expires all database + loaded state on all ORM-managed attributes after transaction commit. + This so that subsequent operations load the most recent + data from the database. This behavior can be disabled using + the ``expire_on_commit=False`` option to :class:`.sessionmaker` or + the :class:`.Session` constructor. + + If a subtransaction is in effect (which occurs when begin() is called + multiple times), the subtransaction will be closed, and the next call + to ``commit()`` will operate on the enclosing transaction. + + When using the :class:`.Session` in its default mode of + ``autocommit=False``, a new transaction will + be begun immediately after the commit, but note that the newly begun + transaction does *not* use any connection resources until the first + SQL is actually emitted. + + .. seealso:: + + :ref:`session_committing` + + """ + if self.transaction is None: + if not self.autocommit: + self.begin() + else: + raise sa_exc.InvalidRequestError("No transaction is begun.") + + self.transaction.commit() + + def prepare(self): + """Prepare the current transaction in progress for two phase commit. + + If no transaction is in progress, this method raises an + :exc:`~sqlalchemy.exc.InvalidRequestError`. + + Only root transactions of two phase sessions can be prepared. If the + current transaction is not such, an + :exc:`~sqlalchemy.exc.InvalidRequestError` is raised. + + """ + if self.transaction is None: + if not self.autocommit: + self.begin() + else: + raise sa_exc.InvalidRequestError("No transaction is begun.") + + self.transaction.prepare() + + def connection(self, mapper=None, clause=None, + bind=None, + close_with_result=False, + execution_options=None, + **kw): + r"""Return a :class:`.Connection` object corresponding to this + :class:`.Session` object's transactional state. + + If this :class:`.Session` is configured with ``autocommit=False``, + either the :class:`.Connection` corresponding to the current + transaction is returned, or if no transaction is in progress, a new + one is begun and the :class:`.Connection` returned (note that no + transactional state is established with the DBAPI until the first + SQL statement is emitted). + + Alternatively, if this :class:`.Session` is configured with + ``autocommit=True``, an ad-hoc :class:`.Connection` is returned + using :meth:`.Engine.contextual_connect` on the underlying + :class:`.Engine`. + + Ambiguity in multi-bind or unbound :class:`.Session` objects can be + resolved through any of the optional keyword arguments. This + ultimately makes usage of the :meth:`.get_bind` method for resolution. + + :param bind: + Optional :class:`.Engine` to be used as the bind. If + this engine is already involved in an ongoing transaction, + that connection will be used. This argument takes precedence + over ``mapper``, ``clause``. + + :param mapper: + Optional :func:`.mapper` mapped class, used to identify + the appropriate bind. This argument takes precedence over + ``clause``. + + :param clause: + A :class:`.ClauseElement` (i.e. :func:`~.sql.expression.select`, + :func:`~.sql.expression.text`, + etc.) which will be used to locate a bind, if a bind + cannot otherwise be identified. + + :param close_with_result: Passed to :meth:`.Engine.connect`, + indicating the :class:`.Connection` should be considered + "single use", automatically closing when the first result set is + closed. This flag only has an effect if this :class:`.Session` is + configured with ``autocommit=True`` and does not already have a + transaction in progress. + + :param execution_options: a dictionary of execution options that will + be passed to :meth:`.Connection.execution_options`, **when the + connection is first procured only**. If the connection is already + present within the :class:`.Session`, a warning is emitted and + the arguments are ignored. + + .. versionadded:: 0.9.9 + + .. seealso:: + + :ref:`session_transaction_isolation` + + :param \**kw: + Additional keyword arguments are sent to :meth:`get_bind()`, + allowing additional arguments to be passed to custom + implementations of :meth:`get_bind`. + + """ + if bind is None: + bind = self.get_bind(mapper, clause=clause, **kw) + + return self._connection_for_bind(bind, + close_with_result=close_with_result, + execution_options=execution_options) + + def _connection_for_bind(self, engine, execution_options=None, **kw): + if self.transaction is not None: + return self.transaction._connection_for_bind( + engine, execution_options) + else: + conn = engine.contextual_connect(**kw) + if execution_options: + conn = conn.execution_options(**execution_options) + return conn + + def execute(self, clause, params=None, mapper=None, bind=None, **kw): + r"""Execute a SQL expression construct or string statement within + the current transaction. + + Returns a :class:`.ResultProxy` representing + results of the statement execution, in the same manner as that of an + :class:`.Engine` or + :class:`.Connection`. + + E.g.:: + + result = session.execute( + user_table.select().where(user_table.c.id == 5) + ) + + :meth:`~.Session.execute` accepts any executable clause construct, + such as :func:`~.sql.expression.select`, + :func:`~.sql.expression.insert`, + :func:`~.sql.expression.update`, + :func:`~.sql.expression.delete`, and + :func:`~.sql.expression.text`. Plain SQL strings can be passed + as well, which in the case of :meth:`.Session.execute` only + will be interpreted the same as if it were passed via a + :func:`~.expression.text` construct. That is, the following usage:: + + result = session.execute( + "SELECT * FROM user WHERE id=:param", + {"param":5} + ) + + is equivalent to:: + + from sqlalchemy import text + result = session.execute( + text("SELECT * FROM user WHERE id=:param"), + {"param":5} + ) + + The second positional argument to :meth:`.Session.execute` is an + optional parameter set. Similar to that of + :meth:`.Connection.execute`, whether this is passed as a single + dictionary, or a list of dictionaries, determines whether the DBAPI + cursor's ``execute()`` or ``executemany()`` is used to execute the + statement. An INSERT construct may be invoked for a single row:: + + result = session.execute( + users.insert(), {"id": 7, "name": "somename"}) + + or for multiple rows:: + + result = session.execute(users.insert(), [ + {"id": 7, "name": "somename7"}, + {"id": 8, "name": "somename8"}, + {"id": 9, "name": "somename9"} + ]) + + The statement is executed within the current transactional context of + this :class:`.Session`. The :class:`.Connection` which is used + to execute the statement can also be acquired directly by + calling the :meth:`.Session.connection` method. Both methods use + a rule-based resolution scheme in order to determine the + :class:`.Connection`, which in the average case is derived directly + from the "bind" of the :class:`.Session` itself, and in other cases + can be based on the :func:`.mapper` + and :class:`.Table` objects passed to the method; see the + documentation for :meth:`.Session.get_bind` for a full description of + this scheme. + + The :meth:`.Session.execute` method does *not* invoke autoflush. + + The :class:`.ResultProxy` returned by the :meth:`.Session.execute` + method is returned with the "close_with_result" flag set to true; + the significance of this flag is that if this :class:`.Session` is + autocommitting and does not have a transaction-dedicated + :class:`.Connection` available, a temporary :class:`.Connection` is + established for the statement execution, which is closed (meaning, + returned to the connection pool) when the :class:`.ResultProxy` has + consumed all available data. This applies *only* when the + :class:`.Session` is configured with autocommit=True and no + transaction has been started. + + :param clause: + An executable statement (i.e. an :class:`.Executable` expression + such as :func:`.expression.select`) or string SQL statement + to be executed. + + :param params: + Optional dictionary, or list of dictionaries, containing + bound parameter values. If a single dictionary, single-row + execution occurs; if a list of dictionaries, an + "executemany" will be invoked. The keys in each dictionary + must correspond to parameter names present in the statement. + + :param mapper: + Optional :func:`.mapper` or mapped class, used to identify + the appropriate bind. This argument takes precedence over + ``clause`` when locating a bind. See :meth:`.Session.get_bind` + for more details. + + :param bind: + Optional :class:`.Engine` to be used as the bind. If + this engine is already involved in an ongoing transaction, + that connection will be used. This argument takes + precedence over ``mapper`` and ``clause`` when locating + a bind. + + :param \**kw: + Additional keyword arguments are sent to :meth:`.Session.get_bind()` + to allow extensibility of "bind" schemes. + + .. seealso:: + + :ref:`sqlexpression_toplevel` - Tutorial on using Core SQL + constructs. + + :ref:`connections_toplevel` - Further information on direct + statement execution. + + :meth:`.Connection.execute` - core level statement execution + method, which is :meth:`.Session.execute` ultimately uses + in order to execute the statement. + + """ + clause = expression._literal_as_text(clause) + + if bind is None: + bind = self.get_bind(mapper, clause=clause, **kw) + + return self._connection_for_bind( + bind, close_with_result=True).execute(clause, params or {}) + + def scalar(self, clause, params=None, mapper=None, bind=None, **kw): + """Like :meth:`~.Session.execute` but return a scalar result.""" + + return self.execute( + clause, params=params, mapper=mapper, bind=bind, **kw).scalar() + + def close(self): + """Close this Session. + + This clears all items and ends any transaction in progress. + + If this session were created with ``autocommit=False``, a new + transaction is immediately begun. Note that this new transaction does + not use any connection resources until they are first needed. + + """ + self._close_impl(invalidate=False) + + def invalidate(self): + """Close this Session, using connection invalidation. + + This is a variant of :meth:`.Session.close` that will additionally + ensure that the :meth:`.Connection.invalidate` method will be called + on all :class:`.Connection` objects. This can be called when + the database is known to be in a state where the connections are + no longer safe to be used. + + E.g.:: + + try: + sess = Session() + sess.add(User()) + sess.commit() + except gevent.Timeout: + sess.invalidate() + raise + except: + sess.rollback() + raise + + This clears all items and ends any transaction in progress. + + If this session were created with ``autocommit=False``, a new + transaction is immediately begun. Note that this new transaction does + not use any connection resources until they are first needed. + + .. versionadded:: 0.9.9 + + """ + self._close_impl(invalidate=True) + + def _close_impl(self, invalidate): + self.expunge_all() + if self.transaction is not None: + for transaction in self.transaction._iterate_self_and_parents(): + transaction.close(invalidate) + + def expunge_all(self): + """Remove all object instances from this ``Session``. + + This is equivalent to calling ``expunge(obj)`` on all objects in this + ``Session``. + + """ + + all_states = self.identity_map.all_states() + list(self._new) + self.identity_map = self._identity_cls() + self._new = {} + self._deleted = {} + + statelib.InstanceState._detach_states( + all_states, self + ) + + def _add_bind(self, key, bind): + try: + insp = inspect(key) + except sa_exc.NoInspectionAvailable: + if not isinstance(key, type): + raise sa_exc.ArgumentError( + "Not an acceptable bind target: %s" % key) + else: + self.__binds[key] = bind + else: + if insp.is_selectable: + self.__binds[insp] = bind + elif insp.is_mapper: + self.__binds[insp.class_] = bind + for selectable in insp._all_tables: + self.__binds[selectable] = bind + else: + raise sa_exc.ArgumentError( + "Not an acceptable bind target: %s" % key) + + def bind_mapper(self, mapper, bind): + """Associate a :class:`.Mapper` or arbitrary Python class with a + "bind", e.g. an :class:`.Engine` or :class:`.Connection`. + + The given entity is added to a lookup used by the + :meth:`.Session.get_bind` method. + + :param mapper: a :class:`.Mapper` object, or an instance of a mapped + class, or any Python class that is the base of a set of mapped + classes. + + :param bind: an :class:`.Engine` or :class:`.Connection` object. + + .. seealso:: + + :ref:`session_partitioning` + + :paramref:`.Session.binds` + + :meth:`.Session.bind_table` + + + """ + self._add_bind(mapper, bind) + + def bind_table(self, table, bind): + """Associate a :class:`.Table` with a "bind", e.g. an :class:`.Engine` + or :class:`.Connection`. + + The given :class:`.Table` is added to a lookup used by the + :meth:`.Session.get_bind` method. + + :param table: a :class:`.Table` object, which is typically the target + of an ORM mapping, or is present within a selectable that is + mapped. + + :param bind: an :class:`.Engine` or :class:`.Connection` object. + + .. seealso:: + + :ref:`session_partitioning` + + :paramref:`.Session.binds` + + :meth:`.Session.bind_mapper` + + + """ + self._add_bind(table, bind) + + def get_bind(self, mapper=None, clause=None): + """Return a "bind" to which this :class:`.Session` is bound. + + The "bind" is usually an instance of :class:`.Engine`, + except in the case where the :class:`.Session` has been + explicitly bound directly to a :class:`.Connection`. + + For a multiply-bound or unbound :class:`.Session`, the + ``mapper`` or ``clause`` arguments are used to determine the + appropriate bind to return. + + Note that the "mapper" argument is usually present + when :meth:`.Session.get_bind` is called via an ORM + operation such as a :meth:`.Session.query`, each + individual INSERT/UPDATE/DELETE operation within a + :meth:`.Session.flush`, call, etc. + + The order of resolution is: + + 1. if mapper given and session.binds is present, + locate a bind based first on the mapper in use, then + on the mapped class in use, then on any base classes that are + present in the ``__mro__`` of the mapped class, from more specific + superclasses to more general. + 2. if clause given and session.binds is present, + locate a bind based on :class:`.Table` objects + found in the given clause present in session.binds. + 3. if session.bind is present, return that. + 4. if clause given, attempt to return a bind + linked to the :class:`.MetaData` ultimately + associated with the clause. + 5. if mapper given, attempt to return a bind + linked to the :class:`.MetaData` ultimately + associated with the :class:`.Table` or other + selectable to which the mapper is mapped. + 6. No bind can be found, :exc:`~sqlalchemy.exc.UnboundExecutionError` + is raised. + + Note that the :meth:`.Session.get_bind` method can be overridden on + a user-defined subclass of :class:`.Session` to provide any kind + of bind resolution scheme. See the example at + :ref:`session_custom_partitioning`. + + :param mapper: + Optional :func:`.mapper` mapped class or instance of + :class:`.Mapper`. The bind can be derived from a :class:`.Mapper` + first by consulting the "binds" map associated with this + :class:`.Session`, and secondly by consulting the :class:`.MetaData` + associated with the :class:`.Table` to which the :class:`.Mapper` + is mapped for a bind. + + :param clause: + A :class:`.ClauseElement` (i.e. :func:`~.sql.expression.select`, + :func:`~.sql.expression.text`, + etc.). If the ``mapper`` argument is not present or could not + produce a bind, the given expression construct will be searched + for a bound element, typically a :class:`.Table` associated with + bound :class:`.MetaData`. + + .. seealso:: + + :ref:`session_partitioning` + + :paramref:`.Session.binds` + + :meth:`.Session.bind_mapper` + + :meth:`.Session.bind_table` + + """ + + if mapper is clause is None: + if self.bind: + return self.bind + else: + raise sa_exc.UnboundExecutionError( + "This session is not bound to a single Engine or " + "Connection, and no context was provided to locate " + "a binding.") + + if mapper is not None: + try: + mapper = inspect(mapper) + except sa_exc.NoInspectionAvailable: + if isinstance(mapper, type): + raise exc.UnmappedClassError(mapper) + else: + raise + + if self.__binds: + if mapper: + for cls in mapper.class_.__mro__: + if cls in self.__binds: + return self.__binds[cls] + if clause is None: + clause = mapper.mapped_table + + if clause is not None: + for t in sql_util.find_tables(clause, include_crud=True): + if t in self.__binds: + return self.__binds[t] + + if self.bind: + return self.bind + + if isinstance(clause, sql.expression.ClauseElement) and clause.bind: + return clause.bind + + if mapper and mapper.mapped_table.bind: + return mapper.mapped_table.bind + + context = [] + if mapper is not None: + context.append('mapper %s' % mapper) + if clause is not None: + context.append('SQL expression') + + raise sa_exc.UnboundExecutionError( + "Could not locate a bind configured on %s or this Session" % ( + ', '.join(context))) + + def query(self, *entities, **kwargs): + """Return a new :class:`.Query` object corresponding to this + :class:`.Session`.""" + + return self._query_cls(entities, self, **kwargs) + + @property + @util.contextmanager + def no_autoflush(self): + """Return a context manager that disables autoflush. + + e.g.:: + + with session.no_autoflush: + + some_object = SomeClass() + session.add(some_object) + # won't autoflush + some_object.related_thing = session.query(SomeRelated).first() + + Operations that proceed within the ``with:`` block + will not be subject to flushes occurring upon query + access. This is useful when initializing a series + of objects which involve existing database queries, + where the uncompleted object should not yet be flushed. + + .. versionadded:: 0.7.6 + + """ + autoflush = self.autoflush + self.autoflush = False + try: + yield self + finally: + self.autoflush = autoflush + + def _autoflush(self): + if self.autoflush and not self._flushing: + try: + self.flush() + except sa_exc.StatementError as e: + # note we are reraising StatementError as opposed to + # raising FlushError with "chaining" to remain compatible + # with code that catches StatementError, IntegrityError, + # etc. + e.add_detail( + "raised as a result of Query-invoked autoflush; " + "consider using a session.no_autoflush block if this " + "flush is occurring prematurely") + util.raise_from_cause(e) + + def refresh( + self, instance, attribute_names=None, with_for_update=None, + lockmode=None): + """Expire and refresh the attributes on the given instance. + + A query will be issued to the database and all attributes will be + refreshed with their current database value. + + Lazy-loaded relational attributes will remain lazily loaded, so that + the instance-wide refresh operation will be followed immediately by + the lazy load of that attribute. + + Eagerly-loaded relational attributes will eagerly load within the + single refresh operation. + + Note that a highly isolated transaction will return the same values as + were previously read in that same transaction, regardless of changes + in database state outside of that transaction - usage of + :meth:`~Session.refresh` usually only makes sense if non-ORM SQL + statement were emitted in the ongoing transaction, or if autocommit + mode is turned on. + + :param attribute_names: optional. An iterable collection of + string attribute names indicating a subset of attributes to + be refreshed. + + :param with_for_update: optional boolean ``True`` indicating FOR UPDATE + should be used, or may be a dictionary containing flags to + indicate a more specific set of FOR UPDATE flags for the SELECT; + flags should match the parameters of :meth:`.Query.with_for_update`. + Supersedes the :paramref:`.Session.refresh.lockmode` parameter. + + .. versionadded:: 1.2 + + :param lockmode: Passed to the :class:`~sqlalchemy.orm.query.Query` + as used by :meth:`~sqlalchemy.orm.query.Query.with_lockmode`. + Superseded by :paramref:`.Session.refresh.with_for_update`. + + .. seealso:: + + :ref:`session_expire` - introductory material + + :meth:`.Session.expire` + + :meth:`.Session.expire_all` + + """ + try: + state = attributes.instance_state(instance) + except exc.NO_STATE: + raise exc.UnmappedInstanceError(instance) + + self._expire_state(state, attribute_names) + + if with_for_update == {}: + raise sa_exc.ArgumentError( + "with_for_update should be the boolean value " + "True, or a dictionary with options. " + "A blank dictionary is ambiguous.") + + if lockmode: + with_for_update = query.LockmodeArg.parse_legacy_query(lockmode) + elif with_for_update is not None: + if with_for_update is True: + with_for_update = query.LockmodeArg() + elif with_for_update: + with_for_update = query.LockmodeArg(**with_for_update) + else: + with_for_update = None + + if loading.load_on_ident( + self.query(object_mapper(instance)), + state.key, refresh_state=state, + with_for_update=with_for_update, + only_load_props=attribute_names) is None: + raise sa_exc.InvalidRequestError( + "Could not refresh instance '%s'" % + instance_str(instance)) + + def expire_all(self): + """Expires all persistent instances within this Session. + + When any attributes on a persistent instance is next accessed, + a query will be issued using the + :class:`.Session` object's current transactional context in order to + load all expired attributes for the given instance. Note that + a highly isolated transaction will return the same values as were + previously read in that same transaction, regardless of changes + in database state outside of that transaction. + + To expire individual objects and individual attributes + on those objects, use :meth:`Session.expire`. + + The :class:`.Session` object's default behavior is to + expire all state whenever the :meth:`Session.rollback` + or :meth:`Session.commit` methods are called, so that new + state can be loaded for the new transaction. For this reason, + calling :meth:`Session.expire_all` should not be needed when + autocommit is ``False``, assuming the transaction is isolated. + + .. seealso:: + + :ref:`session_expire` - introductory material + + :meth:`.Session.expire` + + :meth:`.Session.refresh` + + """ + for state in self.identity_map.all_states(): + state._expire(state.dict, self.identity_map._modified) + + def expire(self, instance, attribute_names=None): + """Expire the attributes on an instance. + + Marks the attributes of an instance as out of date. When an expired + attribute is next accessed, a query will be issued to the + :class:`.Session` object's current transactional context in order to + load all expired attributes for the given instance. Note that + a highly isolated transaction will return the same values as were + previously read in that same transaction, regardless of changes + in database state outside of that transaction. + + To expire all objects in the :class:`.Session` simultaneously, + use :meth:`Session.expire_all`. + + The :class:`.Session` object's default behavior is to + expire all state whenever the :meth:`Session.rollback` + or :meth:`Session.commit` methods are called, so that new + state can be loaded for the new transaction. For this reason, + calling :meth:`Session.expire` only makes sense for the specific + case that a non-ORM SQL statement was emitted in the current + transaction. + + :param instance: The instance to be refreshed. + :param attribute_names: optional list of string attribute names + indicating a subset of attributes to be expired. + + .. seealso:: + + :ref:`session_expire` - introductory material + + :meth:`.Session.expire` + + :meth:`.Session.refresh` + + """ + try: + state = attributes.instance_state(instance) + except exc.NO_STATE: + raise exc.UnmappedInstanceError(instance) + self._expire_state(state, attribute_names) + + def _expire_state(self, state, attribute_names): + self._validate_persistent(state) + if attribute_names: + state._expire_attributes(state.dict, attribute_names) + else: + # pre-fetch the full cascade since the expire is going to + # remove associations + cascaded = list(state.manager.mapper.cascade_iterator( + 'refresh-expire', state)) + self._conditional_expire(state) + for o, m, st_, dct_ in cascaded: + self._conditional_expire(st_) + + def _conditional_expire(self, state): + """Expire a state if persistent, else expunge if pending""" + + if state.key: + state._expire(state.dict, self.identity_map._modified) + elif state in self._new: + self._new.pop(state) + state._detach(self) + + @util.deprecated("0.7", "The non-weak-referencing identity map " + "feature is no longer needed.") + def prune(self): + """Remove unreferenced instances cached in the identity map. + + Note that this method is only meaningful if "weak_identity_map" is set + to False. The default weak identity map is self-pruning. + + Removes any object in this Session's identity map that is not + referenced in user code, modified, new or scheduled for deletion. + Returns the number of objects pruned. + + """ + return self.identity_map.prune() + + def expunge(self, instance): + """Remove the `instance` from this ``Session``. + + This will free all internal references to the instance. Cascading + will be applied according to the *expunge* cascade rule. + + """ + try: + state = attributes.instance_state(instance) + except exc.NO_STATE: + raise exc.UnmappedInstanceError(instance) + if state.session_id is not self.hash_key: + raise sa_exc.InvalidRequestError( + "Instance %s is not present in this Session" % + state_str(state)) + + cascaded = list(state.manager.mapper.cascade_iterator( + 'expunge', state)) + self._expunge_states( + [state] + [st_ for o, m, st_, dct_ in cascaded] + ) + + def _expunge_states(self, states, to_transient=False): + for state in states: + if state in self._new: + self._new.pop(state) + elif self.identity_map.contains_state(state): + self.identity_map.safe_discard(state) + self._deleted.pop(state, None) + elif self.transaction: + # state is "detached" from being deleted, but still present + # in the transaction snapshot + self.transaction._deleted.pop(state, None) + statelib.InstanceState._detach_states( + states, self, to_transient=to_transient) + + def _register_newly_persistent(self, states): + pending_to_persistent = self.dispatch.pending_to_persistent or None + for state in states: + mapper = _state_mapper(state) + + # prevent against last minute dereferences of the object + obj = state.obj() + if obj is not None: + + instance_key = mapper._identity_key_from_state(state) + + if _none_set.intersection(instance_key[1]) and \ + not mapper.allow_partial_pks or \ + _none_set.issuperset(instance_key[1]): + raise exc.FlushError( + "Instance %s has a NULL identity key. If this is an " + "auto-generated value, check that the database table " + "allows generation of new primary key values, and " + "that the mapped Column object is configured to " + "expect these generated values. Ensure also that " + "this flush() is not occurring at an inappropriate " + "time, such as within a load() event." + % state_str(state) + ) + + if state.key is None: + state.key = instance_key + elif state.key != instance_key: + # primary key switch. use safe_discard() in case another + # state has already replaced this one in the identity + # map (see test/orm/test_naturalpks.py ReversePKsTest) + self.identity_map.safe_discard(state) + if state in self.transaction._key_switches: + orig_key = self.transaction._key_switches[state][0] + else: + orig_key = state.key + self.transaction._key_switches[state] = ( + orig_key, instance_key) + state.key = instance_key + + self.identity_map.replace(state) + state._orphaned_outside_of_session = False + + statelib.InstanceState._commit_all_states( + ((state, state.dict) for state in states), + self.identity_map + ) + + self._register_altered(states) + + if pending_to_persistent is not None: + for state in states: + pending_to_persistent(self, state.obj()) + + # remove from new last, might be the last strong ref + for state in set(states).intersection(self._new): + self._new.pop(state) + + def _register_altered(self, states): + if self._enable_transaction_accounting and self.transaction: + for state in states: + if state in self._new: + self.transaction._new[state] = True + else: + self.transaction._dirty[state] = True + + def _remove_newly_deleted(self, states): + persistent_to_deleted = self.dispatch.persistent_to_deleted or None + for state in states: + if self._enable_transaction_accounting and self.transaction: + self.transaction._deleted[state] = True + + if persistent_to_deleted is not None: + # get a strong reference before we pop out of + # self._deleted + obj = state.obj() + + self.identity_map.safe_discard(state) + self._deleted.pop(state, None) + state._deleted = True + # can't call state._detach() here, because this state + # is still in the transaction snapshot and needs to be + # tracked as part of that + if persistent_to_deleted is not None: + persistent_to_deleted(self, obj) + + def add(self, instance, _warn=True): + """Place an object in the ``Session``. + + Its state will be persisted to the database on the next flush + operation. + + Repeated calls to ``add()`` will be ignored. The opposite of ``add()`` + is ``expunge()``. + + """ + if _warn and self._warn_on_events: + self._flush_warning("Session.add()") + + try: + state = attributes.instance_state(instance) + except exc.NO_STATE: + raise exc.UnmappedInstanceError(instance) + + self._save_or_update_state(state) + + def add_all(self, instances): + """Add the given collection of instances to this ``Session``.""" + + if self._warn_on_events: + self._flush_warning("Session.add_all()") + + for instance in instances: + self.add(instance, _warn=False) + + def _save_or_update_state(self, state): + state._orphaned_outside_of_session = False + self._save_or_update_impl(state) + + mapper = _state_mapper(state) + for o, m, st_, dct_ in mapper.cascade_iterator( + 'save-update', + state, + halt_on=self._contains_state): + self._save_or_update_impl(st_) + + def delete(self, instance): + """Mark an instance as deleted. + + The database delete operation occurs upon ``flush()``. + + """ + if self._warn_on_events: + self._flush_warning("Session.delete()") + + try: + state = attributes.instance_state(instance) + except exc.NO_STATE: + raise exc.UnmappedInstanceError(instance) + + self._delete_impl(state, instance, head=True) + + def _delete_impl(self, state, obj, head): + + if state.key is None: + if head: + raise sa_exc.InvalidRequestError( + "Instance '%s' is not persisted" % + state_str(state)) + else: + return + + to_attach = self._before_attach(state, obj) + + if state in self._deleted: + return + + self.identity_map.add(state) + + if to_attach: + self._after_attach(state, obj) + + if head: + # grab the cascades before adding the item to the deleted list + # so that autoflush does not delete the item + # the strong reference to the instance itself is significant here + cascade_states = list(state.manager.mapper.cascade_iterator( + 'delete', state)) + + self._deleted[state] = obj + + if head: + for o, m, st_, dct_ in cascade_states: + self._delete_impl(st_, o, False) + + def merge(self, instance, load=True): + """Copy the state of a given instance into a corresponding instance + within this :class:`.Session`. + + :meth:`.Session.merge` examines the primary key attributes of the + source instance, and attempts to reconcile it with an instance of the + same primary key in the session. If not found locally, it attempts + to load the object from the database based on primary key, and if + none can be located, creates a new instance. The state of each + attribute on the source instance is then copied to the target + instance. The resulting target instance is then returned by the + method; the original source instance is left unmodified, and + un-associated with the :class:`.Session` if not already. + + This operation cascades to associated instances if the association is + mapped with ``cascade="merge"``. + + See :ref:`unitofwork_merging` for a detailed discussion of merging. + + .. versionchanged:: 1.1 - :meth:`.Session.merge` will now reconcile + pending objects with overlapping primary keys in the same way + as persistent. See :ref:`change_3601` for discussion. + + :param instance: Instance to be merged. + :param load: Boolean, when False, :meth:`.merge` switches into + a "high performance" mode which causes it to forego emitting history + events as well as all database access. This flag is used for + cases such as transferring graphs of objects into a :class:`.Session` + from a second level cache, or to transfer just-loaded objects + into the :class:`.Session` owned by a worker thread or process + without re-querying the database. + + The ``load=False`` use case adds the caveat that the given + object has to be in a "clean" state, that is, has no pending changes + to be flushed - even if the incoming object is detached from any + :class:`.Session`. This is so that when + the merge operation populates local attributes and + cascades to related objects and + collections, the values can be "stamped" onto the + target object as is, without generating any history or attribute + events, and without the need to reconcile the incoming data with + any existing related objects or collections that might not + be loaded. The resulting objects from ``load=False`` are always + produced as "clean", so it is only appropriate that the given objects + should be "clean" as well, else this suggests a mis-use of the + method. + + + .. seealso:: + + :func:`.make_transient_to_detached` - provides for an alternative + means of "merging" a single object into the :class:`.Session` + + """ + + if self._warn_on_events: + self._flush_warning("Session.merge()") + + _recursive = {} + _resolve_conflict_map = {} + + if load: + # flush current contents if we expect to load data + self._autoflush() + + object_mapper(instance) # verify mapped + autoflush = self.autoflush + try: + self.autoflush = False + return self._merge( + attributes.instance_state(instance), + attributes.instance_dict(instance), + load=load, _recursive=_recursive, + _resolve_conflict_map=_resolve_conflict_map) + finally: + self.autoflush = autoflush + + def _merge(self, state, state_dict, load=True, _recursive=None, + _resolve_conflict_map=None): + mapper = _state_mapper(state) + if state in _recursive: + return _recursive[state] + + new_instance = False + key = state.key + + if key is None: + if not load: + raise sa_exc.InvalidRequestError( + "merge() with load=False option does not support " + "objects transient (i.e. unpersisted) objects. flush() " + "all changes on mapped instances before merging with " + "load=False.") + key = mapper._identity_key_from_state(state) + key_is_persistent = attributes.NEVER_SET not in key[1] and ( + not _none_set.intersection(key[1]) or + (mapper.allow_partial_pks and not _none_set.issuperset(key[1])) + ) + else: + key_is_persistent = True + + if key in self.identity_map: + try: + merged = self.identity_map[key] + except KeyError: + # object was GC'ed right as we checked for it + merged = None + else: + merged = None + + if merged is None: + if key_is_persistent and key in _resolve_conflict_map: + merged = _resolve_conflict_map[key] + + elif not load: + if state.modified: + raise sa_exc.InvalidRequestError( + "merge() with load=False option does not support " + "objects marked as 'dirty'. flush() all changes on " + "mapped instances before merging with load=False.") + merged = mapper.class_manager.new_instance() + merged_state = attributes.instance_state(merged) + merged_state.key = key + self._update_impl(merged_state) + new_instance = True + + elif key_is_persistent: + merged = self.query(mapper.class_).get(key[1]) + + if merged is None: + merged = mapper.class_manager.new_instance() + merged_state = attributes.instance_state(merged) + merged_dict = attributes.instance_dict(merged) + new_instance = True + self._save_or_update_state(merged_state) + else: + merged_state = attributes.instance_state(merged) + merged_dict = attributes.instance_dict(merged) + + _recursive[state] = merged + _resolve_conflict_map[key] = merged + + # check that we didn't just pull the exact same + # state out. + if state is not merged_state: + # version check if applicable + if mapper.version_id_col is not None: + existing_version = mapper._get_state_attr_by_column( + state, + state_dict, + mapper.version_id_col, + passive=attributes.PASSIVE_NO_INITIALIZE) + + merged_version = mapper._get_state_attr_by_column( + merged_state, + merged_dict, + mapper.version_id_col, + passive=attributes.PASSIVE_NO_INITIALIZE) + + if existing_version is not attributes.PASSIVE_NO_RESULT and \ + merged_version is not attributes.PASSIVE_NO_RESULT and \ + existing_version != merged_version: + raise exc.StaleDataError( + "Version id '%s' on merged state %s " + "does not match existing version '%s'. " + "Leave the version attribute unset when " + "merging to update the most recent version." + % ( + existing_version, + state_str(merged_state), + merged_version + )) + + merged_state.load_path = state.load_path + merged_state.load_options = state.load_options + + # since we are copying load_options, we need to copy + # the callables_ that would have been generated by those + # load_options. + # assumes that the callables we put in state.callables_ + # are not instance-specific (which they should not be) + merged_state._copy_callables(state) + + for prop in mapper.iterate_properties: + prop.merge(self, state, state_dict, + merged_state, merged_dict, + load, _recursive, _resolve_conflict_map) + + if not load: + # remove any history + merged_state._commit_all(merged_dict, self.identity_map) + + if new_instance: + merged_state.manager.dispatch.load(merged_state, None) + return merged + + def _validate_persistent(self, state): + if not self.identity_map.contains_state(state): + raise sa_exc.InvalidRequestError( + "Instance '%s' is not persistent within this Session" % + state_str(state)) + + def _save_impl(self, state): + if state.key is not None: + raise sa_exc.InvalidRequestError( + "Object '%s' already has an identity - " + "it can't be registered as pending" % state_str(state)) + + obj = state.obj() + to_attach = self._before_attach(state, obj) + if state not in self._new: + self._new[state] = obj + state.insert_order = len(self._new) + if to_attach: + self._after_attach(state, obj) + + def _update_impl(self, state, revert_deletion=False): + if state.key is None: + raise sa_exc.InvalidRequestError( + "Instance '%s' is not persisted" % + state_str(state)) + + if state._deleted: + if revert_deletion: + if not state._attached: + return + del state._deleted + else: + raise sa_exc.InvalidRequestError( + "Instance '%s' has been deleted. " + "Use the make_transient() " + "function to send this object back " + "to the transient state." % + state_str(state) + ) + + obj = state.obj() + + # check for late gc + if obj is None: + return + + to_attach = self._before_attach(state, obj) + + self._deleted.pop(state, None) + if revert_deletion: + self.identity_map.replace(state) + else: + self.identity_map.add(state) + + if to_attach: + self._after_attach(state, obj) + elif revert_deletion: + self.dispatch.deleted_to_persistent(self, obj) + + def _save_or_update_impl(self, state): + if state.key is None: + self._save_impl(state) + else: + self._update_impl(state) + + def enable_relationship_loading(self, obj): + """Associate an object with this :class:`.Session` for related + object loading. + + .. warning:: + + :meth:`.enable_relationship_loading` exists to serve special + use cases and is not recommended for general use. + + Accesses of attributes mapped with :func:`.relationship` + will attempt to load a value from the database using this + :class:`.Session` as the source of connectivity. The values + will be loaded based on foreign key and primary key values + present on this object - if not present, then those relationships + will be unavailable. + + The object will be attached to this session, but will + **not** participate in any persistence operations; its state + for almost all purposes will remain either "transient" or + "detached", except for the case of relationship loading. + + Also note that backrefs will often not work as expected. + Altering a relationship-bound attribute on the target object + may not fire off a backref event, if the effective value + is what was already loaded from a foreign-key-holding value. + + The :meth:`.Session.enable_relationship_loading` method is + similar to the ``load_on_pending`` flag on :func:`.relationship`. + Unlike that flag, :meth:`.Session.enable_relationship_loading` allows + an object to remain transient while still being able to load + related items. + + To make a transient object associated with a :class:`.Session` + via :meth:`.Session.enable_relationship_loading` pending, add + it to the :class:`.Session` using :meth:`.Session.add` normally. + If the object instead represents an existing idenity in the database, + it should be merged using :meth:`.Session.merge`. + + :meth:`.Session.enable_relationship_loading` does not improve + behavior when the ORM is used normally - object references should be + constructed at the object level, not at the foreign key level, so + that they are present in an ordinary way before flush() + proceeds. This method is not intended for general use. + + .. versionadded:: 0.8 + + .. seealso:: + + ``load_on_pending`` at :func:`.relationship` - this flag + allows per-relationship loading of many-to-ones on items that + are pending. + + :func:`.make_transient_to_detached` - allows for an object to + be added to a :class:`.Session` without SQL emitted, which then + will unexpire attributes on access. + + """ + state = attributes.instance_state(obj) + to_attach = self._before_attach(state, obj) + state._load_pending = True + if to_attach: + self._after_attach(state, obj) + + def _before_attach(self, state, obj): + if state.session_id == self.hash_key: + return False + + if state.session_id and state.session_id in _sessions: + raise sa_exc.InvalidRequestError( + "Object '%s' is already attached to session '%s' " + "(this is '%s')" % (state_str(state), + state.session_id, self.hash_key)) + + self.dispatch.before_attach(self, obj) + + return True + + def _after_attach(self, state, obj): + state.session_id = self.hash_key + if state.modified and state._strong_obj is None: + state._strong_obj = obj + self.dispatch.after_attach(self, obj) + + if state.key: + self.dispatch.detached_to_persistent(self, obj) + else: + self.dispatch.transient_to_pending(self, obj) + + def __contains__(self, instance): + """Return True if the instance is associated with this session. + + The instance may be pending or persistent within the Session for a + result of True. + + """ + try: + state = attributes.instance_state(instance) + except exc.NO_STATE: + raise exc.UnmappedInstanceError(instance) + return self._contains_state(state) + + def __iter__(self): + """Iterate over all pending or persistent instances within this + Session. + + """ + return iter( + list(self._new.values()) + list(self.identity_map.values())) + + def _contains_state(self, state): + return state in self._new or self.identity_map.contains_state(state) + + def flush(self, objects=None): + """Flush all the object changes to the database. + + Writes out all pending object creations, deletions and modifications + to the database as INSERTs, DELETEs, UPDATEs, etc. Operations are + automatically ordered by the Session's unit of work dependency + solver. + + Database operations will be issued in the current transactional + context and do not affect the state of the transaction, unless an + error occurs, in which case the entire transaction is rolled back. + You may flush() as often as you like within a transaction to move + changes from Python to the database's transaction buffer. + + For ``autocommit`` Sessions with no active manual transaction, flush() + will create a transaction on the fly that surrounds the entire set of + operations into the flush. + + :param objects: Optional; restricts the flush operation to operate + only on elements that are in the given collection. + + This feature is for an extremely narrow set of use cases where + particular objects may need to be operated upon before the + full flush() occurs. It is not intended for general use. + + """ + + if self._flushing: + raise sa_exc.InvalidRequestError("Session is already flushing") + + if self._is_clean(): + return + try: + self._flushing = True + self._flush(objects) + finally: + self._flushing = False + + def _flush_warning(self, method): + util.warn( + "Usage of the '%s' operation is not currently supported " + "within the execution stage of the flush process. " + "Results may not be consistent. Consider using alternative " + "event listeners or connection-level operations instead." + % method) + + def _is_clean(self): + return not self.identity_map.check_modified() and \ + not self._deleted and \ + not self._new + + def _flush(self, objects=None): + + dirty = self._dirty_states + if not dirty and not self._deleted and not self._new: + self.identity_map._modified.clear() + return + + flush_context = UOWTransaction(self) + + if self.dispatch.before_flush: + self.dispatch.before_flush(self, flush_context, objects) + # re-establish "dirty states" in case the listeners + # added + dirty = self._dirty_states + + deleted = set(self._deleted) + new = set(self._new) + + dirty = set(dirty).difference(deleted) + + # create the set of all objects we want to operate upon + if objects: + # specific list passed in + objset = set() + for o in objects: + try: + state = attributes.instance_state(o) + except exc.NO_STATE: + raise exc.UnmappedInstanceError(o) + objset.add(state) + else: + objset = None + + # store objects whose fate has been decided + processed = set() + + # put all saves/updates into the flush context. detect top-level + # orphans and throw them into deleted. + if objset: + proc = new.union(dirty).intersection(objset).difference(deleted) + else: + proc = new.union(dirty).difference(deleted) + + for state in proc: + is_orphan = _state_mapper(state)._is_orphan(state) + + is_persistent_orphan = is_orphan and state.has_identity + + if is_orphan and not is_persistent_orphan and \ + state._orphaned_outside_of_session: + self._expunge_states([state]) + else: + _reg = flush_context.register_object( + state, isdelete=is_persistent_orphan) + assert _reg, "Failed to add object to the flush context!" + processed.add(state) + + # put all remaining deletes into the flush context. + if objset: + proc = deleted.intersection(objset).difference(processed) + else: + proc = deleted.difference(processed) + for state in proc: + _reg = flush_context.register_object(state, isdelete=True) + assert _reg, "Failed to add object to the flush context!" + + if not flush_context.has_work: + return + + flush_context.transaction = transaction = self.begin( + subtransactions=True) + try: + self._warn_on_events = True + try: + flush_context.execute() + finally: + self._warn_on_events = False + + self.dispatch.after_flush(self, flush_context) + + flush_context.finalize_flush_changes() + + if not objects and self.identity_map._modified: + len_ = len(self.identity_map._modified) + + statelib.InstanceState._commit_all_states( + [(state, state.dict) for state in + self.identity_map._modified], + instance_dict=self.identity_map) + util.warn("Attribute history events accumulated on %d " + "previously clean instances " + "within inner-flush event handlers have been " + "reset, and will not result in database updates. " + "Consider using set_committed_value() within " + "inner-flush event handlers to avoid this warning." + % len_) + + # useful assertions: + # if not objects: + # assert not self.identity_map._modified + # else: + # assert self.identity_map._modified == \ + # self.identity_map._modified.difference(objects) + + self.dispatch.after_flush_postexec(self, flush_context) + + transaction.commit() + + except: + with util.safe_reraise(): + transaction.rollback(_capture_exception=True) + + def bulk_save_objects( + self, objects, return_defaults=False, update_changed_only=True): + """Perform a bulk save of the given list of objects. + + The bulk save feature allows mapped objects to be used as the + source of simple INSERT and UPDATE operations which can be more easily + grouped together into higher performing "executemany" + operations; the extraction of data from the objects is also performed + using a lower-latency process that ignores whether or not attributes + have actually been modified in the case of UPDATEs, and also ignores + SQL expressions. + + The objects as given are not added to the session and no additional + state is established on them, unless the ``return_defaults`` flag + is also set, in which case primary key attributes and server-side + default values will be populated. + + .. versionadded:: 1.0.0 + + .. warning:: + + The bulk save feature allows for a lower-latency INSERT/UPDATE + of rows at the expense of most other unit-of-work features. + Features such as object management, relationship handling, + and SQL clause support are **silently omitted** in favor of raw + INSERT/UPDATES of records. + + **Please read the list of caveats at** :ref:`bulk_operations` + **before using this method, and fully test and confirm the + functionality of all code developed using these systems.** + + :param objects: a list of mapped object instances. The mapped + objects are persisted as is, and are **not** associated with the + :class:`.Session` afterwards. + + For each object, whether the object is sent as an INSERT or an + UPDATE is dependent on the same rules used by the :class:`.Session` + in traditional operation; if the object has the + :attr:`.InstanceState.key` + attribute set, then the object is assumed to be "detached" and + will result in an UPDATE. Otherwise, an INSERT is used. + + In the case of an UPDATE, statements are grouped based on which + attributes have changed, and are thus to be the subject of each + SET clause. If ``update_changed_only`` is False, then all + attributes present within each object are applied to the UPDATE + statement, which may help in allowing the statements to be grouped + together into a larger executemany(), and will also reduce the + overhead of checking history on attributes. + + :param return_defaults: when True, rows that are missing values which + generate defaults, namely integer primary key defaults and sequences, + will be inserted **one at a time**, so that the primary key value + is available. In particular this will allow joined-inheritance + and other multi-table mappings to insert correctly without the need + to provide primary key values ahead of time; however, + :paramref:`.Session.bulk_save_objects.return_defaults` **greatly + reduces the performance gains** of the method overall. + + :param update_changed_only: when True, UPDATE statements are rendered + based on those attributes in each state that have logged changes. + When False, all attributes present are rendered into the SET clause + with the exception of primary key attributes. + + .. seealso:: + + :ref:`bulk_operations` + + :meth:`.Session.bulk_insert_mappings` + + :meth:`.Session.bulk_update_mappings` + + """ + for (mapper, isupdate), states in itertools.groupby( + (attributes.instance_state(obj) for obj in objects), + lambda state: (state.mapper, state.key is not None) + ): + self._bulk_save_mappings( + mapper, states, isupdate, True, + return_defaults, update_changed_only, False) + + def bulk_insert_mappings( + self, mapper, mappings, return_defaults=False, render_nulls=False): + """Perform a bulk insert of the given list of mapping dictionaries. + + The bulk insert feature allows plain Python dictionaries to be used as + the source of simple INSERT operations which can be more easily + grouped together into higher performing "executemany" + operations. Using dictionaries, there is no "history" or session + state management features in use, reducing latency when inserting + large numbers of simple rows. + + The values within the dictionaries as given are typically passed + without modification into Core :meth:`.Insert` constructs, after + organizing the values within them across the tables to which + the given mapper is mapped. + + .. versionadded:: 1.0.0 + + .. warning:: + + The bulk insert feature allows for a lower-latency INSERT + of rows at the expense of most other unit-of-work features. + Features such as object management, relationship handling, + and SQL clause support are **silently omitted** in favor of raw + INSERT of records. + + **Please read the list of caveats at** :ref:`bulk_operations` + **before using this method, and fully test and confirm the + functionality of all code developed using these systems.** + + :param mapper: a mapped class, or the actual :class:`.Mapper` object, + representing the single kind of object represented within the mapping + list. + + :param mappings: a list of dictionaries, each one containing the state + of the mapped row to be inserted, in terms of the attribute names + on the mapped class. If the mapping refers to multiple tables, + such as a joined-inheritance mapping, each dictionary must contain + all keys to be populated into all tables. + + :param return_defaults: when True, rows that are missing values which + generate defaults, namely integer primary key defaults and sequences, + will be inserted **one at a time**, so that the primary key value + is available. In particular this will allow joined-inheritance + and other multi-table mappings to insert correctly without the need + to provide primary + key values ahead of time; however, + :paramref:`.Session.bulk_insert_mappings.return_defaults` + **greatly reduces the performance gains** of the method overall. + If the rows + to be inserted only refer to a single table, then there is no + reason this flag should be set as the returned default information + is not used. + + :param render_nulls: When True, a value of ``None`` will result + in a NULL value being included in the INSERT statement, rather + than the column being omitted from the INSERT. This allows all + the rows being INSERTed to have the identical set of columns which + allows the full set of rows to be batched to the DBAPI. Normally, + each column-set that contains a different combination of NULL values + than the previous row must omit a different series of columns from + the rendered INSERT statement, which means it must be emitted as a + separate statement. By passing this flag, the full set of rows + are guaranteed to be batchable into one batch; the cost however is + that server-side defaults which are invoked by an omitted column will + be skipped, so care must be taken to ensure that these are not + necessary. + + .. warning:: + + When this flag is set, **server side default SQL values will + not be invoked** for those columns that are inserted as NULL; + the NULL value will be sent explicitly. Care must be taken + to ensure that no server-side default functions need to be + invoked for the operation as a whole. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`bulk_operations` + + :meth:`.Session.bulk_save_objects` + + :meth:`.Session.bulk_update_mappings` + + """ + self._bulk_save_mappings( + mapper, mappings, False, False, + return_defaults, False, render_nulls) + + def bulk_update_mappings(self, mapper, mappings): + """Perform a bulk update of the given list of mapping dictionaries. + + The bulk update feature allows plain Python dictionaries to be used as + the source of simple UPDATE operations which can be more easily + grouped together into higher performing "executemany" + operations. Using dictionaries, there is no "history" or session + state management features in use, reducing latency when updating + large numbers of simple rows. + + .. versionadded:: 1.0.0 + + .. warning:: + + The bulk update feature allows for a lower-latency UPDATE + of rows at the expense of most other unit-of-work features. + Features such as object management, relationship handling, + and SQL clause support are **silently omitted** in favor of raw + UPDATES of records. + + **Please read the list of caveats at** :ref:`bulk_operations` + **before using this method, and fully test and confirm the + functionality of all code developed using these systems.** + + :param mapper: a mapped class, or the actual :class:`.Mapper` object, + representing the single kind of object represented within the mapping + list. + + :param mappings: a list of dictionaries, each one containing the state + of the mapped row to be updated, in terms of the attribute names + on the mapped class. If the mapping refers to multiple tables, + such as a joined-inheritance mapping, each dictionary may contain + keys corresponding to all tables. All those keys which are present + and are not part of the primary key are applied to the SET clause + of the UPDATE statement; the primary key values, which are required, + are applied to the WHERE clause. + + + .. seealso:: + + :ref:`bulk_operations` + + :meth:`.Session.bulk_insert_mappings` + + :meth:`.Session.bulk_save_objects` + + """ + self._bulk_save_mappings( + mapper, mappings, True, False, False, False, False) + + def _bulk_save_mappings( + self, mapper, mappings, isupdate, isstates, + return_defaults, update_changed_only, render_nulls): + mapper = _class_to_mapper(mapper) + self._flushing = True + + transaction = self.begin( + subtransactions=True) + try: + if isupdate: + persistence._bulk_update( + mapper, mappings, transaction, + isstates, update_changed_only) + else: + persistence._bulk_insert( + mapper, mappings, transaction, + isstates, return_defaults, render_nulls) + transaction.commit() + + except: + with util.safe_reraise(): + transaction.rollback(_capture_exception=True) + finally: + self._flushing = False + + def is_modified(self, instance, include_collections=True, + passive=True): + r"""Return ``True`` if the given instance has locally + modified attributes. + + This method retrieves the history for each instrumented + attribute on the instance and performs a comparison of the current + value to its previously committed value, if any. + + It is in effect a more expensive and accurate + version of checking for the given instance in the + :attr:`.Session.dirty` collection; a full test for + each attribute's net "dirty" status is performed. + + E.g.:: + + return session.is_modified(someobject) + + .. versionchanged:: 0.8 + When using SQLAlchemy 0.7 and earlier, the ``passive`` + flag should **always** be explicitly set to ``True``, + else SQL loads/autoflushes may proceed which can affect + the modified state itself: + ``session.is_modified(someobject, passive=True)``\ . + In 0.8 and above, the behavior is corrected and + this flag is ignored. + + A few caveats to this method apply: + + * Instances present in the :attr:`.Session.dirty` collection may + report ``False`` when tested with this method. This is because + the object may have received change events via attribute mutation, + thus placing it in :attr:`.Session.dirty`, but ultimately the state + is the same as that loaded from the database, resulting in no net + change here. + * Scalar attributes may not have recorded the previously set + value when a new value was applied, if the attribute was not loaded, + or was expired, at the time the new value was received - in these + cases, the attribute is assumed to have a change, even if there is + ultimately no net change against its database value. SQLAlchemy in + most cases does not need the "old" value when a set event occurs, so + it skips the expense of a SQL call if the old value isn't present, + based on the assumption that an UPDATE of the scalar value is + usually needed, and in those few cases where it isn't, is less + expensive on average than issuing a defensive SELECT. + + The "old" value is fetched unconditionally upon set only if the + attribute container has the ``active_history`` flag set to ``True``. + This flag is set typically for primary key attributes and scalar + object references that are not a simple many-to-one. To set this + flag for any arbitrary mapped column, use the ``active_history`` + argument with :func:`.column_property`. + + :param instance: mapped instance to be tested for pending changes. + :param include_collections: Indicates if multivalued collections + should be included in the operation. Setting this to ``False`` is a + way to detect only local-column based properties (i.e. scalar columns + or many-to-one foreign keys) that would result in an UPDATE for this + instance upon flush. + :param passive: + + .. versionchanged:: 0.8 + Ignored for backwards compatibility. + When using SQLAlchemy 0.7 and earlier, this flag should always + be set to ``True``. + + """ + state = object_state(instance) + + if not state.modified: + return False + + dict_ = state.dict + + for attr in state.manager.attributes: + if \ + ( + not include_collections and + hasattr(attr.impl, 'get_collection') + ) or not hasattr(attr.impl, 'get_history'): + continue + + (added, unchanged, deleted) = \ + attr.impl.get_history(state, dict_, + passive=attributes.NO_CHANGE) + + if added or deleted: + return True + else: + return False + + @property + def is_active(self): + """True if this :class:`.Session` is in "transaction mode" and + is not in "partial rollback" state. + + The :class:`.Session` in its default mode of ``autocommit=False`` + is essentially always in "transaction mode", in that a + :class:`.SessionTransaction` is associated with it as soon as + it is instantiated. This :class:`.SessionTransaction` is immediately + replaced with a new one as soon as it is ended, due to a rollback, + commit, or close operation. + + "Transaction mode" does *not* indicate whether + or not actual database connection resources are in use; the + :class:`.SessionTransaction` object coordinates among zero or more + actual database transactions, and starts out with none, accumulating + individual DBAPI connections as different data sources are used + within its scope. The best way to track when a particular + :class:`.Session` has actually begun to use DBAPI resources is to + implement a listener using the :meth:`.SessionEvents.after_begin` + method, which will deliver both the :class:`.Session` as well as the + target :class:`.Connection` to a user-defined event listener. + + The "partial rollback" state refers to when an "inner" transaction, + typically used during a flush, encounters an error and emits a + rollback of the DBAPI connection. At this point, the + :class:`.Session` is in "partial rollback" and awaits for the user to + call :meth:`.Session.rollback`, in order to close out the + transaction stack. It is in this "partial rollback" period that the + :attr:`.is_active` flag returns False. After the call to + :meth:`.Session.rollback`, the :class:`.SessionTransaction` is + replaced with a new one and :attr:`.is_active` returns ``True`` again. + + When a :class:`.Session` is used in ``autocommit=True`` mode, the + :class:`.SessionTransaction` is only instantiated within the scope + of a flush call, or when :meth:`.Session.begin` is called. So + :attr:`.is_active` will always be ``False`` outside of a flush or + :meth:`.Session.begin` block in this mode, and will be ``True`` + within the :meth:`.Session.begin` block as long as it doesn't enter + "partial rollback" state. + + From all the above, it follows that the only purpose to this flag is + for application frameworks that wish to detect is a "rollback" is + necessary within a generic error handling routine, for + :class:`.Session` objects that would otherwise be in + "partial rollback" mode. In a typical integration case, this is also + not necessary as it is standard practice to emit + :meth:`.Session.rollback` unconditionally within the outermost + exception catch. + + To track the transactional state of a :class:`.Session` fully, + use event listeners, primarily the :meth:`.SessionEvents.after_begin`, + :meth:`.SessionEvents.after_commit`, + :meth:`.SessionEvents.after_rollback` and related events. + + """ + return self.transaction and self.transaction.is_active + + identity_map = None + """A mapping of object identities to objects themselves. + + Iterating through ``Session.identity_map.values()`` provides + access to the full set of persistent objects (i.e., those + that have row identity) currently in the session. + + .. seealso:: + + :func:`.identity_key` - helper function to produce the keys used + in this dictionary. + + """ + + @property + def _dirty_states(self): + """The set of all persistent states considered dirty. + + This method returns all states that were modified including + those that were possibly deleted. + + """ + return self.identity_map._dirty_states() + + @property + def dirty(self): + """The set of all persistent instances considered dirty. + + E.g.:: + + some_mapped_object in session.dirty + + Instances are considered dirty when they were modified but not + deleted. + + Note that this 'dirty' calculation is 'optimistic'; most + attribute-setting or collection modification operations will + mark an instance as 'dirty' and place it in this set, even if + there is no net change to the attribute's value. At flush + time, the value of each attribute is compared to its + previously saved value, and if there's no net change, no SQL + operation will occur (this is a more expensive operation so + it's only done at flush time). + + To check if an instance has actionable net changes to its + attributes, use the :meth:`.Session.is_modified` method. + + """ + return util.IdentitySet( + [state.obj() + for state in self._dirty_states + if state not in self._deleted]) + + @property + def deleted(self): + "The set of all instances marked as 'deleted' within this ``Session``" + + return util.IdentitySet(list(self._deleted.values())) + + @property + def new(self): + "The set of all instances marked as 'new' within this ``Session``." + + return util.IdentitySet(list(self._new.values())) + + +class sessionmaker(_SessionClassMethods): + """A configurable :class:`.Session` factory. + + The :class:`.sessionmaker` factory generates new + :class:`.Session` objects when called, creating them given + the configurational arguments established here. + + e.g.:: + + # global scope + Session = sessionmaker(autoflush=False) + + # later, in a local scope, create and use a session: + sess = Session() + + Any keyword arguments sent to the constructor itself will override the + "configured" keywords:: + + Session = sessionmaker() + + # bind an individual session to a connection + sess = Session(bind=connection) + + The class also includes a method :meth:`.configure`, which can + be used to specify additional keyword arguments to the factory, which + will take effect for subsequent :class:`.Session` objects generated. + This is usually used to associate one or more :class:`.Engine` objects + with an existing :class:`.sessionmaker` factory before it is first + used:: + + # application starts + Session = sessionmaker() + + # ... later + engine = create_engine('sqlite:///foo.db') + Session.configure(bind=engine) + + sess = Session() + + .. seealso: + + :ref:`session_getting` - introductory text on creating + sessions using :class:`.sessionmaker`. + + """ + + def __init__(self, bind=None, class_=Session, autoflush=True, + autocommit=False, + expire_on_commit=True, + info=None, **kw): + r"""Construct a new :class:`.sessionmaker`. + + All arguments here except for ``class_`` correspond to arguments + accepted by :class:`.Session` directly. See the + :meth:`.Session.__init__` docstring for more details on parameters. + + :param bind: a :class:`.Engine` or other :class:`.Connectable` with + which newly created :class:`.Session` objects will be associated. + :param class_: class to use in order to create new :class:`.Session` + objects. Defaults to :class:`.Session`. + :param autoflush: The autoflush setting to use with newly created + :class:`.Session` objects. + :param autocommit: The autocommit setting to use with newly created + :class:`.Session` objects. + :param expire_on_commit=True: the expire_on_commit setting to use + with newly created :class:`.Session` objects. + :param info: optional dictionary of information that will be available + via :attr:`.Session.info`. Note this dictionary is *updated*, not + replaced, when the ``info`` parameter is specified to the specific + :class:`.Session` construction operation. + + .. versionadded:: 0.9.0 + + :param \**kw: all other keyword arguments are passed to the + constructor of newly created :class:`.Session` objects. + + """ + kw['bind'] = bind + kw['autoflush'] = autoflush + kw['autocommit'] = autocommit + kw['expire_on_commit'] = expire_on_commit + if info is not None: + kw['info'] = info + self.kw = kw + # make our own subclass of the given class, so that + # events can be associated with it specifically. + self.class_ = type(class_.__name__, (class_,), {}) + + def __call__(self, **local_kw): + """Produce a new :class:`.Session` object using the configuration + established in this :class:`.sessionmaker`. + + In Python, the ``__call__`` method is invoked on an object when + it is "called" in the same way as a function:: + + Session = sessionmaker() + session = Session() # invokes sessionmaker.__call__() + + """ + for k, v in self.kw.items(): + if k == 'info' and 'info' in local_kw: + d = v.copy() + d.update(local_kw['info']) + local_kw['info'] = d + else: + local_kw.setdefault(k, v) + return self.class_(**local_kw) + + def configure(self, **new_kw): + """(Re)configure the arguments for this sessionmaker. + + e.g.:: + + Session = sessionmaker() + + Session.configure(bind=create_engine('sqlite://')) + """ + self.kw.update(new_kw) + + def __repr__(self): + return "%s(class_=%r, %s)" % ( + self.__class__.__name__, + self.class_.__name__, + ", ".join("%s=%r" % (k, v) for k, v in self.kw.items()) + ) + + +def make_transient(instance): + """Alter the state of the given instance so that it is :term:`transient`. + + .. note:: + + :func:`.make_transient` is a special-case function for + advanced use cases only. + + The given mapped instance is assumed to be in the :term:`persistent` or + :term:`detached` state. The function will remove its association with any + :class:`.Session` as well as its :attr:`.InstanceState.identity`. The + effect is that the object will behave as though it were newly constructed, + except retaining any attribute / collection values that were loaded at the + time of the call. The :attr:`.InstanceState.deleted` flag is also reset + if this object had been deleted as a result of using + :meth:`.Session.delete`. + + .. warning:: + + :func:`.make_transient` does **not** "unexpire" or otherwise eagerly + load ORM-mapped attributes that are not currently loaded at the time + the function is called. This includes attributes which: + + * were expired via :meth:`.Session.expire` + + * were expired as the natural effect of committing a session + transaction, e.g. :meth:`.Session.commit` + + * are normally :term:`lazy loaded` but are not currently loaded + + * are "deferred" via :ref:`deferred` and are not yet loaded + + * were not present in the query which loaded this object, such as that + which is common in joined table inheritance and other scenarios. + + After :func:`.make_transient` is called, unloaded attributes such + as those above will normally resolve to the value ``None`` when + accessed, or an empty collection for a collection-oriented attribute. + As the object is transient and un-associated with any database + identity, it will no longer retrieve these values. + + .. seealso:: + + :func:`.make_transient_to_detached` + + """ + state = attributes.instance_state(instance) + s = _state_session(state) + if s: + s._expunge_states([state]) + + # remove expired state + state.expired_attributes.clear() + + # remove deferred callables + if state.callables: + del state.callables + + if state.key: + del state.key + if state._deleted: + del state._deleted + + +def make_transient_to_detached(instance): + """Make the given transient instance :term:`detached`. + + .. note:: + + :func:`.make_transient_to_detached` is a special-case function for + advanced use cases only. + + All attribute history on the given instance + will be reset as though the instance were freshly loaded + from a query. Missing attributes will be marked as expired. + The primary key attributes of the object, which are required, will be made + into the "key" of the instance. + + The object can then be added to a session, or merged + possibly with the load=False flag, at which point it will look + as if it were loaded that way, without emitting SQL. + + This is a special use case function that differs from a normal + call to :meth:`.Session.merge` in that a given persistent state + can be manufactured without any SQL calls. + + .. versionadded:: 0.9.5 + + .. seealso:: + + :func:`.make_transient` + + :meth:`.Session.enable_relationship_loading` + + """ + state = attributes.instance_state(instance) + if state.session_id or state.key: + raise sa_exc.InvalidRequestError( + "Given object must be transient") + state.key = state.mapper._identity_key_from_state(state) + if state._deleted: + del state._deleted + state._commit_all(state.dict) + state._expire_attributes(state.dict, state.unloaded_expirable) + + +def object_session(instance): + """Return the :class:`.Session` to which the given instance belongs. + + This is essentially the same as the :attr:`.InstanceState.session` + accessor. See that attribute for details. + + """ + + try: + state = attributes.instance_state(instance) + except exc.NO_STATE: + raise exc.UnmappedInstanceError(instance) + else: + return _state_session(state) + + +_new_sessionid = util.counter() diff --git a/venv/Lib/site-packages/sqlalchemy/orm/state.py b/venv/Lib/site-packages/sqlalchemy/orm/state.py new file mode 100644 index 0000000..03d68ab --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/state.py @@ -0,0 +1,887 @@ +# orm/state.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Defines instrumentation of instances. + +This module is usually not directly visible to user applications, but +defines a large part of the ORM's interactivity. + +""" + +import weakref +from .. import util +from .. import inspection +from .. import exc as sa_exc +from . import exc as orm_exc, interfaces +from .path_registry import PathRegistry +from .base import PASSIVE_NO_RESULT, SQL_OK, NEVER_SET, ATTR_WAS_SET, \ + NO_VALUE, PASSIVE_NO_INITIALIZE, INIT_OK, PASSIVE_OFF +from . import base + + +@inspection._self_inspects +class InstanceState(interfaces.InspectionAttr): + """tracks state information at the instance level. + + The :class:`.InstanceState` is a key object used by the + SQLAlchemy ORM in order to track the state of an object; + it is created the moment an object is instantiated, typically + as a result of :term:`instrumentation` which SQLAlchemy applies + to the ``__init__()`` method of the class. + + :class:`.InstanceState` is also a semi-public object, + available for runtime inspection as to the state of a + mapped instance, including information such as its current + status within a particular :class:`.Session` and details + about data on individual attributes. The public API + in order to acquire a :class:`.InstanceState` object + is to use the :func:`.inspect` system:: + + >>> from sqlalchemy import inspect + >>> insp = inspect(some_mapped_object) + + .. seealso:: + + :ref:`core_inspection_toplevel` + + """ + + session_id = None + key = None + runid = None + load_options = util.EMPTY_SET + load_path = () + insert_order = None + _strong_obj = None + modified = False + expired = False + _deleted = False + _load_pending = False + _orphaned_outside_of_session = False + is_instance = True + identity_token = None + + callables = () + """A namespace where a per-state loader callable can be associated. + + In SQLAlchemy 1.0, this is only used for lazy loaders / deferred + loaders that were set up via query option. + + Previously, callables was used also to indicate expired attributes + by storing a link to the InstanceState itself in this dictionary. + This role is now handled by the expired_attributes set. + + """ + + def __init__(self, obj, manager): + self.class_ = obj.__class__ + self.manager = manager + self.obj = weakref.ref(obj, self._cleanup) + self.committed_state = {} + self.expired_attributes = set() + + expired_attributes = None + """The set of keys which are 'expired' to be loaded by + the manager's deferred scalar loader, assuming no pending + changes. + + see also the ``unmodified`` collection which is intersected + against this set when a refresh operation occurs.""" + + @util.memoized_property + def attrs(self): + """Return a namespace representing each attribute on + the mapped object, including its current value + and history. + + The returned object is an instance of :class:`.AttributeState`. + This object allows inspection of the current data + within an attribute as well as attribute history + since the last flush. + + """ + return util.ImmutableProperties( + dict( + (key, AttributeState(self, key)) + for key in self.manager + ) + ) + + @property + def transient(self): + """Return true if the object is :term:`transient`. + + .. seealso:: + + :ref:`session_object_states` + + """ + return self.key is None and \ + not self._attached + + @property + def pending(self): + """Return true if the object is :term:`pending`. + + + .. seealso:: + + :ref:`session_object_states` + + """ + return self.key is None and \ + self._attached + + @property + def deleted(self): + """Return true if the object is :term:`deleted`. + + An object that is in the deleted state is guaranteed to + not be within the :attr:`.Session.identity_map` of its parent + :class:`.Session`; however if the session's transaction is rolled + back, the object will be restored to the persistent state and + the identity map. + + .. note:: + + The :attr:`.InstanceState.deleted` attribute refers to a specific + state of the object that occurs between the "persistent" and + "detached" states; once the object is :term:`detached`, the + :attr:`.InstanceState.deleted` attribute **no longer returns + True**; in order to detect that a state was deleted, regardless + of whether or not the object is associated with a :class:`.Session`, + use the :attr:`.InstanceState.was_deleted` accessor. + + .. versionadded: 1.1 + + .. seealso:: + + :ref:`session_object_states` + + """ + return self.key is not None and \ + self._attached and self._deleted + + @property + def was_deleted(self): + """Return True if this object is or was previously in the + "deleted" state and has not been reverted to persistent. + + This flag returns True once the object was deleted in flush. + When the object is expunged from the session either explicitly + or via transaction commit and enters the "detached" state, + this flag will continue to report True. + + .. versionadded:: 1.1 - added a local method form of + :func:`.orm.util.was_deleted`. + + .. seealso:: + + :attr:`.InstanceState.deleted` - refers to the "deleted" state + + :func:`.orm.util.was_deleted` - standalone function + + :ref:`session_object_states` + + """ + return self._deleted + + @property + def persistent(self): + """Return true if the object is :term:`persistent`. + + An object that is in the persistent state is guaranteed to + be within the :attr:`.Session.identity_map` of its parent + :class:`.Session`. + + .. versionchanged:: 1.1 The :attr:`.InstanceState.persistent` + accessor no longer returns True for an object that was + "deleted" within a flush; use the :attr:`.InstanceState.deleted` + accessor to detect this state. This allows the "persistent" + state to guarantee membership in the identity map. + + .. seealso:: + + :ref:`session_object_states` + + """ + return self.key is not None and \ + self._attached and not self._deleted + + @property + def detached(self): + """Return true if the object is :term:`detached`. + + .. seealso:: + + :ref:`session_object_states` + + """ + return self.key is not None and not self._attached + + @property + @util.dependencies("sqlalchemy.orm.session") + def _attached(self, sessionlib): + return self.session_id is not None and \ + self.session_id in sessionlib._sessions + + @property + @util.dependencies("sqlalchemy.orm.session") + def session(self, sessionlib): + """Return the owning :class:`.Session` for this instance, + or ``None`` if none available. + + Note that the result here can in some cases be *different* + from that of ``obj in session``; an object that's been deleted + will report as not ``in session``, however if the transaction is + still in progress, this attribute will still refer to that session. + Only when the transaction is completed does the object become + fully detached under normal circumstances. + + """ + return sessionlib._state_session(self) + + @property + def object(self): + """Return the mapped object represented by this + :class:`.InstanceState`.""" + return self.obj() + + @property + def identity(self): + """Return the mapped identity of the mapped object. + This is the primary key identity as persisted by the ORM + which can always be passed directly to + :meth:`.Query.get`. + + Returns ``None`` if the object has no primary key identity. + + .. note:: + An object which is :term:`transient` or :term:`pending` + does **not** have a mapped identity until it is flushed, + even if its attributes include primary key values. + + """ + if self.key is None: + return None + else: + return self.key[1] + + @property + def identity_key(self): + """Return the identity key for the mapped object. + + This is the key used to locate the object within + the :attr:`.Session.identity_map` mapping. It contains + the identity as returned by :attr:`.identity` within it. + + + """ + # TODO: just change .key to .identity_key across + # the board ? probably + return self.key + + @util.memoized_property + def parents(self): + return {} + + @util.memoized_property + def _pending_mutations(self): + return {} + + @util.memoized_property + def mapper(self): + """Return the :class:`.Mapper` used for this mapepd object.""" + return self.manager.mapper + + @property + def has_identity(self): + """Return ``True`` if this object has an identity key. + + This should always have the same value as the + expression ``state.persistent or state.detached``. + + """ + return bool(self.key) + + @classmethod + def _detach_states(self, states, session, to_transient=False): + persistent_to_detached = \ + session.dispatch.persistent_to_detached or None + deleted_to_detached = \ + session.dispatch.deleted_to_detached or None + pending_to_transient = \ + session.dispatch.pending_to_transient or None + persistent_to_transient = \ + session.dispatch.persistent_to_transient or None + + for state in states: + deleted = state._deleted + pending = state.key is None + persistent = not pending and not deleted + + state.session_id = None + + if to_transient and state.key: + del state.key + if persistent: + if to_transient: + if persistent_to_transient is not None: + obj = state.obj() + if obj is not None: + persistent_to_transient(session, obj) + elif persistent_to_detached is not None: + obj = state.obj() + if obj is not None: + persistent_to_detached(session, obj) + elif deleted and deleted_to_detached is not None: + obj = state.obj() + if obj is not None: + deleted_to_detached(session, obj) + elif pending and pending_to_transient is not None: + obj = state.obj() + if obj is not None: + pending_to_transient(session, obj) + + state._strong_obj = None + + def _detach(self, session=None): + if session: + InstanceState._detach_states([self], session) + else: + self.session_id = self._strong_obj = None + + def _dispose(self): + self._detach() + del self.obj + + def _cleanup(self, ref): + """Weakref callback cleanup. + + This callable cleans out the state when it is being garbage + collected. + + this _cleanup **assumes** that there are no strong refs to us! + Will not work otherwise! + + """ + + # Python builtins become undefined during interpreter shutdown. + # Guard against exceptions during this phase, as the method cannot + # proceed in any case if builtins have been undefined. + if dict is None: + return + + instance_dict = self._instance_dict() + if instance_dict is not None: + instance_dict._fast_discard(self) + del self._instance_dict + + # we can't possibly be in instance_dict._modified + # b.c. this is weakref cleanup only, that set + # is strong referencing! + # assert self not in instance_dict._modified + + self.session_id = self._strong_obj = None + del self.obj + + def obj(self): + return None + + @property + def dict(self): + """Return the instance dict used by the object. + + Under normal circumstances, this is always synonymous + with the ``__dict__`` attribute of the mapped object, + unless an alternative instrumentation system has been + configured. + + In the case that the actual object has been garbage + collected, this accessor returns a blank dictionary. + + """ + o = self.obj() + if o is not None: + return base.instance_dict(o) + else: + return {} + + def _initialize_instance(*mixed, **kwargs): + self, instance, args = mixed[0], mixed[1], mixed[2:] # noqa + manager = self.manager + + manager.dispatch.init(self, args, kwargs) + + try: + return manager.original_init(*mixed[1:], **kwargs) + except: + with util.safe_reraise(): + manager.dispatch.init_failure(self, args, kwargs) + + def get_history(self, key, passive): + return self.manager[key].impl.get_history(self, self.dict, passive) + + def get_impl(self, key): + return self.manager[key].impl + + def _get_pending_mutation(self, key): + if key not in self._pending_mutations: + self._pending_mutations[key] = PendingCollection() + return self._pending_mutations[key] + + def __getstate__(self): + state_dict = {'instance': self.obj()} + state_dict.update( + (k, self.__dict__[k]) for k in ( + 'committed_state', '_pending_mutations', 'modified', + 'expired', 'callables', 'key', 'parents', 'load_options', + 'class_', 'expired_attributes' + ) if k in self.__dict__ + ) + if self.load_path: + state_dict['load_path'] = self.load_path.serialize() + + state_dict['manager'] = self.manager._serialize(self, state_dict) + + return state_dict + + def __setstate__(self, state_dict): + inst = state_dict['instance'] + if inst is not None: + self.obj = weakref.ref(inst, self._cleanup) + self.class_ = inst.__class__ + else: + # None being possible here generally new as of 0.7.4 + # due to storage of state in "parents". "class_" + # also new. + self.obj = None + self.class_ = state_dict['class_'] + + self.committed_state = state_dict.get('committed_state', {}) + self._pending_mutations = state_dict.get('_pending_mutations', {}) + self.parents = state_dict.get('parents', {}) + self.modified = state_dict.get('modified', False) + self.expired = state_dict.get('expired', False) + if 'callables' in state_dict: + self.callables = state_dict['callables'] + + try: + self.expired_attributes = state_dict['expired_attributes'] + except KeyError: + self.expired_attributes = set() + # 0.9 and earlier compat + for k in list(self.callables): + if self.callables[k] is self: + self.expired_attributes.add(k) + del self.callables[k] + else: + if 'expired_attributes' in state_dict: + self.expired_attributes = state_dict['expired_attributes'] + else: + self.expired_attributes = set() + + self.__dict__.update([ + (k, state_dict[k]) for k in ( + 'key', 'load_options' + ) if k in state_dict + ]) + if self.key: + try: + self.identity_token = self.key[2] + except IndexError: + # 1.1 and earlier compat before identity_token + assert len(self.key) == 2 + self.key = self.key + (None, ) + self.identity_token = None + + if 'load_path' in state_dict: + self.load_path = PathRegistry.\ + deserialize(state_dict['load_path']) + + state_dict['manager'](self, inst, state_dict) + + def _reset(self, dict_, key): + """Remove the given attribute and any + callables associated with it.""" + + old = dict_.pop(key, None) + if old is not None and self.manager[key].impl.collection: + self.manager[key].impl._invalidate_collection(old) + self.expired_attributes.discard(key) + if self.callables: + self.callables.pop(key, None) + + def _copy_callables(self, from_): + if 'callables' in from_.__dict__: + self.callables = dict(from_.callables) + + @classmethod + def _instance_level_callable_processor(cls, manager, fn, key): + impl = manager[key].impl + if impl.collection: + def _set_callable(state, dict_, row): + if 'callables' not in state.__dict__: + state.callables = {} + old = dict_.pop(key, None) + if old is not None: + impl._invalidate_collection(old) + state.callables[key] = fn + else: + def _set_callable(state, dict_, row): + if 'callables' not in state.__dict__: + state.callables = {} + state.callables[key] = fn + return _set_callable + + def _expire(self, dict_, modified_set): + self.expired = True + + if self.modified: + modified_set.discard(self) + self.committed_state.clear() + self.modified = False + + self._strong_obj = None + + if '_pending_mutations' in self.__dict__: + del self.__dict__['_pending_mutations'] + + if 'parents' in self.__dict__: + del self.__dict__['parents'] + + self.expired_attributes.update( + [impl.key for impl in self.manager._scalar_loader_impls + if impl.expire_missing or impl.key in dict_] + ) + + if self.callables: + for k in self.expired_attributes.intersection(self.callables): + del self.callables[k] + + for k in self.manager._collection_impl_keys.intersection(dict_): + collection = dict_.pop(k) + collection._sa_adapter.invalidated = True + + for key in self.manager._all_key_set.intersection(dict_): + del dict_[key] + + self.manager.dispatch.expire(self, None) + + def _expire_attributes(self, dict_, attribute_names, no_loader=False): + pending = self.__dict__.get('_pending_mutations', None) + + callables = self.callables + + for key in attribute_names: + impl = self.manager[key].impl + if impl.accepts_scalar_loader: + if no_loader and ( + impl.callable_ or + key in callables + ): + continue + + self.expired_attributes.add(key) + if callables and key in callables: + del callables[key] + old = dict_.pop(key, None) + if impl.collection and old is not None: + impl._invalidate_collection(old) + + self.committed_state.pop(key, None) + if pending: + pending.pop(key, None) + + self.manager.dispatch.expire(self, attribute_names) + + def _load_expired(self, state, passive): + """__call__ allows the InstanceState to act as a deferred + callable for loading expired attributes, which is also + serializable (picklable). + + """ + + if not passive & SQL_OK: + return PASSIVE_NO_RESULT + + toload = self.expired_attributes.\ + intersection(self.unmodified) + + self.manager.deferred_scalar_loader(self, toload) + + # if the loader failed, or this + # instance state didn't have an identity, + # the attributes still might be in the callables + # dict. ensure they are removed. + self.expired_attributes.clear() + + return ATTR_WAS_SET + + @property + def unmodified(self): + """Return the set of keys which have no uncommitted changes""" + + return set(self.manager).difference(self.committed_state) + + def unmodified_intersection(self, keys): + """Return self.unmodified.intersection(keys).""" + + + return set(keys).intersection(self.manager).\ + difference(self.committed_state) + + @property + def unloaded(self): + """Return the set of keys which do not have a loaded value. + + This includes expired attributes and any other attribute that + was never populated or modified. + + """ + return set(self.manager).\ + difference(self.committed_state).\ + difference(self.dict) + + @property + def unloaded_expirable(self): + """Return the set of keys which do not have a loaded value. + + This includes expired attributes and any other attribute that + was never populated or modified. + + """ + return self.unloaded.intersection( + attr for attr in self.manager + if self.manager[attr].impl.expire_missing) + + @property + def _unloaded_non_object(self): + return self.unloaded.intersection( + attr for attr in self.manager + if self.manager[attr].impl.accepts_scalar_loader + ) + + def _instance_dict(self): + return None + + def _modified_event( + self, dict_, attr, previous, collection=False, is_userland=False): + if attr: + if not attr.send_modified_events: + return + if is_userland and attr.key not in dict_: + raise sa_exc.InvalidRequestError( + "Can't flag attribute '%s' modified; it's not present in " + "the object state" % attr.key) + if attr.key not in self.committed_state or is_userland: + if collection: + if previous is NEVER_SET: + if attr.key in dict_: + previous = dict_[attr.key] + + if previous not in (None, NO_VALUE, NEVER_SET): + previous = attr.copy(previous) + self.committed_state[attr.key] = previous + + # assert self._strong_obj is None or self.modified + + if (self.session_id and self._strong_obj is None) \ + or not self.modified: + self.modified = True + instance_dict = self._instance_dict() + if instance_dict: + instance_dict._modified.add(self) + + # only create _strong_obj link if attached + # to a session + + inst = self.obj() + if self.session_id: + self._strong_obj = inst + + if inst is None and attr: + raise orm_exc.ObjectDereferencedError( + "Can't emit change event for attribute '%s' - " + "parent object of type %s has been garbage " + "collected." + % ( + self.manager[attr.key], + base.state_class_str(self) + )) + + def _commit(self, dict_, keys): + """Commit attributes. + + This is used by a partial-attribute load operation to mark committed + those attributes which were refreshed from the database. + + Attributes marked as "expired" can potentially remain "expired" after + this step if a value was not populated in state.dict. + + """ + for key in keys: + self.committed_state.pop(key, None) + + self.expired = False + + self.expired_attributes.difference_update( + set(keys).intersection(dict_)) + + # the per-keys commit removes object-level callables, + # while that of commit_all does not. it's not clear + # if this behavior has a clear rationale, however tests do + # ensure this is what it does. + if self.callables: + for key in set(self.callables).\ + intersection(keys).\ + intersection(dict_): + del self.callables[key] + + def _commit_all(self, dict_, instance_dict=None): + """commit all attributes unconditionally. + + This is used after a flush() or a full load/refresh + to remove all pending state from the instance. + + - all attributes are marked as "committed" + - the "strong dirty reference" is removed + - the "modified" flag is set to False + - any "expired" markers for scalar attributes loaded are removed. + - lazy load callables for objects / collections *stay* + + Attributes marked as "expired" can potentially remain + "expired" after this step if a value was not populated in state.dict. + + """ + self._commit_all_states([(self, dict_)], instance_dict) + + @classmethod + def _commit_all_states(self, iter, instance_dict=None): + """Mass / highly inlined version of commit_all().""" + + for state, dict_ in iter: + state_dict = state.__dict__ + + state.committed_state.clear() + + if '_pending_mutations' in state_dict: + del state_dict['_pending_mutations'] + + state.expired_attributes.difference_update(dict_) + + if instance_dict and state.modified: + instance_dict._modified.discard(state) + + state.modified = state.expired = False + state._strong_obj = None + + +class AttributeState(object): + """Provide an inspection interface corresponding + to a particular attribute on a particular mapped object. + + The :class:`.AttributeState` object is accessed + via the :attr:`.InstanceState.attrs` collection + of a particular :class:`.InstanceState`:: + + from sqlalchemy import inspect + + insp = inspect(some_mapped_object) + attr_state = insp.attrs.some_attribute + + """ + + def __init__(self, state, key): + self.state = state + self.key = key + + @property + def loaded_value(self): + """The current value of this attribute as loaded from the database. + + If the value has not been loaded, or is otherwise not present + in the object's dictionary, returns NO_VALUE. + + """ + return self.state.dict.get(self.key, NO_VALUE) + + @property + def value(self): + """Return the value of this attribute. + + This operation is equivalent to accessing the object's + attribute directly or via ``getattr()``, and will fire + off any pending loader callables if needed. + + """ + return self.state.manager[self.key].__get__( + self.state.obj(), self.state.class_) + + @property + def history(self): + """Return the current pre-flush change history for + this attribute, via the :class:`.History` interface. + + This method will **not** emit loader callables if the value of the + attribute is unloaded. + + .. seealso:: + + :meth:`.AttributeState.load_history` - retrieve history + using loader callables if the value is not locally present. + + :func:`.attributes.get_history` - underlying function + + """ + return self.state.get_history(self.key, + PASSIVE_NO_INITIALIZE) + + def load_history(self): + """Return the current pre-flush change history for + this attribute, via the :class:`.History` interface. + + This method **will** emit loader callables if the value of the + attribute is unloaded. + + .. seealso:: + + :attr:`.AttributeState.history` + + :func:`.attributes.get_history` - underlying function + + .. versionadded:: 0.9.0 + + """ + return self.state.get_history(self.key, + PASSIVE_OFF ^ INIT_OK) + + +class PendingCollection(object): + """A writable placeholder for an unloaded collection. + + Stores items appended to and removed from a collection that has not yet + been loaded. When the collection is loaded, the changes stored in + PendingCollection are applied to it to produce the final result. + + """ + + def __init__(self): + self.deleted_items = util.IdentitySet() + self.added_items = util.OrderedIdentitySet() + + def append(self, value): + if value in self.deleted_items: + self.deleted_items.remove(value) + else: + self.added_items.add(value) + + def remove(self, value): + if value in self.added_items: + self.added_items.remove(value) + else: + self.deleted_items.add(value) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/strategies.py b/venv/Lib/site-packages/sqlalchemy/orm/strategies.py new file mode 100644 index 0000000..d7597d3 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/strategies.py @@ -0,0 +1,2039 @@ +# orm/strategies.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""sqlalchemy.orm.interfaces.LoaderStrategy + implementations, and related MapperOptions.""" + +from .. import exc as sa_exc, inspect +from .. import util, log, event +from ..sql import util as sql_util, visitors +from .. import sql +from . import ( + attributes, interfaces, exc as orm_exc, loading, + unitofwork, util as orm_util, query +) +from .state import InstanceState +from .util import _none_set, aliased +from . import properties +from .interfaces import ( + LoaderStrategy, StrategizedProperty +) +from .base import _SET_DEFERRED_EXPIRED, _DEFER_FOR_STATE +from .session import _state_session +import itertools + + +def _register_attribute( + prop, mapper, useobject, + compare_function=None, + typecallable=None, + callable_=None, + proxy_property=None, + active_history=False, + impl_class=None, + **kw +): + + attribute_ext = list(util.to_list(prop.extension, default=[])) + + listen_hooks = [] + + uselist = useobject and prop.uselist + + if useobject and prop.single_parent: + listen_hooks.append(single_parent_validator) + + if prop.key in prop.parent.validators: + fn, opts = prop.parent.validators[prop.key] + listen_hooks.append( + lambda desc, prop: orm_util._validator_events( + desc, + prop.key, fn, **opts) + ) + + if useobject: + listen_hooks.append(unitofwork.track_cascade_events) + + # need to assemble backref listeners + # after the singleparentvalidator, mapper validator + if useobject: + backref = prop.back_populates + if backref: + listen_hooks.append( + lambda desc, prop: attributes.backref_listeners( + desc, + backref, + uselist + ) + ) + + # a single MapperProperty is shared down a class inheritance + # hierarchy, so we set up attribute instrumentation and backref event + # for each mapper down the hierarchy. + + # typically, "mapper" is the same as prop.parent, due to the way + # the configure_mappers() process runs, however this is not strongly + # enforced, and in the case of a second configure_mappers() run the + # mapper here might not be prop.parent; also, a subclass mapper may + # be called here before a superclass mapper. That is, can't depend + # on mappers not already being set up so we have to check each one. + + for m in mapper.self_and_descendants: + if prop is m._props.get(prop.key) and \ + not m.class_manager._attr_has_impl(prop.key): + + desc = attributes.register_attribute_impl( + m.class_, + prop.key, + parent_token=prop, + uselist=uselist, + compare_function=compare_function, + useobject=useobject, + extension=attribute_ext, + trackparent=useobject and ( + prop.single_parent or + prop.direction is interfaces.ONETOMANY), + typecallable=typecallable, + callable_=callable_, + active_history=active_history, + impl_class=impl_class, + send_modified_events=not useobject or not prop.viewonly, + doc=prop.doc, + **kw + ) + + for hook in listen_hooks: + hook(desc, prop) + + +@properties.ColumnProperty.strategy_for(instrument=False, deferred=False) +class UninstrumentedColumnLoader(LoaderStrategy): + """Represent a non-instrumented MapperProperty. + + The polymorphic_on argument of mapper() often results in this, + if the argument is against the with_polymorphic selectable. + + """ + __slots__ = 'columns', + + def __init__(self, parent, strategy_key): + super(UninstrumentedColumnLoader, self).__init__(parent, strategy_key) + self.columns = self.parent_property.columns + + def setup_query( + self, context, entity, path, loadopt, adapter, + column_collection=None, **kwargs): + for c in self.columns: + if adapter: + c = adapter.columns[c] + column_collection.append(c) + + def create_row_processor( + self, context, path, loadopt, + mapper, result, adapter, populators): + pass + + +@log.class_logger +@properties.ColumnProperty.strategy_for(instrument=True, deferred=False) +class ColumnLoader(LoaderStrategy): + """Provide loading behavior for a :class:`.ColumnProperty`.""" + + __slots__ = 'columns', 'is_composite' + + def __init__(self, parent, strategy_key): + super(ColumnLoader, self).__init__(parent, strategy_key) + self.columns = self.parent_property.columns + self.is_composite = hasattr(self.parent_property, 'composite_class') + + def setup_query( + self, context, entity, path, loadopt, + adapter, column_collection, memoized_populators, **kwargs): + + for c in self.columns: + if adapter: + c = adapter.columns[c] + column_collection.append(c) + + fetch = self.columns[0] + if adapter: + fetch = adapter.columns[fetch] + memoized_populators[self.parent_property] = fetch + + def init_class_attribute(self, mapper): + self.is_class_level = True + coltype = self.columns[0].type + # TODO: check all columns ? check for foreign key as well? + active_history = self.parent_property.active_history or \ + self.columns[0].primary_key or \ + mapper.version_id_col in set(self.columns) + + _register_attribute( + self.parent_property, mapper, useobject=False, + compare_function=coltype.compare_values, + active_history=active_history + ) + + def create_row_processor( + self, context, path, + loadopt, mapper, result, adapter, populators): + # look through list of columns represented here + # to see which, if any, is present in the row. + for col in self.columns: + if adapter: + col = adapter.columns[col] + getter = result._getter(col, False) + if getter: + populators["quick"].append((self.key, getter)) + break + else: + populators["expire"].append((self.key, True)) + + +@log.class_logger +@properties.ColumnProperty.strategy_for(query_expression=True) +class ExpressionColumnLoader(ColumnLoader): + def __init__(self, parent, strategy_key): + super(ExpressionColumnLoader, self).__init__(parent, strategy_key) + + def setup_query( + self, context, entity, path, loadopt, + adapter, column_collection, memoized_populators, **kwargs): + + if loadopt and "expression" in loadopt.local_opts: + columns = [loadopt.local_opts["expression"]] + + for c in columns: + if adapter: + c = adapter.columns[c] + column_collection.append(c) + + fetch = columns[0] + if adapter: + fetch = adapter.columns[fetch] + memoized_populators[self.parent_property] = fetch + + def create_row_processor( + self, context, path, + loadopt, mapper, result, adapter, populators): + # look through list of columns represented here + # to see which, if any, is present in the row. + if loadopt and "expression" in loadopt.local_opts: + columns = [loadopt.local_opts["expression"]] + + for col in columns: + if adapter: + col = adapter.columns[col] + getter = result._getter(col, False) + if getter: + populators["quick"].append((self.key, getter)) + break + else: + populators["expire"].append((self.key, True)) + + def init_class_attribute(self, mapper): + self.is_class_level = True + + _register_attribute( + self.parent_property, mapper, useobject=False, + compare_function=self.columns[0].type.compare_values, + accepts_scalar_loader=False + ) + + +@log.class_logger +@properties.ColumnProperty.strategy_for(deferred=True, instrument=True) +@properties.ColumnProperty.strategy_for(do_nothing=True) +class DeferredColumnLoader(LoaderStrategy): + """Provide loading behavior for a deferred :class:`.ColumnProperty`.""" + + __slots__ = 'columns', 'group' + + def __init__(self, parent, strategy_key): + super(DeferredColumnLoader, self).__init__(parent, strategy_key) + if hasattr(self.parent_property, 'composite_class'): + raise NotImplementedError("Deferred loading for composite " + "types not implemented yet") + self.columns = self.parent_property.columns + self.group = self.parent_property.group + + def create_row_processor( + self, context, path, loadopt, + mapper, result, adapter, populators): + + # this path currently does not check the result + # for the column; this is because in most cases we are + # working just with the setup_query() directive which does + # not support this, and the behavior here should be consistent. + if not self.is_class_level: + set_deferred_for_local_state = \ + self.parent_property._deferred_column_loader + populators["new"].append((self.key, set_deferred_for_local_state)) + else: + populators["expire"].append((self.key, False)) + + def init_class_attribute(self, mapper): + self.is_class_level = True + + _register_attribute( + self.parent_property, mapper, useobject=False, + compare_function=self.columns[0].type.compare_values, + callable_=self._load_for_state, + expire_missing=False + ) + + def setup_query( + self, context, entity, path, loadopt, + adapter, column_collection, memoized_populators, + only_load_props=None, **kw): + + if ( + ( + loadopt and + 'undefer_pks' in loadopt.local_opts and + set(self.columns).intersection( + self.parent._should_undefer_in_wildcard) + ) + or + ( + loadopt and + self.group and + loadopt.local_opts.get('undefer_group_%s' % self.group, False) + ) + or + ( + only_load_props and self.key in only_load_props + ) + ): + self.parent_property._get_strategy( + (("deferred", False), ("instrument", True)) + ).setup_query( + context, entity, + path, loadopt, adapter, + column_collection, memoized_populators, **kw) + elif self.is_class_level: + memoized_populators[self.parent_property] = _SET_DEFERRED_EXPIRED + else: + memoized_populators[self.parent_property] = _DEFER_FOR_STATE + + def _load_for_state(self, state, passive): + if not state.key: + return attributes.ATTR_EMPTY + + if not passive & attributes.SQL_OK: + return attributes.PASSIVE_NO_RESULT + + localparent = state.manager.mapper + + if self.group: + toload = [ + p.key for p in + localparent.iterate_properties + if isinstance(p, StrategizedProperty) and + isinstance(p.strategy, DeferredColumnLoader) and + p.group == self.group + ] + else: + toload = [self.key] + + # narrow the keys down to just those which have no history + group = [k for k in toload if k in state.unmodified] + + session = _state_session(state) + if session is None: + raise orm_exc.DetachedInstanceError( + "Parent instance %s is not bound to a Session; " + "deferred load operation of attribute '%s' cannot proceed" % + (orm_util.state_str(state), self.key) + ) + + query = session.query(localparent) + if loading.load_on_ident( + query, state.key, + only_load_props=group, refresh_state=state) is None: + raise orm_exc.ObjectDeletedError(state) + + return attributes.ATTR_WAS_SET + + +class LoadDeferredColumns(object): + """serializable loader object used by DeferredColumnLoader""" + + def __init__(self, key): + self.key = key + + def __call__(self, state, passive=attributes.PASSIVE_OFF): + key = self.key + + localparent = state.manager.mapper + prop = localparent._props[key] + strategy = prop._strategies[DeferredColumnLoader] + return strategy._load_for_state(state, passive) + + +class AbstractRelationshipLoader(LoaderStrategy): + """LoaderStratgies which deal with related objects.""" + + __slots__ = 'mapper', 'target', 'uselist' + + def __init__(self, parent, strategy_key): + super(AbstractRelationshipLoader, self).__init__(parent, strategy_key) + self.mapper = self.parent_property.mapper + self.target = self.parent_property.target + self.uselist = self.parent_property.uselist + + +@log.class_logger +@properties.RelationshipProperty.strategy_for(do_nothing=True) +class DoNothingLoader(LoaderStrategy): + """Relationship loader that makes no change to the object's state. + + Compared to NoLoader, this loader does not initialize the + collection/attribute to empty/none; the usual default LazyLoader will + take effect. + + """ + + +@log.class_logger +@properties.RelationshipProperty.strategy_for(lazy="noload") +@properties.RelationshipProperty.strategy_for(lazy=None) +class NoLoader(AbstractRelationshipLoader): + """Provide loading behavior for a :class:`.RelationshipProperty` + with "lazy=None". + + """ + + __slots__ = () + + def init_class_attribute(self, mapper): + self.is_class_level = True + + _register_attribute( + self.parent_property, mapper, + useobject=True, + typecallable=self.parent_property.collection_class, + ) + + def create_row_processor( + self, context, path, loadopt, mapper, + result, adapter, populators): + def invoke_no_load(state, dict_, row): + if self.uselist: + state.manager.get_impl(self.key).initialize(state, dict_) + else: + dict_[self.key] = None + populators["new"].append((self.key, invoke_no_load)) + + +@log.class_logger +@properties.RelationshipProperty.strategy_for(lazy=True) +@properties.RelationshipProperty.strategy_for(lazy="select") +@properties.RelationshipProperty.strategy_for(lazy="raise") +@properties.RelationshipProperty.strategy_for(lazy="raise_on_sql") +@properties.RelationshipProperty.strategy_for(lazy="baked_select") +class LazyLoader(AbstractRelationshipLoader, util.MemoizedSlots): + """Provide loading behavior for a :class:`.RelationshipProperty` + with "lazy=True", that is loads when first accessed. + + """ + + __slots__ = ( + '_lazywhere', '_rev_lazywhere', 'use_get', '_bind_to_col', + '_equated_columns', '_rev_bind_to_col', '_rev_equated_columns', + '_simple_lazy_clause', '_raise_always', '_raise_on_sql', + '_bakery') + + def __init__(self, parent, strategy_key): + super(LazyLoader, self).__init__(parent, strategy_key) + self._raise_always = self.strategy_opts["lazy"] == "raise" + self._raise_on_sql = self.strategy_opts["lazy"] == "raise_on_sql" + + join_condition = self.parent_property._join_condition + self._lazywhere, \ + self._bind_to_col, \ + self._equated_columns = join_condition.create_lazy_clause() + + self._rev_lazywhere, \ + self._rev_bind_to_col, \ + self._rev_equated_columns = join_condition.create_lazy_clause( + reverse_direction=True) + + self.logger.info("%s lazy loading clause %s", self, self._lazywhere) + + # determine if our "lazywhere" clause is the same as the mapper's + # get() clause. then we can just use mapper.get() + self.use_get = not self.uselist and \ + self.mapper._get_clause[0].compare( + self._lazywhere, + use_proxies=True, + equivalents=self.mapper._equivalent_columns + ) + + if self.use_get: + for col in list(self._equated_columns): + if col in self.mapper._equivalent_columns: + for c in self.mapper._equivalent_columns[col]: + self._equated_columns[c] = self._equated_columns[col] + + self.logger.info("%s will use query.get() to " + "optimize instance loads", self) + + def init_class_attribute(self, mapper): + self.is_class_level = True + + active_history = ( + self.parent_property.active_history or + self.parent_property.direction is not interfaces.MANYTOONE or + not self.use_get + ) + + # MANYTOONE currently only needs the + # "old" value for delete-orphan + # cascades. the required _SingleParentValidator + # will enable active_history + # in that case. otherwise we don't need the + # "old" value during backref operations. + _register_attribute( + self.parent_property, + mapper, + useobject=True, + callable_=self._load_for_state, + typecallable=self.parent_property.collection_class, + active_history=active_history + ) + + def _memoized_attr__simple_lazy_clause(self): + criterion, bind_to_col = ( + self._lazywhere, + self._bind_to_col + ) + + params = [] + + def visit_bindparam(bindparam): + bindparam.unique = False + if bindparam._identifying_key in bind_to_col: + params.append(( + bindparam.key, bind_to_col[bindparam._identifying_key], + None)) + elif bindparam.callable is None: + params.append((bindparam.key, None, bindparam.value)) + + criterion = visitors.cloned_traverse( + criterion, {}, {'bindparam': visit_bindparam} + ) + + return criterion, params + + def _generate_lazy_clause(self, state, passive): + criterion, param_keys = self._simple_lazy_clause + + if state is None: + return sql_util.adapt_criterion_to_null( + criterion, [key for key, ident, value in param_keys]) + + mapper = self.parent_property.parent + + o = state.obj() # strong ref + dict_ = attributes.instance_dict(o) + + if passive & attributes.INIT_OK: + passive ^= attributes.INIT_OK + + params = {} + for key, ident, value in param_keys: + if ident is not None: + if passive and passive & attributes.LOAD_AGAINST_COMMITTED: + value = mapper._get_committed_state_attr_by_column( + state, dict_, ident, passive) + else: + value = mapper._get_state_attr_by_column( + state, dict_, ident, passive) + + params[key] = value + + return criterion, params + + def _invoke_raise_load(self, state, passive, lazy): + raise sa_exc.InvalidRequestError( + "'%s' is not available due to lazy='%s'" % (self, lazy) + ) + + def _load_for_state(self, state, passive): + + if not state.key and ( + ( + not self.parent_property.load_on_pending + and not state._load_pending + ) + or not state.session_id + ): + return attributes.ATTR_EMPTY + + pending = not state.key + primary_key_identity = None + + if ( + (not passive & attributes.SQL_OK and not self.use_get) + or + (not passive & attributes.NON_PERSISTENT_OK and pending) + ): + return attributes.PASSIVE_NO_RESULT + + if self._raise_always: + self._invoke_raise_load(state, passive, "raise") + + session = _state_session(state) + if not session: + raise orm_exc.DetachedInstanceError( + "Parent instance %s is not bound to a Session; " + "lazy load operation of attribute '%s' cannot proceed" % + (orm_util.state_str(state), self.key) + ) + + # if we have a simple primary key load, check the + # identity map without generating a Query at all + if self.use_get: + primary_key_identity = self._get_ident_for_use_get( + session, + state, + passive + ) + if attributes.PASSIVE_NO_RESULT in primary_key_identity: + return attributes.PASSIVE_NO_RESULT + elif attributes.NEVER_SET in primary_key_identity: + return attributes.NEVER_SET + + if _none_set.issuperset(primary_key_identity): + return None + + # look for this identity in the identity map. Delegate to the + # Query class in use, as it may have special rules for how it + # does this, including how it decides what the correct + # identity_token would be for this identity. + instance = session.query()._identity_lookup( + self.mapper, primary_key_identity, passive=passive, + lazy_loaded_from=state + ) + + if instance is not None: + return instance + elif not passive & attributes.SQL_OK or \ + not passive & attributes.RELATED_OBJECT_OK: + return attributes.PASSIVE_NO_RESULT + + return self._emit_lazyload( + session, state, primary_key_identity, passive) + + def _get_ident_for_use_get(self, session, state, passive): + instance_mapper = state.manager.mapper + + if passive & attributes.LOAD_AGAINST_COMMITTED: + get_attr = instance_mapper._get_committed_state_attr_by_column + else: + get_attr = instance_mapper._get_state_attr_by_column + + dict_ = state.dict + + return [ + get_attr( + state, + dict_, + self._equated_columns[pk], + passive=passive) + for pk in self.mapper.primary_key + ] + + @util.dependencies("sqlalchemy.ext.baked") + def _memoized_attr__bakery(self, baked): + return baked.bakery(size=50) + + @util.dependencies( + "sqlalchemy.orm.strategy_options") + def _emit_lazyload( + self, strategy_options, session, state, + primary_key_identity, passive): + # emit lazy load now using BakedQuery, to cut way down on the overhead + # of generating queries. + # there are two big things we are trying to guard against here: + # + # 1. two different lazy loads that need to have a different result, + # being cached on the same key. The results between two lazy loads + # can be different due to the options passed to the query, which + # take effect for descendant objects. Therefore we have to make + # sure paths and load options generate good cache keys, and if they + # don't, we don't cache. + # 2. a lazy load that gets cached on a key that includes some + # "throwaway" object, like a per-query AliasedClass, meaning + # the cache key will never be seen again and the cache itself + # will fill up. (the cache is an LRU cache, so while we won't + # run out of memory, it will perform terribly when it's full. A + # warning is emitted if this occurs.) We must prevent the + # generation of a cache key that is including a throwaway object + # in the key. + + # note that "lazy='select'" and "lazy=True" make two separate + # lazy loaders. Currently the LRU cache is local to the LazyLoader, + # however add ourselves to the initial cache key just to future + # proof in case it moves + q = self._bakery(lambda session: session.query(self.mapper), self) + + q.add_criteria( + lambda q: q._adapt_all_clauses()._with_invoke_all_eagers(False), + self.parent_property) + + if not self.parent_property.bake_queries: + q.spoil(full=True) + + if self.parent_property.secondary is not None: + q.add_criteria( + lambda q: + q.select_from(self.mapper, self.parent_property.secondary)) + + pending = not state.key + + # don't autoflush on pending + if pending or passive & attributes.NO_AUTOFLUSH: + q.add_criteria(lambda q: q.autoflush(False)) + + if state.load_options: + # here, if any of the options cannot return a cache key, + # the BakedQuery "spoils" and caching will not occur. a path + # that features Cls.attribute.of_type(some_alias) will cancel + # caching, for example, since "some_alias" is user-defined and + # is usually a throwaway object. + effective_path = state.load_path[self.parent_property] + + q._add_lazyload_options( + state.load_options, effective_path + ) + + if self.use_get: + if self._raise_on_sql: + self._invoke_raise_load(state, passive, "raise_on_sql") + + return q(session).\ + with_post_criteria(lambda q: q._set_lazyload_from(state)).\ + _load_on_pk_identity( + session.query(self.mapper), + primary_key_identity) + + if self.parent_property.order_by: + q.add_criteria( + lambda q: + q.order_by(*util.to_list(self.parent_property.order_by))) + + for rev in self.parent_property._reverse_property: + # reverse props that are MANYTOONE are loading *this* + # object from get(), so don't need to eager out to those. + if rev.direction is interfaces.MANYTOONE and \ + rev._use_get and \ + not isinstance(rev.strategy, LazyLoader): + + q.add_criteria( + lambda q: + q.options( + strategy_options.Load.for_existing_path( + q._current_path[rev.parent] + ).lazyload(rev.key) + ) + ) + + lazy_clause, params = self._generate_lazy_clause(state, passive) + + if pending: + if util.has_intersection( + orm_util._none_set, params.values()): + return None + + elif util.has_intersection(orm_util._never_set, params.values()): + return None + + if self._raise_on_sql: + self._invoke_raise_load(state, passive, "raise_on_sql") + + q.add_criteria(lambda q: q.filter(lazy_clause)) + + # set parameters in the query such that we don't overwrite + # parameters that are already set within it + def set_default_params(q): + params.update(q._params) + q._params = params + return q + + result = q(session).\ + with_post_criteria(lambda q: q._set_lazyload_from(state)).\ + with_post_criteria(set_default_params).all() + if self.uselist: + return result + else: + l = len(result) + if l: + if l > 1: + util.warn( + "Multiple rows returned with " + "uselist=False for lazily-loaded attribute '%s' " + % self.parent_property) + + return result[0] + else: + return None + + def create_row_processor( + self, context, path, loadopt, + mapper, result, adapter, populators): + key = self.key + + if not self.is_class_level: + # we are not the primary manager for this attribute + # on this class - set up a + # per-instance lazyloader, which will override the + # class-level behavior. + # this currently only happens when using a + # "lazyload" option on a "no load" + # attribute - "eager" attributes always have a + # class-level lazyloader installed. + set_lazy_callable = InstanceState._instance_level_callable_processor( + mapper.class_manager, + LoadLazyAttribute(key, self), key) + + populators["new"].append((self.key, set_lazy_callable)) + elif context.populate_existing or mapper.always_refresh: + def reset_for_lazy_callable(state, dict_, row): + # we are the primary manager for this attribute on + # this class - reset its + # per-instance attribute state, so that the class-level + # lazy loader is + # executed when next referenced on this instance. + # this is needed in + # populate_existing() types of scenarios to reset + # any existing state. + state._reset(dict_, key) + + populators["new"].append((self.key, reset_for_lazy_callable)) + + +class LoadLazyAttribute(object): + """serializable loader object used by LazyLoader""" + + def __init__(self, key, initiating_strategy): + self.key = key + self.strategy_key = initiating_strategy.strategy_key + + def __call__(self, state, passive=attributes.PASSIVE_OFF): + key = self.key + instance_mapper = state.manager.mapper + prop = instance_mapper._props[key] + strategy = prop._strategies[self.strategy_key] + + return strategy._load_for_state(state, passive) + + +@properties.RelationshipProperty.strategy_for(lazy="immediate") +class ImmediateLoader(AbstractRelationshipLoader): + __slots__ = () + + def init_class_attribute(self, mapper): + self.parent_property.\ + _get_strategy((("lazy", "select"),)).\ + init_class_attribute(mapper) + + def setup_query( + self, context, entity, + path, loadopt, adapter, column_collection=None, + parentmapper=None, **kwargs): + pass + + def create_row_processor( + self, context, path, loadopt, + mapper, result, adapter, populators): + def load_immediate(state, dict_, row): + state.get_impl(self.key).get(state, dict_) + + populators["delayed"].append((self.key, load_immediate)) + + +@log.class_logger +@properties.RelationshipProperty.strategy_for(lazy="subquery") +class SubqueryLoader(AbstractRelationshipLoader): + __slots__ = 'join_depth', + + def __init__(self, parent, strategy_key): + super(SubqueryLoader, self).__init__(parent, strategy_key) + self.join_depth = self.parent_property.join_depth + + def init_class_attribute(self, mapper): + self.parent_property.\ + _get_strategy((("lazy", "select"),)).\ + init_class_attribute(mapper) + + def setup_query( + self, context, entity, + path, loadopt, adapter, + column_collection=None, + parentmapper=None, **kwargs): + + if not context.query._enable_eagerloads: + return + elif context.query._yield_per: + context.query._no_yield_per("subquery") + + path = path[self.parent_property] + + # build up a path indicating the path from the leftmost + # entity to the thing we're subquery loading. + with_poly_info = path.get( + context.attributes, + "path_with_polymorphic", None) + if with_poly_info is not None: + effective_entity = with_poly_info.entity + else: + effective_entity = self.mapper + + subq_path = context.attributes.get( + ('subquery_path', None), + orm_util.PathRegistry.root) + + subq_path = subq_path + path + + # if not via query option, check for + # a cycle + if not path.contains(context.attributes, "loader"): + if self.join_depth: + if ( + (context.query._current_path.length + if context.query._current_path else 0) + + path.length + ) / 2 > self.join_depth: + return + elif subq_path.contains_mapper(self.mapper): + return + + leftmost_mapper, leftmost_attr, leftmost_relationship = \ + self._get_leftmost(subq_path) + + orig_query = context.attributes.get( + ("orig_query", SubqueryLoader), + context.query) + + # generate a new Query from the original, then + # produce a subquery from it. + left_alias = self._generate_from_original_query( + orig_query, leftmost_mapper, + leftmost_attr, leftmost_relationship, + entity.entity_zero + ) + + # generate another Query that will join the + # left alias to the target relationships. + # basically doing a longhand + # "from_self()". (from_self() itself not quite industrial + # strength enough for all contingencies...but very close) + q = orig_query.session.query(effective_entity) + q._attributes = { + ("orig_query", SubqueryLoader): orig_query, + ('subquery_path', None): subq_path + } + + q = q._set_enable_single_crit(False) + to_join, local_attr, parent_alias = \ + self._prep_for_joins(left_alias, subq_path) + q = q.order_by(*local_attr) + q = q.add_columns(*local_attr) + q = self._apply_joins( + q, to_join, left_alias, + parent_alias, effective_entity) + + q = self._setup_options(q, subq_path, orig_query, effective_entity) + q = self._setup_outermost_orderby(q) + + # add new query to attributes to be picked up + # by create_row_processor + path.set(context.attributes, "subquery", q) + + def _get_leftmost(self, subq_path): + subq_path = subq_path.path + subq_mapper = orm_util._class_to_mapper(subq_path[0]) + + # determine attributes of the leftmost mapper + if self.parent.isa(subq_mapper) and \ + self.parent_property is subq_path[1]: + leftmost_mapper, leftmost_prop = \ + self.parent, self.parent_property + else: + leftmost_mapper, leftmost_prop = \ + subq_mapper, \ + subq_path[1] + + leftmost_cols = leftmost_prop.local_columns + + leftmost_attr = [ + getattr( + subq_path[0].entity, + leftmost_mapper._columntoproperty[c].key) + for c in leftmost_cols + ] + + return leftmost_mapper, leftmost_attr, leftmost_prop + + def _generate_from_original_query( + self, + orig_query, leftmost_mapper, + leftmost_attr, leftmost_relationship, orig_entity + ): + # reformat the original query + # to look only for significant columns + q = orig_query._clone().correlate(None) + + # set the query's "FROM" list explicitly to what the + # FROM list would be in any case, as we will be limiting + # the columns in the SELECT list which may no longer include + # all entities mentioned in things like WHERE, JOIN, etc. + if not q._from_obj: + q._set_select_from( + list(set([ + ent['entity'] for ent in orig_query.column_descriptions + if ent['entity'] is not None + ])), + False + ) + + # select from the identity columns of the outer (specifically, these + # are the 'local_cols' of the property). This will remove + # other columns from the query that might suggest the right entity + # which is why we do _set_select_from above. + target_cols = q._adapt_col_list(leftmost_attr) + q._set_entities(target_cols) + + distinct_target_key = leftmost_relationship.distinct_target_key + + if distinct_target_key is True: + q._distinct = True + elif distinct_target_key is None: + # if target_cols refer to a non-primary key or only + # part of a composite primary key, set the q as distinct + for t in set(c.table for c in target_cols): + if not set(target_cols).issuperset(t.primary_key): + q._distinct = True + break + + if q._order_by is False: + q._order_by = leftmost_mapper.order_by + + # don't need ORDER BY if no limit/offset + if q._limit is None and q._offset is None: + q._order_by = None + + # the original query now becomes a subquery + # which we'll join onto. + + embed_q = q.with_labels().subquery() + left_alias = orm_util.AliasedClass( + leftmost_mapper, embed_q, + use_mapper_path=True) + return left_alias + + def _prep_for_joins(self, left_alias, subq_path): + # figure out what's being joined. a.k.a. the fun part + to_join = [] + pairs = list(subq_path.pairs()) + + for i, (mapper, prop) in enumerate(pairs): + if i > 0: + # look at the previous mapper in the chain - + # if it is as or more specific than this prop's + # mapper, use that instead. + # note we have an assumption here that + # the non-first element is always going to be a mapper, + # not an AliasedClass + + prev_mapper = pairs[i - 1][1].mapper + to_append = prev_mapper if prev_mapper.isa(mapper) else mapper + else: + to_append = mapper + + to_join.append((to_append, prop.key)) + + # determine the immediate parent class we are joining from, + # which needs to be aliased. + + if len(to_join) < 2: + # in the case of a one level eager load, this is the + # leftmost "left_alias". + parent_alias = left_alias + else: + info = inspect(to_join[-1][0]) + if info.is_aliased_class: + parent_alias = info.entity + else: + # alias a plain mapper as we may be + # joining multiple times + parent_alias = orm_util.AliasedClass( + info.entity, + use_mapper_path=True) + + local_cols = self.parent_property.local_columns + + local_attr = [ + getattr(parent_alias, self.parent._columntoproperty[c].key) + for c in local_cols + ] + return to_join, local_attr, parent_alias + + def _apply_joins( + self, q, to_join, left_alias, parent_alias, + effective_entity): + + ltj = len(to_join) + if ltj == 1: + to_join = [ + getattr(left_alias, to_join[0][1]).of_type(effective_entity) + ] + elif ltj == 2: + to_join = [ + getattr(left_alias, to_join[0][1]).of_type(parent_alias), + getattr(parent_alias, to_join[-1][1]).of_type(effective_entity) + ] + elif ltj > 2: + middle = [ + ( + orm_util.AliasedClass(item[0]) + if not inspect(item[0]).is_aliased_class + else item[0].entity, + item[1] + ) for item in to_join[1:-1] + ] + inner = [] + + while middle: + item = middle.pop(0) + attr = getattr(item[0], item[1]) + if middle: + attr = attr.of_type(middle[0][0]) + else: + attr = attr.of_type(parent_alias) + + inner.append(attr) + + to_join = [ + getattr(left_alias, to_join[0][1]).of_type(inner[0].parent) + ] + inner + [ + getattr(parent_alias, to_join[-1][1]).of_type(effective_entity) + ] + + for attr in to_join: + q = q.join(attr, from_joinpoint=True) + return q + + def _setup_options(self, q, subq_path, orig_query, effective_entity): + # propagate loader options etc. to the new query. + # these will fire relative to subq_path. + q = q._with_current_path(subq_path) + q = q._conditional_options(*orig_query._with_options) + if orig_query._populate_existing: + q._populate_existing = orig_query._populate_existing + + return q + + def _setup_outermost_orderby(self, q): + if self.parent_property.order_by: + # if there's an ORDER BY, alias it the same + # way joinedloader does, but we have to pull out + # the "eagerjoin" from the query. + # this really only picks up the "secondary" table + # right now. + eagerjoin = q._from_obj[0] + eager_order_by = \ + eagerjoin._target_adapter.\ + copy_and_process( + util.to_list( + self.parent_property.order_by + ) + ) + q = q.order_by(*eager_order_by) + return q + + class _SubqCollections(object): + """Given a :class:`.Query` used to emit the "subquery load", + provide a load interface that executes the query at the + first moment a value is needed. + + """ + _data = None + + def __init__(self, subq): + self.subq = subq + + def get(self, key, default): + if self._data is None: + self._load() + return self._data.get(key, default) + + def _load(self): + self._data = dict( + (k, [vv[0] for vv in v]) + for k, v in itertools.groupby( + self.subq, + lambda x: x[1:] + ) + ) + + def loader(self, state, dict_, row): + if self._data is None: + self._load() + + def create_row_processor( + self, context, path, loadopt, + mapper, result, adapter, populators): + if not self.parent.class_manager[self.key].impl.supports_population: + raise sa_exc.InvalidRequestError( + "'%s' does not support object " + "population - eager loading cannot be applied." % + self) + + path = path[self.parent_property] + + subq = path.get(context.attributes, 'subquery') + + if subq is None: + return + + assert subq.session is context.session, ( + "Subquery session doesn't refer to that of " + "our context. Are there broken context caching " + "schemes being used?" + ) + + local_cols = self.parent_property.local_columns + + # cache the loaded collections in the context + # so that inheriting mappers don't re-load when they + # call upon create_row_processor again + collections = path.get(context.attributes, "collections") + if collections is None: + collections = self._SubqCollections(subq) + path.set(context.attributes, 'collections', collections) + + if adapter: + local_cols = [adapter.columns[c] for c in local_cols] + + if self.uselist: + self._create_collection_loader( + context, collections, local_cols, populators) + else: + self._create_scalar_loader( + context, collections, local_cols, populators) + + def _create_collection_loader( + self, context, collections, local_cols, populators): + def load_collection_from_subq(state, dict_, row): + collection = collections.get( + tuple([row[col] for col in local_cols]), + () + ) + state.get_impl(self.key).\ + set_committed_value(state, dict_, collection) + + def load_collection_from_subq_existing_row(state, dict_, row): + if self.key not in dict_: + load_collection_from_subq(state, dict_, row) + + populators["new"].append( + (self.key, load_collection_from_subq)) + populators["existing"].append( + (self.key, load_collection_from_subq_existing_row)) + + if context.invoke_all_eagers: + populators["eager"].append((self.key, collections.loader)) + + def _create_scalar_loader( + self, context, collections, local_cols, populators): + def load_scalar_from_subq(state, dict_, row): + collection = collections.get( + tuple([row[col] for col in local_cols]), + (None,) + ) + if len(collection) > 1: + util.warn( + "Multiple rows returned with " + "uselist=False for eagerly-loaded attribute '%s' " + % self) + + scalar = collection[0] + state.get_impl(self.key).\ + set_committed_value(state, dict_, scalar) + + def load_scalar_from_subq_existing_row(state, dict_, row): + if self.key not in dict_: + load_scalar_from_subq(state, dict_, row) + + populators["new"].append( + (self.key, load_scalar_from_subq)) + populators["existing"].append( + (self.key, load_scalar_from_subq_existing_row)) + if context.invoke_all_eagers: + populators["eager"].append((self.key, collections.loader)) + + +@log.class_logger +@properties.RelationshipProperty.strategy_for(lazy="joined") +@properties.RelationshipProperty.strategy_for(lazy=False) +class JoinedLoader(AbstractRelationshipLoader): + """Provide loading behavior for a :class:`.RelationshipProperty` + using joined eager loading. + + """ + + __slots__ = 'join_depth', '_aliased_class_pool' + + def __init__(self, parent, strategy_key): + super(JoinedLoader, self).__init__(parent, strategy_key) + self.join_depth = self.parent_property.join_depth + self._aliased_class_pool = [] + + def init_class_attribute(self, mapper): + self.parent_property.\ + _get_strategy((("lazy", "select"),)).init_class_attribute(mapper) + + def setup_query( + self, context, entity, path, loadopt, adapter, + column_collection=None, parentmapper=None, + chained_from_outerjoin=False, + **kwargs): + """Add a left outer join to the statement that's being constructed.""" + + if not context.query._enable_eagerloads: + return + elif context.query._yield_per and self.uselist: + context.query._no_yield_per("joined collection") + + path = path[self.parent_property] + + with_polymorphic = None + + user_defined_adapter = self._init_user_defined_eager_proc( + loadopt, context) if loadopt else False + + if user_defined_adapter is not False: + clauses, adapter, add_to_collection = \ + self._setup_query_on_user_defined_adapter( + context, entity, path, adapter, + user_defined_adapter + ) + else: + # if not via query option, check for + # a cycle + if not path.contains(context.attributes, "loader"): + if self.join_depth: + if path.length / 2 > self.join_depth: + return + elif path.contains_mapper(self.mapper): + return + + clauses, adapter, add_to_collection, chained_from_outerjoin = \ + self._generate_row_adapter( + context, entity, path, loadopt, adapter, + column_collection, parentmapper, chained_from_outerjoin + ) + + with_poly_info = path.get( + context.attributes, + "path_with_polymorphic", + None + ) + if with_poly_info is not None: + with_polymorphic = with_poly_info.with_polymorphic_mappers + else: + with_polymorphic = None + + path = path[self.mapper] + + loading._setup_entity_query( + context, self.mapper, entity, + path, clauses, add_to_collection, + with_polymorphic=with_polymorphic, + parentmapper=self.mapper, + chained_from_outerjoin=chained_from_outerjoin) + + if with_poly_info is not None and \ + None in set(context.secondary_columns): + raise sa_exc.InvalidRequestError( + "Detected unaliased columns when generating joined " + "load. Make sure to use aliased=True or flat=True " + "when using joined loading with with_polymorphic()." + ) + + def _init_user_defined_eager_proc(self, loadopt, context): + + # check if the opt applies at all + if "eager_from_alias" not in loadopt.local_opts: + # nope + return False + + path = loadopt.path.parent + + # the option applies. check if the "user_defined_eager_row_processor" + # has been built up. + adapter = path.get( + context.attributes, + "user_defined_eager_row_processor", False) + if adapter is not False: + # just return it + return adapter + + # otherwise figure it out. + alias = loadopt.local_opts["eager_from_alias"] + + root_mapper, prop = path[-2:] + + #from .mapper import Mapper + #from .interfaces import MapperProperty + #assert isinstance(root_mapper, Mapper) + #assert isinstance(prop, MapperProperty) + + if alias is not None: + if isinstance(alias, str): + alias = prop.target.alias(alias) + adapter = sql_util.ColumnAdapter( + alias, + equivalents=prop.mapper._equivalent_columns) + else: + if path.contains(context.attributes, "path_with_polymorphic"): + with_poly_info = path.get( + context.attributes, + "path_with_polymorphic") + adapter = orm_util.ORMAdapter( + with_poly_info.entity, + equivalents=prop.mapper._equivalent_columns) + else: + adapter = context.query._polymorphic_adapters.get( + prop.mapper, None) + path.set( + context.attributes, + "user_defined_eager_row_processor", + adapter) + + return adapter + + def _setup_query_on_user_defined_adapter( + self, context, entity, + path, adapter, user_defined_adapter): + + # apply some more wrapping to the "user defined adapter" + # if we are setting up the query for SQL render. + adapter = entity._get_entity_clauses(context.query, context) + + if adapter and user_defined_adapter: + user_defined_adapter = user_defined_adapter.wrap(adapter) + path.set( + context.attributes, "user_defined_eager_row_processor", + user_defined_adapter) + elif adapter: + user_defined_adapter = adapter + path.set( + context.attributes, "user_defined_eager_row_processor", + user_defined_adapter) + + add_to_collection = context.primary_columns + return user_defined_adapter, adapter, add_to_collection + + def _gen_pooled_aliased_class(self, context): + # keep a local pool of AliasedClass objects that get re-used. + # we need one unique AliasedClass per query per appearance of our + # entity in the query. + + key = ('joinedloader_ac', self) + if key not in context.attributes: + context.attributes[key] = idx = 0 + else: + context.attributes[key] = idx = context.attributes[key] + 1 + + if idx >= len(self._aliased_class_pool): + to_adapt = orm_util.AliasedClass( + self.mapper, + flat=True, + use_mapper_path=True) + # load up the .columns collection on the Alias() before + # the object becomes shared among threads. this prevents + # races for column identities. + inspect(to_adapt).selectable.c + + self._aliased_class_pool.append(to_adapt) + + return self._aliased_class_pool[idx] + + def _generate_row_adapter( + self, + context, entity, path, loadopt, adapter, + column_collection, parentmapper, chained_from_outerjoin): + with_poly_info = path.get( + context.attributes, + "path_with_polymorphic", + None + ) + if with_poly_info: + to_adapt = with_poly_info.entity + else: + to_adapt = self._gen_pooled_aliased_class(context) + + clauses = inspect(to_adapt)._memo( + ("joinedloader_ormadapter", self), + orm_util.ORMAdapter, + to_adapt, + equivalents=self.mapper._equivalent_columns, + adapt_required=True, allow_label_resolve=False, + anonymize_labels=True + ) + + assert clauses.aliased_class is not None + + if self.parent_property.uselist: + context.multi_row_eager_loaders = True + + innerjoin = ( + loadopt.local_opts.get( + 'innerjoin', self.parent_property.innerjoin) + if loadopt is not None + else self.parent_property.innerjoin + ) + + if not innerjoin: + # if this is an outer join, all non-nested eager joins from + # this path must also be outer joins + chained_from_outerjoin = True + + context.create_eager_joins.append( + ( + self._create_eager_join, context, + entity, path, adapter, + parentmapper, clauses, innerjoin, chained_from_outerjoin + ) + ) + + add_to_collection = context.secondary_columns + path.set(context.attributes, "eager_row_processor", clauses) + + return clauses, adapter, add_to_collection, chained_from_outerjoin + + def _create_eager_join( + self, context, entity, + path, adapter, parentmapper, + clauses, innerjoin, chained_from_outerjoin): + + if parentmapper is None: + localparent = entity.mapper + else: + localparent = parentmapper + + # whether or not the Query will wrap the selectable in a subquery, + # and then attach eager load joins to that (i.e., in the case of + # LIMIT/OFFSET etc.) + should_nest_selectable = context.multi_row_eager_loaders and \ + context.query._should_nest_selectable + + entity_key = None + + if entity not in context.eager_joins and \ + not should_nest_selectable and \ + context.from_clause: + index, clause = sql_util.find_join_source( + context.from_clause, entity.selectable) + if clause is not None: + # join to an existing FROM clause on the query. + # key it to its list index in the eager_joins dict. + # Query._compile_context will adapt as needed and + # append to the FROM clause of the select(). + entity_key, default_towrap = index, clause + + if entity_key is None: + entity_key, default_towrap = entity, entity.selectable + + towrap = context.eager_joins.setdefault(entity_key, default_towrap) + + if adapter: + if getattr(adapter, 'aliased_class', None): + # joining from an adapted entity. The adapted entity + # might be a "with_polymorphic", so resolve that to our + # specific mapper's entity before looking for our attribute + # name on it. + efm = inspect(adapter.aliased_class).\ + _entity_for_mapper( + localparent + if localparent.isa(self.parent) else self.parent) + + # look for our attribute on the adapted entity, else fall back + # to our straight property + onclause = getattr( + efm.entity, self.key, + self.parent_property) + else: + onclause = getattr( + orm_util.AliasedClass( + self.parent, + adapter.selectable, + use_mapper_path=True + ), + self.key, self.parent_property + ) + + else: + onclause = self.parent_property + + assert clauses.aliased_class is not None + + attach_on_outside = ( + not chained_from_outerjoin or + not innerjoin or innerjoin == 'unnested' or + entity.entity_zero.represents_outer_join + ) + + if attach_on_outside: + # this is the "classic" eager join case. + eagerjoin = orm_util._ORMJoin( + towrap, + clauses.aliased_class, + onclause, + isouter=not innerjoin or + entity.entity_zero.represents_outer_join or + ( + chained_from_outerjoin and isinstance(towrap, sql.Join) + ), _left_memo=self.parent, _right_memo=self.mapper + ) + else: + # all other cases are innerjoin=='nested' approach + eagerjoin = self._splice_nested_inner_join( + path, towrap, clauses, onclause) + + context.eager_joins[entity_key] = eagerjoin + + # send a hint to the Query as to where it may "splice" this join + eagerjoin.stop_on = entity.selectable + + if not parentmapper: + # for parentclause that is the non-eager end of the join, + # ensure all the parent cols in the primaryjoin are actually + # in the + # columns clause (i.e. are not deferred), so that aliasing applied + # by the Query propagates those columns outward. + # This has the effect + # of "undefering" those columns. + for col in sql_util._find_columns( + self.parent_property.primaryjoin): + if localparent.mapped_table.c.contains_column(col): + if adapter: + col = adapter.columns[col] + context.primary_columns.append(col) + + if self.parent_property.order_by: + context.eager_order_by += eagerjoin._target_adapter.\ + copy_and_process( + util.to_list( + self.parent_property.order_by + ) + ) + + def _splice_nested_inner_join( + self, path, join_obj, clauses, onclause, splicing=False): + + if splicing is False: + # first call is always handed a join object + # from the outside + assert isinstance(join_obj, orm_util._ORMJoin) + elif isinstance(join_obj, sql.selectable.FromGrouping): + return self._splice_nested_inner_join( + path, join_obj.element, clauses, onclause, splicing + ) + elif not isinstance(join_obj, orm_util._ORMJoin): + if path[-2] is splicing: + return orm_util._ORMJoin( + join_obj, clauses.aliased_class, + onclause, isouter=False, + _left_memo=splicing, + _right_memo=path[-1].mapper + ) + else: + # only here if splicing == True + return None + + target_join = self._splice_nested_inner_join( + path, join_obj.right, clauses, + onclause, join_obj._right_memo) + if target_join is None: + right_splice = False + target_join = self._splice_nested_inner_join( + path, join_obj.left, clauses, + onclause, join_obj._left_memo) + if target_join is None: + # should only return None when recursively called, + # e.g. splicing==True + assert splicing is not False, \ + "assertion failed attempting to produce joined eager loads" + return None + else: + right_splice = True + + if right_splice: + # for a right splice, attempt to flatten out + # a JOIN b JOIN c JOIN .. to avoid needless + # parenthesis nesting + if not join_obj.isouter and not target_join.isouter: + eagerjoin = join_obj._splice_into_center(target_join) + else: + eagerjoin = orm_util._ORMJoin( + join_obj.left, target_join, + join_obj.onclause, isouter=join_obj.isouter, + _left_memo=join_obj._left_memo) + else: + eagerjoin = orm_util._ORMJoin( + target_join, join_obj.right, + join_obj.onclause, isouter=join_obj.isouter, + _right_memo=join_obj._right_memo) + + eagerjoin._target_adapter = target_join._target_adapter + return eagerjoin + + def _create_eager_adapter(self, context, result, adapter, path, loadopt): + user_defined_adapter = self._init_user_defined_eager_proc( + loadopt, context) if loadopt else False + + if user_defined_adapter is not False: + decorator = user_defined_adapter + # user defined eagerloads are part of the "primary" + # portion of the load. + # the adapters applied to the Query should be honored. + if context.adapter and decorator: + decorator = decorator.wrap(context.adapter) + elif context.adapter: + decorator = context.adapter + else: + decorator = path.get(context.attributes, "eager_row_processor") + if decorator is None: + return False + + if self.mapper._result_has_identity_key(result, decorator): + return decorator + else: + # no identity key - don't return a row + # processor, will cause a degrade to lazy + return False + + def create_row_processor( + self, context, path, loadopt, mapper, + result, adapter, populators): + if not self.parent.class_manager[self.key].impl.supports_population: + raise sa_exc.InvalidRequestError( + "'%s' does not support object " + "population - eager loading cannot be applied." % + self + ) + + our_path = path[self.parent_property] + + eager_adapter = self._create_eager_adapter( + context, + result, + adapter, our_path, loadopt) + + if eager_adapter is not False: + key = self.key + + _instance = loading._instance_processor( + self.mapper, + context, + result, + our_path[self.mapper], + eager_adapter) + + if not self.uselist: + self._create_scalar_loader(context, key, _instance, populators) + else: + self._create_collection_loader( + context, key, _instance, populators) + else: + self.parent_property._get_strategy((("lazy", "select"),)).\ + create_row_processor( + context, path, loadopt, + mapper, result, adapter, populators) + + def _create_collection_loader(self, context, key, _instance, populators): + def load_collection_from_joined_new_row(state, dict_, row): + collection = attributes.init_state_collection( + state, dict_, key) + result_list = util.UniqueAppender(collection, + 'append_without_event') + context.attributes[(state, key)] = result_list + inst = _instance(row) + if inst is not None: + result_list.append(inst) + + def load_collection_from_joined_existing_row(state, dict_, row): + if (state, key) in context.attributes: + result_list = context.attributes[(state, key)] + else: + # appender_key can be absent from context.attributes + # with isnew=False when self-referential eager loading + # is used; the same instance may be present in two + # distinct sets of result columns + collection = attributes.init_state_collection( + state, dict_, key) + result_list = util.UniqueAppender( + collection, + 'append_without_event') + context.attributes[(state, key)] = result_list + inst = _instance(row) + if inst is not None: + result_list.append(inst) + + def load_collection_from_joined_exec(state, dict_, row): + _instance(row) + + populators["new"].append((self.key, load_collection_from_joined_new_row)) + populators["existing"].append( + (self.key, load_collection_from_joined_existing_row)) + if context.invoke_all_eagers: + populators["eager"].append( + (self.key, load_collection_from_joined_exec)) + + def _create_scalar_loader(self, context, key, _instance, populators): + def load_scalar_from_joined_new_row(state, dict_, row): + # set a scalar object instance directly on the parent + # object, bypassing InstrumentedAttribute event handlers. + dict_[key] = _instance(row) + + def load_scalar_from_joined_existing_row(state, dict_, row): + # call _instance on the row, even though the object has + # been created, so that we further descend into properties + existing = _instance(row) + + # conflicting value already loaded, this shouldn't happen + if key in dict_: + if existing is not dict_[key]: + util.warn( + "Multiple rows returned with " + "uselist=False for eagerly-loaded attribute '%s' " + % self) + else: + # this case is when one row has multiple loads of the + # same entity (e.g. via aliasing), one has an attribute + # that the other doesn't. + dict_[key] = existing + + def load_scalar_from_joined_exec(state, dict_, row): + _instance(row) + + populators["new"].append((self.key, load_scalar_from_joined_new_row)) + populators["existing"].append( + (self.key, load_scalar_from_joined_existing_row)) + if context.invoke_all_eagers: + populators["eager"].append((self.key, load_scalar_from_joined_exec)) + + +@log.class_logger +@properties.RelationshipProperty.strategy_for(lazy="selectin") +class SelectInLoader(AbstractRelationshipLoader, util.MemoizedSlots): + __slots__ = ( + 'join_depth', '_parent_alias', '_in_expr', '_parent_pk_cols', + '_zero_idx', '_bakery' + ) + + _chunksize = 500 + + def __init__(self, parent, strategy_key): + super(SelectInLoader, self).__init__(parent, strategy_key) + self.join_depth = self.parent_property.join_depth + self._parent_alias = aliased(self.parent.class_) + pa_insp = inspect(self._parent_alias) + self._parent_pk_cols = pk_cols = [ + pa_insp._adapt_element(col) for col in self.parent.primary_key] + if len(pk_cols) > 1: + self._in_expr = sql.tuple_(*pk_cols) + self._zero_idx = False + else: + self._in_expr = pk_cols[0] + self._zero_idx = True + + def init_class_attribute(self, mapper): + self.parent_property.\ + _get_strategy((("lazy", "select"),)).\ + init_class_attribute(mapper) + + @util.dependencies("sqlalchemy.ext.baked") + def _memoized_attr__bakery(self, baked): + return baked.bakery(size=50) + + def create_row_processor( + self, context, path, loadopt, mapper, + result, adapter, populators): + if not self.parent.class_manager[self.key].impl.supports_population: + raise sa_exc.InvalidRequestError( + "'%s' does not support object " + "population - eager loading cannot be applied." % + self + ) + + selectin_path = ( + context.query._current_path or orm_util.PathRegistry.root) + path + + if not orm_util._entity_isa(path[-1], self.parent): + return + + if loading.PostLoad.path_exists(context, selectin_path, self.key): + return + + path_w_prop = path[self.parent_property] + selectin_path_w_prop = selectin_path[self.parent_property] + + # build up a path indicating the path from the leftmost + # entity to the thing we're subquery loading. + with_poly_info = path_w_prop.get( + context.attributes, + "path_with_polymorphic", None) + + if with_poly_info is not None: + effective_entity = with_poly_info.entity + else: + effective_entity = self.mapper + + if not path_w_prop.contains(context.attributes, "loader"): + if self.join_depth: + if selectin_path_w_prop.length / 2 > self.join_depth: + return + elif selectin_path_w_prop.contains_mapper(self.mapper): + return + + loading.PostLoad.callable_for_path( + context, selectin_path, self.parent, self.key, + self._load_for_path, effective_entity) + + @util.dependencies("sqlalchemy.ext.baked") + def _load_for_path( + self, baked, context, path, states, load_only, effective_entity): + + if load_only and self.key not in load_only: + return + + our_states = [ + (state.key[1], state, overwrite) + for state, overwrite in states + ] + + pk_cols = self._parent_pk_cols + pa = self._parent_alias + + q = self._bakery( + lambda session: session.query( + query.Bundle("pk", *pk_cols), effective_entity, + ), self + ) + + q.add_criteria( + lambda q: q.select_from(pa).join( + getattr(pa, + self.parent_property.key).of_type(effective_entity)). + filter( + self._in_expr.in_( + sql.bindparam('primary_keys', expanding=True)) + ).order_by(*pk_cols) + ) + + orig_query = context.query + + q._add_lazyload_options( + orig_query._with_options, + path[self.parent_property] + ) + + if orig_query._populate_existing: + q.add_criteria( + lambda q: q.populate_existing() + ) + + if self.parent_property.order_by: + def _setup_outermost_orderby(q): + # imitate the same method that + # subquery eager loading does it, looking for the + # adapted "secondary" table + eagerjoin = q._from_obj[0] + eager_order_by = \ + eagerjoin._target_adapter.\ + copy_and_process( + util.to_list( + self.parent_property.order_by + ) + ) + return q.order_by(*eager_order_by) + + q.add_criteria( + _setup_outermost_orderby + ) + + uselist = self.uselist + _empty_result = () if uselist else None + + while our_states: + chunk = our_states[0:self._chunksize] + our_states = our_states[self._chunksize:] + + data = { + k: [vv[1] for vv in v] + for k, v in itertools.groupby( + q(context.session).params( + primary_keys=[ + key[0] if self._zero_idx else key + for key, state, overwrite in chunk] + ), + lambda x: x[0] + ) + } + + for key, state, overwrite in chunk: + + if not overwrite and self.key in state.dict: + continue + + collection = data.get(key, _empty_result) + + if not uselist and collection: + if len(collection) > 1: + util.warn( + "Multiple rows returned with " + "uselist=False for eagerly-loaded " + "attribute '%s' " + % self) + state.get_impl(self.key).set_committed_value( + state, state.dict, collection[0]) + else: + state.get_impl(self.key).set_committed_value( + state, state.dict, collection) + + +def single_parent_validator(desc, prop): + def _do_check(state, value, oldvalue, initiator): + if value is not None and initiator.key == prop.key: + hasparent = initiator.hasparent(attributes.instance_state(value)) + if hasparent and oldvalue is not value: + raise sa_exc.InvalidRequestError( + "Instance %s is already associated with an instance " + "of %s via its %s attribute, and is only allowed a " + "single parent." % + (orm_util.instance_str(value), state.class_, prop) + ) + return value + + def append(state, value, initiator): + return _do_check(state, value, None, initiator) + + def set_(state, value, oldvalue, initiator): + return _do_check(state, value, oldvalue, initiator) + + event.listen( + desc, 'append', append, raw=True, retval=True, + active_history=True) + event.listen( + desc, 'set', set_, raw=True, retval=True, + active_history=True) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/strategy_options.py b/venv/Lib/site-packages/sqlalchemy/orm/strategy_options.py new file mode 100644 index 0000000..2f872f5 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/strategy_options.py @@ -0,0 +1,1493 @@ +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +""" + +""" + +from .interfaces import MapperOption, PropComparator, MapperProperty +from .attributes import QueryableAttribute +from .. import util +from ..sql.base import _generative, Generative +from .. import exc as sa_exc, inspect +from .base import _is_aliased_class, _class_to_mapper, _is_mapped_class, \ + InspectionAttr +from . import util as orm_util +from .path_registry import PathRegistry, TokenRegistry, \ + _WILDCARD_TOKEN, _DEFAULT_TOKEN + + +class Load(Generative, MapperOption): + """Represents loader options which modify the state of a + :class:`.Query` in order to affect how various mapped attributes are + loaded. + + The :class:`.Load` object is in most cases used implicitly behind the + scenes when one makes use of a query option like :func:`.joinedload`, + :func:`.defer`, or similar. However, the :class:`.Load` object + can also be used directly, and in some cases can be useful. + + To use :class:`.Load` directly, instantiate it with the target mapped + class as the argument. This style of usage is + useful when dealing with a :class:`.Query` that has multiple entities:: + + myopt = Load(MyClass).joinedload("widgets") + + The above ``myopt`` can now be used with :meth:`.Query.options`, where it + will only take effect for the ``MyClass`` entity:: + + session.query(MyClass, MyOtherClass).options(myopt) + + One case where :class:`.Load` is useful as public API is when specifying + "wildcard" options that only take effect for a certain class:: + + session.query(Order).options(Load(Order).lazyload('*')) + + Above, all relationships on ``Order`` will be lazy-loaded, but other + attributes on those descendant objects will load using their normal + loader strategy. + + .. seealso:: + + :ref:`loading_toplevel` + + """ + + def __init__(self, entity): + insp = inspect(entity) + self.path = insp._path_registry + # note that this .context is shared among all descendant + # Load objects + self.context = util.OrderedDict() + self.local_opts = {} + self._of_type = None + self.is_class_strategy = False + + @classmethod + def for_existing_path(cls, path): + load = cls.__new__(cls) + load.path = path + load.context = {} + load.local_opts = {} + load._of_type = None + return load + + def _generate_cache_key(self, path): + if path.path[0].is_aliased_class: + return False + + serialized = [] + for (key, loader_path), obj in self.context.items(): + if key != "loader": + continue + + for local_elem, obj_elem in zip(self.path.path, loader_path): + if local_elem is not obj_elem: + break + else: + endpoint = obj._of_type or obj.path.path[-1] + chopped = self._chop_path(loader_path, path) + + if ( + # means loader_path and path are unrelated, + # this does not need to be part of a cache key + chopped is None + ) or ( + # means no additional path with loader_path + path + # and the endpoint isn't using of_type so isn't modified + # into an alias or other unsafe entity + not chopped and not obj._of_type + ): + continue + + serialized_path = [] + + for token in chopped: + if isinstance(token, util.string_types): + serialized_path.append(token) + elif token.is_aliased_class: + return False + elif token.is_property: + serialized_path.append(token.key) + else: + assert token.is_mapper + serialized_path.append(token.class_) + + if not serialized_path or endpoint != serialized_path[-1]: + if endpoint.is_mapper: + serialized_path.append(endpoint.class_) + elif endpoint.is_aliased_class: + return False + + serialized.append( + ( + tuple(serialized_path) + + (obj.strategy or ()) + + (tuple([ + (key, obj.local_opts[key]) + for key in sorted(obj.local_opts) + ]) if obj.local_opts else ()) + ) + ) + if not serialized: + return None + else: + return tuple(serialized) + + def _generate(self): + cloned = super(Load, self)._generate() + cloned.local_opts = {} + return cloned + + is_opts_only = False + is_class_strategy = False + strategy = None + propagate_to_loaders = False + + def process_query(self, query): + self._process(query, True) + + def process_query_conditionally(self, query): + self._process(query, False) + + def _process(self, query, raiseerr): + current_path = query._current_path + if current_path: + for (token, start_path), loader in self.context.items(): + chopped_start_path = self._chop_path(start_path, current_path) + if chopped_start_path is not None: + query._attributes[(token, chopped_start_path)] = loader + else: + query._attributes.update(self.context) + + def _generate_path(self, path, attr, wildcard_key, raiseerr=True): + existing_of_type = self._of_type + self._of_type = None + + if raiseerr and not path.has_entity: + if isinstance(path, TokenRegistry): + raise sa_exc.ArgumentError( + "Wildcard token cannot be followed by another entity") + else: + raise sa_exc.ArgumentError( + "Attribute '%s' of entity '%s' does not " + "refer to a mapped entity" % + (path.prop.key, path.parent.entity) + ) + + if isinstance(attr, util.string_types): + default_token = attr.endswith(_DEFAULT_TOKEN) + if attr.endswith(_WILDCARD_TOKEN) or default_token: + if default_token: + self.propagate_to_loaders = False + if wildcard_key: + attr = "%s:%s" % (wildcard_key, attr) + path = path.token(attr) + self.path = path + return path + + if existing_of_type: + ent = inspect(existing_of_type) + else: + ent = path.entity + try: + # use getattr on the class to work around + # synonyms, hybrids, etc. + attr = getattr(ent.class_, attr) + except AttributeError: + if raiseerr: + raise sa_exc.ArgumentError( + "Can't find property named '%s' on the " + "mapped entity %s in this Query. " % ( + attr, ent) + ) + else: + return None + else: + attr = attr.property + + path = path[attr] + elif _is_mapped_class(attr): + if not attr.common_parent(path.mapper): + if raiseerr: + raise sa_exc.ArgumentError( + "Attribute '%s' does not " + "link from element '%s'" % (attr, path.entity)) + else: + return None + else: + prop = attr.property + + if not prop.parent.common_parent(path.mapper): + if raiseerr: + raise sa_exc.ArgumentError( + "Attribute '%s' does not " + "link from element '%s'" % (attr, path.entity)) + else: + return None + + if getattr(attr, '_of_type', None): + ac = attr._of_type + ext_info = of_type_info = inspect(ac) + + existing = path.entity_path[prop].get( + self.context, "path_with_polymorphic") + if not ext_info.is_aliased_class: + ac = orm_util.with_polymorphic( + ext_info.mapper.base_mapper, + ext_info.mapper, aliased=True, + _use_mapper_path=True, + _existing_alias=existing) + ext_info = inspect(ac) + elif not ext_info.with_polymorphic_mappers: + ext_info = orm_util.AliasedInsp( + ext_info.entity, + ext_info.mapper.base_mapper, + ext_info.selectable, + ext_info.name, + ext_info.with_polymorphic_mappers or [ext_info.mapper], + ext_info.polymorphic_on, + ext_info._base_alias, + ext_info._use_mapper_path, + ext_info._adapt_on_names, + ext_info.represents_outer_join + ) + + path.entity_path[prop].set( + self.context, "path_with_polymorphic", ext_info) + + # the path here will go into the context dictionary and + # needs to match up to how the class graph is traversed. + # so we can't put an AliasedInsp in the path here, needs + # to be the base mapper. + path = path[prop][ext_info.mapper] + + # but, we need to know what the original of_type() + # argument is for cache key purposes. so....store that too. + # it might be better for "path" to really represent, + # "the path", but trying to keep the impact of the cache + # key feature localized for now + self._of_type = of_type_info + else: + path = path[prop] + + if path.has_entity: + path = path.entity_path + self.path = path + return path + + def __str__(self): + return "Load(strategy=%r)" % (self.strategy, ) + + def _coerce_strat(self, strategy): + if strategy is not None: + strategy = tuple(sorted(strategy.items())) + return strategy + + @_generative + def set_relationship_strategy( + self, attr, strategy, propagate_to_loaders=True): + strategy = self._coerce_strat(strategy) + + self.is_class_strategy = False + self.propagate_to_loaders = propagate_to_loaders + # if the path is a wildcard, this will set propagate_to_loaders=False + self._generate_path(self.path, attr, "relationship") + self.strategy = strategy + if strategy is not None: + self._set_path_strategy() + + @_generative + def set_column_strategy(self, attrs, strategy, opts=None, opts_only=False): + strategy = self._coerce_strat(strategy) + + self.is_class_strategy = False + for attr in attrs: + cloned = self._generate() + cloned.strategy = strategy + cloned._generate_path(self.path, attr, "column") + cloned.propagate_to_loaders = True + if opts: + cloned.local_opts.update(opts) + if opts_only: + cloned.is_opts_only = True + cloned._set_path_strategy() + self.is_class_strategy = False + + @_generative + def set_generic_strategy(self, attrs, strategy): + strategy = self._coerce_strat(strategy) + + for attr in attrs: + path = self._generate_path(self.path, attr, None) + cloned = self._generate() + cloned.strategy = strategy + cloned.path = path + cloned.propagate_to_loaders = True + cloned._set_path_strategy() + + @_generative + def set_class_strategy(self, strategy, opts): + strategy = self._coerce_strat(strategy) + cloned = self._generate() + cloned.is_class_strategy = True + path = cloned._generate_path(self.path, None, None) + cloned.strategy = strategy + cloned.path = path + cloned.propagate_to_loaders = True + cloned._set_path_strategy() + cloned.local_opts.update(opts) + + def _set_for_path(self, context, path, replace=True, merge_opts=False): + if merge_opts or not replace: + existing = path.get(self.context, "loader") + + if existing: + if merge_opts: + existing.local_opts.update(self.local_opts) + else: + path.set(context, "loader", self) + else: + existing = path.get(self.context, "loader") + path.set(context, "loader", self) + if existing and existing.is_opts_only: + self.local_opts.update(existing.local_opts) + + def _set_path_strategy(self): + if not self.is_class_strategy and self.path.has_entity: + effective_path = self.path.parent + else: + effective_path = self.path + + self._set_for_path( + self.context, effective_path, replace=True, + merge_opts=self.is_opts_only) + + def __getstate__(self): + d = self.__dict__.copy() + d["path"] = self.path.serialize() + return d + + def __setstate__(self, state): + self.__dict__.update(state) + self.path = PathRegistry.deserialize(self.path) + + def _chop_path(self, to_chop, path): + i = -1 + + for i, (c_token, p_token) in enumerate(zip(to_chop, path.path)): + if isinstance(c_token, util.string_types): + # TODO: this is approximated from the _UnboundLoad + # version and probably has issues, not fully covered. + + if i == 0 and c_token.endswith(':' + _DEFAULT_TOKEN): + return to_chop + elif c_token != 'relationship:%s' % (_WILDCARD_TOKEN,) and \ + c_token != p_token.key: + return None + + if c_token is p_token: + continue + elif isinstance(c_token, InspectionAttr) and \ + c_token.is_mapper and p_token.is_mapper and \ + c_token.isa(p_token): + continue + else: + return None + return to_chop[i + 1:] + + +class _UnboundLoad(Load): + """Represent a loader option that isn't tied to a root entity. + + The loader option will produce an entity-linked :class:`.Load` + object when it is passed :metfh:`.Query.options`. + + This provides compatibility with the traditional system + of freestanding options, e.g. ``joinedload('x.y.z')``. + + """ + + def __init__(self): + self.path = () + self._to_bind = [] + self.local_opts = {} + + _is_chain_link = False + + def _generate_cache_key(self, path): + serialized = () + for val in self._to_bind: + for local_elem, val_elem in zip(self.path, val.path): + if local_elem is not val_elem: + break + else: + opt = val._bind_loader( + [path.path[0]], + None, None, False) + if opt: + c_key = opt._generate_cache_key(path) + if c_key is False: + return False + elif c_key: + serialized += c_key + if not serialized: + return None + else: + return serialized + + def _set_path_strategy(self): + self._to_bind.append(self) + + def _generate_path(self, path, attr, wildcard_key): + if wildcard_key and isinstance(attr, util.string_types) and \ + attr in (_WILDCARD_TOKEN, _DEFAULT_TOKEN): + if attr == _DEFAULT_TOKEN: + self.propagate_to_loaders = False + attr = "%s:%s" % (wildcard_key, attr) + if path and _is_mapped_class(path[-1]) and not self.is_class_strategy: + path = path[0:-1] + if attr: + path = path + (attr, ) + self.path = path + return path + + def __getstate__(self): + d = self.__dict__.copy() + d['path'] = self._serialize_path(self.path, filter_aliased_class=True) + return d + + def __setstate__(self, state): + ret = [] + for key in state['path']: + if isinstance(key, tuple): + if len(key) == 2: + # support legacy + cls, propkey = key + of_type = None + else: + cls, propkey, of_type = key + prop = getattr(cls, propkey) + if of_type: + prop = prop.of_type(of_type) + ret.append(prop) + else: + ret.append(key) + state['path'] = tuple(ret) + self.__dict__ = state + + def _process(self, query, raiseerr): + dedupes = query._attributes['_unbound_load_dedupes'] + for val in self._to_bind: + if val not in dedupes: + dedupes.add(val) + val._bind_loader( + [ent.entity_zero for ent in query._mapper_entities], + query._current_path, query._attributes, raiseerr) + + @classmethod + def _from_keys(cls, meth, keys, chained, kw): + opt = _UnboundLoad() + + def _split_key(key): + if isinstance(key, util.string_types): + # coerce fooload('*') into "default loader strategy" + if key == _WILDCARD_TOKEN: + return (_DEFAULT_TOKEN, ) + # coerce fooload(".*") into "wildcard on default entity" + elif key.startswith("." + _WILDCARD_TOKEN): + key = key[1:] + return key.split(".") + else: + return (key,) + all_tokens = [token for key in keys for token in _split_key(key)] + + for token in all_tokens[0:-1]: + if chained: + opt = meth(opt, token, **kw) + else: + opt = opt.defaultload(token) + opt._is_chain_link = True + + opt = meth(opt, all_tokens[-1], **kw) + opt._is_chain_link = False + + return opt + + def _chop_path(self, to_chop, path): + i = -1 + for i, (c_token, (p_entity, p_prop)) in enumerate( + zip(to_chop, path.pairs())): + if isinstance(c_token, util.string_types): + if i == 0 and c_token.endswith(':' + _DEFAULT_TOKEN): + return to_chop + elif c_token != 'relationship:%s' % ( + _WILDCARD_TOKEN,) and c_token != p_prop.key: + return None + elif isinstance(c_token, PropComparator): + if c_token.property is not p_prop or \ + ( + c_token._parententity is not p_entity and ( + not c_token._parententity.is_mapper or + not c_token._parententity.isa(p_entity) + ) + ): + return None + else: + i += 1 + + return to_chop[i:] + + def _serialize_path(self, path, filter_aliased_class=False): + ret = [] + for token in path: + if isinstance(token, QueryableAttribute): + if filter_aliased_class and token._of_type and \ + inspect(token._of_type).is_aliased_class: + ret.append( + (token._parentmapper.class_, + token.key, None)) + else: + ret.append( + (token._parentmapper.class_, token.key, + token._of_type)) + elif isinstance(token, PropComparator): + ret.append((token._parentmapper.class_, token.key, None)) + else: + ret.append(token) + return ret + + def _bind_loader(self, entities, current_path, context, raiseerr): + """Convert from an _UnboundLoad() object into a Load() object. + + The _UnboundLoad() uses an informal "path" and does not necessarily + refer to a lead entity as it may use string tokens. The Load() + OTOH refers to a complete path. This method reconciles from a + given Query into a Load. + + Example:: + + + query = session.query(User).options( + joinedload("orders").joinedload("items")) + + The above options will be an _UnboundLoad object along the lines + of (note this is not the exact API of _UnboundLoad):: + + _UnboundLoad( + _to_bind=[ + _UnboundLoad(["orders"], {"lazy": "joined"}), + _UnboundLoad(["orders", "items"], {"lazy": "joined"}), + ] + ) + + After this method, we get something more like this (again this is + not exact API):: + + Load( + User, + (User, User.orders.property)) + Load( + User, + (User, User.orders.property, Order, Order.items.property)) + + """ + + start_path = self.path + + if self.is_class_strategy and current_path: + start_path += (entities[0], ) + + # _current_path implies we're in a + # secondary load with an existing path + + if current_path: + start_path = self._chop_path(start_path, current_path) + + if not start_path: + return None + + # look at the first token and try to locate within the Query + # what entity we are referring towards. + token = start_path[0] + + if isinstance(token, util.string_types): + entity = self._find_entity_basestring( + entities, token, raiseerr) + elif isinstance(token, PropComparator): + prop = token.property + entity = self._find_entity_prop_comparator( + entities, + prop.key, + token._parententity, + raiseerr) + elif self.is_class_strategy and _is_mapped_class(token): + entity = inspect(token) + if entity not in entities: + entity = None + else: + raise sa_exc.ArgumentError( + "mapper option expects " + "string key or list of attributes") + + if not entity: + return + + path_element = entity + + # transfer our entity-less state into a Load() object + # with a real entity path. Start with the lead entity + # we just located, then go through the rest of our path + # tokens and populate into the Load(). + loader = Load(path_element) + if context is not None: + loader.context = context + else: + context = loader.context + + loader.strategy = self.strategy + loader.is_opts_only = self.is_opts_only + loader.is_class_strategy = self.is_class_strategy + + path = loader.path + + if not loader.is_class_strategy: + for token in start_path: + if not loader._generate_path( + loader.path, token, None, raiseerr): + return + + loader.local_opts.update(self.local_opts) + + if not loader.is_class_strategy and loader.path.has_entity: + effective_path = loader.path.parent + else: + effective_path = loader.path + + # prioritize "first class" options over those + # that were "links in the chain", e.g. "x" and "y" in + # someload("x.y.z") versus someload("x") / someload("x.y") + + if effective_path.is_token: + for path in effective_path.generate_for_superclasses(): + loader._set_for_path( + context, path, + replace=not self._is_chain_link, + merge_opts=self.is_opts_only) + else: + loader._set_for_path( + context, effective_path, + replace=not self._is_chain_link, + merge_opts=self.is_opts_only) + + return loader + + def _find_entity_prop_comparator(self, entities, token, mapper, raiseerr): + if _is_aliased_class(mapper): + searchfor = mapper + else: + searchfor = _class_to_mapper(mapper) + for ent in entities: + if orm_util._entity_corresponds_to(ent, searchfor): + return ent + else: + if raiseerr: + if not list(entities): + raise sa_exc.ArgumentError( + "Query has only expression-based entities - " + "can't find property named '%s'." + % (token, ) + ) + else: + raise sa_exc.ArgumentError( + "Can't find property '%s' on any entity " + "specified in this Query. Note the full path " + "from root (%s) to target entity must be specified." + % (token, ",".join(str(x) for + x in entities)) + ) + else: + return None + + def _find_entity_basestring(self, entities, token, raiseerr): + if token.endswith(':' + _WILDCARD_TOKEN): + if len(list(entities)) != 1: + if raiseerr: + raise sa_exc.ArgumentError( + "Wildcard loader can only be used with exactly " + "one entity. Use Load(ent) to specify " + "specific entities.") + elif token.endswith(_DEFAULT_TOKEN): + raiseerr = False + + for ent in entities: + # return only the first _MapperEntity when searching + # based on string prop name. Ideally object + # attributes are used to specify more exactly. + return ent + else: + if raiseerr: + raise sa_exc.ArgumentError( + "Query has only expression-based entities - " + "can't find property named '%s'." + % (token, ) + ) + else: + return None + + +class loader_option(object): + def __init__(self): + pass + + def __call__(self, fn): + self.name = name = fn.__name__ + self.fn = fn + if hasattr(Load, name): + raise TypeError("Load class already has a %s method." % (name)) + setattr(Load, name, fn) + + return self + + def _add_unbound_fn(self, fn): + self._unbound_fn = fn + fn_doc = self.fn.__doc__ + self.fn.__doc__ = """Produce a new :class:`.Load` object with the +:func:`.orm.%(name)s` option applied. + +See :func:`.orm.%(name)s` for usage examples. + +""" % {"name": self.name} + + fn.__doc__ = fn_doc + return self + + def _add_unbound_all_fn(self, fn): + self._unbound_all_fn = fn + fn.__doc__ = """Produce a standalone "all" option for :func:`.orm.%(name)s`. + +.. deprecated:: 0.9.0 + + The "_all()" style is replaced by method chaining, e.g.:: + + session.query(MyClass).options( + %(name)s("someattribute").%(name)s("anotherattribute") + ) + +""" % {"name": self.name} + return self + + +@loader_option() +def contains_eager(loadopt, attr, alias=None): + r"""Indicate that the given attribute should be eagerly loaded from + columns stated manually in the query. + + This function is part of the :class:`.Load` interface and supports + both method-chained and standalone operation. + + The option is used in conjunction with an explicit join that loads + the desired rows, i.e.:: + + sess.query(Order).\ + join(Order.user).\ + options(contains_eager(Order.user)) + + The above query would join from the ``Order`` entity to its related + ``User`` entity, and the returned ``Order`` objects would have the + ``Order.user`` attribute pre-populated. + + :func:`.contains_eager` also accepts an `alias` argument, which is the + string name of an alias, an :func:`~sqlalchemy.sql.expression.alias` + construct, or an :func:`~sqlalchemy.orm.aliased` construct. Use this when + the eagerly-loaded rows are to come from an aliased table:: + + user_alias = aliased(User) + sess.query(Order).\ + join((user_alias, Order.user)).\ + options(contains_eager(Order.user, alias=user_alias)) + + When using :func:`.contains_eager` in conjunction with inherited + subclasses, the :meth:`.RelationshipProperty.of_type` modifier should + also be used in order to set up the pathing properly:: + + sess.query(Company).\ + outerjoin(Company.employees.of_type(Manager)).\ + options( + contains_eager( + Company.employees.of_type(Manager), + alias=Manager) + ) + + .. seealso:: + + :ref:`loading_toplevel` + + :ref:`contains_eager` + + """ + if alias is not None: + if not isinstance(alias, str): + info = inspect(alias) + alias = info.selectable + + elif getattr(attr, '_of_type', None): + ot = inspect(attr._of_type) + alias = ot.selectable + + cloned = loadopt.set_relationship_strategy( + attr, + {"lazy": "joined"}, + propagate_to_loaders=False + ) + cloned.local_opts['eager_from_alias'] = alias + return cloned + + +@contains_eager._add_unbound_fn +def contains_eager(*keys, **kw): + return _UnboundLoad()._from_keys( + _UnboundLoad.contains_eager, keys, True, kw) + + +@loader_option() +def load_only(loadopt, *attrs): + """Indicate that for a particular entity, only the given list + of column-based attribute names should be loaded; all others will be + deferred. + + This function is part of the :class:`.Load` interface and supports + both method-chained and standalone operation. + + Example - given a class ``User``, load only the ``name`` and ``fullname`` + attributes:: + + session.query(User).options(load_only("name", "fullname")) + + Example - given a relationship ``User.addresses -> Address``, specify + subquery loading for the ``User.addresses`` collection, but on each + ``Address`` object load only the ``email_address`` attribute:: + + session.query(User).options( + subqueryload("addresses").load_only("email_address") + ) + + For a :class:`.Query` that has multiple entities, the lead entity can be + specifically referred to using the :class:`.Load` constructor:: + + session.query(User, Address).join(User.addresses).options( + Load(User).load_only("name", "fullname"), + Load(Address).load_only("email_addres") + ) + + + .. versionadded:: 0.9.0 + + """ + cloned = loadopt.set_column_strategy( + attrs, + {"deferred": False, "instrument": True} + ) + cloned.set_column_strategy("*", + {"deferred": True, "instrument": True}, + {"undefer_pks": True}) + return cloned + + +@load_only._add_unbound_fn +def load_only(*attrs): + return _UnboundLoad().load_only(*attrs) + + +@loader_option() +def joinedload(loadopt, attr, innerjoin=None): + """Indicate that the given attribute should be loaded using joined + eager loading. + + This function is part of the :class:`.Load` interface and supports + both method-chained and standalone operation. + + examples:: + + # joined-load the "orders" collection on "User" + query(User).options(joinedload(User.orders)) + + # joined-load Order.items and then Item.keywords + query(Order).options( + joinedload(Order.items).joinedload(Item.keywords)) + + # lazily load Order.items, but when Items are loaded, + # joined-load the keywords collection + query(Order).options( + lazyload(Order.items).joinedload(Item.keywords)) + + :param innerjoin: if ``True``, indicates that the joined eager load should + use an inner join instead of the default of left outer join:: + + query(Order).options(joinedload(Order.user, innerjoin=True)) + + In order to chain multiple eager joins together where some may be + OUTER and others INNER, right-nested joins are used to link them:: + + query(A).options( + joinedload(A.bs, innerjoin=False). + joinedload(B.cs, innerjoin=True) + ) + + The above query, linking A.bs via "outer" join and B.cs via "inner" join + would render the joins as "a LEFT OUTER JOIN (b JOIN c)". When using + older versions of SQLite (< 3.7.16), this form of JOIN is translated to + use full subqueries as this syntax is otherwise not directly supported. + + The ``innerjoin`` flag can also be stated with the term ``"unnested"``. + This indicates that an INNER JOIN should be used, *unless* the join + is linked to a LEFT OUTER JOIN to the left, in which case it + will render as LEFT OUTER JOIN. For example, supposing ``A.bs`` + is an outerjoin:: + + query(A).options( + joinedload(A.bs). + joinedload(B.cs, innerjoin="unnested") + ) + + The above join will render as "a LEFT OUTER JOIN b LEFT OUTER JOIN c", + rather than as "a LEFT OUTER JOIN (b JOIN c)". + + .. note:: The "unnested" flag does **not** affect the JOIN rendered + from a many-to-many association table, e.g. a table configured + as :paramref:`.relationship.secondary`, to the target table; for + correctness of results, these joins are always INNER and are + therefore right-nested if linked to an OUTER join. + + .. versionchanged:: 1.0.0 ``innerjoin=True`` now implies + ``innerjoin="nested"``, whereas in 0.9 it implied + ``innerjoin="unnested"``. In order to achieve the pre-1.0 "unnested" + inner join behavior, use the value ``innerjoin="unnested"``. + See :ref:`migration_3008`. + + .. note:: + + The joins produced by :func:`.orm.joinedload` are **anonymously + aliased**. The criteria by which the join proceeds cannot be + modified, nor can the :class:`.Query` refer to these joins in any way, + including ordering. See :ref:`zen_of_eager_loading` for further + detail. + + To produce a specific SQL JOIN which is explicitly available, use + :meth:`.Query.join`. To combine explicit JOINs with eager loading + of collections, use :func:`.orm.contains_eager`; see + :ref:`contains_eager`. + + .. seealso:: + + :ref:`loading_toplevel` + + :ref:`joined_eager_loading` + + """ + loader = loadopt.set_relationship_strategy(attr, {"lazy": "joined"}) + if innerjoin is not None: + loader.local_opts['innerjoin'] = innerjoin + return loader + + +@joinedload._add_unbound_fn +def joinedload(*keys, **kw): + return _UnboundLoad._from_keys( + _UnboundLoad.joinedload, keys, False, kw) + + +@joinedload._add_unbound_all_fn +def joinedload_all(*keys, **kw): + return _UnboundLoad._from_keys( + _UnboundLoad.joinedload, keys, True, kw) + + +@loader_option() +def subqueryload(loadopt, attr): + """Indicate that the given attribute should be loaded using + subquery eager loading. + + This function is part of the :class:`.Load` interface and supports + both method-chained and standalone operation. + + examples:: + + # subquery-load the "orders" collection on "User" + query(User).options(subqueryload(User.orders)) + + # subquery-load Order.items and then Item.keywords + query(Order).options( + subqueryload(Order.items).subqueryload(Item.keywords)) + + # lazily load Order.items, but when Items are loaded, + # subquery-load the keywords collection + query(Order).options( + lazyload(Order.items).subqueryload(Item.keywords)) + + + .. seealso:: + + :ref:`loading_toplevel` + + :ref:`subquery_eager_loading` + + """ + return loadopt.set_relationship_strategy(attr, {"lazy": "subquery"}) + + +@subqueryload._add_unbound_fn +def subqueryload(*keys): + return _UnboundLoad._from_keys(_UnboundLoad.subqueryload, keys, False, {}) + + +@subqueryload._add_unbound_all_fn +def subqueryload_all(*keys): + return _UnboundLoad._from_keys(_UnboundLoad.subqueryload, keys, True, {}) + + +@loader_option() +def selectinload(loadopt, attr): + """Indicate that the given attribute should be loaded using + SELECT IN eager loading. + + This function is part of the :class:`.Load` interface and supports + both method-chained and standalone operation. + + examples:: + + # selectin-load the "orders" collection on "User" + query(User).options(selectinload(User.orders)) + + # selectin-load Order.items and then Item.keywords + query(Order).options( + selectinload(Order.items).selectinload(Item.keywords)) + + # lazily load Order.items, but when Items are loaded, + # selectin-load the keywords collection + query(Order).options( + lazyload(Order.items).selectinload(Item.keywords)) + + .. versionadded:: 1.2 + + .. seealso:: + + :ref:`loading_toplevel` + + :ref:`selectin_eager_loading` + + """ + return loadopt.set_relationship_strategy(attr, {"lazy": "selectin"}) + + +@selectinload._add_unbound_fn +def selectinload(*keys): + return _UnboundLoad._from_keys(_UnboundLoad.selectinload, keys, False, {}) + + +@selectinload._add_unbound_all_fn +def selectinload_all(*keys): + return _UnboundLoad._from_keys(_UnboundLoad.selectinload, keys, True, {}) + + +@loader_option() +def lazyload(loadopt, attr): + """Indicate that the given attribute should be loaded using "lazy" + loading. + + This function is part of the :class:`.Load` interface and supports + both method-chained and standalone operation. + + .. seealso:: + + :ref:`loading_toplevel` + + :ref:`lazy_loading` + + """ + return loadopt.set_relationship_strategy(attr, {"lazy": "select"}) + + +@lazyload._add_unbound_fn +def lazyload(*keys): + return _UnboundLoad._from_keys(_UnboundLoad.lazyload, keys, False, {}) + + +@lazyload._add_unbound_all_fn +def lazyload_all(*keys): + return _UnboundLoad._from_keys(_UnboundLoad.lazyload, keys, True, {}) + + +@loader_option() +def immediateload(loadopt, attr): + """Indicate that the given attribute should be loaded using + an immediate load with a per-attribute SELECT statement. + + The :func:`.immediateload` option is superseded in general + by the :func:`.selectinload` option, which performs the same task + more efficiently by emitting a SELECT for all loaded objects. + + This function is part of the :class:`.Load` interface and supports + both method-chained and standalone operation. + + .. seealso:: + + :ref:`loading_toplevel` + + :ref:`selectin_eager_loading` + + """ + loader = loadopt.set_relationship_strategy(attr, {"lazy": "immediate"}) + return loader + + +@immediateload._add_unbound_fn +def immediateload(*keys): + return _UnboundLoad._from_keys( + _UnboundLoad.immediateload, keys, False, {}) + + +@loader_option() +def noload(loadopt, attr): + """Indicate that the given relationship attribute should remain unloaded. + + This function is part of the :class:`.Load` interface and supports + both method-chained and standalone operation. + + :func:`.orm.noload` applies to :func:`.relationship` attributes; for + column-based attributes, see :func:`.orm.defer`. + + .. seealso:: + + :ref:`loading_toplevel` + + """ + + return loadopt.set_relationship_strategy(attr, {"lazy": "noload"}) + + +@noload._add_unbound_fn +def noload(*keys): + return _UnboundLoad._from_keys(_UnboundLoad.noload, keys, False, {}) + + +@loader_option() +def raiseload(loadopt, attr, sql_only=False): + """Indicate that the given relationship attribute should disallow lazy loads. + + A relationship attribute configured with :func:`.orm.raiseload` will + raise an :exc:`~sqlalchemy.exc.InvalidRequestError` upon access. The + typical way this is useful is when an application is attempting to ensure + that all relationship attributes that are accessed in a particular context + would have been already loaded via eager loading. Instead of having + to read through SQL logs to ensure lazy loads aren't occurring, this + strategy will cause them to raise immediately. + + :param sql_only: if True, raise only if the lazy load would emit SQL, + but not if it is only checking the identity map, or determining that + the related value should just be None due to missing keys. When False, + the strategy will raise for all varieties of lazyload. + + This function is part of the :class:`.Load` interface and supports + both method-chained and standalone operation. + + :func:`.orm.raiseload` applies to :func:`.relationship` attributes only. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`loading_toplevel` + + :ref:`prevent_lazy_with_raiseload` + + """ + + return loadopt.set_relationship_strategy( + attr, {"lazy": "raise_on_sql" if sql_only else "raise"}) + + +@raiseload._add_unbound_fn +def raiseload(*keys, **kw): + return _UnboundLoad._from_keys(_UnboundLoad.raiseload, keys, False, kw) + + +@loader_option() +def defaultload(loadopt, attr): + """Indicate an attribute should load using its default loader style. + + This method is used to link to other loader options further into + a chain of attributes without altering the loader style of the links + along the chain. For example, to set joined eager loading for an + element of an element:: + + session.query(MyClass).options( + defaultload(MyClass.someattribute). + joinedload(MyOtherClass.someotherattribute) + ) + + :func:`.defaultload` is also useful for setting column-level options + on a related class, namely that of :func:`.defer` and :func:`.undefer`:: + + session.query(MyClass).options( + defaultload(MyClass.someattribute). + defer("some_column"). + undefer("some_other_column") + ) + + .. seealso:: + + :ref:`relationship_loader_options` + + :ref:`deferred_loading_w_multiple` + + """ + return loadopt.set_relationship_strategy( + attr, + None + ) + + +@defaultload._add_unbound_fn +def defaultload(*keys): + return _UnboundLoad._from_keys(_UnboundLoad.defaultload, keys, False, {}) + + +@loader_option() +def defer(loadopt, key): + r"""Indicate that the given column-oriented attribute should be deferred, e.g. + not loaded until accessed. + + This function is part of the :class:`.Load` interface and supports + both method-chained and standalone operation. + + e.g.:: + + from sqlalchemy.orm import defer + + session.query(MyClass).options( + defer("attribute_one"), + defer("attribute_two")) + + session.query(MyClass).options( + defer(MyClass.attribute_one), + defer(MyClass.attribute_two)) + + To specify a deferred load of an attribute on a related class, + the path can be specified one token at a time, specifying the loading + style for each link along the chain. To leave the loading style + for a link unchanged, use :func:`.orm.defaultload`:: + + session.query(MyClass).options(defaultload("someattr").defer("some_column")) + + A :class:`.Load` object that is present on a certain path can have + :meth:`.Load.defer` called multiple times, each will operate on the same + parent entity:: + + + session.query(MyClass).options( + defaultload("someattr"). + defer("some_column"). + defer("some_other_column"). + defer("another_column") + ) + + :param key: Attribute to be deferred. + + :param \*addl_attrs: Deprecated; this option supports the old 0.8 style + of specifying a path as a series of attributes, which is now superseded + by the method-chained style. + + .. seealso:: + + :ref:`deferred` + + :func:`.orm.undefer` + + """ + return loadopt.set_column_strategy( + (key, ), + {"deferred": True, "instrument": True} + ) + + +@defer._add_unbound_fn +def defer(key, *addl_attrs): + return _UnboundLoad._from_keys( + _UnboundLoad.defer, (key, ) + addl_attrs, False, {}) + + +@loader_option() +def undefer(loadopt, key): + r"""Indicate that the given column-oriented attribute should be undeferred, + e.g. specified within the SELECT statement of the entity as a whole. + + The column being undeferred is typically set up on the mapping as a + :func:`.deferred` attribute. + + This function is part of the :class:`.Load` interface and supports + both method-chained and standalone operation. + + Examples:: + + # undefer two columns + session.query(MyClass).options(undefer("col1"), undefer("col2")) + + # undefer all columns specific to a single class using Load + * + session.query(MyClass, MyOtherClass).options( + Load(MyClass).undefer("*")) + + :param key: Attribute to be undeferred. + + :param \*addl_attrs: Deprecated; this option supports the old 0.8 style + of specifying a path as a series of attributes, which is now superseded + by the method-chained style. + + .. seealso:: + + :ref:`deferred` + + :func:`.orm.defer` + + :func:`.orm.undefer_group` + + """ + return loadopt.set_column_strategy( + (key, ), + {"deferred": False, "instrument": True} + ) + + +@undefer._add_unbound_fn +def undefer(key, *addl_attrs): + return _UnboundLoad._from_keys( + _UnboundLoad.undefer, (key, ) + addl_attrs, False, {}) + + +@loader_option() +def undefer_group(loadopt, name): + """Indicate that columns within the given deferred group name should be + undeferred. + + The columns being undeferred are set up on the mapping as + :func:`.deferred` attributes and include a "group" name. + + E.g:: + + session.query(MyClass).options(undefer_group("large_attrs")) + + To undefer a group of attributes on a related entity, the path can be + spelled out using relationship loader options, such as + :func:`.orm.defaultload`:: + + session.query(MyClass).options( + defaultload("someattr").undefer_group("large_attrs")) + + .. versionchanged:: 0.9.0 :func:`.orm.undefer_group` is now specific to a + particiular entity load path. + + .. seealso:: + + :ref:`deferred` + + :func:`.orm.defer` + + :func:`.orm.undefer` + + """ + return loadopt.set_column_strategy( + "*", + None, + {"undefer_group_%s" % name: True}, + opts_only=True + ) + + +@undefer_group._add_unbound_fn +def undefer_group(name): + return _UnboundLoad().undefer_group(name) + + +from ..sql import expression as sql_expr +from .util import _orm_full_deannotate + + +@loader_option() +def with_expression(loadopt, key, expression): + r"""Apply an ad-hoc SQL expression to a "deferred expression" attribute. + + This option is used in conjunction with the :func:`.orm.query_expression` + mapper-level construct that indicates an attribute which should be the + target of an ad-hoc SQL expression. + + E.g.:: + + + sess.query(SomeClass).options( + with_expression(SomeClass.x_y_expr, SomeClass.x + SomeClass.y) + ) + + .. versionadded:: 1.2 + + :param key: Attribute to be undeferred. + + :param expr: SQL expression to be applied to the attribute. + + .. seealso:: + + :ref:`mapper_query_expression` + + """ + + expression = sql_expr._labeled( + _orm_full_deannotate(expression)) + + return loadopt.set_column_strategy( + (key, ), + {"query_expression": True}, + opts={"expression": expression} + ) + + +@with_expression._add_unbound_fn +def with_expression(key, expression): + return _UnboundLoad._from_keys( + _UnboundLoad.with_expression, (key, ), + False, {"expression": expression}) + + +@loader_option() +def selectin_polymorphic(loadopt, classes): + """Indicate an eager load should take place for all attributes + specific to a subclass. + + This uses an additional SELECT with IN against all matched primary + key values, and is the per-query analogue to the ``"selectin"`` + setting on the :paramref:`.mapper.polymorphic_load` parameter. + + .. versionadded:: 1.2 + + .. seealso:: + + :ref:`inheritance_polymorphic_load` + + """ + loadopt.set_class_strategy( + {"selectinload_polymorphic": True}, + opts={"entities": tuple(sorted((inspect(cls) for cls in classes), key=id))} + ) + return loadopt + + +@selectin_polymorphic._add_unbound_fn +def selectin_polymorphic(base_cls, classes): + ul = _UnboundLoad() + ul.is_class_strategy = True + ul.path = (inspect(base_cls), ) + ul.selectin_polymorphic( + classes + ) + return ul diff --git a/venv/Lib/site-packages/sqlalchemy/orm/sync.py b/venv/Lib/site-packages/sqlalchemy/orm/sync.py new file mode 100644 index 0000000..08a66a8 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/sync.py @@ -0,0 +1,140 @@ +# orm/sync.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""private module containing functions used for copying data +between instances based on join conditions. + +""" + +from . import exc, util as orm_util, attributes + + +def populate(source, source_mapper, dest, dest_mapper, + synchronize_pairs, uowcommit, flag_cascaded_pks): + source_dict = source.dict + dest_dict = dest.dict + + for l, r in synchronize_pairs: + try: + # inline of source_mapper._get_state_attr_by_column + prop = source_mapper._columntoproperty[l] + value = source.manager[prop.key].impl.get(source, source_dict, + attributes.PASSIVE_OFF) + except exc.UnmappedColumnError: + _raise_col_to_prop(False, source_mapper, l, dest_mapper, r) + + try: + # inline of dest_mapper._set_state_attr_by_column + prop = dest_mapper._columntoproperty[r] + dest.manager[prop.key].impl.set(dest, dest_dict, value, None) + except exc.UnmappedColumnError: + _raise_col_to_prop(True, source_mapper, l, dest_mapper, r) + + # technically the "r.primary_key" check isn't + # needed here, but we check for this condition to limit + # how often this logic is invoked for memory/performance + # reasons, since we only need this info for a primary key + # destination. + if flag_cascaded_pks and l.primary_key and \ + r.primary_key and \ + r.references(l): + uowcommit.attributes[("pk_cascaded", dest, r)] = True + + +def bulk_populate_inherit_keys( + source_dict, source_mapper, synchronize_pairs): + # a simplified version of populate() used by bulk insert mode + for l, r in synchronize_pairs: + try: + prop = source_mapper._columntoproperty[l] + value = source_dict[prop.key] + except exc.UnmappedColumnError: + _raise_col_to_prop(False, source_mapper, l, source_mapper, r) + + try: + prop = source_mapper._columntoproperty[r] + source_dict[prop.key] = value + except exc.UnmappedColumnError: + _raise_col_to_prop(True, source_mapper, l, source_mapper, r) + + +def clear(dest, dest_mapper, synchronize_pairs): + for l, r in synchronize_pairs: + if r.primary_key and \ + dest_mapper._get_state_attr_by_column( + dest, dest.dict, r) not in orm_util._none_set: + + raise AssertionError( + "Dependency rule tried to blank-out primary key " + "column '%s' on instance '%s'" % + (r, orm_util.state_str(dest)) + ) + try: + dest_mapper._set_state_attr_by_column(dest, dest.dict, r, None) + except exc.UnmappedColumnError: + _raise_col_to_prop(True, None, l, dest_mapper, r) + + +def update(source, source_mapper, dest, old_prefix, synchronize_pairs): + for l, r in synchronize_pairs: + try: + oldvalue = source_mapper._get_committed_attr_by_column( + source.obj(), l) + value = source_mapper._get_state_attr_by_column( + source, source.dict, l, passive=attributes.PASSIVE_OFF) + except exc.UnmappedColumnError: + _raise_col_to_prop(False, source_mapper, l, None, r) + dest[r.key] = value + dest[old_prefix + r.key] = oldvalue + + +def populate_dict(source, source_mapper, dict_, synchronize_pairs): + for l, r in synchronize_pairs: + try: + value = source_mapper._get_state_attr_by_column( + source, source.dict, l, passive=attributes.PASSIVE_OFF) + except exc.UnmappedColumnError: + _raise_col_to_prop(False, source_mapper, l, None, r) + + dict_[r.key] = value + + +def source_modified(uowcommit, source, source_mapper, synchronize_pairs): + """return true if the source object has changes from an old to a + new value on the given synchronize pairs + + """ + for l, r in synchronize_pairs: + try: + prop = source_mapper._columntoproperty[l] + except exc.UnmappedColumnError: + _raise_col_to_prop(False, source_mapper, l, None, r) + history = uowcommit.get_attribute_history( + source, prop.key, attributes.PASSIVE_NO_INITIALIZE) + if bool(history.deleted): + return True + else: + return False + + +def _raise_col_to_prop(isdest, source_mapper, source_column, + dest_mapper, dest_column): + if isdest: + raise exc.UnmappedColumnError( + "Can't execute sync rule for " + "destination column '%s'; mapper '%s' does not map " + "this column. Try using an explicit `foreign_keys` " + "collection which does not include this column (or use " + "a viewonly=True relation)." % (dest_column, dest_mapper)) + else: + raise exc.UnmappedColumnError( + "Can't execute sync rule for " + "source column '%s'; mapper '%s' does not map this " + "column. Try using an explicit `foreign_keys` " + "collection which does not include destination column " + "'%s' (or use a viewonly=True relation)." % + (source_column, source_mapper, dest_column)) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/unitofwork.py b/venv/Lib/site-packages/sqlalchemy/orm/unitofwork.py new file mode 100644 index 0000000..ae5cee8 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/unitofwork.py @@ -0,0 +1,704 @@ +# orm/unitofwork.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""The internals for the unit of work system. + +The session's flush() process passes objects to a contextual object +here, which assembles flush tasks based on mappers and their properties, +organizes them in order of dependency, and executes. + +""" + +from .. import util, event +from ..util import topological +from . import attributes, persistence, util as orm_util +from . import exc as orm_exc +import itertools + + +def track_cascade_events(descriptor, prop): + """Establish event listeners on object attributes which handle + cascade-on-set/append. + + """ + key = prop.key + + def append(state, item, initiator): + # process "save_update" cascade rules for when + # an instance is appended to the list of another instance + + if item is None: + return + + sess = state.session + if sess: + if sess._warn_on_events: + sess._flush_warning("collection append") + + prop = state.manager.mapper._props[key] + item_state = attributes.instance_state(item) + if prop._cascade.save_update and \ + (prop.cascade_backrefs or key == initiator.key) and \ + not sess._contains_state(item_state): + sess._save_or_update_state(item_state) + return item + + def remove(state, item, initiator): + if item is None: + return + + sess = state.session + + prop = state.manager.mapper._props[key] + + if sess and sess._warn_on_events: + sess._flush_warning( + "collection remove" + if prop.uselist + else "related attribute delete") + + # expunge pending orphans + item_state = attributes.instance_state(item) + + if prop._cascade.delete_orphan and \ + prop.mapper._is_orphan(item_state): + if sess and item_state in sess._new: + sess.expunge(item) + else: + # the related item may or may not itself be in a + # Session, however the parent for which we are catching + # the event is not in a session, so memoize this on the + # item + item_state._orphaned_outside_of_session = True + + def set_(state, newvalue, oldvalue, initiator): + # process "save_update" cascade rules for when an instance + # is attached to another instance + if oldvalue is newvalue: + return newvalue + + sess = state.session + if sess: + + if sess._warn_on_events: + sess._flush_warning("related attribute set") + + prop = state.manager.mapper._props[key] + if newvalue is not None: + newvalue_state = attributes.instance_state(newvalue) + if prop._cascade.save_update and \ + (prop.cascade_backrefs or key == initiator.key) and \ + not sess._contains_state(newvalue_state): + sess._save_or_update_state(newvalue_state) + + if oldvalue is not None and \ + oldvalue is not attributes.NEVER_SET and \ + oldvalue is not attributes.PASSIVE_NO_RESULT and \ + prop._cascade.delete_orphan: + # possible to reach here with attributes.NEVER_SET ? + oldvalue_state = attributes.instance_state(oldvalue) + + if oldvalue_state in sess._new and \ + prop.mapper._is_orphan(oldvalue_state): + sess.expunge(oldvalue) + return newvalue + + event.listen(descriptor, 'append', append, raw=True, retval=True) + event.listen(descriptor, 'remove', remove, raw=True, retval=True) + event.listen(descriptor, 'set', set_, raw=True, retval=True) + + +class UOWTransaction(object): + def __init__(self, session): + self.session = session + + # dictionary used by external actors to + # store arbitrary state information. + self.attributes = {} + + # dictionary of mappers to sets of + # DependencyProcessors, which are also + # set to be part of the sorted flush actions, + # which have that mapper as a parent. + self.deps = util.defaultdict(set) + + # dictionary of mappers to sets of InstanceState + # items pending for flush which have that mapper + # as a parent. + self.mappers = util.defaultdict(set) + + # a dictionary of Preprocess objects, which gather + # additional states impacted by the flush + # and determine if a flush action is needed + self.presort_actions = {} + + # dictionary of PostSortRec objects, each + # one issues work during the flush within + # a certain ordering. + self.postsort_actions = {} + + # a set of 2-tuples, each containing two + # PostSortRec objects where the second + # is dependent on the first being executed + # first + self.dependencies = set() + + # dictionary of InstanceState-> (isdelete, listonly) + # tuples, indicating if this state is to be deleted + # or insert/updated, or just refreshed + self.states = {} + + # tracks InstanceStates which will be receiving + # a "post update" call. Keys are mappers, + # values are a set of states and a set of the + # columns which should be included in the update. + self.post_update_states = util.defaultdict(lambda: (set(), set())) + + @property + def has_work(self): + return bool(self.states) + + def was_already_deleted(self, state): + """return true if the given state is expired and was deleted + previously. + """ + if state.expired: + try: + state._load_expired(state, attributes.PASSIVE_OFF) + except orm_exc.ObjectDeletedError: + self.session._remove_newly_deleted([state]) + return True + return False + + def is_deleted(self, state): + """return true if the given state is marked as deleted + within this uowtransaction.""" + + return state in self.states and self.states[state][0] + + def memo(self, key, callable_): + if key in self.attributes: + return self.attributes[key] + else: + self.attributes[key] = ret = callable_() + return ret + + def remove_state_actions(self, state): + """remove pending actions for a state from the uowtransaction.""" + + isdelete = self.states[state][0] + + self.states[state] = (isdelete, True) + + def get_attribute_history(self, state, key, + passive=attributes.PASSIVE_NO_INITIALIZE): + """facade to attributes.get_state_history(), including + caching of results.""" + + hashkey = ("history", state, key) + + # cache the objects, not the states; the strong reference here + # prevents newly loaded objects from being dereferenced during the + # flush process + + if hashkey in self.attributes: + history, state_history, cached_passive = self.attributes[hashkey] + # if the cached lookup was "passive" and now + # we want non-passive, do a non-passive lookup and re-cache + + if not cached_passive & attributes.SQL_OK \ + and passive & attributes.SQL_OK: + impl = state.manager[key].impl + history = impl.get_history(state, state.dict, + attributes.PASSIVE_OFF | + attributes.LOAD_AGAINST_COMMITTED) + if history and impl.uses_objects: + state_history = history.as_state() + else: + state_history = history + self.attributes[hashkey] = (history, state_history, passive) + else: + impl = state.manager[key].impl + # TODO: store the history as (state, object) tuples + # so we don't have to keep converting here + history = impl.get_history(state, state.dict, passive | + attributes.LOAD_AGAINST_COMMITTED) + if history and impl.uses_objects: + state_history = history.as_state() + else: + state_history = history + self.attributes[hashkey] = (history, state_history, + passive) + + return state_history + + def has_dep(self, processor): + return (processor, True) in self.presort_actions + + def register_preprocessor(self, processor, fromparent): + key = (processor, fromparent) + if key not in self.presort_actions: + self.presort_actions[key] = Preprocess(processor, fromparent) + + def register_object(self, state, isdelete=False, + listonly=False, cancel_delete=False, + operation=None, prop=None): + if not self.session._contains_state(state): + # this condition is normal when objects are registered + # as part of a relationship cascade operation. it should + # not occur for the top-level register from Session.flush(). + if not state.deleted and operation is not None: + util.warn("Object of type %s not in session, %s operation " + "along '%s' will not proceed" % + (orm_util.state_class_str(state), operation, prop)) + return False + + if state not in self.states: + mapper = state.manager.mapper + + if mapper not in self.mappers: + self._per_mapper_flush_actions(mapper) + + self.mappers[mapper].add(state) + self.states[state] = (isdelete, listonly) + else: + if not listonly and (isdelete or cancel_delete): + self.states[state] = (isdelete, False) + return True + + def register_post_update(self, state, post_update_cols): + mapper = state.manager.mapper.base_mapper + states, cols = self.post_update_states[mapper] + states.add(state) + cols.update(post_update_cols) + + def _per_mapper_flush_actions(self, mapper): + saves = SaveUpdateAll(self, mapper.base_mapper) + deletes = DeleteAll(self, mapper.base_mapper) + self.dependencies.add((saves, deletes)) + + for dep in mapper._dependency_processors: + dep.per_property_preprocessors(self) + + for prop in mapper.relationships: + if prop.viewonly: + continue + dep = prop._dependency_processor + dep.per_property_preprocessors(self) + + @util.memoized_property + def _mapper_for_dep(self): + """return a dynamic mapping of (Mapper, DependencyProcessor) to + True or False, indicating if the DependencyProcessor operates + on objects of that Mapper. + + The result is stored in the dictionary persistently once + calculated. + + """ + return util.PopulateDict( + lambda tup: tup[0]._props.get(tup[1].key) is tup[1].prop + ) + + def filter_states_for_dep(self, dep, states): + """Filter the given list of InstanceStates to those relevant to the + given DependencyProcessor. + + """ + mapper_for_dep = self._mapper_for_dep + return [s for s in states if mapper_for_dep[(s.manager.mapper, dep)]] + + def states_for_mapper_hierarchy(self, mapper, isdelete, listonly): + checktup = (isdelete, listonly) + for mapper in mapper.base_mapper.self_and_descendants: + for state in self.mappers[mapper]: + if self.states[state] == checktup: + yield state + + def _generate_actions(self): + """Generate the full, unsorted collection of PostSortRecs as + well as dependency pairs for this UOWTransaction. + + """ + # execute presort_actions, until all states + # have been processed. a presort_action might + # add new states to the uow. + while True: + ret = False + for action in list(self.presort_actions.values()): + if action.execute(self): + ret = True + if not ret: + break + + # see if the graph of mapper dependencies has cycles. + self.cycles = cycles = topological.find_cycles( + self.dependencies, + list(self.postsort_actions.values())) + + if cycles: + # if yes, break the per-mapper actions into + # per-state actions + convert = dict( + (rec, set(rec.per_state_flush_actions(self))) + for rec in cycles + ) + + # rewrite the existing dependencies to point to + # the per-state actions for those per-mapper actions + # that were broken up. + for edge in list(self.dependencies): + if None in edge or \ + edge[0].disabled or edge[1].disabled or \ + cycles.issuperset(edge): + self.dependencies.remove(edge) + elif edge[0] in cycles: + self.dependencies.remove(edge) + for dep in convert[edge[0]]: + self.dependencies.add((dep, edge[1])) + elif edge[1] in cycles: + self.dependencies.remove(edge) + for dep in convert[edge[1]]: + self.dependencies.add((edge[0], dep)) + + return set([a for a in self.postsort_actions.values() + if not a.disabled + ] + ).difference(cycles) + + def execute(self): + postsort_actions = self._generate_actions() + + # sort = topological.sort(self.dependencies, postsort_actions) + # print "--------------" + # print "\ndependencies:", self.dependencies + # print "\ncycles:", self.cycles + # print "\nsort:", list(sort) + # print "\nCOUNT OF POSTSORT ACTIONS", len(postsort_actions) + + # execute + if self.cycles: + for set_ in topological.sort_as_subsets( + self.dependencies, + postsort_actions): + while set_: + n = set_.pop() + n.execute_aggregate(self, set_) + else: + for rec in topological.sort( + self.dependencies, + postsort_actions): + rec.execute(self) + + def finalize_flush_changes(self): + """mark processed objects as clean / deleted after a successful + flush(). + + this method is called within the flush() method after the + execute() method has succeeded and the transaction has been committed. + + """ + if not self.states: + return + + states = set(self.states) + isdel = set( + s for (s, (isdelete, listonly)) in self.states.items() + if isdelete + ) + other = states.difference(isdel) + if isdel: + self.session._remove_newly_deleted(isdel) + if other: + self.session._register_newly_persistent(other) + + +class IterateMappersMixin(object): + def _mappers(self, uow): + if self.fromparent: + return iter( + m for m in + self.dependency_processor.parent.self_and_descendants + if uow._mapper_for_dep[(m, self.dependency_processor)] + ) + else: + return self.dependency_processor.mapper.self_and_descendants + + +class Preprocess(IterateMappersMixin): + __slots__ = ( + 'dependency_processor', 'fromparent', 'processed', + 'setup_flush_actions' + ) + + def __init__(self, dependency_processor, fromparent): + self.dependency_processor = dependency_processor + self.fromparent = fromparent + self.processed = set() + self.setup_flush_actions = False + + def execute(self, uow): + delete_states = set() + save_states = set() + + for mapper in self._mappers(uow): + for state in uow.mappers[mapper].difference(self.processed): + (isdelete, listonly) = uow.states[state] + if not listonly: + if isdelete: + delete_states.add(state) + else: + save_states.add(state) + + if delete_states: + self.dependency_processor.presort_deletes(uow, delete_states) + self.processed.update(delete_states) + if save_states: + self.dependency_processor.presort_saves(uow, save_states) + self.processed.update(save_states) + + if (delete_states or save_states): + if not self.setup_flush_actions and ( + self.dependency_processor. + prop_has_changes(uow, delete_states, True) or + self.dependency_processor. + prop_has_changes(uow, save_states, False) + ): + self.dependency_processor.per_property_flush_actions(uow) + self.setup_flush_actions = True + return True + else: + return False + + +class PostSortRec(object): + __slots__ = 'disabled', + + def __new__(cls, uow, *args): + key = (cls, ) + args + if key in uow.postsort_actions: + return uow.postsort_actions[key] + else: + uow.postsort_actions[key] = \ + ret = \ + object.__new__(cls) + ret.disabled = False + return ret + + def execute_aggregate(self, uow, recs): + self.execute(uow) + + +class ProcessAll(IterateMappersMixin, PostSortRec): + __slots__ = 'dependency_processor', 'isdelete', 'fromparent' + + def __init__(self, uow, dependency_processor, isdelete, fromparent): + self.dependency_processor = dependency_processor + self.isdelete = isdelete + self.fromparent = fromparent + uow.deps[dependency_processor.parent.base_mapper].\ + add(dependency_processor) + + def execute(self, uow): + states = self._elements(uow) + if self.isdelete: + self.dependency_processor.process_deletes(uow, states) + else: + self.dependency_processor.process_saves(uow, states) + + def per_state_flush_actions(self, uow): + # this is handled by SaveUpdateAll and DeleteAll, + # since a ProcessAll should unconditionally be pulled + # into per-state if either the parent/child mappers + # are part of a cycle + return iter([]) + + def __repr__(self): + return "%s(%s, isdelete=%s)" % ( + self.__class__.__name__, + self.dependency_processor, + self.isdelete + ) + + def _elements(self, uow): + for mapper in self._mappers(uow): + for state in uow.mappers[mapper]: + (isdelete, listonly) = uow.states[state] + if isdelete == self.isdelete and not listonly: + yield state + + +class PostUpdateAll(PostSortRec): + __slots__ = 'mapper', 'isdelete' + + def __init__(self, uow, mapper, isdelete): + self.mapper = mapper + self.isdelete = isdelete + + def execute(self, uow): + states, cols = uow.post_update_states[self.mapper] + states = [s for s in states if uow.states[s][0] == self.isdelete] + + persistence.post_update(self.mapper, states, uow, cols) + + +class SaveUpdateAll(PostSortRec): + __slots__ = 'mapper', + + def __init__(self, uow, mapper): + self.mapper = mapper + assert mapper is mapper.base_mapper + + def execute(self, uow): + persistence.save_obj(self.mapper, + uow.states_for_mapper_hierarchy( + self.mapper, False, False), + uow + ) + + def per_state_flush_actions(self, uow): + states = list(uow.states_for_mapper_hierarchy( + self.mapper, False, False)) + base_mapper = self.mapper.base_mapper + delete_all = DeleteAll(uow, base_mapper) + for state in states: + # keep saves before deletes - + # this ensures 'row switch' operations work + action = SaveUpdateState(uow, state) + uow.dependencies.add((action, delete_all)) + yield action + + for dep in uow.deps[self.mapper]: + states_for_prop = uow.filter_states_for_dep(dep, states) + dep.per_state_flush_actions(uow, states_for_prop, False) + + def __repr__(self): + return "%s(%s)" % ( + self.__class__.__name__, + self.mapper + ) + + +class DeleteAll(PostSortRec): + __slots__ = 'mapper', + + def __init__(self, uow, mapper): + self.mapper = mapper + assert mapper is mapper.base_mapper + + def execute(self, uow): + persistence.delete_obj(self.mapper, + uow.states_for_mapper_hierarchy( + self.mapper, True, False), + uow + ) + + def per_state_flush_actions(self, uow): + states = list(uow.states_for_mapper_hierarchy( + self.mapper, True, False)) + base_mapper = self.mapper.base_mapper + save_all = SaveUpdateAll(uow, base_mapper) + for state in states: + # keep saves before deletes - + # this ensures 'row switch' operations work + action = DeleteState(uow, state) + uow.dependencies.add((save_all, action)) + yield action + + for dep in uow.deps[self.mapper]: + states_for_prop = uow.filter_states_for_dep(dep, states) + dep.per_state_flush_actions(uow, states_for_prop, True) + + def __repr__(self): + return "%s(%s)" % ( + self.__class__.__name__, + self.mapper + ) + + +class ProcessState(PostSortRec): + __slots__ = 'dependency_processor', 'isdelete', 'state' + + def __init__(self, uow, dependency_processor, isdelete, state): + self.dependency_processor = dependency_processor + self.isdelete = isdelete + self.state = state + + def execute_aggregate(self, uow, recs): + cls_ = self.__class__ + dependency_processor = self.dependency_processor + isdelete = self.isdelete + our_recs = [r for r in recs + if r.__class__ is cls_ and + r.dependency_processor is dependency_processor and + r.isdelete is isdelete] + recs.difference_update(our_recs) + states = [self.state] + [r.state for r in our_recs] + if isdelete: + dependency_processor.process_deletes(uow, states) + else: + dependency_processor.process_saves(uow, states) + + def __repr__(self): + return "%s(%s, %s, delete=%s)" % ( + self.__class__.__name__, + self.dependency_processor, + orm_util.state_str(self.state), + self.isdelete + ) + + +class SaveUpdateState(PostSortRec): + __slots__ = 'state', 'mapper' + + def __init__(self, uow, state): + self.state = state + self.mapper = state.mapper.base_mapper + + def execute_aggregate(self, uow, recs): + cls_ = self.__class__ + mapper = self.mapper + our_recs = [r for r in recs + if r.__class__ is cls_ and + r.mapper is mapper] + recs.difference_update(our_recs) + persistence.save_obj(mapper, + [self.state] + + [r.state for r in our_recs], + uow) + + def __repr__(self): + return "%s(%s)" % ( + self.__class__.__name__, + orm_util.state_str(self.state) + ) + + +class DeleteState(PostSortRec): + __slots__ = 'state', 'mapper' + + def __init__(self, uow, state): + self.state = state + self.mapper = state.mapper.base_mapper + + def execute_aggregate(self, uow, recs): + cls_ = self.__class__ + mapper = self.mapper + our_recs = [r for r in recs + if r.__class__ is cls_ and + r.mapper is mapper] + recs.difference_update(our_recs) + states = [self.state] + [r.state for r in our_recs] + persistence.delete_obj(mapper, + [s for s in states if uow.states[s][0]], + uow) + + def __repr__(self): + return "%s(%s)" % ( + self.__class__.__name__, + orm_util.state_str(self.state) + ) diff --git a/venv/Lib/site-packages/sqlalchemy/orm/util.py b/venv/Lib/site-packages/sqlalchemy/orm/util.py new file mode 100644 index 0000000..43709a5 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/orm/util.py @@ -0,0 +1,1130 @@ +# orm/util.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + + +from .. import sql, util, event, exc as sa_exc, inspection +from ..sql import expression, util as sql_util, operators +from .interfaces import PropComparator, MapperProperty +from . import attributes +import re + +from .base import instance_str, state_str, state_class_str, attribute_str, \ + state_attribute_str, object_mapper, object_state, _none_set, _never_set +from .base import class_mapper, _class_to_mapper +from .base import InspectionAttr +from .path_registry import PathRegistry + +all_cascades = frozenset(("delete", "delete-orphan", "all", "merge", + "expunge", "save-update", "refresh-expire", + "none")) + + +class CascadeOptions(frozenset): + """Keeps track of the options sent to relationship().cascade""" + + _add_w_all_cascades = all_cascades.difference([ + 'all', 'none', 'delete-orphan']) + _allowed_cascades = all_cascades + + __slots__ = ( + 'save_update', 'delete', 'refresh_expire', 'merge', + 'expunge', 'delete_orphan') + + def __new__(cls, value_list): + if isinstance(value_list, util.string_types) or value_list is None: + return cls.from_string(value_list) + values = set(value_list) + if values.difference(cls._allowed_cascades): + raise sa_exc.ArgumentError( + "Invalid cascade option(s): %s" % + ", ".join([repr(x) for x in + sorted(values.difference(cls._allowed_cascades))])) + + if "all" in values: + values.update(cls._add_w_all_cascades) + if "none" in values: + values.clear() + values.discard('all') + + self = frozenset.__new__(CascadeOptions, values) + self.save_update = 'save-update' in values + self.delete = 'delete' in values + self.refresh_expire = 'refresh-expire' in values + self.merge = 'merge' in values + self.expunge = 'expunge' in values + self.delete_orphan = "delete-orphan" in values + + if self.delete_orphan and not self.delete: + util.warn("The 'delete-orphan' cascade " + "option requires 'delete'.") + return self + + def __repr__(self): + return "CascadeOptions(%r)" % ( + ",".join([x for x in sorted(self)]) + ) + + @classmethod + def from_string(cls, arg): + values = [ + c for c + in re.split(r'\s*,\s*', arg or "") + if c + ] + return cls(values) + + +def _validator_events( + desc, key, validator, include_removes, include_backrefs): + """Runs a validation method on an attribute value to be set or + appended. + """ + + if not include_backrefs: + def detect_is_backref(state, initiator): + impl = state.manager[key].impl + return initiator.impl is not impl + + if include_removes: + def append(state, value, initiator): + if ( + initiator.op is not attributes.OP_BULK_REPLACE and + (include_backrefs or not detect_is_backref(state, initiator)) + ): + return validator(state.obj(), key, value, False) + else: + return value + + def bulk_set(state, values, initiator): + if include_backrefs or not detect_is_backref(state, initiator): + obj = state.obj() + values[:] = [ + validator(obj, key, value, False) for value in values] + + def set_(state, value, oldvalue, initiator): + if include_backrefs or not detect_is_backref(state, initiator): + return validator(state.obj(), key, value, False) + else: + return value + + def remove(state, value, initiator): + if include_backrefs or not detect_is_backref(state, initiator): + validator(state.obj(), key, value, True) + + else: + def append(state, value, initiator): + if ( + initiator.op is not attributes.OP_BULK_REPLACE and + (include_backrefs or not detect_is_backref(state, initiator)) + ): + return validator(state.obj(), key, value) + else: + return value + + def bulk_set(state, values, initiator): + if include_backrefs or not detect_is_backref(state, initiator): + obj = state.obj() + values[:] = [ + validator(obj, key, value) for value in values] + + def set_(state, value, oldvalue, initiator): + if include_backrefs or not detect_is_backref(state, initiator): + return validator(state.obj(), key, value) + else: + return value + + event.listen(desc, 'append', append, raw=True, retval=True) + event.listen(desc, 'bulk_replace', bulk_set, raw=True) + event.listen(desc, 'set', set_, raw=True, retval=True) + if include_removes: + event.listen(desc, "remove", remove, raw=True, retval=True) + + +def polymorphic_union(table_map, typecolname, + aliasname='p_union', cast_nulls=True): + """Create a ``UNION`` statement used by a polymorphic mapper. + + See :ref:`concrete_inheritance` for an example of how + this is used. + + :param table_map: mapping of polymorphic identities to + :class:`.Table` objects. + :param typecolname: string name of a "discriminator" column, which will be + derived from the query, producing the polymorphic identity for + each row. If ``None``, no polymorphic discriminator is generated. + :param aliasname: name of the :func:`~sqlalchemy.sql.expression.alias()` + construct generated. + :param cast_nulls: if True, non-existent columns, which are represented + as labeled NULLs, will be passed into CAST. This is a legacy behavior + that is problematic on some backends such as Oracle - in which case it + can be set to False. + + """ + + colnames = util.OrderedSet() + colnamemaps = {} + types = {} + for key in table_map: + table = table_map[key] + + # mysql doesn't like selecting from a select; + # make it an alias of the select + if isinstance(table, sql.Select): + table = table.alias() + table_map[key] = table + + m = {} + for c in table.c: + colnames.add(c.key) + m[c.key] = c + types[c.key] = c.type + colnamemaps[table] = m + + def col(name, table): + try: + return colnamemaps[table][name] + except KeyError: + if cast_nulls: + return sql.cast(sql.null(), types[name]).label(name) + else: + return sql.type_coerce(sql.null(), types[name]).label(name) + + result = [] + for type, table in table_map.items(): + if typecolname is not None: + result.append( + sql.select([col(name, table) for name in colnames] + + [sql.literal_column( + sql_util._quote_ddl_expr(type)). + label(typecolname)], + from_obj=[table])) + else: + result.append(sql.select([col(name, table) for name in colnames], + from_obj=[table])) + return sql.union_all(*result).alias(aliasname) + + +def identity_key(*args, **kwargs): + """Generate "identity key" tuples, as are used as keys in the + :attr:`.Session.identity_map` dictionary. + + This function has several call styles: + + * ``identity_key(class, ident, identity_token=token)`` + + This form receives a mapped class and a primary key scalar or + tuple as an argument. + + E.g.:: + + >>> identity_key(MyClass, (1, 2)) + (<class '__main__.MyClass'>, (1, 2), None) + + :param class: mapped class (must be a positional argument) + :param ident: primary key, may be a scalar or tuple argument. + ;param identity_token: optional identity token + + .. versionadded:: 1.2 added identity_token + + + * ``identity_key(instance=instance)`` + + This form will produce the identity key for a given instance. The + instance need not be persistent, only that its primary key attributes + are populated (else the key will contain ``None`` for those missing + values). + + E.g.:: + + >>> instance = MyClass(1, 2) + >>> identity_key(instance=instance) + (<class '__main__.MyClass'>, (1, 2), None) + + In this form, the given instance is ultimately run though + :meth:`.Mapper.identity_key_from_instance`, which will have the + effect of performing a database check for the corresponding row + if the object is expired. + + :param instance: object instance (must be given as a keyword arg) + + * ``identity_key(class, row=row, identity_token=token)`` + + This form is similar to the class/tuple form, except is passed a + database result row as a :class:`.RowProxy` object. + + E.g.:: + + >>> row = engine.execute("select * from table where a=1 and b=2").\ +first() + >>> identity_key(MyClass, row=row) + (<class '__main__.MyClass'>, (1, 2), None) + + :param class: mapped class (must be a positional argument) + :param row: :class:`.RowProxy` row returned by a :class:`.ResultProxy` + (must be given as a keyword arg) + ;param identity_token: optional identity token + + .. versionadded:: 1.2 added identity_token + + """ + if args: + row = None + largs = len(args) + if largs == 1: + class_ = args[0] + try: + row = kwargs.pop("row") + except KeyError: + ident = kwargs.pop("ident") + elif largs in (2, 3): + class_, ident = args + else: + raise sa_exc.ArgumentError( + "expected up to three positional arguments, " + "got %s" % largs) + + identity_token = kwargs.pop("identity_token", None) + if kwargs: + raise sa_exc.ArgumentError("unknown keyword arguments: %s" + % ", ".join(kwargs)) + mapper = class_mapper(class_) + if row is None: + return mapper.identity_key_from_primary_key( + util.to_list(ident), identity_token=identity_token) + else: + return mapper.identity_key_from_row( + row, identity_token=identity_token) + else: + instance = kwargs.pop("instance") + if kwargs: + raise sa_exc.ArgumentError("unknown keyword arguments: %s" + % ", ".join(kwargs.keys)) + mapper = object_mapper(instance) + return mapper.identity_key_from_instance(instance) + + +class ORMAdapter(sql_util.ColumnAdapter): + """ColumnAdapter subclass which excludes adaptation of entities from + non-matching mappers. + + """ + + def __init__(self, entity, equivalents=None, adapt_required=False, + chain_to=None, allow_label_resolve=True, + anonymize_labels=False): + info = inspection.inspect(entity) + + self.mapper = info.mapper + selectable = info.selectable + is_aliased_class = info.is_aliased_class + if is_aliased_class: + self.aliased_class = entity + else: + self.aliased_class = None + + sql_util.ColumnAdapter.__init__( + self, selectable, equivalents, chain_to, + adapt_required=adapt_required, + allow_label_resolve=allow_label_resolve, + anonymize_labels=anonymize_labels, + include_fn=self._include_fn + ) + + def _include_fn(self, elem): + entity = elem._annotations.get('parentmapper', None) + return not entity or entity.isa(self.mapper) + + +class AliasedClass(object): + r"""Represents an "aliased" form of a mapped class for usage with Query. + + The ORM equivalent of a :func:`sqlalchemy.sql.expression.alias` + construct, this object mimics the mapped class using a + __getattr__ scheme and maintains a reference to a + real :class:`~sqlalchemy.sql.expression.Alias` object. + + Usage is via the :func:`.orm.aliased` function, or alternatively + via the :func:`.orm.with_polymorphic` function. + + Usage example:: + + # find all pairs of users with the same name + user_alias = aliased(User) + session.query(User, user_alias).\ + join((user_alias, User.id > user_alias.id)).\ + filter(User.name==user_alias.name) + + The resulting object is an instance of :class:`.AliasedClass`. + This object implements an attribute scheme which produces the + same attribute and method interface as the original mapped + class, allowing :class:`.AliasedClass` to be compatible + with any attribute technique which works on the original class, + including hybrid attributes (see :ref:`hybrids_toplevel`). + + The :class:`.AliasedClass` can be inspected for its underlying + :class:`.Mapper`, aliased selectable, and other information + using :func:`.inspect`:: + + from sqlalchemy import inspect + my_alias = aliased(MyClass) + insp = inspect(my_alias) + + The resulting inspection object is an instance of :class:`.AliasedInsp`. + + See :func:`.aliased` and :func:`.with_polymorphic` for construction + argument descriptions. + + """ + + def __init__(self, cls, alias=None, + name=None, + flat=False, + adapt_on_names=False, + # TODO: None for default here? + with_polymorphic_mappers=(), + with_polymorphic_discriminator=None, + base_alias=None, + use_mapper_path=False, + represents_outer_join=False): + mapper = _class_to_mapper(cls) + if alias is None: + alias = mapper._with_polymorphic_selectable.alias( + name=name, flat=flat) + + self._aliased_insp = AliasedInsp( + self, + mapper, + alias, + name, + with_polymorphic_mappers + if with_polymorphic_mappers + else mapper.with_polymorphic_mappers, + with_polymorphic_discriminator + if with_polymorphic_discriminator is not None + else mapper.polymorphic_on, + base_alias, + use_mapper_path, + adapt_on_names, + represents_outer_join + ) + + self.__name__ = 'AliasedClass_%s' % mapper.class_.__name__ + + def __getattr__(self, key): + try: + _aliased_insp = self.__dict__['_aliased_insp'] + except KeyError: + raise AttributeError() + else: + for base in _aliased_insp._target.__mro__: + try: + attr = object.__getattribute__(base, key) + except AttributeError: + continue + else: + break + else: + raise AttributeError(key) + + if isinstance(attr, PropComparator): + ret = attr.adapt_to_entity(_aliased_insp) + setattr(self, key, ret) + return ret + elif hasattr(attr, 'func_code'): + is_method = getattr(_aliased_insp._target, key, None) + if is_method and is_method.__self__ is not None: + return util.types.MethodType(attr.__func__, self, self) + else: + return None + elif hasattr(attr, '__get__'): + ret = attr.__get__(None, self) + if isinstance(ret, PropComparator): + return ret.adapt_to_entity(_aliased_insp) + else: + return ret + else: + return attr + + def __repr__(self): + return '<AliasedClass at 0x%x; %s>' % ( + id(self), self._aliased_insp._target.__name__) + + +class AliasedInsp(InspectionAttr): + """Provide an inspection interface for an + :class:`.AliasedClass` object. + + The :class:`.AliasedInsp` object is returned + given an :class:`.AliasedClass` using the + :func:`.inspect` function:: + + from sqlalchemy import inspect + from sqlalchemy.orm import aliased + + my_alias = aliased(MyMappedClass) + insp = inspect(my_alias) + + Attributes on :class:`.AliasedInsp` + include: + + * ``entity`` - the :class:`.AliasedClass` represented. + * ``mapper`` - the :class:`.Mapper` mapping the underlying class. + * ``selectable`` - the :class:`.Alias` construct which ultimately + represents an aliased :class:`.Table` or :class:`.Select` + construct. + * ``name`` - the name of the alias. Also is used as the attribute + name when returned in a result tuple from :class:`.Query`. + * ``with_polymorphic_mappers`` - collection of :class:`.Mapper` objects + indicating all those mappers expressed in the select construct + for the :class:`.AliasedClass`. + * ``polymorphic_on`` - an alternate column or SQL expression which + will be used as the "discriminator" for a polymorphic load. + + .. seealso:: + + :ref:`inspection_toplevel` + + """ + + def __init__(self, entity, mapper, selectable, name, + with_polymorphic_mappers, polymorphic_on, + _base_alias, _use_mapper_path, adapt_on_names, + represents_outer_join): + self.entity = entity + self.mapper = mapper + self.selectable = selectable + self.name = name + self.with_polymorphic_mappers = with_polymorphic_mappers + self.polymorphic_on = polymorphic_on + self._base_alias = _base_alias or self + self._use_mapper_path = _use_mapper_path + self.represents_outer_join = represents_outer_join + + self._adapter = sql_util.ColumnAdapter( + selectable, equivalents=mapper._equivalent_columns, + adapt_on_names=adapt_on_names, anonymize_labels=True) + + self._adapt_on_names = adapt_on_names + self._target = mapper.class_ + + for poly in self.with_polymorphic_mappers: + if poly is not mapper: + setattr(self.entity, poly.class_.__name__, + AliasedClass(poly.class_, selectable, base_alias=self, + adapt_on_names=adapt_on_names, + use_mapper_path=_use_mapper_path)) + + is_aliased_class = True + "always returns True" + + @property + def class_(self): + """Return the mapped class ultimately represented by this + :class:`.AliasedInsp`.""" + return self.mapper.class_ + + @util.memoized_property + def _path_registry(self): + if self._use_mapper_path: + return self.mapper._path_registry + else: + return PathRegistry.per_mapper(self) + + def __getstate__(self): + return { + 'entity': self.entity, + 'mapper': self.mapper, + 'alias': self.selectable, + 'name': self.name, + 'adapt_on_names': self._adapt_on_names, + 'with_polymorphic_mappers': + self.with_polymorphic_mappers, + 'with_polymorphic_discriminator': + self.polymorphic_on, + 'base_alias': self._base_alias, + 'use_mapper_path': self._use_mapper_path, + 'represents_outer_join': self.represents_outer_join + } + + def __setstate__(self, state): + self.__init__( + state['entity'], + state['mapper'], + state['alias'], + state['name'], + state['with_polymorphic_mappers'], + state['with_polymorphic_discriminator'], + state['base_alias'], + state['use_mapper_path'], + state['adapt_on_names'], + state['represents_outer_join'] + ) + + def _adapt_element(self, elem): + return self._adapter.traverse(elem).\ + _annotate({ + 'parententity': self, + 'parentmapper': self.mapper} + ) + + def _entity_for_mapper(self, mapper): + self_poly = self.with_polymorphic_mappers + if mapper in self_poly: + if mapper is self.mapper: + return self + else: + return getattr( + self.entity, mapper.class_.__name__)._aliased_insp + elif mapper.isa(self.mapper): + return self + else: + assert False, "mapper %s doesn't correspond to %s" % ( + mapper, self) + + @util.memoized_property + def _memoized_values(self): + return {} + + def _memo(self, key, callable_, *args, **kw): + if key in self._memoized_values: + return self._memoized_values[key] + else: + self._memoized_values[key] = value = callable_(*args, **kw) + return value + + def __repr__(self): + if self.with_polymorphic_mappers: + with_poly = "(%s)" % ", ".join( + mp.class_.__name__ for mp in self.with_polymorphic_mappers) + else: + with_poly = "" + return '<AliasedInsp at 0x%x; %s%s>' % ( + id(self), self.class_.__name__, with_poly) + + +inspection._inspects(AliasedClass)(lambda target: target._aliased_insp) +inspection._inspects(AliasedInsp)(lambda target: target) + + +def aliased(element, alias=None, name=None, flat=False, adapt_on_names=False): + """Produce an alias of the given element, usually an :class:`.AliasedClass` + instance. + + E.g.:: + + my_alias = aliased(MyClass) + + session.query(MyClass, my_alias).filter(MyClass.id > my_alias.id) + + The :func:`.aliased` function is used to create an ad-hoc mapping + of a mapped class to a new selectable. By default, a selectable + is generated from the normally mapped selectable (typically a + :class:`.Table`) using the :meth:`.FromClause.alias` method. + However, :func:`.aliased` can also be used to link the class to + a new :func:`.select` statement. Also, the :func:`.with_polymorphic` + function is a variant of :func:`.aliased` that is intended to specify + a so-called "polymorphic selectable", that corresponds to the union + of several joined-inheritance subclasses at once. + + For convenience, the :func:`.aliased` function also accepts plain + :class:`.FromClause` constructs, such as a :class:`.Table` or + :func:`.select` construct. In those cases, the :meth:`.FromClause.alias` + method is called on the object and the new :class:`.Alias` object + returned. The returned :class:`.Alias` is not ORM-mapped in this case. + + :param element: element to be aliased. Is normally a mapped class, + but for convenience can also be a :class:`.FromClause` element. + + :param alias: Optional selectable unit to map the element to. This should + normally be a :class:`.Alias` object corresponding to the :class:`.Table` + to which the class is mapped, or to a :func:`.select` construct that + is compatible with the mapping. By default, a simple anonymous + alias of the mapped table is generated. + + :param name: optional string name to use for the alias, if not specified + by the ``alias`` parameter. The name, among other things, forms the + attribute name that will be accessible via tuples returned by a + :class:`.Query` object. + + :param flat: Boolean, will be passed through to the + :meth:`.FromClause.alias` call so that aliases of :class:`.Join` objects + don't include an enclosing SELECT. This can lead to more efficient + queries in many circumstances. A JOIN against a nested JOIN will be + rewritten as a JOIN against an aliased SELECT subquery on backends that + don't support this syntax. + + .. versionadded:: 0.9.0 + + .. seealso:: :meth:`.Join.alias` + + :param adapt_on_names: if True, more liberal "matching" will be used when + mapping the mapped columns of the ORM entity to those of the + given selectable - a name-based match will be performed if the + given selectable doesn't otherwise have a column that corresponds + to one on the entity. The use case for this is when associating + an entity with some derived selectable such as one that uses + aggregate functions:: + + class UnitPrice(Base): + __tablename__ = 'unit_price' + ... + unit_id = Column(Integer) + price = Column(Numeric) + + aggregated_unit_price = Session.query( + func.sum(UnitPrice.price).label('price') + ).group_by(UnitPrice.unit_id).subquery() + + aggregated_unit_price = aliased(UnitPrice, + alias=aggregated_unit_price, adapt_on_names=True) + + Above, functions on ``aggregated_unit_price`` which refer to + ``.price`` will return the + ``func.sum(UnitPrice.price).label('price')`` column, as it is + matched on the name "price". Ordinarily, the "price" function + wouldn't have any "column correspondence" to the actual + ``UnitPrice.price`` column as it is not a proxy of the original. + + .. versionadded:: 0.7.3 + + + """ + if isinstance(element, expression.FromClause): + if adapt_on_names: + raise sa_exc.ArgumentError( + "adapt_on_names only applies to ORM elements" + ) + return element.alias(name, flat=flat) + else: + return AliasedClass(element, alias=alias, flat=flat, + name=name, adapt_on_names=adapt_on_names) + + +def with_polymorphic(base, classes, selectable=False, + flat=False, + polymorphic_on=None, aliased=False, + innerjoin=False, _use_mapper_path=False, + _existing_alias=None): + """Produce an :class:`.AliasedClass` construct which specifies + columns for descendant mappers of the given base. + + Using this method will ensure that each descendant mapper's + tables are included in the FROM clause, and will allow filter() + criterion to be used against those tables. The resulting + instances will also have those columns already loaded so that + no "post fetch" of those columns will be required. + + .. seealso:: + + :ref:`with_polymorphic` - full discussion of + :func:`.orm.with_polymorphic`. + + :param base: Base class to be aliased. + + :param classes: a single class or mapper, or list of + class/mappers, which inherit from the base class. + Alternatively, it may also be the string ``'*'``, in which case + all descending mapped classes will be added to the FROM clause. + + :param aliased: when True, the selectable will be wrapped in an + alias, that is ``(SELECT * FROM <fromclauses>) AS anon_1``. + This can be important when using the with_polymorphic() + to create the target of a JOIN on a backend that does not + support parenthesized joins, such as SQLite and older + versions of MySQL. However if the + :paramref:`.with_polymorphic.selectable` parameter is in use + with an existing :class:`.Alias` construct, then you should not + set this flag. + + :param flat: Boolean, will be passed through to the + :meth:`.FromClause.alias` call so that aliases of :class:`.Join` + objects don't include an enclosing SELECT. This can lead to more + efficient queries in many circumstances. A JOIN against a nested JOIN + will be rewritten as a JOIN against an aliased SELECT subquery on + backends that don't support this syntax. + + Setting ``flat`` to ``True`` implies the ``aliased`` flag is + also ``True``. + + .. versionadded:: 0.9.0 + + .. seealso:: :meth:`.Join.alias` + + :param selectable: a table or select() statement that will + be used in place of the generated FROM clause. This argument is + required if any of the desired classes use concrete table + inheritance, since SQLAlchemy currently cannot generate UNIONs + among tables automatically. If used, the ``selectable`` argument + must represent the full set of tables and columns mapped by every + mapped class. Otherwise, the unaccounted mapped columns will + result in their table being appended directly to the FROM clause + which will usually lead to incorrect results. + + :param polymorphic_on: a column to be used as the "discriminator" + column for the given selectable. If not given, the polymorphic_on + attribute of the base classes' mapper will be used, if any. This + is useful for mappings that don't have polymorphic loading + behavior by default. + + :param innerjoin: if True, an INNER JOIN will be used. This should + only be specified if querying for one specific subtype only + """ + primary_mapper = _class_to_mapper(base) + if _existing_alias: + assert _existing_alias.mapper is primary_mapper + classes = util.to_set(classes) + new_classes = set([ + mp.class_ for mp in + _existing_alias.with_polymorphic_mappers]) + if classes == new_classes: + return _existing_alias + else: + classes = classes.union(new_classes) + mappers, selectable = primary_mapper.\ + _with_polymorphic_args(classes, selectable, + innerjoin=innerjoin) + if aliased or flat: + selectable = selectable.alias(flat=flat) + return AliasedClass(base, + selectable, + with_polymorphic_mappers=mappers, + with_polymorphic_discriminator=polymorphic_on, + use_mapper_path=_use_mapper_path, + represents_outer_join=not innerjoin) + + +def _orm_annotate(element, exclude=None): + """Deep copy the given ClauseElement, annotating each element with the + "_orm_adapt" flag. + + Elements within the exclude collection will be cloned but not annotated. + + """ + return sql_util._deep_annotate(element, {'_orm_adapt': True}, exclude) + + +def _orm_deannotate(element): + """Remove annotations that link a column to a particular mapping. + + Note this doesn't affect "remote" and "foreign" annotations + passed by the :func:`.orm.foreign` and :func:`.orm.remote` + annotators. + + """ + + return sql_util._deep_deannotate(element, + values=("_orm_adapt", "parententity") + ) + + +def _orm_full_deannotate(element): + return sql_util._deep_deannotate(element) + + +class _ORMJoin(expression.Join): + """Extend Join to support ORM constructs as input.""" + + __visit_name__ = expression.Join.__visit_name__ + + def __init__( + self, + left, right, onclause=None, isouter=False, + full=False, _left_memo=None, _right_memo=None): + + left_info = inspection.inspect(left) + left_orm_info = getattr(left, '_joined_from_info', left_info) + + right_info = inspection.inspect(right) + adapt_to = right_info.selectable + + self._joined_from_info = right_info + + self._left_memo = _left_memo + self._right_memo = _right_memo + + if isinstance(onclause, util.string_types): + onclause = getattr(left_orm_info.entity, onclause) + + if isinstance(onclause, attributes.QueryableAttribute): + on_selectable = onclause.comparator._source_selectable() + prop = onclause.property + elif isinstance(onclause, MapperProperty): + prop = onclause + on_selectable = prop.parent.selectable + else: + prop = None + + if prop: + if sql_util.clause_is_present( + on_selectable, left_info.selectable): + adapt_from = on_selectable + else: + adapt_from = left_info.selectable + + pj, sj, source, dest, \ + secondary, target_adapter = prop._create_joins( + source_selectable=adapt_from, + dest_selectable=adapt_to, + source_polymorphic=True, + dest_polymorphic=True, + of_type=right_info.mapper) + + if sj is not None: + if isouter: + # note this is an inner join from secondary->right + right = sql.join(secondary, right, sj) + onclause = pj + else: + left = sql.join(left, secondary, pj, isouter) + onclause = sj + else: + onclause = pj + self._target_adapter = target_adapter + + expression.Join.__init__(self, left, right, onclause, isouter, full) + + if not prop and getattr(right_info, 'mapper', None) \ + and right_info.mapper.single: + # if single inheritance target and we are using a manual + # or implicit ON clause, augment it the same way we'd augment the + # WHERE. + single_crit = right_info.mapper._single_table_criterion + if single_crit is not None: + if right_info.is_aliased_class: + single_crit = right_info._adapter.traverse(single_crit) + self.onclause = self.onclause & single_crit + + def _splice_into_center(self, other): + """Splice a join into the center. + + Given join(a, b) and join(b, c), return join(a, b).join(c) + + """ + leftmost = other + while isinstance(leftmost, sql.Join): + leftmost = leftmost.left + + assert self.right is leftmost + + left = _ORMJoin( + self.left, other.left, + self.onclause, isouter=self.isouter, + _left_memo=self._left_memo, + _right_memo=other._left_memo + ) + + return _ORMJoin( + left, + other.right, + other.onclause, isouter=other.isouter, + _right_memo=other._right_memo + ) + + def join( + self, right, onclause=None, + isouter=False, full=False, join_to_left=None): + return _ORMJoin(self, right, onclause, full, isouter) + + def outerjoin( + self, right, onclause=None, + full=False, join_to_left=None): + return _ORMJoin(self, right, onclause, True, full=full) + + +def join( + left, right, onclause=None, isouter=False, + full=False, join_to_left=None): + r"""Produce an inner join between left and right clauses. + + :func:`.orm.join` is an extension to the core join interface + provided by :func:`.sql.expression.join()`, where the + left and right selectables may be not only core selectable + objects such as :class:`.Table`, but also mapped classes or + :class:`.AliasedClass` instances. The "on" clause can + be a SQL expression, or an attribute or string name + referencing a configured :func:`.relationship`. + + :func:`.orm.join` is not commonly needed in modern usage, + as its functionality is encapsulated within that of the + :meth:`.Query.join` method, which features a + significant amount of automation beyond :func:`.orm.join` + by itself. Explicit usage of :func:`.orm.join` + with :class:`.Query` involves usage of the + :meth:`.Query.select_from` method, as in:: + + from sqlalchemy.orm import join + session.query(User).\ + select_from(join(User, Address, User.addresses)).\ + filter(Address.email_address=='foo@bar.com') + + In modern SQLAlchemy the above join can be written more + succinctly as:: + + session.query(User).\ + join(User.addresses).\ + filter(Address.email_address=='foo@bar.com') + + See :meth:`.Query.join` for information on modern usage + of ORM level joins. + + .. versionchanged:: 0.8.1 - the ``join_to_left`` parameter + is no longer used, and is deprecated. + + """ + return _ORMJoin(left, right, onclause, isouter, full) + + +def outerjoin(left, right, onclause=None, full=False, join_to_left=None): + """Produce a left outer join between left and right clauses. + + This is the "outer join" version of the :func:`.orm.join` function, + featuring the same behavior except that an OUTER JOIN is generated. + See that function's documentation for other usage details. + + """ + return _ORMJoin(left, right, onclause, True, full) + + +def with_parent(instance, prop, from_entity=None): + """Create filtering criterion that relates this query's primary entity + to the given related instance, using established :func:`.relationship()` + configuration. + + The SQL rendered is the same as that rendered when a lazy loader + would fire off from the given parent on that attribute, meaning + that the appropriate state is taken from the parent object in + Python without the need to render joins to the parent table + in the rendered statement. + + :param instance: + An instance which has some :func:`.relationship`. + + :param property: + String property name, or class-bound attribute, which indicates + what relationship from the instance should be used to reconcile the + parent/child relationship. + + :param from_entity: + Entity in which to consider as the left side. This defaults to the + "zero" entity of the :class:`.Query` itself. + + .. versionadded:: 1.2 + + """ + if isinstance(prop, util.string_types): + mapper = object_mapper(instance) + prop = getattr(mapper.class_, prop).property + elif isinstance(prop, attributes.QueryableAttribute): + prop = prop.property + + return prop._with_parent(instance, from_entity=from_entity) + + +def has_identity(object): + """Return True if the given object has a database + identity. + + This typically corresponds to the object being + in either the persistent or detached state. + + .. seealso:: + + :func:`.was_deleted` + + """ + state = attributes.instance_state(object) + return state.has_identity + + +def was_deleted(object): + """Return True if the given object was deleted + within a session flush. + + This is regardless of whether or not the object is + persistent or detached. + + .. versionadded:: 0.8.0 + + .. seealso:: + + :attr:`.InstanceState.was_deleted` + + """ + + state = attributes.instance_state(object) + return state.was_deleted + + +def _entity_corresponds_to(given, entity): + """determine if 'given' corresponds to 'entity', in terms + of an entity passed to Query that would match the same entity + being referred to elsewhere in the query. + + """ + if entity.is_aliased_class: + if given.is_aliased_class: + if entity._base_alias is given._base_alias: + return True + return False + elif given.is_aliased_class: + if given._use_mapper_path: + return entity in given.with_polymorphic_mappers + else: + return entity is given + + return entity.common_parent(given) + + +def _entity_isa(given, mapper): + """determine if 'given' "is a" mapper, in terms of the given + would load rows of type 'mapper'. + + """ + if given.is_aliased_class: + return mapper in given.with_polymorphic_mappers or \ + given.mapper.isa(mapper) + elif given.with_polymorphic_mappers: + return mapper in given.with_polymorphic_mappers + else: + return given.isa(mapper) + + +def randomize_unitofwork(): + """Use random-ordering sets within the unit of work in order + to detect unit of work sorting issues. + + This is a utility function that can be used to help reproduce + inconsistent unit of work sorting issues. For example, + if two kinds of objects A and B are being inserted, and + B has a foreign key reference to A - the A must be inserted first. + However, if there is no relationship between A and B, the unit of work + won't know to perform this sorting, and an operation may or may not + fail, depending on how the ordering works out. Since Python sets + and dictionaries have non-deterministic ordering, such an issue may + occur on some runs and not on others, and in practice it tends to + have a great dependence on the state of the interpreter. This leads + to so-called "heisenbugs" where changing entirely irrelevant aspects + of the test program still cause the failure behavior to change. + + By calling ``randomize_unitofwork()`` when a script first runs, the + ordering of a key series of sets within the unit of work implementation + are randomized, so that the script can be minimized down to the + fundamental mapping and operation that's failing, while still reproducing + the issue on at least some runs. + + This utility is also available when running the test suite via the + ``--reversetop`` flag. + + .. versionadded:: 0.8.1 created a standalone version of the + ``--reversetop`` feature. + + """ + from sqlalchemy.orm import unitofwork, session, mapper, dependency + from sqlalchemy.util import topological + from sqlalchemy.testing.util import RandomSet + topological.set = unitofwork.set = session.set = mapper.set = \ + dependency.set = RandomSet diff --git a/venv/Lib/site-packages/sqlalchemy/pool.py b/venv/Lib/site-packages/sqlalchemy/pool.py new file mode 100644 index 0000000..db0378e --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/pool.py @@ -0,0 +1,1500 @@ +# sqlalchemy/pool.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + + +"""Connection pooling for DB-API connections. + +Provides a number of connection pool implementations for a variety of +usage scenarios and thread behavior requirements imposed by the +application, DB-API or database itself. + +Also provides a DB-API 2.0 connection proxying mechanism allowing +regular DB-API connect() methods to be transparently managed by a +SQLAlchemy connection pool. +""" + +import time +import traceback +import weakref + +from . import exc, log, event, interfaces, util +from .util import queue as sqla_queue +from .util import threading, memoized_property, \ + chop_traceback + +from collections import deque +proxies = {} + + +def manage(module, **params): + r"""Return a proxy for a DB-API module that automatically + pools connections. + + Given a DB-API 2.0 module and pool management parameters, returns + a proxy for the module that will automatically pool connections, + creating new connection pools for each distinct set of connection + arguments sent to the decorated module's connect() function. + + :param module: a DB-API 2.0 database module + + :param poolclass: the class used by the pool module to provide + pooling. Defaults to :class:`.QueuePool`. + + :param \**params: will be passed through to *poolclass* + + """ + try: + return proxies[module] + except KeyError: + return proxies.setdefault(module, _DBProxy(module, **params)) + + +def clear_managers(): + """Remove all current DB-API 2.0 managers. + + All pools and connections are disposed. + """ + + for manager in proxies.values(): + manager.close() + proxies.clear() + +reset_rollback = util.symbol('reset_rollback') +reset_commit = util.symbol('reset_commit') +reset_none = util.symbol('reset_none') + + +class _ConnDialect(object): + + """partial implementation of :class:`.Dialect` + which provides DBAPI connection methods. + + When a :class:`.Pool` is combined with an :class:`.Engine`, + the :class:`.Engine` replaces this with its own + :class:`.Dialect`. + + """ + + def do_rollback(self, dbapi_connection): + dbapi_connection.rollback() + + def do_commit(self, dbapi_connection): + dbapi_connection.commit() + + def do_close(self, dbapi_connection): + dbapi_connection.close() + + def do_ping(self, dbapi_connection): + raise NotImplementedError( + "The ping feature requires that a dialect is " + "passed to the connection pool.") + + +class Pool(log.Identified): + + """Abstract base class for connection pools.""" + + _dialect = _ConnDialect() + + def __init__(self, + creator, recycle=-1, echo=None, + use_threadlocal=False, + logging_name=None, + reset_on_return=True, + listeners=None, + events=None, + dialect=None, + pre_ping=False, + _dispatch=None): + """ + Construct a Pool. + + :param creator: a callable function that returns a DB-API + connection object. The function will be called with + parameters. + + :param recycle: If set to a value other than -1, number of + seconds between connection recycling, which means upon + checkout, if this timeout is surpassed the connection will be + closed and replaced with a newly opened connection. Defaults to -1. + + :param logging_name: String identifier which will be used within + the "name" field of logging records generated within the + "sqlalchemy.pool" logger. Defaults to a hexstring of the object's + id. + + :param echo: If True, connections being pulled and retrieved + from the pool will be logged to the standard output, as well + as pool sizing information. Echoing can also be achieved by + enabling logging for the "sqlalchemy.pool" + namespace. Defaults to False. + + :param use_threadlocal: If set to True, repeated calls to + :meth:`connect` within the same application thread will be + guaranteed to return the same connection object, if one has + already been retrieved from the pool and has not been + returned yet. Offers a slight performance advantage at the + cost of individual transactions by default. The + :meth:`.Pool.unique_connection` method is provided to return + a consistently unique connection to bypass this behavior + when the flag is set. + + .. warning:: The :paramref:`.Pool.use_threadlocal` flag + **does not affect the behavior** of :meth:`.Engine.connect`. + :meth:`.Engine.connect` makes use of the + :meth:`.Pool.unique_connection` method which **does not use thread + local context**. To produce a :class:`.Connection` which refers + to the :meth:`.Pool.connect` method, use + :meth:`.Engine.contextual_connect`. + + Note that other SQLAlchemy connectivity systems such as + :meth:`.Engine.execute` as well as the orm + :class:`.Session` make use of + :meth:`.Engine.contextual_connect` internally, so these functions + are compatible with the :paramref:`.Pool.use_threadlocal` setting. + + .. seealso:: + + :ref:`threadlocal_strategy` - contains detail on the + "threadlocal" engine strategy, which provides a more comprehensive + approach to "threadlocal" connectivity for the specific + use case of using :class:`.Engine` and :class:`.Connection` objects + directly. + + :param reset_on_return: Determine steps to take on + connections as they are returned to the pool. + reset_on_return can have any of these values: + + * ``"rollback"`` - call rollback() on the connection, + to release locks and transaction resources. + This is the default value. The vast majority + of use cases should leave this value set. + * ``True`` - same as 'rollback', this is here for + backwards compatibility. + * ``"commit"`` - call commit() on the connection, + to release locks and transaction resources. + A commit here may be desirable for databases that + cache query plans if a commit is emitted, + such as Microsoft SQL Server. However, this + value is more dangerous than 'rollback' because + any data changes present on the transaction + are committed unconditionally. + * ``None`` - don't do anything on the connection. + This setting should generally only be made on a database + that has no transaction support at all, + namely MySQL MyISAM; when used on this backend, performance + can be improved as the "rollback" call is still expensive on + MySQL. It is **strongly recommended** that this setting not be + used for transaction-supporting databases in conjunction with + a persistent pool such as :class:`.QueuePool`, as it opens + the possibility for connections still in a transaction to be + idle in the pool. The setting may be appropriate in the + case of :class:`.NullPool` or special circumstances where + the connection pool in use is not being used to maintain connection + lifecycle. + + * ``False`` - same as None, this is here for + backwards compatibility. + + :param events: a list of 2-tuples, each of the form + ``(callable, target)`` which will be passed to :func:`.event.listen` + upon construction. Provided here so that event listeners + can be assigned via :func:`.create_engine` before dialect-level + listeners are applied. + + :param listeners: Deprecated. A list of + :class:`~sqlalchemy.interfaces.PoolListener`-like objects or + dictionaries of callables that receive events when DB-API + connections are created, checked out and checked in to the + pool. This has been superseded by + :func:`~sqlalchemy.event.listen`. + + :param dialect: a :class:`.Dialect` that will handle the job + of calling rollback(), close(), or commit() on DBAPI connections. + If omitted, a built-in "stub" dialect is used. Applications that + make use of :func:`~.create_engine` should not use this parameter + as it is handled by the engine creation strategy. + + .. versionadded:: 1.1 - ``dialect`` is now a public parameter + to the :class:`.Pool`. + + :param pre_ping: if True, the pool will emit a "ping" (typically + "SELECT 1", but is dialect-specific) on the connection + upon checkout, to test if the connection is alive or not. If not, + the connection is transparently re-connected and upon success, all + other pooled connections established prior to that timestamp are + invalidated. Requires that a dialect is passed as well to + interpret the disconnection error. + + .. versionadded:: 1.2 + + """ + if logging_name: + self.logging_name = self._orig_logging_name = logging_name + else: + self._orig_logging_name = None + + log.instance_logger(self, echoflag=echo) + self._threadconns = threading.local() + self._creator = creator + self._recycle = recycle + self._invalidate_time = 0 + self._use_threadlocal = use_threadlocal + self._pre_ping = pre_ping + if reset_on_return in ('rollback', True, reset_rollback): + self._reset_on_return = reset_rollback + elif reset_on_return in ('none', None, False, reset_none): + self._reset_on_return = reset_none + elif reset_on_return in ('commit', reset_commit): + self._reset_on_return = reset_commit + else: + raise exc.ArgumentError( + "Invalid value for 'reset_on_return': %r" + % reset_on_return) + + self.echo = echo + + if _dispatch: + self.dispatch._update(_dispatch, only_propagate=False) + if dialect: + self._dialect = dialect + if events: + for fn, target in events: + event.listen(self, target, fn) + if listeners: + util.warn_deprecated( + "The 'listeners' argument to Pool (and " + "create_engine()) is deprecated. Use event.listen().") + for l in listeners: + self.add_listener(l) + + @property + def _creator(self): + return self.__dict__['_creator'] + + @_creator.setter + def _creator(self, creator): + self.__dict__['_creator'] = creator + self._invoke_creator = self._should_wrap_creator(creator) + + def _should_wrap_creator(self, creator): + """Detect if creator accepts a single argument, or is sent + as a legacy style no-arg function. + + """ + + try: + argspec = util.get_callable_argspec(self._creator, no_self=True) + except TypeError: + return lambda crec: creator() + + defaulted = argspec[3] is not None and len(argspec[3]) or 0 + positionals = len(argspec[0]) - defaulted + + # look for the exact arg signature that DefaultStrategy + # sends us + if (argspec[0], argspec[3]) == (['connection_record'], (None,)): + return creator + # or just a single positional + elif positionals == 1: + return creator + # all other cases, just wrap and assume legacy "creator" callable + # thing + else: + return lambda crec: creator() + + def _close_connection(self, connection): + self.logger.debug("Closing connection %r", connection) + + try: + self._dialect.do_close(connection) + except Exception: + self.logger.error("Exception closing connection %r", + connection, exc_info=True) + + @util.deprecated( + 2.7, "Pool.add_listener is deprecated. Use event.listen()") + def add_listener(self, listener): + """Add a :class:`.PoolListener`-like object to this pool. + + ``listener`` may be an object that implements some or all of + PoolListener, or a dictionary of callables containing implementations + of some or all of the named methods in PoolListener. + + """ + interfaces.PoolListener._adapt_listener(self, listener) + + def unique_connection(self): + """Produce a DBAPI connection that is not referenced by any + thread-local context. + + This method is equivalent to :meth:`.Pool.connect` when the + :paramref:`.Pool.use_threadlocal` flag is not set to True. + When :paramref:`.Pool.use_threadlocal` is True, the + :meth:`.Pool.unique_connection` method provides a means of bypassing + the threadlocal context. + + """ + return _ConnectionFairy._checkout(self) + + def _create_connection(self): + """Called by subclasses to create a new ConnectionRecord.""" + + return _ConnectionRecord(self) + + def _invalidate(self, connection, exception=None, _checkin=True): + """Mark all connections established within the generation + of the given connection as invalidated. + + If this pool's last invalidate time is before when the given + connection was created, update the timestamp til now. Otherwise, + no action is performed. + + Connections with a start time prior to this pool's invalidation + time will be recycled upon next checkout. + """ + rec = getattr(connection, "_connection_record", None) + if not rec or self._invalidate_time < rec.starttime: + self._invalidate_time = time.time() + if _checkin and getattr(connection, 'is_valid', False): + connection.invalidate(exception) + + def recreate(self): + """Return a new :class:`.Pool`, of the same class as this one + and configured with identical creation arguments. + + This method is used in conjunction with :meth:`dispose` + to close out an entire :class:`.Pool` and create a new one in + its place. + + """ + + raise NotImplementedError() + + def dispose(self): + """Dispose of this pool. + + This method leaves the possibility of checked-out connections + remaining open, as it only affects connections that are + idle in the pool. + + See also the :meth:`Pool.recreate` method. + + """ + + raise NotImplementedError() + + def connect(self): + """Return a DBAPI connection from the pool. + + The connection is instrumented such that when its + ``close()`` method is called, the connection will be returned to + the pool. + + """ + if not self._use_threadlocal: + return _ConnectionFairy._checkout(self) + + try: + rec = self._threadconns.current() + except AttributeError: + pass + else: + if rec is not None: + return rec._checkout_existing() + + return _ConnectionFairy._checkout(self, self._threadconns) + + def _return_conn(self, record): + """Given a _ConnectionRecord, return it to the :class:`.Pool`. + + This method is called when an instrumented DBAPI connection + has its ``close()`` method called. + + """ + if self._use_threadlocal: + try: + del self._threadconns.current + except AttributeError: + pass + self._do_return_conn(record) + + def _do_get(self): + """Implementation for :meth:`get`, supplied by subclasses.""" + + raise NotImplementedError() + + def _do_return_conn(self, conn): + """Implementation for :meth:`return_conn`, supplied by subclasses.""" + + raise NotImplementedError() + + def status(self): + raise NotImplementedError() + + +class _ConnectionRecord(object): + + """Internal object which maintains an individual DBAPI connection + referenced by a :class:`.Pool`. + + The :class:`._ConnectionRecord` object always exists for any particular + DBAPI connection whether or not that DBAPI connection has been + "checked out". This is in contrast to the :class:`._ConnectionFairy` + which is only a public facade to the DBAPI connection while it is checked + out. + + A :class:`._ConnectionRecord` may exist for a span longer than that + of a single DBAPI connection. For example, if the + :meth:`._ConnectionRecord.invalidate` + method is called, the DBAPI connection associated with this + :class:`._ConnectionRecord` + will be discarded, but the :class:`._ConnectionRecord` may be used again, + in which case a new DBAPI connection is produced when the :class:`.Pool` + next uses this record. + + The :class:`._ConnectionRecord` is delivered along with connection + pool events, including :meth:`.PoolEvents.connect` and + :meth:`.PoolEvents.checkout`, however :class:`._ConnectionRecord` still + remains an internal object whose API and internals may change. + + .. seealso:: + + :class:`._ConnectionFairy` + + """ + + def __init__(self, pool, connect=True): + self.__pool = pool + if connect: + self.__connect(first_connect_check=True) + self.finalize_callback = deque() + + fairy_ref = None + + starttime = None + + connection = None + """A reference to the actual DBAPI connection being tracked. + + May be ``None`` if this :class:`._ConnectionRecord` has been marked + as invalidated; a new DBAPI connection may replace it if the owning + pool calls upon this :class:`._ConnectionRecord` to reconnect. + + """ + + _soft_invalidate_time = 0 + + @util.memoized_property + def info(self): + """The ``.info`` dictionary associated with the DBAPI connection. + + This dictionary is shared among the :attr:`._ConnectionFairy.info` + and :attr:`.Connection.info` accessors. + + .. note:: + + The lifespan of this dictionary is linked to the + DBAPI connection itself, meaning that it is **discarded** each time + the DBAPI connection is closed and/or invalidated. The + :attr:`._ConnectionRecord.record_info` dictionary remains + persistent throughout the lifespan of the + :class:`._ConnectionRecord` container. + + """ + return {} + + @util.memoized_property + def record_info(self): + """An "info' dictionary associated with the connection record + itself. + + Unlike the :attr:`._ConnectionRecord.info` dictionary, which is linked + to the lifespan of the DBAPI connection, this dictionary is linked + to the lifespan of the :class:`._ConnectionRecord` container itself + and will remain persisent throughout the life of the + :class:`._ConnectionRecord`. + + .. versionadded:: 1.1 + + """ + return {} + + @classmethod + def checkout(cls, pool): + rec = pool._do_get() + try: + dbapi_connection = rec.get_connection() + except Exception as err: + with util.safe_reraise(): + rec._checkin_failed(err) + echo = pool._should_log_debug() + fairy = _ConnectionFairy(dbapi_connection, rec, echo) + rec.fairy_ref = weakref.ref( + fairy, + lambda ref: _finalize_fairy and + _finalize_fairy( + None, + rec, pool, ref, echo) + ) + _refs.add(rec) + if echo: + pool.logger.debug("Connection %r checked out from pool", + dbapi_connection) + return fairy + + def _checkin_failed(self, err): + self.invalidate(e=err) + self.checkin(_no_fairy_ref=True) + + def checkin(self, _no_fairy_ref=False): + if self.fairy_ref is None and not _no_fairy_ref: + util.warn("Double checkin attempted on %s" % self) + return + self.fairy_ref = None + connection = self.connection + pool = self.__pool + while self.finalize_callback: + finalizer = self.finalize_callback.pop() + finalizer(connection) + if pool.dispatch.checkin: + pool.dispatch.checkin(connection, self) + pool._return_conn(self) + + @property + def in_use(self): + return self.fairy_ref is not None + + @property + def last_connect_time(self): + return self.starttime + + def close(self): + if self.connection is not None: + self.__close() + + def invalidate(self, e=None, soft=False): + """Invalidate the DBAPI connection held by this :class:`._ConnectionRecord`. + + This method is called for all connection invalidations, including + when the :meth:`._ConnectionFairy.invalidate` or + :meth:`.Connection.invalidate` methods are called, as well as when any + so-called "automatic invalidation" condition occurs. + + :param e: an exception object indicating a reason for the invalidation. + + :param soft: if True, the connection isn't closed; instead, this + connection will be recycled on next checkout. + + .. versionadded:: 1.0.3 + + .. seealso:: + + :ref:`pool_connection_invalidation` + + """ + # already invalidated + if self.connection is None: + return + if soft: + self.__pool.dispatch.soft_invalidate(self.connection, self, e) + else: + self.__pool.dispatch.invalidate(self.connection, self, e) + if e is not None: + self.__pool.logger.info( + "%sInvalidate connection %r (reason: %s:%s)", + "Soft " if soft else "", + self.connection, e.__class__.__name__, e) + else: + self.__pool.logger.info( + "%sInvalidate connection %r", + "Soft " if soft else "", + self.connection) + if soft: + self._soft_invalidate_time = time.time() + else: + self.__close() + self.connection = None + + def get_connection(self): + recycle = False + if self.connection is None: + self.info.clear() + self.__connect() + elif self.__pool._recycle > -1 and \ + time.time() - self.starttime > self.__pool._recycle: + self.__pool.logger.info( + "Connection %r exceeded timeout; recycling", + self.connection) + recycle = True + elif self.__pool._invalidate_time > self.starttime: + self.__pool.logger.info( + "Connection %r invalidated due to pool invalidation; " + + "recycling", + self.connection + ) + recycle = True + elif self._soft_invalidate_time > self.starttime: + self.__pool.logger.info( + "Connection %r invalidated due to local soft invalidation; " + + "recycling", + self.connection + ) + recycle = True + + if recycle: + self.__close() + self.info.clear() + + self.__connect() + return self.connection + + def __close(self): + self.finalize_callback.clear() + if self.__pool.dispatch.close: + self.__pool.dispatch.close(self.connection, self) + self.__pool._close_connection(self.connection) + self.connection = None + + def __connect(self, first_connect_check=False): + pool = self.__pool + + # ensure any existing connection is removed, so that if + # creator fails, this attribute stays None + self.connection = None + try: + self.starttime = time.time() + connection = pool._invoke_creator(self) + pool.logger.debug("Created new connection %r", connection) + self.connection = connection + except Exception as e: + pool.logger.debug("Error on connect(): %s", e) + raise + else: + if first_connect_check: + pool.dispatch.first_connect.\ + for_modify(pool.dispatch).\ + exec_once(self.connection, self) + if pool.dispatch.connect: + pool.dispatch.connect(self.connection, self) + + +def _finalize_fairy(connection, connection_record, + pool, ref, echo, fairy=None): + """Cleanup for a :class:`._ConnectionFairy` whether or not it's already + been garbage collected. + + """ + _refs.discard(connection_record) + + if ref is not None: + if connection_record.fairy_ref is not ref: + return + assert connection is None + connection = connection_record.connection + + if connection is not None: + if connection_record and echo: + pool.logger.debug("Connection %r being returned to pool", + connection) + + try: + fairy = fairy or _ConnectionFairy( + connection, connection_record, echo) + assert fairy.connection is connection + fairy._reset(pool) + + # Immediately close detached instances + if not connection_record: + if pool.dispatch.close_detached: + pool.dispatch.close_detached(connection) + pool._close_connection(connection) + except BaseException as e: + pool.logger.error( + "Exception during reset or similar", exc_info=True) + if connection_record: + connection_record.invalidate(e=e) + if not isinstance(e, Exception): + raise + + if connection_record and connection_record.fairy_ref is not None: + connection_record.checkin() + + +_refs = set() + + +class _ConnectionFairy(object): + + """Proxies a DBAPI connection and provides return-on-dereference + support. + + This is an internal object used by the :class:`.Pool` implementation + to provide context management to a DBAPI connection delivered by + that :class:`.Pool`. + + The name "fairy" is inspired by the fact that the + :class:`._ConnectionFairy` object's lifespan is transitory, as it lasts + only for the length of a specific DBAPI connection being checked out from + the pool, and additionally that as a transparent proxy, it is mostly + invisible. + + .. seealso:: + + :class:`._ConnectionRecord` + + """ + + def __init__(self, dbapi_connection, connection_record, echo): + self.connection = dbapi_connection + self._connection_record = connection_record + self._echo = echo + + connection = None + """A reference to the actual DBAPI connection being tracked.""" + + _connection_record = None + """A reference to the :class:`._ConnectionRecord` object associated + with the DBAPI connection. + + This is currently an internal accessor which is subject to change. + + """ + + _reset_agent = None + """Refer to an object with a ``.commit()`` and ``.rollback()`` method; + if non-None, the "reset-on-return" feature will call upon this object + rather than directly against the dialect-level do_rollback() and + do_commit() methods. + + In practice, a :class:`.Connection` assigns a :class:`.Transaction` object + to this variable when one is in scope so that the :class:`.Transaction` + takes the job of committing or rolling back on return if + :meth:`.Connection.close` is called while the :class:`.Transaction` + still exists. + + This is essentially an "event handler" of sorts but is simplified as an + instance variable both for performance/simplicity as well as that there + can only be one "reset agent" at a time. + """ + + @classmethod + def _checkout(cls, pool, threadconns=None, fairy=None): + if not fairy: + fairy = _ConnectionRecord.checkout(pool) + + fairy._pool = pool + fairy._counter = 0 + + if threadconns is not None: + threadconns.current = weakref.ref(fairy) + + if fairy.connection is None: + raise exc.InvalidRequestError("This connection is closed") + fairy._counter += 1 + + if (not pool.dispatch.checkout and not pool._pre_ping) or \ + fairy._counter != 1: + return fairy + + # Pool listeners can trigger a reconnection on checkout, as well + # as the pre-pinger. + # there are three attempts made here, but note that if the database + # is not accessible from a connection standpoint, those won't proceed + # here. + attempts = 2 + while attempts > 0: + try: + if pool._pre_ping: + if fairy._echo: + pool.logger.debug( + "Pool pre-ping on connection %s", + fairy.connection) + + result = pool._dialect.do_ping(fairy.connection) + if not result: + if fairy._echo: + pool.logger.debug( + "Pool pre-ping on connection %s failed, " + "will invalidate pool", fairy.connection) + raise exc.InvalidatePoolError() + + pool.dispatch.checkout(fairy.connection, + fairy._connection_record, + fairy) + return fairy + except exc.DisconnectionError as e: + if e.invalidate_pool: + pool.logger.info( + "Disconnection detected on checkout, " + "invalidating all pooled connections prior to " + "current timestamp (reason: %r)", e) + fairy._connection_record.invalidate(e) + pool._invalidate(fairy, e, _checkin=False) + else: + pool.logger.info( + "Disconnection detected on checkout, " + "invalidating individual connection %s (reason: %r)", + fairy.connection, e) + fairy._connection_record.invalidate(e) + try: + fairy.connection = \ + fairy._connection_record.get_connection() + except Exception as err: + with util.safe_reraise(): + fairy._connection_record._checkin_failed(err) + + attempts -= 1 + + pool.logger.info("Reconnection attempts exhausted on checkout") + fairy.invalidate() + raise exc.InvalidRequestError("This connection is closed") + + def _checkout_existing(self): + return _ConnectionFairy._checkout(self._pool, fairy=self) + + def _checkin(self): + _finalize_fairy(self.connection, self._connection_record, + self._pool, None, self._echo, fairy=self) + self.connection = None + self._connection_record = None + + _close = _checkin + + def _reset(self, pool): + if pool.dispatch.reset: + pool.dispatch.reset(self, self._connection_record) + if pool._reset_on_return is reset_rollback: + if self._echo: + pool.logger.debug("Connection %s rollback-on-return%s", + self.connection, + ", via agent" + if self._reset_agent else "") + if self._reset_agent: + self._reset_agent.rollback() + else: + pool._dialect.do_rollback(self) + elif pool._reset_on_return is reset_commit: + if self._echo: + pool.logger.debug("Connection %s commit-on-return%s", + self.connection, + ", via agent" + if self._reset_agent else "") + if self._reset_agent: + self._reset_agent.commit() + else: + pool._dialect.do_commit(self) + + @property + def _logger(self): + return self._pool.logger + + @property + def is_valid(self): + """Return True if this :class:`._ConnectionFairy` still refers + to an active DBAPI connection.""" + + return self.connection is not None + + @util.memoized_property + def info(self): + """Info dictionary associated with the underlying DBAPI connection + referred to by this :class:`.ConnectionFairy`, allowing user-defined + data to be associated with the connection. + + The data here will follow along with the DBAPI connection including + after it is returned to the connection pool and used again + in subsequent instances of :class:`._ConnectionFairy`. It is shared + with the :attr:`._ConnectionRecord.info` and :attr:`.Connection.info` + accessors. + + The dictionary associated with a particular DBAPI connection is + discarded when the connection itself is discarded. + + """ + return self._connection_record.info + + @property + def record_info(self): + """Info dictionary associated with the :class:`._ConnectionRecord + container referred to by this :class:`.ConnectionFairy`. + + Unlike the :attr:`._ConnectionFairy.info` dictionary, the lifespan + of this dictionary is persistent across connections that are + disconnected and/or invalidated within the lifespan of a + :class:`._ConnectionRecord`. + + .. versionadded:: 1.1 + + """ + if self._connection_record: + return self._connection_record.record_info + else: + return None + + def invalidate(self, e=None, soft=False): + """Mark this connection as invalidated. + + This method can be called directly, and is also called as a result + of the :meth:`.Connection.invalidate` method. When invoked, + the DBAPI connection is immediately closed and discarded from + further use by the pool. The invalidation mechanism proceeds + via the :meth:`._ConnectionRecord.invalidate` internal method. + + :param e: an exception object indicating a reason for the invalidation. + + :param soft: if True, the connection isn't closed; instead, this + connection will be recycled on next checkout. + + .. versionadded:: 1.0.3 + + .. seealso:: + + :ref:`pool_connection_invalidation` + + """ + + if self.connection is None: + util.warn("Can't invalidate an already-closed connection.") + return + if self._connection_record: + self._connection_record.invalidate(e=e, soft=soft) + if not soft: + self.connection = None + self._checkin() + + def cursor(self, *args, **kwargs): + """Return a new DBAPI cursor for the underlying connection. + + This method is a proxy for the ``connection.cursor()`` DBAPI + method. + + """ + return self.connection.cursor(*args, **kwargs) + + def __getattr__(self, key): + return getattr(self.connection, key) + + def detach(self): + """Separate this connection from its Pool. + + This means that the connection will no longer be returned to the + pool when closed, and will instead be literally closed. The + containing ConnectionRecord is separated from the DB-API connection, + and will create a new connection when next used. + + Note that any overall connection limiting constraints imposed by a + Pool implementation may be violated after a detach, as the detached + connection is removed from the pool's knowledge and control. + """ + + if self._connection_record is not None: + rec = self._connection_record + _refs.remove(rec) + rec.fairy_ref = None + rec.connection = None + # TODO: should this be _return_conn? + self._pool._do_return_conn(self._connection_record) + self.info = self.info.copy() + self._connection_record = None + + if self._pool.dispatch.detach: + self._pool.dispatch.detach(self.connection, rec) + + def close(self): + self._counter -= 1 + if self._counter == 0: + self._checkin() + + +class SingletonThreadPool(Pool): + + """A Pool that maintains one connection per thread. + + Maintains one connection per each thread, never moving a connection to a + thread other than the one which it was created in. + + .. warning:: the :class:`.SingletonThreadPool` will call ``.close()`` + on arbitrary connections that exist beyond the size setting of + ``pool_size``, e.g. if more unique **thread identities** + than what ``pool_size`` states are used. This cleanup is + non-deterministic and not sensitive to whether or not the connections + linked to those thread identities are currently in use. + + :class:`.SingletonThreadPool` may be improved in a future release, + however in its current status it is generally used only for test + scenarios using a SQLite ``:memory:`` database and is not recommended + for production use. + + + Options are the same as those of :class:`.Pool`, as well as: + + :param pool_size: The number of threads in which to maintain connections + at once. Defaults to five. + + :class:`.SingletonThreadPool` is used by the SQLite dialect + automatically when a memory-based database is used. + See :ref:`sqlite_toplevel`. + + """ + + def __init__(self, creator, pool_size=5, **kw): + kw['use_threadlocal'] = True + Pool.__init__(self, creator, **kw) + self._conn = threading.local() + self._all_conns = set() + self.size = pool_size + + def recreate(self): + self.logger.info("Pool recreating") + return self.__class__(self._creator, + pool_size=self.size, + recycle=self._recycle, + echo=self.echo, + logging_name=self._orig_logging_name, + use_threadlocal=self._use_threadlocal, + reset_on_return=self._reset_on_return, + _dispatch=self.dispatch, + dialect=self._dialect) + + def dispose(self): + """Dispose of this pool.""" + + for conn in self._all_conns: + try: + conn.close() + except Exception: + # pysqlite won't even let you close a conn from a thread + # that didn't create it + pass + + self._all_conns.clear() + + def _cleanup(self): + while len(self._all_conns) >= self.size: + c = self._all_conns.pop() + c.close() + + def status(self): + return "SingletonThreadPool id:%d size: %d" % \ + (id(self), len(self._all_conns)) + + def _do_return_conn(self, conn): + pass + + def _do_get(self): + try: + c = self._conn.current() + if c: + return c + except AttributeError: + pass + c = self._create_connection() + self._conn.current = weakref.ref(c) + if len(self._all_conns) >= self.size: + self._cleanup() + self._all_conns.add(c) + return c + + +class QueuePool(Pool): + + """A :class:`.Pool` that imposes a limit on the number of open connections. + + :class:`.QueuePool` is the default pooling implementation used for + all :class:`.Engine` objects, unless the SQLite dialect is in use. + + """ + + def __init__(self, creator, pool_size=5, max_overflow=10, timeout=30, + **kw): + r""" + Construct a QueuePool. + + :param creator: a callable function that returns a DB-API + connection object, same as that of :paramref:`.Pool.creator`. + + :param pool_size: The size of the pool to be maintained, + defaults to 5. This is the largest number of connections that + will be kept persistently in the pool. Note that the pool + begins with no connections; once this number of connections + is requested, that number of connections will remain. + ``pool_size`` can be set to 0 to indicate no size limit; to + disable pooling, use a :class:`~sqlalchemy.pool.NullPool` + instead. + + :param max_overflow: The maximum overflow size of the + pool. When the number of checked-out connections reaches the + size set in pool_size, additional connections will be + returned up to this limit. When those additional connections + are returned to the pool, they are disconnected and + discarded. It follows then that the total number of + simultaneous connections the pool will allow is pool_size + + `max_overflow`, and the total number of "sleeping" + connections the pool will allow is pool_size. `max_overflow` + can be set to -1 to indicate no overflow limit; no limit + will be placed on the total number of concurrent + connections. Defaults to 10. + + :param timeout: The number of seconds to wait before giving up + on returning a connection. Defaults to 30. + + :param \**kw: Other keyword arguments including + :paramref:`.Pool.recycle`, :paramref:`.Pool.echo`, + :paramref:`.Pool.reset_on_return` and others are passed to the + :class:`.Pool` constructor. + + """ + Pool.__init__(self, creator, **kw) + self._pool = sqla_queue.Queue(pool_size) + self._overflow = 0 - pool_size + self._max_overflow = max_overflow + self._timeout = timeout + self._overflow_lock = threading.Lock() + + def _do_return_conn(self, conn): + try: + self._pool.put(conn, False) + except sqla_queue.Full: + try: + conn.close() + finally: + self._dec_overflow() + + def _do_get(self): + use_overflow = self._max_overflow > -1 + + try: + wait = use_overflow and self._overflow >= self._max_overflow + return self._pool.get(wait, self._timeout) + except sqla_queue.Empty: + # don't do things inside of "except Empty", because when we say + # we timed out or can't connect and raise, Python 3 tells + # people the real error is queue.Empty which it isn't. + pass + if use_overflow and self._overflow >= self._max_overflow: + if not wait: + return self._do_get() + else: + raise exc.TimeoutError( + "QueuePool limit of size %d overflow %d reached, " + "connection timed out, timeout %d" % + (self.size(), self.overflow(), self._timeout), code="3o7r") + + if self._inc_overflow(): + try: + return self._create_connection() + except: + with util.safe_reraise(): + self._dec_overflow() + else: + return self._do_get() + + def _inc_overflow(self): + if self._max_overflow == -1: + self._overflow += 1 + return True + with self._overflow_lock: + if self._overflow < self._max_overflow: + self._overflow += 1 + return True + else: + return False + + def _dec_overflow(self): + if self._max_overflow == -1: + self._overflow -= 1 + return True + with self._overflow_lock: + self._overflow -= 1 + return True + + def recreate(self): + self.logger.info("Pool recreating") + return self.__class__(self._creator, pool_size=self._pool.maxsize, + max_overflow=self._max_overflow, + timeout=self._timeout, + recycle=self._recycle, echo=self.echo, + logging_name=self._orig_logging_name, + use_threadlocal=self._use_threadlocal, + reset_on_return=self._reset_on_return, + _dispatch=self.dispatch, + dialect=self._dialect) + + def dispose(self): + while True: + try: + conn = self._pool.get(False) + conn.close() + except sqla_queue.Empty: + break + + self._overflow = 0 - self.size() + self.logger.info("Pool disposed. %s", self.status()) + + def status(self): + return "Pool size: %d Connections in pool: %d "\ + "Current Overflow: %d Current Checked out "\ + "connections: %d" % (self.size(), + self.checkedin(), + self.overflow(), + self.checkedout()) + + def size(self): + return self._pool.maxsize + + def checkedin(self): + return self._pool.qsize() + + def overflow(self): + return self._overflow + + def checkedout(self): + return self._pool.maxsize - self._pool.qsize() + self._overflow + + +class NullPool(Pool): + + """A Pool which does not pool connections. + + Instead it literally opens and closes the underlying DB-API connection + per each connection open/close. + + Reconnect-related functions such as ``recycle`` and connection + invalidation are not supported by this Pool implementation, since + no connections are held persistently. + + .. versionchanged:: 0.7 + :class:`.NullPool` is used by the SQlite dialect automatically + when a file-based database is used. See :ref:`sqlite_toplevel`. + + """ + + def status(self): + return "NullPool" + + def _do_return_conn(self, conn): + conn.close() + + def _do_get(self): + return self._create_connection() + + def recreate(self): + self.logger.info("Pool recreating") + + return self.__class__(self._creator, + recycle=self._recycle, + echo=self.echo, + logging_name=self._orig_logging_name, + use_threadlocal=self._use_threadlocal, + reset_on_return=self._reset_on_return, + _dispatch=self.dispatch, + dialect=self._dialect) + + def dispose(self): + pass + + +class StaticPool(Pool): + + """A Pool of exactly one connection, used for all requests. + + Reconnect-related functions such as ``recycle`` and connection + invalidation (which is also used to support auto-reconnect) are not + currently supported by this Pool implementation but may be implemented + in a future release. + + """ + + @memoized_property + def _conn(self): + return self._creator() + + @memoized_property + def connection(self): + return _ConnectionRecord(self) + + def status(self): + return "StaticPool" + + def dispose(self): + if '_conn' in self.__dict__: + self._conn.close() + self._conn = None + + def recreate(self): + self.logger.info("Pool recreating") + return self.__class__(creator=self._creator, + recycle=self._recycle, + use_threadlocal=self._use_threadlocal, + reset_on_return=self._reset_on_return, + echo=self.echo, + logging_name=self._orig_logging_name, + _dispatch=self.dispatch, + dialect=self._dialect) + + def _create_connection(self): + return self._conn + + def _do_return_conn(self, conn): + pass + + def _do_get(self): + return self.connection + + +class AssertionPool(Pool): + + """A :class:`.Pool` that allows at most one checked out connection at + any given time. + + This will raise an exception if more than one connection is checked out + at a time. Useful for debugging code that is using more connections + than desired. + + .. versionchanged:: 0.7 + :class:`.AssertionPool` also logs a traceback of where + the original connection was checked out, and reports + this in the assertion error raised. + + """ + + def __init__(self, *args, **kw): + self._conn = None + self._checked_out = False + self._store_traceback = kw.pop('store_traceback', True) + self._checkout_traceback = None + Pool.__init__(self, *args, **kw) + + def status(self): + return "AssertionPool" + + def _do_return_conn(self, conn): + if not self._checked_out: + raise AssertionError("connection is not checked out") + self._checked_out = False + assert conn is self._conn + + def dispose(self): + self._checked_out = False + if self._conn: + self._conn.close() + + def recreate(self): + self.logger.info("Pool recreating") + return self.__class__(self._creator, echo=self.echo, + logging_name=self._orig_logging_name, + _dispatch=self.dispatch, + dialect=self._dialect) + + def _do_get(self): + if self._checked_out: + if self._checkout_traceback: + suffix = ' at:\n%s' % ''.join( + chop_traceback(self._checkout_traceback)) + else: + suffix = '' + raise AssertionError("connection is already checked out" + suffix) + + if not self._conn: + self._conn = self._create_connection() + + self._checked_out = True + if self._store_traceback: + self._checkout_traceback = traceback.format_stack() + return self._conn + + +class _DBProxy(object): + + """Layers connection pooling behavior on top of a standard DB-API module. + + Proxies a DB-API 2.0 connect() call to a connection pool keyed to the + specific connect parameters. Other functions and attributes are delegated + to the underlying DB-API module. + """ + + def __init__(self, module, poolclass=QueuePool, **kw): + """Initializes a new proxy. + + module + a DB-API 2.0 module + + poolclass + a Pool class, defaulting to QueuePool + + Other parameters are sent to the Pool object's constructor. + + """ + + self.module = module + self.kw = kw + self.poolclass = poolclass + self.pools = {} + self._create_pool_mutex = threading.Lock() + + def close(self): + for key in list(self.pools): + del self.pools[key] + + def __del__(self): + self.close() + + def __getattr__(self, key): + return getattr(self.module, key) + + def get_pool(self, *args, **kw): + key = self._serialize(*args, **kw) + try: + return self.pools[key] + except KeyError: + self._create_pool_mutex.acquire() + try: + if key not in self.pools: + kw.pop('sa_pool_key', None) + pool = self.poolclass( + lambda: self.module.connect(*args, **kw), **self.kw) + self.pools[key] = pool + return pool + else: + return self.pools[key] + finally: + self._create_pool_mutex.release() + + def connect(self, *args, **kw): + """Activate a connection to the database. + + Connect to the database using this DBProxy's module and the given + connect arguments. If the arguments match an existing pool, the + connection will be returned from the pool's current thread-local + connection instance, or if there is no thread-local connection + instance it will be checked out from the set of pooled connections. + + If the pool has no available connections and allows new connections + to be created, a new database connection will be made. + + """ + + return self.get_pool(*args, **kw).connect() + + def dispose(self, *args, **kw): + """Dispose the pool referenced by the given connect arguments.""" + + key = self._serialize(*args, **kw) + try: + del self.pools[key] + except KeyError: + pass + + def _serialize(self, *args, **kw): + if "sa_pool_key" in kw: + return kw['sa_pool_key'] + + return tuple( + list(args) + + [(k, kw[k]) for k in sorted(kw)] + ) diff --git a/venv/Lib/site-packages/sqlalchemy/processors.py b/venv/Lib/site-packages/sqlalchemy/processors.py new file mode 100644 index 0000000..860a55b --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/processors.py @@ -0,0 +1,148 @@ +# sqlalchemy/processors.py +# Copyright (C) 2010-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# Copyright (C) 2010 Gaetan de Menten gdementen@gmail.com +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""defines generic type conversion functions, as used in bind and result +processors. + +They all share one common characteristic: None is passed through unchanged. + +""" + +import codecs +import re +import datetime +from . import util + + +def str_to_datetime_processor_factory(regexp, type_): + rmatch = regexp.match + # Even on python2.6 datetime.strptime is both slower than this code + # and it does not support microseconds. + has_named_groups = bool(regexp.groupindex) + + def process(value): + if value is None: + return None + else: + try: + m = rmatch(value) + except TypeError: + raise ValueError("Couldn't parse %s string '%r' " + "- value is not a string." % + (type_.__name__, value)) + if m is None: + raise ValueError("Couldn't parse %s string: " + "'%s'" % (type_.__name__, value)) + if has_named_groups: + groups = m.groupdict(0) + return type_(**dict(list(zip( + iter(groups.keys()), + list(map(int, iter(groups.values()))) + )))) + else: + return type_(*list(map(int, m.groups(0)))) + return process + + +def py_fallback(): + def to_unicode_processor_factory(encoding, errors=None): + decoder = codecs.getdecoder(encoding) + + def process(value): + if value is None: + return None + else: + # decoder returns a tuple: (value, len). Simply dropping the + # len part is safe: it is done that way in the normal + # 'xx'.decode(encoding) code path. + return decoder(value, errors)[0] + return process + + def to_conditional_unicode_processor_factory(encoding, errors=None): + decoder = codecs.getdecoder(encoding) + + def process(value): + if value is None: + return None + elif isinstance(value, util.text_type): + return value + else: + # decoder returns a tuple: (value, len). Simply dropping the + # len part is safe: it is done that way in the normal + # 'xx'.decode(encoding) code path. + return decoder(value, errors)[0] + return process + + def to_decimal_processor_factory(target_class, scale): + fstring = "%%.%df" % scale + + def process(value): + if value is None: + return None + else: + return target_class(fstring % value) + return process + + def to_float(value): + if value is None: + return None + else: + return float(value) + + def to_str(value): + if value is None: + return None + else: + return str(value) + + def int_to_boolean(value): + if value is None: + return None + else: + return bool(value) + + DATETIME_RE = re.compile( + r"(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)(?:\.(\d+))?") + TIME_RE = re.compile(r"(\d+):(\d+):(\d+)(?:\.(\d+))?") + DATE_RE = re.compile(r"(\d+)-(\d+)-(\d+)") + + str_to_datetime = str_to_datetime_processor_factory(DATETIME_RE, + datetime.datetime) + str_to_time = str_to_datetime_processor_factory(TIME_RE, datetime.time) + str_to_date = str_to_datetime_processor_factory(DATE_RE, datetime.date) + return locals() + +try: + from sqlalchemy.cprocessors import UnicodeResultProcessor, \ + DecimalResultProcessor, \ + to_float, to_str, int_to_boolean, \ + str_to_datetime, str_to_time, \ + str_to_date + + def to_unicode_processor_factory(encoding, errors=None): + if errors is not None: + return UnicodeResultProcessor(encoding, errors).process + else: + return UnicodeResultProcessor(encoding).process + + def to_conditional_unicode_processor_factory(encoding, errors=None): + if errors is not None: + return UnicodeResultProcessor(encoding, errors).conditional_process + else: + return UnicodeResultProcessor(encoding).conditional_process + + def to_decimal_processor_factory(target_class, scale): + # Note that the scale argument is not taken into account for integer + # values in the C implementation while it is in the Python one. + # For example, the Python implementation might return + # Decimal('5.00000') whereas the C implementation will + # return Decimal('5'). These are equivalent of course. + return DecimalResultProcessor(target_class, "%%.%df" % scale).process + +except ImportError: + globals().update(py_fallback()) diff --git a/venv/Lib/site-packages/sqlalchemy/schema.py b/venv/Lib/site-packages/sqlalchemy/schema.py new file mode 100644 index 0000000..aa7b4f0 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/schema.py @@ -0,0 +1,70 @@ +# schema.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Compatibility namespace for sqlalchemy.sql.schema and related. + +""" + +from .sql.base import ( + SchemaVisitor + ) + + +from .sql.schema import ( + BLANK_SCHEMA, + CheckConstraint, + Column, + ColumnDefault, + Constraint, + DefaultClause, + DefaultGenerator, + FetchedValue, + ForeignKey, + ForeignKeyConstraint, + Index, + MetaData, + PassiveDefault, + PrimaryKeyConstraint, + SchemaItem, + Sequence, + Table, + ThreadLocalMetaData, + UniqueConstraint, + _get_table_key, + ColumnCollectionConstraint, + ColumnCollectionMixin + ) + + +from .sql.naming import conv + + +from .sql.ddl import ( + DDL, + CreateTable, + DropTable, + CreateSequence, + DropSequence, + CreateIndex, + DropIndex, + CreateSchema, + DropSchema, + _DropView, + CreateColumn, + AddConstraint, + DropConstraint, + DDLBase, + DDLElement, + _CreateDropBase, + _DDLCompiles, + sort_tables, + sort_tables_and_constraints, + SetTableComment, + DropTableComment, + SetColumnComment, + DropColumnComment, +) diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__init__.py b/venv/Lib/site-packages/sqlalchemy/sql/__init__.py new file mode 100644 index 0000000..aa81138 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/__init__.py @@ -0,0 +1,101 @@ +# sql/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from .expression import ( + Alias, + ClauseElement, + ColumnCollection, + ColumnElement, + CompoundSelect, + Delete, + FromClause, + Insert, + Join, + Select, + Selectable, + TableClause, + TableSample, + Update, + alias, + and_, + any_, + all_, + asc, + between, + bindparam, + case, + cast, + collate, + column, + delete, + desc, + distinct, + except_, + except_all, + exists, + extract, + false, + False_, + func, + funcfilter, + insert, + intersect, + intersect_all, + join, + label, + lateral, + literal, + literal_column, + modifier, + not_, + null, + nullsfirst, + nullslast, + or_, + outerjoin, + outparam, + over, + quoted_name, + select, + subquery, + table, + tablesample, + text, + true, + True_, + tuple_, + type_coerce, + union, + union_all, + update, + within_group +) + +from .visitors import ClauseVisitor + + +def __go(lcls): + global __all__ + from .. import util as _sa_util + + import inspect as _inspect + + __all__ = sorted(name for name, obj in lcls.items() + if not (name.startswith('_') or _inspect.ismodule(obj))) + + from .annotation import _prepare_annotations, Annotated + from .elements import AnnotatedColumnElement, ClauseList + from .selectable import AnnotatedFromClause + _prepare_annotations(ColumnElement, AnnotatedColumnElement) + _prepare_annotations(FromClause, AnnotatedFromClause) + _prepare_annotations(ClauseList, Annotated) + + _sa_util.dependencies.resolve_all("sqlalchemy.sql") + + from . import naming + +__go(locals()) diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..108340eeba2030fba4b8f92d51d5c20fa981cec6 GIT binary patch literal 2275 zcmY+FTT>KA6o6-DXJ=<G+;56_!waqoq9`gN0t*;Z(IkbM)TvEP4Lc1E&Y4-dXA!L3 zCsLK<J%1uUAb+M`^W<OfB{^pY!7MdjpXu&7eXh-1PfzLK?-$<7f?@n)eD$+PUZzLQ zn}!h>ff*TSf{7Mbn1dYJU}GNg=zxO-D4+{27NLkGC}A1OSb+-mKo9mpFZMwn_Cr4o zzyJ=yAP&J04#O~xzzB}QD2~Axo`ExX7S7^0jN>^thZ8V?=ixkFfD3pLF5)G)gqPtm zPQoN!fh%|wuHrSghS%XbPQeu3fE!qaD&B;fcnfafZMcooFpV=XgLmK#&cZC-g}ZnU z?%^EF;eEJ|58wgL!#qBOhxiB{;bVA=3$TDs;0Z3mB0hzuxCBf144&aKEaM8S;3}+Q z4QeLG#ERB%9o9+C1@^B7Y={l<{Kz;mX(qpERGm*0JEL0D`IRX2mF}rUeoKk<NT7&Q z-Cs+h7RJO#G}17MX+wAB%ZA=sg3Y8A2b+R-XlISaROr%%Ofb`>ofokZGS&9CNf>MA zcxNda{jEso@@pQBw`VV#J~nB^dD9Gh(wFzU=GlH6c$#>~j3Q6xeAUo}Es=f@BG$#N zFb<l&^ikUlUx_YLU1*R4ti~aM{LxO3nP~@tK6isqrD06w7sNqBG*eHPjs-eHJK})W z6)&dJr(OANKO)`E1`lM&b}Me^5_3C@QX#byW*!&Am|hhJrsqWTSf{<qvB*dMmWXtL zZIeC=VJ7q*3*NE4E+#>^9g=E0PRKkvZjq7_Q*DP*rMlRaBcH@vB0XJ9TBNFbic8{g zOp<+4QQm7MsR+E-M{+`CiE~wJYp*5bq0XmSPP&82(qO0LyS7u=qEx&ldfG`_lps%+ z)5E49JB4h}S}W!vDRzghqfRSJVC6%Yz6)b-M<%Ui)%;RuZ$lNPiEMBc^3SEmrYA>_ z`jZftRKwKQ9j@41U_{m*Mw<K0*fs+@$p7lLja~c5Sfh$QGW~qu1civYy<?%<$<Y^1 z;6~K(9UIG?eD}Q=QP+1I(`Z|1Z`(eyx>8@;$)t9^o$E^GZvXB;Q0jJy6O_q@!A^lR z7}PbDSD*OuAvf$+8b+T;Hcnh%)A^aBN8Kc}jbA3~R>$mEZF83t9GT6D-;G_fZ5^44 z=H42nv1bJ)DYmrfy&{%W>dRDd%(NRS%6=;n)tt6wW-0faw_LKdOXs^Dm0FVu>@VZ< zc5R{lhB{5v52w`BVSP=g_i56suO`X+>6d=&?{u4bdh<}F0;iE`MacTTi1+JJxK*b_ zi0P)^cu)IOUF}7F)OaWGkj1)3=Y*-}%`^``7Z-O#EDoA-=_huP(`6fu`Ektic9NOJ zC}~h%EX|zklHDuD(!*X;3Mxe5`7u?o&-aPa#g*(u1mySV$;<z*nNoH)d`EpqP7j^z zI=y8+_MTciLn3W+C{SU%QytQVd`?E{!f~vq=o3jDp!4K1Qe%5usvdRysbO-HzL2l^ zGnYDqZ$l;D@Z4Jhl(M$rPLUi1nP+hI>PjG*A`V2{2!+yRDO3{elh08kf2Q@StL>-} zDN3F4JuaWqqfQUVl=6Q01Ak=7?^*tlplyybOKOy0SQX2)#&i8<zgaO${6{k~pp{qE zZ-6c`in>x1Ysob$xilr`EHl|*iT<-@#>*m0B}SQ1Ve~M18GVd?#sFiGF~k^Vj4;?F zImS4{ILjDkoMTKd&ND7BE;24LE;A+>R~T0r*BI9sQ;ZvoDubI!-eTNlOfzN}cNnvb zyNr7bZbNyW@qjVUc*uCfc+6N}JYg&{o-&pg<$6~-!~##keOyPsWNO0M%9M_fBu c^>>r@6}owI7qdQI;xbW;e~poiO3oer54{MBDgXcg literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/annotation.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/annotation.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91e83f44704eae4053700980cf2e763a920ea081 GIT binary patch literal 5732 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 BeJub0 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/base.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/base.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3be6280e7fb1aa8cfcc3f3e3cc4ca19787daaf2d GIT binary patch literal 23345 zcmch9TW}oLnO^tYFc<<L2tvG>l3TDS01`On`jSXANs}TeE!QR_i<D%;q#KRt1~9-} zz|%b>F*USz4Y?%e;$$~TRjRV_dM{PGu56OZ=K7F4B`?|9r#$vUDph`oE6H;zTd7LE z??0z6GZ<1*E(=um^!1$o{O7;_|LHGJPuCv*^IPuxyO#9}EB9B%@l|Z$l5JVOwPyLY zUufCw!djtSTr1j^U-V0@(pt$c`;}I?U0JJ`YZGe|L1CxrPaN25lX6tWQSB|upY&@V zT7E4!a!_2G@{jmaA6jeEcw)w%_Gdn{@YGQ}b=055Q?s}}yK@Xr&f(fIe-78?(rd?Y z?YKXWYxC0DJkFo<PvHE7oIi*2lm023p9-Es8z*pn+CPKyGjjhV&Ohg$#rfHwg8Qd% z{=EMJ&R;+ur*Y*Bu6*8iaK*uu&jn`=N^56v?L~h9*A{T?c|7%kf6o7c|I&x{+UNb! z9c%gJ{#V}Y_By^7HM<?J<@BOvs~I(e&}nqr?QX}3x=!2c>^tFumecO~y;cy`Yqeh% zfa&EzT;lU_F?ihgB`#R2zXa~BWjmg}-tu~3@P0FFMqL%pzH{x}J2%~H@7;a-`rEf} z-?|%DR^N87z57;!ZzX?h^D4G*3A?~rvoVVW-(D;F1-}T)mwe0}eldUk1b!=i6(g98 zCvUaey~x{W1^ugSPwhIMqr1P^3fe&@a=g%4Y$nevI*+!4juSlIQ$ZMF)S=_4z-hF) zoxra*H~{_?u@RuRv4vCEMIfjH`-cDuofcO6OKGo8cVj1LM4{t%olZA$!rtCqS4F_! z-hQ*Q1#}!=?MAoSdm5a;zo}GLEf?cb7_>IysqUUezTa#_ab@?Br?$eMTWSWK{`toB zi}&6O0~OxeUkn%b@7)N(-Ke{F?~QJE_xx?I<88?h&)?Y(qo94h?cqn=dl+;c-fJ~C z?g9P5`8}_(i|4|7m_x7C*bdtJJiNEzg+YC9Kc4P&nh$z`yXPsd9iHZZIdaSHpG?PC zAA08h3zk$3eJ&T(5&S4(TupOFH0a$?9K{t4<yjmjP|jvh>YFKeoJ?-l`E&`p0VmI1 zmG+ix-PLFZu8_j#`HDe9t^<8@b$KGLx^Bm72d*2}Tvu}jj;CDrLC<TMI}@(!cN?y& zX3?5D#@!ruS(kFefvHLCPGAeG*j4RPp<1jK<!Z_#917|3Ej~7nU1aUp6muV9U^gI+ ze!}4uSJUwme!g&rFR<q=Jb1tM#2(l?)^8RbKmr(4#MADk>b6}&%3$q6{X#quXp+qV zOj!TCcB$12BS?t7D=)EW_EWL?FAlX)ztrkByjFMx2g%cDcM>G2qHebp#^uJg*X)2l zs-~ZCq$70_4Syl&!LsE%9UUi>QeRk<GGhQ_oVIqX1M5j~U=50!7$;@Nj|!))pB4s% zpV&680!5|%+1pxBw1a57>r+B(1)V@a3_c9%iY+TPy$awaU2`i~F2#k-PFx~`#)aKS zW9jEe@;F*rL=#~JJF7HhpR&~gj?&rWWg_k>w(wW6`*b4DpM>a}@N51N{8s%be;U7& z{)~SVzcqgr!#frqb#FAi7KG9@we@~8c+~f<ITSAx@@n6Ch)Yg)6PM(n^R3`v(1IA; z>QP>XP7`u}J8(9;s$FyNcgZ|RvcBS8N*)vjxB_*+XBr#}|M<)8I<}BJJ+KFs3zaOa z2ZR<uWXlDOJd)4Iq?5Cnoo3{^e~B$fvv&W~$cXC6LutR9G|CR?_X-a7=Sk+|kAuR! z!jr<2;s;jeY{`o3-I}^!Lo(P_R2Ya<|BltEecKuoKs1Fr8rI@!Y`a1Q5Y^>y4|*}K zz3;VpfynQ;`i)>;OY)M8B`)p;`|*T9RXq8SkGi-vI=&JwzRs}`JC<Fr=j|DLrqG{1 zG)6p_j*s-1j_=oSG61O)dOf!NS)AKDAoJ7KlM+Vy0!CWgDPgRa%vj4qV=WDer!gnV zyj0YAw5d2D@li7fSF5H_6?yg8YWGgBv3*;t02#Sj#`Wd0p2}DF`m5Zn@aZDn9Ub`* z7t9es0JL5A8XgkGYGLRFdlvsd!RHR4AbX=hDEK#oi8pa!e=diMVmkVvdEO~%{f(KP zlbN=5N;_rZU{JcGu>h@p;Tyqz=(Kxb<ZJ|Fk%SM%@PKf`vcVfMq0uxAo)bROC=v>< z<8B^LaapJ%L)REgg$bs<ftL-Y3icdGrDRWy#Z>x6N;Bk%louVG<nRQ3$S7t|yqr=( zF{1<}fNT0z!xKCPVm|<naBlAsV)l)>wa3kA`eZ816Wu1<dj@aP-qM<lRJ~I?5Dj-= zkJoS^dpw05^xNzcI|5_ufHmF+i$5qNBwmP%t)K%{*Xed#KiKqottf;f;(N;BE>p~+ z=O!;v^utoT@*Uh2QB<<}uN<aPH0!!Kg_pbrjZqAHZQ&}xWn7Vo51(Ruz+jN3zRm75 z0;@2?p3>nV<dH!_8WA5^o7ym_eheY8W79YoMjRidX8JN;`3iPG+{zPMKR%!uE!?o~ zpL$ZD4Dl($3!8-#Xhu*v52UieLce@2bkBvWAnugV)n7()iewkpLT?i?kR~lGJK{N> zN@NS!XRW5CB#(zsA|}ymc>a6X1QR8@Q0TvU7!x!n#^b(_fs}7MIc684YovjDNk3Mh zHeTDvW2?(Y)NwqjXnv{ZxFf#RIqpP&{tAzXQ1w;rzQ!H3QI57Uqwfp2aRFOM1F>2X zqpw=6RVS;ZYDw{K16%q>oL<Eia`%5{E@1MG4w(rzU>@}U^=C2@U>`W16Yd3#=4P`Y zR)CpYV-wtlc^6@Wi{*p?jq?beR<}jV1f~e(E4)N?-ZT-w5KC+jY0c~Ssg067V>mZ^ zHnBh`L-@zvRO%74O;ES1_u7B6SHboqQ&@MYAQ<Zp$jo-|q+!R(FmVuTnJJP7pon*_ z^F7i>1fv7Ows2tYQ0SHpAe%4)0HdsE<v>aVkp`#?PpjKeS8+9=)M2TlNeZ_U(<m<< z3_K!44vB|P15R8bL#b<MU-R)BJi3bAQ=lPj_pl|<<PEh1JHk4tQsUI4)3bmR8i+gA zZbi-Wm%`@`6&0-ayn&)8OoL*qN6|<l*dhNtCOl$y+cmp3tSb#nsU4D96`Zhi(%2}h zK>^2D2be5dP?Gql(mUm6j<rQ@k^8@oEkQ}%s4?9iR@FmTq5G>l&(yN%{24>1!1;}5 z_nRu-)#cjL1z9GvMx<GWYdMn9ejPmq?3I_Q6Odsgx~|m<+*jUfU`zjq;H%ieJ?!!l zEGX$H4`OQNpt3WeV?8*l29rA8!<ZcI9PtsI(~&t`nZlLnU<yYwCO&sm-cI6kvpPDr zcFaEpew&M@Z)lh28y~<*3%%FDu{T4;j+)`N*m~Xu17Q2~eH;%_AukLW1Kai<2BV^= zkx3wtR*#y|wv$K`hq9}I=u)J54aC44D7vN&vl(%97arC{>EPmeeF&^|IACBIk1-`_ z6h^4@aM6n*#izCHVqU7J-MFdvTz44WV(<%M)=MBaJ=g1y#sOUO?!}ARd7}gO85%bp zpBN~ZN800@*z4FtH8Czh=6zBclze-)s+<TGoDW}%7Q_$1?t{|qgt`E^TpC!H;FR+h zmUscYCWeal>tNq&J`{q}fv>vT1U7RHYoc=;I7pAWD$^S>Z_#!W@k>sf88C-;v%Xc& z50}?&b^PFQJwcj_IbT|6?;9Pqu;MI0j&4QUI9f$?a5=*jUk<y7{s6Avv2$6sw6y3g zt~iSu*rlz|o<kFtznXhnW}cCU(TN`dW!*8s2@J+__Mj#cK!p|v!Xt0r@q8aaM#M8C z<z)m(noX#NPkx5U1+gT7g1U1Ts7$-dZh*SF51T&1#Gb?84(BF;&Y>}f8;wS=7eS?O zAl~4qX1A9$6fy|unXp4jy&-9JBiQy@o6_D0)p8iCpanBMp%ZR*doACI_V=2IGqn(- z7E5Fw0|&}-z~ZLkkrNsLBRyU=ATRQO<@QFP*dq{@v~I}90RjTrC{6>xQY{qx^V?mp zKG1G#n6$YO3;>7>Hp-Kr<DaL!1G(S_=nXJ&Zo!A>0+X0E<TT)ccH4Ncz1M6>aNZH5 z1&CfZ8`~mT;AEl)z?Z;+_<aIGp7JkBR5Tfx1Z^(LBn@L(kJVr|ae-5i90v0w$iCtP zE#Sh-gx8_C00da*D6bhJqK;Vu4zqTKI*_q9JB?Nk2=N$|2QlR_@tFuWvn*F2K|ugO zTn?)S<B<b=M9oHX57uZJ>oS5Q<*}T+7Ex~GdNz(wBJKtmsT{wS_x*>_aBG;r{BDpj zgyanpJZSNiTFEP%V+e61VGc>V25`2KLvCgy53!PA2nqIq-c;cE`<R$(;ER--OqRgU zMo86zNZ_4see;^m8UU$!rVc^4-F<`*qDrJM1Tt~FpS6{Avyx^<+K^q43;Q_{Z3s1? z;SowDr3)no(2a-RG81$$(ZHO*UK+v~_af9FU}g#k3>5emFFF_Muh+klN%__OyEnAC z;4GdC7m?oqy@)07DXAp{%QG(}mcV9ntEU2gxukADPa;#W`^Z(n76@MLOXNs>3D=j) zak0?~)ny#7O%I_0u|=)-<5Q{3q{eqMQ8!|V##3nnkUzgg^*oE6wN$f_VSqzj#V-O3 zXUs6QRWzi9hMb7hYG;lDF&0aEG2ZJ%C~L^1ykj>?tB}{)QKXkg@vKA7i6&1;#e^KS z0)rSyhnkym2$W3H;Ru9btex!uCpvuHgiKe>ttQi&>+4Af8{umSM_gH7PrC$e+Fk^P zc)J5FlAw&T6%zUpTZAcwtPXDO!Ke^7mAwcv_+Lxg>2+GoT?%#>c8R5yH#h5M&`DTz zJ%_RtE!fkxFl^&FgGm#d>~^}3!0@St(+sTvp%Gx{BQN}ks@%jR`sR^piZK5gWeRkV zjL|4;tqD^|h@J5WWF{3aa=e<^9yI|h;-*AWn{=Nx0)tizlGIu-L&{C+cy|0!#*Wzo z$QyOGGL97`{cMcutZFvp&`QHt0cM>9DZ@aGF_meGJi9+LymjY1>{sMu8OKPhDoV+A zHY|9RUjRFecdz4_gl6;z{vDpwZsn=|*<r37esG9y8JD=uH|Z18uVn0@|CMV_xQ|fi zep}@Bvj=1lSO=KDhDOFiD;eAW$c`u}tYUwD7)0Gj|9F;&PFNyLqXQ5nS#*FgMZ!yW z;=q1_<i-x7xo{}XFs2(7d7ea4N^vz2r*Wy(_58KtsqDxcqqxM0Kt`u(@MT2pkpz7d zzL}dVLBiukRMSC#uobTR1t61DtyP#pkfCJvUmPCmn0L}K(qW?WBy+;ZW9l#!qJm^E z^(bE%6n7YYMbN5=@!M9X{&*!S10(Q|4-nP8f0i!Rpn%xPYx-FPElQ^$ml5}V?E{_d zEFh8f%5qiIj=G87#4VRVN?hsf!6^-hI_ko}M?efYZZI&C*@#BBRo8Ws%msClVx}^- zef1_!Mi6y!%%IU)_+Rj>);vH}1zKmWFbBOeZ}-oABGUN&5HXWL1aVAg6LHuWsVOEw zHSfQI-v46N5yUDbs}nuy8n4E1eL|5dl(Ucmkb;>m()rVzB4W${w-XJN+=pg*lkUS~ zk2NG>58|=CZWwJr?LBC@B8T(s7%S9yBpwnZa%#OpHjjG2=nfuid*3Ag6P8T0)OQ!N zo)+(acTqZ9yq^iMOfKi`sA0kVe<VbW%z+Hugb(x#30l(GhN*YDdp$fE+yINmCs~9; z>kCdgLOgK(v_Gxh2i-hHe${d7zhl$E-s9lv=Z6W@aDRfEl%r`7kr{+A{}T=dc37p1 zdi);-<kLSzA_b)CBaL#v#9J|}M+F2#B&C9Y2>6`!X_BZeJt^<cfS|$I2%%Jv%6%WH z-11HZsodW)soV+FtCh*?Iw^xh_T&LXihk>o^<-i&u~S2=tdd9$CU6j7nb?^clrLG| zu^!BP+wv=e3c@gdHYn>~^fB#EuvN4(vsu)^ng=uH-3s4)F!O;<6joRJQ{P*vzxwrM z7yIRx`fpt`E^*!sO`LR)Aj1Owm^7;7O0TRqTPU5PnJp=IMU~b6)$3koF-lyq%;s~> zsjtB!=RF~8+<|wLb5zVUnLf`c^$weR{aV5o=M?#s#aj6=Me-Kz4!KX~6mlg66~twj ziH+@e^6vhgDFP^OscvsiQhVBl{~|il)L25+tCNXZ^)23!)EMo2bq6~LWoGn6ZN~O) zY`3-DUj<7F^NX}q@9~)tQG3cY0mUHENRYjFEt~=7LiS)4OQ<5M*;BOi@n5xPk!PPl zo}HHelw{oJ#RK3uGVsOz%frO}v@TYc=f=g3<0GOt442&^&@=<$V~DVHoBhCd5j8>_ zE7}(mBCe*VEc8NRFxW$cB<M6yeyHB&>mP6@8jC!uvAoWs3>k7HacFu~Kf)%3PSqo& z!qk^$&P<i(Dz!p&YUZS<zXtB7e+2F-Hpv_3W6?Si&3Z%FN#(fqR$3a=e-}P8EJbgm z!VqFTDH3v;5zG2eV*_<y0v}g%H9;`a8(k#Ax`_{)DmTKzO=QRmH~<&Y^1&XqkK~V- z1|Qfi0;xKCR1z|xqDGVy8XKiEG;eTJV)db^vWe|clSZ+52ER>~WCm2ypU=iwPXG>> zp&FO)WCLTTA;66<vt=4MOlc}!8A5y!7p`I(Q}wh~3LxkXSm9KZ(7%*?GW2hXJ`w(x z3aF`ITGv2H=$}PhQ*rI)<Dg+GL4RQiysk?jYOWI0RA<s%3gmJ4|Hj}?qc~YR62kEq zv<Q??w~hhA%~^m>L9qiu><bL@nP3ilCsRVl5ELCmc64#G*J)f_2VEfXxxSK|BN>BO zMs}`kvGo=4n#?=UUErAPnFxX2bp!9+;YvT!j?{`UqX<o7!z!E;tpghwg{wum@{(LZ z29jZT7FN)*MtZd1mx08JcLDR}+zBE`$If|()YSQe@hh%gI&Kq+j6C?f!R980=J-98 zkGPE@n0}MStz>_t?iC)s1hJ5kFv$vry0cEYDs>`pBMUlS?{+#mjlAxllC>E!(wM2B z*1p(D>ATt7b`TN>)+MO-_Pe*!2ESj2S5`+IEJ9M>;(k4u$t2jFwnd$x(?aP0Q!MH5 zEEs=Z!Vo>xVFW|Rew^N3x0AMl%Vq$h{`y6o8%P^Ztc`p|z=#``D`MD8lB5kM=8g_& zL(gT%#EA+b07X-j(G38h!whudjng`6A7-y##w3x1m%b~8RlNas#7PeK?=PnyaH5A^ zOL(PzXi!UDFY2<`ycu1-t9tn?3#z6~n7k{28L;7oP|2nPlQe9?w##@pfj%K8Zx0oF zxd;SAH264y12!c_9HvnmXCVV)L1+W~jW}r=A-a%dLJOKJIk1f$nIm!*nqgPR=8@z< z0p$YXP0oT2*+bbiJY+c*>M2g{1PBZ1nXPBUdl(_>TEsY;JxCdoTuB>A$2~TF@-!>^ zQ}nvDyq;O~9AyHk@WNpzhn`<MI)aM+&lX3nEFPLEtSi`uorRzpjKeH82+QkP*jZX$ zA(|;8kXQpJ>(q%zrjahwNFKee1|~BKT>Z|{a(!)Pc%u64#o-J8F99aCP<W-}S6@XK zm}*dBzVV!Uh%M9`xSLeTzQ`vtn@55Y#}2*nO=?{}Wxc@YZlx^yV*l*TR4pE!$JF{@ zXoxDChF5q6JK7!eR3w3Wf%Pl!S43kRpu`f|q;$f1QjS<eSr%<l+^Hb3OAklaScA%$ zy`piBH7NoE*%>k@6UHfI>-E+62oj17)fBbR|L88CJSUDd@#i-cBoC>D>s2_VFv3z1 zN}9MS^iX%OUP8#h3^rI=PBN*?QjUFz%?&{axu`Sub(H#-Hjqt<Cr~oNsIHh?+9u)* z#?QUi5s);Nq1dGEGd^MFG+;OdSw9F)8VjfwrO6*3X+x?Y1;r&7vN9mH;a{}8jR5Ou z+TqsdbWa%Y9;cIBsL-T`>0Uulr9Y>;r@xzwJDpmB$xWt~2>?zlC+a6GlGMS#&S#pe z6{ALI+$2UWg@zWEi%>bp$a)IrCNM84%v@X*l>LxssOSfik{X3;dY!~DwW~+CaloO| z$g#@(W4Yn#CJv4EIvxxerQ&Eo#FL5@(sap%m?eDXa4E!q0mY>UXHYVVD)B)<o;@=_ z9r%MYaCTv8>sm+es{r6#QJ*^Rnvt=wDL{njwTw2`L0zc;Pwa!IZ~@&?p(c@XtU7R- zn>59kd<Na;P$>L?$KhruRHvvAez!>uZwHOtP+Vi=EOh=b1prEkH$5!wlDTshxbS0v z32#s-a`51hFzhy(5SOUA*pQ`7>D#>y7JQ*g&4fk*W7yyqW6W>_qoA&`g5D(;$qEkD z!K|1mkL}I4qT3lEd3oxuaLyU%vucbS&Vb|#HVMC4=pW6|0NY+ZI?hvx=ZwW4W<pl` zrPJw}F#1et7o8|pj;RIiTHKLFh4xf~M}#{ErO>GOo;$elb%ILnVhvOYAi@J(sUE3L zDBk7!=^u&sDz?zUZoKE|m$94!E8Sp|PsGRWNWsKStm}&I!pRS!{!iavK|<z|81Z6~ zR?O$nqKo8VnbedpZ`}B%G!*J=2rL&Y`A9qp%3PD!%86;+{VljVu$xhn6u>gUZ`!23 z&!|Y^nxug-4i$*ePA&C|=B56HYYCAhB~Iu$H%!P72uFZsNU&}eZRzqiSq6rixycZv zc*KQ$$c1dU_jAq%I<)%F=i1fitLL5@0=1Zc`U(yxF6e*5Ln>h95W~z8g_rAGVI<d+ z>T={C{AP%vg2wjR{6+wGNtV<B5*KO%#-REE+FU#CZL%Cv!)Wd#dj9)(V01{QbD+9R z$-~3fz5Fo;NVUx>d#Hd{pE=CocUPf@#<}$bQT@g+7#W#A!HwTddaYJiuB>=%JaMz! zdz84Fb{bZh*5H~oqWv%42sUZXk!&Aw-6(Ynea`iD{h*Lv>XgBN3xz3MdAO_YJ|$8S z&%3S>kU`=|VMU${D%%Mc{}2HG35O@43-GVvm7&3rKKK{@33kJSTPsP`J)*cDR>8U8 zU2rf~s`^#@PUwn7EZEYkh$r<5VuUqh_3+vey<!-vjAfPZ+O%FBjH*Ui6})!TKfN{^ z9NU@m&!GBIRstjNaa^wfUYpk|f7hPVt9{o_$Qoa3?Ic$FzKq^aeFW2P?KEV`Vmy1j z+v>HU?vrGF|DRn$CARXB${os5ihqbIB<OS}tMbxs-_*v8sD;GcOk~L0&_zmW!#I`e z>#iF-a9#Kdsrw3J3!y*1*9aK1O*PL(5IqV4?I44VMQR~brUHiaSwca$lU`6ifrPwk zE)FSw&fWy)i0cP*<@EvcgpfmA%m6#q-m(sgw)Oag#O7z9^etGih>{>{wqWAhg;m99 z;&KIRzZm^dAL2}^!Q+b7iZPZm0Z!V42)GrI{77`#gaIqI8%Q)T(yQrBsO%h0|1GJE zIRmn;Fz;!NSnN%4{3leEQzvFiWVpU#bcF{cFHynnWwod*{+!Z_V-D=$<)1}ZyO2U; zS@l=B8(|Hu$oW$O%~rA0b7c&;Xkus~Mz-qHj!U!z$0uDFy_8q~ebPQw9-n$z`>C`h zCvtfF4SyRu-~O>IUx7JSQCF~#Mb37M>dL_4vKHi0m_J65>5jcyz}3Q!A=|lNZpXgN zC*%z8zR#;XKW(wa<m{65G4eMsBmde*)<^axvNu?jUd7!avCm)RY3&~sE?J!^p6!%A z#&vvSfU(+=_jzCrBqH){3t=0^g-FnJJr31VDMZX4QULZ)z0!oH&nhWkWt*xcVUXli zwv^Y+NhMvvHi?$a6^kK6gOm&tlR2vr%{#%EQdwS)kS4rK4`XSO#jg_hVXZ5UkVN)Y z;97A+OYo#yzk(aLP{q6zsO90fkhFxFWzEf?i<Z|2md3v-Y17MW5ADJTUPN1|Ey{+Z z;zY2eHAh<UqhWAp+X~9jgkh`|@kcQgLk&?@vC>#~1-wHZ%8N`}IR*kAFAgIM&Zd;O zYGnkMH<{C}5rgRj=-8+Zm%%aGO!zz-&~P}H#7z|)a;>}Y?mWGfk6BMW|FPA9F_Pn9 zVc0weW2tpT4<@c@1W{P#<Pcf<KPN1QWi5wU+RROEU0cS|<+wy_$f7{l_MCr_mvIGw zH>7bAkM3`vm$=*odQ}1<FR8!D9pzy>VY>M<!a|^|*^+$%1`QKeH6*I0uvKw(qA*4* zn3q$rK>0+Ga2;F7Bn8(1hqDoUJH7ss{vNMgLO27AS?vFH1dSapnfSxpn49mrAQG?( ziisoDL>MqFFEmt>)IY(ILX+A_-1e}t057G~rsL?acIkDn_;9EOvVUIZJSi%A9i1~Z ztphZ3gdama@T~N@OpaL>5Sj%dg5?n@{$o7!--u>WJrfm1iIDfXE<}rcLXzlw2%&gI zBeaHt2<z+3XE0<*46h{yEUo5I)xlLM|0AI@i1xFGx<k?`SvlI^IQYlkPwv)yEke5e z2ycw`{V}fn&rj$(RpDvh<UAS>*Rh2=*h!3_qTV6pK#d?MhLW4AI!n_Zse7oD13P>} z)7Zd9SrR{40uuvkCMAgu!^4)<#!wl?l|TWHHaL)Ow|1%n8}x|QmP@Ps2O2?0ax)Dr zQkKUX0TqIwcab^gl&Km-QK}}kRG>QybA_<N6sy-=i7U}BM+{O&@Rze-WLS^O(n~xE zv+VIcjT(GbWhUH)9~HVSSmpS9i1^IvKCq#>+-+bNm+=4=uF+6Ze~Y`{!VaNLhPX6c zei=vjzJsaj9-SYql=@$sA1<e~Y^)hVLYhhIO7TQ_27Eu3TF@mHsbQUQYW9&*KABp^ zF7U%wC|ZH1PD60$;F8@xafm|Yks+2yO%E=1#|(-AaXGi}7S{^KFaRz177YWL;gRoi zq+*`7mxy_+Uxt{sU}=;-LB!iC<8sPUMe#{PBQeunJv0)n3dV}`qRgJ2HZgriB?;^| z>5pB-72VQV_Qo+-kk$Q@Pw6j39oafX9fNaRsm;p1WPRk1#opQ}W-FKnf<{_WKq;cl zsFUFbPtvP~ZBklb7u-mBS_4@3Pv9E~Qa%W@PAHgsLkq*TV$$oARfN~X1jd&tju;n% z9HB{9uuQ+=T!LjT2tPDxoypB%ndv&NBR>RXNGLgNSbsfdphRSL^V2{$%1wmp<qR&k zE+r~#y_l0Tib&hz2+CPcGz(G^HBC{+ngLV%h?)NTxFQN#3Rhlavy}8;QmEz%YxfRM zwjTCh<YWV^Y_bzH8vfx#4}SqbPjt@BNyaI9GM(F3GaQbYTaChJ&20jd%q`hkf0Te$ z#{iDQ5bXg1YOlQIHf~y1GLR6t`6=GWW>x(TPbqn2s`68+e#WaDT{2P`Bwtu!i`oU{ zjGh@cpL(dP{gb(ZT^!bvU}LH=8HFliBN>I?#BK{(^6@1wH>@rugir#v@}%_mWw5X< zWvk^u39c6sN9qL@zoF!;ysI7X7e25a%%OA@8go#TFB&DTcv)*6(Kde<oruc8=Xg54 zYtky}d0zh8*u{2BxEMJVeuZcRr*TC`B`y#NTx@G0Vmk*Lvj)p^3}Nf1Q^HOrScdx& z-li0z`adXyC**?(kZC{U6%@1RI3ZL!J}<Heon<oat_xlMpogp$!z<uYYIlTdr5p}1 z-x^|IrfyM(tEcoct`wW(l849M*0Gy+ba54mr}!1BteeC(BUVPxp#BKgUMCK87?I=e zKYvJ?<y%Vy7}aeNo8W*HMkUDU_3huLVO`)7dm&1rOS0qfdKf?>uq`Q4*S$iZv|1^R z2Pu1YXj)P#Tmncebs4|`*B^#r$j4lPVDM26$ty-D4}mJCByC57JA{B(I52`eq8W<8 zK>S7q@!WWbvuB4O9!4TRT>_{NP`m>q!oV(MjGjPKgq$9Q<!CzLxCv+ZO@f0B0=sYg za?ERD;Ez1~^hwpw03wjKhNl>xPQSM{N+~%cK4p%*&`q;0w{jpJCc6BS%V)*~t68cZ zwz?5Y)%Eu`euB}ezr!6J9sT_c&Z7Pt$KT@-14X(LlQNL?a`ey808yoU?*4!~GKTs& zcmII9f5_cG#tt8k(dF?C<UsOaWXRV$d`RT$hdBCIG;2wOk`5|NVL5#D#p;wSjX#5Z zxjMlJ`rIUr@Uag>()m^cTlz<su3`%v?6P1p&7ZYm08?nEg0Sucg36L`SN{l)r6QSR zRl+9x>EM8*50OiI3)ze$5=!B0m>?6g>^MH2PID$%G@di|(Eb<JRKWdTORObO-=TWL zb7*lD%^IG@SAiDN=0~Y0jeM>yPmazz+tG9NPYGi!qb98dWqv7*M)*T;b2|Y5>BEx7 z7M@K1h_S2Kr21hL-K@?A+U4t^_|@Mim_RIE$`KKZubNM?P5M({l<9a<59>}44RJeB zD^pU%f%YqfuLNGh;OdWYZ*%}8!S^`;wg9F!0b{h!_5V`DWuoC}e;g1u>5oyQp@6q| ztq6Fdz+7aYIP+wj#DMr2j>m#R*7*)WA$`H_xBDikKwz}ZkitM{Qy7>Xr9?T94>TRv zC+YD@zqDwQ(T`D7j1eO!;kx&87&w+<7!)ISKck`Y209f(<xBJ??!N*9<)wj@nkSZ+ zC-X2*?3{Tr5A&oZ<_SI)C*}zTP*kZsqJEb{(N!LqIih|KS9N6l_j#0&s0fTdz_oP_ zEwM%L*%EDw<S=WD5Gr2&@i%4g6F6b210iTSM;HVX0^?Ew=ta_^l2wOcDqn*fGD#Cs zq~#jhk4IsB4VQNaD_es1b1pv(TAGeAj`^`z-bAROhea$9ml7iw!NWg6lg*@U5c%BU zw#R(9M5MDA!T%EPkBV}(nHz)jC`kVoxbYr4qhAA&P^1)|n^e4>(k~68G#2zdoY2f+ zA|YI(o<_w;5%C__7_yhB56hzlCG{Fgq+r)_fyR?^r|QF+<<Y>F1#ItN`9>M%wiHl_ zxyXu><;rSYW#yj)SAP@z^`}q+r4vqw0`(`;vwj<g2qUsMDyxa=m@+69L|spdGEgI` zizQfh%X(X>NK@TLfIuaa%3ahndJe@nzY3c#hw`@g0jH=AlI{SUT=i3zr{rz5huv~z zt)_$EjLoPXugeP@gm~5RX`yy*DmV-AjMRm=SXqY%F-mYbArb|KlgTQI3i4wmd>W7o zm36I?`Zw$jT<6HQw_-En0?FstWYwgEL&e!s{B|1(jrQ9cFq=4+in3RU7MtYJNj@-{ z*5#`Op2MI@%%#xvHX8BFZCzS^8v$<E>au84FYma)Mgr~_ER>A6WRBGz^7KRQsI5ff z#B(OqYJ|0f7D#b2svL4sI*qHB^yg)-kWRxD>`KL1iHKJbDXSG{r>muz$@#C&JF`Eq XzBoHIyD@ugwlenv>%@F@?$rMVV)w{O literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/compiler.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/compiler.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d6375c9a6d8d304c48437c21f1edaffcff63c26 GIT binary patch literal 91153 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/crud.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/crud.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7d89f11eac5bbcf7217f31517f209c10d617fc1 GIT binary patch literal 15377 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%0AL% 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 TcOP;OyA!xhxOcmg?)3iw`3Rbv literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/ddl.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/ddl.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d515bf02d5b736ee77619722366491cc08e00cd GIT binary patch literal 37395 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/default_comparator.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/default_comparator.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..abea94324daa49679e4df6a12a81f0e1d49860af GIT binary patch literal 6921 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/dml.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/dml.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..385d7f1a70f9ee7a2a23dca12432dc4f79de74cd GIT binary patch literal 32522 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≀Z)AcLMRXkSY?{b?^@dlp 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 TRpEOJUlqP8RPp>09zXtn(cH0% literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/elements.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/elements.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ad0a8497c5e1ea540c67b1b4b7d439d674f8e85 GIT binary patch literal 147645 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 S{_5X0m@AAGb`<*hcl^IT3t7kj literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/expression.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/expression.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f9f2d8de3fcc11e9470c634c2bc857fc3c337fb GIT binary patch literal 5458 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/functions.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/functions.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e56f7285ce911a77cae51af38f0caf7f1d5ab89f GIT binary patch literal 27588 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/naming.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/naming.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f0819e4d5481db5c1ce3993e8208b7ee6a25e87 GIT binary patch literal 4291 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/operators.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/operators.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9bef5464501c93bfb674635d24d075564e6c9862 GIT binary patch literal 45700 zcmeHwd2C!qnqOZeTNHIzl4bcCKU0=T%2ey}HMAt_qHN7blq}I#TXy$j*CW|dAN1>{ zBywoa&S)<0th3o5L9*GIWG2aMGD!wW2G~u4WEWT<0TN*UNsvE+Mv!2$0Rkt%=8psk zFtfqr_xtL-des-%q@-C7#+2UGuU;KreRq9Tb#%*?!P~!cKKDo8kH!8tX8q0L{v?jt z?<Qk070bp{TqR2Jaw40E$5c|KO3896n=1EZ`^x><{_;R}pq$R8%bT*B%7fX#^5*R3 z@=$iDyd}G(yfwSEye+$}ygj?U{6O}B@{a6|^3Lqe@~-Tz@`Kq2%e%9?<1sIB{UQH0 za_&RUd$N1tu}aF@oZa(EO!cdQuf^1W_wb!$_7Oa%)h0Y|lIKV9Jg7F~d9(Kj-an>> z)RwQsvX85+Y8$SfQ`^-8xIUqFsGYbvYL|Kt*C*9(^$@PZYL9vt*S+cy^(d}SsmIjg zxb9P*Q%~Ufv~tvwxQ?h5wO2j$wRm<^eW3QKr*Su?K2#%W6nFd8m(`fskGp5ouc&c# z0C(f+D=MQ7;_iStqz)sGj5?x@;(Aa$tDeL4ka}Ldfa_uPqWV0pN7RIR3D=|QtLm6K zjxV28chm{>GVY#JUsEU5Dcn7;zOGKIGq`&}eM6m9lel|PeN&xNui)<U>Q!|fc}%D; zs4wFBlDeQS;(AO?sY|#XSC`dmxSmi~)Ky$xR@3TrTu-Vu)SI}TQg5lZaXqcF>K$Cq zsB7w7T+gcaR1Vik?*VVCH{hk`Q`vKJKjdxmwtHK=ac|Un%G>7+dwb`HvahJTa*_U3 zHKPi+o>xkFxPC#+syST0==FP!_oSEd`eysG7u38e;@6AFYe;fqu3NnA#Z-1mT~{~o z?vi)z`epBBeleH46Tki%OP@>JiD$2PSMX-1ch;Nao9tEK(zGh6Z>h@1iR|n0R8=3z z(;M>izFLr{H@(M|?;XZ*1jkMsXK_s8NUNH+8OI(RgE)5J7*KU@6ONbFqBo&#dN1MF zkK-8}qd3NJWN;kB@f41II3B>U6~{1+y*Q5JIDumujv*Y|acsdcj^hB1Lpb_zI5?if zky5w3J{9+lsbBR@;{Ftlr`3}8IrVMt36=C-SHI@Hfq!p$Zx)Yc-%`u!OMuneYWQkw z?BnJAXT8~C#j82h1<!ZuRlnve);#4DE6&x|E;!!p1>dXHiq%Rj^GO2KJ{E7J7VE{* zC%BJ|?QEpnipn*TRUfBf<z^%4D%D7q7fX#~ZE*&7Rn-`%`-`3`-fSdFbsSzJQNb~X z`#D^__Z!KIH`f@L^XkRAS8nu|YV*a}dZXXh*L0y;sk_BW4P_^hO@T#+f8afu3th&I z7t6*~ESpgAY;rc1O{s)RqOE-@rTTE~*L)&7pmwUwY6!HD2CZ*VTXDBZZByHkW>7t# zcHp{M4FWz7Ha1-f;o|<=cr13>t$9v$)+toW3$9<RRVxkv;FPL!#e!RkfS4H^bnth+ zyioGWUZq}hX82{<tIt<eP2NrLVIu!w?fsHlD$IN3r3`K}rXMn<<lr|i&$8D0t?%9R z{F;~3`Fi<r$E`WHyiy6*_2!k!vA1*i@%4WQ1liDcz$wqK<J$nDuh$mcQfbL426%e2 z=sHD!w_L5&*(+uA2oSGOa%(~J<aQ#TIa4hymMaFRVTJ`_HUD@eHl4vy8^UQNc0E>) zuf*>pfC+Iog9N5Oo%+1z<nvGG^QO;(3bNbZocAi4jBXX{^R0w5?tr8gOQ<x6%&iD4 z`PHeZvq5Gcw{gdNf3bMeEdhC)detPFm|d(CfHj$jGBfNH^lN?ItphF-6B_rJCI4LK zX=f(-34m+Lxi$H$UoATkPCCW%Le;Mm^KzyVk}ZB1b;q5Vu_*5|uJ1Xu`RZaxIWwM9 zcW(d=w=^({mAL?5zBlLk3eP6L5a%5*n}VmjO2K=n1B9J<pe_MFU!0rw(BDz=jLB;n z?<K#As#$eGo~3HQ*g&~}DsliUinGjd38*7H#$+?Q=;L>vxMVZIR%ng7&hg{Ukukt{ zbcVOjTHqK<GzJ2)^HV@5Jf~`2X|~Z<W!^uG`H$h(pUj<^xb}JtM0#y$q&Bj2?W|Y3 zQLipsJ6)~bIB?OexO2in4qRQTfju55ySVt*ZhDoQ*Gk2iYc(*<0}F292GZ58MLoyc zYawK7nT4gsKtR9yiQ$6wyO$rh6!N!B@U~}y*x!L5R_-N1T)MI`dH}CkjT~0)0Xd)u zgDRv3Jg=V^2&{DxgALtJGcl}mBnJOs;GN$mgy28I`$u^q4d~P3xNG$1a+DRPaUbI7 zb{j|evH(def4mZrg=Y`qM)T}DapBmo5ZXBQhj)*lrsJnBO>YcAE7kfc82X_banucR zYZhRtN&Mswqiz##(%sPyppC_KceM1xU@jL*-z-w;-q^Oh%kGUZfltYKZmPiE<sY-< zJ%6q_IMrh-@s-3%awT;q^-=7bv2Vs_p{A+C*U}&Kt@NS0pg5*uu?4@X77HGP)|^-I z{9+*haMgDx`09|1mYnr6bD3t2T&tG7f(UvjO{2s6hsQ>rvsxWw-y$QUkDUF^a}n^T z=K=n(Sf}7ZT9;~7N;XlE>s6-=3Fpid>$i$j3qwvyfl(?JiuH_huIih9@NT<Q2Un}` z$kQWZqla$K&RTUw9b8j}=m;XR9Owp%9#x2ZK59TdRBPDj6A*N6+0<(S`PAw@1@W!< zVqxC7RfHCZ)>Iek3yTo(TDTS~v&GxyBU5_^DeJd92=qF^;%U8Bv|k7_OGY{iP`zM5 z06chMeJ?%g*6}KYsnwN1T_cG;VM%x9<t{8h?bDf>+Kr2L5tR{1_GLCR(vfNz=vPqF zZ$YG27RxiZNb3tOx>9tCC~cG-N&|STOi_1HXIPmc9^5(FCDe{YqMLdl^ZI<0kLWAD z)gaVn8bDLttP4dj=t`Is`TQa9CxBPi6dE0It`y~D<5<C4s5`Tu48otPuj`EF^W*va znDk0Mf9k5m>}rvU1W0J)L5YVC9Wr<jR1-NuLxeR$r~^YxnJ^t->m0x*QH4SrvMSv5 zd#k&hOva&I0(yqQ=&6Yb=TPQF=YS(m(xQ<F7e*Y{pIf9IWn~dyOtF@WjIjyobJMUk zsSQUIX<7&^uU1r^&#nVmMa>wF@)$M^HY#;MFCL)=1O}Y|GR{o3TJkLY-JzL#a27nm z)9@X4N=5L8eE#@x^b4*hc=755>3;H-k~?SVs5P%1)d-A66#7e7E<#BtxnzB?hZkzX zh@kztGo@l}UMFg5EEz_WPt-8eE{(&XneRG<#abOo|Me=E2_U43Eo`=9XMH|%#O|)B zK*M-^Zx&`(BrU27nWoOlOZ(e<)c5L(ekE65TJXrSCPD*l8b3@{=HRW!JMtT}E~h$k z-7COCqU}RmwCVxUfFvPkbdC;lr11`-il`k_DIy0J3)l-;TZr#gR+<suQz?h}pbMST zOCbf1n~%stQI*<)S18UdSy^awT^lAsP%sdyCAfGj!w>8{1OyBNA|7yw^aziFurQRW zsuc7F+oirR$lS8ch6M@%uP=GC^#fosO4q-ltln@sbo=4Vk<6i)qg^{b5IG%MX>}^x z812*yeo*r~%GYN3h-OP}taUALV!%n2kEFCnI!R-bNlqiR`NI03NqbB}H`z%gh1%n{ zV&96d#I(IXB|0foBPyw|+|nUhSh|uZtOJ1%_#tzlG8&67Ki-7($4XT=hiWG>K}z_s zHks;;^-G4#Ukm5PV8r+uTbd}QvDpHnKa8SuB@_OWyceBX>XfK1+`un2s$sEXTN7Io z%R_dR3T!t2c%`0p21ji>PE??&sZlrkI4OGBGf<<i2u(VMR|LGzpuL4RqBUtp7bO|o z0kEWrViZ+?L&>)M11DG$mk>9XXc3g*pkG5pJ<^a#FZ@+pFq8>a9owZ(fY}jRt%8u+ zao1eu=)>u;guf4v^q<BF_%q5&Qg_Z%{9RtQ=-LAWD3J9>NDwO8mT4ZmHKsYe3GBK{ zOwMkrYfn}DPW-EJMe8Tw9z_D(=U{qtG<707=`8dTxg<q6beR$yh7kI*LEnlm%6FmQ zYJZxLTfm8?s&(TL^VFp8SN%NMXrR|v0KWub?-%LxK~dzfsEzT{1*$d54?K9L182N> z_hu1zqZx3hb~Py`l#7vW1BDyRT@-CF<)~D{JX_s>rYvSEI%Ymk{WK#Q&FC0ZD7+E) zfLRL1_2GVsBsCR^R-UEB)5WDT;wGkBeYB<Au?bT!A<92MEr7W<N;eUO3Jw(QcSy}I z0=WiD$Eq<u9UB)NJ7|N>!ZIlW)v3BL;W!~%5YmKB0Wx9M{0ov@+b<bR2oOf(e#Bv` zi6jI}RSX~WL&kZtNd!ZAMlYk&OOQe!eHT_7`bT`V<|C`~Oo97v+=7g#O(3Z%&K994 zYK<9!uIw&prCf{*Ae)F<*Ao0XXO3jl7GdQ;)CF9or3*6QxB_h^)95Mi`kfsR8{-*u ze=Mcxm^3Ydq0z5v+Tz6!+xquOoFWkR*lspmJ<qV<ke0vLotBs1x}v@KA%1ix=+|p- z1CsBuV<`Ep_6H@~CL&tv(;etPgHv1U{Bgc&VO85&Mil-%>n+XfUw-&fXai?jQh@`K zVV#=-N94`eP$QkoRZziPt^rR2I(o6h`=MNp#z*i5E=yG{;O9-bT&+~C*K#@kbF9=8 zJUKi`UySii{7VOT7gbsAC=>lXI8j2@cHxxXl8UDi={@+jBNiV#Iru>O5lK<NOY=v` zcoIkLd7SLQCB@<HEvirTW9TTQ22>hDMty3N8pO3<2A5(OW=eq(Z}s=$WA|SGLRUOU z_8J}ft}~()fRUg_tA4;N4WBX!g4lzVs$bUQ9xylEO1&s{wAiBPXhGi2q7>xT{vr@3 z`KIwG`tC!&M#1+`0II2LVJaCh32ne?K4*DGq$ZM!D#DjvhsiFLgpxf6Uql&Z3{?Q} zw}YM*;n8Wc)167UbtEG&Uf;ls2t7RO7Qjr4ZYi|Vz;$fq+cJ8=5w&mx<MoQ`FHM^9 zOhfRid}26Bx5Q!IQ!q~9tctkEA`5elJ1vEd(rWWEg-qnJ(PL?v;p%h9^NyS1GX@z( zho2lCYyX7Waqf6f3ov@AZ)Xjr9UH0(mU7(wIz$$oY3S%108;ahR}%jj96}~g9%H!} zTz#=z9PYkYF0s;wb8@90=hRBdA74pb?}HmKUP&mZo4I}!1DV99#s>UIJ)-W^*j;g9 z3A&{mMOrS@itCN79(bIeoAaxS3qT*RjCvKWN)krF^GEP0@)BjZIYW8nAH{qBS)L*; zAxbm&QL$3QSmU4KrHlZ^QvLDZUt)QO=>&|F=!8ZeBLtE-1PZ%xLw9E#BZt*pEAi`q z#r4Du8AY5LOEw0J5Q#x$_adz{8+=%5+{GpCy`Rf%f*xL@v;f13FK@T2!`Dq_jp-8j zR9Tsrc8P=gHWEeE#o-G8V1#Wxm)pXUm<3Fkre^Ec-9;A~i?UV{v+(r(IdQ_1-;*x~ zvZaxt$=T==6{?ZAajQcokU`{f+q>04zaHxgF!9{Ev9zB-3I0Kz4)JuDC-Q9nMV=;j zI>A%a<B?oo7yJ<p(WBFQAQzIcI6yp@PNkE=ybE}1{*t&miKF&CRM3_QI^Y0O0HIGJ z1t=XL1-$figggZ@Vla>qn*$kvP$YyO#Ijq0@Pn;E_`x=WDD1-T+tq_=H?9w;htwWi zcc_QeBe?EVkE+LT-K8E^pTqS*1kpU997v<xs-T`!!?=4$!jWRxJrax*%RY<%8y$@F z2s~~&80k^<j2g%HkEsJHgX`n!pgM%>=hR_!1lK3jQS~gYj(Sc#kL#1_1@$7X!|L;D z0@uCjC3Otfr_^zE0@r=&WpxtQr`0KS8rKnZMxDiVR86XLxQ?k;)T_AeSLf9iaD4{B zJ`6u%Fq95IIuL{(WrFadgF*Pwp&<O|a1efUBnUq`8iXG`8-yP{7lai(uSjrnjcqMj zpZh21!7%8BybYd|aoQbl^D>@8J5@x>HDV8*y1<Wb#Iw$IQhUMkmOlyVtZ-l3IRu0b zMXg9sIBIUekFH1cLyjA2XY^`?zqKe)LEr@)>=c`D*HI|KYVoTJ5Q>o4p~x^Uc1Ofp zs3Tlng14yR%q$j55WMh6hUsDDXblXdOZ8nwEu7MEDF?JStJ7eNWG^2~;QNVuH2iD_ zD1@cd@^qV7CYpT7HstfU9GMvE5e^R6Vx6XuP7qo~_^8z?3alXti!fql)Rdg@-Vbdl z-9K+41D0#2K<Ut&5$^;Y4LU|?skIK?`+AhO*?0>ImQa8o(qbBIDpXNGK?b8N{BRZ_ zV<KN(Gad;%0;?5n<`4}o8Le8^R!0J9(@<OGqAAgB3h?Q`g>INi7q}>1HLXU3erDYZ z^i&5(1kK8EXHXf!1Zr5xMQ8@CduW9v4x312O?Cm=U-f{H^x|nKujag)aO-s+9^z>z zFWRpQi;QGQdpV~yy`?}kX=#lCNbBp%poGZi4&F|6mecJqKFi9uGla78`C|qgMnk8P zcAoBwplfMGdbjK2p(9zFUK&KeFV{Kd%rp(lLDY+-!t&P;`z2^2FB?JFzr0(ci)lVI z5hm%meY0)ti~9R68hE@*3%B&pLfCw-tkFUw>A8j9Yi*&0(Hn1J&_J!9qx-erqYfX1 z$B-*@^HjCsadambP2f+ibN;F`_4<Vi9ee+Q)nOx=%WDQO1D(2Fv_4VOkQc8>9A|KJ z2wz+NK`UUMycb>n<Xu1p(Gy*vvlXpagElHai|pzEVv?RQ=U=q~2)%dq{MG66Q)i}~ zb5|~1?92+TcohsN)|_GK?xqi%8Ca<m?d}5mtV4+?*Ip8~Nld~knt{ek1%ZoDE^%-n zJ`I@$c{0poW;o!l{IHYv`mpA=d>!zY#&(PysA3I*zEICm4Oj)BKGCLNpt)(;Y2`Gn zne`0UKkNWkHmM0zy}-8Zqk9D3jlgziv4)Yh-obko;H_BT-P;b{w)}dAaGTV^IJ40N zExcKm)H>mGmRj4^rp!5rq$ShXj#7(BdTw8v)H-o5Sl5Ywbs2gL=*H)G$||J72(>vd zmanYQLh>FYkw0F^poKZN#;Gm4trT+LuKVwR+fnH3Kpnam|M0XT3O|?o$ZFO@tymhQ z>9yzRT^3b`E|Jwh3a4-AjL+qM!_*+Y{8)PpqJ+IxWBJ?70DkA)PShboVRU6;)IvO% z99tlNRxZ4Dh~AIPAQ_A{coU8?Tt*1`obPek#HlM+PQ5K-MXz3+zI0{MBEk?P9CQvq z(&#IK-QTvn6XK8BOgxh*5xLx7G%y)hezYAXCSA`U`EfCvnjo6)v16S;f?*H?ipU%k z!-lajP<-$o8qZA(2I+|jls_EeY4sb|Bz74S4|L!eJlGsn3ylbVg&Hs<hoKpD4^cXh zjtdA#LBtQ=@-KuO{r9UO6GjvC43b>#yB63UX$M=FY@<*+apKP!YN$bvP<wD~sOkAO zx!iBB4z(~@&rthLGx?2xf(vfFZRmr2ye<*7f=F)Y+y+5_cfcrZf93PK6dAzPRx@4$ zsV*)7liTSL5gAe|jKQylTm3rKKd>OwW|Qe81uEnG<Fzfsg7ZokWFa9I3HMKtUo(*M zdB%=yR1QSpO|}Pv0Ct@V=f60qT?diM$Pt&;l(l-fju8yFQ!tJ*`VLv4j9$Dumbqp? z&Mf(dK^`BY)?pfqa1?YDE?Y4=yl=KzopJqr!($yrq3Oz*ustN;4B)}q7hv&~V075S zNHQbCstXKLLztre!70qcb5Ow42+pt3I=S{~OkO>6>hfehZ&mt+w{)xOD+J?pYB*Za zRC`qZ;67A;80n?(<HHAst%h_SXdzfa2Tm*NDN5%?@P}KzUuPXKxyArf5E+BW5g8X? zxJRp$NHO6*G5Gk<@~+mxm|$Jl`0G$rTCq{rI8rM$=-|!7kUT~+$g%51_=<>K7Im#o z&%*6FCMxXSL(D9Uo*<}z#!@Rv>W~a~OyjVA(!mW8C@%?>&tN_bkbt~^(RhmsGKq=! za9-no;8ANW0Y==<mp%lpzqkYvRR7*nNE?CpME;OE(o$|O<k2T7|Ba!KN0%RLr4LEb zt1xp9LVa%|j><+EwQ&<_hU1){a*hc`!-TWQXc~w+2l9eW#E^auTn-K<Nr;klNQ^b@ zZ@+_BcUb<Ur4oD!7(>+&$+a#V53D|;S-7r8rdY|1zH@loIWq1Xeb;8%BBDk0<2W2g zAxNk?+&V0hvv@@{ge2tbO}LH0(p`uW*2rSRGu1`J1)3&VK`WT{IER^&VVEl0BJFRb z*Ee6CyfW#WxpaY$8-01);eU%Y0}{|E%M0}-0m!<V-*dYoX=@~p%TNUbf)87AEmBmR zj&eu0<*);vn&r)5parz-_UvQ=7+x_Rf_<@sAFvcbadsA>82WV>I~RrRIUF&nmgrfs zwD}+mAE<!<T&r`mxYeP&Ivym4PC@i_M&OAd@EisbFz7V5gh3l9S7CubHZ7aOp)g89 z+iL|$U73@Ze?(m(J`9!!gS;5ZjM+3{$RBWbSod79GHlfgSw#S^MU0<F--1wqkP7C^ zW@cb!39`XY8-$UGp@%sG#VDSSP%4xbRiH@PU@#eQa0?-=Kz|*?-insgo!SY?d_FoN z^DjnFzc8QUj8GlxmC_;rSQr-s!XX5_asd5nflS73=5^0opNecOVU{W8n?OfsZA3F| zy)*gNWz0T0KlMsZD*vuUWdV+MDTZbuhDZ>Mzd%+%d0YqDa5@Lt?-j6|K(%7kwqAtM z9qND~13?Zt3iBq0X&FQlPU8WZwDh`_6?v0`kVH&yAf_tEbA3$Q@i`0`IPs&pU~UlS zF|k6dX3Hco2MTGW0#z85MfXdpQTFq=^QB>d$nb<?Smp;U(22YV&aH5IbTChf6G@ub zpb(SiB6Lx*UA<e{S)NU4;oKV1nVwlQY%p%PwOosqbaa=Ub`}9)i+~R}NWd^0W(w$A ztVC5oH1K0uJrJs1;g~g5KEsa1c8&PIZtqLZJOYtXh|MpfGLK^L9HoT$QWRN&azDgO zNbcHl!CaAW#FMkdIPVPzxfm=Xz)?J3<_%beh7$&>>Ks}-z#?v|p=5Ey5iT+y;<N1? z--p(ei0&{LYzZb+$JAORhskbpetFY}x5wH(YyPDP3!#g55Zql&luC}XleHlQIoyNz zy*hbe5@RJ+73~Qy82HB^iuKmf48}^17Glu=M#!OMrfGrQC+RpLC{!~7%?5&NwLUl5 zFs>p>Xn3)OAKF;3xqtL=Phvnc9Pp`@*#@E33OC;x?gcENlKT^*t4=NNY|~XMJ-f}_ z{|GaVvQ=|h%2o_hV&LWcRA+5f^bF8O)AA6Y6e@Xe4a*EP%q_AeHm-&{<4!SySyK7@ zhqr|smJmsA5vm{~jC|UUB8MV%7F94@qK3+FIS8#_(j`zVdC`<wGuny|0%Za02TaP= zyU6UNZNanzsF}@EQ!SA=d>ANrc$g`Iyk%A&*&c&<Slu8L-Jphz8EfiEw%TD$?s7}i z=!H5%{l|h;G~U;&ImsN-)%t)QXj4x>p<7zO42TyQ_qNIietyfq0%|SgWwBtxSLBuH zG3eo8r`birnI3?@xnbQVeFbxDI^e$*tQrzlS~p+~*PTvu0#u=68+W&lsp}-`ah5Fq zzfL4ZAHw}6GO>_tD&r5MEo2s<PN)iCnr*~d*YCMn-7X&l0GXl}%X}b6`-bU|Ez4~K zPC=UW^oSb=1MSo);@P?1y&}#8*cTS_`?>UsE;s5~jPltIlg-5JUVBE!iHSX{YxcB~ zVwhvko|9YXm-8+1=)65e-Qo;WP~2(La@Mq@fl!Fn4{#STU)fA0fR@C0%M!o|Q3ci= zO@9^*np!GEUl5_Ab--YTt;hnr(@{E(^J<`a(lC=H91IUzl!-NxlT;|+<S-M9zStwM zBAY4^#Fhq*7n2W$AP}U^^~@X-6Jj;I6T!Za`Fzx|HpzBn=n|dKfl($A#Q~<-Ez;b- z1|M79>hyN~1GDY%UjBGZ;y8n&L&TB$m5%-GKn3Ycgw1^(r2BOBdr+xQdEe(c^n9P7 z#A<#2F1qoK)%WdwQii;k-e=GAFw@q;<<b!QpU|=Y?oSZn*h=xxAN+caYGh|DxU8q+ zLkHAeeoHwMJrT<<VCA%6CN&a)55T0O|A`u|<Q~x)=K6<Ao^-UOwYjDX3jD4PMioc) zjYI@@|Hj55P>c@i0kWp?v8%8|^!f#6{8END^Ledaz??0XQ~_(LMPpx)=9bS7?;8e) zIrAF#Io=BZInOJchlY_G&f_HH!sbWtHei$`2}Cc&Bw>gJQ!e^JuO%3Ub%)MoxLaT~ zm<$B=u+vzMtuD8Uln5XhvUoirraf#XuYmUGO9rRU=V@!PY2oZ$`G_V9eyJ7;@L2$g zm~bp1n?cYrRACEbV$*;fYy6S*akSX5LZg=gJkt*8GR*j!)_iEf7_=eKXzvmL=M?pO z%w~cMpE3<4nfCMuj}R8dS<X%<VLX!dGS&ex`C;WG87MHrq2gqL*F{Dyqh<%bkRad% z-~n!N7&r375gtK^YlIYZo=8)H33^~b_q8R$bc?j1zsk6-G0Tu+<c@(%EJjs;!$dDw zBprgq2C+<$HQNx#jY;iTf63=;J2;{!wu9kBWKS|NaKRXLd)EMe_8A)C<O321o2Q{A z;qHOig1G$>1}d%m?gE<uMJuBKms`9B0T?4fn1I7hrmMUp{S>ToV=w?nP9v<Po2_EV za|1M&G0q59f{_5FtZD>rt&kS&T_GStK~`P?ccFTwSqn*k0BJ3si*sT^hDBpmE`h$p zRaAoxHV!&WalxEQ890(z<jvS7Tuso>%B`jUBMuJw17HkhgaNm0e!XbiKVn3}nS;Uq zs4KL$-Y(%yQloa4f#AAltz}Knw_4OVIzYi0IcP~8EFy~T5|C<U1PTo?g2Ck!7&<7S z$*t1G`D@oS(lDF637JE5Cs^PN=l|;wpdMLu#GaZk9Q>AU1PEg>%tAzV;FRZ(Kje~3 zs0brC^x~fym6~c~v9AEHz&tqJBqUfpd&_q8xYP}>nn1AXE?K|))v!YByzvNYBn_CM z4qhuR1O2r33zclHhVF?qA){N6k38J%Tv8{YTQg$VL698H8qLdv)d$E$DsB=W4zM8% zadCpc779PZ7zkdl%@DYh2N9)O?J_CAI>ldReh{9EZzMKSMtLx5D@(Of&{|Bw%1tb# zX68y6UE(Sq$f_(78!I<8Nz{<8t$aFgZ3~KbP1>!2J@46pJtV@rC+qi`$a)$~GNKY{ zGlIW5;0a!7ARW8df<TYR7ICM1clML%z}eoLaqCuS(|fp&^6J3p80w{GhG~sX<7mjB zqqYgwO1r{AJj>%rtR)Y7kG%CgsRG3Bj0a-n7%=fF7#{=H5M9G4T&weKQ`qKk-5s3_ z2_keY1BuL_n_m<oN3!sEkIAVUTS-Ij<dz8Lt$wji>uU@~$^U{e$KF_O3tx?rt;;6+ z&uuo@NC=gV5t(;qk0EP=$>0dRTVpW6Xo&?@Nz}Xd&l(G%@R==!h>}_+1jlEVSQ9kb zMEYlT81?f{vmMqIlRvY<L=*nhT47<w-5(n)kT8K!^^0hOh45)&(UJc3OKX7{xU?~A zOL+|X{WJSZtiiye`E#(p!p;$*zNgk#XM(<;)>qgTn*dj_y~5;%pZyY9UXit8SxEQa z_Szi6ycf$W9VY&7mfh7h?j0stm(}&tcB?BiF7JcYRW2%3id<HAVR-!<N5evRd}e=% z{S|`4<}I>dy0NE!nMT44G}=V^XZF{p-TpFt7kL%+nC@rx*QeY53Oml|D{Fa%*X1m4 zR@Z)khr&WIHF4_C%rLXOcBYMIe`bdLnGb`7NPEA{uok+$pVnB|B%2ObvBtvWhP!=c zjddRh3yq#0jkjPV%&4m2P}t@W=slQYJ{Ju-okVOY15IOZF1J}?VP@;H#NI}3CMFTV z17=_`THa?wVhTIjWx+_{hpoHGX5<sZ57jZTaRC8}7_|&SjB#t=;5rviO`m!7fJD)S z3!Ta~M!|XnoM|De>FC{vFchj6GKl7fP3p}pIHyu(sWwgfww53|U?y+n^S7OoCsB=D zy@uH|{*p`*Fm>9mmqfvyK03mH(Irw#XY}@%bLy2-=cletJEKdSVSW12r3;g%rksnH z&Q4-JkCn$Iti6FrnRX^;E=^7I<I&sW4yPqpsbwx#*fwn5i&5~P;o)`;Tv=VuT#|W5 z)+Q1Aozvms4jjtG=5_E|nQb6iz6-2_a?|`?4kw$CO2i)wOLK?Aum^;ch)cX)<TO)> zx48Nmhqtk*hP9m^ry<KeBbGD^@jNZ)&;yb;`XuFV88qMCzCMAZC?J?x{&*!poWUWR z7TH@~E?*3WYk}9Db_zzcdI1yOWcgMTLkn=2u7TRZX}Nk=txVXQ0e;8Ublxa3#n(SF zO-QycLBa$*w&23*EpRu~fPQ?|z5YqO{jpX3wy1Z!@3Hn*SFe3#u~e$f7X4a%1HG^% z`nr2t2BSp(*lJQ+^gxuX$Ch2`p=AiE+DOBOq%!=td%HH7H2>Uc*2Aq~X(pAC38WQ# z(QTH(^6#$RE<}i9<xaih93y0dz;i<|<Rwi+Gakfj@rC}FOPRF=$*vN+Lb=eg!*^JV zC=SAqX7CEnoYlc)tXN@`*|JcOU&iY{FtFRyKG!P9sb|Q!MvO{;PCoxkKAJ+v8I!^4 z<raVB>d@VSZLs~c;^y*Jax_Y|bKxcvK?6JP8H|k!eEIkfP5b)W)rBz8#v5p_%eavS zVl(m14cyd21Lfm?w?+dc(Z(CN|1Nv5yoj9}I`ts8p;)U2<>UXbMgt|$#v6F>E*q$d zH#;}*p&lA2AODv%8Yqc2-atlN8r^0CG>BFFJ2#N4maf@@{P=&Y(LgETTtPN6L7u#F zo5C|VYFwW*oJYjnqOm|;iksOje`$3t9c)e9DpY&p(z1@2jC^95FhnE!T%E?|LfEdc zwp7N9WGu7;5O77)#$Zq+C6?Y4EZYYKc($JHFUaO5ejUkLcyMDN`09Tf81=RD;UG<+ zH;wO|)gcou0Nn~2vTOsMCIM!*JZS9ZFqiJ~IUOeXsnxVLi!DslTie`kLwK*=JZS!y zx8Act8_7=zaE=ItI}&0AC3`E4Xo?x8RB-D$@K!OO4A``mjBO?Kwsx{utXX&tAyE*h zSPaCTc_5M4R$N9{29*W9W-OPM)yo#Ox-Rx&Jycm>mU~)_Jco4(PEEb-$eN3;@4HK5 zBc@_>D|5aB(jeV&r@R!dA;LOJ>I@+_YOS)tZ}uW(bMC>C9(oFbtluFytx{IF!bw=7 ze2N85+*(e<aTND0Wv@>e(tUn_bNwpHEn!-3xFk)~y=+Y<0|@cwz?TEgsS6hjn~c7S zrbdLb<qv19rMN=c?`I9VTSI)C09(_3iw+AiGQqv00BO-LD+Qn%H%<Y7-0Bp--0u?w zw3NM03J9=xLlgky6AA$H2>IUzC;&BBg#z&Q(N!q`33}CW+-Ez?BezWrOJJRsX+)as z(mS~7zAVo;GnP{d)A!qKIay+l%c%vk5Z&2XZv3_sdE<gb#F_-CeC38S7sGF&)vOl6 z8P3{7<XXrc=Z3;e3_FDL>RiTNpoY!?!`HD`N>}p<%|%8ny0<L8zLxcbmEXm9de-#V zV7pQhr0KQO+IP~iZKsX)*a5=B`Z!?7{#xb}S}2CmNz=mqPAy!Y@zl2Kf^9rmm0TB2 z?(fpVwd^OfP%NWYS8w6^tf#gX+Sb!XTX?Wb3)eEB&_XedF0bCg^_fp?Ews(2jka)K zmlm#NL9q{MK)tnk3nfj@bm+A@X%61cPTUNeXEh@cTN>0cUtc;YZ#A>SmIfwW&rNOH zCZwsU8}ZkeI<znyaZ0V#R_tlvn+)cy#&+1#B1*`OP^16n#$M>Sx8)I<pqsc*W_=OE zB>qX-nx}Z;F2{PqOWFGJG#_bN`)7HY<mntwukiFLPha4Pt9bYqc$(trHJ+~W^g2&( z^7J-O@9@M`Vtg(a;^%mBdE#K5uXvi}X`ZL+Je7E&@6oUFw7}E*Jo!A;c)H2cZJw5S zTH)yfo<8L1%RK!GPha8bt32J|Nw)BePTVuYe;KD;&=+c~Mf#~!0{@a}{Pic&oA7Tl z{^`G@Jma^)hX)@|Urz5C+%~u^&3|v<-`nXO>Av7!_yxWj97qqOAI86-AQxPu%mRut ze<(j1o~g&~#J(C2_qdJg-L|7z%N?uU=;Ibmx%UmC#Qm@1=X;PbhBtDHR<_@`H`(@k zx3TzeZOO{{o6MQ5=O3?YLIMll$6c!@1rq(p7hK{4E9Z}p*?M#KzlD!l^BySHWMSs7 zSegGSGv_z~|9Is=qA}xZD{-@bu22(r95ZjkXIk=?w_msNW;VU&eHN*My!~(EzBPBg z|H#Un&%Nh<ZH?Ud{$ne5KKGvcyKCgm_rGD~&gb59CkI&#GJOA+tlarrNaDmlUQM5J zYdQKD*{3Uss9#6%J9b+Tq<ISW9Xo)De$y(3&r?n1;HAc$s2thogS*itxEr!8%N7?F z8x|*B&EH%5`!as}u2meLH&hy#_nM`>g+$-CO5^i}N~7dhv$QKn^aHCjK5wWr%A_?* z%OcSat<w11?1XOHqlG)PhzmLyb{$>1=t0UWIDG|E*maF~p_8}QU&Ol~S!MFM$?x5B zky$RR82E+llKo#pmYvFOY>7UAq5hAoT71k}^vvxj|5u~CX}td(D|bHko;wwb)pCCW z@BgWlJD)p?fi&Kbs_z7w$;U;ZKxauWiIkNS1&n_Sr{A-(<#VsuQbp;K?Gj%7ft4+v zd(C#Gd$wP~tAA-_%jYIE!aL1!s6(wJ{SvYE#wLGvJ$5~=z~bWAl3m4ar2S!){2)<J zU~l@^_2l)`okZ=$dY?+&iGP*55v6Yo_?NMTdq40V{~p3}a35fwqU-&t4{!VUjC;IQ z{lPmhQ9KQNOy;&}YI^ylX*jZ@FmG$23t2X*!wN9d5cCJnajS%%#1Ag-ziKV_L>D&J zTjzopTzVwRR@S8WNqk=;o(uVD!vA%&-T(7AjScw3?nVlWzEu1dc#B<|usUSDF{J%C zIoZFhu}L-__Wi2g7zp+^9!oV6vlTtYOn%%Lj7n|c&5a#71hrr%B9&t_SB~*Y|C+t@ z2)>KOQ}Hy8ex4Is<LN{?zCF1;K9uzT3f?yLUK10j6NzPSsFma$4t>EENXR#$Yd6up z2#mjs>{@E(zlY!clZ7yR?p)*!ylF0STR=bSEwr&63@4i81)lTARt<i$YYn!y)u5@- zE?)Q6=sZ&XSF1=qZ?Mrl7uwj0Mu(x1X!3uz%Kxif%U`3(yYXRfO`bu@|7jJ;=M6Tw z=R&2)Vc;d2{J*U7e~aaZl7%`K4_cPBNEey~!_cckQWsKq*(AD;<A+vsF5tKSYZb%i zbrqB4g0>Pxw^msVu9F+a?)}s%^E-B#Y~tFMjW!WFX_1XI_)*yf^nPX)@!fk`#AdTe zOLZX$7NZBF;{IQ|r=@Jwr2uHMwEcip&|hyYh)_F&qoYwpufuAJz(u^@Y~{}9UPUWi z0bL}vhgaLIZ28=4w)5Syox`i0R<?ZZHCsAOy41FaR}Wd)^10V+>4xc&?G3zo)XJ96 zhQ96^*SB5A@_uF`)9S(#R!QHxrzP!@k|5{jt>X4t#eJW}k#PCPEBTY4<R09tKoZ{Q zU)YVU8L<ZPokVpvL@`x=h~QLXGd8G|6{gV|Q7Zhs_{M)4CxCU7_bo~$UmalSpm7wJ zj!WXE@(oF7?Um*0^5TslmRDZnV*59}&LuRqVl`(g)e)<ye+xx66@_=Ysgyo!YCT?0 z)RVKRu*j58EcHE}h*&<+pi9BKP%ySn^te_keb(H)$$vg-Yk%DT0lsdkz&c?4P!7Xk zi`cX*$E724dZjspC0lSm+P!iQqjH_Ue<`XVdgE`m*3k4uD4_4e7q$Xa@eo7_>A}XP zT(AyjtqXVtQG+1KX%@+hV{!k7tpys84B-uR^%eBY9qQ8Y<#+rlH%W5ba8YqCk{J#U zI)`68gzXft*)Vpa(1W*AP((0*2{l_ZZLHu4O}vJ!;<&(GxO+#Cb}Z2t$d!wt`6jV- zaF=HE>m(OV6Z-wXkBZ`3x5nmuGKeQQ@HS!D#A=0mkO4F`F58Z!2R~6vn$96`W{!{B z@c0SQ{F4Em`guaeu}=tuPe_cPkWQNT4pW}NQ9FqfU=xQ0vy1BLow&bC#jm1kt|!nz z$(5K&)KhnocM|eT9KXQssrT`h8%QVK*W}bWe;+a$>zA$vMPI%$d1mtL<kXo-|L@^N zV*s$g1P<T-hj{XT%(MyrN!}0h)J)%vA@s6A%^wg*?8aDpJ2d|O_z-k||M&20dGn$1 zoa6t*RL7(Ee}49}pB2A@(<eB7=Knsvmfyy*{$boVVq)0n8WC7N0q7HyK0)OZyfPN* zvuFIxJh4vx7M`es`RoSS;M{+Jchvv=ojmR0=|P^T3N}&;OGj?_5An8NhdJqyg}qEc zC%ZVO{crH@n>>BQ6CJ76Xu~x=zROdYCt;G8c}K^RPk)b3H;nj6{EvA@?}1MvUrcnL z2DDFeS6e`|V|<ztex4_4*!~PpR6P9xPt?<VYEq);_*6oC>I#~#&GF7KH~)Kl{60^A zlc&GI6S=le?kOBhFvb5nd?fYyf0rjAg1*HH2J9Py;eLAsFFORfs8tI^VcGs~BWa@_ zaf36}lK&%qXRyluXM7}bSU4rv^*BFD9|KEWEO{r`fi()MRAMMG-Pa%AiouNSUyi-H z{V2}Szd}0I>EB=~`e(i09sN4k@jIp+O6-UaCNTh#;;}WEPNiVZ!l+H9H$9R_#fJvA j#_=zyf0Z+Swf=G71%%M_OY?J>+D@Z?)4!PS)2aUt^x}k` literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/schema.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/schema.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69fe9806e10bb09cc669fb2d163616ff37570ca1 GIT binary patch literal 143059 zcmeFa3vgUndLGs<0FB0jAPItxdG%m81At5RJhiifA*tc;u}d!pGa@lNyU=L6n|&K( z1C4I*-Udmuo4eL<EUgo*v|8D+q-<15qN=PEMWv{cc<soJqmmrSO3I4LajH6wl}hA_ zq{K<894FSsapn8||D1ErZ8RS%$tkDGAp7>cx9>gA|9$@dKd(+r74H5=m&@<9GO7PD z<^7G~{4IQ<AI_vwL24}(q=QT|y_H$Zq*FmQ$ThQDxwYKZ$lA!(=-TMk*xDHGj|8L5 z{MPu|_*P-9fa`QH)|^<I!2LX)pIn=iXU1_pwKgT^1w22!HodiPZJ%6EG-tN<ukGJD zuy$bU;M&2h*|piNLu-e&=GNx64zC@SXC|9RwvMhH#W~AAwsuUupICbW^-Q7cC)b`# zr|KgcPX*KW(`!%5*}h=rerD~NV1IDn<J4L)I2g?0``P-~#{8#K!J%O8<5VzLAFoet zJa<2bw~piP;ou1F9;p}V$LmM$kE}h9dq;y~xOWV94&2YJy?{GU1W)45lY9zy7I5dO z;Az}>y1sZnyS9YuXM!TGi}fXW;sowI8_eU*eEkHT_yn$>3y$OZxLlt!ZF)X<0ry_O zTjSEAQ+RG6Sj4@>`YF6u!u3*c0@o+%C0xIV>rVtHaedOX<)z?MP(m+X4qgmi!uKn9 z;^p8KJn@RO?NwaA8oY+<*Xpm<Uqb6&!=3L6K8ZV@tiOgbzYEu|2cN?Ar{wyRxPBv8 z#`UsXzmDrSgVVS^E!UsI^_k!-uFs+kucE&rYj0rumxJ#P-fE;Vu5aRrbHUqq;%)qz zT|15I^T7pNU#OqP^%-1W3@+jNlC<$`{cJFX_rAM+cH^zJw}P>2sijX>U(BUat2gV# zsCKiyRV{9{gPmr*xE;1{H-dUp?BJ*CJB?=0Xx%7YZ?<cjQE|N;7K3W1dc7Lei(B<h zl^3N#;bOIRvsiD|x9Y7<@lK<2v(du)jmQ>?@@iq@I!dj^@8j7<XSaCgW&<zRs;%Pn zdZ8B9tDSmKthRz;5Vp6s>%qw)Dn(IkG$_2&K*ihDuv2WW7o#1#i1)OhQn7gPZgp$B ziCP-1T5~6;7tm5%o-Eed&7G|lPNMp)oqDUrd#s|-3hH<35qfa0+0=3xY-EU0*|}b8 zR-*`)sA^fxmn&b2ZZ)e-4%lu<$E|cthVOExzE#met5G4SuQyug#=tP32fI>uqt&US z>Y&(Zqu0gijaEDAG-|A=+S)DN#*gjrRJ2{MHP#!oLap73I^j;OBQ0fbn)O<z(Qfet z>Ah|Bjj+9gF+;^Dxmw(5H98ntJRTGt(uH?0lNT_Pl~U23Vx+>`912cZvwfq1Qi@VK z!J)lgFi>MA-Haz^vD%EZQ&H5eHDopk5uB)Mt+Rt-90Z|XE$v7_=CX6MiizE>hgEit z(|Ehxyv+&;jCi_Jm0`7&3e?E$T1OtSB^S`_R=ti9#?-FfX>*)dgnJR7gkhJK62yx; z+Zaqd(Flw4(Jt!2@Yd#0<ql>lB*3AE0;vT8hc~}NhxJyoRKL3&)}sjhC;=|)RAMT2 zA2t)K1lWqe=Fox#)=K9EoGYcmH2`{@?FLq~uWw*16?iR%u+P`N=SnedOtD#Q-Poz# zsGsC(3XMAvYvq{L+FKY1KorM*;7!8bACS2%rThD;*CVu{Q!h90LfH8O@VC@buAi;n zt@U#|okp`iQU?@w`h`Y|edm1sL7ER``ui1QF7o%)YIvjG=}%o~R0-0b{oq`9BU(WX z`P+@C(P@Vf&pW%@^>TH)ftRPt*MYR<c37^5VLR;4=vd%rP-1DUKXtxY-HGZKl^*pc z%_DNNKUJ<qWgt_lM$qgRR_k{=`h0(;+-zXBt4(<X_fe9Jv@L1k)=nEQm0Q)VI@&cM zv!yTNG8+WVwW$!e)M%9zJ=P|#W4t%bRrj~9?QUIfH!-#W@TsGK4Un%o`cg6g0Fr0t zzjV#etV*GXzvyOr2LOAsdb?g+Sixj12vu%i%CydtMUH&2p%4#9z%a|3u0V_96_kRv zl**9kni4S4tf^E2Aqok>)c}~Zyj*;_^it{N*YwvKr<{NM=I1;<(UUmz(m`r7A5QjC z*La>joa&_x<2=A;DYMc~mwt|5kmD%!N43%6QM@RJZwKd>KYSn5C;D)AAzIk|@IpP> z?6kK(e7oJ=JoQes1=uB<IdyF}0?j?ORmE5MA-a6~!)D|9hk%;;sqJcQ6VF8-#^jo( zA8KW#?OnhT;I1tEp}bk&J;TORTuY_%>0Bm!3TL$}uki;yDIZ=-4}UQAgX#4Qg}IOO z_p+VTM!J`3q<Y!=S@bZwQX`=AkKcUhAU@G+ICL-+o%H=oc(Rw;$b5yv#Ix*IQX9F= z(eUI~(i=HmA2q`zLzZ4y8UY?fz@$zU_}(8?sO*nO^ZQw}zn`uV0&DFiMs_l4?}W9w z_O3tK5ggMi9cRu`e=MwTH><V!r`fnX4ykk@J(r%$be}EP+S|M3m}rzvgZ+bAo-LVo zmNMZ4%a}FSoDd)tU^0yApaX^Y-S9Xb44>!W1s*ujcs;%=jhPv&@)914IPj_T^k}$* z?;0=cAAflZpXfRc_0$?bZVeN%mJKp%IZD=$)J7;t<9jq13-b6LtLN+E8--vzDBRBk z6T#%isr!_sCUAE$m<pzOy*3p<%Ei;uM#|k69K@u~_6ye>pYOi<Hsv1?2@LYpw<s_H zm6bCTJu%0?Tz^7O%7D);2O)$Geq<6jCU>9ir7xtuZ|+{EhyHAihoimB2B7ACI-R;T z`T?YMB2oAC%i!#wnT@WH|6;QdiAYqtNp8j}R9F)sEN*#;r1fYy0_ZDYD3djpgGRJn z?bL2AW&63P-dykJ05VZO$8uuK3}vP!$_-R4y@)=C1}oNN3&5N1oL7_gnCmUc1aacC ze2-IjgCrr{od>lRSqSX0eW$3Ud`=kvga(Lfk%gO6c&k6Ud4~-gY}zDIT1Egg@B3H> zD@<j&hrM?B&$x!MQEbn7e4-f~6sLpq4dD1!GlJC^j@Eh`<5y^JVF08F0OS!o-AB!> zdf2Fyfe+zQI%F#cTbD1(ujO);l}O9d-Gg4s?C;&|OQkouM?eFMmC9+b^|K|C%91dj zN+r=~He4D_1P0{*DUuQW`qg@8ClryUc)Qx%fq0<&pi(i#6xW;88xW--;RZtPN%A2y z3dBSL>qewFDKOr-*+zrR4+>x^5Ai+z2}i1;$!u(;Qe1D;n-stF*J2T4bqtN{k4OWC z4^sF#o(S;K@fq*Vc;h4Q4eveYt|72gy@DaRjEO3O#Zv01h7fWFL{W-GDlS1{C&-Z# z0|vuWMzC{@ra)<h^gV?@DRDeCZYRVt_xdU-^4ftHsym(b7Fa)p^4+3<jf%)O!L!#J zA<$R>o}#NAy2}*eW63(^*rH#EWUTrD#0JcB)ZViFi%z;iR9o_zY(x#}L9QFhS^O?2 z;td7q@Kp?Z+s#&?yDuJv5|L%NjIf9|0&vWY6j<IIzSA3-`;ZjM>0Wvxs|=9jt|z2! zMQQ;D1paU+fhDZApCgaz=Pl{#kCk_}F<W(jnPA^1@X#O%?l*#%P%q&)f0nJc%x^M1 zo$fyA^<mHBP9(5&TX1g@g$R{K2kw(tfMbIhgVHQzr7``P`uaMB;M;XmWIwl6-7fEK zA9!dhYN8wFx$kHD46R7CZ1BOAr5TP8aj#tN7s_Q-=W#w+F5iOIWqujMD7I_3JzXw~ zh;3d8U*dSZ!ow$V=;vVvfFjn}RRRZ%7V3<M%z~r+{af{|Hn>kvb`Nq|!qYsw#e<XR z^o-0SJb}Y|_(Zcf<TE+^%a7%A`D`XVSD2p6kIEA@{OJCO+Wd=N0AfFg)7M8~@5z(j zgR={_7tTITUJqVg_`%eAS~z}wZDKtYOhI^?4)%SVUYo3s1~b8a<@`855FC{A>0mZE zgg5sEbHQPJ&jd$;qxjw*91EVn_W?ZnWbl+cdl2VO2hYg)EX)?q;`c+reDEB;=kVL{ z;CcD&aPUH~fO|)R#b62FM}rf=C-8kNI2oM6_Y*-WcoE-E2B(8pf>+VHr(g$qE%+{+ zJsq44J{i1@vu9uh`c&`+&WiPCA+=1cXM^S7O<d3aAb8l?bAZ3M`Xhqe)hcH7DrYW& zfK~)0qoPOEE|yOWwW^nHl$Muc%C)tt&z>ntNsII5#{5ZV_lZf1rBn7>smS7jd8gb6 z7ETr~LzBKy4^P6v)Yt-57O{IZ+^H`;x*%a;DC1fu1gT#9u9udU99^@O;(JNkPpa!E znl!&+$USWc#*%^|fOQ1cAPk4f)g+xzqagnjY2Q+=2(tiHD`Jmm<?U@@Dox7?<p*c+ zsuynn$6L}i*MeBxQl3*Po(J)_Rt@)<HdJ19tpq(6+X)#~#3s0Q$d!9Ht^#t=4yAD^ zrwS_$%7SRsz`J)EuyH{>wQ|7;mT}@iBXx>kBErC(>aLW=Mz|Wyu=*{9&BWM?!uk!! zqhP%2P_sE+u|^ry>omLgptc;=*Ox0>#+q0*wnB_grFe=^6T&blrH~O@3y*_Bo$~W` zUNK^J2-Fk;4R;;Jf%cuqK=(2v-|Ci(<Z_XqB4aAz5%_aQFa$4Ak`uHMYZ{FuuI>Q_ zudlEaN)v5}5^l~0M6XE1D#%9CggW%r81!LUh0I<pLYXrrQ3X#(2{6aPR0?^DI)*QF zm`PCZlQ|+Xu&M1kEdrREt{6a|A{t9cEMJ@p0%#MEm6~EKL^bXBRj(2$+QH#4N<d*k zd#S@Z2y;oWHANe=h>6b%#xk;oVYT`dTWuKWz>&~G>RvZ7i8Tyh<hm_Y!I;{yCS;hi z_$bi3-hx#Ch$(F{rb>Cd)$Y*#Dw1U^1@E+AH0D?v`&bpgAm-4Zc&8l(v_}K(Y35Pm z35U(K#~W2U1Y-Q$Ew*;HuA}{cw4oiLjhpC%*$$H;Ia(+^as5y`v+c0b_Q>?Y2hKK( z!vfDhLy!SG$LublgVuZkix0pmTkgtzY6xz4jpA^I6dMpMnnFVI6gxeHXnEBEo(`=D z8EU^GgtD7r&t@@xjYJ~T0fVNNEGz>xT%j>EE9e+dkpZzYLrigvmmXq)U=_W!C33t) z?*y_}i`#9O%sDq!R{%QjQV>pFcZCUd%W8y<oP|^xhC#|Odoa=j#oLW4N4Va&(IQf# zc8Kw$eXgLb>Mo&A8i9eZHFE?VC6da}JXoL`wVh@)bZ@)SIImW8PP7%=&ES1_f@<<| z(ZlcUHc>%PJxM2}70Q|$uu*fG_{VkyU?3PcYh&;#=BkS2tpdHqK(7=@`Vp7J29}D~ z8e0u?!vav$*@Z;HaW9^~dhy)q#p3F@x363*f+fTF0J;aTLghoc9n0yJ7@)A?q=PCZ z%OcJECqV(Y8U%=UZE;<APi2wM)^{<AfJxwV$1LNxXD;a^CZ9<{DYd$0yBaJPFVxqo zJ54Y`4BREmHmWz|MCU|!omzQI_AN2O#Mptkhk=Id*Dj5biiv5-6-_LBGe!ubw%IiR z*H6W82TaCx446YR$OQnV>tTCKrq^;-2xx7rTU_^mpt^H|Q$d;mlA$&~Uexb`&GW>K z)j&f~Fl=IXFbl3punaaEwMGYg&FQfo&5DiAAgQez&g}dW$HZ97A%+M8eT=aL1Up_^ z#9BpDnJ60d;36t2DZv!L;dTvIWZ=Ljn>$3JA>s^3HD<tpd1V;0n=sKgE6VT;t~y}Q z;jJe@sZvq8`zBxlhJqa!gFR?|;AnV2)gqE?7<o)3l)XcMH4iL3!t$&03QRCd6?Ko6 za!MaK;a1lW;SnNp6Wlo1J!MODqv;Ar$N+YALm*VF(_pz=JcQK31ze@bDhs=!M1F%@ z8#XYi9zfd><&ri{h@im54>)?2ieP31-81F7vP^sNimFUISfvePIA>KL_Y|bjMA`v; z6mq4+lkMvEgc~d&DP9Pfw_qK#Q8MKbKq(}f2ad3-#9<HXxB-ziHY+*uOThtc1uYsJ zZz85Eid>28(2f{@>rFI~20!=SAZE)TRGUx^V$d+48;{qJX){6*=D}6}XgFl!ED9vC zZ(xP$rPAxA?~2D7)Z{_v`4F6O4HG=x^WY$o3`b+m0XT0f9>I&P8(6&tyY$xf4DSY6 zux!*-E^x0LKmf(fL=s~xFgl8^B*GO#RzfBY*lL~PdZF5bf!P9Tk3|q&Vg58=a4xSG zoAvb$I2>v@32%{ylT}Y3^SOXF&`vL9TL*}-Gk_vp75KKb%?`OH333DNukXT5!7Qqx z1UFib8ujZ~;*Ujtj1)y3LHp)zg0&3_ok=+S<ARx?{sS<zYzdW8eUA()<&ec)Z#Z3V z4QvpXmGU}DW(cy_C*sj@or)FF>h?Bx^ZHImvCC7YeWGIJcPbD%Vvi#5-e>`89s}ds zp2)Wc1vl&U?U+l6OASe<Kp|C7&Y|{{6x+dy$n($w>v!T2Fe3)^fikI>GB}zd#CW6q zfT7iO*0^Lhw&XHK=N8S)wPz?^Te^-Z^we_8|DZq0ynvY-LzXKIlR=G*iYquY?7xk5 z)#EypycZx<Ql@4L+$-WkLE;L*7Zxw0A#Lx35CKIhM_ttD;6bysL*JbySvjQXh}yK& z=E_1TfCa0~-C-N(MFTY|7CSrDP4Hh4VLWwCr@$}Bjsa(qec|FH*dy@T!dX1b`u54< zF6fATFJ?)JF|<SG0pnvpqTzI4k<k7(DVKYalxww+>%FB}mMR)EyiQ1n0e-QB;0kbl zPjCl=8iIu&LUAmWTd$4>51avSXP^<5KAa3Fth%}##5<UMlg$JjaBL+W@BvA{6+*;Q z;8Y`NEGVfm2GvSwg}|W^<=oq@33fiWAtyoSkae;)*!-OkimR`57~#T|AoYuRHX$d9 zz~RmKFb#uZ!)uR2l?V&2LU2HOsBJ>b9hg*~Z&BieUlrVzz@-Q!{`(ID7{j>-G>&s{ z%_4QD;3W_urlChcUrgTJI1fxCBt?2)IutU_i^lHk-WtGFM~2!k?5nUM=t4v%>wzJJ z)({Yux9hOMiqo0HVK)#RF;Z13X1xIBB`eh!&C`tBqhatm0MLWkA<8YRJ$LGmiSZqC z3D-R{D6VB>A3<?`ciuNxQ!JEm_~WgL?8b{B4_W#ckO;)?7SAG^;Qv`N^O%o`#o<g{ z1X9;mzdW;&!dw4BWN6!7x<t?k$>_qi3475Z$?*#bnOpRFm=sVL7dJp8VAzMAPMOeX z@mNW4nyl~?*XyenSKq(7a(U&`76HT%=Di7`wO$ARF$9t3<d9mQS;nGm&lM*2ei)c( zcexH9G$_;7IvBy|3r|T@-k>JBVzof`uDU*#by60W#8gPtPQ9^#oO`<|9N3UH9WwM% zJfE?+EebO<5NEf8ynvyhXQ%T%!~)UIb<!C)^xCmEZ<3jV9MGzT&eE7kbg5MA%mGOq z$AYpb7;Im?b2FCD>3_J;Ns6H$U}8)ZEH^fI0D@a@QD0o(aRdVl+U(i}C4m*OZt-?4 zCG2gL3iTUO4{<4vC!9L4X`3r57D^n88yv~?_HyxEyWU4e?E7+f#16x>-QI?AmSADI zoU3RVN=vw0@%+TaHv&$+xZi-zWCSe?N@5|zOlSh|A5$+->f~A={6g$7$!V}MvZH>E z`$OS?OS)&Z`_onjL=MITyTwS?2AyELUJGA(3zzM%0sUFT0TU%qf;}I7!+*dket_#3 zOzzgiC6XiJ1t2)OGVq*w_!y48r&jBOL$ir0wCz}07F=Qcfcn%M`Nr$Su+^=YW|K#^ zp(4}6--v2K9ZrPS+kFvgjxq8>@KVl7dvp}9bziSj0-}c20H`IFcRdN`Uny7?X8x6e z6-6LDic<~?fG}23`WLTYeJA3>fXtILkdtH3%aZF^j;D2YB;LoM6WHz+Sk&Ta>Pj8T zZf8#xPp`MmE>&Vn_yFM+2S^f-Wl|0dmb_ci+A10t;-p+?v+??_5T9xji_k4l{^Cve zr$4wHAx|oD)}jAgNBK9Mp9aAdT|l6X>0?aA%tX~#@=$2LUPrHE69<{}yyemJlpkpN z1}6vYq}=hIPh%kJtnV}_tvJ}=`^#H(*e)xj3m2|j<UmFxWuB0)WYwr55eA(>kr<TK zbfAn9?Zb=gEUXTu2=Sp^cGX!#oyTHNfQ3L-uTNSjo(2S&lKDL^JqOv;BYP~XQ?w3C zd!AmPTck8*A*RW;emi0hke}_mL}$KtPq{2&aUnTEknr^te<WfuZfpHMBW4>`zqJDh z=hDlg*t`IOAT`V~^cYno-!+W_iCMsSgxQaS?Mzd3ELXZR#MV-)i}>SNh!TP~Dp_wJ zelDKR7&EQ33Aj~}xmaBXs@PRt(=LC_*H!J-e$^fYe;Bji64hx5q?85u94_ks?WD{m z-UiFXOKpTdh(83`r4R=S2?Ru#!AKX1T-~QH#yTN0#9$%rDX~P1c3YD)pLA<RXp<}= ziQyZzDSC%lB?C2C88ikl52T$V58b+S4N3hY=<^HD61Eog!ajzV)Hg#K>)9TYjv&Rw zxJZg4^fc+fCXbjH^bmIJAs#cdmb#%Hbe3^0CQBN2Nsl-T*?<R(%nL>|;UI$Rb_;@w z#@(?WpaRKh7%0Zd>ALQ<*@DxOYZxY&1Bc+Xa~f_5*;_)yhD^Bem0eaBIVkoEGO7!* zAfdvNh=?rEI48x!mMl+yR=+Ucj|p8-@@uG>(J<&Hc59~re;veIxwf+&d5#(kYLZ|& z2y4cv-pnl!j1OAaf#0Dn$m?USFs&J(i=wP!F04fmaBHI{F&`H^zXJ<{9&Ln3$jTn< z7G{xTP$IU1LM%fQ;89M(T4ogo6BFw@xUpXL6mU7vGXZUj2Uey9*bHTJvf|;UPZk8| zKj(&to*pDq#%Sh-0G1jUrJ><g5yOm7JUa`P2t3%Ds``Tj+lf_ELXW{QWgyL5it4QM z_`so!DS?iq0=AsPaV*k^K&vFaedXNBXUo^lf9B#l=d3dixkYTW6kDo`#VgXc2hhjK z9a@I1z*{jxaaLIxoNbK03FFv1SY*N^P10RA?kEhCL6yjrWr3mMq)Y@I$c%wqK&-I8 zTVV0PbQRqo?vfCOh(@<TOm5<3+URJ-4vV@ac08_D7({U`bAc!hSh$Ra7XgX34gib= zJibnziKu6F^8=K};6<`~$`MClArY(|kLO_Lnq;7cov<On5B9V(RPFH`?pzga7L>85 z;~hzbt`@P$J{YnoNeT!Ew`(kPL7@$o92X?!eGIZck0O8|&N=Bak}M5|Z%VnI9Yy5= zod!XE01tgEKn@wHY+fLL782?@RurXQ1n~IYFtm8ZM+pz?w7I5yk3z+z0hTd5ZUp58 zi-bl*`|!kx%{vK)I+*{&DKz$`qd`mDrNDuqq+iG4MS|xVMrN_2rnV4`#8L<qV5TN> z+C0l?I$J)CI^?W^R-v)-cdf3-fT#{}{FcypK=O^M$`dpkLb8DsQ9|#Pi9$+n1*oHv zsVH_%5_}36%-rN;*y7nE<6>@Quds;lj2a`+_{U(FBlx_FPxSY2x0jBlHJt7~0(y3j zGbH#Z1A{Yi%m%4HkFzXdi0@_Z<?f|FNVT5Kr8=3-Lb#GfxL`WfK|JUE^hfD0q*|HJ zGtwr9NRQP0%)OBwz7a#)?BzB_?v1o2dn27uT;B~+_${+B79jQ$&oPD)Q6X|Z7L4~Y z2t4|=pl~nW$#0DJ@)B$`7Je(3z@5Uz1n+#5mUqVWo!)5J?~R51U~<nBQ}V=kXR?>= zji9bVuYlG}ZH|5|9bV{-PNwjEyf>!booPe}rFjSS><gxQg}Ic}g*SSIqp5ony)pdy zR&QcsriT{%a2jDy69}Uo$KB7%JNxWAc1-ck#K!)O1HFk}?mqI$p!S1oOK;+SHl5mi z0l&`TI=7zbrGFYxrCKK5ION_y4Q8bC8*}$lKXO1yIjlWOrx10T`kvI))aO$=0}_=y zvl4R3Lk3TUSsc2@1z}*h=Hd?ss})4KdhB2(d;*X5rxI4pOZ|NVRAg<!BSYPnybJR9 zI2OrJrGd9g-4_SmKuU*pYj>-?V}#^AFT9ax5Qq^n%_IpMQP?el<43ayUh0oKwXOT( zG6Kbg=c5I-)A|WO#FNE8Cq!BfNX<w>y3vno5{AE9To|ZV=>Gx<6QoQj2zGZatD}6h zQuR)X-lU+Ga;pK@+LOC;Ub{%0OoNt=^hed?jOe*C)_WN_c9|(rE=q2N@m1tqkkgRy z@j7*rzKGJ#!tkvTV*3!1+d}Y$Wf&m?xB64(46)E+7*^jO?NAXzc=7diyV*Z@xrJ4e zMsSr`4x*0MJgw2gwmac6-<wrahmBl@`5aki5b6er$b`C&$ZP$BqG=91HyKse>o7`) zrvtJ;gos~5IG(g+X%eCH%_uCfn#|@M4OGo<nUECec=%`VW5~$mes(Lm5nkctvpf`e zh@;&k&}{|xeiZl-eGw-qWE3c5GU?-pFPzRyB2t?Fa_LF@<97ic9w#$$g(q;HK8Aal zTps76xXNWl<(UG@NN3WsIOgz6A>Eyl*)5qF)ac=3WY(36^v))#5sBb2<A=X^H8#x; z1hGglMmWJ*3w1z<fJ4KG_pnmKvlt6ZIv}Bh!y|$FlYTwF#i5ii$g%DL?XLf`V?Lxq zq)Nhk2!MgakamCNycK{T<q-{S%f4g>M+&vfp3M~N9w6Xm8@e5E4gPbd+S@?XqUE^o z<2@ygypEMxCaFC5A@#U7_>2?IaPH`&Cls#;aG6k2fzhynt_^@3qdz4wzx;POSa=}S zowksnKh!up{F}mu$u9Qc6MYMhcaW6=IR-KtnNAjbCkGCTj0*QN_?HGR$n;X>5xJY; zU0jVKY<E<6?<Md_T$M*#%w#dz83X_Qe1KSV+&L!ifWwnlqh$O}9?9Q2d4&9?%H!s{ z(3z0y$<9;&?*7&E#<b=<Fu(6J-!rD(@$feSX#=kJuV?01)`3=@S3Hw?$E4m-InVdl z+HtgFOs>ZvQ7Es>1qFF7FZU<pd|b{a`3y=sC|6Vb1y{4++Q`)be*CuFM|L#aJrwLS zckjyGefQJ%3U`aW0&+PV`7m>D;@)IuZsTwd+`KnQzWlZHtq(sSug(QCH?-s<X#LT3 z+!GE>IpF7U=NLwRv<E3A*#FfW<|4K6L@)cpfO6r&2UY}!6q{<8&Q(MnA7oY@ak58} z6IjeCzjsHTk4n!+{V8kd6c>Pg-d^@+>=`JdEXjki8dlV9_4f^|EB6n$D%_%bf6NHi z8lXMyjHV1GRzo3hy@5t9J=q_@dU!nyFXJi<csRtvRUWE52zmG(ob|I%vioC@VVS9` zKcUzn>tf+Ff6v>s?*8nsXpZnCADRTBF%pMCrx6ASJqxSkR#ZM(HADp%nh$%pgi42- zY(<j?iEAdh^hdQzYvTxd)((M@Xi=JxqCZ}?!zcmOOH&g6EoE!J$YR2mdEjh?>pa}x zf#VWx@UVpgGAZ5e^fT)%Oy_vHEU88i`7T^W7|i$Lq2DE<Gn|^4TV~UvnQ46UUmh%E z8q9?M$UgLYHpBZf_)KOdGt>A-HpAzq)3dmm93hJVBg%C(vr$P1%1VuW=iiw5oX2-` z35Q-Ld>ZIaksLAn0B(qx5XsYD9Z9EJ$4N?pOpyI51@KWkHM;jH0pc9O<)#1KUs|C? zO*d)MTyf2%<P+PwVgItiQweJsSlOV|qi@KAbtC%FVZly?db^ya{a+u&st{?dP3R!T z1Y4}JqPkL9kQD?<%sq9+F9;34j)6eRn@KqLmAPz8;if-k^r^K;016Fz!ihr?-lZ{x z2?2ANf6h8Z;DqOf3<zW-)Hp!t!!DkG65KGNo|VdS&PFK;Ac{Vd&X4F-cb_5tb`%_w z1k|L+8)X^~;DlL;HlURSnU9f`QqrB|x~rZ9AZD(0!>9!H=su;(RWM}0tp#c!EVfof zckB%|?Rx03vP2u$j%)Zx1{t(K@^>(E!oIRC2zkCx00wy;&kYPiKYzMe-MSuB&%VL2 zzyqo7CrqEUNgxa~dq{)h6h`(fl=>{*LB6C>eLM_#IW*G+D8}9*UuCuUfR}cUKCt3M zm$P_ShMoBjI*?8{<Q)79Py?wRFJ-&`o0UyUmxcwN8KIt*XLu~r@(j2aYq_^d;EIrD z?GbB8#)x289Ykm~ig12%@p2HCO<XZ?OX77@)f^D@w}V!+7|-Pp%1`?;%tZ52>7_VE z!wnFJk{?c`B#7vxzHjm#lY*o^2Yz=8vWLyBVKO3eW-))6(o6m9`etnaK)yYGn(PmG z_|9IkQ<Y0~U(%`a7{UYK#J&t+bsJ3zcW`(BcpQE}X>ZzkQxn~%9$K#(1=>$YXhnqm zByfZTR;96fSvk-2AW?}dlap&&M`#P-b7~inoKdiSQ0^Lo?V<t+0zkHz4J)0@Mm8AZ zRc~}1@}1Z@Qm~Ei**v5SlpOxl-Q##72ifJ=z46}o#z-%}ITrSCZ**g<H%^)GR__Dk zstWc?t}5C*XjAzaDSd(#jaxqhtHGq){cUO4q>-QU!Ia$nJ-It2(i7w@NIOsVM!uGg zG&2X00`)_RnrP+LX%|Ut6!4h<i^)TV8VhE~WN>E^5|i3AQm~<<eaR?ht~W8Dt145F z&vsuwU!_x;h8Wr<0RX&JIL-2npoM7jz%hO+gJzN<Vc*?Xv>d0I+l41^7d<t4(n2iQ z$1;VRasoXmgAhX!<QwSN_Z1ZMe>8I|cTH&@fR7oJ0FOV2uVFOt+lAA_ZOCEUXE!bE zji$PbhCGQLU_0rPMEeq|5CJWtVr7lhO{-WY5*hhMxaw+Wb3)>4e^mA9xOc*(ROZ>` zQ^-V;TAEbW7YjkjDT-%E2YQ|_l1PVtj)wsq`}4T>8Fun4PErtMX7HhKL(MoNLQY-; zoEZo@s#)hnESiKaJ(@d^QN{^jDBYbMHb999;v`AlG76o5J6JG@2Qf`LQWiS~gl*(0 zlBG(`5f<5uL?ZPK^npXOV<#$wd|;*>zEJ6!L59hXa1haMl_1K%Jb*IoSEVqf>(O1W zy2F0Ay=VSJRqlfZ0f_;EF&$p(!JqXiJY*_NJ~6yHS{6Cbn$m0qgnq*K+)|3~9}-a; zdI=y8{Ksek!-paKQIz#1*N#m03DXV=M}tKqT0^{)@#eIvpoBJoaFP{O%a{&NPFg^_ zG#%%#HB3(q661o}RltT}tb|ovorAn=VJJ+5QFWDYGL>ECvjS_WBjz*)!CO}jS}Blh z07WBns!26w7tCBaQX!1A<nzWwRR@Jj46y4^LN;_z91B{pPjB*hUDd*BU%m6Dm!+BU zg?<BMg;`fNTpKDCeOZ>Fq#shbFR;MwL93yvP^3W*m|>EMkK7DTbjP(b7{tVUj^Z)S zX><Vx%3m8WeIWfN3>`Ppa+c-UVM)*aaf*pK$%YQ6B!$!n5=&wkLGRp8&k_IfE2gf+ z=cA==2L2?(QU0MdB4`U;r^F|89U=|j2*e{n+50&<KpWEDPv0FRcK<qF|GMoSDEhp1 z&*lsqM#~fJATk}qCz`kIICc*vo*vr5UNdpoedIKqOKGc+lo1AlGw4w!{5sy~k0F~G zp~SUtGcgaMld_FJ7PnETIi8Y98|mjZfN4@#tPay;Ab#q8VfI|gWHPcJVVRt`6m;Ad z(y>{KLk8v8jyJy_imxjMnEDf%WJv7jwwaPOE=itg7KZ{+F8?LlE0AbsZk1mWm9qgt zEXc%3x<Xj7NWivdj3M$Bk6J)GVfJfcuO~XI{uyh$NZFZDHv0W2)Q(NEogshv3D?G4 zx1i8L<#Bipz8TX%h=Iz+sC#6pKZhQ`p#X@#;A|s4Mblzq*<I)uu)K;@1$r#W@78x) zePqNz4Gw9d(s_ApG}ob;^i{#9!xDm!L^n6N-;K|hj0S%8DCql<L?fQ$Vvw`pz|FC9 z2RgozEi?;DHdw-}hg-7#0#)8oo>-%?3KfawXWDn*<`8NChjt0+Qg+0iOZx0Bn`(cO z)x*1+{yrqBMj>EyNK{-DUk17#V2~t=%?tdI&cVV!Lo;KrwyG`ZTG9E5nnd?-DQs<e zLqNc|jd|>6u>yi~0pKET4A2g&hhT-I5jk^Lr`&G+w1bvOtGo_*YB=zS1O%;2Ewl>R z)h;ma9Nf*YCKdbCIP>~>L30U$l>S=H(mf!M<I{b*g3M#|<~f>VUK)>xMuJ)HdEJzc zAub`<43E2scB>ckTf)LQimZ6C7l>M;Cm!CA=(p-^talS5-TTH-5pUM;Ud&^)S%CdL zZPBjis=nV6C3U*^COLStYT$-3u1d%qqXT@eNF=?xc;({x)!2>b{r4^)PY%+2cWOv) zVDsceUNTXm&Of+uu6J(j!G|jwnuJk75t5kV#0g;di4#_1;(W*=qV~hgP$w~PkS`zt zh_a!pt{6#n&Wo+Lu51{qDB1y%GSR<>V^W|bDYr|h*NNSRlwjC6Z_Xi(2z<4UC>$(~ zR-3Miwi5V;xezQ6ToYT(Y1D}Ue#7`^GulYivD2F*T#?%7q;l@c>cy*g4JkKT*q_39 zl#8nvy&uIQ6GKqlrU{`$g)vZPU6&HY$XMc+#IY8|RMU&OKCqiU{A-K-IRj&$q1%vw z{-{1NNC~Fk?F0-pvhpuD8Zc`WkHqia0rvl2DC%L@Z|!i70=TX{0DZi^tr7~Esh*>t ziBv63hz)!o{RE!@OmelXBYGXoz(=eYbv*D>-ze<F0pIt%=1lQJXIq#`4(sRwjj`3s zvTi>MBw%tcg*fl*k}i-f=BX<WK|f=#lacWHPWgO5O9&kzIQ$TMxJweFJkCn^0(}<A z6ldMo4MDS2%?efp4%nF`(EFJMEFukSh#;570`Ea5@o0B-gE&$<0&07nz>==UMMeTD z+~XFQO6P*WA7pJNvPc?SC7+GQ65z#V(6`Z*q=DVwWxB#}w_e*(YJ{a2w8#TbE?#)3 z7yiHy4!qytl^w2Mb+~u1Yqan^&$#tk>iSM`VO_&EAPBgNK!&TQ_cPdX0h<xPIxz~d zZmipNs7bIED2_u-FmFQV;q6ZPP2Vy%3NYTr9vyHxMV!3*jgZZya5v^IT*NQI(GZ9S zq6|?RkQD1*q1r*kgYEPU8CiOW$hIIf^O_A}!V2Dl37{aY7q`ZK-mWkznWMhYpT?F% z+Z}A=p}|7G>xMbqeOdZfGQ*6`I^0vS_LO|PpZa$CR6kv&-oWt>@AB{hTar_8>)+yy zf18JYhX;z~Ai?onY16?WRsJ_@80R&WpUyf7OsTWqG&g>DK3=t9v9yaryI>-1EWL3Q zv2vd(!!}IQ#0pxthNoh^S(FGXCRX`VQi<qKc>4nUo@-wEK)SnMN6!0w^-=;j*%2|0 z(aP7$M90)Q4M~@wUo_Ia>|ch7mb-z3iybs}13LCKN&YX5#hxGslwXlCnQ)W{9hhS) zU%{cD<AjEP8K-vSe~!1O)qJ3ttP#dh!Oekg$@{<0?$ENH5~CkYe}#<oBuVTDz+vVe zznR+qJU-DP4s=_DYr_m}Krl;lS%M#!)6pMp+A)Hi0%ZS>?z2Y8lNfYyTnB|w>Y=2S z+c2owIVwwn;g?WB_+=c{#=+>!Rvlto73JB;_TSH^1}1G(OZ>;|Aa(py7K^>GcXlUr z&h%$DS;RuuEduheZqbSl%*?Puz)h-#+3s&%hKL5a2{N>D(F!a+R`?Wa5vhoy4vs2F zGmvR5mY@bw5P?oxH8@^eqV>rl|849NmZV8=Q6>)<3_!DSYtAyJf?^qqvOY=T5@P|e z7`08&K><))mPMX)uHc$&hr+|WweOmQY<yasDB7X*!I6ph_wmG@+0YvQ8E2z_-%}I1 z2%mBDp%45i%*U_C^Ks$oyYKz~U^b}J51EUQ18F89{6!9`jQy03{RP<s?$INkW9d@> zZyoo78+T(0;m<WfKK~~0as0HK1{T2I3CKMS4w|}$RkaSUo+dxNmuXGQPAuH12k3r} z9#VKhR@_E#hx_)dbw=O~h5dXs#^}#WN(_=P!d-lBr0$L0E#jBFtdwCj4j#v2Sj(bg zgqU7#t$&c}z<0L?^ILC}9=doMzDM8S_Y?`|Ur68m63(YlLg(ICXJ2ougMApoe~NPU zW6drb{&o+%AAUk(@LbN_8*d%x<?(!GGZX%UUZ#}^()Y%(v(N#l7iaLV62Gon-^87R z5s$N2f6L$crh6*mKZTXP6_j@f^<SWWvpoGNcrYz?=5U4F`bHjsxAn()+&N4GDSA@a zID&HW-{6__MEsr>KcHFl=vUBtP?qs(1(;$eCI!lE6CDqlIY31ASjm`>0UIlD`FStl zu~fiE{fh%x?kU4;y<9xwoJtnG#3%d_qF#ufg@`RK@Cu=wTQ8q1o)?40;=-QP_SG#v zwf#VGTdySRViop}g@ga0`)8!u5;h7k4ycP;uXqL7Xi@Vcg4IM?ibu=!+?cj#c!HFt zYN{_TQ>TnyQ%dcucP2p5DBB(ZHuWp(T{hmx3}ur1A{t%^!(+U|83Givh5AYyyO4-l zqjvXzs3_!UId^%*hDDi<m@2&N4v=H9o`FQ+W?fe#TNJ8scU)QUQju*{VW=~z4TizN z&)|4K^+50(Jz!8=)$gP#FhhngE=z1nqArSv&1a<<Mwx}It&PKpUrog%GLahM(o9Ee z$ed}DblO;8%oF^D0W=uVfka<%?;Ma=`vqkbw_0RdK>dg5l57@ZuQ8}2&f+-8ij#n@ z%{sGy{_E+22hN25=LF&f`eB<iTNTK-&AWHz*%kqkcs}Fipb&>ZQp7MMg0GUidxm*p z2AZ(-Y63OcCNr8abh*6Cd@)v!2Aj@Ehj_~Q?BlnY?+$76s}E|k!9wc=WE>63>n#r> ztvi~h^+KVh8I1Wu;=uzNh@%xj-00OtqDx|rfLfe52bK@GRQd%OjFUz&?-xvAvb+KC zjE%y>{hB<gunM?!P3LY%+svm;E6`x5p%pFx7IAM9#71et9ZUqwKSCEY$a(;^9H)ub z1t6D#qS3|38flzK*O>|9O6L=`8lyF+5rkM%3NV}<bNOk^IEK#AJ}%5qQUiZNF$RZT zr_dX;VXTet3FvjMVlj}|tPGvDxfh$4`O@S3#XUPYSiw@95Wp`9s?Y$q7SEh1E?h@! zrK&v9!tfVah#9AW0^%|ADq<dkM;7FIo<wIB1Cpcd0YVD=CRwq=$ay6HFvB<h_~SRw z17Pf#y|;YgwAX_U`@@x9iQ8>R0pY|${ztQ8#ZA8E!4a4pO`FCL3JiuOMoOmKE`EiJ z`<N5phshg?YY8ikbwD5I6;stRP9YEUhZjQ6$(S7iI=-}rb!mD7ky*6O@#OCvB5qac zIT@~W-<B3=Y*y0HDP~(E38-#r+kmWb9199%pc*Bsw430SCbS6HQ>dZK{+2Z;67ome z0o4mxZ>Nv0tozUCy8l;F5PY+VwSU>(5+%{AtDmF8qBYYLaYuDbSq$d_`&X&sNDXNd zapys2Y{-iJz+f~R)vaXuqbA(xm{^7SQwh0BJ0iXo@3UIj*f7VauJ9*#P2)>{R83jo z8@zLw2abX|w1+%<8i)SuFe-}ebqC-){AoTsQI_p}#n~H5xG@AVtSG$2-$yRf^}j#C zaX~5@M!tprA*(u#!_tJ>;i|mBnOHk8Pzmr$ZD7yxkrEH{d~V;sb8EBfo2cAUYlGMQ z5r~;}^~Oj{iWv3^v55W@r{z5qoO(NNJns2T?|M1H;xvifRWtBlI0A<=-ldb;boKzg z52Qz7w#nd`+4LmNa_Yg4XJ*pf839~xl9w{8muTS2tXz`M@$ejb^)?UZd7x`cc#(${ z9xm~~*$h9;Lyd=khwtT~%fp}N;a}r{PIcif4?oPqkMKa}DRsl6H%9oAJbXV7{{|2L zCJ&s+@ZaU(n>^5F7ybedzr@2Y^Y9<@@HcTl%!CFs)F4>epaz<?fM56ckia^Di_MDI zul$!&UrrTr8EhA~jIHAG`H9K1({q#g$*KIQe3t(vb2uNx_h^1PKbJq0pUUSD7EvgS z7xousGHC%;cHRA9!sIWaQFm}-Iq~-4HnRD>+sK+t<?_0P?Ao|)9lKUA8^=zVjbkT+ zgTXAebDIjD2o48FutD22wv0O(9K+c@*{W>@>hDwGGpN41c$I;Z+{p)26a<G(G1N|= z;Z{L&!66MN3crEJ9i<_a;mk@P*D`t}PPOoAf_Xxng|Q(n&T|Owg1SwsW%wQ}u<0Oo z6k*jHa2yWbgFA5sPQRDLh%qAJ$S^H4<Qiq9!;kc71aOTZwgVgN;Z9E8xGQgrCEm!H z(tf%F!-TXZ&la#ftph<`Tz`K_-W%t8o$(IbdJ%X=$6gJ|q8oDo%NH3Kv*AFDD{R#( zS8yE0)f8;4Q<EAp1=9@fOtS_&y-%+8nX4JOnlV@V<!ZmVIv`gE%+*1;I%sN{m8)6v z+ab9+WUl7q3R?n7eTO?oBxvoZ`9`P~T64ra`$TY*y@g@rXXXA;adm#ObxbVaPj#NY zpTTc`1HE}_;~90Oz&TpKIsUaw_>w%e4_fE5bkoL@e+Z+}JfEO}sdcII9N)xtga{|Y z^F{M~cH=mn=5==C`QQmWgMGL+_R+J;7^dbsMJ+|HQyUl|sj(#`%yeFm_rGXro7q@E z4PvNjAtZfczO#t8XY>xv=FHo_C~wcX_GZGLG9}DOduQcc);!0J6V(8PE6j-0{=dun z`%UeC-@LmYwLfWU*AiI!{zM5&>hz8Np3qSY5R-&)&xSf5Ps_M}qL;y)PYUEc(>V!z zfC-F#eT2e*-kini*I!GeoJ^tOKbH(Oxiie(>ce8SfxQ+H6~HLu^Gw&MAr%>HhW1K- zH9>L+KStoN=<P{h8hUCYLG6Y{l$6wGYRDE&;xpw$HrI!u<ZXq=3o}%B?M3=oG!UA- z<Vgk7lfBFyxx-q~Ijjt(jbetc?`)4XsOt~kWew_*X{2Peg5$`*{%Kw>B`Fq^JF_L6 zYo^2q2UwQIrgR{!P}`7^zR9h)l8fwKQP=A*J2W8kLeP=DF}U6+%CMa&J8X>H513>F zTX0LBEw2=55P<;H8~{M_?GKUNiuYYK7<*#HlDw*j0zI;QvYmXp(~MIn?S_cl*OA@R z)_N*^k=bZNXsnuVR5pNg&USEwFdT;Mi`umYLqtUmF2*i$vEzbCASoIw02==xx?67= zfDeYK;oD$oi>cnA83$Us+M$-QAHj0vOVO=nRr7f<aZJhOKfMi)W85GRx!oeEiW$|0 z65@wtP0OX|gg`*vq-j$ujcC5EFkxliW>rx5_NE01TqTkc(_-}vzeOxCDy=f<fkYG< zqa5(UBBQ6<h(W<+|KfTTF;v^cGpWe>sqnTq_tE^494TjD8{C1F-50wU2#~z5`j1V% z;&rR&tkHx~7^ts_C04gdO+2UNJ)lEzF%s2|axUwZt-dlUHVh;dK!>p8WXHwADLZbq z934WXyry8d9Jylj+AtagO_b%i$^drGEQ$AVY0v^d0LKp6tCc_+gB^g>t5KT992>Ay z%Wzr<D6Jsmca{ACe4|dHlSy;st!H}3VE6zVAVy^1g6I-N3hM}KXug1?*9cc<tRP_! zTfvLy&ye4g2Z;&FsNZO&SDdl>GW4cXKP8P&tDC9bT8e#r8?83KdbyQB*~Q2UYQb^Z z!pZ~4rP!Q!4d}Mp7^>PIwVO<nRdpLxcuspX)v!HrhwOl9#9)Hl#frPy>;rK!c>}6D z!QcK}Gmd-2GT0}#QLk()!u}JpIPiO`6E8@3eI|+;GRl^ri;tPObRo7KdDq!Nat<VI zZX&$ZA9$=9VMx%?W<%_oe2yd*yd@^)hIT#fCXB{)4cjw?4J4}ZTJ73=O{~|Ku?JEG zcD)dLu)t+lR95f<%a`n=?_~)fH8yn&MvN1ykRWs&atR2hgdjTtis$33h6xO?YBCn~ zUF$rU_U`KBUr-x*#W$02(A>+{nEnM8V?jb}83PCuz6l0}P;BH_gdZ<q(hv%7bQ}!= zWIOj=<1$T(At~KLI6sROpPRl_D(=EB2hFKe)KZdgf->`%2o+j}>l609+SXjV95{_a zng{uVQz+7SYw9}TMzZg<SChAMKw<`HtK~|A?$9M{7IS3mc$JEz^_}-J83Stv65R{N zVv!2gs%HI^!E?Vld4QDDpI0>U^2+MPPs68G&`B5#n1r}@0Ky;#wK}KNvmFSy3s?1) zUYZ8`!0OD}wL13*;;6;L<Bbk~aL$NtdZPm)qS;1<m{9L<$3`H!-wW*+0G>1-7CIxC zc!d~9=CT23+A>1`mMd^jgU`wiwsHU_yw{F8H^Mp{|E^uUdimTH8am(E#kE(=x%XG! zEni-N!^=AtS5~z?uyEg_SJoxWNC!LIVS)TQ;R%fmZ)HznNMmTT+kC_jTk|fVmrl?? zX^fc7+d*rm*B&fJa1ZswV2Fb|ksorZ%p|(As&gk)Ts;{Lb@0lvbOJ_PvvZ=!xoV;p zJxXR)E|}@ZY_Dq89^o_Swga|;m=3u!VhD(O)uNwR#gVWxKC>lqLcO?1@dv)2*zdu> z?NXwb3RYe@vO1W+$r!lEEZaLbuw{qyrIpmQT#(b@_6jmWD)!jby<kaVq?_@&Eqcq8 zlmIh{tE2ZQ{g1ptM6LtJ^pXY(+^cLVSm0<x+)l~b<&|PUL12~+&lsR2?kO!x;2t{E zT5sHdcXQwq*P9y2;*9_;*~Bd~y;Muk4dfl(f@lu;Uw)MBcztvR=2@!52DOynxxUO} zOb}^kM<mH59gvvqHl@3dB2Ln8Z%ioC3ROZ&ER}*z;npjIMx-qG0ffdYisFNet&Vd7 zz=sJUFPB~xaW@HNlJE+-itV^i8S*Lth{U9N4}ne=74r(9o*Cw3QEYOOnhzlXppQtr z{HAEB4x^IgEItskB2Xni2$pQ81*I1Pwn-x7Z3HX=neHhbyh&=N`%sakCJM1hONGYB zst_BOY!b-d7HBZqZcHUi;;J(v`Xx|mFSwZpd~1me_|V!(tHf8_U?+3r#S+JPHRmK7 z6{8-L>1h(B$3Q|K^UxmXnHz}gBRObdyK4Zj<m;oj3Wjbr28u1a6og`;>osVV?B8sA ze1da)LW5z@Hn=^Z0)mGfGL;3e%)K0opvB?|tHc4mRA5|`5GkM%^u%}0UAZD>Z(sg2 zzLydxaY8l=uqv!a9h2q5l%o2N@IG{TKnG$19Vg~paw$#d5-JuTufNE@iXOYT=vYGp zq*W>lx+hI}eRH8w8FV-yWIk7v?PNI335ez>CD=5wy*D~ZdEUH_>f<KD8H~66KLr}& z_T@dfAi;tqc!PP7eg2E~+zcZl*mbXgWLwX8olL0RLS})QAcZENoAexEtc_5}AI4q% zWfEPVM{Xqk>>GxPN`@6|lpc!aC`f1Lj+2IAvs#}>E-E=0cuR~7+T|=EXh^ILo4FLM zGW+**AkK+q##MR<E#Y4Kmj!tf^TV|dIL9c+lh!$qmvWKRNwik4MVfQBhEPkue02aH z7y{9tFEi5K2w+&MqvD;B936I^GEi9a$}d_qgqhu${#b=Jdc)|Rn-~x?zQQyUuZkES zGf?v&oC=I!N|_U!&dQwVu1KXx5@SWymM6zzl0Zl<Ux1`~dG!k%r+2QMf;_7%nxSQh zc1T2@jzp1l##M4y@E%5wyr+iYr|%8^`?f()Ib%&Rj|yLu8NrhTP6rY!F9DX2r4miC zM%M)SdDzZ##3s#K41*%tnM?(d*i7z(uy8XQR_3MBj6hwRJqet~#(g>~VxobAbGD`7 zx)wA@=!a2uozz50fCsuZQBByvmJ=Xr$e)DVQl%#LYzW(T8UZy_HD$(I({UxRl_=-( z%7B=UCLoYO?7iR_s!X4<Y6LcLt2TFFEyYX>+N9!%w5~~vlvE{YUSm(;35UJj%DQPn zXLoUFKFNT=r+}n7D1Z`JMs7B4+(bsG!2!DP_PO^iQ;Y<|GwYf}p+wK%#wQOz``ax- zL#x7KkAZ|&CkuAg80c8>Ub4o9JqTA1luEcy1`=0AP(iKSWmPV4YUiT`cyvuORsbWV zaSx&h6Iq3c{XJ2LWm-vVImqrU0aP&sBk{d5kBd#oS3-NYb!Y&pn1vz21t2N${s}99 z%N(9CR0~L^KyCduEo|G!H32WKI)t%vG@H|)6yn|{TF2WP5N*wzfutGJ{pZeIdH>=y z8nihz3Cn>pY^@~TjSU8fo5=ivh&dvL!m^V9bPz5WYYb$_L|>tWwGnZTNr0KwT6W$9 zK$8eTVpOX-2?`T6V(x5y^1(p^GQlRsm`@{h*}?{B3{0$W@`D&EV`3j-yLH0B%iPSB z224qbmYcOSQS<@fgJ6Xg1F%RWEZ~E-21(xccoYw4hA6cTvsbELYMiruFSNF6o=2E2 z3z&g~Yz?s(Gb)+;*syo6zC-#DL1G15Cpw4Wu(@qPVa&FL!ixE#K>+Cw0Bn`e<(`uG z@kC)ZmfR%%2m1gd%QgYgn{1Gb(vZ<N55+-3gl4z7LyQ(?`)Y5Z4emnb0^UM*w0k-l z)0)yXEVyA?9x<p1Dw(_7pdqmiK>bdE7z-e3(za9UfLvU5V9NYJj51<Tz?c}<+Jv4? z48l+N&yf@>h1VQw(l=zmuQ^^@OAM}9eX(N$Fo<howCD0=&TTv;TX+h0v4I8Zbo*q; ziesWMv`PR(nk5D%oimn5!D0`*iL=zBcdv317kf_sD!33PDi#pJj~*o<&MPVL)QQV{ znOPZVkLFsv;#i_AvBiNn#T#E}l{6oM4>&DPmupdr{h0(OQ4*?=g>uoQvG1HAz|f!_ z)LK$M*sw%7T%W90{RMdgpxhkI+bV-t4#iM5Q71F>fD@?ukS#}v7&b>!yk_ut+m6b+ zf>l^>*KDR{LI=$`U_7+H)`Tq(lIB<5T`jJ>f8|PYtG4Z%Ah5t{*-uvUcL*#Q;e^0M znVb{MQk1LF)<*0}3|z&S8%@pFp>zwf6r==`<(Oh87MyPs7s0wnR|K~#okR-~rOiuI zvC$PWufoNg>`s;)VEgbS7o)8^HK14zu(Hi1{Uql?m%wbaOferC@@C;n+zt`}FSa+s z^k9$teaXlTB~Nv%h%^opB(PU4utatPAnI<A1YK_o@QSg=VT=XQVj2pR6I>Upmr0_g zgb_^)-0&<4b~_LcfZDB{Z2&R6lD~6l$zPa<kPmkOD=n4IizMRYsD$v8R8dT~ts1Hj zXm(EpJjUQAv;ZThxm6OqbwRGc^CSi)BrWaf5a|jqnJCXldYmuTf9>LGELzDOE$IQ` zRHCRyiB_PWL&PcRYoeS=1;|&{dX4GMqwSnfm`5KFbSu*>Qqms5qyqnG+9R7t!2~)M zLExr)2PHBK8GAU02jt6KjgR4bs~T>SO9DofM2P*-x7sKAWXZ@oTvG6eHBh&urV=Si zv{pmeRPv2?^JdqaJ=L!W8)=@S76a=dEDYHF6DwW1K<`<}*v%v~4u}WR5}GDu=MOr> z)9Qj!Ey+mq$G!ZXE>nEAz6;qEY){++j3DC8nqetPk(G$(3cAEy&FjJEs2PHwpsXsC zeW+R;C^hf58nt$iR760a%_gZ2K~C&oiGZ1yBm3By=n#RQbpOJ|OXuFdvTAU2fD6R% z!O6I$5r$k2Aom9I!SLC|xN3B&0Thjpj=0SnLn7Cz?OgWz!Zwu&OArf;U0&|O-DMR` zM*-Xd%&ixnnTOUjKVXXEi+78w7eBx1TEBqD7Lu*SQ0tbmj~uO-w%J}KMoKveI%K*? zxrG}^i+kHcd$o&RT>t@Ig>S;1o^hrY7gpZ=U~y?-X-E%ui_9~%dikA;u9xy~vX?q1 z#0D%}vcx6Z2Lb^`lAdL-lEE&V=#&G)3#!H_QFjux(O#hnVsn_T$g>`Ba{}InOyLSi z!ZIp@5XS&iqc#ev$`0r*?NXvKhM)*YIbkB!;Y9H^vSm_#Q;v`*%fe-BQbKbejM#lF z3E`m1om1<?Ka=53RcO!^GUP;YF(yeOu2@6I+6J5=i9s^-z$Px(h;2cJiy{IyDNU4S zm>wldI+YV$)Y2oQrwIJIf%WBqk$xQFpTwHzA*5fQ6zw<BG|s6NQ(_QHk}=CRmhtQ( zDOs{~pAJzyHzSZ-OSK}YX9G&wUEO$Ljo*yb-YIjyCnj}o>cxa1?!=zZYHV{BYm%*w zfk=1HRUU2N#G|qfHsIhY*#ncsILHu~*#vTsI}PbAS^>70G*k?XfXYBdhmmYeq{1;1 zvzzoLe4#kGj{`<#Q0q2DS>)Mr4JZiJd(a-gipX#d6(3s48oJ4EaySw@{!%fqQU)LQ z#A7NTf<r}^p1h5IlcixKD;0*w(Q@mFl%5`MX@Ye~_ICSCmIav`C8#1KU7)l>?J4%J ze)yKZ$>PK>SqpIP(PEz+bl`Ty5A6G!ERN3WZhuT8po#!x^@fM-{rixjj9iUUNZw>4 zVBOLH;&Dtk<0Vo;6aNfEsL$4;%?=z(^?L?Ur6v!qm28!kWblfDl~~MSF|Aa@DgcWQ z3USZ^W4`)LT5gjh-2evT<Ky_$Wi?pDR&0}ALGVtc|5gLWd)ZbiX%q1+7=soFP#gn! zRw8^++Dmu_cn=e7K?$6oDcCZ^iUA=IZ8`6YnMu1~%z#bD9Jh*G;R!lm$@wlUAX^We zF^n_*SBvwJJUefuZh#oc2+MPpg{wy#-t_&;)KNDkrt0aGoO{a=bQ#Ue5aAKj%`6H! zleks}{)((rVz?N9Bby_sxO(pGD;EJZPKg{~tz2zZUsB}jyWZwjO;}gJqAh$t>9R$e zg00K}O|sn>^1xWk!Bw2B=rW_=Cg`BMwC)Gdz({iDK(XLYpvxjud|(F_KihaKUI)nM z#n63TeJBPy6+6?2xkr3@KUI3Qh^%}N21z2}mMPO}9Ytaj#DI}T(A-+p_Eu-$$(V*c z+ZmJ?8iE8qg!&qD44|HdA@c%8COMd5EOdz<AKkB)OAp#^R@+)K6(x<<?3CZ*7>(i{ zx^_wQ>PmlvT2EQ_-0Z&e4m?{3n%s9!SzWB>W4<X6a^cx7Q<t!p7b7nu(zg3@;%zC` zGZPGZT{eU0zH!ky#4M0C$rhJxjaQ6C;38sew%cIX<rEDb{Tc!lFZJ`*s)#+3RPsj_ zw}i^o-)F9^v={!TcvE72u~m<NO+Sm}h<@Hapu3v6HT8au$S;w}*c}X3_q*^R#TGuY zLuI>FzP<}51c8t4cN;y|Aen}T)9;`K7gult>cCbiH5%&kUg$!J+pQI1<B9AQ);<1y zOL>^>KV2TVK_Y3f&hT<^>6C7(@$>8(qoy_BdQuxEGZo1mnzG$Se+-SH0bln?Yx8*q zj)oG*E1}IC-tgCW_;nus77zb94{r3r-{9qM^6<BL_$?l`d63X;?r)>v-2Wxc`Z+>I zIL@0Vc|D~qH|knHZ#}ldZ}E}8$VVoSa2$qeX?*xscxTdA%=*)1B)5x4t3Q>{{bY-f z{s9<c2KIl_9oW8(@|H%!Q>;zGxL@X(B+2_#R>f5EY_g3aW<;@u5_61xGMh?893lh~ ze>pX4x89tQjX@Zp&6sQ5CIna5J8hPGry)9<+imJ`dW73|@|ypcU~D>zz0+`qTZrJ9 z%nahS5!KCD?+o`@<GyPA=5Mp+ni1gzYzNBiJ{bev-KXP<{X5MrjcK+LfBeqia~_}Q z0uBg$M5avUDGQiP5xE~_x1MF5ZDhh%<Qu`f$o`2Gi-(bYjNjK%E$;q<Z%MY8#dZaY zaPB4wFNH@?EOM{OC>C+v-_QGj?3iXk@g@2ngDL|rgBp+QPvVt*2tbaeal&2bxQ%H( zeE>Vnb*Ejw0Mdx~W>@%!iHNiG6SD5Ufaax;T<pj_BtK(UkbV%y`$$Q~j^4^+D<MQ^ z2I#_4uKVs)&6q;Fw==hDUFvznFaljp(`*ia3r16WE=`WMVnwjDdWpF*1^5|T-5)2x z*k!KE{v^G7LIgCC3)<v=@=#`db1?T`!7kR-qhlNr$;O!Dt~3~o{bn%YXWV#jT--Es z9MN}79HVAs!Vu}7OfdTo@XTQ42e6{Y#Lhr2yUpQoHbMqH-92im-}_X}LK~lCI60>$ z>f?aSb=(+(x!SmM)G<y;kY=-RCmWuSYsn*r>s+)f*GTLszmJ4}My?S=F4xGBC)Wrs zm+P@`L#{`89bj+njeKXEHOV#{JS&)OIL3p5Y{N03n{FT&T(2kf8c$9M7W_&u$@7ir zxzzUG2&TA&hXnCY<N16qo!GKt-%8Aplw=A$bab;@(D8csjvh=?AelX88CNfj$oV^a zFh@gLHoP#oJ;EQu8v`>JGN;GucB-(k$8%<?9(!kx913`x9Y!9jC7<$IQV?P2%<1Q& zvt4seEjIx%1oA(@wj#nd?CAU@l$D?}9AFurH{(_<?bE3fiY1&U=6lIW41b)jlWT;( z!-HVBFuEcye*y=S(W5^iyLZaW*ak@5On0%sy7>lnahBbr$Sn4JMy~pVoVF&9;-Q2X zwItmn&?=hg7=*$WY(OX>?2PXR?w7M2WR%6;nW_6((nNu+RVIc+o`zL<k6CA#UtvTe z`?U-pz$nsD{sO<TQ5&9+91eIE@BPBv*Lqn=LwVv}4o~Efh*HoKo6r_;J%KlwW^#h} ziGJAR?MsrXag;d(c#RUL!v6x#0L}DHi!Em2X>52O{;$$9=ECJ2lXKuBN%LIh#=ZNI z#h}zVz_ZT5jakWRFemRw)``v`ZX1l0lBtcu_tV(EZj2k=wm!*gycx)whuxd+$(wn` z@1wROouhazi?_yIZh>P$01Cmx$9T$6)5(?Yx?-3!T{)c<0-Cr4aIfdqsq4*ZYtz_~ z;J+xj&Wip|B{9&LplT>OqHn;=U0kIbSScjXbYC`=8AlP>OwOhs(lS*nly^k@?5?hc zOgBVf(QPbe)|F0yVI@RWk@DHvArC<7Q+0-wVsCn=T?=9rSg?A5^RS|ZXc`J-n)|ZX zp(t@F7*-y5-ifkDvnKc8wS#0#el=q~Fc0}=1u+U(9~rC-aILJ#eo+O~QCbBKN;aQg z;b{F6X{1_V{<y8ww=qiA6oNJF*r~%z@Ic#@FD6VPPz(~bk3_R!U=-CKroXzH+_Cb6 zTK;%r0Gh+zJHl!OKOw9I`~t`Z)0K3o<SG6Al8IZxY)wk1bV#uPB^B|dJVr>^eO%rq z3Bz})d)zV?3fa&=5!dIZjMD8?m!430kc>t5`g@jVl<+kZ6w3<F@E|nxhj{h_JP37t zfoCMj(7iwio7Gx9yu*7%9z+>&>Q4CEy!Y36_#6)c2|taq{><R`_vhH@ooys{EN>ye z7-<(E?rcU&BPy8|`Bq-iT?2W-=UAMRl<c_3kT65h84k%6oM1~S(DhM@{t(bbd?%gH zQh4X?f%u1=0x7CP%*QVo2=t>+5BTJ0_6VMlGi<s$gM{LHhu4XDl&v_T=kbX+P@GiB z-j2M#0dxP_>kO{(xq)26cR$n1Krns_JBVkImzYE!L{4%JpSa6ZD00=y9;Ns^hdn?? zuvcde8p3<X1uwGJt@l1i-8umQ8ZyFCcIDD4RH!0q_qb>km)K+)>907hQGb&A48f^T zlI&ZJtA~fy#_$X%P}CpQlkoRhk{e|Y?EWEsxXUpX#Q=&CHkhV51m4+w${VG<^XUeR z0;wCY6S!b^g%ciOqI+I(3-{JE`2e^jGaO|NmCvk%AL0#&>_vRVx%&;n580amK@921 z09isX%TTndwVU_Zeoh+|o9RsVux<Cia}ICueChx`OfMZ#`Vjtu>@pBQ(OQb2Ca;v< zF+wRgJQGc4kysjhANiFofKQLC1LJ>~8JNJ!k=hxlrDghA(l|c48LDRYVMZiwG`0^P z;j{QAmpzEjED}lYLpkKihsf^%1|!`YYQ6)nVs1Nikp<z89UB>(W7b~jo97fJE>F@f zD-J?1I>Qt$%q~uZmI$48L&M4B@EqjO5dB4Pp3^N(Bjmo`NNb1v#CVzTKf#ldYtG7u z^SA+IuJC@WhEj(9I%i9jnC>%Xrr0^3zQI~cv*y`9X|sgby}_n<Td@vr){n)_g6Qh3 z6@&VR5We4FuY^E~W#-p;`L}ow!B%WJV!Uax<~h%3^E-TqqpIc_woEeVkpsdj5*s5( z#X`noarry0Q#0-vdRt+2Vu$%vo^fJiM(i*Msbx$40!Ls5C#mrR={XQmT0V07FJ!tC z-ZZUTa@pCbQ%vFWS$v{z<G^hiWt(h4A#5g(-07fkCjruFJ(GQh0o)&@cUN&Y*UN<$ zI@l3;`z=0mKixv&vF$57#|Qf!OP=*?_{Cnfg%o4qI<lk_K`1gC1iOJO>EFl!amGGK zwH9-!PGPeUelrcDOd8qRHzqrUfL4_$rq!Zw`Gr&~^LeQ5y|MB%pm`q<aZE(!G}2XN zA%6co+}$rZ*vF2h@YH_R75+Z%=H0W%#=bF&EbiEh5yJKuP<5<&9MXWp))Qiz*b@RU z1VJB&*|Cj89AxNVYEr1vBn^SU;rgQW4S+}oCk%+1kZ>Z%JMgo0>lsVpL}(W&On8PX z0!t@CDb%kS{*~r!yTXCB7LXI9t`Kj2_%gg`Y{ISty2QfEQ<fn>cWCs6%Tp`Tz0bXl zCvcin`x?QAP-t~@>gKv<N09nz#_n?oacaWL`?!?t7cRLVMai}&vW>y?etLa?Z-|2W zEr;dl?hBSpY#~5Jtj|Mf&`S4<=c_IDhh{(Va;bwJBXsnXjuPprT^2;owNxui?;yoQ zQ%26%;%T#%+$C12D8Z;TfbcZycK`Ny-KPu{TWK}P1YqAbiCT)qi!HcQsYAZ3!Ptc` zt}U2B6Z!(DpV+&cD_R;KrLF>T24}2onWR~^J5Kk)OU_y@qI7I26K%nC-?XBGAzCjj z9Z~^Qh?_{OAE1Zf|IEXF9)vCq48nW-0n-H$6RvZ}Jj`IkvN$no_Q@h2h<NxZJ~hdm z6KhQvKFaNNNrD<K^GA^{@*;co3-OG_l(v76`XS#25pKa3i4&@vOC%;oF=IvsNQ{Bz z7YS1&u&DxRCi8@jW;5wr<`_1wehI=9gwb>+cOW||W{DXHvJ_flAv7+^$y8aC#*dKJ zw{U{|j-{Bqf+Rr@rJN$!_+ULaoRTw;R+^KUJ+FuD9=PGUGbbkykFy|%N5isBduxqj zJ+tL43}ebF4EOvoJUa}l{yUBUOB_vM1C<4XSN?WA!<hThm@a?_)BJfB9b10Lh9o(u zHRk?%ydet-B2CCaIM*xq;lE}5DgfrPCo<iGLs|ft8fjS5mzMTFI16jQN`8`u-{s-I z=Yggr&6WBOdG?1qkXwfTm<QG#{%1V6W`VE9$x)xdPk#{;85MDYe6g8aW|Hd~ck>hZ z0@9(*<=@2FX#RPKt|w)osD=mKUjb)t;S>Ee90pUL1`7ewd?HEd2<@^+U^*HsO3Ksp z+E}n8Nl(*j`MODadOsZ}KCO?}VQ5*KGzn0rOaj#DI=EqQD46>gfVH+?lA$6`ucSgv ztsO*q)F)BPtUUEp@U%R2D0n6);_h7VY%q`S!@+aGaeN;Mo)2EY_fc64I1&CMbfEeN zm?x|xArm#}lG_k=7X;C42aE9Gnydm?Cv`aaI3iyWr;gZxi8d8*Jd<dRdcPNOq99=1 z6m<=KFmqd{Xm<>m_jUAn>>OfZw~}k`TIQmCxDnvVxOt27Tku3t%>)7F^KjKHnm^;T zx#F^%?Cq9t-YWbb!cDC08!MEoii6W~#H6rCjv6YYX-2)vJll%AO_hKm0%JiC76A#v z__)KgWpc_mzk#@4=beE@7snmb(Vzs=RO>lqy)uIV#}2MLn=b6_Ja}O;x8a_JcF%v4 zE;KtKvIk=NAN-!EJ4{jq4o2@*Q2t$=O<4hdct54=IQ+l^D=k(F8t8{k9U28joOEi5 z#{@8H@{`%<0vPn_&|&?qxV9M>w&4s?uuW$(*mYX!VpE!x3{)YE;cmCRLvMCUGYVEf znR%*O4CbuQ(?!On67WjX?)ok~)@0`}w~7eh_6~#9sH8f#CzVuRtlexhHMN1kZW}p0 zFoX(A@R|e^Ba%@7A5sNo+Lmsc;=2?(0^vcb#UQ(@HKkj17=j7WZK`1`oNmBlpb;dB zp=)?M6`nEODxeL3ypefX((u>)IahNhQ(^{<+l>IOp;EYOS(2P7jJZ&F<5b4r5WA=d z$6gGz*`iO1!nDW?>I|61UD^o&I3aw%s`N>Q=CD2p1Cp~y<$4032?E!keoH3pawcs~ zb{nU)39mWO8YjU4_(zSAvVw><6#Qg2L~L#kp$0+>mO?V&lQ0sc%DL7$bVC}GkXSk7 z@&a^1N#vY^o`@?xBk$!pWA`)BhaD`H1T@6tsO^1}ZoQztZWK`NL}!u-nxl{8DQb~e zJkjT9wkc0ZozpblAVD>L$5IGthyDjqs*F9ka6H&Uz2(^+3_ImRJ(zaNbG?c1H+mB| zJKV$KOZf;F!k0#>6X>oCJ2`;DCBq)6?QxO&lw|HKUV8WH#mk>wDSr0i7d#D#5aT93 zro)z((c6rvHH^^!wA<1b&>mOkkOF-4FwVhyNhrW!P``9VDS8;xf@wX5Fv5+th|ZXn zd9&}%@r&J-_%hH4C`@J>799?8&1216HnAJgMmHPVh~IHNlF|k|%q*HyD=|Z>D?yYE zpv2I(EV;u7vXoN3r^pYVFE$EwLb5;!@O6{CjisSaB%M5w4#ImG1d6S%RJ1G|+&!9} zU764~6a88Qt#f-9u_!YxvT_Bz8(I_`eoZFkO2s$)id2*+%(=aSE@)H8?xls!>re_7 zlHlq>CrBkdK;(!Ia}j*K^je}qJ{R*C$y)#oQpQqe14WbNSno_F#OUUpQoKFK9n`%8 zSq!l;)r#g+f}^RF4B1QQnZ|rj{Mb{Pv1)idGSJB0@9{wdtdahnvi_yobPzEaIYMT` zPsWJw@)+VpGG!NS==k0`tq<IgdJ#T{B@@KAA_Z0P-4zquSUfFVJ1h8}=$nc4^rCSw zz;R=+6sXCE)DezCpQZ~vf9~4(a~Ccu59Ors?*ewMU%e=<h*vLOTfKVu{OYjA2e1e+ zK=j>YOn%91x)wLyl=m-D<AGcCP_i$1&F9+bK;seMcJb;}Is%g#AVxYd=-#hFfFCsp zu`S*JoiQIZk=x+mgLrx6@+$INUircw9|HDN?o*6q0CJIDSsDeQR8pZ`9kD<+EPdNc zT}YjVk`ZSJVy;^f){~0#uo^bo%%zhI*tGU9OEvcb9jFnuA;n_!vgek`>SHRAm?1|o zVxH-<JP3P~u1v65LRwaj?L=Xq9hR*IFLG1TZ7^E9G!EZeNsYzE_(9e$7P-Z%I+G_# zb7okGwz3zucok#Bkh5f1YfG;0Nrz-oqje**A*UEJX9+rbV@H&JL;%lsP<NseEL!D0 zuXXMT1LZIQ*KR%v!rwW!dj2yB=!V9%H-0_NHt=}#?b_vc-n()UiGI#qB?7sovEg*J zzJ!zq-cUKb{Fft{16#S5+YaoHW!n7Vi4!Kuro4FvL3VK1H=zZ4Tt-wOHBTpo#QIh! zJ2sxrMwbAfZ2>b+oA<~t&z4Wa)*@%J4d&_q|F(u0t&q{&fCXsXVB4(#sqJWDppX_K zItdxI=!TPM&co5yAPzCkX1Fmi5A_?|++m<F7=~h13QH$QN=@+_DTW2<?BI3U(0Xpy zn-!5jU6!ImyKHsnf$O3NMZ>3UVCOe%oGyDQ5pj$oO1a#9<C+Mlc9U8$r=M5-Kz7-3 z^)W^V?WYQR14730VfRxp)c`e?6wZOQ)l&}9_d)WEmy^7>j5n8#!rcS(nUf#8vx|QR z_Inb^*ZLFxS5VHv|APlH$5LriH<KdH`g!^oLeQw|?(bY`?T@Oy-ye4bLe1<n@iGcP zg@}G3=E(hV#}>s&p+6!7L_Ep+hs!%H$UqH9Wbh%ebGCVBX-Zv5zJX%H|AmJ?;z5=d zSXKCK9{z70{uvMA{qX<r>{T34y@CT!CpO#09_SOWBuAgZgQ<7uOhR{)Y4}NtQwc2h z8L{wZ(v$g7Sn>z|!S<i-9*igF5#Hidim7+PMItpv&yUPXcRGH15%Yz0ugS95(sj~c zUp|Gm23t-$^_SW53{FymZ5VKL%;J#=e9q$&UBUrNZd`Ls2kEb-v1|Z02Yf`6#y53v zuA#bB*C6u^>fZ(Kc`Z&66WpW+t<P5fYpjC47q)|)8jN5-cF$>H2!30Y)<Y*y71=p( z=+k0k6!1oNRwfT{h)PIDl(l<H(a72&h@;{WVb;4u)sgU9UG_A_Pw^s$=bI~4+7FQ) z8>C`+O{^n>HFz2HRXvWSYqA{oxYJ_1z8Ye#`iyRxqPu5;)VLR8XsBS7F`?x+iAyvT zleZIod$J<Q+Eoq{(e5O&5CD~sJ1QQuqP6;}*k=SmPjU0w3b1<ufU=Z-TwGh46r+F0 z@QBBKC;aEALcJHV3gxVGsN)jXU_K(ea{()bjpK-){y#*8WV-v}(Sx?Xy;~#X^AB%| z6-qGRV_aZ?4ck&}q(02jg9{#78SFGa9zNN_rfdN=VuM9!WIan9g&)G2c@pkAcrqPc zktZ_t3D}@`j;*{9)A0hH1zw@7uGpsF1SOb-o#*ul{62z3I^s{R`}-znqH2s6MMnPz z#RW5ymSqd;TQFu5FA>2osPQH-_6kzqCpY!>Y%ul!g@aRCGxp2%%q8UhandNu4()zd z5b4oYV^mdJu2_kjdyChUm2Up|s3iZpBl5L0int@~C!E@lGcpSQPfSc$<Ut%0Wrg^k z;L<v~5jy(n@-9wrYh!LZ$PSw-u|_-@q?QEApCR;c4KbCc!#H<y<XSD^h_ONiQ2JRo zlX(#2li?alkUxHt<_aWn!H%#Iq>m~!mpDM+P8yFrfO2zf@2|2gLb)^DC;S=(#o=9S z`sHdRGbSuct#9E(YNh));f)Z-_@iU=Ce-T<1m?8pvYiElgKEQ#Ee9xt2a8!ChQsQJ zH{j2rOlfua6wctIAWTQlF^j&0@c16&K?q({`2n_)zs?Sc$(L?KSomb<RriqJHTSGr z_2vM$p`^?KKM-L|@G=Jikv(=l+!Iv<MGa|vS~103CB%wF#t}%fzV#?f!CsyH*@T2@ zWg&_ha4nHRDB^q&ptxZCn{E<wiJrPvM8kW^VTyZ-UZ%T$-}I6Q>TufdGzhjr?}zr< zu`XAs>v~*JJPSU9)G8xb@{s6VjCf#^u5#}k$rLHNy{jZ{kZXW@Q>Vj2eT?HMq301} z+MlG<W2)%<9XGh+37qtwaoB-%<`_F}b9hC<A|2MvHL;mtK72vE(Y*BntTy2HAh($b z-@=4ot>GR#e6b-WH&o;m!`<Yrd2(bk7v2ogkc?Pvt_4m32TtNucAY_Rqutjb5ySRs z$+;n_RshFT(kcN9iLAl3xN#0%fB{_s{#LptFRK7NuqZ?8ip}H!dj^8uAlgSE9rBdm z<c00_M<T3sb>isMki@f}h51`!T7Mju#xJ{{-U?&H7Cd(@4JsRxg!MTKLNjx8yQXIg z(HlGH!qpm^z0zY4QEV}=l#&$P&CnQXCJht%^|LtP3<=HBKog87)^A9!K(XXLwt_@l z8{|w0p~2mI>AQ@i#5xXqs-M1>p&wy~+n%09xD94%Gas#W#y(2Fm%4Qpe%0CNCa>^D z-tl-X`D>Wf@r?p%84t}lsN)3Y5o=NKVoYNN2)v*Cd8K<rmLua1+7$?w*ku>VopsL> zvyNibvcVPGMyJ$0KTsOjrK(U?bOQ=f-esYK?;FjobP1ByKg9^@G>d0d5tsc*GH^5e zk*a!hN9j+W1Dg&BG#8P9D^wz1WdSnlLhi?e()A~_F+!=;b2P~S`g!>Y^nQRIB8VIE zGB+P%0}MO=BLdGnD$)Q{>;Vh_!iLJDWVX`;q3mP^;k@wi?>-%maA`<UHNp=62uCPZ z;plJTL^T=Uc_zXpu$b+1IRpH~H*^|cee^;ni;YB6^wJl&qPMBYdmG|3g;l|^01oTG z=F^0g@OCeIZ{+Sv4B-Nt9RNIx!m&FG8u7*6=)K&nFT(Hi0H5I#(34Oq-^SLv<iNk& zDQrwYI-~<QpbXE91D3Epsb}}m7W_7)0ba)UzrRnZ9@)N(Z?_Tq0(wq}dzaASLDK;F zL}byj7iA<%DF`k$VkJc+2$={!TAB_;?f(G`WjI4BJ;{T>7pG3cNuK4IXw{-~b29pe zSK3!;XqS!clvOEw3C%emkucg~1C#Lo5_USp3W_X|GDUyP{H#uqQfHD7^^caVHJh+? zeU~q&%z6yq5G|sp6o^s*5g-T_9~nh717k;U2lpu43E@J>I245A@sO1sSgd2)l(_6C zx_}GJoj_JNi_kXF<I8D;2MAT_jDQv`$DswHDN~a+vA9?*2Bd+iWLF$n!~lo{bP(U7 z*$k^dUV^xm)oI}xI!3J#F<}E_Ogb&%i`-SQlJvTtXCx8B?k{kR1br|b-534-%Oeo# z8Q3posMu}Kz773PV1bo}hd6WM&R*mtcLEQ^OHMeu(cq*Vb9|E1XdA{hg$Ho=O*Tga zMWB5lH=RL9Pp13SL!0G>i+tW;|Igw=vEO(R^e}k2`_$+}-lep=F|s)pzSTj{rC43y zZB9;2Y`mZOD1GZ^1V{C$5AjridY$?R-1-?@PpAfmr{Il_HwBNuC4j}P;~j=~KzhKe z|2WS!rXewa4rm4dJqw`eEsa)x4juO-JcGziVp8m>z_4fWNfq@+iLddh4^|X`-AMxB z)}xVA0}^s0LQYAVeqmf>T3zr1C%b{`h|jIR7#y3XAGQOenKxO;PgXDdU`PDJxhYe- z#{Cq))R4;;WQtzV$O-IPK#L89-xv-oqorMgznIM{b*!j#8kYvM$xSlD6Fpa0{vIaE zk|I#QqP5SMYCLhZ+w(g$$b}VAl-$;w$2L6n&`Rnd`ir>tXamtwyRZbYaNTpbqT(#r zXUVd}gTApU#XK+JJeX8?ICE#Y!FEuHG@^s;%KwLKL(If02%HgWK#DJ}+Zr~wkH}x; zJz*4cKBJJ%DW4F!@+=<{8D>A<687Tk&^ClnIL0)87cKN4&yqYssNZKDH`oV-9^?h$ zDr62u3yR<hUXaPohyaV<5b)C7AA_554soQPdMQ65uv*eDBIDDf(GWMs!>2_=7133l zn)nOD09{nJF{ZjGXPA3m5Ti_7G5j+n0jBYGFmw7WxaEM5x5J_ODIWe12WAc;g(t9B zMO8@ez`%SH&)#t~7p##MYudTHb8#C=QkGWjMu5bY6DT8a5-04PkXH?QBm)#f8Ke8~ zn+V~qr{Gt)P#|HJ(?$8BKU=oeee97C*~WWuqT#?y5A@}KKp|gdU&sSe=mUE3<X|s+ z&tBITzCcDUefbnl2Ktii!8iwzj(Rx)G>@hbHUFADr*YixS62J=KcSfLhj6ggsVZ-< z2mNuG7KUUh#Qp()Np_}x2twZ$HnY_@2N<el^N5exJ?eGrULH`~^++Nm<Grsq8z*0L zR{kQ|!A^=)dL4donGU_`u6MFG;QudCt@Mf8W9!GgLXiFjZT}Or_d{xtawyMY7#ROV zAfHmH22$-b!y7p~;(IsKK`a&O`F%WfP=H0PtF)$4w}KSDF}raH!~1vjtd~0qt$UYY zk#q3HgB_LN1Q~k{v0aBZjv%`0cQm@oL~`-{qrK6isf}Y=Z|k+r6L@=uUV}(S0I$4_ znT;pGi`j>#G}=ovf*Jpgk;H&zf5gcV!ZU0wNtThgD7N&p#!?H#6Vd0d^2Nt^`=8@E z$oMA5i&+ko+)Ww*Avpa&8u~tK5gZ~ihC(I<^uEPQM=M3Rg0v<gwh)3Me;0&0Mhq0v zDS$YPejdRiKSbDj4R58gvrzP@@blj!6Dc9uT9Yd=@*2S|_!aTiAScx27=@j%fO({; zuySdIe>gJlN(NuY9ubkjr7exQkhO$My!>lCjG&Qg`;=#v8JQ-gUWwfpV;4!W!q4zP z+l5DcD0r%XOmY~Kj)YI}9=q)!rl6>Zs{aU&{2t1aK%e|`4CFv#@UL(ypDjFHI69d_ zKt~3l923*e<e$aAndy`He10UKmt~L|-gkebJa6F>k&4D)9Soja%LdsX_b~|VT5fHm zK8g?zTp=7|EEt98I0m>`X?M=4)Pp>4p_7Ht@|dK};i5xnlsX9rvC6Ol<6t?j3(J8- zVO+s&^k2}xQZjaNqBQ2*DdU{ij8qet6aIV;Rmj8*H)~_T8-|N=)o2oB7W2ylD5ce8 zz}@+QzLfU9lIR}0C={EN4IKmb!I%Jw(&5wiZFob%`*`@Tux5Oxx<>}<Jl)i63V%v9 zeaqIg0^~b~8pF4FV6&x>A}L8A+Im@>+C#!Kmfj`2%{agh1;MA##s9#LAP{k6G9B_> z4ez_ZEY9A-Ct~<q3|auI%vvtUB33AiIH4TCX`hK6%A4q+@nFV84<QjtFbPncs80q{ z!L)$iQ~*6@X@7s_f;y^xT6gkjhuwv@>0GMue(P{gG6J#P4C{n(LM$PmNMFS&N~?|p zn(gq?ietzbG?yQaQzWY>!fC9kn-2dDez0;4&%GfM1PyTS*El*X1?%5O2OAo%#LE+S zC{gD;ZaASW9m4WR0E6uaE8TLIwQb2M<&Z`QhxqmXx4m}(jx)RNJAv*7&;UV@!{IzM z8d)u9MnjH=(2Qj3!FhNnkd$c6U`8B_WFeA)Cc4Q1NCKR%!5I<(tw@|?sMvB8$9BA4 zC2<nlaZ*VfKN81@?UbBXt&>V6>#R46-P+AoQnlICdaF{kyLqfryTAWA_ulV*-3@X^ zkrTTTu=-;^zVANHJ@?%6K0F$SwinCwcQrJjt@2n9CRp2kaBzTP;QE*@t*2h*Ew7`- zV)UBDK}ou<%a-0DhWZ&vE%tLCX%ff*Lus#WtUnb)>AuLX!$RqHC&B~&AR^g_(fjTD z2SyrhOj<Zm1-)$b%G79*k#9<GYFl&`T#a_5ALHiTeI6##F4~vLRKdjgzi(MdCK6{s zG6!5-Noyq$^4GJKeq>;H!=I^a@HASjyI`r{%A4#G*m(E|Pi}{0Ox7`8N9)_)mI>@( zASRgg3u30x?KrB>8LEi${Xx2MylLs3)?g2ErJsHyt$^f{+V(vIQzU_7N70NxcBY@q zoKz2|xa{sDs5RCY+`Y>_<TVp~Kpov~=iqx;q2hhI*cq#lWb08LhVS8)zpf!3<_?c) zG|PK!s@|NK8f(@I-LcA#=}++1U++s?a=K*tG4cew(OhbD(Vuo2oiRV~>=VhQ>{HV| zH9M2`X;PnfO?9TQ*BG@@bpO&mr&mNa9_So|g6;3lbocZ9fe`6<@J$`8cd?##7r)Lx z+W&%ao@kN#2Af(~vyeShI*CBbf-b-gO&9S?6QM^Pf)5Iq#)X?_@N_lAh*cWhAq2|U z_Kdbx6eqOVr}7EQY_`j&B<NIw$xK5c2V^V#EcM08N*KA)ny;)xKl7E9<US_H6??9) zJKf=d|9IDBY5JYJh=Z{wRg48TAvg(Elh}H4?Jy(6uNY0_?#1T`L!w+JaTr-u5AWf~ zBj-0aseEyU?2YI&9*axx^tV4AA|}s}p8D0RYlw+5jcskdg*GaKKSE11<x{8$0r{(k zmt-T;;%=F&a<jHxJC7#t`z5<zKe7}r?^7tXtcG72-I^fh1n&yv@tib^A0AW4Lud&_ zA=>L8`Rsg*%VD`Uy04c8)XKPIceO#a_zP}8sD6&8B}2UxA_Re)>ANcEnFXULNiZuT zqiyitC?Q_<(R!5eIG!sDSwhlK63`+I$|4hG7!kvB)w=3dg;fe^$IM;PN>Zv6p(^)C zy_}8ALs2W^HV-BZ7Y9KQ+jLqM{)n?^{-{Q)^v@;p8KOPVOlqdCO7lwDUZyxfQvj_- z37rasJVEkB_Di+hexE80F1fdxc~*NYRB;8jz3>!3!6)%DNLjjj(J{XV<Ge-OupR*2 z%K({1bk(%#0|6ab{a82m8FW+?Jp_x=wEn;d%A(kzj#a-4x7C&YHVjTWMtTQuM06;K z!yn@MD4V;l_-TMeq)};IVyP@~a$1<Ok(Qw|m2Z{$B34Z=drzRr`gM3GT(ekAPck7A zAxR@|e!E+23W8eSlE9=Lp{P(TA3q-}9glER&f72t!Qc-o(8OX%acd;ZIAvgjlVHDE zC8WwaI(mgz9t*9}J@`z~Adm@ZPC#F&Y#9@(Y;dN`$O6n$PjnEN7RdUB5MOw&as>K+ zkEauQF^P`!x~Qm?;WiLit?c%<RVa#;vJ0+}ZNiCk&R1)u*zR07y05UL)H?nOJNiw5 z@f-T6L)txjB{}al<I16^0~ybCmtQa1S4hF(2i$I*Wr$4Ww{nmgQwdjNl3~d$sFVL& z+=8VTdjPYW=^U~4pmVIup=8l%&HnVi$o4!hvl2<Dh5Wj^Da=pZh5V!HUSk-}R2d@_ z$9*4cc=WXj`F6umH>V6DVRjX6v0fd5_(N&WoU^UF(%T?#fE)K1g4i$Ul02h>%IBTg zKtd+0d?1y3E5i{~3?ldQhQW+Xk8H|SVl@0;n+|euy?9K3Ii|}adqZbZoFNl$7w^`P zOhT7G$7oEYmJQ4A+rCd}>Mtn}@A{mo{_zriRXv;%*jizn#r>ufG|e!kIUA=OAA@4z zv=sartD&R;)Qp?W1#~-$G#{iajG3Xkj6IbUh!_n{S}GUr?mV1kQy(YS_4Ygaur0qg zNuol1*&{Xho>Xzy4+-gRnS}5aa{Zx;d<b1+4Nc^STocLfb>m~Dx@nRC63igSRIP*J zxHFDK@uGa~^y%j4m9w1*_r2?oU+U)QPW^tf5_Tr=DENJ;rDoX@o&=Np_I<ZlRO3eA zdGTP7I%wba&wPe0nRrgo6Ji`Xn;VbdFS2UYGR4IjiOXn*v9cVckT1|3lHP6il&|#i z6`YCAt>H675mOaijfZ#K6{S_pI)Z&vPthyY+OuB8wAkr4q;DMBCpI>bOD>%sz-qI5 z3;zJ3<FKtTkJMMUuCcRr_{|sHr2d<H{IFJ;|3~^L;@mKzRa&&8-sgYULNA!x)LS1; z@j>5BLx%Ho9FDnTZJ(^*Ts(%;xvS#nz1E$MKD5$Ckq?_`K2Rv3x<zqF)w8?5LYs?8 zZKFibqv1@$-JJ-}Elq~i?RJ*-l?Sei;1SdY<wt4kn5HFqGcutFh#4}(J&4Me{QGjJ zz?8NP>np#sNGpaxe*nPqwAVYtMO3W7&=5p!t$lrA>A<QrNy103r?oT_z6c{{lv2a& zL2>sjzqUqN3-b&<ok#)NmqslnT2F&5@2rfma`t$j#(5k97#(WVkSjj4al~RZZgU7( zDxv2!4iiL%9DW|9arDqof!@w)VS#J3OTs?K>g-OZY&w+CD4x*jYt@R6=wj0RDLrbc z^snokozJ=eJUg&$rNtHeFFLEGKyDt!ZFhF2!N2jw=;4XxJ5XoWOmJT1=k%vTPk+6y zami)-q;q_iQAf%0lS|`W<fu+5$?KC+KSK+bruYqM9+^Js9jEO{N%PI6{XvSK3Hgc+ zbn|RQ-(vZSM#)$7Zp&CST4pRVssA1ptyTPEhS>fGZ27ZMzLKij{hw0ccT|}KiYcx$ zv2(GSMhsmk1vO{p)RZx8P`)yCMu;gX89AqpgFV97oq=?#VsMfGW9lDTL>5yT2~XtV zBKd5T2xdLNE-S>927PMd0x5(%loSp-eLr9FrjG@wCyJX`rwDp2)>P<db_4F$^&+H* zAxBzr7Lk}B7B=`Tr1^+zEv)>5+H3Q^M?m21;i>b;<JQdr(Vr1$P>#x#0V)XX#_v|l z=`<xSIsQFT-hjd(T30JffAG^h^2k~z*Ga?$Kaw>erP?D$t51jV$;fNPk_NIreMV_J z@@QdY8Hz<wBOs|Q^;(8)<{p7s$K0_!T|xjgviz1r5u~vzS2l4OhUX^_wn}P93l;i# z>C~rQKD97^s+3ntc^5%9_8tThBrI8%r6;hqE9^w*C+o%ltkqBW6k(Oum1UA^7pMT9 zGD{4PPEKuj4$ah3slrHioCGoZewTZj&wi0qGk1v^@u&aU>dwmjDsM_f&(1-Gy1Alf zXa7ws`pa_L9$6l<1TZp8<DO>;V49WyX3`SCOj!b$eU|!V+S0!4x3n)aJCj=naQEEk z)Eb;+%Li{vZals-W9sA|*qJnU^B>rmSwC3VHQh6fqG@vHfc1ZHhupx+cac4RbZ64O zxqD}(_!UmZ!#gwPZ{TXCKYt(Ge<B50ZmI;6K_8}|ViIxkI5Q(tL0=R>S4!?NFb!PW z!fli#Mj*vyn7xeNxqB^&)9Fcx<}9?SkwEKm>zO9Q@+GL1eyoJ!yev|YZG<3EM@mw* z6oh5(B8+_YrNt9x7h9h>v-k<>&beY09LD=?B`@??HsbfFzfE#&SU|1I`lx$Jsg3u2 zLVbk~D_4NdhdBGPp)`yq2B4P8TRG&?$Ea9V9wq=9@YS>$Im99iT}AfxKv#}WT|^j0 zJwqC^ew;nCz-l~yYGJYUvCn3}%VAe-?i+*MM%hRS36^gVZkN$Lsl>H|jZbTruU`Hg z2DInTEPVRJb1$FzZ3I{euLuzBwkM3IIQD_>{?G@HJz;TwZIvIiS{B+tN(K==M$F0! z2Xg)@fSfp~N|nbw@(p}ABf7@2pj^6^uTA^f@5Hq^@m>EB*Cqf&YZDel%kZCKZ8jif zap&M4`a5&uEWFVA&voyxK56%8eSSx79<9%Rru%30`syVP9aDt43>PV=^jT7?V1rPy z0khlK;T;#Wq}6cEaTl87?zt|^K7PDtNG$*pS*;}4+&qL{myK1ZbsuZ>*4Towt-V>R z$QI1be|G-4z7tIx!lgB(Bg<KnFdw(;noPKH_3}BK(Mwyk8DNks)Ib7=fD^VrCNuTT z62`+;;_1^!zundm<6zv4f~4i-c90H|^`Rmn(kQ1<IY%$Kt_RY}DcB)`U#{`hT^Gzm zmPWRnaVVq-+bl9Y>03S@V+w~du~(M;F3NUsa3;<vB^LZ=ISDh}h1?5nRYl78NZ+9C z3I?#U$ggKJ4-$LXQkKBMQ8tqiVr+o97?_$NCZd%qfPoMhp=`^zZdA$~b1oDRlS0la zlS}8kqdbN<2o9^l`{S+6m@6)rj-8o%vK6p~HqT#W4Z8ll^3-VrW#<uK!Q876f&{Bq z;nCSu0$As@HWDv@ISccHeavPse`5X<r(zzYvNg*Z*4x_Lv<>+}(PrBlSM?%{|AnH9 z;^a<C|HRxA9~j_`HVb;rCL61A8N>9XiSCwb&8^Bd#BxwCoIQE!Y#ggHJZcuzG0O68 zqDWNDB(UUr6CAP7Kyi3jQE3K&8(Ap02VjEi_IlKO)dmWNO^6USMez&0t+my1O!l-o zyV}z=K<ey?`ELw70SS&1faB%L3@}P+42g`nD$^v9w@kZ*Ejtq<lkERe5Sx<v&GKfe zoV}3-=`=#|W5AHeQZ_f-SlX*3m(cyCtt3LEjRjTta9_vv4xnNK(THERgO;a76>h3K zkWmG@EJ<G7227S|0=ioOTR%dwuA^<D4#s1xSvSjguHdpIO|+z2ir9pXl_(j9Yw!wN z^rcg0NpBXhrfkZaHi0#O$s~oVe3KVhpB)fcU%g6Z^4l+vEc2u>7HyWFQ3>U;d+3}B zx~ehZ4cpu3Nk$2o9nEZ>KM%Z29d@aE-m-E*qRVyyxl;oxNsLE>bQ$}5Y}|>UrzoLS z+%424EO^-r=Zmc19NQo?tBs{a;yvUd-4n=_O+RGVIL=x~!P-q-K_EjI9-bx)U%$mv zu`5}6KB-i)9TM7(s>X~!BxEj$vHSf7I(J9ZkB4lZ>mfkRoVe0XNwa0-P;#)X?>Q74 zLxyw#pE&K2E|U%(TxCnE?JI4w6Jl-1+`8~IS>%D#S{j4<m#l5Dx7ZwNG(Sxu9O8je z_LG-i4^~DNl%1(2f+jTe7MlS*O{tSN)24RLDtv&RW3!W=$|4a#v7nO*sT)Z-XvZ@Q zN4A!6UM%J-oPKdybH3qlqk}m--v6VLMy`YkD;ai^iZI`d<`AM$bxlX_X|3W62d9?L z$&u#H!;NqW!zHKYrRtE=4H~ljoOh#nZL)z3>nufW6=c3AK&v#`2%nXV+iSIOSi~X6 ziNfHm!b`cY4B+NQ#YreV=$i4?`^#S4{J6prx3|cnKrMx?F{eg{^1~!^&WSV6c^E@s z*TbLdlF4Ch6TU+Jk9O2p-0E$oQNpeyk^|GSEr3R?*8`u@D6ICi9SF%%lw;v=%{hmB z$ZFUNRG<2Q9zC|wI_4FdI%~bvt5il60k#z=2dSs6H|Ol)+Rc@13>!<JZmj*Xn`V#W z#TgZ*HiNWMu|?aKpg_68<sdx${lHq{k@$+m)N-!po@xC~c-i){0~9jnhGOu*LhrC{ zwE?+^Hw!6@4Nh~((TW3F3`sd8M0I0SOj)dFU#AEWw1ZOxS5!y()K6etOEXKhY{5vq zKounOypjpBFl+8TdZ$P;617Obu4IUi6uA}dHly^!yC6I5PL--2dmq0aGr?(h%-HVT zG2`Dzw-^|&;#2Cz5_h!q?p^AzAv+cBG733}D@|z}ix$&}iYH|_eoB|8b$LdY@6hF0 zT~6p?O@2)8=5;x#%ZK%BTbET`f^~j~N0@>kEdGgVIJH9rk@aU%)Mth2-b=y;{i>kd z?Ykl}4R;k|1nw9m&xa1q1eobh2M-dTTQYllP8(U#_f0YqGs)Ms52yC`as_+aUC5#7 zK-R?rl`bkyUfb{L^Qwxr*4~`nB^M<PRx_OEWfl8~OvUOX4=}7+vJ%y{e?EjOWM{kN zW<}gyB{^1XIhMsuc<<(rARtQ`u_H6L1}7X%jiiTfa}=0Akwi8m`KkwQb*p@lIgy2H zsvN8bOL*?B-G&BUIxqMtejE_roj^RXeRl$J*-8a?bvn_j*W!{H1@Z1Co^{kSUz*9y zosjUKDYx!V@r>7jQnX+m_!C_zO5aD_`=Caq<r<*`ZH8gC_n@up_Eil=fBIjmJI~8k z7QYyRYHeUK0I61-reoV>(fv}M7ez~#J_OegTM0Cz1x=u_?u|GOfW~?^-$Dz*@&G9J zyL`%h_003KO@9`49_-G=sSF<av41cc_Ipcc5F~nUbvTg{ReW%G$l)QM6!Q~O4OMX6 z{RT^@SeIYqrs4iThDVR6dv|bzywtY$rvWIUrm_z5wJ}J%PyMp9u<usFu9Rj3*pQU! zD|PDGirmzxDu~)iR>P`X=c<illABY^;$P5E8y2lq{6*du7<NaBpVqe_cZw!sxAjBv zFY&{irlL@}k-7)w5>a^|O~+Z&Z^ndVZttH?%6=YgjJKK4&oII}PUvU3>BB=NRIZPB z(+XZ_uh{wsFXW;*JlOSXixRLOe%ryWt=7w&_aZlQB^5c+3oO=AvFI*HphRjtqFbC_ zAS-9^kcph=)m{h`<3?voBGr9c5w#XR`fS2qj>B4G`<-dQ2V-ZC9<WI37;a7ZyV?R} z+<DzQ)Wgg?#N;OI+{rLsvybXmky{TKvv6r6M&@E>=ql7Kw45I9`gOfaL@828b2bwr z^eM3c+F;EG6;mCfs>ycng^_#YdUj}%$m)8tF+DLgIW=kiTdUlj{_4DYmY1b59_oCi zfY$>SR%TJu3Mg9&4rsVQn(G;*@d$P0`+c1mcJaaDH)yf_Cz$66G{;tvO-EbXOPV^( zPJ6lHWgiYRp-y6ePj^X)-|c+bPu^tFk6T9!W^@({3Sq}B>Oi5<#<8iC7?VS;567`7 z^m%IT!d&hW<?_%#j8#6GBMOc}0BC*2xa{ciV2JBIlc4;9(#<gYsFn#zvCYDsG)}>Z z4e(LKlXK0^3P@8zjMs`)qf*jB@+iBa#vXPiJdA(1bmRHj!!SsQ=2Pyi__!BOR}Qaq zbbg}|#OJiV)Cv*A_vDZ&`Az$k8}xtbPx!1@u=No<wK<+V=aXW@{E+gvTyev@<6fjR z@(uc~+#k-*{P%p<EjPN7(~uQ5!=kz6c2708g6x%Gpv|DyJTJ@K;0L9y#0r!D&XTfN z_yl~8J~cYz!m}(MWCX$a$(Y9l3k)TViSg|VG**0s%kH>~dY(H{1CM0gCN{d!KacNZ z16@o+9iL(Sv;63lxmeNGG=~aYXX)haM`izAjri|qd?912u}6srIgpKw)8L>$WmNY4 zRWN4}+04cM^nY4+;hBye#bQG^V2f%Vkn-*Pum>CRA%41k8$5F_U>F1?c09q&Sq6cV z`gnvP>`o%CtENG>vpbEkOU~d|B{(|m76ugpztd@TnJrGqpNh_Xr^X^5?B?Aef41Uy z$D0$)38RCn{FMHTkL8o)!5AR7P4m`O1n(r71}t`WY23VTRU><{_$NHs{xHxxq1r{q zN{t@Hbxyqe++sW)jf4cUkjUssHwMm|$mI5`J!q`a7E^Ybg)3_Dr4yPBJw*qeQs|!z zTCGJD0Gbt14&IS>9!Kn;Qxa+^{+&KnLv=kcD~9BI#BTU5I=MqE<6^$uz}+f{tpEQw zfxFddJw5yFkI#PN5xkXI9I>`SW03oZfcjq?ipX8>I2YT7qg-oBzP&|y>+^*3D|yh? z*cRmr2=AQ;Ns1)ZG|kU*(xivFhkv;5{*;GW96(J)mQ;%;Qe;y|WkyGZ_KM4c!e`%b z->SX#JeQc^z`hV&T$)lgPv5GM{V6wKhdX{q8&Dxf%JHOxx+)R9eQyN}<@rM&_NV_f z`FUO@DtdSVw4Tz?F+`zNX@n4k$E3Lm$Hvo&Sg6^oqoT(2KYfH!#N&@?hz5G)Z5bF^ z2wbS0K0TQ9<Rp(53j&YkddCO^f%JxY6IoUSLZdd*G{UjU7wJ!9d6t*)jyWCbHrB^F z<P#tdW8E^GK$&3xMSq&RGcU5LIKc&|Z2!pHphK`H#8?npD&zC<<E_W%K4cqA>{Fsq z)%J}@l@FD6rwV&mSt1nCYH+(w^V_H1V!JM?q?`L1Je{N_p^IKG2ypju@uQ36A)|jB z=0Jh(r8J#zJ09|e(JMTaog~J`YhC}WmQ2Uu$k_IMRVXCXT_B9<gr5JHF2dWwki!Ix zkL#mhNQr*sHhB^J`BMoih(;M7t<?&BTfvHP8hMtN5riBoED~jZ$atf)N>(u&%$Ts8 zHxdXu=gp+mtDf1Nat~{4U|ZYoI^jy>P_$FpEBJLnG4$)4WdXzKoTwT56W$6Cma*ko z-UR+d&gDe=aV4i2btP0=lr(c(8|GjRky3>f(?F4T*z=UR4zvBQnV9PpS{@gl^*`W` zU{2l%HCw6JoV)D|6gUj#5g7Gy%G7rnXtpCOs8`V&(zqG-F+k$Q1J4wy0cIuhkRK?h zN3p?FVy~)g=e)K0UwhUx92ieXgiJZTVZ-sH1^ojWURf{iJ8R1&7#GgsVh<U`@4n(; z%Py`h%Z8o<5A6hOi|Hy{3(VX@!Vj6N;MnGg5M8?@anLgWxHOl+Od+IexaVMKn!_hY z4pu!YH7Ehn9lMZidjy17EIoOgQ<*)tO=WGF0<FZ!3?n@e05#9bU(Ez7O;z@l%Ezt7 zKnbQ&Y)Ycedf<kT8v;AgWo1*)_=_$qn@$N;#>S?l$uL@Oo^6>PT;;lhDLrc}K#B_O z)GC!@g8B}T|Hfq+Y6H@9T}i;gcX{aKi;=a)eac<XZBxXDMZxHuB<JhVWNn<P4LN{3 z^ARcXc`B1cObvkg9m2KIBmDyNdVe-XRFr@SN2ng|ET7nXT?uC7%aV<Xk!-$h+KL30 zY{jF;hr?XN(QUrkNYUvgYs_)1gFhc;-xyug4u$O0R@si!kGNo7c%^mXxkdb?Z05Of z+1m<f7;w44MsevXK9({yP}sbJJ2}L~=3iAR5-LEMrp1ETrP~WCjcg3%@!jX_Bh@0= z91N??osCAb92uT2S+(b6`|w032@JuG`a7YXA9X3yu*8FC&&S$#wxe;V45jv%2_TM@ z%Cqu%l?L8YslC(%i;%>gY`qo`8_gj3|8kTTzop9uxlj`=qhiHh*Z0Op{11Bf6)rLN zW#7YR#fho~f9C(wyOQHb{LEhwvmr)*<d_>KJxwl|+pwX`m#6>ovd$rD`_ZiZ>Hm~; zRdUJodt*{^(x4s|d;XHrnI3K+qmV*?C|&*YWTsnUUQrsUKmiOZF~%L!UYA%Xgte_~ z4$0tY+j$dCx|3_2*7eP+=5A&fETl98&s<0~S~hc}e0#L@9cX$i<HEmTxR%A88WCHl z+|H3b&T})1-L|yMd+0P;{QjE)ndSJX)v(rDNe9_%4`$#Rv^R-W5-Vu@XkeAdxjUF+ z*9KQ`r{z43njQPLZ~dg%`8OJjMy-5%N;8u9wcP9$7fplEd*j($r*m^8ECM>6(cQZj zHn+|w$B|Sl-Hrhz4q=4{`%&nleD_-#f?7tGcpw{q>X02Bcdh)<si7S4DK%-#@M*nk z>7rSA7SwXO_RetjQw;xS;sSU|Hgc1GXnJ3>QRue{e%5KqVE!m?Joz2Csg%2;q{$_< z#SXYh0gd+=K9XxiMJm8Y<mCU0Coof+Pv4wtm?JZQw~Ohcz@~qsvFcXGE0rC|k`a*q zAtt>or=nTx-bJO5;D;9!y(6rK8@WIzO1kl{_;fxW0a)v7^U;3WYd-6Zt+Df?_n`;q zeTXtWqsDGCFHenHncowEE1+Cv+;44MJ4|!!fs-lCWta%Wrf<$hjQ%0xDGuvm8)%Vt zV9m>nzc{CF`=<ZSf$1+Jtb4~z0m7980>mM9*34+}8G(Y9(w~<0Szf~>AWJP9(#noo z#Qg+vk7S`yBp{3-m_Q~?=9u2S$5)rDbQkan&#zru+d$BvOZ(H5)SXF_pWqD&Wly6> zK5%<4A>70slI18WHVEW9qbCt_Oz^1}C&9XgUB_;+2aWDcEbQ)EmX}<6YYS#;vG^rg zFFTG){u19>3c?FC{`qN^6j|%$=pU}V+IY!f3$+r2fccx;Tg>WGg?aJ4eD@x~R;@xR zJ}`9JNruhyhu}6X?&ld0{;XQoC<_t5;*WA60p}Hu$P+3n=LYUJ?3>k#%Ra5BDbmfw ziY}iOV)TtqRN!42pHWums?sp)7*dg(+mAJ=)FqlqG!~{<0>*1QT7%DTQQnH8Rw{-A z=+K)%1s-DML<L9>PKt18(MWXE`v_$a>l+VU7`ZW~6f`@vHPT}4jIB?sjY`9YK>N8J zQp=LM+Y8@J7O(6yHc0<Titn0|eD92(Z+x2deRZ5tOrj+XrzM_(QYbaJg>2)PvP}mn zwT6l|EGb&KzpC3Oie>&Qenpp`)9({(6zxuHa<>7{d%OGKCu6#$KFIFQk|vZUSFP9y z@8#Mh4>>JYK>7Cx@M0iFta?BLpeqOaIO$H5IKL@}j*slI2zP8eEf1ZPs!W+wvB{7M z`W8+-+9)g%ip)1yAp2BT?)IBHAnP|rzf7W>;!8Uue(F85rR}CXg`;}X?%+NxNbgH; zj=YIh(DWb88)LU<d%1D#-dnW)`5CKdG)A6v)IYCY^5tRSCR|6dGhw|pQK2@;%b`p} ze~I=dcAD$Wo2ISU=%n8oo4?9$lhY&QT|c}t(U~GE`_z|fZ`RH$OK0s3v>H&Ek>6i? zhtF(sXHs`=?OUIg0d}3NADsitbW(lZ9KA(0dFe6}Lv(N<Q6Ftr(O4q_S|TF?hnxs( z36mQ;6;e<u&!RiggD17@B5&g(Xgfc=#{%bjc^Fw3K2)7^Hq^sDRL>;VKiH5{ti{d7 zklks-&uYX*!A7><`;z-iicPc-s20wP%A04>7@m>vg$eicjEMN%ysUDPXsChjAu1X{ zyoD9FMtTpiu65z1k|x*&uH#2ATKry~gOC~fc!u}86LK}BAb0V5obY~)FN>D$s_pLA zo?L!?(-U=M4tWBKs3YZ^oHlkl3!0I1X(J8MI{3)rZ)+VYja8OH(>1ISX?aCQ$J7lp zpz9+)P&qhS{CXR(|GUYjP;?pn|9l4g5528HrY0;`R5b^cRz^5zE0ZI*hI&i2>iQ6T zG=RB0>sTMn!U>;XY^UA1Y`8~!xJP<N<sqX0D`5=|4i@oc+`1L8ncJ>C)*66fk0zaO z7a1AtfI9b%7?tNNk^HjVPzjp_8^CQ~S$vs>&c|UjMz-H36lH&dd_P0|nI#>eS7=UE zzVZ3TyKc2>HO!W*xNL@g|55<4JXuP^Z$~)krcK4M?3t-Ag_JTq2cuTQ(<A<{-u(y{ z_;)jzXd5gwg}-RVdP~#1qbJAnr313hIu|Fb>{-tK;=O9a&VE&NSQ@v$qmX>7UYyfB ztm&5Sv&GYn`lz7vIA4v_4vx*#XKIc5OyeNiV}^3%61mJ^0-JCU$JwdT2VfQO6M|jb zey=Yz*PNfDK4Zp`B(k7AqUp}_>d62^0X&=tI4<4?XJExc){zj3S&)&uDJt2)@5UL} zG13gDJi^n2uZdBJk}000O`rXb@-CVfTKd)i)5ir#_*vJ~jUq5T$(P&j*<(tj)m}#Q z0TIi9eqI3X(CqF$bHm5=kSK>@MPCchE^n2?uy?i$-^H(YX)NN#o3%!>QEN_4-#zt2 zp<h<{GW}@}Kg+B49G9v+f{fRccpwx1n4QWXK*+;CXpyj7Shy&8PV0Rk6aNGKlP^Wg z?PDi0<r#_`bWf=Et`V%$YKJsOB_7PBeXZru%Q*Uv@n$$j54ULYxhI`h4jmm7!HR@Q zqciq3w<TG{iuMvvNP3~9?^?CYZUhgZX$pTQvM@Ml|E$WXI6BPWMHqGvHuFTNp~-L1 z?6F#E1_qS-|Hb5ieQN+J=qfM1PB@#zQxHX<@VZiU7gVoOad!xRM0AX99$AEGcDv3T zaPvz0f(7-W(URUV*)mI{UXhNtTi9mJ24zB1kPtayOoaL+P*+L!O)8W2!#28Y&zK3Z zPw3JUee!`lv!#8`Yh>iGXpZWG4-(RHj7dza7PhGK{5&tKoa;7M1XW5W;X#ZX?C*`K zGQVmYu_&I8_%>Cib<+=Oh@#c1d5Qb6ZJTm;=YuQhVj}dSP<q+$G|}M2rmZW@m(gh8 zYDdu@;rBi$p@-#6+t@<e-&<b(5sgL7q4Ij<b|cB2Nz_MBl9XynxJP-T6NeYHM%n{+ z%GW7FIjZ{i%bjh+i)o6}P$pn8Nkvi<bMTn^npS$Z0iO2yZYZAOmw585=-F9aN|JBX z{-b>NUu#+#+6e3a;OKTp);ko)((Vn1u8s?a%!J%8kTT!@T?o=xBtYC_%24kDPM8HV zpG3&op746#tG2$`20$~Zz<o%(8O(xZ&K#f8<EZQ5t6iasp*PgF>t)fa$>5j8i}Wub zG1p`Ph(T-qYmCFr2{PznKqq@=Knx#rM5Ffth<-3%WS9O}!k*^#qy3%FC#W#`O0x-p zXu@O}A(Cx1hxV68%qvYYHMx1$O^OC(vZ|}XSUvPXJt)5jWCHlvTWp4RM1B^Lfem59 zW*(+adJlJ^8=kg!D_m0vf7|kT8Rm$o{pmQRVN7i{CCmtf;x8{}a%5s^>kiGaWf>NC z$EZ@<+hSwdjuf);{*q{%I^Au?mj0Z2HnPwlkMjWYH>B?h91~3t)=H&n+fYosktk!E zjfP&P>wGjN0H{g3OxcFz<)4W_fGUI}v8-<3LMsEY+xPH#6tCtSqmjlnO<9|>5b6}0 zx?Itv+`snDmf{tDcvYj2$S3W1^MS^AQ-0-RRPCB5^!qA5r9Wdc`P6%sOO<hhY1?NA zg@dEHIhrep(wcV1&DChy6sagw&GB&(knP_+FfT5<oa^?b6TzoMtc`Dv;9f%Riuq4w zL?CSSdG@eHL7HLHvR{~iE~I_%z|K$-ykwW$W~rQr%-;T_O4EDWBrCj;z;A**^mKU? z4d0Z<wXBb#<E!sXtv6i9mwC}msjv@Py$KvwCU;0x{j~}C4xs@YLoEFq$kKFmQYd%( zH|fmhuH>a~pu?vqHZU-A%I}Q=t&cl&4g9Kae{mRs03Rc{gkLkSkj;${0q!x&fK4{u zjMGGocn*{1Wh-If9yO)@FSN)|F{;U-#R)H&P};!=kXxQLgg|qhcGI@9lbC5P5z9p; z6G>%gRS$-$0@=u1Of2o!Z6ODXixF9>N?GK|^wz#2l23o0p`&f)^&Zj0ujBy$f@dGz z9VupU$8UX4>(nFdBj544<L7o(k9MM~rtbd_V5ak>$j+zG;(y_#hw;)XCviM^XqoE> zY_pkLl&i&0<(Tf_%I1}n`o{U>3~^XG9r4OefJA?}eTMQRb7~%M;&k*!UoS`4Y>~9| zI5<kZjs_g1xw$w#G57e~<0b!3wW;D4pcoz<FwxIW^;y=%iOYC@H&39O_YGZ${1l%P zot6^cy=Y(6M<ef#@f`&!>}ZoVtiOwvp3x~?SZBuUo9SOi5u+OtXZ*_|`(`_NH_8&S zmQRh{kK%s&{=S`&Sog{?C@MM6%QBz)F=%B|$sS8!2f-3S6}of~pkvu7iP6%1PSt~c zrThVovubP~etKE~uYak*JL5_Ca}9$}^%!B$W#V!eabvw1u7r3Id1Ixed$6i;I8yi& zAkY-*y;;YkgJbmiDD?>H@=R2r;;aHJ#{!?5=zN=45mfUC`+{*!r(v#*+x{)uc9z=~ zq@|B(RnWW~)fcvs_LHsjum^1spW@~ypFOMxS9Gzb_u|a|l>np$RjRILB}hXIgQ>mb zOuc=$A82NPZ+dLpqLok^P!XL}&q+mOYvEbcjpO*beocHv#5?0OG{e8FK^^3tW<%OI ztX`ut-)gDT_3J7>q(3dvv%Gq5a2en;-eIbtQB*{M$LQ2?SgkKLyOVCLwUp_T?z+nR zeSB}qBHouJ&?Y@?I^WIk``w+xZ=lqtB-%Zldr?6h>^u+>r{86+ukS2=k)gDIf{{iR z<r1D?ms+P^ID6{M#}`_kJoVY^%ndZ;q)uMbE-4QWd9@t*ow>Pe#RaJ4p0zLswK*$i zP-Uln$>><QMdmCEHPK|7$IcgPWPKwKM5S3g%DVUvDgzS9u5lSt?jLI;ow%p{mQQxC zujniu#w(?J=S`;akP>T!tP=$y6<J*f_l(uY<i-XQc6CFVrcU3G_Ue2-aVOEG_SChu z97z<~Az%DB55cs|7jLR~9P-?SDuzWX<V*KUn2pUX-5b7CLWhk0!1C>9A@wKfCljz} zZ|~Q*NCk0A3OmfQFmcle)Qz3}s_qOE*SKXK%3MiJ&9$+E)I?<16xZ>si7l&vc9Uvo zvO-?07uPD^VYTDCjjc&)^YQzaMmMJHJ+{g(ji%_JrrR=Uao^T-7z-}w-_KUr*=H3~ z{?68ZDy~eRV*aI_eMoIH7n{9b+B)zh#=U-UXP=bJRsp5io<KrzC_8g0lguV%AG#Dd zBS{gsnBiS^<OROO5w&-`b)uquRC>Nk5sOU+IcIrcZ0*DMS=N!3)wY7sG!|5@Y~W7J z6%t~<L)hq|##A<jtDu?d41shEw}?0;UeqvlWramuSqU5{Xl%98!rnSUUv0AL;~eSu zt+5>iKS8d1Xe%qY*<O@uiEf}Z_(mxl24O**${yf|h#I;!fRs{3Hg@MMa3EbY)?6xF zTx+kASzF#{04U@=jOie>FMABD<zOk9jhqop4ASjf*t?<zgL8^eRT-D%Joa4GSN(;B zSlaC=i#~g%j12Qtxs!u>ivmR?48K$+Gc-x9HGkrz`4cBk<ub!b3bZYrG8{R3>ZQfA zXH=~%0Wr)R*JGTEl$NbbDro}H;J`zBze5I|ar))ZmHV%6;8`pHly%{S#RLI-8LM#u zpzJps9Z-&8=(-ON3CM=>w!6-E+i_|2wAmbUgLJ3u6ZSN;VP(a4mF>)|V{Hx383{eH zM~r>fm%*-yZ@8UYkD1k0X-<u7;s2~KrwbbunK#gqK}16~#_EY^NeZVp*x;3I*}gOm zxza-eT<9zhYWm3roXo~NkOxybQ8Yh#P{6&~A1iY!k`V5-wKlsUYbtZ|E^{gw7KpZ< zXRt;eRr6gi2)ZP5Ko@z<M@0s&E`*1~Q|>U#$t~inbP`I`=w0W2<ynVbV=)_-V6kVX zc4(_zN{?k=_Gks5Z0b{h)V|CLBB-5CEK12G%%#;3oz8j|aMzUjw6MgMm`$W(cpXm* zImg3rDdK>b8q6tMuUj2O3urJCe%_AkEnTurokNYN+{CGB<YiK^LZh!Ai<N{d$yLst zfW6X6XM-xb9_!J2W$nt7tyf~qfW{Nk;XO|(vXdw-`;*UQtqj~M7<!gEx5|dxb69`C z!OM<>IBS9X@=MHGs$|xKL-TX-{H3<yE!ZSVAqn{8<DitGealn|!0xiIC_fW{Tdfl@ zWgnX__S?t6Wy9dWu#8;2G8MFm)T*&_nIM#)xzZ4Hb_+ECS+xN_1>Pc}kTf(LKD1SB zM|hDd=S|M<b-OYU^ec;W&^gChV&TBKWZcP}4yET$EY5$Tl^;lvglD7MgQ&ne3_nR< zI`jOC&z*X#_2P-Mi{wAeTGEC-{ql2p{@!qk{;pE6gfV{;f<Z!L{i@Tt5SkSDP}@T* zD^CUUtWK6^mY>2w)!tzpYPG7NAllCV0f8XjlrRvXX1PD3g6i?PkIrQYnKAiRZGfgL z+J0dep*6xsP}P-7R^<g+ah2?~;Xo~q*wQH~L?}TswwH%CSZ1|O2PFI3Pw2n{mz;fh z^T27|D;+Xw2y2qwq(kd{jEi{=9(T{d^T<L-Lz0MUyz0D=-PZB$Tp#B(!K>LB`5H3Z z`lMt#_lrI{icaqTpeYBH#KGd<>GE%N`4_tUdtFL9>QwQY`obW4=i7H@0$wd&<Z!ys z3&yggapnmuD#|X?n0zmdUqdu?_r>Y$Cx!txY3DKnne1TZf45L53WV&1pF(Njio|l9 zHY1yxP0I{d@((LL@^_oTiN9y|c6a1gi{>^~m`-xWb&^suE=~DNB-!)>B>}A;nk_YF zipvym6d_?ikoNPSfbb}9M!r*y#>$iu{?!gsWf6Dd34)L&r>3Xs2hn!vj_r5d7FerN zU+Pc)3ztllsSeTRfjP26BXNU27}t^3gJ&lezJ^=cew15_EkRY<+Gs2Pn!f#?bxArf zcTjtfJHMh?H2Bfn5gr;oz7UTT;9k+V>QNh}yuYQWP&7(N$j1=f06FG48uq>5dCpIw zG2g9kuU)~H%NSzmLV7mv2<7TXcPFD$c<)c9@z(L}_?F@0jR=xPDqvOsdQJ6r<sa8H zUsA^D9&pjCqvl}zYdjas;JXdA+7CkA@KYL+X?;n2NshXQhY!jK2W(&nm`P(}5z4WN zdT3<Ii6Ij%@T;}In_CveqeWZ33E$B^C6>eu4>B>OQ#Rc^Y&0tw#mrb=3tofsA}IDc zr;Eg6*e|Xzhkt3B@E-F>)z{jDwZhD&5g<zGC`!~-0hOKiBb5Zrit)P6=xw#Glf}Et zG;7p8HKf$Qo**|D9?XY=#?6<-dWsMZQFJ9MG<XvAnM$Oz)_$?QWBaA0FEXpWFTD2R z?o8~*{Y8IT%P-^_ncjZyFksJB@2_b6g*LW*mUy!jA_+hM!ad|suYix8+Qo_D(+cmv zpTB;w!F${NN|7ed{Lc~Ufs3C)5yx@wl!ahI=HBy-%oBfNA>o^bTIcl=uSz(M9F=d5 zRKsz`08I3sxX$~2{Qq7W`IQI&!f~D+3IJs>UBexsOCp`#mV<5B$xymg7aHOAcMfY! zp79U&jy#MHv;o%iY0&}LICeSMm{@t-yrf|^xw&a>#=gRKsz`-jo+`!bd?W@4f}}yN z7HgJ{H`+H^{JJz{ts1c+C<|)vxc~P=KUCfUq<&dY5mA(>W19FAMWEC;7UbW%eRSB& z=7zy3t(#C(P%(5l!W$WfEC$G;RONLg8<!kaL4mpuh|h#p9UW``5D%vWvpR?+3<d3m zXfI$3+RrHLm}VmFz{nLDOA3LwJ#p!$DAplQbtzFDGy$9BsasQ+ei%dwB}S{8iL60W z>LdhxF~lP%Rc=3mQhUb}gLFvMW=zEpl2WW~2g$me*u2{q5(I*xlmL5|-@~})fS!hA zir#SSWYfPh;SuAKmRMU;wm{pO5AnwM#|N_@nfuwKwT*Eo0MsGhsv)MsVzA!Ble=|B z7<cY_`Jx|3wR8WcZ0CO4P>jsZ=V2RGoQDNciqk6A!7}q6A5J)n*ZKYLsY`R$*-xBE z9g-pAk<=ES>|UQw)|cS&W_vAxhsVp45;FqW?;eN7XApf&&;wI8Z-4c*$1r{`KmF4< z^N~Hz>pn_QA=~mxvVHjDyvaR8uOG~PM2Q=Js^M|t_?q9qgLJ#)G2=$4$#B(yfX9%j z&y0K)`!vygweP8Y*T@F1YsWF)$dHRO{Y@O<K7S+-`+azA<IhhR*{z+%`t(j6Kc02o z?N^W)VPWf^5e=AehS7MM#=`ce@JXWjd&rb94C*4Sekv;t<T#3#C}2s}>{w`tHb9^i zb9QVxH-Esn`JVzF=AO0NWIeNB^hN#fBf8A#@)~R4JgXhKhC{}~i+wLdKP!~CcCqVm z2E*7`_4&`Wp7-+xHkNSqgZvY<eP8W<3q6>r?SW=_bh(JD{$zU-cG2WWZ<|a1xg4VN z6y>Mov9p^06XVN!vIT{cSk6@}VW;txsT+-J4{c5HX_TeHp_m0d#Db0y(KoifZwGfY zmTt+Gjs-TP9(#-D_IiaF)ND<P_b~QI0{#f^hmnAP6F0*6$o8>eIKfW5a(&p|Puo&d zOMJ}Tyn4sE7}ffK2WcsGv7_jxIZ&QJRnf2Y`g()7#5rQr#x~$^lmtCUyEq2dJW)NH zWrRpwOpIiT(8bWBMe{Z<6Xo*B#plcu!NdnKYZgKEZ_^ydVFMn}*QLN|vSuj={tNvo z(G$1EU(|cU&$sxDqxDmIdcq~pcdCifIpP){mnNtKc7FN%#igeC<nzuJrZNE=`$xI^ z|IyfIc{2jrFf%$mjyq)K-;}Ww2PIv;W6$+FI_3EbPV*nm*9&{ptUpCJE|5z!%=YJL z{cug+{WZPY);n!dBk6?*>K*bO2$Z!rmF7OCV15?lJlMwTV!SamJ;A@3=FIdx<PSb% zpkL*g^cOd0Z;7w^Jul2qtzh})(pd8KYz~I#;7?&`!|<n=>MDpedSC1}-_IF%r31wl z9kl;4#`qJ#<<tEsUOao|`4eY9JKUe5BAvZhtdWHHdgVMz!dgi^ha{hb{3_>6;%yei z#l}Y|O%|&GnXRbRR_ZR8593kNGl6l5fj1EF0v-4jWk!-7TPpTV6xfy~+06;|yd<?; zhwaVGWmVECLV#jE*Fit?%rmXa*PWej#WL=XT)u8c2sPQG)K+q1awF;g^Pc+4xMv-E zdG@!~NbrbTuGWPA1{*_SX)}$N%#3^F+aD+UavXe|Ncl|;j2vv~kzr_YB>k#}he6`V zQB09SgQuSgfPJQfL>vwO_fHWUBgQs+(DHa4tv(gI8V0X#T9;{ZF&!v47RD*#zxd`X zd?6tFs)8RdcF6glOgKxvC-a@PCbNfz0X1%ztmqkgM}^whe3v&#(oM%CdB4zgm<aV} z#o)~92YqsR!$UfT4F@$nHm)wlO#U|_ZsYO_F8uP!8V(`SjW7o?P;Oq>FbRW61Z8V4 z-$rk53uS9Q6k+-nI^KS}1;4{1gWI5rOR<Y#YZ12fBRQTWw1^Mx`M(zB=}1tPuUvej z+-~ui47FrO?QLBTzFMm$z@=a|&xjSu6L|vQXj){EF0WmaaZ!z)*r1wVu_0m!x`dp< zr3~pFGF4Y0doB^+VPIIXTfCrPX;knbN}ePd=yjQb162mAR#w7JkqBfIP|1f8WzT_c z)|yA+S$U%py=6Xh--Xz?Zc{D24HLCjw@~QCY7lQ>-1q{Zbfg#)hFh>6Rf-=Uv2{a! zRa&yPZa;i?lDmLe7g&cfxv^rGV2!O>RY7HAwbob{rJ-}1xhw~a0z&APAXb1%UzAFW zEd;5%l(<Wyu$i5dbXALrGI8yirF~<w$7w{Xy7dbZ+<q}dOytEVpy))6t)%J4W`->y zLe=YrR#LMjOmn&;MM)3aoy0Ul6Guq~BnAWDF&GP!TXD)FSFi%;Qp`fFW4&)ER;No8 zO-FYemDNQ9-evl|%OZ8Wfc<$CCWBC4yR^1-{Yj^wVa%~LkvaSf=o?Dey|&sly_tas z*9!wQZU<fPGOQ3xsu5Y4(S@$T3nH38Lls&#<N@5uF(7yc%lqw@=&=^b&_Xx3NjP0_ zx(~2y>_ZnvZ~_zY^4r3Z73}Y=Sq@d<KC5<R%d>>vzVk@>)A3aaZ?VFb*+jxVw-eEq zPpcSRWa3^eQ8Jv9sg`C0hz<R>?_7B-9^Hu`-Qn2CNq*}Ldz-oxZ@ZEIptEP-_!xd} ziRushUG5PALForvAtB5i=RqURr@O=HQxctur90tFxp+el2g4W0LTtW*^4gHv!B?&p z_{q8-a&idL#^-$K3+dLbk8SDElAYlMfx;@!6<StKg23;@z2}D{U(RMoIUAdbv?UNr zwVd`rKuUbtkZqsrCqt#%+0acF17-**mqV0-<$~hZc3d|8N0NPOV5bD?vUG2DZf5?~ zNn24zywmFduby<QWzqx%)NPSO%aYHNttCsbO)9-E_nt;8Wl`$-;L$J=7foi4=2laM zuJA4D#~}eOpF_1246ZT=4?~D#Jx@Bv2F*2jY2dy8lgscTXsB6^qhP@l#MLx`idM%l zT;D0D{wOA6OIF1<o+wI6t7h@5JGJ6hw<cv`;>tJZ8@U_T@L#oLPikR*r?Ea`5e^}> z73L^?Gtn70i$l{*R`y-9`kkAiMx_|4NyGX3Yfp__dqDl50y{`Xv&o%0+2*FW9(eWF zOaa!!ZJf~r6ZgJ_w0_by2@IwqSMaZCn+X49h}d(gNu)-LuX0CJ6G@FSOb7OgmPGBM z1N*k&3sQ#S4^xG0|6Rj>*N;Z3ugGxc79Zv?lhV}G0*QRO7pNWc+Q!ipG(KLsqCDJt zQj)$EX}0a`m8)Ze*}-9^u!qf?n<FeT7cPjvK;8L>-aBoObgP6}AM~_A>!e{Ek~j#A zm!|A?@K%&{5#ddg^#|Vuykm&<050@s)U?0ftPv_#jA2BhYMinC_s#BXtL%*Mrn@q- zBF(m8a8c-8TY9!1)jPvvBQ)}j*&R0*_?~Cf{RQ2iWpG2t-?AHsXNBlvt+80W7T+yR zZb>_URMoTQ2C0>LU(wj!#~X?vl2cDkF4MJxIJ4X<{rXsgOht!BAFNMX$xni}T6bE` zMA8IGz2<rKq%ysB$dVjRVe^LaE?eqB>B=qk4MITL8WA=lVtj9QZX*zKlBl;&vVpN; zhL}r^Om;vzo`#JoglWXnt<?_dw76|EGv29dFy`8%aYqN&JcA%zDOYqwdh6foWhZu9 z<hHXePDtf(ehpO}15sHnm0!-YxkTR#_`cwmacNBz(yuyUK$duA6XA$QQ)hRWeh7nh zRx~(e3_cr9_HeV#{!KV*Vw=tTScYS2v<o&OO@3E4twnt)uOHnJxDAibj#lUdQYJ1K zl(LBA4aD0tq<Cj!7u+k!<VEYezeV>umqOJPGN~OcRr?aD_Bz+0p(a4*BVNEppi{%u z-(S$4vorn=^BvW|^uU&RnH4R*shs-n)no5S7b)?{s(|>j+3tLgcG48zjNFUiv!)%h z{opXXoC^=6ouZRTFWV_<QwB#_n}q%B1olN{=))UqtQ>Ih`!s%|6xzj5W+MPY23q_x ze*c;VDQ3gG!S-NIe960s3e&@go~iS)<YSC!M42wl?sMeU$OVrivy2|v(P9+t;{Z2T zG3?kWEH^c&>8eRHDe363L!QHD2lq+1oa!+j%_o-SW0t$g@-ZRba+SWd<uG%~_wvh6 zL|V&H|IAS62Q0IEwmiSgp6qQ6JM>w3p@akX&snbyT=fVw{gNPWC~(ivHh)Y*5mPrp zg3{*b1GVi(-}-<Oqy(igUWObRh4XjjG61N}WvFKEL;k5@x!*9sk^7-pJS8T#GiC+l zAfvFtaA6n6i=VXbn}qZIq)N<fyr{gh*G~K5ef}vcP3J9~%UY=0g8!&lxZx!s(e?H5 zXx)DGoO05>dJgWc9u^#~P7AHSxZuXseIH*4ht_2|Y{JxtN#&EB0N72ZscOA5S{zqu z+gL>@b#qH$-W#7F-Vyc^%PqLJHL0YyJhh_CtHp}$;MXY|?v{PG6~5b7+~SvMfHP58 zOBQ51zOeoAd1L0}xZjmm5meJxH5tVfrO>aXib!aA$W4Yky-3w?(}7|4J6c;T-T+OG z?l1mZ=Da(ztmc>7=PfVB_1!52Go3T9Kk-cDzB{w9c<SS)&MtoT#Z#H@k_D*$ZEmo7 zn^sD%xT=qTj|+QV+kI&|3}$(Y_@;g;byw+n6mI+TLeGbIP*twHoVP6MjnQeGHtyw{ zhHUKc1bVl9YAEW)P2@Gw<y(apZn>8R`Yipw7K^`6&jy^++aG^nL$WHhN5Ucm)q2#o zg`HjkSyi*HqN|jyv~&TR0}`oK476oZ9PJx}RR&XIWCOo+OIuk$^i5r*@J0pxte|Cl zQoVUUWfOsm%0P<JkJo`2pOgk%5Ij`JM|AjB9||u_k|)&l$8^y)FHY)mN|%l<#wC7H zL;Pp@Xtvs$`Y5_kd_or^RA1M-H1^2v8EMo3cTiI}$eZRT8ic`5H>SU(`7IQ3owU<r zl^@a{jgLIb%lzB=jj^_48pS{7_x63<Sv8}+ym01IFP~zg#4Ir3=zU8O>4OgsM4sCz zUyVL5`$CK7!RIS0S2xxugB=v>ZWdMXQ%A6nXv>0wUCRqI*;eH5Hxg$4vUN3|&A7$i z(p^4;Oh=)U`vhPcy3}dmvJ*`({$I6h4c_2xUNfjyma5J#&+;0Q(vr+bA;G*$^~#Cl znG3LwH#-Nd*6t_?=I^p<yV%S<-7{5pl_lv=@yoQ^{`txNB|fuo^3-<?EUQSP#d)`m zXeXo93Ke`r6OOj>hx#kys?v+I+R?~7o4%SIW@Z+A^FLqJBW~oY7<Bj62*zi?7+Ag@ z{RBJk6OZSH;Jlll4b)d>(6C)QV#B232=945jd_j|)mXN|?b3(eCU=589TC^CJF)cv zS)FeV@h-9)*&_k9bA@mkKr+^Uv*74@#cO+LNd=I~WJ_EKvrF!~t3hQVD{!s`&8KnZ zEx@)4?XZS%458jPW&!J7A{Qb-3K)vUUF>Kc*v8=#?avZ4d=aFXCMZ<J!&NO=<V*<- zkPP{-9!rY50Y-xMv2!|-hA*ItO%<c?ut{2Rb1FJ@iz8_%BQAxx4bwz_Bu>qYJF(h1 zvzIxq%%gS8EeZi7VgF(cP3UGrVy-MrG|*peZ@3uP=eiF<r^j4?^o^E0fW11@+J;pW z%WRCI3#FkyMw(g^gH>@9`t})$f3YZ{&sWGxs#;&X$F@Y=uq0A#G*=o2>C@*|YS;QP zO{>pZBCl4~!-n7++DOrVBD%9Ty&8rvk>JfnBDS~v9Mk#LD_1z*FxqW+LaR$!jJ@zf zh;CrVK0#i#v@P^B+d`Sr@@)|~Hmys(mCd?^p@j$&l3-B>j5C5E=;<<g<98+KHH9Oy zzFCdf&t~33B8d9C&8Ge6+_xM49=|g{qrIF4U3r{&kcB;4t|Ga4*d{8hCsTfA!SOqd zKG6_+h-ZkkT$7ooe|J|NwB=EH8+#T4G$dNtV@kaYk)Ma%-b(9O>*HjCFN_Wgje_}U zfaC;PB#3!KFcMoTe20b3x3e|&qJK*Xx#aax8k1vml4xL@<S+n~q<o;)%h&rleE#~K z`${{*Mjr#X!m^}eW3VqXKW3AA{-tknU&Y^mdrR^+Or)t1ES2~!A3CEAWaMQE#Hff2 zukmXiLNZ2Oa1;Fvj_8dW6E~VSCT~pbOm#+4$vlM*b8TnJbW-EIo6x&gzXfq0P3<y; zX;2i@t=2CRyp<V}dVnG{q394XOMDvBryJxH!liw@_yPM4zi3)$cDRG@e%!vJ^^oJt z%v@Vj_)m|JA!+Q7qt4jbw>Ijzk!)nYA&Fl8)G~g-xHjA01HbJ2xaA<S;&|O_gWAgw z;H~|er?y-S=Soy)+A@{&Dusn-yDop+*|v<bn%O3lKy<oFc409&a+XwQSwjhTTgJ4c z90{sE*`{`a<*7<%Z{QH!8$vW#2Y1#4ELOWXw??7}vKNA2c~8NOEqa|m1}xl4vXbZS z5=LjPGNG>=l`VM&8IDZUGB?B=4y_h0vSzwAkCYe!gfr9mkb@<JDzrlr0Vo5Ie)TFj zm7iWftawY!lXU|EhE<)3T)D!$^t@@X%e+i1Z3d>6QHr=EpV=%f%3I04v}8!ILC`UA z4rUAJdv=Iht&KRQ1T!c)sFeP|CsL-t5u^DQl1x^Hn+)i`?MWsRvRjh*6TvGAl0N8` zAO)G~@!>Va;nP01Nun1R@5;b~e`NIhTt)DKNgYgxP<iOK$q~qnU<<W(MubXREb<zs zqkM+qSg`KArJM+rc4Bv$X>sAI=AEbYSSUUWku83n%V{q}_CbCA%Un<fw)B1l-HB)2 z{1tt?M;G%wFx#G<^a%B@=$%Sgl-eL-7fj7>ciPh{x~Zj$YynI6zym-l;Ks5-T0N`F z<rTg@ulAO8`6WG6Dj!T;)Zx41`%x-9%A1jvhaik&3$!rz+Bh~rLKPJENHXiG+EfEO zAu8_enKMS!QW4IY9_LU0Cnfd<AM%hYs5edncETvXWlis-2{z5I;%ua7ChqloJ(LF2 zX+gfs^$A7Z7}d9+NaeNTZqAAIX~o{ysj@EyM}5+UmXx{e3%J7(CTYCBee0Nrv>-GV z-R$)?SJ$ivLMi0gopVV!FXfBoWm@f7-k2HV0b+iL>{TLXtK9U!&M2kVLIjL~8bzh4 zbmgOhnZfKDKgavs3FKoP2K@^n)5agvf(Vl1gtP@f-`;HjOf$SO1!9XQ`_!Kvul1^a z_)%RnmEC>B?Jtw!_ySHyUd>Gy6%9)*-PeaLYJOXTi}6`85*Dp8x@2C$jk^YhoWKYg zj_5Vd%Pb}=A*={TIraZSSgTl4*eMlomb5*%JQ5MT3&z@_rQ(ZR(1O}d=^L_s`f7yd zNMn1-pFFp=(W%-jw8Azlt*#PtV^le)z+Ctfd8FV^q+|$YU;zy%vGm`ZQ6=eCTgt|> zr%o)MTA@<L7I-8QVG_E;szgiDQEnr^1=yrxqiAkNO*g>M#M!&MvCb8^-3S)Q+E$)$ zZ#buJhmq1aA;djDBo|b6yes$<7>yKvQJ4F4`Drc@6$Hb6RQOAL_rro~lQ$z%O-KYz zP}2@wKkXAsDpY<O;5F8+w*9wD@SZ&T!i&EPfSa;s-5#_*12}F6+Mnh3pAcxrdE=ms zXsaG7n<>#+m5P$VaD9X#QBcyGqZi0AKp>UuV|BOF@~%-l))~7ndS&jy2nAUw8U`j~ z6PvVkQzZU?-;fm2M)eN!#;D#N^*(=q-<g(3-0o<<S<0gKej)BdOt!O*Da)A1uT;w~ z$oMa{;xpU(GSxl`b&Cd8FYXg#r=bqdJ$Akz@^cA8iMeak9>@+Z@7GGLEbLME;FS8( z3F(FzGsoCo5b_sVrb3-$Tf>Nt9jwx*|7NnooRJ--`E2vv>G7!pQ!}P-U#hKL+rDS* z+VaEHgqG@j`SE4Ke7cv8_Evc~{Rs=6<<<N9Tn0@vB#)?f>RrUI^W&!dA0=8&*@``L zaaml};^ol%dr_->p{Uig=O|v<@A-+BW<qx21Koq`cXjT=>U@9a9dCdtONY95cMo(A z-K>$p_<_zl$zXiA^I+#){C`jP?#{P#-fef?+d0~KPiL00w)b`3+j)pz@9#X?dARdF zzIjLITRV?*j_~dQGc7%~+l+*%{qG?XCm?XiT^c1{B8aY-NP%!3ywF_}pPr6xS4kMP zO~QEO(>}3ziDZ54x7vJ)2(s2tl6p80jHHf@AYbrx8sDi6QXnY2&lM8s`#{KHlcnH1 zqmU0Y$F9yyH&rKRT?UCHgGM=J#}&e#kO4h4;Tqu-5;LW9Gd+*wKWnFZt-ETO`T{|c z1iDARI)^Uf^7~rUp_8Ejj@L(GjYR<@F4>$N&**E)wE3Z%)J4426Fk#}Ty>81+UKKx zk)3M#5)h+4l0d<l>Er@*u7_7-2Dl(cD(|pNoEVIQL%MS{d__~8DZw~Nm3$H*IQZsc zc><b&9Te?R`d7rsY`|e^!;e}QDlUiAHX-W>xN<?7F&yTL%?|1b%>+qRQlJ#{DX7w= z;G)g;CU>-A*7c|V)1etTX)ER|zh<(+nC7Li1K&)J=<(uxrkx>AwB6HOlg?*S+LWzn z<B#{FwHaBS!992#p8SA{-Q#mK+B>(Ujm~+YQse7~Zo;3r-M9S*;PU2K9FWT$+2V5U zsb10c$OU(74!$*{e=awJprBj%pKbAcP-Yk_9u)Sv|K+oe#KH#Dm_aLIn+M>@&J%=5 zT62bDxoi9I$WaqDO{blPELSr_EN>3B-}a)0B8C$cq|_t0ws{-b2_EF)fGzp-VtQZ% zikPL+(k#hI4E@Zdh_EU0!@G<KkV1kO5=QfsQQFiib8wY{5o{0aZlP@G+K*^Fg0|ji zk_EVQbMHdS)<<Lj(TWmj$fWuB+()vR`}|+DI38J7Z2HkW6TziKN7@@JLQ<+T4uZqU zZ4M37Z((THJX-#<psOy#y9&x_r#j8^{pkZLe|w3!s&*(GvS|#^2%%{(z*>x2c?^`q zUvgVMM_c>UTbva{IEVAfiWE(xT`bj5jHNU~nJaDpu*Q-p<9+<X($q;Bc0LY|AB~RS zvgmr32_gYbrZa-(6c9J?M#v6P%v-W#x;Cxwh*X&|={dV-ku7y~?Gkub4#`KEw=#6V zEDeeg50g?pojnGbw<DL<3>VAcgR8+2Q}Rtfu23H3W6zyf_~i0S)F68PL@Cz*^4|5! z=SWgrSq2C#^thXwm;$1-PG~<U5i^sO>M5M3HVA&m#T|#$iVjM^1$^^KSbL_hwB}5A zD=dBV(TK%{0gpcF0Lq+IFRjsch;59c(*K6z**I(g<^~3#^9=R@J@MvcNY22R5>7=l zJGahWlnVLG7BPjK&^oGY(Ar^S|5eX-Y=*UgB(yJWUbres!j=hqM(QtZl*-Pu89FB8 z{v~aS1koOBy5!u~2vW#!E;W%cTLU9Agi8|v+Hg-yLC6yqN+>-@F$P+??JRsVIbuWb z(PJ|26m3U@(?D~+RL5RLX}ES?i>zHn;itI^Ik7f?uuTZ}2C3Sj=YbjeJ?<ys&CJDc z@Y&Dz6B%nk^9zxioPBKYHMVE4ZKowUol+{LV0geZ1ou|giq)%^<!bJb<V3^vZCr<f z78R3B)(P3jy$}G4wq^PiQ$7qpLa6M&8=(PfHUNaTqu+dr4q?}!p&8YtU@8NR1(FcM zm6?FkRvGB8bXOhJ!Fzr4bwz9sG?4%@48cH_Pn0RJY2G+t_6eYnNB=-;KC^O}P4Ab} zCjm;Etc<K>lD6X@JA=(J>+6wqF?iXLpT?XmqIPlX1t4h26NwS-Gco9A*!L5`jNZG7 z8w6lF8H2acv`1dmj)dOYWs)@#=^GGe5T|u4$eZP{m?qpBAkRE5`VfG2Uu-WYopgdm zABI3K+xg%=tbs7r0VD2-^^bT(+c`ub?>XFtGt!#9{&GW$5y41AD{Tzi8qeUty8{zv z)|!yEv%<TyUh1+ILhO~5{3h5hMdOC^T5;KFQEgl+RZ5wDj3Wt5OqFqoStSViu;dkC z)92vvATkCM4v!V1MA;D;CiJ^8%QhPXRFSu&s1;Jtad4&-BCFWs@lX&R5^-bzvcot9 z&x#K+w$q!|@f4cw*~+D5nK!ADBWx>YK*VOITh7R^5os+x7S6kB<%4gRkHrKKQHXx> ziKrybtQ0r1Me{R_bI5GFcHm|6*1kT6;WL}&qd-PGE>xxEe6ZoQ%=^I4B}3XY`hE}9 zz3{W7NEt|Cv)Tr!#Q+cMv43bfVc7JSH%P9(v9@Zk;+%+w*hM0t&516K-x}%mp3G+0 z0>@|1Uz}A=4BlNy@7Aux?^e@yS6}5j>xET`1rn+|@QTM~>aY*4jE_;mkqU7xO}Iue zgQ+S5=nUFgqtsTeRByG!_i@nq9Bom>w~4JSDAgdIrPG?-+9V;zte-PFTsbDVp?X9> z6a$YVEk?jmdoU`fjFK6{6YxD#Y}wglSt(~TR4`DI=B8+6-)3-)Ycgv=rREixCSrt| zfNV-gbXDQY&zS6C7UJrw=q2r#wBkiad%2oYz&P5|`eY9|Rr8FiKp}9Yz8gZe7QS|e z(U3Nz8%p+Mt@KZPD70+{Wo;rHij@hwD(64Ilgr@M`3-ceaT_5o=<cIJIUjU;Rmm=# z{+IW?AMv|;EgPE?09f~oT=*%VBRUy}g;He8u*5U%fInF)u4)*d3U*8%<Iogv-H(+< z&!Z6Cd|ivApkMGWo*jy!Ktokww%WOCRBM%Ts8BkKb1}%2Gpo=TA^OTnb+OrH#|--; z+E++In6rZ;o0+h<fBp1e*>PpovE#^AdHS({Zf|7-eO<@psj3BR8rB6$1;cz6%CfBu za5stO4W*7l$DTCzz9nuy>UmT1t1M{W$PLk2=2R~w;q2qHxp;O5@TDOH<3SpOeU7v^ z&FyUrA{;PL@#{~{S+gtoQ4kk0jfDMqlp5y&hxqi(9|esL9YaE<G?+bBCX-nMNct89 z!C}a{L`;$-i4#`eGYQIX2ZJJA$}u0hA;HMKZ~Szzo}-eYuitJAYqtIB=44KtzRi~_ zx3nmkaoP`{q`IkW_xVIQBU|){d`d$gwe=WuBYmR^;iax?u&6H+$B@tBDG(&Wa2Cb0 zR(JI^#W5iAY@#m*?p&s(vx8J>x-@#vu9$SDLz>%jel+9I<0y|ToF8HBSIkU7dZ)F^ zF#(c8P3WH=Q?Rq>3USo7fFqXBN9gRGiKgZu;20zwHdLu^y38P#!uCGFkW>BaljOy$ z`r2V}Xgp)JGd{aQ&k)B#mLJ9)w8oVRFK?}SQAw(V%+;>VvzPN2UWjw)64N)~r*Fh9 z!0u%_v+->|3Gz!HZDA=|>!jd*vW2lm))ZF2@IBm#5y0isx$TdfaBD-X6e;(3)8KJ9 zGT>^2N8ZuG(YxJKX8+n{igbx-5W8NyjA_QbxRexj_pX7dx;xhu{n!lBX0JFiDxVw3 zQZPF8cJ~Fb#TvE9#s5t$n~7N__tJ@HcOr}{W}~%xet;G|#qO%^{(gP@px*WQz{rHX zD@%BbHzQvdAz$~j1$&O;wSn6XZeG+qAt010C$;UnqK2hRUPHt7r~l>5G0)2);5}y7 zya-6zot``*%f-_V7e_J8f027{W1)X@hNjf!2#!Np&u*R{?m{91yZc<>=TPgBZDPGH zA07EOTpX7(k>c;n?@Im=#Xr>LJ-TS+g6qZ7v@PiJW}!ST{UBRos((;JGan2C*Y-Wx z@W30ZRzc}!IY{VFUFp36XO1@-B2LydN;+7OKkh>fvU=AsyVZ(EHCU2hV0a6eaTxS? z@h+a*Z3rQD_bn@>HRV(<<0N4y_lFpi`EN11W8CO|5SEy@rH?knzO`TChd-o=n4b=z zt4gQ6eRnw(_PaA)t95SGpQfB9vciWnk$Rd)y)!};>jufo&7beu{p7)J;7vYrV+`-{ zklW+cM=b%+c=5Td2@hsx()IP`P0F>XCI<G%@leKlm8sdEP(huW%ZV1+Tuu&qQXuOL z_w)5o+rqpzYzHLAldb-hltbOu1OT-<u8IwhmXo)taNWJjSWvM^D^mOsLH?}X*+N{^ zyLak4TM}*b;(PeW0`{%Td-!RI%}epQZf9uep6}+4AJX^^@`fzRERd>9C{i0=x_gI@ zHGzvx%~EVgCyWQedTB2x);`r1r_!)?23fhseGW#tCS1ML+nwwMt6OikdHy15?aP}z zBPjXpK3mbT!U&ZJ&hV|hJH5ONlWpE`%gaBe=_xK@1l)`ND%+{Nx3c3Ct&!`EeDdBQ ztG3>eY@c`R`A*&5)jIl2a;pw$5qJlZbjIu*3A8%n_Reu_VxgSBlgvM=3H`7xKcWdu z^!>g&wk&dH^xGkz$p<R3J7#n2Lpn{Ly&I<gXEl9Zwz`TQALm7eot&POcUP7+0boi1 zU}+TwVa|ugRom2)nQ|OPEYZ=J?Aov}!>sRf3#{3X?!9!)wcfI|@v|DYk>+|vn&YpP zanhR8J<$%0Q$)Bu$-^gxy2+I4C74V3IwJP0h=p<SR*YWa3ldKqCutUO8)3#Ggvy1N zRF=GK-RJM_C!sK_GfKcIRX|ETp%6C)bv8NDBnO&VQ<?|m&SO%$SmD+UTvntvN=--) z0OQfdMuxEN@mUf&-K!w^4@qqsqokO1R)O_}v(J~qe+kEKgN%!|{l?JH=+k}f4`ze9 zT3kB*{`b#T!vrr!Yn6FlJRL?=igJ4@<%8G!Iqz{f!bcLGQjA_82o$A=L|4bY*wJj+ z_HB{QxP)Iw_;%O!{<zq48tV2vWn|%;k714prV~piS30pC;{zvF&CV(~gY#slA^pj$ zl9}kZg*uFHjPF1lIep}MT-$!~V|3!;MeJ8-k~Yv|nez&&7+0i$1GuQLn<n~Ljomum z?@pCN-rZmEPw~xHx1(aW&ODsss~+4ij+!~e!+%X+F*c-uGk%3X3wKnuVj>w2865m1 zFkc^?z0_^6Ohc+#Ax|Hl|F_P=Lkp!ZwM@5@RNcXIC8IFRcg&8%-LX|Y<iygBK_iR9 zy|Di@1mYj3dDN#x{$?Vm=d7U$;n#xB^Rl!Qq9<~|<^?yE2K~fYJ5`P8cbP38duD4) z{Vd>tuj97ln*tRtTKcxS0LAz5-T#<Svc7#Fwd2Qm1?tHU*Ll-eJa4^m)4X!uz^Cdf z;2zl4X#Z1C)qUMXEMl&!MCNQ3QPRdhN*FD&60(YA--4_pVgn6{N^l88mJ4(>*I_S8 zf6eovKqjIq!&j!;@9WBlpIH@CYin6WXUm^N09sf58rbvO^hIzWlmave<LWsVOMMPZ zCK7to5J7G3dqnx9<;)@#9S?>{#|P5X_0yK?d|%(ZirWjMITU~{9ZyzPNG;thP{$uZ z+1q;YI`&F5^s<MdXw41}=w3*%*iT{?=_l&Hd?lP&M3|M*7*!))`5}@7g^DFj)i0Zu zNl%Y@Jq=i@g5)J8n*+K)^9baaNMHmO*Vzn8g}wE<3TOKigPOY97E5ro=OO#6ZG>Ob zN5xXosmZ-cNe3@WjY@w2+=hg88OS0@`#4Yl;w7R0yFSfAZl67!JQO&~gQX`grkz|m z;t{G)y+I2JYomTs6X?8wZIJ3KQ;&hv!OKF+5*B3{)qIwPg++=X@B!akPlZq8#Y&6H zgO_inY!qy~*EaErwH+8%r`Oe-<RMzcp&{YY0`A`9?bBfHedEBy%5&9v2p(36LOI^_ zH-FiKLC$QL!E3XaW0N`LO!e4%OZVryz=n1H;%a(4LA630#T}YFmve{WLf^v={HL0B z{nYm+Kh=qv>hb@`_K`HRy!&36fa<AB?aSvn?Pp@6V;04-v@b)B;tg8z<H}^Up(oYu z{z?b`G|~U}W};Z-mc`_;D*;Y{QTurHevLUHd4wpUTVpYb=;p{*NB_`I-e`7eJI!0Q zo$)P-{dMYJ8@n;NGl{fjvDUYUh$H&w^sTY=#+MuRX|nkAt;P<ur=A*7t~8E9V&eFw z*%{Nh@?}!76>m^)Sm}Kg9mj9PW{-}Jgr|Q}PrcbVk1W)gc%ykMNZ>c7HmF>DV`{5L zU;Aih^j6*a{wHsazF9k8>kzB`MssWW)(G+7HA(F^_HEpvt^K!%*8b{f(a?Qw*54dE z-{?#Zx%YEc#AnL-tlRU`JJUS(+^q)J$2$9NQQPvX#G`bkZ|vWi!7mv<WvX5hq(ItW zJeOlEU>MJVtfhU|mUi}`b2xir=K7(nyLM)7jh(OUOe$WSa%~@JjL_bpi&OOT^OS5m zK*_fI8Ts9vgF7=j2RzH1?xa*3>vZG5_Z;w2ZC@RI_55d4M0WI3BWFjx%Xy@`+Ru?4 z>AC3GTUy#|r$KHl&l|itCc9$uvK?KepKsb0Cn|oze&WgDa#2SaM-=`L1xKz<dsB9= ztVG1;M<|pu%c(K%x*Uo<)GhtXl{a4=3M$ANj$?1QJeBhHIe6uY%|R-iG6XNB@{z{d z3=db<Rxh@$UWq3!F2)Qvmwu!>4w~pRj?(XlB6QKJLDo@Ox?n85q6LBCZErSBZzv3C z!Xg%x&IAKQ3f>+9>);_H9yxdQIZ=vNLH0D*>!h)v`>|eGK#OCS<F2t5rDW<3pUD!U zo+JYetQ?LAa`?<oi!eAp!t&vjKIM2p6Ur}RbI|=pup5FQo0tU3F%*39at}p9O?<o@ z+|gBU&@v136W5vWEWsb2`<Q-bui?M8)_uK~kDh*|myiz`!gL(B-Ig7dVe&Fy=MTrJ zgNU}#JeL6O3FnTX9+5m<;<~I(ESGVB330Qb8=jd5r%B{dyQN0-DAGB5$q-#lWMi{1 zoi>?I+zGvW0AR#)D{v;Vy*tO^V~CEB*$~3)&D8-dNVh2HlUA(62JA$_c>7ZQ;ZO?~ z=ess*D9xIQE@blt$O@fFsc(l<jcQ!JC#=l<`NM>h9jP);>`=k9=ljEed2W-?W$+K0 zf}&aoSaC-^4}0w_W0GyIL?3=%F<$~^vj}$P9XsL4rf$A3fiMI!TolxE(#6ctwGlHQ zKa6NbZ(qt9O3oR!p@Qp(yJ?8<NSEd=se#CbfuaNm%b1}^KwnS9P1L;`LXWpzWQVbU zZn)z~^Vvxo%t<^0%tqxc(E~o~_WN94qt)1i;YPYAN?tIGOmc=|P!wA!f$y6$wEkAV zv9Wy)?|RJIsQi{y4~W!UtrMlC6iZBB1q>ZvDb+*?x|DMw<#q@=e%1ZFZkK1`wn-*% z1T`55KYQG%Io>>#QPI87E-dsf)761?--Hwsu_K|*ko*#6cpYY&MfV2e`UI(p=WL|u zF`8$tANHnl4cWp<*h-=ZYjn&jCWbwr6Wha%+^tgjqCWhtw&H3_3)j$!t1wr(VPO$P z(gm^P=XLp{F0bgari-i-@bW*#e@T>(W=ndOyLnj#f|yFWLx$>|QE_yk_UP#L7_}1~ z?wL>#Ohcyhp-D5BnX1R)bY&j;Zpj&Hof^gu4FK&atLM;r24RDpF3*K$(va#rB*mi* zsl^)`(*2$m;?`*I{+$|Gt|x|OxenPNl-Xlfb{L&YM{6X8@S+e7k?KItYoO|HoYWAN zK_s{+kB1Fnc5o2L8}Rq&La~ly6{j6a*>H-VKzJb~z)wmp)57dFbl|ydt2&}}jYwjh zMl?N|#w2Jazzn4Q9u5$X9S`fYJDH9>7Y);DZKl;4*`AKfjj9|MKTfCHC+1BbB)SpM z_Be0i>1wC-5em-h_tb+<&WDSKp+$R!z}pJv_DELuu;nnn^JK`q9U<8_m)Eid#US+3 z#kDI}Ol2mngnY*;dw=wBf$iQhhM1|OFmvN4)uQ3DmVS5Y1*PWic3$dk?T)*Kd}*Ie z)@AeJ3*5CdBY|JsSoqS6nXjp(qq=CpiqGg`q4sCg@O>|D1Tbnyc1U?G=wIO1qX!)) zzNlw3n58k}^@=~E=E^PI<Ku1vt;)vIbZBiELeg70l$K!m_24M6+tAUqJ8_we*??kq zv}YKx)PNoKY$=!|e@g9_K(+1+7-oJoRFQMUn7}Ku?Z{IksHjcXYuJDFZ>l~;rV?c< zY2x;9$c&N)>q=oUO`yJho2E(zuhD~zL)hy)&&j=`hwD>3tx6f&2m1*{vPSp@4O#TH zA58|vX8A_*M&0w_bR?(O2;C=P`+dmnati3kK0C2}<HQwda^Xr&o_x-0M;ITa{CdfD zl`}sXO@%=~mzYO>emK!vQFWbdiD1+GDS65F@`F5S;o~F()t&%Kc6rRNH|n=!T!Gx~ zP&E514TddSA4QdfOOV&_pGX1}<WKOQ5%FS0@4}CNO7BZL@~8Ru2jWH;k=6U#5^T{G z0*vKO=hCvMuYgc0eTclevX#EK?O*(a+Wtu{Qub>U#Wh{t$0gQG`IGv>#-N^^`i=S* zhB<$RZ@-YnGcn!Re(-kVNe&<pWb-`VJHn;Z|MX{(A%43Sz$cqPy?q^mIUi!JIaO`v zDtjrq)ewXaUw%27@5)mY5jMG*sVJ$(L7*^J7^%^?EgVR$pi37g>k5b1dJrEHhE#O~ z++}{Uza5fw>JmnS;5mil^~u;Ke@E8Z-M99Vth2_3%GeIeVS;9oPf7($;8>z#I6!Yp z01fh|r0K0qLMXF7!%_w1OTIBlkIe%GO(^!xt7RU|c5C3)Ejqz$EBP2tW&z<YQhPB; z)e#_jzU3QoE-Vb7PZ-Qb7>2g@da6?UU(i;t{W&}pP{^PE#dw=PV4FDY<lQMpuw@v- z@6!e{pO3@J4b(KUoB17;cYag$&ldaVB*}4*zJ8l_kE}~^=V)V<yCs3z(Br@C8tF6o zc1;2iDl_e$2*}_5U1ORW-!#f1RoOByB<#CkVXNih8X@|Iw~nQHdP`fUDg4sL`7=W| zPU#vM?;~x~!s&(6i=4fPQg(S~aYEbkW4h=NDo*Nhip%ahPM$h_;^pTSmlsYve`ew1 z%kwWReEQVF;+Yo~ijHp5&i7<S@_a1hHRz`D|4JXfrpq7G<%{}39B1)Sef(3poYu!b zt#>k+7eB6d=9;R5wD=i){8?Q}Vv#6D@qPU8b=X_0Hq^XFX_DllR~w!F!pOTCGtGK) ztU2D?*PP_vSo7{fO?b13=GgSZI4_J&U2E>2dZ)FqN>}Nx9^Uo7i#PollZ+WtVyxzp zlGIZ1P}LIFF?5jYh-^u3VB}bu>`ryZyOZQP8n@aBBUDP5?(FB@={J#UmiBXK&+Oj4 zXbEke+eB{_IfVARp+qNI@grKLVy$g4*~AkBw9@wItesa11AZy<e4r#m(2r?n!b$Nw z6`$q$uE<nrm{-VV0ICq&lcFk8XK1oK#g2)ruF;Y%mJlLXY1@RRoGzV^rQZw(2pj^s z|KvTE5Ac-dH}aTCIscdgyQk}sCRLohJ8_vp-V|Rxk}!!yhCc*YFgWePVxU86B(W({ zTuUI!TWPgE#?)PP7Tvfnx2gXgee@Lyg|ggyyi9=d;D;d~%d^t2tb~!vKLCB?XE(3P znh(TGdwt9_`km`Y=AlqqpGkyO2tbu0_OaGFHD+Ndi0f^`ZC$vk!0enO2+*)pKS@+( z2UwyXVQudrE)o&&bdeil-O~6pw#K$dy?nE_K5?^j+9^{c(c2K<;;GYg_w`9%3rSOB zh8fB}Ng3a3Tf6UC9`fXJNTk#A0)|-6LS0lhnZo7YV3-*PFB0<WqFZ9PM{w^!k@Db> zhKGBUH+W{Am-$FaxS(1T($Y<eOC7Cm|4ZSHW2K{^5N<vpoLR*TseO_ef3;`{CE{KQ z;TMh~(zOluj&$F?=qp&p_f(v|gr>=vgYAXiM&R9Xhoh)p^t+TY*~TVBN@t)cwLv|5 zB;_mu0jV(OqPrNf9|R)BF93SLZBlJXK21vbBERnn=Fl5_fVb}-25<FyC+9hVe;+Sf zf^FUmah_Yceu!mh?3ml_`q=XLXGUIq4Fl`M#i`=cP-6aVHP@}`><uNSvU{IGu;m?h zf0ny3u0ANIJ6!Z9iVc6aQ{x_e$34Ln?U**Y!8b0lsX_vyU<QJM@T0KU3rDB=obQT1 zE^Pg)x(Im1FY0nbmp`G)k8w$IpuYGb6E412@A}}YqwSvwzRG(zg6o~+{u%S!J=ie+ z-QgH6kmI#Cu)Phu(u73}zggS7m#Iq+Kqg$n^&_QIujT8|L80~rkldyv3HKjq_-Ueh zpo&iJf32x#sYk3_;V@(w@az?4rOhct#<hnSFhiCD|L5<eG93y<5{cD{Y1A2Y(WY0Y zF`QGBjg9+(lWRR}G0;L-a^Jv?-FWt}och*RQOAdfQI8GK@n@8f)Yed*8a~Ot#=GdQ zH*CH1`)`#rNR>^u-#KiWp^Kw4dvZ(85`B>g4`p?X5mI->*L6$6n@AUJdx~iOO$|q0 zA<bzqHq#T$$wP-=V8-N-FtW-I=}%yOmRIisTq-Qf^ird%>UiYNFOxcUC-JtJGFD}( zvk!3Z-@SYJB~{*NFBUi-E3*6f_LcU#>E}f+5`mG+e=sfn`KUN`Yp5h}vt8r7jLL<R zBGAzjIthc_v)6j1dx;~@d1$^Qsh!T+RZ6v3b{)_aT$P33^4fMVOaybf<t@W_A6l1) z5SXFR(aXVuO^i-%7Aq+|wy}`SP`X38T7)zol)(#AUZ#0xQqGgM?rISYyMu<aC+0Al z8AUF>1Bl~oyDZwWyu8~a1=cGH2~y2IQu$e82~%z+-B_I`bkgbnUWd!Akz1q7u}tQ3 zZ;rf)F+xWe3O~|8^BZ9b>owy%QTgFeBQ08^GyavDTw-pHa>rO_g6sIk;Z9SZZs7!D z&rRxE)3Hr0B$}d)sm@I)Edv6#b8ecC5!+L0X~K^9AIuo#FxkB~P9!K<6`X;Qws@9j z`MHkTqj<*N8#uB9N65T;m-{I!bDk}`hU9+GI)xN9q_g|*5d2H$eW|k>XzAaLK3SQU zgo_XI>UlJa$PBR^%xDMoqij5tYSq|zo_@HuV4E-+dVWX~8TE;jlE#<$_T3t_QMJ+S z_rJCAJ{3z<)8cz*;&j258d-$365bTQ#3$F5zoCyi`uNMb{E9Arg9}93j&HmD>-tt> zVyyOs{bU3~cyy1(cQ0>7Ec$0=%mVX957(MVMZoyM+l@J)XT*+q+ObN85NWD!7Smg( zYRL`^*@k43tDH{|@Km)GOsPXg0?J2BVN3ZD``0y@TCq&48Eg}PDR~*z)^Y2I=Ay8b z5wz)q7m|K=dv@sfE02ltmyk`MJi2cfwced1&gRnQR!{rZGl*^$yHoOp3<$0jaB_Y@ z@BUVVe@V5iuxp{oHR6aN*NhfIeYbso^n?l0yHm@{=dX&Y5RWGqyTVzN+m8v6gkplr zZbS66z+r5p5Cn?9qi)4xS?y0V=M;DrQjS<Za7v66^gUACrOP2*w6hCw9v)}<uk=oc zQG8WT7%OuhA9u$+cV6*V^xLO&v3Rwk`i%wYUOG=*9dq|2#_Q8-y)_f|3LWN!>^sG~ zb@@$Qp4a7FYW_XC{JK8=V_kkjmw%$mi~8YHy1?{dD$>RB&cC8}uj=xPx^GLDf3C|f z>+%)-@N>FI+_%He09*V`ecWqJcAHP(Lw)s9_Zd-wo^%V8->lsq0_0?zc?eNMy2WW0 zi-MdJ;|<gVQy6i+?eG_fbB+J*ITYV5hIcO>oI3Q-!Q$W-Mz&$;K5+2ig9i@1&}=p* m53U?KdGL9@|H8;Yu4fNU)6$EeZ^ORR_u~go96Z(>{r>^seT?n^ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/selectable.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/selectable.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9cc828afce2ca03b0e73e504644e4e3682c254ca GIT binary patch literal 122398 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/sqltypes.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/sqltypes.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7831a61f35e45002b71ef8c6986efa2bc01bca2b GIT binary patch literal 87757 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 W!Xsl69Ks}gGR88wL(D1{|NbxNSVi;z literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/type_api.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/type_api.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6b006774f7e668b919ef494d45701ffbfa0115b0 GIT binary patch literal 47762 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 QK6b>OIXQi$TB%O_U$K!yw*UYD literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/util.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/util.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5019f345843cba3b0cfa61cce5ba9b5a4892ad8d GIT binary patch literal 21713 zcmch9TaX;rd0ux<&vj=n0GEpkDXP5$xI5rt34jzyK>!4D35b-T6^mGk;t0@icDi>l zi@EHco&|Qtvnf~$nV>DvlI%)3wvtj@D%pwk5Jz$)k)u30iOY6Ua@i>-U4F5YL}67f z52=!?Sjy%5{&Tu#X0cEy56%L8`t-S<|NQ5_o&U_!qoakb-+QG}tXkF&tbxBY;urCR z-?1&rx5}1p`%c4dI%TJsDyMAAPx)@cZKliVW~Q8x?=<qW<!m!o&fz=bXB+uup<Ix7 zt})UqmWxs@-xzI<mB*Un<?-f3d7^ose4u%-e6aaY`Jv`yd9wL%`QheNc}mI_8i$&P z%ZHms%14?<%SW4!lpkq6T7FdWMj9VzK30Crwu02!2iLsd=-SWt#a*X7jj@e39&a8i zA8Q^jA8$TUenQHRq2HPEjKs$qv&~Yug!qJi;I36Z;UDxL!uv`8y#KI2b=NM>`G@?& zNIB^r@sHv???2){iuY6g2mHtIKIMPV_wYXLU+|~>$5G>qf6PCQl&AeC{29ET@n`)K z-XHpy<)84MyleSS2G8!M%Fp3@&OeFolfiTNejeZR{!{pVO1?krpYl(m-dX>n{?q<5 zX!#@lhx})ea?Zc#Kj%M>l=G<bVgD@ZoDFho7tqQ_k@6A$98%5&AI0}Y|IAgZ^g{O| zFV|OZc#Ysz(C{`p^+vr@4?^$uje6~dx4c<z_}=DvyXCbzHv;7~+x}()*@y@8g~EFd zP@v>QsbH%Xxvc#j(19tVl@--)R@%#JL9G);`Gt04v)Q^DbdZ;CuLr8yX{#{G-KvLm z#@<6Ut7J#%+Kp<x#fTqlYzC!#H2Pw_<-b~0)n?EMR8-V0Ei|f|VGxb#uS<=f8MHc) zySUkCMB}fws%rbv*18J9u-<M(W0(|rY*cD8iYR@hx*Rm3iAsa-iV9XRomMS~4p%zC zR%a6>_vDn)QQ>OPz+7IeqTAxtTD4JCIwf*H-mbTz!eva@46Dfa$5eZBz1~{IWHNe~ z-NW<YMq{3EnqNa5jHl%T-tAT?6&<LAH>!<B`*x+;YPCDnP7sY%{2*BGe^2OdKZr}9 zX#g7VF*6I1FcP*^r_!k|W1VVTAO2<VxOf)ugda!Hu{w6o-o?KiXY2Wn)3bK%m#piv z*PNZyj=S|(&%xK!PP*r=rF!XITOI4AdhUvI)!I1rhP5&EhUFvf+d!dI^;fazAFJOA zT3*#_)WeR~Uhyu!_Q_9pHCd(5tG0Y(g#thhY_8zpPX(P#)$)K?-b!7e8YU8KNnw=a zf_g+EDPWqFdU2~7M{CuVw;XtcXMkQ-fj(%8jrRMSk2?#cfyH(!nDZEV^%bwx_L|iW zu)Q97EA7pe55(s1g89|?vuAaj-@4n=-g!@eds>!oR^n$$Qlnq$OkC=jfl?nT^&6Ar z@3iA$X9kKrJy1;6R?3F$O;rnKKVwEQXNGg;`kYL?(rK5x^XKD1>W&sm&f;Rpja))c zkz_~tptafL?hB&vdI)?{ST|obYbg_D4QR_*GrsajABbUO*CTr+vcrF1sTnl({_?`v zYp;ia3a@R?gfrXMUJAmSo%Z^*7u)TdCqGebRaa#dPhQ;)J3;egvx=9x20XcStx;dT zhMgarT(8z{qFi_lJEPjD-3Xf7j9!yXvc4Tn>;=IzQMib}I#9F^+6DZZa8wE33APjK zQ|1YeA+l<{W4~>^ZNKfTq{yi5=H78S);&k>01|xQv0MFLsQ9V^iK_)b*N&>*!rZD2 zqBHSnRZu)g=*pbu2MwetY;uj5LSDRDySZLf^|0LvJuGp*>dI!T#@%iZY>9|(l8Ah! zo0Ya|4&t33ta#1s%1Uc?xlY112V%X_ne!CrcE{k#<+k#IDk-)U@%n@G8tocZ+cy;b zMisl!3^pF67t`Z88SA=0KsNC-dz$8slq%EG<+OLZ-e_>ondzf(nPwIH)l5fk@H#ek zpqgf8$kwAX(|?&H7i|v>wth1hoI851%qDw_`)ju9J?SkAu5Td54(G$o=4`-^lJ1w~ z__xL#p_1wbR=3-mU}`t8xH8>jnTPuI&IKD6yp5!8RVM^{6IPOh<-Ju*LcfYh=vP_O zRg~VXOE&<adN}8S6{~Gx5y3@#A!a;aDLSm=w}Y@XL+C3Yn?luA2V_u&R*g}jkCnu= z^=cTBTrvjQYnG%&?#(~a3i6Dn@r2()(6M^p7`u)-*t0%m*;ea2J!{SJ?VFDJN+-4E z;)`73EBEZRj8AUj*w)sw*kRUMR&$meduv+0bMig8<J`0EVJ{xBdUns*g~+(^Dmcr< zH;}rP_nloEEi9paVb=mLD(<9EJH^zEJ8xL6vofL)jOaJ@h_+^vvXAbhwx)Dx)GX|{ zJ(pwN_zv3tCODTxuC*9tG&-t0=2t341&AVG9aUhZnnDuTZ$-qDN(-__rJ}M-90k?v zgEGp+dnd}Ea-blCaL+_(*(y<Ly}ce4SA$Nab_1dmqzpC6-qKOlu(-%>M7D}tenxfx zLBLsQMQMzqb~8##|3YllEb6I`AgJOAJp`6rNaaBOiugBye}@qtv&Wo*o#ET=KCXwa zr&%%kdj1?KLL<B|FS&~)Td}c{qdtrm1_JsfJ(g0EqRt{&I+h_sllwJ#4rM|jjP<zd zOiZfth+VEp0%AElls-t4!@rFH^w_FfJDHxV9=m6)A+wk9?Q;wrgef)0RD7lI<*2jf z%f(k(efDi@#rD&F=59{vWzXsQIjNsVXscUNrr?j<&Fy4+S+rC<XPH(;&jFLyfSLZ7 zKVG+c8RFhO;>1Mvw5FEc5~UQ+NUxxGog1WO`|yDID5E)KG)}Cb_)swkE_rB_t~Tn` zFxgn#U}R4Z5ftQNGAL^ef*1y1gJVaT>iT-n@}snf7Le#tkc22i%6HhuhA4NgQEe{! z)eFDG*6>)}4+y)|CHpHKms3xnOqAJN_bC8mK))eV??1K)l<bcUERq1?_|%KYQ40t- zCM{6|hb}RdQh~;jqQ(-P{VtM2&f2oOKeO)u2TQA0*a|y{4(j@pZYg_%6o!|9mNEh& zRg^Q-J1FHa7Lhi~qYxr$H)DyoJYH|r8k>Glk%b7t@@SmE^$Gj=dfCEqG0v~4O~0Ji zGyp1!qTH-^hM6w6WHd4`p)Dlnt(kUh*KzHGcnbP)JiLoe&M7ztZMBG!3E;U;xHmZG z@EZs^L<nogUbS`{%I2#;574w<#LoCyFSV9Jp4$Oc>!d;dJ}0rvu2Zzampj?D9Ax(V zu3fZDP61^;jkFO{2P&}gHxBpQo_#Z`{$9^s8|}HIt379JOvg43ZyiOAaiP3p&;nA( z0euGz{Q_v{;Wtp0=+NDGQ3chT&=ASTXW9z#q_@01Gsx2GEeHpY?CZDcJ~S&ZCu)Kx z`w~j1nKn0Bd$Z%cbm{8CQ!n6IeErImtKQ{TUc0(v;#Xd{x>TA_1XWZps1XJoHNu1} zNG(aPdJ<o1i~(_2Q|(DmZ5bUzwF;Ej`br(TWG;>=Zb$VDgGmHYs_Oe1%bvz(DWg8Y zR2N-_Q3hJ&W}_oyF|s?6UDIga$Nllm)@@Z?*Bw@tw|@<lMGUpvoDF;cc?Q{biiT7d zY=_^#jWNix>eDElU<Nl$qPLMLiS!E0AcpinLm|OZc1<lNRY+l2<tm==FapiGJI;!O zg`WhIM1^=ayBb<Dea>89fZhBY<1(ZEw6ZE#Z&Yi+x7jdzu*hR(%w+c{%zfHpYJqUb zj-52$RPQRu?sYWq-`uC$sr@_n7Dpir#?HB}tv-YAAq5~|IA@U4F@Wnql%Uf4jwK8t zMOC0<`!3!9CG*~u5&&ts`<Gg$CE3@=78><RRMbzt_R^)-ycgf3a8@1AHHUQDzCNs5 z7}yxl4Jf-A^%V#gi_y{>H8zpoYHbCCg9XM1>c;9s-1nTYChi(QHGr(hA~)J0#Oek% zroSys7}kXmu+_byUW{UbFCJjE3Q_O+;T@O<)_^BB^Xf2Sb_>eVmz@pQaDnJhg5Jhf zV&oZ(K~Ch%pNdlGat{{0UpU7>kXK&#m*`B`LdNbs1A0JVUxTT_GRA^Lnu1N#N2q=S z3C_oJARY_O%N!;+hkF)?97vrWdw(>*$LlDqK8pa1Y!V%ZU=ac}h)O{S{M4mM<y8g2 zAK?+i&sa|SzP<&1+(XiLkz17N<v_pKSKQpD=g4mNN28@XcJe3eg!9SRG^~-l6{HfB zbG?9&ISovtK3aVX1vJ|we*Hu+eiubU5=SfRfDRR0QlE^i0;D9{hXj_-0-o>*1Y4e- z45aCa9q^-`1G&FI>(55PSb{`@?tU*e19hqjQt#k?sbY`N-bEY^gQ|kEs^uzZvf2=| zq&Bs(1fvW$ooR#GN(1UlW7{+90(K$=2Abg>sJ1k|hnveV9V(hJp%xpqk2<&74cIyi z{=<$}hZu}Teb~t$Z*f1uVh3C3$0-+>VoR4HAV&{{n`#B5&xm$nTZ0@TTmkYQC$lff zJfKCA3b>CM`kxV`B!pIe3{*eos2hkU$P`1q#$umBK;93{>En=TU*(1b=XZJzwHes& zARB<BKacbjc(;ou4I)o24Wfoz$OI~f+$?gSbury&KM>^diiA<q|3ipH8JIo+2X%_c zrx_e!@FIg}5SR&TePAC3p;l1n2Y5pAT?<%6J_j7LC-F$`Qi5Wn+@#Pl(PczB5b7~C zJDkM8?G^jEVbSLTM4xN1=%dxSttTL&xFVt)-AQ-Sy)@KFM_t)ChjJMsuAGCo;$hO1 zuAT00#SBL%_;x*jA)rmf0{R4sG6`s>Z>YUzC>k{Ka1+=HU0F;ALQ`S=po{<mvuFwi zA>VlU(rcGuq!+$$_0p9~3rjRJLWKz9CcWD*IPam&5Q<{{GQU{LYT!Q%$cT8OUS;fQ z1|r{ZRg7Fxo{9z5P^{7hNy8|+Qg2{dmHvu>o@_4fp*qo_7`YAYji>$J(M3p^!%Al; zwuoNI1s%h3I<{&cCs|KYEUxDQp71CF8kn_Nc^Bx37-%8?k$-g0zSiIIgbn4kr$l4c zcA2o<g!Tn9NCUbMnOffpNpt3M8+?UGBS;lNV8=8;UBr^^3?T3x3Fe`GGY|)L^Vk(e zjz~Gsqg5NtCfkv-qA4VWM+hMmSCJgVh}ajy{8Z1bLliTLq}%;dGed~QLal*S<Y{8b zS*Dga?cJ(2HbJ6>6iZDn4DGqJ#{hld!Ud5Zyz@lg0lV1zJPh2PHcQXf=2zP7^XESV zV;)g6{(er-jm22HZ?9&fFcA0^J)Bv}<^9~xpqb~cOVtD@Ln<t{`h(TPXgpY1;kw)k zbo-#CE_=h<zSU>1(RgB1@L@WwHji*s2z_Xb#|DPimjd=wi1I8Yb&aH{uAnyJB+@Em zKx_cFRE(3UM<W9*kFZA}%thBJrXYxKqR7ysi3Nh+bBJiEdW{svqfAY~eAn55DY<7& zLHLGJ25Hhr+V0}}_Dt8And!PS$HSRYT5n=D7!_ffr&iM0UJpV=2{>{g%_)qORCn)g z9j|O`wmU(^_Zt<im6wcL>_A{B+o)B;ll&2m7x9P=5?E#1x5_Zxl_C9>T|ZS$`?+#P z3@TPR>u0e;^HBk(V(XB*aM~sIF~!ZTxb#uJS>1v;tE&3POWY&L?kA<v;sTUcn)aZ& z;_wLO!kezT!nbc9g#z1iU?_Ay4+9`zCi#Z}xkC^xdhQ4$Pv#sFc83wUi_*D_rTe(x z@a%C=Y7C=V<L-yK9;RSgx=R|vQ)-K&O*LS1`#Hp5b91r(S721*#*0#zU^&BFyvM>| zRWuTOuR;#kLpajX_)`S6$WG+kAc6(=FwP#j6Mb0COF`Pyq|02Cs~GDcAWFp(r>B)l zRIFeXs|{^ejM9}WuUuV<GL@y*U%he(pD(?#uoUGhm9Ww7gfKkb!SDx$l&EB?i;P*0 ziXtL)OR+jQl0Jk2#I%cegoq8HKprSifC7}pp)qV~eip~b89xV*k6;Lb1gd?z`$3$G zK!FF>^EAGCx9Y)dt@|K<9^h=>es2zAsj)Bq@lDiQz#}37?7UWm%WGADf@B0VC%uDg z4Xu>gVLV9~n6hkFz_Q+|cPf<^Ic4G-g^cdOWR&x9fuU`i5O5kYfqo$2+^lOj5rQ2N zZVGpEL`tm~b_5Z3VPAxVd&bYS96!6_a6^}Ji`~rZ@$h(9>N+@n!x-d`Q{+_1n}uCM zdm7t;-a(EhW@pDtR_0@*fAzOe9B6U}FFj9A@%{X{)u6>~ec=_(k!`zH_xRupWl1W1 zLYUN$E&9Ga7h$*Pb}zDV0cQ^}^d|5%Wbh6%W4P)@FSAdffq<(6d-5=Bbs-`*+r;Y^ z`O2!FXG@=Bu!|tdRw{nG2DE*fNi<EWUuMwXQ+))!hm7-auRvhObqcOxY7I~FNBCaE z6F!Sz2z|$ZzT-l>h`w1rC+M5^3wUS!5x<Cc&L73{O<>o_41`I9-QRs6Mhkz&xrlK& zNK^qsU_#HYhtNp2smWB$sUS{OTgxzhLFk)l!aP);={ErBrDKd-lpzBgp5fhSL#{D0 zz;HvRojz?d@<5c)Lyxk{Jg*Kyz29Ub04{tJVHWUcL>$;}B(Rkj5h2Nc1%=eFFc3oJ zYSb7`9au5qi7e%(xmeolLixV?@E}a@-+vra7LSYxUOB=x*B}^zZJxreOF>14C96G0 zcDiQ!_7SV&bYRz9Nr9O<ttFi<cFwm@B4f&5dqDYPro4;xw@^O2gPo3ciimTUivWrf zg&%eD`U_zJZ0SdSY6p~HZ3N#e3;M(yKYdu(5VkUymY*d{5>{1+>ytJlb#j_%N$HG^ zV;&F!dd`<@Rfx+mA88isuINdB35m!r3V*xYeO_3YK{Lv~$Lqu_gdKQ>;OvzW<Q@c* zD(~B#=92{cd;bT2@BMECr7<mV$ohK7Qg;zVu3xX=sH(FG!>Rf_lYfE19R{Qr8bK~1 z7LDk27SvW>Mha*QsY#TA+)~cgL&=F!&2V)u+Hmt<C)%7q#DbJUwQy?aopFlJq_D4P zm_Vl;=orp@sHMqI!d^JTgtUJZA5f9ISbmK}JgPj3gGA>YtMyWn3Zzn}km@3JK1rn! z{W4NBNImR3;b%KpKh=TaOmy6}r|^6ZHo~<$T?kejp*G0X3}YwNa{VkM9EZJuX?)4g z^-^RQe%`#vJPPsB9(lcWv(>)c8dyC_e%|r$KCZ$U3Rj^%1+H=sTm>co+O&4X1hA28 zXj`v|v1xIc>1RbvPUOly;TMXpzJLOs=aLA6vAa(W?_z<pEa-l0=3P|ox}P~7Uhlf# zhow?MBbSGC!7KH57!!Q@HO7ck(bx+(a8}gNE-BT<fhT9JE+WfdQn@HSRy2)Mzrjkm zSQizX0_*&{NY#@OiuCLF+~PbuL@Xc>Z7Svg67lo}hofXFu(Wg!KWLT-PLa<fI7RM; z4GM9ABFmb6GpA08;NT1h4$y6BfuLr~HJ6euO0EG*oHboCBO->u7uYFK*3SY{mi-*W zi@X*uAcV!`M+VCaCJ}>*i(U8Uj)yx#Xc7-t&}3Q$eEleVxL`^F1)~rG@tYkOK}4HD zGRM_fAl#ycYDx^DQR-%}y}wi<=t3W_7+vP}0i<5Geefj~n*K%vCK=kVCquKen({>e zLiN$~Z?5OB^?Qaf<_^0R3VIQ-C=K_fW~ldp$X`aj6(UUZ*5EfXpx`qJ>o2oSp4(8k zrJ)6;7keVb*pz+Pb?oku2ZEpSv@}o74gu{~`O5DR(A1ZZx>q>kP7)e2K)w578ppNB zaTO8CWkgI%=N`!&k}8Q2D8@#<!^aQ|m`dPprBAG&>^U@*0HxjT8``I;rs$waPZ#65 zLY_zETvJI{;E)b`O~)95U`>fBgV-h@KfQ9*<AI?Om69dLphY@=h(l^2FaiPvkh9#? zYbl%%WF!uay<$Vuz^<>joea#TRwaiwM1@MBbxLZwBD$=hCLANB=Gud5#465+CXc7k zxgz}FcM3(S)v2#+Q}5@t6Km@@3s6{!Qm_}RdB)Q95Wb*MrWLG0wZ)2(hr)Q0Zo$!Y z&=tU(w*zMI5c`K*swtEtx5be&h+^y~!dUlY)vvC@B_)PBoChmxu)li~>k3-aeVXID z@fze-I1I*yRQRj7Y)AbTdQ-p6fM|n}N&>MuI`n|<zRqEi$yu4=nED;O7ZXZMI1r5% z@PxmK0PY$V=xVV?aSGzuiUY%VifaP<g6ms5>8&no#9xK&7f0tp0yDHacF6N@(ZvWS zS$NZ8EN5F-XB>IEuoS~Uz6&v|l0{vb%kw>uOq$DE1)Yx`;3gtxVeg`)OeNn-%V>`E z?42|nVtZ+zGt}7vJd(u=;RQlCQ1{u`D+0f099tM#0WCQ2)(w0yR^ea=t_t*P)<$<? z4Ad(@9AZe64dGo`X}9#*b5_8LV{BM5I^{eL7;cFPbzlbz_=`|h>NvX)cZc66%#Wcl z3y1)|2;^`<XXr^u)W8Wed8LSGFeQBp<&v#bz{eOK;%P`tAEyeUNkdZS`2$`4v8~`) z2|)-V3JzHj1ABbO-um`EFv;uRglF+LI<8n72>LV`Cl6e&zl-I&CuQ!yD+qo;SnB1C zrR_p54Vga+f3FL!Mc;;-1@#3R3x}mksb9Fg)N(#&y=k>P$;~4dxNma5p5!{8wtCsM z0z7?ewRDeu5`WmsK-P!64>r66p{l6;9$vLJmT+M3aXhE6s5Yeb#}dC$+D39aIUGBH zm0y2sC*RBaIUv=KJEQQ9$k!?G<+Kra<Hv7UopIPjC+>mSi_yDKT>w67a<}(X81&5? zfmOX37100GpP|<1(f-CG3J;EeoA6*4Y6r*oIuu8ckXSY4X&H;-guzsgPm04P&bR^? zo@48?HuA>~{^A7*2k8Bq-+SK1HehLf!J9Vaz5Y<9y(Zk!&7pSZc3azw#n%I!(P5NY zF&wP}9Jf%wloM8cj0g8RtqH@av_|}A^0rr10ZKM#)05VgkpvHI71UQ-9>}Jl_18}5 zO#@RpJ<Z|GN+}asQaoiMzB!bM=N)^n#$-%)05%f4P{tHGIr*2vdX8v%V9v=1PTaf= zC-QACTn}oX>)s58h=EpGAeLKznm9YivEd9?M&}OBC6pQ7ma}{9y(QBJ@%OfQj@oXm z)K@pLxN|(yj2AH;6rDd=%s|x)Yl$aLu}7{reJU(~s$&5FV>yWRd8eWY&v{vhW*dlt zFO+I{)9X@NAE^S`)7lxoYFIEQGY4NEjd0}GF%m*iHC4Gt$ym+UWS+_SExo$f5`nU| zDSrF(UWn%f-~8CMAvw5>jyw>ym!nah=2gONz<s;f_qwB+LvC`Fr0XC8IioKKAFzf_ z=%b=RF!JRvHrBm?DtpMPcm(_pQd2gbvAWZG=f!?!eO|hczXHcXJwv3DJBU!vAlU>t z>d~FHlcuI|mq#lZk`PTOg%81~0mrS3>Y)y}6=PfHI(ZUYkfl>R9tNpH9*Et`LhKqy zp<yE{<nCo5cf#L5?p{vZVG}z4{{Sp7o%R;s&1*f{o#H`|8bzxyq@I5eqz0vq{oq#` z6{$!INlvgKo?z;efS>qCL<RVDnX5blTr&s8$m2mj8p916&>eV}v;y2Yh+G=r6_ElK zF-lfyi2>&TxrY80Jd};xb`t{5?=g$AjaH#4>J&JsNkk}Tx%N>UbdMpXPe{ARewt}6 zYU%2L78HmV(Lr-y82iEiy8St-e}W>~Lz3$h9a?O^7GGcT7i7R%zn&V*SmGX&boKkJ z|IZn4-r|!b!iS<!PyI6l(C5F)_~Qs7=jLtoZGO>D2Cm6B1v=%-ia1zmflp4@MCwcF zn*V|o1YRP5j12?g&$(&Np@=m}>69W!0kEUk;ou*Y9Ufz3%$vCsP~h9lv=Nq@a?~H7 zeocf&M7Wm{z5Qi;+=HX~y_7mgrNhVmf&IquQ+JbVAaAGMc309Oh~#!$oa#cZhZ6-H z>U;<v<|LQCPTB5<V;(`?LespyR3;t-##>lR-CiP3Lu;W=efR1D*%AE<V!psDM>G+G zo`+yDs!|Org9K-W)O{#7xt!(;MVd*JEiOMFp=p%ONJD5_(2}T4{N)Xg0G1%T8rk=q z`_p$9o+-zP#B9iNy3@%NI50hn#9dq$PsjvTv9DR(#n(LxE~ir*g;27*nxY0I2P)#I z3bE^qd}V0PMcHSWkCplc>bUnDP_p%VGzVw+z3YV67)N>*=`OV~onvJ4XH0HJeO+qt z3IZHn!mr@X*0M;~wIs*XiteQ-)Jd5Xdi?%SkN;NMOrh@g_o+*LF>?>K(88U;KEGs# zq_{5fvXXcIXqOEfa=SDI-!X77U#v?l6XU1I`PqGDJI!1v16SRFJ_hC%>zjo>kyQT_ zi!`8qQhe9S=8KHct{4>(48!d~O^wn~5x!KF_-cdtLzX-IAe@aJ8O%vq)A$;t1&j6& z&JuC%ELydochG7##-kr@aXj=E(r?;7bc`1qiuNQtWXBMj#M7PqNw_-5N%WjtMI4Vt zUr?*K0N3hhYM4Wsb`-z`&;I6Cf6VkhVem%`ew%?%xU^0nL<S1|8-C6rfX8~GRL48$ zQBIbSx8VL;aC7dY2gh(*eGe%$CdYrIU>EU()cA%@x#9y_H-I{XLrbb6HqN(leqPSE z=!`w0zKLqp523dO>gf8`rSWi%J&VG=MxnVI#IxAHl-IG0<T&hkA`%+!JqjC{ouPM* zq|MAh=<h%c?NkRZUTHQo3o_O?jUunWJ7+aeLwB<VikulJBElhkRJ_DPMLObj%7M=1 zF7p3}5t!isz4a|!j6!-n4W}ATj@Qfd_)LPn$knXg49G0R^N{3TSL)v{3;PX8nb{e* z?97<!X>>}YIQ`aJLzaZuumtA<xse#mCL=1n^%ivln#QySk>pUKrZek6uRnRzi<fIO zy6}&0iXsbmWSjQvRT@C-uR1V=fyp4W^@$>G5m=Q}2fBRESwB&S5Q=X&-yzHjL2_u5 z{6}agny89YUhf9lV>rZ}jmb)4Ndmc%3m$NfDD?fba4XAP-@vUX>`;A`W2L1p0gPZ3 zAfi>|9BZVnn1#`p7)M|$5%-G_W)wO5{xb|j{RM+RXYlVB{Cfs}%78n3fKu`3y58Ts zq)$Bagb-qPr-l}%fAASgB$TH32ng>Y0{Tq@DC|(a{Y8gY(!_$aOkhIp&nU*od6u`H zWl9fcPmt$larpwrr1WisbqapWyb9twU&JMCX(6wk?Lki2coyagT)|U!OuKjRMUDYo zftmnIgGQw2%sQb6C@7~vb2*^fITq+YV1#fy34@>odoiIzkLZ{(&`hZzEwGCo#@GFM zQF^ll#aqLZGIW%J3$VzVFpdlODMU(NDnhIbv*vBI8>MCC_JaQ}QDl?gC!4Y$`|+B@ zQ2-y(Awq4g!csiGXDRj@+mO_hptg^8c?u{O8y_Xq0cpTjNgT2Z^C*d&ASw%dFEV5q z$S)U7YDO&~6?R0_Trz2ZJLFE#WwQyEN;%DkY1tP5t7*oBBlmHUbH;M?fwc!E-$efB z2@|b#@a`X(B>4$p;^NeF7#gs&@Xkgly|}n|`SJtK0>npyP>J%FKJn_(o0Y4VmK28~ z?q~z+BBLRn6JE2(fyjx#5!a#BtuQh+E;%(6N&a$(UBnY!LomeF9x{#_E`FLqTsQn& zkj76_Wc)o296_#}4+?7|<)T08kAdZl!s%lI?=k;?e-Q6+{u)7PGX6b?7qs=Yx(k+i zNxYgvZwM~pdJbtG6xICb#dcyTiH&MMOA{0Y_l{g(UJz1aT87uAP$Kj504KaliDF<( zU^Ep84T7PCahPtQ2GQHI>fzus)J7r0VaQFi|8H{9?IT{utK+yKGOTu7N?PT`DpP+? zk<+{v0*jk5DvDu-Ha2~q(YtgSm%;gC4FV-?`tmTW*gwDC3}EH+#C!w`QXSVC6|Tkd z_ZfH|)2Y`s@#7b?USWWs4e({bwQVyL`Du;h=44{XNoYR|Sn<R-u3@O+GK^_vIY?|& z#&CoIg1H&EdDCX_aHBPTXnH6{fZ>8*f%xkmWWaI~*Z(nzKKyB&GB*no$;gbrtngEY z=Xi=gd;*IXFB&v31)NUd&J6+p23_D{J$^w#&UHGlOo|MnK^WUKr{iDkfbXv!5IBAR z9(u?X8ft}JLV!^{zQImqt`*<kIpIz7k{P|Dr*VmBy}HbwZrK(aD2e(om<<__-_*ch zhw#OW6@KeThC!zm&^Bo9LJ%wTO$yY~yD9{72ySR=3rvjWM><SHB75U*OXy9@kGaSQ zS8zKC=1ZcG-~`tmnlx>~kaY}V0@?JvbkkBY2*G6%G8T?4nxO}W&sa3$O%7Pmt7w;4 ziJ9=MgwQc<VI#3m{X-*E!(|wHhlH>*woWc0H9?;j%=W-JjWA0X;YB=|(`X61CoM<{ zUR>4qViphrH%dCXWz*{$_>qJ*t+Vh8g>|!P=Ip^)lBkD0ug8Iz)6@xa)C$GmJ%mBX zg*1nKfS=A_Bc`UneK{aZ!~-4$_(KnZHC26k%oFhw^Oo!9(ydS+I>Mebs#>QGYcH?B zz%<+j+63`duD1Zpx?G5Vs)I8GdIQ&M^XuCuFI?i}m*kEQx?e)(yp#|Wsb~*T=o8!K zcStTT)o7vMAK!UA3wXj61o~W<f)RwA4DM>@As}VteCG+WWPRtq{~H4$U&5Ffk`?E0 zE5@Q)Oa{D5=@jN0goPfwy(%O0;;Ix43x=ObpV1~lA8d-$o3x)I2d5>=N!%s{m4l<T z2DT9~E8612L~ZIyT8ySDuV|hil*bP~p*LqNiN<&oJp@$6kNm{<B|~0Hg6PG~49nZF zlv>Wo4#N4Y`cE8M(wi2t{xj1aB)R@$a%V~#R)zusgeRk6Q<oOsy{Uykme!YHSttHO zBbs?Xib$V%(>4wNaXQah@WABhXQq2>PkZ~qBRSzsp+UATh%kW&CE@$rgT?0&STE3q zl~&;MUr<_ue~)-XLFQj`-Xex$mfe#-&6xUA=YaUfcQUv|d;q^7zH9TA`0POd3{Oa^ zqh8W)5dd{OrHGvn+$pjjq43CKYnXZT7mte5N~Y0Y(jTH5JxwD3{(UA4&k%li6$sU2 zAC<}<bEcHPEN9A@gc)@VE8IQxQ_eVPhC}&zcJ(<1y9`7ie2=ky_YxD??>{2rU$JjO z8DhArn39OtTv$G`u&*K_kf53a?NE*<#MFwb6E=1^_C1^}VAjAP1&`sKN)EAnc#g*y za$R5ql!5ox5Hb&_2Iv*(A75+WS4^NU!s8g41S0Sdrz!~F<By?V^+N_6iuyi-A29eX zgFetX@))gt7xA)Fz3)D^%f0y*oUmANV1gP0YSGdU=l_Gl;AYVT5~$M0BGMMZppOCe z;A%%HFu?(E3#6~0&R?-(wuGxNdv<JW4@f%Xf+V1S86Pomr86x$cN9^U;0N$|D?@x5 zPV`ce>nZ>pCrUYW&S){vIzdnk$g`SIa!eYs0#Bh!Bt+2V_n~aYxlfJ{<me!BYG?LN zlMVfl)8x^d1z%9w`{|xQvmhMzjts+ifCwkn=pj%~q7{N#|A+!LvGcM4@pD))$p0&f z@IwFkwo$ht+ut)R-qOG(!7s6Z3snA^<D*6oHHU=d=$<hR6it?6Xi)Tr(t{$M!2M0V z3t-L#*@HWY#_NpY$Ta$LC8Of2h7r94#O|YTJnFOzZ=aFl)X0qd#~e8^gQryYkGo$G zHyjo(<NwlS{K&!{itr_liew?mXkktEy!vlUCPtKtn)PE#(tqWfEIC2&;@9hZ{`5=@ z$0p^1F2sX*J(d5-`afc@AEEgxWc&(8PuS%?pp{H<@30Hw69;mI98QWdPHrNXRxDde zU*=!QULuJ|VJf8;w71*4?1Iaoxw|X(nrLs;$6i^+atNY49nm_tUl{5mMOv8RpB~W{ zF~7#LMFyh`M1dFAE%GpRfC151|D*&lQEU4TGe(~*b%=qO0gfPs-;d#so>jFmexy-t zt=<S4)KH?a_045??o@E|7y`gHoQCMh0NKNsBg<pM-%-J67_`DX!d}R<)dv_5f>8>W z#?%M-Sz>UW0hyazOP60zkbB^AC0VXk${kO+5-GRg<Z7AvYu4y8xX0kj41R~fw;22Z zgYO}T4!tI}2grxCN8|M={acTkjBvg7TXg)WjlL#zfdn%oZs8Y&AhPfPpy<q{9!1Fe zsF~CxQ&PF3xpQKCcnl7Sh1?XdVWjX7Vh`tX6Aw=unD|ugP~k}KW$fG22QU3!mOzV= literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/visitors.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/sql/__pycache__/visitors.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f46ff7eb6691d5b4ef125eedf755234fbf373c45 GIT binary patch literal 10260 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/sql/annotation.py b/venv/Lib/site-packages/sqlalchemy/sql/annotation.py new file mode 100644 index 0000000..c1d484d --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/annotation.py @@ -0,0 +1,206 @@ +# sql/annotation.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""The :class:`.Annotated` class and related routines; creates hash-equivalent +copies of SQL constructs which contain context-specific markers and +associations. + +""" + +from .. import util +from . import operators + + +class Annotated(object): + """clones a ClauseElement and applies an 'annotations' dictionary. + + Unlike regular clones, this clone also mimics __hash__() and + __cmp__() of the original element so that it takes its place + in hashed collections. + + A reference to the original element is maintained, for the important + reason of keeping its hash value current. When GC'ed, the + hash value may be reused, causing conflicts. + + .. note:: The rationale for Annotated producing a brand new class, + rather than placing the functionality directly within ClauseElement, + is **performance**. The __hash__() method is absent on plain + ClauseElement which leads to significantly reduced function call + overhead, as the use of sets and dictionaries against ClauseElement + objects is prevalent, but most are not "annotated". + + """ + + def __new__(cls, *args): + if not args: + # clone constructor + return object.__new__(cls) + else: + element, values = args + # pull appropriate subclass from registry of annotated + # classes + try: + cls = annotated_classes[element.__class__] + except KeyError: + cls = _new_annotation_type(element.__class__, cls) + return object.__new__(cls) + + def __init__(self, element, values): + self.__dict__ = element.__dict__.copy() + self.__element = element + self._annotations = values + self._hash = hash(element) + + def _annotate(self, values): + _values = self._annotations.copy() + _values.update(values) + return self._with_annotations(_values) + + def _with_annotations(self, values): + clone = self.__class__.__new__(self.__class__) + clone.__dict__ = self.__dict__.copy() + clone._annotations = values + return clone + + def _deannotate(self, values=None, clone=True): + if values is None: + return self.__element + else: + _values = self._annotations.copy() + for v in values: + _values.pop(v, None) + return self._with_annotations(_values) + + def _compiler_dispatch(self, visitor, **kw): + return self.__element.__class__._compiler_dispatch( + self, visitor, **kw) + + @property + def _constructor(self): + return self.__element._constructor + + def _clone(self): + clone = self.__element._clone() + if clone is self.__element: + # detect immutable, don't change anything + return self + else: + # update the clone with any changes that have occurred + # to this object's __dict__. + clone.__dict__.update(self.__dict__) + return self.__class__(clone, self._annotations) + + def __reduce__(self): + return self.__class__, (self.__element, self._annotations) + + def __hash__(self): + return self._hash + + def __eq__(self, other): + if isinstance(self.__element, operators.ColumnOperators): + return self.__element.__class__.__eq__(self, other) + else: + return hash(other) == hash(self) + + +# hard-generate Annotated subclasses. this technique +# is used instead of on-the-fly types (i.e. type.__new__()) +# so that the resulting objects are pickleable. +annotated_classes = {} + + +def _deep_annotate(element, annotations, exclude=None): + """Deep copy the given ClauseElement, annotating each element + with the given annotations dictionary. + + Elements within the exclude collection will be cloned but not annotated. + + """ + def clone(elem): + if exclude and \ + hasattr(elem, 'proxy_set') and \ + elem.proxy_set.intersection(exclude): + newelem = elem._clone() + elif annotations != elem._annotations: + newelem = elem._annotate(annotations) + else: + newelem = elem + newelem._copy_internals(clone=clone) + return newelem + + if element is not None: + element = clone(element) + return element + + +def _deep_deannotate(element, values=None): + """Deep copy the given element, removing annotations.""" + + cloned = util.column_dict() + + def clone(elem): + # if a values dict is given, + # the elem must be cloned each time it appears, + # as there may be different annotations in source + # elements that are remaining. if totally + # removing all annotations, can assume the same + # slate... + if values or elem not in cloned: + newelem = elem._deannotate(values=values, clone=True) + newelem._copy_internals(clone=clone) + if not values: + cloned[elem] = newelem + return newelem + else: + return cloned[elem] + + if element is not None: + element = clone(element) + return element + + +def _shallow_annotate(element, annotations): + """Annotate the given ClauseElement and copy its internals so that + internal objects refer to the new annotated object. + + Basically used to apply a "dont traverse" annotation to a + selectable, without digging throughout the whole + structure wasting time. + """ + element = element._annotate(annotations) + element._copy_internals() + return element + + +def _new_annotation_type(cls, base_cls): + if issubclass(cls, Annotated): + return cls + elif cls in annotated_classes: + return annotated_classes[cls] + + for super_ in cls.__mro__: + # check if an Annotated subclass more specific than + # the given base_cls is already registered, such + # as AnnotatedColumnElement. + if super_ in annotated_classes: + base_cls = annotated_classes[super_] + break + + annotated_classes[cls] = anno_cls = type( + "Annotated%s" % cls.__name__, + (base_cls, cls), {}) + globals()["Annotated%s" % cls.__name__] = anno_cls + return anno_cls + + +def _prepare_annotations(target_hierarchy, base_cls): + stack = [target_hierarchy] + while stack: + cls = stack.pop() + stack.extend(cls.__subclasses__()) + + _new_annotation_type(cls, base_cls) diff --git a/venv/Lib/site-packages/sqlalchemy/sql/base.py b/venv/Lib/site-packages/sqlalchemy/sql/base.py new file mode 100644 index 0000000..6b9b557 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/base.py @@ -0,0 +1,636 @@ +# sql/base.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Foundational utilities common to many sql modules. + +""" + + +from .. import util, exc +import itertools +from .visitors import ClauseVisitor +import re + +PARSE_AUTOCOMMIT = util.symbol('PARSE_AUTOCOMMIT') +NO_ARG = util.symbol('NO_ARG') + + +class Immutable(object): + """mark a ClauseElement as 'immutable' when expressions are cloned.""" + + def unique_params(self, *optionaldict, **kwargs): + raise NotImplementedError("Immutable objects do not support copying") + + def params(self, *optionaldict, **kwargs): + raise NotImplementedError("Immutable objects do not support copying") + + def _clone(self): + return self + + +def _from_objects(*elements): + return itertools.chain(*[element._from_objects for element in elements]) + + +@util.decorator +def _generative(fn, *args, **kw): + """Mark a method as generative.""" + + self = args[0]._generate() + fn(self, *args[1:], **kw) + return self + + +class _DialectArgView(util.collections_abc.MutableMapping): + """A dictionary view of dialect-level arguments in the form + <dialectname>_<argument_name>. + + """ + + def __init__(self, obj): + self.obj = obj + + def _key(self, key): + try: + dialect, value_key = key.split("_", 1) + except ValueError: + raise KeyError(key) + else: + return dialect, value_key + + def __getitem__(self, key): + dialect, value_key = self._key(key) + + try: + opt = self.obj.dialect_options[dialect] + except exc.NoSuchModuleError: + raise KeyError(key) + else: + return opt[value_key] + + def __setitem__(self, key, value): + try: + dialect, value_key = self._key(key) + except KeyError: + raise exc.ArgumentError( + "Keys must be of the form <dialectname>_<argname>") + else: + self.obj.dialect_options[dialect][value_key] = value + + def __delitem__(self, key): + dialect, value_key = self._key(key) + del self.obj.dialect_options[dialect][value_key] + + def __len__(self): + return sum(len(args._non_defaults) for args in + self.obj.dialect_options.values()) + + def __iter__(self): + return ( + util.safe_kwarg("%s_%s" % (dialect_name, value_name)) + for dialect_name in self.obj.dialect_options + for value_name in + self.obj.dialect_options[dialect_name]._non_defaults + ) + + +class _DialectArgDict(util.collections_abc.MutableMapping): + """A dictionary view of dialect-level arguments for a specific + dialect. + + Maintains a separate collection of user-specified arguments + and dialect-specified default arguments. + + """ + + def __init__(self): + self._non_defaults = {} + self._defaults = {} + + def __len__(self): + return len(set(self._non_defaults).union(self._defaults)) + + def __iter__(self): + return iter(set(self._non_defaults).union(self._defaults)) + + def __getitem__(self, key): + if key in self._non_defaults: + return self._non_defaults[key] + else: + return self._defaults[key] + + def __setitem__(self, key, value): + self._non_defaults[key] = value + + def __delitem__(self, key): + del self._non_defaults[key] + + +class DialectKWArgs(object): + """Establish the ability for a class to have dialect-specific arguments + with defaults and constructor validation. + + The :class:`.DialectKWArgs` interacts with the + :attr:`.DefaultDialect.construct_arguments` present on a dialect. + + .. seealso:: + + :attr:`.DefaultDialect.construct_arguments` + + """ + + @classmethod + def argument_for(cls, dialect_name, argument_name, default): + """Add a new kind of dialect-specific keyword argument for this class. + + E.g.:: + + Index.argument_for("mydialect", "length", None) + + some_index = Index('a', 'b', mydialect_length=5) + + The :meth:`.DialectKWArgs.argument_for` method is a per-argument + way adding extra arguments to the + :attr:`.DefaultDialect.construct_arguments` dictionary. This + dictionary provides a list of argument names accepted by various + schema-level constructs on behalf of a dialect. + + New dialects should typically specify this dictionary all at once as a + data member of the dialect class. The use case for ad-hoc addition of + argument names is typically for end-user code that is also using + a custom compilation scheme which consumes the additional arguments. + + :param dialect_name: name of a dialect. The dialect must be + locatable, else a :class:`.NoSuchModuleError` is raised. The + dialect must also include an existing + :attr:`.DefaultDialect.construct_arguments` collection, indicating + that it participates in the keyword-argument validation and default + system, else :class:`.ArgumentError` is raised. If the dialect does + not include this collection, then any keyword argument can be + specified on behalf of this dialect already. All dialects packaged + within SQLAlchemy include this collection, however for third party + dialects, support may vary. + + :param argument_name: name of the parameter. + + :param default: default value of the parameter. + + .. versionadded:: 0.9.4 + + """ + + construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name] + if construct_arg_dictionary is None: + raise exc.ArgumentError( + "Dialect '%s' does have keyword-argument " + "validation and defaults enabled configured" % + dialect_name) + if cls not in construct_arg_dictionary: + construct_arg_dictionary[cls] = {} + construct_arg_dictionary[cls][argument_name] = default + + @util.memoized_property + def dialect_kwargs(self): + """A collection of keyword arguments specified as dialect-specific + options to this construct. + + The arguments are present here in their original ``<dialect>_<kwarg>`` + format. Only arguments that were actually passed are included; + unlike the :attr:`.DialectKWArgs.dialect_options` collection, which + contains all options known by this dialect including defaults. + + The collection is also writable; keys are accepted of the + form ``<dialect>_<kwarg>`` where the value will be assembled + into the list of options. + + .. versionadded:: 0.9.2 + + .. versionchanged:: 0.9.4 The :attr:`.DialectKWArgs.dialect_kwargs` + collection is now writable. + + .. seealso:: + + :attr:`.DialectKWArgs.dialect_options` - nested dictionary form + + """ + return _DialectArgView(self) + + @property + def kwargs(self): + """A synonym for :attr:`.DialectKWArgs.dialect_kwargs`.""" + return self.dialect_kwargs + + @util.dependencies("sqlalchemy.dialects") + def _kw_reg_for_dialect(dialects, dialect_name): + dialect_cls = dialects.registry.load(dialect_name) + if dialect_cls.construct_arguments is None: + return None + return dict(dialect_cls.construct_arguments) + _kw_registry = util.PopulateDict(_kw_reg_for_dialect) + + def _kw_reg_for_dialect_cls(self, dialect_name): + construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name] + d = _DialectArgDict() + + if construct_arg_dictionary is None: + d._defaults.update({"*": None}) + else: + for cls in reversed(self.__class__.__mro__): + if cls in construct_arg_dictionary: + d._defaults.update(construct_arg_dictionary[cls]) + return d + + @util.memoized_property + def dialect_options(self): + """A collection of keyword arguments specified as dialect-specific + options to this construct. + + This is a two-level nested registry, keyed to ``<dialect_name>`` + and ``<argument_name>``. For example, the ``postgresql_where`` + argument would be locatable as:: + + arg = my_object.dialect_options['postgresql']['where'] + + .. versionadded:: 0.9.2 + + .. seealso:: + + :attr:`.DialectKWArgs.dialect_kwargs` - flat dictionary form + + """ + + return util.PopulateDict( + util.portable_instancemethod(self._kw_reg_for_dialect_cls) + ) + + def _validate_dialect_kwargs(self, kwargs): + # validate remaining kwargs that they all specify DB prefixes + + if not kwargs: + return + + for k in kwargs: + m = re.match('^(.+?)_(.+)$', k) + if not m: + raise TypeError( + "Additional arguments should be " + "named <dialectname>_<argument>, got '%s'" % k) + dialect_name, arg_name = m.group(1, 2) + + try: + construct_arg_dictionary = self.dialect_options[dialect_name] + except exc.NoSuchModuleError: + util.warn( + "Can't validate argument %r; can't " + "locate any SQLAlchemy dialect named %r" % + (k, dialect_name)) + self.dialect_options[dialect_name] = d = _DialectArgDict() + d._defaults.update({"*": None}) + d._non_defaults[arg_name] = kwargs[k] + else: + if "*" not in construct_arg_dictionary and \ + arg_name not in construct_arg_dictionary: + raise exc.ArgumentError( + "Argument %r is not accepted by " + "dialect %r on behalf of %r" % ( + k, + dialect_name, self.__class__ + )) + else: + construct_arg_dictionary[arg_name] = kwargs[k] + + +class Generative(object): + """Allow a ClauseElement to generate itself via the + @_generative decorator. + + """ + + def _generate(self): + s = self.__class__.__new__(self.__class__) + s.__dict__ = self.__dict__.copy() + return s + + +class Executable(Generative): + """Mark a ClauseElement as supporting execution. + + :class:`.Executable` is a superclass for all "statement" types + of objects, including :func:`select`, :func:`delete`, :func:`update`, + :func:`insert`, :func:`text`. + + """ + + supports_execution = True + _execution_options = util.immutabledict() + _bind = None + + @_generative + def execution_options(self, **kw): + """ Set non-SQL options for the statement which take effect during + execution. + + Execution options can be set on a per-statement or + per :class:`.Connection` basis. Additionally, the + :class:`.Engine` and ORM :class:`~.orm.query.Query` objects provide + access to execution options which they in turn configure upon + connections. + + The :meth:`execution_options` method is generative. A new + instance of this statement is returned that contains the options:: + + statement = select([table.c.x, table.c.y]) + statement = statement.execution_options(autocommit=True) + + Note that only a subset of possible execution options can be applied + to a statement - these include "autocommit" and "stream_results", + but not "isolation_level" or "compiled_cache". + See :meth:`.Connection.execution_options` for a full list of + possible options. + + .. seealso:: + + :meth:`.Connection.execution_options()` + + :meth:`.Query.execution_options()` + + """ + if 'isolation_level' in kw: + raise exc.ArgumentError( + "'isolation_level' execution option may only be specified " + "on Connection.execution_options(), or " + "per-engine using the isolation_level " + "argument to create_engine()." + ) + if 'compiled_cache' in kw: + raise exc.ArgumentError( + "'compiled_cache' execution option may only be specified " + "on Connection.execution_options(), not per statement." + ) + self._execution_options = self._execution_options.union(kw) + + def execute(self, *multiparams, **params): + """Compile and execute this :class:`.Executable`.""" + e = self.bind + if e is None: + label = getattr(self, 'description', self.__class__.__name__) + msg = ('This %s is not directly bound to a Connection or Engine. ' + 'Use the .execute() method of a Connection or Engine ' + 'to execute this construct.' % label) + raise exc.UnboundExecutionError(msg) + return e._execute_clauseelement(self, multiparams, params) + + def scalar(self, *multiparams, **params): + """Compile and execute this :class:`.Executable`, returning the + result's scalar representation. + + """ + return self.execute(*multiparams, **params).scalar() + + @property + def bind(self): + """Returns the :class:`.Engine` or :class:`.Connection` to + which this :class:`.Executable` is bound, or None if none found. + + This is a traversal which checks locally, then + checks among the "from" clauses of associated objects + until a bound engine or connection is found. + + """ + if self._bind is not None: + return self._bind + + for f in _from_objects(self): + if f is self: + continue + engine = f.bind + if engine is not None: + return engine + else: + return None + + +class SchemaEventTarget(object): + """Base class for elements that are the targets of :class:`.DDLEvents` + events. + + This includes :class:`.SchemaItem` as well as :class:`.SchemaType`. + + """ + + def _set_parent(self, parent): + """Associate with this SchemaEvent's parent object.""" + + def _set_parent_with_dispatch(self, parent): + self.dispatch.before_parent_attach(self, parent) + self._set_parent(parent) + self.dispatch.after_parent_attach(self, parent) + + +class SchemaVisitor(ClauseVisitor): + """Define the visiting for ``SchemaItem`` objects.""" + + __traverse_options__ = {'schema_visitor': True} + + +class ColumnCollection(util.OrderedProperties): + """An ordered dictionary that stores a list of ColumnElement + instances. + + Overrides the ``__eq__()`` method to produce SQL clauses between + sets of correlated columns. + + """ + + __slots__ = '_all_columns' + + def __init__(self, *columns): + super(ColumnCollection, self).__init__() + object.__setattr__(self, '_all_columns', []) + for c in columns: + self.add(c) + + def __str__(self): + return repr([str(c) for c in self]) + + def replace(self, column): + """add the given column to this collection, removing unaliased + versions of this column as well as existing columns with the + same key. + + e.g.:: + + t = Table('sometable', metadata, Column('col1', Integer)) + t.columns.replace(Column('col1', Integer, key='columnone')) + + will remove the original 'col1' from the collection, and add + the new column under the name 'columnname'. + + Used by schema.Column to override columns during table reflection. + + """ + remove_col = None + if column.name in self and column.key != column.name: + other = self[column.name] + if other.name == other.key: + remove_col = other + del self._data[other.key] + + if column.key in self._data: + remove_col = self._data[column.key] + + self._data[column.key] = column + if remove_col is not None: + self._all_columns[:] = [column if c is remove_col + else c for c in self._all_columns] + else: + self._all_columns.append(column) + + def add(self, column): + """Add a column to this collection. + + The key attribute of the column will be used as the hash key + for this dictionary. + + """ + if not column.key: + raise exc.ArgumentError( + "Can't add unnamed column to column collection") + self[column.key] = column + + def __delitem__(self, key): + raise NotImplementedError() + + def __setattr__(self, key, object): + raise NotImplementedError() + + def __setitem__(self, key, value): + if key in self: + + # this warning is primarily to catch select() statements + # which have conflicting column names in their exported + # columns collection + + existing = self[key] + + if existing is value: + return + + if not existing.shares_lineage(value): + util.warn('Column %r on table %r being replaced by ' + '%r, which has the same key. Consider ' + 'use_labels for select() statements.' % + (key, getattr(existing, 'table', None), value)) + + # pop out memoized proxy_set as this + # operation may very well be occurring + # in a _make_proxy operation + util.memoized_property.reset(value, "proxy_set") + + self._all_columns.append(value) + self._data[key] = value + + def clear(self): + raise NotImplementedError() + + def remove(self, column): + del self._data[column.key] + self._all_columns[:] = [ + c for c in self._all_columns if c is not column] + + def update(self, iter): + cols = list(iter) + all_col_set = set(self._all_columns) + self._all_columns.extend( + c for label, c in cols if c not in all_col_set) + self._data.update((label, c) for label, c in cols) + + def extend(self, iter): + cols = list(iter) + all_col_set = set(self._all_columns) + self._all_columns.extend(c for c in cols if c not in all_col_set) + self._data.update((c.key, c) for c in cols) + + __hash__ = None + + @util.dependencies("sqlalchemy.sql.elements") + def __eq__(self, elements, other): + l = [] + for c in getattr(other, "_all_columns", other): + for local in self._all_columns: + if c.shares_lineage(local): + l.append(c == local) + return elements.and_(*l) + + def __contains__(self, other): + if not isinstance(other, util.string_types): + raise exc.ArgumentError("__contains__ requires a string argument") + return util.OrderedProperties.__contains__(self, other) + + def __getstate__(self): + return {'_data': self._data, + '_all_columns': self._all_columns} + + def __setstate__(self, state): + object.__setattr__(self, '_data', state['_data']) + object.__setattr__(self, '_all_columns', state['_all_columns']) + + def contains_column(self, col): + return col in set(self._all_columns) + + def as_immutable(self): + return ImmutableColumnCollection(self._data, self._all_columns) + + +class ImmutableColumnCollection(util.ImmutableProperties, ColumnCollection): + def __init__(self, data, all_columns): + util.ImmutableProperties.__init__(self, data) + object.__setattr__(self, '_all_columns', all_columns) + + extend = remove = util.ImmutableProperties._immutable + + +class ColumnSet(util.ordered_column_set): + def contains_column(self, col): + return col in self + + def extend(self, cols): + for col in cols: + self.add(col) + + def __add__(self, other): + return list(self) + list(other) + + @util.dependencies("sqlalchemy.sql.elements") + def __eq__(self, elements, other): + l = [] + for c in other: + for local in self: + if c.shares_lineage(local): + l.append(c == local) + return elements.and_(*l) + + def __hash__(self): + return hash(tuple(x for x in self)) + + +def _bind_or_error(schemaitem, msg=None): + bind = schemaitem.bind + if not bind: + name = schemaitem.__class__.__name__ + label = getattr(schemaitem, 'fullname', + getattr(schemaitem, 'name', None)) + if label: + item = '%s object %r' % (name, label) + else: + item = '%s object' % name + if msg is None: + msg = "%s is not bound to an Engine or Connection. "\ + "Execution can not proceed without a database to execute "\ + "against." % item + raise exc.UnboundExecutionError(msg) + return bind diff --git a/venv/Lib/site-packages/sqlalchemy/sql/compiler.py b/venv/Lib/site-packages/sqlalchemy/sql/compiler.py new file mode 100644 index 0000000..e27a736 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/compiler.py @@ -0,0 +1,3232 @@ +# sql/compiler.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Base SQL and DDL compiler implementations. + +Classes provided include: + +:class:`.compiler.SQLCompiler` - renders SQL +strings + +:class:`.compiler.DDLCompiler` - renders DDL +(data definition language) strings + +:class:`.compiler.GenericTypeCompiler` - renders +type specification strings. + +To generate user-defined SQL strings, see +:doc:`/ext/compiler`. + +""" + +import contextlib +import re +from . import schema, sqltypes, operators, functions, visitors, \ + elements, selectable, crud +from .. import util, exc +import itertools + +RESERVED_WORDS = set([ + 'all', 'analyse', 'analyze', 'and', 'any', 'array', + 'as', 'asc', 'asymmetric', 'authorization', 'between', + 'binary', 'both', 'case', 'cast', 'check', 'collate', + 'column', 'constraint', 'create', 'cross', 'current_date', + 'current_role', 'current_time', 'current_timestamp', + 'current_user', 'default', 'deferrable', 'desc', + 'distinct', 'do', 'else', 'end', 'except', 'false', + 'for', 'foreign', 'freeze', 'from', 'full', 'grant', + 'group', 'having', 'ilike', 'in', 'initially', 'inner', + 'intersect', 'into', 'is', 'isnull', 'join', 'leading', + 'left', 'like', 'limit', 'localtime', 'localtimestamp', + 'natural', 'new', 'not', 'notnull', 'null', 'off', 'offset', + 'old', 'on', 'only', 'or', 'order', 'outer', 'overlaps', + 'placing', 'primary', 'references', 'right', 'select', + 'session_user', 'set', 'similar', 'some', 'symmetric', 'table', + 'then', 'to', 'trailing', 'true', 'union', 'unique', 'user', + 'using', 'verbose', 'when', 'where']) + +LEGAL_CHARACTERS = re.compile(r'^[A-Z0-9_$]+$', re.I) +ILLEGAL_INITIAL_CHARACTERS = {str(x) for x in range(0, 10)}.union(['$']) + +BIND_PARAMS = re.compile(r'(?<![:\w\$\x5c]):([\w\$]+)(?![:\w\$])', re.UNICODE) +BIND_PARAMS_ESC = re.compile(r'\x5c(:[\w\$]*)(?![:\w\$])', re.UNICODE) + +BIND_TEMPLATES = { + 'pyformat': "%%(%(name)s)s", + 'qmark': "?", + 'format': "%%s", + 'numeric': ":[_POSITION]", + 'named': ":%(name)s" +} + + +OPERATORS = { + # binary + operators.and_: ' AND ', + operators.or_: ' OR ', + operators.add: ' + ', + operators.mul: ' * ', + operators.sub: ' - ', + operators.div: ' / ', + operators.mod: ' % ', + operators.truediv: ' / ', + operators.neg: '-', + operators.lt: ' < ', + operators.le: ' <= ', + operators.ne: ' != ', + operators.gt: ' > ', + operators.ge: ' >= ', + operators.eq: ' = ', + operators.is_distinct_from: ' IS DISTINCT FROM ', + operators.isnot_distinct_from: ' IS NOT DISTINCT FROM ', + operators.concat_op: ' || ', + operators.match_op: ' MATCH ', + operators.notmatch_op: ' NOT MATCH ', + operators.in_op: ' IN ', + operators.notin_op: ' NOT IN ', + operators.comma_op: ', ', + operators.from_: ' FROM ', + operators.as_: ' AS ', + operators.is_: ' IS ', + operators.isnot: ' IS NOT ', + operators.collate: ' COLLATE ', + + # unary + operators.exists: 'EXISTS ', + operators.distinct_op: 'DISTINCT ', + operators.inv: 'NOT ', + operators.any_op: 'ANY ', + operators.all_op: 'ALL ', + + # modifiers + operators.desc_op: ' DESC', + operators.asc_op: ' ASC', + operators.nullsfirst_op: ' NULLS FIRST', + operators.nullslast_op: ' NULLS LAST', + +} + +FUNCTIONS = { + functions.coalesce: 'coalesce%(expr)s', + functions.current_date: 'CURRENT_DATE', + functions.current_time: 'CURRENT_TIME', + functions.current_timestamp: 'CURRENT_TIMESTAMP', + functions.current_user: 'CURRENT_USER', + functions.localtime: 'LOCALTIME', + functions.localtimestamp: 'LOCALTIMESTAMP', + functions.random: 'random%(expr)s', + functions.sysdate: 'sysdate', + functions.session_user: 'SESSION_USER', + functions.user: 'USER', + functions.cube: 'CUBE%(expr)s', + functions.rollup: 'ROLLUP%(expr)s', + functions.grouping_sets: 'GROUPING SETS%(expr)s', +} + +EXTRACT_MAP = { + 'month': 'month', + 'day': 'day', + 'year': 'year', + 'second': 'second', + 'hour': 'hour', + 'doy': 'doy', + 'minute': 'minute', + 'quarter': 'quarter', + 'dow': 'dow', + 'week': 'week', + 'epoch': 'epoch', + 'milliseconds': 'milliseconds', + 'microseconds': 'microseconds', + 'timezone_hour': 'timezone_hour', + 'timezone_minute': 'timezone_minute' +} + +COMPOUND_KEYWORDS = { + selectable.CompoundSelect.UNION: 'UNION', + selectable.CompoundSelect.UNION_ALL: 'UNION ALL', + selectable.CompoundSelect.EXCEPT: 'EXCEPT', + selectable.CompoundSelect.EXCEPT_ALL: 'EXCEPT ALL', + selectable.CompoundSelect.INTERSECT: 'INTERSECT', + selectable.CompoundSelect.INTERSECT_ALL: 'INTERSECT ALL' +} + + +class Compiled(object): + + """Represent a compiled SQL or DDL expression. + + The ``__str__`` method of the ``Compiled`` object should produce + the actual text of the statement. ``Compiled`` objects are + specific to their underlying database dialect, and also may + or may not be specific to the columns referenced within a + particular set of bind parameters. In no case should the + ``Compiled`` object be dependent on the actual values of those + bind parameters, even though it may reference those values as + defaults. + """ + + _cached_metadata = None + + execution_options = util.immutabledict() + """ + Execution options propagated from the statement. In some cases, + sub-elements of the statement can modify these. + """ + + def __init__(self, dialect, statement, bind=None, + schema_translate_map=None, + compile_kwargs=util.immutabledict()): + """Construct a new :class:`.Compiled` object. + + :param dialect: :class:`.Dialect` to compile against. + + :param statement: :class:`.ClauseElement` to be compiled. + + :param bind: Optional Engine or Connection to compile this + statement against. + + :param schema_translate_map: dictionary of schema names to be + translated when forming the resultant SQL + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`schema_translating` + + :param compile_kwargs: additional kwargs that will be + passed to the initial call to :meth:`.Compiled.process`. + + + """ + + self.dialect = dialect + self.bind = bind + self.preparer = self.dialect.identifier_preparer + if schema_translate_map: + self.preparer = self.preparer._with_schema_translate( + schema_translate_map) + + if statement is not None: + self.statement = statement + self.can_execute = statement.supports_execution + if self.can_execute: + self.execution_options = statement._execution_options + self.string = self.process(self.statement, **compile_kwargs) + + @util.deprecated("0.7", ":class:`.Compiled` objects now compile " + "within the constructor.") + def compile(self): + """Produce the internal string representation of this element. + """ + pass + + def _execute_on_connection(self, connection, multiparams, params): + if self.can_execute: + return connection._execute_compiled(self, multiparams, params) + else: + raise exc.ObjectNotExecutableError(self.statement) + + @property + def sql_compiler(self): + """Return a Compiled that is capable of processing SQL expressions. + + If this compiler is one, it would likely just return 'self'. + + """ + + raise NotImplementedError() + + def process(self, obj, **kwargs): + return obj._compiler_dispatch(self, **kwargs) + + def __str__(self): + """Return the string text of the generated SQL or DDL.""" + + return self.string or '' + + def construct_params(self, params=None): + """Return the bind params for this compiled object. + + :param params: a dict of string/object pairs whose values will + override bind values compiled in to the + statement. + """ + + raise NotImplementedError() + + @property + def params(self): + """Return the bind params for this compiled object.""" + return self.construct_params() + + def execute(self, *multiparams, **params): + """Execute this compiled object.""" + + e = self.bind + if e is None: + raise exc.UnboundExecutionError( + "This Compiled object is not bound to any Engine " + "or Connection.", code="2afi") + return e._execute_compiled(self, multiparams, params) + + def scalar(self, *multiparams, **params): + """Execute this compiled object and return the result's + scalar value.""" + + return self.execute(*multiparams, **params).scalar() + + +class TypeCompiler(util.with_metaclass(util.EnsureKWArgType, object)): + """Produces DDL specification for TypeEngine objects.""" + + ensure_kwarg = r'visit_\w+' + + def __init__(self, dialect): + self.dialect = dialect + + def process(self, type_, **kw): + return type_._compiler_dispatch(self, **kw) + + +class _CompileLabel(visitors.Visitable): + + """lightweight label object which acts as an expression.Label.""" + + __visit_name__ = 'label' + __slots__ = 'element', 'name' + + def __init__(self, col, name, alt_names=()): + self.element = col + self.name = name + self._alt_names = (col,) + alt_names + + @property + def proxy_set(self): + return self.element.proxy_set + + @property + def type(self): + return self.element.type + + def self_group(self, **kw): + return self + + +class SQLCompiler(Compiled): + """Default implementation of :class:`.Compiled`. + + Compiles :class:`.ClauseElement` objects into SQL strings. + + """ + + extract_map = EXTRACT_MAP + + compound_keywords = COMPOUND_KEYWORDS + + isdelete = isinsert = isupdate = False + """class-level defaults which can be set at the instance + level to define if this Compiled instance represents + INSERT/UPDATE/DELETE + """ + + isplaintext = False + + returning = None + """holds the "returning" collection of columns if + the statement is CRUD and defines returning columns + either implicitly or explicitly + """ + + returning_precedes_values = False + """set to True classwide to generate RETURNING + clauses before the VALUES or WHERE clause (i.e. MSSQL) + """ + + render_table_with_column_in_update_from = False + """set to True classwide to indicate the SET clause + in a multi-table UPDATE statement should qualify + columns with the table name (i.e. MySQL only) + """ + + contains_expanding_parameters = False + """True if we've encountered bindparam(..., expanding=True). + + These need to be converted before execution time against the + string statement. + + """ + + ansi_bind_rules = False + """SQL 92 doesn't allow bind parameters to be used + in the columns clause of a SELECT, nor does it allow + ambiguous expressions like "? = ?". A compiler + subclass can set this flag to False if the target + driver/DB enforces this + """ + + _textual_ordered_columns = False + """tell the result object that the column names as rendered are important, + but they are also "ordered" vs. what is in the compiled object here. + """ + + _ordered_columns = True + """ + if False, means we can't be sure the list of entries + in _result_columns is actually the rendered order. Usually + True unless using an unordered TextAsFrom. + """ + + _numeric_binds = False + """ + True if paramstyle is "numeric". This paramstyle is trickier than + all the others. + + """ + + insert_prefetch = update_prefetch = () + + def __init__(self, dialect, statement, column_keys=None, + inline=False, **kwargs): + """Construct a new :class:`.SQLCompiler` object. + + :param dialect: :class:`.Dialect` to be used + + :param statement: :class:`.ClauseElement` to be compiled + + :param column_keys: a list of column names to be compiled into an + INSERT or UPDATE statement. + + :param inline: whether to generate INSERT statements as "inline", e.g. + not formatted to return any generated defaults + + :param kwargs: additional keyword arguments to be consumed by the + superclass. + + """ + self.column_keys = column_keys + + # compile INSERT/UPDATE defaults/sequences inlined (no pre- + # execute) + self.inline = inline or getattr(statement, 'inline', False) + + # a dictionary of bind parameter keys to BindParameter + # instances. + self.binds = {} + + # a dictionary of BindParameter instances to "compiled" names + # that are actually present in the generated SQL + self.bind_names = util.column_dict() + + # stack which keeps track of nested SELECT statements + self.stack = [] + + # relates label names in the final SQL to a tuple of local + # column/label name, ColumnElement object (if any) and + # TypeEngine. ResultProxy uses this for type processing and + # column targeting + self._result_columns = [] + + # true if the paramstyle is positional + self.positional = dialect.positional + if self.positional: + self.positiontup = [] + self._numeric_binds = dialect.paramstyle == "numeric" + self.bindtemplate = BIND_TEMPLATES[dialect.paramstyle] + + self.ctes = None + + self.label_length = dialect.label_length \ + or dialect.max_identifier_length + + # a map which tracks "anonymous" identifiers that are created on + # the fly here + self.anon_map = util.PopulateDict(self._process_anon) + + # a map which tracks "truncated" names based on + # dialect.label_length or dialect.max_identifier_length + self.truncated_names = {} + Compiled.__init__(self, dialect, statement, **kwargs) + + if ( + self.isinsert or self.isupdate or self.isdelete + ) and statement._returning: + self.returning = statement._returning + + if self.positional and self._numeric_binds: + self._apply_numbered_params() + + @property + def prefetch(self): + return list(self.insert_prefetch + self.update_prefetch) + + @util.memoized_instancemethod + def _init_cte_state(self): + """Initialize collections related to CTEs only if + a CTE is located, to save on the overhead of + these collections otherwise. + + """ + # collect CTEs to tack on top of a SELECT + self.ctes = util.OrderedDict() + self.ctes_by_name = {} + self.ctes_recursive = False + if self.positional: + self.cte_positional = {} + + @contextlib.contextmanager + def _nested_result(self): + """special API to support the use case of 'nested result sets'""" + result_columns, ordered_columns = ( + self._result_columns, self._ordered_columns) + self._result_columns, self._ordered_columns = [], False + + try: + if self.stack: + entry = self.stack[-1] + entry['need_result_map_for_nested'] = True + else: + entry = None + yield self._result_columns, self._ordered_columns + finally: + if entry: + entry.pop('need_result_map_for_nested') + self._result_columns, self._ordered_columns = ( + result_columns, ordered_columns) + + def _apply_numbered_params(self): + poscount = itertools.count(1) + self.string = re.sub( + r'\[_POSITION\]', + lambda m: str(util.next(poscount)), + self.string) + + @util.memoized_property + def _bind_processors(self): + return dict( + (key, value) for key, value in + ((self.bind_names[bindparam], + bindparam.type._cached_bind_processor(self.dialect) + ) + for bindparam in self.bind_names) + if value is not None + ) + + def is_subquery(self): + return len(self.stack) > 1 + + @property + def sql_compiler(self): + return self + + def construct_params(self, params=None, _group_number=None, _check=True): + """return a dictionary of bind parameter keys and values""" + + if params: + pd = {} + for bindparam in self.bind_names: + name = self.bind_names[bindparam] + if bindparam.key in params: + pd[name] = params[bindparam.key] + elif name in params: + pd[name] = params[name] + + elif _check and bindparam.required: + if _group_number: + raise exc.InvalidRequestError( + "A value is required for bind parameter %r, " + "in parameter group %d" % + (bindparam.key, _group_number), code="cd3x") + else: + raise exc.InvalidRequestError( + "A value is required for bind parameter %r" + % bindparam.key, code="cd3x") + + elif bindparam.callable: + pd[name] = bindparam.effective_value + else: + pd[name] = bindparam.value + return pd + else: + pd = {} + for bindparam in self.bind_names: + if _check and bindparam.required: + if _group_number: + raise exc.InvalidRequestError( + "A value is required for bind parameter %r, " + "in parameter group %d" % + (bindparam.key, _group_number), code="cd3x") + else: + raise exc.InvalidRequestError( + "A value is required for bind parameter %r" + % bindparam.key, code="cd3x") + + if bindparam.callable: + pd[self.bind_names[bindparam]] = bindparam.effective_value + else: + pd[self.bind_names[bindparam]] = bindparam.value + return pd + + @property + def params(self): + """Return the bind param dictionary embedded into this + compiled object, for those values that are present.""" + return self.construct_params(_check=False) + + @util.dependencies("sqlalchemy.engine.result") + def _create_result_map(self, result): + """utility method used for unit tests only.""" + return result.ResultMetaData._create_result_map(self._result_columns) + + def default_from(self): + """Called when a SELECT statement has no froms, and no FROM clause is + to be appended. + + Gives Oracle a chance to tack on a ``FROM DUAL`` to the string output. + + """ + return "" + + def visit_grouping(self, grouping, asfrom=False, **kwargs): + return "(" + grouping.element._compiler_dispatch(self, **kwargs) + ")" + + def visit_label_reference( + self, element, within_columns_clause=False, **kwargs): + if self.stack and self.dialect.supports_simple_order_by_label: + selectable = self.stack[-1]['selectable'] + + with_cols, only_froms, only_cols = selectable._label_resolve_dict + if within_columns_clause: + resolve_dict = only_froms + else: + resolve_dict = only_cols + + # this can be None in the case that a _label_reference() + # were subject to a replacement operation, in which case + # the replacement of the Label element may have changed + # to something else like a ColumnClause expression. + order_by_elem = element.element._order_by_label_element + + if order_by_elem is not None and order_by_elem.name in \ + resolve_dict and \ + order_by_elem.shares_lineage( + resolve_dict[order_by_elem.name]): + kwargs['render_label_as_label'] = \ + element.element._order_by_label_element + return self.process( + element.element, within_columns_clause=within_columns_clause, + **kwargs) + + def visit_textual_label_reference( + self, element, within_columns_clause=False, **kwargs): + if not self.stack: + # compiling the element outside of the context of a SELECT + return self.process( + element._text_clause + ) + + selectable = self.stack[-1]['selectable'] + with_cols, only_froms, only_cols = selectable._label_resolve_dict + try: + if within_columns_clause: + col = only_froms[element.element] + else: + col = with_cols[element.element] + except KeyError: + # treat it like text() + util.warn_limited( + "Can't resolve label reference %r; converting to text()", + util.ellipses_string(element.element)) + return self.process( + element._text_clause + ) + else: + kwargs['render_label_as_label'] = col + return self.process( + col, within_columns_clause=within_columns_clause, **kwargs) + + def visit_label(self, label, + add_to_result_map=None, + within_label_clause=False, + within_columns_clause=False, + render_label_as_label=None, + **kw): + # only render labels within the columns clause + # or ORDER BY clause of a select. dialect-specific compilers + # can modify this behavior. + render_label_with_as = (within_columns_clause and not + within_label_clause) + render_label_only = render_label_as_label is label + + if render_label_only or render_label_with_as: + if isinstance(label.name, elements._truncated_label): + labelname = self._truncated_identifier("colident", label.name) + else: + labelname = label.name + + if render_label_with_as: + if add_to_result_map is not None: + add_to_result_map( + labelname, + label.name, + (label, labelname, ) + label._alt_names, + label.type + ) + + return label.element._compiler_dispatch( + self, within_columns_clause=True, + within_label_clause=True, **kw) + \ + OPERATORS[operators.as_] + \ + self.preparer.format_label(label, labelname) + elif render_label_only: + return self.preparer.format_label(label, labelname) + else: + return label.element._compiler_dispatch( + self, within_columns_clause=False, **kw) + + def _fallback_column_name(self, column): + raise exc.CompileError("Cannot compile Column object until " + "its 'name' is assigned.") + + def visit_column(self, column, add_to_result_map=None, + include_table=True, **kwargs): + name = orig_name = column.name + if name is None: + name = self._fallback_column_name(column) + + is_literal = column.is_literal + if not is_literal and isinstance(name, elements._truncated_label): + name = self._truncated_identifier("colident", name) + + if add_to_result_map is not None: + add_to_result_map( + name, + orig_name, + (column, name, column.key), + column.type + ) + + if is_literal: + name = self.escape_literal_column(name) + else: + name = self.preparer.quote(name) + table = column.table + if table is None or not include_table or not table.named_with_column: + return name + else: + effective_schema = self.preparer.schema_for_object(table) + + if effective_schema: + schema_prefix = self.preparer.quote_schema( + effective_schema) + '.' + else: + schema_prefix = '' + tablename = table.name + if isinstance(tablename, elements._truncated_label): + tablename = self._truncated_identifier("alias", tablename) + + return schema_prefix + \ + self.preparer.quote(tablename) + \ + "." + name + + def visit_collation(self, element, **kw): + return self.preparer.format_collation(element.collation) + + def visit_fromclause(self, fromclause, **kwargs): + return fromclause.name + + def visit_index(self, index, **kwargs): + return index.name + + def visit_typeclause(self, typeclause, **kw): + kw['type_expression'] = typeclause + return self.dialect.type_compiler.process(typeclause.type, **kw) + + def post_process_text(self, text): + if self.preparer._double_percents: + text = text.replace('%', '%%') + return text + + def escape_literal_column(self, text): + if self.preparer._double_percents: + text = text.replace('%', '%%') + return text + + def visit_textclause(self, textclause, **kw): + def do_bindparam(m): + name = m.group(1) + if name in textclause._bindparams: + return self.process(textclause._bindparams[name], **kw) + else: + return self.bindparam_string(name, **kw) + + if not self.stack: + self.isplaintext = True + + # un-escape any \:params + return BIND_PARAMS_ESC.sub( + lambda m: m.group(1), + BIND_PARAMS.sub( + do_bindparam, + self.post_process_text(textclause.text)) + ) + + def visit_text_as_from(self, taf, + compound_index=None, + asfrom=False, + parens=True, **kw): + + toplevel = not self.stack + entry = self._default_stack_entry if toplevel else self.stack[-1] + + populate_result_map = toplevel or \ + ( + compound_index == 0 and entry.get( + 'need_result_map_for_compound', False) + ) or entry.get('need_result_map_for_nested', False) + + if populate_result_map: + self._ordered_columns = \ + self._textual_ordered_columns = taf.positional + for c in taf.column_args: + self.process(c, within_columns_clause=True, + add_to_result_map=self._add_to_result_map) + + text = self.process(taf.element, **kw) + if asfrom and parens: + text = "(%s)" % text + return text + + def visit_null(self, expr, **kw): + return 'NULL' + + def visit_true(self, expr, **kw): + if self.dialect.supports_native_boolean: + return 'true' + else: + return "1" + + def visit_false(self, expr, **kw): + if self.dialect.supports_native_boolean: + return 'false' + else: + return "0" + + def visit_clauselist(self, clauselist, **kw): + sep = clauselist.operator + if sep is None: + sep = " " + else: + sep = OPERATORS[clauselist.operator] + return sep.join( + s for s in + ( + c._compiler_dispatch(self, **kw) + for c in clauselist.clauses) + if s) + + def visit_case(self, clause, **kwargs): + x = "CASE " + if clause.value is not None: + x += clause.value._compiler_dispatch(self, **kwargs) + " " + for cond, result in clause.whens: + x += "WHEN " + cond._compiler_dispatch( + self, **kwargs + ) + " THEN " + result._compiler_dispatch( + self, **kwargs) + " " + if clause.else_ is not None: + x += "ELSE " + clause.else_._compiler_dispatch( + self, **kwargs + ) + " " + x += "END" + return x + + def visit_type_coerce(self, type_coerce, **kw): + return type_coerce.typed_expression._compiler_dispatch(self, **kw) + + def visit_cast(self, cast, **kwargs): + return "CAST(%s AS %s)" % \ + (cast.clause._compiler_dispatch(self, **kwargs), + cast.typeclause._compiler_dispatch(self, **kwargs)) + + def _format_frame_clause(self, range_, **kw): + + return '%s AND %s' % ( + "UNBOUNDED PRECEDING" + if range_[0] is elements.RANGE_UNBOUNDED + else "CURRENT ROW" if range_[0] is elements.RANGE_CURRENT + else "%s PRECEDING" % ( + self.process(elements.literal(abs(range_[0])), **kw), ) + if range_[0] < 0 + else "%s FOLLOWING" % ( + self.process(elements.literal(range_[0]), **kw), ), + + "UNBOUNDED FOLLOWING" + if range_[1] is elements.RANGE_UNBOUNDED + else "CURRENT ROW" if range_[1] is elements.RANGE_CURRENT + else "%s PRECEDING" % ( + self.process(elements.literal(abs(range_[1])), **kw), ) + if range_[1] < 0 + else "%s FOLLOWING" % ( + self.process(elements.literal(range_[1]), **kw), ), + ) + + def visit_over(self, over, **kwargs): + if over.range_: + range_ = "RANGE BETWEEN %s" % self._format_frame_clause( + over.range_, **kwargs) + elif over.rows: + range_ = "ROWS BETWEEN %s" % self._format_frame_clause( + over.rows, **kwargs) + else: + range_ = None + + return "%s OVER (%s)" % ( + over.element._compiler_dispatch(self, **kwargs), + ' '.join([ + '%s BY %s' % ( + word, clause._compiler_dispatch(self, **kwargs) + ) + for word, clause in ( + ('PARTITION', over.partition_by), + ('ORDER', over.order_by) + ) + if clause is not None and len(clause) + ] + ([range_] if range_ else []) + ) + ) + + def visit_withingroup(self, withingroup, **kwargs): + return "%s WITHIN GROUP (ORDER BY %s)" % ( + withingroup.element._compiler_dispatch(self, **kwargs), + withingroup.order_by._compiler_dispatch(self, **kwargs) + ) + + def visit_funcfilter(self, funcfilter, **kwargs): + return "%s FILTER (WHERE %s)" % ( + funcfilter.func._compiler_dispatch(self, **kwargs), + funcfilter.criterion._compiler_dispatch(self, **kwargs) + ) + + def visit_extract(self, extract, **kwargs): + field = self.extract_map.get(extract.field, extract.field) + return "EXTRACT(%s FROM %s)" % ( + field, extract.expr._compiler_dispatch(self, **kwargs)) + + def visit_function(self, func, add_to_result_map=None, **kwargs): + if add_to_result_map is not None: + add_to_result_map( + func.name, func.name, (), func.type + ) + + disp = getattr(self, "visit_%s_func" % func.name.lower(), None) + if disp: + return disp(func, **kwargs) + else: + name = FUNCTIONS.get(func.__class__, func.name + "%(expr)s") + return ".".join(list(func.packagenames) + [name]) % \ + {'expr': self.function_argspec(func, **kwargs)} + + def visit_next_value_func(self, next_value, **kw): + return self.visit_sequence(next_value.sequence) + + def visit_sequence(self, sequence, **kw): + raise NotImplementedError( + "Dialect '%s' does not support sequence increments." % + self.dialect.name + ) + + def function_argspec(self, func, **kwargs): + return func.clause_expr._compiler_dispatch(self, **kwargs) + + def visit_compound_select(self, cs, asfrom=False, + parens=True, compound_index=0, **kwargs): + toplevel = not self.stack + entry = self._default_stack_entry if toplevel else self.stack[-1] + need_result_map = toplevel or \ + (compound_index == 0 + and entry.get('need_result_map_for_compound', False)) + + self.stack.append( + { + 'correlate_froms': entry['correlate_froms'], + 'asfrom_froms': entry['asfrom_froms'], + 'selectable': cs, + 'need_result_map_for_compound': need_result_map + }) + + keyword = self.compound_keywords.get(cs.keyword) + + text = (" " + keyword + " ").join( + (c._compiler_dispatch(self, + asfrom=asfrom, parens=False, + compound_index=i, **kwargs) + for i, c in enumerate(cs.selects)) + ) + + text += self.group_by_clause(cs, **dict(asfrom=asfrom, **kwargs)) + text += self.order_by_clause(cs, **kwargs) + text += (cs._limit_clause is not None + or cs._offset_clause is not None) and \ + self.limit_clause(cs, **kwargs) or "" + + if self.ctes and toplevel: + text = self._render_cte_clause() + text + + self.stack.pop(-1) + if asfrom and parens: + return "(" + text + ")" + else: + return text + + def _get_operator_dispatch(self, operator_, qualifier1, qualifier2): + attrname = "visit_%s_%s%s" % ( + operator_.__name__, qualifier1, + "_" + qualifier2 if qualifier2 else "") + return getattr(self, attrname, None) + + def visit_unary(self, unary, **kw): + if unary.operator: + if unary.modifier: + raise exc.CompileError( + "Unary expression does not support operator " + "and modifier simultaneously") + disp = self._get_operator_dispatch( + unary.operator, "unary", "operator") + if disp: + return disp(unary, unary.operator, **kw) + else: + return self._generate_generic_unary_operator( + unary, OPERATORS[unary.operator], **kw) + elif unary.modifier: + disp = self._get_operator_dispatch( + unary.modifier, "unary", "modifier") + if disp: + return disp(unary, unary.modifier, **kw) + else: + return self._generate_generic_unary_modifier( + unary, OPERATORS[unary.modifier], **kw) + else: + raise exc.CompileError( + "Unary expression has no operator or modifier") + + def visit_istrue_unary_operator(self, element, operator, **kw): + if element._is_implicitly_boolean or \ + self.dialect.supports_native_boolean: + return self.process(element.element, **kw) + else: + return "%s = 1" % self.process(element.element, **kw) + + def visit_isfalse_unary_operator(self, element, operator, **kw): + if element._is_implicitly_boolean or \ + self.dialect.supports_native_boolean: + return "NOT %s" % self.process(element.element, **kw) + else: + return "%s = 0" % self.process(element.element, **kw) + + def visit_notmatch_op_binary(self, binary, operator, **kw): + return "NOT %s" % self.visit_binary( + binary, override_operator=operators.match_op) + + def _emit_empty_in_warning(self): + util.warn( + 'The IN-predicate was invoked with an ' + 'empty sequence. This results in a ' + 'contradiction, which nonetheless can be ' + 'expensive to evaluate. Consider alternative ' + 'strategies for improved performance.') + + def visit_empty_in_op_binary(self, binary, operator, **kw): + if self.dialect._use_static_in: + return "1 != 1" + else: + if self.dialect._warn_on_empty_in: + self._emit_empty_in_warning() + return self.process(binary.left != binary.left) + + def visit_empty_notin_op_binary(self, binary, operator, **kw): + if self.dialect._use_static_in: + return "1 = 1" + else: + if self.dialect._warn_on_empty_in: + self._emit_empty_in_warning() + return self.process(binary.left == binary.left) + + def visit_binary(self, binary, override_operator=None, + eager_grouping=False, **kw): + + # don't allow "? = ?" to render + if self.ansi_bind_rules and \ + isinstance(binary.left, elements.BindParameter) and \ + isinstance(binary.right, elements.BindParameter): + kw['literal_binds'] = True + + operator_ = override_operator or binary.operator + disp = self._get_operator_dispatch(operator_, "binary", None) + if disp: + return disp(binary, operator_, **kw) + else: + try: + opstring = OPERATORS[operator_] + except KeyError: + raise exc.UnsupportedCompilationError(self, operator_) + else: + return self._generate_generic_binary(binary, opstring, **kw) + + def visit_mod_binary(self, binary, operator, **kw): + if self.preparer._double_percents: + return self.process(binary.left, **kw) + " %% " + \ + self.process(binary.right, **kw) + else: + return self.process(binary.left, **kw) + " % " + \ + self.process(binary.right, **kw) + + def visit_custom_op_binary(self, element, operator, **kw): + kw['eager_grouping'] = operator.eager_grouping + return self._generate_generic_binary( + element, " " + operator.opstring + " ", **kw) + + def visit_custom_op_unary_operator(self, element, operator, **kw): + return self._generate_generic_unary_operator( + element, operator.opstring + " ", **kw) + + def visit_custom_op_unary_modifier(self, element, operator, **kw): + return self._generate_generic_unary_modifier( + element, " " + operator.opstring, **kw) + + def _generate_generic_binary( + self, binary, opstring, eager_grouping=False, **kw): + + _in_binary = kw.get('_in_binary', False) + + kw['_in_binary'] = True + text = binary.left._compiler_dispatch( + self, eager_grouping=eager_grouping, **kw) + \ + opstring + \ + binary.right._compiler_dispatch( + self, eager_grouping=eager_grouping, **kw) + + if _in_binary and eager_grouping: + text = "(%s)" % text + return text + + def _generate_generic_unary_operator(self, unary, opstring, **kw): + return opstring + unary.element._compiler_dispatch(self, **kw) + + def _generate_generic_unary_modifier(self, unary, opstring, **kw): + return unary.element._compiler_dispatch(self, **kw) + opstring + + @util.memoized_property + def _like_percent_literal(self): + return elements.literal_column("'%'", type_=sqltypes.STRINGTYPE) + + def visit_contains_op_binary(self, binary, operator, **kw): + binary = binary._clone() + percent = self._like_percent_literal + binary.right = percent.__add__(binary.right).__add__(percent) + return self.visit_like_op_binary(binary, operator, **kw) + + def visit_notcontains_op_binary(self, binary, operator, **kw): + binary = binary._clone() + percent = self._like_percent_literal + binary.right = percent.__add__(binary.right).__add__(percent) + return self.visit_notlike_op_binary(binary, operator, **kw) + + def visit_startswith_op_binary(self, binary, operator, **kw): + binary = binary._clone() + percent = self._like_percent_literal + binary.right = percent.__radd__( + binary.right + ) + return self.visit_like_op_binary(binary, operator, **kw) + + def visit_notstartswith_op_binary(self, binary, operator, **kw): + binary = binary._clone() + percent = self._like_percent_literal + binary.right = percent.__radd__( + binary.right + ) + return self.visit_notlike_op_binary(binary, operator, **kw) + + def visit_endswith_op_binary(self, binary, operator, **kw): + binary = binary._clone() + percent = self._like_percent_literal + binary.right = percent.__add__(binary.right) + return self.visit_like_op_binary(binary, operator, **kw) + + def visit_notendswith_op_binary(self, binary, operator, **kw): + binary = binary._clone() + percent = self._like_percent_literal + binary.right = percent.__add__(binary.right) + return self.visit_notlike_op_binary(binary, operator, **kw) + + def visit_like_op_binary(self, binary, operator, **kw): + escape = binary.modifiers.get("escape", None) + + # TODO: use ternary here, not "and"/ "or" + return '%s LIKE %s' % ( + binary.left._compiler_dispatch(self, **kw), + binary.right._compiler_dispatch(self, **kw)) \ + + ( + ' ESCAPE ' + + self.render_literal_value(escape, sqltypes.STRINGTYPE) + if escape else '' + ) + + def visit_notlike_op_binary(self, binary, operator, **kw): + escape = binary.modifiers.get("escape", None) + return '%s NOT LIKE %s' % ( + binary.left._compiler_dispatch(self, **kw), + binary.right._compiler_dispatch(self, **kw)) \ + + ( + ' ESCAPE ' + + self.render_literal_value(escape, sqltypes.STRINGTYPE) + if escape else '' + ) + + def visit_ilike_op_binary(self, binary, operator, **kw): + escape = binary.modifiers.get("escape", None) + return 'lower(%s) LIKE lower(%s)' % ( + binary.left._compiler_dispatch(self, **kw), + binary.right._compiler_dispatch(self, **kw)) \ + + ( + ' ESCAPE ' + + self.render_literal_value(escape, sqltypes.STRINGTYPE) + if escape else '' + ) + + def visit_notilike_op_binary(self, binary, operator, **kw): + escape = binary.modifiers.get("escape", None) + return 'lower(%s) NOT LIKE lower(%s)' % ( + binary.left._compiler_dispatch(self, **kw), + binary.right._compiler_dispatch(self, **kw)) \ + + ( + ' ESCAPE ' + + self.render_literal_value(escape, sqltypes.STRINGTYPE) + if escape else '' + ) + + def visit_between_op_binary(self, binary, operator, **kw): + symmetric = binary.modifiers.get("symmetric", False) + return self._generate_generic_binary( + binary, " BETWEEN SYMMETRIC " + if symmetric else " BETWEEN ", **kw) + + def visit_notbetween_op_binary(self, binary, operator, **kw): + symmetric = binary.modifiers.get("symmetric", False) + return self._generate_generic_binary( + binary, " NOT BETWEEN SYMMETRIC " + if symmetric else " NOT BETWEEN ", **kw) + + def visit_bindparam(self, bindparam, within_columns_clause=False, + literal_binds=False, + skip_bind_expression=False, + **kwargs): + if not skip_bind_expression and bindparam.type._has_bind_expression: + bind_expression = bindparam.type.bind_expression(bindparam) + return self.process(bind_expression, + skip_bind_expression=True) + + if literal_binds or \ + (within_columns_clause and + self.ansi_bind_rules): + if bindparam.value is None and bindparam.callable is None: + raise exc.CompileError("Bind parameter '%s' without a " + "renderable value not allowed here." + % bindparam.key) + return self.render_literal_bindparam( + bindparam, within_columns_clause=True, **kwargs) + + name = self._truncate_bindparam(bindparam) + + if name in self.binds: + existing = self.binds[name] + if existing is not bindparam: + if (existing.unique or bindparam.unique) and \ + not existing.proxy_set.intersection( + bindparam.proxy_set): + raise exc.CompileError( + "Bind parameter '%s' conflicts with " + "unique bind parameter of the same name" % + bindparam.key + ) + elif existing._is_crud or bindparam._is_crud: + raise exc.CompileError( + "bindparam() name '%s' is reserved " + "for automatic usage in the VALUES or SET " + "clause of this " + "insert/update statement. Please use a " + "name other than column name when using bindparam() " + "with insert() or update() (for example, 'b_%s')." % + (bindparam.key, bindparam.key) + ) + + self.binds[bindparam.key] = self.binds[name] = bindparam + + return self.bindparam_string( + name, expanding=bindparam.expanding, **kwargs) + + def render_literal_bindparam(self, bindparam, **kw): + value = bindparam.effective_value + return self.render_literal_value(value, bindparam.type) + + def render_literal_value(self, value, type_): + """Render the value of a bind parameter as a quoted literal. + + This is used for statement sections that do not accept bind parameters + on the target driver/database. + + This should be implemented by subclasses using the quoting services + of the DBAPI. + + """ + + processor = type_._cached_literal_processor(self.dialect) + if processor: + return processor(value) + else: + raise NotImplementedError( + "Don't know how to literal-quote value %r" % value) + + def _truncate_bindparam(self, bindparam): + if bindparam in self.bind_names: + return self.bind_names[bindparam] + + bind_name = bindparam.key + if isinstance(bind_name, elements._truncated_label): + bind_name = self._truncated_identifier("bindparam", bind_name) + + # add to bind_names for translation + self.bind_names[bindparam] = bind_name + + return bind_name + + def _truncated_identifier(self, ident_class, name): + if (ident_class, name) in self.truncated_names: + return self.truncated_names[(ident_class, name)] + + anonname = name.apply_map(self.anon_map) + + if len(anonname) > self.label_length - 6: + counter = self.truncated_names.get(ident_class, 1) + truncname = anonname[0:max(self.label_length - 6, 0)] + \ + "_" + hex(counter)[2:] + self.truncated_names[ident_class] = counter + 1 + else: + truncname = anonname + self.truncated_names[(ident_class, name)] = truncname + return truncname + + def _anonymize(self, name): + return name % self.anon_map + + def _process_anon(self, key): + (ident, derived) = key.split(' ', 1) + anonymous_counter = self.anon_map.get(derived, 1) + self.anon_map[derived] = anonymous_counter + 1 + return derived + "_" + str(anonymous_counter) + + def bindparam_string( + self, name, positional_names=None, expanding=False, **kw): + if self.positional: + if positional_names is not None: + positional_names.append(name) + else: + self.positiontup.append(name) + if expanding: + self.contains_expanding_parameters = True + return "([EXPANDING_%s])" % name + else: + return self.bindtemplate % {'name': name} + + def visit_cte(self, cte, asfrom=False, ashint=False, + fromhints=None, visiting_cte=None, + **kwargs): + self._init_cte_state() + + kwargs['visiting_cte'] = cte + if isinstance(cte.name, elements._truncated_label): + cte_name = self._truncated_identifier("alias", cte.name) + else: + cte_name = cte.name + + is_new_cte = True + embedded_in_current_named_cte = False + + if cte_name in self.ctes_by_name: + existing_cte = self.ctes_by_name[cte_name] + embedded_in_current_named_cte = visiting_cte is existing_cte + + # we've generated a same-named CTE that we are enclosed in, + # or this is the same CTE. just return the name. + if cte in existing_cte._restates or cte is existing_cte: + is_new_cte = False + elif existing_cte in cte._restates: + # we've generated a same-named CTE that is + # enclosed in us - we take precedence, so + # discard the text for the "inner". + del self.ctes[existing_cte] + else: + raise exc.CompileError( + "Multiple, unrelated CTEs found with " + "the same name: %r" % + cte_name) + + if asfrom or is_new_cte: + if cte._cte_alias is not None: + pre_alias_cte = cte._cte_alias + cte_pre_alias_name = cte._cte_alias.name + if isinstance(cte_pre_alias_name, elements._truncated_label): + cte_pre_alias_name = self._truncated_identifier( + "alias", cte_pre_alias_name) + else: + pre_alias_cte = cte + cte_pre_alias_name = None + + if is_new_cte: + self.ctes_by_name[cte_name] = cte + + # look for embedded DML ctes and propagate autocommit + if 'autocommit' in cte.element._execution_options and \ + 'autocommit' not in self.execution_options: + self.execution_options = self.execution_options.union( + {"autocommit": + cte.element._execution_options['autocommit']}) + + if pre_alias_cte not in self.ctes: + self.visit_cte(pre_alias_cte, **kwargs) + + if not cte_pre_alias_name and cte not in self.ctes: + if cte.recursive: + self.ctes_recursive = True + text = self.preparer.format_alias(cte, cte_name) + if cte.recursive: + if isinstance(cte.original, selectable.Select): + col_source = cte.original + elif isinstance(cte.original, selectable.CompoundSelect): + col_source = cte.original.selects[0] + else: + assert False + recur_cols = [c for c in + util.unique_list(col_source.inner_columns) + if c is not None] + + text += "(%s)" % (", ".join( + self.preparer.format_column(ident) + for ident in recur_cols)) + + if self.positional: + kwargs['positional_names'] = self.cte_positional[cte] = [] + + text += " AS \n" + \ + cte.original._compiler_dispatch( + self, asfrom=True, **kwargs + ) + + if cte._suffixes: + text += " " + self._generate_prefixes( + cte, cte._suffixes, **kwargs) + + self.ctes[cte] = text + + if asfrom: + if not is_new_cte and embedded_in_current_named_cte: + return self.preparer.format_alias(cte, cte_name) + + if cte_pre_alias_name: + text = self.preparer.format_alias(cte, cte_pre_alias_name) + if self.preparer._requires_quotes(cte_name): + cte_name = self.preparer.quote(cte_name) + text += self.get_render_as_alias_suffix(cte_name) + return text + else: + return self.preparer.format_alias(cte, cte_name) + + def visit_alias(self, alias, asfrom=False, ashint=False, + iscrud=False, + fromhints=None, **kwargs): + if asfrom or ashint: + if isinstance(alias.name, elements._truncated_label): + alias_name = self._truncated_identifier("alias", alias.name) + else: + alias_name = alias.name + + if ashint: + return self.preparer.format_alias(alias, alias_name) + elif asfrom: + ret = alias.original._compiler_dispatch(self, + asfrom=True, **kwargs) + \ + self.get_render_as_alias_suffix( + self.preparer.format_alias(alias, alias_name)) + + if fromhints and alias in fromhints: + ret = self.format_from_hint_text(ret, alias, + fromhints[alias], iscrud) + + return ret + else: + return alias.original._compiler_dispatch(self, **kwargs) + + def visit_lateral(self, lateral, **kw): + kw['lateral'] = True + return "LATERAL %s" % self.visit_alias(lateral, **kw) + + def visit_tablesample(self, tablesample, asfrom=False, **kw): + text = "%s TABLESAMPLE %s" % ( + self.visit_alias(tablesample, asfrom=True, **kw), + tablesample._get_method()._compiler_dispatch(self, **kw)) + + if tablesample.seed is not None: + text += " REPEATABLE (%s)" % ( + tablesample.seed._compiler_dispatch(self, **kw)) + + return text + + def get_render_as_alias_suffix(self, alias_name_text): + return " AS " + alias_name_text + + def _add_to_result_map(self, keyname, name, objects, type_): + self._result_columns.append((keyname, name, objects, type_)) + + def _label_select_column(self, select, column, + populate_result_map, + asfrom, column_clause_args, + name=None, + within_columns_clause=True): + """produce labeled columns present in a select().""" + + if column.type._has_column_expression and \ + populate_result_map: + col_expr = column.type.column_expression(column) + add_to_result_map = lambda keyname, name, objects, type_: \ + self._add_to_result_map( + keyname, name, + (column,) + objects, type_) + else: + col_expr = column + if populate_result_map: + add_to_result_map = self._add_to_result_map + else: + add_to_result_map = None + + if not within_columns_clause: + result_expr = col_expr + elif isinstance(column, elements.Label): + if col_expr is not column: + result_expr = _CompileLabel( + col_expr, + column.name, + alt_names=(column.element,) + ) + else: + result_expr = col_expr + + elif select is not None and name: + result_expr = _CompileLabel( + col_expr, + name, + alt_names=(column._key_label,) + ) + + elif \ + asfrom and \ + isinstance(column, elements.ColumnClause) and \ + not column.is_literal and \ + column.table is not None and \ + not isinstance(column.table, selectable.Select): + result_expr = _CompileLabel(col_expr, + elements._as_truncated(column.name), + alt_names=(column.key,)) + elif ( + not isinstance(column, elements.TextClause) and + ( + not isinstance(column, elements.UnaryExpression) or + column.wraps_column_expression + ) and + ( + not hasattr(column, 'name') or + isinstance(column, functions.Function) + ) + ): + result_expr = _CompileLabel(col_expr, column.anon_label) + elif col_expr is not column: + # TODO: are we sure "column" has a .name and .key here ? + # assert isinstance(column, elements.ColumnClause) + result_expr = _CompileLabel(col_expr, + elements._as_truncated(column.name), + alt_names=(column.key,)) + else: + result_expr = col_expr + + column_clause_args.update( + within_columns_clause=within_columns_clause, + add_to_result_map=add_to_result_map + ) + return result_expr._compiler_dispatch( + self, + **column_clause_args + ) + + def format_from_hint_text(self, sqltext, table, hint, iscrud): + hinttext = self.get_from_hint_text(table, hint) + if hinttext: + sqltext += " " + hinttext + return sqltext + + def get_select_hint_text(self, byfroms): + return None + + def get_from_hint_text(self, table, text): + return None + + def get_crud_hint_text(self, table, text): + return None + + def get_statement_hint_text(self, hint_texts): + return " ".join(hint_texts) + + def _transform_select_for_nested_joins(self, select): + """Rewrite any "a JOIN (b JOIN c)" expression as + "a JOIN (select * from b JOIN c) AS anon", to support + databases that can't parse a parenthesized join correctly + (i.e. sqlite < 3.7.16). + + """ + cloned = {} + column_translate = [{}] + + def visit(element, **kw): + if element in column_translate[-1]: + return column_translate[-1][element] + + elif element in cloned: + return cloned[element] + + newelem = cloned[element] = element._clone() + + if newelem.is_selectable and newelem._is_join and \ + isinstance(newelem.right, selectable.FromGrouping): + + newelem._reset_exported() + newelem.left = visit(newelem.left, **kw) + + right = visit(newelem.right, **kw) + + selectable_ = selectable.Select( + [right.element], + use_labels=True).alias() + + for c in selectable_.c: + c._key_label = c.key + c._label = c.name + + translate_dict = dict( + zip(newelem.right.element.c, selectable_.c) + ) + + # translating from both the old and the new + # because different select() structures will lead us + # to traverse differently + translate_dict[right.element.left] = selectable_ + translate_dict[right.element.right] = selectable_ + translate_dict[newelem.right.element.left] = selectable_ + translate_dict[newelem.right.element.right] = selectable_ + + # propagate translations that we've gained + # from nested visit(newelem.right) outwards + # to the enclosing select here. this happens + # only when we have more than one level of right + # join nesting, i.e. "a JOIN (b JOIN (c JOIN d))" + for k, v in list(column_translate[-1].items()): + if v in translate_dict: + # remarkably, no current ORM tests (May 2013) + # hit this condition, only test_join_rewriting + # does. + column_translate[-1][k] = translate_dict[v] + + column_translate[-1].update(translate_dict) + + newelem.right = selectable_ + + newelem.onclause = visit(newelem.onclause, **kw) + + elif newelem._is_from_container: + # if we hit an Alias, CompoundSelect or ScalarSelect, put a + # marker in the stack. + kw['transform_clue'] = 'select_container' + newelem._copy_internals(clone=visit, **kw) + elif newelem.is_selectable and newelem._is_select: + barrier_select = kw.get('transform_clue', None) == \ + 'select_container' + # if we're still descended from an + # Alias/CompoundSelect/ScalarSelect, we're + # in a FROM clause, so start with a new translate collection + if barrier_select: + column_translate.append({}) + kw['transform_clue'] = 'inside_select' + newelem._copy_internals(clone=visit, **kw) + if barrier_select: + del column_translate[-1] + else: + newelem._copy_internals(clone=visit, **kw) + + return newelem + + return visit(select) + + def _transform_result_map_for_nested_joins( + self, select, transformed_select): + inner_col = dict((c._key_label, c) for + c in transformed_select.inner_columns) + + d = dict( + (inner_col[c._key_label], c) + for c in select.inner_columns + ) + + self._result_columns = [ + (key, name, tuple([d.get(col, col) for col in objs]), typ) + for key, name, objs, typ in self._result_columns + ] + + _default_stack_entry = util.immutabledict([ + ('correlate_froms', frozenset()), + ('asfrom_froms', frozenset()) + ]) + + def _display_froms_for_select(self, select, asfrom, lateral=False): + # utility method to help external dialects + # get the correct from list for a select. + # specifically the oracle dialect needs this feature + # right now. + toplevel = not self.stack + entry = self._default_stack_entry if toplevel else self.stack[-1] + + correlate_froms = entry['correlate_froms'] + asfrom_froms = entry['asfrom_froms'] + + if asfrom and not lateral: + froms = select._get_display_froms( + explicit_correlate_froms=correlate_froms.difference( + asfrom_froms), + implicit_correlate_froms=()) + else: + froms = select._get_display_froms( + explicit_correlate_froms=correlate_froms, + implicit_correlate_froms=asfrom_froms) + return froms + + def visit_select(self, select, asfrom=False, parens=True, + fromhints=None, + compound_index=0, + nested_join_translation=False, + select_wraps_for=None, + lateral=False, + **kwargs): + + needs_nested_translation = \ + select.use_labels and \ + not nested_join_translation and \ + not self.stack and \ + not self.dialect.supports_right_nested_joins + + if needs_nested_translation: + transformed_select = self._transform_select_for_nested_joins( + select) + text = self.visit_select( + transformed_select, asfrom=asfrom, parens=parens, + fromhints=fromhints, + compound_index=compound_index, + nested_join_translation=True, **kwargs + ) + + toplevel = not self.stack + entry = self._default_stack_entry if toplevel else self.stack[-1] + + populate_result_map = toplevel or \ + ( + compound_index == 0 and entry.get( + 'need_result_map_for_compound', False) + ) or entry.get('need_result_map_for_nested', False) + + # this was first proposed as part of #3372; however, it is not + # reached in current tests and could possibly be an assertion + # instead. + if not populate_result_map and 'add_to_result_map' in kwargs: + del kwargs['add_to_result_map'] + + if needs_nested_translation: + if populate_result_map: + self._transform_result_map_for_nested_joins( + select, transformed_select) + return text + + froms = self._setup_select_stack(select, entry, asfrom, lateral) + + column_clause_args = kwargs.copy() + column_clause_args.update({ + 'within_label_clause': False, + 'within_columns_clause': False + }) + + text = "SELECT " # we're off to a good start ! + + if select._hints: + hint_text, byfrom = self._setup_select_hints(select) + if hint_text: + text += hint_text + " " + else: + byfrom = None + + if select._prefixes: + text += self._generate_prefixes( + select, select._prefixes, **kwargs) + + text += self.get_select_precolumns(select, **kwargs) + # the actual list of columns to print in the SELECT column list. + inner_columns = [ + c for c in [ + self._label_select_column( + select, + column, + populate_result_map, asfrom, + column_clause_args, + name=name) + for name, column in select._columns_plus_names + ] + if c is not None + ] + + if populate_result_map and select_wraps_for is not None: + # if this select is a compiler-generated wrapper, + # rewrite the targeted columns in the result map + + translate = dict( + zip( + [name for (key, name) in select._columns_plus_names], + [name for (key, name) in + select_wraps_for._columns_plus_names]) + ) + + self._result_columns = [ + (key, name, tuple(translate.get(o, o) for o in obj), type_) + for key, name, obj, type_ in self._result_columns + ] + + text = self._compose_select_body( + text, select, inner_columns, froms, byfrom, kwargs) + + if select._statement_hints: + per_dialect = [ + ht for (dialect_name, ht) + in select._statement_hints + if dialect_name in ('*', self.dialect.name) + ] + if per_dialect: + text += " " + self.get_statement_hint_text(per_dialect) + + if self.ctes and toplevel: + text = self._render_cte_clause() + text + + if select._suffixes: + text += " " + self._generate_prefixes( + select, select._suffixes, **kwargs) + + self.stack.pop(-1) + + if (asfrom or lateral) and parens: + return "(" + text + ")" + else: + return text + + def _setup_select_hints(self, select): + byfrom = dict([ + (from_, hinttext % { + 'name': from_._compiler_dispatch( + self, ashint=True) + }) + for (from_, dialect), hinttext in + select._hints.items() + if dialect in ('*', self.dialect.name) + ]) + hint_text = self.get_select_hint_text(byfrom) + return hint_text, byfrom + + def _setup_select_stack(self, select, entry, asfrom, lateral): + correlate_froms = entry['correlate_froms'] + asfrom_froms = entry['asfrom_froms'] + + if asfrom and not lateral: + froms = select._get_display_froms( + explicit_correlate_froms=correlate_froms.difference( + asfrom_froms), + implicit_correlate_froms=()) + else: + froms = select._get_display_froms( + explicit_correlate_froms=correlate_froms, + implicit_correlate_froms=asfrom_froms) + + new_correlate_froms = set(selectable._from_objects(*froms)) + all_correlate_froms = new_correlate_froms.union(correlate_froms) + + new_entry = { + 'asfrom_froms': new_correlate_froms, + 'correlate_froms': all_correlate_froms, + 'selectable': select, + } + self.stack.append(new_entry) + + return froms + + def _compose_select_body( + self, text, select, inner_columns, froms, byfrom, kwargs): + text += ', '.join(inner_columns) + + if froms: + text += " \nFROM " + + if select._hints: + text += ', '.join( + [f._compiler_dispatch(self, asfrom=True, + fromhints=byfrom, **kwargs) + for f in froms]) + else: + text += ', '.join( + [f._compiler_dispatch(self, asfrom=True, **kwargs) + for f in froms]) + else: + text += self.default_from() + + if select._whereclause is not None: + t = select._whereclause._compiler_dispatch(self, **kwargs) + if t: + text += " \nWHERE " + t + + if select._group_by_clause.clauses: + text += self.group_by_clause(select, **kwargs) + + if select._having is not None: + t = select._having._compiler_dispatch(self, **kwargs) + if t: + text += " \nHAVING " + t + + if select._order_by_clause.clauses: + text += self.order_by_clause(select, **kwargs) + + if (select._limit_clause is not None or + select._offset_clause is not None): + text += self.limit_clause(select, **kwargs) + + if select._for_update_arg is not None: + text += self.for_update_clause(select, **kwargs) + + return text + + def _generate_prefixes(self, stmt, prefixes, **kw): + clause = " ".join( + prefix._compiler_dispatch(self, **kw) + for prefix, dialect_name in prefixes + if dialect_name is None or + dialect_name == self.dialect.name + ) + if clause: + clause += " " + return clause + + def _render_cte_clause(self): + if self.positional: + self.positiontup = sum([ + self.cte_positional[cte] + for cte in self.ctes], []) + \ + self.positiontup + cte_text = self.get_cte_preamble(self.ctes_recursive) + " " + cte_text += ", \n".join( + [txt for txt in self.ctes.values()] + ) + cte_text += "\n " + return cte_text + + def get_cte_preamble(self, recursive): + if recursive: + return "WITH RECURSIVE" + else: + return "WITH" + + def get_select_precolumns(self, select, **kw): + """Called when building a ``SELECT`` statement, position is just + before column list. + + """ + return select._distinct and "DISTINCT " or "" + + def group_by_clause(self, select, **kw): + """allow dialects to customize how GROUP BY is rendered.""" + + group_by = select._group_by_clause._compiler_dispatch(self, **kw) + if group_by: + return " GROUP BY " + group_by + else: + return "" + + def order_by_clause(self, select, **kw): + """allow dialects to customize how ORDER BY is rendered.""" + + order_by = select._order_by_clause._compiler_dispatch(self, **kw) + if order_by: + return " ORDER BY " + order_by + else: + return "" + + def for_update_clause(self, select, **kw): + return " FOR UPDATE" + + def returning_clause(self, stmt, returning_cols): + raise exc.CompileError( + "RETURNING is not supported by this " + "dialect's statement compiler.") + + def limit_clause(self, select, **kw): + text = "" + if select._limit_clause is not None: + text += "\n LIMIT " + self.process(select._limit_clause, **kw) + if select._offset_clause is not None: + if select._limit_clause is None: + text += "\n LIMIT -1" + text += " OFFSET " + self.process(select._offset_clause, **kw) + return text + + def visit_table(self, table, asfrom=False, iscrud=False, ashint=False, + fromhints=None, use_schema=True, **kwargs): + if asfrom or ashint: + effective_schema = self.preparer.schema_for_object(table) + + if use_schema and effective_schema: + ret = self.preparer.quote_schema(effective_schema) + \ + "." + self.preparer.quote(table.name) + else: + ret = self.preparer.quote(table.name) + if fromhints and table in fromhints: + ret = self.format_from_hint_text(ret, table, + fromhints[table], iscrud) + return ret + else: + return "" + + def visit_join(self, join, asfrom=False, **kwargs): + if join.full: + join_type = " FULL OUTER JOIN " + elif join.isouter: + join_type = " LEFT OUTER JOIN " + else: + join_type = " JOIN " + return ( + join.left._compiler_dispatch(self, asfrom=True, **kwargs) + + join_type + + join.right._compiler_dispatch(self, asfrom=True, **kwargs) + + " ON " + + join.onclause._compiler_dispatch(self, **kwargs) + ) + + def _setup_crud_hints(self, stmt, table_text): + dialect_hints = dict([ + (table, hint_text) + for (table, dialect), hint_text in + stmt._hints.items() + if dialect in ('*', self.dialect.name) + ]) + if stmt.table in dialect_hints: + table_text = self.format_from_hint_text( + table_text, + stmt.table, + dialect_hints[stmt.table], + True + ) + return dialect_hints, table_text + + def visit_insert(self, insert_stmt, asfrom=False, **kw): + toplevel = not self.stack + + self.stack.append( + {'correlate_froms': set(), + "asfrom_froms": set(), + "selectable": insert_stmt}) + + crud_params = crud._setup_crud_params( + self, insert_stmt, crud.ISINSERT, **kw) + + if not crud_params and \ + not self.dialect.supports_default_values and \ + not self.dialect.supports_empty_insert: + raise exc.CompileError("The '%s' dialect with current database " + "version settings does not support empty " + "inserts." % + self.dialect.name) + + if insert_stmt._has_multi_parameters: + if not self.dialect.supports_multivalues_insert: + raise exc.CompileError( + "The '%s' dialect with current database " + "version settings does not support " + "in-place multirow inserts." % + self.dialect.name) + crud_params_single = crud_params[0] + else: + crud_params_single = crud_params + + preparer = self.preparer + supports_default_values = self.dialect.supports_default_values + + text = "INSERT " + + if insert_stmt._prefixes: + text += self._generate_prefixes(insert_stmt, + insert_stmt._prefixes, **kw) + + text += "INTO " + table_text = preparer.format_table(insert_stmt.table) + + if insert_stmt._hints: + dialect_hints, table_text = self._setup_crud_hints( + insert_stmt, table_text) + else: + dialect_hints = None + + text += table_text + + if crud_params_single or not supports_default_values: + text += " (%s)" % ', '.join([preparer.format_column(c[0]) + for c in crud_params_single]) + + if self.returning or insert_stmt._returning: + returning_clause = self.returning_clause( + insert_stmt, self.returning or insert_stmt._returning) + + if self.returning_precedes_values: + text += " " + returning_clause + else: + returning_clause = None + + if insert_stmt.select is not None: + select_text = self.process(self._insert_from_select, **kw) + + if self.ctes and toplevel and self.dialect.cte_follows_insert: + text += " %s%s" % (self._render_cte_clause(), select_text) + else: + text += " %s" % select_text + elif not crud_params and supports_default_values: + text += " DEFAULT VALUES" + elif insert_stmt._has_multi_parameters: + text += " VALUES %s" % ( + ", ".join( + "(%s)" % ( + ', '.join(c[1] for c in crud_param_set) + ) + for crud_param_set in crud_params + ) + ) + else: + text += " VALUES (%s)" % \ + ', '.join([c[1] for c in crud_params]) + + if insert_stmt._post_values_clause is not None: + post_values_clause = self.process( + insert_stmt._post_values_clause, **kw) + if post_values_clause: + text += " " + post_values_clause + + if returning_clause and not self.returning_precedes_values: + text += " " + returning_clause + + if self.ctes and toplevel and not self.dialect.cte_follows_insert: + text = self._render_cte_clause() + text + + self.stack.pop(-1) + + if asfrom: + return "(" + text + ")" + else: + return text + + def update_limit_clause(self, update_stmt): + """Provide a hook for MySQL to add LIMIT to the UPDATE""" + return None + + def update_tables_clause(self, update_stmt, from_table, + extra_froms, **kw): + """Provide a hook to override the initial table clause + in an UPDATE statement. + + MySQL overrides this. + + """ + kw['asfrom'] = True + return from_table._compiler_dispatch(self, iscrud=True, **kw) + + def update_from_clause(self, update_stmt, + from_table, extra_froms, + from_hints, + **kw): + """Provide a hook to override the generation of an + UPDATE..FROM clause. + + MySQL and MSSQL override this. + + """ + raise NotImplementedError( + "This backend does not support multiple-table " + "criteria within UPDATE") + + def visit_update(self, update_stmt, asfrom=False, **kw): + toplevel = not self.stack + + extra_froms = update_stmt._extra_froms + is_multitable = bool(extra_froms) + + if is_multitable: + # main table might be a JOIN + main_froms = set(selectable._from_objects(update_stmt.table)) + render_extra_froms = [ + f for f in extra_froms if f not in main_froms + ] + correlate_froms = main_froms.union(extra_froms) + else: + render_extra_froms = [] + correlate_froms = {update_stmt.table} + + self.stack.append( + {'correlate_froms': correlate_froms, + "asfrom_froms": correlate_froms, + "selectable": update_stmt}) + + text = "UPDATE " + + if update_stmt._prefixes: + text += self._generate_prefixes(update_stmt, + update_stmt._prefixes, **kw) + + table_text = self.update_tables_clause(update_stmt, update_stmt.table, + render_extra_froms, **kw) + crud_params = crud._setup_crud_params( + self, update_stmt, crud.ISUPDATE, **kw) + + if update_stmt._hints: + dialect_hints, table_text = self._setup_crud_hints( + update_stmt, table_text) + else: + dialect_hints = None + + text += table_text + + text += ' SET ' + include_table = is_multitable and \ + self.render_table_with_column_in_update_from + text += ', '.join( + c[0]._compiler_dispatch(self, + include_table=include_table) + + '=' + c[1] for c in crud_params + ) + + if self.returning or update_stmt._returning: + if self.returning_precedes_values: + text += " " + self.returning_clause( + update_stmt, self.returning or update_stmt._returning) + + if extra_froms: + extra_from_text = self.update_from_clause( + update_stmt, + update_stmt.table, + render_extra_froms, + dialect_hints, **kw) + if extra_from_text: + text += " " + extra_from_text + + if update_stmt._whereclause is not None: + t = self.process(update_stmt._whereclause, **kw) + if t: + text += " WHERE " + t + + limit_clause = self.update_limit_clause(update_stmt) + if limit_clause: + text += " " + limit_clause + + if (self.returning or update_stmt._returning) and \ + not self.returning_precedes_values: + text += " " + self.returning_clause( + update_stmt, self.returning or update_stmt._returning) + + if self.ctes and toplevel: + text = self._render_cte_clause() + text + + self.stack.pop(-1) + + if asfrom: + return "(" + text + ")" + else: + return text + + @util.memoized_property + def _key_getters_for_crud_column(self): + return crud._key_getters_for_crud_column(self, self.statement) + + def delete_extra_from_clause(self, update_stmt, + from_table, extra_froms, + from_hints, **kw): + """Provide a hook to override the generation of an + DELETE..FROM clause. + + This can be used to implement DELETE..USING for example. + + MySQL and MSSQL override this. + + """ + raise NotImplementedError( + "This backend does not support multiple-table " + "criteria within DELETE") + + def delete_table_clause(self, delete_stmt, from_table, + extra_froms): + return from_table._compiler_dispatch(self, asfrom=True, iscrud=True) + + def visit_delete(self, delete_stmt, asfrom=False, **kw): + toplevel = not self.stack + + crud._setup_crud_params(self, delete_stmt, crud.ISDELETE, **kw) + + extra_froms = delete_stmt._extra_froms + + correlate_froms = {delete_stmt.table}.union(extra_froms) + self.stack.append({'correlate_froms': correlate_froms, + "asfrom_froms": correlate_froms, + "selectable": delete_stmt}) + + text = "DELETE " + + if delete_stmt._prefixes: + text += self._generate_prefixes(delete_stmt, + delete_stmt._prefixes, **kw) + + text += "FROM " + table_text = self.delete_table_clause(delete_stmt, delete_stmt.table, + extra_froms) + + if delete_stmt._hints: + dialect_hints, table_text = self._setup_crud_hints( + delete_stmt, table_text) + else: + dialect_hints = None + + text += table_text + + if delete_stmt._returning: + if self.returning_precedes_values: + text += " " + self.returning_clause( + delete_stmt, delete_stmt._returning) + + if extra_froms: + extra_from_text = self.delete_extra_from_clause( + delete_stmt, + delete_stmt.table, + extra_froms, + dialect_hints, **kw) + if extra_from_text: + text += " " + extra_from_text + + if delete_stmt._whereclause is not None: + t = delete_stmt._whereclause._compiler_dispatch(self, **kw) + if t: + text += " WHERE " + t + + if delete_stmt._returning and not self.returning_precedes_values: + text += " " + self.returning_clause( + delete_stmt, delete_stmt._returning) + + if self.ctes and toplevel: + text = self._render_cte_clause() + text + + self.stack.pop(-1) + + if asfrom: + return "(" + text + ")" + else: + return text + + def visit_savepoint(self, savepoint_stmt): + return "SAVEPOINT %s" % self.preparer.format_savepoint(savepoint_stmt) + + def visit_rollback_to_savepoint(self, savepoint_stmt): + return "ROLLBACK TO SAVEPOINT %s" % \ + self.preparer.format_savepoint(savepoint_stmt) + + def visit_release_savepoint(self, savepoint_stmt): + return "RELEASE SAVEPOINT %s" % \ + self.preparer.format_savepoint(savepoint_stmt) + + +class StrSQLCompiler(SQLCompiler): + """"a compiler subclass with a few non-standard SQL features allowed. + + Used for stringification of SQL statements when a real dialect is not + available. + + """ + + def _fallback_column_name(self, column): + return "<name unknown>" + + def visit_getitem_binary(self, binary, operator, **kw): + return "%s[%s]" % ( + self.process(binary.left, **kw), + self.process(binary.right, **kw) + ) + + def visit_json_getitem_op_binary(self, binary, operator, **kw): + return self.visit_getitem_binary(binary, operator, **kw) + + def visit_json_path_getitem_op_binary(self, binary, operator, **kw): + return self.visit_getitem_binary(binary, operator, **kw) + + def returning_clause(self, stmt, returning_cols): + columns = [ + self._label_select_column(None, c, True, False, {}) + for c in elements._select_iterables(returning_cols) + ] + + return 'RETURNING ' + ', '.join(columns) + + def update_from_clause(self, update_stmt, + from_table, extra_froms, + from_hints, + **kw): + return "FROM " + ', '.join( + t._compiler_dispatch(self, asfrom=True, + fromhints=from_hints, **kw) + for t in extra_froms) + + def delete_extra_from_clause(self, update_stmt, + from_table, extra_froms, + from_hints, + **kw): + return ', ' + ', '.join( + t._compiler_dispatch(self, asfrom=True, + fromhints=from_hints, **kw) + for t in extra_froms) + + +class DDLCompiler(Compiled): + + @util.memoized_property + def sql_compiler(self): + return self.dialect.statement_compiler(self.dialect, None) + + @util.memoized_property + def type_compiler(self): + return self.dialect.type_compiler + + def construct_params(self, params=None): + return None + + def visit_ddl(self, ddl, **kwargs): + # table events can substitute table and schema name + context = ddl.context + if isinstance(ddl.target, schema.Table): + context = context.copy() + + preparer = self.preparer + path = preparer.format_table_seq(ddl.target) + if len(path) == 1: + table, sch = path[0], '' + else: + table, sch = path[-1], path[0] + + context.setdefault('table', table) + context.setdefault('schema', sch) + context.setdefault('fullname', preparer.format_table(ddl.target)) + + return self.sql_compiler.post_process_text(ddl.statement % context) + + def visit_create_schema(self, create): + schema = self.preparer.format_schema(create.element) + return "CREATE SCHEMA " + schema + + def visit_drop_schema(self, drop): + schema = self.preparer.format_schema(drop.element) + text = "DROP SCHEMA " + schema + if drop.cascade: + text += " CASCADE" + return text + + def visit_create_table(self, create): + table = create.element + preparer = self.preparer + + text = "\nCREATE " + if table._prefixes: + text += " ".join(table._prefixes) + " " + text += "TABLE " + preparer.format_table(table) + " " + + create_table_suffix = self.create_table_suffix(table) + if create_table_suffix: + text += create_table_suffix + " " + + text += "(" + + separator = "\n" + + # if only one primary key, specify it along with the column + first_pk = False + for create_column in create.columns: + column = create_column.element + try: + processed = self.process(create_column, + first_pk=column.primary_key + and not first_pk) + if processed is not None: + text += separator + separator = ", \n" + text += "\t" + processed + if column.primary_key: + first_pk = True + except exc.CompileError as ce: + util.raise_from_cause( + exc.CompileError( + util.u("(in table '%s', column '%s'): %s") % + (table.description, column.name, ce.args[0]) + )) + + const = self.create_table_constraints( + table, _include_foreign_key_constraints= # noqa + create.include_foreign_key_constraints) + if const: + text += separator + "\t" + const + + text += "\n)%s\n\n" % self.post_create_table(table) + return text + + def visit_create_column(self, create, first_pk=False): + column = create.element + + if column.system: + return None + + text = self.get_column_specification( + column, + first_pk=first_pk + ) + const = " ".join(self.process(constraint) + for constraint in column.constraints) + if const: + text += " " + const + + return text + + def create_table_constraints( + self, table, + _include_foreign_key_constraints=None): + + # On some DB order is significant: visit PK first, then the + # other constraints (engine.ReflectionTest.testbasic failed on FB2) + constraints = [] + if table.primary_key: + constraints.append(table.primary_key) + + all_fkcs = table.foreign_key_constraints + if _include_foreign_key_constraints is not None: + omit_fkcs = all_fkcs.difference(_include_foreign_key_constraints) + else: + omit_fkcs = set() + + constraints.extend([c for c in table._sorted_constraints + if c is not table.primary_key and + c not in omit_fkcs]) + + return ", \n\t".join( + p for p in + (self.process(constraint) + for constraint in constraints + if ( + constraint._create_rule is None or + constraint._create_rule(self)) + and ( + not self.dialect.supports_alter or + not getattr(constraint, 'use_alter', False) + )) if p is not None + ) + + def visit_drop_table(self, drop): + return "\nDROP TABLE " + self.preparer.format_table(drop.element) + + def visit_drop_view(self, drop): + return "\nDROP VIEW " + self.preparer.format_table(drop.element) + + def _verify_index_table(self, index): + if index.table is None: + raise exc.CompileError("Index '%s' is not associated " + "with any table." % index.name) + + def visit_create_index(self, create, include_schema=False, + include_table_schema=True): + index = create.element + self._verify_index_table(index) + preparer = self.preparer + text = "CREATE " + if index.unique: + text += "UNIQUE " + text += "INDEX %s ON %s (%s)" \ + % ( + self._prepared_index_name(index, + include_schema=include_schema), + preparer.format_table(index.table, + use_schema=include_table_schema), + ', '.join( + self.sql_compiler.process( + expr, include_table=False, literal_binds=True) for + expr in index.expressions) + ) + return text + + def visit_drop_index(self, drop): + index = drop.element + return "\nDROP INDEX " + self._prepared_index_name( + index, include_schema=True) + + def _prepared_index_name(self, index, include_schema=False): + if index.table is not None: + effective_schema = self.preparer.schema_for_object(index.table) + else: + effective_schema = None + if include_schema and effective_schema: + schema_name = self.preparer.quote_schema(effective_schema) + else: + schema_name = None + + ident = index.name + if isinstance(ident, elements._truncated_label): + max_ = self.dialect.max_index_name_length or \ + self.dialect.max_identifier_length + if len(ident) > max_: + ident = ident[0:max_ - 8] + \ + "_" + util.md5_hex(ident)[-4:] + else: + self.dialect.validate_identifier(ident) + + index_name = self.preparer.quote(ident) + + if schema_name: + index_name = schema_name + "." + index_name + return index_name + + def visit_add_constraint(self, create): + return "ALTER TABLE %s ADD %s" % ( + self.preparer.format_table(create.element.table), + self.process(create.element) + ) + + def visit_set_table_comment(self, create): + return "COMMENT ON TABLE %s IS %s" % ( + self.preparer.format_table(create.element), + self.sql_compiler.render_literal_value( + create.element.comment, sqltypes.String()) + ) + + def visit_drop_table_comment(self, drop): + return "COMMENT ON TABLE %s IS NULL" % \ + self.preparer.format_table(drop.element) + + def visit_set_column_comment(self, create): + return "COMMENT ON COLUMN %s IS %s" % ( + self.preparer.format_column( + create.element, use_table=True, use_schema=True), + self.sql_compiler.render_literal_value( + create.element.comment, sqltypes.String()) + ) + + def visit_drop_column_comment(self, drop): + return "COMMENT ON COLUMN %s IS NULL" % \ + self.preparer.format_column(drop.element, use_table=True) + + def visit_create_sequence(self, create): + text = "CREATE SEQUENCE %s" % \ + self.preparer.format_sequence(create.element) + if create.element.increment is not None: + text += " INCREMENT BY %d" % create.element.increment + if create.element.start is not None: + text += " START WITH %d" % create.element.start + if create.element.minvalue is not None: + text += " MINVALUE %d" % create.element.minvalue + if create.element.maxvalue is not None: + text += " MAXVALUE %d" % create.element.maxvalue + if create.element.nominvalue is not None: + text += " NO MINVALUE" + if create.element.nomaxvalue is not None: + text += " NO MAXVALUE" + if create.element.cache is not None: + text += " CACHE %d" % create.element.cache + if create.element.order is True: + text += " ORDER" + if create.element.cycle is not None: + text += " CYCLE" + return text + + def visit_drop_sequence(self, drop): + return "DROP SEQUENCE %s" % \ + self.preparer.format_sequence(drop.element) + + def visit_drop_constraint(self, drop): + constraint = drop.element + if constraint.name is not None: + formatted_name = self.preparer.format_constraint(constraint) + else: + formatted_name = None + + if formatted_name is None: + raise exc.CompileError( + "Can't emit DROP CONSTRAINT for constraint %r; " + "it has no name" % drop.element) + return "ALTER TABLE %s DROP CONSTRAINT %s%s" % ( + self.preparer.format_table(drop.element.table), + formatted_name, + drop.cascade and " CASCADE" or "" + ) + + def get_column_specification(self, column, **kwargs): + colspec = self.preparer.format_column(column) + " " + \ + self.dialect.type_compiler.process( + column.type, type_expression=column) + default = self.get_column_default_string(column) + if default is not None: + colspec += " DEFAULT " + default + + if not column.nullable: + colspec += " NOT NULL" + return colspec + + def create_table_suffix(self, table): + return '' + + def post_create_table(self, table): + return '' + + def get_column_default_string(self, column): + if isinstance(column.server_default, schema.DefaultClause): + if isinstance(column.server_default.arg, util.string_types): + return self.sql_compiler.render_literal_value( + column.server_default.arg, sqltypes.STRINGTYPE) + else: + return self.sql_compiler.process( + column.server_default.arg, literal_binds=True) + else: + return None + + def visit_check_constraint(self, constraint): + text = "" + if constraint.name is not None: + formatted_name = self.preparer.format_constraint(constraint) + if formatted_name is not None: + text += "CONSTRAINT %s " % formatted_name + text += "CHECK (%s)" % self.sql_compiler.process(constraint.sqltext, + include_table=False, + literal_binds=True) + text += self.define_constraint_deferrability(constraint) + return text + + def visit_column_check_constraint(self, constraint): + text = "" + if constraint.name is not None: + formatted_name = self.preparer.format_constraint(constraint) + if formatted_name is not None: + text += "CONSTRAINT %s " % formatted_name + text += "CHECK (%s)" % self.sql_compiler.process(constraint.sqltext, + include_table=False, + literal_binds=True) + text += self.define_constraint_deferrability(constraint) + return text + + def visit_primary_key_constraint(self, constraint): + if len(constraint) == 0: + return '' + text = "" + if constraint.name is not None: + formatted_name = self.preparer.format_constraint(constraint) + if formatted_name is not None: + text += "CONSTRAINT %s " % formatted_name + text += "PRIMARY KEY " + text += "(%s)" % ', '.join(self.preparer.quote(c.name) + for c in (constraint.columns_autoinc_first + if constraint._implicit_generated + else constraint.columns)) + text += self.define_constraint_deferrability(constraint) + return text + + def visit_foreign_key_constraint(self, constraint): + preparer = self.preparer + text = "" + if constraint.name is not None: + formatted_name = self.preparer.format_constraint(constraint) + if formatted_name is not None: + text += "CONSTRAINT %s " % formatted_name + remote_table = list(constraint.elements)[0].column.table + text += "FOREIGN KEY(%s) REFERENCES %s (%s)" % ( + ', '.join(preparer.quote(f.parent.name) + for f in constraint.elements), + self.define_constraint_remote_table( + constraint, remote_table, preparer), + ', '.join(preparer.quote(f.column.name) + for f in constraint.elements) + ) + text += self.define_constraint_match(constraint) + text += self.define_constraint_cascades(constraint) + text += self.define_constraint_deferrability(constraint) + return text + + def define_constraint_remote_table(self, constraint, table, preparer): + """Format the remote table clause of a CREATE CONSTRAINT clause.""" + + return preparer.format_table(table) + + def visit_unique_constraint(self, constraint): + if len(constraint) == 0: + return '' + text = "" + if constraint.name is not None: + formatted_name = self.preparer.format_constraint(constraint) + text += "CONSTRAINT %s " % formatted_name + text += "UNIQUE (%s)" % ( + ', '.join(self.preparer.quote(c.name) + for c in constraint)) + text += self.define_constraint_deferrability(constraint) + return text + + def define_constraint_cascades(self, constraint): + text = "" + if constraint.ondelete is not None: + text += " ON DELETE %s" % constraint.ondelete + if constraint.onupdate is not None: + text += " ON UPDATE %s" % constraint.onupdate + return text + + def define_constraint_deferrability(self, constraint): + text = "" + if constraint.deferrable is not None: + if constraint.deferrable: + text += " DEFERRABLE" + else: + text += " NOT DEFERRABLE" + if constraint.initially is not None: + text += " INITIALLY %s" % constraint.initially + return text + + def define_constraint_match(self, constraint): + text = "" + if constraint.match is not None: + text += " MATCH %s" % constraint.match + return text + + +class GenericTypeCompiler(TypeCompiler): + + def visit_FLOAT(self, type_, **kw): + return "FLOAT" + + def visit_REAL(self, type_, **kw): + return "REAL" + + def visit_NUMERIC(self, type_, **kw): + if type_.precision is None: + return "NUMERIC" + elif type_.scale is None: + return "NUMERIC(%(precision)s)" % \ + {'precision': type_.precision} + else: + return "NUMERIC(%(precision)s, %(scale)s)" % \ + {'precision': type_.precision, + 'scale': type_.scale} + + def visit_DECIMAL(self, type_, **kw): + if type_.precision is None: + return "DECIMAL" + elif type_.scale is None: + return "DECIMAL(%(precision)s)" % \ + {'precision': type_.precision} + else: + return "DECIMAL(%(precision)s, %(scale)s)" % \ + {'precision': type_.precision, + 'scale': type_.scale} + + def visit_INTEGER(self, type_, **kw): + return "INTEGER" + + def visit_SMALLINT(self, type_, **kw): + return "SMALLINT" + + def visit_BIGINT(self, type_, **kw): + return "BIGINT" + + def visit_TIMESTAMP(self, type_, **kw): + return 'TIMESTAMP' + + def visit_DATETIME(self, type_, **kw): + return "DATETIME" + + def visit_DATE(self, type_, **kw): + return "DATE" + + def visit_TIME(self, type_, **kw): + return "TIME" + + def visit_CLOB(self, type_, **kw): + return "CLOB" + + def visit_NCLOB(self, type_, **kw): + return "NCLOB" + + def _render_string_type(self, type_, name): + + text = name + if type_.length: + text += "(%d)" % type_.length + if type_.collation: + text += ' COLLATE "%s"' % type_.collation + return text + + def visit_CHAR(self, type_, **kw): + return self._render_string_type(type_, "CHAR") + + def visit_NCHAR(self, type_, **kw): + return self._render_string_type(type_, "NCHAR") + + def visit_VARCHAR(self, type_, **kw): + return self._render_string_type(type_, "VARCHAR") + + def visit_NVARCHAR(self, type_, **kw): + return self._render_string_type(type_, "NVARCHAR") + + def visit_TEXT(self, type_, **kw): + return self._render_string_type(type_, "TEXT") + + def visit_BLOB(self, type_, **kw): + return "BLOB" + + def visit_BINARY(self, type_, **kw): + return "BINARY" + (type_.length and "(%d)" % type_.length or "") + + def visit_VARBINARY(self, type_, **kw): + return "VARBINARY" + (type_.length and "(%d)" % type_.length or "") + + def visit_BOOLEAN(self, type_, **kw): + return "BOOLEAN" + + def visit_large_binary(self, type_, **kw): + return self.visit_BLOB(type_, **kw) + + def visit_boolean(self, type_, **kw): + return self.visit_BOOLEAN(type_, **kw) + + def visit_time(self, type_, **kw): + return self.visit_TIME(type_, **kw) + + def visit_datetime(self, type_, **kw): + return self.visit_DATETIME(type_, **kw) + + def visit_date(self, type_, **kw): + return self.visit_DATE(type_, **kw) + + def visit_big_integer(self, type_, **kw): + return self.visit_BIGINT(type_, **kw) + + def visit_small_integer(self, type_, **kw): + return self.visit_SMALLINT(type_, **kw) + + def visit_integer(self, type_, **kw): + return self.visit_INTEGER(type_, **kw) + + def visit_real(self, type_, **kw): + return self.visit_REAL(type_, **kw) + + def visit_float(self, type_, **kw): + return self.visit_FLOAT(type_, **kw) + + def visit_numeric(self, type_, **kw): + return self.visit_NUMERIC(type_, **kw) + + def visit_string(self, type_, **kw): + return self.visit_VARCHAR(type_, **kw) + + def visit_unicode(self, type_, **kw): + return self.visit_VARCHAR(type_, **kw) + + def visit_text(self, type_, **kw): + return self.visit_TEXT(type_, **kw) + + def visit_unicode_text(self, type_, **kw): + return self.visit_TEXT(type_, **kw) + + def visit_enum(self, type_, **kw): + return self.visit_VARCHAR(type_, **kw) + + def visit_null(self, type_, **kw): + raise exc.CompileError("Can't generate DDL for %r; " + "did you forget to specify a " + "type on this Column?" % type_) + + def visit_type_decorator(self, type_, **kw): + return self.process(type_.type_engine(self.dialect), **kw) + + def visit_user_defined(self, type_, **kw): + return type_.get_col_spec(**kw) + + +class StrSQLTypeCompiler(GenericTypeCompiler): + def __getattr__(self, key): + if key.startswith("visit_"): + return self._visit_unknown + else: + raise AttributeError(key) + + def _visit_unknown(self, type_, **kw): + return "%s" % type_.__class__.__name__ + + +class IdentifierPreparer(object): + + """Handle quoting and case-folding of identifiers based on options.""" + + reserved_words = RESERVED_WORDS + + legal_characters = LEGAL_CHARACTERS + + illegal_initial_characters = ILLEGAL_INITIAL_CHARACTERS + + schema_for_object = schema._schema_getter(None) + + def __init__(self, dialect, initial_quote='"', + final_quote=None, escape_quote='"', + quote_case_sensitive_collations=True, omit_schema=False): + """Construct a new ``IdentifierPreparer`` object. + + initial_quote + Character that begins a delimited identifier. + + final_quote + Character that ends a delimited identifier. Defaults to + `initial_quote`. + + omit_schema + Prevent prepending schema name. Useful for databases that do + not support schemae. + """ + + self.dialect = dialect + self.initial_quote = initial_quote + self.final_quote = final_quote or self.initial_quote + self.escape_quote = escape_quote + self.escape_to_quote = self.escape_quote * 2 + self.omit_schema = omit_schema + self.quote_case_sensitive_collations = quote_case_sensitive_collations + self._strings = {} + self._double_percents = self.dialect.paramstyle in ('format', 'pyformat') + + def _with_schema_translate(self, schema_translate_map): + prep = self.__class__.__new__(self.__class__) + prep.__dict__.update(self.__dict__) + prep.schema_for_object = schema._schema_getter(schema_translate_map) + return prep + + def _escape_identifier(self, value): + """Escape an identifier. + + Subclasses should override this to provide database-dependent + escaping behavior. + """ + + value = value.replace(self.escape_quote, self.escape_to_quote) + if self._double_percents: + value = value.replace('%', '%%') + return value + + def _unescape_identifier(self, value): + """Canonicalize an escaped identifier. + + Subclasses should override this to provide database-dependent + unescaping behavior that reverses _escape_identifier. + """ + + return value.replace(self.escape_to_quote, self.escape_quote) + + def quote_identifier(self, value): + """Quote an identifier. + + Subclasses should override this to provide database-dependent + quoting behavior. + """ + + return self.initial_quote + \ + self._escape_identifier(value) + \ + self.final_quote + + def _requires_quotes(self, value): + """Return True if the given identifier requires quoting.""" + lc_value = value.lower() + return (lc_value in self.reserved_words + or value[0] in self.illegal_initial_characters + or not self.legal_characters.match(util.text_type(value)) + or (lc_value != value)) + + def quote_schema(self, schema, force=None): + """Conditionally quote a schema. + + Subclasses can override this to provide database-dependent + quoting behavior for schema names. + + the 'force' flag should be considered deprecated. + + """ + return self.quote(schema, force) + + def quote(self, ident, force=None): + """Conditionally quote an identifier. + + the 'force' flag should be considered deprecated. + """ + + force = getattr(ident, "quote", None) + + if force is None: + if ident in self._strings: + return self._strings[ident] + else: + if self._requires_quotes(ident): + self._strings[ident] = self.quote_identifier(ident) + else: + self._strings[ident] = ident + return self._strings[ident] + elif force: + return self.quote_identifier(ident) + else: + return ident + + def format_collation(self, collation_name): + if self.quote_case_sensitive_collations: + return self.quote(collation_name) + else: + return collation_name + + def format_sequence(self, sequence, use_schema=True): + name = self.quote(sequence.name) + + effective_schema = self.schema_for_object(sequence) + + if (not self.omit_schema and use_schema and + effective_schema is not None): + name = self.quote_schema(effective_schema) + "." + name + return name + + def format_label(self, label, name=None): + return self.quote(name or label.name) + + def format_alias(self, alias, name=None): + return self.quote(name or alias.name) + + def format_savepoint(self, savepoint, name=None): + # Running the savepoint name through quoting is unnecessary + # for all known dialects. This is here to support potential + # third party use cases + ident = name or savepoint.ident + if self._requires_quotes(ident): + ident = self.quote_identifier(ident) + return ident + + @util.dependencies("sqlalchemy.sql.naming") + def format_constraint(self, naming, constraint): + if isinstance(constraint.name, elements._defer_name): + name = naming._constraint_name_for_table( + constraint, constraint.table) + if name: + return self.quote(name) + elif isinstance(constraint.name, elements._defer_none_name): + return None + return self.quote(constraint.name) + + def format_table(self, table, use_schema=True, name=None): + """Prepare a quoted table and schema name.""" + + if name is None: + name = table.name + result = self.quote(name) + + effective_schema = self.schema_for_object(table) + + if not self.omit_schema and use_schema \ + and effective_schema: + result = self.quote_schema(effective_schema) + "." + result + return result + + def format_schema(self, name, quote=None): + """Prepare a quoted schema name.""" + + return self.quote(name, quote) + + def format_column(self, column, use_table=False, + name=None, table_name=None, use_schema=False): + """Prepare a quoted column name.""" + + if name is None: + name = column.name + if not getattr(column, 'is_literal', False): + if use_table: + return self.format_table( + column.table, use_schema=use_schema, + name=table_name) + "." + self.quote(name) + else: + return self.quote(name) + else: + # literal textual elements get stuck into ColumnClause a lot, + # which shouldn't get quoted + + if use_table: + return self.format_table( + column.table, use_schema=use_schema, + name=table_name) + '.' + name + else: + return name + + def format_table_seq(self, table, use_schema=True): + """Format table name and schema as a tuple.""" + + # Dialects with more levels in their fully qualified references + # ('database', 'owner', etc.) could override this and return + # a longer sequence. + + effective_schema = self.schema_for_object(table) + + if not self.omit_schema and use_schema and \ + effective_schema: + return (self.quote_schema(effective_schema), + self.format_table(table, use_schema=False)) + else: + return (self.format_table(table, use_schema=False), ) + + @util.memoized_property + def _r_identifiers(self): + initial, final, escaped_final = \ + [re.escape(s) for s in + (self.initial_quote, self.final_quote, + self._escape_identifier(self.final_quote))] + r = re.compile( + r'(?:' + r'(?:%(initial)s((?:%(escaped)s|[^%(final)s])+)%(final)s' + r'|([^\.]+))(?=\.|$))+' % + {'initial': initial, + 'final': final, + 'escaped': escaped_final}) + return r + + def unformat_identifiers(self, identifiers): + """Unpack 'schema.table.column'-like strings into components.""" + + r = self._r_identifiers + return [self._unescape_identifier(i) + for i in [a or b for a, b in r.findall(identifiers)]] diff --git a/venv/Lib/site-packages/sqlalchemy/sql/crud.py b/venv/Lib/site-packages/sqlalchemy/sql/crud.py new file mode 100644 index 0000000..999d48a --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/crud.py @@ -0,0 +1,700 @@ +# sql/crud.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Functions used by compiler.py to determine the parameters rendered +within INSERT and UPDATE statements. + +""" +from .. import util +from .. import exc +from . import dml +from . import elements +import operator + +REQUIRED = util.symbol('REQUIRED', """ +Placeholder for the value within a :class:`.BindParameter` +which is required to be present when the statement is passed +to :meth:`.Connection.execute`. + +This symbol is typically used when a :func:`.expression.insert` +or :func:`.expression.update` statement is compiled without parameter +values present. + +""") + +ISINSERT = util.symbol('ISINSERT') +ISUPDATE = util.symbol('ISUPDATE') +ISDELETE = util.symbol('ISDELETE') + + +def _setup_crud_params(compiler, stmt, local_stmt_type, **kw): + restore_isinsert = compiler.isinsert + restore_isupdate = compiler.isupdate + restore_isdelete = compiler.isdelete + + should_restore = ( + restore_isinsert or restore_isupdate or restore_isdelete + ) or len(compiler.stack) > 1 + + if local_stmt_type is ISINSERT: + compiler.isupdate = False + compiler.isinsert = True + elif local_stmt_type is ISUPDATE: + compiler.isupdate = True + compiler.isinsert = False + elif local_stmt_type is ISDELETE: + if not should_restore: + compiler.isdelete = True + else: + assert False, "ISINSERT, ISUPDATE, or ISDELETE expected" + + try: + if local_stmt_type in (ISINSERT, ISUPDATE): + return _get_crud_params(compiler, stmt, **kw) + finally: + if should_restore: + compiler.isinsert = restore_isinsert + compiler.isupdate = restore_isupdate + compiler.isdelete = restore_isdelete + + +def _get_crud_params(compiler, stmt, **kw): + """create a set of tuples representing column/string pairs for use + in an INSERT or UPDATE statement. + + Also generates the Compiled object's postfetch, prefetch, and + returning column collections, used for default handling and ultimately + populating the ResultProxy's prefetch_cols() and postfetch_cols() + collections. + + """ + + compiler.postfetch = [] + compiler.insert_prefetch = [] + compiler.update_prefetch = [] + compiler.returning = [] + + # no parameters in the statement, no parameters in the + # compiled params - return binds for all columns + if compiler.column_keys is None and stmt.parameters is None: + return [ + (c, _create_bind_param( + compiler, c, None, required=True)) + for c in stmt.table.columns + ] + + if stmt._has_multi_parameters: + stmt_parameters = stmt.parameters[0] + else: + stmt_parameters = stmt.parameters + + # getters - these are normally just column.key, + # but in the case of mysql multi-table update, the rules for + # .key must conditionally take tablename into account + _column_as_key, _getattr_col_key, _col_bind_name = \ + _key_getters_for_crud_column(compiler, stmt) + + # if we have statement parameters - set defaults in the + # compiled params + if compiler.column_keys is None: + parameters = {} + else: + parameters = dict((_column_as_key(key), REQUIRED) + for key in compiler.column_keys + if not stmt_parameters or + key not in stmt_parameters) + + # create a list of column assignment clauses as tuples + values = [] + + if stmt_parameters is not None: + _get_stmt_parameters_params( + compiler, + parameters, stmt_parameters, _column_as_key, values, kw) + + check_columns = {} + + # special logic that only occurs for multi-table UPDATE + # statements + if compiler.isupdate and stmt._extra_froms and stmt_parameters: + _get_multitable_params( + compiler, stmt, stmt_parameters, check_columns, + _col_bind_name, _getattr_col_key, values, kw) + + if compiler.isinsert and stmt.select_names: + _scan_insert_from_select_cols( + compiler, stmt, parameters, + _getattr_col_key, _column_as_key, + _col_bind_name, check_columns, values, kw) + else: + _scan_cols( + compiler, stmt, parameters, + _getattr_col_key, _column_as_key, + _col_bind_name, check_columns, values, kw) + + if parameters and stmt_parameters: + check = set(parameters).intersection( + _column_as_key(k) for k in stmt_parameters + ).difference(check_columns) + if check: + raise exc.CompileError( + "Unconsumed column names: %s" % + (", ".join("%s" % c for c in check)) + ) + + if stmt._has_multi_parameters: + values = _extend_values_for_multiparams(compiler, stmt, values, kw) + + return values + + +def _create_bind_param( + compiler, col, value, process=True, + required=False, name=None, **kw): + if name is None: + name = col.key + bindparam = elements.BindParameter( + name, value, type_=col.type, required=required) + bindparam._is_crud = True + if process: + bindparam = bindparam._compiler_dispatch(compiler, **kw) + return bindparam + + +def _key_getters_for_crud_column(compiler, stmt): + if compiler.isupdate and stmt._extra_froms: + # when extra tables are present, refer to the columns + # in those extra tables as table-qualified, including in + # dictionaries and when rendering bind param names. + # the "main" table of the statement remains unqualified, + # allowing the most compatibility with a non-multi-table + # statement. + _et = set(stmt._extra_froms) + + def _column_as_key(key): + str_key = elements._column_as_key(key) + if hasattr(key, 'table') and key.table in _et: + return (key.table.name, str_key) + else: + return str_key + + def _getattr_col_key(col): + if col.table in _et: + return (col.table.name, col.key) + else: + return col.key + + def _col_bind_name(col): + if col.table in _et: + return "%s_%s" % (col.table.name, col.key) + else: + return col.key + + else: + _column_as_key = elements._column_as_key + _getattr_col_key = _col_bind_name = operator.attrgetter("key") + + return _column_as_key, _getattr_col_key, _col_bind_name + + +def _scan_insert_from_select_cols( + compiler, stmt, parameters, _getattr_col_key, + _column_as_key, _col_bind_name, check_columns, values, kw): + + need_pks, implicit_returning, \ + implicit_return_defaults, postfetch_lastrowid = \ + _get_returning_modifiers(compiler, stmt) + + cols = [stmt.table.c[_column_as_key(name)] + for name in stmt.select_names] + + compiler._insert_from_select = stmt.select + + add_select_cols = [] + if stmt.include_insert_from_select_defaults: + col_set = set(cols) + for col in stmt.table.columns: + if col not in col_set and col.default: + cols.append(col) + + for c in cols: + col_key = _getattr_col_key(c) + if col_key in parameters and col_key not in check_columns: + parameters.pop(col_key) + values.append((c, None)) + else: + _append_param_insert_select_hasdefault( + compiler, stmt, c, add_select_cols, kw) + + if add_select_cols: + values.extend(add_select_cols) + compiler._insert_from_select = compiler._insert_from_select._generate() + compiler._insert_from_select._raw_columns = \ + tuple(compiler._insert_from_select._raw_columns) + tuple( + expr for col, expr in add_select_cols) + + +def _scan_cols( + compiler, stmt, parameters, _getattr_col_key, + _column_as_key, _col_bind_name, check_columns, values, kw): + + need_pks, implicit_returning, \ + implicit_return_defaults, postfetch_lastrowid = \ + _get_returning_modifiers(compiler, stmt) + + if stmt._parameter_ordering: + parameter_ordering = [ + _column_as_key(key) for key in stmt._parameter_ordering + ] + ordered_keys = set(parameter_ordering) + cols = [ + stmt.table.c[key] for key in parameter_ordering + ] + [ + c for c in stmt.table.c if c.key not in ordered_keys + ] + else: + cols = stmt.table.columns + + for c in cols: + col_key = _getattr_col_key(c) + + if col_key in parameters and col_key not in check_columns: + + _append_param_parameter( + compiler, stmt, c, col_key, parameters, _col_bind_name, + implicit_returning, implicit_return_defaults, values, kw) + + elif compiler.isinsert: + if c.primary_key and \ + need_pks and \ + ( + implicit_returning or + not postfetch_lastrowid or + c is not stmt.table._autoincrement_column + ): + + if implicit_returning: + _append_param_insert_pk_returning( + compiler, stmt, c, values, kw) + else: + _append_param_insert_pk(compiler, stmt, c, values, kw) + + elif c.default is not None: + + _append_param_insert_hasdefault( + compiler, stmt, c, implicit_return_defaults, + values, kw) + + elif c.server_default is not None: + if implicit_return_defaults and \ + c in implicit_return_defaults: + compiler.returning.append(c) + elif not c.primary_key: + compiler.postfetch.append(c) + elif implicit_return_defaults and \ + c in implicit_return_defaults: + compiler.returning.append(c) + elif c.primary_key and \ + c is not stmt.table._autoincrement_column and \ + not c.nullable: + _warn_pk_with_no_anticipated_value(c) + + elif compiler.isupdate: + _append_param_update( + compiler, stmt, c, implicit_return_defaults, values, kw) + + +def _append_param_parameter( + compiler, stmt, c, col_key, parameters, _col_bind_name, + implicit_returning, implicit_return_defaults, values, kw): + value = parameters.pop(col_key) + if elements._is_literal(value): + value = _create_bind_param( + compiler, c, value, required=value is REQUIRED, + name=_col_bind_name(c) + if not stmt._has_multi_parameters + else "%s_m0" % _col_bind_name(c), + **kw + ) + else: + if isinstance(value, elements.BindParameter) and \ + value.type._isnull: + value = value._clone() + value.type = c.type + + if c.primary_key and implicit_returning: + compiler.returning.append(c) + value = compiler.process(value.self_group(), **kw) + elif implicit_return_defaults and \ + c in implicit_return_defaults: + compiler.returning.append(c) + value = compiler.process(value.self_group(), **kw) + else: + compiler.postfetch.append(c) + value = compiler.process(value.self_group(), **kw) + values.append((c, value)) + + +def _append_param_insert_pk_returning(compiler, stmt, c, values, kw): + """Create a primary key expression in the INSERT statement and + possibly a RETURNING clause for it. + + If the column has a Python-side default, we will create a bound + parameter for it and "pre-execute" the Python function. If + the column has a SQL expression default, or is a sequence, + we will add it directly into the INSERT statement and add a + RETURNING element to get the new value. If the column has a + server side default or is marked as the "autoincrement" column, + we will add a RETRUNING element to get at the value. + + If all the above tests fail, that indicates a primary key column with no + noted default generation capabilities that has no parameter passed; + raise an exception. + + """ + if c.default is not None: + if c.default.is_sequence: + if compiler.dialect.supports_sequences and \ + (not c.default.optional or + not compiler.dialect.sequences_optional): + proc = compiler.process(c.default, **kw) + values.append((c, proc)) + compiler.returning.append(c) + elif c.default.is_clause_element: + values.append( + (c, compiler.process( + c.default.arg.self_group(), **kw)) + ) + compiler.returning.append(c) + else: + values.append( + (c, _create_insert_prefetch_bind_param(compiler, c)) + ) + elif c is stmt.table._autoincrement_column or c.server_default is not None: + compiler.returning.append(c) + elif not c.nullable: + # no .default, no .server_default, not autoincrement, we have + # no indication this primary key column will have any value + _warn_pk_with_no_anticipated_value(c) + + +def _create_insert_prefetch_bind_param(compiler, c, process=True, name=None): + param = _create_bind_param(compiler, c, None, process=process, name=name) + compiler.insert_prefetch.append(c) + return param + + +def _create_update_prefetch_bind_param(compiler, c, process=True, name=None): + param = _create_bind_param(compiler, c, None, process=process, name=name) + compiler.update_prefetch.append(c) + return param + + +class _multiparam_column(elements.ColumnElement): + _is_multiparam_column = True + + def __init__(self, original, index): + self.index = index + self.key = "%s_m%d" % (original.key, index + 1) + self.original = original + self.default = original.default + self.type = original.type + + def __eq__(self, other): + return isinstance(other, _multiparam_column) and \ + other.key == self.key and \ + other.original == self.original + + +def _process_multiparam_default_bind(compiler, stmt, c, index, kw): + + if not c.default: + raise exc.CompileError( + "INSERT value for column %s is explicitly rendered as a bound" + "parameter in the VALUES clause; " + "a Python-side value or SQL expression is required" % c) + elif c.default.is_clause_element: + return compiler.process(c.default.arg.self_group(), **kw) + else: + col = _multiparam_column(c, index) + if isinstance(stmt, dml.Insert): + return _create_insert_prefetch_bind_param(compiler, col) + else: + return _create_update_prefetch_bind_param(compiler, col) + + +def _append_param_insert_pk(compiler, stmt, c, values, kw): + """Create a bound parameter in the INSERT statement to receive a + 'prefetched' default value. + + The 'prefetched' value indicates that we are to invoke a Python-side + default function or expliclt SQL expression before the INSERT statement + proceeds, so that we have a primary key value available. + + if the column has no noted default generation capabilities, it has + no value passed in either; raise an exception. + + """ + if ( + ( + # column has a Python-side default + c.default is not None and + ( + # and it won't be a Sequence + not c.default.is_sequence or + compiler.dialect.supports_sequences + ) + ) + or + ( + # column is the "autoincrement column" + c is stmt.table._autoincrement_column and + ( + # and it's either a "sequence" or a + # pre-executable "autoincrement" sequence + compiler.dialect.supports_sequences or + compiler.dialect.preexecute_autoincrement_sequences + ) + ) + ): + values.append( + (c, _create_insert_prefetch_bind_param(compiler, c)) + ) + elif c.default is None and c.server_default is None and not c.nullable: + # no .default, no .server_default, not autoincrement, we have + # no indication this primary key column will have any value + _warn_pk_with_no_anticipated_value(c) + + +def _append_param_insert_hasdefault( + compiler, stmt, c, implicit_return_defaults, values, kw): + + if c.default.is_sequence: + if compiler.dialect.supports_sequences and \ + (not c.default.optional or + not compiler.dialect.sequences_optional): + proc = compiler.process(c.default, **kw) + values.append((c, proc)) + if implicit_return_defaults and \ + c in implicit_return_defaults: + compiler.returning.append(c) + elif not c.primary_key: + compiler.postfetch.append(c) + elif c.default.is_clause_element: + proc = compiler.process(c.default.arg.self_group(), **kw) + values.append((c, proc)) + + if implicit_return_defaults and \ + c in implicit_return_defaults: + compiler.returning.append(c) + elif not c.primary_key: + # don't add primary key column to postfetch + compiler.postfetch.append(c) + else: + values.append( + (c, _create_insert_prefetch_bind_param(compiler, c)) + ) + + +def _append_param_insert_select_hasdefault( + compiler, stmt, c, values, kw): + + if c.default.is_sequence: + if compiler.dialect.supports_sequences and \ + (not c.default.optional or + not compiler.dialect.sequences_optional): + proc = c.default + values.append((c, proc.next_value())) + elif c.default.is_clause_element: + proc = c.default.arg.self_group() + values.append((c, proc)) + else: + values.append( + (c, _create_insert_prefetch_bind_param(compiler, c, process=False)) + ) + + +def _append_param_update( + compiler, stmt, c, implicit_return_defaults, values, kw): + + if c.onupdate is not None and not c.onupdate.is_sequence: + if c.onupdate.is_clause_element: + values.append( + (c, compiler.process( + c.onupdate.arg.self_group(), **kw)) + ) + if implicit_return_defaults and \ + c in implicit_return_defaults: + compiler.returning.append(c) + else: + compiler.postfetch.append(c) + else: + values.append( + (c, _create_update_prefetch_bind_param(compiler, c)) + ) + elif c.server_onupdate is not None: + if implicit_return_defaults and \ + c in implicit_return_defaults: + compiler.returning.append(c) + else: + compiler.postfetch.append(c) + elif implicit_return_defaults and \ + stmt._return_defaults is not True and \ + c in implicit_return_defaults: + compiler.returning.append(c) + + +def _get_multitable_params( + compiler, stmt, stmt_parameters, check_columns, + _col_bind_name, _getattr_col_key, values, kw): + + normalized_params = dict( + (elements._clause_element_as_expr(c), param) + for c, param in stmt_parameters.items() + ) + affected_tables = set() + for t in stmt._extra_froms: + for c in t.c: + if c in normalized_params: + affected_tables.add(t) + check_columns[_getattr_col_key(c)] = c + value = normalized_params[c] + if elements._is_literal(value): + value = _create_bind_param( + compiler, c, value, required=value is REQUIRED, + name=_col_bind_name(c)) + else: + compiler.postfetch.append(c) + value = compiler.process(value.self_group(), **kw) + values.append((c, value)) + # determine tables which are actually to be updated - process onupdate + # and server_onupdate for these + for t in affected_tables: + for c in t.c: + if c in normalized_params: + continue + elif (c.onupdate is not None and not + c.onupdate.is_sequence): + if c.onupdate.is_clause_element: + values.append( + (c, compiler.process( + c.onupdate.arg.self_group(), + **kw) + ) + ) + compiler.postfetch.append(c) + else: + values.append( + (c, _create_update_prefetch_bind_param( + compiler, c, name=_col_bind_name(c))) + ) + elif c.server_onupdate is not None: + compiler.postfetch.append(c) + + +def _extend_values_for_multiparams(compiler, stmt, values, kw): + values_0 = values + values = [values] + + for i, row in enumerate(stmt.parameters[1:]): + extension = [] + for (col, param) in values_0: + if col in row or col.key in row: + key = col if col in row else col.key + + if elements._is_literal(row[key]): + new_param = _create_bind_param( + compiler, col, row[key], + name="%s_m%d" % (col.key, i + 1), **kw + ) + else: + new_param = compiler.process(row[key].self_group(), **kw) + else: + new_param = _process_multiparam_default_bind( + compiler, stmt, col, i, kw + ) + + extension.append((col, new_param)) + + values.append(extension) + + return values + + +def _get_stmt_parameters_params( + compiler, parameters, stmt_parameters, _column_as_key, values, kw): + for k, v in stmt_parameters.items(): + colkey = _column_as_key(k) + if colkey is not None: + parameters.setdefault(colkey, v) + else: + # a non-Column expression on the left side; + # add it to values() in an "as-is" state, + # coercing right side to bound param + if elements._is_literal(v): + v = compiler.process( + elements.BindParameter(None, v, type_=k.type), + **kw) + else: + v = compiler.process(v.self_group(), **kw) + + values.append((k, v)) + + +def _get_returning_modifiers(compiler, stmt): + need_pks = compiler.isinsert and \ + not compiler.inline and \ + not stmt._returning and \ + not stmt._has_multi_parameters + + implicit_returning = need_pks and \ + compiler.dialect.implicit_returning and \ + stmt.table.implicit_returning + + if compiler.isinsert: + implicit_return_defaults = (implicit_returning and + stmt._return_defaults) + elif compiler.isupdate: + implicit_return_defaults = (compiler.dialect.implicit_returning and + stmt.table.implicit_returning and + stmt._return_defaults) + else: + # this line is unused, currently we are always + # isinsert or isupdate + implicit_return_defaults = False # pragma: no cover + + if implicit_return_defaults: + if stmt._return_defaults is True: + implicit_return_defaults = set(stmt.table.c) + else: + implicit_return_defaults = set(stmt._return_defaults) + + postfetch_lastrowid = need_pks and compiler.dialect.postfetch_lastrowid + + return need_pks, implicit_returning, \ + implicit_return_defaults, postfetch_lastrowid + + +def _warn_pk_with_no_anticipated_value(c): + msg = ( + "Column '%s.%s' is marked as a member of the " + "primary key for table '%s', " + "but has no Python-side or server-side default generator indicated, " + "nor does it indicate 'autoincrement=True' or 'nullable=True', " + "and no explicit value is passed. " + "Primary key columns typically may not store NULL." + % + (c.table.fullname, c.name, c.table.fullname)) + if len(c.table.primary_key) > 1: + msg += ( + " Note that as of SQLAlchemy 1.1, 'autoincrement=True' must be " + "indicated explicitly for composite (e.g. multicolumn) primary " + "keys if AUTO_INCREMENT/SERIAL/IDENTITY " + "behavior is expected for one of the columns in the primary key. " + "CREATE TABLE statements are impacted by this change as well on " + "most backends.") + util.warn(msg) diff --git a/venv/Lib/site-packages/sqlalchemy/sql/ddl.py b/venv/Lib/site-packages/sqlalchemy/sql/ddl.py new file mode 100644 index 0000000..91e93ef --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/ddl.py @@ -0,0 +1,1138 @@ +# sql/ddl.py +# Copyright (C) 2009-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +""" +Provides the hierarchy of DDL-defining schema items as well as routines +to invoke them for a create/drop call. + +""" + +from .. import util +from .elements import ClauseElement +from .base import Executable, _generative, SchemaVisitor, _bind_or_error +from ..util import topological +from .. import event +from .. import exc + + +class _DDLCompiles(ClauseElement): + def _compiler(self, dialect, **kw): + """Return a compiler appropriate for this ClauseElement, given a + Dialect.""" + + return dialect.ddl_compiler(dialect, self, **kw) + + +class DDLElement(Executable, _DDLCompiles): + """Base class for DDL expression constructs. + + This class is the base for the general purpose :class:`.DDL` class, + as well as the various create/drop clause constructs such as + :class:`.CreateTable`, :class:`.DropTable`, :class:`.AddConstraint`, + etc. + + :class:`.DDLElement` integrates closely with SQLAlchemy events, + introduced in :ref:`event_toplevel`. An instance of one is + itself an event receiving callable:: + + event.listen( + users, + 'after_create', + AddConstraint(constraint).execute_if(dialect='postgresql') + ) + + .. seealso:: + + :class:`.DDL` + + :class:`.DDLEvents` + + :ref:`event_toplevel` + + :ref:`schema_ddl_sequences` + + """ + + _execution_options = Executable.\ + _execution_options.union({'autocommit': True}) + + target = None + on = None + dialect = None + callable_ = None + + def _execute_on_connection(self, connection, multiparams, params): + return connection._execute_ddl(self, multiparams, params) + + def execute(self, bind=None, target=None): + """Execute this DDL immediately. + + Executes the DDL statement in isolation using the supplied + :class:`.Connectable` or + :class:`.Connectable` assigned to the ``.bind`` + property, if not supplied. If the DDL has a conditional ``on`` + criteria, it will be invoked with None as the event. + + :param bind: + Optional, an ``Engine`` or ``Connection``. If not supplied, a valid + :class:`.Connectable` must be present in the + ``.bind`` property. + + :param target: + Optional, defaults to None. The target SchemaItem for the + execute call. Will be passed to the ``on`` callable if any, + and may also provide string expansion data for the + statement. See ``execute_at`` for more information. + + """ + + if bind is None: + bind = _bind_or_error(self) + + if self._should_execute(target, bind): + return bind.execute(self.against(target)) + else: + bind.engine.logger.info( + "DDL execution skipped, criteria not met.") + + @util.deprecated("0.7", "See :class:`.DDLEvents`, as well as " + ":meth:`.DDLElement.execute_if`.") + def execute_at(self, event_name, target): + """Link execution of this DDL to the DDL lifecycle of a SchemaItem. + + Links this ``DDLElement`` to a ``Table`` or ``MetaData`` instance, + executing it when that schema item is created or dropped. The DDL + statement will be executed using the same Connection and transactional + context as the Table create/drop itself. The ``.bind`` property of + this statement is ignored. + + :param event: + One of the events defined in the schema item's ``.ddl_events``; + e.g. 'before-create', 'after-create', 'before-drop' or 'after-drop' + + :param target: + The Table or MetaData instance for which this DDLElement will + be associated with. + + A DDLElement instance can be linked to any number of schema items. + + ``execute_at`` builds on the ``append_ddl_listener`` interface of + :class:`.MetaData` and :class:`.Table` objects. + + Caveat: Creating or dropping a Table in isolation will also trigger + any DDL set to ``execute_at`` that Table's MetaData. This may change + in a future release. + + """ + + def call_event(target, connection, **kw): + if self._should_execute_deprecated(event_name, + target, connection, **kw): + return connection.execute(self.against(target)) + + event.listen(target, "" + event_name.replace('-', '_'), call_event) + + @_generative + def against(self, target): + """Return a copy of this DDL against a specific schema item.""" + + self.target = target + + @_generative + def execute_if(self, dialect=None, callable_=None, state=None): + r"""Return a callable that will execute this + DDLElement conditionally. + + Used to provide a wrapper for event listening:: + + event.listen( + metadata, + 'before_create', + DDL("my_ddl").execute_if(dialect='postgresql') + ) + + :param dialect: May be a string, tuple or a callable + predicate. If a string, it will be compared to the name of the + executing database dialect:: + + DDL('something').execute_if(dialect='postgresql') + + If a tuple, specifies multiple dialect names:: + + DDL('something').execute_if(dialect=('postgresql', 'mysql')) + + :param callable_: A callable, which will be invoked with + four positional arguments as well as optional keyword + arguments: + + :ddl: + This DDL element. + + :target: + The :class:`.Table` or :class:`.MetaData` object which is the + target of this event. May be None if the DDL is executed + explicitly. + + :bind: + The :class:`.Connection` being used for DDL execution + + :tables: + Optional keyword argument - a list of Table objects which are to + be created/ dropped within a MetaData.create_all() or drop_all() + method call. + + :state: + Optional keyword argument - will be the ``state`` argument + passed to this function. + + :checkfirst: + Keyword argument, will be True if the 'checkfirst' flag was + set during the call to ``create()``, ``create_all()``, + ``drop()``, ``drop_all()``. + + If the callable returns a true value, the DDL statement will be + executed. + + :param state: any value which will be passed to the callable\_ + as the ``state`` keyword argument. + + .. seealso:: + + :class:`.DDLEvents` + + :ref:`event_toplevel` + + """ + self.dialect = dialect + self.callable_ = callable_ + self.state = state + + def _should_execute(self, target, bind, **kw): + if self.on is not None and \ + not self._should_execute_deprecated(None, target, bind, **kw): + return False + + if isinstance(self.dialect, util.string_types): + if self.dialect != bind.engine.name: + return False + elif isinstance(self.dialect, (tuple, list, set)): + if bind.engine.name not in self.dialect: + return False + if (self.callable_ is not None and + not self.callable_(self, target, bind, + state=self.state, **kw)): + return False + + return True + + def _should_execute_deprecated(self, event, target, bind, **kw): + if self.on is None: + return True + elif isinstance(self.on, util.string_types): + return self.on == bind.engine.name + elif isinstance(self.on, (tuple, list, set)): + return bind.engine.name in self.on + else: + return self.on(self, event, target, bind, **kw) + + def __call__(self, target, bind, **kw): + """Execute the DDL as a ddl_listener.""" + + if self._should_execute(target, bind, **kw): + return bind.execute(self.against(target)) + + def _check_ddl_on(self, on): + if (on is not None and + (not isinstance(on, util.string_types + (tuple, list, set)) and + not util.callable(on))): + raise exc.ArgumentError( + "Expected the name of a database dialect, a tuple " + "of names, or a callable for " + "'on' criteria, got type '%s'." % type(on).__name__) + + def bind(self): + if self._bind: + return self._bind + + def _set_bind(self, bind): + self._bind = bind + bind = property(bind, _set_bind) + + def _generate(self): + s = self.__class__.__new__(self.__class__) + s.__dict__ = self.__dict__.copy() + return s + + +class DDL(DDLElement): + """A literal DDL statement. + + Specifies literal SQL DDL to be executed by the database. DDL objects + function as DDL event listeners, and can be subscribed to those events + listed in :class:`.DDLEvents`, using either :class:`.Table` or + :class:`.MetaData` objects as targets. Basic templating support allows + a single DDL instance to handle repetitive tasks for multiple tables. + + Examples:: + + from sqlalchemy import event, DDL + + tbl = Table('users', metadata, Column('uid', Integer)) + event.listen(tbl, 'before_create', DDL('DROP TRIGGER users_trigger')) + + spow = DDL('ALTER TABLE %(table)s SET secretpowers TRUE') + event.listen(tbl, 'after_create', spow.execute_if(dialect='somedb')) + + drop_spow = DDL('ALTER TABLE users SET secretpowers FALSE') + connection.execute(drop_spow) + + When operating on Table events, the following ``statement`` + string substitions are available:: + + %(table)s - the Table name, with any required quoting applied + %(schema)s - the schema name, with any required quoting applied + %(fullname)s - the Table name including schema, quoted if needed + + The DDL's "context", if any, will be combined with the standard + substitutions noted above. Keys present in the context will override + the standard substitutions. + + """ + + __visit_name__ = "ddl" + + def __init__(self, statement, on=None, context=None, bind=None): + """Create a DDL statement. + + :param statement: + A string or unicode string to be executed. Statements will be + processed with Python's string formatting operator. See the + ``context`` argument and the ``execute_at`` method. + + A literal '%' in a statement must be escaped as '%%'. + + SQL bind parameters are not available in DDL statements. + + :param on: + .. deprecated:: 0.7 + See :meth:`.DDLElement.execute_if`. + + Optional filtering criteria. May be a string, tuple or a callable + predicate. If a string, it will be compared to the name of the + executing database dialect:: + + DDL('something', on='postgresql') + + If a tuple, specifies multiple dialect names:: + + DDL('something', on=('postgresql', 'mysql')) + + If a callable, it will be invoked with four positional arguments + as well as optional keyword arguments: + + :ddl: + This DDL element. + + :event: + The name of the event that has triggered this DDL, such as + 'after-create' Will be None if the DDL is executed explicitly. + + :target: + The ``Table`` or ``MetaData`` object which is the target of + this event. May be None if the DDL is executed explicitly. + + :connection: + The ``Connection`` being used for DDL execution + + :tables: + Optional keyword argument - a list of Table objects which are to + be created/ dropped within a MetaData.create_all() or drop_all() + method call. + + + If the callable returns a true value, the DDL statement will be + executed. + + :param context: + Optional dictionary, defaults to None. These values will be + available for use in string substitutions on the DDL statement. + + :param bind: + Optional. A :class:`.Connectable`, used by + default when ``execute()`` is invoked without a bind argument. + + + .. seealso:: + + :class:`.DDLEvents` + + :ref:`event_toplevel` + + """ + + if not isinstance(statement, util.string_types): + raise exc.ArgumentError( + "Expected a string or unicode SQL statement, got '%r'" % + statement) + + self.statement = statement + self.context = context or {} + + self._check_ddl_on(on) + self.on = on + self._bind = bind + + def __repr__(self): + return '<%s@%s; %s>' % ( + type(self).__name__, id(self), + ', '.join([repr(self.statement)] + + ['%s=%r' % (key, getattr(self, key)) + for key in ('on', 'context') + if getattr(self, key)])) + + +class _CreateDropBase(DDLElement): + """Base class for DDL constructs that represent CREATE and DROP or + equivalents. + + The common theme of _CreateDropBase is a single + ``element`` attribute which refers to the element + to be created or dropped. + + """ + + def __init__(self, element, on=None, bind=None): + self.element = element + self._check_ddl_on(on) + self.on = on + self.bind = bind + + def _create_rule_disable(self, compiler): + """Allow disable of _create_rule using a callable. + + Pass to _create_rule using + util.portable_instancemethod(self._create_rule_disable) + to retain serializability. + + """ + return False + + +class CreateSchema(_CreateDropBase): + """Represent a CREATE SCHEMA statement. + + .. versionadded:: 0.7.4 + + The argument here is the string name of the schema. + + """ + + __visit_name__ = "create_schema" + + def __init__(self, name, quote=None, **kw): + """Create a new :class:`.CreateSchema` construct.""" + + self.quote = quote + super(CreateSchema, self).__init__(name, **kw) + + +class DropSchema(_CreateDropBase): + """Represent a DROP SCHEMA statement. + + The argument here is the string name of the schema. + + .. versionadded:: 0.7.4 + + """ + + __visit_name__ = "drop_schema" + + def __init__(self, name, quote=None, cascade=False, **kw): + """Create a new :class:`.DropSchema` construct.""" + + self.quote = quote + self.cascade = cascade + super(DropSchema, self).__init__(name, **kw) + + +class CreateTable(_CreateDropBase): + """Represent a CREATE TABLE statement.""" + + __visit_name__ = "create_table" + + def __init__( + self, element, on=None, bind=None, + include_foreign_key_constraints=None): + """Create a :class:`.CreateTable` construct. + + :param element: a :class:`.Table` that's the subject + of the CREATE + :param on: See the description for 'on' in :class:`.DDL`. + :param bind: See the description for 'bind' in :class:`.DDL`. + :param include_foreign_key_constraints: optional sequence of + :class:`.ForeignKeyConstraint` objects that will be included + inline within the CREATE construct; if omitted, all foreign key + constraints that do not specify use_alter=True are included. + + .. versionadded:: 1.0.0 + + """ + super(CreateTable, self).__init__(element, on=on, bind=bind) + self.columns = [CreateColumn(column) + for column in element.columns + ] + self.include_foreign_key_constraints = include_foreign_key_constraints + + +class _DropView(_CreateDropBase): + """Semi-public 'DROP VIEW' construct. + + Used by the test suite for dialect-agnostic drops of views. + This object will eventually be part of a public "view" API. + + """ + __visit_name__ = "drop_view" + + +class CreateColumn(_DDLCompiles): + """Represent a :class:`.Column` as rendered in a CREATE TABLE statement, + via the :class:`.CreateTable` construct. + + This is provided to support custom column DDL within the generation + of CREATE TABLE statements, by using the + compiler extension documented in :ref:`sqlalchemy.ext.compiler_toplevel` + to extend :class:`.CreateColumn`. + + Typical integration is to examine the incoming :class:`.Column` + object, and to redirect compilation if a particular flag or condition + is found:: + + from sqlalchemy import schema + from sqlalchemy.ext.compiler import compiles + + @compiles(schema.CreateColumn) + def compile(element, compiler, **kw): + column = element.element + + if "special" not in column.info: + return compiler.visit_create_column(element, **kw) + + text = "%s SPECIAL DIRECTIVE %s" % ( + column.name, + compiler.type_compiler.process(column.type) + ) + default = compiler.get_column_default_string(column) + if default is not None: + text += " DEFAULT " + default + + if not column.nullable: + text += " NOT NULL" + + if column.constraints: + text += " ".join( + compiler.process(const) + for const in column.constraints) + return text + + The above construct can be applied to a :class:`.Table` as follows:: + + from sqlalchemy import Table, Metadata, Column, Integer, String + from sqlalchemy import schema + + metadata = MetaData() + + table = Table('mytable', MetaData(), + Column('x', Integer, info={"special":True}, primary_key=True), + Column('y', String(50)), + Column('z', String(20), info={"special":True}) + ) + + metadata.create_all(conn) + + Above, the directives we've added to the :attr:`.Column.info` collection + will be detected by our custom compilation scheme:: + + CREATE TABLE mytable ( + x SPECIAL DIRECTIVE INTEGER NOT NULL, + y VARCHAR(50), + z SPECIAL DIRECTIVE VARCHAR(20), + PRIMARY KEY (x) + ) + + The :class:`.CreateColumn` construct can also be used to skip certain + columns when producing a ``CREATE TABLE``. This is accomplished by + creating a compilation rule that conditionally returns ``None``. + This is essentially how to produce the same effect as using the + ``system=True`` argument on :class:`.Column`, which marks a column + as an implicitly-present "system" column. + + For example, suppose we wish to produce a :class:`.Table` which skips + rendering of the PostgreSQL ``xmin`` column against the PostgreSQL + backend, but on other backends does render it, in anticipation of a + triggered rule. A conditional compilation rule could skip this name only + on PostgreSQL:: + + from sqlalchemy.schema import CreateColumn + + @compiles(CreateColumn, "postgresql") + def skip_xmin(element, compiler, **kw): + if element.element.name == 'xmin': + return None + else: + return compiler.visit_create_column(element, **kw) + + + my_table = Table('mytable', metadata, + Column('id', Integer, primary_key=True), + Column('xmin', Integer) + ) + + Above, a :class:`.CreateTable` construct will generate a ``CREATE TABLE`` + which only includes the ``id`` column in the string; the ``xmin`` column + will be omitted, but only against the PostgreSQL backend. + + .. versionadded:: 0.8.3 The :class:`.CreateColumn` construct supports + skipping of columns by returning ``None`` from a custom compilation + rule. + + .. versionadded:: 0.8 The :class:`.CreateColumn` construct was added + to support custom column creation styles. + + """ + __visit_name__ = 'create_column' + + def __init__(self, element): + self.element = element + + +class DropTable(_CreateDropBase): + """Represent a DROP TABLE statement.""" + + __visit_name__ = "drop_table" + + +class CreateSequence(_CreateDropBase): + """Represent a CREATE SEQUENCE statement.""" + + __visit_name__ = "create_sequence" + + +class DropSequence(_CreateDropBase): + """Represent a DROP SEQUENCE statement.""" + + __visit_name__ = "drop_sequence" + + +class CreateIndex(_CreateDropBase): + """Represent a CREATE INDEX statement.""" + + __visit_name__ = "create_index" + + +class DropIndex(_CreateDropBase): + """Represent a DROP INDEX statement.""" + + __visit_name__ = "drop_index" + + +class AddConstraint(_CreateDropBase): + """Represent an ALTER TABLE ADD CONSTRAINT statement.""" + + __visit_name__ = "add_constraint" + + def __init__(self, element, *args, **kw): + super(AddConstraint, self).__init__(element, *args, **kw) + element._create_rule = util.portable_instancemethod( + self._create_rule_disable) + + +class DropConstraint(_CreateDropBase): + """Represent an ALTER TABLE DROP CONSTRAINT statement.""" + + __visit_name__ = "drop_constraint" + + def __init__(self, element, cascade=False, **kw): + self.cascade = cascade + super(DropConstraint, self).__init__(element, **kw) + element._create_rule = util.portable_instancemethod( + self._create_rule_disable) + + +class SetTableComment(_CreateDropBase): + """Represent a COMMENT ON TABLE IS statement.""" + + __visit_name__ = "set_table_comment" + + +class DropTableComment(_CreateDropBase): + """Represent a COMMENT ON TABLE IS NULL statement.""" + + __visit_name__ = "drop_table_comment" + + +class SetColumnComment(_CreateDropBase): + """Represent a COMMENT ON COLUMN IS statement.""" + + __visit_name__ = "set_column_comment" + + +class DropColumnComment(_CreateDropBase): + """Represent a COMMENT ON COLUMN IS NULL statement.""" + + __visit_name__ = "drop_column_comment" + + +class DDLBase(SchemaVisitor): + def __init__(self, connection): + self.connection = connection + + +class SchemaGenerator(DDLBase): + + def __init__(self, dialect, connection, checkfirst=False, + tables=None, **kwargs): + super(SchemaGenerator, self).__init__(connection, **kwargs) + self.checkfirst = checkfirst + self.tables = tables + self.preparer = dialect.identifier_preparer + self.dialect = dialect + self.memo = {} + + def _can_create_table(self, table): + self.dialect.validate_identifier(table.name) + effective_schema = self.connection.schema_for_object(table) + if effective_schema: + self.dialect.validate_identifier(effective_schema) + return not self.checkfirst or \ + not self.dialect.has_table(self.connection, + table.name, schema=effective_schema) + + def _can_create_sequence(self, sequence): + effective_schema = self.connection.schema_for_object(sequence) + + return self.dialect.supports_sequences and \ + ( + (not self.dialect.sequences_optional or + not sequence.optional) and + ( + not self.checkfirst or + not self.dialect.has_sequence( + self.connection, + sequence.name, + schema=effective_schema) + ) + ) + + def visit_metadata(self, metadata): + if self.tables is not None: + tables = self.tables + else: + tables = list(metadata.tables.values()) + + collection = sort_tables_and_constraints( + [t for t in tables if self._can_create_table(t)]) + + seq_coll = [s for s in metadata._sequences.values() + if s.column is None and self._can_create_sequence(s)] + + event_collection = [ + t for (t, fks) in collection if t is not None + ] + metadata.dispatch.before_create(metadata, self.connection, + tables=event_collection, + checkfirst=self.checkfirst, + _ddl_runner=self) + + for seq in seq_coll: + self.traverse_single(seq, create_ok=True) + + for table, fkcs in collection: + if table is not None: + self.traverse_single( + table, create_ok=True, + include_foreign_key_constraints=fkcs, + _is_metadata_operation=True) + else: + for fkc in fkcs: + self.traverse_single(fkc) + + metadata.dispatch.after_create(metadata, self.connection, + tables=event_collection, + checkfirst=self.checkfirst, + _ddl_runner=self) + + def visit_table( + self, table, create_ok=False, + include_foreign_key_constraints=None, + _is_metadata_operation=False): + if not create_ok and not self._can_create_table(table): + return + + table.dispatch.before_create( + table, self.connection, + checkfirst=self.checkfirst, + _ddl_runner=self, + _is_metadata_operation=_is_metadata_operation) + + for column in table.columns: + if column.default is not None: + self.traverse_single(column.default) + + if not self.dialect.supports_alter: + # e.g., don't omit any foreign key constraints + include_foreign_key_constraints = None + + self.connection.execute( + CreateTable( + table, + include_foreign_key_constraints=include_foreign_key_constraints + )) + + if hasattr(table, 'indexes'): + for index in table.indexes: + self.traverse_single(index) + + if self.dialect.supports_comments and not self.dialect.inline_comments: + if table.comment is not None: + self.connection.execute(SetTableComment(table)) + + for column in table.columns: + if column.comment is not None: + self.connection.execute(SetColumnComment(column)) + + table.dispatch.after_create( + table, self.connection, + checkfirst=self.checkfirst, + _ddl_runner=self, + _is_metadata_operation=_is_metadata_operation) + + def visit_foreign_key_constraint(self, constraint): + if not self.dialect.supports_alter: + return + self.connection.execute(AddConstraint(constraint)) + + def visit_sequence(self, sequence, create_ok=False): + if not create_ok and not self._can_create_sequence(sequence): + return + self.connection.execute(CreateSequence(sequence)) + + def visit_index(self, index): + self.connection.execute(CreateIndex(index)) + + +class SchemaDropper(DDLBase): + + def __init__(self, dialect, connection, checkfirst=False, + tables=None, **kwargs): + super(SchemaDropper, self).__init__(connection, **kwargs) + self.checkfirst = checkfirst + self.tables = tables + self.preparer = dialect.identifier_preparer + self.dialect = dialect + self.memo = {} + + def visit_metadata(self, metadata): + if self.tables is not None: + tables = self.tables + else: + tables = list(metadata.tables.values()) + + try: + unsorted_tables = [t for t in tables if self._can_drop_table(t)] + collection = list(reversed( + sort_tables_and_constraints( + unsorted_tables, + filter_fn=lambda constraint: False + if not self.dialect.supports_alter + or constraint.name is None + else None + ) + )) + except exc.CircularDependencyError as err2: + if not self.dialect.supports_alter: + util.warn( + "Can't sort tables for DROP; an " + "unresolvable foreign key " + "dependency exists between tables: %s, and backend does " + "not support ALTER. To restore at least a partial sort, " + "apply use_alter=True to ForeignKey and " + "ForeignKeyConstraint " + "objects involved in the cycle to mark these as known " + "cycles that will be ignored." + % ( + ", ".join(sorted([t.fullname for t in err2.cycles])) + ) + ) + collection = [(t, ()) for t in unsorted_tables] + else: + util.raise_from_cause( + exc.CircularDependencyError( + err2.args[0], + err2.cycles, err2.edges, + msg="Can't sort tables for DROP; an " + "unresolvable foreign key " + "dependency exists between tables: %s. Please ensure " + "that the ForeignKey and ForeignKeyConstraint objects " + "involved in the cycle have " + "names so that they can be dropped using " + "DROP CONSTRAINT." + % ( + ", ".join(sorted([t.fullname for t in err2.cycles])) + ) + + ) + ) + + seq_coll = [ + s + for s in metadata._sequences.values() + if s.column is None and self._can_drop_sequence(s) + ] + + event_collection = [ + t for (t, fks) in collection if t is not None + ] + + metadata.dispatch.before_drop( + metadata, self.connection, tables=event_collection, + checkfirst=self.checkfirst, _ddl_runner=self) + + for table, fkcs in collection: + if table is not None: + self.traverse_single( + table, drop_ok=True, _is_metadata_operation=True) + else: + for fkc in fkcs: + self.traverse_single(fkc) + + for seq in seq_coll: + self.traverse_single(seq, drop_ok=True) + + metadata.dispatch.after_drop( + metadata, self.connection, tables=event_collection, + checkfirst=self.checkfirst, _ddl_runner=self) + + def _can_drop_table(self, table): + self.dialect.validate_identifier(table.name) + effective_schema = self.connection.schema_for_object(table) + if effective_schema: + self.dialect.validate_identifier(effective_schema) + return not self.checkfirst or self.dialect.has_table( + self.connection, table.name, schema=effective_schema) + + def _can_drop_sequence(self, sequence): + effective_schema = self.connection.schema_for_object(sequence) + return self.dialect.supports_sequences and \ + ((not self.dialect.sequences_optional or + not sequence.optional) and + (not self.checkfirst or + self.dialect.has_sequence( + self.connection, + sequence.name, + schema=effective_schema)) + ) + + def visit_index(self, index): + self.connection.execute(DropIndex(index)) + + def visit_table(self, table, drop_ok=False, _is_metadata_operation=False): + if not drop_ok and not self._can_drop_table(table): + return + + table.dispatch.before_drop( + table, self.connection, + checkfirst=self.checkfirst, + _ddl_runner=self, + _is_metadata_operation=_is_metadata_operation) + + self.connection.execute(DropTable(table)) + + # traverse client side defaults which may refer to server-side + # sequences. noting that some of these client side defaults may also be + # set up as server side defaults (see http://docs.sqlalchemy.org/en/ + # latest/core/defaults.html#associating-a-sequence-as-the-server-side- + # default), so have to be dropped after the table is dropped. + for column in table.columns: + if column.default is not None: + self.traverse_single(column.default) + + table.dispatch.after_drop( + table, self.connection, + checkfirst=self.checkfirst, + _ddl_runner=self, + _is_metadata_operation=_is_metadata_operation) + + def visit_foreign_key_constraint(self, constraint): + if not self.dialect.supports_alter: + return + self.connection.execute(DropConstraint(constraint)) + + def visit_sequence(self, sequence, drop_ok=False): + + if not drop_ok and not self._can_drop_sequence(sequence): + return + self.connection.execute(DropSequence(sequence)) + + +def sort_tables(tables, skip_fn=None, extra_dependencies=None): + """sort a collection of :class:`.Table` objects based on dependency. + + This is a dependency-ordered sort which will emit :class:`.Table` + objects such that they will follow their dependent :class:`.Table` objects. + Tables are dependent on another based on the presence of + :class:`.ForeignKeyConstraint` objects as well as explicit dependencies + added by :meth:`.Table.add_is_dependent_on`. + + .. warning:: + + The :func:`.sort_tables` function cannot by itself accommodate + automatic resolution of dependency cycles between tables, which + are usually caused by mutually dependent foreign key constraints. + To resolve these cycles, either the + :paramref:`.ForeignKeyConstraint.use_alter` parameter may be appled + to those constraints, or use the + :func:`.sql.sort_tables_and_constraints` function which will break + out foreign key constraints involved in cycles separately. + + :param tables: a sequence of :class:`.Table` objects. + + :param skip_fn: optional callable which will be passed a + :class:`.ForeignKey` object; if it returns True, this + constraint will not be considered as a dependency. Note this is + **different** from the same parameter in + :func:`.sort_tables_and_constraints`, which is + instead passed the owning :class:`.ForeignKeyConstraint` object. + + :param extra_dependencies: a sequence of 2-tuples of tables which will + also be considered as dependent on each other. + + .. seealso:: + + :func:`.sort_tables_and_constraints` + + :meth:`.MetaData.sorted_tables` - uses this function to sort + + + """ + + if skip_fn is not None: + def _skip_fn(fkc): + for fk in fkc.elements: + if skip_fn(fk): + return True + else: + return None + else: + _skip_fn = None + + return [ + t for (t, fkcs) in + sort_tables_and_constraints( + tables, filter_fn=_skip_fn, extra_dependencies=extra_dependencies) + if t is not None + ] + + +def sort_tables_and_constraints( + tables, filter_fn=None, extra_dependencies=None): + """sort a collection of :class:`.Table` / :class:`.ForeignKeyConstraint` + objects. + + This is a dependency-ordered sort which will emit tuples of + ``(Table, [ForeignKeyConstraint, ...])`` such that each + :class:`.Table` follows its dependent :class:`.Table` objects. + Remaining :class:`.ForeignKeyConstraint` objects that are separate due to + dependency rules not satisfied by the sort are emitted afterwards + as ``(None, [ForeignKeyConstraint ...])``. + + Tables are dependent on another based on the presence of + :class:`.ForeignKeyConstraint` objects, explicit dependencies + added by :meth:`.Table.add_is_dependent_on`, as well as dependencies + stated here using the :paramref:`~.sort_tables_and_constraints.skip_fn` + and/or :paramref:`~.sort_tables_and_constraints.extra_dependencies` + parameters. + + :param tables: a sequence of :class:`.Table` objects. + + :param filter_fn: optional callable which will be passed a + :class:`.ForeignKeyConstraint` object, and returns a value based on + whether this constraint should definitely be included or excluded as + an inline constraint, or neither. If it returns False, the constraint + will definitely be included as a dependency that cannot be subject + to ALTER; if True, it will **only** be included as an ALTER result at + the end. Returning None means the constraint is included in the + table-based result unless it is detected as part of a dependency cycle. + + :param extra_dependencies: a sequence of 2-tuples of tables which will + also be considered as dependent on each other. + + .. versionadded:: 1.0.0 + + .. seealso:: + + :func:`.sort_tables` + + + """ + + fixed_dependencies = set() + mutable_dependencies = set() + + if extra_dependencies is not None: + fixed_dependencies.update(extra_dependencies) + + remaining_fkcs = set() + for table in tables: + for fkc in table.foreign_key_constraints: + if fkc.use_alter is True: + remaining_fkcs.add(fkc) + continue + + if filter_fn: + filtered = filter_fn(fkc) + + if filtered is True: + remaining_fkcs.add(fkc) + continue + + dependent_on = fkc.referred_table + if dependent_on is not table: + mutable_dependencies.add((dependent_on, table)) + + fixed_dependencies.update( + (parent, table) for parent in table._extra_dependencies + ) + + try: + candidate_sort = list( + topological.sort( + fixed_dependencies.union(mutable_dependencies), tables, + deterministic_order=True + ) + ) + except exc.CircularDependencyError as err: + for edge in err.edges: + if edge in mutable_dependencies: + table = edge[1] + can_remove = [ + fkc for fkc in table.foreign_key_constraints + if filter_fn is None or filter_fn(fkc) is not False] + remaining_fkcs.update(can_remove) + for fkc in can_remove: + dependent_on = fkc.referred_table + if dependent_on is not table: + mutable_dependencies.discard((dependent_on, table)) + candidate_sort = list( + topological.sort( + fixed_dependencies.union(mutable_dependencies), tables, + deterministic_order=True + ) + ) + + return [ + (table, table.foreign_key_constraints.difference(remaining_fkcs)) + for table in candidate_sort + ] + [(None, list(remaining_fkcs))] diff --git a/venv/Lib/site-packages/sqlalchemy/sql/default_comparator.py b/venv/Lib/site-packages/sqlalchemy/sql/default_comparator.py new file mode 100644 index 0000000..5d02f65 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/default_comparator.py @@ -0,0 +1,325 @@ +# sql/default_comparator.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Default implementation of SQL comparison operations. +""" + +from .. import exc, util +from . import type_api +from . import operators +from .elements import BindParameter, True_, False_, BinaryExpression, \ + Null, _const_expr, _clause_element_as_expr, \ + ClauseList, ColumnElement, TextClause, UnaryExpression, \ + collate, _is_literal, _literal_as_text, ClauseElement, and_, or_, \ + Slice, Visitable, _literal_as_binds, CollectionAggregate +from .selectable import SelectBase, Alias, Selectable, ScalarSelect + + +def _boolean_compare(expr, op, obj, negate=None, reverse=False, + _python_is_types=(util.NoneType, bool), + result_type = None, + **kwargs): + + if result_type is None: + result_type = type_api.BOOLEANTYPE + + if isinstance(obj, _python_is_types + (Null, True_, False_)): + + # allow x ==/!= True/False to be treated as a literal. + # this comes out to "== / != true/false" or "1/0" if those + # constants aren't supported and works on all platforms + if op in (operators.eq, operators.ne) and \ + isinstance(obj, (bool, True_, False_)): + return BinaryExpression(expr, + _literal_as_text(obj), + op, + type_=result_type, + negate=negate, modifiers=kwargs) + elif op in (operators.is_distinct_from, operators.isnot_distinct_from): + return BinaryExpression(expr, + _literal_as_text(obj), + op, + type_=result_type, + negate=negate, modifiers=kwargs) + else: + # all other None/True/False uses IS, IS NOT + if op in (operators.eq, operators.is_): + return BinaryExpression(expr, _const_expr(obj), + operators.is_, + negate=operators.isnot, + type_=result_type + ) + elif op in (operators.ne, operators.isnot): + return BinaryExpression(expr, _const_expr(obj), + operators.isnot, + negate=operators.is_, + type_=result_type + ) + else: + raise exc.ArgumentError( + "Only '=', '!=', 'is_()', 'isnot()', " + "'is_distinct_from()', 'isnot_distinct_from()' " + "operators can be used with None/True/False") + else: + obj = _check_literal(expr, op, obj) + + if reverse: + return BinaryExpression(obj, + expr, + op, + type_=result_type, + negate=negate, modifiers=kwargs) + else: + return BinaryExpression(expr, + obj, + op, + type_=result_type, + negate=negate, modifiers=kwargs) + + +def _custom_op_operate(expr, op, obj, reverse=False, result_type=None, + **kw): + if result_type is None: + if op.return_type: + result_type = op.return_type + elif op.is_comparison: + result_type = type_api.BOOLEANTYPE + + return _binary_operate( + expr, op, obj, reverse=reverse, result_type=result_type, **kw) + + +def _binary_operate(expr, op, obj, reverse=False, result_type=None, + **kw): + obj = _check_literal(expr, op, obj) + + if reverse: + left, right = obj, expr + else: + left, right = expr, obj + + if result_type is None: + op, result_type = left.comparator._adapt_expression( + op, right.comparator) + + return BinaryExpression( + left, right, op, type_=result_type, modifiers=kw) + + +def _conjunction_operate(expr, op, other, **kw): + if op is operators.and_: + return and_(expr, other) + elif op is operators.or_: + return or_(expr, other) + else: + raise NotImplementedError() + + +def _scalar(expr, op, fn, **kw): + return fn(expr) + + +def _in_impl(expr, op, seq_or_selectable, negate_op, **kw): + seq_or_selectable = _clause_element_as_expr(seq_or_selectable) + + if isinstance(seq_or_selectable, ScalarSelect): + return _boolean_compare(expr, op, seq_or_selectable, + negate=negate_op) + elif isinstance(seq_or_selectable, SelectBase): + + # TODO: if we ever want to support (x, y, z) IN (select x, + # y, z from table), we would need a multi-column version of + # as_scalar() to produce a multi- column selectable that + # does not export itself as a FROM clause + + return _boolean_compare( + expr, op, seq_or_selectable.as_scalar(), + negate=negate_op, **kw) + elif isinstance(seq_or_selectable, (Selectable, TextClause)): + return _boolean_compare(expr, op, seq_or_selectable, + negate=negate_op, **kw) + elif isinstance(seq_or_selectable, ClauseElement): + if isinstance(seq_or_selectable, BindParameter) and \ + seq_or_selectable.expanding: + return _boolean_compare( + expr, op, + seq_or_selectable, + negate=negate_op) + else: + raise exc.InvalidRequestError( + 'in_() accepts' + ' either a list of expressions, ' + 'a selectable, or an "expanding" bound parameter: %r' + % seq_or_selectable) + + # Handle non selectable arguments as sequences + args = [] + for o in seq_or_selectable: + if not _is_literal(o): + if not isinstance(o, operators.ColumnOperators): + raise exc.InvalidRequestError( + 'in_() accepts' + ' either a list of expressions, ' + 'a selectable, or an "expanding" bound parameter: %r' % o) + elif o is None: + o = Null() + else: + o = expr._bind_param(op, o) + args.append(o) + + if len(args) == 0: + op, negate_op = ( + operators.empty_in_op, + operators.empty_notin_op) if op is operators.in_op \ + else ( + operators.empty_notin_op, + operators.empty_in_op) + + return _boolean_compare(expr, op, + ClauseList(*args).self_group(against=op), + negate=negate_op) + + +def _getitem_impl(expr, op, other, **kw): + if isinstance(expr.type, type_api.INDEXABLE): + other = _check_literal(expr, op, other) + return _binary_operate(expr, op, other, **kw) + else: + _unsupported_impl(expr, op, other, **kw) + + +def _unsupported_impl(expr, op, *arg, **kw): + raise NotImplementedError("Operator '%s' is not supported on " + "this expression" % op.__name__) + + +def _inv_impl(expr, op, **kw): + """See :meth:`.ColumnOperators.__inv__`.""" + if hasattr(expr, 'negation_clause'): + return expr.negation_clause + else: + return expr._negate() + + +def _neg_impl(expr, op, **kw): + """See :meth:`.ColumnOperators.__neg__`.""" + return UnaryExpression(expr, operator=operators.neg, type_=expr.type) + + +def _match_impl(expr, op, other, **kw): + """See :meth:`.ColumnOperators.match`.""" + + return _boolean_compare( + expr, operators.match_op, + _check_literal( + expr, operators.match_op, other), + result_type=type_api.MATCHTYPE, + negate=operators.notmatch_op + if op is operators.match_op else operators.match_op, + **kw + ) + + +def _distinct_impl(expr, op, **kw): + """See :meth:`.ColumnOperators.distinct`.""" + return UnaryExpression(expr, operator=operators.distinct_op, + type_=expr.type) + + +def _between_impl(expr, op, cleft, cright, **kw): + """See :meth:`.ColumnOperators.between`.""" + return BinaryExpression( + expr, + ClauseList( + _check_literal(expr, operators.and_, cleft), + _check_literal(expr, operators.and_, cright), + operator=operators.and_, + group=False, group_contents=False), + op, + negate=operators.notbetween_op + if op is operators.between_op + else operators.between_op, + modifiers=kw) + + +def _collate_impl(expr, op, other, **kw): + return collate(expr, other) + +# a mapping of operators with the method they use, along with +# their negated operator for comparison operators +operator_lookup = { + "and_": (_conjunction_operate,), + "or_": (_conjunction_operate,), + "inv": (_inv_impl,), + "add": (_binary_operate,), + "mul": (_binary_operate,), + "sub": (_binary_operate,), + "div": (_binary_operate,), + "mod": (_binary_operate,), + "truediv": (_binary_operate,), + "custom_op": (_custom_op_operate,), + "json_path_getitem_op": (_binary_operate, ), + "json_getitem_op": (_binary_operate, ), + "concat_op": (_binary_operate,), + "any_op": (_scalar, CollectionAggregate._create_any), + "all_op": (_scalar, CollectionAggregate._create_all), + "lt": (_boolean_compare, operators.ge), + "le": (_boolean_compare, operators.gt), + "ne": (_boolean_compare, operators.eq), + "gt": (_boolean_compare, operators.le), + "ge": (_boolean_compare, operators.lt), + "eq": (_boolean_compare, operators.ne), + "is_distinct_from": (_boolean_compare, operators.isnot_distinct_from), + "isnot_distinct_from": (_boolean_compare, operators.is_distinct_from), + "like_op": (_boolean_compare, operators.notlike_op), + "ilike_op": (_boolean_compare, operators.notilike_op), + "notlike_op": (_boolean_compare, operators.like_op), + "notilike_op": (_boolean_compare, operators.ilike_op), + "contains_op": (_boolean_compare, operators.notcontains_op), + "startswith_op": (_boolean_compare, operators.notstartswith_op), + "endswith_op": (_boolean_compare, operators.notendswith_op), + "desc_op": (_scalar, UnaryExpression._create_desc), + "asc_op": (_scalar, UnaryExpression._create_asc), + "nullsfirst_op": (_scalar, UnaryExpression._create_nullsfirst), + "nullslast_op": (_scalar, UnaryExpression._create_nullslast), + "in_op": (_in_impl, operators.notin_op), + "notin_op": (_in_impl, operators.in_op), + "is_": (_boolean_compare, operators.is_), + "isnot": (_boolean_compare, operators.isnot), + "collate": (_collate_impl,), + "match_op": (_match_impl,), + "notmatch_op": (_match_impl,), + "distinct_op": (_distinct_impl,), + "between_op": (_between_impl, ), + "notbetween_op": (_between_impl, ), + "neg": (_neg_impl,), + "getitem": (_getitem_impl,), + "lshift": (_unsupported_impl,), + "rshift": (_unsupported_impl,), + "contains": (_unsupported_impl,), +} + + +def _check_literal(expr, operator, other, bindparam_type=None): + if isinstance(other, (ColumnElement, TextClause)): + if isinstance(other, BindParameter) and \ + other.type._isnull: + other = other._clone() + other.type = expr.type + return other + elif hasattr(other, '__clause_element__'): + other = other.__clause_element__() + elif isinstance(other, type_api.TypeEngine.Comparator): + other = other.expr + + if isinstance(other, (SelectBase, Alias)): + return other.as_scalar() + elif not isinstance(other, Visitable): + return expr._bind_param(operator, other, type_=bindparam_type) + else: + return other + diff --git a/venv/Lib/site-packages/sqlalchemy/sql/dml.py b/venv/Lib/site-packages/sqlalchemy/sql/dml.py new file mode 100644 index 0000000..d6890de --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/dml.py @@ -0,0 +1,879 @@ +# sql/dml.py +# Copyright (C) 2009-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +""" +Provide :class:`.Insert`, :class:`.Update` and :class:`.Delete`. + +""" + +from .base import Executable, _generative, _from_objects, DialectKWArgs, \ + ColumnCollection +from .elements import ClauseElement, _literal_as_text, Null, and_, _clone, \ + _column_as_key +from .selectable import _interpret_as_from, _interpret_as_select, \ + HasPrefixes, HasCTE +from .. import util +from .. import exc + + +class UpdateBase( + HasCTE, DialectKWArgs, HasPrefixes, Executable, ClauseElement): + """Form the base for ``INSERT``, ``UPDATE``, and ``DELETE`` statements. + + """ + + __visit_name__ = 'update_base' + + _execution_options = \ + Executable._execution_options.union({'autocommit': True}) + _hints = util.immutabledict() + _parameter_ordering = None + _prefixes = () + named_with_column = False + + def _process_colparams(self, parameters): + def process_single(p): + if isinstance(p, (list, tuple)): + return dict( + (c.key, pval) + for c, pval in zip(self.table.c, p) + ) + else: + return p + + if self._preserve_parameter_order and parameters is not None: + if not isinstance(parameters, list) or \ + (parameters and not isinstance(parameters[0], tuple)): + raise ValueError( + "When preserve_parameter_order is True, " + "values() only accepts a list of 2-tuples") + self._parameter_ordering = [key for key, value in parameters] + + return dict(parameters), False + + if (isinstance(parameters, (list, tuple)) and parameters and + isinstance(parameters[0], (list, tuple, dict))): + + if not self._supports_multi_parameters: + raise exc.InvalidRequestError( + "This construct does not support " + "multiple parameter sets.") + + return [process_single(p) for p in parameters], True + else: + return process_single(parameters), False + + def params(self, *arg, **kw): + """Set the parameters for the statement. + + This method raises ``NotImplementedError`` on the base class, + and is overridden by :class:`.ValuesBase` to provide the + SET/VALUES clause of UPDATE and INSERT. + + """ + raise NotImplementedError( + "params() is not supported for INSERT/UPDATE/DELETE statements." + " To set the values for an INSERT or UPDATE statement, use" + " stmt.values(**parameters).") + + def bind(self): + """Return a 'bind' linked to this :class:`.UpdateBase` + or a :class:`.Table` associated with it. + + """ + return self._bind or self.table.bind + + def _set_bind(self, bind): + self._bind = bind + bind = property(bind, _set_bind) + + @_generative + def returning(self, *cols): + r"""Add a :term:`RETURNING` or equivalent clause to this statement. + + e.g.:: + + stmt = table.update().\ + where(table.c.data == 'value').\ + values(status='X').\ + returning(table.c.server_flag, + table.c.updated_timestamp) + + for server_flag, updated_timestamp in connection.execute(stmt): + print(server_flag, updated_timestamp) + + The given collection of column expressions should be derived from + the table that is + the target of the INSERT, UPDATE, or DELETE. While :class:`.Column` + objects are typical, the elements can also be expressions:: + + stmt = table.insert().returning( + (table.c.first_name + " " + table.c.last_name). + label('fullname')) + + Upon compilation, a RETURNING clause, or database equivalent, + will be rendered within the statement. For INSERT and UPDATE, + the values are the newly inserted/updated values. For DELETE, + the values are those of the rows which were deleted. + + Upon execution, the values of the columns to be returned are made + available via the result set and can be iterated using + :meth:`.ResultProxy.fetchone` and similar. For DBAPIs which do not + natively support returning values (i.e. cx_oracle), SQLAlchemy will + approximate this behavior at the result level so that a reasonable + amount of behavioral neutrality is provided. + + Note that not all databases/DBAPIs + support RETURNING. For those backends with no support, + an exception is raised upon compilation and/or execution. + For those who do support it, the functionality across backends + varies greatly, including restrictions on executemany() + and other statements which return multiple rows. Please + read the documentation notes for the database in use in + order to determine the availability of RETURNING. + + .. seealso:: + + :meth:`.ValuesBase.return_defaults` - an alternative method tailored + towards efficient fetching of server-side defaults and triggers + for single-row INSERTs or UPDATEs. + + + """ + self._returning = cols + + @_generative + def with_hint(self, text, selectable=None, dialect_name="*"): + """Add a table hint for a single table to this + INSERT/UPDATE/DELETE statement. + + .. note:: + + :meth:`.UpdateBase.with_hint` currently applies only to + Microsoft SQL Server. For MySQL INSERT/UPDATE/DELETE hints, use + :meth:`.UpdateBase.prefix_with`. + + The text of the hint is rendered in the appropriate + location for the database backend in use, relative + to the :class:`.Table` that is the subject of this + statement, or optionally to that of the given + :class:`.Table` passed as the ``selectable`` argument. + + The ``dialect_name`` option will limit the rendering of a particular + hint to a particular backend. Such as, to add a hint + that only takes effect for SQL Server:: + + mytable.insert().with_hint("WITH (PAGLOCK)", dialect_name="mssql") + + .. versionadded:: 0.7.6 + + :param text: Text of the hint. + :param selectable: optional :class:`.Table` that specifies + an element of the FROM clause within an UPDATE or DELETE + to be the subject of the hint - applies only to certain backends. + :param dialect_name: defaults to ``*``, if specified as the name + of a particular dialect, will apply these hints only when + that dialect is in use. + """ + if selectable is None: + selectable = self.table + + self._hints = self._hints.union( + {(selectable, dialect_name): text}) + + +class ValuesBase(UpdateBase): + """Supplies support for :meth:`.ValuesBase.values` to + INSERT and UPDATE constructs.""" + + __visit_name__ = 'values_base' + + _supports_multi_parameters = False + _has_multi_parameters = False + _preserve_parameter_order = False + select = None + _post_values_clause = None + + def __init__(self, table, values, prefixes): + self.table = _interpret_as_from(table) + self.parameters, self._has_multi_parameters = \ + self._process_colparams(values) + if prefixes: + self._setup_prefixes(prefixes) + + @_generative + def values(self, *args, **kwargs): + r"""specify a fixed VALUES clause for an INSERT statement, or the SET + clause for an UPDATE. + + Note that the :class:`.Insert` and :class:`.Update` constructs support + per-execution time formatting of the VALUES and/or SET clauses, + based on the arguments passed to :meth:`.Connection.execute`. + However, the :meth:`.ValuesBase.values` method can be used to "fix" a + particular set of parameters into the statement. + + Multiple calls to :meth:`.ValuesBase.values` will produce a new + construct, each one with the parameter list modified to include + the new parameters sent. In the typical case of a single + dictionary of parameters, the newly passed keys will replace + the same keys in the previous construct. In the case of a list-based + "multiple values" construct, each new list of values is extended + onto the existing list of values. + + :param \**kwargs: key value pairs representing the string key + of a :class:`.Column` mapped to the value to be rendered into the + VALUES or SET clause:: + + users.insert().values(name="some name") + + users.update().where(users.c.id==5).values(name="some name") + + :param \*args: As an alternative to passing key/value parameters, + a dictionary, tuple, or list of dictionaries or tuples can be passed + as a single positional argument in order to form the VALUES or + SET clause of the statement. The forms that are accepted vary + based on whether this is an :class:`.Insert` or an :class:`.Update` + construct. + + For either an :class:`.Insert` or :class:`.Update` construct, a + single dictionary can be passed, which works the same as that of + the kwargs form:: + + users.insert().values({"name": "some name"}) + + users.update().values({"name": "some new name"}) + + Also for either form but more typically for the :class:`.Insert` + construct, a tuple that contains an entry for every column in the + table is also accepted:: + + users.insert().values((5, "some name")) + + The :class:`.Insert` construct also supports being passed a list + of dictionaries or full-table-tuples, which on the server will + render the less common SQL syntax of "multiple values" - this + syntax is supported on backends such as SQLite, PostgreSQL, MySQL, + but not necessarily others:: + + users.insert().values([ + {"name": "some name"}, + {"name": "some other name"}, + {"name": "yet another name"}, + ]) + + The above form would render a multiple VALUES statement similar to:: + + INSERT INTO users (name) VALUES + (:name_1), + (:name_2), + (:name_3) + + It is essential to note that **passing multiple values is + NOT the same as using traditional executemany() form**. The above + syntax is a **special** syntax not typically used. To emit an + INSERT statement against multiple rows, the normal method is + to pass a multiple values list to the :meth:`.Connection.execute` + method, which is supported by all database backends and is generally + more efficient for a very large number of parameters. + + .. seealso:: + + :ref:`execute_multiple` - an introduction to + the traditional Core method of multiple parameter set + invocation for INSERTs and other statements. + + .. versionchanged:: 1.0.0 an INSERT that uses a multiple-VALUES + clause, even a list of length one, + implies that the :paramref:`.Insert.inline` flag is set to + True, indicating that the statement will not attempt to fetch + the "last inserted primary key" or other defaults. The + statement deals with an arbitrary number of rows, so the + :attr:`.ResultProxy.inserted_primary_key` accessor does not + apply. + + .. versionchanged:: 1.0.0 A multiple-VALUES INSERT now supports + columns with Python side default values and callables in the + same way as that of an "executemany" style of invocation; the + callable is invoked for each row. See :ref:`bug_3288` + for other details. + + The :class:`.Update` construct supports a special form which is a + list of 2-tuples, which when provided must be passed in conjunction + with the + :paramref:`~sqlalchemy.sql.expression.update.preserve_parameter_order` + parameter. + This form causes the UPDATE statement to render the SET clauses + using the order of parameters given to :meth:`.Update.values`, rather + than the ordering of columns given in the :class:`.Table`. + + .. versionadded:: 1.0.10 - added support for parameter-ordered + UPDATE statements via the + :paramref:`~sqlalchemy.sql.expression.update.preserve_parameter_order` + flag. + + .. seealso:: + + :ref:`updates_order_parameters` - full example of the + :paramref:`~sqlalchemy.sql.expression.update.preserve_parameter_order` + flag + + .. seealso:: + + :ref:`inserts_and_updates` - SQL Expression + Language Tutorial + + :func:`~.expression.insert` - produce an ``INSERT`` statement + + :func:`~.expression.update` - produce an ``UPDATE`` statement + + """ + if self.select is not None: + raise exc.InvalidRequestError( + "This construct already inserts from a SELECT") + if self._has_multi_parameters and kwargs: + raise exc.InvalidRequestError( + "This construct already has multiple parameter sets.") + + if args: + if len(args) > 1: + raise exc.ArgumentError( + "Only a single dictionary/tuple or list of " + "dictionaries/tuples is accepted positionally.") + v = args[0] + else: + v = {} + + if self.parameters is None: + self.parameters, self._has_multi_parameters = \ + self._process_colparams(v) + else: + if self._has_multi_parameters: + self.parameters = list(self.parameters) + p, self._has_multi_parameters = self._process_colparams(v) + if not self._has_multi_parameters: + raise exc.ArgumentError( + "Can't mix single-values and multiple values " + "formats in one statement") + + self.parameters.extend(p) + else: + self.parameters = self.parameters.copy() + p, self._has_multi_parameters = self._process_colparams(v) + if self._has_multi_parameters: + raise exc.ArgumentError( + "Can't mix single-values and multiple values " + "formats in one statement") + self.parameters.update(p) + + if kwargs: + if self._has_multi_parameters: + raise exc.ArgumentError( + "Can't pass kwargs and multiple parameter sets " + "simultaneously") + else: + self.parameters.update(kwargs) + + @_generative + def return_defaults(self, *cols): + """Make use of a :term:`RETURNING` clause for the purpose + of fetching server-side expressions and defaults. + + E.g.:: + + stmt = table.insert().values(data='newdata').return_defaults() + + result = connection.execute(stmt) + + server_created_at = result.returned_defaults['created_at'] + + When used against a backend that supports RETURNING, all column + values generated by SQL expression or server-side-default will be + added to any existing RETURNING clause, provided that + :meth:`.UpdateBase.returning` is not used simultaneously. The column + values will then be available on the result using the + :attr:`.ResultProxy.returned_defaults` accessor as a dictionary, + referring to values keyed to the :class:`.Column` object as well as + its ``.key``. + + This method differs from :meth:`.UpdateBase.returning` in these ways: + + 1. :meth:`.ValuesBase.return_defaults` is only intended for use with + an INSERT or an UPDATE statement that matches exactly one row. + While the RETURNING construct in the general sense supports + multiple rows for a multi-row UPDATE or DELETE statement, or for + special cases of INSERT that return multiple rows (e.g. INSERT from + SELECT, multi-valued VALUES clause), + :meth:`.ValuesBase.return_defaults` is intended only for an + "ORM-style" single-row INSERT/UPDATE statement. The row returned + by the statement is also consumed implicitly when + :meth:`.ValuesBase.return_defaults` is used. By contrast, + :meth:`.UpdateBase.returning` leaves the RETURNING result-set + intact with a collection of any number of rows. + + 2. It is compatible with the existing logic to fetch auto-generated + primary key values, also known as "implicit returning". Backends + that support RETURNING will automatically make use of RETURNING in + order to fetch the value of newly generated primary keys; while the + :meth:`.UpdateBase.returning` method circumvents this behavior, + :meth:`.ValuesBase.return_defaults` leaves it intact. + + 3. It can be called against any backend. Backends that don't support + RETURNING will skip the usage of the feature, rather than raising + an exception. The return value of + :attr:`.ResultProxy.returned_defaults` will be ``None`` + + :meth:`.ValuesBase.return_defaults` is used by the ORM to provide + an efficient implementation for the ``eager_defaults`` feature of + :func:`.mapper`. + + :param cols: optional list of column key names or :class:`.Column` + objects. If omitted, all column expressions evaluated on the server + are added to the returning list. + + .. versionadded:: 0.9.0 + + .. seealso:: + + :meth:`.UpdateBase.returning` + + :attr:`.ResultProxy.returned_defaults` + + """ + self._return_defaults = cols or True + + +class Insert(ValuesBase): + """Represent an INSERT construct. + + The :class:`.Insert` object is created using the + :func:`~.expression.insert()` function. + + .. seealso:: + + :ref:`coretutorial_insert_expressions` + + """ + __visit_name__ = 'insert' + + _supports_multi_parameters = True + + def __init__(self, + table, + values=None, + inline=False, + bind=None, + prefixes=None, + returning=None, + return_defaults=False, + **dialect_kw): + """Construct an :class:`.Insert` object. + + Similar functionality is available via the + :meth:`~.TableClause.insert` method on + :class:`~.schema.Table`. + + :param table: :class:`.TableClause` which is the subject of the + insert. + + :param values: collection of values to be inserted; see + :meth:`.Insert.values` for a description of allowed formats here. + Can be omitted entirely; a :class:`.Insert` construct will also + dynamically render the VALUES clause at execution time based on + the parameters passed to :meth:`.Connection.execute`. + + :param inline: if True, no attempt will be made to retrieve the + SQL-generated default values to be provided within the statement; + in particular, + this allows SQL expressions to be rendered 'inline' within the + statement without the need to pre-execute them beforehand; for + backends that support "returning", this turns off the "implicit + returning" feature for the statement. + + If both `values` and compile-time bind parameters are present, the + compile-time bind parameters override the information specified + within `values` on a per-key basis. + + The keys within `values` can be either + :class:`~sqlalchemy.schema.Column` objects or their string + identifiers. Each key may reference one of: + + * a literal data value (i.e. string, number, etc.); + * a Column object; + * a SELECT statement. + + If a ``SELECT`` statement is specified which references this + ``INSERT`` statement's table, the statement will be correlated + against the ``INSERT`` statement. + + .. seealso:: + + :ref:`coretutorial_insert_expressions` - SQL Expression Tutorial + + :ref:`inserts_and_updates` - SQL Expression Tutorial + + """ + ValuesBase.__init__(self, table, values, prefixes) + self._bind = bind + self.select = self.select_names = None + self.include_insert_from_select_defaults = False + self.inline = inline + self._returning = returning + self._validate_dialect_kwargs(dialect_kw) + self._return_defaults = return_defaults + + def get_children(self, **kwargs): + if self.select is not None: + return self.select, + else: + return () + + @_generative + def from_select(self, names, select, include_defaults=True): + """Return a new :class:`.Insert` construct which represents + an ``INSERT...FROM SELECT`` statement. + + e.g.:: + + sel = select([table1.c.a, table1.c.b]).where(table1.c.c > 5) + ins = table2.insert().from_select(['a', 'b'], sel) + + :param names: a sequence of string column names or :class:`.Column` + objects representing the target columns. + :param select: a :func:`.select` construct, :class:`.FromClause` + or other construct which resolves into a :class:`.FromClause`, + such as an ORM :class:`.Query` object, etc. The order of + columns returned from this FROM clause should correspond to the + order of columns sent as the ``names`` parameter; while this + is not checked before passing along to the database, the database + would normally raise an exception if these column lists don't + correspond. + :param include_defaults: if True, non-server default values and + SQL expressions as specified on :class:`.Column` objects + (as documented in :ref:`metadata_defaults_toplevel`) not + otherwise specified in the list of names will be rendered + into the INSERT and SELECT statements, so that these values are also + included in the data to be inserted. + + .. note:: A Python-side default that uses a Python callable function + will only be invoked **once** for the whole statement, and **not + per row**. + + .. versionadded:: 1.0.0 - :meth:`.Insert.from_select` now renders + Python-side and SQL expression column defaults into the + SELECT statement for columns otherwise not included in the + list of column names. + + .. versionchanged:: 1.0.0 an INSERT that uses FROM SELECT + implies that the :paramref:`.insert.inline` flag is set to + True, indicating that the statement will not attempt to fetch + the "last inserted primary key" or other defaults. The statement + deals with an arbitrary number of rows, so the + :attr:`.ResultProxy.inserted_primary_key` accessor does not apply. + + .. versionadded:: 0.8.3 + + """ + if self.parameters: + raise exc.InvalidRequestError( + "This construct already inserts value expressions") + + self.parameters, self._has_multi_parameters = \ + self._process_colparams( + {_column_as_key(n): Null() for n in names}) + + self.select_names = names + self.inline = True + self.include_insert_from_select_defaults = include_defaults + self.select = _interpret_as_select(select) + + def _copy_internals(self, clone=_clone, **kw): + # TODO: coverage + self.parameters = self.parameters.copy() + if self.select is not None: + self.select = _clone(self.select) + + +class Update(ValuesBase): + """Represent an Update construct. + + The :class:`.Update` object is created using the :func:`update()` + function. + + """ + __visit_name__ = 'update' + + def __init__(self, + table, + whereclause=None, + values=None, + inline=False, + bind=None, + prefixes=None, + returning=None, + return_defaults=False, + preserve_parameter_order=False, + **dialect_kw): + r"""Construct an :class:`.Update` object. + + E.g.:: + + from sqlalchemy import update + + stmt = update(users).where(users.c.id==5).\ + values(name='user #5') + + Similar functionality is available via the + :meth:`~.TableClause.update` method on + :class:`.Table`:: + + stmt = users.update().\ + where(users.c.id==5).\ + values(name='user #5') + + :param table: A :class:`.Table` object representing the database + table to be updated. + + :param whereclause: Optional SQL expression describing the ``WHERE`` + condition of the ``UPDATE`` statement. Modern applications + may prefer to use the generative :meth:`~Update.where()` + method to specify the ``WHERE`` clause. + + The WHERE clause can refer to multiple tables. + For databases which support this, an ``UPDATE FROM`` clause will + be generated, or on MySQL, a multi-table update. The statement + will fail on databases that don't have support for multi-table + update statements. A SQL-standard method of referring to + additional tables in the WHERE clause is to use a correlated + subquery:: + + users.update().values(name='ed').where( + users.c.name==select([addresses.c.email_address]).\ + where(addresses.c.user_id==users.c.id).\ + as_scalar() + ) + + .. versionchanged:: 0.7.4 + The WHERE clause of UPDATE can refer to multiple tables. + + :param values: + Optional dictionary which specifies the ``SET`` conditions of the + ``UPDATE``. If left as ``None``, the ``SET`` + conditions are determined from those parameters passed to the + statement during the execution and/or compilation of the + statement. When compiled standalone without any parameters, + the ``SET`` clause generates for all columns. + + Modern applications may prefer to use the generative + :meth:`.Update.values` method to set the values of the + UPDATE statement. + + :param inline: + if True, SQL defaults present on :class:`.Column` objects via + the ``default`` keyword will be compiled 'inline' into the statement + and not pre-executed. This means that their values will not + be available in the dictionary returned from + :meth:`.ResultProxy.last_updated_params`. + + :param preserve_parameter_order: if True, the update statement is + expected to receive parameters **only** via the :meth:`.Update.values` + method, and they must be passed as a Python ``list`` of 2-tuples. + The rendered UPDATE statement will emit the SET clause for each + referenced column maintaining this order. + + .. versionadded:: 1.0.10 + + .. seealso:: + + :ref:`updates_order_parameters` - full example of the + :paramref:`~sqlalchemy.sql.expression.update.preserve_parameter_order` flag + + If both ``values`` and compile-time bind parameters are present, the + compile-time bind parameters override the information specified + within ``values`` on a per-key basis. + + The keys within ``values`` can be either :class:`.Column` + objects or their string identifiers (specifically the "key" of the + :class:`.Column`, normally but not necessarily equivalent to + its "name"). Normally, the + :class:`.Column` objects used here are expected to be + part of the target :class:`.Table` that is the table + to be updated. However when using MySQL, a multiple-table + UPDATE statement can refer to columns from any of + the tables referred to in the WHERE clause. + + The values referred to in ``values`` are typically: + + * a literal data value (i.e. string, number, etc.) + * a SQL expression, such as a related :class:`.Column`, + a scalar-returning :func:`.select` construct, + etc. + + When combining :func:`.select` constructs within the values + clause of an :func:`.update` construct, + the subquery represented by the :func:`.select` should be + *correlated* to the parent table, that is, providing criterion + which links the table inside the subquery to the outer table + being updated:: + + users.update().values( + name=select([addresses.c.email_address]).\ + where(addresses.c.user_id==users.c.id).\ + as_scalar() + ) + + .. seealso:: + + :ref:`inserts_and_updates` - SQL Expression + Language Tutorial + + + """ + self._preserve_parameter_order = preserve_parameter_order + ValuesBase.__init__(self, table, values, prefixes) + self._bind = bind + self._returning = returning + if whereclause is not None: + self._whereclause = _literal_as_text(whereclause) + else: + self._whereclause = None + self.inline = inline + self._validate_dialect_kwargs(dialect_kw) + self._return_defaults = return_defaults + + def get_children(self, **kwargs): + if self._whereclause is not None: + return self._whereclause, + else: + return () + + def _copy_internals(self, clone=_clone, **kw): + # TODO: coverage + self._whereclause = clone(self._whereclause, **kw) + self.parameters = self.parameters.copy() + + @_generative + def where(self, whereclause): + """return a new update() construct with the given expression added to + its WHERE clause, joined to the existing clause via AND, if any. + + """ + if self._whereclause is not None: + self._whereclause = and_(self._whereclause, + _literal_as_text(whereclause)) + else: + self._whereclause = _literal_as_text(whereclause) + + @property + def _extra_froms(self): + froms = [] + seen = {self.table} + + if self._whereclause is not None: + for item in _from_objects(self._whereclause): + if not seen.intersection(item._cloned_set): + froms.append(item) + seen.update(item._cloned_set) + + return froms + + +class Delete(UpdateBase): + """Represent a DELETE construct. + + The :class:`.Delete` object is created using the :func:`delete()` + function. + + """ + + __visit_name__ = 'delete' + + def __init__(self, + table, + whereclause=None, + bind=None, + returning=None, + prefixes=None, + **dialect_kw): + """Construct :class:`.Delete` object. + + Similar functionality is available via the + :meth:`~.TableClause.delete` method on + :class:`~.schema.Table`. + + :param table: The table to delete rows from. + + :param whereclause: A :class:`.ClauseElement` describing the ``WHERE`` + condition of the ``DELETE`` statement. Note that the + :meth:`~Delete.where()` generative method may be used instead. + + The WHERE clause can refer to multiple tables. + For databases which support this, a ``DELETE..USING`` or similar + clause will be generated. The statement + will fail on databases that don't have support for multi-table + delete statements. A SQL-standard method of referring to + additional tables in the WHERE clause is to use a correlated + subquery:: + + users.delete().where( + users.c.name==select([addresses.c.email_address]).\ + where(addresses.c.user_id==users.c.id).\ + as_scalar() + ) + + .. versionchanged:: 1.2.0 + The WHERE clause of DELETE can refer to multiple tables. + + .. seealso:: + + :ref:`deletes` - SQL Expression Tutorial + + """ + self._bind = bind + self.table = _interpret_as_from(table) + self._returning = returning + + if prefixes: + self._setup_prefixes(prefixes) + + if whereclause is not None: + self._whereclause = _literal_as_text(whereclause) + else: + self._whereclause = None + + self._validate_dialect_kwargs(dialect_kw) + + def get_children(self, **kwargs): + if self._whereclause is not None: + return self._whereclause, + else: + return () + + @_generative + def where(self, whereclause): + """Add the given WHERE clause to a newly returned delete construct.""" + + if self._whereclause is not None: + self._whereclause = and_(self._whereclause, + _literal_as_text(whereclause)) + else: + self._whereclause = _literal_as_text(whereclause) + + @property + def _extra_froms(self): + froms = [] + seen = {self.table} + + if self._whereclause is not None: + for item in _from_objects(self._whereclause): + if not seen.intersection(item._cloned_set): + froms.append(item) + seen.update(item._cloned_set) + + return froms + + def _copy_internals(self, clone=_clone, **kw): + # TODO: coverage + self._whereclause = clone(self._whereclause, **kw) diff --git a/venv/Lib/site-packages/sqlalchemy/sql/elements.py b/venv/Lib/site-packages/sqlalchemy/sql/elements.py new file mode 100644 index 0000000..7b827d1 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/elements.py @@ -0,0 +1,4470 @@ +# sql/elements.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Core SQL expression elements, including :class:`.ClauseElement`, +:class:`.ColumnElement`, and derived classes. + +""" + +from __future__ import unicode_literals + +from .. import util, exc, inspection +from . import type_api +from . import operators +from .visitors import Visitable, cloned_traverse, traverse +from .annotation import Annotated +import itertools +from .base import Executable, PARSE_AUTOCOMMIT, Immutable, NO_ARG +from .base import _generative +import numbers + +import re +import operator + + +def _clone(element, **kw): + return element._clone() + + +def collate(expression, collation): + """Return the clause ``expression COLLATE collation``. + + e.g.:: + + collate(mycolumn, 'utf8_bin') + + produces:: + + mycolumn COLLATE utf8_bin + + The collation expression is also quoted if it is a case sensitive + identifier, e.g. contains uppercase characters. + + .. versionchanged:: 1.2 quoting is automatically applied to COLLATE + expressions if they are case sensitive. + + """ + + expr = _literal_as_binds(expression) + return BinaryExpression( + expr, + CollationClause(collation), + operators.collate, type_=expr.type) + + +def between(expr, lower_bound, upper_bound, symmetric=False): + """Produce a ``BETWEEN`` predicate clause. + + E.g.:: + + from sqlalchemy import between + stmt = select([users_table]).where(between(users_table.c.id, 5, 7)) + + Would produce SQL resembling:: + + SELECT id, name FROM user WHERE id BETWEEN :id_1 AND :id_2 + + The :func:`.between` function is a standalone version of the + :meth:`.ColumnElement.between` method available on all + SQL expressions, as in:: + + stmt = select([users_table]).where(users_table.c.id.between(5, 7)) + + All arguments passed to :func:`.between`, including the left side + column expression, are coerced from Python scalar values if a + the value is not a :class:`.ColumnElement` subclass. For example, + three fixed values can be compared as in:: + + print(between(5, 3, 7)) + + Which would produce:: + + :param_1 BETWEEN :param_2 AND :param_3 + + :param expr: a column expression, typically a :class:`.ColumnElement` + instance or alternatively a Python scalar expression to be coerced + into a column expression, serving as the left side of the ``BETWEEN`` + expression. + + :param lower_bound: a column or Python scalar expression serving as the + lower bound of the right side of the ``BETWEEN`` expression. + + :param upper_bound: a column or Python scalar expression serving as the + upper bound of the right side of the ``BETWEEN`` expression. + + :param symmetric: if True, will render " BETWEEN SYMMETRIC ". Note + that not all databases support this syntax. + + .. versionadded:: 0.9.5 + + .. seealso:: + + :meth:`.ColumnElement.between` + + """ + expr = _literal_as_binds(expr) + return expr.between(lower_bound, upper_bound, symmetric=symmetric) + + +def literal(value, type_=None): + r"""Return a literal clause, bound to a bind parameter. + + Literal clauses are created automatically when non- + :class:`.ClauseElement` objects (such as strings, ints, dates, etc.) are + used in a comparison operation with a :class:`.ColumnElement` subclass, + such as a :class:`~sqlalchemy.schema.Column` object. Use this function + to force the generation of a literal clause, which will be created as a + :class:`BindParameter` with a bound value. + + :param value: the value to be bound. Can be any Python object supported by + the underlying DB-API, or is translatable via the given type argument. + + :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine` which + will provide bind-parameter translation for this literal. + + """ + return BindParameter(None, value, type_=type_, unique=True) + + + + +def outparam(key, type_=None): + """Create an 'OUT' parameter for usage in functions (stored procedures), + for databases which support them. + + The ``outparam`` can be used like a regular function parameter. + The "output" value will be available from the + :class:`~sqlalchemy.engine.ResultProxy` object via its ``out_parameters`` + attribute, which returns a dictionary containing the values. + + """ + return BindParameter( + key, None, type_=type_, unique=False, isoutparam=True) + + +def not_(clause): + """Return a negation of the given clause, i.e. ``NOT(clause)``. + + The ``~`` operator is also overloaded on all + :class:`.ColumnElement` subclasses to produce the + same result. + + """ + return operators.inv(_literal_as_binds(clause)) + + +@inspection._self_inspects +class ClauseElement(Visitable): + """Base class for elements of a programmatically constructed SQL + expression. + + """ + __visit_name__ = 'clause' + + _annotations = {} + supports_execution = False + _from_objects = [] + bind = None + _is_clone_of = None + is_selectable = False + is_clause_element = True + + description = None + _order_by_label_element = None + _is_from_container = False + + def _clone(self): + """Create a shallow copy of this ClauseElement. + + This method may be used by a generative API. Its also used as + part of the "deep" copy afforded by a traversal that combines + the _copy_internals() method. + + """ + c = self.__class__.__new__(self.__class__) + c.__dict__ = self.__dict__.copy() + ClauseElement._cloned_set._reset(c) + ColumnElement.comparator._reset(c) + + # this is a marker that helps to "equate" clauses to each other + # when a Select returns its list of FROM clauses. the cloning + # process leaves around a lot of remnants of the previous clause + # typically in the form of column expressions still attached to the + # old table. + c._is_clone_of = self + + return c + + @property + def _constructor(self): + """return the 'constructor' for this ClauseElement. + + This is for the purposes for creating a new object of + this type. Usually, its just the element's __class__. + However, the "Annotated" version of the object overrides + to return the class of its proxied element. + + """ + return self.__class__ + + @util.memoized_property + def _cloned_set(self): + """Return the set consisting all cloned ancestors of this + ClauseElement. + + Includes this ClauseElement. This accessor tends to be used for + FromClause objects to identify 'equivalent' FROM clauses, regardless + of transformative operations. + + """ + s = util.column_set() + f = self + while f is not None: + s.add(f) + f = f._is_clone_of + return s + + def __getstate__(self): + d = self.__dict__.copy() + d.pop('_is_clone_of', None) + return d + + def _annotate(self, values): + """return a copy of this ClauseElement with annotations + updated by the given dictionary. + + """ + return Annotated(self, values) + + def _with_annotations(self, values): + """return a copy of this ClauseElement with annotations + replaced by the given dictionary. + + """ + return Annotated(self, values) + + def _deannotate(self, values=None, clone=False): + """return a copy of this :class:`.ClauseElement` with annotations + removed. + + :param values: optional tuple of individual values + to remove. + + """ + if clone: + # clone is used when we are also copying + # the expression for a deep deannotation + return self._clone() + else: + # if no clone, since we have no annotations we return + # self + return self + + def _execute_on_connection(self, connection, multiparams, params): + if self.supports_execution: + return connection._execute_clauseelement(self, multiparams, params) + else: + raise exc.ObjectNotExecutableError(self) + + def unique_params(self, *optionaldict, **kwargs): + """Return a copy with :func:`bindparam()` elements replaced. + + Same functionality as ``params()``, except adds `unique=True` + to affected bind parameters so that multiple statements can be + used. + + """ + return self._params(True, optionaldict, kwargs) + + def params(self, *optionaldict, **kwargs): + """Return a copy with :func:`bindparam()` elements replaced. + + Returns a copy of this ClauseElement with :func:`bindparam()` + elements replaced with values taken from the given dictionary:: + + >>> clause = column('x') + bindparam('foo') + >>> print clause.compile().params + {'foo':None} + >>> print clause.params({'foo':7}).compile().params + {'foo':7} + + """ + return self._params(False, optionaldict, kwargs) + + def _params(self, unique, optionaldict, kwargs): + if len(optionaldict) == 1: + kwargs.update(optionaldict[0]) + elif len(optionaldict) > 1: + raise exc.ArgumentError( + "params() takes zero or one positional dictionary argument") + + def visit_bindparam(bind): + if bind.key in kwargs: + bind.value = kwargs[bind.key] + bind.required = False + if unique: + bind._convert_to_unique() + return cloned_traverse(self, {}, {'bindparam': visit_bindparam}) + + def compare(self, other, **kw): + r"""Compare this ClauseElement to the given ClauseElement. + + Subclasses should override the default behavior, which is a + straight identity comparison. + + \**kw are arguments consumed by subclass compare() methods and + may be used to modify the criteria for comparison. + (see :class:`.ColumnElement`) + + """ + return self is other + + def _copy_internals(self, clone=_clone, **kw): + """Reassign internal elements to be clones of themselves. + + Called during a copy-and-traverse operation on newly + shallow-copied elements to create a deep copy. + + The given clone function should be used, which may be applying + additional transformations to the element (i.e. replacement + traversal, cloned traversal, annotations). + + """ + pass + + def get_children(self, **kwargs): + r"""Return immediate child elements of this :class:`.ClauseElement`. + + This is used for visit traversal. + + \**kwargs may contain flags that change the collection that is + returned, for example to return a subset of items in order to + cut down on larger traversals, or to return child items from a + different context (such as schema-level collections instead of + clause-level). + + """ + return [] + + def self_group(self, against=None): + """Apply a 'grouping' to this :class:`.ClauseElement`. + + This method is overridden by subclasses to return a + "grouping" construct, i.e. parenthesis. In particular + it's used by "binary" expressions to provide a grouping + around themselves when placed into a larger expression, + as well as by :func:`.select` constructs when placed into + the FROM clause of another :func:`.select`. (Note that + subqueries should be normally created using the + :meth:`.Select.alias` method, as many platforms require + nested SELECT statements to be named). + + As expressions are composed together, the application of + :meth:`self_group` is automatic - end-user code should never + need to use this method directly. Note that SQLAlchemy's + clause constructs take operator precedence into account - + so parenthesis might not be needed, for example, in + an expression like ``x OR (y AND z)`` - AND takes precedence + over OR. + + The base :meth:`self_group` method of :class:`.ClauseElement` + just returns self. + """ + return self + + @util.dependencies("sqlalchemy.engine.default") + def compile(self, default, bind=None, dialect=None, **kw): + """Compile this SQL expression. + + The return value is a :class:`~.Compiled` object. + Calling ``str()`` or ``unicode()`` on the returned value will yield a + string representation of the result. The + :class:`~.Compiled` object also can return a + dictionary of bind parameter names and values + using the ``params`` accessor. + + :param bind: An ``Engine`` or ``Connection`` from which a + ``Compiled`` will be acquired. This argument takes precedence over + this :class:`.ClauseElement`'s bound engine, if any. + + :param column_keys: Used for INSERT and UPDATE statements, a list of + column names which should be present in the VALUES clause of the + compiled statement. If ``None``, all columns from the target table + object are rendered. + + :param dialect: A ``Dialect`` instance from which a ``Compiled`` + will be acquired. This argument takes precedence over the `bind` + argument as well as this :class:`.ClauseElement`'s bound engine, + if any. + + :param inline: Used for INSERT statements, for a dialect which does + not support inline retrieval of newly generated primary key + columns, will force the expression used to create the new primary + key value to be rendered inline within the INSERT statement's + VALUES clause. This typically refers to Sequence execution but may + also refer to any server-side default generation function + associated with a primary key `Column`. + + :param compile_kwargs: optional dictionary of additional parameters + that will be passed through to the compiler within all "visit" + methods. This allows any custom flag to be passed through to + a custom compilation construct, for example. It is also used + for the case of passing the ``literal_binds`` flag through:: + + from sqlalchemy.sql import table, column, select + + t = table('t', column('x')) + + s = select([t]).where(t.c.x == 5) + + print s.compile(compile_kwargs={"literal_binds": True}) + + .. versionadded:: 0.9.0 + + .. seealso:: + + :ref:`faq_sql_expression_string` + + """ + + if not dialect: + if bind: + dialect = bind.dialect + elif self.bind: + dialect = self.bind.dialect + bind = self.bind + else: + dialect = default.StrCompileDialect() + return self._compiler(dialect, bind=bind, **kw) + + def _compiler(self, dialect, **kw): + """Return a compiler appropriate for this ClauseElement, given a + Dialect.""" + + return dialect.statement_compiler(dialect, self, **kw) + + def __str__(self): + if util.py3k: + return str(self.compile()) + else: + return unicode(self.compile()).encode('ascii', 'backslashreplace') + + def __and__(self, other): + """'and' at the ClauseElement level. + + .. deprecated:: 0.9.5 - conjunctions are intended to be + at the :class:`.ColumnElement`. level + + """ + return and_(self, other) + + def __or__(self, other): + """'or' at the ClauseElement level. + + .. deprecated:: 0.9.5 - conjunctions are intended to be + at the :class:`.ColumnElement`. level + + """ + return or_(self, other) + + def __invert__(self): + if hasattr(self, 'negation_clause'): + return self.negation_clause + else: + return self._negate() + + def _negate(self): + return UnaryExpression( + self.self_group(against=operators.inv), + operator=operators.inv, + negate=None) + + def __bool__(self): + raise TypeError("Boolean value of this clause is not defined") + + __nonzero__ = __bool__ + + def __repr__(self): + friendly = self.description + if friendly is None: + return object.__repr__(self) + else: + return '<%s.%s at 0x%x; %s>' % ( + self.__module__, self.__class__.__name__, id(self), friendly) + + +class ColumnElement(operators.ColumnOperators, ClauseElement): + """Represent a column-oriented SQL expression suitable for usage in the + "columns" clause, WHERE clause etc. of a statement. + + While the most familiar kind of :class:`.ColumnElement` is the + :class:`.Column` object, :class:`.ColumnElement` serves as the basis + for any unit that may be present in a SQL expression, including + the expressions themselves, SQL functions, bound parameters, + literal expressions, keywords such as ``NULL``, etc. + :class:`.ColumnElement` is the ultimate base class for all such elements. + + A wide variety of SQLAlchemy Core functions work at the SQL expression + level, and are intended to accept instances of :class:`.ColumnElement` as + arguments. These functions will typically document that they accept a + "SQL expression" as an argument. What this means in terms of SQLAlchemy + usually refers to an input which is either already in the form of a + :class:`.ColumnElement` object, or a value which can be **coerced** into + one. The coercion rules followed by most, but not all, SQLAlchemy Core + functions with regards to SQL expressions are as follows: + + * a literal Python value, such as a string, integer or floating + point value, boolean, datetime, ``Decimal`` object, or virtually + any other Python object, will be coerced into a "literal bound + value". This generally means that a :func:`.bindparam` will be + produced featuring the given value embedded into the construct; the + resulting :class:`.BindParameter` object is an instance of + :class:`.ColumnElement`. The Python value will ultimately be sent + to the DBAPI at execution time as a parameterized argument to the + ``execute()`` or ``executemany()`` methods, after SQLAlchemy + type-specific converters (e.g. those provided by any associated + :class:`.TypeEngine` objects) are applied to the value. + + * any special object value, typically ORM-level constructs, which + feature a method called ``__clause_element__()``. The Core + expression system looks for this method when an object of otherwise + unknown type is passed to a function that is looking to coerce the + argument into a :class:`.ColumnElement` expression. The + ``__clause_element__()`` method, if present, should return a + :class:`.ColumnElement` instance. The primary use of + ``__clause_element__()`` within SQLAlchemy is that of class-bound + attributes on ORM-mapped classes; a ``User`` class which contains a + mapped attribute named ``.name`` will have a method + ``User.name.__clause_element__()`` which when invoked returns the + :class:`.Column` called ``name`` associated with the mapped table. + + * The Python ``None`` value is typically interpreted as ``NULL``, + which in SQLAlchemy Core produces an instance of :func:`.null`. + + A :class:`.ColumnElement` provides the ability to generate new + :class:`.ColumnElement` + objects using Python expressions. This means that Python operators + such as ``==``, ``!=`` and ``<`` are overloaded to mimic SQL operations, + and allow the instantiation of further :class:`.ColumnElement` instances + which are composed from other, more fundamental :class:`.ColumnElement` + objects. For example, two :class:`.ColumnClause` objects can be added + together with the addition operator ``+`` to produce + a :class:`.BinaryExpression`. + Both :class:`.ColumnClause` and :class:`.BinaryExpression` are subclasses + of :class:`.ColumnElement`:: + + >>> from sqlalchemy.sql import column + >>> column('a') + column('b') + <sqlalchemy.sql.expression.BinaryExpression object at 0x101029dd0> + >>> print column('a') + column('b') + a + b + + .. seealso:: + + :class:`.Column` + + :func:`.expression.column` + + """ + + __visit_name__ = 'column_element' + primary_key = False + foreign_keys = [] + + _label = None + """The named label that can be used to target + this column in a result set. + + This label is almost always the label used when + rendering <expr> AS <label> in a SELECT statement. It also + refers to a name that this column expression can be located from + in a result set. + + For a regular Column bound to a Table, this is typically the label + <tablename>_<columnname>. For other constructs, different rules + may apply, such as anonymized labels and others. + + """ + + key = None + """the 'key' that in some circumstances refers to this object in a + Python namespace. + + This typically refers to the "key" of the column as present in the + ``.c`` collection of a selectable, e.g. sometable.c["somekey"] would + return a Column with a .key of "somekey". + + """ + + _key_label = None + """A label-based version of 'key' that in some circumstances refers + to this object in a Python namespace. + + + _key_label comes into play when a select() statement is constructed with + apply_labels(); in this case, all Column objects in the ``.c`` collection + are rendered as <tablename>_<columnname> in SQL; this is essentially the + value of ._label. But to locate those columns in the ``.c`` collection, + the name is along the lines of <tablename>_<key>; that's the typical + value of .key_label. + + """ + + _render_label_in_columns_clause = True + """A flag used by select._columns_plus_names that helps to determine + we are actually going to render in terms of "SELECT <col> AS <label>". + This flag can be returned as False for some Column objects that want + to be rendered as simple "SELECT <col>"; typically columns that don't have + any parent table and are named the same as what the label would be + in any case. + + """ + + _resolve_label = None + """The name that should be used to identify this ColumnElement in a + select() object when "label resolution" logic is used; this refers + to using a string name in an expression like order_by() or group_by() + that wishes to target a labeled expression in the columns clause. + + The name is distinct from that of .name or ._label to account for the case + where anonymizing logic may be used to change the name that's actually + rendered at compile time; this attribute should hold onto the original + name that was user-assigned when producing a .label() construct. + + """ + + _allow_label_resolve = True + """A flag that can be flipped to prevent a column from being resolvable + by string label name.""" + + _is_implicitly_boolean = False + + _alt_names = () + + def self_group(self, against=None): + if (against in (operators.and_, operators.or_, operators._asbool) and + self.type._type_affinity + is type_api.BOOLEANTYPE._type_affinity): + return AsBoolean(self, operators.istrue, operators.isfalse) + elif (against in (operators.any_op, operators.all_op)): + return Grouping(self) + else: + return self + + def _negate(self): + if self.type._type_affinity is type_api.BOOLEANTYPE._type_affinity: + # TODO: see the note in AsBoolean that it seems to assume + # the element is the True_() / False_() constant, so this + # is too broad + return AsBoolean(self, operators.isfalse, operators.istrue) + else: + return super(ColumnElement, self)._negate() + + @util.memoized_property + def type(self): + return type_api.NULLTYPE + + @util.memoized_property + def comparator(self): + try: + comparator_factory = self.type.comparator_factory + except AttributeError: + raise TypeError( + "Object %r associated with '.type' attribute " + "is not a TypeEngine class or object" % self.type) + else: + return comparator_factory(self) + + def __getattr__(self, key): + try: + return getattr(self.comparator, key) + except AttributeError: + raise AttributeError( + 'Neither %r object nor %r object has an attribute %r' % ( + type(self).__name__, + type(self.comparator).__name__, + key) + ) + + def operate(self, op, *other, **kwargs): + return op(self.comparator, *other, **kwargs) + + def reverse_operate(self, op, other, **kwargs): + return op(other, self.comparator, **kwargs) + + def _bind_param(self, operator, obj, type_=None): + return BindParameter(None, obj, + _compared_to_operator=operator, + type_=type_, + _compared_to_type=self.type, unique=True) + + @property + def expression(self): + """Return a column expression. + + Part of the inspection interface; returns self. + + """ + return self + + @property + def _select_iterable(self): + return (self, ) + + @util.memoized_property + def base_columns(self): + return util.column_set(c for c in self.proxy_set + if not hasattr(c, '_proxies')) + + @util.memoized_property + def proxy_set(self): + s = util.column_set([self]) + if hasattr(self, '_proxies'): + for c in self._proxies: + s.update(c.proxy_set) + return s + + def shares_lineage(self, othercolumn): + """Return True if the given :class:`.ColumnElement` + has a common ancestor to this :class:`.ColumnElement`.""" + + return bool(self.proxy_set.intersection(othercolumn.proxy_set)) + + def _compare_name_for_result(self, other): + """Return True if the given column element compares to this one + when targeting within a result row.""" + + return hasattr(other, 'name') and hasattr(self, 'name') and \ + other.name == self.name + + def _make_proxy( + self, selectable, name=None, name_is_truncatable=False, **kw): + """Create a new :class:`.ColumnElement` representing this + :class:`.ColumnElement` as it appears in the select list of a + descending selectable. + + """ + if name is None: + name = self.anon_label + if self.key: + key = self.key + else: + try: + key = str(self) + except exc.UnsupportedCompilationError: + key = self.anon_label + + else: + key = name + co = ColumnClause( + _as_truncated(name) if name_is_truncatable else name, + type_=getattr(self, 'type', None), + _selectable=selectable + ) + co._proxies = [self] + if selectable._is_clone_of is not None: + co._is_clone_of = \ + selectable._is_clone_of.columns.get(key) + selectable._columns[key] = co + return co + + def compare(self, other, use_proxies=False, equivalents=None, **kw): + """Compare this ColumnElement to another. + + Special arguments understood: + + :param use_proxies: when True, consider two columns that + share a common base column as equivalent (i.e. shares_lineage()) + + :param equivalents: a dictionary of columns as keys mapped to sets + of columns. If the given "other" column is present in this + dictionary, if any of the columns in the corresponding set() pass + the comparison test, the result is True. This is used to expand the + comparison to other columns that may be known to be equivalent to + this one via foreign key or other criterion. + + """ + to_compare = (other, ) + if equivalents and other in equivalents: + to_compare = equivalents[other].union(to_compare) + + for oth in to_compare: + if use_proxies and self.shares_lineage(oth): + return True + elif hash(oth) == hash(self): + return True + else: + return False + + def cast(self, type_): + """Produce a type cast, i.e. ``CAST(<expression> AS <type>)``. + + This is a shortcut to the :func:`~.expression.cast` function. + + .. versionadded:: 1.0.7 + + """ + return Cast(self, type_) + + def label(self, name): + """Produce a column label, i.e. ``<columnname> AS <name>``. + + This is a shortcut to the :func:`~.expression.label` function. + + if 'name' is None, an anonymous label name will be generated. + + """ + return Label(name, self, self.type) + + @util.memoized_property + def anon_label(self): + """provides a constant 'anonymous label' for this ColumnElement. + + This is a label() expression which will be named at compile time. + The same label() is returned each time anon_label is called so + that expressions can reference anon_label multiple times, producing + the same label name at compile time. + + the compiler uses this function automatically at compile time + for expressions that are known to be 'unnamed' like binary + expressions and function calls. + + """ + while self._is_clone_of is not None: + self = self._is_clone_of + + return _anonymous_label( + '%%(%d %s)s' % (id(self), getattr(self, 'name', 'anon')) + ) + + +class BindParameter(ColumnElement): + r"""Represent a "bound expression". + + :class:`.BindParameter` is invoked explicitly using the + :func:`.bindparam` function, as in:: + + from sqlalchemy import bindparam + + stmt = select([users_table]).\ + where(users_table.c.name == bindparam('username')) + + Detailed discussion of how :class:`.BindParameter` is used is + at :func:`.bindparam`. + + .. seealso:: + + :func:`.bindparam` + + """ + + __visit_name__ = 'bindparam' + + _is_crud = False + + def __init__(self, key, value=NO_ARG, type_=None, + unique=False, required=NO_ARG, + quote=None, callable_=None, + expanding=False, + isoutparam=False, + _compared_to_operator=None, + _compared_to_type=None): + r"""Produce a "bound expression". + + The return value is an instance of :class:`.BindParameter`; this + is a :class:`.ColumnElement` subclass which represents a so-called + "placeholder" value in a SQL expression, the value of which is + supplied at the point at which the statement in executed against a + database connection. + + In SQLAlchemy, the :func:`.bindparam` construct has + the ability to carry along the actual value that will be ultimately + used at expression time. In this way, it serves not just as + a "placeholder" for eventual population, but also as a means of + representing so-called "unsafe" values which should not be rendered + directly in a SQL statement, but rather should be passed along + to the :term:`DBAPI` as values which need to be correctly escaped + and potentially handled for type-safety. + + When using :func:`.bindparam` explicitly, the use case is typically + one of traditional deferment of parameters; the :func:`.bindparam` + construct accepts a name which can then be referred to at execution + time:: + + from sqlalchemy import bindparam + + stmt = select([users_table]).\ + where(users_table.c.name == bindparam('username')) + + The above statement, when rendered, will produce SQL similar to:: + + SELECT id, name FROM user WHERE name = :username + + In order to populate the value of ``:username`` above, the value + would typically be applied at execution time to a method + like :meth:`.Connection.execute`:: + + result = connection.execute(stmt, username='wendy') + + Explicit use of :func:`.bindparam` is also common when producing + UPDATE or DELETE statements that are to be invoked multiple times, + where the WHERE criterion of the statement is to change on each + invocation, such as:: + + stmt = (users_table.update(). + where(user_table.c.name == bindparam('username')). + values(fullname=bindparam('fullname')) + ) + + connection.execute( + stmt, [{"username": "wendy", "fullname": "Wendy Smith"}, + {"username": "jack", "fullname": "Jack Jones"}, + ] + ) + + SQLAlchemy's Core expression system makes wide use of + :func:`.bindparam` in an implicit sense. It is typical that Python + literal values passed to virtually all SQL expression functions are + coerced into fixed :func:`.bindparam` constructs. For example, given + a comparison operation such as:: + + expr = users_table.c.name == 'Wendy' + + The above expression will produce a :class:`.BinaryExpression` + construct, where the left side is the :class:`.Column` object + representing the ``name`` column, and the right side is a + :class:`.BindParameter` representing the literal value:: + + print(repr(expr.right)) + BindParameter('%(4327771088 name)s', 'Wendy', type_=String()) + + The expression above will render SQL such as:: + + user.name = :name_1 + + Where the ``:name_1`` parameter name is an anonymous name. The + actual string ``Wendy`` is not in the rendered string, but is carried + along where it is later used within statement execution. If we + invoke a statement like the following:: + + stmt = select([users_table]).where(users_table.c.name == 'Wendy') + result = connection.execute(stmt) + + We would see SQL logging output as:: + + SELECT "user".id, "user".name + FROM "user" + WHERE "user".name = %(name_1)s + {'name_1': 'Wendy'} + + Above, we see that ``Wendy`` is passed as a parameter to the database, + while the placeholder ``:name_1`` is rendered in the appropriate form + for the target database, in this case the PostgreSQL database. + + Similarly, :func:`.bindparam` is invoked automatically + when working with :term:`CRUD` statements as far as the "VALUES" + portion is concerned. The :func:`.insert` construct produces an + ``INSERT`` expression which will, at statement execution time, + generate bound placeholders based on the arguments passed, as in:: + + stmt = users_table.insert() + result = connection.execute(stmt, name='Wendy') + + The above will produce SQL output as:: + + INSERT INTO "user" (name) VALUES (%(name)s) + {'name': 'Wendy'} + + The :class:`.Insert` construct, at compilation/execution time, + rendered a single :func:`.bindparam` mirroring the column + name ``name`` as a result of the single ``name`` parameter + we passed to the :meth:`.Connection.execute` method. + + :param key: + the key (e.g. the name) for this bind param. + Will be used in the generated + SQL statement for dialects that use named parameters. This + value may be modified when part of a compilation operation, + if other :class:`BindParameter` objects exist with the same + key, or if its length is too long and truncation is + required. + + :param value: + Initial value for this bind param. Will be used at statement + execution time as the value for this parameter passed to the + DBAPI, if no other value is indicated to the statement execution + method for this particular parameter name. Defaults to ``None``. + + :param callable\_: + A callable function that takes the place of "value". The function + will be called at statement execution time to determine the + ultimate value. Used for scenarios where the actual bind + value cannot be determined at the point at which the clause + construct is created, but embedded bind values are still desirable. + + :param type\_: + A :class:`.TypeEngine` class or instance representing an optional + datatype for this :func:`.bindparam`. If not passed, a type + may be determined automatically for the bind, based on the given + value; for example, trivial Python types such as ``str``, + ``int``, ``bool`` + may result in the :class:`.String`, :class:`.Integer` or + :class:`.Boolean` types being automatically selected. + + The type of a :func:`.bindparam` is significant especially in that + the type will apply pre-processing to the value before it is + passed to the database. For example, a :func:`.bindparam` which + refers to a datetime value, and is specified as holding the + :class:`.DateTime` type, may apply conversion needed to the + value (such as stringification on SQLite) before passing the value + to the database. + + :param unique: + if True, the key name of this :class:`.BindParameter` will be + modified if another :class:`.BindParameter` of the same name + already has been located within the containing + expression. This flag is used generally by the internals + when producing so-called "anonymous" bound expressions, it + isn't generally applicable to explicitly-named :func:`.bindparam` + constructs. + + :param required: + If ``True``, a value is required at execution time. If not passed, + it defaults to ``True`` if neither :paramref:`.bindparam.value` + or :paramref:`.bindparam.callable` were passed. If either of these + parameters are present, then :paramref:`.bindparam.required` + defaults to ``False``. + + .. versionchanged:: 0.8 If the ``required`` flag is not specified, + it will be set automatically to ``True`` or ``False`` depending + on whether or not the ``value`` or ``callable`` parameters + were specified. + + :param quote: + True if this parameter name requires quoting and is not + currently known as a SQLAlchemy reserved word; this currently + only applies to the Oracle backend, where bound names must + sometimes be quoted. + + :param isoutparam: + if True, the parameter should be treated like a stored procedure + "OUT" parameter. This applies to backends such as Oracle which + support OUT parameters. + + :param expanding: + if True, this parameter will be treated as an "expanding" parameter + at execution time; the parameter value is expected to be a sequence, + rather than a scalar value, and the string SQL statement will + be transformed on a per-execution basis to accomodate the sequence + with a variable number of parameter slots passed to the DBAPI. + This is to allow statement caching to be used in conjunction with + an IN clause. + + .. seealso:: + + :meth:`.ColumnOperators.in_` + + :ref:`baked_in` - with baked queries + + .. note:: The "expanding" feature does not support "executemany"- + style parameter sets. In the 1.2 series it does not support + empty IN expressions, however it does support these in + version 1.3. + + .. versionadded:: 1.2 + + .. seealso:: + + :ref:`coretutorial_bind_param` + + :ref:`coretutorial_insert_expressions` + + :func:`.outparam` + + """ + if isinstance(key, ColumnClause): + type_ = key.type + key = key.key + if required is NO_ARG: + required = (value is NO_ARG and callable_ is None) + if value is NO_ARG: + value = None + + if quote is not None: + key = quoted_name(key, quote) + + if unique: + self.key = _anonymous_label('%%(%d %s)s' % (id(self), key + or 'param')) + else: + self.key = key or _anonymous_label('%%(%d param)s' + % id(self)) + + # identifying key that won't change across + # clones, used to identify the bind's logical + # identity + self._identifying_key = self.key + + # key that was passed in the first place, used to + # generate new keys + self._orig_key = key or 'param' + + self.unique = unique + self.value = value + self.callable = callable_ + self.isoutparam = isoutparam + self.required = required + self.expanding = expanding + + if type_ is None: + if _compared_to_type is not None: + self.type = \ + _compared_to_type.coerce_compared_value( + _compared_to_operator, value) + else: + self.type = type_api._resolve_value_to_type(value) + elif isinstance(type_, type): + self.type = type_() + else: + self.type = type_ + + def _with_value(self, value): + """Return a copy of this :class:`.BindParameter` with the given value + set. + """ + cloned = self._clone() + cloned.value = value + cloned.callable = None + cloned.required = False + if cloned.type is type_api.NULLTYPE: + cloned.type = type_api._resolve_value_to_type(value) + return cloned + + @property + def effective_value(self): + """Return the value of this bound parameter, + taking into account if the ``callable`` parameter + was set. + + The ``callable`` value will be evaluated + and returned if present, else ``value``. + + """ + if self.callable: + return self.callable() + else: + return self.value + + def _clone(self): + c = ClauseElement._clone(self) + if self.unique: + c.key = _anonymous_label('%%(%d %s)s' % (id(c), c._orig_key + or 'param')) + return c + + def _convert_to_unique(self): + if not self.unique: + self.unique = True + self.key = _anonymous_label( + '%%(%d %s)s' % (id(self), self._orig_key or 'param')) + + def compare(self, other, **kw): + """Compare this :class:`BindParameter` to the given + clause.""" + + return isinstance(other, BindParameter) \ + and self.type._compare_type_affinity(other.type) \ + and self.value == other.value \ + and self.callable == other.callable + + def __getstate__(self): + """execute a deferred value for serialization purposes.""" + + d = self.__dict__.copy() + v = self.value + if self.callable: + v = self.callable() + d['callable'] = None + d['value'] = v + return d + + def __repr__(self): + return 'BindParameter(%r, %r, type_=%r)' % (self.key, + self.value, self.type) + + +class TypeClause(ClauseElement): + """Handle a type keyword in a SQL statement. + + Used by the ``Case`` statement. + + """ + + __visit_name__ = 'typeclause' + + def __init__(self, type): + self.type = type + + +class TextClause(Executable, ClauseElement): + """Represent a literal SQL text fragment. + + E.g.:: + + from sqlalchemy import text + + t = text("SELECT * FROM users") + result = connection.execute(t) + + + The :class:`.Text` construct is produced using the :func:`.text` + function; see that function for full documentation. + + .. seealso:: + + :func:`.text` + + """ + + __visit_name__ = 'textclause' + + _bind_params_regex = re.compile(r'(?<![:\w\x5c]):(\w+)(?!:)', re.UNICODE) + _execution_options = \ + Executable._execution_options.union( + {'autocommit': PARSE_AUTOCOMMIT}) + _is_implicitly_boolean = False + + @property + def _select_iterable(self): + return (self,) + + @property + def selectable(self): + # allows text() to be considered by + # _interpret_as_from + return self + + _hide_froms = [] + + # help in those cases where text() is + # interpreted in a column expression situation + key = _label = _resolve_label = None + + _allow_label_resolve = False + + def __init__( + self, + text, + bind=None): + self._bind = bind + self._bindparams = {} + + def repl(m): + self._bindparams[m.group(1)] = BindParameter(m.group(1)) + return ':%s' % m.group(1) + + # scan the string and search for bind parameter names, add them + # to the list of bindparams + self.text = self._bind_params_regex.sub(repl, text) + + @classmethod + def _create_text(self, text, bind=None, bindparams=None, + typemap=None, autocommit=None): + r"""Construct a new :class:`.TextClause` clause, representing + a textual SQL string directly. + + E.g.:: + + from sqlalchemy import text + + t = text("SELECT * FROM users") + result = connection.execute(t) + + The advantages :func:`.text` provides over a plain string are + backend-neutral support for bind parameters, per-statement + execution options, as well as + bind parameter and result-column typing behavior, allowing + SQLAlchemy type constructs to play a role when executing + a statement that is specified literally. The construct can also + be provided with a ``.c`` collection of column elements, allowing + it to be embedded in other SQL expression constructs as a subquery. + + Bind parameters are specified by name, using the format ``:name``. + E.g.:: + + t = text("SELECT * FROM users WHERE id=:user_id") + result = connection.execute(t, user_id=12) + + For SQL statements where a colon is required verbatim, as within + an inline string, use a backslash to escape:: + + t = text("SELECT * FROM users WHERE name='\:username'") + + The :class:`.TextClause` construct includes methods which can + provide information about the bound parameters as well as the column + values which would be returned from the textual statement, assuming + it's an executable SELECT type of statement. The + :meth:`.TextClause.bindparams` method is used to provide bound + parameter detail, and :meth:`.TextClause.columns` method allows + specification of return columns including names and types:: + + t = text("SELECT * FROM users WHERE id=:user_id").\ + bindparams(user_id=7).\ + columns(id=Integer, name=String) + + for id, name in connection.execute(t): + print(id, name) + + The :func:`.text` construct is used in cases when + a literal string SQL fragment is specified as part of a larger query, + such as for the WHERE clause of a SELECT statement:: + + s = select([users.c.id, users.c.name]).where(text("id=:user_id")) + result = connection.execute(s, user_id=12) + + :func:`.text` is also used for the construction + of a full, standalone statement using plain text. + As such, SQLAlchemy refers + to it as an :class:`.Executable` object, and it supports + the :meth:`Executable.execution_options` method. For example, + a :func:`.text` construct that should be subject to "autocommit" + can be set explicitly so using the + :paramref:`.Connection.execution_options.autocommit` option:: + + t = text("EXEC my_procedural_thing()").\ + execution_options(autocommit=True) + + Note that SQLAlchemy's usual "autocommit" behavior applies to + :func:`.text` constructs implicitly - that is, statements which begin + with a phrase such as ``INSERT``, ``UPDATE``, ``DELETE``, + or a variety of other phrases specific to certain backends, will + be eligible for autocommit if no transaction is in progress. + + :param text: + the text of the SQL statement to be created. use ``:<param>`` + to specify bind parameters; they will be compiled to their + engine-specific format. + + :param autocommit: + Deprecated. Use .execution_options(autocommit=<True|False>) + to set the autocommit option. + + :param bind: + an optional connection or engine to be used for this text query. + + :param bindparams: + Deprecated. A list of :func:`.bindparam` instances used to + provide information about parameters embedded in the statement. + This argument now invokes the :meth:`.TextClause.bindparams` + method on the construct before returning it. E.g.:: + + stmt = text("SELECT * FROM table WHERE id=:id", + bindparams=[bindparam('id', value=5, type_=Integer)]) + + Is equivalent to:: + + stmt = text("SELECT * FROM table WHERE id=:id").\ + bindparams(bindparam('id', value=5, type_=Integer)) + + .. deprecated:: 0.9.0 the :meth:`.TextClause.bindparams` method + supersedes the ``bindparams`` argument to :func:`.text`. + + :param typemap: + Deprecated. A dictionary mapping the names of columns + represented in the columns clause of a ``SELECT`` statement + to type objects, + which will be used to perform post-processing on columns within + the result set. This parameter now invokes the + :meth:`.TextClause.columns` method, which returns a + :class:`.TextAsFrom` construct that gains a ``.c`` collection and + can be embedded in other expressions. E.g.:: + + stmt = text("SELECT * FROM table", + typemap={'id': Integer, 'name': String}, + ) + + Is equivalent to:: + + stmt = text("SELECT * FROM table").columns(id=Integer, + name=String) + + Or alternatively:: + + from sqlalchemy.sql import column + stmt = text("SELECT * FROM table").columns( + column('id', Integer), + column('name', String) + ) + + .. deprecated:: 0.9.0 the :meth:`.TextClause.columns` method + supersedes the ``typemap`` argument to :func:`.text`. + + .. seealso:: + + :ref:`sqlexpression_text` - in the Core tutorial + + :ref:`orm_tutorial_literal_sql` - in the ORM tutorial + + """ + stmt = TextClause(text, bind=bind) + if bindparams: + stmt = stmt.bindparams(*bindparams) + if typemap: + stmt = stmt.columns(**typemap) + if autocommit is not None: + util.warn_deprecated('autocommit on text() is deprecated. ' + 'Use .execution_options(autocommit=True)') + stmt = stmt.execution_options(autocommit=autocommit) + + return stmt + + @_generative + def bindparams(self, *binds, **names_to_values): + """Establish the values and/or types of bound parameters within + this :class:`.TextClause` construct. + + Given a text construct such as:: + + from sqlalchemy import text + stmt = text("SELECT id, name FROM user WHERE name=:name " + "AND timestamp=:timestamp") + + the :meth:`.TextClause.bindparams` method can be used to establish + the initial value of ``:name`` and ``:timestamp``, + using simple keyword arguments:: + + stmt = stmt.bindparams(name='jack', + timestamp=datetime.datetime(2012, 10, 8, 15, 12, 5)) + + Where above, new :class:`.BindParameter` objects + will be generated with the names ``name`` and ``timestamp``, and + values of ``jack`` and ``datetime.datetime(2012, 10, 8, 15, 12, 5)``, + respectively. The types will be + inferred from the values given, in this case :class:`.String` and + :class:`.DateTime`. + + When specific typing behavior is needed, the positional ``*binds`` + argument can be used in which to specify :func:`.bindparam` constructs + directly. These constructs must include at least the ``key`` + argument, then an optional value and type:: + + from sqlalchemy import bindparam + stmt = stmt.bindparams( + bindparam('name', value='jack', type_=String), + bindparam('timestamp', type_=DateTime) + ) + + Above, we specified the type of :class:`.DateTime` for the + ``timestamp`` bind, and the type of :class:`.String` for the ``name`` + bind. In the case of ``name`` we also set the default value of + ``"jack"``. + + Additional bound parameters can be supplied at statement execution + time, e.g.:: + + result = connection.execute(stmt, + timestamp=datetime.datetime(2012, 10, 8, 15, 12, 5)) + + The :meth:`.TextClause.bindparams` method can be called repeatedly, + where it will re-use existing :class:`.BindParameter` objects to add + new information. For example, we can call + :meth:`.TextClause.bindparams` first with typing information, and a + second time with value information, and it will be combined:: + + stmt = text("SELECT id, name FROM user WHERE name=:name " + "AND timestamp=:timestamp") + stmt = stmt.bindparams( + bindparam('name', type_=String), + bindparam('timestamp', type_=DateTime) + ) + stmt = stmt.bindparams( + name='jack', + timestamp=datetime.datetime(2012, 10, 8, 15, 12, 5) + ) + + + .. versionadded:: 0.9.0 The :meth:`.TextClause.bindparams` method + supersedes the argument ``bindparams`` passed to + :func:`~.expression.text`. + + + """ + self._bindparams = new_params = self._bindparams.copy() + + for bind in binds: + try: + existing = new_params[bind.key] + except KeyError: + raise exc.ArgumentError( + "This text() construct doesn't define a " + "bound parameter named %r" % bind.key) + else: + new_params[existing.key] = bind + + for key, value in names_to_values.items(): + try: + existing = new_params[key] + except KeyError: + raise exc.ArgumentError( + "This text() construct doesn't define a " + "bound parameter named %r" % key) + else: + new_params[key] = existing._with_value(value) + + @util.dependencies('sqlalchemy.sql.selectable') + def columns(self, selectable, *cols, **types): + """Turn this :class:`.TextClause` object into a :class:`.TextAsFrom` + object that can be embedded into another statement. + + This function essentially bridges the gap between an entirely + textual SELECT statement and the SQL expression language concept + of a "selectable":: + + from sqlalchemy.sql import column, text + + stmt = text("SELECT id, name FROM some_table") + stmt = stmt.columns(column('id'), column('name')).alias('st') + + stmt = select([mytable]).\ + select_from( + mytable.join(stmt, mytable.c.name == stmt.c.name) + ).where(stmt.c.id > 5) + + Above, we pass a series of :func:`.column` elements to the + :meth:`.TextClause.columns` method positionally. These :func:`.column` + elements now become first class elements upon the :attr:`.TextAsFrom.c` + column collection, just like any other selectable. + + The column expressions we pass to :meth:`.TextClause.columns` may + also be typed; when we do so, these :class:`.TypeEngine` objects become + the effective return type of the column, so that SQLAlchemy's + result-set-processing systems may be used on the return values. + This is often needed for types such as date or boolean types, as well + as for unicode processing on some dialect configurations:: + + stmt = text("SELECT id, name, timestamp FROM some_table") + stmt = stmt.columns( + column('id', Integer), + column('name', Unicode), + column('timestamp', DateTime) + ) + + for id, name, timestamp in connection.execute(stmt): + print(id, name, timestamp) + + As a shortcut to the above syntax, keyword arguments referring to + types alone may be used, if only type conversion is needed:: + + stmt = text("SELECT id, name, timestamp FROM some_table") + stmt = stmt.columns( + id=Integer, + name=Unicode, + timestamp=DateTime + ) + + for id, name, timestamp in connection.execute(stmt): + print(id, name, timestamp) + + The positional form of :meth:`.TextClause.columns` also provides + the unique feature of **positional column targeting**, which is + particularly useful when using the ORM with complex textual queries. + If we specify the columns from our model to :meth:`.TextClause.columns`, + the result set will match to those columns positionally, meaning the + name or origin of the column in the textual SQL doesn't matter:: + + stmt = text("SELECT users.id, addresses.id, users.id, " + "users.name, addresses.email_address AS email " + "FROM users JOIN addresses ON users.id=addresses.user_id " + "WHERE users.id = 1").columns( + User.id, + Address.id, + Address.user_id, + User.name, + Address.email_address + ) + + query = session.query(User).from_statement(stmt).options( + contains_eager(User.addresses)) + + .. versionadded:: 1.1 the :meth:`.TextClause.columns` method now + offers positional column targeting in the result set when + the column expressions are passed purely positionally. + + The :meth:`.TextClause.columns` method provides a direct + route to calling :meth:`.FromClause.alias` as well as + :meth:`.SelectBase.cte` against a textual SELECT statement:: + + stmt = stmt.columns(id=Integer, name=String).cte('st') + + stmt = select([sometable]).where(sometable.c.id == stmt.c.id) + + .. versionadded:: 0.9.0 :func:`.text` can now be converted into a + fully featured "selectable" construct using the + :meth:`.TextClause.columns` method. This method supersedes the + ``typemap`` argument to :func:`.text`. + + + """ + + positional_input_cols = [ + ColumnClause(col.key, types.pop(col.key)) + if col.key in types + else col + for col in cols + ] + keyed_input_cols = [ + ColumnClause(key, type_) for key, type_ in types.items()] + + return selectable.TextAsFrom( + self, + positional_input_cols + keyed_input_cols, + positional=bool(positional_input_cols) and not keyed_input_cols) + + @property + def type(self): + return type_api.NULLTYPE + + @property + def comparator(self): + return self.type.comparator_factory(self) + + def self_group(self, against=None): + if against is operators.in_op: + return Grouping(self) + else: + return self + + def _copy_internals(self, clone=_clone, **kw): + self._bindparams = dict((b.key, clone(b, **kw)) + for b in self._bindparams.values()) + + def get_children(self, **kwargs): + return list(self._bindparams.values()) + + def compare(self, other): + return isinstance(other, TextClause) and other.text == self.text + + +class Null(ColumnElement): + """Represent the NULL keyword in a SQL statement. + + :class:`.Null` is accessed as a constant via the + :func:`.null` function. + + """ + + __visit_name__ = 'null' + + @util.memoized_property + def type(self): + return type_api.NULLTYPE + + @classmethod + def _instance(cls): + """Return a constant :class:`.Null` construct.""" + + return Null() + + def compare(self, other): + return isinstance(other, Null) + + +class False_(ColumnElement): + """Represent the ``false`` keyword, or equivalent, in a SQL statement. + + :class:`.False_` is accessed as a constant via the + :func:`.false` function. + + """ + + __visit_name__ = 'false' + + @util.memoized_property + def type(self): + return type_api.BOOLEANTYPE + + def _negate(self): + return True_() + + @classmethod + def _instance(cls): + """Return a :class:`.False_` construct. + + E.g.:: + + >>> from sqlalchemy import false + >>> print select([t.c.x]).where(false()) + SELECT x FROM t WHERE false + + A backend which does not support true/false constants will render as + an expression against 1 or 0:: + + >>> print select([t.c.x]).where(false()) + SELECT x FROM t WHERE 0 = 1 + + The :func:`.true` and :func:`.false` constants also feature + "short circuit" operation within an :func:`.and_` or :func:`.or_` + conjunction:: + + >>> print select([t.c.x]).where(or_(t.c.x > 5, true())) + SELECT x FROM t WHERE true + + >>> print select([t.c.x]).where(and_(t.c.x > 5, false())) + SELECT x FROM t WHERE false + + .. versionchanged:: 0.9 :func:`.true` and :func:`.false` feature + better integrated behavior within conjunctions and on dialects + that don't support true/false constants. + + .. seealso:: + + :func:`.true` + + """ + + return False_() + + def compare(self, other): + return isinstance(other, False_) + + +class True_(ColumnElement): + """Represent the ``true`` keyword, or equivalent, in a SQL statement. + + :class:`.True_` is accessed as a constant via the + :func:`.true` function. + + """ + + __visit_name__ = 'true' + + @util.memoized_property + def type(self): + return type_api.BOOLEANTYPE + + def _negate(self): + return False_() + + @classmethod + def _ifnone(cls, other): + if other is None: + return cls._instance() + else: + return other + + @classmethod + def _instance(cls): + """Return a constant :class:`.True_` construct. + + E.g.:: + + >>> from sqlalchemy import true + >>> print select([t.c.x]).where(true()) + SELECT x FROM t WHERE true + + A backend which does not support true/false constants will render as + an expression against 1 or 0:: + + >>> print select([t.c.x]).where(true()) + SELECT x FROM t WHERE 1 = 1 + + The :func:`.true` and :func:`.false` constants also feature + "short circuit" operation within an :func:`.and_` or :func:`.or_` + conjunction:: + + >>> print select([t.c.x]).where(or_(t.c.x > 5, true())) + SELECT x FROM t WHERE true + + >>> print select([t.c.x]).where(and_(t.c.x > 5, false())) + SELECT x FROM t WHERE false + + .. versionchanged:: 0.9 :func:`.true` and :func:`.false` feature + better integrated behavior within conjunctions and on dialects + that don't support true/false constants. + + .. seealso:: + + :func:`.false` + + """ + + return True_() + + def compare(self, other): + return isinstance(other, True_) + + +class ClauseList(ClauseElement): + """Describe a list of clauses, separated by an operator. + + By default, is comma-separated, such as a column listing. + + """ + __visit_name__ = 'clauselist' + + def __init__(self, *clauses, **kwargs): + self.operator = kwargs.pop('operator', operators.comma_op) + self.group = kwargs.pop('group', True) + self.group_contents = kwargs.pop('group_contents', True) + text_converter = kwargs.pop( + '_literal_as_text', + _expression_literal_as_text) + if self.group_contents: + self.clauses = [ + text_converter(clause).self_group(against=self.operator) + for clause in clauses] + else: + self.clauses = [ + text_converter(clause) + for clause in clauses] + self._is_implicitly_boolean = operators.is_boolean(self.operator) + + def __iter__(self): + return iter(self.clauses) + + def __len__(self): + return len(self.clauses) + + @property + def _select_iterable(self): + return iter(self) + + def append(self, clause): + if self.group_contents: + self.clauses.append(_literal_as_text(clause). + self_group(against=self.operator)) + else: + self.clauses.append(_literal_as_text(clause)) + + def _copy_internals(self, clone=_clone, **kw): + self.clauses = [clone(clause, **kw) for clause in self.clauses] + + def get_children(self, **kwargs): + return self.clauses + + @property + def _from_objects(self): + return list(itertools.chain(*[c._from_objects for c in self.clauses])) + + def self_group(self, against=None): + if self.group and operators.is_precedent(self.operator, against): + return Grouping(self) + else: + return self + + def compare(self, other, **kw): + """Compare this :class:`.ClauseList` to the given :class:`.ClauseList`, + including a comparison of all the clause items. + + """ + if not isinstance(other, ClauseList) and len(self.clauses) == 1: + return self.clauses[0].compare(other, **kw) + elif isinstance(other, ClauseList) and \ + len(self.clauses) == len(other.clauses) and \ + self.operator is other.operator: + + if self.operator in (operators.and_, operators.or_): + completed = set() + for clause in self.clauses: + for other_clause in set(other.clauses).difference(completed): + if clause.compare(other_clause, **kw): + completed.add(other_clause) + break + return len(completed) == len(other.clauses) + else: + for i in range(0, len(self.clauses)): + if not self.clauses[i].compare(other.clauses[i], **kw): + return False + else: + return True + else: + return False + + +class BooleanClauseList(ClauseList, ColumnElement): + __visit_name__ = 'clauselist' + + def __init__(self, *arg, **kw): + raise NotImplementedError( + "BooleanClauseList has a private constructor") + + @classmethod + def _construct(cls, operator, continue_on, skip_on, *clauses, **kw): + convert_clauses = [] + + clauses = [ + _expression_literal_as_text(clause) + for clause in + util.coerce_generator_arg(clauses) + ] + for clause in clauses: + + if isinstance(clause, continue_on): + continue + elif isinstance(clause, skip_on): + return clause.self_group(against=operators._asbool) + + convert_clauses.append(clause) + + if len(convert_clauses) == 1: + return convert_clauses[0].self_group(against=operators._asbool) + elif not convert_clauses and clauses: + return clauses[0].self_group(against=operators._asbool) + + convert_clauses = [c.self_group(against=operator) + for c in convert_clauses] + + self = cls.__new__(cls) + self.clauses = convert_clauses + self.group = True + self.operator = operator + self.group_contents = True + self.type = type_api.BOOLEANTYPE + self._is_implicitly_boolean = True + return self + + @classmethod + def and_(cls, *clauses): + """Produce a conjunction of expressions joined by ``AND``. + + E.g.:: + + from sqlalchemy import and_ + + stmt = select([users_table]).where( + and_( + users_table.c.name == 'wendy', + users_table.c.enrolled == True + ) + ) + + The :func:`.and_` conjunction is also available using the + Python ``&`` operator (though note that compound expressions + need to be parenthesized in order to function with Python + operator precedence behavior):: + + stmt = select([users_table]).where( + (users_table.c.name == 'wendy') & + (users_table.c.enrolled == True) + ) + + The :func:`.and_` operation is also implicit in some cases; + the :meth:`.Select.where` method for example can be invoked multiple + times against a statement, which will have the effect of each + clause being combined using :func:`.and_`:: + + stmt = select([users_table]).\ + where(users_table.c.name == 'wendy').\ + where(users_table.c.enrolled == True) + + .. seealso:: + + :func:`.or_` + + """ + return cls._construct(operators.and_, True_, False_, *clauses) + + @classmethod + def or_(cls, *clauses): + """Produce a conjunction of expressions joined by ``OR``. + + E.g.:: + + from sqlalchemy import or_ + + stmt = select([users_table]).where( + or_( + users_table.c.name == 'wendy', + users_table.c.name == 'jack' + ) + ) + + The :func:`.or_` conjunction is also available using the + Python ``|`` operator (though note that compound expressions + need to be parenthesized in order to function with Python + operator precedence behavior):: + + stmt = select([users_table]).where( + (users_table.c.name == 'wendy') | + (users_table.c.name == 'jack') + ) + + .. seealso:: + + :func:`.and_` + + """ + return cls._construct(operators.or_, False_, True_, *clauses) + + @property + def _select_iterable(self): + return (self, ) + + def self_group(self, against=None): + if not self.clauses: + return self + else: + return super(BooleanClauseList, self).self_group(against=against) + + def _negate(self): + return ClauseList._negate(self) + + +and_ = BooleanClauseList.and_ +or_ = BooleanClauseList.or_ + + +class Tuple(ClauseList, ColumnElement): + """Represent a SQL tuple.""" + + def __init__(self, *clauses, **kw): + """Return a :class:`.Tuple`. + + Main usage is to produce a composite IN construct:: + + from sqlalchemy import tuple_ + + tuple_(table.c.col1, table.c.col2).in_( + [(1, 2), (5, 12), (10, 19)] + ) + + .. warning:: + + The composite IN construct is not supported by all backends, + and is currently known to work on PostgreSQL and MySQL, + but not SQLite. Unsupported backends will raise + a subclass of :class:`~sqlalchemy.exc.DBAPIError` when such + an expression is invoked. + + """ + + clauses = [_literal_as_binds(c) for c in clauses] + self._type_tuple = [arg.type for arg in clauses] + self.type = kw.pop('type_', self._type_tuple[0] + if self._type_tuple else type_api.NULLTYPE) + + super(Tuple, self).__init__(*clauses, **kw) + + @property + def _select_iterable(self): + return (self, ) + + def _bind_param(self, operator, obj, type_=None): + return Tuple(*[ + BindParameter(None, o, _compared_to_operator=operator, + _compared_to_type=compared_to_type, unique=True, + type_=type_) + for o, compared_to_type in zip(obj, self._type_tuple) + ]).self_group() + + +class Case(ColumnElement): + """Represent a ``CASE`` expression. + + :class:`.Case` is produced using the :func:`.case` factory function, + as in:: + + from sqlalchemy import case + + stmt = select([users_table]).\ + where( + case( + [ + (users_table.c.name == 'wendy', 'W'), + (users_table.c.name == 'jack', 'J') + ], + else_='E' + ) + ) + + Details on :class:`.Case` usage is at :func:`.case`. + + .. seealso:: + + :func:`.case` + + """ + + __visit_name__ = 'case' + + def __init__(self, whens, value=None, else_=None): + r"""Produce a ``CASE`` expression. + + The ``CASE`` construct in SQL is a conditional object that + acts somewhat analogously to an "if/then" construct in other + languages. It returns an instance of :class:`.Case`. + + :func:`.case` in its usual form is passed a list of "when" + constructs, that is, a list of conditions and results as tuples:: + + from sqlalchemy import case + + stmt = select([users_table]).\ + where( + case( + [ + (users_table.c.name == 'wendy', 'W'), + (users_table.c.name == 'jack', 'J') + ], + else_='E' + ) + ) + + The above statement will produce SQL resembling:: + + SELECT id, name FROM user + WHERE CASE + WHEN (name = :name_1) THEN :param_1 + WHEN (name = :name_2) THEN :param_2 + ELSE :param_3 + END + + When simple equality expressions of several values against a single + parent column are needed, :func:`.case` also has a "shorthand" format + used via the + :paramref:`.case.value` parameter, which is passed a column + expression to be compared. In this form, the :paramref:`.case.whens` + parameter is passed as a dictionary containing expressions to be + compared against keyed to result expressions. The statement below is + equivalent to the preceding statement:: + + stmt = select([users_table]).\ + where( + case( + {"wendy": "W", "jack": "J"}, + value=users_table.c.name, + else_='E' + ) + ) + + The values which are accepted as result values in + :paramref:`.case.whens` as well as with :paramref:`.case.else_` are + coerced from Python literals into :func:`.bindparam` constructs. + SQL expressions, e.g. :class:`.ColumnElement` constructs, are accepted + as well. To coerce a literal string expression into a constant + expression rendered inline, use the :func:`.literal_column` construct, + as in:: + + from sqlalchemy import case, literal_column + + case( + [ + ( + orderline.c.qty > 100, + literal_column("'greaterthan100'") + ), + ( + orderline.c.qty > 10, + literal_column("'greaterthan10'") + ) + ], + else_=literal_column("'lessthan10'") + ) + + The above will render the given constants without using bound + parameters for the result values (but still for the comparison + values), as in:: + + CASE + WHEN (orderline.qty > :qty_1) THEN 'greaterthan100' + WHEN (orderline.qty > :qty_2) THEN 'greaterthan10' + ELSE 'lessthan10' + END + + :param whens: The criteria to be compared against, + :paramref:`.case.whens` accepts two different forms, based on + whether or not :paramref:`.case.value` is used. + + In the first form, it accepts a list of 2-tuples; each 2-tuple + consists of ``(<sql expression>, <value>)``, where the SQL + expression is a boolean expression and "value" is a resulting value, + e.g.:: + + case([ + (users_table.c.name == 'wendy', 'W'), + (users_table.c.name == 'jack', 'J') + ]) + + In the second form, it accepts a Python dictionary of comparison + values mapped to a resulting value; this form requires + :paramref:`.case.value` to be present, and values will be compared + using the ``==`` operator, e.g.:: + + case( + {"wendy": "W", "jack": "J"}, + value=users_table.c.name + ) + + :param value: An optional SQL expression which will be used as a + fixed "comparison point" for candidate values within a dictionary + passed to :paramref:`.case.whens`. + + :param else\_: An optional SQL expression which will be the evaluated + result of the ``CASE`` construct if all expressions within + :paramref:`.case.whens` evaluate to false. When omitted, most + databases will produce a result of NULL if none of the "when" + expressions evaluate to true. + + + """ + + try: + whens = util.dictlike_iteritems(whens) + except TypeError: + pass + + if value is not None: + whenlist = [ + (_literal_as_binds(c).self_group(), + _literal_as_binds(r)) for (c, r) in whens + ] + else: + whenlist = [ + (_no_literals(c).self_group(), + _literal_as_binds(r)) for (c, r) in whens + ] + + if whenlist: + type_ = list(whenlist[-1])[-1].type + else: + type_ = None + + if value is None: + self.value = None + else: + self.value = _literal_as_binds(value) + + self.type = type_ + self.whens = whenlist + if else_ is not None: + self.else_ = _literal_as_binds(else_) + else: + self.else_ = None + + def _copy_internals(self, clone=_clone, **kw): + if self.value is not None: + self.value = clone(self.value, **kw) + self.whens = [(clone(x, **kw), clone(y, **kw)) + for x, y in self.whens] + if self.else_ is not None: + self.else_ = clone(self.else_, **kw) + + def get_children(self, **kwargs): + if self.value is not None: + yield self.value + for x, y in self.whens: + yield x + yield y + if self.else_ is not None: + yield self.else_ + + @property + def _from_objects(self): + return list(itertools.chain(*[x._from_objects for x in + self.get_children()])) + + +def literal_column(text, type_=None): + r"""Produce a :class:`.ColumnClause` object that has the + :paramref:`.column.is_literal` flag set to True. + + :func:`.literal_column` is similar to :func:`.column`, except that + it is more often used as a "standalone" column expression that renders + exactly as stated; while :func:`.column` stores a string name that + will be assumed to be part of a table and may be quoted as such, + :func:`.literal_column` can be that, or any other arbitrary column-oriented + expression. + + :param text: the text of the expression; can be any SQL expression. + Quoting rules will not be applied. To specify a column-name expression + which should be subject to quoting rules, use the :func:`column` + function. + + :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine` + object which will + provide result-set translation and additional expression semantics for + this column. If left as None the type will be NullType. + + .. seealso:: + + :func:`.column` + + :func:`.text` + + :ref:`sqlexpression_literal_column` + + """ + return ColumnClause(text, type_=type_, is_literal=True) + + +class Cast(ColumnElement): + """Represent a ``CAST`` expression. + + :class:`.Cast` is produced using the :func:`.cast` factory function, + as in:: + + from sqlalchemy import cast, Numeric + + stmt = select([ + cast(product_table.c.unit_price, Numeric(10, 4)) + ]) + + Details on :class:`.Cast` usage is at :func:`.cast`. + + .. seealso:: + + :func:`.cast` + + """ + + __visit_name__ = 'cast' + + def __init__(self, expression, type_): + """Produce a ``CAST`` expression. + + :func:`.cast` returns an instance of :class:`.Cast`. + + E.g.:: + + from sqlalchemy import cast, Numeric + + stmt = select([ + cast(product_table.c.unit_price, Numeric(10, 4)) + ]) + + The above statement will produce SQL resembling:: + + SELECT CAST(unit_price AS NUMERIC(10, 4)) FROM product + + The :func:`.cast` function performs two distinct functions when + used. The first is that it renders the ``CAST`` expression within + the resulting SQL string. The second is that it associates the given + type (e.g. :class:`.TypeEngine` class or instance) with the column + expression on the Python side, which means the expression will take + on the expression operator behavior associated with that type, + as well as the bound-value handling and result-row-handling behavior + of the type. + + .. versionchanged:: 0.9.0 :func:`.cast` now applies the given type + to the expression such that it takes effect on the bound-value, + e.g. the Python-to-database direction, in addition to the + result handling, e.g. database-to-Python, direction. + + An alternative to :func:`.cast` is the :func:`.type_coerce` function. + This function performs the second task of associating an expression + with a specific type, but does not render the ``CAST`` expression + in SQL. + + :param expression: A SQL expression, such as a :class:`.ColumnElement` + expression or a Python string which will be coerced into a bound + literal value. + + :param type_: A :class:`.TypeEngine` class or instance indicating + the type to which the ``CAST`` should apply. + + .. seealso:: + + :func:`.type_coerce` - Python-side type coercion without emitting + CAST. + + """ + self.type = type_api.to_instance(type_) + self.clause = _literal_as_binds(expression, type_=self.type) + self.typeclause = TypeClause(self.type) + + def _copy_internals(self, clone=_clone, **kw): + self.clause = clone(self.clause, **kw) + self.typeclause = clone(self.typeclause, **kw) + + def get_children(self, **kwargs): + return self.clause, self.typeclause + + @property + def _from_objects(self): + return self.clause._from_objects + + +class TypeCoerce(ColumnElement): + """Represent a Python-side type-coercion wrapper. + + :class:`.TypeCoerce` supplies the :func:`.expression.type_coerce` + function; see that function for usage details. + + .. versionchanged:: 1.1 The :func:`.type_coerce` function now produces + a persistent :class:`.TypeCoerce` wrapper object rather than + translating the given object in place. + + .. seealso:: + + :func:`.expression.type_coerce` + + """ + + __visit_name__ = 'type_coerce' + + def __init__(self, expression, type_): + """Associate a SQL expression with a particular type, without rendering + ``CAST``. + + E.g.:: + + from sqlalchemy import type_coerce + + stmt = select([ + type_coerce(log_table.date_string, StringDateTime()) + ]) + + The above construct will produce a :class:`.TypeCoerce` object, which + renders SQL that labels the expression, but otherwise does not + modify its value on the SQL side:: + + SELECT date_string AS anon_1 FROM log + + When result rows are fetched, the ``StringDateTime`` type + will be applied to result rows on behalf of the ``date_string`` column. + The rationale for the "anon_1" label is so that the type-coerced + column remains separate in the list of result columns vs. other + type-coerced or direct values of the target column. In order to + provide a named label for the expression, use + :meth:`.ColumnElement.label`:: + + stmt = select([ + type_coerce( + log_table.date_string, StringDateTime()).label('date') + ]) + + + A type that features bound-value handling will also have that behavior + take effect when literal values or :func:`.bindparam` constructs are + passed to :func:`.type_coerce` as targets. + For example, if a type implements the + :meth:`.TypeEngine.bind_expression` + method or :meth:`.TypeEngine.bind_processor` method or equivalent, + these functions will take effect at statement compilation/execution + time when a literal value is passed, as in:: + + # bound-value handling of MyStringType will be applied to the + # literal value "some string" + stmt = select([type_coerce("some string", MyStringType)]) + + :func:`.type_coerce` is similar to the :func:`.cast` function, + except that it does not render the ``CAST`` expression in the resulting + statement. + + :param expression: A SQL expression, such as a :class:`.ColumnElement` + expression or a Python string which will be coerced into a bound + literal value. + + :param type_: A :class:`.TypeEngine` class or instance indicating + the type to which the expression is coerced. + + .. seealso:: + + :func:`.cast` + + """ + self.type = type_api.to_instance(type_) + self.clause = _literal_as_binds(expression, type_=self.type) + + def _copy_internals(self, clone=_clone, **kw): + self.clause = clone(self.clause, **kw) + self.__dict__.pop('typed_expression', None) + + def get_children(self, **kwargs): + return self.clause, + + @property + def _from_objects(self): + return self.clause._from_objects + + @util.memoized_property + def typed_expression(self): + if isinstance(self.clause, BindParameter): + bp = self.clause._clone() + bp.type = self.type + return bp + else: + return self.clause + + +class Extract(ColumnElement): + """Represent a SQL EXTRACT clause, ``extract(field FROM expr)``.""" + + __visit_name__ = 'extract' + + def __init__(self, field, expr, **kwargs): + """Return a :class:`.Extract` construct. + + This is typically available as :func:`.extract` + as well as ``func.extract`` from the + :data:`.func` namespace. + + """ + self.type = type_api.INTEGERTYPE + self.field = field + self.expr = _literal_as_binds(expr, None) + + def _copy_internals(self, clone=_clone, **kw): + self.expr = clone(self.expr, **kw) + + def get_children(self, **kwargs): + return self.expr, + + @property + def _from_objects(self): + return self.expr._from_objects + + +class _label_reference(ColumnElement): + """Wrap a column expression as it appears in a 'reference' context. + + This expression is any that includes an _order_by_label_element, + which is a Label, or a DESC / ASC construct wrapping a Label. + + The production of _label_reference() should occur when an expression + is added to this context; this includes the ORDER BY or GROUP BY of a + SELECT statement, as well as a few other places, such as the ORDER BY + within an OVER clause. + + """ + __visit_name__ = 'label_reference' + + def __init__(self, element): + self.element = element + + def _copy_internals(self, clone=_clone, **kw): + self.element = clone(self.element, **kw) + + @property + def _from_objects(self): + return () + + +class _textual_label_reference(ColumnElement): + __visit_name__ = 'textual_label_reference' + + def __init__(self, element): + self.element = element + + @util.memoized_property + def _text_clause(self): + return TextClause._create_text(self.element) + + +class UnaryExpression(ColumnElement): + """Define a 'unary' expression. + + A unary expression has a single column expression + and an operator. The operator can be placed on the left + (where it is called the 'operator') or right (where it is called the + 'modifier') of the column expression. + + :class:`.UnaryExpression` is the basis for several unary operators + including those used by :func:`.desc`, :func:`.asc`, :func:`.distinct`, + :func:`.nullsfirst` and :func:`.nullslast`. + + """ + __visit_name__ = 'unary' + + def __init__(self, element, operator=None, modifier=None, + type_=None, negate=None, wraps_column_expression=False): + self.operator = operator + self.modifier = modifier + self.element = element.self_group( + against=self.operator or self.modifier) + self.type = type_api.to_instance(type_) + self.negate = negate + self.wraps_column_expression = wraps_column_expression + + @classmethod + def _create_nullsfirst(cls, column): + """Produce the ``NULLS FIRST`` modifier for an ``ORDER BY`` expression. + + :func:`.nullsfirst` is intended to modify the expression produced + by :func:`.asc` or :func:`.desc`, and indicates how NULL values + should be handled when they are encountered during ordering:: + + + from sqlalchemy import desc, nullsfirst + + stmt = select([users_table]).\ + order_by(nullsfirst(desc(users_table.c.name))) + + The SQL expression from the above would resemble:: + + SELECT id, name FROM user ORDER BY name DESC NULLS FIRST + + Like :func:`.asc` and :func:`.desc`, :func:`.nullsfirst` is typically + invoked from the column expression itself using + :meth:`.ColumnElement.nullsfirst`, rather than as its standalone + function version, as in:: + + stmt = (select([users_table]). + order_by(users_table.c.name.desc().nullsfirst()) + ) + + .. seealso:: + + :func:`.asc` + + :func:`.desc` + + :func:`.nullslast` + + :meth:`.Select.order_by` + + """ + return UnaryExpression( + _literal_as_label_reference(column), + modifier=operators.nullsfirst_op, + wraps_column_expression=False) + + @classmethod + def _create_nullslast(cls, column): + """Produce the ``NULLS LAST`` modifier for an ``ORDER BY`` expression. + + :func:`.nullslast` is intended to modify the expression produced + by :func:`.asc` or :func:`.desc`, and indicates how NULL values + should be handled when they are encountered during ordering:: + + + from sqlalchemy import desc, nullslast + + stmt = select([users_table]).\ + order_by(nullslast(desc(users_table.c.name))) + + The SQL expression from the above would resemble:: + + SELECT id, name FROM user ORDER BY name DESC NULLS LAST + + Like :func:`.asc` and :func:`.desc`, :func:`.nullslast` is typically + invoked from the column expression itself using + :meth:`.ColumnElement.nullslast`, rather than as its standalone + function version, as in:: + + stmt = select([users_table]).\ + order_by(users_table.c.name.desc().nullslast()) + + .. seealso:: + + :func:`.asc` + + :func:`.desc` + + :func:`.nullsfirst` + + :meth:`.Select.order_by` + + """ + return UnaryExpression( + _literal_as_label_reference(column), + modifier=operators.nullslast_op, + wraps_column_expression=False) + + @classmethod + def _create_desc(cls, column): + """Produce a descending ``ORDER BY`` clause element. + + e.g.:: + + from sqlalchemy import desc + + stmt = select([users_table]).order_by(desc(users_table.c.name)) + + will produce SQL as:: + + SELECT id, name FROM user ORDER BY name DESC + + The :func:`.desc` function is a standalone version of the + :meth:`.ColumnElement.desc` method available on all SQL expressions, + e.g.:: + + + stmt = select([users_table]).order_by(users_table.c.name.desc()) + + :param column: A :class:`.ColumnElement` (e.g. scalar SQL expression) + with which to apply the :func:`.desc` operation. + + .. seealso:: + + :func:`.asc` + + :func:`.nullsfirst` + + :func:`.nullslast` + + :meth:`.Select.order_by` + + """ + return UnaryExpression( + _literal_as_label_reference(column), + modifier=operators.desc_op, + wraps_column_expression=False) + + @classmethod + def _create_asc(cls, column): + """Produce an ascending ``ORDER BY`` clause element. + + e.g.:: + + from sqlalchemy import asc + stmt = select([users_table]).order_by(asc(users_table.c.name)) + + will produce SQL as:: + + SELECT id, name FROM user ORDER BY name ASC + + The :func:`.asc` function is a standalone version of the + :meth:`.ColumnElement.asc` method available on all SQL expressions, + e.g.:: + + + stmt = select([users_table]).order_by(users_table.c.name.asc()) + + :param column: A :class:`.ColumnElement` (e.g. scalar SQL expression) + with which to apply the :func:`.asc` operation. + + .. seealso:: + + :func:`.desc` + + :func:`.nullsfirst` + + :func:`.nullslast` + + :meth:`.Select.order_by` + + """ + return UnaryExpression( + _literal_as_label_reference(column), + modifier=operators.asc_op, + wraps_column_expression=False) + + @classmethod + def _create_distinct(cls, expr): + """Produce an column-expression-level unary ``DISTINCT`` clause. + + This applies the ``DISTINCT`` keyword to an individual column + expression, and is typically contained within an aggregate function, + as in:: + + from sqlalchemy import distinct, func + stmt = select([func.count(distinct(users_table.c.name))]) + + The above would produce an expression resembling:: + + SELECT COUNT(DISTINCT name) FROM user + + The :func:`.distinct` function is also available as a column-level + method, e.g. :meth:`.ColumnElement.distinct`, as in:: + + stmt = select([func.count(users_table.c.name.distinct())]) + + The :func:`.distinct` operator is different from the + :meth:`.Select.distinct` method of :class:`.Select`, + which produces a ``SELECT`` statement + with ``DISTINCT`` applied to the result set as a whole, + e.g. a ``SELECT DISTINCT`` expression. See that method for further + information. + + .. seealso:: + + :meth:`.ColumnElement.distinct` + + :meth:`.Select.distinct` + + :data:`.func` + + """ + expr = _literal_as_binds(expr) + return UnaryExpression( + expr, operator=operators.distinct_op, + type_=expr.type, wraps_column_expression=False) + + @property + def _order_by_label_element(self): + if self.modifier in (operators.desc_op, operators.asc_op): + return self.element._order_by_label_element + else: + return None + + @property + def _from_objects(self): + return self.element._from_objects + + def _copy_internals(self, clone=_clone, **kw): + self.element = clone(self.element, **kw) + + def get_children(self, **kwargs): + return self.element, + + def compare(self, other, **kw): + """Compare this :class:`UnaryExpression` against the given + :class:`.ClauseElement`.""" + + return ( + isinstance(other, UnaryExpression) and + self.operator == other.operator and + self.modifier == other.modifier and + self.element.compare(other.element, **kw) + ) + + def _negate(self): + if self.negate is not None: + return UnaryExpression( + self.element, + operator=self.negate, + negate=self.operator, + modifier=self.modifier, + type_=self.type, + wraps_column_expression=self.wraps_column_expression) + elif self.type._type_affinity is type_api.BOOLEANTYPE._type_affinity: + return UnaryExpression( + self.self_group(against=operators.inv), + operator=operators.inv, + type_=type_api.BOOLEANTYPE, + wraps_column_expression=self.wraps_column_expression, + negate=None) + else: + return ClauseElement._negate(self) + + def self_group(self, against=None): + if self.operator and operators.is_precedent(self.operator, against): + return Grouping(self) + else: + return self + + +class CollectionAggregate(UnaryExpression): + """Forms the basis for right-hand collection operator modifiers + ANY and ALL. + + The ANY and ALL keywords are available in different ways on different + backends. On PostgreSQL, they only work for an ARRAY type. On + MySQL, they only work for subqueries. + + """ + @classmethod + def _create_any(cls, expr): + """Produce an ANY expression. + + This may apply to an array type for some dialects (e.g. postgresql), + or to a subquery for others (e.g. mysql). e.g.:: + + # postgresql '5 = ANY (somearray)' + expr = 5 == any_(mytable.c.somearray) + + # mysql '5 = ANY (SELECT value FROM table)' + expr = 5 == any_(select([table.c.value])) + + .. versionadded:: 1.1 + + .. seealso:: + + :func:`.expression.all_` + + """ + + expr = _literal_as_binds(expr) + + if expr.is_selectable and hasattr(expr, 'as_scalar'): + expr = expr.as_scalar() + expr = expr.self_group() + return CollectionAggregate( + expr, operator=operators.any_op, + type_=type_api.NULLTYPE, wraps_column_expression=False) + + @classmethod + def _create_all(cls, expr): + """Produce an ALL expression. + + This may apply to an array type for some dialects (e.g. postgresql), + or to a subquery for others (e.g. mysql). e.g.:: + + # postgresql '5 = ALL (somearray)' + expr = 5 == all_(mytable.c.somearray) + + # mysql '5 = ALL (SELECT value FROM table)' + expr = 5 == all_(select([table.c.value])) + + .. versionadded:: 1.1 + + .. seealso:: + + :func:`.expression.any_` + + """ + + expr = _literal_as_binds(expr) + if expr.is_selectable and hasattr(expr, 'as_scalar'): + expr = expr.as_scalar() + expr = expr.self_group() + return CollectionAggregate( + expr, operator=operators.all_op, + type_=type_api.NULLTYPE, wraps_column_expression=False) + + # operate and reverse_operate are hardwired to + # dispatch onto the type comparator directly, so that we can + # ensure "reversed" behavior. + def operate(self, op, *other, **kwargs): + if not operators.is_comparison(op): + raise exc.ArgumentError( + "Only comparison operators may be used with ANY/ALL") + kwargs['reverse'] = True + return self.comparator.operate(operators.mirror(op), *other, **kwargs) + + def reverse_operate(self, op, other, **kwargs): + # comparison operators should never call reverse_operate + assert not operators.is_comparison(op) + raise exc.ArgumentError( + "Only comparison operators may be used with ANY/ALL") + + +class AsBoolean(UnaryExpression): + + def __init__(self, element, operator, negate): + self.element = element + self.type = type_api.BOOLEANTYPE + self.operator = operator + self.negate = negate + self.modifier = None + self.wraps_column_expression = True + self._is_implicitly_boolean = element._is_implicitly_boolean + + def self_group(self, against=None): + return self + + def _negate(self): + # TODO: this assumes the element is the True_() or False_() + # object, but this assumption isn't enforced and + # ColumnElement._negate() can send any number of expressions here + return self.element._negate() + + +class BinaryExpression(ColumnElement): + """Represent an expression that is ``LEFT <operator> RIGHT``. + + A :class:`.BinaryExpression` is generated automatically + whenever two column expressions are used in a Python binary expression:: + + >>> from sqlalchemy.sql import column + >>> column('a') + column('b') + <sqlalchemy.sql.expression.BinaryExpression object at 0x101029dd0> + >>> print column('a') + column('b') + a + b + + """ + + __visit_name__ = 'binary' + + _is_implicitly_boolean = True + """Indicates that any database will know this is a boolean expression + even if the database does not have an explicit boolean datatype. + + """ + + def __init__(self, left, right, operator, type_=None, + negate=None, modifiers=None): + # allow compatibility with libraries that + # refer to BinaryExpression directly and pass strings + if isinstance(operator, util.string_types): + operator = operators.custom_op(operator) + self._orig = (left, right) + self.left = left.self_group(against=operator) + self.right = right.self_group(against=operator) + self.operator = operator + self.type = type_api.to_instance(type_) + self.negate = negate + self._is_implicitly_boolean = operators.is_boolean(operator) + + if modifiers is None: + self.modifiers = {} + else: + self.modifiers = modifiers + + def __bool__(self): + if self.operator in (operator.eq, operator.ne): + return self.operator(hash(self._orig[0]), hash(self._orig[1])) + else: + raise TypeError("Boolean value of this clause is not defined") + + __nonzero__ = __bool__ + + @property + def is_comparison(self): + return operators.is_comparison(self.operator) + + @property + def _from_objects(self): + return self.left._from_objects + self.right._from_objects + + def _copy_internals(self, clone=_clone, **kw): + self.left = clone(self.left, **kw) + self.right = clone(self.right, **kw) + + def get_children(self, **kwargs): + return self.left, self.right + + def compare(self, other, **kw): + """Compare this :class:`BinaryExpression` against the + given :class:`BinaryExpression`.""" + + return ( + isinstance(other, BinaryExpression) and + self.operator == other.operator and + ( + self.left.compare(other.left, **kw) and + self.right.compare(other.right, **kw) or + ( + operators.is_commutative(self.operator) and + self.left.compare(other.right, **kw) and + self.right.compare(other.left, **kw) + ) + ) + ) + + def self_group(self, against=None): + if operators.is_precedent(self.operator, against): + return Grouping(self) + else: + return self + + def _negate(self): + if self.negate is not None: + return BinaryExpression( + self.left, + self.right, + self.negate, + negate=self.operator, + type_=self.type, + modifiers=self.modifiers) + else: + return super(BinaryExpression, self)._negate() + + +class Slice(ColumnElement): + """Represent SQL for a Python array-slice object. + + This is not a specific SQL construct at this level, but + may be interpreted by specific dialects, e.g. PostgreSQL. + + """ + __visit_name__ = 'slice' + + def __init__(self, start, stop, step): + self.start = start + self.stop = stop + self.step = step + self.type = type_api.NULLTYPE + + def self_group(self, against=None): + assert against is operator.getitem + return self + + +class IndexExpression(BinaryExpression): + """Represent the class of expressions that are like an "index" operation. + """ + pass + + +class Grouping(ColumnElement): + """Represent a grouping within a column expression""" + + __visit_name__ = 'grouping' + + def __init__(self, element): + self.element = element + self.type = getattr(element, 'type', type_api.NULLTYPE) + + def self_group(self, against=None): + return self + + @util.memoized_property + def _is_implicitly_boolean(self): + return self.element._is_implicitly_boolean + + @property + def _key_label(self): + return self._label + + @property + def _label(self): + return getattr(self.element, '_label', None) or self.anon_label + + def _copy_internals(self, clone=_clone, **kw): + self.element = clone(self.element, **kw) + + def get_children(self, **kwargs): + return self.element, + + @property + def _from_objects(self): + return self.element._from_objects + + def __getattr__(self, attr): + return getattr(self.element, attr) + + def __getstate__(self): + return {'element': self.element, 'type': self.type} + + def __setstate__(self, state): + self.element = state['element'] + self.type = state['type'] + + def compare(self, other, **kw): + return isinstance(other, Grouping) and \ + self.element.compare(other.element) + + +RANGE_UNBOUNDED = util.symbol("RANGE_UNBOUNDED") +RANGE_CURRENT = util.symbol("RANGE_CURRENT") + + +class Over(ColumnElement): + """Represent an OVER clause. + + This is a special operator against a so-called + "window" function, as well as any aggregate function, + which produces results relative to the result set + itself. It's supported only by certain database + backends. + + """ + __visit_name__ = 'over' + + order_by = None + partition_by = None + + def __init__( + self, element, partition_by=None, + order_by=None, range_=None, rows=None): + """Produce an :class:`.Over` object against a function. + + Used against aggregate or so-called "window" functions, + for database backends that support window functions. + + :func:`~.expression.over` is usually called using + the :meth:`.FunctionElement.over` method, e.g.:: + + func.row_number().over(order_by=mytable.c.some_column) + + Would produce:: + + ROW_NUMBER() OVER(ORDER BY some_column) + + Ranges are also possible using the :paramref:`.expression.over.range_` + and :paramref:`.expression.over.rows` parameters. These + mutually-exclusive parameters each accept a 2-tuple, which contains + a combination of integers and None:: + + func.row_number().over(order_by=my_table.c.some_column, range_=(None, 0)) + + The above would produce:: + + ROW_NUMBER() OVER(ORDER BY some_column RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) + + A value of None indicates "unbounded", a + value of zero indicates "current row", and negative / positive + integers indicate "preceding" and "following": + + * RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING:: + + func.row_number().over(order_by='x', range_=(-5, 10)) + + * ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:: + + func.row_number().over(order_by='x', rows=(None, 0)) + + * RANGE BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING:: + + func.row_number().over(order_by='x', range_=(-2, None)) + + * RANGE BETWEEN 1 FOLLOWING AND 3 FOLLOWING:: + + func.row_number().over(order_by='x', range_=(1, 3)) + + .. versionadded:: 1.1 support for RANGE / ROWS within a window + + + :param element: a :class:`.FunctionElement`, :class:`.WithinGroup`, + or other compatible construct. + :param partition_by: a column element or string, or a list + of such, that will be used as the PARTITION BY clause + of the OVER construct. + :param order_by: a column element or string, or a list + of such, that will be used as the ORDER BY clause + of the OVER construct. + :param range_: optional range clause for the window. This is a + tuple value which can contain integer values or None, and will + render a RANGE BETWEEN PRECEDING / FOLLOWING clause + + .. versionadded:: 1.1 + + :param rows: optional rows clause for the window. This is a tuple + value which can contain integer values or None, and will render + a ROWS BETWEEN PRECEDING / FOLLOWING clause. + + .. versionadded:: 1.1 + + This function is also available from the :data:`~.expression.func` + construct itself via the :meth:`.FunctionElement.over` method. + + .. seealso:: + + :data:`.expression.func` + + :func:`.expression.within_group` + + """ + self.element = element + if order_by is not None: + self.order_by = ClauseList( + *util.to_list(order_by), + _literal_as_text=_literal_as_label_reference) + if partition_by is not None: + self.partition_by = ClauseList( + *util.to_list(partition_by), + _literal_as_text=_literal_as_label_reference) + + if range_: + self.range_ = self._interpret_range(range_) + if rows: + raise exc.ArgumentError( + "'range_' and 'rows' are mutually exclusive") + else: + self.rows = None + elif rows: + self.rows = self._interpret_range(rows) + self.range_ = None + else: + self.rows = self.range_ = None + + def _interpret_range(self, range_): + if not isinstance(range_, tuple) or len(range_) != 2: + raise exc.ArgumentError("2-tuple expected for range/rows") + + if range_[0] is None: + lower = RANGE_UNBOUNDED + else: + try: + lower = int(range_[0]) + except ValueError: + raise exc.ArgumentError( + "Integer or None expected for range value") + else: + if lower == 0: + lower = RANGE_CURRENT + + if range_[1] is None: + upper = RANGE_UNBOUNDED + else: + try: + upper = int(range_[1]) + except ValueError: + raise exc.ArgumentError( + "Integer or None expected for range value") + else: + if upper == 0: + upper = RANGE_CURRENT + + return lower, upper + + @property + def func(self): + """the element referred to by this :class:`.Over` + clause. + + .. deprecated:: 1.1 the ``func`` element has been renamed to + ``.element``. The two attributes are synonymous though + ``.func`` is read-only. + + """ + return self.element + + @util.memoized_property + def type(self): + return self.element.type + + def get_children(self, **kwargs): + return [c for c in + (self.element, self.partition_by, self.order_by) + if c is not None] + + def _copy_internals(self, clone=_clone, **kw): + self.element = clone(self.element, **kw) + if self.partition_by is not None: + self.partition_by = clone(self.partition_by, **kw) + if self.order_by is not None: + self.order_by = clone(self.order_by, **kw) + + @property + def _from_objects(self): + return list(itertools.chain( + *[c._from_objects for c in + (self.element, self.partition_by, self.order_by) + if c is not None] + )) + + +class WithinGroup(ColumnElement): + """Represent a WITHIN GROUP (ORDER BY) clause. + + This is a special operator against so-called + "ordered set aggregate" and "hypothetical + set aggregate" functions, including ``percentile_cont()``, + ``rank()``, ``dense_rank()``, etc. + + It's supported only by certain database backends, such as PostgreSQL, + Oracle and MS SQL Server. + + The :class:`.WithinGroup` construct extracts its type from the + method :meth:`.FunctionElement.within_group_type`. If this returns + ``None``, the function's ``.type`` is used. + + """ + __visit_name__ = 'withingroup' + + order_by = None + + def __init__(self, element, *order_by): + r"""Produce a :class:`.WithinGroup` object against a function. + + Used against so-called "ordered set aggregate" and "hypothetical + set aggregate" functions, including :class:`.percentile_cont`, + :class:`.rank`, :class:`.dense_rank`, etc. + + :func:`~.expression.within_group` is usually called using + the :meth:`.FunctionElement.within_group` method, e.g.:: + + from sqlalchemy import within_group + stmt = select([ + department.c.id, + func.percentile_cont(0.5).within_group( + department.c.salary.desc() + ) + ]) + + The above statement would produce SQL similar to + ``SELECT department.id, percentile_cont(0.5) + WITHIN GROUP (ORDER BY department.salary DESC)``. + + :param element: a :class:`.FunctionElement` construct, typically + generated by :data:`~.expression.func`. + :param \*order_by: one or more column elements that will be used + as the ORDER BY clause of the WITHIN GROUP construct. + + .. versionadded:: 1.1 + + .. seealso:: + + :data:`.expression.func` + + :func:`.expression.over` + + """ + self.element = element + if order_by is not None: + self.order_by = ClauseList( + *util.to_list(order_by), + _literal_as_text=_literal_as_label_reference) + + def over(self, partition_by=None, order_by=None, range_=None, rows=None): + """Produce an OVER clause against this :class:`.WithinGroup` + construct. + + This function has the same signature as that of + :meth:`.FunctionElement.over`. + + """ + return Over( + self, partition_by=partition_by, order_by=order_by, + range_=range_, rows=rows) + + @util.memoized_property + def type(self): + wgt = self.element.within_group_type(self) + if wgt is not None: + return wgt + else: + return self.element.type + + def get_children(self, **kwargs): + return [c for c in + (self.element, self.order_by) + if c is not None] + + def _copy_internals(self, clone=_clone, **kw): + self.element = clone(self.element, **kw) + if self.order_by is not None: + self.order_by = clone(self.order_by, **kw) + + @property + def _from_objects(self): + return list(itertools.chain( + *[c._from_objects for c in + (self.element, self.order_by) + if c is not None] + )) + + +class FunctionFilter(ColumnElement): + """Represent a function FILTER clause. + + This is a special operator against aggregate and window functions, + which controls which rows are passed to it. + It's supported only by certain database backends. + + Invocation of :class:`.FunctionFilter` is via + :meth:`.FunctionElement.filter`:: + + func.count(1).filter(True) + + .. versionadded:: 1.0.0 + + .. seealso:: + + :meth:`.FunctionElement.filter` + + """ + __visit_name__ = 'funcfilter' + + criterion = None + + def __init__(self, func, *criterion): + """Produce a :class:`.FunctionFilter` object against a function. + + Used against aggregate and window functions, + for database backends that support the "FILTER" clause. + + E.g.:: + + from sqlalchemy import funcfilter + funcfilter(func.count(1), MyClass.name == 'some name') + + Would produce "COUNT(1) FILTER (WHERE myclass.name = 'some name')". + + This function is also available from the :data:`~.expression.func` + construct itself via the :meth:`.FunctionElement.filter` method. + + .. versionadded:: 1.0.0 + + .. seealso:: + + :meth:`.FunctionElement.filter` + + + """ + self.func = func + self.filter(*criterion) + + def filter(self, *criterion): + """Produce an additional FILTER against the function. + + This method adds additional criteria to the initial criteria + set up by :meth:`.FunctionElement.filter`. + + Multiple criteria are joined together at SQL render time + via ``AND``. + + + """ + + for criterion in list(criterion): + criterion = _expression_literal_as_text(criterion) + + if self.criterion is not None: + self.criterion = self.criterion & criterion + else: + self.criterion = criterion + + return self + + def over(self, partition_by=None, order_by=None, range_=None, rows=None): + """Produce an OVER clause against this filtered function. + + Used against aggregate or so-called "window" functions, + for database backends that support window functions. + + The expression:: + + func.rank().filter(MyClass.y > 5).over(order_by='x') + + is shorthand for:: + + from sqlalchemy import over, funcfilter + over(funcfilter(func.rank(), MyClass.y > 5), order_by='x') + + See :func:`~.expression.over` for a full description. + + """ + return Over( + self, partition_by=partition_by, order_by=order_by, + range_=range_, rows=rows) + + @util.memoized_property + def type(self): + return self.func.type + + def get_children(self, **kwargs): + return [c for c in + (self.func, self.criterion) + if c is not None] + + def _copy_internals(self, clone=_clone, **kw): + self.func = clone(self.func, **kw) + if self.criterion is not None: + self.criterion = clone(self.criterion, **kw) + + @property + def _from_objects(self): + return list(itertools.chain( + *[c._from_objects for c in (self.func, self.criterion) + if c is not None] + )) + + +class Label(ColumnElement): + """Represents a column label (AS). + + Represent a label, as typically applied to any column-level + element using the ``AS`` sql keyword. + + """ + + __visit_name__ = 'label' + + def __init__(self, name, element, type_=None): + """Return a :class:`Label` object for the + given :class:`.ColumnElement`. + + A label changes the name of an element in the columns clause of a + ``SELECT`` statement, typically via the ``AS`` SQL keyword. + + This functionality is more conveniently available via the + :meth:`.ColumnElement.label` method on :class:`.ColumnElement`. + + :param name: label name + + :param obj: a :class:`.ColumnElement`. + + """ + + if isinstance(element, Label): + self._resolve_label = element._label + + while isinstance(element, Label): + element = element.element + + if name: + self.name = name + self._resolve_label = self.name + else: + self.name = _anonymous_label( + '%%(%d %s)s' % (id(self), getattr(element, 'name', 'anon')) + ) + + self.key = self._label = self._key_label = self.name + self._element = element + self._type = type_ + self._proxies = [element] + + def __reduce__(self): + return self.__class__, (self.name, self._element, self._type) + + @util.memoized_property + def _is_implicitly_boolean(self): + return self.element._is_implicitly_boolean + + @util.memoized_property + def _allow_label_resolve(self): + return self.element._allow_label_resolve + + @property + def _order_by_label_element(self): + return self + + @util.memoized_property + def type(self): + return type_api.to_instance( + self._type or getattr(self._element, 'type', None) + ) + + @util.memoized_property + def element(self): + return self._element.self_group(against=operators.as_) + + def self_group(self, against=None): + return self._apply_to_inner(self._element.self_group, against=against) + + def _negate(self): + return self._apply_to_inner(self._element._negate) + + def _apply_to_inner(self, fn, *arg, **kw): + sub_element = fn(*arg, **kw) + if sub_element is not self._element: + return Label(self.name, + sub_element, + type_=self._type) + else: + return self + + @property + def primary_key(self): + return self.element.primary_key + + @property + def foreign_keys(self): + return self.element.foreign_keys + + def get_children(self, **kwargs): + return self.element, + + def _copy_internals(self, clone=_clone, anonymize_labels=False, **kw): + self._element = clone(self._element, **kw) + self.__dict__.pop('element', None) + self.__dict__.pop('_allow_label_resolve', None) + if anonymize_labels: + self.name = self._resolve_label = _anonymous_label( + '%%(%d %s)s' % ( + id(self), getattr(self.element, 'name', 'anon')) + ) + self.key = self._label = self._key_label = self.name + + @property + def _from_objects(self): + return self.element._from_objects + + def _make_proxy(self, selectable, name=None, **kw): + e = self.element._make_proxy(selectable, + name=name if name else self.name) + e._proxies.append(self) + if self._type is not None: + e.type = self._type + return e + + +class ColumnClause(Immutable, ColumnElement): + """Represents a column expression from any textual string. + + The :class:`.ColumnClause`, a lightweight analogue to the + :class:`.Column` class, is typically invoked using the + :func:`.column` function, as in:: + + from sqlalchemy import column + + id, name = column("id"), column("name") + stmt = select([id, name]).select_from("user") + + The above statement would produce SQL like:: + + SELECT id, name FROM user + + :class:`.ColumnClause` is the immediate superclass of the schema-specific + :class:`.Column` object. While the :class:`.Column` class has all the + same capabilities as :class:`.ColumnClause`, the :class:`.ColumnClause` + class is usable by itself in those cases where behavioral requirements + are limited to simple SQL expression generation. The object has none of + the associations with schema-level metadata or with execution-time + behavior that :class:`.Column` does, so in that sense is a "lightweight" + version of :class:`.Column`. + + Full details on :class:`.ColumnClause` usage is at :func:`.column`. + + .. seealso:: + + :func:`.column` + + :class:`.Column` + + """ + __visit_name__ = 'column' + + onupdate = default = server_default = server_onupdate = None + + _is_multiparam_column = False + + _memoized_property = util.group_expirable_memoized_property() + + def __init__(self, text, type_=None, is_literal=False, _selectable=None): + """Produce a :class:`.ColumnClause` object. + + The :class:`.ColumnClause` is a lightweight analogue to the + :class:`.Column` class. The :func:`.column` function can + be invoked with just a name alone, as in:: + + from sqlalchemy import column + + id, name = column("id"), column("name") + stmt = select([id, name]).select_from("user") + + The above statement would produce SQL like:: + + SELECT id, name FROM user + + Once constructed, :func:`.column` may be used like any other SQL + expression element such as within :func:`.select` constructs:: + + from sqlalchemy.sql import column + + id, name = column("id"), column("name") + stmt = select([id, name]).select_from("user") + + The text handled by :func:`.column` is assumed to be handled + like the name of a database column; if the string contains mixed case, + special characters, or matches a known reserved word on the target + backend, the column expression will render using the quoting + behavior determined by the backend. To produce a textual SQL + expression that is rendered exactly without any quoting, + use :func:`.literal_column` instead, or pass ``True`` as the + value of :paramref:`.column.is_literal`. Additionally, full SQL + statements are best handled using the :func:`.text` construct. + + :func:`.column` can be used in a table-like + fashion by combining it with the :func:`.table` function + (which is the lightweight analogue to :class:`.Table`) to produce + a working table construct with minimal boilerplate:: + + from sqlalchemy import table, column, select + + user = table("user", + column("id"), + column("name"), + column("description"), + ) + + stmt = select([user.c.description]).where(user.c.name == 'wendy') + + A :func:`.column` / :func:`.table` construct like that illustrated + above can be created in an + ad-hoc fashion and is not associated with any + :class:`.schema.MetaData`, DDL, or events, unlike its + :class:`.Table` counterpart. + + .. versionchanged:: 1.0.0 :func:`.expression.column` can now + be imported from the plain ``sqlalchemy`` namespace like any + other SQL element. + + :param text: the text of the element. + + :param type: :class:`.types.TypeEngine` object which can associate + this :class:`.ColumnClause` with a type. + + :param is_literal: if True, the :class:`.ColumnClause` is assumed to + be an exact expression that will be delivered to the output with no + quoting rules applied regardless of case sensitive settings. the + :func:`.literal_column()` function essentially invokes + :func:`.column` while passing ``is_literal=True``. + + .. seealso:: + + :class:`.Column` + + :func:`.literal_column` + + :func:`.table` + + :func:`.text` + + :ref:`sqlexpression_literal_column` + + """ + + self.key = self.name = text + self.table = _selectable + self.type = type_api.to_instance(type_) + self.is_literal = is_literal + + def _compare_name_for_result(self, other): + if self.is_literal or \ + self.table is None or self.table._textual or \ + not hasattr(other, 'proxy_set') or ( + isinstance(other, ColumnClause) and + (other.is_literal or + other.table is None or + other.table._textual) + ): + return (hasattr(other, 'name') and self.name == other.name) or \ + (hasattr(other, '_label') and self._label == other._label) + else: + return other.proxy_set.intersection(self.proxy_set) + + def _get_table(self): + return self.__dict__['table'] + + def _set_table(self, table): + self._memoized_property.expire_instance(self) + self.__dict__['table'] = table + table = property(_get_table, _set_table) + + @_memoized_property + def _from_objects(self): + t = self.table + if t is not None: + return [t] + else: + return [] + + @util.memoized_property + def description(self): + if util.py3k: + return self.name + else: + return self.name.encode('ascii', 'backslashreplace') + + @_memoized_property + def _key_label(self): + if self.key != self.name: + return self._gen_label(self.key) + else: + return self._label + + @_memoized_property + def _label(self): + return self._gen_label(self.name) + + @_memoized_property + def _render_label_in_columns_clause(self): + return self.table is not None + + def _gen_label(self, name): + t = self.table + + if self.is_literal: + return None + + elif t is not None and t.named_with_column: + if getattr(t, 'schema', None): + label = t.schema.replace('.', '_') + "_" + \ + t.name + "_" + name + else: + label = t.name + "_" + name + + # propagate name quoting rules for labels. + if getattr(name, "quote", None) is not None: + if isinstance(label, quoted_name): + label.quote = name.quote + else: + label = quoted_name(label, name.quote) + elif getattr(t.name, "quote", None) is not None: + # can't get this situation to occur, so let's + # assert false on it for now + assert not isinstance(label, quoted_name) + label = quoted_name(label, t.name.quote) + + # ensure the label name doesn't conflict with that + # of an existing column + if label in t.c: + _label = label + counter = 1 + while _label in t.c: + _label = label + "_" + str(counter) + counter += 1 + label = _label + + return _as_truncated(label) + + else: + return name + + def _bind_param(self, operator, obj, type_=None): + return BindParameter(self.key, obj, + _compared_to_operator=operator, + _compared_to_type=self.type, + type_=type_, + unique=True) + + def _make_proxy(self, selectable, name=None, attach=True, + name_is_truncatable=False, **kw): + # propagate the "is_literal" flag only if we are keeping our name, + # otherwise its considered to be a label + is_literal = self.is_literal and (name is None or name == self.name) + c = self._constructor( + _as_truncated(name or self.name) if + name_is_truncatable else + (name or self.name), + type_=self.type, + _selectable=selectable, + is_literal=is_literal + ) + if name is None: + c.key = self.key + c._proxies = [self] + if selectable._is_clone_of is not None: + c._is_clone_of = \ + selectable._is_clone_of.columns.get(c.key) + + if attach: + selectable._columns[c.key] = c + return c + + +class CollationClause(ColumnElement): + __visit_name__ = "collation" + + def __init__(self, collation): + self.collation = collation + + +class _IdentifiedClause(Executable, ClauseElement): + + __visit_name__ = 'identified' + _execution_options = \ + Executable._execution_options.union({'autocommit': False}) + + def __init__(self, ident): + self.ident = ident + + +class SavepointClause(_IdentifiedClause): + __visit_name__ = 'savepoint' + + +class RollbackToSavepointClause(_IdentifiedClause): + __visit_name__ = 'rollback_to_savepoint' + + +class ReleaseSavepointClause(_IdentifiedClause): + __visit_name__ = 'release_savepoint' + + +class quoted_name(util.MemoizedSlots, util.text_type): + """Represent a SQL identifier combined with quoting preferences. + + :class:`.quoted_name` is a Python unicode/str subclass which + represents a particular identifier name along with a + ``quote`` flag. This ``quote`` flag, when set to + ``True`` or ``False``, overrides automatic quoting behavior + for this identifier in order to either unconditionally quote + or to not quote the name. If left at its default of ``None``, + quoting behavior is applied to the identifier on a per-backend basis + based on an examination of the token itself. + + A :class:`.quoted_name` object with ``quote=True`` is also + prevented from being modified in the case of a so-called + "name normalize" option. Certain database backends, such as + Oracle, Firebird, and DB2 "normalize" case-insensitive names + as uppercase. The SQLAlchemy dialects for these backends + convert from SQLAlchemy's lower-case-means-insensitive convention + to the upper-case-means-insensitive conventions of those backends. + The ``quote=True`` flag here will prevent this conversion from occurring + to support an identifier that's quoted as all lower case against + such a backend. + + The :class:`.quoted_name` object is normally created automatically + when specifying the name for key schema constructs such as + :class:`.Table`, :class:`.Column`, and others. The class can also be + passed explicitly as the name to any function that receives a name which + can be quoted. Such as to use the :meth:`.Engine.has_table` method with + an unconditionally quoted name:: + + from sqlalchemy import create_engine + from sqlalchemy.sql import quoted_name + + engine = create_engine("oracle+cx_oracle://some_dsn") + engine.has_table(quoted_name("some_table", True)) + + The above logic will run the "has table" logic against the Oracle backend, + passing the name exactly as ``"some_table"`` without converting to + upper case. + + .. versionadded:: 0.9.0 + + .. versionchanged:: 1.2 The :class:`.quoted_name` construct is now + importable from ``sqlalchemy.sql``, in addition to the previous + location of ``sqlalchemy.sql.elements``. + + """ + + __slots__ = 'quote', 'lower', 'upper' + + def __new__(cls, value, quote): + if value is None: + return None + # experimental - don't bother with quoted_name + # if quote flag is None. doesn't seem to make any dent + # in performance however + # elif not sprcls and quote is None: + # return value + elif isinstance(value, cls) and ( + quote is None or value.quote == quote + ): + return value + self = super(quoted_name, cls).__new__(cls, value) + self.quote = quote + return self + + def __reduce__(self): + return quoted_name, (util.text_type(self), self.quote) + + def _memoized_method_lower(self): + if self.quote: + return self + else: + return util.text_type(self).lower() + + def _memoized_method_upper(self): + if self.quote: + return self + else: + return util.text_type(self).upper() + + def __repr__(self): + backslashed = self.encode('ascii', 'backslashreplace') + if not util.py2k: + backslashed = backslashed.decode('ascii') + return "'%s'" % backslashed + + +class _truncated_label(quoted_name): + """A unicode subclass used to identify symbolic " + "names that may require truncation.""" + + __slots__ = () + + def __new__(cls, value, quote=None): + quote = getattr(value, "quote", quote) + # return super(_truncated_label, cls).__new__(cls, value, quote, True) + return super(_truncated_label, cls).__new__(cls, value, quote) + + def __reduce__(self): + return self.__class__, (util.text_type(self), self.quote) + + def apply_map(self, map_): + return self + + +class conv(_truncated_label): + """Mark a string indicating that a name has already been converted + by a naming convention. + + This is a string subclass that indicates a name that should not be + subject to any further naming conventions. + + E.g. when we create a :class:`.Constraint` using a naming convention + as follows:: + + m = MetaData(naming_convention={ + "ck": "ck_%(table_name)s_%(constraint_name)s" + }) + t = Table('t', m, Column('x', Integer), + CheckConstraint('x > 5', name='x5')) + + The name of the above constraint will be rendered as ``"ck_t_x5"``. + That is, the existing name ``x5`` is used in the naming convention as the + ``constraint_name`` token. + + In some situations, such as in migration scripts, we may be rendering + the above :class:`.CheckConstraint` with a name that's already been + converted. In order to make sure the name isn't double-modified, the + new name is applied using the :func:`.schema.conv` marker. We can + use this explicitly as follows:: + + + m = MetaData(naming_convention={ + "ck": "ck_%(table_name)s_%(constraint_name)s" + }) + t = Table('t', m, Column('x', Integer), + CheckConstraint('x > 5', name=conv('ck_t_x5'))) + + Where above, the :func:`.schema.conv` marker indicates that the constraint + name here is final, and the name will render as ``"ck_t_x5"`` and not + ``"ck_t_ck_t_x5"`` + + .. versionadded:: 0.9.4 + + .. seealso:: + + :ref:`constraint_naming_conventions` + + """ + __slots__ = () + + +class _defer_name(_truncated_label): + """mark a name as 'deferred' for the purposes of automated name + generation. + + """ + __slots__ = () + + def __new__(cls, value): + if value is None: + return _NONE_NAME + elif isinstance(value, conv): + return value + else: + return super(_defer_name, cls).__new__(cls, value) + + def __reduce__(self): + return self.__class__, (util.text_type(self), ) + + +class _defer_none_name(_defer_name): + """indicate a 'deferred' name that was ultimately the value None.""" + __slots__ = () + +_NONE_NAME = _defer_none_name("_unnamed_") + +# for backwards compatibility in case +# someone is re-implementing the +# _truncated_identifier() sequence in a custom +# compiler +_generated_label = _truncated_label + + +class _anonymous_label(_truncated_label): + """A unicode subclass used to identify anonymously + generated names.""" + + __slots__ = () + + def __add__(self, other): + return _anonymous_label( + quoted_name( + util.text_type.__add__(self, util.text_type(other)), + self.quote) + ) + + def __radd__(self, other): + return _anonymous_label( + quoted_name( + util.text_type.__add__(util.text_type(other), self), + self.quote) + ) + + def apply_map(self, map_): + if self.quote is not None: + # preserve quoting only if necessary + return quoted_name(self % map_, self.quote) + else: + # else skip the constructor call + return self % map_ + + +def _as_truncated(value): + """coerce the given value to :class:`._truncated_label`. + + Existing :class:`._truncated_label` and + :class:`._anonymous_label` objects are passed + unchanged. + """ + + if isinstance(value, _truncated_label): + return value + else: + return _truncated_label(value) + + +def _string_or_unprintable(element): + if isinstance(element, util.string_types): + return element + else: + try: + return str(element) + except Exception: + return "unprintable element %r" % element + + +def _expand_cloned(elements): + """expand the given set of ClauseElements to be the set of all 'cloned' + predecessors. + + """ + return itertools.chain(*[x._cloned_set for x in elements]) + + +def _select_iterables(elements): + """expand tables into individual columns in the + given list of column expressions. + + """ + return itertools.chain(*[c._select_iterable for c in elements]) + + +def _cloned_intersection(a, b): + """return the intersection of sets a and b, counting + any overlap between 'cloned' predecessors. + + The returned set is in terms of the entities present within 'a'. + + """ + all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b)) + return set(elem for elem in a + if all_overlap.intersection(elem._cloned_set)) + + +def _cloned_difference(a, b): + all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b)) + return set(elem for elem in a + if not all_overlap.intersection(elem._cloned_set)) + + +@util.dependencies("sqlalchemy.sql.functions") +def _labeled(functions, element): + if not hasattr(element, 'name') or \ + isinstance(element, functions.FunctionElement): + return element.label(None) + else: + return element + + +def _is_column(col): + """True if ``col`` is an instance of :class:`.ColumnElement`.""" + + return isinstance(col, ColumnElement) + + +def _find_columns(clause): + """locate Column objects within the given expression.""" + + cols = util.column_set() + traverse(clause, {}, {'column': cols.add}) + return cols + + +# there is some inconsistency here between the usage of +# inspect() vs. checking for Visitable and __clause_element__. +# Ideally all functions here would derive from inspect(), +# however the inspect() versions add significant callcount +# overhead for critical functions like _interpret_as_column_or_from(). +# Generally, the column-based functions are more performance critical +# and are fine just checking for __clause_element__(). It is only +# _interpret_as_from() where we'd like to be able to receive ORM entities +# that have no defined namespace, hence inspect() is needed there. + + +def _column_as_key(element): + if isinstance(element, util.string_types): + return element + if hasattr(element, '__clause_element__'): + element = element.__clause_element__() + try: + return element.key + except AttributeError: + return None + + +def _clause_element_as_expr(element): + if hasattr(element, '__clause_element__'): + return element.__clause_element__() + else: + return element + + +def _literal_as_label_reference(element): + if isinstance(element, util.string_types): + return _textual_label_reference(element) + + elif hasattr(element, '__clause_element__'): + element = element.__clause_element__() + + return _literal_as_text(element) + + +def _literal_and_labels_as_label_reference(element): + if isinstance(element, util.string_types): + return _textual_label_reference(element) + + elif hasattr(element, '__clause_element__'): + element = element.__clause_element__() + + if isinstance(element, ColumnElement) and \ + element._order_by_label_element is not None: + return _label_reference(element) + else: + return _literal_as_text(element) + + +def _expression_literal_as_text(element): + return _literal_as_text(element, warn=True) + + +def _literal_as_text(element, warn=False): + if isinstance(element, Visitable): + return element + elif hasattr(element, '__clause_element__'): + return element.__clause_element__() + elif isinstance(element, util.string_types): + if warn: + util.warn_limited( + "Textual SQL expression %(expr)r should be " + "explicitly declared as text(%(expr)r)", + {"expr": util.ellipses_string(element)}) + + return TextClause(util.text_type(element)) + elif isinstance(element, (util.NoneType, bool)): + return _const_expr(element) + else: + raise exc.ArgumentError( + "SQL expression object or string expected, got object of type %r " + "instead" % type(element) + ) + + +def _no_literals(element): + if hasattr(element, '__clause_element__'): + return element.__clause_element__() + elif not isinstance(element, Visitable): + raise exc.ArgumentError("Ambiguous literal: %r. Use the 'text()' " + "function to indicate a SQL expression " + "literal, or 'literal()' to indicate a " + "bound value." % (element, )) + else: + return element + + +def _is_literal(element): + return not isinstance(element, Visitable) and \ + not hasattr(element, '__clause_element__') + + +def _only_column_elements_or_none(element, name): + if element is None: + return None + else: + return _only_column_elements(element, name) + + +def _only_column_elements(element, name): + if hasattr(element, '__clause_element__'): + element = element.__clause_element__() + if not isinstance(element, ColumnElement): + raise exc.ArgumentError( + "Column-based expression object expected for argument " + "'%s'; got: '%s', type %s" % (name, element, type(element))) + return element + + +def _literal_as_binds(element, name=None, type_=None): + if hasattr(element, '__clause_element__'): + return element.__clause_element__() + elif not isinstance(element, Visitable): + if element is None: + return Null() + else: + return BindParameter(name, element, type_=type_, unique=True) + else: + return element + +_guess_straight_column = re.compile(r'^\w\S*$', re.I) + + +def _interpret_as_column_or_from(element): + if isinstance(element, Visitable): + return element + elif hasattr(element, '__clause_element__'): + return element.__clause_element__() + + insp = inspection.inspect(element, raiseerr=False) + if insp is None: + if isinstance(element, (util.NoneType, bool)): + return _const_expr(element) + elif hasattr(insp, "selectable"): + return insp.selectable + + # be forgiving as this is an extremely common + # and known expression + if element == "*": + guess_is_literal = True + elif isinstance(element, (numbers.Number)): + return ColumnClause(str(element), is_literal=True) + else: + element = str(element) + # give into temptation, as this fact we are guessing about + # is not one we've previously ever needed our users tell us; + # but let them know we are not happy about it + guess_is_literal = not _guess_straight_column.match(element) + util.warn_limited( + "Textual column expression %(column)r should be " + "explicitly declared with text(%(column)r), " + "or use %(literal_column)s(%(column)r) " + "for more specificity", + { + "column": util.ellipses_string(element), + "literal_column": "literal_column" + if guess_is_literal else "column" + }) + return ColumnClause( + element, + is_literal=guess_is_literal) + + +def _const_expr(element): + if isinstance(element, (Null, False_, True_)): + return element + elif element is None: + return Null() + elif element is False: + return False_() + elif element is True: + return True_() + else: + raise exc.ArgumentError( + "Expected None, False, or True" + ) + + +def _type_from_args(args): + for a in args: + if not a.type._isnull: + return a.type + else: + return type_api.NULLTYPE + + +def _corresponding_column_or_error(fromclause, column, + require_embedded=False): + c = fromclause.corresponding_column(column, + require_embedded=require_embedded) + if c is None: + raise exc.InvalidRequestError( + "Given column '%s', attached to table '%s', " + "failed to locate a corresponding column from table '%s'" + % + (column, + getattr(column, 'table', None), + fromclause.description) + ) + return c + + +class AnnotatedColumnElement(Annotated): + def __init__(self, element, values): + Annotated.__init__(self, element, values) + ColumnElement.comparator._reset(self) + for attr in ('name', 'key', 'table'): + if self.__dict__.get(attr, False) is None: + self.__dict__.pop(attr) + + def _with_annotations(self, values): + clone = super(AnnotatedColumnElement, self)._with_annotations(values) + ColumnElement.comparator._reset(clone) + return clone + + @util.memoized_property + def name(self): + """pull 'name' from parent, if not present""" + return self._Annotated__element.name + + @util.memoized_property + def table(self): + """pull 'table' from parent, if not present""" + return self._Annotated__element.table + + @util.memoized_property + def key(self): + """pull 'key' from parent, if not present""" + return self._Annotated__element.key + + @util.memoized_property + def info(self): + return self._Annotated__element.info + + @util.memoized_property + def anon_label(self): + return self._Annotated__element.anon_label diff --git a/venv/Lib/site-packages/sqlalchemy/sql/expression.py b/venv/Lib/site-packages/sqlalchemy/sql/expression.py new file mode 100644 index 0000000..b69b6ee --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/expression.py @@ -0,0 +1,145 @@ +# sql/expression.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Defines the public namespace for SQL expression constructs. + +Prior to version 0.9, this module contained all of "elements", "dml", +"default_comparator" and "selectable". The module was broken up +and most "factory" functions were moved to be grouped with their associated +class. + +""" + +__all__ = [ + 'Alias', 'any_', 'all_', 'ClauseElement', 'ColumnCollection', 'ColumnElement', + 'CompoundSelect', 'Delete', 'FromClause', 'Insert', 'Join', 'Lateral', + 'Select', + 'Selectable', 'TableClause', 'Update', 'alias', 'and_', 'asc', 'between', + 'bindparam', 'case', 'cast', 'column', 'delete', 'desc', 'distinct', + 'except_', 'except_all', 'exists', 'extract', 'func', 'modifier', + 'collate', 'insert', 'intersect', 'intersect_all', 'join', 'label', + 'lateral', 'literal', 'literal_column', 'not_', 'null', 'nullsfirst', + 'nullslast', + 'or_', 'outparam', 'outerjoin', 'over', 'select', 'subquery', + 'table', 'text', + 'tuple_', 'type_coerce', 'quoted_name', 'union', 'union_all', 'update', + 'within_group', + 'TableSample', 'tablesample'] + + +from .visitors import Visitable +from .functions import func, modifier, FunctionElement, Function +from ..util.langhelpers import public_factory +from .elements import ClauseElement, ColumnElement,\ + BindParameter, CollectionAggregate, UnaryExpression, BooleanClauseList, \ + Label, Cast, Case, ColumnClause, TextClause, Over, Null, \ + True_, False_, BinaryExpression, Tuple, TypeClause, Extract, \ + Grouping, WithinGroup, not_, quoted_name, \ + collate, literal_column, between,\ + literal, outparam, TypeCoerce, ClauseList, FunctionFilter + +from .elements import SavepointClause, RollbackToSavepointClause, \ + ReleaseSavepointClause + +from .base import ColumnCollection, Generative, Executable, \ + PARSE_AUTOCOMMIT + +from .selectable import Alias, Join, Select, Selectable, TableClause, \ + CompoundSelect, CTE, FromClause, FromGrouping, Lateral, SelectBase, \ + alias, GenerativeSelect, subquery, HasCTE, HasPrefixes, HasSuffixes, \ + lateral, Exists, ScalarSelect, TextAsFrom, TableSample, tablesample + + +from .dml import Insert, Update, Delete, UpdateBase, ValuesBase + +# factory functions - these pull class-bound constructors and classmethods +# from SQL elements and selectables into public functions. This allows +# the functions to be available in the sqlalchemy.sql.* namespace and +# to be auto-cross-documenting from the function to the class itself. + +all_ = public_factory(CollectionAggregate._create_all, ".expression.all_") +any_ = public_factory(CollectionAggregate._create_any, ".expression.any_") +and_ = public_factory(BooleanClauseList.and_, ".expression.and_") +or_ = public_factory(BooleanClauseList.or_, ".expression.or_") +bindparam = public_factory(BindParameter, ".expression.bindparam") +select = public_factory(Select, ".expression.select") +text = public_factory(TextClause._create_text, ".expression.text") +table = public_factory(TableClause, ".expression.table") +column = public_factory(ColumnClause, ".expression.column") +over = public_factory(Over, ".expression.over") +within_group = public_factory(WithinGroup, ".expression.within_group") +label = public_factory(Label, ".expression.label") +case = public_factory(Case, ".expression.case") +cast = public_factory(Cast, ".expression.cast") +extract = public_factory(Extract, ".expression.extract") +tuple_ = public_factory(Tuple, ".expression.tuple_") +except_ = public_factory(CompoundSelect._create_except, ".expression.except_") +except_all = public_factory( + CompoundSelect._create_except_all, ".expression.except_all") +intersect = public_factory( + CompoundSelect._create_intersect, ".expression.intersect") +intersect_all = public_factory( + CompoundSelect._create_intersect_all, ".expression.intersect_all") +union = public_factory(CompoundSelect._create_union, ".expression.union") +union_all = public_factory( + CompoundSelect._create_union_all, ".expression.union_all") +exists = public_factory(Exists, ".expression.exists") +nullsfirst = public_factory( + UnaryExpression._create_nullsfirst, ".expression.nullsfirst") +nullslast = public_factory( + UnaryExpression._create_nullslast, ".expression.nullslast") +asc = public_factory(UnaryExpression._create_asc, ".expression.asc") +desc = public_factory(UnaryExpression._create_desc, ".expression.desc") +distinct = public_factory( + UnaryExpression._create_distinct, ".expression.distinct") +type_coerce = public_factory(TypeCoerce, ".expression.type_coerce") +true = public_factory(True_._instance, ".expression.true") +false = public_factory(False_._instance, ".expression.false") +null = public_factory(Null._instance, ".expression.null") +join = public_factory(Join._create_join, ".expression.join") +outerjoin = public_factory(Join._create_outerjoin, ".expression.outerjoin") +insert = public_factory(Insert, ".expression.insert") +update = public_factory(Update, ".expression.update") +delete = public_factory(Delete, ".expression.delete") +funcfilter = public_factory( + FunctionFilter, ".expression.funcfilter") + + +# internal functions still being called from tests and the ORM, +# these might be better off in some other namespace +from .base import _from_objects +from .elements import _literal_as_text, _clause_element_as_expr,\ + _is_column, _labeled, _only_column_elements, _string_or_unprintable, \ + _truncated_label, _clone, _cloned_difference, _cloned_intersection,\ + _column_as_key, _literal_as_binds, _select_iterables, \ + _corresponding_column_or_error, _literal_as_label_reference, \ + _expression_literal_as_text +from .selectable import _interpret_as_from + + +# old names for compatibility +_Executable = Executable +_BindParamClause = BindParameter +_Label = Label +_SelectBase = SelectBase +_BinaryExpression = BinaryExpression +_Cast = Cast +_Null = Null +_False = False_ +_True = True_ +_TextClause = TextClause +_UnaryExpression = UnaryExpression +_Case = Case +_Tuple = Tuple +_Over = Over +_Generative = Generative +_TypeClause = TypeClause +_Extract = Extract +_Exists = Exists +_Grouping = Grouping +_FromGrouping = FromGrouping +_ScalarSelect = ScalarSelect diff --git a/venv/Lib/site-packages/sqlalchemy/sql/functions.py b/venv/Lib/site-packages/sqlalchemy/sql/functions.py new file mode 100644 index 0000000..5e2e3ec --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/functions.py @@ -0,0 +1,883 @@ +# sql/functions.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""SQL function API, factories, and built-in functions. + +""" +from . import sqltypes, schema +from .base import Executable, ColumnCollection +from .elements import ClauseList, Cast, Extract, _literal_as_binds, \ + literal_column, _type_from_args, ColumnElement, _clone,\ + Over, BindParameter, FunctionFilter, Grouping, WithinGroup +from .selectable import FromClause, Select, Alias +from . import util as sqlutil +from . import operators +from .visitors import VisitableType +from .. import util +from . import annotation + +_registry = util.defaultdict(dict) + + +def register_function(identifier, fn, package="_default"): + """Associate a callable with a particular func. name. + + This is normally called by _GenericMeta, but is also + available by itself so that a non-Function construct + can be associated with the :data:`.func` accessor (i.e. + CAST, EXTRACT). + + """ + reg = _registry[package] + reg[identifier] = fn + + +class FunctionElement(Executable, ColumnElement, FromClause): + """Base for SQL function-oriented constructs. + + .. seealso:: + + :class:`.Function` - named SQL function. + + :data:`.func` - namespace which produces registered or ad-hoc + :class:`.Function` instances. + + :class:`.GenericFunction` - allows creation of registered function + types. + + """ + + packagenames = () + + def __init__(self, *clauses, **kwargs): + """Construct a :class:`.FunctionElement`. + """ + args = [_literal_as_binds(c, self.name) for c in clauses] + self.clause_expr = ClauseList( + operator=operators.comma_op, + group_contents=True, *args).\ + self_group() + + def _execute_on_connection(self, connection, multiparams, params): + return connection._execute_function(self, multiparams, params) + + @property + def columns(self): + """The set of columns exported by this :class:`.FunctionElement`. + + Function objects currently have no result column names built in; + this method returns a single-element column collection with + an anonymously named column. + + An interim approach to providing named columns for a function + as a FROM clause is to build a :func:`.select` with the + desired columns:: + + from sqlalchemy.sql import column + + stmt = select([column('x'), column('y')]).\ + select_from(func.myfunction()) + + + """ + return ColumnCollection(self.label(None)) + + @util.memoized_property + def clauses(self): + """Return the underlying :class:`.ClauseList` which contains + the arguments for this :class:`.FunctionElement`. + + """ + return self.clause_expr.element + + def over(self, partition_by=None, order_by=None, rows=None, range_=None): + """Produce an OVER clause against this function. + + Used against aggregate or so-called "window" functions, + for database backends that support window functions. + + The expression:: + + func.row_number().over(order_by='x') + + is shorthand for:: + + from sqlalchemy import over + over(func.row_number(), order_by='x') + + See :func:`~.expression.over` for a full description. + + .. versionadded:: 0.7 + + """ + return Over( + self, + partition_by=partition_by, + order_by=order_by, + rows=rows, + range_=range_ + ) + + def within_group(self, *order_by): + """Produce a WITHIN GROUP (ORDER BY expr) clause against this function. + + Used against so-called "ordered set aggregate" and "hypothetical + set aggregate" functions, including :class:`.percentile_cont`, + :class:`.rank`, :class:`.dense_rank`, etc. + + See :func:`~.expression.within_group` for a full description. + + .. versionadded:: 1.1 + + + """ + return WithinGroup(self, *order_by) + + def filter(self, *criterion): + """Produce a FILTER clause against this function. + + Used against aggregate and window functions, + for database backends that support the "FILTER" clause. + + The expression:: + + func.count(1).filter(True) + + is shorthand for:: + + from sqlalchemy import funcfilter + funcfilter(func.count(1), True) + + .. versionadded:: 1.0.0 + + .. seealso:: + + :class:`.FunctionFilter` + + :func:`.funcfilter` + + + """ + if not criterion: + return self + return FunctionFilter(self, *criterion) + + @property + def _from_objects(self): + return self.clauses._from_objects + + def get_children(self, **kwargs): + return self.clause_expr, + + def _copy_internals(self, clone=_clone, **kw): + self.clause_expr = clone(self.clause_expr, **kw) + self._reset_exported() + FunctionElement.clauses._reset(self) + + def within_group_type(self, within_group): + """For types that define their return type as based on the criteria + within a WITHIN GROUP (ORDER BY) expression, called by the + :class:`.WithinGroup` construct. + + Returns None by default, in which case the function's normal ``.type`` + is used. + + """ + + return None + + def alias(self, name=None, flat=False): + r"""Produce a :class:`.Alias` construct against this + :class:`.FunctionElement`. + + This construct wraps the function in a named alias which + is suitable for the FROM clause, in the style accepted for example + by PostgreSQL. + + e.g.:: + + from sqlalchemy.sql import column + + stmt = select([column('data_view')]).\ + select_from(SomeTable).\ + select_from(func.unnest(SomeTable.data).alias('data_view') + ) + + Would produce: + + .. sourcecode:: sql + + SELECT data_view + FROM sometable, unnest(sometable.data) AS data_view + + .. versionadded:: 0.9.8 The :meth:`.FunctionElement.alias` method + is now supported. Previously, this method's behavior was + undefined and did not behave consistently across versions. + + """ + + return Alias(self, name) + + def select(self): + """Produce a :func:`~.expression.select` construct + against this :class:`.FunctionElement`. + + This is shorthand for:: + + s = select([function_element]) + + """ + s = Select([self]) + if self._execution_options: + s = s.execution_options(**self._execution_options) + return s + + def scalar(self): + """Execute this :class:`.FunctionElement` against an embedded + 'bind' and return a scalar value. + + This first calls :meth:`~.FunctionElement.select` to + produce a SELECT construct. + + Note that :class:`.FunctionElement` can be passed to + the :meth:`.Connectable.scalar` method of :class:`.Connection` + or :class:`.Engine`. + + """ + return self.select().execute().scalar() + + def execute(self): + """Execute this :class:`.FunctionElement` against an embedded + 'bind'. + + This first calls :meth:`~.FunctionElement.select` to + produce a SELECT construct. + + Note that :class:`.FunctionElement` can be passed to + the :meth:`.Connectable.execute` method of :class:`.Connection` + or :class:`.Engine`. + + """ + return self.select().execute() + + def _bind_param(self, operator, obj, type_=None): + return BindParameter(None, obj, _compared_to_operator=operator, + _compared_to_type=self.type, unique=True, + type_=type_) + + def self_group(self, against=None): + # for the moment, we are parenthesizing all array-returning + # expressions against getitem. This may need to be made + # more portable if in the future we support other DBs + # besides postgresql. + if against is operators.getitem and \ + isinstance(self.type, sqltypes.ARRAY): + return Grouping(self) + else: + return super(FunctionElement, self).self_group(against=against) + + +class _FunctionGenerator(object): + """Generate :class:`.Function` objects based on getattr calls.""" + + def __init__(self, **opts): + self.__names = [] + self.opts = opts + + def __getattr__(self, name): + # passthru __ attributes; fixes pydoc + if name.startswith('__'): + try: + return self.__dict__[name] + except KeyError: + raise AttributeError(name) + + elif name.endswith('_'): + name = name[0:-1] + f = _FunctionGenerator(**self.opts) + f.__names = list(self.__names) + [name] + return f + + def __call__(self, *c, **kwargs): + o = self.opts.copy() + o.update(kwargs) + + tokens = len(self.__names) + + if tokens == 2: + package, fname = self.__names + elif tokens == 1: + package, fname = "_default", self.__names[0] + else: + package = None + + if package is not None: + func = _registry[package].get(fname) + if func is not None: + return func(*c, **o) + + return Function(self.__names[-1], + packagenames=self.__names[0:-1], *c, **o) + + +func = _FunctionGenerator() +"""Generate SQL function expressions. + + :data:`.func` is a special object instance which generates SQL + functions based on name-based attributes, e.g.:: + + >>> print(func.count(1)) + count(:param_1) + + The element is a column-oriented SQL element like any other, and is + used in that way:: + + >>> print(select([func.count(table.c.id)])) + SELECT count(sometable.id) FROM sometable + + Any name can be given to :data:`.func`. If the function name is unknown to + SQLAlchemy, it will be rendered exactly as is. For common SQL functions + which SQLAlchemy is aware of, the name may be interpreted as a *generic + function* which will be compiled appropriately to the target database:: + + >>> print(func.current_timestamp()) + CURRENT_TIMESTAMP + + To call functions which are present in dot-separated packages, + specify them in the same manner:: + + >>> print(func.stats.yield_curve(5, 10)) + stats.yield_curve(:yield_curve_1, :yield_curve_2) + + SQLAlchemy can be made aware of the return type of functions to enable + type-specific lexical and result-based behavior. For example, to ensure + that a string-based function returns a Unicode value and is similarly + treated as a string in expressions, specify + :class:`~sqlalchemy.types.Unicode` as the type: + + >>> print(func.my_string(u'hi', type_=Unicode) + ' ' + + ... func.my_string(u'there', type_=Unicode)) + my_string(:my_string_1) || :my_string_2 || my_string(:my_string_3) + + The object returned by a :data:`.func` call is usually an instance of + :class:`.Function`. + This object meets the "column" interface, including comparison and labeling + functions. The object can also be passed the :meth:`~.Connectable.execute` + method of a :class:`.Connection` or :class:`.Engine`, where it will be + wrapped inside of a SELECT statement first:: + + print(connection.execute(func.current_timestamp()).scalar()) + + In a few exception cases, the :data:`.func` accessor + will redirect a name to a built-in expression such as :func:`.cast` + or :func:`.extract`, as these names have well-known meaning + but are not exactly the same as "functions" from a SQLAlchemy + perspective. + + .. versionadded:: 0.8 :data:`.func` can return non-function expression + constructs for common quasi-functional names like :func:`.cast` + and :func:`.extract`. + + Functions which are interpreted as "generic" functions know how to + calculate their return type automatically. For a listing of known generic + functions, see :ref:`generic_functions`. + + .. note:: + + The :data:`.func` construct has only limited support for calling + standalone "stored procedures", especially those with special + parameterization concerns. + + See the section :ref:`stored_procedures` for details on how to use + the DBAPI-level ``callproc()`` method for fully traditional stored + procedures. + +""" + +modifier = _FunctionGenerator(group=False) + + +class Function(FunctionElement): + """Describe a named SQL function. + + See the superclass :class:`.FunctionElement` for a description + of public methods. + + .. seealso:: + + :data:`.func` - namespace which produces registered or ad-hoc + :class:`.Function` instances. + + :class:`.GenericFunction` - allows creation of registered function + types. + + """ + + __visit_name__ = 'function' + + def __init__(self, name, *clauses, **kw): + """Construct a :class:`.Function`. + + The :data:`.func` construct is normally used to construct + new :class:`.Function` instances. + + """ + self.packagenames = kw.pop('packagenames', None) or [] + self.name = name + self._bind = kw.get('bind', None) + self.type = sqltypes.to_instance(kw.get('type_', None)) + + FunctionElement.__init__(self, *clauses, **kw) + + def _bind_param(self, operator, obj, type_=None): + return BindParameter(self.name, obj, + _compared_to_operator=operator, + _compared_to_type=self.type, + type_=type_, + unique=True) + + +class _GenericMeta(VisitableType): + def __init__(cls, clsname, bases, clsdict): + if annotation.Annotated not in cls.__mro__: + cls.name = name = clsdict.get('name', clsname) + cls.identifier = identifier = clsdict.get('identifier', name) + package = clsdict.pop('package', '_default') + # legacy + if '__return_type__' in clsdict: + cls.type = clsdict['__return_type__'] + register_function(identifier, cls, package) + super(_GenericMeta, cls).__init__(clsname, bases, clsdict) + + +class GenericFunction(util.with_metaclass(_GenericMeta, Function)): + """Define a 'generic' function. + + A generic function is a pre-established :class:`.Function` + class that is instantiated automatically when called + by name from the :data:`.func` attribute. Note that + calling any name from :data:`.func` has the effect that + a new :class:`.Function` instance is created automatically, + given that name. The primary use case for defining + a :class:`.GenericFunction` class is so that a function + of a particular name may be given a fixed return type. + It can also include custom argument parsing schemes as well + as additional methods. + + Subclasses of :class:`.GenericFunction` are automatically + registered under the name of the class. For + example, a user-defined function ``as_utc()`` would + be available immediately:: + + from sqlalchemy.sql.functions import GenericFunction + from sqlalchemy.types import DateTime + + class as_utc(GenericFunction): + type = DateTime + + print select([func.as_utc()]) + + User-defined generic functions can be organized into + packages by specifying the "package" attribute when defining + :class:`.GenericFunction`. Third party libraries + containing many functions may want to use this in order + to avoid name conflicts with other systems. For example, + if our ``as_utc()`` function were part of a package + "time":: + + class as_utc(GenericFunction): + type = DateTime + package = "time" + + The above function would be available from :data:`.func` + using the package name ``time``:: + + print select([func.time.as_utc()]) + + A final option is to allow the function to be accessed + from one name in :data:`.func` but to render as a different name. + The ``identifier`` attribute will override the name used to + access the function as loaded from :data:`.func`, but will retain + the usage of ``name`` as the rendered name:: + + class GeoBuffer(GenericFunction): + type = Geometry + package = "geo" + name = "ST_Buffer" + identifier = "buffer" + + The above function will render as follows:: + + >>> print func.geo.buffer() + ST_Buffer() + + .. versionadded:: 0.8 :class:`.GenericFunction` now supports + automatic registration of new functions as well as package + and custom naming support. + + .. versionchanged:: 0.8 The attribute name ``type`` is used + to specify the function's return type at the class level. + Previously, the name ``__return_type__`` was used. This + name is still recognized for backwards-compatibility. + + """ + + coerce_arguments = True + + def __init__(self, *args, **kwargs): + parsed_args = kwargs.pop('_parsed_args', None) + if parsed_args is None: + parsed_args = [_literal_as_binds(c, self.name) for c in args] + self.packagenames = [] + self._bind = kwargs.get('bind', None) + self.clause_expr = ClauseList( + operator=operators.comma_op, + group_contents=True, *parsed_args).self_group() + self.type = sqltypes.to_instance( + kwargs.pop("type_", None) or getattr(self, 'type', None)) + +register_function("cast", Cast) +register_function("extract", Extract) + + +class next_value(GenericFunction): + """Represent the 'next value', given a :class:`.Sequence` + as its single argument. + + Compiles into the appropriate function on each backend, + or will raise NotImplementedError if used on a backend + that does not provide support for sequences. + + """ + type = sqltypes.Integer() + name = "next_value" + + def __init__(self, seq, **kw): + assert isinstance(seq, schema.Sequence), \ + "next_value() accepts a Sequence object as input." + self._bind = kw.get('bind', None) + self.sequence = seq + + @property + def _from_objects(self): + return [] + + +class AnsiFunction(GenericFunction): + def __init__(self, **kwargs): + GenericFunction.__init__(self, **kwargs) + + +class ReturnTypeFromArgs(GenericFunction): + """Define a function whose return type is the same as its arguments.""" + + def __init__(self, *args, **kwargs): + args = [_literal_as_binds(c, self.name) for c in args] + kwargs.setdefault('type_', _type_from_args(args)) + kwargs['_parsed_args'] = args + super(ReturnTypeFromArgs, self).__init__(*args, **kwargs) + + +class coalesce(ReturnTypeFromArgs): + pass + + +class max(ReturnTypeFromArgs): + pass + + +class min(ReturnTypeFromArgs): + pass + + +class sum(ReturnTypeFromArgs): + pass + + +class now(GenericFunction): + type = sqltypes.DateTime + + +class concat(GenericFunction): + type = sqltypes.String + + +class char_length(GenericFunction): + type = sqltypes.Integer + + def __init__(self, arg, **kwargs): + GenericFunction.__init__(self, arg, **kwargs) + + +class random(GenericFunction): + pass + + +class count(GenericFunction): + r"""The ANSI COUNT aggregate function. With no arguments, + emits COUNT \*. + + """ + type = sqltypes.Integer + + def __init__(self, expression=None, **kwargs): + if expression is None: + expression = literal_column('*') + super(count, self).__init__(expression, **kwargs) + + +class current_date(AnsiFunction): + type = sqltypes.Date + + +class current_time(AnsiFunction): + type = sqltypes.Time + + +class current_timestamp(AnsiFunction): + type = sqltypes.DateTime + + +class current_user(AnsiFunction): + type = sqltypes.String + + +class localtime(AnsiFunction): + type = sqltypes.DateTime + + +class localtimestamp(AnsiFunction): + type = sqltypes.DateTime + + +class session_user(AnsiFunction): + type = sqltypes.String + + +class sysdate(AnsiFunction): + type = sqltypes.DateTime + + +class user(AnsiFunction): + type = sqltypes.String + + +class array_agg(GenericFunction): + """support for the ARRAY_AGG function. + + The ``func.array_agg(expr)`` construct returns an expression of + type :class:`.types.ARRAY`. + + e.g.:: + + stmt = select([func.array_agg(table.c.values)[2:5]]) + + .. versionadded:: 1.1 + + .. seealso:: + + :func:`.postgresql.array_agg` - PostgreSQL-specific version that + returns :class:`.postgresql.ARRAY`, which has PG-specific operators added. + + """ + + type = sqltypes.ARRAY + + def __init__(self, *args, **kwargs): + args = [_literal_as_binds(c) for c in args] + + default_array_type = kwargs.pop('_default_array_type', sqltypes.ARRAY) + if 'type_' not in kwargs: + + type_from_args = _type_from_args(args) + if isinstance(type_from_args, sqltypes.ARRAY): + kwargs['type_'] = type_from_args + else: + kwargs['type_'] = default_array_type(type_from_args) + kwargs['_parsed_args'] = args + super(array_agg, self).__init__(*args, **kwargs) + + +class OrderedSetAgg(GenericFunction): + """Define a function where the return type is based on the sort + expression type as defined by the expression passed to the + :meth:`.FunctionElement.within_group` method.""" + + array_for_multi_clause = False + + def within_group_type(self, within_group): + func_clauses = self.clause_expr.element + order_by = sqlutil.unwrap_order_by(within_group.order_by) + if self.array_for_multi_clause and len(func_clauses.clauses) > 1: + return sqltypes.ARRAY(order_by[0].type) + else: + return order_by[0].type + + +class mode(OrderedSetAgg): + """implement the ``mode`` ordered-set aggregate function. + + This function must be used with the :meth:`.FunctionElement.within_group` + modifier to supply a sort expression to operate upon. + + The return type of this function is the same as the sort expression. + + .. versionadded:: 1.1 + + """ + + +class percentile_cont(OrderedSetAgg): + """implement the ``percentile_cont`` ordered-set aggregate function. + + This function must be used with the :meth:`.FunctionElement.within_group` + modifier to supply a sort expression to operate upon. + + The return type of this function is the same as the sort expression, + or if the arguments are an array, an :class:`.types.ARRAY` of the sort + expression's type. + + .. versionadded:: 1.1 + + """ + + array_for_multi_clause = True + + +class percentile_disc(OrderedSetAgg): + """implement the ``percentile_disc`` ordered-set aggregate function. + + This function must be used with the :meth:`.FunctionElement.within_group` + modifier to supply a sort expression to operate upon. + + The return type of this function is the same as the sort expression, + or if the arguments are an array, an :class:`.types.ARRAY` of the sort + expression's type. + + .. versionadded:: 1.1 + + """ + + array_for_multi_clause = True + + +class rank(GenericFunction): + """Implement the ``rank`` hypothetical-set aggregate function. + + This function must be used with the :meth:`.FunctionElement.within_group` + modifier to supply a sort expression to operate upon. + + The return type of this function is :class:`.Integer`. + + .. versionadded:: 1.1 + + """ + type = sqltypes.Integer() + + +class dense_rank(GenericFunction): + """Implement the ``dense_rank`` hypothetical-set aggregate function. + + This function must be used with the :meth:`.FunctionElement.within_group` + modifier to supply a sort expression to operate upon. + + The return type of this function is :class:`.Integer`. + + .. versionadded:: 1.1 + + """ + type = sqltypes.Integer() + + +class percent_rank(GenericFunction): + """Implement the ``percent_rank`` hypothetical-set aggregate function. + + This function must be used with the :meth:`.FunctionElement.within_group` + modifier to supply a sort expression to operate upon. + + The return type of this function is :class:`.Numeric`. + + .. versionadded:: 1.1 + + """ + type = sqltypes.Numeric() + + +class cume_dist(GenericFunction): + """Implement the ``cume_dist`` hypothetical-set aggregate function. + + This function must be used with the :meth:`.FunctionElement.within_group` + modifier to supply a sort expression to operate upon. + + The return type of this function is :class:`.Numeric`. + + .. versionadded:: 1.1 + + """ + type = sqltypes.Numeric() + + +class cube(GenericFunction): + r"""Implement the ``CUBE`` grouping operation. + + This function is used as part of the GROUP BY of a statement, + e.g. :meth:`.Select.group_by`:: + + stmt = select( + [func.sum(table.c.value), table.c.col_1, table.c.col_2] + ).group_by(func.cube(table.c.col_1, table.c.col_2)) + + .. versionadded:: 1.2 + + """ + + +class rollup(GenericFunction): + r"""Implement the ``ROLLUP`` grouping operation. + + This function is used as part of the GROUP BY of a statement, + e.g. :meth:`.Select.group_by`:: + + stmt = select( + [func.sum(table.c.value), table.c.col_1, table.c.col_2] + ).group_by(func.rollup(table.c.col_1, table.c.col_2)) + + .. versionadded:: 1.2 + + """ + + +class grouping_sets(GenericFunction): + r"""Implement the ``GROUPING SETS`` grouping operation. + + This function is used as part of the GROUP BY of a statement, + e.g. :meth:`.Select.group_by`:: + + stmt = select( + [func.sum(table.c.value), table.c.col_1, table.c.col_2] + ).group_by(func.grouping_sets(table.c.col_1, table.c.col_2)) + + In order to group by multiple sets, use the :func:`.tuple_` construct:: + + from sqlalchemy import tuple_ + + stmt = select( + [ + func.sum(table.c.value), + table.c.col_1, table.c.col_2, + table.c.col_3] + ).group_by( + func.grouping_sets( + tuple_(table.c.col_1, table.c.col_2), + tuple_(table.c.value, table.c.col_3), + ) + ) + + + .. versionadded:: 1.2 + + """ diff --git a/venv/Lib/site-packages/sqlalchemy/sql/naming.py b/venv/Lib/site-packages/sqlalchemy/sql/naming.py new file mode 100644 index 0000000..5334293 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/naming.py @@ -0,0 +1,146 @@ +# sqlalchemy/naming.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Establish constraint and index naming conventions. + + +""" + +from .schema import Constraint, ForeignKeyConstraint, PrimaryKeyConstraint, \ + UniqueConstraint, CheckConstraint, Index, Table, Column +from .. import event, events +from .. import exc +from .elements import _truncated_label, _defer_name, _defer_none_name, conv +import re + + +class ConventionDict(object): + + def __init__(self, const, table, convention): + self.const = const + self._is_fk = isinstance(const, ForeignKeyConstraint) + self.table = table + self.convention = convention + self._const_name = const.name + + def _key_table_name(self): + return self.table.name + + def _column_X(self, idx): + if self._is_fk: + fk = self.const.elements[idx] + return fk.parent + else: + return list(self.const.columns)[idx] + + def _key_constraint_name(self): + if isinstance(self._const_name, (type(None), _defer_none_name)): + raise exc.InvalidRequestError( + "Naming convention including " + "%(constraint_name)s token requires that " + "constraint is explicitly named." + ) + if not isinstance(self._const_name, conv): + self.const.name = None + return self._const_name + + def _key_column_X_name(self, idx): + return self._column_X(idx).name + + def _key_column_X_label(self, idx): + return self._column_X(idx)._label + + def _key_referred_table_name(self): + fk = self.const.elements[0] + refs = fk.target_fullname.split(".") + if len(refs) == 3: + refschema, reftable, refcol = refs + else: + reftable, refcol = refs + return reftable + + def _key_referred_column_X_name(self, idx): + fk = self.const.elements[idx] + refs = fk.target_fullname.split(".") + if len(refs) == 3: + refschema, reftable, refcol = refs + else: + reftable, refcol = refs + return refcol + + def __getitem__(self, key): + if key in self.convention: + return self.convention[key](self.const, self.table) + elif hasattr(self, '_key_%s' % key): + return getattr(self, '_key_%s' % key)() + else: + col_template = re.match(r".*_?column_(\d+)_.+", key) + if col_template: + idx = col_template.group(1) + attr = "_key_" + key.replace(idx, "X") + idx = int(idx) + if hasattr(self, attr): + return getattr(self, attr)(idx) + raise KeyError(key) + +_prefix_dict = { + Index: "ix", + PrimaryKeyConstraint: "pk", + CheckConstraint: "ck", + UniqueConstraint: "uq", + ForeignKeyConstraint: "fk" +} + + +def _get_convention(dict_, key): + + for super_ in key.__mro__: + if super_ in _prefix_dict and _prefix_dict[super_] in dict_: + return dict_[_prefix_dict[super_]] + elif super_ in dict_: + return dict_[super_] + else: + return None + + +def _constraint_name_for_table(const, table): + metadata = table.metadata + convention = _get_convention(metadata.naming_convention, type(const)) + + if isinstance(const.name, conv): + return const.name + elif convention is not None and \ + not isinstance(const.name, conv) and \ + ( + const.name is None or + "constraint_name" in convention or + isinstance(const.name, _defer_name)): + return conv( + convention % ConventionDict(const, table, + metadata.naming_convention) + ) + elif isinstance(convention, _defer_none_name): + return None + + +@event.listens_for(Constraint, "after_parent_attach") +@event.listens_for(Index, "after_parent_attach") +def _constraint_name(const, table): + if isinstance(table, Column): + # for column-attached constraint, set another event + # to link the column attached to the table as this constraint + # associated with the table. + event.listen(table, "after_parent_attach", + lambda col, table: _constraint_name(const, table) + ) + elif isinstance(table, Table): + if isinstance(const.name, (conv, _defer_name)): + return + + newname = _constraint_name_for_table(const, table) + if newname is not None: + const.name = newname diff --git a/venv/Lib/site-packages/sqlalchemy/sql/operators.py b/venv/Lib/site-packages/sqlalchemy/sql/operators.py new file mode 100644 index 0000000..8f6a879 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/operators.py @@ -0,0 +1,1452 @@ +# sql/operators.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Defines operators used in SQL expressions.""" + +from .. import util + +from operator import ( + and_, or_, inv, add, mul, sub, mod, truediv, lt, le, ne, gt, ge, eq, neg, + getitem, lshift, rshift, contains +) + +if util.py2k: + from operator import div +else: + div = truediv + + +class Operators(object): + """Base of comparison and logical operators. + + Implements base methods + :meth:`~sqlalchemy.sql.operators.Operators.operate` and + :meth:`~sqlalchemy.sql.operators.Operators.reverse_operate`, as well as + :meth:`~sqlalchemy.sql.operators.Operators.__and__`, + :meth:`~sqlalchemy.sql.operators.Operators.__or__`, + :meth:`~sqlalchemy.sql.operators.Operators.__invert__`. + + Usually is used via its most common subclass + :class:`.ColumnOperators`. + + """ + __slots__ = () + + def __and__(self, other): + """Implement the ``&`` operator. + + When used with SQL expressions, results in an + AND operation, equivalent to + :func:`~.expression.and_`, that is:: + + a & b + + is equivalent to:: + + from sqlalchemy import and_ + and_(a, b) + + Care should be taken when using ``&`` regarding + operator precedence; the ``&`` operator has the highest precedence. + The operands should be enclosed in parenthesis if they contain + further sub expressions:: + + (a == 2) & (b == 4) + + """ + return self.operate(and_, other) + + def __or__(self, other): + """Implement the ``|`` operator. + + When used with SQL expressions, results in an + OR operation, equivalent to + :func:`~.expression.or_`, that is:: + + a | b + + is equivalent to:: + + from sqlalchemy import or_ + or_(a, b) + + Care should be taken when using ``|`` regarding + operator precedence; the ``|`` operator has the highest precedence. + The operands should be enclosed in parenthesis if they contain + further sub expressions:: + + (a == 2) | (b == 4) + + """ + return self.operate(or_, other) + + def __invert__(self): + """Implement the ``~`` operator. + + When used with SQL expressions, results in a + NOT operation, equivalent to + :func:`~.expression.not_`, that is:: + + ~a + + is equivalent to:: + + from sqlalchemy import not_ + not_(a) + + """ + return self.operate(inv) + + def op( + self, opstring, precedence=0, is_comparison=False, + return_type=None): + """produce a generic operator function. + + e.g.:: + + somecolumn.op("*")(5) + + produces:: + + somecolumn * 5 + + This function can also be used to make bitwise operators explicit. For + example:: + + somecolumn.op('&')(0xff) + + is a bitwise AND of the value in ``somecolumn``. + + :param operator: a string which will be output as the infix operator + between this element and the expression passed to the + generated function. + + :param precedence: precedence to apply to the operator, when + parenthesizing expressions. A lower number will cause the expression + to be parenthesized when applied against another operator with + higher precedence. The default value of ``0`` is lower than all + operators except for the comma (``,``) and ``AS`` operators. + A value of 100 will be higher or equal to all operators, and -100 + will be lower than or equal to all operators. + + .. versionadded:: 0.8 - added the 'precedence' argument. + + :param is_comparison: if True, the operator will be considered as a + "comparison" operator, that is which evaluates to a boolean + true/false value, like ``==``, ``>``, etc. This flag should be set + so that ORM relationships can establish that the operator is a + comparison operator when used in a custom join condition. + + .. versionadded:: 0.9.2 - added the + :paramref:`.Operators.op.is_comparison` flag. + + :param return_type: a :class:`.TypeEngine` class or object that will + force the return type of an expression produced by this operator + to be of that type. By default, operators that specify + :paramref:`.Operators.op.is_comparison` will resolve to + :class:`.Boolean`, and those that do not will be of the same + type as the left-hand operand. + + .. versionadded:: 1.2.0b3 - added the + :paramref:`.Operators.op.return_type` argument. + + .. seealso:: + + :ref:`types_operators` + + :ref:`relationship_custom_operator` + + """ + operator = custom_op(opstring, precedence, is_comparison, return_type) + + def against(other): + return operator(self, other) + return against + + def bool_op(self, opstring, precedence=0): + """Return a custom boolean operator. + + This method is shorthand for calling + :meth:`.Operators.op` and passing the + :paramref:`.Operators.op.is_comparison` + flag with True. + + .. versionadded:: 1.2.0b3 + + .. seealso:: + + :meth:`.Operators.op` + + """ + return self.op(opstring, precedence=precedence, is_comparison=True) + + def operate(self, op, *other, **kwargs): + r"""Operate on an argument. + + This is the lowest level of operation, raises + :class:`NotImplementedError` by default. + + Overriding this on a subclass can allow common + behavior to be applied to all operations. + For example, overriding :class:`.ColumnOperators` + to apply ``func.lower()`` to the left and right + side:: + + class MyComparator(ColumnOperators): + def operate(self, op, other): + return op(func.lower(self), func.lower(other)) + + :param op: Operator callable. + :param \*other: the 'other' side of the operation. Will + be a single scalar for most operations. + :param \**kwargs: modifiers. These may be passed by special + operators such as :meth:`ColumnOperators.contains`. + + + """ + raise NotImplementedError(str(op)) + + def reverse_operate(self, op, other, **kwargs): + """Reverse operate on an argument. + + Usage is the same as :meth:`operate`. + + """ + raise NotImplementedError(str(op)) + + +class custom_op(object): + """Represent a 'custom' operator. + + :class:`.custom_op` is normally instantiated when the + :meth:`.Operators.op` or :meth:`.Operators.bool_op` methods + are used to create a custom operator callable. The class can also be + used directly when programmatically constructing expressions. E.g. + to represent the "factorial" operation:: + + from sqlalchemy.sql import UnaryExpression + from sqlalchemy.sql import operators + from sqlalchemy import Numeric + + unary = UnaryExpression(table.c.somecolumn, + modifier=operators.custom_op("!"), + type_=Numeric) + + + .. seealso:: + + :meth:`.Operators.op` + + :meth:`.Operators.bool_op` + + """ + __name__ = 'custom_op' + + def __init__( + self, opstring, precedence=0, is_comparison=False, + return_type=None, natural_self_precedent=False, + eager_grouping=False): + self.opstring = opstring + self.precedence = precedence + self.is_comparison = is_comparison + self.natural_self_precedent = natural_self_precedent + self.eager_grouping = eager_grouping + self.return_type = ( + return_type._to_instance(return_type) if return_type else None + ) + + def __eq__(self, other): + return isinstance(other, custom_op) and \ + other.opstring == self.opstring + + def __hash__(self): + return id(self) + + def __call__(self, left, right, **kw): + return left.operate(self, right, **kw) + + +class ColumnOperators(Operators): + """Defines boolean, comparison, and other operators for + :class:`.ColumnElement` expressions. + + By default, all methods call down to + :meth:`.operate` or :meth:`.reverse_operate`, + passing in the appropriate operator function from the + Python builtin ``operator`` module or + a SQLAlchemy-specific operator function from + :mod:`sqlalchemy.expression.operators`. For example + the ``__eq__`` function:: + + def __eq__(self, other): + return self.operate(operators.eq, other) + + Where ``operators.eq`` is essentially:: + + def eq(a, b): + return a == b + + The core column expression unit :class:`.ColumnElement` + overrides :meth:`.Operators.operate` and others + to return further :class:`.ColumnElement` constructs, + so that the ``==`` operation above is replaced by a clause + construct. + + See also: + + :ref:`types_operators` + + :attr:`.TypeEngine.comparator_factory` + + :class:`.ColumnOperators` + + :class:`.PropComparator` + + """ + + __slots__ = () + + timetuple = None + """Hack, allows datetime objects to be compared on the LHS.""" + + def __lt__(self, other): + """Implement the ``<`` operator. + + In a column context, produces the clause ``a < b``. + + """ + return self.operate(lt, other) + + def __le__(self, other): + """Implement the ``<=`` operator. + + In a column context, produces the clause ``a <= b``. + + """ + return self.operate(le, other) + + __hash__ = Operators.__hash__ + + def __eq__(self, other): + """Implement the ``==`` operator. + + In a column context, produces the clause ``a = b``. + If the target is ``None``, produces ``a IS NULL``. + + """ + return self.operate(eq, other) + + def __ne__(self, other): + """Implement the ``!=`` operator. + + In a column context, produces the clause ``a != b``. + If the target is ``None``, produces ``a IS NOT NULL``. + + """ + return self.operate(ne, other) + + def is_distinct_from(self, other): + """Implement the ``IS DISTINCT FROM`` operator. + + Renders "a IS DISTINCT FROM b" on most platforms; + on some such as SQLite may render "a IS NOT b". + + .. versionadded:: 1.1 + + """ + return self.operate(is_distinct_from, other) + + def isnot_distinct_from(self, other): + """Implement the ``IS NOT DISTINCT FROM`` operator. + + Renders "a IS NOT DISTINCT FROM b" on most platforms; + on some such as SQLite may render "a IS b". + + .. versionadded:: 1.1 + + """ + return self.operate(isnot_distinct_from, other) + + def __gt__(self, other): + """Implement the ``>`` operator. + + In a column context, produces the clause ``a > b``. + + """ + return self.operate(gt, other) + + def __ge__(self, other): + """Implement the ``>=`` operator. + + In a column context, produces the clause ``a >= b``. + + """ + return self.operate(ge, other) + + def __neg__(self): + """Implement the ``-`` operator. + + In a column context, produces the clause ``-a``. + + """ + return self.operate(neg) + + def __contains__(self, other): + return self.operate(contains, other) + + def __getitem__(self, index): + """Implement the [] operator. + + This can be used by some database-specific types + such as PostgreSQL ARRAY and HSTORE. + + """ + return self.operate(getitem, index) + + def __lshift__(self, other): + """implement the << operator. + + Not used by SQLAlchemy core, this is provided + for custom operator systems which want to use + << as an extension point. + """ + return self.operate(lshift, other) + + def __rshift__(self, other): + """implement the >> operator. + + Not used by SQLAlchemy core, this is provided + for custom operator systems which want to use + >> as an extension point. + """ + return self.operate(rshift, other) + + def concat(self, other): + """Implement the 'concat' operator. + + In a column context, produces the clause ``a || b``, + or uses the ``concat()`` operator on MySQL. + + """ + return self.operate(concat_op, other) + + def like(self, other, escape=None): + r"""Implement the ``like`` operator. + + In a column context, produces the expression:: + + a LIKE other + + E.g.:: + + stmt = select([sometable]).\ + where(sometable.c.column.like("%foobar%")) + + :param other: expression to be compared + :param escape: optional escape character, renders the ``ESCAPE`` + keyword, e.g.:: + + somecolumn.like("foo/%bar", escape="/") + + .. seealso:: + + :meth:`.ColumnOperators.ilike` + + """ + return self.operate(like_op, other, escape=escape) + + def ilike(self, other, escape=None): + r"""Implement the ``ilike`` operator, e.g. case insensitive LIKE. + + In a column context, produces an expression either of the form:: + + lower(a) LIKE lower(other) + + Or on backends that support the ILIKE operator:: + + a ILIKE other + + E.g.:: + + stmt = select([sometable]).\ + where(sometable.c.column.ilike("%foobar%")) + + :param other: expression to be compared + :param escape: optional escape character, renders the ``ESCAPE`` + keyword, e.g.:: + + somecolumn.ilike("foo/%bar", escape="/") + + .. seealso:: + + :meth:`.ColumnOperators.like` + + """ + return self.operate(ilike_op, other, escape=escape) + + def in_(self, other): + """Implement the ``in`` operator. + + In a column context, produces the clause ``column IN <other>``. + + The given parameter ``other`` may be: + + * A list of literal values, e.g.:: + + stmt.where(column.in_([1, 2, 3])) + + In this calling form, the list of items is converted to a set of + bound parameters the same length as the list given:: + + WHERE COL IN (?, ?, ?) + + * An empty list, e.g.:: + + stmt.where(column.in_([])) + + In this calling form, the expression renders a "false" expression, + e.g.:: + + WHERE 1 != 1 + + This "false" expression has historically had different behaviors + in older SQLAlchemy versions, see + :paramref:`.create_engine.empty_in_strategy` for behavioral options. + + .. versionchanged:: 1.2 simplified the behavior of "empty in" + expressions + + * A bound parameter, e.g. :func:`.bindparam`, may be used if it + includes the :paramref:`.bindparam.expanding` flag:: + + stmt.where(column.in_(bindparam('value', expanding=True))) + + In this calling form, the expression renders a special non-SQL + placeholder expression that looks like:: + + WHERE COL IN ([EXPANDING_value]) + + This placeholder expression is intercepted at statement execution + time to be converted into the variable number of bound parameter + form illustrated earlier. If the statement were executed as:: + + connection.execute(stmt, {"value": [1, 2, 3]}) + + The database would be passed a bound parameter for each value:: + + WHERE COL IN (?, ?, ?) + + .. versionadded:: 1.2 added "expanding" bound parameters + + The "expanding" feature in version 1.2 of SQLAlchemy does not + support passing an empty list as a parameter value; however, + version 1.3 does support this. + + * a :func:`.select` construct, which is usually a correlated + scalar select:: + + stmt.where( + column.in_( + select([othertable.c.y]). + where(table.c.x == othertable.c.x) + ) + ) + + In this calling form, :meth:`.ColumnOperators.in_` renders as given:: + + WHERE COL IN (SELECT othertable.y + FROM othertable WHERE othertable.x = table.x) + + :param other: a list of literals, a :func:`.select` construct, + or a :func:`.bindparam` construct that includes the + :paramref:`.bindparam.expanding` flag set to True. + + """ + return self.operate(in_op, other) + + def notin_(self, other): + """implement the ``NOT IN`` operator. + + This is equivalent to using negation with + :meth:`.ColumnOperators.in_`, i.e. ``~x.in_(y)``. + + In the case that ``other`` is an empty sequence, the compiler + produces an "empty not in" expression. This defaults to the + expression "1 = 1" to produce true in all cases. The + :paramref:`.create_engine.empty_in_strategy` may be used to + alter this behavior. + + .. versionchanged:: 1.2 The :meth:`.ColumnOperators.in_` and + :meth:`.ColumnOperators.notin_` operators + now produce a "static" expression for an empty IN sequence + by default. + + .. seealso:: + + :meth:`.ColumnOperators.in_` + + """ + return self.operate(notin_op, other) + + def notlike(self, other, escape=None): + """implement the ``NOT LIKE`` operator. + + This is equivalent to using negation with + :meth:`.ColumnOperators.like`, i.e. ``~x.like(y)``. + + .. versionadded:: 0.8 + + .. seealso:: + + :meth:`.ColumnOperators.like` + + """ + return self.operate(notlike_op, other, escape=escape) + + def notilike(self, other, escape=None): + """implement the ``NOT ILIKE`` operator. + + This is equivalent to using negation with + :meth:`.ColumnOperators.ilike`, i.e. ``~x.ilike(y)``. + + .. versionadded:: 0.8 + + .. seealso:: + + :meth:`.ColumnOperators.ilike` + + """ + return self.operate(notilike_op, other, escape=escape) + + def is_(self, other): + """Implement the ``IS`` operator. + + Normally, ``IS`` is generated automatically when comparing to a + value of ``None``, which resolves to ``NULL``. However, explicit + usage of ``IS`` may be desirable if comparing to boolean values + on certain platforms. + + .. versionadded:: 0.7.9 + + .. seealso:: :meth:`.ColumnOperators.isnot` + + """ + return self.operate(is_, other) + + def isnot(self, other): + """Implement the ``IS NOT`` operator. + + Normally, ``IS NOT`` is generated automatically when comparing to a + value of ``None``, which resolves to ``NULL``. However, explicit + usage of ``IS NOT`` may be desirable if comparing to boolean values + on certain platforms. + + .. versionadded:: 0.7.9 + + .. seealso:: :meth:`.ColumnOperators.is_` + + """ + return self.operate(isnot, other) + + def startswith(self, other, **kwargs): + r"""Implement the ``startswith`` operator. + + Produces a LIKE expression that tests against a match for the start + of a string value:: + + column LIKE <other> || '%' + + E.g.:: + + stmt = select([sometable]).\ + where(sometable.c.column.startswith("foobar")) + + Since the operator uses ``LIKE``, wildcard characters + ``"%"`` and ``"_"`` that are present inside the <other> expression + will behave like wildcards as well. For literal string + values, the :paramref:`.ColumnOperators.startswith.autoescape` flag + may be set to ``True`` to apply escaping to occurences of these + characters within the string value so that they match as themselves + and not as wildcard characters. Alternatively, the + :paramref:`.ColumnOperators.startswith.escape` parameter will establish + a given character as an escape character which can be of use when + the target expression is not a literal string. + + :param other: expression to be compared. This is usually a plain + string value, but can also be an arbitrary SQL expression. LIKE + wildcard characters ``%`` and ``_`` are not escaped by default unless + the :paramref:`.ColumnOperators.startswith.autoescape` flag is + set to True. + + :param autoescape: boolean; when True, establishes an escape character + within the LIKE expression, then applies it to all occurrences of + ``"%"``, ``"_"`` and the escape character itself within the + comparison value, which is assumed to be a literal string and not a + SQL expression. + + An expression such as:: + + somecolumn.startswith("foo%bar", autoescape=True) + + Will render as:: + + somecolumn LIKE :param || '%' ESCAPE '/' + + With the value of :param as ``"foo/%bar"``. + + .. versionadded:: 1.2 + + .. versionchanged:: 1.2.0 The + :paramref:`.ColumnOperators.startswith.autoescape` parameter is + now a simple boolean rather than a character; the escape + character itself is also escaped, and defaults to a forwards + slash, which itself can be customized using the + :paramref:`.ColumnOperators.startswith.escape` parameter. + + :param escape: a character which when given will render with the + ``ESCAPE`` keyword to establish that character as the escape + character. This character can then be placed preceding occurrences + of ``%`` and ``_`` to allow them to act as themselves and not + wildcard characters. + + An expression such as:: + + somecolumn.startswith("foo/%bar", escape="^") + + Will render as:: + + somecolumn LIKE :param || '%' ESCAPE '^' + + The parameter may also be combined with + :paramref:`.ColumnOperators.startswith.autoescape`:: + + somecolumn.startswith("foo%bar^bat", escape="^", autoescape=True) + + Where above, the given literal parameter will be converted to + ``"foo^%bar^^bat"`` before being passed to the database. + + .. seealso:: + + :meth:`.ColumnOperators.endswith` + + :meth:`.ColumnOperators.contains` + + :meth:`.ColumnOperators.like` + + """ + return self.operate(startswith_op, other, **kwargs) + + def endswith(self, other, **kwargs): + r"""Implement the 'endswith' operator. + + Produces a LIKE expression that tests against a match for the end + of a string value:: + + column LIKE '%' || <other> + + E.g.:: + + stmt = select([sometable]).\ + where(sometable.c.column.endswith("foobar")) + + Since the operator uses ``LIKE``, wildcard characters + ``"%"`` and ``"_"`` that are present inside the <other> expression + will behave like wildcards as well. For literal string + values, the :paramref:`.ColumnOperators.endswith.autoescape` flag + may be set to ``True`` to apply escaping to occurences of these + characters within the string value so that they match as themselves + and not as wildcard characters. Alternatively, the + :paramref:`.ColumnOperators.endswith.escape` parameter will establish + a given character as an escape character which can be of use when + the target expression is not a literal string. + + :param other: expression to be compared. This is usually a plain + string value, but can also be an arbitrary SQL expression. LIKE + wildcard characters ``%`` and ``_`` are not escaped by default unless + the :paramref:`.ColumnOperators.endswith.autoescape` flag is + set to True. + + :param autoescape: boolean; when True, establishes an escape character + within the LIKE expression, then applies it to all occurrences of + ``"%"``, ``"_"`` and the escape character itself within the + comparison value, which is assumed to be a literal string and not a + SQL expression. + + An expression such as:: + + somecolumn.endswith("foo%bar", autoescape=True) + + Will render as:: + + somecolumn LIKE '%' || :param ESCAPE '/' + + With the value of :param as ``"foo/%bar"``. + + .. versionadded:: 1.2 + + .. versionchanged:: 1.2.0 The + :paramref:`.ColumnOperators.endswith.autoescape` parameter is + now a simple boolean rather than a character; the escape + character itself is also escaped, and defaults to a forwards + slash, which itself can be customized using the + :paramref:`.ColumnOperators.endswith.escape` parameter. + + :param escape: a character which when given will render with the + ``ESCAPE`` keyword to establish that character as the escape + character. This character can then be placed preceding occurrences + of ``%`` and ``_`` to allow them to act as themselves and not + wildcard characters. + + An expression such as:: + + somecolumn.endswith("foo/%bar", escape="^") + + Will render as:: + + somecolumn LIKE '%' || :param ESCAPE '^' + + The parameter may also be combined with + :paramref:`.ColumnOperators.endswith.autoescape`:: + + somecolumn.endswith("foo%bar^bat", escape="^", autoescape=True) + + Where above, the given literal parameter will be converted to + ``"foo^%bar^^bat"`` before being passed to the database. + + .. seealso:: + + :meth:`.ColumnOperators.startswith` + + :meth:`.ColumnOperators.contains` + + :meth:`.ColumnOperators.like` + + """ + return self.operate(endswith_op, other, **kwargs) + + def contains(self, other, **kwargs): + r"""Implement the 'contains' operator. + + Produces a LIKE expression that tests against a match for the middle + of a string value:: + + column LIKE '%' || <other> || '%' + + E.g.:: + + stmt = select([sometable]).\ + where(sometable.c.column.contains("foobar")) + + Since the operator uses ``LIKE``, wildcard characters + ``"%"`` and ``"_"`` that are present inside the <other> expression + will behave like wildcards as well. For literal string + values, the :paramref:`.ColumnOperators.contains.autoescape` flag + may be set to ``True`` to apply escaping to occurences of these + characters within the string value so that they match as themselves + and not as wildcard characters. Alternatively, the + :paramref:`.ColumnOperators.contains.escape` parameter will establish + a given character as an escape character which can be of use when + the target expression is not a literal string. + + :param other: expression to be compared. This is usually a plain + string value, but can also be an arbitrary SQL expression. LIKE + wildcard characters ``%`` and ``_`` are not escaped by default unless + the :paramref:`.ColumnOperators.contains.autoescape` flag is + set to True. + + :param autoescape: boolean; when True, establishes an escape character + within the LIKE expression, then applies it to all occurrences of + ``"%"``, ``"_"`` and the escape character itself within the + comparison value, which is assumed to be a literal string and not a + SQL expression. + + An expression such as:: + + somecolumn.contains("foo%bar", autoescape=True) + + Will render as:: + + somecolumn LIKE '%' || :param || '%' ESCAPE '/' + + With the value of :param as ``"foo/%bar"``. + + .. versionadded:: 1.2 + + .. versionchanged:: 1.2.0 The + :paramref:`.ColumnOperators.contains.autoescape` parameter is + now a simple boolean rather than a character; the escape + character itself is also escaped, and defaults to a forwards + slash, which itself can be customized using the + :paramref:`.ColumnOperators.contains.escape` parameter. + + :param escape: a character which when given will render with the + ``ESCAPE`` keyword to establish that character as the escape + character. This character can then be placed preceding occurrences + of ``%`` and ``_`` to allow them to act as themselves and not + wildcard characters. + + An expression such as:: + + somecolumn.contains("foo/%bar", escape="^") + + Will render as:: + + somecolumn LIKE '%' || :param || '%' ESCAPE '^' + + The parameter may also be combined with + :paramref:`.ColumnOperators.contains.autoescape`:: + + somecolumn.contains("foo%bar^bat", escape="^", autoescape=True) + + Where above, the given literal parameter will be converted to + ``"foo^%bar^^bat"`` before being passed to the database. + + .. seealso:: + + :meth:`.ColumnOperators.startswith` + + :meth:`.ColumnOperators.endswith` + + :meth:`.ColumnOperators.like` + + + """ + return self.operate(contains_op, other, **kwargs) + + def match(self, other, **kwargs): + """Implements a database-specific 'match' operator. + + :meth:`~.ColumnOperators.match` attempts to resolve to + a MATCH-like function or operator provided by the backend. + Examples include: + + * PostgreSQL - renders ``x @@ to_tsquery(y)`` + * MySQL - renders ``MATCH (x) AGAINST (y IN BOOLEAN MODE)`` + * Oracle - renders ``CONTAINS(x, y)`` + * other backends may provide special implementations. + * Backends without any special implementation will emit + the operator as "MATCH". This is compatible with SQlite, for + example. + + """ + return self.operate(match_op, other, **kwargs) + + def desc(self): + """Produce a :func:`~.expression.desc` clause against the + parent object.""" + return self.operate(desc_op) + + def asc(self): + """Produce a :func:`~.expression.asc` clause against the + parent object.""" + return self.operate(asc_op) + + def nullsfirst(self): + """Produce a :func:`~.expression.nullsfirst` clause against the + parent object.""" + return self.operate(nullsfirst_op) + + def nullslast(self): + """Produce a :func:`~.expression.nullslast` clause against the + parent object.""" + return self.operate(nullslast_op) + + def collate(self, collation): + """Produce a :func:`~.expression.collate` clause against + the parent object, given the collation string. + + .. seealso:: + + :func:`~.expression.collate` + + """ + return self.operate(collate, collation) + + def __radd__(self, other): + """Implement the ``+`` operator in reverse. + + See :meth:`.ColumnOperators.__add__`. + + """ + return self.reverse_operate(add, other) + + def __rsub__(self, other): + """Implement the ``-`` operator in reverse. + + See :meth:`.ColumnOperators.__sub__`. + + """ + return self.reverse_operate(sub, other) + + def __rmul__(self, other): + """Implement the ``*`` operator in reverse. + + See :meth:`.ColumnOperators.__mul__`. + + """ + return self.reverse_operate(mul, other) + + def __rdiv__(self, other): + """Implement the ``/`` operator in reverse. + + See :meth:`.ColumnOperators.__div__`. + + """ + return self.reverse_operate(div, other) + + def __rmod__(self, other): + """Implement the ``%`` operator in reverse. + + See :meth:`.ColumnOperators.__mod__`. + + """ + return self.reverse_operate(mod, other) + + def between(self, cleft, cright, symmetric=False): + """Produce a :func:`~.expression.between` clause against + the parent object, given the lower and upper range. + + """ + return self.operate(between_op, cleft, cright, symmetric=symmetric) + + def distinct(self): + """Produce a :func:`~.expression.distinct` clause against the + parent object. + + """ + return self.operate(distinct_op) + + def any_(self): + """Produce a :func:`~.expression.any_` clause against the + parent object. + + This operator is only appropriate against a scalar subquery + object, or for some backends an column expression that is + against the ARRAY type, e.g.:: + + # postgresql '5 = ANY (somearray)' + expr = 5 == mytable.c.somearray.any_() + + # mysql '5 = ANY (SELECT value FROM table)' + expr = 5 == select([table.c.value]).as_scalar().any_() + + .. seealso:: + + :func:`~.expression.any_` - standalone version + + :func:`~.expression.all_` - ALL operator + + .. versionadded:: 1.1 + + """ + return self.operate(any_op) + + def all_(self): + """Produce a :func:`~.expression.all_` clause against the + parent object. + + This operator is only appropriate against a scalar subquery + object, or for some backends an column expression that is + against the ARRAY type, e.g.:: + + # postgresql '5 = ALL (somearray)' + expr = 5 == mytable.c.somearray.all_() + + # mysql '5 = ALL (SELECT value FROM table)' + expr = 5 == select([table.c.value]).as_scalar().all_() + + .. seealso:: + + :func:`~.expression.all_` - standalone version + + :func:`~.expression.any_` - ANY operator + + .. versionadded:: 1.1 + + """ + return self.operate(all_op) + + def __add__(self, other): + """Implement the ``+`` operator. + + In a column context, produces the clause ``a + b`` + if the parent object has non-string affinity. + If the parent object has a string affinity, + produces the concatenation operator, ``a || b`` - + see :meth:`.ColumnOperators.concat`. + + """ + return self.operate(add, other) + + def __sub__(self, other): + """Implement the ``-`` operator. + + In a column context, produces the clause ``a - b``. + + """ + return self.operate(sub, other) + + def __mul__(self, other): + """Implement the ``*`` operator. + + In a column context, produces the clause ``a * b``. + + """ + return self.operate(mul, other) + + def __div__(self, other): + """Implement the ``/`` operator. + + In a column context, produces the clause ``a / b``. + + """ + return self.operate(div, other) + + def __mod__(self, other): + """Implement the ``%`` operator. + + In a column context, produces the clause ``a % b``. + + """ + return self.operate(mod, other) + + def __truediv__(self, other): + """Implement the ``//`` operator. + + In a column context, produces the clause ``a / b``. + + """ + return self.operate(truediv, other) + + def __rtruediv__(self, other): + """Implement the ``//`` operator in reverse. + + See :meth:`.ColumnOperators.__truediv__`. + + """ + return self.reverse_operate(truediv, other) + + +def from_(): + raise NotImplementedError() + + +def as_(): + raise NotImplementedError() + + +def exists(): + raise NotImplementedError() + + +def istrue(a): + raise NotImplementedError() + + +def isfalse(a): + raise NotImplementedError() + + +def is_distinct_from(a, b): + return a.is_distinct_from(b) + + +def isnot_distinct_from(a, b): + return a.isnot_distinct_from(b) + + +def is_(a, b): + return a.is_(b) + + +def isnot(a, b): + return a.isnot(b) + + +def collate(a, b): + return a.collate(b) + + +def op(a, opstring, b): + return a.op(opstring)(b) + + +def like_op(a, b, escape=None): + return a.like(b, escape=escape) + + +def notlike_op(a, b, escape=None): + return a.notlike(b, escape=escape) + + +def ilike_op(a, b, escape=None): + return a.ilike(b, escape=escape) + + +def notilike_op(a, b, escape=None): + return a.notilike(b, escape=escape) + + +def between_op(a, b, c, symmetric=False): + return a.between(b, c, symmetric=symmetric) + + +def notbetween_op(a, b, c, symmetric=False): + return a.notbetween(b, c, symmetric=symmetric) + + +def in_op(a, b): + return a.in_(b) + + +def notin_op(a, b): + return a.notin_(b) + + +def distinct_op(a): + return a.distinct() + + +def any_op(a): + return a.any_() + + +def all_op(a): + return a.all_() + + +def _escaped_like_impl(fn, other, escape, autoescape): + if autoescape: + if autoescape is not True: + util.warn( + "The autoescape parameter is now a simple boolean True/False") + if escape is None: + escape = '/' + + if not isinstance(other, util.compat.string_types): + raise TypeError("String value expected when autoescape=True") + + if escape not in ('%', '_'): + other = other.replace(escape, escape + escape) + + other = ( + other.replace('%', escape + '%'). + replace('_', escape + '_') + ) + + return fn(other, escape=escape) + + +def startswith_op(a, b, escape=None, autoescape=False): + return _escaped_like_impl(a.startswith, b, escape, autoescape) + + +def notstartswith_op(a, b, escape=None, autoescape=False): + return ~_escaped_like_impl(a.startswith, b, escape, autoescape) + + +def endswith_op(a, b, escape=None, autoescape=False): + return _escaped_like_impl(a.endswith, b, escape, autoescape) + + +def notendswith_op(a, b, escape=None, autoescape=False): + return ~_escaped_like_impl(a.endswith, b, escape, autoescape) + + +def contains_op(a, b, escape=None, autoescape=False): + return _escaped_like_impl(a.contains, b, escape, autoescape) + + +def notcontains_op(a, b, escape=None, autoescape=False): + return ~_escaped_like_impl(a.contains, b, escape, autoescape) + + +def match_op(a, b, **kw): + return a.match(b, **kw) + + +def notmatch_op(a, b, **kw): + return a.notmatch(b, **kw) + + +def comma_op(a, b): + raise NotImplementedError() + + +def empty_in_op(a, b): + raise NotImplementedError() + + +def empty_notin_op(a, b): + raise NotImplementedError() + + +def concat_op(a, b): + return a.concat(b) + + +def desc_op(a): + return a.desc() + + +def asc_op(a): + return a.asc() + + +def nullsfirst_op(a): + return a.nullsfirst() + + +def nullslast_op(a): + return a.nullslast() + + +def json_getitem_op(a, b): + raise NotImplementedError() + + +def json_path_getitem_op(a, b): + raise NotImplementedError() + + +_commutative = {eq, ne, add, mul} + +_comparison = {eq, ne, lt, gt, ge, le, between_op, like_op, is_, + isnot, is_distinct_from, isnot_distinct_from} + + +def is_comparison(op): + return op in _comparison or \ + isinstance(op, custom_op) and op.is_comparison + + +def is_commutative(op): + return op in _commutative + + +def is_ordering_modifier(op): + return op in (asc_op, desc_op, + nullsfirst_op, nullslast_op) + + +def is_natural_self_precedent(op): + return op in _natural_self_precedent or \ + isinstance(op, custom_op) and op.natural_self_precedent + +_booleans = (inv, istrue, isfalse, and_, or_) + + +def is_boolean(op): + return is_comparison(op) or op in _booleans + +_mirror = { + gt: lt, + ge: le, + lt: gt, + le: ge +} + + +def mirror(op): + """rotate a comparison operator 180 degrees. + + Note this is not the same as negation. + + """ + return _mirror.get(op, op) + + +_associative = _commutative.union([concat_op, and_, or_]).difference([eq, ne]) + +_natural_self_precedent = _associative.union([ + getitem, json_getitem_op, json_path_getitem_op]) +"""Operators where if we have (a op b) op c, we don't want to +parenthesize (a op b). + +""" + + +_asbool = util.symbol('_asbool', canonical=-10) +_smallest = util.symbol('_smallest', canonical=-100) +_largest = util.symbol('_largest', canonical=100) + +_PRECEDENCE = { + from_: 15, + any_op: 15, + all_op: 15, + getitem: 15, + json_getitem_op: 15, + json_path_getitem_op: 15, + + mul: 8, + truediv: 8, + div: 8, + mod: 8, + neg: 8, + add: 7, + sub: 7, + + concat_op: 6, + + match_op: 5, + notmatch_op: 5, + + ilike_op: 5, + notilike_op: 5, + like_op: 5, + notlike_op: 5, + in_op: 5, + notin_op: 5, + + is_: 5, + isnot: 5, + + eq: 5, + ne: 5, + is_distinct_from: 5, + isnot_distinct_from: 5, + empty_in_op: 5, + empty_notin_op: 5, + gt: 5, + lt: 5, + ge: 5, + le: 5, + + between_op: 5, + notbetween_op: 5, + distinct_op: 5, + inv: 5, + istrue: 5, + isfalse: 5, + and_: 3, + or_: 2, + comma_op: -1, + + desc_op: 3, + asc_op: 3, + collate: 4, + + as_: -1, + exists: 0, + + _asbool: -10, + _smallest: _smallest, + _largest: _largest +} + + +def is_precedent(operator, against): + if operator is against and is_natural_self_precedent(operator): + return False + else: + return (_PRECEDENCE.get(operator, + getattr(operator, 'precedence', _smallest)) <= + _PRECEDENCE.get(against, + getattr(against, 'precedence', _largest))) diff --git a/venv/Lib/site-packages/sqlalchemy/sql/schema.py b/venv/Lib/site-packages/sqlalchemy/sql/schema.py new file mode 100644 index 0000000..3782d29 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/schema.py @@ -0,0 +1,4147 @@ +# sql/schema.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""The schema module provides the building blocks for database metadata. + +Each element within this module describes a database entity which can be +created and dropped, or is otherwise part of such an entity. Examples include +tables, columns, sequences, and indexes. + +All entities are subclasses of :class:`~sqlalchemy.schema.SchemaItem`, and as +defined in this module they are intended to be agnostic of any vendor-specific +constructs. + +A collection of entities are grouped into a unit called +:class:`~sqlalchemy.schema.MetaData`. MetaData serves as a logical grouping of +schema elements, and can also be associated with an actual database connection +such that operations involving the contained elements can contact the database +as needed. + +Two of the elements here also build upon their "syntactic" counterparts, which +are defined in :class:`~sqlalchemy.sql.expression.`, specifically +:class:`~sqlalchemy.schema.Table` and :class:`~sqlalchemy.schema.Column`. +Since these objects are part of the SQL expression language, they are usable +as components in SQL expressions. + +""" +from __future__ import absolute_import + +from .. import exc, util, event, inspection +from .base import SchemaEventTarget, DialectKWArgs +import operator +from . import visitors +from . import type_api +from .base import _bind_or_error, ColumnCollection +from .elements import ClauseElement, ColumnClause, \ + _as_truncated, TextClause, _literal_as_text,\ + ColumnElement, quoted_name +from .selectable import TableClause +import collections +import sqlalchemy +from . import ddl + +RETAIN_SCHEMA = util.symbol('retain_schema') + +BLANK_SCHEMA = util.symbol( + 'blank_schema', + """Symbol indicating that a :class:`.Table` or :class:`.Sequence` + should have 'None' for its schema, even if the parent + :class:`.MetaData` has specified a schema. + + .. versionadded:: 1.0.14 + + """ +) + + +def _get_table_key(name, schema): + if schema is None: + return name + else: + return schema + "." + name + + +# this should really be in sql/util.py but we'd have to +# break an import cycle +def _copy_expression(expression, source_table, target_table): + def replace(col): + if isinstance(col, Column) and \ + col.table is source_table and col.key in source_table.c: + return target_table.c[col.key] + else: + return None + return visitors.replacement_traverse(expression, {}, replace) + + +@inspection._self_inspects +class SchemaItem(SchemaEventTarget, visitors.Visitable): + """Base class for items that define a database schema.""" + + __visit_name__ = 'schema_item' + + def _init_items(self, *args): + """Initialize the list of child items for this SchemaItem.""" + + for item in args: + if item is not None: + item._set_parent_with_dispatch(self) + + def get_children(self, **kwargs): + """used to allow SchemaVisitor access""" + return [] + + def __repr__(self): + return util.generic_repr(self, omit_kwarg=['info']) + + @property + @util.deprecated('0.9', 'Use ``<obj>.name.quote``') + def quote(self): + """Return the value of the ``quote`` flag passed + to this schema object, for those schema items which + have a ``name`` field. + + """ + + return self.name.quote + + @util.memoized_property + def info(self): + """Info dictionary associated with the object, allowing user-defined + data to be associated with this :class:`.SchemaItem`. + + The dictionary is automatically generated when first accessed. + It can also be specified in the constructor of some objects, + such as :class:`.Table` and :class:`.Column`. + + """ + return {} + + def _schema_item_copy(self, schema_item): + if 'info' in self.__dict__: + schema_item.info = self.info.copy() + schema_item.dispatch._update(self.dispatch) + return schema_item + + def _translate_schema(self, effective_schema, map_): + return map_.get(effective_schema, effective_schema) + + +class Table(DialectKWArgs, SchemaItem, TableClause): + r"""Represent a table in a database. + + e.g.:: + + mytable = Table("mytable", metadata, + Column('mytable_id', Integer, primary_key=True), + Column('value', String(50)) + ) + + The :class:`.Table` object constructs a unique instance of itself based + on its name and optional schema name within the given + :class:`.MetaData` object. Calling the :class:`.Table` + constructor with the same name and same :class:`.MetaData` argument + a second time will return the *same* :class:`.Table` object - in this way + the :class:`.Table` constructor acts as a registry function. + + .. seealso:: + + :ref:`metadata_describing` - Introduction to database metadata + + Constructor arguments are as follows: + + :param name: The name of this table as represented in the database. + + The table name, along with the value of the ``schema`` parameter, + forms a key which uniquely identifies this :class:`.Table` within + the owning :class:`.MetaData` collection. + Additional calls to :class:`.Table` with the same name, metadata, + and schema name will return the same :class:`.Table` object. + + Names which contain no upper case characters + will be treated as case insensitive names, and will not be quoted + unless they are a reserved word or contain special characters. + A name with any number of upper case characters is considered + to be case sensitive, and will be sent as quoted. + + To enable unconditional quoting for the table name, specify the flag + ``quote=True`` to the constructor, or use the :class:`.quoted_name` + construct to specify the name. + + :param metadata: a :class:`.MetaData` object which will contain this + table. The metadata is used as a point of association of this table + with other tables which are referenced via foreign key. It also + may be used to associate this table with a particular + :class:`.Connectable`. + + :param \*args: Additional positional arguments are used primarily + to add the list of :class:`.Column` objects contained within this + table. Similar to the style of a CREATE TABLE statement, other + :class:`.SchemaItem` constructs may be added here, including + :class:`.PrimaryKeyConstraint`, and :class:`.ForeignKeyConstraint`. + + :param autoload: Defaults to False, unless :paramref:`.Table.autoload_with` + is set in which case it defaults to True; :class:`.Column` objects + for this table should be reflected from the database, possibly + augmenting or replacing existing :class:`.Column` objects that were + explicitly specified. + + .. versionchanged:: 1.0.0 setting the :paramref:`.Table.autoload_with` + parameter implies that :paramref:`.Table.autoload` will default + to True. + + .. seealso:: + + :ref:`metadata_reflection_toplevel` + + :param autoload_replace: Defaults to ``True``; when using + :paramref:`.Table.autoload` + in conjunction with :paramref:`.Table.extend_existing`, indicates + that :class:`.Column` objects present in the already-existing + :class:`.Table` object should be replaced with columns of the same + name retrieved from the autoload process. When ``False``, columns + already present under existing names will be omitted from the + reflection process. + + Note that this setting does not impact :class:`.Column` objects + specified programmatically within the call to :class:`.Table` that + also is autoloading; those :class:`.Column` objects will always + replace existing columns of the same name when + :paramref:`.Table.extend_existing` is ``True``. + + .. versionadded:: 0.7.5 + + .. seealso:: + + :paramref:`.Table.autoload` + + :paramref:`.Table.extend_existing` + + :param autoload_with: An :class:`.Engine` or :class:`.Connection` object + with which this :class:`.Table` object will be reflected; when + set to a non-None value, it implies that :paramref:`.Table.autoload` + is ``True``. If left unset, but :paramref:`.Table.autoload` is + explicitly set to ``True``, an autoload operation will attempt to + proceed by locating an :class:`.Engine` or :class:`.Connection` bound + to the underlying :class:`.MetaData` object. + + .. seealso:: + + :paramref:`.Table.autoload` + + :param extend_existing: When ``True``, indicates that if this + :class:`.Table` is already present in the given :class:`.MetaData`, + apply further arguments within the constructor to the existing + :class:`.Table`. + + If :paramref:`.Table.extend_existing` or + :paramref:`.Table.keep_existing` are not set, and the given name + of the new :class:`.Table` refers to a :class:`.Table` that is + already present in the target :class:`.MetaData` collection, and + this :class:`.Table` specifies additional columns or other constructs + or flags that modify the table's state, an + error is raised. The purpose of these two mutually-exclusive flags + is to specify what action should be taken when a :class:`.Table` + is specified that matches an existing :class:`.Table`, yet specifies + additional constructs. + + :paramref:`.Table.extend_existing` will also work in conjunction + with :paramref:`.Table.autoload` to run a new reflection + operation against the database, even if a :class:`.Table` + of the same name is already present in the target + :class:`.MetaData`; newly reflected :class:`.Column` objects + and other options will be added into the state of the + :class:`.Table`, potentially overwriting existing columns + and options of the same name. + + .. versionchanged:: 0.7.4 :paramref:`.Table.extend_existing` will + invoke a new reflection operation when combined with + :paramref:`.Table.autoload` set to True. + + As is always the case with :paramref:`.Table.autoload`, + :class:`.Column` objects can be specified in the same :class:`.Table` + constructor, which will take precedence. Below, the existing + table ``mytable`` will be augmented with :class:`.Column` objects + both reflected from the database, as well as the given :class:`.Column` + named "y":: + + Table("mytable", metadata, + Column('y', Integer), + extend_existing=True, + autoload=True, + autoload_with=engine + ) + + .. seealso:: + + :paramref:`.Table.autoload` + + :paramref:`.Table.autoload_replace` + + :paramref:`.Table.keep_existing` + + + :param implicit_returning: True by default - indicates that + RETURNING can be used by default to fetch newly inserted primary key + values, for backends which support this. Note that + create_engine() also provides an implicit_returning flag. + + :param include_columns: A list of strings indicating a subset of + columns to be loaded via the ``autoload`` operation; table columns who + aren't present in this list will not be represented on the resulting + ``Table`` object. Defaults to ``None`` which indicates all columns + should be reflected. + + :param info: Optional data dictionary which will be populated into the + :attr:`.SchemaItem.info` attribute of this object. + + :param keep_existing: When ``True``, indicates that if this Table + is already present in the given :class:`.MetaData`, ignore + further arguments within the constructor to the existing + :class:`.Table`, and return the :class:`.Table` object as + originally created. This is to allow a function that wishes + to define a new :class:`.Table` on first call, but on + subsequent calls will return the same :class:`.Table`, + without any of the declarations (particularly constraints) + being applied a second time. + + If :paramref:`.Table.extend_existing` or + :paramref:`.Table.keep_existing` are not set, and the given name + of the new :class:`.Table` refers to a :class:`.Table` that is + already present in the target :class:`.MetaData` collection, and + this :class:`.Table` specifies additional columns or other constructs + or flags that modify the table's state, an + error is raised. The purpose of these two mutually-exclusive flags + is to specify what action should be taken when a :class:`.Table` + is specified that matches an existing :class:`.Table`, yet specifies + additional constructs. + + .. seealso:: + + :paramref:`.Table.extend_existing` + + :param listeners: A list of tuples of the form ``(<eventname>, <fn>)`` + which will be passed to :func:`.event.listen` upon construction. + This alternate hook to :func:`.event.listen` allows the establishment + of a listener function specific to this :class:`.Table` before + the "autoload" process begins. Particularly useful for + the :meth:`.DDLEvents.column_reflect` event:: + + def listen_for_reflect(table, column_info): + "handle the column reflection event" + # ... + + t = Table( + 'sometable', + autoload=True, + listeners=[ + ('column_reflect', listen_for_reflect) + ]) + + :param mustexist: When ``True``, indicates that this Table must already + be present in the given :class:`.MetaData` collection, else + an exception is raised. + + :param prefixes: + A list of strings to insert after CREATE in the CREATE TABLE + statement. They will be separated by spaces. + + :param quote: Force quoting of this table's name on or off, corresponding + to ``True`` or ``False``. When left at its default of ``None``, + the column identifier will be quoted according to whether the name is + case sensitive (identifiers with at least one upper case character are + treated as case sensitive), or if it's a reserved word. This flag + is only needed to force quoting of a reserved word which is not known + by the SQLAlchemy dialect. + + :param quote_schema: same as 'quote' but applies to the schema identifier. + + :param schema: The schema name for this table, which is required if + the table resides in a schema other than the default selected schema + for the engine's database connection. Defaults to ``None``. + + If the owning :class:`.MetaData` of this :class:`.Table` specifies + its own :paramref:`.MetaData.schema` parameter, then that schema + name will be applied to this :class:`.Table` if the schema parameter + here is set to ``None``. To set a blank schema name on a :class:`.Table` + that would otherwise use the schema set on the owning :class:`.MetaData`, + specify the special symbol :attr:`.BLANK_SCHEMA`. + + .. versionadded:: 1.0.14 Added the :attr:`.BLANK_SCHEMA` symbol to + allow a :class:`.Table` to have a blank schema name even when the + parent :class:`.MetaData` specifies :paramref:`.MetaData.schema`. + + The quoting rules for the schema name are the same as those for the + ``name`` parameter, in that quoting is applied for reserved words or + case-sensitive names; to enable unconditional quoting for the + schema name, specify the flag + ``quote_schema=True`` to the constructor, or use the + :class:`.quoted_name` construct to specify the name. + + :param useexisting: Deprecated. Use :paramref:`.Table.extend_existing`. + + :param comment: Optional string that will render an SQL comment on table + creation. + + .. versionadded:: 1.2 Added the :paramref:`.Table.comment` parameter + to :class:`.Table`. + + :param \**kw: Additional keyword arguments not mentioned above are + dialect specific, and passed in the form ``<dialectname>_<argname>``. + See the documentation regarding an individual dialect at + :ref:`dialect_toplevel` for detail on documented arguments. + + """ + + __visit_name__ = 'table' + + def __new__(cls, *args, **kw): + if not args: + # python3k pickle seems to call this + return object.__new__(cls) + + try: + name, metadata, args = args[0], args[1], args[2:] + except IndexError: + raise TypeError("Table() takes at least two arguments") + + schema = kw.get('schema', None) + if schema is None: + schema = metadata.schema + elif schema is BLANK_SCHEMA: + schema = None + keep_existing = kw.pop('keep_existing', False) + extend_existing = kw.pop('extend_existing', False) + if 'useexisting' in kw: + msg = "useexisting is deprecated. Use extend_existing." + util.warn_deprecated(msg) + if extend_existing: + msg = "useexisting is synonymous with extend_existing." + raise exc.ArgumentError(msg) + extend_existing = kw.pop('useexisting', False) + + if keep_existing and extend_existing: + msg = "keep_existing and extend_existing are mutually exclusive." + raise exc.ArgumentError(msg) + + mustexist = kw.pop('mustexist', False) + key = _get_table_key(name, schema) + if key in metadata.tables: + if not keep_existing and not extend_existing and bool(args): + raise exc.InvalidRequestError( + "Table '%s' is already defined for this MetaData " + "instance. Specify 'extend_existing=True' " + "to redefine " + "options and columns on an " + "existing Table object." % key) + table = metadata.tables[key] + if extend_existing: + table._init_existing(*args, **kw) + return table + else: + if mustexist: + raise exc.InvalidRequestError( + "Table '%s' not defined" % (key)) + table = object.__new__(cls) + table.dispatch.before_parent_attach(table, metadata) + metadata._add_table(name, schema, table) + try: + table._init(name, metadata, *args, **kw) + table.dispatch.after_parent_attach(table, metadata) + return table + except: + with util.safe_reraise(): + metadata._remove_table(name, schema) + + @property + @util.deprecated('0.9', 'Use ``table.schema.quote``') + def quote_schema(self): + """Return the value of the ``quote_schema`` flag passed + to this :class:`.Table`. + """ + + return self.schema.quote + + def __init__(self, *args, **kw): + """Constructor for :class:`~.schema.Table`. + + This method is a no-op. See the top-level + documentation for :class:`~.schema.Table` + for constructor arguments. + + """ + # __init__ is overridden to prevent __new__ from + # calling the superclass constructor. + + def _init(self, name, metadata, *args, **kwargs): + super(Table, self).__init__( + quoted_name(name, kwargs.pop('quote', None))) + self.metadata = metadata + + self.schema = kwargs.pop('schema', None) + if self.schema is None: + self.schema = metadata.schema + elif self.schema is BLANK_SCHEMA: + self.schema = None + else: + quote_schema = kwargs.pop('quote_schema', None) + self.schema = quoted_name(self.schema, quote_schema) + + self.indexes = set() + self.constraints = set() + self._columns = ColumnCollection() + PrimaryKeyConstraint(_implicit_generated=True).\ + _set_parent_with_dispatch(self) + self.foreign_keys = set() + self._extra_dependencies = set() + if self.schema is not None: + self.fullname = "%s.%s" % (self.schema, self.name) + else: + self.fullname = self.name + + autoload_with = kwargs.pop('autoload_with', None) + autoload = kwargs.pop('autoload', autoload_with is not None) + # this argument is only used with _init_existing() + kwargs.pop('autoload_replace', True) + _extend_on = kwargs.pop("_extend_on", None) + + include_columns = kwargs.pop('include_columns', None) + + self.implicit_returning = kwargs.pop('implicit_returning', True) + + self.comment = kwargs.pop('comment', None) + + if 'info' in kwargs: + self.info = kwargs.pop('info') + if 'listeners' in kwargs: + listeners = kwargs.pop('listeners') + for evt, fn in listeners: + event.listen(self, evt, fn) + + self._prefixes = kwargs.pop('prefixes', []) + + self._extra_kwargs(**kwargs) + + # load column definitions from the database if 'autoload' is defined + # we do it after the table is in the singleton dictionary to support + # circular foreign keys + if autoload: + self._autoload( + metadata, autoload_with, + include_columns, _extend_on=_extend_on) + + # initialize all the column, etc. objects. done after reflection to + # allow user-overrides + self._init_items(*args) + + def _autoload(self, metadata, autoload_with, include_columns, + exclude_columns=(), _extend_on=None): + + if autoload_with: + autoload_with.run_callable( + autoload_with.dialect.reflecttable, + self, include_columns, exclude_columns, + _extend_on=_extend_on + ) + else: + bind = _bind_or_error( + metadata, + msg="No engine is bound to this Table's MetaData. " + "Pass an engine to the Table via " + "autoload_with=<someengine>, " + "or associate the MetaData with an engine via " + "metadata.bind=<someengine>") + bind.run_callable( + bind.dialect.reflecttable, + self, include_columns, exclude_columns, + _extend_on=_extend_on + ) + + @property + def _sorted_constraints(self): + """Return the set of constraints as a list, sorted by creation + order. + + """ + return sorted(self.constraints, key=lambda c: c._creation_order) + + @property + def foreign_key_constraints(self): + """:class:`.ForeignKeyConstraint` objects referred to by this + :class:`.Table`. + + This list is produced from the collection of :class:`.ForeignKey` + objects currently associated. + + .. versionadded:: 1.0.0 + + """ + return set(fkc.constraint for fkc in self.foreign_keys) + + def _init_existing(self, *args, **kwargs): + autoload_with = kwargs.pop('autoload_with', None) + autoload = kwargs.pop('autoload', autoload_with is not None) + autoload_replace = kwargs.pop('autoload_replace', True) + schema = kwargs.pop('schema', None) + _extend_on = kwargs.pop('_extend_on', None) + + if schema and schema != self.schema: + raise exc.ArgumentError( + "Can't change schema of existing table from '%s' to '%s'", + (self.schema, schema)) + + include_columns = kwargs.pop('include_columns', None) + + if include_columns is not None: + for c in self.c: + if c.name not in include_columns: + self._columns.remove(c) + + for key in ('quote', 'quote_schema'): + if key in kwargs: + raise exc.ArgumentError( + "Can't redefine 'quote' or 'quote_schema' arguments") + + if 'comment' in kwargs: + self.comment = kwargs.pop('comment', None) + + if 'info' in kwargs: + self.info = kwargs.pop('info') + + if autoload: + if not autoload_replace: + # don't replace columns already present. + # we'd like to do this for constraints also however we don't + # have simple de-duping for unnamed constraints. + exclude_columns = [c.name for c in self.c] + else: + exclude_columns = () + self._autoload( + self.metadata, autoload_with, + include_columns, exclude_columns, _extend_on=_extend_on) + + self._extra_kwargs(**kwargs) + self._init_items(*args) + + def _extra_kwargs(self, **kwargs): + self._validate_dialect_kwargs(kwargs) + + def _init_collections(self): + pass + + def _reset_exported(self): + pass + + @property + def _autoincrement_column(self): + return self.primary_key._autoincrement_column + + @property + def key(self): + """Return the 'key' for this :class:`.Table`. + + This value is used as the dictionary key within the + :attr:`.MetaData.tables` collection. It is typically the same + as that of :attr:`.Table.name` for a table with no + :attr:`.Table.schema` set; otherwise it is typically of the form + ``schemaname.tablename``. + + """ + return _get_table_key(self.name, self.schema) + + def __repr__(self): + return "Table(%s)" % ', '.join( + [repr(self.name)] + [repr(self.metadata)] + + [repr(x) for x in self.columns] + + ["%s=%s" % (k, repr(getattr(self, k))) for k in ['schema']]) + + def __str__(self): + return _get_table_key(self.description, self.schema) + + @property + def bind(self): + """Return the connectable associated with this Table.""" + + return self.metadata and self.metadata.bind or None + + def add_is_dependent_on(self, table): + """Add a 'dependency' for this Table. + + This is another Table object which must be created + first before this one can, or dropped after this one. + + Usually, dependencies between tables are determined via + ForeignKey objects. However, for other situations that + create dependencies outside of foreign keys (rules, inheriting), + this method can manually establish such a link. + + """ + self._extra_dependencies.add(table) + + def append_column(self, column): + """Append a :class:`~.schema.Column` to this :class:`~.schema.Table`. + + The "key" of the newly added :class:`~.schema.Column`, i.e. the + value of its ``.key`` attribute, will then be available + in the ``.c`` collection of this :class:`~.schema.Table`, and the + column definition will be included in any CREATE TABLE, SELECT, + UPDATE, etc. statements generated from this :class:`~.schema.Table` + construct. + + Note that this does **not** change the definition of the table + as it exists within any underlying database, assuming that + table has already been created in the database. Relational + databases support the addition of columns to existing tables + using the SQL ALTER command, which would need to be + emitted for an already-existing table that doesn't contain + the newly added column. + + """ + + column._set_parent_with_dispatch(self) + + def append_constraint(self, constraint): + """Append a :class:`~.schema.Constraint` to this + :class:`~.schema.Table`. + + This has the effect of the constraint being included in any + future CREATE TABLE statement, assuming specific DDL creation + events have not been associated with the given + :class:`~.schema.Constraint` object. + + Note that this does **not** produce the constraint within the + relational database automatically, for a table that already exists + in the database. To add a constraint to an + existing relational database table, the SQL ALTER command must + be used. SQLAlchemy also provides the + :class:`.AddConstraint` construct which can produce this SQL when + invoked as an executable clause. + + """ + + constraint._set_parent_with_dispatch(self) + + def append_ddl_listener(self, event_name, listener): + """Append a DDL event listener to this ``Table``. + + .. deprecated:: 0.7 + See :class:`.DDLEvents`. + + """ + + def adapt_listener(target, connection, **kw): + listener(event_name, target, connection) + + event.listen(self, "" + event_name.replace('-', '_'), adapt_listener) + + def _set_parent(self, metadata): + metadata._add_table(self.name, self.schema, self) + self.metadata = metadata + + def get_children(self, column_collections=True, + schema_visitor=False, **kw): + if not schema_visitor: + return TableClause.get_children( + self, column_collections=column_collections, **kw) + else: + if column_collections: + return list(self.columns) + else: + return [] + + def exists(self, bind=None): + """Return True if this table exists.""" + + if bind is None: + bind = _bind_or_error(self) + + return bind.run_callable(bind.dialect.has_table, + self.name, schema=self.schema) + + def create(self, bind=None, checkfirst=False): + """Issue a ``CREATE`` statement for this + :class:`.Table`, using the given :class:`.Connectable` + for connectivity. + + .. seealso:: + + :meth:`.MetaData.create_all`. + + """ + + if bind is None: + bind = _bind_or_error(self) + bind._run_visitor(ddl.SchemaGenerator, + self, + checkfirst=checkfirst) + + def drop(self, bind=None, checkfirst=False): + """Issue a ``DROP`` statement for this + :class:`.Table`, using the given :class:`.Connectable` + for connectivity. + + .. seealso:: + + :meth:`.MetaData.drop_all`. + + """ + if bind is None: + bind = _bind_or_error(self) + bind._run_visitor(ddl.SchemaDropper, + self, + checkfirst=checkfirst) + + def tometadata(self, metadata, schema=RETAIN_SCHEMA, + referred_schema_fn=None, name=None): + """Return a copy of this :class:`.Table` associated with a different + :class:`.MetaData`. + + E.g.:: + + m1 = MetaData() + + user = Table('user', m1, Column('id', Integer, priamry_key=True)) + + m2 = MetaData() + user_copy = user.tometadata(m2) + + :param metadata: Target :class:`.MetaData` object, into which the + new :class:`.Table` object will be created. + + :param schema: optional string name indicating the target schema. + Defaults to the special symbol :attr:`.RETAIN_SCHEMA` which indicates + that no change to the schema name should be made in the new + :class:`.Table`. If set to a string name, the new :class:`.Table` + will have this new name as the ``.schema``. If set to ``None``, the + schema will be set to that of the schema set on the target + :class:`.MetaData`, which is typically ``None`` as well, unless + set explicitly:: + + m2 = MetaData(schema='newschema') + + # user_copy_one will have "newschema" as the schema name + user_copy_one = user.tometadata(m2, schema=None) + + m3 = MetaData() # schema defaults to None + + # user_copy_two will have None as the schema name + user_copy_two = user.tometadata(m3, schema=None) + + :param referred_schema_fn: optional callable which can be supplied + in order to provide for the schema name that should be assigned + to the referenced table of a :class:`.ForeignKeyConstraint`. + The callable accepts this parent :class:`.Table`, the + target schema that we are changing to, the + :class:`.ForeignKeyConstraint` object, and the existing + "target schema" of that constraint. The function should return the + string schema name that should be applied. + E.g.:: + + def referred_schema_fn(table, to_schema, + constraint, referred_schema): + if referred_schema == 'base_tables': + return referred_schema + else: + return to_schema + + new_table = table.tometadata(m2, schema="alt_schema", + referred_schema_fn=referred_schema_fn) + + .. versionadded:: 0.9.2 + + :param name: optional string name indicating the target table name. + If not specified or None, the table name is retained. This allows + a :class:`.Table` to be copied to the same :class:`.MetaData` target + with a new name. + + .. versionadded:: 1.0.0 + + """ + if name is None: + name = self.name + if schema is RETAIN_SCHEMA: + schema = self.schema + elif schema is None: + schema = metadata.schema + key = _get_table_key(name, schema) + if key in metadata.tables: + util.warn("Table '%s' already exists within the given " + "MetaData - not copying." % self.description) + return metadata.tables[key] + + args = [] + for c in self.columns: + args.append(c.copy(schema=schema)) + table = Table( + name, metadata, schema=schema, + comment=self.comment, + *args, **self.kwargs + ) + for c in self.constraints: + if isinstance(c, ForeignKeyConstraint): + referred_schema = c._referred_schema + if referred_schema_fn: + fk_constraint_schema = referred_schema_fn( + self, schema, c, referred_schema) + else: + fk_constraint_schema = ( + schema if referred_schema == self.schema else None) + table.append_constraint( + c.copy(schema=fk_constraint_schema, target_table=table)) + elif not c._type_bound: + # skip unique constraints that would be generated + # by the 'unique' flag on Column + if c._column_flag: + continue + + table.append_constraint( + c.copy(schema=schema, target_table=table)) + for index in self.indexes: + # skip indexes that would be generated + # by the 'index' flag on Column + if index._column_flag: + continue + Index(index.name, + unique=index.unique, + *[_copy_expression(expr, self, table) + for expr in index.expressions], + _table=table, + **index.kwargs) + return self._schema_item_copy(table) + + +class Column(SchemaItem, ColumnClause): + """Represents a column in a database table.""" + + __visit_name__ = 'column' + + def __init__(self, *args, **kwargs): + r""" + Construct a new ``Column`` object. + + :param name: The name of this column as represented in the database. + This argument may be the first positional argument, or specified + via keyword. + + Names which contain no upper case characters + will be treated as case insensitive names, and will not be quoted + unless they are a reserved word. Names with any number of upper + case characters will be quoted and sent exactly. Note that this + behavior applies even for databases which standardize upper + case names as case insensitive such as Oracle. + + The name field may be omitted at construction time and applied + later, at any time before the Column is associated with a + :class:`.Table`. This is to support convenient + usage within the :mod:`~sqlalchemy.ext.declarative` extension. + + :param type\_: The column's type, indicated using an instance which + subclasses :class:`~sqlalchemy.types.TypeEngine`. If no arguments + are required for the type, the class of the type can be sent + as well, e.g.:: + + # use a type with arguments + Column('data', String(50)) + + # use no arguments + Column('level', Integer) + + The ``type`` argument may be the second positional argument + or specified by keyword. + + If the ``type`` is ``None`` or is omitted, it will first default to + the special type :class:`.NullType`. If and when this + :class:`.Column` is made to refer to another column using + :class:`.ForeignKey` and/or :class:`.ForeignKeyConstraint`, the type + of the remote-referenced column will be copied to this column as + well, at the moment that the foreign key is resolved against that + remote :class:`.Column` object. + + .. versionchanged:: 0.9.0 + Support for propagation of type to a :class:`.Column` from its + :class:`.ForeignKey` object has been improved and should be + more reliable and timely. + + :param \*args: Additional positional arguments include various + :class:`.SchemaItem` derived constructs which will be applied + as options to the column. These include instances of + :class:`.Constraint`, :class:`.ForeignKey`, :class:`.ColumnDefault`, + and :class:`.Sequence`. In some cases an equivalent keyword + argument is available such as ``server_default``, ``default`` + and ``unique``. + + :param autoincrement: Set up "auto increment" semantics for an integer + primary key column. The default value is the string ``"auto"`` + which indicates that a single-column primary key that is of + an INTEGER type with no stated client-side or python-side defaults + should receive auto increment semantics automatically; + all other varieties of primary key columns will not. This + includes that :term:`DDL` such as PostgreSQL SERIAL or MySQL + AUTO_INCREMENT will be emitted for this column during a table + create, as well as that the column is assumed to generate new + integer primary key values when an INSERT statement invokes which + will be retrieved by the dialect. + + The flag may be set to ``True`` to indicate that a column which + is part of a composite (e.g. multi-column) primary key should + have autoincrement semantics, though note that only one column + within a primary key may have this setting. It can also + be set to ``True`` to indicate autoincrement semantics on a + column that has a client-side or server-side default configured, + however note that not all dialects can accommodate all styles + of default as an "autoincrement". It can also be + set to ``False`` on a single-column primary key that has a + datatype of INTEGER in order to disable auto increment semantics + for that column. + + .. versionchanged:: 1.1 The autoincrement flag now defaults to + ``"auto"`` which indicates autoincrement semantics by default + for single-column integer primary keys only; for composite + (multi-column) primary keys, autoincrement is never implicitly + enabled; as always, ``autoincrement=True`` will allow for + at most one of those columns to be an "autoincrement" column. + ``autoincrement=True`` may also be set on a :class:`.Column` + that has an explicit client-side or server-side default, + subject to limitations of the backend database and dialect. + + + The setting *only* has an effect for columns which are: + + * Integer derived (i.e. INT, SMALLINT, BIGINT). + + * Part of the primary key + + * Not referring to another column via :class:`.ForeignKey`, unless + the value is specified as ``'ignore_fk'``:: + + # turn on autoincrement for this column despite + # the ForeignKey() + Column('id', ForeignKey('other.id'), + primary_key=True, autoincrement='ignore_fk') + + It is typically not desirable to have "autoincrement" enabled + on a column that refers to another via foreign key, as such a column + is required to refer to a value that originates from elsewhere. + + The setting has these two effects on columns that meet the + above criteria: + + * DDL issued for the column will include database-specific + keywords intended to signify this column as an + "autoincrement" column, such as AUTO INCREMENT on MySQL, + SERIAL on PostgreSQL, and IDENTITY on MS-SQL. It does + *not* issue AUTOINCREMENT for SQLite since this is a + special SQLite flag that is not required for autoincrementing + behavior. + + .. seealso:: + + :ref:`sqlite_autoincrement` + + * The column will be considered to be available using an + "autoincrement" method specific to the backend database, such + as calling upon ``cursor.lastrowid``, using RETURNING in an + INSERT statement to get at a sequence-generated value, or using + special functions such as "SELECT scope_identity()". + These methods are highly specific to the DBAPIs and databases in + use and vary greatly, so care should be taken when associating + ``autoincrement=True`` with a custom default generation function. + + + :param default: A scalar, Python callable, or + :class:`.ColumnElement` expression representing the + *default value* for this column, which will be invoked upon insert + if this column is otherwise not specified in the VALUES clause of + the insert. This is a shortcut to using :class:`.ColumnDefault` as + a positional argument; see that class for full detail on the + structure of the argument. + + Contrast this argument to :paramref:`.Column.server_default` + which creates a default generator on the database side. + + .. seealso:: + + :ref:`metadata_defaults_toplevel` + + :param doc: optional String that can be used by the ORM or similar + to document attributes on the Python side. This attribute does + **not** render SQL comments; use the :paramref:`.Column.comment` + parameter for this purpose. + + :param key: An optional string identifier which will identify this + ``Column`` object on the :class:`.Table`. When a key is provided, + this is the only identifier referencing the ``Column`` within the + application, including ORM attribute mapping; the ``name`` field + is used only when rendering SQL. + + :param index: When ``True``, indicates that the column is indexed. + This is a shortcut for using a :class:`.Index` construct on the + table. To specify indexes with explicit names or indexes that + contain multiple columns, use the :class:`.Index` construct + instead. + + :param info: Optional data dictionary which will be populated into the + :attr:`.SchemaItem.info` attribute of this object. + + :param nullable: When set to ``False``, will cause the "NOT NULL" + phrase to be added when generating DDL for the column. When + ``True``, will normally generate nothing (in SQL this defaults to + "NULL"), except in some very specific backend-specific edge cases + where "NULL" may render explicitly. Defaults to ``True`` unless + :paramref:`~.Column.primary_key` is also ``True``, in which case it + defaults to ``False``. This parameter is only used when issuing + CREATE TABLE statements. + + :param onupdate: A scalar, Python callable, or + :class:`~sqlalchemy.sql.expression.ClauseElement` representing a + default value to be applied to the column within UPDATE + statements, which will be invoked upon update if this column is not + present in the SET clause of the update. This is a shortcut to + using :class:`.ColumnDefault` as a positional argument with + ``for_update=True``. + + .. seealso:: + + :ref:`metadata_defaults` - complete discussion of onupdate + + :param primary_key: If ``True``, marks this column as a primary key + column. Multiple columns can have this flag set to specify + composite primary keys. As an alternative, the primary key of a + :class:`.Table` can be specified via an explicit + :class:`.PrimaryKeyConstraint` object. + + :param server_default: A :class:`.FetchedValue` instance, str, Unicode + or :func:`~sqlalchemy.sql.expression.text` construct representing + the DDL DEFAULT value for the column. + + String types will be emitted as-is, surrounded by single quotes:: + + Column('x', Text, server_default="val") + + x TEXT DEFAULT 'val' + + A :func:`~sqlalchemy.sql.expression.text` expression will be + rendered as-is, without quotes:: + + Column('y', DateTime, server_default=text('NOW()')) + + y DATETIME DEFAULT NOW() + + Strings and text() will be converted into a + :class:`.DefaultClause` object upon initialization. + + Use :class:`.FetchedValue` to indicate that an already-existing + column will generate a default value on the database side which + will be available to SQLAlchemy for post-fetch after inserts. This + construct does not specify any DDL and the implementation is left + to the database, such as via a trigger. + + .. seealso:: + + :ref:`server_defaults` - complete discussion of server side + defaults + + :param server_onupdate: A :class:`.FetchedValue` instance + representing a database-side default generation function, + such as a trigger. This + indicates to SQLAlchemy that a newly generated value will be + available after updates. This construct does not actually + implement any kind of generation function within the database, + which instead must be specified separately. + + .. seealso:: + + :ref:`triggered_columns` + + :param quote: Force quoting of this column's name on or off, + corresponding to ``True`` or ``False``. When left at its default + of ``None``, the column identifier will be quoted according to + whether the name is case sensitive (identifiers with at least one + upper case character are treated as case sensitive), or if it's a + reserved word. This flag is only needed to force quoting of a + reserved word which is not known by the SQLAlchemy dialect. + + :param unique: When ``True``, indicates that this column contains a + unique constraint, or if ``index`` is ``True`` as well, indicates + that the :class:`.Index` should be created with the unique flag. + To specify multiple columns in the constraint/index or to specify + an explicit name, use the :class:`.UniqueConstraint` or + :class:`.Index` constructs explicitly. + + :param system: When ``True``, indicates this is a "system" column, + that is a column which is automatically made available by the + database, and should not be included in the columns list for a + ``CREATE TABLE`` statement. + + For more elaborate scenarios where columns should be + conditionally rendered differently on different backends, + consider custom compilation rules for :class:`.CreateColumn`. + + .. versionadded:: 0.8.3 Added the ``system=True`` parameter to + :class:`.Column`. + + :param comment: Optional string that will render an SQL comment on + table creation. + + .. versionadded:: 1.2 Added the :paramref:`.Column.comment` + parameter to :class:`.Column`. + + + """ + + name = kwargs.pop('name', None) + type_ = kwargs.pop('type_', None) + args = list(args) + if args: + if isinstance(args[0], util.string_types): + if name is not None: + raise exc.ArgumentError( + "May not pass name positionally and as a keyword.") + name = args.pop(0) + if args: + coltype = args[0] + + if hasattr(coltype, "_sqla_type"): + if type_ is not None: + raise exc.ArgumentError( + "May not pass type_ positionally and as a keyword.") + type_ = args.pop(0) + + if name is not None: + name = quoted_name(name, kwargs.pop('quote', None)) + elif "quote" in kwargs: + raise exc.ArgumentError("Explicit 'name' is required when " + "sending 'quote' argument") + + super(Column, self).__init__(name, type_) + self.key = kwargs.pop('key', name) + self.primary_key = kwargs.pop('primary_key', False) + self.nullable = kwargs.pop('nullable', not self.primary_key) + self.default = kwargs.pop('default', None) + self.server_default = kwargs.pop('server_default', None) + self.server_onupdate = kwargs.pop('server_onupdate', None) + + # these default to None because .index and .unique is *not* + # an informational flag about Column - there can still be an + # Index or UniqueConstraint referring to this Column. + self.index = kwargs.pop('index', None) + self.unique = kwargs.pop('unique', None) + + self.system = kwargs.pop('system', False) + self.doc = kwargs.pop('doc', None) + self.onupdate = kwargs.pop('onupdate', None) + self.autoincrement = kwargs.pop('autoincrement', "auto") + self.constraints = set() + self.foreign_keys = set() + self.comment = kwargs.pop('comment', None) + + # check if this Column is proxying another column + if '_proxies' in kwargs: + self._proxies = kwargs.pop('_proxies') + # otherwise, add DDL-related events + elif isinstance(self.type, SchemaEventTarget): + self.type._set_parent_with_dispatch(self) + + if self.default is not None: + if isinstance(self.default, (ColumnDefault, Sequence)): + args.append(self.default) + else: + if getattr(self.type, '_warn_on_bytestring', False): + if isinstance(self.default, util.binary_type): + util.warn( + "Unicode column '%s' has non-unicode " + "default value %r specified." % ( + self.key, + self.default + )) + args.append(ColumnDefault(self.default)) + + if self.server_default is not None: + if isinstance(self.server_default, FetchedValue): + args.append(self.server_default._as_for_update(False)) + else: + args.append(DefaultClause(self.server_default)) + + if self.onupdate is not None: + if isinstance(self.onupdate, (ColumnDefault, Sequence)): + args.append(self.onupdate) + else: + args.append(ColumnDefault(self.onupdate, for_update=True)) + + if self.server_onupdate is not None: + if isinstance(self.server_onupdate, FetchedValue): + args.append(self.server_onupdate._as_for_update(True)) + else: + args.append(DefaultClause(self.server_onupdate, + for_update=True)) + self._init_items(*args) + + util.set_creation_order(self) + + if 'info' in kwargs: + self.info = kwargs.pop('info') + + if kwargs: + raise exc.ArgumentError( + "Unknown arguments passed to Column: " + repr(list(kwargs))) + +# @property +# def quote(self): +# return getattr(self.name, "quote", None) + + def __str__(self): + if self.name is None: + return "(no name)" + elif self.table is not None: + if self.table.named_with_column: + return (self.table.description + "." + self.description) + else: + return self.description + else: + return self.description + + def references(self, column): + """Return True if this Column references the given column via foreign + key.""" + + for fk in self.foreign_keys: + if fk.column.proxy_set.intersection(column.proxy_set): + return True + else: + return False + + def append_foreign_key(self, fk): + fk._set_parent_with_dispatch(self) + + def __repr__(self): + kwarg = [] + if self.key != self.name: + kwarg.append('key') + if self.primary_key: + kwarg.append('primary_key') + if not self.nullable: + kwarg.append('nullable') + if self.onupdate: + kwarg.append('onupdate') + if self.default: + kwarg.append('default') + if self.server_default: + kwarg.append('server_default') + return "Column(%s)" % ', '.join( + [repr(self.name)] + [repr(self.type)] + + [repr(x) for x in self.foreign_keys if x is not None] + + [repr(x) for x in self.constraints] + + [(self.table is not None and "table=<%s>" % + self.table.description or "table=None")] + + ["%s=%s" % (k, repr(getattr(self, k))) for k in kwarg]) + + def _set_parent(self, table): + if not self.name: + raise exc.ArgumentError( + "Column must be constructed with a non-blank name or " + "assign a non-blank .name before adding to a Table.") + if self.key is None: + self.key = self.name + + existing = getattr(self, 'table', None) + if existing is not None and existing is not table: + raise exc.ArgumentError( + "Column object '%s' already assigned to Table '%s'" % ( + self.key, + existing.description + )) + + if self.key in table._columns: + col = table._columns.get(self.key) + if col is not self: + for fk in col.foreign_keys: + table.foreign_keys.remove(fk) + if fk.constraint in table.constraints: + # this might have been removed + # already, if it's a composite constraint + # and more than one col being replaced + table.constraints.remove(fk.constraint) + + table._columns.replace(self) + + if self.primary_key: + table.primary_key._replace(self) + elif self.key in table.primary_key: + raise exc.ArgumentError( + "Trying to redefine primary-key column '%s' as a " + "non-primary-key column on table '%s'" % ( + self.key, table.fullname)) + + self.table = table + + if self.index: + if isinstance(self.index, util.string_types): + raise exc.ArgumentError( + "The 'index' keyword argument on Column is boolean only. " + "To create indexes with a specific name, create an " + "explicit Index object external to the Table.") + Index(None, self, unique=bool(self.unique), _column_flag=True) + elif self.unique: + if isinstance(self.unique, util.string_types): + raise exc.ArgumentError( + "The 'unique' keyword argument on Column is boolean " + "only. To create unique constraints or indexes with a " + "specific name, append an explicit UniqueConstraint to " + "the Table's list of elements, or create an explicit " + "Index object external to the Table.") + table.append_constraint( + UniqueConstraint(self.key, _column_flag=True)) + + self._setup_on_memoized_fks(lambda fk: fk._set_remote_table(table)) + + def _setup_on_memoized_fks(self, fn): + fk_keys = [ + ((self.table.key, self.key), False), + ((self.table.key, self.name), True), + ] + for fk_key, link_to_name in fk_keys: + if fk_key in self.table.metadata._fk_memos: + for fk in self.table.metadata._fk_memos[fk_key]: + if fk.link_to_name is link_to_name: + fn(fk) + + def _on_table_attach(self, fn): + if self.table is not None: + fn(self, self.table) + else: + event.listen(self, 'after_parent_attach', fn) + + def copy(self, **kw): + """Create a copy of this ``Column``, unitialized. + + This is used in ``Table.tometadata``. + + """ + + # Constraint objects plus non-constraint-bound ForeignKey objects + args = \ + [c.copy(**kw) for c in self.constraints if not c._type_bound] + \ + [c.copy(**kw) for c in self.foreign_keys if not c.constraint] + + type_ = self.type + if isinstance(type_, SchemaEventTarget): + type_ = type_.copy(**kw) + + c = self._constructor( + name=self.name, + type_=type_, + key=self.key, + primary_key=self.primary_key, + nullable=self.nullable, + unique=self.unique, + system=self.system, + # quote=self.quote, + index=self.index, + autoincrement=self.autoincrement, + default=self.default, + server_default=self.server_default, + onupdate=self.onupdate, + server_onupdate=self.server_onupdate, + doc=self.doc, + comment=self.comment, + *args + ) + return self._schema_item_copy(c) + + def _make_proxy(self, selectable, name=None, key=None, + name_is_truncatable=False, **kw): + """Create a *proxy* for this column. + + This is a copy of this ``Column`` referenced by a different parent + (such as an alias or select statement). The column should + be used only in select scenarios, as its full DDL/default + information is not transferred. + + """ + fk = [ForeignKey(f.column, _constraint=f.constraint) + for f in self.foreign_keys] + if name is None and self.name is None: + raise exc.InvalidRequestError( + "Cannot initialize a sub-selectable" + " with this Column object until its 'name' has " + "been assigned.") + try: + c = self._constructor( + _as_truncated(name or self.name) if + name_is_truncatable else (name or self.name), + self.type, + key=key if key else name if name else self.key, + primary_key=self.primary_key, + nullable=self.nullable, + _proxies=[self], *fk) + except TypeError: + util.raise_from_cause( + TypeError( + "Could not create a copy of this %r object. " + "Ensure the class includes a _constructor() " + "attribute or method which accepts the " + "standard Column constructor arguments, or " + "references the Column class itself." % self.__class__) + ) + + c.table = selectable + selectable._columns.add(c) + if selectable._is_clone_of is not None: + c._is_clone_of = selectable._is_clone_of.columns[c.key] + if self.primary_key: + selectable.primary_key.add(c) + c.dispatch.after_parent_attach(c, selectable) + return c + + def get_children(self, schema_visitor=False, **kwargs): + if schema_visitor: + return [x for x in (self.default, self.onupdate) + if x is not None] + \ + list(self.foreign_keys) + list(self.constraints) + else: + return ColumnClause.get_children(self, **kwargs) + + +class ForeignKey(DialectKWArgs, SchemaItem): + """Defines a dependency between two columns. + + ``ForeignKey`` is specified as an argument to a :class:`.Column` object, + e.g.:: + + t = Table("remote_table", metadata, + Column("remote_id", ForeignKey("main_table.id")) + ) + + Note that ``ForeignKey`` is only a marker object that defines + a dependency between two columns. The actual constraint + is in all cases represented by the :class:`.ForeignKeyConstraint` + object. This object will be generated automatically when + a ``ForeignKey`` is associated with a :class:`.Column` which + in turn is associated with a :class:`.Table`. Conversely, + when :class:`.ForeignKeyConstraint` is applied to a :class:`.Table`, + ``ForeignKey`` markers are automatically generated to be + present on each associated :class:`.Column`, which are also + associated with the constraint object. + + Note that you cannot define a "composite" foreign key constraint, + that is a constraint between a grouping of multiple parent/child + columns, using ``ForeignKey`` objects. To define this grouping, + the :class:`.ForeignKeyConstraint` object must be used, and applied + to the :class:`.Table`. The associated ``ForeignKey`` objects + are created automatically. + + The ``ForeignKey`` objects associated with an individual + :class:`.Column` object are available in the `foreign_keys` collection + of that column. + + Further examples of foreign key configuration are in + :ref:`metadata_foreignkeys`. + + """ + + __visit_name__ = 'foreign_key' + + def __init__(self, column, _constraint=None, use_alter=False, name=None, + onupdate=None, ondelete=None, deferrable=None, + initially=None, link_to_name=False, match=None, + info=None, + **dialect_kw): + r""" + Construct a column-level FOREIGN KEY. + + The :class:`.ForeignKey` object when constructed generates a + :class:`.ForeignKeyConstraint` which is associated with the parent + :class:`.Table` object's collection of constraints. + + :param column: A single target column for the key relationship. A + :class:`.Column` object or a column name as a string: + ``tablename.columnkey`` or ``schema.tablename.columnkey``. + ``columnkey`` is the ``key`` which has been assigned to the column + (defaults to the column name itself), unless ``link_to_name`` is + ``True`` in which case the rendered name of the column is used. + + .. versionadded:: 0.7.4 + Note that if the schema name is not included, and the + underlying :class:`.MetaData` has a "schema", that value will + be used. + + :param name: Optional string. An in-database name for the key if + `constraint` is not provided. + + :param onupdate: Optional string. If set, emit ON UPDATE <value> when + issuing DDL for this constraint. Typical values include CASCADE, + DELETE and RESTRICT. + + :param ondelete: Optional string. If set, emit ON DELETE <value> when + issuing DDL for this constraint. Typical values include CASCADE, + DELETE and RESTRICT. + + :param deferrable: Optional bool. If set, emit DEFERRABLE or NOT + DEFERRABLE when issuing DDL for this constraint. + + :param initially: Optional string. If set, emit INITIALLY <value> when + issuing DDL for this constraint. + + :param link_to_name: if True, the string name given in ``column`` is + the rendered name of the referenced column, not its locally + assigned ``key``. + + :param use_alter: passed to the underlying + :class:`.ForeignKeyConstraint` to indicate the constraint should + be generated/dropped externally from the CREATE TABLE/ DROP TABLE + statement. See :paramref:`.ForeignKeyConstraint.use_alter` + for further description. + + .. seealso:: + + :paramref:`.ForeignKeyConstraint.use_alter` + + :ref:`use_alter` + + :param match: Optional string. If set, emit MATCH <value> when issuing + DDL for this constraint. Typical values include SIMPLE, PARTIAL + and FULL. + + :param info: Optional data dictionary which will be populated into the + :attr:`.SchemaItem.info` attribute of this object. + + .. versionadded:: 1.0.0 + + :param \**dialect_kw: Additional keyword arguments are dialect + specific, and passed in the form ``<dialectname>_<argname>``. The + arguments are ultimately handled by a corresponding + :class:`.ForeignKeyConstraint`. See the documentation regarding + an individual dialect at :ref:`dialect_toplevel` for detail on + documented arguments. + + .. versionadded:: 0.9.2 + + """ + + self._colspec = column + if isinstance(self._colspec, util.string_types): + self._table_column = None + else: + if hasattr(self._colspec, '__clause_element__'): + self._table_column = self._colspec.__clause_element__() + else: + self._table_column = self._colspec + + if not isinstance(self._table_column, ColumnClause): + raise exc.ArgumentError( + "String, Column, or Column-bound argument " + "expected, got %r" % self._table_column) + elif not isinstance( + self._table_column.table, (util.NoneType, TableClause)): + raise exc.ArgumentError( + "ForeignKey received Column not bound " + "to a Table, got: %r" % self._table_column.table + ) + + # the linked ForeignKeyConstraint. + # ForeignKey will create this when parent Column + # is attached to a Table, *or* ForeignKeyConstraint + # object passes itself in when creating ForeignKey + # markers. + self.constraint = _constraint + self.parent = None + self.use_alter = use_alter + self.name = name + self.onupdate = onupdate + self.ondelete = ondelete + self.deferrable = deferrable + self.initially = initially + self.link_to_name = link_to_name + self.match = match + if info: + self.info = info + self._unvalidated_dialect_kw = dialect_kw + + def __repr__(self): + return "ForeignKey(%r)" % self._get_colspec() + + def copy(self, schema=None): + """Produce a copy of this :class:`.ForeignKey` object. + + The new :class:`.ForeignKey` will not be bound + to any :class:`.Column`. + + This method is usually used by the internal + copy procedures of :class:`.Column`, :class:`.Table`, + and :class:`.MetaData`. + + :param schema: The returned :class:`.ForeignKey` will + reference the original table and column name, qualified + by the given string schema name. + + """ + + fk = ForeignKey( + self._get_colspec(schema=schema), + use_alter=self.use_alter, + name=self.name, + onupdate=self.onupdate, + ondelete=self.ondelete, + deferrable=self.deferrable, + initially=self.initially, + link_to_name=self.link_to_name, + match=self.match, + **self._unvalidated_dialect_kw + ) + return self._schema_item_copy(fk) + + def _get_colspec(self, schema=None, table_name=None): + """Return a string based 'column specification' for this + :class:`.ForeignKey`. + + This is usually the equivalent of the string-based "tablename.colname" + argument first passed to the object's constructor. + + """ + if schema: + _schema, tname, colname = self._column_tokens + if table_name is not None: + tname = table_name + return "%s.%s.%s" % (schema, tname, colname) + elif table_name: + schema, tname, colname = self._column_tokens + if schema: + return "%s.%s.%s" % (schema, table_name, colname) + else: + return "%s.%s" % (table_name, colname) + elif self._table_column is not None: + return "%s.%s" % ( + self._table_column.table.fullname, self._table_column.key) + else: + return self._colspec + + @property + def _referred_schema(self): + return self._column_tokens[0] + + def _table_key(self): + if self._table_column is not None: + if self._table_column.table is None: + return None + else: + return self._table_column.table.key + else: + schema, tname, colname = self._column_tokens + return _get_table_key(tname, schema) + + target_fullname = property(_get_colspec) + + def references(self, table): + """Return True if the given :class:`.Table` is referenced by this + :class:`.ForeignKey`.""" + + return table.corresponding_column(self.column) is not None + + def get_referent(self, table): + """Return the :class:`.Column` in the given :class:`.Table` + referenced by this :class:`.ForeignKey`. + + Returns None if this :class:`.ForeignKey` does not reference the given + :class:`.Table`. + + """ + + return table.corresponding_column(self.column) + + @util.memoized_property + def _column_tokens(self): + """parse a string-based _colspec into its component parts.""" + + m = self._get_colspec().split('.') + if m is None: + raise exc.ArgumentError( + "Invalid foreign key column specification: %s" % + self._colspec) + if (len(m) == 1): + tname = m.pop() + colname = None + else: + colname = m.pop() + tname = m.pop() + + # A FK between column 'bar' and table 'foo' can be + # specified as 'foo', 'foo.bar', 'dbo.foo.bar', + # 'otherdb.dbo.foo.bar'. Once we have the column name and + # the table name, treat everything else as the schema + # name. Some databases (e.g. Sybase) support + # inter-database foreign keys. See tickets#1341 and -- + # indirectly related -- Ticket #594. This assumes that '.' + # will never appear *within* any component of the FK. + + if (len(m) > 0): + schema = '.'.join(m) + else: + schema = None + return schema, tname, colname + + def _resolve_col_tokens(self): + if self.parent is None: + raise exc.InvalidRequestError( + "this ForeignKey object does not yet have a " + "parent Column associated with it.") + + elif self.parent.table is None: + raise exc.InvalidRequestError( + "this ForeignKey's parent column is not yet associated " + "with a Table.") + + parenttable = self.parent.table + + # assertion, can be commented out. + # basically Column._make_proxy() sends the actual + # target Column to the ForeignKey object, so the + # string resolution here is never called. + for c in self.parent.base_columns: + if isinstance(c, Column): + assert c.table is parenttable + break + else: + assert False + ###################### + + schema, tname, colname = self._column_tokens + + if schema is None and parenttable.metadata.schema is not None: + schema = parenttable.metadata.schema + + tablekey = _get_table_key(tname, schema) + return parenttable, tablekey, colname + + def _link_to_col_by_colstring(self, parenttable, table, colname): + if not hasattr(self.constraint, '_referred_table'): + self.constraint._referred_table = table + else: + assert self.constraint._referred_table is table + + _column = None + if colname is None: + # colname is None in the case that ForeignKey argument + # was specified as table name only, in which case we + # match the column name to the same column on the + # parent. + key = self.parent + _column = table.c.get(self.parent.key, None) + elif self.link_to_name: + key = colname + for c in table.c: + if c.name == colname: + _column = c + else: + key = colname + _column = table.c.get(colname, None) + + if _column is None: + raise exc.NoReferencedColumnError( + "Could not initialize target column " + "for ForeignKey '%s' on table '%s': " + "table '%s' has no column named '%s'" % + (self._colspec, parenttable.name, table.name, key), + table.name, key) + + self._set_target_column(_column) + + def _set_target_column(self, column): + # propagate TypeEngine to parent if it didn't have one + if self.parent.type._isnull: + self.parent.type = column.type + + # super-edgy case, if other FKs point to our column, + # they'd get the type propagated out also. + if isinstance(self.parent.table, Table): + + def set_type(fk): + if fk.parent.type._isnull: + fk.parent.type = column.type + self.parent._setup_on_memoized_fks(set_type) + + self.column = column + + @util.memoized_property + def column(self): + """Return the target :class:`.Column` referenced by this + :class:`.ForeignKey`. + + If no target column has been established, an exception + is raised. + + .. versionchanged:: 0.9.0 + Foreign key target column resolution now occurs as soon as both + the ForeignKey object and the remote Column to which it refers + are both associated with the same MetaData object. + + """ + + if isinstance(self._colspec, util.string_types): + + parenttable, tablekey, colname = self._resolve_col_tokens() + + if tablekey not in parenttable.metadata: + raise exc.NoReferencedTableError( + "Foreign key associated with column '%s' could not find " + "table '%s' with which to generate a " + "foreign key to target column '%s'" % + (self.parent, tablekey, colname), + tablekey) + elif parenttable.key not in parenttable.metadata: + raise exc.InvalidRequestError( + "Table %s is no longer associated with its " + "parent MetaData" % parenttable) + else: + raise exc.NoReferencedColumnError( + "Could not initialize target column for " + "ForeignKey '%s' on table '%s': " + "table '%s' has no column named '%s'" % ( + self._colspec, parenttable.name, tablekey, colname), + tablekey, colname) + elif hasattr(self._colspec, '__clause_element__'): + _column = self._colspec.__clause_element__() + return _column + else: + _column = self._colspec + return _column + + def _set_parent(self, column): + if self.parent is not None and self.parent is not column: + raise exc.InvalidRequestError( + "This ForeignKey already has a parent !") + self.parent = column + self.parent.foreign_keys.add(self) + self.parent._on_table_attach(self._set_table) + + def _set_remote_table(self, table): + parenttable, tablekey, colname = self._resolve_col_tokens() + self._link_to_col_by_colstring(parenttable, table, colname) + self.constraint._validate_dest_table(table) + + def _remove_from_metadata(self, metadata): + parenttable, table_key, colname = self._resolve_col_tokens() + fk_key = (table_key, colname) + + if self in metadata._fk_memos[fk_key]: + # TODO: no test coverage for self not in memos + metadata._fk_memos[fk_key].remove(self) + + def _set_table(self, column, table): + # standalone ForeignKey - create ForeignKeyConstraint + # on the hosting Table when attached to the Table. + if self.constraint is None and isinstance(table, Table): + self.constraint = ForeignKeyConstraint( + [], [], use_alter=self.use_alter, name=self.name, + onupdate=self.onupdate, ondelete=self.ondelete, + deferrable=self.deferrable, initially=self.initially, + match=self.match, + **self._unvalidated_dialect_kw + ) + self.constraint._append_element(column, self) + self.constraint._set_parent_with_dispatch(table) + table.foreign_keys.add(self) + + # set up remote ".column" attribute, or a note to pick it + # up when the other Table/Column shows up + if isinstance(self._colspec, util.string_types): + parenttable, table_key, colname = self._resolve_col_tokens() + fk_key = (table_key, colname) + if table_key in parenttable.metadata.tables: + table = parenttable.metadata.tables[table_key] + try: + self._link_to_col_by_colstring( + parenttable, table, colname) + except exc.NoReferencedColumnError: + # this is OK, we'll try later + pass + parenttable.metadata._fk_memos[fk_key].append(self) + elif hasattr(self._colspec, '__clause_element__'): + _column = self._colspec.__clause_element__() + self._set_target_column(_column) + else: + _column = self._colspec + self._set_target_column(_column) + + +class _NotAColumnExpr(object): + def _not_a_column_expr(self): + raise exc.InvalidRequestError( + "This %s cannot be used directly " + "as a column expression." % self.__class__.__name__) + + __clause_element__ = self_group = lambda self: self._not_a_column_expr() + _from_objects = property(lambda self: self._not_a_column_expr()) + + +class DefaultGenerator(_NotAColumnExpr, SchemaItem): + """Base class for column *default* values.""" + + __visit_name__ = 'default_generator' + + is_sequence = False + is_server_default = False + column = None + + def __init__(self, for_update=False): + self.for_update = for_update + + def _set_parent(self, column): + self.column = column + if self.for_update: + self.column.onupdate = self + else: + self.column.default = self + + def execute(self, bind=None, **kwargs): + if bind is None: + bind = _bind_or_error(self) + return bind._execute_default(self, **kwargs) + + def _execute_on_connection(self, connection, multiparams, params): + return connection._execute_default(self, multiparams, params) + + @property + def bind(self): + """Return the connectable associated with this default.""" + if getattr(self, 'column', None) is not None: + return self.column.table.bind + else: + return None + + +class ColumnDefault(DefaultGenerator): + """A plain default value on a column. + + This could correspond to a constant, a callable function, + or a SQL clause. + + :class:`.ColumnDefault` is generated automatically + whenever the ``default``, ``onupdate`` arguments of + :class:`.Column` are used. A :class:`.ColumnDefault` + can be passed positionally as well. + + For example, the following:: + + Column('foo', Integer, default=50) + + Is equivalent to:: + + Column('foo', Integer, ColumnDefault(50)) + + + """ + + def __init__(self, arg, **kwargs): + """"Construct a new :class:`.ColumnDefault`. + + + :param arg: argument representing the default value. + May be one of the following: + + * a plain non-callable Python value, such as a + string, integer, boolean, or other simple type. + The default value will be used as is each time. + * a SQL expression, that is one which derives from + :class:`.ColumnElement`. The SQL expression will + be rendered into the INSERT or UPDATE statement, + or in the case of a primary key column when + RETURNING is not used may be + pre-executed before an INSERT within a SELECT. + * A Python callable. The function will be invoked for each + new row subject to an INSERT or UPDATE. + The callable must accept exactly + zero or one positional arguments. The one-argument form + will receive an instance of the :class:`.ExecutionContext`, + which provides contextual information as to the current + :class:`.Connection` in use as well as the current + statement and parameters. + + """ + super(ColumnDefault, self).__init__(**kwargs) + if isinstance(arg, FetchedValue): + raise exc.ArgumentError( + "ColumnDefault may not be a server-side default type.") + if util.callable(arg): + arg = self._maybe_wrap_callable(arg) + self.arg = arg + + @util.memoized_property + def is_callable(self): + return util.callable(self.arg) + + @util.memoized_property + def is_clause_element(self): + return isinstance(self.arg, ClauseElement) + + @util.memoized_property + def is_scalar(self): + return not self.is_callable and \ + not self.is_clause_element and \ + not self.is_sequence + + @util.memoized_property + @util.dependencies("sqlalchemy.sql.sqltypes") + def _arg_is_typed(self, sqltypes): + if self.is_clause_element: + return not isinstance(self.arg.type, sqltypes.NullType) + else: + return False + + def _maybe_wrap_callable(self, fn): + """Wrap callables that don't accept a context. + + This is to allow easy compatibility with default callables + that aren't specific to accepting of a context. + + """ + try: + argspec = util.get_callable_argspec(fn, no_self=True) + except TypeError: + return util.wrap_callable(lambda ctx: fn(), fn) + + defaulted = argspec[3] is not None and len(argspec[3]) or 0 + positionals = len(argspec[0]) - defaulted + + if positionals == 0: + return util.wrap_callable(lambda ctx: fn(), fn) + + elif positionals == 1: + return fn + else: + raise exc.ArgumentError( + "ColumnDefault Python function takes zero or one " + "positional arguments") + + def _visit_name(self): + if self.for_update: + return "column_onupdate" + else: + return "column_default" + __visit_name__ = property(_visit_name) + + def __repr__(self): + return "ColumnDefault(%r)" % (self.arg, ) + + +class Sequence(DefaultGenerator): + """Represents a named database sequence. + + The :class:`.Sequence` object represents the name and configurational + parameters of a database sequence. It also represents + a construct that can be "executed" by a SQLAlchemy :class:`.Engine` + or :class:`.Connection`, rendering the appropriate "next value" function + for the target database and returning a result. + + The :class:`.Sequence` is typically associated with a primary key column:: + + some_table = Table( + 'some_table', metadata, + Column('id', Integer, Sequence('some_table_seq'), + primary_key=True) + ) + + When CREATE TABLE is emitted for the above :class:`.Table`, if the + target platform supports sequences, a CREATE SEQUENCE statement will + be emitted as well. For platforms that don't support sequences, + the :class:`.Sequence` construct is ignored. + + .. seealso:: + + :class:`.CreateSequence` + + :class:`.DropSequence` + + """ + + __visit_name__ = 'sequence' + + is_sequence = True + + def __init__(self, name, start=None, increment=None, minvalue=None, + maxvalue=None, nominvalue=None, nomaxvalue=None, cycle=None, + schema=None, cache=None, order=None, optional=False, + quote=None, metadata=None, quote_schema=None, + for_update=False): + """Construct a :class:`.Sequence` object. + + :param name: The name of the sequence. + :param start: the starting index of the sequence. This value is + used when the CREATE SEQUENCE command is emitted to the database + as the value of the "START WITH" clause. If ``None``, the + clause is omitted, which on most platforms indicates a starting + value of 1. + :param increment: the increment value of the sequence. This + value is used when the CREATE SEQUENCE command is emitted to + the database as the value of the "INCREMENT BY" clause. If ``None``, + the clause is omitted, which on most platforms indicates an + increment of 1. + :param minvalue: the minimum value of the sequence. This + value is used when the CREATE SEQUENCE command is emitted to + the database as the value of the "MINVALUE" clause. If ``None``, + the clause is omitted, which on most platforms indicates a + minvalue of 1 and -2^63-1 for ascending and descending sequences, + respectively. + + .. versionadded:: 1.0.7 + + :param maxvalue: the maximum value of the sequence. This + value is used when the CREATE SEQUENCE command is emitted to + the database as the value of the "MAXVALUE" clause. If ``None``, + the clause is omitted, which on most platforms indicates a + maxvalue of 2^63-1 and -1 for ascending and descending sequences, + respectively. + + .. versionadded:: 1.0.7 + + :param nominvalue: no minimum value of the sequence. This + value is used when the CREATE SEQUENCE command is emitted to + the database as the value of the "NO MINVALUE" clause. If ``None``, + the clause is omitted, which on most platforms indicates a + minvalue of 1 and -2^63-1 for ascending and descending sequences, + respectively. + + .. versionadded:: 1.0.7 + + :param nomaxvalue: no maximum value of the sequence. This + value is used when the CREATE SEQUENCE command is emitted to + the database as the value of the "NO MAXVALUE" clause. If ``None``, + the clause is omitted, which on most platforms indicates a + maxvalue of 2^63-1 and -1 for ascending and descending sequences, + respectively. + + .. versionadded:: 1.0.7 + + :param cycle: allows the sequence to wrap around when the maxvalue + or minvalue has been reached by an ascending or descending sequence + respectively. This value is used when the CREATE SEQUENCE command + is emitted to the database as the "CYCLE" clause. If the limit is + reached, the next number generated will be the minvalue or maxvalue, + respectively. If cycle=False (the default) any calls to nextval + after the sequence has reached its maximum value will return an + error. + + .. versionadded:: 1.0.7 + + :param schema: Optional schema name for the sequence, if located + in a schema other than the default. The rules for selecting the + schema name when a :class:`.MetaData` is also present are the same + as that of :paramref:`.Table.schema`. + + :param cache: optional integer value; number of future values in the + sequence which are calculated in advance. Renders the CACHE keyword + understood by Oracle and PostgreSQL. + + .. versionadded:: 1.1.12 + + :param order: optional boolean value; if true, renders the + ORDER keyword, understood by Oracle, indicating the sequence is + definitively ordered. May be necessary to provide deterministic + ordering using Oracle RAC. + + .. versionadded:: 1.1.12 + + :param optional: boolean value, when ``True``, indicates that this + :class:`.Sequence` object only needs to be explicitly generated + on backends that don't provide another way to generate primary + key identifiers. Currently, it essentially means, "don't create + this sequence on the PostgreSQL backend, where the SERIAL keyword + creates a sequence for us automatically". + :param quote: boolean value, when ``True`` or ``False``, explicitly + forces quoting of the schema name on or off. When left at its + default of ``None``, normal quoting rules based on casing and + reserved words take place. + :param quote_schema: set the quoting preferences for the ``schema`` + name. + + :param metadata: optional :class:`.MetaData` object which this + :class:`.Sequence` will be associated with. A :class:`.Sequence` + that is associated with a :class:`.MetaData` gains the following + capabilities: + + * The :class:`.Sequence` will inherit the :paramref:`.MetaData.schema` + parameter specified to the target :class:`.MetaData`, which + affects the production of CREATE / DROP DDL, if any. + + * The :meth:`.Sequence.create` and :meth:`.Sequence.drop` methods + automatically use the engine bound to the :class:`.MetaData` + object, if any. + + * The :meth:`.MetaData.create_all` and :meth:`.MetaData.drop_all` + methods will emit CREATE / DROP for this :class:`.Sequence`, + even if the :class:`.Sequence` is not associated with any + :class:`.Table` / :class:`.Column` that's a member of this + :class:`.MetaData`. + + The above behaviors can only occur if the :class:`.Sequence` is + explicitly associated with the :class:`.MetaData` via this parameter. + + .. seealso:: + + :ref:`sequence_metadata` - full discussion of the + :paramref:`.Sequence.metadata` parameter. + + :param for_update: Indicates this :class:`.Sequence`, when associated + with a :class:`.Column`, should be invoked for UPDATE statements + on that column's table, rather than for INSERT statements, when + no value is otherwise present for that column in the statement. + + """ + super(Sequence, self).__init__(for_update=for_update) + self.name = quoted_name(name, quote) + self.start = start + self.increment = increment + self.minvalue = minvalue + self.maxvalue = maxvalue + self.nominvalue = nominvalue + self.nomaxvalue = nomaxvalue + self.cycle = cycle + self.cache = cache + self.order = order + self.optional = optional + if schema is BLANK_SCHEMA: + self.schema = schema = None + elif metadata is not None and schema is None and metadata.schema: + self.schema = schema = metadata.schema + else: + self.schema = quoted_name(schema, quote_schema) + self.metadata = metadata + self._key = _get_table_key(name, schema) + if metadata: + self._set_metadata(metadata) + + @util.memoized_property + def is_callable(self): + return False + + @util.memoized_property + def is_clause_element(self): + return False + + @util.dependencies("sqlalchemy.sql.functions.func") + def next_value(self, func): + """Return a :class:`.next_value` function element + which will render the appropriate increment function + for this :class:`.Sequence` within any SQL expression. + + """ + return func.next_value(self, bind=self.bind) + + def _set_parent(self, column): + super(Sequence, self)._set_parent(column) + column._on_table_attach(self._set_table) + + def _set_table(self, column, table): + self._set_metadata(table.metadata) + + def _set_metadata(self, metadata): + self.metadata = metadata + self.metadata._sequences[self._key] = self + + @property + def bind(self): + if self.metadata: + return self.metadata.bind + else: + return None + + def create(self, bind=None, checkfirst=True): + """Creates this sequence in the database.""" + + if bind is None: + bind = _bind_or_error(self) + bind._run_visitor(ddl.SchemaGenerator, + self, + checkfirst=checkfirst) + + def drop(self, bind=None, checkfirst=True): + """Drops this sequence from the database.""" + + if bind is None: + bind = _bind_or_error(self) + bind._run_visitor(ddl.SchemaDropper, + self, + checkfirst=checkfirst) + + def _not_a_column_expr(self): + raise exc.InvalidRequestError( + "This %s cannot be used directly " + "as a column expression. Use func.next_value(sequence) " + "to produce a 'next value' function that's usable " + "as a column element." + % self.__class__.__name__) + + + +@inspection._self_inspects +class FetchedValue(_NotAColumnExpr, SchemaEventTarget): + """A marker for a transparent database-side default. + + Use :class:`.FetchedValue` when the database is configured + to provide some automatic default for a column. + + E.g.:: + + Column('foo', Integer, FetchedValue()) + + Would indicate that some trigger or default generator + will create a new value for the ``foo`` column during an + INSERT. + + .. seealso:: + + :ref:`triggered_columns` + + """ + is_server_default = True + reflected = False + has_argument = False + + def __init__(self, for_update=False): + self.for_update = for_update + + def _as_for_update(self, for_update): + if for_update == self.for_update: + return self + else: + return self._clone(for_update) + + def _clone(self, for_update): + n = self.__class__.__new__(self.__class__) + n.__dict__.update(self.__dict__) + n.__dict__.pop('column', None) + n.for_update = for_update + return n + + def _set_parent(self, column): + self.column = column + if self.for_update: + self.column.server_onupdate = self + else: + self.column.server_default = self + + def __repr__(self): + return util.generic_repr(self) + + +class DefaultClause(FetchedValue): + """A DDL-specified DEFAULT column value. + + :class:`.DefaultClause` is a :class:`.FetchedValue` + that also generates a "DEFAULT" clause when + "CREATE TABLE" is emitted. + + :class:`.DefaultClause` is generated automatically + whenever the ``server_default``, ``server_onupdate`` arguments of + :class:`.Column` are used. A :class:`.DefaultClause` + can be passed positionally as well. + + For example, the following:: + + Column('foo', Integer, server_default="50") + + Is equivalent to:: + + Column('foo', Integer, DefaultClause("50")) + + """ + + has_argument = True + + def __init__(self, arg, for_update=False, _reflected=False): + util.assert_arg_type(arg, (util.string_types[0], + ClauseElement, + TextClause), 'arg') + super(DefaultClause, self).__init__(for_update) + self.arg = arg + self.reflected = _reflected + + def __repr__(self): + return "DefaultClause(%r, for_update=%r)" % \ + (self.arg, self.for_update) + + +class PassiveDefault(DefaultClause): + """A DDL-specified DEFAULT column value. + + .. deprecated:: 0.6 + :class:`.PassiveDefault` is deprecated. + Use :class:`.DefaultClause`. + """ + @util.deprecated("0.6", + ":class:`.PassiveDefault` is deprecated. " + "Use :class:`.DefaultClause`.", + False) + def __init__(self, *arg, **kw): + DefaultClause.__init__(self, *arg, **kw) + + +class Constraint(DialectKWArgs, SchemaItem): + """A table-level SQL constraint.""" + + __visit_name__ = 'constraint' + + def __init__(self, name=None, deferrable=None, initially=None, + _create_rule=None, info=None, _type_bound=False, + **dialect_kw): + r"""Create a SQL constraint. + + :param name: + Optional, the in-database name of this ``Constraint``. + + :param deferrable: + Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when + issuing DDL for this constraint. + + :param initially: + Optional string. If set, emit INITIALLY <value> when issuing DDL + for this constraint. + + :param info: Optional data dictionary which will be populated into the + :attr:`.SchemaItem.info` attribute of this object. + + .. versionadded:: 1.0.0 + + :param _create_rule: + a callable which is passed the DDLCompiler object during + compilation. Returns True or False to signal inline generation of + this Constraint. + + The AddConstraint and DropConstraint DDL constructs provide + DDLElement's more comprehensive "conditional DDL" approach that is + passed a database connection when DDL is being issued. _create_rule + is instead called during any CREATE TABLE compilation, where there + may not be any transaction/connection in progress. However, it + allows conditional compilation of the constraint even for backends + which do not support addition of constraints through ALTER TABLE, + which currently includes SQLite. + + _create_rule is used by some types to create constraints. + Currently, its call signature is subject to change at any time. + + :param \**dialect_kw: Additional keyword arguments are dialect + specific, and passed in the form ``<dialectname>_<argname>``. See + the documentation regarding an individual dialect at + :ref:`dialect_toplevel` for detail on documented arguments. + + """ + + self.name = name + self.deferrable = deferrable + self.initially = initially + if info: + self.info = info + self._create_rule = _create_rule + self._type_bound = _type_bound + util.set_creation_order(self) + self._validate_dialect_kwargs(dialect_kw) + + @property + def table(self): + try: + if isinstance(self.parent, Table): + return self.parent + except AttributeError: + pass + raise exc.InvalidRequestError( + "This constraint is not bound to a table. Did you " + "mean to call table.append_constraint(constraint) ?") + + def _set_parent(self, parent): + self.parent = parent + parent.constraints.add(self) + + def copy(self, **kw): + raise NotImplementedError() + + +def _to_schema_column(element): + if hasattr(element, '__clause_element__'): + element = element.__clause_element__() + if not isinstance(element, Column): + raise exc.ArgumentError("schema.Column object expected") + return element + + +def _to_schema_column_or_string(element): + if hasattr(element, '__clause_element__'): + element = element.__clause_element__() + if not isinstance(element, util.string_types + (ColumnElement, )): + msg = "Element %r is not a string name or column element" + raise exc.ArgumentError(msg % element) + return element + + +class ColumnCollectionMixin(object): + + columns = None + """A :class:`.ColumnCollection` of :class:`.Column` objects. + + This collection represents the columns which are referred to by + this object. + + """ + + _allow_multiple_tables = False + + def __init__(self, *columns, **kw): + _autoattach = kw.pop('_autoattach', True) + self._column_flag = kw.pop('_column_flag', False) + self.columns = ColumnCollection() + self._pending_colargs = [_to_schema_column_or_string(c) + for c in columns] + if _autoattach and self._pending_colargs: + self._check_attach() + + @classmethod + def _extract_col_expression_collection(cls, expressions): + for expr in expressions: + strname = None + column = None + if hasattr(expr, '__clause_element__'): + expr = expr.__clause_element__() + + if not isinstance(expr, (ColumnElement, TextClause)): + # this assumes a string + strname = expr + else: + cols = [] + visitors.traverse(expr, {}, {'column': cols.append}) + if cols: + column = cols[0] + add_element = column if column is not None else strname + yield expr, column, strname, add_element + + def _check_attach(self, evt=False): + col_objs = [ + c for c in self._pending_colargs + if isinstance(c, Column) + ] + + cols_w_table = [ + c for c in col_objs if isinstance(c.table, Table) + ] + + cols_wo_table = set(col_objs).difference(cols_w_table) + + if cols_wo_table: + # feature #3341 - place event listeners for Column objects + # such that when all those cols are attached, we autoattach. + assert not evt, "Should not reach here on event call" + + # issue #3411 - don't do the per-column auto-attach if some of the + # columns are specified as strings. + has_string_cols = set(self._pending_colargs).difference(col_objs) + if not has_string_cols: + def _col_attached(column, table): + # this isinstance() corresponds with the + # isinstance() above; only want to count Table-bound + # columns + if isinstance(table, Table): + cols_wo_table.discard(column) + if not cols_wo_table: + self._check_attach(evt=True) + self._cols_wo_table = cols_wo_table + for col in cols_wo_table: + col._on_table_attach(_col_attached) + return + + columns = cols_w_table + + tables = {c.table for c in columns} + if len(tables) == 1: + self._set_parent_with_dispatch(tables.pop()) + elif len(tables) > 1 and not self._allow_multiple_tables: + table = columns[0].table + others = [c for c in columns[1:] if c.table is not table] + if others: + raise exc.ArgumentError( + "Column(s) %s are not part of table '%s'." % + (", ".join("'%s'" % c for c in others), + table.description) + ) + + def _set_parent(self, table): + for col in self._pending_colargs: + if isinstance(col, util.string_types): + col = table.c[col] + self.columns.add(col) + + +class ColumnCollectionConstraint(ColumnCollectionMixin, Constraint): + """A constraint that proxies a ColumnCollection.""" + + def __init__(self, *columns, **kw): + r""" + :param \*columns: + A sequence of column names or Column objects. + + :param name: + Optional, the in-database name of this constraint. + + :param deferrable: + Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when + issuing DDL for this constraint. + + :param initially: + Optional string. If set, emit INITIALLY <value> when issuing DDL + for this constraint. + + :param \**kw: other keyword arguments including dialect-specific + arguments are propagated to the :class:`.Constraint` superclass. + + """ + _autoattach = kw.pop('_autoattach', True) + _column_flag = kw.pop('_column_flag', False) + Constraint.__init__(self, **kw) + ColumnCollectionMixin.__init__( + self, *columns, _autoattach=_autoattach, _column_flag=_column_flag) + + columns = None + """A :class:`.ColumnCollection` representing the set of columns + for this constraint. + + """ + + def _set_parent(self, table): + Constraint._set_parent(self, table) + ColumnCollectionMixin._set_parent(self, table) + + def __contains__(self, x): + return x in self.columns + + def copy(self, **kw): + c = self.__class__(name=self.name, deferrable=self.deferrable, + initially=self.initially, *self.columns.keys()) + return self._schema_item_copy(c) + + def contains_column(self, col): + """Return True if this constraint contains the given column. + + Note that this object also contains an attribute ``.columns`` + which is a :class:`.ColumnCollection` of :class:`.Column` objects. + + """ + + return self.columns.contains_column(col) + + def __iter__(self): + # inlining of + # return iter(self.columns) + # ColumnCollection->OrderedProperties->OrderedDict + ordered_dict = self.columns._data + return (ordered_dict[key] for key in ordered_dict._list) + + def __len__(self): + return len(self.columns._data) + + +class CheckConstraint(ColumnCollectionConstraint): + """A table- or column-level CHECK constraint. + + Can be included in the definition of a Table or Column. + """ + + _allow_multiple_tables = True + + def __init__(self, sqltext, name=None, deferrable=None, + initially=None, table=None, info=None, _create_rule=None, + _autoattach=True, _type_bound=False): + r"""Construct a CHECK constraint. + + :param sqltext: + A string containing the constraint definition, which will be used + verbatim, or a SQL expression construct. If given as a string, + the object is converted to a :class:`.Text` object. If the textual + string includes a colon character, escape this using a backslash:: + + CheckConstraint(r"foo ~ E'a(?\:b|c)d") + + :param name: + Optional, the in-database name of the constraint. + + :param deferrable: + Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when + issuing DDL for this constraint. + + :param initially: + Optional string. If set, emit INITIALLY <value> when issuing DDL + for this constraint. + + :param info: Optional data dictionary which will be populated into the + :attr:`.SchemaItem.info` attribute of this object. + + .. versionadded:: 1.0.0 + + """ + + self.sqltext = _literal_as_text(sqltext, warn=False) + + columns = [] + visitors.traverse(self.sqltext, {}, {'column': columns.append}) + + super(CheckConstraint, self).\ + __init__( + name=name, deferrable=deferrable, + initially=initially, _create_rule=_create_rule, info=info, + _type_bound=_type_bound, _autoattach=_autoattach, + *columns) + if table is not None: + self._set_parent_with_dispatch(table) + + def __visit_name__(self): + if isinstance(self.parent, Table): + return "check_constraint" + else: + return "column_check_constraint" + __visit_name__ = property(__visit_name__) + + def copy(self, target_table=None, **kw): + if target_table is not None: + sqltext = _copy_expression( + self.sqltext, self.table, target_table) + else: + sqltext = self.sqltext + c = CheckConstraint(sqltext, + name=self.name, + initially=self.initially, + deferrable=self.deferrable, + _create_rule=self._create_rule, + table=target_table, + _autoattach=False, + _type_bound=self._type_bound) + return self._schema_item_copy(c) + + +class ForeignKeyConstraint(ColumnCollectionConstraint): + """A table-level FOREIGN KEY constraint. + + Defines a single column or composite FOREIGN KEY ... REFERENCES + constraint. For a no-frills, single column foreign key, adding a + :class:`.ForeignKey` to the definition of a :class:`.Column` is a + shorthand equivalent for an unnamed, single column + :class:`.ForeignKeyConstraint`. + + Examples of foreign key configuration are in :ref:`metadata_foreignkeys`. + + """ + __visit_name__ = 'foreign_key_constraint' + + def __init__(self, columns, refcolumns, name=None, onupdate=None, + ondelete=None, deferrable=None, initially=None, + use_alter=False, link_to_name=False, match=None, + table=None, info=None, **dialect_kw): + r"""Construct a composite-capable FOREIGN KEY. + + :param columns: A sequence of local column names. The named columns + must be defined and present in the parent Table. The names should + match the ``key`` given to each column (defaults to the name) unless + ``link_to_name`` is True. + + :param refcolumns: A sequence of foreign column names or Column + objects. The columns must all be located within the same Table. + + :param name: Optional, the in-database name of the key. + + :param onupdate: Optional string. If set, emit ON UPDATE <value> when + issuing DDL for this constraint. Typical values include CASCADE, + DELETE and RESTRICT. + + :param ondelete: Optional string. If set, emit ON DELETE <value> when + issuing DDL for this constraint. Typical values include CASCADE, + DELETE and RESTRICT. + + :param deferrable: Optional bool. If set, emit DEFERRABLE or NOT + DEFERRABLE when issuing DDL for this constraint. + + :param initially: Optional string. If set, emit INITIALLY <value> when + issuing DDL for this constraint. + + :param link_to_name: if True, the string name given in ``column`` is + the rendered name of the referenced column, not its locally assigned + ``key``. + + :param use_alter: If True, do not emit the DDL for this constraint as + part of the CREATE TABLE definition. Instead, generate it via an + ALTER TABLE statement issued after the full collection of tables + have been created, and drop it via an ALTER TABLE statement before + the full collection of tables are dropped. + + The use of :paramref:`.ForeignKeyConstraint.use_alter` is + particularly geared towards the case where two or more tables + are established within a mutually-dependent foreign key constraint + relationship; however, the :meth:`.MetaData.create_all` and + :meth:`.MetaData.drop_all` methods will perform this resolution + automatically, so the flag is normally not needed. + + .. versionchanged:: 1.0.0 Automatic resolution of foreign key + cycles has been added, removing the need to use the + :paramref:`.ForeignKeyConstraint.use_alter` in typical use + cases. + + .. seealso:: + + :ref:`use_alter` + + :param match: Optional string. If set, emit MATCH <value> when issuing + DDL for this constraint. Typical values include SIMPLE, PARTIAL + and FULL. + + :param info: Optional data dictionary which will be populated into the + :attr:`.SchemaItem.info` attribute of this object. + + .. versionadded:: 1.0.0 + + :param \**dialect_kw: Additional keyword arguments are dialect + specific, and passed in the form ``<dialectname>_<argname>``. See + the documentation regarding an individual dialect at + :ref:`dialect_toplevel` for detail on documented arguments. + + .. versionadded:: 0.9.2 + + """ + + Constraint.__init__( + self, name=name, deferrable=deferrable, initially=initially, + info=info, **dialect_kw) + self.onupdate = onupdate + self.ondelete = ondelete + self.link_to_name = link_to_name + self.use_alter = use_alter + self.match = match + + if len(set(columns)) != len(refcolumns): + if len(set(columns)) != len(columns): + # e.g. FOREIGN KEY (a, a) REFERENCES r (b, c) + raise exc.ArgumentError( + "ForeignKeyConstraint with duplicate source column " + "references are not supported." + ) + else: + # e.g. FOREIGN KEY (a) REFERENCES r (b, c) + # paraphrasing https://www.postgresql.org/docs/9.2/static/\ + # ddl-constraints.html + raise exc.ArgumentError( + "ForeignKeyConstraint number " + "of constrained columns must match the number of " + "referenced columns.") + + # standalone ForeignKeyConstraint - create + # associated ForeignKey objects which will be applied to hosted + # Column objects (in col.foreign_keys), either now or when attached + # to the Table for string-specified names + self.elements = [ + ForeignKey( + refcol, + _constraint=self, + name=self.name, + onupdate=self.onupdate, + ondelete=self.ondelete, + use_alter=self.use_alter, + link_to_name=self.link_to_name, + match=self.match, + deferrable=self.deferrable, + initially=self.initially, + **self.dialect_kwargs + ) for refcol in refcolumns + ] + + ColumnCollectionMixin.__init__(self, *columns) + if table is not None: + if hasattr(self, "parent"): + assert table is self.parent + self._set_parent_with_dispatch(table) + + def _append_element(self, column, fk): + self.columns.add(column) + self.elements.append(fk) + + columns = None + """A :class:`.ColumnCollection` representing the set of columns + for this constraint. + + """ + + elements = None + """A sequence of :class:`.ForeignKey` objects. + + Each :class:`.ForeignKey` represents a single referring column/referred + column pair. + + This collection is intended to be read-only. + + """ + + @property + def _elements(self): + # legacy - provide a dictionary view of (column_key, fk) + return util.OrderedDict( + zip(self.column_keys, self.elements) + ) + + @property + def _referred_schema(self): + for elem in self.elements: + return elem._referred_schema + else: + return None + + @property + def referred_table(self): + """The :class:`.Table` object to which this + :class:`.ForeignKeyConstraint` references. + + This is a dynamically calculated attribute which may not be available + if the constraint and/or parent table is not yet associated with + a metadata collection that contains the referred table. + + .. versionadded:: 1.0.0 + + """ + return self.elements[0].column.table + + def _validate_dest_table(self, table): + table_keys = set([elem._table_key() + for elem in self.elements]) + if None not in table_keys and len(table_keys) > 1: + elem0, elem1 = sorted(table_keys)[0:2] + raise exc.ArgumentError( + 'ForeignKeyConstraint on %s(%s) refers to ' + 'multiple remote tables: %s and %s' % ( + table.fullname, + self._col_description, + elem0, + elem1 + )) + + @property + def column_keys(self): + """Return a list of string keys representing the local + columns in this :class:`.ForeignKeyConstraint`. + + This list is either the original string arguments sent + to the constructor of the :class:`.ForeignKeyConstraint`, + or if the constraint has been initialized with :class:`.Column` + objects, is the string .key of each element. + + .. versionadded:: 1.0.0 + + """ + if hasattr(self, "parent"): + return self.columns.keys() + else: + return [ + col.key if isinstance(col, ColumnElement) + else str(col) for col in self._pending_colargs + ] + + @property + def _col_description(self): + return ", ".join(self.column_keys) + + def _set_parent(self, table): + Constraint._set_parent(self, table) + + try: + ColumnCollectionConstraint._set_parent(self, table) + except KeyError as ke: + raise exc.ArgumentError( + "Can't create ForeignKeyConstraint " + "on table '%s': no column " + "named '%s' is present." % (table.description, ke.args[0])) + + for col, fk in zip(self.columns, self.elements): + if not hasattr(fk, 'parent') or \ + fk.parent is not col: + fk._set_parent_with_dispatch(col) + + self._validate_dest_table(table) + + def copy(self, schema=None, target_table=None, **kw): + fkc = ForeignKeyConstraint( + [x.parent.key for x in self.elements], + [x._get_colspec( + schema=schema, + table_name=target_table.name + if target_table is not None + and x._table_key() == x.parent.table.key + else None) + for x in self.elements], + name=self.name, + onupdate=self.onupdate, + ondelete=self.ondelete, + use_alter=self.use_alter, + deferrable=self.deferrable, + initially=self.initially, + link_to_name=self.link_to_name, + match=self.match + ) + for self_fk, other_fk in zip( + self.elements, + fkc.elements): + self_fk._schema_item_copy(other_fk) + return self._schema_item_copy(fkc) + + +class PrimaryKeyConstraint(ColumnCollectionConstraint): + """A table-level PRIMARY KEY constraint. + + The :class:`.PrimaryKeyConstraint` object is present automatically + on any :class:`.Table` object; it is assigned a set of + :class:`.Column` objects corresponding to those marked with + the :paramref:`.Column.primary_key` flag:: + + >>> my_table = Table('mytable', metadata, + ... Column('id', Integer, primary_key=True), + ... Column('version_id', Integer, primary_key=True), + ... Column('data', String(50)) + ... ) + >>> my_table.primary_key + PrimaryKeyConstraint( + Column('id', Integer(), table=<mytable>, + primary_key=True, nullable=False), + Column('version_id', Integer(), table=<mytable>, + primary_key=True, nullable=False) + ) + + The primary key of a :class:`.Table` can also be specified by using + a :class:`.PrimaryKeyConstraint` object explicitly; in this mode of usage, + the "name" of the constraint can also be specified, as well as other + options which may be recognized by dialects:: + + my_table = Table('mytable', metadata, + Column('id', Integer), + Column('version_id', Integer), + Column('data', String(50)), + PrimaryKeyConstraint('id', 'version_id', + name='mytable_pk') + ) + + The two styles of column-specification should generally not be mixed. + An warning is emitted if the columns present in the + :class:`.PrimaryKeyConstraint` + don't match the columns that were marked as ``primary_key=True``, if both + are present; in this case, the columns are taken strictly from the + :class:`.PrimaryKeyConstraint` declaration, and those columns otherwise + marked as ``primary_key=True`` are ignored. This behavior is intended to + be backwards compatible with previous behavior. + + .. versionchanged:: 0.9.2 Using a mixture of columns within a + :class:`.PrimaryKeyConstraint` in addition to columns marked as + ``primary_key=True`` now emits a warning if the lists don't match. + The ultimate behavior of ignoring those columns marked with the flag + only is currently maintained for backwards compatibility; this warning + may raise an exception in a future release. + + For the use case where specific options are to be specified on the + :class:`.PrimaryKeyConstraint`, but the usual style of using + ``primary_key=True`` flags is still desirable, an empty + :class:`.PrimaryKeyConstraint` may be specified, which will take on the + primary key column collection from the :class:`.Table` based on the + flags:: + + my_table = Table('mytable', metadata, + Column('id', Integer, primary_key=True), + Column('version_id', Integer, primary_key=True), + Column('data', String(50)), + PrimaryKeyConstraint(name='mytable_pk', + mssql_clustered=True) + ) + + .. versionadded:: 0.9.2 an empty :class:`.PrimaryKeyConstraint` may now + be specified for the purposes of establishing keyword arguments with + the constraint, independently of the specification of "primary key" + columns within the :class:`.Table` itself; columns marked as + ``primary_key=True`` will be gathered into the empty constraint's + column collection. + + """ + + __visit_name__ = 'primary_key_constraint' + + def __init__(self, *columns, **kw): + self._implicit_generated = kw.pop('_implicit_generated', False) + super(PrimaryKeyConstraint, self).__init__(*columns, **kw) + + def _set_parent(self, table): + super(PrimaryKeyConstraint, self)._set_parent(table) + + if table.primary_key is not self: + table.constraints.discard(table.primary_key) + table.primary_key = self + table.constraints.add(self) + + table_pks = [c for c in table.c if c.primary_key] + if self.columns and table_pks and \ + set(table_pks) != set(self.columns.values()): + util.warn( + "Table '%s' specifies columns %s as primary_key=True, " + "not matching locally specified columns %s; setting the " + "current primary key columns to %s. This warning " + "may become an exception in a future release" % + ( + table.name, + ", ".join("'%s'" % c.name for c in table_pks), + ", ".join("'%s'" % c.name for c in self.columns), + ", ".join("'%s'" % c.name for c in self.columns) + ) + ) + table_pks[:] = [] + + for c in self.columns: + c.primary_key = True + c.nullable = False + self.columns.extend(table_pks) + + def _reload(self, columns): + """repopulate this :class:`.PrimaryKeyConstraint` given + a set of columns. + + Existing columns in the table that are marked as primary_key=True + are maintained. + + Also fires a new event. + + This is basically like putting a whole new + :class:`.PrimaryKeyConstraint` object on the parent + :class:`.Table` object without actually replacing the object. + + The ordering of the given list of columns is also maintained; these + columns will be appended to the list of columns after any which + are already present. + + """ + + # set the primary key flag on new columns. + # note any existing PK cols on the table also have their + # flag still set. + for col in columns: + col.primary_key = True + + self.columns.extend(columns) + + PrimaryKeyConstraint._autoincrement_column._reset(self) + self._set_parent_with_dispatch(self.table) + + def _replace(self, col): + PrimaryKeyConstraint._autoincrement_column._reset(self) + self.columns.replace(col) + + @property + def columns_autoinc_first(self): + autoinc = self._autoincrement_column + + if autoinc is not None: + return [autoinc] + [c for c in self.columns if c is not autoinc] + else: + return list(self.columns) + + @util.memoized_property + def _autoincrement_column(self): + + def _validate_autoinc(col, autoinc_true): + if col.type._type_affinity is None or not issubclass( + col.type._type_affinity, + type_api.INTEGERTYPE._type_affinity): + if autoinc_true: + raise exc.ArgumentError( + "Column type %s on column '%s' is not " + "compatible with autoincrement=True" % ( + col.type, + col + )) + else: + return False + elif not isinstance(col.default, (type(None), Sequence)) and \ + not autoinc_true: + return False + elif col.server_default is not None and not autoinc_true: + return False + elif ( + col.foreign_keys and col.autoincrement + not in (True, 'ignore_fk')): + return False + return True + + if len(self.columns) == 1: + col = list(self.columns)[0] + + if col.autoincrement is True: + _validate_autoinc(col, True) + return col + elif ( + col.autoincrement in ('auto', 'ignore_fk') and + _validate_autoinc(col, False) + ): + return col + + else: + autoinc = None + for col in self.columns: + if col.autoincrement is True: + _validate_autoinc(col, True) + if autoinc is not None: + raise exc.ArgumentError( + "Only one Column may be marked " + "autoincrement=True, found both %s and %s." % + (col.name, autoinc.name) + ) + else: + autoinc = col + + return autoinc + + +class UniqueConstraint(ColumnCollectionConstraint): + """A table-level UNIQUE constraint. + + Defines a single column or composite UNIQUE constraint. For a no-frills, + single column constraint, adding ``unique=True`` to the ``Column`` + definition is a shorthand equivalent for an unnamed, single column + UniqueConstraint. + """ + + __visit_name__ = 'unique_constraint' + + +class Index(DialectKWArgs, ColumnCollectionMixin, SchemaItem): + """A table-level INDEX. + + Defines a composite (one or more column) INDEX. + + E.g.:: + + sometable = Table("sometable", metadata, + Column("name", String(50)), + Column("address", String(100)) + ) + + Index("some_index", sometable.c.name) + + For a no-frills, single column index, adding + :class:`.Column` also supports ``index=True``:: + + sometable = Table("sometable", metadata, + Column("name", String(50), index=True) + ) + + For a composite index, multiple columns can be specified:: + + Index("some_index", sometable.c.name, sometable.c.address) + + Functional indexes are supported as well, typically by using the + :data:`.func` construct in conjunction with table-bound + :class:`.Column` objects:: + + Index("some_index", func.lower(sometable.c.name)) + + .. versionadded:: 0.8 support for functional and expression-based indexes. + + An :class:`.Index` can also be manually associated with a :class:`.Table`, + either through inline declaration or using + :meth:`.Table.append_constraint`. When this approach is used, the names + of the indexed columns can be specified as strings:: + + Table("sometable", metadata, + Column("name", String(50)), + Column("address", String(100)), + Index("some_index", "name", "address") + ) + + To support functional or expression-based indexes in this form, the + :func:`.text` construct may be used:: + + from sqlalchemy import text + + Table("sometable", metadata, + Column("name", String(50)), + Column("address", String(100)), + Index("some_index", text("lower(name)")) + ) + + .. versionadded:: 0.9.5 the :func:`.text` construct may be used to + specify :class:`.Index` expressions, provided the :class:`.Index` + is explicitly associated with the :class:`.Table`. + + + .. seealso:: + + :ref:`schema_indexes` - General information on :class:`.Index`. + + :ref:`postgresql_indexes` - PostgreSQL-specific options available for + the :class:`.Index` construct. + + :ref:`mysql_indexes` - MySQL-specific options available for the + :class:`.Index` construct. + + :ref:`mssql_indexes` - MSSQL-specific options available for the + :class:`.Index` construct. + + """ + + __visit_name__ = 'index' + + def __init__(self, name, *expressions, **kw): + r"""Construct an index object. + + :param name: + The name of the index + + :param \*expressions: + Column expressions to include in the index. The expressions + are normally instances of :class:`.Column`, but may also + be arbitrary SQL expressions which ultimately refer to a + :class:`.Column`. + + :param unique=False: + Keyword only argument; if True, create a unique index. + + :param quote=None: + Keyword only argument; whether to apply quoting to the name of + the index. Works in the same manner as that of + :paramref:`.Column.quote`. + + :param info=None: Optional data dictionary which will be populated + into the :attr:`.SchemaItem.info` attribute of this object. + + .. versionadded:: 1.0.0 + + :param \**kw: Additional keyword arguments not mentioned above are + dialect specific, and passed in the form + ``<dialectname>_<argname>``. See the documentation regarding an + individual dialect at :ref:`dialect_toplevel` for detail on + documented arguments. + + """ + self.table = table = None + + columns = [] + processed_expressions = [] + for expr, column, strname, add_element in self.\ + _extract_col_expression_collection(expressions): + if add_element is not None: + columns.append(add_element) + processed_expressions.append(expr) + + self.expressions = processed_expressions + self.name = quoted_name(name, kw.pop("quote", None)) + self.unique = kw.pop('unique', False) + _column_flag = kw.pop('_column_flag', False) + if 'info' in kw: + self.info = kw.pop('info') + + # TODO: consider "table" argument being public, but for + # the purpose of the fix here, it starts as private. + if '_table' in kw: + table = kw.pop('_table') + + self._validate_dialect_kwargs(kw) + + # will call _set_parent() if table-bound column + # objects are present + ColumnCollectionMixin.__init__( + self, *columns, _column_flag=_column_flag) + + if table is not None: + self._set_parent(table) + + def _set_parent(self, table): + ColumnCollectionMixin._set_parent(self, table) + + if self.table is not None and table is not self.table: + raise exc.ArgumentError( + "Index '%s' is against table '%s', and " + "cannot be associated with table '%s'." % ( + self.name, + self.table.description, + table.description + ) + ) + self.table = table + table.indexes.add(self) + + self.expressions = [ + expr if isinstance(expr, ClauseElement) + else colexpr + for expr, colexpr in util.zip_longest(self.expressions, + self.columns) + ] + + @property + def bind(self): + """Return the connectable associated with this Index.""" + + return self.table.bind + + def create(self, bind=None): + """Issue a ``CREATE`` statement for this + :class:`.Index`, using the given :class:`.Connectable` + for connectivity. + + .. seealso:: + + :meth:`.MetaData.create_all`. + + """ + if bind is None: + bind = _bind_or_error(self) + bind._run_visitor(ddl.SchemaGenerator, self) + return self + + def drop(self, bind=None): + """Issue a ``DROP`` statement for this + :class:`.Index`, using the given :class:`.Connectable` + for connectivity. + + .. seealso:: + + :meth:`.MetaData.drop_all`. + + """ + if bind is None: + bind = _bind_or_error(self) + bind._run_visitor(ddl.SchemaDropper, self) + + def __repr__(self): + return 'Index(%s)' % ( + ", ".join( + [repr(self.name)] + + [repr(e) for e in self.expressions] + + (self.unique and ["unique=True"] or []) + )) + + +DEFAULT_NAMING_CONVENTION = util.immutabledict({ + "ix": 'ix_%(column_0_label)s' +}) + + +class MetaData(SchemaItem): + """A collection of :class:`.Table` objects and their associated schema + constructs. + + Holds a collection of :class:`.Table` objects as well as + an optional binding to an :class:`.Engine` or + :class:`.Connection`. If bound, the :class:`.Table` objects + in the collection and their columns may participate in implicit SQL + execution. + + The :class:`.Table` objects themselves are stored in the + :attr:`.MetaData.tables` dictionary. + + :class:`.MetaData` is a thread-safe object for read operations. + Construction of new tables within a single :class:`.MetaData` object, + either explicitly or via reflection, may not be completely thread-safe. + + .. seealso:: + + :ref:`metadata_describing` - Introduction to database metadata + + """ + + __visit_name__ = 'metadata' + + def __init__(self, bind=None, reflect=False, schema=None, + quote_schema=None, + naming_convention=DEFAULT_NAMING_CONVENTION, + info=None + ): + """Create a new MetaData object. + + :param bind: + An Engine or Connection to bind to. May also be a string or URL + instance, these are passed to create_engine() and this MetaData will + be bound to the resulting engine. + + :param reflect: + Optional, automatically load all tables from the bound database. + Defaults to False. ``bind`` is required when this option is set. + + .. deprecated:: 0.8 + Please use the :meth:`.MetaData.reflect` method. + + :param schema: + The default schema to use for the :class:`.Table`, + :class:`.Sequence`, and potentially other objects associated with + this :class:`.MetaData`. Defaults to ``None``. + + When this value is set, any :class:`.Table` or :class:`.Sequence` + which specifies ``None`` for the schema parameter will instead + have this schema name defined. To build a :class:`.Table` + or :class:`.Sequence` that still has ``None`` for the schema + even when this parameter is present, use the :attr:`.BLANK_SCHEMA` + symbol. + + .. note:: + + As refered above, the :paramref:`.MetaData.schema` parameter + only refers to the **default value** that will be applied to + the :paramref:`.Table.schema` parameter of an incoming + :class:`.Table` object. It does not refer to how the + :class:`.Table` is catalogued within the :class:`.MetaData`, + which remains consistent vs. a :class:`.MetaData` collection + that does not define this parameter. The :class:`.Table` + within the :class:`.MetaData` will still be keyed based on its + schema-qualified name, e.g. + ``my_metadata.tables["some_schema.my_table"]``. + + The current behavior of the :class:`.ForeignKey` object is to + circumvent this restriction, where it can locate a table given + the table name alone, where the schema will be assumed to be + present from this value as specified on the owning + :class:`.MetaData` collection. However, this implies that a + table qualified with BLANK_SCHEMA cannot currently be referred + to by string name from :class:`.ForeignKey`. Other parts of + SQLAlchemy such as Declarative may not have similar behaviors + built in, however may do so in a future release, along with a + consistent method of referring to a table in BLANK_SCHEMA. + + + .. seealso:: + + :paramref:`.Table.schema` + + :paramref:`.Sequence.schema` + + :param quote_schema: + Sets the ``quote_schema`` flag for those :class:`.Table`, + :class:`.Sequence`, and other objects which make usage of the + local ``schema`` name. + + :param info: Optional data dictionary which will be populated into the + :attr:`.SchemaItem.info` attribute of this object. + + .. versionadded:: 1.0.0 + + :param naming_convention: a dictionary referring to values which + will establish default naming conventions for :class:`.Constraint` + and :class:`.Index` objects, for those objects which are not given + a name explicitly. + + The keys of this dictionary may be: + + * a constraint or Index class, e.g. the :class:`.UniqueConstraint`, + :class:`.ForeignKeyConstraint` class, the :class:`.Index` class + + * a string mnemonic for one of the known constraint classes; + ``"fk"``, ``"pk"``, ``"ix"``, ``"ck"``, ``"uq"`` for foreign key, + primary key, index, check, and unique constraint, respectively. + + * the string name of a user-defined "token" that can be used + to define new naming tokens. + + The values associated with each "constraint class" or "constraint + mnemonic" key are string naming templates, such as + ``"uq_%(table_name)s_%(column_0_name)s"``, + which describe how the name should be composed. The values + associated with user-defined "token" keys should be callables of the + form ``fn(constraint, table)``, which accepts the constraint/index + object and :class:`.Table` as arguments, returning a string + result. + + The built-in names are as follows, some of which may only be + available for certain types of constraint: + + * ``%(table_name)s`` - the name of the :class:`.Table` object + associated with the constraint. + + * ``%(referred_table_name)s`` - the name of the :class:`.Table` + object associated with the referencing target of a + :class:`.ForeignKeyConstraint`. + + * ``%(column_0_name)s`` - the name of the :class:`.Column` at + index position "0" within the constraint. + + * ``%(column_0_label)s`` - the label of the :class:`.Column` at + index position "0", e.g. :attr:`.Column.label` + + * ``%(column_0_key)s`` - the key of the :class:`.Column` at + index position "0", e.g. :attr:`.Column.key` + + * ``%(referred_column_0_name)s`` - the name of a :class:`.Column` + at index position "0" referenced by a + :class:`.ForeignKeyConstraint`. + + * ``%(constraint_name)s`` - a special key that refers to the + existing name given to the constraint. When this key is + present, the :class:`.Constraint` object's existing name will be + replaced with one that is composed from template string that + uses this token. When this token is present, it is required that + the :class:`.Constraint` is given an explicit name ahead of time. + + * user-defined: any additional token may be implemented by passing + it along with a ``fn(constraint, table)`` callable to the + naming_convention dictionary. + + .. versionadded:: 0.9.2 + + .. seealso:: + + :ref:`constraint_naming_conventions` - for detailed usage + examples. + + """ + self.tables = util.immutabledict() + self.schema = quoted_name(schema, quote_schema) + self.naming_convention = naming_convention + if info: + self.info = info + self._schemas = set() + self._sequences = {} + self._fk_memos = collections.defaultdict(list) + + self.bind = bind + if reflect: + util.warn_deprecated("reflect=True is deprecate; please " + "use the reflect() method.") + if not bind: + raise exc.ArgumentError( + "A bind must be supplied in conjunction " + "with reflect=True") + self.reflect() + + tables = None + """A dictionary of :class:`.Table` objects keyed to their name or "table key". + + The exact key is that determined by the :attr:`.Table.key` attribute; + for a table with no :attr:`.Table.schema` attribute, this is the same + as :attr:`.Table.name`. For a table with a schema, it is typically of the + form ``schemaname.tablename``. + + .. seealso:: + + :attr:`.MetaData.sorted_tables` + + """ + + def __repr__(self): + return 'MetaData(bind=%r)' % self.bind + + def __contains__(self, table_or_key): + if not isinstance(table_or_key, util.string_types): + table_or_key = table_or_key.key + return table_or_key in self.tables + + def _add_table(self, name, schema, table): + key = _get_table_key(name, schema) + dict.__setitem__(self.tables, key, table) + if schema: + self._schemas.add(schema) + + def _remove_table(self, name, schema): + key = _get_table_key(name, schema) + removed = dict.pop(self.tables, key, None) + if removed is not None: + for fk in removed.foreign_keys: + fk._remove_from_metadata(self) + if self._schemas: + self._schemas = set([t.schema + for t in self.tables.values() + if t.schema is not None]) + + def __getstate__(self): + return {'tables': self.tables, + 'schema': self.schema, + 'schemas': self._schemas, + 'sequences': self._sequences, + 'fk_memos': self._fk_memos, + 'naming_convention': self.naming_convention + } + + def __setstate__(self, state): + self.tables = state['tables'] + self.schema = state['schema'] + self.naming_convention = state['naming_convention'] + self._bind = None + self._sequences = state['sequences'] + self._schemas = state['schemas'] + self._fk_memos = state['fk_memos'] + + def is_bound(self): + """True if this MetaData is bound to an Engine or Connection.""" + + return self._bind is not None + + def bind(self): + """An :class:`.Engine` or :class:`.Connection` to which this + :class:`.MetaData` is bound. + + Typically, a :class:`.Engine` is assigned to this attribute + so that "implicit execution" may be used, or alternatively + as a means of providing engine binding information to an + ORM :class:`.Session` object:: + + engine = create_engine("someurl://") + metadata.bind = engine + + .. seealso:: + + :ref:`dbengine_implicit` - background on "bound metadata" + + """ + return self._bind + + @util.dependencies("sqlalchemy.engine.url") + def _bind_to(self, url, bind): + """Bind this MetaData to an Engine, Connection, string or URL.""" + + if isinstance(bind, util.string_types + (url.URL, )): + self._bind = sqlalchemy.create_engine(bind) + else: + self._bind = bind + bind = property(bind, _bind_to) + + def clear(self): + """Clear all Table objects from this MetaData.""" + + dict.clear(self.tables) + self._schemas.clear() + self._fk_memos.clear() + + def remove(self, table): + """Remove the given Table object from this MetaData.""" + + self._remove_table(table.name, table.schema) + + @property + def sorted_tables(self): + """Returns a list of :class:`.Table` objects sorted in order of + foreign key dependency. + + The sorting will place :class:`.Table` objects that have dependencies + first, before the dependencies themselves, representing the + order in which they can be created. To get the order in which + the tables would be dropped, use the ``reversed()`` Python built-in. + + .. warning:: + + The :attr:`.sorted_tables` accessor cannot by itself accommodate + automatic resolution of dependency cycles between tables, which + are usually caused by mutually dependent foreign key constraints. + To resolve these cycles, either the + :paramref:`.ForeignKeyConstraint.use_alter` parameter may be appled + to those constraints, or use the + :func:`.schema.sort_tables_and_constraints` function which will break + out foreign key constraints involved in cycles separately. + + .. seealso:: + + :func:`.schema.sort_tables` + + :func:`.schema.sort_tables_and_constraints` + + :attr:`.MetaData.tables` + + :meth:`.Inspector.get_table_names` + + :meth:`.Inspector.get_sorted_table_and_fkc_names` + + + """ + return ddl.sort_tables(sorted(self.tables.values(), key=lambda t: t.key)) + + def reflect(self, bind=None, schema=None, views=False, only=None, + extend_existing=False, + autoload_replace=True, + **dialect_kwargs): + r"""Load all available table definitions from the database. + + Automatically creates ``Table`` entries in this ``MetaData`` for any + table available in the database but not yet present in the + ``MetaData``. May be called multiple times to pick up tables recently + added to the database, however no special action is taken if a table + in this ``MetaData`` no longer exists in the database. + + :param bind: + A :class:`.Connectable` used to access the database; if None, uses + the existing bind on this ``MetaData``, if any. + + :param schema: + Optional, query and reflect tables from an alterate schema. + If None, the schema associated with this :class:`.MetaData` + is used, if any. + + :param views: + If True, also reflect views. + + :param only: + Optional. Load only a sub-set of available named tables. May be + specified as a sequence of names or a callable. + + If a sequence of names is provided, only those tables will be + reflected. An error is raised if a table is requested but not + available. Named tables already present in this ``MetaData`` are + ignored. + + If a callable is provided, it will be used as a boolean predicate to + filter the list of potential table names. The callable is called + with a table name and this ``MetaData`` instance as positional + arguments and should return a true value for any table to reflect. + + :param extend_existing: Passed along to each :class:`.Table` as + :paramref:`.Table.extend_existing`. + + .. versionadded:: 0.9.1 + + :param autoload_replace: Passed along to each :class:`.Table` as + :paramref:`.Table.autoload_replace`. + + .. versionadded:: 0.9.1 + + :param \**dialect_kwargs: Additional keyword arguments not mentioned + above are dialect specific, and passed in the form + ``<dialectname>_<argname>``. See the documentation regarding an + individual dialect at :ref:`dialect_toplevel` for detail on + documented arguments. + + .. versionadded:: 0.9.2 - Added + :paramref:`.MetaData.reflect.**dialect_kwargs` to support + dialect-level reflection options for all :class:`.Table` + objects reflected. + + """ + if bind is None: + bind = _bind_or_error(self) + + with bind.connect() as conn: + + reflect_opts = { + 'autoload': True, + 'autoload_with': conn, + 'extend_existing': extend_existing, + 'autoload_replace': autoload_replace, + '_extend_on': set() + } + + reflect_opts.update(dialect_kwargs) + + if schema is None: + schema = self.schema + + if schema is not None: + reflect_opts['schema'] = schema + + available = util.OrderedSet( + bind.engine.table_names(schema, connection=conn)) + if views: + available.update( + bind.dialect.get_view_names(conn, schema) + ) + + if schema is not None: + available_w_schema = util.OrderedSet(["%s.%s" % (schema, name) + for name in available]) + else: + available_w_schema = available + + current = set(self.tables) + + if only is None: + load = [name for name, schname in + zip(available, available_w_schema) + if extend_existing or schname not in current] + elif util.callable(only): + load = [name for name, schname in + zip(available, available_w_schema) + if (extend_existing or schname not in current) + and only(name, self)] + else: + missing = [name for name in only if name not in available] + if missing: + s = schema and (" schema '%s'" % schema) or '' + raise exc.InvalidRequestError( + 'Could not reflect: requested table(s) not available ' + 'in %r%s: (%s)' % + (bind.engine, s, ', '.join(missing))) + load = [name for name in only if extend_existing or + name not in current] + + for name in load: + try: + Table(name, self, **reflect_opts) + except exc.UnreflectableTableError as uerr: + util.warn("Skipping table %s: %s" % (name, uerr)) + + def append_ddl_listener(self, event_name, listener): + """Append a DDL event listener to this ``MetaData``. + + .. deprecated:: 0.7 + See :class:`.DDLEvents`. + + """ + def adapt_listener(target, connection, **kw): + tables = kw['tables'] + listener(event, target, connection, tables=tables) + + event.listen(self, "" + event_name.replace('-', '_'), adapt_listener) + + def create_all(self, bind=None, tables=None, checkfirst=True): + """Create all tables stored in this metadata. + + Conditional by default, will not attempt to recreate tables already + present in the target database. + + :param bind: + A :class:`.Connectable` used to access the + database; if None, uses the existing bind on this ``MetaData``, if + any. + + :param tables: + Optional list of ``Table`` objects, which is a subset of the total + tables in the ``MetaData`` (others are ignored). + + :param checkfirst: + Defaults to True, don't issue CREATEs for tables already present + in the target database. + + """ + if bind is None: + bind = _bind_or_error(self) + bind._run_visitor(ddl.SchemaGenerator, + self, + checkfirst=checkfirst, + tables=tables) + + def drop_all(self, bind=None, tables=None, checkfirst=True): + """Drop all tables stored in this metadata. + + Conditional by default, will not attempt to drop tables not present in + the target database. + + :param bind: + A :class:`.Connectable` used to access the + database; if None, uses the existing bind on this ``MetaData``, if + any. + + :param tables: + Optional list of ``Table`` objects, which is a subset of the + total tables in the ``MetaData`` (others are ignored). + + :param checkfirst: + Defaults to True, only issue DROPs for tables confirmed to be + present in the target database. + + """ + if bind is None: + bind = _bind_or_error(self) + bind._run_visitor(ddl.SchemaDropper, + self, + checkfirst=checkfirst, + tables=tables) + + +class ThreadLocalMetaData(MetaData): + """A MetaData variant that presents a different ``bind`` in every thread. + + Makes the ``bind`` property of the MetaData a thread-local value, allowing + this collection of tables to be bound to different ``Engine`` + implementations or connections in each thread. + + The ThreadLocalMetaData starts off bound to None in each thread. Binds + must be made explicitly by assigning to the ``bind`` property or using + ``connect()``. You can also re-bind dynamically multiple times per + thread, just like a regular ``MetaData``. + + """ + + __visit_name__ = 'metadata' + + def __init__(self): + """Construct a ThreadLocalMetaData.""" + + self.context = util.threading.local() + self.__engines = {} + super(ThreadLocalMetaData, self).__init__() + + def bind(self): + """The bound Engine or Connection for this thread. + + This property may be assigned an Engine or Connection, or assigned a + string or URL to automatically create a basic Engine for this bind + with ``create_engine()``.""" + + return getattr(self.context, '_engine', None) + + @util.dependencies("sqlalchemy.engine.url") + def _bind_to(self, url, bind): + """Bind to a Connectable in the caller's thread.""" + + if isinstance(bind, util.string_types + (url.URL, )): + try: + self.context._engine = self.__engines[bind] + except KeyError: + e = sqlalchemy.create_engine(bind) + self.__engines[bind] = e + self.context._engine = e + else: + # TODO: this is squirrely. we shouldn't have to hold onto engines + # in a case like this + if bind not in self.__engines: + self.__engines[bind] = bind + self.context._engine = bind + + bind = property(bind, _bind_to) + + def is_bound(self): + """True if there is a bind for this thread.""" + return (hasattr(self.context, '_engine') and + self.context._engine is not None) + + def dispose(self): + """Dispose all bound engines, in all thread contexts.""" + + for e in self.__engines.values(): + if hasattr(e, 'dispose'): + e.dispose() + + +class _SchemaTranslateMap(object): + """Provide translation of schema names based on a mapping. + + Also provides helpers for producing cache keys and optimized + access when no mapping is present. + + Used by the :paramref:`.Connection.execution_options.schema_translate_map` + feature. + + .. versionadded:: 1.1 + + + """ + __slots__ = 'map_', '__call__', 'hash_key', 'is_default' + + _default_schema_getter = operator.attrgetter("schema") + + def __init__(self, map_): + self.map_ = map_ + if map_ is not None: + def schema_for_object(obj): + effective_schema = self._default_schema_getter(obj) + effective_schema = obj._translate_schema( + effective_schema, map_) + return effective_schema + self.__call__ = schema_for_object + self.hash_key = ";".join( + "%s=%s" % (k, map_[k]) + for k in sorted(map_, key=str) + ) + self.is_default = False + else: + self.hash_key = 0 + self.__call__ = self._default_schema_getter + self.is_default = True + + @classmethod + def _schema_getter(cls, map_): + if map_ is None: + return _default_schema_map + elif isinstance(map_, _SchemaTranslateMap): + return map_ + else: + return _SchemaTranslateMap(map_) + +_default_schema_map = _SchemaTranslateMap(None) +_schema_getter = _SchemaTranslateMap._schema_getter + diff --git a/venv/Lib/site-packages/sqlalchemy/sql/selectable.py b/venv/Lib/site-packages/sqlalchemy/sql/selectable.py new file mode 100644 index 0000000..64886b3 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/selectable.py @@ -0,0 +1,3729 @@ +# sql/selectable.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""The :class:`.FromClause` class of SQL expression elements, representing +SQL tables and derived rowsets. + +""" + +from .elements import ClauseElement, TextClause, ClauseList, \ + and_, Grouping, UnaryExpression, literal_column, BindParameter +from .elements import _clone, \ + _literal_as_text, _interpret_as_column_or_from, _expand_cloned,\ + _select_iterables, _anonymous_label, _clause_element_as_expr,\ + _cloned_intersection, _cloned_difference, True_, \ + _literal_as_label_reference, _literal_and_labels_as_label_reference +from .base import Immutable, Executable, _generative, \ + ColumnCollection, ColumnSet, _from_objects, Generative +from . import type_api +from .. import inspection +from .. import util +from .. import exc +from operator import attrgetter +from . import operators +import operator +import collections +from .annotation import Annotated +import itertools +from sqlalchemy.sql.visitors import Visitable + + +def _interpret_as_from(element): + insp = inspection.inspect(element, raiseerr=False) + if insp is None: + if isinstance(element, util.string_types): + util.warn_limited( + "Textual SQL FROM expression %(expr)r should be " + "explicitly declared as text(%(expr)r), " + "or use table(%(expr)r) for more specificity", + {"expr": util.ellipses_string(element)}) + + return TextClause(util.text_type(element)) + try: + return insp.selectable + except AttributeError: + raise exc.ArgumentError("FROM expression expected") + + +def _interpret_as_select(element): + element = _interpret_as_from(element) + if isinstance(element, Alias): + element = element.original + if not isinstance(element, SelectBase): + element = element.select() + return element + + +class _OffsetLimitParam(BindParameter): + @property + def _limit_offset_value(self): + return self.effective_value + + +def _offset_or_limit_clause(element, name=None, type_=None): + """Convert the given value to an "offset or limit" clause. + + This handles incoming integers and converts to an expression; if + an expression is already given, it is passed through. + + """ + if element is None: + return None + elif hasattr(element, '__clause_element__'): + return element.__clause_element__() + elif isinstance(element, Visitable): + return element + else: + value = util.asint(element) + return _OffsetLimitParam(name, value, type_=type_, unique=True) + + +def _offset_or_limit_clause_asint(clause, attrname): + """Convert the "offset or limit" clause of a select construct to an + integer. + + This is only possible if the value is stored as a simple bound parameter. + Otherwise, a compilation error is raised. + + """ + if clause is None: + return None + try: + value = clause._limit_offset_value + except AttributeError: + raise exc.CompileError( + "This SELECT structure does not use a simple " + "integer value for %s" % attrname) + else: + return util.asint(value) + + +def subquery(alias, *args, **kwargs): + r"""Return an :class:`.Alias` object derived + from a :class:`.Select`. + + name + alias name + + \*args, \**kwargs + + all other arguments are delivered to the + :func:`select` function. + + """ + return Select(*args, **kwargs).alias(alias) + + +def alias(selectable, name=None, flat=False): + """Return an :class:`.Alias` object. + + An :class:`.Alias` represents any :class:`.FromClause` + with an alternate name assigned within SQL, typically using the ``AS`` + clause when generated, e.g. ``SELECT * FROM table AS aliasname``. + + Similar functionality is available via the + :meth:`~.FromClause.alias` method + available on all :class:`.FromClause` subclasses. + + When an :class:`.Alias` is created from a :class:`.Table` object, + this has the effect of the table being rendered + as ``tablename AS aliasname`` in a SELECT statement. + + For :func:`.select` objects, the effect is that of creating a named + subquery, i.e. ``(select ...) AS aliasname``. + + The ``name`` parameter is optional, and provides the name + to use in the rendered SQL. If blank, an "anonymous" name + will be deterministically generated at compile time. + Deterministic means the name is guaranteed to be unique against + other constructs used in the same statement, and will also be the + same name for each successive compilation of the same statement + object. + + :param selectable: any :class:`.FromClause` subclass, + such as a table, select statement, etc. + + :param name: string name to be assigned as the alias. + If ``None``, a name will be deterministically generated + at compile time. + + :param flat: Will be passed through to if the given selectable + is an instance of :class:`.Join` - see :meth:`.Join.alias` + for details. + + .. versionadded:: 0.9.0 + + """ + return _interpret_as_from(selectable).alias(name=name, flat=flat) + + +def lateral(selectable, name=None): + """Return a :class:`.Lateral` object. + + :class:`.Lateral` is an :class:`.Alias` subclass that represents + a subquery with the LATERAL keyword applied to it. + + The special behavior of a LATERAL subquery is that it appears in the + FROM clause of an enclosing SELECT, but may correlate to other + FROM clauses of that SELECT. It is a special case of subquery + only supported by a small number of backends, currently more recent + PostgreSQL versions. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`lateral_selects` - overview of usage. + + """ + return _interpret_as_from(selectable).lateral(name=name) + + +def tablesample(selectable, sampling, name=None, seed=None): + """Return a :class:`.TableSample` object. + + :class:`.TableSample` is an :class:`.Alias` subclass that represents + a table with the TABLESAMPLE clause applied to it. + :func:`~.expression.tablesample` + is also available from the :class:`.FromClause` class via the + :meth:`.FromClause.tablesample` method. + + The TABLESAMPLE clause allows selecting a randomly selected approximate + percentage of rows from a table. It supports multiple sampling methods, + most commonly BERNOULLI and SYSTEM. + + e.g.:: + + from sqlalchemy import func + + selectable = people.tablesample( + func.bernoulli(1), + name='alias', + seed=func.random()) + stmt = select([selectable.c.people_id]) + + Assuming ``people`` with a column ``people_id``, the above + statement would render as:: + + SELECT alias.people_id FROM + people AS alias TABLESAMPLE bernoulli(:bernoulli_1) + REPEATABLE (random()) + + .. versionadded:: 1.1 + + :param sampling: a ``float`` percentage between 0 and 100 or + :class:`.functions.Function`. + + :param name: optional alias name + + :param seed: any real-valued SQL expression. When specified, the + REPEATABLE sub-clause is also rendered. + + """ + return _interpret_as_from(selectable).tablesample( + sampling, name=name, seed=seed) + + +class Selectable(ClauseElement): + """mark a class as being selectable""" + __visit_name__ = 'selectable' + + is_selectable = True + + @property + def selectable(self): + return self + + +class HasPrefixes(object): + _prefixes = () + + @_generative + def prefix_with(self, *expr, **kw): + r"""Add one or more expressions following the statement keyword, i.e. + SELECT, INSERT, UPDATE, or DELETE. Generative. + + This is used to support backend-specific prefix keywords such as those + provided by MySQL. + + E.g.:: + + stmt = table.insert().prefix_with("LOW_PRIORITY", dialect="mysql") + + Multiple prefixes can be specified by multiple calls + to :meth:`.prefix_with`. + + :param \*expr: textual or :class:`.ClauseElement` construct which + will be rendered following the INSERT, UPDATE, or DELETE + keyword. + :param \**kw: A single keyword 'dialect' is accepted. This is an + optional string dialect name which will + limit rendering of this prefix to only that dialect. + + """ + dialect = kw.pop('dialect', None) + if kw: + raise exc.ArgumentError("Unsupported argument(s): %s" % + ",".join(kw)) + self._setup_prefixes(expr, dialect) + + def _setup_prefixes(self, prefixes, dialect=None): + self._prefixes = self._prefixes + tuple( + [(_literal_as_text(p, warn=False), dialect) for p in prefixes]) + + +class HasSuffixes(object): + _suffixes = () + + @_generative + def suffix_with(self, *expr, **kw): + r"""Add one or more expressions following the statement as a whole. + + This is used to support backend-specific suffix keywords on + certain constructs. + + E.g.:: + + stmt = select([col1, col2]).cte().suffix_with( + "cycle empno set y_cycle to 1 default 0", dialect="oracle") + + Multiple suffixes can be specified by multiple calls + to :meth:`.suffix_with`. + + :param \*expr: textual or :class:`.ClauseElement` construct which + will be rendered following the target clause. + :param \**kw: A single keyword 'dialect' is accepted. This is an + optional string dialect name which will + limit rendering of this suffix to only that dialect. + + """ + dialect = kw.pop('dialect', None) + if kw: + raise exc.ArgumentError("Unsupported argument(s): %s" % + ",".join(kw)) + self._setup_suffixes(expr, dialect) + + def _setup_suffixes(self, suffixes, dialect=None): + self._suffixes = self._suffixes + tuple( + [(_literal_as_text(p, warn=False), dialect) for p in suffixes]) + + +class FromClause(Selectable): + """Represent an element that can be used within the ``FROM`` + clause of a ``SELECT`` statement. + + The most common forms of :class:`.FromClause` are the + :class:`.Table` and the :func:`.select` constructs. Key + features common to all :class:`.FromClause` objects include: + + * a :attr:`.c` collection, which provides per-name access to a collection + of :class:`.ColumnElement` objects. + * a :attr:`.primary_key` attribute, which is a collection of all those + :class:`.ColumnElement` objects that indicate the ``primary_key`` flag. + * Methods to generate various derivations of a "from" clause, including + :meth:`.FromClause.alias`, :meth:`.FromClause.join`, + :meth:`.FromClause.select`. + + + """ + __visit_name__ = 'fromclause' + named_with_column = False + _hide_froms = [] + + _is_join = False + _is_select = False + _is_from_container = False + + _is_lateral = False + + _textual = False + """a marker that allows us to easily distinguish a :class:`.TextAsFrom` + or similar object from other kinds of :class:`.FromClause` objects.""" + + schema = None + """Define the 'schema' attribute for this :class:`.FromClause`. + + This is typically ``None`` for most objects except that of + :class:`.Table`, where it is taken as the value of the + :paramref:`.Table.schema` argument. + + """ + + def _translate_schema(self, effective_schema, map_): + return effective_schema + + _memoized_property = util.group_expirable_memoized_property(["_columns"]) + + @util.deprecated( + '1.1', + message="``FromClause.count()`` is deprecated. Counting " + "rows requires that the correct column expression and " + "accommodations for joins, DISTINCT, etc. must be made, " + "otherwise results may not be what's expected. " + "Please use an appropriate ``func.count()`` expression " + "directly.") + @util.dependencies("sqlalchemy.sql.functions") + def count(self, functions, whereclause=None, **params): + """return a SELECT COUNT generated against this + :class:`.FromClause`. + + The function generates COUNT against the + first column in the primary key of the table, or against + the first column in the table overall. Explicit use of + ``func.count()`` should be preferred:: + + row_count = conn.scalar( + select([func.count('*')]).select_from(table) + ) + + + .. seealso:: + + :data:`.func` + + """ + + if self.primary_key: + col = list(self.primary_key)[0] + else: + col = list(self.columns)[0] + return Select( + [functions.func.count(col).label('tbl_row_count')], + whereclause, + from_obj=[self], + **params) + + def select(self, whereclause=None, **params): + """return a SELECT of this :class:`.FromClause`. + + .. seealso:: + + :func:`~.sql.expression.select` - general purpose + method which allows for arbitrary column lists. + + """ + + return Select([self], whereclause, **params) + + def join(self, right, onclause=None, isouter=False, full=False): + """Return a :class:`.Join` from this :class:`.FromClause` + to another :class:`FromClause`. + + E.g.:: + + from sqlalchemy import join + + j = user_table.join(address_table, + user_table.c.id == address_table.c.user_id) + stmt = select([user_table]).select_from(j) + + would emit SQL along the lines of:: + + SELECT user.id, user.name FROM user + JOIN address ON user.id = address.user_id + + :param right: the right side of the join; this is any + :class:`.FromClause` object such as a :class:`.Table` object, and + may also be a selectable-compatible object such as an ORM-mapped + class. + + :param onclause: a SQL expression representing the ON clause of the + join. If left at ``None``, :meth:`.FromClause.join` will attempt to + join the two tables based on a foreign key relationship. + + :param isouter: if True, render a LEFT OUTER JOIN, instead of JOIN. + + :param full: if True, render a FULL OUTER JOIN, instead of LEFT OUTER + JOIN. Implies :paramref:`.FromClause.join.isouter`. + + .. versionadded:: 1.1 + + .. seealso:: + + :func:`.join` - standalone function + + :class:`.Join` - the type of object produced + + """ + + return Join(self, right, onclause, isouter, full) + + def outerjoin(self, right, onclause=None, full=False): + """Return a :class:`.Join` from this :class:`.FromClause` + to another :class:`FromClause`, with the "isouter" flag set to + True. + + E.g.:: + + from sqlalchemy import outerjoin + + j = user_table.outerjoin(address_table, + user_table.c.id == address_table.c.user_id) + + The above is equivalent to:: + + j = user_table.join( + address_table, + user_table.c.id == address_table.c.user_id, + isouter=True) + + :param right: the right side of the join; this is any + :class:`.FromClause` object such as a :class:`.Table` object, and + may also be a selectable-compatible object such as an ORM-mapped + class. + + :param onclause: a SQL expression representing the ON clause of the + join. If left at ``None``, :meth:`.FromClause.join` will attempt to + join the two tables based on a foreign key relationship. + + :param full: if True, render a FULL OUTER JOIN, instead of + LEFT OUTER JOIN. + + .. versionadded:: 1.1 + + .. seealso:: + + :meth:`.FromClause.join` + + :class:`.Join` + + """ + + return Join(self, right, onclause, True, full) + + def alias(self, name=None, flat=False): + """return an alias of this :class:`.FromClause`. + + This is shorthand for calling:: + + from sqlalchemy import alias + a = alias(self, name=name) + + See :func:`~.expression.alias` for details. + + """ + + return Alias(self, name) + + def lateral(self, name=None): + """Return a LATERAL alias of this :class:`.FromClause`. + + The return value is the :class:`.Lateral` construct also + provided by the top-level :func:`~.expression.lateral` function. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`lateral_selects` - overview of usage. + + """ + return Lateral(self, name) + + def tablesample(self, sampling, name=None, seed=None): + """Return a TABLESAMPLE alias of this :class:`.FromClause`. + + The return value is the :class:`.TableSample` construct also + provided by the top-level :func:`~.expression.tablesample` function. + + .. versionadded:: 1.1 + + .. seealso:: + + :func:`~.expression.tablesample` - usage guidelines and parameters + + """ + return TableSample(self, sampling, name, seed) + + def is_derived_from(self, fromclause): + """Return True if this FromClause is 'derived' from the given + FromClause. + + An example would be an Alias of a Table is derived from that Table. + + """ + # this is essentially an "identity" check in the base class. + # Other constructs override this to traverse through + # contained elements. + return fromclause in self._cloned_set + + def _is_lexical_equivalent(self, other): + """Return True if this FromClause and the other represent + the same lexical identity. + + This tests if either one is a copy of the other, or + if they are the same via annotation identity. + + """ + return self._cloned_set.intersection(other._cloned_set) + + @util.dependencies("sqlalchemy.sql.util") + def replace_selectable(self, sqlutil, old, alias): + """replace all occurrences of FromClause 'old' with the given Alias + object, returning a copy of this :class:`.FromClause`. + + """ + + return sqlutil.ClauseAdapter(alias).traverse(self) + + def correspond_on_equivalents(self, column, equivalents): + """Return corresponding_column for the given column, or if None + search for a match in the given dictionary. + + """ + col = self.corresponding_column(column, require_embedded=True) + if col is None and col in equivalents: + for equiv in equivalents[col]: + nc = self.corresponding_column(equiv, require_embedded=True) + if nc: + return nc + return col + + def corresponding_column(self, column, require_embedded=False): + """Given a :class:`.ColumnElement`, return the exported + :class:`.ColumnElement` object from this :class:`.Selectable` + which corresponds to that original + :class:`~sqlalchemy.schema.Column` via a common ancestor + column. + + :param column: the target :class:`.ColumnElement` to be matched + + :param require_embedded: only return corresponding columns for + the given :class:`.ColumnElement`, if the given + :class:`.ColumnElement` is actually present within a sub-element + of this :class:`.FromClause`. Normally the column will match if + it merely shares a common ancestor with one of the exported + columns of this :class:`.FromClause`. + + """ + + def embedded(expanded_proxy_set, target_set): + for t in target_set.difference(expanded_proxy_set): + if not set(_expand_cloned([t]) + ).intersection(expanded_proxy_set): + return False + return True + + # don't dig around if the column is locally present + if self.c.contains_column(column): + return column + col, intersect = None, None + target_set = column.proxy_set + cols = self.c._all_columns + for c in cols: + expanded_proxy_set = set(_expand_cloned(c.proxy_set)) + i = target_set.intersection(expanded_proxy_set) + if i and (not require_embedded + or embedded(expanded_proxy_set, target_set)): + if col is None: + + # no corresponding column yet, pick this one. + + col, intersect = c, i + elif len(i) > len(intersect): + + # 'c' has a larger field of correspondence than + # 'col'. i.e. selectable.c.a1_x->a1.c.x->table.c.x + # matches a1.c.x->table.c.x better than + # selectable.c.x->table.c.x does. + + col, intersect = c, i + elif i == intersect: + + # they have the same field of correspondence. see + # which proxy_set has fewer columns in it, which + # indicates a closer relationship with the root + # column. Also take into account the "weight" + # attribute which CompoundSelect() uses to give + # higher precedence to columns based on vertical + # position in the compound statement, and discard + # columns that have no reference to the target + # column (also occurs with CompoundSelect) + + col_distance = util.reduce( + operator.add, + [sc._annotations.get('weight', 1) for sc in + col.proxy_set if sc.shares_lineage(column)]) + c_distance = util.reduce( + operator.add, + [sc._annotations.get('weight', 1) for sc in + c.proxy_set if sc.shares_lineage(column)]) + if c_distance < col_distance: + col, intersect = c, i + return col + + @property + def description(self): + """a brief description of this FromClause. + + Used primarily for error message formatting. + + """ + return getattr(self, 'name', self.__class__.__name__ + " object") + + def _reset_exported(self): + """delete memoized collections when a FromClause is cloned.""" + + self._memoized_property.expire_instance(self) + + @_memoized_property + def columns(self): + """A named-based collection of :class:`.ColumnElement` objects + maintained by this :class:`.FromClause`. + + The :attr:`.columns`, or :attr:`.c` collection, is the gateway + to the construction of SQL expressions using table-bound or + other selectable-bound columns:: + + select([mytable]).where(mytable.c.somecolumn == 5) + + """ + + if '_columns' not in self.__dict__: + self._init_collections() + self._populate_column_collection() + return self._columns.as_immutable() + + @_memoized_property + def primary_key(self): + """Return the collection of Column objects which comprise the + primary key of this FromClause.""" + + self._init_collections() + self._populate_column_collection() + return self.primary_key + + @_memoized_property + def foreign_keys(self): + """Return the collection of ForeignKey objects which this + FromClause references.""" + + self._init_collections() + self._populate_column_collection() + return self.foreign_keys + + c = property(attrgetter('columns'), + doc="An alias for the :attr:`.columns` attribute.") + _select_iterable = property(attrgetter('columns')) + + def _init_collections(self): + assert '_columns' not in self.__dict__ + assert 'primary_key' not in self.__dict__ + assert 'foreign_keys' not in self.__dict__ + + self._columns = ColumnCollection() + self.primary_key = ColumnSet() + self.foreign_keys = set() + + @property + def _cols_populated(self): + return '_columns' in self.__dict__ + + def _populate_column_collection(self): + """Called on subclasses to establish the .c collection. + + Each implementation has a different way of establishing + this collection. + + """ + + def _refresh_for_new_column(self, column): + """Given a column added to the .c collection of an underlying + selectable, produce the local version of that column, assuming this + selectable ultimately should proxy this column. + + this is used to "ping" a derived selectable to add a new column + to its .c. collection when a Column has been added to one of the + Table objects it ultimtely derives from. + + If the given selectable hasn't populated its .c. collection yet, + it should at least pass on the message to the contained selectables, + but it will return None. + + This method is currently used by Declarative to allow Table + columns to be added to a partially constructed inheritance + mapping that may have already produced joins. The method + isn't public right now, as the full span of implications + and/or caveats aren't yet clear. + + It's also possible that this functionality could be invoked by + default via an event, which would require that + selectables maintain a weak referencing collection of all + derivations. + + """ + if not self._cols_populated: + return None + elif (column.key in self.columns and + self.columns[column.key] is column): + return column + else: + return None + + +class Join(FromClause): + """represent a ``JOIN`` construct between two :class:`.FromClause` + elements. + + The public constructor function for :class:`.Join` is the module-level + :func:`.join()` function, as well as the :meth:`.FromClause.join` method + of any :class:`.FromClause` (e.g. such as :class:`.Table`). + + .. seealso:: + + :func:`.join` + + :meth:`.FromClause.join` + + """ + __visit_name__ = 'join' + + _is_join = True + + def __init__(self, left, right, onclause=None, isouter=False, full=False): + """Construct a new :class:`.Join`. + + The usual entrypoint here is the :func:`~.expression.join` + function or the :meth:`.FromClause.join` method of any + :class:`.FromClause` object. + + """ + self.left = _interpret_as_from(left) + self.right = _interpret_as_from(right).self_group() + + if onclause is None: + self.onclause = self._match_primaries(self.left, self.right) + else: + self.onclause = onclause + + self.isouter = isouter + self.full = full + + @classmethod + def _create_outerjoin(cls, left, right, onclause=None, full=False): + """Return an ``OUTER JOIN`` clause element. + + The returned object is an instance of :class:`.Join`. + + Similar functionality is also available via the + :meth:`~.FromClause.outerjoin()` method on any + :class:`.FromClause`. + + :param left: The left side of the join. + + :param right: The right side of the join. + + :param onclause: Optional criterion for the ``ON`` clause, is + derived from foreign key relationships established between + left and right otherwise. + + To chain joins together, use the :meth:`.FromClause.join` or + :meth:`.FromClause.outerjoin` methods on the resulting + :class:`.Join` object. + + """ + return cls(left, right, onclause, isouter=True, full=full) + + @classmethod + def _create_join(cls, left, right, onclause=None, isouter=False, + full=False): + """Produce a :class:`.Join` object, given two :class:`.FromClause` + expressions. + + E.g.:: + + j = join(user_table, address_table, + user_table.c.id == address_table.c.user_id) + stmt = select([user_table]).select_from(j) + + would emit SQL along the lines of:: + + SELECT user.id, user.name FROM user + JOIN address ON user.id = address.user_id + + Similar functionality is available given any + :class:`.FromClause` object (e.g. such as a :class:`.Table`) using + the :meth:`.FromClause.join` method. + + :param left: The left side of the join. + + :param right: the right side of the join; this is any + :class:`.FromClause` object such as a :class:`.Table` object, and + may also be a selectable-compatible object such as an ORM-mapped + class. + + :param onclause: a SQL expression representing the ON clause of the + join. If left at ``None``, :meth:`.FromClause.join` will attempt to + join the two tables based on a foreign key relationship. + + :param isouter: if True, render a LEFT OUTER JOIN, instead of JOIN. + + :param full: if True, render a FULL OUTER JOIN, instead of JOIN. + + .. versionadded:: 1.1 + + .. seealso:: + + :meth:`.FromClause.join` - method form, based on a given left side + + :class:`.Join` - the type of object produced + + """ + + return cls(left, right, onclause, isouter, full) + + @property + def description(self): + return "Join object on %s(%d) and %s(%d)" % ( + self.left.description, + id(self.left), + self.right.description, + id(self.right)) + + def is_derived_from(self, fromclause): + return fromclause is self or \ + self.left.is_derived_from(fromclause) or \ + self.right.is_derived_from(fromclause) + + def self_group(self, against=None): + return FromGrouping(self) + + @util.dependencies("sqlalchemy.sql.util") + def _populate_column_collection(self, sqlutil): + columns = [c for c in self.left.columns] + \ + [c for c in self.right.columns] + + self.primary_key.extend(sqlutil.reduce_columns( + (c for c in columns if c.primary_key), self.onclause)) + self._columns.update((col._label, col) for col in columns) + self.foreign_keys.update(itertools.chain( + *[col.foreign_keys for col in columns])) + + def _refresh_for_new_column(self, column): + col = self.left._refresh_for_new_column(column) + if col is None: + col = self.right._refresh_for_new_column(column) + if col is not None: + if self._cols_populated: + self._columns[col._label] = col + self.foreign_keys.update(col.foreign_keys) + if col.primary_key: + self.primary_key.add(col) + return col + return None + + def _copy_internals(self, clone=_clone, **kw): + self._reset_exported() + self.left = clone(self.left, **kw) + self.right = clone(self.right, **kw) + self.onclause = clone(self.onclause, **kw) + + def get_children(self, **kwargs): + return self.left, self.right, self.onclause + + def _match_primaries(self, left, right): + if isinstance(left, Join): + left_right = left.right + else: + left_right = None + return self._join_condition(left, right, a_subset=left_right) + + @classmethod + def _join_condition(cls, a, b, ignore_nonexistent_tables=False, + a_subset=None, + consider_as_foreign_keys=None): + """create a join condition between two tables or selectables. + + e.g.:: + + join_condition(tablea, tableb) + + would produce an expression along the lines of:: + + tablea.c.id==tableb.c.tablea_id + + The join is determined based on the foreign key relationships + between the two selectables. If there are multiple ways + to join, or no way to join, an error is raised. + + :param ignore_nonexistent_tables: Deprecated - this + flag is no longer used. Only resolution errors regarding + the two given tables are propagated. + + :param a_subset: An optional expression that is a sub-component + of ``a``. An attempt will be made to join to just this sub-component + first before looking at the full ``a`` construct, and if found + will be successful even if there are other ways to join to ``a``. + This allows the "right side" of a join to be passed thereby + providing a "natural join". + + """ + constraints = cls._joincond_scan_left_right( + a, a_subset, b, consider_as_foreign_keys) + + if len(constraints) > 1: + cls._joincond_trim_constraints( + a, b, constraints, consider_as_foreign_keys) + + if len(constraints) == 0: + if isinstance(b, FromGrouping): + hint = " Perhaps you meant to convert the right side to a "\ + "subquery using alias()?" + else: + hint = "" + raise exc.NoForeignKeysError( + "Can't find any foreign key relationships " + "between '%s' and '%s'.%s" % + (a.description, b.description, hint)) + + crit = [(x == y) for x, y in list(constraints.values())[0]] + if len(crit) == 1: + return (crit[0]) + else: + return and_(*crit) + + @classmethod + def _joincond_scan_left_right( + cls, a, a_subset, b, consider_as_foreign_keys): + constraints = collections.defaultdict(list) + + for left in (a_subset, a): + if left is None: + continue + for fk in sorted( + b.foreign_keys, + key=lambda fk: fk.parent._creation_order): + if consider_as_foreign_keys is not None and \ + fk.parent not in consider_as_foreign_keys: + continue + try: + col = fk.get_referent(left) + except exc.NoReferenceError as nrte: + if nrte.table_name == left.name: + raise + else: + continue + + if col is not None: + constraints[fk.constraint].append((col, fk.parent)) + if left is not b: + for fk in sorted( + left.foreign_keys, + key=lambda fk: fk.parent._creation_order): + if consider_as_foreign_keys is not None and \ + fk.parent not in consider_as_foreign_keys: + continue + try: + col = fk.get_referent(b) + except exc.NoReferenceError as nrte: + if nrte.table_name == b.name: + raise + else: + continue + + if col is not None: + constraints[fk.constraint].append((col, fk.parent)) + if constraints: + break + return constraints + + @classmethod + def _joincond_trim_constraints( + cls, a, b, constraints, consider_as_foreign_keys): + # more than one constraint matched. narrow down the list + # to include just those FKCs that match exactly to + # "consider_as_foreign_keys". + if consider_as_foreign_keys: + for const in list(constraints): + if set(f.parent for f in const.elements) != set( + consider_as_foreign_keys): + del constraints[const] + + # if still multiple constraints, but + # they all refer to the exact same end result, use it. + if len(constraints) > 1: + dedupe = set(tuple(crit) for crit in constraints.values()) + if len(dedupe) == 1: + key = list(constraints)[0] + constraints = {key: constraints[key]} + + if len(constraints) != 1: + raise exc.AmbiguousForeignKeysError( + "Can't determine join between '%s' and '%s'; " + "tables have more than one foreign key " + "constraint relationship between them. " + "Please specify the 'onclause' of this " + "join explicitly." % (a.description, b.description)) + + def select(self, whereclause=None, **kwargs): + r"""Create a :class:`.Select` from this :class:`.Join`. + + The equivalent long-hand form, given a :class:`.Join` object + ``j``, is:: + + from sqlalchemy import select + j = select([j.left, j.right], **kw).\ + where(whereclause).\ + select_from(j) + + :param whereclause: the WHERE criterion that will be sent to + the :func:`select()` function + + :param \**kwargs: all other kwargs are sent to the + underlying :func:`select()` function. + + """ + collist = [self.left, self.right] + + return Select(collist, whereclause, from_obj=[self], **kwargs) + + @property + def bind(self): + return self.left.bind or self.right.bind + + @util.dependencies("sqlalchemy.sql.util") + def alias(self, sqlutil, name=None, flat=False): + r"""return an alias of this :class:`.Join`. + + The default behavior here is to first produce a SELECT + construct from this :class:`.Join`, then to produce an + :class:`.Alias` from that. So given a join of the form:: + + j = table_a.join(table_b, table_a.c.id == table_b.c.a_id) + + The JOIN by itself would look like:: + + table_a JOIN table_b ON table_a.id = table_b.a_id + + Whereas the alias of the above, ``j.alias()``, would in a + SELECT context look like:: + + (SELECT table_a.id AS table_a_id, table_b.id AS table_b_id, + table_b.a_id AS table_b_a_id + FROM table_a + JOIN table_b ON table_a.id = table_b.a_id) AS anon_1 + + The equivalent long-hand form, given a :class:`.Join` object + ``j``, is:: + + from sqlalchemy import select, alias + j = alias( + select([j.left, j.right]).\ + select_from(j).\ + with_labels(True).\ + correlate(False), + name=name + ) + + The selectable produced by :meth:`.Join.alias` features the same + columns as that of the two individual selectables presented under + a single name - the individual columns are "auto-labeled", meaning + the ``.c.`` collection of the resulting :class:`.Alias` represents + the names of the individual columns using a + ``<tablename>_<columname>`` scheme:: + + j.c.table_a_id + j.c.table_b_a_id + + :meth:`.Join.alias` also features an alternate + option for aliasing joins which produces no enclosing SELECT and + does not normally apply labels to the column names. The + ``flat=True`` option will call :meth:`.FromClause.alias` + against the left and right sides individually. + Using this option, no new ``SELECT`` is produced; + we instead, from a construct as below:: + + j = table_a.join(table_b, table_a.c.id == table_b.c.a_id) + j = j.alias(flat=True) + + we get a result like this:: + + table_a AS table_a_1 JOIN table_b AS table_b_1 ON + table_a_1.id = table_b_1.a_id + + The ``flat=True`` argument is also propagated to the contained + selectables, so that a composite join such as:: + + j = table_a.join( + table_b.join(table_c, + table_b.c.id == table_c.c.b_id), + table_b.c.a_id == table_a.c.id + ).alias(flat=True) + + Will produce an expression like:: + + table_a AS table_a_1 JOIN ( + table_b AS table_b_1 JOIN table_c AS table_c_1 + ON table_b_1.id = table_c_1.b_id + ) ON table_a_1.id = table_b_1.a_id + + The standalone :func:`~.expression.alias` function as well as the + base :meth:`.FromClause.alias` method also support the ``flat=True`` + argument as a no-op, so that the argument can be passed to the + ``alias()`` method of any selectable. + + .. versionadded:: 0.9.0 Added the ``flat=True`` option to create + "aliases" of joins without enclosing inside of a SELECT + subquery. + + :param name: name given to the alias. + + :param flat: if True, produce an alias of the left and right + sides of this :class:`.Join` and return the join of those + two selectables. This produces join expression that does not + include an enclosing SELECT. + + .. versionadded:: 0.9.0 + + .. seealso:: + + :func:`~.expression.alias` + + """ + if flat: + assert name is None, "Can't send name argument with flat" + left_a, right_a = self.left.alias(flat=True), \ + self.right.alias(flat=True) + adapter = sqlutil.ClauseAdapter(left_a).\ + chain(sqlutil.ClauseAdapter(right_a)) + + return left_a.join(right_a, adapter.traverse(self.onclause), + isouter=self.isouter, full=self.full) + else: + return self.select(use_labels=True, correlate=False).alias(name) + + @property + def _hide_froms(self): + return itertools.chain(*[_from_objects(x.left, x.right) + for x in self._cloned_set]) + + @property + def _from_objects(self): + return [self] + \ + self.onclause._from_objects + \ + self.left._from_objects + \ + self.right._from_objects + + +class Alias(FromClause): + """Represents an table or selectable alias (AS). + + Represents an alias, as typically applied to any table or + sub-select within a SQL statement using the ``AS`` keyword (or + without the keyword on certain databases such as Oracle). + + This object is constructed from the :func:`~.expression.alias` module + level function as well as the :meth:`.FromClause.alias` method available + on all :class:`.FromClause` subclasses. + + """ + + __visit_name__ = 'alias' + named_with_column = True + + _is_from_container = True + + def __init__(self, selectable, name=None): + baseselectable = selectable + while isinstance(baseselectable, Alias): + baseselectable = baseselectable.element + self.original = baseselectable + self.supports_execution = baseselectable.supports_execution + if self.supports_execution: + self._execution_options = baseselectable._execution_options + self.element = selectable + if name is None: + if self.original.named_with_column: + name = getattr(self.original, 'name', None) + name = _anonymous_label('%%(%d %s)s' % (id(self), name + or 'anon')) + self.name = name + + def self_group(self, against=None): + if isinstance(against, CompoundSelect) and \ + isinstance(self.original, Select) and \ + self.original._needs_parens_for_grouping(): + return FromGrouping(self) + + return super(Alias, self).self_group(against=against) + + @property + def description(self): + if util.py3k: + return self.name + else: + return self.name.encode('ascii', 'backslashreplace') + + def as_scalar(self): + try: + return self.element.as_scalar() + except AttributeError: + raise AttributeError("Element %s does not support " + "'as_scalar()'" % self.element) + + def is_derived_from(self, fromclause): + if fromclause in self._cloned_set: + return True + return self.element.is_derived_from(fromclause) + + def _populate_column_collection(self): + for col in self.element.columns._all_columns: + col._make_proxy(self) + + def _refresh_for_new_column(self, column): + col = self.element._refresh_for_new_column(column) + if col is not None: + if not self._cols_populated: + return None + else: + return col._make_proxy(self) + else: + return None + + def _copy_internals(self, clone=_clone, **kw): + # don't apply anything to an aliased Table + # for now. May want to drive this from + # the given **kw. + if isinstance(self.element, TableClause): + return + self._reset_exported() + self.element = clone(self.element, **kw) + baseselectable = self.element + while isinstance(baseselectable, Alias): + baseselectable = baseselectable.element + self.original = baseselectable + + def get_children(self, column_collections=True, **kw): + if column_collections: + for c in self.c: + yield c + yield self.element + + @property + def _from_objects(self): + return [self] + + @property + def bind(self): + return self.element.bind + + +class Lateral(Alias): + """Represent a LATERAL subquery. + + This object is constructed from the :func:`~.expression.lateral` module + level function as well as the :meth:`.FromClause.lateral` method available + on all :class:`.FromClause` subclasses. + + While LATERAL is part of the SQL standard, curently only more recent + PostgreSQL versions provide support for this keyword. + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`lateral_selects` - overview of usage. + + """ + + __visit_name__ = 'lateral' + _is_lateral = True + + +class TableSample(Alias): + """Represent a TABLESAMPLE clause. + + This object is constructed from the :func:`~.expression.tablesample` module + level function as well as the :meth:`.FromClause.tablesample` method available + on all :class:`.FromClause` subclasses. + + .. versionadded:: 1.1 + + .. seealso:: + + :func:`~.expression.tablesample` + + """ + + __visit_name__ = 'tablesample' + + def __init__(self, selectable, sampling, + name=None, + seed=None): + self.sampling = sampling + self.seed = seed + super(TableSample, self).__init__(selectable, name=name) + + @util.dependencies("sqlalchemy.sql.functions") + def _get_method(self, functions): + if isinstance(self.sampling, functions.Function): + return self.sampling + else: + return functions.func.system(self.sampling) + + +class CTE(Generative, HasSuffixes, Alias): + """Represent a Common Table Expression. + + The :class:`.CTE` object is obtained using the + :meth:`.SelectBase.cte` method from any selectable. + See that method for complete examples. + + .. versionadded:: 0.7.6 + + """ + __visit_name__ = 'cte' + + def __init__(self, selectable, + name=None, + recursive=False, + _cte_alias=None, + _restates=frozenset(), + _suffixes=None): + self.recursive = recursive + self._cte_alias = _cte_alias + self._restates = _restates + if _suffixes: + self._suffixes = _suffixes + super(CTE, self).__init__(selectable, name=name) + + def _copy_internals(self, clone=_clone, **kw): + super(CTE, self)._copy_internals(clone, **kw) + if self._cte_alias is not None: + self._cte_alias = clone(self._cte_alias, **kw) + self._restates = frozenset([ + clone(elem, **kw) for elem in self._restates + ]) + + @util.dependencies("sqlalchemy.sql.dml") + def _populate_column_collection(self, dml): + if isinstance(self.element, dml.UpdateBase): + for col in self.element._returning: + col._make_proxy(self) + else: + for col in self.element.columns._all_columns: + col._make_proxy(self) + + def alias(self, name=None, flat=False): + return CTE( + self.original, + name=name, + recursive=self.recursive, + _cte_alias=self, + _suffixes=self._suffixes + ) + + def union(self, other): + return CTE( + self.original.union(other), + name=self.name, + recursive=self.recursive, + _restates=self._restates.union([self]), + _suffixes=self._suffixes + ) + + def union_all(self, other): + return CTE( + self.original.union_all(other), + name=self.name, + recursive=self.recursive, + _restates=self._restates.union([self]), + _suffixes=self._suffixes + ) + + +class HasCTE(object): + """Mixin that declares a class to include CTE support. + + .. versionadded:: 1.1 + + """ + + def cte(self, name=None, recursive=False): + r"""Return a new :class:`.CTE`, or Common Table Expression instance. + + Common table expressions are a SQL standard whereby SELECT + statements can draw upon secondary statements specified along + with the primary statement, using a clause called "WITH". + Special semantics regarding UNION can also be employed to + allow "recursive" queries, where a SELECT statement can draw + upon the set of rows that have previously been selected. + + CTEs can also be applied to DML constructs UPDATE, INSERT + and DELETE on some databases, both as a source of CTE rows + when combined with RETURNING, as well as a consumer of + CTE rows. + + SQLAlchemy detects :class:`.CTE` objects, which are treated + similarly to :class:`.Alias` objects, as special elements + to be delivered to the FROM clause of the statement as well + as to a WITH clause at the top of the statement. + + .. versionchanged:: 1.1 Added support for UPDATE/INSERT/DELETE as + CTE, CTEs added to UPDATE/INSERT/DELETE. + + :param name: name given to the common table expression. Like + :meth:`._FromClause.alias`, the name can be left as ``None`` + in which case an anonymous symbol will be used at query + compile time. + :param recursive: if ``True``, will render ``WITH RECURSIVE``. + A recursive common table expression is intended to be used in + conjunction with UNION ALL in order to derive rows + from those already selected. + + The following examples include two from PostgreSQL's documentation at + http://www.postgresql.org/docs/current/static/queries-with.html, + as well as additional examples. + + Example 1, non recursive:: + + from sqlalchemy import (Table, Column, String, Integer, + MetaData, select, func) + + metadata = MetaData() + + orders = Table('orders', metadata, + Column('region', String), + Column('amount', Integer), + Column('product', String), + Column('quantity', Integer) + ) + + regional_sales = select([ + orders.c.region, + func.sum(orders.c.amount).label('total_sales') + ]).group_by(orders.c.region).cte("regional_sales") + + + top_regions = select([regional_sales.c.region]).\ + where( + regional_sales.c.total_sales > + select([ + func.sum(regional_sales.c.total_sales)/10 + ]) + ).cte("top_regions") + + statement = select([ + orders.c.region, + orders.c.product, + func.sum(orders.c.quantity).label("product_units"), + func.sum(orders.c.amount).label("product_sales") + ]).where(orders.c.region.in_( + select([top_regions.c.region]) + )).group_by(orders.c.region, orders.c.product) + + result = conn.execute(statement).fetchall() + + Example 2, WITH RECURSIVE:: + + from sqlalchemy import (Table, Column, String, Integer, + MetaData, select, func) + + metadata = MetaData() + + parts = Table('parts', metadata, + Column('part', String), + Column('sub_part', String), + Column('quantity', Integer), + ) + + included_parts = select([ + parts.c.sub_part, + parts.c.part, + parts.c.quantity]).\ + where(parts.c.part=='our part').\ + cte(recursive=True) + + + incl_alias = included_parts.alias() + parts_alias = parts.alias() + included_parts = included_parts.union_all( + select([ + parts_alias.c.sub_part, + parts_alias.c.part, + parts_alias.c.quantity + ]). + where(parts_alias.c.part==incl_alias.c.sub_part) + ) + + statement = select([ + included_parts.c.sub_part, + func.sum(included_parts.c.quantity). + label('total_quantity') + ]).\ + group_by(included_parts.c.sub_part) + + result = conn.execute(statement).fetchall() + + Example 3, an upsert using UPDATE and INSERT with CTEs:: + + from datetime import date + from sqlalchemy import (MetaData, Table, Column, Integer, + Date, select, literal, and_, exists) + + metadata = MetaData() + + visitors = Table('visitors', metadata, + Column('product_id', Integer, primary_key=True), + Column('date', Date, primary_key=True), + Column('count', Integer), + ) + + # add 5 visitors for the product_id == 1 + product_id = 1 + day = date.today() + count = 5 + + update_cte = ( + visitors.update() + .where(and_(visitors.c.product_id == product_id, + visitors.c.date == day)) + .values(count=visitors.c.count + count) + .returning(literal(1)) + .cte('update_cte') + ) + + upsert = visitors.insert().from_select( + [visitors.c.product_id, visitors.c.date, visitors.c.count], + select([literal(product_id), literal(day), literal(count)]) + .where(~exists(update_cte.select())) + ) + + connection.execute(upsert) + + .. seealso:: + + :meth:`.orm.query.Query.cte` - ORM version of + :meth:`.HasCTE.cte`. + + """ + return CTE(self, name=name, recursive=recursive) + + +class FromGrouping(FromClause): + """Represent a grouping of a FROM clause""" + __visit_name__ = 'grouping' + + def __init__(self, element): + self.element = element + + def _init_collections(self): + pass + + @property + def columns(self): + return self.element.columns + + @property + def primary_key(self): + return self.element.primary_key + + @property + def foreign_keys(self): + return self.element.foreign_keys + + def is_derived_from(self, element): + return self.element.is_derived_from(element) + + def alias(self, **kw): + return FromGrouping(self.element.alias(**kw)) + + @property + def _hide_froms(self): + return self.element._hide_froms + + def get_children(self, **kwargs): + return self.element, + + def _copy_internals(self, clone=_clone, **kw): + self.element = clone(self.element, **kw) + + @property + def _from_objects(self): + return self.element._from_objects + + def __getattr__(self, attr): + return getattr(self.element, attr) + + def __getstate__(self): + return {'element': self.element} + + def __setstate__(self, state): + self.element = state['element'] + + +class TableClause(Immutable, FromClause): + """Represents a minimal "table" construct. + + This is a lightweight table object that has only a name and a + collection of columns, which are typically produced + by the :func:`.expression.column` function:: + + from sqlalchemy import table, column + + user = table("user", + column("id"), + column("name"), + column("description"), + ) + + The :class:`.TableClause` construct serves as the base for + the more commonly used :class:`~.schema.Table` object, providing + the usual set of :class:`~.expression.FromClause` services including + the ``.c.`` collection and statement generation methods. + + It does **not** provide all the additional schema-level services + of :class:`~.schema.Table`, including constraints, references to other + tables, or support for :class:`.MetaData`-level services. It's useful + on its own as an ad-hoc construct used to generate quick SQL + statements when a more fully fledged :class:`~.schema.Table` + is not on hand. + + """ + + __visit_name__ = 'table' + + named_with_column = True + + implicit_returning = False + """:class:`.TableClause` doesn't support having a primary key or column + -level defaults, so implicit returning doesn't apply.""" + + _autoincrement_column = None + """No PK or default support so no autoincrement column.""" + + def __init__(self, name, *columns): + """Produce a new :class:`.TableClause`. + + The object returned is an instance of :class:`.TableClause`, which + represents the "syntactical" portion of the schema-level + :class:`~.schema.Table` object. + It may be used to construct lightweight table constructs. + + .. versionchanged:: 1.0.0 :func:`.expression.table` can now + be imported from the plain ``sqlalchemy`` namespace like any + other SQL element. + + :param name: Name of the table. + + :param columns: A collection of :func:`.expression.column` constructs. + + """ + + super(TableClause, self).__init__() + self.name = self.fullname = name + self._columns = ColumnCollection() + self.primary_key = ColumnSet() + self.foreign_keys = set() + for c in columns: + self.append_column(c) + + def _init_collections(self): + pass + + @util.memoized_property + def description(self): + if util.py3k: + return self.name + else: + return self.name.encode('ascii', 'backslashreplace') + + def append_column(self, c): + self._columns[c.key] = c + c.table = self + + def get_children(self, column_collections=True, **kwargs): + if column_collections: + return [c for c in self.c] + else: + return [] + + @util.dependencies("sqlalchemy.sql.dml") + def insert(self, dml, values=None, inline=False, **kwargs): + """Generate an :func:`.insert` construct against this + :class:`.TableClause`. + + E.g.:: + + table.insert().values(name='foo') + + See :func:`.insert` for argument and usage information. + + """ + + return dml.Insert(self, values=values, inline=inline, **kwargs) + + @util.dependencies("sqlalchemy.sql.dml") + def update( + self, dml, whereclause=None, values=None, inline=False, **kwargs): + """Generate an :func:`.update` construct against this + :class:`.TableClause`. + + E.g.:: + + table.update().where(table.c.id==7).values(name='foo') + + See :func:`.update` for argument and usage information. + + """ + + return dml.Update(self, whereclause=whereclause, + values=values, inline=inline, **kwargs) + + @util.dependencies("sqlalchemy.sql.dml") + def delete(self, dml, whereclause=None, **kwargs): + """Generate a :func:`.delete` construct against this + :class:`.TableClause`. + + E.g.:: + + table.delete().where(table.c.id==7) + + See :func:`.delete` for argument and usage information. + + """ + + return dml.Delete(self, whereclause, **kwargs) + + @property + def _from_objects(self): + return [self] + + +class ForUpdateArg(ClauseElement): + + @classmethod + def parse_legacy_select(self, arg): + """Parse the for_update argument of :func:`.select`. + + :param mode: Defines the lockmode to use. + + ``None`` - translates to no lockmode + + ``'update'`` - translates to ``FOR UPDATE`` + (standard SQL, supported by most dialects) + + ``'nowait'`` - translates to ``FOR UPDATE NOWAIT`` + (supported by Oracle, PostgreSQL 8.1 upwards) + + ``'read'`` - translates to ``LOCK IN SHARE MODE`` (for MySQL), + and ``FOR SHARE`` (for PostgreSQL) + + ``'read_nowait'`` - translates to ``FOR SHARE NOWAIT`` + (supported by PostgreSQL). ``FOR SHARE`` and + ``FOR SHARE NOWAIT`` (PostgreSQL). + + """ + if arg in (None, False): + return None + + nowait = read = False + if arg == 'nowait': + nowait = True + elif arg == 'read': + read = True + elif arg == 'read_nowait': + read = nowait = True + elif arg is not True: + raise exc.ArgumentError("Unknown for_update argument: %r" % arg) + + return ForUpdateArg(read=read, nowait=nowait) + + @property + def legacy_for_update_value(self): + if self.read and not self.nowait: + return "read" + elif self.read and self.nowait: + return "read_nowait" + elif self.nowait: + return "nowait" + else: + return True + + def __eq__(self, other): + return ( + isinstance(other, ForUpdateArg) and + other.nowait == self.nowait and + other.read == self.read and + other.skip_locked == self.skip_locked and + other.key_share == self.key_share and + other.of is self.of + ) + + def __hash__(self): + return id(self) + + def _copy_internals(self, clone=_clone, **kw): + if self.of is not None: + self.of = [clone(col, **kw) for col in self.of] + + def __init__( + self, nowait=False, read=False, of=None, + skip_locked=False, key_share=False): + """Represents arguments specified to :meth:`.Select.for_update`. + + .. versionadded:: 0.9.0 + + """ + + self.nowait = nowait + self.read = read + self.skip_locked = skip_locked + self.key_share = key_share + if of is not None: + self.of = [_interpret_as_column_or_from(elem) + for elem in util.to_list(of)] + else: + self.of = None + + +class SelectBase(HasCTE, Executable, FromClause): + """Base class for SELECT statements. + + + This includes :class:`.Select`, :class:`.CompoundSelect` and + :class:`.TextAsFrom`. + + + """ + + def as_scalar(self): + """return a 'scalar' representation of this selectable, which can be + used as a column expression. + + Typically, a select statement which has only one column in its columns + clause is eligible to be used as a scalar expression. + + The returned object is an instance of + :class:`ScalarSelect`. + + """ + return ScalarSelect(self) + + def label(self, name): + """return a 'scalar' representation of this selectable, embedded as a + subquery with a label. + + .. seealso:: + + :meth:`~.SelectBase.as_scalar`. + + """ + return self.as_scalar().label(name) + + @_generative + @util.deprecated('0.6', + message="``autocommit()`` is deprecated. Use " + ":meth:`.Executable.execution_options` with the " + "'autocommit' flag.") + def autocommit(self): + """return a new selectable with the 'autocommit' flag set to + True. + """ + + self._execution_options = \ + self._execution_options.union({'autocommit': True}) + + def _generate(self): + """Override the default _generate() method to also clear out + exported collections.""" + + s = self.__class__.__new__(self.__class__) + s.__dict__ = self.__dict__.copy() + s._reset_exported() + return s + + @property + def _from_objects(self): + return [self] + + +class GenerativeSelect(SelectBase): + """Base class for SELECT statements where additional elements can be + added. + + This serves as the base for :class:`.Select` and :class:`.CompoundSelect` + where elements such as ORDER BY, GROUP BY can be added and column + rendering can be controlled. Compare to :class:`.TextAsFrom`, which, + while it subclasses :class:`.SelectBase` and is also a SELECT construct, + represents a fixed textual string which cannot be altered at this level, + only wrapped as a subquery. + + .. versionadded:: 0.9.0 :class:`.GenerativeSelect` was added to + provide functionality specific to :class:`.Select` and + :class:`.CompoundSelect` while allowing :class:`.SelectBase` to be + used for other SELECT-like objects, e.g. :class:`.TextAsFrom`. + + """ + _order_by_clause = ClauseList() + _group_by_clause = ClauseList() + _limit_clause = None + _offset_clause = None + _for_update_arg = None + + def __init__(self, + use_labels=False, + for_update=False, + limit=None, + offset=None, + order_by=None, + group_by=None, + bind=None, + autocommit=None): + self.use_labels = use_labels + + if for_update is not False: + self._for_update_arg = (ForUpdateArg. + parse_legacy_select(for_update)) + + if autocommit is not None: + util.warn_deprecated('autocommit on select() is ' + 'deprecated. Use .execution_options(a' + 'utocommit=True)') + self._execution_options = \ + self._execution_options.union( + {'autocommit': autocommit}) + if limit is not None: + self._limit_clause = _offset_or_limit_clause(limit) + if offset is not None: + self._offset_clause = _offset_or_limit_clause(offset) + self._bind = bind + + if order_by is not None: + self._order_by_clause = ClauseList( + *util.to_list(order_by), + _literal_as_text=_literal_and_labels_as_label_reference) + if group_by is not None: + self._group_by_clause = ClauseList( + *util.to_list(group_by), + _literal_as_text=_literal_as_label_reference) + + @property + def for_update(self): + """Provide legacy dialect support for the ``for_update`` attribute. + """ + if self._for_update_arg is not None: + return self._for_update_arg.legacy_for_update_value + else: + return None + + @for_update.setter + def for_update(self, value): + self._for_update_arg = ForUpdateArg.parse_legacy_select(value) + + @_generative + def with_for_update(self, nowait=False, read=False, of=None, + skip_locked=False, key_share=False): + """Specify a ``FOR UPDATE`` clause for this :class:`.GenerativeSelect`. + + E.g.:: + + stmt = select([table]).with_for_update(nowait=True) + + On a database like PostgreSQL or Oracle, the above would render a + statement like:: + + SELECT table.a, table.b FROM table FOR UPDATE NOWAIT + + on other backends, the ``nowait`` option is ignored and instead + would produce:: + + SELECT table.a, table.b FROM table FOR UPDATE + + When called with no arguments, the statement will render with + the suffix ``FOR UPDATE``. Additional arguments can then be + provided which allow for common database-specific + variants. + + :param nowait: boolean; will render ``FOR UPDATE NOWAIT`` on Oracle + and PostgreSQL dialects. + + :param read: boolean; will render ``LOCK IN SHARE MODE`` on MySQL, + ``FOR SHARE`` on PostgreSQL. On PostgreSQL, when combined with + ``nowait``, will render ``FOR SHARE NOWAIT``. + + :param of: SQL expression or list of SQL expression elements + (typically :class:`.Column` objects or a compatible expression) which + will render into a ``FOR UPDATE OF`` clause; supported by PostgreSQL + and Oracle. May render as a table or as a column depending on + backend. + + :param skip_locked: boolean, will render ``FOR UPDATE SKIP LOCKED`` + on Oracle and PostgreSQL dialects or ``FOR SHARE SKIP LOCKED`` if + ``read=True`` is also specified. + + .. versionadded:: 1.1.0 + + :param key_share: boolean, will render ``FOR NO KEY UPDATE``, + or if combined with ``read=True`` will render ``FOR KEY SHARE``, + on the PostgreSQL dialect. + + .. versionadded:: 1.1.0 + + """ + self._for_update_arg = ForUpdateArg(nowait=nowait, read=read, of=of, + skip_locked=skip_locked, + key_share=key_share) + + @_generative + def apply_labels(self): + """return a new selectable with the 'use_labels' flag set to True. + + This will result in column expressions being generated using labels + against their table name, such as "SELECT somecolumn AS + tablename_somecolumn". This allows selectables which contain multiple + FROM clauses to produce a unique set of column names regardless of + name conflicts among the individual FROM clauses. + + """ + self.use_labels = True + + @property + def _limit(self): + """Get an integer value for the limit. This should only be used + by code that cannot support a limit as a BindParameter or + other custom clause as it will throw an exception if the limit + isn't currently set to an integer. + + """ + return _offset_or_limit_clause_asint(self._limit_clause, "limit") + + @property + def _simple_int_limit(self): + """True if the LIMIT clause is a simple integer, False + if it is not present or is a SQL expression. + """ + return isinstance(self._limit_clause, _OffsetLimitParam) + + @property + def _simple_int_offset(self): + """True if the OFFSET clause is a simple integer, False + if it is not present or is a SQL expression. + """ + return isinstance(self._offset_clause, _OffsetLimitParam) + + @property + def _offset(self): + """Get an integer value for the offset. This should only be used + by code that cannot support an offset as a BindParameter or + other custom clause as it will throw an exception if the + offset isn't currently set to an integer. + + """ + return _offset_or_limit_clause_asint(self._offset_clause, "offset") + + @_generative + def limit(self, limit): + """return a new selectable with the given LIMIT criterion + applied. + + This is a numerical value which usually renders as a ``LIMIT`` + expression in the resulting select. Backends that don't + support ``LIMIT`` will attempt to provide similar + functionality. + + .. versionchanged:: 1.0.0 - :meth:`.Select.limit` can now + accept arbitrary SQL expressions as well as integer values. + + :param limit: an integer LIMIT parameter, or a SQL expression + that provides an integer result. + + """ + + self._limit_clause = _offset_or_limit_clause(limit) + + @_generative + def offset(self, offset): + """return a new selectable with the given OFFSET criterion + applied. + + + This is a numeric value which usually renders as an ``OFFSET`` + expression in the resulting select. Backends that don't + support ``OFFSET`` will attempt to provide similar + functionality. + + + .. versionchanged:: 1.0.0 - :meth:`.Select.offset` can now + accept arbitrary SQL expressions as well as integer values. + + :param offset: an integer OFFSET parameter, or a SQL expression + that provides an integer result. + + """ + + self._offset_clause = _offset_or_limit_clause(offset) + + @_generative + def order_by(self, *clauses): + """return a new selectable with the given list of ORDER BY + criterion applied. + + The criterion will be appended to any pre-existing ORDER BY + criterion. + + """ + + self.append_order_by(*clauses) + + @_generative + def group_by(self, *clauses): + """return a new selectable with the given list of GROUP BY + criterion applied. + + The criterion will be appended to any pre-existing GROUP BY + criterion. + + """ + + self.append_group_by(*clauses) + + def append_order_by(self, *clauses): + """Append the given ORDER BY criterion applied to this selectable. + + The criterion will be appended to any pre-existing ORDER BY criterion. + + This is an **in-place** mutation method; the + :meth:`~.GenerativeSelect.order_by` method is preferred, as it + provides standard :term:`method chaining`. + + """ + if len(clauses) == 1 and clauses[0] is None: + self._order_by_clause = ClauseList() + else: + if getattr(self, '_order_by_clause', None) is not None: + clauses = list(self._order_by_clause) + list(clauses) + self._order_by_clause = ClauseList( + *clauses, + _literal_as_text=_literal_and_labels_as_label_reference) + + def append_group_by(self, *clauses): + """Append the given GROUP BY criterion applied to this selectable. + + The criterion will be appended to any pre-existing GROUP BY criterion. + + This is an **in-place** mutation method; the + :meth:`~.GenerativeSelect.group_by` method is preferred, as it + provides standard :term:`method chaining`. + + """ + if len(clauses) == 1 and clauses[0] is None: + self._group_by_clause = ClauseList() + else: + if getattr(self, '_group_by_clause', None) is not None: + clauses = list(self._group_by_clause) + list(clauses) + self._group_by_clause = ClauseList( + *clauses, _literal_as_text=_literal_as_label_reference) + + @property + def _label_resolve_dict(self): + raise NotImplementedError() + + def _copy_internals(self, clone=_clone, **kw): + if self._limit_clause is not None: + self._limit_clause = clone(self._limit_clause, **kw) + if self._offset_clause is not None: + self._offset_clause = clone(self._offset_clause, **kw) + + +class CompoundSelect(GenerativeSelect): + """Forms the basis of ``UNION``, ``UNION ALL``, and other + SELECT-based set operations. + + + .. seealso:: + + :func:`.union` + + :func:`.union_all` + + :func:`.intersect` + + :func:`.intersect_all` + + :func:`.except` + + :func:`.except_all` + + """ + + __visit_name__ = 'compound_select' + + UNION = util.symbol('UNION') + UNION_ALL = util.symbol('UNION ALL') + EXCEPT = util.symbol('EXCEPT') + EXCEPT_ALL = util.symbol('EXCEPT ALL') + INTERSECT = util.symbol('INTERSECT') + INTERSECT_ALL = util.symbol('INTERSECT ALL') + + _is_from_container = True + + def __init__(self, keyword, *selects, **kwargs): + self._auto_correlate = kwargs.pop('correlate', False) + self.keyword = keyword + self.selects = [] + + numcols = None + + # some DBs do not like ORDER BY in the inner queries of a UNION, etc. + for n, s in enumerate(selects): + s = _clause_element_as_expr(s) + + if not numcols: + numcols = len(s.c._all_columns) + elif len(s.c._all_columns) != numcols: + raise exc.ArgumentError( + 'All selectables passed to ' + 'CompoundSelect must have identical numbers of ' + 'columns; select #%d has %d columns, select ' + '#%d has %d' % + (1, len(self.selects[0].c._all_columns), + n + 1, len(s.c._all_columns)) + ) + + self.selects.append(s.self_group(against=self)) + + GenerativeSelect.__init__(self, **kwargs) + + @property + def _label_resolve_dict(self): + d = dict( + (c.key, c) for c in self.c + ) + return d, d, d + + @classmethod + def _create_union(cls, *selects, **kwargs): + r"""Return a ``UNION`` of multiple selectables. + + The returned object is an instance of + :class:`.CompoundSelect`. + + A similar :func:`union()` method is available on all + :class:`.FromClause` subclasses. + + \*selects + a list of :class:`.Select` instances. + + \**kwargs + available keyword arguments are the same as those of + :func:`select`. + + """ + return CompoundSelect(CompoundSelect.UNION, *selects, **kwargs) + + @classmethod + def _create_union_all(cls, *selects, **kwargs): + r"""Return a ``UNION ALL`` of multiple selectables. + + The returned object is an instance of + :class:`.CompoundSelect`. + + A similar :func:`union_all()` method is available on all + :class:`.FromClause` subclasses. + + \*selects + a list of :class:`.Select` instances. + + \**kwargs + available keyword arguments are the same as those of + :func:`select`. + + """ + return CompoundSelect(CompoundSelect.UNION_ALL, *selects, **kwargs) + + @classmethod + def _create_except(cls, *selects, **kwargs): + r"""Return an ``EXCEPT`` of multiple selectables. + + The returned object is an instance of + :class:`.CompoundSelect`. + + \*selects + a list of :class:`.Select` instances. + + \**kwargs + available keyword arguments are the same as those of + :func:`select`. + + """ + return CompoundSelect(CompoundSelect.EXCEPT, *selects, **kwargs) + + @classmethod + def _create_except_all(cls, *selects, **kwargs): + r"""Return an ``EXCEPT ALL`` of multiple selectables. + + The returned object is an instance of + :class:`.CompoundSelect`. + + \*selects + a list of :class:`.Select` instances. + + \**kwargs + available keyword arguments are the same as those of + :func:`select`. + + """ + return CompoundSelect(CompoundSelect.EXCEPT_ALL, *selects, **kwargs) + + @classmethod + def _create_intersect(cls, *selects, **kwargs): + r"""Return an ``INTERSECT`` of multiple selectables. + + The returned object is an instance of + :class:`.CompoundSelect`. + + \*selects + a list of :class:`.Select` instances. + + \**kwargs + available keyword arguments are the same as those of + :func:`select`. + + """ + return CompoundSelect(CompoundSelect.INTERSECT, *selects, **kwargs) + + @classmethod + def _create_intersect_all(cls, *selects, **kwargs): + r"""Return an ``INTERSECT ALL`` of multiple selectables. + + The returned object is an instance of + :class:`.CompoundSelect`. + + \*selects + a list of :class:`.Select` instances. + + \**kwargs + available keyword arguments are the same as those of + :func:`select`. + + """ + return CompoundSelect( + CompoundSelect.INTERSECT_ALL, *selects, **kwargs) + + def _scalar_type(self): + return self.selects[0]._scalar_type() + + def self_group(self, against=None): + return FromGrouping(self) + + def is_derived_from(self, fromclause): + for s in self.selects: + if s.is_derived_from(fromclause): + return True + return False + + def _populate_column_collection(self): + for cols in zip(*[s.c._all_columns for s in self.selects]): + + # this is a slightly hacky thing - the union exports a + # column that resembles just that of the *first* selectable. + # to get at a "composite" column, particularly foreign keys, + # you have to dig through the proxies collection which we + # generate below. We may want to improve upon this, such as + # perhaps _make_proxy can accept a list of other columns + # that are "shared" - schema.column can then copy all the + # ForeignKeys in. this would allow the union() to have all + # those fks too. + + proxy = cols[0]._make_proxy( + self, name=cols[0]._label if self.use_labels else None, + key=cols[0]._key_label if self.use_labels else None) + + # hand-construct the "_proxies" collection to include all + # derived columns place a 'weight' annotation corresponding + # to how low in the list of select()s the column occurs, so + # that the corresponding_column() operation can resolve + # conflicts + + proxy._proxies = [ + c._annotate({'weight': i + 1}) for (i, c) in enumerate(cols)] + + def _refresh_for_new_column(self, column): + for s in self.selects: + s._refresh_for_new_column(column) + + if not self._cols_populated: + return None + + raise NotImplementedError("CompoundSelect constructs don't support " + "addition of columns to underlying " + "selectables") + + def _copy_internals(self, clone=_clone, **kw): + super(CompoundSelect, self)._copy_internals(clone, **kw) + self._reset_exported() + self.selects = [clone(s, **kw) for s in self.selects] + if hasattr(self, '_col_map'): + del self._col_map + for attr in ( + '_order_by_clause', '_group_by_clause', '_for_update_arg'): + if getattr(self, attr) is not None: + setattr(self, attr, clone(getattr(self, attr), **kw)) + + def get_children(self, column_collections=True, **kwargs): + return (column_collections and list(self.c) or []) \ + + [self._order_by_clause, self._group_by_clause] \ + + list(self.selects) + + def bind(self): + if self._bind: + return self._bind + for s in self.selects: + e = s.bind + if e: + return e + else: + return None + + def _set_bind(self, bind): + self._bind = bind + bind = property(bind, _set_bind) + + +class Select(HasPrefixes, HasSuffixes, GenerativeSelect): + """Represents a ``SELECT`` statement. + + """ + + __visit_name__ = 'select' + + _prefixes = () + _suffixes = () + _hints = util.immutabledict() + _statement_hints = () + _distinct = False + _from_cloned = None + _correlate = () + _correlate_except = None + _memoized_property = SelectBase._memoized_property + _is_select = True + + def __init__(self, + columns=None, + whereclause=None, + from_obj=None, + distinct=False, + having=None, + correlate=True, + prefixes=None, + suffixes=None, + **kwargs): + """Construct a new :class:`.Select`. + + Similar functionality is also available via the + :meth:`.FromClause.select` method on any :class:`.FromClause`. + + All arguments which accept :class:`.ClauseElement` arguments also + accept string arguments, which will be converted as appropriate into + either :func:`text()` or :func:`literal_column()` constructs. + + .. seealso:: + + :ref:`coretutorial_selecting` - Core Tutorial description of + :func:`.select`. + + :param columns: + A list of :class:`.ColumnElement` or :class:`.FromClause` + objects which will form the columns clause of the resulting + statement. For those objects that are instances of + :class:`.FromClause` (typically :class:`.Table` or :class:`.Alias` + objects), the :attr:`.FromClause.c` collection is extracted + to form a collection of :class:`.ColumnElement` objects. + + This parameter will also accept :class:`.Text` constructs as + given, as well as ORM-mapped classes. + + .. note:: + + The :paramref:`.select.columns` parameter is not available + in the method form of :func:`.select`, e.g. + :meth:`.FromClause.select`. + + .. seealso:: + + :meth:`.Select.column` + + :meth:`.Select.with_only_columns` + + :param whereclause: + A :class:`.ClauseElement` expression which will be used to form the + ``WHERE`` clause. It is typically preferable to add WHERE + criterion to an existing :class:`.Select` using method chaining + with :meth:`.Select.where`. + + .. seealso:: + + :meth:`.Select.where` + + :param from_obj: + A list of :class:`.ClauseElement` objects which will be added to the + ``FROM`` clause of the resulting statement. This is equivalent + to calling :meth:`.Select.select_from` using method chaining on + an existing :class:`.Select` object. + + .. seealso:: + + :meth:`.Select.select_from` - full description of explicit + FROM clause specification. + + :param autocommit: + Deprecated. Use ``.execution_options(autocommit=<True|False>)`` + to set the autocommit option. + + .. seealso:: + + :meth:`.Executable.execution_options` + + :param bind=None: + an :class:`~.Engine` or :class:`~.Connection` instance + to which the + resulting :class:`.Select` object will be bound. The + :class:`.Select` object will otherwise automatically bind to + whatever :class:`~.base.Connectable` instances can be located within + its contained :class:`.ClauseElement` members. + + :param correlate=True: + indicates that this :class:`.Select` object should have its + contained :class:`.FromClause` elements "correlated" to an enclosing + :class:`.Select` object. It is typically preferable to specify + correlations on an existing :class:`.Select` construct using + :meth:`.Select.correlate`. + + .. seealso:: + + :meth:`.Select.correlate` - full description of correlation. + + :param distinct=False: + when ``True``, applies a ``DISTINCT`` qualifier to the columns + clause of the resulting statement. + + The boolean argument may also be a column expression or list + of column expressions - this is a special calling form which + is understood by the PostgreSQL dialect to render the + ``DISTINCT ON (<columns>)`` syntax. + + ``distinct`` is also available on an existing :class:`.Select` + object via the :meth:`~.Select.distinct` method. + + .. seealso:: + + :meth:`.Select.distinct` + + :param for_update=False: + when ``True``, applies ``FOR UPDATE`` to the end of the + resulting statement. + + .. deprecated:: 0.9.0 - use + :meth:`.Select.with_for_update` to specify the + structure of the ``FOR UPDATE`` clause. + + ``for_update`` accepts various string values interpreted by + specific backends, including: + + * ``"read"`` - on MySQL, translates to ``LOCK IN SHARE MODE``; + on PostgreSQL, translates to ``FOR SHARE``. + * ``"nowait"`` - on PostgreSQL and Oracle, translates to + ``FOR UPDATE NOWAIT``. + * ``"read_nowait"`` - on PostgreSQL, translates to + ``FOR SHARE NOWAIT``. + + .. seealso:: + + :meth:`.Select.with_for_update` - improved API for + specifying the ``FOR UPDATE`` clause. + + :param group_by: + a list of :class:`.ClauseElement` objects which will comprise the + ``GROUP BY`` clause of the resulting select. This parameter + is typically specified more naturally using the + :meth:`.Select.group_by` method on an existing :class:`.Select`. + + .. seealso:: + + :meth:`.Select.group_by` + + :param having: + a :class:`.ClauseElement` that will comprise the ``HAVING`` clause + of the resulting select when ``GROUP BY`` is used. This parameter + is typically specified more naturally using the + :meth:`.Select.having` method on an existing :class:`.Select`. + + .. seealso:: + + :meth:`.Select.having` + + :param limit=None: + a numerical value which usually renders as a ``LIMIT`` + expression in the resulting select. Backends that don't + support ``LIMIT`` will attempt to provide similar + functionality. This parameter is typically specified more naturally + using the :meth:`.Select.limit` method on an existing + :class:`.Select`. + + .. seealso:: + + :meth:`.Select.limit` + + :param offset=None: + a numeric value which usually renders as an ``OFFSET`` + expression in the resulting select. Backends that don't + support ``OFFSET`` will attempt to provide similar + functionality. This parameter is typically specified more naturally + using the :meth:`.Select.offset` method on an existing + :class:`.Select`. + + .. seealso:: + + :meth:`.Select.offset` + + :param order_by: + a scalar or list of :class:`.ClauseElement` objects which will + comprise the ``ORDER BY`` clause of the resulting select. + This parameter is typically specified more naturally using the + :meth:`.Select.order_by` method on an existing :class:`.Select`. + + .. seealso:: + + :meth:`.Select.order_by` + + :param use_labels=False: + when ``True``, the statement will be generated using labels + for each column in the columns clause, which qualify each + column with its parent table's (or aliases) name so that name + conflicts between columns in different tables don't occur. + The format of the label is <tablename>_<column>. The "c" + collection of the resulting :class:`.Select` object will use these + names as well for targeting column members. + + This parameter can also be specified on an existing + :class:`.Select` object using the :meth:`.Select.apply_labels` + method. + + .. seealso:: + + :meth:`.Select.apply_labels` + + """ + self._auto_correlate = correlate + if distinct is not False: + if distinct is True: + self._distinct = True + else: + self._distinct = [ + _literal_as_text(e) + for e in util.to_list(distinct) + ] + + if from_obj is not None: + self._from_obj = util.OrderedSet( + _interpret_as_from(f) + for f in util.to_list(from_obj)) + else: + self._from_obj = util.OrderedSet() + + try: + cols_present = bool(columns) + except TypeError: + raise exc.ArgumentError("columns argument to select() must " + "be a Python list or other iterable") + + if cols_present: + self._raw_columns = [] + for c in columns: + c = _interpret_as_column_or_from(c) + if isinstance(c, ScalarSelect): + c = c.self_group(against=operators.comma_op) + self._raw_columns.append(c) + else: + self._raw_columns = [] + + if whereclause is not None: + self._whereclause = _literal_as_text( + whereclause).self_group(against=operators._asbool) + else: + self._whereclause = None + + if having is not None: + self._having = _literal_as_text( + having).self_group(against=operators._asbool) + else: + self._having = None + + if prefixes: + self._setup_prefixes(prefixes) + + if suffixes: + self._setup_suffixes(suffixes) + + GenerativeSelect.__init__(self, **kwargs) + + @property + def _froms(self): + # would love to cache this, + # but there's just enough edge cases, particularly now that + # declarative encourages construction of SQL expressions + # without tables present, to just regen this each time. + froms = [] + seen = set() + translate = self._from_cloned + + for item in itertools.chain( + _from_objects(*self._raw_columns), + _from_objects(self._whereclause) + if self._whereclause is not None else (), + self._from_obj + ): + if item is self: + raise exc.InvalidRequestError( + "select() construct refers to itself as a FROM") + if translate and item in translate: + item = translate[item] + if not seen.intersection(item._cloned_set): + froms.append(item) + seen.update(item._cloned_set) + + return froms + + def _get_display_froms(self, explicit_correlate_froms=None, + implicit_correlate_froms=None): + """Return the full list of 'from' clauses to be displayed. + + Takes into account a set of existing froms which may be + rendered in the FROM clause of enclosing selects; this Select + may want to leave those absent if it is automatically + correlating. + + """ + froms = self._froms + + toremove = set(itertools.chain(*[ + _expand_cloned(f._hide_froms) + for f in froms])) + if toremove: + # if we're maintaining clones of froms, + # add the copies out to the toremove list. only include + # clones that are lexical equivalents. + if self._from_cloned: + toremove.update( + self._from_cloned[f] for f in + toremove.intersection(self._from_cloned) + if self._from_cloned[f]._is_lexical_equivalent(f) + ) + # filter out to FROM clauses not in the list, + # using a list to maintain ordering + froms = [f for f in froms if f not in toremove] + + if self._correlate: + to_correlate = self._correlate + if to_correlate: + froms = [ + f for f in froms if f not in + _cloned_intersection( + _cloned_intersection( + froms, explicit_correlate_froms or ()), + to_correlate + ) + ] + + if self._correlate_except is not None: + + froms = [ + f for f in froms if f not in + _cloned_difference( + _cloned_intersection( + froms, explicit_correlate_froms or ()), + self._correlate_except + ) + ] + + if self._auto_correlate and \ + implicit_correlate_froms and \ + len(froms) > 1: + + froms = [ + f for f in froms if f not in + _cloned_intersection(froms, implicit_correlate_froms) + ] + + if not len(froms): + raise exc.InvalidRequestError("Select statement '%s" + "' returned no FROM clauses " + "due to auto-correlation; " + "specify correlate(<tables>) " + "to control correlation " + "manually." % self) + + return froms + + def _scalar_type(self): + elem = self._raw_columns[0] + cols = list(elem._select_iterable) + return cols[0].type + + @property + def froms(self): + """Return the displayed list of FromClause elements.""" + + return self._get_display_froms() + + def with_statement_hint(self, text, dialect_name='*'): + """add a statement hint to this :class:`.Select`. + + This method is similar to :meth:`.Select.with_hint` except that + it does not require an individual table, and instead applies to the + statement as a whole. + + Hints here are specific to the backend database and may include + directives such as isolation levels, file directives, fetch directives, + etc. + + .. versionadded:: 1.0.0 + + .. seealso:: + + :meth:`.Select.with_hint` + + """ + return self.with_hint(None, text, dialect_name) + + @_generative + def with_hint(self, selectable, text, dialect_name='*'): + r"""Add an indexing or other executional context hint for the given + selectable to this :class:`.Select`. + + The text of the hint is rendered in the appropriate + location for the database backend in use, relative + to the given :class:`.Table` or :class:`.Alias` passed as the + ``selectable`` argument. The dialect implementation + typically uses Python string substitution syntax + with the token ``%(name)s`` to render the name of + the table or alias. E.g. when using Oracle, the + following:: + + select([mytable]).\ + with_hint(mytable, "index(%(name)s ix_mytable)") + + Would render SQL as:: + + select /*+ index(mytable ix_mytable) */ ... from mytable + + The ``dialect_name`` option will limit the rendering of a particular + hint to a particular backend. Such as, to add hints for both Oracle + and Sybase simultaneously:: + + select([mytable]).\ + with_hint(mytable, "index(%(name)s ix_mytable)", 'oracle').\ + with_hint(mytable, "WITH INDEX ix_mytable", 'sybase') + + .. seealso:: + + :meth:`.Select.with_statement_hint` + + """ + if selectable is None: + self._statement_hints += ((dialect_name, text), ) + else: + self._hints = self._hints.union( + {(selectable, dialect_name): text}) + + @property + def type(self): + raise exc.InvalidRequestError("Select objects don't have a type. " + "Call as_scalar() on this Select " + "object to return a 'scalar' version " + "of this Select.") + + @_memoized_property.method + def locate_all_froms(self): + """return a Set of all FromClause elements referenced by this Select. + + This set is a superset of that returned by the ``froms`` property, + which is specifically for those FromClause elements that would + actually be rendered. + + """ + froms = self._froms + return froms + list(_from_objects(*froms)) + + @property + def inner_columns(self): + """an iterator of all ColumnElement expressions which would + be rendered into the columns clause of the resulting SELECT statement. + + """ + return _select_iterables(self._raw_columns) + + @_memoized_property + def _label_resolve_dict(self): + with_cols = dict( + (c._resolve_label or c._label or c.key, c) + for c in _select_iterables(self._raw_columns) + if c._allow_label_resolve) + only_froms = dict( + (c.key, c) for c in + _select_iterables(self.froms) if c._allow_label_resolve) + only_cols = with_cols.copy() + for key, value in only_froms.items(): + with_cols.setdefault(key, value) + + return with_cols, only_froms, only_cols + + def is_derived_from(self, fromclause): + if self in fromclause._cloned_set: + return True + + for f in self.locate_all_froms(): + if f.is_derived_from(fromclause): + return True + return False + + def _copy_internals(self, clone=_clone, **kw): + super(Select, self)._copy_internals(clone, **kw) + + # Select() object has been cloned and probably adapted by the + # given clone function. Apply the cloning function to internal + # objects + + # 1. keep a dictionary of the froms we've cloned, and what + # they've become. This is consulted later when we derive + # additional froms from "whereclause" and the columns clause, + # which may still reference the uncloned parent table. + # as of 0.7.4 we also put the current version of _froms, which + # gets cleared on each generation. previously we were "baking" + # _froms into self._from_obj. + self._from_cloned = from_cloned = dict( + (f, clone(f, **kw)) for f in self._from_obj.union(self._froms)) + + # 3. update persistent _from_obj with the cloned versions. + self._from_obj = util.OrderedSet(from_cloned[f] for f in + self._from_obj) + + # the _correlate collection is done separately, what can happen + # here is the same item is _correlate as in _from_obj but the + # _correlate version has an annotation on it - (specifically + # RelationshipProperty.Comparator._criterion_exists() does + # this). Also keep _correlate liberally open with its previous + # contents, as this set is used for matching, not rendering. + self._correlate = set(clone(f) for f in + self._correlate).union(self._correlate) + # 4. clone other things. The difficulty here is that Column + # objects are not actually cloned, and refer to their original + # .table, resulting in the wrong "from" parent after a clone + # operation. Hence _from_cloned and _from_obj supersede what is + # present here. + self._raw_columns = [clone(c, **kw) for c in self._raw_columns] + for attr in '_whereclause', '_having', '_order_by_clause', \ + '_group_by_clause', '_for_update_arg': + if getattr(self, attr) is not None: + setattr(self, attr, clone(getattr(self, attr), **kw)) + + # erase exported column list, _froms collection, + # etc. + self._reset_exported() + + def get_children(self, column_collections=True, **kwargs): + """return child elements as per the ClauseElement specification.""" + + return (column_collections and list(self.columns) or []) + \ + self._raw_columns + list(self._froms) + \ + [x for x in + (self._whereclause, self._having, + self._order_by_clause, self._group_by_clause) + if x is not None] + + @_generative + def column(self, column): + """return a new select() construct with the given column expression + added to its columns clause. + + E.g.:: + + my_select = my_select.column(table.c.new_column) + + See the documentation for :meth:`.Select.with_only_columns` + for guidelines on adding /replacing the columns of a + :class:`.Select` object. + + """ + self.append_column(column) + + @util.dependencies("sqlalchemy.sql.util") + def reduce_columns(self, sqlutil, only_synonyms=True): + """Return a new :func`.select` construct with redundantly + named, equivalently-valued columns removed from the columns clause. + + "Redundant" here means two columns where one refers to the + other either based on foreign key, or via a simple equality + comparison in the WHERE clause of the statement. The primary purpose + of this method is to automatically construct a select statement + with all uniquely-named columns, without the need to use + table-qualified labels as :meth:`.apply_labels` does. + + When columns are omitted based on foreign key, the referred-to + column is the one that's kept. When columns are omitted based on + WHERE eqivalence, the first column in the columns clause is the + one that's kept. + + :param only_synonyms: when True, limit the removal of columns + to those which have the same name as the equivalent. Otherwise, + all columns that are equivalent to another are removed. + + .. versionadded:: 0.8 + + """ + return self.with_only_columns( + sqlutil.reduce_columns( + self.inner_columns, + only_synonyms=only_synonyms, + *(self._whereclause, ) + tuple(self._from_obj) + ) + ) + + @_generative + def with_only_columns(self, columns): + r"""Return a new :func:`.select` construct with its columns + clause replaced with the given columns. + + This method is exactly equivalent to as if the original + :func:`.select` had been called with the given columns + clause. I.e. a statement:: + + s = select([table1.c.a, table1.c.b]) + s = s.with_only_columns([table1.c.b]) + + should be exactly equivalent to:: + + s = select([table1.c.b]) + + This means that FROM clauses which are only derived + from the column list will be discarded if the new column + list no longer contains that FROM:: + + >>> table1 = table('t1', column('a'), column('b')) + >>> table2 = table('t2', column('a'), column('b')) + >>> s1 = select([table1.c.a, table2.c.b]) + >>> print s1 + SELECT t1.a, t2.b FROM t1, t2 + >>> s2 = s1.with_only_columns([table2.c.b]) + >>> print s2 + SELECT t2.b FROM t1 + + The preferred way to maintain a specific FROM clause + in the construct, assuming it won't be represented anywhere + else (i.e. not in the WHERE clause, etc.) is to set it using + :meth:`.Select.select_from`:: + + >>> s1 = select([table1.c.a, table2.c.b]).\ + ... select_from(table1.join(table2, + ... table1.c.a==table2.c.a)) + >>> s2 = s1.with_only_columns([table2.c.b]) + >>> print s2 + SELECT t2.b FROM t1 JOIN t2 ON t1.a=t2.a + + Care should also be taken to use the correct + set of column objects passed to :meth:`.Select.with_only_columns`. + Since the method is essentially equivalent to calling the + :func:`.select` construct in the first place with the given + columns, the columns passed to :meth:`.Select.with_only_columns` + should usually be a subset of those which were passed + to the :func:`.select` construct, not those which are available + from the ``.c`` collection of that :func:`.select`. That + is:: + + s = select([table1.c.a, table1.c.b]).select_from(table1) + s = s.with_only_columns([table1.c.b]) + + and **not**:: + + # usually incorrect + s = s.with_only_columns([s.c.b]) + + The latter would produce the SQL:: + + SELECT b + FROM (SELECT t1.a AS a, t1.b AS b + FROM t1), t1 + + Since the :func:`.select` construct is essentially being + asked to select both from ``table1`` as well as itself. + + """ + self._reset_exported() + rc = [] + for c in columns: + c = _interpret_as_column_or_from(c) + if isinstance(c, ScalarSelect): + c = c.self_group(against=operators.comma_op) + rc.append(c) + self._raw_columns = rc + + @_generative + def where(self, whereclause): + """return a new select() construct with the given expression added to + its WHERE clause, joined to the existing clause via AND, if any. + + """ + + self.append_whereclause(whereclause) + + @_generative + def having(self, having): + """return a new select() construct with the given expression added to + its HAVING clause, joined to the existing clause via AND, if any. + + """ + self.append_having(having) + + @_generative + def distinct(self, *expr): + r"""Return a new select() construct which will apply DISTINCT to its + columns clause. + + :param \*expr: optional column expressions. When present, + the PostgreSQL dialect will render a ``DISTINCT ON (<expressions>>)`` + construct. + + """ + if expr: + expr = [_literal_as_label_reference(e) for e in expr] + if isinstance(self._distinct, list): + self._distinct = self._distinct + expr + else: + self._distinct = expr + else: + self._distinct = True + + @_generative + def select_from(self, fromclause): + r"""return a new :func:`.select` construct with the + given FROM expression + merged into its list of FROM objects. + + E.g.:: + + table1 = table('t1', column('a')) + table2 = table('t2', column('b')) + s = select([table1.c.a]).\ + select_from( + table1.join(table2, table1.c.a==table2.c.b) + ) + + The "from" list is a unique set on the identity of each element, + so adding an already present :class:`.Table` or other selectable + will have no effect. Passing a :class:`.Join` that refers + to an already present :class:`.Table` or other selectable will have + the effect of concealing the presence of that selectable as + an individual element in the rendered FROM list, instead + rendering it into a JOIN clause. + + While the typical purpose of :meth:`.Select.select_from` is to + replace the default, derived FROM clause with a join, it can + also be called with individual table elements, multiple times + if desired, in the case that the FROM clause cannot be fully + derived from the columns clause:: + + select([func.count('*')]).select_from(table1) + + """ + self.append_from(fromclause) + + @_generative + def correlate(self, *fromclauses): + r"""return a new :class:`.Select` which will correlate the given FROM + clauses to that of an enclosing :class:`.Select`. + + Calling this method turns off the :class:`.Select` object's + default behavior of "auto-correlation". Normally, FROM elements + which appear in a :class:`.Select` that encloses this one via + its :term:`WHERE clause`, ORDER BY, HAVING or + :term:`columns clause` will be omitted from this :class:`.Select` + object's :term:`FROM clause`. + Setting an explicit correlation collection using the + :meth:`.Select.correlate` method provides a fixed list of FROM objects + that can potentially take place in this process. + + When :meth:`.Select.correlate` is used to apply specific FROM clauses + for correlation, the FROM elements become candidates for + correlation regardless of how deeply nested this :class:`.Select` + object is, relative to an enclosing :class:`.Select` which refers to + the same FROM object. This is in contrast to the behavior of + "auto-correlation" which only correlates to an immediate enclosing + :class:`.Select`. Multi-level correlation ensures that the link + between enclosed and enclosing :class:`.Select` is always via + at least one WHERE/ORDER BY/HAVING/columns clause in order for + correlation to take place. + + If ``None`` is passed, the :class:`.Select` object will correlate + none of its FROM entries, and all will render unconditionally + in the local FROM clause. + + :param \*fromclauses: a list of one or more :class:`.FromClause` + constructs, or other compatible constructs (i.e. ORM-mapped + classes) to become part of the correlate collection. + + .. versionchanged:: 0.8.0 ORM-mapped classes are accepted by + :meth:`.Select.correlate`. + + .. versionchanged:: 0.8.0 The :meth:`.Select.correlate` method no + longer unconditionally removes entries from the FROM clause; + instead, the candidate FROM entries must also be matched by a FROM + entry located in an enclosing :class:`.Select`, which ultimately + encloses this one as present in the WHERE clause, ORDER BY clause, + HAVING clause, or columns clause of an enclosing :meth:`.Select`. + + .. versionchanged:: 0.8.2 explicit correlation takes place + via any level of nesting of :class:`.Select` objects; in previous + 0.8 versions, correlation would only occur relative to the + immediate enclosing :class:`.Select` construct. + + .. seealso:: + + :meth:`.Select.correlate_except` + + :ref:`correlated_subqueries` + + """ + self._auto_correlate = False + if fromclauses and fromclauses[0] is None: + self._correlate = () + else: + self._correlate = set(self._correlate).union( + _interpret_as_from(f) for f in fromclauses) + + @_generative + def correlate_except(self, *fromclauses): + r"""return a new :class:`.Select` which will omit the given FROM + clauses from the auto-correlation process. + + Calling :meth:`.Select.correlate_except` turns off the + :class:`.Select` object's default behavior of + "auto-correlation" for the given FROM elements. An element + specified here will unconditionally appear in the FROM list, while + all other FROM elements remain subject to normal auto-correlation + behaviors. + + .. versionchanged:: 0.8.2 The :meth:`.Select.correlate_except` + method was improved to fully prevent FROM clauses specified here + from being omitted from the immediate FROM clause of this + :class:`.Select`. + + If ``None`` is passed, the :class:`.Select` object will correlate + all of its FROM entries. + + .. versionchanged:: 0.8.2 calling ``correlate_except(None)`` will + correctly auto-correlate all FROM clauses. + + :param \*fromclauses: a list of one or more :class:`.FromClause` + constructs, or other compatible constructs (i.e. ORM-mapped + classes) to become part of the correlate-exception collection. + + .. seealso:: + + :meth:`.Select.correlate` + + :ref:`correlated_subqueries` + + """ + + self._auto_correlate = False + if fromclauses and fromclauses[0] is None: + self._correlate_except = () + else: + self._correlate_except = set(self._correlate_except or ()).union( + _interpret_as_from(f) for f in fromclauses) + + def append_correlation(self, fromclause): + """append the given correlation expression to this select() + construct. + + This is an **in-place** mutation method; the + :meth:`~.Select.correlate` method is preferred, as it provides + standard :term:`method chaining`. + + """ + + self._auto_correlate = False + self._correlate = set(self._correlate).union( + _interpret_as_from(f) for f in fromclause) + + def append_column(self, column): + """append the given column expression to the columns clause of this + select() construct. + + E.g.:: + + my_select.append_column(some_table.c.new_column) + + This is an **in-place** mutation method; the + :meth:`~.Select.column` method is preferred, as it provides standard + :term:`method chaining`. + + See the documentation for :meth:`.Select.with_only_columns` + for guidelines on adding /replacing the columns of a + :class:`.Select` object. + + """ + self._reset_exported() + column = _interpret_as_column_or_from(column) + + if isinstance(column, ScalarSelect): + column = column.self_group(against=operators.comma_op) + + self._raw_columns = self._raw_columns + [column] + + def append_prefix(self, clause): + """append the given columns clause prefix expression to this select() + construct. + + This is an **in-place** mutation method; the + :meth:`~.Select.prefix_with` method is preferred, as it provides + standard :term:`method chaining`. + + """ + clause = _literal_as_text(clause) + self._prefixes = self._prefixes + (clause,) + + def append_whereclause(self, whereclause): + """append the given expression to this select() construct's WHERE + criterion. + + The expression will be joined to existing WHERE criterion via AND. + + This is an **in-place** mutation method; the + :meth:`~.Select.where` method is preferred, as it provides standard + :term:`method chaining`. + + """ + + self._reset_exported() + self._whereclause = and_( + True_._ifnone(self._whereclause), whereclause) + + def append_having(self, having): + """append the given expression to this select() construct's HAVING + criterion. + + The expression will be joined to existing HAVING criterion via AND. + + This is an **in-place** mutation method; the + :meth:`~.Select.having` method is preferred, as it provides standard + :term:`method chaining`. + + """ + self._reset_exported() + self._having = and_(True_._ifnone(self._having), having) + + def append_from(self, fromclause): + """append the given FromClause expression to this select() construct's + FROM clause. + + This is an **in-place** mutation method; the + :meth:`~.Select.select_from` method is preferred, as it provides + standard :term:`method chaining`. + + """ + self._reset_exported() + fromclause = _interpret_as_from(fromclause) + self._from_obj = self._from_obj.union([fromclause]) + + @_memoized_property + def _columns_plus_names(self): + if self.use_labels: + names = set() + + def name_for_col(c): + if c._label is None or not c._render_label_in_columns_clause: + return (None, c) + + name = c._label + if name in names: + name = c.anon_label + else: + names.add(name) + return name, c + + return [ + name_for_col(c) + for c in util.unique_list( + _select_iterables(self._raw_columns)) + ] + else: + return [ + (None, c) + for c in util.unique_list( + _select_iterables(self._raw_columns)) + ] + + def _populate_column_collection(self): + for name, c in self._columns_plus_names: + if not hasattr(c, '_make_proxy'): + continue + if name is None: + key = None + elif self.use_labels: + key = c._key_label + if key is not None and key in self.c: + key = c.anon_label + else: + key = None + + c._make_proxy(self, key=key, + name=name, + name_is_truncatable=True) + + def _refresh_for_new_column(self, column): + for fromclause in self._froms: + col = fromclause._refresh_for_new_column(column) + if col is not None: + if col in self.inner_columns and self._cols_populated: + our_label = col._key_label if self.use_labels else col.key + if our_label not in self.c: + return col._make_proxy( + self, + name=col._label if self.use_labels else None, + key=col._key_label if self.use_labels else None, + name_is_truncatable=True) + return None + return None + + def _needs_parens_for_grouping(self): + return ( + self._limit_clause is not None or + self._offset_clause is not None or + bool(self._order_by_clause.clauses) + ) + + def self_group(self, against=None): + """return a 'grouping' construct as per the ClauseElement + specification. + + This produces an element that can be embedded in an expression. Note + that this method is called automatically as needed when constructing + expressions and should not require explicit use. + + """ + if isinstance(against, CompoundSelect) and \ + not self._needs_parens_for_grouping(): + return self + return FromGrouping(self) + + def union(self, other, **kwargs): + """return a SQL UNION of this select() construct against the given + selectable.""" + + return CompoundSelect._create_union(self, other, **kwargs) + + def union_all(self, other, **kwargs): + """return a SQL UNION ALL of this select() construct against the given + selectable. + + """ + return CompoundSelect._create_union_all(self, other, **kwargs) + + def except_(self, other, **kwargs): + """return a SQL EXCEPT of this select() construct against the given + selectable.""" + + return CompoundSelect._create_except(self, other, **kwargs) + + def except_all(self, other, **kwargs): + """return a SQL EXCEPT ALL of this select() construct against the + given selectable. + + """ + return CompoundSelect._create_except_all(self, other, **kwargs) + + def intersect(self, other, **kwargs): + """return a SQL INTERSECT of this select() construct against the given + selectable. + + """ + return CompoundSelect._create_intersect(self, other, **kwargs) + + def intersect_all(self, other, **kwargs): + """return a SQL INTERSECT ALL of this select() construct against the + given selectable. + + """ + return CompoundSelect._create_intersect_all(self, other, **kwargs) + + def bind(self): + if self._bind: + return self._bind + froms = self._froms + if not froms: + for c in self._raw_columns: + e = c.bind + if e: + self._bind = e + return e + else: + e = list(froms)[0].bind + if e: + self._bind = e + return e + + return None + + def _set_bind(self, bind): + self._bind = bind + bind = property(bind, _set_bind) + + +class ScalarSelect(Generative, Grouping): + _from_objects = [] + _is_from_container = True + _is_implicitly_boolean = False + + def __init__(self, element): + self.element = element + self.type = element._scalar_type() + + @property + def columns(self): + raise exc.InvalidRequestError('Scalar Select expression has no ' + 'columns; use this object directly ' + 'within a column-level expression.') + c = columns + + @_generative + def where(self, crit): + """Apply a WHERE clause to the SELECT statement referred to + by this :class:`.ScalarSelect`. + + """ + self.element = self.element.where(crit) + + def self_group(self, **kwargs): + return self + + +class Exists(UnaryExpression): + """Represent an ``EXISTS`` clause. + + """ + __visit_name__ = UnaryExpression.__visit_name__ + _from_objects = [] + + def __init__(self, *args, **kwargs): + """Construct a new :class:`.Exists` against an existing + :class:`.Select` object. + + Calling styles are of the following forms:: + + # use on an existing select() + s = select([table.c.col1]).where(table.c.col2==5) + s = exists(s) + + # construct a select() at once + exists(['*'], **select_arguments).where(criterion) + + # columns argument is optional, generates "EXISTS (SELECT *)" + # by default. + exists().where(table.c.col2==5) + + """ + if args and isinstance(args[0], (SelectBase, ScalarSelect)): + s = args[0] + else: + if not args: + args = ([literal_column('*')],) + s = Select(*args, **kwargs).as_scalar().self_group() + + UnaryExpression.__init__(self, s, operator=operators.exists, + type_=type_api.BOOLEANTYPE, + wraps_column_expression=True) + + def select(self, whereclause=None, **params): + return Select([self], whereclause, **params) + + def correlate(self, *fromclause): + e = self._clone() + e.element = self.element.correlate(*fromclause).self_group() + return e + + def correlate_except(self, *fromclause): + e = self._clone() + e.element = self.element.correlate_except(*fromclause).self_group() + return e + + def select_from(self, clause): + """return a new :class:`.Exists` construct, applying the given + expression to the :meth:`.Select.select_from` method of the select + statement contained. + + """ + e = self._clone() + e.element = self.element.select_from(clause).self_group() + return e + + def where(self, clause): + """return a new exists() construct with the given expression added to + its WHERE clause, joined to the existing clause via AND, if any. + + """ + e = self._clone() + e.element = self.element.where(clause).self_group() + return e + + +class TextAsFrom(SelectBase): + """Wrap a :class:`.TextClause` construct within a :class:`.SelectBase` + interface. + + This allows the :class:`.TextClause` object to gain a ``.c`` collection + and other FROM-like capabilities such as :meth:`.FromClause.alias`, + :meth:`.SelectBase.cte`, etc. + + The :class:`.TextAsFrom` construct is produced via the + :meth:`.TextClause.columns` method - see that method for details. + + .. versionadded:: 0.9.0 + + .. seealso:: + + :func:`.text` + + :meth:`.TextClause.columns` + + """ + __visit_name__ = "text_as_from" + + _textual = True + + def __init__(self, text, columns, positional=False): + self.element = text + self.column_args = columns + self.positional = positional + + @property + def _bind(self): + return self.element._bind + + @_generative + def bindparams(self, *binds, **bind_as_values): + self.element = self.element.bindparams(*binds, **bind_as_values) + + def _populate_column_collection(self): + for c in self.column_args: + c._make_proxy(self) + + def _copy_internals(self, clone=_clone, **kw): + self._reset_exported() + self.element = clone(self.element, **kw) + + def _scalar_type(self): + return self.column_args[0].type + + +class AnnotatedFromClause(Annotated): + def __init__(self, element, values): + # force FromClause to generate their internal + # collections into __dict__ + element.c + Annotated.__init__(self, element, values) diff --git a/venv/Lib/site-packages/sqlalchemy/sql/sqltypes.py b/venv/Lib/site-packages/sqlalchemy/sql/sqltypes.py new file mode 100644 index 0000000..f01a6ce --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/sqltypes.py @@ -0,0 +1,2734 @@ +# sql/sqltypes.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""SQL specific types. + +""" + +import datetime as dt +import codecs +import collections +import json + +from . import elements +from .type_api import TypeEngine, TypeDecorator, to_instance, Variant, \ + Emulated, NativeForEmulated +from .elements import quoted_name, TypeCoerce as type_coerce, _defer_name, \ + Slice, _literal_as_binds +from .. import exc, util, processors +from .base import _bind_or_error, SchemaEventTarget +from . import operators +from .. import inspection +from .. import event +from ..util import pickle +from ..util import compat +import decimal + +if util.jython: + import array + + +class _LookupExpressionAdapter(object): + + """Mixin expression adaptations based on lookup tables. + + These rules are currenly used by the numeric, integer and date types + which have detailed cross-expression coercion rules. + + """ + + @property + def _expression_adaptations(self): + raise NotImplementedError() + + class Comparator(TypeEngine.Comparator): + _blank_dict = util.immutabledict() + + def _adapt_expression(self, op, other_comparator): + othertype = other_comparator.type._type_affinity + lookup = self.type._expression_adaptations.get( + op, self._blank_dict).get( + othertype, self.type) + if lookup is othertype: + return (op, other_comparator.type) + elif lookup is self.type._type_affinity: + return (op, self.type) + else: + return (op, to_instance(lookup)) + comparator_factory = Comparator + + +class Concatenable(object): + + """A mixin that marks a type as supporting 'concatenation', + typically strings.""" + + class Comparator(TypeEngine.Comparator): + + def _adapt_expression(self, op, other_comparator): + if (op is operators.add and + isinstance( + other_comparator, + (Concatenable.Comparator, NullType.Comparator) + )): + return operators.concat_op, self.expr.type + else: + return super(Concatenable.Comparator, self)._adapt_expression( + op, other_comparator) + + comparator_factory = Comparator + + +class Indexable(object): + """A mixin that marks a type as supporting indexing operations, + such as array or JSON structures. + + + .. versionadded:: 1.1.0 + + + """ + + class Comparator(TypeEngine.Comparator): + + def _setup_getitem(self, index): + raise NotImplementedError() + + def __getitem__(self, index): + adjusted_op, adjusted_right_expr, result_type = \ + self._setup_getitem(index) + return self.operate( + adjusted_op, + adjusted_right_expr, + result_type=result_type + ) + + comparator_factory = Comparator + + +class String(Concatenable, TypeEngine): + + """The base for all string and character types. + + In SQL, corresponds to VARCHAR. Can also take Python unicode objects + and encode to the database's encoding in bind params (and the reverse for + result sets.) + + The `length` field is usually required when the `String` type is + used within a CREATE TABLE statement, as VARCHAR requires a length + on most databases. + + """ + + __visit_name__ = 'string' + + def __init__(self, length=None, collation=None, + convert_unicode=False, + unicode_error=None, + _warn_on_bytestring=False + ): + """ + Create a string-holding type. + + :param length: optional, a length for the column for use in + DDL and CAST expressions. May be safely omitted if no ``CREATE + TABLE`` will be issued. Certain databases may require a + ``length`` for use in DDL, and will raise an exception when + the ``CREATE TABLE`` DDL is issued if a ``VARCHAR`` + with no length is included. Whether the value is + interpreted as bytes or characters is database specific. + + :param collation: Optional, a column-level collation for + use in DDL and CAST expressions. Renders using the + COLLATE keyword supported by SQLite, MySQL, and PostgreSQL. + E.g.:: + + >>> from sqlalchemy import cast, select, String + >>> print select([cast('some string', String(collation='utf8'))]) + SELECT CAST(:param_1 AS VARCHAR COLLATE utf8) AS anon_1 + + .. versionadded:: 0.8 Added support for COLLATE to all + string types. + + :param convert_unicode: When set to ``True``, the + :class:`.String` type will assume that + input is to be passed as Python ``unicode`` objects, + and results returned as Python ``unicode`` objects. + If the DBAPI in use does not support Python unicode + (which is fewer and fewer these days), SQLAlchemy + will encode/decode the value, using the + value of the ``encoding`` parameter passed to + :func:`.create_engine` as the encoding. + + When using a DBAPI that natively supports Python + unicode objects, this flag generally does not + need to be set. For columns that are explicitly + intended to store non-ASCII data, the :class:`.Unicode` + or :class:`.UnicodeText` + types should be used regardless, which feature + the same behavior of ``convert_unicode`` but + also indicate an underlying column type that + directly supports unicode, such as ``NVARCHAR``. + + For the extremely rare case that Python ``unicode`` + is to be encoded/decoded by SQLAlchemy on a backend + that does natively support Python ``unicode``, + the value ``force`` can be passed here which will + cause SQLAlchemy's encode/decode services to be + used unconditionally. + + :param unicode_error: Optional, a method to use to handle Unicode + conversion errors. Behaves like the ``errors`` keyword argument to + the standard library's ``string.decode()`` functions. This flag + requires that ``convert_unicode`` is set to ``force`` - otherwise, + SQLAlchemy is not guaranteed to handle the task of unicode + conversion. Note that this flag adds significant performance + overhead to row-fetching operations for backends that already + return unicode objects natively (which most DBAPIs do). This + flag should only be used as a last resort for reading + strings from a column with varied or corrupted encodings. + + """ + if unicode_error is not None and convert_unicode != 'force': + raise exc.ArgumentError("convert_unicode must be 'force' " + "when unicode_error is set.") + + self.length = length + self.collation = collation + self.convert_unicode = convert_unicode + self.unicode_error = unicode_error + self._warn_on_bytestring = _warn_on_bytestring + + def literal_processor(self, dialect): + def process(value): + value = value.replace("'", "''") + + if dialect.identifier_preparer._double_percents: + value = value.replace('%', '%%') + + return "'%s'" % value + return process + + def bind_processor(self, dialect): + if self.convert_unicode or dialect.convert_unicode: + if dialect.supports_unicode_binds and \ + self.convert_unicode != 'force': + if self._warn_on_bytestring: + def process(value): + if isinstance(value, util.binary_type): + util.warn_limited( + "Unicode type received non-unicode " + "bind param value %r.", + (util.ellipses_string(value),)) + return value + return process + else: + return None + else: + encoder = codecs.getencoder(dialect.encoding) + warn_on_bytestring = self._warn_on_bytestring + + def process(value): + if isinstance(value, util.text_type): + return encoder(value, self.unicode_error)[0] + elif warn_on_bytestring and value is not None: + util.warn_limited( + "Unicode type received non-unicode bind " + "param value %r.", + (util.ellipses_string(value),)) + return value + return process + else: + return None + + def result_processor(self, dialect, coltype): + wants_unicode = self.convert_unicode or dialect.convert_unicode + needs_convert = wants_unicode and \ + (dialect.returns_unicode_strings is not True or + self.convert_unicode in ('force', 'force_nocheck')) + needs_isinstance = ( + needs_convert and + dialect.returns_unicode_strings and + self.convert_unicode != 'force_nocheck' + ) + if needs_convert: + if needs_isinstance: + return processors.to_conditional_unicode_processor_factory( + dialect.encoding, self.unicode_error) + else: + return processors.to_unicode_processor_factory( + dialect.encoding, self.unicode_error) + else: + return None + + @property + def python_type(self): + if self.convert_unicode: + return util.text_type + else: + return str + + def get_dbapi_type(self, dbapi): + return dbapi.STRING + + +class Text(String): + + """A variably sized string type. + + In SQL, usually corresponds to CLOB or TEXT. Can also take Python + unicode objects and encode to the database's encoding in bind + params (and the reverse for result sets.) In general, TEXT objects + do not have a length; while some databases will accept a length + argument here, it will be rejected by others. + + """ + __visit_name__ = 'text' + + +class Unicode(String): + + """A variable length Unicode string type. + + The :class:`.Unicode` type is a :class:`.String` subclass + that assumes input and output as Python ``unicode`` data, + and in that regard is equivalent to the usage of the + ``convert_unicode`` flag with the :class:`.String` type. + However, unlike plain :class:`.String`, it also implies an + underlying column type that is explicitly supporting of non-ASCII + data, such as ``NVARCHAR`` on Oracle and SQL Server. + This can impact the output of ``CREATE TABLE`` statements + and ``CAST`` functions at the dialect level, and can + also affect the handling of bound parameters in some + specific DBAPI scenarios. + + The encoding used by the :class:`.Unicode` type is usually + determined by the DBAPI itself; most modern DBAPIs + feature support for Python ``unicode`` objects as bound + values and result set values, and the encoding should + be configured as detailed in the notes for the target + DBAPI in the :ref:`dialect_toplevel` section. + + For those DBAPIs which do not support, or are not configured + to accommodate Python ``unicode`` objects + directly, SQLAlchemy does the encoding and decoding + outside of the DBAPI. The encoding in this scenario + is determined by the ``encoding`` flag passed to + :func:`.create_engine`. + + When using the :class:`.Unicode` type, it is only appropriate + to pass Python ``unicode`` objects, and not plain ``str``. + If a plain ``str`` is passed under Python 2, a warning + is emitted. If you notice your application emitting these warnings but + you're not sure of the source of them, the Python + ``warnings`` filter, documented at + http://docs.python.org/library/warnings.html, + can be used to turn these warnings into exceptions + which will illustrate a stack trace:: + + import warnings + warnings.simplefilter('error') + + For an application that wishes to pass plain bytestrings + and Python ``unicode`` objects to the ``Unicode`` type + equally, the bytestrings must first be decoded into + unicode. The recipe at :ref:`coerce_to_unicode` illustrates + how this is done. + + See also: + + :class:`.UnicodeText` - unlengthed textual counterpart + to :class:`.Unicode`. + + """ + + __visit_name__ = 'unicode' + + def __init__(self, length=None, **kwargs): + """ + Create a :class:`.Unicode` object. + + Parameters are the same as that of :class:`.String`, + with the exception that ``convert_unicode`` + defaults to ``True``. + + """ + kwargs.setdefault('convert_unicode', True) + kwargs.setdefault('_warn_on_bytestring', True) + super(Unicode, self).__init__(length=length, **kwargs) + + +class UnicodeText(Text): + + """An unbounded-length Unicode string type. + + See :class:`.Unicode` for details on the unicode + behavior of this object. + + Like :class:`.Unicode`, usage the :class:`.UnicodeText` type implies a + unicode-capable type being used on the backend, such as + ``NCLOB``, ``NTEXT``. + + """ + + __visit_name__ = 'unicode_text' + + def __init__(self, length=None, **kwargs): + """ + Create a Unicode-converting Text type. + + Parameters are the same as that of :class:`.Text`, + with the exception that ``convert_unicode`` + defaults to ``True``. + + """ + kwargs.setdefault('convert_unicode', True) + kwargs.setdefault('_warn_on_bytestring', True) + super(UnicodeText, self).__init__(length=length, **kwargs) + + +class Integer(_LookupExpressionAdapter, TypeEngine): + + """A type for ``int`` integers.""" + + __visit_name__ = 'integer' + + def get_dbapi_type(self, dbapi): + return dbapi.NUMBER + + @property + def python_type(self): + return int + + def literal_processor(self, dialect): + def process(value): + return str(value) + return process + + @util.memoized_property + def _expression_adaptations(self): + # TODO: need a dictionary object that will + # handle operators generically here, this is incomplete + return { + operators.add: { + Date: Date, + Integer: self.__class__, + Numeric: Numeric, + }, + operators.mul: { + Interval: Interval, + Integer: self.__class__, + Numeric: Numeric, + }, + operators.div: { + Integer: self.__class__, + Numeric: Numeric, + }, + operators.truediv: { + Integer: self.__class__, + Numeric: Numeric, + }, + operators.sub: { + Integer: self.__class__, + Numeric: Numeric, + }, + } + + +class SmallInteger(Integer): + + """A type for smaller ``int`` integers. + + Typically generates a ``SMALLINT`` in DDL, and otherwise acts like + a normal :class:`.Integer` on the Python side. + + """ + + __visit_name__ = 'small_integer' + + +class BigInteger(Integer): + + """A type for bigger ``int`` integers. + + Typically generates a ``BIGINT`` in DDL, and otherwise acts like + a normal :class:`.Integer` on the Python side. + + """ + + __visit_name__ = 'big_integer' + + +class Numeric(_LookupExpressionAdapter, TypeEngine): + + """A type for fixed precision numbers, such as ``NUMERIC`` or ``DECIMAL``. + + This type returns Python ``decimal.Decimal`` objects by default, unless + the :paramref:`.Numeric.asdecimal` flag is set to False, in which case + they are coerced to Python ``float`` objects. + + .. note:: + + The :class:`.Numeric` type is designed to receive data from a database + type that is explicitly known to be a decimal type + (e.g. ``DECIMAL``, ``NUMERIC``, others) and not a floating point + type (e.g. ``FLOAT``, ``REAL``, others). + If the database column on the server is in fact a floating-point type + type, such as ``FLOAT`` or ``REAL``, use the :class:`.Float` + type or a subclass, otherwise numeric coercion between + ``float``/``Decimal`` may or may not function as expected. + + .. note:: + + The Python ``decimal.Decimal`` class is generally slow + performing; cPython 3.3 has now switched to use the `cdecimal + <http://pypi.python.org/pypi/cdecimal/>`_ library natively. For + older Python versions, the ``cdecimal`` library can be patched + into any application where it will replace the ``decimal`` + library fully, however this needs to be applied globally and + before any other modules have been imported, as follows:: + + import sys + import cdecimal + sys.modules["decimal"] = cdecimal + + Note that the ``cdecimal`` and ``decimal`` libraries are **not + compatible with each other**, so patching ``cdecimal`` at the + global level is the only way it can be used effectively with + various DBAPIs that hardcode to import the ``decimal`` library. + + """ + + __visit_name__ = 'numeric' + + _default_decimal_return_scale = 10 + + def __init__(self, precision=None, scale=None, + decimal_return_scale=None, asdecimal=True): + """ + Construct a Numeric. + + :param precision: the numeric precision for use in DDL ``CREATE + TABLE``. + + :param scale: the numeric scale for use in DDL ``CREATE TABLE``. + + :param asdecimal: default True. Return whether or not + values should be sent as Python Decimal objects, or + as floats. Different DBAPIs send one or the other based on + datatypes - the Numeric type will ensure that return values + are one or the other across DBAPIs consistently. + + :param decimal_return_scale: Default scale to use when converting + from floats to Python decimals. Floating point values will typically + be much longer due to decimal inaccuracy, and most floating point + database types don't have a notion of "scale", so by default the + float type looks for the first ten decimal places when converting. + Specfiying this value will override that length. Types which + do include an explicit ".scale" value, such as the base + :class:`.Numeric` as well as the MySQL float types, will use the + value of ".scale" as the default for decimal_return_scale, if not + otherwise specified. + + .. versionadded:: 0.9.0 + + When using the ``Numeric`` type, care should be taken to ensure + that the asdecimal setting is apppropriate for the DBAPI in use - + when Numeric applies a conversion from Decimal->float or float-> + Decimal, this conversion incurs an additional performance overhead + for all result columns received. + + DBAPIs that return Decimal natively (e.g. psycopg2) will have + better accuracy and higher performance with a setting of ``True``, + as the native translation to Decimal reduces the amount of floating- + point issues at play, and the Numeric type itself doesn't need + to apply any further conversions. However, another DBAPI which + returns floats natively *will* incur an additional conversion + overhead, and is still subject to floating point data loss - in + which case ``asdecimal=False`` will at least remove the extra + conversion overhead. + + """ + self.precision = precision + self.scale = scale + self.decimal_return_scale = decimal_return_scale + self.asdecimal = asdecimal + + @property + def _effective_decimal_return_scale(self): + if self.decimal_return_scale is not None: + return self.decimal_return_scale + elif getattr(self, "scale", None) is not None: + return self.scale + else: + return self._default_decimal_return_scale + + def get_dbapi_type(self, dbapi): + return dbapi.NUMBER + + def literal_processor(self, dialect): + def process(value): + return str(value) + return process + + @property + def python_type(self): + if self.asdecimal: + return decimal.Decimal + else: + return float + + def bind_processor(self, dialect): + if dialect.supports_native_decimal: + return None + else: + return processors.to_float + + def result_processor(self, dialect, coltype): + if self.asdecimal: + if dialect.supports_native_decimal: + # we're a "numeric", DBAPI will give us Decimal directly + return None + else: + util.warn('Dialect %s+%s does *not* support Decimal ' + 'objects natively, and SQLAlchemy must ' + 'convert from floating point - rounding ' + 'errors and other issues may occur. Please ' + 'consider storing Decimal numbers as strings ' + 'or integers on this platform for lossless ' + 'storage.' % (dialect.name, dialect.driver)) + + # we're a "numeric", DBAPI returns floats, convert. + return processors.to_decimal_processor_factory( + decimal.Decimal, + self.scale if self.scale is not None + else self._default_decimal_return_scale) + else: + if dialect.supports_native_decimal: + return processors.to_float + else: + return None + + @util.memoized_property + def _expression_adaptations(self): + return { + operators.mul: { + Interval: Interval, + Numeric: self.__class__, + Integer: self.__class__, + }, + operators.div: { + Numeric: self.__class__, + Integer: self.__class__, + }, + operators.truediv: { + Numeric: self.__class__, + Integer: self.__class__, + }, + operators.add: { + Numeric: self.__class__, + Integer: self.__class__, + }, + operators.sub: { + Numeric: self.__class__, + Integer: self.__class__, + } + } + + +class Float(Numeric): + + """Type representing floating point types, such as ``FLOAT`` or ``REAL``. + + This type returns Python ``float`` objects by default, unless the + :paramref:`.Float.asdecimal` flag is set to True, in which case they + are coerced to ``decimal.Decimal`` objects. + + .. note:: + + The :class:`.Float` type is designed to receive data from a database + type that is explicitly known to be a floating point type + (e.g. ``FLOAT``, ``REAL``, others) + and not a decimal type (e.g. ``DECIMAL``, ``NUMERIC``, others). + If the database column on the server is in fact a Numeric + type, such as ``DECIMAL`` or ``NUMERIC``, use the :class:`.Numeric` + type or a subclass, otherwise numeric coercion between + ``float``/``Decimal`` may or may not function as expected. + + """ + + __visit_name__ = 'float' + + scale = None + + def __init__(self, precision=None, asdecimal=False, + decimal_return_scale=None, **kwargs): + r""" + Construct a Float. + + :param precision: the numeric precision for use in DDL ``CREATE + TABLE``. + + :param asdecimal: the same flag as that of :class:`.Numeric`, but + defaults to ``False``. Note that setting this flag to ``True`` + results in floating point conversion. + + :param decimal_return_scale: Default scale to use when converting + from floats to Python decimals. Floating point values will typically + be much longer due to decimal inaccuracy, and most floating point + database types don't have a notion of "scale", so by default the + float type looks for the first ten decimal places when converting. + Specfiying this value will override that length. Note that the + MySQL float types, which do include "scale", will use "scale" + as the default for decimal_return_scale, if not otherwise specified. + + .. versionadded:: 0.9.0 + + :param \**kwargs: deprecated. Additional arguments here are ignored + by the default :class:`.Float` type. For database specific + floats that support additional arguments, see that dialect's + documentation for details, such as + :class:`sqlalchemy.dialects.mysql.FLOAT`. + + """ + self.precision = precision + self.asdecimal = asdecimal + self.decimal_return_scale = decimal_return_scale + if kwargs: + util.warn_deprecated("Additional keyword arguments " + "passed to Float ignored.") + + def result_processor(self, dialect, coltype): + if self.asdecimal: + return processors.to_decimal_processor_factory( + decimal.Decimal, + self._effective_decimal_return_scale) + elif dialect.supports_native_decimal: + return processors.to_float + else: + return None + + +class DateTime(_LookupExpressionAdapter, TypeEngine): + + """A type for ``datetime.datetime()`` objects. + + Date and time types return objects from the Python ``datetime`` + module. Most DBAPIs have built in support for the datetime + module, with the noted exception of SQLite. In the case of + SQLite, date and time types are stored as strings which are then + converted back to datetime objects when rows are returned. + + For the time representation within the datetime type, some + backends include additional options, such as timezone support and + fractional seconds support. For fractional seconds, use the + dialect-specific datatype, such as :class:`.mysql.TIME`. For + timezone support, use at least the :class:`~.types.TIMESTAMP` datatype, + if not the dialect-specific datatype object. + + """ + + __visit_name__ = 'datetime' + + def __init__(self, timezone=False): + """Construct a new :class:`.DateTime`. + + :param timezone: boolean. Indicates that the datetime type should + enable timezone support, if available on the + **base date/time-holding type only**. It is recommended + to make use of the :class:`~.types.TIMESTAMP` datatype directly when + using this flag, as some databases include separate generic + date/time-holding types distinct from the timezone-capable + TIMESTAMP datatype, such as Oracle. + + + """ + self.timezone = timezone + + def get_dbapi_type(self, dbapi): + return dbapi.DATETIME + + @property + def python_type(self): + return dt.datetime + + @util.memoized_property + def _expression_adaptations(self): + + # Based on http://www.postgresql.org/docs/current/\ + # static/functions-datetime.html. + + return { + operators.add: { + Interval: self.__class__, + }, + operators.sub: { + Interval: self.__class__, + DateTime: Interval, + }, + } + + +class Date(_LookupExpressionAdapter, TypeEngine): + + """A type for ``datetime.date()`` objects.""" + + __visit_name__ = 'date' + + def get_dbapi_type(self, dbapi): + return dbapi.DATETIME + + @property + def python_type(self): + return dt.date + + @util.memoized_property + def _expression_adaptations(self): + # Based on http://www.postgresql.org/docs/current/\ + # static/functions-datetime.html. + + return { + operators.add: { + Integer: self.__class__, + Interval: DateTime, + Time: DateTime, + }, + operators.sub: { + # date - integer = date + Integer: self.__class__, + + # date - date = integer. + Date: Integer, + + Interval: DateTime, + + # date - datetime = interval, + # this one is not in the PG docs + # but works + DateTime: Interval, + }, + } + + +class Time(_LookupExpressionAdapter, TypeEngine): + + """A type for ``datetime.time()`` objects.""" + + __visit_name__ = 'time' + + def __init__(self, timezone=False): + self.timezone = timezone + + def get_dbapi_type(self, dbapi): + return dbapi.DATETIME + + @property + def python_type(self): + return dt.time + + @util.memoized_property + def _expression_adaptations(self): + # Based on http://www.postgresql.org/docs/current/\ + # static/functions-datetime.html. + + return { + operators.add: { + Date: DateTime, + Interval: self.__class__ + }, + operators.sub: { + Time: Interval, + Interval: self.__class__, + }, + } + + +class _Binary(TypeEngine): + + """Define base behavior for binary types.""" + + def __init__(self, length=None): + self.length = length + + def literal_processor(self, dialect): + def process(value): + value = value.decode(dialect.encoding).replace("'", "''") + return "'%s'" % value + return process + + @property + def python_type(self): + return util.binary_type + + # Python 3 - sqlite3 doesn't need the `Binary` conversion + # here, though pg8000 does to indicate "bytea" + def bind_processor(self, dialect): + if dialect.dbapi is None: + return None + + DBAPIBinary = dialect.dbapi.Binary + + def process(value): + if value is not None: + return DBAPIBinary(value) + else: + return None + return process + + # Python 3 has native bytes() type + # both sqlite3 and pg8000 seem to return it, + # psycopg2 as of 2.5 returns 'memoryview' + if util.py2k: + def result_processor(self, dialect, coltype): + if util.jython: + def process(value): + if value is not None: + if isinstance(value, array.array): + return value.tostring() + return str(value) + else: + return None + else: + process = processors.to_str + return process + else: + def result_processor(self, dialect, coltype): + def process(value): + if value is not None: + value = bytes(value) + return value + return process + + def coerce_compared_value(self, op, value): + """See :meth:`.TypeEngine.coerce_compared_value` for a description.""" + + if isinstance(value, util.string_types): + return self + else: + return super(_Binary, self).coerce_compared_value(op, value) + + def get_dbapi_type(self, dbapi): + return dbapi.BINARY + + +class LargeBinary(_Binary): + + """A type for large binary byte data. + + The :class:`.LargeBinary` type corresponds to a large and/or unlengthed + binary type for the target platform, such as BLOB on MySQL and BYTEA for + PostgreSQL. It also handles the necessary conversions for the DBAPI. + + """ + + __visit_name__ = 'large_binary' + + def __init__(self, length=None): + """ + Construct a LargeBinary type. + + :param length: optional, a length for the column for use in + DDL statements, for those binary types that accept a length, + such as the MySQL BLOB type. + + """ + _Binary.__init__(self, length=length) + + +class Binary(LargeBinary): + + """Deprecated. Renamed to LargeBinary.""" + + def __init__(self, *arg, **kw): + util.warn_deprecated('The Binary type has been renamed to ' + 'LargeBinary.') + LargeBinary.__init__(self, *arg, **kw) + + +class SchemaType(SchemaEventTarget): + + """Mark a type as possibly requiring schema-level DDL for usage. + + Supports types that must be explicitly created/dropped (i.e. PG ENUM type) + as well as types that are complimented by table or schema level + constraints, triggers, and other rules. + + :class:`.SchemaType` classes can also be targets for the + :meth:`.DDLEvents.before_parent_attach` and + :meth:`.DDLEvents.after_parent_attach` events, where the events fire off + surrounding the association of the type object with a parent + :class:`.Column`. + + .. seealso:: + + :class:`.Enum` + + :class:`.Boolean` + + + """ + + def __init__(self, name=None, schema=None, metadata=None, + inherit_schema=False, quote=None, _create_events=True): + if name is not None: + self.name = quoted_name(name, quote) + else: + self.name = None + self.schema = schema + self.metadata = metadata + self.inherit_schema = inherit_schema + self._create_events = _create_events + + if _create_events and self.metadata: + event.listen( + self.metadata, + "before_create", + util.portable_instancemethod(self._on_metadata_create) + ) + event.listen( + self.metadata, + "after_drop", + util.portable_instancemethod(self._on_metadata_drop) + ) + + def _translate_schema(self, effective_schema, map_): + return map_.get(effective_schema, effective_schema) + + def _set_parent(self, column): + column._on_table_attach(util.portable_instancemethod(self._set_table)) + + def _variant_mapping_for_set_table(self, column): + if isinstance(column.type, Variant): + variant_mapping = column.type.mapping.copy() + variant_mapping['_default'] = column.type.impl + else: + variant_mapping = None + return variant_mapping + + def _set_table(self, column, table): + if self.inherit_schema: + self.schema = table.schema + + if not self._create_events: + return + + variant_mapping = self._variant_mapping_for_set_table(column) + + event.listen( + table, + "before_create", + util.portable_instancemethod( + self._on_table_create, + {"variant_mapping": variant_mapping}) + ) + event.listen( + table, + "after_drop", + util.portable_instancemethod( + self._on_table_drop, + {"variant_mapping": variant_mapping}) + ) + if self.metadata is None: + # TODO: what's the difference between self.metadata + # and table.metadata here ? + event.listen( + table.metadata, + "before_create", + util.portable_instancemethod( + self._on_metadata_create, + {"variant_mapping": variant_mapping}) + ) + event.listen( + table.metadata, + "after_drop", + util.portable_instancemethod( + self._on_metadata_drop, + {"variant_mapping": variant_mapping}) + ) + + def copy(self, **kw): + return self.adapt(self.__class__, _create_events=True) + + def adapt(self, impltype, **kw): + schema = kw.pop('schema', self.schema) + metadata = kw.pop('metadata', self.metadata) + _create_events = kw.pop('_create_events', False) + return impltype(name=self.name, + schema=schema, + inherit_schema=self.inherit_schema, + metadata=metadata, + _create_events=_create_events, + **kw) + + @property + def bind(self): + return self.metadata and self.metadata.bind or None + + def create(self, bind=None, checkfirst=False): + """Issue CREATE ddl for this type, if applicable.""" + + if bind is None: + bind = _bind_or_error(self) + t = self.dialect_impl(bind.dialect) + if t.__class__ is not self.__class__ and isinstance(t, SchemaType): + t.create(bind=bind, checkfirst=checkfirst) + + def drop(self, bind=None, checkfirst=False): + """Issue DROP ddl for this type, if applicable.""" + + if bind is None: + bind = _bind_or_error(self) + t = self.dialect_impl(bind.dialect) + if t.__class__ is not self.__class__ and isinstance(t, SchemaType): + t.drop(bind=bind, checkfirst=checkfirst) + + def _on_table_create(self, target, bind, **kw): + if not self._is_impl_for_variant(bind.dialect, kw): + return + + t = self.dialect_impl(bind.dialect) + if t.__class__ is not self.__class__ and isinstance(t, SchemaType): + t._on_table_create(target, bind, **kw) + + def _on_table_drop(self, target, bind, **kw): + if not self._is_impl_for_variant(bind.dialect, kw): + return + + t = self.dialect_impl(bind.dialect) + if t.__class__ is not self.__class__ and isinstance(t, SchemaType): + t._on_table_drop(target, bind, **kw) + + def _on_metadata_create(self, target, bind, **kw): + if not self._is_impl_for_variant(bind.dialect, kw): + return + + t = self.dialect_impl(bind.dialect) + if t.__class__ is not self.__class__ and isinstance(t, SchemaType): + t._on_metadata_create(target, bind, **kw) + + def _on_metadata_drop(self, target, bind, **kw): + if not self._is_impl_for_variant(bind.dialect, kw): + return + + t = self.dialect_impl(bind.dialect) + if t.__class__ is not self.__class__ and isinstance(t, SchemaType): + t._on_metadata_drop(target, bind, **kw) + + def _is_impl_for_variant(self, dialect, kw): + variant_mapping = kw.pop('variant_mapping', None) + if variant_mapping is None: + return True + + if dialect.name in variant_mapping and \ + variant_mapping[dialect.name] is self: + return True + elif dialect.name not in variant_mapping: + return variant_mapping['_default'] is self + + +class Enum(Emulated, String, SchemaType): + """Generic Enum Type. + + The :class:`.Enum` type provides a set of possible string values + which the column is constrained towards. + + The :class:`.Enum` type will make use of the backend's native "ENUM" + type if one is available; otherwise, it uses a VARCHAR datatype and + produces a CHECK constraint. Use of the backend-native enum type + can be disabled using the :paramref:`.Enum.native_enum` flag, and + the production of the CHECK constraint is configurable using the + :paramref:`.Enum.create_constraint` flag. + + The :class:`.Enum` type also provides in-Python validation of string + values during both read and write operations. When reading a value + from the database in a result set, the string value is always checked + against the list of possible values and a ``LookupError`` is raised + if no match is found. When passing a value to the database as a + plain string within a SQL statement, if the + :paramref:`.Enum.validate_strings` parameter is + set to True, a ``LookupError`` is raised for any string value that's + not located in the given list of possible values; note that this + impacts usage of LIKE expressions with enumerated values (an unusual + use case). + + .. versionchanged:: 1.1 the :class:`.Enum` type now provides in-Python + validation of input values as well as on data being returned by + the database. + + The source of enumerated values may be a list of string values, or + alternatively a PEP-435-compliant enumerated class. For the purposes + of the :class:`.Enum` datatype, this class need only provide a + ``__members__`` method. + + When using an enumerated class, the enumerated objects are used + both for input and output, rather than strings as is the case with + a plain-string enumerated type:: + + import enum + class MyEnum(enum.Enum): + one = 1 + two = 2 + three = 3 + + + t = Table( + 'data', MetaData(), + Column('value', Enum(MyEnum)) + ) + + connection.execute(t.insert(), {"value": MyEnum.two}) + assert connection.scalar(t.select()) is MyEnum.two + + Above, the string names of each element, e.g. "one", "two", "three", + are persisted to the database; the values of the Python Enum, here + indicated as integers, are **not** used; the value of each enum can + therefore be any kind of Python object whether or not it is persistable. + + In order to persist the values and not the names, the + :paramref:`.Enum.values_callable` parameter may be used. The value of + this parameter is a user-supplied callable, which is intended to be used + with a PEP-435-compliant enumerated class and returns a list of string + values to be persisted. For a simple enumeration that uses string values, + a callable such as ``lambda x: [e.value for e in x]`` is sufficient. + + .. versionadded:: 1.1 - support for PEP-435-style enumerated + classes. + + + .. seealso:: + + :class:`.postgresql.ENUM` - PostgreSQL-specific type, + which has additional functionality. + + :class:`.mysql.ENUM` - MySQL-specific type + + """ + __visit_name__ = 'enum' + + def __init__(self, *enums, **kw): + r"""Construct an enum. + + Keyword arguments which don't apply to a specific backend are ignored + by that backend. + + :param \*enums: either exactly one PEP-435 compliant enumerated type + or one or more string or unicode enumeration labels. If unicode + labels are present, the `convert_unicode` flag is auto-enabled. + + .. versionadded:: 1.1 a PEP-435 style enumerated class may be + passed. + + :param convert_unicode: Enable unicode-aware bind parameter and + result-set processing for this Enum's data. This is set + automatically based on the presence of unicode label strings. + + :param create_constraint: defaults to True. When creating a non-native + enumerated type, also build a CHECK constraint on the database + against the valid values. + + .. versionadded:: 1.1 - added :paramref:`.Enum.create_constraint` + which provides the option to disable the production of the + CHECK constraint for a non-native enumerated type. + + :param metadata: Associate this type directly with a ``MetaData`` + object. For types that exist on the target database as an + independent schema construct (PostgreSQL), this type will be + created and dropped within ``create_all()`` and ``drop_all()`` + operations. If the type is not associated with any ``MetaData`` + object, it will associate itself with each ``Table`` in which it is + used, and will be created when any of those individual tables are + created, after a check is performed for its existence. The type is + only dropped when ``drop_all()`` is called for that ``Table`` + object's metadata, however. + + :param name: The name of this type. This is required for PostgreSQL + and any future supported database which requires an explicitly + named type, or an explicitly named constraint in order to generate + the type and/or a table that uses it. If a PEP-435 enumerated + class was used, its name (converted to lower case) is used by + default. + + :param native_enum: Use the database's native ENUM type when + available. Defaults to True. When False, uses VARCHAR + check + constraint for all backends. + + :param schema: Schema name of this type. For types that exist on the + target database as an independent schema construct (PostgreSQL), + this parameter specifies the named schema in which the type is + present. + + .. note:: + + The ``schema`` of the :class:`.Enum` type does not + by default make use of the ``schema`` established on the + owning :class:`.Table`. If this behavior is desired, + set the ``inherit_schema`` flag to ``True``. + + :param quote: Set explicit quoting preferences for the type's name. + + :param inherit_schema: When ``True``, the "schema" from the owning + :class:`.Table` will be copied to the "schema" attribute of this + :class:`.Enum`, replacing whatever value was passed for the + ``schema`` attribute. This also takes effect when using the + :meth:`.Table.tometadata` operation. + + :param validate_strings: when True, string values that are being + passed to the database in a SQL statement will be checked + for validity against the list of enumerated values. Unrecognized + values will result in a ``LookupError`` being raised. + + .. versionadded:: 1.1.0b2 + + :param values_callable: A callable which will be passed the PEP-435 + compliant enumerated type, which should then return a list of string + values to be persisted. This allows for alternate usages such as + using the string value of an enum to be persisted to the database + instead of its name. + + .. versionadded:: 1.2.3 + + """ + self._enum_init(enums, kw) + + @property + def _enums_argument(self): + if self.enum_class is not None: + return [self.enum_class] + else: + return self.enums + + def _enum_init(self, enums, kw): + """internal init for :class:`.Enum` and subclasses. + + friendly init helper used by subclasses to remove + all the Enum-specific keyword arguments from kw. Allows all + other arguments in kw to pass through. + + """ + self.native_enum = kw.pop('native_enum', True) + self.create_constraint = kw.pop('create_constraint', True) + self.values_callable = kw.pop('values_callable', None) + + values, objects = self._parse_into_values(enums, kw) + self._setup_for_values(values, objects, kw) + + convert_unicode = kw.pop('convert_unicode', None) + self.validate_strings = kw.pop('validate_strings', False) + + if convert_unicode is None: + for e in self.enums: + if isinstance(e, util.text_type): + convert_unicode = True + break + else: + convert_unicode = False + + if self.enums: + length = max(len(x) for x in self.enums) + else: + length = 0 + self._valid_lookup[None] = self._object_lookup[None] = None + + super(Enum, self).__init__( + length=length, + convert_unicode=convert_unicode, + ) + + if self.enum_class: + kw.setdefault('name', self.enum_class.__name__.lower()) + SchemaType.__init__( + self, + name=kw.pop('name', None), + schema=kw.pop('schema', None), + metadata=kw.pop('metadata', None), + inherit_schema=kw.pop('inherit_schema', False), + quote=kw.pop('quote', None), + _create_events=kw.pop('_create_events', True) + ) + + def _parse_into_values(self, enums, kw): + if not enums and '_enums' in kw: + enums = kw.pop('_enums') + + if len(enums) == 1 and hasattr(enums[0], '__members__'): + self.enum_class = enums[0] + if self.values_callable: + values = self.values_callable(self.enum_class) + else: + values = list(self.enum_class.__members__) + objects = [self.enum_class.__members__[k] for k in self.enum_class.__members__] + return values, objects + else: + self.enum_class = None + return enums, enums + + def _setup_for_values(self, values, objects, kw): + self.enums = list(values) + + self._valid_lookup = dict( + zip(reversed(objects), reversed(values)) + ) + + self._object_lookup = dict( + zip(values, objects) + ) + + self._valid_lookup.update([ + (value, self._valid_lookup[self._object_lookup[value]]) + for value in values + ]) + + @property + def native(self): + return self.native_enum + + def _db_value_for_elem(self, elem): + try: + return self._valid_lookup[elem] + except KeyError: + # for unknown string values, we return as is. While we can + # validate these if we wanted, that does not allow for lesser-used + # end-user use cases, such as using a LIKE comparison with an enum, + # or for an application that wishes to apply string tests to an + # ENUM (see [ticket:3725]). While we can decide to differentiate + # here between an INSERT statement and a criteria used in a SELECT, + # for now we're staying conservative w/ behavioral changes (perhaps + # someone has a trigger that handles strings on INSERT) + if not self.validate_strings and \ + isinstance(elem, compat.string_types): + return elem + else: + raise LookupError( + '"%s" is not among the defined enum values' % elem) + + class Comparator(String.Comparator): + + def _adapt_expression(self, op, other_comparator): + op, typ = super(Enum.Comparator, self)._adapt_expression( + op, other_comparator) + if op is operators.concat_op: + typ = String( + self.type.length, + convert_unicode=self.type.convert_unicode) + return op, typ + + comparator_factory = Comparator + + def _object_value_for_elem(self, elem): + try: + return self._object_lookup[elem] + except KeyError: + raise LookupError( + '"%s" is not among the defined enum values' % elem) + + def __repr__(self): + return util.generic_repr( + self, + additional_kw=[('native_enum', True)], + to_inspect=[Enum, SchemaType], + ) + + def adapt_to_emulated(self, impltype, **kw): + kw.setdefault("convert_unicode", self.convert_unicode) + kw.setdefault("validate_strings", self.validate_strings) + kw.setdefault('name', self.name) + kw.setdefault('schema', self.schema) + kw.setdefault('inherit_schema', self.inherit_schema) + kw.setdefault('metadata', self.metadata) + kw.setdefault('_create_events', False) + kw.setdefault('native_enum', self.native_enum) + kw.setdefault('values_callable', self.values_callable) + kw.setdefault('create_constraint', self.create_constraint) + assert '_enums' in kw + return impltype(**kw) + + def adapt(self, impltype, **kw): + kw['_enums'] = self._enums_argument + return super(Enum, self).adapt(impltype, **kw) + + def _should_create_constraint(self, compiler, **kw): + if not self._is_impl_for_variant(compiler.dialect, kw): + return False + return not self.native_enum or \ + not compiler.dialect.supports_native_enum + + @util.dependencies("sqlalchemy.sql.schema") + def _set_table(self, schema, column, table): + SchemaType._set_table(self, column, table) + + if not self.create_constraint: + return + + variant_mapping = self._variant_mapping_for_set_table(column) + + e = schema.CheckConstraint( + type_coerce(column, self).in_(self.enums), + name=_defer_name(self.name), + _create_rule=util.portable_instancemethod( + self._should_create_constraint, + {"variant_mapping": variant_mapping}), + _type_bound=True + ) + assert e.table is table + + def literal_processor(self, dialect): + parent_processor = super( + Enum, self).literal_processor(dialect) + + def process(value): + value = self._db_value_for_elem(value) + if parent_processor: + value = parent_processor(value) + return value + return process + + def bind_processor(self, dialect): + def process(value): + value = self._db_value_for_elem(value) + if parent_processor: + value = parent_processor(value) + return value + + parent_processor = super(Enum, self).bind_processor(dialect) + return process + + def result_processor(self, dialect, coltype): + parent_processor = super(Enum, self).result_processor( + dialect, coltype) + + def process(value): + if parent_processor: + value = parent_processor(value) + + value = self._object_value_for_elem(value) + return value + + return process + + def copy(self, **kw): + return SchemaType.copy(self, **kw) + + @property + def python_type(self): + if self.enum_class: + return self.enum_class + else: + return super(Enum, self).python_type + + +class PickleType(TypeDecorator): + """Holds Python objects, which are serialized using pickle. + + PickleType builds upon the Binary type to apply Python's + ``pickle.dumps()`` to incoming objects, and ``pickle.loads()`` on + the way out, allowing any pickleable Python object to be stored as + a serialized binary field. + + To allow ORM change events to propagate for elements associated + with :class:`.PickleType`, see :ref:`mutable_toplevel`. + + """ + + impl = LargeBinary + + def __init__(self, protocol=pickle.HIGHEST_PROTOCOL, + pickler=None, comparator=None): + """ + Construct a PickleType. + + :param protocol: defaults to ``pickle.HIGHEST_PROTOCOL``. + + :param pickler: defaults to cPickle.pickle or pickle.pickle if + cPickle is not available. May be any object with + pickle-compatible ``dumps` and ``loads`` methods. + + :param comparator: a 2-arg callable predicate used + to compare values of this type. If left as ``None``, + the Python "equals" operator is used to compare values. + + """ + self.protocol = protocol + self.pickler = pickler or pickle + self.comparator = comparator + super(PickleType, self).__init__() + + def __reduce__(self): + return PickleType, (self.protocol, + None, + self.comparator) + + def bind_processor(self, dialect): + impl_processor = self.impl.bind_processor(dialect) + dumps = self.pickler.dumps + protocol = self.protocol + if impl_processor: + def process(value): + if value is not None: + value = dumps(value, protocol) + return impl_processor(value) + else: + def process(value): + if value is not None: + value = dumps(value, protocol) + return value + return process + + def result_processor(self, dialect, coltype): + impl_processor = self.impl.result_processor(dialect, coltype) + loads = self.pickler.loads + if impl_processor: + def process(value): + value = impl_processor(value) + if value is None: + return None + return loads(value) + else: + def process(value): + if value is None: + return None + return loads(value) + return process + + def compare_values(self, x, y): + if self.comparator: + return self.comparator(x, y) + else: + return x == y + + +class Boolean(Emulated, TypeEngine, SchemaType): + + """A bool datatype. + + :class:`.Boolean` typically uses BOOLEAN or SMALLINT on the DDL side, and on + the Python side deals in ``True`` or ``False``. + + The :class:`.Boolean` datatype currently has two levels of assertion + that the values persisted are simple true/false values. For all + backends, only the Python values ``None``, ``True``, ``False``, ``1`` + or ``0`` are accepted as parameter values. For those backends that + don't support a "native boolean" datatype, a CHECK constraint is also + created on the target column. Production of the CHECK constraint + can be disabled by passing the :paramref:`.Boolean.create_constraint` + flag set to ``False``. + + .. versionchanged:: 1.2 the :class:`.Boolean` datatype now asserts that + incoming Python values are already in pure boolean form. + + + """ + + __visit_name__ = 'boolean' + native = True + + def __init__( + self, create_constraint=True, name=None, _create_events=True): + """Construct a Boolean. + + :param create_constraint: defaults to True. If the boolean + is generated as an int/smallint, also create a CHECK constraint + on the table that ensures 1 or 0 as a value. + + :param name: if a CHECK constraint is generated, specify + the name of the constraint. + + """ + self.create_constraint = create_constraint + self.name = name + self._create_events = _create_events + + def _should_create_constraint(self, compiler, **kw): + if not self._is_impl_for_variant(compiler.dialect, kw): + return False + return not compiler.dialect.supports_native_boolean and \ + compiler.dialect.non_native_boolean_check_constraint + + @util.dependencies("sqlalchemy.sql.schema") + def _set_table(self, schema, column, table): + if not self.create_constraint: + return + + variant_mapping = self._variant_mapping_for_set_table(column) + + e = schema.CheckConstraint( + type_coerce(column, self).in_([0, 1]), + name=_defer_name(self.name), + _create_rule=util.portable_instancemethod( + self._should_create_constraint, + {"variant_mapping": variant_mapping}), + _type_bound=True + ) + assert e.table is table + + @property + def python_type(self): + return bool + + _strict_bools = frozenset([None, True, False]) + + def _strict_as_bool(self, value): + if value not in self._strict_bools: + if not isinstance(value, int): + raise TypeError( + "Not a boolean value: %r" % value) + else: + raise ValueError( + "Value %r is not None, True, or False" % value) + return value + + def literal_processor(self, dialect): + compiler = dialect.statement_compiler(dialect, None) + true = compiler.visit_true(None) + false = compiler.visit_false(None) + + def process(value): + return true if self._strict_as_bool(value) else false + return process + + def bind_processor(self, dialect): + _strict_as_bool = self._strict_as_bool + if dialect.supports_native_boolean: + _coerce = bool + else: + _coerce = int + + def process(value): + value = _strict_as_bool(value) + if value is not None: + value = _coerce(value) + return value + return process + + def result_processor(self, dialect, coltype): + if dialect.supports_native_boolean: + return None + else: + return processors.int_to_boolean + + +class _AbstractInterval(_LookupExpressionAdapter, TypeEngine): + @util.memoized_property + def _expression_adaptations(self): + # Based on http://www.postgresql.org/docs/current/\ + # static/functions-datetime.html. + + return { + operators.add: { + Date: DateTime, + Interval: self.__class__, + DateTime: DateTime, + Time: Time, + }, + operators.sub: { + Interval: self.__class__ + }, + operators.mul: { + Numeric: self.__class__ + }, + operators.truediv: { + Numeric: self.__class__ + }, + operators.div: { + Numeric: self.__class__ + } + } + + @property + def _type_affinity(self): + return Interval + + def coerce_compared_value(self, op, value): + """See :meth:`.TypeEngine.coerce_compared_value` for a description.""" + return self.impl.coerce_compared_value(op, value) + + +class Interval(Emulated, _AbstractInterval, TypeDecorator): + + """A type for ``datetime.timedelta()`` objects. + + The Interval type deals with ``datetime.timedelta`` objects. In + PostgreSQL, the native ``INTERVAL`` type is used; for others, the + value is stored as a date which is relative to the "epoch" + (Jan. 1, 1970). + + Note that the ``Interval`` type does not currently provide date arithmetic + operations on platforms which do not support interval types natively. Such + operations usually require transformation of both sides of the expression + (such as, conversion of both sides into integer epoch values first) which + currently is a manual procedure (such as via + :attr:`~sqlalchemy.sql.expression.func`). + + """ + + impl = DateTime + epoch = dt.datetime.utcfromtimestamp(0) + + def __init__(self, native=True, + second_precision=None, + day_precision=None): + """Construct an Interval object. + + :param native: when True, use the actual + INTERVAL type provided by the database, if + supported (currently PostgreSQL, Oracle). + Otherwise, represent the interval data as + an epoch value regardless. + + :param second_precision: For native interval types + which support a "fractional seconds precision" parameter, + i.e. Oracle and PostgreSQL + + :param day_precision: for native interval types which + support a "day precision" parameter, i.e. Oracle. + + """ + super(Interval, self).__init__() + self.native = native + self.second_precision = second_precision + self.day_precision = day_precision + + @property + def python_type(self): + return dt.timedelta + + def adapt_to_emulated(self, impltype, **kw): + return _AbstractInterval.adapt(self, impltype, **kw) + + def bind_processor(self, dialect): + impl_processor = self.impl.bind_processor(dialect) + epoch = self.epoch + if impl_processor: + def process(value): + if value is not None: + value = epoch + value + return impl_processor(value) + else: + def process(value): + if value is not None: + value = epoch + value + return value + return process + + def result_processor(self, dialect, coltype): + impl_processor = self.impl.result_processor(dialect, coltype) + epoch = self.epoch + if impl_processor: + def process(value): + value = impl_processor(value) + if value is None: + return None + return value - epoch + else: + def process(value): + if value is None: + return None + return value - epoch + return process + + +class JSON(Indexable, TypeEngine): + """Represent a SQL JSON type. + + .. note:: :class:`.types.JSON` is provided as a facade for vendor-specific + JSON types. Since it supports JSON SQL operations, it only + works on backends that have an actual JSON type, currently + PostgreSQL as well as certain versions of MySQL. + + :class:`.types.JSON` is part of the Core in support of the growing + popularity of native JSON datatypes. + + The :class:`.types.JSON` type stores arbitrary JSON format data, e.g.:: + + data_table = Table('data_table', metadata, + Column('id', Integer, primary_key=True), + Column('data', JSON) + ) + + with engine.connect() as conn: + conn.execute( + data_table.insert(), + data = {"key1": "value1", "key2": "value2"} + ) + + The base :class:`.types.JSON` provides these two operations: + + * Keyed index operations:: + + data_table.c.data['some key'] + + * Integer index operations:: + + data_table.c.data[3] + + * Path index operations:: + + data_table.c.data[('key_1', 'key_2', 5, ..., 'key_n')] + + Additional operations are available from the dialect-specific versions + of :class:`.types.JSON`, such as :class:`.postgresql.JSON` and + :class:`.postgresql.JSONB`, each of which offer more operators than + just the basic type. + + Index operations return an expression object whose type defaults to + :class:`.JSON` by default, so that further JSON-oriented instructions + may be called upon the result type. Note that there are backend-specific + idiosyncracies here, including that the Postgresql database does not generally + compare a "json" to a "json" structure without type casts. These idiosyncracies + can be accommodated in a backend-neutral way by by making explicit use + of the :func:`.cast` and :func:`.type_coerce` constructs. + Comparison of specific index elements of a :class:`.JSON` object + to other objects work best if the **left hand side is CAST to a string** + and the **right hand side is rendered as a json string**; a future SQLAlchemy + feature such as a generic "astext" modifier may simplify this at some point: + + * **Compare an element of a JSON structure to a string**:: + + from sqlalchemy import cast, type_coerce + from sqlalchemy import String, JSON + + cast( + data_table.c.data['some_key'], String + ) == '"some_value"' + + cast( + data_table.c.data['some_key'], String + ) == type_coerce("some_value", JSON) + + * **Compare an element of a JSON structure to an integer**:: + + from sqlalchemy import cast, type_coerce + from sqlalchemy import String, JSON + + cast(data_table.c.data['some_key'], String) == '55' + + cast( + data_table.c.data['some_key'], String + ) == type_coerce(55, JSON) + + * **Compare an element of a JSON structure to some other JSON structure** - note + that Python dictionaries are typically not ordered so care should be taken + here to assert that the JSON structures are identical:: + + from sqlalchemy import cast, type_coerce + from sqlalchemy import String, JSON + import json + + cast( + data_table.c.data['some_key'], String + ) == json.dumps({"foo": "bar"}) + + cast( + data_table.c.data['some_key'], String + ) == type_coerce({"foo": "bar"}, JSON) + + The :class:`.JSON` type, when used with the SQLAlchemy ORM, does not + detect in-place mutations to the structure. In order to detect these, the + :mod:`sqlalchemy.ext.mutable` extension must be used. This extension will + allow "in-place" changes to the datastructure to produce events which + will be detected by the unit of work. See the example at :class:`.HSTORE` + for a simple example involving a dictionary. + + When working with NULL values, the :class:`.JSON` type recommends the + use of two specific constants in order to differentiate between a column + that evaluates to SQL NULL, e.g. no value, vs. the JSON-encoded string + of ``"null"``. To insert or select against a value that is SQL NULL, + use the constant :func:`.null`:: + + from sqlalchemy import null + conn.execute(table.insert(), json_value=null()) + + To insert or select against a value that is JSON ``"null"``, use the + constant :attr:`.JSON.NULL`:: + + conn.execute(table.insert(), json_value=JSON.NULL) + + The :class:`.JSON` type supports a flag + :paramref:`.JSON.none_as_null` which when set to True will result + in the Python constant ``None`` evaluating to the value of SQL + NULL, and when set to False results in the Python constant + ``None`` evaluating to the value of JSON ``"null"``. The Python + value ``None`` may be used in conjunction with either + :attr:`.JSON.NULL` and :func:`.null` in order to indicate NULL + values, but care must be taken as to the value of the + :paramref:`.JSON.none_as_null` in these cases. + + .. seealso:: + + :class:`.postgresql.JSON` + + :class:`.postgresql.JSONB` + + :class:`.mysql.JSON` + + .. versionadded:: 1.1 + + + """ + __visit_name__ = 'JSON' + + hashable = False + NULL = util.symbol('JSON_NULL') + """Describe the json value of NULL. + + This value is used to force the JSON value of ``"null"`` to be + used as the value. A value of Python ``None`` will be recognized + either as SQL NULL or JSON ``"null"``, based on the setting + of the :paramref:`.JSON.none_as_null` flag; the :attr:`.JSON.NULL` + constant can be used to always resolve to JSON ``"null"`` regardless + of this setting. This is in contrast to the :func:`.sql.null` construct, + which always resolves to SQL NULL. E.g.:: + + from sqlalchemy import null + from sqlalchemy.dialects.postgresql import JSON + + obj1 = MyObject(json_value=null()) # will *always* insert SQL NULL + obj2 = MyObject(json_value=JSON.NULL) # will *always* insert JSON string "null" + + session.add_all([obj1, obj2]) + session.commit() + + In order to set JSON NULL as a default value for a column, the most + transparent method is to use :func:`.text`:: + + Table( + 'my_table', metadata, + Column('json_data', JSON, default=text("'null'")) + ) + + While it is possible to use :attr:`.JSON.NULL` in this context, the + :attr:`.JSON.NULL` value will be returned as the value of the column, + which in the context of the ORM or other repurposing of the default + value, may not be desirable. Using a SQL expression means the value + will be re-fetched from the database within the context of retrieving + generated defaults. + + + """ + + def __init__(self, none_as_null=False): + """Construct a :class:`.types.JSON` type. + + :param none_as_null=False: if True, persist the value ``None`` as a + SQL NULL value, not the JSON encoding of ``null``. Note that + when this flag is False, the :func:`.null` construct can still + be used to persist a NULL value:: + + from sqlalchemy import null + conn.execute(table.insert(), data=null()) + + .. note:: + + :paramref:`.JSON.none_as_null` does **not** apply to the + values passed to :paramref:`.Column.default` and + :paramref:`.Column.server_default`; a value of ``None`` passed for + these parameters means "no default present". + + .. seealso:: + + :attr:`.types.JSON.NULL` + + """ + self.none_as_null = none_as_null + + class JSONElementType(TypeEngine): + """common function for index / path elements in a JSON expression.""" + + _integer = Integer() + _string = String() + + def string_bind_processor(self, dialect): + return self._string._cached_bind_processor(dialect) + + def string_literal_processor(self, dialect): + return self._string._cached_literal_processor(dialect) + + def bind_processor(self, dialect): + int_processor = self._integer._cached_bind_processor(dialect) + string_processor = self.string_bind_processor(dialect) + + def process(value): + if int_processor and isinstance(value, int): + value = int_processor(value) + elif string_processor and isinstance(value, util.string_types): + value = string_processor(value) + return value + + return process + + def literal_processor(self, dialect): + int_processor = self._integer._cached_literal_processor(dialect) + string_processor = self.string_literal_processor(dialect) + + def process(value): + if int_processor and isinstance(value, int): + value = int_processor(value) + elif string_processor and isinstance(value, util.string_types): + value = string_processor(value) + return value + + return process + + class JSONIndexType(JSONElementType): + """Placeholder for the datatype of a JSON index value. + + This allows execution-time processing of JSON index values + for special syntaxes. + + """ + + class JSONPathType(JSONElementType): + """Placeholder type for JSON path operations. + + This allows execution-time processing of a path-based + index value into a specific SQL syntax. + + """ + + class Comparator(Indexable.Comparator, Concatenable.Comparator): + """Define comparison operations for :class:`.types.JSON`.""" + + @util.dependencies('sqlalchemy.sql.default_comparator') + def _setup_getitem(self, default_comparator, index): + if not isinstance(index, util.string_types) and \ + isinstance(index, compat.collections_abc.Sequence): + index = default_comparator._check_literal( + self.expr, operators.json_path_getitem_op, + index, bindparam_type=JSON.JSONPathType + ) + + operator = operators.json_path_getitem_op + else: + index = default_comparator._check_literal( + self.expr, operators.json_getitem_op, + index, bindparam_type=JSON.JSONIndexType + ) + operator = operators.json_getitem_op + + return operator, index, self.type + + comparator_factory = Comparator + + @property + def python_type(self): + return dict + + @property + def should_evaluate_none(self): + return not self.none_as_null + + @util.memoized_property + def _str_impl(self): + return String(convert_unicode=True) + + def bind_processor(self, dialect): + string_process = self._str_impl.bind_processor(dialect) + + json_serializer = dialect._json_serializer or json.dumps + + def process(value): + if value is self.NULL: + value = None + elif isinstance(value, elements.Null) or ( + value is None and self.none_as_null + ): + return None + + serialized = json_serializer(value) + if string_process: + serialized = string_process(serialized) + return serialized + + return process + + def result_processor(self, dialect, coltype): + string_process = self._str_impl.result_processor(dialect, coltype) + json_deserializer = dialect._json_deserializer or json.loads + + def process(value): + if value is None: + return None + if string_process: + value = string_process(value) + return json_deserializer(value) + return process + + +class ARRAY(SchemaEventTarget, Indexable, Concatenable, TypeEngine): + """Represent a SQL Array type. + + .. note:: This type serves as the basis for all ARRAY operations. + However, currently **only the PostgreSQL backend has support + for SQL arrays in SQLAlchemy**. It is recommended to use the + :class:`.postgresql.ARRAY` type directly when using ARRAY types + with PostgreSQL, as it provides additional operators specific + to that backend. + + :class:`.types.ARRAY` is part of the Core in support of various SQL standard + functions such as :class:`.array_agg` which explicitly involve arrays; + however, with the exception of the PostgreSQL backend and possibly + some third-party dialects, no other SQLAlchemy built-in dialect has + support for this type. + + An :class:`.types.ARRAY` type is constructed given the "type" + of element:: + + mytable = Table("mytable", metadata, + Column("data", ARRAY(Integer)) + ) + + The above type represents an N-dimensional array, + meaning a supporting backend such as PostgreSQL will interpret values + with any number of dimensions automatically. To produce an INSERT + construct that passes in a 1-dimensional array of integers:: + + connection.execute( + mytable.insert(), + data=[1,2,3] + ) + + The :class:`.types.ARRAY` type can be constructed given a fixed number + of dimensions:: + + mytable = Table("mytable", metadata, + Column("data", ARRAY(Integer, dimensions=2)) + ) + + Sending a number of dimensions is optional, but recommended if the + datatype is to represent arrays of more than one dimension. This number + is used: + + * When emitting the type declaration itself to the database, e.g. + ``INTEGER[][]`` + + * When translating Python values to database values, and vice versa, e.g. + an ARRAY of :class:`.Unicode` objects uses this number to efficiently + access the string values inside of array structures without resorting + to per-row type inspection + + * When used with the Python ``getitem`` accessor, the number of dimensions + serves to define the kind of type that the ``[]`` operator should + return, e.g. for an ARRAY of INTEGER with two dimensions:: + + >>> expr = table.c.column[5] # returns ARRAY(Integer, dimensions=1) + >>> expr = expr[6] # returns Integer + + For 1-dimensional arrays, an :class:`.types.ARRAY` instance with no + dimension parameter will generally assume single-dimensional behaviors. + + SQL expressions of type :class:`.types.ARRAY` have support for "index" and + "slice" behavior. The Python ``[]`` operator works normally here, given + integer indexes or slices. Arrays default to 1-based indexing. + The operator produces binary expression + constructs which will produce the appropriate SQL, both for + SELECT statements:: + + select([mytable.c.data[5], mytable.c.data[2:7]]) + + as well as UPDATE statements when the :meth:`.Update.values` method + is used:: + + mytable.update().values({ + mytable.c.data[5]: 7, + mytable.c.data[2:7]: [1, 2, 3] + }) + + The :class:`.types.ARRAY` type also provides for the operators + :meth:`.types.ARRAY.Comparator.any` and :meth:`.types.ARRAY.Comparator.all`. + The PostgreSQL-specific version of :class:`.types.ARRAY` also provides additional + operators. + + .. versionadded:: 1.1.0 + + .. seealso:: + + :class:`.postgresql.ARRAY` + + """ + __visit_name__ = 'ARRAY' + + zero_indexes = False + """if True, Python zero-based indexes should be interpreted as one-based + on the SQL expression side.""" + + class Comparator(Indexable.Comparator, Concatenable.Comparator): + + """Define comparison operations for :class:`.types.ARRAY`. + + More operators are available on the dialect-specific form + of this type. See :class:`.postgresql.ARRAY.Comparator`. + + """ + + def _setup_getitem(self, index): + if isinstance(index, slice): + return_type = self.type + if self.type.zero_indexes: + index = slice( + index.start + 1, + index.stop + 1, + index.step + ) + index = Slice( + _literal_as_binds( + index.start, name=self.expr.key, + type_=type_api.INTEGERTYPE), + _literal_as_binds( + index.stop, name=self.expr.key, + type_=type_api.INTEGERTYPE), + _literal_as_binds( + index.step, name=self.expr.key, + type_=type_api.INTEGERTYPE) + ) + else: + if self.type.zero_indexes: + index += 1 + if self.type.dimensions is None or self.type.dimensions == 1: + return_type = self.type.item_type + else: + adapt_kw = {'dimensions': self.type.dimensions - 1} + return_type = self.type.adapt( + self.type.__class__, **adapt_kw) + + return operators.getitem, index, return_type + + def contains(self, *arg, **kw): + raise NotImplementedError( + "ARRAY.contains() not implemented for the base " + "ARRAY type; please use the dialect-specific ARRAY type") + + @util.dependencies("sqlalchemy.sql.elements") + def any(self, elements, other, operator=None): + """Return ``other operator ANY (array)`` clause. + + Argument places are switched, because ANY requires array + expression to be on the right hand-side. + + E.g.:: + + from sqlalchemy.sql import operators + + conn.execute( + select([table.c.data]).where( + table.c.data.any(7, operator=operators.lt) + ) + ) + + :param other: expression to be compared + :param operator: an operator object from the + :mod:`sqlalchemy.sql.operators` + package, defaults to :func:`.operators.eq`. + + .. seealso:: + + :func:`.sql.expression.any_` + + :meth:`.types.ARRAY.Comparator.all` + + """ + operator = operator if operator else operators.eq + return operator( + elements._literal_as_binds(other), + elements.CollectionAggregate._create_any(self.expr) + ) + + @util.dependencies("sqlalchemy.sql.elements") + def all(self, elements, other, operator=None): + """Return ``other operator ALL (array)`` clause. + + Argument places are switched, because ALL requires array + expression to be on the right hand-side. + + E.g.:: + + from sqlalchemy.sql import operators + + conn.execute( + select([table.c.data]).where( + table.c.data.all(7, operator=operators.lt) + ) + ) + + :param other: expression to be compared + :param operator: an operator object from the + :mod:`sqlalchemy.sql.operators` + package, defaults to :func:`.operators.eq`. + + .. seealso:: + + :func:`.sql.expression.all_` + + :meth:`.types.ARRAY.Comparator.any` + + """ + operator = operator if operator else operators.eq + return operator( + elements._literal_as_binds(other), + elements.CollectionAggregate._create_all(self.expr) + ) + + comparator_factory = Comparator + + def __init__(self, item_type, as_tuple=False, dimensions=None, + zero_indexes=False): + """Construct an :class:`.types.ARRAY`. + + E.g.:: + + Column('myarray', ARRAY(Integer)) + + Arguments are: + + :param item_type: The data type of items of this array. Note that + dimensionality is irrelevant here, so multi-dimensional arrays like + ``INTEGER[][]``, are constructed as ``ARRAY(Integer)``, not as + ``ARRAY(ARRAY(Integer))`` or such. + + :param as_tuple=False: Specify whether return results + should be converted to tuples from lists. This parameter is + not generally needed as a Python list corresponds well + to a SQL array. + + :param dimensions: if non-None, the ARRAY will assume a fixed + number of dimensions. This impacts how the array is declared + on the database, how it goes about interpreting Python and + result values, as well as how expression behavior in conjunction + with the "getitem" operator works. See the description at + :class:`.types.ARRAY` for additional detail. + + :param zero_indexes=False: when True, index values will be converted + between Python zero-based and SQL one-based indexes, e.g. + a value of one will be added to all index values before passing + to the database. + + """ + if isinstance(item_type, ARRAY): + raise ValueError("Do not nest ARRAY types; ARRAY(basetype) " + "handles multi-dimensional arrays of basetype") + if isinstance(item_type, type): + item_type = item_type() + self.item_type = item_type + self.as_tuple = as_tuple + self.dimensions = dimensions + self.zero_indexes = zero_indexes + + @property + def hashable(self): + return self.as_tuple + + @property + def python_type(self): + return list + + def compare_values(self, x, y): + return x == y + + def _set_parent(self, column): + """Support SchemaEventTarget""" + + if isinstance(self.item_type, SchemaEventTarget): + self.item_type._set_parent(column) + + def _set_parent_with_dispatch(self, parent): + """Support SchemaEventTarget""" + + super(ARRAY, self)._set_parent_with_dispatch(parent) + + if isinstance(self.item_type, SchemaEventTarget): + self.item_type._set_parent_with_dispatch(parent) + + +class REAL(Float): + + """The SQL REAL type.""" + + __visit_name__ = 'REAL' + + +class FLOAT(Float): + + """The SQL FLOAT type.""" + + __visit_name__ = 'FLOAT' + + +class NUMERIC(Numeric): + + """The SQL NUMERIC type.""" + + __visit_name__ = 'NUMERIC' + + +class DECIMAL(Numeric): + + """The SQL DECIMAL type.""" + + __visit_name__ = 'DECIMAL' + + +class INTEGER(Integer): + + """The SQL INT or INTEGER type.""" + + __visit_name__ = 'INTEGER' +INT = INTEGER + + +class SMALLINT(SmallInteger): + + """The SQL SMALLINT type.""" + + __visit_name__ = 'SMALLINT' + + +class BIGINT(BigInteger): + + """The SQL BIGINT type.""" + + __visit_name__ = 'BIGINT' + + +class TIMESTAMP(DateTime): + + """The SQL TIMESTAMP type. + + :class:`~.types.TIMESTAMP` datatypes have support for timezone + storage on some backends, such as PostgreSQL and Oracle. Use the + :paramref:`~types.TIMESTAMP.timezone` argument in order to enable + "TIMESTAMP WITH TIMEZONE" for these backends. + + """ + + __visit_name__ = 'TIMESTAMP' + + def __init__(self, timezone=False): + """Construct a new :class:`.TIMESTAMP`. + + :param timezone: boolean. Indicates that the TIMESTAMP type should + enable timezone support, if available on the target database. + On a per-dialect basis is similar to "TIMESTAMP WITH TIMEZONE". + If the target database does not support timezones, this flag is + ignored. + + + """ + super(TIMESTAMP, self).__init__(timezone=timezone) + + def get_dbapi_type(self, dbapi): + return dbapi.TIMESTAMP + + +class DATETIME(DateTime): + + """The SQL DATETIME type.""" + + __visit_name__ = 'DATETIME' + + +class DATE(Date): + + """The SQL DATE type.""" + + __visit_name__ = 'DATE' + + +class TIME(Time): + + """The SQL TIME type.""" + + __visit_name__ = 'TIME' + + +class TEXT(Text): + + """The SQL TEXT type.""" + + __visit_name__ = 'TEXT' + + +class CLOB(Text): + + """The CLOB type. + + This type is found in Oracle and Informix. + """ + + __visit_name__ = 'CLOB' + + +class VARCHAR(String): + + """The SQL VARCHAR type.""" + + __visit_name__ = 'VARCHAR' + + +class NVARCHAR(Unicode): + + """The SQL NVARCHAR type.""" + + __visit_name__ = 'NVARCHAR' + + +class CHAR(String): + + """The SQL CHAR type.""" + + __visit_name__ = 'CHAR' + + +class NCHAR(Unicode): + + """The SQL NCHAR type.""" + + __visit_name__ = 'NCHAR' + + +class BLOB(LargeBinary): + + """The SQL BLOB type.""" + + __visit_name__ = 'BLOB' + + +class BINARY(_Binary): + + """The SQL BINARY type.""" + + __visit_name__ = 'BINARY' + + +class VARBINARY(_Binary): + + """The SQL VARBINARY type.""" + + __visit_name__ = 'VARBINARY' + + +class BOOLEAN(Boolean): + + """The SQL BOOLEAN type.""" + + __visit_name__ = 'BOOLEAN' + + +class NullType(TypeEngine): + + """An unknown type. + + :class:`.NullType` is used as a default type for those cases where + a type cannot be determined, including: + + * During table reflection, when the type of a column is not recognized + by the :class:`.Dialect` + * When constructing SQL expressions using plain Python objects of + unknown types (e.g. ``somecolumn == my_special_object``) + * When a new :class:`.Column` is created, and the given type is passed + as ``None`` or is not passed at all. + + The :class:`.NullType` can be used within SQL expression invocation + without issue, it just has no behavior either at the expression + construction level or at the bind-parameter/result processing level. + :class:`.NullType` will result in a :exc:`.CompileError` if the compiler + is asked to render the type itself, such as if it is used in a + :func:`.cast` operation or within a schema creation operation such as that + invoked by :meth:`.MetaData.create_all` or the :class:`.CreateTable` + construct. + + """ + __visit_name__ = 'null' + + _isnull = True + + hashable = False + + def literal_processor(self, dialect): + def process(value): + return "NULL" + return process + + class Comparator(TypeEngine.Comparator): + + def _adapt_expression(self, op, other_comparator): + if isinstance(other_comparator, NullType.Comparator) or \ + not operators.is_commutative(op): + return op, self.expr.type + else: + return other_comparator._adapt_expression(op, self) + comparator_factory = Comparator + + +class MatchType(Boolean): + """Refers to the return type of the MATCH operator. + + As the :meth:`.ColumnOperators.match` is probably the most open-ended + operator in generic SQLAlchemy Core, we can't assume the return type + at SQL evaluation time, as MySQL returns a floating point, not a boolean, + and other backends might do something different. So this type + acts as a placeholder, currently subclassing :class:`.Boolean`. + The type allows dialects to inject result-processing functionality + if needed, and on MySQL will return floating-point values. + + .. versionadded:: 1.0.0 + + """ + +NULLTYPE = NullType() +BOOLEANTYPE = Boolean() +STRINGTYPE = String() +INTEGERTYPE = Integer() +MATCHTYPE = MatchType() + +_type_map = { + int: Integer(), + float: Float(), + bool: BOOLEANTYPE, + decimal.Decimal: Numeric(), + dt.date: Date(), + dt.datetime: DateTime(), + dt.time: Time(), + dt.timedelta: Interval(), + util.NoneType: NULLTYPE +} + +if util.py3k: + _type_map[bytes] = LargeBinary() + _type_map[str] = Unicode() +else: + _type_map[unicode] = Unicode() + _type_map[str] = String() + +_type_map_get = _type_map.get + + +def _resolve_value_to_type(value): + _result_type = _type_map_get(type(value), False) + if _result_type is False: + # use inspect() to detect SQLAlchemy built-in + # objects. + insp = inspection.inspect(value, False) + if ( + insp is not None and + # foil mock.Mock() and other impostors by ensuring + # the inspection target itself self-inspects + insp.__class__ in inspection._registrars + ): + raise exc.ArgumentError( + "Object %r is not legal as a SQL literal value" % value) + return NULLTYPE + else: + return _result_type + +# back-assign to type_api +from . import type_api +type_api.BOOLEANTYPE = BOOLEANTYPE +type_api.STRINGTYPE = STRINGTYPE +type_api.INTEGERTYPE = INTEGERTYPE +type_api.NULLTYPE = NULLTYPE +type_api.MATCHTYPE = MATCHTYPE +type_api.INDEXABLE = Indexable +type_api._resolve_value_to_type = _resolve_value_to_type +TypeEngine.Comparator.BOOLEANTYPE = BOOLEANTYPE diff --git a/venv/Lib/site-packages/sqlalchemy/sql/type_api.py b/venv/Lib/site-packages/sqlalchemy/sql/type_api.py new file mode 100644 index 0000000..b48a025 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/type_api.py @@ -0,0 +1,1413 @@ +# sql/types_api.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Base types API. + +""" + + +from .. import exc, util +from . import operators +from .visitors import Visitable, VisitableType +from .base import SchemaEventTarget + +# these are back-assigned by sqltypes. +BOOLEANTYPE = None +INTEGERTYPE = None +NULLTYPE = None +STRINGTYPE = None +MATCHTYPE = None +INDEXABLE = None +_resolve_value_to_type = None + + +class TypeEngine(Visitable): + """The ultimate base class for all SQL datatypes. + + Common subclasses of :class:`.TypeEngine` include + :class:`.String`, :class:`.Integer`, and :class:`.Boolean`. + + For an overview of the SQLAlchemy typing system, see + :ref:`types_toplevel`. + + .. seealso:: + + :ref:`types_toplevel` + + """ + + _sqla_type = True + _isnull = False + + class Comparator(operators.ColumnOperators): + """Base class for custom comparison operations defined at the + type level. See :attr:`.TypeEngine.comparator_factory`. + + + """ + __slots__ = 'expr', 'type' + + default_comparator = None + + def __init__(self, expr): + self.expr = expr + self.type = expr.type + + @util.dependencies('sqlalchemy.sql.default_comparator') + def operate(self, default_comparator, op, *other, **kwargs): + o = default_comparator.operator_lookup[op.__name__] + return o[0](self.expr, op, *(other + o[1:]), **kwargs) + + @util.dependencies('sqlalchemy.sql.default_comparator') + def reverse_operate(self, default_comparator, op, other, **kwargs): + o = default_comparator.operator_lookup[op.__name__] + return o[0](self.expr, op, other, + reverse=True, *o[1:], **kwargs) + + def _adapt_expression(self, op, other_comparator): + """evaluate the return type of <self> <op> <othertype>, + and apply any adaptations to the given operator. + + This method determines the type of a resulting binary expression + given two source types and an operator. For example, two + :class:`.Column` objects, both of the type :class:`.Integer`, will + produce a :class:`.BinaryExpression` that also has the type + :class:`.Integer` when compared via the addition (``+``) operator. + However, using the addition operator with an :class:`.Integer` + and a :class:`.Date` object will produce a :class:`.Date`, assuming + "days delta" behavior by the database (in reality, most databases + other than PostgreSQL don't accept this particular operation). + + The method returns a tuple of the form <operator>, <type>. + The resulting operator and type will be those applied to the + resulting :class:`.BinaryExpression` as the final operator and the + right-hand side of the expression. + + Note that only a subset of operators make usage of + :meth:`._adapt_expression`, + including math operators and user-defined operators, but not + boolean comparison or special SQL keywords like MATCH or BETWEEN. + + """ + + return op, self.type + + def __reduce__(self): + return _reconstitute_comparator, (self.expr, ) + + hashable = True + """Flag, if False, means values from this type aren't hashable. + + Used by the ORM when uniquing result lists. + + """ + + comparator_factory = Comparator + """A :class:`.TypeEngine.Comparator` class which will apply + to operations performed by owning :class:`.ColumnElement` objects. + + The :attr:`.comparator_factory` attribute is a hook consulted by + the core expression system when column and SQL expression operations + are performed. When a :class:`.TypeEngine.Comparator` class is + associated with this attribute, it allows custom re-definition of + all existing operators, as well as definition of new operators. + Existing operators include those provided by Python operator overloading + such as :meth:`.operators.ColumnOperators.__add__` and + :meth:`.operators.ColumnOperators.__eq__`, + those provided as standard + attributes of :class:`.operators.ColumnOperators` such as + :meth:`.operators.ColumnOperators.like` + and :meth:`.operators.ColumnOperators.in_`. + + Rudimentary usage of this hook is allowed through simple subclassing + of existing types, or alternatively by using :class:`.TypeDecorator`. + See the documentation section :ref:`types_operators` for examples. + + .. versionadded:: 0.8 The expression system was enhanced to support + customization of operators on a per-type level. + + """ + + should_evaluate_none = False + """If True, the Python constant ``None`` is considered to be handled + explicitly by this type. + + The ORM uses this flag to indicate that a positive value of ``None`` + is passed to the column in an INSERT statement, rather than omitting + the column from the INSERT statement which has the effect of firing + off column-level defaults. It also allows types which have special + behavior for Python None, such as a JSON type, to indicate that + they'd like to handle the None value explicitly. + + To set this flag on an existing type, use the + :meth:`.TypeEngine.evaluates_none` method. + + .. seealso:: + + :meth:`.TypeEngine.evaluates_none` + + .. versionadded:: 1.1 + + + """ + + def evaluates_none(self): + """Return a copy of this type which has the :attr:`.should_evaluate_none` + flag set to True. + + E.g.:: + + Table( + 'some_table', metadata, + Column( + String(50).evaluates_none(), + nullable=True, + server_default='no value') + ) + + The ORM uses this flag to indicate that a positive value of ``None`` + is passed to the column in an INSERT statement, rather than omitting + the column from the INSERT statement which has the effect of firing + off column-level defaults. It also allows for types which have + special behavior associated with the Python None value to indicate + that the value doesn't necessarily translate into SQL NULL; a + prime example of this is a JSON type which may wish to persist the + JSON value ``'null'``. + + In all cases, the actual NULL SQL value can be always be + persisted in any column by using + the :obj:`~.expression.null` SQL construct in an INSERT statement + or associated with an ORM-mapped attribute. + + .. note:: + + The "evaulates none" flag does **not** apply to a value + of ``None`` passed to :paramref:`.Column.default` or + :paramref:`.Column.server_default`; in these cases, ``None`` + still means "no default". + + .. versionadded:: 1.1 + + .. seealso:: + + :ref:`session_forcing_null` - in the ORM documentation + + :paramref:`.postgresql.JSON.none_as_null` - PostgreSQL JSON + interaction with this flag. + + :attr:`.TypeEngine.should_evaluate_none` - class-level flag + + """ + typ = self.copy() + typ.should_evaluate_none = True + return typ + + def copy(self, **kw): + return self.adapt(self.__class__) + + def compare_against_backend(self, dialect, conn_type): + """Compare this type against the given backend type. + + This function is currently not implemented for SQLAlchemy + types, and for all built in types will return ``None``. However, + it can be implemented by a user-defined type + where it can be consumed by schema comparison tools such as + Alembic autogenerate. + + A future release of SQLAlchemy will potentially impement this method + for builtin types as well. + + The function should return True if this type is equivalent to the + given type; the type is typically reflected from the database + so should be database specific. The dialect in use is also + passed. It can also return False to assert that the type is + not equivalent. + + :param dialect: a :class:`.Dialect` that is involved in the comparison. + + :param conn_type: the type object reflected from the backend. + + .. versionadded:: 1.0.3 + + """ + return None + + def copy_value(self, value): + return value + + def literal_processor(self, dialect): + """Return a conversion function for processing literal values that are + to be rendered directly without using binds. + + This function is used when the compiler makes use of the + "literal_binds" flag, typically used in DDL generation as well + as in certain scenarios where backends don't accept bound parameters. + + .. versionadded:: 0.9.0 + + """ + return None + + def bind_processor(self, dialect): + """Return a conversion function for processing bind values. + + Returns a callable which will receive a bind parameter value + as the sole positional argument and will return a value to + send to the DB-API. + + If processing is not necessary, the method should return ``None``. + + :param dialect: Dialect instance in use. + + """ + return None + + def result_processor(self, dialect, coltype): + """Return a conversion function for processing result row values. + + Returns a callable which will receive a result row column + value as the sole positional argument and will return a value + to return to the user. + + If processing is not necessary, the method should return ``None``. + + :param dialect: Dialect instance in use. + + :param coltype: DBAPI coltype argument received in cursor.description. + + """ + return None + + def column_expression(self, colexpr): + """Given a SELECT column expression, return a wrapping SQL expression. + + This is typically a SQL function that wraps a column expression + as rendered in the columns clause of a SELECT statement. + It is used for special data types that require + columns to be wrapped in some special database function in order + to coerce the value before being sent back to the application. + It is the SQL analogue of the :meth:`.TypeEngine.result_processor` + method. + + The method is evaluated at statement compile time, as opposed + to statement construction time. + + See also: + + :ref:`types_sql_value_processing` + + """ + + return None + + @util.memoized_property + def _has_column_expression(self): + """memoized boolean, check if column_expression is implemented. + + Allows the method to be skipped for the vast majority of expression + types that don't use this feature. + + """ + + return self.__class__.column_expression.__code__ \ + is not TypeEngine.column_expression.__code__ + + def bind_expression(self, bindvalue): + """"Given a bind value (i.e. a :class:`.BindParameter` instance), + return a SQL expression in its place. + + This is typically a SQL function that wraps the existing bound + parameter within the statement. It is used for special data types + that require literals being wrapped in some special database function + in order to coerce an application-level value into a database-specific + format. It is the SQL analogue of the + :meth:`.TypeEngine.bind_processor` method. + + The method is evaluated at statement compile time, as opposed + to statement construction time. + + Note that this method, when implemented, should always return + the exact same structure, without any conditional logic, as it + may be used in an executemany() call against an arbitrary number + of bound parameter sets. + + See also: + + :ref:`types_sql_value_processing` + + """ + return None + + @util.memoized_property + def _has_bind_expression(self): + """memoized boolean, check if bind_expression is implemented. + + Allows the method to be skipped for the vast majority of expression + types that don't use this feature. + + """ + + return self.__class__.bind_expression.__code__ \ + is not TypeEngine.bind_expression.__code__ + + @staticmethod + def _to_instance(cls_or_self): + return to_instance(cls_or_self) + + def compare_values(self, x, y): + """Compare two values for equality.""" + + return x == y + + def get_dbapi_type(self, dbapi): + """Return the corresponding type object from the underlying DB-API, if + any. + + This can be useful for calling ``setinputsizes()``, for example. + + """ + return None + + @property + def python_type(self): + """Return the Python type object expected to be returned + by instances of this type, if known. + + Basically, for those types which enforce a return type, + or are known across the board to do such for all common + DBAPIs (like ``int`` for example), will return that type. + + If a return type is not defined, raises + ``NotImplementedError``. + + Note that any type also accommodates NULL in SQL which + means you can also get back ``None`` from any type + in practice. + + """ + raise NotImplementedError() + + def with_variant(self, type_, dialect_name): + """Produce a new type object that will utilize the given + type when applied to the dialect of the given name. + + e.g.:: + + from sqlalchemy.types import String + from sqlalchemy.dialects import mysql + + s = String() + + s = s.with_variant(mysql.VARCHAR(collation='foo'), 'mysql') + + The construction of :meth:`.TypeEngine.with_variant` is always + from the "fallback" type to that which is dialect specific. + The returned type is an instance of :class:`.Variant`, which + itself provides a :meth:`.Variant.with_variant` + that can be called repeatedly. + + :param type_: a :class:`.TypeEngine` that will be selected + as a variant from the originating type, when a dialect + of the given name is in use. + :param dialect_name: base name of the dialect which uses + this type. (i.e. ``'postgresql'``, ``'mysql'``, etc.) + + .. versionadded:: 0.7.2 + + """ + return Variant(self, {dialect_name: to_instance(type_)}) + + @util.memoized_property + def _type_affinity(self): + """Return a rudimental 'affinity' value expressing the general class + of type.""" + + typ = None + for t in self.__class__.__mro__: + if t in (TypeEngine, UserDefinedType): + return typ + elif issubclass(t, (TypeEngine, UserDefinedType)): + typ = t + else: + return self.__class__ + + def dialect_impl(self, dialect): + """Return a dialect-specific implementation for this + :class:`.TypeEngine`. + + """ + try: + return dialect._type_memos[self]['impl'] + except KeyError: + return self._dialect_info(dialect)['impl'] + + def _unwrapped_dialect_impl(self, dialect): + """Return the 'unwrapped' dialect impl for this type. + + For a type that applies wrapping logic (e.g. TypeDecorator), give + us the real, actual dialect-level type that is used. + + This is used for class-based lookups by dialects. + + """ + return self.dialect_impl(dialect) + + def _cached_literal_processor(self, dialect): + """Return a dialect-specific literal processor for this type.""" + try: + return dialect._type_memos[self]['literal'] + except KeyError: + d = self._dialect_info(dialect) + d['literal'] = lp = d['impl'].literal_processor(dialect) + return lp + + def _cached_bind_processor(self, dialect): + """Return a dialect-specific bind processor for this type.""" + + try: + return dialect._type_memos[self]['bind'] + except KeyError: + d = self._dialect_info(dialect) + d['bind'] = bp = d['impl'].bind_processor(dialect) + return bp + + def _cached_result_processor(self, dialect, coltype): + """Return a dialect-specific result processor for this type.""" + + try: + return dialect._type_memos[self][coltype] + except KeyError: + d = self._dialect_info(dialect) + # key assumption: DBAPI type codes are + # constants. Else this dictionary would + # grow unbounded. + d[coltype] = rp = d['impl'].result_processor(dialect, coltype) + return rp + + def _cached_custom_processor(self, dialect, key, fn): + try: + return dialect._type_memos[self][key] + except KeyError: + d = self._dialect_info(dialect) + impl = d['impl'] + d[key] = result = fn(impl) + return result + + def _dialect_info(self, dialect): + """Return a dialect-specific registry which + caches a dialect-specific implementation, bind processing + function, and one or more result processing functions.""" + + if self in dialect._type_memos: + return dialect._type_memos[self] + else: + impl = self._gen_dialect_impl(dialect) + if impl is self: + impl = self.adapt(type(self)) + # this can't be self, else we create a cycle + assert impl is not self + dialect._type_memos[self] = d = {'impl': impl} + return d + + def _gen_dialect_impl(self, dialect): + return dialect.type_descriptor(self) + + def adapt(self, cls, **kw): + """Produce an "adapted" form of this type, given an "impl" class + to work with. + + This method is used internally to associate generic + types with "implementation" types that are specific to a particular + dialect. + """ + return util.constructor_copy(self, cls, **kw) + + def coerce_compared_value(self, op, value): + """Suggest a type for a 'coerced' Python value in an expression. + + Given an operator and value, gives the type a chance + to return a type which the value should be coerced into. + + The default behavior here is conservative; if the right-hand + side is already coerced into a SQL type based on its + Python type, it is usually left alone. + + End-user functionality extension here should generally be via + :class:`.TypeDecorator`, which provides more liberal behavior in that + it defaults to coercing the other side of the expression into this + type, thus applying special Python conversions above and beyond those + needed by the DBAPI to both ides. It also provides the public method + :meth:`.TypeDecorator.coerce_compared_value` which is intended for + end-user customization of this behavior. + + """ + _coerced_type = _resolve_value_to_type(value) + if _coerced_type is NULLTYPE or _coerced_type._type_affinity \ + is self._type_affinity: + return self + else: + return _coerced_type + + def _compare_type_affinity(self, other): + return self._type_affinity is other._type_affinity + + def compile(self, dialect=None): + """Produce a string-compiled form of this :class:`.TypeEngine`. + + When called with no arguments, uses a "default" dialect + to produce a string result. + + :param dialect: a :class:`.Dialect` instance. + + """ + # arg, return value is inconsistent with + # ClauseElement.compile()....this is a mistake. + + if not dialect: + dialect = self._default_dialect() + + return dialect.type_compiler.process(self) + + @util.dependencies("sqlalchemy.engine.default") + def _default_dialect(self, default): + if self.__class__.__module__.startswith("sqlalchemy.dialects"): + tokens = self.__class__.__module__.split(".")[0:3] + mod = ".".join(tokens) + return getattr(__import__(mod).dialects, tokens[-1]).dialect() + else: + return default.DefaultDialect() + + def __str__(self): + if util.py2k: + return unicode(self.compile()).\ + encode('ascii', 'backslashreplace') + else: + return str(self.compile()) + + def __repr__(self): + return util.generic_repr(self) + + +class VisitableCheckKWArg(util.EnsureKWArgType, VisitableType): + pass + + +class UserDefinedType(util.with_metaclass(VisitableCheckKWArg, TypeEngine)): + """Base for user defined types. + + This should be the base of new types. Note that + for most cases, :class:`.TypeDecorator` is probably + more appropriate:: + + import sqlalchemy.types as types + + class MyType(types.UserDefinedType): + def __init__(self, precision = 8): + self.precision = precision + + def get_col_spec(self, **kw): + return "MYTYPE(%s)" % self.precision + + def bind_processor(self, dialect): + def process(value): + return value + return process + + def result_processor(self, dialect, coltype): + def process(value): + return value + return process + + Once the type is made, it's immediately usable:: + + table = Table('foo', meta, + Column('id', Integer, primary_key=True), + Column('data', MyType(16)) + ) + + The ``get_col_spec()`` method will in most cases receive a keyword + argument ``type_expression`` which refers to the owning expression + of the type as being compiled, such as a :class:`.Column` or + :func:`.cast` construct. This keyword is only sent if the method + accepts keyword arguments (e.g. ``**kw``) in its argument signature; + introspection is used to check for this in order to support legacy + forms of this function. + + .. versionadded:: 1.0.0 the owning expression is passed to + the ``get_col_spec()`` method via the keyword argument + ``type_expression``, if it receives ``**kw`` in its signature. + + """ + __visit_name__ = "user_defined" + + ensure_kwarg = 'get_col_spec' + + class Comparator(TypeEngine.Comparator): + __slots__ = () + + def _adapt_expression(self, op, other_comparator): + if hasattr(self.type, 'adapt_operator'): + util.warn_deprecated( + "UserDefinedType.adapt_operator is deprecated. Create " + "a UserDefinedType.Comparator subclass instead which " + "generates the desired expression constructs, given a " + "particular operator." + ) + return self.type.adapt_operator(op), self.type + else: + return super( + UserDefinedType.Comparator, self + )._adapt_expression(op, other_comparator) + + comparator_factory = Comparator + + def coerce_compared_value(self, op, value): + """Suggest a type for a 'coerced' Python value in an expression. + + Default behavior for :class:`.UserDefinedType` is the + same as that of :class:`.TypeDecorator`; by default it returns + ``self``, assuming the compared value should be coerced into + the same type as this one. See + :meth:`.TypeDecorator.coerce_compared_value` for more detail. + + .. versionchanged:: 0.8 :meth:`.UserDefinedType.coerce_compared_value` + now returns ``self`` by default, rather than falling onto the + more fundamental behavior of + :meth:`.TypeEngine.coerce_compared_value`. + + """ + + return self + + +class Emulated(object): + """Mixin for base types that emulate the behavior of a DB-native type. + + An :class:`.Emulated` type will use an available database type + in conjunction with Python-side routines and/or database constraints + in order to approximate the behavior of a database type that is provided + natively by some backends. When a native-providing backend is in + use, the native version of the type is used. This native version + should include the :class:`.NativeForEmulated` mixin to allow it to be + distinguished from :class:`.Emulated`. + + Current examples of :class:`.Emulated` are: :class:`.Interval`, + :class:`.Enum`, :class:`.Boolean`. + + .. versionadded:: 1.2.0b3 + + """ + + def adapt_to_emulated(self, impltype, **kw): + """Given an impl class, adapt this type to the impl assuming "emulated". + + The impl should also be an "emulated" version of this type, + most likely the same class as this type itself. + + e.g.: sqltypes.Enum adapts to the Enum class. + + """ + return super(Emulated, self).adapt(impltype, **kw) + + def adapt(self, impltype, **kw): + if hasattr(impltype, "adapt_emulated_to_native"): + + if self.native: + # native support requested, dialect gave us a native + # implementor, pass control over to it + return impltype.adapt_emulated_to_native(self, **kw) + else: + # impltype adapts to native, and we are not native, + # so reject the impltype in favor of "us" + impltype = self.__class__ + + if issubclass(impltype, self.__class__): + return self.adapt_to_emulated(impltype, **kw) + else: + return super(Emulated, self).adapt(impltype, **kw) + + +class NativeForEmulated(object): + """Indicates DB-native types supported by an :class:`.Emulated` type. + + .. versionadded:: 1.2.0b3 + + """ + + @classmethod + def adapt_emulated_to_native(cls, impl, **kw): + """Given an impl, adapt this type's class to the impl assuming "native". + + The impl will be an :class:`.Emulated` class but not a + :class:`.NativeForEmulated`. + + e.g.: postgresql.ENUM produces a type given an Enum instance. + + """ + return cls(**kw) + + +class TypeDecorator(SchemaEventTarget, TypeEngine): + """Allows the creation of types which add additional functionality + to an existing type. + + This method is preferred to direct subclassing of SQLAlchemy's + built-in types as it ensures that all required functionality of + the underlying type is kept in place. + + Typical usage:: + + import sqlalchemy.types as types + + class MyType(types.TypeDecorator): + '''Prefixes Unicode values with "PREFIX:" on the way in and + strips it off on the way out. + ''' + + impl = types.Unicode + + def process_bind_param(self, value, dialect): + return "PREFIX:" + value + + def process_result_value(self, value, dialect): + return value[7:] + + def copy(self, **kw): + return MyType(self.impl.length) + + The class-level "impl" attribute is required, and can reference any + TypeEngine class. Alternatively, the load_dialect_impl() method + can be used to provide different type classes based on the dialect + given; in this case, the "impl" variable can reference + ``TypeEngine`` as a placeholder. + + Types that receive a Python type that isn't similar to the ultimate type + used may want to define the :meth:`TypeDecorator.coerce_compared_value` + method. This is used to give the expression system a hint when coercing + Python objects into bind parameters within expressions. Consider this + expression:: + + mytable.c.somecol + datetime.date(2009, 5, 15) + + Above, if "somecol" is an ``Integer`` variant, it makes sense that + we're doing date arithmetic, where above is usually interpreted + by databases as adding a number of days to the given date. + The expression system does the right thing by not attempting to + coerce the "date()" value into an integer-oriented bind parameter. + + However, in the case of ``TypeDecorator``, we are usually changing an + incoming Python type to something new - ``TypeDecorator`` by default will + "coerce" the non-typed side to be the same type as itself. Such as below, + we define an "epoch" type that stores a date value as an integer:: + + class MyEpochType(types.TypeDecorator): + impl = types.Integer + + epoch = datetime.date(1970, 1, 1) + + def process_bind_param(self, value, dialect): + return (value - self.epoch).days + + def process_result_value(self, value, dialect): + return self.epoch + timedelta(days=value) + + Our expression of ``somecol + date`` with the above type will coerce the + "date" on the right side to also be treated as ``MyEpochType``. + + This behavior can be overridden via the + :meth:`~TypeDecorator.coerce_compared_value` method, which returns a type + that should be used for the value of the expression. Below we set it such + that an integer value will be treated as an ``Integer``, and any other + value is assumed to be a date and will be treated as a ``MyEpochType``:: + + def coerce_compared_value(self, op, value): + if isinstance(value, int): + return Integer() + else: + return self + + .. warning:: + + Note that the **behavior of coerce_compared_value is not inherited + by default from that of the base type**. + If the :class:`.TypeDecorator` is augmenting a + type that requires special logic for certain types of operators, + this method **must** be overridden. A key example is when decorating + the :class:`.postgresql.JSON` and :class:`.postgresql.JSONB` types; + the default rules of :meth:`.TypeEngine.coerce_compared_value` should + be used in order to deal with operators like index operations:: + + class MyJsonType(TypeDecorator): + impl = postgresql.JSON + + def coerce_compared_value(self, op, value): + return self.impl.coerce_compared_value(op, value) + + Without the above step, index operations such as ``mycol['foo']`` + will cause the index value ``'foo'`` to be JSON encoded. + + """ + __visit_name__ = "type_decorator" + + def __init__(self, *args, **kwargs): + """Construct a :class:`.TypeDecorator`. + + Arguments sent here are passed to the constructor + of the class assigned to the ``impl`` class level attribute, + assuming the ``impl`` is a callable, and the resulting + object is assigned to the ``self.impl`` instance attribute + (thus overriding the class attribute of the same name). + + If the class level ``impl`` is not a callable (the unusual case), + it will be assigned to the same instance attribute 'as-is', + ignoring those arguments passed to the constructor. + + Subclasses can override this to customize the generation + of ``self.impl`` entirely. + + """ + + if not hasattr(self.__class__, 'impl'): + raise AssertionError("TypeDecorator implementations " + "require a class-level variable " + "'impl' which refers to the class of " + "type being decorated") + self.impl = to_instance(self.__class__.impl, *args, **kwargs) + + coerce_to_is_types = (util.NoneType, ) + """Specify those Python types which should be coerced at the expression + level to "IS <constant>" when compared using ``==`` (and same for + ``IS NOT`` in conjunction with ``!=``. + + For most SQLAlchemy types, this includes ``NoneType``, as well as + ``bool``. + + :class:`.TypeDecorator` modifies this list to only include ``NoneType``, + as typedecorator implementations that deal with boolean types are common. + + Custom :class:`.TypeDecorator` classes can override this attribute to + return an empty tuple, in which case no values will be coerced to + constants. + + .. versionadded:: 0.8.2 + Added :attr:`.TypeDecorator.coerce_to_is_types` to allow for easier + control of ``__eq__()`` ``__ne__()`` operations. + + """ + + class Comparator(TypeEngine.Comparator): + __slots__ = () + + def operate(self, op, *other, **kwargs): + kwargs['_python_is_types'] = self.expr.type.coerce_to_is_types + return super(TypeDecorator.Comparator, self).operate( + op, *other, **kwargs) + + def reverse_operate(self, op, other, **kwargs): + kwargs['_python_is_types'] = self.expr.type.coerce_to_is_types + return super(TypeDecorator.Comparator, self).reverse_operate( + op, other, **kwargs) + + @property + def comparator_factory(self): + if TypeDecorator.Comparator in self.impl.comparator_factory.__mro__: + return self.impl.comparator_factory + else: + return type("TDComparator", + (TypeDecorator.Comparator, + self.impl.comparator_factory), + {}) + + def _gen_dialect_impl(self, dialect): + """ + #todo + """ + adapted = dialect.type_descriptor(self) + if adapted is not self: + return adapted + + # otherwise adapt the impl type, link + # to a copy of this TypeDecorator and return + # that. + typedesc = self._unwrapped_dialect_impl(dialect) + tt = self.copy() + if not isinstance(tt, self.__class__): + raise AssertionError('Type object %s does not properly ' + 'implement the copy() method, it must ' + 'return an object of type %s' % + (self, self.__class__)) + tt.impl = typedesc + return tt + + @property + def _type_affinity(self): + """ + #todo + """ + return self.impl._type_affinity + + def _set_parent(self, column): + """Support SchemaEventTarget""" + + super(TypeDecorator, self)._set_parent(column) + + if isinstance(self.impl, SchemaEventTarget): + self.impl._set_parent(column) + + def _set_parent_with_dispatch(self, parent): + """Support SchemaEventTarget""" + + super(TypeDecorator, self)._set_parent_with_dispatch(parent) + + if isinstance(self.impl, SchemaEventTarget): + self.impl._set_parent_with_dispatch(parent) + + def type_engine(self, dialect): + """Return a dialect-specific :class:`.TypeEngine` instance + for this :class:`.TypeDecorator`. + + In most cases this returns a dialect-adapted form of + the :class:`.TypeEngine` type represented by ``self.impl``. + Makes usage of :meth:`dialect_impl` but also traverses + into wrapped :class:`.TypeDecorator` instances. + Behavior can be customized here by overriding + :meth:`load_dialect_impl`. + + """ + adapted = dialect.type_descriptor(self) + if not isinstance(adapted, type(self)): + return adapted + elif isinstance(self.impl, TypeDecorator): + return self.impl.type_engine(dialect) + else: + return self.load_dialect_impl(dialect) + + def load_dialect_impl(self, dialect): + """Return a :class:`.TypeEngine` object corresponding to a dialect. + + This is an end-user override hook that can be used to provide + differing types depending on the given dialect. It is used + by the :class:`.TypeDecorator` implementation of :meth:`type_engine` + to help determine what type should ultimately be returned + for a given :class:`.TypeDecorator`. + + By default returns ``self.impl``. + + """ + return self.impl + + def _unwrapped_dialect_impl(self, dialect): + """Return the 'unwrapped' dialect impl for this type. + + For a type that applies wrapping logic (e.g. TypeDecorator), give + us the real, actual dialect-level type that is used. + + This is used for class-based lookups by dialects. + + """ + return self.load_dialect_impl(dialect).dialect_impl(dialect) + + def __getattr__(self, key): + """Proxy all other undefined accessors to the underlying + implementation.""" + return getattr(self.impl, key) + + def process_literal_param(self, value, dialect): + """Receive a literal parameter value to be rendered inline within + a statement. + + This method is used when the compiler renders a + literal value without using binds, typically within DDL + such as in the "server default" of a column or an expression + within a CHECK constraint. + + The returned string will be rendered into the output string. + + .. versionadded:: 0.9.0 + + """ + raise NotImplementedError() + + def process_bind_param(self, value, dialect): + """Receive a bound parameter value to be converted. + + Subclasses override this method to return the + value that should be passed along to the underlying + :class:`.TypeEngine` object, and from there to the + DBAPI ``execute()`` method. + + The operation could be anything desired to perform custom + behavior, such as transforming or serializing data. + This could also be used as a hook for validating logic. + + This operation should be designed with the reverse operation + in mind, which would be the process_result_value method of + this class. + + :param value: Data to operate upon, of any type expected by + this method in the subclass. Can be ``None``. + :param dialect: the :class:`.Dialect` in use. + + """ + + raise NotImplementedError() + + def process_result_value(self, value, dialect): + """Receive a result-row column value to be converted. + + Subclasses should implement this method to operate on data + fetched from the database. + + Subclasses override this method to return the + value that should be passed back to the application, + given a value that is already processed by + the underlying :class:`.TypeEngine` object, originally + from the DBAPI cursor method ``fetchone()`` or similar. + + The operation could be anything desired to perform custom + behavior, such as transforming or serializing data. + This could also be used as a hook for validating logic. + + :param value: Data to operate upon, of any type expected by + this method in the subclass. Can be ``None``. + :param dialect: the :class:`.Dialect` in use. + + This operation should be designed to be reversible by + the "process_bind_param" method of this class. + + """ + + raise NotImplementedError() + + @util.memoized_property + def _has_bind_processor(self): + """memoized boolean, check if process_bind_param is implemented. + + Allows the base process_bind_param to raise + NotImplementedError without needing to test an expensive + exception throw. + + """ + + return self.__class__.process_bind_param.__code__ \ + is not TypeDecorator.process_bind_param.__code__ + + @util.memoized_property + def _has_literal_processor(self): + """memoized boolean, check if process_literal_param is implemented. + + + """ + + return self.__class__.process_literal_param.__code__ \ + is not TypeDecorator.process_literal_param.__code__ + + def literal_processor(self, dialect): + """Provide a literal processing function for the given + :class:`.Dialect`. + + Subclasses here will typically override + :meth:`.TypeDecorator.process_literal_param` instead of this method + directly. + + By default, this method makes use of + :meth:`.TypeDecorator.process_bind_param` if that method is + implemented, where :meth:`.TypeDecorator.process_literal_param` is + not. The rationale here is that :class:`.TypeDecorator` typically + deals with Python conversions of data that are above the layer of + database presentation. With the value converted by + :meth:`.TypeDecorator.process_bind_param`, the underlying type will + then handle whether it needs to be presented to the DBAPI as a bound + parameter or to the database as an inline SQL value. + + .. versionadded:: 0.9.0 + + """ + if self._has_literal_processor: + process_param = self.process_literal_param + elif self._has_bind_processor: + # the bind processor should normally be OK + # for TypeDecorator since it isn't doing DB-level + # handling, the handling here won't be different for bound vs. + # literals. + process_param = self.process_bind_param + else: + process_param = None + + if process_param: + impl_processor = self.impl.literal_processor(dialect) + if impl_processor: + def process(value): + return impl_processor(process_param(value, dialect)) + else: + def process(value): + return process_param(value, dialect) + + return process + else: + return self.impl.literal_processor(dialect) + + def bind_processor(self, dialect): + """Provide a bound value processing function for the + given :class:`.Dialect`. + + This is the method that fulfills the :class:`.TypeEngine` + contract for bound value conversion. :class:`.TypeDecorator` + will wrap a user-defined implementation of + :meth:`process_bind_param` here. + + User-defined code can override this method directly, + though its likely best to use :meth:`process_bind_param` so that + the processing provided by ``self.impl`` is maintained. + + :param dialect: Dialect instance in use. + + This method is the reverse counterpart to the + :meth:`result_processor` method of this class. + + """ + if self._has_bind_processor: + process_param = self.process_bind_param + impl_processor = self.impl.bind_processor(dialect) + if impl_processor: + def process(value): + return impl_processor(process_param(value, dialect)) + + else: + def process(value): + return process_param(value, dialect) + + return process + else: + return self.impl.bind_processor(dialect) + + @util.memoized_property + def _has_result_processor(self): + """memoized boolean, check if process_result_value is implemented. + + Allows the base process_result_value to raise + NotImplementedError without needing to test an expensive + exception throw. + + """ + return self.__class__.process_result_value.__code__ \ + is not TypeDecorator.process_result_value.__code__ + + def result_processor(self, dialect, coltype): + """Provide a result value processing function for the given + :class:`.Dialect`. + + This is the method that fulfills the :class:`.TypeEngine` + contract for result value conversion. :class:`.TypeDecorator` + will wrap a user-defined implementation of + :meth:`process_result_value` here. + + User-defined code can override this method directly, + though its likely best to use :meth:`process_result_value` so that + the processing provided by ``self.impl`` is maintained. + + :param dialect: Dialect instance in use. + :param coltype: A SQLAlchemy data type + + This method is the reverse counterpart to the + :meth:`bind_processor` method of this class. + + """ + if self._has_result_processor: + process_value = self.process_result_value + impl_processor = self.impl.result_processor(dialect, + coltype) + if impl_processor: + def process(value): + return process_value(impl_processor(value), dialect) + + else: + def process(value): + return process_value(value, dialect) + + return process + else: + return self.impl.result_processor(dialect, coltype) + + def coerce_compared_value(self, op, value): + """Suggest a type for a 'coerced' Python value in an expression. + + By default, returns self. This method is called by + the expression system when an object using this type is + on the left or right side of an expression against a plain Python + object which does not yet have a SQLAlchemy type assigned:: + + expr = table.c.somecolumn + 35 + + Where above, if ``somecolumn`` uses this type, this method will + be called with the value ``operator.add`` + and ``35``. The return value is whatever SQLAlchemy type should + be used for ``35`` for this particular operation. + + """ + return self + + def copy(self, **kw): + """Produce a copy of this :class:`.TypeDecorator` instance. + + This is a shallow copy and is provided to fulfill part of + the :class:`.TypeEngine` contract. It usually does not + need to be overridden unless the user-defined :class:`.TypeDecorator` + has local state that should be deep-copied. + + """ + + instance = self.__class__.__new__(self.__class__) + instance.__dict__.update(self.__dict__) + return instance + + def get_dbapi_type(self, dbapi): + """Return the DBAPI type object represented by this + :class:`.TypeDecorator`. + + By default this calls upon :meth:`.TypeEngine.get_dbapi_type` of the + underlying "impl". + """ + return self.impl.get_dbapi_type(dbapi) + + def compare_values(self, x, y): + """Given two values, compare them for equality. + + By default this calls upon :meth:`.TypeEngine.compare_values` + of the underlying "impl", which in turn usually + uses the Python equals operator ``==``. + + This function is used by the ORM to compare + an original-loaded value with an intercepted + "changed" value, to determine if a net change + has occurred. + + """ + return self.impl.compare_values(x, y) + + def __repr__(self): + return util.generic_repr(self, to_inspect=self.impl) + + + + +class Variant(TypeDecorator): + """A wrapping type that selects among a variety of + implementations based on dialect in use. + + The :class:`.Variant` type is typically constructed + using the :meth:`.TypeEngine.with_variant` method. + + .. versionadded:: 0.7.2 + + .. seealso:: :meth:`.TypeEngine.with_variant` for an example of use. + + """ + + def __init__(self, base, mapping): + """Construct a new :class:`.Variant`. + + :param base: the base 'fallback' type + :param mapping: dictionary of string dialect names to + :class:`.TypeEngine` instances. + + """ + self.impl = base + self.mapping = mapping + + def coerce_compared_value(self, operator, value): + result = self.impl.coerce_compared_value(operator, value) + if result is self.impl: + return self + else: + return result + + def load_dialect_impl(self, dialect): + if dialect.name in self.mapping: + return self.mapping[dialect.name] + else: + return self.impl + + def _set_parent(self, column): + """Support SchemaEventTarget""" + + if isinstance(self.impl, SchemaEventTarget): + self.impl._set_parent(column) + for impl in self.mapping.values(): + if isinstance(impl, SchemaEventTarget): + impl._set_parent(column) + + def _set_parent_with_dispatch(self, parent): + """Support SchemaEventTarget""" + + if isinstance(self.impl, SchemaEventTarget): + self.impl._set_parent_with_dispatch(parent) + for impl in self.mapping.values(): + if isinstance(impl, SchemaEventTarget): + impl._set_parent_with_dispatch(parent) + + def with_variant(self, type_, dialect_name): + """Return a new :class:`.Variant` which adds the given + type + dialect name to the mapping, in addition to the + mapping present in this :class:`.Variant`. + + :param type_: a :class:`.TypeEngine` that will be selected + as a variant from the originating type, when a dialect + of the given name is in use. + :param dialect_name: base name of the dialect which uses + this type. (i.e. ``'postgresql'``, ``'mysql'``, etc.) + + """ + + if dialect_name in self.mapping: + raise exc.ArgumentError( + "Dialect '%s' is already present in " + "the mapping for this Variant" % dialect_name) + mapping = self.mapping.copy() + mapping[dialect_name] = type_ + return Variant(self.impl, mapping) + + @property + def comparator_factory(self): + """express comparison behavior in terms of the base type""" + return self.impl.comparator_factory + + +def _reconstitute_comparator(expression): + return expression.comparator + + +def to_instance(typeobj, *arg, **kw): + if typeobj is None: + return NULLTYPE + + if util.callable(typeobj): + return typeobj(*arg, **kw) + else: + return typeobj + + +def adapt_type(typeobj, colspecs): + if isinstance(typeobj, type): + typeobj = typeobj() + for t in typeobj.__class__.__mro__[0:-1]: + try: + impltype = colspecs[t] + break + except KeyError: + pass + else: + # couldn't adapt - so just return the type itself + # (it may be a user-defined type) + return typeobj + # if we adapted the given generic type to a database-specific type, + # but it turns out the originally given "generic" type + # is actually a subclass of our resulting type, then we were already + # given a more specific type than that required; so use that. + if (issubclass(typeobj.__class__, impltype)): + return typeobj + return typeobj.adapt(impltype) diff --git a/venv/Lib/site-packages/sqlalchemy/sql/util.py b/venv/Lib/site-packages/sqlalchemy/sql/util.py new file mode 100644 index 0000000..f15cc95 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/util.py @@ -0,0 +1,766 @@ +# sql/util.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""High level utilities which build upon other modules here. + +""" + +from .. import exc, util +from .base import _from_objects, ColumnSet +from . import operators, visitors +from itertools import chain +from collections import deque + +from .elements import BindParameter, ColumnClause, ColumnElement, \ + Null, UnaryExpression, literal_column, Label, _label_reference, \ + _textual_label_reference +from .selectable import SelectBase, ScalarSelect, Join, FromClause, FromGrouping +from .schema import Column + +join_condition = util.langhelpers.public_factory( + Join._join_condition, + ".sql.util.join_condition") + +# names that are still being imported from the outside +from .annotation import _shallow_annotate, _deep_annotate, _deep_deannotate +from .elements import _find_columns +from .ddl import sort_tables + + +def find_join_source(clauses, join_to): + """Given a list of FROM clauses and a selectable, + return the first index and element from the list of + clauses which can be joined against the selectable. returns + None, None if no match is found. + + e.g.:: + + clause1 = table1.join(table2) + clause2 = table4.join(table5) + + join_to = table2.join(table3) + + find_join_source([clause1, clause2], join_to) == clause1 + + """ + + selectables = list(_from_objects(join_to)) + for i, f in enumerate(clauses): + for s in selectables: + if f.is_derived_from(s): + return i, f + else: + return None, None + + +def visit_binary_product(fn, expr): + """Produce a traversal of the given expression, delivering + column comparisons to the given function. + + The function is of the form:: + + def my_fn(binary, left, right) + + For each binary expression located which has a + comparison operator, the product of "left" and + "right" will be delivered to that function, + in terms of that binary. + + Hence an expression like:: + + and_( + (a + b) == q + func.sum(e + f), + j == r + ) + + would have the traversal:: + + a <eq> q + a <eq> e + a <eq> f + b <eq> q + b <eq> e + b <eq> f + j <eq> r + + That is, every combination of "left" and + "right" that doesn't further contain + a binary comparison is passed as pairs. + + """ + stack = [] + + def visit(element): + if isinstance(element, ScalarSelect): + # we don't want to dig into correlated subqueries, + # those are just column elements by themselves + yield element + elif element.__visit_name__ == 'binary' and \ + operators.is_comparison(element.operator): + stack.insert(0, element) + for l in visit(element.left): + for r in visit(element.right): + fn(stack[0], l, r) + stack.pop(0) + for elem in element.get_children(): + visit(elem) + else: + if isinstance(element, ColumnClause): + yield element + for elem in element.get_children(): + for e in visit(elem): + yield e + list(visit(expr)) + + +def find_tables(clause, check_columns=False, + include_aliases=False, include_joins=False, + include_selects=False, include_crud=False): + """locate Table objects within the given expression.""" + + tables = [] + _visitors = {} + + if include_selects: + _visitors['select'] = _visitors['compound_select'] = tables.append + + if include_joins: + _visitors['join'] = tables.append + + if include_aliases: + _visitors['alias'] = tables.append + + if include_crud: + _visitors['insert'] = _visitors['update'] = \ + _visitors['delete'] = lambda ent: tables.append(ent.table) + + if check_columns: + def visit_column(column): + tables.append(column.table) + _visitors['column'] = visit_column + + _visitors['table'] = tables.append + + visitors.traverse(clause, {'column_collections': False}, _visitors) + return tables + + +def unwrap_order_by(clause): + """Break up an 'order by' expression into individual column-expressions, + without DESC/ASC/NULLS FIRST/NULLS LAST""" + + cols = util.column_set() + result = [] + stack = deque([clause]) + while stack: + t = stack.popleft() + if isinstance(t, ColumnElement) and \ + ( + not isinstance(t, UnaryExpression) or + not operators.is_ordering_modifier(t.modifier) + ): + if isinstance(t, _label_reference): + t = t.element + if isinstance(t, (_textual_label_reference)): + continue + if t not in cols: + cols.add(t) + result.append(t) + else: + for c in t.get_children(): + stack.append(c) + return result + + +def unwrap_label_reference(element): + def replace(elem): + if isinstance(elem, (_label_reference, _textual_label_reference)): + return elem.element + + return visitors.replacement_traverse( + element, {}, replace + ) + + +def expand_column_list_from_order_by(collist, order_by): + """Given the columns clause and ORDER BY of a selectable, + return a list of column expressions that can be added to the collist + corresponding to the ORDER BY, without repeating those already + in the collist. + + """ + cols_already_present = set([ + col.element if col._order_by_label_element is not None + else col for col in collist + ]) + + return [ + col for col in + chain(*[ + unwrap_order_by(o) + for o in order_by + ]) + if col not in cols_already_present + ] + + +def clause_is_present(clause, search): + """Given a target clause and a second to search within, return True + if the target is plainly present in the search without any + subqueries or aliases involved. + + Basically descends through Joins. + + """ + + for elem in surface_selectables(search): + if clause == elem: # use == here so that Annotated's compare + return True + else: + return False + + +def surface_selectables(clause): + stack = [clause] + while stack: + elem = stack.pop() + yield elem + if isinstance(elem, Join): + stack.extend((elem.left, elem.right)) + elif isinstance(elem, FromGrouping): + stack.append(elem.element) + + +def surface_column_elements(clause, include_scalar_selects=True): + """traverse and yield only outer-exposed column elements, such as would + be addressable in the WHERE clause of a SELECT if this element were + in the columns clause.""" + + filter_ = (FromGrouping, ) + if not include_scalar_selects: + filter_ += (SelectBase, ) + + stack = deque([clause]) + while stack: + elem = stack.popleft() + yield elem + for sub in elem.get_children(): + if isinstance(sub, filter_): + continue + stack.append(sub) + + +def selectables_overlap(left, right): + """Return True if left/right have some overlapping selectable""" + + return bool( + set(surface_selectables(left)).intersection( + surface_selectables(right) + ) + ) + + +def bind_values(clause): + """Return an ordered list of "bound" values in the given clause. + + E.g.:: + + >>> expr = and_( + ... table.c.foo==5, table.c.foo==7 + ... ) + >>> bind_values(expr) + [5, 7] + """ + + v = [] + + def visit_bindparam(bind): + v.append(bind.effective_value) + + visitors.traverse(clause, {}, {'bindparam': visit_bindparam}) + return v + + +def _quote_ddl_expr(element): + if isinstance(element, util.string_types): + element = element.replace("'", "''") + return "'%s'" % element + else: + return repr(element) + + +class _repr_base(object): + _LIST = 0 + _TUPLE = 1 + _DICT = 2 + + __slots__ = 'max_chars', + + def trunc(self, value): + rep = repr(value) + lenrep = len(rep) + if lenrep > self.max_chars: + segment_length = self.max_chars // 2 + rep = ( + rep[0:segment_length] + + (" ... (%d characters truncated) ... " + % (lenrep - self.max_chars)) + + rep[-segment_length:] + ) + return rep + + +class _repr_row(_repr_base): + """Provide a string view of a row.""" + + __slots__ = 'row', + + def __init__(self, row, max_chars=300): + self.row = row + self.max_chars = max_chars + + def __repr__(self): + trunc = self.trunc + return "(%s%s)" % ( + ", ".join(trunc(value) for value in self.row), + "," if len(self.row) == 1 else "" + ) + + +class _repr_params(_repr_base): + """Provide a string view of bound parameters. + + Truncates display to a given numnber of 'multi' parameter sets, + as well as long values to a given number of characters. + + """ + + __slots__ = 'params', 'batches', + + def __init__(self, params, batches, max_chars=300): + self.params = params + self.batches = batches + self.max_chars = max_chars + + def __repr__(self): + if isinstance(self.params, list): + typ = self._LIST + ismulti = self.params and isinstance( + self.params[0], (list, dict, tuple)) + elif isinstance(self.params, tuple): + typ = self._TUPLE + ismulti = self.params and isinstance( + self.params[0], (list, dict, tuple)) + elif isinstance(self.params, dict): + typ = self._DICT + ismulti = False + else: + return self.trunc(self.params) + + if ismulti and len(self.params) > self.batches: + msg = " ... displaying %i of %i total bound parameter sets ... " + return ' '.join(( + self._repr_multi(self.params[:self.batches - 2], typ)[0:-1], + msg % (self.batches, len(self.params)), + self._repr_multi(self.params[-2:], typ)[1:] + )) + elif ismulti: + return self._repr_multi(self.params, typ) + else: + return self._repr_params(self.params, typ) + + def _repr_multi(self, multi_params, typ): + if multi_params: + if isinstance(multi_params[0], list): + elem_type = self._LIST + elif isinstance(multi_params[0], tuple): + elem_type = self._TUPLE + elif isinstance(multi_params[0], dict): + elem_type = self._DICT + else: + assert False, \ + "Unknown parameter type %s" % (type(multi_params[0])) + + elements = ", ".join( + self._repr_params(params, elem_type) + for params in multi_params) + else: + elements = "" + + if typ == self._LIST: + return "[%s]" % elements + else: + return "(%s)" % elements + + def _repr_params(self, params, typ): + trunc = self.trunc + if typ is self._DICT: + return "{%s}" % ( + ", ".join( + "%r: %s" % (key, trunc(value)) + for key, value in params.items() + ) + ) + elif typ is self._TUPLE: + return "(%s%s)" % ( + ", ".join(trunc(value) for value in params), + "," if len(params) == 1 else "" + + ) + else: + return "[%s]" % ( + ", ".join(trunc(value) for value in params) + ) + + +def adapt_criterion_to_null(crit, nulls): + """given criterion containing bind params, convert selected elements + to IS NULL. + + """ + + def visit_binary(binary): + if isinstance(binary.left, BindParameter) \ + and binary.left._identifying_key in nulls: + # reverse order if the NULL is on the left side + binary.left = binary.right + binary.right = Null() + binary.operator = operators.is_ + binary.negate = operators.isnot + elif isinstance(binary.right, BindParameter) \ + and binary.right._identifying_key in nulls: + binary.right = Null() + binary.operator = operators.is_ + binary.negate = operators.isnot + + return visitors.cloned_traverse(crit, {}, {'binary': visit_binary}) + + +def splice_joins(left, right, stop_on=None): + if left is None: + return right + + stack = [(right, None)] + + adapter = ClauseAdapter(left) + ret = None + while stack: + (right, prevright) = stack.pop() + if isinstance(right, Join) and right is not stop_on: + right = right._clone() + right._reset_exported() + right.onclause = adapter.traverse(right.onclause) + stack.append((right.left, right)) + else: + right = adapter.traverse(right) + if prevright is not None: + prevright.left = right + if ret is None: + ret = right + + return ret + + +def reduce_columns(columns, *clauses, **kw): + r"""given a list of columns, return a 'reduced' set based on natural + equivalents. + + the set is reduced to the smallest list of columns which have no natural + equivalent present in the list. A "natural equivalent" means that two + columns will ultimately represent the same value because they are related + by a foreign key. + + \*clauses is an optional list of join clauses which will be traversed + to further identify columns that are "equivalent". + + \**kw may specify 'ignore_nonexistent_tables' to ignore foreign keys + whose tables are not yet configured, or columns that aren't yet present. + + This function is primarily used to determine the most minimal "primary + key" from a selectable, by reducing the set of primary key columns present + in the selectable to just those that are not repeated. + + """ + ignore_nonexistent_tables = kw.pop('ignore_nonexistent_tables', False) + only_synonyms = kw.pop('only_synonyms', False) + + columns = util.ordered_column_set(columns) + + omit = util.column_set() + for col in columns: + for fk in chain(*[c.foreign_keys for c in col.proxy_set]): + for c in columns: + if c is col: + continue + try: + fk_col = fk.column + except exc.NoReferencedColumnError: + # TODO: add specific coverage here + # to test/sql/test_selectable ReduceTest + if ignore_nonexistent_tables: + continue + else: + raise + except exc.NoReferencedTableError: + # TODO: add specific coverage here + # to test/sql/test_selectable ReduceTest + if ignore_nonexistent_tables: + continue + else: + raise + if fk_col.shares_lineage(c) and \ + (not only_synonyms or + c.name == col.name): + omit.add(col) + break + + if clauses: + def visit_binary(binary): + if binary.operator == operators.eq: + cols = util.column_set( + chain(*[c.proxy_set for c in columns.difference(omit)])) + if binary.left in cols and binary.right in cols: + for c in reversed(columns): + if c.shares_lineage(binary.right) and \ + (not only_synonyms or + c.name == binary.left.name): + omit.add(c) + break + for clause in clauses: + if clause is not None: + visitors.traverse(clause, {}, {'binary': visit_binary}) + + return ColumnSet(columns.difference(omit)) + + +def criterion_as_pairs(expression, consider_as_foreign_keys=None, + consider_as_referenced_keys=None, any_operator=False): + """traverse an expression and locate binary criterion pairs.""" + + if consider_as_foreign_keys and consider_as_referenced_keys: + raise exc.ArgumentError("Can only specify one of " + "'consider_as_foreign_keys' or " + "'consider_as_referenced_keys'") + + def col_is(a, b): + # return a is b + return a.compare(b) + + def visit_binary(binary): + if not any_operator and binary.operator is not operators.eq: + return + if not isinstance(binary.left, ColumnElement) or \ + not isinstance(binary.right, ColumnElement): + return + + if consider_as_foreign_keys: + if binary.left in consider_as_foreign_keys and \ + (col_is(binary.right, binary.left) or + binary.right not in consider_as_foreign_keys): + pairs.append((binary.right, binary.left)) + elif binary.right in consider_as_foreign_keys and \ + (col_is(binary.left, binary.right) or + binary.left not in consider_as_foreign_keys): + pairs.append((binary.left, binary.right)) + elif consider_as_referenced_keys: + if binary.left in consider_as_referenced_keys and \ + (col_is(binary.right, binary.left) or + binary.right not in consider_as_referenced_keys): + pairs.append((binary.left, binary.right)) + elif binary.right in consider_as_referenced_keys and \ + (col_is(binary.left, binary.right) or + binary.left not in consider_as_referenced_keys): + pairs.append((binary.right, binary.left)) + else: + if isinstance(binary.left, Column) and \ + isinstance(binary.right, Column): + if binary.left.references(binary.right): + pairs.append((binary.right, binary.left)) + elif binary.right.references(binary.left): + pairs.append((binary.left, binary.right)) + pairs = [] + visitors.traverse(expression, {}, {'binary': visit_binary}) + return pairs + + +class ClauseAdapter(visitors.ReplacingCloningVisitor): + """Clones and modifies clauses based on column correspondence. + + E.g.:: + + table1 = Table('sometable', metadata, + Column('col1', Integer), + Column('col2', Integer) + ) + table2 = Table('someothertable', metadata, + Column('col1', Integer), + Column('col2', Integer) + ) + + condition = table1.c.col1 == table2.c.col1 + + make an alias of table1:: + + s = table1.alias('foo') + + calling ``ClauseAdapter(s).traverse(condition)`` converts + condition to read:: + + s.c.col1 == table2.c.col1 + + """ + + def __init__(self, selectable, equivalents=None, + include_fn=None, exclude_fn=None, + adapt_on_names=False, anonymize_labels=False): + self.__traverse_options__ = { + 'stop_on': [selectable], + 'anonymize_labels': anonymize_labels} + self.selectable = selectable + self.include_fn = include_fn + self.exclude_fn = exclude_fn + self.equivalents = util.column_dict(equivalents or {}) + self.adapt_on_names = adapt_on_names + + def _corresponding_column(self, col, require_embedded, + _seen=util.EMPTY_SET): + newcol = self.selectable.corresponding_column( + col, + require_embedded=require_embedded) + if newcol is None and col in self.equivalents and col not in _seen: + for equiv in self.equivalents[col]: + newcol = self._corresponding_column( + equiv, require_embedded=require_embedded, + _seen=_seen.union([col])) + if newcol is not None: + return newcol + if self.adapt_on_names and newcol is None: + newcol = self.selectable.c.get(col.name) + return newcol + + def replace(self, col): + if isinstance(col, FromClause) and \ + self.selectable.is_derived_from(col): + return self.selectable + elif not isinstance(col, ColumnElement): + return None + elif self.include_fn and not self.include_fn(col): + return None + elif self.exclude_fn and self.exclude_fn(col): + return None + else: + return self._corresponding_column(col, True) + + +class ColumnAdapter(ClauseAdapter): + """Extends ClauseAdapter with extra utility functions. + + Key aspects of ColumnAdapter include: + + * Expressions that are adapted are stored in a persistent + .columns collection; so that an expression E adapted into + an expression E1, will return the same object E1 when adapted + a second time. This is important in particular for things like + Label objects that are anonymized, so that the ColumnAdapter can + be used to present a consistent "adapted" view of things. + + * Exclusion of items from the persistent collection based on + include/exclude rules, but also independent of hash identity. + This because "annotated" items all have the same hash identity as their + parent. + + * "wrapping" capability is added, so that the replacement of an expression + E can proceed through a series of adapters. This differs from the + visitor's "chaining" feature in that the resulting object is passed + through all replacing functions unconditionally, rather than stopping + at the first one that returns non-None. + + * An adapt_required option, used by eager loading to indicate that + We don't trust a result row column that is not translated. + This is to prevent a column from being interpreted as that + of the child row in a self-referential scenario, see + inheritance/test_basic.py->EagerTargetingTest.test_adapt_stringency + + """ + + def __init__(self, selectable, equivalents=None, + chain_to=None, adapt_required=False, + include_fn=None, exclude_fn=None, + adapt_on_names=False, + allow_label_resolve=True, + anonymize_labels=False): + ClauseAdapter.__init__(self, selectable, equivalents, + include_fn=include_fn, exclude_fn=exclude_fn, + adapt_on_names=adapt_on_names, + anonymize_labels=anonymize_labels) + + if chain_to: + self.chain(chain_to) + self.columns = util.populate_column_dict(self._locate_col) + if self.include_fn or self.exclude_fn: + self.columns = self._IncludeExcludeMapping(self, self.columns) + self.adapt_required = adapt_required + self.allow_label_resolve = allow_label_resolve + self._wrap = None + + class _IncludeExcludeMapping(object): + def __init__(self, parent, columns): + self.parent = parent + self.columns = columns + + def __getitem__(self, key): + if ( + self.parent.include_fn and not self.parent.include_fn(key) + ) or ( + self.parent.exclude_fn and self.parent.exclude_fn(key) + ): + if self.parent._wrap: + return self.parent._wrap.columns[key] + else: + return key + return self.columns[key] + + def wrap(self, adapter): + ac = self.__class__.__new__(self.__class__) + ac.__dict__.update(self.__dict__) + ac._wrap = adapter + ac.columns = util.populate_column_dict(ac._locate_col) + if ac.include_fn or ac.exclude_fn: + ac.columns = self._IncludeExcludeMapping(ac, ac.columns) + + return ac + + def traverse(self, obj): + return self.columns[obj] + + adapt_clause = traverse + adapt_list = ClauseAdapter.copy_and_process + + def _locate_col(self, col): + + c = ClauseAdapter.traverse(self, col) + + if self._wrap: + c2 = self._wrap._locate_col(c) + if c2 is not None: + c = c2 + + if self.adapt_required and c is col: + return None + + c._allow_label_resolve = self.allow_label_resolve + + return c + + def __getstate__(self): + d = self.__dict__.copy() + del d['columns'] + return d + + def __setstate__(self, state): + self.__dict__.update(state) + self.columns = util.PopulateDict(self._locate_col) diff --git a/venv/Lib/site-packages/sqlalchemy/sql/visitors.py b/venv/Lib/site-packages/sqlalchemy/sql/visitors.py new file mode 100644 index 0000000..2297692 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/sql/visitors.py @@ -0,0 +1,328 @@ +# sql/visitors.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Visitor/traversal interface and library functions. + +SQLAlchemy schema and expression constructs rely on a Python-centric +version of the classic "visitor" pattern as the primary way in which +they apply functionality. The most common use of this pattern +is statement compilation, where individual expression classes match +up to rendering methods that produce a string result. Beyond this, +the visitor system is also used to inspect expressions for various +information and patterns, as well as for usage in +some kinds of expression transformation. Other kinds of transformation +use a non-visitor traversal system. + +For many examples of how the visit system is used, see the +sqlalchemy.sql.util and the sqlalchemy.sql.compiler modules. +For an introduction to clause adaption, see +http://techspot.zzzeek.org/2008/01/23/expression-transformations/ + +""" + +from collections import deque +from .. import util +import operator +from .. import exc + +__all__ = ['VisitableType', 'Visitable', 'ClauseVisitor', + 'CloningVisitor', 'ReplacingCloningVisitor', 'iterate', + 'iterate_depthfirst', 'traverse_using', 'traverse', + 'traverse_depthfirst', + 'cloned_traverse', 'replacement_traverse'] + + +class VisitableType(type): + """Metaclass which assigns a `_compiler_dispatch` method to classes + having a `__visit_name__` attribute. + + The _compiler_dispatch attribute becomes an instance method which + looks approximately like the following:: + + def _compiler_dispatch (self, visitor, **kw): + '''Look for an attribute named "visit_" + self.__visit_name__ + on the visitor, and call it with the same kw params.''' + visit_attr = 'visit_%s' % self.__visit_name__ + return getattr(visitor, visit_attr)(self, **kw) + + Classes having no __visit_name__ attribute will remain unaffected. + """ + + def __init__(cls, clsname, bases, clsdict): + if clsname != 'Visitable' and \ + hasattr(cls, '__visit_name__'): + _generate_dispatch(cls) + + super(VisitableType, cls).__init__(clsname, bases, clsdict) + + +def _generate_dispatch(cls): + """Return an optimized visit dispatch function for the cls + for use by the compiler. + """ + if '__visit_name__' in cls.__dict__: + visit_name = cls.__visit_name__ + if isinstance(visit_name, str): + # There is an optimization opportunity here because the + # the string name of the class's __visit_name__ is known at + # this early stage (import time) so it can be pre-constructed. + getter = operator.attrgetter("visit_%s" % visit_name) + + def _compiler_dispatch(self, visitor, **kw): + try: + meth = getter(visitor) + except AttributeError: + raise exc.UnsupportedCompilationError(visitor, cls) + else: + return meth(self, **kw) + else: + # The optimization opportunity is lost for this case because the + # __visit_name__ is not yet a string. As a result, the visit + # string has to be recalculated with each compilation. + def _compiler_dispatch(self, visitor, **kw): + visit_attr = 'visit_%s' % self.__visit_name__ + try: + meth = getattr(visitor, visit_attr) + except AttributeError: + raise exc.UnsupportedCompilationError(visitor, cls) + else: + return meth(self, **kw) + + _compiler_dispatch.__doc__ = \ + """Look for an attribute named "visit_" + self.__visit_name__ + on the visitor, and call it with the same kw params. + """ + cls._compiler_dispatch = _compiler_dispatch + + +class Visitable(util.with_metaclass(VisitableType, object)): + """Base class for visitable objects, applies the + ``VisitableType`` metaclass. + + """ + + +class ClauseVisitor(object): + """Base class for visitor objects which can traverse using + the traverse() function. + + """ + + __traverse_options__ = {} + + def traverse_single(self, obj, **kw): + for v in self._visitor_iterator: + meth = getattr(v, "visit_%s" % obj.__visit_name__, None) + if meth: + return meth(obj, **kw) + + def iterate(self, obj): + """traverse the given expression structure, returning an iterator + of all elements. + + """ + return iterate(obj, self.__traverse_options__) + + def traverse(self, obj): + """traverse and visit the given expression structure.""" + + return traverse(obj, self.__traverse_options__, self._visitor_dict) + + @util.memoized_property + def _visitor_dict(self): + visitors = {} + + for name in dir(self): + if name.startswith('visit_'): + visitors[name[6:]] = getattr(self, name) + return visitors + + @property + def _visitor_iterator(self): + """iterate through this visitor and each 'chained' visitor.""" + + v = self + while v: + yield v + v = getattr(v, '_next', None) + + def chain(self, visitor): + """'chain' an additional ClauseVisitor onto this ClauseVisitor. + + the chained visitor will receive all visit events after this one. + + """ + tail = list(self._visitor_iterator)[-1] + tail._next = visitor + return self + + +class CloningVisitor(ClauseVisitor): + """Base class for visitor objects which can traverse using + the cloned_traverse() function. + + """ + + def copy_and_process(self, list_): + """Apply cloned traversal to the given list of elements, and return + the new list. + + """ + return [self.traverse(x) for x in list_] + + def traverse(self, obj): + """traverse and visit the given expression structure.""" + + return cloned_traverse( + obj, self.__traverse_options__, self._visitor_dict) + + +class ReplacingCloningVisitor(CloningVisitor): + """Base class for visitor objects which can traverse using + the replacement_traverse() function. + + """ + + def replace(self, elem): + """receive pre-copied elements during a cloning traversal. + + If the method returns a new element, the element is used + instead of creating a simple copy of the element. Traversal + will halt on the newly returned element if it is re-encountered. + """ + return None + + def traverse(self, obj): + """traverse and visit the given expression structure.""" + + def replace(elem): + for v in self._visitor_iterator: + e = v.replace(elem) + if e is not None: + return e + return replacement_traverse(obj, self.__traverse_options__, replace) + + +def iterate(obj, opts): + """traverse the given expression structure, returning an iterator. + + traversal is configured to be breadth-first. + + """ + # fasttrack for atomic elements like columns + children = obj.get_children(**opts) + if not children: + return [obj] + + traversal = deque() + stack = deque([obj]) + while stack: + t = stack.popleft() + traversal.append(t) + for c in t.get_children(**opts): + stack.append(c) + return iter(traversal) + + +def iterate_depthfirst(obj, opts): + """traverse the given expression structure, returning an iterator. + + traversal is configured to be depth-first. + + """ + # fasttrack for atomic elements like columns + children = obj.get_children(**opts) + if not children: + return [obj] + + stack = deque([obj]) + traversal = deque() + while stack: + t = stack.pop() + traversal.appendleft(t) + for c in t.get_children(**opts): + stack.append(c) + return iter(traversal) + + +def traverse_using(iterator, obj, visitors): + """visit the given expression structure using the given iterator of + objects. + + """ + for target in iterator: + meth = visitors.get(target.__visit_name__, None) + if meth: + meth(target) + return obj + + +def traverse(obj, opts, visitors): + """traverse and visit the given expression structure using the default + iterator. + + """ + return traverse_using(iterate(obj, opts), obj, visitors) + + +def traverse_depthfirst(obj, opts, visitors): + """traverse and visit the given expression structure using the + depth-first iterator. + + """ + return traverse_using(iterate_depthfirst(obj, opts), obj, visitors) + + +def cloned_traverse(obj, opts, visitors): + """clone the given expression structure, allowing + modifications by visitors.""" + + cloned = {} + stop_on = set(opts.get('stop_on', [])) + + def clone(elem): + if elem in stop_on: + return elem + else: + if id(elem) not in cloned: + cloned[id(elem)] = newelem = elem._clone() + newelem._copy_internals(clone=clone) + meth = visitors.get(newelem.__visit_name__, None) + if meth: + meth(newelem) + return cloned[id(elem)] + + if obj is not None: + obj = clone(obj) + return obj + + +def replacement_traverse(obj, opts, replace): + """clone the given expression structure, allowing element + replacement by a given replacement function.""" + + cloned = {} + stop_on = {id(x) for x in opts.get('stop_on', [])} + + def clone(elem, **kw): + if id(elem) in stop_on or \ + 'no_replacement_traverse' in elem._annotations: + return elem + else: + newelem = replace(elem) + if newelem is not None: + stop_on.add(id(newelem)) + return newelem + else: + if elem not in cloned: + cloned[elem] = newelem = elem._clone() + newelem._copy_internals(clone=clone, **kw) + return cloned[elem] + + if obj is not None: + obj = clone(obj, **opts) + return obj diff --git a/venv/Lib/site-packages/sqlalchemy/testing/__init__.py b/venv/Lib/site-packages/sqlalchemy/testing/__init__.py new file mode 100644 index 0000000..413a492 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/__init__.py @@ -0,0 +1,36 @@ +# testing/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + + +from .warnings import assert_warnings + +from . import config + +from .exclusions import db_spec, _is_excluded, fails_if, skip_if, future,\ + fails_on, fails_on_everything_except, skip, only_on, exclude, \ + against as _against, _server_version, only_if, fails + + +def against(*queries): + return _against(config._current, *queries) + +from .assertions import emits_warning, emits_warning_on, uses_deprecated, \ + eq_, ne_, le_, is_, is_not_, startswith_, assert_raises, \ + assert_raises_message, AssertsCompiledSQL, ComparesTables, \ + AssertsExecutionResults, expect_deprecated, expect_warnings, \ + in_, not_in_, eq_ignore_whitespace, eq_regex, is_true, is_false + +from .util import run_as_contextmanager, rowset, fail, \ + provide_metadata, adict, force_drop_names, \ + teardown_events + +crashes = skip + +from .config import db +from .config import requirements as requires + +from . import mock diff --git a/venv/Lib/site-packages/sqlalchemy/testing/assertions.py b/venv/Lib/site-packages/sqlalchemy/testing/assertions.py new file mode 100644 index 0000000..e423769 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/assertions.py @@ -0,0 +1,543 @@ +# testing/assertions.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from __future__ import absolute_import + +from . import util as testutil +from sqlalchemy import pool, orm, util +from sqlalchemy.engine import default, url +from sqlalchemy.util import decorator, compat +from sqlalchemy import types as sqltypes, schema, exc as sa_exc +import warnings +import re +from .exclusions import db_spec +from . import assertsql +from . import config +from .util import fail +import contextlib +from . import mock + + +def expect_warnings(*messages, **kw): + """Context manager which expects one or more warnings. + + With no arguments, squelches all SAWarnings emitted via + sqlalchemy.util.warn and sqlalchemy.util.warn_limited. Otherwise + pass string expressions that will match selected warnings via regex; + all non-matching warnings are sent through. + + The expect version **asserts** that the warnings were in fact seen. + + Note that the test suite sets SAWarning warnings to raise exceptions. + + """ + return _expect_warnings(sa_exc.SAWarning, messages, **kw) + + +@contextlib.contextmanager +def expect_warnings_on(db, *messages, **kw): + """Context manager which expects one or more warnings on specific + dialects. + + The expect version **asserts** that the warnings were in fact seen. + + """ + spec = db_spec(db) + + if isinstance(db, util.string_types) and not spec(config._current): + yield + else: + with expect_warnings(*messages, **kw): + yield + + +def emits_warning(*messages): + """Decorator form of expect_warnings(). + + Note that emits_warning does **not** assert that the warnings + were in fact seen. + + """ + + @decorator + def decorate(fn, *args, **kw): + with expect_warnings(assert_=False, *messages): + return fn(*args, **kw) + + return decorate + + +def expect_deprecated(*messages, **kw): + return _expect_warnings(sa_exc.SADeprecationWarning, messages, **kw) + + +def emits_warning_on(db, *messages): + """Mark a test as emitting a warning on a specific dialect. + + With no arguments, squelches all SAWarning failures. Or pass one or more + strings; these will be matched to the root of the warning description by + warnings.filterwarnings(). + + Note that emits_warning_on does **not** assert that the warnings + were in fact seen. + + """ + @decorator + def decorate(fn, *args, **kw): + with expect_warnings_on(db, assert_=False, *messages): + return fn(*args, **kw) + + return decorate + + +def uses_deprecated(*messages): + """Mark a test as immune from fatal deprecation warnings. + + With no arguments, squelches all SADeprecationWarning failures. + Or pass one or more strings; these will be matched to the root + of the warning description by warnings.filterwarnings(). + + As a special case, you may pass a function name prefixed with // + and it will be re-written as needed to match the standard warning + verbiage emitted by the sqlalchemy.util.deprecated decorator. + + Note that uses_deprecated does **not** assert that the warnings + were in fact seen. + + """ + + @decorator + def decorate(fn, *args, **kw): + with expect_deprecated(*messages, assert_=False): + return fn(*args, **kw) + return decorate + + +@contextlib.contextmanager +def _expect_warnings(exc_cls, messages, regex=True, assert_=True, + py2konly=False): + + if regex: + filters = [re.compile(msg, re.I | re.S) for msg in messages] + else: + filters = messages + + seen = set(filters) + + real_warn = warnings.warn + + def our_warn(msg, *arg, **kw): + if isinstance(msg, exc_cls): + exception = msg + msg = str(exception) + elif arg: + exception = arg[0] + else: + exception = None + if not exception or not issubclass(exception, exc_cls): + return real_warn(msg, *arg, **kw) + + if not filters: + return + + for filter_ in filters: + if (regex and filter_.match(msg)) or \ + (not regex and filter_ == msg): + seen.discard(filter_) + break + else: + real_warn(msg, *arg, **kw) + + with mock.patch("warnings.warn", our_warn): + yield + + if assert_ and (not py2konly or not compat.py3k): + assert not seen, "Warnings were not seen: %s" % \ + ", ".join("%r" % (s.pattern if regex else s) for s in seen) + + +def global_cleanup_assertions(): + """Check things that have to be finalized at the end of a test suite. + + Hardcoded at the moment, a modular system can be built here + to support things like PG prepared transactions, tables all + dropped, etc. + + """ + _assert_no_stray_pool_connections() + +_STRAY_CONNECTION_FAILURES = 0 + + +def _assert_no_stray_pool_connections(): + global _STRAY_CONNECTION_FAILURES + + # lazy gc on cPython means "do nothing." pool connections + # shouldn't be in cycles, should go away. + testutil.lazy_gc() + + # however, once in awhile, on an EC2 machine usually, + # there's a ref in there. usually just one. + if pool._refs: + + # OK, let's be somewhat forgiving. + _STRAY_CONNECTION_FAILURES += 1 + + print("Encountered a stray connection in test cleanup: %s" + % str(pool._refs)) + # then do a real GC sweep. We shouldn't even be here + # so a single sweep should really be doing it, otherwise + # there's probably a real unreachable cycle somewhere. + testutil.gc_collect() + + # if we've already had two of these occurrences, or + # after a hard gc sweep we still have pool._refs?! + # now we have to raise. + if pool._refs: + err = str(pool._refs) + + # but clean out the pool refs collection directly, + # reset the counter, + # so the error doesn't at least keep happening. + pool._refs.clear() + _STRAY_CONNECTION_FAILURES = 0 + warnings.warn( + "Stray connection refused to leave " + "after gc.collect(): %s" % err) + elif _STRAY_CONNECTION_FAILURES > 10: + assert False, "Encountered more than 10 stray connections" + _STRAY_CONNECTION_FAILURES = 0 + + +def eq_regex(a, b, msg=None): + assert re.match(b, a), msg or "%r !~ %r" % (a, b) + + +def eq_(a, b, msg=None): + """Assert a == b, with repr messaging on failure.""" + assert a == b, msg or "%r != %r" % (a, b) + + +def ne_(a, b, msg=None): + """Assert a != b, with repr messaging on failure.""" + assert a != b, msg or "%r == %r" % (a, b) + + +def le_(a, b, msg=None): + """Assert a <= b, with repr messaging on failure.""" + assert a <= b, msg or "%r != %r" % (a, b) + + +def is_true(a, msg=None): + is_(a, True, msg=msg) + + +def is_false(a, msg=None): + is_(a, False, msg=msg) + + +def is_(a, b, msg=None): + """Assert a is b, with repr messaging on failure.""" + assert a is b, msg or "%r is not %r" % (a, b) + + +def is_not_(a, b, msg=None): + """Assert a is not b, with repr messaging on failure.""" + assert a is not b, msg or "%r is %r" % (a, b) + + +def in_(a, b, msg=None): + """Assert a in b, with repr messaging on failure.""" + assert a in b, msg or "%r not in %r" % (a, b) + + +def not_in_(a, b, msg=None): + """Assert a in not b, with repr messaging on failure.""" + assert a not in b, msg or "%r is in %r" % (a, b) + + +def startswith_(a, fragment, msg=None): + """Assert a.startswith(fragment), with repr messaging on failure.""" + assert a.startswith(fragment), msg or "%r does not start with %r" % ( + a, fragment) + + +def eq_ignore_whitespace(a, b, msg=None): + a = re.sub(r'^\s+?|\n', "", a) + a = re.sub(r' {2,}', " ", a) + b = re.sub(r'^\s+?|\n', "", b) + b = re.sub(r' {2,}', " ", b) + + assert a == b, msg or "%r != %r" % (a, b) + + +def assert_raises(except_cls, callable_, *args, **kw): + try: + callable_(*args, **kw) + success = False + except except_cls: + success = True + + # assert outside the block so it works for AssertionError too ! + assert success, "Callable did not raise an exception" + + +def assert_raises_message(except_cls, msg, callable_, *args, **kwargs): + try: + callable_(*args, **kwargs) + assert False, "Callable did not raise an exception" + except except_cls as e: + assert re.search( + msg, util.text_type(e), re.UNICODE), "%r !~ %s" % (msg, e) + print(util.text_type(e).encode('utf-8')) + +class AssertsCompiledSQL(object): + def assert_compile(self, clause, result, params=None, + checkparams=None, dialect=None, + checkpositional=None, + check_prefetch=None, + use_default_dialect=False, + allow_dialect_select=False, + literal_binds=False, + schema_translate_map=None): + if use_default_dialect: + dialect = default.DefaultDialect() + elif allow_dialect_select: + dialect = None + else: + if dialect is None: + dialect = getattr(self, '__dialect__', None) + + if dialect is None: + dialect = config.db.dialect + elif dialect == 'default': + dialect = default.DefaultDialect() + elif dialect == 'default_enhanced': + dialect = default.StrCompileDialect() + elif isinstance(dialect, util.string_types): + dialect = url.URL(dialect).get_dialect()() + + kw = {} + compile_kwargs = {} + + if schema_translate_map: + kw['schema_translate_map'] = schema_translate_map + + if params is not None: + kw['column_keys'] = list(params) + + if literal_binds: + compile_kwargs['literal_binds'] = True + + if isinstance(clause, orm.Query): + context = clause._compile_context() + context.statement.use_labels = True + clause = context.statement + elif isinstance(clause, orm.persistence.BulkUD): + with mock.patch.object(clause, "_execute_stmt") as stmt_mock: + clause.exec_() + clause = stmt_mock.mock_calls[0][1][0] + + if compile_kwargs: + kw['compile_kwargs'] = compile_kwargs + + c = clause.compile(dialect=dialect, **kw) + + param_str = repr(getattr(c, 'params', {})) + + if util.py3k: + param_str = param_str.encode('utf-8').decode('ascii', 'ignore') + print( + ("\nSQL String:\n" + + util.text_type(c) + + param_str).encode('utf-8')) + else: + print( + "\nSQL String:\n" + + util.text_type(c).encode('utf-8') + + param_str) + + cc = re.sub(r'[\n\t]', '', util.text_type(c)) + + eq_(cc, result, "%r != %r on dialect %r" % (cc, result, dialect)) + + if checkparams is not None: + eq_(c.construct_params(params), checkparams) + if checkpositional is not None: + p = c.construct_params(params) + eq_(tuple([p[x] for x in c.positiontup]), checkpositional) + if check_prefetch is not None: + eq_(c.prefetch, check_prefetch) + + +class ComparesTables(object): + + def assert_tables_equal(self, table, reflected_table, strict_types=False): + assert len(table.c) == len(reflected_table.c) + for c, reflected_c in zip(table.c, reflected_table.c): + eq_(c.name, reflected_c.name) + assert reflected_c is reflected_table.c[c.name] + eq_(c.primary_key, reflected_c.primary_key) + eq_(c.nullable, reflected_c.nullable) + + if strict_types: + msg = "Type '%s' doesn't correspond to type '%s'" + assert isinstance(reflected_c.type, type(c.type)), \ + msg % (reflected_c.type, c.type) + else: + self.assert_types_base(reflected_c, c) + + if isinstance(c.type, sqltypes.String): + eq_(c.type.length, reflected_c.type.length) + + eq_( + {f.column.name for f in c.foreign_keys}, + {f.column.name for f in reflected_c.foreign_keys} + ) + if c.server_default: + assert isinstance(reflected_c.server_default, + schema.FetchedValue) + + assert len(table.primary_key) == len(reflected_table.primary_key) + for c in table.primary_key: + assert reflected_table.primary_key.columns[c.name] is not None + + def assert_types_base(self, c1, c2): + assert c1.type._compare_type_affinity(c2.type),\ + "On column %r, type '%s' doesn't correspond to type '%s'" % \ + (c1.name, c1.type, c2.type) + + +class AssertsExecutionResults(object): + def assert_result(self, result, class_, *objects): + result = list(result) + print(repr(result)) + self.assert_list(result, class_, objects) + + def assert_list(self, result, class_, list): + self.assert_(len(result) == len(list), + "result list is not the same size as test list, " + + "for class " + class_.__name__) + for i in range(0, len(list)): + self.assert_row(class_, result[i], list[i]) + + def assert_row(self, class_, rowobj, desc): + self.assert_(rowobj.__class__ is class_, + "item class is not " + repr(class_)) + for key, value in desc.items(): + if isinstance(value, tuple): + if isinstance(value[1], list): + self.assert_list(getattr(rowobj, key), value[0], value[1]) + else: + self.assert_row(value[0], getattr(rowobj, key), value[1]) + else: + self.assert_(getattr(rowobj, key) == value, + "attribute %s value %s does not match %s" % ( + key, getattr(rowobj, key), value)) + + def assert_unordered_result(self, result, cls, *expected): + """As assert_result, but the order of objects is not considered. + + The algorithm is very expensive but not a big deal for the small + numbers of rows that the test suite manipulates. + """ + + class immutabledict(dict): + def __hash__(self): + return id(self) + + found = util.IdentitySet(result) + expected = {immutabledict(e) for e in expected} + + for wrong in util.itertools_filterfalse(lambda o: + isinstance(o, cls), found): + fail('Unexpected type "%s", expected "%s"' % ( + type(wrong).__name__, cls.__name__)) + + if len(found) != len(expected): + fail('Unexpected object count "%s", expected "%s"' % ( + len(found), len(expected))) + + NOVALUE = object() + + def _compare_item(obj, spec): + for key, value in spec.items(): + if isinstance(value, tuple): + try: + self.assert_unordered_result( + getattr(obj, key), value[0], *value[1]) + except AssertionError: + return False + else: + if getattr(obj, key, NOVALUE) != value: + return False + return True + + for expected_item in expected: + for found_item in found: + if _compare_item(found_item, expected_item): + found.remove(found_item) + break + else: + fail( + "Expected %s instance with attributes %s not found." % ( + cls.__name__, repr(expected_item))) + return True + + def sql_execution_asserter(self, db=None): + if db is None: + from . import db as db + + return assertsql.assert_engine(db) + + def assert_sql_execution(self, db, callable_, *rules): + with self.sql_execution_asserter(db) as asserter: + result = callable_() + asserter.assert_(*rules) + return result + + def assert_sql(self, db, callable_, rules): + + newrules = [] + for rule in rules: + if isinstance(rule, dict): + newrule = assertsql.AllOf(*[ + assertsql.CompiledSQL(k, v) for k, v in rule.items() + ]) + else: + newrule = assertsql.CompiledSQL(*rule) + newrules.append(newrule) + + return self.assert_sql_execution(db, callable_, *newrules) + + def assert_sql_count(self, db, callable_, count): + self.assert_sql_execution( + db, callable_, assertsql.CountStatements(count)) + + def assert_multiple_sql_count(self, dbs, callable_, counts): + recs = [ + (self.sql_execution_asserter(db), db, count) + for (db, count) in zip(dbs, counts) + ] + asserters = [] + for ctx, db, count in recs: + asserters.append(ctx.__enter__()) + try: + return callable_() + finally: + for asserter, (ctx, db, count) in zip(asserters, recs): + ctx.__exit__(None, None, None) + asserter.assert_(assertsql.CountStatements(count)) + + @contextlib.contextmanager + def assert_execution(self, db, *rules): + with self.sql_execution_asserter(db) as asserter: + yield + asserter.assert_(*rules) + + def assert_statement_count(self, db, count): + return self.assert_execution(db, assertsql.CountStatements(count)) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/assertsql.py b/venv/Lib/site-packages/sqlalchemy/testing/assertsql.py new file mode 100644 index 0000000..7a52558 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/assertsql.py @@ -0,0 +1,399 @@ +# testing/assertsql.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from ..engine.default import DefaultDialect +from .. import util +import re +import collections +import contextlib +from .. import event +from sqlalchemy.schema import _DDLCompiles +from sqlalchemy.engine.util import _distill_params +from sqlalchemy.engine import url + + +class AssertRule(object): + + is_consumed = False + errormessage = None + consume_statement = True + + def process_statement(self, execute_observed): + pass + + def no_more_statements(self): + assert False, 'All statements are complete, but pending '\ + 'assertion rules remain' + + +class SQLMatchRule(AssertRule): + pass + + +class CursorSQL(SQLMatchRule): + consume_statement = False + + def __init__(self, statement, params=None): + self.statement = statement + self.params = params + + def process_statement(self, execute_observed): + stmt = execute_observed.statements[0] + if self.statement != stmt.statement or ( + self.params is not None and self.params != stmt.parameters): + self.errormessage = \ + "Testing for exact SQL %s parameters %s received %s %s" % ( + self.statement, self.params, + stmt.statement, stmt.parameters + ) + else: + execute_observed.statements.pop(0) + self.is_consumed = True + if not execute_observed.statements: + self.consume_statement = True + + +class CompiledSQL(SQLMatchRule): + + def __init__(self, statement, params=None, dialect='default'): + self.statement = statement + self.params = params + self.dialect = dialect + + def _compare_sql(self, execute_observed, received_statement): + stmt = re.sub(r'[\n\t]', '', self.statement) + return received_statement == stmt + + def _compile_dialect(self, execute_observed): + if self.dialect == 'default': + return DefaultDialect() + else: + # ugh + if self.dialect == 'postgresql': + params = {'implicit_returning': True} + else: + params = {} + return url.URL(self.dialect).get_dialect()(**params) + + def _received_statement(self, execute_observed): + """reconstruct the statement and params in terms + of a target dialect, which for CompiledSQL is just DefaultDialect.""" + + context = execute_observed.context + compare_dialect = self._compile_dialect(execute_observed) + if isinstance(context.compiled.statement, _DDLCompiles): + compiled = \ + context.compiled.statement.compile( + dialect=compare_dialect, + schema_translate_map=context. + execution_options.get('schema_translate_map')) + else: + compiled = ( + context.compiled.statement.compile( + dialect=compare_dialect, + column_keys=context.compiled.column_keys, + inline=context.compiled.inline, + schema_translate_map=context. + execution_options.get('schema_translate_map')) + ) + _received_statement = re.sub(r'[\n\t]', '', util.text_type(compiled)) + parameters = execute_observed.parameters + + if not parameters: + _received_parameters = [compiled.construct_params()] + else: + _received_parameters = [ + compiled.construct_params(m) for m in parameters] + + return _received_statement, _received_parameters + + def process_statement(self, execute_observed): + context = execute_observed.context + + _received_statement, _received_parameters = \ + self._received_statement(execute_observed) + params = self._all_params(context) + + equivalent = self._compare_sql(execute_observed, _received_statement) + + if equivalent: + if params is not None: + all_params = list(params) + all_received = list(_received_parameters) + while all_params and all_received: + param = dict(all_params.pop(0)) + + for idx, received in enumerate(list(all_received)): + # do a positive compare only + for param_key in param: + # a key in param did not match current + # 'received' + if param_key not in received or \ + received[param_key] != param[param_key]: + break + else: + # all keys in param matched 'received'; + # onto next param + del all_received[idx] + break + else: + # param did not match any entry + # in all_received + equivalent = False + break + if all_params or all_received: + equivalent = False + + if equivalent: + self.is_consumed = True + self.errormessage = None + else: + self.errormessage = self._failure_message(params) % { + 'received_statement': _received_statement, + 'received_parameters': _received_parameters + } + + def _all_params(self, context): + if self.params: + if util.callable(self.params): + params = self.params(context) + else: + params = self.params + if not isinstance(params, list): + params = [params] + return params + else: + return None + + def _failure_message(self, expected_params): + return ( + 'Testing for compiled statement %r partial params %r, ' + 'received %%(received_statement)r with params ' + '%%(received_parameters)r' % ( + self.statement.replace('%', '%%'), expected_params + ) + ) + + +class RegexSQL(CompiledSQL): + def __init__(self, regex, params=None): + SQLMatchRule.__init__(self) + self.regex = re.compile(regex) + self.orig_regex = regex + self.params = params + self.dialect = 'default' + + def _failure_message(self, expected_params): + return ( + 'Testing for compiled statement ~%r partial params %r, ' + 'received %%(received_statement)r with params ' + '%%(received_parameters)r' % ( + self.orig_regex, expected_params + ) + ) + + def _compare_sql(self, execute_observed, received_statement): + return bool(self.regex.match(received_statement)) + + +class DialectSQL(CompiledSQL): + def _compile_dialect(self, execute_observed): + return execute_observed.context.dialect + + def _compare_no_space(self, real_stmt, received_stmt): + stmt = re.sub(r'[\n\t]', '', real_stmt) + return received_stmt == stmt + + def _received_statement(self, execute_observed): + received_stmt, received_params = super(DialectSQL, self).\ + _received_statement(execute_observed) + + # TODO: why do we need this part? + for real_stmt in execute_observed.statements: + if self._compare_no_space(real_stmt.statement, received_stmt): + break + else: + raise AssertionError( + "Can't locate compiled statement %r in list of " + "statements actually invoked" % received_stmt) + + return received_stmt, execute_observed.context.compiled_parameters + + def _compare_sql(self, execute_observed, received_statement): + stmt = re.sub(r'[\n\t]', '', self.statement) + # convert our comparison statement to have the + # paramstyle of the received + paramstyle = execute_observed.context.dialect.paramstyle + if paramstyle == 'pyformat': + stmt = re.sub( + r':([\w_]+)', r"%(\1)s", stmt) + else: + # positional params + repl = None + if paramstyle == 'qmark': + repl = "?" + elif paramstyle == 'format': + repl = r"%s" + elif paramstyle == 'numeric': + repl = None + stmt = re.sub(r':([\w_]+)', repl, stmt) + + return received_statement == stmt + + +class CountStatements(AssertRule): + + def __init__(self, count): + self.count = count + self._statement_count = 0 + + def process_statement(self, execute_observed): + self._statement_count += 1 + + def no_more_statements(self): + if self.count != self._statement_count: + assert False, 'desired statement count %d does not match %d' \ + % (self.count, self._statement_count) + + +class AllOf(AssertRule): + + def __init__(self, *rules): + self.rules = set(rules) + + def process_statement(self, execute_observed): + for rule in list(self.rules): + rule.errormessage = None + rule.process_statement(execute_observed) + if rule.is_consumed: + self.rules.discard(rule) + if not self.rules: + self.is_consumed = True + break + elif not rule.errormessage: + # rule is not done yet + self.errormessage = None + break + else: + self.errormessage = list(self.rules)[0].errormessage + + +class EachOf(AssertRule): + + def __init__(self, *rules): + self.rules = list(rules) + + def process_statement(self, execute_observed): + while self.rules: + rule = self.rules[0] + rule.process_statement(execute_observed) + if rule.is_consumed: + self.rules.pop(0) + elif rule.errormessage: + self.errormessage = rule.errormessage + if rule.consume_statement: + break + + if not self.rules: + self.is_consumed = True + + def no_more_statements(self): + if self.rules and not self.rules[0].is_consumed: + self.rules[0].no_more_statements() + elif self.rules: + super(EachOf, self).no_more_statements() + + +class Or(AllOf): + + def process_statement(self, execute_observed): + for rule in self.rules: + rule.process_statement(execute_observed) + if rule.is_consumed: + self.is_consumed = True + break + else: + self.errormessage = list(self.rules)[0].errormessage + + +class SQLExecuteObserved(object): + def __init__(self, context, clauseelement, multiparams, params): + self.context = context + self.clauseelement = clauseelement + self.parameters = _distill_params(multiparams, params) + self.statements = [] + + +class SQLCursorExecuteObserved( + collections.namedtuple( + "SQLCursorExecuteObserved", + ["statement", "parameters", "context", "executemany"]) +): + pass + + +class SQLAsserter(object): + def __init__(self): + self.accumulated = [] + + def _close(self): + self._final = self.accumulated + del self.accumulated + + def assert_(self, *rules): + rule = EachOf(*rules) + + observed = list(self._final) + while observed: + statement = observed.pop(0) + rule.process_statement(statement) + if rule.is_consumed: + break + elif rule.errormessage: + assert False, rule.errormessage + if observed: + assert False, "Additional SQL statements remain" + elif not rule.is_consumed: + rule.no_more_statements() + + +@contextlib.contextmanager +def assert_engine(engine): + asserter = SQLAsserter() + + orig = [] + + @event.listens_for(engine, "before_execute") + def connection_execute(conn, clauseelement, multiparams, params): + # grab the original statement + params before any cursor + # execution + orig[:] = clauseelement, multiparams, params + + @event.listens_for(engine, "after_cursor_execute") + def cursor_execute(conn, cursor, statement, parameters, + context, executemany): + if not context: + return + # then grab real cursor statements and associate them all + # around a single context + if asserter.accumulated and \ + asserter.accumulated[-1].context is context: + obs = asserter.accumulated[-1] + else: + obs = SQLExecuteObserved(context, orig[0], orig[1], orig[2]) + asserter.accumulated.append(obs) + obs.statements.append( + SQLCursorExecuteObserved( + statement, parameters, context, executemany) + ) + + try: + yield asserter + finally: + event.remove(engine, "after_cursor_execute", cursor_execute) + event.remove(engine, "before_execute", connection_execute) + asserter._close() diff --git a/venv/Lib/site-packages/sqlalchemy/testing/config.py b/venv/Lib/site-packages/sqlalchemy/testing/config.py new file mode 100644 index 0000000..dfe0da9 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/config.py @@ -0,0 +1,101 @@ +# testing/config.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +import collections + +requirements = None +db = None +db_url = None +db_opts = None +file_config = None +test_schema = None +test_schema_2 = None +_current = None + +try: + from unittest import SkipTest as _skip_test_exception +except ImportError: + _skip_test_exception = None + + +class Config(object): + def __init__(self, db, db_opts, options, file_config): + self._set_name(db) + self.db = db + self.db_opts = db_opts + self.options = options + self.file_config = file_config + self.test_schema = "test_schema" + self.test_schema_2 = "test_schema_2" + + _stack = collections.deque() + _configs = set() + + def _set_name(self, db): + if db.dialect.server_version_info: + svi = ".".join(str(tok) for tok in db.dialect.server_version_info) + self.name = "%s+%s_[%s]" % (db.name, db.driver, svi) + else: + self.name = "%s+%s" % (db.name, db.driver) + + @classmethod + def register(cls, db, db_opts, options, file_config): + """add a config as one of the global configs. + + If there are no configs set up yet, this config also + gets set as the "_current". + """ + cfg = Config(db, db_opts, options, file_config) + cls._configs.add(cfg) + return cfg + + @classmethod + def set_as_current(cls, config, namespace): + global db, _current, db_url, test_schema, test_schema_2, db_opts + _current = config + db_url = config.db.url + db_opts = config.db_opts + test_schema = config.test_schema + test_schema_2 = config.test_schema_2 + namespace.db = db = config.db + + @classmethod + def push_engine(cls, db, namespace): + assert _current, "Can't push without a default Config set up" + cls.push( + Config( + db, _current.db_opts, _current.options, _current.file_config), + namespace + ) + + @classmethod + def push(cls, config, namespace): + cls._stack.append(_current) + cls.set_as_current(config, namespace) + + @classmethod + def reset(cls, namespace): + if cls._stack: + cls.set_as_current(cls._stack[0], namespace) + cls._stack.clear() + + @classmethod + def all_configs(cls): + return cls._configs + + @classmethod + def all_dbs(cls): + for cfg in cls.all_configs(): + yield cfg.db + + def skip_test(self, msg): + skip_test(msg) + + +def skip_test(msg): + raise _skip_test_exception(msg) + diff --git a/venv/Lib/site-packages/sqlalchemy/testing/engines.py b/venv/Lib/site-packages/sqlalchemy/testing/engines.py new file mode 100644 index 0000000..d17e30e --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/engines.py @@ -0,0 +1,371 @@ +# testing/engines.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from __future__ import absolute_import + +import weakref +from . import config +from .util import decorator +from .. import event, pool +import re +import warnings + + +class ConnectionKiller(object): + + def __init__(self): + self.proxy_refs = weakref.WeakKeyDictionary() + self.testing_engines = weakref.WeakKeyDictionary() + self.conns = set() + + def add_engine(self, engine): + self.testing_engines[engine] = True + + def connect(self, dbapi_conn, con_record): + self.conns.add((dbapi_conn, con_record)) + + def checkout(self, dbapi_con, con_record, con_proxy): + self.proxy_refs[con_proxy] = True + + def invalidate(self, dbapi_con, con_record, exception): + self.conns.discard((dbapi_con, con_record)) + + def _safe(self, fn): + try: + fn() + except Exception as e: + warnings.warn( + "testing_reaper couldn't " + "rollback/close connection: %s" % e) + + def rollback_all(self): + for rec in list(self.proxy_refs): + if rec is not None and rec.is_valid: + self._safe(rec.rollback) + + def close_all(self): + for rec in list(self.proxy_refs): + if rec is not None and rec.is_valid: + self._safe(rec._close) + + def _after_test_ctx(self): + # this can cause a deadlock with pg8000 - pg8000 acquires + # prepared statement lock inside of rollback() - if async gc + # is collecting in finalize_fairy, deadlock. + # not sure if this should be if pypy/jython only. + # note that firebird/fdb definitely needs this though + for conn, rec in list(self.conns): + if rec.connection is None: + # this is a hint that the connection is closed, which + # is causing segfaults on mysqlclient due to + # https://github.com/PyMySQL/mysqlclient-python/issues/270; + # try to work around here + continue + self._safe(conn.rollback) + + def _stop_test_ctx(self): + if config.options.low_connections: + self._stop_test_ctx_minimal() + else: + self._stop_test_ctx_aggressive() + + def _stop_test_ctx_minimal(self): + self.close_all() + + self.conns = set() + + for rec in list(self.testing_engines): + if rec is not config.db: + rec.dispose() + + def _stop_test_ctx_aggressive(self): + self.close_all() + for conn, rec in list(self.conns): + self._safe(conn.close) + rec.connection = None + + self.conns = set() + for rec in list(self.testing_engines): + rec.dispose() + + def assert_all_closed(self): + for rec in self.proxy_refs: + if rec.is_valid: + assert False + +testing_reaper = ConnectionKiller() + + +def drop_all_tables(metadata, bind): + testing_reaper.close_all() + if hasattr(bind, 'close'): + bind.close() + + if not config.db.dialect.supports_alter: + from . import assertions + with assertions.expect_warnings( + "Can't sort tables", assert_=False): + metadata.drop_all(bind) + else: + metadata.drop_all(bind) + + +@decorator +def assert_conns_closed(fn, *args, **kw): + try: + fn(*args, **kw) + finally: + testing_reaper.assert_all_closed() + + +@decorator +def rollback_open_connections(fn, *args, **kw): + """Decorator that rolls back all open connections after fn execution.""" + + try: + fn(*args, **kw) + finally: + testing_reaper.rollback_all() + + +@decorator +def close_first(fn, *args, **kw): + """Decorator that closes all connections before fn execution.""" + + testing_reaper.close_all() + fn(*args, **kw) + + +@decorator +def close_open_connections(fn, *args, **kw): + """Decorator that closes all connections after fn execution.""" + try: + fn(*args, **kw) + finally: + testing_reaper.close_all() + + +def all_dialects(exclude=None): + import sqlalchemy.databases as d + for name in d.__all__: + # TEMPORARY + if exclude and name in exclude: + continue + mod = getattr(d, name, None) + if not mod: + mod = getattr(__import__( + 'sqlalchemy.databases.%s' % name).databases, name) + yield mod.dialect() + + +class ReconnectFixture(object): + + def __init__(self, dbapi): + self.dbapi = dbapi + self.connections = [] + self.is_stopped = False + + def __getattr__(self, key): + return getattr(self.dbapi, key) + + def connect(self, *args, **kwargs): + + conn = self.dbapi.connect(*args, **kwargs) + if self.is_stopped: + self._safe(conn.close) + curs = conn.cursor() # should fail on Oracle etc. + # should fail for everything that didn't fail + # above, connection is closed + curs.execute("select 1") + assert False, "simulated connect failure didn't work" + else: + self.connections.append(conn) + return conn + + def _safe(self, fn): + try: + fn() + except Exception as e: + warnings.warn( + "ReconnectFixture couldn't " + "close connection: %s" % e) + + def shutdown(self, stop=False): + # TODO: this doesn't cover all cases + # as nicely as we'd like, namely MySQLdb. + # would need to implement R. Brewer's + # proxy server idea to get better + # coverage. + self.is_stopped = stop + for c in list(self.connections): + self._safe(c.close) + self.connections = [] + + def restart(self): + self.is_stopped = False + + +def reconnecting_engine(url=None, options=None): + url = url or config.db.url + dbapi = config.db.dialect.dbapi + if not options: + options = {} + options['module'] = ReconnectFixture(dbapi) + engine = testing_engine(url, options) + _dispose = engine.dispose + + def dispose(): + engine.dialect.dbapi.shutdown() + engine.dialect.dbapi.is_stopped = False + _dispose() + + engine.test_shutdown = engine.dialect.dbapi.shutdown + engine.test_restart = engine.dialect.dbapi.restart + engine.dispose = dispose + return engine + + +def testing_engine(url=None, options=None): + """Produce an engine configured by --options with optional overrides.""" + + from sqlalchemy import create_engine + from sqlalchemy.engine.url import make_url + + if not options: + use_reaper = True + else: + use_reaper = options.pop('use_reaper', True) + + url = url or config.db.url + + url = make_url(url) + if options is None: + if config.db is None or url.drivername == config.db.url.drivername: + options = config.db_opts + else: + options = {} + elif config.db is not None and url.drivername == config.db.url.drivername: + default_opt = config.db_opts.copy() + default_opt.update(options) + + engine = create_engine(url, **options) + engine._has_events = True # enable event blocks, helps with profiling + + if isinstance(engine.pool, pool.QueuePool): + engine.pool._timeout = 0 + engine.pool._max_overflow = 0 + if use_reaper: + event.listen(engine.pool, 'connect', testing_reaper.connect) + event.listen(engine.pool, 'checkout', testing_reaper.checkout) + event.listen(engine.pool, 'invalidate', testing_reaper.invalidate) + testing_reaper.add_engine(engine) + + return engine + + +def mock_engine(dialect_name=None): + """Provides a mocking engine based on the current testing.db. + + This is normally used to test DDL generation flow as emitted + by an Engine. + + It should not be used in other cases, as assert_compile() and + assert_sql_execution() are much better choices with fewer + moving parts. + + """ + + from sqlalchemy import create_engine + + if not dialect_name: + dialect_name = config.db.name + + buffer = [] + + def executor(sql, *a, **kw): + buffer.append(sql) + + def assert_sql(stmts): + recv = [re.sub(r'[\n\t]', '', str(s)) for s in buffer] + assert recv == stmts, recv + + def print_sql(): + d = engine.dialect + return "\n".join( + str(s.compile(dialect=d)) + for s in engine.mock + ) + + engine = create_engine(dialect_name + '://', + strategy='mock', executor=executor) + assert not hasattr(engine, 'mock') + engine.mock = buffer + engine.assert_sql = assert_sql + engine.print_sql = print_sql + return engine + + +class DBAPIProxyCursor(object): + """Proxy a DBAPI cursor. + + Tests can provide subclasses of this to intercept + DBAPI-level cursor operations. + + """ + + def __init__(self, engine, conn, *args, **kwargs): + self.engine = engine + self.connection = conn + self.cursor = conn.cursor(*args, **kwargs) + + def execute(self, stmt, parameters=None, **kw): + if parameters: + return self.cursor.execute(stmt, parameters, **kw) + else: + return self.cursor.execute(stmt, **kw) + + def executemany(self, stmt, params, **kw): + return self.cursor.executemany(stmt, params, **kw) + + def __getattr__(self, key): + return getattr(self.cursor, key) + + +class DBAPIProxyConnection(object): + """Proxy a DBAPI connection. + + Tests can provide subclasses of this to intercept + DBAPI-level connection operations. + + """ + + def __init__(self, engine, cursor_cls): + self.conn = self._sqla_unwrap = engine.pool._creator() + self.engine = engine + self.cursor_cls = cursor_cls + + def cursor(self, *args, **kwargs): + return self.cursor_cls(self.engine, self.conn, *args, **kwargs) + + def close(self): + self.conn.close() + + def __getattr__(self, key): + return getattr(self.conn, key) + + +def proxying_engine(conn_cls=DBAPIProxyConnection, + cursor_cls=DBAPIProxyCursor): + """Produce an engine that provides proxy hooks for + common methods. + + """ + def mock_conn(): + return conn_cls(config.db, cursor_cls) + return testing_engine(options={'creator': mock_conn}) + + diff --git a/venv/Lib/site-packages/sqlalchemy/testing/entities.py b/venv/Lib/site-packages/sqlalchemy/testing/entities.py new file mode 100644 index 0000000..b634735 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/entities.py @@ -0,0 +1,101 @@ +# testing/entities.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +import sqlalchemy as sa +from sqlalchemy import exc as sa_exc + +_repr_stack = set() + + +class BasicEntity(object): + + def __init__(self, **kw): + for key, value in kw.items(): + setattr(self, key, value) + + def __repr__(self): + if id(self) in _repr_stack: + return object.__repr__(self) + _repr_stack.add(id(self)) + try: + return "%s(%s)" % ( + (self.__class__.__name__), + ', '.join(["%s=%r" % (key, getattr(self, key)) + for key in sorted(self.__dict__.keys()) + if not key.startswith('_')])) + finally: + _repr_stack.remove(id(self)) + +_recursion_stack = set() + + +class ComparableEntity(BasicEntity): + + def __hash__(self): + return hash(self.__class__) + + def __ne__(self, other): + return not self.__eq__(other) + + def __eq__(self, other): + """'Deep, sparse compare. + + Deeply compare two entities, following the non-None attributes of the + non-persisted object, if possible. + + """ + if other is self: + return True + elif not self.__class__ == other.__class__: + return False + + if id(self) in _recursion_stack: + return True + _recursion_stack.add(id(self)) + + try: + # pick the entity that's not SA persisted as the source + try: + self_key = sa.orm.attributes.instance_state(self).key + except sa.orm.exc.NO_STATE: + self_key = None + + if other is None: + a = self + b = other + elif self_key is not None: + a = other + b = self + else: + a = self + b = other + + for attr in list(a.__dict__): + if attr.startswith('_'): + continue + value = getattr(a, attr) + + try: + # handle lazy loader errors + battr = getattr(b, attr) + except (AttributeError, sa_exc.UnboundExecutionError): + return False + + if hasattr(value, '__iter__'): + if hasattr(value, '__getitem__') and not hasattr( + value, 'keys'): + if list(value) != list(battr): + return False + else: + if set(value) != set(battr): + return False + else: + if value is not None and value != battr: + return False + return True + finally: + _recursion_stack.remove(id(self)) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/exclusions.py b/venv/Lib/site-packages/sqlalchemy/testing/exclusions.py new file mode 100644 index 0000000..65b70a5 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/exclusions.py @@ -0,0 +1,446 @@ +# testing/exclusions.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + + +import operator +from ..util import decorator +from . import config +from .. import util +import inspect +import contextlib +from sqlalchemy.util.compat import inspect_getargspec + + +def skip_if(predicate, reason=None): + rule = compound() + pred = _as_predicate(predicate, reason) + rule.skips.add(pred) + return rule + + +def fails_if(predicate, reason=None): + rule = compound() + pred = _as_predicate(predicate, reason) + rule.fails.add(pred) + return rule + + +class compound(object): + def __init__(self): + self.fails = set() + self.skips = set() + self.tags = set() + + def __add__(self, other): + return self.add(other) + + def add(self, *others): + copy = compound() + copy.fails.update(self.fails) + copy.skips.update(self.skips) + copy.tags.update(self.tags) + for other in others: + copy.fails.update(other.fails) + copy.skips.update(other.skips) + copy.tags.update(other.tags) + return copy + + def not_(self): + copy = compound() + copy.fails.update(NotPredicate(fail) for fail in self.fails) + copy.skips.update(NotPredicate(skip) for skip in self.skips) + copy.tags.update(self.tags) + return copy + + @property + def enabled(self): + return self.enabled_for_config(config._current) + + def enabled_for_config(self, config): + for predicate in self.skips.union(self.fails): + if predicate(config): + return False + else: + return True + + def matching_config_reasons(self, config): + return [ + predicate._as_string(config) for predicate + in self.skips.union(self.fails) + if predicate(config) + ] + + def include_test(self, include_tags, exclude_tags): + return bool( + not self.tags.intersection(exclude_tags) and + (not include_tags or self.tags.intersection(include_tags)) + ) + + def _extend(self, other): + self.skips.update(other.skips) + self.fails.update(other.fails) + self.tags.update(other.tags) + + def __call__(self, fn): + if hasattr(fn, '_sa_exclusion_extend'): + fn._sa_exclusion_extend._extend(self) + return fn + + @decorator + def decorate(fn, *args, **kw): + return self._do(config._current, fn, *args, **kw) + decorated = decorate(fn) + decorated._sa_exclusion_extend = self + return decorated + + @contextlib.contextmanager + def fail_if(self): + all_fails = compound() + all_fails.fails.update(self.skips.union(self.fails)) + + try: + yield + except Exception as ex: + all_fails._expect_failure(config._current, ex) + else: + all_fails._expect_success(config._current) + + def _do(self, cfg, fn, *args, **kw): + for skip in self.skips: + if skip(cfg): + msg = "'%s' : %s" % ( + fn.__name__, + skip._as_string(cfg) + ) + config.skip_test(msg) + + try: + return_value = fn(*args, **kw) + except Exception as ex: + self._expect_failure(cfg, ex, name=fn.__name__) + else: + self._expect_success(cfg, name=fn.__name__) + return return_value + + def _expect_failure(self, config, ex, name='block'): + for fail in self.fails: + if fail(config): + print(("%s failed as expected (%s): %s " % ( + name, fail._as_string(config), str(ex)))) + break + else: + util.raise_from_cause(ex) + + def _expect_success(self, config, name='block'): + if not self.fails: + return + for fail in self.fails: + if not fail(config): + break + else: + raise AssertionError( + "Unexpected success for '%s' (%s)" % + ( + name, + " and ".join( + fail._as_string(config) + for fail in self.fails + ) + ) + ) + + +def requires_tag(tagname): + return tags([tagname]) + + +def tags(tagnames): + comp = compound() + comp.tags.update(tagnames) + return comp + + +def only_if(predicate, reason=None): + predicate = _as_predicate(predicate) + return skip_if(NotPredicate(predicate), reason) + + +def succeeds_if(predicate, reason=None): + predicate = _as_predicate(predicate) + return fails_if(NotPredicate(predicate), reason) + + +class Predicate(object): + @classmethod + def as_predicate(cls, predicate, description=None): + if isinstance(predicate, compound): + return cls.as_predicate(predicate.enabled_for_config, description) + elif isinstance(predicate, Predicate): + if description and predicate.description is None: + predicate.description = description + return predicate + elif isinstance(predicate, (list, set)): + return OrPredicate( + [cls.as_predicate(pred) for pred in predicate], + description) + elif isinstance(predicate, tuple): + return SpecPredicate(*predicate) + elif isinstance(predicate, util.string_types): + tokens = predicate.split(" ", 2) + op = spec = None + db = tokens.pop(0) + if tokens: + op = tokens.pop(0) + if tokens: + spec = tuple(int(d) for d in tokens.pop(0).split(".")) + return SpecPredicate(db, op, spec, description=description) + elif util.callable(predicate): + return LambdaPredicate(predicate, description) + else: + assert False, "unknown predicate type: %s" % predicate + + def _format_description(self, config, negate=False): + bool_ = self(config) + if negate: + bool_ = not negate + return self.description % { + "driver": config.db.url.get_driver_name() + if config else "<no driver>", + "database": config.db.url.get_backend_name() + if config else "<no database>", + "doesnt_support": "doesn't support" if bool_ else "does support", + "does_support": "does support" if bool_ else "doesn't support" + } + + def _as_string(self, config=None, negate=False): + raise NotImplementedError() + + +class BooleanPredicate(Predicate): + def __init__(self, value, description=None): + self.value = value + self.description = description or "boolean %s" % value + + def __call__(self, config): + return self.value + + def _as_string(self, config, negate=False): + return self._format_description(config, negate=negate) + + +class SpecPredicate(Predicate): + def __init__(self, db, op=None, spec=None, description=None): + self.db = db + self.op = op + self.spec = spec + self.description = description + + _ops = { + '<': operator.lt, + '>': operator.gt, + '==': operator.eq, + '!=': operator.ne, + '<=': operator.le, + '>=': operator.ge, + 'in': operator.contains, + 'between': lambda val, pair: val >= pair[0] and val <= pair[1], + } + + def __call__(self, config): + engine = config.db + + if "+" in self.db: + dialect, driver = self.db.split('+') + else: + dialect, driver = self.db, None + + if dialect and engine.name != dialect: + return False + if driver is not None and engine.driver != driver: + return False + + if self.op is not None: + assert driver is None, "DBAPI version specs not supported yet" + + version = _server_version(engine) + oper = hasattr(self.op, '__call__') and self.op \ + or self._ops[self.op] + return oper(version, self.spec) + else: + return True + + def _as_string(self, config, negate=False): + if self.description is not None: + return self._format_description(config) + elif self.op is None: + if negate: + return "not %s" % self.db + else: + return "%s" % self.db + else: + if negate: + return "not %s %s %s" % ( + self.db, + self.op, + self.spec + ) + else: + return "%s %s %s" % ( + self.db, + self.op, + self.spec + ) + + +class LambdaPredicate(Predicate): + def __init__(self, lambda_, description=None, args=None, kw=None): + spec = inspect_getargspec(lambda_) + if not spec[0]: + self.lambda_ = lambda db: lambda_() + else: + self.lambda_ = lambda_ + self.args = args or () + self.kw = kw or {} + if description: + self.description = description + elif lambda_.__doc__: + self.description = lambda_.__doc__ + else: + self.description = "custom function" + + def __call__(self, config): + return self.lambda_(config) + + def _as_string(self, config, negate=False): + return self._format_description(config) + + +class NotPredicate(Predicate): + def __init__(self, predicate, description=None): + self.predicate = predicate + self.description = description + + def __call__(self, config): + return not self.predicate(config) + + def _as_string(self, config, negate=False): + if self.description: + return self._format_description(config, not negate) + else: + return self.predicate._as_string(config, not negate) + + +class OrPredicate(Predicate): + def __init__(self, predicates, description=None): + self.predicates = predicates + self.description = description + + def __call__(self, config): + for pred in self.predicates: + if pred(config): + return True + return False + + def _eval_str(self, config, negate=False): + if negate: + conjunction = " and " + else: + conjunction = " or " + return conjunction.join(p._as_string(config, negate=negate) + for p in self.predicates) + + def _negation_str(self, config): + if self.description is not None: + return "Not " + self._format_description(config) + else: + return self._eval_str(config, negate=True) + + def _as_string(self, config, negate=False): + if negate: + return self._negation_str(config) + else: + if self.description is not None: + return self._format_description(config) + else: + return self._eval_str(config) + + +_as_predicate = Predicate.as_predicate + + +def _is_excluded(db, op, spec): + return SpecPredicate(db, op, spec)(config._current) + + +def _server_version(engine): + """Return a server_version_info tuple.""" + + # force metadata to be retrieved + conn = engine.connect() + version = getattr(engine.dialect, 'server_version_info', None) + if version is None: + version = () + conn.close() + return version + + +def db_spec(*dbs): + return OrPredicate( + [Predicate.as_predicate(db) for db in dbs] + ) + + +def open(): + return skip_if(BooleanPredicate(False, "mark as execute")) + + +def closed(): + return skip_if(BooleanPredicate(True, "marked as skip")) + + +def fails(reason=None): + return fails_if(BooleanPredicate(True, reason or "expected to fail")) + + +@decorator +def future(fn, *arg): + return fails_if(LambdaPredicate(fn), "Future feature") + + +def fails_on(db, reason=None): + return fails_if(db, reason) + + +def fails_on_everything_except(*dbs): + return succeeds_if( + OrPredicate([ + Predicate.as_predicate(db) for db in dbs + ]) + ) + + +def skip(db, reason=None): + return skip_if(db, reason) + + +def only_on(dbs, reason=None): + return only_if( + OrPredicate([Predicate.as_predicate(db, reason) + for db in util.to_list(dbs)]) + ) + + +def exclude(db, op, spec, reason=None): + return skip_if(SpecPredicate(db, op, spec), reason) + + +def against(config, *queries): + assert queries, "no queries sent!" + return OrPredicate([ + Predicate.as_predicate(query) + for query in queries + ])(config) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/fixtures.py b/venv/Lib/site-packages/sqlalchemy/testing/fixtures.py new file mode 100644 index 0000000..dd0fa5a --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/fixtures.py @@ -0,0 +1,386 @@ +# testing/fixtures.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from . import config +from . import assertions, schema +from .util import adict +from .. import util +from .engines import drop_all_tables +from .entities import BasicEntity, ComparableEntity +import sys +import sqlalchemy as sa +from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta + +# whether or not we use unittest changes things dramatically, +# as far as how py.test collection works. + + +class TestBase(object): + # A sequence of database names to always run, regardless of the + # constraints below. + __whitelist__ = () + + # A sequence of requirement names matching testing.requires decorators + __requires__ = () + + # A sequence of dialect names to exclude from the test class. + __unsupported_on__ = () + + # If present, test class is only runnable for the *single* specified + # dialect. If you need multiple, use __unsupported_on__ and invert. + __only_on__ = None + + # A sequence of no-arg callables. If any are True, the entire testcase is + # skipped. + __skip_if__ = None + + def assert_(self, val, msg=None): + assert val, msg + + # apparently a handful of tests are doing this....OK + def setup(self): + if hasattr(self, "setUp"): + self.setUp() + + def teardown(self): + if hasattr(self, "tearDown"): + self.tearDown() + + +class TablesTest(TestBase): + + # 'once', None + run_setup_bind = 'once' + + # 'once', 'each', None + run_define_tables = 'once' + + # 'once', 'each', None + run_create_tables = 'once' + + # 'once', 'each', None + run_inserts = 'each' + + # 'each', None + run_deletes = 'each' + + # 'once', None + run_dispose_bind = None + + bind = None + metadata = None + tables = None + other = None + + @classmethod + def setup_class(cls): + cls._init_class() + + cls._setup_once_tables() + + cls._setup_once_inserts() + + @classmethod + def _init_class(cls): + if cls.run_define_tables == 'each': + if cls.run_create_tables == 'once': + cls.run_create_tables = 'each' + assert cls.run_inserts in ('each', None) + + cls.other = adict() + cls.tables = adict() + + cls.bind = cls.setup_bind() + cls.metadata = sa.MetaData() + cls.metadata.bind = cls.bind + + @classmethod + def _setup_once_inserts(cls): + if cls.run_inserts == 'once': + cls._load_fixtures() + cls.insert_data() + + @classmethod + def _setup_once_tables(cls): + if cls.run_define_tables == 'once': + cls.define_tables(cls.metadata) + if cls.run_create_tables == 'once': + cls.metadata.create_all(cls.bind) + cls.tables.update(cls.metadata.tables) + + def _setup_each_tables(self): + if self.run_define_tables == 'each': + self.tables.clear() + if self.run_create_tables == 'each': + drop_all_tables(self.metadata, self.bind) + self.metadata.clear() + self.define_tables(self.metadata) + if self.run_create_tables == 'each': + self.metadata.create_all(self.bind) + self.tables.update(self.metadata.tables) + elif self.run_create_tables == 'each': + drop_all_tables(self.metadata, self.bind) + self.metadata.create_all(self.bind) + + def _setup_each_inserts(self): + if self.run_inserts == 'each': + self._load_fixtures() + self.insert_data() + + def _teardown_each_tables(self): + # no need to run deletes if tables are recreated on setup + if self.run_define_tables != 'each' and self.run_deletes == 'each': + with self.bind.connect() as conn: + for table in reversed(self.metadata.sorted_tables): + try: + conn.execute(table.delete()) + except sa.exc.DBAPIError as ex: + util.print_( + ("Error emptying table %s: %r" % (table, ex)), + file=sys.stderr) + + def setup(self): + self._setup_each_tables() + self._setup_each_inserts() + + def teardown(self): + self._teardown_each_tables() + + @classmethod + def _teardown_once_metadata_bind(cls): + if cls.run_create_tables: + drop_all_tables(cls.metadata, cls.bind) + + if cls.run_dispose_bind == 'once': + cls.dispose_bind(cls.bind) + + cls.metadata.bind = None + + if cls.run_setup_bind is not None: + cls.bind = None + + @classmethod + def teardown_class(cls): + cls._teardown_once_metadata_bind() + + @classmethod + def setup_bind(cls): + return config.db + + @classmethod + def dispose_bind(cls, bind): + if hasattr(bind, 'dispose'): + bind.dispose() + elif hasattr(bind, 'close'): + bind.close() + + @classmethod + def define_tables(cls, metadata): + pass + + @classmethod + def fixtures(cls): + return {} + + @classmethod + def insert_data(cls): + pass + + def sql_count_(self, count, fn): + self.assert_sql_count(self.bind, fn, count) + + def sql_eq_(self, callable_, statements): + self.assert_sql(self.bind, callable_, statements) + + @classmethod + def _load_fixtures(cls): + """Insert rows as represented by the fixtures() method.""" + headers, rows = {}, {} + for table, data in cls.fixtures().items(): + if len(data) < 2: + continue + if isinstance(table, util.string_types): + table = cls.tables[table] + headers[table] = data[0] + rows[table] = data[1:] + for table in cls.metadata.sorted_tables: + if table not in headers: + continue + cls.bind.execute( + table.insert(), + [dict(zip(headers[table], column_values)) + for column_values in rows[table]]) + +from sqlalchemy import event + + +class RemovesEvents(object): + @util.memoized_property + def _event_fns(self): + return set() + + def event_listen(self, target, name, fn, **kw): + self._event_fns.add((target, name, fn)) + event.listen(target, name, fn, **kw) + + def teardown(self): + for key in self._event_fns: + event.remove(*key) + super_ = super(RemovesEvents, self) + if hasattr(super_, "teardown"): + super_.teardown() + + +class _ORMTest(object): + + @classmethod + def teardown_class(cls): + sa.orm.session.Session.close_all() + sa.orm.clear_mappers() + + +class ORMTest(_ORMTest, TestBase): + pass + + +class MappedTest(_ORMTest, TablesTest, assertions.AssertsExecutionResults): + # 'once', 'each', None + run_setup_classes = 'once' + + # 'once', 'each', None + run_setup_mappers = 'each' + + classes = None + + @classmethod + def setup_class(cls): + cls._init_class() + + if cls.classes is None: + cls.classes = adict() + + cls._setup_once_tables() + cls._setup_once_classes() + cls._setup_once_mappers() + cls._setup_once_inserts() + + @classmethod + def teardown_class(cls): + cls._teardown_once_class() + cls._teardown_once_metadata_bind() + + def setup(self): + self._setup_each_tables() + self._setup_each_classes() + self._setup_each_mappers() + self._setup_each_inserts() + + def teardown(self): + sa.orm.session.Session.close_all() + self._teardown_each_mappers() + self._teardown_each_classes() + self._teardown_each_tables() + + @classmethod + def _teardown_once_class(cls): + cls.classes.clear() + _ORMTest.teardown_class() + + @classmethod + def _setup_once_classes(cls): + if cls.run_setup_classes == 'once': + cls._with_register_classes(cls.setup_classes) + + @classmethod + def _setup_once_mappers(cls): + if cls.run_setup_mappers == 'once': + cls._with_register_classes(cls.setup_mappers) + + def _setup_each_mappers(self): + if self.run_setup_mappers == 'each': + self._with_register_classes(self.setup_mappers) + + def _setup_each_classes(self): + if self.run_setup_classes == 'each': + self._with_register_classes(self.setup_classes) + + @classmethod + def _with_register_classes(cls, fn): + """Run a setup method, framing the operation with a Base class + that will catch new subclasses to be established within + the "classes" registry. + + """ + cls_registry = cls.classes + + class FindFixture(type): + def __init__(cls, classname, bases, dict_): + cls_registry[classname] = cls + return type.__init__(cls, classname, bases, dict_) + + class _Base(util.with_metaclass(FindFixture, object)): + pass + + class Basic(BasicEntity, _Base): + pass + + class Comparable(ComparableEntity, _Base): + pass + + cls.Basic = Basic + cls.Comparable = Comparable + fn() + + def _teardown_each_mappers(self): + # some tests create mappers in the test bodies + # and will define setup_mappers as None - + # clear mappers in any case + if self.run_setup_mappers != 'once': + sa.orm.clear_mappers() + + def _teardown_each_classes(self): + if self.run_setup_classes != 'once': + self.classes.clear() + + @classmethod + def setup_classes(cls): + pass + + @classmethod + def setup_mappers(cls): + pass + + +class DeclarativeMappedTest(MappedTest): + run_setup_classes = 'once' + run_setup_mappers = 'once' + + @classmethod + def _setup_once_tables(cls): + pass + + @classmethod + def _with_register_classes(cls, fn): + cls_registry = cls.classes + + class FindFixtureDeclarative(DeclarativeMeta): + def __init__(cls, classname, bases, dict_): + cls_registry[classname] = cls + return DeclarativeMeta.__init__( + cls, classname, bases, dict_) + + class DeclarativeBasic(object): + __table_cls__ = schema.Table + + _DeclBase = declarative_base(metadata=cls.metadata, + metaclass=FindFixtureDeclarative, + cls=DeclarativeBasic) + cls.DeclarativeBasic = _DeclBase + fn() + + if cls.metadata.tables and cls.run_create_tables: + cls.metadata.create_all(config.db) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/mock.py b/venv/Lib/site-packages/sqlalchemy/testing/mock.py new file mode 100644 index 0000000..ea0a8da --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/mock.py @@ -0,0 +1,21 @@ +# testing/mock.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Import stub for mock library. +""" +from __future__ import absolute_import +from ..util import py33 + +if py33: + from unittest.mock import MagicMock, Mock, call, patch, ANY +else: + try: + from mock import MagicMock, Mock, call, patch, ANY + except ImportError: + raise ImportError( + "SQLAlchemy's test suite requires the " + "'mock' library as of 0.8.2.") diff --git a/venv/Lib/site-packages/sqlalchemy/testing/pickleable.py b/venv/Lib/site-packages/sqlalchemy/testing/pickleable.py new file mode 100644 index 0000000..087fc1f --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/pickleable.py @@ -0,0 +1,143 @@ +# testing/pickleable.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Classes used in pickling tests, need to be at the module level for +unpickling. +""" + +from . import fixtures + + +class User(fixtures.ComparableEntity): + pass + + +class Order(fixtures.ComparableEntity): + pass + + +class Dingaling(fixtures.ComparableEntity): + pass + + +class EmailUser(User): + pass + + +class Address(fixtures.ComparableEntity): + pass + + +# TODO: these are kind of arbitrary.... +class Child1(fixtures.ComparableEntity): + pass + + +class Child2(fixtures.ComparableEntity): + pass + + +class Parent(fixtures.ComparableEntity): + pass + + +class Screen(object): + + def __init__(self, obj, parent=None): + self.obj = obj + self.parent = parent + + +class Foo(object): + + def __init__(self, moredata): + self.data = 'im data' + self.stuff = 'im stuff' + self.moredata = moredata + + __hash__ = object.__hash__ + + def __eq__(self, other): + return other.data == self.data and \ + other.stuff == self.stuff and \ + other.moredata == self.moredata + + +class Bar(object): + + def __init__(self, x, y): + self.x = x + self.y = y + + __hash__ = object.__hash__ + + def __eq__(self, other): + return other.__class__ is self.__class__ and \ + other.x == self.x and \ + other.y == self.y + + def __str__(self): + return "Bar(%d, %d)" % (self.x, self.y) + + +class OldSchool: + + def __init__(self, x, y): + self.x = x + self.y = y + + def __eq__(self, other): + return other.__class__ is self.__class__ and \ + other.x == self.x and \ + other.y == self.y + + +class OldSchoolWithoutCompare: + + def __init__(self, x, y): + self.x = x + self.y = y + + +class BarWithoutCompare(object): + + def __init__(self, x, y): + self.x = x + self.y = y + + def __str__(self): + return "Bar(%d, %d)" % (self.x, self.y) + + +class NotComparable(object): + + def __init__(self, data): + self.data = data + + def __hash__(self): + return id(self) + + def __eq__(self, other): + return NotImplemented + + def __ne__(self, other): + return NotImplemented + + +class BrokenComparable(object): + + def __init__(self, data): + self.data = data + + def __hash__(self): + return id(self) + + def __eq__(self, other): + raise NotImplementedError + + def __ne__(self, other): + raise NotImplementedError diff --git a/venv/Lib/site-packages/sqlalchemy/testing/plugin/__init__.py b/venv/Lib/site-packages/sqlalchemy/testing/plugin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/sqlalchemy/testing/plugin/bootstrap.py b/venv/Lib/site-packages/sqlalchemy/testing/plugin/bootstrap.py new file mode 100644 index 0000000..497fcb7 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/plugin/bootstrap.py @@ -0,0 +1,44 @@ +""" +Bootstrapper for nose/pytest plugins. + +The entire rationale for this system is to get the modules in plugin/ +imported without importing all of the supporting library, so that we can +set up things for testing before coverage starts. + +The rationale for all of plugin/ being *in* the supporting library in the +first place is so that the testing and plugin suite is available to other +libraries, mainly external SQLAlchemy and Alembic dialects, to make use +of the same test environment and standard suites available to +SQLAlchemy/Alembic themselves without the need to ship/install a separate +package outside of SQLAlchemy. + +NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; +this should be removable when Alembic targets SQLAlchemy 1.0.0. + +""" + +import os +import sys + +bootstrap_file = locals()['bootstrap_file'] +to_bootstrap = locals()['to_bootstrap'] + + +def load_file_as_module(name): + path = os.path.join(os.path.dirname(bootstrap_file), "%s.py" % name) + if sys.version_info >= (3, 3): + from importlib import machinery + mod = machinery.SourceFileLoader(name, path).load_module() + else: + import imp + mod = imp.load_source(name, path) + return mod + +if to_bootstrap == "pytest": + sys.modules["sqla_plugin_base"] = load_file_as_module("plugin_base") + sys.modules["sqla_pytestplugin"] = load_file_as_module("pytestplugin") +elif to_bootstrap == "nose": + sys.modules["sqla_plugin_base"] = load_file_as_module("plugin_base") + sys.modules["sqla_noseplugin"] = load_file_as_module("noseplugin") +else: + raise Exception("unknown bootstrap: %s" % to_bootstrap) # noqa diff --git a/venv/Lib/site-packages/sqlalchemy/testing/plugin/noseplugin.py b/venv/Lib/site-packages/sqlalchemy/testing/plugin/noseplugin.py new file mode 100644 index 0000000..20ea61d --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/plugin/noseplugin.py @@ -0,0 +1,107 @@ +# plugin/noseplugin.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Enhance nose with extra options and behaviors for running SQLAlchemy tests. + +Must be run via ./sqla_nose.py so that it is imported in the expected +way (e.g. as a package-less import). + +""" + +try: + # installed by bootstrap.py + import sqla_plugin_base as plugin_base +except ImportError: + # assume we're a package, use traditional import + from . import plugin_base + + +import os +import sys + +from nose.plugins import Plugin +import nose +fixtures = None + +py3k = sys.version_info >= (3, 0) + + +class NoseSQLAlchemy(Plugin): + enabled = True + + name = 'sqla_testing' + score = 100 + + def options(self, parser, env=os.environ): + Plugin.options(self, parser, env) + opt = parser.add_option + + def make_option(name, **kw): + callback_ = kw.pop("callback", None) or kw.pop("zeroarg_callback", None) + if callback_: + def wrap_(option, opt_str, value, parser): + callback_(opt_str, value, parser) + kw["callback"] = wrap_ + opt(name, **kw) + + plugin_base.setup_options(make_option) + plugin_base.read_config() + + def configure(self, options, conf): + super(NoseSQLAlchemy, self).configure(options, conf) + plugin_base.pre_begin(options) + + plugin_base.set_coverage_flag(options.enable_plugin_coverage) + + plugin_base.set_skip_test(nose.SkipTest) + + def begin(self): + global fixtures + from sqlalchemy.testing import fixtures # noqa + + plugin_base.post_begin() + + def describeTest(self, test): + return "" + + def wantFunction(self, fn): + return False + + def wantMethod(self, fn): + if py3k: + if not hasattr(fn.__self__, 'cls'): + return False + cls = fn.__self__.cls + else: + cls = fn.im_class + return plugin_base.want_method(cls, fn) + + def wantClass(self, cls): + return plugin_base.want_class(cls) + + def beforeTest(self, test): + if not hasattr(test.test, 'cls'): + return + plugin_base.before_test( + test, + test.test.cls.__module__, + test.test.cls, test.test.method.__name__) + + def afterTest(self, test): + plugin_base.after_test(test) + + def startContext(self, ctx): + if not isinstance(ctx, type) \ + or not issubclass(ctx, fixtures.TestBase): + return + plugin_base.start_test_class(ctx) + + def stopContext(self, ctx): + if not isinstance(ctx, type) \ + or not issubclass(ctx, fixtures.TestBase): + return + plugin_base.stop_test_class(ctx) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/plugin/plugin_base.py b/venv/Lib/site-packages/sqlalchemy/testing/plugin/plugin_base.py new file mode 100644 index 0000000..8939ff7 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/plugin/plugin_base.py @@ -0,0 +1,586 @@ +# plugin/plugin_base.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Testing extensions. + +this module is designed to work as a testing-framework-agnostic library, +so that we can continue to support nose and also begin adding new +functionality via py.test. + +""" + +from __future__ import absolute_import + +import sys +import re + +py3k = sys.version_info >= (3, 0) + +if py3k: + import configparser +else: + import ConfigParser as configparser + +# late imports +fixtures = None +engines = None +exclusions = None +warnings = None +profiling = None +assertions = None +requirements = None +config = None +testing = None +util = None +file_config = None + + +logging = None +include_tags = set() +exclude_tags = set() +options = None + + +def setup_options(make_option): + make_option("--log-info", action="callback", type="string", callback=_log, + help="turn on info logging for <LOG> (multiple OK)") + make_option("--log-debug", action="callback", + type="string", callback=_log, + help="turn on debug logging for <LOG> (multiple OK)") + make_option("--db", action="append", type="string", dest="db", + help="Use prefab database uri. Multiple OK, " + "first one is run by default.") + make_option('--dbs', action='callback', zeroarg_callback=_list_dbs, + help="List available prefab dbs") + make_option("--dburi", action="append", type="string", dest="dburi", + help="Database uri. Multiple OK, " + "first one is run by default.") + make_option("--dropfirst", action="store_true", dest="dropfirst", + help="Drop all tables in the target database first") + make_option("--backend-only", action="store_true", dest="backend_only", + help="Run only tests marked with __backend__") + make_option("--nomemory", action="store_true", dest="nomemory", + help="Don't run memory profiling tests") + make_option("--postgresql-templatedb", type="string", + help="name of template database to use for Postgresql " + "CREATE DATABASE (defaults to current database)") + make_option("--low-connections", action="store_true", + dest="low_connections", + help="Use a low number of distinct connections - " + "i.e. for Oracle TNS") + make_option("--write-idents", type="string", dest="write_idents", + help="write out generated follower idents to <file>, " + "when -n<num> is used") + make_option("--reversetop", action="store_true", + dest="reversetop", default=False, + help="Use a random-ordering set implementation in the ORM " + "(helps reveal dependency issues)") + make_option("--requirements", action="callback", type="string", + callback=_requirements_opt, + help="requirements class for testing, overrides setup.cfg") + make_option("--with-cdecimal", action="store_true", + dest="cdecimal", default=False, + help="Monkeypatch the cdecimal library into Python 'decimal' " + "for all tests") + make_option("--include-tag", action="callback", callback=_include_tag, + type="string", + help="Include tests with tag <tag>") + make_option("--exclude-tag", action="callback", callback=_exclude_tag, + type="string", + help="Exclude tests with tag <tag>") + make_option("--write-profiles", action="store_true", + dest="write_profiles", default=False, + help="Write/update failing profiling data.") + make_option("--force-write-profiles", action="store_true", + dest="force_write_profiles", default=False, + help="Unconditionally write/update profiling data.") + + +def configure_follower(follower_ident): + """Configure required state for a follower. + + This invokes in the parent process and typically includes + database creation. + + """ + from sqlalchemy.testing import provision + provision.FOLLOWER_IDENT = follower_ident + + +def memoize_important_follower_config(dict_): + """Store important configuration we will need to send to a follower. + + This invokes in the parent process after normal config is set up. + + This is necessary as py.test seems to not be using forking, so we + start with nothing in memory, *but* it isn't running our argparse + callables, so we have to just copy all of that over. + + """ + dict_['memoized_config'] = { + 'include_tags': include_tags, + 'exclude_tags': exclude_tags + } + + +def restore_important_follower_config(dict_): + """Restore important configuration needed by a follower. + + This invokes in the follower process. + + """ + global include_tags, exclude_tags + include_tags.update(dict_['memoized_config']['include_tags']) + exclude_tags.update(dict_['memoized_config']['exclude_tags']) + + +def read_config(): + global file_config + file_config = configparser.ConfigParser() + file_config.read(['setup.cfg', 'test.cfg']) + + +def pre_begin(opt): + """things to set up early, before coverage might be setup.""" + global options + options = opt + for fn in pre_configure: + fn(options, file_config) + + +def set_coverage_flag(value): + options.has_coverage = value + +_skip_test_exception = None + + +def set_skip_test(exc): + global _skip_test_exception + _skip_test_exception = exc + + +def post_begin(): + """things to set up later, once we know coverage is running.""" + # Lazy setup of other options (post coverage) + for fn in post_configure: + fn(options, file_config) + + # late imports, has to happen after config as well + # as nose plugins like coverage + global util, fixtures, engines, exclusions, \ + assertions, warnings, profiling,\ + config, testing + from sqlalchemy import testing # noqa + from sqlalchemy.testing import fixtures, engines, exclusions # noqa + from sqlalchemy.testing import assertions, warnings, profiling # noqa + from sqlalchemy.testing import config # noqa + from sqlalchemy import util # noqa + warnings.setup_filters() + + + +def _log(opt_str, value, parser): + global logging + if not logging: + import logging + logging.basicConfig() + + if opt_str.endswith('-info'): + logging.getLogger(value).setLevel(logging.INFO) + elif opt_str.endswith('-debug'): + logging.getLogger(value).setLevel(logging.DEBUG) + + +def _list_dbs(*args): + print("Available --db options (use --dburi to override)") + for macro in sorted(file_config.options('db')): + print("%20s\t%s" % (macro, file_config.get('db', macro))) + sys.exit(0) + + +def _requirements_opt(opt_str, value, parser): + _setup_requirements(value) + + +def _exclude_tag(opt_str, value, parser): + exclude_tags.add(value.replace('-', '_')) + + +def _include_tag(opt_str, value, parser): + include_tags.add(value.replace('-', '_')) + +pre_configure = [] +post_configure = [] + + +def pre(fn): + pre_configure.append(fn) + return fn + + +def post(fn): + post_configure.append(fn) + return fn + + +@pre +def _setup_options(opt, file_config): + global options + options = opt + + +@pre +def _set_nomemory(opt, file_config): + if opt.nomemory: + exclude_tags.add("memory_intensive") + + +@pre +def _monkeypatch_cdecimal(options, file_config): + if options.cdecimal: + import cdecimal + sys.modules['decimal'] = cdecimal + + +@post +def _init_skiptest(options, file_config): + from sqlalchemy.testing import config + + config._skip_test_exception = _skip_test_exception + + +@post +def _engine_uri(options, file_config): + from sqlalchemy.testing import config + from sqlalchemy import testing + from sqlalchemy.testing import provision + + if options.dburi: + db_urls = list(options.dburi) + else: + db_urls = [] + + if options.db: + for db_token in options.db: + for db in re.split(r'[,\s]+', db_token): + if db not in file_config.options('db'): + raise RuntimeError( + "Unknown URI specifier '%s'. " + "Specify --dbs for known uris." + % db) + else: + db_urls.append(file_config.get('db', db)) + + if not db_urls: + db_urls.append(file_config.get('db', 'default')) + + config._current = None + for db_url in db_urls: + + if options.write_idents and provision.FOLLOWER_IDENT: # != 'master': + with open(options.write_idents, "a") as file_: + file_.write(provision.FOLLOWER_IDENT + " " + db_url + "\n") + + cfg = provision.setup_config( + db_url, options, file_config, provision.FOLLOWER_IDENT) + + if not config._current: + cfg.set_as_current(cfg, testing) + + +@post +def _requirements(options, file_config): + + requirement_cls = file_config.get('sqla_testing', "requirement_cls") + _setup_requirements(requirement_cls) + + +def _setup_requirements(argument): + from sqlalchemy.testing import config + from sqlalchemy import testing + + if config.requirements is not None: + return + + modname, clsname = argument.split(":") + + # importlib.import_module() only introduced in 2.7, a little + # late + mod = __import__(modname) + for component in modname.split(".")[1:]: + mod = getattr(mod, component) + req_cls = getattr(mod, clsname) + + config.requirements = testing.requires = req_cls() + + +@post +def _prep_testing_database(options, file_config): + from sqlalchemy.testing import config, util + from sqlalchemy.testing.exclusions import against + from sqlalchemy import schema, inspect + + if options.dropfirst: + for cfg in config.Config.all_configs(): + e = cfg.db + inspector = inspect(e) + try: + view_names = inspector.get_view_names() + except NotImplementedError: + pass + else: + for vname in view_names: + e.execute(schema._DropView( + schema.Table(vname, schema.MetaData()) + )) + + if config.requirements.schemas.enabled_for_config(cfg): + try: + view_names = inspector.get_view_names( + schema="test_schema") + except NotImplementedError: + pass + else: + for vname in view_names: + e.execute(schema._DropView( + schema.Table(vname, schema.MetaData(), + schema="test_schema") + )) + + util.drop_all_tables(e, inspector) + + if config.requirements.schemas.enabled_for_config(cfg): + util.drop_all_tables(e, inspector, schema=cfg.test_schema) + + if against(cfg, "postgresql"): + from sqlalchemy.dialects import postgresql + for enum in inspector.get_enums("*"): + e.execute(postgresql.DropEnumType( + postgresql.ENUM( + name=enum['name'], + schema=enum['schema']))) + + +@post +def _reverse_topological(options, file_config): + if options.reversetop: + from sqlalchemy.orm.util import randomize_unitofwork + randomize_unitofwork() + + +@post +def _post_setup_options(opt, file_config): + from sqlalchemy.testing import config + config.options = options + config.file_config = file_config + + +@post +def _setup_profiling(options, file_config): + from sqlalchemy.testing import profiling + profiling._profile_stats = profiling.ProfileStatsFile( + file_config.get('sqla_testing', 'profile_file')) + + +def want_class(cls): + if not issubclass(cls, fixtures.TestBase): + return False + elif cls.__name__.startswith('_'): + return False + elif config.options.backend_only and not getattr(cls, '__backend__', + False): + return False + else: + return True + + +def want_method(cls, fn): + if not fn.__name__.startswith("test_"): + return False + elif fn.__module__ is None: + return False + elif include_tags: + return ( + hasattr(cls, '__tags__') and + exclusions.tags(cls.__tags__).include_test( + include_tags, exclude_tags) + ) or ( + hasattr(fn, '_sa_exclusion_extend') and + fn._sa_exclusion_extend.include_test( + include_tags, exclude_tags) + ) + elif exclude_tags and hasattr(cls, '__tags__'): + return exclusions.tags(cls.__tags__).include_test( + include_tags, exclude_tags) + elif exclude_tags and hasattr(fn, '_sa_exclusion_extend'): + return fn._sa_exclusion_extend.include_test(include_tags, exclude_tags) + else: + return True + + +def generate_sub_tests(cls, module): + if getattr(cls, '__backend__', False): + for cfg in _possible_configs_for_cls(cls): + orig_name = cls.__name__ + + # we can have special chars in these names except for the + # pytest junit plugin, which is tripped up by the brackets + # and periods, so sanitize + + alpha_name = re.sub('[_\[\]\.]+', '_', cfg.name) + alpha_name = re.sub('_+$', '', alpha_name) + name = "%s_%s" % (cls.__name__, alpha_name) + subcls = type( + name, + (cls, ), + { + "_sa_orig_cls_name": orig_name, + "__only_on_config__": cfg + } + ) + setattr(module, name, subcls) + yield subcls + else: + yield cls + + +def start_test_class(cls): + _do_skips(cls) + _setup_engine(cls) + + +def stop_test_class(cls): + #from sqlalchemy import inspect + #assert not inspect(testing.db).get_table_names() + engines.testing_reaper._stop_test_ctx() + try: + if not options.low_connections: + assertions.global_cleanup_assertions() + finally: + _restore_engine() + + +def _restore_engine(): + config._current.reset(testing) + + +def final_process_cleanup(): + engines.testing_reaper._stop_test_ctx_aggressive() + assertions.global_cleanup_assertions() + _restore_engine() + + +def _setup_engine(cls): + if getattr(cls, '__engine_options__', None): + eng = engines.testing_engine(options=cls.__engine_options__) + config._current.push_engine(eng, testing) + + +def before_test(test, test_module_name, test_class, test_name): + + # like a nose id, e.g.: + # "test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause" + + name = getattr(test_class, '_sa_orig_cls_name', test_class.__name__) + + id_ = "%s.%s.%s" % (test_module_name, name, test_name) + + profiling._current_test = id_ + + +def after_test(test): + engines.testing_reaper._after_test_ctx() + + +def _possible_configs_for_cls(cls, reasons=None): + all_configs = set(config.Config.all_configs()) + + if cls.__unsupported_on__: + spec = exclusions.db_spec(*cls.__unsupported_on__) + for config_obj in list(all_configs): + if spec(config_obj): + all_configs.remove(config_obj) + + if getattr(cls, '__only_on__', None): + spec = exclusions.db_spec(*util.to_list(cls.__only_on__)) + for config_obj in list(all_configs): + if not spec(config_obj): + all_configs.remove(config_obj) + + if getattr(cls, '__only_on_config__', None): + all_configs.intersection_update([cls.__only_on_config__]) + + if hasattr(cls, '__requires__'): + requirements = config.requirements + for config_obj in list(all_configs): + for requirement in cls.__requires__: + check = getattr(requirements, requirement) + + skip_reasons = check.matching_config_reasons(config_obj) + if skip_reasons: + all_configs.remove(config_obj) + if reasons is not None: + reasons.extend(skip_reasons) + break + + if hasattr(cls, '__prefer_requires__'): + non_preferred = set() + requirements = config.requirements + for config_obj in list(all_configs): + for requirement in cls.__prefer_requires__: + check = getattr(requirements, requirement) + + if not check.enabled_for_config(config_obj): + non_preferred.add(config_obj) + if all_configs.difference(non_preferred): + all_configs.difference_update(non_preferred) + + return all_configs + + +def _do_skips(cls): + reasons = [] + all_configs = _possible_configs_for_cls(cls, reasons) + + if getattr(cls, '__skip_if__', False): + for c in getattr(cls, '__skip_if__'): + if c(): + config.skip_test("'%s' skipped by %s" % ( + cls.__name__, c.__name__) + ) + + if not all_configs: + msg = "'%s' unsupported on any DB implementation %s%s" % ( + cls.__name__, + ", ".join( + "'%s(%s)+%s'" % ( + config_obj.db.name, + ".".join( + str(dig) for dig in + exclusions._server_version(config_obj.db)), + config_obj.db.driver + ) + for config_obj in config.Config.all_configs() + ), + ", ".join(reasons) + ) + config.skip_test(msg) + elif hasattr(cls, '__prefer_backends__'): + non_preferred = set() + spec = exclusions.db_spec(*util.to_list(cls.__prefer_backends__)) + for config_obj in all_configs: + if not spec(config_obj): + non_preferred.add(config_obj) + if all_configs.difference(non_preferred): + all_configs.difference_update(non_preferred) + + if config._current not in all_configs: + _setup_config(all_configs.pop(), cls) + + +def _setup_config(config_obj, ctx): + config._current.push(config_obj, testing) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/plugin/pytestplugin.py b/venv/Lib/site-packages/sqlalchemy/testing/plugin/pytestplugin.py new file mode 100644 index 0000000..da682ea --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/plugin/pytestplugin.py @@ -0,0 +1,210 @@ +try: + # installed by bootstrap.py + import sqla_plugin_base as plugin_base +except ImportError: + # assume we're a package, use traditional import + from . import plugin_base + +import pytest +import argparse +import inspect +import collections +import os + +try: + import xdist # noqa + has_xdist = True +except ImportError: + has_xdist = False + + +def pytest_addoption(parser): + group = parser.getgroup("sqlalchemy") + + def make_option(name, **kw): + callback_ = kw.pop("callback", None) + if callback_: + class CallableAction(argparse.Action): + def __call__(self, parser, namespace, + values, option_string=None): + callback_(option_string, values, parser) + kw["action"] = CallableAction + + zeroarg_callback = kw.pop("zeroarg_callback", None) + if zeroarg_callback: + class CallableAction(argparse.Action): + def __init__(self, option_strings, + dest, default=False, + required=False, help=None): + super(CallableAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=0, + const=True, + default=default, + required=required, + help=help) + + def __call__(self, parser, namespace, + values, option_string=None): + zeroarg_callback(option_string, values, parser) + kw["action"] = CallableAction + + group.addoption(name, **kw) + + plugin_base.setup_options(make_option) + plugin_base.read_config() + + +def pytest_configure(config): + if hasattr(config, "slaveinput"): + plugin_base.restore_important_follower_config(config.slaveinput) + plugin_base.configure_follower( + config.slaveinput["follower_ident"] + ) + else: + if config.option.write_idents and \ + os.path.exists(config.option.write_idents): + os.remove(config.option.write_idents) + + plugin_base.pre_begin(config.option) + + plugin_base.set_coverage_flag(bool(getattr(config.option, + "cov_source", False))) + + plugin_base.set_skip_test(pytest.skip.Exception) + + +def pytest_sessionstart(session): + plugin_base.post_begin() + + +def pytest_sessionfinish(session): + plugin_base.final_process_cleanup() + + +if has_xdist: + import uuid + + def pytest_configure_node(node): + # the master for each node fills slaveinput dictionary + # which pytest-xdist will transfer to the subprocess + + plugin_base.memoize_important_follower_config(node.slaveinput) + + node.slaveinput["follower_ident"] = "test_%s" % uuid.uuid4().hex[0:12] + from sqlalchemy.testing import provision + provision.create_follower_db(node.slaveinput["follower_ident"]) + + def pytest_testnodedown(node, error): + from sqlalchemy.testing import provision + provision.drop_follower_db(node.slaveinput["follower_ident"]) + + +def pytest_collection_modifyitems(session, config, items): + # look for all those classes that specify __backend__ and + # expand them out into per-database test cases. + + # this is much easier to do within pytest_pycollect_makeitem, however + # pytest is iterating through cls.__dict__ as makeitem is + # called which causes a "dictionary changed size" error on py3k. + # I'd submit a pullreq for them to turn it into a list first, but + # it's to suit the rather odd use case here which is that we are adding + # new classes to a module on the fly. + + rebuilt_items = collections.defaultdict(list) + items[:] = [ + item for item in + items if isinstance(item.parent, pytest.Instance) + and not item.parent.parent.name.startswith("_")] + test_classes = set(item.parent for item in items) + for test_class in test_classes: + for sub_cls in plugin_base.generate_sub_tests( + test_class.cls, test_class.parent.module): + if sub_cls is not test_class.cls: + list_ = rebuilt_items[test_class.cls] + + for inst in pytest.Class( + sub_cls.__name__, + parent=test_class.parent.parent).collect(): + list_.extend(inst.collect()) + + newitems = [] + for item in items: + if item.parent.cls in rebuilt_items: + newitems.extend(rebuilt_items[item.parent.cls]) + rebuilt_items[item.parent.cls][:] = [] + else: + newitems.append(item) + + # seems like the functions attached to a test class aren't sorted already? + # is that true and why's that? (when using unittest, they're sorted) + items[:] = sorted(newitems, key=lambda item: ( + item.parent.parent.parent.name, + item.parent.parent.name, + item.name + )) + + +def pytest_pycollect_makeitem(collector, name, obj): + if inspect.isclass(obj) and plugin_base.want_class(obj): + return pytest.Class(name, parent=collector) + elif inspect.isfunction(obj) and \ + isinstance(collector, pytest.Instance) and \ + plugin_base.want_method(collector.cls, obj): + return pytest.Function(name, parent=collector) + else: + return [] + +_current_class = None + + +def pytest_runtest_setup(item): + # here we seem to get called only based on what we collected + # in pytest_collection_modifyitems. So to do class-based stuff + # we have to tear that out. + global _current_class + + if not isinstance(item, pytest.Function): + return + + # ... so we're doing a little dance here to figure it out... + if _current_class is None: + class_setup(item.parent.parent) + _current_class = item.parent.parent + + # this is needed for the class-level, to ensure that the + # teardown runs after the class is completed with its own + # class-level teardown... + def finalize(): + global _current_class + class_teardown(item.parent.parent) + _current_class = None + item.parent.parent.addfinalizer(finalize) + + test_setup(item) + + +def pytest_runtest_teardown(item): + # ...but this works better as the hook here rather than + # using a finalizer, as the finalizer seems to get in the way + # of the test reporting failures correctly (you get a bunch of + # py.test assertion stuff instead) + test_teardown(item) + + +def test_setup(item): + plugin_base.before_test(item, item.parent.module.__name__, + item.parent.cls, item.name) + + +def test_teardown(item): + plugin_base.after_test(item) + + +def class_setup(item): + plugin_base.start_test_class(item.cls) + + +def class_teardown(item): + plugin_base.stop_test_class(item.cls) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/profiling.py b/venv/Lib/site-packages/sqlalchemy/testing/profiling.py new file mode 100644 index 0000000..42265f7 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/profiling.py @@ -0,0 +1,265 @@ +# testing/profiling.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Profiling support for unit and performance tests. + +These are special purpose profiling methods which operate +in a more fine-grained way than nose's profiling plugin. + +""" + +import os +import sys +from .util import gc_collect +from . import config +import pstats +import collections +import contextlib + +try: + import cProfile +except ImportError: + cProfile = None +from ..util import jython, pypy, win32, update_wrapper + +_current_test = None + +# ProfileStatsFile instance, set up in plugin_base +_profile_stats = None + + +class ProfileStatsFile(object): + """"Store per-platform/fn profiling results in a file. + + We're still targeting Py2.5, 2.4 on 0.7 with no dependencies, + so no json lib :( need to roll something silly + + """ + + def __init__(self, filename): + self.force_write = ( + config.options is not None and + config.options.force_write_profiles + ) + self.write = self.force_write or ( + config.options is not None and + config.options.write_profiles + ) + self.fname = os.path.abspath(filename) + self.short_fname = os.path.split(self.fname)[-1] + self.data = collections.defaultdict( + lambda: collections.defaultdict(dict)) + self._read() + if self.write: + # rewrite for the case where features changed, + # etc. + self._write() + + @property + def platform_key(self): + + dbapi_key = config.db.name + "_" + config.db.driver + + # keep it at 2.7, 3.1, 3.2, etc. for now. + py_version = '.'.join([str(v) for v in sys.version_info[0:2]]) + + platform_tokens = [py_version] + platform_tokens.append(dbapi_key) + if jython: + platform_tokens.append("jython") + if pypy: + platform_tokens.append("pypy") + if win32: + platform_tokens.append("win") + platform_tokens.append( + "nativeunicode" + if config.db.dialect.convert_unicode + else "dbapiunicode" + ) + _has_cext = config.requirements._has_cextensions() + platform_tokens.append(_has_cext and "cextensions" or "nocextensions") + return "_".join(platform_tokens) + + def has_stats(self): + test_key = _current_test + return ( + test_key in self.data and + self.platform_key in self.data[test_key] + ) + + def result(self, callcount): + test_key = _current_test + per_fn = self.data[test_key] + per_platform = per_fn[self.platform_key] + + if 'counts' not in per_platform: + per_platform['counts'] = counts = [] + else: + counts = per_platform['counts'] + + if 'current_count' not in per_platform: + per_platform['current_count'] = current_count = 0 + else: + current_count = per_platform['current_count'] + + has_count = len(counts) > current_count + + if not has_count: + counts.append(callcount) + if self.write: + self._write() + result = None + else: + result = per_platform['lineno'], counts[current_count] + per_platform['current_count'] += 1 + return result + + def replace(self, callcount): + test_key = _current_test + per_fn = self.data[test_key] + per_platform = per_fn[self.platform_key] + counts = per_platform['counts'] + current_count = per_platform['current_count'] + if current_count < len(counts): + counts[current_count - 1] = callcount + else: + counts[-1] = callcount + if self.write: + self._write() + + def _header(self): + return ( + "# %s\n" + "# This file is written out on a per-environment basis.\n" + "# For each test in aaa_profiling, the corresponding " + "function and \n" + "# environment is located within this file. " + "If it doesn't exist,\n" + "# the test is skipped.\n" + "# If a callcount does exist, it is compared " + "to what we received. \n" + "# assertions are raised if the counts do not match.\n" + "# \n" + "# To add a new callcount test, apply the function_call_count \n" + "# decorator and re-run the tests using the --write-profiles \n" + "# option - this file will be rewritten including the new count.\n" + "# \n" + ) % (self.fname) + + def _read(self): + try: + profile_f = open(self.fname) + except IOError: + return + for lineno, line in enumerate(profile_f): + line = line.strip() + if not line or line.startswith("#"): + continue + + test_key, platform_key, counts = line.split() + per_fn = self.data[test_key] + per_platform = per_fn[platform_key] + c = [int(count) for count in counts.split(",")] + per_platform['counts'] = c + per_platform['lineno'] = lineno + 1 + per_platform['current_count'] = 0 + profile_f.close() + + def _write(self): + print(("Writing profile file %s" % self.fname)) + profile_f = open(self.fname, "w") + profile_f.write(self._header()) + for test_key in sorted(self.data): + + per_fn = self.data[test_key] + profile_f.write("\n# TEST: %s\n\n" % test_key) + for platform_key in sorted(per_fn): + per_platform = per_fn[platform_key] + c = ",".join(str(count) for count in per_platform['counts']) + profile_f.write("%s %s %s\n" % (test_key, platform_key, c)) + profile_f.close() + + +def function_call_count(variance=0.05): + """Assert a target for a test case's function call count. + + The main purpose of this assertion is to detect changes in + callcounts for various functions - the actual number is not as important. + Callcounts are stored in a file keyed to Python version and OS platform + information. This file is generated automatically for new tests, + and versioned so that unexpected changes in callcounts will be detected. + + """ + + def decorate(fn): + def wrap(*args, **kw): + with count_functions(variance=variance): + return fn(*args, **kw) + return update_wrapper(wrap, fn) + return decorate + + +@contextlib.contextmanager +def count_functions(variance=0.05): + if cProfile is None: + raise SkipTest("cProfile is not installed") + + if not _profile_stats.has_stats() and not _profile_stats.write: + config.skip_test( + "No profiling stats available on this " + "platform for this function. Run tests with " + "--write-profiles to add statistics to %s for " + "this platform." % _profile_stats.short_fname) + + gc_collect() + + pr = cProfile.Profile() + pr.enable() + #began = time.time() + yield + #ended = time.time() + pr.disable() + + #s = compat.StringIO() + stats = pstats.Stats(pr, stream=sys.stdout) + + #timespent = ended - began + callcount = stats.total_calls + + expected = _profile_stats.result(callcount) + + if expected is None: + expected_count = None + else: + line_no, expected_count = expected + + print(("Pstats calls: %d Expected %s" % ( + callcount, + expected_count + ) + )) + stats.sort_stats("cumulative") + stats.print_stats() + + if expected_count: + deviance = int(callcount * variance) + failed = abs(callcount - expected_count) > deviance + + if failed or _profile_stats.force_write: + if _profile_stats.write: + _profile_stats.replace(callcount) + else: + raise AssertionError( + "Adjusted function call count %s not within %s%% " + "of expected %s, platform %s. Rerun with " + "--write-profiles to " + "regenerate this callcount." + % ( + callcount, (variance * 100), + expected_count, _profile_stats.platform_key)) + + diff --git a/venv/Lib/site-packages/sqlalchemy/testing/provision.py b/venv/Lib/site-packages/sqlalchemy/testing/provision.py new file mode 100644 index 0000000..4c749d3 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/provision.py @@ -0,0 +1,423 @@ +from sqlalchemy.engine import url as sa_url +from sqlalchemy import create_engine +from sqlalchemy import text +from sqlalchemy import exc +from sqlalchemy.util import compat +from . import config, engines +import collections +import os +import time +import logging +log = logging.getLogger(__name__) + +FOLLOWER_IDENT = None + + +class register(object): + def __init__(self): + self.fns = {} + + @classmethod + def init(cls, fn): + return register().for_db("*")(fn) + + def for_db(self, dbname): + def decorate(fn): + self.fns[dbname] = fn + return self + return decorate + + def __call__(self, cfg, *arg): + if isinstance(cfg, compat.string_types): + url = sa_url.make_url(cfg) + elif isinstance(cfg, sa_url.URL): + url = cfg + else: + url = cfg.db.url + backend = url.get_backend_name() + if backend in self.fns: + return self.fns[backend](cfg, *arg) + else: + return self.fns['*'](cfg, *arg) + + +def create_follower_db(follower_ident): + for cfg in _configs_for_db_operation(): + log.info("CREATE database %s, URI %r", follower_ident, cfg.db.url) + _create_db(cfg, cfg.db, follower_ident) + + +def configure_follower(follower_ident): + for cfg in config.Config.all_configs(): + _configure_follower(cfg, follower_ident) + + +def setup_config(db_url, options, file_config, follower_ident): + if follower_ident: + db_url = _follower_url_from_main(db_url, follower_ident) + db_opts = {} + _update_db_opts(db_url, db_opts) + eng = engines.testing_engine(db_url, db_opts) + _post_configure_engine(db_url, eng, follower_ident) + eng.connect().close() + + cfg = config.Config.register(eng, db_opts, options, file_config) + if follower_ident: + _configure_follower(cfg, follower_ident) + return cfg + + +def drop_follower_db(follower_ident): + for cfg in _configs_for_db_operation(): + log.info("DROP database %s, URI %r", follower_ident, cfg.db.url) + _drop_db(cfg, cfg.db, follower_ident) + + +def _configs_for_db_operation(): + hosts = set() + + for cfg in config.Config.all_configs(): + cfg.db.dispose() + + for cfg in config.Config.all_configs(): + url = cfg.db.url + backend = url.get_backend_name() + host_conf = ( + backend, + url.username, url.host, url.database) + + if host_conf not in hosts: + yield cfg + hosts.add(host_conf) + + for cfg in config.Config.all_configs(): + cfg.db.dispose() + + +@register.init +def _create_db(cfg, eng, ident): + raise NotImplementedError("no DB creation routine for cfg: %s" % eng.url) + + +@register.init +def _drop_db(cfg, eng, ident): + raise NotImplementedError("no DB drop routine for cfg: %s" % eng.url) + + +@register.init +def _update_db_opts(db_url, db_opts): + pass + + +@register.init +def _configure_follower(cfg, ident): + pass + + +@register.init +def _post_configure_engine(url, engine, follower_ident): + pass + + +@register.init +def _follower_url_from_main(url, ident): + url = sa_url.make_url(url) + url.database = ident + return url + + +@_update_db_opts.for_db("mssql") +def _mssql_update_db_opts(db_url, db_opts): + db_opts['legacy_schema_aliasing'] = False + + + +@_follower_url_from_main.for_db("sqlite") +def _sqlite_follower_url_from_main(url, ident): + url = sa_url.make_url(url) + if not url.database or url.database == ':memory:': + return url + else: + return sa_url.make_url("sqlite:///%s.db" % ident) + + +@_post_configure_engine.for_db("sqlite") +def _sqlite_post_configure_engine(url, engine, follower_ident): + from sqlalchemy import event + + @event.listens_for(engine, "connect") + def connect(dbapi_connection, connection_record): + # use file DBs in all cases, memory acts kind of strangely + # as an attached + if not follower_ident: + dbapi_connection.execute( + 'ATTACH DATABASE "test_schema.db" AS test_schema') + else: + dbapi_connection.execute( + 'ATTACH DATABASE "%s_test_schema.db" AS test_schema' + % follower_ident) + + +@_create_db.for_db("postgresql") +def _pg_create_db(cfg, eng, ident): + template_db = cfg.options.postgresql_templatedb + + with eng.connect().execution_options( + isolation_level="AUTOCOMMIT") as conn: + try: + _pg_drop_db(cfg, conn, ident) + except Exception: + pass + if not template_db: + template_db = conn.scalar("select current_database()") + for attempt in range(3): + try: + conn.execute( + "CREATE DATABASE %s TEMPLATE %s" % (ident, template_db)) + except exc.OperationalError as err: + if "accessed by other users" in str(err): + log.info( + "Waiting to create %s, URI %r, " + "template DB %s is in use sleeping for .5", + ident, eng.url, template_db) + time.sleep(.5) + else: + break + else: + raise err + + +@_create_db.for_db("mysql") +def _mysql_create_db(cfg, eng, ident): + with eng.connect() as conn: + try: + _mysql_drop_db(cfg, conn, ident) + except Exception: + pass + + # using utf8mb4 we are getting collation errors on UNIONS: + # test/orm/inheritance/test_polymorphic_rel.py" + # 1271, u"Illegal mix of collations for operation 'UNION'" + conn.execute("CREATE DATABASE %s CHARACTER SET utf8mb3" % ident) + conn.execute( + "CREATE DATABASE %s_test_schema CHARACTER SET utf8mb3" % ident) + conn.execute( + "CREATE DATABASE %s_test_schema_2 CHARACTER SET utf8mb3" % ident) + + +@_configure_follower.for_db("mysql") +def _mysql_configure_follower(config, ident): + config.test_schema = "%s_test_schema" % ident + config.test_schema_2 = "%s_test_schema_2" % ident + + +@_create_db.for_db("sqlite") +def _sqlite_create_db(cfg, eng, ident): + pass + + +@_drop_db.for_db("postgresql") +def _pg_drop_db(cfg, eng, ident): + with eng.connect().execution_options( + isolation_level="AUTOCOMMIT") as conn: + conn.execute( + text( + "select pg_terminate_backend(pid) from pg_stat_activity " + "where usename=current_user and pid != pg_backend_pid() " + "and datname=:dname" + ), dname=ident) + conn.execute("DROP DATABASE %s" % ident) + + +@_drop_db.for_db("sqlite") +def _sqlite_drop_db(cfg, eng, ident): + if ident: + os.remove("%s_test_schema.db" % ident) + else: + os.remove("%s.db" % ident) + + +@_drop_db.for_db("mysql") +def _mysql_drop_db(cfg, eng, ident): + with eng.connect() as conn: + conn.execute("DROP DATABASE %s_test_schema" % ident) + conn.execute("DROP DATABASE %s_test_schema_2" % ident) + conn.execute("DROP DATABASE %s" % ident) + + +@_create_db.for_db("oracle") +def _oracle_create_db(cfg, eng, ident): + # NOTE: make sure you've run "ALTER DATABASE default tablespace users" or + # similar, so that the default tablespace is not "system"; reflection will + # fail otherwise + with eng.connect() as conn: + conn.execute("create user %s identified by xe" % ident) + conn.execute("create user %s_ts1 identified by xe" % ident) + conn.execute("create user %s_ts2 identified by xe" % ident) + conn.execute("grant dba to %s" % (ident, )) + conn.execute("grant unlimited tablespace to %s" % ident) + conn.execute("grant unlimited tablespace to %s_ts1" % ident) + conn.execute("grant unlimited tablespace to %s_ts2" % ident) + +@_configure_follower.for_db("oracle") +def _oracle_configure_follower(config, ident): + config.test_schema = "%s_ts1" % ident + config.test_schema_2 = "%s_ts2" % ident + + +def _ora_drop_ignore(conn, dbname): + try: + conn.execute("drop user %s cascade" % dbname) + log.info("Reaped db: %s", dbname) + return True + except exc.DatabaseError as err: + log.warning("couldn't drop db: %s", err) + return False + + +@_drop_db.for_db("oracle") +def _oracle_drop_db(cfg, eng, ident): + with eng.connect() as conn: + # cx_Oracle seems to occasionally leak open connections when a large + # suite it run, even if we confirm we have zero references to + # connection objects. + # while there is a "kill session" command in Oracle, + # it unfortunately does not release the connection sufficiently. + _ora_drop_ignore(conn, ident) + _ora_drop_ignore(conn, "%s_ts1" % ident) + _ora_drop_ignore(conn, "%s_ts2" % ident) + + +@_update_db_opts.for_db("oracle") +def _oracle_update_db_opts(db_url, db_opts): + pass + + +def reap_dbs(idents_file): + log.info("Reaping databases...") + + urls = collections.defaultdict(set) + idents = collections.defaultdict(set) + + with open(idents_file) as file_: + for line in file_: + line = line.strip() + db_name, db_url = line.split(" ") + url_obj = sa_url.make_url(db_url) + url_key = (url_obj.get_backend_name(), url_obj.host) + urls[url_key].add(db_url) + idents[url_key].add(db_name) + + for url_key in urls: + backend = url_key[0] + url = list(urls[url_key])[0] + ident = idents[url_key] + if backend == "oracle": + _reap_oracle_dbs(url, ident) + elif backend == "mssql": + _reap_mssql_dbs(url, ident) + +def _reap_oracle_dbs(url, idents): + log.info("db reaper connecting to %r", url) + eng = create_engine(url) + with eng.connect() as conn: + + log.info("identifiers in file: %s", ", ".join(idents)) + + to_reap = conn.execute( + "select u.username from all_users u where username " + "like 'TEST_%' and not exists (select username " + "from v$session where username=u.username)") + all_names = {username.lower() for (username, ) in to_reap} + to_drop = set() + for name in all_names: + if name.endswith("_ts1") or name.endswith("_ts2"): + continue + elif name in idents: + to_drop.add(name) + if "%s_ts1" % name in all_names: + to_drop.add("%s_ts1" % name) + if "%s_ts2" % name in all_names: + to_drop.add("%s_ts2" % name) + + dropped = total = 0 + for total, username in enumerate(to_drop, 1): + if _ora_drop_ignore(conn, username): + dropped += 1 + log.info( + "Dropped %d out of %d stale databases detected", + dropped, total) + + + +@_follower_url_from_main.for_db("oracle") +def _oracle_follower_url_from_main(url, ident): + url = sa_url.make_url(url) + url.username = ident + url.password = 'xe' + return url + + +@_create_db.for_db("mssql") +def _mssql_create_db(cfg, eng, ident): + with eng.connect().execution_options( + isolation_level="AUTOCOMMIT") as conn: + conn.execute("create database %s" % ident) + conn.execute( + "ALTER DATABASE %s SET ALLOW_SNAPSHOT_ISOLATION ON" % ident) + conn.execute( + "ALTER DATABASE %s SET READ_COMMITTED_SNAPSHOT ON" % ident) + conn.execute("use %s" % ident) + conn.execute("create schema test_schema") + conn.execute("create schema test_schema_2") + + +@_drop_db.for_db("mssql") +def _mssql_drop_db(cfg, eng, ident): + with eng.connect().execution_options( + isolation_level="AUTOCOMMIT") as conn: + _mssql_drop_ignore(conn, ident) + +def _mssql_drop_ignore(conn, ident): + try: + # typically when this happens, we can't KILL the session anyway, + # so let the cleanup process drop the DBs + # for row in conn.execute("select session_id from sys.dm_exec_sessions " + # "where database_id=db_id('%s')" % ident): + # log.info("killing SQL server sesssion %s", row['session_id']) + # conn.execute("kill %s" % row['session_id']) + + conn.execute("drop database %s" % ident) + log.info("Reaped db: %s", ident) + return True + except exc.DatabaseError as err: + log.warning("couldn't drop db: %s", err) + return False + + +def _reap_mssql_dbs(url, idents): + log.info("db reaper connecting to %r", url) + eng = create_engine(url) + with eng.connect().execution_options( + isolation_level="AUTOCOMMIT") as conn: + + log.info("identifiers in file: %s", ", ".join(idents)) + + to_reap = conn.execute( + "select d.name from sys.databases as d where name " + "like 'TEST_%' and not exists (select session_id " + "from sys.dm_exec_sessions " + "where database_id=d.database_id)") + all_names = {dbname.lower() for (dbname, ) in to_reap} + to_drop = set() + for name in all_names: + if name in idents: + to_drop.add(name) + + dropped = total = 0 + for total, dbname in enumerate(to_drop, 1): + if _mssql_drop_ignore(conn, dbname): + dropped += 1 + log.info( + "Dropped %d out of %d stale databases detected", + dropped, total) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/replay_fixture.py b/venv/Lib/site-packages/sqlalchemy/testing/replay_fixture.py new file mode 100644 index 0000000..b50f52e --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/replay_fixture.py @@ -0,0 +1,172 @@ +from . import fixtures +from . import profiling +from .. import util +import types +from collections import deque +import contextlib +from . import config +from sqlalchemy import MetaData +from sqlalchemy import create_engine +from sqlalchemy.orm import Session + + +class ReplayFixtureTest(fixtures.TestBase): + + @contextlib.contextmanager + def _dummy_ctx(self, *arg, **kw): + yield + + def test_invocation(self): + + dbapi_session = ReplayableSession() + creator = config.db.pool._creator + recorder = lambda: dbapi_session.recorder(creator()) + engine = create_engine( + config.db.url, creator=recorder, + use_native_hstore=False) + self.metadata = MetaData(engine) + self.engine = engine + self.session = Session(engine) + + self.setup_engine() + try: + self._run_steps(ctx=self._dummy_ctx) + finally: + self.teardown_engine() + engine.dispose() + + player = lambda: dbapi_session.player() + engine = create_engine( + config.db.url, creator=player, + use_native_hstore=False) + + self.metadata = MetaData(engine) + self.engine = engine + self.session = Session(engine) + + self.setup_engine() + try: + self._run_steps(ctx=profiling.count_functions) + finally: + self.session.close() + engine.dispose() + + def setup_engine(self): + pass + + def teardown_engine(self): + pass + + def _run_steps(self, ctx): + raise NotImplementedError() + + +class ReplayableSession(object): + """A simple record/playback tool. + + This is *not* a mock testing class. It only records a session for later + playback and makes no assertions on call consistency whatsoever. It's + unlikely to be suitable for anything other than DB-API recording. + + """ + + Callable = object() + NoAttribute = object() + + if util.py2k: + Natives = set([getattr(types, t) + for t in dir(types) if not t.startswith('_')]).\ + difference([getattr(types, t) + for t in ('FunctionType', 'BuiltinFunctionType', + 'MethodType', 'BuiltinMethodType', + 'LambdaType', 'UnboundMethodType',)]) + else: + Natives = set([getattr(types, t) + for t in dir(types) if not t.startswith('_')]).\ + union([type(t) if not isinstance(t, type) + else t for t in __builtins__.values()]).\ + difference([getattr(types, t) + for t in ('FunctionType', 'BuiltinFunctionType', + 'MethodType', 'BuiltinMethodType', + 'LambdaType', )]) + + def __init__(self): + self.buffer = deque() + + def recorder(self, base): + return self.Recorder(self.buffer, base) + + def player(self): + return self.Player(self.buffer) + + class Recorder(object): + def __init__(self, buffer, subject): + self._buffer = buffer + self._subject = subject + + def __call__(self, *args, **kw): + subject, buffer = [object.__getattribute__(self, x) + for x in ('_subject', '_buffer')] + + result = subject(*args, **kw) + if type(result) not in ReplayableSession.Natives: + buffer.append(ReplayableSession.Callable) + return type(self)(buffer, result) + else: + buffer.append(result) + return result + + @property + def _sqla_unwrap(self): + return self._subject + + def __getattribute__(self, key): + try: + return object.__getattribute__(self, key) + except AttributeError: + pass + + subject, buffer = [object.__getattribute__(self, x) + for x in ('_subject', '_buffer')] + try: + result = type(subject).__getattribute__(subject, key) + except AttributeError: + buffer.append(ReplayableSession.NoAttribute) + raise + else: + if type(result) not in ReplayableSession.Natives: + buffer.append(ReplayableSession.Callable) + return type(self)(buffer, result) + else: + buffer.append(result) + return result + + class Player(object): + def __init__(self, buffer): + self._buffer = buffer + + def __call__(self, *args, **kw): + buffer = object.__getattribute__(self, '_buffer') + result = buffer.popleft() + if result is ReplayableSession.Callable: + return self + else: + return result + + @property + def _sqla_unwrap(self): + return None + + def __getattribute__(self, key): + try: + return object.__getattribute__(self, key) + except AttributeError: + pass + buffer = object.__getattribute__(self, '_buffer') + result = buffer.popleft() + if result is ReplayableSession.Callable: + return self + elif result is ReplayableSession.NoAttribute: + raise AttributeError(key) + else: + return result diff --git a/venv/Lib/site-packages/sqlalchemy/testing/requirements.py b/venv/Lib/site-packages/sqlalchemy/testing/requirements.py new file mode 100644 index 0000000..60c643c --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/requirements.py @@ -0,0 +1,924 @@ +# testing/requirements.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Global database feature support policy. + +Provides decorators to mark tests requiring specific feature support from the +target database. + +External dialect test suites should subclass SuiteRequirements +to provide specific inclusion/exclusions. + +""" + +import sys + +from . import exclusions +from .. import util + + +class Requirements(object): + pass + + +class SuiteRequirements(Requirements): + + @property + def create_table(self): + """target platform can emit basic CreateTable DDL.""" + + return exclusions.open() + + @property + def drop_table(self): + """target platform can emit basic DropTable DDL.""" + + return exclusions.open() + + @property + def foreign_keys(self): + """Target database must support foreign keys.""" + + return exclusions.open() + + @property + def on_update_cascade(self): + """"target database must support ON UPDATE..CASCADE behavior in + foreign keys.""" + + return exclusions.open() + + @property + def non_updating_cascade(self): + """target database must *not* support ON UPDATE..CASCADE behavior in + foreign keys.""" + return exclusions.closed() + + @property + def deferrable_fks(self): + return exclusions.closed() + + @property + def on_update_or_deferrable_fks(self): + # TODO: exclusions should be composable, + # somehow only_if([x, y]) isn't working here, negation/conjunctions + # getting confused. + return exclusions.only_if( + lambda: self.on_update_cascade.enabled or + self.deferrable_fks.enabled + ) + + @property + def self_referential_foreign_keys(self): + """Target database must support self-referential foreign keys.""" + + return exclusions.open() + + @property + def foreign_key_ddl(self): + """Target database must support the DDL phrases for FOREIGN KEY.""" + + return exclusions.open() + + @property + def named_constraints(self): + """target database must support names for constraints.""" + + return exclusions.open() + + @property + def subqueries(self): + """Target database must support subqueries.""" + + return exclusions.open() + + @property + def offset(self): + """target database can render OFFSET, or an equivalent, in a + SELECT. + """ + + return exclusions.open() + + @property + def bound_limit_offset(self): + """target database can render LIMIT and/or OFFSET using a bound + parameter + """ + + return exclusions.open() + + @property + def parens_in_union_contained_select_w_limit_offset(self): + """Target database must support parenthesized SELECT in UNION + when LIMIT/OFFSET is specifically present. + + E.g. (SELECT ...) UNION (SELECT ..) + + This is known to fail on SQLite. + + """ + return exclusions.open() + + @property + def parens_in_union_contained_select_wo_limit_offset(self): + """Target database must support parenthesized SELECT in UNION + when OFFSET/LIMIT is specifically not present. + + E.g. (SELECT ... LIMIT ..) UNION (SELECT .. OFFSET ..) + + This is known to fail on SQLite. It also fails on Oracle + because without LIMIT/OFFSET, there is currently no step that + creates an additional subquery. + + """ + return exclusions.open() + + @property + def boolean_col_expressions(self): + """Target database must support boolean expressions as columns""" + + return exclusions.closed() + + @property + def nullsordering(self): + """Target backends that support nulls ordering.""" + + return exclusions.closed() + + @property + def standalone_binds(self): + """target database/driver supports bound parameters as column expressions + without being in the context of a typed column. + + """ + return exclusions.closed() + + @property + def intersect(self): + """Target database must support INTERSECT or equivalent.""" + return exclusions.closed() + + @property + def except_(self): + """Target database must support EXCEPT or equivalent (i.e. MINUS).""" + return exclusions.closed() + + @property + def window_functions(self): + """Target database must support window functions.""" + return exclusions.closed() + + @property + def ctes(self): + """Target database supports CTEs""" + + return exclusions.closed() + + @property + def ctes_with_update_delete(self): + """target database supports CTES that ride on top of a normal UPDATE + or DELETE statement which refers to the CTE in a correlated subquery. + + """ + + return exclusions.closed() + + @property + def ctes_on_dml(self): + """target database supports CTES which consist of INSERT, UPDATE + or DELETE *within* the CTE, e.g. WITH x AS (UPDATE....)""" + + return exclusions.closed() + + @property + def autoincrement_insert(self): + """target platform generates new surrogate integer primary key values + when insert() is executed, excluding the pk column.""" + + return exclusions.open() + + @property + def fetch_rows_post_commit(self): + """target platform will allow cursor.fetchone() to proceed after a + COMMIT. + + Typically this refers to an INSERT statement with RETURNING which + is invoked within "autocommit". If the row can be returned + after the autocommit, then this rule can be open. + + """ + + return exclusions.open() + + @property + def group_by_complex_expression(self): + """target platform supports SQL expressions in GROUP BY + + e.g. + + SELECT x + y AS somelabel FROM table GROUP BY x + y + + """ + + return exclusions.open() + + @property + def sane_rowcount(self): + return exclusions.skip_if( + lambda config: not config.db.dialect.supports_sane_rowcount, + "driver doesn't support 'sane' rowcount" + ) + + @property + def sane_multi_rowcount(self): + return exclusions.fails_if( + lambda config: not config.db.dialect.supports_sane_multi_rowcount, + "driver %(driver)s %(doesnt_support)s 'sane' multi row count" + ) + + @property + def sane_rowcount_w_returning(self): + return exclusions.fails_if( + lambda config: + not config.db.dialect.supports_sane_rowcount_returning, + "driver doesn't support 'sane' rowcount when returning is on" + ) + + @property + def empty_inserts(self): + """target platform supports INSERT with no values, i.e. + INSERT DEFAULT VALUES or equivalent.""" + + return exclusions.only_if( + lambda config: config.db.dialect.supports_empty_insert or + config.db.dialect.supports_default_values, + "empty inserts not supported" + ) + + @property + def insert_from_select(self): + """target platform supports INSERT from a SELECT.""" + + return exclusions.open() + + @property + def returning(self): + """target platform supports RETURNING.""" + + return exclusions.only_if( + lambda config: config.db.dialect.implicit_returning, + "%(database)s %(does_support)s 'returning'" + ) + + @property + def tuple_in(self): + """Target platform supports the syntax + "(x, y) IN ((x1, y1), (x2, y2), ...)" + """ + + return exclusions.closed() + + @property + def duplicate_names_in_cursor_description(self): + """target platform supports a SELECT statement that has + the same name repeated more than once in the columns list.""" + + return exclusions.open() + + @property + def denormalized_names(self): + """Target database must have 'denormalized', i.e. + UPPERCASE as case insensitive names.""" + + return exclusions.skip_if( + lambda config: not config.db.dialect.requires_name_normalize, + "Backend does not require denormalized names." + ) + + @property + def multivalues_inserts(self): + """target database must support multiple VALUES clauses in an + INSERT statement.""" + + return exclusions.skip_if( + lambda config: not config.db.dialect.supports_multivalues_insert, + "Backend does not support multirow inserts." + ) + + @property + def implements_get_lastrowid(self): + """"target dialect implements the executioncontext.get_lastrowid() + method without reliance on RETURNING. + + """ + return exclusions.open() + + @property + def emulated_lastrowid(self): + """"target dialect retrieves cursor.lastrowid, or fetches + from a database-side function after an insert() construct executes, + within the get_lastrowid() method. + + Only dialects that "pre-execute", or need RETURNING to get last + inserted id, would return closed/fail/skip for this. + + """ + return exclusions.closed() + + @property + def dbapi_lastrowid(self): + """"target platform includes a 'lastrowid' accessor on the DBAPI + cursor object. + + """ + return exclusions.closed() + + @property + def views(self): + """Target database must support VIEWs.""" + + return exclusions.closed() + + @property + def schemas(self): + """Target database must support external schemas, and have one + named 'test_schema'.""" + + return exclusions.closed() + + @property + def server_side_cursors(self): + """Target dialect must support server side cursors.""" + + return exclusions.only_if([ + lambda config: config.db.dialect.supports_server_side_cursors + ], "no server side cursors support") + + @property + def sequences(self): + """Target database must support SEQUENCEs.""" + + return exclusions.only_if([ + lambda config: config.db.dialect.supports_sequences + ], "no sequence support") + + @property + def sequences_optional(self): + """Target database supports sequences, but also optionally + as a means of generating new PK values.""" + + return exclusions.only_if([ + lambda config: config.db.dialect.supports_sequences and + config.db.dialect.sequences_optional + ], "no sequence support, or sequences not optional") + + @property + def reflects_pk_names(self): + return exclusions.closed() + + @property + def table_reflection(self): + return exclusions.open() + + @property + def comment_reflection(self): + return exclusions.closed() + + @property + def view_column_reflection(self): + """target database must support retrieval of the columns in a view, + similarly to how a table is inspected. + + This does not include the full CREATE VIEW definition. + + """ + return self.views + + @property + def view_reflection(self): + """target database must support inspection of the full CREATE VIEW definition. + """ + return self.views + + @property + def schema_reflection(self): + return self.schemas + + @property + def primary_key_constraint_reflection(self): + return exclusions.open() + + @property + def foreign_key_constraint_reflection(self): + return exclusions.open() + + @property + def foreign_key_constraint_option_reflection_ondelete(self): + return exclusions.closed() + + @property + def foreign_key_constraint_option_reflection_onupdate(self): + return exclusions.closed() + + @property + def temp_table_reflection(self): + return exclusions.open() + + @property + def temp_table_names(self): + """target dialect supports listing of temporary table names""" + return exclusions.closed() + + @property + def temporary_tables(self): + """target database supports temporary tables""" + return exclusions.open() + + @property + def temporary_views(self): + """target database supports temporary views""" + return exclusions.closed() + + @property + def index_reflection(self): + return exclusions.open() + + @property + def unique_constraint_reflection(self): + """target dialect supports reflection of unique constraints""" + return exclusions.open() + + @property + def check_constraint_reflection(self): + """target dialect supports reflection of check constraints""" + return exclusions.closed() + + @property + def duplicate_key_raises_integrity_error(self): + """target dialect raises IntegrityError when reporting an INSERT + with a primary key violation. (hint: it should) + + """ + return exclusions.open() + + @property + def unbounded_varchar(self): + """Target database must support VARCHAR with no length""" + + return exclusions.open() + + @property + def unicode_data(self): + """Target database/dialect must support Python unicode objects with + non-ASCII characters represented, delivered as bound parameters + as well as in result rows. + + """ + return exclusions.open() + + @property + def unicode_ddl(self): + """Target driver must support some degree of non-ascii symbol + names. + """ + return exclusions.closed() + + @property + def datetime_literals(self): + """target dialect supports rendering of a date, time, or datetime as a + literal string, e.g. via the TypeEngine.literal_processor() method. + + """ + + return exclusions.closed() + + @property + def datetime(self): + """target dialect supports representation of Python + datetime.datetime() objects.""" + + return exclusions.open() + + @property + def datetime_microseconds(self): + """target dialect supports representation of Python + datetime.datetime() with microsecond objects.""" + + return exclusions.open() + + @property + def timestamp_microseconds(self): + """target dialect supports representation of Python + datetime.datetime() with microsecond objects but only + if TIMESTAMP is used.""" + return exclusions.closed() + + @property + def datetime_historic(self): + """target dialect supports representation of Python + datetime.datetime() objects with historic (pre 1970) values.""" + + return exclusions.closed() + + @property + def date(self): + """target dialect supports representation of Python + datetime.date() objects.""" + + return exclusions.open() + + @property + def date_coerces_from_datetime(self): + """target dialect accepts a datetime object as the target + of a date column.""" + + return exclusions.open() + + @property + def date_historic(self): + """target dialect supports representation of Python + datetime.datetime() objects with historic (pre 1970) values.""" + + return exclusions.closed() + + @property + def time(self): + """target dialect supports representation of Python + datetime.time() objects.""" + + return exclusions.open() + + @property + def time_microseconds(self): + """target dialect supports representation of Python + datetime.time() with microsecond objects.""" + + return exclusions.open() + + @property + def binary_comparisons(self): + """target database/driver can allow BLOB/BINARY fields to be compared + against a bound parameter value. + """ + + return exclusions.open() + + @property + def binary_literals(self): + """target backend supports simple binary literals, e.g. an + expression like:: + + SELECT CAST('foo' AS BINARY) + + Where ``BINARY`` is the type emitted from :class:`.LargeBinary`, + e.g. it could be ``BLOB`` or similar. + + Basically fails on Oracle. + + """ + + return exclusions.open() + + @property + def autocommit(self): + """target dialect supports 'AUTOCOMMIT' as an isolation_level""" + return exclusions.closed() + + @property + def json_type(self): + """target platform implements a native JSON type.""" + + return exclusions.closed() + + @property + def json_array_indexes(self): + """"target platform supports numeric array indexes + within a JSON structure""" + + return self.json_type + + @property + def precision_numerics_general(self): + """target backend has general support for moderately high-precision + numerics.""" + return exclusions.open() + + @property + def precision_numerics_enotation_small(self): + """target backend supports Decimal() objects using E notation + to represent very small values.""" + return exclusions.closed() + + @property + def precision_numerics_enotation_large(self): + """target backend supports Decimal() objects using E notation + to represent very large values.""" + return exclusions.closed() + + @property + def precision_numerics_many_significant_digits(self): + """target backend supports values with many digits on both sides, + such as 319438950232418390.273596, 87673.594069654243 + + """ + return exclusions.closed() + + @property + def nested_aggregates(self): + """target database can select an aggregate from a subquery that's + also using an aggregate + + """ + return exclusions.open() + + @property + def recursive_fk_cascade(self): + """target database must support ON DELETE CASCADE on a self-referential + foreign key + + """ + return exclusions.open() + + @property + def precision_numerics_retains_significant_digits(self): + """A precision numeric type will return empty significant digits, + i.e. a value such as 10.000 will come back in Decimal form with + the .000 maintained.""" + + return exclusions.closed() + + @property + def precision_generic_float_type(self): + """target backend will return native floating point numbers with at + least seven decimal places when using the generic Float type. + + """ + return exclusions.open() + + @property + def floats_to_four_decimals(self): + """target backend can return a floating-point number with four + significant digits (such as 15.7563) accurately + (i.e. without FP inaccuracies, such as 15.75629997253418). + + """ + return exclusions.open() + + @property + def fetch_null_from_numeric(self): + """target backend doesn't crash when you try to select a NUMERIC + value that has a value of NULL. + + Added to support Pyodbc bug #351. + """ + + return exclusions.open() + + @property + def text_type(self): + """Target database must support an unbounded Text() " + "type such as TEXT or CLOB""" + + return exclusions.open() + + @property + def empty_strings_varchar(self): + """target database can persist/return an empty string with a + varchar. + + """ + return exclusions.open() + + @property + def empty_strings_text(self): + """target database can persist/return an empty string with an + unbounded text.""" + + return exclusions.open() + + @property + def selectone(self): + """target driver must support the literal statement 'select 1'""" + return exclusions.open() + + @property + def savepoints(self): + """Target database must support savepoints.""" + + return exclusions.closed() + + @property + def two_phase_transactions(self): + """Target database must support two-phase transactions.""" + + return exclusions.closed() + + @property + def update_from(self): + """Target must support UPDATE..FROM syntax""" + return exclusions.closed() + + @property + def delete_from(self): + """Target must support DELETE FROM..FROM or DELETE..USING syntax""" + return exclusions.closed() + + @property + def update_where_target_in_subquery(self): + """Target must support UPDATE (or DELETE) where the same table is + present in a subquery in the WHERE clause. + + This is an ANSI-standard syntax that apparently MySQL can't handle, + such as: + + UPDATE documents SET flag=1 WHERE documents.title IN + (SELECT max(documents.title) AS title + FROM documents GROUP BY documents.user_id + ) + """ + return exclusions.open() + + @property + def mod_operator_as_percent_sign(self): + """target database must use a plain percent '%' as the 'modulus' + operator.""" + return exclusions.closed() + + @property + def percent_schema_names(self): + """target backend supports weird identifiers with percent signs + in them, e.g. 'some % column'. + + this is a very weird use case but often has problems because of + DBAPIs that use python formatting. It's not a critical use + case either. + + """ + return exclusions.closed() + + @property + def order_by_label_with_expression(self): + """target backend supports ORDER BY a column label within an + expression. + + Basically this:: + + select data as foo from test order by foo || 'bar' + + Lots of databases including PostgreSQL don't support this, + so this is off by default. + + """ + return exclusions.closed() + + @property + def order_by_collation(self): + def check(config): + try: + self.get_order_by_collation(config) + return False + except NotImplementedError: + return True + + return exclusions.skip_if(check) + + def get_order_by_collation(self, config): + raise NotImplementedError() + + @property + def unicode_connections(self): + """Target driver must support non-ASCII characters being passed at + all. + """ + return exclusions.open() + + @property + def graceful_disconnects(self): + """Target driver must raise a DBAPI-level exception, such as + InterfaceError, when the underlying connection has been closed + and the execute() method is called. + """ + return exclusions.open() + + @property + def skip_mysql_on_windows(self): + """Catchall for a large variety of MySQL on Windows failures""" + return exclusions.open() + + @property + def ad_hoc_engines(self): + """Test environment must allow ad-hoc engine/connection creation. + + DBs that scale poorly for many connections, even when closed, i.e. + Oracle, may use the "--low-connections" option which flags this + requirement as not present. + + """ + return exclusions.skip_if( + lambda config: config.options.low_connections) + + @property + def timing_intensive(self): + return exclusions.requires_tag("timing_intensive") + + @property + def memory_intensive(self): + return exclusions.requires_tag("memory_intensive") + + @property + def threading_with_mock(self): + """Mark tests that use threading and mock at the same time - stability + issues have been observed with coverage + python 3.3 + + """ + return exclusions.skip_if( + lambda config: util.py3k and config.options.has_coverage, + "Stability issues with coverage + py3k" + ) + + @property + def python2(self): + return exclusions.skip_if( + lambda: sys.version_info >= (3,), + "Python version 2.xx is required." + ) + + @property + def python3(self): + return exclusions.skip_if( + lambda: sys.version_info < (3,), + "Python version 3.xx is required." + ) + + @property + def cpython(self): + return exclusions.only_if( + lambda: util.cpython, + "cPython interpreter needed" + ) + + @property + def non_broken_pickle(self): + from sqlalchemy.util import pickle + return exclusions.only_if( + lambda: not util.pypy and pickle.__name__ == 'cPickle' + or sys.version_info >= (3, 2), + "Needs cPickle+cPython or newer Python 3 pickle" + ) + + @property + def predictable_gc(self): + """target platform must remove all cycles unconditionally when + gc.collect() is called, as well as clean out unreferenced subclasses. + + """ + return self.cpython + + @property + def no_coverage(self): + """Test should be skipped if coverage is enabled. + + This is to block tests that exercise libraries that seem to be + sensitive to coverage, such as PostgreSQL notice logging. + + """ + return exclusions.skip_if( + lambda config: config.options.has_coverage, + "Issues observed when coverage is enabled" + ) + + def _has_mysql_on_windows(self, config): + return False + + def _has_mysql_fully_case_sensitive(self, config): + return False + + @property + def sqlite(self): + return exclusions.skip_if(lambda: not self._has_sqlite()) + + @property + def cextensions(self): + return exclusions.skip_if( + lambda: not self._has_cextensions(), "C extensions not installed" + ) + + def _has_sqlite(self): + from sqlalchemy import create_engine + try: + create_engine('sqlite://') + return True + except ImportError: + return False + + def _has_cextensions(self): + try: + from sqlalchemy import cresultproxy, cprocessors + return True + except ImportError: + return False diff --git a/venv/Lib/site-packages/sqlalchemy/testing/runner.py b/venv/Lib/site-packages/sqlalchemy/testing/runner.py new file mode 100644 index 0000000..87be074 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/runner.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# testing/runner.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +""" +Nose test runner module. + +This script is a front-end to "nosetests" which +installs SQLAlchemy's testing plugin into the local environment. + +The script is intended to be used by third-party dialects and extensions +that run within SQLAlchemy's testing framework. The runner can +be invoked via:: + + python -m sqlalchemy.testing.runner + +The script is then essentially the same as the "nosetests" script, including +all of the usual Nose options. The test environment requires that a +setup.cfg is locally present including various required options. + +Note that when using this runner, Nose's "coverage" plugin will not be +able to provide coverage for SQLAlchemy itself, since SQLAlchemy is +imported into sys.modules before coverage is started. The special +script sqla_nose.py is provided as a top-level script which loads the +plugin in a special (somewhat hacky) way so that coverage against +SQLAlchemy itself is possible. + +""" + +from .plugin.noseplugin import NoseSQLAlchemy + +import nose + + +def main(): + nose.main(addplugins=[NoseSQLAlchemy()]) + + +def setup_py_test(): + """Runner to use for the 'test_suite' entry of your setup.py. + + Prevents any name clash shenanigans from the command line + argument "test" that the "setup.py test" command sends + to nose. + + """ + nose.main(addplugins=[NoseSQLAlchemy()], argv=['runner']) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/schema.py b/venv/Lib/site-packages/sqlalchemy/testing/schema.py new file mode 100644 index 0000000..401c8cb --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/schema.py @@ -0,0 +1,100 @@ +# testing/schema.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from . import exclusions +from .. import schema, event +from . import config + +__all__ = 'Table', 'Column', + +table_options = {} + + +def Table(*args, **kw): + """A schema.Table wrapper/hook for dialect-specific tweaks.""" + + test_opts = {k: kw.pop(k) for k in list(kw) if k.startswith('test_')} + + kw.update(table_options) + + if exclusions.against(config._current, 'mysql'): + if 'mysql_engine' not in kw and 'mysql_type' not in kw and \ + "autoload_with" not in kw: + if 'test_needs_fk' in test_opts or 'test_needs_acid' in test_opts: + kw['mysql_engine'] = 'InnoDB' + else: + kw['mysql_engine'] = 'MyISAM' + + # Apply some default cascading rules for self-referential foreign keys. + # MySQL InnoDB has some issues around seleting self-refs too. + if exclusions.against(config._current, 'firebird'): + table_name = args[0] + unpack = (config.db.dialect. + identifier_preparer.unformat_identifiers) + + # Only going after ForeignKeys in Columns. May need to + # expand to ForeignKeyConstraint too. + fks = [fk + for col in args if isinstance(col, schema.Column) + for fk in col.foreign_keys] + + for fk in fks: + # root around in raw spec + ref = fk._colspec + if isinstance(ref, schema.Column): + name = ref.table.name + else: + # take just the table name: on FB there cannot be + # a schema, so the first element is always the + # table name, possibly followed by the field name + name = unpack(ref)[0] + if name == table_name: + if fk.ondelete is None: + fk.ondelete = 'CASCADE' + if fk.onupdate is None: + fk.onupdate = 'CASCADE' + + return schema.Table(*args, **kw) + + +def Column(*args, **kw): + """A schema.Column wrapper/hook for dialect-specific tweaks.""" + + test_opts = {k: kw.pop(k) for k in list(kw) if k.startswith('test_')} + + if not config.requirements.foreign_key_ddl.enabled_for_config(config): + args = [arg for arg in args if not isinstance(arg, schema.ForeignKey)] + + col = schema.Column(*args, **kw) + if test_opts.get('test_needs_autoincrement', False) and \ + kw.get('primary_key', False): + + if col.default is None and col.server_default is None: + col.autoincrement = True + + # allow any test suite to pick up on this + col.info['test_needs_autoincrement'] = True + + # hardcoded rule for firebird, oracle; this should + # be moved out + if exclusions.against(config._current, 'firebird', 'oracle'): + def add_seq(c, tbl): + c._init_items( + schema.Sequence(_truncate_name( + config.db.dialect, tbl.name + '_' + c.name + '_seq'), + optional=True) + ) + event.listen(col, 'after_parent_attach', add_seq, propagate=True) + return col + + +def _truncate_name(dialect, name): + if len(name) > dialect.max_identifier_length: + return name[0:max(dialect.max_identifier_length - 6, 0)] + \ + "_" + hex(hash(name) % 64)[2:] + else: + return name diff --git a/venv/Lib/site-packages/sqlalchemy/testing/suite/__init__.py b/venv/Lib/site-packages/sqlalchemy/testing/suite/__init__.py new file mode 100644 index 0000000..748d972 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/suite/__init__.py @@ -0,0 +1,11 @@ + +from sqlalchemy.testing.suite.test_cte import * +from sqlalchemy.testing.suite.test_dialect import * +from sqlalchemy.testing.suite.test_ddl import * +from sqlalchemy.testing.suite.test_insert import * +from sqlalchemy.testing.suite.test_sequence import * +from sqlalchemy.testing.suite.test_select import * +from sqlalchemy.testing.suite.test_results import * +from sqlalchemy.testing.suite.test_update_delete import * +from sqlalchemy.testing.suite.test_reflection import * +from sqlalchemy.testing.suite.test_types import * diff --git a/venv/Lib/site-packages/sqlalchemy/testing/suite/test_cte.py b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_cte.py new file mode 100644 index 0000000..cc72278 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_cte.py @@ -0,0 +1,193 @@ +from .. import fixtures, config +from ..assertions import eq_ + +from sqlalchemy import Integer, String, select +from sqlalchemy import ForeignKey +from sqlalchemy import testing + +from ..schema import Table, Column + + +class CTETest(fixtures.TablesTest): + __backend__ = True + __requires__ = 'ctes', + + run_inserts = 'each' + run_deletes = 'each' + + @classmethod + def define_tables(cls, metadata): + Table("some_table", metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50)), + Column("parent_id", ForeignKey("some_table.id"))) + + Table("some_other_table", metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50)), + Column("parent_id", Integer)) + + @classmethod + def insert_data(cls): + config.db.execute( + cls.tables.some_table.insert(), + [ + {"id": 1, "data": "d1", "parent_id": None}, + {"id": 2, "data": "d2", "parent_id": 1}, + {"id": 3, "data": "d3", "parent_id": 1}, + {"id": 4, "data": "d4", "parent_id": 3}, + {"id": 5, "data": "d5", "parent_id": 3} + ] + ) + + def test_select_nonrecursive_round_trip(self): + some_table = self.tables.some_table + + with config.db.connect() as conn: + cte = select([some_table]).where( + some_table.c.data.in_(["d2", "d3", "d4"])).cte("some_cte") + result = conn.execute( + select([cte.c.data]).where(cte.c.data.in_(["d4", "d5"])) + ) + eq_(result.fetchall(), [("d4", )]) + + def test_select_recursive_round_trip(self): + some_table = self.tables.some_table + + with config.db.connect() as conn: + cte = select([some_table]).where( + some_table.c.data.in_(["d2", "d3", "d4"])).cte( + "some_cte", recursive=True) + + cte_alias = cte.alias("c1") + st1 = some_table.alias() + # note that SQL Server requires this to be UNION ALL, + # can't be UNION + cte = cte.union_all( + select([st1]).where(st1.c.id == cte_alias.c.parent_id) + ) + result = conn.execute( + select([cte.c.data]).where( + cte.c.data != "d2").order_by(cte.c.data.desc()) + ) + eq_( + result.fetchall(), + [('d4',), ('d3',), ('d3',), ('d1',), ('d1',), ('d1',)] + ) + + def test_insert_from_select_round_trip(self): + some_table = self.tables.some_table + some_other_table = self.tables.some_other_table + + with config.db.connect() as conn: + cte = select([some_table]).where( + some_table.c.data.in_(["d2", "d3", "d4"]) + ).cte("some_cte") + conn.execute( + some_other_table.insert().from_select( + ["id", "data", "parent_id"], + select([cte]) + ) + ) + eq_( + conn.execute( + select([some_other_table]).order_by(some_other_table.c.id) + ).fetchall(), + [(2, "d2", 1), (3, "d3", 1), (4, "d4", 3)] + ) + + @testing.requires.ctes_with_update_delete + @testing.requires.update_from + def test_update_from_round_trip(self): + some_table = self.tables.some_table + some_other_table = self.tables.some_other_table + + with config.db.connect() as conn: + conn.execute( + some_other_table.insert().from_select( + ['id', 'data', 'parent_id'], + select([some_table]) + ) + ) + + cte = select([some_table]).where( + some_table.c.data.in_(["d2", "d3", "d4"]) + ).cte("some_cte") + conn.execute( + some_other_table.update().values(parent_id=5).where( + some_other_table.c.data == cte.c.data + ) + ) + eq_( + conn.execute( + select([some_other_table]).order_by(some_other_table.c.id) + ).fetchall(), + [ + (1, "d1", None), (2, "d2", 5), + (3, "d3", 5), (4, "d4", 5), (5, "d5", 3) + ] + ) + + @testing.requires.ctes_with_update_delete + @testing.requires.delete_from + def test_delete_from_round_trip(self): + some_table = self.tables.some_table + some_other_table = self.tables.some_other_table + + with config.db.connect() as conn: + conn.execute( + some_other_table.insert().from_select( + ['id', 'data', 'parent_id'], + select([some_table]) + ) + ) + + cte = select([some_table]).where( + some_table.c.data.in_(["d2", "d3", "d4"]) + ).cte("some_cte") + conn.execute( + some_other_table.delete().where( + some_other_table.c.data == cte.c.data + ) + ) + eq_( + conn.execute( + select([some_other_table]).order_by(some_other_table.c.id) + ).fetchall(), + [ + (1, "d1", None), (5, "d5", 3) + ] + ) + + @testing.requires.ctes_with_update_delete + def test_delete_scalar_subq_round_trip(self): + + some_table = self.tables.some_table + some_other_table = self.tables.some_other_table + + with config.db.connect() as conn: + conn.execute( + some_other_table.insert().from_select( + ['id', 'data', 'parent_id'], + select([some_table]) + ) + ) + + cte = select([some_table]).where( + some_table.c.data.in_(["d2", "d3", "d4"]) + ).cte("some_cte") + conn.execute( + some_other_table.delete().where( + some_other_table.c.data == + select([cte.c.data]).where( + cte.c.id == some_other_table.c.id) + ) + ) + eq_( + conn.execute( + select([some_other_table]).order_by(some_other_table.c.id) + ).fetchall(), + [ + (1, "d1", None), (5, "d5", 3) + ] + ) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/suite/test_ddl.py b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_ddl.py new file mode 100644 index 0000000..1d8010c --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_ddl.py @@ -0,0 +1,65 @@ + + +from .. import fixtures, config, util +from ..config import requirements +from ..assertions import eq_ + +from sqlalchemy import Table, Column, Integer, String + + +class TableDDLTest(fixtures.TestBase): + __backend__ = True + + def _simple_fixture(self): + return Table('test_table', self.metadata, + Column('id', Integer, primary_key=True, + autoincrement=False), + Column('data', String(50)) + ) + + def _underscore_fixture(self): + return Table('_test_table', self.metadata, + Column('id', Integer, primary_key=True, + autoincrement=False), + Column('_data', String(50)) + ) + + def _simple_roundtrip(self, table): + with config.db.begin() as conn: + conn.execute(table.insert().values((1, 'some data'))) + result = conn.execute(table.select()) + eq_( + result.first(), + (1, 'some data') + ) + + @requirements.create_table + @util.provide_metadata + def test_create_table(self): + table = self._simple_fixture() + table.create( + config.db, checkfirst=False + ) + self._simple_roundtrip(table) + + @requirements.drop_table + @util.provide_metadata + def test_drop_table(self): + table = self._simple_fixture() + table.create( + config.db, checkfirst=False + ) + table.drop( + config.db, checkfirst=False + ) + + @requirements.create_table + @util.provide_metadata + def test_underscore_names(self): + table = self._underscore_fixture() + table.create( + config.db, checkfirst=False + ) + self._simple_roundtrip(table) + +__all__ = ('TableDDLTest', ) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/suite/test_dialect.py b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_dialect.py new file mode 100644 index 0000000..2c5dd0e --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_dialect.py @@ -0,0 +1,120 @@ +from .. import fixtures, config +from ..config import requirements +from sqlalchemy import exc +from sqlalchemy import Integer, String, select, literal_column +from .. import assert_raises +from ..schema import Table, Column +from .. import provide_metadata +from .. import eq_ + + +class ExceptionTest(fixtures.TablesTest): + """Test basic exception wrapping. + + DBAPIs vary a lot in exception behavior so to actually anticipate + specific exceptions from real round trips, we need to be conservative. + + """ + run_deletes = 'each' + + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table('manual_pk', metadata, + Column('id', Integer, primary_key=True, autoincrement=False), + Column('data', String(50)) + ) + + @requirements.duplicate_key_raises_integrity_error + def test_integrity_error(self): + + with config.db.connect() as conn: + + trans = conn.begin() + conn.execute( + self.tables.manual_pk.insert(), + {'id': 1, 'data': 'd1'} + ) + + assert_raises( + exc.IntegrityError, + conn.execute, + self.tables.manual_pk.insert(), + {'id': 1, 'data': 'd1'} + ) + + trans.rollback() + + +class AutocommitTest(fixtures.TablesTest): + + run_deletes = 'each' + + __requires__ = 'autocommit', + + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table('some_table', metadata, + Column('id', Integer, primary_key=True, autoincrement=False), + Column('data', String(50)), + test_needs_acid=True + ) + + def _test_conn_autocommits(self, conn, autocommit): + trans = conn.begin() + conn.execute( + self.tables.some_table.insert(), + {"id": 1, "data": "some data"} + ) + trans.rollback() + + eq_( + conn.scalar(select([self.tables.some_table.c.id])), + 1 if autocommit else None + ) + + conn.execute(self.tables.some_table.delete()) + + def test_autocommit_on(self): + conn = config.db.connect() + c2 = conn.execution_options(isolation_level='AUTOCOMMIT') + self._test_conn_autocommits(c2, True) + conn.invalidate() + self._test_conn_autocommits(conn, False) + + def test_autocommit_off(self): + conn = config.db.connect() + self._test_conn_autocommits(conn, False) + + +class EscapingTest(fixtures.TestBase): + @provide_metadata + def test_percent_sign_round_trip(self): + """test that the DBAPI accommodates for escaped / nonescaped + percent signs in a way that matches the compiler + + """ + m = self.metadata + t = Table('t', m, Column('data', String(50))) + t.create(config.db) + with config.db.begin() as conn: + conn.execute(t.insert(), dict(data="some % value")) + conn.execute(t.insert(), dict(data="some %% other value")) + + eq_( + conn.scalar( + select([t.c.data]).where( + t.c.data == literal_column("'some % value'")) + ), + "some % value" + ) + + eq_( + conn.scalar( + select([t.c.data]).where( + t.c.data == literal_column("'some %% other value'")) + ), "some %% other value" + ) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/suite/test_insert.py b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_insert.py new file mode 100644 index 0000000..c0b6b18 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_insert.py @@ -0,0 +1,319 @@ +from .. import fixtures, config +from ..config import requirements +from .. import exclusions +from ..assertions import eq_ +from .. import engines + +from sqlalchemy import Integer, String, select, literal_column, literal + +from ..schema import Table, Column + + +class LastrowidTest(fixtures.TablesTest): + run_deletes = 'each' + + __backend__ = True + + __requires__ = 'implements_get_lastrowid', 'autoincrement_insert' + + __engine_options__ = {"implicit_returning": False} + + @classmethod + def define_tables(cls, metadata): + Table('autoinc_pk', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('data', String(50)) + ) + + Table('manual_pk', metadata, + Column('id', Integer, primary_key=True, autoincrement=False), + Column('data', String(50)) + ) + + def _assert_round_trip(self, table, conn): + row = conn.execute(table.select()).first() + eq_( + row, + (config.db.dialect.default_sequence_base, "some data") + ) + + def test_autoincrement_on_insert(self): + + config.db.execute( + self.tables.autoinc_pk.insert(), + data="some data" + ) + self._assert_round_trip(self.tables.autoinc_pk, config.db) + + def test_last_inserted_id(self): + + r = config.db.execute( + self.tables.autoinc_pk.insert(), + data="some data" + ) + pk = config.db.scalar(select([self.tables.autoinc_pk.c.id])) + eq_( + r.inserted_primary_key, + [pk] + ) + + # failed on pypy1.9 but seems to be OK on pypy 2.1 + # @exclusions.fails_if(lambda: util.pypy, + # "lastrowid not maintained after " + # "connection close") + @requirements.dbapi_lastrowid + def test_native_lastrowid_autoinc(self): + r = config.db.execute( + self.tables.autoinc_pk.insert(), + data="some data" + ) + lastrowid = r.lastrowid + pk = config.db.scalar(select([self.tables.autoinc_pk.c.id])) + eq_( + lastrowid, pk + ) + + +class InsertBehaviorTest(fixtures.TablesTest): + run_deletes = 'each' + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table('autoinc_pk', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('data', String(50)) + ) + Table('manual_pk', metadata, + Column('id', Integer, primary_key=True, autoincrement=False), + Column('data', String(50)) + ) + Table('includes_defaults', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('data', String(50)), + Column('x', Integer, default=5), + Column('y', Integer, + default=literal_column("2", type_=Integer) + literal(2))) + + def test_autoclose_on_insert(self): + if requirements.returning.enabled: + engine = engines.testing_engine( + options={'implicit_returning': False}) + else: + engine = config.db + + r = engine.execute( + self.tables.autoinc_pk.insert(), + data="some data" + ) + assert r._soft_closed + assert not r.closed + assert r.is_insert + assert not r.returns_rows + + @requirements.returning + def test_autoclose_on_insert_implicit_returning(self): + r = config.db.execute( + self.tables.autoinc_pk.insert(), + data="some data" + ) + assert r._soft_closed + assert not r.closed + assert r.is_insert + assert not r.returns_rows + + @requirements.empty_inserts + def test_empty_insert(self): + r = config.db.execute( + self.tables.autoinc_pk.insert(), + ) + assert r._soft_closed + assert not r.closed + + r = config.db.execute( + self.tables.autoinc_pk.select(). + where(self.tables.autoinc_pk.c.id != None) + ) + + assert len(r.fetchall()) + + @requirements.insert_from_select + def test_insert_from_select_autoinc(self): + src_table = self.tables.manual_pk + dest_table = self.tables.autoinc_pk + config.db.execute( + src_table.insert(), + [ + dict(id=1, data="data1"), + dict(id=2, data="data2"), + dict(id=3, data="data3"), + ] + ) + + result = config.db.execute( + dest_table.insert(). + from_select( + ("data",), + select([src_table.c.data]). + where(src_table.c.data.in_(["data2", "data3"])) + ) + ) + + eq_(result.inserted_primary_key, [None]) + + result = config.db.execute( + select([dest_table.c.data]).order_by(dest_table.c.data) + ) + eq_(result.fetchall(), [("data2", ), ("data3", )]) + + @requirements.insert_from_select + def test_insert_from_select_autoinc_no_rows(self): + src_table = self.tables.manual_pk + dest_table = self.tables.autoinc_pk + + result = config.db.execute( + dest_table.insert(). + from_select( + ("data",), + select([src_table.c.data]). + where(src_table.c.data.in_(["data2", "data3"])) + ) + ) + eq_(result.inserted_primary_key, [None]) + + result = config.db.execute( + select([dest_table.c.data]).order_by(dest_table.c.data) + ) + + eq_(result.fetchall(), []) + + @requirements.insert_from_select + def test_insert_from_select(self): + table = self.tables.manual_pk + config.db.execute( + table.insert(), + [ + dict(id=1, data="data1"), + dict(id=2, data="data2"), + dict(id=3, data="data3"), + ] + ) + + config.db.execute( + table.insert(inline=True). + from_select(("id", "data",), + select([table.c.id + 5, table.c.data]). + where(table.c.data.in_(["data2", "data3"])) + ), + ) + + eq_( + config.db.execute( + select([table.c.data]).order_by(table.c.data) + ).fetchall(), + [("data1", ), ("data2", ), ("data2", ), + ("data3", ), ("data3", )] + ) + + @requirements.insert_from_select + def test_insert_from_select_with_defaults(self): + table = self.tables.includes_defaults + config.db.execute( + table.insert(), + [ + dict(id=1, data="data1"), + dict(id=2, data="data2"), + dict(id=3, data="data3"), + ] + ) + + config.db.execute( + table.insert(inline=True). + from_select(("id", "data",), + select([table.c.id + 5, table.c.data]). + where(table.c.data.in_(["data2", "data3"])) + ), + ) + + eq_( + config.db.execute( + select([table]).order_by(table.c.data, table.c.id) + ).fetchall(), + [(1, 'data1', 5, 4), (2, 'data2', 5, 4), + (7, 'data2', 5, 4), (3, 'data3', 5, 4), (8, 'data3', 5, 4)] + ) + + +class ReturningTest(fixtures.TablesTest): + run_create_tables = 'each' + __requires__ = 'returning', 'autoincrement_insert' + __backend__ = True + + __engine_options__ = {"implicit_returning": True} + + def _assert_round_trip(self, table, conn): + row = conn.execute(table.select()).first() + eq_( + row, + (config.db.dialect.default_sequence_base, "some data") + ) + + @classmethod + def define_tables(cls, metadata): + Table('autoinc_pk', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('data', String(50)) + ) + + @requirements.fetch_rows_post_commit + def test_explicit_returning_pk_autocommit(self): + engine = config.db + table = self.tables.autoinc_pk + r = engine.execute( + table.insert().returning( + table.c.id), + data="some data" + ) + pk = r.first()[0] + fetched_pk = config.db.scalar(select([table.c.id])) + eq_(fetched_pk, pk) + + def test_explicit_returning_pk_no_autocommit(self): + engine = config.db + table = self.tables.autoinc_pk + with engine.begin() as conn: + r = conn.execute( + table.insert().returning( + table.c.id), + data="some data" + ) + pk = r.first()[0] + fetched_pk = config.db.scalar(select([table.c.id])) + eq_(fetched_pk, pk) + + def test_autoincrement_on_insert_implcit_returning(self): + + config.db.execute( + self.tables.autoinc_pk.insert(), + data="some data" + ) + self._assert_round_trip(self.tables.autoinc_pk, config.db) + + def test_last_inserted_id_implicit_returning(self): + + r = config.db.execute( + self.tables.autoinc_pk.insert(), + data="some data" + ) + pk = config.db.scalar(select([self.tables.autoinc_pk.c.id])) + eq_( + r.inserted_primary_key, + [pk] + ) + + +__all__ = ('LastrowidTest', 'InsertBehaviorTest', 'ReturningTest') diff --git a/venv/Lib/site-packages/sqlalchemy/testing/suite/test_reflection.py b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_reflection.py new file mode 100644 index 0000000..00a5aac --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_reflection.py @@ -0,0 +1,998 @@ + + +import sqlalchemy as sa +from sqlalchemy import exc as sa_exc +from sqlalchemy import types as sql_types +from sqlalchemy import inspect +from sqlalchemy import MetaData, Integer, String, func +from sqlalchemy.engine.reflection import Inspector +from sqlalchemy.testing import engines, fixtures +from sqlalchemy.testing.schema import Table, Column +from sqlalchemy.testing import eq_, is_, assert_raises_message +from sqlalchemy import testing +from .. import config +import operator +from sqlalchemy.schema import DDL, Index +from sqlalchemy import event +from sqlalchemy.sql.elements import quoted_name +from sqlalchemy import ForeignKey +import re + +metadata, users = None, None + + +class HasTableTest(fixtures.TablesTest): + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table('test_table', metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50)) + ) + + def test_has_table(self): + with config.db.begin() as conn: + assert config.db.dialect.has_table(conn, "test_table") + assert not config.db.dialect.has_table(conn, "nonexistent_table") + + +class ComponentReflectionTest(fixtures.TablesTest): + run_inserts = run_deletes = None + + __backend__ = True + + @classmethod + def setup_bind(cls): + if config.requirements.independent_connections.enabled: + from sqlalchemy import pool + return engines.testing_engine( + options=dict(poolclass=pool.StaticPool)) + else: + return config.db + + @classmethod + def define_tables(cls, metadata): + cls.define_reflected_tables(metadata, None) + if testing.requires.schemas.enabled: + cls.define_reflected_tables(metadata, testing.config.test_schema) + + @classmethod + def define_reflected_tables(cls, metadata, schema): + if schema: + schema_prefix = schema + "." + else: + schema_prefix = "" + + if testing.requires.self_referential_foreign_keys.enabled: + users = Table('users', metadata, + Column('user_id', sa.INT, primary_key=True), + Column('test1', sa.CHAR(5), nullable=False), + Column('test2', sa.Float(5), nullable=False), + Column('parent_user_id', sa.Integer, + sa.ForeignKey('%susers.user_id' % + schema_prefix, + name='user_id_fk')), + schema=schema, + test_needs_fk=True, + ) + else: + users = Table('users', metadata, + Column('user_id', sa.INT, primary_key=True), + Column('test1', sa.CHAR(5), nullable=False), + Column('test2', sa.Float(5), nullable=False), + schema=schema, + test_needs_fk=True, + ) + + Table("dingalings", metadata, + Column('dingaling_id', sa.Integer, primary_key=True), + Column('address_id', sa.Integer, + sa.ForeignKey('%semail_addresses.address_id' % + schema_prefix)), + Column('data', sa.String(30)), + schema=schema, + test_needs_fk=True, + ) + Table('email_addresses', metadata, + Column('address_id', sa.Integer), + Column('remote_user_id', sa.Integer, + sa.ForeignKey(users.c.user_id)), + Column('email_address', sa.String(20)), + sa.PrimaryKeyConstraint('address_id', name='email_ad_pk'), + schema=schema, + test_needs_fk=True, + ) + Table('comment_test', metadata, + Column('id', sa.Integer, primary_key=True, comment='id comment'), + Column('data', sa.String(20), comment='data % comment'), + Column( + 'd2', sa.String(20), + comment=r"""Comment types type speedily ' " \ '' Fun!"""), + schema=schema, + comment=r"""the test % ' " \ table comment""") + + if testing.requires.cross_schema_fk_reflection.enabled: + if schema is None: + Table( + 'local_table', metadata, + Column('id', sa.Integer, primary_key=True), + Column('data', sa.String(20)), + Column( + 'remote_id', + ForeignKey( + '%s.remote_table_2.id' % + testing.config.test_schema) + ), + test_needs_fk=True, + schema=config.db.dialect.default_schema_name + ) + else: + Table( + 'remote_table', metadata, + Column('id', sa.Integer, primary_key=True), + Column( + 'local_id', + ForeignKey( + '%s.local_table.id' % + config.db.dialect.default_schema_name) + ), + Column('data', sa.String(20)), + schema=schema, + test_needs_fk=True, + ) + Table( + 'remote_table_2', metadata, + Column('id', sa.Integer, primary_key=True), + Column('data', sa.String(20)), + schema=schema, + test_needs_fk=True, + ) + + if testing.requires.index_reflection.enabled: + cls.define_index(metadata, users) + + if not schema: + # test_needs_fk is at the moment to force MySQL InnoDB + noncol_idx_test_nopk = Table( + 'noncol_idx_test_nopk', metadata, + Column('q', sa.String(5)), + test_needs_fk=True, + ) + + noncol_idx_test_pk = Table( + 'noncol_idx_test_pk', metadata, + Column('id', sa.Integer, primary_key=True), + Column('q', sa.String(5)), + test_needs_fk=True, + ) + Index('noncol_idx_nopk', noncol_idx_test_nopk.c.q.desc()) + Index('noncol_idx_pk', noncol_idx_test_pk.c.q.desc()) + + if testing.requires.view_column_reflection.enabled: + cls.define_views(metadata, schema) + if not schema and testing.requires.temp_table_reflection.enabled: + cls.define_temp_tables(metadata) + + @classmethod + def define_temp_tables(cls, metadata): + # cheat a bit, we should fix this with some dialect-level + # temp table fixture + if testing.against("oracle"): + kw = { + 'prefixes': ["GLOBAL TEMPORARY"], + 'oracle_on_commit': 'PRESERVE ROWS' + } + else: + kw = { + 'prefixes': ["TEMPORARY"], + } + + user_tmp = Table( + "user_tmp", metadata, + Column("id", sa.INT, primary_key=True), + Column('name', sa.VARCHAR(50)), + Column('foo', sa.INT), + sa.UniqueConstraint('name', name='user_tmp_uq'), + sa.Index("user_tmp_ix", "foo"), + **kw + ) + if testing.requires.view_reflection.enabled and \ + testing.requires.temporary_views.enabled: + event.listen( + user_tmp, "after_create", + DDL("create temporary view user_tmp_v as " + "select * from user_tmp") + ) + event.listen( + user_tmp, "before_drop", + DDL("drop view user_tmp_v") + ) + + @classmethod + def define_index(cls, metadata, users): + Index("users_t_idx", users.c.test1, users.c.test2) + Index("users_all_idx", users.c.user_id, users.c.test2, users.c.test1) + + @classmethod + def define_views(cls, metadata, schema): + for table_name in ('users', 'email_addresses'): + fullname = table_name + if schema: + fullname = "%s.%s" % (schema, table_name) + view_name = fullname + '_v' + query = "CREATE VIEW %s AS SELECT * FROM %s" % ( + view_name, fullname) + + event.listen( + metadata, + "after_create", + DDL(query) + ) + event.listen( + metadata, + "before_drop", + DDL("DROP VIEW %s" % view_name) + ) + + @testing.requires.schema_reflection + def test_get_schema_names(self): + insp = inspect(testing.db) + + self.assert_(testing.config.test_schema in insp.get_schema_names()) + + @testing.requires.schema_reflection + def test_dialect_initialize(self): + engine = engines.testing_engine() + assert not hasattr(engine.dialect, 'default_schema_name') + inspect(engine) + assert hasattr(engine.dialect, 'default_schema_name') + + @testing.requires.schema_reflection + def test_get_default_schema_name(self): + insp = inspect(testing.db) + eq_(insp.default_schema_name, testing.db.dialect.default_schema_name) + + @testing.provide_metadata + def _test_get_table_names(self, schema=None, table_type='table', + order_by=None): + _ignore_tables = [ + 'comment_test', 'noncol_idx_test_pk', 'noncol_idx_test_nopk', + 'local_table', 'remote_table', 'remote_table_2' + ] + meta = self.metadata + users, addresses, dingalings = self.tables.users, \ + self.tables.email_addresses, self.tables.dingalings + insp = inspect(meta.bind) + + if table_type == 'view': + table_names = insp.get_view_names(schema) + table_names.sort() + answer = ['email_addresses_v', 'users_v'] + eq_(sorted(table_names), answer) + else: + table_names = [ + t for t in insp.get_table_names( + schema, + order_by=order_by) if t not in _ignore_tables] + + if order_by == 'foreign_key': + answer = ['users', 'email_addresses', 'dingalings'] + eq_(table_names, answer) + else: + answer = ['dingalings', 'email_addresses', 'users'] + eq_(sorted(table_names), answer) + + @testing.requires.temp_table_names + def test_get_temp_table_names(self): + insp = inspect(self.bind) + temp_table_names = insp.get_temp_table_names() + eq_(sorted(temp_table_names), ['user_tmp']) + + @testing.requires.view_reflection + @testing.requires.temp_table_names + @testing.requires.temporary_views + def test_get_temp_view_names(self): + insp = inspect(self.bind) + temp_table_names = insp.get_temp_view_names() + eq_(sorted(temp_table_names), ['user_tmp_v']) + + @testing.requires.table_reflection + def test_get_table_names(self): + self._test_get_table_names() + + @testing.requires.table_reflection + @testing.requires.foreign_key_constraint_reflection + def test_get_table_names_fks(self): + self._test_get_table_names(order_by='foreign_key') + + @testing.requires.comment_reflection + def test_get_comments(self): + self._test_get_comments() + + @testing.requires.comment_reflection + @testing.requires.schemas + def test_get_comments_with_schema(self): + self._test_get_comments(testing.config.test_schema) + + def _test_get_comments(self, schema=None): + insp = inspect(testing.db) + + eq_( + insp.get_table_comment("comment_test", schema=schema), + {"text": r"""the test % ' " \ table comment"""} + ) + + eq_( + insp.get_table_comment("users", schema=schema), + {"text": None} + ) + + eq_( + [ + {"name": rec['name'], "comment": rec['comment']} + for rec in + insp.get_columns("comment_test", schema=schema) + ], + [ + {'comment': 'id comment', 'name': 'id'}, + {'comment': 'data % comment', 'name': 'data'}, + {'comment': r"""Comment types type speedily ' " \ '' Fun!""", + 'name': 'd2'} + ] + ) + + @testing.requires.table_reflection + @testing.requires.schemas + def test_get_table_names_with_schema(self): + self._test_get_table_names(testing.config.test_schema) + + @testing.requires.view_column_reflection + def test_get_view_names(self): + self._test_get_table_names(table_type='view') + + @testing.requires.view_column_reflection + @testing.requires.schemas + def test_get_view_names_with_schema(self): + self._test_get_table_names( + testing.config.test_schema, table_type='view') + + @testing.requires.table_reflection + @testing.requires.view_column_reflection + def test_get_tables_and_views(self): + self._test_get_table_names() + self._test_get_table_names(table_type='view') + + def _test_get_columns(self, schema=None, table_type='table'): + meta = MetaData(testing.db) + users, addresses, dingalings = self.tables.users, \ + self.tables.email_addresses, self.tables.dingalings + table_names = ['users', 'email_addresses'] + if table_type == 'view': + table_names = ['users_v', 'email_addresses_v'] + insp = inspect(meta.bind) + for table_name, table in zip(table_names, (users, + addresses)): + schema_name = schema + cols = insp.get_columns(table_name, schema=schema_name) + self.assert_(len(cols) > 0, len(cols)) + + # should be in order + + for i, col in enumerate(table.columns): + eq_(col.name, cols[i]['name']) + ctype = cols[i]['type'].__class__ + ctype_def = col.type + if isinstance(ctype_def, sa.types.TypeEngine): + ctype_def = ctype_def.__class__ + + # Oracle returns Date for DateTime. + + if testing.against('oracle') and ctype_def \ + in (sql_types.Date, sql_types.DateTime): + ctype_def = sql_types.Date + + # assert that the desired type and return type share + # a base within one of the generic types. + + self.assert_(len(set(ctype.__mro__). + intersection(ctype_def.__mro__). + intersection([ + sql_types.Integer, + sql_types.Numeric, + sql_types.DateTime, + sql_types.Date, + sql_types.Time, + sql_types.String, + sql_types._Binary, + ])) > 0, '%s(%s), %s(%s)' % + (col.name, col.type, cols[i]['name'], ctype)) + + if not col.primary_key: + assert cols[i]['default'] is None + + @testing.requires.table_reflection + def test_get_columns(self): + self._test_get_columns() + + @testing.provide_metadata + def _type_round_trip(self, *types): + t = Table('t', self.metadata, + *[ + Column('t%d' % i, type_) + for i, type_ in enumerate(types) + ] + ) + t.create() + + return [ + c['type'] for c in + inspect(self.metadata.bind).get_columns('t') + ] + + @testing.requires.table_reflection + def test_numeric_reflection(self): + for typ in self._type_round_trip( + sql_types.Numeric(18, 5), + ): + assert isinstance(typ, sql_types.Numeric) + eq_(typ.precision, 18) + eq_(typ.scale, 5) + + @testing.requires.table_reflection + def test_varchar_reflection(self): + typ = self._type_round_trip(sql_types.String(52))[0] + assert isinstance(typ, sql_types.String) + eq_(typ.length, 52) + + @testing.requires.table_reflection + @testing.provide_metadata + def test_nullable_reflection(self): + t = Table('t', self.metadata, + Column('a', Integer, nullable=True), + Column('b', Integer, nullable=False)) + t.create() + eq_( + dict( + (col['name'], col['nullable']) + for col in inspect(self.metadata.bind).get_columns('t') + ), + {"a": True, "b": False} + ) + + @testing.requires.table_reflection + @testing.requires.schemas + def test_get_columns_with_schema(self): + self._test_get_columns(schema=testing.config.test_schema) + + @testing.requires.temp_table_reflection + def test_get_temp_table_columns(self): + meta = MetaData(self.bind) + user_tmp = self.tables.user_tmp + insp = inspect(meta.bind) + cols = insp.get_columns('user_tmp') + self.assert_(len(cols) > 0, len(cols)) + + for i, col in enumerate(user_tmp.columns): + eq_(col.name, cols[i]['name']) + + @testing.requires.temp_table_reflection + @testing.requires.view_column_reflection + @testing.requires.temporary_views + def test_get_temp_view_columns(self): + insp = inspect(self.bind) + cols = insp.get_columns('user_tmp_v') + eq_( + [col['name'] for col in cols], + ['id', 'name', 'foo'] + ) + + @testing.requires.view_column_reflection + def test_get_view_columns(self): + self._test_get_columns(table_type='view') + + @testing.requires.view_column_reflection + @testing.requires.schemas + def test_get_view_columns_with_schema(self): + self._test_get_columns( + schema=testing.config.test_schema, table_type='view') + + @testing.provide_metadata + def _test_get_pk_constraint(self, schema=None): + meta = self.metadata + users, addresses = self.tables.users, self.tables.email_addresses + insp = inspect(meta.bind) + + users_cons = insp.get_pk_constraint(users.name, schema=schema) + users_pkeys = users_cons['constrained_columns'] + eq_(users_pkeys, ['user_id']) + + addr_cons = insp.get_pk_constraint(addresses.name, schema=schema) + addr_pkeys = addr_cons['constrained_columns'] + eq_(addr_pkeys, ['address_id']) + + with testing.requires.reflects_pk_names.fail_if(): + eq_(addr_cons['name'], 'email_ad_pk') + + @testing.requires.primary_key_constraint_reflection + def test_get_pk_constraint(self): + self._test_get_pk_constraint() + + @testing.requires.table_reflection + @testing.requires.primary_key_constraint_reflection + @testing.requires.schemas + def test_get_pk_constraint_with_schema(self): + self._test_get_pk_constraint(schema=testing.config.test_schema) + + @testing.requires.table_reflection + @testing.provide_metadata + def test_deprecated_get_primary_keys(self): + meta = self.metadata + users = self.tables.users + insp = Inspector(meta.bind) + assert_raises_message( + sa_exc.SADeprecationWarning, + "Call to deprecated method get_primary_keys." + " Use get_pk_constraint instead.", + insp.get_primary_keys, users.name + ) + + @testing.provide_metadata + def _test_get_foreign_keys(self, schema=None): + meta = self.metadata + users, addresses, dingalings = self.tables.users, \ + self.tables.email_addresses, self.tables.dingalings + insp = inspect(meta.bind) + expected_schema = schema + # users + + if testing.requires.self_referential_foreign_keys.enabled: + users_fkeys = insp.get_foreign_keys(users.name, + schema=schema) + fkey1 = users_fkeys[0] + + with testing.requires.named_constraints.fail_if(): + eq_(fkey1['name'], "user_id_fk") + + eq_(fkey1['referred_schema'], expected_schema) + eq_(fkey1['referred_table'], users.name) + eq_(fkey1['referred_columns'], ['user_id', ]) + if testing.requires.self_referential_foreign_keys.enabled: + eq_(fkey1['constrained_columns'], ['parent_user_id']) + + # addresses + addr_fkeys = insp.get_foreign_keys(addresses.name, + schema=schema) + fkey1 = addr_fkeys[0] + + with testing.requires.implicitly_named_constraints.fail_if(): + self.assert_(fkey1['name'] is not None) + + eq_(fkey1['referred_schema'], expected_schema) + eq_(fkey1['referred_table'], users.name) + eq_(fkey1['referred_columns'], ['user_id', ]) + eq_(fkey1['constrained_columns'], ['remote_user_id']) + + @testing.requires.foreign_key_constraint_reflection + def test_get_foreign_keys(self): + self._test_get_foreign_keys() + + @testing.requires.foreign_key_constraint_reflection + @testing.requires.schemas + def test_get_foreign_keys_with_schema(self): + self._test_get_foreign_keys(schema=testing.config.test_schema) + + @testing.requires.cross_schema_fk_reflection + @testing.requires.schemas + def test_get_inter_schema_foreign_keys(self): + local_table, remote_table, remote_table_2 = self.tables( + '%s.local_table' % testing.db.dialect.default_schema_name, + '%s.remote_table' % testing.config.test_schema, + '%s.remote_table_2' % testing.config.test_schema + ) + + insp = inspect(config.db) + + local_fkeys = insp.get_foreign_keys(local_table.name) + eq_(len(local_fkeys), 1) + + fkey1 = local_fkeys[0] + eq_(fkey1['referred_schema'], testing.config.test_schema) + eq_(fkey1['referred_table'], remote_table_2.name) + eq_(fkey1['referred_columns'], ['id', ]) + eq_(fkey1['constrained_columns'], ['remote_id']) + + remote_fkeys = insp.get_foreign_keys( + remote_table.name, schema=testing.config.test_schema) + eq_(len(remote_fkeys), 1) + + fkey2 = remote_fkeys[0] + + assert fkey2['referred_schema'] in ( + None, + testing.db.dialect.default_schema_name + ) + eq_(fkey2['referred_table'], local_table.name) + eq_(fkey2['referred_columns'], ['id', ]) + eq_(fkey2['constrained_columns'], ['local_id']) + + + @testing.requires.foreign_key_constraint_option_reflection_ondelete + def test_get_foreign_key_options_ondelete(self): + self._test_get_foreign_key_options(ondelete="CASCADE") + + @testing.requires.foreign_key_constraint_option_reflection_onupdate + def test_get_foreign_key_options_onupdate(self): + self._test_get_foreign_key_options(onupdate="SET NULL") + + @testing.provide_metadata + def _test_get_foreign_key_options(self, **options): + meta = self.metadata + + Table( + 'x', meta, + Column('id', Integer, primary_key=True), + test_needs_fk=True + ) + + Table('table', meta, + Column('id', Integer, primary_key=True), + Column('x_id', Integer, sa.ForeignKey('x.id', name='xid')), + Column('test', String(10)), + test_needs_fk=True) + + Table('user', meta, + Column('id', Integer, primary_key=True), + Column('name', String(50), nullable=False), + Column('tid', Integer), + sa.ForeignKeyConstraint( + ['tid'], ['table.id'], + name='myfk', + **options), + test_needs_fk=True) + + meta.create_all() + + insp = inspect(meta.bind) + + # test 'options' is always present for a backend + # that can reflect these, since alembic looks for this + opts = insp.get_foreign_keys('table')[0]['options'] + + eq_( + dict( + (k, opts[k]) + for k in opts if opts[k] + ), + {} + ) + + opts = insp.get_foreign_keys('user')[0]['options'] + eq_( + dict( + (k, opts[k]) + for k in opts if opts[k] + ), + options + ) + + def _assert_insp_indexes(self, indexes, expected_indexes): + index_names = [d['name'] for d in indexes] + for e_index in expected_indexes: + assert e_index['name'] in index_names + index = indexes[index_names.index(e_index['name'])] + for key in e_index: + eq_(e_index[key], index[key]) + + @testing.provide_metadata + def _test_get_indexes(self, schema=None): + meta = self.metadata + users, addresses, dingalings = self.tables.users, \ + self.tables.email_addresses, self.tables.dingalings + # The database may decide to create indexes for foreign keys, etc. + # so there may be more indexes than expected. + insp = inspect(meta.bind) + indexes = insp.get_indexes('users', schema=schema) + expected_indexes = [ + {'unique': False, + 'column_names': ['test1', 'test2'], + 'name': 'users_t_idx'}, + {'unique': False, + 'column_names': ['user_id', 'test2', 'test1'], + 'name': 'users_all_idx'} + ] + self._assert_insp_indexes(indexes, expected_indexes) + + @testing.requires.index_reflection + def test_get_indexes(self): + self._test_get_indexes() + + @testing.requires.index_reflection + @testing.requires.schemas + def test_get_indexes_with_schema(self): + self._test_get_indexes(schema=testing.config.test_schema) + + @testing.provide_metadata + def _test_get_noncol_index(self, tname, ixname): + meta = self.metadata + insp = inspect(meta.bind) + indexes = insp.get_indexes(tname) + + # reflecting an index that has "x DESC" in it as the column. + # the DB may or may not give us "x", but make sure we get the index + # back, it has a name, it's connected to the table. + expected_indexes = [ + {'unique': False, + 'name': ixname} + ] + self._assert_insp_indexes(indexes, expected_indexes) + + t = Table(tname, meta, autoload_with=meta.bind) + eq_(len(t.indexes), 1) + is_(list(t.indexes)[0].table, t) + eq_(list(t.indexes)[0].name, ixname) + + @testing.requires.index_reflection + def test_get_noncol_index_no_pk(self): + self._test_get_noncol_index("noncol_idx_test_nopk", "noncol_idx_nopk") + + @testing.requires.index_reflection + def test_get_noncol_index_pk(self): + self._test_get_noncol_index("noncol_idx_test_pk", "noncol_idx_pk") + + @testing.requires.unique_constraint_reflection + def test_get_unique_constraints(self): + self._test_get_unique_constraints() + + @testing.requires.temp_table_reflection + @testing.requires.unique_constraint_reflection + def test_get_temp_table_unique_constraints(self): + insp = inspect(self.bind) + reflected = insp.get_unique_constraints('user_tmp') + for refl in reflected: + # Different dialects handle duplicate index and constraints + # differently, so ignore this flag + refl.pop('duplicates_index', None) + eq_(reflected, [{'column_names': ['name'], 'name': 'user_tmp_uq'}]) + + @testing.requires.temp_table_reflection + def test_get_temp_table_indexes(self): + insp = inspect(self.bind) + indexes = insp.get_indexes('user_tmp') + for ind in indexes: + ind.pop('dialect_options', None) + eq_( + # TODO: we need to add better filtering for indexes/uq constraints + # that are doubled up + [idx for idx in indexes if idx['name'] == 'user_tmp_ix'], + [{'unique': False, 'column_names': ['foo'], 'name': 'user_tmp_ix'}] + ) + + @testing.requires.unique_constraint_reflection + @testing.requires.schemas + def test_get_unique_constraints_with_schema(self): + self._test_get_unique_constraints(schema=testing.config.test_schema) + + @testing.provide_metadata + def _test_get_unique_constraints(self, schema=None): + # SQLite dialect needs to parse the names of the constraints + # separately from what it gets from PRAGMA index_list(), and + # then matches them up. so same set of column_names in two + # constraints will confuse it. Perhaps we should no longer + # bother with index_list() here since we have the whole + # CREATE TABLE? + uniques = sorted( + [ + {'name': 'unique_a', 'column_names': ['a']}, + {'name': 'unique_a_b_c', 'column_names': ['a', 'b', 'c']}, + {'name': 'unique_c_a_b', 'column_names': ['c', 'a', 'b']}, + {'name': 'unique_asc_key', 'column_names': ['asc', 'key']}, + {'name': 'i.have.dots', 'column_names': ['b']}, + {'name': 'i have spaces', 'column_names': ['c']}, + ], + key=operator.itemgetter('name') + ) + orig_meta = self.metadata + table = Table( + 'testtbl', orig_meta, + Column('a', sa.String(20)), + Column('b', sa.String(30)), + Column('c', sa.Integer), + # reserved identifiers + Column('asc', sa.String(30)), + Column('key', sa.String(30)), + schema=schema + ) + for uc in uniques: + table.append_constraint( + sa.UniqueConstraint(*uc['column_names'], name=uc['name']) + ) + orig_meta.create_all() + + inspector = inspect(orig_meta.bind) + reflected = sorted( + inspector.get_unique_constraints('testtbl', schema=schema), + key=operator.itemgetter('name') + ) + + names_that_duplicate_index = set() + + for orig, refl in zip(uniques, reflected): + # Different dialects handle duplicate index and constraints + # differently, so ignore this flag + dupe = refl.pop('duplicates_index', None) + if dupe: + names_that_duplicate_index.add(dupe) + eq_(orig, refl) + + reflected_metadata = MetaData() + reflected = Table( + 'testtbl', reflected_metadata, autoload_with=orig_meta.bind, + schema=schema) + + # test "deduplicates for index" logic. MySQL and Oracle + # "unique constraints" are actually unique indexes (with possible + # exception of a unique that is a dupe of another one in the case + # of Oracle). make sure # they aren't duplicated. + idx_names = set([idx.name for idx in reflected.indexes]) + uq_names = set([ + uq.name for uq in reflected.constraints + if isinstance(uq, sa.UniqueConstraint)]).difference( + ['unique_c_a_b']) + + assert not idx_names.intersection(uq_names) + if names_that_duplicate_index: + eq_(names_that_duplicate_index, idx_names) + eq_(uq_names, set()) + + @testing.requires.check_constraint_reflection + def test_get_check_constraints(self): + self._test_get_check_constraints() + + @testing.requires.check_constraint_reflection + @testing.requires.schemas + def test_get_check_constraints_schema(self): + self._test_get_check_constraints(schema=testing.config.test_schema) + + @testing.provide_metadata + def _test_get_check_constraints(self, schema=None): + orig_meta = self.metadata + Table( + 'sa_cc', orig_meta, + Column('a', Integer()), + sa.CheckConstraint('a > 1 AND a < 5', name='cc1'), + sa.CheckConstraint('a = 1 OR (a > 2 AND a < 5)', name='cc2'), + schema=schema + ) + + orig_meta.create_all() + + inspector = inspect(orig_meta.bind) + reflected = sorted( + inspector.get_check_constraints('sa_cc', schema=schema), + key=operator.itemgetter('name') + ) + + # trying to minimize effect of quoting, parenthesis, etc. + # may need to add more to this as new dialects get CHECK + # constraint reflection support + def normalize(sqltext): + return " ".join(re.findall(r"and|\d|=|a|or|<|>", sqltext.lower(), re.I)) + + reflected = [ + {"name": item["name"], + "sqltext": normalize(item["sqltext"])} + for item in reflected + ] + eq_( + reflected, + [ + {'name': 'cc1', 'sqltext': 'a > 1 and a < 5'}, + {'name': 'cc2', 'sqltext': 'a = 1 or a > 2 and a < 5'} + ] + ) + + @testing.provide_metadata + def _test_get_view_definition(self, schema=None): + meta = self.metadata + users, addresses, dingalings = self.tables.users, \ + self.tables.email_addresses, self.tables.dingalings + view_name1 = 'users_v' + view_name2 = 'email_addresses_v' + insp = inspect(meta.bind) + v1 = insp.get_view_definition(view_name1, schema=schema) + self.assert_(v1) + v2 = insp.get_view_definition(view_name2, schema=schema) + self.assert_(v2) + + @testing.requires.view_reflection + def test_get_view_definition(self): + self._test_get_view_definition() + + @testing.requires.view_reflection + @testing.requires.schemas + def test_get_view_definition_with_schema(self): + self._test_get_view_definition(schema=testing.config.test_schema) + + @testing.only_on("postgresql", "PG specific feature") + @testing.provide_metadata + def _test_get_table_oid(self, table_name, schema=None): + meta = self.metadata + users, addresses, dingalings = self.tables.users, \ + self.tables.email_addresses, self.tables.dingalings + insp = inspect(meta.bind) + oid = insp.get_table_oid(table_name, schema) + self.assert_(isinstance(oid, int)) + + def test_get_table_oid(self): + self._test_get_table_oid('users') + + @testing.requires.schemas + def test_get_table_oid_with_schema(self): + self._test_get_table_oid('users', schema=testing.config.test_schema) + + @testing.requires.table_reflection + @testing.provide_metadata + def test_autoincrement_col(self): + """test that 'autoincrement' is reflected according to sqla's policy. + + Don't mark this test as unsupported for any backend ! + + (technically it fails with MySQL InnoDB since "id" comes before "id2") + + A backend is better off not returning "autoincrement" at all, + instead of potentially returning "False" for an auto-incrementing + primary key column. + + """ + + meta = self.metadata + insp = inspect(meta.bind) + + for tname, cname in [ + ('users', 'user_id'), + ('email_addresses', 'address_id'), + ('dingalings', 'dingaling_id'), + ]: + cols = insp.get_columns(tname) + id_ = {c['name']: c for c in cols}[cname] + assert id_.get('autoincrement', True) + + +class NormalizedNameTest(fixtures.TablesTest): + __requires__ = 'denormalized_names', + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table( + quoted_name('t1', quote=True), metadata, + Column('id', Integer, primary_key=True), + ) + Table( + quoted_name('t2', quote=True), metadata, + Column('id', Integer, primary_key=True), + Column('t1id', ForeignKey('t1.id')) + ) + + def test_reflect_lowercase_forced_tables(self): + + m2 = MetaData(testing.db) + t2_ref = Table(quoted_name('t2', quote=True), m2, autoload=True) + t1_ref = m2.tables['t1'] + assert t2_ref.c.t1id.references(t1_ref.c.id) + + m3 = MetaData(testing.db) + m3.reflect(only=lambda name, m: name.lower() in ('t1', 't2')) + assert m3.tables['t2'].c.t1id.references(m3.tables['t1'].c.id) + + def test_get_table_names(self): + tablenames = [ + t for t in inspect(testing.db).get_table_names() + if t.lower() in ("t1", "t2")] + + eq_(tablenames[0].upper(), tablenames[0].lower()) + eq_(tablenames[1].upper(), tablenames[1].lower()) + + +__all__ = ('ComponentReflectionTest', 'HasTableTest', 'NormalizedNameTest') diff --git a/venv/Lib/site-packages/sqlalchemy/testing/suite/test_results.py b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_results.py new file mode 100644 index 0000000..f464d47 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_results.py @@ -0,0 +1,367 @@ +from .. import fixtures, config +from ..config import requirements +from .. import exclusions +from ..assertions import eq_ +from .. import engines +from ... import testing + +from sqlalchemy import Integer, String, select, util, sql, DateTime, text, func +import datetime +from ..schema import Table, Column + + +class RowFetchTest(fixtures.TablesTest): + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table('plain_pk', metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50)) + ) + Table('has_dates', metadata, + Column('id', Integer, primary_key=True), + Column('today', DateTime) + ) + + @classmethod + def insert_data(cls): + config.db.execute( + cls.tables.plain_pk.insert(), + [ + {"id": 1, "data": "d1"}, + {"id": 2, "data": "d2"}, + {"id": 3, "data": "d3"}, + ] + ) + + config.db.execute( + cls.tables.has_dates.insert(), + [ + {"id": 1, "today": datetime.datetime(2006, 5, 12, 12, 0, 0)} + ] + ) + + def test_via_string(self): + row = config.db.execute( + self.tables.plain_pk.select(). + order_by(self.tables.plain_pk.c.id) + ).first() + + eq_( + row['id'], 1 + ) + eq_( + row['data'], "d1" + ) + + def test_via_int(self): + row = config.db.execute( + self.tables.plain_pk.select(). + order_by(self.tables.plain_pk.c.id) + ).first() + + eq_( + row[0], 1 + ) + eq_( + row[1], "d1" + ) + + def test_via_col_object(self): + row = config.db.execute( + self.tables.plain_pk.select(). + order_by(self.tables.plain_pk.c.id) + ).first() + + eq_( + row[self.tables.plain_pk.c.id], 1 + ) + eq_( + row[self.tables.plain_pk.c.data], "d1" + ) + + @requirements.duplicate_names_in_cursor_description + def test_row_with_dupe_names(self): + result = config.db.execute( + select([self.tables.plain_pk.c.data, + self.tables.plain_pk.c.data.label('data')]). + order_by(self.tables.plain_pk.c.id) + ) + row = result.first() + eq_(result.keys(), ['data', 'data']) + eq_(row, ('d1', 'd1')) + + def test_row_w_scalar_select(self): + """test that a scalar select as a column is returned as such + and that type conversion works OK. + + (this is half a SQLAlchemy Core test and half to catch database + backends that may have unusual behavior with scalar selects.) + + """ + datetable = self.tables.has_dates + s = select([datetable.alias('x').c.today]).as_scalar() + s2 = select([datetable.c.id, s.label('somelabel')]) + row = config.db.execute(s2).first() + + eq_(row['somelabel'], datetime.datetime(2006, 5, 12, 12, 0, 0)) + + +class PercentSchemaNamesTest(fixtures.TablesTest): + """tests using percent signs, spaces in table and column names. + + This is a very fringe use case, doesn't work for MySQL + or PostgreSQL. the requirement, "percent_schema_names", + is marked "skip" by default. + + """ + + __requires__ = ('percent_schema_names', ) + + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + cls.tables.percent_table = Table('percent%table', metadata, + Column("percent%", Integer), + Column( + "spaces % more spaces", Integer), + ) + cls.tables.lightweight_percent_table = sql.table( + 'percent%table', sql.column("percent%"), + sql.column("spaces % more spaces") + ) + + def test_single_roundtrip(self): + percent_table = self.tables.percent_table + for params in [ + {'percent%': 5, 'spaces % more spaces': 12}, + {'percent%': 7, 'spaces % more spaces': 11}, + {'percent%': 9, 'spaces % more spaces': 10}, + {'percent%': 11, 'spaces % more spaces': 9} + ]: + config.db.execute(percent_table.insert(), params) + self._assert_table() + + def test_executemany_roundtrip(self): + percent_table = self.tables.percent_table + config.db.execute( + percent_table.insert(), + {'percent%': 5, 'spaces % more spaces': 12} + ) + config.db.execute( + percent_table.insert(), + [{'percent%': 7, 'spaces % more spaces': 11}, + {'percent%': 9, 'spaces % more spaces': 10}, + {'percent%': 11, 'spaces % more spaces': 9}] + ) + self._assert_table() + + def _assert_table(self): + percent_table = self.tables.percent_table + lightweight_percent_table = self.tables.lightweight_percent_table + + for table in ( + percent_table, + percent_table.alias(), + lightweight_percent_table, + lightweight_percent_table.alias()): + eq_( + list( + config.db.execute( + table.select().order_by(table.c['percent%']) + ) + ), + [ + (5, 12), + (7, 11), + (9, 10), + (11, 9) + ] + ) + + eq_( + list( + config.db.execute( + table.select(). + where(table.c['spaces % more spaces'].in_([9, 10])). + order_by(table.c['percent%']), + ) + ), + [ + (9, 10), + (11, 9) + ] + ) + + row = config.db.execute(table.select(). + order_by(table.c['percent%'])).first() + eq_(row['percent%'], 5) + eq_(row['spaces % more spaces'], 12) + + eq_(row[table.c['percent%']], 5) + eq_(row[table.c['spaces % more spaces']], 12) + + config.db.execute( + percent_table.update().values( + {percent_table.c['spaces % more spaces']: 15} + ) + ) + + eq_( + list( + config.db.execute( + percent_table. + select(). + order_by(percent_table.c['percent%']) + ) + ), + [(5, 15), (7, 15), (9, 15), (11, 15)] + ) + + +class ServerSideCursorsTest(fixtures.TestBase, testing.AssertsExecutionResults): + + __requires__ = ('server_side_cursors', ) + + __backend__ = True + + def _is_server_side(self, cursor): + if self.engine.dialect.driver == "psycopg2": + return cursor.name + elif self.engine.dialect.driver == 'pymysql': + sscursor = __import__('pymysql.cursors').cursors.SSCursor + return isinstance(cursor, sscursor) + elif self.engine.dialect.driver == "mysqldb": + sscursor = __import__('MySQLdb.cursors').cursors.SSCursor + return isinstance(cursor, sscursor) + else: + return False + + def _fixture(self, server_side_cursors): + self.engine = engines.testing_engine( + options={'server_side_cursors': server_side_cursors} + ) + return self.engine + + def tearDown(self): + engines.testing_reaper.close_all() + self.engine.dispose() + + def test_global_string(self): + engine = self._fixture(True) + result = engine.execute('select 1') + assert self._is_server_side(result.cursor) + + def test_global_text(self): + engine = self._fixture(True) + result = engine.execute(text('select 1')) + assert self._is_server_side(result.cursor) + + def test_global_expr(self): + engine = self._fixture(True) + result = engine.execute(select([1])) + assert self._is_server_side(result.cursor) + + def test_global_off_explicit(self): + engine = self._fixture(False) + result = engine.execute(text('select 1')) + + # It should be off globally ... + + assert not self._is_server_side(result.cursor) + + def test_stmt_option(self): + engine = self._fixture(False) + + s = select([1]).execution_options(stream_results=True) + result = engine.execute(s) + + # ... but enabled for this one. + + assert self._is_server_side(result.cursor) + + def test_conn_option(self): + engine = self._fixture(False) + + # and this one + result = \ + engine.connect().execution_options(stream_results=True).\ + execute('select 1' + ) + assert self._is_server_side(result.cursor) + + def test_stmt_enabled_conn_option_disabled(self): + engine = self._fixture(False) + + s = select([1]).execution_options(stream_results=True) + + # not this one + result = \ + engine.connect().execution_options(stream_results=False).\ + execute(s) + assert not self._is_server_side(result.cursor) + + def test_stmt_option_disabled(self): + engine = self._fixture(True) + s = select([1]).execution_options(stream_results=False) + result = engine.execute(s) + assert not self._is_server_side(result.cursor) + + def test_aliases_and_ss(self): + engine = self._fixture(False) + s1 = select([1]).execution_options(stream_results=True).alias() + result = engine.execute(s1) + assert self._is_server_side(result.cursor) + + # s1's options shouldn't affect s2 when s2 is used as a + # from_obj. + s2 = select([1], from_obj=s1) + result = engine.execute(s2) + assert not self._is_server_side(result.cursor) + + def test_for_update_expr(self): + engine = self._fixture(True) + s1 = select([1], for_update=True) + result = engine.execute(s1) + assert self._is_server_side(result.cursor) + + def test_for_update_string(self): + engine = self._fixture(True) + result = engine.execute('SELECT 1 FOR UPDATE') + assert self._is_server_side(result.cursor) + + def test_text_no_ss(self): + engine = self._fixture(False) + s = text('select 42') + result = engine.execute(s) + assert not self._is_server_side(result.cursor) + + def test_text_ss_option(self): + engine = self._fixture(False) + s = text('select 42').execution_options(stream_results=True) + result = engine.execute(s) + assert self._is_server_side(result.cursor) + + @testing.provide_metadata + def test_roundtrip(self): + md = self.metadata + + engine = self._fixture(True) + test_table = Table('test_table', md, + Column('id', Integer, primary_key=True), + Column('data', String(50))) + test_table.create(checkfirst=True) + test_table.insert().execute(data='data1') + test_table.insert().execute(data='data2') + eq_(test_table.select().order_by(test_table.c.id).execute().fetchall(), + [(1, 'data1'), (2, 'data2')]) + test_table.update().where( + test_table.c.id == 2).values( + data=test_table.c.data + + ' updated').execute() + eq_(test_table.select().order_by(test_table.c.id).execute().fetchall(), + [(1, 'data1'), (2, 'data2 updated')]) + test_table.delete().execute() + eq_(select([func.count('*')]).select_from(test_table).scalar(), 0) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/suite/test_select.py b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_select.py new file mode 100644 index 0000000..05b9162 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_select.py @@ -0,0 +1,515 @@ +from .. import fixtures, config +from ..assertions import eq_ + +from sqlalchemy import util +from sqlalchemy import Integer, String, select, func, bindparam, union, tuple_ +from sqlalchemy import testing +from sqlalchemy import literal_column + +from ..schema import Table, Column + + +class CollateTest(fixtures.TablesTest): + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table("some_table", metadata, + Column('id', Integer, primary_key=True), + Column('data', String(100)) + ) + + @classmethod + def insert_data(cls): + config.db.execute( + cls.tables.some_table.insert(), + [ + {"id": 1, "data": "collate data1"}, + {"id": 2, "data": "collate data2"}, + ] + ) + + def _assert_result(self, select, result): + eq_( + config.db.execute(select).fetchall(), + result + ) + + @testing.requires.order_by_collation + def test_collate_order_by(self): + collation = testing.requires.get_order_by_collation(testing.config) + + self._assert_result( + select([self.tables.some_table]). + order_by(self.tables.some_table.c.data.collate(collation).asc()), + [ + (1, "collate data1"), + (2, "collate data2"), + ] + ) + + +class OrderByLabelTest(fixtures.TablesTest): + """Test the dialect sends appropriate ORDER BY expressions when + labels are used. + + This essentially exercises the "supports_simple_order_by_label" + setting. + + """ + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table("some_table", metadata, + Column('id', Integer, primary_key=True), + Column('x', Integer), + Column('y', Integer), + Column('q', String(50)), + Column('p', String(50)) + ) + + @classmethod + def insert_data(cls): + config.db.execute( + cls.tables.some_table.insert(), + [ + {"id": 1, "x": 1, "y": 2, "q": "q1", "p": "p3"}, + {"id": 2, "x": 2, "y": 3, "q": "q2", "p": "p2"}, + {"id": 3, "x": 3, "y": 4, "q": "q3", "p": "p1"}, + ] + ) + + def _assert_result(self, select, result): + eq_( + config.db.execute(select).fetchall(), + result + ) + + def test_plain(self): + table = self.tables.some_table + lx = table.c.x.label('lx') + self._assert_result( + select([lx]).order_by(lx), + [(1, ), (2, ), (3, )] + ) + + def test_composed_int(self): + table = self.tables.some_table + lx = (table.c.x + table.c.y).label('lx') + self._assert_result( + select([lx]).order_by(lx), + [(3, ), (5, ), (7, )] + ) + + def test_composed_multiple(self): + table = self.tables.some_table + lx = (table.c.x + table.c.y).label('lx') + ly = (func.lower(table.c.q) + table.c.p).label('ly') + self._assert_result( + select([lx, ly]).order_by(lx, ly.desc()), + [(3, util.u('q1p3')), (5, util.u('q2p2')), (7, util.u('q3p1'))] + ) + + def test_plain_desc(self): + table = self.tables.some_table + lx = table.c.x.label('lx') + self._assert_result( + select([lx]).order_by(lx.desc()), + [(3, ), (2, ), (1, )] + ) + + def test_composed_int_desc(self): + table = self.tables.some_table + lx = (table.c.x + table.c.y).label('lx') + self._assert_result( + select([lx]).order_by(lx.desc()), + [(7, ), (5, ), (3, )] + ) + + @testing.requires.group_by_complex_expression + def test_group_by_composed(self): + table = self.tables.some_table + expr = (table.c.x + table.c.y).label('lx') + stmt = select([func.count(table.c.id), expr]).group_by(expr).order_by(expr) + self._assert_result( + stmt, + [(1, 3), (1, 5), (1, 7)] + ) + + +class LimitOffsetTest(fixtures.TablesTest): + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table("some_table", metadata, + Column('id', Integer, primary_key=True), + Column('x', Integer), + Column('y', Integer)) + + @classmethod + def insert_data(cls): + config.db.execute( + cls.tables.some_table.insert(), + [ + {"id": 1, "x": 1, "y": 2}, + {"id": 2, "x": 2, "y": 3}, + {"id": 3, "x": 3, "y": 4}, + {"id": 4, "x": 4, "y": 5}, + ] + ) + + def _assert_result(self, select, result, params=()): + eq_( + config.db.execute(select, params).fetchall(), + result + ) + + def test_simple_limit(self): + table = self.tables.some_table + self._assert_result( + select([table]).order_by(table.c.id).limit(2), + [(1, 1, 2), (2, 2, 3)] + ) + + @testing.requires.offset + def test_simple_offset(self): + table = self.tables.some_table + self._assert_result( + select([table]).order_by(table.c.id).offset(2), + [(3, 3, 4), (4, 4, 5)] + ) + + @testing.requires.offset + def test_simple_limit_offset(self): + table = self.tables.some_table + self._assert_result( + select([table]).order_by(table.c.id).limit(2).offset(1), + [(2, 2, 3), (3, 3, 4)] + ) + + @testing.requires.offset + def test_limit_offset_nobinds(self): + """test that 'literal binds' mode works - no bound params.""" + + table = self.tables.some_table + stmt = select([table]).order_by(table.c.id).limit(2).offset(1) + sql = stmt.compile( + dialect=config.db.dialect, + compile_kwargs={"literal_binds": True}) + sql = str(sql) + + self._assert_result( + sql, + [(2, 2, 3), (3, 3, 4)] + ) + + @testing.requires.bound_limit_offset + def test_bound_limit(self): + table = self.tables.some_table + self._assert_result( + select([table]).order_by(table.c.id).limit(bindparam('l')), + [(1, 1, 2), (2, 2, 3)], + params={"l": 2} + ) + + @testing.requires.bound_limit_offset + def test_bound_offset(self): + table = self.tables.some_table + self._assert_result( + select([table]).order_by(table.c.id).offset(bindparam('o')), + [(3, 3, 4), (4, 4, 5)], + params={"o": 2} + ) + + @testing.requires.bound_limit_offset + def test_bound_limit_offset(self): + table = self.tables.some_table + self._assert_result( + select([table]).order_by(table.c.id). + limit(bindparam("l")).offset(bindparam("o")), + [(2, 2, 3), (3, 3, 4)], + params={"l": 2, "o": 1} + ) + + +class CompoundSelectTest(fixtures.TablesTest): + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table("some_table", metadata, + Column('id', Integer, primary_key=True), + Column('x', Integer), + Column('y', Integer)) + + @classmethod + def insert_data(cls): + config.db.execute( + cls.tables.some_table.insert(), + [ + {"id": 1, "x": 1, "y": 2}, + {"id": 2, "x": 2, "y": 3}, + {"id": 3, "x": 3, "y": 4}, + {"id": 4, "x": 4, "y": 5}, + ] + ) + + def _assert_result(self, select, result, params=()): + eq_( + config.db.execute(select, params).fetchall(), + result + ) + + def test_plain_union(self): + table = self.tables.some_table + s1 = select([table]).where(table.c.id == 2) + s2 = select([table]).where(table.c.id == 3) + + u1 = union(s1, s2) + self._assert_result( + u1.order_by(u1.c.id), + [(2, 2, 3), (3, 3, 4)] + ) + + def test_select_from_plain_union(self): + table = self.tables.some_table + s1 = select([table]).where(table.c.id == 2) + s2 = select([table]).where(table.c.id == 3) + + u1 = union(s1, s2).alias().select() + self._assert_result( + u1.order_by(u1.c.id), + [(2, 2, 3), (3, 3, 4)] + ) + + @testing.requires.order_by_col_from_union + @testing.requires.parens_in_union_contained_select_w_limit_offset + def test_limit_offset_selectable_in_unions(self): + table = self.tables.some_table + s1 = select([table]).where(table.c.id == 2).\ + limit(1).order_by(table.c.id) + s2 = select([table]).where(table.c.id == 3).\ + limit(1).order_by(table.c.id) + + u1 = union(s1, s2).limit(2) + self._assert_result( + u1.order_by(u1.c.id), + [(2, 2, 3), (3, 3, 4)] + ) + + @testing.requires.parens_in_union_contained_select_wo_limit_offset + def test_order_by_selectable_in_unions(self): + table = self.tables.some_table + s1 = select([table]).where(table.c.id == 2).\ + order_by(table.c.id) + s2 = select([table]).where(table.c.id == 3).\ + order_by(table.c.id) + + u1 = union(s1, s2).limit(2) + self._assert_result( + u1.order_by(u1.c.id), + [(2, 2, 3), (3, 3, 4)] + ) + + def test_distinct_selectable_in_unions(self): + table = self.tables.some_table + s1 = select([table]).where(table.c.id == 2).\ + distinct() + s2 = select([table]).where(table.c.id == 3).\ + distinct() + + u1 = union(s1, s2).limit(2) + self._assert_result( + u1.order_by(u1.c.id), + [(2, 2, 3), (3, 3, 4)] + ) + + @testing.requires.parens_in_union_contained_select_w_limit_offset + def test_limit_offset_in_unions_from_alias(self): + table = self.tables.some_table + s1 = select([table]).where(table.c.id == 2).\ + limit(1).order_by(table.c.id) + s2 = select([table]).where(table.c.id == 3).\ + limit(1).order_by(table.c.id) + + # this necessarily has double parens + u1 = union(s1, s2).alias() + self._assert_result( + u1.select().limit(2).order_by(u1.c.id), + [(2, 2, 3), (3, 3, 4)] + ) + + def test_limit_offset_aliased_selectable_in_unions(self): + table = self.tables.some_table + s1 = select([table]).where(table.c.id == 2).\ + limit(1).order_by(table.c.id).alias().select() + s2 = select([table]).where(table.c.id == 3).\ + limit(1).order_by(table.c.id).alias().select() + + u1 = union(s1, s2).limit(2) + self._assert_result( + u1.order_by(u1.c.id), + [(2, 2, 3), (3, 3, 4)] + ) + + +class ExpandingBoundInTest(fixtures.TablesTest): + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table("some_table", metadata, + Column('id', Integer, primary_key=True), + Column('x', Integer), + Column('y', Integer)) + + @classmethod + def insert_data(cls): + config.db.execute( + cls.tables.some_table.insert(), + [ + {"id": 1, "x": 1, "y": 2}, + {"id": 2, "x": 2, "y": 3}, + {"id": 3, "x": 3, "y": 4}, + {"id": 4, "x": 4, "y": 5}, + ] + ) + + def _assert_result(self, select, result, params=()): + eq_( + config.db.execute(select, params).fetchall(), + result + ) + + def test_bound_in_scalar(self): + table = self.tables.some_table + + stmt = select([table.c.id]).where( + table.c.x.in_(bindparam('q', expanding=True))).order_by(table.c.id) + + self._assert_result( + stmt, + [(2, ), (3, ), (4, )], + params={"q": [2, 3, 4]}, + ) + + @testing.requires.tuple_in + def test_bound_in_two_tuple(self): + table = self.tables.some_table + + stmt = select([table.c.id]).where( + tuple_(table.c.x, table.c.y).in_(bindparam('q', expanding=True))).order_by(table.c.id) + + self._assert_result( + stmt, + [(2, ), (3, ), (4, )], + params={"q": [(2, 3), (3, 4), (4, 5)]}, + ) + + +class LikeFunctionsTest(fixtures.TablesTest): + __backend__ = True + + run_inserts = 'once' + run_deletes = None + + @classmethod + def define_tables(cls, metadata): + Table("some_table", metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50))) + + @classmethod + def insert_data(cls): + config.db.execute( + cls.tables.some_table.insert(), + [ + {"id": 1, "data": "abcdefg"}, + {"id": 2, "data": "ab/cdefg"}, + {"id": 3, "data": "ab%cdefg"}, + {"id": 4, "data": "ab_cdefg"}, + {"id": 5, "data": "abcde/fg"}, + {"id": 6, "data": "abcde%fg"}, + {"id": 7, "data": "ab#cdefg"}, + {"id": 8, "data": "ab9cdefg"}, + {"id": 9, "data": "abcde#fg"}, + {"id": 10, "data": "abcd9fg"}, + ] + ) + + def _test(self, expr, expected): + some_table = self.tables.some_table + + with config.db.connect() as conn: + rows = { + value for value, in + conn.execute(select([some_table.c.id]).where(expr)) + } + + eq_(rows, expected) + + def test_startswith_unescaped(self): + col = self.tables.some_table.c.data + self._test(col.startswith("ab%c"), {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + + def test_startswith_autoescape(self): + col = self.tables.some_table.c.data + self._test(col.startswith("ab%c", autoescape=True), {3}) + + def test_startswith_sqlexpr(self): + col = self.tables.some_table.c.data + self._test( + col.startswith(literal_column("'ab%c'")), + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + + def test_startswith_escape(self): + col = self.tables.some_table.c.data + self._test(col.startswith("ab##c", escape="#"), {7}) + + def test_startswith_autoescape_escape(self): + col = self.tables.some_table.c.data + self._test(col.startswith("ab%c", autoescape=True, escape="#"), {3}) + self._test(col.startswith("ab#c", autoescape=True, escape="#"), {7}) + + def test_endswith_unescaped(self): + col = self.tables.some_table.c.data + self._test(col.endswith("e%fg"), {1, 2, 3, 4, 5, 6, 7, 8, 9}) + + def test_endswith_sqlexpr(self): + col = self.tables.some_table.c.data + self._test(col.endswith(literal_column("'e%fg'")), + {1, 2, 3, 4, 5, 6, 7, 8, 9}) + + def test_endswith_autoescape(self): + col = self.tables.some_table.c.data + self._test(col.endswith("e%fg", autoescape=True), {6}) + + def test_endswith_escape(self): + col = self.tables.some_table.c.data + self._test(col.endswith("e##fg", escape="#"), {9}) + + def test_endswith_autoescape_escape(self): + col = self.tables.some_table.c.data + self._test(col.endswith("e%fg", autoescape=True, escape="#"), {6}) + self._test(col.endswith("e#fg", autoescape=True, escape="#"), {9}) + + def test_contains_unescaped(self): + col = self.tables.some_table.c.data + self._test(col.contains("b%cde"), {1, 2, 3, 4, 5, 6, 7, 8, 9}) + + def test_contains_autoescape(self): + col = self.tables.some_table.c.data + self._test(col.contains("b%cde", autoescape=True), {3}) + + def test_contains_escape(self): + col = self.tables.some_table.c.data + self._test(col.contains("b##cde", escape="#"), {7}) + + def test_contains_autoescape_escape(self): + col = self.tables.some_table.c.data + self._test(col.contains("b%cd", autoescape=True, escape="#"), {3}) + self._test(col.contains("b#cd", autoescape=True, escape="#"), {7}) + + diff --git a/venv/Lib/site-packages/sqlalchemy/testing/suite/test_sequence.py b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_sequence.py new file mode 100644 index 0000000..f1c00de --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_sequence.py @@ -0,0 +1,148 @@ +from .. import fixtures, config +from ..config import requirements +from ..assertions import eq_ +from ... import testing + +from ... import Integer, String, Sequence, schema, MetaData + +from ..schema import Table, Column + + +class SequenceTest(fixtures.TablesTest): + __requires__ = ('sequences',) + __backend__ = True + + run_create_tables = 'each' + + @classmethod + def define_tables(cls, metadata): + Table('seq_pk', metadata, + Column('id', Integer, Sequence('tab_id_seq'), primary_key=True), + Column('data', String(50)) + ) + + Table('seq_opt_pk', metadata, + Column('id', Integer, Sequence('tab_id_seq', optional=True), + primary_key=True), + Column('data', String(50)) + ) + + def test_insert_roundtrip(self): + config.db.execute( + self.tables.seq_pk.insert(), + data="some data" + ) + self._assert_round_trip(self.tables.seq_pk, config.db) + + def test_insert_lastrowid(self): + r = config.db.execute( + self.tables.seq_pk.insert(), + data="some data" + ) + eq_( + r.inserted_primary_key, + [1] + ) + + def test_nextval_direct(self): + r = config.db.execute( + self.tables.seq_pk.c.id.default + ) + eq_( + r, 1 + ) + + @requirements.sequences_optional + def test_optional_seq(self): + r = config.db.execute( + self.tables.seq_opt_pk.insert(), + data="some data" + ) + eq_( + r.inserted_primary_key, + [1] + ) + + def _assert_round_trip(self, table, conn): + row = conn.execute(table.select()).first() + eq_( + row, + (1, "some data") + ) + + +class SequenceCompilerTest(testing.AssertsCompiledSQL, fixtures.TestBase): + __requires__ = ('sequences',) + __backend__ = True + + def test_literal_binds_inline_compile(self): + table = Table( + 'x', MetaData(), + Column('y', Integer, Sequence('y_seq')), + Column('q', Integer)) + + stmt = table.insert().values(q=5) + + seq_nextval = testing.db.dialect.statement_compiler( + statement=None, dialect=testing.db.dialect).visit_sequence( + Sequence("y_seq")) + self.assert_compile( + stmt, + "INSERT INTO x (y, q) VALUES (%s, 5)" % (seq_nextval, ), + literal_binds=True, + dialect=testing.db.dialect) + + +class HasSequenceTest(fixtures.TestBase): + __requires__ = 'sequences', + __backend__ = True + + def test_has_sequence(self): + s1 = Sequence('user_id_seq') + testing.db.execute(schema.CreateSequence(s1)) + try: + eq_(testing.db.dialect.has_sequence(testing.db, + 'user_id_seq'), True) + finally: + testing.db.execute(schema.DropSequence(s1)) + + @testing.requires.schemas + def test_has_sequence_schema(self): + s1 = Sequence('user_id_seq', schema=config.test_schema) + testing.db.execute(schema.CreateSequence(s1)) + try: + eq_(testing.db.dialect.has_sequence( + testing.db, 'user_id_seq', schema=config.test_schema), True) + finally: + testing.db.execute(schema.DropSequence(s1)) + + def test_has_sequence_neg(self): + eq_(testing.db.dialect.has_sequence(testing.db, 'user_id_seq'), + False) + + @testing.requires.schemas + def test_has_sequence_schemas_neg(self): + eq_(testing.db.dialect.has_sequence(testing.db, 'user_id_seq', + schema=config.test_schema), + False) + + @testing.requires.schemas + def test_has_sequence_default_not_in_remote(self): + s1 = Sequence('user_id_seq') + testing.db.execute(schema.CreateSequence(s1)) + try: + eq_(testing.db.dialect.has_sequence(testing.db, 'user_id_seq', + schema=config.test_schema), + False) + finally: + testing.db.execute(schema.DropSequence(s1)) + + @testing.requires.schemas + def test_has_sequence_remote_not_in_default(self): + s1 = Sequence('user_id_seq', schema=config.test_schema) + testing.db.execute(schema.CreateSequence(s1)) + try: + eq_(testing.db.dialect.has_sequence(testing.db, 'user_id_seq'), + False) + finally: + testing.db.execute(schema.DropSequence(s1)) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/suite/test_types.py b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_types.py new file mode 100644 index 0000000..04e0b3b --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_types.py @@ -0,0 +1,1005 @@ +# coding: utf-8 + +from .. import fixtures, config +from ..assertions import eq_ +from ..config import requirements +from sqlalchemy import Integer, Unicode, UnicodeText, select, TIMESTAMP +from sqlalchemy import Date, DateTime, Time, MetaData, String, \ + Text, Numeric, Float, literal, Boolean, cast, null, JSON, and_, \ + type_coerce, BigInteger +from ..schema import Table, Column +from ... import testing +import decimal +import datetime +from ...util import u +from ... import util + + +class _LiteralRoundTripFixture(object): + @testing.provide_metadata + def _literal_round_trip(self, type_, input_, output, filter_=None): + """test literal rendering """ + + # for literal, we test the literal render in an INSERT + # into a typed column. we can then SELECT it back as its + # official type; ideally we'd be able to use CAST here + # but MySQL in particular can't CAST fully + t = Table('t', self.metadata, Column('x', type_)) + t.create() + + for value in input_: + ins = t.insert().values(x=literal(value)).compile( + dialect=testing.db.dialect, + compile_kwargs=dict(literal_binds=True) + ) + testing.db.execute(ins) + + for row in t.select().execute(): + value = row[0] + if filter_ is not None: + value = filter_(value) + assert value in output + + +class _UnicodeFixture(_LiteralRoundTripFixture): + __requires__ = 'unicode_data', + + data = u("Alors vous imaginez ma surprise, au lever du jour, " + "quand une drôle de petite voix m’a réveillé. Elle " + "disait: « S’il vous plaît… dessine-moi un mouton! »") + + @classmethod + def define_tables(cls, metadata): + Table('unicode_table', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('unicode_data', cls.datatype), + ) + + def test_round_trip(self): + unicode_table = self.tables.unicode_table + + config.db.execute( + unicode_table.insert(), + { + 'unicode_data': self.data, + } + ) + + row = config.db.execute( + select([ + unicode_table.c.unicode_data, + ]) + ).first() + + eq_( + row, + (self.data, ) + ) + assert isinstance(row[0], util.text_type) + + def test_round_trip_executemany(self): + unicode_table = self.tables.unicode_table + + config.db.execute( + unicode_table.insert(), + [ + { + 'unicode_data': self.data, + } + for i in range(3) + ] + ) + + rows = config.db.execute( + select([ + unicode_table.c.unicode_data, + ]) + ).fetchall() + eq_( + rows, + [(self.data, ) for i in range(3)] + ) + for row in rows: + assert isinstance(row[0], util.text_type) + + def _test_empty_strings(self): + unicode_table = self.tables.unicode_table + + config.db.execute( + unicode_table.insert(), + {"unicode_data": u('')} + ) + row = config.db.execute( + select([unicode_table.c.unicode_data]) + ).first() + eq_(row, (u(''),)) + + def test_literal(self): + self._literal_round_trip(self.datatype, [self.data], [self.data]) + + +class UnicodeVarcharTest(_UnicodeFixture, fixtures.TablesTest): + __requires__ = 'unicode_data', + __backend__ = True + + datatype = Unicode(255) + + @requirements.empty_strings_varchar + def test_empty_strings_varchar(self): + self._test_empty_strings() + + +class UnicodeTextTest(_UnicodeFixture, fixtures.TablesTest): + __requires__ = 'unicode_data', 'text_type' + __backend__ = True + + datatype = UnicodeText() + + @requirements.empty_strings_text + def test_empty_strings_text(self): + self._test_empty_strings() + + +class TextTest(_LiteralRoundTripFixture, fixtures.TablesTest): + __requires__ = 'text_type', + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table('text_table', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('text_data', Text), + ) + + def test_text_roundtrip(self): + text_table = self.tables.text_table + + config.db.execute( + text_table.insert(), + {"text_data": 'some text'} + ) + row = config.db.execute( + select([text_table.c.text_data]) + ).first() + eq_(row, ('some text',)) + + def test_text_empty_strings(self): + text_table = self.tables.text_table + + config.db.execute( + text_table.insert(), + {"text_data": ''} + ) + row = config.db.execute( + select([text_table.c.text_data]) + ).first() + eq_(row, ('',)) + + def test_literal(self): + self._literal_round_trip(Text, ["some text"], ["some text"]) + + def test_literal_quoting(self): + data = '''some 'text' hey "hi there" that's text''' + self._literal_round_trip(Text, [data], [data]) + + def test_literal_backslashes(self): + data = r'backslash one \ backslash two \\ end' + self._literal_round_trip(Text, [data], [data]) + + def test_literal_percentsigns(self): + data = r'percent % signs %% percent' + self._literal_round_trip(Text, [data], [data]) + + +class StringTest(_LiteralRoundTripFixture, fixtures.TestBase): + __backend__ = True + + @requirements.unbounded_varchar + def test_nolength_string(self): + metadata = MetaData() + foo = Table('foo', metadata, + Column('one', String) + ) + + foo.create(config.db) + foo.drop(config.db) + + def test_literal(self): + self._literal_round_trip(String(40), ["some text"], ["some text"]) + + def test_literal_quoting(self): + data = '''some 'text' hey "hi there" that's text''' + self._literal_round_trip(String(40), [data], [data]) + + def test_literal_backslashes(self): + data = r'backslash one \ backslash two \\ end' + self._literal_round_trip(String(40), [data], [data]) + + +class _DateFixture(_LiteralRoundTripFixture): + compare = None + + @classmethod + def define_tables(cls, metadata): + Table('date_table', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('date_data', cls.datatype), + ) + + def test_round_trip(self): + date_table = self.tables.date_table + + config.db.execute( + date_table.insert(), + {'date_data': self.data} + ) + + row = config.db.execute( + select([ + date_table.c.date_data, + ]) + ).first() + + compare = self.compare or self.data + eq_(row, + (compare, )) + assert isinstance(row[0], type(compare)) + + def test_null(self): + date_table = self.tables.date_table + + config.db.execute( + date_table.insert(), + {'date_data': None} + ) + + row = config.db.execute( + select([ + date_table.c.date_data, + ]) + ).first() + eq_(row, (None,)) + + @testing.requires.datetime_literals + def test_literal(self): + compare = self.compare or self.data + self._literal_round_trip(self.datatype, [self.data], [compare]) + + +class DateTimeTest(_DateFixture, fixtures.TablesTest): + __requires__ = 'datetime', + __backend__ = True + datatype = DateTime + data = datetime.datetime(2012, 10, 15, 12, 57, 18) + + +class DateTimeMicrosecondsTest(_DateFixture, fixtures.TablesTest): + __requires__ = 'datetime_microseconds', + __backend__ = True + datatype = DateTime + data = datetime.datetime(2012, 10, 15, 12, 57, 18, 396) + +class TimestampMicrosecondsTest(_DateFixture, fixtures.TablesTest): + __requires__ = 'timestamp_microseconds', + __backend__ = True + datatype = TIMESTAMP + data = datetime.datetime(2012, 10, 15, 12, 57, 18, 396) + + +class TimeTest(_DateFixture, fixtures.TablesTest): + __requires__ = 'time', + __backend__ = True + datatype = Time + data = datetime.time(12, 57, 18) + + +class TimeMicrosecondsTest(_DateFixture, fixtures.TablesTest): + __requires__ = 'time_microseconds', + __backend__ = True + datatype = Time + data = datetime.time(12, 57, 18, 396) + + +class DateTest(_DateFixture, fixtures.TablesTest): + __requires__ = 'date', + __backend__ = True + datatype = Date + data = datetime.date(2012, 10, 15) + + +class DateTimeCoercedToDateTimeTest(_DateFixture, fixtures.TablesTest): + __requires__ = 'date', 'date_coerces_from_datetime' + __backend__ = True + datatype = Date + data = datetime.datetime(2012, 10, 15, 12, 57, 18) + compare = datetime.date(2012, 10, 15) + + +class DateTimeHistoricTest(_DateFixture, fixtures.TablesTest): + __requires__ = 'datetime_historic', + __backend__ = True + datatype = DateTime + data = datetime.datetime(1850, 11, 10, 11, 52, 35) + + +class DateHistoricTest(_DateFixture, fixtures.TablesTest): + __requires__ = 'date_historic', + __backend__ = True + datatype = Date + data = datetime.date(1727, 4, 1) + + +class IntegerTest(_LiteralRoundTripFixture, fixtures.TestBase): + __backend__ = True + + def test_literal(self): + self._literal_round_trip(Integer, [5], [5]) + + def test_huge_int(self): + self._round_trip(BigInteger, 1376537018368127) + + @testing.provide_metadata + def _round_trip(self, datatype, data): + metadata = self.metadata + int_table = Table( + 'integer_table', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('integer_data', datatype), + ) + + metadata.create_all(config.db) + + config.db.execute( + int_table.insert(), + {'integer_data': data} + ) + + row = config.db.execute( + select([ + int_table.c.integer_data, + ]) + ).first() + + eq_(row, (data, )) + + if util.py3k: + assert isinstance(row[0], int) + else: + assert isinstance(row[0], (long, int)) + + +class NumericTest(_LiteralRoundTripFixture, fixtures.TestBase): + __backend__ = True + + @testing.emits_warning(r".*does \*not\* support Decimal objects natively") + @testing.provide_metadata + def _do_test(self, type_, input_, output, + filter_=None, check_scale=False): + metadata = self.metadata + t = Table('t', metadata, Column('x', type_)) + t.create() + t.insert().execute([{'x': x} for x in input_]) + + result = {row[0] for row in t.select().execute()} + output = set(output) + if filter_: + result = set(filter_(x) for x in result) + output = set(filter_(x) for x in output) + eq_(result, output) + if check_scale: + eq_( + [str(x) for x in result], + [str(x) for x in output], + ) + + @testing.emits_warning(r".*does \*not\* support Decimal objects natively") + def test_render_literal_numeric(self): + self._literal_round_trip( + Numeric(precision=8, scale=4), + [15.7563, decimal.Decimal("15.7563")], + [decimal.Decimal("15.7563")], + ) + + @testing.emits_warning(r".*does \*not\* support Decimal objects natively") + def test_render_literal_numeric_asfloat(self): + self._literal_round_trip( + Numeric(precision=8, scale=4, asdecimal=False), + [15.7563, decimal.Decimal("15.7563")], + [15.7563], + ) + + def test_render_literal_float(self): + self._literal_round_trip( + Float(4), + [15.7563, decimal.Decimal("15.7563")], + [15.7563, ], + filter_=lambda n: n is not None and round(n, 5) or None + ) + + @testing.requires.precision_generic_float_type + def test_float_custom_scale(self): + self._do_test( + Float(None, decimal_return_scale=7, asdecimal=True), + [15.7563827, decimal.Decimal("15.7563827")], + [decimal.Decimal("15.7563827"), ], + check_scale=True + ) + + def test_numeric_as_decimal(self): + self._do_test( + Numeric(precision=8, scale=4), + [15.7563, decimal.Decimal("15.7563")], + [decimal.Decimal("15.7563")], + ) + + def test_numeric_as_float(self): + self._do_test( + Numeric(precision=8, scale=4, asdecimal=False), + [15.7563, decimal.Decimal("15.7563")], + [15.7563], + ) + + @testing.requires.fetch_null_from_numeric + def test_numeric_null_as_decimal(self): + self._do_test( + Numeric(precision=8, scale=4), + [None], + [None], + ) + + @testing.requires.fetch_null_from_numeric + def test_numeric_null_as_float(self): + self._do_test( + Numeric(precision=8, scale=4, asdecimal=False), + [None], + [None], + ) + + @testing.requires.floats_to_four_decimals + def test_float_as_decimal(self): + self._do_test( + Float(precision=8, asdecimal=True), + [15.7563, decimal.Decimal("15.7563"), None], + [decimal.Decimal("15.7563"), None], + ) + + def test_float_as_float(self): + self._do_test( + Float(precision=8), + [15.7563, decimal.Decimal("15.7563")], + [15.7563], + filter_=lambda n: n is not None and round(n, 5) or None + ) + + def test_float_coerce_round_trip(self): + expr = 15.7563 + + val = testing.db.scalar( + select([literal(expr)]) + ) + eq_(val, expr) + + # TODO: this one still breaks on MySQL + # def test_decimal_coerce_round_trip(self): + # expr = decimal.Decimal("15.7563") + # + # val = testing.db.scalar( + # select([literal(expr)]) + # ) + # eq_(val, expr) + + @testing.requires.precision_numerics_general + def test_precision_decimal(self): + numbers = set([ + decimal.Decimal("54.234246451650"), + decimal.Decimal("0.004354"), + decimal.Decimal("900.0"), + ]) + + self._do_test( + Numeric(precision=18, scale=12), + numbers, + numbers, + ) + + @testing.requires.precision_numerics_enotation_large + def test_enotation_decimal(self): + """test exceedingly small decimals. + + Decimal reports values with E notation when the exponent + is greater than 6. + + """ + + numbers = set([ + decimal.Decimal('1E-2'), + decimal.Decimal('1E-3'), + decimal.Decimal('1E-4'), + decimal.Decimal('1E-5'), + decimal.Decimal('1E-6'), + decimal.Decimal('1E-7'), + decimal.Decimal('1E-8'), + decimal.Decimal("0.01000005940696"), + decimal.Decimal("0.00000005940696"), + decimal.Decimal("0.00000000000696"), + decimal.Decimal("0.70000000000696"), + decimal.Decimal("696E-12"), + ]) + self._do_test( + Numeric(precision=18, scale=14), + numbers, + numbers + ) + + @testing.requires.precision_numerics_enotation_large + def test_enotation_decimal_large(self): + """test exceedingly large decimals. + + """ + + numbers = set([ + decimal.Decimal('4E+8'), + decimal.Decimal("5748E+15"), + decimal.Decimal('1.521E+15'), + decimal.Decimal('00000000000000.1E+12'), + ]) + self._do_test( + Numeric(precision=25, scale=2), + numbers, + numbers + ) + + @testing.requires.precision_numerics_many_significant_digits + def test_many_significant_digits(self): + numbers = set([ + decimal.Decimal("31943874831932418390.01"), + decimal.Decimal("319438950232418390.273596"), + decimal.Decimal("87673.594069654243"), + ]) + self._do_test( + Numeric(precision=38, scale=12), + numbers, + numbers + ) + + @testing.requires.precision_numerics_retains_significant_digits + def test_numeric_no_decimal(self): + numbers = set([ + decimal.Decimal("1.000") + ]) + self._do_test( + Numeric(precision=5, scale=3), + numbers, + numbers, + check_scale=True + ) + + +class BooleanTest(_LiteralRoundTripFixture, fixtures.TablesTest): + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table('boolean_table', metadata, + Column('id', Integer, primary_key=True, autoincrement=False), + Column('value', Boolean), + Column('unconstrained_value', Boolean(create_constraint=False)), + ) + + def test_render_literal_bool(self): + self._literal_round_trip( + Boolean(), + [True, False], + [True, False] + ) + + def test_round_trip(self): + boolean_table = self.tables.boolean_table + + config.db.execute( + boolean_table.insert(), + { + 'id': 1, + 'value': True, + 'unconstrained_value': False + } + ) + + row = config.db.execute( + select([ + boolean_table.c.value, + boolean_table.c.unconstrained_value + ]) + ).first() + + eq_( + row, + (True, False) + ) + assert isinstance(row[0], bool) + + def test_null(self): + boolean_table = self.tables.boolean_table + + config.db.execute( + boolean_table.insert(), + { + 'id': 1, + 'value': None, + 'unconstrained_value': None + } + ) + + row = config.db.execute( + select([ + boolean_table.c.value, + boolean_table.c.unconstrained_value + ]) + ).first() + + eq_( + row, + (None, None) + ) + + def test_whereclause(self): + # testing "WHERE <column>" renders a compatible expression + boolean_table = self.tables.boolean_table + + with config.db.connect() as conn: + conn.execute( + boolean_table.insert(), + [ + {'id': 1, 'value': True, 'unconstrained_value': True}, + {'id': 2, 'value': False, 'unconstrained_value': False} + ] + ) + + eq_( + conn.scalar( + select([boolean_table.c.id]).where(boolean_table.c.value) + ), + 1 + ) + eq_( + conn.scalar( + select([boolean_table.c.id]).where( + boolean_table.c.unconstrained_value) + ), + 1 + ) + eq_( + conn.scalar( + select([boolean_table.c.id]).where(~boolean_table.c.value) + ), + 2 + ) + eq_( + conn.scalar( + select([boolean_table.c.id]).where( + ~boolean_table.c.unconstrained_value) + ), + 2 + ) + + + + +class JSONTest(_LiteralRoundTripFixture, fixtures.TablesTest): + __requires__ = 'json_type', + __backend__ = True + + datatype = JSON + + data1 = { + "key1": "value1", + "key2": "value2" + } + + data2 = { + "Key 'One'": "value1", + "key two": "value2", + "key three": "value ' three '" + } + + data3 = { + "key1": [1, 2, 3], + "key2": ["one", "two", "three"], + "key3": [{"four": "five"}, {"six": "seven"}] + } + + data4 = ["one", "two", "three"] + + data5 = { + "nested": { + "elem1": [ + {"a": "b", "c": "d"}, + {"e": "f", "g": "h"} + ], + "elem2": { + "elem3": {"elem4": "elem5"} + } + } + } + + data6 = { + "a": 5, + "b": "some value", + "c": {"foo": "bar"} + } + + @classmethod + def define_tables(cls, metadata): + Table('data_table', metadata, + Column('id', Integer, primary_key=True), + Column('name', String(30), nullable=False), + Column('data', cls.datatype), + Column('nulldata', cls.datatype(none_as_null=True)) + ) + + def test_round_trip_data1(self): + self._test_round_trip(self.data1) + + def _test_round_trip(self, data_element): + data_table = self.tables.data_table + + config.db.execute( + data_table.insert(), + {'name': 'row1', 'data': data_element} + ) + + row = config.db.execute( + select([ + data_table.c.data, + ]) + ).first() + + eq_(row, (data_element, )) + + def test_round_trip_none_as_sql_null(self): + col = self.tables.data_table.c['nulldata'] + + with config.db.connect() as conn: + conn.execute( + self.tables.data_table.insert(), + {"name": "r1", "data": None} + ) + + eq_( + conn.scalar( + select([self.tables.data_table.c.name]). + where(col.is_(null())) + ), + "r1" + ) + + eq_( + conn.scalar( + select([col]) + ), + None + ) + + def test_round_trip_json_null_as_json_null(self): + col = self.tables.data_table.c['data'] + + with config.db.connect() as conn: + conn.execute( + self.tables.data_table.insert(), + {"name": "r1", "data": JSON.NULL} + ) + + eq_( + conn.scalar( + select([self.tables.data_table.c.name]). + where(cast(col, String) == 'null') + ), + "r1" + ) + + eq_( + conn.scalar( + select([col]) + ), + None + ) + + def test_round_trip_none_as_json_null(self): + col = self.tables.data_table.c['data'] + + with config.db.connect() as conn: + conn.execute( + self.tables.data_table.insert(), + {"name": "r1", "data": None} + ) + + eq_( + conn.scalar( + select([self.tables.data_table.c.name]). + where(cast(col, String) == 'null') + ), + "r1" + ) + + eq_( + conn.scalar( + select([col]) + ), + None + ) + + def _criteria_fixture(self): + config.db.execute( + self.tables.data_table.insert(), + [{"name": "r1", "data": self.data1}, + {"name": "r2", "data": self.data2}, + {"name": "r3", "data": self.data3}, + {"name": "r4", "data": self.data4}, + {"name": "r5", "data": self.data5}, + {"name": "r6", "data": self.data6}] + ) + + def _test_index_criteria(self, crit, expected, test_literal=True): + self._criteria_fixture() + with config.db.connect() as conn: + stmt = select([self.tables.data_table.c.name]).where(crit) + + eq_( + conn.scalar(stmt), + expected + ) + + if test_literal: + literal_sql = str(stmt.compile( + config.db, compile_kwargs={"literal_binds": True})) + + eq_(conn.scalar(literal_sql), expected) + + def test_crit_spaces_in_key(self): + name = self.tables.data_table.c.name + col = self.tables.data_table.c['data'] + + # limit the rows here to avoid PG error + # "cannot extract field from a non-object", which is + # fixed in 9.4 but may exist in 9.3 + self._test_index_criteria( + and_( + name.in_(["r1", "r2", "r3"]), + cast(col["key two"], String) == '"value2"' + ), + "r2" + ) + + @config.requirements.json_array_indexes + def test_crit_simple_int(self): + name = self.tables.data_table.c.name + col = self.tables.data_table.c['data'] + + # limit the rows here to avoid PG error + # "cannot extract array element from a non-array", which is + # fixed in 9.4 but may exist in 9.3 + self._test_index_criteria( + and_(name == 'r4', cast(col[1], String) == '"two"'), + "r4" + ) + + def test_crit_mixed_path(self): + col = self.tables.data_table.c['data'] + self._test_index_criteria( + cast(col[("key3", 1, "six")], String) == '"seven"', + "r3" + ) + + def test_crit_string_path(self): + col = self.tables.data_table.c['data'] + self._test_index_criteria( + cast(col[("nested", "elem2", "elem3", "elem4")], String) + == '"elem5"', + "r5" + ) + + def test_crit_against_string_basic(self): + name = self.tables.data_table.c.name + col = self.tables.data_table.c['data'] + + self._test_index_criteria( + and_(name == 'r6', cast(col["b"], String) == '"some value"'), + "r6" + ) + + def test_crit_against_string_coerce_type(self): + name = self.tables.data_table.c.name + col = self.tables.data_table.c['data'] + + self._test_index_criteria( + and_(name == 'r6', + cast(col["b"], String) == type_coerce("some value", JSON)), + "r6", + test_literal=False + ) + + def test_crit_against_int_basic(self): + name = self.tables.data_table.c.name + col = self.tables.data_table.c['data'] + + self._test_index_criteria( + and_(name == 'r6', cast(col["a"], String) == '5'), + "r6" + ) + + def test_crit_against_int_coerce_type(self): + name = self.tables.data_table.c.name + col = self.tables.data_table.c['data'] + + self._test_index_criteria( + and_(name == 'r6', cast(col["a"], String) == type_coerce(5, JSON)), + "r6", + test_literal=False + ) + + def test_unicode_round_trip(self): + with config.db.connect() as conn: + conn.execute( + self.tables.data_table.insert(), + { + "name": "r1", + "data": { + util.u('réveillé'): util.u('réveillé'), + "data": {"k1": util.u('drôle')} + } + } + ) + + eq_( + conn.scalar(select([self.tables.data_table.c.data])), + { + util.u('réveillé'): util.u('réveillé'), + "data": {"k1": util.u('drôle')} + }, + ) + + def test_eval_none_flag_orm(self): + from sqlalchemy.ext.declarative import declarative_base + from sqlalchemy.orm import Session + + Base = declarative_base() + + class Data(Base): + __table__ = self.tables.data_table + + s = Session(testing.db) + + d1 = Data(name='d1', data=None, nulldata=None) + s.add(d1) + s.commit() + + s.bulk_insert_mappings( + Data, [{"name": "d2", "data": None, "nulldata": None}] + ) + eq_( + s.query( + cast(self.tables.data_table.c.data, String(convert_unicode="force")), + cast(self.tables.data_table.c.nulldata, String) + ).filter(self.tables.data_table.c.name == 'd1').first(), + ("null", None) + ) + eq_( + s.query( + cast(self.tables.data_table.c.data, String(convert_unicode="force")), + cast(self.tables.data_table.c.nulldata, String) + ).filter(self.tables.data_table.c.name == 'd2').first(), + ("null", None) + ) + + +__all__ = ('UnicodeVarcharTest', 'UnicodeTextTest', 'JSONTest', + 'DateTest', 'DateTimeTest', 'TextTest', + 'NumericTest', 'IntegerTest', + 'DateTimeHistoricTest', 'DateTimeCoercedToDateTimeTest', + 'TimeMicrosecondsTest', 'TimestampMicrosecondsTest', 'TimeTest', + 'DateTimeMicrosecondsTest', + 'DateHistoricTest', 'StringTest', 'BooleanTest') diff --git a/venv/Lib/site-packages/sqlalchemy/testing/suite/test_update_delete.py b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_update_delete.py new file mode 100644 index 0000000..e4c61e7 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/suite/test_update_delete.py @@ -0,0 +1,63 @@ +from .. import fixtures, config +from ..assertions import eq_ + +from sqlalchemy import Integer, String +from ..schema import Table, Column + + +class SimpleUpdateDeleteTest(fixtures.TablesTest): + run_deletes = 'each' + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table('plain_pk', metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50)) + ) + + @classmethod + def insert_data(cls): + config.db.execute( + cls.tables.plain_pk.insert(), + [ + {"id": 1, "data": "d1"}, + {"id": 2, "data": "d2"}, + {"id": 3, "data": "d3"}, + ] + ) + + def test_update(self): + t = self.tables.plain_pk + r = config.db.execute( + t.update().where(t.c.id == 2), + data="d2_new" + ) + assert not r.is_insert + assert not r.returns_rows + + eq_( + config.db.execute(t.select().order_by(t.c.id)).fetchall(), + [ + (1, "d1"), + (2, "d2_new"), + (3, "d3") + ] + ) + + def test_delete(self): + t = self.tables.plain_pk + r = config.db.execute( + t.delete().where(t.c.id == 2) + ) + assert not r.is_insert + assert not r.returns_rows + eq_( + config.db.execute(t.select().order_by(t.c.id)).fetchall(), + [ + (1, "d1"), + (3, "d3") + ] + ) + +__all__ = ('SimpleUpdateDeleteTest', ) diff --git a/venv/Lib/site-packages/sqlalchemy/testing/util.py b/venv/Lib/site-packages/sqlalchemy/testing/util.py new file mode 100644 index 0000000..409d3bd --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/util.py @@ -0,0 +1,280 @@ +# testing/util.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from ..util import jython, pypy, defaultdict, decorator, py2k +import decimal +import gc +import time +import random +import sys +import types + +if jython: + def jython_gc_collect(*args): + """aggressive gc.collect for tests.""" + gc.collect() + time.sleep(0.1) + gc.collect() + gc.collect() + return 0 + + # "lazy" gc, for VM's that don't GC on refcount == 0 + gc_collect = lazy_gc = jython_gc_collect +elif pypy: + def pypy_gc_collect(*args): + gc.collect() + gc.collect() + gc_collect = lazy_gc = pypy_gc_collect +else: + # assume CPython - straight gc.collect, lazy_gc() is a pass + gc_collect = gc.collect + + def lazy_gc(): + pass + + +def picklers(): + picklers = set() + if py2k: + try: + import cPickle + picklers.add(cPickle) + except ImportError: + pass + + import pickle + picklers.add(pickle) + + # yes, this thing needs this much testing + for pickle_ in picklers: + for protocol in -1, 0, 1, 2: + yield pickle_.loads, lambda d: pickle_.dumps(d, protocol) + + +def round_decimal(value, prec): + if isinstance(value, float): + return round(value, prec) + + # can also use shift() here but that is 2.6 only + return (value * decimal.Decimal("1" + "0" * prec) + ).to_integral(decimal.ROUND_FLOOR) / \ + pow(10, prec) + + +class RandomSet(set): + def __iter__(self): + l = list(set.__iter__(self)) + random.shuffle(l) + return iter(l) + + def pop(self): + index = random.randint(0, len(self) - 1) + item = list(set.__iter__(self))[index] + self.remove(item) + return item + + def union(self, other): + return RandomSet(set.union(self, other)) + + def difference(self, other): + return RandomSet(set.difference(self, other)) + + def intersection(self, other): + return RandomSet(set.intersection(self, other)) + + def copy(self): + return RandomSet(self) + + +def conforms_partial_ordering(tuples, sorted_elements): + """True if the given sorting conforms to the given partial ordering.""" + + deps = defaultdict(set) + for parent, child in tuples: + deps[parent].add(child) + for i, node in enumerate(sorted_elements): + for n in sorted_elements[i:]: + if node in deps[n]: + return False + else: + return True + + +def all_partial_orderings(tuples, elements): + edges = defaultdict(set) + for parent, child in tuples: + edges[child].add(parent) + + def _all_orderings(elements): + + if len(elements) == 1: + yield list(elements) + else: + for elem in elements: + subset = set(elements).difference([elem]) + if not subset.intersection(edges[elem]): + for sub_ordering in _all_orderings(subset): + yield [elem] + sub_ordering + + return iter(_all_orderings(elements)) + + +def function_named(fn, name): + """Return a function with a given __name__. + + Will assign to __name__ and return the original function if possible on + the Python implementation, otherwise a new function will be constructed. + + This function should be phased out as much as possible + in favor of @decorator. Tests that "generate" many named tests + should be modernized. + + """ + try: + fn.__name__ = name + except TypeError: + fn = types.FunctionType(fn.__code__, fn.__globals__, name, + fn.__defaults__, fn.__closure__) + return fn + + +def run_as_contextmanager(ctx, fn, *arg, **kw): + """Run the given function under the given contextmanager, + simulating the behavior of 'with' to support older + Python versions. + + This is not necessary anymore as we have placed 2.6 + as minimum Python version, however some tests are still using + this structure. + + """ + + obj = ctx.__enter__() + try: + result = fn(obj, *arg, **kw) + ctx.__exit__(None, None, None) + return result + except: + exc_info = sys.exc_info() + raise_ = ctx.__exit__(*exc_info) + if not raise_: + raise + else: + return raise_ + + +def rowset(results): + """Converts the results of sql execution into a plain set of column tuples. + + Useful for asserting the results of an unordered query. + """ + + return {tuple(row) for row in results} + + +def fail(msg): + assert False, msg + + +@decorator +def provide_metadata(fn, *args, **kw): + """Provide bound MetaData for a single test, dropping afterwards.""" + + from . import config + from . import engines + from sqlalchemy import schema + + metadata = schema.MetaData(config.db) + self = args[0] + prev_meta = getattr(self, 'metadata', None) + self.metadata = metadata + try: + return fn(*args, **kw) + finally: + engines.drop_all_tables(metadata, config.db) + self.metadata = prev_meta + + +def force_drop_names(*names): + """Force the given table names to be dropped after test complete, + isolating for foreign key cycles + + """ + from . import config + from sqlalchemy import inspect + + @decorator + def go(fn, *args, **kw): + + try: + return fn(*args, **kw) + finally: + drop_all_tables( + config.db, inspect(config.db), include_names=names) + return go + + +class adict(dict): + """Dict keys available as attributes. Shadows.""" + + def __getattribute__(self, key): + try: + return self[key] + except KeyError: + return dict.__getattribute__(self, key) + + def __call__(self, *keys): + return tuple([self[key] for key in keys]) + + get_all = __call__ + + +def drop_all_tables(engine, inspector, schema=None, include_names=None): + from sqlalchemy import Column, Table, Integer, MetaData, \ + ForeignKeyConstraint + from sqlalchemy.schema import DropTable, DropConstraint + + if include_names is not None: + include_names = set(include_names) + + with engine.connect() as conn: + for tname, fkcs in reversed( + inspector.get_sorted_table_and_fkc_names(schema=schema)): + if tname: + if include_names is not None and tname not in include_names: + continue + conn.execute(DropTable( + Table(tname, MetaData(), schema=schema) + )) + elif fkcs: + if not engine.dialect.supports_alter: + continue + for tname, fkc in fkcs: + if include_names is not None and \ + tname not in include_names: + continue + tb = Table( + tname, MetaData(), + Column('x', Integer), + Column('y', Integer), + schema=schema + ) + conn.execute(DropConstraint( + ForeignKeyConstraint( + [tb.c.x], [tb.c.y], name=fkc) + )) + + +def teardown_events(event_cls): + @decorator + def decorate(fn, *arg, **kw): + try: + return fn(*arg, **kw) + finally: + event_cls._clear() + return decorate + diff --git a/venv/Lib/site-packages/sqlalchemy/testing/warnings.py b/venv/Lib/site-packages/sqlalchemy/testing/warnings.py new file mode 100644 index 0000000..46e7c54 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/testing/warnings.py @@ -0,0 +1,41 @@ +# testing/warnings.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from __future__ import absolute_import + +import warnings +from .. import exc as sa_exc +from . import assertions + + +def setup_filters(): + """Set global warning behavior for the test suite.""" + + warnings.filterwarnings('ignore', + category=sa_exc.SAPendingDeprecationWarning) + warnings.filterwarnings('error', category=sa_exc.SADeprecationWarning) + warnings.filterwarnings('error', category=sa_exc.SAWarning) + + # some selected deprecations... + warnings.filterwarnings('error', category=DeprecationWarning) + warnings.filterwarnings( + "ignore", category=DeprecationWarning, message=".*StopIteration") + warnings.filterwarnings( + "ignore", category=DeprecationWarning, message=".*inspect.getargspec") + + +def assert_warnings(fn, warning_msgs, regex=False): + """Assert that each of the given warnings are emitted by fn. + + Deprecated. Please use assertions.expect_warnings(). + + """ + + with assertions._expect_warnings( + sa_exc.SAWarning, warning_msgs, regex=regex): + return fn() + diff --git a/venv/Lib/site-packages/sqlalchemy/types.py b/venv/Lib/site-packages/sqlalchemy/types.py new file mode 100644 index 0000000..cd0ded7 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/types.py @@ -0,0 +1,81 @@ +# types.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Compatibility namespace for sqlalchemy.sql.types. + +""" + +__all__ = ['TypeEngine', 'TypeDecorator', 'UserDefinedType', + 'INT', 'CHAR', 'VARCHAR', 'NCHAR', 'NVARCHAR', 'TEXT', 'Text', + 'FLOAT', 'NUMERIC', 'REAL', 'DECIMAL', 'TIMESTAMP', 'DATETIME', + 'CLOB', 'BLOB', 'BINARY', 'VARBINARY', 'BOOLEAN', 'BIGINT', + 'SMALLINT', 'INTEGER', 'DATE', 'TIME', 'String', 'Integer', + 'SmallInteger', 'BigInteger', 'Numeric', 'Float', 'DateTime', + 'Date', 'Time', 'LargeBinary', 'Binary', 'Boolean', 'Unicode', + 'Concatenable', 'UnicodeText', 'PickleType', 'Interval', 'Enum', + 'Indexable', 'ARRAY', 'JSON'] + +from .sql.type_api import ( + adapt_type, + TypeEngine, + TypeDecorator, + Variant, + to_instance, + UserDefinedType +) +from .sql.sqltypes import ( + ARRAY, + BIGINT, + BINARY, + BLOB, + BOOLEAN, + BigInteger, + Binary, + _Binary, + Boolean, + CHAR, + CLOB, + Concatenable, + DATE, + DATETIME, + DECIMAL, + Date, + DateTime, + Enum, + FLOAT, + Float, + Indexable, + INT, + INTEGER, + Integer, + Interval, + JSON, + LargeBinary, + MatchType, + NCHAR, + NVARCHAR, + NullType, + NULLTYPE, + NUMERIC, + Numeric, + PickleType, + REAL, + SchemaType, + SMALLINT, + SmallInteger, + String, + STRINGTYPE, + TEXT, + TIME, + TIMESTAMP, + Text, + Time, + Unicode, + UnicodeText, + VARBINARY, + VARCHAR, + ) diff --git a/venv/Lib/site-packages/sqlalchemy/util/__init__.py b/venv/Lib/site-packages/sqlalchemy/util/__init__.py new file mode 100644 index 0000000..031376d --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/util/__init__.py @@ -0,0 +1,49 @@ +# util/__init__.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from .compat import callable, cmp, reduce, \ + threading, py3k, py33, py36, py2k, jython, pypy, cpython, win32, \ + pickle, dottedgetter, parse_qsl, namedtuple, next, reraise, \ + raise_from_cause, text_type, safe_kwarg, string_types, int_types, \ + binary_type, nested, \ + quote_plus, with_metaclass, print_, itertools_filterfalse, u, ue, b,\ + unquote_plus, unquote, b64decode, b64encode, byte_buffer, itertools_filter,\ + iterbytes, StringIO, inspect_getargspec, zip_longest + +from ._collections import KeyedTuple, ImmutableContainer, immutabledict, \ + Properties, OrderedProperties, ImmutableProperties, OrderedDict, \ + OrderedSet, IdentitySet, OrderedIdentitySet, column_set, \ + column_dict, ordered_column_set, populate_column_dict, unique_list, \ + UniqueAppender, PopulateDict, EMPTY_SET, to_list, to_set, \ + to_column_set, update_copy, flatten_iterator, has_intersection, \ + LRUCache, ScopedRegistry, ThreadLocalRegistry, WeakSequence, \ + coerce_generator_arg, lightweight_named_tuple, collections_abc + +from .langhelpers import iterate_attributes, class_hierarchy, \ + portable_instancemethod, unbound_method_to_callable, \ + getargspec_init, format_argspec_init, format_argspec_plus, \ + get_func_kwargs, get_cls_kwargs, decorator, as_interface, \ + memoized_property, memoized_instancemethod, md5_hex, \ + group_expirable_memoized_property, dependencies, decode_slice, \ + monkeypatch_proxied_specials, asbool, bool_or_str, coerce_kw_type,\ + duck_type_collection, assert_arg_type, symbol, dictlike_iteritems,\ + classproperty, set_creation_order, warn_exception, warn, NoneType,\ + constructor_copy, methods_equivalent, chop_traceback, asint,\ + generic_repr, counter, PluginLoader, hybridproperty, hybridmethod, \ + safe_reraise,\ + get_callable_argspec, only_once, attrsetter, ellipses_string, \ + warn_limited, map_bits, MemoizedSlots, EnsureKWArgType, wrap_callable + +from .deprecations import warn_deprecated, warn_pending_deprecation, \ + deprecated, pending_deprecation, inject_docstring_text + +# things that used to be not always available, +# but are now as of current support Python versions +from collections import defaultdict +from functools import partial +from functools import update_wrapper +from contextlib import contextmanager diff --git a/venv/Lib/site-packages/sqlalchemy/util/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/util/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09d94d1d0de96383ccfd991ccd9e36f23b4c9820 GIT binary patch literal 3421 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 DQ_h0J literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/util/__pycache__/_collections.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/util/__pycache__/_collections.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78530ae29adc2f0073833e4c9990bf9e62022c9f GIT binary patch literal 37036 zcmchA4Rl<`b>6<+#bUAeAqaxMqNpc|6bXs~B}$ehn3O<4Kc*}}vP4<Zn#^Lc?*Uj~ zu?x;yki-V6V^g*$C$ZzkacnogK4}uWu@fhb+c>ow`#4VP#%-FO^rWvnZ4xI<-L&Vp zC(UV_p62xX?#%ne0;DW8Am8l!ow;-Gojdp5xifRm;9%*>ub-`ae>0Q$b|&`MgY!uo zUN)P_IGJ+Bv7BtfTF#cU7SD5yTsbGd`EuULJ3Wn_<wCiz+*|HlE|!bSedWI8Qn`e8 z^G>1BzdTSLFy9T92i>8onew*BGfuBld?n))-Qlab@^)PJIVD_|-0ip?!F9hgfa?Ld z-ht~uX9(9r?kJw`#Pv337}vw@PIuSUe0ewSY<EU*XGGrEgX<m6D6U7{J@{fTu6H`S zaJ>s(?7<iNaA&u(2Y2=$hg|t4T<>-E;d-CDAJ;eI`X*;TuJ=oh2XK9}a{$)|<oXs| z-{RP~w%r4G-^TT=&Ouxsl>BaW_m^>2zRkV4e8|1EeAvC!%`e`5)hZuxZ*^{S4!x4W z`BCR#=dg48D^__7-yd;~;`^hL|1oFGIrd7Xe1|jT+~FL@8^@hHopIc`(>dYXh2L@K zZs#O^PdJY__c-_Bjk}z;IQQYs-N@^$&i%;ieu3qrd(wFTrQPF9IB&!Ad-42~a~jW2 zOIdHh^@GksxPHie3+i&8GwGbcdvA4e&t=9Q?c6feYBbzhyWVQrwMNzRT+gmH9ecrT zthmY>{|>ol%<{KYXT4Tqwe42w%PTF_p8k%-2R(jYdqKHXr{0|Rch=jkYPVVquQFF} z;C!yy@LYeuYb!jiwAWT#&mWkrH>+yRTy3j`#e0?NYz-ylSJqB0)mUus2VT`&y9Xz3 zrffNxa&|5woSH>l@=}+)KXPUkMOAC<cerb=b8dA76(f}V<1h1>!r_&Wv@`3OMeC{s zELfe_o^{)+s%cm4M%`=MtvOr6Y%jTMcC)$+q|CLH-Cn4B_Jp8mVrD!lWoEoovd!Q4 zxb3+vD&Cryi0<;?M77;kiSNdNC%55vGtrA<SwBx0{9f~n81dZ3+@~{Y2w?y9($vKH z=RJ_-{Mr%k$lCcwTyLq}S~>r4tF?6JQ`Kg5-d%Q^?K_`a^V;t6oy%4HsPmWH=B4wE z`s{fRG`e%8T3f<<-g)n0quQt~xXWwjSKIZ*`AUo&<11@^9`*P35@p0)rmwSmBZ|lQ z&NMioSg8<Im5N`gRF+%LYJ=wkmCD7{YQsF~t5m#3tL;@PY8V+xm14;@-XnE(;qdZE zidH@=cM=?t!;?wAavzd_BOD7H(eLD(Jlad%?QwdXg0#efmPqAZEs4rSCkv_%_@#(~ z)o-A%nVB|uW@g6rR%eCEpm?=yR~^T*8`W92;X0Zd480#6pC6A?`N0Ptw3qDDc9ieY zci(;7K6%`}=fZKFgLnGf2U|_|;Bot4`*KTu(0<&57sf&kl3d2|Qh4X?@Jv6$m*Jh0 zns=Ucz12qBR;|mPt%R8Y>+D*@t?yiQ)!K}%P_5N$SL;oC8K2ZqW6A94XP>gcIQ4d2 zi+~egc(B9ja?^INtSHy>AR|0`xw_=qtDeh7!uBBF8f3Lh(E^6S(YXaT0wO}p3^HA) z&(_<X?bVm-jjFO|-G%C<I-6MwIcuP1X5t)4CUS&EN5?=VR0GX)wbr&*RO?dRL5)!< zd}=?l)?R2e1vIY>aaL6hTK24h;5lwDv@W}sTxFvzS5<8R4Wib@r?|CdJN3CaR{;%; zwQ>3U`DUYz3U39+@$U6AsBv0AXR`&;xQ#WNQWoM;#4Qk5TlLzlWwM(HM}pr{4qyWc zluM)o6oI2^H{HvSEQ-{@muz&$@jhC272Q;|wy+5lc%#~^X>2OD=GHGE1EM{Ubj*nb zuQaMPcN}tx6f+Rn)fJ!?nF|akb}Kv}leR*$ub}|1KHsd*)oaye8)RArk6o#^*K|$L zyP#`<JY6PWb_<AKKqZdbmlx`&2FOBmH36&VfnY}R>KcT?*RXSIb`jJ&;t5CAs-A1O z4pJD|l4ENN)#kkGOib8!jo&W-P!>-CCQxCv-T+wkvI}-W8^2s{FKk^!aHtDLbv17> zCkO}OyINzFo#0kdo*iol!2}CU^#)bXsn^;;Z_>RdnNaTB#0-$vUTwEjz1o0y1?Qq@ z(~C^kC>{An`N?Anhe*mcdc5__x^*@Ca&A4V4z1@Fvx~WQem%>3S1l{k%+F_D&f*$R z9P3nOomxV68oiOoKM!KQM;ajRRKSrC@DTfi8fS&o6{p&E{amNMGM4vqwT9>CFQF^+ z3;Ju{YNv%gyYwdr-vE+~HDKkf(QMu-S)GwcSd5#p61_QVDK#6#Nn148SENsW1lpJM z>$%Qfh5fn!{f0XBbL{*zrv&oG6W)&a=(_xq)DsMc3I`ef4B4YQ@yT#q7>7+brbC2i zVy8_8cn*h0OwR*B7lvQ9)}cZz^{M>2^^xqwJXN4+Aq=#S?D$>2)ku-)uS=&WF4&SE zoMC3nxsd7H9;cDkWyVi6S~bx8L7W7ijAhkse4yB?k6CJv=Tv5NBAq_TFJOwIF_@&j z&N<|3Qa+?U)d*65-P#4ZzYi4?j_vH8sy2_bAu-W^L+`53LU*084|~w3hRy^x9#yKP z#&QCYp932G9`GyLQxZDAA8L!+W<Hh5EDF@>kk#20uYdGPNC0Y`0kj@#|7vE=f_;^J zrFbo~n7NuEuL9m&wFj>nW@QJ)9vY%wME#~(r(H!4hAvCAcSy<@cfA?RdOT!C;cJ5B zUa7e&(s_wu#9LAEk=}uZa_ks{__1TUJ9C{FXpv~{%J?Df0_NR8e3N<WhIxs(b!#t< z9~I^mVu7uwr-IXL{HQ}bk8>KC3|vfTg^OVscjsa)gJWDAeuDhSZ1HS^bLI|-sK0fC ztf4O_gqVJewl>zMw&Nu=!i4psG<8}vXvM6kUqq*iF0#F**eulTOpY>%*iz1<<?O_R z!#KpMDc+XPmih*^6?2OBYdFF`B6Jdm$E*USIcY{gb<UKH;#`K>4Aw0Axyq9-)c7&~ z_#3u8LKpQZB1VFy&U)Z{KYrBBOd`+)YCdn{2wGA<3fWrB6Ity{8y(nH9E;gG{L@A1 zx~hJ>0CNNCRU@>Wy2%=vC(5OsQmI@HLCAN8Gzh_?4U9O&cWopZip4A-L8nP+x=5!9 zx=;&D37E2-GjF&pPooFTllRn3C{i6q;^$UcD?lpcr*AoFl#;A3<M0YdGPz-NrX7t| z&3Myey(zX+>^g<z)SW!rl*jJC(~CH~A`+Tp#aywVc(*%;*?1Cgn20+V>uxR&QyoVS zIE!ip4ED3j%d73`Y{Q+>7Ky7utdq%jJdMK(x_FxGi`k^0-*p&LQq(}Ij`bkNv7Fy` z4ju;)@jmQMu~9&Iw!Bc}SJk|qUAmk?B{(F^=L0NXxZmpByb<2<=*=$prFqKE!*QW! zy35d!bWfr$bi<AsM%1S7#&_>$!R)<@7Fsj!bjLNdrvTS(Zc#7{EEjO};wYB;3<XMJ z8fVJ=+AuB;7_)fLn8ia*Kd3U`4~BIY(HD470)jkFxDFuEEguaai!<nqb7%-ZmHiN0 zE$d;!9LIY`rd2BSW*urGa8Ie==Yf0A&q0YyVPD?=2ul%0hAud4b#^9z+8`-NAOb4D z5ow|Zgj$Uj18>9qbXgO)_c0s+iFc*T(Ljf68T9B={DuSzMADoUh@-4}0QVKE9`p}F zSxH3rv9WyuM^NaVbfKXP3dC-Jd?|JkpRQY8aUH78dd52hSC-X=^ilgLeIOx4{cY~i zvJ&KhlCM>KO&Wt6G{92bqX|hnECO2XL;j|PsJn1p&N?Skc=LN!n=r0^0#7{(<_z?9 z{wqN?l(I#No*l_b$@`n5(Xj${+GP~D?TPP?LUV)YBT{K`&^QJw#e4V&y$|7~PvZ#C zyMd<q>*=v=X?e(2rlisK)EaJ8sS_-(7h;z!)-OQ`blkZr+$Mq~Ejr0UT7EvrGnO91 zRs<Hg;)igEsZ-4D$`*SdMsvkML46HR!atIG5{LIiBthdA|JHGOJJa1X{p~UDmiX7v z?8UEME@^){n!R|@(d@;0ULMq5^YW1Pm6x|^A9;CL`^L-LwNJb}0^hXl+zOxC4(FhA z8%i2=4mpSMyVJSdIfCC^&QWI!zq_4d&K>yO<J<y4GVYh2(P|7_7M=TXP8W?`wJVkR zK$kud)2rz|pv$<XT>^NK;%D6NslceLCKM1h@F}84>jX4vX#iZ!VK72-k9q`;Va*hD z&mfXE=sEMuFGeA0k|g4<GD{=scSNv5*mMyBd{1CVJCmnTY_8!pH3(ffl<FSds8oJE zR^mvaM0wo3x#&u>anpLg3Ux?(u|;D5pDNLWq<Fs|<_wIUu8I&E65p>>{!t8$RL$ji z0{gk3=Gye4K1K<2)!I$mL%X#EfB@Y+Etm-^g>Pa2>VKv3o3WCU7?8)^Z9cXs2sE(+ zP6eVK#R}22H76E;dK53FN>=yd-dAF!?n>YtEnIibi7F?Et;^jTR#vl`M)0AH1+vQr z!|PnXx@p1TE8PpGs;vt?ovyG}2z7-;+C(Dft@!Hen-&>rC2tJNeh!T+jb+*_zq4t{ zp)sA{@C268ph4fdsMFIyTM|@v=VgpD0EOtwFho`PW~}7h3HnCwB=FpW52Q8k!inyQ zC{Y}knH!~wW-Hb(<Z&rCg#!+KprQt(Z*2-v^iBdKnll0tRsXe8#C3Ue@k=Fjy9JHM z%CzabgWuz&Of&mp$Qn7_{EP5Uhytu`i=gesDTYb4hvWAMDmDF#l|_#}1V2X_hx)|? zyb0>93-y)?fk3!k8v&}!pah17{te4#b{VUaq+ayS=8TgV*t<V)05&7bfZn?+fm<0S zL+DeEDmgO_A->~mU`s>~0!E|>wt?JLFER%HZCHm~q7LzQx}%R$mBdD`2Ev*i4N^5L zWozZkG<ll+DJbOme+Ub=I@=@Q$F+Ypau8FJhh(EgPO>c()G(dI6mV4J6Pk2Pxca?; zDwGsDehzKyj|qWR-(kLFJ3}!j@c724$2zkWY=Ly~$ox4AGyJkGRuKGoUiXp+vF4M- zEIUxmrn#szMXRcNuPKlIORUH^hIM!QJr?Tsu#?!NvsPsk`QKv24##RL-*smvswc0v zHTEumO@Y1d#)^w~&gOBVnly|=ToJdLf3HG31-6Fa-bp;%1g(7ZmpB4S#_K1_c+kP4 zE$mGz?qm2+dox5Ee>*SV!Q?3>?DEw#6LyH&?0$x4&oVj3<as79FxkiCB_?c%LT$ml zdN(g8nM4dPXQDu|?w`fsQC!nSB2E(O_sM?+Ttn|K@mKySep$m2{*jd@ad@<o<Jv!* zb$KW2<e+2sICR_h_}ic6c%kb=%+Prr=Qpfsi)**qv`sdTK;;&<FI>yBh!Wt47KSL5 zJDwnR4uBEtm;&BrLt+$>(Tj3YFGX_r{juCPp+4XHzs%9J<9!?I6{_LWV}<LGzZ-qd zBlG{s0w|S=pbp{+crSr@GVvr1aX4+nJTRV9@51xWBLR*>BUP-G6UGt6?omb@IFJkM zt~NoW1_$Y4{_=4IO>AC~ZgpVM=n(?95Ng0Xi5ieHlQ_IDAc@z&ITo`V?{MJUg+*6z zicTN6sn;nv{rD|91I{3R`<x+X8-7d9ac9`sjyL<A5oZVP3^=3CPW%o!yPVzl9dh<K zd-1!?+2`DZ-(h#V`VnWpb2H9IoCD4+_}wA)Bke|QKkhX4<1S-A?l$)09;XKwzEfQQ z=$)xEf+`)bpd_NflTq&zTE?Cbna6=^jzYBbsQCiIbuObh=uuhPjxq*Cvht6=<S$`e zBG~8(7?g>eH!NKm{3e`wC!QnfE#%Z)G3Ex32CkdwP#lvaJSdO6lSnuUSAkH*7+g`= z7>FH*ytL-f+kObtZ4U8j-GVz!u7d`ZRo{nib^mWhtoBr5WmoX$xfl$FGsiD1U8Z|2 zCaFRzcN<}6uaL0{76g?wl<kbh@(J5nqQ!@3qbpWdpbb|njlX!C7ZAq@=OmI%Fvv%{ z2}K?!grXBjF^ZpY5%!6~NjISxl~VRCR|DgVzMxubWo<KGke}z%n_}b|j#rfr6D5V! zWJ%<XKsT_2D>tc76)zu%m61ZH1~4oq;D@L2l?YJXTcVThLT?0rA=)nR#@&lg)LWQD z;z0;o!@XN$5F~2J971lSrNcwGaTX_LEchAJR>m;YeOFFlM8o3X<b_cL3edTm19Umq zLJ;s5_rH*7?z^&Q-NN(X^9Ut)IlrEV#+W;mxp?XYaIyM6d;^bT$Qs#cJ#v=!saa4s z>b_O_W%joagoF4|6(%!G4l&tGzdgj7vUkpafoY)Z$_`2BKxZUQU9IkRH*RUYQ7u~P zReOkkVqeB@pz-2bbnj62g&rEJ5CJJR1@K|ye1ZUK3yQwbe72L260rD;?i?}&K8QpU zTc;6CMm5c<v$!V`U=toBJ@1QgK#B(;E#c?sUff1r??+~vltZ*X5GyCaX&W@|fY+r% zO%8qsL*LUOP;7h@2{Pgv$e9ZRFI(3@p1m0zst|ELsAA)7VU+~1(jpGx(dhtAhAPB4 zLw6yuLEb1-!P<c2AaBX`31oXJ$QJf#g5kPo|F0VXY@31r+GA0?hDO0$_VpuSGJqCH zI2=cUbfDi04$6S+p&(b_AR)cADT1f+$6u0l3WxZ;gB~}KtA??O)&S2*(S|V!u&&1J zRIJcMgNj}WnJH27Mh<6l+N8iDb0a4|7Ar2%oPrm+7e-gzCe<Y7HYv4_nc`UX&c=$} znIf2eBO%gQoS^sZI58sazBQQekqZF=dq2EJS$K`^)n21@YaYU{lF?qHHiivp@<RmX zqTakmT7q9d44~U|#DgT`FCq&QV(?Lv9JNa&4TS>pN!En$QBG$uj5m~nGii6cPU0eC zwIQ6yDE#9u+l|zUHjsA6!5zW@cJX>AT_br7ml$`{K>UJoml0P82_fBz$m%3pi6!_h z!bge2z_T=5k#{tJi%pYo$si+@9Km+s<y-*Y97aJo0*VeXr;f!TUDrWYV+PU__*koR zb({sgpMd!yZj^gX=x5g+kRO)`xmMze)(Dh1z`reyR}5K&+?l}V0el?{wIOuDU{f5O z(ByRZ!n8v67@*f^J%jTYuqOT>PokxX&R44hO}l<%<aS32EPjVJKsw1_!IsuMm2I&h zwc$O>%PNx^6RHmCVJ7VT()M~E@3~Con9MVgZd~|8NFY^<7(>p4duaspvLbP7zEmvc zwas4YFZKrZdmh8{@~?!ay`m-(xbTl0Gl|3d8j{c=yIt&fMyz9&4QwRLr-4OAyIxyl z1!Ix*8jGxGEV4ea$7q>pd#vBsV*~C$WRtl=u*bArHtY^#Bv0FBBkl-nF>RZTx}zAs z)Arde%w^eaY_vV@p7LH}r|on1m2Wb(+J4M=x!IvMa>OqgiazJIBP%D1+Oeurtm-Kw z0h3wUT!5QeVh&HXp-VD?r^2uSiXE^Z8+NlGW<@rzcY6FB^dwDuY1I7~LWX%GCY?5e zO!K}ArKLG>0{0e3KrMbsV(Wk(vz>q+0exiZ5PJS4YQgqs1nMZD3_Y?ko{lGkI)FB> zTgU=FQ4J)14&zrw{`kF^IlxI7X&{?;@gjlL%|(XIha%*TBa7D|6jT1<oWP+dV02?M z*lO`c#Ph=LeCZMa358+MrxQzBm?k=%kkX<@!wiHTy(_?x)}#%Vgtkd6`2_?WAiPZj zNF<ovgaJ;T>IUEnUh2d#g?=OsqRy~8vO+QzU~DJJ2sTWTF&7CsfiCDly1)egcipRD zX2E;na6t|SDj`v4UI|4pUXAo2s<`P^5@5<uZxXK<QQ2(-SES-A5#bOv9q8jMU0c#| z-sZWqQ1%@}hW7L8QCktoTXdPz_~sf5lN>QB6l2xc7-#p&vcT`fruo5GfvKjeO`7Xe zFHkfs^&lV!==mhh(~aOM-1|_hv^X8jXmz4$VH@CpydJJ(BmyH7gBX^=VejZ+H3s&+ zhXrnDqC38>)MV($M`92p>#L(yLLO)FY_391x|OaR7nA&u#P72~77VwGAs%mz7Yi`c zjSD3$xKzA7iYm<bJl{&kM2iIe7=hJlNC_(503)^H&C*!l0f`85$bqzPgahGYA;`ga zR}egw)i`4xA1Iro8g)BPbxKo@)ct9K6Q~}kwxHvW0!a8v<PjpzM$*n)101grA}g{} z*Cjw(SSXjYQ~4oj>JdH!)sHX?^cn}x(9yjMp2mwoM<#%R?`*v2TUDq&5%W>_{&nA} zdAG?aRuBGSK>K{Ab1c@lBr?X+vovD{Tksh&XO|=R+Q8sJc`V8wi0Ab4EKLjiVx;|2 zZp7c(z;A*w-N9>=U4yeILfJLAg&2_{B+b4GHzwkhB;H-$H_?Il8$)OBmgrQMP^qAT z(xtJM>N1kg#ZY-NhDvX38FM&Py@p9Eda4&DJw;O%0+fagos6;>t#1rCu|Sf*{c;2x zSUaR$+_$TlWR=z7w;-!<jbtXI9PL(<Ut-2Ic4=_!!N1Ne*Qt9*-v4hc;>NI)Y!gDl zFGg7E9F0|DOTyiFTPQ{Oe1Ro}Eg`VEb^jeONdi&i2x$l7I0;0D?#j_7pcY2$fgJ)l zp9-(Y+>{_;v0eQ=5GCRKoJiQAem1_?g!2#L(U$|X!QT_-{CK0TXu%v~{Kx^O8Pea; z<^6;n8B#20fYSE99}s*g&V>WkE~|4OM!qijCU6^A+1?|_ia4d~D~VIg01=#C15U3& zjxS~f-y&z=x|U~eMNh-0uvk*E=8a)I)trg(ug5Sx5vysp7P<xI6Ef#b2lBTgAdSrN zZ`uODO-UU}AhnLFe=Q<434Oo{{j6*~TjZZ0dDxt2P232{IS`xdK;CqG{ceP>&RsF0 zMp9!7QU(~_zTxc~!0XFE!Z%sE)?}ms?cWLlYVQZqBgr&?MVHeSAPp#`m`pO6W+G0L zr+7xilOdUyPK^;vsx-X9Tx?uXvGNO*id<YVk1&%JkJil96?xV%SC~7DXA}e+ovZ6t z$t-fI-(?-;Qok(SW0Z&_S;8yxM8kbJ!7xuB!Y1-Kn3~WDgjn?A=)sZ4QHcHWyhkyQ z8jkRfoHmKWdjdI!&KS61+&o+_PfKVuhs9;-01S)EQYIJ{mnBRvEUrVVOD43s--K2V zn9%A$6IwlFLaVo#(CT3mTD{$bR*#s_>K%HS5e$sW@*;4^$l@UgsOG{U88~HR*$@O& z>jgvh%5ou@@;<X%$W3Osko{)4keg+(kWBf2SuErhy;Mlqb{=phkl(G&+niJQJ?NZv z9>ni$&O^>5eh)ckoQLsy7z>g-g5TSnN1ey;d&E70WlSD--j4Gz=dAMtevditaGu2P z9nMqEG=7iEVhYdr{bwC`I53+U4Zrg*&&b3}nIwpqJr9B33bS;YoB?m9>NrQoWM;Q^ z>NGLe+sqM^sf%34qwUVG*%&XhU1uIMA~CsJufj3Aip51Z?$7l^?!;VnO#g=8z^lVf zVY~(BXiE$WmPxRW%(Yra%xWGo%^mA1=%ouVx1K<H#{~@cN|m$JG5;NlYuMgOl}naz z5dg_4a&rZuYKlyB6iFL{(dgTu{i{P)c8LWx1l<>Xk{$rPIPya1B+3Qz7na@SSzsh? zYbg(z`Bp3H>a`!8?fBob83bybOpYFnVY|B*ivm%Aq;*O!)}omi5(P|W)isuxtp|>( zuI}b0-uV*(Kn)S2J=@|%r#)1BM@nU5hPf}EzC|kt1kI`s;=U9c7%GQ(Bh~a7jP+-+ zBKO3Lj7N%Y=tZRLVSFCdH;R|m^%W4dDxJ=-{~RlQBwo7lg$XY39)J6BgzT`!=nbj2 zmBFyfs0r3RsE1vWLAqHjnxDh>TfsxA{TC4jWHH_i@7InvRB1O;0xSj%tc?&X4(iL~ zZwc-+BSPar9I*pGK_ZSgH?4B`wzCt13HF@ALKO&Qa+g=IVgect`Z%(WI>RJZ2Q1)I zt*7JtWg^XgXC<_^fN{k|RwKbp@j(l`EAcuZHw6`le6EI`lXw$4Xr!14H%TADNB@hZ z3ccW)h$C$a@7Mso<D~NVgz{Ukm<1Q6;Pl}tLX0Rou<Qh5x@yZSN5=rdNkU>AXrX$n znOkf&^r?8YQu#kIkP>t>&%3kr0RhTP^`f&Co`%Kj5&{<JmlwxjcKR`p<uqPYuQDNl zwgL#<x_hxw`5VF$_)P|~odn10S6&xLxdce(UN1;ZMDXW9u`NL2D<Mb}b)mR>J1*#F zgvBLZ2t5f7DY$znuWaJ#%0WcUWA4mizTE>n<`xSwiw1VX1&rqL4ajWZ6-wq~=f-;d zJi8jjVLwIdMDx8wjGpBs<qZbQgaIkV#Z>pL^yrvhtb0&*z&9mUj$Ddy$Lz37n;T^q zR6?o;FNf7653@Z=i=g+6@|E-cyrDLLWkpzJfDL^xA8~;{G~H^SW$(r@2-i`ZTX`L> z;L=T_aT4U=*N!(HPGE|WAOYLa4X|O%2L#b0KTDa`?SK`wZ2)Ue1grYH_*P^3MxexW z(!JM#60d|9ym3a!#2AGPLPjCOY|SX@!y)uH%qQwc_)W3{<h)F8mFus$?E|b|j90QH zOL%2hoEp*VVO<jxc^%xMDez+e>N+ek5BiU7jeNe+9iZ#5NdCrH<VK(b{l~8ZWt>F< z?}IclS{_=Kpx>lU$Nn>?XWU?nUnB_b_N?cwT8oACJpG==zlkQ;E9~HJuV`(rQg<q{ z3XKQD2ZP`WH?$-XduyO_xuqCy%{fDQtilAviAY%mD1L9CH+Jb<<db_?Q{#`vEL{2k zlYQPsFZlN&Q8bUxFx-Mi)DPhK+qOnM?ZyOy;B_tEL~Avg6h~Dm7(LdKEh{xmbZvs6 zWD^o2c9NwtOQ@|0>6IFn;42{>_T!?vWM%Nl2I;DCK@x6-i}&F96rm~NBm?C~wD?rt zFZOnbjyIhX)yGk^`UI0}ObE03B$E!3AP6PoHud-Ts2iv8^|P!@iqjGzDe`=THA<lQ zb&Dhca&zTMT?JI{BvgSSgDxwDU~x7iz#&M|)VJa{mV8})qetUiES_0{-)@e}=Li|b zX_Nrsjj<jJ-iq};fafm}umtNRR1J7(lBoGx<h?5dKP~At=RLk&VQo^pmoO3IF6Qo- ze%;(BU~a*E?*&}*gllu|6GvDA9J0!FxsN4Zm;2~#Yp{4`3GTZk&V6xoy!rgcqPOC| zSMa<=(6;2iBy#>1`Hw!#*TH{$eU-JLi3mY1y=(y?cpW>A@@}5*(7z9BOB@G6dIo{$ z1zMm<h=$QUFq%SwvN<;3%wCFS&RjMz;0#0bgLn}%vGL<umr~lL46eN=U=|ED-xY^^ zgSHYIBnn1Hk+t+7Pq3KomiNc;=<ft&q{ZQeG7_yK6o;?7kqRxhXr%80Q12yBn>SK% zPba{^``2%zEcv>Pls@ne#Bg#Xj`waH^X50!>tX2q0P!_~xMgG2NFp2akG~<!cHu&+ z%v~3#&=!(d&DZ0gS>*KL7`BGv*xG#J4*LZH5UlOYNyK8gv%F21_3e1{(O6L_wPE90 z9C%mr1eH!*T5=-s4b_n2FFzhDHr*%d_@+3T!*VHo(hjP9FdU3~oh#d7mEI=2=L-zq z34Bvs+bYLl_+q8<$<2Y$FC;`*=yIXP%=LR{<1bn;kHli8OJ!puRFCEWiG-r#^tytC zLsLJwIb6X)K+~8vD49}`lQs3!7|$9^KFMT)Nt20qZt6T!OinPFWb#8yewfLJn9u{E zew4{anb6u)A7k?4OlbayjiVK5YWSi^iz2G1l_{!TO8W2j@{B#DVs|J5XIdS9A1^5w z6q}x!Wm08Ap420BKglzqAtuMg*bx^e{d~U<9`T;V35US208=oxxL~4`3&ldQBtvKY zk`|F-0k>lMw-0yqlN^T6^z3BjU&9gpv8hku@R$TcXmi23mW*iD>k}ca*(qRvsK*SV z70e)7uNg!uI)gB#hWvpS-0ISEX5wN(^0Fiz9y@|qM1@2cVeDV~JW~U~R2_EkqkTwZ z{tQC1Bs2qo1dKF}&7i>=alMyOI+oP+b4+?MebY=Fu<^Nsq9$a9p!^^n{ahAB;NU8# zWAPe<qcO35GYYP-(AYSi8mq4xovFOL68{|DR_&+Lb2o#+rCf6Gr-E`b+0M@Rhhe!1 z6oze-*w6}w1OjqAD6g0N4eaV3^(nlg7MT1rlb>Povq%i2YLPdD^CI@+>%S1f1dA&H zlb$+~fQUGo!XfkbG(^;5;$D4$gY7dY_-AmQE|`FQCRXrhqF_VEguDt%-n5Hg!2!lo z^<;DW?B>Pk<(v}5k#nV@M7`GVBoa=@KpoHMd8z0WVR(EI)ALN^{8oX5>S=tEMiJxS zK2Mm*1^G<Y>KsiX$*leq<J3UI3u{88@etLMx+-EFF(VHo%OsReDI4^%@rvf06aop% zuLcOr6V0E(olUUKp7B@sG9i~4!W(!!8T?DEh2wB+C&4(}UO4VB4riNjxM;8oRnY55 zVoa|1B4({}PO%w8>Fhj<3Hw*DTCm+(mF*xD7ZJxe24R-LF25wN$l^M<Pv1Y5@8DNm zNimJ*UtopEdt6qBV|XLk&Yl!Our`XG47G8MSlxk1^Ca)bItXSSqrD(r1jbL{!OyZ( zstX9u)$7gWih0E+H5}m|5j}~+yB$d&)v<Soh1gAug}6EF9l|*(oULL(relZ)10z9* zbWAl%<cfYt=gJ|HAXkTdgp96)(TTC|$K!s`a~#M>cI)L4gsD`u!s1@xXe5G7B3_Fs z|49`26;>Wc2C*Q#-eesABp&JLJqjYOWC4dG22<2{VniPvUq0sq=tyX?f7GP8z_V*i znn(;&$gsY8jdvp;Luvlogio^s$3w*_c#{eBN#G<{I}>i@4U1naF>}^VgAo-I;o|S( z>6cj+$y~ICdll~_NJT77;_z%FNm9vzBeETkbmv^usn;KPrnR!#K#&IK?00_bj9#rl zFX4}ITzQ1`(lIi@5mG&W)4o)%+SO<Wgt^tGtX&;MS+v>+;J|7Q;T8$O3w9g3PH?en zOv^kDy_S12NMJ^McL+13T2>9zVH@2$9^#t;5k`LiN-e)vio{yr#(Mk(ywi<fe?TM> zRWNCH#xbz5`_17cqI(L5to_Z-j$K_I+_jFi^%)_p-N}|>c>%{9!nvOV#!>)qjV^=F zO63m;2(!=7_pm1gO88nAb`iChB2s;tNjG-+c|7<Q3!t4WVO2TBdttA@cFp=p-IxPc zLDj241cQ2*0Ezp4kqcQ@q4CH(-et%)2yP%eS9h+&%4Uo3i>DgZ<yoit;CEOy?qt+w zktC?dQh9h(zZrY;n#K6@*AVIH9eUMz6_YPSW?^wu-M_w?k-OAN5rrPymG+zJd5KNY z91r3|G{=1=sJ~a`ugP%oKxT`WT;IBaRl~4ZkK6Q%Oe=_v(RGvt&uqUNV6}km7*Y4P z#OAccy5Ue^3^9)HWZ4D*keC$!59+z`SAhe}_sCxZ7RX^&tt|mnpTqaieTy{e*u>N? z<A(YbCXvh_xc?&#?tq2XA@)?0-(!_aEH|P)AAAM&mGdYssr53R|5s+iX2-6)05Lb3 zSG-^2t>BMToy6g>b_oI0XTtI}S`=(QC4cC7nHYFR<|(_X^JRU;ZB8(qT9)8J)JC|? zmD}`I8Qldr!uf=;(6cj+gY9ox&1!=ipJ39nhaffC=LX>(*n1`jUZZi14NkBul7oP; zr*tsaLVX1bP>WP@Trl}^y{Wh0shhw!`{>LJmYb{2%#6tnCop-cvi%6|pFWMdddHdM z@@&M)6tO}DR7)+cgO7N#tUADvNc2fCxGTBl?byddFW_+!aUqCnz`YT<hd0zCxQF>b zm7H)sB6~!U5JL8<$B;K{AB_*c7mbu_bofKE@@hq+0!w&0crEswSqqLImvfNpspc%b z5!<R+sY6qK2xDLT*V&yUT6g&w)}A~82Lm{pQWKJt{LU}}U_guq8Z^L#32w_yGw9@l zg!c<fzKA3cuZuV@L;S?G2H{j<q(pd&IMFMM0M4B$I5jrHc1cMH?$$Ia;Uw*QI*WUP z;4M_$VV25V(5rO8i>SH`>e#S;dnC(+oK%}*REXO0ui?S}W+P<P7%y8YVf}XAO;jXA z_g#3R{W*@clvPLR%X6}Op&DVqSzcq_F0U!<IVKt~6*JIt$9uHX?KEy+#~4Ox`h$<7 ztw~?Nb=mbAv6i}*p9rKOxc+FZg_Yo4Ea+)Is9-UpQ6h;QQpVbkuo?9mI7@U5l;2Z0 zbW;ILSY2sAR>TI3?4GV>2N1K#dbP3X&2CK8;L1z$sDs?=!SyNDhCxBBS)k+<bbJbN z=NHd{8g!{Cf}(nv(C~n0{2b3;WRj{*Z@VRvmTzJWs0ov~u<jE66*v@}RlkXQ-Roj} z;9zd8imzMj*7y&iFW33JUKz_ir&b}7<`AH_W~3g(7W{z~aRi#x1h`lh0}lwkQMn%H z?o`;pueCbAfT&luh7E;mcMh8~wJA46RJvHPS-by&y_vAT3${WNQHKq!w3LnIt=H^w zTC)Gj>dFc;tG7*wA;Xd1$uH005dBDcv>|r6i|K9_ZHHbkV_*7KJ%wU*5Kr$SdYLO+ zGKT;#bY@g`<FvW3b7gNmi|q=<MK_i$qw8*0w8EaE;BY#-BP>hQuVU6xUqPW^y%M_7 zaXdj&AuA41Tao42!EOlJ!m{mjm1<IkjR>vW%m`!C5yn8}g|$*J6~I*h_+COn!9%7Z zJE3X(6I{KC@c1RY51d4%To0^4fEB%dx(jq)C3K_{|L|N6iY~D|sU;)I2C`lzl#ANM zz>8>sG#!M|dCL?Q?-Fxi9($TKwf%u%L|X@U=*_%nP3Xtk0>Qej+Q+&N<pI+abmcsE zvg)?1mfx@Qk|o9-3#*dTmfC=&zJ||3M6)Vni5d=(L02B8x_M!O{zo0D2eoX1oi_Ih zQ4?1V=!iJHQ)0I!4wYjsWG)uW6jDsnHHPwgAD22J{#F_=^l9oP+S;01Ra{}4d$!#f zI1jN+-<r)$D6v_YwA0`-hP%UR!{$PQy2LkVFIcS*d$%F-9nyr70tK0NE57I;t`Mpp z*6^Y};18SZ#FxbPfmO1rpC>H7wU%zGU&q5|LR|KgFl`8ncV!jt6LsMqJ-L%OJeHGC z2=~JkakDItL05!cvao=~EVyW)g;^V1vXF}z>LoJz%n}(Tvt(hvJD?ZGz_zJ_W@(He zrw{V<7Qgu9v(HaqzdN_{m$YlC3t|U4StRp_cX74uEvy~EK1c8oLr#JM*d)Ny3uba} zJ#HJwo#?O+AvMpWS&NM%UF<IhLP5WxUa9PNU$DJ})@s86mGpOb>>jtVSCtS=_kRs! z$_@J%eC$={U?R6{N0ya5SZ^M*0c>r_o~tWtT#CsO<anCE8g$79fy;P{9xU>RUL#P% zyPlr`VQwJxi?u5D2E`-}y{#*NYdF~Tpx$vSRdji6c&}@%Rf<*22*TDa?v?M5IDd`< ze}DAQbS<w>r6I75PvOw*S}bnrZqQv34qAboMt!uMU+fWoOF_C@EEPuq3RUNy;z#{6 zCf{W8&zUfHKmTN_wxoU+r$Nwd>eZy66g{*aGMrc-)I~en16oJACyVA^LWXOvCuSMK z8z!>~DUBe>u`iy)$3s37xbFHL76Gp}YXR0)2!@~tJm1+F0T6qqhIizT@9_{6MlLE* z`Eyv84=e0p4>wt62na;j@+qd!%Ak|MFfuwBZRw1LQ%^t9Yuo-F?gr>P#5Y87p>CXR zuP@<VJ3`+Ox|snk={yiZ4}RVt&jo0kpHeTU=AM-ZO8VA7d4QVLpd!Xv{VNvBYD0F6 z^L(0#fEZhFy6gA@-&`kjS~DErLP(<_WSgx@!LkHG(pD>0f&b5V<x`_dq=P6b{6G{H zae_OUU|Bj)pP35sjGChUad1PURE&&+xc5H7OR_*wxh;g%bWqV;^q8KavGN2y>CR=c zO7VMxY+*%gOJy4}6@lb2g+p9@v?-HIev?|HPpAOvO2sL07+<IP<YC-<g&<H*rZQ4Q zY)ofI2nckQV9`SYz`q2C(~gWW3;0{qCK$6~@PTpW!S#3p!UOl<kj9!pMLg4-&+#m@ z+dzo02!!<rH<5#dn`HSXv`B0`iUGI^<yxW*NVU4Pf%}oe53+7_q;s(_&RrR1{o=fa z8dB~fq_wLY8NsKN9Oke_Gr@)#*+|%Rr>{RGGMHO!i%k9~VIc=$6mWNhVan<7oe&6O znPpGm@V<nE0MMSeYI(!NSLfq;%pS(Ib>$juJ5E=p3qg(o0mambpMy&Fgv0&Lga+a& z?`;<s`}lslwAjxHM0C~saPH+S))1wc-s5E64~YZ~=h(|Rj>&*pJ$o~!F~Q<u8)Gs9 zSd#81^aP~K?U!)xMI3l`-~~>>(2x$2u4Ze4!t`SzEzY5hQEd6WihKEDH6iIWhNN5Y zd#g3{)yh?0#w)t}c${ZpKj9ZV7$ukrA^gF1r9Q*tA24A%XTI#)L;m<CZk9{4Yk?&1 zDvW*~FMXV(VJp|0nR24jei(an?bF3yMD)Il{$xinfE$u7!IC`@ps~JBI=r0v7IIeq z0twvKU3M@Mj{t>~9t@xi)dHV_QZHuChN>4rJI1qr&4iH08uSn^MPB_5&MvS@4D7-B zXACdsE#|Ql8V8eli+uxm#did*uooc9_2LkgTfxaX;sdAzbCVcsNJU>Un1!7f)?~t1 z5+{hX^LVGnNMoqLdW6);A+{hpkMCe9vD-t_uk<4J1uLOrzgJ6}!?;&m&vBLyBp1h~ zdZOv!fL>guBEQKrBER=z<yvtj$*N=++97y*QB=4iD!kO&D=TIvMs6-bYZ5(_jV#nJ zA@O@;W6~JjLJ|KZqKpj`dd3}%`usxsvddI<NH_BUy7_x$4g0X=G6SMgAg3olAm4KT zR)Nm9=DGp(TOm*x>_E92`C+5G#$_`rL9@w+aaT6>lcwSKxy{vO+5xUQ&L@<A+?bF% zar*_mFKf9+yaVMzdj*^9rPrMHcW+Yt&l4Jk4rL%q84#b(W}%kxz&dTY0`>teTIxUI zd5tdxf5gco4v#W9VHN0w@nhpuvY=j<CC<SfgZ|KSh#_;GXWe=1QmfXg-+%x)V+WVo zg+awxR=>y8Wv($EZkP^FirzI%HfXIHlSwPvjjG3~&Z3CVRy}xpVJB4WgC5H}Xa_@D zM&&-o#mi^8i8VGTF;lcWdu9fm2=wwwWo9N&>JMrT#Fp{2>hxaPe21RwnVID^WojZ@ zqtgm7o29@S2?~g|h8Ixb<q;@rtBPBm+XA71YXWl|tIK*M&Lzwf@(K34#!T!6wsYoO z>jl^}!P4w{gK|?6EEHHYvZHvoCwl;&9-1@xOOab;f4Sahm@-f_hW}{&2u)6)WbB5H z{kTCTU9NVZCv+XM%M{s#@{76-7G4({M7-$GM5)JDxe(}7xSPDLO1Lddl2L-)xo!My zM3QZv={9;vL`hAIM^oG6%z_Bz&hDc91zVkCU-%eBu+94%j6ZN7c*(f(43+r(222u$ zfXmV}VoN3M3^DnAyR`&o0OROnw|TOYa8Dcr`>3~yt=X&a9vIN#89*z+9`t&c10q3T zAR~MXcNW}7-11(y$nOg&?iT}&fJ_|(b<`g-`4cA6|3sY-M=Spz2O^4+-!InN?j%_@ z+Hj<NKMtWjbAUQR&ypp3DZ#HqLkTKKd{tbiMVv&5H=#^kg$5k$sMMn&`!+HX4MDFv zN|2$72>9$;QjjyWQn%wjCHN)0LCx6gs<g2R`rT0egDa!K1)@V&0jZf$-+AC!*%=|` z|BW^FnA>*)Md=6=RI@^xD4jWQ%U=*~qB4`~jHFPG$6=4pSMaLFwrIM<A^}FE&?(qh zY-gQA&mlyWux!EZ*cKzJ2$jGWAopqBNU=o&_rALYe9*@es0_J*dXHWqS;I$lRHo{a z7qGZI|Cy=!8g6@vyFqSj7t%E0a|g`12xdJSzH6+lnnclmMVQ4(%E_Y0up&~Jm8t1n zLV%+2o<BuC>d%-E3zBUNJyFy7BRu^E%VW=797I^>HfU{FMMd#R4M+G#5>DcHqdn`6 zb0QGnrKvW8{<L%cOtjBdva&N;M!{{y0FRj&8ZcD{VrX18l4o}&(&Q4B<dANM-Ij@= zG=e6<g{;ODt08_Hk@zzB170+vpU|80TG&vz@_VElAz#34B8gJmMiGL|bp!tm6ubRh zDta9`bP}1YAqs&8SSWptYmUQR?B<x~3JrM96-DCmC$ITSOw`+ui&P+h_ZN911ur?I zNO-l>VYmTo)=i=AB6@PjK`=?);SiY^jgX4;d=U^7@8CzHt~<YzMYj<Ol7+*Y-Efu` zBq0e|u!!Tt{LgRWZcx5R-pz!=$n^@|R6-m=5qi(P87n3mu*pYEHlY8Y4Q5(?NSW@^ zRGR2c0;K&TrVrTL4Igh_^Z!q%$KQaCy7_aFB&<=*$Zo|ap>!m|N$naA&qi`(pA20{ z=rk>{aFP*+B(zLC77pi5*hi1noujR><916g>eGr3HI6|#hEtL}WZu<w{sbOik$Tg- zawSV<suI8Y|NRor&%N8{R@<uzGs_jB)}hLu!I@u>l{wntB-ZORP+ibNw8XieQGLAe z4|(2)^-MY#-gMi3-=j}GbMB?ebB~@An@F*}sGUsiVDc!FyP1qLd4S0jlk-g8#pD7L zk;6+odzA?zEG5cLBB#`E;nMFn17q~h`y(yAa~C`cjn(BQ2iN@3AaX9a$v4F-0ZqbK zpz05q^D>iPWAYnJIDjm}kTUkCzRJt5G5I<ZG0^`V&uC7G<QMwu$-t4^kTcq_2*p}% zIjar#K_bq35lKEvuUFC9&)-}jH@t25$>GuA-jQ2}pBOHU6ozMpmxkYyd1iS3@bK^s pIy~?%SAtDf%9RF(Zz}ed28x5lzG5F@4+e+d2`1b*eE;x){|Dg%o)-WB literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/util/__pycache__/compat.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/util/__pycache__/compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7be9b5a188f3892d820f0ec7b23a34194e9f73c2 GIT binary patch literal 8936 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 MTqsUeUZL8516Cf2P5=M^ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/util/__pycache__/deprecations.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/util/__pycache__/deprecations.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb66736c958042a53d4fe772d3bd978f9074d280 GIT binary patch literal 4593 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 J<*HMi`!~WDg5m%G literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/util/__pycache__/langhelpers.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/util/__pycache__/langhelpers.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a856777e5c0b7927de50bcaa832d123009b4cee9 GIT binary patch literal 42056 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 YM5VItV7bD-X&^Q~eJ}2$@tH&aFWTx28~^|S literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/util/__pycache__/queue.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/util/__pycache__/queue.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a5d3dc34dd7a26d88ec5c0b1970560d8a873dee GIT binary patch literal 5826 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 S32o9c7IplYuDMcKsr(l-&aj^V literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/util/__pycache__/topological.cpython-36.pyc b/venv/Lib/site-packages/sqlalchemy/util/__pycache__/topological.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..079d68062cde57f74561b6d0be5113d7e7d7e7f8 GIT binary patch literal 1992 zcmZuyPj4GV6rY*>>$Q`nsc0Milodis4T^;X5)!H+)Rsyd2vt+4WVx)iJ7Z^^wRbZ! z4t6wq!A11Y3lc}fi3=Zq55SEx$GLJUAE6g`Z{4H;G4^|YJNxF%%<sM5^ZRRS(frp> zlfNQD{v_vy1NO%tY7Z1AeZnbc1)Z=yqi8#YGjaPa*e>_DKXUpWU*{qB9@D<hFYp$3 z!3uc9{l}yq@;2|lZ;P+(lkUdJoA=AAEXq-yrA4etsq^V5PK!|~bA2#Ty=M$|?NWQ? zRxYzyk;>bm5>qaw+2Ru^O9`*sna&IA;$nLNb&{$?%^oSCm2D66DNnLRRtR+`!@J;s zI}#5h15rPN(qv3EThiK@Uo+%3d3briYPa@`W4tXgc8t5ElpJk5Acn~gz}uZN&1&EH zhQg{~{*?*jPsU%8I-I|40*LT>-7>)#Beb^2lFEA~ggC7x&OL||s;AoLR0oDY45mZl zEa}quhQg|r3DM7lUlQ2$<^#YT&|-ThZ=Avq^D`X4)FRV@_qvh98`?G(!*o_?o@d%R zfP-~X&TaI$<U$I*4--+Yc&^4}KGhN-mOd_wl98Auf{%o<7kNG$g8MWR$*kh37Tw7D zdR76FHcX2m*J7gVCEQ(3@@cMgo+Tx2VZBudze-`fwqACS7u-6yv8zj7TECp>Y6ef! zl8ax6yb8NNAK&_D@P!gm4HnyKdoj2z)S)h`!OgNf-1#h>rXw+da69`81zYb-QuvXB zV=+A*6#1ipf&=YTX?6&HYM_paw8##`WHG?{7(k%YI_p&niP_%(QE!1FETZdlldZFm z`P8TQw9Z`0;1klwiRfA7;5rwishl1d^Xr;m1`S&}koBujL7N7xsCJFp<nQVOa<mRP zq?p5#->^hueIVjd4rh-G04^?vaRX=kXmKvHy(o%dY_CAyj&l{KXa1|nSc*yt1+X;4 zi2-q)1C^RQz*KJ);eV2}9Ulr&sTccx4Rp(=5dK_U>0HPdCRZoK#IB<4b!mfN*OTjj zfV_YT!2}Wtomi@xe6G|MzC^^{1yNXKB-jLmGpsrs9S}tI7Kr)-)EO7qIW9VYCNs{z zxu_YSPHPXCOKN}qgYf|Or*&Y05v@bxja}qX-I^bpQ2uU0&bCM$%{fGhwvgp_(bt5B zzWmXIz)#NvOUlW&=+`!_v2WT95B+iQ!U{|ayvN`hqAzf?1#E4syX0t$ydFnzn&9X@ zWPKN&eaO1A_l&~Lp8XA@+iplI1DKE@sun04y`-D<pl6A>T*G;P#Xak%6*fNWOED>r zg>|a3>UPd0;39_FM0E*Ne+`+X5?v-*C@nAJiz}e4r!;UxzJj&`tmr$?9sip!%+rS; z>K#zTZRkOP9POx~hYz$^2f;)}3@8F0pKZ_#Z;1ncG3gG7!o4;5jtnXObvzEA0QAu6 zkPgn?*%EZcI>6I6tQ-Na9==-BC%~>ITb&y6ZRn7F?;LF$x%;cO(Hy<&Sk`;bx`h}* zyOH_mKtqu>kYH<(ScT7{8(7I%IjMG2+yoEk1I16Sy=;5E8$}6!MQRs}Q)h^Tc{(5P z)NZO9D>%V`O?5-Sh)SFTp4`E2WP>E(WtJq?5%a9+x0bA0HN1gVnj{UO8lE(rxLJQL gDQB0k;f+bjXNB0sBPryc%R02fpi8|Lb;9fa0BjxXZU6uP literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/sqlalchemy/util/_collections.py b/venv/Lib/site-packages/sqlalchemy/util/_collections.py new file mode 100644 index 0000000..d8e389c --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/util/_collections.py @@ -0,0 +1,1056 @@ +# util/_collections.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Collection classes and helpers.""" + +from __future__ import absolute_import +import weakref +import operator +from .compat import threading, itertools_filterfalse, string_types, \ + binary_types, collections_abc +from . import py2k +import types + + +EMPTY_SET = frozenset() + + +class AbstractKeyedTuple(tuple): + __slots__ = () + + def keys(self): + """Return a list of string key names for this :class:`.KeyedTuple`. + + .. seealso:: + + :attr:`.KeyedTuple._fields` + + """ + + return list(self._fields) + + +class KeyedTuple(AbstractKeyedTuple): + """``tuple`` subclass that adds labeled names. + + E.g.:: + + >>> k = KeyedTuple([1, 2, 3], labels=["one", "two", "three"]) + >>> k.one + 1 + >>> k.two + 2 + + Result rows returned by :class:`.Query` that contain multiple + ORM entities and/or column expressions make use of this + class to return rows. + + The :class:`.KeyedTuple` exhibits similar behavior to the + ``collections.namedtuple()`` construct provided in the Python + standard library, however is architected very differently. + Unlike ``collections.namedtuple()``, :class:`.KeyedTuple` is + does not rely on creation of custom subtypes in order to represent + a new series of keys, instead each :class:`.KeyedTuple` instance + receives its list of keys in place. The subtype approach + of ``collections.namedtuple()`` introduces significant complexity + and performance overhead, which is not necessary for the + :class:`.Query` object's use case. + + .. versionchanged:: 0.8 + Compatibility methods with ``collections.namedtuple()`` have been + added including :attr:`.KeyedTuple._fields` and + :meth:`.KeyedTuple._asdict`. + + .. seealso:: + + :ref:`ormtutorial_querying` + + """ + + def __new__(cls, vals, labels=None): + t = tuple.__new__(cls, vals) + if labels: + t.__dict__.update(zip(labels, vals)) + else: + labels = [] + t.__dict__['_labels'] = labels + return t + + @property + def _fields(self): + """Return a tuple of string key names for this :class:`.KeyedTuple`. + + This method provides compatibility with ``collections.namedtuple()``. + + .. versionadded:: 0.8 + + .. seealso:: + + :meth:`.KeyedTuple.keys` + + """ + return tuple([l for l in self._labels if l is not None]) + + def __setattr__(self, key, value): + raise AttributeError("Can't set attribute: %s" % key) + + def _asdict(self): + """Return the contents of this :class:`.KeyedTuple` as a dictionary. + + This method provides compatibility with ``collections.namedtuple()``, + with the exception that the dictionary returned is **not** ordered. + + .. versionadded:: 0.8 + + """ + return {key: self.__dict__[key] for key in self.keys()} + + +class _LW(AbstractKeyedTuple): + __slots__ = () + + def __new__(cls, vals): + return tuple.__new__(cls, vals) + + def __reduce__(self): + # for pickling, degrade down to the regular + # KeyedTuple, thus avoiding anonymous class pickling + # difficulties + return KeyedTuple, (list(self), self._real_fields) + + def _asdict(self): + """Return the contents of this :class:`.KeyedTuple` as a dictionary.""" + + d = dict(zip(self._real_fields, self)) + d.pop(None, None) + return d + + +class ImmutableContainer(object): + def _immutable(self, *arg, **kw): + raise TypeError("%s object is immutable" % self.__class__.__name__) + + __delitem__ = __setitem__ = __setattr__ = _immutable + + +class immutabledict(ImmutableContainer, dict): + + clear = pop = popitem = setdefault = \ + update = ImmutableContainer._immutable + + def __new__(cls, *args): + new = dict.__new__(cls) + dict.__init__(new, *args) + return new + + def __init__(self, *args): + pass + + def __reduce__(self): + return immutabledict, (dict(self), ) + + def union(self, d): + if not d: + return self + elif not self: + if isinstance(d, immutabledict): + return d + else: + return immutabledict(d) + else: + d2 = immutabledict(self) + dict.update(d2, d) + return d2 + + def __repr__(self): + return "immutabledict(%s)" % dict.__repr__(self) + + +class Properties(object): + """Provide a __getattr__/__setattr__ interface over a dict.""" + + __slots__ = '_data', + + def __init__(self, data): + object.__setattr__(self, '_data', data) + + def __len__(self): + return len(self._data) + + def __iter__(self): + return iter(list(self._data.values())) + + def __add__(self, other): + return list(self) + list(other) + + def __setitem__(self, key, object): + self._data[key] = object + + def __getitem__(self, key): + return self._data[key] + + def __delitem__(self, key): + del self._data[key] + + def __setattr__(self, key, obj): + self._data[key] = obj + + def __getstate__(self): + return {'_data': self._data} + + def __setstate__(self, state): + object.__setattr__(self, '_data', state['_data']) + + def __getattr__(self, key): + try: + return self._data[key] + except KeyError: + raise AttributeError(key) + + def __contains__(self, key): + return key in self._data + + def as_immutable(self): + """Return an immutable proxy for this :class:`.Properties`.""" + + return ImmutableProperties(self._data) + + def update(self, value): + self._data.update(value) + + def get(self, key, default=None): + if key in self: + return self[key] + else: + return default + + def keys(self): + return list(self._data) + + def values(self): + return list(self._data.values()) + + def items(self): + return list(self._data.items()) + + def has_key(self, key): + return key in self._data + + def clear(self): + self._data.clear() + + +class OrderedProperties(Properties): + """Provide a __getattr__/__setattr__ interface with an OrderedDict + as backing store.""" + + __slots__ = () + + def __init__(self): + Properties.__init__(self, OrderedDict()) + + +class ImmutableProperties(ImmutableContainer, Properties): + """Provide immutable dict/object attribute to an underlying dictionary.""" + + __slots__ = () + + +class OrderedDict(dict): + """A dict that returns keys/values/items in the order they were added.""" + + __slots__ = '_list', + + def __reduce__(self): + return OrderedDict, (self.items(),) + + def __init__(self, ____sequence=None, **kwargs): + self._list = [] + if ____sequence is None: + if kwargs: + self.update(**kwargs) + else: + self.update(____sequence, **kwargs) + + def clear(self): + self._list = [] + dict.clear(self) + + def copy(self): + return self.__copy__() + + def __copy__(self): + return OrderedDict(self) + + def sort(self, *arg, **kw): + self._list.sort(*arg, **kw) + + def update(self, ____sequence=None, **kwargs): + if ____sequence is not None: + if hasattr(____sequence, 'keys'): + for key in ____sequence.keys(): + self.__setitem__(key, ____sequence[key]) + else: + for key, value in ____sequence: + self[key] = value + if kwargs: + self.update(kwargs) + + def setdefault(self, key, value): + if key not in self: + self.__setitem__(key, value) + return value + else: + return self.__getitem__(key) + + def __iter__(self): + return iter(self._list) + + def keys(self): + return list(self) + + def values(self): + return [self[key] for key in self._list] + + def items(self): + return [(key, self[key]) for key in self._list] + + if py2k: + def itervalues(self): + return iter(self.values()) + + def iterkeys(self): + return iter(self) + + def iteritems(self): + return iter(self.items()) + + def __setitem__(self, key, object): + if key not in self: + try: + self._list.append(key) + except AttributeError: + # work around Python pickle loads() with + # dict subclass (seems to ignore __setstate__?) + self._list = [key] + dict.__setitem__(self, key, object) + + def __delitem__(self, key): + dict.__delitem__(self, key) + self._list.remove(key) + + def pop(self, key, *default): + present = key in self + value = dict.pop(self, key, *default) + if present: + self._list.remove(key) + return value + + def popitem(self): + item = dict.popitem(self) + self._list.remove(item[0]) + return item + + +class OrderedSet(set): + def __init__(self, d=None): + set.__init__(self) + self._list = [] + if d is not None: + self._list = unique_list(d) + set.update(self, self._list) + else: + self._list = [] + + def add(self, element): + if element not in self: + self._list.append(element) + set.add(self, element) + + def remove(self, element): + set.remove(self, element) + self._list.remove(element) + + def insert(self, pos, element): + if element not in self: + self._list.insert(pos, element) + set.add(self, element) + + def discard(self, element): + if element in self: + self._list.remove(element) + set.remove(self, element) + + def clear(self): + set.clear(self) + self._list = [] + + def __getitem__(self, key): + return self._list[key] + + def __iter__(self): + return iter(self._list) + + def __add__(self, other): + return self.union(other) + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, self._list) + + __str__ = __repr__ + + def update(self, iterable): + for e in iterable: + if e not in self: + self._list.append(e) + set.add(self, e) + return self + + __ior__ = update + + def union(self, other): + result = self.__class__(self) + result.update(other) + return result + + __or__ = union + + def intersection(self, other): + other = set(other) + return self.__class__(a for a in self if a in other) + + __and__ = intersection + + def symmetric_difference(self, other): + other = set(other) + result = self.__class__(a for a in self if a not in other) + result.update(a for a in other if a not in self) + return result + + __xor__ = symmetric_difference + + def difference(self, other): + other = set(other) + return self.__class__(a for a in self if a not in other) + + __sub__ = difference + + def intersection_update(self, other): + other = set(other) + set.intersection_update(self, other) + self._list = [a for a in self._list if a in other] + return self + + __iand__ = intersection_update + + def symmetric_difference_update(self, other): + set.symmetric_difference_update(self, other) + self._list = [a for a in self._list if a in self] + self._list += [a for a in other._list if a in self] + return self + + __ixor__ = symmetric_difference_update + + def difference_update(self, other): + set.difference_update(self, other) + self._list = [a for a in self._list if a in self] + return self + + __isub__ = difference_update + + +class IdentitySet(object): + """A set that considers only object id() for uniqueness. + + This strategy has edge cases for builtin types- it's possible to have + two 'foo' strings in one of these sets, for example. Use sparingly. + + """ + + _working_set = set + + def __init__(self, iterable=None): + self._members = dict() + if iterable: + for o in iterable: + self.add(o) + + def add(self, value): + self._members[id(value)] = value + + def __contains__(self, value): + return id(value) in self._members + + def remove(self, value): + del self._members[id(value)] + + def discard(self, value): + try: + self.remove(value) + except KeyError: + pass + + def pop(self): + try: + pair = self._members.popitem() + return pair[1] + except KeyError: + raise KeyError('pop from an empty set') + + def clear(self): + self._members.clear() + + def __cmp__(self, other): + raise TypeError('cannot compare sets using cmp()') + + def __eq__(self, other): + if isinstance(other, IdentitySet): + return self._members == other._members + else: + return False + + def __ne__(self, other): + if isinstance(other, IdentitySet): + return self._members != other._members + else: + return True + + def issubset(self, iterable): + other = type(self)(iterable) + + if len(self) > len(other): + return False + for m in itertools_filterfalse(other._members.__contains__, + iter(self._members.keys())): + return False + return True + + def __le__(self, other): + if not isinstance(other, IdentitySet): + return NotImplemented + return self.issubset(other) + + def __lt__(self, other): + if not isinstance(other, IdentitySet): + return NotImplemented + return len(self) < len(other) and self.issubset(other) + + def issuperset(self, iterable): + other = type(self)(iterable) + + if len(self) < len(other): + return False + + for m in itertools_filterfalse(self._members.__contains__, + iter(other._members.keys())): + return False + return True + + def __ge__(self, other): + if not isinstance(other, IdentitySet): + return NotImplemented + return self.issuperset(other) + + def __gt__(self, other): + if not isinstance(other, IdentitySet): + return NotImplemented + return len(self) > len(other) and self.issuperset(other) + + def union(self, iterable): + result = type(self)() + # testlib.pragma exempt:__hash__ + members = self._member_id_tuples() + other = _iter_id(iterable) + result._members.update(self._working_set(members).union(other)) + return result + + def __or__(self, other): + if not isinstance(other, IdentitySet): + return NotImplemented + return self.union(other) + + def update(self, iterable): + self._members = self.union(iterable)._members + + def __ior__(self, other): + if not isinstance(other, IdentitySet): + return NotImplemented + self.update(other) + return self + + def difference(self, iterable): + result = type(self)() + # testlib.pragma exempt:__hash__ + members = self._member_id_tuples() + other = _iter_id(iterable) + result._members.update(self._working_set(members).difference(other)) + return result + + def __sub__(self, other): + if not isinstance(other, IdentitySet): + return NotImplemented + return self.difference(other) + + def difference_update(self, iterable): + self._members = self.difference(iterable)._members + + def __isub__(self, other): + if not isinstance(other, IdentitySet): + return NotImplemented + self.difference_update(other) + return self + + def intersection(self, iterable): + result = type(self)() + # testlib.pragma exempt:__hash__ + members = self._member_id_tuples() + other = _iter_id(iterable) + result._members.update(self._working_set(members).intersection(other)) + return result + + def __and__(self, other): + if not isinstance(other, IdentitySet): + return NotImplemented + return self.intersection(other) + + def intersection_update(self, iterable): + self._members = self.intersection(iterable)._members + + def __iand__(self, other): + if not isinstance(other, IdentitySet): + return NotImplemented + self.intersection_update(other) + return self + + def symmetric_difference(self, iterable): + result = type(self)() + # testlib.pragma exempt:__hash__ + members = self._member_id_tuples() + other = _iter_id(iterable) + result._members.update( + self._working_set(members).symmetric_difference(other)) + return result + + def _member_id_tuples(self): + return ((id(v), v) for v in self._members.values()) + + def __xor__(self, other): + if not isinstance(other, IdentitySet): + return NotImplemented + return self.symmetric_difference(other) + + def symmetric_difference_update(self, iterable): + self._members = self.symmetric_difference(iterable)._members + + def __ixor__(self, other): + if not isinstance(other, IdentitySet): + return NotImplemented + self.symmetric_difference(other) + return self + + def copy(self): + return type(self)(iter(self._members.values())) + + __copy__ = copy + + def __len__(self): + return len(self._members) + + def __iter__(self): + return iter(self._members.values()) + + def __hash__(self): + raise TypeError('set objects are unhashable') + + def __repr__(self): + return '%s(%r)' % (type(self).__name__, list(self._members.values())) + + +class WeakSequence(object): + def __init__(self, __elements=()): + self._storage = [ + weakref.ref(element, self._remove) for element in __elements + ] + + def append(self, item): + self._storage.append(weakref.ref(item, self._remove)) + + def _remove(self, ref): + self._storage.remove(ref) + + def __len__(self): + return len(self._storage) + + def __iter__(self): + return (obj for obj in + (ref() for ref in self._storage) if obj is not None) + + def __getitem__(self, index): + try: + obj = self._storage[index] + except KeyError: + raise IndexError("Index %s out of range" % index) + else: + return obj() + + +class OrderedIdentitySet(IdentitySet): + class _working_set(OrderedSet): + # a testing pragma: exempt the OIDS working set from the test suite's + # "never call the user's __hash__" assertions. this is a big hammer, + # but it's safe here: IDS operates on (id, instance) tuples in the + # working set. + __sa_hash_exempt__ = True + + def __init__(self, iterable=None): + IdentitySet.__init__(self) + self._members = OrderedDict() + if iterable: + for o in iterable: + self.add(o) + + +class PopulateDict(dict): + """A dict which populates missing values via a creation function. + + Note the creation function takes a key, unlike + collections.defaultdict. + + """ + + def __init__(self, creator): + self.creator = creator + + def __missing__(self, key): + self[key] = val = self.creator(key) + return val + +# Define collections that are capable of storing +# ColumnElement objects as hashable keys/elements. +# At this point, these are mostly historical, things +# used to be more complicated. +column_set = set +column_dict = dict +ordered_column_set = OrderedSet +populate_column_dict = PopulateDict + + +_getters = PopulateDict(operator.itemgetter) + +_property_getters = PopulateDict( + lambda idx: property(operator.itemgetter(idx))) + + +def unique_list(seq, hashfunc=None): + seen = set() + seen_add = seen.add + if not hashfunc: + return [x for x in seq + if x not in seen + and not seen_add(x)] + else: + return [x for x in seq + if hashfunc(x) not in seen + and not seen_add(hashfunc(x))] + + +class UniqueAppender(object): + """Appends items to a collection ensuring uniqueness. + + Additional appends() of the same object are ignored. Membership is + determined by identity (``is a``) not equality (``==``). + """ + + def __init__(self, data, via=None): + self.data = data + self._unique = {} + if via: + self._data_appender = getattr(data, via) + elif hasattr(data, 'append'): + self._data_appender = data.append + elif hasattr(data, 'add'): + self._data_appender = data.add + + def append(self, item): + id_ = id(item) + if id_ not in self._unique: + self._data_appender(item) + self._unique[id_] = True + + def __iter__(self): + return iter(self.data) + + +def coerce_generator_arg(arg): + if len(arg) == 1 and isinstance(arg[0], types.GeneratorType): + return list(arg[0]) + else: + return arg + + +def to_list(x, default=None): + if x is None: + return default + if not isinstance(x, collections_abc.Iterable) or \ + isinstance(x, string_types + binary_types): + return [x] + elif isinstance(x, list): + return x + else: + return list(x) + + +def has_intersection(set_, iterable): + """return True if any items of set_ are present in iterable. + + Goes through special effort to ensure __hash__ is not called + on items in iterable that don't support it. + + """ + # TODO: optimize, write in C, etc. + return bool( + set_.intersection([i for i in iterable if i.__hash__]) + ) + + +def to_set(x): + if x is None: + return set() + if not isinstance(x, set): + return set(to_list(x)) + else: + return x + + +def to_column_set(x): + if x is None: + return column_set() + if not isinstance(x, column_set): + return column_set(to_list(x)) + else: + return x + + +def update_copy(d, _new=None, **kw): + """Copy the given dict and update with the given values.""" + + d = d.copy() + if _new: + d.update(_new) + d.update(**kw) + return d + + +def flatten_iterator(x): + """Given an iterator of which further sub-elements may also be + iterators, flatten the sub-elements into a single iterator. + + """ + for elem in x: + if not isinstance(elem, str) and hasattr(elem, '__iter__'): + for y in flatten_iterator(elem): + yield y + else: + yield elem + + +class LRUCache(dict): + """Dictionary with 'squishy' removal of least + recently used items. + + Note that either get() or [] should be used here, but + generally its not safe to do an "in" check first as the dictionary + can change subsequent to that call. + + """ + + __slots__ = 'capacity', 'threshold', 'size_alert', '_counter', '_mutex' + + def __init__(self, capacity=100, threshold=.5, size_alert=None): + self.capacity = capacity + self.threshold = threshold + self.size_alert = size_alert + self._counter = 0 + self._mutex = threading.Lock() + + def _inc_counter(self): + self._counter += 1 + return self._counter + + def get(self, key, default=None): + item = dict.get(self, key, default) + if item is not default: + item[2] = self._inc_counter() + return item[1] + else: + return default + + def __getitem__(self, key): + item = dict.__getitem__(self, key) + item[2] = self._inc_counter() + return item[1] + + def values(self): + return [i[1] for i in dict.values(self)] + + def setdefault(self, key, value): + if key in self: + return self[key] + else: + self[key] = value + return value + + def __setitem__(self, key, value): + item = dict.get(self, key) + if item is None: + item = [key, value, self._inc_counter()] + dict.__setitem__(self, key, item) + else: + item[1] = value + self._manage_size() + + @property + def size_threshold(self): + return self.capacity + self.capacity * self.threshold + + def _manage_size(self): + if not self._mutex.acquire(False): + return + try: + size_alert = bool(self.size_alert) + while len(self) > self.capacity + self.capacity * self.threshold: + if size_alert: + size_alert = False + self.size_alert(self) + by_counter = sorted(dict.values(self), + key=operator.itemgetter(2), + reverse=True) + for item in by_counter[self.capacity:]: + try: + del self[item[0]] + except KeyError: + # deleted elsewhere; skip + continue + finally: + self._mutex.release() + + +_lw_tuples = LRUCache(100) + + +def lightweight_named_tuple(name, fields): + hash_ = (name, ) + tuple(fields) + tp_cls = _lw_tuples.get(hash_) + if tp_cls: + return tp_cls + + tp_cls = type( + name, (_LW,), + dict([ + (field, _property_getters[idx]) + for idx, field in enumerate(fields) if field is not None + ] + [('__slots__', ())]) + ) + + tp_cls._real_fields = fields + tp_cls._fields = tuple([f for f in fields if f is not None]) + + _lw_tuples[hash_] = tp_cls + return tp_cls + + +class ScopedRegistry(object): + """A Registry that can store one or multiple instances of a single + class on the basis of a "scope" function. + + The object implements ``__call__`` as the "getter", so by + calling ``myregistry()`` the contained object is returned + for the current scope. + + :param createfunc: + a callable that returns a new object to be placed in the registry + + :param scopefunc: + a callable that will return a key to store/retrieve an object. + """ + + def __init__(self, createfunc, scopefunc): + """Construct a new :class:`.ScopedRegistry`. + + :param createfunc: A creation function that will generate + a new value for the current scope, if none is present. + + :param scopefunc: A function that returns a hashable + token representing the current scope (such as, current + thread identifier). + + """ + self.createfunc = createfunc + self.scopefunc = scopefunc + self.registry = {} + + def __call__(self): + key = self.scopefunc() + try: + return self.registry[key] + except KeyError: + return self.registry.setdefault(key, self.createfunc()) + + def has(self): + """Return True if an object is present in the current scope.""" + + return self.scopefunc() in self.registry + + def set(self, obj): + """Set the value for the current scope.""" + + self.registry[self.scopefunc()] = obj + + def clear(self): + """Clear the current scope, if any.""" + + try: + del self.registry[self.scopefunc()] + except KeyError: + pass + + +class ThreadLocalRegistry(ScopedRegistry): + """A :class:`.ScopedRegistry` that uses a ``threading.local()`` + variable for storage. + + """ + + def __init__(self, createfunc): + self.createfunc = createfunc + self.registry = threading.local() + + def __call__(self): + try: + return self.registry.value + except AttributeError: + val = self.registry.value = self.createfunc() + return val + + def has(self): + return hasattr(self.registry, "value") + + def set(self, obj): + self.registry.value = obj + + def clear(self): + try: + del self.registry.value + except AttributeError: + pass + + +def _iter_id(iterable): + """Generator: ((id(o), o) for o in iterable).""" + + for item in iterable: + yield id(item), item diff --git a/venv/Lib/site-packages/sqlalchemy/util/compat.py b/venv/Lib/site-packages/sqlalchemy/util/compat.py new file mode 100644 index 0000000..02be2b3 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/util/compat.py @@ -0,0 +1,338 @@ +# util/compat.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Handle Python version/platform incompatibilities.""" + +import sys +from contextlib import contextmanager + +try: + import threading +except ImportError: + import dummy_threading as threading + +py36 = sys.version_info >= (3, 6) +py33 = sys.version_info >= (3, 3) +py35 = sys.version_info >= (3, 5) +py32 = sys.version_info >= (3, 2) +py3k = sys.version_info >= (3, 0) +py2k = sys.version_info < (3, 0) +py265 = sys.version_info >= (2, 6, 5) +jython = sys.platform.startswith('java') +pypy = hasattr(sys, 'pypy_version_info') +win32 = sys.platform.startswith('win') +cpython = not pypy and not jython # TODO: something better for this ? + +import collections +next = next + +if py3k: + import pickle +else: + try: + import cPickle as pickle + except ImportError: + import pickle + +# work around http://bugs.python.org/issue2646 +if py265: + safe_kwarg = lambda arg: arg +else: + safe_kwarg = str + +ArgSpec = collections.namedtuple("ArgSpec", + ["args", "varargs", "keywords", "defaults"]) + +if py3k: + import builtins + + from inspect import getfullargspec as inspect_getfullargspec + from urllib.parse import (quote_plus, unquote_plus, + parse_qsl, quote, unquote) + import configparser + from io import StringIO + + from io import BytesIO as byte_buffer + + def inspect_getargspec(func): + return ArgSpec( + *inspect_getfullargspec(func)[0:4] + ) + + string_types = str, + binary_types = bytes, + binary_type = bytes + text_type = str + int_types = int, + iterbytes = iter + + def u(s): + return s + + def ue(s): + return s + + def b(s): + return s.encode("latin-1") + + if py32: + callable = callable + else: + def callable(fn): + return hasattr(fn, '__call__') + + def cmp(a, b): + return (a > b) - (a < b) + + from functools import reduce + + print_ = getattr(builtins, "print") + + import_ = getattr(builtins, '__import__') + + import itertools + itertools_filterfalse = itertools.filterfalse + itertools_filter = filter + itertools_imap = map + from itertools import zip_longest + + import base64 + + def b64encode(x): + return base64.b64encode(x).decode('ascii') + + def b64decode(x): + return base64.b64decode(x.encode('ascii')) + +else: + from inspect import getargspec as inspect_getfullargspec + inspect_getargspec = inspect_getfullargspec + from urllib import quote_plus, unquote_plus, quote, unquote + from urlparse import parse_qsl + import ConfigParser as configparser + from StringIO import StringIO + from cStringIO import StringIO as byte_buffer + + string_types = basestring, + binary_types = bytes, + binary_type = str + text_type = unicode + int_types = int, long + + def iterbytes(buf): + return (ord(byte) for byte in buf) + + def u(s): + # this differs from what six does, which doesn't support non-ASCII + # strings - we only use u() with + # literal source strings, and all our source files with non-ascii + # in them (all are tests) are utf-8 encoded. + return unicode(s, "utf-8") + + def ue(s): + return unicode(s, "unicode_escape") + + def b(s): + return s + + def import_(*args): + if len(args) == 4: + args = args[0:3] + ([str(arg) for arg in args[3]],) + return __import__(*args) + + callable = callable + cmp = cmp + reduce = reduce + + import base64 + b64encode = base64.b64encode + b64decode = base64.b64decode + + def print_(*args, **kwargs): + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + for arg in enumerate(args): + if not isinstance(arg, basestring): + arg = str(arg) + fp.write(arg) + + import itertools + itertools_filterfalse = itertools.ifilterfalse + itertools_filter = itertools.ifilter + itertools_imap = itertools.imap + from itertools import izip_longest as zip_longest + +if py35: + from inspect import formatannotation + + def inspect_formatargspec( + args, varargs=None, varkw=None, defaults=None, + kwonlyargs=(), kwonlydefaults={}, annotations={}, + formatarg=str, + formatvarargs=lambda name: '*' + name, + formatvarkw=lambda name: '**' + name, + formatvalue=lambda value: '=' + repr(value), + formatreturns=lambda text: ' -> ' + text, + formatannotation=formatannotation): + """Copy formatargspec from python 3.7 standard library. + + Python 3 has deprecated formatargspec and requested that Signature + be used instead, however this requires a full reimplementation + of formatargspec() in terms of creating Parameter objects and such. + Instead of introducing all the object-creation overhead and having + to reinvent from scratch, just copy their compatibility routine. + + Utimately we would need to rewrite our "decorator" routine completely + which is not really worth it right now, until all Python 2.x support + is dropped. + + """ + + def formatargandannotation(arg): + result = formatarg(arg) + if arg in annotations: + result += ': ' + formatannotation(annotations[arg]) + return result + specs = [] + if defaults: + firstdefault = len(args) - len(defaults) + for i, arg in enumerate(args): + spec = formatargandannotation(arg) + if defaults and i >= firstdefault: + spec = spec + formatvalue(defaults[i - firstdefault]) + specs.append(spec) + if varargs is not None: + specs.append(formatvarargs(formatargandannotation(varargs))) + else: + if kwonlyargs: + specs.append('*') + if kwonlyargs: + for kwonlyarg in kwonlyargs: + spec = formatargandannotation(kwonlyarg) + if kwonlydefaults and kwonlyarg in kwonlydefaults: + spec += formatvalue(kwonlydefaults[kwonlyarg]) + specs.append(spec) + if varkw is not None: + specs.append(formatvarkw(formatargandannotation(varkw))) + result = '(' + ', '.join(specs) + ')' + if 'return' in annotations: + result += formatreturns(formatannotation(annotations['return'])) + return result + +else: + from inspect import formatargspec as inspect_formatargspec + + + +import time +if win32 or jython: + time_func = time.clock +else: + time_func = time.time + +from collections import namedtuple +from operator import attrgetter as dottedgetter + + +if py3k: + def reraise(tp, value, tb=None, cause=None): + if cause is not None: + assert cause is not value, "Same cause emitted" + value.__cause__ = cause + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + # not as nice as that of Py3K, but at least preserves + # the code line where the issue occurred + exec("def reraise(tp, value, tb=None, cause=None):\n" + " if cause is not None:\n" + " assert cause is not value, 'Same cause emitted'\n" + " raise tp, value, tb\n") + + +def raise_from_cause(exception, exc_info=None): + if exc_info is None: + exc_info = sys.exc_info() + exc_type, exc_value, exc_tb = exc_info + cause = exc_value if exc_value is not exception else None + reraise(type(exception), exception, tb=exc_tb, cause=cause) + +if py3k: + exec_ = getattr(builtins, 'exec') +else: + def exec_(func_text, globals_, lcl=None): + if lcl is None: + exec('exec func_text in globals_') + else: + exec('exec func_text in globals_, lcl') + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass. + + Drops the middle class upon creation. + + Source: http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/ + + """ + + class metaclass(meta): + __call__ = type.__call__ + __init__ = type.__init__ + + def __new__(cls, name, this_bases, d): + if this_bases is None: + return type.__new__(cls, name, (), d) + return meta(name, bases, d) + return metaclass('temporary_class', None, {}) + + +@contextmanager +def nested(*managers): + """Implement contextlib.nested, mostly for unit tests. + + As tests still need to run on py2.6 we can't use multiple-with yet. + + Function is removed in py3k but also emits deprecation warning in 2.7 + so just roll it here for everyone. + + """ + + exits = [] + vars = [] + exc = (None, None, None) + try: + for mgr in managers: + exit = mgr.__exit__ + enter = mgr.__enter__ + vars.append(enter()) + exits.append(exit) + yield vars + except: + exc = sys.exc_info() + finally: + while exits: + exit = exits.pop() + try: + if exit(*exc): + exc = (None, None, None) + except: + exc = sys.exc_info() + if exc != (None, None, None): + reraise(exc[0], exc[1], exc[2]) + + +# Fix deprecation of accessing ABCs straight from collections module +# (which will stop working in 3.8). +if py33: + import collections.abc as collections_abc +else: + import collections as collections_abc + diff --git a/venv/Lib/site-packages/sqlalchemy/util/deprecations.py b/venv/Lib/site-packages/sqlalchemy/util/deprecations.py new file mode 100644 index 0000000..e3eebfc --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/util/deprecations.py @@ -0,0 +1,146 @@ +# util/deprecations.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Helpers related to deprecation of functions, methods, classes, other +functionality.""" + +from .. import exc +import warnings +import re +from .langhelpers import decorator + + +def warn_deprecated(msg, stacklevel=3): + warnings.warn(msg, exc.SADeprecationWarning, stacklevel=stacklevel) + + +def warn_pending_deprecation(msg, stacklevel=3): + warnings.warn(msg, exc.SAPendingDeprecationWarning, stacklevel=stacklevel) + + +def deprecated(version, message=None, add_deprecation_to_docstring=True): + """Decorates a function and issues a deprecation warning on use. + + :param message: + If provided, issue message in the warning. A sensible default + is used if not provided. + + :param add_deprecation_to_docstring: + Default True. If False, the wrapped function's __doc__ is left + as-is. If True, the 'message' is prepended to the docs if + provided, or sensible default if message is omitted. + + """ + + if add_deprecation_to_docstring: + header = ".. deprecated:: %s %s" % \ + (version, (message or '')) + else: + header = None + + if message is None: + message = "Call to deprecated function %(func)s" + + def decorate(fn): + return _decorate_with_warning( + fn, exc.SADeprecationWarning, + message % dict(func=fn.__name__), header) + return decorate + + +def pending_deprecation(version, message=None, + add_deprecation_to_docstring=True): + """Decorates a function and issues a pending deprecation warning on use. + + :param version: + An approximate future version at which point the pending deprecation + will become deprecated. Not used in messaging. + + :param message: + If provided, issue message in the warning. A sensible default + is used if not provided. + + :param add_deprecation_to_docstring: + Default True. If False, the wrapped function's __doc__ is left + as-is. If True, the 'message' is prepended to the docs if + provided, or sensible default if message is omitted. + """ + + if add_deprecation_to_docstring: + header = ".. deprecated:: %s (pending) %s" % \ + (version, (message or '')) + else: + header = None + + if message is None: + message = "Call to deprecated function %(func)s" + + def decorate(fn): + return _decorate_with_warning( + fn, exc.SAPendingDeprecationWarning, + message % dict(func=fn.__name__), header) + return decorate + + +def _sanitize_restructured_text(text): + def repl(m): + type_, name = m.group(1, 2) + if type_ in ("func", "meth"): + name += "()" + return name + return re.sub(r'\:(\w+)\:`~?\.?(.+?)`', repl, text) + + +def _decorate_with_warning(func, wtype, message, docstring_header=None): + """Wrap a function with a warnings.warn and augmented docstring.""" + + message = _sanitize_restructured_text(message) + + @decorator + def warned(fn, *args, **kwargs): + warnings.warn(message, wtype, stacklevel=3) + return fn(*args, **kwargs) + + doc = func.__doc__ is not None and func.__doc__ or '' + if docstring_header is not None: + docstring_header %= dict(func=func.__name__) + + doc = inject_docstring_text(doc, docstring_header, 1) + + decorated = warned(func) + decorated.__doc__ = doc + return decorated + +import textwrap + + +def _dedent_docstring(text): + split_text = text.split("\n", 1) + if len(split_text) == 1: + return text + else: + firstline, remaining = split_text + if not firstline.startswith(" "): + return firstline + "\n" + textwrap.dedent(remaining) + else: + return textwrap.dedent(text) + + +def inject_docstring_text(doctext, injecttext, pos): + doctext = _dedent_docstring(doctext or "") + lines = doctext.split('\n') + injectlines = textwrap.dedent(injecttext).split("\n") + if injectlines[0]: + injectlines.insert(0, "") + + blanks = [num for num, line in enumerate(lines) if not line.strip()] + blanks.insert(0, 0) + + inject_pos = blanks[min(pos, len(blanks) - 1)] + + lines = lines[0:inject_pos] + injectlines + lines[inject_pos:] + return "\n".join(lines) diff --git a/venv/Lib/site-packages/sqlalchemy/util/langhelpers.py b/venv/Lib/site-packages/sqlalchemy/util/langhelpers.py new file mode 100644 index 0000000..c2ebf76 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/util/langhelpers.py @@ -0,0 +1,1422 @@ +# util/langhelpers.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Routines to help with the creation, loading and introspection of +modules, classes, hierarchies, attributes, functions, and methods. + +""" +import inspect +import itertools +import operator +import re +import sys +import types +import warnings +from functools import update_wrapper +from .. import exc +import hashlib +from . import compat +from . import _collections + + +def md5_hex(x): + if compat.py3k: + x = x.encode('utf-8') + m = hashlib.md5() + m.update(x) + return m.hexdigest() + + +class safe_reraise(object): + """Reraise an exception after invoking some + handler code. + + Stores the existing exception info before + invoking so that it is maintained across a potential + coroutine context switch. + + e.g.:: + + try: + sess.commit() + except: + with safe_reraise(): + sess.rollback() + + """ + + __slots__ = ('warn_only', '_exc_info') + + def __init__(self, warn_only=False): + self.warn_only = warn_only + + def __enter__(self): + self._exc_info = sys.exc_info() + + def __exit__(self, type_, value, traceback): + # see #2703 for notes + if type_ is None: + exc_type, exc_value, exc_tb = self._exc_info + self._exc_info = None # remove potential circular references + if not self.warn_only: + compat.reraise(exc_type, exc_value, exc_tb) + else: + if not compat.py3k and self._exc_info and self._exc_info[1]: + # emulate Py3K's behavior of telling us when an exception + # occurs in an exception handler. + warn( + "An exception has occurred during handling of a " + "previous exception. The previous exception " + "is:\n %s %s\n" % (self._exc_info[0], self._exc_info[1])) + self._exc_info = None # remove potential circular references + compat.reraise(type_, value, traceback) + + +def decode_slice(slc): + """decode a slice object as sent to __getitem__. + + takes into account the 2.5 __index__() method, basically. + + """ + ret = [] + for x in slc.start, slc.stop, slc.step: + if hasattr(x, '__index__'): + x = x.__index__() + ret.append(x) + return tuple(ret) + + +def _unique_symbols(used, *bases): + used = set(used) + for base in bases: + pool = itertools.chain((base,), + compat.itertools_imap(lambda i: base + str(i), + range(1000))) + for sym in pool: + if sym not in used: + used.add(sym) + yield sym + break + else: + raise NameError("exhausted namespace for symbol base %s" % base) + + +def map_bits(fn, n): + """Call the given function given each nonzero bit from n.""" + + while n: + b = n & (~n + 1) + yield fn(b) + n ^= b + + +def decorator(target): + """A signature-matching decorator factory.""" + + def decorate(fn): + if not inspect.isfunction(fn): + raise Exception("not a decoratable function") + spec = compat.inspect_getfullargspec(fn) + names = tuple(spec[0]) + spec[1:3] + (fn.__name__,) + targ_name, fn_name = _unique_symbols(names, 'target', 'fn') + + metadata = dict(target=targ_name, fn=fn_name) + metadata.update(format_argspec_plus(spec, grouped=False)) + metadata['name'] = fn.__name__ + code = """\ +def %(name)s(%(args)s): + return %(target)s(%(fn)s, %(apply_kw)s) +""" % metadata + decorated = _exec_code_in_env(code, + {targ_name: target, fn_name: fn}, + fn.__name__) + decorated.__defaults__ = getattr(fn, 'im_func', fn).__defaults__ + decorated.__wrapped__ = fn + return update_wrapper(decorated, fn) + return update_wrapper(decorate, target) + + +def _exec_code_in_env(code, env, fn_name): + exec(code, env) + return env[fn_name] + + +def public_factory(target, location): + """Produce a wrapping function for the given cls or classmethod. + + Rationale here is so that the __init__ method of the + class can serve as documentation for the function. + + """ + if isinstance(target, type): + fn = target.__init__ + callable_ = target + doc = "Construct a new :class:`.%s` object. \n\n"\ + "This constructor is mirrored as a public API function; "\ + "see :func:`~%s` "\ + "for a full usage and argument description." % ( + target.__name__, location, ) + else: + fn = callable_ = target + doc = "This function is mirrored; see :func:`~%s` "\ + "for a description of arguments." % location + + location_name = location.split(".")[-1] + spec = compat.inspect_getfullargspec(fn) + del spec[0][0] + metadata = format_argspec_plus(spec, grouped=False) + metadata['name'] = location_name + code = """\ +def %(name)s(%(args)s): + return cls(%(apply_kw)s) +""" % metadata + env = {'cls': callable_, 'symbol': symbol} + exec(code, env) + decorated = env[location_name] + decorated.__doc__ = fn.__doc__ + decorated.__module__ = "sqlalchemy" + location.rsplit(".", 1)[0] + if compat.py2k or hasattr(fn, '__func__'): + fn.__func__.__doc__ = doc + else: + fn.__doc__ = doc + return decorated + + +class PluginLoader(object): + + def __init__(self, group, auto_fn=None): + self.group = group + self.impls = {} + self.auto_fn = auto_fn + + def clear(self): + self.impls.clear() + + def load(self, name): + if name in self.impls: + return self.impls[name]() + + if self.auto_fn: + loader = self.auto_fn(name) + if loader: + self.impls[name] = loader + return loader() + + try: + import pkg_resources + except ImportError: + pass + else: + for impl in pkg_resources.iter_entry_points( + self.group, name): + self.impls[name] = impl.load + return impl.load() + + raise exc.NoSuchModuleError( + "Can't load plugin: %s:%s" % + (self.group, name)) + + def register(self, name, modulepath, objname): + def load(): + mod = compat.import_(modulepath) + for token in modulepath.split(".")[1:]: + mod = getattr(mod, token) + return getattr(mod, objname) + self.impls[name] = load + + +def get_cls_kwargs(cls, _set=None): + r"""Return the full set of inherited kwargs for the given `cls`. + + Probes a class's __init__ method, collecting all named arguments. If the + __init__ defines a \**kwargs catch-all, then the constructor is presumed + to pass along unrecognized keywords to its base classes, and the + collection process is repeated recursively on each of the bases. + + Uses a subset of inspect.getargspec() to cut down on method overhead. + No anonymous tuple arguments please ! + + """ + toplevel = _set is None + if toplevel: + _set = set() + + ctr = cls.__dict__.get('__init__', False) + + has_init = ctr and isinstance(ctr, types.FunctionType) and \ + isinstance(ctr.__code__, types.CodeType) + + if has_init: + names, has_kw = inspect_func_args(ctr) + _set.update(names) + + if not has_kw and not toplevel: + return None + + if not has_init or has_kw: + for c in cls.__bases__: + if get_cls_kwargs(c, _set) is None: + break + + _set.discard('self') + return _set + + +try: + # TODO: who doesn't have this constant? + from inspect import CO_VARKEYWORDS + + def inspect_func_args(fn): + co = fn.__code__ + nargs = co.co_argcount + names = co.co_varnames + args = list(names[:nargs]) + has_kw = bool(co.co_flags & CO_VARKEYWORDS) + return args, has_kw + +except ImportError: + def inspect_func_args(fn): + names, _, has_kw, _ = compat.inspect_getargspec(fn) + return names, bool(has_kw) + + +def get_func_kwargs(func): + """Return the set of legal kwargs for the given `func`. + + Uses getargspec so is safe to call for methods, functions, + etc. + + """ + + return compat.inspect_getargspec(func)[0] + + +def get_callable_argspec(fn, no_self=False, _is_init=False): + """Return the argument signature for any callable. + + All pure-Python callables are accepted, including + functions, methods, classes, objects with __call__; + builtins and other edge cases like functools.partial() objects + raise a TypeError. + + """ + if inspect.isbuiltin(fn): + raise TypeError("Can't inspect builtin: %s" % fn) + elif inspect.isfunction(fn): + if _is_init and no_self: + spec = compat.inspect_getargspec(fn) + return compat.ArgSpec(spec.args[1:], spec.varargs, + spec.keywords, spec.defaults) + else: + return compat.inspect_getargspec(fn) + elif inspect.ismethod(fn): + if no_self and (_is_init or fn.__self__): + spec = compat.inspect_getargspec(fn.__func__) + return compat.ArgSpec(spec.args[1:], spec.varargs, + spec.keywords, spec.defaults) + else: + return compat.inspect_getargspec(fn.__func__) + elif inspect.isclass(fn): + return get_callable_argspec( + fn.__init__, no_self=no_self, _is_init=True) + elif hasattr(fn, '__func__'): + return compat.inspect_getargspec(fn.__func__) + elif hasattr(fn, '__call__'): + if inspect.ismethod(fn.__call__): + return get_callable_argspec(fn.__call__, no_self=no_self) + else: + raise TypeError("Can't inspect callable: %s" % fn) + else: + raise TypeError("Can't inspect callable: %s" % fn) + + +def format_argspec_plus(fn, grouped=True): + """Returns a dictionary of formatted, introspected function arguments. + + A enhanced variant of inspect.formatargspec to support code generation. + + fn + An inspectable callable or tuple of inspect getargspec() results. + grouped + Defaults to True; include (parens, around, argument) lists + + Returns: + + args + Full inspect.formatargspec for fn + self_arg + The name of the first positional argument, varargs[0], or None + if the function defines no positional arguments. + apply_pos + args, re-written in calling rather than receiving syntax. Arguments are + passed positionally. + apply_kw + Like apply_pos, except keyword-ish args are passed as keywords. + + Example:: + + >>> format_argspec_plus(lambda self, a, b, c=3, **d: 123) + {'args': '(self, a, b, c=3, **d)', + 'self_arg': 'self', + 'apply_kw': '(self, a, b, c=c, **d)', + 'apply_pos': '(self, a, b, c, **d)'} + + """ + if compat.callable(fn): + spec = compat.inspect_getfullargspec(fn) + else: + # we accept an existing argspec... + spec = fn + args = compat.inspect_formatargspec(*spec) + if spec[0]: + self_arg = spec[0][0] + elif spec[1]: + self_arg = '%s[0]' % spec[1] + else: + self_arg = None + + if compat.py3k: + apply_pos = compat.inspect_formatargspec( + spec[0], spec[1], spec[2], None, spec[4]) + num_defaults = 0 + if spec[3]: + num_defaults += len(spec[3]) + if spec[4]: + num_defaults += len(spec[4]) + name_args = spec[0] + spec[4] + else: + apply_pos = compat.inspect_formatargspec(spec[0], spec[1], spec[2]) + num_defaults = 0 + if spec[3]: + num_defaults += len(spec[3]) + name_args = spec[0] + + if num_defaults: + defaulted_vals = name_args[0 - num_defaults:] + else: + defaulted_vals = () + + apply_kw = compat.inspect_formatargspec( + name_args, spec[1], spec[2], defaulted_vals, + formatvalue=lambda x: '=' + x) + if grouped: + return dict(args=args, self_arg=self_arg, + apply_pos=apply_pos, apply_kw=apply_kw) + else: + return dict(args=args[1:-1], self_arg=self_arg, + apply_pos=apply_pos[1:-1], apply_kw=apply_kw[1:-1]) + + +def format_argspec_init(method, grouped=True): + """format_argspec_plus with considerations for typical __init__ methods + + Wraps format_argspec_plus with error handling strategies for typical + __init__ cases:: + + object.__init__ -> (self) + other unreflectable (usually C) -> (self, *args, **kwargs) + + """ + if method is object.__init__: + args = grouped and '(self)' or 'self' + else: + try: + return format_argspec_plus(method, grouped=grouped) + except TypeError: + args = (grouped and '(self, *args, **kwargs)' + or 'self, *args, **kwargs') + return dict(self_arg='self', args=args, apply_pos=args, apply_kw=args) + + +def getargspec_init(method): + """inspect.getargspec with considerations for typical __init__ methods + + Wraps inspect.getargspec with error handling for typical __init__ cases:: + + object.__init__ -> (self) + other unreflectable (usually C) -> (self, *args, **kwargs) + + """ + try: + return compat.inspect_getargspec(method) + except TypeError: + if method is object.__init__: + return (['self'], None, None, None) + else: + return (['self'], 'args', 'kwargs', None) + + +def unbound_method_to_callable(func_or_cls): + """Adjust the incoming callable such that a 'self' argument is not + required. + + """ + + if isinstance(func_or_cls, types.MethodType) and not func_or_cls.__self__: + return func_or_cls.__func__ + else: + return func_or_cls + + +def generic_repr(obj, additional_kw=(), to_inspect=None, omit_kwarg=()): + """Produce a __repr__() based on direct association of the __init__() + specification vs. same-named attributes present. + + """ + if to_inspect is None: + to_inspect = [obj] + else: + to_inspect = _collections.to_list(to_inspect) + + missing = object() + + pos_args = [] + kw_args = _collections.OrderedDict() + vargs = None + for i, insp in enumerate(to_inspect): + try: + (_args, _vargs, vkw, defaults) = \ + compat.inspect_getargspec(insp.__init__) + except TypeError: + continue + else: + default_len = defaults and len(defaults) or 0 + if i == 0: + if _vargs: + vargs = _vargs + if default_len: + pos_args.extend(_args[1:-default_len]) + else: + pos_args.extend(_args[1:]) + else: + kw_args.update([ + (arg, missing) for arg in _args[1:-default_len] + ]) + + if default_len: + kw_args.update([ + (arg, default) + for arg, default + in zip(_args[-default_len:], defaults) + ]) + output = [] + + output.extend(repr(getattr(obj, arg, None)) for arg in pos_args) + + if vargs is not None and hasattr(obj, vargs): + output.extend([repr(val) for val in getattr(obj, vargs)]) + + for arg, defval in kw_args.items(): + if arg in omit_kwarg: + continue + try: + val = getattr(obj, arg, missing) + if val is not missing and val != defval: + output.append('%s=%r' % (arg, val)) + except Exception: + pass + + if additional_kw: + for arg, defval in additional_kw: + try: + val = getattr(obj, arg, missing) + if val is not missing and val != defval: + output.append('%s=%r' % (arg, val)) + except Exception: + pass + + return "%s(%s)" % (obj.__class__.__name__, ", ".join(output)) + + +class portable_instancemethod(object): + """Turn an instancemethod into a (parent, name) pair + to produce a serializable callable. + + """ + + __slots__ = 'target', 'name', 'kwargs', '__weakref__' + + def __getstate__(self): + return {'target': self.target, 'name': self.name, + 'kwargs': self.kwargs} + + def __setstate__(self, state): + self.target = state['target'] + self.name = state['name'] + self.kwargs = state.get('kwargs', ()) + + def __init__(self, meth, kwargs=()): + self.target = meth.__self__ + self.name = meth.__name__ + self.kwargs = kwargs + + def __call__(self, *arg, **kw): + kw.update(self.kwargs) + return getattr(self.target, self.name)(*arg, **kw) + + +def class_hierarchy(cls): + """Return an unordered sequence of all classes related to cls. + + Traverses diamond hierarchies. + + Fibs slightly: subclasses of builtin types are not returned. Thus + class_hierarchy(class A(object)) returns (A, object), not A plus every + class systemwide that derives from object. + + Old-style classes are discarded and hierarchies rooted on them + will not be descended. + + """ + if compat.py2k: + if isinstance(cls, types.ClassType): + return list() + + hier = {cls} + process = list(cls.__mro__) + while process: + c = process.pop() + if compat.py2k: + if isinstance(c, types.ClassType): + continue + bases = (_ for _ in c.__bases__ + if _ not in hier and not isinstance(_, types.ClassType)) + else: + bases = (_ for _ in c.__bases__ if _ not in hier) + + for b in bases: + process.append(b) + hier.add(b) + + if compat.py3k: + if c.__module__ == 'builtins' or not hasattr(c, '__subclasses__'): + continue + else: + if c.__module__ == '__builtin__' or not hasattr( + c, '__subclasses__'): + continue + + for s in [_ for _ in c.__subclasses__() if _ not in hier]: + process.append(s) + hier.add(s) + return list(hier) + + +def iterate_attributes(cls): + """iterate all the keys and attributes associated + with a class, without using getattr(). + + Does not use getattr() so that class-sensitive + descriptors (i.e. property.__get__()) are not called. + + """ + keys = dir(cls) + for key in keys: + for c in cls.__mro__: + if key in c.__dict__: + yield (key, c.__dict__[key]) + break + + +def monkeypatch_proxied_specials(into_cls, from_cls, skip=None, only=None, + name='self.proxy', from_instance=None): + """Automates delegation of __specials__ for a proxying type.""" + + if only: + dunders = only + else: + if skip is None: + skip = ('__slots__', '__del__', '__getattribute__', + '__metaclass__', '__getstate__', '__setstate__') + dunders = [m for m in dir(from_cls) + if (m.startswith('__') and m.endswith('__') and + not hasattr(into_cls, m) and m not in skip)] + + for method in dunders: + try: + fn = getattr(from_cls, method) + if not hasattr(fn, '__call__'): + continue + fn = getattr(fn, 'im_func', fn) + except AttributeError: + continue + try: + spec = compat.inspect_getargspec(fn) + fn_args = compat.inspect_formatargspec(spec[0]) + d_args = compat.inspect_formatargspec(spec[0][1:]) + except TypeError: + fn_args = '(self, *args, **kw)' + d_args = '(*args, **kw)' + + py = ("def %(method)s%(fn_args)s: " + "return %(name)s.%(method)s%(d_args)s" % locals()) + + env = from_instance is not None and {name: from_instance} or {} + compat.exec_(py, env) + try: + env[method].__defaults__ = fn.__defaults__ + except AttributeError: + pass + setattr(into_cls, method, env[method]) + + +def methods_equivalent(meth1, meth2): + """Return True if the two methods are the same implementation.""" + + return getattr(meth1, '__func__', meth1) is getattr( + meth2, '__func__', meth2) + + +def as_interface(obj, cls=None, methods=None, required=None): + """Ensure basic interface compliance for an instance or dict of callables. + + Checks that ``obj`` implements public methods of ``cls`` or has members + listed in ``methods``. If ``required`` is not supplied, implementing at + least one interface method is sufficient. Methods present on ``obj`` that + are not in the interface are ignored. + + If ``obj`` is a dict and ``dict`` does not meet the interface + requirements, the keys of the dictionary are inspected. Keys present in + ``obj`` that are not in the interface will raise TypeErrors. + + Raises TypeError if ``obj`` does not meet the interface criteria. + + In all passing cases, an object with callable members is returned. In the + simple case, ``obj`` is returned as-is; if dict processing kicks in then + an anonymous class is returned. + + obj + A type, instance, or dictionary of callables. + cls + Optional, a type. All public methods of cls are considered the + interface. An ``obj`` instance of cls will always pass, ignoring + ``required``.. + methods + Optional, a sequence of method names to consider as the interface. + required + Optional, a sequence of mandatory implementations. If omitted, an + ``obj`` that provides at least one interface method is considered + sufficient. As a convenience, required may be a type, in which case + all public methods of the type are required. + + """ + if not cls and not methods: + raise TypeError('a class or collection of method names are required') + + if isinstance(cls, type) and isinstance(obj, cls): + return obj + + interface = set(methods or [m for m in dir(cls) if not m.startswith('_')]) + implemented = set(dir(obj)) + + complies = operator.ge + if isinstance(required, type): + required = interface + elif not required: + required = set() + complies = operator.gt + else: + required = set(required) + + if complies(implemented.intersection(interface), required): + return obj + + # No dict duck typing here. + if not isinstance(obj, dict): + qualifier = complies is operator.gt and 'any of' or 'all of' + raise TypeError("%r does not implement %s: %s" % ( + obj, qualifier, ', '.join(interface))) + + class AnonymousInterface(object): + """A callable-holding shell.""" + + if cls: + AnonymousInterface.__name__ = 'Anonymous' + cls.__name__ + found = set() + + for method, impl in dictlike_iteritems(obj): + if method not in interface: + raise TypeError("%r: unknown in this interface" % method) + if not compat.callable(impl): + raise TypeError("%r=%r is not callable" % (method, impl)) + setattr(AnonymousInterface, method, staticmethod(impl)) + found.add(method) + + if complies(found, required): + return AnonymousInterface + + raise TypeError("dictionary does not contain required keys %s" % + ', '.join(required - found)) + + +class memoized_property(object): + """A read-only @property that is only evaluated once.""" + + def __init__(self, fget, doc=None): + self.fget = fget + self.__doc__ = doc or fget.__doc__ + self.__name__ = fget.__name__ + + def __get__(self, obj, cls): + if obj is None: + return self + obj.__dict__[self.__name__] = result = self.fget(obj) + return result + + def _reset(self, obj): + memoized_property.reset(obj, self.__name__) + + @classmethod + def reset(cls, obj, name): + obj.__dict__.pop(name, None) + + +def memoized_instancemethod(fn): + """Decorate a method memoize its return value. + + Best applied to no-arg methods: memoization is not sensitive to + argument values, and will always return the same value even when + called with different arguments. + + """ + + def oneshot(self, *args, **kw): + result = fn(self, *args, **kw) + memo = lambda *a, **kw: result + memo.__name__ = fn.__name__ + memo.__doc__ = fn.__doc__ + self.__dict__[fn.__name__] = memo + return result + return update_wrapper(oneshot, fn) + + +class group_expirable_memoized_property(object): + """A family of @memoized_properties that can be expired in tandem.""" + + def __init__(self, attributes=()): + self.attributes = [] + if attributes: + self.attributes.extend(attributes) + + def expire_instance(self, instance): + """Expire all memoized properties for *instance*.""" + stash = instance.__dict__ + for attribute in self.attributes: + stash.pop(attribute, None) + + def __call__(self, fn): + self.attributes.append(fn.__name__) + return memoized_property(fn) + + def method(self, fn): + self.attributes.append(fn.__name__) + return memoized_instancemethod(fn) + + +class MemoizedSlots(object): + """Apply memoized items to an object using a __getattr__ scheme. + + This allows the functionality of memoized_property and + memoized_instancemethod to be available to a class using __slots__. + + """ + + __slots__ = () + + def _fallback_getattr(self, key): + raise AttributeError(key) + + def __getattr__(self, key): + if key.startswith('_memoized'): + raise AttributeError(key) + elif hasattr(self, '_memoized_attr_%s' % key): + value = getattr(self, '_memoized_attr_%s' % key)() + setattr(self, key, value) + return value + elif hasattr(self, '_memoized_method_%s' % key): + fn = getattr(self, '_memoized_method_%s' % key) + + def oneshot(*args, **kw): + result = fn(*args, **kw) + memo = lambda *a, **kw: result + memo.__name__ = fn.__name__ + memo.__doc__ = fn.__doc__ + setattr(self, key, memo) + return result + oneshot.__doc__ = fn.__doc__ + return oneshot + else: + return self._fallback_getattr(key) + + +def dependency_for(modulename): + def decorate(obj): + # TODO: would be nice to improve on this import silliness, + # unfortunately importlib doesn't work that great either + tokens = modulename.split(".") + mod = compat.import_( + ".".join(tokens[0:-1]), globals(), locals(), [tokens[-1]]) + mod = getattr(mod, tokens[-1]) + setattr(mod, obj.__name__, obj) + return obj + return decorate + + +class dependencies(object): + """Apply imported dependencies as arguments to a function. + + E.g.:: + + @util.dependencies( + "sqlalchemy.sql.widget", + "sqlalchemy.engine.default" + ); + def some_func(self, widget, default, arg1, arg2, **kw): + # ... + + Rationale is so that the impact of a dependency cycle can be + associated directly with the few functions that cause the cycle, + and not pollute the module-level namespace. + + """ + + def __init__(self, *deps): + self.import_deps = [] + for dep in deps: + tokens = dep.split(".") + self.import_deps.append( + dependencies._importlater( + ".".join(tokens[0:-1]), + tokens[-1] + ) + ) + + def __call__(self, fn): + import_deps = self.import_deps + spec = compat.inspect_getfullargspec(fn) + + spec_zero = list(spec[0]) + hasself = spec_zero[0] in ('self', 'cls') + + for i in range(len(import_deps)): + spec[0][i + (1 if hasself else 0)] = "import_deps[%r]" % i + + inner_spec = format_argspec_plus(spec, grouped=False) + + for impname in import_deps: + del spec_zero[1 if hasself else 0] + spec[0][:] = spec_zero + + outer_spec = format_argspec_plus(spec, grouped=False) + + code = 'lambda %(args)s: fn(%(apply_kw)s)' % { + "args": outer_spec['args'], + "apply_kw": inner_spec['apply_kw'] + } + + decorated = eval(code, locals()) + decorated.__defaults__ = getattr(fn, 'im_func', fn).__defaults__ + return update_wrapper(decorated, fn) + + @classmethod + def resolve_all(cls, path): + for m in list(dependencies._unresolved): + if m._full_path.startswith(path): + m._resolve() + + _unresolved = set() + _by_key = {} + + class _importlater(object): + _unresolved = set() + + _by_key = {} + + def __new__(cls, path, addtl): + key = path + "." + addtl + if key in dependencies._by_key: + return dependencies._by_key[key] + else: + dependencies._by_key[key] = imp = object.__new__(cls) + return imp + + def __init__(self, path, addtl): + self._il_path = path + self._il_addtl = addtl + dependencies._unresolved.add(self) + + @property + def _full_path(self): + return self._il_path + "." + self._il_addtl + + @memoized_property + def module(self): + if self in dependencies._unresolved: + raise ImportError( + "importlater.resolve_all() hasn't " + "been called (this is %s %s)" + % (self._il_path, self._il_addtl)) + + return getattr(self._initial_import, self._il_addtl) + + def _resolve(self): + dependencies._unresolved.discard(self) + self._initial_import = compat.import_( + self._il_path, globals(), locals(), + [self._il_addtl]) + + def __getattr__(self, key): + if key == 'module': + raise ImportError("Could not resolve module %s" + % self._full_path) + try: + attr = getattr(self.module, key) + except AttributeError: + raise AttributeError( + "Module %s has no attribute '%s'" % + (self._full_path, key) + ) + self.__dict__[key] = attr + return attr + + +# from paste.deploy.converters +def asbool(obj): + if isinstance(obj, compat.string_types): + obj = obj.strip().lower() + if obj in ['true', 'yes', 'on', 'y', 't', '1']: + return True + elif obj in ['false', 'no', 'off', 'n', 'f', '0']: + return False + else: + raise ValueError("String is not true/false: %r" % obj) + return bool(obj) + + +def bool_or_str(*text): + """Return a callable that will evaluate a string as + boolean, or one of a set of "alternate" string values. + + """ + def bool_or_value(obj): + if obj in text: + return obj + else: + return asbool(obj) + return bool_or_value + + +def asint(value): + """Coerce to integer.""" + + if value is None: + return value + return int(value) + + +def coerce_kw_type(kw, key, type_, flexi_bool=True): + r"""If 'key' is present in dict 'kw', coerce its value to type 'type\_' if + necessary. If 'flexi_bool' is True, the string '0' is considered false + when coercing to boolean. + """ + + if key in kw and not isinstance(kw[key], type_) and kw[key] is not None: + if type_ is bool and flexi_bool: + kw[key] = asbool(kw[key]) + else: + kw[key] = type_(kw[key]) + + +def constructor_copy(obj, cls, *args, **kw): + """Instantiate cls using the __dict__ of obj as constructor arguments. + + Uses inspect to match the named arguments of ``cls``. + + """ + + names = get_cls_kwargs(cls) + kw.update( + (k, obj.__dict__[k]) for k in names.difference(kw) + if k in obj.__dict__) + return cls(*args, **kw) + + +def counter(): + """Return a threadsafe counter function.""" + + lock = compat.threading.Lock() + counter = itertools.count(1) + + # avoid the 2to3 "next" transformation... + def _next(): + lock.acquire() + try: + return next(counter) + finally: + lock.release() + + return _next + + +def duck_type_collection(specimen, default=None): + """Given an instance or class, guess if it is or is acting as one of + the basic collection types: list, set and dict. If the __emulates__ + property is present, return that preferentially. + """ + + if hasattr(specimen, '__emulates__'): + # canonicalize set vs sets.Set to a standard: the builtin set + if (specimen.__emulates__ is not None and + issubclass(specimen.__emulates__, set)): + return set + else: + return specimen.__emulates__ + + isa = isinstance(specimen, type) and issubclass or isinstance + if isa(specimen, list): + return list + elif isa(specimen, set): + return set + elif isa(specimen, dict): + return dict + + if hasattr(specimen, 'append'): + return list + elif hasattr(specimen, 'add'): + return set + elif hasattr(specimen, 'set'): + return dict + else: + return default + + +def assert_arg_type(arg, argtype, name): + if isinstance(arg, argtype): + return arg + else: + if isinstance(argtype, tuple): + raise exc.ArgumentError( + "Argument '%s' is expected to be one of type %s, got '%s'" % + (name, ' or '.join("'%s'" % a for a in argtype), type(arg))) + else: + raise exc.ArgumentError( + "Argument '%s' is expected to be of type '%s', got '%s'" % + (name, argtype, type(arg))) + + +def dictlike_iteritems(dictlike): + """Return a (key, value) iterator for almost any dict-like object.""" + + if compat.py3k: + if hasattr(dictlike, 'items'): + return list(dictlike.items()) + else: + if hasattr(dictlike, 'iteritems'): + return dictlike.iteritems() + elif hasattr(dictlike, 'items'): + return iter(dictlike.items()) + + getter = getattr(dictlike, '__getitem__', getattr(dictlike, 'get', None)) + if getter is None: + raise TypeError( + "Object '%r' is not dict-like" % dictlike) + + if hasattr(dictlike, 'iterkeys'): + def iterator(): + for key in dictlike.iterkeys(): + yield key, getter(key) + return iterator() + elif hasattr(dictlike, 'keys'): + return iter((key, getter(key)) for key in dictlike.keys()) + else: + raise TypeError( + "Object '%r' is not dict-like" % dictlike) + + +class classproperty(property): + """A decorator that behaves like @property except that operates + on classes rather than instances. + + The decorator is currently special when using the declarative + module, but note that the + :class:`~.sqlalchemy.ext.declarative.declared_attr` + decorator should be used for this purpose with declarative. + + """ + + def __init__(self, fget, *arg, **kw): + super(classproperty, self).__init__(fget, *arg, **kw) + self.__doc__ = fget.__doc__ + + def __get__(desc, self, cls): + return desc.fget(cls) + + +class hybridproperty(object): + def __init__(self, func): + self.func = func + + def __get__(self, instance, owner): + if instance is None: + clsval = self.func(owner) + clsval.__doc__ = self.func.__doc__ + return clsval + else: + return self.func(instance) + + +class hybridmethod(object): + """Decorate a function as cls- or instance- level.""" + + def __init__(self, func): + self.func = func + + def __get__(self, instance, owner): + if instance is None: + return self.func.__get__(owner, owner.__class__) + else: + return self.func.__get__(instance, owner) + + +class _symbol(int): + def __new__(self, name, doc=None, canonical=None): + """Construct a new named symbol.""" + assert isinstance(name, compat.string_types) + if canonical is None: + canonical = hash(name) + v = int.__new__(_symbol, canonical) + v.name = name + if doc: + v.__doc__ = doc + return v + + def __reduce__(self): + return symbol, (self.name, "x", int(self)) + + def __str__(self): + return repr(self) + + def __repr__(self): + return "symbol(%r)" % self.name + +_symbol.__name__ = 'symbol' + + +class symbol(object): + """A constant symbol. + + >>> symbol('foo') is symbol('foo') + True + >>> symbol('foo') + <symbol 'foo> + + A slight refinement of the MAGICCOOKIE=object() pattern. The primary + advantage of symbol() is its repr(). They are also singletons. + + Repeated calls of symbol('name') will all return the same instance. + + The optional ``doc`` argument assigns to ``__doc__``. This + is strictly so that Sphinx autoattr picks up the docstring we want + (it doesn't appear to pick up the in-module docstring if the datamember + is in a different module - autoattribute also blows up completely). + If Sphinx fixes/improves this then we would no longer need + ``doc`` here. + + """ + symbols = {} + _lock = compat.threading.Lock() + + def __new__(cls, name, doc=None, canonical=None): + cls._lock.acquire() + try: + sym = cls.symbols.get(name) + if sym is None: + cls.symbols[name] = sym = _symbol(name, doc, canonical) + return sym + finally: + symbol._lock.release() + + +_creation_order = 1 + + +def set_creation_order(instance): + """Assign a '_creation_order' sequence to the given instance. + + This allows multiple instances to be sorted in order of creation + (typically within a single thread; the counter is not particularly + threadsafe). + + """ + global _creation_order + instance._creation_order = _creation_order + _creation_order += 1 + + +def warn_exception(func, *args, **kwargs): + """executes the given function, catches all exceptions and converts to + a warning. + + """ + try: + return func(*args, **kwargs) + except Exception: + warn("%s('%s') ignored" % sys.exc_info()[0:2]) + + +def ellipses_string(value, len_=25): + try: + if len(value) > len_: + return "%s..." % value[0:len_] + else: + return value + except TypeError: + return value + + +class _hash_limit_string(compat.text_type): + """A string subclass that can only be hashed on a maximum amount + of unique values. + + This is used for warnings so that we can send out parameterized warnings + without the __warningregistry__ of the module, or the non-overridable + "once" registry within warnings.py, overloading memory, + + + """ + def __new__(cls, value, num, args): + interpolated = (value % args) + \ + (" (this warning may be suppressed after %d occurrences)" % num) + self = super(_hash_limit_string, cls).__new__(cls, interpolated) + self._hash = hash("%s_%d" % (value, hash(interpolated) % num)) + return self + + def __hash__(self): + return self._hash + + def __eq__(self, other): + return hash(self) == hash(other) + + +def warn(msg): + """Issue a warning. + + If msg is a string, :class:`.exc.SAWarning` is used as + the category. + + """ + warnings.warn(msg, exc.SAWarning, stacklevel=2) + + +def warn_limited(msg, args): + """Issue a warning with a paramterized string, limiting the number + of registrations. + + """ + if args: + msg = _hash_limit_string(msg, 10, args) + warnings.warn(msg, exc.SAWarning, stacklevel=2) + + +def only_once(fn): + """Decorate the given function to be a no-op after it is called exactly + once.""" + + once = [fn] + + def go(*arg, **kw): + if once: + once_fn = once.pop() + return once_fn(*arg, **kw) + + return go + + +_SQLA_RE = re.compile(r'sqlalchemy/([a-z_]+/){0,2}[a-z_]+\.py') +_UNITTEST_RE = re.compile(r'unit(?:2|test2?/)') + + +def chop_traceback(tb, exclude_prefix=_UNITTEST_RE, exclude_suffix=_SQLA_RE): + """Chop extraneous lines off beginning and end of a traceback. + + :param tb: + a list of traceback lines as returned by ``traceback.format_stack()`` + + :param exclude_prefix: + a regular expression object matching lines to skip at beginning of + ``tb`` + + :param exclude_suffix: + a regular expression object matching lines to skip at end of ``tb`` + """ + start = 0 + end = len(tb) - 1 + while start <= end and exclude_prefix.search(tb[start]): + start += 1 + while start <= end and exclude_suffix.search(tb[end]): + end -= 1 + return tb[start:end + 1] + +NoneType = type(None) + + +def attrsetter(attrname): + code = \ + "def set(obj, value):"\ + " obj.%s = value" % attrname + env = locals().copy() + exec(code, env) + return env['set'] + + +class EnsureKWArgType(type): + """Apply translation of functions to accept **kw arguments if they + don't already. + + """ + def __init__(cls, clsname, bases, clsdict): + fn_reg = cls.ensure_kwarg + if fn_reg: + for key in clsdict: + m = re.match(fn_reg, key) + if m: + fn = clsdict[key] + spec = compat.inspect_getargspec(fn) + if not spec.keywords: + clsdict[key] = wrapped = cls._wrap_w_kw(fn) + setattr(cls, key, wrapped) + super(EnsureKWArgType, cls).__init__(clsname, bases, clsdict) + + def _wrap_w_kw(self, fn): + + def wrap(*arg, **kw): + return fn(*arg) + return update_wrapper(wrap, fn) + + +def wrap_callable(wrapper, fn): + """Augment functools.update_wrapper() to work with objects with + a ``__call__()`` method. + + :param fn: + object with __call__ method + + """ + if hasattr(fn, '__name__'): + return update_wrapper(wrapper, fn) + else: + _f = wrapper + _f.__name__ = fn.__class__.__name__ + if hasattr(fn, '__module__'): + _f.__module__ = fn.__module__ + + if hasattr(fn.__call__, '__doc__') and fn.__call__.__doc__: + _f.__doc__ = fn.__call__.__doc__ + elif fn.__doc__: + _f.__doc__ = fn.__doc__ + + return _f diff --git a/venv/Lib/site-packages/sqlalchemy/util/queue.py b/venv/Lib/site-packages/sqlalchemy/util/queue.py new file mode 100644 index 0000000..1958702 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/util/queue.py @@ -0,0 +1,199 @@ +# util/queue.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""An adaptation of Py2.3/2.4's Queue module which supports reentrant +behavior, using RLock instead of Lock for its mutex object. The +Queue object is used exclusively by the sqlalchemy.pool.QueuePool +class. + +This is to support the connection pool's usage of weakref callbacks to return +connections to the underlying Queue, which can in extremely +rare cases be invoked within the ``get()`` method of the Queue itself, +producing a ``put()`` inside the ``get()`` and therefore a reentrant +condition. + +""" + +from collections import deque +from time import time as _time +from .compat import threading + + +__all__ = ['Empty', 'Full', 'Queue'] + + +class Empty(Exception): + "Exception raised by Queue.get(block=0)/get_nowait()." + + pass + + +class Full(Exception): + "Exception raised by Queue.put(block=0)/put_nowait()." + + pass + + +class Queue: + def __init__(self, maxsize=0): + """Initialize a queue object with a given maximum size. + + If `maxsize` is <= 0, the queue size is infinite. + """ + + self._init(maxsize) + # mutex must be held whenever the queue is mutating. All methods + # that acquire mutex must release it before returning. mutex + # is shared between the two conditions, so acquiring and + # releasing the conditions also acquires and releases mutex. + self.mutex = threading.RLock() + # Notify not_empty whenever an item is added to the queue; a + # thread waiting to get is notified then. + self.not_empty = threading.Condition(self.mutex) + # Notify not_full whenever an item is removed from the queue; + # a thread waiting to put is notified then. + self.not_full = threading.Condition(self.mutex) + + def qsize(self): + """Return the approximate size of the queue (not reliable!).""" + + self.mutex.acquire() + n = self._qsize() + self.mutex.release() + return n + + def empty(self): + """Return True if the queue is empty, False otherwise (not + reliable!).""" + + self.mutex.acquire() + n = self._empty() + self.mutex.release() + return n + + def full(self): + """Return True if the queue is full, False otherwise (not + reliable!).""" + + self.mutex.acquire() + n = self._full() + self.mutex.release() + return n + + def put(self, item, block=True, timeout=None): + """Put an item into the queue. + + If optional args `block` is True and `timeout` is None (the + default), block if necessary until a free slot is + available. If `timeout` is a positive number, it blocks at + most `timeout` seconds and raises the ``Full`` exception if no + free slot was available within that time. Otherwise (`block` + is false), put an item on the queue if a free slot is + immediately available, else raise the ``Full`` exception + (`timeout` is ignored in that case). + """ + + self.not_full.acquire() + try: + if not block: + if self._full(): + raise Full + elif timeout is None: + while self._full(): + self.not_full.wait() + else: + if timeout < 0: + raise ValueError("'timeout' must be a positive number") + endtime = _time() + timeout + while self._full(): + remaining = endtime - _time() + if remaining <= 0.0: + raise Full + self.not_full.wait(remaining) + self._put(item) + self.not_empty.notify() + finally: + self.not_full.release() + + def put_nowait(self, item): + """Put an item into the queue without blocking. + + Only enqueue the item if a free slot is immediately available. + Otherwise raise the ``Full`` exception. + """ + return self.put(item, False) + + def get(self, block=True, timeout=None): + """Remove and return an item from the queue. + + If optional args `block` is True and `timeout` is None (the + default), block if necessary until an item is available. If + `timeout` is a positive number, it blocks at most `timeout` + seconds and raises the ``Empty`` exception if no item was + available within that time. Otherwise (`block` is false), + return an item if one is immediately available, else raise the + ``Empty`` exception (`timeout` is ignored in that case). + """ + self.not_empty.acquire() + try: + if not block: + if self._empty(): + raise Empty + elif timeout is None: + while self._empty(): + self.not_empty.wait() + else: + if timeout < 0: + raise ValueError("'timeout' must be a positive number") + endtime = _time() + timeout + while self._empty(): + remaining = endtime - _time() + if remaining <= 0.0: + raise Empty + self.not_empty.wait(remaining) + item = self._get() + self.not_full.notify() + return item + finally: + self.not_empty.release() + + def get_nowait(self): + """Remove and return an item from the queue without blocking. + + Only get an item if one is immediately available. Otherwise + raise the ``Empty`` exception. + """ + + return self.get(False) + + # Override these methods to implement other queue organizations + # (e.g. stack or priority queue). + # These will only be called with appropriate locks held + + # Initialize the queue representation + def _init(self, maxsize): + self.maxsize = maxsize + self.queue = deque() + + def _qsize(self): + return len(self.queue) + + # Check whether the queue is empty + def _empty(self): + return not self.queue + + # Check whether the queue is full + def _full(self): + return self.maxsize > 0 and len(self.queue) == self.maxsize + + # Put a new item in the queue + def _put(self, item): + self.queue.append(item) + + # Get an item from the queue + def _get(self): + return self.queue.popleft() diff --git a/venv/Lib/site-packages/sqlalchemy/util/topological.py b/venv/Lib/site-packages/sqlalchemy/util/topological.py new file mode 100644 index 0000000..5f516d6 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/util/topological.py @@ -0,0 +1,100 @@ +# util/topological.py +# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors +# <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +"""Topological sorting algorithms.""" + +from ..exc import CircularDependencyError +from .. import util + +__all__ = ['sort', 'sort_as_subsets', 'find_cycles'] + + +def sort_as_subsets(tuples, allitems, deterministic_order=False): + + edges = util.defaultdict(set) + for parent, child in tuples: + edges[child].add(parent) + + Set = util.OrderedSet if deterministic_order else set + + todo = Set(allitems) + + while todo: + output = Set() + for node in todo: + if todo.isdisjoint(edges[node]): + output.add(node) + + if not output: + raise CircularDependencyError( + "Circular dependency detected.", + find_cycles(tuples, allitems), + _gen_edges(edges) + ) + + todo.difference_update(output) + yield output + + +def sort(tuples, allitems, deterministic_order=False): + """sort the given list of items by dependency. + + 'tuples' is a list of tuples representing a partial ordering. + 'deterministic_order' keeps items within a dependency tier in list order. + """ + + for set_ in sort_as_subsets(tuples, allitems, deterministic_order): + for s in set_: + yield s + + +def find_cycles(tuples, allitems): + # adapted from: + # http://neopythonic.blogspot.com/2009/01/detecting-cycles-in-directed-graph.html + + edges = util.defaultdict(set) + for parent, child in tuples: + edges[parent].add(child) + nodes_to_test = set(edges) + + output = set() + + # we'd like to find all nodes that are + # involved in cycles, so we do the full + # pass through the whole thing for each + # node in the original list. + + # we can go just through parent edge nodes. + # if a node is only a child and never a parent, + # by definition it can't be part of a cycle. same + # if it's not in the edges at all. + for node in nodes_to_test: + stack = [node] + todo = nodes_to_test.difference(stack) + while stack: + top = stack[-1] + for node in edges[top]: + if node in stack: + cyc = stack[stack.index(node):] + todo.difference_update(cyc) + output.update(cyc) + + if node in todo: + stack.append(node) + todo.remove(node) + break + else: + node = stack.pop() + return output + + +def _gen_edges(edges): + return set([ + (right, left) + for left in edges + for right in edges[left] + ]) diff --git a/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/PKG-INFO b/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/PKG-INFO new file mode 100644 index 0000000..e87dec1 --- /dev/null +++ b/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/PKG-INFO @@ -0,0 +1,103 @@ +Metadata-Version: 1.1 +Name: visitor +Version: 0.1.3 +Summary: A tiny pythonic visitor implementation. +Home-page: http://github.com/mbr/visitor +Author: Marc Brinkmann +Author-email: git@marcbrinkmann.de +License: MIT +Description: visitor + ======= + + A tiny library to facilitate `visitor + <https://en.wikipedia.org/wiki/Visitor_pattern>`_ implementation in Python + (which are slightly peculiar due to dynamic typing). In fact, it is so small, + you may just be better off copy & pasting the source straight into your + project... + + + Example use + ----------- + + A simple JSON-encoder: + + .. code-block:: python + + from visitor import Visitor + + + class JSONEncoder(Visitor): + def __init__(self): + self.indent = 0 + + def escape_str(self, s): + # note: this is not a good escape function, do not use this in + # production! + s = s.replace('\\', '\\\\') + s = s.replace('"', '\\"') + return '"' + s + '"' + + def visit_list(self, node): + self.indent += 1 + s = '[\n' + ' ' * self.indent + s += (',\n' + ' ' * self.indent).join(self.visit(item) + for item in node) + self.indent -= 1 + s += '\n' + ' ' * self.indent + ']' + return s + + def visit_str(self, node): + return self.escape_str(node) + + def visit_int(self, node): + return str(node) + + def visit_bool(self, node): + return 'true' if node else 'false' + + def visit_dict(self, node): + self.indent += 1 + s = '{\n' + ' ' * self.indent + s += (',\n' + ' ' * self.indent).join( + '{}: {}'.format(self.escape_str(key), self.visit(value)) + for key, value in sorted(node.items()) + ) + self.indent -= 1 + s += '\n' + ' ' * self.indent + '}' + return s + + + data = [ + 'List', 'of', 42, 'items', True, { + 'sub1': 'some string', + 'sub2': { + 'sub2sub1': False, + 'sub2sub2': 123, + } + } + ] + + print(JSONEncoder().visit(data)) + + + + Output:: + + [ + "List", + "of", + 42, + "items", + true, + { + "sub1": "some string", + "sub2": { + "sub2sub1": false, + "sub2sub2": 123 + } + } + ] + +Platform: UNKNOWN +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 diff --git a/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/SOURCES.txt b/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/SOURCES.txt new file mode 100644 index 0000000..fcb876c --- /dev/null +++ b/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/SOURCES.txt @@ -0,0 +1,10 @@ +LICENSE +MANIFEST.in +README.rst +setup.cfg +setup.py +visitor/__init__.py +visitor.egg-info/PKG-INFO +visitor.egg-info/SOURCES.txt +visitor.egg-info/dependency_links.txt +visitor.egg-info/top_level.txt \ No newline at end of file diff --git a/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/dependency_links.txt b/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/installed-files.txt b/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/installed-files.txt new file mode 100644 index 0000000..4d45006 --- /dev/null +++ b/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/installed-files.txt @@ -0,0 +1,6 @@ +..\visitor\__init__.py +..\visitor\__pycache__\__init__.cpython-36.pyc +dependency_links.txt +PKG-INFO +SOURCES.txt +top_level.txt diff --git a/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/top_level.txt b/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/top_level.txt new file mode 100644 index 0000000..2a587d2 --- /dev/null +++ b/venv/Lib/site-packages/visitor-0.1.3-py3.6.egg-info/top_level.txt @@ -0,0 +1 @@ +visitor diff --git a/venv/Lib/site-packages/visitor/__init__.py b/venv/Lib/site-packages/visitor/__init__.py new file mode 100644 index 0000000..de2fd36 --- /dev/null +++ b/venv/Lib/site-packages/visitor/__init__.py @@ -0,0 +1,51 @@ +# Copyright (c) 2015 Marc Brinkmann + +# 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. + + +class Visitor(object): + """Base class for visitors.""" + + def visit(self, node): + """Visit a node. + + Calls ``visit_CLASSNAME`` on itself passing ``node``, where + ``CLASSNAME`` is the node's class. If the visitor does not implement an + appropriate visitation method, will go up the + `MRO <https://www.python.org/download/releases/2.3/mro/>`_ until a + match is found. + + If the search exhausts all classes of node, raises a + :class:`~exceptions.NotImplementedError`. + + :param node: The node to visit. + :return: The return value of the called visitation function. + """ + if isinstance(node, type): + mro = node.mro() + else: + mro = type(node).mro() + for cls in mro: + meth = getattr(self, 'visit_' + cls.__name__, None) + if meth is None: + continue + return meth(node) + + raise NotImplementedError('No visitation method visit_{}' + .format(node.__class__.__name__)) diff --git a/venv/Lib/site-packages/werkzeug/__init__.py b/venv/Lib/site-packages/werkzeug/__init__.py new file mode 100644 index 0000000..56404f4 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/__init__.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +""" + werkzeug + ~~~~~~~~ + + Werkzeug is the Swiss Army knife of Python web development. + + It provides useful classes and functions for any WSGI application to make + the life of a python web developer much easier. All of the provided + classes are independent from each other so you can mix it with any other + library. + + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +from types import ModuleType +import sys + +from werkzeug._compat import iteritems + +__version__ = '0.14.1' + + +# This import magic raises concerns quite often which is why the implementation +# and motivation is explained here in detail now. +# +# The majority of the functions and classes provided by Werkzeug work on the +# HTTP and WSGI layer. There is no useful grouping for those which is why +# they are all importable from "werkzeug" instead of the modules where they are +# implemented. The downside of that is, that now everything would be loaded at +# once, even if unused. +# +# The implementation of a lazy-loading module in this file replaces the +# werkzeug package when imported from within. Attribute access to the werkzeug +# module will then lazily import from the modules that implement the objects. + + +# import mapping to objects in other modules +all_by_module = { + 'werkzeug.debug': ['DebuggedApplication'], + 'werkzeug.local': ['Local', 'LocalManager', 'LocalProxy', 'LocalStack', + 'release_local'], + 'werkzeug.serving': ['run_simple'], + 'werkzeug.test': ['Client', 'EnvironBuilder', 'create_environ', + 'run_wsgi_app'], + 'werkzeug.testapp': ['test_app'], + 'werkzeug.exceptions': ['abort', 'Aborter'], + 'werkzeug.urls': ['url_decode', 'url_encode', 'url_quote', + 'url_quote_plus', 'url_unquote', 'url_unquote_plus', + 'url_fix', 'Href', 'iri_to_uri', 'uri_to_iri'], + 'werkzeug.formparser': ['parse_form_data'], + 'werkzeug.utils': ['escape', 'environ_property', 'append_slash_redirect', + 'redirect', 'cached_property', 'import_string', + 'dump_cookie', 'parse_cookie', 'unescape', + 'format_string', 'find_modules', 'header_property', + 'html', 'xhtml', 'HTMLBuilder', 'validate_arguments', + 'ArgumentValidationError', 'bind_arguments', + 'secure_filename'], + 'werkzeug.wsgi': ['get_current_url', 'get_host', 'pop_path_info', + 'peek_path_info', 'SharedDataMiddleware', + 'DispatcherMiddleware', 'ClosingIterator', 'FileWrapper', + 'make_line_iter', 'LimitedStream', 'responder', + 'wrap_file', 'extract_path_info'], + 'werkzeug.datastructures': ['MultiDict', 'CombinedMultiDict', 'Headers', + 'EnvironHeaders', 'ImmutableList', + 'ImmutableDict', 'ImmutableMultiDict', + 'TypeConversionDict', + 'ImmutableTypeConversionDict', 'Accept', + 'MIMEAccept', 'CharsetAccept', + 'LanguageAccept', 'RequestCacheControl', + 'ResponseCacheControl', 'ETags', 'HeaderSet', + 'WWWAuthenticate', 'Authorization', + 'FileMultiDict', 'CallbackDict', 'FileStorage', + 'OrderedMultiDict', 'ImmutableOrderedMultiDict' + ], + 'werkzeug.useragents': ['UserAgent'], + 'werkzeug.http': ['parse_etags', 'parse_date', 'http_date', 'cookie_date', + 'parse_cache_control_header', 'is_resource_modified', + 'parse_accept_header', 'parse_set_header', 'quote_etag', + 'unquote_etag', 'generate_etag', 'dump_header', + 'parse_list_header', 'parse_dict_header', + 'parse_authorization_header', + 'parse_www_authenticate_header', 'remove_entity_headers', + 'is_entity_header', 'remove_hop_by_hop_headers', + 'parse_options_header', 'dump_options_header', + 'is_hop_by_hop_header', 'unquote_header_value', + 'quote_header_value', 'HTTP_STATUS_CODES'], + 'werkzeug.wrappers': ['BaseResponse', 'BaseRequest', 'Request', 'Response', + 'AcceptMixin', 'ETagRequestMixin', + 'ETagResponseMixin', 'ResponseStreamMixin', + 'CommonResponseDescriptorsMixin', 'UserAgentMixin', + 'AuthorizationMixin', 'WWWAuthenticateMixin', + 'CommonRequestDescriptorsMixin'], + 'werkzeug.security': ['generate_password_hash', 'check_password_hash'], + # the undocumented easteregg ;-) + 'werkzeug._internal': ['_easteregg'] +} + +# modules that should be imported when accessed as attributes of werkzeug +attribute_modules = frozenset(['exceptions', 'routing']) + + +object_origins = {} +for module, items in iteritems(all_by_module): + for item in items: + object_origins[item] = module + + +class module(ModuleType): + + """Automatically import objects from the modules.""" + + def __getattr__(self, name): + if name in object_origins: + module = __import__(object_origins[name], None, None, [name]) + for extra_name in all_by_module[module.__name__]: + setattr(self, extra_name, getattr(module, extra_name)) + return getattr(module, name) + elif name in attribute_modules: + __import__('werkzeug.' + name) + return ModuleType.__getattribute__(self, name) + + def __dir__(self): + """Just show what we want to show.""" + result = list(new_module.__all__) + result.extend(('__file__', '__doc__', '__all__', + '__docformat__', '__name__', '__path__', + '__package__', '__version__')) + return result + +# keep a reference to this module so that it's not garbage collected +old_module = sys.modules['werkzeug'] + + +# setup the new module and patch it into the dict of loaded modules +new_module = sys.modules['werkzeug'] = module('werkzeug') +new_module.__dict__.update({ + '__file__': __file__, + '__package__': 'werkzeug', + '__path__': __path__, + '__doc__': __doc__, + '__version__': __version__, + '__all__': tuple(object_origins) + tuple(attribute_modules), + '__docformat__': 'restructuredtext en' +}) + + +# Due to bootstrapping issues we need to import exceptions here. +# Don't ask :-( +__import__('werkzeug.exceptions') diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/__init__.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cfb02f8686fb289ff798ccc25249b269add678e9 GIT binary patch literal 4739 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 XlD&xZmy7MIPH7QW6sEs9+o}8?n&kpD literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/_compat.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/_compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bcae6c6f18561225551a00e56fedd4f3eedc6f12 GIT binary patch literal 7142 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 Pm{@NZ{n`A{Qkne^NwdFz literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/_internal.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/_internal.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dbb2c7dddb3bdbb5d393d0bb3f99927e7487c6de GIT binary patch literal 12628 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 W^CrB@%R%q!-uJw3;Qyxg_5TOh1q8+b literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/datastructures.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/datastructures.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71d2ed7bedba3a50518337ed31a8c1dc80cf1a81 GIT binary patch literal 100079 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 DHHzj0 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/exceptions.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/exceptions.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f089ce2ba63d622096b249bc77c7be643c1a272 GIT binary patch literal 22973 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 N`F(?Ub-_sb{{UTrvS0uJ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/filesystem.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/filesystem.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e6ae72ae50f9df18d919f75fefcb855b5902a4d GIT binary patch literal 2250 zcmZuyU2oeq6qRh*QJfES-P&S%V$p}x*leV0S7b&pG)<b$K<5p0vJ{2^l@_mU#xm)V zR2*mR_K^OW{eb-$J#E<2{=%MiN!hWnRf#B3B=6-tJm>h~)>h@>_h$5K=y`v6D_05M zpW>~%*f6i-F`pF@zvH`Sp;K_rVy6g&v*4`6idX(*mj$*rDtF4P#LDlz&e{u)t+DXE z$3h5U{VM2G*gD-{6}Ew+o2=0Gs+*U8RtR2GP@_vo2lYNrK+m*+vHSkZ|F4Q0_>n3u zQ%zFC6K*(Y(wB;uA&^AIG$G44^?X4imXn#{gQ01V`*D?gx%=QN(wn&@r}OOO80h#5 zq5;VM$?><}ziPXvW2t~JFq9{{?y@%$9)r-(AkW%|`GS|tgXgXG^A9Ze8$tA1St;Qn zz33Esn#I2TgqMA8;C+C%evHjqgnASz2hQw<@y`6W-j#n=V8s!_6?mpNs;FHb7y6zB z@HNgy7=z0{+`eA$a}b0I0wR`-ivgC$5gI;76+$FaLwMhnd_4h&X>3xOBr~$#K4><{ z{ppa$Ll+MRwHyNm!BA5KL5dx-oIj-k0}3*h+K`xP(A7FQwi)qXKnz+_HFJ@A5Ytp6 zt}aE4LGkPa14YJE%!r`&gHGcip_=Ts+Rf<o!}_DzgRdX&<&d|IvJ%xX=hYx9V{s7c ztei+WN+(%hmuDL<UGKS4QdRvdT$K2mr`8eLe|iUv?uiDayR#j=GwU9L9vM05KEq|T z@2H>y7=tjiwxeHdOp%4Ei-4YY6W;4;ZlE@y@d)SX?t-x06{1aM+0BUS$Za!Pq`zg) zb`u+~9QuJ@@Nf9rzOttt+mDOOQsF7y`UW=Oq1HUBvrf_Jq?p}#rsN33(X~EKsS<cB zm+>)@ewud(Ld-0yuNiG5(R?6K5tQ^?gjH#fQ=TM53OxHBxN~v=q=<n`@N85n3?D<i zg`kH~WMPh?tP(|I$<oBWZ${BiNS^t~S`;xEN09@3rKw;|%3^TOR#?C$EV%yBH3qi+ zBHjmf#uIET`LDcpo++rCZ@o=Ve`bnS3~J8=`jIJ}m6?CGX1|#5s$k_n16FLKgsdRw zI%aVdy#kXep&jd&hqbB@YKR`nG(qIRAy_9ZX#iK?h!YUkE)?VaJ}Be}=_@&Q5r3GO zp%gilwV}d^PUaf>P`0JE#@35$Mg}m!1L!Ao3am~JB@xotedM{%X_5k=1Ih)eEYG)N z#lx)%$o>z_d~*whaYL4&7aOjVHkw*PLBH{4xvkoQqi?R=6E4sV=87HCb1v1~KP}5* zON@yjSRY-X(GpAaf&mCn&P}WbU?EEbnaR{eo`bCbtCpfp_UFn+Yr-i6&XIWnkjO~h zYZ&^ehz&=m;lk|!?cN2ODzty=PKr(}WgRFAaI2c5z2xR``Q{J|#0FzhL`UOj?um?? zw3@G1YH8$>uxU?@k7|3?j+TI}R*LRGnT6u#-Mi}D@ntDB{n}o2JuBkxS9WUvX1Q*G zb9Qe?b(HWCmLB!e!C)56&oNp~cHWTPjjX1DMYM;uy)t}lELk{Lz0LyrmX#5cF*QzD zXn+NE{vQcucW_r93iY`a-EC~Vt<bOd)~E8nvU{peaiqGXtTm~?HXqr>I$IV_5^8MX ztb+VkMo+mJs*mls^|);NRqi3DxkF{!CpmHFeYaWliJfkftZdkNwz9^Ng->$3`NsB~ TKL&*mHyH-Ei{bXoQc(OCWmBU~ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/formparser.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/formparser.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b02d4cf8f74b5b19a2cceb2a5b9e3d924697c4f GIT binary patch literal 16196 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 Ag8%>k literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/http.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/http.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e0d6f007998c5d2160818dbd67b5bded6ae52f5 GIT binary patch literal 33502 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%0D;-3YI#e=WvKy7x}y$}X)vA8>c(JY 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¥8&Se<q}{m&-=y4Y&}@^8ONmA* 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 T?Aw9O_%FMf*|tBuEB?O${AbEj literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/local.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/local.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5f2aedebb2dd0889814d932e81efedf3f800bb37 GIT binary patch literal 18719 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 X6XNgg&H0IZ@$s=2#%_(>8g~B|K{dW! literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/routing.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/routing.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0657beb3b6dfee77a177c8c79b57f96b7f834a5 GIT binary patch literal 60193 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 Ycxd>s;r+w=h7a(4^RJj2<}=y<A4{(p_5c6? literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/security.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/security.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be2c9ae94fb07dae7fe3582b2a23e571172ebe1a GIT binary patch literal 8561 zcmd^EO>7%UcJ4p^>c_Gy+p=fY+vAbPrdQ_J9(%^ab&SY=W6gSGuO*Ln8O@R{R#R$< z?53-V`XQP*tQ;Vh1bYs!3k1j|mtZe@%_XN@<dAb;bMoF2AVJ^&LB3bjq$tJMTYvy* ziPhazudAx7-uvG7UVk<<Rowf{a_#NMhVjqF*x}&%kMR@NO~YVD)nF#G+Gf>MXRB(d zvt6~-*{M3}oT+ATw%gfGu9`FH8K<4^6siSvooN?46V(ZIoo!Ecrm9owI@g}=oT#4Y z%v5JOC#xqrv(?$osp_fDTy?H<x_TNi`RW-~sGjw6taxCy=BwxY^S;F<m~~)RKky3& zM)gDgdu)<Ty)k^Vb)ofrRl1wjL*u|aFsmQ2X?Eg`QN8G2gzU%uS!OY-X|ox2@{L(t z@Gto`n?10qpNv{gu{pF{W~bR1Ja>hiW%D?f*g19{=d0{v_C0n15<g(yXD(_M*+=Xm z&exc=YLpiG|59{u?D~AW@Ao##!f*6=Aoo@6pB_Jos(Lk*ak=02q|e+fzuon@a7C}N z<$5@ifbVuaA$B8<c28^t;ifC2s4dF6lS(7%?(<-COIF+~jgouw`i)!e#=a}JeD_s4 z8+Xn3I*aJecb8tSJ$d$G)on)H?L_FD`O*uhH=2H>9W?w<_!am5>O=j&)8z+`R#qRq z=YhARJu4+M$*xM?Ye-y8uI>xzcV2iQi#oWs_acr{PDqb;ylyF%6uB2}`Zc-V^%MK~ zPj4m#>F-H>?*#pzo8)Cw+t`=BNG65kcwN;fAQ;s`PX;@F^`5uUV19FRD`;)EJ7Ls) z&4ujk?C$ONm+n7!_~`LhPnN&_(bI2Mo<0BZi`BK4-@f|EPpj8&-2Cj;?a#ls^JVb- z9}J^!-CItk8&Rk0algibO<%|cR9_tyN-~HBe&Rfe*!XUCV8$?s?Hspa^IO9-UR%t- z*4r!Z%uh?!3ZI7D-x?(=vC7vITligm0yV05SAS4he<`rH>-(3*<^A=CzSx#gcl~}8 zZD0Gw3%yOh<A?H^UcYM{56j5ccl>Z?y&Y_<i$MC<x?W=&a$<d`Wa~pED|h$b74B~O zp}*JV_gsuBX!ed_nfw$k=rz<qwORZg;3vLDAq`0eZXDPHydyKV^L(D=2S#8F%)KkI z@zD6r9atn4<82{j$2O!2{9<g!sJm;tzWB<BO{_y<rCRjbn-R2rt0NM-!)_;z*z#^9 zS(G<#-qPpWpR4m{cl7=38#hZ?K8LCB(^Q<H;w%;ORGdSB70qozOQqyVj!Xb6QgW&p z8cZY>->EuunK;c}*f?BsK8t6Q`C2mwSuK66b~wQWv<eb2%t<qEI`}K9t4WhzKy4#K zZ8&JXMr-EcMq;Jt-LV4BL1vJVrnFji3?MwP4rnDTuLK2p8f^P+h2~VLcQ>|K^JZ-$ z2<waPMo+@xWsir#^<eXR*Zi;%G1$M>_tdb<Wp@V(6GWlM&{(Os*UMj~HV>WkIxh31 zS5byeS3m_AGlzYowycGO*9I~W8qk$8W4ck2u%;+HA`Ly`s#fS{!d_>?=Wf(=4_l5r zu<h@+{ZI?zVpDFBFpa={(Q8YBE69{xce&|?k#xH}+6nMJiwbZEbjA)fGi8dd2>QOe z8?@W*hVS--&yEaB`>B>z4Mle@-O~HeIzS#Hu5AUvZFr&ULnZMR=m_Wcjq9dM?}lE- zrx)<t?J(L672=M1UUUJP-stxI9W{B>8BGeB3Y2BNHcjWD-}JB|f+k27ar<-qN=X4a zP$La!l$@k^s4ILD+LmNhi%7CsOG??qq3wokVLy}N;l3nUy%|Y<xH|kpba31vh67;G z8)#NYVVsyT`4t*%A{~CK<26PmFol*pehM~b0Fp2PM?&5CoU~%+oH201V(f?u(r!5e zM`mJp;emlOeDT1PSzd>!7%-KbHWS=uT6t-0qXo|t=x%6K2bonv7F!d5BlHc($;P(W zi?gjsfKRpww!xe?<{&4h&|`j2%4wE~?FoY<&Kna3%bYb1zyrSf$)FJDVpE=o3kN3Z zOve}$2NQd@<TSu6GmjcbEes~(oSca#h&K!-<di%qXXUB5h#HFwdQg1*J3w5%H5Y@6 z0QUdpl>zxYW@rDIbq>A2<g?zt!sK6!tQwZ?@qJgjm66dszyG6$k8f(F8X*y>Tb*{a zt`uq)x~lYN6P6shb=u{6*<F^Y1^U9RAMSbGl|ctpaJXbytk<9$)e)3Ib*+`64o(1{ zT6XVaR$37k-PI>c*Pw<j%!DkAD8~^~`l0H`!)r8~K8Jr&&7{=zp^DX&fx)tdcK!bY zz20a0|F^;Ge+j!BLd=y05hglp{OEcWKS6FZHd_Yl)QYW^NvL2NKn%Fc18q0v3LwuR z3Y%nmU08vi6i87$&OKCV`zhIpxd%ewH4<~{IGjz@y29_V2)q)UGo*3JTO07{MYI3W zA)1!&wxfpE7Wc|Wo_I@~uXGwfDuNI^HEj5N0#(U`A`LZiD}sN38cj=z)HX|PDI2Zn zArv(sj>4~!Y>C7iLea!|^3BqNWJ147jr<zFMj|xLq);PTR|`U!WR!(+;vJlb8ZVGQ z7WCK;S!s%YOm#$C_$4Ynq2e-%YOw~(mTJ2EI@R7pk=Pr(Ci=unZ!15Ln7zadjx8T| zA^H`5f}SyEi&!+r%wqBK4xQ;fk8{?Vrh04A$(k3;Idc|NY2KVR)$^m>r>WCV`H5fS zf~b=A6S2{<K#<5QKqpk)73hYpGo=k;UEmdIlNUHZL?s>47L<5q+vY3GUNx8lDw^SM z_MXeEmIHo&xNZ&XffHNsEjFIW!DpCD#xv;L>y1}NxUhF|OnPo8P2??=`UTM4V$L?j zahd+VE7mX~6KQ~eSWLwt!VN-KL>+&3%ad*+QW_%_2&aTYAp`ec_W}?g06D}67!J7+ zD%w8yy&Hw#WU5yJV9htsyT^7&2~!*$x|I4F@A-8%(A-Myf<mMdLiplhIIpefN&PWY zd?dK<QY&cJA3)_V)QtO?o@`#bv+|fLd&u)!*gj59pI=0goUU#30-|#@;Whml2Cp?b z-NeRw@f=AhWwjBdmNJRyCFX`EAD`00=Ke9YLufbFOAHwW7Dk@*4SyDl*P1aMi)i_r zdES~cXDt2%a-$H9rb$+!bd9JB_=34@@)-%<aA5u%JfVeb1k1c3#DOm9%mDf7{?b<n zmB4d-*K;*)3^xM4cy;ONTJ6D;B?QUJ7mKdn2$})hW4*3-v0g8$RqB7ZN|ZX?+D;F9 zN{%o>v<Bau9L|*x(Z)~EkK!7HlQZB&Mhl_C!FVtUJhYR1P3(&r4>!U6wjxC2N%3$} zC5z9Xhs3NMTT>P3{fb7Q;T)s?0RlqoR$Cq--zY}JD8+BRcF%vrdBhW^6$POJBns#C z3mxk|$q&2xD;h&#EVe0c@*m^AL9a&#!6UV)ZMlF=Qrm(#BVIC^WY{n^W&R6r14zmM z6427J8H|_zIi#^=M5|g3%c9K&i9`!KiMV@BW*Mm5&&}9wsl*UsR-1E~M@(8gXLLhK z7Imd83>-Ew$goL(gwwRn7=vt>XHx^v(m_^JOZ1$Jb1Z{+_wVE+n~se$P!aT;;r|qy z;_ori3A&e4n70`_ke&GpXOIsc$L22#{$C>y`@I4&dJ9udw@#?IFn3_LW{8}I^KlO6 zLR@H_j3HwhxKb@~9#ob9XSV;#<q$zv=(UgLdD3JMP-V5(?M57NL$~e0Prx52#f%U} z((dX&su$8VaX2u+^m3Rt66j<(1W^+@2!zzANdW|rKZOG1uZC<e4Nfv@)D#~2IqmE! zZ^`D7_^9yDaPgKjuK&q$xZ|}0<{k!(9^dRyUPCAftm&^ZowfdC+S4Ubxx^m#PX_73 z9baxm%)P|>AFPsf0UA>@Wn-O`Oy&Au3^YD}iJ~-n9J7j)c9MduILU}^JCK~zlCPjh z>}~}3BENeM38ST#FnT?|+Va|?7fWl)&sJ9XZ6%51LsD2n#!4BBij(*q>gY>qrsz%k zcD{)FQZdQucTm1M$q$!0aj+Uf`R-~V5JTE9<uxS0yQZJ17Km6Y(xLH_LbT(SIIWop z=>qA^UqeLPMPX!Mj~2maQ8{R0K=T05X@XJI<;_V2sARRp)Va@D<m6$+8+iWRRJtiT zJbg>>KeBAH>Ji`)TR=99Tr&Wdbh|st;E0hYf^`CJZ8%y7Hi*0kx!R_EhH_<&_a6X? zCls!GBOTN*OHNXUJgEi@6<IfXhX}1S^-x@+RT<H~PYTk3e@!zGsdLr27ASoXHrhR= zyt41=QPRHf%r1f-6$FV$d+juJ)7bLC&xV_*R$j+EszLd7+OpKaj!l9xtTan%#o*~= z+DP~bw@tYriS$IJqBA;_1JN^DAmd%2hjfdgBk7V*F5~J|xbv%5UCQ<;7dEo|;T(m^ zFa^qV;EY7D(bh>1$~*YTz^LSr?(&Z=EAzfgv<AK#NfHS8A?DtICmZ3-sA~-hNabKZ zQtPBeTXe^f1D$EjY$FYgF-eeCV%ST%R_G-Sh+*Tq9-hG#fmGoo;YR(?!@L=#vN36u zN1{H`m5`Czxq6yetLW;=SYTXtcaih)$MRMLB~h1BL|%J*-H+&e<<4~+W1B!bL&p)R zv350&ieA&<3+i;(@Q&6%s|fXzil!4DX~^M=krujFh#V^NNKaD4EmAK~D!UK7P>o96 zjUB^x!>VKbu%WbGsYQ$y>aHI6-k*%E)pvEBdKl}AE|n(GCZyY>_dcCJZE8)M-use& zS~)^+BMDVVzb^0>Kn=c(qMyGc7SulwocM!+A0&cnbp10F)rqkYbkw7u&?A9SUR~#S z&kSwhsG0sFL^V%22l{AoANTJMRVru!2LXWu)3CM;K1<;SYJhNHDAu|?P(g;Y5TH=r z*E~QH>Kxbwg({_t_gfGhjR#|}=54}VgA;lwuF(rYp$XSgOc-eh{AMpr+rm+g3^Cnv zcVyyif75I1j|_?83n+DJ6GIJAx}j{>Rb*i}idb}!lh*_RX?Z;f;a=H@kZ~HD+;Ftg z9}Gc;y}AHgygDFY7<!;PpYH}p!5+z0cM&p)tqPVSefuFI5*CgydP9FPJEn>$7QcU= z>qiuGIIq+O{|AJTTBkvsJKP~M6q=Oo=$*Ns2t^tSIZ37w^+L&Ck?K(lqLnpM&FWV= zR9dB{RrQSmJ9=DAPrb*L{2tv2a<j$+1=zDD-@^4M$f01Ge6EVncW@CKo8ZfPET+`u zy80SJKKLSq62y}S_TId-&^CjR*okc<Sg+6r%GXzrv_x{YrP#Enf-&3M7M)vJhJcy> zjHJNGRS=CN=Op9}BxS9?B)^^M|Jy18HXsWH3P@KXxNXRY@7I+&5m*hsC(=qG5`v=F zyPn*l^pHUQlG6XAtV%fn@$@MGD8=yTqSoS3$gOn&Y0mlzS`lm=QKQk&dV@EJ)Q;+o zX+k=@mOPx;XkJh|e0W8yMoOX3_E=8}tXBF~x!kwQ<xfk6q(I@_9`%sqL$D2XHCc-U zJ}t=_KKkK{6uwG1sy-74Z$t2Jv2jV(>voaH;m>e`5V<fK30)4?DapW(NRi~50iYEt z$4^4~xE>W$>`WW+pJ-K|CyktDaTGigyQ42!l*mDXM$&i|Z%{{!uFFZTR%214R^x4| zR7_Uow*tET2nD{-sV};mczu%9Au_k=CLiGowTG=&DXOGE9#?&iR(Y#rX83Kgj1L;L zI4Y>r83Iysl_ua6B&sBv`oh7V(ruM`$Fd<?^+UR+ude()6<@2Cc7Qo`=$_UD%ZV>3 z_{6$RHI!&nTwQbXFX;AjDqJd*$&Q?ias$fB)Y6PCuJS6gcp}N))gNu{(L@D#2S+U{ qEiEms`m+|0A#-f{Xo#GclP_j%%bcDqQh6(XA)m|tpm-sF^1lJN*++8# literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/serving.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/serving.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ceac46dfd93367a8357b2b7af829fdecdc5b6e1 GIT binary patch literal 26545 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/test.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/test.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89042ee21b0183cbc455987b79fe12513be98e9a GIT binary patch literal 31079 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/urls.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/urls.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..747a09f416ad46d77d0b8773799f42f02e3fa786 GIT binary patch literal 33353 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/utils.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/utils.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7614e8f08b9e3b5768963ccfccfefda97dcc3a94 GIT binary patch literal 21206 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/wrappers.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/wrappers.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6cbfbc0947e1019f4cde03d0022bd8a436f75b90 GIT binary patch literal 76453 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 DP--=D literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/__pycache__/wsgi.cpython-36.pyc b/venv/Lib/site-packages/werkzeug/__pycache__/wsgi.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38a328d2a9da435add858b4eead528ccd72ed608 GIT binary patch literal 44708 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/_compat.py b/venv/Lib/site-packages/werkzeug/_compat.py new file mode 100644 index 0000000..fda038b --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/_compat.py @@ -0,0 +1,206 @@ +# flake8: noqa +# This whole file is full of lint errors +import codecs +import sys +import operator +import functools +import warnings + +try: + import builtins +except ImportError: + import __builtin__ as builtins + + +PY2 = sys.version_info[0] == 2 +WIN = sys.platform.startswith('win') + +_identity = lambda x: x + +if PY2: + unichr = unichr + text_type = unicode + string_types = (str, unicode) + integer_types = (int, long) + + iterkeys = lambda d, *args, **kwargs: d.iterkeys(*args, **kwargs) + itervalues = lambda d, *args, **kwargs: d.itervalues(*args, **kwargs) + iteritems = lambda d, *args, **kwargs: d.iteritems(*args, **kwargs) + + iterlists = lambda d, *args, **kwargs: d.iterlists(*args, **kwargs) + iterlistvalues = lambda d, *args, **kwargs: d.iterlistvalues(*args, **kwargs) + + int_to_byte = chr + iter_bytes = iter + + exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') + + def fix_tuple_repr(obj): + def __repr__(self): + cls = self.__class__ + return '%s(%s)' % (cls.__name__, ', '.join( + '%s=%r' % (field, self[index]) + for index, field in enumerate(cls._fields) + )) + obj.__repr__ = __repr__ + return obj + + def implements_iterator(cls): + cls.next = cls.__next__ + del cls.__next__ + return cls + + def implements_to_string(cls): + cls.__unicode__ = cls.__str__ + cls.__str__ = lambda x: x.__unicode__().encode('utf-8') + return cls + + def native_string_result(func): + def wrapper(*args, **kwargs): + return func(*args, **kwargs).encode('utf-8') + return functools.update_wrapper(wrapper, func) + + def implements_bool(cls): + cls.__nonzero__ = cls.__bool__ + del cls.__bool__ + return cls + + from itertools import imap, izip, ifilter + range_type = xrange + + from StringIO import StringIO + from cStringIO import StringIO as BytesIO + NativeStringIO = BytesIO + + def make_literal_wrapper(reference): + return _identity + + def normalize_string_tuple(tup): + """Normalizes a string tuple to a common type. Following Python 2 + rules, upgrades to unicode are implicit. + """ + if any(isinstance(x, text_type) for x in tup): + return tuple(to_unicode(x) for x in tup) + return tup + + def try_coerce_native(s): + """Try to coerce a unicode string to native if possible. Otherwise, + leave it as unicode. + """ + try: + return to_native(s) + except UnicodeError: + return s + + wsgi_get_bytes = _identity + + def wsgi_decoding_dance(s, charset='utf-8', errors='replace'): + return s.decode(charset, errors) + + def wsgi_encoding_dance(s, charset='utf-8', errors='replace'): + if isinstance(s, bytes): + return s + return s.encode(charset, errors) + + def to_bytes(x, charset=sys.getdefaultencoding(), errors='strict'): + if x is None: + return None + if isinstance(x, (bytes, bytearray, buffer)): + return bytes(x) + if isinstance(x, unicode): + return x.encode(charset, errors) + raise TypeError('Expected bytes') + + def to_native(x, charset=sys.getdefaultencoding(), errors='strict'): + if x is None or isinstance(x, str): + return x + return x.encode(charset, errors) + +else: + unichr = chr + text_type = str + string_types = (str, ) + integer_types = (int, ) + + iterkeys = lambda d, *args, **kwargs: iter(d.keys(*args, **kwargs)) + itervalues = lambda d, *args, **kwargs: iter(d.values(*args, **kwargs)) + iteritems = lambda d, *args, **kwargs: iter(d.items(*args, **kwargs)) + + iterlists = lambda d, *args, **kwargs: iter(d.lists(*args, **kwargs)) + iterlistvalues = lambda d, *args, **kwargs: iter(d.listvalues(*args, **kwargs)) + + int_to_byte = operator.methodcaller('to_bytes', 1, 'big') + iter_bytes = functools.partial(map, int_to_byte) + + def reraise(tp, value, tb=None): + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + + fix_tuple_repr = _identity + implements_iterator = _identity + implements_to_string = _identity + implements_bool = _identity + native_string_result = _identity + imap = map + izip = zip + ifilter = filter + range_type = range + + from io import StringIO, BytesIO + NativeStringIO = StringIO + + _latin1_encode = operator.methodcaller('encode', 'latin1') + + def make_literal_wrapper(reference): + if isinstance(reference, text_type): + return _identity + return _latin1_encode + + def normalize_string_tuple(tup): + """Ensures that all types in the tuple are either strings + or bytes. + """ + tupiter = iter(tup) + is_text = isinstance(next(tupiter, None), text_type) + for arg in tupiter: + if isinstance(arg, text_type) != is_text: + raise TypeError('Cannot mix str and bytes arguments (got %s)' + % repr(tup)) + return tup + + try_coerce_native = _identity + wsgi_get_bytes = _latin1_encode + + def wsgi_decoding_dance(s, charset='utf-8', errors='replace'): + return s.encode('latin1').decode(charset, errors) + + def wsgi_encoding_dance(s, charset='utf-8', errors='replace'): + if isinstance(s, text_type): + s = s.encode(charset) + return s.decode('latin1', errors) + + def to_bytes(x, charset=sys.getdefaultencoding(), errors='strict'): + if x is None: + return None + if isinstance(x, (bytes, bytearray, memoryview)): # noqa + return bytes(x) + if isinstance(x, str): + return x.encode(charset, errors) + raise TypeError('Expected bytes') + + def to_native(x, charset=sys.getdefaultencoding(), errors='strict'): + if x is None or isinstance(x, str): + return x + return x.decode(charset, errors) + + +def to_unicode(x, charset=sys.getdefaultencoding(), errors='strict', + allow_none_charset=False): + if x is None: + return None + if not isinstance(x, bytes): + return text_type(x) + if charset is None and allow_none_charset: + return x + return x.decode(charset, errors) diff --git a/venv/Lib/site-packages/werkzeug/_internal.py b/venv/Lib/site-packages/werkzeug/_internal.py new file mode 100644 index 0000000..28bfd9f --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/_internal.py @@ -0,0 +1,419 @@ +# -*- coding: utf-8 -*- +""" + werkzeug._internal + ~~~~~~~~~~~~~~~~~~ + + This module provides internally used helpers and constants. + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +import re +import string +import inspect +from weakref import WeakKeyDictionary +from datetime import datetime, date +from itertools import chain + +from werkzeug._compat import iter_bytes, text_type, BytesIO, int_to_byte, \ + range_type, integer_types + + +_logger = None +_empty_stream = BytesIO() +_signature_cache = WeakKeyDictionary() +_epoch_ord = date(1970, 1, 1).toordinal() +_cookie_params = set((b'expires', b'path', b'comment', + b'max-age', b'secure', b'httponly', + b'version')) +_legal_cookie_chars = (string.ascii_letters + + string.digits + + u"/=!#$%&'*+-.^_`|~:").encode('ascii') + +_cookie_quoting_map = { + b',': b'\\054', + b';': b'\\073', + b'"': b'\\"', + b'\\': b'\\\\', +} +for _i in chain(range_type(32), range_type(127, 256)): + _cookie_quoting_map[int_to_byte(_i)] = ('\\%03o' % _i).encode('latin1') + + +_octal_re = re.compile(br'\\[0-3][0-7][0-7]') +_quote_re = re.compile(br'[\\].') +_legal_cookie_chars_re = br'[\w\d!#%&\'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]' +_cookie_re = re.compile(br""" + (?P<key>[^=;]+) + (?:\s*=\s* + (?P<val> + "(?:[^\\"]|\\.)*" | + (?:.*?) + ) + )? + \s*; +""", flags=re.VERBOSE) + + +class _Missing(object): + + def __repr__(self): + return 'no value' + + def __reduce__(self): + return '_missing' + +_missing = _Missing() + + +def _get_environ(obj): + env = getattr(obj, 'environ', obj) + assert isinstance(env, dict), \ + '%r is not a WSGI environment (has to be a dict)' % type(obj).__name__ + return env + + +def _log(type, message, *args, **kwargs): + """Log into the internal werkzeug logger.""" + global _logger + if _logger is None: + import logging + _logger = logging.getLogger('werkzeug') + # Only set up a default log handler if the + # end-user application didn't set anything up. + if not logging.root.handlers and _logger.level == logging.NOTSET: + _logger.setLevel(logging.INFO) + handler = logging.StreamHandler() + _logger.addHandler(handler) + getattr(_logger, type)(message.rstrip(), *args, **kwargs) + + +def _parse_signature(func): + """Return a signature object for the function.""" + if hasattr(func, 'im_func'): + func = func.im_func + + # if we have a cached validator for this function, return it + parse = _signature_cache.get(func) + if parse is not None: + return parse + + # inspect the function signature and collect all the information + if hasattr(inspect, 'getfullargspec'): + tup = inspect.getfullargspec(func) + else: + tup = inspect.getargspec(func) + positional, vararg_var, kwarg_var, defaults = tup[:4] + defaults = defaults or () + arg_count = len(positional) + arguments = [] + for idx, name in enumerate(positional): + if isinstance(name, list): + raise TypeError('cannot parse functions that unpack tuples ' + 'in the function signature') + try: + default = defaults[idx - arg_count] + except IndexError: + param = (name, False, None) + else: + param = (name, True, default) + arguments.append(param) + arguments = tuple(arguments) + + def parse(args, kwargs): + new_args = [] + missing = [] + extra = {} + + # consume as many arguments as positional as possible + for idx, (name, has_default, default) in enumerate(arguments): + try: + new_args.append(args[idx]) + except IndexError: + try: + new_args.append(kwargs.pop(name)) + except KeyError: + if has_default: + new_args.append(default) + else: + missing.append(name) + else: + if name in kwargs: + extra[name] = kwargs.pop(name) + + # handle extra arguments + extra_positional = args[arg_count:] + if vararg_var is not None: + new_args.extend(extra_positional) + extra_positional = () + if kwargs and kwarg_var is None: + extra.update(kwargs) + kwargs = {} + + return new_args, kwargs, missing, extra, extra_positional, \ + arguments, vararg_var, kwarg_var + _signature_cache[func] = parse + return parse + + +def _date_to_unix(arg): + """Converts a timetuple, integer or datetime object into the seconds from + epoch in utc. + """ + if isinstance(arg, datetime): + arg = arg.utctimetuple() + elif isinstance(arg, integer_types + (float,)): + return int(arg) + year, month, day, hour, minute, second = arg[:6] + days = date(year, month, 1).toordinal() - _epoch_ord + day - 1 + hours = days * 24 + hour + minutes = hours * 60 + minute + seconds = minutes * 60 + second + return seconds + + +class _DictAccessorProperty(object): + + """Baseclass for `environ_property` and `header_property`.""" + read_only = False + + def __init__(self, name, default=None, load_func=None, dump_func=None, + read_only=None, doc=None): + self.name = name + self.default = default + self.load_func = load_func + self.dump_func = dump_func + if read_only is not None: + self.read_only = read_only + self.__doc__ = doc + + def __get__(self, obj, type=None): + if obj is None: + return self + storage = self.lookup(obj) + if self.name not in storage: + return self.default + rv = storage[self.name] + if self.load_func is not None: + try: + rv = self.load_func(rv) + except (ValueError, TypeError): + rv = self.default + return rv + + def __set__(self, obj, value): + if self.read_only: + raise AttributeError('read only property') + if self.dump_func is not None: + value = self.dump_func(value) + self.lookup(obj)[self.name] = value + + def __delete__(self, obj): + if self.read_only: + raise AttributeError('read only property') + self.lookup(obj).pop(self.name, None) + + def __repr__(self): + return '<%s %s>' % ( + self.__class__.__name__, + self.name + ) + + +def _cookie_quote(b): + buf = bytearray() + all_legal = True + _lookup = _cookie_quoting_map.get + _push = buf.extend + + for char in iter_bytes(b): + if char not in _legal_cookie_chars: + all_legal = False + char = _lookup(char, char) + _push(char) + + if all_legal: + return bytes(buf) + return bytes(b'"' + buf + b'"') + + +def _cookie_unquote(b): + if len(b) < 2: + return b + if b[:1] != b'"' or b[-1:] != b'"': + return b + + b = b[1:-1] + + i = 0 + n = len(b) + rv = bytearray() + _push = rv.extend + + while 0 <= i < n: + o_match = _octal_re.search(b, i) + q_match = _quote_re.search(b, i) + if not o_match and not q_match: + rv.extend(b[i:]) + break + j = k = -1 + if o_match: + j = o_match.start(0) + if q_match: + k = q_match.start(0) + if q_match and (not o_match or k < j): + _push(b[i:k]) + _push(b[k + 1:k + 2]) + i = k + 2 + else: + _push(b[i:j]) + rv.append(int(b[j + 1:j + 4], 8)) + i = j + 4 + + return bytes(rv) + + +def _cookie_parse_impl(b): + """Lowlevel cookie parsing facility that operates on bytes.""" + i = 0 + n = len(b) + + while i < n: + match = _cookie_re.search(b + b';', i) + if not match: + break + + key = match.group('key').strip() + value = match.group('val') or b'' + i = match.end(0) + + # Ignore parameters. We have no interest in them. + if key.lower() not in _cookie_params: + yield _cookie_unquote(key), _cookie_unquote(value) + + +def _encode_idna(domain): + # If we're given bytes, make sure they fit into ASCII + if not isinstance(domain, text_type): + domain.decode('ascii') + return domain + + # Otherwise check if it's already ascii, then return + try: + return domain.encode('ascii') + except UnicodeError: + pass + + # Otherwise encode each part separately + parts = domain.split('.') + for idx, part in enumerate(parts): + parts[idx] = part.encode('idna') + return b'.'.join(parts) + + +def _decode_idna(domain): + # If the input is a string try to encode it to ascii to + # do the idna decoding. if that fails because of an + # unicode error, then we already have a decoded idna domain + if isinstance(domain, text_type): + try: + domain = domain.encode('ascii') + except UnicodeError: + return domain + + # Decode each part separately. If a part fails, try to + # decode it with ascii and silently ignore errors. This makes + # most sense because the idna codec does not have error handling + parts = domain.split(b'.') + for idx, part in enumerate(parts): + try: + parts[idx] = part.decode('idna') + except UnicodeError: + parts[idx] = part.decode('ascii', 'ignore') + + return '.'.join(parts) + + +def _make_cookie_domain(domain): + if domain is None: + return None + domain = _encode_idna(domain) + if b':' in domain: + domain = domain.split(b':', 1)[0] + if b'.' in domain: + return domain + raise ValueError( + 'Setting \'domain\' for a cookie on a server running locally (ex: ' + 'localhost) is not supported by complying browsers. You should ' + 'have something like: \'127.0.0.1 localhost dev.localhost\' on ' + 'your hosts file and then point your server to run on ' + '\'dev.localhost\' and also set \'domain\' for \'dev.localhost\'' + ) + + +def _easteregg(app=None): + """Like the name says. But who knows how it works?""" + def bzzzzzzz(gyver): + import base64 + import zlib + return zlib.decompress(base64.b64decode(gyver)).decode('ascii') + gyver = u'\n'.join([x + (77 - len(x)) * u' ' for x in bzzzzzzz(b''' +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 +7f2zLkGNv8b191cD/3vs9Q833z8t''').splitlines()]) + + def easteregged(environ, start_response): + def injecting_start_response(status, headers, exc_info=None): + headers.append(('X-Powered-By', 'Werkzeug')) + return start_response(status, headers, exc_info) + if app is not None and environ.get('QUERY_STRING') != 'macgybarchakku': + return app(environ, injecting_start_response) + injecting_start_response('200 OK', [('Content-Type', 'text/html')]) + return [(u''' +<!DOCTYPE html> +<html> +<head> +<title>About Werkzeug + + + +

            Werkzeug

            +

            the Swiss Army knife of Python web development.

            +
            %s\n\n\n
            + +''' % gyver).encode('latin1')] + return easteregged diff --git a/venv/Lib/site-packages/werkzeug/_reloader.py b/venv/Lib/site-packages/werkzeug/_reloader.py new file mode 100644 index 0000000..0d23dba --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/_reloader.py @@ -0,0 +1,277 @@ +import os +import sys +import time +import subprocess +import threading +from itertools import chain + +from werkzeug._internal import _log +from werkzeug._compat import PY2, iteritems, text_type + + +def _iter_module_files(): + """This iterates over all relevant Python files. It goes through all + loaded files from modules, all files in folders of already loaded modules + as well as all files reachable through a package. + """ + # The list call is necessary on Python 3 in case the module + # dictionary modifies during iteration. + for module in list(sys.modules.values()): + if module is None: + continue + filename = getattr(module, '__file__', None) + if filename: + if os.path.isdir(filename) and \ + os.path.exists(os.path.join(filename, "__init__.py")): + filename = os.path.join(filename, "__init__.py") + + old = None + while not os.path.isfile(filename): + old = filename + filename = os.path.dirname(filename) + if filename == old: + break + else: + if filename[-4:] in ('.pyc', '.pyo'): + filename = filename[:-1] + yield filename + + +def _find_observable_paths(extra_files=None): + """Finds all paths that should be observed.""" + rv = set(os.path.dirname(os.path.abspath(x)) + if os.path.isfile(x) else os.path.abspath(x) + for x in sys.path) + + for filename in extra_files or (): + rv.add(os.path.dirname(os.path.abspath(filename))) + + for module in list(sys.modules.values()): + fn = getattr(module, '__file__', None) + if fn is None: + continue + fn = os.path.abspath(fn) + rv.add(os.path.dirname(fn)) + + return _find_common_roots(rv) + + +def _get_args_for_reloading(): + """Returns the executable. This contains a workaround for windows + if the executable is incorrectly reported to not have the .exe + extension which can cause bugs on reloading. + """ + rv = [sys.executable] + py_script = sys.argv[0] + if os.name == 'nt' and not os.path.exists(py_script) and \ + os.path.exists(py_script + '.exe'): + py_script += '.exe' + if os.path.splitext(rv[0])[1] == '.exe' and os.path.splitext(py_script)[1] == '.exe': + rv.pop(0) + rv.append(py_script) + rv.extend(sys.argv[1:]) + return rv + + +def _find_common_roots(paths): + """Out of some paths it finds the common roots that need monitoring.""" + paths = [x.split(os.path.sep) for x in paths] + root = {} + for chunks in sorted(paths, key=len, reverse=True): + node = root + for chunk in chunks: + node = node.setdefault(chunk, {}) + node.clear() + + rv = set() + + def _walk(node, path): + for prefix, child in iteritems(node): + _walk(child, path + (prefix,)) + if not node: + rv.add('/'.join(path)) + _walk(root, ()) + return rv + + +class ReloaderLoop(object): + name = None + + # monkeypatched by testsuite. wrapping with `staticmethod` is required in + # case time.sleep has been replaced by a non-c function (e.g. by + # `eventlet.monkey_patch`) before we get here + _sleep = staticmethod(time.sleep) + + def __init__(self, extra_files=None, interval=1): + self.extra_files = set(os.path.abspath(x) + for x in extra_files or ()) + self.interval = interval + + def run(self): + pass + + def restart_with_reloader(self): + """Spawn a new Python interpreter with the same arguments as this one, + but running the reloader thread. + """ + while 1: + _log('info', ' * Restarting with %s' % self.name) + args = _get_args_for_reloading() + new_environ = os.environ.copy() + new_environ['WERKZEUG_RUN_MAIN'] = 'true' + + # a weird bug on windows. sometimes unicode strings end up in the + # environment and subprocess.call does not like this, encode them + # to latin1 and continue. + if os.name == 'nt' and PY2: + for key, value in iteritems(new_environ): + if isinstance(value, text_type): + new_environ[key] = value.encode('iso-8859-1') + + exit_code = subprocess.call(args, env=new_environ, + close_fds=False) + if exit_code != 3: + return exit_code + + def trigger_reload(self, filename): + self.log_reload(filename) + sys.exit(3) + + def log_reload(self, filename): + filename = os.path.abspath(filename) + _log('info', ' * Detected change in %r, reloading' % filename) + + +class StatReloaderLoop(ReloaderLoop): + name = 'stat' + + def run(self): + mtimes = {} + while 1: + for filename in chain(_iter_module_files(), + self.extra_files): + try: + mtime = os.stat(filename).st_mtime + except OSError: + continue + + old_time = mtimes.get(filename) + if old_time is None: + mtimes[filename] = mtime + continue + elif mtime > old_time: + self.trigger_reload(filename) + self._sleep(self.interval) + + +class WatchdogReloaderLoop(ReloaderLoop): + + def __init__(self, *args, **kwargs): + ReloaderLoop.__init__(self, *args, **kwargs) + from watchdog.observers import Observer + from watchdog.events import FileSystemEventHandler + self.observable_paths = set() + + def _check_modification(filename): + if filename in self.extra_files: + self.trigger_reload(filename) + dirname = os.path.dirname(filename) + if dirname.startswith(tuple(self.observable_paths)): + if filename.endswith(('.pyc', '.pyo', '.py')): + self.trigger_reload(filename) + + class _CustomHandler(FileSystemEventHandler): + + def on_created(self, event): + _check_modification(event.src_path) + + def on_modified(self, event): + _check_modification(event.src_path) + + def on_moved(self, event): + _check_modification(event.src_path) + _check_modification(event.dest_path) + + def on_deleted(self, event): + _check_modification(event.src_path) + + reloader_name = Observer.__name__.lower() + if reloader_name.endswith('observer'): + reloader_name = reloader_name[:-8] + reloader_name += ' reloader' + + self.name = reloader_name + + self.observer_class = Observer + self.event_handler = _CustomHandler() + self.should_reload = False + + def trigger_reload(self, filename): + # This is called inside an event handler, which means throwing + # SystemExit has no effect. + # https://github.com/gorakhargosh/watchdog/issues/294 + self.should_reload = True + self.log_reload(filename) + + def run(self): + watches = {} + observer = self.observer_class() + observer.start() + + try: + while not self.should_reload: + to_delete = set(watches) + paths = _find_observable_paths(self.extra_files) + for path in paths: + if path not in watches: + try: + watches[path] = observer.schedule( + self.event_handler, path, recursive=True) + except OSError: + # Clear this path from list of watches We don't want + # the same error message showing again in the next + # iteration. + watches[path] = None + to_delete.discard(path) + for path in to_delete: + watch = watches.pop(path, None) + if watch is not None: + observer.unschedule(watch) + self.observable_paths = paths + self._sleep(self.interval) + finally: + observer.stop() + observer.join() + + sys.exit(3) + + +reloader_loops = { + 'stat': StatReloaderLoop, + 'watchdog': WatchdogReloaderLoop, +} + +try: + __import__('watchdog.observers') +except ImportError: + reloader_loops['auto'] = reloader_loops['stat'] +else: + reloader_loops['auto'] = reloader_loops['watchdog'] + + +def run_with_reloader(main_func, extra_files=None, interval=1, + reloader_type='auto'): + """Run the given function in an independent python interpreter.""" + import signal + reloader = reloader_loops[reloader_type](extra_files, interval) + signal.signal(signal.SIGTERM, lambda *args: sys.exit(0)) + try: + if os.environ.get('WERKZEUG_RUN_MAIN') == 'true': + t = threading.Thread(target=main_func, args=()) + t.setDaemon(True) + t.start() + reloader.run() + else: + sys.exit(reloader.restart_with_reloader()) + except KeyboardInterrupt: + pass diff --git a/venv/Lib/site-packages/werkzeug/contrib/__init__.py b/venv/Lib/site-packages/werkzeug/contrib/__init__.py new file mode 100644 index 0000000..3e572eb --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/__init__.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.contrib + ~~~~~~~~~~~~~~~~ + + Contains user-submitted code that other users may find useful, but which + is not part of the Werkzeug core. Anyone can write code for inclusion in + the `contrib` package. All modules in this package are distributed as an + add-on library and thus are not part of Werkzeug itself. + + This file itself is mostly for informational purposes and to tell the + Python interpreter that `contrib` is a package. + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" diff --git a/venv/Lib/site-packages/werkzeug/contrib/atom.py b/venv/Lib/site-packages/werkzeug/contrib/atom.py new file mode 100644 index 0000000..51d1a4e --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/atom.py @@ -0,0 +1,355 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.contrib.atom + ~~~~~~~~~~~~~~~~~~~~~ + + This module provides a class called :class:`AtomFeed` which can be + used to generate feeds in the Atom syndication format (see :rfc:`4287`). + + Example:: + + def atom_feed(request): + feed = AtomFeed("My Blog", feed_url=request.url, + url=request.host_url, + subtitle="My example blog for a feed test.") + for post in Post.query.limit(10).all(): + feed.add(post.title, post.body, content_type='html', + author=post.author, url=post.url, id=post.uid, + updated=post.last_update, published=post.pub_date) + return feed.get_response() + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +from datetime import datetime + +from werkzeug.utils import escape +from werkzeug.wrappers import BaseResponse +from werkzeug._compat import implements_to_string, string_types + + +XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml' + + +def _make_text_block(name, content, content_type=None): + """Helper function for the builder that creates an XML text block.""" + if content_type == 'xhtml': + return u'<%s type="xhtml">
            %s
            \n' % \ + (name, XHTML_NAMESPACE, content, name) + if not content_type: + return u'<%s>%s\n' % (name, escape(content), name) + return u'<%s type="%s">%s\n' % (name, content_type, + escape(content), name) + + +def format_iso8601(obj): + """Format a datetime object for iso8601""" + iso8601 = obj.isoformat() + if obj.tzinfo: + return iso8601 + return iso8601 + 'Z' + + +@implements_to_string +class AtomFeed(object): + + """A helper class that creates Atom feeds. + + :param title: the title of the feed. Required. + :param title_type: the type attribute for the title element. One of + ``'html'``, ``'text'`` or ``'xhtml'``. + :param url: the url for the feed (not the url *of* the feed) + :param id: a globally unique id for the feed. Must be an URI. If + not present the `feed_url` is used, but one of both is + required. + :param updated: the time the feed was modified the last time. Must + be a :class:`datetime.datetime` object. If not + present the latest entry's `updated` is used. + Treated as UTC if naive datetime. + :param feed_url: the URL to the feed. Should be the URL that was + requested. + :param author: the author of the feed. Must be either a string (the + name) or a dict with name (required) and uri or + email (both optional). Can be a list of (may be + mixed, too) strings and dicts, too, if there are + multiple authors. Required if not every entry has an + author element. + :param icon: an icon for the feed. + :param logo: a logo for the feed. + :param rights: copyright information for the feed. + :param rights_type: the type attribute for the rights element. One of + ``'html'``, ``'text'`` or ``'xhtml'``. Default is + ``'text'``. + :param subtitle: a short description of the feed. + :param subtitle_type: the type attribute for the subtitle element. + One of ``'text'``, ``'html'``, ``'text'`` + or ``'xhtml'``. Default is ``'text'``. + :param links: additional links. Must be a list of dictionaries with + href (required) and rel, type, hreflang, title, length + (all optional) + :param generator: the software that generated this feed. This must be + a tuple in the form ``(name, url, version)``. If + you don't want to specify one of them, set the item + to `None`. + :param entries: a list with the entries for the feed. Entries can also + be added later with :meth:`add`. + + For more information on the elements see + http://www.atomenabled.org/developers/syndication/ + + Everywhere where a list is demanded, any iterable can be used. + """ + + default_generator = ('Werkzeug', None, None) + + def __init__(self, title=None, entries=None, **kwargs): + self.title = title + self.title_type = kwargs.get('title_type', 'text') + self.url = kwargs.get('url') + self.feed_url = kwargs.get('feed_url', self.url) + self.id = kwargs.get('id', self.feed_url) + self.updated = kwargs.get('updated') + self.author = kwargs.get('author', ()) + self.icon = kwargs.get('icon') + self.logo = kwargs.get('logo') + self.rights = kwargs.get('rights') + self.rights_type = kwargs.get('rights_type') + self.subtitle = kwargs.get('subtitle') + self.subtitle_type = kwargs.get('subtitle_type', 'text') + self.generator = kwargs.get('generator') + if self.generator is None: + self.generator = self.default_generator + self.links = kwargs.get('links', []) + self.entries = entries and list(entries) or [] + + if not hasattr(self.author, '__iter__') \ + or isinstance(self.author, string_types + (dict,)): + self.author = [self.author] + for i, author in enumerate(self.author): + if not isinstance(author, dict): + self.author[i] = {'name': author} + + if not self.title: + raise ValueError('title is required') + if not self.id: + raise ValueError('id is required') + for author in self.author: + if 'name' not in author: + raise TypeError('author must contain at least a name') + + def add(self, *args, **kwargs): + """Add a new entry to the feed. This function can either be called + with a :class:`FeedEntry` or some keyword and positional arguments + that are forwarded to the :class:`FeedEntry` constructor. + """ + if len(args) == 1 and not kwargs and isinstance(args[0], FeedEntry): + self.entries.append(args[0]) + else: + kwargs['feed_url'] = self.feed_url + self.entries.append(FeedEntry(*args, **kwargs)) + + def __repr__(self): + return '<%s %r (%d entries)>' % ( + self.__class__.__name__, + self.title, + len(self.entries) + ) + + def generate(self): + """Return a generator that yields pieces of XML.""" + # atom demands either an author element in every entry or a global one + if not self.author: + if any(not e.author for e in self.entries): + self.author = ({'name': 'Unknown author'},) + + if not self.updated: + dates = sorted([entry.updated for entry in self.entries]) + self.updated = dates and dates[-1] or datetime.utcnow() + + yield u'\n' + yield u'\n' + yield ' ' + _make_text_block('title', self.title, self.title_type) + yield u' %s\n' % escape(self.id) + yield u' %s\n' % format_iso8601(self.updated) + if self.url: + yield u' \n' % escape(self.url) + if self.feed_url: + yield u' \n' % \ + escape(self.feed_url) + for link in self.links: + yield u' \n' % ''.join('%s="%s" ' % + (k, escape(link[k])) for k in link) + for author in self.author: + yield u' \n' + yield u' %s\n' % escape(author['name']) + if 'uri' in author: + yield u' %s\n' % escape(author['uri']) + if 'email' in author: + yield ' %s\n' % escape(author['email']) + yield ' \n' + if self.subtitle: + yield ' ' + _make_text_block('subtitle', self.subtitle, + self.subtitle_type) + if self.icon: + yield u' %s\n' % escape(self.icon) + if self.logo: + yield u' %s\n' % escape(self.logo) + if self.rights: + yield ' ' + _make_text_block('rights', self.rights, + self.rights_type) + generator_name, generator_url, generator_version = self.generator + if generator_name or generator_url or generator_version: + tmp = [u' %s\n' % escape(generator_name)) + yield u''.join(tmp) + for entry in self.entries: + for line in entry.generate(): + yield u' ' + line + yield u'\n' + + def to_string(self): + """Convert the feed into a string.""" + return u''.join(self.generate()) + + def get_response(self): + """Return a response object for the feed.""" + return BaseResponse(self.to_string(), mimetype='application/atom+xml') + + def __call__(self, environ, start_response): + """Use the class as WSGI response object.""" + return self.get_response()(environ, start_response) + + def __str__(self): + return self.to_string() + + +@implements_to_string +class FeedEntry(object): + + """Represents a single entry in a feed. + + :param title: the title of the entry. Required. + :param title_type: the type attribute for the title element. One of + ``'html'``, ``'text'`` or ``'xhtml'``. + :param content: the content of the entry. + :param content_type: the type attribute for the content element. One + of ``'html'``, ``'text'`` or ``'xhtml'``. + :param summary: a summary of the entry's content. + :param summary_type: the type attribute for the summary element. One + of ``'html'``, ``'text'`` or ``'xhtml'``. + :param url: the url for the entry. + :param id: a globally unique id for the entry. Must be an URI. If + not present the URL is used, but one of both is required. + :param updated: the time the entry was modified the last time. Must + be a :class:`datetime.datetime` object. Treated as + UTC if naive datetime. Required. + :param author: the author of the entry. Must be either a string (the + name) or a dict with name (required) and uri or + email (both optional). Can be a list of (may be + mixed, too) strings and dicts, too, if there are + multiple authors. Required if the feed does not have an + author element. + :param published: the time the entry was initially published. Must + be a :class:`datetime.datetime` object. Treated as + UTC if naive datetime. + :param rights: copyright information for the entry. + :param rights_type: the type attribute for the rights element. One of + ``'html'``, ``'text'`` or ``'xhtml'``. Default is + ``'text'``. + :param links: additional links. Must be a list of dictionaries with + href (required) and rel, type, hreflang, title, length + (all optional) + :param categories: categories for the entry. Must be a list of dictionaries + with term (required), scheme and label (all optional) + :param xml_base: The xml base (url) for this feed item. If not provided + it will default to the item url. + + For more information on the elements see + http://www.atomenabled.org/developers/syndication/ + + Everywhere where a list is demanded, any iterable can be used. + """ + + def __init__(self, title=None, content=None, feed_url=None, **kwargs): + self.title = title + self.title_type = kwargs.get('title_type', 'text') + self.content = content + self.content_type = kwargs.get('content_type', 'html') + self.url = kwargs.get('url') + self.id = kwargs.get('id', self.url) + self.updated = kwargs.get('updated') + self.summary = kwargs.get('summary') + self.summary_type = kwargs.get('summary_type', 'html') + self.author = kwargs.get('author', ()) + self.published = kwargs.get('published') + self.rights = kwargs.get('rights') + self.links = kwargs.get('links', []) + self.categories = kwargs.get('categories', []) + self.xml_base = kwargs.get('xml_base', feed_url) + + if not hasattr(self.author, '__iter__') \ + or isinstance(self.author, string_types + (dict,)): + self.author = [self.author] + for i, author in enumerate(self.author): + if not isinstance(author, dict): + self.author[i] = {'name': author} + + if not self.title: + raise ValueError('title is required') + if not self.id: + raise ValueError('id is required') + if not self.updated: + raise ValueError('updated is required') + + def __repr__(self): + return '<%s %r>' % ( + self.__class__.__name__, + self.title + ) + + def generate(self): + """Yields pieces of ATOM XML.""" + base = '' + if self.xml_base: + base = ' xml:base="%s"' % escape(self.xml_base) + yield u'\n' % base + yield u' ' + _make_text_block('title', self.title, self.title_type) + yield u' %s\n' % escape(self.id) + yield u' %s\n' % format_iso8601(self.updated) + if self.published: + yield u' %s\n' % \ + format_iso8601(self.published) + if self.url: + yield u' \n' % escape(self.url) + for author in self.author: + yield u' \n' + yield u' %s\n' % escape(author['name']) + if 'uri' in author: + yield u' %s\n' % escape(author['uri']) + if 'email' in author: + yield u' %s\n' % escape(author['email']) + yield u' \n' + for link in self.links: + yield u' \n' % ''.join('%s="%s" ' % + (k, escape(link[k])) for k in link) + for category in self.categories: + yield u' \n' % ''.join('%s="%s" ' % + (k, escape(category[k])) for k in category) + if self.summary: + yield u' ' + _make_text_block('summary', self.summary, + self.summary_type) + if self.content: + yield u' ' + _make_text_block('content', self.content, + self.content_type) + yield u'\n' + + def to_string(self): + """Convert the feed item into a unicode object.""" + return u''.join(self.generate()) + + def __str__(self): + return self.to_string() diff --git a/venv/Lib/site-packages/werkzeug/contrib/cache.py b/venv/Lib/site-packages/werkzeug/contrib/cache.py new file mode 100644 index 0000000..179ba24 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/cache.py @@ -0,0 +1,913 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.contrib.cache + ~~~~~~~~~~~~~~~~~~~~~~ + + The main problem with dynamic Web sites is, well, they're dynamic. Each + time a user requests a page, the webserver executes a lot of code, queries + the database, renders templates until the visitor gets the page he sees. + + This is a lot more expensive than just loading a file from the file system + and sending it to the visitor. + + For most Web applications, this overhead isn't a big deal but once it + becomes, you will be glad to have a cache system in place. + + How Caching Works + ================= + + Caching is pretty simple. Basically you have a cache object lurking around + somewhere that is connected to a remote cache or the file system or + something else. When the request comes in you check if the current page + is already in the cache and if so, you're returning it from the cache. + Otherwise you generate the page and put it into the cache. (Or a fragment + of the page, you don't have to cache the full thing) + + Here is a simple example of how to cache a sidebar for 5 minutes:: + + def get_sidebar(user): + identifier = 'sidebar_for/user%d' % user.id + value = cache.get(identifier) + if value is not None: + return value + value = generate_sidebar_for(user=user) + cache.set(identifier, value, timeout=60 * 5) + return value + + Creating a Cache Object + ======================= + + To create a cache object you just import the cache system of your choice + from the cache module and instantiate it. Then you can start working + with that object: + + >>> from werkzeug.contrib.cache import SimpleCache + >>> c = SimpleCache() + >>> c.set("foo", "value") + >>> c.get("foo") + 'value' + >>> c.get("missing") is None + True + + Please keep in mind that you have to create the cache and put it somewhere + you have access to it (either as a module global you can import or you just + put it into your WSGI application). + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +import os +import re +import errno +import tempfile +import platform +from hashlib import md5 +from time import time +try: + import cPickle as pickle +except ImportError: # pragma: no cover + import pickle + +from werkzeug._compat import iteritems, string_types, text_type, \ + integer_types, to_native +from werkzeug.posixemulation import rename + + +def _items(mappingorseq): + """Wrapper for efficient iteration over mappings represented by dicts + or sequences:: + + >>> for k, v in _items((i, i*i) for i in xrange(5)): + ... assert k*k == v + + >>> for k, v in _items(dict((i, i*i) for i in xrange(5))): + ... assert k*k == v + + """ + if hasattr(mappingorseq, 'items'): + return iteritems(mappingorseq) + return mappingorseq + + +class BaseCache(object): + + """Baseclass for the cache systems. All the cache systems implement this + API or a superset of it. + + :param default_timeout: the default timeout (in seconds) that is used if + no timeout is specified on :meth:`set`. A timeout + of 0 indicates that the cache never expires. + """ + + def __init__(self, default_timeout=300): + self.default_timeout = default_timeout + + def _normalize_timeout(self, timeout): + if timeout is None: + timeout = self.default_timeout + return timeout + + def get(self, key): + """Look up key in the cache and return the value for it. + + :param key: the key to be looked up. + :returns: The value if it exists and is readable, else ``None``. + """ + return None + + def delete(self, key): + """Delete `key` from the cache. + + :param key: the key to delete. + :returns: Whether the key existed and has been deleted. + :rtype: boolean + """ + return True + + def get_many(self, *keys): + """Returns a list of values for the given keys. + For each key an item in the list is created:: + + foo, bar = cache.get_many("foo", "bar") + + Has the same error handling as :meth:`get`. + + :param keys: The function accepts multiple keys as positional + arguments. + """ + return [self.get(k) for k in keys] + + def get_dict(self, *keys): + """Like :meth:`get_many` but return a dict:: + + d = cache.get_dict("foo", "bar") + foo = d["foo"] + bar = d["bar"] + + :param keys: The function accepts multiple keys as positional + arguments. + """ + return dict(zip(keys, self.get_many(*keys))) + + def set(self, key, value, timeout=None): + """Add a new key/value to the cache (overwrites value, if key already + exists in the cache). + + :param key: the key to set + :param value: the value for the key + :param timeout: the cache timeout for the key in seconds (if not + specified, it uses the default timeout). A timeout of + 0 idicates that the cache never expires. + :returns: ``True`` if key has been updated, ``False`` for backend + errors. Pickling errors, however, will raise a subclass of + ``pickle.PickleError``. + :rtype: boolean + """ + return True + + def add(self, key, value, timeout=None): + """Works like :meth:`set` but does not overwrite the values of already + existing keys. + + :param key: the key to set + :param value: the value for the key + :param timeout: the cache timeout for the key in seconds (if not + specified, it uses the default timeout). A timeout of + 0 idicates that the cache never expires. + :returns: Same as :meth:`set`, but also ``False`` for already + existing keys. + :rtype: boolean + """ + return True + + def set_many(self, mapping, timeout=None): + """Sets multiple keys and values from a mapping. + + :param mapping: a mapping with the keys/values to set. + :param timeout: the cache timeout for the key in seconds (if not + specified, it uses the default timeout). A timeout of + 0 idicates that the cache never expires. + :returns: Whether all given keys have been set. + :rtype: boolean + """ + rv = True + for key, value in _items(mapping): + if not self.set(key, value, timeout): + rv = False + return rv + + def delete_many(self, *keys): + """Deletes multiple keys at once. + + :param keys: The function accepts multiple keys as positional + arguments. + :returns: Whether all given keys have been deleted. + :rtype: boolean + """ + return all(self.delete(key) for key in keys) + + def has(self, key): + """Checks if a key exists in the cache without returning it. This is a + cheap operation that bypasses loading the actual data on the backend. + + This method is optional and may not be implemented on all caches. + + :param key: the key to check + """ + raise NotImplementedError( + '%s doesn\'t have an efficient implementation of `has`. That ' + 'means it is impossible to check whether a key exists without ' + 'fully loading the key\'s data. Consider using `self.get` ' + 'explicitly if you don\'t care about performance.' + ) + + def clear(self): + """Clears the cache. Keep in mind that not all caches support + completely clearing the cache. + + :returns: Whether the cache has been cleared. + :rtype: boolean + """ + return True + + def inc(self, key, delta=1): + """Increments the value of a key by `delta`. If the key does + not yet exist it is initialized with `delta`. + + For supporting caches this is an atomic operation. + + :param key: the key to increment. + :param delta: the delta to add. + :returns: The new value or ``None`` for backend errors. + """ + value = (self.get(key) or 0) + delta + return value if self.set(key, value) else None + + def dec(self, key, delta=1): + """Decrements the value of a key by `delta`. If the key does + not yet exist it is initialized with `-delta`. + + For supporting caches this is an atomic operation. + + :param key: the key to increment. + :param delta: the delta to subtract. + :returns: The new value or `None` for backend errors. + """ + value = (self.get(key) or 0) - delta + return value if self.set(key, value) else None + + +class NullCache(BaseCache): + + """A cache that doesn't cache. This can be useful for unit testing. + + :param default_timeout: a dummy parameter that is ignored but exists + for API compatibility with other caches. + """ + + def has(self, key): + return False + + +class SimpleCache(BaseCache): + + """Simple memory cache for single process environments. This class exists + mainly for the development server and is not 100% thread safe. It tries + to use as many atomic operations as possible and no locks for simplicity + but it could happen under heavy load that keys are added multiple times. + + :param threshold: the maximum number of items the cache stores before + it starts deleting some. + :param default_timeout: the default timeout that is used if no timeout is + specified on :meth:`~BaseCache.set`. A timeout of + 0 indicates that the cache never expires. + """ + + def __init__(self, threshold=500, default_timeout=300): + BaseCache.__init__(self, default_timeout) + self._cache = {} + self.clear = self._cache.clear + self._threshold = threshold + + def _prune(self): + if len(self._cache) > self._threshold: + now = time() + toremove = [] + for idx, (key, (expires, _)) in enumerate(self._cache.items()): + if (expires != 0 and expires <= now) or idx % 3 == 0: + toremove.append(key) + for key in toremove: + self._cache.pop(key, None) + + def _normalize_timeout(self, timeout): + timeout = BaseCache._normalize_timeout(self, timeout) + if timeout > 0: + timeout = time() + timeout + return timeout + + def get(self, key): + try: + expires, value = self._cache[key] + if expires == 0 or expires > time(): + return pickle.loads(value) + except (KeyError, pickle.PickleError): + return None + + def set(self, key, value, timeout=None): + expires = self._normalize_timeout(timeout) + self._prune() + self._cache[key] = (expires, pickle.dumps(value, + pickle.HIGHEST_PROTOCOL)) + return True + + def add(self, key, value, timeout=None): + expires = self._normalize_timeout(timeout) + self._prune() + item = (expires, pickle.dumps(value, + pickle.HIGHEST_PROTOCOL)) + if key in self._cache: + return False + self._cache.setdefault(key, item) + return True + + def delete(self, key): + return self._cache.pop(key, None) is not None + + def has(self, key): + try: + expires, value = self._cache[key] + return expires == 0 or expires > time() + except KeyError: + return False + +_test_memcached_key = re.compile(r'[^\x00-\x21\xff]{1,250}$').match + + +class MemcachedCache(BaseCache): + + """A cache that uses memcached as backend. + + The first argument can either be an object that resembles the API of a + :class:`memcache.Client` or a tuple/list of server addresses. In the + event that a tuple/list is passed, Werkzeug tries to import the best + available memcache library. + + This cache looks into the following packages/modules to find bindings for + memcached: + + - ``pylibmc`` + - ``google.appengine.api.memcached`` + - ``memcached`` + - ``libmc`` + + Implementation notes: This cache backend works around some limitations in + memcached to simplify the interface. For example unicode keys are encoded + to utf-8 on the fly. Methods such as :meth:`~BaseCache.get_dict` return + the keys in the same format as passed. Furthermore all get methods + silently ignore key errors to not cause problems when untrusted user data + is passed to the get methods which is often the case in web applications. + + :param servers: a list or tuple of server addresses or alternatively + a :class:`memcache.Client` or a compatible client. + :param default_timeout: the default timeout that is used if no timeout is + specified on :meth:`~BaseCache.set`. A timeout of + 0 indicates that the cache never expires. + :param key_prefix: a prefix that is added before all keys. This makes it + possible to use the same memcached server for different + applications. Keep in mind that + :meth:`~BaseCache.clear` will also clear keys with a + different prefix. + """ + + def __init__(self, servers=None, default_timeout=300, key_prefix=None): + BaseCache.__init__(self, default_timeout) + if servers is None or isinstance(servers, (list, tuple)): + if servers is None: + servers = ['127.0.0.1:11211'] + self._client = self.import_preferred_memcache_lib(servers) + if self._client is None: + raise RuntimeError('no memcache module found') + else: + # NOTE: servers is actually an already initialized memcache + # client. + self._client = servers + + self.key_prefix = to_native(key_prefix) + + def _normalize_key(self, key): + key = to_native(key, 'utf-8') + if self.key_prefix: + key = self.key_prefix + key + return key + + def _normalize_timeout(self, timeout): + timeout = BaseCache._normalize_timeout(self, timeout) + if timeout > 0: + timeout = int(time()) + timeout + return timeout + + def get(self, key): + key = self._normalize_key(key) + # memcached doesn't support keys longer than that. Because often + # checks for so long keys can occur because it's tested from user + # submitted data etc we fail silently for getting. + if _test_memcached_key(key): + return self._client.get(key) + + def get_dict(self, *keys): + key_mapping = {} + have_encoded_keys = False + for key in keys: + encoded_key = self._normalize_key(key) + if not isinstance(key, str): + have_encoded_keys = True + if _test_memcached_key(key): + key_mapping[encoded_key] = key + _keys = list(key_mapping) + d = rv = self._client.get_multi(_keys) + if have_encoded_keys or self.key_prefix: + rv = {} + for key, value in iteritems(d): + rv[key_mapping[key]] = value + if len(rv) < len(keys): + for key in keys: + if key not in rv: + rv[key] = None + return rv + + def add(self, key, value, timeout=None): + key = self._normalize_key(key) + timeout = self._normalize_timeout(timeout) + return self._client.add(key, value, timeout) + + def set(self, key, value, timeout=None): + key = self._normalize_key(key) + timeout = self._normalize_timeout(timeout) + return self._client.set(key, value, timeout) + + def get_many(self, *keys): + d = self.get_dict(*keys) + return [d[key] for key in keys] + + def set_many(self, mapping, timeout=None): + new_mapping = {} + for key, value in _items(mapping): + key = self._normalize_key(key) + new_mapping[key] = value + + timeout = self._normalize_timeout(timeout) + failed_keys = self._client.set_multi(new_mapping, timeout) + return not failed_keys + + def delete(self, key): + key = self._normalize_key(key) + if _test_memcached_key(key): + return self._client.delete(key) + + def delete_many(self, *keys): + new_keys = [] + for key in keys: + key = self._normalize_key(key) + if _test_memcached_key(key): + new_keys.append(key) + return self._client.delete_multi(new_keys) + + def has(self, key): + key = self._normalize_key(key) + if _test_memcached_key(key): + return self._client.append(key, '') + return False + + def clear(self): + return self._client.flush_all() + + def inc(self, key, delta=1): + key = self._normalize_key(key) + return self._client.incr(key, delta) + + def dec(self, key, delta=1): + key = self._normalize_key(key) + return self._client.decr(key, delta) + + def import_preferred_memcache_lib(self, servers): + """Returns an initialized memcache client. Used by the constructor.""" + try: + import pylibmc + except ImportError: + pass + else: + return pylibmc.Client(servers) + + try: + from google.appengine.api import memcache + except ImportError: + pass + else: + return memcache.Client() + + try: + import memcache + except ImportError: + pass + else: + return memcache.Client(servers) + + try: + import libmc + except ImportError: + pass + else: + return libmc.Client(servers) + + +# backwards compatibility +GAEMemcachedCache = MemcachedCache + + +class RedisCache(BaseCache): + + """Uses the Redis key-value store as a cache backend. + + The first argument can be either a string denoting address of the Redis + server or an object resembling an instance of a redis.Redis class. + + Note: Python Redis API already takes care of encoding unicode strings on + the fly. + + .. versionadded:: 0.7 + + .. versionadded:: 0.8 + `key_prefix` was added. + + .. versionchanged:: 0.8 + This cache backend now properly serializes objects. + + .. versionchanged:: 0.8.3 + This cache backend now supports password authentication. + + .. versionchanged:: 0.10 + ``**kwargs`` is now passed to the redis object. + + :param host: address of the Redis server or an object which API is + compatible with the official Python Redis client (redis-py). + :param port: port number on which Redis server listens for connections. + :param password: password authentication for the Redis server. + :param db: db (zero-based numeric index) on Redis Server to connect. + :param default_timeout: the default timeout that is used if no timeout is + specified on :meth:`~BaseCache.set`. A timeout of + 0 indicates that the cache never expires. + :param key_prefix: A prefix that should be added to all keys. + + Any additional keyword arguments will be passed to ``redis.Redis``. + """ + + def __init__(self, host='localhost', port=6379, password=None, + db=0, default_timeout=300, key_prefix=None, **kwargs): + BaseCache.__init__(self, default_timeout) + if host is None: + raise ValueError('RedisCache host parameter may not be None') + if isinstance(host, string_types): + try: + import redis + except ImportError: + raise RuntimeError('no redis module found') + if kwargs.get('decode_responses', None): + raise ValueError('decode_responses is not supported by ' + 'RedisCache.') + self._client = redis.Redis(host=host, port=port, password=password, + db=db, **kwargs) + else: + self._client = host + self.key_prefix = key_prefix or '' + + def _normalize_timeout(self, timeout): + timeout = BaseCache._normalize_timeout(self, timeout) + if timeout == 0: + timeout = -1 + return timeout + + def dump_object(self, value): + """Dumps an object into a string for redis. By default it serializes + integers as regular string and pickle dumps everything else. + """ + t = type(value) + if t in integer_types: + return str(value).encode('ascii') + return b'!' + pickle.dumps(value) + + def load_object(self, value): + """The reversal of :meth:`dump_object`. This might be called with + None. + """ + if value is None: + return None + if value.startswith(b'!'): + try: + return pickle.loads(value[1:]) + except pickle.PickleError: + return None + try: + return int(value) + except ValueError: + # before 0.8 we did not have serialization. Still support that. + return value + + def get(self, key): + return self.load_object(self._client.get(self.key_prefix + key)) + + def get_many(self, *keys): + if self.key_prefix: + keys = [self.key_prefix + key for key in keys] + return [self.load_object(x) for x in self._client.mget(keys)] + + def set(self, key, value, timeout=None): + timeout = self._normalize_timeout(timeout) + dump = self.dump_object(value) + if timeout == -1: + result = self._client.set(name=self.key_prefix + key, + value=dump) + else: + result = self._client.setex(name=self.key_prefix + key, + value=dump, time=timeout) + return result + + def add(self, key, value, timeout=None): + timeout = self._normalize_timeout(timeout) + dump = self.dump_object(value) + return ( + self._client.setnx(name=self.key_prefix + key, value=dump) and + self._client.expire(name=self.key_prefix + key, time=timeout) + ) + + def set_many(self, mapping, timeout=None): + timeout = self._normalize_timeout(timeout) + # Use transaction=False to batch without calling redis MULTI + # which is not supported by twemproxy + pipe = self._client.pipeline(transaction=False) + + for key, value in _items(mapping): + dump = self.dump_object(value) + if timeout == -1: + pipe.set(name=self.key_prefix + key, value=dump) + else: + pipe.setex(name=self.key_prefix + key, value=dump, + time=timeout) + return pipe.execute() + + def delete(self, key): + return self._client.delete(self.key_prefix + key) + + def delete_many(self, *keys): + if not keys: + return + if self.key_prefix: + keys = [self.key_prefix + key for key in keys] + return self._client.delete(*keys) + + def has(self, key): + return self._client.exists(self.key_prefix + key) + + def clear(self): + status = False + if self.key_prefix: + keys = self._client.keys(self.key_prefix + '*') + if keys: + status = self._client.delete(*keys) + else: + status = self._client.flushdb() + return status + + def inc(self, key, delta=1): + return self._client.incr(name=self.key_prefix + key, amount=delta) + + def dec(self, key, delta=1): + return self._client.decr(name=self.key_prefix + key, amount=delta) + + +class FileSystemCache(BaseCache): + + """A cache that stores the items on the file system. This cache depends + on being the only user of the `cache_dir`. Make absolutely sure that + nobody but this cache stores files there or otherwise the cache will + randomly delete files therein. + + :param cache_dir: the directory where cache files are stored. + :param threshold: the maximum number of items the cache stores before + it starts deleting some. A threshold value of 0 + indicates no threshold. + :param default_timeout: the default timeout that is used if no timeout is + specified on :meth:`~BaseCache.set`. A timeout of + 0 indicates that the cache never expires. + :param mode: the file mode wanted for the cache files, default 0600 + """ + + #: used for temporary files by the FileSystemCache + _fs_transaction_suffix = '.__wz_cache' + #: keep amount of files in a cache element + _fs_count_file = '__wz_cache_count' + + def __init__(self, cache_dir, threshold=500, default_timeout=300, + mode=0o600): + BaseCache.__init__(self, default_timeout) + self._path = cache_dir + self._threshold = threshold + self._mode = mode + + try: + os.makedirs(self._path) + except OSError as ex: + if ex.errno != errno.EEXIST: + raise + + self._update_count(value=len(self._list_dir())) + + @property + def _file_count(self): + return self.get(self._fs_count_file) or 0 + + def _update_count(self, delta=None, value=None): + # If we have no threshold, don't count files + if self._threshold == 0: + return + + if delta: + new_count = self._file_count + delta + else: + new_count = value or 0 + self.set(self._fs_count_file, new_count, mgmt_element=True) + + def _normalize_timeout(self, timeout): + timeout = BaseCache._normalize_timeout(self, timeout) + if timeout != 0: + timeout = time() + timeout + return int(timeout) + + def _list_dir(self): + """return a list of (fully qualified) cache filenames + """ + mgmt_files = [self._get_filename(name).split('/')[-1] + for name in (self._fs_count_file,)] + return [os.path.join(self._path, fn) for fn in os.listdir(self._path) + if not fn.endswith(self._fs_transaction_suffix) + and fn not in mgmt_files] + + def _prune(self): + if self._threshold == 0 or not self._file_count > self._threshold: + return + + entries = self._list_dir() + now = time() + for idx, fname in enumerate(entries): + try: + remove = False + with open(fname, 'rb') as f: + expires = pickle.load(f) + remove = (expires != 0 and expires <= now) or idx % 3 == 0 + + if remove: + os.remove(fname) + except (IOError, OSError): + pass + self._update_count(value=len(self._list_dir())) + + def clear(self): + for fname in self._list_dir(): + try: + os.remove(fname) + except (IOError, OSError): + self._update_count(value=len(self._list_dir())) + return False + self._update_count(value=0) + return True + + def _get_filename(self, key): + if isinstance(key, text_type): + key = key.encode('utf-8') # XXX unicode review + hash = md5(key).hexdigest() + return os.path.join(self._path, hash) + + def get(self, key): + filename = self._get_filename(key) + try: + with open(filename, 'rb') as f: + pickle_time = pickle.load(f) + if pickle_time == 0 or pickle_time >= time(): + return pickle.load(f) + else: + os.remove(filename) + return None + except (IOError, OSError, pickle.PickleError): + return None + + def add(self, key, value, timeout=None): + filename = self._get_filename(key) + if not os.path.exists(filename): + return self.set(key, value, timeout) + return False + + def set(self, key, value, timeout=None, mgmt_element=False): + # Management elements have no timeout + if mgmt_element: + timeout = 0 + + # Don't prune on management element update, to avoid loop + else: + self._prune() + + timeout = self._normalize_timeout(timeout) + filename = self._get_filename(key) + try: + fd, tmp = tempfile.mkstemp(suffix=self._fs_transaction_suffix, + dir=self._path) + with os.fdopen(fd, 'wb') as f: + pickle.dump(timeout, f, 1) + pickle.dump(value, f, pickle.HIGHEST_PROTOCOL) + rename(tmp, filename) + os.chmod(filename, self._mode) + except (IOError, OSError): + return False + else: + # Management elements should not count towards threshold + if not mgmt_element: + self._update_count(delta=1) + return True + + def delete(self, key, mgmt_element=False): + try: + os.remove(self._get_filename(key)) + except (IOError, OSError): + return False + else: + # Management elements should not count towards threshold + if not mgmt_element: + self._update_count(delta=-1) + return True + + def has(self, key): + filename = self._get_filename(key) + try: + with open(filename, 'rb') as f: + pickle_time = pickle.load(f) + if pickle_time == 0 or pickle_time >= time(): + return True + else: + os.remove(filename) + return False + except (IOError, OSError, pickle.PickleError): + return False + + +class UWSGICache(BaseCache): + """ Implements the cache using uWSGI's caching framework. + + .. note:: + This class cannot be used when running under PyPy, because the uWSGI + API implementation for PyPy is lacking the needed functionality. + + :param default_timeout: The default timeout in seconds. + :param cache: The name of the caching instance to connect to, for + example: mycache@localhost:3031, defaults to an empty string, which + means uWSGI will cache in the local instance. If the cache is in the + same instance as the werkzeug app, you only have to provide the name of + the cache. + """ + def __init__(self, default_timeout=300, cache=''): + BaseCache.__init__(self, default_timeout) + + if platform.python_implementation() == 'PyPy': + raise RuntimeError("uWSGI caching does not work under PyPy, see " + "the docs for more details.") + + try: + import uwsgi + self._uwsgi = uwsgi + except ImportError: + raise RuntimeError("uWSGI could not be imported, are you " + "running under uWSGI?") + + self.cache = cache + + def get(self, key): + rv = self._uwsgi.cache_get(key, self.cache) + if rv is None: + return + return pickle.loads(rv) + + def delete(self, key): + return self._uwsgi.cache_del(key, self.cache) + + def set(self, key, value, timeout=None): + return self._uwsgi.cache_update(key, pickle.dumps(value), + self._normalize_timeout(timeout), + self.cache) + + def add(self, key, value, timeout=None): + return self._uwsgi.cache_set(key, pickle.dumps(value), + self._normalize_timeout(timeout), + self.cache) + + def clear(self): + return self._uwsgi.cache_clear(self.cache) + + def has(self, key): + return self._uwsgi.cache_exists(key, self.cache) is not None diff --git a/venv/Lib/site-packages/werkzeug/contrib/fixers.py b/venv/Lib/site-packages/werkzeug/contrib/fixers.py new file mode 100644 index 0000000..b88a861 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/fixers.py @@ -0,0 +1,254 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.contrib.fixers + ~~~~~~~~~~~~~~~~~~~~~~~ + + .. versionadded:: 0.5 + + This module includes various helpers that fix bugs in web servers. They may + be necessary for some versions of a buggy web server but not others. We try + to stay updated with the status of the bugs as good as possible but you have + to make sure whether they fix the problem you encounter. + + If you notice bugs in webservers not fixed in this module consider + contributing a patch. + + :copyright: Copyright 2009 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +try: + from urllib import unquote +except ImportError: + from urllib.parse import unquote + +from werkzeug.http import parse_options_header, parse_cache_control_header, \ + parse_set_header +from werkzeug.useragents import UserAgent +from werkzeug.datastructures import Headers, ResponseCacheControl + + +class CGIRootFix(object): + + """Wrap the application in this middleware if you are using FastCGI or CGI + and you have problems with your app root being set to the cgi script's path + instead of the path users are going to visit + + .. versionchanged:: 0.9 + Added `app_root` parameter and renamed from `LighttpdCGIRootFix`. + + :param app: the WSGI application + :param app_root: Defaulting to ``'/'``, you can set this to something else + if your app is mounted somewhere else. + """ + + def __init__(self, app, app_root='/'): + self.app = app + self.app_root = app_root + + def __call__(self, environ, start_response): + # only set PATH_INFO for older versions of Lighty or if no + # server software is provided. That's because the test was + # added in newer Werkzeug versions and we don't want to break + # people's code if they are using this fixer in a test that + # does not set the SERVER_SOFTWARE key. + if 'SERVER_SOFTWARE' not in environ or \ + environ['SERVER_SOFTWARE'] < 'lighttpd/1.4.28': + environ['PATH_INFO'] = environ.get('SCRIPT_NAME', '') + \ + environ.get('PATH_INFO', '') + environ['SCRIPT_NAME'] = self.app_root.strip('/') + return self.app(environ, start_response) + +# backwards compatibility +LighttpdCGIRootFix = CGIRootFix + + +class PathInfoFromRequestUriFix(object): + + """On windows environment variables are limited to the system charset + which makes it impossible to store the `PATH_INFO` variable in the + environment without loss of information on some systems. + + This is for example a problem for CGI scripts on a Windows Apache. + + This fixer works by recreating the `PATH_INFO` from `REQUEST_URI`, + `REQUEST_URL`, or `UNENCODED_URL` (whatever is available). Thus the + fix can only be applied if the webserver supports either of these + variables. + + :param app: the WSGI application + """ + + def __init__(self, app): + self.app = app + + def __call__(self, environ, start_response): + for key in 'REQUEST_URL', 'REQUEST_URI', 'UNENCODED_URL': + if key not in environ: + continue + request_uri = unquote(environ[key]) + script_name = unquote(environ.get('SCRIPT_NAME', '')) + if request_uri.startswith(script_name): + environ['PATH_INFO'] = request_uri[len(script_name):] \ + .split('?', 1)[0] + break + return self.app(environ, start_response) + + +class ProxyFix(object): + + """This middleware can be applied to add HTTP proxy support to an + application that was not designed with HTTP proxies in mind. It + sets `REMOTE_ADDR`, `HTTP_HOST` from `X-Forwarded` headers. While + Werkzeug-based applications already can use + :py:func:`werkzeug.wsgi.get_host` to retrieve the current host even if + behind proxy setups, this middleware can be used for applications which + access the WSGI environment directly. + + If you have more than one proxy server in front of your app, set + `num_proxies` accordingly. + + Do not use this middleware in non-proxy setups for security reasons. + + The original values of `REMOTE_ADDR` and `HTTP_HOST` are stored in + the WSGI environment as `werkzeug.proxy_fix.orig_remote_addr` and + `werkzeug.proxy_fix.orig_http_host`. + + :param app: the WSGI application + :param num_proxies: the number of proxy servers in front of the app. + """ + + def __init__(self, app, num_proxies=1): + self.app = app + self.num_proxies = num_proxies + + def get_remote_addr(self, forwarded_for): + """Selects the new remote addr from the given list of ips in + X-Forwarded-For. By default it picks the one that the `num_proxies` + proxy server provides. Before 0.9 it would always pick the first. + + .. versionadded:: 0.8 + """ + if len(forwarded_for) >= self.num_proxies: + return forwarded_for[-self.num_proxies] + + def __call__(self, environ, start_response): + getter = environ.get + forwarded_proto = getter('HTTP_X_FORWARDED_PROTO', '') + forwarded_for = getter('HTTP_X_FORWARDED_FOR', '').split(',') + forwarded_host = getter('HTTP_X_FORWARDED_HOST', '') + environ.update({ + 'werkzeug.proxy_fix.orig_wsgi_url_scheme': getter('wsgi.url_scheme'), + 'werkzeug.proxy_fix.orig_remote_addr': getter('REMOTE_ADDR'), + 'werkzeug.proxy_fix.orig_http_host': getter('HTTP_HOST') + }) + forwarded_for = [x for x in [x.strip() for x in forwarded_for] if x] + remote_addr = self.get_remote_addr(forwarded_for) + if remote_addr is not None: + environ['REMOTE_ADDR'] = remote_addr + if forwarded_host: + environ['HTTP_HOST'] = forwarded_host + if forwarded_proto: + environ['wsgi.url_scheme'] = forwarded_proto + return self.app(environ, start_response) + + +class HeaderRewriterFix(object): + + """This middleware can remove response headers and add others. This + is for example useful to remove the `Date` header from responses if you + are using a server that adds that header, no matter if it's present or + not or to add `X-Powered-By` headers:: + + app = HeaderRewriterFix(app, remove_headers=['Date'], + add_headers=[('X-Powered-By', 'WSGI')]) + + :param app: the WSGI application + :param remove_headers: a sequence of header keys that should be + removed. + :param add_headers: a sequence of ``(key, value)`` tuples that should + be added. + """ + + def __init__(self, app, remove_headers=None, add_headers=None): + self.app = app + self.remove_headers = set(x.lower() for x in (remove_headers or ())) + self.add_headers = list(add_headers or ()) + + def __call__(self, environ, start_response): + def rewriting_start_response(status, headers, exc_info=None): + new_headers = [] + for key, value in headers: + if key.lower() not in self.remove_headers: + new_headers.append((key, value)) + new_headers += self.add_headers + return start_response(status, new_headers, exc_info) + return self.app(environ, rewriting_start_response) + + +class InternetExplorerFix(object): + + """This middleware fixes a couple of bugs with Microsoft Internet + Explorer. Currently the following fixes are applied: + + - removing of `Vary` headers for unsupported mimetypes which + causes troubles with caching. Can be disabled by passing + ``fix_vary=False`` to the constructor. + see: http://support.microsoft.com/kb/824847/en-us + + - removes offending headers to work around caching bugs in + Internet Explorer if `Content-Disposition` is set. Can be + disabled by passing ``fix_attach=False`` to the constructor. + + If it does not detect affected Internet Explorer versions it won't touch + the request / response. + """ + + # This code was inspired by Django fixers for the same bugs. The + # fix_vary and fix_attach fixers were originally implemented in Django + # by Michael Axiak and is available as part of the Django project: + # http://code.djangoproject.com/ticket/4148 + + def __init__(self, app, fix_vary=True, fix_attach=True): + self.app = app + self.fix_vary = fix_vary + self.fix_attach = fix_attach + + def fix_headers(self, environ, headers, status=None): + if self.fix_vary: + header = headers.get('content-type', '') + mimetype, options = parse_options_header(header) + if mimetype not in ('text/html', 'text/plain', 'text/sgml'): + headers.pop('vary', None) + + if self.fix_attach and 'content-disposition' in headers: + pragma = parse_set_header(headers.get('pragma', '')) + pragma.discard('no-cache') + header = pragma.to_header() + if not header: + headers.pop('pragma', '') + else: + headers['Pragma'] = header + header = headers.get('cache-control', '') + if header: + cc = parse_cache_control_header(header, + cls=ResponseCacheControl) + cc.no_cache = None + cc.no_store = False + header = cc.to_header() + if not header: + headers.pop('cache-control', '') + else: + headers['Cache-Control'] = header + + def run_fixed(self, environ, start_response): + def fixing_start_response(status, headers, exc_info=None): + headers = Headers(headers) + self.fix_headers(environ, headers, status) + return start_response(status, headers.to_wsgi_list(), exc_info) + return self.app(environ, fixing_start_response) + + def __call__(self, environ, start_response): + ua = UserAgent(environ) + if ua.browser != 'msie': + return self.app(environ, start_response) + return self.run_fixed(environ, start_response) diff --git a/venv/Lib/site-packages/werkzeug/contrib/iterio.py b/venv/Lib/site-packages/werkzeug/contrib/iterio.py new file mode 100644 index 0000000..c0ced37 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/iterio.py @@ -0,0 +1,352 @@ +# -*- coding: utf-8 -*- +r""" + werkzeug.contrib.iterio + ~~~~~~~~~~~~~~~~~~~~~~~ + + This module implements a :class:`IterIO` that converts an iterator into + a stream object and the other way round. Converting streams into + iterators requires the `greenlet`_ module. + + To convert an iterator into a stream all you have to do is to pass it + directly to the :class:`IterIO` constructor. In this example we pass it + a newly created generator:: + + def foo(): + yield "something\n" + yield "otherthings" + stream = IterIO(foo()) + print stream.read() # read the whole iterator + + The other way round works a bit different because we have to ensure that + the code execution doesn't take place yet. An :class:`IterIO` call with a + callable as first argument does two things. The function itself is passed + an :class:`IterIO` stream it can feed. The object returned by the + :class:`IterIO` constructor on the other hand is not an stream object but + an iterator:: + + def foo(stream): + stream.write("some") + stream.write("thing") + stream.flush() + stream.write("otherthing") + iterator = IterIO(foo) + print iterator.next() # prints something + print iterator.next() # prints otherthing + iterator.next() # raises StopIteration + + .. _greenlet: https://github.com/python-greenlet/greenlet + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +try: + import greenlet +except ImportError: + greenlet = None + +from werkzeug._compat import implements_iterator + + +def _mixed_join(iterable, sentinel): + """concatenate any string type in an intelligent way.""" + iterator = iter(iterable) + first_item = next(iterator, sentinel) + if isinstance(first_item, bytes): + return first_item + b''.join(iterator) + return first_item + u''.join(iterator) + + +def _newline(reference_string): + if isinstance(reference_string, bytes): + return b'\n' + return u'\n' + + +@implements_iterator +class IterIO(object): + + """Instances of this object implement an interface compatible with the + standard Python :class:`file` object. Streams are either read-only or + write-only depending on how the object is created. + + If the first argument is an iterable a file like object is returned that + returns the contents of the iterable. In case the iterable is empty + read operations will return the sentinel value. + + If the first argument is a callable then the stream object will be + created and passed to that function. The caller itself however will + not receive a stream but an iterable. The function will be be executed + step by step as something iterates over the returned iterable. Each + call to :meth:`flush` will create an item for the iterable. If + :meth:`flush` is called without any writes in-between the sentinel + value will be yielded. + + Note for Python 3: due to the incompatible interface of bytes and + streams you should set the sentinel value explicitly to an empty + bytestring (``b''``) if you are expecting to deal with bytes as + otherwise the end of the stream is marked with the wrong sentinel + value. + + .. versionadded:: 0.9 + `sentinel` parameter was added. + """ + + def __new__(cls, obj, sentinel=''): + try: + iterator = iter(obj) + except TypeError: + return IterI(obj, sentinel) + return IterO(iterator, sentinel) + + def __iter__(self): + return self + + def tell(self): + if self.closed: + raise ValueError('I/O operation on closed file') + return self.pos + + def isatty(self): + if self.closed: + raise ValueError('I/O operation on closed file') + return False + + def seek(self, pos, mode=0): + if self.closed: + raise ValueError('I/O operation on closed file') + raise IOError(9, 'Bad file descriptor') + + def truncate(self, size=None): + if self.closed: + raise ValueError('I/O operation on closed file') + raise IOError(9, 'Bad file descriptor') + + def write(self, s): + if self.closed: + raise ValueError('I/O operation on closed file') + raise IOError(9, 'Bad file descriptor') + + def writelines(self, list): + if self.closed: + raise ValueError('I/O operation on closed file') + raise IOError(9, 'Bad file descriptor') + + def read(self, n=-1): + if self.closed: + raise ValueError('I/O operation on closed file') + raise IOError(9, 'Bad file descriptor') + + def readlines(self, sizehint=0): + if self.closed: + raise ValueError('I/O operation on closed file') + raise IOError(9, 'Bad file descriptor') + + def readline(self, length=None): + if self.closed: + raise ValueError('I/O operation on closed file') + raise IOError(9, 'Bad file descriptor') + + def flush(self): + if self.closed: + raise ValueError('I/O operation on closed file') + raise IOError(9, 'Bad file descriptor') + + def __next__(self): + if self.closed: + raise StopIteration() + line = self.readline() + if not line: + raise StopIteration() + return line + + +class IterI(IterIO): + + """Convert an stream into an iterator.""" + + def __new__(cls, func, sentinel=''): + if greenlet is None: + raise RuntimeError('IterI requires greenlet support') + stream = object.__new__(cls) + stream._parent = greenlet.getcurrent() + stream._buffer = [] + stream.closed = False + stream.sentinel = sentinel + stream.pos = 0 + + def run(): + func(stream) + stream.close() + + g = greenlet.greenlet(run, stream._parent) + while 1: + rv = g.switch() + if not rv: + return + yield rv[0] + + def close(self): + if not self.closed: + self.closed = True + self._flush_impl() + + def write(self, s): + if self.closed: + raise ValueError('I/O operation on closed file') + if s: + self.pos += len(s) + self._buffer.append(s) + + def writelines(self, list): + for item in list: + self.write(item) + + def flush(self): + if self.closed: + raise ValueError('I/O operation on closed file') + self._flush_impl() + + def _flush_impl(self): + data = _mixed_join(self._buffer, self.sentinel) + self._buffer = [] + if not data and self.closed: + self._parent.switch() + else: + self._parent.switch((data,)) + + +class IterO(IterIO): + + """Iter output. Wrap an iterator and give it a stream like interface.""" + + def __new__(cls, gen, sentinel=''): + self = object.__new__(cls) + self._gen = gen + self._buf = None + self.sentinel = sentinel + self.closed = False + self.pos = 0 + return self + + def __iter__(self): + return self + + def _buf_append(self, string): + '''Replace string directly without appending to an empty string, + avoiding type issues.''' + if not self._buf: + self._buf = string + else: + self._buf += string + + def close(self): + if not self.closed: + self.closed = True + if hasattr(self._gen, 'close'): + self._gen.close() + + def seek(self, pos, mode=0): + if self.closed: + raise ValueError('I/O operation on closed file') + if mode == 1: + pos += self.pos + elif mode == 2: + self.read() + self.pos = min(self.pos, self.pos + pos) + return + elif mode != 0: + raise IOError('Invalid argument') + buf = [] + try: + tmp_end_pos = len(self._buf) + while pos > tmp_end_pos: + item = next(self._gen) + tmp_end_pos += len(item) + buf.append(item) + except StopIteration: + pass + if buf: + self._buf_append(_mixed_join(buf, self.sentinel)) + self.pos = max(0, pos) + + def read(self, n=-1): + if self.closed: + raise ValueError('I/O operation on closed file') + if n < 0: + self._buf_append(_mixed_join(self._gen, self.sentinel)) + result = self._buf[self.pos:] + self.pos += len(result) + return result + new_pos = self.pos + n + buf = [] + try: + tmp_end_pos = 0 if self._buf is None else len(self._buf) + while new_pos > tmp_end_pos or (self._buf is None and not buf): + item = next(self._gen) + tmp_end_pos += len(item) + buf.append(item) + except StopIteration: + pass + if buf: + self._buf_append(_mixed_join(buf, self.sentinel)) + + if self._buf is None: + return self.sentinel + + new_pos = max(0, new_pos) + try: + return self._buf[self.pos:new_pos] + finally: + self.pos = min(new_pos, len(self._buf)) + + def readline(self, length=None): + if self.closed: + raise ValueError('I/O operation on closed file') + + nl_pos = -1 + if self._buf: + nl_pos = self._buf.find(_newline(self._buf), self.pos) + buf = [] + try: + if self._buf is None: + pos = self.pos + else: + pos = len(self._buf) + while nl_pos < 0: + item = next(self._gen) + local_pos = item.find(_newline(item)) + buf.append(item) + if local_pos >= 0: + nl_pos = pos + local_pos + break + pos += len(item) + except StopIteration: + pass + if buf: + self._buf_append(_mixed_join(buf, self.sentinel)) + + if self._buf is None: + return self.sentinel + + if nl_pos < 0: + new_pos = len(self._buf) + else: + new_pos = nl_pos + 1 + if length is not None and self.pos + length < new_pos: + new_pos = self.pos + length + try: + return self._buf[self.pos:new_pos] + finally: + self.pos = min(new_pos, len(self._buf)) + + def readlines(self, sizehint=0): + total = 0 + lines = [] + line = self.readline() + while line: + lines.append(line) + total += len(line) + if 0 < sizehint <= total: + break + line = self.readline() + return lines diff --git a/venv/Lib/site-packages/werkzeug/contrib/jsrouting.py b/venv/Lib/site-packages/werkzeug/contrib/jsrouting.py new file mode 100644 index 0000000..d815892 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/jsrouting.py @@ -0,0 +1,264 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.contrib.jsrouting + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Addon module that allows to create a JavaScript function from a map + that generates rules. + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +try: + from simplejson import dumps +except ImportError: + try: + from json import dumps + except ImportError: + def dumps(*args): + raise RuntimeError('simplejson required for jsrouting') + +from inspect import getmro +from werkzeug.routing import NumberConverter +from werkzeug._compat import iteritems + + +def render_template(name_parts, rules, converters): + result = u'' + if name_parts: + for idx in range(0, len(name_parts) - 1): + name = u'.'.join(name_parts[:idx + 1]) + result += u"if (typeof %s === 'undefined') %s = {}\n" % (name, name) + result += '%s = ' % '.'.join(name_parts) + result += """(function (server_name, script_name, subdomain, url_scheme) { + var converters = [%(converters)s]; + var rules = %(rules)s; + function in_array(array, value) { + if (array.indexOf != undefined) { + return array.indexOf(value) != -1; + } + for (var i = 0; i < array.length; i++) { + if (array[i] == value) { + return true; + } + } + return false; + } + function array_diff(array1, array2) { + array1 = array1.slice(); + for (var i = array1.length-1; i >= 0; i--) { + if (in_array(array2, array1[i])) { + array1.splice(i, 1); + } + } + return array1; + } + function split_obj(obj) { + var names = []; + var values = []; + for (var name in obj) { + if (typeof(obj[name]) != 'function') { + names.push(name); + values.push(obj[name]); + } + } + return {names: names, values: values, original: obj}; + } + function suitable(rule, args) { + var default_args = split_obj(rule.defaults || {}); + var diff_arg_names = array_diff(rule.arguments, default_args.names); + + for (var i = 0; i < diff_arg_names.length; i++) { + if (!in_array(args.names, diff_arg_names[i])) { + return false; + } + } + + if (array_diff(rule.arguments, args.names).length == 0) { + if (rule.defaults == null) { + return true; + } + for (var i = 0; i < default_args.names.length; i++) { + var key = default_args.names[i]; + var value = default_args.values[i]; + if (value != args.original[key]) { + return false; + } + } + } + + return true; + } + function build(rule, args) { + var tmp = []; + var processed = rule.arguments.slice(); + for (var i = 0; i < rule.trace.length; i++) { + var part = rule.trace[i]; + if (part.is_dynamic) { + var converter = converters[rule.converters[part.data]]; + var data = converter(args.original[part.data]); + if (data == null) { + return null; + } + tmp.push(data); + processed.push(part.name); + } else { + tmp.push(part.data); + } + } + tmp = tmp.join(''); + var pipe = tmp.indexOf('|'); + var subdomain = tmp.substring(0, pipe); + var url = tmp.substring(pipe+1); + + var unprocessed = array_diff(args.names, processed); + var first_query_var = true; + for (var i = 0; i < unprocessed.length; i++) { + if (first_query_var) { + url += '?'; + } else { + url += '&'; + } + first_query_var = false; + url += encodeURIComponent(unprocessed[i]); + url += '='; + url += encodeURIComponent(args.original[unprocessed[i]]); + } + return {subdomain: subdomain, path: url}; + } + function lstrip(s, c) { + while (s && s.substring(0, 1) == c) { + s = s.substring(1); + } + return s; + } + function rstrip(s, c) { + while (s && s.substring(s.length-1, s.length) == c) { + s = s.substring(0, s.length-1); + } + return s; + } + return function(endpoint, args, force_external) { + args = split_obj(args); + var rv = null; + for (var i = 0; i < rules.length; i++) { + var rule = rules[i]; + if (rule.endpoint != endpoint) continue; + if (suitable(rule, args)) { + rv = build(rule, args); + if (rv != null) { + break; + } + } + } + if (rv == null) { + return null; + } + if (!force_external && rv.subdomain == subdomain) { + return rstrip(script_name, '/') + '/' + lstrip(rv.path, '/'); + } else { + return url_scheme + '://' + + (rv.subdomain ? rv.subdomain + '.' : '') + + server_name + rstrip(script_name, '/') + + '/' + lstrip(rv.path, '/'); + } + }; +})""" % {'converters': u', '.join(converters), + 'rules': rules} + + return result + + +def generate_map(map, name='url_map'): + """ + Generates a JavaScript function containing the rules defined in + this map, to be used with a MapAdapter's generate_javascript + method. If you don't pass a name the returned JavaScript code is + an expression that returns a function. Otherwise it's a standalone + script that assigns the function with that name. Dotted names are + resolved (so you an use a name like 'obj.url_for') + + In order to use JavaScript generation, simplejson must be installed. + + Note that using this feature will expose the rules + defined in your map to users. If your rules contain sensitive + information, don't use JavaScript generation! + """ + from warnings import warn + warn(DeprecationWarning('This module is deprecated')) + map.update() + rules = [] + converters = [] + for rule in map.iter_rules(): + trace = [{ + 'is_dynamic': is_dynamic, + 'data': data + } for is_dynamic, data in rule._trace] + rule_converters = {} + for key, converter in iteritems(rule._converters): + js_func = js_to_url_function(converter) + try: + index = converters.index(js_func) + except ValueError: + converters.append(js_func) + index = len(converters) - 1 + rule_converters[key] = index + rules.append({ + u'endpoint': rule.endpoint, + u'arguments': list(rule.arguments), + u'converters': rule_converters, + u'trace': trace, + u'defaults': rule.defaults + }) + + return render_template(name_parts=name and name.split('.') or [], + rules=dumps(rules), + converters=converters) + + +def generate_adapter(adapter, name='url_for', map_name='url_map'): + """Generates the url building function for a map.""" + values = { + u'server_name': dumps(adapter.server_name), + u'script_name': dumps(adapter.script_name), + u'subdomain': dumps(adapter.subdomain), + u'url_scheme': dumps(adapter.url_scheme), + u'name': name, + u'map_name': map_name + } + return u'''\ +var %(name)s = %(map_name)s( + %(server_name)s, + %(script_name)s, + %(subdomain)s, + %(url_scheme)s +);''' % values + + +def js_to_url_function(converter): + """Get the JavaScript converter function from a rule.""" + if hasattr(converter, 'js_to_url_function'): + data = converter.js_to_url_function() + else: + for cls in getmro(type(converter)): + if cls in js_to_url_functions: + data = js_to_url_functions[cls](converter) + break + else: + return 'encodeURIComponent' + return '(function(value) { %s })' % data + + +def NumberConverter_js_to_url(conv): + if conv.fixed_digits: + return u'''\ +var result = value.toString(); +while (result.length < %s) + result = '0' + result; +return result;''' % conv.fixed_digits + return u'return value.toString();' + + +js_to_url_functions = { + NumberConverter: NumberConverter_js_to_url +} diff --git a/venv/Lib/site-packages/werkzeug/contrib/limiter.py b/venv/Lib/site-packages/werkzeug/contrib/limiter.py new file mode 100644 index 0000000..6bc9d39 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/limiter.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.contrib.limiter + ~~~~~~~~~~~~~~~~~~~~~~~~ + + A middleware that limits incoming data. This works around problems with + Trac_ or Django_ because those directly stream into the memory. + + .. _Trac: http://trac.edgewall.org/ + .. _Django: http://www.djangoproject.com/ + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +from warnings import warn + +from werkzeug.wsgi import LimitedStream + + +class StreamLimitMiddleware(object): + + """Limits the input stream to a given number of bytes. This is useful if + you have a WSGI application that reads form data into memory (django for + example) and you don't want users to harm the server by uploading tons of + data. + + Default is 10MB + + .. versionchanged:: 0.9 + Deprecated middleware. + """ + + def __init__(self, app, maximum_size=1024 * 1024 * 10): + warn(DeprecationWarning('This middleware is deprecated')) + self.app = app + self.maximum_size = maximum_size + + def __call__(self, environ, start_response): + limit = min(self.maximum_size, int(environ.get('CONTENT_LENGTH') or 0)) + environ['wsgi.input'] = LimitedStream(environ['wsgi.input'], limit) + return self.app(environ, start_response) diff --git a/venv/Lib/site-packages/werkzeug/contrib/lint.py b/venv/Lib/site-packages/werkzeug/contrib/lint.py new file mode 100644 index 0000000..969c687 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/lint.py @@ -0,0 +1,343 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.contrib.lint + ~~~~~~~~~~~~~~~~~~~~~ + + .. versionadded:: 0.5 + + This module provides a middleware that performs sanity checks of the WSGI + application. It checks that :pep:`333` is properly implemented and warns + on some common HTTP errors such as non-empty responses for 304 status + codes. + + This module provides a middleware, the :class:`LintMiddleware`. Wrap your + application with it and it will warn about common problems with WSGI and + HTTP while your application is running. + + It's strongly recommended to use it during development. + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +try: + from urllib.parse import urlparse +except ImportError: + from urlparse import urlparse + +from warnings import warn + +from werkzeug.datastructures import Headers +from werkzeug.http import is_entity_header +from werkzeug.wsgi import FileWrapper +from werkzeug._compat import string_types + + +class WSGIWarning(Warning): + + """Warning class for WSGI warnings.""" + + +class HTTPWarning(Warning): + + """Warning class for HTTP warnings.""" + + +def check_string(context, obj, stacklevel=3): + if type(obj) is not str: + warn(WSGIWarning('%s requires bytestrings, got %s' % + (context, obj.__class__.__name__))) + + +class InputStream(object): + + def __init__(self, stream): + self._stream = stream + + def read(self, *args): + if len(args) == 0: + warn(WSGIWarning('wsgi does not guarantee an EOF marker on the ' + 'input stream, thus making calls to ' + 'wsgi.input.read() unsafe. Conforming servers ' + 'may never return from this call.'), + stacklevel=2) + elif len(args) != 1: + warn(WSGIWarning('too many parameters passed to wsgi.input.read()'), + stacklevel=2) + return self._stream.read(*args) + + def readline(self, *args): + if len(args) == 0: + warn(WSGIWarning('Calls to wsgi.input.readline() without arguments' + ' are unsafe. Use wsgi.input.read() instead.'), + stacklevel=2) + elif len(args) == 1: + warn(WSGIWarning('wsgi.input.readline() was called with a size hint. ' + 'WSGI does not support this, although it\'s available ' + 'on all major servers.'), + stacklevel=2) + else: + raise TypeError('too many arguments passed to wsgi.input.readline()') + return self._stream.readline(*args) + + def __iter__(self): + try: + return iter(self._stream) + except TypeError: + warn(WSGIWarning('wsgi.input is not iterable.'), stacklevel=2) + return iter(()) + + def close(self): + warn(WSGIWarning('application closed the input stream!'), + stacklevel=2) + self._stream.close() + + +class ErrorStream(object): + + def __init__(self, stream): + self._stream = stream + + def write(self, s): + check_string('wsgi.error.write()', s) + self._stream.write(s) + + def flush(self): + self._stream.flush() + + def writelines(self, seq): + for line in seq: + self.write(seq) + + def close(self): + warn(WSGIWarning('application closed the error stream!'), + stacklevel=2) + self._stream.close() + + +class GuardedWrite(object): + + def __init__(self, write, chunks): + self._write = write + self._chunks = chunks + + def __call__(self, s): + check_string('write()', s) + self._write.write(s) + self._chunks.append(len(s)) + + +class GuardedIterator(object): + + def __init__(self, iterator, headers_set, chunks): + self._iterator = iterator + self._next = iter(iterator).next + self.closed = False + self.headers_set = headers_set + self.chunks = chunks + + def __iter__(self): + return self + + def next(self): + if self.closed: + warn(WSGIWarning('iterated over closed app_iter'), + stacklevel=2) + rv = self._next() + if not self.headers_set: + warn(WSGIWarning('Application returned before it ' + 'started the response'), stacklevel=2) + check_string('application iterator items', rv) + self.chunks.append(len(rv)) + return rv + + def close(self): + self.closed = True + if hasattr(self._iterator, 'close'): + self._iterator.close() + + if self.headers_set: + status_code, headers = self.headers_set + bytes_sent = sum(self.chunks) + content_length = headers.get('content-length', type=int) + + if status_code == 304: + for key, value in headers: + key = key.lower() + if key not in ('expires', 'content-location') and \ + is_entity_header(key): + warn(HTTPWarning('entity header %r found in 304 ' + 'response' % key)) + if bytes_sent: + warn(HTTPWarning('304 responses must not have a body')) + elif 100 <= status_code < 200 or status_code == 204: + if content_length != 0: + warn(HTTPWarning('%r responses must have an empty ' + 'content length' % status_code)) + if bytes_sent: + warn(HTTPWarning('%r responses must not have a body' % + status_code)) + elif content_length is not None and content_length != bytes_sent: + warn(WSGIWarning('Content-Length and the number of bytes ' + 'sent to the client do not match.')) + + def __del__(self): + if not self.closed: + try: + warn(WSGIWarning('Iterator was garbage collected before ' + 'it was closed.')) + except Exception: + pass + + +class LintMiddleware(object): + + """This middleware wraps an application and warns on common errors. + Among other thing it currently checks for the following problems: + + - invalid status codes + - non-bytestrings sent to the WSGI server + - strings returned from the WSGI application + - non-empty conditional responses + - unquoted etags + - relative URLs in the Location header + - unsafe calls to wsgi.input + - unclosed iterators + + Detected errors are emitted using the standard Python :mod:`warnings` + system and usually end up on :data:`stderr`. + + :: + + from werkzeug.contrib.lint import LintMiddleware + app = LintMiddleware(app) + + :param app: the application to wrap + """ + + def __init__(self, app): + self.app = app + + def check_environ(self, environ): + if type(environ) is not dict: + warn(WSGIWarning('WSGI environment is not a standard python dict.'), + stacklevel=4) + for key in ('REQUEST_METHOD', 'SERVER_NAME', 'SERVER_PORT', + 'wsgi.version', 'wsgi.input', 'wsgi.errors', + 'wsgi.multithread', 'wsgi.multiprocess', + 'wsgi.run_once'): + if key not in environ: + warn(WSGIWarning('required environment key %r not found' + % key), stacklevel=3) + if environ['wsgi.version'] != (1, 0): + warn(WSGIWarning('environ is not a WSGI 1.0 environ'), + stacklevel=3) + + script_name = environ.get('SCRIPT_NAME', '') + if script_name and script_name[:1] != '/': + warn(WSGIWarning('SCRIPT_NAME does not start with a slash: %r' + % script_name), stacklevel=3) + path_info = environ.get('PATH_INFO', '') + if path_info[:1] != '/': + warn(WSGIWarning('PATH_INFO does not start with a slash: %r' + % path_info), stacklevel=3) + + def check_start_response(self, status, headers, exc_info): + check_string('status', status) + status_code = status.split(None, 1)[0] + if len(status_code) != 3 or not status_code.isdigit(): + warn(WSGIWarning('Status code must be three digits'), stacklevel=3) + if len(status) < 4 or status[3] != ' ': + warn(WSGIWarning('Invalid value for status %r. Valid ' + 'status strings are three digits, a space ' + 'and a status explanation'), stacklevel=3) + status_code = int(status_code) + if status_code < 100: + warn(WSGIWarning('status code < 100 detected'), stacklevel=3) + + if type(headers) is not list: + warn(WSGIWarning('header list is not a list'), stacklevel=3) + for item in headers: + if type(item) is not tuple or len(item) != 2: + warn(WSGIWarning('Headers must tuple 2-item tuples'), + stacklevel=3) + name, value = item + if type(name) is not str or type(value) is not str: + warn(WSGIWarning('header items must be strings'), + stacklevel=3) + if name.lower() == 'status': + warn(WSGIWarning('The status header is not supported due to ' + 'conflicts with the CGI spec.'), + stacklevel=3) + + if exc_info is not None and not isinstance(exc_info, tuple): + warn(WSGIWarning('invalid value for exc_info'), stacklevel=3) + + headers = Headers(headers) + self.check_headers(headers) + + return status_code, headers + + def check_headers(self, headers): + etag = headers.get('etag') + if etag is not None: + if etag.startswith(('W/', 'w/')): + if etag.startswith('w/'): + warn(HTTPWarning('weak etag indicator should be upcase.'), + stacklevel=4) + etag = etag[2:] + if not (etag[:1] == etag[-1:] == '"'): + warn(HTTPWarning('unquoted etag emitted.'), stacklevel=4) + + location = headers.get('location') + if location is not None: + if not urlparse(location).netloc: + warn(HTTPWarning('absolute URLs required for location header'), + stacklevel=4) + + def check_iterator(self, app_iter): + if isinstance(app_iter, string_types): + warn(WSGIWarning('application returned string. Response will ' + 'send character for character to the client ' + 'which will kill the performance. Return a ' + 'list or iterable instead.'), stacklevel=3) + + def __call__(self, *args, **kwargs): + if len(args) != 2: + warn(WSGIWarning('Two arguments to WSGI app required'), stacklevel=2) + if kwargs: + warn(WSGIWarning('No keyword arguments to WSGI app allowed'), + stacklevel=2) + environ, start_response = args + + self.check_environ(environ) + environ['wsgi.input'] = InputStream(environ['wsgi.input']) + environ['wsgi.errors'] = ErrorStream(environ['wsgi.errors']) + + # hook our own file wrapper in so that applications will always + # iterate to the end and we can check the content length + environ['wsgi.file_wrapper'] = FileWrapper + + headers_set = [] + chunks = [] + + def checking_start_response(*args, **kwargs): + if len(args) not in (2, 3): + warn(WSGIWarning('Invalid number of arguments: %s, expected ' + '2 or 3' % len(args), stacklevel=2)) + if kwargs: + warn(WSGIWarning('no keyword arguments allowed.')) + + status, headers = args[:2] + if len(args) == 3: + exc_info = args[2] + else: + exc_info = None + + headers_set[:] = self.check_start_response(status, headers, + exc_info) + return GuardedWrite(start_response(status, headers, exc_info), + chunks) + + app_iter = self.app(environ, checking_start_response) + self.check_iterator(app_iter) + return GuardedIterator(app_iter, headers_set, chunks) diff --git a/venv/Lib/site-packages/werkzeug/contrib/profiler.py b/venv/Lib/site-packages/werkzeug/contrib/profiler.py new file mode 100644 index 0000000..be860af --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/profiler.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.contrib.profiler + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + This module provides a simple WSGI profiler middleware for finding + bottlenecks in web application. It uses the :mod:`profile` or + :mod:`cProfile` module to do the profiling and writes the stats to the + stream provided (defaults to stderr). + + Example usage:: + + from werkzeug.contrib.profiler import ProfilerMiddleware + app = ProfilerMiddleware(app) + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +import sys +import time +import os.path +try: + try: + from cProfile import Profile + except ImportError: + from profile import Profile + from pstats import Stats + available = True +except ImportError: + available = False + + +class MergeStream(object): + + """An object that redirects `write` calls to multiple streams. + Use this to log to both `sys.stdout` and a file:: + + f = open('profiler.log', 'w') + stream = MergeStream(sys.stdout, f) + profiler = ProfilerMiddleware(app, stream) + """ + + def __init__(self, *streams): + if not streams: + raise TypeError('at least one stream must be given') + self.streams = streams + + def write(self, data): + for stream in self.streams: + stream.write(data) + + +class ProfilerMiddleware(object): + + """Simple profiler middleware. Wraps a WSGI application and profiles + a request. This intentionally buffers the response so that timings are + more exact. + + By giving the `profile_dir` argument, pstat.Stats files are saved to that + directory, one file per request. Without it, a summary is printed to + `stream` instead. + + For the exact meaning of `sort_by` and `restrictions` consult the + :mod:`profile` documentation. + + .. versionadded:: 0.9 + Added support for `restrictions` and `profile_dir`. + + :param app: the WSGI application to profile. + :param stream: the stream for the profiled stats. defaults to stderr. + :param sort_by: a tuple of columns to sort the result by. + :param restrictions: a tuple of profiling strictions, not used if dumping + to `profile_dir`. + :param profile_dir: directory name to save pstat files + """ + + def __init__(self, app, stream=None, + sort_by=('time', 'calls'), restrictions=(), profile_dir=None): + if not available: + raise RuntimeError('the profiler is not available because ' + 'profile or pstat is not installed.') + self._app = app + self._stream = stream or sys.stdout + self._sort_by = sort_by + self._restrictions = restrictions + self._profile_dir = profile_dir + + def __call__(self, environ, start_response): + response_body = [] + + def catching_start_response(status, headers, exc_info=None): + start_response(status, headers, exc_info) + return response_body.append + + def runapp(): + appiter = self._app(environ, catching_start_response) + response_body.extend(appiter) + if hasattr(appiter, 'close'): + appiter.close() + + p = Profile() + start = time.time() + p.runcall(runapp) + body = b''.join(response_body) + elapsed = time.time() - start + + if self._profile_dir is not None: + prof_filename = os.path.join(self._profile_dir, + '%s.%s.%06dms.%d.prof' % ( + environ['REQUEST_METHOD'], + environ.get('PATH_INFO').strip( + '/').replace('/', '.') or 'root', + elapsed * 1000.0, + time.time() + )) + p.dump_stats(prof_filename) + + else: + stats = Stats(p, stream=self._stream) + stats.sort_stats(*self._sort_by) + + self._stream.write('-' * 80) + self._stream.write('\nPATH: %r\n' % environ.get('PATH_INFO')) + stats.print_stats(*self._restrictions) + self._stream.write('-' * 80 + '\n\n') + + return [body] + + +def make_action(app_factory, hostname='localhost', port=5000, + threaded=False, processes=1, stream=None, + sort_by=('time', 'calls'), restrictions=()): + """Return a new callback for :mod:`werkzeug.script` that starts a local + server with the profiler enabled. + + :: + + from werkzeug.contrib import profiler + action_profile = profiler.make_action(make_app) + """ + def action(hostname=('h', hostname), port=('p', port), + threaded=threaded, processes=processes): + """Start a new development server.""" + from werkzeug.serving import run_simple + app = ProfilerMiddleware(app_factory(), stream, sort_by, restrictions) + run_simple(hostname, port, app, False, None, threaded, processes) + return action diff --git a/venv/Lib/site-packages/werkzeug/contrib/securecookie.py b/venv/Lib/site-packages/werkzeug/contrib/securecookie.py new file mode 100644 index 0000000..5927e2b --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/securecookie.py @@ -0,0 +1,323 @@ +# -*- coding: utf-8 -*- +r""" + werkzeug.contrib.securecookie + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + This module implements a cookie that is not alterable from the client + because it adds a checksum the server checks for. You can use it as + session replacement if all you have is a user id or something to mark + a logged in user. + + Keep in mind that the data is still readable from the client as a + normal cookie is. However you don't have to store and flush the + sessions you have at the server. + + Example usage: + + >>> from werkzeug.contrib.securecookie import SecureCookie + >>> x = SecureCookie({"foo": 42, "baz": (1, 2, 3)}, "deadbeef") + + Dumping into a string so that one can store it in a cookie: + + >>> value = x.serialize() + + Loading from that string again: + + >>> x = SecureCookie.unserialize(value, "deadbeef") + >>> x["baz"] + (1, 2, 3) + + If someone modifies the cookie and the checksum is wrong the unserialize + method will fail silently and return a new empty `SecureCookie` object. + + Keep in mind that the values will be visible in the cookie so do not + store data in a cookie you don't want the user to see. + + Application Integration + ======================= + + If you are using the werkzeug request objects you could integrate the + secure cookie into your application like this:: + + from werkzeug.utils import cached_property + from werkzeug.wrappers import BaseRequest + from werkzeug.contrib.securecookie import SecureCookie + + # don't use this key but a different one; you could just use + # os.urandom(20) to get something random + SECRET_KEY = '\xfa\xdd\xb8z\xae\xe0}4\x8b\xea' + + class Request(BaseRequest): + + @cached_property + def client_session(self): + data = self.cookies.get('session_data') + if not data: + return SecureCookie(secret_key=SECRET_KEY) + return SecureCookie.unserialize(data, SECRET_KEY) + + def application(environ, start_response): + request = Request(environ) + + # get a response object here + response = ... + + if request.client_session.should_save: + session_data = request.client_session.serialize() + response.set_cookie('session_data', session_data, + httponly=True) + return response(environ, start_response) + + A less verbose integration can be achieved by using shorthand methods:: + + class Request(BaseRequest): + + @cached_property + def client_session(self): + return SecureCookie.load_cookie(self, secret_key=COOKIE_SECRET) + + def application(environ, start_response): + request = Request(environ) + + # get a response object here + response = ... + + request.client_session.save_cookie(response) + return response(environ, start_response) + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +import pickle +import base64 +from hmac import new as hmac +from time import time +from hashlib import sha1 as _default_hash + +from werkzeug._compat import iteritems, text_type, to_bytes +from werkzeug.urls import url_quote_plus, url_unquote_plus +from werkzeug._internal import _date_to_unix +from werkzeug.contrib.sessions import ModificationTrackingDict +from werkzeug.security import safe_str_cmp +from werkzeug._compat import to_native + + +class UnquoteError(Exception): + + """Internal exception used to signal failures on quoting.""" + + +class SecureCookie(ModificationTrackingDict): + + """Represents a secure cookie. You can subclass this class and provide + an alternative mac method. The import thing is that the mac method + is a function with a similar interface to the hashlib. Required + methods are update() and digest(). + + Example usage: + + >>> x = SecureCookie({"foo": 42, "baz": (1, 2, 3)}, "deadbeef") + >>> x["foo"] + 42 + >>> x["baz"] + (1, 2, 3) + >>> x["blafasel"] = 23 + >>> x.should_save + True + + :param data: the initial data. Either a dict, list of tuples or `None`. + :param secret_key: the secret key. If not set `None` or not specified + it has to be set before :meth:`serialize` is called. + :param new: The initial value of the `new` flag. + """ + + #: The hash method to use. This has to be a module with a new function + #: or a function that creates a hashlib object. Such as `hashlib.md5` + #: Subclasses can override this attribute. The default hash is sha1. + #: Make sure to wrap this in staticmethod() if you store an arbitrary + #: function there such as hashlib.sha1 which might be implemented + #: as a function. + hash_method = staticmethod(_default_hash) + + #: the module used for serialization. Unless overriden by subclasses + #: the standard pickle module is used. + serialization_method = pickle + + #: if the contents should be base64 quoted. This can be disabled if the + #: serialization process returns cookie safe strings only. + quote_base64 = True + + def __init__(self, data=None, secret_key=None, new=True): + ModificationTrackingDict.__init__(self, data or ()) + # explicitly convert it into a bytestring because python 2.6 + # no longer performs an implicit string conversion on hmac + if secret_key is not None: + secret_key = to_bytes(secret_key, 'utf-8') + self.secret_key = secret_key + self.new = new + + def __repr__(self): + return '<%s %s%s>' % ( + self.__class__.__name__, + dict.__repr__(self), + self.should_save and '*' or '' + ) + + @property + def should_save(self): + """True if the session should be saved. By default this is only true + for :attr:`modified` cookies, not :attr:`new`. + """ + return self.modified + + @classmethod + def quote(cls, value): + """Quote the value for the cookie. This can be any object supported + by :attr:`serialization_method`. + + :param value: the value to quote. + """ + if cls.serialization_method is not None: + value = cls.serialization_method.dumps(value) + if cls.quote_base64: + value = b''.join(base64.b64encode(value).splitlines()).strip() + return value + + @classmethod + def unquote(cls, value): + """Unquote the value for the cookie. If unquoting does not work a + :exc:`UnquoteError` is raised. + + :param value: the value to unquote. + """ + try: + if cls.quote_base64: + value = base64.b64decode(value) + if cls.serialization_method is not None: + value = cls.serialization_method.loads(value) + return value + except Exception: + # unfortunately pickle and other serialization modules can + # cause pretty every error here. if we get one we catch it + # and convert it into an UnquoteError + raise UnquoteError() + + def serialize(self, expires=None): + """Serialize the secure cookie into a string. + + If expires is provided, the session will be automatically invalidated + after expiration when you unseralize it. This provides better + protection against session cookie theft. + + :param expires: an optional expiration date for the cookie (a + :class:`datetime.datetime` object) + """ + if self.secret_key is None: + raise RuntimeError('no secret key defined') + if expires: + self['_expires'] = _date_to_unix(expires) + result = [] + mac = hmac(self.secret_key, None, self.hash_method) + for key, value in sorted(self.items()): + result.append(('%s=%s' % ( + url_quote_plus(key), + self.quote(value).decode('ascii') + )).encode('ascii')) + mac.update(b'|' + result[-1]) + return b'?'.join([ + base64.b64encode(mac.digest()).strip(), + b'&'.join(result) + ]) + + @classmethod + def unserialize(cls, string, secret_key): + """Load the secure cookie from a serialized string. + + :param string: the cookie value to unserialize. + :param secret_key: the secret key used to serialize the cookie. + :return: a new :class:`SecureCookie`. + """ + if isinstance(string, text_type): + string = string.encode('utf-8', 'replace') + if isinstance(secret_key, text_type): + secret_key = secret_key.encode('utf-8', 'replace') + try: + base64_hash, data = string.split(b'?', 1) + except (ValueError, IndexError): + items = () + else: + items = {} + mac = hmac(secret_key, None, cls.hash_method) + for item in data.split(b'&'): + mac.update(b'|' + item) + if b'=' not in item: + items = None + break + key, value = item.split(b'=', 1) + # try to make the key a string + key = url_unquote_plus(key.decode('ascii')) + try: + key = to_native(key) + except UnicodeError: + pass + items[key] = value + + # no parsing error and the mac looks okay, we can now + # sercurely unpickle our cookie. + try: + client_hash = base64.b64decode(base64_hash) + except TypeError: + items = client_hash = None + if items is not None and safe_str_cmp(client_hash, mac.digest()): + try: + for key, value in iteritems(items): + items[key] = cls.unquote(value) + except UnquoteError: + items = () + else: + if '_expires' in items: + if time() > items['_expires']: + items = () + else: + del items['_expires'] + else: + items = () + return cls(items, secret_key, False) + + @classmethod + def load_cookie(cls, request, key='session', secret_key=None): + """Loads a :class:`SecureCookie` from a cookie in request. If the + cookie is not set, a new :class:`SecureCookie` instanced is + returned. + + :param request: a request object that has a `cookies` attribute + which is a dict of all cookie values. + :param key: the name of the cookie. + :param secret_key: the secret key used to unquote the cookie. + Always provide the value even though it has + no default! + """ + data = request.cookies.get(key) + if not data: + return cls(secret_key=secret_key) + return cls.unserialize(data, secret_key) + + def save_cookie(self, response, key='session', expires=None, + session_expires=None, max_age=None, path='/', domain=None, + secure=None, httponly=False, force=False): + """Saves the SecureCookie in a cookie on response object. All + parameters that are not described here are forwarded directly + to :meth:`~BaseResponse.set_cookie`. + + :param response: a response object that has a + :meth:`~BaseResponse.set_cookie` method. + :param key: the name of the cookie. + :param session_expires: the expiration date of the secure cookie + stored information. If this is not provided + the cookie `expires` date is used instead. + """ + if force or self.should_save: + data = self.serialize(session_expires or expires) + response.set_cookie(key, data, expires=expires, max_age=max_age, + path=path, domain=domain, secure=secure, + httponly=httponly) diff --git a/venv/Lib/site-packages/werkzeug/contrib/sessions.py b/venv/Lib/site-packages/werkzeug/contrib/sessions.py new file mode 100644 index 0000000..a9c3aa7 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/sessions.py @@ -0,0 +1,352 @@ +# -*- coding: utf-8 -*- +r""" + werkzeug.contrib.sessions + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + This module contains some helper classes that help one to add session + support to a python WSGI application. For full client-side session + storage see :mod:`~werkzeug.contrib.securecookie` which implements a + secure, client-side session storage. + + + Application Integration + ======================= + + :: + + from werkzeug.contrib.sessions import SessionMiddleware, \ + FilesystemSessionStore + + app = SessionMiddleware(app, FilesystemSessionStore()) + + The current session will then appear in the WSGI environment as + `werkzeug.session`. However it's recommended to not use the middleware + but the stores directly in the application. However for very simple + scripts a middleware for sessions could be sufficient. + + This module does not implement methods or ways to check if a session is + expired. That should be done by a cronjob and storage specific. For + example to prune unused filesystem sessions one could check the modified + time of the files. If sessions are stored in the database the new() + method should add an expiration timestamp for the session. + + For better flexibility it's recommended to not use the middleware but the + store and session object directly in the application dispatching:: + + session_store = FilesystemSessionStore() + + def application(environ, start_response): + request = Request(environ) + sid = request.cookies.get('cookie_name') + if sid is None: + request.session = session_store.new() + else: + request.session = session_store.get(sid) + response = get_the_response_object(request) + if request.session.should_save: + session_store.save(request.session) + response.set_cookie('cookie_name', request.session.sid) + return response(environ, start_response) + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +import re +import os +import tempfile +from os import path +from time import time +from random import random +from hashlib import sha1 +from pickle import dump, load, HIGHEST_PROTOCOL + +from werkzeug.datastructures import CallbackDict +from werkzeug.utils import dump_cookie, parse_cookie +from werkzeug.wsgi import ClosingIterator +from werkzeug.posixemulation import rename +from werkzeug._compat import PY2, text_type +from werkzeug.filesystem import get_filesystem_encoding + + +_sha1_re = re.compile(r'^[a-f0-9]{40}$') + + +def _urandom(): + if hasattr(os, 'urandom'): + return os.urandom(30) + return text_type(random()).encode('ascii') + + +def generate_key(salt=None): + if salt is None: + salt = repr(salt).encode('ascii') + return sha1(b''.join([ + salt, + str(time()).encode('ascii'), + _urandom() + ])).hexdigest() + + +class ModificationTrackingDict(CallbackDict): + __slots__ = ('modified',) + + def __init__(self, *args, **kwargs): + def on_update(self): + self.modified = True + self.modified = False + CallbackDict.__init__(self, on_update=on_update) + dict.update(self, *args, **kwargs) + + def copy(self): + """Create a flat copy of the dict.""" + missing = object() + result = object.__new__(self.__class__) + for name in self.__slots__: + val = getattr(self, name, missing) + if val is not missing: + setattr(result, name, val) + return result + + def __copy__(self): + return self.copy() + + +class Session(ModificationTrackingDict): + + """Subclass of a dict that keeps track of direct object changes. Changes + in mutable structures are not tracked, for those you have to set + `modified` to `True` by hand. + """ + __slots__ = ModificationTrackingDict.__slots__ + ('sid', 'new') + + def __init__(self, data, sid, new=False): + ModificationTrackingDict.__init__(self, data) + self.sid = sid + self.new = new + + def __repr__(self): + return '<%s %s%s>' % ( + self.__class__.__name__, + dict.__repr__(self), + self.should_save and '*' or '' + ) + + @property + def should_save(self): + """True if the session should be saved. + + .. versionchanged:: 0.6 + By default the session is now only saved if the session is + modified, not if it is new like it was before. + """ + return self.modified + + +class SessionStore(object): + + """Baseclass for all session stores. The Werkzeug contrib module does not + implement any useful stores besides the filesystem store, application + developers are encouraged to create their own stores. + + :param session_class: The session class to use. Defaults to + :class:`Session`. + """ + + def __init__(self, session_class=None): + if session_class is None: + session_class = Session + self.session_class = session_class + + def is_valid_key(self, key): + """Check if a key has the correct format.""" + return _sha1_re.match(key) is not None + + def generate_key(self, salt=None): + """Simple function that generates a new session key.""" + return generate_key(salt) + + def new(self): + """Generate a new session.""" + return self.session_class({}, self.generate_key(), True) + + def save(self, session): + """Save a session.""" + + def save_if_modified(self, session): + """Save if a session class wants an update.""" + if session.should_save: + self.save(session) + + def delete(self, session): + """Delete a session.""" + + def get(self, sid): + """Get a session for this sid or a new session object. This method + has to check if the session key is valid and create a new session if + that wasn't the case. + """ + return self.session_class({}, sid, True) + + +#: used for temporary files by the filesystem session store +_fs_transaction_suffix = '.__wz_sess' + + +class FilesystemSessionStore(SessionStore): + + """Simple example session store that saves sessions on the filesystem. + This store works best on POSIX systems and Windows Vista / Windows + Server 2008 and newer. + + .. versionchanged:: 0.6 + `renew_missing` was added. Previously this was considered `True`, + now the default changed to `False` and it can be explicitly + deactivated. + + :param path: the path to the folder used for storing the sessions. + If not provided the default temporary directory is used. + :param filename_template: a string template used to give the session + a filename. ``%s`` is replaced with the + session id. + :param session_class: The session class to use. Defaults to + :class:`Session`. + :param renew_missing: set to `True` if you want the store to + give the user a new sid if the session was + not yet saved. + """ + + def __init__(self, path=None, filename_template='werkzeug_%s.sess', + session_class=None, renew_missing=False, mode=0o644): + SessionStore.__init__(self, session_class) + if path is None: + path = tempfile.gettempdir() + self.path = path + if isinstance(filename_template, text_type) and PY2: + filename_template = filename_template.encode( + get_filesystem_encoding()) + assert not filename_template.endswith(_fs_transaction_suffix), \ + 'filename templates may not end with %s' % _fs_transaction_suffix + self.filename_template = filename_template + self.renew_missing = renew_missing + self.mode = mode + + def get_session_filename(self, sid): + # out of the box, this should be a strict ASCII subset but + # you might reconfigure the session object to have a more + # arbitrary string. + if isinstance(sid, text_type) and PY2: + sid = sid.encode(get_filesystem_encoding()) + return path.join(self.path, self.filename_template % sid) + + def save(self, session): + fn = self.get_session_filename(session.sid) + fd, tmp = tempfile.mkstemp(suffix=_fs_transaction_suffix, + dir=self.path) + f = os.fdopen(fd, 'wb') + try: + dump(dict(session), f, HIGHEST_PROTOCOL) + finally: + f.close() + try: + rename(tmp, fn) + os.chmod(fn, self.mode) + except (IOError, OSError): + pass + + def delete(self, session): + fn = self.get_session_filename(session.sid) + try: + os.unlink(fn) + except OSError: + pass + + def get(self, sid): + if not self.is_valid_key(sid): + return self.new() + try: + f = open(self.get_session_filename(sid), 'rb') + except IOError: + if self.renew_missing: + return self.new() + data = {} + else: + try: + try: + data = load(f) + except Exception: + data = {} + finally: + f.close() + return self.session_class(data, sid, False) + + def list(self): + """Lists all sessions in the store. + + .. versionadded:: 0.6 + """ + before, after = self.filename_template.split('%s', 1) + filename_re = re.compile(r'%s(.{5,})%s$' % (re.escape(before), + re.escape(after))) + result = [] + for filename in os.listdir(self.path): + #: this is a session that is still being saved. + if filename.endswith(_fs_transaction_suffix): + continue + match = filename_re.match(filename) + if match is not None: + result.append(match.group(1)) + return result + + +class SessionMiddleware(object): + + """A simple middleware that puts the session object of a store provided + into the WSGI environ. It automatically sets cookies and restores + sessions. + + However a middleware is not the preferred solution because it won't be as + fast as sessions managed by the application itself and will put a key into + the WSGI environment only relevant for the application which is against + the concept of WSGI. + + The cookie parameters are the same as for the :func:`~dump_cookie` + function just prefixed with ``cookie_``. Additionally `max_age` is + called `cookie_age` and not `cookie_max_age` because of backwards + compatibility. + """ + + def __init__(self, app, store, cookie_name='session_id', + cookie_age=None, cookie_expires=None, cookie_path='/', + cookie_domain=None, cookie_secure=None, + cookie_httponly=False, environ_key='werkzeug.session'): + self.app = app + self.store = store + self.cookie_name = cookie_name + self.cookie_age = cookie_age + self.cookie_expires = cookie_expires + self.cookie_path = cookie_path + self.cookie_domain = cookie_domain + self.cookie_secure = cookie_secure + self.cookie_httponly = cookie_httponly + self.environ_key = environ_key + + def __call__(self, environ, start_response): + cookie = parse_cookie(environ.get('HTTP_COOKIE', '')) + sid = cookie.get(self.cookie_name, None) + if sid is None: + session = self.store.new() + else: + session = self.store.get(sid) + environ[self.environ_key] = session + + def injecting_start_response(status, headers, exc_info=None): + if session.should_save: + self.store.save(session) + headers.append(('Set-Cookie', dump_cookie(self.cookie_name, + session.sid, self.cookie_age, + self.cookie_expires, self.cookie_path, + self.cookie_domain, self.cookie_secure, + self.cookie_httponly))) + return start_response(status, headers, exc_info) + return ClosingIterator(self.app(environ, injecting_start_response), + lambda: self.store.save_if_modified(session)) diff --git a/venv/Lib/site-packages/werkzeug/contrib/testtools.py b/venv/Lib/site-packages/werkzeug/contrib/testtools.py new file mode 100644 index 0000000..3dab6db --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/testtools.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.contrib.testtools + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + This module implements extended wrappers for simplified testing. + + `TestResponse` + A response wrapper which adds various cached attributes for + simplified assertions on various content types. + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +from werkzeug.utils import cached_property, import_string +from werkzeug.wrappers import Response + +from warnings import warn +warn(DeprecationWarning('werkzeug.contrib.testtools is deprecated and ' + 'will be removed with Werkzeug 1.0')) + + +class ContentAccessors(object): + + """ + A mixin class for response objects that provides a couple of useful + accessors for unittesting. + """ + + def xml(self): + """Get an etree if possible.""" + if 'xml' not in self.mimetype: + raise AttributeError( + 'Not a XML response (Content-Type: %s)' + % self.mimetype) + for module in ['xml.etree.ElementTree', 'ElementTree', + 'elementtree.ElementTree']: + etree = import_string(module, silent=True) + if etree is not None: + return etree.XML(self.body) + raise RuntimeError('You must have ElementTree installed ' + 'to use TestResponse.xml') + xml = cached_property(xml) + + def lxml(self): + """Get an lxml etree if possible.""" + if ('html' not in self.mimetype and 'xml' not in self.mimetype): + raise AttributeError('Not an HTML/XML response') + from lxml import etree + try: + from lxml.html import fromstring + except ImportError: + fromstring = etree.HTML + if self.mimetype == 'text/html': + return fromstring(self.data) + return etree.XML(self.data) + lxml = cached_property(lxml) + + def json(self): + """Get the result of simplejson.loads if possible.""" + if 'json' not in self.mimetype: + raise AttributeError('Not a JSON response') + try: + from simplejson import loads + except ImportError: + from json import loads + return loads(self.data) + json = cached_property(json) + + +class TestResponse(Response, ContentAccessors): + + """Pass this to `werkzeug.test.Client` for easier unittesting.""" diff --git a/venv/Lib/site-packages/werkzeug/contrib/wrappers.py b/venv/Lib/site-packages/werkzeug/contrib/wrappers.py new file mode 100644 index 0000000..6c86452 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/contrib/wrappers.py @@ -0,0 +1,284 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.contrib.wrappers + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Extra wrappers or mixins contributed by the community. These wrappers can + be mixed in into request objects to add extra functionality. + + Example:: + + from werkzeug.wrappers import Request as RequestBase + from werkzeug.contrib.wrappers import JSONRequestMixin + + class Request(RequestBase, JSONRequestMixin): + pass + + Afterwards this request object provides the extra functionality of the + :class:`JSONRequestMixin`. + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +import codecs +try: + from simplejson import loads +except ImportError: + from json import loads + +from werkzeug.exceptions import BadRequest +from werkzeug.utils import cached_property +from werkzeug.http import dump_options_header, parse_options_header +from werkzeug._compat import wsgi_decoding_dance + + +def is_known_charset(charset): + """Checks if the given charset is known to Python.""" + try: + codecs.lookup(charset) + except LookupError: + return False + return True + + +class JSONRequestMixin(object): + + """Add json method to a request object. This will parse the input data + through simplejson if possible. + + :exc:`~werkzeug.exceptions.BadRequest` will be raised if the content-type + is not json or if the data itself cannot be parsed as json. + """ + + @cached_property + def json(self): + """Get the result of simplejson.loads if possible.""" + if 'json' not in self.environ.get('CONTENT_TYPE', ''): + raise BadRequest('Not a JSON request') + try: + return loads(self.data.decode(self.charset, self.encoding_errors)) + except Exception: + raise BadRequest('Unable to read JSON request') + + +class ProtobufRequestMixin(object): + + """Add protobuf parsing method to a request object. This will parse the + input data through `protobuf`_ if possible. + + :exc:`~werkzeug.exceptions.BadRequest` will be raised if the content-type + is not protobuf or if the data itself cannot be parsed property. + + .. _protobuf: http://code.google.com/p/protobuf/ + """ + + #: by default the :class:`ProtobufRequestMixin` will raise a + #: :exc:`~werkzeug.exceptions.BadRequest` if the object is not + #: initialized. You can bypass that check by setting this + #: attribute to `False`. + protobuf_check_initialization = True + + def parse_protobuf(self, proto_type): + """Parse the data into an instance of proto_type.""" + if 'protobuf' not in self.environ.get('CONTENT_TYPE', ''): + raise BadRequest('Not a Protobuf request') + + obj = proto_type() + try: + obj.ParseFromString(self.data) + except Exception: + raise BadRequest("Unable to parse Protobuf request") + + # Fail if not all required fields are set + if self.protobuf_check_initialization and not obj.IsInitialized(): + raise BadRequest("Partial Protobuf request") + + return obj + + +class RoutingArgsRequestMixin(object): + + """This request mixin adds support for the wsgiorg routing args + `specification`_. + + .. _specification: https://wsgi.readthedocs.io/en/latest/specifications/routing_args.html + """ + + def _get_routing_args(self): + return self.environ.get('wsgiorg.routing_args', (()))[0] + + def _set_routing_args(self, value): + if self.shallow: + raise RuntimeError('A shallow request tried to modify the WSGI ' + 'environment. If you really want to do that, ' + 'set `shallow` to False.') + self.environ['wsgiorg.routing_args'] = (value, self.routing_vars) + + routing_args = property(_get_routing_args, _set_routing_args, doc=''' + The positional URL arguments as `tuple`.''') + del _get_routing_args, _set_routing_args + + def _get_routing_vars(self): + rv = self.environ.get('wsgiorg.routing_args') + if rv is not None: + return rv[1] + rv = {} + if not self.shallow: + self.routing_vars = rv + return rv + + def _set_routing_vars(self, value): + if self.shallow: + raise RuntimeError('A shallow request tried to modify the WSGI ' + 'environment. If you really want to do that, ' + 'set `shallow` to False.') + self.environ['wsgiorg.routing_args'] = (self.routing_args, value) + + routing_vars = property(_get_routing_vars, _set_routing_vars, doc=''' + The keyword URL arguments as `dict`.''') + del _get_routing_vars, _set_routing_vars + + +class ReverseSlashBehaviorRequestMixin(object): + + """This mixin reverses the trailing slash behavior of :attr:`script_root` + and :attr:`path`. This makes it possible to use :func:`~urlparse.urljoin` + directly on the paths. + + Because it changes the behavior or :class:`Request` this class has to be + mixed in *before* the actual request class:: + + class MyRequest(ReverseSlashBehaviorRequestMixin, Request): + pass + + This example shows the differences (for an application mounted on + `/application` and the request going to `/application/foo/bar`): + + +---------------+-------------------+---------------------+ + | | normal behavior | reverse behavior | + +===============+===================+=====================+ + | `script_root` | ``/application`` | ``/application/`` | + +---------------+-------------------+---------------------+ + | `path` | ``/foo/bar`` | ``foo/bar`` | + +---------------+-------------------+---------------------+ + """ + + @cached_property + def path(self): + """Requested path as unicode. This works a bit like the regular path + info in the WSGI environment but will not include a leading slash. + """ + path = wsgi_decoding_dance(self.environ.get('PATH_INFO') or '', + self.charset, self.encoding_errors) + return path.lstrip('/') + + @cached_property + def script_root(self): + """The root path of the script includling a trailing slash.""" + path = wsgi_decoding_dance(self.environ.get('SCRIPT_NAME') or '', + self.charset, self.encoding_errors) + return path.rstrip('/') + '/' + + +class DynamicCharsetRequestMixin(object): + + """"If this mixin is mixed into a request class it will provide + a dynamic `charset` attribute. This means that if the charset is + transmitted in the content type headers it's used from there. + + Because it changes the behavior or :class:`Request` this class has + to be mixed in *before* the actual request class:: + + class MyRequest(DynamicCharsetRequestMixin, Request): + pass + + By default the request object assumes that the URL charset is the + same as the data charset. If the charset varies on each request + based on the transmitted data it's not a good idea to let the URLs + change based on that. Most browsers assume either utf-8 or latin1 + for the URLs if they have troubles figuring out. It's strongly + recommended to set the URL charset to utf-8:: + + class MyRequest(DynamicCharsetRequestMixin, Request): + url_charset = 'utf-8' + + .. versionadded:: 0.6 + """ + + #: the default charset that is assumed if the content type header + #: is missing or does not contain a charset parameter. The default + #: is latin1 which is what HTTP specifies as default charset. + #: You may however want to set this to utf-8 to better support + #: browsers that do not transmit a charset for incoming data. + default_charset = 'latin1' + + def unknown_charset(self, charset): + """Called if a charset was provided but is not supported by + the Python codecs module. By default latin1 is assumed then + to not lose any information, you may override this method to + change the behavior. + + :param charset: the charset that was not found. + :return: the replacement charset. + """ + return 'latin1' + + @cached_property + def charset(self): + """The charset from the content type.""" + header = self.environ.get('CONTENT_TYPE') + if header: + ct, options = parse_options_header(header) + charset = options.get('charset') + if charset: + if is_known_charset(charset): + return charset + return self.unknown_charset(charset) + return self.default_charset + + +class DynamicCharsetResponseMixin(object): + + """If this mixin is mixed into a response class it will provide + a dynamic `charset` attribute. This means that if the charset is + looked up and stored in the `Content-Type` header and updates + itself automatically. This also means a small performance hit but + can be useful if you're working with different charsets on + responses. + + Because the charset attribute is no a property at class-level, the + default value is stored in `default_charset`. + + Because it changes the behavior or :class:`Response` this class has + to be mixed in *before* the actual response class:: + + class MyResponse(DynamicCharsetResponseMixin, Response): + pass + + .. versionadded:: 0.6 + """ + + #: the default charset. + default_charset = 'utf-8' + + def _get_charset(self): + header = self.headers.get('content-type') + if header: + charset = parse_options_header(header)[1].get('charset') + if charset: + return charset + return self.default_charset + + def _set_charset(self, charset): + header = self.headers.get('content-type') + ct, options = parse_options_header(header) + if not ct: + raise TypeError('Cannot set charset if Content-Type ' + 'header is missing.') + options['charset'] = charset + self.headers['Content-Type'] = dump_options_header(ct, options) + + charset = property(_get_charset, _set_charset, doc=""" + The charset for the response. It's stored inside the + Content-Type header as a parameter.""") + del _get_charset, _set_charset diff --git a/venv/Lib/site-packages/werkzeug/datastructures.py b/venv/Lib/site-packages/werkzeug/datastructures.py new file mode 100644 index 0000000..9624599 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/datastructures.py @@ -0,0 +1,2762 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.datastructures + ~~~~~~~~~~~~~~~~~~~~~~~ + + This module provides mixins and classes with an immutable interface. + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +import re +import codecs +import mimetypes +from copy import deepcopy +from itertools import repeat +from collections import Container, Iterable, MutableSet + +from werkzeug._internal import _missing, _empty_stream +from werkzeug._compat import iterkeys, itervalues, iteritems, iterlists, \ + PY2, text_type, integer_types, string_types, make_literal_wrapper, \ + to_native +from werkzeug.filesystem import get_filesystem_encoding + + +_locale_delim_re = re.compile(r'[_-]') + + +def is_immutable(self): + raise TypeError('%r objects are immutable' % self.__class__.__name__) + + +def iter_multi_items(mapping): + """Iterates over the items of a mapping yielding keys and values + without dropping any from more complex structures. + """ + if isinstance(mapping, MultiDict): + for item in iteritems(mapping, multi=True): + yield item + elif isinstance(mapping, dict): + for key, value in iteritems(mapping): + if isinstance(value, (tuple, list)): + for value in value: + yield key, value + else: + yield key, value + else: + for item in mapping: + yield item + + +def native_itermethods(names): + if not PY2: + return lambda x: x + + def setviewmethod(cls, name): + viewmethod_name = 'view%s' % name + viewmethod = lambda self, *a, **kw: ViewItems(self, name, 'view_%s' % name, *a, **kw) + viewmethod.__doc__ = \ + '"""`%s()` object providing a view on %s"""' % (viewmethod_name, name) + setattr(cls, viewmethod_name, viewmethod) + + def setitermethod(cls, name): + itermethod = getattr(cls, name) + setattr(cls, 'iter%s' % name, itermethod) + listmethod = lambda self, *a, **kw: list(itermethod(self, *a, **kw)) + listmethod.__doc__ = \ + 'Like :py:meth:`iter%s`, but returns a list.' % name + setattr(cls, name, listmethod) + + def wrap(cls): + for name in names: + setitermethod(cls, name) + setviewmethod(cls, name) + return cls + return wrap + + +class ImmutableListMixin(object): + + """Makes a :class:`list` immutable. + + .. versionadded:: 0.5 + + :private: + """ + + _hash_cache = None + + def __hash__(self): + if self._hash_cache is not None: + return self._hash_cache + rv = self._hash_cache = hash(tuple(self)) + return rv + + def __reduce_ex__(self, protocol): + return type(self), (list(self),) + + def __delitem__(self, key): + is_immutable(self) + + def __iadd__(self, other): + is_immutable(self) + __imul__ = __iadd__ + + def __setitem__(self, key, value): + is_immutable(self) + + def append(self, item): + is_immutable(self) + remove = append + + def extend(self, iterable): + is_immutable(self) + + def insert(self, pos, value): + is_immutable(self) + + def pop(self, index=-1): + is_immutable(self) + + def reverse(self): + is_immutable(self) + + def sort(self, cmp=None, key=None, reverse=None): + is_immutable(self) + + +class ImmutableList(ImmutableListMixin, list): + + """An immutable :class:`list`. + + .. versionadded:: 0.5 + + :private: + """ + + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + list.__repr__(self), + ) + + +class ImmutableDictMixin(object): + + """Makes a :class:`dict` immutable. + + .. versionadded:: 0.5 + + :private: + """ + _hash_cache = None + + @classmethod + def fromkeys(cls, keys, value=None): + instance = super(cls, cls).__new__(cls) + instance.__init__(zip(keys, repeat(value))) + return instance + + def __reduce_ex__(self, protocol): + return type(self), (dict(self),) + + def _iter_hashitems(self): + return iteritems(self) + + def __hash__(self): + if self._hash_cache is not None: + return self._hash_cache + rv = self._hash_cache = hash(frozenset(self._iter_hashitems())) + return rv + + def setdefault(self, key, default=None): + is_immutable(self) + + def update(self, *args, **kwargs): + is_immutable(self) + + def pop(self, key, default=None): + is_immutable(self) + + def popitem(self): + is_immutable(self) + + def __setitem__(self, key, value): + is_immutable(self) + + def __delitem__(self, key): + is_immutable(self) + + def clear(self): + is_immutable(self) + + +class ImmutableMultiDictMixin(ImmutableDictMixin): + + """Makes a :class:`MultiDict` immutable. + + .. versionadded:: 0.5 + + :private: + """ + + def __reduce_ex__(self, protocol): + return type(self), (list(iteritems(self, multi=True)),) + + def _iter_hashitems(self): + return iteritems(self, multi=True) + + def add(self, key, value): + is_immutable(self) + + def popitemlist(self): + is_immutable(self) + + def poplist(self, key): + is_immutable(self) + + def setlist(self, key, new_list): + is_immutable(self) + + def setlistdefault(self, key, default_list=None): + is_immutable(self) + + +class UpdateDictMixin(object): + + """Makes dicts call `self.on_update` on modifications. + + .. versionadded:: 0.5 + + :private: + """ + + on_update = None + + def calls_update(name): + def oncall(self, *args, **kw): + rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw) + if self.on_update is not None: + self.on_update(self) + return rv + oncall.__name__ = name + return oncall + + def setdefault(self, key, default=None): + modified = key not in self + rv = super(UpdateDictMixin, self).setdefault(key, default) + if modified and self.on_update is not None: + self.on_update(self) + return rv + + def pop(self, key, default=_missing): + modified = key in self + if default is _missing: + rv = super(UpdateDictMixin, self).pop(key) + else: + rv = super(UpdateDictMixin, self).pop(key, default) + if modified and self.on_update is not None: + self.on_update(self) + return rv + + __setitem__ = calls_update('__setitem__') + __delitem__ = calls_update('__delitem__') + clear = calls_update('clear') + popitem = calls_update('popitem') + update = calls_update('update') + del calls_update + + +class TypeConversionDict(dict): + + """Works like a regular dict but the :meth:`get` method can perform + type conversions. :class:`MultiDict` and :class:`CombinedMultiDict` + are subclasses of this class and provide the same feature. + + .. versionadded:: 0.5 + """ + + def get(self, key, default=None, type=None): + """Return the default value if the requested data doesn't exist. + If `type` is provided and is a callable it should convert the value, + return it or raise a :exc:`ValueError` if that is not possible. In + this case the function will return the default as if the value was not + found: + + >>> d = TypeConversionDict(foo='42', bar='blub') + >>> d.get('foo', type=int) + 42 + >>> d.get('bar', -1, type=int) + -1 + + :param key: The key to be looked up. + :param default: The default value to be returned if the key can't + be looked up. If not further specified `None` is + returned. + :param type: A callable that is used to cast the value in the + :class:`MultiDict`. If a :exc:`ValueError` is raised + by this callable the default value is returned. + """ + try: + rv = self[key] + except KeyError: + return default + if type is not None: + try: + rv = type(rv) + except ValueError: + rv = default + return rv + + +class ImmutableTypeConversionDict(ImmutableDictMixin, TypeConversionDict): + + """Works like a :class:`TypeConversionDict` but does not support + modifications. + + .. versionadded:: 0.5 + """ + + def copy(self): + """Return a shallow mutable copy of this object. Keep in mind that + the standard library's :func:`copy` function is a no-op for this class + like for any other python immutable type (eg: :class:`tuple`). + """ + return TypeConversionDict(self) + + def __copy__(self): + return self + + +class ViewItems(object): + + def __init__(self, multi_dict, method, repr_name, *a, **kw): + self.__multi_dict = multi_dict + self.__method = method + self.__repr_name = repr_name + self.__a = a + self.__kw = kw + + def __get_items(self): + return getattr(self.__multi_dict, self.__method)(*self.__a, **self.__kw) + + def __repr__(self): + return '%s(%r)' % (self.__repr_name, list(self.__get_items())) + + def __iter__(self): + return iter(self.__get_items()) + + +@native_itermethods(['keys', 'values', 'items', 'lists', 'listvalues']) +class MultiDict(TypeConversionDict): + + """A :class:`MultiDict` is a dictionary subclass customized to deal with + multiple values for the same key which is for example used by the parsing + functions in the wrappers. This is necessary because some HTML form + elements pass multiple values for the same key. + + :class:`MultiDict` implements all standard dictionary methods. + Internally, it saves all values for a key as a list, but the standard dict + access methods will only return the first value for a key. If you want to + gain access to the other values, too, you have to use the `list` methods as + explained below. + + Basic Usage: + + >>> d = MultiDict([('a', 'b'), ('a', 'c')]) + >>> d + MultiDict([('a', 'b'), ('a', 'c')]) + >>> d['a'] + 'b' + >>> d.getlist('a') + ['b', 'c'] + >>> 'a' in d + True + + It behaves like a normal dict thus all dict functions will only return the + first value when multiple values for one key are found. + + From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a + subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will + render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP + exceptions. + + A :class:`MultiDict` can be constructed from an iterable of + ``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2 + onwards some keyword parameters. + + :param mapping: the initial value for the :class:`MultiDict`. Either a + regular dict, an iterable of ``(key, value)`` tuples + or `None`. + """ + + def __init__(self, mapping=None): + if isinstance(mapping, MultiDict): + dict.__init__(self, ((k, l[:]) for k, l in iterlists(mapping))) + elif isinstance(mapping, dict): + tmp = {} + for key, value in iteritems(mapping): + if isinstance(value, (tuple, list)): + if len(value) == 0: + continue + value = list(value) + else: + value = [value] + tmp[key] = value + dict.__init__(self, tmp) + else: + tmp = {} + for key, value in mapping or (): + tmp.setdefault(key, []).append(value) + dict.__init__(self, tmp) + + def __getstate__(self): + return dict(self.lists()) + + def __setstate__(self, value): + dict.clear(self) + dict.update(self, value) + + def __getitem__(self, key): + """Return the first data value for this key; + raises KeyError if not found. + + :param key: The key to be looked up. + :raise KeyError: if the key does not exist. + """ + if key in self: + lst = dict.__getitem__(self, key) + if len(lst) > 0: + return lst[0] + raise exceptions.BadRequestKeyError(key) + + def __setitem__(self, key, value): + """Like :meth:`add` but removes an existing key first. + + :param key: the key for the value. + :param value: the value to set. + """ + dict.__setitem__(self, key, [value]) + + def add(self, key, value): + """Adds a new value for the key. + + .. versionadded:: 0.6 + + :param key: the key for the value. + :param value: the value to add. + """ + dict.setdefault(self, key, []).append(value) + + def getlist(self, key, type=None): + """Return the list of items for a given key. If that key is not in the + `MultiDict`, the return value will be an empty list. Just as `get` + `getlist` accepts a `type` parameter. All items will be converted + with the callable defined there. + + :param key: The key to be looked up. + :param type: A callable that is used to cast the value in the + :class:`MultiDict`. If a :exc:`ValueError` is raised + by this callable the value will be removed from the list. + :return: a :class:`list` of all the values for the key. + """ + try: + rv = dict.__getitem__(self, key) + except KeyError: + return [] + if type is None: + return list(rv) + result = [] + for item in rv: + try: + result.append(type(item)) + except ValueError: + pass + return result + + def setlist(self, key, new_list): + """Remove the old values for a key and add new ones. Note that the list + you pass the values in will be shallow-copied before it is inserted in + the dictionary. + + >>> d = MultiDict() + >>> d.setlist('foo', ['1', '2']) + >>> d['foo'] + '1' + >>> d.getlist('foo') + ['1', '2'] + + :param key: The key for which the values are set. + :param new_list: An iterable with the new values for the key. Old values + are removed first. + """ + dict.__setitem__(self, key, list(new_list)) + + def setdefault(self, key, default=None): + """Returns the value for the key if it is in the dict, otherwise it + returns `default` and sets that value for `key`. + + :param key: The key to be looked up. + :param default: The default value to be returned if the key is not + in the dict. If not further specified it's `None`. + """ + if key not in self: + self[key] = default + else: + default = self[key] + return default + + def setlistdefault(self, key, default_list=None): + """Like `setdefault` but sets multiple values. The list returned + is not a copy, but the list that is actually used internally. This + means that you can put new values into the dict by appending items + to the list: + + >>> d = MultiDict({"foo": 1}) + >>> d.setlistdefault("foo").extend([2, 3]) + >>> d.getlist("foo") + [1, 2, 3] + + :param key: The key to be looked up. + :param default_list: An iterable of default values. It is either copied + (in case it was a list) or converted into a list + before returned. + :return: a :class:`list` + """ + if key not in self: + default_list = list(default_list or ()) + dict.__setitem__(self, key, default_list) + else: + default_list = dict.__getitem__(self, key) + return default_list + + def items(self, multi=False): + """Return an iterator of ``(key, value)`` pairs. + + :param multi: If set to `True` the iterator returned will have a pair + for each value of each key. Otherwise it will only + contain pairs for the first value of each key. + """ + + for key, values in iteritems(dict, self): + if multi: + for value in values: + yield key, value + else: + yield key, values[0] + + def lists(self): + """Return a list of ``(key, values)`` pairs, where values is the list + of all values associated with the key.""" + + for key, values in iteritems(dict, self): + yield key, list(values) + + def keys(self): + return iterkeys(dict, self) + + __iter__ = keys + + def values(self): + """Returns an iterator of the first value on every key's value list.""" + for values in itervalues(dict, self): + yield values[0] + + def listvalues(self): + """Return an iterator of all values associated with a key. Zipping + :meth:`keys` and this is the same as calling :meth:`lists`: + + >>> d = MultiDict({"foo": [1, 2, 3]}) + >>> zip(d.keys(), d.listvalues()) == d.lists() + True + """ + + return itervalues(dict, self) + + def copy(self): + """Return a shallow copy of this object.""" + return self.__class__(self) + + def deepcopy(self, memo=None): + """Return a deep copy of this object.""" + return self.__class__(deepcopy(self.to_dict(flat=False), memo)) + + def to_dict(self, flat=True): + """Return the contents as regular dict. If `flat` is `True` the + returned dict will only have the first item present, if `flat` is + `False` all values will be returned as lists. + + :param flat: If set to `False` the dict returned will have lists + with all the values in it. Otherwise it will only + contain the first value for each key. + :return: a :class:`dict` + """ + if flat: + return dict(iteritems(self)) + return dict(self.lists()) + + def update(self, other_dict): + """update() extends rather than replaces existing key lists: + + >>> a = MultiDict({'x': 1}) + >>> b = MultiDict({'x': 2, 'y': 3}) + >>> a.update(b) + >>> a + MultiDict([('y', 3), ('x', 1), ('x', 2)]) + + If the value list for a key in ``other_dict`` is empty, no new values + will be added to the dict and the key will not be created: + + >>> x = {'empty_list': []} + >>> y = MultiDict() + >>> y.update(x) + >>> y + MultiDict([]) + """ + for key, value in iter_multi_items(other_dict): + MultiDict.add(self, key, value) + + def pop(self, key, default=_missing): + """Pop the first item for a list on the dict. Afterwards the + key is removed from the dict, so additional values are discarded: + + >>> d = MultiDict({"foo": [1, 2, 3]}) + >>> d.pop("foo") + 1 + >>> "foo" in d + False + + :param key: the key to pop. + :param default: if provided the value to return if the key was + not in the dictionary. + """ + try: + lst = dict.pop(self, key) + + if len(lst) == 0: + raise exceptions.BadRequestKeyError() + + return lst[0] + except KeyError as e: + if default is not _missing: + return default + raise exceptions.BadRequestKeyError(str(e)) + + def popitem(self): + """Pop an item from the dict.""" + try: + item = dict.popitem(self) + + if len(item[1]) == 0: + raise exceptions.BadRequestKeyError() + + return (item[0], item[1][0]) + except KeyError as e: + raise exceptions.BadRequestKeyError(str(e)) + + def poplist(self, key): + """Pop the list for a key from the dict. If the key is not in the dict + an empty list is returned. + + .. versionchanged:: 0.5 + If the key does no longer exist a list is returned instead of + raising an error. + """ + return dict.pop(self, key, []) + + def popitemlist(self): + """Pop a ``(key, list)`` tuple from the dict.""" + try: + return dict.popitem(self) + except KeyError as e: + raise exceptions.BadRequestKeyError(str(e)) + + def __copy__(self): + return self.copy() + + def __deepcopy__(self, memo): + return self.deepcopy(memo=memo) + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, list(iteritems(self, multi=True))) + + +class _omd_bucket(object): + + """Wraps values in the :class:`OrderedMultiDict`. This makes it + possible to keep an order over multiple different keys. It requires + a lot of extra memory and slows down access a lot, but makes it + possible to access elements in O(1) and iterate in O(n). + """ + __slots__ = ('prev', 'key', 'value', 'next') + + def __init__(self, omd, key, value): + self.prev = omd._last_bucket + self.key = key + self.value = value + self.next = None + + if omd._first_bucket is None: + omd._first_bucket = self + if omd._last_bucket is not None: + omd._last_bucket.next = self + omd._last_bucket = self + + def unlink(self, omd): + if self.prev: + self.prev.next = self.next + if self.next: + self.next.prev = self.prev + if omd._first_bucket is self: + omd._first_bucket = self.next + if omd._last_bucket is self: + omd._last_bucket = self.prev + + +@native_itermethods(['keys', 'values', 'items', 'lists', 'listvalues']) +class OrderedMultiDict(MultiDict): + + """Works like a regular :class:`MultiDict` but preserves the + order of the fields. To convert the ordered multi dict into a + list you can use the :meth:`items` method and pass it ``multi=True``. + + In general an :class:`OrderedMultiDict` is an order of magnitude + slower than a :class:`MultiDict`. + + .. admonition:: note + + Due to a limitation in Python you cannot convert an ordered + multi dict into a regular dict by using ``dict(multidict)``. + Instead you have to use the :meth:`to_dict` method, otherwise + the internal bucket objects are exposed. + """ + + def __init__(self, mapping=None): + dict.__init__(self) + self._first_bucket = self._last_bucket = None + if mapping is not None: + OrderedMultiDict.update(self, mapping) + + def __eq__(self, other): + if not isinstance(other, MultiDict): + return NotImplemented + if isinstance(other, OrderedMultiDict): + iter1 = iteritems(self, multi=True) + iter2 = iteritems(other, multi=True) + try: + for k1, v1 in iter1: + k2, v2 = next(iter2) + if k1 != k2 or v1 != v2: + return False + except StopIteration: + return False + try: + next(iter2) + except StopIteration: + return True + return False + if len(self) != len(other): + return False + for key, values in iterlists(self): + if other.getlist(key) != values: + return False + return True + + __hash__ = None + + def __ne__(self, other): + return not self.__eq__(other) + + def __reduce_ex__(self, protocol): + return type(self), (list(iteritems(self, multi=True)),) + + def __getstate__(self): + return list(iteritems(self, multi=True)) + + def __setstate__(self, values): + dict.clear(self) + for key, value in values: + self.add(key, value) + + def __getitem__(self, key): + if key in self: + return dict.__getitem__(self, key)[0].value + raise exceptions.BadRequestKeyError(key) + + def __setitem__(self, key, value): + self.poplist(key) + self.add(key, value) + + def __delitem__(self, key): + self.pop(key) + + def keys(self): + return (key for key, value in iteritems(self)) + + __iter__ = keys + + def values(self): + return (value for key, value in iteritems(self)) + + def items(self, multi=False): + ptr = self._first_bucket + if multi: + while ptr is not None: + yield ptr.key, ptr.value + ptr = ptr.next + else: + returned_keys = set() + while ptr is not None: + if ptr.key not in returned_keys: + returned_keys.add(ptr.key) + yield ptr.key, ptr.value + ptr = ptr.next + + def lists(self): + returned_keys = set() + ptr = self._first_bucket + while ptr is not None: + if ptr.key not in returned_keys: + yield ptr.key, self.getlist(ptr.key) + returned_keys.add(ptr.key) + ptr = ptr.next + + def listvalues(self): + for key, values in iterlists(self): + yield values + + def add(self, key, value): + dict.setdefault(self, key, []).append(_omd_bucket(self, key, value)) + + def getlist(self, key, type=None): + try: + rv = dict.__getitem__(self, key) + except KeyError: + return [] + if type is None: + return [x.value for x in rv] + result = [] + for item in rv: + try: + result.append(type(item.value)) + except ValueError: + pass + return result + + def setlist(self, key, new_list): + self.poplist(key) + for value in new_list: + self.add(key, value) + + def setlistdefault(self, key, default_list=None): + raise TypeError('setlistdefault is unsupported for ' + 'ordered multi dicts') + + def update(self, mapping): + for key, value in iter_multi_items(mapping): + OrderedMultiDict.add(self, key, value) + + def poplist(self, key): + buckets = dict.pop(self, key, ()) + for bucket in buckets: + bucket.unlink(self) + return [x.value for x in buckets] + + def pop(self, key, default=_missing): + try: + buckets = dict.pop(self, key) + except KeyError as e: + if default is not _missing: + return default + raise exceptions.BadRequestKeyError(str(e)) + for bucket in buckets: + bucket.unlink(self) + return buckets[0].value + + def popitem(self): + try: + key, buckets = dict.popitem(self) + except KeyError as e: + raise exceptions.BadRequestKeyError(str(e)) + for bucket in buckets: + bucket.unlink(self) + return key, buckets[0].value + + def popitemlist(self): + try: + key, buckets = dict.popitem(self) + except KeyError as e: + raise exceptions.BadRequestKeyError(str(e)) + for bucket in buckets: + bucket.unlink(self) + return key, [x.value for x in buckets] + + +def _options_header_vkw(value, kw): + return dump_options_header(value, dict((k.replace('_', '-'), v) + for k, v in kw.items())) + + +def _unicodify_header_value(value): + if isinstance(value, bytes): + value = value.decode('latin-1') + if not isinstance(value, text_type): + value = text_type(value) + return value + + +@native_itermethods(['keys', 'values', 'items']) +class Headers(object): + + """An object that stores some headers. It has a dict-like interface + but is ordered and can store the same keys multiple times. + + This data structure is useful if you want a nicer way to handle WSGI + headers which are stored as tuples in a list. + + From Werkzeug 0.3 onwards, the :exc:`KeyError` raised by this class is + also a subclass of the :class:`~exceptions.BadRequest` HTTP exception + and will render a page for a ``400 BAD REQUEST`` if caught in a + catch-all for HTTP exceptions. + + Headers is mostly compatible with the Python :class:`wsgiref.headers.Headers` + class, with the exception of `__getitem__`. :mod:`wsgiref` will return + `None` for ``headers['missing']``, whereas :class:`Headers` will raise + a :class:`KeyError`. + + To create a new :class:`Headers` object pass it a list or dict of headers + which are used as default values. This does not reuse the list passed + to the constructor for internal usage. + + :param defaults: The list of default values for the :class:`Headers`. + + .. versionchanged:: 0.9 + This data structure now stores unicode values similar to how the + multi dicts do it. The main difference is that bytes can be set as + well which will automatically be latin1 decoded. + + .. versionchanged:: 0.9 + The :meth:`linked` function was removed without replacement as it + was an API that does not support the changes to the encoding model. + """ + + def __init__(self, defaults=None): + self._list = [] + if defaults is not None: + if isinstance(defaults, (list, Headers)): + self._list.extend(defaults) + else: + self.extend(defaults) + + def __getitem__(self, key, _get_mode=False): + if not _get_mode: + if isinstance(key, integer_types): + return self._list[key] + elif isinstance(key, slice): + return self.__class__(self._list[key]) + if not isinstance(key, string_types): + raise exceptions.BadRequestKeyError(key) + ikey = key.lower() + for k, v in self._list: + if k.lower() == ikey: + return v + # micro optimization: if we are in get mode we will catch that + # exception one stack level down so we can raise a standard + # key error instead of our special one. + if _get_mode: + raise KeyError() + raise exceptions.BadRequestKeyError(key) + + def __eq__(self, other): + return other.__class__ is self.__class__ and \ + set(other._list) == set(self._list) + + __hash__ = None + + def __ne__(self, other): + return not self.__eq__(other) + + def get(self, key, default=None, type=None, as_bytes=False): + """Return the default value if the requested data doesn't exist. + If `type` is provided and is a callable it should convert the value, + return it or raise a :exc:`ValueError` if that is not possible. In + this case the function will return the default as if the value was not + found: + + >>> d = Headers([('Content-Length', '42')]) + >>> d.get('Content-Length', type=int) + 42 + + If a headers object is bound you must not add unicode strings + because no encoding takes place. + + .. versionadded:: 0.9 + Added support for `as_bytes`. + + :param key: The key to be looked up. + :param default: The default value to be returned if the key can't + be looked up. If not further specified `None` is + returned. + :param type: A callable that is used to cast the value in the + :class:`Headers`. If a :exc:`ValueError` is raised + by this callable the default value is returned. + :param as_bytes: return bytes instead of unicode strings. + """ + try: + rv = self.__getitem__(key, _get_mode=True) + except KeyError: + return default + if as_bytes: + rv = rv.encode('latin1') + if type is None: + return rv + try: + return type(rv) + except ValueError: + return default + + def getlist(self, key, type=None, as_bytes=False): + """Return the list of items for a given key. If that key is not in the + :class:`Headers`, the return value will be an empty list. Just as + :meth:`get` :meth:`getlist` accepts a `type` parameter. All items will + be converted with the callable defined there. + + .. versionadded:: 0.9 + Added support for `as_bytes`. + + :param key: The key to be looked up. + :param type: A callable that is used to cast the value in the + :class:`Headers`. If a :exc:`ValueError` is raised + by this callable the value will be removed from the list. + :return: a :class:`list` of all the values for the key. + :param as_bytes: return bytes instead of unicode strings. + """ + ikey = key.lower() + result = [] + for k, v in self: + if k.lower() == ikey: + if as_bytes: + v = v.encode('latin1') + if type is not None: + try: + v = type(v) + except ValueError: + continue + result.append(v) + return result + + def get_all(self, name): + """Return a list of all the values for the named field. + + This method is compatible with the :mod:`wsgiref` + :meth:`~wsgiref.headers.Headers.get_all` method. + """ + return self.getlist(name) + + def items(self, lower=False): + for key, value in self: + if lower: + key = key.lower() + yield key, value + + def keys(self, lower=False): + for key, _ in iteritems(self, lower): + yield key + + def values(self): + for _, value in iteritems(self): + yield value + + def extend(self, iterable): + """Extend the headers with a dict or an iterable yielding keys and + values. + """ + if isinstance(iterable, dict): + for key, value in iteritems(iterable): + if isinstance(value, (tuple, list)): + for v in value: + self.add(key, v) + else: + self.add(key, value) + else: + for key, value in iterable: + self.add(key, value) + + def __delitem__(self, key, _index_operation=True): + if _index_operation and isinstance(key, (integer_types, slice)): + del self._list[key] + return + key = key.lower() + new = [] + for k, v in self._list: + if k.lower() != key: + new.append((k, v)) + self._list[:] = new + + def remove(self, key): + """Remove a key. + + :param key: The key to be removed. + """ + return self.__delitem__(key, _index_operation=False) + + def pop(self, key=None, default=_missing): + """Removes and returns a key or index. + + :param key: The key to be popped. If this is an integer the item at + that position is removed, if it's a string the value for + that key is. If the key is omitted or `None` the last + item is removed. + :return: an item. + """ + if key is None: + return self._list.pop() + if isinstance(key, integer_types): + return self._list.pop(key) + try: + rv = self[key] + self.remove(key) + except KeyError: + if default is not _missing: + return default + raise + return rv + + def popitem(self): + """Removes a key or index and returns a (key, value) item.""" + return self.pop() + + def __contains__(self, key): + """Check if a key is present.""" + try: + self.__getitem__(key, _get_mode=True) + except KeyError: + return False + return True + + has_key = __contains__ + + def __iter__(self): + """Yield ``(key, value)`` tuples.""" + return iter(self._list) + + def __len__(self): + return len(self._list) + + def add(self, _key, _value, **kw): + """Add a new header tuple to the list. + + Keyword arguments can specify additional parameters for the header + value, with underscores converted to dashes:: + + >>> d = Headers() + >>> d.add('Content-Type', 'text/plain') + >>> d.add('Content-Disposition', 'attachment', filename='foo.png') + + The keyword argument dumping uses :func:`dump_options_header` + behind the scenes. + + .. versionadded:: 0.4.1 + keyword arguments were added for :mod:`wsgiref` compatibility. + """ + if kw: + _value = _options_header_vkw(_value, kw) + _value = _unicodify_header_value(_value) + self._validate_value(_value) + self._list.append((_key, _value)) + + def _validate_value(self, value): + if not isinstance(value, text_type): + raise TypeError('Value should be unicode.') + if u'\n' in value or u'\r' in value: + raise ValueError('Detected newline in header value. This is ' + 'a potential security problem') + + def add_header(self, _key, _value, **_kw): + """Add a new header tuple to the list. + + An alias for :meth:`add` for compatibility with the :mod:`wsgiref` + :meth:`~wsgiref.headers.Headers.add_header` method. + """ + self.add(_key, _value, **_kw) + + def clear(self): + """Clears all headers.""" + del self._list[:] + + def set(self, _key, _value, **kw): + """Remove all header tuples for `key` and add a new one. The newly + added key either appears at the end of the list if there was no + entry or replaces the first one. + + Keyword arguments can specify additional parameters for the header + value, with underscores converted to dashes. See :meth:`add` for + more information. + + .. versionchanged:: 0.6.1 + :meth:`set` now accepts the same arguments as :meth:`add`. + + :param key: The key to be inserted. + :param value: The value to be inserted. + """ + if kw: + _value = _options_header_vkw(_value, kw) + _value = _unicodify_header_value(_value) + self._validate_value(_value) + if not self._list: + self._list.append((_key, _value)) + return + listiter = iter(self._list) + ikey = _key.lower() + for idx, (old_key, old_value) in enumerate(listiter): + if old_key.lower() == ikey: + # replace first ocurrence + self._list[idx] = (_key, _value) + break + else: + self._list.append((_key, _value)) + return + self._list[idx + 1:] = [t for t in listiter if t[0].lower() != ikey] + + def setdefault(self, key, default): + """Returns the value for the key if it is in the dict, otherwise it + returns `default` and sets that value for `key`. + + :param key: The key to be looked up. + :param default: The default value to be returned if the key is not + in the dict. If not further specified it's `None`. + """ + if key in self: + return self[key] + self.set(key, default) + return default + + def __setitem__(self, key, value): + """Like :meth:`set` but also supports index/slice based setting.""" + if isinstance(key, (slice, integer_types)): + if isinstance(key, integer_types): + value = [value] + value = [(k, _unicodify_header_value(v)) for (k, v) in value] + [self._validate_value(v) for (k, v) in value] + if isinstance(key, integer_types): + self._list[key] = value[0] + else: + self._list[key] = value + else: + self.set(key, value) + + def to_list(self, charset='iso-8859-1'): + """Convert the headers into a list suitable for WSGI.""" + from warnings import warn + warn(DeprecationWarning('Method removed, use to_wsgi_list instead'), + stacklevel=2) + return self.to_wsgi_list() + + def to_wsgi_list(self): + """Convert the headers into a list suitable for WSGI. + + The values are byte strings in Python 2 converted to latin1 and unicode + strings in Python 3 for the WSGI server to encode. + + :return: list + """ + if PY2: + return [(to_native(k), v.encode('latin1')) for k, v in self] + return list(self) + + def copy(self): + return self.__class__(self._list) + + def __copy__(self): + return self.copy() + + def __str__(self): + """Returns formatted headers suitable for HTTP transmission.""" + strs = [] + for key, value in self.to_wsgi_list(): + strs.append('%s: %s' % (key, value)) + strs.append('\r\n') + return '\r\n'.join(strs) + + def __repr__(self): + return '%s(%r)' % ( + self.__class__.__name__, + list(self) + ) + + +class ImmutableHeadersMixin(object): + + """Makes a :class:`Headers` immutable. We do not mark them as + hashable though since the only usecase for this datastructure + in Werkzeug is a view on a mutable structure. + + .. versionadded:: 0.5 + + :private: + """ + + def __delitem__(self, key): + is_immutable(self) + + def __setitem__(self, key, value): + is_immutable(self) + set = __setitem__ + + def add(self, item): + is_immutable(self) + remove = add_header = add + + def extend(self, iterable): + is_immutable(self) + + def insert(self, pos, value): + is_immutable(self) + + def pop(self, index=-1): + is_immutable(self) + + def popitem(self): + is_immutable(self) + + def setdefault(self, key, default): + is_immutable(self) + + +class EnvironHeaders(ImmutableHeadersMixin, Headers): + + """Read only version of the headers from a WSGI environment. This + provides the same interface as `Headers` and is constructed from + a WSGI environment. + + From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a + subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will + render a page for a ``400 BAD REQUEST`` if caught in a catch-all for + HTTP exceptions. + """ + + def __init__(self, environ): + self.environ = environ + + def __eq__(self, other): + return self.environ is other.environ + + __hash__ = None + + def __getitem__(self, key, _get_mode=False): + # _get_mode is a no-op for this class as there is no index but + # used because get() calls it. + if not isinstance(key, string_types): + raise KeyError(key) + key = key.upper().replace('-', '_') + if key in ('CONTENT_TYPE', 'CONTENT_LENGTH'): + return _unicodify_header_value(self.environ[key]) + return _unicodify_header_value(self.environ['HTTP_' + key]) + + def __len__(self): + # the iter is necessary because otherwise list calls our + # len which would call list again and so forth. + return len(list(iter(self))) + + def __iter__(self): + for key, value in iteritems(self.environ): + if key.startswith('HTTP_') and key not in \ + ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): + yield (key[5:].replace('_', '-').title(), + _unicodify_header_value(value)) + elif key in ('CONTENT_TYPE', 'CONTENT_LENGTH') and value: + yield (key.replace('_', '-').title(), + _unicodify_header_value(value)) + + def copy(self): + raise TypeError('cannot create %r copies' % self.__class__.__name__) + + +@native_itermethods(['keys', 'values', 'items', 'lists', 'listvalues']) +class CombinedMultiDict(ImmutableMultiDictMixin, MultiDict): + + """A read only :class:`MultiDict` that you can pass multiple :class:`MultiDict` + instances as sequence and it will combine the return values of all wrapped + dicts: + + >>> from werkzeug.datastructures import CombinedMultiDict, MultiDict + >>> post = MultiDict([('foo', 'bar')]) + >>> get = MultiDict([('blub', 'blah')]) + >>> combined = CombinedMultiDict([get, post]) + >>> combined['foo'] + 'bar' + >>> combined['blub'] + 'blah' + + This works for all read operations and will raise a `TypeError` for + methods that usually change data which isn't possible. + + From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a + subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will + render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP + exceptions. + """ + + def __reduce_ex__(self, protocol): + return type(self), (self.dicts,) + + def __init__(self, dicts=None): + self.dicts = dicts or [] + + @classmethod + def fromkeys(cls): + raise TypeError('cannot create %r instances by fromkeys' % + cls.__name__) + + def __getitem__(self, key): + for d in self.dicts: + if key in d: + return d[key] + raise exceptions.BadRequestKeyError(key) + + def get(self, key, default=None, type=None): + for d in self.dicts: + if key in d: + if type is not None: + try: + return type(d[key]) + except ValueError: + continue + return d[key] + return default + + def getlist(self, key, type=None): + rv = [] + for d in self.dicts: + rv.extend(d.getlist(key, type)) + return rv + + def _keys_impl(self): + """This function exists so __len__ can be implemented more efficiently, + saving one list creation from an iterator. + + Using this for Python 2's ``dict.keys`` behavior would be useless since + `dict.keys` in Python 2 returns a list, while we have a set here. + """ + rv = set() + for d in self.dicts: + rv.update(iterkeys(d)) + return rv + + def keys(self): + return iter(self._keys_impl()) + + __iter__ = keys + + def items(self, multi=False): + found = set() + for d in self.dicts: + for key, value in iteritems(d, multi): + if multi: + yield key, value + elif key not in found: + found.add(key) + yield key, value + + def values(self): + for key, value in iteritems(self): + yield value + + def lists(self): + rv = {} + for d in self.dicts: + for key, values in iterlists(d): + rv.setdefault(key, []).extend(values) + return iteritems(rv) + + def listvalues(self): + return (x[1] for x in self.lists()) + + def copy(self): + """Return a shallow copy of this object.""" + return self.__class__(self.dicts[:]) + + def to_dict(self, flat=True): + """Return the contents as regular dict. If `flat` is `True` the + returned dict will only have the first item present, if `flat` is + `False` all values will be returned as lists. + + :param flat: If set to `False` the dict returned will have lists + with all the values in it. Otherwise it will only + contain the first item for each key. + :return: a :class:`dict` + """ + rv = {} + for d in reversed(self.dicts): + rv.update(d.to_dict(flat)) + return rv + + def __len__(self): + return len(self._keys_impl()) + + def __contains__(self, key): + for d in self.dicts: + if key in d: + return True + return False + + has_key = __contains__ + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, self.dicts) + + +class FileMultiDict(MultiDict): + + """A special :class:`MultiDict` that has convenience methods to add + files to it. This is used for :class:`EnvironBuilder` and generally + useful for unittesting. + + .. versionadded:: 0.5 + """ + + def add_file(self, name, file, filename=None, content_type=None): + """Adds a new file to the dict. `file` can be a file name or + a :class:`file`-like or a :class:`FileStorage` object. + + :param name: the name of the field. + :param file: a filename or :class:`file`-like object + :param filename: an optional filename + :param content_type: an optional content type + """ + if isinstance(file, FileStorage): + value = file + else: + if isinstance(file, string_types): + if filename is None: + filename = file + file = open(file, 'rb') + if filename and content_type is None: + content_type = mimetypes.guess_type(filename)[0] or \ + 'application/octet-stream' + value = FileStorage(file, filename, name, content_type) + + self.add(name, value) + + +class ImmutableDict(ImmutableDictMixin, dict): + + """An immutable :class:`dict`. + + .. versionadded:: 0.5 + """ + + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + dict.__repr__(self), + ) + + def copy(self): + """Return a shallow mutable copy of this object. Keep in mind that + the standard library's :func:`copy` function is a no-op for this class + like for any other python immutable type (eg: :class:`tuple`). + """ + return dict(self) + + def __copy__(self): + return self + + +class ImmutableMultiDict(ImmutableMultiDictMixin, MultiDict): + + """An immutable :class:`MultiDict`. + + .. versionadded:: 0.5 + """ + + def copy(self): + """Return a shallow mutable copy of this object. Keep in mind that + the standard library's :func:`copy` function is a no-op for this class + like for any other python immutable type (eg: :class:`tuple`). + """ + return MultiDict(self) + + def __copy__(self): + return self + + +class ImmutableOrderedMultiDict(ImmutableMultiDictMixin, OrderedMultiDict): + + """An immutable :class:`OrderedMultiDict`. + + .. versionadded:: 0.6 + """ + + def _iter_hashitems(self): + return enumerate(iteritems(self, multi=True)) + + def copy(self): + """Return a shallow mutable copy of this object. Keep in mind that + the standard library's :func:`copy` function is a no-op for this class + like for any other python immutable type (eg: :class:`tuple`). + """ + return OrderedMultiDict(self) + + def __copy__(self): + return self + + +@native_itermethods(['values']) +class Accept(ImmutableList): + + """An :class:`Accept` object is just a list subclass for lists of + ``(value, quality)`` tuples. It is automatically sorted by specificity + and quality. + + All :class:`Accept` objects work similar to a list but provide extra + functionality for working with the data. Containment checks are + normalized to the rules of that header: + + >>> a = CharsetAccept([('ISO-8859-1', 1), ('utf-8', 0.7)]) + >>> a.best + 'ISO-8859-1' + >>> 'iso-8859-1' in a + True + >>> 'UTF8' in a + True + >>> 'utf7' in a + False + + To get the quality for an item you can use normal item lookup: + + >>> print a['utf-8'] + 0.7 + >>> a['utf7'] + 0 + + .. versionchanged:: 0.5 + :class:`Accept` objects are forced immutable now. + """ + + def __init__(self, values=()): + if values is None: + list.__init__(self) + self.provided = False + elif isinstance(values, Accept): + self.provided = values.provided + list.__init__(self, values) + else: + self.provided = True + values = sorted(values, key=lambda x: (self._specificity(x[0]), x[1], x[0]), + reverse=True) + list.__init__(self, values) + + def _specificity(self, value): + """Returns a tuple describing the value's specificity.""" + return value != '*', + + def _value_matches(self, value, item): + """Check if a value matches a given accept item.""" + return item == '*' or item.lower() == value.lower() + + def __getitem__(self, key): + """Besides index lookup (getting item n) you can also pass it a string + to get the quality for the item. If the item is not in the list, the + returned quality is ``0``. + """ + if isinstance(key, string_types): + return self.quality(key) + return list.__getitem__(self, key) + + def quality(self, key): + """Returns the quality of the key. + + .. versionadded:: 0.6 + In previous versions you had to use the item-lookup syntax + (eg: ``obj[key]`` instead of ``obj.quality(key)``) + """ + for item, quality in self: + if self._value_matches(key, item): + return quality + return 0 + + def __contains__(self, value): + for item, quality in self: + if self._value_matches(value, item): + return True + return False + + def __repr__(self): + return '%s([%s])' % ( + self.__class__.__name__, + ', '.join('(%r, %s)' % (x, y) for x, y in self) + ) + + def index(self, key): + """Get the position of an entry or raise :exc:`ValueError`. + + :param key: The key to be looked up. + + .. versionchanged:: 0.5 + This used to raise :exc:`IndexError`, which was inconsistent + with the list API. + """ + if isinstance(key, string_types): + for idx, (item, quality) in enumerate(self): + if self._value_matches(key, item): + return idx + raise ValueError(key) + return list.index(self, key) + + def find(self, key): + """Get the position of an entry or return -1. + + :param key: The key to be looked up. + """ + try: + return self.index(key) + except ValueError: + return -1 + + def values(self): + """Iterate over all values.""" + for item in self: + yield item[0] + + def to_header(self): + """Convert the header set into an HTTP header string.""" + result = [] + for value, quality in self: + if quality != 1: + value = '%s;q=%s' % (value, quality) + result.append(value) + return ','.join(result) + + def __str__(self): + return self.to_header() + + def _best_single_match(self, match): + for client_item, quality in self: + if self._value_matches(match, client_item): + # self is sorted by specificity descending, we can exit + return client_item, quality + + def best_match(self, matches, default=None): + """Returns the best match from a list of possible matches based + on the specificity and quality of the client. If two items have the + same quality and specificity, the one is returned that comes first. + + :param matches: a list of matches to check for + :param default: the value that is returned if none match + """ + result = default + best_quality = -1 + best_specificity = (-1,) + for server_item in matches: + match = self._best_single_match(server_item) + if not match: + continue + client_item, quality = match + specificity = self._specificity(client_item) + if quality <= 0 or quality < best_quality: + continue + # better quality or same quality but more specific => better match + if quality > best_quality or specificity > best_specificity: + result = server_item + best_quality = quality + best_specificity = specificity + return result + + @property + def best(self): + """The best match as value.""" + if self: + return self[0][0] + + +class MIMEAccept(Accept): + + """Like :class:`Accept` but with special methods and behavior for + mimetypes. + """ + + def _specificity(self, value): + return tuple(x != '*' for x in value.split('/', 1)) + + def _value_matches(self, value, item): + def _normalize(x): + x = x.lower() + return x == '*' and ('*', '*') or x.split('/', 1) + + # this is from the application which is trusted. to avoid developer + # frustration we actually check these for valid values + if '/' not in value: + raise ValueError('invalid mimetype %r' % value) + value_type, value_subtype = _normalize(value) + if value_type == '*' and value_subtype != '*': + raise ValueError('invalid mimetype %r' % value) + + if '/' not in item: + return False + item_type, item_subtype = _normalize(item) + if item_type == '*' and item_subtype != '*': + return False + return ( + (item_type == item_subtype == '*' or + value_type == value_subtype == '*') or + (item_type == value_type and (item_subtype == '*' or + value_subtype == '*' or + item_subtype == value_subtype)) + ) + + @property + def accept_html(self): + """True if this object accepts HTML.""" + return ( + 'text/html' in self or + 'application/xhtml+xml' in self or + self.accept_xhtml + ) + + @property + def accept_xhtml(self): + """True if this object accepts XHTML.""" + return ( + 'application/xhtml+xml' in self or + 'application/xml' in self + ) + + @property + def accept_json(self): + """True if this object accepts JSON.""" + return 'application/json' in self + + +class LanguageAccept(Accept): + + """Like :class:`Accept` but with normalization for languages.""" + + def _value_matches(self, value, item): + def _normalize(language): + return _locale_delim_re.split(language.lower()) + return item == '*' or _normalize(value) == _normalize(item) + + +class CharsetAccept(Accept): + + """Like :class:`Accept` but with normalization for charsets.""" + + def _value_matches(self, value, item): + def _normalize(name): + try: + return codecs.lookup(name).name + except LookupError: + return name.lower() + return item == '*' or _normalize(value) == _normalize(item) + + +def cache_property(key, empty, type): + """Return a new property object for a cache header. Useful if you + want to add support for a cache extension in a subclass.""" + return property(lambda x: x._get_cache_value(key, empty, type), + lambda x, v: x._set_cache_value(key, v, type), + lambda x: x._del_cache_value(key), + 'accessor for %r' % key) + + +class _CacheControl(UpdateDictMixin, dict): + + """Subclass of a dict that stores values for a Cache-Control header. It + has accessors for all the cache-control directives specified in RFC 2616. + The class does not differentiate between request and response directives. + + Because the cache-control directives in the HTTP header use dashes the + python descriptors use underscores for that. + + To get a header of the :class:`CacheControl` object again you can convert + the object into a string or call the :meth:`to_header` method. If you plan + to subclass it and add your own items have a look at the sourcecode for + that class. + + .. versionchanged:: 0.4 + + Setting `no_cache` or `private` to boolean `True` will set the implicit + none-value which is ``*``: + + >>> cc = ResponseCacheControl() + >>> cc.no_cache = True + >>> cc + + >>> cc.no_cache + '*' + >>> cc.no_cache = None + >>> cc + + + In versions before 0.5 the behavior documented here affected the now + no longer existing `CacheControl` class. + """ + + no_cache = cache_property('no-cache', '*', None) + no_store = cache_property('no-store', None, bool) + max_age = cache_property('max-age', -1, int) + no_transform = cache_property('no-transform', None, None) + + def __init__(self, values=(), on_update=None): + dict.__init__(self, values or ()) + self.on_update = on_update + self.provided = values is not None + + def _get_cache_value(self, key, empty, type): + """Used internally by the accessor properties.""" + if type is bool: + return key in self + if key in self: + value = self[key] + if value is None: + return empty + elif type is not None: + try: + value = type(value) + except ValueError: + pass + return value + + def _set_cache_value(self, key, value, type): + """Used internally by the accessor properties.""" + if type is bool: + if value: + self[key] = None + else: + self.pop(key, None) + else: + if value is None: + self.pop(key) + elif value is True: + self[key] = None + else: + self[key] = value + + def _del_cache_value(self, key): + """Used internally by the accessor properties.""" + if key in self: + del self[key] + + def to_header(self): + """Convert the stored values into a cache control header.""" + return dump_header(self) + + def __str__(self): + return self.to_header() + + def __repr__(self): + return '<%s %s>' % ( + self.__class__.__name__, + " ".join( + "%s=%r" % (k, v) for k, v in sorted(self.items()) + ), + ) + + +class RequestCacheControl(ImmutableDictMixin, _CacheControl): + + """A cache control for requests. This is immutable and gives access + to all the request-relevant cache control headers. + + To get a header of the :class:`RequestCacheControl` object again you can + convert the object into a string or call the :meth:`to_header` method. If + you plan to subclass it and add your own items have a look at the sourcecode + for that class. + + .. versionadded:: 0.5 + In previous versions a `CacheControl` class existed that was used + both for request and response. + """ + + max_stale = cache_property('max-stale', '*', int) + min_fresh = cache_property('min-fresh', '*', int) + no_transform = cache_property('no-transform', None, None) + only_if_cached = cache_property('only-if-cached', None, bool) + + +class ResponseCacheControl(_CacheControl): + + """A cache control for responses. Unlike :class:`RequestCacheControl` + this is mutable and gives access to response-relevant cache control + headers. + + To get a header of the :class:`ResponseCacheControl` object again you can + convert the object into a string or call the :meth:`to_header` method. If + you plan to subclass it and add your own items have a look at the sourcecode + for that class. + + .. versionadded:: 0.5 + In previous versions a `CacheControl` class existed that was used + both for request and response. + """ + + public = cache_property('public', None, bool) + private = cache_property('private', '*', None) + must_revalidate = cache_property('must-revalidate', None, bool) + proxy_revalidate = cache_property('proxy-revalidate', None, bool) + s_maxage = cache_property('s-maxage', None, None) + + +# attach cache_property to the _CacheControl as staticmethod +# so that others can reuse it. +_CacheControl.cache_property = staticmethod(cache_property) + + +class CallbackDict(UpdateDictMixin, dict): + + """A dict that calls a function passed every time something is changed. + The function is passed the dict instance. + """ + + def __init__(self, initial=None, on_update=None): + dict.__init__(self, initial or ()) + self.on_update = on_update + + def __repr__(self): + return '<%s %s>' % ( + self.__class__.__name__, + dict.__repr__(self) + ) + + +class HeaderSet(MutableSet): + + """Similar to the :class:`ETags` class this implements a set-like structure. + Unlike :class:`ETags` this is case insensitive and used for vary, allow, and + content-language headers. + + If not constructed using the :func:`parse_set_header` function the + instantiation works like this: + + >>> hs = HeaderSet(['foo', 'bar', 'baz']) + >>> hs + HeaderSet(['foo', 'bar', 'baz']) + """ + + def __init__(self, headers=None, on_update=None): + self._headers = list(headers or ()) + self._set = set([x.lower() for x in self._headers]) + self.on_update = on_update + + def add(self, header): + """Add a new header to the set.""" + self.update((header,)) + + def remove(self, header): + """Remove a header from the set. This raises an :exc:`KeyError` if the + header is not in the set. + + .. versionchanged:: 0.5 + In older versions a :exc:`IndexError` was raised instead of a + :exc:`KeyError` if the object was missing. + + :param header: the header to be removed. + """ + key = header.lower() + if key not in self._set: + raise KeyError(header) + self._set.remove(key) + for idx, key in enumerate(self._headers): + if key.lower() == header: + del self._headers[idx] + break + if self.on_update is not None: + self.on_update(self) + + def update(self, iterable): + """Add all the headers from the iterable to the set. + + :param iterable: updates the set with the items from the iterable. + """ + inserted_any = False + for header in iterable: + key = header.lower() + if key not in self._set: + self._headers.append(header) + self._set.add(key) + inserted_any = True + if inserted_any and self.on_update is not None: + self.on_update(self) + + def discard(self, header): + """Like :meth:`remove` but ignores errors. + + :param header: the header to be discarded. + """ + try: + return self.remove(header) + except KeyError: + pass + + def find(self, header): + """Return the index of the header in the set or return -1 if not found. + + :param header: the header to be looked up. + """ + header = header.lower() + for idx, item in enumerate(self._headers): + if item.lower() == header: + return idx + return -1 + + def index(self, header): + """Return the index of the header in the set or raise an + :exc:`IndexError`. + + :param header: the header to be looked up. + """ + rv = self.find(header) + if rv < 0: + raise IndexError(header) + return rv + + def clear(self): + """Clear the set.""" + self._set.clear() + del self._headers[:] + if self.on_update is not None: + self.on_update(self) + + def as_set(self, preserve_casing=False): + """Return the set as real python set type. When calling this, all + the items are converted to lowercase and the ordering is lost. + + :param preserve_casing: if set to `True` the items in the set returned + will have the original case like in the + :class:`HeaderSet`, otherwise they will + be lowercase. + """ + if preserve_casing: + return set(self._headers) + return set(self._set) + + def to_header(self): + """Convert the header set into an HTTP header string.""" + return ', '.join(map(quote_header_value, self._headers)) + + def __getitem__(self, idx): + return self._headers[idx] + + def __delitem__(self, idx): + rv = self._headers.pop(idx) + self._set.remove(rv.lower()) + if self.on_update is not None: + self.on_update(self) + + def __setitem__(self, idx, value): + old = self._headers[idx] + self._set.remove(old.lower()) + self._headers[idx] = value + self._set.add(value.lower()) + if self.on_update is not None: + self.on_update(self) + + def __contains__(self, header): + return header.lower() in self._set + + def __len__(self): + return len(self._set) + + def __iter__(self): + return iter(self._headers) + + def __nonzero__(self): + return bool(self._set) + + def __str__(self): + return self.to_header() + + def __repr__(self): + return '%s(%r)' % ( + self.__class__.__name__, + self._headers + ) + + +class ETags(Container, Iterable): + + """A set that can be used to check if one etag is present in a collection + of etags. + """ + + def __init__(self, strong_etags=None, weak_etags=None, star_tag=False): + self._strong = frozenset(not star_tag and strong_etags or ()) + self._weak = frozenset(weak_etags or ()) + self.star_tag = star_tag + + def as_set(self, include_weak=False): + """Convert the `ETags` object into a python set. Per default all the + weak etags are not part of this set.""" + rv = set(self._strong) + if include_weak: + rv.update(self._weak) + return rv + + def is_weak(self, etag): + """Check if an etag is weak.""" + return etag in self._weak + + def is_strong(self, etag): + """Check if an etag is strong.""" + return etag in self._strong + + def contains_weak(self, etag): + """Check if an etag is part of the set including weak and strong tags.""" + return self.is_weak(etag) or self.contains(etag) + + def contains(self, etag): + """Check if an etag is part of the set ignoring weak tags. + It is also possible to use the ``in`` operator. + """ + if self.star_tag: + return True + return self.is_strong(etag) + + def contains_raw(self, etag): + """When passed a quoted tag it will check if this tag is part of the + set. If the tag is weak it is checked against weak and strong tags, + otherwise strong only.""" + etag, weak = unquote_etag(etag) + if weak: + return self.contains_weak(etag) + return self.contains(etag) + + def to_header(self): + """Convert the etags set into a HTTP header string.""" + if self.star_tag: + return '*' + return ', '.join( + ['"%s"' % x for x in self._strong] + + ['W/"%s"' % x for x in self._weak] + ) + + def __call__(self, etag=None, data=None, include_weak=False): + if [etag, data].count(None) != 1: + raise TypeError('either tag or data required, but at least one') + if etag is None: + etag = generate_etag(data) + if include_weak: + if etag in self._weak: + return True + return etag in self._strong + + def __bool__(self): + return bool(self.star_tag or self._strong or self._weak) + + __nonzero__ = __bool__ + + def __str__(self): + return self.to_header() + + def __iter__(self): + return iter(self._strong) + + def __contains__(self, etag): + return self.contains(etag) + + def __repr__(self): + return '<%s %r>' % (self.__class__.__name__, str(self)) + + +class IfRange(object): + + """Very simple object that represents the `If-Range` header in parsed + form. It will either have neither a etag or date or one of either but + never both. + + .. versionadded:: 0.7 + """ + + def __init__(self, etag=None, date=None): + #: The etag parsed and unquoted. Ranges always operate on strong + #: etags so the weakness information is not necessary. + self.etag = etag + #: The date in parsed format or `None`. + self.date = date + + def to_header(self): + """Converts the object back into an HTTP header.""" + if self.date is not None: + return http_date(self.date) + if self.etag is not None: + return quote_etag(self.etag) + return '' + + def __str__(self): + return self.to_header() + + def __repr__(self): + return '<%s %r>' % (self.__class__.__name__, str(self)) + + +class Range(object): + + """Represents a range header. All the methods are only supporting bytes + as unit. It does store multiple ranges but :meth:`range_for_length` will + only work if only one range is provided. + + .. versionadded:: 0.7 + """ + + def __init__(self, units, ranges): + #: The units of this range. Usually "bytes". + self.units = units + #: A list of ``(begin, end)`` tuples for the range header provided. + #: The ranges are non-inclusive. + self.ranges = ranges + + def range_for_length(self, length): + """If the range is for bytes, the length is not None and there is + exactly one range and it is satisfiable it returns a ``(start, stop)`` + tuple, otherwise `None`. + """ + if self.units != 'bytes' or length is None or len(self.ranges) != 1: + return None + start, end = self.ranges[0] + if end is None: + end = length + if start < 0: + start += length + if is_byte_range_valid(start, end, length): + return start, min(end, length) + + def make_content_range(self, length): + """Creates a :class:`~werkzeug.datastructures.ContentRange` object + from the current range and given content length. + """ + rng = self.range_for_length(length) + if rng is not None: + return ContentRange(self.units, rng[0], rng[1], length) + + def to_header(self): + """Converts the object back into an HTTP header.""" + ranges = [] + for begin, end in self.ranges: + if end is None: + ranges.append(begin >= 0 and '%s-' % begin or str(begin)) + else: + ranges.append('%s-%s' % (begin, end - 1)) + return '%s=%s' % (self.units, ','.join(ranges)) + + def to_content_range_header(self, length): + """Converts the object into `Content-Range` HTTP header, + based on given length + """ + range_for_length = self.range_for_length(length) + if range_for_length is not None: + return '%s %d-%d/%d' % (self.units, + range_for_length[0], + range_for_length[1] - 1, length) + return None + + def __str__(self): + return self.to_header() + + def __repr__(self): + return '<%s %r>' % (self.__class__.__name__, str(self)) + + +class ContentRange(object): + + """Represents the content range header. + + .. versionadded:: 0.7 + """ + + def __init__(self, units, start, stop, length=None, on_update=None): + assert is_byte_range_valid(start, stop, length), \ + 'Bad range provided' + self.on_update = on_update + self.set(start, stop, length, units) + + def _callback_property(name): + def fget(self): + return getattr(self, name) + + def fset(self, value): + setattr(self, name, value) + if self.on_update is not None: + self.on_update(self) + return property(fget, fset) + + #: The units to use, usually "bytes" + units = _callback_property('_units') + #: The start point of the range or `None`. + start = _callback_property('_start') + #: The stop point of the range (non-inclusive) or `None`. Can only be + #: `None` if also start is `None`. + stop = _callback_property('_stop') + #: The length of the range or `None`. + length = _callback_property('_length') + + def set(self, start, stop, length=None, units='bytes'): + """Simple method to update the ranges.""" + assert is_byte_range_valid(start, stop, length), \ + 'Bad range provided' + self._units = units + self._start = start + self._stop = stop + self._length = length + if self.on_update is not None: + self.on_update(self) + + def unset(self): + """Sets the units to `None` which indicates that the header should + no longer be used. + """ + self.set(None, None, units=None) + + def to_header(self): + if self.units is None: + return '' + if self.length is None: + length = '*' + else: + length = self.length + if self.start is None: + return '%s */%s' % (self.units, length) + return '%s %s-%s/%s' % ( + self.units, + self.start, + self.stop - 1, + length + ) + + def __nonzero__(self): + return self.units is not None + + __bool__ = __nonzero__ + + def __str__(self): + return self.to_header() + + def __repr__(self): + return '<%s %r>' % (self.__class__.__name__, str(self)) + + +class Authorization(ImmutableDictMixin, dict): + + """Represents an `Authorization` header sent by the client. You should + not create this kind of object yourself but use it when it's returned by + the `parse_authorization_header` function. + + This object is a dict subclass and can be altered by setting dict items + but it should be considered immutable as it's returned by the client and + not meant for modifications. + + .. versionchanged:: 0.5 + This object became immutable. + """ + + def __init__(self, auth_type, data=None): + dict.__init__(self, data or {}) + self.type = auth_type + + username = property(lambda x: x.get('username'), doc=''' + The username transmitted. This is set for both basic and digest + auth all the time.''') + password = property(lambda x: x.get('password'), doc=''' + When the authentication type is basic this is the password + transmitted by the client, else `None`.''') + realm = property(lambda x: x.get('realm'), doc=''' + This is the server realm sent back for HTTP digest auth.''') + nonce = property(lambda x: x.get('nonce'), doc=''' + The nonce the server sent for digest auth, sent back by the client. + A nonce should be unique for every 401 response for HTTP digest + auth.''') + uri = property(lambda x: x.get('uri'), doc=''' + The URI from Request-URI of the Request-Line; duplicated because + proxies are allowed to change the Request-Line in transit. HTTP + digest auth only.''') + nc = property(lambda x: x.get('nc'), doc=''' + The nonce count value transmitted by clients if a qop-header is + also transmitted. HTTP digest auth only.''') + cnonce = property(lambda x: x.get('cnonce'), doc=''' + If the server sent a qop-header in the ``WWW-Authenticate`` + header, the client has to provide this value for HTTP digest auth. + See the RFC for more details.''') + response = property(lambda x: x.get('response'), doc=''' + A string of 32 hex digits computed as defined in RFC 2617, which + proves that the user knows a password. Digest auth only.''') + opaque = property(lambda x: x.get('opaque'), doc=''' + The opaque header from the server returned unchanged by the client. + It is recommended that this string be base64 or hexadecimal data. + Digest auth only.''') + qop = property(lambda x: x.get('qop'), doc=''' + Indicates what "quality of protection" the client has applied to + the message for HTTP digest auth. Note that this is a single token, + not a quoted list of alternatives as in WWW-Authenticate.''') + + +class WWWAuthenticate(UpdateDictMixin, dict): + + """Provides simple access to `WWW-Authenticate` headers.""" + + #: list of keys that require quoting in the generated header + _require_quoting = frozenset(['domain', 'nonce', 'opaque', 'realm', 'qop']) + + def __init__(self, auth_type=None, values=None, on_update=None): + dict.__init__(self, values or ()) + if auth_type: + self['__auth_type__'] = auth_type + self.on_update = on_update + + def set_basic(self, realm='authentication required'): + """Clear the auth info and enable basic auth.""" + dict.clear(self) + dict.update(self, {'__auth_type__': 'basic', 'realm': realm}) + if self.on_update: + self.on_update(self) + + def set_digest(self, realm, nonce, qop=('auth',), opaque=None, + algorithm=None, stale=False): + """Clear the auth info and enable digest auth.""" + d = { + '__auth_type__': 'digest', + 'realm': realm, + 'nonce': nonce, + 'qop': dump_header(qop) + } + if stale: + d['stale'] = 'TRUE' + if opaque is not None: + d['opaque'] = opaque + if algorithm is not None: + d['algorithm'] = algorithm + dict.clear(self) + dict.update(self, d) + if self.on_update: + self.on_update(self) + + def to_header(self): + """Convert the stored values into a WWW-Authenticate header.""" + d = dict(self) + auth_type = d.pop('__auth_type__', None) or 'basic' + return '%s %s' % (auth_type.title(), ', '.join([ + '%s=%s' % (key, quote_header_value(value, + allow_token=key not in self._require_quoting)) + for key, value in iteritems(d) + ])) + + def __str__(self): + return self.to_header() + + def __repr__(self): + return '<%s %r>' % ( + self.__class__.__name__, + self.to_header() + ) + + def auth_property(name, doc=None): + """A static helper function for subclasses to add extra authentication + system properties onto a class:: + + class FooAuthenticate(WWWAuthenticate): + special_realm = auth_property('special_realm') + + For more information have a look at the sourcecode to see how the + regular properties (:attr:`realm` etc.) are implemented. + """ + + def _set_value(self, value): + if value is None: + self.pop(name, None) + else: + self[name] = str(value) + return property(lambda x: x.get(name), _set_value, doc=doc) + + def _set_property(name, doc=None): + def fget(self): + def on_update(header_set): + if not header_set and name in self: + del self[name] + elif header_set: + self[name] = header_set.to_header() + return parse_set_header(self.get(name), on_update) + return property(fget, doc=doc) + + type = auth_property('__auth_type__', doc=''' + The type of the auth mechanism. HTTP currently specifies + `Basic` and `Digest`.''') + realm = auth_property('realm', doc=''' + A string to be displayed to users so they know which username and + password to use. This string should contain at least the name of + the host performing the authentication and might additionally + indicate the collection of users who might have access.''') + domain = _set_property('domain', doc=''' + A list of URIs that define the protection space. If a URI is an + absolute path, it is relative to the canonical root URL of the + server being accessed.''') + nonce = auth_property('nonce', doc=''' + A server-specified data string which should be uniquely generated + each time a 401 response is made. It is recommended that this + string be base64 or hexadecimal data.''') + opaque = auth_property('opaque', doc=''' + A string of data, specified by the server, which should be returned + by the client unchanged in the Authorization header of subsequent + requests with URIs in the same protection space. It is recommended + that this string be base64 or hexadecimal data.''') + algorithm = auth_property('algorithm', doc=''' + A string indicating a pair of algorithms used to produce the digest + and a checksum. If this is not present it is assumed to be "MD5". + If the algorithm is not understood, the challenge should be ignored + (and a different one used, if there is more than one).''') + qop = _set_property('qop', doc=''' + A set of quality-of-privacy directives such as auth and auth-int.''') + + def _get_stale(self): + val = self.get('stale') + if val is not None: + return val.lower() == 'true' + + def _set_stale(self, value): + if value is None: + self.pop('stale', None) + else: + self['stale'] = value and 'TRUE' or 'FALSE' + stale = property(_get_stale, _set_stale, doc=''' + A flag, indicating that the previous request from the client was + rejected because the nonce value was stale.''') + del _get_stale, _set_stale + + # make auth_property a staticmethod so that subclasses of + # `WWWAuthenticate` can use it for new properties. + auth_property = staticmethod(auth_property) + del _set_property + + +class FileStorage(object): + + """The :class:`FileStorage` class is a thin wrapper over incoming files. + It is used by the request object to represent uploaded files. All the + attributes of the wrapper stream are proxied by the file storage so + it's possible to do ``storage.read()`` instead of the long form + ``storage.stream.read()``. + """ + + def __init__(self, stream=None, filename=None, name=None, + content_type=None, content_length=None, + headers=None): + self.name = name + self.stream = stream or _empty_stream + + # if no filename is provided we can attempt to get the filename + # from the stream object passed. There we have to be careful to + # skip things like , etc. Python marks these + # special filenames with angular brackets. + if filename is None: + filename = getattr(stream, 'name', None) + s = make_literal_wrapper(filename) + if filename and filename[0] == s('<') and filename[-1] == s('>'): + filename = None + + # On Python 3 we want to make sure the filename is always unicode. + # This might not be if the name attribute is bytes due to the + # file being opened from the bytes API. + if not PY2 and isinstance(filename, bytes): + filename = filename.decode(get_filesystem_encoding(), + 'replace') + + self.filename = filename + if headers is None: + headers = Headers() + self.headers = headers + if content_type is not None: + headers['Content-Type'] = content_type + if content_length is not None: + headers['Content-Length'] = str(content_length) + + def _parse_content_type(self): + if not hasattr(self, '_parsed_content_type'): + self._parsed_content_type = \ + parse_options_header(self.content_type) + + @property + def content_type(self): + """The content-type sent in the header. Usually not available""" + return self.headers.get('content-type') + + @property + def content_length(self): + """The content-length sent in the header. Usually not available""" + return int(self.headers.get('content-length') or 0) + + @property + def mimetype(self): + """Like :attr:`content_type`, but without parameters (eg, without + charset, type etc.) and always lowercase. For example if the content + type is ``text/HTML; charset=utf-8`` the mimetype would be + ``'text/html'``. + + .. versionadded:: 0.7 + """ + self._parse_content_type() + return self._parsed_content_type[0].lower() + + @property + def mimetype_params(self): + """The mimetype parameters as dict. For example if the content + type is ``text/html; charset=utf-8`` the params would be + ``{'charset': 'utf-8'}``. + + .. versionadded:: 0.7 + """ + self._parse_content_type() + return self._parsed_content_type[1] + + def save(self, dst, buffer_size=16384): + """Save the file to a destination path or file object. If the + destination is a file object you have to close it yourself after the + call. The buffer size is the number of bytes held in memory during + the copy process. It defaults to 16KB. + + For secure file saving also have a look at :func:`secure_filename`. + + :param dst: a filename or open file object the uploaded file + is saved to. + :param buffer_size: the size of the buffer. This works the same as + the `length` parameter of + :func:`shutil.copyfileobj`. + """ + from shutil import copyfileobj + close_dst = False + if isinstance(dst, string_types): + dst = open(dst, 'wb') + close_dst = True + try: + copyfileobj(self.stream, dst, buffer_size) + finally: + if close_dst: + dst.close() + + def close(self): + """Close the underlying file if possible.""" + try: + self.stream.close() + except Exception: + pass + + def __nonzero__(self): + return bool(self.filename) + __bool__ = __nonzero__ + + def __getattr__(self, name): + return getattr(self.stream, name) + + def __iter__(self): + return iter(self.stream) + + def __repr__(self): + return '<%s: %r (%r)>' % ( + self.__class__.__name__, + self.filename, + self.content_type + ) + + +# circular dependencies +from werkzeug.http import dump_options_header, dump_header, generate_etag, \ + quote_header_value, parse_set_header, unquote_etag, quote_etag, \ + parse_options_header, http_date, is_byte_range_valid +from werkzeug import exceptions diff --git a/venv/Lib/site-packages/werkzeug/debug/__init__.py b/venv/Lib/site-packages/werkzeug/debug/__init__.py new file mode 100644 index 0000000..14fe193 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/debug/__init__.py @@ -0,0 +1,470 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.debug + ~~~~~~~~~~~~~~ + + WSGI application traceback debugger. + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +import os +import re +import sys +import uuid +import json +import time +import getpass +import hashlib +import mimetypes +from itertools import chain +from os.path import join, dirname, basename, isfile +from werkzeug.wrappers import BaseRequest as Request, BaseResponse as Response +from werkzeug.http import parse_cookie +from werkzeug.debug.tbtools import get_current_traceback, render_console_html +from werkzeug.debug.console import Console +from werkzeug.security import gen_salt +from werkzeug._internal import _log +from werkzeug._compat import text_type + + +# DEPRECATED +#: import this here because it once was documented as being available +#: from this module. In case there are users left ... +from werkzeug.debug.repr import debug_repr # noqa + + +# A week +PIN_TIME = 60 * 60 * 24 * 7 + + +def hash_pin(pin): + if isinstance(pin, text_type): + pin = pin.encode('utf-8', 'replace') + return hashlib.md5(pin + b'shittysalt').hexdigest()[:12] + + +_machine_id = None + + +def get_machine_id(): + global _machine_id + rv = _machine_id + if rv is not None: + return rv + + def _generate(): + # Potential sources of secret information on linux. The machine-id + # is stable across boots, the boot id is not + for filename in '/etc/machine-id', '/proc/sys/kernel/random/boot_id': + try: + with open(filename, 'rb') as f: + return f.readline().strip() + except IOError: + continue + + # On OS X we can use the computer's serial number assuming that + # ioreg exists and can spit out that information. + try: + # Also catch import errors: subprocess may not be available, e.g. + # Google App Engine + # See https://github.com/pallets/werkzeug/issues/925 + from subprocess import Popen, PIPE + dump = Popen(['ioreg', '-c', 'IOPlatformExpertDevice', '-d', '2'], + stdout=PIPE).communicate()[0] + match = re.search(b'"serial-number" = <([^>]+)', dump) + if match is not None: + return match.group(1) + except (OSError, ImportError): + pass + + # On Windows we can use winreg to get the machine guid + wr = None + try: + import winreg as wr + except ImportError: + try: + import _winreg as wr + except ImportError: + pass + if wr is not None: + try: + with wr.OpenKey(wr.HKEY_LOCAL_MACHINE, + 'SOFTWARE\\Microsoft\\Cryptography', 0, + wr.KEY_READ | wr.KEY_WOW64_64KEY) as rk: + machineGuid, wrType = wr.QueryValueEx(rk, 'MachineGuid') + if (wrType == wr.REG_SZ): + return machineGuid.encode('utf-8') + else: + return machineGuid + except WindowsError: + pass + + _machine_id = rv = _generate() + return rv + + +class _ConsoleFrame(object): + + """Helper class so that we can reuse the frame console code for the + standalone console. + """ + + def __init__(self, namespace): + self.console = Console(namespace) + self.id = 0 + + +def get_pin_and_cookie_name(app): + """Given an application object this returns a semi-stable 9 digit pin + code and a random key. The hope is that this is stable between + restarts to not make debugging particularly frustrating. If the pin + was forcefully disabled this returns `None`. + + Second item in the resulting tuple is the cookie name for remembering. + """ + pin = os.environ.get('WERKZEUG_DEBUG_PIN') + rv = None + num = None + + # Pin was explicitly disabled + if pin == 'off': + return None, None + + # Pin was provided explicitly + if pin is not None and pin.replace('-', '').isdigit(): + # If there are separators in the pin, return it directly + if '-' in pin: + rv = pin + else: + num = pin + + modname = getattr(app, '__module__', + getattr(app.__class__, '__module__')) + + try: + # `getpass.getuser()` imports the `pwd` module, + # which does not exist in the Google App Engine sandbox. + username = getpass.getuser() + except ImportError: + username = None + + mod = sys.modules.get(modname) + + # This information only exists to make the cookie unique on the + # computer, not as a security feature. + probably_public_bits = [ + username, + modname, + getattr(app, '__name__', getattr(app.__class__, '__name__')), + getattr(mod, '__file__', None), + ] + + # This information is here to make it harder for an attacker to + # guess the cookie name. They are unlikely to be contained anywhere + # within the unauthenticated debug page. + private_bits = [ + str(uuid.getnode()), + get_machine_id(), + ] + + h = hashlib.md5() + for bit in chain(probably_public_bits, private_bits): + if not bit: + continue + if isinstance(bit, text_type): + bit = bit.encode('utf-8') + h.update(bit) + h.update(b'cookiesalt') + + cookie_name = '__wzd' + h.hexdigest()[:20] + + # If we need to generate a pin we salt it a bit more so that we don't + # end up with the same value and generate out 9 digits + if num is None: + h.update(b'pinsalt') + num = ('%09d' % int(h.hexdigest(), 16))[:9] + + # Format the pincode in groups of digits for easier remembering if + # we don't have a result yet. + if rv is None: + for group_size in 5, 4, 3: + if len(num) % group_size == 0: + rv = '-'.join(num[x:x + group_size].rjust(group_size, '0') + for x in range(0, len(num), group_size)) + break + else: + rv = num + + return rv, cookie_name + + +class DebuggedApplication(object): + """Enables debugging support for a given application:: + + from werkzeug.debug import DebuggedApplication + from myapp import app + app = DebuggedApplication(app, evalex=True) + + The `evalex` keyword argument allows evaluating expressions in a + traceback's frame context. + + .. versionadded:: 0.9 + The `lodgeit_url` parameter was deprecated. + + :param app: the WSGI application to run debugged. + :param evalex: enable exception evaluation feature (interactive + debugging). This requires a non-forking server. + :param request_key: The key that points to the request object in ths + environment. This parameter is ignored in current + versions. + :param console_path: the URL for a general purpose console. + :param console_init_func: the function that is executed before starting + the general purpose console. The return value + is used as initial namespace. + :param show_hidden_frames: by default hidden traceback frames are skipped. + You can show them by setting this parameter + to `True`. + :param pin_security: can be used to disable the pin based security system. + :param pin_logging: enables the logging of the pin system. + """ + + def __init__(self, app, evalex=False, request_key='werkzeug.request', + console_path='/console', console_init_func=None, + show_hidden_frames=False, lodgeit_url=None, + pin_security=True, pin_logging=True): + if lodgeit_url is not None: + from warnings import warn + warn(DeprecationWarning('Werkzeug now pastes into gists.')) + if not console_init_func: + console_init_func = None + self.app = app + self.evalex = evalex + self.frames = {} + self.tracebacks = {} + self.request_key = request_key + self.console_path = console_path + self.console_init_func = console_init_func + self.show_hidden_frames = show_hidden_frames + self.secret = gen_salt(20) + self._failed_pin_auth = 0 + + self.pin_logging = pin_logging + if pin_security: + # Print out the pin for the debugger on standard out. + if os.environ.get('WERKZEUG_RUN_MAIN') == 'true' and \ + pin_logging: + _log('warning', ' * Debugger is active!') + if self.pin is None: + _log('warning', ' * Debugger PIN disabled. ' + 'DEBUGGER UNSECURED!') + else: + _log('info', ' * Debugger PIN: %s' % self.pin) + else: + self.pin = None + + def _get_pin(self): + if not hasattr(self, '_pin'): + self._pin, self._pin_cookie = get_pin_and_cookie_name(self.app) + return self._pin + + def _set_pin(self, value): + self._pin = value + + pin = property(_get_pin, _set_pin) + del _get_pin, _set_pin + + @property + def pin_cookie_name(self): + """The name of the pin cookie.""" + if not hasattr(self, '_pin_cookie'): + self._pin, self._pin_cookie = get_pin_and_cookie_name(self.app) + return self._pin_cookie + + def debug_application(self, environ, start_response): + """Run the application and conserve the traceback frames.""" + app_iter = None + try: + app_iter = self.app(environ, start_response) + for item in app_iter: + yield item + if hasattr(app_iter, 'close'): + app_iter.close() + except Exception: + if hasattr(app_iter, 'close'): + app_iter.close() + traceback = get_current_traceback( + skip=1, show_hidden_frames=self.show_hidden_frames, + ignore_system_exceptions=True) + for frame in traceback.frames: + self.frames[frame.id] = frame + self.tracebacks[traceback.id] = traceback + + try: + start_response('500 INTERNAL SERVER ERROR', [ + ('Content-Type', 'text/html; charset=utf-8'), + # Disable Chrome's XSS protection, the debug + # output can cause false-positives. + ('X-XSS-Protection', '0'), + ]) + except Exception: + # if we end up here there has been output but an error + # occurred. in that situation we can do nothing fancy any + # more, better log something into the error log and fall + # back gracefully. + environ['wsgi.errors'].write( + 'Debugging middleware caught exception in streamed ' + 'response at a point where response headers were already ' + 'sent.\n') + else: + is_trusted = bool(self.check_pin_trust(environ)) + yield traceback.render_full(evalex=self.evalex, + evalex_trusted=is_trusted, + secret=self.secret) \ + .encode('utf-8', 'replace') + + traceback.log(environ['wsgi.errors']) + + def execute_command(self, request, command, frame): + """Execute a command in a console.""" + return Response(frame.console.eval(command), mimetype='text/html') + + def display_console(self, request): + """Display a standalone shell.""" + if 0 not in self.frames: + if self.console_init_func is None: + ns = {} + else: + ns = dict(self.console_init_func()) + ns.setdefault('app', self.app) + self.frames[0] = _ConsoleFrame(ns) + is_trusted = bool(self.check_pin_trust(request.environ)) + return Response(render_console_html(secret=self.secret, + evalex_trusted=is_trusted), + mimetype='text/html') + + def paste_traceback(self, request, traceback): + """Paste the traceback and return a JSON response.""" + rv = traceback.paste() + return Response(json.dumps(rv), mimetype='application/json') + + def get_resource(self, request, filename): + """Return a static resource from the shared folder.""" + filename = join(dirname(__file__), 'shared', basename(filename)) + if isfile(filename): + mimetype = mimetypes.guess_type(filename)[0] \ + or 'application/octet-stream' + f = open(filename, 'rb') + try: + return Response(f.read(), mimetype=mimetype) + finally: + f.close() + return Response('Not Found', status=404) + + def check_pin_trust(self, environ): + """Checks if the request passed the pin test. This returns `True` if the + request is trusted on a pin/cookie basis and returns `False` if not. + Additionally if the cookie's stored pin hash is wrong it will return + `None` so that appropriate action can be taken. + """ + if self.pin is None: + return True + val = parse_cookie(environ).get(self.pin_cookie_name) + if not val or '|' not in val: + return False + ts, pin_hash = val.split('|', 1) + if not ts.isdigit(): + return False + if pin_hash != hash_pin(self.pin): + return None + return (time.time() - PIN_TIME) < int(ts) + + def _fail_pin_auth(self): + time.sleep(self._failed_pin_auth > 5 and 5.0 or 0.5) + self._failed_pin_auth += 1 + + def pin_auth(self, request): + """Authenticates with the pin.""" + exhausted = False + auth = False + trust = self.check_pin_trust(request.environ) + + # If the trust return value is `None` it means that the cookie is + # set but the stored pin hash value is bad. This means that the + # pin was changed. In this case we count a bad auth and unset the + # cookie. This way it becomes harder to guess the cookie name + # instead of the pin as we still count up failures. + bad_cookie = False + if trust is None: + self._fail_pin_auth() + bad_cookie = True + + # If we're trusted, we're authenticated. + elif trust: + auth = True + + # If we failed too many times, then we're locked out. + elif self._failed_pin_auth > 10: + exhausted = True + + # Otherwise go through pin based authentication + else: + entered_pin = request.args.get('pin') + if entered_pin.strip().replace('-', '') == \ + self.pin.replace('-', ''): + self._failed_pin_auth = 0 + auth = True + else: + self._fail_pin_auth() + + rv = Response(json.dumps({ + 'auth': auth, + 'exhausted': exhausted, + }), mimetype='application/json') + if auth: + rv.set_cookie(self.pin_cookie_name, '%s|%s' % ( + int(time.time()), + hash_pin(self.pin) + ), httponly=True) + elif bad_cookie: + rv.delete_cookie(self.pin_cookie_name) + return rv + + def log_pin_request(self): + """Log the pin if needed.""" + if self.pin_logging and self.pin is not None: + _log('info', ' * To enable the debugger you need to ' + 'enter the security pin:') + _log('info', ' * Debugger pin code: %s' % self.pin) + return Response('') + + def __call__(self, environ, start_response): + """Dispatch the requests.""" + # important: don't ever access a function here that reads the incoming + # form data! Otherwise the application won't have access to that data + # any more! + request = Request(environ) + response = self.debug_application + if request.args.get('__debugger__') == 'yes': + cmd = request.args.get('cmd') + arg = request.args.get('f') + secret = request.args.get('s') + traceback = self.tracebacks.get(request.args.get('tb', type=int)) + frame = self.frames.get(request.args.get('frm', type=int)) + if cmd == 'resource' and arg: + response = self.get_resource(request, arg) + elif cmd == 'paste' and traceback is not None and \ + secret == self.secret: + response = self.paste_traceback(request, traceback) + elif cmd == 'pinauth' and secret == self.secret: + response = self.pin_auth(request) + elif cmd == 'printpin' and secret == self.secret: + response = self.log_pin_request() + elif self.evalex and cmd is not None and frame is not None \ + and self.secret == secret and \ + self.check_pin_trust(environ): + response = self.execute_command(request, cmd, frame) + elif self.evalex and self.console_path is not None and \ + request.path == self.console_path: + response = self.display_console(request) + return response(environ, start_response) diff --git a/venv/Lib/site-packages/werkzeug/debug/console.py b/venv/Lib/site-packages/werkzeug/debug/console.py new file mode 100644 index 0000000..30e8906 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/debug/console.py @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.debug.console + ~~~~~~~~~~~~~~~~~~~~~~ + + Interactive console support. + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD. +""" +import sys +import code +from types import CodeType + +from werkzeug.utils import escape +from werkzeug.local import Local +from werkzeug.debug.repr import debug_repr, dump, helper + + +_local = Local() + + +class HTMLStringO(object): + + """A StringO version that HTML escapes on write.""" + + def __init__(self): + self._buffer = [] + + def isatty(self): + return False + + def close(self): + pass + + def flush(self): + pass + + def seek(self, n, mode=0): + pass + + def readline(self): + if len(self._buffer) == 0: + return '' + ret = self._buffer[0] + del self._buffer[0] + return ret + + def reset(self): + val = ''.join(self._buffer) + del self._buffer[:] + return val + + def _write(self, x): + if isinstance(x, bytes): + x = x.decode('utf-8', 'replace') + self._buffer.append(x) + + def write(self, x): + self._write(escape(x)) + + def writelines(self, x): + self._write(escape(''.join(x))) + + +class ThreadedStream(object): + + """Thread-local wrapper for sys.stdout for the interactive console.""" + + def push(): + if not isinstance(sys.stdout, ThreadedStream): + sys.stdout = ThreadedStream() + _local.stream = HTMLStringO() + push = staticmethod(push) + + def fetch(): + try: + stream = _local.stream + except AttributeError: + return '' + return stream.reset() + fetch = staticmethod(fetch) + + def displayhook(obj): + try: + stream = _local.stream + except AttributeError: + return _displayhook(obj) + # stream._write bypasses escaping as debug_repr is + # already generating HTML for us. + if obj is not None: + _local._current_ipy.locals['_'] = obj + stream._write(debug_repr(obj)) + displayhook = staticmethod(displayhook) + + def __setattr__(self, name, value): + raise AttributeError('read only attribute %s' % name) + + def __dir__(self): + return dir(sys.__stdout__) + + def __getattribute__(self, name): + if name == '__members__': + return dir(sys.__stdout__) + try: + stream = _local.stream + except AttributeError: + stream = sys.__stdout__ + return getattr(stream, name) + + def __repr__(self): + return repr(sys.__stdout__) + + +# add the threaded stream as display hook +_displayhook = sys.displayhook +sys.displayhook = ThreadedStream.displayhook + + +class _ConsoleLoader(object): + + def __init__(self): + self._storage = {} + + def register(self, code, source): + self._storage[id(code)] = source + # register code objects of wrapped functions too. + for var in code.co_consts: + if isinstance(var, CodeType): + self._storage[id(var)] = source + + def get_source_by_code(self, code): + try: + return self._storage[id(code)] + except KeyError: + pass + + +def _wrap_compiler(console): + compile = console.compile + + def func(source, filename, symbol): + code = compile(source, filename, symbol) + console.loader.register(code, source) + return code + console.compile = func + + +class _InteractiveConsole(code.InteractiveInterpreter): + + def __init__(self, globals, locals): + code.InteractiveInterpreter.__init__(self, locals) + self.globals = dict(globals) + self.globals['dump'] = dump + self.globals['help'] = helper + self.globals['__loader__'] = self.loader = _ConsoleLoader() + self.more = False + self.buffer = [] + _wrap_compiler(self) + + def runsource(self, source): + source = source.rstrip() + '\n' + ThreadedStream.push() + prompt = self.more and '... ' or '>>> ' + try: + source_to_eval = ''.join(self.buffer + [source]) + if code.InteractiveInterpreter.runsource(self, + source_to_eval, '', 'single'): + self.more = True + self.buffer.append(source) + else: + self.more = False + del self.buffer[:] + finally: + output = ThreadedStream.fetch() + return prompt + escape(source) + output + + def runcode(self, code): + try: + eval(code, self.globals, self.locals) + except Exception: + self.showtraceback() + + def showtraceback(self): + from werkzeug.debug.tbtools import get_current_traceback + tb = get_current_traceback(skip=1) + sys.stdout._write(tb.render_summary()) + + def showsyntaxerror(self, filename=None): + from werkzeug.debug.tbtools import get_current_traceback + tb = get_current_traceback(skip=4) + sys.stdout._write(tb.render_summary()) + + def write(self, data): + sys.stdout.write(data) + + +class Console(object): + + """An interactive console.""" + + def __init__(self, globals=None, locals=None): + if locals is None: + locals = {} + if globals is None: + globals = {} + self._ipy = _InteractiveConsole(globals, locals) + + def eval(self, code): + _local._current_ipy = self._ipy + old_sys_stdout = sys.stdout + try: + return self._ipy.runsource(code) + finally: + sys.stdout = old_sys_stdout diff --git a/venv/Lib/site-packages/werkzeug/debug/repr.py b/venv/Lib/site-packages/werkzeug/debug/repr.py new file mode 100644 index 0000000..0a02457 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/debug/repr.py @@ -0,0 +1,280 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.debug.repr + ~~~~~~~~~~~~~~~~~~~ + + This module implements object representations for debugging purposes. + Unlike the default repr these reprs expose a lot more information and + produce HTML instead of ASCII. + + Together with the CSS and JavaScript files of the debugger this gives + a colorful and more compact output. + + :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. + :license: BSD. +""" +import sys +import re +import codecs +from traceback import format_exception_only +try: + from collections import deque +except ImportError: # pragma: no cover + deque = None +from werkzeug.utils import escape +from werkzeug._compat import iteritems, PY2, text_type, integer_types, \ + string_types + + +missing = object() +_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}') +RegexType = type(_paragraph_re) + + +HELP_HTML = '''\ +
            +

            %(title)s

            +
            %(text)s
            +
            \ +''' +OBJECT_DUMP_HTML = '''\ +
            +

            %(title)s

            + %(repr)s + %(items)s
            +
            \ +''' + + +def debug_repr(obj): + """Creates a debug repr of an object as HTML unicode string.""" + return DebugReprGenerator().repr(obj) + + +def dump(obj=missing): + """Print the object details to stdout._write (for the interactive + console of the web debugger. + """ + gen = DebugReprGenerator() + if obj is missing: + rv = gen.dump_locals(sys._getframe(1).f_locals) + else: + rv = gen.dump_object(obj) + sys.stdout._write(rv) + + +class _Helper(object): + + """Displays an HTML version of the normal help, for the interactive + debugger only because it requires a patched sys.stdout. + """ + + def __repr__(self): + return 'Type help(object) for help about object.' + + def __call__(self, topic=None): + if topic is None: + sys.stdout._write('%s' % repr(self)) + return + import pydoc + pydoc.help(topic) + rv = sys.stdout.reset() + if isinstance(rv, bytes): + rv = rv.decode('utf-8', 'ignore') + paragraphs = _paragraph_re.split(rv) + if len(paragraphs) > 1: + title = paragraphs[0] + text = '\n\n'.join(paragraphs[1:]) + else: # pragma: no cover + title = 'Help' + text = paragraphs[0] + sys.stdout._write(HELP_HTML % {'title': title, 'text': text}) + + +helper = _Helper() + + +def _add_subclass_info(inner, obj, base): + if isinstance(base, tuple): + for base in base: + if type(obj) is base: + return inner + elif type(obj) is base: + return inner + module = '' + if obj.__class__.__module__ not in ('__builtin__', 'exceptions'): + module = '%s.' % obj.__class__.__module__ + return '%s%s(%s)' % (module, obj.__class__.__name__, inner) + + +class DebugReprGenerator(object): + + def __init__(self): + self._stack = [] + + def _sequence_repr_maker(left, right, base=object(), limit=8): + def proxy(self, obj, recursive): + if recursive: + return _add_subclass_info(left + '...' + right, obj, base) + buf = [left] + have_extended_section = False + for idx, item in enumerate(obj): + if idx: + buf.append(', ') + if idx == limit: + buf.append('') + have_extended_section = True + buf.append(self.repr(item)) + if have_extended_section: + buf.append('') + buf.append(right) + return _add_subclass_info(u''.join(buf), obj, base) + return proxy + + list_repr = _sequence_repr_maker('[', ']', list) + tuple_repr = _sequence_repr_maker('(', ')', tuple) + set_repr = _sequence_repr_maker('set([', '])', set) + frozenset_repr = _sequence_repr_maker('frozenset([', '])', frozenset) + if deque is not None: + deque_repr = _sequence_repr_maker('collections.' + 'deque([', '])', deque) + del _sequence_repr_maker + + def regex_repr(self, obj): + pattern = repr(obj.pattern) + if PY2: + pattern = pattern.decode('string-escape', 'ignore') + else: + pattern = codecs.decode(pattern, 'unicode-escape', 'ignore') + if pattern[:1] == 'u': + pattern = 'ur' + pattern[1:] + else: + pattern = 'r' + pattern + return u're.compile(%s)' % pattern + + def string_repr(self, obj, limit=70): + buf = [''] + a = repr(obj[:limit]) + b = repr(obj[limit:]) + if isinstance(obj, text_type) and PY2: + buf.append('u') + a = a[1:] + b = b[1:] + if b != "''": + buf.extend((escape(a[:-1]), '', escape(b[1:]), '')) + else: + buf.append(escape(a)) + buf.append('') + return _add_subclass_info(u''.join(buf), obj, (bytes, text_type)) + + def dict_repr(self, d, recursive, limit=5): + if recursive: + return _add_subclass_info(u'{...}', d, dict) + buf = ['{'] + have_extended_section = False + for idx, (key, value) in enumerate(iteritems(d)): + if idx: + buf.append(', ') + if idx == limit - 1: + buf.append('') + have_extended_section = True + buf.append('%s: ' + '%s' % + (self.repr(key), self.repr(value))) + if have_extended_section: + buf.append('') + buf.append('}') + return _add_subclass_info(u''.join(buf), d, dict) + + def object_repr(self, obj): + r = repr(obj) + if PY2: + r = r.decode('utf-8', 'replace') + return u'%s' % escape(r) + + def dispatch_repr(self, obj, recursive): + if obj is helper: + return u'%r' % helper + if isinstance(obj, (integer_types, float, complex)): + return u'%r' % obj + if isinstance(obj, string_types): + return self.string_repr(obj) + if isinstance(obj, RegexType): + return self.regex_repr(obj) + if isinstance(obj, list): + return self.list_repr(obj, recursive) + if isinstance(obj, tuple): + return self.tuple_repr(obj, recursive) + if isinstance(obj, set): + return self.set_repr(obj, recursive) + if isinstance(obj, frozenset): + return self.frozenset_repr(obj, recursive) + if isinstance(obj, dict): + return self.dict_repr(obj, recursive) + if deque is not None and isinstance(obj, deque): + return self.deque_repr(obj, recursive) + return self.object_repr(obj) + + def fallback_repr(self): + try: + info = ''.join(format_exception_only(*sys.exc_info()[:2])) + except Exception: # pragma: no cover + info = '?' + if PY2: + info = info.decode('utf-8', 'ignore') + return u'<broken repr (%s)>' \ + u'' % escape(info.strip()) + + def repr(self, obj): + recursive = False + for item in self._stack: + if item is obj: + recursive = True + break + self._stack.append(obj) + try: + try: + return self.dispatch_repr(obj, recursive) + except Exception: + return self.fallback_repr() + finally: + self._stack.pop() + + def dump_object(self, obj): + repr = items = None + if isinstance(obj, dict): + title = 'Contents of' + items = [] + for key, value in iteritems(obj): + if not isinstance(key, string_types): + items = None + break + items.append((key, self.repr(value))) + if items is None: + items = [] + repr = self.repr(obj) + for key in dir(obj): + try: + items.append((key, self.repr(getattr(obj, key)))) + except Exception: + pass + title = 'Details for' + title += ' ' + object.__repr__(obj)[1:-1] + return self.render_object_dump(items, title, repr) + + def dump_locals(self, d): + items = [(key, self.repr(value)) for key, value in d.items()] + return self.render_object_dump(items, 'Local variables in frame') + + def render_object_dump(self, items, title, repr=None): + html_items = [] + for key, value in items: + html_items.append('%s
            %s
            ' % + (escape(key), value)) + if not html_items: + html_items.append('Nothing') + return OBJECT_DUMP_HTML % { + 'title': escape(title), + 'repr': repr and '
            %s
            ' % repr or '', + 'items': '\n'.join(html_items) + } diff --git a/venv/Lib/site-packages/werkzeug/debug/shared/FONT_LICENSE b/venv/Lib/site-packages/werkzeug/debug/shared/FONT_LICENSE new file mode 100644 index 0000000..ae78a8f --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/debug/shared/FONT_LICENSE @@ -0,0 +1,96 @@ +------------------------------- +UBUNTU FONT LICENCE Version 1.0 +------------------------------- + +PREAMBLE +This licence allows the licensed fonts to be used, studied, modified and +redistributed freely. The fonts, including any derivative works, can be +bundled, embedded, and redistributed provided the terms of this licence +are met. The fonts and derivatives, however, cannot be released under +any other licence. The requirement for fonts to remain under this +licence does not require any document created using the fonts or their +derivatives to be published under this licence, as long as the primary +purpose of the document is not to be a vehicle for the distribution of +the fonts. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this licence and clearly marked as such. This may +include source files, build scripts and documentation. + +"Original Version" refers to the collection of Font Software components +as received under this licence. + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to +a new environment. + +"Copyright Holder(s)" refers to all individuals and companies who have a +copyright ownership of the Font Software. + +"Substantially Changed" refers to Modified Versions which can be easily +identified as dissimilar to the Font Software by users of the Font +Software comparing the Original Version with the Modified Version. + +To "Propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification and with or without charging +a redistribution fee), making available to the public, and in some +countries other activities as well. + +PERMISSION & CONDITIONS +This licence does not grant any rights under trademark law and all such +rights are reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Font Software, to propagate the Font Software, subject to +the below conditions: + +1) Each copy of the Font Software must contain the above copyright +notice and this licence. These can be included either as stand-alone +text files, human-readable headers or in the appropriate machine- +readable metadata fields within text or binary files as long as those +fields can be easily viewed by the user. + +2) The font name complies with the following: +(a) The Original Version must retain its name, unmodified. +(b) Modified Versions which are Substantially Changed must be renamed to +avoid use of the name of the Original Version or similar names entirely. +(c) Modified Versions which are not Substantially Changed must be +renamed to both (i) retain the name of the Original Version and (ii) add +additional naming elements to distinguish the Modified Version from the +Original Version. The name of such Modified Versions must be the name of +the Original Version, with "derivative X" where X represents the name of +the new work, appended to that name. + +3) The name(s) of the Copyright Holder(s) and any contributor to the +Font Software shall not be used to promote, endorse or advertise any +Modified Version, except (i) as required by this licence, (ii) to +acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with +their explicit written permission. + +4) The Font Software, modified or unmodified, in part or in whole, must +be distributed entirely under this licence, and must not be distributed +under any other licence. The requirement for fonts to remain under this +licence does not affect any document created using the Font Software, +except any version of the Font Software extracted from a document +created using the Font Software may only be distributed under this +licence. + +TERMINATION +This licence becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER +DEALINGS IN THE FONT SOFTWARE. diff --git a/venv/Lib/site-packages/werkzeug/debug/shared/console.png b/venv/Lib/site-packages/werkzeug/debug/shared/console.png new file mode 100644 index 0000000000000000000000000000000000000000..c28dd63812d80e416682f835652f8e5824bdccb2 GIT binary patch literal 507 zcmVM#v1#3=Jvuyn6YS$$CoGDq?9U@APAh~ZOF>jp#TVKsZ^rDVDQJO z=z(unDix`Pp@6(DbUGbswOaq~fE^0}P{J^D(r7fOTCGyOUUzmBMUy*UwAN0eHX3ch z0VGKh>h*fm?RK3*p0=MB(nc}0O&K6sv)ObGuro)jd=1C!>krB`s44?r3Yg)uiG7!8OBL$a-Fo@&0(0LjNH2#KGJZi@q2YVKu xMc5!EfO4vTMwnf`nA&W_@!!aPwbiWO`5Vn?>V~$MffN7$002ovPDHLkV1l0g-PQmA literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/debug/shared/debugger.js b/venv/Lib/site-packages/werkzeug/debug/shared/debugger.js new file mode 100644 index 0000000..534019d --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/debug/shared/debugger.js @@ -0,0 +1,205 @@ +$(function() { + if (!EVALEX_TRUSTED) { + initPinBox(); + } + + /** + * if we are in console mode, show the console. + */ + if (CONSOLE_MODE && EVALEX) { + openShell(null, $('div.console div.inner').empty(), 0); + } + + $('div.traceback div.frame').each(function() { + var + target = $('pre', this), + consoleNode = null, + frameID = this.id.substring(6); + + target.click(function() { + $(this).parent().toggleClass('expanded'); + }); + + /** + * Add an interactive console to the frames + */ + if (EVALEX && target.is('.current')) { + $('') + .attr('title', 'Open an interactive python shell in this frame') + .click(function() { + consoleNode = openShell(consoleNode, target, frameID); + return false; + }) + .prependTo(target); + } + }); + + /** + * toggle traceback types on click. + */ + $('h2.traceback').click(function() { + $(this).next().slideToggle('fast'); + $('div.plain').slideToggle('fast'); + }).css('cursor', 'pointer'); + $('div.plain').hide(); + + /** + * Add extra info (this is here so that only users with JavaScript + * enabled see it.) + */ + $('span.nojavascript') + .removeClass('nojavascript') + .html('

            To switch between the interactive traceback and the plaintext ' + + 'one, you can click on the "Traceback" headline. From the text ' + + 'traceback you can also create a paste of it. ' + (!EVALEX ? '' : + 'For code execution mouse-over the frame you want to debug and ' + + 'click on the console icon on the right side.' + + '

            You can execute arbitrary Python code in the stack frames and ' + + 'there are some extra helpers available for introspection:' + + '

            • dump() shows all variables in the frame' + + '
            • dump(obj) dumps all that\'s known about the object
            ')); + + /** + * Add the pastebin feature + */ + $('div.plain form') + .submit(function() { + var label = $('input[type="submit"]', this); + var old_val = label.val(); + label.val('submitting...'); + $.ajax({ + dataType: 'json', + url: document.location.pathname, + data: {__debugger__: 'yes', tb: TRACEBACK, cmd: 'paste', + s: SECRET}, + success: function(data) { + $('div.plain span.pastemessage') + .removeClass('pastemessage') + .text('Paste created: ') + .append($('#' + data.id + '').attr('href', data.url)); + }, + error: function() { + alert('Error: Could not submit paste. No network connection?'); + label.val(old_val); + } + }); + return false; + }); + + // if we have javascript we submit by ajax anyways, so no need for the + // not scaling textarea. + var plainTraceback = $('div.plain textarea'); + plainTraceback.replaceWith($('
            ').text(plainTraceback.text()));
            +});
            +
            +function initPinBox() {
            +  $('.pin-prompt form').submit(function(evt) {
            +    evt.preventDefault();
            +    var pin = this.pin.value;
            +    var btn = this.btn;
            +    btn.disabled = true;
            +    $.ajax({
            +      dataType: 'json',
            +      url: document.location.pathname,
            +      data: {__debugger__: 'yes', cmd: 'pinauth', pin: pin,
            +             s: SECRET},
            +      success: function(data) {
            +        btn.disabled = false;
            +        if (data.auth) {
            +          EVALEX_TRUSTED = true;
            +          $('.pin-prompt').fadeOut();
            +        } else {
            +          if (data.exhausted) {
            +            alert('Error: too many attempts.  Restart server to retry.');
            +          } else {
            +            alert('Error: incorrect pin');
            +          }
            +        }
            +        console.log(data);
            +      },
            +      error: function() {
            +        btn.disabled = false;
            +        alert('Error: Could not verify PIN.  Network error?');
            +      }
            +    });
            +  });
            +}
            +
            +function promptForPin() {
            +  if (!EVALEX_TRUSTED) {
            +    $.ajax({
            +      url: document.location.pathname,
            +      data: {__debugger__: 'yes', cmd: 'printpin', s: SECRET}
            +    });
            +    $('.pin-prompt').fadeIn(function() {
            +      $('.pin-prompt input[name="pin"]').focus();
            +    });
            +  }
            +}
            +
            +
            +/**
            + * Helper function for shell initialization
            + */
            +function openShell(consoleNode, target, frameID) {
            +  promptForPin();
            +  if (consoleNode)
            +    return consoleNode.slideToggle('fast');
            +  consoleNode = $('
            ')
            +    .appendTo(target.parent())
            +    .hide()
            +  var historyPos = 0, history = [''];
            +  var output = $('
            [console ready]
            ') + .appendTo(consoleNode); + var form = $('
            >>>
            ') + .submit(function() { + var cmd = command.val(); + $.get('', { + __debugger__: 'yes', cmd: cmd, frm: frameID, s: SECRET}, function(data) { + var tmp = $('
            ').html(data); + $('span.extended', tmp).each(function() { + var hidden = $(this).wrap('').hide(); + hidden + .parent() + .append($('  ') + .click(function() { + hidden.toggle(); + $(this).toggleClass('open') + return false; + })); + }); + output.append(tmp); + command.focus(); + consoleNode.scrollTop(consoleNode.get(0).scrollHeight); + var old = history.pop(); + history.push(cmd); + if (typeof old != 'undefined') + history.push(old); + historyPos = history.length - 1; + }); + command.val(''); + return false; + }). + appendTo(consoleNode); + + var command = $('') + .appendTo(form) + .keydown(function(e) { + if (e.charCode == 100 && e.ctrlKey) { + output.text('--- screen cleared ---'); + return false; + } + else if (e.charCode == 0 && (e.keyCode == 38 || e.keyCode == 40)) { + if (e.keyCode == 38 && historyPos > 0) + historyPos--; + else if (e.keyCode == 40 && historyPos < history.length) + historyPos++; + command.val(history[historyPos]); + return false; + } + }); + + return consoleNode.slideDown('fast', function() { + command.focus(); + }); +} diff --git a/venv/Lib/site-packages/werkzeug/debug/shared/jquery.js b/venv/Lib/site-packages/werkzeug/debug/shared/jquery.js new file mode 100644 index 0000000..0f60b7b --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/debug/shared/jquery.js @@ -0,0 +1,5 @@ +/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1; + +return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
            a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/\s*$/g,ra={option:[1,""],legend:[1,"
            ","
            "],area:[1,"",""],param:[1,"",""],thead:[1,"","
            "],tr:[2,"","
            "],col:[2,"","
            "],td:[3,"","
            "],_default:k.htmlSerialize?[0,"",""]:[1,"X
            ","
            "]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("